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:
parent
9e2a10b614
commit
3dc49bfc7d
40
.env.example
40
.env.example
|
|
@ -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
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
.idea/
|
||||
cursor-api-proxy*
|
||||
.env
|
||||
.env
|
||||
.opencode
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ type Config struct {
|
|||
|
||||
// 日誌
|
||||
SessionsLogPath string
|
||||
Verbose bool
|
||||
// Verbose is inherited from rest.RestConf
|
||||
|
||||
// Gemini
|
||||
GeminiAccountDir string
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue