| import launch from "cross-spawn"; | |
| import { stop, bindAbort } from "./process.js"; | |
| export async function createOpencodeServer(options) { | |
| options = Object.assign({ | |
| hostname: "127.0.0.1", | |
| port: 4096, | |
| timeout: 5000, | |
| }, options ?? {}); | |
| const args = [`serve`, `--hostname=${options.hostname}`, `--port=${options.port}`]; | |
| if (options.config?.logLevel) | |
| args.push(`--log-level=${options.config.logLevel}`); | |
| const proc = launch(`opencode`, args, { | |
| env: { | |
| ...process.env, | |
| OPENCODE_CONFIG_CONTENT: JSON.stringify(options.config ?? {}), | |
| }, | |
| }); | |
| let clear = () => { }; | |
| const url = await new Promise((resolve, reject) => { | |
| const id = setTimeout(() => { | |
| clear(); | |
| stop(proc); | |
| reject(new Error(`Timeout waiting for server to start after ${options.timeout}ms`)); | |
| }, options.timeout); | |
| let output = ""; | |
| let resolved = false; | |
| proc.stdout?.on("data", (chunk) => { | |
| if (resolved) | |
| return; | |
| output += chunk.toString(); | |
| const lines = output.split("\n"); | |
| for (const line of lines) { | |
| if (line.startsWith("opencode server listening")) { | |
| const match = line.match(/on\s+(https?:\/\/[^\s]+)/); | |
| if (!match) { | |
| clear(); | |
| stop(proc); | |
| clearTimeout(id); | |
| reject(new Error(`Failed to parse server url from output: ${line}`)); | |
| return; | |
| } | |
| clearTimeout(id); | |
| resolved = true; | |
| resolve(match[1]); | |
| return; | |
| } | |
| } | |
| }); | |
| proc.stderr?.on("data", (chunk) => { | |
| output += chunk.toString(); | |
| }); | |
| proc.on("exit", (code) => { | |
| clearTimeout(id); | |
| let msg = `Server exited with code ${code}`; | |
| if (output.trim()) { | |
| msg += `\nServer output: ${output}`; | |
| } | |
| reject(new Error(msg)); | |
| }); | |
| proc.on("error", (error) => { | |
| clearTimeout(id); | |
| reject(error); | |
| }); | |
| clear = bindAbort(proc, options.signal, () => { | |
| clearTimeout(id); | |
| reject(options.signal?.reason); | |
| }); | |
| }); | |
| return { | |
| url, | |
| close() { | |
| clear(); | |
| stop(proc); | |
| }, | |
| }; | |
| } | |
| export function createOpencodeTui(options) { | |
| const args = []; | |
| if (options?.project) { | |
| args.push(`--project=${options.project}`); | |
| } | |
| if (options?.model) { | |
| args.push(`--model=${options.model}`); | |
| } | |
| if (options?.session) { | |
| args.push(`--session=${options.session}`); | |
| } | |
| if (options?.agent) { | |
| args.push(`--agent=${options.agent}`); | |
| } | |
| const proc = launch(`opencode`, args, { | |
| stdio: "inherit", | |
| env: { | |
| ...process.env, | |
| OPENCODE_CONFIG_CONTENT: JSON.stringify(options?.config ?? {}), | |
| }, | |
| }); | |
| const clear = bindAbort(proc, options?.signal); | |
| return { | |
| close() { | |
| clear(); | |
| stop(proc); | |
| }, | |
| }; | |
| } | |
Xet Storage Details
- Size:
- 3.3 kB
- Xet hash:
- f31a441b6fd7b1433995b266be449edd363341f97175c67c772ac02d0f6d2843
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.