Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ docs/PRIVATE_DEPLOYMENT.md
docs/private/
docs/capture-notes.md
docs/DEVELOPMENT.md
docs/REQUEST.md
docs/ARCHITECTURE.md
docs/DESIGN.md
docs/PLAN.md
docs/PROVIDERS.md
Expand Down
33 changes: 22 additions & 11 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,28 @@

Go + Node service that turns a local Qoder CLI login into an OpenAI-compatible API.

Keep writing-code docs in these files only:

| File | What belongs there |
|------|--------------------|
| `AGENTS.md` | Hard rules for agents. Short. |
| `docs/DESIGN.md` | Architecture, login, routing, console, design system. |
| `docs/PLAN.md` | Current milestone checklist. |
| `docs/PROVIDERS.md` | Future account-provider design. Not a current milestone. |
| `docs/PROVIDERS_TRAE_SOLO.md` | Trae CN Solo in-process adapter survey. Not a current milestone. |

Do not add new `TODO.md`, `NOTES.md`, or extra plan files. Extra design docs are `docs/PROVIDERS.md` (WorkBuddy / Qoder CN / future account types) and `docs/PROVIDERS_TRAE_SOLO.md` (Trae CN Solo). `docs/PROVIDERS_TRAE.md` is superseded; do not implement from it. User-facing install stays in `README.md` (Chinese) / `README_EN.md` (English). Local protocol facts stay in ignored `docs/capture-notes.md`. Host ops stay gitignored in `docs/PRIVATE_DEPLOYMENT.md`.
## Docs

Read this file first. Every other doc carries YAML frontmatter
(`id / title / scope / status / read-when / summary / related / last-updated`);
use `read-when` to decide whether to open it.

| File | Read when | What belongs there |
|------|-----------|--------------------|
| `AGENTS.md` | always | Hard rules for agents. Short. |
| `docs/ARCHITECTURE.md` | backend, protocol adapters, login, account routing, migrations, console IA, managed update | Architecture, runtime, login, routing contract, console IA |
| `docs/DESIGN.md` | any console UI work | Frontend design system: tokens, radii, type, HeroUI picks, copy |
| `docs/REQUEST.md` | routing, failover, cooldown, session affinity, error taxonomy | Per-request pick / failover / cooldown contract |
| `docs/PLAN.md` | before starting a milestone | Current milestone checklist |
| `docs/PROVIDERS.md` | adding / designing a provider | Future account-provider design. Not a current milestone. |
| `docs/PROVIDERS_TRAE_SOLO.md` | Trae CN Solo work | Trae CN Solo in-process adapter survey. Not a current milestone. |
| `docs/DEVELOPMENT.md` | build / test / release | Local build loop and maintainer release workflow |
| `docs/capture-notes.md` (ignored) | protocol facts | Local redacted protocol facts |
| `docs/PRIVATE_DEPLOYMENT.md` (ignored) | host ops | Host ops runbook |

Keep these files only. Do not add new `TODO.md`, `NOTES.md`, or extra plan files.
`docs/PROVIDERS_TRAE.md` is superseded; do not implement from it. User-facing
install stays in `README.md` (Chinese) / `README_EN.md` (English).

## Do

Expand Down
40 changes: 13 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,17 @@

## 功能

