DeepSeek Harness Hub
← 返回列表

手机链路管理ArimaKana-Akane/dsh-sakurafrp

DeepSeek Harnessspec-screened在 GitHub 查看 ↗
未验证

在会话头部管理手机隧道,扫码配对并自愈网关

尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/9/13 · 已提供中文文档

dsh-mobile 卫星:从 DSH UI 管理 SakuraFrp 手机-桌面链接(状态、网关开关、配对二维码、设备、自愈)。Vibe 编码。

综合分
29.7
GitHub 分
29.7
用户评分
★ Stars
0
周下载量
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add ArimaKana-Akane/dsh-sakurafrp
该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
数据截至 2026/9/16(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

dsh-sakurafrp

[!WARNING]
本仓库是 vibe coding 产物。 代码由作者与 AI(DeepSeek Harness 会话)对话生成、
多轮迭代而来,没有经过人工逐行审计,也只在作者本机环境(WSL2 + dsh 0.1.5-rc.1 系列)
实测过。请自行审阅后再使用;不要直接用于生产或安全敏感场景。
代码按 MIT「按原样(AS IS)」提供,不附带任何担保,风险自负。

一个为 dsh-mobile 打造的 vibe coding 卫星插件:
完全在 DSH 自己的 UI 中管理手机 ↔ 桌面链路(SakuraFrp TCP 隧道 + dsh-mobile 网关)——
状态、网关开关、一次性配对二维码、设备撤销、自愈。

把「手机通过 SakuraFrp 隧道访问电脑上的 DSH」这条链路图形化:
原先要跑脚本、改 YAML、盯 systemd 才能完成的事,现在在 DSH 会话头部点一个手机图标就能做。

1. 它做什么

| 能力 | 说明 |
|---|---|
| 链路状态一屏可见 | 网关是否在跑、监听端口、公网入口、四个公网节点的实测延迟、已配对设备数、外部守护服务状态 |
| 一键开关手机访问 | 直接控制 dsh-mobile 的移动网关(等价于 POST /api/mobile-access/lan/control);关闭会留下抑制标记,自愈不再把它拉回来 |
| 配对二维码 | 申请一次性配对窗口(默认 5 分钟有效),二维码与链接内联返回,手机相机/浏览器扫码即可 |
| 设备管理 | 逐台撤销、一键清空(清空会带上游要求的 confirm:true);配合 maxDevices 实现「只信任某一台设备」 |
| 网关自愈 | 每 15 秒检查移动网关,发现被静默关闭(已知问题)就重新拉起 —— 但用户主动关闭时让路(见下方说明) |
| 公网可达性探测 | 每 60 秒并发探测主入口 + 三个运营商前缀节点,面板直接显示谁通谁不通 |
| 外部守护开关 | 可启停 dsh-mobile-lan-watchdog.service(冗余层,插件自带自愈已能独立兜底;它同样尊重「用户已关闭」标记) |

「关得掉」是硬要求。 早期版本的自愈是无条件的:你关掉手机访问后,15 秒内它就会被
自动拉起来,连 dsh-mobile 自己持久化的 enabled:false 偏好也一并被覆盖——表现为
「根本关不掉」。现在点「关闭手机访问」会写一个抑制标记(默认
$DSH_HOME/mobile-access/.gateway-user-disabled),插件自愈与外部 watchdog 都会跳过;
面板也会显示「你已主动关闭…」。点「开启手机访问」即删除标记、恢复自愈。

入口:DSH 会话头部工具区的手机图标按钮(order: -15,位于「重启」与「打开文件管理器」之间)。
手机端也能打开这个面板,但只能看状态(原因见第 6 节)。

2. 依赖声明(重要)

2.1 运行期硬依赖:dsh-mobile 插件

本插件不自己实现任何网关,它只是 dsh-mobile 的移动访问网关的图形化管理壳。
没有 dsh-mobile 时:/status 会显示网关无响应,/pair 会报「网关未启动或 dsh-mobile 未启用」。

它依赖 dsh-mobile 提供的这些本机(loopback)管理接口:

| 方法 | 路径 | 用途 |
|---|---|---|
| GET/POST | /api/mobile-access/lan/control | 网关开关({running: bool}) |
| GET | /api/mobile-access/lan/status | 网关/配对/设备数/资源占用 |
| GET | /api/mobile-access/lan/devices | 设备列表 |
| POST | /api/mobile-access/lan/devices/revoke | 撤销设备({deviceId},32 位小写 hex) |
| POST | /api/mobile-access/lan/devices/reset | 清空设备 |
| POST | /api/mobile-access/lan/pairing/open | 申请一次性配对窗口,返回 pairUrl + qrSvg |

2.2 运行期硬依赖:SakuraFrp 隧道(非 npm)

公网入口由 SakuraFrp 启动器常驻的 frpc 提供。本插件不启动、不配置、也绝不接触访问密钥。
面板预建一条 TCP 隧道,并且:

- 隧道远程端口必须等于 profile 里 mobile-access.listenPort(插件硬约束:显式 authority 的端口必须等于监听端口);
- 隧道不要设置「访问密码」——那会让 frpc 接管 TLS 去渲染认证页,破坏端到端自签证书与 Host 校验;
- Windows 端 frpc 经 WSL2 networkingMode=mirrored 的 localhost 直通,回连 WSL 回环监听。

2.3 运行期软依赖

| 项目 | 必需 | 用途 |
|---|---|---|
| dsh-mobile-lan-watchdog.service(本仓库 scripts/dsh-mobile-lan-watchdog.sh) | 否 | 网关被静默关闭时的第二层自愈;插件自带 15s 自愈已可独立兜底 |
| systemctl --user | 否 | 外部守护服务的启停 |

2.4 平台与版本

| 项目 | 要求 |
|---|---|
| 操作系统 | Linux / WSL2(插件用 systemctl --user、路径按 POSIX 拼) |
| Node | >= 20(用到全局 fetch、AbortSignal.timeout) |
| dsh 核心 | 在 0.1.5-rc.1 系列的 web profile 上验证;其它版本自测 |
| dsh profile | web($DSH_HOME/profiles/web) |

2.5 dsh-dependencies 声明

除 README 外,package.json 里也带了机器可读的声明:

"peerDependencies": { "dsh-mobile": "" },
"dshDependencies": {
"core": ">=0.1.5-rc.1",
"plugins": [{ "name": "dsh-mobile", "required": true, "provides": "..." }],
"programs": [{ "name": "SakuraFrp 启动器 / frpc", "required": true, "platform": "Windows" }],
"services": [{ "name": "dsh-mobile-lan-watchdog.service", "required": false }],
"platform": ["Linux", "WSL2"], "node": ">=20"
}

3. 安装

3.1 一键安装(推荐)

git clone https://github.com/ankhishtar2-lang/dsh-sakurafrp.git
cd dsh-sakurafrp
bash scripts/install.sh          # 拷贝到 profile + 注册 bundles + 静态自检
systemctl --user restart dsh-web-profiled.service   # 生效(由你手动执行)

scripts/install.sh 只做三件事:把包拷进 $DSH_HOME/profiles/web/node_modules/、
把 dsh-sakurafrp 追加进 profile package.json 的 dsh.profile.bundles、做静态自检
(host 是否有 apply 导出是硬红线:缺了会让整棵插件树 boot 失败)。它也不会替你重启。

3.2 手动安装

cp -r dsh-sakurafrp "$DSH_HOME/profiles/web/node_modules/"
再把 "dsh-sakurafrp" 加进 $DSH_HOME/profiles/web/package.json 的 dsh.profile.bundles
dsh --profile web --dump-config >/dev/null && echo 配置可解析
systemctl --user restart dsh-web-profiled.service

3.3 前置:配置 dsh-mobile 的移动网关

在 $DSH_HOME/profiles/web/cordis.patch.yml 里加一段(值都要换成你自己的):

- id: mobile-access
config:
initiallyEnabled: true
maxDevices: 1                     # 只信任一台设备就写 1
pairingTtlMs: 300000              # 配对窗口 5 分钟
listenHost: 127.0.0.1
listenPort: 33782                 # 必须等于 SakuraFrp 隧道的远程端口
publicAuthorities:
- node.example.com:33782        # 手机访问时用的 Host,必须与隧道入口一致
- yd.node.example.com:33782     # SakuraFrp 运营商前缀节点(可选)
allowedCidrs: ['127.0.0.0/8', '::1/128']   # 隧道过来的源地址恒为回环
upstreamOrigin: http://127.0.0.1:3080
tls:
mode: provided
certFile: !!js dshHomePath('mobile-access/tls/server.crt')
keyFile: !!js dshHomePath('mobile-access/tls/server.key')
pairingCaFile: !!js dshHomePath('mobile-access/tls/ca.crt')
instanceId: ''   # 必须与 CA 指纹完全一致

⚠️ 两个会让你起不来 dsh 的坑(作者踩过)
1. pairingCaFile 配了却漏了 instanceId:插件会拿 sha256(stateFile 路径) 当 instanceId,
永远对不上 CA 指纹 → start() 抛错 → loader fail-fast → 整个 dsh web 起不来。
2. maxDevices 在「配对准入」时只数活跃设备,但在启动加载时数全部记录(含已撤销)。
撤销过设备后忘记清理、又把 maxDevices 改小,重启即崩。本仓库的
scripts/dsh-mobile-security-audit.sh 会自动清理已撤销记录。

安装方式补充:从 npm 安装(可选)

本包的 package.json 已按 npm 发布要求准备好(去掉 private、用 files 白名单控制内容)。
发布到 npm 后即可用 dsh 自己的命令安装(dsh plugin add 本质就是 pnpm add):

dsh plugin --profile web add dsh-sakurafrp

自己发布(需要你自己的 npm 账号;scripts/publish.sh 不接触也不保存任何 token):

npm login
bash scripts/publish.sh --dry     # 自检 + 列出将要发布的文件,不发布
bash scripts/publish.sh           # 真正发布(改过代码要先升 version,同版本号不可覆盖)

发布后请同步更新 README 与上游收录表的描述,保持「描述属实」这一条成立。

4. 配置(环境变量)
If you don't want to modify the profile, all behavior can be overridden with environment variables (set them in the systemd unit or startup script):

| Variable | Default value | Description |
|---|---|---|
| DSH_SAKURA_ORIGIN | https://node.example.com:33782 (placeholder) | Required: your own public entry point. If the placeholder is not changed, both probing and pairing will fail |
| DSH_SAKURA_AUTHORITIES | empty | Additional public authorities to probe (comma-separated); the primary entry point is automatically taken from origin; SakuraFrp's yd./dx./lt. prefix nodes go here |
| DSH_SAKURA_SELFHEAL_MS | 15000 | Gateway self-healing polling interval |
| DSH_SAKURA_PROBE_MS | 60000 | Public node probing interval |
| DSH_SAKURA_LOG | $HOME/dsh/memory/dsh-sakurafrp.log | Event log (automatically created with 0600) |
| DSH_SAKURA_WATCHDOG_UNIT | dsh-mobile-lan-watchdog.service | External watchdog service unit name |
| DSH_SAKURA_SUPPRESS_FILE | $DSH_HOME/mobile-access/.gateway-user-disabled | Path of the suppression marker for "user has disabled mobile access"; when present, both self-healing and watchdog step aside |
| DSH_SAKURA_ADMIN_TOKEN | not set | Once set, the identification header for mutating routes must be this value (replace the public 1 with a real credential) |
| DSH_SAKURA_ALLOW_REMOTE_ADMIN | not set | When set to 1, disables the "desktop-only" gate (see section 6, not recommended) |

📌 Differences from the author's actual running version: The author's local copy sets the default value of DSH_SAKURA_ORIGIN to their own tunnel entry point
(to save configuring an environment variable). To avoid exposing a personal tunnel address, this repository changes the default value to the placeholder
https://node.example.com:33782, and by default no longer probes yd./dx./lt. prefix nodes.
Therefore, after installing from this repository, you must explicitly set DSH_SAKURA_ORIGIN (and DSH_SAKURA_AUTHORITIES if necessary).

5. HTTP routes

All are registered on dsh web (default 3080), and only accept loopback origins (anything non-loopback is rejected with 403):

| Method | Path | Desktop-only | Description |
|---|---|---|---|
| GET | /dsh-sakurafrp/status | No (redacted for mobile) | Aggregated status: gateway (including suppressed) / probing / devices / watchdog / self-healing counters |
| POST | /dsh-sakurafrp/control | ✅ | {running: bool} toggles mobile access; disabling writes the suppression marker, enabling clears it |
| POST | /dsh-sakurafrp/pair | ✅ | Requests a pairing window, returns pairUrl + inline qrDataUrl |
| GET | /dsh-sakurafrp/qr.svg | ✅ | The most recent QR code SVG (for debugging; the frontend now uses an inline data URL) |
| POST | /dsh-sakurafrp/revoke | ✅ | {deviceId} revokes a device |
| POST | /dsh-sakurafrp/devices/reset | ✅ | Clears all paired devices (sends confirm: true to dsh-mobile) |
| POST | /dsh-sakurafrp/selfheal | ✅ | Immediately performs one "ensure the gateway is running"; skipped if the user has disabled it |
| POST | /dsh-sakurafrp/probe | ✅ | Immediately probes all public nodes |
| POST | /dsh-sakurafrp/watchdog | ✅ | {active: bool} starts/stops the external watchdog service |

6. Security notes (please be sure to read)

6.1 Why there is a "desktop-only" gate

dsh-mobile's mobile gateway is a reverse proxy with a privileged upstream cookie: it forwards everything except /mobile-access/
to dsh web (3080), attaching the auth cookie it obtained by exchanging the DSH in-process launch token.
The consequence is: any paired device can reach all plugin routes registered on 3080, and the source address appears to be 127.0.0.1 in every case.

During a security review, the author actually tested this (using a real paired device from the public internet):

| Request | Before hardening | After hardening |
|---|---|---|
| POST /dsh-sakurafrp/pair | 200 (the phone could mint its own pairing code and then pull more devices in) | 403 desktop_only |
| POST /dsh-sakurafrp/watchdog {"active":false} | 200 (watchdog service stopped → self-healing disabled) | 403 desktop_only |
| POST /dsh-whale-tools/restart | 200 (dsh was actually restarted) | 403 desktop_only |

How the gate works (three layers, all must pass):

1. The source address must be loopback;
2. If Origin / Referer are present, they must be loopback (127.0.0.1 / ::1 / localhost).
When the gateway forwards, it rebuilds the request headers using a whitelist (only allowing
accept* / content-* / if-* / range / user-agent / origin / sec-fetch-site) —
note that origin is in the whitelist, so when the phone accesses through the tunnel, the Origin the server can see is
the tunnel domain, not loopback; a malicious web page would instead show the attacker's domain. Both are rejected.
These two headers are filled in by the browser itself and cannot be changed by page scripts;
3. The request header x-dsh-sakurafrp-desktop: 1. When DSH_SAKURA_ADMIN_TOKEN is set,
this must be the value of that token (the real credential).

What it is and what it is not (please understand this accurately): The header in item 3 is a discriminator, not authentication — its value is publicly written in
this README and the source code. The real boundary is: 3080 only binds to 127.0.0.1 + the gateway strips custom headers +
a process that can send requests on the local machine already has equivalent user privileges (it can directly call the loopback management interface).
So this gate blocks browser cross-site requests and devices coming through the public tunnel, but it does not sandbox local processes.
- 逃生舱:DSH_SAKURA_ALLOW_REMOTE_ADMIN=1 会让手机端也能管理(等于把上面三道全关掉,不建议)。
- 本插件不接触任何密钥:SakuraFrp 访问密钥始终只在 Windows 启动器手里;
前端只处理一次性、短时有效的配对 token。
- 事件日志以 0600 创建,内容不含密钥与 token。

6.2 安全边界在哪

真正的安全边界是「设备配对」本身:配对成功后,那台设备就拥有完整的 DSH 会话
(能驱动 agent、读写会话)——这是移动访问的功能本意。本插件收紧的是控制面
(铸造配对码、开关网关、撤销设备、停守护、重启 dsh),不是会话面。
手机丢失/借人 ≈ 把 DSH 交出去;要收回就在电脑端面板撤销该设备。

7. 仓库脚本

| 脚本 | 用途 |
|---|---|
| scripts/install.sh | 幂等安装(拷贝 + 注册 bundles + 静态自检) |
| scripts/uninstall.sh | 卸载(移除 bundles + 删目录) |
| scripts/dsh-mobile-lan-watchdog.sh | 移动网关守护:发现 running:false 就拉起(systemd user 服务用) |
| scripts/dsh-mobile-security-audit.sh | 可复跑的审查脚本:匿名面 / 文件权限 / 「已配对设备越权」实测。⚠️ 它并不是完全非破坏性的:为了实测越权,它会真的在本机配对一台临时设备,因此会改写 ~/.dsh/mobile-access/devices.json(0600)。脚本结束时会清理该记录,但中途被打断会留下一条有效设备记录——跑完请用面板确认设备列表,或直接跑 devices/reset |
| scripts/legacy/dsh-mobile-pair.sh | 早期命令行配对脚本,已被插件面板取代,留作备用 |

审计脚本用法:

bash scripts/dsh-mobile-security-audit.sh [公网origin]
关键项全过 → 退出码 0;发现越权/暴露 → 1

8. 已验证 / 已知限制

已在作者本机验证

- 4 个公网节点探测全部 200 {"ok":true},延迟 161–235 ms(真实隧道,自签证书校验关闭);
- 真实配对 → 带会话拉取手机端首页 200;手机端所有控制面请求 403;
- host 半桩测试 37 项 + 浏览器半 bundle 复核;dsh --profile web --dump-config 退出码 0;
- 网关被静默关闭后 15 秒内自愈(多次实测)。

已知限制

- 只做「管理壳」,不提供隧道:隧道断了要去看 SakuraFrp 启动器/面板;
- 依赖 dsh-mobile 的内部管理接口,上游改路径就会失效(接口非公开契约);
- 公网探测会周期性向公网节点发 HTTPS 请求(每 60 秒 4 条,可 DSH_SAKURA_PROBE_MS 调大或关掉插件);
- 面板为原生 DOM + 内联样式,深色/浅色主题都做了变量兜底,但主题差异大的皮肤下观感可能一般;
- 手机端只能读状态,这是设计而非缺陷。

9. 卸载

bash scripts/uninstall.sh
systemctl --user restart dsh-web-profiled.service

卸载本插件不会影响:SakuraFrp 隧道、mobile-access 配置与自签证书、设备状态文件、
外部守护服务。这些要单独处理(见第 3.3 节的配置段)。

10. 目录结构

dsh-sakurafrp/
├── package.json          # dsh.bundle / dsh.client / peerDependencies / dshDependencies
├── cordis.patch.yml      # loader 条目:id=sakurafrp, name=dsh-sakurafrp
├── lib/
│   ├── index.js          # host 半:9 条路由 + 网关自愈(尊重用户关闭)+ 公网探测 + 桌面闸门
│   └── client.js         # 浏览器半:头部手机按钮 + 管理面板 + 每秒倒计时
├── scripts/              # install.sh / uninstall.sh / publish.sh / 守护 / 安全审计 / 早期配对
├── LICENSE               # MIT
└── README.md

11. 许可

MIT © 2026 ankhishtar2-lang —— 见 LICENSE。
再次提醒:vibe coding 产物,未经人工逐行审计,按「原样」提供,风险自负。

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

同作者(ArimaKana-Akane)的其他插件

💬 加入 DPharness 群聊

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

点击加入 QQ 群
DPharness 群聊二维码,手机 QQ 扫码进群
扫码进群