Skip to content

请求确认:是否收录 Command Code(commandcode.ai)渠道 — 独家价值在 $1 Go 档的 alpha/generate 适配 #226

Description

@BrianBoyCN

诉求

想加一个 provider:Command Code(commandcode.ai,CLI 是 npm 上的 command-code,命令名 cmd)。

先问一句「要不要收」,不是催实现 —— 实现我来,想先确认这是否落在项目定位内、以及你要哪个范围。

TL;DR:Command Code 的高价档位本身就是标准 OpenAI/Anthropic API,接进去价值有限;真正有独家价值的是 $1 Go 档 —— 它没有 API 访问,只能走 CLI 自用的 alpha/generate(自定义 envelope + NDJSON),而这恰好是「上游协议适配」而不是「再做一个 OpenAI 网关」。想问的是:只收 Go 档这层适配,还是整个渠道都收?

上游事实(本机实测,非照抄文档)

认证只有一把 user_… Bearer key,同一个 key 同时用于 CLI 与 API,没有 OAuth、没有浏览器回环、没有 WASM/machineId。

四个流量面:

Path 协议形状 档位
POST /provider/v1/chat/completions 已是标准 OpenAI Chat Completions GOAT / Pro / Max / Team / Provider
POST /provider/v1/messages 已是标准 Anthropic Messages 同上
GET /provider/v1/models 标准列表,匿名可访问(实测 200,15,696 字节;80 个模型,其中 9 个声明 /messages、63 个声明 /responses) 同上
POST /alpha/generate 自定义 envelope(Vercel AI SDK ModelMessage[] + NDJSON,不是 SSE) 任意档位,含 $1 Go 档

