- Home
- Plugins
- Tools & Capabilities
- dsh-account-pool
dsh-account-pool
cliii-one/dsh-account-pool
把多个 WorkBuddy / Trae 账号汇成账号池接入 DeepSeek Harness:自动切换、限流熔断退避、凭证安全落盘
Install
dsh plugin --profile web add github:cliii-one/dsh-account-poolREADME
dsh-account-pool
把多个 WorkBuddy / Trae 账号汇成账号池接入 DeepSeek Harness,并在账号之间自动切换。
某个账号被限流、积分耗尽或掉线时,自动换下一个账号继续,无需手动干预。 两家上游(腾讯 CodeBuddy 系的 WorkBuddy、字节的 Trae)共用同一套选号、 失败换号、冷却熔断与用量统计。
它解决什么问题
单个 WorkBuddy 账号会限流、会耗尽积分,一个请求被卡住整轮对话就断。 本项目在插件内部维护一个账号池:
- 多账号并行登录——插件内走 OAuth 设备流,扫码/点链接即可添加,不依赖 WorkBuddy 桌面端 App
- 自动切换——按三因子加权选号(积分比例、闲置补偿、成功率),429 限流 / 402 欠费 / 会话失效时自动换号重试
- 冷却与熔断——限流的账号按指数退避冷却(600 秒起,封顶 2 小时),连续失败达阈值熔断
- 会话粘性——同一会话尽量复用同一账号,多轮对话不跳号
架构
插件自带「网关」,不依赖任何外部服务:
DSH LLM seam
→ pi-ai adapter(本插件注册的 provider 路由)
→ loopback shim(127.0.0.1:随机端口,本插件内置)
├─ 选号器:三因子加权 + 冷却 + 熔断 + 会话粘性
└─ 失败自动换号重试
→ copilot.tencent.com / www.workbuddy.ai
为什么需要 shim:pi-ai provider 只认「一个 baseURL + 一个 apiKey」, 而切换必须发生在每次请求。所以在回环地址起一个 OpenAI 兼容端点, pi-ai 指向它,由它决定本次请求用哪个账号。
安全:shim 只绑 127.0.0.1,用进程内随机 secret 做鉴权,真实的
WorkBuddy token 不交给 pi-ai。入站请求校验 Host/Origin 必须回环
(防 DNS rebinding 与跨站请求),secret 用常量时间比较。
安装
dsh plugin --profile web add dsh-account-pool
安装后需要重启 DSH 进程(bundle 在启动期加载)。
使用
- 重启后打开 设置 → WorkBuddy 账号池
- 点「添加账号」,在浏览器中打开给出的链接完成登录
- 登录完成后账号自动加入池子,模型出现在 DSH 的模型选择器里
- 重复第 2 步可以添加多个账号;它们会被自动调度
账号列表里可以看到每个账号的状态(可用 / 冷却 / 熔断)、积分、成功率和凭证到期时间。 可以手动「解冻」某个账号,或「移除」它。
导入已有 auth 文件
已有凭证(例如 workbuddy2api 网关的 auths/*.json,或桌面端导出的文件)不必重新登录,
设置页点「导入 auth」即可,支持两种格式:
嵌套格式(网关与官方客户端落盘的形态):
{
"auth": {
"accessToken": "eyJ...",
"refreshToken": "eyJ...",
"expiresAt": 1794467101,
"domain": "www.codebuddy.cn",
"realm": "cn"
},
"account": { "uid": "...", "nickname": "..." }
}
扁平格式(手写方便,字段平铺在顶层):
{
"accessToken": "eyJ...",
"refreshToken": "eyJ...",
"expiresAt": 1794467101,
"domain": "www.codebuddy.cn",
"uid": "...",
"nickname": "..."
}
导入方式有两种:
- 粘贴 JSON:把文件内容贴进对话框
- 服务器路径:填文件路径,或填目录(会自动扫描里面所有
.json逐个尝试)
行为说明:
expiresAt兼容 Unix 秒(auth 文件)与毫秒(插件内部),自动识别- 缺
realm时按domain推断区域 - 区域不匹配的会被跳过并报明原因,不会把国际版凭证塞进国内版池子
- 目录里损坏的文件不会中断整批导入,失败的会逐个列出
每日任务
设置页有两个页签:账号(账号池与运维)与每日任务。
任务清单
页面按任务列出,每行显示完成数量(已完成账号数 / 总账号数):
| 任务 | 判定依据 | 频率 |
|---|---|---|
| 签到 | 调签到的返回(幂等) | 每天一次 |
| 猫猫旅行 | daily_limit_reached 或在途 | 每天一次 |
| 连登兑换 | 上游给的档位解锁状态 | 满 7/14/28 天 |
| 抽奖 | 当前抽奖次数 | 次数来自兑换 |
全完成显示绿色 3/3 已完成,部分完成显示黄色 2/3,没做显示灰色 0/3。
账号明细表里还有一列「今日获得」,按账号显示本次到手的积分:
| 情况 | 显示 |
|---|---|
| 还没执行过 | 未执行 |
| 算出收益 | +150(有收益时绿色) |
| 收益为 0 | +0——「确实没加」与「不知道」是两回事 |
| 算不出 | —,不编造数字 |
数值来自执行前后的余额差:签到接口只回成功/失败、不回奖励数量, Trae 的任务流也没有计费字段,余额差是唯一可靠的真值。只把正差值 算作获得(负差值可能是期间用了额度)。
只读与执行分离
打开页面只做只读查询,不会替你签到或派出。 点「全部执行」才真正执行。
一个例外要说明:签到没有只读状态接口,只能靠调用签到本身来判断(幂等,不会重复扣费)。 所以清单里的签到完成数来自上次执行结果,从没执行过时显示「未执行」。
执行顺序
签到 → 连登兑换 → 抽奖 → 猫猫旅行
顺序有意义:
- 签到放最前——它会让积分冷却的账号恢复可用
- 兑换必须在抽奖之前——抽奖次数只能从连登兑换获得
- 账号间串行执行,避免密集请求触发风控
其他
- 国际版账号自动跳过(签到/旅行/连登都是国内版活动),不发任何上游请求
- 重复执行是幂等的:上游返回「今天已签到」,不算失败
两个区域
国内版与国际版是两个并行的 provider:
| provider | 区域 | 上游 |
|---|---|---|
workbuddy | 国内版 | copilot.tencent.com |
workbuddy-global | 国际版 | www.workbuddy.ai |
两者的账号不通用。 上游按登录域签发 token,送到另一个区域的网关会被直接拒绝 (openresty 层返回 HTML 401)。一个账号只属于一个区域,想两边都用必须分别登录。
在 cordis.patch.yml 的 regions 里配置启用哪些区域,默认只开国内版。
没有国际版账号时请不要开 global——否则 workbuddy-global 会出现在模型选择器里
却没有任何可用账号。
两边账号各存一份凭据文件,互不覆盖;同时启用时不同会话可以各选一边。
配置
cordis.patch.yml 支持以下字段:
| 字段 | 默认 | 说明 |
|---|---|---|
regions | ['cn'] | 启用哪些区域,可选 cn / global |
softCooldownSeconds | 600 | 限流后的冷却基数,指数退避封顶 2 小时 |
breakerThreshold | 3 | 连续失败多少次后熔断该账号 |
用量统计
设置页底部有「用量统计」卡片,按 (时间片, 账号, 模型) 分桶记录每一次请求:
| 指标 | 说明 |
|---|---|
| 请求数 / 失败数 | 失败的尝试也计入请求数,这样「重试放大」才看得出来 |
| 输入 / 输出 / 总 tokens | 取自上游 SSE 末尾的 usage 帧 |
| 积分消耗 | 上游 usage.credit。只有逐次上报消耗的上游才有这一格(WorkBuddy);Trae 的对话流没有计费字段,该指标整块隐藏,而不是显示恒为 0 |
| 平均延迟 | 按样本数加权,不是对每桶均值再取平均 |
| 按模型明细 | 每个模型的请求、失败、token、积分 |
Trae 为什么没有积分消耗:它的对话流只有 6 种事件 (
metadata/timing_cost/output/extra_info/token_usage/done), 没有任何计费字段。积分接口虽然能给「账号累计已消耗」,但那是账号总量、 不是本页消耗,混着展示会误导,所以不提供。
保留策略:按小时分桶;当桶数超过上限(20 万)时,把超过 90 天的小时桶 折叠为日桶(日桶长期保留)。折叠由桶数上限触发,不是按时间定时执行—— 所以在流量不大的情况下,历史小时桶会原样保留,不会自动精简。
数据落盘到 $DSH_HOME/.account-pool.usage.<区域>.json,防抖 30 秒写入,重启不丢。
凭证自动续期
不需要手动管凭证,插件会自己续。
两个触发点
| 时机 | 行为 |
|---|---|
| 每次请求前 | 取凭据时若发现剩不足 5 分钟,立刻刷新 |
| 后台每 6 小时 | 检查所有账号,剩余不足 7 天的提前续上 |
后台保活是为了应对闲置:只在有请求时刷新的话,长期没人用会导致 access token 过期——这还能靠 refresh token 救回;但如果闲置到 refresh token 也过期,账号就彻底作废、只能重新登录。保活把两个到期时间都往后推。
上游的两个有效期
实测上游返回:
| 字段 | 时长 | 含义 |
|---|---|---|
expiresIn | 30 天 | access token 有效期 |
refreshExpiresIn | 60 天 | refresh token 有效期,即「最晚还能换新 token」的时间 |
refresh 比 access 长一倍,所以保活窗口是充足的。
失败处理
- 刷新失败但 token 未过期 → 继续用旧的,不因一次网络抖动踢掉账号
- 并发刷新合并 → 同一账号的并发请求只发一次刷新,不会重复打上游
- refresh token 轮换 → 上游返回新 refresh token 时会一并保存
凭据存放
凭证落在 $DSH_HOME/.account-pool.<区域>.json,权限 0600。
刷新得到的 token 写回这里,不写入 DSH settings。
登出(设置页「全部登出」)会删除对应区域的文件。
环境要求
- DeepSeek Harness(
webprofile) - Node
^22.19.0 || >=24.0.0 - 能访问
copilot.tencent.com(国内版)或www.workbuddy.ai(国际版)
设置界面
插件在 DSH 设置里注册两个独立菜单:
| 菜单 | 内容 |
|---|---|
| WorkBuddy | WorkBuddy 国内版 / 国际版的账号、每日任务(签到·旅行·兑换·抽奖)、用量统计 |
| Trae | Trae 账号、每日任务(签到·积分)、用量统计 |
两者是平级的独立分区,互不混杂——各自只显示自己的区域与账号。
Trae 支持
插件同时支持 Trae(TRAE SOLO),与 WorkBuddy 并列成为独立的上游:
| WorkBuddy | Trae | |
|---|---|---|
| 上游 | copilot.tencent.com | trae-api-cn.mchost.guru |
| 认证 | 标准 Bearer | 自定义 Cloud-IDE-JWT |
| 设备标识 | 无 | 需要 machine/device id |
| 登录方式 | 浏览器授权,自动回调 | 需粘贴回调链接 |
| 每日任务 | 签到/旅行/兑换/抽奖 | 签到 + 积分 |
两边共用同一套选号、失败换号、冷却、用量统计与安全 shim,
只有「上游协议」这一层不同(适配器在 lib/trae-*.js)。
Trae 登录
登录链接的参数必须与官方客户端完全一致——缺一个都可能走错登录流程:
少了 auth_type / login_channel / login_version 等字段时,Trae 授权页
返回的内容明显不同(实测差约 4.5KB),表现为登录完成后不跳转回调,
用户永远看不到带 refreshToken 的地址。
对齐后的参数:login_version auth_from login_channel plugin_version
auth_type client_id redirect login_trace_id auth_callback_url
machine_id device_id x_device_id x_machine_id x_device_brand
x_device_type x_os_version x_app_version x_app_type
Trae 凭证:导入 storage.json
不做网页登录,改为导入 Trae 桌面端已登录的凭证。
原因是网页登录在当前部署下走不通:Trae 授权页把 token fetch 投递到
127.0.0.1:18080(不是导航跳转)。DSH 在 NAS、浏览器在另一台电脑时,
那个 127.0.0.1 指用户自己的电脑:
- 请求被拒 → 页面提示「网络错误」
- 因为是 fetch 而非导航 → 地址栏不会出现带 token 的链接
- 所以「复制地址栏链接」这条路也拿不到东西
flowchart LR
A["Trae 授权页"] -->|"fetch 投递 token"| B["127.0.0.1:18080"]
B -->|"NAS 部署时<br/>指向用户自己的电脑"| C["无服务 → 拒绝"]
C --> D["页面报网络错误<br/>地址栏无变化 ✗"]
操作步骤
-
在已登录 Trae 的电脑(Windows/macOS)上找到凭证文件:
Windows: %APPDATA%\Trae CN\User\globalStorage\storage.json macOS: ~/Library/Application Support/Trae CN/User/globalStorage/storage.json -
插件设置里点「导入凭证」,对话框默认就是选择文件:
- 直接把文件拖进虚线框,或点「选择文件」按钮选它
- 文件在你本地被浏览器读取,随表单一起提交——不需要先把文件传到 NAS
- 也可以切到「粘贴内容」手动粘贴 JSON,或切到「服务器路径」填 NAS 上的路径
三种方式等价,按你的习惯选。文件读取用浏览器标准 FileReader, 不引第三方库;超过 2MB 会提示「可能选错了」(storage.json 通常几十 KB)。
为什么换个机器也能用
解密只用硬编码盐值 + 密文自带的随机数:
salt = SALT_A xor SALT_B
first = sha512(random) ← random 取自密文
derived = sha512(first + salt)
key = derived[0..16] iv = derived[16..32]
没有机器码、没有 DPAPI、没有系统密钥——任何机器上都能解。 明文前 64 字节是 sha512 校验值,用来确认解密正确。
区域由凭证自带的 userRegion 判定(CN / SG),大小写不敏感。
开发自检
npm run check
17 项检查,覆盖静态、运行时与交互三层:
| 类别 | 检查项 |
|---|---|
| 静态 | 语法、import/export 一致性、跨作用域引用、props 完整性、死代码 |
| Host | 模块加载、路由注册/注销配对、路径唯一、同源校验、定时器 unref |
| 前端 | client.js 加载、两个页签渲染、所有按钮点击、边界状态 |
| 行为 | 并发写入、用量统计口径、选号策略、打包内容 |
这些检查项都是从实际出过的问题里来的,不是凭空设计的:
| 检查项 | 对应的问题 |
|---|---|
| 跨作用域引用 | DailyTasksView 直接调主组件的 setLastRun,报 not defined |
| props 完整性 | RegionPanel 没收到 onImport,按钮点了没反应 |
| 并发写入 | 多个 save 共用 .tmp 文件名,rename ENOENT 丢数据 |
| 死代码 | streamBody 零引用 |
| 定时器 unref | 粘性清理定时器漏 unref,拖住宿主退出 |
改动后跑一遍,能提前挡住这几类问题。scripts/ 不进发布包。
已知限制
- 依赖 WorkBuddy 客户端接口(非官方开放 API),上游变更后插件可能需要跟进
- 账号池状态(冷却、熔断计数)是进程内状态,重启后重置
- 本插件只负责「接入与调度」,不做自动签到、成长任务等上游活动
参考与致谢
本项目的上游协议细节(端点、请求头、字段名、刷新契约、解密方式) 不是猜出来的,而是在这四个项目的实测结论基础上做的。没有它们, 就没有这个插件——尤其是那些"看起来奇怪但必须这样写"的地方, 每一处背后都有人踩过坑并记录下来。
workbuddy2api-panel
WorkBuddy(CodeBuddy)网关面板,Go 实现。
| 用到的部分 | 在哪个文件 |
|---|---|
请求头形态(X-CodeBuddy-Request、设备标识派生规则) | lib/headers.js |
| 选号策略(三因子加权 + Top-5 抽签) | lib/selector.js |
| 用量统计口径 | lib/usage.js |
积分包解析(CapacityRemain / 周期包三档判定) | lib/upstream.js |
dsh-connect-workbuddy
同样把 WorkBuddy 接进 DSH 的插件,安全设计上做了正确的示范。
| 用到的部分 | 在哪个文件 |
|---|---|
| shim 安全模型(回环绑定、随机端口、进程内 secret、Host 校验) | lib/shim.js |
安全代码不做"改善"——这是它验证过的形态,照做。
dsh-connect-trae
Trae 接入 DSH 的插件,带完整取证文档(docs/ 下有实测记录)。
| 用到的部分 | 在哪个文件 |
|---|---|
上游常量(host、APP_ID、IDE_VERSION) | lib/trae-accounts.js |
| 刷新契约(按 region 分流的 ClientID 与端点) | lib/trae-accounts.js |
| SSE 协议转换(Trae 命名事件 → OpenAI chunk) | lib/trae-bridge.js |
目录与通道不一致的取证(glm-5.3 为何不可调用) | lib/trae-upstream.js |
| 设备身份策略(导入时生成一次、复用而非每请求重新生成) | lib/trae-accounts.js |
traework2api
Trae 网关,Go 实现,登录脚本带有最完整的实测结论。
| 用到的部分 | 在哪个文件 |
|---|---|
凭据解密(storage.json 的 AES-CBC 盐值与布局) | lib/trae-storage.js |
签到流程与必需的 X-Device-Id | lib/trae-accounts.js |
这些参考带来的具体判断
有些"为什么不那样写"的答案来自这些项目:
glm-5.3在目录里但不能用 —— Trae 的目录(get_detail_param)与调用通道 (llm_utils_chat)是两个面,前者是全量、后者只服务子集- Trae 必须做协议转换 —— 它的 SSE 是命名事件格式,pi-ai 只认 OpenAI 格式, 直通会撑到超时(表现为"很慢、几乎都失败")
- 签到要带
X-Device-Id—— 缺了回业务码9004,HTTP 却是 200 - 周期积分包不能靠
CapacityType枚举判定 —— 要用三档数据驱动, 否则"刚好用完"的包会算错
免责声明
本项目仅供个人学习和研究使用,仅驱动使用者自己的账号。
使用者需遵守所接入服务(WorkBuddy / CodeBuddy、Trae)的服务条款; 因使用本项目产生的任何后果由使用者自行承担。
本项目与腾讯、字节跳动、WorkBuddy、CodeBuddy、Trae、DeepSeek 均无关联, 未获得上述任何一方的授权或认可。
许可
MIT © 2026 cliii-one
Related plugins
archify (deepseek-harness)
tt-a1i/archify
WeKnora (dsh-weknora)
tencent/weknora
weknora
tencent/weknora
BrowserSkill (dsh-plugin-browserskill)
tencent/browserskill