summaryrefslogtreecommitdiffhomepage
path: root/packages/sdk/js/src/v2
diff options
context:
space:
mode:
authorLuke Parker <[email protected]>2026-04-03 13:18:50 +1000
committerGitHub <[email protected]>2026-04-03 13:18:50 +1000
commit7b8dc8065e63b3bfee9dd56dad5548f1706b5acc (patch)
tree5d5f0aa4337471c089c3d70e012841cd57bbf50e /packages/sdk/js/src/v2
parente89527c9f0eba6c7aa866f3ba39ce9f6809023a7 (diff)
downloadopencode-7b8dc8065e63b3bfee9dd56dad5548f1706b5acc.tar.gz
opencode-7b8dc8065e63b3bfee9dd56dad5548f1706b5acc.zip
fix(sdk): handle Windows opencode spawn and shutdown (#20772)
Diffstat (limited to 'packages/sdk/js/src/v2')
-rw-r--r--packages/sdk/js/src/v2/server.ts39
1 files changed, 25 insertions, 14 deletions
diff --git a/packages/sdk/js/src/v2/server.ts b/packages/sdk/js/src/v2/server.ts
index 174131ccf..48f1a253d 100644
--- a/packages/sdk/js/src/v2/server.ts
+++ b/packages/sdk/js/src/v2/server.ts
@@ -1,5 +1,6 @@
-import { spawn } from "node:child_process"
+import launch from "cross-spawn"
import { type Config } from "./gen/types.gen.js"
+import { stop, bindAbort } from "../process.js"
export type ServerOptions = {
hostname?: string
@@ -31,29 +32,38 @@ export async function createOpencodeServer(options?: ServerOptions) {
const args = [`serve`, `--hostname=${options.hostname}`, `--port=${options.port}`]
if (options.config?.logLevel) args.push(`--log-level=${options.config.logLevel}`)
- const proc = spawn(`opencode`, args, {
- signal: options.signal,
+ const proc = launch(`opencode`, args, {
env: {
...process.env,
OPENCODE_CONFIG_CONTENT: JSON.stringify(options.config ?? {}),
},
})
+ let clear = () => {}
const url = await new Promise<string>((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) {
- throw new Error(`Failed to parse server url from output: ${line}`)
+ 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
}
@@ -74,18 +84,17 @@ export async function createOpencodeServer(options?: ServerOptions) {
clearTimeout(id)
reject(error)
})
- if (options.signal) {
- options.signal.addEventListener("abort", () => {
- clearTimeout(id)
- reject(new Error("Aborted"))
- })
- }
+ clear = bindAbort(proc, options.signal, () => {
+ clearTimeout(id)
+ reject(options.signal?.reason)
+ })
})
return {
url,
close() {
- proc.kill()
+ clear()
+ stop(proc)
},
}
}
@@ -106,8 +115,7 @@ export function createOpencodeTui(options?: TuiOptions) {
args.push(`--agent=${options.agent}`)
}
- const proc = spawn(`opencode`, args, {
- signal: options?.signal,
+ const proc = launch(`opencode`, args, {
stdio: "inherit",
env: {
...process.env,
@@ -115,9 +123,12 @@ export function createOpencodeTui(options?: TuiOptions) {
},
})
+ const clear = bindAbort(proc, options?.signal)
+
return {
close() {
- proc.kill()
+ clear()
+ stop(proc)
},
}
}