dsh-console-sentinel 0.0.0-stage → 0.3.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 +231 -0
- package/LICENSE +21 -0
- package/README-es.md +29 -0
- package/README-hi.md +29 -0
- package/README-pt.md +29 -0
- package/README-zh.md +29 -0
- package/README.en.md +127 -0
- package/README.md +125 -2
- package/README.zh.md +123 -0
- package/cordis.patch.yml +27 -0
- package/icon.svg +11 -0
- package/lib/client.js +637 -0
- package/lib/desktop-cli.mjs +136 -0
- package/lib/desktop.js +283 -0
- package/lib/index.js +982 -0
- package/lib/logic.js +336 -0
- package/lib/probe.js +320 -0
- package/lib/whitelist.js +78 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +80 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0 (2026-11-03) — 设置面板 UI(任务栏窗口清理 / 白名单 / 窗口状态)
|
|
4
|
+
|
|
5
|
+
用户需求:想看插件「现在什么状态」,并要「清理任务栏窗口」的操作入口 + 有没有 UI 设计。
|
|
6
|
+
此前插件是纯宿主插件(无 `dsh.client`),只能在对话里让模型调工具,用户自己点不了。
|
|
7
|
+
|
|
8
|
+
### 新增
|
|
9
|
+
1. **客户端设置面板** `lib/client.js`(`__ModuleLoader__` bundle,`dsh.client` 声明,
|
|
10
|
+
platform `web`,id=完整包名):在 DSH「设置」页注册「控制台哨兵」分区。分区内含
|
|
11
|
+
- 概览:总窗口 / 空壳 / 可见空壳 / 已隐藏 计数 + 「隐藏可见空壳」按钮;
|
|
12
|
+
- 任务栏窗口清理:预演 / 一键关闭其他窗口;
|
|
13
|
+
- 控制台窗口列表:逐行显示并可 隐藏 / 显示 / 关闭;
|
|
14
|
+
- 豁免白名单:进程名 / 标题关键字 标签式增删与清空。
|
|
15
|
+
2. **宿主 HTTP 路由** `lib/index.js` `registerHttpUi()`:`/dsh-console-sentinel/{status,scan,hide-empty,window,desktop-cleanup,whitelist}`,
|
|
16
|
+
均做 loopback 校验;客户端 fetch 根相对路径(桌面端 Electron renderer 自动带
|
|
17
|
+
capability header,普通 curl 仍 403)。webServer 缺失时面板优雅降级,不影响工具功能。
|
|
18
|
+
3. `package.json`:新增 `dsh.client`、`exports["./client"]`,版本 0.2.0→0.3.0。
|
|
19
|
+
4. `tests/client.test.mjs`:DOM stub 评估真实 bundle,断言 bundle id / `exports.inject`
|
|
20
|
+
为数组 / `settings.section` 注册 / 首屏计数-窗口-白名单渲染 / 按钮触发路由。
|
|
21
|
+
|
|
22
|
+
### 约束(都遵守,来自本机踩坑)
|
|
23
|
+
- `exports.inject` 必须是普通数组;`ctx.slots.inject("settings.section", …)` 直接调用不包 `ctx.effect`;
|
|
24
|
+
组件不用 React hooks,结果按 id 写回 DOM 并在渲染入口 `setTimeout(0)` 自愈重挂载。
|
|
25
|
+
- 样式只用 `--dsw-alias-*` 设计令牌;不使用 iframe;不轮询 agent/status。
|
|
26
|
+
- 客户端无绝对 URL(探针断言 https?:// 计数 0、iframe 计数 0)。
|
|
27
|
+
|
|
28
|
+
### 修复(2026-11-04,仍在 0.3.0 内)
|
|
29
|
+
1. **插件根本没加载(duplicate loader entry)**:`dsh-console-sentinel` 同时出现在
|
|
30
|
+
`profiles/desktop/package.json` 的 `dsh.profile.bundles` 与 `profiles/desktop/cordis.patch.yml`
|
|
31
|
+
两条同 id 条目中,加载器按 duplicate 处理 → 插件不挂载(日志无任何 mounted 行、对话里
|
|
32
|
+
也无 `console_windows_list` 等工具)。按 `dsh-artifact-linker` 的约定,**配置迁入插件自身
|
|
33
|
+
bundle patch `cordis.patch.yml`**,并删除 profile `cordis.patch.yml` 里的手工条目(bundles 保留)。
|
|
34
|
+
校验:profile 28 条目、无 sentinel、bundles 36 项含 sentinel、trueDuplicates=[]。
|
|
35
|
+
2. **Windows Terminal 空壳识别**:可见的「黑窗」主要是 `CASCADIA_HOSTING_WINDOW_CLASS`
|
|
36
|
+
(窗口属主是 `WindowsTerminal.exe`,不是里面的 shell),旧的 `isEmptyConsole` 只认解释器
|
|
37
|
+
进程本身 → 永远判「非空」。新增 `isBareShellTitle()` 与 `isEmptyConsole` 的 CASCADIA 分支
|
|
38
|
+
(标题为裸解释器路径如 `…\powershell.exe` / `…\cmd.exe` 且 `cpuPct ≤ 1` 才算空),并把
|
|
39
|
+
`windowsterminal` 从 `HARD_PROTECTED_PROCESSES` 移出(`autoAction` 仍为 `none`,只经
|
|
40
|
+
`emptyConsoleAction: hide` 处理空壳)。实测 8 个可见窗口中 5 个 CASCADIA 黑窗被判空。
|
|
41
|
+
3. `tests/cascadia.test.mjs`:`isBareShellTitle` + CASCADIA 空判定 3 用例。
|
|
42
|
+
4. **bundle patch 里 `disabled: !!js …` 使整条 insert 静默失败**(2026-11-04 二次实测,真凶):
|
|
43
|
+
插件自己的 `cordis.patch.yml` 属 **bundle patch**,由普通 YAML 解析器读取,不认自定义标签;
|
|
44
|
+
本机 17 个能正常加载的插件其 patch **无一使用自定义标签**。移除该行(平台守卫保留在
|
|
45
|
+
`apply()`:非 Windows 打 `staying inert` 并 return),普通 YAML 解析通过。
|
|
46
|
+
同时在 `apply()` 入口加 `logger.info('host half mounted (v0.3.0, …)')`,以后日志可直接确认挂载。
|
|
47
|
+
5. **扫描定时器被宿主 dispose 清掉(真凶 2)**:`ctx.effect(() => () => clearInterval(scanTimer))`
|
|
48
|
+
的 disposer 在宿主侧 teardown/reload 时会被调用,导致**第一轮 `scan #1` 之后定时器被
|
|
49
|
+
`clearInterval`**,之后再无 `scan #2`、也无任何异常日志(`catch` 不触发)。改为**进程自有的
|
|
50
|
+
plain `setInterval`(不挂 `ctx.effect`)**,并保留一个「超过 3×周期未扫描就强制补扫」的自愈
|
|
51
|
+
supervisor;临时的心跳/逐窗口诊断日志已清理。
|
|
52
|
+
实测(修复后):启动 22 秒内 **6 个空壳窗口被自动隐藏**(5 个 Windows Terminal `CASCADIA` +
|
|
53
|
+
1 个 orphaned powershell),`visibleAfter=false`、`stillWindow=true`。
|
|
54
|
+
6. 默认 `idleThresholdSec` 180 → **60**(空壳窗口更快消失;CPU 持续空闲才计数,不影响正在
|
|
55
|
+
计算的 Fluent/mpiexec 控制台)。
|
|
56
|
+
|
|
57
|
+
### 生效方式
|
|
58
|
+
- 改 `lib/client.js` → **Ctrl+Shift+R** 硬刷新;
|
|
59
|
+
- 改宿主 `lib/index.js` / `package.json` / 挂载 → **完全退出 DSH 再启动**。
|
|
60
|
+
|
|
61
|
+
## 0.2.0 (2026-11-03) — 空壳 PowerShell 安全清理 + 一键关窗口统一白名单
|
|
62
|
+
|
|
63
|
+
用户需求:检查插件运营状态;能否有效隐藏/关闭「没有进程捆绑的空 PowerShell」;
|
|
64
|
+
把之前开发的一键关闭其他窗口加进来;可设置豁免软件白名单。
|
|
65
|
+
|
|
66
|
+
### 修的真问题
|
|
67
|
+
0.1.x 的 `scope.orphaned` 默认 `false`,导致最常见的痛点——WMI/脚本退出后残留的
|
|
68
|
+
**空 PowerShell 窗口**——永远不被自动处理;而直接打开 `orphaned` 又会误伤
|
|
69
|
+
`mpiexec`(Fluent MPI)、`fl2610`(求解器)等在跑的计算(同属「父进程已退出」)。
|
|
70
|
+
空判定缺位 = 只能二选一,两个都不对。
|
|
71
|
+
|
|
72
|
+
### 新增
|
|
73
|
+
1. **空控制台安全判定**(`lib/logic.js` `isEmptyConsole`):解释器进程
|
|
74
|
+
(powershell/pwsh/cmd/python/pythonw)**且没有任何工作子进程**(children 去掉
|
|
75
|
+
conhost/openconsole 后为空)才算「空壳」。probe 现返回每进程 `Children`
|
|
76
|
+
(`lib/probe.js` 用 NtQueryInformationProcess 自建父子表,零依赖)。新配置
|
|
77
|
+
`scope.emptyConsole`(默认 `true`)+ `emptyConsoleAction`(默认 `hide`,独立于
|
|
78
|
+
`autoAction`)——只清空壳、绝不碰在跑计算。
|
|
79
|
+
2. **硬保护进程集** `HARD_PROTECTED_PROCESSES`:explorer/taskmgr/dsh desktop/
|
|
80
|
+
dwm/winlogon/csrss/lsass 等系统进程,以及 fl2610/cx2610/mpiexec/smpd/fluent/
|
|
81
|
+
fluent-meshing/launcher1 —— 无论配置如何都拒绝管理。
|
|
82
|
+
3. **持久化豁免白名单** `lib/whitelist.js` + 新工具 `sentinel_whitelist`
|
|
83
|
+
(list/add/remove/reset,kind=process|title)。落盘
|
|
84
|
+
`~/.dsh/console-sentinel/whitelist.json`,与静态 `config.whitelist` 合并,工具
|
|
85
|
+
改动即时生效、无需改 yml 或重启。白名单同时作用于自动动作与
|
|
86
|
+
`desktop_windows_cleanup`。
|
|
87
|
+
4. **一键关闭其他窗口**(`desktop_windows_cleanup`)现在读统一白名单,
|
|
88
|
+
`protectedProcesses`/`keepTitles` 合并用户名单。
|
|
89
|
+
5. `console_windows_list` 每行新增 `kids=`(子进程数)与 `[empty]` 标记。
|
|
90
|
+
|
|
91
|
+
### 测试
|
|
92
|
+
`desktop.test.mjs` D5 默认保护列表断言从 4 项更新为实际 9 项(实现更安全,测试
|
|
93
|
+
落后于实现,改测试不改实现)。全套 `node --test` 51 断言全绿
|
|
94
|
+
(logic 29 + desktop 17 + tool-output 5)。
|
|
95
|
+
|
|
96
|
+
## 0.1.2 (2026-09-23) — the ledger claimed hides it never verified
|
|
97
|
+
|
|
98
|
+
Reported by the user, correctly: “你说你隐藏了 13 个真的假的”. It was **partly
|
|
99
|
+
false**. An independent Win32 check (`IsWindowVisible`) of the 13 hwnds the
|
|
100
|
+
plugin had reported as `[hidden]` found **9 genuinely hidden, 4 already
|
|
101
|
+
destroyed, and 1 still visible** (a minimised Windows Terminal). Three defects:
|
|
102
|
+
|
|
103
|
+
1. **`hidden` was set from intent, not from observation** (`lib/index.js`
|
|
104
|
+
`actOn`). `rec.hidden = true` ran as soon as `runWindowAction` returned
|
|
105
|
+
`ok`, before anything re-checked the window. The action script *already*
|
|
106
|
+
measured the post-state (`afterVisible`, `stillWindow`) — the ledger simply
|
|
107
|
+
never looked at it. `hidden` is now derived from that measured state, and a
|
|
108
|
+
failed action can no longer leave a stale `hidden` claim behind.
|
|
109
|
+
2. **“destroyed” and “hidden” were conflated.** A window that no longer exists
|
|
110
|
+
was counted as a successful hide, which inflated the reported number. There
|
|
111
|
+
is now a separate `destroyed` flag, surfaced in the list as `[gone]`, and a
|
|
112
|
+
gone window is never counted as hidden. This is why “I hid 13” was wrong:
|
|
113
|
+
4 of them were gone, not hidden.
|
|
114
|
+
3. **`console_window_action` returned invalid output on every success**
|
|
115
|
+
(`lib/index.js`). It built `reason: res.ok ? undefined : res.reason`, so a
|
|
116
|
+
successful call carried an explicit `reason: undefined` against a
|
|
117
|
+
schema-declared `string` → validation failed and the runtime dropped the
|
|
118
|
+
whole result as *“value is not lossless JSON”*. The hide really happened;
|
|
119
|
+
the caller just never saw it. `reason` is now omitted on success (and
|
|
120
|
+
coerced to a string on failure) instead of being set to `undefined`.
|
|
121
|
+
**`desktop_windows_cleanup` had the identical defect** — and there it sat on
|
|
122
|
+
the tool's *normal* path (returning a dry-run plan), so it broke every call,
|
|
123
|
+
not just some. Fixed in both; a grep for the pattern now matches only the
|
|
124
|
+
explanatory comment.
|
|
125
|
+
|
|
126
|
+
Also added:
|
|
127
|
+
|
|
128
|
+
- **`probeVisibility()`** (`lib/probe.js`) — a lightweight batch
|
|
129
|
+
`IsWindowVisible` query (no full scan), used as an independent source of
|
|
130
|
+
truth.
|
|
131
|
+
- **`ledger.reconcileVisibility()`** (`lib/logic.js`) — an anti-lie guard.
|
|
132
|
+
`console_windows_list` now re-measures every tracked window before printing,
|
|
133
|
+
so `[hidden]` can only appear for a window that is genuinely not visible at
|
|
134
|
+
that moment; windows missing from the truth map are reported as `[gone]`.
|
|
135
|
+
|
|
136
|
+
Tests: `tests/logic.test.mjs` 24 → 29 assertions (destroyed-vs-hidden, and
|
|
137
|
+
reconcileVisibility in all three directions); new `tests/tool-output.test.mjs`
|
|
138
|
+
(5 assertions) locks the schema contract and **asserts the old shape was
|
|
139
|
+
buggy**, so the defect cannot silently return.
|
|
140
|
+
|
|
141
|
+
Lesson recorded: a plugin’s own ledger is not evidence. “The bookkeeping says
|
|
142
|
+
success” and “the user can see it worked” are different claims, and only the
|
|
143
|
+
second one is worth reporting — verify with something outside the component
|
|
144
|
+
that is making the claim.
|
|
145
|
+
|
|
146
|
+
## 0.1.1 (2026-09-22) — desktop-cleanup path repaired + protected set widened
|
|
147
|
+
|
|
148
|
+
The console path (`console_windows_list` / `_action` / `_sweep`) was fine, but
|
|
149
|
+
the **desktop-cleanup path was dead on arrival** — `desktop_windows_cleanup`,
|
|
150
|
+
`desktop-cli.mjs list|cleanup`, and the 一键关闭其他窗口 shortcut all failed.
|
|
151
|
+
Four defects, each found by running the code rather than reading it:
|
|
152
|
+
|
|
153
|
+
1. **Marker never substituted** (`lib/desktop.js`). The probe template used
|
|
154
|
+
`'${JSON_BEGIN}'` but was never `.replace()`d, so the literal text
|
|
155
|
+
`${JSON_BEGIN}` reached PowerShell and `extractMarkeredJson` found nothing
|
|
156
|
+
→ `no-marker-output`. `probe.js` had always done this correctly
|
|
157
|
+
(`.replace(/__JSON_BEGIN__/g, …)`); desktop.js now matches it.
|
|
158
|
+
2. **`@(['Progman',…])` is a PowerShell parse error** (`lib/desktop.js`).
|
|
159
|
+
`SHELL_CLASSES_PS` wrapped the list in `[ … ]`, and the call site wrapped
|
|
160
|
+
that again in `@( … )` → *"Missing type name after '['"*, aborting the
|
|
161
|
+
probe before any window was enumerated.
|
|
162
|
+
3. **Every selection guard silently fell through** (`lib/desktop.js`).
|
|
163
|
+
`selectCleanupTargets` read `w.hwnd` / `w.process` / `w.class` / `w.title`,
|
|
164
|
+
but the embedded probe emits **PascalCase** (`Hwnd` / `Process` / `Class` /
|
|
165
|
+
`Title`). All comparisons were `undefined !== anything`, so the foreground
|
|
166
|
+
guard, the shell-class filter **and the protected-process guarantee** did
|
|
167
|
+
nothing — DSH Desktop itself, explorer and Taskmgr landed in the close list.
|
|
168
|
+
Rows are now normalised through one `raw.x ?? raw.X` shim.
|
|
169
|
+
4. **CLI crashed printing the plan** (`lib/desktop-cli.mjs`). `t.title.slice()`
|
|
170
|
+
on the same casing mismatch → `TypeError: Cannot read properties of
|
|
171
|
+
undefined`. Shares the normaliser now, and tolerates empty titles.
|
|
172
|
+
|
|
173
|
+
### Protected set widened (user-approved, beyond upstream)
|
|
174
|
+
|
|
175
|
+
Upstream protected only `powershell / pwsh / python / pythonw`. Each addition
|
|
176
|
+
below was justified against a live probe, not guessed:
|
|
177
|
+
|
|
178
|
+
| added | why |
|
|
179
|
+
|---|---|
|
|
180
|
+
| `dsh desktop` | closing the harness host kills the session |
|
|
181
|
+
| `explorer` | shell/taskbar; multiple file-manager Hwnds share one pid |
|
|
182
|
+
| `taskmgr` | closing Task Manager is a forced-end acting on other apps |
|
|
183
|
+
| `fluent`, `launcher1` | Fluent Launcher / solver front-ends are long-running CFD work |
|
|
184
|
+
|
|
185
|
+
### Verification
|
|
186
|
+
|
|
187
|
+
- `node tests/logic.test.mjs` — **24/24 still pass** (no regression)
|
|
188
|
+
- live probe: enumerates 30 windows, 10 visible consoles
|
|
189
|
+
- `desktop-cli.mjs list` — 14–18 targets, **0** protected/foreground/shell-class
|
|
190
|
+
windows selected (DSH Desktop, explorer, Taskmgr, Fluent all correctly kept)
|
|
191
|
+
- `desktop-cli.mjs cleanup` — dry run prints the plan, closes nothing
|
|
192
|
+
|
|
193
|
+
## 0.1.0 (2026-09-16) — first MVP
|
|
194
|
+
|
|
195
|
+
Host-side cordis plugin: auto-hide idle AI/script console windows on Windows.
|
|
196
|
+
|
|
197
|
+
### Configured defaults (per user's MVP decision)
|
|
198
|
+
- `autoAction: hide` (lossless; process keeps running, `show` restores it)
|
|
199
|
+
- `idleThresholdSec: 60`, `cpuThresholdPct: 1`, `spikeTolerance: 2`
|
|
200
|
+
- scope: `wmiDetached` + `dshHost` + `scriptConsole` ON, `orphaned` OFF
|
|
201
|
+
- host-side only; no client bundle in v1
|
|
202
|
+
|
|
203
|
+
### Empirical findings fixed during development
|
|
204
|
+
1. **Windows Terminal windows are classified by title, not parent chain.**
|
|
205
|
+
A WT top-level window belongs to the WindowsTerminal process (parent
|
|
206
|
+
svchost), not to the script it hosts — parent-chain classification left
|
|
207
|
+
all 8 stacked black windows as `manual` (unmanaged). Added `script-console`
|
|
208
|
+
source keyed on the console title being a script executable path.
|
|
209
|
+
(Interactive consoles keep prompt-style titles like "Windows PowerShell"
|
|
210
|
+
and stay unmanaged.)
|
|
211
|
+
2. **`SW_SHOW(5)` cannot restore a hidden WT window.** Measured: after
|
|
212
|
+
`SW_HIDE`, `SW_SHOW` leaves `IsWindowVisible` false; `SW_SHOWNORMAL(1)`
|
|
213
|
+
restores it. Show action now uses SW_SHOWNORMAL with SW_RESTORE fallback.
|
|
214
|
+
3. **Single-sample CPU spikes reset the idle clock.** The WindowsTerminal
|
|
215
|
+
host process spikes briefly even when all its sessions are idle, so
|
|
216
|
+
stacked windows never accumulated 60s of idle. `spikeTolerance=2` now
|
|
217
|
+
requires two consecutive busy samples to break an idle streak.
|
|
218
|
+
4. **`NtQueryInformationProcess` over CIM for parent-pid.** Measured 39 s vs
|
|
219
|
+
24 ms for 13 processes (`Get-CimInstance Win32_Process`). Full probe
|
|
220
|
+
~350 ms at a 10 s scan interval.
|
|
221
|
+
5. **dsh-tools output-schema constraints**: object schemas must declare
|
|
222
|
+
`additionalProperties` explicitly; output value schemas reject `required`.
|
|
223
|
+
Caught only by the real-runtime load precheck, not by `node --check`.
|
|
224
|
+
|
|
225
|
+
### Verification
|
|
226
|
+
- `node tests/logic.test.mjs` — 24/24 (classification, idle accumulation,
|
|
227
|
+
spike tolerance, scope guards, GC, ordering)
|
|
228
|
+
- `node tests/e2e.mjs` — live probe (24 windows), reversible hide→show
|
|
229
|
+
- load precheck against the profile's real cordis/schemastery/dsh-tools —
|
|
230
|
+
3 tools register, scheduler effect mounts, config defaults correct
|
|
231
|
+
- privacy audit: 0 hits across 9 files; `npm pack` 8 files / 16.1 kB
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 loyalchiiina
|
|
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-es.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Console Sentinel
|
|
2
|
+
|
|
3
|
+
> Watch the browser console and DSH logs, surface errors early, and auto-hide finished console windows.
|
|
4
|
+
|
|
5
|
+
> Full documentation: [English README](./README.md)
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
> 📖 [Leer esta sección en el README principal](./README.md#how-it-works)
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
> 📖 [Leer esta sección en el README principal](./README.md#install)
|
|
14
|
+
|
|
15
|
+
## Tools (model-visible)
|
|
16
|
+
|
|
17
|
+
> 📖 [Leer esta sección en el README principal](./README.md#tools-model-visible)
|
|
18
|
+
|
|
19
|
+
## Config (cordis.yml)
|
|
20
|
+
|
|
21
|
+
> 📖 [Leer esta sección en el README principal](./README.md#config-cordisyml)
|
|
22
|
+
|
|
23
|
+
## Safety
|
|
24
|
+
|
|
25
|
+
> 📖 [Leer esta sección en el README principal](./README.md#safety)
|
|
26
|
+
|
|
27
|
+
## Development
|
|
28
|
+
|
|
29
|
+
> 📖 [Leer esta sección en el README principal](./README.md#development)
|
package/README-hi.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Console Sentinel
|
|
2
|
+
|
|
3
|
+
> Watch the browser console and DSH logs, surface errors early, and auto-hide finished console windows.
|
|
4
|
+
|
|
5
|
+
> Full documentation: [English README](./README.md)
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
> 📖 [यह अनुभाग मुख्य README में पढ़ें](./README.md#how-it-works)
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
> 📖 [यह अनुभाग मुख्य README में पढ़ें](./README.md#install)
|
|
14
|
+
|
|
15
|
+
## Tools (model-visible)
|
|
16
|
+
|
|
17
|
+
> 📖 [यह अनुभाग मुख्य README में पढ़ें](./README.md#tools-model-visible)
|
|
18
|
+
|
|
19
|
+
## Config (cordis.yml)
|
|
20
|
+
|
|
21
|
+
> 📖 [यह अनुभाग मुख्य README में पढ़ें](./README.md#config-cordisyml)
|
|
22
|
+
|
|
23
|
+
## Safety
|
|
24
|
+
|
|
25
|
+
> 📖 [यह अनुभाग मुख्य README में पढ़ें](./README.md#safety)
|
|
26
|
+
|
|
27
|
+
## Development
|
|
28
|
+
|
|
29
|
+
> 📖 [यह अनुभाग मुख्य README में पढ़ें](./README.md#development)
|
package/README-pt.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Console Sentinel
|
|
2
|
+
|
|
3
|
+
> Watch the browser console and DSH logs, surface errors early, and auto-hide finished console windows.
|
|
4
|
+
|
|
5
|
+
> Full documentation: [English README](./README.md)
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
> 📖 [Ler esta seção no README principal](./README.md#how-it-works)
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
> 📖 [Ler esta seção no README principal](./README.md#install)
|
|
14
|
+
|
|
15
|
+
## Tools (model-visible)
|
|
16
|
+
|
|
17
|
+
> 📖 [Ler esta seção no README principal](./README.md#tools-model-visible)
|
|
18
|
+
|
|
19
|
+
## Config (cordis.yml)
|
|
20
|
+
|
|
21
|
+
> 📖 [Ler esta seção no README principal](./README.md#config-cordisyml)
|
|
22
|
+
|
|
23
|
+
## Safety
|
|
24
|
+
|
|
25
|
+
> 📖 [Ler esta seção no README principal](./README.md#safety)
|
|
26
|
+
|
|
27
|
+
## Development
|
|
28
|
+
|
|
29
|
+
> 📖 [Ler esta seção no README principal](./README.md#development)
|
package/README-zh.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# dsh-console-sentinel
|
|
2
|
+
|
|
3
|
+
> 盯住浏览器控制台与 DSH 日志,提前发现报错,并自动隐藏已经跑完的控制台窗口。
|
|
4
|
+
|
|
5
|
+
> 完整文档以 [English README](./README.md) 为准,本页提供中文导读与关键信息速查。
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
本节内容详见 [English README](./README.md)。
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
本节内容详见 [English README](./README.md)。
|
|
14
|
+
|
|
15
|
+
## Tools (model-visible)
|
|
16
|
+
|
|
17
|
+
本节内容详见 [English README](./README.md)。
|
|
18
|
+
|
|
19
|
+
## Config (cordis.yml)
|
|
20
|
+
|
|
21
|
+
本节内容详见 [English README](./README.md)。
|
|
22
|
+
|
|
23
|
+
## Safety
|
|
24
|
+
|
|
25
|
+
本节内容详见 [English README](./README.md)。
|
|
26
|
+
|
|
27
|
+
## Development
|
|
28
|
+
|
|
29
|
+
本节内容详见 [English README](./README.md)。
|
package/README.en.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# dsh-console-sentinel
|
|
2
|
+
|
|
3
|
+
Auto-hide (or close) idle AI/script console windows on Windows — a
|
|
4
|
+
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin.
|
|
5
|
+
|
|
6
|
+
Long-running harness workflows (unattended Fluent jobs, batch scripts,
|
|
7
|
+
WMI-launched detached processes) leave PowerShell/Python console windows
|
|
8
|
+
stacked in the taskbar. You cannot tell which are still working and you do not
|
|
9
|
+
want to kill them by hand. This plugin tracks them, tells busy from finished,
|
|
10
|
+
and hides the finished ones for you — the process keeps running, nothing is
|
|
11
|
+
lost, and every window can be brought back on demand.
|
|
12
|
+
|
|
13
|
+
## How it works
|
|
14
|
+
|
|
15
|
+
1. Every `scanIntervalSec` seconds (default 10) it enumerates console windows
|
|
16
|
+
with a zero-dependency Windows PowerShell probe (`user32` + `ntdll`
|
|
17
|
+
P/Invoke): `CASCADIA_HOSTING_WINDOW_CLASS` (Windows Terminal),
|
|
18
|
+
`ConsoleWindowClass` (classic conhost), and `PseudoConsoleWindow` (ConPTY).
|
|
19
|
+
2. Each console's process is classified: by its **parent process**
|
|
20
|
+
(`WmiPrvSE.exe` → `wmi-detached` — WMI-launched detached jobs, how
|
|
21
|
+
unattended Fluent runs are started; `DSH Desktop.exe` → `dsh-host`;
|
|
22
|
+
parent already exited → `orphaned`), or, when the parent chain is
|
|
23
|
+
uninformative (a Windows Terminal window belongs to the WindowsTerminal
|
|
24
|
+
process, not to the script it hosts), by the **console title**: a scripted
|
|
25
|
+
session defaults its title to the launched executable path
|
|
26
|
+
(`...\python.exe`), while an interactive console keeps a prompt-style
|
|
27
|
+
title (`Windows PowerShell`) → `script-console`.
|
|
28
|
+
3. CPU usage is sampled per scan (process total CPU time deltas). A window
|
|
29
|
+
under `cpuThresholdPct` (default 1%) for `idleThresholdSec` (default 60)
|
|
30
|
+
counts as **finished**. Single-sample background jitter does not reset the
|
|
31
|
+
idle clock (`spikeTolerance`, default 2).
|
|
32
|
+
4. Finished windows **in scope** get the configured `autoAction`
|
|
33
|
+
(default `hide` = `SW_HIDE`; the process keeps running). `close` sends
|
|
34
|
+
`WM_CLOSE`. The focused window is never touched (`guardForeground`).
|
|
35
|
+
|
|
36
|
+
Parent-pid lookup uses `NtQueryInformationProcess` rather than CIM
|
|
37
|
+
(`Get-CimInstance Win32_Process`), which measured **39 s vs 24 ms** for 13
|
|
38
|
+
processes on the reference machine. A full probe takes ~350 ms.
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
dsh plugin --profile <name> add dsh-console-sentinel
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Bundle plugin (`dsh.bundle.patch`): enters the profile bundle stack, takes
|
|
47
|
+
effect on restart. Windows-only — the patch row is `disabled` on every other
|
|
48
|
+
platform and `apply()` also guards at runtime.
|
|
49
|
+
|
|
50
|
+
## Tools (model-visible)
|
|
51
|
+
|
|
52
|
+
- `console_windows_list` — every tracked console window with idle state and
|
|
53
|
+
origin classification. Use this first.
|
|
54
|
+
- `console_window_action` — hide / show / minimize / restore / close one
|
|
55
|
+
window (by `hwnd` or `pid`). `hide` is lossless; `close` may lose unsaved
|
|
56
|
+
output. Pass `force` to act on the focused window.
|
|
57
|
+
- `console_windows_sweep` — apply the action to every idle in-scope window at
|
|
58
|
+
once (handy after a batch of Fluent jobs).
|
|
59
|
+
- `desktop_windows_cleanup` — **close all other windows**: close every desktop
|
|
60
|
+
app window except the focused one, self, shell classes, and protected
|
|
61
|
+
processes (`dryRun` first for a plan; `protected` / `keepTitle` / `keepPid`
|
|
62
|
+
add temporary protection).
|
|
63
|
+
- `sentinel_whitelist` — manage the **persistent exemption whitelist**:
|
|
64
|
+
`list` / `add` / `remove` / `reset`, `kind=process` (image name, e.g.
|
|
65
|
+
`code.exe`) or `kind=title` (title substring). Applies to both auto-actions
|
|
66
|
+
and desktop cleanup; stored at `~/.dsh/console-sentinel/whitelist.json`.
|
|
67
|
+
|
|
68
|
+
## Config (cordis.yml)
|
|
69
|
+
|
|
70
|
+
| Key | Default | Meaning |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `enabled` | `true` | master switch |
|
|
73
|
+
| `scanIntervalSec` | `10` | how often to scan + sample CPU |
|
|
74
|
+
| `idleThresholdSec` | `60` | sustained-low-CPU seconds before a window counts as finished |
|
|
75
|
+
| `cpuThresholdPct` | `1` | average CPU below this = idle |
|
|
76
|
+
| `autoAction` | `hide` | `none` \| `hide` \| `close` |
|
|
77
|
+
| `probeTimeoutMs` | `20000` | hard probe timeout |
|
|
78
|
+
| `spikeTolerance` | `2` | consecutive busy samples needed to reset the idle clock |
|
|
79
|
+
| `guardForeground` | `true` | never auto-manage the focused window |
|
|
80
|
+
| `scope.wmiDetached` | `true` | manage WMI-launched detached consoles |
|
|
81
|
+
| `scope.dshHost` | `true` | manage host-spawned consoles |
|
|
82
|
+
| `scope.orphaned` | `false` | manage consoles whose parent already exited |
|
|
83
|
+
| `scope.scriptConsole` | `true` | manage consoles whose title is a script executable path (Windows Terminal windows hosting a scripted session) |
|
|
84
|
+
| `scope.excludeTitles` | `[]` | title substring blocklist |
|
|
85
|
+
| `scope.emptyConsole` | `true` | treat an interpreter console (PowerShell/Python/cmd with only conhost and **no workload child**) as an empty shell and manage it regardless of parent chain — the safe way to clean up leftover empty PowerShell windows without enabling the whole `orphaned` scope (which would also match mpiexec / fl2610 / live compute) |
|
|
86
|
+
| `emptyConsoleAction` | `hide` | action for empty shells: `none` \| `hide` \| `close`; independent of `autoAction` |
|
|
87
|
+
| `whitelist.processes` | `[]` | exempt image names (e.g. `code.exe`), never auto-managed nor bulk-closed (edit at runtime via `sentinel_whitelist`) |
|
|
88
|
+
| `whitelist.titles` | `[]` | exempt title substrings, never auto-managed nor bulk-closed |
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
- id: dsh-console-sentinel
|
|
92
|
+
config:
|
|
93
|
+
idleThresholdSec: 90
|
|
94
|
+
scope:
|
|
95
|
+
excludeTitles:
|
|
96
|
+
- keep-alive
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Safety
|
|
100
|
+
|
|
101
|
+
`hide` only calls `ShowWindow(SW_HIDE)` — the process, its logs, and its
|
|
102
|
+
output are untouched; `console_window_action` with `show` restores it
|
|
103
|
+
(`SW_SHOWNORMAL` — measured: `SW_SHOW` leaves a hidden Windows Terminal
|
|
104
|
+
window invisible). `close` sends `WM_CLOSE` (graceful, same as clicking the
|
|
105
|
+
X) but may lose unsaved state, so it is never the default. Automatic actions
|
|
106
|
+
never touch the focused window, manually opened consoles (`manual` source),
|
|
107
|
+
title-blocklisted or **whitelisted** windows, or any process in
|
|
108
|
+
`HARD_PROTECTED_PROCESSES` (explorer, taskmgr, dsh desktop,
|
|
109
|
+
fl2610/cx2610/mpiexec/smpd/fluent, etc.). Every action is emitted as
|
|
110
|
+
`console-sentinel/action` and logged.
|
|
111
|
+
|
|
112
|
+
## Development
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
lib/index.js Cordis plugin: config, scan scheduler, tools
|
|
116
|
+
lib/logic.js pure ledger/idle state machine + empty-console detection (unit-tested)
|
|
117
|
+
lib/probe.js embedded PowerShell probe + window actions
|
|
118
|
+
lib/desktop.js desktop-wide window close (一键关闭其他窗口)
|
|
119
|
+
lib/whitelist.js persistent exemption whitelist (JSON)
|
|
120
|
+
tests/ node-only regression tests, no live Windows needed
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`node --test tests/*.test.mjs` — 51 assertions (logic 29 + desktop 17 +
|
|
124
|
+
tool-output 5) covering classification, idle accumulation, busy-blip reset,
|
|
125
|
+
scope guards, empty-console detection, whitelist, GC, and ordering.
|
|
126
|
+
|
|
127
|
+
MIT License.
|
package/README.md
CHANGED
|
@@ -1,3 +1,126 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-console-sentinel
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
自动隐藏(或关闭)Windows 上空闲的 AI/脚本控制台窗口 —— 一个
|
|
4
|
+
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 插件。
|
|
5
|
+
|
|
6
|
+
长时间运行的自动化工作流(Fluent 无人值守任务、批量脚本、WMI 拉起的脱离进程)
|
|
7
|
+
会在任务栏堆下一排 PowerShell/Python 黑窗口。你分不清哪些还在跑,也不敢随手关。
|
|
8
|
+
本插件替你追踪它们、区分「在忙」和「用完了」,并把用完的隐藏掉 —— 进程继续跑、
|
|
9
|
+
数据不丢,每个窗口都能随时叫回来。
|
|
10
|
+
|
|
11
|
+
## 工作原理
|
|
12
|
+
|
|
13
|
+
1. 每隔 `scanIntervalSec` 秒(默认 10)用零依赖的 Windows PowerShell 探针
|
|
14
|
+
(`user32` + `ntdll` P/Invoke)枚举控制台窗口:`CASCADIA_HOSTING_WINDOW_CLASS`
|
|
15
|
+
(Windows Terminal)、`ConsoleWindowClass`(传统 conhost)、
|
|
16
|
+
`PseudoConsoleWindow`(ConPTY 伪控制台)。
|
|
17
|
+
2. 按进程归类:优先看**父进程**(父进程是 `WmiPrvSE.exe` → `wmi-detached`,
|
|
18
|
+
WMI 拉起的脱离任务,无人值守 Fluent 计算正是这样启动的;父进程是
|
|
19
|
+
`DSH Desktop.exe` → `dsh-host`;父进程已退出 → `orphaned`)。当父链说明
|
|
20
|
+
不了问题时(Windows Terminal 的窗口归属于 WindowsTerminal 进程,而不是
|
|
21
|
+
它里面跑的脚本),改看**控制台标题**:脚本启动的控制台标题默认是
|
|
22
|
+
被启动程序的路径(`...\python.exe`),而交互式手开的控制台是提示符式
|
|
23
|
+
标题(`Windows PowerShell`)→ 归为 `script-console`。
|
|
24
|
+
3. 每轮采样进程 CPU(累计 CPU 时间差值)。CPU 低于 `cpuThresholdPct`(默认 1%)
|
|
25
|
+
且持续 `idleThresholdSec`(默认 60 秒)即判定为**用完了**。单次采样的后台
|
|
26
|
+
CPU 抖动不会重置空闲时钟(`spikeTolerance`,默认 2)。
|
|
27
|
+
4. 在管理范围内的「用完了」窗口执行配置的 `autoAction`(默认 `hide` = `SW_HIDE`,
|
|
28
|
+
进程继续跑)。`close` 发送 `WM_CLOSE`。前台窗口永不自动处理(`guardForeground`)。
|
|
29
|
+
|
|
30
|
+
父进程 PID 用 `NtQueryInformationProcess` 而非 CIM(`Get-CimInstance Win32_Process`)
|
|
31
|
+
—— 参考机上实测 **39 秒 vs 24 毫秒**(13 个进程)。单次完整探针约 350 毫秒。
|
|
32
|
+
|
|
33
|
+
## 安装
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
dsh plugin --profile <name> add dsh-console-sentinel
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Bundle 插件(`dsh.bundle.patch`):进入 profile bundle 栈,重启后生效。
|
|
40
|
+
仅限 Windows —— patch 行在其他平台 `disabled`,`apply()` 运行时也会再守一次。
|
|
41
|
+
|
|
42
|
+
## 工具(模型可见)
|
|
43
|
+
|
|
44
|
+
- `console_windows_list` —— 列出所有被追踪的控制台窗口及其空闲状态和来源归类。先用它。
|
|
45
|
+
- `console_window_action` —— 对单个窗口执行 hide / show / minimize / restore / close
|
|
46
|
+
(用 `hwnd` 或 `pid` 定位)。`hide` 无损;`close` 可能丢失未保存输出。
|
|
47
|
+
对前台窗口操作需传 `force`。
|
|
48
|
+
- `console_windows_sweep` —— 一次性对所有空闲且在范围内的窗口执行动作
|
|
49
|
+
(跑完一批 Fluent 任务后清任务栏很好用)。
|
|
50
|
+
- `desktop_windows_cleanup` —— **一键关闭其他窗口**:关闭桌面上除前台、自身、
|
|
51
|
+
系统外壳和受保护进程以外的所有应用窗口(`dryRun` 先出计划;`protected` /
|
|
52
|
+
`keepTitle` / `keepPid` 可临时加固保护)。
|
|
53
|
+
- `sentinel_whitelist` —— 管理**持久化豁免白名单**:`list` / `add` / `remove` / `reset`,
|
|
54
|
+
`kind=process`(按映像名,如 `code.exe`)或 `kind=title`(按标题子串)。
|
|
55
|
+
白名单同时作用于自动隐藏/关闭与一键关闭窗口,落盘到
|
|
56
|
+
`~/.dsh/console-sentinel/whitelist.json`。
|
|
57
|
+
|
|
58
|
+
## 设置面板(DSH 设置页 · 0.3.0+)
|
|
59
|
+
|
|
60
|
+
插件在 DSH「设置」里注册「控制台哨兵」分区(客户端 bundle `lib/client.js`),
|
|
61
|
+
不记工具名也能点操作:
|
|
62
|
+
|
|
63
|
+
- **概览**:总窗口 / 空壳 PowerShell / 可见空壳 / 已隐藏 计数 + 「隐藏可见空壳」。
|
|
64
|
+
- **任务栏窗口清理**:预演 / 一键关闭其他窗口(前台、系统进程、白名单自动跳过)。
|
|
65
|
+
- **控制台窗口**:逐行显示进程名 / 标题 / hwnd / pid / CPU / 空闲秒数 / 子进程数,行内可隐藏 / 显示 / 关闭。
|
|
66
|
+
- **豁免白名单**:进程名与标题关键字 标签式增删、清空,即时落盘。
|
|
67
|
+
|
|
68
|
+
面板通过宿主路由 `/dsh-console-sentinel/*` 取数(客户端 fetch 根相对路径,桌面端自动带
|
|
69
|
+
capability header),不使用 iframe。改 `lib/client.js` 后按 **Ctrl+Shift+R** 硬刷新;
|
|
70
|
+
改宿主(`lib/index.js` / `package.json`)需**完全退出 DSH 再启动**。
|
|
71
|
+
|
|
72
|
+
## 配置(cordis.yml)
|
|
73
|
+
|
|
74
|
+
| 键 | 默认值 | 含义 |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `enabled` | `true` | 总开关 |
|
|
77
|
+
| `scanIntervalSec` | `10` | 扫描 + CPU 采样间隔 |
|
|
78
|
+
| `idleThresholdSec` | `60` | 持续低 CPU 多少秒算「用完了」 |
|
|
79
|
+
| `cpuThresholdPct` | `1` | CPU 低于此值算空闲 |
|
|
80
|
+
| `autoAction` | `hide` | `none` \| `hide` \| `close` |
|
|
81
|
+
| `probeTimeoutMs` | `20000` | 探针硬超时 |
|
|
82
|
+
| `spikeTolerance` | `2` | 连续多少次「忙」采样才重置空闲时钟 |
|
|
83
|
+
| `guardForeground` | `true` | 永不自动处理前台窗口 |
|
|
84
|
+
| `scope.wmiDetached` | `true` | 管理 WMI 拉起的脱离控制台 |
|
|
85
|
+
| `scope.dshHost` | `true` | 管理宿主派生的控制台 |
|
|
86
|
+
| `scope.orphaned` | `false` | 管理父进程已退出的控制台 |
|
|
87
|
+
| `scope.scriptConsole` | `true` | 管理标题是脚本可执行文件路径的控制台(Windows Terminal 承载的脚本会话) |
|
|
88
|
+
| `scope.excludeTitles` | `[]` | 标题 substring 黑名单 |
|
|
89
|
+
| `scope.emptyConsole` | `true` | 把「没有任何工作子进程」的解释器控制台(PowerShell/Python/cmd 只剩 conhost)判为**空壳**,无论父链如何都纳入管理 —— 这是安全清理残留空 PowerShell 的开关(不必打开整个 `orphaned`,后者会误伤 mpiexec/fl2610/在跑的计算) |
|
|
90
|
+
| `emptyConsoleAction` | `hide` | 对空壳控制台的动作:`none` \| `hide` \| `close`;独立于 `autoAction`,即便自动动作关闭也能单独生效 |
|
|
91
|
+
| `whitelist.processes` | `[]` | 豁免进程映像名(如 `code.exe`),永不自动处理/一键关闭(运行时用 `sentinel_whitelist` 增删) |
|
|
92
|
+
| `whitelist.titles` | `[]` | 豁免标题子串,永不自动处理/一键关闭 |
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
- id: dsh-console-sentinel
|
|
96
|
+
config:
|
|
97
|
+
idleThresholdSec: 90
|
|
98
|
+
scope:
|
|
99
|
+
excludeTitles:
|
|
100
|
+
- keep-alive
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## 安全性
|
|
104
|
+
|
|
105
|
+
`hide` 只调用 `ShowWindow(SW_HIDE)` —— 进程、日志、输出一概不动;
|
|
106
|
+
`console_window_action` 的 `show` 可随时恢复(用 `SW_SHOWNORMAL` ——
|
|
107
|
+
实测 `SW_SHOW` 无法恢复被隐藏的 Windows Terminal 窗口)。`close` 发 `WM_CLOSE`
|
|
108
|
+
(优雅关闭,和你点 X 一样),但可能丢失未保存状态,所以从不是默认动作。
|
|
109
|
+
自动动作绝不触碰:前台窗口、手动打开的控制台(`manual` 来源)、
|
|
110
|
+
标题命中黑名单或**白名单**的窗口、以及 `HARD_PROTECTED_PROCESSES` 里的系统/计算进程
|
|
111
|
+
(explorer、taskmgr、dsh desktop、fl2610/cx2610/mpiexec/smpd/fluent 等)。
|
|
112
|
+
每个动作都发 `console-sentinel/action` 事件并写日志。
|
|
113
|
+
|
|
114
|
+
## 开发
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
lib/index.js Cordis 插件:配置、扫描调度、工具
|
|
118
|
+
lib/logic.js 纯台账/空闲状态机(单测覆盖)
|
|
119
|
+
lib/probe.js 内嵌 PowerShell 探针 + 窗口动作
|
|
120
|
+
tests/ 仅 node 的回归测试,不需要真实 Windows
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`node tests/logic.test.mjs` —— 24 项断言,覆盖来源归类、空闲累积、
|
|
124
|
+
忙碌脉冲重置、范围守卫、台账 GC、排序。
|
|
125
|
+
|
|
126
|
+
MIT License。
|