@moonquake2004/dsh-doctor 0.4.28 → 0.4.30
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/dsh-doctor.mjs +53 -4
- package/package.json +1 -1
package/dsh-doctor.mjs
CHANGED
|
@@ -310,6 +310,36 @@ function checkEnv() {
|
|
|
310
310
|
supported ? undefined : `升级 node 到 ${rangeSrc.range}(${rangeSrc.from},见 #2259)`);
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
+
// E13:CLI 是否真的会执行(#6341 的静默失效签名)
|
|
314
|
+
// `lib/bin.js` 末尾是 `if (import.meta.main) await runCli();`,而 `import.meta.main` 只在
|
|
315
|
+
// Node **≥22.18.0 / ≥24.2.0** 存在。落在窗口外的 Node 上,**任何** dsh 命令都零输出、退出码 0。
|
|
316
|
+
// 判据(#6341 的关键观察):连 `--version` 都静默——缺原生模块、端口占用、patch 问题都会打印东西,
|
|
317
|
+
// 只有"入口根本没执行"才会连版本号都不输出。故这里直接问一次版本号。
|
|
318
|
+
{
|
|
319
|
+
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
320
|
+
const found = (() => { const r = spawnSync(whichCmd, ['dsh'], { encoding: 'utf8' }); return r.status === 0 ? String(r.stdout).split(/\r?\n/)[0].trim() : null; })();
|
|
321
|
+
if (!found) {
|
|
322
|
+
reportSkip('env', 'E13', 'PATH 中未找到 dsh,跳过 CLI 可执行性探测');
|
|
323
|
+
} else {
|
|
324
|
+
const r = spawnSync(process.platform === 'win32' ? 'dsh.cmd' : 'dsh', ['--version'], { encoding: 'utf8', timeout: 15000, shell: process.platform === 'win32' });
|
|
325
|
+
const out = String(r.stdout || '').trim();
|
|
326
|
+
const ver = /\d+\.\d+\.\d+[-\w.]*/.exec(out);
|
|
327
|
+
if (r.status === 0 && out === '') {
|
|
328
|
+
report('env', 'E13', false,
|
|
329
|
+
`dsh 在 PATH 中(${found})但 \`dsh --version\` **零输出且退出码 0** —— 这是 #6341 的静默失效签名:`
|
|
330
|
+
+ `CLI 入口被 \`if (import.meta.main) await runCli();\` 门控,而该 API 仅在 Node **≥22.18.0 / ≥24.2.0** 存在;`
|
|
331
|
+
+ `(当前 Node ${process.version};若它落在窗口外,这就是原因——本机 Node 24.20 上该 API 存在、命令正常)`,
|
|
332
|
+
'换用 Node ≥22.18.0 或 ≥24.2.0(注意:仓库声明范围 >=24.0.0 **包含** 24.0/24.1 这两个不可用版本)');
|
|
333
|
+
} else if (r.status !== 0) {
|
|
334
|
+
report('env', 'E13', false,
|
|
335
|
+
`dsh 在 PATH 中但 \`--version\` 以退出码 ${r.status} 失败:${String((r.stderr || '') + out).slice(0, 120)}`,
|
|
336
|
+
'按上面的报错定位(依赖缺失 / 入口异常)');
|
|
337
|
+
} else {
|
|
338
|
+
report('env', 'E13', true, `CLI 可执行(dsh --version → ${ver ? ver[0] : out.slice(0, 24)})`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
313
343
|
// E12:运行时 zstd 稳定性(#6651 的运行时线索)
|
|
314
344
|
// 锚点已核实:dsh-session-persistence-jsonl:15 直接 `import { zstdCompress, zstdDecompress,
|
|
315
345
|
// zstdDecompressSync } from "node:zlib"`(用于 :1263 / :1287 / :1368)——会话日志的读写**依赖 Node 内置 zstd**。
|
|
@@ -1326,10 +1356,29 @@ function checkSession(targetPath) {
|
|
|
1326
1356
|
// ② surfaceOp 为 replace 形态({op:'replace', start/end} 或 startSeq/endSeq),
|
|
1327
1357
|
// 但引用的位置在本日志里解析不到。
|
|
1328
1358
|
{
|
|
1329
|
-
const
|
|
1359
|
+
const rawRows = [];
|
|
1330
1360
|
for (const ln of String(text).split('\n')) {
|
|
1331
1361
|
if (!ln.trim()) continue;
|
|
1332
|
-
try {
|
|
1362
|
+
try { rawRows.push(JSON.parse(ln)); } catch { /* 解析失败由 S11/SS4 报 */ }
|
|
1363
|
+
}
|
|
1364
|
+
// **判定对象必须是"打开时真正被投影的那条流"**:对需要迁移的日志(v0/v1/v2),
|
|
1365
|
+
// 缺消息体可能是在**迁移过程中**产生的,磁盘行上看不出来。所以这里优先走真实迁移链取"迁移后的视图"。
|
|
1366
|
+
// (2026-09 自查:初版只看磁盘行,会漏掉报告者 #6686 的实际病例。)
|
|
1367
|
+
let rows = rawRows;
|
|
1368
|
+
let view = '磁盘原始行';
|
|
1369
|
+
const hdr = rawRows[0];
|
|
1370
|
+
if (hdr && hdr.type === 'session' && hdr.version !== 3 && FORMAT_CATALOG) {
|
|
1371
|
+
try {
|
|
1372
|
+
const r = FORMAT_CATALOG.createRestore(hdr, { validation: 'current', recovery: 'strict' });
|
|
1373
|
+
const decoded = [];
|
|
1374
|
+
for (let i = 1; i < rawRows.length; i++) {
|
|
1375
|
+
const d = r.decodeRow(rawRows[i]);
|
|
1376
|
+
decoded.push((d && d.type) ? d : (d && d.event) ? d.event : rawRows[i]);
|
|
1377
|
+
}
|
|
1378
|
+
r.finish();
|
|
1379
|
+
rows = decoded;
|
|
1380
|
+
view = `迁移后视图(v${hdr.version} → v3,走真实迁移链)`;
|
|
1381
|
+
} catch { /* 迁移被拒由 S12 报;这里退回磁盘行并注明 */ view = '磁盘原始行(迁移链未走通,见 S12)'; }
|
|
1333
1382
|
}
|
|
1334
1383
|
const seqs = new Set(rows.map((r) => r.seq).filter((x) => typeof x === 'number'));
|
|
1335
1384
|
const probs = [];
|
|
@@ -1351,13 +1400,13 @@ function checkSession(targetPath) {
|
|
|
1351
1400
|
}
|
|
1352
1401
|
if (probs.length) {
|
|
1353
1402
|
report('session', 'S14', false,
|
|
1354
|
-
`检测到 ${probs.length} 处会让会话**打不开**的事件体/引用不完整(#6686:迁移成功但 project
|
|
1403
|
+
`检测到 ${probs.length} 处会让会话**打不开**的事件体/引用不完整(#6686:迁移成功但 project 抛错;判定视图:${view}):\n `
|
|
1355
1404
|
+ probs.slice(0, 6).join('\n ')
|
|
1356
1405
|
+ (probs.length > 6 ? `\n …另有 ${probs.length - 6} 处` : ''),
|
|
1357
1406
|
'这类会话在 GUI 里点开会报 failed to project session;上游修法(可选链 + 中性兜底)见 #6686;'
|
|
1358
1407
|
+ '在那之前可先把该会话目录移出 sessions/ 隔离,避免它影响会话列表');
|
|
1359
1408
|
} else {
|
|
1360
|
-
report('session', 'S14', true, `投影安全性预检通过(${rows.length} 个事件:消息体完整、replace
|
|
1409
|
+
report('session', 'S14', true, `投影安全性预检通过(${rows.length} 个事件:消息体完整、replace 引用可解析;判定视图:${view})`);
|
|
1361
1410
|
}
|
|
1362
1411
|
}
|
|
1363
1412
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moonquake2004/dsh-doctor",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.30",
|
|
4
4
|
"description": "Offline diagnostic for DeepSeek Harness — 28 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": [
|