@moonquake2004/dsh-doctor 0.3.2 → 0.3.4
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/README.md +4 -4
- package/README.zh.md +4 -4
- package/client/client.js +1 -1
- package/cordis.patch.yml +1 -1
- package/dsh-doctor.mjs +18 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Zero npm dependencies. One file. Runs anywhere `node` exists (`zstd` needed only
|
|
|
8
8
|
|
|
9
9
|
## Why
|
|
10
10
|
|
|
11
|
-
dsh's plugin tree is "fragile by install": a dangling reference, a broken `file:` link, a duplicate entry id, or a corrupted session log can brick the profile at boot or stall the whole web server — and `--dump-config` never mounts the loader, so it passes on broken setups. This class of failure was consolidated in [dsh discussion #1496](https://github.com/deepseek-ai/deepseek-harness/discussions/1496) (Advisory: plugin-install path needs guardrails). `dsh-doctor` is the offline check that advisory calls for —
|
|
11
|
+
dsh's plugin tree is "fragile by install": a dangling reference, a broken `file:` link, a duplicate entry id, or a corrupted session log can brick the profile at boot or stall the whole web server — and `--dump-config` never mounts the loader, so it passes on broken setups. This class of failure was consolidated in [dsh discussion #1496](https://github.com/deepseek-ai/deepseek-harness/discussions/1496) (Advisory: plugin-install path needs guardrails). `dsh-doctor` is the offline check that advisory calls for — 26 built-in checks (env/profile/session) mapped to 18 community reports, each verified with synthetic negative fixtures, plus a self-updating remote catalog of 5 declarative pattern checks.
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
@@ -26,7 +26,7 @@ Exit codes (default mode): `0` = all pass · `1` = problems found (built-in chec
|
|
|
26
26
|
|
|
27
27
|
With `--envelope` (doctor-contract mode): `0` = all pass · `1` = any WARN · `2` = any FAIL. The envelope follows the shared `dsh-doctor/v1` schema (`{ schema, generatedAt, profile, exitCode, summary, ok, checks:[{name,status,detail}] }`) so implementations are interchangeable for CI/marketplace use. Installed via npm, the CLI is also available as the `dsh-doctor` bin.
|
|
28
28
|
|
|
29
|
-
## Checks (
|
|
29
|
+
## Checks (26 built-in + 5 catalog = 31)
|
|
30
30
|
|
|
31
31
|
### env
|
|
32
32
|
| ID | Checks | Discussion |
|
|
@@ -113,7 +113,7 @@ The tool also watches its own npm version: each run compares the installed versi
|
|
|
113
113
|
|
|
114
114
|
## Remote check catalog (v0.2.0)
|
|
115
115
|
|
|
116
|
-
The built-in
|
|
116
|
+
The built-in 26 checks are compiled into the tool. The **catalog** is a second, self-updating layer: `plugin/checks.json` in this repo holds declarative rules (data, not code), and every installed instance picks up new rules automatically — no reinstall needed.
|
|
117
117
|
|
|
118
118
|
- **How it works**: each run tries to fetch `plugin/checks.json` from GitHub (3s timeout) → on success it's cached to `$DSH_HOME/.cache/dsh-doctor/checks.json` (TTL 6h) → on failure it falls back to the last-known-good cache, then to the bundled copy. New checks therefore arrive within ≤6h of being committed upstream.
|
|
119
119
|
- **Safety**: rules are **read-only probes** executed by the built-in engine (`command-exists`, `path-*`, `json-valid`, `text-contains` / `text-not-contains`, `file-size-above`, `glob-count`). The remote payload can never run code — it can only add pattern checks.
|
|
@@ -141,7 +141,7 @@ Safety invariants: closed probe vocabulary (LLM output is data, never code), pro
|
|
|
141
141
|
|
|
142
142
|
## Also installable as a dsh plugin
|
|
143
143
|
|
|
144
|
-
The tool ships as a proper dsh bundle (`plugin/`), so you can run the same checks (
|
|
144
|
+
The tool ships as a proper dsh bundle (`plugin/`), so you can run the same checks (26 built-in + 5 catalog rules) from inside the web UI:
|
|
145
145
|
|
|
146
146
|
```bash
|
|
147
147
|
# install into a profile (works from a checkout or a published path)
|
package/README.zh.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## 为什么
|
|
8
8
|
|
|
9
|
-
dsh 的插件树"装出来就是脆的":一个悬空引用、一个断掉的 `file:` 链接、一个重复的 entry id、一段损坏的会话日志,都能让 profile 在启动时直接挂掉或拖垮整个 web 服务器——而 `--dump-config` 从不挂载 loader,所以在坏配置上也"一切正常"。这类故障被汇总在 [dsh discussion #1496](https://github.com/deepseek-ai/deepseek-harness/discussions/1496)(Advisory:插件安装路径需要护栏)。`dsh-doctor` 就是这个护栏——**
|
|
9
|
+
dsh 的插件树"装出来就是脆的":一个悬空引用、一个断掉的 `file:` 链接、一个重复的 entry id、一段损坏的会话日志,都能让 profile 在启动时直接挂掉或拖垮整个 web 服务器——而 `--dump-config` 从不挂载 loader,所以在坏配置上也"一切正常"。这类故障被汇总在 [dsh discussion #1496](https://github.com/deepseek-ai/deepseek-harness/discussions/1496)(Advisory:插件安装路径需要护栏)。`dsh-doctor` 就是这个护栏——**26 项内置检查**(env 7 / profile 11 / session 8)映射到 18 个社区报告,每一项都用合成负样例验证过;外加一个**自更新的远程检查目录**(声明式规则,v0.2.0 起)。
|
|
10
10
|
|
|
11
11
|
## 用法
|
|
12
12
|
|
|
@@ -21,7 +21,7 @@ node dsh-doctor.mjs --no-catalog # 不拉远程目录(只用内置副
|
|
|
21
21
|
|
|
22
22
|
退出码:`0` = 全部通过 · `1` = 发现问题(内置检查 + 目录中 `severity: error` 的项)· warn 级目录失败不改退出码。
|
|
23
23
|
|
|
24
|
-
## 检查项(
|
|
24
|
+
## 检查项(26 内置 + 5 目录)
|
|
25
25
|
|
|
26
26
|
### env
|
|
27
27
|
| ID | 检查 | 对应讨论 |
|
|
@@ -106,7 +106,7 @@ The `dsh-doctor/v1` envelope (`--json --envelope`) is the machine-readable form
|
|
|
106
106
|
|
|
107
107
|
## 远程检查目录(v0.2.0,层 A)
|
|
108
108
|
|
|
109
|
-
内置
|
|
109
|
+
内置 26 项检查编译在工具里。**目录**是第二层、自更新的:本仓库的 `plugin/checks.json` 放声明式规则(**规则是数据,不是代码**),所有已装实例自动获取新规则——无需重装。
|
|
110
110
|
|
|
111
111
|
- **工作机制**:每次运行尝试从 GitHub 拉 `plugin/checks.json`(3s 超时)→ 成功后缓存到 `$DSH_HOME/.cache/dsh-doctor/checks.json`(TTL 6h)→ 失败回退 last-known-good 缓存 → 再回退内置副本。新检查因此在上游提交后 ≤6h 内自动到达。
|
|
112
112
|
- **安全性**:规则是**只读探测原语**,由内置引擎执行(`command-exists`、`path-*`、`json-valid`、`text-contains` / `text-not-contains`、`file-size-above`、`glob-count`)。远程内容永远无法执行代码——只能新增模式检查。
|
|
@@ -134,7 +134,7 @@ The `dsh-doctor/v1` envelope (`--json --envelope`) is the machine-readable form
|
|
|
134
134
|
|
|
135
135
|
## 也可作为 dsh 插件安装
|
|
136
136
|
|
|
137
|
-
工具以标准 dsh bundle 形态发布(`plugin/`),可以在 web UI 里跑同样的检查(
|
|
137
|
+
工具以标准 dsh bundle 形态发布(`plugin/`),可以在 web UI 里跑同样的检查(26 内置 + 5 目录规则):
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
140
|
# 装进 profile(checkout 或已发布路径均可)
|
package/client/client.js
CHANGED
package/cordis.patch.yml
CHANGED
package/dsh-doctor.mjs
CHANGED
|
@@ -99,6 +99,11 @@ function report(section, id, ok, detail, fix, src) {
|
|
|
99
99
|
results.push({ section, id, ok, detail, fix, src: src ?? 'builtin' });
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
/** skip 状态(v1 词汇表 r5:#1719)——"不适用"而非"通过",必须带 reason(detail)。不计入 pass/fail,不翻退出码。 */
|
|
103
|
+
function reportSkip(section, id, detail, src) {
|
|
104
|
+
results.push({ section, id, ok: true, skip: true, detail, src: src ?? 'builtin' });
|
|
105
|
+
}
|
|
106
|
+
|
|
102
107
|
/** 解析 --profile 参数:名字(如 web)→ $DSH_HOME/profiles/<name>;含路径分隔符/~/开头 → 直接当 profile 目录(契约 harness 传绝对路径)。 */
|
|
103
108
|
function resolveProfile(name) {
|
|
104
109
|
if (!name) throw new Error('无效 profile 名');
|
|
@@ -535,23 +540,30 @@ function checkProfile(name) {
|
|
|
535
540
|
// P12:profile 内 bundle 版本 vs 运行 CLI 版本(#1719 v1.1 `installed_bundle` 候选)
|
|
536
541
|
// web 设置「诊断」面板与 /dsh-doctor/run API 跑的是 profile 里装的 bundle;独立 CLI(checkout/npx)是另一个副本——
|
|
537
542
|
// Layer-B 自更新只比 npm latest vs 运行模块,profile 内 bundle 落后/超前都不报警(dsh-win32/bundle 同坑,sjh9714 先发现的)。
|
|
538
|
-
// 语义(#1719
|
|
543
|
+
// 语义(#1719 合稿,sjh9714 四态分析 + skip 修正):pass/warn/skip 三态 + detail 注明条件——
|
|
544
|
+
// manifest 未声明 = skip(无对比对象,pass 会让 CI 误判"已同步"——git_bash 同形);
|
|
545
|
+
// manifest 声明但 node_modules 缺失 = warn(manifest 撒谎,运行时从不加载);
|
|
546
|
+
// 已装且版本一致 = pass;已装但版本分歧 = warn(detail 含 age-gate 提示,升级可能被 pnpm-workspace.yaml 的
|
|
547
|
+
// minimumReleaseAgeExclude 年龄门暂缓一天,指令不再静默无效——sjh9714 实测)。
|
|
539
548
|
try {
|
|
540
549
|
const selfName = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8')).name ?? '@moonquake2004/dsh-doctor';
|
|
550
|
+
const listed = Object.keys(deps).some((k) => k === selfName || k === 'dsh-doctor');
|
|
541
551
|
// 安装形态两种都找:npm scoped 名(@moonquake2004/dsh-doctor)与 file: 依赖的裸名(dsh-doctor)
|
|
542
552
|
let bundlePkg = null;
|
|
543
553
|
for (const cand of [selfName, 'dsh-doctor']) {
|
|
544
554
|
const p = join(dir, 'node_modules', cand, 'package.json');
|
|
545
555
|
if (existsSync(p)) { bundlePkg = p; break; }
|
|
546
556
|
}
|
|
547
|
-
if (!bundlePkg) {
|
|
548
|
-
|
|
557
|
+
if (!listed && !bundlePkg) {
|
|
558
|
+
reportSkip('profile', 'P12-bundle-version', 'profile 未声明也未安装 dsh-doctor bundle——无对比对象(CLI 独立运行),skip 而非 pass(#1719 installed_bundle 合稿,sjh9714:pass 会让 CI 误判"已同步")');
|
|
559
|
+
} else if (listed && !bundlePkg) {
|
|
560
|
+
report('profile', 'P12-bundle-version', false, `profile 的 package.json 声明了 ${selfName} 依赖,但 node_modules 里没有对应包(manifest 与运行时不一致,web 面板/API 实际加载不到)`, `dsh plugin --profile ${name} install ${selfName}(或先移除该依赖再重装)`);
|
|
549
561
|
} else {
|
|
550
562
|
const bundleVersion = JSON.parse(readFileSync(bundlePkg, 'utf8')).version;
|
|
551
563
|
const cliVersion = localVersion();
|
|
552
564
|
const same = bundleVersion === cliVersion;
|
|
553
565
|
report('profile', 'P12-bundle-version', same,
|
|
554
|
-
same ? `profile 内 bundle 版本 ${bundleVersion} 与运行 CLI ${cliVersion} 一致` : `profile 内 bundle 版本 ${bundleVersion} ≠ 运行 CLI ${cliVersion}(web 面板/API 跑的是 bundle
|
|
566
|
+
same ? `profile 内 bundle 版本 ${bundleVersion} 与运行 CLI ${cliVersion} 一致` : `profile 内 bundle 版本 ${bundleVersion} ≠ 运行 CLI ${cliVersion}(web 面板/API 跑的是 bundle,两边行为可能不一致;若刚发布过新版本,升级可能被 pnpm-workspace.yaml 的 minimumReleaseAgeExclude 年龄门暂缓,可次日重试)`,
|
|
555
567
|
same ? undefined : `同步安装版本:dsh plugin --profile ${name} update ${selfName}(或让 CLI 与 bundle 走同一安装方式)`);
|
|
556
568
|
}
|
|
557
569
|
} catch (e) {
|
|
@@ -1145,8 +1157,8 @@ async function run() {
|
|
|
1145
1157
|
const bad = results.filter((r) => !r.ok && catalogSeverity.get(r.id) !== 'warn');
|
|
1146
1158
|
if (jsonOut && process.argv.includes('--envelope')) {
|
|
1147
1159
|
// v1 契约信封(dsh doctor 规格,zoahdev/doctor 对齐):status 小写 + 退出码 0/1/2
|
|
1148
|
-
const st = (r) => (!r.ok ? (catalogSeverity.get(r.id) === 'warn' ? 'warn' : 'fail') : 'pass');
|
|
1149
|
-
const summary = { pass: 0, warn: 0, fail: 0, skip: 0 }; // skip 常驻(v1 词汇表 r5:#1719
|
|
1160
|
+
const st = (r) => (r.skip ? 'skip' : (!r.ok ? (catalogSeverity.get(r.id) === 'warn' ? 'warn' : 'fail') : 'pass'));
|
|
1161
|
+
const summary = { pass: 0, warn: 0, fail: 0, skip: 0 }; // skip 常驻(v1 词汇表 r5:#1719),r5 后 P12 会在未装 bundle 时实际触发
|
|
1150
1162
|
const checks = results.map((r) => { summary[st(r)]++; return { name: r.id, status: st(r), detail: r.detail }; });
|
|
1151
1163
|
const exitCode = summary.fail > 0 ? 2 : summary.warn > 0 ? 1 : 0;
|
|
1152
1164
|
console.log(JSON.stringify({
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moonquake2004/dsh-doctor",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "Offline diagnostic for DeepSeek Harness —
|
|
3
|
+
"version": "0.3.4",
|
|
4
|
+
"description": "Offline diagnostic for DeepSeek Harness — 26 built-in + 5 catalog checks across env/profile/session (Layer A checks-as-data), self-update (Layer B), and a semi-automatic LLM observer (Layer C, --observe); Doctor panel in web UI settings.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"README.md",
|