Skip to content

fix(darwin): boot the packaged Harness without Node's internal loader - #146

Merged
yaojin3616 merged 1 commit into
mainfrom
fix/macos-packaged-hmr-fallback
Aug 22, 2026
Merged

fix(darwin): boot the packaged Harness without Node's internal loader#146
yaojin3616 merged 1 commit into
mainfrom
fix/macos-packaged-hmr-fallback

Conversation

@yaojin3616

Copy link
Copy Markdown
Contributor

问题

签名打包的 macOS 正式版每次启动都在 profile boot 的同一步失败(0.4.3 用户日志 8/8):

DSH entry failed: failed to apply loader entry (@deepseek-ai/cordis-plugin-hmr):
--expose-internals is required for HMR service

对应内部文档《DSH Desktop v0.4.3 macOS 正式版启动失败:Utility Process 与 HMR 集成问题》的第二个问题(第一个是插件残留,与本 PR 无关)。

根因

runProfile 在 boot 之后发现没有 hmr 服务时会自己创建一个完整的 Cordis HMR(日志里那个随机 id 就是它),而该服务在缺少 ctx.loader.internal 时构造即抛,把 boot 的后半段一起带走——包括用户 patch 层的监听。

打包版拿不到这个 internal loader,两条路都断:

  • --expose-internals 进得了 utility process 的 execArgv,但进不了 Node 的选项解析器;
  • loader 的备选路径 node-addon-require-builtin 是按 plain Node 构建的,在 Electron 下直接报 Unsupported/no-realm

补上文档建议 1 要求的最小复现(直接验证 internal loader 对象,而非只看 process.execArgv),四组组合结果如下:

构建形态 disclaim internal loader
开发运行 false ✅ 可用
开发运行 true ✅ 可用
签名打包 false MODULE_NOT_FOUND
签名打包 true MODULE_NOT_FOUND

分水岭是打包,不是 disclaim 这修正了文档"最可能的解释",也意味着 #107 的 macOS TCC 责任隔离不需要动

另外确认:在 profile 层把 hmrdisabled: true 关掉没有用——那只会让 runProfile 换个 id 再创建一个,照样抛错。

修复

watchUserPatches 对 HMR 的全部要求就是 registerConfig(filename, refresh)——一个文件监听加一个回调,不需要 internals。本 PR 新增一个桌面端自有的 hmr 服务,只提供这个子集,且仅在 internals 不可达时生效:

  • 能拿到 internals 的宿主(开发运行、Windows/Linux 的打包 Node)保持原样,完整 HMR 与模块级热替换不受影响;
  • 拿不到的宿主(签名打包的 macOS)由它顶上,boot 得以走完,用户 patch 仍然实时生效。

变更点:packages/dsh-desktop-hmr-fallback/(新包)、build/dsh-desktop.patch.yml(插入该行)、package.json(file: 依赖)。

验证

签名的 --dir 构建上验证(文档建议 4 要求的正式打包态,非 dev build):

  • 连续两次冷启动都到达 DSH entry loadedgrep -c 'expose-internals'0;同一构建去掉本改动则两次都失败;
  • Harness endpoint 持续返回 200
  • 修改 profile 的 cordis.patch.yml 后 Harness 继续正常服务,监听未打挂;
  • npm run typecheck 通过;npm test 36 文件 245 测试全绿(新增 4 条)。

#145 的关系

不重叠。#145 修的是 buildPnpmEnvironment 删掉 ELECTRON_RUN_AS_NODE 导致市场安装报 "pnpm completed, but dshmarket was not found"(issue #140);本 PR 修的是 Harness 启动本身。两者都只在 macOS 上出现,但根因和代码路径都不同。

遗留

  • 文档建议 3(推动 Harness 官方提供 config-only watcher / graceful fallback)仍值得推进:本 PR 是宿主侧的兜底,上游若解耦,这个包可以删掉。
  • 文档建议 4 的签名构建 smoke test 尚未进 CI,建议单开。

Every signed macOS launch failed profile boot at the same step:

    DSH entry failed: failed to apply loader entry (@deepseek-ai/cordis-plugin-hmr):
    --expose-internals is required for HMR service

Harness creates the full Cordis HMR service after boot whenever no `hmr`
service exists, and that service throws on construction without
`ctx.loader.internal`, taking the rest of boot with it — user patch-layer
watching included. A packaged Electron app cannot provide those internals:
the flag reaches the utility process's execArgv without reaching Node's
option parser, and the `node-addon-require-builtin` path the loader tries
next is built for plain Node and reports `Unsupported/no-realm` under
Electron.

A minimal Electron repro over the four combinations puts the boundary at
packaging, not at the TCC disclaim this branch was suspected of:

    | build     | disclaim | internal loader        |
    | dev run   | false    | available              |
    | dev run   | true     | available              |
    | signed    | false    | MODULE_NOT_FOUND       |
    | signed    | true     | MODULE_NOT_FOUND       |

So the macOS TCC isolation stays as it is. What profile boot actually asks
of HMR is a file watcher and a callback — `watchUserPatches` only calls
`registerConfig` — and that needs no internals. This adds a desktop-owned
service supplying exactly that subset, and only where the internals are out
of reach: a host that has them keeps the real service, module-level hot
replacement included, so development runs and the bundled-Node platforms are
unchanged.

Verified on a signed --dir build: two consecutive cold starts both reach
`DSH entry loaded` with the endpoint answering 200 and no HMR error in the
log, where the same build without this change failed both. Editing the
profile's patch file leaves Harness serving.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant