Acoder416/dsh-plugin-workbuddy-gateway ↗★ 0

dsh-plugin-workbuddy-gateway

将WorkBuddy订阅额度接入为模型服务商 适合拥有WorkBuddy订阅账号,并希望将其额度作为DSH模型服务商使用的用户。

套件
dsh-plugin-workbuddy-gateway
相容性
待驗證
版本
0.1.1
授權
MIT
最近更新
2026年9月15日

安裝

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:Acoder416/dsh-plugin-workbuddy-gateway

dsh-plugin-workbuddy-gateway

把 WorkBuddy(www.workbuddy.ai 国际版 / codebuddy.cn 国内版)的订阅额度 接进 DSH,并且在设置页里管理它。

中文 | English


先读这一段:前置条件与适用范围

这不是官方集成。 它依赖的网关走的是 WorkBuddy 订阅端的内部接口,不是官方 API。 使用前请自行确认这符合你所在地区的条款与法律。作者与 WorkBuddy / 腾讯无任何关系。

需要说明
DSH 带 web profile本插件针对 @deepseek-ai/dsh-* 0.1.5-rc.2 开发与验证
Node ≥ 20.19宿主半只用 Node 内置模块
Python ≥ 3.9网关是 Python,仅用标准库(本机在 3.13 上实测)
WorkBuddy 账号国际版或国内版订阅;浏览器 OAuth 授权,或从桌面端已存的凭证导入

平台支持

WindowsmacOSLinux
网关启动与代理✅ 已实测✅ 代码已适配,未经实机验证✅ 代码已适配,未经实机验证
导入桌面端凭证✅❌ 不可用❌ 不可用
浏览器 OAuth 授权登录✅✅ 应该可用✅ 应该可用
进程托管(启停 / 就绪 / 日志)✅✅ 代码已适配✅ 代码已适配

macOS / Linux 上「导入桌面端凭证」用不了,因为网关只在 Windows 路径下找官方桌面端存的凭证 (%LOCALAPPDATA%\CodeBuddyExtension\Data\Public\auth\*.info)。这是内置上游代码的行为,不是本插件的。 在这些平台上改用设置页里的「浏览器授权登录」 —— 那条路不依赖桌面端。

解释器的名字:插件不硬编码 python。它按平台探测,第一个能跑起来的胜出:

平台依次尝试
Windowspython → python3
macOS / Linuxpython3 → python

所以 macOS 上不需要手动配 python3(Apple 从 macOS 12.3 起就不再提供 python 这个名字了)。 探测结果会显示在设置页的网关卡片里;也可以在该页的 pythonPath 字段里写死。

它依赖 DSH 的非公开内部接口:settings.installSection、settings.mutate、 webServer.register、credentials.*、客户端 settings.section 槽位。 DSH 改动这些会让插件失效 —— 见 CONTRIBUTING.md。


它解决什么

WorkBuddy 不提供官方 OpenAI 兼容 API:额度绑在订阅账号上,只能通过 OAuth 凭证访问内部接口。 所以要用它,本机必须常驻一个反代网关把订阅协议翻译成 OpenAI 协议。

在这之前,那意味着一个需要手动双击、手动记住端口、出问题只能翻黑窗口日志的 .bat 文件。 这个插件把那套东西变成 DSH 的一部分:网关进程由插件托管,账号、模型、日志和模型路由都在设置页里。

它做什么

  • 托管网关进程:启动、就绪检测、日志环形缓冲、退出、随 DSH 关闭而关闭
  • 管理账号:扫描本机桌面端凭证并导入、浏览器 OAuth 授权、移除
  • 维护 llm-pi-ai 模型路由:把网关实际提供的模型写进 DSH,模型选择器里就能选
  • 端口、Python 解释器、网关目录、随 DSH 启动、自动维护路由 —— 都在设置页里改

它不做什么

  • 不修改 DSH 核心,不碰你其他的模型提供方
  • 不提供任何账号或额度

