DeepSeek Harness 插件 failed to import 真凶:process/ 拿到 null
装上一个带 CJS 依赖的插件之后,DSH 启动日志只给你两行、且一个字的原因都没有:dsh: warning: 1 entry did not activate 和 whale_craft (whale_craft): failed to import。 真实错误被宿主吞掉了,用探针插件在加载器上下文里才拿到它:
TypeError: createRequire.resolve.paths is not a function or its return value is not iterable
at ResolutionRouter.routeScoped (…/@deepseek-ai/dsh-app-boot/lib/index.js:1422:58)
真因不是「那个 API 不存在」,而是它的返回值是 null:触发插件 readable-stream@4.7.0 里写了 require('process/')(注意尾斜杠),isBuiltin('process/') 为 false 所以它正常进入解析路由;DSH 随后用 barePackageName() 把请求剥成裸包名 process 再去问 resolve.paths(),命中「核心模块返回 null」这条 Node 语义,紧接着 for…of null 直接崩(#8674)。而 V8 把「不是函数」与「返回值不可迭代」两种情形合并成同一句报错,让这句提示极具误导性——我们分三轮才定位到。 更要紧的是:0.2.0-rc.2 与 master 都还没修,升级解决不了(#8674)。下面按「先分诊 → 根因 → 定位过程 → 版本矩阵 → 修法 → 最小复现」展开,全部给出可粘贴的代码与可复跑的命令。
先分诊:failed to import 为什么不给原因,以及怎么自己拿到
关键结论:这不是依赖缺失,也不是插件写错了——原生 Node 下那句 require('process/') 解析完全正常。 但在怀疑任何东西之前,先把被吞掉的错误逼出来。
1. 认识这条日志的局限
宿主在 inactiveEntries() 里遇到 fiber === undefined 时,只记下一句:
dsh: warning: 1 entry did not activate
whale_craft (whale_craft): failed to import
底层 import 异常被完全丢弃。 也就是说,日志能告诉你的只有「哪个条目没激活」,不能告诉你「为什么」。
2. 用探针插件把真实错误写出来
写一个最小探针插件,让它自己声明 dsh.bundle.patch 从而被当作 bundle 加载,然后在加载器上下文里逐个试 import,把异常写进文件:
// profile/node_modules/<probe>/index.js
import { appendFileSync } from 'node:fs'
const OUT = '<某处>/probe-result.txt'
const log = (s) => { try { appendFileSync(OUT, s + '\n') } catch {} }
for (const spec of [
'@deepseek-ai/schemastery',
'@deepseek-ai/dsh-tools',
'@deepseek-ai/dsh-llm',
'mineflayer',
'whale_craft',
]) {
try {
await import(spec)
log(`OK ${spec}`)
} catch (error) {
log(`FAIL ${spec}`)
log(String((error && (error.stack || error.message)) || error))
}
}
export const name = 'probe'
export function apply() {}
结果很有信息量:宿主包全部 OK,失败集中在 mineflayer 及其依赖链上,真实错误就是上面那句 TypeError ... lib/index.js:1422。定位到这一步,方向就从「插件是不是写坏了」转到「宿主的解析钩子在这条链上做了什么」。
3. 一条命令区分「依赖缺失」和「宿主解析问题」
这两个症状都会表现为「某个 require 失败」,但修法完全不同。用纯 Node 跑三行就能分开:
const { createRequire } = require('node:module')
const req = createRequire('C:/any/dir/parent.js')
console.log(req.resolve.paths('process')) // null ← 内置名
console.log(req.resolve.paths('process/')) // 18 条搜索链 ← 加斜杠就绕开内置判定
console.log(req.resolve('process/')) // …/node_modules/process/index.js
第三行能解析出真实的 node_modules/process/index.js,说明包是装着的、原生解析是通的——问题不在依赖,在宿主的解析路由。
根因:剥掉斜杠之后,请求变成了「对核心模块名的查询」
一句话结论:resolve.paths() 对核心模块名返回 null,而调用点直接 for…of,于是 TypeError。
1. 崩溃点
@deepseek-ai/dsh-app-boot 的 ResolutionRouter.routeScoped(0.1.7-rc.2 第 1422 行):
for (const searchPath of createRequire(parent).resolve.paths(name)) { // ← 崩在这
把这条链拆开就是:
readable-stream@4.7.0 lib/internal/streams/end-of-stream.js:8
const process = require('process/') ← 尾斜杠;该包 dependencies 里确实有 process
│
├─ isBuiltin('process/') === false → 不被当内置模块拦下,正常进入解析路由
│
└─ barePackageName('process/') === 'process' → 剥成裸包名
│
└─ resolve.paths('process') === null → 命中「核心模块」分支
│
└─ for (… of null) → TypeError
2. 那句误导性报错
V8 对「for-of 里的调用表达式」会把两种完全不同的情形合并成同一句话:
TypeError: createRequire.resolve.paths is not a function or its return value is not iterable
报错文本指向的 API 其实是存在的,真因是返回值不可迭代(是 null)。正是这个合并让排查多花了两轮(先怀疑 monkey-patch 没带上 resolve.paths,再怀疑 createRequire 用错)。
3. 实测数据(Node v26.7.0 / win32)
| 表达式 | 结果 |
|---|---|
resolve.paths('process') | null |
resolve.paths('process/') | 18 条搜索链 |
resolve.paths('fs') | null |
resolve.paths('fs/') | 18 条搜索链 |
resolve.paths('mineflayer') | 正常数组(非内置名不受影响) |
resolve('process/') | <profile>\node_modules\process\index.js ✅ |
规律非常干净:内置名 → null,内置名加斜杠 → 完整搜索链,普通包名 → 正常数组。
4. 为什么 ?? [] 不够(第二个坑)
同一文件第 883 行的同款调用带了 ?? [],其余调用点漏了。但实测表明,只补空数组会把错误换个样子继续存在:
// 只加 ?? [] 之后
Error: Cannot find module 'process/'
原因是 localSearchPaths 变成空数组 → cjs.resolveNative([]) 失败 → 那条 require 仍然挂。必须给出正确的搜索链,不能只兜空。
5. 同类写法的分布
同文件同款写法还在 1247 / 1475 / 2019 / 3263;worker 侧 lib/worker/profile-resolution-bootstrap.js 第 310 / 363 行也有。本地规避补丁覆盖两个文件共 9 处调用点——只改崩溃那一行,别的链上仍会重演。
定位过程:四步,供遇到同类问题的人复用
这一节的价值在于流程,不在于结论。 起点很普通:装一个带 CJS 依赖的插件 → 启动只得到一行 failed to import、没有任何原因。
- 把被吞掉的错误逼出来。 报错在 loader 内部,普通日志看不到。写探针插件(自己声明
dsh.bundle.patch被当作 bundle 加载),在 loader 上下文里try { await import(spec) } catch (e) { 写文件 },逐个试@deepseek-ai/schemastery、dsh-tools、mineflayer、目标插件本身。结果:宿主包都 OK,mineflayer与其依赖链失败,真实错误是TypeError ...(指向lib/index.js:1422)。 - 先试常规兜底,发现不够。 给那句循环加上
?? []后,错误前进成Cannot find module 'process/'——说明搜索链被清空,只兜空数组是错的方向。 - 直接问 Node 要事实。 实测两个调用:
resolve.paths('process')→ null;resolve.paths('process/')→ 18 条搜索链。根因就此明确,同时解释了那句报错为什么具有误导性。 - 验证修法并反查上游。 按守卫写
dshResolvePaths()(拿不到就加斜杠重试)替换全部调用点 → 插件正常加载;随后搭最小 home(只装readable-stream+ 探针)对照0.2.0-rc.2,确认最新发布版同样中招。
两条经验值得单独记下来:
- 宿主把 import 的真实异常吞成一句话,排障成本高一个数量级;哪怕只在
DSH_DEBUG=1下带上底层异常,也能省掉上面第 1 步。 - 类型断言会掩盖运行期风险:master 里这些调用写的是
resolve.paths(name) as string[],as是编译期谎言,运行期null照样穿过类型检查。
版本矩阵:这不是「升级就好」的问题
先给结论:0.1.7-rc.2 实测复现、0.2.0-rc.2(npm 最新发布)实机复现未修、master 源码同样未修。
| 版本 | 结论 | 依据 |
|---|---|---|
0.1.7-rc.2 | 实测复现 | 本机启动 whale_craft 即 failed to import;探针拿到 TypeError(lib/index.js:1422) |
0.2.0-rc.2(npm 最新发布) | 实机复现,未修 | ① 与 0.1.7-rc.2 逐行 diff:只有 1 个 hunk,且是无关的 bundle 列表新增(@deepseek-ai/dsh-experimental-schedule-bundle);routeScoped 函数体 63 行逐字节相同;无守卫的 resolve.paths(name) 仍在 1248 / 1423 / 1476 / 2020 / 3264。② 最小 home 实机验证:仍抛同一句 TypeError、同一调用栈,只是行号变 1423 |
master(源码) | 同样未修 | packages/boot/app-boot/src/profile-resolution/resolver.ts 第 244 / 471 / 524 行为同写法,仅多一个 as string[] 断言;全文件 Array.isArray 出现 0 次,?? 均不在这些调用点。且该文件最近一次改动是 2026-09-23,此后未再变动 |
| 发布记录 | 无相关修复 | 最新 tag dsh-v0.2.0-rc.2;release notes 未提及 resolve.paths / CJS 解析 |
这不是新问题。 讨论区用 resolve.paths 搜索得到 19 条结果,其中至少 5 条是同源报告,触发包各不相同、跨度从 0.1.6-alpha 到 0.1.7-rc.2,且没有任何一条拿到修复或维护者表态:
| # | 日期 | 触发链 | 亮点 |
|---|---|---|---|
| #7031 | 09-18 | jsdom → whatwg-url → tr46 → require("punycode/") | 最早;已指出「本地加 ?? [] 后错误前进到 Cannot find module 'punycode/'」;0.1.6-alpha.1 vs .2 对照 |
| #7377 | 09-21(+09-30 评论) | ExcelJS → process/(另有 string_decoder/) | 给出三行最小复现;09-30 补充:0.2.0-rc.2(桌面版)仍受影响 |
| #7903 | 09-26 | jsdom → … → punycode/ | 明确「0.1.7 重写的 ResolutionRouter 不再守卫 resolve.paths()」;0.1.5-rc.2 正常 vs 0.1.7-rc.2 失败 |
| #7911 | 09-26 | 顶层调用 createRequire(...).resolve.paths() | 解释「劫持后的 require.resolve 未携带原生 resolve.paths」;点名 whale_craft、行号 1422 |
| #8250 | 09-29 | 静态依赖 msedge-tts | 探针式逐包二分;指出「全新 DSH_HOME 正常、旧 home 失败」的环境差异 |
顺带记一个方法论教训:报告人初版曾写「此前无人报过」,因为他只用 GitHub 的 REST 搜索(只覆盖 issue 与 PR)得到 0 命中——REST 搜索不覆盖 Discussions。改用讨论区搜索后是 19 条。在 GitHub 上判断「是否已有人报过」,必须包含 Discussions。
修法:三种守卫,从最小改动到长期方向
修法 A(最小改动,1 行):加斜杠重试
const paths = resolve.paths(name) ?? resolve.paths(name + "/")
实测拿到 18 条搜索链。原理是尾斜杠让名字不再被判定为核心模块,从而走回正常的搜索链计算。
修法 B:用 Node 自己的算法兜底
const paths = resolve.paths(name) ?? Module._nodeModulePaths(dirname(parent))
实测得到 15 条搜索链。这个文件别处已经在用 _nodeModulePaths,所以风格上是一致的。
修法 C(本地规避补丁):统一守卫函数,替换全部调用点
把两个文件里的 createRequire(x).resolve.paths(y) 全部换成守卫版本:
/**
* 守卫 require.resolve.paths 的两种失败:
* 1) 函数不存在 → 返回 fallback
* 2) 返回 null(核心模块名)→ 加斜杠重试绕开内置判定
* 注意:不能只 `?? []`,否则同一条 require 会前进成 Cannot find module。
* 参照 jestjs/jest#16052 的守卫写法。
*/
function dshResolvePaths(req, name) {
try {
const resolvePaths = req && req.resolve && req.resolve.paths
if (typeof resolvePaths === 'function') {
const value = resolvePaths.call(req.resolve, name)
if (Array.isArray(value)) return value
const retry = resolvePaths.call(req.resolve, name + '/') // ← 绕开内置判定
if (Array.isArray(retry)) return retry
}
} catch {
/* 拿不到就让调用方回退到原生解析 */
}
return []
}
接入要点:
0.1.7-rc.2共 9 处,分布在lib/index.js与lib/worker/profile-resolution-bootstrap.js;0.2.0-rc.2位置完全对应(lib/index.js行号 +1),改写方式相同;- 这是改宿主运行时文件的做法,DSH 升级后会被覆盖(新版本目录是另一份),需要重打;
- 上游修好后建议删掉;补丁只影响解析回退路径,
resolve.paths()正常返回数组时行为与上游完全一致。
打补丁后的实测效果
| 阶段 | 结果 |
|---|---|
| 打补丁前 | 只有 dsh: warning: 1 entry did not activate + whale_craft (whale_craft): failed to import(真错误被吞) |
| 打补丁后 | 探针逐项 OK:@deepseek-ai/schemastery / dsh-tools / dsh-llm / mineflayer / whale_craft;启动日志不再有 failed to import;插件自身日志出现「插件已加载|工具注册中…」,29 个 mc_* 工具全部可用 |
长期方向:别 monkey-patch Module._resolveFilename
这类对 CJS 解析器的 monkey-patch 有已知的生态风险,官方替代是:
// Node ≥22.15 / 24 稳定
import { registerHooks } from 'node:module'
registerHooks({ resolve(/* … */) { /* … */ } })
最小复现:两种,都不依赖具体插件
要给维护者报,或者要自己确认「是不是同一条」,用下面任一种,两分钟就能跑。
方式一:零依赖、跨机器可跑的纯 Node 脚本
#!/usr/bin/env node
import { createRequire, _nodeModulePaths } from 'node:module'
import { dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const argv = process.argv.slice(2)
const parentArg = argv.indexOf('--parent')
const parent = parentArg >= 0 && argv[parentArg + 1] ? argv[parentArg + 1] : fileURLToPath(import.meta.url)
const req = createRequire(parent)
const line = (label, value) => console.log(` ${label.padEnd(34)} → ${value}`)
console.log('① 关键数据(这就是根因)')
for (const name of ['process', 'process/', 'fs', 'fs/']) {
const value = req.resolve.paths(name)
line(`resolve.paths(${JSON.stringify(name)})`,
Array.isArray(value) ? `${value.length} 条搜索链` : String(value))
}
console.log('\n② 原生解析本身是好的(说明不是依赖缺失)')
try { line("resolve('process/')", req.resolve('process/')) }
catch (error) { line("resolve('process/')", `${error.code ?? error.name}: 本机没装 process 包(不影响结论)`) }
console.log('\n③ 复刻 DSH 的写法:把请求剥成裸包名后直接 for...of')
const bare = 'process' // barePackageName('process/')
try {
for (const searchPath of req.resolve.paths(bare)) void searchPath
console.log(' …没崩(本机环境与报告环境不同)')
} catch (error) {
line('for (… of resolve.paths(bare))', `${error.constructor.name}: ${error.message}`)
}
console.log('\n④ 两种建议修法(都能拿到可迭代的搜索链)')
const fixedA = req.resolve.paths(bare) ?? req.resolve.paths(`${bare}/`)
line('A. paths ?? resolve.paths(name + "/")', Array.isArray(fixedA) ? `${fixedA.length} 条搜索链 ✅` : String(fixedA))
const fixedB = req.resolve.paths(bare) ?? _nodeModulePaths(dirname(parent))
line('B. paths ?? _nodeModulePaths(dirname)', Array.isArray(fixedB) ? `${fixedB.length} 条搜索链 ✅` : String(fixedB))
console.log('\n⑤ 反例:只加 `?? []` 是不够的')
const empty = req.resolve.paths(bare) ?? []
line('paths ?? [] 得到的搜索链长度', `${empty.length} ← 搜索链为空时,同一条 require 会变成 Cannot find module`)
跑法(--parent 指向任意位于 node_modules 深处的文件即可):
node repro-resolve-paths.mjs --parent ".../node_modules/readable-stream/lib/internal/streams/end-of-stream.js"
本机输出(Node v26.7.0 / win32):
① 关键数据(这就是根因)
resolve.paths("process") → null
resolve.paths("process/") → 18 条搜索链
resolve.paths("fs") → null
resolve.paths("fs/") → 18 条搜索链
③ 复刻 DSH 的写法:把请求剥成裸包名后直接 for...of
for (… of resolve.paths(bare)) → TypeError: req.resolve.paths is not a function or its return value is not iterable
⑤ 反例:只加 `?? []` 是不够的
paths ?? [] 得到的搜索链长度 → 0 ← 搜索链为空时,同一条 require 会变成 Cannot find module
方式二:最小 profile 复现(走真实加载器,不装任何第三方插件)
四步搭一个干净 home:
- 建目录
<home>/profiles/web/plugins/cjs-probe/; - 写 profile 的
package.json:
{
"name": "dsh-profile-web",
"private": true,
"dependencies": {
"readable-stream": "4.7.0",
"cjs-probe": "file:plugins/cjs-probe"
},
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"cjs-probe"
]
}
}
}
- 写探针三件套——
index.js负责require('readable-stream')并把异常写文件;package.json必须声明dsh.bundle.patch,否则 bundle 不会被加载:
{
"name": "cjs-probe",
"version": "0.0.1",
"private": true,
"type": "module",
"main": "index.js",
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}
# cordis.patch.yml
- insert:
- id: cjs-probe
name: cjs-probe
// index.js
import { appendFileSync } from 'node:fs'
import { createRequire } from 'node:module'
const OUT = '<某处>/minimal-result.txt'
const log = (s) => { try { appendFileSync(OUT, s + '\n') } catch {} }
log(`===== cjs-probe @ ${new Date().toISOString()} =====`)
const req = createRequire(import.meta.url)
try {
const rs = req('readable-stream')
log('OK require("readable-stream") 成功 version=' + (rs && rs.Writable ? 'has Writable' : '?'))
} catch (e) {
log('FAIL require("readable-stream")')
log(String((e && (e.stack || e.message)) || e))
}
export const name = 'cjs-probe'
export function apply() {}
- 装依赖并启动:
pnpm install --dir <home>/profiles/web --node-linker=hoisted # store 指向你自己的
DSH_HOME=<home> node <dsh>/lib/bin.js --profile web --port 3399
预期:结果文件里出现
TypeError: createRequire.resolve.paths is not a function or its return value is not iterable
at ResolutionRouter.routeScoped (…/@deepseek-ai/dsh-app-boot/lib/index.js:1423:58) # 0.1.7 是 1422
0.2.0-rc.2 原样启动即复现(stack 同上),所以升级到最新发布版并不能规避。
排查注意事项
failed to import不等于「插件本身有 bug」。先把它依赖链里的 CJS 包找出来,再看宿主解析钩子。TypeError: X is not a function or its return value is not iterable要当成两个假设分别验证:本例中函数是存在的,崩的是返回值(null)。- 别停在
?? []。它把 TypeError 换成Cannot find module,看起来「换了个错」,实际是同一个坑。 resolve.paths()的返回值有三种可能:数组、null(核心模块)、函数不存在(被劫持过)。守卫必须三个都覆盖。- 只修崩溃那一行不够。同款写法在两个文件共 9 处,替换时要全部覆盖。
- 注意
as string[]这类编译期断言。它让null通过类型检查,是这类 bug 能长期存活的直接原因。 - 改宿主运行时文件会被升级覆盖。打补丁要记录位置,升级后重打;上游修好后删掉。
- 判断「是否已有人报过」时要搜 Discussions。REST 搜索只覆盖 issue/PR,会漏掉整类报告。
- 报问题时附可复跑脚本。本例这类「装某个具体插件才会触发」的问题,一个零依赖复现脚本能显著提高被处理的概率。
dsh-why一类诊断工具可能还不认识这个模式(它当前的规则库只覆盖客户端模块表那一类错误)。判别特征是:报错里出现resolve.paths,且插件带 CJS 依赖。
来源
- #8674 — [Bug] dsh-app-boot 的 CJS 解析钩子被 require('process/') 打崩:resolve.paths() 返回 null → 插件 failed to import
- #7031 / #7377 / #7903 / #7911 / #8250 —— 同源历史报告
- 守卫写法参考:jestjs/jest#16052 — Guard missing
require.resolve.paths - monkey-patch 爆炸半径与替代方案盘点:nub — Monkey-patching of Node's CJS resolver
- 同类讨论:#380(软链接真实路径导致
createRequire找不到宿主包)、#5235(插件把 loader 搞崩的实战处置)
文中所有行号、diff 结论、搜索链条数与补丁前后效果均为报告人在本机的实测记录(Node v26.7.0 / win32);0.2.0-rc.2 的「逐字节相同」与 master 源码核实亦来自同一份报告。
排查「插件装上却加载不起来」这类问题时,最麻烦的往往是确认到底是哪个插件、哪个版本、哪条依赖链在出问题——尤其当启动日志只给一句 failed to import 的时候。DSH Plugin Hub 提供插件市场、已安装插件列表、自定义安装、设置与系统日志五个界面:已安装列表会标注每个插件的来源(目录插件 / 自定义安装)、版本号与更新时间,并可直接定位到安装目录;系统日志页则按分类与级别记录安装、卸载与诊断轨迹,支持导出全文。先用它把「装了什么、什么版本、装在哪」对齐,再去逐条链地查解析问题,能省掉不少来回。

