본문으로 건너뛰기
2

DSH-Official-WorkBuddy-Credit-Proxy

2861292267/dsh-official-workbuddy-credit-proxy

이 기기에서 로그인한 모든 WorkBuddy 계정을 하나의 자동 장애 조치 provider로 묶어 OpenAI Chat, OpenAI Responses, Anthropic Messages를 같은 풀에서 제공합니다. 모델 별칭, 사용 통계, 예약 크레딧 자동화를 지원합니다.

설치

dsh plugin --profile web add github:2861292267/dsh-official-workbuddy-credit-proxy

README

RotaKit · DSH 官方版 WorkBuddy 积分池

把本机登录过的 WorkBuddy / CodeBuddy 账号合并成一个自动故障转移的 DeepSeek Harness 模型池,附带一整套积分自动化。

你在桌面端登过几个号,池里就有几个号 —— 不用手动录入,不用一个个切换。哪个号限流了、积分用完了、令牌失效了,请求自动落到下一个能用的号上。

池子同时说 OpenAI Chat / OpenAI Responses / Anthropic Messages 三种协议,所以 DSH、Codex、Claude Code 以及任何 OpenAI / Anthropic SDK 客户端都能直接接进来,共用同一个账号池。

来源与署名。 RotaKit 是独立维护的改编版,基于 XDTrees/dsh-workbuddy-xdpool(MIT,作者 XDTrees)。账号凭据解密移植自 dingminhua/dsh-connect-workbuddy,shim 的加固与错误映射参照 corrinehu/dsh-workbuddy-connect,两者均为 MIT。原版文档见 UPSTREAM-README.md。


功能总览

一、多账号池

能力说明
自动发现账号读本机桌面端登录快照(国内 / 国际两套),一个文件一个号,无需手动导入
按权重选号空闲越久的账号权重越高;取权重最高的前 5 个再洗牌,避免长尾账号被反复抽中
会话粘性同一对话固定用同一个账号,缓存不失效;账号不可用时自动换绑并固定新号
在途租约同一账号并发超过 2 个请求时让路,避免把限流风险堆在一个号上
冷却分级429(限流)与 5xx(网关故障)分开计数 —— 网关抖动不再污染限流历史
按模型冷却某个模型被限流只影响该模型,同账号其他模型照常可用
凭据熔断连续 3 次被拒的账号移出池 30 分钟,并标「需重新登录」,不再无声消失
额度门槛余额低于可服务门槛(默认 50)的账号自动跳过 —— 余额恢复后自动回到池中,无需任何操作
积分保留可给单账号设保留线,低于该线不再派活
手动停用可停用指定账号,与自动跳过分开管理
选号预算单次请求最多尝试 8 个不同账号;同一请求内不会重复选到同一账号

二、模型目录

  • 从上游实时拉取,带积分倍率、免费 / 夜间折扣标签、图片能力、思考档位、上下文窗口
  • 标记「无可用账号」的模型:某模型没人能服务时一眼可见
  • 标记「成本未知」:上游未公布倍率时不再默默留空
  • 目录刷新带重试:网络抖动不再把正常列表打回兜底
  • 实测输出上限:可发现网关对 max_tokens 的静默钳制

三、积分自动化

每项任务可单独配置执行时刻,默认:

任务默认时刻内容
每日签到09:00领取当日签到积分
活跃上报10:00上报活跃状态
任务奖励11:00报名并领取任务奖励
连登奖励12:00连登档位兑换与抽奖
猫猫旅行09:00 / 21:00出发与领取

细节处理:

  • 漏跑补跑:某个时刻在应用关闭期间过去了,启动后立即补跑,不用等到明天
  • 签到结果分开呈现:今日已签 21/21 —— 本次新领 16 个。「已签到」与「本次领到积分」是两件事,混在一起会让人误以为有账号失败
  • 每账号签到时间:区分「本次领到」与「发现今天已领过」两个时刻 —— 上游不提供签到时间,所以只报插件能作证的时刻
  • 收益账本落盘:跨天自动清零,重启不丢

四、请求可观测

能力说明
请求记录时间、模型、账号、结果、总耗时、排队、首字、token 数、轮换次数
排队时长统计按模型给出 p50 / p95 / 平均排队时长 —— 用数据选模型,而不是凭感觉
输出速度每次请求的 tok/s 与 p50 / 最小 / 最大,按模型聚合
消耗账本今日 / 近 7 天 / 本月消耗(由插件累积观测;上游无时间窗接口)
请求形状诊断上游 400 时记录请求形状(参数名、消息条数、工具数、字符数)

五、界面

  • 双区域标签:国内版 / 国际版各自独立 —— 账号、积分、模型目录、未保存草稿全部隔离,两边同时生效
  • 可折叠列表:池中账号 / 模型 / 请求记录三块默认收起,卡片不占地方
  • 响应式布局:窄窗口(≤640px)自动堆叠
  • 主题三态:跟随系统 / 浅色 / 深色,写在宿主声明主题变量的那一层,切换立即生效
  • 状态标记:冷却中、需重新登录、额度不足、已保留 —— 每种都有自己的原因与对策
  • 一键清除:只要有任何可清除状态(冷却 / 熔断 / 按模型缓存)按钮就出现,点了真的清干净

六、协议兼容

池对外同时说三种协议,三条路由共用同一套账号轮换、冷却、熔断、积分门槛与上下文压缩:

协议路径谁在用
OpenAI Chat/v1/chat/completionsDSH 本体、普通客户端
OpenAI Responses/v1/responsesCodex、任何 Responses 客户端
Anthropic Messages/v1/messagesClaude Code、Anthropic SDK

翻译层与调度层是分开的:新协议只负责把请求转成 chat 体、把回答转回去,调度逻辑一行都不重复。 好处是改轮换策略时三条路由同时生效,不会有人掉队;协议出 bug 也只会是翻译错,不会串到调度。

几个不显然的实现点(都是实测踩出来的):

  • 工具调用在 response.output 里出现两次 —— Codex 靠读最终响应判断「这轮要不要继续」, 只在流事件里发 output_item.done 而 output 数组里没有,Codex 会认为模型只是说了句话, 于是结束回合等人输入,表现为「任务做到一半停了」
  • 工具调用的「调用」与「结果」先配对再发送 —— 上游拒绝「结果早于调用」和「调用没有结果」, 而客户端传来的历史里两种都真实存在(会话被中断、被裁剪过)
  • 对话不以工具轮开头 —— 裁剪过的历史第一条可能就是助手工具调用,上游直接拒; 补一条用户轮在最前面,而不是删掉工具调用(后面的结果是真的)
  • Anthropic 的 stop_reason 不是装饰 —— 模型要用工具时必须报 tool_use, 报成 end_turn 客户端会认为回合结束,刚拿到的 tool_use 块永远不会被执行
  • max_tokens 有地板 —— 池里是推理模型,思考计入 max_tokens。 实测(同一提示词,各 6 次):max_tokens=16 时只有 1/6 能产出内容、64 时 3/6、256 以上 6/6 —— 预算被思考吃光时上游返回 finish_reason: length 且 content 完全为空, 客户端收到一个语法完整、内容为空、无法分辨原因的回复。所以低于 512 的预算会被抬高 (预算仍是上限不是配额,要 64 token 照样得到简短回复,只是至少能得到)

七、请求链路

  • 固定端口:国内 8120 / 国际 8121 —— 重载不会换端口,宿主不再「Connection error」
  • 只绑回环、强制 loopback Host / Origin、常量时间比较令牌、请求体上限 64MB
  • 写操作要求 JSON body —— 这道校验才是真正的防线:跨站的 JSON POST 会被浏览器预检拦下 (本服务不批准预检),而跨站的表单 POST 虽然发得出去、也带环回 Host, 却无法声明 application/json。仅靠 Origin 挡不住表单,因为 file:// 加载的卡片 和沙箱 iframe 发送的都是 Origin: null
  • 上下文超限自动压缩重试,压缩目标取「按窗口」与「按原文一半」的较小值
  • 错误归因:池为空时如实报原因(限流 429 / 额度耗尽 402 / 全部停用 403 / 需重登 401),不再一律 401
  • 冷却跨重载存活:冷却窗口落盘(.workbuddy-xdpool/cooldowns.json), 插件重载不再清空限流惩罚;显式「清除冷却」会同步清掉账本

用数据选模型(实测结论)

