dsh-remote-access

DSH PLUGIN浏览器与联网

仓库 zhexuany/dsh-remote-access

浏览器与联网 L5 · 运行验证通过 安全审查:低风险 可信度:金 可安装插件
安装这个插件

DSH plugin 通过 Cloudflare 或 SSH 隧道把本机 Web GUI 映射到 HTTPS 域名,生成带令牌的分享链接,让另一台机器直接接管同一批会话与工作区。

一句话选型结论

按资料字段整理

dsh-remote-access 是浏览器与联网类的 DSH 可安装插件,验证等级 L5 · 运行验证通过,安全审查为低风险,列出 5 项功能,有可直接复制的安装命令。验证通过不等于安全:插件会读写文件、执行命令或联网,装前先到仓库确认它做了什么;涉及密钥或系统权限的,建议先在隔离环境试装。

分类浏览器与联网 收录类型可安装插件 验证等级L5 · 运行验证通过 安全审查低风险
验证等级 L5 L5 · 运行验证通过
GitHub 星标 0 复刻 0 · 未关闭 issue 未标注
最近更新 2026-11-08 创建于 2026-11-05
README 34.4 KB 正文体量,越大说明说明书写得越细

安装

可安装插件
在 DSH 里执行,装完用 dsh plugin list 确认已挂上;标了 L4 / L5 的命令已在沙盒里真实跑通
dsh plugin --profile web add github:zhexuany/dsh-remote-access
  1. 1
    确认版本与运行环境

    兼容的 DSH 版本为 0.2.2-rc.1;版本不匹配时安装会报错。

  2. 2
    执行安装命令

    在 DSH 里执行上面的命令;多包仓库要注意区分具体子包名,别装错包。

  3. 3
    确认已挂载

    用 dsh plugin list 查看列表里是否出现这个插件,出现即挂载成功。

  4. 4
    试跑并检查权限

    加载后跑一个最小用例,确认它能正常工作;同时看它申请了哪些文件、命令或网络权限。

兼容 DSH 版本0.2.2-rc.1版本不匹配时安装会报错
Node.js 要求未标注来自仓库 package.json 的 engines 字段
前置依赖无需要同时安装的同级依赖
npm 包名未发布到 npm可用 npm view 查看包信息

能做什么

5 项功能
字段来自项目仓库与公开索引,中文说明由本站标注

dsh-remote-access:用安全隧道把 DeepSeek Harness 的 Web GUI 带到另一台机器

  • Cloudflare 快速/命名隧道
  • SSH 反向隧道与自定义命令
  • 带令牌分享链接
  • 自动维护信任围栏
  • 设置面板启停隧道

适用场景

终端命令联网搜索

上游标签

cloudflare-tunneldeepseek-harnessdshdsh-pluginremote-access

验证等级与安全审查

L5 · 运行验证通过
自动化流程结论,不是人工审核
验证等级L5 · 运行验证通过在 dsh 沙盒中完成安装与运行冒烟——装得上、跑得起来
安全审查低风险静态分析结论,未标注不代表已通过审查
仓库健康度活跃按仓库活跃度与最近更新时间给出
可信度金综合仓库活跃度与验证结果

上面的结论来自上游最近一次自动化验证(2026-11-10): 同一套检查会在插件发新版后重跑,页面显示的是最新一次的逐项结果。

逐项检查结果

L1 · 已找到 ✓ 通过
  • L1.0_DEFAULTED_FROM_L3 由 L3 推定 默认通过——L3 安装规范已通过(全库 L3 扫描策略)
L2 · 结构化 ✓ 通过
  • L2.0_DEFAULTED_FROM_L3 由 L3 推定 默认通过——L3 安装规范已通过(全库 L3 扫描策略)
L3 · 安装规范 ✓ 通过
  • L3.1_INSTALL_COMMAND_PARSED 安装命令已解析 方式:安装规范解析
  • L3.2_DSH_VERSION_DECLARED DSH 版本已声明 方式:DSH 版本声明
L4 · 安装已测 ✓ 通过
  • L4.1_INSTALL_EXECUTED_CONFINED 受限沙盒内执行安装 方式:沙盒安装执行
  • L4.2_INSTALL_SUCCESS 沙盒安装成功 方式:沙盒安装执行
  • L4.3_ARTIFACT_CONFIRMED 安装产物已确认 方式:沙盒安装执行
L5 · 运行已测 ✓ 通过
  • L5.1_INSTALL_PASSED_SANDBOXED 沙盒安装通过 方式:沙盒安装执行
  • L5.2_WEB_BOOT_READY Web 启动就绪 方式:Web 启动
  • L5.3_HTTP_SERVED HTTP 服务响应 方式:HTTP 请求
  • L5.4_PLUGIN_INVENTORY_ACTIVE 插件清单中已激活 方式:插件清单查询

风险详情

  • 结论说明 静态扫描 低;动态审计无结论(以下以静态为准)→ 总评级 低。
  • 风险计数 严重 0 / 高 0

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 验证等级 L5:安装和运行都在沙盒里跑通过,装完能直接用的概率高。
  • 静态安全审查为低风险,没有发现明显可疑行为。
  • 有可直接复制的安装命令,不用自己翻文档找入口。
  • 标注了兼容的 DSH 版本(0.2.2-rc.1),按版本装不容易报错。
  • 列出了 5 项功能,能直接看出它解决什么问题。

先想清楚的地方

  • 没有声明 Node.js 版本要求,版本不匹配可能装不上。
  • 验证通过不等于安全:插件会读写文件、执行命令或联网,装前先到仓库确认它做了什么。

