dsh-remote-bridge 2.5.1 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <img alt="Version" src="https://img.shields.io/badge/version-v2.5.1-087f8c">
12
+ <img alt="Version" src="https://img.shields.io/badge/version-v2.6.0-087f8c">
13
13
  <img alt="DSH plugin" src="https://img.shields.io/badge/DeepSeek_Harness-plugin-1f2328">
14
14
  <img alt="Platforms" src="https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-586069">
15
15
  <img alt="License" src="https://img.shields.io/badge/license-AGPL--3.0-2da44e">
@@ -102,10 +102,10 @@ dsh plugin --profile web add dsh-remote-bridge
102
102
  dsh plugin --profile web add dsh-remote-bridge --registry https://registry.npmmirror.com
103
103
 
104
104
  # 2. tarball from the GitHub release
105
- dsh plugin --profile web add https://github.com/westanke/dsh-remote-bridge/releases/download/v2.5.1/dsh-remote-bridge-v2.5.1.tgz
105
+ dsh plugin --profile web add https://github.com/westanke/dsh-remote-bridge/releases/download/v2.6.0/dsh-remote-bridge-v2.6.0.tgz
106
106
 
107
107
  # 3. tarball from the Gitee release (faster from mainland China)
108
- dsh plugin --profile web add https://gitee.com/westanke/dsh-remote-bridge/releases/download/v2.5.1/dsh-remote-bridge-v2.5.1.tgz
108
+ dsh plugin --profile web add https://gitee.com/westanke/dsh-remote-bridge/releases/download/v2.6.0/dsh-remote-bridge-v2.6.0.tgz
109
109
 
110
110
  npx @deepseek-ai/dsh web
111
111
  ```
@@ -122,7 +122,7 @@ Restart DSH after installation, then open settings — The section is titled **
122
122
  To install a downloaded release archive:
123
123
 
124
124
  ```sh
125
- dsh plugin --profile web add ./dsh-remote-bridge-v2.5.1.tgz
125
+ dsh plugin --profile web add ./dsh-remote-bridge-v2.6.0.tgz
126
126
  ```
127
127
 
128
128
  ## First connection
@@ -352,28 +352,31 @@ pnpm check
352
352
  pnpm pack
353
353
  ```
354
354
 
355
- `pnpm check` covers type-checking, documentation consistency, and tests. It currently passes **30 test files / 226 cases**, plus **4 browser-layer cases** (Playwright measuring real computed styles).
355
+ `pnpm check` covers type-checking, documentation consistency, and tests. It currently passes **67 test files / 557 cases**, plus **8 browser-layer cases** (Playwright measuring real computed styles, including the 375px narrow-viewport contract).
356
356
 
357
- `npm pack` produces roughly a **0.55 MB** archive (v2.5.0 measured 572,951 bytes, SHA-256 `38f25c10…299c93`): 2.2.0 dropped the standalone page and its editor dependencies and turned source maps off, down from 1.72 MB; it has since grown slightly with projection passthrough and the permission BFF.
357
+ `npm pack` produces roughly a **0.61 MB** archive (v2.5.2 measured 635,325 bytes, SHA-256 `18d36904…c8095`): 2.2.0 dropped the standalone page and its editor dependencies and turned source maps off, down from 1.72 MB; it has since grown slightly with projection passthrough, the permission BFF, and the five-dimension hardening.
358
358
 
359
359
  v2.5.0 was verified on DSH **0.2.1-rc.2** on real hardware: all 14/14 host services injected, the three jobs endpoints exercised, and the permission catalog returned the kernel's real three presets. The development baseline is upstream `dsh-v0.2.1-rc.2` (commit `687f282e8625c6b8d11ff58211a90524be2e4317`); see `engines.dsh` above for the supported range. CI covers Windows, Ubuntu, and macOS.
360
360
 