来自真实请求记录,不是推测:

模型样本排队 p50排队 p95输出速度 p50倍率
deepseek-v4.1-flash574.2s6.0s263 tok/s0.11
glm-5.3-flash26.7s23.2s40 tok/s0.06

小样本提醒:glm-5.3-flash 只有 2 个样本,排队数字分歧极大(1.6–23s),速度数字则一致。倍率便宜但速度慢约 6 倍。

卡片里的「按模型排队时长」会持续累积样本,样本多了结论才可靠。


目录结构

.
├── lib/
│   ├── index.js        宿主端:账号池、shim、模型目录、调度器(源码,直接编辑)
│   ├── client.js       浏览器端卡片(源码,直接编辑)
│   ├── protocol-compat.js  Anthropic / OpenAI Responses 协议互译
│   ├── bin.js          CLI:doctor 等诊断命令
│   └── index.d.ts      类型
├── .github/workflows/  打 tag 即发布到 npm(OIDC,无需 token)
├── tools/              发布前检查与回归套件
│   ├── run-suite.mjs           全量回归(332 断言)
│   ├── check-publish.mjs       本机发布前检查(需已安装副本)
│   └── check-package-files.mjs CI 可跑的打包完整性检查
├── cordis.patch.yml    插件注册补丁
├── package.json        清单(dsh.bundle / dsh.client 都在这里)
├── screenshots.json    卡片截图清单
├── assets/             截图
├── README.md           本文件
├── README.en.md        英文版
├── UPSTREAM-README.md  上游原版文档(致谢与出处)
├── AGENTS.md           开发约定(**改代码前先读**)
└── CHANGELOG.md        变更记录

lib/ 下的文件是源码,不是构建产物 —— 本仓库没有 src/、没有构建步骤,直接改 lib/ 即可。

tools/ 是开发期脚本,安装和运行都用不到。日常使用只看 lib/。


与上游的关系

本仓库是 XDTrees/dsh-workbuddy-xdpool 的二次开发, 按 MIT 许可保留原作者版权。上游是 TypeScript 项目(src/ → 构建出 lib/),本仓库不是。

两条版本线互相独立,不对齐、不互认:

上游本仓库
包名dsh-workbuddy-xdpooldsh-rotakit
版本1.8.42.3.23
形态TS 源码 + 构建lib/ 即源码

不要往 dsh-workbuddy-xdpool 发布。 那个包名属于上游作者,不是我们的。 参见 AGENTS.md §零。


安装

# 1. 装进 DSH profile(仓库根目录就是插件本体)
#    默认 profile 是 desktop,也可换成你自己的
git clone https://github.com/2861292267/DSH-Official-WorkBuddy-Credit-Proxy.git \
  ~/.dsh/profiles/desktop/node_modules/dsh-rotakit

# 2.(可选)二维码兜底依赖,纯 JS、无原生编译
#    登录链接已能自动打开;只有上游不返回 authUrl 时才回退二维码,此时才需要它
cd ~/.dsh/profiles/desktop/node_modules/dsh-rotakit && npm i qrcode

# 3. 确认 profile 的 bundles 里有它
#    ~/.dsh/profiles/<你的profile>/package.json → dsh.profile.bundles 应含 "dsh-rotakit"

然后重启 DeepSeek Harness。 模型选择器里会出现 WorkBuddy XD Pool 分组。

宿主插件不会热重载。 改了 lib/ 下的文件必须完全退出应用再启动,否则运行中的实例仍在用内存里的旧代码。


自动更新

卡片的「设置」页有检查更新与更新按钮。整条链路是:

检查更新   拉取本仓库 main 分支的 tarball → 读 package.json 的版本号
           (12 秒超时;只读开头一小段,不下载整个包)
更新       下载整个 tarball → 校验 → 逐个文件替换
回滚       把某次更新前的备份原样放回

几条设计上的取舍

只更新运行时真正加载的文件。 TRACKED_FILES 列了六个:三个 .js 模块、 bin.js(doctor 命令的全部逻辑)、cordis.patch.yml(DSH 据此注册 provider)、 以及 package.json。index.d.ts 是纯类型、运行时不加载,因此刻意不在列表里 —— 带上它只会让每次更新多传 94 KB 而不改变任何行为。

先备份全部,再写入任何一个。 早先的版本是"备份一个、立刻覆盖一个", 于是更新失败时留下的备份集是残缺的;而回滚点又只接受完整的备份集, 结果最需要回滚的那次更新恰恰没有回滚点。现在两个阶段分开: 六个文件全部备份完,才开始写第一个。

写入失败会停下并说清楚是哪个文件,而不是抛异常。 临时文件 + 同卷 rename,所以任何单个文件要么是旧内容、要么是新内容,不会有半个。

回滚是可以反悔的。 覆盖前会先把"当前版本"另存为 .pre-rollback-<时间戳>。 备份集本身只按它实际覆盖到的文件恢复 —— 早于某个文件加入的备份集仍然可用, 恢复它等于"回到那个文件还不存在的版本"。

备份只保留最近 3 组。 每次更新会把六个文件复制一份(约 1.1 MB); 成功更新之后自动清理更旧的,否则 lib/ 会一直长下去。

关于「环境不匹配」

情况行为
没装桌面客户端 / 只用了网页版检测不到账号,卡片给出各区域(国内版 / 国际版)的登录指引。插件读的是桌面客户端留在本机的凭据,网页版登录不会被读到。
非 Windows跳过注册表探测,改走 macOS bundle 与配置文件路径;两者都没有时安静返回空,不抛错
GitHub 不可达版本号照常显示(显示本机版本),更新按钮可用,失败信息可读;不会卡在"正在检查更新"
安装目录只读更新失败并明确告知,不留下半写状态

验证方式

每个结论都有对应的可复现验证,核心几类:

验证检查什么
选号逻辑额度门槛、冷却分级、按模型冷却、熔断、租约、粘性、同请求不重复选号
错误归因每种失败类型映射到正确的状态码与原因
签到语义「已签到」与「本次领到」分开;已签到标记不虚报积分
计数口径available 与实际可选账号一致,不虚高
界面中英文案 1:1、CSS 类完整、运行时探针、渲染输出
协议翻译三条路由的事件序列、工具调用往返、非流式与流式分支、stop_reason 映射
写接口鉴权JSON 才放行;表单 / 无 content-type / 外域 Origin / 伪造 Host 全部拒绝
冷却持久化跨重载存活、模型级窗口、过期不复活、reset 同步清账本、多实例合并
推理预算地板小预算被抬高但不越过模型上限(上限是硬界,地板是软界)
打包完整性每个运行时加载的文件都在 files 白名单里(tools/check-package-files.mjs)

回归套件 17 套、332 条断言,全部通过。

其中协议与安全相关的几套是实际跑真实 HTTP 的:起真实的 shim、发真实的请求、 打到真实的 WorkBuddy 上游,而不是对着 fixture 断言。

改完代码后跑:

node tools/run-suite.mjs          # 全量回归
node tools/check-publish.mjs      # 本机发布前检查(需已安装副本)
node tools/check-package-files.mjs  # 打包完整性(CI 也跑这个)

安全须知

  • 插件只读取本机桌面端已经存在的登录态,不发起任何登录流程。卡片内扫码是可选操作,只在明确点击后打开,走的是桌面客户端自己的接口,没有绕过任何鉴权,消耗的是你自己的账号与积分。
  • 令牌只在本机内存与回环 shim 之间传递,卡片上显示的账号一律脱敏。
  • shim 只绑 127.0.0.1,强制回环来源校验。
  • 管理接口的写操作要求 JSON 请求体:跨站页面发不出 JSON POST(预检不被批准), 而它发得出的表单 POST 无法声明 application/json —— 所以本机的账号启停、冷却清除、 插件自更新等操作不会被网页越权调用。
  • 二维码内容经过净化:上游返回的登录二维码先按 SVG 解析并剥掉 script / on* / href 等可执行部分才注入 DOM,不直接信任上游返回的标记。

免责声明

本项目仅供个人学习和研究使用,仅驱动使用者自己的账号在本机调用。请遵守 WorkBuddy 的服务条款;因使用本项目产生的任何后果由使用者自行承担。


许可

MIT。上游版权归 XDTrees 所有,另有部分代码来自 dingminhua、corrinehu(均为 MIT),详见 LICENSE 与文件内的署名注释。

관련 플러그인