Skip to content

fix(windows): run the better-sqlite3 boot-install via node, not npm.cmd+shell (#861 follow-up)#863

Open
ken-jo wants to merge 1 commit into
mksglu:nextfrom
ken-jo:fix/win-ensure-deps-npm-cwd-861
Open

fix(windows): run the better-sqlite3 boot-install via node, not npm.cmd+shell (#861 follow-up)#863
ken-jo wants to merge 1 commit into
mksglu:nextfrom
ken-jo:fix/win-ensure-deps-npm-cwd-861

Conversation

@ken-jo

@ken-jo ken-jo commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

What / Why / How

Follow-up to #861 (e918eac). That fix stopped the Windows boot-install EPERM for the optional fetch deps in start.mjs by running npm's own CLI through node (shell:false honors cwd) instead of npm.cmd + shell:true (which DROPS cwd on Windows → npm resolves node_modules from the inherited cwd C:\Windowsmkdir C:\Windows\node_modules → EPERM). The identical npm.cmd cwd-drop still affected the native-dep install in hooks/ensure-deps.mjs — the reporter explicitly listed better-sqlite3 among the EPERMs, and that path was left unfixed.

With this PR applied, the better-sqlite3 boot-install uses the same cwd-honoring path, so it can no longer EPERM at C:\Windows\node_modules.

This PR absorbs e918eac's approach for the native-dep install:

  • hooks/ensure-deps.mjs — resolve npm-cli.js beside process.execPath and run it via execFileSync(process.execPath, [npmCliJs, "install", …], { shell:false }) (honors cwd) + windowsHide:true; fall back to the npm.cmd shim only when npm-cli.js isn't beside node (so no host regresses); surface failures to stderr (was silently swallowed behind stdio:"pipe" + an empty catch).
  • tests/hooks/ensure-deps.test.ts — pin the install branch to the node-CLI / shell:false path + the failure surfacing (source guard, matching this file's existing Windows install can leave better-sqlite3 native binding missing in v1.0.107 #408/Codex CLI 0.131.0: MCP startup_timeout_sec=30s default trips when codex prewarm + cold-start stack #634 patterns; the bug is Windows-only and doesn't reproduce on Linux CI).
  • preserved: the ensureNativeCompat ABI-rebuild paths are untouched (they use npm.cmd too, but don't mkdir node_modules → no EPERM; left for a separate change).

Affected platforms

Agent / CLI scope:

  • All platforms — hooks/ensure-deps.mjs runs on every adapter that boots the MCP server via start.mjs. No MCP/adapter behavior change; only the native-dep bootstrap install.

OS scope:

  • Windows — the affected OS (npm.cmd drops cwd). Reachable on Windows + Node when better-sqlite3 is missing (codex/marketplace clones, broken installs) — the same condition that triggers the start.mjs turndown installs e918eac already fixed.
  • Linux / macOS — unaffected: ensureDeps() returns early on Bun, and POSIX already runs npm with shell:false (cwd honored). The node-CLI path is equivalent there.

Scope notes:

  • Only the "package not installed at all" install branch is changed. The probe and ABI-rebuild logic are untouched.

Root Cause

Validation

Check Result
npm run build + npm run typecheck passed (0 errors)
vitest run tests/hooks/ensure-deps.test.ts 26 passed (existing #206/#148/#408/#331 + 2 new #861-follow-up guards)
Windows (ssh my-window, Node v24.13) node <npm-cli.js> install is-number launched from cwd=C:\Windows → installed into <target>\node_modules (installedInTarget:true, eperm:false); npm-cli.js found beside node

Behavioral evidence:

  • BEFORE: execSync("npm.cmd install better-sqlite3", { shell:true }) — same cwd-drop class as the (now-fixed) turndown installs → EPERM at C:\Windows\node_modules when better-sqlite3 is missing.
  • AFTER: node <npm-cli.js> install … (shell:false) installs into the plugin dir regardless of cwd — verified on real Windows from cwd=C:\Windows.

Cross-agent / cross-OS risk

Minimal. One install call site in hooks/ensure-deps.mjs; no new dependency; no MCP/adapter/hook-routing change. The node-CLI path is the same mechanism e918eac already shipped and validated for start.mjs. POSIX behavior is unchanged (already shell:false).

Checklist

  • Tests added (source guard for the node-CLI/shell:false install + failure surfacing), integrated into the existing tests/hooks/ensure-deps.test.ts
  • npm run build + npm run typecheck pass
  • Generated bundles regenerate via CI (not committed)
  • No new runtime dependency
  • One PR, single logical fix (absorbs e918eac for the native-dep install)
  • 3OS validation — Windows verified (mechanism); Linux/macOS unaffected (Bun early-return / POSIX shell:false)
  • Targets next

…md+shell (mksglu#861 follow-up)

mksglu#861/e918eac fixed start.mjs's background install of the optional fetch deps
(turndown/turndown-plugin-gfm/@mixmark-io/domino) by running npm's own CLI
through node with shell:false (which honors `cwd`) instead of `npm.cmd` +
shell:true (which DROPS `cwd` on Windows → npm targets the inherited cwd
C:\Windows → `mkdir C:\Windows\node_modules` → EPERM every boot). The IDENTICAL
npm.cmd cwd-drop still affected the native-dep install in hooks/ensure-deps.mjs
— the reporter listed better-sqlite3 among the EPERMs, and that path was left
unfixed. It runs on Windows + Node whenever better-sqlite3 is missing (codex/
marketplace clones, broken installs) — the same condition that triggers the
turndown installs.

This change absorbs e918eac's approach for the native-dep install:
- hooks/ensure-deps.mjs: resolve npm-cli.js beside process.execPath and run it
  via execFileSync(process.execPath, [npmCliJs, "install", ...], { shell:false })
  (honors cwd) + windowsHide; fall back to the npm.cmd shim only when npm-cli.js
  isn't beside node (no host regresses); surface failures to stderr (was
  silently swallowed behind stdio:"pipe" + an empty catch).
- tests/hooks/ensure-deps.test.ts: pin the install branch to the node-CLI /
  shell:false path and the failure surfacing.

Verified on Windows (ssh my-window): `node <npm-cli.js> install <pkg>` launched
from cwd=C:\Windows installs into the target's node_modules (cwd honored, no
EPERM). ensure-deps suite 26 pass; npm run build + typecheck pass. The rebuild
paths in ensureNativeCompat use the same execSync(npm.cmd) but don't mkdir
node_modules (no EPERM) and are left for a separate change. Bundles via CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@NgoQuocViet2001

Copy link
Copy Markdown
Contributor

@ken-jo reviewed the Windows install path here.

The change matches the #861 invariant: the primary native-dep install now runs node <npm-cli.js> with shell:false, so cwd is honored instead of falling through the npm.cmd + shell path.

I also checked it locally on Windows:

  • npx vitest run tests/hooks/ensure-deps.test.ts -> 26 passed
  • smoke from parent cwd C:\Windows with cwd set to a temp target -> package installed into the temp target node_modules; nothing was written under C:\Windows\node_modules

Looks good to me.

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.

2 participants