dsh-shutdown-guard 0.0.0-stage → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,125 @@
1
+ # Changelog
2
+
3
+ ## v1.5.0 — 2026-11-02
4
+
5
+ - 改名:`dsh-shutdown-panel` → **`dsh-shutdown-guard`**(Shutdown Guard)。原名只描述了界面,没表达它真正的特长——关机前逐条排除。`shutdown` 仍在包名第二位,市场按关键词搜索时排序不受影响。
6
+ - 同步改名:HTTP 前缀 `/api/dsh-shutdown-guard`、`localStorage` 键前缀、`<style data-dsh-shutdown-guard>`、客户端 bundle id、`cordis.patch.yml` 的行 id。
7
+ - ⚠️ 升级注意:旧版写在浏览器 `localStorage` 里的外观偏好(`dsh-shutdown-panel.variant` 等)不会迁移,升级后回到默认 A / 跟随主题 / 不折叠。如需保留,手动在设置里重选一次。
8
+
9
+ ## v1.4.2 — 2026-11-02
10
+
11
+ - 文档补齐:README 按 v1.4 重写(截图、功能矩阵、重试策略、FAQ),新增 CONTRIBUTING.md、LICENSE。
12
+ - 修复 README 早前被 PowerShell 写坏的问题( ransient 被吞成
13
+ - 新增 est/(服务端 27 项断言 + 客户端 bundle smoke,路径改为相对插件根)与 scripts/(从真实 CSS 生成图库、headless Edge 截图)。
14
+
15
+ ## v1.4.1 — 2026-11-02
16
+
17
+ - 重试节奏改为按故障类型分别配置:
18
+ - 默认分配:RATE_LIMIT →
19
+ - WAITING_CODES 增至 6 项,补 TIMEOUT 与 IMAGE_OFFLOAD_REQUIRED。
20
+ - 修 bug:setArmed() 只清 locked、没清
21
+
22
+ ## v1.4.0 — 2026-11-02
23
+
24
+ - 新增阻断态(blocked),插件不因此解除武装:卡片转色、状态行写原因,三种外观都有对应暂停外观。
25
+ - halted(灰蓝):额度、凭证、上下文超限、能力不支持、磁盘写满 —— 只有人能处理,按钮变「解除并重试」。
26
+ - waiting(灰绿、圆点呼吸):限流、5xx、网络、空响应 —— 到点自动重试,并向会话注入「继续原计划」指令。
27
+ - /state 新增 locked、
28
+ - 设计文档 docs/blocked-states.md(分级语义、错误码表、重试节奏分析、stuck 归档理由)。
29
+
30
+ ## v1.3.9 — 2026-11-02
31
+
32
+ - 修复:在分支对话里手动终止任务仍会触发关机。取消和正常结束都表现为 gent/status = idle,插件此前只看到后者。
33
+ - 新增 session/event 监听(宿主签名 (session, event)),按两条独立信号识别中断: urn/end 带取消原因;或本轮从 urn/start 到 urn/end 没追加过任何 ssistant/message。
34
+ - 命中中断时不武装,并记「已检测到用户中断,本轮不关机」。
35
+
36
+ ## v1.3.8 — 2026-11-02
37
+
38
+ - 尝试用 AbortError 拦截用户中断。事后证明打不中 —— 终止不走 gent/error,被 v1.3.9 取代。
39
+
40
+ ## v1.3.7 — 2026-11-02
41
+
42
+ - 引入 HALTED_CODES:额度、凭证、上下文超限等不可恢复错误不再当成普通失败处理(否则会在用户不在场时照常关机),改为置 locked,并在 ryArm() 最前面拦截。
43
+
44
+ ## v1.3.6 — 2026-11-02
45
+
46
+ - 新语义:当「有会话报错就跳过」关闭、且 AFK 自动回答开启时,出错批次不再永久阻止关机。第 1 次报错让 agent 排查,第 2 次下「收尾」指令;收尾那一轮结束后该会话的错误不再计入拦截条件。
47
+ - 仅在「报错就跳过」关闭时生效;该开关打开时出错的批次照旧整轮跳过。
48
+ - 重试计数与收尾标记都在武装/解除时清零。
49
+ ## v1.3.5 — 2026-11-02
50
+
51
+ - 新增调试钩子 POST /api/dsh-shutdown-guard/error-test,仅在 cordis.patch.yml 里设置 debugHooks: true 时注册。它用第一个 root 会话走一遍真实的 AFK 报错恢复链路(注入 + 日志 + 不关机),便于在没有真实崩溃时演练 T4。
52
+ - 错误处理逻辑抽成 handleAgentError(agent),事件监听与调试端点共用同一条代码路径。
53
+
54
+ ## v1.3.4 — 2026-11-02
55
+
56
+ - AFK 错误恢复:武装且 AFK 开关打开时,root 会话报错不再直接跳过/关机,而是向该会话注入一条恢复指令——先定位并修复报错、按原计划继续;同一会话第二次报错则要求它停止重试、把结果与失败原因写成报告落盘后收尾;第三次不再注入。
57
+ - 注入走 `agent.followup(message)`(等价于追加一轮用户消息),失败回落 `agent.steer`,都不可用则只记日志、不触发关机。
58
+ - 重试次数只在武装/解除时清空,避免「注入 → 重跑 → 再报错」无限循环。
59
+ - 另加一层保险:AFK 开关打开时,只要本批次存在报错会话就不武装关机(即使「有会话报错就跳过」是关的),等恢复成功、报错集合清空后再走正常流程。
60
+
61
+ ## v1.2.0 – v1.3.3(补记)
62
+
63
+ - 事件与 state 跟随插件:data/events.log、data/config.json,1 个默认档 + 8 个自定义档。
64
+ - 设置拆成三组:外观 / 何时关机 / 状态,含事件日志。
65
+ - 默认配置倒计时锁 60 秒(countdownLocked),自定义档可调。
66
+ - AFK 自动回答:武装时替用户回答提问(user-questions/request,prepend 抢在 Web 回答者之前)。
67
+ - 「AFK 自动回答」行下移到「倒计时时长」之后。
68
+ ## v1.1.1 — 2026-11-02
69
+
70
+ - 设置页按 Claude Style 的行式布局重做:每行 14px 标题 + 12px 说明,行间 0.5px 分隔线,上下 14px 留白。
71
+ - 控件放大:分段按钮 30px 高 / 13px 字,开关 36×20,滑块 16px。
72
+ - 卡片字号同步放大:标题 12.5px、说明 11.5px、按钮 30px 高 / 12.5px、主数字 26px。
73
+
74
+ ## v1.1.0 — 2026-11-02
75
+
76
+ - 折叠态上下内边距 10→6px(高 42→34px)。
77
+ - 警戒带 115°→45°,滚动位移改 28.28px。
78
+ - 设置页补齐外观/配色/折叠/判定条件/跳过原因/C 模式说明。
79
+ - 新增 POST /config(onlyOnSuccess / blockOnJobs),/state 同步返回。
80
+ - 修「设置不跟手」:切换后立即重绘,不再等 1s 轮询。
81
+
82
+ ## v1.0.3 — 2026-11-02
83
+
84
+ - 警戒带接缝修好:改为单条 2 倍宽元素(`::before`)承载 `repeating-linear-gradient(115deg)`,用 `translateX(-22.07px)` 平移;周期 20/sin115°≈22.07px,不再按 34px 平铺,斜线连续。
85
+ - 卡片颜色不再受宿主主题干预:改用自有调色板 `--dsp-*`(bg/fg/muted/line/accent/danger/track/tape-a/tape-b),只在运行时读取宿主表面亮度(probe `var(--dsw-alias-bg-layer-1)`)决定 `data-lum="light|dark"`,浅色用另一套值;字体仍随宿主。
86
+ - 终端屏幕改为固定深色(`#0b0d10` / `#9ef0a8`),不再受主题 token 影响;设置分区保留宿主 token(它位于设置页)。
87
+
88
+ ## v1.0.2 — 2026-11-02
89
+
90
+ - 警戒带改用固定琥珀 #f0a83a + 主题错误红:此前用 --dsw-alias-button-primary-fill,而当前主题的主按钮填充是近白色,条带成了白红相间。
91
+ - 卡片与「技能中心」按钮的点击判定区域对齐:host padding:0 12px 8px 0、卡片内边距 8px,实测卡片与面板按钮同为 x=12、宽 256。
92
+ - 未开启状态警戒带保持静止但保留 60% 不透明度,不再灰成一片。
93
+
94
+ ## v1.0.1 — 2026-11-02
95
+
96
+ 修复重启后出现的三个问题。
97
+
98
+ ### 修复
99
+ - **会话列表消失**:卡片原先注册在 `sidebar.workspaces`,而该槽是 `single` 槽、已被会话浏览器占用;同优先级注册冲突导致会话浏览器整块不再渲染。卡片改为在侧边栏 `regionArea` 顶部插入独立 DOM 节点(React 不管理它),不再占用任何槽位,会话列表恢复正常。
100
+ - **卡片左缘未与「技能中心」对齐**:容器改为 `padding:6px 2px 8px`、卡片 `width:100%;margin:0`,左缘与面板行(`margin:0 2px`)对齐到同一 x。
101
+ - **警戒带图案/颜色异常**:去掉 `color-mix(... transparent)`,改为实色 `--dsw-alias-button-primary-fill` 与 `--dsw-alias-state-error-primary`,条带高度 10px、`background-size:34px 100%`,保持自右向左滚动。
102
+
103
+ ### 变更
104
+ - 卡片改为纯 DOM 渲染 + 每秒对齐重挂载(侧边栏折叠/重建后会自动插回),只有设置分区仍用 React。
105
+ - 旧插件 `dsh-shutdown-after-task` 已从 profile 依赖、`pnpm.overrides`、`generationProjection`、`bundles`、`desired.json`、`node_modules` 软链与 `.generations/live` 目录中移除。
106
+
107
+ ## v1.0.0 — 2026-11-02
108
+
109
+ 侧边栏卡片版。
110
+
111
+ - 卡片位于「技能中心」下方、会话列表上方。
112
+ - 三种外观:A 警戒带 / B 雷达 / C 终端,可在 设置 → 插件 → 任务完成后关机 中切换。
113
+ - 纵向折叠:只留标题与状态标签,点击标题展开;可设为默认折叠。
114
+ - 状态机可视化:未开启 / 待命 / 等待后台任务 / 倒计时 / 已关机。
115
+ - 浅色、深色与第三方主题(如 Claude Style)自适应,颜色统一走 `--dsw-alias-*` token。
116
+ - 后台任务守卫(v0.1.1 引入)保留:有运行中的后台任务时不触发;倒计时中出现新任务会 `shutdown /a` 并保持武装。
117
+ - 外观与折叠状态写入 `localStorage`。
118
+
119
+ ## v0.1.1 — 2026-10-24
120
+
121
+ - 新增 `ctx.jobs` 事件订阅:运行中的后台任务会阻止关机;取消倒计时后仍保持武装。
122
+
123
+ ## v0.1.0 — 2026-10-21
124
+
125
+ - 首个可用版本:复用原插件关机逻辑,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.md CHANGED
@@ -1,3 +1,253 @@
1
- # Temporary Holding Version
1
+ # Shutdown Guard · 任务完成后关机
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <p align="center"><img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/banner.png" width="880" alt="Shutdown Guard — 任务完成后自动关机,侧边栏守夜卡片"></p>
4
+
5
+ [![version](https://img.shields.io/badge/version-1.5.0-2ea44f)](CHANGELOG.md)
6
+ [![demo](https://img.shields.io/badge/%E2%96%B6-%E4%BA%A4%E4%BA%92%E6%BC%94%E7%A4%BA-d97757)](https://ltayshark0637.github.io/dsh-Shutdown-Guard/demo.html)
7
+ [![DSH](https://img.shields.io/badge/DSH-%E2%89%A50.1.5--rc.1-4a90d9)](https://github.com/deepseek-ai)
8
+ [![platform](https://img.shields.io/badge/platform-Windows-0078d4)](#约束)
9
+ [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
10
+
11
+ 为 DeepSeek Harness 中的侧边栏添加「所有任务完成后自动关机」卡片,拥有更好且多样的视觉效果、判定逻辑与个性化定制。位于「技能中心」下方、会话列表上方,随任务状态实时变化。
12
+
13
+ **▶ [交互演示页](https://ltayshark0637.github.io/dsh-Shutdown-Guard/demo.html)** —— 不用装插件,先看它长什么样、它怎么判断。左右两页:**了解**(判定、外观、状态)与 **试一试**(随机一个场景,亲手点武装、取消、解除,一路看到关机或阻断)。数据全部本地随机,不联网。
14
+
15
+ ## 为什么需要它
16
+
17
+ 跑长任务的人常有这个场景:晚上让 agent 把一批活干完,想让它干完就关机,人不在跟前。
18
+
19
+ 但「任务结束」不等于「可以关机」——后台任务还在跑、会话报错了、agent 在等你回答问题、你在半路手动终止了……这些情况下关机全是事故。这个插件把这些情况逐个挡下来,只有真的可以关的时候才关。
20
+
21
+ ## 判定:什么时候才关
22
+
23
+ 处于阻断态(见下文)时直接不下发;其余五道闸门同时满足才下发 `shutdown /s`:
24
+
25
+ 1. **已武装** —— 你手动点的
26
+ 2. **没有倒计时在跑** —— 避免重复下发
27
+ 3. **所有 root 会话都空闲** —— 没有任何 agent 还在干活
28
+ 4. **没有运行中 / 停止中的后台任务** —— `blockOnJobs`,默认开
29
+ 5. **本批次没有会话报错** —— `onlyOnSuccess`,默认开
30
+
31
+ 缺任何一条都不关,并把原因写在卡片和设置页里。
32
+
33
+ ## 三种外观
34
+
35
+ | A · 警戒带 | B · 雷达 | C · 终端 |
36
+ |:----------------------------------------------------------------------:|:---------------------------------------------------------------:|:------------------------------------------------------------------------------------:|
37
+ | <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"> |
38
+ | 大字状态 + 底部滚动的红黄斜纹 | 同心圆 + 扫描光束,会话与后台任务以圆点呈现 | 命令行摘要 + 六段判定条件指示灯 |
39
+
40
+ 雷达里的圆点按数量增删、不重排:数量变化时,你正在看的那几个点不会跳位置。
41
+
42
+ <p align="center"><img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/anim-radar.gif" width="330" alt="雷达扫描"></p>
43
+
44
+ 设置里切换,立即生效,无需重启。
45
+
46
+ ## 六种运行状态
47
+
48
+ | 未开启 | 待命 | 等待后台任务 |
49
+ |:---------------------------------------------------:|:-----------------------------------------------------:|:----------------------------------------------------------------------:|
50
+ | <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"> |
51
+
52
+ | 倒计时 | 已下发 | 阻断 |
53
+ |:----------------------------------------------------:|:-----------------------------------------------------------------------:|:------------------------------------------------------------------------:|
54
+ | <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"> |
55
+
56
+ `未开启` → `待命` → `等待后台任务` → `倒计时` → `已下发`;任意阶段遇到阻断性错误会进入 `阻断`。倒计时期间随时可取消,**取消后仍保持武装**——下一批任务干完照样会关。
57
+
58
+ 想在这六种状态之间来回看?演示页「了解」第 04 节右上角有 `A` `B` `C` 三枚胶囊,切换的是当前预览用哪种外观。
59
+
60
+ ## 阻断态
61
+
62
+ | halted · 需人工处理 | waiting · 自动重试 |
63
+ |:--------------------------------------------------------------:|:---------------------------------------------------------------------------------:|
64
+ | <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"> |
65
+
66
+ 遇到没法当普通失败处理的错误,插件**不解除武装**,卡片转色、把原因写出来:
67
+
68
+ - **`halted`**(灰蓝)—— 额度不足、凭证失效、上下文超限、能力不支持、磁盘写满。只有人能处理,点「解除并重试」。
69
+ - **`waiting`**(灰绿,圆点呼吸)—— 限流、5xx、网络中断、空响应、超时、图片卸载。按策略自动重试,到点注入「继续原计划」指令。
70
+
71
+ <p align="center"><img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/anim-blocked.gif" width="380" alt="等待恢复,状态点呼吸"></p>
72
+
73
+ 重试节奏按故障类型分别配置,见 [重试策略](#重试策略)。
74
+
75
+ ## AFK 自动接管
76
+
77
+ 人不在跟前时,插件替你顶三件事:
78
+
79
+ - **自动回答提问**(默认开):agent 问你问题时,回复「用户处于 AFK,先完成已交代的任务,汇总报告并落盘,不碰未交代的分支」。返回 `selected: []` + 自定义文本,**不会替你选中任何选项**;关掉后提问照常抛给你。
80
+ - **错误恢复**:会话报错时先要求 agent 排查并继续原计划;同一会话再次失败改为「停止重试、写报告收尾」。
81
+ - **中断保护**:你在半路手动终止任务,不会触发关机。
82
+
83
+ ## 装在哪里
84
+
85
+ | 武装时:技能中心下方、会话列表上方 | 倒计时:同一位置,整条转红 |
86
+ |:-------------------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------------:|
87
+ | <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"> |
88
+
89
+ 卡片目前固定在侧边栏的这个位置。后续计划把它做成可以独立出现的形态(设置里给个开关),届时这里会补充说明。
90
+
91
+ ## 其他
92
+
93
+ - **纵向折叠**:收起只留标题和状态,点标题展开。
94
+
95
+ <img src="https://cdn.jsdelivr.net/gh/ltayshark0637/dsh-Shutdown-Guard@main/docs/screenshots/16-folded.png" width="250" align="right" alt="折叠态">
96
+
97
+ - **主题自适应**:卡片自带 `--dsp-*` 调色板,按宿主表面亮度自动取深浅色;也可强制锁定深色 / 浅色 / Claude Style 配色。
98
+
99
+ - **事件日志**:设置页里能看最近的事件与「上次为什么没关机」,完整流水在 `data/events.log`。
100
+
101
+ <br clear="all">
102
+
103
+ ## 设置页
104
+
105
+ | 深色 | 浅色 |
106
+ |:---------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------:|
107
+ | <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"> |
108
+
109
+ 分三组:外观 / 何时关机 / 状态。所有开关立即生效。
110
+
111
+ - **默认配置**锁定 60 秒不可改;另有 8 个自定义档位可自由调。
112
+ - 「会话都停下来」是底线条件,始终开启,不可关。
113
+ - 「后台任务跑完」挡后台任务,「有会话报错就跳过这一轮」挡出错批次。
114
+ - 「AFK 自动回答」控制武装期间是否替你回复提问;说明里写明「回复不会替你选中任何选项,关闭后提问照常弹给你」。
115
+
116
+ ## 浅色与第三方主题
117
+
118
+ | 浅色卡片 | Claude Style |
119
+ |:-----------------------------------------------------:|:------------------------------------------------------:|
120
+ | <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"> |
121
+
122
+ 卡片不套用宿主配色,自带调色板;设置页则跟宿主主题走。第三方的 Claude Style 也有对应配色。
123
+
124
+ ## 安装
125
+
126
+ ```bash
127
+ dsh plugin --profile web add github:ltayshark0637/dsh-Shutdown-Guard
128
+ ```
129
+
130
+ 装完**重启 DSH**。
131
+
132
+ > ⚠️ **客户端 bundle 只在 DSH 启动时编入模块图**:改 `lib/client.js` 后必须重启,刷新页面无效。服务端同理。
133
+
134
+ **网络受限时**:上面这条命令本质是 `pnpm add github:...`,需要 clone 到 GitHub。若 clone 不通,改用本地路径安装——DSH 设置 → 插件 → 添加,填入本目录的**绝对路径** → 安装 → 重启。
135
+
136
+ **手工兜底**:在 `profiles\web` 下建 `node_modules\dsh-shutdown-guard` 软链指向本目录,然后:
137
+
138
+ - `profiles\web\package.json` 的 `dependencies` 与 `pnpm.overrides` 加 `link:<本目录绝对路径>`
139
+ - `dsh.profile.bundles` 追加 `dsh-shutdown-guard`
140
+
141
+ ## 权限与影响
142
+
143
+ 装上之后,这个插件会做以下几件事:
144
+
145
+ | 影响面 | 具体行为 |
146
+ | --- | --- |
147
+ | **执行命令** | 经 `subprocess` 调 Windows 的 `shutdown.exe`:`/s /t <秒>` 安排关机、`/a` 取消。**只在武装且五道闸门全满足时才调** |
148
+ | **读写文件** | 只在插件目录下的 `data/`:`config.json`(配置)与 `events.log`(事件日志) |
149
+ | **监听事件** | `agent/status`、`agent/error`、`session/event`、`user-questions/request` |
150
+ | **HTTP 路由** | 本机 DSH Web 服务下 `/api/dsh-shutdown-guard/` 的 `GET /state`、`GET /events`、`POST /arm`、`POST /disarm`、`POST /cancel`、`POST /config`;POST 校验 Origin 必须为 `127.0.0.1` 或 `localhost` |
151
+ | **网络** | **不发起任何外部请求**,不收集数据 |
152
+ | **平台** | **仅 Windows**。非 `win32` 时插件静默不加载 |
153
+
154
+ AFK 自动接管开启时,插件会在武装期间替你回答 agent 的提问、并向报错会话注入恢复指令;这个开关在设置里可以随时关掉。
155
+
156
+ ## 配置
157
+
158
+ 写在 `cordis.patch.yml`(插件包内的 patch),或经 `POST /config` 在运行时修改。
159
+
160
+ | 键 | 范围 | 默认 | 说明 |
161
+ | --------------- | ------ | ------ | ----------------------------------------- |
162
+ | `countdownSec` | 10–600 | 60 | 关机前倒计时秒数(默认档固定 60,不可改) |
163
+ | `marginSec` | 0–60 | 5 | 传给 `shutdown /t` 的额外余量 |
164
+ | `onlyOnSuccess` | bool | `true` | 本批次有会话报错时跳过关机 |
165
+ | `blockOnJobs` | bool | `true` | 有运行中的后台任务时跳过关机 |
166
+ | `afkAutoAnswer` | bool | `true` | 武装期间替用户回答提问、做错误恢复 |
167
+
168
+ ### 重试策略
169
+
170
+ 每个错误码分配一个策略名,策略本身可配置:
171
+
172
+ | 策略 | 默认节奏 | 分配给 |
173
+ | ------------ | -------------------------------------- | ---------------------------------- |
174
+ | `rate-limit` | backoff 30 → 60 → 120 → 240 → 300 封顶 | `RATE_LIMIT` |
175
+ | `transient` | 固定 20 秒 | `SERVER` / `TRANSPORT` / `TIMEOUT` |
176
+ | `empty` | 固定 30 秒 | `EMPTY_RESPONSE` |
177
+ | `offload` | 固定 5 秒 | `IMAGE_OFFLOAD_REQUIRED` |
178
+
179
+ 限流用退避而不是固定间隔,是因为对限流服务高频重试**会延长**限流期,而不只是浪费请求。5xx 和网络故障恢复时间可预测,固定间隔反而更快。图片卸载是本地磁盘操作,几乎立刻可重试,所以给 5 秒。
180
+
181
+ 覆盖单个错误码:
182
+
183
+ ```yaml
184
+ retryAssign:
185
+ RATE_LIMIT: slow-limit
186
+ retryPolicies:
187
+ slow-limit: { mode: backoff, base: 60, max: 900 }
188
+ ```
189
+
190
+ 未知 `code` 回落 `transient`;形状写错的策略会被丢弃并保留默认值。当前生效的分配可从 `/state` 的 `retryAssign`、`retryPolicies` 读到。
191
+
192
+ UI 偏好存在浏览器 `localStorage`:`dsh-shutdown-guard.variant` / `.theme` / `.folded`。
193
+
194
+ ## HTTP 接口
195
+
196
+ 前缀 `/api/dsh-shutdown-guard`,供卡片自己的同源轮询使用:
197
+
198
+ | 方法 | 路径 | 说明 |
199
+ | ---- | -------------------------- | -------------------------------------------------------------------------------- |
200
+ | GET | `/state?since=<ms>` | 完整状态 + 增量事件 |
201
+ | POST | `/arm` `/disarm` `/cancel` | 武装 / 解除 / 取消本次倒计时 |
202
+ | POST | `/config` | 改配置:`customEnabled`、`activeProfileId`、`profileId`+`patch`、`afkAutoAnswer` |
203
+ | GET | `/events?since=&limit=` | 事件流水 |
204
+
205
+ 非本机 Origin 的 POST 返回 403;非 POST 返回 405。
206
+
207
+ ## 日志
208
+
209
+ - `<插件目录>\data\events.log` —— 人类可读的事件流水(`config` / `afk` / `skip` 分类),设置页「事件日志」即读它
210
+ - `<DSH_HOME>\dsh-shutdown-guard.log` —— 进程级追加日志
211
+
212
+ ## 开发
213
+
214
+ ```bash
215
+ node test/host.test.mjs # 服务端:26 项断言,mock ctx,不真的关机
216
+ node test/client.test.cjs # 客户端 bundle smoke
217
+ node scripts/gen-gallery.cjs # 从 lib/client.js 真实 CSS 生成截图页
218
+ powershell -NoProfile -ExecutionPolicy Bypass -File scripts/screenshot.ps1 # headless Edge 渲染 PNG(透明背景 + 自动裁边)
219
+ node scripts/gen-gif.cjs # 生成 README 里的三张动图
220
+ node scripts/gen-banner.cjs # 生成 1280x640 的社交预览图
221
+ node scripts/gen-demo.cjs # 生成 docs/demo.html(交互演示页)
222
+ ```
223
+
224
+ 截图全部由 `lib/client.js` 里的真实 CSS 生成,不会与产品脱节。动图同理:把 CSS 动画用 `animation-delay` 冻结在若干时间点逐帧截图,再用内置的 GIF89a 编码器合成——本机不需要 ffmpeg 或 ImageMagick。
225
+
226
+ 演示页面的「了解」部分同样是实时渲染:六种状态用与产品同一套 `card()` 标记现场生成,不是截图。
227
+
228
+ ## 常见问题
229
+
230
+ **Q:为什么改了设置没反应?**
231
+ A:服务端和客户端都得重启 DSH 才加载新代码。本机 HMR 对本地链接插件不生效。
232
+
233
+ **Q:武装后为什么不关机?**
234
+ A:看设置页「上次为什么没关机」,那里写着具体原因(后台任务、报错、未武装等)。
235
+
236
+ **Q:倒计时期间能反悔吗?**
237
+ A:能。卡片上的「取消关机」或直接 `shutdown /a` 都行,取消后仍保持武装,下一个批次继续生效。
238
+
239
+ **Q:会误关机吗?**
240
+ A:默认配置下,插件要看到「所有会话空闲 + 没有后台任务 + 没有报错 + 你手动武装」四条同时成立才动手,其中任何一条被打破都会取消或跳过。手动终止任务也已被识别并排除。
241
+
242
+ **Q:非 Windows 能用吗?**
243
+ A:暂时不能,等待后续开发。
244
+
245
+ ## 约束
246
+
247
+ - 仅 Windows:非 `win32` 平台早退,不挂载。
248
+ - 零运行时依赖:服务端只用 `node:fs` / `node:path` / `node:crypto`,浏览器只 `require('react')`。
249
+ - 不修改 `app.asar` 与 DSH 源码。
250
+
251
+ ## 许可
252
+
253
+ [MIT](LICENSE)
@@ -0,0 +1,21 @@
1
+ # dsh-shutdown-guard:任务完成后自动关机
2
+ - insert:
3
+ - id: dsh-shutdown-guard
4
+ name: 'dsh-shutdown-guard'
5
+ config:
6
+ countdownSec: 60
7
+ marginSec: 5
8
+ onlyOnSuccess: true
9
+ blockOnJobs: true
10
+ retryPolicies:
11
+ rate-limit: { mode: backoff, base: 30, max: 300 }
12
+ transient: { mode: fixed, every: 20 }
13
+ empty: { mode: fixed, every: 30 }
14
+ offload: { mode: fixed, every: 5 }
15
+ retryAssign:
16
+ RATE_LIMIT: rate-limit
17
+ SERVER: transient
18
+ TRANSPORT: transient
19
+ TIMEOUT: transient
20
+ EMPTY_RESPONSE: empty
21
+ IMAGE_OFFLOAD_REQUIRED: offload