Skip to content

fix(setup): register a stable launcher when run through bunx - #1731

Merged
morluto merged 2 commits into
morluto:mainfrom
xeropresence:fix/setup-bunx-registration
Oct 11, 2026
Merged

morluto merged 2 commits into
morluto:mainfrom
xeropresence:fix/setup-bunx-registration

Conversation

@xeropresence

Copy link
Copy Markdown
Contributor

Summary

bunx rea-agents setup is Bun's version of the README quick start, npx rea-agents setup. When run that way, setup registered the bin inside Bun's temporary bunx cache. That registration stops working once the cache is cleared. Setup now detects a bunx (or bun x) launch and registers the pinned bunx rea-agents@<version> mcp launcher, handled the same way as the existing npx launcher. Doctor, update maintenance, and uninstall treat the bunx launcher as REA-owned. Setup's printed next steps now say bunx rea-agents when setup was started through bunx.

The fix also covers a Windows-only cause that affected npx as well. The CLI's environment snapshot upper-cases variable names on Windows, so the old lookup of environment.npm_command never matched there. As a result, setup run through npx on Windows also registered the cache path, not cmd.exe /d /c npx -y rea-agents@<version> mcp.

No issue is linked. The problem was found by running bunx rea-agents@6.4.0 setup on Windows.

Problem and expected behavior

Trigger (observed on Windows x64, Bun 1.4.2, published 6.4.0):

bunx rea-agents@6.4.0 setup --client omp --dry-run
… Command: "C:\Users\<user>\.bun\bin\bun.exe" "C:\Users\<user>\AppData\Local\Temp\bunx-2705681118-rea-agents@6.4.0\node_modules\rea-agents\scripts\rea.mjs" "mcp".

The broken invariant: a persistent registration must not depend on a package runner's temporary cache. Setup already does this for npx (setupRegistrationCommand → npxRegistrationCommand when npm_command === "exec").

Two causes, both confirmed by testing:

  1. bunx sets the same npm_command=exec variable as npx, plus npm_config_user_agent=bun/1.4.2 npm/? node/v26.3.0 win32 x64 and npm_lifecycle_event=bunx. A probe bin run via bunx --package file:<tgz>, and a --preload probe run through bunx rea-agents@6.4.0 and bun x rea-agents@6.4.0, all showed these values. Before this change REA could not tell bunx apart from npx. On POSIX it would have registered an npx launcher, which needs npm on the machine.
  2. On Windows, snapshotEnvironment upper-cases keys (NPM_COMMAND), so environment.npm_command was always undefined. Setup then fell back to the direct process.execPath + process.argv[1] registration, and under any package runner argv[1] is in that runner's cache. The same snapshot also meant runUpdateCommand's delete environment.npm_command did nothing on Windows.

Expected: setup run through bunx/bun x registers bunx rea-agents@<version> mcp (cmd.exe /d /c bunx rea-agents@<version> mcp on Windows). Setup run through npx registers the npx launcher on every platform. Direct installs keep their current registration.

Change and scope

  • ClientRegistrationIdentity.ts (application layer, which already owns the npx launcher identity):
    • PackageRunner = "npx" | "bunx".
    • invokingPackageRunner(environment, platform) reads npm_command and npm_config_user_agent. On Windows the lookup ignores case. exec with a bun/ user agent → bunx; exec with anything else → npx (same as before); otherwise none. bun run reports npm_command=run-script (checked), so it is not detected as a runner.
    • packageRunnerRegistrationCommand(runner, platform) builds npx -y <spec> mcp or bunx <spec> mcp, wrapped in cmd.exe /d /c on Windows. npxRegistrationCommand now calls it, so the npx launcher stays one definition.
    • isOwnedClientRegistrationCommand also accepts bunx rea-agents[@<exact version>] mcp, with or without the cmd /c / cmd.exe /d /c wrapper. Other bunx shapes, other packages, and extra arguments are still not owned.
    • withoutPackageRunnerIdentity takes over the update subprocess's removal of npm_command, ignoring case on Windows.
  • SetupHost.setupRegistrationCommand(platform, packageRunner) takes the detected runner instead of a boolean.
  • ClientRegistrationStatus reports either runner's exact launcher as aligned.
  • interactiveSetup prints npx rea-agents or bunx rea-agents, matching the runner, in its next-step hints.
  • Docs: docs/installation.md (start setup, Windows registrations, update/remove, MCP Registry note) and the README update section now mention bunx.