两个细节对我们的适配层很关键:

  1. models 每项带 supported_endpoints,Claude 系只有 ["/messages"],非 Anthropic 是 ["/chat/completions","/responses"];把 Claude 发给 /chat/completions 直接 400。这相当于天然的 per-model endpoint 路由,形状和 ModelInfo.Scene(feat(trae): PKCE login, scene routing, max-mode tiers; devin quota layout; workbuddy check-in retry #223 刚给 Trae 做的那套 scene routing)完全一致,不需要新机制。
  2. 官方也有 /responses,所以 gateway 现有的 /v1/chat/completions、/v1/messages、/v1/responses 三个面在 GOAT+ 档位上基本是薄透传。

错误分类表同样干净,Classify() 不用猜:401 authentication_error / 403 upgrade_required(Go 档专属) / 422 cmd_zdr_no_providers / 429 rate_limit_error / 5xx server_error。

alpha/generate 的协议已被别人逆向并有可用实现,不是我要从零猜:safzanpirani/pi-commandcode-provider(附 docs/wire-protocol.md,含完整 NDJSON 事件表与 usage 口径)、thaolaptrinh/commandcode-api-proxy(37⭐,9 月还在动,npx 直接跑)。所以 B 范围的不确定性主要在「跟版」,不在「能不能通」。

为什么想放进 cli2api,而不是直连

这大概是第一个会被问的问题,先答:

直连官方 API 就能用的场景(单把 GOAT/Pro key)确实不需要这里。动机全部集中在低价档位的「组池」上:

  • 订阅档位是 5 小时 / 周 / 月三重滚动窗口(GOAT:$14 / $35 / $70;Go:约 $3 / $6 / $10)。月积分没打满,但 5h 窗口先撞墙是常态 —— 这正是 QuotaWindow + 日/周窗口渲染(feat: show Devin daily and weekly quota windows #208、v0.6.3)和按模型冷却该解决的问题。
  • $1 Go 档没有 API 访问(403 upgrade_required),只能走 alpha/generate。这个 envelope 不对外承诺、客户端专用,恰好属于「上游协议适配」而不是「再做一个 OpenAI 网关」;把它翻译成 OpenAI 形状 + 多把 key 组池轮换,是 cli2api 的价值,直连做不到。
  • 客户端侧我这边已有 Qoder/WorkBuddy 一把 key 接 Codex/Claude Code 的习惯,想把这个渠道并进同一个入口、同一套日志和额度视图,不想再多起一个进程。

如果你认为「卖 API 的上游」不符合项目定位(AGENTS.md 里写着调度保持在个人账号路由、不做商业网关特性),这条我完全接受,见最后一节。

实现边界(我打算怎么走,先确认不越线)

按 in-process adapter,模板是 internal/providers/devin/,不起子进程、不复用 Qoder worker 生命周期:

internal/providers/command/
  constants.go    # base、paths、CredentialFormat = "command-key-v1"
  credential.go   # Bearer key 校验
  catalog.go      # GET /provider/v1/models,把 supported_endpoints 映射成 Scene
  payload.go      # ChatRequest → alpha/generate 的 config envelope(范围 B 才需要)
  chat.go         # 非流式 + 流式;错误 → accounts taxonomy
  rewrite.go      # NDJSON → OpenAI SSE,io.Pipe 自造 http.Response(范围 B)
  status.go       # 三窗口 QuotaWindow(若上游有额度端点,没有就先只做 used/total)
  errors.go       # Classify
  client.go       # Adapter() 组装

既有文件只动三处:providers/registry.go 加 descriptor(RuntimeInProcess、AuthTypes{AuthPAT}、PATLogin: true、Login/Checkin/ImportExport 留 nil)+ registry map + List();app/app.go 里一行 Register();前端 lib/provider.ts / ProviderMark.tsx / AddAccountModal.tsx / i18n/messages.ts,然后 npm run sync。

已确认 AddAccountModal.tsx:181 是 pat_login !== false,所以直接走现成的粘贴 key 页签,不需要新的登录流程。gateway / console / server / executor / control / runtime 全部不碰,注册只在 app.go —— TestImportConstraints 这关我先本地跑。

想请你定的三件事

  1. 范围要 A 还是 B?
    • A(薄):只支持 GOAT/Pro/Max/Team/Provider 档,上游已是标准 OpenAI/Anthropic 形状,adapter 主要是贴 header + 按 supported_endpoints 选路 + catalog + 额度。代码量小、漂移风险低,但对「官方已有兼容 API」这个质疑,价值确实主要是组池和统一入口。
    • B(含 Go 档):额外逆 alpha/generate(config envelope 字段全必填、messages 是 ModelMessage[] 既非 Anthropic blocks 也非 OpenAI tool messages、响应是 NDJSON 逐行、tool-call 终态用 toolCallId 而 delta 用 id)。真正独家价值,但它是未公开端点,上游改版就得跟版。
    • 我倾向 A 先落地、B 作为独立后续 PR,但如果你想收的只有 B 的独家能力,我就直接写 B。
  2. x-command-code-version 这类版本耦合放哪? alpha/generate 需要带 CLI 版本号,陈旧会被拒。npm 上 command-code 现在 1.64.0,而协议逆向资料还停在 0.52.1 —— 漂移是真的。我倾向照 worker/src/compat.mjs 的做法:显式钉一个版本 + 不匹配就 fail loudly 并在错误里说清去改哪,而不是静默降级。这个约定你觉得 OK 吗(尤其 provider 包里不引 Node 侧 pinned 常量,钉版值放哪合适)?
  3. 额度端点要不要一起进范围? 目前没找到公开的「查我的 5h/周/月窗口」端点。如果拿不到,我先只做 catalog + chat,卡片额度显示 unknown(不假装可用),后续再补。这算你能接受的「实现完成但额度验收未完成」状态吗?

我会负责的部分

  • 按 3 个小 PR 走,不甩一个 600 行的:① descriptor + credential + catalog + 非流式;② 流式 + tool roundtrip;③ 前端 + 额度窗口。
  • 每个 PR 带 changelog/unreleased/<slug>.md 双语 fragment,不动 CHANGELOG.md。
  • 测试用脱敏 fixture(照 devin/testdata/devin_models.sample.json 的做法),不提交真实 key / raw capture;go test ./...、-race、go vet、worker npm test、frontend build+lint、git diff --check 我本地全跑齐再提。
  • README / README_EN 里和 Devin 同口径标注:experimental,不承诺生产可用。
  • 后续上游漂移的跟版我来做。

如果你近期没精力 review 新渠道,或者觉得这类「上游本身在卖 API」的渠道不该进项目,直接关掉这条就行 —— 我自己 fork 出去维护,将来如果方向和你想的一致再重新提。要不要顺手在 README 的支持列表里加一行,也听你的。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions