dsh-update-status 0.3.0 → 0.3.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to this project are documented here.
4
4
 
5
+ ## 0.3.1 — 2026-10-07
6
+
7
+ Verified DeepSeek Harness: `0.2.1-alpha.1` (the current release, and the one this profile runs), also `0.2.0-rc.2`, `0.2.0-rc.1`, `0.1.7-rc.2`, `0.1.7-rc.1` and `0.1.7-alpha.2`. Release notes: [`docs/releases/v0.3.1.md`](docs/releases/v0.3.1.md).
8
+
9
+ - **The settings page states the restart precondition.** `UpdateSettings` rendered the shared `RestartControl` but not the promise note that the panel carries, so an enabled button there had no explanation; it now reuses `panel.readOnly` under the action row.
10
+ - **Long work labels no longer push the confirm buttons out of the card.** A job label is an entire shell command; `.dus-restart-work li` is clamped to two lines (`-webkit-line-clamp: 2`) while keeping full text selectable in the DOM, and `tests/client/styles.spec.ts` locks the rule.
11
+ - **New `docs/platform-acceptance.md`**: the three-step real-machine check for Linux and Windows (plan → install → user-stops-DSH → activate), with the status commands to consult and the uninstall path. Linked from both READMEs.
12
+ - **Both READMEs de-AI-ified** (per the project's writing rule): the 0.3.0 compatibility description went from a ~400-character paragraph of internals to one user-facing sentence, and the supervised-restart section now reads as instructions rather than mechanism.
13
+ - **`docs/releases/v0.3.0.md` rewritten and the published GitHub Release body updated to match**: bilingual, one-line compatibility conclusion, three user-facing bullets, no internal identifiers or test counts.
14
+
5
15
  ## 0.3.0 — 2026-10-07
6
16
 
7
17
  Verified DeepSeek Harness: `0.2.1-alpha.1` (the current release, and the one this profile runs), also `0.2.0-rc.2`, `0.2.0-rc.1`, `0.1.7-rc.2`, `0.1.7-rc.1` and `0.1.7-alpha.2`. Full bilingual release notes: [`docs/releases/v0.3.0.md`](docs/releases/v0.3.0.md).
package/README.md CHANGED
@@ -46,7 +46,7 @@ It shadows only the expanded sidebar brand name with `DeepSeek` plus a compact v
46
46
  - **Copy-only guidance** — generates an installation-kind-aware command for the exact newer version it found, never a dist-tag that can move, and never executes it.
47
47
  - **Cache without polling** — one Host check on mount, a configurable 30–1,440 minute cache, single-flight registry access, and no frontend polling. Only the Check for updates button bypasses the cache.
48
48
  - **Mobile-safe panel** — uses a modal browser top layer so the scrollable, safe-area-aware bottom sheet stays above the DSH drawer.
49
- - **Supervised safe restart** — after an explicit user-level service setup, the panel can check active agents/jobs/terminals, demand a second confirmation for interruption, ask the current Host to exit, wait for a distinct Host instance, and refresh itself. A terminal-started, desktop, or unverified DSH is refused rather than shut down.
49
+ - **Supervised safe restart** — after a one-time service setup, the panel can restart DSH for you: it warns about the work it would interrupt, and refuses outright when DSH was started by hand or by the desktop app.
50
50
 
51
51
  ## Install
52
52
 
@@ -128,7 +128,7 @@ Nothing here installs anything. Choosing to follow a channel — and the `channe
128
128
 
129
129
  ## Supervised restart
130
130
 
131
- A Web plugin runs **inside** the `dsh web` process it is asking to restart: it can end that process, but cannot relaunch itself. The Restart button is consequently disabled unless the currently running Host proves it is owned by a user-level service manager.
131
+ A plugin cannot restart DSH on its own: it lives inside the very process it would have to replace. So the button only works once your operating system owns that process — and every OS already ships a way to do that.
132
132
 
133
133
  The shortest safe setup is explicit and terminal-driven:
134
134
 
@@ -146,7 +146,7 @@ dsh plugin --profile web exec dsh-update-status-service activate \
146
146
  --workspace "$PWD"
147
147
  ```
148
148
 
149
- `activate` refuses to take over an occupied port and never kills a process. After the one-time handoff, the red-box **Restart** button checks activity, waits for a second confirmation where necessary, and only then exits the Host. The native user service returns it; the browser waits for a changed process instance ID and refreshes.
149
+ `activate` stops if the port is still busy instead of killing anything, so the DSH you are already using is never taken down by surprise. From then on, **Restart** lists what is running, asks for a second confirmation when something is, and lets DSH exit; the service starts it again and the page comes back on its own.
150
150
 
151
151
  | Platform | User-level owner | Starts | Restart works after setup | Notes |
152
152
  | --- | --- | --- | --- | --- |
@@ -154,9 +154,9 @@ dsh plugin --profile web exec dsh-update-status-service activate \
154
154
  | Linux | `systemd --user` | at user login | Implemented; verify on your host | `loginctl enable-linger` is optional for running after logout and is never enabled automatically. The unit template is render-tested but has not been run on a real Linux host yet. |
155
155
  | Windows | Task Scheduler + current-user wrapper | at user logon | Template supplied; verify on Windows before production use | No administrator account required. |
156
156
 
157
- The generated definitions use absolute Node/DSH paths and a minimal, non-secret environment. They do **not** copy your terminal's API keys, npm tokens, cookies, or proxy credentials. See [user-service supervision](docs/service-supervision.md) for native files, logs, status, repair, and removal.
157
+ The generated service files contain absolute paths and **no secrets** — never your API keys, npm tokens, cookies or proxy credentials. Native files, logs, status and removal: [user-service supervision](docs/service-supervision.md). To check Linux or Windows on your own machine: [platform acceptance](docs/platform-acceptance.md).
158
158
 
159
- A restart can interrupt running work; it does not save or resume it. The button lists active agents, jobs, and terminals and requires **Force restart** after showing them. If activity cannot be inspected, if DSH Desktop owns the Host, or if the current process was launched from a terminal, the Host refuses the request even if a browser sends a forged RPC.
159
+ A restart interrupts running work and does not save it. If DSH cannot list what is running, or was started by hand or by the desktop app, it refuses to restart at all — a web page cannot talk it into it.
160
160
 
161
161
  ## LAN / non-loopback pages
162
162
 
@@ -171,13 +171,14 @@ If you want DSH's stock policy instead (a non-loopback page never persists setti
171
171
 
172
172
  ## Compatibility
173
173
 
174
- Current release: plugin **`0.3.0`** is verified against DeepSeek Harness **`0.2.1-alpha.1`** (the current release), **`0.2.0-rc.2`**, **`0.2.0-rc.1`**, **`0.1.7-rc.2`**, **`0.1.7-rc.1`** and **`0.1.7-alpha.2`**.
174
+ Current release: plugin **`0.3.1`** is verified against DeepSeek Harness **`0.2.1-alpha.1`** (the current release), **`0.2.0-rc.2`**, **`0.2.0-rc.1`**, **`0.1.7-rc.2`**, **`0.1.7-rc.1`** and **`0.1.7-alpha.2`**.
175
175
 
176
176
  ### Which plugin version goes with which DeepSeek Harness version
177
177
 
178
178
  | Plugin | Verified DeepSeek Harness | On npm | What that version is |
179
179
  | --- | --- | --- | --- |
180
- | **`0.3.0`** | `0.2.1-alpha.1`, `0.2.0-rc.2`, `0.2.0-rc.1`, `0.1.7-rc.2`, `0.1.7-rc.1`, `0.1.7-alpha.2` | `latest` | Adds the guarded **Restart** control and the cross-platform user-service CLI (`plan`/`install`/`activate`/`status`/`stop`/`uninstall`). Restart stays disabled until a user-level service supervises this Host; the Host re-checks the supervision marker and the platform identity, lists active agents/jobs/terminals, and refuses when it cannot inspect them. Verified end to end on a real macOS host: force restart → exit `42` → launchd relaunch → automatic page refresh. The public promise moves from "read-only, never restarts" to "read-only plus one guarded restart after you install a user-level service" |
180
+ | **`0.3.1`** | `0.2.1-alpha.1`, `0.2.0-rc.2`, `0.2.0-rc.1`, `0.1.7-rc.2`, `0.1.7-rc.1`, `0.1.7-alpha.2` | `latest` | Small patch: the settings page explains restart too, long job labels can no longer push the buttons away, and there is now a Linux/Windows check page |
181
+ | **`0.3.0`** | `0.2.1-alpha.1`, `0.2.0-rc.2`, `0.2.0-rc.1`, `0.1.7-rc.2`, `0.1.7-rc.1`, `0.1.7-alpha.2` | published | Adds **Restart** to the panel, plus the one-command service setup it requires (`dsh-update-status-service`). Without that service the button stays disabled and says why. Verified end to end on macOS |
181
182
  | **`0.2.1`** | `0.2.1-alpha.1`, `0.2.0-rc.2`, `0.2.0-rc.1`, `0.1.7-rc.2`, `0.1.7-rc.1`, `0.1.7-alpha.2` | published | Fixes the blank brand row: hiding the version chip disposes the `sidebar.brand.name` registration instead of leaving an empty occupant, so DSH's own fish mark and wordmark come back; the settings hint says so |
182
183
  | **`0.2.0`** | `0.2.1-alpha.1`, `0.2.0-rc.2`, `0.2.0-rc.1`, `0.1.7-rc.2`, `0.1.7-rc.1`, `0.1.7-alpha.2` | published | One release line instead of a channel catalogue: the Host keeps only the newest version any npm dist-tag points at (stable, release candidate or alpha) and the panel simply reports the running version, or names the newer release with a command pinned to it; the `channel` preference, the channel list and the per-channel select are gone, and the settings section shows the same single answer |
183
184
  | **`0.1.12`** | `0.2.0-rc.2`, `0.2.0-rc.1`, `0.1.7-rc.2`, `0.1.7-rc.1`, `0.1.7-alpha.2` | published | Declares compatibility with the running `0.2.0-rc.2`: audits all 7 packages in `dsh.client.inject` across rc.1 → rc.2 (only three files differ, and no surface this plugin uses is among them), adds `0.2.0-rc.2` to the verified list so following `latest` no longer shows the *not verified compatible* advisory, and re-verifies the chip, panel and settings section in a real browser on a disposable profile; zero code change |
@@ -200,7 +201,7 @@ Current release: plugin **`0.3.0`** is verified against DeepSeek Harness **`0.2.
200
201
  - Match them explicitly when it matters:
201
202
 
202
203
  ```sh
203
- dsh plugin --profile web add dsh-update-status@0.3.0 # DSH 0.2.1-alpha.1
204
+ dsh plugin --profile web add dsh-update-status@0.3.1 # DSH 0.2.1-alpha.1
204
205
  dsh plugin --profile web add dsh-update-status@0.2.0 # DSH 0.2.1-alpha.1, 0.2.0-rc.2, 0.2.0-rc.1, 0.1.7-rc.2, 0.1.7-rc.1 or 0.1.7-alpha.2
205
206
  dsh plugin --profile web add dsh-update-status@0.1.12 # DSH 0.2.0-rc.2, 0.2.0-rc.1, 0.1.7-rc.2, 0.1.7-rc.1 or 0.1.7-alpha.2
206
207
  dsh plugin --profile web add dsh-update-status@0.1.11 # DSH 0.2.0-rc.1, 0.1.7-rc.2, 0.1.7-rc.1 or 0.1.7-alpha.2
@@ -217,7 +218,7 @@ Current release: plugin **`0.3.0`** is verified against DeepSeek Harness **`0.2.
217
218
  - Two declarations make the verified lines load at all, and a test keeps them honest:
218
219
  - `dsh.engines.dsh` and `peerDependencies['@deepseek-ai/dsh-settings']` both declare `>=0.1.7-alpha.2 <0.3.0`, which admits all six verified releases. The lower bound names the alpha on purpose — under node-semver's default prerelease rule a range like `>=0.1.6-0 <0.2.0` does **not** admit `0.1.7-alpha.2`. The upper bound moved from `<0.2.0` to `<0.3.0` in `0.1.11` for the mirror-image reason: DSH refuses an incompatible bundle at profile load, and a plain `<0.2.0` excludes the `0.2.0` stable, so the day DSH `0.2.0` shipped the plugin would have been silently dropped. DSH evaluates the range with `includePrerelease: true`, so `0.2.0-rc.2` was already inside it and `0.1.12` needed no range change; conversely, a bundle that enumerates `... || 0.2.0-rc.1 || >=0.2.0 <0.3.0` is refused on rc.2, because `>=0.2.0` does not admit a `0.2.0` prerelease.
219
220
  - `@deepseek-ai/schemastery` is a **peer**, not a plain dependency: DSH 0.1.7 resolves only a linked plugin's peer dependencies from the running installation, so a `link:` install of this directory would otherwise fail to import the Host half.
220
- - Per-release notes — what changed, who is affected, what to do — are hand-written in Chinese and English and become the GitHub Release body: [`v0.3.0`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.3.0.md) · [`v0.2.1`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.2.1.md) · [`v0.2.0`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.2.0.md) · [`v0.1.12`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.12.md) · [`v0.1.11`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.11.md) · [`v0.1.10`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.10.md) · [`v0.1.9`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.9.md) · [`v0.1.8`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.8.md) · [`v0.1.7`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.7.md) · [`v0.1.6`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.6.md) · [`v0.1.5`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.5.md) · [`v0.1.4`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.4.md) · [`v0.1.3`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.3.md) (covers the never-published `0.1.2`).
221
+ - Per-release notes — what changed, who is affected, what to do — are hand-written in Chinese and English and become the GitHub Release body: [`v0.3.0`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.3.1.md) · [v0.3.0](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.3.0.md) · [`v0.2.1`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.2.1.md) · [`v0.2.0`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.2.0.md) · [`v0.1.12`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.12.md) · [`v0.1.11`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.11.md) · [`v0.1.10`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.10.md) · [`v0.1.9`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.9.md) · [`v0.1.8`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.8.md) · [`v0.1.7`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.7.md) · [`v0.1.6`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.6.md) · [`v0.1.5`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.5.md) · [`v0.1.4`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.4.md) · [`v0.1.3`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.3.md) (covers the never-published `0.1.2`).
221
222
 
222
223
  ## Configuration
223
224
 
@@ -270,7 +271,7 @@ rm -rf ~/.dsh/profiles/<profile>/node_modules/dsh-update-status/node_modules
270
271
  - No package-manager process is spawned by plugin code.
271
272
  - `canApplyInPlace` is always `false`.
272
273
  - The plugin never installs, upgrades, rolls back, or replaces DSH files. Its separate, explicitly invoked service CLI stages only current-user service definitions; it never kills an occupied DSH port.
273
- - Restart is a POST-only authenticated Connection RPC, server-gated by an explicit native-service marker plus a platform identity check. Terminal, Desktop, unknown, and incomplete-activity environments refuse to exit.
274
+ - Restart requests travel over DSH's authenticated channel and are re-checked by the Host itself: a hand-started, desktop, or un-inspectable DSH refuses to exit even when a page asks it to.
274
275
  - A failed refresh retains the last good cache and displays a warning; a cold failure still shows the local version.
275
276
 
276
277
  ## Uninstall
package/README.zh.md CHANGED
@@ -46,7 +46,7 @@
46
46
  - **仅复制命令**:根据安装来源,为**找到的那个确切版本**生成命令,不用会移动的 dist-tag,也从不执行。
47
47
  - **缓存但不轮询**:Host 挂载时检查一次,缓存可设为 30–1,440 分钟并合并并发请求;前端没有轮询,只有“检查更新”按钮绕过缓存。
48
48
  - **移动端可靠**:详情使用浏览器 modal top layer;底部 sheet 可滚动、适配 safe area,并保持在 DSH 抽屉上方。
49
- - **受监督的安全重启**:只有你显式完成用户级后台服务接管后,面板才可检查正在运行的 Agent/Job/Terminal、在会中断工作时要求二次强制确认、请求当前 Host 退出、等待新实例并自动刷新。终端直启、桌面版或未验证的 DSH 一律拒绝退出。
49
+ - **受监督的安全重启**:一次性装好后台服务后,面板就能替你重启 DSH——它会先提醒会中断哪些工作,而终端直启或桌面应用托管的 DSH 会被直接拒绝。
50
50
  ## 安装
51
51
 
52
52
  要求:
@@ -127,7 +127,7 @@ npm install -g @deepseek-ai/dsh@0.2.1-alpha.1
127
127
 
128
128
  ## 受监督的重启
129
129
 
130
- Web 插件运行在它要重启的 `dsh web` 进程**内部**:它可以请求当前进程退出,却不能自行拉起替代进程。因此只有当前 Host 已证明自己由用户级服务管理器拥有时,重启按钮才会启用。
130
+ 插件没法自己重启 DSH:它就住在那个要被换掉的进程里。所以只有让操作系统来托管这个进程,按钮才有意义——而每个系统本来就自带这个能力。
131
131
 
132
132
  最短且安全的接管流程是显式在终端执行:
133
133
 
@@ -145,17 +145,17 @@ dsh plugin --profile web exec dsh-update-status-service activate \
145
145
  --workspace "$PWD"
146
146
  ```
147
147
 
148
- `activate` 发现目标端口仍被占用就会拒绝,**绝不会杀掉已有进程**,以避免 `EADDRINUSE` 循环。完成这一次接管后,红框中的**重启**按钮会先检查活动任务、必要时要求二次确认,再让当前 Host 退出;原生用户服务负责拉起新 Host,浏览器等到不同的进程实例 ID 后自动刷新。
148
+ `activate` 发现端口还被占着就停手,绝不去杀进程,所以你正在用的 DSH 不会被突然掐掉。这之后就交给**重启**按钮:它先列出正在跑的东西,有东西在跑时再问一次,然后才让 DSH 退出;服务把新的拉起来,页面自己回来。
149
149
 
150
150
  | 平台 | 用户级拥有者 | 启动时机 | 接管后页面重启 | 说明 |
151
151
  | --- | --- | --- | --- | --- |
152
- | macOS | `launchd` LaunchAgent | 用户登录 | 可以 | 首个真机验证目标;不需要管理员。 |
153
- | Linux | `systemd --user` | 用户登录 | 已实现;请在你的机器上验证 | 登出后仍运行需可选的 `loginctl enable-linger`;CLI 不会自动启用。unit 模板有渲染测试,但尚未在真实 Linux 主机上跑过。 |
154
- | Windows | Task Scheduler + 当前用户 wrapper | 用户登录 | 提供模板;上线前须 Windows 真机验证 | 不需要管理员。 |
152
+ | macOS | `launchd` LaunchAgent | 用户登录 | 可以 | 已真机验证;不需要管理员。 |
153
+ | Linux | `systemd --user` | 用户登录 | 已实现;请在你自己机器上验证 | 想在登出后继续运行,可自行开启 `loginctl enable-linger`(本插件不会替你开)。 |
154
+ | Windows | 任务计划程序 + 当前用户 wrapper | 用户登录 | 已实现;请在你自己机器上验证 | 不需要管理员。 |
155
155
 
156
- 生成的服务定义使用绝对 Node/DSH 路径和最小、非敏感环境;不会复制终端里的 API key、npm token、cookie 或代理凭据。原生文件、日志、状态、修复和卸载见[用户服务监督说明](docs/service-supervision.md)。
156
+ 生成的服务文件里只有绝对路径,**没有密钥**——不会带上你终端里的 API key、npm token、cookie 或代理凭据。原生文件、日志、状态与卸载见[用户服务监督说明](docs/service-supervision.md);想在 Linux 或 Windows 上自己确认,见[跨平台验收步骤](docs/platform-acceptance.md)。
157
157
 
158
- 重启会中断运行中的工作,并不会保存或续跑它。按钮会列出活动 Agent、Job 与 Terminal,并在展示后才允许**强制重启**。如果无法完整读取活动工作、DSH Desktop 管理当前 Host,或当前进程仍是终端启动,Host 会拒绝请求——即使浏览器伪造了 RPC 也一样。
158
+ 重启会中断正在跑的工作,且不会保存。如果 DSH 列不出正在跑什么,或者它本来就是终端直启、桌面应用托管的,它会直接拒绝重启——网页端说什么都没用。
159
159
 
160
160
  ## 局域网(非回环页面)访问
161
161
 
@@ -170,13 +170,14 @@ DSH 对来源不是 loopback(`localhost` / `127.0.0.1`)的页面会关闭 Ho
170
170
 
171
171
  ## 兼容性
172
172
 
173
- 当前发布:插件 **`0.3.0`** 已针对 DeepSeek Harness **`0.2.1-alpha.1`**(当前发布版)、**`0.2.0-rc.2`**、**`0.2.0-rc.1`**、**`0.1.7-rc.2`**、**`0.1.7-rc.1`** 与 **`0.1.7-alpha.2`** 验证。
173
+ 当前发布:插件 **`0.3.1`** 已针对 DeepSeek Harness **`0.2.1-alpha.1`**(当前发布版)、**`0.2.0-rc.2`**、**`0.2.0-rc.1`**、**`0.1.7-rc.2`**、**`0.1.7-rc.1`** 与 **`0.1.7-alpha.2`** 验证。
174
174
 
175
175
  ### 插件版本与 DeepSeek Harness 版本的对应关系
176
176
 
177
177
  | 插件版本 | 已验证的 DeepSeek Harness | npm 发布状态 | 该版本是什么 |
178
178
  | --- | --- | --- | --- |
179
- | **`0.3.0`** | `0.2.1-alpha.1`、`0.2.0-rc.2`、`0.2.0-rc.1`、`0.1.7-rc.2`、`0.1.7-rc.1`、`0.1.7-alpha.2` | `latest` | 新增受监督的**重启**按钮与跨平台用户级服务 CLI(`plan`/`install`/`activate`/`status`/`stop`/`uninstall`)。没有用户级服务接管时按钮保持禁用;Host 会自行复核监督标记与平台身份、列出正在运行的 Agent/Job/终端,读不完整就拒绝。已在 macOS 真机跑通闭环:强制重启 → 退出码 `42` → launchd 拉起 → 页面自动刷新。公开承诺从「只读、绝不重启」变为「只读 + 安装用户级服务后的一次受限重启」 |
179
+ | **`0.3.1`** | `0.2.1-alpha.1`、`0.2.0-rc.2`、`0.2.0-rc.1`、`0.1.7-rc.2`、`0.1.7-rc.1`、`0.1.7-alpha.2` | `latest` | 小补丁:设置页也说明重启前提,会中断的任务列表不再把按钮挤走;新增 Linux/Windows 自验步骤 |
180
+ | **`0.3.0`** | `0.2.1-alpha.1`、`0.2.0-rc.2`、`0.2.0-rc.1`、`0.1.7-rc.2`、`0.1.7-rc.1`、`0.1.7-alpha.2` | 已发布 | 新增受监督的**重启**按钮与跨平台用户级服务 CLI(`plan`/`install`/`activate`/`status`/`stop`/`uninstall`)。没有用户级服务接管时按钮保持禁用;Host 会自行复核监督标记与平台身份、列出正在运行的 Agent/Job/终端,读不完整就拒绝。已在 macOS 真机跑通闭环:强制重启 → 退出码 `42` → launchd 拉起 → 页面自动刷新。公开承诺从「只读、绝不重启」变为「只读 + 安装用户级服务后的一次受限重启」 |
180
181
  | **`0.2.1`** | `0.2.1-alpha.1`、`0.2.0-rc.2`、`0.2.0-rc.1`、`0.1.7-rc.2`、`0.1.7-rc.1`、`0.1.7-alpha.2` | 已发布 | 修复「取消勾选后品牌行空白」:隐藏版本芯片时改为注销 `sidebar.brand.name` 注册,而不是留一个空白占用者,DSH 官方的鱼标与字标因此会回来;设置里的说明同步更新 |
181
182
  | **`0.2.0`** | `0.2.1-alpha.1`、`0.2.0-rc.2`、`0.2.0-rc.1`、`0.1.7-rc.2`、`0.1.7-rc.1`、`0.1.7-alpha.2` | 已发布 | 从「通道目录」改为「一条发布线」:Host 只保留 npm 全部 dist-tag 里最新的那个版本(稳定版、RC、Alpha 一视同仁),面板直接显示当前运行版本;有更新时给出更新版本号与钉在它上面的命令。`channel` 偏好、通道列表与通道下拉框全部移除,设置区块显示同一份结论 |
182
183
  | **`0.1.12`** | `0.2.0-rc.2`、`0.2.0-rc.1`、`0.1.7-rc.2`、`0.1.7-rc.1`、`0.1.7-alpha.2` | 已发布 | 声明兼容正在运行的 `0.2.0-rc.2`:逐包核对 `dsh.client.inject` 里 7 个包在 rc.1 → rc.2 之间的差异(仅 3 个文件有实质改动,本插件用到的接口面无一变化),把 `0.2.0-rc.2` 加入已验证清单,消除跟随 `latest` 时的「尚未验证兼容」提示;并在一次性 profile + 真实浏览器里实测芯片、面板与设置区块;代码零改动 |
@@ -199,7 +200,7 @@ DSH 对来源不是 loopback(`localhost` / `127.0.0.1`)的页面会关闭 Ho
199
200
  - 需要精确对应时显式指定版本:
200
201
 
201
202
  ```sh
202
- dsh plugin --profile web add dsh-update-status@0.3.0 # DSH 0.2.1-alpha.1
203
+ dsh plugin --profile web add dsh-update-status@0.3.1 # DSH 0.2.1-alpha.1
203
204
  dsh plugin --profile web add dsh-update-status@0.2.0 # DSH 0.2.1-alpha.1、0.2.0-rc.2、0.2.0-rc.1、0.1.7-rc.2、0.1.7-rc.1 或 0.1.7-alpha.2
204
205
  dsh plugin --profile web add dsh-update-status@0.1.12 # DSH 0.2.0-rc.2、0.2.0-rc.1、0.1.7-rc.2、0.1.7-rc.1 或 0.1.7-alpha.2
205
206
  dsh plugin --profile web add dsh-update-status@0.1.11 # DSH 0.2.0-rc.1、0.1.7-rc.2、0.1.7-rc.1 或 0.1.7-alpha.2
@@ -216,7 +217,7 @@ DSH 对来源不是 loopback(`localhost` / `127.0.0.1`)的页面会关闭 Ho
216
217
  - 有两处声明让已验证的版本线能正常加载,并由测试守住:
217
218
  - `dsh.engines.dsh` 与 `peerDependencies['@deepseek-ai/dsh-settings']` 都声明 `>=0.1.7-alpha.2 <0.3.0`,六个已验证版本都在范围内。下界特意写成这个 alpha:按 node-semver 默认的预发布规则,`>=0.1.6-0 <0.2.0` 这样的范围**并不接纳** `0.1.7-alpha.2`。上界在 `0.1.11` 从 `<0.2.0` 放宽到 `<0.3.0` 是同一件事的镜像:DSH 会在 profile 加载时拒绝不兼容的 bundle,而裸的 `<0.2.0` 恰好排除 `0.2.0` 正式版——DSH `0.2.0` 发布当天插件就会被静默丢弃。DSH 判定时使用 `includePrerelease: true`,所以 `0.2.0-rc.2` 本来就在这个范围内,`0.1.12` 无需再动范围;反过来说,把范围写成 `... || 0.2.0-rc.1 || >=0.2.0 <0.3.0` 这类枚举的插件会因为 `>=0.2.0` 不接纳 `0.2.0` 预发布而被 rc.2 拒载。
218
219
  - `@deepseek-ai/schemastery` 是 **peer**,不是普通依赖:DSH 0.1.7 只从运行安装解析 link 插件的 peer 依赖,否则 `link:` 安装会连 Host 半边都 import 失败。
219
- - 每个版本的中英文详细说明(改了什么、影响谁、需要做什么)手写后直接作为 GitHub Release 正文:[`v0.3.0`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.3.0.md) · [`v0.2.1`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.2.1.md) · [`v0.2.0`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.2.0.md) · [`v0.1.12`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.12.md) · [`v0.1.11`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.11.md) · [`v0.1.10`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.10.md) · [`v0.1.9`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.9.md) · [`v0.1.8`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.8.md) · [`v0.1.7`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.7.md) · [`v0.1.6`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.6.md) · [`v0.1.5`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.5.md) · [`v0.1.4`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.4.md) · [`v0.1.3`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.3.md)(含从未发布的 `0.1.2`)。
220
+ - 每个版本的中英文详细说明(改了什么、影响谁、需要做什么)手写后直接作为 GitHub Release 正文:[`v0.3.0`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.3.1.md) · [v0.3.0](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.3.0.md) · [`v0.2.1`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.2.1.md) · [`v0.2.0`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.2.0.md) · [`v0.1.12`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.12.md) · [`v0.1.11`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.11.md) · [`v0.1.10`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.10.md) · [`v0.1.9`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.9.md) · [`v0.1.8`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.8.md) · [`v0.1.7`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.7.md) · [`v0.1.6`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.6.md) · [`v0.1.5`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.5.md) · [`v0.1.4`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.4.md) · [`v0.1.3`](https://github.com/idoall/dsh-update-status/blob/main/docs/releases/v0.1.3.md)(含从未发布的 `0.1.2`)。
220
221
 
221
222
  ## 配置
222
223
 
@@ -0,0 +1,48 @@
1
+ # Verifying on Linux or Windows
2
+
3
+ macOS is verified on a real machine. The Linux and Windows service templates are generated and covered by tests, but **have not been run on those systems yet** — that is what this page is for.
4
+
5
+ Three steps, each one safe to stop at. If a step fails, the section below says what to look at.
6
+
7
+ ## 1. Look first — writes nothing
8
+
9
+ ```sh
10
+ dsh plugin --profile web exec dsh-update-status-service plan --workspace "$PWD" --dry-run
11
+ ```
12
+
13
+ Check the printed Node path, DSH path and port against what you actually run. Override any of them with `--node`, `--dsh`, `--port`.
14
+
15
+ ## 2. Write the service definition — still starts nothing
16
+
17
+ ```sh
18
+ dsh plugin --profile web exec dsh-update-status-service install --workspace "$PWD"
19
+ ```
20
+
21
+ It runs a boot precheck that does not bind the port. If that precheck fails, nothing is written.
22
+
23
+ ## 3. Stop your terminal's `dsh web` yourself, then
24
+
25
+ ```sh
26
+ dsh plugin --profile web exec dsh-update-status-service activate --workspace "$PWD"
27
+ ```
28
+
29
+ It refuses to start while the port is still busy — that is deliberate, so the DSH you are using is never pushed aside.
30
+
31
+ Then reopen the Web UI and check that **Restart** in the update panel is enabled (not greyed out). That is the whole acceptance test: restart availability means the running process was recognised as service-managed.
32
+
33
+ ## Where to look when something is off
34
+
35
+ | Platform | Status command |
36
+ | --- | --- |
37
+ | Linux | `systemctl --user status dsh-update-status-web.service` and `journalctl --user -u dsh-update-status-web.service -f` |
38
+ | Windows | Task Scheduler → **DSH Update Status Web**, or `schtasks /Query /TN "DSH Update Status Web" /V /FO LIST` |
39
+
40
+ If **Restart** stays greyed out after activating, the running process was not recognised as the service's process. For an issue report, include the `plan` output and the status output above.
41
+
42
+ ## Removing it again
43
+
44
+ ```sh
45
+ dsh plugin --profile web exec dsh-update-status-service uninstall --workspace "$PWD"
46
+ ```
47
+
48
+ That removes only the definition this tool created. Logs are kept.
package/lib/client.js CHANGED
@@ -946,6 +946,10 @@ window.__ModuleLoader__.load({
946
946
  }),
947
947
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RestartControl, { restart: ui.restart })
948
948
  ]
949
+ }),
950
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
951
+ className: "dus-settings-hint",
952
+ children: t("panel.readOnly")
949
953
  })
950
954
  ]
951
955
  })
@@ -2066,6 +2070,9 @@ window.__ModuleLoader__.load({
2066
2070
  .dus-restart{border-color:var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-primary)}
2067
2071
  .dus-restart-message{color:var(--dsw-alias-label-secondary);flex-basis:100%;font-size:11px;line-height:1.45;margin:0}
2068
2072
  .dus-restart-work{color:var(--dsw-alias-label-secondary);flex-basis:100%;font-size:11px;line-height:1.5;margin:0;overflow-wrap:anywhere;padding-left:18px}
2073
+ /* A job label is a whole shell command: clamp it so one long command cannot push
2074
+ the panel's buttons off a 390px card. */
2075
+ .dus-restart-work li{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
2069
2076
  .dus-command-label{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600;margin:0 0 6px}
2070
2077
  .dus-command{background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;display:block;font-family:var(--dsw-font-family-mono,ui-monospace,SFMono-Regular,Menlo,monospace);font-size:11px;line-height:1.5;margin:0;overflow-wrap:anywhere;padding:9px;tab-size:2;user-select:text;white-space:pre-wrap}
2071
2078
  .dus-command:focus{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["PLUGIN_ID","PLUGIN_ID","sameSnapshot"],"sources":["../src/shared/visual-state.ts","../src/shared/types.ts","../src/client/i18n.ts","../src/client/react.ts","../src/client/components.tsx","../src/client/contract.ts","../src/client/stores.ts","../src/client/settings/hostDirectScope.ts","../src/client/settings/configFormScope.ts","../src/client/settings/settingsChannel.ts","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/**\n * The single place that decides which visual state the sidebar chip is in.\n *\n * It lives in `shared` rather than inside the component so the decision is a pure\n * function that a test can drive directly: the regression this guards against is\n * exactly a state-classification mistake, and it was invisible to every test while\n * it sat inside TSX.\n */\nimport type { UpdateStatus } from './types.ts'\n\nexport type VisualState = 'loading' | 'update' | 'current' | 'problem'\n\n/**\n * Only a read the plugin could not complete is a chip-level problem.\n *\n * An advisory notice — a preview channel this bundle has not been verified against,\n * say — leaves the chip surface alone and belongs in the panel. Repainting the brand\n * row for a plugin-side bookkeeping fact is the surprise the breathing dot replaced:\n * an operator who upgrades DSH ahead of this bundle must not watch the chip turn red.\n *\n * A Host older than `warningKind` sends no field at all; `undefined` then keeps the\n * previous behaviour (any warning is a problem) rather than silently dropping a real\n * failure on the floor.\n */\nexport function isChipProblem(status: UpdateStatus | null): boolean {\n if (status === null) return false\n if (status.warningKind === undefined) return status.warning !== null\n return status.warningKind === 'failure'\n}\n\nexport function visualState(status: UpdateStatus | null, loading: boolean, error: string | null): VisualState {\n if (loading) return 'loading'\n if (error !== null || isChipProblem(status)) return status?.hasUpdate === true ? 'update' : 'problem'\n return status?.hasUpdate === true ? 'update' : 'current'\n}\n","/**\n * JSON-only contract shared by the Host and Web halves.\n *\n * The static package uses the authenticated Connection RPC channel because\n * `harness.handle` / `host.call` are dynamic-Cordis-only closure APIs. The\n * endpoint vocabulary remains deliberately small and private to this plugin\n * channel.\n *\n * There is ONE release line, not a channel catalogue: the Host reports the\n * newest version the npm registry publishes under any dist-tag, and the client\n * compares it with the running version. `latest`, `next` and `alpha` are npm's\n * bookkeeping, not a user choice this plugin asks anyone to make.\n */\n\nexport const PLUGIN_ID = 'dsh-update-status'\nexport const PACKAGE_NAME = '@deepseek-ai/dsh'\n/** Shared Connection RPC channel. Custom prefixes 405 on the SPA fallback. */\nexport const UPDATE_STATUS_CHANNEL = '/api'\nexport const RELEASES_URL = 'https://github.com/deepseek-ai/deepseek-harness/releases'\n/**\n * DSH releases this bundle has actually been verified against. The same list is\n * declared in package.json's `dsh.compatibility.dshReleases`, and every other\n * release the registry reports stays `unverified` — the plugin never claims a\n * compatibility nobody checked.\n */\nexport const VERIFIED_DSH_VERSIONS: readonly string[] = ['0.1.7-alpha.2', '0.1.7-rc.1', '0.1.7-rc.2', '0.2.0-rc.1', '0.2.0-rc.2', '0.2.1-alpha.1']\nexport const DEFAULT_CACHE_TTL_MINUTES = 360\nexport const MIN_CACHE_TTL_MINUTES = 30\nexport const MAX_CACHE_TTL_MINUTES = 1_440\n\nexport function isCacheTtlMinutes(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value)\n && value >= MIN_CACHE_TTL_MINUTES && value <= MAX_CACHE_TTL_MINUTES\n}\n\nexport const UPDATE_ENDPOINTS = {\n getStatus: 'dsh-update-status.get-status',\n checkUpdate: 'dsh-update-status.check-update',\n} as const\n\n/** Restart RPC remains on the same authenticated Connection `/api` channel. */\nexport const RESTART_ENDPOINTS = {\n status: 'dsh-update-status.restart-status',\n check: 'dsh-update-status.restart-check',\n request: 'dsh-update-status.restart',\n} as const\n\nexport type UpdateEndpoint = (typeof UPDATE_ENDPOINTS)[keyof typeof UPDATE_ENDPOINTS]\nexport type RestartEndpoint = (typeof RESTART_ENDPOINTS)[keyof typeof RESTART_ENDPOINTS]\nexport type ReleaseCompatibility = 'verified' | 'unverified' | 'incompatible'\n/**\n * Severity of `warning`, so a surface can tell \"the plugin could not determine\n * the update state\" from \"here is something worth knowing\".\n *\n * - `failure`: no usable answer — a failed registry read, or a version SemVer\n * cannot compare. This is what justifies repainting the sidebar chip.\n * - `notice`: the answer is complete and usable; the text is advisory, e.g. a\n * newer release this bundle has not been verified against. The chip must NOT\n * repaint for this — an operator upgrading DSH ahead of the plugin would\n * otherwise see the chip turn red for a plugin-side bookkeeping fact.\n *\n * Optional on purpose: a Host older than this field leaves it `undefined`, and\n * the client then falls back to treating any warning as a failure.\n */\nexport type UpdateWarningKind = 'failure' | 'notice'\nexport type InstallKind = 'npm-global' | 'pnpm-global' | 'source-checkout' | 'unknown'\n\n/** Language-neutral warning facts; the browser renders them in its own locale. */\nexport type UpdateWarning =\n | { code: 'registry-unavailable'; detail: string }\n | { code: 'version-incomparable'; currentVersion: string; latestVersion: string }\n /** A newer release exists, but this bundle has not been tested against it. */\n | { code: 'version-unverified'; version: string }\n /**\n * The Host resolved a `@deepseek-ai/schemastery` that DSH does not ship, so the\n * Loader's volatile projection is unavailable and the preference fields cannot\n * be marked volatile. Advisory: the version answer itself is complete, and the\n * text carries the exact directory to remove. See `host/schemastery.ts`.\n */\n | { code: 'stale-schemastery'; version: string | null; path: string; nodeModulesDir: string | null }\n\n/** Arguments accepted by either read/check endpoint. */\nexport interface CheckUpdateRequest {\n force?: boolean\n /** User preference, bounded by the Host before it affects cache expiry. */\n cacheTtlMinutes?: number\n}\n\n/**\n * A lossless, JSON-serializable snapshot used by both browser surfaces.\n * Nulls are intentional: a failed first check must still render a truthful\n * current-version card instead of an empty or malformed UI.\n */\nexport interface UpdateStatus {\n currentVersion: string\n /** Newest version the registry publishes under any dist-tag; null when unknown. */\n latestVersion: string | null\n hasUpdate: boolean\n /** Whether this bundle was verified against `latestVersion`. */\n compatibility: ReleaseCompatibility\n cached: boolean\n checkedAt: string | null\n /** English fallback for older clients; current clients localize `warnings`. */\n warning: string | null\n /** Severity of `warning`; absent from a Host older than the field. */\n warningKind?: UpdateWarningKind | null\n /** Structured warning facts; absent from a Host older than this field. */\n warnings?: UpdateWarning[]\n installKind: InstallKind\n upgradeCommand: string\n releaseUrl: string\n changelogUrl: string\n publishedAt: string | null\n packageName: string\n /** Phase 1 is informational only; the GUI must never apply an update. */\n canApplyInPlace: false\n}\n","/** Tiny bilingual dictionary kept local to avoid changing the host locale tree. */\n\nimport type { UpdateStatus, UpdateWarning } from '../shared/types.ts'\n\nexport type Language = 'zh' | 'en'\n\ntype Entry = { zh: string; en: string }\n\nconst DICTIONARY: Record<string, Entry> = {\n 'brand.status': { zh: 'DSH 版本状态', en: 'DSH version status' },\n 'brand.checking': { zh: '正在检查版本', en: 'Checking version' },\n 'brand.update': { zh: '有可用更新', en: 'Update available' },\n 'brand.current': { zh: '已是最新或尚未发现更新', en: 'Up to date or no update found' },\n 'brand.problem': { zh: '检查遇到问题', en: 'Check encountered a problem' },\n 'panel.title': { zh: 'DSH 更新状态', en: 'DSH update status' },\n 'panel.close': { zh: '关闭', en: 'Close' },\n 'panel.cacheDuration': { zh: '缓存时长', en: 'Cache duration' },\n 'panel.minutes': { zh: '分钟', en: 'minutes' },\n 'panel.cacheHint': { zh: '到期后在下次打开或读取状态时检查;不会后台轮询。点击“检查更新”会立即检查。', en: 'After expiry, DSH checks only on the next status read; there is no background polling. “Check for updates” checks immediately.' },\n 'panel.cacheReadonly': { zh: '此连接的缓存设置为只读。', en: 'This connection’s cache setting is read-only.' },\n 'panel.current': { zh: '当前运行版本', en: 'Current running version' },\n 'panel.newer': { zh: '可更新至', en: 'Newer version available' },\n 'panel.compatVerified': { zh: '已验证兼容', en: 'Verified compatible' },\n 'panel.compatUnverified': { zh: '尚未验证兼容', en: 'Compatibility unverified' },\n 'panel.compatIncompatible': { zh: '已知不兼容', en: 'Known incompatible' },\n 'panel.notChecked': { zh: '尚未取得', en: 'Not available yet' },\n 'panel.available': { zh: '发现新版本', en: 'Update available' },\n 'panel.currentState': { zh: '已是最新', en: 'Up to date' },\n 'panel.cached': { zh: '来自 Host 缓存', en: 'From Host cache' },\n 'panel.live': { zh: '刚从 npm registry 检查', en: 'Checked npm registry now' },\n 'panel.checkedAt': { zh: '上次检查', en: 'Last checked' },\n 'panel.publishedAt': { zh: '发布时间', en: 'Published' },\n 'panel.check': { zh: '检查更新', en: 'Check for updates' },\n 'panel.checking': { zh: '检查中…', en: 'Checking…' },\n 'panel.releaseNotes': { zh: '发布说明', en: 'Release notes' },\n 'panel.command': { zh: '升级命令', en: 'Upgrade command' },\n 'panel.copy': { zh: '复制命令', en: 'Copy command' },\n 'panel.copied': { zh: '已复制', en: 'Copied' },\n 'panel.copyFallback': { zh: '浏览器未允许复制;已选中文本,请长按或复制。', en: 'Clipboard unavailable; the command is selected for long-press or copy.' },\n 'panel.commandNote': { zh: '仅复制,不会执行。请在运行 DSH 的那台电脑的终端执行;完成后仅在已安装用户级监督服务时使用本面板的重启,否则由你自行重启 DSH。', en: 'Copy only — nothing runs here. Execute it in a terminal on the computer running DSH, then use this panel’s restart only when user-level supervision is installed; otherwise restart DSH yourself.' },\n 'panel.readOnly': { zh: '插件不会安装、升级、回滚或替换 DSH 文件;只有当前 DSH 已由用户显式安装的后台服务监督时,才允许安全重启。', en: 'The plugin never installs, upgrades, rolls back, or replaces DSH files. Safe restart is available only when the current DSH is supervised by a user-installed service.' },\n 'restart.title': { zh: '安全重启 DSH Web', en: 'Safely restart DSH Web' },\n 'restart.button': { zh: '重启', en: 'Restart' },\n 'restart.loading': { zh: '读取重启状态…', en: 'Reading restart status…' },\n 'restart.checking': { zh: '检查活动任务…', en: 'Checking active work…' },\n 'restart.confirm': { zh: '确认重启', en: 'Confirm restart' },\n 'restart.force': { zh: '强制重启', en: 'Force restart' },\n 'restart.cancel': { zh: '取消', en: 'Cancel' },\n 'restart.retry': { zh: '重新检查', en: 'Check again' },\n 'restart.confirmHint': { zh: '再次点击“确认重启”后,页面会短暂断开;后台服务恢复新实例后将自动刷新。', en: 'Click Confirm restart again. The page disconnects briefly and refreshes once the supervised service starts a new instance.' },\n 'restart.activeWarning': { zh: '检测到 {count} 个正在运行的任务。强制重启会中断它们。', en: '{count} active work item(s) are running. A forced restart interrupts them.' },\n 'restart.waitingShort': { zh: '重启中…', en: 'Restarting…' },\n 'restart.waiting': { zh: '正在等待后台服务恢复 DSH(已等待 {seconds} 秒)…', en: 'Waiting for the user service to restore DSH ({seconds}s elapsed)…' },\n 'restart.timeout': { zh: '60 秒内未检测到新 DSH 实例。请检查用户服务状态和日志;重新加载本页不会再次触发重启。', en: 'No new DSH instance was detected within 60 seconds. Check the user-service status and logs; reloading this page will not trigger another restart.' },\n 'restart.unavailable': { zh: '当前 DSH 不是由已确认的用户级后台服务启动。为避免页面重启后无法恢复,按钮已禁用;请先按服务管理文档安装并接管 DSH。', en: 'This DSH is not running under a verified user-level service. Restart is disabled so the page cannot strand itself; install and activate supervision first.' },\n 'restart.unavailableDesktop': { zh: 'DSH Desktop 管理当前 Host;请退出并重新打开桌面应用。', en: 'DSH Desktop manages this Host. Quit and reopen the desktop app instead.' },\n 'restart.unavailableMismatch': { zh: '检测到监督标记,但当前进程不符合对应平台服务的身份。为安全起见,重启已禁用。', en: 'A supervision marker exists, but this process does not match the platform service identity. Restart is disabled for safety.' },\n 'restart.unavailableActivity': { zh: '无法完整读取正在运行的任务。为避免中断未能列出的工作,重启已禁用。', en: 'Active work could not be inspected completely. Restart is disabled to avoid interrupting work that could not be listed.' },\n 'restart.unavailableTimer': { zh: '当前 Host 没有可用的安全退出调度器。为确保响应先送达浏览器,重启已禁用。', en: 'This Host has no safe exit scheduler. Restart is disabled so its response can reach the browser first.' },\n 'restart.unavailableStale': { zh: '此页面连接的是过期 DSH 实例。请等待恢复或重新加载后再重试。', en: 'This page is connected to a stale DSH instance. Wait for recovery or reload before trying again.' },\n 'panel.error': { zh: '检查提示', en: 'Check notice' },\n 'warning.registryUnavailable': { zh: '无法检查 npm registry:{detail}', en: 'Unable to check the npm registry: {detail}' },\n 'warning.versionIncomparable': { zh: '无法按 SemVer 比较当前版本 {currentVersion} 与线上版本 {latestVersion}。', en: 'Unable to compare the current version {currentVersion} with the published version {latestVersion} using SemVer.' },\n 'warning.versionUnverified': { zh: '线上版本 {version} 尚未验证与本插件兼容。', en: 'Version {version} has not been verified as compatible with this plugin.' },\n 'warning.staleSchemastery': { zh: '本插件解析到的 @deepseek-ai/schemastery(版本 {version})来自 {path},不是 DSH 自带的那份,偏好字段无法标记为 volatile。请删除该残留目录并重启 DSH:rm -rf {nodeModulesDir}', en: 'This plugin resolved @deepseek-ai/schemastery {version} from {path} instead of the copy DSH provides, so preference fields cannot be marked volatile. Remove the stale directory and restart DSH: rm -rf {nodeModulesDir}' },\n 'warning.staleSchemasteryNoPath': { zh: '本插件解析到的 @deepseek-ai/schemastery(版本 {version})来自 {path},不是 DSH 自带的那份,偏好字段无法标记为 volatile。请删除该处残留的 @deepseek-ai/schemastery 目录并重启 DSH。', en: 'This plugin resolved @deepseek-ai/schemastery {version} from {path} instead of the copy DSH provides, so preference fields cannot be marked volatile. Remove the stale @deepseek-ai/schemastery directory there and restart DSH.' },\n 'guidance.sourceCheckout': { zh: '请更新 DSH 源码 checkout、安装依赖并重新构建;本插件无法从 GUI 原地替换。', en: 'Update the DSH source checkout, install its dependencies, and rebuild it; this plugin cannot replace it in place from the GUI.' },\n 'guidance.unknownInstall': { zh: '升级前请先确认 DSH 的安装方式;本插件无法代为执行升级。', en: 'Confirm how DSH was installed before upgrading; this plugin cannot perform the upgrade for you.' },\n 'settings.title': { zh: '版本与更新', en: 'Version & updates' },\n 'settings.sidebar': { zh: '在侧栏显示版本状态入口', en: 'Show the version-status entry in the sidebar' },\n 'settings.sidebarHint': { zh: '关闭后不再渲染品牌行里的版本芯片,品牌行恢复为 DSH 官方内容。不会执行或安排升级。', en: 'When off, the version chip in the brand row is not rendered and the brand row falls back to DSH’s own content. No update is run or scheduled.' },\n 'settings.readonly': { zh: '此连接的设置为只读;显示状态不受影响。', en: 'Settings are read-only on this connection; status display is unchanged.' },\n}\n\nexport function languageOf(): Language {\n try {\n return navigator.language.toLowerCase().startsWith('zh') ? 'zh' : 'en'\n } catch {\n return 'en'\n }\n}\n\nexport function t(key: keyof typeof DICTIONARY, params: Record<string, string> = {}, language: Language = languageOf()): string {\n const entry = DICTIONARY[key]\n if (entry === undefined) return key\n const template = entry[language] ?? entry.en\n return template.replace(/\\{([A-Za-z]+)\\}/g, (match, name: string) => params[name] ?? match)\n}\n\nexport function warningText(warning: UpdateWarning, language: Language = languageOf()): string {\n switch (warning.code) {\n case 'registry-unavailable': return t('warning.registryUnavailable', { detail: warning.detail }, language)\n case 'version-incomparable': return t('warning.versionIncomparable', {\n currentVersion: warning.currentVersion,\n latestVersion: warning.latestVersion,\n }, language)\n case 'version-unverified': return t('warning.versionUnverified', { version: warning.version }, language)\n case 'stale-schemastery': {\n const params = { version: warning.version ?? '—', path: warning.path }\n // Only print a removal command when the Host could name the directory to\n // remove; the module path alone would make `rm -rf` delete a single file.\n return warning.nodeModulesDir === null\n ? t('warning.staleSchemasteryNoPath', params, language)\n : t('warning.staleSchemastery', { ...params, nodeModulesDir: warning.nodeModulesDir }, language)\n }\n }\n}\n\n/** Prefer structured warnings, but keep the Host string for mixed-version clients. */\nexport function localizedWarning(status: Pick<UpdateStatus, 'warning' | 'warnings'>, language: Language = languageOf()): string | null {\n const warnings = status.warnings ?? []\n return warnings.length === 0 ? status.warning : warnings.map(item => warningText(item, language)).join(' ')\n}\n\nexport function localizedUpgradeGuidance(\n status: Pick<UpdateStatus, 'installKind' | 'upgradeCommand'>,\n language: Language = languageOf(),\n): string {\n if (status.installKind === 'source-checkout') return t('guidance.sourceCheckout', {}, language)\n if (status.installKind === 'unknown') return t('guidance.unknownInstall', {}, language)\n return status.upgradeCommand\n}\n","/** React arrives from DSH's frozen browser module table at client materialization. */\nimport type * as ReactNS from 'react'\n\n// eslint/oxlint would normally discourage require; DSH's closure factory makes\n// this the supported runtime import path for a third-party client bundle.\nexport const React: typeof ReactNS = require('react') as typeof ReactNS\nexport const h = React.createElement\n","/** Sidebar chip, overlay detail panel, and settings page. */\n\nimport type * as ReactNS from 'react'\nimport { isChipProblem, visualState } from '../shared/visual-state.ts'\nimport { MAX_CACHE_TTL_MINUTES, MIN_CACHE_TTL_MINUTES, isCacheTtlMinutes, type ReleaseCompatibility, type UpdateStatus } from '../shared/types.ts'\nimport type { PreferencesStore, PanelStore, RestartStore, StatusStore } from './stores.ts'\nimport { localizedUpgradeGuidance, localizedWarning, t } from './i18n.ts'\nimport { React, h } from './react.ts'\n\nfunction useObservable<T>(store: { subscribe(listener: () => void): () => void; getSnapshot(): T }): T {\n return React.useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)\n}\n\nfunction timeText(value: string | null): string | null {\n if (value === null) return null\n try {\n return new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' }).format(new Date(value))\n } catch {\n return value\n }\n}\n\nfunction shortVersion(value: string): string {\n if (value.length <= 16) return value\n return value.slice(0, 15) + '…'\n}\n\nfunction compatibilityLabel(value: ReleaseCompatibility): string {\n if (value === 'verified') return t('panel.compatVerified')\n if (value === 'incompatible') return t('panel.compatIncompatible')\n return t('panel.compatUnverified')\n}\n\n/** Persists a cache policy only; it never schedules a browser or Host timer. */\nfunction CacheTtlControl({ preferences }: { preferences: PreferencesStore }): ReactNS.ReactElement {\n const snapshot = useObservable(preferences)\n const [draft, setDraft] = React.useState(String(snapshot.cacheTtlMinutes))\n React.useEffect(() => { setDraft(String(snapshot.cacheTtlMinutes)) }, [snapshot.cacheTtlMinutes])\n const save = (): void => {\n const value = Number(draft)\n if (isCacheTtlMinutes(value)) preferences.setCacheTtlMinutes(value)\n else setDraft(String(snapshot.cacheTtlMinutes))\n }\n return (\n <div className=\"dus-cache-setting\">\n <label className=\"dus-cache-field\">\n <span>{t('panel.cacheDuration')}</span>\n <span className=\"dus-cache-input-wrap\">\n <input\n className=\"dus-cache-input\"\n type=\"number\"\n inputMode=\"numeric\"\n min={MIN_CACHE_TTL_MINUTES}\n max={MAX_CACHE_TTL_MINUTES}\n step={1}\n value={draft}\n disabled={!snapshot.writable}\n aria-describedby=\"dus-cache-hint\"\n onChange={(event) => { setDraft(event.currentTarget.value) }}\n onBlur={save}\n onKeyDown={(event) => {\n if (event.key === 'Enter') { event.currentTarget.blur() }\n if (event.key === 'Escape') { setDraft(String(snapshot.cacheTtlMinutes)); event.currentTarget.blur() }\n }}\n />\n <span>{t('panel.minutes')}</span>\n </span>\n </label>\n <p className=\"dus-cache-hint\" id=\"dus-cache-hint\">{t('panel.cacheHint')}</p>\n {!snapshot.writable && <p className=\"dus-cache-hint\">{t('panel.cacheReadonly')}</p>}\n </div>\n )\n}\n\nfunction badgeLabel(status: UpdateStatus | null, loading: boolean, error: string | null): string {\n const state = visualState(status, loading, error)\n if (state === 'loading') return t('brand.checking')\n if (state === 'update') return t('brand.update')\n if (state === 'problem') return t('brand.problem')\n return t('brand.current')\n}\n\nexport interface SharedUi {\n status: StatusStore\n preferences: PreferencesStore\n panel: PanelStore\n restart: RestartStore\n}\n\n/** Occupies ONLY sidebar.brand.name; the official fish mark stays untouched. */\nexport function BrandName({ ui }: { ui: SharedUi }): ReactNS.ReactElement | null {\n const preferences = useObservable(ui.preferences)\n const snapshot = useObservable(ui.status)\n // Defence in depth only: the entry that renders this component is already\n // disposed while the preference is off, because a single slot arbitrates by\n // priority and returning null would keep occupying the cell. See\n // client/index.ts — that disposal is what brings the official brand back.\n if (!preferences.sidebarEnabled) return null\n\n const state = visualState(snapshot.status, snapshot.loading, snapshot.error)\n const version = snapshot.status?.currentVersion ?? '—'\n const activate = (event: ReactNS.SyntheticEvent): void => {\n // The surrounding sidebar brand is an existing New Session <button>. This\n // non-button interaction prevents a nested button and stops its click.\n event.preventDefault()\n event.stopPropagation()\n // The official brand identity ancestor is aria-hidden. Do not leave focus\n // inside it while the modal dialog is open (Chrome otherwise warns).\n const target = event.currentTarget\n if (target instanceof HTMLElement) target.blur()\n ui.panel.toggle()\n }\n const onKeyDown = (event: ReactNS.KeyboardEvent<HTMLSpanElement>): void => {\n if (event.key !== 'Enter' && event.key !== ' ') return\n activate(event)\n }\n\n return (\n <span className=\"dus-brand-name\">\n {/* No handler: clicking this still bubbles to the shell's New Session button. */}\n <span className=\"dus-brand-deepseek\">DeepSeek</span>\n <span\n className=\"dus-badge\"\n data-update={snapshot.status?.hasUpdate === true || undefined}\n data-error={state === 'problem' || undefined}\n role=\"button\"\n tabIndex={0}\n aria-label={badgeLabel(snapshot.status, snapshot.loading, snapshot.error)}\n title={badgeLabel(snapshot.status, snapshot.loading, snapshot.error)}\n onClick={activate}\n onKeyDown={onKeyDown}\n >\n <span className=\"dus-badge-version\">{shortVersion(version)}</span>\n {/* `data-state` drives the palette (green when up to date); `data-update`\n and `data-loading` remain the louder, state-specific rules. */}\n <span className=\"dus-dot\" data-state={state} data-update={snapshot.status?.hasUpdate === true || undefined} data-loading={snapshot.loading || undefined} aria-hidden=\"true\" />\n </span>\n </span>\n )\n}\n\nfunction restartUnavailableText(reason: 'not-supervised' | 'desktop' | 'supervisor-mismatch' | 'activity-unavailable' | 'timer-unavailable' | 'stale-instance' | null | undefined): string {\n if (reason === 'desktop') return t('restart.unavailableDesktop')\n if (reason === 'supervisor-mismatch') return t('restart.unavailableMismatch')\n if (reason === 'activity-unavailable') return t('restart.unavailableActivity')\n if (reason === 'timer-unavailable') return t('restart.unavailableTimer')\n if (reason === 'stale-instance') return t('restart.unavailableStale')\n return t('restart.unavailable')\n}\n\nfunction RestartControl({ restart }: { restart: RestartStore }): ReactNS.ReactElement {\n const snapshot = useObservable(restart)\n React.useEffect(() => { void restart.refresh() }, [restart])\n const unavailable = snapshot.status !== null && !snapshot.status.available\n const hasActivity = snapshot.activity.length > 0\n const waiting = snapshot.phase === 'waiting'\n const label = snapshot.phase === 'unknown' ? t('restart.loading')\n : snapshot.phase === 'checking' ? t('restart.checking')\n : snapshot.phase === 'armed' ? (hasActivity ? t('restart.force') : t('restart.confirm'))\n : waiting ? t('restart.waitingShort')\n : snapshot.phase === 'timeout' ? t('restart.retry')\n : snapshot.phase === 'error' ? t('restart.retry')\n : t('restart.button')\n const disabled = snapshot.phase === 'unknown' || snapshot.phase === 'checking' || waiting || unavailable\n const click = (): void => {\n if (snapshot.phase === 'armed') { void restart.request(hasActivity); return }\n if (snapshot.phase === 'timeout' || snapshot.phase === 'error') { void restart.check(); return }\n void restart.check()\n }\n return <>\n <button\n className=\"dus-action dus-restart\"\n type=\"button\"\n disabled={disabled}\n title={unavailable ? restartUnavailableText(snapshot.status?.unavailableReason) : t('restart.title')}\n onClick={click}\n >{label}</button>\n {snapshot.phase === 'armed' && <button className=\"dus-action\" type=\"button\" onClick={() => restart.cancel()}>{t('restart.cancel')}</button>}\n {unavailable && <p className=\"dus-restart-message\" role=\"status\">{restartUnavailableText(snapshot.status?.unavailableReason)}</p>}\n {snapshot.phase === 'armed' && <p className=\"dus-restart-message\" role=\"status\">\n {hasActivity ? t('restart.activeWarning', { count: String(snapshot.activity.length) }) : t('restart.confirmHint')}\n </p>}\n {hasActivity && snapshot.phase === 'armed' && <ul className=\"dus-restart-work\">{snapshot.activity.map(item => <li key={`${item.type}:${item.id}`}>{item.label} · {item.status}</li>)}</ul>}\n {waiting && <p className=\"dus-restart-message\" role=\"status\">{t('restart.waiting', { seconds: String(Math.floor(snapshot.elapsedMs / 1000)) })}</p>}\n {snapshot.phase === 'timeout' && <p className=\"dus-restart-message\" role=\"alert\">{t('restart.timeout')}</p>}\n {snapshot.phase === 'error' && snapshot.error !== null && <p className=\"dus-restart-message\" role=\"alert\">{snapshot.error}</p>}\n </>\n}\n\nfunction statusSummary(status: UpdateStatus | null, loading: boolean, error: string | null): { kind: 'update' | 'ok' | 'warning' | 'error'; text: string } {\n if (loading && status === null) return { kind: 'warning', text: t('brand.checking') }\n if (error !== null) return { kind: 'error', text: error }\n if (status?.hasUpdate === true) {\n return { kind: 'update', text: `${t('panel.available')} · ${status.latestVersion ?? ''}`.trim() }\n }\n if (status !== null) {\n const warning = localizedWarning(status)\n if (warning !== null) return { kind: 'warning', text: warning }\n }\n return { kind: 'ok', text: t('panel.currentState') }\n}\n\nfunction selectCommand(element: HTMLElement | null): void {\n if (element === null) return\n try {\n const selection = window.getSelection()\n if (selection === null) return\n const range = document.createRange()\n range.selectNodeContents(element)\n selection.removeAllRanges()\n selection.addRange(range)\n element.focus()\n } catch {\n // The command remains ordinary selectable text even when selection fails.\n }\n}\n\n/**\n * The one upgrade command plus its copy affordance, shared by the panel and the\n * settings section so both surfaces can never disagree about what to run.\n */\nfunction CommandBlock({ command, disabled = false }: { command: string; disabled?: boolean }): ReactNS.ReactElement {\n const commandRef = React.useRef<HTMLElement | null>(null)\n const [copyMessage, setCopyMessage] = React.useState<string | null>(null)\n const copy = async (): Promise<void> => {\n setCopyMessage(null)\n try {\n if (!navigator.clipboard?.writeText) throw new Error('Clipboard API unavailable')\n await navigator.clipboard.writeText(command)\n setCopyMessage(t('panel.copied'))\n } catch {\n selectCommand(commandRef.current)\n setCopyMessage(t('panel.copyFallback'))\n }\n }\n return (\n <>\n <p className=\"dus-command-label\">{t('panel.command')}</p>\n <code ref={commandRef} className=\"dus-command\" tabIndex={0}>{command}</code>\n <div className=\"dus-actions dus-command-actions\">\n <button className=\"dus-action\" type=\"button\" disabled={disabled} onClick={() => { void copy() }}>{t('panel.copy')}</button>\n {copyMessage !== null && <p className=\"dus-copy-message\" role=\"status\">{copyMessage}</p>}\n </div>\n <p className=\"dus-note\">{t('panel.commandNote')}</p>\n </>\n )\n}\n\n/** Full detail from whichever page the operator is on (the transport is authenticated). */\nexport function UpdatePanel({ ui }: { ui: SharedUi }): ReactNS.ReactElement | null {\n const panel = useObservable(ui.panel)\n const preferences = useObservable(ui.preferences)\n const snapshot = useObservable(ui.status)\n const dialogRef = React.useRef<HTMLDialogElement | null>(null)\n const visible = panel.open && preferences.sidebarEnabled\n\n React.useLayoutEffect(() => {\n if (!visible) return undefined\n const dialog = dialogRef.current\n if (dialog === null) return undefined\n try {\n if (!dialog.open) dialog.showModal()\n } catch {\n // Older embedded Chromium still receives a visible non-top-layer dialog.\n dialog.setAttribute('open', '')\n }\n return () => {\n if (dialog.open) dialog.close()\n }\n }, [visible])\n\n React.useEffect(() => {\n if (!panel.open) return undefined\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') ui.panel.close()\n }\n window.addEventListener('keydown', onKeyDown)\n return () => { window.removeEventListener('keydown', onKeyDown) }\n }, [panel.open, ui.panel])\n\n if (!visible) return null\n const status = snapshot.status\n const warning = status === null ? null : localizedWarning(status)\n const summary = statusSummary(status, snapshot.loading, snapshot.error)\n const hasUpdate = status?.hasUpdate === true && status.latestVersion !== null\n const command = status === null || !hasUpdate ? '' : localizedUpgradeGuidance(status)\n\n return (\n <dialog\n ref={dialogRef}\n className=\"dus-overlay-root\"\n aria-label={t('panel.title')}\n onCancel={(event) => {\n event.preventDefault()\n ui.panel.close()\n }}\n >\n <div className=\"dus-backdrop\" onClick={() => { ui.panel.close() }} />\n <section className=\"dus-panel\" role=\"dialog\" aria-modal=\"true\" aria-label={t('panel.title')} onClick={(event) => { event.stopPropagation() }}>\n <div className=\"dus-panel-head\">\n <span className=\"dus-panel-title\">{t('panel.title')}</span>\n <button className=\"dus-close\" type=\"button\" aria-label={t('panel.close')} onClick={() => { ui.panel.close() }}>×</button>\n </div>\n\n <CacheTtlControl preferences={ui.preferences} />\n <p className=\"dus-state\" data-kind={summary.kind}>{summary.text}</p>\n\n <div className=\"dus-metadata\">\n <div className=\"dus-metadata-row\">\n <span className=\"dus-meta-label\">{t('panel.current')}</span>\n <code className=\"dus-meta-value\">{status?.currentVersion ?? '…'}</code>\n </div>\n {hasUpdate && <div className=\"dus-metadata-row\">\n <span className=\"dus-meta-label\">{t('panel.newer')}</span>\n <code className=\"dus-meta-value\">{status.latestVersion}</code>\n {status.publishedAt !== null && <span className=\"dus-meta-sub\">{t('panel.publishedAt')}: {timeText(status.publishedAt)}</span>}\n <span className=\"dus-meta-sub dus-compat\" data-compatibility={status.compatibility}>{compatibilityLabel(status.compatibility)}</span>\n </div>}\n {status?.checkedAt !== null && status?.checkedAt !== undefined && <div className=\"dus-metadata-row\">\n <span className=\"dus-meta-label\">{t('panel.checkedAt')}</span>\n <span className=\"dus-meta-value\">{timeText(status.checkedAt)}</span>\n <span className=\"dus-meta-sub\">{status.cached ? t('panel.cached') : t('panel.live')}</span>\n </div>}\n </div>\n\n {warning !== null && <p className=\"dus-warning\">{t('panel.error')}: {warning}</p>}\n {snapshot.error !== null && <p className=\"dus-warning\">{t('panel.error')}: {snapshot.error}</p>}\n\n <div className=\"dus-actions\">\n <button className=\"dus-action\" type=\"button\" disabled={snapshot.loading} onClick={() => { void ui.status.refresh(preferences.cacheTtlMinutes) }}>\n {snapshot.loading ? t('panel.checking') : t('panel.check')}\n </button>\n {status !== null && <a className=\"dus-action\" href={status.changelogUrl} target=\"_blank\" rel=\"noreferrer\">{t('panel.releaseNotes')}</a>}\n <RestartControl restart={ui.restart} />\n </div>\n\n {hasUpdate && <CommandBlock command={command} disabled={snapshot.loading} />}\n <p className=\"dus-note\">{t('panel.readOnly')}</p>\n </section>\n </dialog>\n )\n}\n\n/** Separate setting page: disables only this plugin's own rendering. */\nexport function UpdateSettings({ ui }: { ui: SharedUi }): ReactNS.ReactElement {\n const preferences = useObservable(ui.preferences)\n const snapshot = useObservable(ui.status)\n const status = snapshot.status\n const summary = statusSummary(status, snapshot.loading, snapshot.error)\n const hasUpdate = status?.hasUpdate === true && status.latestVersion !== null\n const command = status === null || !hasUpdate ? '' : localizedUpgradeGuidance(status)\n return (\n <section className=\"dus-settings\">\n <h2 className=\"dus-settings-heading\">{t('settings.title')}</h2>\n <div className=\"dus-settings-card\">\n <label className=\"dus-settings-toggle\">\n <input\n type=\"checkbox\"\n checked={preferences.sidebarEnabled}\n disabled={!preferences.writable}\n onChange={(event) => { ui.preferences.setSidebarEnabled(event.currentTarget.checked) }}\n />\n <span>{t('settings.sidebar')}</span>\n </label>\n <p className=\"dus-settings-hint\">{t('settings.sidebarHint')}</p>\n {!preferences.writable && <p className=\"dus-settings-hint\">{t('settings.readonly')}</p>}\n </div>\n <div className=\"dus-settings-card\">\n <p className=\"dus-settings-hint\">{t('panel.current')}: <code>{status?.currentVersion ?? '…'}</code></p>\n <p className=\"dus-state\" data-kind={summary.kind}>{summary.text}</p>\n {hasUpdate && <p className=\"dus-settings-hint\">{t('panel.newer')}: <code>{status.latestVersion}</code> · <span className=\"dus-compat\" data-compatibility={status.compatibility}>{compatibilityLabel(status.compatibility)}</span></p>}\n {snapshot.error !== null && <p role=\"alert\">{snapshot.error}</p>}\n {hasUpdate && <CommandBlock command={command} disabled={snapshot.loading} />}\n <div className=\"dus-actions\">\n <button className=\"dus-action\" type=\"button\" disabled={snapshot.loading} onClick={() => { void ui.status.refresh(preferences.cacheTtlMinutes) }}>\n {snapshot.loading ? t('panel.checking') : t('panel.check')}\n </button>\n {status !== null && <a className=\"dus-action\" href={status.changelogUrl} target=\"_blank\" rel=\"noreferrer\">{t('panel.releaseNotes')}</a>}\n <RestartControl restart={ui.restart} />\n </div>\n </div>\n </section>\n )\n}\n","/** Minimal structural client faces — runtime services stay owned by DSH. */\n\nimport type { UpdateStatus, UpdateWarning } from '../shared/types.ts'\n\nexport type RestartPhase = 'unknown' | 'idle' | 'armed' | 'checking' | 'waiting' | 'timeout' | 'error'\n\nexport interface RestartStatusValue {\n instanceId: string\n available: boolean\n supervisor: 'launchd' | 'systemd' | 'task-scheduler' | 'unknown' | null\n unavailableReason: 'not-supervised' | 'desktop' | 'supervisor-mismatch' | 'activity-unavailable' | 'timer-unavailable' | 'stale-instance' | null\n}\n\nexport interface RestartActivityItemValue {\n type: 'agent' | 'job' | 'terminal'\n id: string\n label: string\n status: string\n ownerSession?: string\n}\n\nexport interface RestartCheckValue {\n kind: 'ready' | 'active-work' | 'unavailable'\n status: RestartStatusValue\n activity?: { hasActive: boolean; items: RestartActivityItemValue[] }\n}\n\nexport interface RestartRequestValue {\n kind: 'ready' | 'active-work' | 'unavailable' | 'scheduled' | 'in-progress'\n status?: RestartStatusValue\n activity?: { hasActive: boolean; items: RestartActivityItemValue[] }\n instanceId?: string\n}\n\n\nexport interface Observable<T> {\n getSnapshot(): T\n subscribe(listener: () => void): () => void\n}\n\nexport interface ConnectionRpc {\n call(channel: string, endpoint: string, payload: unknown, signal?: AbortSignal): Promise<unknown>\n}\n\n/**\n * The authenticated transport face. There is deliberately NO `isLoopback` gate\n * here: DSH's settings Client treats a non-loopback page as process-local, but\n * that is a settings *persistence* policy — the Connection RPC itself stays\n * authenticated and reachable over a LAN bridge, so gating this plugin's own\n * read-only status on it only made the panel inert on exactly the page it is\n * used from. See settings/settingsChannel.ts for the one place a loopback\n * distinction still matters (choosing the Host settings channel).\n */\nexport interface ConnectionClient {\n rpc?: ConnectionRpc\n}\n\nexport interface SlotRegistration {\n name: string\n id?: string\n order?: number\n /** Single-slot arbitration: the lowest registered priority renders. */\n priority?: number\n label?: () => string\n locale?: string\n inject?: () => unknown\n}\n\nexport interface Slots {\n inject(name: string, callback: () => unknown): () => void\n register(registration: SlotRegistration, component: (props: Record<string, unknown>) => unknown): () => void\n}\n\nexport interface ClientContext {\n slots: Slots\n get(name: string): unknown\n inject(names: string[], callback: (ctx: unknown) => void): () => void\n effect(setup: () => (() => void) | void, label?: string): () => void\n /** Optional lifecycle event seam (Cordis contexts expose it; guarded here). */\n on?(name: string, listener: () => void): unknown\n}\n\nexport function errorMessage(error: unknown): string {\n const text = error instanceof Error ? error.message : String(error)\n return text.replace(/\\s+/g, ' ').trim().slice(0, 220) || 'unknown error'\n}\n\nfunction objectOf(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined\n}\n\nexport function restartStatusOf(value: unknown): RestartStatusValue | undefined {\n const record = objectOf(value)\n if (record === undefined || typeof record.instanceId !== 'string' || record.instanceId === '' || typeof record.available !== 'boolean') return undefined\n const supervisor = record.supervisor === null ? null\n : record.supervisor === 'launchd' || record.supervisor === 'systemd' || record.supervisor === 'task-scheduler' || record.supervisor === 'unknown'\n ? record.supervisor : undefined\n const unavailableReason = record.unavailableReason === null ? null\n : record.unavailableReason === 'not-supervised' || record.unavailableReason === 'desktop' || record.unavailableReason === 'supervisor-mismatch' || record.unavailableReason === 'activity-unavailable' || record.unavailableReason === 'timer-unavailable' || record.unavailableReason === 'stale-instance'\n ? record.unavailableReason : undefined\n if (supervisor === undefined || unavailableReason === undefined) return undefined\n if (record.available && (supervisor === null || unavailableReason !== null)) return undefined\n if (!record.available && unavailableReason === null) return undefined\n return { instanceId: record.instanceId, available: record.available, supervisor, unavailableReason }\n}\n\nfunction restartActivityOf(value: unknown): { hasActive: boolean; items: RestartActivityItemValue[] } | undefined {\n const record = objectOf(value)\n if (record === undefined || typeof record.hasActive !== 'boolean' || !Array.isArray(record.items)) return undefined\n const items: RestartActivityItemValue[] = []\n for (const raw of record.items) {\n const item = objectOf(raw)\n if (item === undefined || (item.type !== 'agent' && item.type !== 'job' && item.type !== 'terminal')\n || typeof item.id !== 'string' || typeof item.label !== 'string' || typeof item.status !== 'string') return undefined\n if (item.ownerSession !== undefined && typeof item.ownerSession !== 'string') return undefined\n items.push({ type: item.type, id: item.id, label: item.label, status: item.status, ...(item.ownerSession === undefined ? {} : { ownerSession: item.ownerSession }) })\n }\n if (record.hasActive !== (items.length > 0)) return undefined\n return { hasActive: record.hasActive, items }\n}\n\nexport function restartCheckOf(value: unknown): RestartCheckValue | undefined {\n const record = objectOf(value)\n if (record === undefined || (record.kind !== 'ready' && record.kind !== 'active-work' && record.kind !== 'unavailable')) return undefined\n const status = restartStatusOf(record.status)\n if (status === undefined) return undefined\n const activity = record.activity === undefined ? undefined : restartActivityOf(record.activity)\n if (record.activity !== undefined && activity === undefined) return undefined\n if (record.kind === 'active-work' && (activity === undefined || !activity.hasActive)) return undefined\n if (record.kind !== 'active-work' && activity !== undefined) return undefined\n return { kind: record.kind, status, ...(activity === undefined ? {} : { activity }) }\n}\n\nexport function restartRequestOf(value: unknown): RestartRequestValue | undefined {\n const record = objectOf(value)\n if (record === undefined || (record.kind !== 'ready' && record.kind !== 'active-work' && record.kind !== 'unavailable' && record.kind !== 'scheduled' && record.kind !== 'in-progress')) return undefined\n if (record.kind === 'scheduled' || record.kind === 'in-progress') return typeof record.instanceId === 'string' && record.instanceId !== '' ? { kind: record.kind, instanceId: record.instanceId } : undefined\n const check = restartCheckOf(record)\n return check === undefined ? undefined : check\n}\n\nfunction stringOrNull(value: unknown): string | null {\n return typeof value === 'string' ? value : null\n}\n\nfunction warningOf(value: unknown): UpdateWarning | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined\n const record = value as Record<string, unknown>\n if (record.code === 'registry-unavailable' && typeof record.detail === 'string') {\n return { code: record.code, detail: record.detail }\n }\n if (record.code === 'version-incomparable' && typeof record.currentVersion === 'string'\n && typeof record.latestVersion === 'string') {\n return {\n code: record.code,\n currentVersion: record.currentVersion,\n latestVersion: record.latestVersion,\n }\n }\n if (record.code === 'version-unverified' && typeof record.version === 'string') {\n return { code: record.code, version: record.version }\n }\n if (record.code === 'stale-schemastery') {\n const version = record.version === null ? null : stringOrNull(record.version)\n const nodeModulesDir = record.nodeModulesDir === null ? null : stringOrNull(record.nodeModulesDir)\n const path = stringOrNull(record.path)\n if (path !== null && (record.version === null || version !== null)\n && (record.nodeModulesDir === null || nodeModulesDir !== null)) {\n return { code: record.code, version, path, nodeModulesDir }\n }\n }\n return undefined\n}\n\n/** Reject malformed RPC output before it reaches a slot component. */\nexport function updateStatusOf(value: unknown): UpdateStatus | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined\n const record = value as Record<string, unknown>\n if (typeof record.currentVersion !== 'string' || typeof record.hasUpdate !== 'boolean'\n || typeof record.cached !== 'boolean' || typeof record.installKind !== 'string'\n || typeof record.upgradeCommand !== 'string' || typeof record.releaseUrl !== 'string'\n || typeof record.changelogUrl !== 'string' || typeof record.packageName !== 'string'\n || record.canApplyInPlace !== false) return undefined\n const validKind = record.installKind === 'npm-global' || record.installKind === 'pnpm-global'\n || record.installKind === 'source-checkout' || record.installKind === 'unknown'\n if (!validKind) return undefined\n if (record.compatibility !== 'verified' && record.compatibility !== 'unverified' && record.compatibility !== 'incompatible') return undefined\n const latestVersion = record.latestVersion === null ? null : stringOrNull(record.latestVersion)\n const checkedAt = record.checkedAt === null ? null : stringOrNull(record.checkedAt)\n const warning = record.warning === null ? null : stringOrNull(record.warning)\n // Absent or unrecognised means a Host older than the field: leave it undefined and\n // let the visual state fall back to \"any warning is a failure\", never to \"no warning\".\n const warningKind = record.warningKind === 'failure' || record.warningKind === 'notice' ? record.warningKind : undefined\n const publishedAt = record.publishedAt === null ? null : stringOrNull(record.publishedAt)\n if ((record.latestVersion !== null && latestVersion === null) || (record.checkedAt !== null && checkedAt === null)\n || (record.warning !== null && warning === null) || (record.publishedAt !== null && publishedAt === null)) return undefined\n const warnings: UpdateWarning[] = []\n if (record.warnings !== undefined) {\n if (!Array.isArray(record.warnings)) return undefined\n for (const raw of record.warnings) {\n const parsed = warningOf(raw)\n if (parsed === undefined) return undefined\n warnings.push(parsed)\n }\n }\n return {\n currentVersion: record.currentVersion,\n latestVersion,\n hasUpdate: record.hasUpdate,\n compatibility: record.compatibility,\n cached: record.cached,\n checkedAt,\n warning,\n warningKind,\n warnings,\n installKind: record.installKind as UpdateStatus['installKind'],\n upgradeCommand: record.upgradeCommand,\n releaseUrl: record.releaseUrl,\n changelogUrl: record.changelogUrl,\n publishedAt,\n packageName: record.packageName,\n canApplyInPlace: false,\n }\n}\n","/** Small observable stores shared by the independent sidebar and overlay slots. */\n\nimport { DEFAULT_CACHE_TTL_MINUTES, isCacheTtlMinutes, PLUGIN_ID, RESTART_ENDPOINTS, UPDATE_ENDPOINTS, UPDATE_STATUS_CHANNEL, type UpdateStatus } from '../shared/types.ts'\nimport type { ConnectionClient, Observable, RestartActivityItemValue, RestartPhase, RestartStatusValue } from './contract.ts'\nimport { errorMessage, restartCheckOf, restartRequestOf, restartStatusOf, updateStatusOf } from './contract.ts'\nimport type { SettingsScopeLike } from './settings/scopeFaces.ts'\n\nexport interface StatusSnapshot {\n status: UpdateStatus | null\n loading: boolean\n error: string | null\n}\n\nconst INITIAL_STATUS: StatusSnapshot = { status: null, loading: true, error: null }\n\nexport class StatusStore implements Observable<StatusSnapshot> {\n private snapshot: StatusSnapshot = INITIAL_STATUS\n private readonly listeners = new Set<() => void>()\n private inFlight: Promise<boolean> | undefined\n private stopped = false\n\n constructor(private readonly connection: ConnectionClient) {}\n\n getSnapshot = (): StatusSnapshot => this.snapshot\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n /** First mount reads the Host's cached status; it never starts a browser timer. */\n async load(cacheTtlMinutes: number = DEFAULT_CACHE_TTL_MINUTES): Promise<void> {\n await this.request(false, cacheTtlMinutes)\n }\n\n /** User gesture only: force the Host to bypass TTL (while retaining single-flight). */\n async refresh(cacheTtlMinutes: number = DEFAULT_CACHE_TTL_MINUTES): Promise<void> {\n await this.request(true, cacheTtlMinutes)\n }\n\n /**\n * Ordinary read that waits out an in-flight request first.\n *\n * Used once per mount, when the stored cache policy arrives after the first\n * read already started: joining that request would keep the default policy, so\n * the follow-up read has to come after it settles.\n */\n async reload(cacheTtlMinutes: number): Promise<void> {\n const pending = this.inFlight\n if (pending !== undefined) await pending\n if (this.stopped) return\n await this.request(false, cacheTtlMinutes)\n }\n\n stop(): void {\n this.stopped = true\n this.listeners.clear()\n }\n\n private publish(next: StatusSnapshot): void {\n if (this.stopped) return\n this.snapshot = next\n for (const listener of this.listeners) listener()\n }\n\n private request(force: boolean, cacheTtlMinutes: number): Promise<boolean> {\n if (this.inFlight !== undefined) return this.inFlight\n const rpc = this.connection.rpc\n if (rpc === undefined || typeof rpc.call !== 'function') {\n this.publish({ ...this.snapshot, loading: false, error: 'DSH connection RPC is unavailable' })\n return Promise.resolve(false)\n }\n\n this.publish({ ...this.snapshot, loading: true, error: null })\n const run = (async () => {\n try {\n const endpoint = force ? UPDATE_ENDPOINTS.checkUpdate : UPDATE_ENDPOINTS.getStatus\n const ttl = isCacheTtlMinutes(cacheTtlMinutes) ? cacheTtlMinutes : DEFAULT_CACHE_TTL_MINUTES\n const raw = await rpc.call(UPDATE_STATUS_CHANNEL, endpoint, force\n ? { force: true, cacheTtlMinutes: ttl }\n : { cacheTtlMinutes: ttl })\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('invalid update-status RPC response')\n const envelope = raw as { ok?: unknown; value?: unknown; error?: { message?: unknown } }\n if (envelope.ok !== true) throw new Error(typeof envelope.error?.message === 'string' ? envelope.error.message : 'update-status RPC failed')\n const status = updateStatusOf(envelope.value)\n if (status === undefined) throw new Error('invalid update-status payload')\n this.publish({ status, loading: false, error: null })\n return true\n } catch (error) {\n this.publish({ ...this.snapshot, loading: false, error: errorMessage(error) })\n return false\n }\n })()\n this.inFlight = run\n void run.finally(() => {\n if (this.inFlight === run) this.inFlight = undefined\n })\n return run\n }\n}\n\nexport interface PreferencesSnapshot {\n sidebarEnabled: boolean\n cacheTtlMinutes: number\n writable: boolean\n status: 'loading' | 'ready' | 'unavailable'\n}\n\nconst INITIAL_PREFERENCES: PreferencesSnapshot = {\n sidebarEnabled: true,\n cacheTtlMinutes: DEFAULT_CACHE_TTL_MINUTES,\n writable: false,\n status: 'loading',\n}\n\nexport class PreferencesStore implements Observable<PreferencesSnapshot> {\n private snapshot: PreferencesSnapshot = INITIAL_PREFERENCES\n private readonly listeners = new Set<() => void>()\n private scope: SettingsScopeLike | undefined\n\n getSnapshot = (): PreferencesSnapshot => this.snapshot\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n attach(scope: SettingsScopeLike): () => void {\n this.scope = scope\n const sync = () => {\n const raw = scope.getSnapshot()\n const value = raw.value !== null && typeof raw.value === 'object' && !Array.isArray(raw.value)\n ? raw.value as Record<string, unknown>\n : {}\n const status = raw.status === 'ready' || raw.status === 'unavailable' ? raw.status : 'loading'\n this.publish({\n sidebarEnabled: value.sidebarEnabled !== false,\n cacheTtlMinutes: isCacheTtlMinutes(value.cacheTtlMinutes) ? value.cacheTtlMinutes : DEFAULT_CACHE_TTL_MINUTES,\n writable: raw.writable === true,\n status,\n })\n }\n sync()\n return scope.subscribe(sync)\n }\n\n setSidebarEnabled(enabled: boolean): void {\n const previous = this.snapshot\n this.publish({ ...previous, sidebarEnabled: enabled })\n const scope = this.scope\n if (scope === undefined || !previous.writable) return\n void scope.set('sidebarEnabled', enabled).catch(() => {\n // A rejected Host-backed write restores the authoritative scope snapshot.\n try {\n const raw = scope.getSnapshot()\n const value = raw.value !== null && typeof raw.value === 'object' && !Array.isArray(raw.value)\n ? raw.value as Record<string, unknown>\n : {}\n this.publish({\n sidebarEnabled: value.sidebarEnabled !== false,\n cacheTtlMinutes: isCacheTtlMinutes(value.cacheTtlMinutes) ? value.cacheTtlMinutes : DEFAULT_CACHE_TTL_MINUTES,\n writable: raw.writable === true,\n status: raw.status === 'ready' || raw.status === 'unavailable' ? raw.status : 'loading',\n })\n } catch {\n this.publish(previous)\n }\n })\n }\n\n setCacheTtlMinutes(cacheTtlMinutes: number): void {\n if (!isCacheTtlMinutes(cacheTtlMinutes)) return\n const previous = this.snapshot\n this.publish({ ...previous, cacheTtlMinutes })\n const scope = this.scope\n if (scope === undefined || !previous.writable) return\n void scope.set('cacheTtlMinutes', cacheTtlMinutes).catch(() => { this.publish(previous) })\n }\n\n private publish(next: PreferencesSnapshot): void {\n const previous = this.snapshot\n if (previous.sidebarEnabled === next.sidebarEnabled\n && previous.cacheTtlMinutes === next.cacheTtlMinutes\n && previous.writable === next.writable && previous.status === next.status) return\n this.snapshot = next\n for (const listener of this.listeners) listener()\n }\n}\n\nexport interface PanelSnapshot {\n open: boolean\n}\n\n/** Open state of the detail panel, whose only trigger is the brand-row chip. */\nexport class PanelStore implements Observable<PanelSnapshot> {\n private snapshot: PanelSnapshot = { open: false }\n private readonly listeners = new Set<() => void>()\n\n getSnapshot = (): PanelSnapshot => this.snapshot\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n toggle(): void {\n this.set({ open: !this.snapshot.open })\n }\n\n close(): void { this.set({ open: false }) }\n\n private set(next: PanelSnapshot): void {\n if (this.snapshot.open === next.open) return\n this.snapshot = next\n for (const listener of this.listeners) listener()\n }\n}\n\nexport interface RestartSnapshot {\n phase: RestartPhase\n status: RestartStatusValue | null\n activity: readonly RestartActivityItemValue[]\n elapsedMs: number\n error: string | null\n}\n\nexport interface RestartStoreOptions {\n /** Injected by tests; defaults to browser timers. */\n readonly schedule?: (callback: () => void, delay: number) => unknown\n readonly cancel?: (handle: unknown) => void\n /** Per-RPC safety timeout used only while the Host is restarting. */\n readonly requestTimeout?: (callback: () => void, delay: number) => unknown\n readonly cancelRequestTimeout?: (handle: unknown) => void\n /** Same-origin liveness fallback when a new Host lacks this plugin route. */\n readonly liveness?: () => Promise<boolean>\n readonly reload?: () => void\n}\n\nconst INITIAL_RESTART: RestartSnapshot = {\n phase: 'unknown', status: null, activity: [], elapsedMs: 0, error: null,\n}\n\n/**\n * Restart state belongs to the client entry, not a modal component, so closing\n * the update panel cannot strand a confirmed restart half-way through recovery.\n * It starts no timer until the user confirms a restart request.\n */\nexport class RestartStore implements Observable<RestartSnapshot> {\n private snapshot: RestartSnapshot = INITIAL_RESTART\n private readonly listeners = new Set<() => void>()\n private timer: unknown\n private generation = 0\n private stopped = false\n private readonly schedule: (callback: () => void, delay: number) => unknown\n private readonly cancelTimer: (handle: unknown) => void\n private readonly requestTimeout: (callback: () => void, delay: number) => unknown\n private readonly cancelRequestTimeout: (handle: unknown) => void\n private readonly liveness: () => Promise<boolean>\n private readonly reload: () => void\n private pendingAbort: AbortController | undefined\n private reloaded = false\n\n constructor(private readonly connection: ConnectionClient, options: RestartStoreOptions = {}) {\n this.schedule = options.schedule ?? ((callback, delay) => window.setTimeout(callback, delay))\n this.cancelTimer = options.cancel ?? (handle => { window.clearTimeout(handle as number) })\n this.requestTimeout = options.requestTimeout ?? ((callback, delay) => window.setTimeout(callback, delay))\n this.cancelRequestTimeout = options.cancelRequestTimeout ?? (handle => { window.clearTimeout(handle as number) })\n this.liveness = options.liveness ?? (async () => {\n try {\n const response = await fetch(new URL('.', window.location.href), { method: 'HEAD', credentials: 'same-origin', cache: 'no-store' })\n return response.status === 200 || response.status === 401\n } catch { return false }\n })\n this.reload = options.reload ?? (() => window.location.reload())\n }\n\n getSnapshot = (): RestartSnapshot => this.snapshot\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n stop(): void {\n this.stopped = true\n this.generation += 1\n if (this.timer !== undefined) this.cancelTimer(this.timer)\n this.timer = undefined\n this.pendingAbort?.abort()\n this.pendingAbort = undefined\n this.listeners.clear()\n }\n\n /** Read capability on a visible surface; this is not a restart and never polls. */\n async refresh(): Promise<void> {\n if (this.snapshot.phase === 'waiting') return\n const raw = await this.call(RESTART_ENDPOINTS.status, {})\n if (raw === undefined) return\n const status = restartStatusOf(raw)\n if (status === undefined) {\n this.publish({ ...this.snapshot, phase: 'error', error: 'invalid restart-status payload' })\n return\n }\n this.publish({ phase: status.available ? 'idle' : 'idle', status, activity: [], elapsedMs: 0, error: null })\n }\n\n /** First click checks active work and arms the second, destructive action. */\n async check(): Promise<void> {\n this.publish({ ...this.snapshot, phase: 'checking', error: null })\n const raw = await this.call(RESTART_ENDPOINTS.check, {})\n if (raw === undefined) return\n const checked = restartCheckOf(raw)\n if (checked === undefined) {\n this.publish({ ...this.snapshot, phase: 'error', error: 'invalid restart-check payload' })\n return\n }\n if (checked.kind === 'unavailable') {\n this.publish({ phase: 'idle', status: checked.status, activity: [], elapsedMs: 0, error: null })\n return\n }\n this.publish({\n phase: 'armed', status: checked.status,\n activity: checked.activity?.items ?? [], elapsedMs: 0, error: null,\n })\n }\n\n cancel(): void {\n if (this.snapshot.phase !== 'armed' && this.snapshot.phase !== 'error' && this.snapshot.phase !== 'timeout') return\n this.publish({ ...this.snapshot, phase: this.snapshot.status === null ? 'unknown' : 'idle', activity: [], elapsedMs: 0, error: null })\n }\n\n /** Second click emits the Host request; `force` is only true after the activity list was shown. */\n async request(force: boolean): Promise<void> {\n const current = this.snapshot\n if (current.phase !== 'armed' || current.status === null || !current.status.available) return\n this.publish({ ...current, phase: 'checking', error: null })\n const raw = await this.call(RESTART_ENDPOINTS.request, { force, expectedInstanceId: current.status.instanceId })\n if (raw === undefined) return\n const result = restartRequestOf(raw)\n if (result === undefined) {\n this.publish({ ...this.snapshot, phase: 'error', error: 'invalid restart payload' })\n return\n }\n if (result.kind === 'scheduled' || result.kind === 'in-progress') {\n if (result.instanceId === undefined) {\n this.publish({ ...this.snapshot, phase: 'error', error: 'restart response omitted instance identity' })\n return\n }\n this.beginRecovery(result.instanceId)\n return\n }\n if (result.kind === 'active-work' || result.kind === 'ready') {\n this.publish({\n phase: 'armed', status: result.status ?? current.status,\n activity: result.activity?.items ?? [], elapsedMs: 0, error: null,\n })\n return\n }\n this.publish({ phase: 'idle', status: result.status ?? current.status, activity: [], elapsedMs: 0, error: null })\n }\n\n private beginRecovery(previousInstanceId: string): void {\n const generation = ++this.generation\n const startedAt = Date.now()\n this.publish({ ...this.snapshot, phase: 'waiting', elapsedMs: 0, error: null })\n const attempt = async (delay: number): Promise<void> => {\n if (this.stopped || generation !== this.generation) return\n const elapsedMs = Date.now() - startedAt\n if (elapsedMs >= 60_000) {\n this.publish({ ...this.snapshot, phase: 'timeout', elapsedMs, error: null })\n return\n }\n this.timer = this.schedule(() => {\n void (async () => {\n if (this.stopped || generation !== this.generation) return\n const raw = await this.call(RESTART_ENDPOINTS.status, {}, false, 4_000)\n if (this.stopped || generation !== this.generation) return\n const status = raw === undefined ? undefined : restartStatusOf(raw)\n const nextElapsed = Date.now() - startedAt\n if (status !== undefined && status.instanceId !== previousInstanceId) {\n this.publish({ phase: 'waiting', status, activity: [], elapsedMs: nextElapsed, error: null })\n if (!this.reloaded) { this.reloaded = true; this.reload() }\n return\n }\n // A healthy carrier with no plugin route means the new DSH came back\n // but this plugin did not load. Refresh so the page can show the real\n // startup failure instead of claiming DSH is still down forever.\n if (raw === undefined && await this.liveness()) {\n if (!this.reloaded) { this.reloaded = true; this.reload() }\n return\n }\n this.publish({ ...this.snapshot, phase: 'waiting', elapsedMs: nextElapsed })\n await attempt(Math.min(delay * 2, 10_000))\n })()\n }, delay)\n }\n void attempt(1_000)\n }\n\n private async call(endpoint: string, payload: unknown, surfaceError: boolean = true, timeoutMs?: number): Promise<unknown | undefined> {\n const rpc = this.connection.rpc\n if (rpc === undefined || typeof rpc.call !== 'function') {\n if (surfaceError) this.publish({ ...this.snapshot, phase: 'error', error: 'DSH connection RPC is unavailable' })\n return undefined\n }\n const controller = timeoutMs === undefined ? undefined : new AbortController()\n if (controller !== undefined) this.pendingAbort = controller\n let timeout: unknown\n if (controller !== undefined && timeoutMs !== undefined) timeout = this.requestTimeout(() => controller.abort(), timeoutMs)\n try {\n const raw = await rpc.call(UPDATE_STATUS_CHANNEL, endpoint, payload, controller?.signal)\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('invalid restart RPC response')\n const envelope = raw as { ok?: unknown; value?: unknown; error?: { message?: unknown } }\n if (envelope.ok !== true) throw new Error(typeof envelope.error?.message === 'string' ? envelope.error.message : 'restart RPC failed')\n return envelope.value\n } catch (error) {\n if (surfaceError) this.publish({ ...this.snapshot, phase: 'error', error: errorMessage(error) })\n return undefined\n } finally {\n if (timeout !== undefined) this.cancelRequestTimeout(timeout)\n if (this.pendingAbort === controller) this.pendingAbort = undefined\n }\n }\n\n private publish(next: RestartSnapshot): void {\n if (this.stopped) return\n this.snapshot = next\n for (const listener of this.listeners) listener()\n }\n}\n\n/**\n * The one settings namespace, which on DSH 0.1.7 IS the Loader entry id in\n * `cordis.patch.yml`: the Host half's volatile `Config` fields are projected as\n * that entry's form, and the browser half addresses the same id through\n * `ctx.configForms.get(...)`.\n */\nexport const SETTINGS_NAMESPACE = PLUGIN_ID\n","/**\n * dsh-update-status — direct Host settings channel (non-loopback pages).\n *\n * WHY THIS EXISTS\n *\n * DSH's settings Client keeps Host persistence disabled on any page whose\n * location is not a loopback authority. `ctx.configForms.get(entryId)` — the\n * DSH 0.1.7 successor of `settingsScope.bind()` — then answers\n * `status: 'unavailable'` immediately and NEVER sends `settings.describe`\n * (`@deepseek-ai/dsh-client-ui-settings`: the persistence mode resolves from\n * `remote.$host.isLoopback` and pins a remote page to `memory`; every row it\n * backs is inert even though Connection authentication covers the API).\n *\n * The decision itself is one line in the official client:\n *\n * const persistence = ctx.remote.$host.isLoopback ? \"host\" : \"memory\"\n *\n * and `isLoopback` is true only for `localhost` / `[::1]` / `127.0.0.0/8` pages\n * (or a transport that declares `ownsHost`). A LAN page reached through a\n * forwarding plugin is therefore non-loopback even though the API answers and\n * the page is authenticated.\n *\n * This plugin's preferences — the sidebar toggle, the followed release channel\n * and the cache policy — live in the Host settings entry `dsh-update-status`, so\n * without a channel they all silently fall back to defaults on a LAN page (the\n * host's configured `channel` would be ignored, silently switching the followed\n * release line). This module speaks the SAME public Remote the official form\n * speaks (`settings.describe` / `settings.mutate`, the generated Typert face\n * mounted by `@deepseek-ai/dsh-api-remotes`) and derives the same per-namespace\n * snapshot shape, so the ONE shared Host document keeps serving every device.\n *\n * This channel is opened ONLY when the official form reports `unavailable`\n * (see `settingsChannel.ts`), so loopback pages keep the official path and pay\n * no extra wire read. Everything here is guarded: an absent Remote, a refused\n * read, or a malformed answer leaves the preferences exactly as unavailable as\n * they were before this channel existed.\n */\nimport type { SettingsPathOpLike, SettingsScopeLike, SettingsScopeSnapshotLike } from './scopeFaces.ts'\n\n/** One namespace row of a `settings.describe` answer (the fields consumed here). */\nexport interface SettingsNamespaceViewLike {\n readonly ns: string\n readonly value: unknown\n readonly base?: unknown\n readonly user?: unknown\n readonly revision: number\n}\n\n/** The `settings.describe` answer body. */\nexport interface SettingsDescribeViewLike {\n readonly writable: boolean\n readonly hasDocument?: boolean\n readonly namespaces: readonly SettingsNamespaceViewLike[]\n}\n\n/** The Remote envelope every generated client call answers with. */\nexport type SettingsRemoteOutcome<T> =\n | { readonly ok: true; readonly value: T }\n | { readonly ok: false; readonly error?: { readonly code?: string; readonly message?: string } }\n\n/** The `remote.settings` namespace face (structural; never imported at runtime). */\nexport interface SettingsRemoteLike {\n describe(): Promise<SettingsRemoteOutcome<SettingsDescribeViewLike>>\n mutate(\n namespace: string,\n ops: readonly SettingsPathOpLike[],\n expectedRevision?: number,\n ): Promise<SettingsRemoteOutcome<SettingsNamespaceViewLike>>\n}\n\n/** A refused namespace write; `code` keeps the Host's own diagnosis. */\nexport class SettingsWriteFailure extends Error {\n readonly code: string\n\n constructor(code: string, message?: string) {\n super(message === undefined || message === '' ? code : message)\n this.name = 'SettingsWriteFailure'\n this.code = code\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\n/**\n * Guard: does this value look like the `remote.settings` namespace face?\n * Applied to a bare service object (`ctx.get('remote.settings')` or a payload\n * member), and it never calls anything.\n */\nexport function settingsRemoteFace(value: unknown): SettingsRemoteLike | undefined {\n if (!isRecord(value)) return undefined\n if (typeof value.describe !== 'function' || typeof value.mutate !== 'function') return undefined\n return value as unknown as SettingsRemoteLike\n}\n\n/**\n * Guard: read the `remote.settings` face off an injection payload.\n *\n * The payload a `ctx.inject(['remote.settings'], …)` callback receives exposes\n * the LITERAL service key. Reading the dotted PARENT off that injected context\n * (`payload.remote`) throws `cannot get property \"remote\" without inject` — a\n * throw that used to abort a whole wiring callback on a LAN page. So the literal\n * key is read FIRST and every read is contained; the nested\n * (`payload.remote.settings`) shape is only a fallback for a plain object\n * payload. Both absence and refusal answer `undefined`.\n */\nexport function settingsRemoteOf(raw: unknown): SettingsRemoteLike | undefined {\n if (!isRecord(raw)) return undefined\n try {\n const direct = settingsRemoteFace((raw as Record<string, unknown>)['remote.settings'])\n if (direct !== undefined) return direct\n } catch {\n // Injected-context proxy refusal: fall through to the nested shape.\n }\n try {\n const remote = (raw as { remote?: unknown }).remote\n if (isRecord(remote)) return settingsRemoteFace(remote.settings)\n } catch {\n // Same refusal: no face is readable from this payload.\n }\n return undefined\n}\n\n/**\n * Guard: subscribe to the Host's settings-document invalidation on the Remote\n * service object (`remote.$on`). Returns undefined when the service exposes no\n * event seam — the preferences then refresh on reconnect only.\n */\nexport function settingsInvalidationsOf(service: unknown): ((listener: () => void) => () => void) | undefined {\n if (!isRecord(service)) return undefined\n const on = service.$on\n if (typeof on !== 'function') return undefined\n return (listener) => {\n const dispose = (on as (event: string, callback: () => void) => unknown)\n .call(service, 'settings/document-updated', listener)\n return typeof dispose === 'function' ? dispose as () => void : () => {}\n }\n}\n\n/** The channel consumers subscribe to, plus the refresh/dispose seams the client wires. */\nexport interface HostDirectScope extends SettingsScopeLike {\n /** Re-read the Host document; resolves after the snapshot reflects the answer. */\n load(): Promise<void>\n /** Drop every listener and stop accepting work. */\n dispose(): void\n /** Last Host error message (diagnostics only; never rendered as plugin state). */\n lastError(): string | undefined\n}\n\nfunction cloneOps(ops: readonly SettingsPathOpLike[]): SettingsPathOpLike[] {\n try {\n return structuredClone(ops) as SettingsPathOpLike[]\n } catch {\n // structuredClone absent/refusing the shape: copy by hand (the op payload is\n // plain JSON by contract, so a shallow structural copy is equivalent).\n return ops.map(op => (op.op === 'set'\n ? { op: 'set' as const, path: [...op.path], value: op.value }\n : { op: 'unset' as const, path: [...op.path] }))\n }\n}\n\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\nfunction sameSnapshot(a: SettingsScopeSnapshotLike, b: SettingsScopeSnapshotLike): boolean {\n return a.status === b.status\n && a.revision === b.revision\n && a.writable === b.writable\n && a.mode === b.mode\n && a.value === b.value\n && a.base === b.base\n && a.user === b.user\n}\n\nfunction readySnapshot(\n row: SettingsNamespaceViewLike,\n writable: boolean,\n): SettingsScopeSnapshotLike {\n return {\n status: 'ready',\n // The raw section crosses this boundary untouched: PreferencesStore is the\n // plugin's own strict judge and reports a hand-edited or future-schema\n // document truthfully instead of silently dropping it.\n value: row.value,\n base: row.base,\n user: row.user,\n revision: row.revision,\n writable,\n mode: 'host',\n }\n}\n\nfunction unavailableSnapshot(writable: boolean): SettingsScopeSnapshotLike {\n return {\n status: 'unavailable',\n value: undefined,\n base: undefined,\n user: undefined,\n revision: undefined,\n writable,\n mode: 'host',\n }\n}\n\n/**\n * Build the direct Host settings scope for one namespace.\n *\n * Snapshot semantics mirror the official per-namespace derivation: a namespace\n * row answers `ready` with the resolved section, its `base`/`user` layers and its\n * revision fence, and the document's `writable` decides editability. A row the\n * Host does not serve answers `unavailable`; a refused read keeps the last\n * confirmed document and reports `unavailable` only while it has never held one.\n *\n * Writes are serialized in issue order, fenced by the latest known revision and\n * REJECT with {@link SettingsWriteFailure} on a Host refusal — that rejection is\n * what makes the settings page surface a conflict instead of pretending the edit\n * landed (the official scope resolves on `!ok` after its recovery read).\n *\n * @param remote - the `remote.settings` face of the page's Remote service.\n * @param namespace - the settings namespace this scope derives from.\n * @returns the scope plus its load/dispose seams.\n */\nexport function createHostDirectScope(remote: SettingsRemoteLike, namespace: string): HostDirectScope {\n let snapshot: SettingsScopeSnapshotLike = {\n status: 'loading',\n value: undefined,\n base: undefined,\n user: undefined,\n revision: undefined,\n writable: false,\n mode: 'host',\n }\n let held: SettingsScopeSnapshotLike | undefined\n let error: string | undefined\n let disposed = false\n let tail: Promise<void> = Promise.resolve()\n const listeners = new Set<() => void>()\n\n const publish = (next: SettingsScopeSnapshotLike): void => {\n if (disposed || sameSnapshot(next, snapshot)) return\n snapshot = next\n for (const listener of [...listeners]) {\n try {\n listener()\n } catch {\n // A broken listener must never break the channel.\n }\n }\n }\n\n const adopt = (next: SettingsScopeSnapshotLike): void => {\n if (next.status === 'ready') held = next\n publish(next)\n }\n\n async function load(): Promise<void> {\n if (disposed) return\n let outcome: SettingsRemoteOutcome<SettingsDescribeViewLike>\n try {\n outcome = await remote.describe()\n } catch (caught) {\n outcome = { ok: false, error: { message: messageOf(caught) } }\n }\n if (disposed) return\n if (!outcome.ok) {\n error = outcome.error?.message ?? outcome.error?.code ?? 'settings/describe-failed'\n // A refused read never invents state: the last confirmed document stays\n // authoritative; without one the channel is unavailable.\n publish(held ?? unavailableSnapshot(snapshot.writable))\n return\n }\n error = undefined\n const view = outcome.value\n const writable = view.writable === true\n const row = Array.isArray(view.namespaces)\n ? view.namespaces.find(candidate => candidate.ns === namespace)\n : undefined\n if (row === undefined || typeof row.revision !== 'number') {\n publish(unavailableSnapshot(writable))\n return\n }\n adopt(readySnapshot(row, writable))\n }\n\n /** Fold one write answer's namespace row in without a second wire read. */\n function fold(row: SettingsNamespaceViewLike, writable: boolean): void {\n if (typeof row.revision !== 'number') return\n adopt(readySnapshot(row, writable))\n }\n\n function mutate(ops: readonly SettingsPathOpLike[], expectedRevision?: number): Promise<void> {\n const owned = cloneOps(ops)\n const run = tail.then(async () => {\n if (disposed) throw new SettingsWriteFailure('settings/unavailable')\n const revision = expectedRevision ?? snapshot.revision\n let outcome: SettingsRemoteOutcome<SettingsNamespaceViewLike>\n try {\n outcome = revision === undefined\n ? await remote.mutate(namespace, owned)\n : await remote.mutate(namespace, owned, revision)\n } catch (caught) {\n await load()\n throw new SettingsWriteFailure('settings/unreachable', messageOf(caught))\n }\n if (outcome.ok) {\n // A successful write proves the document accepts writes on this page.\n fold(outcome.value, true)\n return\n }\n await load()\n throw new SettingsWriteFailure(\n outcome.error?.code ?? 'settings/unknown',\n outcome.error?.message,\n )\n })\n // The queue never poisons itself: the caller sees the rejection, the next\n // queued write still runs.\n tail = run.then(() => undefined, () => undefined)\n return run\n }\n\n return {\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n // The official scope implements `set` exactly this way; the revision fence\n // falls back to the latest known one, same as the official pendingRevision.\n set: (field, value) => mutate([{ op: 'set', path: [field], value }]),\n load,\n dispose() {\n disposed = true\n listeners.clear()\n },\n lastError: () => error,\n }\n}\n","/**\n * dsh-update-status — official settings-form adapter.\n *\n * DSH 0.1.7 removed the per-namespace `settingsScope` service this plugin's\n * client half was built on. Its successor is `ctx.configForms`\n * (`@deepseek-ai/dsh-client-ui-settings`): one shared `settings.describe`\n * mirror per browser plus a per-entry write queue, reached as\n * `ctx.configForms.get(entryId)` for the entry the plugin's Host half owns.\n * That form already answers the exact facts this plugin's own scope contract\n * needs — status, value, base, user, revision, writable, mode — so this module\n * only projects it onto {@link SettingsScopeLike} and leaves the existing\n * channel selection (`settingsChannel.ts`) in charge of the LAN fallback.\n *\n * Two deliberate translations:\n *\n * - A refused write (`set` answers `false`) becomes a REJECTION. The settings\n * page must surface a conflict instead of pretending the edit landed, which\n * is the same contract the direct Host channel already obeys\n * (`hostDirectScope.ts`). A transport fault rejects too.\n * - `mode` is what separates the two pages: DSH pins it to `memory` on a\n * non-loopback page, where the form is terminally `unavailable` and never\n * writes — exactly the state that hands the preferences to the direct Host\n * Remote so a phone still edits the one shared entry.\n *\n * The service is deliberately read structurally: the client bundle purity gate\n * forbids cross-plugin value imports, and the runtime object belongs to the\n * user's harness.\n */\nimport type { SettingsScopeLike, SettingsScopeSnapshotLike } from './scopeFaces.ts'\nimport { SettingsWriteFailure } from './hostDirectScope.ts'\n\n/** One form snapshot as `ConfigForm.getSnapshot()` answers it. */\nexport interface ConfigFormSnapshotLike<T> {\n status: 'loading' | 'ready' | 'unavailable'\n value: T | undefined\n base: unknown\n user: unknown\n revision: number | undefined\n writable: boolean\n mode: 'host' | 'memory'\n}\n\n/** The subset of `ConfigForm<T>` this adapter reads. */\nexport interface ConfigFormLike<T = unknown> {\n getSnapshot(): ConfigFormSnapshotLike<T>\n subscribe(listener: () => void): () => void\n set(field: string, value: unknown): Promise<boolean>\n}\n\n/** The `ctx.configForms` binder face. */\nexport interface ConfigFormsFace {\n get<T>(entryId: string): ConfigFormLike<T>\n}\n\n/**\n * Guard: read the `configForms` face off an injection payload.\n *\n * The literal service key is read inside a `try`, because an injected payload\n * proxy can refuse a member read (the failure this plugin already hit on the\n * dotted `remote.settings` parent). Absence and refusal both answer\n * `undefined`, which leaves the channel on its direct Host path.\n *\n * @param raw - the payload a `ctx.inject(['configForms'], …)` callback received.\n * @returns the binder face, or undefined when this page has no settings forms.\n */\nexport function configFormsOf(raw: unknown): ConfigFormsFace | undefined {\n if (raw === null || typeof raw !== 'object') return undefined\n let face: unknown\n try {\n face = (raw as Record<string, unknown>)['configForms']\n } catch {\n return undefined\n }\n if (face === null || typeof face !== 'object') return undefined\n if (typeof (face as { get?: unknown }).get !== 'function') return undefined\n return face as ConfigFormsFace\n}\n\n/**\n * Project one official config form onto the plugin's settings-scope contract.\n *\n * @param form - the shared form for the plugin's own Loader entry id.\n * @returns a scope the preferences store and the channel selection can use as-is.\n */\nexport function configFormScope(form: ConfigFormLike): SettingsScopeLike {\n return {\n getSnapshot(): SettingsScopeSnapshotLike {\n const snapshot = form.getSnapshot()\n return {\n status: snapshot.status,\n value: snapshot.value as SettingsScopeSnapshotLike['value'],\n base: snapshot.base,\n user: snapshot.user,\n revision: snapshot.revision,\n writable: snapshot.writable,\n mode: snapshot.mode,\n }\n },\n subscribe(listener) {\n return form.subscribe(listener)\n },\n async set(field, value) {\n let accepted: boolean\n try {\n accepted = await form.set(field, value)\n } catch (error) {\n // Transport fault: the write did not land and the caller must not\n // pretend it did (the recovery read belongs to the form itself).\n throw new SettingsWriteFailure(\n 'settings/unreachable',\n error instanceof Error ? error.message : String(error),\n )\n }\n if (!accepted) {\n // Refused or skipped (memory mode, stale revision, Host validation):\n // the form has already re-read the authoritative value.\n throw new SettingsWriteFailure('settings/conflict')\n }\n },\n }\n}\n","/**\n * dsh-update-status — settings source selection.\n *\n * The plugin's ONE storage contract is the Host settings entry\n * `dsh-update-status`. Two client channels can serve it:\n *\n * 1. the official `ctx.configForms` form (loopback pages) — the DSH 0.1.7\n * successor of the removed `settingsScope` service, and\n * 2. the direct Host channel (`hostDirectScope.ts`), which exists exactly\n * because the official form is deliberately inert on a non-loopback page —\n * the LAN page a phone uses.\n *\n * This module picks between them without ever guessing:\n *\n * - The official form wins whenever it is not `unavailable`, so a loopback page\n * keeps the official semantics (the one shared describe mirror, the official\n * write queue and revision fences) and pays NO extra wire read.\n * - Only when the official form reports `unavailable` (the documented\n * non-loopback degradation: it is pinned to `memory` mode there and never\n * crosses the wire) is the direct channel opened, lazily and once.\n * - With neither source the channel stays `loading`, i.e. exactly the state this\n * plugin had before the direct channel existed.\n */\nimport type { SettingsScopeLike, SettingsScopeSnapshotLike } from './scopeFaces.ts'\n\n/** Snapshot used while no channel has answered yet. */\nconst LOADING_SNAPSHOT: SettingsScopeSnapshotLike = Object.freeze({\n status: 'loading',\n value: undefined,\n base: undefined,\n user: undefined,\n revision: undefined,\n writable: false,\n mode: 'host',\n}) as SettingsScopeSnapshotLike\n\nexport interface SettingsChannelOptions {\n /**\n * Build the direct Host channel. Called at most once, and only when the\n * official scope cannot serve this page. Returning `undefined` (the Remote\n * service is not composed) leaves the channel on the official/unavailable\n * path; a later {@link SettingsChannel.refresh} retries.\n */\n openDirect(): SettingsScopeLike | undefined\n}\n\nexport interface SettingsChannel extends SettingsScopeLike {\n /** Point the channel at the official form once the service answers. */\n setOfficial(scope: SettingsScopeLike | undefined): void\n /** Re-evaluate the selection (e.g. the Remote service just arrived). */\n refresh(): void\n /** Ask the active source for a fresh read (no-op for the official form). */\n reload(): void\n dispose(): void\n}\n\nfunction sameSnapshot(a: SettingsScopeSnapshotLike, b: SettingsScopeSnapshotLike): boolean {\n return a.status === b.status\n && a.revision === b.revision\n && a.writable === b.writable\n && a.mode === b.mode\n && a.value === b.value\n && a.base === b.base\n && a.user === b.user\n}\n\n/** The load seam a direct channel exposes (the official scope refreshes itself). */\ntype LoadableScope = SettingsScopeLike & { load?: () => Promise<void> }\n\n/** Ask a channel for its first/next read; a refusal surfaces as a snapshot, never a throw. */\nfunction loadSource(source: SettingsScopeLike | undefined): Promise<void> {\n const loadable = source as LoadableScope | undefined\n if (loadable === undefined || typeof loadable.load !== 'function') return Promise.resolve()\n return Promise.resolve(loadable.load()).then(() => undefined, () => undefined)\n}\n\nexport function createSettingsChannel(options: SettingsChannelOptions): SettingsChannel {\n let official: SettingsScopeLike | undefined\n let direct: SettingsScopeLike | undefined\n let active: SettingsScopeLike | undefined\n let snapshot: SettingsScopeSnapshotLike = LOADING_SNAPSHOT\n let disposed = false\n const disposers: Array<() => void> = []\n const listeners = new Set<() => void>()\n\n const notify = (): void => {\n for (const listener of [...listeners]) {\n try {\n listener()\n } catch {\n // A broken listener must never break the channel.\n }\n }\n }\n\n const publish = (next: SettingsScopeSnapshotLike): void => {\n if (disposed || sameSnapshot(next, snapshot)) return\n snapshot = next\n notify()\n }\n\n const select = (): SettingsScopeLike | undefined => {\n // The official scope is authoritative unless it reports the documented\n // non-loopback degradation.\n if (official !== undefined && official.getSnapshot().status !== 'unavailable') return official\n if (direct === undefined) {\n try {\n direct = options.openDirect()\n } catch {\n direct = undefined\n }\n if (direct !== undefined) {\n try {\n disposers.push(direct.subscribe(() => {\n if (active === direct) publish(direct!.getSnapshot())\n }))\n } catch {\n // An unsubscribable direct channel is still readable synchronously.\n }\n // The direct channel has no owner to `ensure()` it (the official scope is\n // ensured by its own plugin), so the initial read is issued here.\n void loadSource(direct)\n }\n }\n return direct ?? official\n }\n\n const reselect = (): void => {\n if (disposed) return\n const next = select()\n if (next !== active) {\n active = next\n publish(next?.getSnapshot() ?? LOADING_SNAPSHOT)\n return\n }\n // Same source: forward its current snapshot (a change must have come from it).\n publish(next?.getSnapshot() ?? LOADING_SNAPSHOT)\n }\n\n return {\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n set(field, value) {\n const source = active\n if (source === undefined) {\n return Promise.reject(new Error('dsh-update-status: settings channel is unavailable'))\n }\n return source.set(field, value)\n },\n setOfficial(scope) {\n if (disposed) return\n official = scope\n if (scope !== undefined) {\n try {\n disposers.push(scope.subscribe(() => {\n reselect()\n }))\n } catch {\n // Guarded read path: selection still happens synchronously below.\n }\n }\n reselect()\n },\n refresh: reselect,\n reload() {\n if (active === undefined || active === official) return\n void loadSource(active)\n },\n dispose() {\n disposed = true\n listeners.clear()\n for (const dispose of disposers.splice(0)) {\n try {\n dispose()\n } catch {\n // Contained.\n }\n }\n const disposable = active as (SettingsScopeLike & { dispose?: () => void }) | undefined\n if (disposable !== undefined && typeof disposable.dispose === 'function') {\n try {\n disposable.dispose()\n } catch {\n // Contained.\n }\n }\n },\n }\n}\n","/** Plugin-owned CSS only; no shell DOM selection or official SVG manipulation. */\n\nexport const UPDATE_STATUS_CSS = `\n.dus-brand-name{align-items:center;gap:6px;height:24px;max-width:100%;min-width:0;display:inline-flex}\n.dus-brand-deepseek{font-size:14px;font-weight:650;letter-spacing:-.015em;line-height:24px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n/* The chip is a NEUTRAL SECOND-LEVEL SURFACE, never a high-contrast pill. It used\n to be label-primary + label-primary-inverted, which is a near-white box with dark\n text on the dark shell — a white frame that swallows the amber halo and fights the\n warn colour. --dsw-alias-button-floating-hover is the one palette surface that is\n grey in BOTH themes (#f1f3f5 light / #353638 dark), matching the reference: a light\n grey chip on the light shell, a dark grey chip with white text on the dark one. The\n sibling button-floating-fill is pure white in light mode and changes nothing there.\n Text stays label-primary (#0f1115 light / #f9fafb dark), so it is never low-contrast.\n Every colour here is a theme token, so the chip follows whatever appearance DSH\n resolves — light, dark, or system-follows-the-OS — with no plugin-side detection,\n no media query and no per-theme branch to keep in sync. */\n.dus-badge{--dus-badge-surface:var(--dsw-alias-button-floating-hover,#f1f3f5);align-items:center;background:var(--dus-badge-surface);border:0;border-radius:4px;color:var(--dsw-alias-label-primary,#0f1115);cursor:pointer;display:inline-flex;flex:none;font-family:var(--ds-font-family-code,var(--dsw-font-family-mono,ui-monospace,SFMono-Regular,Menlo,monospace));font-size:10px;font-variant-numeric:tabular-nums;font-weight:600;gap:4px;height:16px;line-height:16px;max-width:140px;outline:none;padding:0 6px;touch-action:manipulation;user-select:none;white-space:nowrap}\n.dus-badge:focus-visible,.dus-action:focus-visible,.dus-close:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}\n/* Hover has to read as \"raised\" in BOTH themes. brightness() only ever brightens, and on\n the light shell it turns the grey chip pure white — the chip all but disappears. Mixing\n a little of the theme's own label colour into the surface darkens it in light mode and\n lightens it in dark mode, so one declaration stays correct in both. The brightness\n filter remains the fallback: an engine without color-mix drops only the second\n declaration, never the whole hover rule. */\n.dus-badge:hover{filter:brightness(1.08)}\n@supports (background:color-mix(in srgb,red 50%,transparent)){.dus-badge:hover{background:color-mix(in srgb,var(--dsw-alias-label-primary) 8%,var(--dus-badge-surface));filter:none}}\n/* The failure state is the one deliberate repaint, and it follows the shell's own\n badge pattern (brokenBadge): error fill with a bg-layer-3 label, which is white\n text on the dark red of the light theme and dark text on the light red of the dark\n theme — readable in both, unlike the fixed white text it replaces. */\n.dus-badge[data-error=true]{background:var(--dsw-alias-state-error-primary,#dc2626);color:var(--dsw-alias-bg-layer-3,#fff)}\n.dus-badge-version{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dus-dot{background:currentColor;border-radius:50%;display:inline-block;flex:0 0 auto;height:5px;opacity:.9;width:5px}\n/* An available update repaints NOTHING: the chip keeps its normal fill and only\n the amber dot breathes. The halo is animated with the dot so the glow grows\n and shrinks as one light, never as a separate ripple. The update state is\n declared AFTER the loading state, so a re-check on a known update keeps the\n amber glow instead of falling back to the neutral loading pulse.\n Each box-shadow is declared twice: a literal amber first for any engine\n without color-mix, then the halo mixed from the theme's own warn colour so the\n glow always matches the dot in both light and dark themes. */\n/* The dot carries the whole status. Three states, in precedence order:\n up to date = a plain green circle (success token, never the text colour, so a\n dark theme no longer paints a black dot that reads as \"off\"); checking = the\n neutral grey pulse; a pending update = the amber dot below, which is the only\n state allowed to breathe or glow.\n The up-to-date rule is declared BEFORE loading and update: equal specificity, so\n the later, louder states win if two ever land on the same dot. */\n.dus-dot[data-state=current]{background:var(--dsw-alias-state-success-primary,#22c55e);opacity:1}\n.dus-dot[data-loading=true]{animation:dus-pulse .9s ease-in-out infinite}\n.dus-dot[data-update=true]{--dus-update-color:var(--dsw-alias-state-warn-primary,#f59e0b);animation:dus-update-pulse 1.6s ease-in-out infinite;background:var(--dus-update-color);height:6px;opacity:1;width:6px}\n@keyframes dus-pulse{0%,100%{opacity:.45;transform:scale(.82)}50%{opacity:1;transform:scale(1.15)}}\n@keyframes dus-update-pulse{0%,100%{box-shadow:0 0 2px 0 rgba(245,158,11,.4);box-shadow:0 0 2px 0 color-mix(in srgb,var(--dus-update-color) 45%,transparent);transform:scale(.8)}50%{box-shadow:0 0 7px 2px rgba(245,158,11,.72);box-shadow:0 0 7px 2px color-mix(in srgb,var(--dus-update-color) 72%,transparent);transform:scale(1.15)}}\n@media (prefers-reduced-motion:reduce){.dus-dot[data-update=true],.dus-dot[data-loading=true]{animation:none}.dus-dot[data-update=true]{box-shadow:0 0 5px 1px rgba(245,158,11,.6);box-shadow:0 0 5px 1px color-mix(in srgb,var(--dus-update-color) 62%,transparent)}}\n.dus-overlay-root{background:transparent;border:0;color:inherit;height:100dvh;inset:0;margin:0;max-height:none;max-width:none;padding:0;pointer-events:none;position:fixed;width:100vw}\n.dus-overlay-root::backdrop{background:transparent}\n.dus-backdrop{background:transparent;inset:0;pointer-events:auto;position:absolute}\n.dus-panel{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:14px;box-shadow:var(--dsw-elevation-panel);box-sizing:border-box;color:var(--dsw-alias-label-primary);left:min(292px,calc(100vw - 402px));max-height:min(640px,calc(100dvh - 32px - env(safe-area-inset-top) - env(safe-area-inset-bottom)));overflow:auto;padding:14px;pointer-events:auto;position:absolute;top:12px;width:min(390px,calc(100vw - 24px));z-index:1}\n.dus-panel-head{align-items:center;display:flex;gap:8px;justify-content:space-between;margin-bottom:12px}\n.dus-panel-title{font-size:14px;font-weight:650}\n.dus-close{align-items:center;background:transparent;border:0;border-radius:8px;color:inherit;cursor:pointer;display:inline-flex;font-size:20px;height:32px;justify-content:center;line-height:1;min-height:32px;min-width:32px;padding:0;touch-action:manipulation;width:32px}\n.dus-close:hover,.dus-action:hover{background:var(--dsw-alias-button-floating-hover)}\n.dus-cache-setting{border:1px solid var(--dsw-alias-border-l2);border-radius:9px;margin:0 0 12px;padding:9px}\n.dus-cache-field{align-items:center;display:flex;font-size:12px;font-weight:600;gap:10px;justify-content:space-between}\n.dus-cache-input-wrap{align-items:center;display:flex;gap:6px;font-size:11px;font-weight:400}\n.dus-cache-input{background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:7px;color:inherit;font:inherit;min-height:30px;padding:0 7px;text-align:right;width:72px}\n.dus-cache-input:focus{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}\n.dus-cache-input:disabled{cursor:not-allowed;opacity:.55}\n.dus-cache-hint{color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1.4;margin:7px 0 0}\n.dus-state{border-radius:9px;color:var(--dsw-alias-label-primary);font-size:12px;line-height:1.45;margin:0 0 12px;padding:8px 9px}\n.dus-state[data-kind=update],.dus-state[data-kind=warning]{background:var(--dsw-alias-state-warn-tertiary)}\n.dus-state[data-kind=ok]{background:var(--dsw-alias-state-success-tertiary)}\n.dus-state[data-kind=error]{background:var(--dsw-alias-bg-layer-3)}\n.dus-metadata{display:grid;gap:9px;margin:0 0 12px}\n.dus-metadata-row{align-items:baseline;display:grid;gap:8px;grid-template-columns:minmax(0,1fr) auto}\n.dus-meta-label{color:var(--dsw-alias-label-secondary);font-size:12px}\n.dus-meta-value{font-family:var(--dsw-font-family-mono,ui-monospace,SFMono-Regular,Menlo,monospace);font-size:12px;max-width:210px;overflow-wrap:anywhere;text-align:right}\n.dus-meta-sub{color:var(--dsw-alias-label-secondary);font-size:11px;grid-column:1 / -1}\n.dus-warning{border-left:2px solid var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.45;margin:0 0 12px;padding-left:8px}\n.dus-actions{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 12px}\n.dus-action{background:var(--dsw-alias-button-floating-fill);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;color:inherit;cursor:pointer;font-size:12px;line-height:30px;min-height:32px;padding:0 10px;touch-action:manipulation}\n.dus-action:disabled{cursor:wait;opacity:.65}\n.dus-restart{border-color:var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-primary)}\n.dus-restart-message{color:var(--dsw-alias-label-secondary);flex-basis:100%;font-size:11px;line-height:1.45;margin:0}\n.dus-restart-work{color:var(--dsw-alias-label-secondary);flex-basis:100%;font-size:11px;line-height:1.5;margin:0;overflow-wrap:anywhere;padding-left:18px}\n.dus-command-label{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600;margin:0 0 6px}\n.dus-command{background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;display:block;font-family:var(--dsw-font-family-mono,ui-monospace,SFMono-Regular,Menlo,monospace);font-size:11px;line-height:1.5;margin:0;overflow-wrap:anywhere;padding:9px;tab-size:2;user-select:text;white-space:pre-wrap}\n.dus-command:focus{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}\n.dus-note{color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1.45;margin:8px 0 0}\n.dus-copy-message{color:var(--dsw-alias-state-business-primary);font-size:11px;margin:7px 0 0}\n.dus-settings{display:grid;gap:14px;max-width:640px;padding:4px 0}\n.dus-settings-heading{font-size:16px;font-weight:650;margin:0}\n.dus-settings-card{border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:12px}\n.dus-settings-toggle{align-items:flex-start;cursor:pointer;display:flex;gap:10px;font-size:13px;line-height:1.4}\n.dus-settings-toggle input{accent-color:var(--dsw-alias-state-business-primary);height:18px;margin:0;min-height:18px;min-width:18px;width:18px}\n.dus-settings-hint{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.45;margin:8px 0 0}\n.dus-compat{color:var(--dsw-alias-label-secondary)}\n.dus-compat[data-compatibility=verified]{color:var(--dsw-alias-state-success-primary)}\n.dus-compat[data-compatibility=incompatible]{color:var(--dsw-alias-state-error-primary)}\n.dus-command-actions{align-items:center;margin:8px 0 0}\n@media (max-width:640px),(hover:none) and (pointer:coarse){.dus-panel{border-bottom:0;border-bottom-left-radius:0;border-bottom-right-radius:0;bottom:0;left:0;right:0;max-height:min(78dvh,calc(100dvh - env(safe-area-inset-top)));padding:16px max(16px,env(safe-area-inset-right)) max(16px,env(safe-area-inset-bottom)) max(16px,env(safe-area-inset-left));position:fixed;top:auto;width:100vw}.dus-action{min-height:40px;line-height:38px}.dus-close{height:40px;min-height:40px;min-width:40px;width:40px}}\n`\n","/**\n * DSH Web client half. It shadows only `sidebar.brand.name` with a compact\n * name + version chip that fits the 24px brand row and leaves the official fish\n * (`sidebar.brand.mark`) untouched. The detail panel is an additive\n * `shell.overlay`, never a chat-area floating widget.\n *\n * Deliberately NO `sidebar.footer.action` entry. DSH renders that list in one\n * row directly above `sidebar.settings`, and the collapsed rail's content box is\n * only 36px wide (the 56px column minus its 10px inline padding) — room for the\n * single official action. A second registrant makes that shared, centred row\n * overflow: on a phone it pushed the neighbouring plugin's entry off the screen\n * edge and parked our own dot on the rail's border. The chip in the expanded\n * brand row is the entry, so a collapsed sidebar carries none.\n *\n * The status read and the detail panel are NOT restricted to a loopback page.\n * DSH disables Host settings *persistence* on a non-loopback page, but the\n * Connection RPC stays authenticated and reachable there — see\n * settings/settingsChannel.ts for the one place the loopback distinction still\n * matters.\n *\n * Preferences live in the Host-side `dsh-update-status` settings entry — the\n * plugin's own Loader entry, whose volatile `Config` fields DSH 0.1.7 projects\n * as that entry's form. Two client channels can serve it: the official\n * `ctx.configForms.get(entryId)` form (the successor of the removed\n * `settingsScope` service) and, on the non-loopback pages where that form is\n * deliberately inert, a direct Host channel over `remote.settings`. See\n * settingsChannel.ts.\n */\n\nimport type { ClientContext, ConnectionClient } from './contract.ts'\nimport { BrandName, type SharedUi, UpdatePanel, UpdateSettings } from './components.tsx'\nimport { SETTINGS_NAMESPACE, PanelStore, PreferencesStore, RestartStore, StatusStore } from './stores.ts'\nimport { configFormScope, configFormsOf } from './settings/configFormScope.ts'\nimport {\n createHostDirectScope,\n settingsInvalidationsOf,\n settingsRemoteFace,\n settingsRemoteOf,\n type SettingsRemoteLike,\n} from './settings/hostDirectScope.ts'\nimport { createSettingsChannel } from './settings/settingsChannel.ts'\nimport { t } from './i18n.ts'\nimport { UPDATE_STATUS_CSS } from './styles.ts'\n\nconst PLUGIN_ID = 'dsh-update-status'\nconst CSS_TAG_ID = `${PLUGIN_ID}/styles`\n\nfunction installStyles(): () => void {\n if (typeof document === 'undefined') return () => {}\n const existing = document.querySelector(`style[data-plugin-css=\"${CSS_TAG_ID}\"]`)\n if (existing !== null) return () => {}\n const tag = document.createElement('style')\n tag.dataset.plugin = PLUGIN_ID\n tag.dataset.pluginCss = CSS_TAG_ID\n tag.textContent = UPDATE_STATUS_CSS\n document.head.appendChild(tag)\n return () => {\n const live = document.querySelector(`style[data-plugin-css=\"${CSS_TAG_ID}\"]`)\n if (live?.parentNode !== undefined && live?.parentNode !== null) live.parentNode.removeChild(live)\n }\n}\n\nfunction connectionOf(ctx: ClientContext): ConnectionClient {\n try {\n const candidate = ctx.get('connection')\n return candidate !== null && typeof candidate === 'object' ? candidate as ConnectionClient : {}\n } catch {\n return {}\n }\n}\n\nfunction apply(ctx: ClientContext): void {\n const connection = connectionOf(ctx)\n const status = new StatusStore(connection)\n const preferences = new PreferencesStore()\n const panel = new PanelStore()\n const restart = new RestartStore(connection)\n const ui: SharedUi = { status, preferences, panel, restart }\n\n const disposers: Array<() => void> = []\n let stopped = false\n /** Own a subscription raised from an injection callback; release after teardown at once. */\n const own = (dispose: unknown): void => {\n if (typeof dispose !== 'function') return\n if (stopped) {\n try {\n (dispose as () => void)()\n } catch {\n // Contained.\n }\n return\n }\n disposers.push(dispose as () => void)\n }\n\n // The plugin's ONE storage contract is the Host settings entry\n // `dsh-update-status` (the plugin's own Loader entry id), but two client\n // channels can serve it: the official `configForms` form (loopback pages) and\n // — only when that form reports the documented non-loopback degradation — a\n // direct Host channel over the same public Remote. A LAN page is exactly that\n // non-loopback case; without the direct channel every preference silently\n // falls back to its default there. See settingsChannel.ts.\n let remoteSettings: SettingsRemoteLike | undefined\n /** Resolve the direct channel's Remote face; `ctx.get` covers a payload we could not read. */\n const directRemote = (): SettingsRemoteLike | undefined => {\n if (remoteSettings !== undefined) return remoteSettings\n try {\n remoteSettings = settingsRemoteFace(ctx.get('remote.settings'))\n } catch {\n // The dotted service key is not readable on this context.\n }\n return remoteSettings\n }\n const channel = createSettingsChannel({\n openDirect: () => {\n const remote = directRemote()\n return remote === undefined ? undefined : createHostDirectScope(remote, SETTINGS_NAMESPACE)\n },\n })\n\n ctx.effect(() => {\n const disposeStyles = installStyles()\n const detach = preferences.attach(channel)\n // The mount read uses the cache policy the profile stored. The settings\n // document usually arrives one tick after the effect, so the first\n // authoritative value issues that read; later edits deliberately do not —\n // changing the duration never starts a request of its own. A profile that\n // stores the default policy costs no second read.\n let usedCacheTtl = preferences.getSnapshot().cacheTtlMinutes\n let storedPolicyApplied = false\n const applyStoredPolicyOnce = () => {\n if (storedPolicyApplied) return\n const snapshot = preferences.getSnapshot()\n if (snapshot.status === 'loading') return\n storedPolicyApplied = true\n if (snapshot.cacheTtlMinutes === usedCacheTtl) return\n usedCacheTtl = snapshot.cacheTtlMinutes\n void status.reload(snapshot.cacheTtlMinutes)\n }\n applyStoredPolicyOnce()\n const unsubscribe = preferences.subscribe(applyStoredPolicyOnce)\n // Every mount reads the Host's cached status, whichever page it is on; the\n // transport is authenticated and the Host route is the plugin's own. There is\n // no channel to project: the Host always answers with the newest release.\n void status.load(preferences.getSnapshot().cacheTtlMinutes)\n return () => {\n stopped = true\n unsubscribe()\n detach()\n for (const dispose of disposers.splice(0)) {\n try {\n dispose()\n } catch {\n // Contained.\n }\n }\n channel.dispose()\n restart.stop()\n status.stop()\n disposeStyles()\n }\n }, 'dsh-update-status: styles, settings channel and first status read')\n\n // Official seam first: it stays authoritative whenever it is not `unavailable`,\n // so a loopback page keeps the official semantics (one shared describe mirror,\n // the official write queue) and pays no extra wire read. DSH 0.1.7 replaced\n // the per-namespace `settingsScope` service with these entry-addressed forms,\n // keyed by the Loader entry id the Host Config half owns (SETTINGS_NAMESPACE).\n try {\n ctx.inject(['configForms'], (raw: unknown) => {\n try {\n const forms = configFormsOf(raw)\n if (forms === undefined) return\n channel.setOfficial(configFormScope(forms.get<unknown>(SETTINGS_NAMESPACE)))\n } catch {\n // Unreadable settings seam: the preferences stay on their defaults.\n }\n })\n } catch {\n // No configForms seam on this host: the direct channel is the only hope.\n }\n\n // Direct Host channel. The Remote service can arrive before or after the\n // official scope, so `refresh()` re-evaluates the selection either way; the\n // document invalidation keeps this page in step with edits made elsewhere,\n // and a reconnect retries a read that was refused.\n //\n // Every read here is contained: the injected payload intentionally refuses the\n // dotted PARENT (`payload.remote` throws \"cannot get property … without\n // inject\"), so one unreadable member must never abort the whole wiring.\n try {\n ctx.inject(['remote.settings'], (raw: unknown) => {\n try {\n remoteSettings = settingsRemoteOf(raw) ?? remoteSettings\n channel.refresh()\n const invalidations = settingsInvalidationsOf(ctx.get('remote'))\n if (invalidations !== undefined) own(invalidations(() => channel.reload()))\n } catch {\n // No usable Remote seam: the direct channel stays closed and the official\n // scope keeps its verdict.\n }\n })\n } catch {\n // No Remote seam: non-loopback pages keep the honest unavailable state.\n }\n if (typeof ctx.on === 'function') {\n try {\n own(ctx.on('connection/reset', () => { channel.reload(); void restart.refresh() }))\n } catch {\n // No lifecycle event seam on this host.\n }\n }\n\n // Single slot: a compact name + version chip shadows the official wordmark\n // (priority 0). Lowest priority renders; never touch sidebar.brand.mark.\n //\n // The registration itself is conditional, and that is not a detail: a single\n // slot arbitrates by PRIORITY, not by what the component renders. A component\n // that returns null still OCCUPIES the cell, so the official occupant\n // (@deepseek-ai/dsh-client-ui-brand-official) and the shell's own fallback can\n // never come back. Turning the preference off therefore disposes our entry —\n // the previous winner leaves the ledger and the next-lowest occupant renders\n // again — and turning it back on registers a fresh one.\n ctx.slots.inject('sidebar.brand.name', () => {\n let registration: (() => void) | undefined\n const syncRegistration = (): void => {\n const enabled = preferences.getSnapshot().sidebarEnabled\n if (enabled && registration === undefined) {\n registration = ctx.slots.register({ name: 'sidebar.brand.name', priority: -10 }, () => BrandName({ ui }))\n } else if (!enabled && registration !== undefined) {\n registration()\n registration = undefined\n }\n }\n syncRegistration()\n const unsubscribe = preferences.subscribe(syncRegistration)\n return () => {\n unsubscribe()\n registration?.()\n registration = undefined\n }\n })\n\n // additive frame overlay for click/tap panel and narrow-view bottom sheet.\n ctx.slots.inject('shell.overlay', () => ctx.slots.register(\n { name: 'shell.overlay', id: 'dsh-update-status', order: 40 },\n () => UpdatePanel({ ui }),\n ))\n\n // Independent settings section provides the required way to hide our entry.\n ctx.slots.inject('settings.section', () => ctx.slots.register(\n { name: 'settings.section', id: 'dsh-update-status', order: 80, label: () => t('settings.title') },\n () => UpdateSettings({ ui }),\n ))\n}\n\nmodule.exports = {\n name: PLUGIN_ID,\n inject: ['slots', 'connection'],\n apply,\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["PLUGIN_ID","PLUGIN_ID","sameSnapshot"],"sources":["../src/shared/visual-state.ts","../src/shared/types.ts","../src/client/i18n.ts","../src/client/react.ts","../src/client/components.tsx","../src/client/contract.ts","../src/client/stores.ts","../src/client/settings/hostDirectScope.ts","../src/client/settings/configFormScope.ts","../src/client/settings/settingsChannel.ts","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/**\n * The single place that decides which visual state the sidebar chip is in.\n *\n * It lives in `shared` rather than inside the component so the decision is a pure\n * function that a test can drive directly: the regression this guards against is\n * exactly a state-classification mistake, and it was invisible to every test while\n * it sat inside TSX.\n */\nimport type { UpdateStatus } from './types.ts'\n\nexport type VisualState = 'loading' | 'update' | 'current' | 'problem'\n\n/**\n * Only a read the plugin could not complete is a chip-level problem.\n *\n * An advisory notice — a preview channel this bundle has not been verified against,\n * say — leaves the chip surface alone and belongs in the panel. Repainting the brand\n * row for a plugin-side bookkeeping fact is the surprise the breathing dot replaced:\n * an operator who upgrades DSH ahead of this bundle must not watch the chip turn red.\n *\n * A Host older than `warningKind` sends no field at all; `undefined` then keeps the\n * previous behaviour (any warning is a problem) rather than silently dropping a real\n * failure on the floor.\n */\nexport function isChipProblem(status: UpdateStatus | null): boolean {\n if (status === null) return false\n if (status.warningKind === undefined) return status.warning !== null\n return status.warningKind === 'failure'\n}\n\nexport function visualState(status: UpdateStatus | null, loading: boolean, error: string | null): VisualState {\n if (loading) return 'loading'\n if (error !== null || isChipProblem(status)) return status?.hasUpdate === true ? 'update' : 'problem'\n return status?.hasUpdate === true ? 'update' : 'current'\n}\n","/**\n * JSON-only contract shared by the Host and Web halves.\n *\n * The static package uses the authenticated Connection RPC channel because\n * `harness.handle` / `host.call` are dynamic-Cordis-only closure APIs. The\n * endpoint vocabulary remains deliberately small and private to this plugin\n * channel.\n *\n * There is ONE release line, not a channel catalogue: the Host reports the\n * newest version the npm registry publishes under any dist-tag, and the client\n * compares it with the running version. `latest`, `next` and `alpha` are npm's\n * bookkeeping, not a user choice this plugin asks anyone to make.\n */\n\nexport const PLUGIN_ID = 'dsh-update-status'\nexport const PACKAGE_NAME = '@deepseek-ai/dsh'\n/** Shared Connection RPC channel. Custom prefixes 405 on the SPA fallback. */\nexport const UPDATE_STATUS_CHANNEL = '/api'\nexport const RELEASES_URL = 'https://github.com/deepseek-ai/deepseek-harness/releases'\n/**\n * DSH releases this bundle has actually been verified against. The same list is\n * declared in package.json's `dsh.compatibility.dshReleases`, and every other\n * release the registry reports stays `unverified` — the plugin never claims a\n * compatibility nobody checked.\n */\nexport const VERIFIED_DSH_VERSIONS: readonly string[] = ['0.1.7-alpha.2', '0.1.7-rc.1', '0.1.7-rc.2', '0.2.0-rc.1', '0.2.0-rc.2', '0.2.1-alpha.1']\nexport const DEFAULT_CACHE_TTL_MINUTES = 360\nexport const MIN_CACHE_TTL_MINUTES = 30\nexport const MAX_CACHE_TTL_MINUTES = 1_440\n\nexport function isCacheTtlMinutes(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value)\n && value >= MIN_CACHE_TTL_MINUTES && value <= MAX_CACHE_TTL_MINUTES\n}\n\nexport const UPDATE_ENDPOINTS = {\n getStatus: 'dsh-update-status.get-status',\n checkUpdate: 'dsh-update-status.check-update',\n} as const\n\n/** Restart RPC remains on the same authenticated Connection `/api` channel. */\nexport const RESTART_ENDPOINTS = {\n status: 'dsh-update-status.restart-status',\n check: 'dsh-update-status.restart-check',\n request: 'dsh-update-status.restart',\n} as const\n\nexport type UpdateEndpoint = (typeof UPDATE_ENDPOINTS)[keyof typeof UPDATE_ENDPOINTS]\nexport type RestartEndpoint = (typeof RESTART_ENDPOINTS)[keyof typeof RESTART_ENDPOINTS]\nexport type ReleaseCompatibility = 'verified' | 'unverified' | 'incompatible'\n/**\n * Severity of `warning`, so a surface can tell \"the plugin could not determine\n * the update state\" from \"here is something worth knowing\".\n *\n * - `failure`: no usable answer — a failed registry read, or a version SemVer\n * cannot compare. This is what justifies repainting the sidebar chip.\n * - `notice`: the answer is complete and usable; the text is advisory, e.g. a\n * newer release this bundle has not been verified against. The chip must NOT\n * repaint for this — an operator upgrading DSH ahead of the plugin would\n * otherwise see the chip turn red for a plugin-side bookkeeping fact.\n *\n * Optional on purpose: a Host older than this field leaves it `undefined`, and\n * the client then falls back to treating any warning as a failure.\n */\nexport type UpdateWarningKind = 'failure' | 'notice'\nexport type InstallKind = 'npm-global' | 'pnpm-global' | 'source-checkout' | 'unknown'\n\n/** Language-neutral warning facts; the browser renders them in its own locale. */\nexport type UpdateWarning =\n | { code: 'registry-unavailable'; detail: string }\n | { code: 'version-incomparable'; currentVersion: string; latestVersion: string }\n /** A newer release exists, but this bundle has not been tested against it. */\n | { code: 'version-unverified'; version: string }\n /**\n * The Host resolved a `@deepseek-ai/schemastery` that DSH does not ship, so the\n * Loader's volatile projection is unavailable and the preference fields cannot\n * be marked volatile. Advisory: the version answer itself is complete, and the\n * text carries the exact directory to remove. See `host/schemastery.ts`.\n */\n | { code: 'stale-schemastery'; version: string | null; path: string; nodeModulesDir: string | null }\n\n/** Arguments accepted by either read/check endpoint. */\nexport interface CheckUpdateRequest {\n force?: boolean\n /** User preference, bounded by the Host before it affects cache expiry. */\n cacheTtlMinutes?: number\n}\n\n/**\n * A lossless, JSON-serializable snapshot used by both browser surfaces.\n * Nulls are intentional: a failed first check must still render a truthful\n * current-version card instead of an empty or malformed UI.\n */\nexport interface UpdateStatus {\n currentVersion: string\n /** Newest version the registry publishes under any dist-tag; null when unknown. */\n latestVersion: string | null\n hasUpdate: boolean\n /** Whether this bundle was verified against `latestVersion`. */\n compatibility: ReleaseCompatibility\n cached: boolean\n checkedAt: string | null\n /** English fallback for older clients; current clients localize `warnings`. */\n warning: string | null\n /** Severity of `warning`; absent from a Host older than the field. */\n warningKind?: UpdateWarningKind | null\n /** Structured warning facts; absent from a Host older than this field. */\n warnings?: UpdateWarning[]\n installKind: InstallKind\n upgradeCommand: string\n releaseUrl: string\n changelogUrl: string\n publishedAt: string | null\n packageName: string\n /** Phase 1 is informational only; the GUI must never apply an update. */\n canApplyInPlace: false\n}\n","/** Tiny bilingual dictionary kept local to avoid changing the host locale tree. */\n\nimport type { UpdateStatus, UpdateWarning } from '../shared/types.ts'\n\nexport type Language = 'zh' | 'en'\n\ntype Entry = { zh: string; en: string }\n\nconst DICTIONARY: Record<string, Entry> = {\n 'brand.status': { zh: 'DSH 版本状态', en: 'DSH version status' },\n 'brand.checking': { zh: '正在检查版本', en: 'Checking version' },\n 'brand.update': { zh: '有可用更新', en: 'Update available' },\n 'brand.current': { zh: '已是最新或尚未发现更新', en: 'Up to date or no update found' },\n 'brand.problem': { zh: '检查遇到问题', en: 'Check encountered a problem' },\n 'panel.title': { zh: 'DSH 更新状态', en: 'DSH update status' },\n 'panel.close': { zh: '关闭', en: 'Close' },\n 'panel.cacheDuration': { zh: '缓存时长', en: 'Cache duration' },\n 'panel.minutes': { zh: '分钟', en: 'minutes' },\n 'panel.cacheHint': { zh: '到期后在下次打开或读取状态时检查;不会后台轮询。点击“检查更新”会立即检查。', en: 'After expiry, DSH checks only on the next status read; there is no background polling. “Check for updates” checks immediately.' },\n 'panel.cacheReadonly': { zh: '此连接的缓存设置为只读。', en: 'This connection’s cache setting is read-only.' },\n 'panel.current': { zh: '当前运行版本', en: 'Current running version' },\n 'panel.newer': { zh: '可更新至', en: 'Newer version available' },\n 'panel.compatVerified': { zh: '已验证兼容', en: 'Verified compatible' },\n 'panel.compatUnverified': { zh: '尚未验证兼容', en: 'Compatibility unverified' },\n 'panel.compatIncompatible': { zh: '已知不兼容', en: 'Known incompatible' },\n 'panel.notChecked': { zh: '尚未取得', en: 'Not available yet' },\n 'panel.available': { zh: '发现新版本', en: 'Update available' },\n 'panel.currentState': { zh: '已是最新', en: 'Up to date' },\n 'panel.cached': { zh: '来自 Host 缓存', en: 'From Host cache' },\n 'panel.live': { zh: '刚从 npm registry 检查', en: 'Checked npm registry now' },\n 'panel.checkedAt': { zh: '上次检查', en: 'Last checked' },\n 'panel.publishedAt': { zh: '发布时间', en: 'Published' },\n 'panel.check': { zh: '检查更新', en: 'Check for updates' },\n 'panel.checking': { zh: '检查中…', en: 'Checking…' },\n 'panel.releaseNotes': { zh: '发布说明', en: 'Release notes' },\n 'panel.command': { zh: '升级命令', en: 'Upgrade command' },\n 'panel.copy': { zh: '复制命令', en: 'Copy command' },\n 'panel.copied': { zh: '已复制', en: 'Copied' },\n 'panel.copyFallback': { zh: '浏览器未允许复制;已选中文本,请长按或复制。', en: 'Clipboard unavailable; the command is selected for long-press or copy.' },\n 'panel.commandNote': { zh: '仅复制,不会执行。请在运行 DSH 的那台电脑的终端执行;完成后仅在已安装用户级监督服务时使用本面板的重启,否则由你自行重启 DSH。', en: 'Copy only — nothing runs here. Execute it in a terminal on the computer running DSH, then use this panel’s restart only when user-level supervision is installed; otherwise restart DSH yourself.' },\n 'panel.readOnly': { zh: '插件不会安装、升级、回滚或替换 DSH 文件;只有当前 DSH 已由用户显式安装的后台服务监督时,才允许安全重启。', en: 'The plugin never installs, upgrades, rolls back, or replaces DSH files. Safe restart is available only when the current DSH is supervised by a user-installed service.' },\n 'restart.title': { zh: '安全重启 DSH Web', en: 'Safely restart DSH Web' },\n 'restart.button': { zh: '重启', en: 'Restart' },\n 'restart.loading': { zh: '读取重启状态…', en: 'Reading restart status…' },\n 'restart.checking': { zh: '检查活动任务…', en: 'Checking active work…' },\n 'restart.confirm': { zh: '确认重启', en: 'Confirm restart' },\n 'restart.force': { zh: '强制重启', en: 'Force restart' },\n 'restart.cancel': { zh: '取消', en: 'Cancel' },\n 'restart.retry': { zh: '重新检查', en: 'Check again' },\n 'restart.confirmHint': { zh: '再次点击“确认重启”后,页面会短暂断开;后台服务恢复新实例后将自动刷新。', en: 'Click Confirm restart again. The page disconnects briefly and refreshes once the supervised service starts a new instance.' },\n 'restart.activeWarning': { zh: '检测到 {count} 个正在运行的任务。强制重启会中断它们。', en: '{count} active work item(s) are running. A forced restart interrupts them.' },\n 'restart.waitingShort': { zh: '重启中…', en: 'Restarting…' },\n 'restart.waiting': { zh: '正在等待后台服务恢复 DSH(已等待 {seconds} 秒)…', en: 'Waiting for the user service to restore DSH ({seconds}s elapsed)…' },\n 'restart.timeout': { zh: '60 秒内未检测到新 DSH 实例。请检查用户服务状态和日志;重新加载本页不会再次触发重启。', en: 'No new DSH instance was detected within 60 seconds. Check the user-service status and logs; reloading this page will not trigger another restart.' },\n 'restart.unavailable': { zh: '当前 DSH 不是由已确认的用户级后台服务启动。为避免页面重启后无法恢复,按钮已禁用;请先按服务管理文档安装并接管 DSH。', en: 'This DSH is not running under a verified user-level service. Restart is disabled so the page cannot strand itself; install and activate supervision first.' },\n 'restart.unavailableDesktop': { zh: 'DSH Desktop 管理当前 Host;请退出并重新打开桌面应用。', en: 'DSH Desktop manages this Host. Quit and reopen the desktop app instead.' },\n 'restart.unavailableMismatch': { zh: '检测到监督标记,但当前进程不符合对应平台服务的身份。为安全起见,重启已禁用。', en: 'A supervision marker exists, but this process does not match the platform service identity. Restart is disabled for safety.' },\n 'restart.unavailableActivity': { zh: '无法完整读取正在运行的任务。为避免中断未能列出的工作,重启已禁用。', en: 'Active work could not be inspected completely. Restart is disabled to avoid interrupting work that could not be listed.' },\n 'restart.unavailableTimer': { zh: '当前 Host 没有可用的安全退出调度器。为确保响应先送达浏览器,重启已禁用。', en: 'This Host has no safe exit scheduler. Restart is disabled so its response can reach the browser first.' },\n 'restart.unavailableStale': { zh: '此页面连接的是过期 DSH 实例。请等待恢复或重新加载后再重试。', en: 'This page is connected to a stale DSH instance. Wait for recovery or reload before trying again.' },\n 'panel.error': { zh: '检查提示', en: 'Check notice' },\n 'warning.registryUnavailable': { zh: '无法检查 npm registry:{detail}', en: 'Unable to check the npm registry: {detail}' },\n 'warning.versionIncomparable': { zh: '无法按 SemVer 比较当前版本 {currentVersion} 与线上版本 {latestVersion}。', en: 'Unable to compare the current version {currentVersion} with the published version {latestVersion} using SemVer.' },\n 'warning.versionUnverified': { zh: '线上版本 {version} 尚未验证与本插件兼容。', en: 'Version {version} has not been verified as compatible with this plugin.' },\n 'warning.staleSchemastery': { zh: '本插件解析到的 @deepseek-ai/schemastery(版本 {version})来自 {path},不是 DSH 自带的那份,偏好字段无法标记为 volatile。请删除该残留目录并重启 DSH:rm -rf {nodeModulesDir}', en: 'This plugin resolved @deepseek-ai/schemastery {version} from {path} instead of the copy DSH provides, so preference fields cannot be marked volatile. Remove the stale directory and restart DSH: rm -rf {nodeModulesDir}' },\n 'warning.staleSchemasteryNoPath': { zh: '本插件解析到的 @deepseek-ai/schemastery(版本 {version})来自 {path},不是 DSH 自带的那份,偏好字段无法标记为 volatile。请删除该处残留的 @deepseek-ai/schemastery 目录并重启 DSH。', en: 'This plugin resolved @deepseek-ai/schemastery {version} from {path} instead of the copy DSH provides, so preference fields cannot be marked volatile. Remove the stale @deepseek-ai/schemastery directory there and restart DSH.' },\n 'guidance.sourceCheckout': { zh: '请更新 DSH 源码 checkout、安装依赖并重新构建;本插件无法从 GUI 原地替换。', en: 'Update the DSH source checkout, install its dependencies, and rebuild it; this plugin cannot replace it in place from the GUI.' },\n 'guidance.unknownInstall': { zh: '升级前请先确认 DSH 的安装方式;本插件无法代为执行升级。', en: 'Confirm how DSH was installed before upgrading; this plugin cannot perform the upgrade for you.' },\n 'settings.title': { zh: '版本与更新', en: 'Version & updates' },\n 'settings.sidebar': { zh: '在侧栏显示版本状态入口', en: 'Show the version-status entry in the sidebar' },\n 'settings.sidebarHint': { zh: '关闭后不再渲染品牌行里的版本芯片,品牌行恢复为 DSH 官方内容。不会执行或安排升级。', en: 'When off, the version chip in the brand row is not rendered and the brand row falls back to DSH’s own content. No update is run or scheduled.' },\n 'settings.readonly': { zh: '此连接的设置为只读;显示状态不受影响。', en: 'Settings are read-only on this connection; status display is unchanged.' },\n}\n\nexport function languageOf(): Language {\n try {\n return navigator.language.toLowerCase().startsWith('zh') ? 'zh' : 'en'\n } catch {\n return 'en'\n }\n}\n\nexport function t(key: keyof typeof DICTIONARY, params: Record<string, string> = {}, language: Language = languageOf()): string {\n const entry = DICTIONARY[key]\n if (entry === undefined) return key\n const template = entry[language] ?? entry.en\n return template.replace(/\\{([A-Za-z]+)\\}/g, (match, name: string) => params[name] ?? match)\n}\n\nexport function warningText(warning: UpdateWarning, language: Language = languageOf()): string {\n switch (warning.code) {\n case 'registry-unavailable': return t('warning.registryUnavailable', { detail: warning.detail }, language)\n case 'version-incomparable': return t('warning.versionIncomparable', {\n currentVersion: warning.currentVersion,\n latestVersion: warning.latestVersion,\n }, language)\n case 'version-unverified': return t('warning.versionUnverified', { version: warning.version }, language)\n case 'stale-schemastery': {\n const params = { version: warning.version ?? '—', path: warning.path }\n // Only print a removal command when the Host could name the directory to\n // remove; the module path alone would make `rm -rf` delete a single file.\n return warning.nodeModulesDir === null\n ? t('warning.staleSchemasteryNoPath', params, language)\n : t('warning.staleSchemastery', { ...params, nodeModulesDir: warning.nodeModulesDir }, language)\n }\n }\n}\n\n/** Prefer structured warnings, but keep the Host string for mixed-version clients. */\nexport function localizedWarning(status: Pick<UpdateStatus, 'warning' | 'warnings'>, language: Language = languageOf()): string | null {\n const warnings = status.warnings ?? []\n return warnings.length === 0 ? status.warning : warnings.map(item => warningText(item, language)).join(' ')\n}\n\nexport function localizedUpgradeGuidance(\n status: Pick<UpdateStatus, 'installKind' | 'upgradeCommand'>,\n language: Language = languageOf(),\n): string {\n if (status.installKind === 'source-checkout') return t('guidance.sourceCheckout', {}, language)\n if (status.installKind === 'unknown') return t('guidance.unknownInstall', {}, language)\n return status.upgradeCommand\n}\n","/** React arrives from DSH's frozen browser module table at client materialization. */\nimport type * as ReactNS from 'react'\n\n// eslint/oxlint would normally discourage require; DSH's closure factory makes\n// this the supported runtime import path for a third-party client bundle.\nexport const React: typeof ReactNS = require('react') as typeof ReactNS\nexport const h = React.createElement\n","/** Sidebar chip, overlay detail panel, and settings page. */\n\nimport type * as ReactNS from 'react'\nimport { isChipProblem, visualState } from '../shared/visual-state.ts'\nimport { MAX_CACHE_TTL_MINUTES, MIN_CACHE_TTL_MINUTES, isCacheTtlMinutes, type ReleaseCompatibility, type UpdateStatus } from '../shared/types.ts'\nimport type { PreferencesStore, PanelStore, RestartStore, StatusStore } from './stores.ts'\nimport { localizedUpgradeGuidance, localizedWarning, t } from './i18n.ts'\nimport { React, h } from './react.ts'\n\nfunction useObservable<T>(store: { subscribe(listener: () => void): () => void; getSnapshot(): T }): T {\n return React.useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot)\n}\n\nfunction timeText(value: string | null): string | null {\n if (value === null) return null\n try {\n return new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' }).format(new Date(value))\n } catch {\n return value\n }\n}\n\nfunction shortVersion(value: string): string {\n if (value.length <= 16) return value\n return value.slice(0, 15) + '…'\n}\n\nfunction compatibilityLabel(value: ReleaseCompatibility): string {\n if (value === 'verified') return t('panel.compatVerified')\n if (value === 'incompatible') return t('panel.compatIncompatible')\n return t('panel.compatUnverified')\n}\n\n/** Persists a cache policy only; it never schedules a browser or Host timer. */\nfunction CacheTtlControl({ preferences }: { preferences: PreferencesStore }): ReactNS.ReactElement {\n const snapshot = useObservable(preferences)\n const [draft, setDraft] = React.useState(String(snapshot.cacheTtlMinutes))\n React.useEffect(() => { setDraft(String(snapshot.cacheTtlMinutes)) }, [snapshot.cacheTtlMinutes])\n const save = (): void => {\n const value = Number(draft)\n if (isCacheTtlMinutes(value)) preferences.setCacheTtlMinutes(value)\n else setDraft(String(snapshot.cacheTtlMinutes))\n }\n return (\n <div className=\"dus-cache-setting\">\n <label className=\"dus-cache-field\">\n <span>{t('panel.cacheDuration')}</span>\n <span className=\"dus-cache-input-wrap\">\n <input\n className=\"dus-cache-input\"\n type=\"number\"\n inputMode=\"numeric\"\n min={MIN_CACHE_TTL_MINUTES}\n max={MAX_CACHE_TTL_MINUTES}\n step={1}\n value={draft}\n disabled={!snapshot.writable}\n aria-describedby=\"dus-cache-hint\"\n onChange={(event) => { setDraft(event.currentTarget.value) }}\n onBlur={save}\n onKeyDown={(event) => {\n if (event.key === 'Enter') { event.currentTarget.blur() }\n if (event.key === 'Escape') { setDraft(String(snapshot.cacheTtlMinutes)); event.currentTarget.blur() }\n }}\n />\n <span>{t('panel.minutes')}</span>\n </span>\n </label>\n <p className=\"dus-cache-hint\" id=\"dus-cache-hint\">{t('panel.cacheHint')}</p>\n {!snapshot.writable && <p className=\"dus-cache-hint\">{t('panel.cacheReadonly')}</p>}\n </div>\n )\n}\n\nfunction badgeLabel(status: UpdateStatus | null, loading: boolean, error: string | null): string {\n const state = visualState(status, loading, error)\n if (state === 'loading') return t('brand.checking')\n if (state === 'update') return t('brand.update')\n if (state === 'problem') return t('brand.problem')\n return t('brand.current')\n}\n\nexport interface SharedUi {\n status: StatusStore\n preferences: PreferencesStore\n panel: PanelStore\n restart: RestartStore\n}\n\n/** Occupies ONLY sidebar.brand.name; the official fish mark stays untouched. */\nexport function BrandName({ ui }: { ui: SharedUi }): ReactNS.ReactElement | null {\n const preferences = useObservable(ui.preferences)\n const snapshot = useObservable(ui.status)\n // Defence in depth only: the entry that renders this component is already\n // disposed while the preference is off, because a single slot arbitrates by\n // priority and returning null would keep occupying the cell. See\n // client/index.ts — that disposal is what brings the official brand back.\n if (!preferences.sidebarEnabled) return null\n\n const state = visualState(snapshot.status, snapshot.loading, snapshot.error)\n const version = snapshot.status?.currentVersion ?? '—'\n const activate = (event: ReactNS.SyntheticEvent): void => {\n // The surrounding sidebar brand is an existing New Session <button>. This\n // non-button interaction prevents a nested button and stops its click.\n event.preventDefault()\n event.stopPropagation()\n // The official brand identity ancestor is aria-hidden. Do not leave focus\n // inside it while the modal dialog is open (Chrome otherwise warns).\n const target = event.currentTarget\n if (target instanceof HTMLElement) target.blur()\n ui.panel.toggle()\n }\n const onKeyDown = (event: ReactNS.KeyboardEvent<HTMLSpanElement>): void => {\n if (event.key !== 'Enter' && event.key !== ' ') return\n activate(event)\n }\n\n return (\n <span className=\"dus-brand-name\">\n {/* No handler: clicking this still bubbles to the shell's New Session button. */}\n <span className=\"dus-brand-deepseek\">DeepSeek</span>\n <span\n className=\"dus-badge\"\n data-update={snapshot.status?.hasUpdate === true || undefined}\n data-error={state === 'problem' || undefined}\n role=\"button\"\n tabIndex={0}\n aria-label={badgeLabel(snapshot.status, snapshot.loading, snapshot.error)}\n title={badgeLabel(snapshot.status, snapshot.loading, snapshot.error)}\n onClick={activate}\n onKeyDown={onKeyDown}\n >\n <span className=\"dus-badge-version\">{shortVersion(version)}</span>\n {/* `data-state` drives the palette (green when up to date); `data-update`\n and `data-loading` remain the louder, state-specific rules. */}\n <span className=\"dus-dot\" data-state={state} data-update={snapshot.status?.hasUpdate === true || undefined} data-loading={snapshot.loading || undefined} aria-hidden=\"true\" />\n </span>\n </span>\n )\n}\n\nfunction restartUnavailableText(reason: 'not-supervised' | 'desktop' | 'supervisor-mismatch' | 'activity-unavailable' | 'timer-unavailable' | 'stale-instance' | null | undefined): string {\n if (reason === 'desktop') return t('restart.unavailableDesktop')\n if (reason === 'supervisor-mismatch') return t('restart.unavailableMismatch')\n if (reason === 'activity-unavailable') return t('restart.unavailableActivity')\n if (reason === 'timer-unavailable') return t('restart.unavailableTimer')\n if (reason === 'stale-instance') return t('restart.unavailableStale')\n return t('restart.unavailable')\n}\n\nfunction RestartControl({ restart }: { restart: RestartStore }): ReactNS.ReactElement {\n const snapshot = useObservable(restart)\n React.useEffect(() => { void restart.refresh() }, [restart])\n const unavailable = snapshot.status !== null && !snapshot.status.available\n const hasActivity = snapshot.activity.length > 0\n const waiting = snapshot.phase === 'waiting'\n const label = snapshot.phase === 'unknown' ? t('restart.loading')\n : snapshot.phase === 'checking' ? t('restart.checking')\n : snapshot.phase === 'armed' ? (hasActivity ? t('restart.force') : t('restart.confirm'))\n : waiting ? t('restart.waitingShort')\n : snapshot.phase === 'timeout' ? t('restart.retry')\n : snapshot.phase === 'error' ? t('restart.retry')\n : t('restart.button')\n const disabled = snapshot.phase === 'unknown' || snapshot.phase === 'checking' || waiting || unavailable\n const click = (): void => {\n if (snapshot.phase === 'armed') { void restart.request(hasActivity); return }\n if (snapshot.phase === 'timeout' || snapshot.phase === 'error') { void restart.check(); return }\n void restart.check()\n }\n return <>\n <button\n className=\"dus-action dus-restart\"\n type=\"button\"\n disabled={disabled}\n title={unavailable ? restartUnavailableText(snapshot.status?.unavailableReason) : t('restart.title')}\n onClick={click}\n >{label}</button>\n {snapshot.phase === 'armed' && <button className=\"dus-action\" type=\"button\" onClick={() => restart.cancel()}>{t('restart.cancel')}</button>}\n {unavailable && <p className=\"dus-restart-message\" role=\"status\">{restartUnavailableText(snapshot.status?.unavailableReason)}</p>}\n {snapshot.phase === 'armed' && <p className=\"dus-restart-message\" role=\"status\">\n {hasActivity ? t('restart.activeWarning', { count: String(snapshot.activity.length) }) : t('restart.confirmHint')}\n </p>}\n {hasActivity && snapshot.phase === 'armed' && <ul className=\"dus-restart-work\">{snapshot.activity.map(item => <li key={`${item.type}:${item.id}`}>{item.label} · {item.status}</li>)}</ul>}\n {waiting && <p className=\"dus-restart-message\" role=\"status\">{t('restart.waiting', { seconds: String(Math.floor(snapshot.elapsedMs / 1000)) })}</p>}\n {snapshot.phase === 'timeout' && <p className=\"dus-restart-message\" role=\"alert\">{t('restart.timeout')}</p>}\n {snapshot.phase === 'error' && snapshot.error !== null && <p className=\"dus-restart-message\" role=\"alert\">{snapshot.error}</p>}\n </>\n}\n\nfunction statusSummary(status: UpdateStatus | null, loading: boolean, error: string | null): { kind: 'update' | 'ok' | 'warning' | 'error'; text: string } {\n if (loading && status === null) return { kind: 'warning', text: t('brand.checking') }\n if (error !== null) return { kind: 'error', text: error }\n if (status?.hasUpdate === true) {\n return { kind: 'update', text: `${t('panel.available')} · ${status.latestVersion ?? ''}`.trim() }\n }\n if (status !== null) {\n const warning = localizedWarning(status)\n if (warning !== null) return { kind: 'warning', text: warning }\n }\n return { kind: 'ok', text: t('panel.currentState') }\n}\n\nfunction selectCommand(element: HTMLElement | null): void {\n if (element === null) return\n try {\n const selection = window.getSelection()\n if (selection === null) return\n const range = document.createRange()\n range.selectNodeContents(element)\n selection.removeAllRanges()\n selection.addRange(range)\n element.focus()\n } catch {\n // The command remains ordinary selectable text even when selection fails.\n }\n}\n\n/**\n * The one upgrade command plus its copy affordance, shared by the panel and the\n * settings section so both surfaces can never disagree about what to run.\n */\nfunction CommandBlock({ command, disabled = false }: { command: string; disabled?: boolean }): ReactNS.ReactElement {\n const commandRef = React.useRef<HTMLElement | null>(null)\n const [copyMessage, setCopyMessage] = React.useState<string | null>(null)\n const copy = async (): Promise<void> => {\n setCopyMessage(null)\n try {\n if (!navigator.clipboard?.writeText) throw new Error('Clipboard API unavailable')\n await navigator.clipboard.writeText(command)\n setCopyMessage(t('panel.copied'))\n } catch {\n selectCommand(commandRef.current)\n setCopyMessage(t('panel.copyFallback'))\n }\n }\n return (\n <>\n <p className=\"dus-command-label\">{t('panel.command')}</p>\n <code ref={commandRef} className=\"dus-command\" tabIndex={0}>{command}</code>\n <div className=\"dus-actions dus-command-actions\">\n <button className=\"dus-action\" type=\"button\" disabled={disabled} onClick={() => { void copy() }}>{t('panel.copy')}</button>\n {copyMessage !== null && <p className=\"dus-copy-message\" role=\"status\">{copyMessage}</p>}\n </div>\n <p className=\"dus-note\">{t('panel.commandNote')}</p>\n </>\n )\n}\n\n/** Full detail from whichever page the operator is on (the transport is authenticated). */\nexport function UpdatePanel({ ui }: { ui: SharedUi }): ReactNS.ReactElement | null {\n const panel = useObservable(ui.panel)\n const preferences = useObservable(ui.preferences)\n const snapshot = useObservable(ui.status)\n const dialogRef = React.useRef<HTMLDialogElement | null>(null)\n const visible = panel.open && preferences.sidebarEnabled\n\n React.useLayoutEffect(() => {\n if (!visible) return undefined\n const dialog = dialogRef.current\n if (dialog === null) return undefined\n try {\n if (!dialog.open) dialog.showModal()\n } catch {\n // Older embedded Chromium still receives a visible non-top-layer dialog.\n dialog.setAttribute('open', '')\n }\n return () => {\n if (dialog.open) dialog.close()\n }\n }, [visible])\n\n React.useEffect(() => {\n if (!panel.open) return undefined\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') ui.panel.close()\n }\n window.addEventListener('keydown', onKeyDown)\n return () => { window.removeEventListener('keydown', onKeyDown) }\n }, [panel.open, ui.panel])\n\n if (!visible) return null\n const status = snapshot.status\n const warning = status === null ? null : localizedWarning(status)\n const summary = statusSummary(status, snapshot.loading, snapshot.error)\n const hasUpdate = status?.hasUpdate === true && status.latestVersion !== null\n const command = status === null || !hasUpdate ? '' : localizedUpgradeGuidance(status)\n\n return (\n <dialog\n ref={dialogRef}\n className=\"dus-overlay-root\"\n aria-label={t('panel.title')}\n onCancel={(event) => {\n event.preventDefault()\n ui.panel.close()\n }}\n >\n <div className=\"dus-backdrop\" onClick={() => { ui.panel.close() }} />\n <section className=\"dus-panel\" role=\"dialog\" aria-modal=\"true\" aria-label={t('panel.title')} onClick={(event) => { event.stopPropagation() }}>\n <div className=\"dus-panel-head\">\n <span className=\"dus-panel-title\">{t('panel.title')}</span>\n <button className=\"dus-close\" type=\"button\" aria-label={t('panel.close')} onClick={() => { ui.panel.close() }}>×</button>\n </div>\n\n <CacheTtlControl preferences={ui.preferences} />\n <p className=\"dus-state\" data-kind={summary.kind}>{summary.text}</p>\n\n <div className=\"dus-metadata\">\n <div className=\"dus-metadata-row\">\n <span className=\"dus-meta-label\">{t('panel.current')}</span>\n <code className=\"dus-meta-value\">{status?.currentVersion ?? '…'}</code>\n </div>\n {hasUpdate && <div className=\"dus-metadata-row\">\n <span className=\"dus-meta-label\">{t('panel.newer')}</span>\n <code className=\"dus-meta-value\">{status.latestVersion}</code>\n {status.publishedAt !== null && <span className=\"dus-meta-sub\">{t('panel.publishedAt')}: {timeText(status.publishedAt)}</span>}\n <span className=\"dus-meta-sub dus-compat\" data-compatibility={status.compatibility}>{compatibilityLabel(status.compatibility)}</span>\n </div>}\n {status?.checkedAt !== null && status?.checkedAt !== undefined && <div className=\"dus-metadata-row\">\n <span className=\"dus-meta-label\">{t('panel.checkedAt')}</span>\n <span className=\"dus-meta-value\">{timeText(status.checkedAt)}</span>\n <span className=\"dus-meta-sub\">{status.cached ? t('panel.cached') : t('panel.live')}</span>\n </div>}\n </div>\n\n {warning !== null && <p className=\"dus-warning\">{t('panel.error')}: {warning}</p>}\n {snapshot.error !== null && <p className=\"dus-warning\">{t('panel.error')}: {snapshot.error}</p>}\n\n <div className=\"dus-actions\">\n <button className=\"dus-action\" type=\"button\" disabled={snapshot.loading} onClick={() => { void ui.status.refresh(preferences.cacheTtlMinutes) }}>\n {snapshot.loading ? t('panel.checking') : t('panel.check')}\n </button>\n {status !== null && <a className=\"dus-action\" href={status.changelogUrl} target=\"_blank\" rel=\"noreferrer\">{t('panel.releaseNotes')}</a>}\n <RestartControl restart={ui.restart} />\n </div>\n\n {hasUpdate && <CommandBlock command={command} disabled={snapshot.loading} />}\n <p className=\"dus-note\">{t('panel.readOnly')}</p>\n </section>\n </dialog>\n )\n}\n\n/** Separate setting page: disables only this plugin's own rendering. */\nexport function UpdateSettings({ ui }: { ui: SharedUi }): ReactNS.ReactElement {\n const preferences = useObservable(ui.preferences)\n const snapshot = useObservable(ui.status)\n const status = snapshot.status\n const summary = statusSummary(status, snapshot.loading, snapshot.error)\n const hasUpdate = status?.hasUpdate === true && status.latestVersion !== null\n const command = status === null || !hasUpdate ? '' : localizedUpgradeGuidance(status)\n return (\n <section className=\"dus-settings\">\n <h2 className=\"dus-settings-heading\">{t('settings.title')}</h2>\n <div className=\"dus-settings-card\">\n <label className=\"dus-settings-toggle\">\n <input\n type=\"checkbox\"\n checked={preferences.sidebarEnabled}\n disabled={!preferences.writable}\n onChange={(event) => { ui.preferences.setSidebarEnabled(event.currentTarget.checked) }}\n />\n <span>{t('settings.sidebar')}</span>\n </label>\n <p className=\"dus-settings-hint\">{t('settings.sidebarHint')}</p>\n {!preferences.writable && <p className=\"dus-settings-hint\">{t('settings.readonly')}</p>}\n </div>\n <div className=\"dus-settings-card\">\n <p className=\"dus-settings-hint\">{t('panel.current')}: <code>{status?.currentVersion ?? '…'}</code></p>\n <p className=\"dus-state\" data-kind={summary.kind}>{summary.text}</p>\n {hasUpdate && <p className=\"dus-settings-hint\">{t('panel.newer')}: <code>{status.latestVersion}</code> · <span className=\"dus-compat\" data-compatibility={status.compatibility}>{compatibilityLabel(status.compatibility)}</span></p>}\n {snapshot.error !== null && <p role=\"alert\">{snapshot.error}</p>}\n {hasUpdate && <CommandBlock command={command} disabled={snapshot.loading} />}\n <div className=\"dus-actions\">\n <button className=\"dus-action\" type=\"button\" disabled={snapshot.loading} onClick={() => { void ui.status.refresh(preferences.cacheTtlMinutes) }}>\n {snapshot.loading ? t('panel.checking') : t('panel.check')}\n </button>\n {status !== null && <a className=\"dus-action\" href={status.changelogUrl} target=\"_blank\" rel=\"noreferrer\">{t('panel.releaseNotes')}</a>}\n <RestartControl restart={ui.restart} />\n </div>\n <p className=\"dus-settings-hint\">{t('panel.readOnly')}</p>\n </div>\n </section>\n )\n}\n","/** Minimal structural client faces — runtime services stay owned by DSH. */\n\nimport type { UpdateStatus, UpdateWarning } from '../shared/types.ts'\n\nexport type RestartPhase = 'unknown' | 'idle' | 'armed' | 'checking' | 'waiting' | 'timeout' | 'error'\n\nexport interface RestartStatusValue {\n instanceId: string\n available: boolean\n supervisor: 'launchd' | 'systemd' | 'task-scheduler' | 'unknown' | null\n unavailableReason: 'not-supervised' | 'desktop' | 'supervisor-mismatch' | 'activity-unavailable' | 'timer-unavailable' | 'stale-instance' | null\n}\n\nexport interface RestartActivityItemValue {\n type: 'agent' | 'job' | 'terminal'\n id: string\n label: string\n status: string\n ownerSession?: string\n}\n\nexport interface RestartCheckValue {\n kind: 'ready' | 'active-work' | 'unavailable'\n status: RestartStatusValue\n activity?: { hasActive: boolean; items: RestartActivityItemValue[] }\n}\n\nexport interface RestartRequestValue {\n kind: 'ready' | 'active-work' | 'unavailable' | 'scheduled' | 'in-progress'\n status?: RestartStatusValue\n activity?: { hasActive: boolean; items: RestartActivityItemValue[] }\n instanceId?: string\n}\n\n\nexport interface Observable<T> {\n getSnapshot(): T\n subscribe(listener: () => void): () => void\n}\n\nexport interface ConnectionRpc {\n call(channel: string, endpoint: string, payload: unknown, signal?: AbortSignal): Promise<unknown>\n}\n\n/**\n * The authenticated transport face. There is deliberately NO `isLoopback` gate\n * here: DSH's settings Client treats a non-loopback page as process-local, but\n * that is a settings *persistence* policy — the Connection RPC itself stays\n * authenticated and reachable over a LAN bridge, so gating this plugin's own\n * read-only status on it only made the panel inert on exactly the page it is\n * used from. See settings/settingsChannel.ts for the one place a loopback\n * distinction still matters (choosing the Host settings channel).\n */\nexport interface ConnectionClient {\n rpc?: ConnectionRpc\n}\n\nexport interface SlotRegistration {\n name: string\n id?: string\n order?: number\n /** Single-slot arbitration: the lowest registered priority renders. */\n priority?: number\n label?: () => string\n locale?: string\n inject?: () => unknown\n}\n\nexport interface Slots {\n inject(name: string, callback: () => unknown): () => void\n register(registration: SlotRegistration, component: (props: Record<string, unknown>) => unknown): () => void\n}\n\nexport interface ClientContext {\n slots: Slots\n get(name: string): unknown\n inject(names: string[], callback: (ctx: unknown) => void): () => void\n effect(setup: () => (() => void) | void, label?: string): () => void\n /** Optional lifecycle event seam (Cordis contexts expose it; guarded here). */\n on?(name: string, listener: () => void): unknown\n}\n\nexport function errorMessage(error: unknown): string {\n const text = error instanceof Error ? error.message : String(error)\n return text.replace(/\\s+/g, ' ').trim().slice(0, 220) || 'unknown error'\n}\n\nfunction objectOf(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined\n}\n\nexport function restartStatusOf(value: unknown): RestartStatusValue | undefined {\n const record = objectOf(value)\n if (record === undefined || typeof record.instanceId !== 'string' || record.instanceId === '' || typeof record.available !== 'boolean') return undefined\n const supervisor = record.supervisor === null ? null\n : record.supervisor === 'launchd' || record.supervisor === 'systemd' || record.supervisor === 'task-scheduler' || record.supervisor === 'unknown'\n ? record.supervisor : undefined\n const unavailableReason = record.unavailableReason === null ? null\n : record.unavailableReason === 'not-supervised' || record.unavailableReason === 'desktop' || record.unavailableReason === 'supervisor-mismatch' || record.unavailableReason === 'activity-unavailable' || record.unavailableReason === 'timer-unavailable' || record.unavailableReason === 'stale-instance'\n ? record.unavailableReason : undefined\n if (supervisor === undefined || unavailableReason === undefined) return undefined\n if (record.available && (supervisor === null || unavailableReason !== null)) return undefined\n if (!record.available && unavailableReason === null) return undefined\n return { instanceId: record.instanceId, available: record.available, supervisor, unavailableReason }\n}\n\nfunction restartActivityOf(value: unknown): { hasActive: boolean; items: RestartActivityItemValue[] } | undefined {\n const record = objectOf(value)\n if (record === undefined || typeof record.hasActive !== 'boolean' || !Array.isArray(record.items)) return undefined\n const items: RestartActivityItemValue[] = []\n for (const raw of record.items) {\n const item = objectOf(raw)\n if (item === undefined || (item.type !== 'agent' && item.type !== 'job' && item.type !== 'terminal')\n || typeof item.id !== 'string' || typeof item.label !== 'string' || typeof item.status !== 'string') return undefined\n if (item.ownerSession !== undefined && typeof item.ownerSession !== 'string') return undefined\n items.push({ type: item.type, id: item.id, label: item.label, status: item.status, ...(item.ownerSession === undefined ? {} : { ownerSession: item.ownerSession }) })\n }\n if (record.hasActive !== (items.length > 0)) return undefined\n return { hasActive: record.hasActive, items }\n}\n\nexport function restartCheckOf(value: unknown): RestartCheckValue | undefined {\n const record = objectOf(value)\n if (record === undefined || (record.kind !== 'ready' && record.kind !== 'active-work' && record.kind !== 'unavailable')) return undefined\n const status = restartStatusOf(record.status)\n if (status === undefined) return undefined\n const activity = record.activity === undefined ? undefined : restartActivityOf(record.activity)\n if (record.activity !== undefined && activity === undefined) return undefined\n if (record.kind === 'active-work' && (activity === undefined || !activity.hasActive)) return undefined\n if (record.kind !== 'active-work' && activity !== undefined) return undefined\n return { kind: record.kind, status, ...(activity === undefined ? {} : { activity }) }\n}\n\nexport function restartRequestOf(value: unknown): RestartRequestValue | undefined {\n const record = objectOf(value)\n if (record === undefined || (record.kind !== 'ready' && record.kind !== 'active-work' && record.kind !== 'unavailable' && record.kind !== 'scheduled' && record.kind !== 'in-progress')) return undefined\n if (record.kind === 'scheduled' || record.kind === 'in-progress') return typeof record.instanceId === 'string' && record.instanceId !== '' ? { kind: record.kind, instanceId: record.instanceId } : undefined\n const check = restartCheckOf(record)\n return check === undefined ? undefined : check\n}\n\nfunction stringOrNull(value: unknown): string | null {\n return typeof value === 'string' ? value : null\n}\n\nfunction warningOf(value: unknown): UpdateWarning | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined\n const record = value as Record<string, unknown>\n if (record.code === 'registry-unavailable' && typeof record.detail === 'string') {\n return { code: record.code, detail: record.detail }\n }\n if (record.code === 'version-incomparable' && typeof record.currentVersion === 'string'\n && typeof record.latestVersion === 'string') {\n return {\n code: record.code,\n currentVersion: record.currentVersion,\n latestVersion: record.latestVersion,\n }\n }\n if (record.code === 'version-unverified' && typeof record.version === 'string') {\n return { code: record.code, version: record.version }\n }\n if (record.code === 'stale-schemastery') {\n const version = record.version === null ? null : stringOrNull(record.version)\n const nodeModulesDir = record.nodeModulesDir === null ? null : stringOrNull(record.nodeModulesDir)\n const path = stringOrNull(record.path)\n if (path !== null && (record.version === null || version !== null)\n && (record.nodeModulesDir === null || nodeModulesDir !== null)) {\n return { code: record.code, version, path, nodeModulesDir }\n }\n }\n return undefined\n}\n\n/** Reject malformed RPC output before it reaches a slot component. */\nexport function updateStatusOf(value: unknown): UpdateStatus | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined\n const record = value as Record<string, unknown>\n if (typeof record.currentVersion !== 'string' || typeof record.hasUpdate !== 'boolean'\n || typeof record.cached !== 'boolean' || typeof record.installKind !== 'string'\n || typeof record.upgradeCommand !== 'string' || typeof record.releaseUrl !== 'string'\n || typeof record.changelogUrl !== 'string' || typeof record.packageName !== 'string'\n || record.canApplyInPlace !== false) return undefined\n const validKind = record.installKind === 'npm-global' || record.installKind === 'pnpm-global'\n || record.installKind === 'source-checkout' || record.installKind === 'unknown'\n if (!validKind) return undefined\n if (record.compatibility !== 'verified' && record.compatibility !== 'unverified' && record.compatibility !== 'incompatible') return undefined\n const latestVersion = record.latestVersion === null ? null : stringOrNull(record.latestVersion)\n const checkedAt = record.checkedAt === null ? null : stringOrNull(record.checkedAt)\n const warning = record.warning === null ? null : stringOrNull(record.warning)\n // Absent or unrecognised means a Host older than the field: leave it undefined and\n // let the visual state fall back to \"any warning is a failure\", never to \"no warning\".\n const warningKind = record.warningKind === 'failure' || record.warningKind === 'notice' ? record.warningKind : undefined\n const publishedAt = record.publishedAt === null ? null : stringOrNull(record.publishedAt)\n if ((record.latestVersion !== null && latestVersion === null) || (record.checkedAt !== null && checkedAt === null)\n || (record.warning !== null && warning === null) || (record.publishedAt !== null && publishedAt === null)) return undefined\n const warnings: UpdateWarning[] = []\n if (record.warnings !== undefined) {\n if (!Array.isArray(record.warnings)) return undefined\n for (const raw of record.warnings) {\n const parsed = warningOf(raw)\n if (parsed === undefined) return undefined\n warnings.push(parsed)\n }\n }\n return {\n currentVersion: record.currentVersion,\n latestVersion,\n hasUpdate: record.hasUpdate,\n compatibility: record.compatibility,\n cached: record.cached,\n checkedAt,\n warning,\n warningKind,\n warnings,\n installKind: record.installKind as UpdateStatus['installKind'],\n upgradeCommand: record.upgradeCommand,\n releaseUrl: record.releaseUrl,\n changelogUrl: record.changelogUrl,\n publishedAt,\n packageName: record.packageName,\n canApplyInPlace: false,\n }\n}\n","/** Small observable stores shared by the independent sidebar and overlay slots. */\n\nimport { DEFAULT_CACHE_TTL_MINUTES, isCacheTtlMinutes, PLUGIN_ID, RESTART_ENDPOINTS, UPDATE_ENDPOINTS, UPDATE_STATUS_CHANNEL, type UpdateStatus } from '../shared/types.ts'\nimport type { ConnectionClient, Observable, RestartActivityItemValue, RestartPhase, RestartStatusValue } from './contract.ts'\nimport { errorMessage, restartCheckOf, restartRequestOf, restartStatusOf, updateStatusOf } from './contract.ts'\nimport type { SettingsScopeLike } from './settings/scopeFaces.ts'\n\nexport interface StatusSnapshot {\n status: UpdateStatus | null\n loading: boolean\n error: string | null\n}\n\nconst INITIAL_STATUS: StatusSnapshot = { status: null, loading: true, error: null }\n\nexport class StatusStore implements Observable<StatusSnapshot> {\n private snapshot: StatusSnapshot = INITIAL_STATUS\n private readonly listeners = new Set<() => void>()\n private inFlight: Promise<boolean> | undefined\n private stopped = false\n\n constructor(private readonly connection: ConnectionClient) {}\n\n getSnapshot = (): StatusSnapshot => this.snapshot\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n /** First mount reads the Host's cached status; it never starts a browser timer. */\n async load(cacheTtlMinutes: number = DEFAULT_CACHE_TTL_MINUTES): Promise<void> {\n await this.request(false, cacheTtlMinutes)\n }\n\n /** User gesture only: force the Host to bypass TTL (while retaining single-flight). */\n async refresh(cacheTtlMinutes: number = DEFAULT_CACHE_TTL_MINUTES): Promise<void> {\n await this.request(true, cacheTtlMinutes)\n }\n\n /**\n * Ordinary read that waits out an in-flight request first.\n *\n * Used once per mount, when the stored cache policy arrives after the first\n * read already started: joining that request would keep the default policy, so\n * the follow-up read has to come after it settles.\n */\n async reload(cacheTtlMinutes: number): Promise<void> {\n const pending = this.inFlight\n if (pending !== undefined) await pending\n if (this.stopped) return\n await this.request(false, cacheTtlMinutes)\n }\n\n stop(): void {\n this.stopped = true\n this.listeners.clear()\n }\n\n private publish(next: StatusSnapshot): void {\n if (this.stopped) return\n this.snapshot = next\n for (const listener of this.listeners) listener()\n }\n\n private request(force: boolean, cacheTtlMinutes: number): Promise<boolean> {\n if (this.inFlight !== undefined) return this.inFlight\n const rpc = this.connection.rpc\n if (rpc === undefined || typeof rpc.call !== 'function') {\n this.publish({ ...this.snapshot, loading: false, error: 'DSH connection RPC is unavailable' })\n return Promise.resolve(false)\n }\n\n this.publish({ ...this.snapshot, loading: true, error: null })\n const run = (async () => {\n try {\n const endpoint = force ? UPDATE_ENDPOINTS.checkUpdate : UPDATE_ENDPOINTS.getStatus\n const ttl = isCacheTtlMinutes(cacheTtlMinutes) ? cacheTtlMinutes : DEFAULT_CACHE_TTL_MINUTES\n const raw = await rpc.call(UPDATE_STATUS_CHANNEL, endpoint, force\n ? { force: true, cacheTtlMinutes: ttl }\n : { cacheTtlMinutes: ttl })\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('invalid update-status RPC response')\n const envelope = raw as { ok?: unknown; value?: unknown; error?: { message?: unknown } }\n if (envelope.ok !== true) throw new Error(typeof envelope.error?.message === 'string' ? envelope.error.message : 'update-status RPC failed')\n const status = updateStatusOf(envelope.value)\n if (status === undefined) throw new Error('invalid update-status payload')\n this.publish({ status, loading: false, error: null })\n return true\n } catch (error) {\n this.publish({ ...this.snapshot, loading: false, error: errorMessage(error) })\n return false\n }\n })()\n this.inFlight = run\n void run.finally(() => {\n if (this.inFlight === run) this.inFlight = undefined\n })\n return run\n }\n}\n\nexport interface PreferencesSnapshot {\n sidebarEnabled: boolean\n cacheTtlMinutes: number\n writable: boolean\n status: 'loading' | 'ready' | 'unavailable'\n}\n\nconst INITIAL_PREFERENCES: PreferencesSnapshot = {\n sidebarEnabled: true,\n cacheTtlMinutes: DEFAULT_CACHE_TTL_MINUTES,\n writable: false,\n status: 'loading',\n}\n\nexport class PreferencesStore implements Observable<PreferencesSnapshot> {\n private snapshot: PreferencesSnapshot = INITIAL_PREFERENCES\n private readonly listeners = new Set<() => void>()\n private scope: SettingsScopeLike | undefined\n\n getSnapshot = (): PreferencesSnapshot => this.snapshot\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n attach(scope: SettingsScopeLike): () => void {\n this.scope = scope\n const sync = () => {\n const raw = scope.getSnapshot()\n const value = raw.value !== null && typeof raw.value === 'object' && !Array.isArray(raw.value)\n ? raw.value as Record<string, unknown>\n : {}\n const status = raw.status === 'ready' || raw.status === 'unavailable' ? raw.status : 'loading'\n this.publish({\n sidebarEnabled: value.sidebarEnabled !== false,\n cacheTtlMinutes: isCacheTtlMinutes(value.cacheTtlMinutes) ? value.cacheTtlMinutes : DEFAULT_CACHE_TTL_MINUTES,\n writable: raw.writable === true,\n status,\n })\n }\n sync()\n return scope.subscribe(sync)\n }\n\n setSidebarEnabled(enabled: boolean): void {\n const previous = this.snapshot\n this.publish({ ...previous, sidebarEnabled: enabled })\n const scope = this.scope\n if (scope === undefined || !previous.writable) return\n void scope.set('sidebarEnabled', enabled).catch(() => {\n // A rejected Host-backed write restores the authoritative scope snapshot.\n try {\n const raw = scope.getSnapshot()\n const value = raw.value !== null && typeof raw.value === 'object' && !Array.isArray(raw.value)\n ? raw.value as Record<string, unknown>\n : {}\n this.publish({\n sidebarEnabled: value.sidebarEnabled !== false,\n cacheTtlMinutes: isCacheTtlMinutes(value.cacheTtlMinutes) ? value.cacheTtlMinutes : DEFAULT_CACHE_TTL_MINUTES,\n writable: raw.writable === true,\n status: raw.status === 'ready' || raw.status === 'unavailable' ? raw.status : 'loading',\n })\n } catch {\n this.publish(previous)\n }\n })\n }\n\n setCacheTtlMinutes(cacheTtlMinutes: number): void {\n if (!isCacheTtlMinutes(cacheTtlMinutes)) return\n const previous = this.snapshot\n this.publish({ ...previous, cacheTtlMinutes })\n const scope = this.scope\n if (scope === undefined || !previous.writable) return\n void scope.set('cacheTtlMinutes', cacheTtlMinutes).catch(() => { this.publish(previous) })\n }\n\n private publish(next: PreferencesSnapshot): void {\n const previous = this.snapshot\n if (previous.sidebarEnabled === next.sidebarEnabled\n && previous.cacheTtlMinutes === next.cacheTtlMinutes\n && previous.writable === next.writable && previous.status === next.status) return\n this.snapshot = next\n for (const listener of this.listeners) listener()\n }\n}\n\nexport interface PanelSnapshot {\n open: boolean\n}\n\n/** Open state of the detail panel, whose only trigger is the brand-row chip. */\nexport class PanelStore implements Observable<PanelSnapshot> {\n private snapshot: PanelSnapshot = { open: false }\n private readonly listeners = new Set<() => void>()\n\n getSnapshot = (): PanelSnapshot => this.snapshot\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n toggle(): void {\n this.set({ open: !this.snapshot.open })\n }\n\n close(): void { this.set({ open: false }) }\n\n private set(next: PanelSnapshot): void {\n if (this.snapshot.open === next.open) return\n this.snapshot = next\n for (const listener of this.listeners) listener()\n }\n}\n\nexport interface RestartSnapshot {\n phase: RestartPhase\n status: RestartStatusValue | null\n activity: readonly RestartActivityItemValue[]\n elapsedMs: number\n error: string | null\n}\n\nexport interface RestartStoreOptions {\n /** Injected by tests; defaults to browser timers. */\n readonly schedule?: (callback: () => void, delay: number) => unknown\n readonly cancel?: (handle: unknown) => void\n /** Per-RPC safety timeout used only while the Host is restarting. */\n readonly requestTimeout?: (callback: () => void, delay: number) => unknown\n readonly cancelRequestTimeout?: (handle: unknown) => void\n /** Same-origin liveness fallback when a new Host lacks this plugin route. */\n readonly liveness?: () => Promise<boolean>\n readonly reload?: () => void\n}\n\nconst INITIAL_RESTART: RestartSnapshot = {\n phase: 'unknown', status: null, activity: [], elapsedMs: 0, error: null,\n}\n\n/**\n * Restart state belongs to the client entry, not a modal component, so closing\n * the update panel cannot strand a confirmed restart half-way through recovery.\n * It starts no timer until the user confirms a restart request.\n */\nexport class RestartStore implements Observable<RestartSnapshot> {\n private snapshot: RestartSnapshot = INITIAL_RESTART\n private readonly listeners = new Set<() => void>()\n private timer: unknown\n private generation = 0\n private stopped = false\n private readonly schedule: (callback: () => void, delay: number) => unknown\n private readonly cancelTimer: (handle: unknown) => void\n private readonly requestTimeout: (callback: () => void, delay: number) => unknown\n private readonly cancelRequestTimeout: (handle: unknown) => void\n private readonly liveness: () => Promise<boolean>\n private readonly reload: () => void\n private pendingAbort: AbortController | undefined\n private reloaded = false\n\n constructor(private readonly connection: ConnectionClient, options: RestartStoreOptions = {}) {\n this.schedule = options.schedule ?? ((callback, delay) => window.setTimeout(callback, delay))\n this.cancelTimer = options.cancel ?? (handle => { window.clearTimeout(handle as number) })\n this.requestTimeout = options.requestTimeout ?? ((callback, delay) => window.setTimeout(callback, delay))\n this.cancelRequestTimeout = options.cancelRequestTimeout ?? (handle => { window.clearTimeout(handle as number) })\n this.liveness = options.liveness ?? (async () => {\n try {\n const response = await fetch(new URL('.', window.location.href), { method: 'HEAD', credentials: 'same-origin', cache: 'no-store' })\n return response.status === 200 || response.status === 401\n } catch { return false }\n })\n this.reload = options.reload ?? (() => window.location.reload())\n }\n\n getSnapshot = (): RestartSnapshot => this.snapshot\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n stop(): void {\n this.stopped = true\n this.generation += 1\n if (this.timer !== undefined) this.cancelTimer(this.timer)\n this.timer = undefined\n this.pendingAbort?.abort()\n this.pendingAbort = undefined\n this.listeners.clear()\n }\n\n /** Read capability on a visible surface; this is not a restart and never polls. */\n async refresh(): Promise<void> {\n if (this.snapshot.phase === 'waiting') return\n const raw = await this.call(RESTART_ENDPOINTS.status, {})\n if (raw === undefined) return\n const status = restartStatusOf(raw)\n if (status === undefined) {\n this.publish({ ...this.snapshot, phase: 'error', error: 'invalid restart-status payload' })\n return\n }\n this.publish({ phase: status.available ? 'idle' : 'idle', status, activity: [], elapsedMs: 0, error: null })\n }\n\n /** First click checks active work and arms the second, destructive action. */\n async check(): Promise<void> {\n this.publish({ ...this.snapshot, phase: 'checking', error: null })\n const raw = await this.call(RESTART_ENDPOINTS.check, {})\n if (raw === undefined) return\n const checked = restartCheckOf(raw)\n if (checked === undefined) {\n this.publish({ ...this.snapshot, phase: 'error', error: 'invalid restart-check payload' })\n return\n }\n if (checked.kind === 'unavailable') {\n this.publish({ phase: 'idle', status: checked.status, activity: [], elapsedMs: 0, error: null })\n return\n }\n this.publish({\n phase: 'armed', status: checked.status,\n activity: checked.activity?.items ?? [], elapsedMs: 0, error: null,\n })\n }\n\n cancel(): void {\n if (this.snapshot.phase !== 'armed' && this.snapshot.phase !== 'error' && this.snapshot.phase !== 'timeout') return\n this.publish({ ...this.snapshot, phase: this.snapshot.status === null ? 'unknown' : 'idle', activity: [], elapsedMs: 0, error: null })\n }\n\n /** Second click emits the Host request; `force` is only true after the activity list was shown. */\n async request(force: boolean): Promise<void> {\n const current = this.snapshot\n if (current.phase !== 'armed' || current.status === null || !current.status.available) return\n this.publish({ ...current, phase: 'checking', error: null })\n const raw = await this.call(RESTART_ENDPOINTS.request, { force, expectedInstanceId: current.status.instanceId })\n if (raw === undefined) return\n const result = restartRequestOf(raw)\n if (result === undefined) {\n this.publish({ ...this.snapshot, phase: 'error', error: 'invalid restart payload' })\n return\n }\n if (result.kind === 'scheduled' || result.kind === 'in-progress') {\n if (result.instanceId === undefined) {\n this.publish({ ...this.snapshot, phase: 'error', error: 'restart response omitted instance identity' })\n return\n }\n this.beginRecovery(result.instanceId)\n return\n }\n if (result.kind === 'active-work' || result.kind === 'ready') {\n this.publish({\n phase: 'armed', status: result.status ?? current.status,\n activity: result.activity?.items ?? [], elapsedMs: 0, error: null,\n })\n return\n }\n this.publish({ phase: 'idle', status: result.status ?? current.status, activity: [], elapsedMs: 0, error: null })\n }\n\n private beginRecovery(previousInstanceId: string): void {\n const generation = ++this.generation\n const startedAt = Date.now()\n this.publish({ ...this.snapshot, phase: 'waiting', elapsedMs: 0, error: null })\n const attempt = async (delay: number): Promise<void> => {\n if (this.stopped || generation !== this.generation) return\n const elapsedMs = Date.now() - startedAt\n if (elapsedMs >= 60_000) {\n this.publish({ ...this.snapshot, phase: 'timeout', elapsedMs, error: null })\n return\n }\n this.timer = this.schedule(() => {\n void (async () => {\n if (this.stopped || generation !== this.generation) return\n const raw = await this.call(RESTART_ENDPOINTS.status, {}, false, 4_000)\n if (this.stopped || generation !== this.generation) return\n const status = raw === undefined ? undefined : restartStatusOf(raw)\n const nextElapsed = Date.now() - startedAt\n if (status !== undefined && status.instanceId !== previousInstanceId) {\n this.publish({ phase: 'waiting', status, activity: [], elapsedMs: nextElapsed, error: null })\n if (!this.reloaded) { this.reloaded = true; this.reload() }\n return\n }\n // A healthy carrier with no plugin route means the new DSH came back\n // but this plugin did not load. Refresh so the page can show the real\n // startup failure instead of claiming DSH is still down forever.\n if (raw === undefined && await this.liveness()) {\n if (!this.reloaded) { this.reloaded = true; this.reload() }\n return\n }\n this.publish({ ...this.snapshot, phase: 'waiting', elapsedMs: nextElapsed })\n await attempt(Math.min(delay * 2, 10_000))\n })()\n }, delay)\n }\n void attempt(1_000)\n }\n\n private async call(endpoint: string, payload: unknown, surfaceError: boolean = true, timeoutMs?: number): Promise<unknown | undefined> {\n const rpc = this.connection.rpc\n if (rpc === undefined || typeof rpc.call !== 'function') {\n if (surfaceError) this.publish({ ...this.snapshot, phase: 'error', error: 'DSH connection RPC is unavailable' })\n return undefined\n }\n const controller = timeoutMs === undefined ? undefined : new AbortController()\n if (controller !== undefined) this.pendingAbort = controller\n let timeout: unknown\n if (controller !== undefined && timeoutMs !== undefined) timeout = this.requestTimeout(() => controller.abort(), timeoutMs)\n try {\n const raw = await rpc.call(UPDATE_STATUS_CHANNEL, endpoint, payload, controller?.signal)\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('invalid restart RPC response')\n const envelope = raw as { ok?: unknown; value?: unknown; error?: { message?: unknown } }\n if (envelope.ok !== true) throw new Error(typeof envelope.error?.message === 'string' ? envelope.error.message : 'restart RPC failed')\n return envelope.value\n } catch (error) {\n if (surfaceError) this.publish({ ...this.snapshot, phase: 'error', error: errorMessage(error) })\n return undefined\n } finally {\n if (timeout !== undefined) this.cancelRequestTimeout(timeout)\n if (this.pendingAbort === controller) this.pendingAbort = undefined\n }\n }\n\n private publish(next: RestartSnapshot): void {\n if (this.stopped) return\n this.snapshot = next\n for (const listener of this.listeners) listener()\n }\n}\n\n/**\n * The one settings namespace, which on DSH 0.1.7 IS the Loader entry id in\n * `cordis.patch.yml`: the Host half's volatile `Config` fields are projected as\n * that entry's form, and the browser half addresses the same id through\n * `ctx.configForms.get(...)`.\n */\nexport const SETTINGS_NAMESPACE = PLUGIN_ID\n","/**\n * dsh-update-status — direct Host settings channel (non-loopback pages).\n *\n * WHY THIS EXISTS\n *\n * DSH's settings Client keeps Host persistence disabled on any page whose\n * location is not a loopback authority. `ctx.configForms.get(entryId)` — the\n * DSH 0.1.7 successor of `settingsScope.bind()` — then answers\n * `status: 'unavailable'` immediately and NEVER sends `settings.describe`\n * (`@deepseek-ai/dsh-client-ui-settings`: the persistence mode resolves from\n * `remote.$host.isLoopback` and pins a remote page to `memory`; every row it\n * backs is inert even though Connection authentication covers the API).\n *\n * The decision itself is one line in the official client:\n *\n * const persistence = ctx.remote.$host.isLoopback ? \"host\" : \"memory\"\n *\n * and `isLoopback` is true only for `localhost` / `[::1]` / `127.0.0.0/8` pages\n * (or a transport that declares `ownsHost`). A LAN page reached through a\n * forwarding plugin is therefore non-loopback even though the API answers and\n * the page is authenticated.\n *\n * This plugin's preferences — the sidebar toggle, the followed release channel\n * and the cache policy — live in the Host settings entry `dsh-update-status`, so\n * without a channel they all silently fall back to defaults on a LAN page (the\n * host's configured `channel` would be ignored, silently switching the followed\n * release line). This module speaks the SAME public Remote the official form\n * speaks (`settings.describe` / `settings.mutate`, the generated Typert face\n * mounted by `@deepseek-ai/dsh-api-remotes`) and derives the same per-namespace\n * snapshot shape, so the ONE shared Host document keeps serving every device.\n *\n * This channel is opened ONLY when the official form reports `unavailable`\n * (see `settingsChannel.ts`), so loopback pages keep the official path and pay\n * no extra wire read. Everything here is guarded: an absent Remote, a refused\n * read, or a malformed answer leaves the preferences exactly as unavailable as\n * they were before this channel existed.\n */\nimport type { SettingsPathOpLike, SettingsScopeLike, SettingsScopeSnapshotLike } from './scopeFaces.ts'\n\n/** One namespace row of a `settings.describe` answer (the fields consumed here). */\nexport interface SettingsNamespaceViewLike {\n readonly ns: string\n readonly value: unknown\n readonly base?: unknown\n readonly user?: unknown\n readonly revision: number\n}\n\n/** The `settings.describe` answer body. */\nexport interface SettingsDescribeViewLike {\n readonly writable: boolean\n readonly hasDocument?: boolean\n readonly namespaces: readonly SettingsNamespaceViewLike[]\n}\n\n/** The Remote envelope every generated client call answers with. */\nexport type SettingsRemoteOutcome<T> =\n | { readonly ok: true; readonly value: T }\n | { readonly ok: false; readonly error?: { readonly code?: string; readonly message?: string } }\n\n/** The `remote.settings` namespace face (structural; never imported at runtime). */\nexport interface SettingsRemoteLike {\n describe(): Promise<SettingsRemoteOutcome<SettingsDescribeViewLike>>\n mutate(\n namespace: string,\n ops: readonly SettingsPathOpLike[],\n expectedRevision?: number,\n ): Promise<SettingsRemoteOutcome<SettingsNamespaceViewLike>>\n}\n\n/** A refused namespace write; `code` keeps the Host's own diagnosis. */\nexport class SettingsWriteFailure extends Error {\n readonly code: string\n\n constructor(code: string, message?: string) {\n super(message === undefined || message === '' ? code : message)\n this.name = 'SettingsWriteFailure'\n this.code = code\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\n/**\n * Guard: does this value look like the `remote.settings` namespace face?\n * Applied to a bare service object (`ctx.get('remote.settings')` or a payload\n * member), and it never calls anything.\n */\nexport function settingsRemoteFace(value: unknown): SettingsRemoteLike | undefined {\n if (!isRecord(value)) return undefined\n if (typeof value.describe !== 'function' || typeof value.mutate !== 'function') return undefined\n return value as unknown as SettingsRemoteLike\n}\n\n/**\n * Guard: read the `remote.settings` face off an injection payload.\n *\n * The payload a `ctx.inject(['remote.settings'], …)` callback receives exposes\n * the LITERAL service key. Reading the dotted PARENT off that injected context\n * (`payload.remote`) throws `cannot get property \"remote\" without inject` — a\n * throw that used to abort a whole wiring callback on a LAN page. So the literal\n * key is read FIRST and every read is contained; the nested\n * (`payload.remote.settings`) shape is only a fallback for a plain object\n * payload. Both absence and refusal answer `undefined`.\n */\nexport function settingsRemoteOf(raw: unknown): SettingsRemoteLike | undefined {\n if (!isRecord(raw)) return undefined\n try {\n const direct = settingsRemoteFace((raw as Record<string, unknown>)['remote.settings'])\n if (direct !== undefined) return direct\n } catch {\n // Injected-context proxy refusal: fall through to the nested shape.\n }\n try {\n const remote = (raw as { remote?: unknown }).remote\n if (isRecord(remote)) return settingsRemoteFace(remote.settings)\n } catch {\n // Same refusal: no face is readable from this payload.\n }\n return undefined\n}\n\n/**\n * Guard: subscribe to the Host's settings-document invalidation on the Remote\n * service object (`remote.$on`). Returns undefined when the service exposes no\n * event seam — the preferences then refresh on reconnect only.\n */\nexport function settingsInvalidationsOf(service: unknown): ((listener: () => void) => () => void) | undefined {\n if (!isRecord(service)) return undefined\n const on = service.$on\n if (typeof on !== 'function') return undefined\n return (listener) => {\n const dispose = (on as (event: string, callback: () => void) => unknown)\n .call(service, 'settings/document-updated', listener)\n return typeof dispose === 'function' ? dispose as () => void : () => {}\n }\n}\n\n/** The channel consumers subscribe to, plus the refresh/dispose seams the client wires. */\nexport interface HostDirectScope extends SettingsScopeLike {\n /** Re-read the Host document; resolves after the snapshot reflects the answer. */\n load(): Promise<void>\n /** Drop every listener and stop accepting work. */\n dispose(): void\n /** Last Host error message (diagnostics only; never rendered as plugin state). */\n lastError(): string | undefined\n}\n\nfunction cloneOps(ops: readonly SettingsPathOpLike[]): SettingsPathOpLike[] {\n try {\n return structuredClone(ops) as SettingsPathOpLike[]\n } catch {\n // structuredClone absent/refusing the shape: copy by hand (the op payload is\n // plain JSON by contract, so a shallow structural copy is equivalent).\n return ops.map(op => (op.op === 'set'\n ? { op: 'set' as const, path: [...op.path], value: op.value }\n : { op: 'unset' as const, path: [...op.path] }))\n }\n}\n\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\nfunction sameSnapshot(a: SettingsScopeSnapshotLike, b: SettingsScopeSnapshotLike): boolean {\n return a.status === b.status\n && a.revision === b.revision\n && a.writable === b.writable\n && a.mode === b.mode\n && a.value === b.value\n && a.base === b.base\n && a.user === b.user\n}\n\nfunction readySnapshot(\n row: SettingsNamespaceViewLike,\n writable: boolean,\n): SettingsScopeSnapshotLike {\n return {\n status: 'ready',\n // The raw section crosses this boundary untouched: PreferencesStore is the\n // plugin's own strict judge and reports a hand-edited or future-schema\n // document truthfully instead of silently dropping it.\n value: row.value,\n base: row.base,\n user: row.user,\n revision: row.revision,\n writable,\n mode: 'host',\n }\n}\n\nfunction unavailableSnapshot(writable: boolean): SettingsScopeSnapshotLike {\n return {\n status: 'unavailable',\n value: undefined,\n base: undefined,\n user: undefined,\n revision: undefined,\n writable,\n mode: 'host',\n }\n}\n\n/**\n * Build the direct Host settings scope for one namespace.\n *\n * Snapshot semantics mirror the official per-namespace derivation: a namespace\n * row answers `ready` with the resolved section, its `base`/`user` layers and its\n * revision fence, and the document's `writable` decides editability. A row the\n * Host does not serve answers `unavailable`; a refused read keeps the last\n * confirmed document and reports `unavailable` only while it has never held one.\n *\n * Writes are serialized in issue order, fenced by the latest known revision and\n * REJECT with {@link SettingsWriteFailure} on a Host refusal — that rejection is\n * what makes the settings page surface a conflict instead of pretending the edit\n * landed (the official scope resolves on `!ok` after its recovery read).\n *\n * @param remote - the `remote.settings` face of the page's Remote service.\n * @param namespace - the settings namespace this scope derives from.\n * @returns the scope plus its load/dispose seams.\n */\nexport function createHostDirectScope(remote: SettingsRemoteLike, namespace: string): HostDirectScope {\n let snapshot: SettingsScopeSnapshotLike = {\n status: 'loading',\n value: undefined,\n base: undefined,\n user: undefined,\n revision: undefined,\n writable: false,\n mode: 'host',\n }\n let held: SettingsScopeSnapshotLike | undefined\n let error: string | undefined\n let disposed = false\n let tail: Promise<void> = Promise.resolve()\n const listeners = new Set<() => void>()\n\n const publish = (next: SettingsScopeSnapshotLike): void => {\n if (disposed || sameSnapshot(next, snapshot)) return\n snapshot = next\n for (const listener of [...listeners]) {\n try {\n listener()\n } catch {\n // A broken listener must never break the channel.\n }\n }\n }\n\n const adopt = (next: SettingsScopeSnapshotLike): void => {\n if (next.status === 'ready') held = next\n publish(next)\n }\n\n async function load(): Promise<void> {\n if (disposed) return\n let outcome: SettingsRemoteOutcome<SettingsDescribeViewLike>\n try {\n outcome = await remote.describe()\n } catch (caught) {\n outcome = { ok: false, error: { message: messageOf(caught) } }\n }\n if (disposed) return\n if (!outcome.ok) {\n error = outcome.error?.message ?? outcome.error?.code ?? 'settings/describe-failed'\n // A refused read never invents state: the last confirmed document stays\n // authoritative; without one the channel is unavailable.\n publish(held ?? unavailableSnapshot(snapshot.writable))\n return\n }\n error = undefined\n const view = outcome.value\n const writable = view.writable === true\n const row = Array.isArray(view.namespaces)\n ? view.namespaces.find(candidate => candidate.ns === namespace)\n : undefined\n if (row === undefined || typeof row.revision !== 'number') {\n publish(unavailableSnapshot(writable))\n return\n }\n adopt(readySnapshot(row, writable))\n }\n\n /** Fold one write answer's namespace row in without a second wire read. */\n function fold(row: SettingsNamespaceViewLike, writable: boolean): void {\n if (typeof row.revision !== 'number') return\n adopt(readySnapshot(row, writable))\n }\n\n function mutate(ops: readonly SettingsPathOpLike[], expectedRevision?: number): Promise<void> {\n const owned = cloneOps(ops)\n const run = tail.then(async () => {\n if (disposed) throw new SettingsWriteFailure('settings/unavailable')\n const revision = expectedRevision ?? snapshot.revision\n let outcome: SettingsRemoteOutcome<SettingsNamespaceViewLike>\n try {\n outcome = revision === undefined\n ? await remote.mutate(namespace, owned)\n : await remote.mutate(namespace, owned, revision)\n } catch (caught) {\n await load()\n throw new SettingsWriteFailure('settings/unreachable', messageOf(caught))\n }\n if (outcome.ok) {\n // A successful write proves the document accepts writes on this page.\n fold(outcome.value, true)\n return\n }\n await load()\n throw new SettingsWriteFailure(\n outcome.error?.code ?? 'settings/unknown',\n outcome.error?.message,\n )\n })\n // The queue never poisons itself: the caller sees the rejection, the next\n // queued write still runs.\n tail = run.then(() => undefined, () => undefined)\n return run\n }\n\n return {\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n // The official scope implements `set` exactly this way; the revision fence\n // falls back to the latest known one, same as the official pendingRevision.\n set: (field, value) => mutate([{ op: 'set', path: [field], value }]),\n load,\n dispose() {\n disposed = true\n listeners.clear()\n },\n lastError: () => error,\n }\n}\n","/**\n * dsh-update-status — official settings-form adapter.\n *\n * DSH 0.1.7 removed the per-namespace `settingsScope` service this plugin's\n * client half was built on. Its successor is `ctx.configForms`\n * (`@deepseek-ai/dsh-client-ui-settings`): one shared `settings.describe`\n * mirror per browser plus a per-entry write queue, reached as\n * `ctx.configForms.get(entryId)` for the entry the plugin's Host half owns.\n * That form already answers the exact facts this plugin's own scope contract\n * needs — status, value, base, user, revision, writable, mode — so this module\n * only projects it onto {@link SettingsScopeLike} and leaves the existing\n * channel selection (`settingsChannel.ts`) in charge of the LAN fallback.\n *\n * Two deliberate translations:\n *\n * - A refused write (`set` answers `false`) becomes a REJECTION. The settings\n * page must surface a conflict instead of pretending the edit landed, which\n * is the same contract the direct Host channel already obeys\n * (`hostDirectScope.ts`). A transport fault rejects too.\n * - `mode` is what separates the two pages: DSH pins it to `memory` on a\n * non-loopback page, where the form is terminally `unavailable` and never\n * writes — exactly the state that hands the preferences to the direct Host\n * Remote so a phone still edits the one shared entry.\n *\n * The service is deliberately read structurally: the client bundle purity gate\n * forbids cross-plugin value imports, and the runtime object belongs to the\n * user's harness.\n */\nimport type { SettingsScopeLike, SettingsScopeSnapshotLike } from './scopeFaces.ts'\nimport { SettingsWriteFailure } from './hostDirectScope.ts'\n\n/** One form snapshot as `ConfigForm.getSnapshot()` answers it. */\nexport interface ConfigFormSnapshotLike<T> {\n status: 'loading' | 'ready' | 'unavailable'\n value: T | undefined\n base: unknown\n user: unknown\n revision: number | undefined\n writable: boolean\n mode: 'host' | 'memory'\n}\n\n/** The subset of `ConfigForm<T>` this adapter reads. */\nexport interface ConfigFormLike<T = unknown> {\n getSnapshot(): ConfigFormSnapshotLike<T>\n subscribe(listener: () => void): () => void\n set(field: string, value: unknown): Promise<boolean>\n}\n\n/** The `ctx.configForms` binder face. */\nexport interface ConfigFormsFace {\n get<T>(entryId: string): ConfigFormLike<T>\n}\n\n/**\n * Guard: read the `configForms` face off an injection payload.\n *\n * The literal service key is read inside a `try`, because an injected payload\n * proxy can refuse a member read (the failure this plugin already hit on the\n * dotted `remote.settings` parent). Absence and refusal both answer\n * `undefined`, which leaves the channel on its direct Host path.\n *\n * @param raw - the payload a `ctx.inject(['configForms'], …)` callback received.\n * @returns the binder face, or undefined when this page has no settings forms.\n */\nexport function configFormsOf(raw: unknown): ConfigFormsFace | undefined {\n if (raw === null || typeof raw !== 'object') return undefined\n let face: unknown\n try {\n face = (raw as Record<string, unknown>)['configForms']\n } catch {\n return undefined\n }\n if (face === null || typeof face !== 'object') return undefined\n if (typeof (face as { get?: unknown }).get !== 'function') return undefined\n return face as ConfigFormsFace\n}\n\n/**\n * Project one official config form onto the plugin's settings-scope contract.\n *\n * @param form - the shared form for the plugin's own Loader entry id.\n * @returns a scope the preferences store and the channel selection can use as-is.\n */\nexport function configFormScope(form: ConfigFormLike): SettingsScopeLike {\n return {\n getSnapshot(): SettingsScopeSnapshotLike {\n const snapshot = form.getSnapshot()\n return {\n status: snapshot.status,\n value: snapshot.value as SettingsScopeSnapshotLike['value'],\n base: snapshot.base,\n user: snapshot.user,\n revision: snapshot.revision,\n writable: snapshot.writable,\n mode: snapshot.mode,\n }\n },\n subscribe(listener) {\n return form.subscribe(listener)\n },\n async set(field, value) {\n let accepted: boolean\n try {\n accepted = await form.set(field, value)\n } catch (error) {\n // Transport fault: the write did not land and the caller must not\n // pretend it did (the recovery read belongs to the form itself).\n throw new SettingsWriteFailure(\n 'settings/unreachable',\n error instanceof Error ? error.message : String(error),\n )\n }\n if (!accepted) {\n // Refused or skipped (memory mode, stale revision, Host validation):\n // the form has already re-read the authoritative value.\n throw new SettingsWriteFailure('settings/conflict')\n }\n },\n }\n}\n","/**\n * dsh-update-status — settings source selection.\n *\n * The plugin's ONE storage contract is the Host settings entry\n * `dsh-update-status`. Two client channels can serve it:\n *\n * 1. the official `ctx.configForms` form (loopback pages) — the DSH 0.1.7\n * successor of the removed `settingsScope` service, and\n * 2. the direct Host channel (`hostDirectScope.ts`), which exists exactly\n * because the official form is deliberately inert on a non-loopback page —\n * the LAN page a phone uses.\n *\n * This module picks between them without ever guessing:\n *\n * - The official form wins whenever it is not `unavailable`, so a loopback page\n * keeps the official semantics (the one shared describe mirror, the official\n * write queue and revision fences) and pays NO extra wire read.\n * - Only when the official form reports `unavailable` (the documented\n * non-loopback degradation: it is pinned to `memory` mode there and never\n * crosses the wire) is the direct channel opened, lazily and once.\n * - With neither source the channel stays `loading`, i.e. exactly the state this\n * plugin had before the direct channel existed.\n */\nimport type { SettingsScopeLike, SettingsScopeSnapshotLike } from './scopeFaces.ts'\n\n/** Snapshot used while no channel has answered yet. */\nconst LOADING_SNAPSHOT: SettingsScopeSnapshotLike = Object.freeze({\n status: 'loading',\n value: undefined,\n base: undefined,\n user: undefined,\n revision: undefined,\n writable: false,\n mode: 'host',\n}) as SettingsScopeSnapshotLike\n\nexport interface SettingsChannelOptions {\n /**\n * Build the direct Host channel. Called at most once, and only when the\n * official scope cannot serve this page. Returning `undefined` (the Remote\n * service is not composed) leaves the channel on the official/unavailable\n * path; a later {@link SettingsChannel.refresh} retries.\n */\n openDirect(): SettingsScopeLike | undefined\n}\n\nexport interface SettingsChannel extends SettingsScopeLike {\n /** Point the channel at the official form once the service answers. */\n setOfficial(scope: SettingsScopeLike | undefined): void\n /** Re-evaluate the selection (e.g. the Remote service just arrived). */\n refresh(): void\n /** Ask the active source for a fresh read (no-op for the official form). */\n reload(): void\n dispose(): void\n}\n\nfunction sameSnapshot(a: SettingsScopeSnapshotLike, b: SettingsScopeSnapshotLike): boolean {\n return a.status === b.status\n && a.revision === b.revision\n && a.writable === b.writable\n && a.mode === b.mode\n && a.value === b.value\n && a.base === b.base\n && a.user === b.user\n}\n\n/** The load seam a direct channel exposes (the official scope refreshes itself). */\ntype LoadableScope = SettingsScopeLike & { load?: () => Promise<void> }\n\n/** Ask a channel for its first/next read; a refusal surfaces as a snapshot, never a throw. */\nfunction loadSource(source: SettingsScopeLike | undefined): Promise<void> {\n const loadable = source as LoadableScope | undefined\n if (loadable === undefined || typeof loadable.load !== 'function') return Promise.resolve()\n return Promise.resolve(loadable.load()).then(() => undefined, () => undefined)\n}\n\nexport function createSettingsChannel(options: SettingsChannelOptions): SettingsChannel {\n let official: SettingsScopeLike | undefined\n let direct: SettingsScopeLike | undefined\n let active: SettingsScopeLike | undefined\n let snapshot: SettingsScopeSnapshotLike = LOADING_SNAPSHOT\n let disposed = false\n const disposers: Array<() => void> = []\n const listeners = new Set<() => void>()\n\n const notify = (): void => {\n for (const listener of [...listeners]) {\n try {\n listener()\n } catch {\n // A broken listener must never break the channel.\n }\n }\n }\n\n const publish = (next: SettingsScopeSnapshotLike): void => {\n if (disposed || sameSnapshot(next, snapshot)) return\n snapshot = next\n notify()\n }\n\n const select = (): SettingsScopeLike | undefined => {\n // The official scope is authoritative unless it reports the documented\n // non-loopback degradation.\n if (official !== undefined && official.getSnapshot().status !== 'unavailable') return official\n if (direct === undefined) {\n try {\n direct = options.openDirect()\n } catch {\n direct = undefined\n }\n if (direct !== undefined) {\n try {\n disposers.push(direct.subscribe(() => {\n if (active === direct) publish(direct!.getSnapshot())\n }))\n } catch {\n // An unsubscribable direct channel is still readable synchronously.\n }\n // The direct channel has no owner to `ensure()` it (the official scope is\n // ensured by its own plugin), so the initial read is issued here.\n void loadSource(direct)\n }\n }\n return direct ?? official\n }\n\n const reselect = (): void => {\n if (disposed) return\n const next = select()\n if (next !== active) {\n active = next\n publish(next?.getSnapshot() ?? LOADING_SNAPSHOT)\n return\n }\n // Same source: forward its current snapshot (a change must have come from it).\n publish(next?.getSnapshot() ?? LOADING_SNAPSHOT)\n }\n\n return {\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n set(field, value) {\n const source = active\n if (source === undefined) {\n return Promise.reject(new Error('dsh-update-status: settings channel is unavailable'))\n }\n return source.set(field, value)\n },\n setOfficial(scope) {\n if (disposed) return\n official = scope\n if (scope !== undefined) {\n try {\n disposers.push(scope.subscribe(() => {\n reselect()\n }))\n } catch {\n // Guarded read path: selection still happens synchronously below.\n }\n }\n reselect()\n },\n refresh: reselect,\n reload() {\n if (active === undefined || active === official) return\n void loadSource(active)\n },\n dispose() {\n disposed = true\n listeners.clear()\n for (const dispose of disposers.splice(0)) {\n try {\n dispose()\n } catch {\n // Contained.\n }\n }\n const disposable = active as (SettingsScopeLike & { dispose?: () => void }) | undefined\n if (disposable !== undefined && typeof disposable.dispose === 'function') {\n try {\n disposable.dispose()\n } catch {\n // Contained.\n }\n }\n },\n }\n}\n","/** Plugin-owned CSS only; no shell DOM selection or official SVG manipulation. */\n\nexport const UPDATE_STATUS_CSS = `\n.dus-brand-name{align-items:center;gap:6px;height:24px;max-width:100%;min-width:0;display:inline-flex}\n.dus-brand-deepseek{font-size:14px;font-weight:650;letter-spacing:-.015em;line-height:24px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n/* The chip is a NEUTRAL SECOND-LEVEL SURFACE, never a high-contrast pill. It used\n to be label-primary + label-primary-inverted, which is a near-white box with dark\n text on the dark shell — a white frame that swallows the amber halo and fights the\n warn colour. --dsw-alias-button-floating-hover is the one palette surface that is\n grey in BOTH themes (#f1f3f5 light / #353638 dark), matching the reference: a light\n grey chip on the light shell, a dark grey chip with white text on the dark one. The\n sibling button-floating-fill is pure white in light mode and changes nothing there.\n Text stays label-primary (#0f1115 light / #f9fafb dark), so it is never low-contrast.\n Every colour here is a theme token, so the chip follows whatever appearance DSH\n resolves — light, dark, or system-follows-the-OS — with no plugin-side detection,\n no media query and no per-theme branch to keep in sync. */\n.dus-badge{--dus-badge-surface:var(--dsw-alias-button-floating-hover,#f1f3f5);align-items:center;background:var(--dus-badge-surface);border:0;border-radius:4px;color:var(--dsw-alias-label-primary,#0f1115);cursor:pointer;display:inline-flex;flex:none;font-family:var(--ds-font-family-code,var(--dsw-font-family-mono,ui-monospace,SFMono-Regular,Menlo,monospace));font-size:10px;font-variant-numeric:tabular-nums;font-weight:600;gap:4px;height:16px;line-height:16px;max-width:140px;outline:none;padding:0 6px;touch-action:manipulation;user-select:none;white-space:nowrap}\n.dus-badge:focus-visible,.dus-action:focus-visible,.dus-close:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}\n/* Hover has to read as \"raised\" in BOTH themes. brightness() only ever brightens, and on\n the light shell it turns the grey chip pure white — the chip all but disappears. Mixing\n a little of the theme's own label colour into the surface darkens it in light mode and\n lightens it in dark mode, so one declaration stays correct in both. The brightness\n filter remains the fallback: an engine without color-mix drops only the second\n declaration, never the whole hover rule. */\n.dus-badge:hover{filter:brightness(1.08)}\n@supports (background:color-mix(in srgb,red 50%,transparent)){.dus-badge:hover{background:color-mix(in srgb,var(--dsw-alias-label-primary) 8%,var(--dus-badge-surface));filter:none}}\n/* The failure state is the one deliberate repaint, and it follows the shell's own\n badge pattern (brokenBadge): error fill with a bg-layer-3 label, which is white\n text on the dark red of the light theme and dark text on the light red of the dark\n theme — readable in both, unlike the fixed white text it replaces. */\n.dus-badge[data-error=true]{background:var(--dsw-alias-state-error-primary,#dc2626);color:var(--dsw-alias-bg-layer-3,#fff)}\n.dus-badge-version{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dus-dot{background:currentColor;border-radius:50%;display:inline-block;flex:0 0 auto;height:5px;opacity:.9;width:5px}\n/* An available update repaints NOTHING: the chip keeps its normal fill and only\n the amber dot breathes. The halo is animated with the dot so the glow grows\n and shrinks as one light, never as a separate ripple. The update state is\n declared AFTER the loading state, so a re-check on a known update keeps the\n amber glow instead of falling back to the neutral loading pulse.\n Each box-shadow is declared twice: a literal amber first for any engine\n without color-mix, then the halo mixed from the theme's own warn colour so the\n glow always matches the dot in both light and dark themes. */\n/* The dot carries the whole status. Three states, in precedence order:\n up to date = a plain green circle (success token, never the text colour, so a\n dark theme no longer paints a black dot that reads as \"off\"); checking = the\n neutral grey pulse; a pending update = the amber dot below, which is the only\n state allowed to breathe or glow.\n The up-to-date rule is declared BEFORE loading and update: equal specificity, so\n the later, louder states win if two ever land on the same dot. */\n.dus-dot[data-state=current]{background:var(--dsw-alias-state-success-primary,#22c55e);opacity:1}\n.dus-dot[data-loading=true]{animation:dus-pulse .9s ease-in-out infinite}\n.dus-dot[data-update=true]{--dus-update-color:var(--dsw-alias-state-warn-primary,#f59e0b);animation:dus-update-pulse 1.6s ease-in-out infinite;background:var(--dus-update-color);height:6px;opacity:1;width:6px}\n@keyframes dus-pulse{0%,100%{opacity:.45;transform:scale(.82)}50%{opacity:1;transform:scale(1.15)}}\n@keyframes dus-update-pulse{0%,100%{box-shadow:0 0 2px 0 rgba(245,158,11,.4);box-shadow:0 0 2px 0 color-mix(in srgb,var(--dus-update-color) 45%,transparent);transform:scale(.8)}50%{box-shadow:0 0 7px 2px rgba(245,158,11,.72);box-shadow:0 0 7px 2px color-mix(in srgb,var(--dus-update-color) 72%,transparent);transform:scale(1.15)}}\n@media (prefers-reduced-motion:reduce){.dus-dot[data-update=true],.dus-dot[data-loading=true]{animation:none}.dus-dot[data-update=true]{box-shadow:0 0 5px 1px rgba(245,158,11,.6);box-shadow:0 0 5px 1px color-mix(in srgb,var(--dus-update-color) 62%,transparent)}}\n.dus-overlay-root{background:transparent;border:0;color:inherit;height:100dvh;inset:0;margin:0;max-height:none;max-width:none;padding:0;pointer-events:none;position:fixed;width:100vw}\n.dus-overlay-root::backdrop{background:transparent}\n.dus-backdrop{background:transparent;inset:0;pointer-events:auto;position:absolute}\n.dus-panel{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:14px;box-shadow:var(--dsw-elevation-panel);box-sizing:border-box;color:var(--dsw-alias-label-primary);left:min(292px,calc(100vw - 402px));max-height:min(640px,calc(100dvh - 32px - env(safe-area-inset-top) - env(safe-area-inset-bottom)));overflow:auto;padding:14px;pointer-events:auto;position:absolute;top:12px;width:min(390px,calc(100vw - 24px));z-index:1}\n.dus-panel-head{align-items:center;display:flex;gap:8px;justify-content:space-between;margin-bottom:12px}\n.dus-panel-title{font-size:14px;font-weight:650}\n.dus-close{align-items:center;background:transparent;border:0;border-radius:8px;color:inherit;cursor:pointer;display:inline-flex;font-size:20px;height:32px;justify-content:center;line-height:1;min-height:32px;min-width:32px;padding:0;touch-action:manipulation;width:32px}\n.dus-close:hover,.dus-action:hover{background:var(--dsw-alias-button-floating-hover)}\n.dus-cache-setting{border:1px solid var(--dsw-alias-border-l2);border-radius:9px;margin:0 0 12px;padding:9px}\n.dus-cache-field{align-items:center;display:flex;font-size:12px;font-weight:600;gap:10px;justify-content:space-between}\n.dus-cache-input-wrap{align-items:center;display:flex;gap:6px;font-size:11px;font-weight:400}\n.dus-cache-input{background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:7px;color:inherit;font:inherit;min-height:30px;padding:0 7px;text-align:right;width:72px}\n.dus-cache-input:focus{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}\n.dus-cache-input:disabled{cursor:not-allowed;opacity:.55}\n.dus-cache-hint{color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1.4;margin:7px 0 0}\n.dus-state{border-radius:9px;color:var(--dsw-alias-label-primary);font-size:12px;line-height:1.45;margin:0 0 12px;padding:8px 9px}\n.dus-state[data-kind=update],.dus-state[data-kind=warning]{background:var(--dsw-alias-state-warn-tertiary)}\n.dus-state[data-kind=ok]{background:var(--dsw-alias-state-success-tertiary)}\n.dus-state[data-kind=error]{background:var(--dsw-alias-bg-layer-3)}\n.dus-metadata{display:grid;gap:9px;margin:0 0 12px}\n.dus-metadata-row{align-items:baseline;display:grid;gap:8px;grid-template-columns:minmax(0,1fr) auto}\n.dus-meta-label{color:var(--dsw-alias-label-secondary);font-size:12px}\n.dus-meta-value{font-family:var(--dsw-font-family-mono,ui-monospace,SFMono-Regular,Menlo,monospace);font-size:12px;max-width:210px;overflow-wrap:anywhere;text-align:right}\n.dus-meta-sub{color:var(--dsw-alias-label-secondary);font-size:11px;grid-column:1 / -1}\n.dus-warning{border-left:2px solid var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.45;margin:0 0 12px;padding-left:8px}\n.dus-actions{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 12px}\n.dus-action{background:var(--dsw-alias-button-floating-fill);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;color:inherit;cursor:pointer;font-size:12px;line-height:30px;min-height:32px;padding:0 10px;touch-action:manipulation}\n.dus-action:disabled{cursor:wait;opacity:.65}\n.dus-restart{border-color:var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-primary)}\n.dus-restart-message{color:var(--dsw-alias-label-secondary);flex-basis:100%;font-size:11px;line-height:1.45;margin:0}\n.dus-restart-work{color:var(--dsw-alias-label-secondary);flex-basis:100%;font-size:11px;line-height:1.5;margin:0;overflow-wrap:anywhere;padding-left:18px}\n/* A job label is a whole shell command: clamp it so one long command cannot push\n the panel's buttons off a 390px card. */\n.dus-restart-work li{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}\n.dus-command-label{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600;margin:0 0 6px}\n.dus-command{background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;display:block;font-family:var(--dsw-font-family-mono,ui-monospace,SFMono-Regular,Menlo,monospace);font-size:11px;line-height:1.5;margin:0;overflow-wrap:anywhere;padding:9px;tab-size:2;user-select:text;white-space:pre-wrap}\n.dus-command:focus{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}\n.dus-note{color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1.45;margin:8px 0 0}\n.dus-copy-message{color:var(--dsw-alias-state-business-primary);font-size:11px;margin:7px 0 0}\n.dus-settings{display:grid;gap:14px;max-width:640px;padding:4px 0}\n.dus-settings-heading{font-size:16px;font-weight:650;margin:0}\n.dus-settings-card{border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:12px}\n.dus-settings-toggle{align-items:flex-start;cursor:pointer;display:flex;gap:10px;font-size:13px;line-height:1.4}\n.dus-settings-toggle input{accent-color:var(--dsw-alias-state-business-primary);height:18px;margin:0;min-height:18px;min-width:18px;width:18px}\n.dus-settings-hint{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.45;margin:8px 0 0}\n.dus-compat{color:var(--dsw-alias-label-secondary)}\n.dus-compat[data-compatibility=verified]{color:var(--dsw-alias-state-success-primary)}\n.dus-compat[data-compatibility=incompatible]{color:var(--dsw-alias-state-error-primary)}\n.dus-command-actions{align-items:center;margin:8px 0 0}\n@media (max-width:640px),(hover:none) and (pointer:coarse){.dus-panel{border-bottom:0;border-bottom-left-radius:0;border-bottom-right-radius:0;bottom:0;left:0;right:0;max-height:min(78dvh,calc(100dvh - env(safe-area-inset-top)));padding:16px max(16px,env(safe-area-inset-right)) max(16px,env(safe-area-inset-bottom)) max(16px,env(safe-area-inset-left));position:fixed;top:auto;width:100vw}.dus-action{min-height:40px;line-height:38px}.dus-close{height:40px;min-height:40px;min-width:40px;width:40px}}\n`\n","/**\n * DSH Web client half. It shadows only `sidebar.brand.name` with a compact\n * name + version chip that fits the 24px brand row and leaves the official fish\n * (`sidebar.brand.mark`) untouched. The detail panel is an additive\n * `shell.overlay`, never a chat-area floating widget.\n *\n * Deliberately NO `sidebar.footer.action` entry. DSH renders that list in one\n * row directly above `sidebar.settings`, and the collapsed rail's content box is\n * only 36px wide (the 56px column minus its 10px inline padding) — room for the\n * single official action. A second registrant makes that shared, centred row\n * overflow: on a phone it pushed the neighbouring plugin's entry off the screen\n * edge and parked our own dot on the rail's border. The chip in the expanded\n * brand row is the entry, so a collapsed sidebar carries none.\n *\n * The status read and the detail panel are NOT restricted to a loopback page.\n * DSH disables Host settings *persistence* on a non-loopback page, but the\n * Connection RPC stays authenticated and reachable there — see\n * settings/settingsChannel.ts for the one place the loopback distinction still\n * matters.\n *\n * Preferences live in the Host-side `dsh-update-status` settings entry — the\n * plugin's own Loader entry, whose volatile `Config` fields DSH 0.1.7 projects\n * as that entry's form. Two client channels can serve it: the official\n * `ctx.configForms.get(entryId)` form (the successor of the removed\n * `settingsScope` service) and, on the non-loopback pages where that form is\n * deliberately inert, a direct Host channel over `remote.settings`. See\n * settingsChannel.ts.\n */\n\nimport type { ClientContext, ConnectionClient } from './contract.ts'\nimport { BrandName, type SharedUi, UpdatePanel, UpdateSettings } from './components.tsx'\nimport { SETTINGS_NAMESPACE, PanelStore, PreferencesStore, RestartStore, StatusStore } from './stores.ts'\nimport { configFormScope, configFormsOf } from './settings/configFormScope.ts'\nimport {\n createHostDirectScope,\n settingsInvalidationsOf,\n settingsRemoteFace,\n settingsRemoteOf,\n type SettingsRemoteLike,\n} from './settings/hostDirectScope.ts'\nimport { createSettingsChannel } from './settings/settingsChannel.ts'\nimport { t } from './i18n.ts'\nimport { UPDATE_STATUS_CSS } from './styles.ts'\n\nconst PLUGIN_ID = 'dsh-update-status'\nconst CSS_TAG_ID = `${PLUGIN_ID}/styles`\n\nfunction installStyles(): () => void {\n if (typeof document === 'undefined') return () => {}\n const existing = document.querySelector(`style[data-plugin-css=\"${CSS_TAG_ID}\"]`)\n if (existing !== null) return () => {}\n const tag = document.createElement('style')\n tag.dataset.plugin = PLUGIN_ID\n tag.dataset.pluginCss = CSS_TAG_ID\n tag.textContent = UPDATE_STATUS_CSS\n document.head.appendChild(tag)\n return () => {\n const live = document.querySelector(`style[data-plugin-css=\"${CSS_TAG_ID}\"]`)\n if (live?.parentNode !== undefined && live?.parentNode !== null) live.parentNode.removeChild(live)\n }\n}\n\nfunction connectionOf(ctx: ClientContext): ConnectionClient {\n try {\n const candidate = ctx.get('connection')\n return candidate !== null && typeof candidate === 'object' ? candidate as ConnectionClient : {}\n } catch {\n return {}\n }\n}\n\nfunction apply(ctx: ClientContext): void {\n const connection = connectionOf(ctx)\n const status = new StatusStore(connection)\n const preferences = new PreferencesStore()\n const panel = new PanelStore()\n const restart = new RestartStore(connection)\n const ui: SharedUi = { status, preferences, panel, restart }\n\n const disposers: Array<() => void> = []\n let stopped = false\n /** Own a subscription raised from an injection callback; release after teardown at once. */\n const own = (dispose: unknown): void => {\n if (typeof dispose !== 'function') return\n if (stopped) {\n try {\n (dispose as () => void)()\n } catch {\n // Contained.\n }\n return\n }\n disposers.push(dispose as () => void)\n }\n\n // The plugin's ONE storage contract is the Host settings entry\n // `dsh-update-status` (the plugin's own Loader entry id), but two client\n // channels can serve it: the official `configForms` form (loopback pages) and\n // — only when that form reports the documented non-loopback degradation — a\n // direct Host channel over the same public Remote. A LAN page is exactly that\n // non-loopback case; without the direct channel every preference silently\n // falls back to its default there. See settingsChannel.ts.\n let remoteSettings: SettingsRemoteLike | undefined\n /** Resolve the direct channel's Remote face; `ctx.get` covers a payload we could not read. */\n const directRemote = (): SettingsRemoteLike | undefined => {\n if (remoteSettings !== undefined) return remoteSettings\n try {\n remoteSettings = settingsRemoteFace(ctx.get('remote.settings'))\n } catch {\n // The dotted service key is not readable on this context.\n }\n return remoteSettings\n }\n const channel = createSettingsChannel({\n openDirect: () => {\n const remote = directRemote()\n return remote === undefined ? undefined : createHostDirectScope(remote, SETTINGS_NAMESPACE)\n },\n })\n\n ctx.effect(() => {\n const disposeStyles = installStyles()\n const detach = preferences.attach(channel)\n // The mount read uses the cache policy the profile stored. The settings\n // document usually arrives one tick after the effect, so the first\n // authoritative value issues that read; later edits deliberately do not —\n // changing the duration never starts a request of its own. A profile that\n // stores the default policy costs no second read.\n let usedCacheTtl = preferences.getSnapshot().cacheTtlMinutes\n let storedPolicyApplied = false\n const applyStoredPolicyOnce = () => {\n if (storedPolicyApplied) return\n const snapshot = preferences.getSnapshot()\n if (snapshot.status === 'loading') return\n storedPolicyApplied = true\n if (snapshot.cacheTtlMinutes === usedCacheTtl) return\n usedCacheTtl = snapshot.cacheTtlMinutes\n void status.reload(snapshot.cacheTtlMinutes)\n }\n applyStoredPolicyOnce()\n const unsubscribe = preferences.subscribe(applyStoredPolicyOnce)\n // Every mount reads the Host's cached status, whichever page it is on; the\n // transport is authenticated and the Host route is the plugin's own. There is\n // no channel to project: the Host always answers with the newest release.\n void status.load(preferences.getSnapshot().cacheTtlMinutes)\n return () => {\n stopped = true\n unsubscribe()\n detach()\n for (const dispose of disposers.splice(0)) {\n try {\n dispose()\n } catch {\n // Contained.\n }\n }\n channel.dispose()\n restart.stop()\n status.stop()\n disposeStyles()\n }\n }, 'dsh-update-status: styles, settings channel and first status read')\n\n // Official seam first: it stays authoritative whenever it is not `unavailable`,\n // so a loopback page keeps the official semantics (one shared describe mirror,\n // the official write queue) and pays no extra wire read. DSH 0.1.7 replaced\n // the per-namespace `settingsScope` service with these entry-addressed forms,\n // keyed by the Loader entry id the Host Config half owns (SETTINGS_NAMESPACE).\n try {\n ctx.inject(['configForms'], (raw: unknown) => {\n try {\n const forms = configFormsOf(raw)\n if (forms === undefined) return\n channel.setOfficial(configFormScope(forms.get<unknown>(SETTINGS_NAMESPACE)))\n } catch {\n // Unreadable settings seam: the preferences stay on their defaults.\n }\n })\n } catch {\n // No configForms seam on this host: the direct channel is the only hope.\n }\n\n // Direct Host channel. The Remote service can arrive before or after the\n // official scope, so `refresh()` re-evaluates the selection either way; the\n // document invalidation keeps this page in step with edits made elsewhere,\n // and a reconnect retries a read that was refused.\n //\n // Every read here is contained: the injected payload intentionally refuses the\n // dotted PARENT (`payload.remote` throws \"cannot get property … without\n // inject\"), so one unreadable member must never abort the whole wiring.\n try {\n ctx.inject(['remote.settings'], (raw: unknown) => {\n try {\n remoteSettings = settingsRemoteOf(raw) ?? remoteSettings\n channel.refresh()\n const invalidations = settingsInvalidationsOf(ctx.get('remote'))\n if (invalidations !== undefined) own(invalidations(() => channel.reload()))\n } catch {\n // No usable Remote seam: the direct channel stays closed and the official\n // scope keeps its verdict.\n }\n })\n } catch {\n // No Remote seam: non-loopback pages keep the honest unavailable state.\n }\n if (typeof ctx.on === 'function') {\n try {\n own(ctx.on('connection/reset', () => { channel.reload(); void restart.refresh() }))\n } catch {\n // No lifecycle event seam on this host.\n }\n }\n\n // Single slot: a compact name + version chip shadows the official wordmark\n // (priority 0). Lowest priority renders; never touch sidebar.brand.mark.\n //\n // The registration itself is conditional, and that is not a detail: a single\n // slot arbitrates by PRIORITY, not by what the component renders. A component\n // that returns null still OCCUPIES the cell, so the official occupant\n // (@deepseek-ai/dsh-client-ui-brand-official) and the shell's own fallback can\n // never come back. Turning the preference off therefore disposes our entry —\n // the previous winner leaves the ledger and the next-lowest occupant renders\n // again — and turning it back on registers a fresh one.\n ctx.slots.inject('sidebar.brand.name', () => {\n let registration: (() => void) | undefined\n const syncRegistration = (): void => {\n const enabled = preferences.getSnapshot().sidebarEnabled\n if (enabled && registration === undefined) {\n registration = ctx.slots.register({ name: 'sidebar.brand.name', priority: -10 }, () => BrandName({ ui }))\n } else if (!enabled && registration !== undefined) {\n registration()\n registration = undefined\n }\n }\n syncRegistration()\n const unsubscribe = preferences.subscribe(syncRegistration)\n return () => {\n unsubscribe()\n registration?.()\n registration = undefined\n }\n })\n\n // additive frame overlay for click/tap panel and narrow-view bottom sheet.\n ctx.slots.inject('shell.overlay', () => ctx.slots.register(\n { name: 'shell.overlay', id: 'dsh-update-status', order: 40 },\n () => UpdatePanel({ ui }),\n ))\n\n // Independent settings section provides the required way to hide our entry.\n ctx.slots.inject('settings.section', () => ctx.slots.register(\n { name: 'settings.section', id: 'dsh-update-status', order: 80, label: () => t('settings.title') },\n () => UpdateSettings({ ui }),\n ))\n}\n\nmodule.exports = {\n name: PLUGIN_ID,\n inject: ['slots', 'connection'],\n apply,\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-update-status",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "DeepSeek Harness Web plugin for version status, copy-only upgrade guidance, and safe restart when an explicit user-level supervisor is installed.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -32,7 +32,8 @@
32
32
  "assets/update-panel.zh.png",
33
33
  "assets/sidebar-chip.png",
34
34
  "docs/RELEASING.md",
35
- "docs/service-supervision.md"
35
+ "docs/service-supervision.md",
36
+ "docs/platform-acceptance.md"
36
37
  ],
37
38
  "engines": {
38
39
  "node": ">=20"