Why bunx rea-agents@<version> mcp and not Bun's absolute executable (<bun.exe> x rea-agents@<version> mcp):

  • It follows the existing npx convention: the runner is resolved by name on PATH, the version is pinned, and on Windows it goes through cmd.exe /d /c. That keeps one ownership, alignment, and uninstall rule for both runners.
  • The cmd.exe wrapper also works when bunx is a .cmd shim (Bun installed through npm or another shim manager). A standard Bun install ships a real bunx.exe, and the smoke run below used it.
  • process.execPath can point to a versioned install path, such as a Homebrew Cellar directory, that disappears when Bun is upgraded. Writing it into the registration would swap one unstable path for another.
  • The command contains no --bun flag, so bunx uses the same runtime for the registered server that it used for setup.

Not included: rea update still updates only npm global installs, and its remediation text for other installs still mentions npx rea-agents@latest. Detecting a runner for rea update, the npm-only install.sh, and the MCP Registry runtimeHint (which stays npx) are unchanged.

Contract and boundary impact

  • Semantic owner and earliest changed stage: src/application/ClientRegistrationIdentity.ts, when setup builds the registration command.
  • CLI and MCP/tool-catalog contract: no new CLI flags or MCP tools. Setup plans and writes a different registration command when started through bunx (all platforms) or through npx on Windows. Doctor reports those registrations as aligned.
  • Provider, bridge, target-format, or platform compatibility: Windows clients get the cmd.exe /d /c wrapper, as the npx launcher already does. POSIX clients get bunx directly.
  • Evidence, artifact, provenance, or reconstruction contract: none.
  • Process execution, authorization, cleanup, or containment impact: the update child process now has npm_command removed on Windows too, as the code already intended.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: none. No generated files changed.

Evidence and regression coverage

  • Tests added or updated:
    • tests/boundary/cli/packageRunnerRegistration.test.ts (new): for npx and bunx environments, runs the real built CLI setup --client omp --no-skill --yes --json in an isolated HOME. It checks that the written command equals the runner launcher for the host platform, that doctor --client omp --json reports aligned, and that a repeated setup --dry-run plans nothing. On Windows this goes through the real CLI environment snapshot, so the upper-cased-variable bug is covered too.
    • src/setupEnvironment.test.ts: a Windows-canonicalized snapshot (NPM_COMMAND, NPM_CONFIG_USER_AGENT) resolves to the right runner and setup registration, and withoutPackageRunnerIdentity removes the upper-cased key. This test fails on the base revision, where the lookup misses the upper-cased key.
    • tests/boundary/filesystem/windowsNpxRegistration.test.ts: the Codex TOML align/idempotency/uninstall test now runs for both runners. Invalid bunx command shapes stay retained on uninstall. The native .cmd shim test now starts both an npx.cmd and a bunx.cmd registration as a stdio MCP server through the MCP client SDK (Windows only).
    • tests/boundary/filesystem/clientConfigurationDialects.test.ts: bunx ownership for prior exact versions, and rejection of foreign bunx packages.
    • tests/boundary/cli/cliSetup.test.ts: the completion hint prints bunx rea-agents for a bunx launch.
    • Mechanical signature updates in Setup.configuration.test.ts and Setup.fixture.ts.
  • Base reproduction or other evidence: the published 6.4.0 dry run above. Environment probes of bunx, bun x, and bun run launches as described in "Problem".
  • User-visible CLI/MCP output: setup plan through bunx after the change (Windows):
    Add the REA MCP registration for OMP; preserve unrelated configuration. Command: "cmd.exe" "/d" "/c" "bunx" "rea-agents@6.4.0" "mcp".
  • Remaining proof gaps: no real npx run. Node.js and npm are not installed on this machine, so the Windows npx fix is covered by the snapshot unit test, the boundary test's simulated npx environment, and the npx.cmd shim test. bunx on macOS/Linux was not run locally. POSIX behavior is covered by CI running the same boundary test.

Validation performed

All local validation ran under Bun 1.4.2 on Windows x64 without Node.js or npm installed. CI covers Node.

  • bun --bun run compile and bun --bun run artifacts:skills — passed.
  • bun --bun run typecheck — passed.
  • bun --bun run lint — exit 0. No new warnings. The isOwnedClientRegistrationCommand complexity warning present on the base is gone, and the existing registrationAligned complexity warning dropped from 32 to 31.
  • bun --bun node_modules/.bin/oxfmt --check <changed files> — all files formatted.
  • bun --bun run test:local -- src/setupEnvironment.test.ts src/application/Setup.configuration.test.ts src/application/Update.test.ts — 45 passed.
  • bun --bun node_modules/vitest/vitest.mjs run tests/boundary/cli/packageRunnerRegistration.test.ts tests/boundary/filesystem/windowsNpxRegistration.test.ts tests/process-global/updateMaintenance.test.ts — all passed (2 + 7 + 17), including the new npx.cmd and bunx.cmd stdio MCP launches.
  • bun --bun node_modules/vitest/vitest.mjs run tests/boundary/cli/cliSetup.test.ts tests/boundary/filesystem/clientConfigurationDialects.test.ts — the new and changed cases pass (three completion-hint cases; the bunx ownership assertions run before the existing POSIX-path assertion). 28 other tests in these files fail the same way on the unmodified base under Bun on Windows (POSIX path expectations and the interactive journey harness), so these failures predate this change.
  • bun --bun node_modules/vitest/vitest.mjs run tests/boundary/filesystem/clientRegistrationStatus.test.ts tests/boundary/filesystem/setupConfig.test.ts tests/boundary/filesystem/ompClientConfiguration.test.ts tests/boundary/filesystem/hermesClientConfiguration.test.ts — 13 failures, the same 13 as on the unmodified base under Bun on Windows. bun --bun run test:local -- src/application/Setup.initialSnapshot.test.ts src/application/Setup.planning.test.ts src/application/Setup.recovery.test.ts — 2 failures in Setup.planning.test.ts, also the same on the base.
  • bun --bun node_modules/vitest/vitest.mjs run tests/conformance/readmeConsistency.test.ts — 2 passed.
  • bun --bun run docs:check:built (after artifacts:product-catalog, artifacts:error-schema, artifacts:mcp-catalog, artifacts:managed-evidence) — passed. (docs:check itself fails before running any checks because check-dependency-install.mjs requires npm's node_modules/.package-lock.json.)
  • Real bunx smoke run, using a throwaway root C:\rea-bunx-smoke-6e3f as cwd, with HOME, USERPROFILE, APPDATA, LOCALAPPDATA, and BUN_INSTALL all pointed inside it. The patched package was packed with bun pm pack --ignore-scripts.
    • bunx --package file:<tgz> rea setup --client omp --yes --json → status: ready. home\.omp\agent\mcp.json contains {"type":"stdio","command":"cmd.exe","args":["/d","/c","bunx","rea-agents@6.4.0","mcp"]}, with no reference to the bunx temp cache.
    • bunx --package file:<tgz> rea doctor --client omp --json → identity.registrations: [{client: "omp", command: ["cmd.exe","/d","/c","bunx","rea-agents@6.4.0","mcp"], state: "aligned"}].
    • Started the registered command exactly as written (cmd.exe /d /c bunx rea-agents@6.4.0 mcp) and sent JSON-RPC over stdio. initialize returned serverInfo {name: "rea", version: "6.4.0"} with protocol 2025-06-18. tools/list returned 170 tools. The registered pin resolves the published 6.4.0 from the registry, since the packed build has the same version number.
    • bunx --package file:<tgz> rea uninstall --json → status: complete. The rea entry was removed from the OMP config.
    • No smoke processes were left running. The throwaway root and probe caches were deleted.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Registrations that earlier setups wrote into a runner's cache are not migrated automatically. Running setup again through the runner plans to replace them with the stable launcher, because the planned command differs from the existing one, and the replacement goes through the normal reviewed transaction (plan, approval, backup). I did not run this migration path as a separate smoke test.
  • Real Hopper/Ghidra, browser, or OS coverage: Windows x64 with a real bunx (bunx.exe) and a real MCP stdio launch. No provider is involved.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: the registration runs bunx through cmd.exe /d /c, with fixed arguments and a pinned exact version, the same as the npx launcher. Ownership matching rejects shell-joined or extra-argument variants, and tests cover that. The update subprocess now drops the runner marker on Windows as well.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

Setup run through bunx or bun x registered the bin inside bun's temporary
package cache. Detect bun's package-runner launch from npm_command=exec
with a bun user agent and register the pinned bunx launcher, wrapped in
cmd.exe on Windows like the npx launcher. Doctor, update maintenance and
uninstall recognize it as REA-owned, and setup's printed commands name
the runner that launched it.

Read the runner variables case-insensitively on Windows, where the CLI
environment snapshot upper-cases variable names and setup run through
npx also fell back to the cache path.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T11:29:55.491749Z 58d8969 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@morluto
morluto merged commit d412def into morluto:main Oct 11, 2026
2 checks passed
@morluto

morluto commented Oct 11, 2026

Copy link
Copy Markdown
Owner

This fixes a tricky setup edge across two package runners, @xeropresence: Bunx registrations now point at a pinned bunx command instead of its temporary cache, and Windows’ uppercased NPM_COMMAND no longer breaks the existing npx path. Thanks for verifying both on Windows.

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