refactor(task-0): initialize go-zero project structure

- Add go-zero dependency to go.mod
- Create api/chat.api with OpenAI-compatible types
- Create etc/chat.yaml configuration
- Update Makefile with goctl commands
- Generate go-zero scaffold (types, handlers, logic, svc)
- Move chat.go to cmd/chat/chat.go
- Add Config struct for go-zero (keep BridgeConfig for backward compatibility)
This commit is contained in:
王性驊 2026-04-03 17:15:35 +08:00
parent b18e3d82f0
commit 8b6abbbba7
23 changed files with 2740 additions and 2 deletions

View File

@ -3,6 +3,26 @@
# 編輯下方變數,然後執行 make env 產生 .env 檔 # 編輯下方變數,然後執行 make env 產生 .env 檔
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
# ── go-zero 代碼生成 ───────────────────────────
.PHONY: api api-doc gen fmt lint
api:
goctl api go -api api/chat.api -dir . --style go_zero
api-doc:
goctl api doc -api api/chat.api -dir docs/api/
gen: api
go mod tidy
go fmt ./...
fmt:
go fmt ./...
lint:
go vet ./...
go fmt ./...
# ── 伺服器設定 ───────────────────────────────── # ── 伺服器設定 ─────────────────────────────────
HOST ?= 127.0.0.1 HOST ?= 127.0.0.1
PORT ?= 8766 PORT ?= 8766

141
api/chat.api Normal file
View File

@ -0,0 +1,141 @@
syntax = "v1"
info (
title: "Cursor API Proxy"
desc: "OpenAI-compatible API proxy for Cursor/Gemini"
author: "cursor-api-proxy"
version: "1.0"
)
// ============ Types ============
type (
// Health
HealthRequest {}
HealthResponse {
Status string `json:"status"`
Version string `json:"version"`
}
// Models
ModelsRequest {}
ModelsResponse {
Object string `json:"object"`
Data []ModelData `json:"data"`
}
ModelData {
Id string `json:"id"`
Object string `json:"object"`
OwnedBy string `json:"owned_by"`
}
// Chat Completions
ChatCompletionRequest {
Model string `json:"model"`
Messages []Message `json:"messages"`
Stream bool `json:"stream,optional"`
Tools []Tool `json:"tools,optional"`
Functions []Function `json:"functions,optional"`
MaxTokens int `json:"max_tokens,optional"`
Temperature float64 `json:"temperature,optional"`
}
Message {
Role string `json:"role"`
Content interface{} `json:"content"`
}
Tool {
Type string `json:"type"`
Function ToolFunction `json:"function"`
}
ToolFunction {
Name string `json:"name"`
Description string `json:"description"`
Parameters interface{} `json:"parameters"`
}
Function {
Name string `json:"name"`
Description string `json:"description,optional"`
Parameters interface{} `json:"parameters,optional"`
}
ChatCompletionResponse {
Id string `json:"id"`
Object string `json:"object"`
Created int64 `json:"created"`
Model string `json:"model"`
Choices []Choice `json:"choices"`
Usage Usage `json:"usage"`
}
Choice {
Index int `json:"index"`
Message RespMessage `json:"message,optional"`
Delta Delta `json:"delta,optional"`
FinishReason string `json:"finish_reason"`
}
RespMessage {
Role string `json:"role"`
Content string `json:"content,optional"`
ToolCalls []ToolCall `json:"tool_calls,optional"`
}
Delta {
Role string `json:"role,optional"`
Content string `json:"content,optional"`
ReasoningContent string `json:"reasoning_content,optional"`
ToolCalls []ToolCall `json:"tool_calls,optional"`
}
ToolCall {
Index int `json:"index"`
Id string `json:"id"`
Type string `json:"type"`
Function FunctionCall `json:"function"`
}
FunctionCall {
Name string `json:"name"`
Arguments string `json:"arguments"`
}
Usage {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
}
// Anthropic Messages
AnthropicRequest {
Model string `json:"model"`
Messages []Message `json:"messages"`
MaxTokens int `json:"max_tokens"`
Stream bool `json:"stream,optional"`
System string `json:"system,optional"`
}
AnthropicResponse {
Id string `json:"id"`
Type string `json:"type"`
Role string `json:"role"`
Content []ContentBlock `json:"content"`
Model string `json:"model"`
Usage AnthropicUsage `json:"usage"`
}
ContentBlock {
Type string `json:"type"`
Text string `json:"text,optional"`
}
AnthropicUsage {
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
}
)
// ============ Routes ============
@server (
prefix: /v1
group: chat
)
service chat-api {
@handler Health
get /health returns (HealthResponse)
@handler Models
get /v1/models returns (ModelsResponse)
@handler ChatCompletions
post /v1/chat/completions (ChatCompletionRequest)
@handler AnthropicMessages
post /v1/messages (AnthropicRequest)
}

34
cmd/chat/chat.go Normal file
View File

@ -0,0 +1,34 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1
package main
import (
"flag"
"fmt"
"cursor-api-proxy/internal/config"
"cursor-api-proxy/internal/handler"
"cursor-api-proxy/internal/svc"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
)
var configFile = flag.String("f", "etc/chat-api.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
server := rest.MustNewServer(c.RestConf)
defer server.Stop()
ctx := svc.NewServiceContext(c)
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}

1069
docs/MIGRATION_PLAN.md Normal file

