dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
给 DeepSeek Harness 用的手机端桥接插件:手机发图、下发任务、实时看输出,支持局域网 / USB 共享 / Cloudflare 隧道外网访问。
给 DeepSeek Harness 用的手机端桥接插件: 在手机上导入图片、下发任务、实时看输出,从同一个 WiFi、USB 共享、或者外网隧道都行。
在电脑上跑 DSH,在手机上用。不用装 App,打开浏览器就是控制台。
image_generate 出的图、导出的视频…)会自动变成缩略图,点开全屏,长按就能"存储到照片";附件也走真 URL(data: 在 iOS 上长按没有"存储到照片")🌀 赛博朋克:纯 CSS 画的(霓虹地平线 + 透视网格 + 扫描线),不用传图、不占流量、任何分辨率都清晰;4.8.0 起它默认就在动,4.8.1 把动态感做足(六层:光晕漂 / 网格快速流 / 斜落的霓虹雨 / 地平线呼吸 / 扫描线 / 偶发故障闪,而且压暗层让开、气泡更透 —— 隔着气泡就看得见在动),配色跟着换成霓虹青 + 品红,面板上还有 🐢 让它静下来 的刹车(会把所有层一起停;系统开了"减弱动态效果"也会自动静) —— 它和图片背景二选一(设一个会摘掉另一个)。整套界面是毛玻璃风格(对话气泡透明、面板半透明 + blur,冷色冰蓝调子),跟你挑的背景同一个调子steer = 打断当前回合,手机上默认不用它:宁可等一轮,也不要把你正在跑的事打断)updateQueue 的 remove);已经开始处理的会如实告诉你撤不回来,绝不假装成功https://<机器名>.<tailnet>.ts.net/ 地址、开机自恢复、手机不用装任何 App);也附带 Cloudflare 快速隧道脚本做临时方案image_generate 工具去出图 —— 图因此落在会话里,能像别的素材一样在对话中看、点开、长按存相册。第一次点会把那句话先放进输入框让你过目,之后照发。⚠️ 这需要电脑上装了 image_generate 工具(单独的插件,不在本仓库里),否则 agent 只会回一句"没有这个工具 / 没有生图模型" —— 那不是手机桥坏了,见下面《手机上直接生图》一节。.html 安装页(GET /api/install?format=html ✓),同学直接浏览器打开就能读 ✓、不需要你电脑在线 ✓;手机上不能直接分享文件时会如实说并退到下载 ✓powerControl 那个开关走(见下面《定时开机》一节)版本变更记录见
CHANGELOG.md。
同学先得有 DSH(这个插件是扩展,不是独立程序;Node 22+),还要有 Git(装插件那条命令要用)。
一条命令装插件,然后重启 DSH:
dsh plugin --profile web add github:jxboop/dsh-plugin-mobile-bridge
重启后日志里会打印他自己那台电脑的手机网址:
[mobile-bridge] listening on 0.0.0.0:3081, PIN 123456
[mobile-bridge] phone url: http://192.168.1.20:3081/ab12cd34ef56ab78/ (WLAN)
🔴 Tailscale 必装(电脑上) —— 这是「从外网也能连」的前提,不装就只能和电脑在同一个 WiFi 下用:
tailscale funnel --bg 3081(第一次按它给的链接开一次 HTTPS);https://<电脑名>.<tailnet>.ts.net 写进 ~/.dsh/mobile-bridge.json 的 "publicUrl",再重启 DSH;手机浏览器打开那条网址,输 PIN 就能用。桌面 DSH 左下角的徽标上也写着完整网址 + PIN。
三点务必说清楚:
~/.dsh/mobile-bridge.json)——别把自己的网址/PIN 发给别人,那等于把你电脑的后门给对方。不需要 App Store、不需要越狱、不用装任何第三方东西 —— 用手机浏览器自带的 「添加到主屏幕」,就是一个带图标、全屏、能离线打开的 App:
iPhone(Safari):打开手机桥的网址 → 底部「分享」按钮 → 添加到主屏幕 → 添加。 Android(Chrome):打开网址 → 右上角菜单 → 安装应用 / 添加到主屏幕。
装完从桌面图标点开:没有地址栏、没有浏览器按钮,启动有加载进度条,界面是毛玻璃那套; 后台断网也能打开外壳(页面走 network-first,永远拿最新的)。
图标是 tools/make-icons.mjs 生成的真 PNG(iOS 不认 SVG 图标),改了图标设计重跑一次即可:
node tools/make-icons.mjs
说明:原生 App 做不了(iOS 打包签名要 Mac + 开发者账号 + 上架审核), 而 PWA 在体验上已经等价:独立图标、全屏、启动图、离线外壳、无浏览器界面。
前置条件:你得先有一个能正常运行的 DSH —— 这是个扩展插件,不是独立程序。需要 Node 22 以上(用的都是内置模块,没有构建步骤)。
两个会直接卡住你的坑(同学那边实测踩到,都写在这里):
- 这条命令需要 Git。 pnpm 对
github:/git+https:一律走 git 解析,不会自动改走 tarball;没装 Git 就是ERR_PNPM_GIT_RESOLVE_FAILED。装一次 Git for Windows 再重跑;不想装 Git 就用下面的 tar.gz 地址。- 必须是 1.8.2 及以上。 1.8.0 / 1.8.1 的包里缺
lib/addresses.js(files漏列), 装上去插件一加载就报找不到模块 —— 症状是手机页面打不开、一直转圈。 判断办法:看<profile>\node_modules\dsh-plugin-mobile-bridge\lib里有没有addresses.js。
# 从 GitHub 安装(`link:` 只接受本地路径,网址要用 git+https 或 github: 简写)
dsh plugin --profile web add git+https://github.com/jxboop/dsh-plugin-mobile-bridge.git
# 等价写法
dsh plugin --profile web add github:jxboop/dsh-plugin-mobile-bridge
# 没有 Git?用 codeload 的 tar.gz 地址(这条路不需要 git)
dsh plugin --profile web add "https://codeload.github.com/jxboop/dsh-plugin-mobile-bridge/tar.gz/refs/heads/main"
# 从本地目录(改代码时用)
dsh plugin --profile web add link:/path/to/dsh-plugin-mobile-bridge
tar.gz 地址不会自己更新到最新提交:它的版本串是"分支"而不是提交号,
add时 pnpm 会跳过已装的那一份。要更新得先 remove 再 add:dsh plugin --profile web remove dsh-plugin-mobile-bridge dsh plugin --profile web add "https://codeload.github.com/jxboop/dsh-plugin-mobile-bridge/tar.gz/refs/heads/main"(用
github:那条则可以直接重跑add更新 —— pnpm 会重新解析到最新提交。)
装完重启 DSH,日志里会出现:
[mobile-bridge] listening on 0.0.0.0:3081, PIN 123456
[mobile-bridge] phone url: http://192.168.1.20:3081/ab12cd34ef56ab78/ (WLAN)
把上面那条安装命令原样再跑一遍就行,不需要先卸载:
dsh plugin --profile web add github:jxboop/dsh-plugin-mobile-bridge
然后重启 DSH。(dsh plugin 是 pnpm 的转发器;对分支形式的 git 依赖,add 会重新解析到最新提交。实测 0.2.2 → 0.3.0 可直接升级。)想确认装到了哪一版:看 <profile>/node_modules/dsh-plugin-mobile-bridge/package.json 的 version。
版本号从 0.9.4 起才是准的。0.7.0~0.9.3 那一段只在提交标题里写了版本,
package.json一直停在0.6.7—— 所以那几版看不出新旧,照"重跑一遍安装命令 + 重启"来判断即可。
版本怎么排(2026-10-09 起):加功能 = 进一位小数(1.3.17 之后的下一个功能是 1.4.0,
再下一个 1.5.0…);修 bug / 只改文案 = 补丁位(1.4.1、1.4.2…)。一次提交里两者都有时按功能算。
每个版本都带 package.json 版本、CHANGELOG.md 一段、git tag v<版本> 和 Release。
~/.dsh/mobile-bridge.json(Windows:%USERPROFILE%\.dsh\mobile-bridge.json):
{
"version": 1,
"port": 3081,
"pin": "123456",
"pathSecret": "ab12cd34ef56ab78",
"tokenTtlHours": 12,
"bindTokenToIp": false,
"elevationMinutes": 60
}
pin 和 pathSecret 都是自动生成的,别外传。 在 DSH 界面左下角的徽标上能直接看到完整网址和 PIN。
| 字段 | 默认 | 说明 |
|---|---|---|
port | 3081 | 监听端口 |
pin | 随机 6 位 | 登录密码 |
pathSecret | 随机 16 位十六进制 | 所有路由都藏在这个随机段后面,没有它一律 404 |
tokenTtlHours | 12 | 登录令牌有效期(上限 14 天) |
bindTokenToIp | false | 是否严格绑定令牌的来源 IP。默认 false:地址变化只记录告警(在 /api/bootstrap 的 foreignUses 里可见),不吊销登录。设 true 则换地址即吊销 —— 移动网络下不可用(运营商 NAT、IPv6 隐私扩展会不停换地址,手机会被反复踢下线),只适合固定网络 |
elevationMinutes | 60 | 输一次 PIN 后,多久内可以"指挥 DSH 干活" |
crashProbe | false | 排查用,平时别开。 设为 true 会把宿主的 uncaughtException / exit 事件写进 ~/.dsh/dsh-crash.log(含每 30 秒一条内存心跳)。它跑在宿主进程里、会注册全局处理器并持续写盘,所以默认关闭。 |
answerOnPhone | false | 让手机回答 agent 的提问(ask_user_question)和工具审批。默认关:开着时"正在看这个会话的手机"会抢在桌面前面拿到提问,桌面要等 120 秒才轮到。没有手机在看这个会话时,无论开关如何都是桌面先拿到。 |
pcControl | 关 | 手机上控制电脑(4.5.0 / 4.5.1):静音 / 音量 / 亮度 / 开-关蓝牙 / 看电量与联网 / 看蓝牙设备 / 断开某一台蓝牙设备(4.14.0)。默认关,改完立刻生效、不用重启。开着时"让电脑动手"的动作仍要 PIN 提权。⚠️ 亮度只能调笔记本内置屏(台式机 / 外接屏会如实说调不了);蓝牙设备列表走系统原生接口(约 0.8 秒、带连接状态);「连接某台设备」底层接口实测一律被拒(rc=87),所以列表里点一台就是打开系统蓝牙设置;「断开」以回读到的连接状态为准(详见下面《蓝牙:能「断开」,连不回来》一节)。 |
powerControl | 关 | 手机上让这台机器动手:关机 / 重启 / 解锁,以及 4.13.0 起的「定时开机」(到点在睡眠里唤醒 ✓)。默认关,改完立刻生效、不用重启;每次动作仍要 PIN 提权。⚠️ 完全关机后开机软件做不到 ✗(详见下面《定时开机》一节)。 |
这是设计如此,不是掉线。手机上有两种状态:
提权默认保持一个小时(elevationMinutes —— 2026-10-14 用户要求:从 15 分钟改成 60 分钟),过期后第一次动手会重新弹出 PIN 门;宿主重启后恢复的会话同样不带提权。觉得还不够就把它调大,上限 240 分钟:
{ "elevationMinutes": 240 }
如果你看到"需要重新输入 PIN"却找不到输入框,那是 0.2.0 及更早版本的 bug(页面只认 401、不认服务器用来表达提权的
403 + needPin,于是提示了却没有门)。0.2.1 起已修复:过期时页面会直接把 PIN 门打开。
首次启动会弹 Windows 防火墙提示。**只勾"专用网络"**即可(手机和电脑在同一 WiFi 时够用)。若要走外网隧道,勾"公用网络"。
要在局域网之外(流量 / 别的 WiFi)也能连,Tailscale 是必装的前置 ✓(不装就只能同一 WiFi ✗)。装好之后三条路,按这个顺序试:手机也装 Tailscale(最稳)→ Tailscale Funnel(手机不用装东西)→ Cloudflare 快速隧道(临时)。
📤 手机上的「分享 → 复制全文 / 网页版」给的那份零基础安装文案,5.0.0 起就是按这一节写的 ✓(Tailscale 必装 + Funnel +
publicUrl+ 关 WiFi 验一次 ✓)—— 分享给朋友时直接发那份就行。📌 手机之前能打开、突然打不开了:先看是不是本机局域网 IP 变了(校园 WiFi 漫游 / DHCP 续租很常见 ✓ —— 手机上的旧网址会连不上,桥这边一条请求都收不到 ✓)。扫桌面徽标(左下角)的二维码重新打开一次即可 ✓; 想以后不再遇到:手机「加到主屏幕」时用固定的外网地址(
*.ts.net那条)✓,局域网 IP 会随网络变 ✗。🔎 桥的「外网隧道」自检现在会真的从公网入口去测(不再被本机 Tailscale MagicDNS 骗 ✓)。 如果自检/徽标说「外网入口不通」,在电脑上跑
tailscale funnel --bg 3081重开一次即可 ✓ (WiFi 漫游 / 换 IP 之后它偶尔会卡在这 ✓ —— 5.0.3 起自检会直接把这个命令写给你 ✓)。
电脑和手机都装 Tailscale、用同一个账号登录,然后手机浏览器打开:
http://<电脑的 tailnet IP>:3081/<pathSecret>/
电脑的 tailnet IP 怎么拿:托盘图标点开就能看到(形如 100.122.188.30),或者命令行 tailscale ip -4。
<pathSecret> 是网址密钥段 —— DSH 窗口的日志里、或桌面 DSH 左下角徽标上都有完整网址。
为什么它最稳:流量走 tailnet(设备间端到端加密),完全不经过 Funnel 的公网入口。
2026-10-08 实测过一次:Funnel 的公网入口有两个 IP,其中一个偶发回不到机器
(外部 6 个国家的节点复测,一半报 Broken pipe)—— 手机就会间歇性"网络不通",
而同一时刻 tailnet 直连是好的。
代价:手机要多装一个 App(iPhone 需要非中国区 Apple ID),并且 Tailscale 要在后台常驻。
为什么推荐:Cloudflare 免费隧道的域名每次重启都变,旧的永久失效;Funnel 给的是一条 https://<机器名>.<tailnet>.ts.net/ 的固定公网地址 —— Tailscale 服务随开机自启、配置存在 daemon 状态里,重启电脑后什么都不用做,网址照旧。而且它是标准 HTTPS,手机上不用装任何客户端(这点对 iPhone 尤其重要:Tailscale 的 iOS app 没有上架中国区 App Store)。
# 1. 装 Tailscale 并登录:https://tailscale.com/download
# 2. 首次开 Funnel 需要去控制台同意一次(只此一次),命令会把链接打出来:
tailscale funnel 3081
# Funnel is not enabled on your tailnet.
# To enable, visit: https://login.tailscale.com/f/funnel?node=xxxxx
# 3. 同意之后再执行一次,就常驻后台了:
tailscale funnel --bg 3081
# Available on the internet:
# https://<机器名>.<tailnet>.ts.net/
# |-- proxy http://127.0.0.1:3081
# 4. 手机访问(结尾必须带密钥段):
# https://<机器名>.<tailnet>.ts.net/<pathSecret>/
不用时关掉:tailscale funnel --https=443 off
⚠️ 选了一条路就别同时跑
tools/tunnel-start.ps1—— 它会把配置里的publicUrl改回那个每次都变的 cloudflared 域名。安全上如实说:Funnel 和免费隧道一样是公网地址,保护来自随机密钥段 + PIN 这两层;区别只是域名固定、不会更容易被猜到。要"只有自己的设备能进",就用方案 A(设备之间端到端加密、不经公网入口)。
Funnel 不稳时怎么办:今天实测到的症状是"手机说网络不通",但电脑这边一切正常(
tools/tunnel-status.ps1也是 OK)。 先tailscale down再tailscale up(不需要管理员)常常能救回来;实在不行重启电脑,或直接用方案 A。
tools/ 下有一套脚本:不需要公网 IP、不需要改路由器、不需要注册账号。代价是域名每次重启都变,旧的永久失效 —— 只适合临时开一下。
# 1. 下载 cloudflared.exe 放到 tools/ 目录
# https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-windows-amd64.exe
# 2. 启动隧道(会打印完整网址并复制到剪贴板)
powershell -NoProfile -ExecutionPolicy Bypass -File tools\tunnel-start.ps1
# 3. 不用时关掉
powershell -NoProfile -ExecutionPolicy Bypass -File tools\tunnel-stop.ps1
其它工具:
| 脚本 | 用途 |
|---|---|
tools/tunnel-status.ps1 | 体检:隧道现在是死是活(进程、端口、外网实测),手机上出现错误页时先跑它 |
tools/show-phone-url.ps1 | 一个可复制的小窗口,显示外网/局域网网址 + PIN(带刷新按钮) |
tools/revoke-phone.ps1 | 应急:一键吊销所有手机登录(两步确认,防误触) |
agent 卡在 ask_user_question 上等你选,或者某个工具调用需要你点头时,手机上会直接出现一张卡片:选项按钮、可多选、也能自己写一段话;审批则是「允许一次 / 拒绝」两个键。答完 agent 立刻继续,不用你跑回电脑前面。
默认关闭,打开方式:
{ "answerOnPhone": true }
打开后改配置需重启 DSH(或重载插件)生效。
它怎么决定谁来答:
| 情况 | 谁作答 |
|---|---|
| 有手机正在看着这个会话 | 手机(桌面等 120 秒才轮到,防手机没答导致卡死) |
| 没有手机在看这个会话 | 桌面,和我们没插手时一模一样 |
| 手机答了,别的手机也开着 | 先答的算数,其余卡片自动撤掉 |
| 提问发生时手机不在场(在别的会话 / 页面被系统挂起 / 正在重连) | 桌面先答;手机上事后会出现一条能点的横幅,切过去还能补答(4.14.1) |
4.14.1「回来还能看见、还能答」(用户 2026-10-16 报「我看不见你的问题手机端」✓):
userQuestions.continued)也告诉手机(GET /api/waiting):
手机显示「另一条会话在等你回答」✓,点进去直接看到那张卡 ✓ 并且答得出去 ✓(走宿主自己的 callId ✓,
不是桥上那个 120 秒就失效的临时 id ✗);页面从后台回到前台时也会主动补问一次 ✓;available:false ✓、页面上写「读不到」✗ ——
绝不说成"没有提问" ✗。ask(question) -> 桌面:现在没有手机在看 session-xxx(此刻有人在看:session-yyy) ✓),
以及手机开始看 / 不看哪个会话 ✓。几个已经处理好的细节:
textContent,不是 innerHTML),模型塞不进脚本。z-index:50 的全屏纸整个盖住,
而宿主只等 120 秒就退回桌面 —— 桌面上两分钟后才冒出来,看起来就像"手机上没提示"。)已知限制:加载页和断线整屏(那两屏比卡片高)时不会显示卡片 —— 那属于"还没连上/断开了", 连上以后卡片自然回来。
手机和电脑之间的连接有三种坏法,每一种都会在页面顶部弹出一条说明,而不是像以前那样只把一个 9 像素的圆点变红:
| 情况 | 页面会说什么 |
|---|---|
| 连接被切断(隧道没了 / 网络断了) | 「和电脑断开了」+ 可能的原因 |
| 连接还在、但对面不再说话(电脑睡眠最典型) | 「好像和电脑失去联系了」+ 最后收到消息是多久之前 |
| 提问过期、或已在别处回答 | 卡片自己撤掉,并说明原因 |
而且它会给出两条能点的出路:
bootstrap 里给(含密钥段的完整 URL)。注意换地址等于换站点,cookie 带不过去,要重输一次 PIN。桌面那一侧也不再装死:插件每 120 秒从外网探一次自己的隧道,连续两次探不通就把徽标上的外网地址标红成「⚠️ 本机自检没连上公网(手机可能仍能连)」,
并往 ~/.dsh/mobile-bridge.log 写一行。恢复时也会写一行。
⚠️ 4.16.1 起措辞改准了 ✓:探针是在这台电脑上跑的 ✓,本机 DNS / 网络抖一下就会失败 ✓,
不等于手机连不上 ✗(旧文案写的是「隧道已断,手机连不上」✗ —— 2026-10-17 就是这么把用户误导的 ✓)。
要逐项结论就跑 tools\tunnel-status.ps1 ✓:它会先认路线(外网地址是 *.ts.net 就按 Tailscale Funnel 查 ✓,
不再去要求 cloudflared ✗),最后以外网实测为准 ✓。
这两个错都不是插件的问题,而是隧道没了。
| 错误 | 含义 |
|---|---|
| 1033 Cloudflare Tunnel error | 这个主机名曾经是一条隧道,但现在没有任何隧道连着它 |
| 1016 Origin DNS error | 这个主机名已经完全解析不到了(隧道早就没了) |
最常见的两个原因:
怎么办:
# 先体检:一眼看出是进程没了、端口没起、还是网址失效
powershell -NoProfile -ExecutionPolicy Bypass -File tools\tunnel-status.ps1
# 网址失效就重拿一个(先停再起,确保拿到全新的)
powershell -NoProfile -ExecutionPolicy Bypass -File tools\tunnel-stop.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tools\tunnel-start.ps1
tunnel-start.ps1 会真的从外网打开一次再告诉你成功了 —— 如果它说"没能通过外网验证",那就是现在把网址给手机也没用,直接照它列的原因排查,别浪费时间。
想要一个永不变的网址,只有两条路:Cloudflare 命名隧道(要有自己的域名 + 账号),或者用 Tailscale / ZeroTier 这类组网工具(推荐:地址固定、端到端加密、且完全不暴露到公网)。
⚠️ 免费快速隧道的网址每次重启都会变,而且是一条公开网址 —— 拿到的人都能看到 PIN 登录页。手机桥有 PIN 认证和限速兜着,但别把网址发到群里。
这个桥可以经隧道暴露到公网,所以"安全"必须是能验证的,不是一句承诺。
| 防护 | 说明 |
|---|---|
| 随机路径前缀 | 所有路由藏在 /<pathSecret>/ 后面。无密钥路径一律 404 |
| 限速的钥匙不可伪造 | 只在 socket 对端是 loopback(即自家 cloudflared)时才读转发头;cf-connecting-ip 优先,x-forwarded-for 取最后一段(自家代理追加的),绝不取第一段(那是调用方可控的) |
| 两级登录限速 | 每地址 12 次/10 分钟 + 所有人合计 60 次/10 分钟 |
| 令牌绑定来源 IP | 偷走的 cookie 换一台机器就是废纸 |
| 写操作需要再验 PIN | cookie 只能读;下发指令/取消/新建会话需要一小时内输过一次 PIN |
Cookie Secure 条件化 | HTTPS 下带 Secure,明文 HTTP 下不带(否则局域网登录会静默失效) |
| 令牌 12 小时过期 | 上限 14 天,/api/logout 立即吊销 |
| SSE 连接上限 | MAX_LIVE_STREAMS = 6,超限 503 |
| 网络层超时与连接上限 | headersTimeout 20s(挡 slowloris)、requestTimeout 180s、maxConnections 64 |
| 统一安全响应头 | nosniff、X-Frame-Options: DENY、cache-control: no-store、Referrer-Policy: no-referrer(防止密钥路径经 Referer 泄漏) |
| 不回显内部错误 | 细节只进日志,不回给调用方 |
| 探测留痕但密钥不落盘 | 无密钥请求记为 GET <no-secret> <- ip;日志里永远不出现密钥路径 |
| 登录审计 | 成功/失败的每一次登录都落一行(~/.dsh/mobile-bridge.logins.jsonl,超 256KB 时成功的那几行优先留 —— 否则约 1300 次错 PIN 就能把真记录挤光、面板假报"没有陌生设备")。失败尝试不算"已知设备" —— 否则攻击者先试错一次就把自己洗白;"新地址第一次成功登录"会在桌面角标上报警,并写明"走外网时这是隧道那端的地址" |
| 看审计要现输 PIN | GET /api/logins 走 x-dsh-pin 头(绝不进 URL):这份账里有这台电脑的网络史,和"看剪贴板 / 截图"同一级。current("这台手机")按令牌判,不按 IP(IP 会被 warn-only 路径改写) |
| 踢设备要现输 PIN | 手机端「踢掉其它设备」= 收回权限,仍需证明是本人(和"删除会话"同级);默认保留当前这台。桌面那个 /revoke 照旧只认本机(loopback),密钥泄漏也踢不掉你 |
| 诊断包脱敏 | GET /api/diag 生成的文本里,PIN 与 pathSecret 一律替换成 <PIN> / <密钥段>,另有 Bearer … / sk-… 两条通用规则;读不到配置时在包里写明"没能确认脱敏";每份日志只截最近 48KB —— 它是要发出去的东西 |
| 自检的隧道那一条是"现探" | state.tunnel.ok 是三态(null = 没探过):点一次 🩺 就现探一次,免费隧道换域名后会重新探 —— 绝不说"没测过"就是"可达" |
node test/security.mjs
51 项断言:限速钥匙的单元断言、密钥路径门禁、每个接口的未授权拒绝、限速生效、cookie 标志、令牌有效期、注销吊销、响应头、SSE 上限、附件不泄漏、日志脱敏。退出码非 0 即有失败。
其中只读的那半(密钥门禁、未授权拒绝、响应头、日志脱敏)打的是你正在用的那台桥;破坏性的那一半(连猜 15 次 PIN、真登录拿 cookie、打满 SSE 上限)会在临时目录里另起一台一次性实例,跑完即拆 —— 因为登录限速有一份全局额度,是和手机共用的,在真身上打就是拿手机当靶子。早期版本正是这么把用户锁在门外的。
要连真实 Cloudflare 边缘一起验(会消耗本机出口 IP 的登录额度,10 分钟后自愈):
$env:BRIDGE_TUNNEL_URL='https://<你的隧道域名>'
node test/security.mjs
其它测试。先装一次测试依赖(jsdom,只有 UI 测试用它;插件本身零运行时依赖):
npm install
| 文件 | 内容 | 需要 |
|---|---|---|
test/crashprobe.mjs | 崩溃探针开关:默认不碰宿主全局状态、开启后能在配置重写中存活(17 项) | — |
test/harness.mjs | 桥的端到端行为:PIN 门、cookie、prompt 组装、SSE 扇出、取消、附件、新建会话、余额缓存、重启保活(含"恢复的会话可读但不可写 = 403+needPin")、注销、桌面角标注入与 start() 不抛错、普通请求取对话 /api/transcript(巨型历史裁剪 / 一堆短字符串拼成的 stream 也要丢 / 缺 sessionId 400 / 客户端半路掐断后桥还活着)、子会话的错误帧带 unsupported 且从列表里消失,以及手机作答全链路(提问推到流上 / POST api/answer 收答案 / 防重放 / 审批 outcome 校验 / 取消不惊动桌面 / 刷新补推)(608 项) | — |
test/ui.mjs | 手机页面的 DOM 与交互:含"没有请求跑出密钥段"、"提权过期时必须把 PIN 门打开"、运行中同时给出「插话」与「停止」并确认插话已收下、换会话后「在跑」状态跟着重置、首次说明页(自动弹出 / 讲清四件事 / 点了就记住 / 真有提问时让位)、子会话不许谎报断线、也不再重连、迟到的旧会话回包不许贴到新会话上、localStorage 写不进去(无痕模式)也不许把页面搞死、Ctrl+Enter 连按两下只发一次、提问与审批卡片(渲染、作答编码、防 XSS、重推保草稿、单选与自定义互斥、作废撤卡、跨页签可见),以及断线提示条(弹条、说清原因、给局域网兜底、心跳到达后收起)、面板主题注入 / 撤除(841 项) | jsdom |
test/addresses.mjs | 地址排序与分类(16 项) | — |
test/shot.mjs | 真浏览器截图 + 布局/滚动体检(scroll: 那行会报能不能滚到顶底、滚动中位重取后位置还在不在、有没有块在估算高度) | Windows + Edge |
test/switch-bench.mjs | 真浏览器量"切会话":切到没打开过的会话多久有内容、切回来是不是那一刻就有、这次切换下载了多少字节 | Windows + Edge |
test/reopen-bench.mjs | 真浏览器量"从别的窗口点回来":把网络延迟拉到 2 秒再重载,报"对话出现"与"界面显示"两个时刻 | Windows + Edge |
test/scroll-live.mjs | 真浏览器复现"回弹":开一个临时会话让它真的流式输出一千多行,一边往上滚一边采样 15 秒(回弹次数 / scrollHeight 变动 / 最长掉帧) | Windows + Edge |
test/phone-diag.mjs | 真浏览器诊断:抓页面异常、控制台、网络状态码 | Windows + Edge |
test/boot-probe.mjs | 加载页进度条探针:网络节流下每 250ms 采样一次,回答"百分比会不会冻住"(这类 bug 本机复现不了,只有慢网络才看得见) | Windows + Edge |
node test/package.mjs && node test/harness.mjs && node test/qr.mjs && node test/ui.mjs
node test/security.mjs && node test/crashprobe.mjs && node test/addresses.mjs && node test/offline.mjs
node test/platform.mjs
九个套件共 1795 项(package 186 · harness 614 · qr 12 · ui 852 · security 54 · crashprobe 17 ·
addresses 16 · offline 17 · platform 27)。另有 test/voice.mjs 33 条语音草稿 —— 语音那一版
按用户要求先跳过,文件留在仓库里、能单独跑,但不并进这项门槛(tools/check-counts.mjs 只管上面九个)。
改完必跑全套 ✓ —— 只跑某一个会漏掉内容规矩与界面回归(2.7.2 就是这么漏了两版的 ✓)。
手机桌宠(小鲸 / DeepSeek酱)在 4.15.0 上线、在 4.17.0 下线 ✓(用户 2026-10-19: 「手机桌宠不要了,电脑桌宠不用动」✓):
GET /api/pet / GET /api/pet/asset)也一起删了 ✓;~/.dsh/mobile-bridge-pets/<名字>/ 里的立绘 / 设定图 / pet.json
还在他自己的电脑上 ✓(那是他自己的东西 ✓ —— 删功能不删素材 ✓);CHANGELOG.md 的 4.15.0 / 4.15.1 / 4.16.1 / 4.17.0 几节 ✓。用户 2026-10-16 原话:「还有蓝牙功能的断开设备连接,现在只有连接设备」✓ —— 这一版补上了「断开」那一半 ✓。
| 动作 | 能不能做 | 说明 |
|---|---|---|
| 看设备列表 + 真实连接状态 | ✅ | 系统原生接口(约 0.8 秒) |
| 断开某一台(4.14.0) | ✅ 能(部分设备) | 见下面两条 ⚠️ |
| 连上某一台 | ❌ 不能 | Windows 没有可靠接口(BluetoothSetServiceState(ENABLE) 实测一律 rc=87);面板里点一台 = 打开系统蓝牙设置 ✓ |
| 取消配对(忘记设备) | ❌ 故意不做 | 那是 BluetoothRemoveDevice:要重配一遍,代价太大;本插件不动配对关系 ✓ |
⚠️ 两条必须知道的实话(都是 2026-10-16 在真机上验出来的 ✓):
断开成功之后那一行会变成"未连接" ✓(那一步是重新读一次设备列表得到的 ✓ —— 不是页面自己改的 ✗);要连回来仍然只能去系统蓝牙设置里点一下 ✓。
用户 2026-10-16 原话:「既然有关机那么可以做一个对应的定时开机功能」✓。
这个名字最容易让人误会,所以先把两句话放前面(手机面板上写的也是这两句):
| 机器当时的状态 | 到点能不能自己开 |
|---|---|
| 睡眠 (S3) | ✅ 能 —— 系统还活着,计划任务带「唤醒定时器」(WakeToRun)就有权把它叫醒 |
| 休眠 (S4) | ✅ 能(这台机器上休眠没启用 —— 面板会照实说,这是另一件事) |
| 完全关机 (S5) | ❌ 不能 —— 系统没在运行,任何软件都不存在 |
⇒ 完全关机之后到点开机,只能靠主板 BIOS/UEFI 的 RTC 定时开机(各家叫法不同:RTC Alarm Power On /
Resume by RTC Alarm / Wake on RTC / Power On By RTC),设好之后不需要任何软件。
而"BIOS 里设没设",Windows 这边没有任何接口能读到 ✗ —— 面板会明说查不到,不给假安心;
任何人拿这个动作来问(接口是公开的),服务端也会如实拒绝并把"为什么做不到"说出来 ✓。
这也是插件不摆那个键的原因 ✗(摆一个按了没反应的键 = 骗人)。
怎么用(在「📊 状态盘」上,紧挨着「电脑端一键动作」):
"powerControl": true(见上表 —— 定时开机和"关机 / 重启"共用一个开关:都是"让这台机器自己动";默认关,改完立刻生效);07:30(今天已经过了就定明天)/ 10-17 07:30(今年过了就定明年)/ 2026-10-17 07:30(必须是将来,过去的一律拒绝)→ 点「设它」;只响一次 ✓(同一时刻只留一个 ✓,再设一个就换掉它);「取消这个定时唤醒」删完会回读确认才说删掉了 ✓。
powercfg /waketimers("系统里还排着哪些唤醒定时器")要管理员才能读 —— 读不到时面板说读不到 ✗,
不当成"没有" ✗(这两件事完全不一样)。
在手机上看见一段话、一个链接、一张图,想直接丢给电脑上的会话 —— 不用切到桥的界面,**在原来的 App 里点「分享」**就行。
为什么做成"快捷指令 + 一条短链":手机上这个网页已经很大了(10.8k 行), 再加按钮就要跟"面板重渲染"那套脾气搏斗(3.5.0 为了一个「解锁」按钮折腾了五轮 ✗)。 短链那条路完全不碰页面 —— 更稳,也更快。
在这台电脑的 ~/.dsh/mobile-bridge.json 里加一行:
{ "shareIntake": true }
(改完立刻生效,不用重启 ✓;不想要了删掉/改回 false 就立刻关 ✓。)
手机上打开桥 → 工具键那排的 🩺 一键自检 → 找到**「分享到桥」那一行 → 抄下它给出的短链** (那条链里带着这段密钥,别发给别人 ✓)。
在 iPhone 的快捷指令里新建一个快捷指令:
打开「在分享表单中显示」;
「接收」选 文本、网址(想分享图片再加 图像);
加一个「获取 URL 内容」:方法 POST,URL 填第 2 步抄来的短链, 请求体选 JSON,内容形如:
{ "pin": "你的六位 PIN", "text": "分享进来的文本" }
(有网址就再加 "url": "分享进来的网址";有图片可先「Base64 编码」再放进 "images": [{"mediaType": "image/png", "data": "…"}]。)
保存 ✓ —— 之后在