- **OpenAI / Anthropic 兼容代理**:`/v1/chat/completions`、`/v1/responses`、`/v1/messages`、`/v1/models`;支持流式/非流式、文本与函数工具调用;图片能力取决于 provider(当前 Qoder 支持,WorkBuddy / Trae 不支持);文件输入会明确拒绝。`messages` / `responses` 当前为无状态适配层,不支持服务端会话或上游专属工具。
- **多渠道账号池**:Qoder 国际版 / 国内版、WorkBuddy 国际版 / 国内版、Trae 国内版 Solo,以及实验性 Devin;地域隔离、账号固定、并发限制、冷却与同族故障切换
- **代理出口**:支持统一 HTTP(S) 代理,也支持账号级覆盖;账号可用 `direct` / `none` 显式直连。SOCKS5 仅支持 WorkBuddy / Trae / Devin 的账号级代理,Qoder 账号级代理只支持 HTTP(S)
- **账号级常驻运行时**:Qoder 账号使用独立 Node 进程、HOME 与 WASM 上下文;WorkBuddy / Trae / Devin 使用进程内 HTTP/SSE(或 Connect)适配器。登录态、云端连接和账号隔离由各 provider 的运行时负责
- **OpenAI / Anthropic 兼容代理**:`/v1/chat/completions`、`/v1/responses`、`/v1/messages`、`/v1/models`;支持流式/非流式、文本与函数工具调用。图片能力取决于 provider(当前 Qoder 支持,WorkBuddy / Trae 不支持),文件输入会明确拒绝。`messages` / `responses` 为无状态适配层,不支持服务端会话或上游专属工具
- **多渠道账号池**:地域隔离、账号固定、并发限制、冷却与同族故障切换
- **代理出口**:统一 HTTP(S) 代理,支持账号级覆盖,可用 `direct` / `none` 显式直连。SOCKS5 仅支持 WorkBuddy / Trae / Devin 的账号级代理,Qoder 账号级只支持 HTTP(S)
- **按 provider 支持多种登录方式**:浏览器 Device Flow OAuth、PAT,以及适用 provider 的凭证导入/导出
- **Web 控制台**:账号、模型、接入、请求历史与运行时日志明暗主题
- **部署与运维**:Docker Compose 单容器、安全托管更新(升级前快照、失败自动回滚、直接最新稳定版、可回滚最近三个稳定版)、默认只监听 `127.0.0.1`
- **跨平台**:`linux/amd64` / `linux/arm64` 镜像macOS、Windows 通过 Docker Desktop 运行
- **Web 控制台**:账号、模型、接入、请求历史与运行时日志;请求历史可按账号过滤,并查看状态、延迟、Token 与用量统计;明暗主题
- **账号保活**:WorkBuddy 每日签到与 token 保活(账号级开关,默认关闭;控制台可立即签到 / 刷新积分)
- **部署与运维**:Docker Compose 单容器、安全托管更新(升级前快照、失败自动回滚、直接最新稳定版、可回滚最近三个稳定版)、默认只监听 `127.0.0.1`;提供 `linux/amd64` / `linux/arm64` 镜像macOS、Windows 通过 Docker Desktop 运行

## 快速开始

**强烈建议用 Docker 部署。** 发布镜像、控制台托管更新(升级前快照、失败回滚、直接最新稳定版)都按单容器 Compose 安装来设计;从源码直接跑 Go / Node 不在这条更新路径上。
**强烈建议用 Docker 部署。** 发布镜像与控制台托管更新都按单容器 Compose 安装设计;从源码直接跑 Go / Node 不在这条更新路径上。

依赖:Docker(macOS / Windows 用 Docker Desktop,Linux 用 Docker Engine + Compose),以及一个你自己控制的 Qoder、WorkBuddy、Trae 或实验性 Devin 账号。Windows 的 Docker Desktop 必须切换到 Linux containers。

