@linxin666/dsh-remote-web-ui 0.4.1 → 0.4.3

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.
Files changed (91) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +9 -7
  3. package/README.zh.md +9 -7
  4. package/lib/client.js +282 -619
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +418 -901
  7. package/lib/types/client/RemoteEntry.d.ts.map +1 -1
  8. package/lib/types/client/RemoteEntry.js +9 -10
  9. package/lib/types/client/RemoteSettingsCard.d.ts.map +1 -1
  10. package/lib/types/client/RemoteSettingsCard.js +1 -1
  11. package/lib/types/client/index.d.ts +32 -3
  12. package/lib/types/client/index.d.ts.map +1 -1
  13. package/lib/types/client/index.js +56 -11
  14. package/lib/types/client/locales.d.ts +0 -84
  15. package/lib/types/client/locales.d.ts.map +1 -1
  16. package/lib/types/client/locales.js +2 -86
  17. package/lib/types/client/pair-api.d.ts +4 -0
  18. package/lib/types/client/pair-api.d.ts.map +1 -1
  19. package/lib/types/client/pair-api.js +11 -7
  20. package/lib/types/client/remote-channel.d.ts +27 -7
  21. package/lib/types/client/remote-channel.d.ts.map +1 -1
  22. package/lib/types/client/remote-channel.js +25 -17
  23. package/lib/types/client/settings-entry-form.d.ts +50 -0
  24. package/lib/types/client/settings-entry-form.d.ts.map +1 -0
  25. package/lib/types/client/settings-entry-form.js +98 -0
  26. package/lib/types/index.d.ts +99 -7
  27. package/lib/types/index.d.ts.map +1 -1
  28. package/lib/types/index.js +104 -137
  29. package/lib/types/lan-bind-plan.d.ts +14 -0
  30. package/lib/types/lan-bind-plan.d.ts.map +1 -1
  31. package/lib/types/lan-bind-plan.js +16 -0
  32. package/lib/types/lan-bind.d.ts +5 -1
  33. package/lib/types/lan-bind.d.ts.map +1 -1
  34. package/lib/types/lan-bind.js +65 -2
  35. package/lib/types/mount-once.d.ts +38 -7
  36. package/lib/types/mount-once.d.ts.map +1 -1
  37. package/lib/types/mount-once.js +96 -12
  38. package/lib/types/pairing.d.ts +15 -2
  39. package/lib/types/pairing.d.ts.map +1 -1
  40. package/lib/types/pairing.js +26 -14
  41. package/lib/types/pairing.persist.test.js +43 -3
  42. package/lib/types/remote-channel-boot.d.ts.map +1 -1
  43. package/lib/types/remote-channel-boot.js +13 -3
  44. package/lib/types/remote-channel-rules.d.ts +74 -0
  45. package/lib/types/remote-channel-rules.d.ts.map +1 -1
  46. package/lib/types/remote-channel-rules.js +104 -0
  47. package/lib/types/tunnel.d.ts +60 -3
  48. package/lib/types/tunnel.d.ts.map +1 -1
  49. package/lib/types/tunnel.js +136 -3
  50. package/package.json +21 -20
  51. package/src/client/RemoteEntry.tsx +8 -10
  52. package/src/client/RemoteSettingsCard.tsx +0 -2
  53. package/src/client/index.ts +58 -12
  54. package/src/client/locales.ts +2 -86
  55. package/src/client/pair-api.ts +11 -7
  56. package/src/client/remote-channel.ts +38 -15
  57. package/src/client/remote.module.css +61 -193
  58. package/src/client/settings-entry-form.ts +142 -0
  59. package/src/index.ts +156 -152
  60. package/src/lan-bind-plan.ts +22 -0
  61. package/src/lan-bind.ts +63 -2
  62. package/src/mount-once.ts +107 -13
  63. package/src/pairing.persist.test.ts +47 -3
  64. package/src/pairing.ts +26 -12
  65. package/src/remote-channel-boot.ts +13 -3
  66. package/src/remote-channel-rules.ts +113 -0
  67. package/src/tunnel.ts +142 -3
  68. package/lib/types/client/UpdateEntry.d.ts +0 -14
  69. package/lib/types/client/UpdateEntry.d.ts.map +0 -1
  70. package/lib/types/client/UpdateEntry.js +0 -112
  71. package/lib/types/client/UpdatePanel.d.ts +0 -42
  72. package/lib/types/client/UpdatePanel.d.ts.map +0 -1
  73. package/lib/types/client/UpdatePanel.js +0 -101
  74. package/lib/types/client/update-api.d.ts +0 -29
  75. package/lib/types/client/update-api.d.ts.map +0 -1
  76. package/lib/types/client/update-api.js +0 -47
  77. package/lib/types/console-output.d.ts +0 -41
  78. package/lib/types/console-output.d.ts.map +0 -1
  79. package/lib/types/console-output.js +0 -89
  80. package/lib/types/update-routes.d.ts +0 -30
  81. package/lib/types/update-routes.d.ts.map +0 -1
  82. package/lib/types/update-routes.js +0 -47
  83. package/lib/types/update.d.ts +0 -272
  84. package/lib/types/update.d.ts.map +0 -1
  85. package/lib/types/update.js +0 -744
  86. package/src/client/UpdateEntry.tsx +0 -144
  87. package/src/client/UpdatePanel.tsx +0 -256
  88. package/src/client/update-api.ts +0 -49
  89. package/src/console-output.ts +0 -97
  90. package/src/update-routes.ts +0 -65
  91. package/src/update.ts +0 -882
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # node scripts/verify-docs.mjs --write <dir>
5
- README.md: 6b44be832e6723fa9e75ecb1555e6e423b26adc5
6
- README.zh.md: 42bbfd2dc3b12a7d17987b86f260679af14fcd84
5
+ README.md: 3a962a7fe14fb426a6c1beb238a51837dc345766
6
+ README.zh.md: 1d3d36c040c5c35ce520299934471132234de457
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # DSH Remote Web UI
2
2
 
3
3
  English | [中文](README.zh.md)
4
- > Remote access for the dsh web GUI that shares **one interface**: pair a phone or another computer from a QR beside the settings button, and both run the same official Web GUI this machine runs — phones get an injected portrait-touch adaptation, PCs get the full desktop — through time-limited pairing tokens and revocable device sessions. A settings toggle binds the server to the LAN, an optional Cloudflare quick tunnel reaches the internet — fronted by a never-changing stable hostname so the phone's bookmark and pairing survive restarts with zero setup — and the sidebar checks for a newer dsh-web release with one-click self-update.
4
+ > Remote access for the dsh web GUI that shares **one interface**: pair a phone or another computer from a QR beside the settings button, and both run the same official Web GUI this machine runs — phones get an injected portrait-touch adaptation, PCs get the full desktop — through time-limited pairing tokens and revocable device sessions. A settings toggle binds the server to the LAN, an optional Cloudflare quick tunnel reaches the internet — fronted by a never-changing stable hostname so the phone's bookmark and pairing survive restarts with zero setup.
5
5
 
6
- This repository is an external plugin package for DeepSeek Harness (DSH). It is a single dual-face package: the host half owns pairing tokens, device sessions, the `/api/pair` route family, the gated `/remote` channel, the LAN bind toggle, and the `/api/update` surface; the browser half renders the sidebar-foot entries (the download trigger and the remote-access entry beside the settings button), the pairing panel with a QR code, live device status, the authorized-device roster, the settings card, the portrait-touch adaptation layer over the official UI, and the update panel.
6
+ This repository is an external plugin package for DeepSeek Harness (DSH). It is a single dual-face package: the host half owns pairing tokens, device sessions, the `/api/pair` route family, the gated `/remote` channel, the LAN bind toggle; the browser half renders the sidebar-foot remote-access entry beside the settings button, the pairing panel with a QR code, live device status, the authorized-device roster, the settings card, and the portrait-touch adaptation layer over the official UI.
7
7
 
8
8
  ## What it does
9
9
 
@@ -11,11 +11,11 @@ This repository is an external plugin package for DeepSeek Harness (DSH). It is
11
11
  - **Panel**: "Remote access" title, a "Pair a device" card with the status area ("Waiting for a device" + status badge), a large QR code, the pairing link with its copy button, Stop / Refresh QR actions, and the authorized-device roster (a device name inferred from User-Agent, online/offline, last active time, per-device unpair). Credential-bearing device ids and raw User-Agent values are never rendered. Both the phone and the PC use the same time-limited link: until it expires or the QR is refreshed, that link can pair several devices, each with its own device session.
12
12
  - **Phone side**: scanning the QR binds the phone with a time-limited token and reloads into the **official Web GUI** — there is no second interface to drift from. While the phone is in portrait, the plugin injects a touch-adaptation layer over the running UI (see "The mobile adaptation layer" below). The accept chain is cookieless (`/pair-accept` → `/pair-app`): the plugin itself serves the official shell, so the phone needs no harness browser-auth cookie at all. A reopen service worker (https origins) then owns later navigations to `/` from history, bookmarks, or tab restore, so the phone reopens straight into the app instead of a 401 dead end (see Security model).
13
13
  - **PC side**: the same link opens the full desktop Web GUI on another computer over the gated `/remote` channel; unpaired PCs see a guided blocking page (with a manual pair-token input) and no workspace data behind it.
14
- - **Security**: one active token at a time (a refresh invalidates the old link; the link stays re-usable within its expiry window so a scan handed between browsers can complete pairing; it expires). 停止 revokes every paired device and the current token — the `/remote` channel cuts them off on their next request. Pairing is this plugin's access control for the `/remote` channel; direct `/api` on a LAN-exposed bind is governed by the harness fence + browser auth (see Security model). Loopback (127.0.0.1) keeps using `/api` directly. A paired device is a **full-control credential** (see Security model).
14
+ - **Security**: one active token at a time (a refresh invalidates the old link; the link stays re-usable within its expiry window so a scan handed between browsers can complete pairing; it expires). 停止 revokes every paired device and the current token — the `/remote` channel cuts them off on their next request. Pairing is this plugin's access control for the `/remote` channel; direct `/api` on a LAN-exposed bind is governed by the harness fence + browser auth (see Security model). Loopback (127.0.0.1) keeps using `/api` directly, and so does a page an application on this machine delivered — the desktop shell's `dsh-app://app/` or a `file:` page — because a pairing link is reachable only over the network. A paired device is a **full-control credential** (see Security model).
15
15
  - **LAN bind toggle**: the settings card writes a managed block into the profile `cordis.patch.yml` that pins the webserver bind to `0.0.0.0` (on) or `127.0.0.1` (off) — no `--host` command-line dance; an explicit `--host`/`--port` flag still wins. It maintains the matching host firewall rule (Windows Defender via netsh; Linux firewalld/ufw/iptables; other platforms report the firewall as unmanaged) and shows the live bind, the reachable LAN URLs, and the firewall state.
16
16
  - **Live status**: the desktop badge flips to 已连接 in real time; an `/api` posture probe reports any host whose `/api` fence the SDK leaves open, and the auto-tunnel state appears on the panel while the tunnel is starting.
17
- - **One-click self-update**: the sidebar download trigger checks for a newer dsh-web release after load, marks the button when one exists, and runs the verified update (release notes shown in the panel).
18
- - **Desktop foot row**: on the wide sidebar the update and remote triggers share the official Settings trigger's line instead of stacking above it, so the trigger no longer owns a full-width row; any other foot block (the usage glance card, another plugin's) keeps a full row above them, and the 56px rail keeps the shell's stacked foot.
17
+ - **Self-update lives next door**: the one-click family update is its own plugin (`dsh-update`, the same sidebar foot seat), so turning remote access off — or disabling this plugin — never removes the update trigger.
18
+ - **Desktop foot row**: on the wide sidebar the footer-action occupants share the official Settings trigger's line instead of stacking above it, so no single control owns a full-width row; any other foot block (the usage glance card, another plugin's) keeps a full row above them, and the 56px rail keeps the shell's stacked foot.
19
19
 
20
20
  ## The mobile adaptation layer
21
21
 
@@ -89,7 +89,9 @@ The relay above already fixes the phone's origin on a shared dsh-market subdomai
89
89
  2. Copy the tunnel's token and paste it into **固定域名隧道令牌** (tunnelToken) in the settings card.
90
90
  3. Set the same hostname as **公网地址** (publicBaseUrl). The token does not carry the hostname, so without this step the tunnel stays off and a warning names the missing piece.
91
91
 
