From 2667fc26325a6c3cfc630ae706932250dd05213d Mon Sep 17 00:00:00 2001 From: claude-bot Date: Sat, 2 May 2026 20:46:09 +0900 Subject: [PATCH] Resolve Python via where on Windows --- src/python-runtime.ts | 54 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/python-runtime.ts b/src/python-runtime.ts index 08faec1..77b7d94 100644 --- a/src/python-runtime.ts +++ b/src/python-runtime.ts @@ -28,6 +28,55 @@ async function canRun(command: string, args: string[]): Promise { }); } +async function captureStdout(command: string, args: string[]): Promise { + return await new Promise((resolve) => { + const child = spawn(command, args, { + stdio: ["ignore", "pipe", "ignore"], + windowsHide: true, + }); + + let stdout = ""; + child.stdout.on("data", (chunk: Buffer) => { + stdout += chunk.toString(); + }); + + child.on("error", () => { + resolve(null); + }); + + child.on("exit", (code) => { + if (code === 0) { + resolve(stdout); + return; + } + resolve(null); + }); + }); +} + +async function resolveWindowsExecutable(name: string): Promise { + const stdout = await captureStdout("cmd.exe", ["/d", "/s", "/c", `where ${name}`]); + if (!stdout) { + return null; + } + + const candidates = stdout + .split(/\r?\n/) + .map((line) => line.trim()) + .filter((line) => line.length > 0); + + for (const candidate of candidates) { + try { + await access(candidate, fsConstants.F_OK); + return candidate; + } catch { + // ignore + } + } + + return null; +} + async function fileExists(target: string): Promise { try { await access(target, fsConstants.X_OK); @@ -64,6 +113,11 @@ export async function resolveBasePythonCommand(config: AppConfig): Promise<{ com ]; for (const candidate of candidates) { + const absolute = await resolveWindowsExecutable(candidate.command); + if (absolute && (await canRun(absolute, candidate.args))) { + return { command: absolute, args: candidate.args }; + } + if (await canRun(candidate.command, candidate.args)) { return candidate; }