fix: remove duplicate Verbose field and use raw JSON parsing for handlers

- Remove Verbose from Config struct (inherited from RestConf)
- Remove Verbose from yaml config to fix conflict
- Use raw JSON parsing in handlers for interface{} Content field
- Fix config tests
This commit is contained in:
王性驊 2026-04-04 12:49:32 +08:00
parent 9e2a10b614
commit 3dc49bfc7d
7 changed files with 58 additions and 69 deletions

View File

@ -1,40 +0,0 @@
# ──────────────────────────────────────────────────────────────
# cursor-api-proxy 設定範例
# 複製為 .env 後填入你的設定cp .env.example .env
# ──────────────────────────────────────────────────────────────
# ── 伺服器設定 ────────────────────────────────────────────────
# Docker 模式固定使用 0.0.0.0;本機直接執行時可改 127.0.0.1
CURSOR_BRIDGE_HOST=0.0.0.0
CURSOR_BRIDGE_PORT=8766
CURSOR_BRIDGE_API_KEY=
CURSOR_BRIDGE_TIMEOUT_MS=3600000
CURSOR_BRIDGE_MULTI_PORT=false
CURSOR_BRIDGE_VERBOSE=false
# ── Agent / 模型設定 ──────────────────────────────────────────
# Docker 模式:容器內 agent 路徑(預設掛載至 /usr/local/bin/agent
CURSOR_AGENT_BIN=/usr/local/bin/agent
CURSOR_BRIDGE_DEFAULT_MODEL=auto
CURSOR_BRIDGE_STRICT_MODEL=true
CURSOR_BRIDGE_MAX_MODE=false
CURSOR_BRIDGE_FORCE=false
CURSOR_BRIDGE_APPROVE_MCPS=false
# ── 工作區與帳號 ──────────────────────────────────────────────
CURSOR_BRIDGE_WORKSPACE=/workspace
CURSOR_BRIDGE_CHAT_ONLY_WORKSPACE=true
# 多帳號設定目錄(逗號分隔),留空則自動探索 ~/.cursor-api-proxy/accounts/
CURSOR_CONFIG_DIRS=
# ── TLS / HTTPS選用────────────────────────────────────────
CURSOR_BRIDGE_TLS_CERT=
CURSOR_BRIDGE_TLS_KEY=
# ── Docker 專用:宿主機路徑對映 ──────────────────────────────
# 宿主機上 Cursor agent 二進位檔的實際路徑
CURSOR_AGENT_HOST_BIN=/usr/local/bin/agent
# 宿主機上的帳號資料目錄(會掛載至容器的 /root/.cursor-api-proxy
CURSOR_ACCOUNTS_DIR=~/.cursor-api-proxy
# 宿主機上要掛載進容器的工作區目錄
WORKSPACE_DIR=/tmp/workspace

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
.idea/
cursor-api-proxy*
.env
.env
.opencode

View File

@ -1,12 +1,12 @@
Name: cursor-api-proxy
Name: api-proxy
Host: 0.0.0.0
Port: 8080
Port: 8766
# Cursor Agent 配置
AgentBin: cursor
DefaultModel: claude-3.5-sonnet
DefaultModel: claude-4.5-sonnet
Provider: cursor
TimeoutMs: 300000
TimeoutMs: 36000000
# 多帳號池配置
ConfigDirs:
@ -19,7 +19,7 @@ TLSKeyPath: ""
# 日誌
SessionsLogPath: ""
Verbose: false
# Verbose 使用 RestConf 預設值
# Gemini Web Provider 配置
GeminiAccountDir: ~/.cursor-api-proxy/gemini-accounts

View File

@ -29,7 +29,7 @@ type Config struct {
// 日誌
SessionsLogPath string
Verbose bool
// Verbose is inherited from rest.RestConf
// Gemini
GeminiAccountDir string

View File

@ -21,19 +21,17 @@ func TestConfigToBridgeConfig(t *testing.T) {
func TestConfigToBridgeConfigWithValues(t *testing.T) {
cfg := config.Config{
AgentBin: "cursor",
DefaultModel: "claude-3.5-sonnet",
Provider: "cursor",
TimeoutMs: 300000,
Force: true,
ApproveMcps: true,
StrictModel: true,
Workspace: "/tmp/test",
ChatOnlyWorkspace: true,
Verbose: true,
GeminiAccountDir: "/tmp/gemini",
GeminiBrowserVisible: true,
GeminiMaxSessions: 5,
AgentBin: "cursor",
DefaultModel: "claude-3.5-sonnet",
Provider: "cursor",
TimeoutMs: 300000,
Force: true,
ApproveMcps: true,
StrictModel: true,
Workspace: "/tmp/test",
ChatOnlyWorkspace: true,
GeminiAccountDir: "/tmp/gemini",
GeminiMaxSessions: 5,
}
bc := cfg.ToBridgeConfig()

View File

@ -4,20 +4,33 @@
package chat
import (
"encoding/json"
"io"
"net/http"
"cursor-api-proxy/internal/logic/chat"
"cursor-api-proxy/internal/svc"
"cursor-api-proxy/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"cursor-api-proxy/pkg/infrastructure/httputil"
)
func AnthropicMessagesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Read raw body first
rawBody, err := io.ReadAll(r.Body)
if err != nil {
httputil.WriteJSON(w, 400, map[string]interface{}{
"error": map[string]string{"type": "invalid_request_error", "message": "failed to read body"},
}, nil)
return
}
var req types.AnthropicRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := json.Unmarshal(rawBody, &req); err != nil {
httputil.WriteJSON(w, 400, map[string]interface{}{
"error": map[string]string{"type": "invalid_request_error", "message": "invalid JSON body"},
}, nil)
return
}
@ -31,7 +44,9 @@ func AnthropicMessagesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
} else {
err := l.AnthropicMessages(&req, w, r.Method, r.URL.Path)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
httputil.WriteJSON(w, 500, map[string]interface{}{
"error": map[string]string{"type": "api_error", "message": err.Error()},
}, nil)
}
}
}

View File

@ -4,20 +4,33 @@
package chat
import (
"encoding/json"
"io"
"net/http"
"cursor-api-proxy/internal/logic/chat"
"cursor-api-proxy/internal/svc"
"cursor-api-proxy/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"cursor-api-proxy/pkg/infrastructure/httputil"
)
func ChatCompletionsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Read raw body first
rawBody, err := io.ReadAll(r.Body)
if err != nil {
httputil.WriteJSON(w, 400, map[string]interface{}{
"error": map[string]string{"message": "failed to read body", "code": "bad_request"},
}, nil)
return
}
var req types.ChatCompletionRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := json.Unmarshal(rawBody, &req); err != nil {
httputil.WriteJSON(w, 400, map[string]interface{}{
"error": map[string]string{"message": "invalid JSON body", "code": "bad_request"},
}, nil)
return
}
@ -31,9 +44,11 @@ func ChatCompletionsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
} else {
resp, err := l.ChatCompletions(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
httputil.WriteJSON(w, 500, map[string]interface{}{
"error": map[string]string{"message": err.Error(), "code": "internal_error"},
}, nil)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
httputil.WriteJSON(w, 200, resp, nil)
}
}
}