File diff suppressed because it is too large Load Diff

462
docs/REFACTOR_TASKS.md Normal file
View File

@ -0,0 +1,462 @@
# REFACTOR TASKS
重構任務拆分,支援 git worktree 並行開發。
---
## Task Overview
### 並行策略
```
時間軸 ──────────────────────────────────────────────────────────────►
Task 0: Init (必須先完成)
├── Task 1: Domain Layer ─────────────────────────┐
│ │
│ ┌── Task 2: Infrastructure Layer ────────────┤── 並行
│ │ │
│ └── Task 3: Repository Layer ────────────────┘
│ (依賴 Task 1)
├── Task 4: Provider Layer ──────────────────────┐
│ (依賴 Task 1) │
│ │── 可並行
├── Task 5: Usecase Layer ───────────────────────┤
│ (依賴 Task 3) │
│ │
├── Task 6: Adapter Layer ───────────────────────┘
│ (依賴 Task 1)
├── Task 7: Internal Layer ──────────────────────┐
│ (整合所有,必須最後) │
│ │── 序列
├── Task 8: CLI Tools │
│ │
└── Task 9: Cleanup & Tests ────────────────────┘
```
### Worktree 分支規劃
| 分支名稱 | 基於 | 任務 | 可並行 |
|---------|------|------|--------|
| `refactor/init` | `master` | Task 0 | ❌ |
| `refactor/domain` | `refactor/init` | Task 1 | ✅ |
| `refactor/infrastructure` | `refactor/init` | Task 2 | ✅ |
| `refactor/repository` | `refactor/domain` | Task 3 | ✅ |
| `refactor/provider` | `refactor/domain` | Task 4 | ✅ |
| `refactor/usecase` | `refactor/repository` | Task 5 | ✅ |
| `refactor/adapter` | `refactor/domain` | Task 6 | ✅ |
| `refactor/internal` | 合併所有 | Task 7 | ❌ |
| `refactor/cli` | `refactor/init` | Task 8 | ✅ |
| `refactor/cleanup` | 合併所有 | Task 9 | ❌ |
---
## Task 0: 初始化
### 分支
`refactor/init`
### 依賴
無(必須先完成)
### 小任務
- [ ] **0.1** 更新 go.mod (5min)
- `go get github.com/zeromicro/go-zero@latest`
- `go mod tidy`
- [ ] **0.2** 建立目錄 (1min)
- `mkdir -p api etc`
- [ ] **0.3** 建立 `api/chat.api` (15min)
- 定義 API types
- 定義 routes
- [ ] **0.4** 建立 `etc/chat.yaml` (5min)
- 配置參數
- [ ] **0.5** 更新 Makefile (10min)
- 新增 goctl 命令
- [ ] **0.6** 提交 (2min)
**預估時間**: ~30min
---
## Task 1: Domain Layer
### 分支
`refactor/domain`
### 依賴
Task 0 完成
### 小任務
- [ ] **1.1** 建立目錄結構 (1min)
- `pkg/domain/entity`
- `pkg/domain/repository`
- `pkg/domain/usecase`
- `pkg/domain/const`
- [ ] **1.2** `entity/message.go` (10min)
- Message, Tool, ToolFunction, ToolCall
- [ ] **1.3** `entity/chunk.go` (5min)
- StreamChunk, ChunkType
- [ ] **1.4** `entity/account.go` (5min)
- Account, AccountStat
- [ ] **1.5** `repository/account.go` (10min)
- AccountPool interface
- [ ] **1.6** `repository/provider.go` (5min)
- Provider interface
- [ ] **1.7** `usecase/chat.go` (15min)
- ChatUsecase interface
- [ ] **1.8** `usecase/agent.go` (5min)
- AgentRunner interface
- [ ] **1.9** `const/models.go` (10min)
- Model 常數
- [ ] **1.10** `const/errors.go` (5min)
- 錯誤定義
- [ ] **1.11** 提交 (2min)
**預估時間**: ~2h
---
## Task 2: Infrastructure Layer
### 分支
`refactor/infrastructure`
### 依賴
Task 0 完成(可與 Task 1 並行)
### 小任務
- [ ] **2.1** 建立目錄 (2min)
- `pkg/infrastructure/{process,parser,httputil,logger,env,workspace,winlimit}`
- [ ] **2.2** 遷移 process (10min)
- runner.go, kill_unix.go, kill_windows.go, process_test.go
- [ ] **2.3** 遷移 parser (5min)
- stream.go, stream_test.go
- [ ] **2.4** 遷移 httputil (5min)
- httputil.go, httputil_test.go
- [ ] **2.5** 遷移 logger (5min)
- logger.go
- [ ] **2.6** 遷移 env (5min)
- env.go, env_test.go
- [ ] **2.7** 遷移 workspace (5min)
- workspace.go
- [ ] **2.8** 遷移 winlimit (5min)
- winlimit.go, winlimit_test.go
- [ ] **2.9** 驗證編譯 (5min)
- [ ] **2.10** 提交 (2min)
**預估時間**: ~1h
---
## Task 3: Repository Layer
### 分支
`refactor/repository`
### 依賴
Task 1 完成
### 小任務
- [ ] **3.1** 建立目錄 (1min)
- [ ] **3.2** 遷移 account.go (20min)
- AccountPool 實作
- 移除全局變數
- [ ] **3.3** 遷移 provider.go (10min)
- Provider 工廠
- [ ] **3.4** 遷移測試 (5min)
- [ ] **3.5** 驗證編譯 (5min)
- [ ] **3.6** 提交 (2min)
**預估時間**: ~1h
---
## Task 4: Provider Layer
### 分支
`refactor/provider`
### 依賴
Task 1 完成
### 小任務
- [ ] **4.1** 建立目錄 (1min)
- `pkg/provider/cursor`
- `pkg/provider/geminiweb`
- [ ] **4.2** 遷移 cursor provider (5min)
- [ ] **4.3** 遷移 geminiweb provider (10min)
- [ ] **4.4** 更新 import (5min)
- [ ] **4.5** 驗證編譯 (5min)
- [ ] **4.6** 提交 (2min)
**預估時間**: ~30min
---
## Task 5: Usecase Layer
### 分支
`refactor/usecase`
### 依賴
Task 3 完成
### 小任務
- [ ] **5.1** 建立目錄 (1min)
- [ ] **5.2** 建立 chat.go (30min)
- 核心聊天邏輯
- [ ] **5.3** 遷移 agent.go (20min)
- runner, token, cmdargs, maxmode
- [ ] **5.4** 遷移 sanitizer (10min)
- [ ] **5.5** 遷移 toolcall (10min)
- [ ] **5.6** 驗證編譯 (5min)
- [ ] **5.7** 提交 (2min)
**預估時間**: ~2h
---
## Task 6: Adapter Layer
### 分支
`refactor/adapter`
### 依賴
Task 1 完成
### 小任務
- [ ] **6.1** 建立目錄 (1min)
- [ ] **6.2** 遷移 openai adapter (10min)
- [ ] **6.3** 遷移 anthropic adapter (10min)
- [ ] **6.4** 更新 import (5min)
- [ ] **6.5** 驗證編譯 (5min)
- [ ] **6.6** 提交 (2min)
**預估時間**: ~30min
---
## Task 7: Internal Layer
### 分支
`refactor/internal`
### 依賴
Task 1-6 全部完成
### 小任務
- [ ] **7.1** 合併所有分支 (5min)
- [ ] **7.2** 更新 config/config.go (15min)
- 使用 rest.RestConf
- [ ] **7.3** 建立 svc/servicecontext.go (30min)
- DI 容器
- [ ] **7.4** 建立 logic/ (1h)
- chatcompletionlogic.go
- geminichatlogic.go
- anthropiclogic.go
- healthlogic.go
- modelslogic.go
- [ ] **7.5** 建立 handler/ (1h)
- 自訂 SSE handler
- [ ] **7.6** 建立 middleware/ (20min)
- auth.go
- recovery.go
- [ ] **7.7** 建立 types/ (5min)
- goctl 生成
- [ ] **7.8** 更新 import (30min)
- 批量更新
- [ ] **7.9** 驗證編譯 (10min)
- [ ] **7.10** 提交 (2min)
**預估時間**: ~4h
---
## Task 8: CLI Tools
### 分支
`refactor/cli`
### 依賴
Task 0 完成
### 小任務
- [ ] **8.1** 建立目錄 (1min)
- [ ] **8.2** 遷移 CLI 工具 (10min)
- [ ] **8.3** 遷移 gemini-login (5min)
- [ ] **8.4** 更新 import (5min)
- [ ] **8.5** 提交 (2min)
**預估時間**: ~30min
---
## Task 9: Cleanup & Tests
### 分支
`refactor/cleanup`
### 依賴
Task 7 完成
### 小任務
- [ ] **9.1** 移除舊目錄 (5min)
- [ ] **9.2** 更新 import (30min)
- 批量 sed
- [ ] **9.3** 建立 cmd/chat/chat.go (10min)
- [ ] **9.4** SSE 整合測試 (2h)
- [ ] **9.5** 回歸測試 (1h)
- [ ] **9.6** 更新 README (15min)
- [ ] **9.7** 提交 (2min)
**預估時間**: ~4h
---
## 並行執行計劃
### Wave 1 (可完全並行)
```
Terminal 1: Task 0 (init) → 30min
Terminal 2: (等待 Task 0)
```
### Wave 2 (可完全並行)
```
Terminal 1: Task 1 (domain) → 2h
Terminal 2: Task 2 (infrastructure) → 1h
Terminal 3: Task 8 (cli) → 30min
```
### Wave 3 (可部分並行)
```
Terminal 1: Task 3 (repository) → 1h (依賴 Task 1)
Terminal 2: Task 4 (provider) → 30min (依賴 Task 1)
Terminal 3: Task 6 (adapter) → 30min (依賴 Task 1)
Terminal 4: (等待 Task 3)
```
### Wave 4 (可部分並行)
```
Terminal 1: Task 5 (usecase) → 2h (依賴 Task 3)
Terminal 2: (等待 Task 5)
```
### Wave 5 (序列)
```
Task 7 (internal) → 4h
Task 9 (cleanup) → 4h
```
**總時間估計**:
- 完全序列: ~15h
- 並行執行: ~9h
- 節省: ~40%
---
## Git Worktree 指令
```bash
# 創建 worktrees
git worktree add ../worktrees/init -b refactor/init
git worktree add ../worktrees/domain -b refactor/domain
git worktree add ../worktrees/infrastructure -b refactor/infrastructure
git worktree add ../worktrees/repository -b refactor/repository
git worktree add ../worktrees/provider -b refactor/provider
git worktree add ../worktrees/usecase -b refactor/usecase
git worktree add ../worktrees/adapter -b refactor/adapter
git worktree add ../worktrees/cli -b refactor/cli
# 並行工作
cd ../worktrees/domain && # Terminal 1
cd ../worktrees/infrastructure && # Terminal 2
cd ../worktrees/cli && # Terminal 3
# 清理 worktrees
git worktree remove ../worktrees/init
git worktree remove ../worktrees/domain
# ... 等等
```
---
**文件版本**: v1.0
**建立日期**: 2026-04-03

