fix(build): 修好 npm install 挂掉 (postinstall 只重建 better-sqlite3)

`npm install` 在 postinstall 阶段直接失败退出:

    node-hid 编译报错 -> node-addon-api/napi.h:2477 expected unqualified-id

根因不是 node-hid 坏了, 是重建范围错了:

1. postinstall 跑的是 `electron-builder install-app-deps`, 它重建**所有**原生模块;
2. 它给子进程传 `npm_config_build_from_source=false` —— 但这是**字符串** "false",
   npm 11 对未知的 npm_config_* 不做布尔转换;
3. pkg-prebuilds-verify 用 `if (process.env.npm_config_build_from_source)` 判断,
   字符串 "false" 在 JS 里是真值 -> 直接 exit 1 -> 强制源码编译;
4. node-hid 于是去编译它自带的 node-addon-api 3.2.1, 和现代 Node 头文件不兼容 -> 编译失败
   -> 整个 npm install 挂掉。

而 node-hid 根本不需要编译 —— 它自带 N-API 预编译产物, 任何 ABI 都能加载。

真正需要按 Electron ABI 重编的只有 better-sqlite3 (非 N-API, 绑死 V8/ABI,
Electron 32 -> node ABI 128); 其余 (@lydell/node-pty / node-hid /
@serialport/bindings-cpp / usb) 都是 N-API 预编译, 重编既没必要又有害。

改动:

- postinstall 换成 `node scripts/postinstall.js`:
  只重建 better-sqlite3, 装完**在 Electron 下真跑一次读写校验**, 失败就 exit 1
  —— 把"启动即崩"提前到 install 阶段暴露
- package.json 加 `allowScripts` 白名单 (npm 11 新机制, 目前只提醒不拦截,
  但迟早会拦; 顺便让 npm install 输出干净)
- AGENTS.md 坑 #3 改写成新规矩 + 完整根因链
- docs/windows-compat.md / docs/remote-terminal-forward-usb.md 里过时的
  "postinstall 走 install-app-deps" 表述一并纠正

验证:

- npm run build            ✅
- npm run typecheck        ✅ node + web
- npm run selftest         ✅ 64 通过 / 0 失败
- npm run selftest:sync    ✅ 全部通过
- npm run package:linux    ✅ 新 deb
- npm run selftest:package ✅ 4/4 (从 app.asar 真实 spawn pty)
This commit is contained in:
2026-10-21 09:32:32 +08:00
parent 4fc86edd4e
commit d854c95ee6
5 changed files with 186 additions and 4 deletions
+15 -1
View File
@@ -99,7 +99,21 @@ UDP discovery on **47800**, chat WS on **47900**, file HTTP on **47901**. All th
2. **Drag-drop file path.** Electron 32 removed `File.path`. Use `webUtils.getPathForFile(file)` from `electron` — exposed in `src/preload/index.ts` as `getPathForFile(file)`. Callers must read it via `window.api.getPathForFile(f)` BEFORE the await boundary.
3. **Native rebuild.** `postinstall` runs `electron-builder install-app-deps` to rebuild `better-sqlite3` against the bundled Electron ABI. After `npm i` or upgrading Electron, this must run. If you forget, sqlite open will throw on launch.
3. **Native rebuild —— 只重建 `better-sqlite3`, 别用 `electron-builder install-app-deps`.**
`better-sqlite3` 不是 N-API, 绑死 V8/ABI (Electron 32 → node ABI 128), 必须对着 Electron
头文件编译; `npm i` 或升级 Electron 后必须重编, 否则 sqlite 打开即崩。
`postinstall` 现在是 `node scripts/postinstall.js`: 只重建 better-sqlite3, 装完**在 Electron 下
真跑一次读写校验** (失败就 exit 1, 把"启动即崩"提前到 install 阶段暴露)。
**别改回 `electron-builder install-app-deps`** —— 它重建**所有**原生模块, 且给子进程塞
`npm_config_build_from_source=false`; npm 11 对未知 npm_config_* 保留原始字符串, 于是子进程里
它是字符串 `"false"` —— JS 里是真值 —— `pkg-prebuilds-verify` 直接 exit 1
(`node_modules/pkg-prebuilds/bin/verify.mjs:14`), 强制源码编译。node-hid 于是去编译它自带的
node-addon-api 3.2.1, 和现代 Node 头文件不兼容, 编译失败, 整个 `npm install` 挂掉 ——
而它本来根本不需要编译 (自带 N-API 预编译产物, 任何 ABI 都能加载)。
同理, node-pty 1.2 的二进制在**平台包** `@lydell/node-pty-<plat>-<arch>` 里, 主包没有
`prebuilds/` 目录 —— 按主包去找只会误报。
npm 11 还有个 `allowScripts` 机制 (根 package.json 的 `allowScripts` 字段): 没被显式列入的
安装脚本只会被"提醒"而非拦截, 但建议把信任的包列进白名单, 免得将来 npm 真的开始拦截。
4. **`mkdir` for Windows reserved names.** Output dir is `dist-Electron\` (escaped; `dist\electron` chokes on Windows). Don't rename without verifying the build still produces a valid path.