Files
paseo/packages/cli/src/commands/daemon/runtime-toolchain.ts
T
Mohamed BoudraandGitHub bf76915884 fix: centralize Windows exec handling and async migrations (#318)
* Add Windows exec foundation helpers

* server: centralize windows exec handling phase 2

* migrate cli daemon status to execCommand

* refactor: complete phase 4 async exec migrations

* test: cover windows exec helpers

* fix: use beforeEach for context.skip in test setup
2026-04-13 16:20:59 +08:00

105 lines
2.9 KiB
TypeScript

import { platform } from "node:os";
import { execCommand } from "@getpaseo/server";
export interface NodePathFromPidResult {
nodePath: string | null;
error?: string;
}
function normalizeError(error: unknown): string {
if (error instanceof Error) {
return error.message;
}
return String(error);
}
async function resolveNodePathFromPidUnix(pid: number): Promise<NodePathFromPidResult> {
try {
const { stdout } = await execCommand("ps", ["-o", "comm=", "-p", String(pid)]);
const resolved = stdout.trim();
return resolved
? { nodePath: resolved }
: { nodePath: null, error: "ps returned an empty command path" };
} catch (error) {
return { nodePath: null, error: `ps failed: ${normalizeError(error)}` };
}
}
async function runProcessProbe(
command: string,
args: string[],
): Promise<{
resolved: string | null;
error?: string;
}> {
try {
const { stdout } = await execCommand(command, args);
const resolved = stdout.trim();
return resolved
? { resolved }
: { resolved: null, error: `${command} returned no executable path` };
} catch (error) {
return { resolved: null, error: `${command} failed: ${normalizeError(error)}` };
}
}
async function resolveNodePathFromPidWindows(pid: number): Promise<NodePathFromPidResult> {
const probes: Array<{
label: string;
command: string;
args: string[];
parseValue?: (stdout: string) => string | null;
}> = [
{
label: "powershell-cim",
command: "powershell",
args: [
"-NoProfile",
"-Command",
`(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}").ExecutablePath`,
],
},
{
label: "powershell-process",
command: "powershell",
args: ["-NoProfile", "-Command", `(Get-Process -Id ${pid}).Path`],
},
{
label: "wmic",
command: "wmic",
args: ["process", "where", `ProcessId=${pid}`, "get", "ExecutablePath", "/VALUE"],
parseValue: (stdout) => {
const match = stdout.match(/ExecutablePath=(.+)/);
return match?.[1]?.trim() ?? null;
},
},
];
const errors: string[] = [];
for (const probe of probes) {
const result = await runProcessProbe(probe.command, probe.args);
if (result.resolved) {
const resolved = probe.parseValue ? probe.parseValue(result.resolved) : result.resolved;
if (resolved) {
return { nodePath: resolved };
}
errors.push(`${probe.label} returned no executable path`);
continue;
}
if (result.error) {
errors.push(`${probe.label}: ${result.error}`);
}
}
return {
nodePath: null,
error: errors.join("; ") || "could not resolve executable path from PID",
};
}
export async function resolveNodePathFromPid(pid: number): Promise<NodePathFromPidResult> {
return platform() === "win32"
? await resolveNodePathFromPidWindows(pid)
: await resolveNodePathFromPidUnix(pid);
}