DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

HERO476 /

HERO476/dsh-doubao-work-connect

Verified

把本机已登录的豆包工作(DoubaoWork)客户端接入 DeepSeek Harness:通过客户端 DevTools 端点复用登录态调用 /chat/completion,并在输入框显示 5 小时 / 7 天额度。

★ 1 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHubProject homepage
READMESource: main@79c3be96

dsh-doubao-work-connect

把本机已经登录的豆包工作(DoubaoWork)客户端变成 DeepSeek Harness 里的一个模型 provider: 不用另外配账号、不用 API Key,直接借客户端自己的登录态调它自己的 /chat/completion。

装好并重启 DSH 后,模型选择器里会多出一个 「豆包工作」 分组,含三个模型:

模型 id 显示名 说明
doubao 豆包 2.1(快速) 不思考,最快
doubao-think 豆包 2.1(思考) 默认一档思考,可由 DSH 的 reasoningEffort 调整
doubao-expert 豆包 2.1(专家) 默认最高档思考,最强

思考档位:三个模型都会把 DSH 传来的 reasoning_effort 映射到豆包的 need_deep_think(off→0、minimal/low→1、medium→2、high/xhigh/max→3); 不传时按模型 id 取默认值(doubao→0、doubao-think→1、doubao-expert→3)。 模型 id 里的档位只是默认值,实际请求以 DSH 的选择为准。

工具调用:豆包的客户端协议没有 OpenAI 的 tools 字段,所以插件用提示词模拟—— 把工具目录注入系统指令,要求模型输出 <tool_call>{"name":…,"arguments":{…}}</tool_call>, 小门再把这段标记解析成标准 tool_calls(流式按 chunk 增量、跨 chunk 拼接) 并给出 finish_reason: "tool_calls"。这是尽力兼容,不是原生 function calling: 模型不一定每次都老实输出标记,复杂 schema 的遵循度不如真原生协议。

为什么它是「本地插件」

DSH 判断一个插件「存在」有三个彼此独立的机制,本地开发的插件三样都要做,缺一样就表现为 「我明明写了插件,DSH 里却没有」:

步骤 做什么 缺了会怎样
① 插件目录里的 node_modules 软链 让插件能 import 宿主包(@deepseek-ai/dsh-llm 等) 启动时报 ERR_MODULE_NOT_FOUND
② profile 的 node_modules junction 让宿主能 resolve 到这个包 包根本找不到
③ dsh.profile.bundles 登记 启动时才会组装它的 cordis.patch.yml 能 resolve,但 apply() 永远不被调用
④ dependencies 里写 link: 只影响市场「已安装」列表是否显示 插件能跑,但 UI 里查无此插件

install.mjs 就是把 ①–④ 一次做完的脚本。手抄任何一步都会重现「装不上」的现象。

安装

node install.mjs

脚本做的事:建插件内 4 个宿主包软链 → 在 <profile>/node_modules 建 junction → 把包名加进 dsh.profile.bundles → 在 dependencies 写 link:<本目录> → 备份一份 package.json.<插件名>.bak。

装到哪个 profile:默认挑「装了 dsh-connect-workbuddy/dsh-connect-trae 的那个, 否则名字以 desktop 结尾的,否则第一个」。要装到别的 profile(例如网页端用的 web) 必须显式指定:

node install.mjs --profile web

0.1.1 修正:此前 resolveProfile() 只按目录名猜,永远选 desktop,导致 直接跑 node install.mjs 装不进网页端 profile——网页端因此表现为插件完全不存在 (/plugins/dsh-doubao-work-connect/status 直接 404)。现在支持 --profile。

装完重启 DSH(Electron 应用,不是这个 CLI)。

本机实测:profile 的 package.json 变更后宿主会热加载——不重启,22:25:08 时 127.0.0.1:30812 小门就自己起来了(宿主进程 21:16:28 启动,未重启)。 但热加载不是契约,重启一次最稳。

回滚:

node install.mjs --remove

