问题(Problem)
extensions/shared/child-session.ts:177-179 在模块顶层用裸调的 realpathSync.native 计算常量:
const CHILD_DISABLED_OPENPI_EXTENSION =
"-extensions/git-info/index.ts" as const;
const OPENPI_GIT_INFO_EXTENSION_PATH = realpathSync.native( // 无 try/catch
fileURLToPath(new URL("../git-info/index.ts", import.meta.url)),
);
realpathSync.native 对不存在的路径抛 ENOENT。因为这是在模块顶层执行(不在任何函数内),
一旦 extensions/git-info/index.ts 缺失,整个模块加载失败。
该模块导出的是子会话核心能力:CHILD_SAFE_PACKAGE_TOOL_NAMES、CHILD_EXCLUDED_TOOL_NAMES、
createChildResources、bindChildSessionExtensions、childToolPolicy、
shutdownAndDisposeChildSession。它被 7 个文件 import(subagents/index.ts、
subagents/src/agent-types.ts、subagents/src/backends/pi.ts、subagents/src/prompt.ts、
workflows/index.ts、workflows/replay-safety.ts、workflows/runner.ts)。
所以一个文件的缺失,会把一个纯展示性的小扩展,放大成所有 Direct Subagent 和 Workflow 子会话的
完全瘫痪,且没有任何降级路径。
复现(已实测)
mv extensions/git-info/index.ts /tmp/ # 模拟 git-info 缺失
node --experimental-strip-types -e "await import('./extensions/shared/child-session.ts')"
# ❌ ENOENT: no such file or directory, realpath '.../extensions/git-info/index.ts'
为什么值得修(尽管触发条件边缘)
正常 npm 安装是原子的,生产环境很少触发。但两条现实路径会踩到:
- Fork 裁剪——仓库 README 明确把 OpenPI 定位为可裁剪的东西("don't recommend hard
copying it")。删掉 git-info 这种纯展示扩展是合理的裁剪;它应该让你失去 git 信息,
而不是失去整个编排层。
- 打包漂移——package.json 的 files 决定发布内容。将来某次重构移动/改名 git-info
而没有同步这行,发出去的包在用户机器上就是"git-info 缺失"——升级后才发现所有子会话
加载即死。
讽刺的是,同一文件往下 4 行就是正确写法(canonicalExistingPath 用 try/catch 包住
realpathSync.native 并返回 undefined);顶层常量只是没用它。
建议修复
把路径改为惰性 + 容错——在 excludeOpenPiGitInfoExtension 内部用已有的
canonicalExistingPath 计算,文件缺失时降级为"不排除 git-info"(fail-open 到多带一个
无害扩展),而不是让模块加载失败:
function excludeOpenPiGitInfoExtension(
resources: LoadExtensionsResult,
): LoadExtensionsResult {
const gitInfoPath = canonicalExistingPath(
fileURLToPath(new URL("../git-info/index.ts", import.meta.url)),
);
if (gitInfoPath === undefined) return resources; // git-info 缺失;无需排除
return {
...resources,
extensions: resources.extensions.filter(
(extension) =>
canonicalExistingPath(extension.resolvedPath) !== gitInfoPath,
),
};
}
问题(Problem)
extensions/shared/child-session.ts:177-179在模块顶层用裸调的realpathSync.native计算常量:realpathSync.native 对不存在的路径抛 ENOENT。因为这是在模块顶层执行(不在任何函数内),
一旦 extensions/git-info/index.ts 缺失,整个模块加载失败。
该模块导出的是子会话核心能力:CHILD_SAFE_PACKAGE_TOOL_NAMES、CHILD_EXCLUDED_TOOL_NAMES、
createChildResources、bindChildSessionExtensions、childToolPolicy、
shutdownAndDisposeChildSession。它被 7 个文件 import(subagents/index.ts、
subagents/src/agent-types.ts、subagents/src/backends/pi.ts、subagents/src/prompt.ts、
workflows/index.ts、workflows/replay-safety.ts、workflows/runner.ts)。
所以一个文件的缺失,会把一个纯展示性的小扩展,放大成所有 Direct Subagent 和 Workflow 子会话的
完全瘫痪,且没有任何降级路径。
复现(已实测)
为什么值得修(尽管触发条件边缘)
正常 npm 安装是原子的,生产环境很少触发。但两条现实路径会踩到:
copying it")。删掉 git-info 这种纯展示扩展是合理的裁剪;它应该让你失去 git 信息,
而不是失去整个编排层。
而没有同步这行,发出去的包在用户机器上就是"git-info 缺失"——升级后才发现所有子会话
加载即死。
讽刺的是,同一文件往下 4 行就是正确写法(canonicalExistingPath 用 try/catch 包住
realpathSync.native 并返回 undefined);顶层常量只是没用它。
建议修复
把路径改为惰性 + 容错——在 excludeOpenPiGitInfoExtension 内部用已有的
canonicalExistingPath 计算,文件缺失时降级为"不排除 git-info"(fail-open 到多带一个
无害扩展),而不是让模块加载失败: