用浏览器远程打开并操作桌面端 Harness 界面
29.8/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/11/2 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add yeerock/deepseek-harness-remote-control该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:仅索引
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 79 处,展示前 73 条)
- 读取本地文件apps/agent/src/dsh-session.ts:27
text = readFileSync(filePath, 'utf8'); - 读取本地文件apps/agent/src/dsh-session.ts:32
const mode = statSync(filePath).mode & 0o777; - 读取本地文件apps/agent/src/identity.ts:34
if (!existsSync(stateDir)) { - 写入本地文件apps/agent/src/identity.ts:35
mkdirSync(stateDir, { recursive: true, mode: 0o700 }); - 读取本地文件apps/agent/src/identity.ts:39
const mode = statSync(stateDir).mode & 0o777; - 写入本地文件apps/agent/src/identity.ts:41
chmodSync(stateDir, 0o700); - 读取本地文件apps/agent/src/identity.ts:90
text = readFileSync(path, 'utf8'); - 读取本地文件apps/agent/src/identity.ts:100
const mode = statSync(path).mode & 0o777; - 写入本地文件apps/agent/src/identity.ts:102
chmodSync(path, 0o600); - 写入本地文件apps/agent/src/identity.ts:118
renameSync(path, `${path}.rejected-${String(Date.now())}`); - 删除本地文件apps/agent/src/identity.ts:121
unlinkSync(path); - 写入本地文件apps/agent/src/identity.ts:133
writeFileSync(temporary, `${JSON.stringify(identity, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 }); - 写入本地文件apps/agent/src/identity.ts:134
chmodSync(temporary, 0o600); - 删除本地文件apps/agent/src/identity.ts:139
unlinkSync(temporary); - 读取本地文件apps/agent/src/lock.ts:78
text = readFileSync(path, 'utf8'); - 读取本地文件apps/agent/src/lock.ts:79
mtimeMs = statSync(path).mtimeMs; - 读取本地文件apps/agent/src/lock.ts:90
text = readFileSync(path, 'utf8'); - 写入本地文件apps/agent/src/lock.ts:115
mkdirSync(stateDir, { recursive: true, mode: 0o700 }); - 写入本地文件apps/agent/src/lock.ts:126
writeFileSync(descriptor, payload, { encoding: 'utf8' }); - 删除本地文件apps/agent/src/lock.ts:165
unlinkSync(path); - 读取本地文件apps/agent/src/lock.ts:194
text = readFileSync(path, 'utf8'); - 删除本地文件apps/agent/src/lock.ts:205
unlinkSync(path); - 写入本地文件apps/agent/src/status.ts:72
mkdirSync(directory, { recursive: true, mode: 0o700 }); - 写入本地文件apps/agent/src/status.ts:76
writeFileSync(temporary, `${JSON.stringify(status, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 }); - 写入本地文件apps/agent/src/status.ts:77
renameSync(temporary, filePath); - 删除本地文件apps/agent/src/status.ts:80
unlinkSync(temporary); - 写入本地文件apps/edge/src/db.ts:332
mkdirSync(dataDir, { recursive: true, mode: 0o700 }); - 写入本地文件apps/edge/src/db.ts:336
chmodSync(this.file, 0o600); - 删除本地文件packages/plugin/src/agent-supervisor.ts:285
await rm(this.statusFile, { force: true }).catch(() => undefined); - 执行外部命令来自配置/变量packages/plugin/src/agent-supervisor.ts:289
child = spawn(target.command, [...target.prefixArgs, ...this.buildArgs()], { - 执行外部命令固定命令packages/plugin/src/agent-supervisor.ts:351
const guard = spawn('/usr/bin/caffeinate', ['-i', '-w', String(child.pid)], { stdio: 'ignore', detached: true }); - 读取本地文件packages/plugin/src/agent-supervisor.ts:475
const info = statSync(this.statusFile); - 读取本地文件packages/plugin/src/agent-supervisor.ts:477
const text = await readFile(this.statusFile, 'utf8'); - 写入本地文件packages/plugin/src/agent-supervisor.ts:555
await mkdir(this.options.stateDir, { recursive: true, mode: 0o700 }); - 写入本地文件packages/plugin/src/agent-supervisor.ts:558
await writeFile(temporary, body, { mode: 0o600, encoding: 'utf8' }); - 写入本地文件packages/plugin/src/agent-supervisor.ts:559
await rename(temporary, this.upstreamFile); - 写入本地文件packages/plugin/src/agent-supervisor.ts:560
await chmod(this.upstreamFile, 0o600).catch(() => undefined); - 删除本地文件packages/plugin/src/agent-supervisor.ts:564
await rm(this.upstreamFile, { force: true }).catch(() => undefined); - 读取本地文件packages/plugin/src/agent-supervisor.ts:682
return statSync(path).isFile(); - 读取本地文件packages/plugin/src/agent-supervisor.ts:690
return statSync(path).isDirectory(); - 读取本地文件packages/plugin/src/config.ts:168
if (existsSync(join(modern, 'device.json'))) return modern; - 读取本地文件packages/plugin/src/config.ts:170
if (existsSync(join(legacy, 'device.json'))) return legacy; - 发起网络请求packages/plugin/src/host.ts:239
const response = await fetch(`${this.edgeUrl}/v1/edge`, { headers: { accept: 'application/json' } }); - 写入本地文件packages/plugin/src/host.ts:607
void mkdir(this.stateDir, { recursive: true, mode: 0o700 }) - 写入本地文件packages/plugin/src/host.ts:608
.then(() => writeFile(join(this.stateDir, 'plugin-error.json'), `${JSON.stringify({ at: Date.now(), message: this.setupError }, null, 2)}\n`, { mode: - 读取本地文件packages/plugin/src/host.ts:1266
const parsed = JSON.parse(readFileSync(join(stateDir, 'host.json'), 'utf8')) as { enabled?: unknown; name?: unknown; remoteFullAccess?: unknown }; - 写入本地文件packages/plugin/src/host.ts:1278
await mkdir(stateDir, { recursive: true, mode: 0o700 }); - 写入本地文件packages/plugin/src/host.ts:1279
await writeFile(join(stateDir, 'host.json'), `${JSON.stringify(settings)}\n`, { mode: 0o600 }); - 发起网络请求packages/plugin/src/remote-view.ts:76
const response = await fetch(`${edge.origin}/d/${encodeURIComponent(deviceId)}/dsh-rc/${encodeURIComponent(endpoint)}`, { - 发起网络请求packages/plugin/src/remote-view.ts:130
const response = await fetch(ticket, { redirect: 'manual' }); - 读取本地文件packages/plugin/src/sharing.ts:141
text = readFileSync(file, 'utf8'); - 读取本地文件packages/plugin/src/sharing.ts:150
const mode = statSync(file).mode & 0o777; - 发起网络请求packages/plugin/src/sharing.ts:238
const body = await this.request('GET', '/v1/devices/me/sharing'); - 发起网络请求packages/plugin/src/sharing.ts:244
const body = await this.request('POST', `/v1/devices/${encodeURIComponent(deviceId)}/rename`, { name }); - 发起网络请求packages/plugin/src/sharing.ts:251
const body = await this.request('POST', '/v1/devices/me/embed-ticket', { deviceId }); - 发起网络请求packages/plugin/src/sharing.ts:257
const body = await this.request('GET', '/v1/devices/me/peers'); - 发起网络请求packages/plugin/src/sharing.ts:280
const body = await this.request('PUT', '/v1/devices/me/sharing', { enabled }); - 发起网络请求packages/plugin/src/sharing.ts:303
const response = await this.request('POST', '/v1/devices/me/invites', body); - 读取本地文件packages/plugin/src/update.ts:83
if (!existsSync(join(repoRoot, '.git'))) return false; - 执行外部命令固定命令packages/plugin/src/update.ts:85
execFileSync('git', ['symbolic-ref', '-q', 'HEAD'], { cwd: repoRoot, stdio: 'ignore', windowsHide: true, timeout: 5_000 }); - 读取本地文件packages/plugin/src/update.ts:260
if (this.options.spawnInstaller === undefined && !existsSync(script)) { - 写入本地文件packages/plugin/src/update.ts:286
mkdirSync(this.stateDir, { recursive: true }); - 执行外部命令来自配置/变量packages/plugin/src/update.ts:289
const child = spawn(node.command, [script, '--tag', tag], { - 读取本地文件packages/plugin/src/update.ts:398
const lines = readFileSync(this.restartLogPath, 'utf8').split(/ ? /).map((line) => line.trim()).filter((line) => line !== ''); - 读取本地文件packages/plugin/src/update.ts:418
const value = JSON.parse(readFileSync(this.statusPath, 'utf8')) as unknown; - 读取本地文件packages/plugin/src/update.ts:427
const value = JSON.parse(readFileSync(this.restartsPath, 'utf8')) as { version?: unknown; attempts?: unknown }; - 写入本地文件packages/plugin/src/update.ts:436
mkdirSync(this.stateDir, { recursive: true }); - 写入本地文件packages/plugin/src/update.ts:437
writeFileSync(this.restartsPath, `${JSON.stringify({ version: latest, attempts: this.restartAttempts(latest) + 1, at: Date.now() })}\n`, { mode: 0o600 - 执行外部命令来自配置/变量packages/plugin/src/update.ts:468
const version = execFileSync(candidate, ['-p', 'process.versions.node'], { encoding: 'utf8', timeout: 5_000, windowsHide: true, env }).trim(); - 执行外部命令固定命令packages/plugin/src/update.ts:485
spawn('/bin/sh', ['-c', `exec >>${log} 2>&1; echo "restart requested $(date)"; sleep 3; osascript -e "tell application \\"DeepSeek Harness\\" to quit" - 写入本地文件packages/plugin/src/update.ts:493
writeFileSync(script, windowsRestartScript(logFile, fallbackExe), { encoding: 'utf8', mode: 0o600 }); - 执行外部命令固定命令packages/plugin/src/update.ts:498
spawn('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', launch], { detached: true, stdio: 'ignore', windowsHide: true }).unre - 执行外部命令固定命令packages/plugin/src/update.ts:513
spawn('/bin/sh', ['-c', `exec >>${log} 2>&1; echo "restart requested $(date)"; sleep 3; p=$(${pids}); [ -n "$p" ] && kill -TERM $p; i=0; while [ -n "$
扫描于 2026/11/4(0 天前) · 范围 全包扫描 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
DeepSeek Harness 远程控制:仅靠提示词完成 Harness 配对,原生远程窗口,自托管边缘节点
README
由 DeepSeek 最新模型翻译生成DeepSeek Harness 远程控制(DSH RC)
一条 TLS WebSocket,把你正在用的浏览器变成 DeepSeek Harness 桌面版的远程窗口。
创作者:yeerock
DSH RC 通过一条由桌面端主动外连的长连接,把未经修改的 DeepSeek Harness Web UI 提供到任意浏览器:桌面里的 agent 主动连到 edge,edge 把 HTTP 与 WebSocket 流转发给它,浏览器打开的就是真正的 DSH 界面。没有 WebRTC,没有 UDP,不需要公网入站端口,不需要端口映射,也不用在面前的设备上再装一个客户端。
通过 DSH RC edge 打开的、未经修改的 DeepSeek Harness 界面
这张截图不是效果图:它是真实的 Harness 0.2.1 会话,由 tools/e2e/real-dsh.mjs 启动真实的 dsh --profile web、 edge、agent,完成 owner 初始化与配对后,用无头 Chromium 从 http://<edge>/d/<device>/ 加载出来的。
状态:v0.4.1。 协议、多路复用器、edge、agent、插件、测试与文档均已实现。
展开完整文档(README 剩余 1 节)
DeepSeek Harness 远程控制(DSH RC)
一条 TLS WebSocket,把你正在用的浏览器变成 DeepSeek Harness 桌面版的远程窗口。
创作者:yeerock
DSH RC 通过一条由桌面端主动外连的长连接,把未经修改的 DeepSeek Harness Web UI 提供到任意浏览器:桌面里的 agent 主动连到 edge,edge 把 HTTP 与 WebSocket 流转发给它,浏览器打开的就是真正的 DSH 界面。没有 WebRTC,没有 UDP,不需要公网入站端口,不需要端口映射,也不用在面前的设备上再装一个客户端。
通过 DSH RC edge 打开的、未经修改的 DeepSeek Harness 界面
这张截图不是效果图:它是真实的 Harness 0.2.1 会话,由 tools/e2e/real-dsh.mjs 启动真实的 dsh --profile web、 edge、agent,完成 owner 初始化与配对后,用无头 Chromium 从 http://<edge>/d/<device>/ 加载出来的。
状态:v0.4.1。 协议、多路复用器、edge、agent、插件、测试与文档均已实现。
321 个自动化测试全绿;新电脑只需一条命令即可安装并连接,连接断开(休眠唤醒、网络切换、edge 重置)后自动恢复,每台电脑自动检查、安装并在空闲时重启以保持最新版本,真实 DSH 端到端验证通过,下面的基准数字是实测值。
仍是预发布软件:请把插件当 beta,并在暴露桌面之前阅读 THREAT-MODEL.md。
两步开始
两个步骤都支持 macOS、Windows 和 Linux。每台电脑先安装并打开一次 DeepSeek Harness,然后把提示粘贴给 DeepSeek Harness 的 agent,下载、安装、设置、连接、重启全部自动完成;也可以自己在终端运行对应系统的一条命令。
第 1 步,第一台电脑(主机,任何系统),粘贴给 DeepSeek Harness:
Set up this computer as my DSH RC host, by yourself. Run the command for this system:
Windows (PowerShell): $env:DSHRC_MODE='host'; $env:DSHRC_TAG='v0.4.1'; irm https://raw.githubusercontent.com/yeerock/deepseek-harness-remote-control/v0.4.1/scripts/install.ps1 | iex
macOS or Linux: curl -fsSL https://raw.githubusercontent.com/yeerock/deepseek-harness-remote-control/v0.4.1/scripts/install.sh | sh -s -- host v0.4.1
If it prints ACTION NEEDED with a link, show me the link and keep waiting; it continues after I approve.
If it fails, fix the cause and run it again until it prints DONE. Never print or share the owner password.
只需批准一次 Tailscale 登录链接(以及 Funnel,如有提示);Windows 上安装 Tailscale 时可能还会出现一次系统授权提示。主人密码保存在 ~/.dsh-rc/owner-password.txt(仅你可读)。
第 2 步,其他每台电脑(Mac、Windows、Linux):在主机上打开 Remote Control,点击 Build Setup Prompt,把生成的提示粘贴到那台电脑的 DeepSeek Harness 中。约一分钟后它会出现在 Remote Workspaces,之后自动保持连接并自动更新。
更新时,Remote Workspaces 标题右侧显示与 DeepSeek Harness 自身相同的更新按钮:更新、安装进度百分比、重启以更新、正在准备重启、重试更新。
在远程电脑上新建会话时,点击 + 打开“新建远程会话”:和本地新建会话一样,可以选择项目文件夹(包括还没有会话的文件夹)、模型和推理强度以及权限(只读、工作区可写、完全访问,以及那台电脑提供的自动审查),默认使用那台电脑自己的设置;开启完全访问前会先确认。打开的远程会话中,输入框下方有与本地相同的两个控件:左侧是权限,右侧是模型和推理强度,修改会作用于该会话的下一条消息。
上一代远程插件的问题
参考实现(liguobao/ds-harness-remote)是一个认真的项目,它也做到了本 v1 还没做的部分:Android 与 VS Code 客户端,以及针对中继的应用层端到端加密。它的问题在于拓扑与表面面积,这正是中国境外用户感受到的"先卡住,然后掉线"。
| # | 上一代的做法 | 为什么境外用户受伤 |
| 1 | 优先尝试 WebRTC,ICE 要等 8 秒(DEFAULT_NEGOTIATE_TIMEOUT_MS)才放弃,控制握手最长 15 秒 | 在封 UDP 的网络上,每次连接都先卡几秒,然后才开始回退 |
| 2 | STUN/TURN 依赖 UDP;TURN 还要多一跳中继和带宽成本 | 酒店、企业、校园与 CGNAT 网络会丢弃 UDP;回退更慢,而且仍然失败 |
| 3 | 文档/默认服务器是 dsh.r2049.cn,托管在中国大陆 | 境外用户的每个会话都要跨一条很长、丢包严重的链路;仅 RTT 就足以让输入感觉卡死 |
| 4 | Android App + VS Code 客户端 + client-core 各自重新实现 Harness RPC | 两套协议面要同步;投影一卡,用户看到的就是"应用冻住",桌面功能也滞后 |
| 5 | 重连时拆掉连接,重新走 membership、固定密钥信任、选路与 Noise | 一次 Wi-Fi 抖动就丢几秒,并丢失在途 UI 状态 |
| 6 | Node 侧 WebRTC 是可选原生依赖(@roamhq/wrtc) | 安装/ABI 的脆弱性只在部分机器上出现,然后静默降级到中继 |
单独看,这些都不是 bug;合在一起,就解释了用户的抱怨:系统先用几秒钟尝试一条通常走不通的路,再经过一台很远的中继,抖动之后又从零开始。
Remote Control 如何解决
Remote Control 反过来下注:一种可靠传输、真实 UI、把 edge 放在离自己近的地方。
| 上一代的问题 | Remote Control 的做法 | 代码位置 |
| 8 秒 ICE 尝试、15 秒握手 | 只建立一条 443 出站 WSS;没有 ICE/STUN/TURN 需要协商或等待超时 | packages/mux、apps/agent |
| 依赖 UDP 的路径 | 只用 TCP/TLS;可穿过企业代理、TLS 中间人、CGNAT 与纯 IPv6 网络 | ARCHITECTURE.md |
| 默认使用中国大陆中继 | 自托管 edge;中国 ↔ 境外可放在香港、新加坡、东京或首尔--它就是唯一一跳 | DEPLOY.md §0 |
| 第二个客户端会漂移 | 浏览器直接加载 /d/<device>/ 下未经修改的 DSH Web UI,功能对齐是默认结果 | ARCHITECTURE.md §2 |
| 重连慢、丢状态 | 15 秒心跳、45 秒空闲上限、250 ms-20 s full-jitter、30 秒 presence 宽限;实测被 4004 强制替换后 49 ms 恢复;不重放 mutation | packages/mux、apps/agent |
| 批量传输拖垮交互 | 每流 256 KiB、每隧道 8 MiB 信用,按消费归还;high/normal/low 调度;4 × 16 MiB 下交互 p95 3.1 ms | packages/mux |
| 原生 wrtc 的脆弱性 | 没有原生插件;只用 Node 内置模块与 ws | package.json |
| 崩溃后留下两个 agent | 每个 stateDir 一把 agent.lock,拒绝第二个实例;4004 替换按可重试处理,而不是互相抢 | apps/agent/src/lock.ts |
安全与运维的形态也随之改变:DSH 启动 token 不离开桌面(0600 交接、只在内存中的 cookie),浏览器永远拿不到 DSH 凭证,设备密钥只存 SHA-256,配对码一次性且必须由 owner 配合,edge 可以完全自托管。代价仍然明确:edge 终止 TLS,因此能看到代理流量的明文。它是受信任的中转;THREAT-MODEL.md 写清了这份信任的收益与成本。如果你需要"连中继都无法解密"的端到端加密,这个 v1 不适合你--ADR-0001 解释了为什么仍然这样选。
架构
┌─────────────┐ HTTPS + WSS (443) ┌──────────────┐ one multiplexed WSS ┌───────────┐ loopback HTTP/WS ┌─────────────┐
│ browser │ ────────────────────▶ │ edge │ ◀────────────────────── │ agent │ ───────────────────▶ │ DSH desktop │
│ (anywhere) │ ◀──────────────────── │ (self-host) │ ──────────────────────▶ │ (desktop) │ ◀─────────────────── │ web server │
└─────────────┘ └──────────────┘ └───────────┘ └─────────────┘
auth · pairing mints the DSH unchanged presence · audit cookie, proxies
- 浏览器 → edge: 普通的 443 端口 HTTPS/WSS。挂在 /d/<deviceId>/ 子路径下,应用自身的相对 URL 与 完成其余工作。
- edge → agent: 每台设备一条 WebSocket,多路复用成相互独立的 HTTP 与 WebSocket 流,带每流流控和 high/normal/low 加权调度,批量传输抢不走交互流量。
- agent → DSH: 只连 127.0.0.1:<port>,使用由 DSH 启动 token 换来的 cookie。
token 和 cookie 都不离开桌面。
细节见 ARCHITECTURE.md、PROTOCOL.md、 ADR-0001。
环境要求
| 组件 | 要求 |
| 桌面端 | DeepSeek Harness 0.2.1-rc.3;插件声明 >=0.2.1-rc.3 <0.3.0 |
| Edge | Node.js ≥ 24.0(或 Docker 镜像),生产环境需要一个域名做 TLS |
| 浏览器 | 任意较新的 Chromium、Firefox 或 Safari |
| 网络 | 桌面端能出站访问 443;不需要入站端口,不需要 UDP |
快速开始
1. Edge
在一台小 VPS(或本机做演示)上:
git clone https://github.com/yeerock/deepseek-harness-remote-control && cd deepseek-harness-remote-control
npm ci && npm run build
node apps/edge/dist/main.js \
--host 0.0.0.0 --port 8787 \
--data-dir ~/.dsh-rc --public-url https://link.example.com
启动时会打印一次性 setup code(在完成初始化前每次启动都会轮换)。
用仓库自带的 Caddy + Docker 配置 在前面挡 TLS,或者在负载均衡处终止 TLS 并以 --trust-proxy 运行。
2. Agent(也可以交给插件)
node apps/agent/dist/main.js \
--edge https://link.example.com \
--dsh-url-file ~/.dsh-rc/upstream.json \
--state-dir ~/.dsh-rc/agent \
--name my-desktop \
--status-file ~/.dsh-rc/agent/status.json
upstream.json 的内容是 { "url": "http://127.0.0.1:<port>/?token=…" },权限 0600。token 故意不接受放在命令行参数里。首次运行会打印 DSHRC_PAIR_URL=…;以 owner 身份登录后打开它,即可批准设备并配对浏览器。
之后 DSH 界面就在 https://link.example.com/d/<deviceId>/。
3. 桌面插件(推荐)
插件从运行中的 DSH 桌面取得已认证 URL,写入 0600 文件,启动并监管 agent。
Remote Workspaces 行右侧的齿轮打开 Remote Control 弹窗:版本、两列状态、 Start/Restart/Stop 与 setup prompt。配对只使用一段可复制的提示词,不再有 pair URL、邀请链接或二维码。
点击 Remote Workspaces 中的主机,会在 DSH 的原生 Electron 子窗口中打开完整远程界面(会话、聊天、文件、终端),通过一次性 embed ticket 自动认证,不会出现 edge 登录页。真正的“侧边栏原生会话”设计见 ADR-0002。
当前英文 README 是事实来源;本文件保留为中文说明。
dsh plugin --profile <profile> add link:"$PWD/packages/plugin"
在 profile 的用户层 $DSH_HOME/profiles/<profile>/cordis.patch.yml 中启用:
- id: dsh-rc
config:
enabled: true
edgeUrl: https://link.example.com
deviceName: my-desktop
autoStart: true
然后以 dsh --profile <profile> --port 0 --no-open 启动; --profile <name> 已替代 dsh web 简写,不要再追加 web。
安装、配置与排错见 packages/plugin/README.md;部署形态、参数与 systemd 见 DEPLOY.md。
安全摘要
- DSH 启动 token 不离开桌面。 agent 用它一次性换成本地回环 cookie,只放在内存里;浏览器永远拿不到 DSH 凭证。
- 设备密钥是 32 字节随机数,edge 只保存 SHA-256,隧道上用 Bearer token 发送。
- 配对码是 8 字符、一次性、10 分钟有效,且必须由 owner 会话或 owner 密码配合才能兑换;它不是独立的 bearer 凭证。owner 会话 cookie 为 HttpOnly、 Secure、SameSite=Lax,30 天。
- edge 在终止 TLS 后能看到明文。 这是设计,不是缺陷,见 THREAT-MODEL.md 第 7.1 节。
- 即使 edge 被攻破,agent 也拒绝非回环上游、绝对 URL、.. 以及编码后的路径穿越。
- 没有 WebRTC、没有 UDP、没有入站端口,也不做应用层 mutation 重放。
漏洞报告流程见 SECURITY.md。
Edge 仪表盘:设备、在线状态与审计
验证
npm run typecheck # 全 workspace 的 tsc -b
npm test # 168 个测试:单元 + 集成
npm run bench:quick # 基准测试,JSON + markdown 写入 tools/bench/results
npm run e2e # 启动真实 DSH web、edge、agent,再用 Chromium 验证
npm run e2e 就是上面截图的来源:独立 DSH_HOME、真实 dsh --profile web、真实 edge、真实 agent,完成 owner 初始化、配对、取回 index、升级 /api/remote.mux,最后用 Chromium 渲染。它会断言代理出的 HTML 是 Harness 外壳、不含回环 origin,并且 UI 真的渲染出来。
实测环境:Apple M4 Pro(24 GiB,macOS 26,Node 24.18.0,2026-10-28), edge 在本机回环--这些数字衡量隧道开销,不含公网 RTT:
| 指标 | 数值 |
| 经 edge 的 TTFB,中位数 / p95 | 2.1 ms / 2.9 ms |
| 吞吐,1 MiB / 16 MiB | 157 / 299 MiB/s |
| 200 个串行请求 | 505 req/s(p95 2.1 ms) |
| 50 个并发请求 | 总计 16.6 ms(p95 约 19 ms) |
| 4 × 16 MiB 批量传输期间的交互 p95 | 3.1 ms |
| agent 重启后的隧道建立 | 2.7 ms |
| 被 4004 强制替换后的重连 | 49 ms |
真实 DSH 冒烟:index 37,907 B 字节一致,主 JS bundle 941,436 B 经隧道后 SHA-256 一致,remote.mux 升级成功。
明确不做的事
- 没有手机 App。 浏览器就是客户端。
- v1 没有 WebRTC 数据通道。 取舍与重启条件见 ADR-0001。
- 不在结果未知时重放 Harness mutation。
- 不做第二套聊天/文件/终端 UI。 这正是重点。
- 不是多租户 SaaS。 先做好自托管;托管账号与 OAuth 在路线图上。
仓库结构
| 路径 | 内容 |
| packages/protocol | 线格式、帧校验、路径与请求头策略 |
| packages/mux | 流、流控、优先级调度、心跳 |
| packages/shared | WebSocket 适配、令牌、Cookie、日志 |
| apps/edge | 公网 HTTPS/WSS 中转、认证、配对、SQLite、仪表盘 |
| apps/agent | 桌面隧道客户端与回环代理 |
| packages/plugin | DeepSeek Harness 插件(宿主 + 侧边栏 UI) |
| tests、tools/bench | 集成、故障注入、基准 |
| docs | 架构、协议、ADR、威胁模型、部署、运维手册 |
常见问题
DSH token 会离开本机吗? 不会。它在进程内被换成本地回环 cookie,浏览器看不到,也不会写入磁盘或命令行参数。
为什么不用 P2P? 因为用户报告的正是 P2P 无法工作的网络;而且浏览器无法在没有第二个客户端的情况下用 WebRTC 访问一个 HTTP origin。完整论证见 ADR-0001。
网络抖动时会怎样? 15 秒心跳、45 秒空闲上限、250 ms 到 20 s 的 full-jitter 重连、30 秒 presence 宽限,且不重放消息。桌面崩溃也不会留下两个 agent 抢同一条隧道:每个 stateDir 有单实例锁。
可以自托管 edge 吗? 这是主要部署方式。一个 Docker Compose 文件、Caddy 负责 TLS、一个 SQLite 文件负责备份。
更多:FAQ.md · OPERATIONS.md。
创作者
yeerock 是 DSH RC / Remote Control 的创作者与维护者。
- GitHub:@yeerock
- 项目:github.com/yeerock/deepseek-harness-remote-control
许可证
MIT,见 LICENSE。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
yeerock/deepseek-harness-remote-control
用浏览器远程打开并操作桌面端 Harness 界面
相关插件 · 工具 / 效率
破甲管控台kingcharleslzy-ai/dsh-purge
dsh-purge Community · DeepSeek Harness 社区维护版:宿主兼容、状态诊断、提示词管理、更新与本机备份回滚。基于小杨 / YuJunZhiXue 的 dsh-purge。
★ 4,330已汉化DeepSeek 客户端
74.5
网文长卷lingfengQAQ/webnovel-writer
基于 Claude Code 的长篇网文辅助创作系统,解决 AI 写作中的「遗忘」和「幻觉」问题,支持 200…
★ 7,826已汉化DSH
72.2
花束设计alchaincyf/huashu-design
一句话生成网页、原型、PPT 与信息图等可交付设计
★ 28,638已汉化DSH
71.7
多智能体协作HKUDS/AutoSwarm
AutoSwarm: Unify Your Agents. Unify Your Context (Codex · Claude Code · DeepSeek Harness · Muse · Grok · WorkBuddy· nanobot · and more)
★ 2,349已汉化DSH
71.6
Vox 拼贴视频生成Alisa0808/vox-director
🎬 Vox Director拼贴动效导演
★ 2,129已汉化DSH
71.4
Agent 演化引擎Q00/ouroboros
智能体 OS:智能体自己变聪明,我们只守住底线:面试准入、分阶段评估、带预算的进化循环。MCP 服务器,14 种运行时:Claude Code、Codex CLI、Gemini CLI、OpenCode、Copilot、Kiro 等。
★ 6,311已汉化DSH
71.4
这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。