dsh-cherry-bridge
把 DeepSeek Harness 会话桥接到局域网内的 Cherry Studio 桌面端——二维码配对、14 个 cherry_* 工具(agent/会话/历史/交互)与设置页(设备管理、按工作区的工具启用范围)。
安装
dsh plugin --profile web add dsh-cherry-bridge
沙箱测试结果
| 阶段 | 结果 |
|---|---|
| 1 安装 | ✓ |
| 2 列装 | ✓ |
| 3 挂载 | ✓ |
| 4 启动 | — |
四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。
README
<div align="center">
[English](README.en.md) | 简体中文
</div>
<div align="center">
<img src="static/logo.png" alt="dsh-cherry-bridge" width="880">
</div>
dsh-cherry-bridge
把 DeepSeek Harness 桥接到 Cherry Studio 桌面端 — DSH 插件
在 DSH 会话里直接驱动局域网内 Cherry Studio 的 agent:配对、列设备/会话、读历史、
发消息并等待终态、应答审批与提问卡、取消执行;附一个同源设置页管理设备与工具启用范围。
flowchart TB
T["DSH 会话<br/>14 个 cherry_* 工具"] --> P
W["设置页<br/>设备 · 配对 · 启用范围"] --> P
P["dsh-cherry-bridge<br/>连接池 · 订阅缓存 · 作用域"]
P <-->|"ws:// · Noise XX"| C["Cherry Studio 桌面端<br/>二维码配对 · 审批"]
- 包名:
dsh-cherry-bridge,插件 id:cherry-remote - 传输:默认
ws://+ Noise XX(加密由 Noise 记录层提供);桌面端位于 TLS 前置之后时拨wss://,私有 CA 的信任由wsTls提供 - 工具面:14 个
cherry_*工具,按工作区启用,默认谁都没有(见「启用范围」) - 设置页与 HTTP 面:宿主 Web 服务器上的同源前缀
/cherry-remote/api
安装
DSH 版本需求:>=0.2.0-rc.2 <0.3.0-0(由package.json的engines.dsh声明;不满足时 bundle 被跳过、设置页里该插件的行被禁用,确需在区间外使用可用dsh plugin allow-version精确豁免。0.3.0 需重新评估兼容性后再放开)。
Cherry Studio 兼容范围:桌面端 2.1.4 – 2.1.10 已核对——v3自定义地址二维码需 2.1.8+,2.1.4–2.1.7 的v2二维码同样可用。2.1.9/2.1.10 与 2.1.8 在 remote-access 面上只差桌面端发送链的一处健壮性修复(PR #22593,不影响客户端),并在 2.1.10 真机上跑过「建会话 → 发送 → 等待 → 读取」冒烟。区间外未经验证:握手只协商协议版本[1],不兼容时会以unreachable明确失败,不会静默出错。协议包钉在0.7.0/0.2.5,2.1.10 携带的还是同一对版本。
# 从 npm 安装(推荐,钉住版本) npx @deepseek-ai/dsh plugin --profile web add dsh-cherry-bridge@0.2.3 # 安装 npm 最新发布版 npx @deepseek-ai/dsh plugin --profile web add dsh-cherry-bridge # 从 GitHub 源码安装(与 npm 发布同一提交) npx @deepseek-ai/dsh plugin --profile web add github:SAXEM1997/dsh-cherry-bridge#v0.2.3
本包自带 cordis.patch.yml(package.json 的 dsh.bundle.patch 指向它),
dsh-plugin-manager 据此自动插入插件行。手工加行等价于:
- id: cherry-remote
name: 'dsh-cherry-bridge'
config:
perSessionCacheBytes: 2097152
rateBudgetPerSecond: 32
首次配对
1. 在 Cherry Studio 桌面端打开 remote access → 添加设备:该界面显示二维码,
配对信息要点开扫码后的页面才看得到。
2. 扫这个二维码(微信、浏览器等均可),复制页面上显示的配对信息。
3. 在 DSH 里调用 cherry_pair(设置页向导亦可),粘贴这段配对信息。
4. 双向核对 6 位验证码:与桌面端显示的数字人工比对一致后再在桌面端批准。
5. 批准后设备档案写入数据目录(devices.json,0600),其 desktopIdentity 被 pin 住,
此后每次连接都校验 Noise 对端静态公钥与 pin 一致,防二维码被劫持后的中间人替换。
快速上手
cherry_pair → 粘贴扫码页的配对信息,核对 6 位验证码 cherry_devices → 看设备与连接状态(前缀即可选中,如 "dev-A") cherry_agents → 列设备上的 agent,拿 agentId cherry_sessions → 列某 agent 的会话,拿 sessionId cherry_read → 读会话历史(默认取最新 100 条,从新到旧) cherry_send → 发消息并等待终态,返回助手文本 cherry_interactions → 看待答的提问/审批卡;cherry_respond 作答
配置
键 · 类型 · 默认 · 说明
dataDir · string · $DSH_HOME/cherry-remote(回退 ~/.dsh/cherry-remote) · 插件数据目录,存放 identity.key 与 devices.json(均 0600)。
perSessionCacheBytes · number · 2097152(2 MiB) · 每个会话的本地订阅事件缓存上限;超出后从最旧一端丢弃(FIFO)并 warn,但最新的单条事件总会保留——即使它自己就超过上限(此时不打丢弃日志,窗口保持超预算)。
rateBudgetPerSecond · number · 32 · 每设备平滑请求预算(请求/秒)。桌面端共享额度在 ≤2.1.6 是 64 req/s、≥2.1.7 是 256 req/s,且只有新版本才把超限降级为单请求 RATE_LIMITED(旧版本直接 close 1011 终止会话),故按更严格的一代取保守值。内容读取(agent.content.read)按 8:1 派生独立一档,且封顶 512/s(该额度两代一致)。
wsTls · object · — · 每次 wss 拨号的扁平 tls.connect 选项(只接受 rejectUnauthorized / ca),供桌面端位于私有 CA 或自签 TLS 前置之后时由运维提供信任;配对与重连两个拨号点共用。不写入设备档案、不写日志、不进诊断报告。 整个对象要么按原样生效、要么整体忽略:未知键或不可用值按名 warn 后回退 Node 默认信任库(不部分应用)。
wsTls.ca 是 PEM 内容(证书文本本身),不是文件路径。
⚠️cordis.patch.yml的config是整体替换,不与默认值合并。删掉一个键得到的是
插件默认值;键存在但值非法(0、负数、字符串)会按名warn后回落默认值。
工具
工具 · 作用
cherry_devices · 列出已配对设备及连接状态(connected/connecting/offline/disabled/identity-mismatch)。action: "forget" 可遗忘指定地址(从拨号列表移除,且桌面再广播也不会被重新学习,直到 remember 或重新配对);action: "remember" 撤销该标记(下次握手可重新学习);两者都需要 device 与 hosts。
cherry_agents · 列出某设备上可用的 agent(分页)。被截断时必带 truncated+truncationReason(limit 或 safety-bound,后者是 1000 条内部上限);停点可命名时还给出 resumeCursor——用同一 device 与过滤条件把它回传为 cursor 即可从停点继续(此时 limit 约束的是下一次回答);跨工具/设备/过滤条件的令牌会被本地拒绝,不会转发给对端。
cherry_workspaces · 列出某 agent 的已注册工作区,以及是否存在系统工作区。被截断时必带 truncated+truncationReason;停点可命名时还给出 resumeCursor——用相同过滤条件回传为 cursor 即可续读。
cherry_sessions · 列出某 agent 的会话,可按工作区过滤。被截断时必带 truncated+truncationReason(limit 或 safety-bound),停点可命名时还给出 resumeCursor——用同一 device 与相同过滤条件把它回传为 cursor 即可从停点继续(此时 limit 约束的是下一次回答);跨工具/设备/过滤条件的令牌会被本地拒绝。
cherry_read · 读会话消息历史快照,从新到旧返回(limit 丢掉的是最旧的消息)。失败消息在桌面端给出时附带 failure: {reasonCode, retryable, message}(对端分类、是否值得重试、对端自己的有界摘要;其 source/context 不投影到回答里)。附件 part 只回 name/mediaType/byteLength,content 为空——字节不取。这覆盖桌面端可能发来的两种形状:file part(name 即文件名,byteLength 取自它的 ref);以及桌面端用于未上传附件的旧式 data part——name: "file",元数据以 JSON 形式放在 content.text 里({"mediaType":…,"filename":…}),同样字段被取出结构化,filename: null 则不报 name,且只有当该对象里的每个键都是这两个上报字段时才清空 content(带额外键时保留原始文本)。其他 name 的 data part(例如任务事件)不是附件,原样透传。被截断时 truncated + truncationReason 一定说明:limit 表示你设的条数到了,提高 limit 即可;aggregate-bytes 表示单次回答达到了输出大小上限——与 limit 无关,提高 limit 只会多付往返、仍停在同一预算。被挡下的更旧消息用 messagesResumeCursor 续读:把本次返回的 messagesResumeCursor 原样传给下一次调用的 messagesResumeCursor 参数即可从停点继续(limit 此时约束的是从停点起本次最多返回多少条),长历史分几次回答读完。停点不可命名时没有这个令牌(truncated 仍为 true,缺失本身不代表历史已到底;补救按 truncationReason 分:limit 可以提高 limit,aggregate-bytes 不行——被挡住的旧消息在这一回答里拿不到,详见[错误与限制](#错误与限制))。两种令牌:cursor:<peer position>(整窗读完后的 peer 分页位置)与 after:<messageId>(停在窗内,续读从最新端扫到该消息、返回它更旧的那些)。不是本工具发出的令牌会被本地拒绝(不转发给桌面端——桌面端会静默回退到最新页);after: 锚点在历史里找不到则报错,不会返回空结果。
cherry_events · 读取该会话缓冲区内比 cursor 更新的事件,并在投递后 ack。返回 {events, nextCursor};把 nextCursor 作为下次的 cursor 传回即可续读。
cherry_create · 在 agent 工作区创建会话(工作区选择必填;创建后回读身份)。
cherry_send · 向会话发消息,默认等待运行到终态并返回助手文本;wait: false 立即返回命令回执。内容被截断时 messagesTruncated/messagesTruncationReason 说的是消息被截断(丢更旧的);deadline 表示回答自身的装配时限到了(见下),已读到的内容照常返回。
cherry_wait · 只等待该会话在途执行收敛(不发送新消息)。
cherry_interactions · 列出该会话待答的交互卡(提问 / 审批)。被截断时必带
…(truncated)
以上内容来自 npm 上的 README,我们只做格式化,不改内容。
版本
npm 上共发布:12,最后更新 2026-11-05