// viewer-status.mts —— 查看器版本与漂移诊断(供写盘桥的 /api/viewer-status 调用) // // 为什么单独一个 CLI 而不是在桥里重算一遍哈希: // 桥是 .mjs,不能 import .ts。而"源码内容版本号"的算法**已经在 `memory/viewers/deploy.ts` 里** // (`scanViewerSource`)—— 桥若照抄一份,公式一改就会出现「明明已部署却一直报漂移」或漏报。 // 这是本次要根治的失效模式本身,所以桥只做转发,算法只有一份。 // // 输出(stdout JSON): // { sourceVersion, deployedVersion, drifted, sourceRoot, deployedAt, files, retired } // // 用法:node --experimental-strip-types scripts/l1-admin/viewer-status.mts [--profile ] import { argv, stdout } from "node:process"; import { readDeployedManifest, scanViewerSource } from "../../memory/viewers/deploy.ts"; /** 取 `--flag value` 形式的值。 */ function flagValue(flag: string): string | undefined { const i = argv.indexOf(flag); return i >= 0 ? argv[i + 1] : undefined; } // 与其它 l1-admin 工具同默认值(桥也是这个默认) const PROFILE = flagValue("--profile") ?? "C:/Users/leaf/.pi/agent/profiles/default"; const scan = scanViewerSource(); const deployed = readDeployedManifest(PROFILE); // 一律用 process.exitCode 而非 exit():stdout 是管道时写入是异步的,立刻 exit 会截断 JSON。 stdout.write( `${JSON.stringify( { sourceVersion: scan.version, sourceRoot: scan.sourceDir, sourceFiles: scan.files.length, deployedVersion: deployed?.version ?? null, deployedAt: deployed?.generatedAt ?? null, retired: deployed?.retired ?? [], // 漂移 = 源码内容版本与已部署版本不一致(源码改了但扩展还没重新部署) drifted: !!deployed && !!scan.version && deployed.version !== scan.version, // 从未部署过 neverDeployed: !deployed, errors: scan.errors, }, null, 2, )}\n`, ); process.exitCode = 0;