92
- The plugin then runs `cloudflared tunnel run --token` with the same lifecycle management as the quick tunnel: the binary ships with the package, unexpected exits restart with backoff, and the posture probe audits the `/api` fence for the fixed host. The token is stored as a settings secret and read back redacted. The auto quick tunnel takes precedence while it is on.
92
+ The plugin then runs `cloudflared tunnel run --token` with the same lifecycle management as the quick tunnel: the binary ships with the package, unexpected exits restart with backoff, a running tunnel's public URL is probed every 60 s and two consecutive failures mint a fresh tunnel through that same restart path, and the posture probe audits the `/api` fence for the fixed host. The token is stored as a settings secret and read back redacted. The auto quick tunnel takes precedence while it is on.
93
+
94
+ A `running` tunnel is not a terminal state: a connector that lost its Cloudflare edge registration keeps its process and its metrics port alive while the minted hostname stops resolving, so the plugin used to report `running` indefinitely and the relay kept forwarding the phone to the dead address (Cloudflare 530 / Error 1016) instead of the relay's offline page (issue #1723). The readiness watchdog now treats a public URL that stops answering — DNS failure, refused connection, or a Cloudflare 5xx such as 530 — as a dead tunnel after two consecutive probes, and the ordinary backoff restart mints a new URL and re-registers the relay. An unauthenticated `401`/`403` answer still counts as alive, because that is a healthy round trip to a harness that has not been paired yet.
93
95
 
94
96
  ### Manual tunnels (bring your own)
95
97
 