312
docs/TODOS.md Normal file
View File

@ -0,0 +1,312 @@
# TODOS
重構 cursor-api-proxy → go-zero + DDD Architecture 的待辦事項。
---
## Phase 1: API 定義與骨架生成
### DONE
- [x] 建立 `api/chat.api` 定義檔
- [x] 建立 `etc/chat.yaml` 配置檔
- [x] 生成代碼骨架
- [x] 移動 `chat.go``cmd/chat/`
### TODO
#### TODO-1: 全局變數遷移清單
- **What**: 建立全局變數到 ServiceContext 的遷移清單
- **Why**: 現有代碼有多個全局變數,遷移時容易遺漏
- **Files**:
- `internal/pool/pool.go:36-38``globalPool`, `globalMu` → ServiceContext
- `internal/process/process.go:117``MaxModeFn` → ServiceContext
- `internal/handlers/chat.go:28-29``rateLimitRe`, `retryAfterRe` → ServiceContext 或常數
- `internal/models/cursormap.go:8,47,51` → 正則表達式常數化
- **Decision**: ServiceContext 注入
- **Effort**: human ~2h / CC ~30min
- **Depends on**: Phase 2 (Domain 層建立)
- **Status**: pending
#### TODO-2: go.mod 更新
- **What**: 添加 go-zero 依賴到 go.mod
- **Why**: 現有 go.mod 沒有 go-zero 依賴
- **Command**: `go get github.com/zeromicro/go-zero@latest`
- **Decision**: 使用最新穩定版
- **Effort**: human ~5min / CC ~1min
- **Depends on**: Phase 1 開始前
- **Status**: pending
#### TODO-3: Makefile 更新
- **What**: 更新 Makefile 以支援 go-zero 的建置流程
- **Why**: 需要新增 goctl 命令和整合現有 env/run 命令
- **Commands to add**:
```makefile
.PHONY: api
api:
goctl api go -api api/chat.api -dir . --style go_zero
.PHONY: api-doc
api-doc:
goctl api doc -api api/chat.api -dir docs/
.PHONY: gen
gen: api
go mod tidy
```
- **Decision**: 需要追蹤
- **Effort**: human ~30min / CC ~10min
- **Depends on**: Phase 1 (API 定義與骨架生成)
- **Status**: pending
---
## Phase 2: Domain 層建立
### DONE
- [ ] 建立 `pkg/domain/entity/`
- [ ] 建立 `pkg/domain/repository/`
- [ ] 建立 `pkg/domain/usecase/`
- [ ] 建立 `pkg/domain/const/`
### TODO
#### TODO-4: import 循環依賴檢測
- **What**: 在每個 Phase 完成後執行 `go build ./...` 檢測循環依賴
- **Why**: DDD 架構分層容易產生循環依賴
- **Potential cycles**:
- `pkg/usecase``pkg/domain/usecase`
- `pkg/repository``pkg/domain/repository`
- **Command**: `go build ./... && go test ./... -run=none`
- **Depends on**: 每個 Phase 完成後
- **Status**: pending
---
## Phase 8: Internal 層重組
### DONE
- [ ] 更新 `internal/config/config.go`
- [ ] 建立 `internal/svc/servicecontext.go`
- [ ] 建立 `internal/logic/`
- [ ] 建立 `internal/handler/`
- [ ] 建立 `internal/middleware/`
### TODO
#### TODO-5: SSE 整合測試
- **What**: 增加 SSE streaming 的端對端測試
- **Why**: SSE 是核心功能,自訂 handler 容易出錯,沒有測試覆蓋
- **Test cases**:
1. SSE streaming 請求正常返回
2. SSE client disconnect 正確處理
3. SSE timeout 正確處理
4. 非串流請求轉 SSE 格式
- **Implementation**:
```go
// tests/integration/sse_test.go
func TestSSEStreaming(t *testing.T) {
// 使用 httptest 模擬 SSE 客戶端
// 驗證 data: [DONE] 正確返回
}
```
- **Decision**: 使用 `rest.WithCustom` 路由
- **Effort**: human ~2h / CC ~30min
- **Depends on**: Phase 8 完成Internal 層重組)
- **Status**: pending
#### TODO-6: SSE Handler 實作
- **What**: 使用 `rest.WithCustom` 實作 SSE streaming handler
- **Why**: go-zero 標準 handler 不支援 SSE需要自訂
- **Implementation**:
```go
// internal/handler/chat_handler.go
func NewChatHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// SSE 設定
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
// 委託給 usecase
svcCtx.ChatUsecase.Stream(r.Context(), input, callback)
}
}
```
- **Decision**: 使用 `rest.WithCustom` 路由
- **Effort**: human ~2h / CC ~30min
- **Depends on**: Phase 6 (Usecase 層建立)
- **Status**: pending
---
## Phase 10: 清理與測試
### DONE
- [ ] 移除舊目錄
- [ ] 更新 import 路徑
- [ ] 執行測試
### TODO
#### TODO-7: 測試文件遷移
- **What**: 測試文件跟隨源碼遷移到 pkg/
- **Why**: 測試應該與源碼在同一目錄
- **Files to migrate**:
- `internal/httputil/httputil_test.go``pkg/infrastructure/httputil/`
- `internal/config/config_test.go``internal/config/` (保留)
- `internal/sanitize/sanitize_test.go``pkg/usecase/`
- `internal/models/cursormap_test.go``pkg/domain/const/`
- `internal/models/cursorcli_test.go``pkg/domain/const/`
- `internal/parser/stream_test.go``pkg/infrastructure/parser/`
- `internal/env/env_test.go``pkg/infrastructure/env/`
- `internal/winlimit/winlimit_test.go``pkg/infrastructure/winlimit/`
- `internal/anthropic/anthropic_test.go``pkg/adapter/anthropic/`
- `internal/pool/pool_test.go``pkg/repository/`
- `internal/openai/openai_test.go``pkg/adapter/openai/`
- `internal/process/process_test.go``pkg/infrastructure/process/`
- **Decision**: 測試遷移到 pkg/
- **Effort**: human ~1h / CC ~10min
- **Depends on**: Phase 3-7 完成
- **Status**: pending
#### TODO-8: ServiceContext 單例 Pool
- **What**: AccountPool 使用單例模式,透過 sync.Once 確保只初始化一次
- **Why**: 避免每次請求創建新 Pool 的開銷
- **Implementation**:
```go
// pkg/repository/account.go
var (
globalPool *AccountPool
globalPoolOnce sync.Once
)
func GetAccountPool(configDirs []string) *AccountPool {
globalPoolOnce.Do(func() {
globalPool = NewAccountPool(configDirs)
})
return globalPool
}
```
- **Decision**: 使用單例 Pool
- **Effort**: human ~30min / CC ~10min
- **Depends on**: Phase 4 (Repository 層實作)
- **Status**: pending
---
## Phase 獨立 TODO
### TODO-9: 回歸測試自動化
- **What**: 建立自動化回歸測試腳本
- **Why**: 確保每次遷移後功能正常
- **Script**:
```bash
# scripts/regression-test.sh
#!/bin/bash
set -e
echo "=== Health check ==="
curl -s http://localhost:8080/health | jq .
echo "=== Models list ==="
curl -s http://localhost:8080/v1/models | jq .
echo "=== Chat completion (non-streaming) ==="
curl -s -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"test","messages":[{"role":"user","content":"hi"}],"stream":false}' | jq .
echo "=== Chat completion (streaming) ==="
curl -s -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"test","messages":[{"role":"user","content":"hi"}],"stream":true}'
```
- **Depends on**: Phase 10 完成
- **Status**: pending
---
## Summary
| TODO | Phase | Effort | Status |
|------|-------|--------|--------|
| TODO-1: 全局變數遷移清單 | Phase 2 | 2h | pending |
| TODO-2: go.mod 更新 | Phase 1 | 5min | pending |
| TODO-3: Makefile 更新 | Phase 1 | 30min | pending |
| TODO-4: import 循環依賴檢測 | Each Phase | 5min | pending |
| TODO-5: SSE 整合測試 | Phase 8 | 2h | pending |
| TODO-6: SSE Handler 實作 | Phase 8 | 2h | pending |
| TODO-7: 測試文件遷移 | Phase 10 | 1h | pending |
| TODO-8: ServiceContext 單例 Pool | Phase 4 | 30min | pending |
| TODO-9: 回歸測試自動化 | Phase 10 | 30min | pending |
---
## Dependencies Graph
```
Phase 1 (API 定義)
├── TODO-2: go.mod 更新 (必须在開始前完成)
├── TODO-3: Makefile 更新
Phase 2 (Domain 層)
├── TODO-1: 全局變數遷移清單
├── TODO-4: import 循環依賴檢測
Phase 3 (Infrastructure 層)
├── TODO-4: import 循環依賴檢測
Phase 4 (Repository 層)
├── TODO-8: ServiceContext 單例 Pool
├── TODO-4: import 循環依賴檢測
Phase 5 (Provider 層)
├── TODO-4: import 循環依賴檢測
Phase 6 (Usecase 層)
├── TODO-4: import 循環依賴檢測
Phase 7 (Adapter 層)
├── TODO-4: import 循環依賴檢測
Phase 8 (Internal 層)
├── TODO-5: SSE 整合測試
├── TODO-6: SSE Handler 實作
├── TODO-4: import 循環依賴檢測
Phase 9 (CLI 工具)
├── TODO-4: import 循環依賴檢測
Phase 10 (清理與測試)
├── TODO-7: 測試文件遷移
├── TODO-9: 回歸測試自動化
├── TODO-4: import 循環依賴檢測
完成
```
---
**文件版本**: v1.0
**建立日期**: 2026-04-03
**最後更新**: 2026-04-03

