Skip to content

fix(mac): prefer the node beside the CLI over a version manager's default - #1266

Merged
avs-io merged 1 commit into
getagentseal:mainfrom
TheCrazyAnt:pr/node-beside-cli
Sep 7, 2026
Merged

fix(mac): prefer the node beside the CLI over a version manager's default#1266
avs-io merged 1 commit into
getagentseal:mainfrom
TheCrazyAnt:pr/node-beside-cli

Conversation

@TheCrazyAnt

Copy link
Copy Markdown
Contributor

Summary

On a machine where a Node version manager's default version is older than the 22.13 the CLI requires, the menubar app renders no data: every CLI spawn exits 1 with codeburn requires Node.js >= 22.13.0 (current: v20.20.2), and the popover shows "Could not load Today" with nothing pointing at Node.

CodeburnCLI.makeProcess runs the CLI through /usr/bin/env and builds PATH by taking the inherited value and appending additionalPathEntries plus userNodePaths. The published launcher begins #!/usr/bin/env node, so whichever node comes first on that PATH wins — and on an nvm machine the first one is the default alias, which is easily an older major than the one the CLI was installed under. The CLI itself lives at ~/.nvm/versions/node/v24.20.0/bin/codeburn (resolved and cached by installedArgv()), right next to a v24 node that would run it fine; the app just never puts that directory ahead of the inherited entries.

augmentedPath now takes the resolved CLI path and, when an executable node sits in the same directory, moves that directory to the front of PATH (promoting it if it was already present, so nothing is duplicated and nothing is dropped). The interpreter beside the CLI is by construction the one that installed it and satisfies its engine range. When the CLI resolves through PATH (bare codeburn) or has no sibling interpreter, the inherited order is left exactly as before — so #1263's Nix layout (CLI in ~/.local/bin, node in a profile directory) is untouched, and every existing userNodePaths consumer keeps its precedence.

makeProcess also resolved the CLI twice — once for PATH, once for argv. It now resolves once and passes the same value to both, so they cannot disagree about which install a launch is talking about.

Change evidence

Change Why Receipt
Sibling-node directory leads PATH The engine gate fails on a version manager's default, while a compatible interpreter sits next to the CLI. swift test --filter CodeburnCLIPathTests: 7 tests in 1 suite passed. New title: "interpreter beside the CLI wins over an older node on PATH". Fixture: two node stubs under …/node/v24/bin and …/node/v20/bin, inherited PATH v20:/usr/bin:/bin, resolvedCLI pointing into v24/bin. Asserts v24/bin is first, v20/bin still present and later.
Promote, don't duplicate A CLI directory already on PATH must move, not appear twice. Same run, title "a CLI directory already on PATH is promoted, not duplicated": inherited /usr/bin:<bin>:/bin with resolvedCLI in <bin><bin> first, exactly one occurrence.
Inherited order untouched when there is nothing to prefer Reordering PATH on a guess would change what every other lookup resolves to; #1263's Nix case (no node beside the CLI) must not move. Same run, title "PATH order is untouched when there is no sibling interpreter": for both a bare codeburn and an absolute CLI with no sibling node, PATH begins /usr/bin:/bin and the CLI's directory is not inserted.
Regression proof that the new tests bind to the defect Confirm they fail without the fix rather than restating the implementation. Removed only the reordering block from augmentedPath (signature kept so the tests still compile) and re-ran the filter: the two positive tests failed with the reported shape — entries.first → ".../node/v20/bin" where v24/bin was expected, and entries.first → "/usr/bin" where the CLI's directory was expected — while "PATH order is untouched…" and all four pre-existing tests (mise ×2, Nix ×2) still passed. Restored the block; git diff against the staged fix is empty.
resolvedCLI injection parameter The production value comes from baseArgv(), which reads the persisted CLI path and probes the filesystem; tests pass a fixture path instead. Default nil keeps every existing caller unchanged. Covered by the three tests above; makeProcess passes argv.first.
Full suite, branch vs main CONTRIBUTING asks for both and zero new failures. swift test from mac/ on main (4a9d885): 424 tests in 53 suites passed, 0 failed. On this branch: 427 tests in 53 suites passed, 0 failed. Difference is exactly the 3 new tests.
End-to-end on the affected machine A unit test cannot show the shipped bundle picks the right interpreter under a GUI-inherited environment. Before: the installed app's every refresh failed with the >= 22.13.0 (current: v20.20.2) error above. After rebuilding and reinstalling with this change, the status item rendered today's spend within one refresh and the figure matched codeburn status --format menubar-json --period today run from a shell. Reproduced the spawn directly: PATH="$HOME/.nvm/versions/node/v24.20.0/bin:$PATH" ~/.nvm/versions/node/v24.20.0/bin/codeburn --version0.9.23…, rc 0; the same command without the prefix on that machine → rc 1 with the engine error.

Environment

Observed and fixed on macOS 26 (Darwin 25.6), Apple Silicon.

Fact Value
~/.nvm/alias/default 20.20.2
Persisted CLI path (codeburn-cli-path.v1) /Users/<user>/.nvm/versions/node/v24.20.0/bin/codeburn
node beside it v24.20.0
/opt/homebrew/bin/node v24.20.0 (present, but after the nvm default on the inherited PATH)
Spawn error before the fix codeburn requires Node.js >= 22.13.0 (current: v20.20.2) / Upgrade at https://nodejs.org/

Notes

The change is confined to augmentedPath and makeProcess in CodeburnCLI.swift. It adds no dependency, alters no public interface, and leaves isSafe, baseArgv, installedArgv, userNodePaths (including #1263's Nix entries) and the CODEBURN_BIN override untouched. The only PATH entries it can move are ones that were already on PATH or already curated by the app.

This is complementary to #1263: that change teaches the app where a Node can be; this one settles which one wins when several are present. The stronger follow-up mentioned there — persisting the interpreter path and spawning [node, cli.js] directly — would subsume both; this PR keeps the /usr/bin/env design.

The description above is what this branch does and what was measured; the same defect on Windows (the npm .cmd shim also resolves node via PATH) is out of scope here.

🤖 Generated with Claude Code

…ault

The app built PATH by appending its curated directories after the
inherited value, so whichever `node` came first won -- on an nvm machine
that is the default alias, which can be an older major than the one the
CLI was installed under. Every spawn then died on the engine gate
("codeburn requires Node.js >= 22.13.0 (current: v20.20.2)") and the
popover showed "Could not load Today" with nothing pointing at Node.

augmentedPath now takes the resolved CLI path and, when an executable
node sits in the same directory, moves that directory to the front of
PATH -- promoting it if already present, dropping nothing. That
interpreter is the one that installed the CLI and satisfies its engine
range. A bare `codeburn` or a CLI with no sibling node leaves the
inherited order untouched, so getagentseal#1263's Nix layout is unaffected.

makeProcess resolved the CLI twice, once for PATH and once for argv; it
now resolves once and passes the same value to both.

Three tests cover the reorder, the promote-not-duplicate case, and the
untouched case; removing only the reorder block makes the first two fail
with the reported shape while every pre-existing test still passes.
@avs-io
avs-io merged commit 83f0752 into getagentseal:main Sep 7, 2026
8 checks passed
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