更适合

  • 需要在浏览器与联网场景里给 DSH 补能力的人。
  • 想装完就能用、不想自己排依赖的人。
  • 能接受在本地装依赖、愿意自己确认权限的人。

不太适合

  • 只使用 DSH 官方内置能力、不接受第三方插件的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
插件验证等级安全审查星标兼容版本安装命令
dsh-remote-access当前 L5 · 运行验证通过 低风险 0 0.2.2-rc.1 有
dsh-our-free-model L3 · 安装规范通过 低风险 8,796 未标注 有
dsh-web L3 · 安装规范通过 低风险 8,549 未标注 无
BrowserSkill L4 · 沙盒安装通过 低风险 8,456 未标注 有
dsh-pocket L5 · 运行验证通过 低风险 1,656 0.2.0-rc.2 等 12 个 有
Browser4 L4 · 沙盒安装通过 中风险 1,152 未标注 有

仓库档案

浏览器与联网
字段来自项目仓库与公开索引,中文说明由本站标注
作者 / 维护方zhexuany仓库 dsh-remote-access · 默认分支 未标注
分类浏览器与联网network
收录类型可安装插件声明了 dsh.bundle、可用 dsh plugin add 安装的条目
开发语言JavaScript仓库主语言
许可证Apache-2.0商用或二次分发前请确认条款原文
星标 / 复刻 / issue0 / 0 / 未标注收录时的数值,非实时
创建 / 更新2026-11-05 · 2026-11-08最后推送 未标注
本站收录2026-11-05首次进入本站目录的时间

README 全文

34.4 KB
作者原文,中文版优先;只做安全转义,不改内容

dsh-remote-access


把 DSH Harness Desktop 的 Web GUI 发布给另一台机器的浏览器。

插件在本机启动一条隧道,把 Desktop 的 Web GUI(同一批会话、同一个工作区、同一套工具)映射到一个 HTTPS
域名,并交给你一条带令牌的链接——在另一台机器上打开即可操作,不需要公网 IP、端口转发或路由器配置。

> ⚠️ 分享链接等同于对 Host 所在机器的控制权。 链接里带着本进程的 launch token,谁拿到链接,谁就能执行
> 命令、读写文件、读取会话。请把它当作密码:不要贴到群聊、不要提交进 git,不用时停止隧道。

  • 平台:macOS 为主要验证平台(Windows 也可用,见 §5.1);需要 DSH Harness Desktop 0.2.1-rc.2 及以上(macOS 默认安装在 /Applications/DeepSeek Harness.app)
  • 通道:Cloudflare 快速隧道(默认)、Cloudflare 命名隧道、SSH 反向隧道、自定义命令;也可以完全不开隧道,只用 ssh -L
  • 许可:Apache-2.0(见 LICENSE 与 NOTICE);问题反馈走 Issues

目录

  • 1. 它解决什么问题
  • 2. 架构
  • 3. 工作原理
  • 4. 安全模型
  • 5. 安装
  • 6. 使用
  • 7. 配置
  • 8. 连接稳定性
  • 9. 会话切换速度
  • 10. 远端选择工作区目录
  • 11. 运行时文件
  • 12. 排错
  • 13. 已知限制
  • 14. 开发与测试
  • 15. 许可证

1. 它解决什么问题

DSH Harness Desktop 是一个本机应用:Host 进程在回环地址上提供 Web GUI(127.0.0.1:),平时只服务
它自己的窗口。想在另一台设备上接手同一批任务——换台机器继续、用平板看进度、给同事演示——有两道障碍:

  1. 可达性:回环地址不对外,跨网络访问需要隧道或端口转发。
  2. 授权:Host 用 connection.trustedHosts 围栏校验 Host 头,并要求请求携带本进程的 launch token;
    域名不在围栏里直接 403,没有令牌则是 401。

这个插件把两件事一起做掉:隧道就绪时把域名发布进信任围栏(停止时收回),并生成一条带令牌的分享链接。

它不做什么:不转发到其他机器(目标始终是本机 Host),不是 VPN,不修改系统或路由器配置。

2. 架构

