From 2bc135983b97f71719e98d6fa83a0417f43291ce Mon Sep 17 00:00:00 2001 From: airkjw Date: Mon, 18 May 2026 11:42:23 +0900 Subject: [PATCH] docs: restore deployment guides after v7.1.10 reset --- API_USAGE.md | 343 +++++++++++++++++++++++++++++++++++++++++ DOCKER_DEPLOY.md | 232 ++++++++++++++++++++++++++++ REVERSE_PROXY_SETUP.md | 104 +++++++++++++ 3 files changed, 679 insertions(+) create mode 100644 API_USAGE.md create mode 100644 DOCKER_DEPLOY.md create mode 100644 REVERSE_PROXY_SETUP.md diff --git a/API_USAGE.md b/API_USAGE.md new file mode 100644 index 00000000..354692ad --- /dev/null +++ b/API_USAGE.md @@ -0,0 +1,343 @@ +# CLIProxyAPI 호출 가이드 + +## 접속 정보 + +| 항목 | 값 | +|------|-----| +| 외부 URL | `https://cliproxy.gru.farm` | +| 내부 URL | `http://192.168.0.17:8317` | +| API 키 | `Jinie4eva!` | +| 인증 방식 | `Authorization: Bearer ` | + +## 엔드포인트 + +| 용도 | 경로 | +|------|------| +| Claude 네이티브 (권장) | `/api/provider/claude/v1/messages` | +| OpenAI 호환 | `/v1/chat/completions` | +| 모델 목록 | `/v1/models` | + +## 사용 가능한 모델 + +| 모델 ID | 설명 | +|---------|------| +| `claude-sonnet-4-6` | Claude Sonnet 4.6 (최신, 권장) | +| `claude-opus-4-6` | Claude Opus 4.6 (최고 성능) | +| `claude-sonnet-4-5-20250929` | Claude Sonnet 4.5 | +| `claude-opus-4-5-20251101` | Claude Opus 4.5 | +| `claude-haiku-4-5-20251001` | Claude Haiku 4.5 (경량/빠름) | +| `claude-sonnet-4-20250514` | Claude Sonnet 4 | +| `claude-opus-4-20250514` | Claude Opus 4 | +| `claude-3-7-sonnet-20250219` | Claude 3.7 Sonnet | +| `claude-3-5-haiku-20241022` | Claude 3.5 Haiku | + +--- + +## 1. curl + +### 기본 호출 + +```bash +curl -X POST https://cliproxy.gru.farm/api/provider/claude/v1/messages \ + -H "Authorization: Bearer Jinie4eva!" \ + -H "anthropic-version: 2023-06-01" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "claude-sonnet-4-6", + "max_tokens": 1024, + "messages": [ + {"role": "user", "content": "안녕! 간단히 소개해줘"} + ] + }' +``` + +### 스트리밍 + +```bash +curl -X POST https://cliproxy.gru.farm/api/provider/claude/v1/messages \ + -H "Authorization: Bearer Jinie4eva!" \ + -H "anthropic-version: 2023-06-01" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "claude-sonnet-4-6", + "max_tokens": 1024, + "stream": true, + "messages": [ + {"role": "user", "content": "안녕!"} + ] + }' +``` + +### 모델 목록 조회 + +```bash +curl https://cliproxy.gru.farm/v1/models \ + -H "Authorization: Bearer Jinie4eva!" +``` + +--- + +## 2. Python — Anthropic SDK + +### 설치 + +```bash +pip install anthropic +``` + +### 기본 호출 + +```python +from anthropic import Anthropic + +client = Anthropic( + base_url="https://cliproxy.gru.farm/api/provider/claude", + api_key="Jinie4eva!" +) + +response = client.messages.create( + model="claude-sonnet-4-6", + max_tokens=1024, + messages=[ + {"role": "user", "content": "안녕! 간단히 소개해줘"} + ] +) + +print(response.content[0].text) +``` + +### 스트리밍 + +```python +from anthropic import Anthropic + +client = Anthropic( + base_url="https://cliproxy.gru.farm/api/provider/claude", + api_key="Jinie4eva!" +) + +with client.messages.stream( + model="claude-sonnet-4-6", + max_tokens=1024, + messages=[ + {"role": "user", "content": "안녕! 간단히 소개해줘"} + ] +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +``` + +### 시스템 프롬프트 + 멀티턴 + +```python +from anthropic import Anthropic + +client = Anthropic( + base_url="https://cliproxy.gru.farm/api/provider/claude", + api_key="Jinie4eva!" +) + +response = client.messages.create( + model="claude-sonnet-4-6", + max_tokens=1024, + system="당신은 친절한 한국어 AI 어시스턴트입니다.", + messages=[ + {"role": "user", "content": "파이썬이 뭐야?"}, + {"role": "assistant", "content": "파이썬은 프로그래밍 언어입니다."}, + {"role": "user", "content": "그럼 자바스크립트는?"} + ] +) + +print(response.content[0].text) +``` + +--- + +## 3. Python — OpenAI SDK (호환 모드) + +### 설치 + +```bash +pip install openai +``` + +### 기본 호출 + +```python +from openai import OpenAI + +client = OpenAI( + base_url="https://cliproxy.gru.farm/v1", + api_key="Jinie4eva!" +) + +response = client.chat.completions.create( + model="claude-sonnet-4-6", + messages=[ + {"role": "user", "content": "안녕!"} + ] +) + +print(response.choices[0].message.content) +``` + +### 스트리밍 + +```python +from openai import OpenAI + +client = OpenAI( + base_url="https://cliproxy.gru.farm/v1", + api_key="Jinie4eva!" +) + +stream = client.chat.completions.create( + model="claude-sonnet-4-6", + messages=[{"role": "user", "content": "안녕!"}], + stream=True +) + +for chunk in stream: + if chunk.choices[0].delta.content: + print(chunk.choices[0].delta.content, end="", flush=True) +``` + +--- + +## 4. Node.js — Anthropic SDK + +### 설치 + +```bash +npm install @anthropic-ai/sdk +``` + +### 기본 호출 + +```javascript +import Anthropic from "@anthropic-ai/sdk"; + +const client = new Anthropic({ + baseURL: "https://cliproxy.gru.farm/api/provider/claude", + apiKey: "Jinie4eva!", +}); + +const response = await client.messages.create({ + model: "claude-sonnet-4-6", + max_tokens: 1024, + messages: [{ role: "user", content: "안녕!" }], +}); + +console.log(response.content[0].text); +``` + +### 스트리밍 + +```javascript +import Anthropic from "@anthropic-ai/sdk"; + +const client = new Anthropic({ + baseURL: "https://cliproxy.gru.farm/api/provider/claude", + apiKey: "Jinie4eva!", +}); + +const stream = client.messages.stream({ + model: "claude-sonnet-4-6", + max_tokens: 1024, + messages: [{ role: "user", content: "안녕!" }], +}); + +for await (const chunk of stream) { + if ( + chunk.type === "content_block_delta" && + chunk.delta.type === "text_delta" + ) { + process.stdout.write(chunk.delta.text); + } +} +``` + +--- + +## 5. Node.js — OpenAI SDK (호환 모드) + +### 설치 + +```bash +npm install openai +``` + +### 기본 호출 + +```javascript +import OpenAI from "openai"; + +const client = new OpenAI({ + baseURL: "https://cliproxy.gru.farm/v1", + apiKey: "Jinie4eva!", +}); + +const response = await client.chat.completions.create({ + model: "claude-sonnet-4-6", + messages: [{ role: "user", content: "안녕!" }], +}); + +console.log(response.choices[0].message.content); +``` + +--- + +## 6. Claude Code CLI + +```bash +export ANTHROPIC_BASE_URL=https://cliproxy.gru.farm/api/provider/claude +export ANTHROPIC_API_KEY=Jinie4eva! + +claude +``` + +영구 적용 (`~/.zshrc` 또는 `~/.bashrc`): + +```bash +echo 'export ANTHROPIC_BASE_URL=https://cliproxy.gru.farm/api/provider/claude' >> ~/.zshrc +echo 'export ANTHROPIC_API_KEY=Jinie4eva!' >> ~/.zshrc +source ~/.zshrc +``` + +--- + +## 7. 환경변수로 관리 + +`.env` 파일: + +```env +ANTHROPIC_BASE_URL=https://cliproxy.gru.farm/api/provider/claude +ANTHROPIC_API_KEY=Jinie4eva! +``` + +Python에서 `.env` 사용: + +```python +from dotenv import load_dotenv +from anthropic import Anthropic + +load_dotenv() + +# base_url, api_key 자동으로 환경변수에서 읽음 +client = Anthropic() + +response = client.messages.create( + model="claude-sonnet-4-6", + max_tokens=1024, + messages=[{"role": "user", "content": "안녕!"}] +) +print(response.content[0].text) +``` + +--- + +## 주의사항 + +- **내부망 접근 시** URL을 `http://192.168.0.17:8317`로 변경 +- **OpenAI 호환 모드**는 `/v1/chat/completions`를 사용하지만, Claude 네이티브 기능(extended thinking 등)은 `/api/provider/claude/v1/messages` 사용 권장 +- **타임아웃** 설정: 긴 응답의 경우 클라이언트 타임아웃을 600초 이상으로 설정 diff --git a/DOCKER_DEPLOY.md b/DOCKER_DEPLOY.md new file mode 100644 index 00000000..b4b8c1d0 --- /dev/null +++ b/DOCKER_DEPLOY.md @@ -0,0 +1,232 @@ +# CLIProxyAPI Docker 배포 가이드 + +NAS(nas.gru.farm)에 Docker로 CLIProxyAPI를 배포하는 방법을 정리합니다. + +## 사전 조건 + +| 항목 | 내용 | +|------|------| +| NAS 접속 | `ssh airkjw@nas.gru.farm -p 22` | +| Docker | `sudo /usr/local/bin/docker` (NOPASSWD) | +| Docker Compose | `sudo /usr/local/bin/docker compose` | +| NAS 내부 IP | 192.168.0.17 | + +## 1. 배포 디렉토리 준비 + +```bash +ssh airkjw@nas.gru.farm + +# 배포 디렉토리 생성 +mkdir -p ~/docker/cli-proxy-api +cd ~/docker/cli-proxy-api +``` + +## 2. 필요 파일 구성 + +NAS에 아래 파일들이 필요합니다: + +``` +~/docker/cli-proxy-api/ +├── docker-compose.yml # 컨테이너 설정 +├── config.yaml # 서비스 설정 (API 키, 포트 등) +├── auths/ # OAuth 인증 데이터 (자동 생성) +└── logs/ # 로그 디렉토리 (자동 생성) +``` + +## 3. docker-compose.yml + +로컬 빌드 방식 (소스에서 직접 빌드): + +```yaml +services: + cli-proxy-api: + build: + context: . + dockerfile: Dockerfile + container_name: cli-proxy-api + ports: + - "8317:8317" # 메인 API 포트 + # 필요시 추가 포트 오픈 + # - "8085:8085" + volumes: + - ./config.yaml:/CLIProxyAPI/config.yaml + - ./auths:/root/.cli-proxy-api + - ./logs:/CLIProxyAPI/logs + environment: + - TZ=Asia/Seoul + restart: unless-stopped +``` + +또는 공식 이미지 사용: + +```yaml +services: + cli-proxy-api: + image: eceasy/cli-proxy-api:latest + container_name: cli-proxy-api + ports: + - "8317:8317" + volumes: + - ./config.yaml:/CLIProxyAPI/config.yaml + - ./auths:/root/.cli-proxy-api + - ./logs:/CLIProxyAPI/logs + environment: + - TZ=Asia/Seoul + restart: unless-stopped +``` + +## 4. config.yaml 설정 + +`config.example.yaml`을 기반으로 작성합니다. + +### 최소 설정 예시 + +```yaml +# 서버 바인딩 +host: "" +port: 8317 + +# API 키 (클라이언트 인증용, 원하는 값으로 설정) +api-keys: + - "my-secret-api-key-1" + +# 디버그 (초기 설정 시 true 권장, 안정화 후 false) +debug: false + +# 로그를 파일로 기록 +logging-to-file: true +logs-max-total-size-mb: 100 + +# 재시도 설정 +request-retry: 3 +``` + +### Claude API 키 사용 시 추가 + +```yaml +claude-api-key: + - api-key: "sk-ant-xxxxx" + # base-url: "https://api.anthropic.com" # 기본값이므로 생략 가능 +``` + +### Gemini API 키 사용 시 추가 + +```yaml +gemini-api-key: + - api-key: "AIzaSy..." +``` + +### Management UI 활성화 (웹 관리 패널) + +```yaml +remote-management: + allow-remote: true + secret-key: "my-management-password" + disable-control-panel: false +``` + +## 5. 배포 실행 + +```bash +cd ~/docker/cli-proxy-api + +# 공식 이미지 사용 시 +sudo /usr/local/bin/docker compose up -d + +# 소스 빌드 시 (Gitea에서 소스 가져와서) +git clone http://nas.gru.farm:3001/airkjw/CLIProxyAPI.git src +sudo /usr/local/bin/docker compose -f src/docker-compose.yml up -d --build +``` + +## 6. 확인 + +```bash +# 컨테이너 상태 확인 +sudo /usr/local/bin/docker ps | grep cli-proxy-api + +# 로그 확인 +sudo /usr/local/bin/docker logs cli-proxy-api + +# API 응답 테스트 +curl http://localhost:8317/ +curl http://192.168.0.17:8317/ + +# 모델 목록 확인 (API 키 인증) +curl -H "Authorization: Bearer my-secret-api-key-1" http://localhost:8317/v1/models +``` + +## 7. 클라이언트 연결 + +CLIProxyAPI가 실행되면 각 AI CLI 도구에서 프록시 주소로 연결합니다. + +### Claude Code에서 사용 + +```bash +# 환경변수 설정 +export ANTHROPIC_BASE_URL=http://192.168.0.17:8317 +export ANTHROPIC_API_KEY=my-secret-api-key-1 +``` + +### OpenAI 호환 클라이언트에서 사용 + +```bash +export OPENAI_BASE_URL=http://192.168.0.17:8317/v1 +export OPENAI_API_KEY=my-secret-api-key-1 +``` + +## 8. 관리 & 운영 + +```bash +# 컨테이너 중지 +sudo /usr/local/bin/docker compose down + +# 설정 변경 후 재시작 +sudo /usr/local/bin/docker compose restart + +# 이미지 업데이트 (공식 이미지 사용 시) +sudo /usr/local/bin/docker compose pull +sudo /usr/local/bin/docker compose up -d + +# 로그 실시간 모니터링 +sudo /usr/local/bin/docker logs -f cli-proxy-api +``` + +## 포트 목록 + +| 포트 | 용도 | 필수 여부 | +|------|------|-----------| +| 8317 | 메인 API | 필수 | +| 8085 | 추가 API | 선택 | +| 1455 | 추가 서비스 | 선택 | +| 54545 | 추가 서비스 | 선택 | +| 51121 | 추가 서비스 | 선택 | +| 11451 | 추가 서비스 | 선택 | + +> 기본적으로 8317 포트만 열면 됩니다. 나머지는 특정 기능 사용 시 필요합니다. + +## 주의사항 + +- `config.yaml`은 `.gitignore`에 포함되어 있어 Git에 커밋되지 않음 (API 키 보호) +- OAuth 인증(Claude, Gemini 등)은 최초 1회 브라우저 로그인 필요 +- `auths/` 디렉토리를 볼륨으로 마운트하면 컨테이너 재생성 시에도 인증 유지 +- NAS 외부 접근 시 방화벽/포트포워딩 설정 필요 + +## 업데이트 이력 + +| 날짜 | 버전 | 비고 | +|------|------|------| +| 2026-05-04 | v6.10.4 | 69개 커밋 변경 — WebSocket compact 처리 개선, X-Amp-Thread-Id 기반 session affinity, Codex reasoning/이미지 처리 강화, GPT-5.5 모델 추가, OpenAI 호환 provider 비활성화 옵션. 무중단 업데이트, 재인증 불필요 | +| 2026-04-26 | v6.9.38 | Protocol multiplexer + Redis queue 도입, 관리키/Redis AUTH 반복 실패 시 IP 차단 추가. 무중단 업데이트, 재인증 불필요 | +| 2026-04-23 | v6.9.34 | `docker compose pull && docker compose up -d`로 무중단 업데이트. Auth 파일 형식 변경 없어 재인증 불필요 | +| 2026-04-01 | v6.9.7 | 최초 배포 | + +### 업데이트 절차 + +```bash +ssh airkjw@nas.gru.farm +cd /volume2/docker/CLIProxyAPI +sudo /usr/local/bin/docker compose pull +sudo /usr/local/bin/docker compose up -d +``` + +`auths/` 볼륨이 외부에 마운트되어 있어 컨테이너 교체 시 OAuth 토큰이 유지됩니다. diff --git a/REVERSE_PROXY_SETUP.md b/REVERSE_PROXY_SETUP.md new file mode 100644 index 00000000..8e52bacc --- /dev/null +++ b/REVERSE_PROXY_SETUP.md @@ -0,0 +1,104 @@ +# CLIProxyAPI 역방향 프록시 & HTTPS 설정 가이드 + +외부에서 `https://cliproxy.gru.farm`으로 CLIProxyAPI에 접근하기 위한 설정입니다. + +## 1단계: DNS 레코드 추가 + +hostcocoa.com DNS 관리에서 A 레코드를 추가합니다. + +| 타입 | 호스트 | 값 | +|------|--------|-----| +| A | cliproxy | 125.188.185.74 | + +> 기존 `nas.gru.farm`, `haesol.gru.farm` 등과 같은 IP입니다. + +## 2단계: Synology DSM 역방향 프록시 설정 + +1. DSM 웹 UI 접속 (보통 `https://nas.gru.farm:5001`) +2. **제어판** → **로그인 포털** → **고급** 탭 → **역방향 프록시** 클릭 +3. **생성** 버튼 클릭 +4. 아래와 같이 입력: + +### 일반 설정 + +| 항목 | 값 | +|------|-----| +| 설명 | `CLIProxyAPI` | +| **소스 (프론트엔드)** | | +| 프로토콜 | `HTTPS` | +| 호스트 이름 | `cliproxy.gru.farm` | +| 포트 | `443` | +| HSTS | 비활성화 | +| **대상 (백엔드)** | | +| 프로토콜 | `HTTP` | +| 호스트 이름 | `localhost` | +| 포트 | `8317` | + +### 사용자 지정 헤더 (선택) + +필요 시 WebSocket 지원을 위해 사용자 지정 헤더 추가: +- `Upgrade` → `$http_upgrade` +- `Connection` → `$connection_upgrade` + +### 타임아웃 설정 + +AI 요청은 응답이 오래 걸릴 수 있으므로 타임아웃을 늘려주세요: +- 연결 타임아웃: `600` +- 전송 타임아웃: `600` +- 수신 타임아웃: `600` + +5. **저장** 클릭 + +## 3단계: SSL 인증서 설정 + +Synology DSM에서 `cliproxy.gru.farm` 용 SSL 인증서를 설정합니다. + +### Let's Encrypt 인증서 발급 (권장) + +1. **제어판** → **보안** → **인증서** 탭 +2. **추가** → **새 인증서 추가** → **Let's Encrypt에서 인증서 가져오기** +3. 도메인: `cliproxy.gru.farm` +4. 이메일: 본인 이메일 +5. 발급 완료 후, **설정** 버튼 클릭 +6. `cliproxy.gru.farm` 역방향 프록시 항목에 방금 발급한 인증서 선택 + +### 기존 와일드카드 인증서가 있는 경우 + +`*.gru.farm` 와일드카드 인증서가 있다면 별도 발급 없이 해당 인증서를 선택하면 됩니다. + +## 4단계: 공유기 포트 포워딩 + +공유기에서 443 포트가 NAS(192.168.0.17)로 포워딩되어 있는지 확인합니다. + +> 기존 `haesol.gru.farm` 등이 HTTPS로 동작 중이라면 이미 설정되어 있을 가능성이 높습니다. + +| 외부 포트 | 내부 IP | 내부 포트 | 프로토콜 | +|-----------|---------|-----------|----------| +| 443 | 192.168.0.17 | 443 | TCP | + +## 5단계: 확인 + +```bash +# DNS 전파 확인 +dig +short cliproxy.gru.farm +# 125.188.185.74 가 나오면 성공 + +# HTTPS 접속 테스트 +curl https://cliproxy.gru.farm/ +# {"endpoints":[...],"message":"CLI Proxy API Server"} + +# 모델 목록 확인 +curl -H "Authorization: Bearer Jinie4eva!" https://cliproxy.gru.farm/v1/models +``` + +## 클라이언트 연결 (외부) + +```bash +# Claude Code +export ANTHROPIC_BASE_URL=https://cliproxy.gru.farm +export ANTHROPIC_API_KEY=Jinie4eva! + +# OpenAI 호환 +export OPENAI_BASE_URL=https://cliproxy.gru.farm/v1 +export OPENAI_API_KEY=Jinie4eva! +```