dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
WorkBuddy 转接中枢:注册 WorkBuddy 模型 provider,并附多账号台账悬浮球——实时额度、每日自动签到、限流冷却追踪与一键切号。
界面/看板上显示为 Workhoney;
dsh-workbuddy-hub是包名(内部标识)。
① 网页界面 —— 宽屏台账表格、实时额度、限流状态、积分有效期分段
(GET /dsh-workbuddy-hub/board,或抽屉里点「网页版」):

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

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

WorkBuddy 转接中枢 —— 一个插件同时提供两件事:
workbuddy / workbuddy-ai 两个分组)。自包含:不需要 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 | 不需要 | 已完全自包含。仅"假退出登录"一个功能需要它(该路由会直接报错说明未安装) |
| 签到 | 插件自实现 | 逐个解密账号凭据直调官方接口,不依赖任何第三方工具 |
| 今日用量 | 无数据源 | 见「今日用量(当前无数据源)」一节:已预留字段,但生产环境恒为空 |
| 检查项 | 结果 |
|---|---|
| 插件内版本硬编码 | 除兜底常量外为 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.2 | lib/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_BIN | WORKBUDDY_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 与 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)依次是:
usable(可用优先)soonestExpiry(积分最早过期优先,无过期日的排最后)usedToday —— 仅当 usageKnown === true 时参与;未知用量的账号一律按 MAX_SAFE_INTEGER 排在已知之后credits(余额多优先)今日用量是死字段(见下节),因此第 3 键在当前生产环境下恒等,退化为 「可用 → 最早过期 → 余额多」。排序逻辑本身没坏,是它依赖的数据源没有值。
这一项不会在界面上显示任何数字,请勿按它做判断:
| 事实 | 取证 |
|---|---|
数据源 = %APPDATA%\WorkDaddy\credit-usage.db | lib/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 秒轮询不会每次打上游。
startCheckinScheduler),逐账号容错、跳过今日已签台账写在 ~/.dsh/workbuddy-checkin-ledger.json(CHECKIN_LEDGER_PATH)。
⚠️ 签到目前只覆盖国内版账号。
runCheckin()的账号来源是listAllAccountUids(),它只扫workbuddy-data/accounts/与 WorkDaddy 的accounts/, 不含workbuddy-data/ai/accounts/。国际版行的"今日签到"读的是同一份台账里region === 'global'的记录,当前不会有代码写入它 —— 所以国际版分组里该字段显示「未签到」。 这一条已记录在 CHANGELOG 的「已知边界」里。
为什么要做:用户实测撞上 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 有两个互相独立的桌面客户端,hub 两边都支持,但它们的边界物理隔离:
| 维度 | 国内版 | 国际版 |
|---|---|---|
| 客户端 exe | WorkBuddy.exe | WorkBuddyAI.exe |
| 站点 | workbuddy.cn / codebuddy.cn | workbuddy.ai |
| provider id | workbuddy | workbuddy-ai |
| 官方登录位(加密凭据) | %LOCALAPPDATA%\CodeBuddyExtension\Data\Public\auth\workbuddy-desktop.info | 同目录 …\workbuddy-desktop-ai.info |
| 插件自持目录 | ~/.dsh/workbuddy-data/ | ~/.dsh/workbuddy-data/ai/ |
| 账号索引 | workbuddy-data/accounts-index.json | ai/accounts-index.json |
| 环境变量(找 exe) | WORKBUDDY_ELECTRON_BIN | WORKBUDDY_AI_ELECTRON_BIN |
| 台账行 | state.rows | state.rowsAI(不混进 rows) |
| 签到 | 支持(自动 + 手动) | ⚠️ 当前无签到执行路径(见下) |
| 网页看板 | 展示 | ⚠️ 不展示(只在抽屉里) |
| 限流冷却 CD | 支持(自建台账) | ⚠️ 不参与探测(与签到同一根因,见下) |
为什么强行分两组:用户明确要求「分成两组」;且两边的额度/冷却/签到语义不同, 混在一起会让「今日用量合计」之类的聚合数字失去意义(代码里就是这么写的)。
国际版支持两条获取账号的路径,首选第一条:
| # | 方式 | 触发 | 做了什么 | 安全性 |
|---|---|---|---|---|
| 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、非当前账号带「切到此账号」| 方法 | 路由 | 说明 |
|---|---|---|
POST | /dsh-workbuddy-hub/global/import | 导入国际版当前登录账号(不改动 App 登录态) |
GET | /dsh-workbuddy-hub/global/status | appSignedIn / authPath / accounts(数量)/ index(脱敏列表) |
POST | /dsh-workbuddy-hub/login/start?region=global | 发起国际版扫码登录(不传 region = 国内版) |
POST | /dsh-workbuddy-hub/switch | body {uid, region:'global'} → 写入 workbuddy-desktop-ai.info |
GET | /dsh-workbuddy-hub/state | 返回体含 rowsAI / globalAccountCount / globalAppSignedIn |
runCheckin() 的账号来源是 listAllAccountUids(),
它只扫 workbuddy-data/accounts/ 与 WorkDaddy 的 accounts/,不扫 ai/accounts/。
国际版行显示的「今日签到」读的是同一台账里 region === 'global' 的记录,当前无人写入 →
恒显示「未签到」。抽屉头部的「签到」按钮同理,只签国内版。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,卸载其一 |
MIT。
lib/vendor/ 内含 dsh-workbuddy-connect(MIT, Copyright (c) 2026 Corrine Hu)的
未修改副本(仅调整了相对 import 路径),其许可证见 lib/vendor/LICENSE.connect.txt。