┌──────────────┐  HTTPS   ┌──────────────────┐  隧道   ┌─────────────┐  http  ┌─────────────────────┐
│ 另一台机器的  │ ───────▶ │ Cloudflare 边缘   │ ──────▶ │ cloudflared │ ─────▶ │ 127.0.0.1:    │
│ 浏览器       │          │ (或 ssh -R 通道)│         │ (本机子进程)│        │ DSH Host 进程       │
└──────────────┘          └──────────────────┘         └─────────────┘        └─────────────────────┘
文件 职责
index.js bundle 入口:注册 /remote-access HTTP API、控制套接字与设置面板
lib/tunnel.js 通道抽象:启动/停止/重启 cloudflared、ssh、自定义命令,解析公网地址,失败退避
lib/fence.js 把隧道域名写进 / 移出 connection.trustedHosts 围栏(只改内存,不落盘)
lib/service.js 状态机:启动流程、访问探测、分享链接、state.json / link.txt、日志环
lib/http.js 插件 API(/remote-access/*)与供 CLI 使用的控制套接字
client.js 设置 → 远程协作 面板
bin/ dsh-remote-access CLI 包装与 install 本地安装器
cordis.patch.yml bundle 声明与默认配置

3. 工作原理

3.1 隧道通道

provider 行为 前提
cloudflare-quick(默认) cloudflared tunnel --url http://127.0.0.1:,免登录、免费、随机 *.trycloudflare.com 域名,每次重启都会变 出站 443;UDP 被屏蔽时用 protocol: http2
cloudflare-named 自己的 Cloudflare 账号 + tunnel token,域名固定 配置 hostname 与 tunnelToken
ssh ssh -R 反向隧道转发到一台可访问的跳板机 配置 sshDestination / sshRemotePort
command 任意自定义命令,用 urlPattern 从输出里提取公网地址 自行保证命令可复现

两台机器网络互通时也可以完全不开隧道,只用 ssh -L(见 §6.3)。

3.2 一次启动的生命周期

  1. 等 Desktop 的 webserver 报告监听端口(portWaitMs),得到 target = http://127.0.0.1:。
  2. 启动通道子进程,等待公网地址出现(startTimeoutMs,默认 60s;quick 隧道从 cloudflared 日志中解析域名)。
  3. 把该域名发布进信任围栏(connection.trustedHosts,仅内存),同时刷新 Web 运行时围栏。
  4. 生成分享链接(connection.authenticatedUrl() 附加本进程的 launch token),写入 link.txt 与 state.json。
  5. 做一次访问探测(§6.4),结果记录在 access 里。

停止是逆过程:先收回围栏、删除 link.txt,再停子进程。子进程异常退出时按 1s→2s→…→30s 退避重启,
最多 maxRestarts 次。

3.3 浏览器如何拿到会话

分享链接形如 https://<隧道域名>/?token=:

  1. 浏览器打开链接,Desktop 校验 token 参数后 303 到 /,并 Set-Cookie:签名 cookie、HttpOnly、
    SameSite=Strict、有效期 30 天;
  2. 之后的请求靠这个 cookie 认证,域名在围栏内所以 Host 检查通过;
  3. cookie 的签名与隧道域名绑定——域名一变旧 cookie 即失效,必须打开新链接;
  4. launch token 由 Host 进程启动时生成、只在本进程内有效;Desktop 重启后旧链接全部失效。

4. 安全模型

隧道域名本身不是凭证。进入 GUI 需要同时满足两层:

4.1 信任围栏:connection.trustedHosts

Host 只接受 Host 头在信任列表里的请求,否则返回 403。Desktop 启动时会重写自己的运行时补丁
(其中只有回环与局域网地址),所以插件在隧道就绪时把域名推进内存里的围栏,停止时移除,不修改
Desktop 的任何配置文件。

这一层同时保护 DSH 自带的文件读取面:Session 文件预览器、/api/file、侧边栏的 /sidebar/file 与
/sidebar/html 走同一道围栏,不会因为开了隧道就变成公网可读的静态目录。

4.2 令牌 → Cookie

链接里的 launch token 只有本进程知道,GET /?token=… 是用它兑换 cookie 的唯一入口;cookie 由 Desktop
签名并绑定域名。没有链接就换不到 cookie,只知道域名进不来。

4.3 边界

  • 插件不提供任何绕过上两层的开关。能让浏览器进来的只有「域名在围栏里」和「链接带令牌」,前者由插件
    在隧道就绪时发布、停止时收回,后者由进程内的 launch token 决定。
  • 插件自己的 API(/remote-access/status|start|stop|restart|link|logs)同样先过这两层:没有 cookie 是
    401,域名不被信任是 403。只知道域名的人既看不到 GUI,也控制不了这条隧道。
  • 传输安全由通道提供:quick / named 走 Cloudflare 的 TLS,ssh -L 走 SSH。
  • 真正的薄弱点是人:链接一旦泄露就等于交出控制权。停止隧道会立刻回收围栏并从磁盘删除链接。

5. 安装

5.1 通过 dsh 命令(推荐)

先安装随 Desktop 分发的 dsh 命令(Desktop 里的 Manage dsh Command…),完全退出 Desktop,然后:

dsh plugin --profile desktop add git+https://github.com/zhexuany/dsh-remote-access.git

插件需要 cloudflared,任选一种方式提供:

brew install cloudflared
# 或把二进制放到 /remote-access/bin/cloudflared

重新打开 Desktop,在 设置 → 远程协作 启动隧道。共享安装默认 autoStart: false,不会自动开公网隧道。

> 通过 dsh plugin add 安装时源码已经在 profile 的 node_modules 里,不要再跑下面的 bin/install 复制一份。

> Windows:核心插件是纯 Node 实现,没有 macOS 专属依赖。用 dsh plugin add 安装,把 cloudflared.exe
> 放进 PATH(或 \remote-access\bin\cloudflared.exe),然后在 设置 → 远程协作 启动。
> bin/dsh-remote-access 与 bin/install 是 POSIX shell 脚本,Windows 上改用 node bin/cli.mjs(§6.2)。

5.2 本地安装器(开发 / 离线,macOS / Linux)

git clone https://github.com/zhexuany/dsh-remote-access.git
cd dsh-remote-access
./bin/install --dry-run   # 先看它会做什么,不写任何东西
./bin/install             # 装进 ~/.dsh/profiles/desktop(默认复制一份快照)

安装器做五件事:

# 动作 位置
1 把插件目录复制(或软链)进 profile /node_modules/dsh-remote-access
2 登记依赖(file: 或 link:)并把插件名加入 dsh.profile.bundles,原文件先备份 /package.json
3 放置一份 cloudflared /remote-access/bin/cloudflared
4 (可选 --enable-browser-access)置 browserAccess: true,只有 0.2.1-rc.2 及更旧的 Host 会读它 /desktop-preferences.json
5 写入受管的「mux 心跳覆盖」段(见 §8),原文件先备份 /cordis.patch.yml

常用选项:

--home             DSH_HOME(默认 ~/.dsh)
--profile         profile 名(默认 desktop)
--copy | --link         复制快照(默认)或软链(改代码即生效,适合开发)
--no-cloudflared        不放置 cloudflared(用 PATH 里或 Homebrew 的)
--heartbeat-ms      写进 profile 补丁的心跳间隔(默认 30000)
--no-heartbeat          不写(或删除)那段受管心跳覆盖
--uninstall             卸载(只回收自己写入的依赖、软链与心跳覆盖)
--dry-run / --json

生效方式:

  • 运行中的 Desktop 会热加载新安装的 bundle,不用重启;
  • 网页界面需要刷新一次才会出现设置面板(面板来自客户端的 client.js,已加载的页面不会自动取新 bundle);
  • 只有改 Desktop 端口才会触发 Host 重启;替换已经加载过的插件代码也需要重启一次 Desktop(Host 不会重新
    import 已加载的模块);
  • --copy(默认)装的是快照,改完源码要重跑 ./bin/install;想边改边生效用 ./bin/install --link。

6. 使用

6.1 GUI:设置 → 远程协作

面板显示状态灯(未启动 / 启动中 / 已就绪 / 失败)、本地地址、隧道方式、隧道地址、连接链接、
本机链接,以及启动 / 停止 / 重启、「详情」与日志。

  • 首次使用:点 启动 → 等链接出现(quick 隧道通常 5–20 秒)→ 复制 → 在另一台机器的浏览器里打开,
    该浏览器此后 30 天免令牌(cookie 与域名绑定,域名一换就要重新点新链接);
  • 不用时点 停止,旧域名立刻失效;
  • 「本机链接」配合 ssh -L 使用(§6.3);
  • 探测到 403 时面板顶部会提示先检查围栏、重启一次隧道再分享。

6.2 CLI

./bin/dsh-remote-access status          # 状态、公网地址、分享链接、本机链接
./bin/dsh-remote-access link            # 只打印分享链接(方便 pbcopy)
./bin/dsh-remote-access link | pbcopy
./bin/dsh-remote-access start|stop|restart
./bin/dsh-remote-access logs            # 最近 200 行(含 cloudflared 自己的输出)
./bin/dsh-remote-access open            # 用本机浏览器打开分享链接
./bin/dsh-remote-access status --json   # 完整状态 JSON

以上示例使用 POSIX shell 包装脚本;Windows 上的等价写法是 node bin/cli.mjs status|link|start|stop|...。

CLI 通过回环控制套接字(/remote-access/control.json,0600)操作运行中的 Host;Host 不在时
退回读取落盘的 state.json / link.txt。所有命令都接受 --home。

bin/dsh-remote-access 只适用于 POSIX(macOS / Linux):它会尝试用 Desktop 自带的 Node 运行
(DSH_NEXT_APP 可显式指定 .app 路径),找不到时使用 PATH 里的 node;Windows 上直接调用 node bin/cli.mjs。

browser-access 是旧版本的兼容入口,仍然接受但不再改变任何行为——Desktop 0.2.1-rc.3 起已经没有
「允许在浏览器中打开」这个开关。

6.3 只用 ssh -L(不暴露公网)

两台机器网络互通(同一局域网,或你能 ssh 到 Host 所在机器)时可以完全不开隧道:

# 在另一台机器上执行;8080 换成那台机器上任意空闲端口
ssh -N -L 8080:127.0.0.1: user@

然后在那台机器的浏览器里打开面板给出的 本机链接(http://127.0.0.1:8080/?token=…)。流量只在
SSH 里,不经过 Cloudflare。

注意 cookie 按 host:port 绑定签名:换了 -L 端口就要重新复制一次本机链接,-L 端口的 cookie 与
隧道域名的 cookie 互不通用。

6.4 访问探测

面板每次拉状态时会带 sec-fetch-site: same-origin 探一次本机端口,结果在「详情」的 access 里:

探测结果 含义
401(token-required) 健康:围栏已生效,只认带令牌 / cookie 的请求
200 / 303(open) 端口可访问且不要求令牌(通常是回环豁免)
403(refused) 连隧道域名都被围栏拒绝,先重启一次隧道再分享

7. 配置

改 cordis.patch.yml 中 - id: dsh-remote-access 的 config:,或在 profile 的同名补丁里用同一 id
覆盖(patch 的 config 是整体替换,不做深合并),然后重启 Desktop。

键 默认 说明
enabled true 关掉后插件不注册任何路由
autoStart false 共享安装默认不自动开公网隧道;改为 true 则 Host 启动即开。面板点「停止」会记住 desired: stopped
provider cloudflare-quick cloudflare-quick / cloudflare-named / ssh / command
protocol http2 http2 / quic / auto;UDP/QUIC 被屏蔽时必须用 http2
cloudflaredPath 自动探测 顺序:本项 → /remote-access/bin/cloudflared → 包内 vendor → Homebrew → PATH
hostname — cloudflare-named 用,例如 dsh.example.com
tunnelToken — cloudflare-named 用(cloudflared tunnel run --token)
publicUrl — 手动指定公网地址(不自动探测时用)
sshDestination / sshRemotePort — ssh -R 反向通道
command / urlPattern — 自定义通道:命令数组 + 从输出里提取公网地址的正则
urlPattern quick 隧道内置 覆盖默认的 URL 探测正则
extraTrustedHosts [] 额外要塞进围栏的域名
maxRestarts 5 通道异常退出后的重启次数(退避 1s→2s→…→30s 封顶)
startTimeoutMs 60000 等待隧道就绪的上限
portWaitMs 内建 等待 Desktop 端口开始监听的上限
logLines 200 内存日志环形缓冲行数
keepAliveMs 65000 把 Host 的 node:http keepAliveTimeout 抬到多少毫秒(0 = 不动),见 §8
proxyKeepAliveMs keepAliveMs - 5000 传给 cloudflared 的 --proxy-keepalive-timeout,必须短于 origin keep-alive
healthCheckMs 15000 公网 URL 健康检查间隔(0 = 关闭):只有落到 Host 的 200/303/401/403 算健康,见 §8
healthCheckTimeoutMs 8000 单次健康检查的超时
healthFailures 3 连续失败多少次后重启 cloudflared(quick 隧道会换新地址与链接)
healthDnsGraceMs 120000 新 quick 域名刚生成时 DNS 可能还没生效;这段时间的解析失败(ENOTFOUND/EAI_AGAIN)不计入失败
muxCompression true 经隧道时压缩会话历史的大 WebSocket 下行帧(§9);回环直连时自动不启用
muxCompressionLevel 4 zlib 压缩级别 1–9;越大越省带宽、越费 CPU
muxCompressionThresholdBytes 1024 小于该字节数的下行帧不压缩(128–1048576)
muxHandshakeTimeoutMs 8000 压缩代理连本机 mux 的握手超时
initialWindowMessages 60 首次打开会话请求的消息上限(DSH 默认 500);0 表示不改,翻历史按页加载
stripStreamingReplay true 压缩前把历史消息里回放的 data.stream 事件置空(−48% 体积),只影响经 mux 代理的下行帧
muxChunkBytes 262144 压缩后的帧再切成多大的 DSHD1 分块(16 KiB–1 MiB);避免单条巨帧卡住隧道
rpcCacheMs 30000 切换会话时只读 RPC(skills/commands/agentPresets 等 6 个)的客户端缓存 TTL;0 = 关闭
remoteDirectoryPicker true 远端页面用插件自绘的目录浏览对话框代替会阻塞的 macOS 原生面板(§10);本机回环始终用原生面板
edgeIpVersion auto 仅 cloudflared 通道:传给 --edge-ip-version;本机 IPv6 路由不通时设 4,省掉每次尝试的 AAAA 超时
tunnelRetries 5 仅 cloudflared 通道:连接/协议错误的 --retries 预算(1–20),越高越能扛住边缘连接抖动
wsDiagnostics true 远端页面把每条页面 WebSocket 的生命周期(路径、开启/关闭时间、close code、帧数)上报给 Host,最近 20 条见 status.wsRecent;本机回环页面不上报
tunnelLogLevel info 传给 cloudflared 的 --loglevel(debug 能看清边缘连接细节)
tunnelLogBytes 1000000 日志超过这个字节数就滚成 tunnel.log.1
home 自动探测 强制指定 DSH_HOME

enabled / autoStart 只影响隧道本身;面板上的临时开关是一次性动作,故意不落盘。

8. 连接稳定性

浏览器与 Host 之间是一条 WebSocket(/api/remote.mux):Host 每 2 秒 ping 一次,连续两次没有 pong
就 terminate() 这条 socket,前端于是显示「重新连接中」并按 0.5s→10s 退避重连。任务都在 Host 上执行,
闪断不会丢任务。

公网路径上「pong 迟到」比「链路断开」更常见:浏览器标签页被节流、网络切换、边缘连接被回收,都可能连续
错过两次心跳。插件为此做了五件事(第 1 项要重启一次 Desktop 才生效;第 5 项只做归因,不改变行为):

  1. 放宽心跳:安装器往 profile 补丁写一段受管的 typert-gateway 覆盖,把 websocketHeartbeatIntervalMs
    从 2000 调到 30000——要连丢两次包得跨 60–90 秒才致命,锁屏或短暂丢包不再被立刻判死。已经装过老版本的
    profile 不会自己变:重跑一次 bin/install.mjs(它会把受管块从 15000 改写成 30000)。注意它救不了换网:
    切换 Wi-Fi/蜂窝会换掉 TCP 四元组,旧 socket 必死;
  2. 让代理连接先过期:cloudflared 默认把 origin 连接保留约 90s,而 Node 的 keep-alive 是 65s;插件默认传
    --proxy-keepalive-timeout 60s,避免复用已被回收的连接;
  3. 抬高 origin keep-alive:把 webServer.server.keepAliveTimeout 抬到 keepAliveMs(默认 65s),并把
    headersTimeout 提到至少 70s;
  4. 日志落盘:cloudflared 输出与插件日志都追加进 tunnel.log 并按大小滚动,边缘连接有没有在重连才有
    证据可查,而不是只有一个内存环;
  5. 区分谁断的:mux 代理在 status.mux.closedBy 里按 browser / upstream / failure / dispose
    计数(dispose = 插件卸载时还在的 socket),并在 lastClose 里记下第一个决定关闭的调用方与 WebSocket
    close code;远端页面还会把所有页面 WebSocket 的关闭信息(路径、存活时长、close code、帧数、是否在关前
    收到消息)上报上来,最近 20 条在 status.wsRecent。mux 的计数器看不见侧边栏那条 /sidebar/ws/agent-opens,
    而那正是 重新连接中 之前最常掉的一条——两个字段一起看,才知道是谁断的。

这些手段只能降低「重新连接中」的概率,不能消除:公网隧道本身会抖。

还有一类故障不走「重新连接中」:cloudflared 的进程不退出,但它和边缘的连接已经作废——典型是 quick 隧道在
Cloudflare 侧的记录被回收,日志里反复出现 Unauthorized: Tunnel not found。这时面板仍显示「已就绪」,链接
却只会返回 502/530/1033。插件为此再补了一个看门狗:

  • 公网 URL 看门狗:隧道就绪后每 healthCheckMs(默认 15s)请求一次公网地址,不带令牌;只有拿到 Host
    的应答(401 是健康答案,200/303/403 也算通)才记为健康。连续 healthFailures(默认 3)次失败就杀掉
    cloudflared 并重新拉起——quick 隧道会生成新地址、写一份新链接。新域名刚生成时 DNS 可能还没生效,这段时间的
    解析失败不算失败(healthDnsGraceMs,默认 120s),否则换了新域名反而会被自己误杀。healthCheckMs: 0 可以关掉它。

9. 会话切换速度

点侧边栏切换对话时,浏览器先拿到该会话的「初始历史窗口」:一次 follow 往返之后,整个窗口作为一条
WebSocket 文本帧到达。DSH 默认最多 500 条消息;大会话里这条帧实测 1–11 MB。

两个放大因素:

  1. 网关本身支持 permessage-deflate(直连时 101 会协商,实测约 4×),但 Cloudflare 边缘不在 101 响应里
    回显 Sec-WebSocket-Extensions,扩展协商被静默丢弃——窗口以未压缩 JSON 过隧道;
  2. 隧道有效上行带宽实测约 0.65–0.8 MB/s,1 MB 窗口约 1.3–1.6s,10 MB 量级要十几秒。

插件为此加了四件事(默认都开,可分别关闭):

  1. mux 压缩代理:Host 新增 upgrade 路由 /remote-access/mux。浏览器插件把发往 /api/remote.mux
    的连接改道到这里(带 ?deflate=1),代理经回环连真正的 mux(转发同一个 Cookie,鉴权仍由网关完成),
    把达到 muxCompressionThresholdBytes 的下行文本帧用 deflate-raw 压成二进制帧,浏览器侧用
    DecompressionStream('deflate-raw') 还原。小帧、上行帧原样转发;本地直连或不支持
    DecompressionStream 的浏览器自动走原路径。实测一条 1.1 MB 窗口压到约 0.22 MB(约 5×)。
  2. 缩小初始窗口:客户端把 follow / loadOlder 的 maxMessages 压到 initialWindowMessages
    (默认 60)。往前翻历史时按页补拉,每次多一个往返;想少翻页可以把它调大。

  3. 剥掉流式回放:历史窗口里 assistant/message 的 data.stream(每个 token 的流式事件)占真实
    窗口体积的约一半,而浏览器只把它当 usage 兜底、data.usage 本身完整存在。Host 在压缩前把这类
    data.stream 置为 [](不是删除:客户端轨迹折叠会 for...of 它),单条 2.6 MB 帧从 2.6 MB 降到
    1.3 MB;stripStreamingReplay: false 可关闭。

  4. 分块下行:压缩后的帧按 muxChunkBytes(默认 256 KiB)切成带 DSHD1 头的二进制
    帧,浏览器侧按序重组后一次解压。单条巨帧不再独占隧道,也让首屏不必等整帧到齐;块大小可调,
    16 KiB 的极端配置已在真实隧道 + Chrome 里验证(内容正常渲染、无 1011 断连)。
  5. 只读 RPC 缓存:每次切换还会串行发 5–6 个 HTTP RPC(skills/commands/agentPresets 等)。客户端在
    connection.rpc.call 上加了 30s、按 payload 区分的缓存(rpcCacheMs,只缓存 ok === true 的 6 个
    只读端点,副作用调用永不缓存),把这条串行链从链路上拿掉。

把这几项叠加后,同一会话窗口的线上字节数约为原始窗口的 8–10%:2.6 MB 的样本帧剥流后 1.3 MB,
deflate 后 266 KB,实测隧道下行一条 1.1 MB 窗口只用 ~115 KB(E2E 里 wsInBytes 约 153 KB,含其它帧)。
在 700 KB/s + 80 ms 的模拟链路上,同一会话的首个窗口从 2.8s 降到 1.1s 量级;大会话的首帧时间还受
Host 冷读 zstd 文件影响,收益不是线性的。status 里的 mux 块给出 downlinkRawBytes /
downlinkWireBytes / compressedBytes / strippedFrames / chunksSent 等计数,可以确认链路在生效。

这些补丁都在浏览器侧落地:更新 Host 代码后需要重启一次 Desktop,并硬刷新浏览器页面。

10. 远端选择工作区目录

点击侧边栏的「添加工作区」时,Host 默认挂的是 @deepseek-ai/dsh-host-directory-picker-auto:它在 boot 时
按 connection.allowsRemoteAuthorities 与环境一次性选后端(本机选 native,SSH/远端绑定选 browse)。
隧道域名是在隧道起来之后才写进信任列表的,所以 boot 时永远选到 native —— 而后端一旦选定,运行期间不能
替换。于是远端浏览器点「添加工作区」时,Host 会在那台 Mac 的屏幕上弹出 NSOpenPanel 并阻塞等待;远端
用户看不到任何窗口,directoryPicker/pick 一直不返回,最后被 Cloudflare 以 100 秒超时收尾:

directory picker failed: client api: directoryPicker/pick failed: transport failure for
/api/directoryPicker/pick: HTTP 524

插件因此在远端页面接管 uiWorkspace.pickDirectory(),用自己的对话框代替原生面板:Host 侧提供
/remote-access/fs(GET ?path= 只列一层子目录,POST {op:'mkdir',path,name} 建单段目录,最多 500 条),
页面里渲染遮罩 + 目录列表 + 「上一层 / 新建文件夹 / 取消 / 使用此目录」。选中的绝对路径照常交给
uiWorkspace 走原来的建工作区流程。回环直连(本机浏览器 / Desktop 窗口)不受影响,仍然用系统原生选择器。

  • 关闭:remoteDirectoryPicker: false(此时远端点击仍会落到原生面板并可能 524)。
  • 认证:/remote-access/fs 与其它插件接口一样走 Host 的 launch-token → cookie 校验,未认证返回 401。
  • 能力边界:只列目录、不读文件内容;mkdir 只接受单个目录名,且不递归创建。

11. 运行时文件

/remote-access/          # 目录权限 0700
├── state.json      # desired / state / url / pid / target / provider / attempts(0600)
├── link.txt        # 分享链接纯文本(0600)
├── control.json    # CLI 用的回环控制端口 + 令牌(0600)
├── tunnel.log      # 隧道输出与插件日志(0600,超过 1MB 滚成 tunnel.log.1)
└── bin/cloudflared # 安装时放置的二进制(可选)

state.json 记住上次是运行还是停止,Desktop 重启后由 autoStart 与它共同决定是否恢复隧道。

12. 排错

现象 处理
403 host not trusted,或探测 refused 域名没进围栏。重启一次隧道(停止→启动),再看「详情」里的 trustedHosts
链接打开 401 用了旧链接或书签。隧道域名换过,重新复制最新链接
一直「启动中」,日志 failed to dial to edge with quic: timeout UDP 被屏蔽,改用 protocol: http2
刚看到域名,本机 curl 解析不了 本机缓存了 NXDOMAIN,等一分钟或换 DNS;curl --resolve <域名>:443:<边缘 IP> 可绕过
页面反复「重新连接中」 见 §8。确认心跳覆盖与 keepAliveMs 已随 Desktop 重启生效,再看 tunnel.log
端口不对 端口来自 Desktop 的 desktop-preferences.json(0 为随机);插件直接读 Host 实际监听的端口,通常不用管
想彻底关掉自动隧道 默认就是关的;若被覆盖成 true,面板点一次停止(记住 desired: stopped),或改回 false 后重启 Desktop
ssh -L 打开却 401 本机链接按 host:port 签发,换端口后重新复制一次
改了源码但界面没变化 面板改动刷新页面即可;Host 侧改动必须重启 Desktop,因为 Host 不会重载已 import 的模块

13. 已知限制

  • 目标只能是本机回环上的 DSH Host(不做「转发到另一台机器」)。
  • 主要验证平台是 macOS。Windows 上核心逻辑(隧道、围栏、面板)是纯 Node,可以运行;但 bin/dsh-remote-access
    与 bin/install 是 POSIX shell 脚本,仓库内 vendor 的 cloudflared 也只有 darwin-arm64——Windows 需要自备
    cloudflared.exe,并用 dsh plugin add 安装、node bin/cli.mjs 调 CLI。
  • cloudflare-quick 的域名每次重启都会变(Cloudflare 免费行为);需要固定域名请用 cloudflare-named。
  • ssh / command 通道是通用后备,需要自备可用的 SSH 目标或命令;这两条路径没有随项目做端到端验证。
  • 单实例:面板与 CLI 操作的都是同一条隧道(同一个 Host 进程)。
  • 访问模型跟着 Host 走:跨机访问完全依赖「围栏 + 链接令牌」两层,插件不提供第三层开关。
  • 面板是用自带的轻量 React 组件实现的(不依赖 Harness 内部包),颜色跟随主题变量,深色/浅色都可用,但
    视觉细节不会与 Desktop 自带设置页完全一致。
  • 「重新连接中」只能被降低概率,不能被消灭(§8)。

14. 开发与测试

./test/run-tests.sh   # 离线测试:service-unit + installer-unit + client-smoke
npm test              # service-unit + installer-unit
套件 覆盖
test/service-unit.mjs Host 侧逻辑:本机链接签发、访问探测分类、keep-alive 调优、隧道日志落盘、browser-access 兼容入口行为
test/installer-unit.mjs 安装器:bundle 注册、清单备份、受管心跳块、重复安装幂等、--dry-run、--uninstall
test/client-smoke.mjs 无头加载 client.js,验证面板注册、渲染、请求路径与状态提示;离线可跑,也可 --live 打真隧道
test/client-react.mjs 用真实 React 渲染面板,守住 hook 顺序等不变量;依赖装在仓库外,缺失时跳过

CI(.github/workflows/ci.yml)在 macos-latest + Node 22 上跑语法检查与三个离线套件。

欢迎 issue 与 PR;提交信息使用 Conventional Commits。

15. 许可证

Apache License 2.0,全文见 LICENSE。著作权与归属见 NOTICE:

dsh-remote-access
Copyright 2026 Zhexuan Yang

English summary

dsh-remote-access is a DSH bundle + web client plugin that publishes a running DSH Harness Desktop Web GUI
to a browser on another machine. It starts a tunnel to the Desktop's loopback port (a Cloudflare quick tunnel
by default; named tunnels, ssh -R and custom commands are also supported), hands you a link carrying the
process launch token, and publishes the tunnel hostname into connection.trustedHosts while the tunnel is up.
No public IP, port forwarding or router changes are required. If the two machines can already reach each
other, ssh -N -L 8080:127.0.0.1: plus the panel's loopback link works without any tunnel.

Access is guarded by two independent layers: the trusted-host fence (a request whose Host is unknown gets
403) and the launch-token-to-cookie exchange (GET /?token=… mints a signed, HttpOnly, SameSite=Strict
cookie bound to the tunnel hostname, valid for 30 days). The plugin never offers a switch that bypasses
either layer, and its own /remote-access/* endpoints are authenticated the same way. A share link is
equivalent to full control of the host machine — treat it as a secret, and stop the tunnel when done.

Because the GUI keeps a WebSocket to the Host that DSH pings every 2s and kills after two missed pongs, the
installer writes a managed typert-gateway override (websocketHeartbeatIntervalMs: 30000) into the profile
patch, stretching the tolerated silence to 60-90 s, passes --proxy-keepalive-timeout 60s to cloudflared, raises the origin keepAliveTimeout
(keepAliveMs, default 65s) and rotates tunnel logs on disk. The first two need one Desktop restart. The
supervisor also polls the public URL itself (healthCheckMs, 15s) and restarts cloudflared after
healthFailures (3) consecutive misses, because a dead edge connection — a quick tunnel whose record is gone,
for instance — retries forever without exiting and would otherwise leave a "ready" panel behind a URL that
answers 502/530/1033. These measures reduce, but cannot eliminate, transient reconnects on a public tunnel.

Switching conversations is the other slow path: the initial history window (up to 500 messages, 1–11 MB in
large sessions) arrives as one uncompressed WebSocket frame, because Cloudflare's edge drops the gateway's
permessage-deflate offer. The Host therefore serves a sibling mux route (/remote-access/mux) that neutralises the
replayed data.stream events (about half of a real window), deflates what is left, and splits the result into
bounded DSHD1 chunks; the client half narrows the initial window to initialWindowMessages (default 60),
reassembles and inflates chunks with DecompressionStream, and memoises the read-only RPCs a switch issues
(rpcCacheMs, default 30s). Measured payload drops to roughly 8–10% of the original window (a 2.6 MB frame →
~266 KB after deflate, ~115 KB on the wire for a 1.1 MB window). Everything is best-effort and falls back to
the direct gateway path; see README §9 for numbers and configuration.

The plugin is developed and validated on macOS. The tunnel service, the trusted-host fence and the settings
panel are plain Node/DOM code, so a Windows host works too once the plugin is installed with dsh plugin add
and a cloudflared binary is on PATH; the shell wrappers under bin/ are POSIX-only.

Licensed under the Apache License 2.0; see LICENSE and NOTICE.

常见问题

8 问
答案由本站依据条目字段整理,不引用上游原文
dsh-remote-access 怎么安装?

在 DSH 里执行 `dsh plugin --profile web add github:zhexuany/dsh-remote-access` 即可。

dsh-remote-access 的验证等级是什么意思?

L5 · 运行验证通过。在 dsh 沙盒中完成安装与运行冒烟——装得上、跑得起来

dsh-remote-access 安全吗?

低风险。静态扫描 低;动态审计无结论(以下以静态为准)→ 总评级 低。

dsh-remote-access 装之前要先确认什么?

先看它会读写哪些文件、执行哪些命令、连接哪些网络地址;验证等级只说明装得上、跑得起来,不等于安全。涉及密钥或系统权限的,建议先在隔离环境试装。

dsh-remote-access 支持哪些 DSH 版本?

上游记录的兼容版本包括 0.2.2-rc.1。

dsh-remote-access 用的是什么许可证?

上游标注为 Apache-2.0。商用或二次分发前请到仓库确认许可证原文。

dsh-remote-access 有哪些功能?

Cloudflare 快速/命名隧道、SSH 反向隧道与自定义命令、带令牌分享链接、自动维护信任围栏、设置面板启停隧道。

这条记录的数据可靠吗?

本站按仓库名去重后合并整理,字段以作者仓库与 npm 元数据为准;安装命令、仓库地址与 README 保持原文,带 L4 / L5 标记的安装命令已在隔离沙盒里真实跑通。

同名或近似条目

8 条
不同作者的仓库分开列,请确认你要装的是哪一个
浏览器与联网
按星标排序,可作替代方案

dsh-our-free-model

dsh-our-free-model

在 DeepSeek Harness 中装上这个 DSH plugin,无需登录、注册或填 API Key,即可直接调用 DeepSeek

L3 · 安装规范通过 8,796 星

dsh-web

dsh-web

侧边栏多列任务看板:卡片交给真实 DSH 智能体会话执行,支持 cron 定时(Host 侧到点执行,关浏览器也生效)。

L3 · 安装规范通过 8,549 星

BrowserSkill

BrowserSkill

BrowserSkill 的 DeepSeek Harness 浏览器自动化桥接插件,通过原生浏览器工具控制可见的 Chrome 和 Edg

L4 · 沙盒安装通过 8,456 星

dsh-pocket

dsh-pocket

DSH Pocket 让 DeepSeek Harness 装进口袋:电脑跑 dsh web,手机扫码即实时同步访问(局域网+公网),人在外

L5 · 运行验证通过 1,656 星

Browser4

Browser4

L4 · 沙盒安装通过 1,152 星

agentrq

agentrq

L4 · 沙盒安装通过 1,140 星

zhexuany 的其它插件

1 个

插件的元信息来自作者仓库;分类、验证等级与安全结论的中文说明为中文整理,最近核验 2026-11-10。