loyalchiiina/dsh-workbuddy-hub

dsh-workbuddy-hub

WorkBuddy 转接中枢:注册 WorkBuddy 模型 provider,并附多账号台账悬浮球——实时额度、每日自动签到、限流冷却追踪与一键切号。

未分類deepseek-harnessllm-providermulti-account

dsh-workbuddy-hub (显示名:Workhoney)

界面/看板上显示为 Workhoney;dsh-workbuddy-hub 是包名(内部标识)。

效果图

① 网页界面 —— 宽屏台账表格、实时额度、限流状态、积分有效期分段 (GET /dsh-workbuddy-hub/board,或抽屉里点「网页版」):

网页界面

② 悬浮球主界面 —— 多账号台账、一键切号、签到状态、实时额度:

悬浮球主界面

③ 悬浮球里的 DeepSeek-V4.1-Flash 冷却时间 —— 显示该模型下被限流的账号与精确解禁倒计时(后台每 10 分钟主动探测自动登记):

DeepSeek-V4.1-Flash 冷却时间

WorkBuddy 转接中枢 —— 一个插件同时提供两件事:

  1. 转接(provider):把 WorkBuddy 桌面端(国内版 + 国际版)的模型接进 DeepSeek Harness,像官方 provider 一样出现在模型选择器里(workbuddy / workbuddy-ai 两个分组)。
  2. 记录(ledger):多账号台账悬浮球 —— 手机号(脱敏)、实时额度、签到(含每日自动签到)、积分有效期、限流冷却 CD、一键切号、无感登录(国内版)、国际版账号导入与切号。

自包含:不需要 WorkDaddy,不需要 dsh-workbuddy-connect。 (唯一例外是「假退出登录」这一个动作 —— 它要转发给 WorkDaddy 的本地 API, 没装 WorkDaddy 时该请求会返回 500 并说明原因,不影响其它任何功能。) 账号凭据、手机号索引、积分缓存都存在插件自己的数据目录里(~/.dsh/workbuddy-data/); 国际版的数据与之物理隔离,存在 ~/.dsh/workbuddy-data/ai/ 子目录(见「国际版(WorkBuddy AI)」一节)。


安装

dsh plugin --profile desktop add dsh-workbuddy-hub
# 装完重启一次 DSH

⚠️ 与 dsh-workbuddy-connect 互斥:两者都注册 workbuddy 这个 provider id, 而 DSH 对重复 provider all-or-nothing 拒绝。装 hub 前请先卸载 connect:

dsh plugin --profile desktop remove dsh-workbuddy-connect

前置条件

依赖必要性说明
WorkBuddy 桌面端必需任意版本均可(无版本门槛,见下)。必须已登录至少一个账号。国内版、国际版均可,各自独立
Windows当前实现凭据解密依赖 WorkBuddy 自带的 Electron 运行时
WorkDaddy不需要已完全自包含。仅"假退出登录"一个功能需要它(该路由会直接报错说明未安装)
签到插件自实现逐个解密账号凭据直调官方接口,不依赖任何第三方工具
今日用量无数据源见「今日用量(当前无数据源)」一节:已预留字段,但生产环境恒为空

WorkBuddy 版本要求:没有

检查项结果
插件内版本硬编码除兜底常量外为 0 处(lib/vendor/connect-heartbeat.js 里有两个编译期兜底版本号,见下)
版本校验规则任意数字版本(5.5.x / 5.6.x / 5.7.x / 未来版本均可)
版本来源运行时探测(已安装 App → 版本缓存 → 兜底常量),非写死

兜底常量(只在探测全部失败时才用到,不参与任何版本门槛判断):

常量值用途
FALLBACK_CN_APP_VERSION"5.5.6"国内版 User-Agent / 客户端版本标识的降级形态
FALLBACK_APP_VERSION"5.5.2"国际版同一用途的降级形态

上游 vendor 注释明确写了这是「a shape requirement, not a currency claim」—— 只要求 UA 里带一个形状合法的版本号,上游未见据此分支。

取证说明(只列有出处的两个版本号,不做"跨 N 个版本"这类无法核实的推断):

版本出处说明
5.6.2lib/vendor/connect-heartbeat.js 多处注释:「verified live against 5.6.2」「exactly what WorkBuddy 5.6.2 writes」凭据加密格式(suite 1)就是按这个版本的实测行为对齐的
5.7.7本机注册表卸载项 WorkBuddy 5.7.7 + 默认安装路径实测当前机器上跑通

之所以说"没有版本门槛"是有代码依据的:全仓 grep 不到任何 semver 比较逻辑 (semver / compareVersion / MIN_VERSION 等均 0 命中), 版本号只被当作 UA 字符串的一部分使用。所以新版本出来不需要改插件, 真出问题也更可能是上游接口变了,而不是"版本太新"。

但有两个硬性前提

1|.exe 必须叫 workbuddy.exe(国际版 workbuddyai.exe) 大小写不敏感,但文件名不能改(vendor 的 exeBasename 常量决定)。

2|WorkBuddy 的 Electron 二进制必须能被发现 —— 国内版与国际版各自独立判定:

#途径国内版国际版
1环境变量WORKBUDDY_ELECTRON_BINWORKBUDDY_AI_ELECTRON_BIN
2注册表卸载项读 DisplayIcon / InstallLocation(本机实测 WorkBuddy 5.7.7 → …\WorkBuddy.exe,0)同逻辑(本机实测 WorkBuddy AI 5.7.6 → E:\workbuddy国际版\WorkBuddyAI\WorkBuddyAI.exe,0)
3默认路径%LOCALAPPDATA%\Programs\WorkBuddy\WorkBuddy.exe%LOCALAPPDATA%\Programs\WorkBuddy AI\WorkBuddyAI.exe
4常见位置扫描C:~F: 的 Program Files、Program Files (x86)、盘根,找 WorkBuddy\WorkBuddy.exe同范围,找 WorkBuddy AI\WorkBuddyAI.exe 与 WorkBuddyAI\WorkBuddyAI.exe

⚠️ 卸载项的 InstallLocation 常见为空(只有 DisplayIcon,且带 ,0 后缀), 所以"装了但装在非默认盘"时,靠注册表往往只能拿到带 ,0 的图标路径 —— 插件内部会剥掉后缀再判定。 这也是自检里 foundElsewhere 字段存在的意义:它把实际探测到的路径直接告诉你。

📌 两条探测链不是同一条,看结果时要分清:

  • runInstallSelfCheck()(/selfcheck 用的):只做三件事 —— 读环境变量、查默认路径、 扫 C:~F: 的 Program Files* 与盘根。它自己不读注册表。
  • autoDetectElectronBin()(加载 vendor 之前跑,即插件启动阶段):① 环境变量 → ② 注册表卸载项 → ③ 扫 C:~G: 常见位置,命中后回写到 process.env("本次启动即生效、不需要重启任何东西")。 每个候选还要过布局校验(exe + version 文件 + resources\app.asar 三件齐全), 所以探测到"同名但残缺"的 exe 不会误用。

因为回写这一步,/selfcheck 的 envOk 通常是 true(它看到的是已被自动探测填充过的环境变量), 所以 advice: 'ok' 是可信的。真正会给"找不到"建议的场景是三条途径全空(没装 / 装在怪位置 / 名字被改)。

为什么必需:WorkBuddy 的 accessToken 是加密的,解密要把 ELECTRON_RUN_AS_NODE=1 传给 WorkBuddy 自己的 Electron 运行时才能取到密钥。 找不到二进制 = 凭据解不开 = 模型选择器里没有对应分组(国内版缺 → 没有 workbuddy, 国际版缺 → 没有 workbuddy-ai 且国际版额度一律取不到)。


🔴 首次安装最常见的问题

症状:装好插件、重启 DSH 后,模型选择器里完全没有 WorkBuddy 分组,也没有任何提示。

成因:WorkBuddy 装在非默认位置,且没有可用的注册表记录、盘符又不在扫描范围 (或目录名被改过)→ 上述四条发现路径全部失效。

解决:设置用户环境变量后重启 DSH:

# 把路径换成你自己机器上的 WorkBuddy.exe
[Environment]::SetEnvironmentVariable(
  'WORKBUDDY_ELECTRON_BIN',
  'D:\Your\Path\WorkBuddy\WorkBuddy.exe',
  'User')

自检(推荐先跑这个)

hub 不注册任何斜杠命令(没有 /selfcheck 这种用法),自检是一个 HTTP 路由:

GET http://127.0.0.1:<DSH端口>/dsh-workbuddy-hub/selfcheck

(<DSH端口> = 你当前 DSH 实例的端口;桌面端默认是随机端口,可从 DSH 日志或浏览器地址栏取得。 等价替代:GET /dsh-workbuddy-hub/health,它把同一个 selfCheck 内嵌在 selfCheck 字段里。)

它会主动检查环境变量、默认路径、常见安装位置、登录文件,并给出可直接照做的建议。 国内版与国际版各有一块(workbuddy / workbuddyAI):

{
  "ok": true,
  "selfCheck": {
    "platform": "win32",
    "workbuddy": {
      "envVar": "WORKBUDDY_ELECTRON_BIN",
      "envValue": "D:\\Your\\Path\\WorkBuddy\\WorkBuddy.exe",
      "envOk": true,
      "defaultPath": "C:\\Users\\<你>\\AppData\\Local\\Programs\\WorkBuddy\\WorkBuddy.exe",
      "defaultPathOk": false,
      "foundElsewhere": ["D:\\Your\\Path\\WorkBuddy\\WorkBuddy.exe"],
      "advice": "ok"
    },
    "authFile":    { "path": "…\\workbuddy-desktop.info", "exists": true, "encrypted": true },
    "ownedData":   { "dir": "…\\.dsh\\workbuddy-data", "accounts": 8 },
    "workbuddyAI": {
      "envVar": "WORKBUDDY_AI_ELECTRON_BIN",
      "envOk": false,
      "defaultPath": "…\\Programs\\WorkBuddy AI\\WorkBuddyAI.exe",
      "defaultPathOk": true,
      "foundElsewhere": [],
      "authPath": "…\\workbuddy-desktop-ai.info",
      "authExists": true,
      "advice": "ok"
    }
  }
}

workbuddy.advice(国内版)的四种取值:

情形建议
没登录过提示先去 WorkBuddy 桌面端登录一次(附期望路径)
找不到程序提示设置 WORKBUDDY_ELECTRON_BIN(并说明为何必需)
装在非默认位置直接给出探测到的完整路径
一切正常ok

workbuddyAI.advice(国际版)的四种取值:

情形建议
已登录但找不到程序提示设置 WORKBUDDY_AI_ELECTRON_BIN(说明缺它则模型列表无 workbuddy-ai、国际版额度也取不到)
找不到程序且没登录(多半是没装)ok-or-not-installed
装在非默认位置直接给出探测到的完整路径
一切正常ok

注意判定顺序:国际版是先看"找不找得到程序"、再看"登没登录"。 所以"没装国际版"不会报错,只会安静地返回 ok-or-not-installed —— 国内版用户看到这一句属正常,不必处理。

/health 也内嵌了同样的 selfCheck,方便一眼排查。


能做什么

转接:WorkBuddy 模型 → DSH

注册 workbuddy 与 workbuddy-ai 两个 provider,模型选择器里直接出现 Deepseek-V4.1-Flash、GLM、Kimi 等。凭据复用 WorkBuddy 桌面端登录态,账号在 App 里切换后自动跟随。

模型清单有两个来源:先从上游拉真实目录,拉不到时用 vendor 内置的兜底清单 (FALLBACK_WORKBUDDY_MODELS 16 个 + FALLBACK_WORKBUDDY_AI_MODELS 20 个 = 36 个)。 兜底清单只是保险,实际展示以你账号能访问到的上游目录为准 —— 所以每个账号看到的数量可能不同, 不要按 36 去核对。

记录:多账号台账(悬浮球)

点右下角悬浮球展开。抽屉头部按钮:+ 登录国内账号 / 签到 / 网页版 / 刷新 / ★ Star / ×