361
- **v2.5.1 verification status**: 47 test files / 398 cases green (including the tunnel-topology
362
- management regression, the desktop-panel pass-through assertion, and the `it.fails` conversions),
363
- `typecheck` and `docs:check` pass, and the audit report's §3.1 attack chain was replayed against a real
364
- `RemoteApiServer` (now 403 for every step). **Not yet verified on real hardware**, and not published to
365
- npm or either release site — i.e. "not published ≠ not verified ≠ users have it".
361
+ **v2.6.0 verification status**: 70 test files / 593 cases green, plus 8 Playwright browser cases
362
+ (including the 375px narrow-viewport contract); `typecheck`, `docs:check`, and the build all pass.
363
+ All three rounds of real-hardware tunnel regressions were reproduced and verified on a
364
+ React18 + Playwright click harness (one-click token issuance, the stop-tunnel escape hatch, and the
365
+ locked state when re-entering settings), then confirmed by manual testing on the user's machine.
366
366
 
367
367
  ## Project
368
368
 
369
- - Version: `v2.5.1`
370
- - **v2.5.1 is a security release**: it closes a remote privilege escalation where `/manage/**`
371
- and the loopback admin header were granted to any request whose TCP source looked like loopback —
372
- which is exactly what a Cloudflare tunnel (or any reverse proxy) makes every public request look like.
373
- It also fixes `.heif` handoff images, which were always rejected with 415 by the read-only route.
374
- After upgrading, **`/manage` returns 403 whenever the tunnel is running** (the only exceptions are the
375
- read-only tunnel-status endpoints, which hide the tunnel URL from non-local callers); the desktop panel
376
- is unaffected. See `docs/project/SECURITY.md` §「管理面的信任边界」.
369
+ - Version: `v2.6.0`
370
+ - **v2.6.0 is the plugin-settings + IM-entry release**: it adds the per-plugin settings namespaces (`GET /api/v1/settings/namespaces`, `PATCH .../{ns}`) so the app can build the form from the kernel schema, and two entry points for "I am out and the desktop is still locked" — the host command `/tunnel` and the tool `open_tunnel_config`; plus one-tap pruning of unused devices, the audit page removed, and a guard so a failing optional registration cannot tear down the whole plugin context.
371
+ - **v2.5.2 is a comprehensive hardening release**: it lands the five-dimension audit
372
+ (security, features, performance, UI, button feedback) — including the unauthenticated
373
+ `debug/host-services` information disclosure fix, dual-bucket rate limiting, broadcast backpressure,
374
+ weak ETags, and audit retention — and makes "start tunnel & generate token" complete inside a single
375
+ request. While the tunnel runs, the management surface is safely locked with only "stop tunnel"
376
+ available, restoring automatically afterwards. v2.5.1's security model (403 on `/manage` while a tunnel
377
+ runs, the four-layer admin criteria) is unchanged; see `docs/project/SECURITY.md` §「管理面的信任边界」.
378
+ - **v2.5.1 was the security release**: it closed the remote privilege escalation and the `.heif`
379
+ handoff-image 415 bug described in the changelog.
377
380
  - Author: upstream [Hakunm](https://github.com/Hakunm); this fork maintained by [westanke](https://github.com/westanke)
378
381
  - Repository: [GitHub](https://github.com/westanke/dsh-remote-bridge) · [Gitee](https://gitee.com/westanke/dsh-remote-bridge)
379
382
  - npm: [`dsh-remote-bridge`](https://www.npmjs.com/package/dsh-remote-bridge) ([mainland-China mirror](https://registry.npmmirror.com/dsh-remote-bridge))
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <img alt="Version" src="https://img.shields.io/badge/version-v2.5.1-087f8c">
12
+ <img alt="Version" src="https://img.shields.io/badge/version-v2.6.0-087f8c">
13
13
  <img alt="DSH plugin" src="https://img.shields.io/badge/DeepSeek_Harness-plugin-1f2328">
14
14
  <img alt="Platforms" src="https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-586069">
15
15
  <img alt="License" src="https://img.shields.io/badge/license-AGPL--3.0-2da44e">
@@ -93,10 +93,10 @@ dsh plugin --profile web add dsh-remote-bridge
93
93
  dsh plugin --profile web add dsh-remote-bridge --registry https://registry.npmmirror.com
94
94
 
95
95
  # ② GitHub Release 的 tarball
96
- dsh plugin --profile web add https://github.com/westanke/dsh-remote-bridge/releases/download/v2.5.1/dsh-remote-bridge-v2.5.1.tgz
96
+ dsh plugin --profile web add https://github.com/westanke/dsh-remote-bridge/releases/download/v2.6.0/dsh-remote-bridge-v2.6.0.tgz
97
97
 
98
98
  # ③ Gitee Release 的 tarball(国内访问更快)
99
- dsh plugin --profile web add https://gitee.com/westanke/dsh-remote-bridge/releases/download/v2.5.1/dsh-remote-bridge-v2.5.1.tgz
99
+ dsh plugin --profile web add https://gitee.com/westanke/dsh-remote-bridge/releases/download/v2.6.0/dsh-remote-bridge-v2.6.0.tgz
100
100
 
101
101
  npx @deepseek-ai/dsh web
102
102
  ```
@@ -112,7 +112,7 @@ npx @deepseek-ai/dsh web
112
112
  从 GitHub Release 下载离线安装包时,可直接安装 tarball:
113
113
 
114
114
  ```sh
115
- dsh plugin --profile web add ./dsh-remote-bridge-v2.5.1.tgz
115
+ dsh plugin --profile web add ./dsh-remote-bridge-v2.6.0.tgz
116
116
  ```
117
117
 
118
118
  ## 第一次使用
@@ -182,14 +182,59 @@ dsh plugin --profile web add ./dsh-remote-bridge-v2.5.1.tgz
182
182
  装好 App 后,在「手机设置 → 远程访问」里生成配置文本或配对码即可连上本机。
183
183
  公开 API 不依赖 DSH 私有协议,也可用于其他客户端:
184
184
 
185
- - 查看、新建、重命名和移除 DSH 工作区登记。
186
- - 创建、重命名、分叉和归档会话。
185
+ - 查看、新建、重命名和移除 DSH 工作区登记。- 创建、重命名、分叉和归档会话。
187
186
  - 读取消息、流式接收正文和思考、追加引导或取消运行。
188
187
  - 查看 TODO、斜杠命令、权限模式和待审批操作。
189
188
  - 选择 Agent、模型和思考强度。
190
189
  - 查看或修改模型供应商,支持完全自定义供应商。
191
190
  - 管理设备获准访问的文件和回收站项目。
192
191
 
192
+ ### 远程开隧道:人已经出门、电脑还锁着
193
+
194
+ 常见场景:**人已经出门,才发现电脑上的 Cloudflare 隧道没开,配置文本也没发给自己**。
195
+ 此时公网入口根本不存在,WebUI 和 App 都够不着这台机器。能用的只剩「本机主动连出去」的通道,
196
+ 装了 `@xmanrui/dsh-im`(微信 / 飞书 / 钉钉 / 企业微信 / Telegram …)或任何带命令栏的客户端时,
197
+ 有两条入口可以做这件事:
198
+
199
+ **① 桌面 WebUI 的命令栏(立即可用)**
200
+
201
+ ```
202
+ /tunnel
203
+ ```
204
+
205
+ 回的是这样一段:
206
+
207
+ ```
208
+ 云隧道已开启。
209
+ 地址:https://<random>.trycloudflare.com
210
+ 设备名:Android 设备
211
+ 配置文本(一行,复制后粘贴到 App 的「导入配置」):
212
+ DSH1:eyJ2IjoxLCJlbmRwb2ludHMiOlt7ImxhYmVsIjoi…
213
+ ```
214
+
215
+ **② IM 聊天窗口里用一句话(配合智能体)**
216
+
217
+ 在微信/飞书里直接说「**帮我开隧道,把配置文本发我**」,智能体会调用本插件的
218
+ `open_tunnel_config` 工具,把同样的地址与 `DSH1:` 文本回到聊天里。
219
+
220
+ > 为什么 IM 里不是发 `/tunnel`?因为 `@xmanrui/dsh-im` 的斜杠命令表是**封闭的**:
221
+ > 内置 31 条,另外只把 `/compact` `/clear` `/permission` 三条委派给宿主,没有
222
+ > 「未知命令转交宿主命令表」的通用派发,也从不查询宿主的命令列表。所以 `/tunnel`
223
+ > 在 IM 里会当成一句普通消息交给智能体 —— 这也正是上面第 ② 条走自然语言 + 工具的原因。
224
+ > 如果 dsh-im 以后加上通用派发,`/tunnel` 会立刻可用,桥这边不用改。
225
+
226
+ 隧道**已经在跑**时再触发一次不会重复开,而是复用当前地址再给一份配置文本
227
+ (每次调用都会新建一台设备,方便在面板里单独吊销)。
228
+
229
+ 能力边界刻意压得很小:**只能开,不能停、不能改配置、不读任何文件**。
230
+ 带参数一律拒绝,不提供 `/tunnel stop` 这类第二入口。就算口令泄露,损失上限也只是
231
+ 「隧道被人开了一下」—— 隧道本身不授予任何数据面权限(读文件、发消息仍要设备令牌)。
232
+
233
+ > ⚠️ **必要前提:IM 机器人必须是白名单模式,且白名单里只有你自己。**
234
+ > 这条通道的身份校验**不在本插件里**,而在 IM 插件自己的访问策略(`canExecuteCommands` +
235
+ > allowlist)。如果机器人设成「允许所有用户私聊执行命令」,那等于把开机按钮放到公共群里 ——
236
+ > 这是部署问题,代码替你兜不住。详见 [SECURITY.md](docs/project/SECURITY.md#im-命令tunnel的信任边界)。
237
+
193
238
  ## 远程访问与安全
194
239
 
195
240
  默认远程地址为 `0.0.0.0:3090`,IP 和端口都可修改。客户端可以使用能到达服务器的 IP 或域名连接。
@@ -338,27 +383,31 @@ pnpm check
338
383
  pnpm pack
339
384
  ```
340
385
 
341
- `pnpm check` 覆盖类型检查、文档一致性与测试;当前 **30 个测试文件 / 226 个用例**,外加 **4 个浏览器层用例**(Playwright 实测量出的计算样式),全部通过。
386
+ `pnpm check` 覆盖类型检查、文档一致性与测试;当前 **67 个测试文件 / 557 个用例**,外加 **8 个浏览器层用例**(Playwright 实测量出的计算样式,含 375px 窄屏契约),全部通过。
342
387
 
343
- `npm pack` 的产物约 **0.55 MB**(v2.5.0 实测 572,951 字节,SHA-256 `38f25c10…299c93`):2.2.0 删除独立页及其编辑器依赖、并关闭 source map 之后从 1.72 MB 降下来,之后随投影透传与权限 BFF 小幅增长。
388
+ `npm pack` 的产物约 **0.61 MB**(v2.5.2 实测 635,325 字节,SHA-256 `18d36904…c8095`):2.2.0 删除独立页及其编辑器依赖、并关闭 source map 之后从 1.72 MB 降下来,之后随投影透传、权限 BFF 与五维加固小幅增长。
344
389
 
345
390
  v2.5.0 在 DSH **0.2.1-rc.2** 上完成真机验证:14/14 宿主服务全部可注入、三个 jobs 端点实测通过、
346
391
  权限档位目录返回内核真实三档。开发基线为上游 `dsh-v0.2.1-rc.2`(commit `687f282e8625c6b8d11ff58211a90524be2e4317`),
347
392
  兼容范围见上文 `engines.dsh`。CI 覆盖 Windows、Ubuntu 和 macOS。
348
393
 
349
- **v2.5.1 的验证状态**:47 个测试文件 / 398 个用例全绿(含隧道拓扑下的管理面回归、
350
- 桌面端放行断言、`it.fails` 转正)、`typecheck` 与 `docs:check` 通过,并用真实
351
- `RemoteApiServer` 重打了审计报告 §3.1 的攻击链(逐条 403)。**尚未做真机验收**,
352
- 也未发布到 npm / 两站 Release —— 按「未发布 ≠ 已验证 ≠ 用户吃到了」记三态。
394
+ **v2.6.0 的验证状态**:70 个测试文件 / 593 个用例全绿、8 个 Playwright 浏览器用例
395
+ (含 375px 窄屏契约)、`typecheck`/`docs:check`/构建通过;隧道管理面三轮真机回归均用
396
+ React18 + Playwright 真点击台复现并验证(一键出令牌、停止出口、重进设置页锁定态),
397
+ 用户真机手工复测通过。
353
398
 
354
399
  ## 项目信息
355
400
 
356
- - 当前版本:`v2.5.1`
401
+ - 当前版本:`v2.6.0`
402
+ - **v2.6.0 是通用设置面 + IM 入口版**:新增「按插件划分的设置命名空间」两个端点(手机端照 schema 自建表单,新装插件无需改 App),以及「人已出门、电脑还锁着」时用 IM 一句话开隧道并收回配对配置的双入口(宿主命令 `/tunnel` + 工具 `open_tunnel_config`);另加一键清理 N 天未使用设备、移除审计页,并把「激活期降级保护」补上(apply 尾部抛错不再拆掉整条插件上下文)。
403
+ - **v2.5.2 是全量完善版**:落地五维审计(安全/功能/性能/界面/按钮反馈)——含高危的
404
+ `debug/host-services` 免鉴权信息泄露修复、限流双桶、广播背压、弱 ETag、审计保留策略等,
405
+ 并把「一键开隧道并生成令牌」做成同一次请求内搭车完成;隧道运行时管理面安全锁定,
406
+ 仅保留「停止隧道」出口,停止后自动恢复。v2.5.1 的安全模型(开隧道时 `/manage` 403、
407
+ 四层管理判据)保持不变,见 `docs/project/SECURITY.md` §管理面的信任边界。
357
408
  - **v2.5.1 是安全修复版**:堵住「开隧道/反代后管理面免凭据对公网开放」的远程提权(`/manage/**`
358
409
  与回环 admin 头此前只按「TCP 来源是否回环」判定,而隧道转发进来的请求来源恒为 `127.0.0.1`),
359
- 并修复 `.heif` 交接图必被只读路由 415 的问题。升级后**开隧道时 `/manage` 一律 403**
360
- (唯一例外是只读的隧道状态端点,且对非本机调用方隐去隧道地址);桌面端面板不受影响。
361
- 见 `docs/project/SECURITY.md` §管理面的信任边界。
410
+ 并修复 `.heif` 交接图必被只读路由 415 的问题。
362
411
  - 作者:上游 [Hakunm](https://github.com/Hakunm),本 fork 维护 [westanke](https://github.com/westanke)
363
412
  - 仓库:[GitHub](https://github.com/westanke/dsh-remote-bridge) · [Gitee](https://gitee.com/westanke/dsh-remote-bridge)
364
413
  - npm:[`dsh-remote-bridge`](https://www.npmjs.com/package/dsh-remote-bridge)([国内镜像](https://registry.npmmirror.com/dsh-remote-bridge))
@@ -1,7 +1,7 @@
1
1
  asyncapi: 3.0.0
2
2
  info:
3
3
  title: DeepSeek Harness Mobile Events
4
- version: 2.5.1
4
+ version: 2.6.0
5
5
  description: |
6
6
  Live file and authorized DSH chat events. Reconnect clients must refresh
7
7
  REST state; unbounded replay is not provided.
@@ -10,6 +10,25 @@ info:
10
10
  limit, quota exhausted, invalid API key, signed-out stop). Without it a
11
11
  remote client sees an ordinary `chat.turn.end` and keeps waiting on a run
12
12
  that already failed.
13
+
14
+ Connection-level behavior (source of truth: `src/host/server.ts`):
15
+
16
+ - Authentication happens **before** connection-limit counting. The
17
+ default cap is 32 accepted WebSocket connections (configurable); the
18
+ 33rd authenticated upgrade is refused at the HTTP Upgrade stage with
19
+ `429 Too Many Requests` and `Retry-After: 5`.
20
+ - Frames larger than 1 MiB are refused with WebSocket close `1009`
21
+ (message too big).
22
+ - A server ping is sent every 30 seconds; a connection that misses the
23
+ pong for two consecutive intervals is terminated.
24
+ - Each connection has a 512-frame send queue. Under backpressure the
25
+ server first drops rebuildable `chat.message.delta` frames, then other
26
+ rebuildable frames queued ahead, and as a last resort the oldest state
27
+ frame. When any frame was dropped the server emits a best-effort
28
+ `connection.resyncRequired` frame (also immediately before a
29
+ `1013 Backpressure` close) telling the client to re-sync via REST;
30
+ clients must ignore the exact `dropped` value. Older clients that do
31
+ not know this event simply ignore it.
13
32
  servers:
14
33
  local:
15
34
  host: 127.0.0.1:3090
@@ -39,7 +58,7 @@ components:
39
58
  id: { type: string }
40
59
  type:
41
60
  type: string
42
- examples: [file.changed, file.trashed, chat.message.delta, chat.message.committed, chat.session.status, chat.session.snapshot, chat.deliverables.presented, chat.approval.requested, chat.approval.resolved, chat.turn.failed, connection.ready]
61
+ examples: [file.changed, file.trashed, chat.message.delta, chat.message.committed, chat.session.status, chat.session.snapshot, chat.deliverables.presented, chat.approval.requested, chat.approval.resolved, chat.turn.failed, connection.ready, connection.resyncRequired]
43
62
  time: { type: integer }
44
63
  data:
45
64
  oneOf:
@@ -49,6 +68,7 @@ components:
49
68
  - $ref: '#/components/schemas/ChatApprovalResolved'
50
69
  - $ref: '#/components/schemas/ChatTurnFailed'
51
70
  - $ref: '#/components/schemas/ConnectionReadyData'
71
+ - $ref: '#/components/schemas/ConnectionResyncRequired'
52
72
  - $ref: '#/components/schemas/ChatSessionSnapshot'
53
73
  - $ref: '#/components/schemas/ChatDeliverablesPresented'
54
74
  - type: object
@@ -231,3 +251,32 @@ components:
231
251
  capabilities:
232
252
  type: array
233
253
  items: { type: string }
254
+
255
+ ConnectionResyncRequired:
256
+ type: object
257
+ description: |
258
+ Payload of a connection-scoped `connection.resyncRequired` frame. The
259
+ server emits it (best effort) after it had to drop queued frames for
260
+ this socket because the per-connection send queue (512 frames)
261
+ overflowed under backpressure; it is also emitted once immediately
262
+ before the server closes the socket with WebSocket code `1013`
263
+ (`Backpressure`), when even state frames no longer fit. State frames
264
+ (`chat.turn.*`, `chat.approval.*`, `chat.turn.failed`,
265
+ `connection.ready`) are preserved preferentially over
266
+ `chat.message.delta`.
267
+
268
+ On receipt, clients should re-fetch history and pending state via the
269
+ REST API (the dropped frames are never replayed). The `dropped` count
270
+ is diagnostic only and must not be acted on numerically; clients that
271
+ do not recognize this event type ignore it safely. Example:
272
+ `{"id":"<uuid>","type":"connection.resyncRequired","time":1761100000000,
273
+ "data":{"dropped":12,"reason":"backpressure"}}`.
274
+ required: [dropped, reason]
275
+ properties:
276
+ dropped:
277
+ type: integer
278
+ minimum: 0
279
+ description: Number of frames dropped for this connection since the previous resync notification.
280
+ reason:
281
+ type: string
282
+ const: backpressure