🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI
广告☁️ 云服务器特惠阿里云首购 8 折 · 腾讯云合作特惠
DeepSeek Harness Hub
← 返回列表

23J1633/dsh2server

DeepSeek Harnessspec-screened扫描:中风险在 GitHub 查看 ↗
⚠ 装前注意

把本机的 DeepSeek Harnessdsh…

基本兼容但装前注意:未发布到 npm registry,仅可从源码安装 · 最近上游提交 2026/9/20 · 已提供中文文档

DeepSeek Harness 桥接/插件,用于 A2S,支持远程会话、工作区、审批、任务和弹性重连。

综合分
31.5
GitHub 分
31.5
用户评分
—
★ Stars
2
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add 23J1633/dsh2server
未发布到 npm registry,仅可从源码安装,改用 GitHub 源安装
信任档位:已验证本站已于 3 天前真实安装成功(L4 · 真实安装)
是什么
dsh 原生插件 · chat
装得上吗
本站已真实安装成功(L4 · 真实安装,非静态推断)
安全吗
本站尚未对该插件做风险分级(暂未覆盖,不等同于无风险)
还在维护吗
活跃:最近一次提交在 5 天前

档位由下列信号合成:本站实装验证(真实安装,当前最高到 L4)· 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。下方各区块是它的证据明细。 验证判据与等级说明 →