常见问题
写一个「探针插件」:让它自己声明 dsh.bundle.patch 从而被当作 bundle 加载,然后在加载器上下文里 try { await import(spec) } catch (e) { 把 e.stack 写进文件 },逐个试宿主包(如 @deepseek-ai/schemastery、dsh-tools)、目标插件本身、以及它依赖链上的包。宿主包通常都 OK,失败会集中在带 CJS 依赖的那条链上。之所以必须这样做,是因为宿主在 fiber === undefined 时只记下一句 error: "failed to import",真正的 import 异常被丢掉了,普通日志里看不到。
不是。Node 文档明确写着:require.resolve.paths(request) 在 request 是**核心模块**时返回 null。这里的关键是被查询的名字:触发方写的是 require('process/')——**带尾斜杠**,因此没被 isBuiltin() 拦下、正常进入路由;而 DSH 用 barePackageName(request) 把它剥成裸包名 process 之后再去问 paths,就命中「核心模块」分支拿到 null,紧接着 for…of null 抛出 TypeError。根因是宿主少了一步守卫,不是 API 异常。
不行,这是我们实测走过的弯路。加 ?? [] 之后错误会从 TypeError 变成 Error: Cannot find module 'process/'——因为搜索链被清空,cjs.resolveNative([]) 拿不到任何候选路径,那条 require 依然挂。**必须回退到真实的搜索链**:要么给名字加一个斜杠重试(resolve.paths(name + "/"),实测拿到 18 条链),要么用 Node 自己的算法(Module._nodeModulePaths(dirname(parent)),实测 15 条链)。
不能。0.2.0-rc.2 与 0.1.7-rc.2 逐行 diff 只有 1 个 hunk,且是无关的 bundle 列表新增(@deepseek-ai/dsh-experimental-schedule-bundle),routeScoped 函数体 63 行**逐字节相同**,无守卫的 resolve.paths(name) 仍在(行号 1248 / 1423 / 1476 / 2020 / 3264)。用最小 home(只装 readable-stream@4.7.0 + 一个探针插件)实机启动 0.2.0-rc.2,仍抛同一句 TypeError、同一调用栈,只是行号从 1422 变 1423。master 源码同样未修。
触发者不止一个。讨论区用 resolve.paths 搜索有 19 条结果,其中至少 5 条同源报告,触发链各不相同:jsdom → whatwg-url → tr46 → require("punycode/")、ExcelJS → process/(另有 string_decoder/)、msedge-tts 的静态依赖树等,跨度从 0.1.6-alpha 到 0.1.7-rc.2,且都没有拿到修复。共同点是「带 CJS 依赖、且依赖链里对内置模块名做了带斜杠的 require」。所以换插件只能躲开这一个触发者,不能修好宿主。
相关术语
- require.resolve.paths(request)
- Node 的 CJS 解析 API,返回「按当前父模块位置去解析 request 时,会依次查找哪些目录」的数组;**当 request 是核心模块时返回 `null`**。正因为返回值可能是 `null` 而非空数组,任何直接 `for…of` 的写法都必须在拿到值之后先判类型。— https://github.com/deepseek-ai/deepseek-harness/discussions/8674
- barePackageName()
- DSH 解析路由里用于把请求规范成「裸包名」的函数:`'process/'` → `'process'`。它抹掉了尾斜杠,也就抹掉了 `isBuiltin('process/') === false` 这个「绕过内置判定」的副作用——剥名之前请求能被当普通包路由,剥名之后就变成了对核心模块名的查询。— https://github.com/deepseek-ai/deepseek-harness/discussions/8674
- ResolutionRouter.routeScoped
- `@deepseek-ai/dsh-app-boot` 里决定「某个 require 该按哪个作用域去搜索」的路由函数(0.1.7-rc.2 `lib/index.js:1422`、0.2.0-rc.2 对应 1423)。它通过 monkey-patch `Module._resolveFilename` 接入 CJS 解析链。本文的崩溃点就在它内部对 `createRequire(parent).resolve.paths(name)` 的 `for…of`。— https://github.com/deepseek-ai/deepseek-harness/discussions/8674
- 被吞掉的异常(failed to import)
- 宿主在 `inactiveEntries()` 里遇到 `fiber === undefined` 时,只记录 `error: "failed to import"`,不带底层异常。后果是启动日志里只剩两行没有任何原因的提示(`1 entry did not activate` + `failed to import`),真实 TypeError 完全不可见——排障成本因此高一个数量级,本次光是拿到那句话就花了三轮(探针插件 + 分步守卫)。— https://github.com/deepseek-ai/deepseek-harness/discussions/8674
来源
- #8674 — [Bug] dsh-app-boot 的 CJS 解析钩子被 require('process/') 打崩:resolve.paths() 返回 null → 插件 failed to import· deepseek-ai(GitHub Discussions)
- #7031 — 最早的同源报告:jsdom → whatwg-url → tr46 → require("punycode/")· deepseek-ai(GitHub Discussions)
- #7377 — ExcelJS → process/(另有 string_decoder/);09-30 补充确认 0.2.0-rc.2 桌面版仍受影响· deepseek-ai(GitHub Discussions)
- #7903 — 明确定位到 0.1.7 重写的 ResolutionRouter 不再守卫 resolve.paths()· deepseek-ai(GitHub Discussions)
- #7911 — 顶层调用 createRequire(...).resolve.paths():点名 whale_craft 与行号 1422· deepseek-ai(GitHub Discussions)
- #8250 — 静态依赖 msedge-tts 的同源报告:全新 DSH_HOME 正常、旧 home 失败· deepseek-ai(GitHub Discussions)