diff --git a/src/cli/nodes-cli/register.status.ts b/src/cli/nodes-cli/register.status.ts index 16393bbf4980..011b99b24f30 100644 --- a/src/cli/nodes-cli/register.status.ts +++ b/src/cli/nodes-cli/register.status.ts @@ -80,7 +80,12 @@ function formatNodeVersions(node: { return parts.length > 0 ? parts.join(" · ") : null; } -function formatPathEnv(raw?: string): string | null { +function isWindowsNodePlatform(platform?: string): boolean { + const normalized = normalizeOptionalLowercaseString(platform) ?? ""; + return normalized === "win32" || normalized === "windows"; +} + +function formatPathEnv(raw?: string, platform?: string): string | null { if (typeof raw !== "string") { return null; } @@ -88,9 +93,12 @@ function formatPathEnv(raw?: string): string | null { if (!trimmed) { return null; } - const parts = trimmed.split(":").filter(Boolean); + const delimiter = isWindowsNodePlatform(platform) ? ";" : ":"; + const parts = trimmed.split(delimiter).filter(Boolean); const display = - parts.length <= 3 ? trimmed : `${parts.slice(0, 2).join(":")}:…:${parts.slice(-1)[0]}`; + parts.length <= 3 + ? trimmed + : `${parts.slice(0, 2).join(delimiter)}${delimiter}…${delimiter}${parts.slice(-1)[0]}`; return shortenHomeInString(display); } @@ -298,7 +306,7 @@ export function registerNodesStatusCommands(nodes: Command) { const rows = filtered.map((n) => { const perms = formatPermissions(n.permissions); const versions = formatNodeVersions(n); - const pathEnv = formatPathEnv(n.pathEnv); + const pathEnv = formatPathEnv(n.pathEnv, n.platform); const client = formatClientLabel(n); const lastActive = formatLastActive(now, n.lastActiveAtMs); const detailParts = [ diff --git a/src/cli/program.nodes-basic.e2e.test.ts b/src/cli/program.nodes-basic.e2e.test.ts index 6c419f57807d..516f7d8b0f82 100644 --- a/src/cli/program.nodes-basic.e2e.test.ts +++ b/src/cli/program.nodes-basic.e2e.test.ts @@ -471,6 +471,47 @@ describe("cli program (nodes basics)", () => { ).toBe(true); }); + it.each([ + { + platform: "win32", + pathEnv: "C:\\one;D:\\two;E:\\three;F:\\four", + expectedPath: "path: C:\\one;D:\\two;…;F:\\four", + rejectedPath: "path: C:\\one;D:…:\\four", + }, + { + platform: "windows", + pathEnv: "C:\\one;D:\\two;E:\\three;F:\\four", + expectedPath: "path: C:\\one;D:\\two;…;F:\\four", + rejectedPath: "path: C:\\one;D:…:\\four", + }, + { + platform: "linux", + pathEnv: "/one:/two:/three:/four", + expectedPath: "path: /one:/two:…:/four", + rejectedPath: "path: /one:/two:/three:/four", + }, + ])("renders $platform node PATH entries with their platform delimiter", async (fixture) => { + callGateway.mockResolvedValue({ + ts: Date.now(), + nodes: [ + { + nodeId: `${fixture.platform}-node`, + displayName: `${fixture.platform} node`, + platform: fixture.platform, + pathEnv: fixture.pathEnv, + paired: true, + connected: true, + }, + ], + }); + + await runProgram(["nodes", "status"]); + + const output = getRuntimeOutput(); + expect(output).toContain(fixture.expectedPath); + expect(output).not.toContain(fixture.rejectedPath); + }); + it("keeps connection age adjacent to connection status before pending approval", async () => { callGateway.mockResolvedValue({ ts: Date.now(),