feat(command): 新增实验性 Command Code 渠道(走 CLI 的 /alpha/generate,支持所有档位) - #230
Conversation
Command Code's /provider/v1/messages and /provider/v1/chat/completions require the Pro plan or higher and return 403 upgrade_required on the $1 Go plan. /alpha/generate is the undocumented envelope the `cmd` CLI itself uses on every turn; it is not plan-gated and serves the whole model catalog, so this adapter always speaks it. - internal/providers/command: in-process adapter (constants, credential, catalog, payload -> Vercel ModelMessage[] envelope, chat non-stream + NDJSON->SSE stream rewrite, errors -> accounts taxonomy, probe via /alpha/whoami, credits via /alpha/billing/credits). Modelled on the Devin adapter; imports no executor/store/HTTP packages. - registry + app: register the global in-process descriptor (PAT auth). - control: LoginPAT wires the existing paste-key tab for in-process PAT adapters (Qoder keeps its worker login path). - frontend: provider mark, labels/hints, PAT-tab default for a provider with no browser login. - README/AGENTS/CONTRIBUTING + bilingual changelog fragment. The /alpha/generate schema is strict and version-coupled (x-command-code- version), so the CLI version is pinned and fails loudly rather than degrading silently. Reasoning is only advertised for models with verified reasoning_effort support (DeepSeek V4 Pro/Flash). Experimental; not production-ready. Protocol facts derived from safzanpirani/pi-commandcode-provider (MIT); see internal/providers/command/NOTICE.
The credits endpoint returns the rolling usage windows at the response root (`windowLimits.fiveHour` / `windowLimits.weekly` with `used`/`cap`/`resetAt`) plus `credits.planId`. The adapter ignored both, so the account card showed a single monthly meter pinned at 0%. - Parse `windowLimits` -> 5-Hour Limit and Weekly Limit windows (used/cap percentage, millisecond resetAt -> reset countdown), with a nested fallback if the shape moves into `credits`. - Derive the Monthly Limit window from `planId` via the same plan allowance table the CLI uses (go 10 / goat 70 / pro 30 / pro-v1 80 / provider 15 / max 150 / ultra 300 / teams-pro 40); unknown plans leave it unset rather than fabricating a total. - Label: the provider speaks the CLI's /alpha/generate protocol, which is not plan-gated and works on every plan, so drop the "(Go plan)" suffix from the account type, hints, README, and changelog. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
|
关联 #226 —— 本 PR 落实了那里的范围问题:直接实现 |
|
补充:真账号验收已完成。本分支已构建并部署为本地单容器实例(版本号 |
The cross-provider pool lets a client pin a provider with a leading
"<provider>/" model id, but ProviderPrefix() only knew qoder/workbuddy/trae/
devin. A "command/deepseek/deepseek-v4-pro" request therefore never had its
prefix stripped, could not match the command catalog, and failed with
"model route unavailable" — even though the account was ready and the model
was listed.
- Add command/ to the prefix set (and the console's model-setting prefix
parser), with a regression test that also asserts a bare org-namespaced id
("deepseek/…", "moonshotai/…") is not misread as a provider.
- Refresh the provider-prefix hint in both locales.
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
A real /alpha/generate stream sends two terminal events: `finish-step` with `usage`, then `finish` with `totalUsage` (no `usage`). Both hit the same switch arm, so handling `finish` set usage from a nil `event.Usage` and wiped the numbers from `finish-step`. Streaming requests — all real traffic — therefore recorded no prompt/completion/cache tokens at all. - Read `usage` with a `totalUsage` fallback, and never overwrite a known usage with nil, in both the SSE writer and the non-stream aggregate. - Regression tests for finish-step+finish, finish-only, and the SSE usage chunk. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
|
补充:用真实 Go 档账号跑下来发现并修了两个只有实测才会暴露的问题(均已补回归测试):
实测验证后, 这两条也正是"实验性、未公开端点"风险的体现,PR 描述里的 experimental 标注依然成立。 |
|
新增的 |
Refs #226
概述
新增实验性 Command Code(
commandcode.ai)in-process 渠道,模板参照 Devin 适配器(不起子进程、不复用 worker 生命周期)。Command Code 有三个生成面:
POST /provider/v1/messagesPOST /provider/v1/chat/completionsPOST /alpha/generate$1 Go 档下两个
/provider/v1/*生成路径返回403 upgrade_required;/alpha/generate是cmdCLI 每轮都在用的端点、不受档位限制,因此适配层始终走它 —— 所有档位都能用,且覆盖整个模型目录。属于「上游协议适配」,不是再造一个 OpenAI 网关。具体实现:
user_…Bearer key(CLI 与 API 共用,无 OAuth / 回环 / device seed)。复用现有 PAT 页签粘贴;control.LoginPAT为 in-process 适配器补齐login/pat,因此不需要新登录流程,Qoder 的 worker 路径不受影响。GET /provider/v1/models(匿名可读),带 TTL 缓存。configenvelope + Vercel AI SDKModelMessage[](tool-call/tool-result/role:"tool",既非 Anthropic blocks 也非 OpenAI tool messages);非流式聚合;NDJSON → OpenAI SSE 流式改写;tool 往返(含id与toolCallId的坑);inputTokens含缓存 token 的口径处理。accountstaxonomy(upgrade_required→ invalid_request、insufficient_credit→ quota、401→ auth …)。GET /alpha/billing/credits—— 由planId推导的月套餐窗口,以及滚动的 5 小时 / 周windowLimits;另有/alpha/whoami就绪探针。reasoning_effort的模型(DeepSeek V4 Pro/Flash)声明档位,适配层绝不自行注入档位。版本耦合:
/alpha/generate未公开且以x-command-code-version为键,故 CLI 版本在constants.go中钉死,不匹配时明确报错(可用CMD_CLI_VERSION覆盖)。架构:只新增一个包 + descriptor/注册,
app.go一行,外加control里一个小的login/pat分支。未改动gateway/console/server/executor/store/runtime;internal/providers/command不引入任何被禁包(TestImportConstraints通过,command已加入 concrete-provider 守卫)。协议事实来自safzanpirani/pi-commandcode-provider(MIT),出处见internal/providers/command/NOTICE。实验性 ——
/alpha/generate未公开、可能漂移,不承诺生产可用。Test plan
go test ./...go vet ./...cd worker && npm testcd frontend && npm run build && npm run lintchangelog/unreleased/command-code-provider.md,或本 PR 无用户可见变更本地另跑:
go test -race ./...、TestImportConstraints/TestDutyBoundaries、release-notes.py validate、gitleaks(无发现)、Windows/arm64 交叉编译。新增单测覆盖凭证导入、目录解析、envelope/消息转换、NDJSON 聚合、SSE 改写、错误分类与额度窗口。真账号验收已完成:本分支已构建为镜像在本地单容器部署(
v0.6.5-command-test3),用真实 Go 档user_…key 完成多轮长对话实测,流式、工具往返、长上下文与版本头(x-command-code-version)均正常。仍保留 experimental 标注,原因是/alpha/generate为未公开端点、存在漂移风险,与验收无关。