fix(web): launch npm and the managed backend through node, not .cmd shims - #2033
Conversation
|
Merged One pre-existing failure to flag, not from this diff: Everything else is green on the merged head: Generated by Claude Code |
Size Report
npm unpacked components
Startup median (7 runs, lower is better):
Top changed chunks: no changes in the largest emitted chunks. Top changed packed files
|
|
P1 — preserve the released web status contract. The JS-entry spawn design and focused regressions otherwise look sound. Current Coverage (2) is a confirmed base-branch help-card budget failure, iOS smoke is pending, and merge-readiness still needs exact-head Windows evidence for |
Review on #2033: `AgentBrowserToolStatus`, `web setup --json`, and `web doctor --json` have carried `binaryPath` since #833 and through v0.20.10, and the Windows spawn fix does not need that removed. `binaryPath` is restored to the published shape, naming npm's console shim as before; `entryScript` and `packageDir` are additive. Execution is unchanged — `process.execPath` plus the JS entry — so the shim is now informational and is never spawned. `web setup`'s text output prints it again too. The web fixture now writes the `.bin` shim npm would link, so the spawn regressions prove the shim is present and still not used, rather than merely absent. New `src/__tests__/cli-web.test.ts` pins the published JSON for both `web setup --json` and `web doctor --json`; both go red when `binaryPath` leaves the status. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LMS3BidXb3F4HSr26vvQmG
|
P1 addressed in
CLI-level JSON regressions in the new
Both go red when The web fixture now also writes the On the two open items you named:
Generated by Claude Code |
|
Re-reviewed exact head |
93bc27a to
b548587
Compare
|
Thanks for the re-review. Force-pushed a tidied branch — history only, no change to the reviewed diff, plus the budget fix you approved.
Still outstanding and still not something I can produce: the Windows evidence. There is no Windows host in this environment, so Generated by Claude Code |
|
Re-reviewed exact head |
|
On the approval provenance — you are right that it is not verifiable from the PR record, so here it is explicitly. The approval came out-of-band, not on GitHub: @thymikee, driving this change in the Claude Code session that produced the PR, said "you can increase the budget to 9003, I allow" after I reported the 9003/ Two ways to settle it, whichever you prefer:
Say which and I will do it. Nothing about the web fix changes either way, and the Windows evidence gap is unaffected and still open. Generated by Claude Code |
|
Coordinator update for exact head |
b548587 to
e098e34
Compare
|
Simplified per review — the generic package-launch subsystem is gone. Force-pushed to
npm is untouched on POSIX. The package-manifest Tests are interface-level now: a four-case npm-resolution matrix (POSIX bare One structural note: rather than let Preserved properties, all still covered: no shell, no Generated by Claude Code |
…hims On Windows every `--platform web` command failed with `spawn EINVAL`: the managed backend resolved to `node_modules/.bin/agent-browser.cmd` and was spawned with `shell: false`, which Node refuses for `.bat`/`.cmd` since the CVE-2024-27980 fix. `web setup` failed earlier still — a bare `npm` is not spawnable on Windows, where npm ships as `npm.cmd`. `runManagedAgentBrowser` is now the only path that executes the backend. Entry resolution, the Node runtime, the managed environment, and the spawn all live behind it, so setup, doctor, and the provider cannot reintroduce the shim. The entry comes from the installed package's declared `bin` rather than a hard-coded path, which is the part of this worth being precise about. npm is untouched on macOS and Linux, which were never broken: setup still spawns `npm` from PATH. Only Windows resolves npm's own `npm-cli.js` — from an `npm_execpath` that really is npm's launcher, else the copy bundled beside `node` — and fails with the existing actionable TOOL_MISSING when neither is there. Setup also pins `--no-global` so an ambient `npm_config_global` cannot redirect the install out of the managed prefix. The published status shape is unchanged: `binaryPath` still names npm's console shim, now informational rather than the spawned command, and `entryScript` plus `packageDir` are additive. Closes #2022 Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LMS3BidXb3F4HSr26vvQmG
|
Re-checked exact head |
e098e34 to
99baeb6
Compare
|
Rebased onto How the conflict resolved. The web fix commit replayed cleanly with no conflict. The only conflict was in the second commit, the help-card budget — and it conflicted because #2021 had already raised that budget on Nothing was broadened: the diff against the new base is the same web change, 12 files, +659/−156, production +207/−88. Re-validated on the new base, after a fresh The PR body is updated to match this head — it still described the generic Residual risk is unchanged and as you accepted it: the Windows path is contract-tested, not exercised on a live Windows host. Generated by Claude Code |
|
Summary
On Windows no
--platform webcommand worked, andagent-device web setupcould not install themanaged backend at all. Both failures came from spawning Windows console shims with
shell: false:web setupran a barenpm. On Windows npm ships asnpm.cmd/npm.ps1plus an extensionlessshell script Windows cannot execute, so the spawn failed with
TOOL_MISSING: npm not found in PATHno matter how npm was installed.web doctor— spawned<install>/package/node_modules/.bin/agent-browser.cmd. Since the CVE-2024-27980 fix(Node 18.20.2 / 20.12.2 / 21.7.3 and later)
child_process.spawnrefuses a.bat/.cmdcommand without
shell: true, so this failed withspawn EINVALon every supported Node.runManagedAgentBrowser(args, options)is now the only path that executes the backend. Entryresolution, the Node runtime, the managed environment, and the spawn all live behind it; setup,
doctor, and the provider go through it, so no caller is in a position to reintroduce the shim. The
entry comes from the installed package's declared
bin(bin/agent-browser.jsin 0.27.1) ratherthan a hard-coded path. No shell, no
cmd.exequoting, noDEP0190.npm is untouched on macOS and Linux, which were never broken: setup still spawns
npmfromPATH. Only Windows resolves npm's own
npm-cli.js, from exactly two places — annpm_execpaththatreally is npm's launcher (pnpm and yarn advertise theirs through the same variable), else the copy
bundled beside
node— and otherwise fails with the existing actionableTOOL_MISSING. Setup alsopins
--no-global, so an ambientnpm_config_globalcannot redirect the install out of the managedprefix, and an install that exits 0 without leaving a runnable entry is reported as that rather than
as the not-installed-yet hint telling you to run the command you are already running.
The published status contract is unchanged.
binaryPathstays inAgentBrowserToolStatus,web setup --json,web doctor --json, andweb setup's text output, still naming npm's consoleshim; it is now informational.
entryScriptandpackageDirare additive. The one behaviour changeis that a managed install counts as present only when the backend package is present, not when a
node_modules/.binshim is — npm always writes both, so existing installs are unaffected.One nuance: the backend now runs under the same Node runtime as agent-device rather than whichever
nodethe shim's shebang resolved fromPATH. That is the deterministic choice (setup alreadyrequires Node 24+), but it does pin the pair together.
Closes #2022
Validation
pnpm typecheck,pnpm lint,pnpm format:checkandpnpm check:affected --runare fullygreen at
99baeb6, after a freshpnpm install --frozen-lockfile && pnpm buildon the rebasedbase (#2021 changed a workspace manifest).
Regression coverage, written as interface tests rather than layered unit tests:
setupManagedAgentBrowser: POSIX spawns barenpm;Windows uses a validated
npm_execpath; Windows falls back to the npm bundled besidenode;Windows with neither fails
TOOL_MISSINGand spawns nothing.managed agent-browser runs its JS entry with the current Node runtime, not the shim— thefixture writes the
.binshim npm links, so this proves the shim is present and still not thespawned command, from a state dir whose path contains spaces.
src/__tests__/cli-web.test.tspins the published JSON ofweb setup --jsonandweb doctor --json(binaryPath,entryScript,packageDir,installDir,installed, andsocketDirstill absent). Both go red ifbinaryPathleaves the status.assert logical agent-browser arguments; the spawn shape is owned and asserted by the tool module.
Live evidence on Linux (Node 22 host, state dir path containing a space), running the shipped
resolution and spawn path against the real registry package:
Accepted residual risk: there is no Windows host in this environment, so the Windows path is
contract-tested rather than exercised live. The claim rests on the mechanism — no
.cmdis spawnedanywhere on the path — plus the reporter's own confirmation in #2022 that the backend answers when
it is not launched through its shim. Web smoke coverage
(
test/integration/smoke-web-platform.test.ts) is CI/host-gated and did not run here.Scope
12 files, +659/−156; production is +207/−88 across four files. The change stays inside the web
command family.
An earlier revision of this PR factored npm and JS-entry resolution into a generic
src/utils/node-package-cli.ts. That module had exactly one production consumer, which is ahypothetical seam rather than earned reuse, so it and its test are gone (−405 lines); the package
manifest
binlookup that is worth keeping is now private to the web module. Installation (npminvocation and manifest write) sits in
agent-browser-install.tssoagent-browser-tool.tsstaysinside the repo's 300-line target; it is web-private, takes plain parameters, and exports nothing
generic.
A second commit pinning the workflow help-card budget was dropped during the rebase: #2021 raised
that budget to
< 9100onmain, which resolves the base-branch Coverage failure this PR waspreviously blocked behind. Nothing in this PR touches it now.
Follow-up worth its own change:
agent-device react-devtoolsandagent-device agent-cdpstillrun
runCmdStreaming('npm', ['exec', …]), the same bare-npmspawn that fails on Windows. They area different command family with their own test surface, so they are not in this diff.
Not in scope, per triage on the issue: the stale web-device lease that survives an orphaned
daemon (
DEVICE_IN_USEnaming a sessionsession listdoes not report) is filed separately as#2031. Also left alone:
spawn's synchronous throws (which is why the reporter sawUNKNOWN: spawn EINVALrather than a classified error) bypassrunSpawnedCommand's error mapping.No path in this diff can produce one any more, and repairing that boundary means auditing every
producer of a spawn error.