diff --git a/plugins/opencode/scripts/lib/opencode-server.mjs b/plugins/opencode/scripts/lib/opencode-server.mjs index f4192a8..15f6867 100644 --- a/plugins/opencode/scripts/lib/opencode-server.mjs +++ b/plugins/opencode/scripts/lib/opencode-server.mjs @@ -4,6 +4,7 @@ import { spawn } from "node:child_process"; +const IS_WINDOWS = process.platform === "win32"; const DEFAULT_PORT = 4096; const DEFAULT_HOST = "127.0.0.1"; const SERVER_START_TIMEOUT = 30_000; @@ -43,10 +44,12 @@ export async function ensureServer(opts = {}) { } // Start the server + // Windows npm shims are .cmd/.ps1; spawn() only resolves those via a shell. const proc = spawn("opencode", ["serve", "--port", String(port)], { stdio: ["ignore", "pipe", "pipe"], detached: true, cwd: opts.cwd, + shell: IS_WINDOWS, }); proc.unref(); diff --git a/plugins/opencode/scripts/lib/process.mjs b/plugins/opencode/scripts/lib/process.mjs index a199e78..0806d8d 100644 --- a/plugins/opencode/scripts/lib/process.mjs +++ b/plugins/opencode/scripts/lib/process.mjs @@ -2,16 +2,22 @@ import { spawn } from "node:child_process"; +const IS_WINDOWS = process.platform === "win32"; + /** * Resolve the full path to the `opencode` binary. * @returns {Promise} */ export async function resolveOpencodeBinary() { return new Promise((resolve) => { - const proc = spawn("which", ["opencode"], { stdio: ["ignore", "pipe", "ignore"] }); + // `which` isn't a native Windows binary; `where` is the equivalent. + const proc = spawn(IS_WINDOWS ? "where" : "which", ["opencode"], { + stdio: ["ignore", "pipe", "ignore"], + }); let out = ""; proc.stdout.on("data", (d) => (out += d)); - proc.on("close", (code) => resolve(code === 0 ? out.trim() : null)); + proc.on("close", (code) => resolve(code === 0 ? out.trim().split(/\r?\n/)[0] : null)); + proc.on("error", () => resolve(null)); }); } @@ -30,12 +36,15 @@ export async function isOpencodeInstalled() { */ export async function getOpencodeVersion() { return new Promise((resolve) => { + // Windows npm shims are .cmd/.ps1; spawn() only resolves those via a shell. const proc = spawn("opencode", ["--version"], { stdio: ["ignore", "pipe", "ignore"], + shell: IS_WINDOWS, }); let out = ""; proc.stdout.on("data", (d) => (out += d)); proc.on("close", (code) => resolve(code === 0 ? out.trim() : null)); + proc.on("error", () => resolve(null)); }); } @@ -52,12 +61,14 @@ export function runCommand(cmd, args, opts = {}) { stdio: ["ignore", "pipe", "pipe"], cwd: opts.cwd, env: { ...process.env, ...opts.env }, + shell: IS_WINDOWS, }); let stdout = ""; let stderr = ""; proc.stdout.on("data", (d) => (stdout += d)); proc.stderr.on("data", (d) => (stderr += d)); proc.on("close", (exitCode) => resolve({ stdout, stderr, exitCode: exitCode ?? 1 })); + proc.on("error", (err) => resolve({ stdout: "", stderr: String(err), exitCode: 1 })); }); } @@ -74,6 +85,7 @@ export function spawnDetached(cmd, args, opts = {}) { detached: true, cwd: opts.cwd, env: { ...process.env, ...opts.env }, + shell: IS_WINDOWS, }); child.unref(); return child;