mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-29 19:34:32 +00:00
fix(desktop-wsl): time-bound every wsl.exe invocation to fail fast on wedge
Ubuntu-24.04 in the failed first-run state wedges wsl.exe silently - no stdout, no exit. Without a timeout resolveWslOpencode (and any other runWsl call) blocks the sidecar spawn flow forever, which hides the real failure from logs and from the controller's failed runtime state. Add a 20s default ceiling to runCommand; caller can override for long-running jobs (installs).
This commit is contained in:
parent
0e7e791008
commit
6fc5f342dd
1 changed files with 30 additions and 1 deletions
|
|
@ -16,8 +16,19 @@ export type WslCommandResult = {
|
|||
type RunWslOptions = {
|
||||
onLine?: (line: WslCommandLine) => void
|
||||
signal?: AbortSignal
|
||||
/**
|
||||
* Ceiling on how long we wait for the child process to exit. When the
|
||||
* LXSS service or a specific distro wedges (e.g. Ubuntu-24.04 with a
|
||||
* pending first-run prompt), `wsl.exe` never returns and any command
|
||||
* that doesn't specify a timeout hangs the entire startup flow. Default
|
||||
* is 20s — enough for slow cold-starts, short enough to fail fast on
|
||||
* a wedge. Callers can override for longer-running jobs.
|
||||
*/
|
||||
timeoutMs?: number
|
||||
}
|
||||
|
||||
const DEFAULT_WSL_TIMEOUT_MS = 20_000
|
||||
|
||||
// `--user root` bypasses the distro's default-user requirement. A freshly
|
||||
// installed WSL distro (Ubuntu-24.04 in particular) prompts interactively
|
||||
// for a username/password on its first invocation; when spawned with
|
||||
|
|
@ -51,6 +62,20 @@ function runCommand(command: string, args: string[], opts: RunWslOptions = {}) {
|
|||
signal: opts.signal,
|
||||
})
|
||||
|
||||
// Guard every wsl.exe invocation with a timeout. When the distro or
|
||||
// the LXSS service is wedged (Ubuntu first-run state, Windows update
|
||||
// pending, etc.) wsl.exe produces no output and never exits; without
|
||||
// this the whole sidecar spawn flow stalls the app forever.
|
||||
const timeoutMs = opts.timeoutMs ?? DEFAULT_WSL_TIMEOUT_MS
|
||||
const timeoutId = setTimeout(() => {
|
||||
try {
|
||||
child.kill()
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
reject(new Error(`${command} ${args.join(" ")} timed out after ${timeoutMs}ms`))
|
||||
}, timeoutMs)
|
||||
|
||||
let stdout = ""
|
||||
let stderr = ""
|
||||
let stdoutPending = ""
|
||||
|
|
@ -97,8 +122,12 @@ function runCommand(command: string, args: string[], opts: RunWslOptions = {}) {
|
|||
stderrPending = flush("stderr", stderrPending)
|
||||
})
|
||||
|
||||
child.once("error", reject)
|
||||
child.once("error", (error) => {
|
||||
clearTimeout(timeoutId)
|
||||
reject(error)
|
||||
})
|
||||
child.once("close", (code, signal) => {
|
||||
clearTimeout(timeoutId)
|
||||
resolve({ code, signal, stdout, stderr })
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue