opencode-cursor-agent/Makefile

320 lines
17 KiB
Makefile
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ──────────────────────────────────────────────
# 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 ?=
# ── OpenCode 模型設定 ────────────────────
OPENCODE_MODEL ?= cursor/claude-4.6-sonnet-medium
OPENCODE_SMALL_MODEL ?= cursor/gpt-5.4-nano-medium
# ── 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 ?=
# ── Gemini Web Provider ───────────────────────
PROVIDER ?= cursor
GEMINI_ACCOUNT_DIR ?=
GEMINI_BROWSER_VISIBLE ?= false
GEMINI_MAX_SESSIONS ?= 3
# ── 記錄 ──────────────────────────────────────
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)
@printf '# ── Provider 設定 ───────────────────────────\n' >> $(ENV_FILE)
@printf 'CURSOR_BRIDGE_PROVIDER=%s\n' "$(PROVIDER)" >> $(ENV_FILE)
@printf '# Gemini Web Provider 設定(當 PROVIDER=gemini-web 時使用)\n' >> $(ENV_FILE)
@printf 'GEMINI_ACCOUNT_DIR=%s\n' "$(GEMINI_ACCOUNT_DIR)" >> $(ENV_FILE)
@printf 'GEMINI_BROWSER_VISIBLE=%s\n' "$(GEMINI_BROWSER_VISIBLE)" >> $(ENV_FILE)
@printf 'GEMINI_MAX_SESSIONS=%s\n' "$(GEMINI_MAX_SESSIONS)" >> $(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 與 gemini-web provider
opencode: build
@if [ ! -f "$(OPENCODE_CONFIG)" ]; then \
echo "找不到 $(OPENCODE_CONFIG),建立新設定檔"; \
mkdir -p $$(dirname "$(OPENCODE_CONFIG)"); \
printf '{\n "model": "$(OPENCODE_MODEL)",\n "small_model": "$(OPENCODE_SMALL_MODEL)",\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 "gemini-web": {\n "npm": "@ai-sdk/openai-compatible",\n "name": "Gemini Web",\n "options": {\n "baseURL": "http://$(HOST):$(PORT)/v1",\n "apiKey": "unused"\n },\n "models": {\n "gemini-2.0-flash": { "name": "Gemini 2.0 Flash" },\n "gemini-2.5-pro": { "name": "Gemini 2.5 Pro" },\n "gemini-2.5-pro-thinking": { "name": "Gemini 2.5 Pro Thinking" }\n }\n }\n }\n}\n' > "$(OPENCODE_CONFIG)"; \
echo "已建立 $(OPENCODE_CONFIG)(包含 cursor 與 gemini-web provider"; \
elif [ -n "$(API_KEY)" ]; then \
jq --arg model "$(OPENCODE_MODEL)" --arg small "$(OPENCODE_SMALL_MODEL)" --arg base "http://$(HOST):$(PORT)/v1" --arg key "$(API_KEY)" '.model = $$model | .small_model = $$small | .provider.cursor.options.baseURL = $$base | .provider.cursor.options.apiKey = $$key | .provider["gemini-web"].options.baseURL = $$base | .provider["gemini-web"].options.apiKey = $$key' "$(OPENCODE_CONFIG)" > "$(OPENCODE_CONFIG).tmp" && mv "$(OPENCODE_CONFIG).tmp" "$(OPENCODE_CONFIG)"; \
echo "已更新 $(OPENCODE_CONFIG)model=$(OPENCODE_MODEL), small_model=$(OPENCODE_SMALL_MODEL), baseURL → http://$(HOST):$(PORT)/v1apiKey 已設定)"; \
else \
jq --arg model "$(OPENCODE_MODEL)" --arg small "$(OPENCODE_SMALL_MODEL)" --arg base "http://$(HOST):$(PORT)/v1" '.model = $$model | .small_model = $$small | .provider.cursor.options.baseURL = $$base | .provider["gemini-web"].options.baseURL = $$base' "$(OPENCODE_CONFIG)" > "$(OPENCODE_CONFIG).tmp" && mv "$(OPENCODE_CONFIG).tmp" "$(OPENCODE_CONFIG)"; \
echo "已更新 $(OPENCODE_CONFIG)model=$(OPENCODE_MODEL), small_model=$(OPENCODE_SMALL_MODEL), 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 } | .provider["gemini-web"].models[$id] = { name: $id })' "$(OPENCODE_CONFIG)" > "$(OPENCODE_CONFIG).tmp" && mv "$(OPENCODE_CONFIG).tmp" "$(OPENCODE_CONFIG)"; \
echo "已同步模型列表到 $(OPENCODE_CONFIG)cursor 與 gemini-web"; \
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-proxyhttp://$(HOST):$(PORT)"
## 用 pm2 啟動 OpenCode 代理(設定 + 啟動一步完成)
pm2-opencode: opencode pm2
@echo "OpenCode 設定已更新並用 pm2 啟動代理"
## 寫入 ~/.claude/settings.jsonANTHROPIC_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 並自動偵測本機 agent 路徑(首次設定)
docker-env:
@if [ -f .env ]; then \
echo ".env 已存在,若要重置請手動刪除後再執行"; \
else \
cp .env.example .env; \
DETECTED_AGENT=$$(which agent 2>/dev/null || echo ""); \
if [ -n "$$DETECTED_AGENT" ]; then \
REAL_AGENT=$$(readlink -f "$$DETECTED_AGENT"); \
sed -i "s|^CURSOR_AGENT_HOST_BIN=.*|CURSOR_AGENT_HOST_BIN=$$REAL_AGENT|" .env; \
echo " 已偵測 agent: $$REAL_AGENT"; \
else \
echo " 警告:找不到 agent請手動設定 CURSOR_AGENT_HOST_BIN"; \
fi; \
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 "Provider 設定範例:"
@echo " make env PROVIDER=cursor # 使用 Cursor預設"
@echo " make env PROVIDER=gemini-web # 使用 Gemini Web"
@echo " make env PROVIDER=gemini-web GEMINI_ACCOUNT_DIR=/path/to/sessions"
@echo " make env PROVIDER=gemini-web GEMINI_BROWSER_VISIBLE=true"
@echo " make opencode # 設定 OpenCode含 cursor 與 gemini-web provider"
@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"
@echo ""
@echo "使用 Gemini Web Provider"
@echo " 1. make env PROVIDER=gemini-web"
@echo " 2. gemini-login my-session # 登入並儲存 session"
@echo " 3. make run # 啟動代理"
@echo " 4. 在 OpenCode 設定 model: gemini/gemini-2.5-pro"