dsh-mesh-access
用手机远程控制电脑上的 DSH。 手机浏览器打开,就是你在电脑上正在用的那个界面 ——
同一个实例、同一份会话、同一份上下文,不需要 SSH,不需要第二个 dsh,也不用碰路由器。
底层做法是把正在运行的 DeepSeek Harness 网页界面挂到组网网卡上,在服务进程内部完成。

它做什么
dsh web --host 只接受一个具体的网卡地址,并且拒绝通配。所以只监听 127.0.0.1:3080 的实例,
组网里的手机、笔记本、另一台电脑、带图形界面的 NAS 都看不见;而开机之后才出现的 TAP(星空组网的 StarVPN)或 utun 网卡,也不在它启动时的那一份地址表里。
这个插件在已经在跑的那个进程内部多开一个组网端口:
任何设备 ──▶ 192.168.188.1:3081 ──▶(插件反代)──▶ 127.0.0.1:3080(你正在用的这个 dsh)
一个实例、一份会话列表:不会出现 task-board ledger is already owned by …,也不会出现两台服务状态不同步。
安装
dsh plugin --profile web add dsh-mesh-access
不方便用 npm,或者你的 npm 指向国内镜像?用 Release 里的 tgz:
dsh plugin --profile web add \
https://github.com/SilverKurali/dsh-mesh-access/releases/latest/download/dsh-mesh-access.tgz
从本地目录装(改代码的人用这个):
dsh plugin --profile web add link:/绝对路径/dsh-mesh-access
完整的四种装法、验证步骤、卸载与故障表见 INSTALL.md。
装完重启 profile,左侧导航会多出**「组网访问」**:
| 功能块 | 内容 |
|---|---|
| 当前状态 | 反代是否在跑、DSH 自己的监听端口、找到几张组网网卡 |
| 动作 | 在组网地址上开端口 / 关闭 / 刷新 |
| 访问地址 | 给组网内任何设备打开的地址(带 token,可直接复制) |
| 公网隧道(可选) | 用你自己的服务器做公网入口:启停 frpc、显示公网地址、真实探测是否可达 |
| 第一次用星空组网? | 邀请注册链接 + 成员账号规则 |
用你自己的服务器(可选)
组网那条路对局域网/P2P 很够用,但它要过公网中继:实测 RTT 800–990 ms(最坏 3.7 s),长连接会被心跳判死, 手机上就表现为一直「重新连接中」。如果你自己有一台带公网 IP 的服务器,可以把它当公网入口,延迟通常 30–60 ms。
插件不提供服务器,也不用别人的服务器 —— 这一步完全是你自己的机器。两个脚本,都在这个仓库里:
# 1) 在你自己的服务器上(root):装 frps
sudo ./tools/frps-setup.sh --port 7000 --http-port 21081
# 想直接上 HTTPS:加 --domain dsh.example.com --email you@example.com
# 2) 在跑 DSH 的这台电脑上:装 frpc,指向上面那台服务器
./tools/frpc-setup.sh --server <服务器IP> --token <第 1 步打印的 token> --remote-port 21081
然后回到左侧「组网访问」→「公网隧道」→ 启动隧道。第 1 步会打印 token 和给你复制的第 2 步命令; 安全组要放行 7000/tcp 与 21081/tcp(这一步只能在云控制台做)。
完整说明(前置条件、域名/TLS/Basic 认证、故障表、卸载)见 docs/VPS.md。 想让 AI 帮你配:把 docs/AI-SETUP.md 整份丢给它,它就知道该跑什么、怎么验证。
入口登录(1.4.0 起)
公网地址的门锁做在插件面板里(「入口登录」卡片),服务器零配置——nginx 不再需要账密:
| 模式 | 体验 | 适合 |
|---|---|---|
| 密钥(推荐) | 面板给一次性地址,手机打开一次,30 天内免登录、全程无弹窗 | 自己的设备 |
| 账号密码 | 浏览器弹窗登录一次,24 小时内仅该设备免登录,其他设备各自登录 | 偶尔借别人设备 |
| 关闭 | 不加门锁(只剩 DSH 自己的 token 一层) | 不推荐 |
- 密钥怀疑泄露 → 面板点「重置」,旧密钥立即作废,所有设备重新认证。
- 改账号密码 → 所有已登录设备立刻下线。
- 密钥模式下无效请求一律返回 404,不暴露这里有个服务。
- 认证状态存本机
~/.config/frp/dsh-mesh-access.json(侧车文件),密码 scrypt 哈希存储。
HTTP 接口
面板只是两个接口的外壳,故意做得极小:
GET /api/dsh-mesh-access/status → { ok, host:{upstream,port}, interfaces[], mesh[], proxy{...}, referral }
POST /api/dsh-mesh-access/proxy → { action:'start' | 'stop', port? }
GET /api/dsh-mesh-access/tunnel → frpc 隧道状态(1.1.0+)
GET /api/dsh-mesh-access/auth → 入口登录状态;POST 切换模式 / 改账密 / 重置密钥(1.4.0+)
不开浏览器也能排查:
curl -s http://127.0.0.1:3080/api/dsh-mesh-access/status
配置(可选)
写在 profile 的 patch 层里,每一项都有默认值:
- id: mesh
name: dsh-mesh-access
config:
interfacePattern: '^(StarVPN|utun\d+|tap\d+|tun\d+|wg\d+|gpd\d+|ppp\d+|ipsec\d+)$'
addressPrefix: '192.168.'
proxyPort: 3081
upstream: 127.0.0.1
addressPrefix 的作用是别把 tun* 上的公网地址当成组网地址。
为什么 cookie 能一直用
带 ?token= 的请求返回 303 并种下 dsh-auth-*(HttpOnly、SameSite=Strict、Max-Age=2592000,
即 30 天)。cookie 载荷里写死了签发时的 authority:
{"version":1,"authority":"192.168.188.1:3081","issuedAt":…,"expiresAt":…}
签名密钥存在持久的凭据记录里(不是每进程随机),所以重启也在。反代每次请求都把 Host
改写成上游 authority,于是 cookie 里的 authority 与服务端期望的一致 —— 这就是换个端口访问还能继续用
同一份 cookie 的原因。
反过来也成立:换了组网 IP 或端口,就得重新点一次 token 链接。
限制
- 插件动不了自己进程的监听地址。组网端口是反代,不是把主服务再绑一次。
- 只考虑 IPv4 组网地址。
- 同一份
~/.dsh只允许一个实例持有 task-board 锁;这是刻意的,本插件就是为避开它而存在。
性能与限制(实测,别指望它像局域网)
这套东西能不能用得舒服,只取决于组网隧道怎么给你选路。下面是 2026-11-07 在一条真实链路上量到的一组数字, 用来判断「是不是插件的问题」——绝大多数时候不是。
| 场景 | 单程延迟 | 说明 |
|---|---|---|
同一局域网(10.x.y.z:3081) |
1–3 ms | 直连,等于本地 |
组网直连(path=P2P) |
30–80 ms | 最好情况,但需要双方都能被打洞 |
| 组网 UDP 中继 | 60–150 ms | 可用 |
组网 TCP 中继(path=SN-TCP) |
840–990 ms,最坏见过 3.7 s | 会很卡,见下 |
TCP 中继的坑在于它是 TCP 套 TCP:一个丢包会因为重传把整条流按顺序卡住,延迟被放大十几倍。 更麻烦的是手机侧的地址会变(运营商 CGNAT),客户端每次发现「对端地址变了」就会隔离 P2P 直连、回落中继:
[P2P] 隔离旧直连,等待新的 P2P 确认;业务由 Mesh/Supernode 策略选路
reason=peer-info-address-changed
[SN-RTT] Supernode 业务包往返偏慢: ... rtt=891ms path=SN-TCP
这种链路上,DSH 的实时通道心跳会周期性超时,界面上表现为 「重新连接中…」反复出现。 插件做过两件事把代价压到最低:
- 改写了
Origin(否则 WebSocket 升级会被 DSH 的同源校验直接回 403,一次都握不上手); - 给
/assets/*加了immutable缓存(上游原本没有任何缓存头,每次重载都要重传约 2.4 MB)。
但心跳超时本身救不了。判断标准很简单:
# 在服务端看组网客户端的日志,确认走的是不是 TCP 中继
grep -E 'SN-RTT|path=SN-TCP|peer-info-address-changed' /usr/local/lib/stars/res/stars.log | tail
- 出现
path=SN-TCP且rtt常年 800 ms 以上 → 不是插件的问题,换链路才有用; - 想稳定远程,请在公网放一台国内 VPS 做端口转发/隧道(手机走普通公网 TCP,PC 主动出站), 而不是指望一个「双方都在 NAT 后面、只能中继」的组网。
不只手机
名字叫 phone,但插件本身跟“手机”没有关系 —— 它做的是把组网地址上的请求转给本进程。 组网里任何能打开网页的设备都能用:
| 设备 | 用法 |
|---|---|
| 手机 / 平板 | 浏览器打开那条带 token 的地址,可加到桌面当 App |
| 另一台电脑(Win / macOS / Linux) | 同一个地址,直接开;不用装 DSH,也不用装这个插件 |
| 带图形界面的 NAS(群晖 DSM、威联通 QTS、TrueNAS Scale 等) | NAS 上的浏览器打开即可 |
| 电视盒子 / 平板上的浏览器 | 同理,只要能连到 192.168.188.1 |
条件是三条:它在同一个星空组网里、能路由到组网 IP、浏览器能发 HTTP。 不需要在对方设备上装任何东西,也不需要 SSH。
配套
命令行版(在 DSH 还没启动、或者机器上没有 Node 插件环境时使用: 起第二个实例绑到组网 IP,附带防火墙规则与二维码): dsh-mesh-kit,支持 Linux / macOS / Windows。
设计取舍与实测数据见 docs/architecture.md。
许可
MIT。
常见问题
页面能打开、侧边栏也有,但底部一直「重新连接中…」,工作区列表空着
这是 1.0.0 的 bug,升级到 1.0.1 即可。原因:反代把 Host 改写成上游(127.0.0.1:3080)却没改 Origin,DSH 对 WebSocket 升级做同源校验,浏览器带着 Origin: http://<组网IP>:<端口> 过来,两边不一致 → 上游直接 403 Forbidden。于是普通 HTTP 全通(页面能开、API 能查),实时通道一次都没握上手,浏览器每几秒重试一次,看起来就像“网络一直断”。
自查一行就够:
curl -s -o /dev/null -w '%{http_code}\n' --http1.1 \
-H 'Connection: Upgrade' -H 'Upgrade: websocket' \
-H 'Sec-WebSocket-Version: 13' -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' \
-H "Origin: http://<组网IP>:<端口>" \
-b <你浏览器的 cookie> \
http://<组网IP>:<端口>/api/remote.mux
101 是正常,403 就是中了这个 bug。
改了端口或组网 IP,面板上的地址还能用吗
不能。反代一开出来,监听地址和上游端口就固定了;「访问地址」里的 token 也是按当时的地址签的。换了就点「关闭」→「在组网地址上开端口」,重新复制地址。
组网里的设备打开地址却超时
先确认那台设备能路由到组网 IP(ping <组网IP>),再确认本机防火墙放行该端口。Linux 上 firewall-cmd --zone=<组网域> --list-ports,Windows 上查该端口的入站规则。
No comments yet. Be the first to write one.