🟢实装验证通过· 2026/9/23
由本站实装验证器在真实 dsh 环境安装成功,非静态推断。
数据截至 2026/9/20(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查⚠ 装前注意

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包dsh2server(未发布到 npm,仅可源码安装)
✓Node 引擎要求 >=22.0.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

未发布到 npm registry,仅可从源码安装

验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/20 18:51:23

用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

由 DeepSeek 最新模型翻译生成
dsh2server

中文

把本机的 DeepSeek Harness(dsh) 连接到一台中转服务器:服务器上能看到这台机器的实时工作状态、全部工作目录和会话活动,并能对它下发操作(新命令、中断、暂停/恢复、审批应答、任务终止、会话管理)。

- dsh 主动连出,服务器不需要访问你的电脑,不需要端口映射,不需要内网穿透。
- 服务器不保存会话正文:只持久化机器白名单和控制台归档索引;会话内容仍由已连接的 dsh 提供。
- 每台机器一个统一 key:在 A2S 模式下自动读取与 Claude/Codex 相同的 a2sk_ 设备 key;独立使用时仍可生成原有 dshk_ key。服务器可按设备整体配对和吊销。
- http:// 与 https:// 同等支持,并且可以同时连多个端点:例如内网一台 http:// 中转给本地控制台用,公网一台 https:// 供远程访问。每条连接互相独立,一个端点掉线不影响其它端点。
- 零运行时依赖:纯 ESM JavaScript,无需构建,使用 Node 内置的 fetch / WebSocket。
- 一套固定 API:WebSocket 与 HTTP 长轮询两种载体共享同一套 JSON 帧,后端用任何语言/框架实现都可以。完整规范见 docs/API.md。

A2S 生态(同系列开源仓库)

dsh2server 只是 A2S 体系中的一个插件。同体系还有通用控制台与手机端——它们与本插件共用同一个 a2sk_ 设备 key、同一套协议,可以单独取用,也可以和本插件一起用:

| 仓库 | 定位 | 说明 |
|---|---|---|
| server-api | 通用控制台(Web) | 统一服务器端,同时接收 Claude Code、Codex 与 DSH 的连接,自带多 Agent Web 控制台。正式部署用它;只有单独跑 DSH、不接其它两个 Agent 时才需要本仓库内的 dsh-api/ |
| A2Switch | 通用控制台(桌面)+ 插件配置路由 | Electron 桌面控制中心:维护跨桥接器共享的设备配置与 key、检测本机 Agent 环境、一键安装/更新三个插件、启动与停止桥接进程 |
| a2s_app | 手机控制端 | Flutter Android 客户端,在手机上查看机器状态与会话活动、下发远程操作 |
| cc2server | Claude Code 桥接器 | 把 Claude Code 接入 A2S 协议 |
| codex2server | Codex 桥接器 | 基于官方 Codex app-server 的桥接器 |
| dsh2server | 本仓库 | DeepSeek Harness 插件,也就是你正在读的这个 |
| dsh-api/(本仓库内) | DSH 专用控制台 | 旧版单 Agent 的服务器 + 控制台,保留给 DSH 独立部署与兼容性测试。见 DSH 专用控制台(dsh-api) |

三个桥接器用同一个设备 key、各自独立的实例 ID(:claude / :codex / :dsh),服务器端会把它们聚合为同一台设备,同时仍能单独选择具体 Agent。

目录

- A2S 生态(同系列开源仓库)
- A2S 统一模式
- 快速开始
- 安装
- 配置
- 图形化配置(dsh Web GUI)
- 实例 Key 与配对
- 服务器端
- 远程能做什么
- 能力依赖
- 工作原理
- 目录结构
- 开发与测试
- 安全须知
- 常见问题

A2S 统一模式(推荐)

与 A2Switch、cc2server、codex2server 一起使用时,不需要为 DSH 单独复制 key 或重复填写服务器。插件会在 DSH profile 没有显式设置对应字段时读取 A2S 共享配置:

| 系统 | 默认共享配置 |
|---|---|
| Windows | %APPDATA%\A2S\config.json |
| macOS | ~/Library/Application Support/A2S/config.json |
| Linux | ${XDG_CONFIG_HOME:-~/.config}/a2s/config.json |

它从中继承:

- device.key:与 Claude、Codex 共用的设备 key;
- device.id:统一设备 ID;
- device.name:默认显示名;
- server.endpoints:统一服务器端点;
- agents.dsh.instanceId:默认是 :dsh。
- agents.dsh.locale:插件语言,默认 system,自动识别运行电脑的语言。

推荐流程:

1. 先在 A2Switch 中填写服务器并完成设备配对
2. 安装 DSH 插件(A2Switch 的一键安装也会执行这一步)
dsh plugin --profile web add D:\Project\A2S\dsh2server

3. 启动原有 DSH profile
dsh web

共享配置只填补 DSH profile 中缺失的字段,不会覆盖已经显式写入的 DSH 专用配置。优先级为:

DSH2SERVER_* 环境变量 / profile 显式值
> A2S 共享配置
dsh2server 自身默认值

可用 A2S_CONFIG_PATH 或 A2S_CONFIG_DIR 改变共享配置位置;也可以在 profile 中设置 a2sConfigFile,给该 profile 固定一个配置文件。后者也用于便携式安装与测试隔离。

新的统一服务器基路径为 /a2s-api。server-api 同时保留 /dsh-api 别名,因此旧配置无需立即迁移。

A2Switch 本地启停

dsh2server 0.1.1 起,插件会在 A2S 共享配置旁维护一个不含密钥的本地运行状态与控制通道:

- runtime/dsh.json:插件版本、Harness PID、连接/暂停状态及最近一次控制结果;
- runtime/dsh-control.json:A2Switch 写入的一次性 start、stop 或 restart 指令。

A2Switch 的“停止”只关闭到服务器的桥接链路并发送正常 bye,不会结束 DeepSeek Harness、Web UI 或本地会话;“启动”会恢复链路;“重启”会在同一个 Harness 进程内重建全部链路。控制文件受本机配置目录权限保护,且不保存设备 key。

快速开始

① 装进某个 dsh profile(本例用 web)
dsh plugin --profile web add /path/to/dsh2server

② 在该 profile 的 cordis.patch.yml 里把 endpoint 填成你的服务器地址
(或者直接改 bundle 自带的那一行;也可以设置环境变量 DSH2SERVER_ENDPOINT)
endpoint: 'https://example.com/dsh-api'

③ 启动,日志里会打印本机专属 key
dsh web
[dsh2server] instance key: dshk_xxxxxxxxxxxxxxxxxxxx   ← 复制它

④ 把这个 key 登记到你的服务器(示例用本仓库的参考实现)
curl -X POST https://example.com/dsh-api/keys \
-H 'content-type: application/json' \
-H 'x-admin-key: ' \
-d '{"key":"dshk_xxxxxxxxxxxxxxxxxxxx","label":"我的笔记本"}'

登记完成后(最多 60 秒内)机器就会上线。用本仓库的参考服务器可以立刻验证:

node examples/server.js --port 8787 --keys ./examples/keys.json
然后在插件里把 endpoint 填成 http://127.0.0.1:8787/dsh-api
curl http://127.0.0.1:8787/dsh-api/instances            # 看在线机器

安装

它是一个标准的 dsh bundle(package.json 里声明 dsh.bundle.patch),因此用 dsh 自带的插件管理安装即可:

从本地目录安装(开发时最方便;pnpm 会做 link)
dsh plugin --profile  add /path/to/dsh2server

从 GitHub 安装(需要 Node 侧允许该包的构建脚本;本包无需构建,因此不会被拦)
dsh plugin --profile  add github:/dsh2server

从 tarball 安装
pnpm pack && dsh plugin --profile  add ./dsh2server-1.0.0.tgz

安装后:

dsh plugin --profile  list           # 确认已加入 dsh.profile.bundles
dsh --profile  --dump-config         # 能看到 "# == dsh2server" 这一层

卸载:

dsh plugin --profile  remove dsh2server

手工安装(等效于 dsh plugin add)

在 profile 目录($DSH_HOME/profiles/)里:

1. cordis.patch.yml 追加:

- insert:
- id: dsh2server
name: 'dsh2server'
config:
endpoint: 'https://example.com/dsh-api'

2. package.json 里加上依赖与 bundle 列表:

{
"dependencies": { "dsh2server": "link:/path/to/dsh2server" },
"dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "dsh2server"] } }
}

3. 在 profile 目录执行 pnpm install。

配置

唯一必填项

endpoint: 'https://example.com/dsh-api'

endpoint 是服务器 API 的基地址。插件由它推导出 …/ws、…/events、…/inbox(规则见 docs/API.md §2.1)。

它接受单个 URL、URL 列表或逗号分隔字符串,并且每个 URL 的协议(http / https)可以任意混用:

单个端点(最常见)
endpoint: 'https://example.com/dsh-api'

多个端点:内网 + 公网同时在线
endpoint:
- 'https://example.com/dsh-api'      # 公网可达
- 'http://10.0.0.5:8787/dsh-api'     # 局域网内的中转,给本机控制台用
同一个实例对每个端点使用同一把 key,多个服务器看到的是同一个 instanceId 和同一套事件序号;每条连接各自独立重连、独立订阅,一个端点不可达或被吊销都不会影响其它端点。

空着不填时,插件保持加载但完全空闲,不会发任何网络请求,并在日志里提示如何配置。也可以用环境变量代替:

DSH2SERVER_ENDPOINT=https://example.com/dsh-api        # 或用逗号分隔多个
DSH2SERVER_KEY=

关于 HTTP 与 HTTPS

- 两种协议都完整支持,不强制 TLS。使用 http:// 时插件会在启动日志里明确告警(key 与全部会话流量都是明文),并在 instance.info 的 connection.insecure 中持续标记。
- 私有 CA 签发的证书:NODE_EXTRA_CA_CERTS=/path/to/ca.pem(推荐做法)。
- 自签名证书:仅在你已经信任该网络时使用 NODE_TLS_REJECT_UNAUTHORIZED=0。
- 公司代理阻断 WebSocket 升级时,把 transport 固定为 'http'。

常用可选配置

完整列表(含默认值)见 cordis.patch.yml。最常改的几个:

| 配置 | 默认 | 说明 |
|---|---|---|
| key | '' | 留空 = 自动生成并保存在本机;填写 = 使用该 key 且不落盘 |
| keyFile | '' | 身份文件路径,默认 /dsh2server/identity.json |
| a2sConfigFile | '' | 可选的 A2S 共享配置文件;留空使用系统默认位置 |
| deviceId | '' | 设备聚合 ID;A2S 模式通常自动继承,不需要手填 |
| authMode | hello | key 的传递方式:hello / header / query |
| transport | auto | auto(先 WebSocket 再回退 HTTP)/ ws / http |
| locale | system | 插件设置页与插件状态语言:system / zh-CN / en-US |
| autoSubscribeSessions | running | 自动推送哪些会话的逐条事件:none / running / all |
| forwardApprovals | false | 是否把工具调用审批转发到服务器等待远程批准 |
| forwardQuestions | true | 是否把结构化提问转发到服务器;关闭后远程会话遇到提问只能回本机处理 |
| allowRemotePrompt | true | 是否允许远程下发新命令 |
| allowRemoteControl | true | 是否允许远程中断/暂停/切策略/轮换 key(同时管着权限预设的切换与插件配置的写入) |
| allowRemoteCommand | true | 是否允许远程执行斜杠命令(如 /compact) |
| allowedCwdPrefixes | [] | 非空时,只有工作目录匹配这些前缀的会话才可见/可控 |
| logLevel | info | silent / error / warn / info / debug |

配置错误会在加载阶段直接失败,并给出精确到字段的错误信息(例如 transport: must be one of auto | ws | http)。

图形化配置(dsh Web GUI)

装好之后,dsh 的网页界面里会自动多出一个标签页:

设置 → 插件 → dsh2server

它提供:

| 区域 | 能做什么 |
|---|---|
| 服务器 API 端点 | 独立端点列表,每个 URL 可单独添加、编辑或移除(http / https 可混用)。保存后立即生效,无需重启 dsh |
| 传输方式 | auto / ws / http 下拉(PHP 后端选 http) |
| 插件语言 | 自动跟随系统 / 简体中文 / English;保存后立即生效,也可由 A2Switch 统一配置 |
| 本机实例 Key | 指纹展示、显示 完整 key、复制 Key 一键复制、复制登记命令(生成可直接执行的 curl)、轮换 Key |
| 连接状态 | 每条链路的实时状态(已连接 / 未连接 / 被拒绝)与拒绝原因 |
| 远程权限 | allowRemotePrompt / allowRemoteControl / forwardApprovals 开关 |

语言设置只影响 dsh2server 自己的配置页、提示和上报元数据,不会翻译 DSH 会话正文。显式选择会写入网页设置层;选择“自动跟随系统”时使用浏览器/运行电脑语言。网页层没有显式覆盖时,profile 中的 zh-CN / en-US 可固定语言;profile 为默认 system 时则继续采用 A2Switch 写入的 agents.dsh.locale。

它是怎么出现的(对使用者完全透明)

标签页是这个包自带的前端半边,不是对 dsh 的改动:

- 包在自己的 package.json 里声明了 dsh.client(exports["./client"] → lib/client.js),dsh 的客户端模块系统会自动扫描已启用的 Loader 条目,把每个声明了 dsh.client 的包的浏览器产物送到页面;
- 浏览器半边只往 dsh 公开的插槽 settings.plugins.tab 里注册一行(和内置的「插件配置」标签用的是同一个扩展点),并自带样式与数据获取;
- 数据通过本包自己注册在 Connection 共享通道上的 /api/dsh2server/ 路由取得——因此天然带有 dsh 的 Host/Origin 校验与浏览器会话认证。
结论:任何人 dsh plugin add dsh2server 之后,打开网页就能看到这个标签页,不需要改 dsh、不需要额外步骤,也不需要作者本机有什么特殊配置。前端产物零外部依赖(只用 shell 平台表里的 react,所以连 dsh.client.external 都不用声明,不会因为别人组合里缺某个插件而失败)。

配置的优先级

网页里改过的项写入 /dsh2server/config.json,它逐键覆盖 cordis.yml 里的同名项:

schema 默认值   key、allowedCwdPrefixes 这类部署级/安全级配置不在网页可编辑范围内,仍然只由 cordis.yml 决定。

实例 Key 与配对

每台机器一个 key

- 首次启动时插件用 CSPRNG 生成 dshk_ + 43 字符(256 bit 熵)的唯一 key。
- key 保存在本机 /dsh2server/identity.json(0600),不会上传到服务器。
- 启动日志里会打印一次完整 key;也可以随时用脚本读取:

bash
node scripts/show-key.js            # 人读格式
node scripts/show-key.js --json     # 机器可读

服务器侧多 key 白名单

服务器维护"key → 机器"的映射(示例 examples/keys.json):
json
{
"keys": [
{ "key": "dshk_AAAA…", "label": "办公室台式机" },
{ "key": "dshk_BBBB…", "label": "笔记本" }
]
}

语义:

- 一个 key 只能访问它自己那台机器,多机器之间完全隔离。
- 吊销 = 删除 key:该机器下次重连(≤60s)被拒,其它机器不受影响。
- 轮换:对机器调用 instance.rotateKey,把返回的新 key 登记进白名单、删掉旧 key。
- 服务器只显示 key 指纹(dshk_AbCdEf…9xYz),不要把完整 key 回显到界面。

配对流程
text
① dsh 启动 → 日志打印 key(或 node scripts/show-key.js 读取)
② 把 key 登记进服务器白名单
③ 机器自动上线(GET /instances 可见)

机器离线后再上线不需要重新配对——key 是持久的。

服务器端

后端需要实现的一切都在 docs/API.md:

- 两种传输的端点约定与握手细节
- 全部帧(envelope)字段
- 订阅模型与全部事件(session/event、session/status、session/assistant-stream …)
- 全部远程方法(参数 / 返回 / 错误码)
- 错误码表、序号补发与断线重连语义
- 后端实现清单与安全建议

本仓库还提供一个零依赖、可直接运行的参考实现(同时被测试用作真实服务端):
bash
只用 HTTP
node examples/server.js --port 8787 --keys ./examples/keys.json

HTTP 与 HTTPS 同时监听(同一份 key 白名单、同一份实例表)
node examples/server.js --port 8787 --tls-port 8443 \
--tls-cert ./cert.pem --tls-key ./key.pem --keys ./examples/keys.json

它实现了 WebSocket + HTTP 长轮询两种载体、HTTP 与 HTTPS 双监听、多 key 白名单、内存事件环、以及一组管理接口:

GET    /dsh-api/instances                      在线机器
GET    /dsh-api/instances/:id/events           最近事件(仅内存)
POST   /dsh-api/instances/:id/request          下发任意操作
POST   /dsh-api/instances/:id/subscribe        调整订阅
GET    /dsh-api/keys  / POST /dsh-api/keys     查看 / 登记 key
POST   /dsh-api/keys/remove                    吊销 key

详见 examples/README.md。

PHP 后端(带内置网页测试台)

不想装 Node 也可以直接用 PHP:php/dsh-relay.php 是一个单文件、零依赖的中转服务器,
并且自带一个完整的网页调试台——配对、看在线机器、拉会话与工作目录、下发命令、中断/暂停/恢复、
任务与目标、审批应答、事件流实时刷新,以及"一键自检"逐条验收整条链路。
bash
php -S 127.0.0.1:8080 php/dsh-relay.php      # Windows: D:\xampp\php\php.exe -S 127.0.0.1:8080 php/dsh-relay.php
然后浏览器打开 http://127.0.0.1:8080/

插件侧配置:
yaml
endpoint: 'http://127.0.0.1:8080/dsh-api'
transport: 'http'        # PHP 无法升级 WebSocket;写 auto 也会自动回退到 HTTP 长轮询

PHP 的 php -S 与 mod_php 都无法把 HTTP 请求升级为 WebSocket,因此该实现走协议允许的
HTTP 长轮询载体——功能与 WebSocket 完全一致,只是延迟略高。需要真正的 WebSocket 时用
Node 参考实现或 Workerman / Ratchet / Swoole。
See php/README.md for details.

DSH Dedicated Console (dsh-api)

dsh-api/ is the DSH dedicated console bundled with this repository—it only recognizes one Agent: DSH. It is both another relay implementation of the protocol (WebSocket + HTTP long polling, depending on ws and marked), and it comes with a graphical console that replicates the dsh local interface: machine and session list on the left, conversation stream and tool calls in the middle, status bar on the right, plus a separate trajectory page, and it passes through the local dsh's extension capability bits all the way to the interface. If you want a ready-to-use "cloud operations perspective" interface and only run DSH, use this—no need to assemble it yourself from the management API.

If you need to manage Claude Code, Codex, and DSH at the same time, or view from your phone, switch to the general-purpose consoles server-api, A2Switch, and a2s_app, see A2S Ecosystem.
bash
cd dsh-api
npm install
npm start                       # default :50443; certificate path in dsh-api/lib/config.js, falls back to plaintext HTTP if unreadable

When you don't have a real dsh at hand, use the simulator to get data flowing in every panel
npm run sim
npm run selftest                # end-to-end self-test

Runtime data (key whitelist, management key, archive index, logs) lands in ~/.dsh-relay, and can be changed elsewhere with DSH_RELAY_DATA_DIR; the .runtime-manual/ in the repository is one such manually specified data directory, already git-ignored.

This directory targets legacy single-Agent standalone deployment and compatibility testing. For formal A2S deployment, use server-api—it supports Claude / Codex / DSH sharing a device key, unified device aggregation, and a multi-Agent console, while remaining compatible with the /dsh-api path. See dsh-api/README.md for details.

What can be done remotely

View status

- session.list — all sessions, with cwd (working directory), running (whether it's working), last activity time
- workspace.list — all working directories on this machine and their sessions
- session.get — detailed status of a single session: model, pending queue, todo list (todos), goal, approval policy, pause state
- session/status, session/activity, session/event, session/assistant-stream event streams — see every turn, every step, every tool call, and token-by-token output in real time
- job.list / job.read — background jobs and their output
- goal.get — phases and rounds of long-term goals

Issue operations

| Operation | Method |
|---|---|
| Issue a new command | session.prompt (mode: queue) |
| Append instructions to the currently running turn | session.prompt (mode: steer) |
| Interrupt current execution (keep queued) | session.interrupt |
| Abort and discard queued | session.cancel |
| Pause running | session.pause |
| Resume and release commands queued during pause | session.resume |
| Create a new session (specify working directory) | session.create |
| Read history / full-text search | session.history / session.search |
| Rename / fork | session.rename / session.fork |
| Switch model | session.selectModel |
| Edit/delete queued messages | session.queueUpdate |
| Switch approval policy (ask / never) | session.approvalPolicy |
| Terminate background jobs | job.kill |
| Pause/resume/complete goals | goal.pause / goal.resume / goal.complete |
| Execute slash commands | command.run |
| Respond to approvals and questions | approval.respond / question.answer |
| Read/rotate local key | instance.key / instance.rotateKey |

Implementation semantics of "pause running" (worth noting for the backend):

1. Immediately abort the current turn (already queued work is retained);
2. If the session has an active goal, pause it as well (otherwise the goal round driver would immediately start a new round);
3. session.prompt arriving during the pause is held in plugin memory and queued, returning deferred: true;
4. On session.resume, release them in order, and report the delivered count in the session/resumed event.

The queue limit is controlled by pauseQueueLimit; when the queue is full it returns conflict (retryable: true), and the server should retry later.

Extension capabilities (to bring the cloud interface up to the completeness of the local UI)

Beyond the methods of protocol v1, the plugin also implements the eight extensions described in PLUGIN-EXT.
They are probed by capability bit: only implemented ones appear in hello.capabilities / instance.info.capabilities,
and the server lights up or hides the corresponding interface accordingly.

| Capability bit | Method | What it's for |
|---|---|---|
| sessionEvents | session.events | Raw event window: real log sequence numbers and timestamps, enough to render the trajectory view, per-turn duration, step count, tok/s, context ring |
| messageFeedback | message.feedback | 👍 / 👎 for assistant messages; returned along with session.events when read back |
| permissionPresets | session.permission | The three-level preset on the left of the input box: "full permissions / workspace write / read-only" |
| agentPresets | agentPreset.list / read / select / copy / delete | 与本地相同的 Agent 预设名单、会话选择及用户预设管理 |
| attachments | attachment.put / attachment.get | 上传图片与文件,并在 session.prompt 的 content 里引用 |
| workspaceMutation | workspace.create / rename / remove | 工作区的新建 / 改名 / 删除(只动登记关系,不碰磁盘) |
| sessionArchive | session.archive | 通过 DSH 官方注册表归档会话(本地列表隐藏,日志保留) |
| fileBrowser | workspace.fs.list / workspace.fs.read | 右侧栏「文件」面板,只读预览 |
| pluginManagement | plugin.list / config / setConfig / setEnabled | 设置 → 插件:看清单、读配置表单、改配置、启停 |

还有两个不需要新方法的载荷增强:tool/call 事件带上模型写在参数里的意图描述
(data.description),turn/end 事件带上本轮文件改动(data.files)。

terminal 有意不实现。本 harness 的终端服务是按 Agent 拥有、面向行的交互模型,没有原始
字节流、没有 cols/rows 重排、也没有推送式输出,无法做成云端 xterm 需要的那种持久终端;
按扩展规范的说明,这种情况回退到现有 job.list + job.read 即可,服务器在缺少该能力位时会
自动改用 job 面板。

能力依赖

插件不强制依赖任何 dsh 服务:它用 ctx.get(...) 探测现有服务,并把真实可用的能力写进 hello.capabilities。因此最小组合也能连上并汇报状态,只是方法会少一些。

| 需要的能力 | 由谁提供 | 缺失时 |
|---|---|---|
| 会话/代理核心 | dsh-base(总是存在) | 插件仍会加载,但几乎无事可做 |
| sessionController | dsh-web-app 组合(dsh web) | 会话列表退化为"仅活动会话",且历史/搜索/改名/选模型不可用 |
| sessionPersistence | dsh-base | 冷会话不可见 |
| workspaceRegistry | web 组合 | workspace.list 退化为按 cwd 聚合 |
| jobs / goals / commands | dsh-base | 对应方法返回 capability_unavailable |
| approval | dsh-base | 审批策略不可远程切换 |
| userQuestions | 通常由 UI 组合提供 | 远程回答提问不可用 |

日常使用 dsh web 时上述能力全部具备。

工作原理

┌───────────────────────────── 本机 ─────────────────────────────┐
│  dsh 进程                                                      │
│   ├─ agent / session / jobs / goal / approval / commands …     │
│   └─ dsh2server 插件                                        │
│        ├─ lib/identity.js   本机唯一 key(本地持久化)           │
│        ├─ lib/host.js       能力探测与宿主适配(可选服务降级)    │
│        ├─ lib/forward.js    dsh 事件 → 协议事件                 │
│        ├─ lib/ops/         入站方法 → dsh 操作                 │
│        ├─ lib/gate.js       暂停/恢复的状态机与排队             │
│        ├─ lib/bridge.js     实例级共享状态(身份/序号/缓冲/方法) │
│        └─ lib/link.js + transport/*   每条端点一条独立链路       │
└───────┬──────────────────────────────────────┬─────────────────┘
│ 出站 wss:// (公网)                    │ 出站 ws:// (内网)
▼                                       ▼
https 中转服务器                        http 中转服务器
(纯中转,可无状态)                     (纯中转,可无状态)

一条链路可以同时是 WebSocket 或 HTTP 长轮询,也可以是 TLS 或明文;每个端点各有一套,互不影响。

设计要点:

- 服务器可无状态:所有状态都能被重新推送。断线时插件在内存环形缓冲里保留最近 bufferSize 条事件,重连后按水位补发;补发窗口不够时会发 bridge/resync,服务器重新拉全量即可。
- 不订阅就不序列化:事件按 topic 订阅,没订阅的流完全不走 JSON 编码,空闲链接开销接近零。
- 永不阻塞本机:所有入站方法都有超时与并发上限;所有宿主调用都被包裹,异常只会变成一个协议错误,不会影响 dsh 本体。
- 审批转发是可选的、且不抢占本机:远程不回答或超时,会自动交回本机 UI 处理,绝不会把本地会话卡死。

目录结构

dsh2server/
├── package.json            # bundle 清单(dsh.bundle.patch)+ 浏览器半边声明(dsh.client)
├── cordis.patch.yml        # 装载行 + 全部配置项与默认值
├── index.js                # 插件入口(name / Config / apply)
├── lib/
│   ├── client.js           # 浏览器半边:设置页里的 dsh2server 标签(手写 lazy-CJS 产物)
│   ├── host-ui.js          # 网页控制台的宿主路由(/api/dsh2server/,走 Connection 鉴权)
│   ├── settings-store.js   # 配置分层与持久化(cordis.yml ← 网页设置)
│   ├── bridge.js           # 实例级状态:身份、事件序号、环形缓冲、方法表
│   ├── link.js             # 单端点连接:载体、订阅、心跳、退避(可多条并存)
│   ├── transport/          # WebSocket 与 HTTP 长轮询载体
│   ├── ops/                # 全部入站方法(含 session.events / attachment / permission / plugin)
│   ├── forward.js          # 宿主事件转发(含审批/提问应答)
│   ├── host.js             # 能力探测与宿主适配
│   ├── gate.js             # 暂停/恢复状态机
│   ├── identity.js         # 每机唯一 key
│   ├── protocol.js         # 帧与错误码
│   ├── config.js           # 配置校验(Standard Schema)
│   ├── buffer.js           # 事件环形缓冲与补发
│   └── util.js / log.js / version.js
├── scripts/show-key.js     # 打印本机 key
├── examples/               # 参考后端(Node,可运行)+ 极简 WebSocket 服务端
├── php/                    # 参考后端(PHP 单文件)+ 内置网页测试台
├── dsh-api/                # DSH 专用控制台 + 中转实现(复刻 dsh 本地界面)
├── docs/API.md             # ★ 服务器接口规范
└── test/                   # 单元 + 端到端 + 多端点/双协议 + PHP + 真实 Cordis 集成测试

开发与测试
bash
npm test                        # 全部测试(单元、扩展、端到端、多端点/双协议、PHP 后端、真实 Cordis 集成)
node --test test/unit.test.js
node --test test/extensions.test.js  # PLUGIN-EXT 的八个扩展:方法契约、能力位、事件载荷
node --test test/e2e.test.js
node --test test/multi-endpoint.test.js
node --test test/php-relay.test.js   # 找不到 PHP 时自动跳过(可用 PHP_BIN 指定)

测试覆盖:

- 单元:配置校验(含单端点/列表/逗号分隔)、协议帧、key 生成/持久化/轮换、环形缓冲补发窗口、暂停状态机、方法分发(含权限开关、目录白名单、能力降级)
- 端到端:真实 Bridge 对真实参考服务器 —— key 握手、远程操作、事件流式转发、订阅/退订、HTTP 长轮询回退、断线重连、多机器多 key 管理、key 轮换与吊销
- 多端点 / 双协议:一个中转同时提供 HTTP 与 HTTPS、同一台机器同时连多个端点并把状态镜像给所有服务器、订阅按服务器隔离、某个端点不可达不影响其它端点
- 网页控制台:用真实的 dsh Connection 服务装载插件并驱动 /api/dsh2server/——设置端点会真的把连接切到新服务器、非法写入被拒绝且不生效、重置回落到配置文件、轮换 key 会落盘;浏览器半边用带 Hook 调度器的最小 React 真实渲染并点击每个按钮
- PHP 后端:用真实的插件客户端跑通 php/dsh-relay.php —— 握手、管理接口请求/响应、下发命令、暂停闸门、事件转发、未知 key 的配对流程、内置测试台的 HTML 与前端脚本
- Cordis 集成:把插件真正装载进一个 Cordis Context,验证 Standard Schema 配置在加载期生效、apply/ctx.effect 生命周期与卸载清理(找不到本机 dsh 安装时自动跳过)

调试时把 logLevel 设为 debug,可以看到每次请求/响应的收发。

安全须知

1. 优先用 HTTPS/WSS。key 等同于这台机器的登录凭据。http:// 完整支持(内网/回环中转很常见),但会明文传输 key 与会话内容——插件启动时会告警,并在 connection.insecure 里持续标记,请自行确认网络可信。
2. 服务器只存 key。协议不要求存会话内容;一旦存储,泄露影响远大于 key 泄露。
3. 按需收紧插件权限:allowedCwdPrefixes 可以把远程操作限制在指定目录树内;allowRemotePrompt / allowRemoteCommand / allowRemoteControl 可以分级关闭。
4. 审批转发默认关闭。打开 forwardApprovals 等于"服务器上的人可以批准本机的工具调用",请确认服务器的可信边界后再开。
5. key 吊销是即时的:删除服务器上的 key,该机器下一次重连就会被拒绝。
6. 多端点 = 多个信任边界:同时连多个服务器时,任一服务器的持有者都拥有等同的远程操作能力。只配置你确实要授权的端点。

常见问题

Q:插件加载了但日志说 idle,怎么办?
没填 endpoint。填上服务器地址(或设置 DSH2SERVER_ENDPOINT)后重启 dsh。

Q:日志出现 "unknown instance key",怎么修?
你的机器还没被服务器登记(或 key 被删了)。运行 node scripts/show-key.js 取 key,登记到服务器;插件会自己重连,不必重启 dsh。
问:可以用 http 吗?公司内网没有证书。
可以。endpoint: 'http://10.0.0.5:8787/dsh-api' 直接就能用,插件只会在启动时告警一次。要更安全又不方便办证书,可以用 NODE_EXTRA_CA_CERTS 指定自建 CA,或直接去掉 TLS 而只在内网暴露。

问:能同时连多个服务器吗?比如内网一个、公网一个。
能。endpoint 写成列表即可,http 和 https 可以混用。每条连接互相独立:断线、重连、订阅、补发都是分开的;而 instanceId、key 和事件序号是共享的,所以两台服务器看到的是同一台机器的同一份状态。

问:key 文件丢了会怎样?
插件会生成新 key,服务器会把它当成一台新机器。用配置 key 显式固定 key(配合密钥管理系统)可以避免这种情况。

问:怎么确认只连我自己的服务器?
插件只主动连出到你配置的 endpoint 列表,不监听任何端口,也不会连接其它地址。用 instance.info 可以看到 endpoints(配置值)、connections(每条链路的实时状态)与 transport。

问:服务器需要开公网端口吗?
需要,但那只是服务器自己的入站端口;你的电脑不需要任何入站端口,也不需要内网穿透。

问:WebSocket 被公司代理拦了怎么办?
把 transport 固定为 'http',插件会走 HTTP 长轮询,功能完全一致(延迟略高)。

问:能同时管理多少台机器?
没有上限——服务器侧就是一张 key 表,每台机器是独立的一条连接(或同时多条,如果它配了多个端点)。

许可证

代码采用 MIT License,见 LICENSE。DeepSeek 与 DeepSeek Harness 名称和相关图标属于各自权利人,本项目是独立的兼容插件。

English

dsh2server 是一个 DeepSeek Harness 插件,用于将本地 DSH 主机连接到 A2S 中继。服务器可以观察实时状态、工作区、会话、工具、审批、问题、任务、目标和附件,并可以发送提示、中断、暂停/恢复、交互响应及其它协商操作。DSH 主动向外连接,因此工作站无需任何入站端口或隧道。

A2S 生态系统(兄弟仓库)

dsh2server 是 A2S 家族中的一个插件。该家族还提供通用控制台和移动客户端。它们都共享同一个 a2sk_ 设备 key 和同一套协议,因此你可以单独使用其中任意一个,也可以与本插件一起使用:

| 仓库 | 角色 | 说明 |
|---|---|---|
| server-api | 通用控制台(Web) | 统一服务器端——接受 Claude Code、Codex 和 DSH 连接,并提供多 Agent Web 控制台。实际部署请使用这个;仓库内的 dsh-api/ 仅用于单独运行 DSH |
| A2Switch | 通用控制台(桌面端)+ 插件配置路由 | Electron 桌面控制中心:一份共享的设备配置和 key、本地 Agent 检测、三个插件的一键安装/更新,以及桥接进程的启动/停止 |
| a2s_app | 移动客户端 | Flutter Android 客户端,用于在手机上查看机器状态和会话并发送远程操作 |
| cc2server | Claude Code 桥接 | 将 Claude Code 连接到 A2S 协议 |
| codex2server | Codex 桥接 | 基于官方 Codex app-server 构建的桥接 |
| dsh2server | 本仓库 | 此处描述的 DeepSeek Harness 插件 |
| dsh-api/(本仓库内) | 仅 DSH 控制台 | 旧版单 Agent 服务器加控制台,保留用于独立 DSH 部署和兼容性测试 |
三个桥接共享一个设备密钥,但使用不同的实例 ID(:claude / :codex / :dsh),因此服务器会将它们聚合为单个设备,同时仍允许你定位特定的 Agent。

推荐的 A2S 设置

1. 在 A2Switch 中配置并配对工作站。
2. 打开 A2Switch 的插件,选择全部安装/更新。A2Switch 会复制插件并自行运行 DSH 注册命令。
3. 重启所选的 Harness 配置文件一次,以便加载新注册的插件。

默认注册目标是 web;在启动 A2Switch 之前设置 A2S_DSH_PROFILE 以选择其他配置文件。等效的手动命令为:
powershell
dsh plugin --profile web add C:\path\to\dsh2server
dsh web

在统一模式下,插件会从共享的 A2Switch 配置中读取缺失的端点、设备密钥、设备/实例身份、传输和区域设置值。显式的 DSH 环境/配置文件设置仍具有更高优先级,因此现有的专用配置文件不会被静默覆盖。

独立设置

使用 dsh plugin --profile  add  安装本地目录、GitHub 源或 tarball。至少通过 bundle 补丁、配置文件设置或 DSH2SERVER_* 环境变量配置端点和实例/设备密钥。对于公共服务,请使用 HTTPS/WSS;当 WebSocket 升级被阻止时,插件可以通过 transport: http 强制使用 HTTP 长轮询。

配置与 UI

该 bundle 贡献了自己的 Harness 设置页面,用于配置端点、密钥、传输、语言、重连/心跳行为、审批转发、文件限制和可选能力。提供简体中文和英文;system 会跟随浏览器/主机区域设置。保存设置会重绘页面,并在需要时重新连接。

敏感密钥在 UI/日志输出中会被掩码处理。A2Switch 共享设置仅填充缺失的配置文件字段。环境变量和显式配置文件值优先,其次是共享配置和内置默认值。

能力

插件会发现正在运行的 Harness 主机,并仅通告受支持的操作。根据主机能力,统一目录包括:

- 实例信息、健康检查、ping、密钥轮换和运行时元数据;
- 会话列表/创建/历史/提示/中断/重命名/分叉/归档;
- 实时助手/工具/轨迹事件,支持重放和订阅;
- 工作区、受限文件、附件和反馈;
- 审批、结构化问题、任务、目标、模型、权限和 Agent 预设;
- 插件列表/配置/启用状态;
- 可选的远程终端,具有严格限制且默认禁用策略。

浏览器客户端 bundle 通过 DSH 插件扩展点注入,无需修改 Harness 核心文件。缺失的主机服务会产生诚实的缩减能力目录,而不是虚假的成功响应。

传输、存活检测与恢复
auto 优先使用 WebSocket,在可重试的网络/升级失败后回退到 HTTP 长轮询。帧在两种载体上共享协议 v1。心跳、带抖动的指数重连、多端点故障转移、单调事件序列、有界重放缓冲区以及运行时/控制记录,使 A2Switch 和服务器能够从瞬时丢失中恢复,而无需停止 Harness 会话。

A2Switch 只能通过 runtime/dsh-control.json 停止/启动/重启中继链路;它不会终止 Harness 主机。身份验证和协议错误是致命的,直到配置更改,从而避免无休止的重连风暴。

安全

设备密钥用于授权此工作站,不得提交。审批转发默认禁用,因为启用它允许远程服务器操作员批准本地工具调用。远程终端也默认禁用。文件访问保持在已批准的工作区内,负载和终端重放有界,密钥被排除在运行时记录和日志之外。

开发与测试
powershell
npm install
npm test

该测试套件涵盖包组合、配置优先级、共享 A2S 设置、传输与多端点故障转移、重放、运行时控制、主机 UI 注入、会话、工作区、附件、插件、终端行为、归档以及端到端协议流程。零依赖的 Node 参考中继位于 examples/;PHP 测试后端位于 php/;dsh-api/ 是仅 DSH 的控制台——第二个中继实现,同时提供一个镜像本地 dsh UI 的图形控制台,保留用于旧版单 Agent 独立部署和兼容性测试。

许可证

MIT。DeepSeek 和 DeepSeek Harness 名称及标识仍归其各自权利人所有。这是一个独立的兼容性插件。

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

💬 加入社群

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

DPharness QQ 群二维码,QQ 扫码进群
QQ 扫码进群
DPharness 飞书群二维码,飞书扫码进群
飞书扫码进群