前置条件

  • Windows 上的豆包工作桌面客户端(DoubaoWork.exe),且已登录。

  • 客户端需要带调试口启动:DoubaoWork.exe --remote-debugging-port=30813。 Chromium 只在启动时接受这个参数,所以插件发现「客户端在跑但调试口不通」时不会替你重启客户端 (autoRestartDoubao 默认 false,见下),而是报错并在错误信息里提示你:自己关掉豆包工作, 再用 D:\Program Files\DoubaoWork\app\DoubaoWork.exe --remote-debugging-port=30813 启动它 (注意根目录的 DoubaoWork.exe 只是启动器,给它传参不会传给主程序)。

    破坏性默认值变更(0.1.3):autoRestartDoubao 的默认值由 true 改为 false。 旧默认值意味着插件会主动 taskkill /F 掉你正在用的豆包客户端,打断你手上的对话; 现在默认不动你的进程。要恢复旧行为需显式设 autoRestartDoubao: true, 那会强杀你正在使用的客户端。

  • 网络能直连 www.doubao.com。

工作原理

  1. 读凭据:CDP 连到 127.0.0.1:30813,用 Storage.getCookies 拿登录 Cookie, 在页面里求值取 localStorage 的 device_id / web_id / s_v_web_id。 磁盘上的 Cookie 库被客户端独占锁定,这条路走不通。
  2. 借签名:在客户端自己的页面里调 window.bdms.frontierSign({ url }), 拿回 X-Bogus 值,作为 query 参数拼到 /chat/completion?_signature… 上。 不重写反爬算法,客户端升级也不用改插件。
  3. 翻译:POST www.doubao.com/chat/completion 读 SSE。必须丢弃 FULL_MSG_NOTIFY 回声帧(那是自己发出的消息),否则答案开头会重复用户原话。
  4. 接进 DSH:插件在 127.0.0.1:30812 起一个 OpenAI 兼容小门(只认真 Bearer, 只认 loopback 的 Host/Origin),再用 ctx.llm.registerAdapter 把 doubao-work 这个 provider 挂到 ctx.llm 上。DSH 的模型目录是现算的,注册完立刻可见。
  5. 读额度:走同一个 CDP 连接拿到的 Cookie,POST 豆包自己的 /alice/commerce/sale/subscription/quota/summary/,把 5 小时 / 7 天两个滚动窗口 折算成百分比。宿主侧经 /plugins/dsh-doubao-work-connect/quota 暴露给浏览器半, 由输入框左侧的小胶囊显示。

端口:调试口 30813 沿用上游参考仓库的默认值;小门 30812 是本插件自选(避开 30813)。 两者都可配置,小门被占用时会自动退回随机端口。

多开注意:桌面端和网页端 DSH 若同时运行、都装了本插件,它们会抢同一个调试口 30813、并争着用 taskkill 重启同一个豆包客户端。小门端口冲突会自动退回随机 端口,但调试口没有隔离机制。要么只在一个 profile 里启用,要么给其中一个显式配置 不同的 debugPort——不过豆包客户端本身只会开一个调试口,所以实际上只能二选一。

配置项

都可选,改 profile 里的插件配置即可:

键 默认 说明
shimPort 30812 小门监听端口
debugPort 30813 客户端调试口
doubaoExePath 自动探测 DoubaoWork.exe 路径
autoRestartDoubao false 调试口不通时自动 taskkill /F 重启客户端一次。默认关(0.1.3 起);打开会打断你正在用的客户端
reasoningBlockTypes [] block_type 归类为「思考」的白名单,默认空 ⇒ 一律当正文。该映射未经真机校准,拿到真实帧后再填,详见 CHANGELOG「已知限制」
autoDeleteConversation true 每轮答完删掉豆包侧临时会话(即用即焚)

额度显示

输入框左侧会有一个「豆包额度」胶囊,鼠标悬停展开明细:套餐名、每个窗口的 已用百分比与 已用/总量、以及窗口重置时间。点一下立即刷新。

  • 数据源是豆包自己的订阅额度接口,不是估算值;两个窗口就是官方口径的 5 小时与 7 天滚动限额。
  • 60 秒缓存一次,不做后台轮询轰炸;客户端没启动时显示「豆包未运行」,不会 为了读额度去把客户端拉起来。
  • 豆包客户端没打开时胶囊是灰的;打开并登录后自动恢复。

对应的宿主路由(只接受来自本机回环地址的请求,供浏览器半读取额度):