国际版的登录与导入按钮不在头部,而在「国际版 · WorkBuddy AI」分组内 (导入国际版账号 / + 登录国际账号)—— 见下方「国际版(WorkBuddy AI)」一节。

信息说明
手机号 / 昵称一律脱敏(156****912* / 好…来)—— 后四位只露 3 位
实时额度(上游权威)每账号的真实总额度与剩余,来源 POST /v2/billing/meter/get-user-resource
套餐数 + 即将到期有套餐 3 天内到期会标 ⚠(建议优先用快作废的)
剩余积分 + 有效期分段3 天内到期的会标 ⚠
签到状态含每日自动签到结果
限流冷却 CD自建台账(~/.dsh/workbuddy-rate-limit-ledger.json)

界面上的账号顺序说明文字是「可用 → 积分最早过期 → 今日用得少」。 实际排序键(lib/index.js 的 rows.sort)依次是:

  1. usable(可用优先)
  2. soonestExpiry(积分最早过期优先,无过期日的排最后)
  3. usedToday —— 仅当 usageKnown === true 时参与;未知用量的账号一律按 MAX_SAFE_INTEGER 排在已知之后
  4. credits(余额多优先)

今日用量是死字段(见下节),因此第 3 键在当前生产环境下恒等,退化为 「可用 → 最早过期 → 余额多」。排序逻辑本身没坏,是它依赖的数据源没有值。

今日用量(当前无数据源 —— 已预留字段,恒为空)

这一项不会在界面上显示任何数字,请勿按它做判断:

事实取证
数据源 = %APPDATA%\WorkDaddy\credit-usage.dblib/index.js DB_PATH = path.join(WD_DIR, 'credit-usage.db')
文件不存在 → 直接返回空 Map,不报错loadUsageFromDb() 首行 if (!existsSync(DB_PATH)) return { usage: out, sync }
装了 WorkDaddy 也只是"可能有值"需要该库里的 credit_usage_records 表当天有记录
modelsToday 恒为空对象三处初始化全是 modelsToday: {},,没有任何代码往里写;客户端 `Object.keys(r.modelsToday
上游没有用量端点代码注释记录:实测 8 个候选路径全部 404

结论:usedToday / requestsToday / usageSyncedAt / usageKnown 这套字段是为将来接数据源预留的, 当前生产环境下 usageKnown 恒为 false(files 分支:u.used > 0 || !stale,而 u.used 来自空 Map, stale 因 syncAt === 0 恒为 true)。UI 已经不渲染它们。

实时额度(真正在用的额度数据)

原有「今日用量 / 今日请求数」拿不到之后,改用上游逐套餐的额度接口,信息反而更准确:

POST {billingBase}/v2/billing/meter/get-user-resource
     body: { ProductCode: "p_tcaca", Status: [0,3], ... }
返回: CapacitySize / CapacityUsed / CapacityRemain  总额度 / 已用 / 剩余
      CycleStartTime / CycleEndTime                  周期(= 积分有效期)
      DeductionEndTime                               扣费截止(过期时间戳)
      PackageName / SubProductName                   套餐名

billingBase 按凭据的 domain 自动分流(billingBaseFor()): workbuddy.ai → https://www.workbuddy.ai;其余 → https://www.codebuddy.cn。 所以国际版账号同样能取到额度,不是"国际版不支持"。

这与切号无关:它逐账号解密凭据后直接查上游,不需要该账号是当前登录账号。 结果带 TTL 缓存(国内/国际各一套),客户端 30 秒轮询不会每次打上游。

签到(含每日自动签到)

  • 手动:抽屉里点「签到」,对所有已备份的国内版账号逐一签到
  • 自动:启动后约 8 秒跑一次 + 每 60 分钟一次(startCheckinScheduler),逐账号容错、跳过今日已签
  • 原理:逐个解密该账号自己的凭据调官方接口,不切换当前登录账号

台账写在 ~/.dsh/workbuddy-checkin-ledger.json(CHECKIN_LEDGER_PATH)。

⚠️ 签到目前只覆盖国内版账号。runCheckin() 的账号来源是 listAllAccountUids(),它只扫 workbuddy-data/accounts/ 与 WorkDaddy 的 accounts/, 不含 workbuddy-data/ai/accounts/。国际版行的"今日签到"读的是同一份台账里 region === 'global' 的记录,当前不会有代码写入它 —— 所以国际版分组里该字段显示「未签到」。 这一条已记录在 CHANGELOG 的「已知边界」里。

限流冷却(CD)自动登记

为什么要做:用户实测撞上 429 后,台账里没有记录 —— 因为原来只有手动命令 (workbuddy-ledger.cjs limit --text "...",那是 WorkDaddy 项目里的外部脚本,不在本插件内), 没人跑就永远记不上。

现在三层保障:

机制触发说明
主动探测每 10 分钟自动(启动后 45 秒跑第一轮)逐账号发一次 max_tokens:1 的轻量请求;撞到 429 自动登记(含解禁时间),探到可用则自动清除 CD 记录
报错上报POST /dsh-workbuddy-hub/ledger/record把 DSH 的 429 报错原文 POST 过去,自动识别账号(缺省=当前登录账号)与解禁时间
手动探测POST /dsh-workbuddy-hub/ledger/probe立即跑一轮,不必等定时器

查看台账:GET /dsh-workbuddy-hub/ledger(含探测调度状态) 清理过期:POST /dsh-workbuddy-hub/ledger 或加 ?clear=1

探测成本:max_tokens: 1,可忽略;被限流的账号不消耗额度。 账号之间间隔 1.5 秒,避免探测本身把账号打成限流。

操作:一键切号

悬浮球里点「切到此账号」→ 优先走本地 API(若有),不可用时自动回落内置切号逻辑 (复制账号备份到官方登录位,原子替换)→ 再把凭据镜像给 DSH,切完立刻生效。 响应里的 via 与 credentialMirror 字段标明实际走了哪条路径。

国际版账号的「切到此账号」永远跳过本地 API(那是国内工具,不认识国际版 uid), 直接走自包含切换,把凭据写入 workbuddy-desktop-ai.info,不影响国内版登录位。

登录账号(无感登录,国内版)

抽屉头部点「+ 登录国内账号」→ 打开浏览器(workbuddy.cn)扫码 → 自动入库。 不退出当前账号,新账号直接进插件自己的账号库。

国际版对应的入口是分组内的「+ 登录国际账号」(走 workbuddy.ai,见下节)。

网页版看板

GET /dsh-workbuddy-hub/board(点「网页版」按钮用系统浏览器打开)—— 宽屏表格 + 冷却队列 + 倒计时 + 登录入口 + 30 秒自动刷新(登录轮询期间自动让路,避免打断扫码)。

⚠️ 网页看板只展示国内版账号(renderBoard 只遍历 state.rows,不渲染 rowsAI), 也不显示今日用量。国际版账号请到抽屉里看。

隐私

手机号与昵称在任何输出面都不完整显示:悬浮球抽屉、网页版看板、/state API 响应、 /global/status 响应。(/state 是 HTTP 路由,本机任意进程都能读,所以 API 层也必须脱敏。)


国际版(WorkBuddy AI)

WorkBuddy 有两个互相独立的桌面客户端,hub 两边都支持,但它们的边界物理隔离:

维度国内版国际版
客户端 exeWorkBuddy.exeWorkBuddyAI.exe
站点workbuddy.cn / codebuddy.cnworkbuddy.ai
provider idworkbuddyworkbuddy-ai
官方登录位(加密凭据)%LOCALAPPDATA%\CodeBuddyExtension\Data\Public\auth\workbuddy-desktop.info同目录 …\workbuddy-desktop-ai.info
插件自持目录~/.dsh/workbuddy-data/~/.dsh/workbuddy-data/ai/
账号索引workbuddy-data/accounts-index.jsonai/accounts-index.json
环境变量(找 exe)WORKBUDDY_ELECTRON_BINWORKBUDDY_AI_ELECTRON_BIN
台账行state.rowsstate.rowsAI(不混进 rows)
签到支持(自动 + 手动)⚠️ 当前无签到执行路径(见下)
网页看板展示⚠️ 不展示(只在抽屉里)
限流冷却 CD支持(自建台账)⚠️ 不参与探测(与签到同一根因,见下)

为什么强行分两组:用户明确要求「分成两组」;且两边的额度/冷却/签到语义不同, 混在一起会让「今日用量合计」之类的聚合数字失去意义(代码里就是这么写的)。

两种 auth 来源

国际版支持两条获取账号的路径,首选第一条:

#方式触发做了什么安全性
1导入已登录账号(首选)分组内点「导入国际版账号」→ POST /dsh-workbuddy-hub/global/import纯读 workbuddy-desktop-ai.info,把当前登录账号复制进 ai/accounts/<uid>.info + 索引不改动国际版 App 的登录状态
2扫码登录(备用)分组内点「+ 登录国际账号」→ POST /dsh-workbuddy-hub/login/start?region=global打开 workbuddy.ai 扫码授权,轮询后写入 ai/accounts/不退出当前账号

⚠️ 国际站登录页在未登录态下配色极浅、近乎全白,看起来像"白屏/没打开"。 这是腾讯国际站页面自身的行为(已实测与 VPN 代理无关:走/不走代理资源都返回 200), 不是插件 bug。所以日常请走路径 1(先在 App 里登录,再回来点导入); 扫码只在你需要新登一个国际版账号时才用。

国际版分组里有什么

  • 标题:国际版 · WorkBuddy AI + 副标题 workbuddy.ai / 桌面端已登录|未登录 / N 个账号
  • 两个按钮:导入国际版账号(已登录时显示「(刷新凭据)」,可重复导入以刷新)、+ 登录国际账号
  • 每行:脱敏手机号 / 昵称、可用|登录失效、今日签到(见下方边界)、实时额度(含套餐数)、站点 domain、非当前账号带「切到此账号」
  • 三种空态都给明确提示,不留白、不假装有数据:
    • 国际版 App 未登录 → 「未检测到国际版 WorkBuddy AI 的登录状态」
    • 已登录但没导入 → 「桌面端已登录,但还没导入到台账」
    • 正常 → 账号卡片列表

相关路由

方法路由说明
POST/dsh-workbuddy-hub/global/import导入国际版当前登录账号(不改动 App 登录态)
GET/dsh-workbuddy-hub/global/statusappSignedIn / authPath / accounts(数量)/ index(脱敏列表)
POST/dsh-workbuddy-hub/login/start?region=global发起国际版扫码登录(不传 region = 国内版)
POST/dsh-workbuddy-hub/switchbody {uid, region:'global'} → 写入 workbuddy-desktop-ai.info
GET/dsh-workbuddy-hub/state返回体含 rowsAI / globalAccountCount / globalAppSignedIn

🔴 国际版的两条已知边界(不要误判为故障)

  1. 签到不覆盖国际版:runCheckin() 的账号来源是 listAllAccountUids(), 它只扫 workbuddy-data/accounts/ 与 WorkDaddy 的 accounts/,不扫 ai/accounts/。 国际版行显示的「今日签到」读的是同一台账里 region === 'global' 的记录,当前无人写入 → 恒显示「未签到」。抽屉头部的「签到」按钮同理,只签国内版。
  2. 网页看板不含国际版:renderBoard() 只遍历 state.rows,没有渲染 rowsAI。 国际版账号只能在抽屉里看。

补充一条同根因的边界:限流冷却(CD)自动登记也不覆盖国际版 —— runRateLimitProbe() 的账号来源同样是 listAllAccountUids(),且固定用 variant: CN_VARIANT 读国内版凭据。所以国际版账号不会出现在 CD 台账里, 也不会被自动探测清除记录。

这三条(签到、看板、CD)都可以用同一句话解释:所有"批量遍历账号"的逻辑 都走 listAllAccountUids(),而它的扫描范围从 v1.9.0 加国际版时没有一起扩。 这不是设计选择,是实现遗留。

这两条都是真实实现边界,不是文档遗漏。之所以写在这里,是因为 「国际版显示未签到」「看板上没有国际版」极容易被当成 bug 报上来。


架构

dsh-workbuddy-hub
├── lib/vendor/            ← 内置 dsh-workbuddy-connect(MIT,见 lib/vendor/LICENSE.connect.txt 与 NOTICE.md)
│   ├── connect-index.js       provider 注册 / 凭据解密 / 目录刷新
│   └── connect-heartbeat.js   环回端点 + WorkBuddy Electron 密钥提取 + 兜底模型清单
├── lib/index.js           ← 宿主:台账路由 + 切号 + 签到 + 登录 + 自检 + 两套 provider 注册
├── lib/client.js          ← 客户端:悬浮球 + 抽屉(原生 DOM,不用 iframe)
├── locale/{zh,en}.json    ← 插件管理器的标题 / 描述
└── docs/images/           ← 截图

~/.dsh/workbuddy-data/     ← 插件自持数据(国内版;首次自动迁移)
├── accounts/<uid>.info        国内版账号凭据
├── accounts-index.json        手机号 / 昵称 / uin 索引
├── credit-cache.json          积分缓存(含有效期分段)
└── ai/                        ← 国际版(WorkBuddy AI)**物理隔离**
    ├── accounts/<uid>.info        国际版账号凭据
    └── accounts-index.json        国际版索引(带 region:'global' / domain)

其它落盘文件(都在 ~/.dsh/ 下,不在 workbuddy-data/):

文件内容
~/.dsh/workbuddy-rate-limit-ledger.json限流冷却台账
~/.dsh/workbuddy-checkin-ledger.json签到台账(国内版与国际版共用一份,记录里带 region 字段)

为什么内置 vendor 而不是重写:凭据解密必须用 ELECTRON_RUN_AS_NODE=1 跑 WorkBuddy 自己的二进制取 atRestSecretKey。这段逻辑重写风险极高、且要跟进 DSH 内核与 pi-ai 的 API 变更, 因此内置上游 MIT 代码并保留其版权声明。


数据流

国内版 App ──登录态──> workbuddy-desktop.info            ┐
                                                          ├─ lib/vendor 解密(各自 Electron 密钥)
国际版 App ──登录态──> workbuddy-desktop-ai.info          ┘
                                │
                                ▼
                workbuddy provider ─────> DSH 模型选择器
                workbuddy-ai provider ──> DSH 模型选择器

~/.dsh/workbuddy-data/accounts/*.info     ──> 国内台账 / 切号 / 签到 ──┐
~/.dsh/workbuddy-data/ai/accounts/*.info  ──> 国际台账 / 切号         ─┤
                                                                      ▼
                                                     /state(rows + rowsAI)
                                                                      │
                                                         悬浮球抽屉(两组)
                                                         网页看板(仅 rows)

故障排查

症状处理
模型选择器没有 WorkBuddy 组查 GET http://127.0.0.1:<DSH端口>/dsh-workbuddy-hub/selfcheck 的 workbuddy.advice;或 GET /dsh-workbuddy-hub/health 的 provider 字段
模型选择器没有 workbuddy-ai 组同一 selfcheck 看 workbuddyAI.advice,多半是缺 WORKBUDDY_AI_ELECTRON_BIN
悬浮球看不到账号GET /dsh-workbuddy-hub/selfcheck 看 ownedData.accounts;为 0 就用「+ 登录国内账号」加一个
国际版显示「未检测到登录状态」先在国际版 App 里登录一次,再点「导入国际版账号」
国际版额度「未取到」缺 WORKBUDDY_AI_ELECTRON_BIN(解密凭据要国际版自己的 Electron)
国际版「今日签到」永远未签已知边界:签到只覆盖国内版,见「国际版(WorkBuddy AI)」一节的边界说明
切号后 DSH 没变看返回的 credentialMirror 字段(hardlink-alive 表示正常)
签到失败GET /dsh-workbuddy-hub/checkin/state 看台账里的 message
与 connect 冲突两个都注册 workbuddy,卸载其一

License

MIT。

lib/vendor/ 内含 dsh-workbuddy-connect(MIT, Copyright (c) 2026 Corrine Hu)的 未修改副本(仅调整了相对 import 路径),其许可证见 lib/vendor/LICENSE.connect.txt。