Root cause shared with #2647: a broken 1.38.3 tarball shipped without
sdk/dist/. The pre-#2441-decouple installer reacted by running
spawnSync('npm.cmd', ['install'], { cwd: sdkDir }) inside the npx cache
on Windows, where the cache is read-only, producing the misleading
"Failed to npm install in sdk/" error.
Defensive changes here (user-facing behavior only; packaging fix lives
in the sibling PR for #2647):
- Classify the install context (classifySdkInstall): detect npx cache
paths, node_modules-based installs, and dev clones via path heuristics
plus a side-effect-free write probe. Exported for test.
- Rewrite the dist-missing error to branch on context:
tarball + npxCache -> "don't touch npx cache; npm i -g ...@latest"
tarball (other) -> upgrade path + clone-build escape hatch
dev-clone -> keep existing cd sdk && npm install && npm run build
- Preserve the invariant that the installer never shells out to
npm install itself — users always drive that.
- Add tests/bug-2649-sdk-fail-fast.test.cjs covering the classifier and
both failure messages, with spawnSync/execSync interceptors that
assert no nested npm install is attempted.
Cross-ref: #2647 (packaging).
Fixes#2649
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>