GET /plugins/dsh-doubao-work-connect/quota
    → 200 {state:"ok", plan, personal:{windows:[…]}, enterprise:{windows:[]}, …}
    → 200 {state:"app-not-running"}  客户端没开
    → 202 {state:"loading"}          首次解析还没完成,稍后重试

说明(0.1.1 修正):文档此前写「需要 GUI 会话的 bearer」,实际实现只校验 Host/Origin 是否回环,不要求 Authorization。实测无凭据也能拿到 200。 这类本机路由无法区分「浏览器半」与「本机其他进程」,所以请把它当作 本机可见的只读接口,不要假设它有鉴权。

自查

两套脚本,都不改任何配置:

node test/smoke.mjs   # 离线:假 ctx 走一遍 apply(),验注册/模型目录/小门鉴权。不需要豆包、不需要网络。
node test/live.mjs    # 真机:小门可达 → 模型列表 → 非流式一轮 → 流式一轮。需要豆包已登录 + DSH 已加载本插件。

live.mjs 可带参:--port 30812、--model doubao-think。

也可以直接打小门(PowerShell 里 curl 是 Invoke-WebRequest 的别名,要写 curl.exe)。 令牌是每次启动随机生成的,不再是源码里的固定字符串;它在 DSH 启动插件时写到 <DSH_HOME>/dsh-doubao-work-connect/shim-token.txt,即本机的 ~/.dsh/dsh-doubao-work-connect/shim-token.txt(插件卸载时删除)。

关于权限(0.1.3 更正):此前文档写这个文件是「0600」。实际上 Windows 上 Node 的 mode 对文件权限是 no-op,写盘时传的权限位不生效;文件的实际权限跟随用户目录(~/.dsh)的 ACL。 早期 0.1.1 条目里「(0600,卸载即删)」的写法沿用了这个误述。这不影响它是机密这件事—— 读到它等于能借用你的豆包额度,别往外传、别提交进仓库。

$token = Get-Content "$env:USERPROFILE\.dsh\dsh-doubao-work-connect\shim-token.txt"
curl.exe -H "Authorization: Bearer $token" http://127.0.0.1:30812/v1/models

不带 Authorization 会得到 401,这是设计:小门只服务宿主进程,不是公开端口。

0.1.1 修正:此前小门令牌是写死在源码里的常量 doubao-connect-local,等于没有 密钥——任何本机进程读到这个公开仓库就能借用你的豆包额度。现在改为每进程随机。

插件还注册了两条 host 路由,可以用它们确认插件真的被宿主加载了:

GET /plugins/dsh-doubao-work-connect/status   → {state, debugPort, userId, deviceId, webId, cookieCount}
GET /plugins/dsh-doubao-work-connect/quota    → 见上节

在本机 DSH 0.2.1-rc.2 上实测这两条都是 200(宿主会转发插件的 host 路由;早期笔记里 「返回 404」的结论是在旧端口上测的,已作废)。status 返回 state:"signed-in" 即说明 CDP 连通、Cookie 与签名都拿到了。

诊断:插件到底加载了没有

/plugins/.../status 能回答一半问题——它 404 只说明「路由没挂上」,但不区分「插件没加载」 和「宿主没转发」。0.1.3 起多了一个更直接的判据:apply() 注册成功后会在 <DSH_HOME>/dsh-doubao-work-connect/host-heartbeat.json 写一份心跳, 字段为 version / package / pluginVersion / pid / startedAt / updatedAt / shimPort / debugPort, 每 30 秒刷新一次 updatedAt(卸载时只在 pid 属于自己时才删除):

Get-Content "$env:USERPROFILE\.dsh\dsh-doubao-work-connect\host-heartbeat.json"

看到 updatedAt 时间在往前走,就说明宿主确实加载并 apply 了本插件; 文件不存在或 updatedAt 停住,则插件没起来、或者进程已经死了。

注意:桌面端与网页端 DSH 会共用这一个文件(谁后写谁赢), 所以它一次只能反映最后写的那一端,不能证明"只有一端在跑"。 文件里的 shimPort 是那一端的实际监听端口;想知道两端各在什么端口上跑, 更可靠的办法是看数据目录里的 shim-token-<端口>.txt——每个活着的小门都会留一份自己的 (见「双端同时启用」一节)。