3
etc/chat-api.yaml Normal file
View File

@ -0,0 +1,3 @@
Name: chat-api
Host: 0.0.0.0
Port: 8888

45
etc/chat.yaml Normal file
View File

@ -0,0 +1,45 @@
Name: chat-api
Host: ${CURSOR_BRIDGE_HOST:0.0.0.0}
Port: ${CURSOR_BRIDGE_PORT:8080}
# API Key 驗證(可選)
# Auth:
# AccessSecret: ${CURSOR_API_KEY:}
# AccessExpire: 86400
# Cursor 配置
AgentBin: ${CURSOR_AGENT_BIN:cursor-agent}
DefaultModel: ${CURSOR_DEFAULT_MODEL:claude-3.5-sonnet}
Provider: ${CURSOR_PROVIDER:cursor}
# 超時設定
TimeoutMs: ${CURSOR_TIMEOUT_MS:300000}
# 多帳號池
ConfigDirs:
- ${HOME}/.cursor-api-proxy/accounts/default
MultiPort: false
# TLS
TLSCertPath: ${CURSOR_TLS_CERT_PATH:}
TLSKeyPath: ${CURSOR_TLS_KEY_PATH:}
# 日誌
SessionsLogPath: ${CURSOR_SESSIONS_LOG_PATH:}
Verbose: ${CURSOR_VERBOSE:false}
# Gemini 設定
GeminiAccountDir: ${GEMINI_ACCOUNT_DIR:}
GeminiBrowserVisible: ${GEMINI_BROWSER_VISIBLE:false}
GeminiMaxSessions: ${GEMINI_MAX_SESSIONS:10}
# 工作區設定
Workspace: ${CURSOR_WORKSPACE:}
ChatOnlyWorkspace: ${CURSOR_CHAT_ONLY_WORKSPACE:true}
WinCmdlineMax: ${CURSOR_WIN_CMDLINE_MAX:32768}
# Agent 設定
Force: ${CURSOR_FORCE:false}
ApproveMcps: ${CURSOR_APPROVE_MCPS:false}
MaxMode: ${CURSOR_MAX_MODE:false}
StrictModel: ${CURSOR_STRICT_MODEL:true}

46
go.mod
View File

@ -3,26 +3,68 @@ module cursor-api-proxy
go 1.25.0 go 1.25.0
require ( require (
github.com/go-rod/rod v0.116.2
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/playwright-community/playwright-go v0.5700.1
github.com/zeromicro/go-zero v1.10.1
modernc.org/sqlite v1.48.0 modernc.org/sqlite v1.48.0
) )
require ( require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/deckarep/golang-set/v2 v2.8.0 // indirect github.com/deckarep/golang-set/v2 v2.8.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.5 // indirect github.com/go-jose/go-jose/v3 v3.0.5 // indirect
github.com/go-rod/rod v0.116.2 // indirect github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-stack/stack v1.8.1 // indirect github.com/go-stack/stack v1.8.1 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/grafana/pyroscope-go v1.2.8 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/playwright-community/playwright-go v0.5700.1 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect
github.com/pelletier/go-toml/v2 v2.3.0 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/titanous/json5 v1.0.0 // indirect
github.com/ysmood/fetchup v0.2.3 // indirect github.com/ysmood/fetchup v0.2.3 // indirect
github.com/ysmood/goob v0.4.0 // indirect github.com/ysmood/goob v0.4.0 // indirect
github.com/ysmood/got v0.40.0 // indirect github.com/ysmood/got v0.40.0 // indirect
github.com/ysmood/gson v0.7.3 // indirect github.com/ysmood/gson v0.7.3 // indirect
github.com/ysmood/leakless v0.9.0 // indirect github.com/ysmood/leakless v0.9.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel v1.40.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 // indirect
go.opentelemetry.io/otel/exporters/zipkin v1.40.0 // indirect
go.opentelemetry.io/otel/metric v1.40.0 // indirect
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
go.opentelemetry.io/otel/trace v1.40.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/sys v0.42.0 // indirect golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.34.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/grpc v1.79.3 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
modernc.org/libc v1.70.0 // indirect modernc.org/libc v1.70.0 // indirect
modernc.org/mathutil v1.7.1 // indirect modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect modernc.org/memory v1.11.0 // indirect

131
go.sum
View File

@ -1,44 +1,148 @@
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ=
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/go-jose/go-jose/v3 v3.0.5 h1:BLLJWbC4nMZOfuPVxoZIxeYsn6Nl2r1fITaJ78UQlVQ= github.com/go-jose/go-jose/v3 v3.0.5 h1:BLLJWbC4nMZOfuPVxoZIxeYsn6Nl2r1fITaJ78UQlVQ=
github.com/go-jose/go-jose/v3 v3.0.5/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-jose/go-jose/v3 v3.0.5/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA= github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA=
github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg= github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg=
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grafana/pyroscope-go v1.2.8 h1:UvCwIhlx9DeV7F6TW/z8q1Mi4PIm3vuUJ2ZlCEvmA4M=
github.com/grafana/pyroscope-go v1.2.8/go.mod h1:SSi59eQ1/zmKoY/BKwa5rSFsJaq+242Bcrr4wPix1g8=
github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og=
github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=
github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c=
github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM=
github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/playwright-community/playwright-go v0.5700.1 h1:PNFb1byWqrTT720rEO0JL88C6Ju0EmUnR5deFLvtP/U= github.com/playwright-community/playwright-go v0.5700.1 h1:PNFb1byWqrTT720rEO0JL88C6Ju0EmUnR5deFLvtP/U=
github.com/playwright-community/playwright-go v0.5700.1/go.mod h1:MlSn1dZrx8rszbCxY6x3qK89ZesJUYVx21B2JnkoNF0= github.com/playwright-community/playwright-go v0.5700.1/go.mod h1:MlSn1dZrx8rszbCxY6x3qK89ZesJUYVx21B2JnkoNF0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/robertkrimen/otto v0.2.1 h1:FVP0PJ0AHIjC+N4pKCG9yCDz6LHNPCwi/GKID5pGGF0=
github.com/robertkrimen/otto v0.2.1/go.mod h1:UPwtJ1Xu7JrLcZjNWN8orJaM5n5YEtqL//farB5FlRY=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/titanous/json5 v1.0.0 h1:hJf8Su1d9NuI/ffpxgxQfxh/UiBFZX7bMPid0rIL/7s=
github.com/titanous/json5 v1.0.0/go.mod h1:7JH1M8/LHKc6cyP5o5g3CSaRj+mBrIimTxzpvmckH8c=
github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ= github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ=
github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns= github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns=
github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ=
github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18=
github.com/ysmood/gop v0.2.0 h1:+tFrG0TWPxT6p9ZaZs+VY+opCvHU8/3Fk6BaNv6kqKg=
github.com/ysmood/gop v0.2.0/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk=
github.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q= github.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q=
github.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg= github.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg=
github.com/ysmood/gotrace v0.6.0 h1:SyI1d4jclswLhg7SWTL6os3L1WOKeNn/ZtzVQF8QmdY=
github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM=
github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE=
github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg=
github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU= github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU=
github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zeromicro/go-zero v1.10.1 h1:1nM3ilvYx97GUqyaNH2IQPtfNyK7tp5JvN63c7m6QKU=
github.com/zeromicro/go-zero v1.10.1/go.mod h1:z41DXmO6gx/Se7Ow5UIwPxcUmpVj3ebhoNCcZ1gfp5k=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 h1:MzfofMZN8ulNqobCmCAVbqVL5syHw+eB2qPRkCMA/fQ=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0/go.mod h1:E73G9UFtKRXrxhBsHtG00TB5WxX57lpsQzogDkqBTz8=
go.opentelemetry.io/otel/exporters/zipkin v1.40.0 h1:zu+I4j+FdO6xIxBVPeuncQVbjxUM4LiMgv6GwGe9REE=
go.opentelemetry.io/otel/exporters/zipkin v1.40.0/go.mod h1:zS6cC4nFBYXbu18e7aLfMzubBjOiN7ZcROu477qtMf8=
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
@ -51,6 +155,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -61,6 +167,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -78,6 +185,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
@ -85,8 +194,30 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 h1:kBawHLSnx/mYHmRnNUf9d4CpjREbeZuxoSGOX/J+aYM=
k8s.io/utils v0.0.0-20260319190234-28399d86e0b5/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw= modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw=

View File

@ -2,8 +2,53 @@ package config
import ( import (
"cursor-api-proxy/internal/env" "cursor-api-proxy/internal/env"
"github.com/zeromicro/go-zero/rest"
) )
// Config for go-zero (generated by goctl)
type Config struct {
rest.RestConf
// Cursor 配置
AgentBin string
DefaultModel string
Provider string
TimeoutMs int
// 多帳號池
ConfigDirs []string
MultiPort bool
// TLS
TLSCertPath string
TLSKeyPath string
// 日誌
SessionsLogPath string
Verbose bool
// Gemini
GeminiAccountDir string
GeminiBrowserVisible bool
GeminiMaxSessions int
// 工作區
Workspace string
ChatOnlyWorkspace bool
WinCmdlineMax int
// Agent
Force bool
ApproveMcps bool
MaxMode bool
StrictModel bool
// API Key
RequiredKey string
}
// BridgeConfig for backward compatibility with existing code
type BridgeConfig struct { type BridgeConfig struct {
AgentBin string AgentBin string
Host string Host string
@ -31,6 +76,7 @@ type BridgeConfig struct {
GeminiMaxSessions int GeminiMaxSessions int
} }
// LoadBridgeConfig loads config from environment (for backward compatibility)
func LoadBridgeConfig(e env.EnvSource, cwd string) BridgeConfig { func LoadBridgeConfig(e env.EnvSource, cwd string) BridgeConfig {
loaded := env.LoadEnvConfig(e, cwd) loaded := env.LoadEnvConfig(e, cwd)
return BridgeConfig{ return BridgeConfig{

View File

@ -0,0 +1,31 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1
package chat
import (
"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"
)
func AnthropicMessagesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AnthropicRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := chat.NewAnthropicMessagesLogic(r.Context(), svcCtx)
err := l.AnthropicMessages(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
}
}

View File

@ -0,0 +1,31 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1
package chat
import (
"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"
)
func ChatCompletionsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ChatCompletionRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := chat.NewChatCompletionsLogic(r.Context(), svcCtx)
err := l.ChatCompletions(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
}
}

View File

@ -0,0 +1,24 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1
package chat
import (
"net/http"
"cursor-api-proxy/internal/logic/chat"
"cursor-api-proxy/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
)
func HealthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := chat.NewHealthLogic(r.Context(), svcCtx)
resp, err := l.Health()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@ -0,0 +1,24 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1
package chat
import (
"net/http"
"cursor-api-proxy/internal/logic/chat"
"cursor-api-proxy/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
)
func ModelsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := chat.NewModelsLogic(r.Context(), svcCtx)
resp, err := l.Models()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}

View File

@ -0,0 +1,41 @@
// Code generated by goctl. DO NOT EDIT.
// goctl 1.10.1
package handler
import (
"net/http"
chat "cursor-api-proxy/internal/handler/chat"
"cursor-api-proxy/internal/svc"
"github.com/zeromicro/go-zero/rest"
)
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodGet,
Path: "/health",
Handler: chat.HealthHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/v1/chat/completions",
Handler: chat.ChatCompletionsHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/v1/messages",
Handler: chat.AnthropicMessagesHandler(serverCtx),
},
{
Method: http.MethodGet,
Path: "/v1/models",
Handler: chat.ModelsHandler(serverCtx),
},
},
rest.WithPrefix("/v1"),
)
}

View File

@ -0,0 +1,33 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1
package chat
import (
"context"
"cursor-api-proxy/internal/svc"
"cursor-api-proxy/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AnthropicMessagesLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAnthropicMessagesLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AnthropicMessagesLogic {
return &AnthropicMessagesLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AnthropicMessagesLogic) AnthropicMessages(req *types.AnthropicRequest) error {
// todo: add your logic here and delete this line
return nil
}

View File

@ -0,0 +1,33 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1
package chat
import (
"context"
"cursor-api-proxy/internal/svc"
"cursor-api-proxy/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type ChatCompletionsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewChatCompletionsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ChatCompletionsLogic {
return &ChatCompletionsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ChatCompletionsLogic) ChatCompletions(req *types.ChatCompletionRequest) error {
// todo: add your logic here and delete this line
return nil
}

View File

@ -0,0 +1,33 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1
package chat
import (
"context"
"cursor-api-proxy/internal/svc"
"cursor-api-proxy/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type HealthLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewHealthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *HealthLogic {
return &HealthLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *HealthLogic) Health() (resp *types.HealthResponse, err error) {
// todo: add your logic here and delete this line
return
}

View File

@ -0,0 +1,33 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1
package chat
import (
"context"
"cursor-api-proxy/internal/svc"
"cursor-api-proxy/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type ModelsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewModelsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ModelsLogic {
return &ModelsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ModelsLogic) Models() (resp *types.ModelsResponse, err error) {
// todo: add your logic here and delete this line
return
}

