dingminhua/dsh-connect-workbuddy

DSH Connect WorkBuddy

将本机登录的 WorkBuddy 模型接入 DSH,支持逐模型图片输入开关、账号切换,以及带每日签到的只读积分概览。

模型与账号接入dsh-plugin-marketdsh-plugins

dsh-connect-workbuddy

English · 安装 · 工作原理 · 账号池 · 更新日志 · 问题反馈

一个独立的 DeepSeek Harness (DSH) bundle 插件。它把本机已登录的 WorkBuddy 账号(国内版与国际版 WorkBuddy AI 均支持)接到 DSH 的模型选择器,同时提供账号池、可勾选的模型管理,以及一套围绕积分的只读能力:每次刷新按上游目录自动读取每个模型的积分倍率(x0.79 这种,倍率完全由上游给出,插件不写死也不猜)、逐模型开关图片输入、多账号切换与账号池自动换号、每日签到、以及卡片与输入框里胶囊式的只读积分读数。国内版与国际版是两个并行的供应商(`workbuddy` / `workbuddy-global`),可同时使用;插件设置卡片以 tab 区分两者,方便统一管理。只需本机 WorkBuddy 的登录态,或在浏览器里登录一次即可。

风险提示

WorkBuddy 已开始限制非官方客户端请求。本项目不保证相关接口长期可用,上游一旦收紧限制,插件可能失效。请先阅读文末「免责声明」,自行评估账号风险。使用官方 WorkBuddy 客户端始终是最安全的选择。

需要说明的是,这个插件的目的很普通:省掉在 WorkBuddy 客户端和 DSH 之间来回切换的麻烦。你已经在自己的机器上登录了 WorkBuddy,插件只是把那份已有的登录态接到 DSH 的模型选择器上,让你在写代码的地方直接用到它——不提供账号、不共享凭据、不绕过计费。每一次调用都消耗你自己账号的额度,按上游给出的倍率真实扣费,插件从不修改账号状态,也不附带任何提权或刷额度的能力。

它依赖的是 WorkBuddy 客户端自身的接口(非官方开放 API),所以上游调整时插件可能需要跟着调整——这是这类工具的常态,也是这条提示存在的原因。请自行评估账号风险后再决定是否安装。

