From 2abfe53dbdbdf7340e5243eb593ec3abcf3d2a5d Mon Sep 17 00:00:00 2001 From: Swatantra Yadav Date: Thu, 15 Oct 2026 00:56:31 +0530 Subject: [PATCH] fix(setup): preserve overridden runner variables and test isolation on Windows Support case-insensitive environment value lookup in ClientRegistrationIdentity while preserving JavaScript property order precedence with findLast(). Add unit regression coverage for ambient uppercase npm variables on Windows. Isolate USERPROFILE in cliSetup tests so child processes do not touch the host profile directory. Harden JSON decision extraction in runJourney against ConPTY trailing ANSI escape codes. Deterministically sequence multiselect prompt navigation before confirmation. Filter ambient runner variables case-insensitively before launching interactive setup completion tests. --- src/application/ClientRegistrationIdentity.ts | 5 ++-- src/setupEnvironment.test.ts | 9 +++++++ tests/boundary/cli/cliSetup.test.ts | 25 +++++++++++++++---- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/application/ClientRegistrationIdentity.ts b/src/application/ClientRegistrationIdentity.ts index 0c35bebd79..a1fa9a2e01 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 d40533656b..45b47083d0 100644 --- a/src/setupEnvironment.test.ts +++ b/src/setupEnvironment.test.ts @@ -182,3 +182,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 ad07a5bb8f..15cea67f50 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}`),