Replies: 2 comments
|
Your measurement is right, and it matches mine — I want to add the one variable that explains the whole family, because the framing "tsx 启动时" is one step short of the cause, and it changes who is affected. The trigger is the hook chain, not tsxI ran the same experiment as your §1, one variable at a time, on the same Node. Reading the descriptor of the real
The no-op loader is one line — That predicts your "安装版不会出现" datapoint exactly: One corollary worth putting in the report: Cross-check against the other threadsPerryLink argued in #7518 that on Node 22.22.3 / 24.21.0 the The same root cause is filed separately as #7533 (Windows, Node 22.20.0, same tsx launch) — that is the third instance, and the fix below covers all of them. On your proposed fixYour
The invariant worth writing next to the helper: the error that reaches Who this hitsWorth adding to your report: it is not only ~578 built-in package directories. Out-of-tree plugins have the same shape — any third-party plugin without a |

Uh oh!
There was an error while loading. Please reload this page.
Summary
用源码启动的 DSH(
pnpm dsh web=node --import tsx/esm apps/cli/src/bin.ts)打开 设置 → 内置插件 时,每一个没有locale/en.json的包都显示红色错误:persona、agent-instructions、tool-bash、tool-pwsh… 全部命中(specifier 不同、错误形式相同)。安装版 dsh(非 tsx 启动)不会出现,所以看起来像"所有内置插件都坏了",实际上插件本身都正常(都显示"已启用")。问题不在插件,而在 profile resolution 的一条诊断改写路径:它假设
error.stack可写,而源码启动时 Node 会把解析错误以只读stack数据属性的形式返回,改写因此抛 TypeError,并且该 TypeError 顶替了真正的ERR_PACKAGE_PATH_NOT_EXPORTED。Environment
00102833df(release0.1.7-alpha.2)pnpm dsh web(package.json的scripts.dsh=node --import tsx/esm apps/cli/src/bin.ts)web,位于<home>/.dsh/profiles/web;解析由 runtime resolution 路由到安装目录的apps/cli/node_modulesReproduction
1. 库级最小复现(不需要 GUI)
同一个 Node、同一个解析,只是启动方式不同:
stack描述符error.stack = 'x'node repro.mjs{ get: [Function], set: [Function], configurable: true }node --import tsx/esm repro.mjs{ value: 'Error [ERR_PACKAGE_PATH_NOT_EXPORTED] …', writable: false, enumerable: false, configurable: true }2. 端到端(与 GUI 完全同一条路径)
安装 runtime interception 后直接调用
readPluginMeta:返回:
{ "error": "Plugin metadata for @deepseek-ai/dsh-persona: TypeError: Cannot assign to read only property 'stack' of object 'Error: Package subpath './locale/en.json' is not defined by \"exports\" in <checkout>/apps/cli/node_modules/@deepseek-ai/dsh-persona/package.json imported from <home>/.dsh/profiles/web/'" }3. GUI
源码启动
pnpm dsh web→ 设置 → 内置插件:每个无locale/目录的包都显示上述错误。Current behavior
locale/en.json的包都出现红色"包元信息错误",即便这些包完全正常(显示"已启用")。Package subpath './locale/en.json' is not defined by "exports"这句只作为 TypeError 的被引用对象出现。throwWithImporter、CommonJS 的throwWithoutCjsAnchor)。Root cause
readPluginMeta()(packages/boot/app-boot/src/package-meta.ts:148)先解析<specifier>/locale/en.json;这些包没有exports["./locale/*"],Node 抛ERR_PACKAGE_PATH_NOT_EXPORTED。throwWithImporter()(packages/boot/app-boot/src/profile-resolution/resolver.ts:655)把诊断里的内部 anchor 还原成真实 importer,其中包含error.stack = stack.replace(...)(第 665 行)。module.register()(异步 loader worker)。解析错误从 worker 回主线程要经过node:internal/modules/esm/hooks第 635 行throw deserializeError(body.serialized),而deserializeError(node:internal/error_serdes,第 164 行)用ObjectCreate(ctor.prototype, properties)还原错误,stack因此是writable: false的数据属性,赋值在严格模式下抛TypeError: Cannot assign to read only property 'stack' …。(普通 Node 下同一个错误是 V8 的stack访问器,赋值正常。)optionalResourcePath()(packages/boot/app-boot/src/package-meta.ts:65)通过missingResource()(第 59 行)按error.code判断"子路径不存在";TypeError 没有code,于是向外冒泡。readPluginMeta()的兜底catch(第 169-171 行)把它渲染成{ error: 'Plugin metadata for …' },GUI 显示为"包元信息错误"。即:一个纯诊断性的 stack 改写,把"无 locale 子路径 = 无元信息、回退 package.json"的正常路径,变成了全列表报错。
Expected behavior
locale/en.json时,readPluginMeta应视为"无 locale 资源",回退到package.json的name/description(或者返回undefined),不应产生错误卡片——这正是optionalResourcePath+missingResource现有的设计。code、message),不能用一个 TypeError 顶替它;code是调用方判断"资源缺失"的唯一依据。Suggested fix(供参考,未提交 PR)
packages/boot/app-boot/src/profile-resolution/resolver.ts:把两处error.stack = …换成对描述符友好的写法,例如调用点:
throwWithImporter(665 行)与throwWithoutCjsAnchor(688 行),后者有同一处假设。回归用例可以直接构造"只读 stack"的解析错误(例如
module.registerHooks()抛出一个stack为writable: false的ERR_MODULE_NOT_FOUND),断言抛出的仍是原错误、且 message/stack 已还原成真实 importer。我在本地按这个方案验证过:修复前用例复现截图里的 TypeError,修复后profile-resolution.spec.ts225/225 通过,app-boot 全包 5455 passed。顺带发现(本次未处理)
vendor/cordis里也有同样的"直接给error.stack赋值"假设:vendor/cordis/src/utils.ts:248,263与vendor/cordis/src/reflect.ts:76。因为 vendor 修改要走 同步流程,这里只做记录:任何被composeError包裹的插件调用,如果抛出的是 deserialized 错误,同样会被 TypeError 顶替。Impact
All reactions