import { field, isObject } from "#common"; import { AgentAuthError } from "#profiles"; export function openCodeArgs(model: string, prompt: string): string[] { return ["run", "--format", "json", "--auto", "--model", model, prompt]; } type ChunkWriter = (chunk: Uint8Array) => void | Promise; interface OpenCodeOutput { stdout: string; stderr: string; } export async function collectOpenCodeOutput( stdout: AsyncIterable, stderr: AsyncIterable, writeStdout: ChunkWriter, writeStderr: ChunkWriter, ): Promise { const collect = async ( stream: AsyncIterable, write: ChunkWriter, ): Promise => { const chunks: Uint8Array[] = []; for await (const chunk of stream) { chunks.push(chunk); await write(chunk); } const length = chunks.reduce((total, chunk) => total + chunk.length, 0); const joined = new Uint8Array(length); let offset = 0; for (const chunk of chunks) { joined.set(chunk, offset); offset += chunk.length; } return new TextDecoder().decode(joined); }; const [output, error] = await Promise.all([ collect(stdout, writeStdout), collect(stderr, writeStderr), ]); return { stdout: output, stderr: error }; } // OpenCode reports provider failures, including a rejected OpenRouter key // (401 "User not found."), as a JSON error event on stdout and exits 1 with an // empty stderr. export function openCodeError(output: string): Error | null { let failure: Error | null = null; for (const line of output.split("\n")) { if (line.trim() === "") continue; let event: unknown; try { event = JSON.parse(line); } catch { continue; } const error = field(event, "error"); if (field(event, "type") !== "error" || !isObject(error)) { continue; } const name = field(error, "name"); const data = field(error, "data"); const message = field(data, "message"); const statusCode = field(data, "statusCode"); const detail = `opencode ${typeof name === "string" ? name : "error"}${ typeof statusCode === "number" ? ` (API error ${statusCode})` : "" }: ${typeof message === "string" ? message : "unknown error"}`; failure = statusCode === 401 || statusCode === 403 ? new AgentAuthError("opencode sign-in failed", { cause: new Error(detail), }) : new Error(detail); } return failure; } export function parseOpenCodeEvents(output: string): string { let result: string | null = null; for (const line of output.split("\n")) { if (line.trim() === "") continue; const event: unknown = JSON.parse(line); const part = field(event, "part"); if (field(event, "type") !== "text" || !isObject(part)) continue; const text = field(part, "text"); if (typeof text === "string" && text.trim() !== "") result = text; } if (result === null) { throw openCodeError(output) ?? new Error("OpenCode produced no text output"); } return result; }