界面

设置 → WorkBuddy:

区块内容
状态栏运行状态、接口地址、启动 / 停止 / 重启 / 刷新
网关接口地址、PID、已运行时长、启动脚本、账号目录、用量目录;端口、随 DSH 启动、自动维护路由
接口密钥生成 / 清除,存在 DSH 凭据库里(WORKBUDDY_API_KEY),不进 settings.yaml
账号当前区域可用数、区域切换(国际版 / 国内版)、扫描桌面端凭证、导入、移除、浏览器授权登录(轮询直到授权完成)
模型路由是否已写入、当前所有路由、写入 / 移除
模型网关提供的模型清单:上下文、最大输出、推理档位、是否支持图片
网关日志最近 120 行,错误行标红

安装

先澄清一件事:「bundle」不是插件市场专用

容易误会,但两者没关系。bundle 就是 DSH 组合 profile 的通用单位:一个包只要在自己的 package.json 里声明了

"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }

它就是一个 bundle,包名可以写进 profile 的 dsh.profile.bundles 数组。DSH 启动时按数组顺序 把每个 bundle 的 patch 应用到空的条目树上。

dshmarket(插件市场界面)自己也只是这个数组里的一项 —— 和本插件是同级,不是父子关系:

"bundles": [
  "@deepseek-ai/dsh-base",
  "@deepseek-ai/dsh-web-app",
  "dshmarket",                      // ← 市场,也是一个 bundle
  "dsh-plugin-workbuddy-gateway"    // ← 本插件,平级
]

解析方式是普通的 Node 包解析(先找 dsh 安装目录,再找 profile 目录),不经过任何注册表或市场。 另外 dsh plugin --profile web add 也不是市场命令,它只是把参数原样转发给 profile 目录下的 pnpm:

dsh plugin --profile tui add 
     install a plugin into the tui profile
[args...]  pnpm arguments, forwarded verbatim (add 
, remove 
, why 
, ...)

一句话:市场是安装的一种入口,bundle 是加载的机制。手工把包名写进 bundles 走的是同一个机制。

装法一:bundle 层(推荐)

包名进 dsh.profile.bundles,需要重启 dsh(bundle 层只在启动时解析一次):

// $DSH_HOME/profiles/web/package.json
"dependencies": {
  "dsh-plugin-workbuddy-gateway": "link:/absolute/path/to/dsh-plugin-workbuddy-gateway"
},
"dsh": { "profile": { "bundles": [ /* … */ "dsh-plugin-workbuddy-gateway" ] } }

也可以直接从 git 装:

cd $env:USERPROFILE\.dsh\profiles\web
pnpm add github:Acoder416/dsh-plugin-workbuddy-gateway
# 再把包名加进 package.json 的 dsh.profile.bundles,然后重启 dsh

装了 link 之后让 profile 解析依赖:

pnpm --dir $env:USERPROFILE\.dsh\profiles\web install

装法二:手动 patch 层(不写 bundles)

包名不进 bundles,改为在 profile 的 patch 文件里显式 insert:

# $DSH_HOME/profiles/web/cordis.patch.yml
- insert:
    - id: workbuddy-gateway
      name: dsh-plugin-workbuddy-gateway

custom profile 的 patchReload 默认是 live,所以这条 insert 不用重启就能挂载(代价是改动 client 半仍需刷新页面)。

⚠️ 两种装法只能选一个

不要同时用。 同一个 id 被插入两次会让 dsh 直接起不来:

duplicate loader entry id: workbuddy-gateway

所以:bundles 里有它,就不要再在 cordis.patch.yml 里写 insert;反之亦然。

运行时状态放在哪

网关是第三方 Python 程序,它的可变状态不写在插件目录里(插件是 link: 安装的,等于直接指向源码):

内容位置怎么定的
账号凭证$DSH_HOME/workbuddy/accounts/ACCOUNTS_DIR 环境变量
用量记录$DSH_HOME/workbuddy/usage/WB_PROXY_USAGE_DIR 环境变量
网关代码插件内 vendor/workbuddy-gateway/内置,自包含

设计上的几个要点

为什么端口默认 18088 而不是上游的 8788 本机 Windows 把 TCP 8703–9302 保留给了 Hyper-V/WSL 动态端口,绑在这个区间里直接 WinError 10013。18088 在所有保留区间之外。

为什么就绪靠读日志而不是等固定秒数 网关在开始服务时才会打印 listening : http://…。固定 sleep 会在慢启动时报"运行中"、 在快启动时白等;只有那一行是诚实的信号。

为什么写模型路由用 settings.mutate 而不是改 settings.yaml llm-pi-ai.providers 是 pi-ai 适配器的命名空间。整段写入会替换整个 providers 映射 —— 也就是删掉用户其他的提供方。改用路径级 mutate(providers. 一个 key), 既只动自己那一条,又走 DSH 自己的写入器和文件锁。 副产品:这个插件完全不解析 YAML。

为什么插件自带一个"晚退出"保护 重启会杀掉旧进程再起新进程,而旧进程的 exit 事件可能在新进程装好之后才到达。 如果退出处理无条件清空记账,就会把活着的子进程 PID 抹掉。所以退出处理绑定子进程身份。

为什么要等凭证服务 插件挂载的时刻,ctx.get('credentials') 可能还是空的 —— 凭证服务比插件晚激活。 最初的实现把它当成"没有密钥",于是网关不带鉴权就启动了(本机任意程序都能白用你的额度)。 现在有两道防护:有界等待(最多 10 秒),以及等不到时拒绝启动并在日志里说明原因。 不启动比偷偷裸奔好。

为什么 running 必须是 Gateway 上的真 getter 它一度只作为快照对象的一个字段存在,于是 gateway.running 是 undefined, 而 HTTP 读取路径正是用它决定"能不能调网关" —— 结果账号和模型永远读回空, 但状态徽章却显示"运行中"。测试当时只断言了 snapshot().running,所以没抓到。 现在有一条测试直接断言这个 getter 在四种状态下都返回布尔值。

为什么失败也要写进日志 "没启动"可以是三种完全不同的原因(自动启动关了、等不到凭证、启动失败), 只写进 dsh 控制台的话,设置页只会看到一个安静的红点。插件的日志环形缓冲因此也记录 这些决策,页面上直接能看到是哪一种。

为什么账号和模型都是分区域的,以及"可用数"为什么必须自己算 网关把账号池按区域隔离:/accounts 的列表按 realm 过滤,但它回的 usable 字段是 count_ready() —— 跨所有区域计数。直接把两个数摆在页面上就会出现 "可用 2 / 共 1" 这种自相矛盾的读数(本机两个区各一个账号时正好这样)。 现在两个数字都从同一个列表推导,且请求显式带上区域。这也是区域切换放在账号卡片里的 原因:它是这个列表的过滤条件,另一个区的账号在切换前是不可见的。

为什么有些模型没有推理档位菜单 模型选择器的菜单显示的是模型声明的档位,而网关对每个模型的声明并不一样:

网关给的信息含义插件写进路由
reasoning_efforts: [low, high, …](≥2 个)调用方可以选声明这些档位 → 有菜单
reasoning_efforts: [high](只有 1 个)只有一个档位,选了也没得选不声明 → 无菜单
reasoning_fixed_effort: medium档位写死,不允许调用方选不声明 → 无菜单
两个字段都没有网关没暴露思考控制不声明 → 无菜单

所以「不能切换推理程度」有三种完全不同的原因,而默认模型 deepseek-v4.1-flash 属于第二种:网关给它的是 reasoning_fixed_effort: high + always_reasoning: true, 它一直在以 high 推理,只是不允许你改。想要能切的档位就用 gpt-6-astra、 gpt-5.6-*、glm-5.3 这些。

