dsh-router-codebuddy
已验证dsh-router-codebuddy · v0.3.20 · MIT
DSH plugin: CodeBuddy supplier family for dsh-router. Provides two same-family suppliers through the router.suppliers cordis service: codebuddy (Tencent CodeBuddy, copilot.tencent.com) and codebuddy-en (WorkBuddy international, www.workbuddy.ai). Both sha
安装
dsh plugin add dsh-router-codebuddy 用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。
源码
标签
作者
说明文档
dsh-router-codebuddy
dsh-router 的 CodeBuddy 族供应商插件(国内 CodeBuddy + 国际版 WorkBuddy)
快速安装 · 两个供应商 · 能力 · dsh-router 核心
为 dsh-router 提供腾讯 CodeBuddy 族的两个供应商 (OAuth 轮询登录,OpenAI 兼容网关)。单独装它没用——它只是向核心注册供应商,面板、账号池、 组合回退都在核心里。
两个供应商
| 供应商 id | 面板名 | 上游 | 别名前缀 |
|---|---|---|---|
codebuddy |
CodeBuddy | copilot.tencent.com(国内版) |
codebuddy/ |
codebuddy-en |
CodeBuddyEN | www.workbuddy.ai(WorkBuddy 国际版) |
codebuddy-en/ |
WorkBuddy 是腾讯的国际版 AI 办公工作台,与国内 CodeBuddy 同族同契约:同样的
OAuth 轮询登录、同样的 /v2/chat/completions 网关、同样的 /billing/meter/* 签到积分。
因此两者共用同一份实现(src/core.ts),差异只落在各自的 profile 上:
src/
core.ts 共享实现 —— OAuth 登录、单账号 chat、token 刷新、签到、积分、模型拉取
cn.ts codebuddy profile (copilot.tencent.com:/v2 单路径、CLI 指纹头)
en.ts codebuddy-en profile (www.workbuddy.ai:/console→/v2 回退、桌面端指纹头、接口归一)
index.ts 插件入口(host 半,一次注册两个供应商)
contract.ts 供应商契约(自含,与 dsh-router 契约同步)
types.ts 通用类型(SupplierStatus / ChatRequest 等)
cordis.patch.yml bundle patch,把插件插入 DSH cordis bundle stack
快速安装
需要 DSH 0.2.0 及以上(核心已单面 0.2.0,0.1.x 宿主不再支持)。先装核心,再装本插件,然后重启 dsh web:
dsh plugin --profile web add dsh-router-core
dsh plugin --profile web add dsh-router-codebuddy
dsh plugin add 会在 profile 里 pnpm add,并自动把声明了 dsh.bundle.patch
的包加入 dsh.profile.bundles(本插件即声明了,即 cordis.patch.yml)。
重启后本插件以 cordis service router.suppliers 向 dsh-router 注册两个供应商,
面板「供应商」出现 CodeBuddy 与 CodeBuddyEN 两张卡片。
本地开发版:不用 npm,直接
dependencies加"dsh-router-codebuddy": "link:/path/to/dsh-router-codebuddy"指向本地仓库。
能力
| 能力 | 说明 |
|---|---|
| OAuth 轮询登录 | 生成登录链接 → 浏览器登录 → 后台轮询 token(每 5s,最多 5 分钟),自动落盘凭证。无粘贴回调步骤。 |
| 模型列表 | 「获取模型」从上游 GET /v3/config 实时拉取(带账号 token,服务端下发,新模型上游一上线就能刷出来);上游不可达时回退内置兜底表;仍可手动添加自定义模型。 |
| 接口归一(仅国际版) | chat 出站前 developer → system、tool_choice 对象形态 → string、首条非 system 时前置兜底 system —— 规避上游 11128 first message is not system prompt / 11101 Unmarshal chat params failed。 |
| 连接池 | 多账号由核心按池顺序/策略(fallback / round-robin)选号回退,本插件只报告单个账号的成败与语义状态。 |
| token 自动刷新 | 到期前 24 小时内用 refresh token 刷新(X-Refresh-Token 头),刷新失败继续用旧 token。 |
| 签到领积分 | 每日 100 积分(连续第 7 天 1000),核心遍历所有链接逐个调用。已签到上游返回 code=10001(HTTP 400 + 该码),幂等视为成功。 |
| 积分显示 | 面板账号积分 = get-user-resource 各额度包的剩余求和(CapacityRemain,会续期的基础包取 CycleCapacityRemain),内存缓存 10 分钟,签到后自动刷新。注意 TotalDosage 是累计已消耗,不是剩余。积分的持久化由核心统一做(supplier-config.json),本插件拿不到时报 -1 让核心顶上次的值。 |
使用
- 重启
dsh web - 面板 → 供应商 → CodeBuddy / CodeBuddyEN 卡片 → 添加链接 → 浏览器登录 → 完成添加
新增账号会出现在面板账号池中;模型在供应商详情页点「获取模型」从上游拉取
(列表随服务端下发更新,无需升级插件),仍可手动添加自定义模型。
/v1/chat/completions 请求模型可写 glm-5.3-flash 或带别名前缀
codebuddy/glm-5.3-flash(插件自动剥前缀)。
模型从哪来:
GET <base>/v3/config—— 官方客户端取云端产品配置的同一接口。 它不鉴权也返回 200,但data.models只有在带账号 accessToken 时才下发;所以必须 先添加链接再点获取模型,没账号时只会拿到内置兜底表。回包里的生图/视频模型 (tags含text-to-image等)走不了 chat 端点,会被过滤掉。
上游
国内 CodeBuddy(https://copilot.tencent.com):
- chat:
POST /v2/chat/completions(强制流式,非流式上游拒绝;转成 OpenAI SSE 交回核心写) - 模型:
GET /v3/config→data.models[](id/maxInputTokens/tags) - 登录:
POST /v2/plugin/auth/state生成链接 → 浏览器登录 → 轮询GET /v2/plugin/auth/token?state=...换 token 落盘 - 刷新:
POST /v2/plugin/auth/token/refresh(X-Refresh-Token头,到期前 24 小时内触发) - 积分/签到:
POST /v2/billing/meter/get-user-resource、POST /billing/meter/daily-checkin - 凭证:
auths/codebuddy/{uid}.json({nickname, accessToken, refreshToken, expiresAt})
国际版 WorkBuddy(https://www.workbuddy.ai)—— 与上面同契约,差异在路径与指纹:
- chat:
POST /console/chat/completions,404/405 回退POST /v2/chat/completions;出站前做接口归一(见上表) - 积分/签到:
POST /billing/meter/*(无/v2前缀),404 回退/v2/billing/meter/* - 出站指纹头:UA
WorkBuddy/5.5.4 WorkBuddy AI/5.5.4 CLI/2.137.1、同域Origin/Referer、Accept-Language: en-US、X-No-Enterprise-Id: 1 - 凭证:
auths/codebuddy-en/{uid}.json
与核心的分工
本插件只管对单个账号调通上游:OAuth 协议、token 刷新、SSE 转换、签到、积分。
策略全在核心(AccountPool):选号、冷却、禁用、连续错误累计、遍历回退、
响应写入。所以:
chatOnce(uid, req)一次只服务一个账号,不遍历账号、不维护冷却表、不写响应- 失败时返回语义状态(
rate_limit/quota/session_dead/unavailable/transport/unknown/no_such_model/bad_request),由核心决定冷却多久、 是否禁用、要不要换号。bad_request= 请求本身被上游拒(参数错 / 图片认不出 / tool_call 配对断裂),同一个请求对池里每个号都会失败,所以核心不惩罚账号—— 否则一次坏请求会把整个池冷掉(2026-09-15 读图事故)。 status()只报「现在状态」(凭证 + 积分),冷却/禁用由核心叠加后给面板- 积分只报值,不落盘:拿不到时报
-1(不是 0),核心保留上次持久化的值
上游业务码的归类(两类拒绝必须分清)
同一个 HTTP 状态里混着账号级和模型级两种拒绝,归错一类不是错个提示,而是 可用性损失——两类的处置方向相反:
| 业务码 | 含义 | 归类 | 为什么 |
|---|---|---|---|
11102 |
model [x] service info not found:目录还列着、上游已下架 |
no_such_model |
模型级:2026-10-10 在全部 11 个 CN 账号上逐号实测,同一模型结果完全相同(11/11 号 400/11102),而 glm-5.3 同批 11/11 全 200。归 unknown 会逐号冷却、遍历全池才 503,与「模型不存在」的语义完全不符。 |
11103 |
Backend [x] is not supported:生图模型被当聊天模型调 |
bad_request |
同上,模型级、逐号一致。 |
11140 |
request illegal(响应体带 displayMsg:未通过内容安全审核) |
不归类,走默认 403 → session_dead |
账号级,见下。 |
11128 / 11133 / 11135 |
首条非 system / 参数非法 / 图片认不出 | bad_request |
请求形态问题,与账号无关;国际侧 11128 已在出站前规避。 |
11140 的判据是踩出来的,别被它的文案骗了:它写着 "The content did not pass
the safety review.",看起来像内容审核(请求级),但实测同一段无害内容、甚至
**不带任何 user 消息(仅 system)**也一样被拒;而同一时刻同样请求换本机另外两个号
都能正常出字(wb-1 → 403/11140,wb-2/wb-3 → 200)。它描述的是「这个账号被
风控」,所以必须留在 session_dead(连接级冷却 30 分钟、到期自愈、逐号换)。
若误判成 bad_request,核心会跳过整个供应商,那两个可用号就永远轮不到。
同理,不要把业务码的判定提到 HTTP 状态之前:403/429 与请求级业务码组合时,
那一步会把「可以换号」变成「整个供应商被跳过」。
完整契约见 dsh-router 的 docs/suppliers.md。
架构
通过 cordis service router.suppliers 的共享聚合表向 dsh-router 注册两个供应商工厂
(cordis 每个 service name 只允许一个插件 provide,本插件 inject 等核心先提供该表后
追加并广播 internal/service 触发重扫,与加载顺序无关)。
两个供应商 id 即凭证与配置的存储键:凭证在 credentials.sqlite、配置与积分缓存在
supplier-config.json,均以 supplier id 寻址,故两个供应商各自独立、互不影响。
开发
pnpm install
pnpm build # lib/index.js
pnpm typecheck
pnpm test # 行为回归闸门(node --test)
src/core.test.ts 锁的是 profile 边界:国内侧不多发 system、不动 tool_choice、
只打 /v2;国际侧三个接口归一存在且顺序正确(补 system 必须在改 role 之后);
端点回退只在 404/405 触发;两个 id / 存储键 / uid 前缀不变。改 profile 改错会直接变红。
致谢
- decolua/9router —— codebuddy-cn(open-sse)实现的参考来源。
- Sliverkiss/workbuddy2api —— 国际版 WorkBuddy 的路径/指纹/接口归一参考。
许可证
免责声明
本项目仅用于学习与技术研究,请勿用于商业用途。