dsh-shutdown-guard 0.0.0-stage → 1.5.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 +138 -0
- package/LICENSE +21 -0
- package/README.en.md +263 -0
- package/README.md +263 -3
- package/README.ru.md +263 -0
- package/cordis.patch.yml +23 -0
- package/lib/client.js +1190 -0
- package/lib/i18n.mjs +425 -0
- package/lib/index.mjs +1078 -0
- package/package.json +70 -6
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v1.5.1 — 2026-11-04
|
|
4
|
+
|
|
5
|
+
- 三语界面:中文 / English / Русский。宿主侧文案目录 78 键、侧边栏卡片 74 键,三语键集齐平、零缺失;日志与提示全部改走 `t(lang, key)`,`lib/index.mjs` 内已无硬编码中文串。
|
|
6
|
+
- 新增语言选择器:设置 → 插件 → 任务完成后关机 → 「语言」,切换即时生效——经 `POST /api/dsh-shutdown-guard/config` 的 `language` 字段落盘,卡片强制重绘,不需要重启 DSH。
|
|
7
|
+
- 新增 `language` 配置项:取值 `auto` | `zh` | `en` | `ru`,默认 `auto`(跟随系统语言)。它由 `loadStore`(`lib/index.mjs:283`)读取、经 `POST /config` 在运行时写入;**不是** `normalizeProfile` 字段,三份 README 的配置表已单独为它补一行。
|
|
8
|
+
- 语言偏好同时写入宿主配置(`data/config.json`)与浏览器 `localStorage`:前者是权威值,后者让卡片在首帧就按所选语言渲染,重启后不再闪一下中文。
|
|
9
|
+
- 文档:新增 `README.en.md`、`README.ru.md`,三份 README 的配置表统一补 `language` 行;演示页改成三语(`scripts/demo/i18n.js`,157 键)。
|
|
10
|
+
- 修复:切换语言后,事件日志里切换前的旧行会永久停留在旧语言;现在切换时整环重取(`lib/client.js` `setSnapshot`)。
|
|
11
|
+
- 修复:`saveStore()` 在 `apply()` 外引用越界变量,写 `data/config.json` 失败时会抛 `ReferenceError`、`log.configWriteFailed` 提示永远打不出来(`lib/index.mjs:292-295`、`:1039`)。
|
|
12
|
+
- 可访问性:设置里 5 个开关补 `aria-label`,屏幕阅读器可读出开关名称(未新增文案键)。
|
|
13
|
+
- 测试:新增 i18n 目录断言——`LANGS` 固定为三语、`catalogCoverage().missing` 为空、每语键数与基准一致。
|
|
14
|
+
- ⚠️ 升级注意:**已有配置无需任何改动或迁移**。`language` 缺省即 `auto`,与 v1.5.0 的行为一致;升级后若想固定语言,在设置里选一次即可。
|
|
15
|
+
|
|
16
|
+
## v1.5.0 — 2026-11-02
|
|
17
|
+
|
|
18
|
+
- 改名:`dsh-shutdown-panel` → **`dsh-shutdown-guard`**(Shutdown Guard)。原名只描述了界面,没表达它真正的特长——关机前逐条排除。`shutdown` 仍在包名第二位,市场按关键词搜索时排序不受影响。
|
|
19
|
+
- 同步改名:HTTP 前缀 `/api/dsh-shutdown-guard`、`localStorage` 键前缀、`<style data-dsh-shutdown-guard>`、客户端 bundle id、`cordis.patch.yml` 的行 id。
|
|
20
|
+
- ⚠️ 升级注意:旧版写在浏览器 `localStorage` 里的外观偏好(`dsh-shutdown-panel.variant` 等)不会迁移,升级后回到默认 A / 跟随主题 / 不折叠。如需保留,手动在设置里重选一次。
|
|
21
|
+
|
|
22
|
+
## v1.4.2 — 2026-11-02
|
|
23
|
+
|
|
24
|
+
- 文档补齐:README 按 v1.4 重写(截图、功能矩阵、重试策略、FAQ),新增 CONTRIBUTING.md、LICENSE。
|
|
25
|
+
- 修复 README 早前被 PowerShell 写坏的问题( ransient 被吞成
|
|
26
|
+
- 新增 est/(服务端 27 项断言 + 客户端 bundle smoke,路径改为相对插件根)与 scripts/(从真实 CSS 生成图库、headless Edge 截图)。
|
|
27
|
+
|
|
28
|
+
## v1.4.1 — 2026-11-02
|
|
29
|
+
|
|
30
|
+
- 重试节奏改为按故障类型分别配置:
|
|
31
|
+
- 默认分配:RATE_LIMIT →
|
|
32
|
+
- WAITING_CODES 增至 6 项,补 TIMEOUT 与 IMAGE_OFFLOAD_REQUIRED。
|
|
33
|
+
- 修 bug:setArmed() 只清 locked、没清
|
|
34
|
+
|
|
35
|
+
## v1.4.0 — 2026-11-02
|
|
36
|
+
|
|
37
|
+
- 新增阻断态(blocked),插件不因此解除武装:卡片转色、状态行写原因,三种外观都有对应暂停外观。
|
|
38
|
+
- halted(灰蓝):额度、凭证、上下文超限、能力不支持、磁盘写满 —— 只有人能处理,按钮变「解除并重试」。
|
|
39
|
+
- waiting(灰绿、圆点呼吸):限流、5xx、网络、空响应 —— 到点自动重试,并向会话注入「继续原计划」指令。
|
|
40
|
+
- /state 新增 locked、
|
|
41
|
+
- 设计文档 docs/blocked-states.md(分级语义、错误码表、重试节奏分析、stuck 归档理由)。
|
|
42
|
+
|
|
43
|
+
## v1.3.9 — 2026-11-02
|
|
44
|
+
|
|
45
|
+
- 修复:在分支对话里手动终止任务仍会触发关机。取消和正常结束都表现为 gent/status = idle,插件此前只看到后者。
|
|
46
|
+
- 新增 session/event 监听(宿主签名 (session, event)),按两条独立信号识别中断: urn/end 带取消原因;或本轮从 urn/start 到 urn/end 没追加过任何 ssistant/message。
|
|
47
|
+
- 命中中断时不武装,并记「已检测到用户中断,本轮不关机」。
|
|
48
|
+
|
|
49
|
+
## v1.3.8 — 2026-11-02
|
|
50
|
+
|
|
51
|
+
- 尝试用 AbortError 拦截用户中断。事后证明打不中 —— 终止不走 gent/error,被 v1.3.9 取代。
|
|
52
|
+
|
|
53
|
+
## v1.3.7 — 2026-11-02
|
|
54
|
+
|
|
55
|
+
- 引入 HALTED_CODES:额度、凭证、上下文超限等不可恢复错误不再当成普通失败处理(否则会在用户不在场时照常关机),改为置 locked,并在 ryArm() 最前面拦截。
|
|
56
|
+
|
|
57
|
+
## v1.3.6 — 2026-11-02
|
|
58
|
+
|
|
59
|
+
- 新语义:当「有会话报错就跳过」关闭、且 AFK 自动回答开启时,出错批次不再永久阻止关机。第 1 次报错让 agent 排查,第 2 次下「收尾」指令;收尾那一轮结束后该会话的错误不再计入拦截条件。
|
|
60
|
+
- 仅在「报错就跳过」关闭时生效;该开关打开时出错的批次照旧整轮跳过。
|
|
61
|
+
- 重试计数与收尾标记都在武装/解除时清零。
|
|
62
|
+
## v1.3.5 — 2026-11-02
|
|
63
|
+
|
|
64
|
+
- 新增调试钩子 POST /api/dsh-shutdown-guard/error-test,仅在 cordis.patch.yml 里设置 debugHooks: true 时注册。它用第一个 root 会话走一遍真实的 AFK 报错恢复链路(注入 + 日志 + 不关机),便于在没有真实崩溃时演练 T4。
|
|
65
|
+
- 错误处理逻辑抽成 handleAgentError(agent),事件监听与调试端点共用同一条代码路径。
|
|
66
|
+
|
|
67
|
+
## v1.3.4 — 2026-11-02
|
|
68
|
+
|
|
69
|
+
- AFK 错误恢复:武装且 AFK 开关打开时,root 会话报错不再直接跳过/关机,而是向该会话注入一条恢复指令——先定位并修复报错、按原计划继续;同一会话第二次报错则要求它停止重试、把结果与失败原因写成报告落盘后收尾;第三次不再注入。
|
|
70
|
+
- 注入走 `agent.followup(message)`(等价于追加一轮用户消息),失败回落 `agent.steer`,都不可用则只记日志、不触发关机。
|
|
71
|
+
- 重试次数只在武装/解除时清空,避免「注入 → 重跑 → 再报错」无限循环。
|
|
72
|
+
- 另加一层保险:AFK 开关打开时,只要本批次存在报错会话就不武装关机(即使「有会话报错就跳过」是关的),等恢复成功、报错集合清空后再走正常流程。
|
|
73
|
+
|
|
74
|
+
## v1.2.0 – v1.3.3(补记)
|
|
75
|
+
|
|
76
|
+
- 事件与 state 跟随插件:data/events.log、data/config.json,1 个默认档 + 8 个自定义档。
|
|
77
|
+
- 设置拆成三组:外观 / 何时关机 / 状态,含事件日志。
|
|
78
|
+
- 默认配置倒计时锁 60 秒(countdownLocked),自定义档可调。
|
|
79
|
+
- AFK 自动回答:武装时替用户回答提问(user-questions/request,prepend 抢在 Web 回答者之前)。
|
|
80
|
+
- 「AFK 自动回答」行下移到「倒计时时长」之后。
|
|
81
|
+
## v1.1.1 — 2026-11-02
|
|
82
|
+
|
|
83
|
+
- 设置页按 Claude Style 的行式布局重做:每行 14px 标题 + 12px 说明,行间 0.5px 分隔线,上下 14px 留白。
|
|
84
|
+
- 控件放大:分段按钮 30px 高 / 13px 字,开关 36×20,滑块 16px。
|
|
85
|
+
- 卡片字号同步放大:标题 12.5px、说明 11.5px、按钮 30px 高 / 12.5px、主数字 26px。
|
|
86
|
+
|
|
87
|
+
## v1.1.0 — 2026-11-02
|
|
88
|
+
|
|
89
|
+
- 折叠态上下内边距 10→6px(高 42→34px)。
|
|
90
|
+
- 警戒带 115°→45°,滚动位移改 28.28px。
|
|
91
|
+
- 设置页补齐外观/配色/折叠/判定条件/跳过原因/C 模式说明。
|
|
92
|
+
- 新增 POST /config(onlyOnSuccess / blockOnJobs),/state 同步返回。
|
|
93
|
+
- 修「设置不跟手」:切换后立即重绘,不再等 1s 轮询。
|
|
94
|
+
|
|
95
|
+
## v1.0.3 — 2026-11-02
|
|
96
|
+
|
|
97
|
+
- 警戒带接缝修好:改为单条 2 倍宽元素(`::before`)承载 `repeating-linear-gradient(115deg)`,用 `translateX(-22.07px)` 平移;周期 20/sin115°≈22.07px,不再按 34px 平铺,斜线连续。
|
|
98
|
+
- 卡片颜色不再受宿主主题干预:改用自有调色板 `--dsp-*`(bg/fg/muted/line/accent/danger/track/tape-a/tape-b),只在运行时读取宿主表面亮度(probe `var(--dsw-alias-bg-layer-1)`)决定 `data-lum="light|dark"`,浅色用另一套值;字体仍随宿主。
|
|
99
|
+
- 终端屏幕改为固定深色(`#0b0d10` / `#9ef0a8`),不再受主题 token 影响;设置分区保留宿主 token(它位于设置页)。
|
|
100
|
+
|
|
101
|
+
## v1.0.2 — 2026-11-02
|
|
102
|
+
|
|
103
|
+
- 警戒带改用固定琥珀 #f0a83a + 主题错误红:此前用 --dsw-alias-button-primary-fill,而当前主题的主按钮填充是近白色,条带成了白红相间。
|
|
104
|
+
- 卡片与「技能中心」按钮的点击判定区域对齐:host padding:0 12px 8px 0、卡片内边距 8px,实测卡片与面板按钮同为 x=12、宽 256。
|
|
105
|
+
- 未开启状态警戒带保持静止但保留 60% 不透明度,不再灰成一片。
|
|
106
|
+
|
|
107
|
+
## v1.0.1 — 2026-11-02
|
|
108
|
+
|
|
109
|
+
修复重启后出现的三个问题。
|
|
110
|
+
|
|
111
|
+
### 修复
|
|
112
|
+
- **会话列表消失**:卡片原先注册在 `sidebar.workspaces`,而该槽是 `single` 槽、已被会话浏览器占用;同优先级注册冲突导致会话浏览器整块不再渲染。卡片改为在侧边栏 `regionArea` 顶部插入独立 DOM 节点(React 不管理它),不再占用任何槽位,会话列表恢复正常。
|
|
113
|
+
- **卡片左缘未与「技能中心」对齐**:容器改为 `padding:6px 2px 8px`、卡片 `width:100%;margin:0`,左缘与面板行(`margin:0 2px`)对齐到同一 x。
|
|
114
|
+
- **警戒带图案/颜色异常**:去掉 `color-mix(... transparent)`,改为实色 `--dsw-alias-button-primary-fill` 与 `--dsw-alias-state-error-primary`,条带高度 10px、`background-size:34px 100%`,保持自右向左滚动。
|
|
115
|
+
|
|
116
|
+
### 变更
|
|
117
|
+
- 卡片改为纯 DOM 渲染 + 每秒对齐重挂载(侧边栏折叠/重建后会自动插回),只有设置分区仍用 React。
|
|
118
|
+
- 旧插件 `dsh-shutdown-after-task` 已从 profile 依赖、`pnpm.overrides`、`generationProjection`、`bundles`、`desired.json`、`node_modules` 软链与 `.generations/live` 目录中移除。
|
|
119
|
+
|
|
120
|
+
## v1.0.0 — 2026-11-02
|
|
121
|
+
|
|
122
|
+
侧边栏卡片版。
|
|
123
|
+
|
|
124
|
+
- 卡片位于「技能中心」下方、会话列表上方。
|
|
125
|
+
- 三种外观:A 警戒带 / B 雷达 / C 终端,可在 设置 → 插件 → 任务完成后关机 中切换。
|
|
126
|
+
- 纵向折叠:只留标题与状态标签,点击标题展开;可设为默认折叠。
|
|
127
|
+
- 状态机可视化:未开启 / 待命 / 等待后台任务 / 倒计时 / 已关机。
|
|
128
|
+
- 浅色、深色与第三方主题(如 Claude Style)自适应,颜色统一走 `--dsw-alias-*` token。
|
|
129
|
+
- 后台任务守卫(v0.1.1 引入)保留:有运行中的后台任务时不触发;倒计时中出现新任务会 `shutdown /a` 并保持武装。
|
|
130
|
+
- 外观与折叠状态写入 `localStorage`。
|
|
131
|
+
|
|
132
|
+
## v0.1.1 — 2026-10-24
|
|
133
|
+
|
|
134
|
+
- 新增 `ctx.jobs` 事件订阅:运行中的后台任务会阻止关机;取消倒计时后仍保持武装。
|
|
135
|
+
|
|
136
|
+
## v0.1.0 — 2026-10-21
|
|
137
|
+
|
|
138
|
+
- 首个可用版本:复用原插件关机逻辑,UI 为侧边栏底部一行 + `shell.overlay` 弹层。
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ltayshark0637
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# Shutdown Guard · Shut down when tasks finish
|
|
2
|
+
[中文](README.md) · **English** · [Русский](README.ru.md)
|
|
3
|
+
|
|
4
|
+
<p align="center"><img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/banner.png" width="880" alt="Shutdown Guard — automatic shutdown when tasks finish, a night-watch card in the sidebar"></p>
|
|
5
|
+
|
|
6
|
+
[](CHANGELOG.md)
|
|
7
|
+
[](https://ltayshark0637.github.io/dsh-Shutdown-Guard/demo.html)
|
|
8
|
+
[](https://github.com/deepseek-ai)
|
|
9
|
+
[](#constraints)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
Adds a "shut down automatically when all tasks finish" card to the DeepSeek Harness sidebar, with a more varied and polished visual treatment, decision logic, and customization. It sits below Skill Center and above the session list, and updates in real time with task state.
|
|
13
|
+
|
|
14
|
+
**▶ [Interactive demo](https://ltayshark0637.github.io/dsh-Shutdown-Guard/demo.html)** — no installation needed; see what it looks like and how it decides. Two panes: **Learn** (rules, appearance, states) and **Try** (a random scenario where you arm the guard, cancel it, and release it yourself, all the way to a shutdown or a block). All data is generated locally at random; nothing is sent over the network.
|
|
15
|
+
|
|
16
|
+
## Why you need it
|
|
17
|
+
|
|
18
|
+
People who run long tasks know this scene: you hand the agent a batch of work in the evening, want the machine to shut down when it is done, and you are not at the desk.
|
|
19
|
+
|
|
20
|
+
But "task finished" does not mean "safe to shut down" — background jobs may still be running, a session may have errored, the agent may be waiting for your answer, or you may have stopped it halfway. Shutting down in any of those cases is an incident. The plugin blocks each of these situations and shuts down only when it is genuinely safe.
|
|
21
|
+
|
|
22
|
+
## Rules: when it actually shuts down
|
|
23
|
+
|
|
24
|
+
In a blocked state (see below) nothing is sent at all; otherwise all five gates must pass before `shutdown /s` is issued:
|
|
25
|
+
|
|
26
|
+
1. **Armed** — you turned it on manually
|
|
27
|
+
2. **No countdown already running** — avoids duplicate issuance
|
|
28
|
+
3. **All root sessions idle** — no agent is still working
|
|
29
|
+
4. **No running or stopping background jobs** — `blockOnJobs`, on by default
|
|
30
|
+
5. **No session error in this batch** — `onlyOnSuccess`, on by default
|
|
31
|
+
|
|
32
|
+
If any one is missing, it does not shut down and writes the reason on the card and in the settings page.
|
|
33
|
+
|
|
34
|
+
## Three looks
|
|
35
|
+
|
|
36
|
+
| A · Hazard tape | B · Radar | C · Terminal |
|
|
37
|
+
|:----------------------------------------------------------------------:|:---------------------------------------------------------------:|:------------------------------------------------------------------------------------:|
|
|
38
|
+
| <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/02-armed.png" width="250"> | <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/06-variant-b-radar.png" width="250"> | <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/07-variant-c-terminal.png" width="250"> |
|
|
39
|
+
| Large status text + scrolling red-and-yellow stripes at the bottom | Concentric rings + a scanning beam, sessions and background jobs shown as dots | Command-line summary + six indicator lights for the gate conditions |
|
|
40
|
+
|
|
41
|
+
Dots in the radar are added and removed, never reordered: when the count changes, the dots you are looking at do not jump.
|
|
42
|
+
|
|
43
|
+
<p align="center"><img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/anim-radar.gif" width="330" alt="Radar sweep"></p>
|
|
44
|
+
|
|
45
|
+
Switch in settings; takes effect immediately, no restart needed.
|
|
46
|
+
|
|
47
|
+
## Six runtime states
|
|
48
|
+
|
|
49
|
+
| Off | Standby | Waiting on background jobs |
|
|
50
|
+
|:---------------------------------------------------:|:-----------------------------------------------------:|:----------------------------------------------------------------------:|
|
|
51
|
+
| <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/01-off.png" width="250"> | <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/02-armed.png" width="250"> | <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/03-hold.png" width="250"> |
|
|
52
|
+
|
|
53
|
+
| Countdown | Issued | Blocked |
|
|
54
|
+
|:----------------------------------------------------:|:-----------------------------------------------------------------------:|:------------------------------------------------------------------------:|
|
|
55
|
+
| <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/04-fire.png" width="250"> | <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/05-fired.png" width="250"> | <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/08-blocked-halted.png" width="250"> |
|
|
56
|
+
|
|
57
|
+
`Off` → `Standby` → `Waiting on background jobs` → `Countdown` → `Issued`; a blocking error at any stage moves it to `Blocked`. You can cancel at any point during the countdown; **cancelling leaves it armed** — the next batch will still shut down.
|
|
58
|
+
|
|
59
|
+
Want to flip through all six states? In section 04 of the demo's Learn pane, the three capsules `A` `B` `C` at the top right switch which look the preview uses.
|
|
60
|
+
|
|
61
|
+
## Blocked states
|
|
62
|
+
|
|
63
|
+
| halted · needs manual action | waiting · retries automatically |
|
|
64
|
+
|:--------------------------------------------------------------:|:---------------------------------------------------------------------------------:|
|
|
65
|
+
| <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/08-blocked-halted.png" width="250"> | <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/09-blocked-waiting.png" width="250"> |
|
|
66
|
+
|
|
67
|
+
For errors that cannot be handled as ordinary failures, the plugin **stays armed**, changes the card's colour, and writes out the reason:
|
|
68
|
+
|
|
69
|
+
- **`halted`** (slate blue) — insufficient quota, invalid credentials, context overrun, unsupported capability, disk full. Only a human can resolve it; click "disarm and retry".
|
|
70
|
+
- **`waiting`** (slate green, a breathing dot) — rate limiting, 5xx, network interruption, empty response, timeout, image offload. Retried automatically according to policy; at the right moment a "continue the original plan" instruction is injected.
|
|
71
|
+
|
|
72
|
+
<p align="center"><img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/anim-blocked.gif" width="380" alt="Waiting for recovery, the status dot breathing"></p>
|
|
73
|
+
|
|
74
|
+
Retry cadence is configured per failure type; see [Retry policies](#retry-policies).
|
|
75
|
+
|
|
76
|
+
## AFK auto-pilot
|
|
77
|
+
|
|
78
|
+
When nobody is at the desk, the plugin covers three things for you:
|
|
79
|
+
|
|
80
|
+
- **Auto-answer questions** (on by default): when the agent asks something, it replies "The user is AFK. Finish the tasks already assigned, write up a summary report to disk, and do not touch branches that were not assigned." It returns `selected: []` plus custom text, so **it never selects an option for you**; turn this off and questions come to you as usual.
|
|
81
|
+
- **Error recovery**: when a session errors, the agent is first asked to investigate and continue the original plan; a second failure in the same session switches to "stop retrying and write up a report".
|
|
82
|
+
- **Interruption protection**: if you stop a task yourself midway, that does not trigger a shutdown.
|
|
83
|
+
|
|
84
|
+
## Where it lives
|
|
85
|
+
|
|
86
|
+
| Armed: below Skill Center, above the session list | Countdown: same spot, the whole strip turns red |
|
|
87
|
+
|:-------------------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------------:|
|
|
88
|
+
| <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/10-sidebar-hold.png" width="250"> | <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/11-sidebar-fire.png" width="250"> |
|
|
89
|
+
|
|
90
|
+
The card is currently pinned to this sidebar slot. A future version plans to make it available as a standalone widget (with a switch in settings); this section will be updated then.
|
|
91
|
+
|
|
92
|
+
## Other details
|
|
93
|
+
|
|
94
|
+
- **Vertical collapse**: collapsed it keeps only the title and status; click the title to expand.
|
|
95
|
+
|
|
96
|
+
<img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/16-folded.png" width="250" align="right" alt="Collapsed state">
|
|
97
|
+
|
|
98
|
+
- **Theme adaptation**: the card carries its own `--dsp-*` palette and picks light or dark automatically from the host surface's brightness; you can also force dark / light / Claude Style.
|
|
99
|
+
|
|
100
|
+
- **Event log**: the settings page shows recent events and "why it did not shut down last time"; the full stream is in `data/events.log`.
|
|
101
|
+
|
|
102
|
+
<br clear="all">
|
|
103
|
+
|
|
104
|
+
## Settings page
|
|
105
|
+
|
|
106
|
+
| Dark | Light |
|
|
107
|
+
|:---------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------:|
|
|
108
|
+
| <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/12-settings-dark.png" width="380"> | <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/13-settings-light.png" width="380"> |
|
|
109
|
+
|
|
110
|
+
Three groups: Appearance / When to shut down / Status. Every switch takes effect immediately.
|
|
111
|
+
|
|
112
|
+
- **Default profile** is locked at 60 seconds and cannot be changed; there are 8 custom slots you can tune freely.
|
|
113
|
+
- "All sessions idle" is the floor condition, always on and not switchable.
|
|
114
|
+
- "Background jobs finished" blocks background jobs; "skip this round if any session errored" blocks erroring batches.
|
|
115
|
+
- "AFK auto-answer" controls whether questions are answered for you while armed; the description states "replies never select an option for you; turn this off and questions come to you as usual."
|
|
116
|
+
|
|
117
|
+
## Light and third-party themes
|
|
118
|
+
|
|
119
|
+
| Light card | Claude Style |
|
|
120
|
+
|:-----------------------------------------------------:|:------------------------------------------------------:|
|
|
121
|
+
| <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/14-light.png" width="250"> | <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/15-claude.png" width="250"> |
|
|
122
|
+
|
|
123
|
+
The card does not inherit the host palette; it brings its own, while the settings page follows the host theme. The third-party Claude Style theme has a matching palette too.
|
|
124
|
+
|
|
125
|
+
## Installation
|
|
126
|
+
|
|
127
|
+
Published on npm; recommended:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
dsh plugin --profile web add dsh-shutdown-guard
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Or install straight from GitHub:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
dsh plugin --profile web add github:ltayshark0637/dsh-Shutdown-Guard
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Then **restart DSH**.
|
|
140
|
+
|
|
141
|
+
> ⚠️ **The client bundle is only wired into the module graph at DSH startup**: after editing `lib/client.js` you must restart; refreshing the page does nothing. The same applies to the server side.
|
|
142
|
+
|
|
143
|
+
**On a restricted network**: the GitHub command is essentially `pnpm add github:...` and needs a clone. If cloning fails, prefer the npm command (it goes through the registry); if neither works, install from a local path — DSH Settings → Plugins → Add, enter the **absolute path** of this directory → Install → restart.
|
|
144
|
+
|
|
145
|
+
**Manual fallback**: create a symlink `node_modules\dsh-shutdown-guard` under `profiles\web` pointing at this directory, then:
|
|
146
|
+
|
|
147
|
+
- add `link:<absolute path of this directory>` to `dependencies` and `pnpm.overrides` in `profiles\web\package.json`
|
|
148
|
+
- append `dsh-shutdown-guard` to `dsh.profile.bundles`
|
|
149
|
+
|
|
150
|
+
## Permissions and impact
|
|
151
|
+
|
|
152
|
+
Once installed, the plugin does the following:
|
|
153
|
+
|
|
154
|
+
| Area | Concrete behaviour |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| **Command execution** | Calls Windows `shutdown.exe` through `subprocess`: `/s /t <seconds>` schedules the shutdown, `/a` cancels it. **Only invoked when armed and all five gates pass** |
|
|
157
|
+
| **File access** | Only `data/` inside the plugin directory: `config.json` (configuration) and `events.log` (event log) |
|
|
158
|
+
| **Events** | `agent/status`, `agent/error`, `session/event`, `user-questions/request` |
|
|
159
|
+
| **HTTP routes** | Under the local DSH Web server, `/api/dsh-shutdown-guard/`: `GET /state`, `GET /events`, `POST /arm`, `POST /disarm`, `POST /cancel`, `POST /config`; POST validates that Origin is `127.0.0.1` or `localhost` |
|
|
160
|
+
| **Network** | **No external requests at all**, no data collection |
|
|
161
|
+
| **Platform** | **Windows only**. On anything other than `win32` the plugin silently does not load |
|
|
162
|
+
|
|
163
|
+
While AFK auto-pilot is on, the plugin answers the agent's questions and injects recovery instructions into erroring sessions during armed periods; you can switch this off at any time in settings.
|
|
164
|
+
|
|
165
|
+
## Configuration
|
|
166
|
+
|
|
167
|
+
Written in `cordis.patch.yml` (the patch inside the plugin package), or changed at runtime via `POST /config`.
|
|
168
|
+
|
|
169
|
+
| Key | Range | Default | Description |
|
|
170
|
+
| --------------- | ------ | ------ | ----------------------------------------- |
|
|
171
|
+
| `countdownSec` | 10–600 | 60 | Seconds of countdown before shutdown (fixed at 60 in the default profile, not editable) |
|
|
172
|
+
| `marginSec` | 0–60 | 5 | Extra margin passed to `shutdown /t` |
|
|
173
|
+
| `onlyOnSuccess` | bool | `true` | Skip the shutdown when any session in this batch errored |
|
|
174
|
+
| `blockOnJobs` | bool | `true` | Skip the shutdown while background jobs are running |
|
|
175
|
+
| `afkAutoAnswer` | bool | `true` | Answer questions and perform error recovery for the user while armed |
|
|
176
|
+
| `language` | enum | `auto` | UI language; `auto` follows the system language, or pin it to `zh` / `en` / `ru` |
|
|
177
|
+
|
|
178
|
+
### Retry policies
|
|
179
|
+
|
|
180
|
+
Each error code is assigned a policy name; the policies themselves are configurable:
|
|
181
|
+
|
|
182
|
+
| Policy | Default cadence | Assigned to |
|
|
183
|
+
| ------------ | -------------------------------------- | ---------------------------------- |
|
|
184
|
+
| `rate-limit` | backoff 30 → 60 → 120 → 240 → 300, capped | `RATE_LIMIT` |
|
|
185
|
+
| `transient` | fixed 20 seconds | `SERVER` / `TRANSPORT` / `TIMEOUT` |
|
|
186
|
+
| `empty` | fixed 30 seconds | `EMPTY_RESPONSE` |
|
|
187
|
+
| `offload` | fixed 5 seconds | `IMAGE_OFFLOAD_REQUIRED` |
|
|
188
|
+
|
|
189
|
+
Rate limiting uses backoff rather than a fixed interval because hammering a rate-limited service **extends** the limit rather than just wasting requests. 5xx and network failures recover on a predictable timescale, so a fixed interval is simply faster. Image offload is a local disk operation and is almost immediately retryable, hence 5 seconds.
|
|
190
|
+
|
|
191
|
+
Override a single error code:
|
|
192
|
+
|
|
193
|
+
```yaml
|
|
194
|
+
retryAssign:
|
|
195
|
+
RATE_LIMIT: slow-limit
|
|
196
|
+
retryPolicies:
|
|
197
|
+
slow-limit: { mode: backoff, base: 60, max: 900 }
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
An unknown `code` falls back to `transient`; malformed policies are dropped and the defaults are kept. The currently effective assignments are available from `retryAssign` and `retryPolicies` in `/state`.
|
|
201
|
+
|
|
202
|
+
UI preferences live in the browser's `localStorage`: `dsh-shutdown-guard.variant` / `.theme` / `.folded`.
|
|
203
|
+
|
|
204
|
+
## HTTP API
|
|
205
|
+
|
|
206
|
+
Prefix `/api/dsh-shutdown-guard`, used by the card's own same-origin polling:
|
|
207
|
+
|
|
208
|
+
| Method | Path | Description |
|
|
209
|
+
| ---- | -------------------------- | -------------------------------------------------------------------------------- |
|
|
210
|
+
| GET | `/state?since=<ms>` | Full state plus incremental events |
|
|
211
|
+
| POST | `/arm` `/disarm` `/cancel` | Arm / disarm / cancel this countdown |
|
|
212
|
+
| POST | `/config` | Change configuration: `customEnabled`, `activeProfileId`, `profileId`+`patch`, `afkAutoAnswer`, `language` |
|
|
213
|
+
| GET | `/events?since=&limit=` | Event stream |
|
|
214
|
+
|
|
215
|
+
POSTs from a non-local Origin return 403; non-POST returns 405.
|
|
216
|
+
|
|
217
|
+
## Logs
|
|
218
|
+
|
|
219
|
+
- `<plugin directory>\data\events.log` — human-readable event stream (categories `config` / `afk` / `skip`); the settings page's "Event log" reads it
|
|
220
|
+
- `<DSH_HOME>\dsh-shutdown-guard.log` — process-level append log
|
|
221
|
+
|
|
222
|
+
## Development
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
node test/host.test.mjs # server: 26 assertions, mock ctx, never actually shuts down
|
|
226
|
+
node test/client.test.cjs # client bundle smoke test
|
|
227
|
+
node scripts/gen-gallery.cjs # generate the screenshot page from the real CSS in lib/client.js
|
|
228
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/screenshot.ps1 # headless Edge renders PNGs (transparent background + auto crop)
|
|
229
|
+
node scripts/gen-gif.cjs # generate the three animated GIFs used in the README
|
|
230
|
+
node scripts/gen-banner.cjs # generate the 1280x640 social preview image
|
|
231
|
+
node scripts/gen-demo.cjs # generate docs/demo.html (the interactive demo)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
All screenshots are generated from the real CSS in `lib/client.js`, so they cannot drift from the product. The animations work the same way: CSS animations are frozen at several points in time via `animation-delay`, captured frame by frame, and assembled with a built-in GIF89a encoder — no ffmpeg or ImageMagick needed on the machine.
|
|
235
|
+
|
|
236
|
+
The Learn section of the demo page is rendered live too: the six states are generated on the spot with the same `card()` markup as the product, not captured as screenshots.
|
|
237
|
+
|
|
238
|
+
## FAQ
|
|
239
|
+
|
|
240
|
+
**Q: Why does changing a setting do nothing?**
|
|
241
|
+
A: Both the server and the client need DSH restarted to load new code. Local HMR does not apply to locally linked plugins.
|
|
242
|
+
|
|
243
|
+
**Q: Why didn't it shut down after arming?**
|
|
244
|
+
A: Check "why it did not shut down last time" in settings; it names the specific reason (background jobs, errors, not armed, etc.).
|
|
245
|
+
|
|
246
|
+
**Q: Can I change my mind during the countdown?**
|
|
247
|
+
A: Yes. Use "Cancel shutdown" on the card or just run `shutdown /a`; cancelling keeps it armed, so the next batch still applies.
|
|
248
|
+
|
|
249
|
+
**Q: Can it shut down by mistake?**
|
|
250
|
+
A: With the default configuration, the plugin acts only when all four hold — all sessions idle, no background jobs, no errors, and you armed it manually. Breaking any one of them cancels or skips the shutdown. Manual task termination is also detected and excluded.
|
|
251
|
+
|
|
252
|
+
**Q: Does it work outside Windows?**
|
|
253
|
+
A: Not yet; that is planned for later.
|
|
254
|
+
|
|
255
|
+
## Constraints
|
|
256
|
+
|
|
257
|
+
- Windows only: exits early on anything other than `win32` and mounts nothing.
|
|
258
|
+
- Zero runtime dependencies: the server uses only `node:fs` / `node:path` / `node:crypto`, the browser only `require('react')`.
|
|
259
|
+
- Does not modify `app.asar` or DSH source.
|
|
260
|
+
|
|
261
|
+
## License
|
|
262
|
+
|
|
263
|
+
[MIT](LICENSE)
|