diff --git a/src/python-runtime.ts b/src/python-runtime.ts index 266be2e..08faec1 100644 --- a/src/python-runtime.ts +++ b/src/python-runtime.ts @@ -1,3 +1,4 @@ +import { spawn } from "node:child_process"; import { constants as fsConstants } from "node:fs"; import { access } from "node:fs/promises"; import path from "node:path"; @@ -10,6 +11,23 @@ function splitCommand(command: string): string[] { return parts.map((part) => part.replace(/^"(.*)"$/, "$1")); } +async function canRun(command: string, args: string[]): Promise { + return await new Promise((resolve) => { + const child = spawn(command, [...args, "--version"], { + stdio: ["ignore", "ignore", "ignore"], + windowsHide: true, + }); + + child.on("error", () => { + resolve(false); + }); + + child.on("exit", (code) => { + resolve(code === 0); + }); + }); +} + async function fileExists(target: string): Promise { try { await access(target, fsConstants.X_OK); @@ -39,10 +57,35 @@ export async function resolveBasePythonCommand(config: AppConfig): Promise<{ com } if (process.platform === "win32") { - return { command: "python", args: [] }; + const candidates = [ + { command: "python", args: [] as string[] }, + { command: "py", args: ["-3"] }, + { command: "py", args: [] as string[] }, + ]; + + for (const candidate of candidates) { + if (await canRun(candidate.command, candidate.args)) { + return candidate; + } + } + + throw new Error( + "Windows에서 사용할 Python 실행기를 찾지 못했습니다. `python --version` 또는 `py -3 --version` 이 먼저 동작해야 합니다. 필요하면 .env 에 LOCAL_AI_PYTHON=python 또는 LOCAL_AI_PYTHON=py -3 를 넣으세요.", + ); } - return { command: "python3", args: [] }; + const unixCandidates = [ + { command: "python3", args: [] as string[] }, + { command: "python", args: [] as string[] }, + ]; + + for (const candidate of unixCandidates) { + if (await canRun(candidate.command, candidate.args)) { + return candidate; + } + } + + throw new Error("사용 가능한 Python 실행기를 찾지 못했습니다. `python3 --version` 또는 `python --version` 이 먼저 동작해야 합니다."); } export async function resolveWorkerPythonCommand(config: AppConfig): Promise<{ command: string; args: string[] }> {