# ────────────────────────────────────────────── # cursor-api-proxy — 設定與建置 # 編輯下方變數,然後執行 make env 產生 .env 檔 # ────────────────────────────────────────────── # ── 伺服器設定 ───────────────────────────────── HOST ?= 127.0.0.1 PORT ?= 8766 API_KEY ?= TIMEOUT_MS ?= 3600000 MULTI_PORT ?= false VERBOSE ?= false # ── Agent / 模型設定 ────────────────────────── AGENT_BIN ?= agent AGENT_NODE ?= AGENT_SCRIPT ?= DEFAULT_MODEL ?= auto STRICT_MODEL ?= true MAX_MODE ?= false FORCE ?= false APPROVE_MCPS ?= false # ── 工作區與帳號 ────────────────────────────── WORKSPACE ?= CHAT_ONLY_WORKSPACE ?= true CONFIG_DIRS ?= # ── Cursor / Claude Code(~/.claude)──────────────── CLAUDE_SETTINGS ?= $(HOME)/.claude/settings.json CLAUDE_JSON ?= $(HOME)/.claude.json ANTHROPIC_AUTH_TOKEN ?= ANTHROPIC_DEFAULT_SONNET_MODEL ?= claude-4.6-sonnet-medium ANTHROPIC_DEFAULT_OPUS_MODEL ?= claude-4.6-opus-max ANTHROPIC_DEFAULT_HAIKU_MODEL ?= gemini-3-flash ANTHROPIC_BASE_HOST ?= $(HOST) # ── TLS / HTTPS ─────────────────────────────── TLS_CERT ?= TLS_KEY ?= # ── 記錄 ────────────────────────────────────── SESSIONS_LOG ?= # ────────────────────────────────────────────── ENV_FILE ?= .env OPENCODE_CONFIG ?= $(HOME)/.config/opencode/opencode.json # ── Docker 設定 ─────────────────────────────── DOCKER_IMAGE ?= cursor-api-proxy DOCKER_TAG ?= latest DOCKER_COMPOSE ?= docker compose .PHONY: env run build clean help opencode opencode-models pm2 pm2-stop pm2-logs claude-code pm2-claude-code \ claude-settings claude-onboarding claude-cursor-setup \ docker-build docker-up docker-down docker-logs docker-restart docker-shell docker-env docker-setup ## 產生 .env 檔(預設輸出至 .env,可用 ENV_FILE=xxx 覆寫) env: @printf '# 由 make env 自動產生,請勿手動編輯\n' > $(ENV_FILE) @printf 'CURSOR_BRIDGE_HOST=%s\n' "$(HOST)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_PORT=%s\n' "$(PORT)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_API_KEY=%s\n' "$(API_KEY)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_TIMEOUT_MS=%s\n' "$(TIMEOUT_MS)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_MULTI_PORT=%s\n' "$(MULTI_PORT)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_VERBOSE=%s\n' "$(VERBOSE)" >> $(ENV_FILE) @printf 'CURSOR_AGENT_BIN=%s\n' "$(AGENT_BIN)" >> $(ENV_FILE) @printf 'CURSOR_AGENT_NODE=%s\n' "$(AGENT_NODE)" >> $(ENV_FILE) @printf 'CURSOR_AGENT_SCRIPT=%s\n' "$(AGENT_SCRIPT)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_DEFAULT_MODEL=%s\n' "$(DEFAULT_MODEL)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_STRICT_MODEL=%s\n' "$(STRICT_MODEL)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_MAX_MODE=%s\n' "$(MAX_MODE)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_FORCE=%s\n' "$(FORCE)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_APPROVE_MCPS=%s\n' "$(APPROVE_MCPS)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_WORKSPACE=%s\n' "$(WORKSPACE)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_CHAT_ONLY_WORKSPACE=%s\n' "$(CHAT_ONLY_WORKSPACE)" >> $(ENV_FILE) @printf 'CURSOR_CONFIG_DIRS=%s\n' "$(CONFIG_DIRS)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_TLS_CERT=%s\n' "$(TLS_CERT)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_TLS_KEY=%s\n' "$(TLS_KEY)" >> $(ENV_FILE) @printf 'CURSOR_BRIDGE_SESSIONS_LOG=%s\n' "$(SESSIONS_LOG)" >> $(ENV_FILE) @echo "已產生 $(ENV_FILE)" ## 編譯二進位檔 build: go build -o cursor-api-proxy . ## 載入 .env 後直接執行(需先執行 make env 或已有 .env) run: build @if [ -f $(ENV_FILE) ]; then \ set -a && . ./$(ENV_FILE) && set +a && ./cursor-api-proxy; \ else \ echo "找不到 $(ENV_FILE),請先執行 make env"; exit 1; \ fi ## 清除產出物 clean: rm -f cursor-api-proxy $(ENV_FILE) ## 設定 OpenCode 使用此代理(更新 opencode.json 的 cursor provider) opencode: build @if [ ! -f "$(OPENCODE_CONFIG)" ]; then \ echo "找不到 $(OPENCODE_CONFIG),建立新設定檔"; \ mkdir -p $$(dirname "$(OPENCODE_CONFIG)"); \ printf '{\n "provider": {\n "cursor": {\n "npm": "@ai-sdk/openai-compatible",\n "name": "Cursor Agent",\n "options": {\n "baseURL": "http://$(HOST):$(PORT)/v1",\n "apiKey": "unused"\n },\n "models": { "auto": { "name": "Cursor Auto" } }\n }\n }\n}\n' > "$(OPENCODE_CONFIG)"; \ echo "已建立 $(OPENCODE_CONFIG)"; \ elif [ -n "$(API_KEY)" ]; then \ jq '.provider.cursor.options.baseURL = "http://$(HOST):$(PORT)/v1" | .provider.cursor.options.apiKey = "$(API_KEY)"' "$(OPENCODE_CONFIG)" > "$(OPENCODE_CONFIG).tmp" && mv "$(OPENCODE_CONFIG).tmp" "$(OPENCODE_CONFIG)"; \ echo "已更新 $(OPENCODE_CONFIG)(baseURL → http://$(HOST):$(PORT)/v1,apiKey 已設定)"; \ else \ jq '.provider.cursor.options.baseURL = "http://$(HOST):$(PORT)/v1"' "$(OPENCODE_CONFIG)" > "$(OPENCODE_CONFIG).tmp" && mv "$(OPENCODE_CONFIG).tmp" "$(OPENCODE_CONFIG)"; \ echo "已更新 $(OPENCODE_CONFIG)(baseURL → http://$(HOST):$(PORT)/v1)"; \ fi ## 啟動代理並用 curl 同步模型列表到 opencode.json opencode-models: opencode @echo "啟動代理以取得模型列表..." @set -a && . ./$(ENV_FILE) 2>/dev/null; set +a; \ ./cursor-api-proxy & PID=$$!; \ sleep 2; \ MODELS=$$(curl -s http://$(HOST):$(PORT)/v1/models | jq '[.data[].id]'); \ kill $$PID 2>/dev/null; wait $$PID 2>/dev/null; \ if [ -n "$$MODELS" ] && [ "$$MODELS" != "null" ]; then \ jq --argjson ids "$$MODELS" 'reduce $ids[] as $id (.; .provider.cursor.models[$id] = { name: $id })' "$(OPENCODE_CONFIG)" > "$(OPENCODE_CONFIG).tmp" && mv "$(OPENCODE_CONFIG).tmp" "$(OPENCODE_CONFIG)"; \ echo "已同步模型列表到 $(OPENCODE_CONFIG)"; \ else \ echo "無法取得模型列表,請確認代理已啟動"; \ fi ## 編譯並用 pm2 啟動 pm2: build @if [ -f "$(ENV_FILE)" ]; then \ env $$(cat $(ENV_FILE) | grep -v '^#' | xargs) CURSOR_BRIDGE_HOST=$(HOST) CURSOR_BRIDGE_PORT=$(PORT) pm2 start ./cursor-api-proxy --name cursor-api-proxy --update-env; \ else \ CURSOR_BRIDGE_HOST=$(HOST) CURSOR_BRIDGE_PORT=$(PORT) pm2 start ./cursor-api-proxy --name cursor-api-proxy; \ fi @pm2 save @echo "pm2 已啟動 cursor-api-proxy(http://$(HOST):$(PORT))" ## 用 pm2 啟動 OpenCode 代理(設定 + 啟動一步完成) pm2-opencode: opencode pm2 @echo "OpenCode 設定已更新並用 pm2 啟動代理" ## 寫入 ~/.claude/settings.json(ANTHROPIC_BASE_URL、三個 DEFAULT_* 模型;需 jq) claude-settings: @command -v jq >/dev/null 2>&1 || { echo "需要 jq"; exit 1; } @mkdir -p $$(dirname "$(CLAUDE_SETTINGS)") @jq -n \ --arg base "http://$(ANTHROPIC_BASE_HOST):$(PORT)" \ --arg token "$(ANTHROPIC_AUTH_TOKEN)" \ --arg sonnet "$(ANTHROPIC_DEFAULT_SONNET_MODEL)" \ --arg opus "$(ANTHROPIC_DEFAULT_OPUS_MODEL)" \ --arg haiku "$(ANTHROPIC_DEFAULT_HAIKU_MODEL)" \ '{ env: { ANTHROPIC_BASE_URL: $$base, ANTHROPIC_AUTH_TOKEN: $$token, ANTHROPIC_DEFAULT_SONNET_MODEL: $$sonnet, ANTHROPIC_DEFAULT_OPUS_MODEL: $$opus, ANTHROPIC_DEFAULT_HAIKU_MODEL: $$haiku } }' \ > "$(CLAUDE_SETTINGS).tmp" && mv "$(CLAUDE_SETTINGS).tmp" "$(CLAUDE_SETTINGS)" @echo "已寫入 $(CLAUDE_SETTINGS)(BASE_URL=http://$(ANTHROPIC_BASE_HOST):$(PORT))" ## 將 ~/.claude.json 的 hasCompletedOnboarding 設為 true(繞過初次引導;需 jq) claude-onboarding: @command -v jq >/dev/null 2>&1 || { echo "需要 jq"; exit 1; } @test -f "$(CLAUDE_JSON)" || { echo "找不到 $(CLAUDE_JSON)"; exit 1; } @jq '.hasCompletedOnboarding = true' "$(CLAUDE_JSON)" > "$(CLAUDE_JSON).tmp" && mv "$(CLAUDE_JSON).tmp" "$(CLAUDE_JSON)" @echo "已設定 $(CLAUDE_JSON) hasCompletedOnboarding=true" ## 一次執行 claude-settings + claude-onboarding claude-cursor-setup: claude-settings claude-onboarding @echo "Cursor/Claude Code 本機設定已套用" ## 編譯並用 pm2 啟動 + 設定 Claude Code 環境變數 pm2-claude-code: pm2 @echo "" @echo "Claude Code 設定:將以下指令加入你的 shell 啟動檔(~/.bashrc 或 ~/.zshrc):" @echo "" @echo " export ANTHROPIC_BASE_URL=http://$(HOST):$(PORT)" @echo " export ANTHROPIC_API_KEY=$(if $(API_KEY),$(API_KEY),dummy-key)" @echo "" @echo "或在當前 shell 執行:" @echo "" @echo " export ANTHROPIC_BASE_URL=http://$(HOST):$(PORT)" @echo " export ANTHROPIC_API_KEY=$(if $(API_KEY),$(API_KEY),dummy-key)" @echo " claude" @echo "" ## 停止 pm2 中的代理 pm2-stop: pm2 stop cursor-api-proxy 2>/dev/null || echo "cursor-api-proxy 未在執行" ## 查看 pm2 日誌 pm2-logs: pm2 logs cursor-api-proxy ## ────────────────────────────────────────────────── ## Docker Compose 指令 ## ────────────────────────────────────────────────── ## 複製 .env.example 為 .env(首次設定) docker-env: @if [ -f .env ]; then \ echo ".env 已存在,若要重置請手動刪除後再執行"; \ else \ cp .env.example .env; \ echo "已建立 .env,請編輯填入設定後執行 make docker-up"; \ fi ## 建置 Docker 映像檔 docker-build: $(DOCKER_COMPOSE) build ## 啟動 Docker Compose(背景執行) docker-up: @if [ ! -f .env ]; then \ echo "找不到 .env,請先執行:make docker-env"; exit 1; \ fi $(DOCKER_COMPOSE) up -d @echo "cursor-api-proxy 已啟動(http://0.0.0.0:$(PORT))" ## 首次設定並啟動(複製 .env + build + up,一步完成) docker-setup: @if [ ! -f .env ]; then \ cp .env.example .env; \ echo "已建立 .env,請先編輯填入必要設定(CURSOR_AGENT_HOST_BIN、CURSOR_ACCOUNTS_DIR),然後重新執行 make docker-setup"; \ exit 1; \ fi $(DOCKER_COMPOSE) build $(DOCKER_COMPOSE) up -d @echo "cursor-api-proxy 已啟動(http://0.0.0.0:$(PORT))" @echo "查看日誌:make docker-logs" ## 停止並移除容器 docker-down: $(DOCKER_COMPOSE) down ## 查看容器日誌(即時跟蹤) docker-logs: $(DOCKER_COMPOSE) logs -f cursor-api-proxy ## 重新建置並啟動容器 docker-restart: $(DOCKER_COMPOSE) down $(DOCKER_COMPOSE) build $(DOCKER_COMPOSE) up -d @echo "cursor-api-proxy 已重新啟動" ## 進入容器 shell(除錯用) docker-shell: $(DOCKER_COMPOSE) exec cursor-api-proxy sh ## 顯示說明 help: @echo "可用目標:" @echo " make env 產生 .env(先在 Makefile 頂端填好變數)" @echo " make build 編譯 cursor-api-proxy 二進位檔" @echo " make run 編譯並載入 .env 執行" @echo " make pm2 編譯並用 pm2 啟動代理" @echo " make pm2-stop 停止 pm2 中的代理" @echo " make pm2-logs 查看 pm2 日誌" @echo " make pm2-claude-code 啟動代理 + 輸出 Claude Code 設定指令" @echo " make opencode 編譯並設定 OpenCode(更新 opencode.json)" @echo " make pm2-opencode 設定 OpenCode + 啟動代理" @echo " make opencode-models 編譯、設定 OpenCode 並同步模型列表" @echo " make claude-settings 寫入 ~/.claude/settings.json(模型與 BASE_URL)" @echo " make claude-onboarding 設定 ~/.claude.json hasCompletedOnboarding=true" @echo " make claude-cursor-setup 同上兩步一次完成" @echo " make clean 刪除二進位檔與 .env" @echo "" @echo "Docker Compose 指令:" @echo " make docker-env 複製 .env.example 為 .env(首次設定)" @echo " make docker-setup 首次設定並啟動(自動複製 .env + build + up)" @echo " make docker-build 建置 Docker 映像檔" @echo " make docker-up 啟動容器(背景執行,需已有 .env)" @echo " make docker-down 停止並移除容器" @echo " make docker-logs 查看容器即時日誌" @echo " make docker-restart 重新建置並啟動容器" @echo " make docker-shell 進入容器 shell(除錯用)" @echo "" @echo "覆寫範例:" @echo " make env PORT=9000 API_KEY=mysecret TIMEOUT_MS=60000" @echo " make pm2-claude-code PORT=8765 API_KEY=mykey" @echo " make pm2-opencode PORT=8765" @echo " make claude-settings PORT=8766 ANTHROPIC_BASE_HOST=localhost ANTHROPIC_DEFAULT_OPUS_MODEL=claude-4.6-opus-high"