From c00121273bd0bc1262cd04ad43d24ac9108366f8 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 25 Jun 2026 19:17:02 +0800 Subject: [PATCH] Fix Windows daemon status output (#1725) --- .../src/commands/daemon/runtime-toolchain.ts | 9 +- ...emon-status-windows-process-lookup.test.ts | 97 +++++++++++++++++++ .../scripts/smoke-packaged-desktop-app.js | 16 ++- 3 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 packages/cli/tests/36-daemon-status-windows-process-lookup.test.ts diff --git a/packages/cli/src/commands/daemon/runtime-toolchain.ts b/packages/cli/src/commands/daemon/runtime-toolchain.ts index 32357372a..be1743965 100644 --- a/packages/cli/src/commands/daemon/runtime-toolchain.ts +++ b/packages/cli/src/commands/daemon/runtime-toolchain.ts @@ -28,12 +28,13 @@ async function resolveNodePathFromPidUnix(pid: number): Promise { try { - const { stdout } = await execCommand(command, args); + const { stdout } = await execCommand(command, args, { shell: options?.shell }); const resolved = stdout.trim(); return resolved ? { resolved } @@ -52,7 +53,7 @@ async function resolveNodePathFromPidWindows(pid: number): Promise = [ { label: "powershell-cim", - command: "powershell", + command: "powershell.exe", args: [ "-NoProfile", "-Command", @@ -61,7 +62,7 @@ async function resolveNodePathFromPidWindows(pid: number): Promise { + return new Promise((resolve, reject) => { + const child = spawn(process.execPath, [CLI_ENTRY, ...args], { + env: { ...process.env, ...env }, + stdio: ["ignore", "pipe", "pipe"], + windowsHide: true, + }); + + let stdout = ""; + let stderr = ""; + child.stdout.on("data", (chunk: Buffer) => { + stdout += chunk.toString(); + }); + child.stderr.on("data", (chunk: Buffer) => { + stderr += chunk.toString(); + }); + child.on("error", reject); + child.on("close", (exitCode) => { + resolve({ exitCode, stdout, stderr }); + }); + }); +} + +if (process.platform !== "win32") { + console.log("Skipping Windows daemon status process lookup regression on non-Windows."); + process.exit(0); +} + +console.log("=== Windows Daemon Status Process Lookup ===\n"); + +const paseoHome = await mkdtemp(join(tmpdir(), "paseo-windows-status-home-")); +const port = await getAvailablePort(); +const env = { + PASEO_HOME: paseoHome, + PASEO_LOCAL_SPEECH_AUTO_DOWNLOAD: "0", + PASEO_DICTATION_ENABLED: "0", + PASEO_VOICE_MODE_ENABLED: "0", +}; + +try { + const start = await runLocalPaseo(["daemon", "restart", "--port", String(port)], env); + assert.strictEqual( + start.exitCode, + 0, + `daemon restart should succeed:\nstdout:\n${start.stdout}\nstderr:\n${start.stderr}`, + ); + + const statusResult = await runLocalPaseo( + ["daemon", "status", "--home", paseoHome, "--json"], + env, + ); + assert.strictEqual( + statusResult.exitCode, + 0, + `daemon status should succeed:\nstdout:\n${statusResult.stdout}\nstderr:\n${statusResult.stderr}`, + ); + + const status = JSON.parse(statusResult.stdout) as { + localDaemon?: string; + daemonNode?: string; + }; + assert.strictEqual(status.localDaemon, "running", "daemon should be running"); + assert(status.daemonNode, "daemon status should include daemonNode"); + assert( + !status.daemonNode.startsWith("unknown ("), + `daemonNode should resolve cleanly, got: ${status.daemonNode}`, + ); + assert( + !status.daemonNode.includes("Get-Process") && + !status.daemonNode.includes("Get-CimInstance") && + !status.daemonNode.includes("wmic failed"), + `daemonNode should not contain process probe failures, got: ${status.daemonNode}`, + ); + console.log("✓ daemon status resolves daemonNode on Windows\n"); +} finally { + await runLocalPaseo(["daemon", "stop", "--home", paseoHome, "--force"], env); + await rm(paseoHome, { recursive: true, force: true }); +} + +console.log("=== Windows daemon status process lookup passed ==="); diff --git a/packages/desktop/scripts/smoke-packaged-desktop-app.js b/packages/desktop/scripts/smoke-packaged-desktop-app.js index 8c9643aed..d05efcf37 100644 --- a/packages/desktop/scripts/smoke-packaged-desktop-app.js +++ b/packages/desktop/scripts/smoke-packaged-desktop-app.js @@ -361,12 +361,26 @@ async function runCliShimJsonCommand({ appPath, env, args, label }) { async function smokeCliShim({ appPath, env }) { console.log("Packaged desktop smoke: running bundled CLI shim daemon status"); - await runCliShimCommand({ + const result = await runCliShimCommand({ appPath, env, args: ["daemon", "status"], label: "Bundled CLI shim daemon status", }); + assertCleanDaemonStatusOutput(`${result.stdout}\n${result.stderr}`); +} + +function assertCleanDaemonStatusOutput(output) { + const failureNeedles = [ + "Get-CimInstance", + "Get-Process :", + "Cannot bind parameter", + "wmic failed", + ]; + const failure = failureNeedles.find((needle) => output.includes(needle)); + if (failure) { + throw new Error(`Bundled CLI shim daemon status included process lookup failure: ${failure}`); + } } async function smokeCliTerminal({ appPath, env }) {