diff --git a/src/application/ClientRegistrationIdentity.ts b/src/application/ClientRegistrationIdentity.ts index 0c35bebd7..a1fa9a2e0 100644 --- a/src/application/ClientRegistrationIdentity.ts +++ b/src/application/ClientRegistrationIdentity.ts @@ -13,8 +13,9 @@ const environmentValue = ( platform: NodeJS.Platform, ): string | undefined => { if (platform !== "win32") return environment[name]; - const key = Object.keys(environment).find( - (candidate) => candidate.toLowerCase() === name, + const target = name.toLowerCase(); + const key = Object.keys(environment).findLast( + (candidate) => candidate.toLowerCase() === target, ); return key === undefined ? undefined : environment[key]; }; diff --git a/src/setupEnvironment.test.ts b/src/setupEnvironment.test.ts index a4a777c85..5921d4d6f 100644 --- a/src/setupEnvironment.test.ts +++ b/src/setupEnvironment.test.ts @@ -212,3 +212,12 @@ it.each([ }); }, ); + +it("prioritizes overridden package runner environment variables over ambient keys with differing case on Windows", () => { + const environment = { + NPM_COMMAND: "run", + npm_command: "exec", + NPM_CONFIG_USER_AGENT: "npm/10.9.0 node/v24.11.0 win32 x64", + }; + expect(invokingPackageRunner(environment, "win32")).toBe("npx"); +}); diff --git a/tests/boundary/cli/cliSetup.test.ts b/tests/boundary/cli/cliSetup.test.ts index ad07a5bb8..15cea67f5 100644 --- a/tests/boundary/cli/cliSetup.test.ts +++ b/tests/boundary/cli/cliSetup.test.ts @@ -179,7 +179,10 @@ describe("TTY setup dry run", () => { describe("interactive setup journey", () => { it("shows detected and absent clients without preselecting detected clients", async () => { const result = await runJourney( - [step("Which agents should use REA?", "\u001b[B\r")], + [ + step("Which agents should use REA?", "\u001b[B"), + step("Not detected ยท configuration is still available", "\r"), + ], false, [actions[0]], ); @@ -359,7 +362,14 @@ describe("interactive setup completion", () => { { cwd: process.cwd(), env: { - ...process.env, + ...Object.fromEntries( + Object.entries(process.env).filter( + ([key]) => + !["npm_command", "npm_config_user_agent"].includes( + key.toLowerCase(), + ), + ), + ), npm_command: npmCommand, npm_config_user_agent: userAgent, NO_COLOR: "1", @@ -427,6 +437,7 @@ const runJourney = async ( env: { ...process.env, HOME: isolatedHome, + USERPROFILE: isolatedHome, NO_COLOR: "1", TERM: "xterm-256color", }, @@ -464,9 +475,13 @@ const runJourney = async ( finish(new Error(`Setup journey returned no decision:\n${output}`)); return; } - const serialized = output - .slice(markerIndex + decisionMarker.length) - .split(/\r?\n/u, 1)[0]; + const raw = output.slice(markerIndex + decisionMarker.length); + const jsonStart = raw.indexOf("{"); + const jsonEnd = raw.lastIndexOf("}"); + const serialized = + jsonStart !== -1 && jsonEnd !== -1 && jsonEnd > jsonStart + ? raw.slice(jsonStart, jsonEnd + 1) + : raw.split(/\r?\n/u, 1)[0]; if (serialized === undefined) { finish( new Error(`Setup journey returned an empty decision:\n${output}`),