Repository navigation
fix(windows): probe fnm's real default alias dir for Node fallback - #11359
Conversation
Greptile SummaryThis PR fixes Windows fnm (Fast Node Manager) Node directory resolution in Orca's GUI process PATH. The original code used
Confidence Score: 5/5Safe to merge. The change is Windows-only in its behavioral surface — macOS and Linux paths are untouched — and the new Windows code is guarded behind both a platform check and a route-classification gate before any subprocess is spawned. The core fix (removing the erroneous /bin suffix on the Windows fnm directory and adding the correct FNM_DIR/APPDATA priority chain) is straightforward and well-validated by the test suite. Path injection is prevented by normalizeSingleWindowsPathEntry before any candidate enters PATH. The probe-before-prepend design avoids modifying the spawn environment unless node is confirmed reachable. The two observations flagged are quality notes — an observable startup latency on every agent launch and an allowed .. traversal in validated paths — neither of which causes incorrect behaviour in the current call sites. Files Needing Attention: windows-fnm-node-fallback.ts warrants a second look if agent-launch latency metrics are collected on Windows, since it always forks cmd.exe before the agent starts.
|
| Filename | Overview |
|---|---|
| src/shared/windows-path-entry.ts | New shared utility for validating Windows path entries — rejects semicolons (PATH injection), extended namespace prefixes, device paths with forward slashes, named pipes, relative paths, and paths with leading/trailing whitespace. Regex coverage is thorough. |
| src/main/codex-cli/command.ts | Adds getFnmDefaultDirectory with correct platform branching (POSIX keeps /bin, Windows drops it and resolves via FNM_DIR → APPDATA → home fallback). Exports getWindowsFnmDefaultDirectory for the fallback module. Case-insensitive env lookup for Windows is correct. |
| src/main/pty/windows-fnm-node-fallback.ts | New module implementing probe-before-prepend Windows fnm fallback. Probe uses cmd.exe with 3 s timeout and process-tree termination. Correctly canonicalizes PATH key spelling, deduplicates entries, and validates the fnm directory through normalizeSingleWindowsPathEntry before using it. Added latency per agent launch is worth tracking. |
| src/main/ipc/pty.ts | Adds two withWindowsFnmNodeFallback call sites (fresh agent launch and resume launch). Both are correctly gated by shouldApplyWindowsFnmNodeFallback. The resume path has an additional nativeWindowsConptySpawn guard, which appears intentional. |
| src/main/providers/local-pty-provider.ts | Adds canonicalizeWindowsPathKey call after the full spawn env is built, ensuring PATH key casing is normalized to the inherited env's spelling. Captures inheritedEnv before merging so the base reference is stable. |
| src/main/daemon/pty-subprocess.ts | Parallel change to local-pty-provider.ts: adds canonicalizeWindowsPathKey before the PTY spawn in the daemon subprocess path, stabilizing PATH key casing. |
Reviews (2): Last reviewed commit: "fix(windows): deduplicate PATH at PTY sp..." | Re-trigger Greptile
|
No actionable comments were generated in the recent review.
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/main/codex-cli/command.test.ts (1)
316-353: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winCover
FNM_DIRprecedence overAPPDATA.These tests exercise each fallback independently, but not the required ordering. Add a case with both variables set and assert that only the
FNM_DIR-derived directory is selected.Suggested coverage
+ it('prefers FNM_DIR over APPDATA on Windows', () => { + const root = mkdtempSync(join(tmpdir(), 'orca-vm-paths-')) + const fnmDir = join(root, 'fnm-data') + const appData = join(root, 'Roaming') + const paths = getVersionManagerBinPaths({ + platform: 'win32', + pathEnv: '', + homePath: root, + env: { FNM_DIR: fnmDir, APPDATA: appData } + }) + + expect(paths).toContain(join(fnmDir, 'aliases', 'default')) + expect(paths).not.toContain(join(appData, 'fnm', 'aliases', 'default')) + })
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 4749f929-9de8-4a2a-9874-f42622a308b3
📥 Commits
Reviewing files that changed from the base of the PR and between 4543bb6 and 74ba63355d5d0e5edc6a513cdc2950b2c6ce708e.
📒 Files selected for processing (2)
src/main/codex-cli/command.test.tssrc/main/codex-cli/command.ts
74ba633 to
0d13bf3
Compare
On Windows, fnm stores the default alias under FNM_DIR, else %APPDATA%\fnm (legacy ~\.fnm), and node.exe sits directly in aliases\default with no bin subdir. Probe those dirs and append them to PATH so fnm's default only backfills a missing Node instead of shadowing an inherited one. Fixes stablyai#11356 Co-authored-by: niceSprite <16523864+iFwu@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
0d13bf3 to
5a1f353
Compare
ELI5
On Windows, Orca looks in a few well-known folders for Node when it was launched from the Start Menu and can't see your shell's PATH. For fnm it was looking in the macOS/Linux folder layout, so fnm users on Windows got "node not found" style failures. Orca now looks where fnm actually puts Node on Windows.
What Changed
~\.fnm\aliases\default\bin, a POSIX-only layout. fnm on Windows keeps its data under%FNM_DIR%or%APPDATA%\fnm, andnode.exesits directly inaliases\default(nobin). Agent CLIs installed via fnm's default Node (e.g.codex.cmd) stayed unresolved.src/shared/fnm-default-alias-dirs.tsreturns fnm's default alias dir per platform. On Windows:FNM_DIR\aliases\defaultifFNM_DIRis set, else%APPDATA%\fnm\aliases\default(falling back to<home>\AppData\RoamingwhenAPPDATAis unset) plus the legacy~\.fnm\aliases\default. macOS/Linux keep~/.fnm/aliases/default/bin, unchanged.node-cli-command-resolution.ts) uses those dirs. The packaged-startup PATH patch (configure-process.ts) and the CLI agent env (account.ts) append the Windows fnm default dir instead of prepending it, so it only fills in a missing Node and never shadows an inherited system, nvm-windows, orfnm useNode.Why
fnm's default alias is a global fallback. Prepending it would override whichever Node the user picked for their session, so appending fixes the missing-Node case without hijacking an existing one. Putting the dir logic in one shared module keeps the resolver, startup PATH patch, and CLI env in agreement.
Linked Issue
Fixes #11356
Original fix by @iFwu in #11359; final version reworked during the bug bash.
Reported by @iFwu
Visual Proof
N/A: this changes Windows PATH/command resolution only. Nothing in the UI changes.
Testing
Repro (on
origin/mainsources with the new tests): both new assertions fail. The Windows fnm%APPDATA%alias dir is missing fromgetVersionManagerBinPaths, and the fnm alias is never on the packaged PATH.resolveCliCommand('codex', { platform: 'win32' })withcodex.cmd+node.exein<home>\AppData\Roaming\fnm\aliases\defaultreturns the barecodex(unresolved).On this branch:
src/main/codex-cli/command.test.tsandsrc/main/startup/configure-process.test.tspass (82/82). An earlier run of the related resolver, startup, account, and hydrate-shell-path suites passed 190/190. The extra probes (APPDATA then legacy order, POSIX path unchanged and ignoresFNM_DIR,codex.cmdresolves) pass.oxlint (anti-slop, code-quality native plugins), ast-grep, changed-code quality gate, and
oxfmt --checkare clean on all changed files.Platforms: simulated Windows (
platform: 'win32') on macOS. POSIX behavior is unchanged. Local only: SSH/remote hosts don't inherit this PATH.I manually tested these changes locally
Automated tests added/updated, or explained why not below
Review
Agent skill upstream boundary
Notes
Ensure no issues in: Security, Cross-platform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance
No new subprocesses or filesystem scans. The paths come only from
FNM_DIR,APPDATA, or the home dir. Remote/SSH and mobile are unaffected.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)