View File

@ -0,0 +1,18 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1
package svc
import (
"cursor-api-proxy/internal/config"
)
type ServiceContext struct {
Config config.Config
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
}
}

132
internal/types/types.go Normal file
View File

@ -0,0 +1,132 @@
// Code generated by goctl. DO NOT EDIT.
// goctl 1.10.1
package types
type AnthropicRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
MaxTokens int `json:"max_tokens"`
Stream bool `json:"stream,optional"`
System string `json:"system,optional"`
}
type AnthropicResponse struct {
Id string `json:"id"`
Type string `json:"type"`
Role string `json:"role"`
Content []ContentBlock `json:"content"`
Model string `json:"model"`
Usage AnthropicUsage `json:"usage"`
}
type AnthropicUsage struct {
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
}
type ChatCompletionRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
Stream bool `json:"stream,optional"`
Tools []Tool `json:"tools,optional"`
Functions []Function `json:"functions,optional"`
MaxTokens int `json:"max_tokens,optional"`
Temperature float64 `json:"temperature,optional"`
}
type ChatCompletionResponse struct {
Id string `json:"id"`
Object string `json:"object"`
Created int64 `json:"created"`
Model string `json:"model"`
Choices []Choice `json:"choices"`
Usage Usage `json:"usage"`
}
type Choice struct {
Index int `json:"index"`
Message RespMessage `json:"message,optional"`
Delta Delta `json:"delta,optional"`
FinishReason string `json:"finish_reason"`
}
type ContentBlock struct {
Type string `json:"type"`
Text string `json:"text,optional"`
}
type Delta struct {
Role string `json:"role,optional"`
Content string `json:"content,optional"`
ReasoningContent string `json:"reasoning_content,optional"`
ToolCalls []ToolCall `json:"tool_calls,optional"`
}
type Function struct {
Name string `json:"name"`
Description string `json:"description,optional"`
Parameters interface{} `json:"parameters,optional"`
}
type FunctionCall struct {
Name string `json:"name"`
Arguments string `json:"arguments"`
}
type HealthRequest struct {
}
type HealthResponse struct {
Status string `json:"status"`
Version string `json:"version"`
}
type Message struct {
Role string `json:"role"`
Content interface{} `json:"content"`
}
type ModelData struct {
Id string `json:"id"`
Object string `json:"object"`
OwnedBy string `json:"owned_by"`
}
type ModelsRequest struct {
}
type ModelsResponse struct {
Object string `json:"object"`
Data []ModelData `json:"data"`
}
type RespMessage struct {
Role string `json:"role"`
Content string `json:"content,optional"`
ToolCalls []ToolCall `json:"tool_calls,optional"`
}
type Tool struct {
Type string `json:"type"`
Function ToolFunction `json:"function"`
}
type ToolCall struct {
Index int `json:"index"`
Id string `json:"id"`
Type string `json:"type"`
Function FunctionCall `json:"function"`
}
type ToolFunction struct {
Name string `json:"name"`
Description string `json:"description"`
Parameters interface{} `json:"parameters"`
}
type Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
}