feat: 支持订阅制第三方供应商的通用接入形态(OAuth / 本地 harness 凭证委托)
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 395
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
### 使用场景 / Use case
用户手上往往有多份**订阅制** AI 额度:WorkBuddy / CodeBuddy 订阅、Cursor 订阅、各家 Coding Plan / Token Plan……这些额度已经付过钱,但只能在各自的客户端里用。希望 Cindy 能提供一个**通用的订阅接入形态**,把这些订阅复用进来,而不是在 Cindy 侧按 token 重复付费。
这与现有的「授权 Claude Code / Codex Coding Plan」是同一类诉求,只是 Cindy 目前只覆盖了这两家。
### 当前问题 / Current limitation
Cindy 现在能表达「订阅」的地方只有三处,都不通用:
1. **内置订阅源**:`providers.json` 里的 `xai`(`auth.method: "oauth"` + `access.kind: "subscription"`)—— 一次性硬编码,接新订阅源要改客户端。
2. **Coding Plan preset**:`tencentcloud-coding-plan` / `zhipu-coding-plan-cn` / `moonshot-kimi-code` / `aliyun-bailian-coding-plan` 等 —— 这些本质仍是 **API key + baseUrl**,只是计费方式是包月,并没有解决「只有客户端登录态、没有 API key」的订阅。
3. **Claude Code / Codex 订阅授权** —— 针对这两家的专门实现。
而「添加供应商」的自定义通道(`CustomProviderConfig`)只支持 **HTTP endpoint 形态**:`CustomProviderRuntimeConfig.baseUrl` 必填,`wireProtocol` 只能是 `openai-chat` / `openai-responses` / `anthropic-messages`,`authMethod` 只有 `apiKey` / `none`(见 `packages/model-providers/src/types.ts`)。preset 同样要求有一个可填的端点。
结果是:**任何"只提供客户端、不提供模型 API"的订阅制产品都无法接入**,而且每接一家都得写一套专门逻辑,没有可复用的抽象。
以 **WorkBuddy / CodeBuddy** 为例说明这类产品的共同形态(截至 2026-09-11 查证):
- WorkBuddy / CodeBuddy 是**消费方**:客户端通过 `~/.codebuddy/models.json` 接入*别人的* OpenAI 兼容 API(DeepSeek、火山 Ark、Ollama 等),官方文档明确「仅支持 OpenAI 接口格式」。
- 其**开放平台不提供模型推理端点**:只有 OAuth 2.0 + BackgroundAgent(云端 agent / 沙箱管理)系列接口,如 `POST /oauth2/token`、`/v2/backgroundagent/agentmgmt/agents`,**没有** `/v1/chat/completions`;个人开发者拿到的是 Client ID / Client Secret,不是模型 API Key。
- 所以 preset 路线在这里**无端点可填** —— 不是 Cindy 缺能力,是这类产品根本不对外暴露模型 API。Cursor 等订阅制产品同理。
### 期望方案 / Proposed solution
为「订阅制第三方供应商」抽象出**一个通用的接入形态**,让 provider 可以只声明「订阅来源 + 授权方式」,而不必假装自己是一个 HTTP 端点:
- **凭证归订阅方**:Cindy 不持有、不落任何订阅凭证;由宿主的凭证托管层负责授权与刷新,token 不进 `model-providers` catalog、不经 `listProviders` 泄漏给 renderer(沿用现有 safeStorage / main-only 的既有边界)。
- **两种授权形态可覆盖绝大多数订阅产品**:
- `oauth`:订阅方有可用的 OAuth / 设备码流程(现有 `xai` 内置源已是此形态,可提炼为可配置的一般能力)。
- **本地 harness 凭证委托**:订阅方只给客户端 / CLI(WorkBuddy 的官方 CLI 是 `codebuddy`,`@tencent-ai/codebuddy-code`,与桌面端同一引擎、自带登录),Cindy 以可信方式拉起该 harness,认证 / token 刷新 / 模型可用性 / 上游路由全部归订阅方独占,Cindy 只负责任务生命周期。
- **落地可以复用 #4230 已有的地基**:`apps/desktop/src/main/harness-runtime/`(可信 runtime profile、可执行身份探测、`execFile` + 固定 argv 的审批与 launch 解析、Main-only resolver)正是为这个形态建的,ADR-0001 也已经把所有权边界定死。建议把「订阅制 harness provider」做成该模块的一等公民,而不是每接一家写一套。
- **候选清单**(供排序,非本次范围承诺):WorkBuddy / CodeBuddy 订阅、Cursor 订阅、其他「客户端订阅 + 无 API」的产品。
### 已考虑的替代方案 / Alternatives considered
- **逐个做成 preset**:对本次这类产品不可行(无端点可填),且不解决"每接一家写一套"的复用问题。
- **内置社区反代**(workbuddy2api / codebuddy2api / workbuddy-cliproxy 类):技术上可行但**不建议内置** —— 需要读取本机桌面端登录凭据(如 `~/Library/Application Support/CodeBuddyExtension/Data/Public/auth/*.info`),且必须 `--desensitize`(剥离厂商系统提示、注入对方身份头)才能绕过上游 `content_filter`(参见 Wei-Shaw/sub2api#4623)。这属于规避上游风控 + 凭据经手第三方,与「凭证归订阅方独占」的边界冲突。
- **用户自行在本地跑反代,再在 Cindy 配「自定义供应商」**:**今天就能用,零改动**(反代监听 `127.0.0.1:8787` 后填 baseUrl + 选 wire protocol 即可)。这适合愿意自担风险的用户,**不需要 Cindy 为此改代码** —— 若维护者认为通用订阅形态不在近期范围,建议至少在文档 / FAQ 里点明这条自助路径,让用户知道当前可行解在哪。
- **#4228(把第三方本地 CLI 注册为 provider)**:方向接近,但那个提案是「用户自定义、配置驱动」,本需求是「把订阅授权做成产品能力」。两者可共享 harness 拉起与可信 profile 的部分,建议一并考虑。
### 关联
- #4230(Tencent 本地 Harness 集成基线 / ADR-0001)
- #4228(支持将第三方本地 CLI 注册为 provider)
- #3640(WorkBuddy 历史对话导入,互补:那个解决历史迁移,本需求解决新会话执行)
- `packages/model-providers/src/types.ts`(`CustomProviderConfig` / `ProviderPreset.authMethod`)
- `packages/model-providers/catalog/providers.json`(内置 `xai` 订阅源与各家 Coding Plan preset)
Contributor guide
Research direction
Start with packages/model-providers/src/types.ts, packages/model-providers/catalog/providers.json, and apps/desktop/src/main/harness-runtime/; read ADR-0001 and compare the existing xai subscription flow with the #4230 baseline. Done means a reusable subscription-provider shape supports OAuth or delegated local harness credentials while preserving the stated main-only credential boundary.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- authentication, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100