@moonquake2004/dsh-doctor 0.4.25 → 0.4.27

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.
Files changed (2) hide show
  1. package/dsh-doctor.mjs +60 -7
  2. package/package.json +1 -1
package/dsh-doctor.mjs CHANGED
@@ -246,11 +246,31 @@ function resolveProfile(name) {
246
246
  * 即 `@deepseek-ai/dsh-root`(`private: true`,非发布包)。**没有任何已发布包继承它**,
247
247
  * 所以 registry 查不到、npm 安装时既不校验也不警告;这正是社区里"没有 engines"印象的由来。
248
248
  * 我们内置的范围与之一致(等于官方声明值),但它是**手写维护**的:上游改动时需同步。 */
249
- function nodeInSupportedRange(v) {
249
+ /**
250
+ * 支持范围。默认是**内置常量**(出处与核对日期见上一条注释);若设置了
251
+ * `DSH_DOCTOR_HARNESS_ROOT` 指向 harness 检出,则**真读**其仓库根 package.json 的
252
+ * `engines.node` —— 这是社区建议的方向(#6651 @ciceroyang):手维护的常量会随上游漂移,
253
+ * 能读元数据就别抄数字。真正的解法仍是 #2259(让**已发布**的入口包带上 engines)。
254
+ */
255
+ const NODE_RANGE_FALLBACK = '^22.19.0 || >=24.0.0'; // 核对日期 2026-09-15(master)
256
+ function nodeRangeSource() {
257
+ const root = process.env.DSH_DOCTOR_HARNESS_ROOT;
258
+ if (root) {
259
+ try {
260
+ const e = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).engines?.node;
261
+ if (typeof e === 'string' && e.trim()) return { range: e.trim(), from: `真读 ${root}/package.json` };
262
+ } catch { /* 落到内置常量 */ }
263
+ }
264
+ return { range: NODE_RANGE_FALLBACK, from: '内置常量(出处:仓库根 package.json engines.node,核对 2026-09-15)' };
265
+ }
266
+ function nodeInSupportedRange(v, range = NODE_RANGE_FALLBACK) {
250
267
  const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(String(v));
251
268
  if (!m) return false;
252
269
  const major = Number(m[1]); const minor = Number(m[2]);
253
- return (major === 22 && minor >= 19) || major >= 24;
270
+ // 规则来自该范围本身(^22.19.0 || >=24.0.0):22.19+ 或 24+
271
+ if (range === NODE_RANGE_FALLBACK) return (major === 22 && minor >= 19) || major >= 24;
272
+ const cur = { nums: [Number(m[1]), Number(m[2]), Number(m[3])], pre: [] };
273
+ return peerRangeState(String(v).replace(/^v/, ''), range).state === 'satisfied';
254
274
  }
255
275
  function checkEnv() {
256
276
  if (!wants('env')) return;
@@ -265,12 +285,29 @@ function checkEnv() {
265
285
  report('env', 'E2-env', !isDir, isDir ? `${envFile} 是目录,dsh 启动会报 failed to load .env: EISDIR(#71)` : `${envFile} 正常`, isDir ? '删除或改名该目录' : undefined);
266
286
  }
267
287
  const nv = spawnSync('node', ['-e', 'console.log(process.version)']);
288
+ // 支持范围:优先**从仓库根 manifest 真读**(若给了 DSH_DOCTOR_HARNESS_ROOT 或存在检出),
289
+ // 否则用内置常量。社区(#6651 @ciceroyang)指出:两个实现都把这个范围写成手维护常量,
290
+ // 上游一改就各自漂移;真正的解法在 #2259(让已发布的入口包带上 engines,从而可读安装树元数据)。
291
+ // 在那之前,任何写死它的地方都必须标注**出处与核对日期**。
292
+ const RANGE_FALLBACK = '^22.19.0 || >=24.0.0'; // 出处:deepseek-harness 仓库根 package.json engines.node(@deepseek-ai/dsh-root,private);核对日期 2026-09-15(master)
293
+ const rangeInfo = (() => {
294
+ const root = process.env.DSH_DOCTOR_HARNESS_ROOT;
295
+ if (root) {
296
+ try {
297
+ const e = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).engines?.node;
298
+ if (typeof e === 'string' && e.trim()) return { range: e.trim(), from: `${root}/package.json(真读)` };
299
+ } catch { /* 落到内置 */ }
300
+ }
301
+ return { range: RANGE_FALLBACK, from: '内置常量(出处:仓库根 package.json engines.node;核对日期 2026-09-15)' };
302
+ })();
303
+
268
304
  if (nv.status === 0) {
269
305
  const version = String(nv.stdout).trim();
270
- const supported = nodeInSupportedRange(version);
306
+ const rangeSrc = nodeRangeSource();
307
+ const supported = nodeInSupportedRange(version, rangeSrc.range);
271
308
  report('env', 'E3-node', supported,
272
- supported ? `node ${version}(满足声明范围 ^22.19.0 || >=24.0.0——出处为仓库根私有 workspace package.json 的 engines,#2259)` : `node ${version} 不在支持范围(^22.19.0 || >=24.0.0,v1 词汇表)——会话日志读取等能力受限`,
273
- supported ? undefined : '升级 node 到 ^22.19.0 或 >=24.0.0(仓库根 engines 声明的范围,见 #2259)');
309
+ supported ? `node ${version}(满足 ${rangeSrc.range}——${rangeSrc.from};该范围声明在私有 workspace 上、无发布物继承,见 #2259/#6651)` : `node ${version} 不在支持范围(^22.19.0 || >=24.0.0,v1 词汇表)——会话日志读取等能力受限`,
310
+ supported ? undefined : `升级 node 到 ${rangeSrc.range}(${rangeSrc.from},见 #2259)`);
274
311
  }
275
312
 
276
313
  // E12:运行时 zstd 稳定性(#6651 的运行时线索)
@@ -639,18 +676,34 @@ function checkProfile(name) {
639
676
  findings19.push(`${pkgName} 声明 ${hostName} ${range},但实际提供 ${host.version}`);
640
677
  }
641
678
  }
679
+ // 规则 1(作者侧):未声明 host 范围 = **无信息**,不得当作兼容。这里只做计数提示,
680
+ // 不生成发现——声明与否是作者的选择,消费者该做的是别把它读成"已核对"。
681
+ let undeclared19 = 0;
682
+ for (const { dir: pkgDir } of pkgDirs19) {
683
+ try {
684
+ const mf = JSON.parse(readFileSync(join(pkgDir, 'package.json'), 'utf8'));
685
+ const isBundle = Boolean(mf.dsh && (mf.dsh.bundle || mf.dsh.profile));
686
+ const hasHostPeer = Object.keys(mf.peerDependencies ?? {}).some((k) => k.startsWith('@deepseek-ai/'));
687
+ if (isBundle && !hasHostPeer) undeclared19++;
688
+ } catch { /* 忽略不可读 */ }
689
+ }
690
+ const undeclaredNote = undeclared19
691
+ ? `\n (另有 ${undeclared19} 个 bundle 未声明任何 @deepseek-ai/* host 范围——按规则记为"无信息",不代表兼容)`
692
+ : '';
642
693
  if (findings19.length) {
643
694
  report('profile', 'P19', false,
644
695
  `插件声明的 host peer 范围不接受实际安装的核心版本(${findings19.length} 处,共核对 ${checked19} 条 peer 声明):\n `
645
696
  + findings19.slice(0, 8).join('\n ')
646
- + (unknown19 ? `\n (另有 ${unknown19} 条无法判定:区间为 * / 未声明 / host 解析不到,或纯 release 区间面对预发布版本——按"未知"处理,不计为不兼容)` : ''),
697
+ + undeclaredNote
698
+ + (unknown19 ? `\n (另有 ${unknown19} 条无法判定:区间为 * / 不可解析 / host 解析不到,或纯 release 组面对数值满足的预发布版本——按"未知"处理,不计为不兼容)` : ''),
647
699
  '按提示升级到该插件声明支持的版本(或降级核心)。安装时不会有警告,所以升级 dsh 前先用本检查看一眼最省事');
648
700
  } else if (checked19 === 0) {
649
701
  reportSkip('profile', 'P19', '未见任何 @deepseek-ai/* 的 peer 声明,跳过 host 范围核对');
650
702
  } else {
651
703
  report('profile', 'P19', true,
652
704
  `核对 ${checked19} 条 host peer 声明,均可接受当前核心版本`
653
- + (unknown19 ? `(${unknown19} 条无法判定,按未知处理未计入)` : ''));
705
+ + (unknown19 ? `(${unknown19} 条无法判定,按未知处理未计入)` : '')
706
+ + undeclaredNote.replace(/\n\s*/g, ' '));
654
707
  }
655
708
  }
656
709
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moonquake2004/dsh-doctor",
3
- "version": "0.4.25",
3
+ "version": "0.4.27",
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": [