@@ -161,7 +163,7 @@ The public path is the same round trip through a tunnel (see "Remote access over
161
163
  - **The channel carries the process's own inner credential.** The harness browser-auth cookie is authority-bound (minted for the exact `host:port` a browser visited) and has no loopback exemption, so a proxied re-issue to `127.0.0.1` cannot reuse a device's cookie. The plugin therefore redeems its own launch token once — the same exchange a first browser visit performs — and attaches that cookie to re-issued requests. The credential is only ever exercised behind the pairing gate above; 停止/取消配对 immediately stop exercising it.
162
164
  - **Cohort reality: pairing does not gate direct `/api`.** On the pinned 0.1.2-alpha.2 line nothing emits the `api/gate` seam, so a direct `/api` call from a LAN origin is governed solely by the harness fence (which auto-trusts LAN literals under a `0.0.0.0` bind) plus the harness browser-auth cookie. A browser credential a device has already redeemed therefore survives 停止/取消配对 until its natural expiry (30 days) — revocation binds the `/remote` channel and the pairing cookie, not that credential. The plugin probes the `/api` posture and warns loudly; treat a LAN-exposed bind as a deliberate decision, and prefer loopback plus tunnels when the machine is shared.
163
165
  - **A paired device is a full-control credential.** With host mode active it reaches the complete host API — chat, sessions, settings, credentials, agent presets, deliverables — mirroring the SDK's own stance that a loopback desktop is trusted. Only the three control planes (pairing, self-update, plugin install/remove) stay physically local. Only pair devices you control; 停止 or per-device 取消配对 revokes immediately.
164
- - **Control endpoints stay loopback-only**: mint/stop/revoke, the device roster, the lan-bind status, and the update endpoints answer only to loopback. A LAN-origin browser sees the "配对面板仅限本机使用" banner.
166
+ - **Control endpoints stay loopback-only**: mint/stop/revoke, the device roster, and the lan-bind status answer only to loopback, and the gated channel keeps the self-update and plugin-install paths physically local. A LAN-origin browser sees the "配对面板仅限本机使用" banner.
165
167
  - **Background file uploads ride the channel too.** The official upload service prefers a Web Worker carrier whose own globals no main-thread patch reaches, so the boot patch (and the browser patch as a fallback) publishes the official pre-Cordis hook `__DSH_FILE_UPLOAD__` and hands it the patched `fetch`: the raw `/api/session/uploadFileBinary` POST is rewritten onto `/remote` and carries the device credential exactly like every other gated call. Without the hook a paired browser's uploads bypass the channel and the harness browser-auth fence answers 401 (issue #1580). The hook is only published on non-loopback origins, only while the channel is installed, and never overwrites a hook the page already owns.
166
168
  - **The landing URL carries a one-time grant, never the device id.** `/pair-accept` mints a 256-bit, single-consume grant with a 60 s TTL and redirects to `/pair-app?grant=<g>`; the landing spends it — delete-on-attempt, so a replayed or expired grant resolves to nothing — and hands the device id to the shell in its own response. A device id is a live session credential, so it never enters a URL, the address bar, or any log on the redirect path. The pairing cookie is `Secure` whenever the request arrived over TLS (`x-forwarded-proto: https`) and plain over LAN HTTP, where a Secure cookie would simply be dropped by the browser.
167
169
  - **The app landing is cookieless-optional.** After pairing, the QR lands the device on `/pair-app`, served by this plugin: the official shell is delivered without passing the harness index gate, and the device credential rides a `x-dsh-remote-device` header (fetch) / `device` query (WebSocket upgrades) that the boot patch attaches from sessionStorage. The flow therefore works when the device browser blocks all cookies; the pairing cookie remains the primary credential where the browser stores it, and the harness browser-auth cookie is no longer required on the phone path at all.
package/README.zh.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # DSH 远程访问(Remote Web UI)
2
2
 
3
3
  [English](README.md) | 中文
4
- > 为 dsh web GUI 提供共享**同一份界面**的远程访问:在设置按钮旁的二维码配对手机或另一台电脑,两端运行的都和本机一样是官方 Web GUI——手机获得注入的竖屏触控适配,电脑获得完整桌面;准入基于限时配对令牌与可撤销设备会话。设置卡片可将服务绑定到局域网,可选 Cloudflare 快速隧道触达公网——并由一个永不变化的固定主机名前置,手机的书签与配对零配置跨重启有效——侧栏还会检查 dsh-web 新版本并支持一键自更新。
4
+ > 为 dsh web GUI 提供共享**同一份界面**的远程访问:在设置按钮旁的二维码配对手机或另一台电脑,两端运行的都和本机一样是官方 Web GUI——手机获得注入的竖屏触控适配,电脑获得完整桌面;准入基于限时配对令牌与可撤销设备会话。设置卡片可将服务绑定到局域网,可选 Cloudflare 快速隧道触达公网——并由一个永不变化的固定主机名前置,手机的书签与配对零配置跨重启有效。
5
5
 
6
- 本仓库是 DeepSeek Harness(DSH)的外部插件包,为单一双面包:host 半区持有配对令牌、设备会话、`/api/pair` 路由族、门控 `/remote` 通道、局域网绑定开关与 `/api/update` 界面;浏览器半区渲染侧栏底部入口(下载触发器与设置按钮旁的远程访问入口)、带二维码的配对面板、实时设备状态、已授权设备列表、设置卡片、官方界面之上的竖屏触控适配层,以及更新面板。
6
+ 本仓库是 DeepSeek Harness(DSH)的外部插件包,为单一双面包:host 半区持有配对令牌、设备会话、`/api/pair` 路由族、门控 `/remote` 通道、局域网绑定开关;浏览器半区渲染侧栏底部、设置按钮旁的远程访问入口、带二维码的配对面板、实时设备状态、已授权设备列表、设置卡片,以及官方界面之上的竖屏触控适配层。
7
7
 
8
8
  ## 功能
9
9
 
@@ -11,11 +11,11 @@
11
11
  - **面板**:「远程访问」标题、「设备配对」卡片(状态区「等待设备连接」+ 状态徽标)、大二维码、带复制按钮的配对链接、停止 / 刷新二维码操作,以及已授权设备列表(按 User-Agent 推断的设备名、在线/离线、最近活动时间、逐设备取消配对)。承载凭据的设备 id 与原始 User-Agent 值绝不渲染。手机与电脑共用同一条限时链接:在令牌过期或刷新二维码之前,该链接可为多台设备完成配对,每台各自生成独立设备会话。
12
12
  - **手机侧**:扫码后以限时令牌绑定并重载进入**官方 Web GUI**——不存在会漂移的第二套界面。手机竖屏时插件在运行中的界面上注入触控适配层(见下文)。接受链路不依赖 cookie(`/pair-accept` → `/pair-app`):官方应用壳由插件直接交付,手机全程不需要 harness 浏览器认证 cookie。随后由重开 service worker(https 源)接管此后对 `/` 的导航——来自历史、书签或标签恢复的重开直接回到应用,而不是 401 死路(见安全模型)。
13
13
  - **电脑侧**:同一链接在另一台电脑的浏览器里打开完整桌面 Web GUI,流量走门控 `/remote` 通道;未配对的电脑看到引导式拦截页(含手动粘贴配对令牌输入框),其后没有任何工作区数据。
14
- - **安全**:同时只有一枚有效令牌(刷新二维码使旧链接失效;链接在其有效期窗口内可重复配对——扫码在浏览器间转手也能完成配对;令牌会过期)。停止会撤销所有配对设备与当前令牌——`/remote` 通道在下一次请求即切断它们。配对是本插件对 `/remote` 通道的访问控制;暴露到局域网后的直连 `/api` 由 harness 围栏 + 浏览器认证约束(见安全模型)。回环(127.0.0.1)继续直接使用 `/api`。配对设备是**完全控制凭据**(见安全模型)。
14
+ - **安全**:同时只有一枚有效令牌(刷新二维码使旧链接失效;链接在其有效期窗口内可重复配对——扫码在浏览器间转手也能完成配对;令牌会过期)。停止会撤销所有配对设备与当前令牌——`/remote` 通道在下一次请求即切断它们。配对是本插件对 `/remote` 通道的访问控制;暴露到局域网后的直连 `/api` 由 harness 围栏 + 浏览器认证约束(见安全模型)。回环(127.0.0.1)继续直接使用 `/api`;本机应用交付的页面(桌面外壳的 `dsh-app://app/`、`file:` 页面)同样如此——配对链接只能经网络到达。配对设备是**完全控制凭据**(见安全模型)。
15
15
  - **局域网绑定开关**:设置卡片向 profile `cordis.patch.yml` 写入受管块,将 webserver 绑定固定为 `0.0.0.0`(开)或 `127.0.0.1`(关)——无需 `--host` 命令行操作;显式 `--host`/`--port` 旗标仍然优先。同时维护对应的主机防火墙规则(Windows Defender 经 netsh;Linux firewalld/ufw/iptables;其他平台报告防火墙不受管),并展示运行中的绑定、可达局域网地址与防火墙状态。
16
16
  - **实时状态**:桌面徽标实时切换为已连接;`/api` 姿态探测报告 SDK 栅栏仍敞开的 `/api` 来源;隧道启动期间面板显示自动隧道状态。
17
- - **一键自更新**:侧栏下载触发器在加载后检查 dsh-web 新版本,有新版本时标记按钮并执行带校验的更新(面板展示发布说明)。
18
- - **桌面端底栏一行**:宽栏下更新与远程触发器不再单独成行,而是与官方「设置」触发器共用一行,设置按钮不再独占整行;其他脚部块(用量速览卡、其它插件的块)仍各占一整行,56px 栏轨保持官方原有的堆叠脚部。
17
+ - **自更新就在隔壁**:一键家族更新是独立插件(`dsh-update`,同一个侧栏脚部席位),因此关闭远程访问、甚至禁用本插件,都不会把更新入口一起带走。
18
+ - **桌面端底栏一行**:宽栏下脚部动作占用者不再各自成行,而是与官方「设置」触发器共用一行,设置按钮不再独占整行;其他脚部块(用量速览卡、其它插件的块)仍各占一整行,56px 栏轨保持官方原有的堆叠脚部。
19
19
 
20
20
  ## 移动端适配层
21
21
 
@@ -89,7 +89,9 @@ dsh plugin --profile web add link:$(pwd)/packages/dsh-remote-web-ui
89
89
  2. 复制隧道 Token,粘贴到设置卡片的**固定域名隧道令牌**(tunnelToken)。
90
90
  3. 把同一主机名填入**公网地址**(publicBaseUrl)。Token 本身不携带主机名,缺了这一步隧道不会启动,并会给出缺失警告。
91
91
 
92
- 插件随后以与快速隧道相同的生命周期管理运行 `cloudflared tunnel run --token`:二进制随包分发,意外退出自动退避重启,姿态探测持续审计该固定主机的 `/api` 围栏。Token 作为设置密文存储,读取时脱敏。自动快速隧道开启时优先级更高。
92
+ 插件随后以与快速隧道相同的生命周期管理运行 `cloudflared tunnel run --token`:二进制随包分发,意外退出自动退避重启,运行中的隧道每 60 秒探测一次公网地址,连续两次失败即经同一条重启路径重铸隧道;姿态探测持续审计该固定主机的 `/api` 围栏。Token 作为设置密文存储,读取时脱敏。自动快速隧道开启时优先级更高。
93
+
94
+ `running` 不是终态:连接器失去 Cloudflare 边缘注册后,进程与 metrics 端口仍然活着,而已铸造的域名已经无法解析——插件过去会一直报 `running`,中继继续把手机转发到那个失效地址(Cloudflare 530 / Error 1016),而不是中继自己的离线页(工单 #1723)。就绪看门狗现在把「公网地址不再应答」(DNS 失败、连接被拒、或 530 这类 Cloudflare 5xx)在连续两次探测后判为隧道死亡,走既有退避重启路径重铸隧道并重新注册中继。尚未配对时返回的 `401`/`403` 仍视为存活,因为它是一次正常的往返。
93
95
 
94
96
  ### 手动隧道(自带)
95
97
 
@@ -161,7 +163,7 @@ pnpm run build
161
163
  - **通道携带进程自己的内部凭据。** harness 浏览器认证 cookie 与 authority 绑定(为浏览器访问过的确切 `host:port` 签发)且没有回环豁免,因此转发到 `127.0.0.1` 的再发起请求无法复用设备的 cookie。插件因此自行兑换一次自己的启动令牌——与浏览器首次访问执行的是同一次交换——并把所得 cookie 附到再发起请求上。该凭据只在上面的配对门之后被使用;停止/取消配对会立即停止对它的使用。
162
164
  - **本 cohort 的现实:配对不门控直连 `/api`。** 在锚定的 0.1.2-alpha.2 线上,没有任何组件发出 `api/gate` seam,因此来自局域网源头的直连 `/api` 仅由 harness 围栏(`0.0.0.0` 绑定下自动信任局域网字面量)加 harness 浏览器认证 cookie 约束。设备已经兑换过的浏览器凭据在停止/取消配对后仍然有效,直到其自然过期(30 天)——撤销约束的是 `/remote` 通道与配对 cookie,而不是那个凭据。插件会对 `/api` 姿态做探测并大声告警;请把局域网绑定当作深思熟虑的决定,在共享机器上优先回环加隧道。
163
165
  - **配对设备是完全控制凭据。** host 模式下它可达完整 host API——聊天、会话、设置、凭据、Agent 预设、产出物——与 SDK 对回环桌面的信任一致。只有三个控制面(配对、自更新、插件安装/卸载)保持物理本地。只配对你控制的设备;停止或逐设备取消配对立即撤销。
164
- - **控制端点仅限回环**:铸造/停止/撤销、设备列表、lan-bind 状态与更新端点只应答回环。局域网源浏览器看到「配对面板仅限本机使用」横幅。
166
+ - **控制端点仅限回环**:铸造/停止/撤销、设备列表与 lan-bind 状态只应答回环,门控通道同时让自更新与插件安装路径保持物理本地。局域网源浏览器看到「配对面板仅限本机使用」横幅。
165
167
  - **后台文件上传同样走通道。** 官方上传服务优先使用 Web Worker 载体,其独立全局对象不受主线程补丁影响;因此引导补丁(以及作为兜底的浏览器补丁)会发布官方启动前钩子 `__DSH_FILE_UPLOAD__`,并把打过补丁的 `fetch` 交给它:原始 `/api/session/uploadFileBinary` POST 会被改写到 `/remote`,并像其他受门控调用一样携带设备凭据。没有该钩子时,配对浏览器的上传会绕过通道,被 harness 浏览器认证围栏拒为 401(issue #1580)。该钩子仅在非回环源、且通道安装期间发布,且永不覆盖页面已有的钩子。
166
168
  - **落地页 URL 只携带一次性授权,绝不携带设备 ID。** `/pair-accept` 签发 256 位、一次性消费、TTL 60 秒的授权并 303 到 `/pair-app?grant=<g>`;落地页消费它——无论成功、过期还是重放都先删除记录,因此重放或过期的授权解析不出任何设备——再由自己的响应把设备 ID 交给应用壳。设备 ID 是活的会话凭据,因此永不进入 URL、地址栏或重定向路径上的任何日志。请求经 TLS 到达时(`x-forwarded-proto: https`)配对 cookie 带 `Secure`,局域网明文 HTTP 下不带——在那里 Secure cookie 会被浏览器直接丢弃。
167
169
  - **应用落地页不依赖 cookie。** 配对后二维码把设备带到 `/pair-app`——由本插件直接交付官方应用壳,不经过 harness 索引认证门;设备凭据经 `x-dsh-remote-device` 请求头(fetch)与 `device` 查询参数(WebSocket 升级)由引导补丁从 sessionStorage 挂载。因此手机浏览器完全禁用 cookie 时链路依然成立;有 cookie 时配对 cookie 仍是主凭据,手机路径不再需要 harness 浏览器认证 cookie。