dsh-m 0.9.29 → 0.9.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/CHANGELOG.md CHANGED
@@ -8,12 +8,19 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
8
8
 
9
9
  ## 中文
10
10
 
11
+ ### 0.9.30 变更:修复 bin 符号链接静默 no-op(F2)+ 版本解析 realpath 第三源(F1)
12
+
13
+ - **根因同源**:pnpm 生态里两类关键入口都是**符号链接**——`node_modules/.bin/dshm`(指向 lib/cli.js)与全局 shim `PNPM_HOME/dsh`(指向真实 bin.js)——而两处代码都在 realpath 之前做了路径身份判定。
14
+ - **F2(自 v0.2.0 bin 入口引入以来潜在,本机 0.9.28 装机形态首次踩中并实证;🔴 bin 入口全命令静默 exit 0)**:cli.ts `invokedDirectly` 比较 `argv[1]`(链接路径)与 `import.meta.url`(node realpath 后的真实路径)永假,`dshm <任何命令>` 无输出直接退出。修复:比较前对 `argv[1]` 同样 realpath。新增 `tests/cli-bin-symlink.test.mjs`(经符号链接调用 --help 与 doctor --json 的复活验证,EPERM 环境照仓内先例 skip)。
15
+ - **F1(0.9.29 装机验收实证)**:dsh-version.ts `readLauncherPackageVersion` 从 shim 位置直接向上三级找 package.json,落在 pnpm home 目录树上空走——宿主内纯 FS 版本解析在本机拓扑(`node PNPM_HOME/dsh web`)不可得,doctor 的 stale 判定降级、ping chip 被迫吃 spawn 回退。修复:判定 entry 形态后先 realpath 再上溯(本机实测解析得 0.2.0-rc.2)。新增 dsh-version.test.mjs shim 形态用例。
16
+ - **验证**:专项 9/9(含两新用例);全量 1079 pass / 0 fail / 0 skipped;typecheck 零错误。装机后预期:`dshm doctor`(bin 入口)可用;宿主 method 通路 runtimeVersion=0.2.0-rc.2、stale 判定复活(装机时农场 dsh 链接 0.1.7-rc.2 为现成 stale 形态,heal 后归零)。
17
+
11
18
  ### 0.9.29 变更:profile 体检 Doctor——`dshm doctor` 上线(只读:农场测活 / 残留物清点 / 账实一致)
12
19
 
13
20
  - **动机**:dsh-m 至今没有诊断能力——know-how 014 的「每次 DSH 升级后重跑农场测活」是唯一现役周期必查项(曾 81 条悬空、230 行手工映射留档),know-how 023 实录「账实分裂」形态;本机实扫另发现 8 个空 scope 目录 + 12 个 `*.bak-*` 累积。学 dsh-market check.ts 的设计纪律(纯 FS 边界、三级严重度、unknown≠broken、误报记账、修复责任外移)落地 Day1 子集,选点裁决与对比详见 ADR-0010 与对比报告(2026-10-04)。
14
21
  - **新增**:`src/core/doctor.ts` 纯函数核心(无进程/无网络/无写入,任意时刻可安全调用)+ `/dshm` 新 method `doctor` + CLI `dshm doctor [--json]`(error 级发现 exit 1;HELP 同步)。三项检查:**农场测活**(`@deepseek-ai/*` 符号链祖先链遍历;悬空=error;dsh 伞包指向旧运行时 store=提示级——lockstep 店内非伞包版本不与 runtimeVersion 比较,防 cordis 等误报)、**残留物清点**(空 scope / 无 manifest 目录 / pnpm `*_tmp_*` / `*.bak-*`,全部零告警清单,「可见而非清理」)、**账实一致**(pin/实装/lock 三处核对,不一致=warning;lockfile 仅认 9.0 importers 形状,其余 unknown 不猜)。
15
22
  - **边界纪律(ADR-0010)**:runtimeVersion 仅用 `readLauncherPackageVersion` 纯 FS 通路(CLI 进程下为 null → stale 判定整体降级 unknown,绝不 spawn);密钥红线只禁含密钥**配置文件**内容(包元数据 version 字段可读);布局判定 workspace 声明优先(本机「hoisted 声明 + 仅 lock.yaml 的残留 .pnpm」并存形态实证);双市场并存(dsh-m+dshmarket 同装)信息级呈现;doctor 永不修复,建议以文字给出。
16
- - **验收锚点(发版装机后核对)**:本机首跑 farmChecked ≈236(=0 即遍历空转)、悬空 0、8 空 scope + 12 bak 入清单、双市场信息级出现。
23
+ - **验收结算(2026-10-04 装机实测)**:farmChecked=236(精确命中评审实测值)/ 悬空 0 / 8 空 scope + 12 bak 入清单 / 账实 13:0 / 双市场信息级 / errors=0 → exit=0 / `--json` 可解析。宿主 method 信封正确但 runtimeVersion=null——**发版日新发现**:本机宿主经 pnpm 全局 shim 启动(`node ~/.local/share/pnpm/dsh web`),`readLauncherPackageVersion` 从 shim 三级上溯落空,stale 判定双通路降级(底层事实人工核对成立:农场 dsh 链接 0.1.7-rc.2 vs 运行时 0.2.0-rc.2;版本第三源列下一批)。另录既有 bug:`.bin/dshm` 符号链接静默 no-op(后经考古为 v0.2.0 起潜在、本机 0.9.28 装机形态首次踩中,见 0.9.30 条目——`invokedDirectly` 比较 symlink 路径与 realpath 永假,bin 入口全命令静默 exit 0;修复列 0.9.30 候选,期间用 `node …/dsh-m/lib/cli.js` 直达)。
17
24
  - **验证**:新增 `tests/doctor.test.mjs`(36 例:布局冲突并存/祖先链反空转/两级 targetVersion/降级路径/零告警清单/023 形态/link 协议/lock peer 后缀/CLI 子进程)+ `tests/doctor-api.test.mjs`(2 例:method 信封真跑 + 空 profile);全量 1076 pass / 0 fail / 0 skipped。
18
25
 
19
26
  ### 0.9.28 变更:页大小档位去上游化——32/64/96 取代 24/48/96,默认 32
@@ -291,6 +298,13 @@ The full release history of dsh-m, maintained bilingually: **Chinese first, Engl
291
298
 
292
299
  ## English
293
300
 
