From 558d0c5edefebc543e94da6defc4925be137bed6 Mon Sep 17 00:00:00 2001 From: avenom Date: Sun, 1 Nov 2026 03:20:49 +0300 Subject: [PATCH] fix: preserve Windows PowerShell Unicode output --- src/process-sessions.test.ts | 28 ++++++++++++++++++++++++++++ src/process-sessions.ts | 6 +++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/process-sessions.test.ts b/src/process-sessions.test.ts index 39849168a..c7ae7b376 100644 --- a/src/process-sessions.test.ts +++ b/src/process-sessions.test.ts @@ -1,5 +1,6 @@ import { isJsonNumber } from "./json-value.js"; import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; @@ -61,6 +62,33 @@ test("a command that finishes within the yield time returns no session", async ( assert.equal(foreground.sessionId, undefined); }); +test("Windows PowerShell output is decoded as UTF-8 from a legacy console code page", { skip: process.platform !== "win32" }, async (t) => { + const comspec = process.env.ComSpec ?? process.env.COMSPEC ?? "cmd.exe"; + + const currentCodePage = spawnSync(comspec, ["/d", "/s", "/c", "chcp"], { + encoding: "latin1", + windowsHide: true, + }).stdout.match(/\d+/)?.[0] ?? "65001"; + + spawnSync(comspec, ["/d", "/s", "/c", "chcp 866>nul"], { windowsHide: true }); + t.after(() => { + spawnSync(comspec, ["/d", "/s", "/c", `chcp ${currentCodePage}>nul`], { windowsHide: true }); + }); + + const manager = createManager(t); + + const unicode = await manager.start({ + workspaceId: "workspace-a", + cwd: process.cwd(), + command: 'powershell.exe -NoProfile -Command "Write-Output \'Проверка русского текста: Привет, мир — ёж\'"', + yieldTimeMs: 2_000, + }); + + assert.equal(unicode.running, false); + assert.equal(unicode.exitCode, 0); + assert.match(unicode.output, /Проверка русского текста: Привет, мир — ёж/); +}); + test("commands run with a non-interactive environment and workspace identity", async (t) => { const manager = createManager(t); diff --git a/src/process-sessions.ts b/src/process-sessions.ts index 4502c7e54..3b8963053 100644 --- a/src/process-sessions.ts +++ b/src/process-sessions.ts @@ -431,7 +431,11 @@ export class ProcessSessionManager { const shell = resolveShellCommand(input.command); const detached = process.platform !== "win32"; - const child = spawn(input.command, { + const command = process.platform === "win32" + ? `chcp 65001>nul && ${input.command}` + : input.command; + + const child = spawn(command, { cwd: input.cwd, env: processEnvironment({ workspaceId: input.workspaceId,