Expand All @@ -51,7 +50,7 @@ Base URL: http://127.0.0.1:3010/v1
API Key: <首次启动时生成的 Key>
```

不指定账号时,调度器自动选择可用账号;需要固定账号时加请求头 `X-Qoder-Account: acc_...`(历史命名,适用于所有 provider)。除 Chat Completions 外,也可使用 Anthropic `POST /v1/messages` 与 OpenAI `POST /v1/responses`;两者要求请求携带完整对话,不支持 `previous_response_id` / `conversation` 服务端续接。同一段对话默认按首条用户消息(含纯图片)粘到同一个账号也可显式设置 `X-CLI2API-Session`。curl / PowerShell 示例见 [部署说明](deploy/README.md)。
不指定账号时,调度器自动选择可用账号;需要固定账号时加请求头 `X-Qoder-Account: acc_...`(历史命名,适用于所有 provider)。同一段对话默认按首条用户消息(含纯图片)粘到同一个账号也可显式设置 `X-CLI2API-Session`。curl / PowerShell 示例见 [部署说明](deploy/README.md)。

## 工作方式

Expand All @@ -67,14 +66,12 @@ API Key: <首次启动时生成的 Key>
<img src="./docs/assets/readme/console-window-zh.svg" width="100%" alt="CLI2API 控制台 Accounts 页:每个账号显示登录方式、就绪状态与额度,右侧 Access 面板提供 Base URL 与快速验证">
</p>

账号、模型、接入和日志都在同一个 Web 控制台里管理:每个账号按 provider 支持的方式登录(浏览器 OAuth、PAT 或凭证导入),就绪状态和额度一目了然,Access 页可以直接复制 Base URL 并做一次快速验证。
账号、模型、接入和日志都在同一个 Web 控制台里管理:就绪状态和额度一目了然,Access 页可以直接复制 Base URL 并做一次快速验证。

## 适合什么场景

- 想在本机或私有服务器上统一接入 Qoder / WorkBuddy / Trae(以及实验性 Devin)
- 已经在使用 OpenAI API 格式的客户端或脚本
- 需要在多个账号之间自动路由和故障切换
- 想保留登录能力,同时避免每个请求启动完整 CLI Agent
- 想在本机或私有服务器上统一接入各上游账号,并在多个账号之间自动路由和故障切换
- 已经在使用 OpenAI API 格式的客户端或脚本,且不想每个请求都启动完整 CLI Agent

CLI2API 是本地网关:不提供账号、额度或官方 API 服务,不做多用户共享转售。

Expand All @@ -84,13 +81,6 @@ CLI2API 是本地网关:不提供账号、额度或官方 API 服务,不做

- Qoder 国内版与 WorkBuddy 的真账号验收(登录、故障切换、混合账号池)

**已支持**

- Anthropic `/v1/messages` 与 OpenAI `/v1/responses` 的无状态文本 / 函数工具适配层
- WorkBuddy 每日签到与 token 保活(账号级开关,默认关闭;控制台可立即签到 / 刷新积分)
- 会话粘性路由:默认按对话内容(首条用户消息,含纯图片)复用同一账号,也可设置 `X-CLI2API-Session`,并在失败时按规则切换
- 请求历史按账号过滤,以及请求状态、延迟、Token 和用量统计

**长期**

- 更多上游渠道(Cursor 等)
Expand All @@ -105,13 +95,9 @@ CLI2API 是本地网关:不提供账号、额度或官方 API 服务,不做

默认只监听 `127.0.0.1:3010`;除 `/health`、静态前端资源和 OpenAI 兼容 `/v1/*` 的 CORS 预检 `OPTIONS` 外,所有 API 与控制台数据接口均需要 API Key。不要提交 `.qoder`、Token、Cookie、登录 Blob 或原始抓包;凭证导出是显式敏感操作,请妥善保管导出文件。上游 API 或 CLI 更新可能导致兼容性变化,项目会固定并检查 qodercli 版本。发现安全问题请按 [SECURITY.md](SECURITY.md) 私下报告。

## 社区

中文讨论见 [LINUX DO](https://linux.do)。缺陷和功能请求请继续走 GitHub [Issue](https://github.com/caigee-cmd/cli2api/issues)。

## 贡献
## 社区与贡献

欢迎提交 Issue、改进文档和 Pull Request,规则见 [CONTRIBUTING.md](CONTRIBUTING.md)。
中文讨论见 [LINUX DO](https://linux.do)。缺陷和功能请求请走 GitHub [Issue](https://github.com/caigee-cmd/cli2api/issues);文档改进与 Pull Request [CONTRIBUTING.md](CONTRIBUTING.md)。

## 许可证

Expand Down
36 changes: 11 additions & 25 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,16 @@ Long-lived account runtimes, multi-account scheduling. Deploy with Docker; that
## Features

- **OpenAI / Anthropic-compatible proxy**: `/v1/chat/completions`, `/v1/responses`, `/v1/messages`, `/v1/models` — streaming/non-streaming text and function tools; image support depends on the provider (currently supported by Qoder, not WorkBuddy / Trae); file inputs are rejected explicitly. `messages` / `responses` are stateless adapters today and do not support server-side conversations or upstream-specific tools.
- **Multi-channel account pool**: Qoder Global / Qoder CN, WorkBuddy Global / WorkBuddy CN, Trae CN Solo, plus experimental Devin — region isolation, account pinning, concurrency limits, cooldowns, and same-family failover
- **Multi-channel account pool**: region isolation, account pinning, concurrency limits, cooldowns, and same-family failover
- **Outbound proxies**: set one global HTTP(S) proxy or override it per account; use `direct` / `none` for explicit direct access. SOCKS5 is available for WorkBuddy / Trae / Devin account-level proxies only; Qoder account-level proxies are HTTP(S) only
- **Account-level runtimes**: Qoder accounts use an isolated Node process, HOME, and WASM context; WorkBuddy / Trae / Devin use in-process HTTP/SSE (or Connect) adapters. Each provider owns its login and upstream runtime boundary
- **Provider-specific login methods**: browser Device Flow OAuth, PAT, and credential import/export where supported
- **Web console**: accounts, models, access, request history, and runtime logs, with light and dark themes
- **Deployment and ops**: single Docker Compose container, safe managed updates (pre-update snapshot, automatic rollback on failure, jump to the latest stable release, roll back to one of the three previous stables), binds `127.0.0.1` by default
- **Cross-platform**: `linux/amd64` / `linux/arm64` images; macOS and Windows run them through Docker Desktop
- **Web console**: accounts, models, access, request history, and runtime logs, with light and dark themes; request history can be filtered by account and shows status, latency, token, and usage statistics
- **Keepalive**: WorkBuddy daily check-in and token keepalive (per-account opt-in, off by default; console can check in now / refresh credits)
- **Deployment and ops**: single Docker Compose container, safe managed updates (pre-update snapshot, automatic rollback on failure, jump to the latest stable release, roll back to one of the three previous stables), binds `127.0.0.1` by default; `linux/amd64` / `linux/arm64` images, with macOS and Windows running them through Docker Desktop

## Quick start

**Deploy with Docker.** Published images and console managed updates (pre-update snapshot, automatic rollback, jump to the latest stable release) are built around the single Compose container. Running the Go / Node sources directly is not on that update path.
**Deploy with Docker.** Published images and console managed updates are built around the single Compose container; running the Go / Node sources directly is not on that update path.

Requirements: Docker (Docker Desktop on macOS/Windows, Docker Engine + Compose on Linux) and a Qoder, WorkBuddy, Trae, or experimental Devin account you control. On Windows, Docker Desktop must use Linux containers.

Expand All @@ -51,7 +50,7 @@ Base URL: http://127.0.0.1:3010/v1
API Key: <the key printed on first startup>
```

Without an account header the scheduler picks a ready account; pin a request with the `X-Qoder-Account: acc_...` header (a historical name that applies to every provider). Anthropic `POST /v1/messages` and OpenAI `POST /v1/responses` are also available; both require the complete conversation in each request and do not support server-side continuation through `previous_response_id` / `conversation`. Multi-turn requests stick to the same account from the first user message (including image-only turns) by default; `X-CLI2API-Session` remains an optional override. curl / PowerShell examples in the [deployment guide](deploy/README.md).
Without an account header the scheduler picks a ready account; pin a request with the `X-Qoder-Account: acc_...` header (a historical name that applies to every provider). Multi-turn requests stick to the same account from the first user message (including image-only turns) by default; `X-CLI2API-Session` is an optional override. curl / PowerShell examples in the [deployment guide](deploy/README.md).

## How it works

Expand All @@ -67,14 +66,12 @@ Each enabled account gets an isolated runtime: Qoder uses its own Node process,
<img src="./docs/assets/readme/console-window-en.svg" width="100%" alt="CLI2API console Accounts page: each account shows its login method, ready state, and quota, with an Access panel offering the Base URL and a quick check">
</p>

Accounts, models, access, and logs all live in one web console. Each account signs in through the methods supported by its provider (browser OAuth, PAT, or credential import), readiness and quota are visible at a glance, and the Access page lets you copy the Base URL and run a quick check.
Accounts, models, access, and logs all live in one web console: readiness and quota are visible at a glance, and the Access page lets you copy the Base URL and run a quick check.

## Use cases

- Connect Qoder / WorkBuddy / Trae (and experimental Devin) to local or private-server tooling
- Reuse OpenAI-compatible clients and scripts
- Route requests across multiple accounts with failover
- Keep login state available without starting a full CLI Agent per request
- Connect your upstream accounts on a local or private server, with automatic routing and failover across them
- Reuse OpenAI-compatible clients and scripts without starting a full CLI Agent per request

CLI2API is a local gateway: it does not provide accounts, quotas, or an official API service, and it is not a shared multi-user resale service.

Expand All @@ -84,13 +81,6 @@ CLI2API is a local gateway: it does not provide accounts, quotas, or an official

- Live-account acceptance for Qoder CN and WorkBuddy (login, failover, mixed account pools)

**Supported**

- Stateless text and function-tool adapters for Anthropic `/v1/messages` and OpenAI `/v1/responses`; image input where the provider supports it
- WorkBuddy daily check-in and token keepalive (per-account opt-in, off by default; console can check in now / refresh credits)
- Session-sticky routing from conversation content (first user message, including image-only turns), or via `X-CLI2API-Session`, with rule-based failover when the bound account cannot serve the request
- Request history filtering by account, plus request status, latency, token, and usage statistics

**Longer term**

- More upstream channels (Cursor, etc.)
Expand All @@ -105,13 +95,9 @@ CLI2API is a local gateway: it does not provide accounts, quotas, or an official

The service binds `127.0.0.1:3010` by default; all APIs and console data endpoints require the API key except `/health`, static frontend assets, and CORS preflight `OPTIONS` for the OpenAI-compatible `/v1/*` endpoints. Never commit `.qoder`, tokens, cookies, auth blobs, or raw captures; credential export is an explicit sensitive operation — protect exported files. Upstream API or CLI changes may affect compatibility; qodercli is pinned and checked. Please report security issues privately according to [SECURITY.md](SECURITY.md).

## Community

Chinese-language discussion is on [LINUX DO](https://linux.do). Bugs and feature requests still go to GitHub [Issues](https://github.com/caigee-cmd/cli2api/issues).

## Contributing
## Community & Contributing

Issues, documentation improvements, and pull requests are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
Chinese-language discussion is on [LINUX DO](https://linux.do). Bugs and feature requests go to GitHub [Issues](https://github.com/caigee-cmd/cli2api/issues); documentation improvements and pull requests are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).

## License

Expand Down
25 changes: 25 additions & 0 deletions internal/providers/trae/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,31 @@ func TestPrepareBodyForcesSoloShape(t *testing.T) {
}
}

func TestPrepareBodyExpandsNamespaceTools(t *testing.T) {
out := PrepareBody([]byte(`{"model":"glm-5.3","messages":[{"role":"user","content":"hi"}],"tools":[
{"type":"namespace","name":"mcp__computer-use","tools":[
{"type":"function","name":"left_click","parameters":{"type":"object","properties":{"x":{"type":"number"}}}}
]},
{"type":"mcp","server_label":"computer-use"}
]}`))
var body map[string]any
if err := json.Unmarshal(out, &body); err != nil {
t.Fatal(err)
}
tools, _ := body["tools"].([]any)
if len(tools) != 1 {
t.Fatalf("tools=%v", body["tools"])
}
tool, _ := tools[0].(map[string]any)
fn, _ := tool["function"].(map[string]any)
if fn["name"] != "mcp__computer-use__left_click" {
t.Fatalf("name=%v", fn["name"])
}
if _, ok := fn["parameters"].(string); !ok {
t.Fatalf("parameters should remain Trae JSON string: %v", fn["parameters"])
}
}

func TestProbeReadyWithCredential(t *testing.T) {
client, store := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("probe should not hit network when credential is fresh: %s", r.URL.Path)
Expand Down
Loading
Loading