单档位那一类(hy4-preview、hy4-preview-f)插件刻意不声明档位:菜单会严格只列出 声明的档位,写一个只有一个选项的档位等于给用户一个点了也没用的控件。

自测:node scripts/reasoning-report.mjs(对着运行中的网关打印每个模型的实际结论)。

测试

cd 
npm test          # 67 个离线测试:不需要 DSH、Python、网络
npm run preflight # 验证本机能 spawn Python 并流式读输出
node scripts/check-package.mjs   # 发布前自检

对真实 settings 服务的集成验证(需要 DSH 仓库;在临时文件上跑,不碰你的配置):

cd 
node --import tsx/esm /scripts/verify-provider-route.mjs

它验证单元测试验不到的那件事:DSH 自己的写入器接受对 llm-pi-ai 的路径级 mutate, 兄弟路由和无关段落都原样保留。

排障

# 插件自检(需要 dsh 正在运行)
Invoke-RestMethod http://127.0.0.1:3080/dsh-workbuddy-gateway/api/v1/health | ConvertTo-Json -Depth 6

# 网关本体
Invoke-RestMethod http://127.0.0.1:18088/health | ConvertTo-Json -Depth 4
现象原因 / 处理
设置页没有 WorkBuddyhost 半只在挂载时导入一次:重启 dsh。刚改过 client 半只需刷新页面。
启动失败、日志有 argparsegatewayDir 指错了目录,或内置 vendor/ 被删。设置页会显示实际用的脚本路径。
启动失败、日志有 WinError 10013端口落在保留区间,改端口(默认 18088 是安全的)。
模型路由写入报 "不是已注册的命名空间"pi-ai 适配器还没加载完。刷新页面重试即可,状态每次轮询都会重读。
模型选择器里没有模型先"写入模型路由",再刷新页面。路由是页面加载时下发的。

卸载

  1. 设置页里先「移除模型路由」
  2. 从 profile 的 dsh.profile.bundles 删掉包名,并 pnpm remove dsh-plugin-workbuddy-gateway
  3. 重启 dsh
  4. 可选:删掉 $DSH_HOME/workbuddy/ 与凭据库里的 WORKBUDDY_API_KEY

风险

网关走的是订阅端内部接口,不是官方 API。被风控检测存在封号风险,且大概率不符合 WorkBuddy 服务条款 —— 由使用者自行权衡。本插件只是把这套已知的社区方案托管起来, 不改变它的性质。

致谢

网关实现来自 ardeyouxipianyi/workbuddy2api-intl (MIT),已内置在 vendor/workbuddy-gateway/,许可证随附、未作修改。升级上游时替换该目录内容即可。 第三方代码的完整说明见 THIRD_PARTY_NOTICES.md。

插件机制参考了另外两个 DSH 社区插件(仅参考集成方式,未包含其代码): dsh-plugin-archived-sessions 的 settings.section 挂载方式, 以及 dsh-plugin-codex-monitor 的 host 路由 + client 页面形态与同源守卫约定。

许可证

本插件代码为 MIT,见 LICENSE。 内置网关是另一份独立的 MIT 代码,见 THIRD_PARTY_NOTICES.md。

参与贡献

见 CONTRIBUTING.md。摘要:

npm test                          # 67 个离线测试(不需要 DSH、Python、网络)
npm run preflight                 # 验证本机 spawn + 管道 stdio 可用
node scripts/check-package.mjs    # 发布前自检:文件清单、版本一致性、无硬编码绝对路径
npm run report:reasoning          # 对着运行中的网关打印每个模型的推理档位结论

集成脚本需要一个 DSH 检出(在 DSH 仓库里跑):

node --import tsx/esm \scripts\verify-settings-section.mjs
node --import tsx/esm \scripts\verify-provider-route.mjs