301
+ ### Fixed in 0.9.30 — bin symlink silent no-op (F2) + realpath third source for version resolution (F1)
302
+
303
+ - **Shared root cause**: two critical pnpm-ecosystem entry points are **symlinks** — `node_modules/.bin/dshm` (→ lib/cli.js) and the global shim `PNPM_HOME/dsh` (→ the real bin.js) — while both code sites made path-identity decisions before realpath.
304
+ - **F2 (latent since the bin entry shipped in v0.2.0; first hit and proven on this machine's 0.9.28 install shape; 🔴 every command via the bin entry silently exits 0)**: cli.ts `invokedDirectly` compared `argv[1]` (link path) against `import.meta.url` (node's realpath) — never equal, so `dshm <any command>` printed nothing and exited. Fix: realpath `argv[1]` before comparing. New `tests/cli-bin-symlink.test.mjs` (revival via symlinked --help and doctor --json; EPERM environments skip per repo precedent).
305
+ - **F1 (proven at 0.9.29 install acceptance)**: dsh-version.ts `readLauncherPackageVersion` walked up three levels from the shim location, landing in the pnpm home tree — pure-FS version resolution was unreachable under this host topology (`node PNPM_HOME/dsh web`), degrading doctor's stale judgment and forcing the ping chip onto the spawn fallback. Fix: realpath the entry after the shape check, then walk (locally verified resolving 0.2.0-rc.2). New shim-shape case in dsh-version.test.mjs.
306
+ - **Verification**: targeted 9/9 (two new cases); full suite 1079 pass / 0 fail / 0 skipped; typecheck clean. Post-install expectations: `dshm doctor` works via the bin entry; host method path reports runtimeVersion=0.2.0-rc.2 with stale judgment live (the farm's dsh link at 0.1.7-rc.2 is a ready-made stale form until healed).
307
+
294
308
  ### Added in 0.9.29 — profile Doctor: `dshm doctor` (read-only: farm liveness / residue inventory / manifest-reality consistency)
295
309
 
296
310
  - **Motivation**: dsh-m had no diagnostics at all — know-how 014's "re-run farm liveness after every DSH upgrade" was the only standing periodic check (81 dangling links and a 230-line manual heal log), know-how 023 documented the manifest-reality split, and a live scan found 8 empty scope dirs + 12 `*.bak-*` files. Day1 subset adopting dsh-market check.ts's design disciplines (pure-FS boundary, three-tier severity, unknown≠broken, false-positive bookkeeping, repair responsibility shifted outward). See ADR-0010 and the comparison report (2026-10-04).
package/lib/cli.js CHANGED
@@ -15,7 +15,7 @@ import { COMMUNITY_CATEGORY_LABELS } from './core/community.js';
15
15
  import { scheduleRestart } from './core/restart.js';
16
16
  import { upgradeEffectLine } from './core/activation.js';
17
17
  import { pathToFileURL } from 'node:url';
18
- import { resolve } from 'node:path';
18
+ import { realpathSync } from 'node:fs';
19
19
  /** 分类中文标签(0.7.0 Task 6;0.9.16 策展五桶):精选策展桶本地表 + 社区已知标签单一事实源;未知 slug 原样。 */
20
20
  const CLI_CATEGORY_LABELS = {
21
21
  essentials: '装机必备',
@@ -474,7 +474,11 @@ const invokedDirectly = (() => {
474
474
  if (!entry)
475
475
  return false;
476
476
  try {
477
- return import.meta.url === pathToFileURL(resolve(entry)).href;
477
+ // F2(0.9.28-0.9.29 实证):pnpm 的 node_modules/.bin/dshm 是指向 lib/cli.js 的
478
+ // 符号链接——node 对入口做 realpath,import.meta.url 是真实路径而 argv[1] 仍是
479
+ // 链接路径,直接比较永假 → bin 入口全命令静默 exit 0。比较前同样 realpath。
480
+ const realEntry = realpathSync(entry);
481
+ return import.meta.url === pathToFileURL(realEntry).href;
478
482
  }
479
483
  catch {
480
484
  return false;
@@ -11,7 +11,7 @@
11
11
  * .pnpm 目录名多版本残留无判据——均不走。
12
12
  */
13
13
  import { dirname, join } from 'node:path';
14
- import { readFileSync } from 'node:fs';
14
+ import { readFileSync, realpathSync } from 'node:fs';
15
15
  import { spawn } from 'node:child_process';
16
16
  import { dshArgv } from './dsh-cli.js';
17
17
  /** 从 `dsh --version` 输出提取 semver:容忍 v 前缀与前后散文,取首个命中。 */
@@ -26,10 +26,14 @@ export function parseDshVersionOutput(text) {
26
26
  export function readLauncherPackageVersion(input = {}) {
27
27
  try {
28
28
  const argv = dshArgv(input);
29
- const entry = argv.args.length > 0 ? argv.args[argv.args.length - 1] : undefined;
29
+ let entry = argv.args.length > 0 ? argv.args[argv.args.length - 1] : undefined;
30
30
  // 与 dshArgv 同一判定:entry 必须长得像 launcher bin,否则是 CLI/PATH 模式
31
31
  if (!entry || !/[\\/](?:bin\.(?:js|ts)|dsh)$/.test(entry))
32
32
  return null;
33
+ // F1(0.9.29 装机验收实证):pnpm 全局 shim(如 PNPM_HOME/dsh)是指向真实 bin.js
34
+ // 的符号链接——realpath 后再向上找 package.json,否则会在 pnpm home 目录树上
35
+ // 空走三级,宿主内纯 FS 解析落空(ping chip 被迫吃 spawn 回退)。
36
+ entry = realpathSync(entry);
33
37
  let dir = dirname(entry);
34
38
  for (let up = 0; up < 3; up++) {
35
39
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-m",
3
- "version": "0.9.29",
3
+ "version": "0.9.30",
4
4
  "description": "可自定义收录清单的 DSH 插件市场:收录、安装、卸载、升级一站完成,附 agent 工具与 dshm CLI。",
5
5
  "license": "MIT",
6
6
  "type": "module",