@sidady/dsh-steward 0.0.0-stage → 0.9.12
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/ACCEPTANCE.md +204 -0
- package/LICENSE +21 -0
- package/README.md +339 -2
- package/cordis.patch.yml +9 -0
- package/icon.svg +4 -0
- package/lib/actions.js +534 -0
- package/lib/brain.js +167 -0
- package/lib/client.js +1587 -0
- package/lib/index.js +780 -0
- package/lib/intent.js +298 -0
- package/lib/models.js +212 -0
- package/lib/report.js +1696 -0
- package/lib/settings.js +286 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +63 -4
- package/smoke.mjs +1381 -0
package/ACCEPTANCE.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# 两周无误报验收表(回看日:2026-11-23)
|
|
2
|
+
|
|
3
|
+
> 用途:管理逻辑总纲第 3 条验收标准「连续两周无误报」的可执行检查口径。
|
|
4
|
+
> 起点 = 2026-11-09(v0.2.1 新鲜度门落地后 level 首次稳定在 ok)。
|
|
5
|
+
> 回看时逐条打勾,任何一条 ✗ 都要写清原因;标 N/A 的条目要写明「为什么两周内不具备条件」。
|
|
6
|
+
>
|
|
7
|
+
> **2026-11-10 修订(v0.7.7):基线整体重采,并新增 A5–A8 四行。** 原因是 11-09 之后管家新增了四个检测项
|
|
8
|
+
> (工作区体积、插件更新、可信度自检、静音到期),每一个都带来**新的误报面**,而旧表只覆盖了崩溃/归因那一条线。
|
|
9
|
+
> 拿旧尺子量新报告,会让最该被检查的四项恰好无人检查。
|
|
10
|
+
>
|
|
11
|
+
> **2026-11-10 二次修订(v0.8.0):新增 A9「设置项是否可被越界值带偏」,基线升到 v0.8.0 / 162 断言。**
|
|
12
|
+
> 理由是这轮把 11 项判据的松紧打开了,等于把「阈值」从代码常量变成了用户数据。
|
|
13
|
+
> 一旦用户把某个阈值调到失真(填 0、填负数、填字符串、填一个等于关闭该项的极端值),
|
|
14
|
+
> 报告会变得**比误报更难发现**——它会安静地什么都不报,而你以为它在看着。
|
|
15
|
+
> A9 要抓的正是这种「静默失效」,它比误报更危险,因为误报会喊、它会闭嘴。
|
|
16
|
+
>
|
|
17
|
+
> **2026-11-10 三次修订(v0.9.8):基线升到 v0.9.8 / 252 断言,并新增 A10–A12 三行。**
|
|
18
|
+
> 这轮又加了三类检测(patch 备份结构、client 标识、模型健康),它们各自有独立的误报面;
|
|
19
|
+
> 更重要的是其间真实发生过一次**「修法本身变成误报」**——v0.9.6 把「patch 比备份小 20%」判成
|
|
20
|
+
> warning,真机立刻打脸(14 个 `- id:` 块一个没少,变小发生在块内字段),v0.9.7 才改成按结构判。
|
|
21
|
+
> A10 就是把这次教训钉成一条可复查的判据:**检测项自己的判据也要被验收**,不能因为「新加的」就免检。
|
|
22
|
+
>
|
|
23
|
+
> **2026-11-10 四次修订(v0.9.11):基线升到 v0.9.11 / 274 断言,新增 A13 与 D5 两行。**
|
|
24
|
+
> v0.9.9–v0.9.11 这三轮没有新增检测项,改的全是「说得对但说得不准」;但这两行还是必须加,
|
|
25
|
+
> 因为它们抓的是**修完之后仍然没人看的地方**:
|
|
26
|
+
> - **A13**「一个数字被两个问题共用」——崩溃条目把「距当前启动多久」(49 秒)当成「多久以前」(实际 7 小时)。
|
|
27
|
+
> 两个数各自都算对了,合在一句话里就错。同一类错误还包括列表截断(只列 6 个却写「7 个工作区」)。
|
|
28
|
+
> - **D5**「测试全绿但包装不上」——v0.9.9 的 `files` 漏了 `lib/models.js`,
|
|
29
|
+
> 从源码树跑的 255 条断言全绿,装包的人一启动就 `ERR_MODULE_NOT_FOUND`,整个 Web UI 起不来。
|
|
30
|
+
>
|
|
31
|
+
> 两者的共同点值得单独记一笔:**本地一切正常,只有换一个人装、或换一个时间点读,才暴露**。
|
|
32
|
+
> 前者要换视角,后者要换读者——这正是自测最容易漏的两类。
|
|
33
|
+
>
|
|
34
|
+
> 另外**基线必须跟着换**:否则 11-23 回看时会拿 v0.9.8 的断言数(252)去比当时的 281,
|
|
35
|
+
> 把「覆盖变多了」误读成「有东西坏了」。
|
|
36
|
+
>
|
|
37
|
+
> 还有一条**静态判据**的用法:像「渲染处不许把 ISO 的 T 换成空格」这类规约(v0.9.11),
|
|
38
|
+
> 与其等它在界面上出错,不如直接扫源码——但静态判据必须**验证过会红**,否则它只是装饰。
|
|
39
|
+
|
|
40
|
+
> **2026-11-10 五次修订(v0.9.12):基线升到 v0.9.12 / 281 断言,新增 A14 一行。**
|
|
41
|
+
> 这轮换的是配色,看似「只是好看」,实际改的是**信息语义**:原来选中态靠变蓝表示,
|
|
42
|
+
> 而蓝在同一块面板里还表示 info 级发现——颜色同时承担「选中」和「严重度」两件事,
|
|
43
|
+
> 看的人就没法只看颜色判断状态。改成 baseline 的反相选中后,颜色只剩「数据」这一个含义。
|
|
44
|
+
> A14 要抓的是回流:`check.mjs` 只抓「硬编码的灰」,抓不到「自造一套蓝」——
|
|
45
|
+
> 那只是另一种饱和度,checker 无从判断谁对,所以真正的判据落在了 smoke 的静态扫描里。
|
|
46
|
+
>
|
|
47
|
+
## 基线快照(2026-11-10 15:40 采集(v0.9.12 重采),回看时用来对照)
|
|
48
|
+
|
|
49
|
+
| 项 | 基线值 |
|
|
50
|
+
|---|---|
|
|
51
|
+
| bundle 版本 | `@sidady/dsh-steward` v0.9.12,installed + enabled(ping 与 package.json 一致) |
|
|
52
|
+
| 本地验证 | 9 个 JS `node --check` 全过,`smoke.mjs` **281 passed / 0 failed** |
|
|
53
|
+
| 体检结论 | `level=warning`,`counts={critical:0, warning:2, info:10, muted:0}`,`errors=[]`,共 12 条 findings |
|
|
54
|
+
| crash 日志 | **4 份**:`crash-2026-11-07T02-53-24-868Z-web-boot.log`(10199 B) / `crash-2026-11-08T09-10-33-857Z-web-boot.log`(61087 B) / `crash-2026-11-09T23-27-20-521Z-web-boot.log`(31821 B) / `crash-2026-11-09T23-28-38-158Z-web-boot.log`(31821 B) |
|
|
55
|
+
| audit.jsonl | 40 行(v0.9.8 起有 2000 行上限;`ok:false` 行仍在,说明拒绝路径活着) |
|
|
56
|
+
| trend.json | **4 个点**,最新 `2026-11-10T03:50:33.499Z`;`cFreeBytes` 87.3 GB(更早一点 89.4 GB,不是恒定值) |
|
|
57
|
+
| acked.json | 空数组 `[]`(v0.7.7 起 ack 带 `expiresAt`) |
|
|
58
|
+
| 工作区 | 7 个 / 合计约 **192 MB**(采集时 201341401 B;这个数每次都在变,回看时只看「7 个」与增长判定,不要咬死体积);判据是**增长**不是绝对体积,本轮「与上次相比无显著增长」 |
|
|
59
|
+
| 插件更新 | 32 个已装包比对,13 个有新版本 + 3 个**已装比缓存新**(缓存偏旧,不算升级),0 个无法比较 |
|
|
60
|
+
| 数据源覆盖 | `coverage` **7/7** = `data`,`unavailable=[]`,`errors=[]` |
|
|
61
|
+
| 配置指纹 | `cordis.patch.yml` `1b85f7154b1261fd`(60159 B / 1994 行) / `package.json` `dc6c2f83d584c1a0`(2784 B / 87 行)。version 升会改 `package.json` 指纹,属正常;**块数变少**才报警 |
|
|
62
|
+
| 设置 | `settings.json` 存在,`savedAt=2026-11-10T03:58:45.090Z`;`ttl.plugin-updates=14`(已回默认),`ui.entry=right`,`ui.toast=critical`,`ui.toastSeconds=12`,`model.probeOnReport=cached`,`perProvider=2`,`maxTotal=12`,`growthWarnMb=20` |
|
|
63
|
+
| 客户端包 | 38 个声明了 `dsh.client`,checked 38 / unreadable 0 / withoutId 0;`inject` 只声明 `@deepseek-ai/dsh-client-ui-slots`(locale 已于 v0.9.10 去掉虚挂) |
|
|
64
|
+
| 默认模型 | 12 个哨兵探针:8 ok / 0 dead / 4 busy(busy = 「没有返回内容」,不算坏);默认模型可用 4504 ms |
|
|
65
|
+
| 加密备份 | 2 份,最新 `dsh-config-2026-11-09-1749ba.zip`(95.7 MB)1 天前;另一份 `dsh-config-20261108-180014-ab10fd.zip`(65.6 MB) |
|
|
66
|
+
|
|
67
|
+
> **版本号与基线数字的关系**:本表的数字是「当天真实跑出来的」,不是「必须永远等于这个」;
|
|
68
|
+
> 回看时若断言数已经涨了(比如又加了几条),那是覆盖变多,记一笔新数字即可,不算退化。
|
|
69
|
+
> 真正要盯的是:crash 日志有没有新增、`errors` 是否仍为空、`coverage` 是否仍 7/7。
|
|
70
|
+
> **回看前先做一件事**:把 `package.json` 的 version 与 ping 的 version 对一下。两者不等说明
|
|
71
|
+
> 磁盘改了但宿主没重载——此时面板看到的是旧代码,任何 A 项的结论都不作数。
|
|
72
|
+
|
|
73
|
+
## 检查表
|
|
74
|
+
|
|
75
|
+
### A. 稳定性(核心:误报)
|
|
76
|
+
|
|
77
|
+
| # | 检查项 | 数据源 | 打勾条件 |
|
|
78
|
+
|---|---|---|---|
|
|
79
|
+
| A1 | critical 是否为 0,或每条 critical 都有现行证据 | 面板「体检总览」+ crash 日志目录 | critical=0 → ✅ 直接打勾;critical>0 → 逐条回答「这条有对应的、晚于本次启动的崩溃日志吗」,全部有证才算 ✅ |
|
|
80
|
+
| A2 | 是否新增了 crash 日志 | `%APPDATA%\@deepseek-ai\dsh-desktop\logs\crash-*.log` 文件数 | 仍为 **4 份** → ✅;新增的话,每份都必须在报告里被正确点名并给出建议,且没有连坐错点名 |
|
|
81
|
+
| A3 | 被点名的插件「此刻是否仍在坏」 | 报告 `data.suspects` + `plugin_manager list_bundles` | 没有出现「点名了一个其实正常运行的插件」→ ✅(这是 v0.2.2 归因门要守的那条线) |
|
|
82
|
+
| A4 | 陈旧证据是否都不再冒充现行风险 | 报告里 boot-crash-latest 的 `data.stale` | 若有历史崩溃,其 severity 必须是 info 且标题带「历史…当前实例启动正常」→ ✅ |
|
|
83
|
+
|
|
84
|
+
### A5–A9:v0.7.7 / v0.8.0 新检测项各自的误报面
|
|
85
|
+
|
|
86
|
+
| # | 检查项 | 数据源 | 打勾条件 |
|
|
87
|
+
|---|---|---|---|
|
|
88
|
+
| A5 | 工作区体检是否成了「永远在喊的固定阈值」 | 报告 `workspace-inventory` 的 severity、连续 N 次体检里是否一直 warning | 口径已改为**增长判定**(净增 ≥ 20 MB 才升 warning,另有孤儿目录判定)。若体积没长还天天 warning → ❌;若两周内确有增长并报了 → ✅ |
|
|
89
|
+
| A6 | 插件更新检出是否把常态当发现 | 报告 `plugin-updates` 的 severity | 必须是 **info**,不得升 warning;若它哪次升了 warning → ❌ 记原因 |
|
|
90
|
+
| A7 | 可信度自检是否制造了恒常噪音 | 报告 `confidence` finding + `coverage.unavailable` | `unavailable` 为空时不产生 confidence finding(零噪音)→ ✅;若某个源长期 `unavailable` 导致每次体检都有一条 info,且你从未按它的话去修 → 记 ❌ |
|
|
91
|
+
| A8 | 静音到期是否变成「反复催同一件事」 | `acked.json` 的 `expiresAt` + 审计里的 ack/unack 记录 | 同一 finding 在一个月内因过期回来 **超过 2 次**,且你两次都没有采取行动 → ❌ |
|
|
92
|
+
| A9 | 设置项是否可被越界值带偏(**抓静默失效**) | `settings.json` + `POST {"action":"settings"}` 的 `groups` | 四项都满足才 ✅:①每个值在 SPEC `[min,max]` 内;②填 `0`/负数/`"abc"` 返回 `clamped`/`rejected` 且不静默吞;③阈值调极端时对应 finding **确实不再报**(仍按默认报=没接上,整份空白=波及别的检测);④恢复默认立刻回基线 |
|
|
93
|
+
|
|
94
|
+
### A10–A13:v0.9.x 新检测项各自的误报面(本次新增)
|
|
95
|
+
|
|
96
|
+
| # | 检查项 | 数据源 | 打勾条件 |
|
|
97
|
+
|---|---|---|---|
|
|
98
|
+
| A10 | patch 备份是否又退化成「按字节判丢」 | 报告 `patch-backup` 的 severity + `data.missing` | 判据必须是**结构式**:把 `.bak-<epoch>` 与当前 patch 的 `- id:` 块取差集,只有「整块消失」才 warning;仅体积变小必须是 info 且标题带「一个没少」。若两周内出现过「只小没丢却报 warning」→ ❌(v0.9.6 犯过一次,v0.9.7 修) |
|
|
99
|
+
| A11 | client 标识体检是否把「读不到」当「没问题」 | 报告 `client-identity` 的 `data` 三计数 | 必须 `unreadable` 与 `withoutId` 单独计数,且标题里的「N 个包」只等于 `checked`(不是「声明了 client 的包总数」)。若 `unreadable>0` 而标题仍报「通过」→ ❌(v0.9.5 前犯过:实际只核对 28/38 却按 38 报通过) |
|
|
100
|
+
| A12 | 模型健康是否把「忙」当「坏」 | 报告 `model-health` 的 `data.state` + `deadModels` | 只把 404/422(上游明确拒绝路由)与「连续两轮拿不到内容」算坏;429/5xx/超时/空回复一律记 busy 且不升 warning、不进 `deadModels`。若某个免费模型只是一时冷却却被写成「已不存在」→ ❌(这是最容易被免费额度误伤的一项) |
|
|
101
|
+
| A13 | 「多久以前」与「比启动早多久」有没有被混成一个数 | 报告 `boot-crash-latest` 的 `data.ageHours` / `bootLagHours` / `ageText` / `bootLagText` | 四个数各司其职:`ageHours` 是**距现在**多久、`bootLagHours` 是**距本次启动**多久,两者可以相差很大(真机 7.3 小时 vs 0.01 小时)。标题/正文里出现「约 0 小时前」而事件实际已过去数小时 → ❌(v0.9.10 前就是这样)。同类要一并查:列表截断有没有说出「另有 N 个」(工作区 / 备份 / 采集告警,v0.9.10) |
|
|
102
|
+
| A14 | 配色是不是又从 baseline 溜回自造色 | `node check.mjs ../dsh-steward/lib/client.js` + `smoke.mjs` 的配色静态判据 | 两个都必过:checker 报 `no hardcoded neutral` 不给 error 只说明「没有硬编码的灰」,**抓不到「自造一套蓝」**(那是另一种饱和度,不是灰);所以真正的判据是 smoke 那 7 条 —— baseline 块外不许出现非中性的 `rgba()/rgb()`、不许出现 `var(--dsw` 之外的裸 hex、选中态必须用 `C.solid`/`C.on` 反相。若面板上又出现 `rgba(90,159,212,…)` 这类蓝、或选中态靠变蓝表示 → ❌(v0.9.12 前就是这样,12 处) |
|
|
103
|
+
|
|
104
|
+
### B. 可回溯(每次动作都能查账)
|
|
105
|
+
|
|
106
|
+
| # | 检查项 | 数据源 | 打勾条件 |
|
|
107
|
+
|---|---|---|---|
|
|
108
|
+
| B1 | 每次执行都有审计行 | `audit.jsonl` 与面板「审计趋势」Tab | 台账条数 = 文件行数、时间戳单调递增 → ✅ |
|
|
109
|
+
| B2 | 口令从未落盘 | `audit.jsonl` 全文 | grep `password` / `passwd` / `口令` / `secret` 零命中 → ✅ |
|
|
110
|
+
| B3 | 拒绝路径在干活(门没形同虚设) | `audit.jsonl` 的 `ok:false` 行 | 两周内至少出现过一次 `unknown-or-expired-plan` 或 `confirm-missing` → ✅ |
|
|
111
|
+
|
|
112
|
+
### C. 守底线
|
|
113
|
+
|
|
114
|
+
| # | 检查项 | 数据源 | 打勾条件 |
|
|
115
|
+
|---|---|---|---|
|
|
116
|
+
| C1 | 没有代跑提权命令 | 会话记录 + `audit.jsonl` | 全期零次 `DISM` / `diskpart compact` / 卸载程序被 Agent 执行(只呈现过建议)→ ✅ |
|
|
117
|
+
| C2 | 没有「未经授权就动手」 | `audit.jsonl` 每条 `kind` ∈ {clean,backup,disable,enable,knowledge,ack,unack,model-default} 且都有 `hash` | 不存在 hash 为空却 `ok:true` 的执行行 → ✅ |
|
|
118
|
+
| C3 | 口令红线:11-16 周提醒只提醒不代做 | 会话记录(2026-11-16 10:00 那次触发) | Agent 没有索要口令、没有代跑、没有提议代跑 → ✅;任何一项违反 → ❌ 并回写 USER.md |
|
|
119
|
+
| C4 | 坏请求不会触发写动作 | 对路由发畸形 body | `{oops` / `{}` / 超 16 KB / JSON 数组 必须全部 400,且**不得**因此跑一次体检(v0.9.8 前会把空 action 当 report)→ ✅ |
|
|
120
|
+
|
|
121
|
+
### D. 自愈与降级
|
|
122
|
+
|
|
123
|
+
| # | 检查项 | 数据源 | 打勾条件 |
|
|
124
|
+
|---|---|---|---|
|
|
125
|
+
| D1 | 依赖缺失只降级不崩溃 | `node smoke.mjs` 的 fail-soft 断言 | 回看当天跑一次 smoke,仍全绿(基线 281 passed / 0 failed)→ ✅(若期间又加断言,以当天全绿为准并记下新数字) |
|
|
126
|
+
| D2 | 热挂载后落库一致 | profile `package.json` + `/api/dsh-steward/` 200 + ping 版本 | grep `dsh-steward`=2、路由 200、ping 的 `version` = `package.json` 的 `version` 三者一致 → ✅。**注意**:`remove_bundle` 会因 `bundle-in-use` 失败,实际路径是 `set_bundle enabled:false → true`;ping 版本正确也**不**等于新代码已加载(读的是磁盘 package.json),要看新文案是否出现 |
|
|
127
|
+
| D3 | 可选数据源缺失不拖垮报告 | 报告 `status` 与 `coverage` | 缺一个可选源时 `status` 仍是 `ok`、只在 `coverage` 里记 `unavailable` → ✅ |
|
|
128
|
+
| D4 | 慢请求不会无限等待 | 面板发一次 `model-scan` | 服务端 3 路并发(12 探针最坏 ≈180 s),前端 `report`/`model-scan` 预算 240 s、普通动作 30 s。若面板转圈超过 5 分钟仍无任何提示 → ❌ |
|
|
129
|
+
| D5 | 打包白名单是否覆盖全部源码(**发布前必做**) | `npm pack --dry-run` 的文件清单 vs `lib/*.js` | `files` 必须包含 `lib/` 下每一个 JS;`smoke.mjs` 里的打包自检必须全绿(它会把 `lib/` 与 `files` 取差集,并解析所有 `from './x.js'`)。反向验证过:临时抽掉一个文件,自检应当 FAIL 并点名(v0.9.9 补上漏掉的 `lib/models.js`,否则装包的人一启动就 `ERR_MODULE_NOT_FOUND`、整个 Web UI 起不来) |
|
|
130
|
+
|
|
131
|
+
### E. 知识闭环(智能的雏形)
|
|
132
|
+
|
|
133
|
+
| # | 检查项 | 数据源 | 打勾条件 |
|
|
134
|
+
|---|---|---|---|
|
|
135
|
+
| E1 | 趋势有连续记录 | `trend.json` | ≥3 个时间点且 `cFreeBytes` 不是恒定值 → ✅;不足 3 点 → 标 N/A |
|
|
136
|
+
| E2 | 同类问题第二次出现时能召回上次结论 | 报告 finding 上的 `🧠 上次也出现过` 提示 | 两周内出现过同类问题 → 召回必须命中;没出现过 → 标 N/A |
|
|
137
|
+
|
|
138
|
+
## 打勾时怎么取数
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# 1) 审计与趋势(v0.9.8 起 audit 上限 2000 行,行数不再无界增长)
|
|
142
|
+
wc -l ~/.dsh/dsh-steward/audit.jsonl && cat ~/.dsh/dsh-steward/trend.json
|
|
143
|
+
|
|
144
|
+
# 2) crash 日志基数(基线 4 份)
|
|
145
|
+
ls -la "$APPDATA/@deepseek-ai/dsh-desktop/logs/" | grep crash
|
|
146
|
+
|
|
147
|
+
# 3) 静音与到期时间
|
|
148
|
+
cat ~/.dsh/dsh-steward/acked.json
|
|
149
|
+
|
|
150
|
+
# 3b) 设置项现状
|
|
151
|
+
cat ~/.dsh/dsh-steward/settings.json
|
|
152
|
+
curl -s -X POST http://127.0.0.1:<当前端口>/api/dsh-steward/ -H 'content-type: application/json' -d '{"action":"settings"}'
|
|
153
|
+
|
|
154
|
+
# 4) 落库与版本一致(三者必须一致)
|
|
155
|
+
grep -c dsh-steward ~/.dsh/profiles/desktop/package.json
|
|
156
|
+
node -e "console.log(require('./dsh-steward/package.json').version)"
|
|
157
|
+
curl -s -X POST http://127.0.0.1:<当前端口>/api/dsh-steward/ -H 'content-type: application/json' -d '{"action":"ping"}'
|
|
158
|
+
|
|
159
|
+
# 5) 坏请求不该跑体检(C4)
|
|
160
|
+
curl -s -o /dev/null -w '%{http_code}\n' -X POST http://127.0.0.1:<当前端口>/api/dsh-steward/ -H 'content-type: application/json' -d '{oops'
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
> 端口每轮启动都会变,用运行时上下文里的 GUI URL,不要沿用旧端口。
|
|
164
|
+
> 取数时**别把 curl 管进 head**:`curl | head -c 300` 会让 head 提前关管道触发 SIGPIPE,
|
|
165
|
+
> curl 自己死掉、拿到一串 `status=000`,看起来像服务端崩了。用 `-o 文件` 落盘再读。
|
|
166
|
+
|
|
167
|
+
## A9 预演记录(2026-11-10,真机跑过一遍,回看时当对照)
|
|
168
|
+
|
|
169
|
+
四个子条件当时全部通过,因此 11-23 回看时如果任一项反而坏了,就是**退化**而不是「本来就是那样」:
|
|
170
|
+
|
|
171
|
+
| 子条件 | 当时怎么验的 | 当时结果 |
|
|
172
|
+
|---|---|---|
|
|
173
|
+
| ①值在范围内 | `cat settings.json` + `{"action":"settings"}` 的 `groups` | 全部在 `[min,max]` 内 |
|
|
174
|
+
| ②越界夹紧 / 非数字拒绝 | 分别提交 `growthWarnMb:0` 与 `plugin-updates:"abc"` | `clamped=[{key:"threshold.growthWarnMb",from:0,to:1}]`;`ok=false` + `rejected=[{key:"ttl.plugin-updates"}]` |
|
|
175
|
+
| ③设置真的到达检测 | 把 `collect.maxLogs` 改成 1 后重跑 report | `limitations` 第 5 条真的变成「日志 ≤ 1 份」——证明值穿透到了采集层 |
|
|
176
|
+
| ④恢复默认立刻回基线 | 把 `maxLogs` 改回 50 再跑 report | 文案回到「≤ 50 份」、`counts` 与基线一致 |
|
|
177
|
+
|
|
178
|
+
值得记住的坑:③ 当时用的是 `collect.maxLogs` 而不是某个阈值,因为**阈值的失效不一定会马上显形**
|
|
179
|
+
(改了增长门槛,可能本轮恰好没有增长数据、报告还是那样,看不出设置到底接没接上);
|
|
180
|
+
而 `limitations` 文案是每轮必出的,它变了就等于拿到了「值确实到了检测层」的直接证据。
|
|
181
|
+
回看时若想验阈值本身,得先造出能触发它的数据,再用「改前报 / 改后不报」两边对照。
|
|
182
|
+
|
|
183
|
+
## 判定口径(避免事后含糊)
|
|
184
|
+
|
|
185
|
+
- **误报**的定义:报告在**当下**要求你处置,但你实际去看时**无事可做**。不是「标题不好看」,也不是「这条算 info 还是 warning」。
|
|
186
|
+
- **可接受的降级**:依赖缺失导致某张动作卡不可用、报告里给的是可读错误 → 不算失误,算 fail-soft 正常表现。
|
|
187
|
+
- **不可接受的降级**:任何一次让界面起不来、让面板点不动、让体检结果为空却显示 ok → 直接判 ❌,并升级为事故记录。
|
|
188
|
+
- **静态阈值不等于检测**:一个固定数字(如「超过 50 MB」)在你没变化时会天天亮,那不是发现问题,是把阈值当成了问题。A5/A8 就是专门抓这个的。
|
|
189
|
+
- **静默失效比误报更危险**:误报会让你烦,静默失效会让你放心。设置项一旦被调成失真值,报告会安静地什么都不报——所以 A9 里「调极端值后对应 finding 真的不再报」与「默认值真的回到基线」**两个方向都要验**,只验一边等于没验。
|
|
190
|
+
- **覆盖率不许冒充结论**:核对 28 个包、漏掉 10 个,就写 28 个;标题里的数字必须是**真正核对过的**那个,不是「声明了的」那个(A11)。
|
|
191
|
+
- **新检测项自己也要被验收**:新增一条检测时,它自己的判据也在被检验范围内。v0.9.6 的 patch-backup 就是「新加的判据本身是误报」——**「这次修法是否把 A 误报换成了 B 误报」要成为每轮加检测后的固定一问**(A10 就是这个教训的落地)。
|
|
192
|
+
- **一个数字只能回答一个问题**:v0.9.10 之前崩溃条目把「距当前启动多久」(49 秒)当成「多久以前」(其实已过 7 小时)来措辞,两者都对、合起来就错。回看时若见到「约 0 小时前」「N 秒前」这类与墙钟明显不符的时间,先问这个数在回答哪个问题,再改文案(v0.9.10 / A13 的思路)。
|
|
193
|
+
- **时间戳只有两种合法写法**:存 UTC(ISO 带 Z),显示本地;不接受 `replace('T',' ')` 这种「看起来本地化」实则仍是 UTC 的写法(v0.9.11)。文本报告故意保留 UTC 并标明,因为那份可能被别时区的程序读——**标明时区比猜一个更负责**。
|
|
194
|
+
- **依赖声明必须与代码一致**:`dsh.client.inject` 里声明了却零调用的包就是虚挂,它今天不报错、将来宿主改注入方式时会以「找不到依赖」的形式炸掉(v0.9.10 去掉了 locale)。判定方式是 grep 实际调用,不是看别的插件怎么写。
|
|
195
|
+
- **静态判据也必须验证会红**:像「渲染处不许把 ISO 的 T 换成空格」这种扫源码的断言,写完要故意改回旧写法确认它 FAIL,否则它只是装饰(v0.9.11 做了这次反向验证)。这条对所有 `smoke.mjs` 里的规约型断言都适用。
|
|
196
|
+
- **测试全绿不等于包能装**:`files` 白名单漏一个文件时,从源码树跑的断言全绿、装包的人一启动就 `ERR_MODULE_NOT_FOUND` 并拖垮整个 Web UI(v0.9.9 的 `lib/models.js`)。发布前必须跑一次 `npm pack` 并解包核对相对 import(D5)。
|
|
197
|
+
- **info 不算打扰,warning 才算**:判误报时先看 severity。info 天天在、你从不理它,是噪音的候选而非误报;warning 天天在、你无事可做,就是误报。
|
|
198
|
+
- 出现 ❌ 时不改判定标准,只记原因;判定标准本身要改,必须写下「为什么原标准不可执行」,不许事后放宽。
|
|
199
|
+
|
|
200
|
+
## 回看结论栏(11-23 填)
|
|
201
|
+
|
|
202
|
+
- 总体:✅ 通过 / 🟡 部分 / ❌ 不通过
|
|
203
|
+
- 未通过项与原因:
|
|
204
|
+
- 需要写入定稿的更正:
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 sidady
|
|
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,340 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-steward — 智能管家(体检 + 授权执行)插件
|
|
2
|
+
|
|
3
|
+
**状态:v0.9.12 已安装并热挂载(体检 + 授权执行 + 对话入口 + 设置项)**
|
|
4
|
+
|
|
5
|
+
## v0.9.12(配色:从「自造一套蓝」改成 baseline 的六个 accent)
|
|
6
|
+
|
|
7
|
+
面板里原本散着 12 处私有颜色:`rgba(90,159,212,.14)` 这种自造的蓝、`#e05555` 这种自造的红、
|
|
8
|
+
`rgba(79,180,119,.14)` 这种自造的绿。`dsh-design-baseline` 的 `check.mjs` 抓不到它们,因为那不是
|
|
9
|
+
「硬编码的灰」——它只是另一套饱和度、另一种色相,checker 无从判断谁对。
|
|
10
|
+
|
|
11
|
+
按 baseline 的铁律改了三件事:
|
|
12
|
+
|
|
13
|
+
1. **颜色只来自六个 accent**。自造蓝 → `C.info`(`#06b6d4`),自造红 → `C.critical`(`#f43f5e`),
|
|
14
|
+
自造绿 → `C.ok`(`#22c55e`)。剩下的几档透明度不再手写,统一用 `mix(accent, pct)` 派生,
|
|
15
|
+
与 baseline 的 15% `wash()` 同源。
|
|
16
|
+
2. **选中态反相,不染色**。胶囊按钮、单选行、排序药丸、发送键原来是「被选中就变蓝」,这直接违反
|
|
17
|
+
baseline 那条「选中若是颜色,颜色就不再表示数据」。改成 `C.solid` / `C.on`(宿主反相背景/前景)——
|
|
18
|
+
和 FreeLLMAPI 的 segmented 控件是同一个做法。
|
|
19
|
+
3. **红点不是另一种红**。侧栏图标那个 7px 红点原本是 `#e05555` + 一圈黑边;现在点用 `C.critical`、
|
|
20
|
+
边用 `THEME.surface`(那是背景的镂空,不是描边)。
|
|
21
|
+
|
|
22
|
+
刻意没做的:`sync.mjs` 在 tokens 模式下只给 `THEME`,不给 `BASELINE_CSS`,因为本插件的红线是
|
|
23
|
+
「不注入 `<style>`、不碰 `document.head`」。所以这里拿到的是同一组 token 与 accent,
|
|
24
|
+
而不是 FreeLLMAPI 那种整块样式表注入——**两者的相同点在取值,不在注入方式**。
|
|
25
|
+
|
|
26
|
+
`smoke.mjs` 加了 7 条静态判据(总数 274 → 281),其中最关键的两条都反向验证过会红:
|
|
27
|
+
块外出现 `rgba(90,159,212,.2)` → `FAIL no private accent survives outside the baseline block`;
|
|
28
|
+
块外出现 `#7a4dff` → `FAIL no stray hex colour outside the baseline block`。
|
|
29
|
+
|
|
30
|
+
## v0.9.11(时间戳:UTC 存、本地显示)
|
|
31
|
+
|
|
32
|
+
面板上「生成时间 · 2026-11-10T06:32:40.118Z」这类样子,是把 UTC 原样贴给了东八区的用户——
|
|
33
|
+
差了 8 小时,而读者无从判断该减还是该加。审计行、趋势行、静音项的「上次也出现过」三处都这样。
|
|
34
|
+
|
|
35
|
+
修法:加一个 `localStamp(iso)`,把 `Date.parse` 后的时间按 `getFullYear/getMonth/...` 本地化,
|
|
36
|
+
五个显示点全部改走它(审计行、趋势行、recall 时间、重启前存档时间、生成时间)。
|
|
37
|
+
生成时间那一行还顺手补了相对时间:「生成时间 · 2026-11-10 14:32(刚刚)」。
|
|
38
|
+
|
|
39
|
+
服务端 `renderReportText` 是给工具调用返回的纯文本报告,**显式标注 `(UTC)`** 而不是悄悄换成本地——
|
|
40
|
+
那份文本可能被下游程序或其他时区的人读,写清楚比猜对更有用。
|
|
41
|
+
|
|
42
|
+
smoke 从 271 项加到 274 项,其中一条是静态判据:**源码里不许出现「把 T 换成空格」这种假装本地化的写法**;
|
|
43
|
+
我当场把一处改回旧写法验证过它会红(`FAIL no render path formats a timestamp by swapping the ISO T`)。
|
|
44
|
+
|
|
45
|
+
## v0.9.10(把「说得对」修成「说得准」)
|
|
46
|
+
|
|
47
|
+
这一版没有加新体检项,只修三处「数字对、话不对」的地方。
|
|
48
|
+
|
|
49
|
+
**1. 崩溃的「多久以前」和「比启动早多少」是两个问题,不能共用一个数。**
|
|
50
|
+
旧代码用 `bootedAt - crashTime` 算小时数并四舍五入,真机上两者只差 49 秒,
|
|
51
|
+
于是正文写「约 0 小时前」——而那件事其实发生在 7 小时前。现在分开算:
|
|
52
|
+
`ageText` 用 `now - crashTime` 说人话(「7 小时前」),`bootLagText` 用
|
|
53
|
+
`bootedAt - crashTime` 说「比当前实例的启动还早 49 秒」。两个数都进 `data`
|
|
54
|
+
(`ageHours` / `ageText` / `bootLagHours` / `bootLagText`)。
|
|
55
|
+
|
|
56
|
+
**2. 列表截断必须自己说出来。**
|
|
57
|
+
标题写「7 个工作区」、正文只列 6 条,读者分不清少的那一个是不重要的还是最重要的。
|
|
58
|
+
现在工作区、备份 evidence、采集告警三处都在截断处补上「另有 N 个……(完整见 data.xxx)」,
|
|
59
|
+
并在 `data` 里给出真正的总数(`workspacesTotal` / `countNote` / `data.errors`)。
|
|
60
|
+
|
|
61
|
+
**3. 依赖声明不许虚挂。**
|
|
62
|
+
`dsh.client.inject` 里一直写着 `@deepseek-ai/dsh-client-locale`,但 client.js 全文
|
|
63
|
+
零调用 locale(红线本来就是「不用 locale」)。已删掉这条声明;`locale/*.json` 保留
|
|
64
|
+
(它由宿主读来显示插件名与简介),meta 文案同步到当前的「体检 + 授权执行」口径。
|
|
65
|
+
|
|
66
|
+
smoke 从 255 项加到 271 项:新增的四组断言专门钉这次的三个毛病——
|
|
67
|
+
「正文里不许出现『约 0 小时前』」「截断处必须带 N」「inject 只许写真用到的包」。
|
|
68
|
+
|
|
69
|
+
## v0.9.9(打包自检:测试全绿不等于包能装)
|
|
70
|
+
|
|
71
|
+
0.9.8 发布前跑 `npm pack --dry-run`,发现包里**没有 `lib/models.js`**——`package.json` 的 `files` 白名单漏了它。
|
|
72
|
+
|
|
73
|
+
这条缺陷的形状值得记住:**所有单元测试都是绿的**,因为测试从源码树 import,那里文件当然在;
|
|
74
|
+
而装包的人拿到的是 tarball,`import './models.js'` 会抛 `ERR_MODULE_NOT_FOUND`,
|
|
75
|
+
宿主在 boot 期审计 manifest,一条没进 active 就整个 Web UI 起不来——和 11-09 那次崩溃是同一类后果,只是从另一条路走到。
|
|
76
|
+
|
|
77
|
+
修法两层:把 `lib/models.js` 补进 `files`;再给 smoke 加一条**打包自检**——遍历 `lib/` 与 `files` 白名单取差集,
|
|
78
|
+
并解析所有 `from './x.js'` 确认目标文件会被发布。这条断言当场验证过会红
|
|
79
|
+
(拿掉 `lib/models.js` → `FAIL every file in lib/ is in the files whitelist — models.js`),不是写完就算数。
|
|
80
|
+
|
|
81
|
+
顺带修了 README 的安装一节:它还写着「`- insert:` 的 name 要等于 `dsh-steward`」,那是改名前的旧口径,
|
|
82
|
+
照它做会让从 npm 装的人整个界面起不来。
|
|
83
|
+
|
|
84
|
+
## v0.9.8(健壮性:一条会卡住,两条语义不对)
|
|
85
|
+
|
|
86
|
+
三条都是一次真实的压力测试打出来的,不是读代码猜的:
|
|
87
|
+
|
|
88
|
+
1. **请求可以永远等下去。** 前端 `call()` 没有 `AbortController`,服务端 `runModelScan`
|
|
89
|
+
是严格串行的 for 循环。默认 12 个探针 × 45 秒超时 = 最坏 9 分钟。实测 `model-scan`
|
|
90
|
+
用 115.6 秒、`probeOnReport=always` 时一次体检 151.2 秒——面板只是在转圈,
|
|
91
|
+
用户分不清「还在跑」和「已经死了」。
|
|
92
|
+
修法:服务端改 3 路并发(最坏 180 秒);前端给每个请求一个预算——普通动作 30 秒,
|
|
93
|
+
`report` / `model-scan` 240 秒(180 秒最坏 + 60 秒给报告自身的采集器),`ask` 120 秒。
|
|
94
|
+
超时后说的是「服务端可能还在跑」,不是「失败了」。
|
|
95
|
+
|
|
96
|
+
2. **坏输入会静默触发最贵的动作。** `readBody` 解析失败一律返回 `{}`,而 `run()` 把
|
|
97
|
+
`action === ''` 当成「做一次完整体检」。于是畸形 JSON、超长 body(>16 KB)、
|
|
98
|
+
甚至空对象,都返回 200 加一份 29 KB 的完整报告。
|
|
99
|
+
修法:`readBody` 改返回 `{parsed, value, reason}`,坏 body → 400 并说明原因;
|
|
100
|
+
缺 action → 400 并告诉对方该发什么。**不再有默认动作。**
|
|
101
|
+
|
|
102
|
+
3. **两个 jsonl 没有上限。** `audit.jsonl` 和 `knowledge.jsonl` 只追加,而 `trend.json`
|
|
103
|
+
早就有 400 行上限。同一种文件两套规则。
|
|
104
|
+
修法:`LOG_LINE_CAP = 2000`,超了保留最新 2000 行;`io` 缺 `readText`/`writeText`
|
|
105
|
+
时跳过裁剪而不是让写入失败。
|
|
106
|
+
|
|
107
|
+
一条断言写错过,而且错得有用:我原本给 `report` 写 200 秒预算,断言却是
|
|
108
|
+
`12 * 45000 / 3 + 60000`(= 240 秒)——两个数字对不上,说明我心里算的最坏情况
|
|
109
|
+
和写下的不一样。现在断言直接把 `Math.ceil(MAX_TOTAL / CONCURRENCY) * PROBE_TIMEOUT_MS`
|
|
110
|
+
算出来再比,不写死数字。
|
|
111
|
+
|
|
112
|
+
## v0.9.7(patch 备份:按结构判,不按字节判)
|
|
113
|
+
|
|
114
|
+
v0.9.6 把「当前 patch 比启动期备份小 20% 以上」判成 warning,真机立刻打脸:
|
|
115
|
+
本机 patch 52 KB vs 备份 82 KB(小 42%),报「明显小于」——
|
|
116
|
+
可逐块拆开对比,**14 个 `- id:` 块一个都没少**。少的是块内字段:
|
|
117
|
+
`dsh-connect-workbuddy` 少了 `global:` 段、`dsh-connect-qoder` 的 `enabledModelKeys` 被清空。
|
|
118
|
+
那可能是插件自己把默认值重写成空的,也可能是选择被重置——但这**不等于配置丢了**。
|
|
119
|
+
拿总字节数当判据,就是把「小」当成「丢」,正是这份报告一直在批评的毛病。
|
|
120
|
+
|
|
121
|
+
现在按结构判:
|
|
122
|
+
|
|
123
|
+
- 备份里的 `- id:` 块在当前 patch 里**缺了** → warning,标题点名缺哪几个 id;
|
|
124
|
+
- 块都在、只是块内字段变少 → info,标题直说「配置块一个没少」,正文给出缩小百分比并说明
|
|
125
|
+
「不等于配置丢了,但值得你自己对着 .bak 看一眼具体是哪几个字段」。
|
|
126
|
+
|
|
127
|
+
smoke 由 235 增至 **238** 条,正反两个方向都锁住(变小不报警 / 真缺块必报警并点名)。
|
|
128
|
+
## v0.9.6(patch 备份与磁盘建议:别再拿旧快照当现状)
|
|
129
|
+
|
|
130
|
+
同一条毛病的另外两处,都是「把曾经发生过的事当成此刻的状态」。
|
|
131
|
+
|
|
132
|
+
**patch 备份**:以前只要目录里有 `cordis.patch.yml.bak-<epoch>` 就报 warning。
|
|
133
|
+
但那只是启动期 sanitizeProfile 改名留下的残留——真正要紧的是**现在的 patch 有没有被换成默认 stub**。
|
|
134
|
+
现在会比较两者体积:当前 patch 比备份小 20% 以上 → warning,并算出少了百分之多少;
|
|
135
|
+
否则 → info,标题直说「当前配置未被换掉」。本机实测就是后者(备份 82 KB / 当前 81 KB)。
|
|
136
|
+
|
|
137
|
+
**磁盘建议**:`disk-advice` 的 evidence 里写死了某一次 audit 的数字(131.9 GB / WSL 81.4 GB / WinSxS 12.9 GB),
|
|
138
|
+
报告却把它当现状读。现在改成只留判据与分类口径,具体体积以 `disk_cleaner action=audit` 的实时结果为准。
|
|
139
|
+
|
|
140
|
+
smoke 由 231 增至 **235** 条(新增「残留 → info」「被换成 stub → warning 且算出百分比」两条正反断言)。
|
|
141
|
+
## v0.9.5(client 标识体检:把「读不到」和「没问题」分开)
|
|
142
|
+
|
|
143
|
+
v0.9.3 的 client 标识体检有一个不诚实的角落:它说「38 个已装客户端包的 id 互不冲突」,
|
|
144
|
+
同时在括号里承认「10 个包的 client.js 读不到」——覆盖其实只有 28/38,结论却按 38 报通过。
|
|
145
|
+
|
|
146
|
+
两处漏检,都是同一个毛病:默认了一个并不成立的约定。
|
|
147
|
+
|
|
148
|
+
- **入口不一定是 `lib/client.js`**。10 个真机包把 client 入口写在 `exports["./client"]` 里
|
|
149
|
+
(`dshmarket` → `client/client.js`、`ds-bot` → `client.js`、`deep-view` → `client.js`、
|
|
150
|
+
`@liustack/modsearch` → `dsh/client.js`、`billion-context` → `dist/agent/dsh-native-client.js` …)。
|
|
151
|
+
现在按 `exports["./client"]` → `dsh.client` → `lib/client.js` → `client.js` → `dist/client.js` 依次解析,
|
|
152
|
+
并把解析到的路径记进 `entry` 字段。真机 `unreadable` 由 10 降到 **0**。
|
|
153
|
+
- **id 不一定用引号包**。压缩产物会用反引号:`@changfenhuang/dsh-genui`、`dsh-context` 都是
|
|
154
|
+
``id:`…` ``,只认 `'` 和 `"` 会把它们算成「没声明 id」。现在三种引号都接受;
|
|
155
|
+
`dsh-cost-meter` 把调用包在 load-wrap 代理后面(模块体里根本没有 `__ModuleLoader__` 字面量),
|
|
156
|
+
再加一条宽松回退取第一个带 `id` 的对象字面量。真机 `withoutId` 由 3 降到 **0**。
|
|
157
|
+
|
|
158
|
+
现在「读不到」和「没问题」在报告里长得不一样了:真的读不到会单独计数并写进 detail,
|
|
159
|
+
而不是混在「通过」里。smoke 由 227 增至 **231** 条。
|
|
160
|
+
## v0.9.4(插件更新体检:按方向比,不再「不同即升级」)
|
|
161
|
+
|
|
162
|
+
上一版把「缓存里的 latest 和已装版本不一样」当成「有新版本」。真机立刻暴露了它的错:
|
|
163
|
+
报告里出现 `dsh-disk-cleaner 1.6.3 → 1.6.2`、`dsh-game-center 1.2.1 → 1.0.1` —— 那是**降级**。
|
|
164
|
+
原因是市场缓存比磁盘旧,而代码只做了不等比较,没有判方向。
|
|
165
|
+
|
|
166
|
+
现在 `collectUpdates` 用 semver 比方向,分成三种结果:
|
|
167
|
+
|
|
168
|
+
- `latest > installed` → 进 `outdated`,才是「有新版本可用」;
|
|
169
|
+
- `latest < installed` → 进 `aheadOfCache`,那不是该升级,是**缓存比磁盘旧**;
|
|
170
|
+
- 版本号写法无法比较(如 `beta`)→ 计入 `undetermined`,不猜方向。
|
|
171
|
+
|
|
172
|
+
面板文案跟着分叉:有真更新时,也把「缓存偏旧」和「无法比较」的数量说出来,不再静默丢掉;
|
|
173
|
+
全部最新时同样说明有几项是缓存偏旧。**一个不报的项和一项没查的项,在报告里必须长得不一样。**
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
## v0.9.3(client 标识体检 + 崩溃病因解析)
|
|
177
|
+
|
|
178
|
+
上次那次「整个 Web UI 起不来」暴露了两道盲区,这一版各补一条:
|
|
179
|
+
|
|
180
|
+
1. **client 标识体检**(info 级,新增第 7 个数据源)。DSH 渲染端按 `__ModuleLoader__.load({ id })` 的 id 建工厂表,同一个 id 注册第二次直接抛 `duplicate factory registration` 并让 boot 失败。体检核对三件事:同一份代码是否被装成多个 node_modules 名字(用 `realpath` 比目录真身)、client.js 的 id 是否与包名一致、两个包是否抢同一个 id。**这是市场现有体检的盲区**:`dshmarket` 的静态分析按 loader entry id / bundle 名统计,`trialValidate` 回放的也是 loader 组合树,两者都看不见渲染端的工厂表(实测 `grep ModuleLoader|dsh.client|clientId|client.js check.js trial.js` 零匹配;三个合成夹具上市场体检全绿,本项全命中)。
|
|
181
|
+
2. **崩溃病因解析**。真实崩溃日志的 renderer console 段写着 `client-modules: duplicate factory registration for "X"`,而 `X: import failed` 只是输掉竞争的那个。旧解析只抓后者,于是「装了两遍」被诊断成「某个插件坏了」。现在 `parseBootFailure` 同时抓病因,`boot-crash-latest` 的标题以病因开头,并明说被点名的插件是受害者。
|
|
182
|
+
|
|
183
|
+
这条检测**有意不给一键处置**:修法是删一个 symlink 或改别的包的 id,属于动别人安装状态,超出授权执行面——只点名路径,不代改。
|
|
184
|
+
|
|
185
|
+
## 为什么 client id 必须等于包名(v0.9.2 事故复盘)
|
|
186
|
+
|
|
187
|
+
包名从 `dsh-steward` 改成 `@sidady/dsh-steward` 时只改了 package.json 与 cordis.patch.yml 的 name,漏了两处:`lib/client.js` 的 `__ModuleLoader__.load({ id })` 仍写裸名,profile 的 `node_modules/dsh-steward` 旧 symlink 没删。两者叠加,同一个 bundle 的 client factory 被注册两次,渲染端抛 `duplicate factory registration for "dsh-steward"`,宿主按 boot all-or-nothing 直接退出(应用起不来)。修法:client id 改成完整 scoped 名(同机 @nagi-ovo/dsh-visualize、@michengai/dsh-archive-manager 都是这个约定),并删掉旧裸名链接。
|
|
188
|
+
|
|
189
|
+
## 是什么
|
|
190
|
+
一个 DSH Cordis bundle,host + client 两侧。默认态是只读体检;授权后可执行七类动作(clean 纯缓存清理 / backup 易失数据加密备份 / disable 停用崩溃插件 / enable 恢复启用 / knowledge 结论沉淀+trend / ack 静音某项发现 / unack 恢复显示)。体检做三件**只读**的事:
|
|
191
|
+
|
|
192
|
+
1. **启动后诊断**:读 `%APPDATA%\@deepseek-ai\dsh-desktop\logs\crash-*.log`,解析 `N entries did not activate` 与 `X: import failed`,点名疑似触发 web-boot 崩溃的插件;带新鲜度门(崩溃早于当前实例启动 = 已自愈,降为历史提示)与归因门(连坐 skip 只点名第一个)。
|
|
193
|
+
2. **配置备份告警**:查 profile 下 `cordis.patch.yml.bak-<epoch>` 兄弟文件,提示启动期 `sanitizeProfile` 可能静默吞掉 providers/模型/权限预设,恢复前先备份、旧配置不整体回灌。
|
|
194
|
+
3. **插件变更风险**:订阅 `plugin-manager/changed`,在报告里标注本会话的插件变更(对应「插件变更 → boot 风险」链)。
|
|
195
|
+
|
|
196
|
+
另有升级兼容体检(读各包 `engines.dsh` 门槛对照宿主版本,hostVersion 源链:显式传参 → crash 日志 meta.app → dsh-base package.json)与知识召回(同类问题第二次出现时附「上次也出现过」记录)。
|
|
197
|
+
|
|
198
|
+
报告通过 host 前缀路由 `/api/dsh-steward/`(同源校验 + POST)、Agent 工具 `dsh_steward_report`、侧栏面板与右侧栏 Tab 四个出口暴露;执行走 `dsh_steward_act` 工具或面板动作卡,同一套 plan→指纹→confirm→审计门。v0.4 起有主动出口:boot 后 45 秒自动体检一次 + 向 systemPrompt 注入「用户报崩溃先调体检」护栏段(探针先行,注入不可用则降级为侧栏红点)。
|
|
199
|
+
|
|
200
|
+
v0.5 加拟人化外壳(侧栏头像 + 按时段问候 + 一句话结论 + 三件要紧事,纯前端零 token);v0.6 加 L2 对话入口:面板总览页一个输入框,说一句话(「清理一下缓存」「停用 dsh-guizang-design」「有什么问题吗」)即定位到对应动作卡。**默认解析是规则式的,不花 token、不联网、不执行**,命中后仍然要你在卡里预览并确认。
|
|
201
|
+
|
|
202
|
+
v0.7.8 把「工作区体检」从固定阈值改成增长判定:旧的「有 1 个工作区超过 50 MB」是静态阈值——你没长胖它也会天天亮,那不是发现问题、是把阈值当成了问题。现在拿上一次报告做对照,只有 **净增 ≥ 20 MB** 或**工作区目录消失了**才算 warning,否则降为 info 并写明「与上次相比无显著增长」。没有上一份报告可对照时(首次或报告被清)只记体积、不判定涨跌,绝不凭空报警。同时把 `ACCEPTANCE.md` 基线重采到 v0.7.7/v0.7.8 并新增 A5–A8 四行检查(工作区阈值、更新检出、可信度噪音、静音到期反复),11-23 两周回看直接用新尺子。
|
|
203
|
+
v0.7.7 补上剩下四项日常体检:② **工作区体检**(读 storages/workspace.json,按目录编码算出每个工作区的会话数据体积,看板「有 N 个工作区超过 50 MB」并把消失的工作区标出来);③ **插件更新检出**(读 plugin-update-cache.json 的市场缓存,对照已装版本列出「有新版本可用」的包,并注明升级正是「插件变更→boot 风险」的上游);⑤ **体检可信度自检**(report.coverage 逐源记 data/empty/unavailable,读不到的源会让报告明说「这可能是没看见,不是没问题」);⑥ **静音会过期**(ack 现在带 expiresAt,按发现项给 7–42 天不等的有效期,过期自动重新提醒——静音只代表「此刻我知道」)。
|
|
204
|
+
v0.7.6 新增两项日常体检(①备份新鲜度 / ④配置漂移):① 读 dsh-config-manager/exports 下最新 .zip 的时间,算出「距上次加密备份 N 天」,超过 7 天或一份都没有就升 warning;④ 对 cordis.patch.yml 与 package.json 做 sha256 指纹,与上次体检的基线比,文件变短(超过 20 行)直接升 warning——这是 sanitizeProfile 静默丢配置留下的痕迹。源零 import、基线存在插件自己的数据目录,从不改 DSH 配置。
|
|
205
|
+
|
|
206
|
+
v0.7.5 修了体检结果的折叠语义:以前「折叠」实际是「只露前两张完整卡、其余消失」,同类项一多就只剩一条能看,而露出的两张又是占满屏的全尺寸卡片。现在折叠态把该组**所有**并列项各压成一条两行摘要(严重级别的色条 + ★分 + 标题 + 两行详情戳断),点「展开详情」才切回完整卡(证据/建议/动作按钮);「严重」组默认就是展开的。
|
|
207
|
+
|
|
208
|
+
v0.7.4 把输入卡片的形状重做了一遍:一条圆角输入条(左模式/模型芯片、中无边框输入、右圆形发送键,聚焦时描边高亮),设置面板从条子上方那排胶囊挪到条子下方展开、不再挤压输入框,说明文字从三行收成一行小字。只改呈现,解析与授权链路一行未动。
|
|
209
|
+
|
|
210
|
+
v0.7 给这个输入框加了「模型理解」:可选的第二通话方式,把同一句话交给宿主 `llm` 服务里你选的模型翻译成**同形状的动作意图**。它仍然只翻译不执行——模型拿到的是一次性提示,不持有工具、不写文件、不碰授权,输出还要过一层白名单归一(kind 必须在那八类里、包名必须在真实 bundle 列表里、findingId 必须在现行报告里),不合法就退回「没听懂」。默认仍是规则式,零 token;切到模型式才花一次调用。v0.7.1 补 `goto` 意图(「去到审计趋势」这类导航);v0.7.2 补「只看免费」筛选;v0.7.3 把该判据收窄为「名字带 free/免费,或过半模型带 :free」(否则 WorkBuddy 的 hy4free 单例也会被当成免费 provider)。
|
|
211
|
+
|
|
212
|
+
## 依赖与降级
|
|
213
|
+
本插件**零 npm 依赖**(只用 node:fs/crypto/path/url),也**不 import 任何其它插件的内部模块**。它依赖的是两个插件的 HTTP 路由和几个宿主服务,每个依赖缺失只降级不崩溃:
|
|
214
|
+
|
|
215
|
+
| 依赖 | 谁在用 | 缺了会怎样(fail-soft) |
|
|
216
|
+
| --- | --- | --- |
|
|
217
|
+
| dsh-disk-cleaner(路由 `/api/disk-cleaner/`) | clean 动作的 scan/clean | clean 卡 plan 报错拒绝,其余卡不受影响;disk-advice 建议仍在 |
|
|
218
|
+
| dsh-config-manager(路由 `/export-preview`、`/export`) | backup 动作的预览与真 DCA1 加密导出 | backup 卡 plan 报错拒绝;DCA1 回读校验由本插件用 node:crypto 自实现,不依赖对方模块 |
|
|
219
|
+
| 宿主 pluginManager 服务(`ctx.get` 免 inject) | disable/enable 动作 | 报「pluginManager 不可用」拒绝;#575 enable 守卫在宿主层保证失败不落 durable 状态 |
|
|
220
|
+
| 宿主 webApp / webServer 服务 | routeFetch 拼回环 base URL(①②动作的命脉) | 两者都缺 → clean/backup 执行层全拒;体检报告不受影响(report.js 直接读磁盘) |
|
|
221
|
+
| 宿主 systemPrompt 服务 | v0.4 主动出口注入 | 服务缺失或形态不符 → 跳过注入,降级为侧栏红点兜底 |
|
|
222
|
+
| 宿主 llm 服务(`ctx.get` 免 inject) | v0.7「模型理解」那一通话(`listProviders` / `listModels` / `stream`) | 模型区报「宿主没有可用的模型服务」,切回规则式照常用;规则式路径完全不碰 llm |
|
|
223
|
+
| 宿主 agentDefaultModel 服务 | 模型式默认选中项 | 取不到就回退到目录里第一个 provider 的第一个模型;再取不到则要求先选 |
|
|
224
|
+
| 宿主客户端包 locale / slots / sidebarRightTabs | 面板、侧栏入口、右侧栏 Tab | apply() 整体 try/catch:面板消失但 host 侧路由/工具照常——绝不让 UI 故障拖垮宿主(v0.1.0 整页卡死事故的直接教训) |
|
|
225
|
+
|
|
226
|
+
数据面依赖(宿主文件约定,非插件):crash 日志目录与格式、profile `node_modules/*/package.json` 的 `engines.dsh`、`cordis.patch.yml.bak-<epoch>` 命名。宿主升级若变这些格式,后果是某项 finding 误报/漏报,不会崩。
|
|
227
|
+
|
|
228
|
+
## 明确不做(守底线)
|
|
229
|
+
不删文件(清理永远经 disk-cleaner 的 confirm 语义,只碰 safe 级,caution 需显式勾选)、不代跑提权命令(DISM/diskpart 永远只报告)、不自动拉起/重启、不改启动流程、不整体回灌配置、不碰 `cordis.patch.yml`、凭据不进任何产物(备份口令只在单请求内存过一遍,绝不由 Agent 生成或代输)。所有采集限流(日志 ≤50 份、单份 ≤2MB),任何单步失败都不向宿主抛错(fail-soft)。
|
|
230
|
+
|
|
231
|
+
## 关键设计取舍(为什么 host 不写 `export const inject`)
|
|
232
|
+
本项目第 ①④ 个痛点的根因就是「boot 期一个插件没激活,整个 Web UI 崩」。若本插件声明 `inject: ['tools','webServer']` 而某个可选服务异常,它自己就会变成那个坏插件。因此这里**零强依赖声明**:三处能力全部经 `ctx.get(name)` 容错获取,`apply(ctx)` 每一步包 try/catch,事件监听只在内存里计数、绝不在回调里做磁盘 IO。契约参照本机正在运行的 `dsh-disk-cleaner@1.6.2` 一手范本,但把「强依赖」换成「尽力而为」。
|
|
233
|
+
|
|
234
|
+
`lib/report.js` 刻意**重新实现** `parseBootFailure` / `.bak-<epoch>` 检测 / DCA1 容器校验,不 import `dshmarket/lib/*` 或 `dsh-config-manager/lib/*` 内部模块——避免跨包版本耦合(正是痛点①)。client 侧红线(v0.1.0 整页卡死事故二分定案):零 `<style>` 注入、零 locale 依赖、全部内联样式、不用原生 `<select>`(系统绘制弹层不适配深色主题)、根节点自声明滚动。
|
|
235
|
+
|
|
236
|
+
## 文件结构
|
|
237
|
+
- `package.json` / `cordis.patch.yml` / `icon.svg` / `locale/{zh,en}.json`:bundle 清单与展示元数据。
|
|
238
|
+
- `lib/report.js`:纯函数报告生成(crash 解析、兼容体检、评分分级、静音层、知识召回),零外部依赖,可独立跑 `smoke.mjs`。
|
|
239
|
+
- `lib/actions.js`:授权执行层(plan 人话摘要 / 一次性指纹 / confirm 门 / 归因门 / DCA1 回读校验 / audit 留痕),deps 全注入式。
|
|
240
|
+
- `lib/intent.js`:L2 对话入口的规则式纯函数解析器。一句话 → 一个意图(clean/backup/disable/enable/knowledge/ack/unack/report)。**零 token、零网络、不执行任何东西**:它只决定打开哪张动作卡;听不懂就回去问,不猜。
|
|
241
|
+
- `lib/brain.js`:v0.7 模型理解层。把一句话交给宿主 `llm` 服务,把模型输出归一成与 `intent.js` **完全同形状**的意图;JSON 抽取容错(容忍围栏与前后废话),各字段走白名单校验,任何不合规都降级为 clarify。**只翻译,不执行。**
|
|
242
|
+
- `lib/index.js`:host(路由 + `dsh_steward_report` / `dsh_steward_act` 两个工具 + 事件订阅 + boot 后自动体检 + systemPrompt 注入)。
|
|
243
|
+
- `lib/client.js`:client(`__ModuleLoader__.load` + 三 Tab 面板 + 动作卡 + 侧栏/右侧栏入口,全内联样式)。
|
|
244
|
+
- `smoke.mjs`:本地验证(不启动宿主),281 项断言(含打包自检、时间戳与配色静态规约判据)。
|
|
245
|
+
|
|
246
|
+
## 本地验证(不装、不重启)
|
|
247
|
+
```
|
|
248
|
+
for f in dsh-steward/lib/*.js dsh-steward/smoke.mjs; do node --check "$f"; done
|
|
249
|
+
node dsh-steward/smoke.mjs
|
|
250
|
+
```
|
|
251
|
+
`smoke.mjs` 用注入的 IO/路由夹具覆盖:crash 解析与新鲜度/归因门、fail-soft 与可选目录缺失降级、plan 零写入、指纹一次性、confirm 门、审计不含密码、ack 往返、enable 对偶、DCA1 真字节回读校验(含错口令)、hostVersion 源链、trend 的 cFreeBytes、规则式意图解析的 15 例、模型输出归一的 15 例(含「模型编出不存在的东西不能变成动作」),并做一次真机 best-effort 探测(只读)。
|
|
252
|
+
|
|
253
|
+
## 安装(需用户另行确认后再做)
|
|
254
|
+
**从 npm 装(推荐)**:`plugin_manager` 的 `install_bundle`,target 传 `@sidady/dsh-steward`。
|
|
255
|
+
|
|
256
|
+
**从本地目录装(开发用)**:target 传本目录绝对路径。安装由宿主负责写 profile 的 `package.json` / `cordis.patch.yml` 并热挂载;**不要**手工改这两个文件。
|
|
257
|
+
|
|
258
|
+
装完必查三件(缺一就会出现「已安装但徽章不转 live」或更糟的 boot 失败):
|
|
259
|
+
|
|
260
|
+
1. `cordis.patch.yml` 里 `- insert:` 的 `name` 必须等于 node_modules 里**实际的目录名**——从 npm 装是 `@sidady/dsh-steward`,本地 link 是 `dsh-steward`。写成另一个会导致启动时解析不到包,**整个 Web UI 起不来**。
|
|
261
|
+
2. 同一个包不许同时存在两个目录名(例如改名后旧链接没删)。两份相同的 `cordis.patch.yml` 会把同一个 client id 注册两次,渲染端抛 `duplicate factory registration`,同样是 boot 失败。
|
|
262
|
+
3. link: 依赖改码后必须 remove→install;install 回执 `applied` 不可全信,复查 profile `package.json` 落库 + 路由 200 + ping 版本三者一致。
|
|
263
|
+
|
|
264
|
+
## 运行数据($DSH_HOME/dsh-steward/)
|
|
265
|
+
`audit.jsonl`(每次执行与被拒尝试)、`knowledge.jsonl`(沉淀的结论快照)、`trend.json`(体检趋势,≤400 条,含 cFreeBytes)、`acked.json`(静音清单)、`last-report.json`(跨重启回看,仅作展示,归因动作对其封锁)、`settings.json`(v0.8.0 起:面板「设置」存下的阈值)、`config-baseline.json`(配置指纹基线)。
|
|
266
|
+
|
|
267
|
+
## v0.9.2(模型巡检三件 + 严重问题右下角弹窗)
|
|
268
|
+
|
|
269
|
+
### 模型巡检:三件一起做
|
|
270
|
+
|
|
271
|
+
**① 默认模型健康哨兵。** 只探「默认模型 + 每个服务商前几个」(默认 2 个/服务商,总上限 12),
|
|
272
|
+
不是把 232 个模型全探一遍——每个探针都是一次真实的模型调用,免费额度不是无限的。
|
|
273
|
+
默认模型探不通就升 warning,并给出可切换的替代(同服务商优先,换过去代价最小)。
|
|
274
|
+
|
|
275
|
+
**② 跨插件可用率台账。** 每轮结果按服务商汇总成 ok / dead / busy 三个计数写进报告,
|
|
276
|
+
连续两轮零可用且至少一个明确不可用的服务商会被点名。这是历史,不是当前判定。
|
|
277
|
+
|
|
278
|
+
**③ 只标不判死。** 只有 HTTP 404 / 422(模型确实不存在)才算 dead;429 配额、5xx 网关、
|
|
279
|
+
超时、断网、以及「200 但一个字没返回」全部记 busy。理由很实在:把「此刻不行」当成「坏」
|
|
280
|
+
会把好模型从选择器里赶走,而配额与冷却本来就会自己恢复。**管家不删任何模型。**
|
|
281
|
+
|
|
282
|
+
### 严重问题右下角弹窗
|
|
283
|
+
|
|
284
|
+
挂 `shell.overlay`(frame-wide 浮层,replaceRisk none),默认只弹「严重」,
|
|
285
|
+
停留 12 秒、鼠标移上去暂停计时,带「去看看」(跳到对应动作卡)和「已知晓」(就地静音)。
|
|
286
|
+
|
|
287
|
+
这是**通知渠道**的开关,不是检测的开关:关掉它,体检照做、报告照写、静音与归因门一律不变。
|
|
288
|
+
|
|
289
|
+
### 本轮修掉的三个真 bug(都是「两边对不上」)
|
|
290
|
+
|
|
291
|
+
1. `report.js` 读 `health.probed`,而 `index.js` 传的是 `{ at, default, defaultVerdict, summary }`,
|
|
292
|
+
计数在 `summary` 里 → 模型健康这条 finding 在真机上永远不会出现。单测用扁平形状喂进去反而过了。
|
|
293
|
+
2. `models.js` 的 `prevDead = prev.dead === 0 && prev.ok === 0` 判据写反:上一轮真有一个 dead
|
|
294
|
+
反而被判「不满足」,连续两轮全灭永远报不出来。
|
|
295
|
+
3. `planSentinel` 的 `Number(options.perProvider) || 2` 把 0 吃掉了 —— 而 0 是合法值
|
|
296
|
+
(「只探默认模型自己」),设置项里那句承诺原本是空话。
|
|
297
|
+
|
|
298
|
+
## v0.8.4(入口设置改完立刻生效,不再要求刷新页面)
|
|
299
|
+
|
|
300
|
+
- **真问题**:v0.8.2 的 `ui.entry` 只在 `apply()` 里读一次,改完必须重载整个 Web 界面;而桌面版里 Ctrl+R 未必有绑定(用户实测「好像没有效果」)。**一个设置项不该要求用户去猜快捷键**——把「改完要刷新」当成使用前提本身就是设计缺陷。
|
|
301
|
+
- 修法:`apply()` 改成 `mountFor(entry)` 可重入——先都挂上,读到设置再裁;并把当前重挂函数存进模块级 `ENTRY_APPLIER`。设置页保存成功后若 `ui.entry` 变化,直接调它就地重挂,**全程不刷新**。
|
|
302
|
+
- 顺带修掉一个语义重复:只留右侧栏时不再挂 `left`(导航项 + main 面板本体),避免两处重复;左侧栏的 `sidebar.panellist` 与 `main` 现在一起进出。
|
|
303
|
+
- `settings.js` 里 `ui.entry` 的说明文字去掉「改完要 Ctrl+R」,改为「保存后立刻生效,不用刷新页面」。
|
|
304
|
+
- smoke 仍 168 passed(入口重挂是 client 行为,靠 `node --check` + 真机验证,不进单元断言)。
|
|
305
|
+
## v0.8.3(拒绝措辞分清「不是数字」与「不是可选值」)
|
|
306
|
+
|
|
307
|
+
面板入口是第一个非数值设置项,于是暴露出旧错误文案的偷懒:任何被拒的值都报「这些值不是数字:…」。填了一个不存在的入口选项时看到「不是数字」会让人以为自己填错了格式,而实际上是那天公布的选项表里没有它。
|
|
308
|
+
|
|
309
|
+
现在按原因分组报:「这些值不是数字:ttl.plugin-updates;这些值不是可选值:ui.entry」。分组而不是合并成一句话,是因为两类错的改法完全不同。
|
|
310
|
+
|
|
311
|
+
## v0.8.2(面板入口可选:左侧栏 / 右侧栏 / 两个都要)
|
|
312
|
+
|
|
313
|
+
**问题**:管家在两个地方有入口(左侧栏图标 + 右侧栏标签页),有人只想要一个。
|
|
314
|
+
|
|
315
|
+
**做法**:设置页新增「界面 → 面板入口」,三选一(左侧栏 + 右侧栏 / 仅左侧栏 / 仅右侧栏)。
|
|
316
|
+
|
|
317
|
+
两个设计判断:
|
|
318
|
+
|
|
319
|
+
1. **不提供「完全不显示」**。那会让你再也点不到设置页改回来——把自己锁在门外的选项不是自由,是陷阱。
|
|
320
|
+
2. **先挂后撤,不是先读后挂**。`apply()` 是同步的、读设置是异步的,所以两个入口都先挂上,读到 `ui.entry` 之后再撤掉不要的那个。读失败就保持两个都在(默认态),不会出现一个入口都没有的空白。
|
|
321
|
+
|
|
322
|
+
另外 `ctx.slots` 服务没有 `unregister`(只有 `register` / `registerFactory` / `inject`),所以左侧入口的撤销走的是 `ctx.slots.inject(...)` 的返回函数,而不是按 key 解注册。
|
|
323
|
+
|
|
324
|
+
改完要 Ctrl+R 刷新一次面板才生效——槽位是挂载时建的,不会自己重画。
|
|
325
|
+
|
|
326
|
+
## v0.8.1(设置保存修复 + 输入条强调色)
|
|
327
|
+
|
|
328
|
+
- **修一个真 bug**:面板提交的是扁平键(`ttl.plugin-updates`),`normalize()` 原来只认嵌套形态(`ttl: {plugin-updates}`)→ 保存时全部 continue,返回「没有收到任何可保存的项」。现在两种形态都接受。
|
|
329
|
+
- 保存失败不再吃掉已填的值:错误从「整页替换」改成表单内的红条,读失败才允许整页替换。
|
|
330
|
+
- 输入条默认态改成强调色描边(`rgba(90,159,212,.42)`),聚焦态才升到实色 + 3px 光晕;圆形发送键同色系。
|
|
331
|
+
- smoke 165 passed(新增 3 条扁平键回归断言)。
|
|
332
|
+
|
|
333
|
+
## v0.8.0(设置项:判据松紧可调)
|
|
334
|
+
|
|
335
|
+
- 新增 `lib/settings.js`:11 项设置,分四组(静音有效期 6 / 判定阈值 3 / 采集 2 / 界面 1),每项带 `label/unit/min/max/def/why`。
|
|
336
|
+
- 面板新增第 4 个 Tab「设置」,控件由 host 下发的 SPEC 驱动——标签、范围、解释只有一份,前端不硬编码。
|
|
337
|
+
- 三条设计原则:只放开「判据的松紧」不放开「判据的有无」;越界夹到边界并回报;默认值即推荐值(没配过 = 与 v0.7.8 行为一致)。
|
|
338
|
+
- 阈值真正生效:`report.js` 的增长阈值 / 备份过期天数 / 配置漂移行数 / 采集上限、`actions.js` 的静音有效期全部读设置。
|
|
339
|
+
- 保存到 `~/.dsh/dsh-steward/settings.json`;静音有效期只影响**新**静音条目,已有的到期时间不变。
|
|
2
340
|
|
|
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.
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# dsh-steward — read-only health check + authorized actions for DeepSeek Harness.
|
|
2
|
+
# `- insert:` mounts the host entry; `name` MUST equal the installed package directory.
|
|
3
|
+
# 包名是 scoped 的 '@sidady/dsh-steward',pnpm 装出来的目录就是
|
|
4
|
+
# node_modules/@sidady/dsh-steward,所以这里必须写全名;写裸名 'dsh-steward'
|
|
5
|
+
# 只在本地手工 link 出裸目录时碰巧成立,从 npm 装的用户会解析不到包、
|
|
6
|
+
# 整个 Web UI 起不来。
|
|
7
|
+
- insert:
|
|
8
|
+
- id: dsh-steward
|
|
9
|
+
name: '@sidady/dsh-steward'
|
package/icon.svg
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
|
2
|
+
<path d="M12 2.6l7.4 2.8v5.9c0 4.7-3.1 8.2-7.4 10.1-4.3-1.9-7.4-5.4-7.4-10.1V5.4z"/>
|
|
3
|
+
<path d="M7 12.2h2.5l1.4-2.8 2 5.4 1.3-2.6H17"/>
|
|
4
|
+
</svg>
|