功能特性

  • 双供应商并行接入(国内版 + 国际版) —— 国内版注册为 workbuddy provider(GLM-5.3、DeepSeek-V4-Pro、Kimi-K3、MiniMax-M3、Hy3 等),国际版注册为 workbuddy-global provider(GPT-5.6、Gemini-3.5-Flash、GLM-5.3、Kimi-K3 等)。有账号的区域其模型会出现在 DSH 模型选择器里:两边都有账号时两组同时可选,不同会话可以各选一边,互不干扰。本机没有账号的区域不列出任何模型(该区域一个请求也发不出去,列出来只会点到就失败);只要在该区域登录一次,「重新检测账号」或重启后模型就会自动出现——provider 本身始终注册着,因为它的设置卡片正是登录的入口(重新检测在账号池区块里)。模型名内嵌上游积分倍率(如 GLM-5.3 · x0.79),与 WorkBuddy 自身模型菜单一致;活动时段显示的是折后价——「夜间折扣」开着的时候,卡片与选择器上的数字一起变成 x0.06,活动一过自动回到原价,不需要重启(#120)。
  • 插件卡片 tab 切换 —— 设置卡片顶部为「国内版 / 国际版」两个 tab,各含独立的账号池与模型管理;每个 tab 的账号、目录、勾选与未保存草稿完全隔离——在一个 tab 里切账号或刷新模型,不会触碰另一边的运行时目录与会话。
  • 可单独关闭任一版本供应商 —— 每个 tab 右侧有一个勾选框(默认都勾选)。取消勾选即把该供应商从 DSH 模型选择器里彻底撤掉(不是只藏起 tab):它的路由与「设置 → 模型」页的目录条目一并摘除,启动时也不再为它发无用请求。账号、积分、目录与勾选全部保留,重新勾选即完整恢复。典型用法是「我根本用不到国际版」——关掉它,让选择器干净。注意:若某会话此前选中的正是被关闭供应商的模型,该会话再调用会报 NO_ADAPTER,卡片会在关闭态的 tab 上明确提示这一点,不会静默失败。
  • 设置卡片分四栏:账号 / 模型 / 能力 / 诊断 —— 原来这张卡片是一列 3000 多像素的长页,把两类互不相干的事情混排在一起:运维账号(谁在服务、还剩多少、切换顺序、签到日志)与编辑目录(勾模型、上下文预算、推理档、关闭思考)。它们连提交语义都不同——账号改动点一下即生效,目录改动是草稿、必须点保存——所以「保存」按钮常常离它要保存的东西隔着三屏。现在分栏,一栏只做一件事;切栏既不会重新拉取,也不会丢掉未保存的目录草稿(非活动栏留在页面里,只是通过 hidden 不可见,因此也不进入无障碍树)。第三栏「能力」放的是授权类开关——允许调用本机 CodeBuddy CLI、允许模型自主生图、积分读数显示在哪——它们点一下即生效、从不经过保存,而且每一个都留着长自己子选项的位置(CLI 的工作目录、生图的尺寸或价格上限),所以不挤在账号表格上方那一小条里。第四栏「诊断」是只读的事实表:当前区域、上游域名、真正在服务的账号名、登录有效期、目录里共几个模型、启用了几个、账号池开关,以及上游最近一次错误的原文(原样引用,不翻译、不概括)——这一栏存在的唯一理由就是能被整段贴进问题反馈,所以里面没有任何会改设置的控件。
  • 模型管理 —— 从上游刷新完整模型目录,逐项勾选启用或禁用;刷新是草稿操作,点保存才生效。上游同时给出积分倍率、上下文/输出上限与推理档位;图片输入按厂商核验表判定:刷新时只有厂商文档明确写为原生多模态的模型才自动勾选,文档写明纯文本的不勾,没有厂商资料的一律按「未核实」不猜(上游的 supportsImages 是平台侧声明,不等于模型能看图,不参与判定)。用户仍可随时手动勾选,勾选状态保存在本地。每个模型行还有「可关闭思考」(`off`)勾选框:决定要不要为该模型提供「完全关闭思考」这一档位。默认由内置复核名单给出(上游声称可关闭、实测却对它返回 HTTP 400 的 9 个模型默认不勾),你可以按模型改勾/取消——取消用于当前不需要该档位的模型;名单内那 9 个模型不可勾选(行上标「拒绝 off」,悬停有说明):该档位被勾上后不只是多一个选项,pi-ai 会把它当作所有未指定档位请求的默认值,压缩摘要请求正属于这一类,勾一次等于让该模型的每一次 /compact 都 400 / 11133(issue #83)。名单外的模型仍完全由你的勾选与自身声明决定;首次保存即写入你的勾选,之后不再重算。
  • 模型行上的活动徽标 —— 上游的模型目录里带着活动信息(免费试用、夜间折扣、限时免费、订阅优先等),插件不新增任何请求(复用刷新模型时本来就会拉的那份目录配置),直接在模型行上画一枚胶囊徽标:颜色与文案都由上游给出,悬停能看到上游的活动说明、活动的起止日期与每日生效时段(例如「夜间 23:00–7:50」)。徽标是「此刻」的函数:活动只在它自己声明的时间窗内显示,窗口一关徽标就自己消失;跨零点的时段按上游声明的时区判断;同一个模型命中多条活动时只画优先级最高的那条。上游没给结束时间的权益标记(如「订阅优先」)则常驻,直到上游把它下架。徽标只是提示,不改变模型行为——它标注的是上游此刻给出的活动与折扣,不替你做选择。同一枚徽标也出现在 DSH 的模型选择器里:菜单每一行都在模型名之后、队列图标之前画同样的胶囊 —— 菜单行用上游的短词(「夜间」「限免」),输入框旁那个触发器用完整词(「夜间折扣」「独家限免」);判定用的是同一份数据、同一个时钟,因为选模型的那一屏正好是最该看到「现在免费吗」的地方。(两个区域的上游目录都下发活动字段:国内版按 App 身份读 /v3/config 时带 8 条促销 + 1 条等级标记,国际版带 5 条 + 1 条;取证与判定规则见 docs/PROMO_BADGE.md。)
  • 模型行上的繁忙程度(仅国内版) —— 上游另有一条只读探针(POST /v2/chat/busy,一次最多 50 个 id,免费、不动账号状态)回答「这个模型此刻多忙」,插件在每次刷新目录时顺带问一次,把等级画成模型名旁边的一枚小人图标:排队人数 = 小人个数(正常 1 个黄人 / 轻微拥堵 2 个橙人 / 极度拥堵 3 个红人),空闲则是一枚绿色小火箭(没人排队 = 可以全速跑)——空闲不画成「绿色的 1 个人」,因为 12px 下「没人等」与「有 1 个人在等」必须一眼分开;不可用是一个划掉的小人。探针没回答的 unknown 一律不画,「没被告知」不会被当成「一切正常」;颜色跟随主题,title 悬停文字(「极度拥堵」四个字)仍在。等级不落盘:目录是能活过重启的事实,负载只是那一分钟的事实,所以等级只盖在运行时目录上、随进程消失,探针失败只记一条 warn,绝不挡住目录刷新。国内版实测这条探针在 CLI 与 App 两种身份下答案逐字节相同,50 个 id 的上限也真实存在(62 个 → HTTP 400),19 个模型 186 ms 返回。国际版没有这个徽标,因为国际网关根本没有这条接口 —— 实测 www.workbuddy.ai 对 /v2/chat/busy 回 404(/v3/chat/busy、/v2/models/busy 等同族路径也全是 404),而同一份凭据读它的 /v3/config 正常返回 28 个模型、没有任何负载/排队字段。这不是本插件没实现,也不是配置或权限问题:上游国际版从未提供这个数据。所以国际版只显示模型名与活动徽标,探针那条请求失败后安静退化成「没有徽标」,不会挡住模型列表,也不会用延迟或积分去猜一个「忙不忙」。
  • 排队时说清楚你排在第几位 —— 上游用三个业务码表达排队(6020 等待、6021 队列满、6022 已达上限)。撞上它们时,插件不再给一句上游报错,而是「模型排队中:你排在第 8777 位,队列共 9021 个请求。请稍后重新发送这条消息,或换一个模型」。排队不会触发换号:队列跟随的是模型与平台级的队伍,换个账号拿到的还是同一个位次,所以这里既不走账号池也不额外烧请求(上游 /v2/chat/queue/status 回的 message 是 i18n key 而不是人话,插件因此从不转述它,只用自己的措辞)。排队不会自动重发,也不会被自动重试——插件没有排队轮询循环(它需要排队答案在 OpenAI 兼容通道上的真实信封,没测到就不写),并且明确告诉宿主不要重试(x-should-retry: false):一次排队在几千位之后,重试十几次也轮不到,只会把真实请求白白花掉。位次报出来,由你决定是重发还是换模型。
  • 模型可用性测试(含冷却恢复时间) —— 限量模型的冷却时间过去只能靠反复试错去猜。现在每个模型行有一个「测试」按钮,直接告诉你它此刻能否使用、不能用时何时恢复。它发的是一条真实体积的请求(约 25k 输入 token),而不是「发个 ping 看通不通」——因为上游的限流按请求体积触发(实测约 20k 能过、约 30k 被拒),小请求看着正常、长对话里却可能每次都被拒,那正是「测试通过、实际被限」的来源。恢复时间的来源:上游的 429 不带 Retry-After 响应头(也没有 X-RateLimit-*),但它把重置时间写在响应体里——{"code":6004,"msg":"…将在 2026-09-30 02:30:30 UTC+8 重置…"}。插件从响应体解析该时间(并按其声明的 UTC 偏移换算,不受你本机时区影响),因此卡片能直接显示「✗ 被限流 · 2026/9/30 02:30:30 之后可再用」。三处来源都没说时,插件不编造倒计时,如实显示「上游未给出何时恢复」。限流不会去借月周期包的刷新时间(那会让人为几秒的限流去等几周)。成功即无冷却:这个网关在 200 响应上也会带 Retry-After,值是账号月度积分包的刷新时刻(实测 2026-11-01 00:00 +08:00),它不是「你在此之前不能用」——所以成功的行只显示「✓ 可用」,绝不附「之后可再用」;旧版本已经写进文件的那类记录会在读取时自动治愈。探测在后台发起,凭据不会传到页面;结果不写入设置,也不改变你已保存的勾选。

两点需要知道:① 它真的花钱——实测一次报 credit 0.02(被限流时直接 429,几乎不花)。因此只能按单个模型手工点,没有「测试已勾选」这类批量按钮:一次覆盖整个列表就是一次不可控支出(服务端也据此只接受单个模型,多模型请求会被拒)。② 上游虽宣称 1M 上下文,但节流线远低于此;插件按模型的原生窗口向 DSH 宣称(3.5.0 起不再默认压到 200K),所以 DSH 很晚才压缩对话——长会话撞限流是这条机制的直接结果。想让 DSH 更早压缩,就在模型行上手动设一个「DSH 上下文预算」上限(可选 200K / 500K / 模型原生窗口)。

  • 生图工具 `workbuddy_draw_image`(国内版,默认关闭) —— 把 WorkBuddy 的文生图能力交给 agent:你在对话里说「帮我画一只在窗台上晒太阳的猫」,模型自己就会调用这个工具,画完把图贴回来。默认不开启,因为这是本插件里唯一一项「由模型自己决定花钱」的能力:约 5.71 积分一张,而宿主没有「调用前先确认」的机制。想用就去卡片「账号」栏勾上「允许模型自主生图」;不勾的时候这个工具根本不会注册,模型看不到它,也就不会发起注定失败的调用。它只在国内版可用——国际版的模型目录里一条生图模型都没有(不是没做,是上游没有)。端点与请求体照抄官方 CLI 的 ImageService(POST /v2/images/generations,hunyuan-image-alpha 模型),产物是 24 小时有效的 COS 签名 URL,插件会提醒你及时下载。它真的花钱:实测约 5.71 积分/张,所以工具描述里直接写了单价,失败后有 30 秒冷却(但参数写错、没登录、模型不存在这三类一分钱没花,不进冷却),也不提供「一次画四张」这类不可控支出的参数。工具定义是手写的 raw JSON Schema,没有引入任何新依赖。取证、协议与联调记录见 docs/IMAGE_GEN.md。
  • 本机账号切换 —— 自动发现 WorkBuddy 桌面端留下的多个登录凭据,可按区域切换账号;Token 不写入 DSH 设置。
  • 对话输入框积分读数(国内版 / 国际版自动切换) —— 对话输入框左侧工具栏常驻一行可点击的读数(如 WB CN · 1,072),按本会话当前选中的模型自动切换区域:选中 WB CN 的模型就显示国内版余额,选中 WB AI 的模型就显示国际版余额;用其它 provider 的模型时该读数完全不出现。每 5 分钟自动刷新,也可点开手动刷新。点击读数弹出面板:按账号列出的余额表格,点某一行即可切换到该账号(与卡片上的账号选择是同一个设置);当前不能用的账号会在行内标出原因(如「被限流」「积分耗尽」),标注口径与账号池表格一致。面板里的余额只在打开时才去查,因此不会让 5 分钟的后台刷新按账号数翻倍。要关掉某一边,就在卡片对应区域的 tab 下取消勾选「在输入框工具栏显示积分读数」;该区域的供应商被关闭时,读数不显示、开关也置灰。同一个开关下面还有「显示位置」这一行:默认在输入框工具栏(与模型选择同一行),改成「输入框下方」后读数落到轮次/步数/token 统计那一行——装了别的插件把工具栏座位占掉时用得上。
  • 账号池:多账号批量操作与自动换号 —— 把要用的账号勾进池子,即可签到勾选的账号、测试勾选的账号 × 你指定的模型(默认挑该区域倍率为 0 的免费模型);开启启用账号池即同时打开自动换号——某个请求在上游失败时,插件会依次用池内其他可用账号重试,直到有一个接手、或全部不可用为止,然后才把错误报给你。成员默认一个都不含、必须显式勾选(「本机有登录」不等于「要拿它去签到和花积分」);开启后由池子的排序决定每一次请求用谁(可用优先,其次快过期先花、积分高、凭据更新的);失败时依次用池内其他可用账号重试。排序从不写回你保存的选择——关掉开关,你选定的账号立刻完全生效。只有请求本身不合法(HTTP 400)不换号:同一个请求体换谁都是 400,重试只会白等。这个开关只管「插件要不要自己决定」:关掉它,插件不再替你选账号、也不再替你换号,但签到与测试仍然可用(它们是手动动作)。测试走的是与模型行同一套真实体积探测,因此有真实成本;不提供自动签到——签到会真实领取奖励,只在你点按钮时执行。详见账号池。
  • 企业版 / 教育版账号:签到与「领取」不适用 —— 带 enterpriseId 的席位(企业版或教育版)在池子里不显示签到列、不显示福利活动,也不会发出领取请求(5.8.0,issue #129):上游对这类成员的个人权益写接口一律拒绝(实测 10001),而它的读接口照样把活动列出来、标着「未领」——所以「读到了」绝不能当成「可以领」。积分也在另一本账上(企业用量 = 上限 − 已用),个人积分表对这种账号返回空列表。这个判断贯穿池卡片、输入框胶囊与 Web 状态页,并且在账号进入池子的那一刻就带上标记,而不是等用户点了按钮才发现不支持。
  • 可选:让上游用量页把这笔记成官方客户端发的那笔(5.8.0,issue #132,默认关闭) —— 开 usageIdentity 后 chat 请求带上 X-IDE-Name: WorkBuddy(连同 X-IDE-Type: CLI 与版本号;版本号取自现有的产品 UA,不留第二个硬编码数字),用量页的「使用端」由此不再空白;会话身份三件套同时改成官方客户端那种 32 位十六进制 UUIDv7 并补上 X-Request-ID——网关把这个 id 回显成 SSE 的 id 并写进用量页,此前带连字符的 v4 与官方记录摆在同一页上像两种客户端;每请求现生成、互不相同这条没有放松。`User-Agent` 在两种取值下都一字未动——账单与限流认的是 CLI 通道;生图路径也不跟着这个开关变,它保持自己 2026-10-21 实测过的形状。默认关的理由:形状本身不修任何东西(11128 的三道门读的是消息开头、会话 id 有没有、首条是不是 system,头改不动其中任何一条),而实测证据来自报告者的账号与旧版本、本插件未在真机复现——一次纯显示对齐不该让所有账号先承担风险。另有可选的 keepPromptLast(默认关闭):用量页的「请求」列取自 body 里最后一条 user 消息,而宿主把时间采样、运行时上下文、` 追加在你真正输入的那句**之后**,那一列于是常年记的是注入块;开启后把这些注入块**搬到真实输入之前**(只变顺序;另外会剥掉**被记录那一条**真实输入前面的 mNNNNN` 标记,否则那一列仍然以标记开头而等于没修,更早的消息一律保留自己的标记,因为模型靠它们引用历史)。默认关的理由是它会改变上游会数到的消息顺序、并删掉真实输入的几个前导字符;搬移只匹配宿主自己的注入前缀,而普通对话在没有注入尾块时逐字节不变(连标记也不剥)——这两点各有专门测试钉住。
  • 可选:把每个新会话的标题请求改到隐藏轻量模型(issue #133,默认关闭) —— DSH 宿主每个新会话会发一次标题生成请求,走的是与对话相同的 provider,于是这笔按主付费模型的费率计费。开启 sessionTitle.enabled 后,插件把能按宿主标题模板(Generate the session title from this JSON array of human messages: 开头、整条 body 只有这一条 user 消息)识别出的标题请求改写到 sessionTitle.model(默认 wb-lite)。报告者实测该模型对这类请求 0 积分,但注意三点:这仍是真实上游请求,请求数照算;证据是报告者账号级的,本插件未复现;识别按 fail-closed 设计——宿主若改模板,改写静默停止、回到原计费,不会误伤普通对话。若上游拒绝该模型,只是该会话标题生成失败一次,不影响主对话。另外,sessionTitle 与本条及上一条的 usageIdentity / keepPromptLast / extraInjectedPrefixes 一样,都是插件挂载时读一次配置:改完 `cordis.patch.yml` 后需要重载插件或重启 DSH 才生效,热改不生效。
  • 福利活动:待领取标记 + 单账号「领取」按钮 —— 卡片会逐账号标出上游活动(补偿 / 赠礼)里还没领的那一份,数据来自上游自己的两个状态接口,标记阶段只读、两个写接口一次都不调用。显示规则完全由上游状态决定:active && !claimed 才显示,claimed 整行消失,活动过期(active 翻 false)自动消失——不用本地时钟去比 `end_time`,本机时间偏了也不会与服务端打架;end_time 只用于 3 天内临期高亮。active 单独看不行:已领取的账号 active 仍为 true,只看它会让领过的账号永远挂着待领取。读取复用签到的管线(每账号一次、30 分钟缓存、账号池关闭时不发请求),任一接口失败按「未读」处理,不会把一次失败缓存成「全都没福利」。行尾是一个实心琥珀色的胶囊按钮「领取」,一次只领一个账号的一个活动:没有「全部领取」,因为一次点击花掉的是一份不可撤销的真实权益,写路由也强制要求 `accountId`,请求本身无法批量领取。领取前重新读取状态再判定,已领取或已过期一律 409 且不调用上游;领取后用上游的新读数作答并刷新卡片,不做乐观更新——即使上游静默失败也不会留下「已领取」的假象,成功后同时清掉该账号的福利缓存,按钮不会顶着旧状态赖着不走。
  • 凭据失效时给出可执行的建议 —— 上游拒绝当前账号的 token 时,插件会实际探测本机其他账号:若有账号仍然可用,就直接告诉你「切换到它即可」(并附一键切换),而不是笼统地让你重新登录——被钉在一个作废的旧备份上时,重新登录并不能解决问题。只有本机确实没有别的账号可切换时,才会提示重新登录。
  • 扫码添加账号、批量导入导出、逐账号测试/删除 —— 不必先装 WorkBuddy 桌面 App:卡片里的「扫码登录」按钮(国际版为「网页登录」——那边是浏览器登录页,没有二维码;这一行就在「更多设置」上方,点一下立即生效,不需要保存)点开即弹出对话框,并在浏览器里自动打开授权页;被浏览器拦截时对话框内提供链接、复制与「重新打开页面」兜底。所有凭据统一落到插件自己的凭据库($DSH_HOME/workbuddy-vault//.json,一账号一文件),桌面 App 的认证文件始终只读、永不被改动。账号表格每一行右侧有「测试」与「删除」:测试与批量测试走同一套探测,结果可比;删除只删插件自己存的那份,并把这个账号暂时隐藏:删掉后立刻消失,也不会被自动轮询带回来;点「重新检测账号」即可找回(重新登录或导入同一账号也会清除隐藏记录)。桌面 App 的登录始终不动——删除是「本插件不再列出它」,不是退出 App 登录。「导出 / 导入账号」可把凭据搬到另一台机器:多选导出 JSON → 选文件后先看脱敏预览(只显示字段与是否有 token,明文不进预览)→ 勾选导入。导入按账号去重覆盖,只有确认属于该区域的记录才入库,认不出的域名会被跳过并如实计入「已跳过」。详见账号池。
  • 积分信息在账号池里 —— 成员表逐账号显示剩余积分与最近到期时间,排序的第二键就是积分(谁多谁优先)。查询是只读的、不消耗积分。
  • 凭据路径多候选 —— macOS / Windows / Linux 逐一探测默认位置,也支持环境变量与卡片内直接指定。未登录时卡片会列出「探测过哪些路径、各自为什么没成」:五档原因(不存在 / 读不了 / 没有可用 token / 字段已加密且拿不到密钥 / 属于另一个地区)分开呈现,因此「已经登录了、但桌面 App 不在场」这种最容易误判的情况会明说需要 App 在场,而不是叫你重新登录一次。属于另一个地区那一档尤其不同:登录信息是真实可用的,只是挂在另一个标签页下,卡片会直接给出结论「切换标签页即可,不用重新登录」。列表只讲一次——提示段落说结论,折叠列表给路径细节,两处不重复同一件事。
  • 安全 loopback shim —— 每区域一个随机端口 + 进程内随机 secret,真实 WorkBuddy token 不交给 pi-ai。
  • 命令行诊断 —— status / doctor / logout,按区域报告登录与积分,无需浏览器即可确认宿主状态。
  • CLI 路线(可选,默认关闭) —— 伪装成 CodeBuddy 客户端过渠道校验是一条军备竞赛的路,而且已经出现账号级限制(issue #98 的 code 11140)。另有一条路不做这层伪装:让本机已登录的 CodeBuddy CLI 自己跑任务,由它去读文件、跑 git、改代码。打开 cliBridge.enabled 后插件多出 codebuddy_ask 工具和 /codebuddy 指令,跑完把结论回注给会话模型。关闭时插件行为与之前完全一致。它绕开的是客户端指纹层,不是账号层:用的仍是本插件 vault 里的同一批账号,所以不该把它当成「封号之后的免死金牌」。详见 CLI 路线。

工作原理

DSH PiAiAdapter(每个 provider 一套)
  -> 安全 loopback shim(每区域一个随机端口 + 进程内随机 secret)
  -> WorkBuddyUpstreamClient
  -> 国内版 https://copilot.tencent.com/v2/chat/completions
  -> 国际版 https://www.workbuddy.ai/v2/chat/completions
  -> WorkBuddy SSE
  -> DSH 本地执行工具并回传结果

国内版与国际版各持一套完整的运行时栈——凭据 store、模型 catalog、回环 shim、adapter——按凭据域名(workbuddy.ai / codebuddy.ai → 国际版,其余 → 国内版)隔离可见账号,所以两个区域的账号可以同时在线、同时被不同会话使用。国际版有两个品牌域名:桌面端登录在 workbuddy.ai,CodeBuddy CLI 登录在 codebuddy.ai,两者都是国际版。

模型目录按区域取自各自的正确来源,两者都是 App 自己读的那份 /v3/config,且都以 App 自己的客户端身份去读(WorkBuddy/ WorkBuddy/ CLI/,即桌面端 buildVlmGatewayHeaders 拼出的那个头)。这一步是必需的:/v3/config 按客户端通道下发不同的产品配置,CLI 通道的答复是 App 通道的严格子集,既不发布 `modelPromotions` 也不发布 `modelTiers`——用 CLI 身份读,国内版的活动徽标就会一个都画不出来。聊天与探针仍以 CLI 身份发送(会话身份三元组绑在聊天通道上),只有目录读取换成 App 身份。国内版若 /v3/config 取不到,会退回旧的 /v2/enterprises/personal/models 并记一条日志,所以只在降级时使用。积分概览走 https://www.codebuddy.cn/v2/billing/meter/get-user-resource(国际版账号自动路由到其凭据所属的国际网关,workbuddy.ai 或 codebuddy.ai——两个品牌域名的凭据互不通用),均为只读接口。

凭据读取自 WorkBuddy 桌面 App 自身的 auth 文件(只读);刷新得到的 token 按区域存放在 $DSH_HOME/.workbuddy-auth.cn.json 与 $DSH_HOME/.workbuddy-auth.global.json(双账号同时在线互不覆盖;旧的单文件 .workbuddy-auth.json 作为迁移来源保留读取),桌面端文件永不被写入。

加密凭据字段:新版桌面端(Windows 先行)把 auth 文件里的 accessToken / refreshToken 从纯字符串改为 {"$wbEncrypted":1,"envelope":"…"} 的 AES-256-GCM 信封,账号昵称 `nickname` 同样被加密(不解密就会退回成一串 uin 数字)。信封用的是 App 自身构建期内置的字段密钥,并非用户密钥,因此插件不内置任何密钥副本,而是在需要时用 ELECTRON_RUN_AS_NODE 调用已安装 App 的原生绑定(electron_browser_workbuddy_storage.loggerGet())取回同一份载荷并就地派生密钥——也就是「向写下这个文件的那个构建本身询问」。密钥只在本进程内存中缓存,不落盘、不进日志;纯字符串文件(macOS 与旧版 Windows)走原路径,一次子进程都不会启动。其中的 account.phoneNumber 虽同样加密,插件不读取也不显示。若 App 安装在非常规位置,用 WORKBUDDY_APP_EXECUTABLE 指定其可执行文件。

账号池

一台机器上往往登录过多个 WorkBuddy 账号。账号池把这件事收成一次点击:把要用的账号勾进池子,然后「签到勾选的账号」或「测试勾选的账号」;再打开启用账号池,插件就会在请求失败时自动换号接着跑。

每个区域(国内版 / 国际版)各有独立的一池:成员与目标模型互不影响——两边的账号本来就属于不同的上游栈。签到只有国内版有:海外区域不显示签到按钮,也没有那一列。

自动换号(开启账号池即生效)

  • 谁来服务由排序决定:开启账号池后,每一次请求都发给池子里排序最靠前的可用账号——所以「当前使用」显示的是排序的结果,不一定是你手动选的那个。排序规则见下一节。
  • 失败时继续往下试:某个请求在上游失败时,插件会依次用池内其他可用账号重试,直到有一个接手、或全部不可用为止;全部失败才把错误报出来(并在错误里注明尝试过几个账号)。
  • 哪些失败会换号:429 限流、402 无积分、401 凭据失效、502 网关/网络——这些都是某个账号的状态,换一个账号很可能就过了。
  • 哪些不会:请求本身不合法(HTTP 400)。同一个请求体换哪个账号都是同样的 400,重试只是让用户多等几轮才看到同一个错误。流中途断开也不会重试——字节已经发出去了,重放会让客户端看到两段拼接的回答。
  • 已知不可用的账号会被跳过:正在限流冷却期内、或凭据已被上游拒绝的成员不会参与(这也是「一键测试」的意义:测试结果直接决定谁能被选中)。从没测试过的账号按「默认可用」参与——刚发现的登录不该因为「还没测」就被排除。
  • 手动选择不会被改写:池子通过运行时覆盖决定「谁服务」,从不写回你保存的选择;关闭账号池,你选定的账号立刻完全恢复生效。这也是为什么卡片要显示「当前使用」——它可能不是你在上面选的那个,而你应该看得出来。
  • 失败会被记下来:某个请求撞上失败时,插件把这次失败写成该账号的测量(含上游报文里给出的重置时间),所以下一个请求直接从可用账号开始,不会每次都先撞一遍同一个限流账号。只有需要你手动「一键测试」时才不写测量这件事,从此不成立了。HTTP 400 例外——那是请求不合法,与账号无关,记下来会白白闲置一个好账号;未勾选进池的账号也不记(勾选才是「可以花它的积分」的同意)。
  • 每次换号都会在宿主日志里留一行 warn,写明因何失败、换到了下一个账号。

这个开关到底控制什么

它只控制「插件要不要自己决定」,不控制「你能不能操作」。

· 账号池 开启 · 账号池 关闭 谁服务 · 由排序自动选出(可用优先 → 快过期先花 → 积分高 → 凭据新) · 你在账号池里手动选「使用哪个账号」 请求失败 · 自动换下一个可用账号重试 · 不换号,如实报错 一键签到 / 一键测试 · 可用 · 仍然可用 目标测试模型 · 可改 · 仍然可改

最后两行是刻意的:签到与测试是手动动作,它们作用于你勾选的成员,与「插件是否自动路由」是两件事。之前把按钮也挂在开关上,导致关掉账号池就再也没法签到或测试——而「关闭」本该是「我手动来」,不是「什么都不许做」。

手动选择账号在哪里:账号池区块里。关闭账号池时,该区块会显示一行「使用哪个账号」的下拉,选定即生效(写回本区域的账号槽,与凭据失效时的「切换到可用账号」走同一条已验证的写入路径);开启账号池时这一行不显示——那时由排序决定,一个改了不生效的下拉比没有更糟。账号的发现(「重新检测账号」)、勾选、签到与测试全部集中在这个区块里,所以账号管理只有一处。

唯一的硬约束:池里一个成员都没勾选时,批量动作一律拒绝。空的池绝不退化成「那就对全部账号执行」——那是唯一会花掉你没授权过的积分的解读。

排序规则(开启账号池后决定谁服务)

  1. 可用优先 —— 被测量判定为不可用(限流未到恢复时间、凭据被拒、连通性失败等)的排到最后,不参与选择;
  2. 快过期的积分先花 —— 3 天内到期的积分用不掉就作废,所以有临期积分的账号排在前面,先到期的先排;两个都在临期时,临期积分多的优先;
  3. 积分高者优先 —— 在没有临期积分的账号里,选积分最多的。积分只在有新鲜读数时参与(卡片轮询、批量测试或保存时读到的那份,10 分钟内有效):请求路径不会为了排序去打上游,否则一页对话就会变成 N 个额外请求;
  4. 凭据更晚过期的优先;
  5. 最后按账号 id 收尾,保证顺序是全序、可复现,不会因为账号表顺序变化而抖动。
第 2 条为什么排到第 3 条前面(issue #70):这两条此前是严格字典序——先比积分总量,只有总量完全相等才比到期时间。而总量几乎从不恰好相等,于是「快过期优先」实际上从未生效过:一条「不花就作废」的规则被另一条完全遮蔽,等于死代码。真实病例里,某账号有 196.4 分当天作废,只因为比另两个账号少约 1,200 分就永远排在最后,那 196 分必然烂掉。
代价也要说清:这条规则会让临期 1 分的账号排到余额 5 万的账号前面。这不是缺陷——积分是通用的,不花那 1 分它确定会损失;而余额 5 万的账号下一轮还在。唯一的真实代价是临期账号可能不够跑完一个请求,这时由既有的换号逻辑接手。

两个批量动作

  • 签到勾选的账号 —— 逐个账号签到。签到前先查当天状态,已签到的直接标记为「已签到」而不发写入请求(幂等,不重复领取);任何单个账号失败只标记该行,不中断整批。
  • 测试勾选的账号 —— 对每个成员发一次真实体积的探测请求(约 25k 输入 token;与模型行的「测试」是同一套 probeModel,因而同样继承两个实测结论:限流按请求体积触发、恢复时间写在响应体里而不是响应头),得出该账号此刻能否用这个模型。

测试目标是哪个模型:默认是该区域目录里倍率为 0 的免费模型(实测国内版当前只有 hy3 一个;不写死名字,因为上游会改倍率——deepseek-v4.1-flash 就已从 0 变成 0.11),也可以在下拉里指定一个具体模型。指定的模型若已离开目录,手动动作会明确拒绝(target-model-stale),而不是偷偷换一个——你点的是「测这个模型」,换掉就是答非所问。

成本与边界:测试是真实请求,指向免费模型时(倍率 0)几乎不花钱,但它不是零成本的空转;签到则会真实领取当日奖励。两个动作都只在你点按钮时执行——插件不提供自动签到,卡片上也不放一个永远点不动的开关。

池成员是显式勾选,默认一个都不含

「本机有登录」不等于「要拿它去签到和花积分」。成员默认空,必须逐个勾选(或点「全选」);空池不会退化成「全部账号」——没勾选时两个批量动作直接拒绝,而不是替你把所有账号跑一遍。

勾选是一次草稿编辑:点「保存」才落盘,未保存时保存按钮带 dirty 指示;切走或关掉卡片,草稿仍在。

手动选择:卡片上没有账号下拉了

账号一律在账号池里管:成员勾选就是现在的账号选择面,「重新检测账号」也在那一块。原先那个「账号选择」下拉在有账号池之后是自相矛盾的——池开启时由排序决定谁服务,它改什么都不生效,却看起来像权威;顶部状态行还可能显示成 A、下拉显示成 B。

所以区域状态行也跟着改:池开启时不再报出某一个账号名(多个账号同时登录是常态,用单数说「已登录:A」本身就是错的),只陈述本区域可用、由池决定用谁;池关闭时才报出那个账号——那时它确实是唯一答案。状态行旁边的圆点与 token 过期时间讲的是「这个区域能不能用」,和「谁在计费」是两件事,后者由账号池区块负责显示。

账号为什么出池,以及什么时候回来

探测结果 · 对轮换的意义 可用 · 可入选 被限流 · 暂时出池,按上游给出的重置时间自动回池(上游没说时间就按 30 分钟兜底,不编造倒计时) 积分耗尽 · 出池,按月周期包的刷新时间回池 连不上上游 / 失败 · 暂时出池,30 分钟后自动回池——这类结果说的往往不是账号,而是一次网络抖动或上游 5xx 凭据被拒 / 模型不存在 · 出池,只能靠手动「测试」回池:一个需要重新登录,一个是本区域不再提供该模型,等待都修不好 账号被上游限制使用 · 停权,等待修不好(5.8.0)。上游的话是「账号存在异常行为,已被限制使用」——它点名的是这个账号,所以自动换号会绕开它去用下一个。这与「渠道校验失败」正好相反:那条说的是这个请求,换号只会白烧一次额度,因此不换号。回池的实际出路只有手动「测试」:账号 id 是 uin/uid 的哈希,重新登录后仍是同一个 id,插件也不会因为重登清掉这条测量(「重新授权就回池」不成立);上游解除限制后测一次通过,它才会重新成为候选。插件不给它编一个倒计时,但停权只作用于轮换候选资格——全池没有可用账号时,请求仍会落到你保存的那个账号上

关键一条:「被限流」「连不上上游」都不等于不可用。它们恰恰是最值得等的那两类状态——当成永久失效剔除,正好丢掉池子里本来最能用的账号,而且当池内每个成员都被这样剔除时,池子就没有任何候选,自动换号会无处可去,你只会看到上游原始报错。

为什么只有限流/欠费/瞬时故障会自己回池:这三类要么由上游写明了恢复时间,要么由插件按 30 分钟兜底;而「凭据被拒」「模型不存在」等待都无意义,只能由你手动重测或重新登录来刷新。插件不再有自动复测定时器(见下),所以后两类的唯一出路就是手动动作。

保存与草稿:为什么卡片上有两个「保存」

模型管理一个、账号池一个,这是刻意保留而非漏合并:两者的草稿域不同,合并后一次写入失败会连累另一边(模型目录写不进去,就连池偏好也存不了);一个共享的「放弃」还会在一次误点里同时丢掉两份草稿。两边都是验证写入——写不成功不丢弃草稿,否则用户的编辑就永久丢了。「保存中」那个转圈只覆盖这次已校验的写入(5.8.0,issue #131):写入一落地就解除忙碌、丢弃草稿,随后的用量刷新只是显示——刷新慢或失败都不会把已经保存成功的设置说成「没保存上」,只会另附一行「已保存,但面板未能重新读取。设置已写入;下次刷新后会显示。」(role="status",与「保存失败」的 role="alert" 是两个独立节点)。此前两者共用同一个 loading,于是一次慢刷新能让按钮永远转下去,而数据其实早就已经落盘。

池里还有一类状态不走草稿:探测结果、冷却时间、当前轮换到的账号。它们是观测事实,由插件自己高频写入。原因很实际——草稿是覆盖式的,若观测结果也进草稿,你手工点一次「保存」就可能把插件刚写进去的结果回退成几分钟前的旧值。

安装

⚠️ 版本要求:DSH 0.1.7-rc.1 及以上(当前窗口:`0.1.7-rc.1` ~ `0.2.x` 正式版)。 自 v2.1.0 起本插件只支持 DSH 0.1.7-rc.1 及以上的宿主(旧版宿主无法通过依赖解析安装本版);0.1.5 及更早的宿主请停留在 v2.0.15。自 v2.1.2 起上界放宽为 `<0.3.0-0`,0.3.0 线被明确拒绝。详见下方受支持的宿主范围。

受支持的宿主范围

DSH 的随包门禁(packages/boot/app-boot/src/plugin-compatibility.ts 的 evaluatePluginCompatibility)会逐条核对本插件声明的 dsh-* peer 范围;只要有一条不满足,整个组合包会被跳过(进入 skippedBundles,仅打印到 stderr)——provider 不注册、卡片不出现、模型列表清空,而页面上没有任何报错。

宿主版本 · 判定 0.1.7-rc.1 ~ 0.1.7-rc.2 · ✅ 支持 0.2.x 正式版(0.2.0、0.2.1 …) · ✅ 支持(2.1.2 起实测) 0.2.x 预发布(0.2.0-rc.1、0.2.1-rc.1 …) · ⚠️ 不落在声明范围内,见下方说明 0.3.0-0 及以上 · ❌ 明确拒绝(该线尚未验证) 0.1.6 及更早 · ❌ 明确拒绝(0.1.5 线请停留在 v2.0.15)

关于 `0.2.x` 预发布(v4.7.0 更正):此前本表把 0.2.0-rc.1 写作「✅ 支持」,那是错的。
npm semver 规定:预发布版本只有当一个比较器在同一条 `major.minor.patch` 元组上带预发布标签时才被放行。
现声明 >=0.1.7-rc.1 <0.3.0-0 只对 0.1.7-x 开口,<0.3.0-0 又排除了 0.3.0 元组,因此
`0.2.1-rc.1` 这类版本两条比较器都不满足(satisfies('0.2.1-rc.1', …) === false)。
实际影响很小:宿主门禁只校验 @deepseek-ai/dsh* 命名空间,且 rc 宿主上本插件按 link: / 手工安装照常加载。
这不是靠改范围能解决的问题——任何静态范围都覆盖不了「任意 0.2.x 预发布」,已逐一实算否证五种写法
(见 CHANGELOG 4.7.0)。等宿主发 0.2.1 正式版即自然成立。
上界写作 <0.3.0-0 而非 <0.3.0:DSH 至今全部 tag 都是预发布(-alpha.N / -rc.N),只写 <0.3.0 会放行整条 0.3.0-alpha.1 / 0.3.0-rc.1 这条从未验证过的线,围栏形同虚设——这正是 2.1.2 修掉的缺陷形状。该口径由 tests/dsh-line.spec.ts 守卫(含预发布悬崖的用例)。
🪟 平台支持:Windows、macOS、Linux 三者并列支持。 Windows 是一等目标平台,不是「顺带能跑」:CI 在 windows-latest 上跑完整套件,凭据路径、进程心跳、App 可执行文件定位、设置写入四处都有 Windows 专属分支与测试,并且修复只对 Windows 生效的缺陷时,以 Windows 用户回报的现场与真机取证的加密向量为准(见 CHANGELOG 的 2.0.6 / 2.0.11 / 2.0.12 各节)。见下方平台支持一节了解各平台的具体差异与已知限制。

前置:已安装并登录 WorkBuddy 桌面 App(插件复用 App 的登录状态)。

推荐使用 DSH 插件命令安装 npm 已发布版本:

dsh plugin --profile desktop add dsh-connect-workbuddy

或直接通过 npm 安装:

npm install dsh-connect-workbuddy

安装、更新或卸载 bundle 后,需要重启对应的 DSH 进程。

⚠️ 刚发布的版本请写「精确版本号」,不要依赖浮动范围。 pnpm 有新版本等待期(minimumReleaseAge,默认 24 小时),所以一个版本刚发布不久时,浮动范围会被静默压回上一个版本:声明 ^4.4.2 时它仍解析到 4.4.2,而写精确的 4.4.3 则立即拿到 4.4.3。由于两种写法都「安装成功」,表现是「升级命令跑完了、版本却没变」,很容易被误判成发布失败。已实测(隔离目录、pnpm 12.3.4、--lockfile-only,未触碰用户环境):
| 目标写法 | 解析到的版本 |
| --- | --- |
| 浮动 ^4.4.2 | 4.4.2 ⚠️ |
| 精确 4.4.3 | 4.4.3 |
| 浮动 + --config.minimum-release-age=0 | 4.4.3 |
想立刻用上新版,请写精确版本号:dsh plugin --profile desktop add dsh-connect-workbuddy@4.4.4。过了等待期之后,浮动范围会自然跟上。

插件在 Web、Desktop、TUI 三种界面下均可运行,按你使用的 profile 选择对应命令:

dsh plugin --profile web add dsh-connect-workbuddy     # Web
dsh plugin --profile dsh-tui add dsh-connect-workbuddy # TUI

平台支持

三个平台都是受支持目标;下列差异是实现事实,不是「尚未支持」的委婉说法。要改这一节,请同时改 docs/WINDOWS.md——那份文件是平台相关约束的登记处。