已知限制

  • 模型列表是内置静态表(upstream.js 的 FALLBACK_MODELS),豆包出新模型要手动加。
  • 只有文字对话,图片输入未实现。
  • 必须保持豆包工作客户端登录;客户端大版本更新后请求参数可能需要重新实录校准。
  • block_type → 思考/正文的映射未经真机校准:默认全部当正文(reasoningBlockTypes 默认 [])。 这是一个保守选择——宁可少显示思考,也不把正文误判成思考。取证时该账号对 /chat/completion 一律返回 710022004,连豆包客户端自己发出的请求也一样被拒,因此暂时拿不到带正文/思考的真实帧; 拿到真实帧后再用 reasoningBlockTypes 校准即可,不必改代码。
  • 一轮对话的上下文完全由 DSH 每次重发的 messages 决定,插件侧不保存任何会话状态; 送上游前会按 HISTORY_MAX_CHARS(48000 字符)从最老的消息开始裁剪。
  • 工具调用是提示词模拟,不是豆包原生能力;见上文说明。
  • usage 是估算值(按字符数 ÷ 2),不是豆包的官方计费口径;上游若回传了真实 usage 则优先用真实值。成本统计页的数字因此只能当参考。
  • max_tokens 由插件侧按字符预算近似执行(不是上游硬截断),finish_reason 会相应给出 "length";temperature 会透传,但豆包是否采纳取决于其服务端。

免责声明

本项目仅供个人学习与研究使用,只驱动使用者本人在本机已登录的豆包账号。 因使用本项目产生的任何后果由使用者自行承担。本项目与豆包、字节跳动、 DeepSeek 均无关联,未获其授权或认可。

来源与版权

本项目的代码骨架(upstream.js 与 credentials.js 的 CDP / 签名 / SSE 部分) 来自 marsma-101/dsh-doubao-connect(MIT), 其中 upstream.js 与本机所取的上游版本逐字节一致;credentials.js、index.js、 cordis.patch.yml 做了针对豆包工作(DoubaoWork.exe、doubaowork-chat 页面、 provider id doubao-work)的改造。因此 LICENSE 里保留的是上游的版权行 Copyright (c) 2026 marsma-101——这是 MIT 的要求,不是笔误。

双端同时启用

结论:不需要二选一,两端的端口隔离是自动的。 下面全部是 2026-10-20 的双端实测值 (桌面端 DSH 与 dsh web 同时运行、同一个 ~/.dsh、同一个豆包客户端):

项 桌面端 DSH 网页端 DSH 是否冲突
host Web 端口 2646(动态分配,本次实例还有 2647/2648) 8080 否
小门监听端口 30812 7112 否——30812 被桌面端先占,网页端 EADDRINUSE 后自动回落随机端口
小门令牌文件 shim-token-30812.txt shim-token-7112.txt 否——按端口分开
豆包调试口 30813 30813 否——两端都只做只读 CDP(读 Cookie / 调签名),不独占该端口
/plugins/.../status 200 200 —

跨端鉴权实测:拿桌面端的令牌敲 127.0.0.1:7112/healthz 得 401,反过来同样是 401; 各自用自己那份令牌都是 200。也就是说两端的小门是彼此独立的,不会互相借用额度。

几点必须知道的细节:

  • 共享文件是「谁后写谁赢」:shim-token.txt、shim-token.pid、host-heartbeat.json 在两端之间是同一份。所以 0.1.3 起每个小门额外写一份 shim-token-<实际端口>.txt, 心跳里的 shimPort 记的也是实际监听端口(不是配置里的 30812)—— 否则双端同时启用时,配置值会让人误以为"只有一端在跑"。
  • test/live.mjs 请带 --port:node test/live.mjs --port 7112。脚本会优先读 shim-token-7112.txt;不加 --port 时默认 30812、拿桌面端的令牌去敲,正好敲对。
  • 桌面端的 host 端口不是固定的,本次是 2646;2647/2648 上的同名路由返回 401, 它们是同一个 Electron 应用里的其它监听器,不是 host Server。探测请用当前实际端口。
  • autoRestartDoubao 默认已改为 false:豆包在跑但没开调试口时,插件不会再 taskkill /F 掉它,而是报错提示你自己带 --remote-debugging-port=30813 重启。 双端同时启用时这一点尤其重要——否则杀掉客户端会同时打断两端。

许可证

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit 79c3be96fbc0

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout