This commit is contained in:
吴杨帆 2026-05-31 01:23:29 -04:00 committed by GitHub
commit efbe395f21
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 77 additions and 0 deletions

View file

@ -701,6 +701,30 @@ test('resolveAgentExecutable accepts Windows CODEX_BIN overrides with executable
}
});
test('resolveAgentExecutable searches Windows fnm multishell shims under a minimal GUI PATH (#3062)', () => {
const home = mkdtempSync(join(tmpdir(), 'od-agent-fnm-multishell-'));
const localAppData = join(home, 'AppData', 'Local');
const fnmMultishell = join(localAppData, 'fnm_multishells', '12345-abcd');
try {
return withEnvSnapshot(['PATH', 'PATHEXT', 'OD_AGENT_HOME', 'LOCALAPPDATA'], () => {
mkdirSync(fnmMultishell, { recursive: true });
writeFileSync(join(fnmMultishell, 'codex.CMD'), '@echo off\r\nexit /b 0\r\n');
process.env.OD_AGENT_HOME = home;
process.env.LOCALAPPDATA = localAppData;
process.env.PATH = '';
process.env.PATHEXT = '.EXE;.CMD;.BAT';
const resolved = withPlatform('win32', () =>
resolveAgentExecutable(minimalAgentDef({ id: 'codex', bin: 'codex' })),
);
assert.equal(resolved, join(fnmMultishell, 'codex.CMD'));
});
} finally {
rmSync(home, { recursive: true, force: true });
}
});
test('detectAgents applies configured env while probing the CLI', async () => {
const dir = mkdtempSync(join(tmpdir(), 'od-agent-env-'));
try {

View file

@ -987,6 +987,28 @@ export function wellKnownUserToolchainBins(
dirs.push(dir);
}
}
if (process.platform === "win32") {
const localAppData =
resolveUserScopedHome(env.LOCALAPPDATA, home) ?? join(home, "AppData", "Local");
const appData =
resolveUserScopedHome(env.APPDATA, home) ?? join(home, "AppData", "Roaming");
dirs.push(join(appData, "npm"));
const fnmDir = typeof env.FNM_DIR === "string" ? env.FNM_DIR.trim() : "";
for (const root of [
join(localAppData, "fnm", "node-versions"),
...(fnmDir.length > 0 ? [join(fnmDir, "node-versions")] : []),
]) {
for (const dir of existingChildBinDirs(root, ["installation", "bin"])) {
dirs.push(dir);
}
}
// fnm exposes npm-global CLIs via per-shell shims under fnm_multishells
// after `fnm env`. GUI-launched Electron inherits a stripped PATH without
// those ephemeral dirs (issue #3062).
for (const dir of existingChildBinDirs(join(localAppData, "fnm_multishells"), [])) {
dirs.push(dir);
}
}
return dirs;
}

View file

@ -1042,4 +1042,35 @@ describe("wellKnownUserToolchainBins", () => {
rmSync(home, { recursive: true, force: true });
}
});
it("includes Windows fnm node installs, multishell shims, and npm globals (#3062)", () => {
const originalPlatform = process.platform;
Object.defineProperty(process, "platform", { configurable: true, value: "win32" });
const home = mkdtempSync(join(tmpdir(), "wkutb-win-fnm-"));
const localAppData = join(home, "AppData", "Local");
const appData = join(home, "AppData", "Roaming");
const fnmNodeBin = join(localAppData, "fnm", "node-versions", "v20.20.2", "installation", "bin");
const fnmMultishell = join(localAppData, "fnm_multishells", "12345-abcd");
const npmGlobal = join(appData, "npm");
try {
mkdirSync(fnmNodeBin, { recursive: true });
mkdirSync(fnmMultishell, { recursive: true });
mkdirSync(npmGlobal, { recursive: true });
writeFileSync(join(fnmNodeBin, "marker"), "");
writeFileSync(join(fnmMultishell, "marker"), "");
writeFileSync(join(npmGlobal, "marker"), "");
const dirs = wellKnownUserToolchainBins({
home,
env: { LOCALAPPDATA: localAppData, APPDATA: appData },
includeSystemBins: false,
});
expect(dirs).toContain(fnmNodeBin);
expect(dirs).toContain(fnmMultishell);
expect(dirs).toContain(npmGlobal);
} finally {
Object.defineProperty(process, "platform", { configurable: true, value: originalPlatform });
rmSync(home, { recursive: true, force: true });
}
});
});