← 返回列表
⚠ 装前注意
给 DeepSeek HarnessDSH的 Web 界面加一层带设备鉴权的远程访问:
基本兼容但装前注意:npm 同名包「dsh-remote」归属 flymysql/dsh-remote,装到的可能不是本插件 · 最近上游提交 2026/9/25 · 已提供中文文档
DeepSeek Harness (DSH) 的设备认证远程访问层:配对、按设备角色、只读强制、长期令牌链接、cloudflared 隧道监控。
综合分
30.7
GitHub 分
30.7
用户评分
—
★ Stars
1
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add laolin2000/dsh-remotenpm 同名包「dsh-remote」归属 flymysql/dsh-remote,装到的可能不是本插件,改用 GitHub 源安装
信任档位:已验证本站已于 2 天前真实安装成功(L4 · 真实安装)
- 是什么
- dsh 原生插件 · chat
- 装得上吗
- 本站已真实安装成功(L4 · 真实安装,非静态推断)
- 安全吗
- 本站尚未对该插件做风险分级(暂未覆盖,不等同于无风险)
- 还在维护吗
- 活跃:最近一次提交在 0 天前
档位由下列信号合成:本站实装验证(真实安装,当前最高到 L4)· 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。下方各区块是它的证据明细。 验证判据与等级说明 →
🟢实装验证通过· 2026/9/23
由本站实装验证器在真实 dsh 环境安装成功,非静态推断。
数据截至 2026/9/26(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查⚠ 装前注意
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包dsh-remote @ 0.8.21
✓Node 引擎要求 >=20 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
npm 同名包「dsh-remote」归属 flymysql/dsh-remote,装到的可能不是本插件
验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/23 19:04:01
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
由 DeepSeek 最新模型翻译生成dsh-remote
给 DeepSeek Harness(DSH)的 Web 界面加一层带设备鉴权的远程访问:
手机点一条链接就能进 DSH,但没配对过的设备连不上,只读设备只能看、不能操作。
状态:守卫 + 长期链接 + 角色强制 + 隧道监督 + 二维码 + DSH 界面插件均已实现并实测。
测试合计 423 项断言:守卫 142 / 二维码 46 / 面板(真实 DOM)73 / 插件路由 55 / 桌面工具 15 / 安装器 28 / 一键安装 31 / 文档审计 22 / 隧道实测 11,
全绿。逐条验证记录见文末「验证记录」。
为什么需要它
三条都是 DSH 的既有行为,不是我们的假设:
1. DSH 拒绝把界面开放到网络。 dsh --host 0.0.0.0 会直接报错,原文是
"would expose remote code execution to the network";@deepseek-ai/dsh-host-webserver 也明确不提供
TLS、认证或来源策略。(出处:DSH 官方错误文案与包文档)
2. DSH 的 /api 是一整套能操作本机 agent 的 RPC。 session.prompt 能让 agent 跑命令、
workspace.delete 能删会话、session.cancel 能中止任务。(出处:@deepseek-ai/dsh-host-apiproxy 的路由表)
3. DSH 的 /api 有一道可信来源围栏。 Host 必须是回环或在可信名单里,Origin 必须同源。
(出处:@deepseek-ai/dsh-client-connection 的 isTrustedApiRequest)
所以:直接暴露 = 把整台电脑的控制权交出去;而想远程用,就必须自己补上"鉴权"这一层,并且不能改 DSH 本身(它的安全立场是刻意的)。
它做什么
手机 / 平板 / 另一台电脑
│ https(隧道或局域网)
▼
┌─────────────┐ 没有配对 → 401 + 配对页(不泄漏 DSH 任何内容)
│ guard │ 只读设备 → 写请求一律 403(服务端按方法名强制)
│ (本项目) │ 配对设备 → 透传 + 改写 Host/Origin/Referer
└──────┬──────┘
│ http://127.0.0.1:
▼
┌─────────────┐
│ 本地隧道层 │ 例:remote.mjs(页面注入 / 附件瘦身 / 本地端点)
└──────┬──────┘
▼
┌─────────────┐
│ DSH Web GUI│ 127.0.0.1:(只绑回环)
└─────────────┘
关键设计:守卫是最外层,DSH 与中间层完全不知情——所以任何 DSH 版本、任何本地反代都能直接放在它后面。
从零安装(只靠这个仓库)
前置:Node ≥ 20;DSH 装好并至少启动过一次(profile 目录 ~/.dsh/profiles/web-desktop 存在)。
1)+2) 取代码并一条命令装好(注意:必须在仓库目录里运行;下面这条是完整可粘的一行)
git clone https://github.com/laolin2000/dsh-remote.git && cd dsh-remote && node bin/setup.mjs
不想全自动可加 --no-tunnel / --no-guard / --dry-run;等价脚本:npm run setup
如果 git 连不上 GitHub,再给这条 clone 挂代理:git -c http.proxy=http://127.0.0.1: clone …
3) 重启一次 DSH(插件树不热重载)→ 右下角、EAC监控按钮上方出现「手机链接」
已经在别的目录里?用绝对路径跑即可(脚本按自身位置定位仓库,与当前目录无关):
node "C:\Users\\dsh-remote\bin\setup.mjs"
若报 Cannot find module .../bin/setup.mjs,就是当前目录不在仓库里(请先 cd 进仓库,或用绝对路径)。
setup.mjs 一次做完这些事(幂等,可反复运行):
| 步骤 | 做什么 |
|---|---|
| 1 | 装 DSH 界面插件(拷包 + 追加挂载段 + 更新插件名清单,改前自动备份) |
| 2 | 自动找 cloudflared,并把路径/端口/上游写进 guard.json |
| 3 | 自动判断上游:本机 3081 有中间层就用它,否则用 DSH 自己的端口(默认 50142) |
| 4 | 后台拉起守卫(不占你的终端,关掉终端它也不会死) |
| 5 | 拉起公网隧道,打印当前手机链接 + 终端二维码 |
常用开关:--dry-run(只看计划)、--no-tunnel(不要公网入口)、--no-guard(只装插件)、
--port、--dsh-port、--upstream、--cloudflared、--profile。最后一行是给脚本读的 SETUP_OK {...}。
想手工一步步来(或排查问题时)
node bin/install-plugin.mjs # 1) 只装插件
node guard/guard.mjs serve --upstream http://127.0.0.1:50142 # 2) 前台起守卫(便于看日志)
node guard/guard.mjs pair --qr # 3) 链接 + 二维码
node guard/guard.mjs tunnel up --cloudflared "D:/path/to/cloudflared.exe" # 4) 公网入口markdown
node guard/guard.mjs status # 5) 复查:监听/上游/隧道/设备
命令行覆盖项(都会持久化进 guard.json):--upstream 、--port 、--bind 、
--cloudflared 、--url-file 、--tunnel-log 、--inject-panel 0|1、--supervise 0|1。
想开机自启:见 docs/deploy/(Windows 计划任务 / macOS launchd / Linux systemd --user)。
卸载(只删自己装的):node bin/install-plugin.mjs --uninstall。
快速开始
bash
1) 启动守卫(默认 127.0.0.1:8443 → 上游 127.0.0.1:3081)
node guard/guard.mjs serve
2) 查看当前手机链接(主设备 + 只读各一条,长期有效)
node guard/guard.mjs pair
要脚本友好就用 --role:最后一行是裸链接
node guard/guard.mjs pair --role owner
node guard/guard.mjs pair --role readonly
要在终端里直接打一张可扫的二维码(手机相机扫屏幕即进,不用复制粘贴)
node guard/guard.mjs pair --qr
node guard/guard.mjs qr --role readonly # 只出二维码
node guard/guard.mjs qr --svg > 手机链接.svg # 存成图片(面板里也是这张)
3) 把链接发到手机,或让手机相机扫终端/面板上的二维码
4) 公网入口交给守卫托管(自动重启、域名写入 public-url.txt)
node guard/guard.mjs tunnel up
常用命令:
bash
node guard/guard.mjs pair --reset # 重置:换新链接,旧的立即作废(已配对设备不受影响)
node guard/guard.mjs pair --code # 临时给一台设备一次性凭据(5 分钟、一次性)
node guard/guard.mjs qr [--role X] [--svg] # 打印/输出当前链接的二维码
node guard/guard.mjs devices # 列出已授权设备
node guard/guard.mjs revoke # 吊销设备(立即失效其所有会话)
node guard/guard.mjs status # 监听 / 上游健康 / 隧道 / 设备数
node guard/guard.mjs tunnel up|down|status # down 会同时关掉「自动重启」的意图
链接形态(对齐 ZCode)
- 长期有效:主设备链接与只读链接各一条,只有 --reset 才换;重置会让旧链接立刻失效,但已配对设备照常使用。
- 可重复使用:链接不是一次性的;同一台设备反复打开只会刷新它自己的凭据,不会在设备列表里堆记录。
- 只读链接是读取当前值:查看只读链接不会动主链接。
- 链接 token 优先于已有会话:带着 token 打开就是"这台设备现在以这个身份进来",会重新下发 cookie——
所以先用主链接进过 DSH 的手机,再打开只读链接会立刻降成只读(反过来也一样)。
踩过的坑:早先的逻辑是"已登录就直接放行",于是只读链接被 30 天的旧 cookie 无声顶掉,只读形同虚设。
- 打开流程:服务端校验 token → 下发 30 天 cookie → 302 重定向把 token 从地址栏抹掉;响应带 Referrer-Policy: no-referrer。
- 扫码进入:pair --qr 在终端打二维码、面板里点「二维码」在屏幕上显示、插件面板同理;
二维码由内置编码器生成(guard/qr.mjs,零依赖),逐模块与参考实现比对过(见「验证记录」)。
- 代价:token 在 URL 里,会经过聊天软件、浏览器历史、截图。所以:作废要主动(--reset),
给别人看用只读链接,临时借用一台设备用 --code(5 分钟一次性)。
- 一条链接 = 一个「席位」:链接换来的是同一个设备记录(按名字+角色复用),
所以把只读链接发给多人,他们共用一条设备记录 —— 吊销它会一起掉线。要给每个人独立席位就用 --code。
配置:$DSH_HOME/remote/guard.json(默认 ~/.dsh/remote/guard.json;当前两条链接的 token 明文存在这里,因为面板要随时显示它)。
审计:$DSH_HOME/remote/audit.jsonl(append-only JSONL)。日志:guard.log。
鉴权模型
- 配对:电脑上 pair 生成一次性码(5 分钟、用过即失效、错误尝试超限即作废)→ 手机填码换设备(临时借用要更久时用 pair --code --ttl 900)。
- 会话:配对成功下发 HttpOnly; SameSite=Lax 的会话 cookie(https 下自动带 Secure);服务端只存令牌哈希,令牌明文只在配对那一次返回。
- 角色:
- owner —— 发指令、看图、审批、下载,全权;
- readonly —— 可浏览会话、看图片、订阅事件流;写操作在服务端被拒(默认拒绝:只有一份"读语义方法"白名单,其余非 GET 一律 403,新方法不会被漏放)。
- 我是谁看得见:GET /__guard/whoami 返回当前设备与角色,面板顶部直接写出来;
只读设备看不到控制面板(那部分只给 owner),但会挂一个「只读设备」身份徽章,点开说明哪些能看、哪些会被 403 ——
不然"写入到底有没有被拦住"在界面上完全看不出来。
- 只读的边界是实测过的:拿真实 DSH 的 54 个 RPC 方法逐个打过一遍 —— 放行的 18 个全是读语义
(.list / .history / .describe / .read / .models / attachment…),其余 45 个(含 session.prompt、
workspace.delete、credentials.set、goal.、subagent.prompt)全部 403;
侧栏文件树、命令列表这类插件路由也在默认拒绝里,所以别指望只读席位能浏览文件。
- CSRF:SameSite=Lax + Origin 校验双重防护。
- 可吊销:revoke 立即删除设备及其全部会话,无需重启服务。
稳定连接
- 隧道监督:守卫每 10 秒检查隧道进程,掉了按退避(2s→60s)重启,并把新域名写进 urlFile;
实测:杀掉 cloudflared 后 6 秒内自动拉起新隧道(域名会变,见下);
- 想要 / 不想要是显式意图:tunnel up 打开、tunnel down 关掉(同时关掉自动重启的意图,不会偷偷复活);
- 守卫本身也会自愈:DSH 界面插件每 60 秒检查一次,发现守卫没在运行就把它拉回来(面板里也有「运行状态 + 一键修复」)。
(这条是实测补上的:机器/DSH 重启后守卫是普通后台进程、不会自己回来,表现就是"手机和网页突然都打不开"。)
- 域名可查:urlFile 就是给 agent/skill 读的(本项目默认写到 public-url.txt),所以"域名变了不知道新链接"这件事从根上消失;
- 失败可见:未授权返回配对页而不是空错误页;上游不可达、隧道二进制不存在/不可执行时给出明确文案;所有拒绝进审计。
- 逐环节体检:面板里的「连接体检(逐环节)」按链路顺序列出上游 → 守卫 → 隧道 → 公网可达性 → 手机链接 → 已授权设备,
每步给 正常/注意/失败 + 原因 + 修法(失败项直接告诉你点哪里);命令行同一套判断:node guard/guard.mjs doctor(别名 diag)。
部署
| 平台 | 做法 |
|---|---|
| Windows | 计划任务 / 启动目录快捷方式(Start-Process -WindowStyle Hidden) |
| macOS | launchd 用户级 plist(KeepAlive=true) |
| Linux | systemd --user unit(Restart=always) |
模板见 docs/deploy/(Windows 计划任务 / macOS launchd / Linux systemd --user)。核心只有一句:保持 guard.mjs serve 常驻。
威胁模型(请读完再用)
它保护什么:没有配对过的设备看不到、也改不了 DSH 的任何东西;只读设备改不了任何东西。
它不保护什么:
1. 配对成功的 owner 设备 = 你的电脑。DSH 的 agent 以你的身份执行命令,这是设计使然,不是缺陷;
2. 只读 ≠ 限制 agent:它限制的是"谁能下指令",不是"agent 能做什么";
3. 隧道本身是别人家的服务(如 Cloudflare Quick Tunnel),可用性与 ToS 不在本项目控制内;
4. 会话 cookie 存在浏览器里,手机丢失/被解锁就等于设备被授权——请用 revoke;
5. 明文 HTTP 下 cookie 会裸奔:对外请务必走 https 隧道(本项目的 Secure 标记只在 https 下生效)。
默认即安全:默认只绑 127.0.0.1;经隧道来的请求在没有配对过设备时除了配对页与健康检查一律拒绝(fail-closed)。
唯一例外是本机(回环)直连:不带隧道头的回环请求被当作 owner(桌面上的 DSH 页面靠这条免配对)。
所以别把公网流量原样转到 127.0.0.1:8443:某些 TCP 反代 / ssh -L 就是这么做的,它不带隧道头,
会被当成本机 → 等于把 owner 权限直接给出去。隧道请用 cloudflared 这类会带 Cf- / X-Forwarded- 头的方案。
一个必须知道的坑(本机迁移时踩到的)
坑一:中间层没有任何鉴权,而且它可能正监听在所有网卡上。
本机实测:netstat 显示中间层监听 0.0.0.0:3081,于是同一局域网(实测局域网 IP → HTTP 200、50KB 的 DSH 页面)
甚至 Tailscale 网段的设备不需要任何凭据就能进 DSH —— 中间层还会把 Host 改写回回环,等于替对方绕过了 DSH 自己的来源围栏。
所以:中间层只该绑回环(127.0.0.1),或者它自己也带鉴权;对外的入口只留守卫的 8443。
坑二:旧隧道脚本直接指向中间层 = 公网裸奔。
如果你的旧隧道脚本把隧道指向中间层端口(例如 --url http://127.0.0.1:3081),
那么它绕过了守卫。正确做法:隧道指向守卫端口(默认 8443),
即用 guard tunnel up,或把旧脚本的目标端口改成 8443。
坑三:同一个状态目录只该有一个守卫。
重复启动(例如手工起的守卫还在,又装了开机自启)时,第二个守卫会明确报错退出并告诉你换端口/换状态目录;
不要绕过这条提示硬跑第二个——两个守卫会互相覆盖 tunnel.json,可能各自拉起一个 cloudflared,域名跟着乱变。
在 DSH 界面里看到它
守卫能往走守卫的页面(手机端)注入面板,但桌面 DSH 直连自己的端口、不经过守卫,
所以桌面入口是一个独立的 DSH 插件:plugin/ —— 见其 README(含两个踩坑硬要求:
exports["./package.json"] 必须放行、客户端 chunk 用 window.__ModuleLoader__.load 手写)。
一条命令装好(自动拷包、追加挂载段、更新插件名清单,改前先备份;装完必须重启 DSH):
node bin/install-plugin.mjs # 或 npm run install-plugin
node bin/install-plugin.mjs --uninstall # 反向卸载,只删自己加的
node bin/install-plugin.mjs --dry-run # 只看计划
装好后按钮出现在右下角、EAC监控按钮的正上方,与它同列同款。
桌面端另有免装插件的取链接脚本:bin/phone-link.cmd
(弹窗显示当前链接;--reset 重置、--role readonly 取只读链接、--qr 把二维码用看图程序打开、--quiet 只打印不弹窗)。
面板本身长这样:一条当前链接 + 四个按钮(复制 / 二维码 / 只读 / 重置)+ 已授权设备列表(可逐个吊销)。
「复制」有明确反馈(按钮变「已复制 ✓」+ 状态行带时间),复制被浏览器拦下时如实提示手动选中。
目录
guard/guard.mjs 守卫:鉴权、角色强制、长期链接、二维码、隧道监督(零第三方依赖,仅 Node 内置模块)
guard/qr.mjs 内置二维码编码器(字节模式 / 版本 1–10 / L-M-Q-H / 自动择优掩码,零依赖)
guard/ui.js 守卫注入到手机端页面里的控制面板
plugin/ DSH 界面插件(桌面端的「手机链接」面板 + 控制面路由)
bin/setup.mjs 一条命令装好:插件 + 找 cloudflared + 拉起守卫与隧道 + 打印手机链接与二维码
bin/install-plugin.mjs 只装/卸插件(拷贝包、追加挂载段、更新插件名清单,改前自动备份)
bin/phone-link.mjs 桌面取链接小工具(.cmd 弹窗入口 / .vbs 静默入口)
test/ 测试:selftest(142) qr(46) panel.dom(73) plugin(55) bin(15) install(28) setup(31) docaudit(22) tunnel(11)
docs/deploy/ Windows / macOS / Linux 常驻模板
守卫自己的端点(面板与 CLI 都用它们;除 health 外都要求已配对,控制面另要 owner):
| 端点 | 作用 |
|---|---|
| GET /__guard/health | 存活探针,不鉴权(看门狗用) |
| POST /__guard/pair | 用一次性配对码换设备 |
| GET /__guard/link[?role=][&reset=1] | 单条链接 / 重置 |
| GET /__guard/links | 两条当前长期链接 |
| GET /__guard/devices | 已授权设备列表 |
| POST /__guard/revoke | 吊销某台设备(立即失效) |
| POST /__guard/reset | 重置两条链接(旧的立刻作废) |
| GET /__guard/qr[?role=] | 当前链接的二维码(SVG) |
| GET /__guard/doctor | 逐环节体检(upstream → 守卫 → 隧道 → 公网 → 链接 → 设备) |
| GET /__guard/whoami | 当前设备是谁、什么角色(任何已配对设备可问) |
| GET /__guard/status | 运行状态:设备、在线会话、隧道进程与重启次数 |
| POST /__guard/logout | 断开本机设备的会话(等于退出登录) |
| GET /__guard/ui.js | 面板脚本本体(守卫把它注入到页面里) |
跑测试:
npm test # 守卫 + 二维码 + 插件 + 桌面工具 + 文档审计(不需要任何第三方依赖)
npm run test:panel # 面板真实 DOM 行为(需先 npm i --no-save jsdom)
npm run test:docs # 文档审计:内部链接、文档-代码交叉引用、计数自洽、泄漏扫描
npm run test:tunnel # 真实 cloudflared 的隧道自愈/关闭实测(约 1–2 分钟,没有则跳过)
验证记录(每条功能都实测过)
写这个项目的过程中,实测抓出并修掉了下面这些真问题(都不是"看代码觉得对"):
| 功能 | 实测发现的问题 | 修法 |
|---|---|---|
| tunnel down | 只杀进程、不关「想要隧道」的意图 → 守护 10 秒后又把它拉回来(实测 12 秒复活) | down 同时写 superviseTunnel=false |
| 跨源预检 | OPTIONS 的 204 不带 CORS 头 → 浏览器拦下页面里的跨源 POST(面板的兜底重置/吊销会失败) | withLocalCors 对所有 writeHead 调用都合并 CORS 头 |
| 插件兜底重置 | 客户端直连守卫时没有对应端点(/__guard/reset 404) | 新增 POST /__guard/reset,并把 /dsh-remote/reset、/dsh-remote/qr 加进兜底映射 |
| 隧道启动失败 | spawn 失败只留一条 unhandledRejection,tunnel up 却报「等待域名超时」;.cmd/.bat 包装脚本会被 Node 直接拒绝 | 接住 error 事件、提前退出等待、把真实原因打出来(含 .cmd 专门提示) |
| 面板「只读」按钮 | 点开后再也收不起来(切换条件写错,用户报过) | 改成真正的开/关切换,并加进 DOM 回归用例 |
| 桌面工具 --reset --role readonly | --reset 被静默忽略 | 透传 --reset,并加进 bin 测试 |
| 只读链接形同虚设 | 手机浏览器里先开过 owner 链接(cookie 有效 30 天),之后再打开只读链接被「已登录就直接放行」无声忽略 → 服务器继续按 owner 放行写入,只读权限完全没生效(用户实测报来) | 链接 token 优先于已有会话:只要 token 有效就重新下发 cookie;另加 /__guard/whoami 与「只读设备」身份徽章,让"我在用哪条链接"在界面上看得见(含 DOM 回归用例) |
| 二维码编码器(自研) | ① 格式信息格子没在放数据前标成功能区 → 码流出现空洞,扫码器直接解不出;② 2 号掩码把「列」写成了「行」;③ N4 评分公式与标准不同 → 自动择优选错掩码 | 三处都修,然后用参考实现做逐模块比对:1332 个组合(文本 × 版本 × 纠错级 × 掩码)全部一致,并用 jsqr 端到端解码验证 |
| 配对码有效期写岔了 | 文档四处都写「5 分钟」,代码默认却是 3600 秒(差 12 倍):一个本该临时有效的凭据实际活了一小时(安全相关的值,越是这种越容易没人核对) | 默认改成 300 秒,--ttl 保持显式可选、并可用 --pair-ttl 写进配置;加断言把「文档写的那个值」钉住 |
| 第二个守卫占同一端口 | 拿到 EADDRINUSE 后被 uncaughtException 吞掉,进程继续活着:看起来在跑、一个请求都不服务,10 秒后它还会去动同一状态目录里的隧道(实测复现,进程活了十几分钟) | server.on("error") 明确报错并以非零码退出,提示"同一个状态目录只该有一个守卫"与换端口/换目录的做法;补回归用例 |
| pair --code --name X | 名字只写进了配对码记录、设备那边没人用:手机端表单留空时设备被叫成「设备-3」,命令行指定的意图静默丢失(线上实测) | 名字优先级改成「配对码里指定的 > 手机端表单填的 > 自动编号」,并加用例覆盖两条路径 |
| --port 后面没跟值 | 值被下一个参数吃掉时静默按默认值跑(实测:以为换了端口,其实还在 8443),排查时完全看不出来 | 缺值时明确告警(CLI 直接打出来,服务模式记进日志) |
| CLI 未知子命令 | guard.mjs links 这类打字错误不匹配任何分支就往下走,静默进入 serve 模式:看着什么都没发生,其实起了个前台守卫(端口占用时被异常处理器吞掉、进程一直挂着——实测留下两个活着的 links 进程) | 未知子命令明确报错、列出可用命令、并以非零码退出;补回归用例 |
| 文档自身与实际不一致 | ① 克隆命令里带着本机代理端口,别人照抄必然失败;② 插件 package.json 还写着"一次性 token 的手机链接"(早就是长期链接了);③ 插件版本 0.1.0 与主包 0.3.0 不一致;④ 目录段的测试计数停在 125/68/51;⑤ 守卫自己的 12 个端点与 diag 别名没有任何文档 | 全部改正,并把这些检查固化成可复跑的 node test/docaudit.mjs(内部链接/路径/脚本/版本/泄漏扫描 + 路由与 CLI 的文档-代码交叉引用) |
验证工具与结论:
- 守卫:test/selftest.mjs(142 项)—— fail-closed、配对码、长期链接语义、链接 token 优先于已有会话、
只读边界、owner 控制面、whoami 身份、WS 白名单、Origin/CSRF、本机直连可信与公网不可信、
页面注入与外观纠偏、二维码端点、重置端点、隧道意图、审计。
- 二维码:test/qr.test.mjs(46 项)—— 6 组黄金矩阵逐模块比对 + 3 组 RS 纠错向量 + 生成多项式 +
结构断言;黄金向量由 npm 的 qrcode 生成(仅在开发机上用来生成向量,不是运行时依赖)。
- 面板:test/panel.dom.mjs(73 项)—— 用 jsdom 真挂面板并点一遍:复制反馈、只读开关、
二维码内联、重置走 POST、吊销、关闭后遮罩一并移除、剪贴板被拒时如实报错、
逐环节体检列表、只读设备只出身份徽章不出控制面板、插件槽位注册与去重。
- 插件路由:test/plugin.test.mjs(55 项)—— 真挂 handler,reset/qr 真调守卫 CLI,准入两道闸、whoami。
- 桌面工具:test/bin.test.mjs(15 项)—— 临时 DSH_HOME,验证默认不换链接、--reset 语义、二维码落盘。
- 文档审计:test/docaudit.mjs(22 项)—— 内部链接/路径/命令是否真实存在、脚本与版本一致、
路由与 CLI 子命令的文档-代码双向交叉引用、中英结构对齐、测试计数自洽、运行态凭据与本机私有信息不外泄。
- 隧道:test/tunnel.test.mjs(11 项)—— 真实 cloudflared:拿到域名并写 urlFile、杀掉后 6–45 秒自愈、
down 后 24 秒不被拉回。
路线图
- 命名隧道 / 固定域名(Quick Tunnel 的域名每次重启都会变,是当前最大的体验缺口)
- 审计查看页、按设备限速、只读设备的读方法白名单可配置
- 完成通知推送
许可
MIT