2026-03-08 09:15:13 -06:00
|
|
|
import { existsSync } from "node:fs"
|
2026-03-07 15:56:21 -07:00
|
|
|
import { getOpenCodeCacheDir } from "../../shared/data-path"
|
|
|
|
|
import { log } from "../../shared/logger"
|
2026-02-26 21:00:24 +09:00
|
|
|
import { spawnWithWindowsHide } from "../../shared/spawn-with-windows-hide"
|
2026-02-08 15:01:42 +09:00
|
|
|
|
|
|
|
|
const BUN_INSTALL_TIMEOUT_SECONDS = 60
|
|
|
|
|
const BUN_INSTALL_TIMEOUT_MS = BUN_INSTALL_TIMEOUT_SECONDS * 1000
|
|
|
|
|
|
|
|
|
|
export interface BunInstallResult {
|
|
|
|
|
success: boolean
|
|
|
|
|
timedOut?: boolean
|
|
|
|
|
error?: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function runBunInstall(): Promise<boolean> {
|
|
|
|
|
const result = await runBunInstallWithDetails()
|
|
|
|
|
return result.success
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function runBunInstallWithDetails(): Promise<BunInstallResult> {
|
2026-03-07 15:56:21 -07:00
|
|
|
const cacheDir = getOpenCodeCacheDir()
|
2026-03-08 09:15:13 -06:00
|
|
|
const packageJsonPath = `${cacheDir}/package.json`
|
|
|
|
|
|
|
|
|
|
if (!existsSync(packageJsonPath)) {
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: `Workspace not initialized: ${packageJsonPath} not found. OpenCode should create this on first run.`,
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-07 15:56:21 -07:00
|
|
|
|
2026-02-08 15:01:42 +09:00
|
|
|
try {
|
2026-02-26 21:00:24 +09:00
|
|
|
const proc = spawnWithWindowsHide(["bun", "install"], {
|
2026-03-07 15:56:21 -07:00
|
|
|
cwd: cacheDir,
|
2026-02-08 21:57:34 +09:00
|
|
|
stdout: "inherit",
|
|
|
|
|
stderr: "inherit",
|
2026-02-08 15:01:42 +09:00
|
|
|
})
|
|
|
|
|
|
2026-02-08 15:31:32 +09:00
|
|
|
let timeoutId: ReturnType<typeof setTimeout>
|
|
|
|
|
const timeoutPromise = new Promise<"timeout">((resolve) => {
|
|
|
|
|
timeoutId = setTimeout(() => resolve("timeout"), BUN_INSTALL_TIMEOUT_MS)
|
|
|
|
|
})
|
2026-02-08 15:01:42 +09:00
|
|
|
const exitPromise = proc.exited.then(() => "completed" as const)
|
|
|
|
|
const result = await Promise.race([exitPromise, timeoutPromise])
|
2026-02-08 15:31:32 +09:00
|
|
|
clearTimeout(timeoutId!)
|
2026-02-08 15:01:42 +09:00
|
|
|
|
|
|
|
|
if (result === "timeout") {
|
|
|
|
|
try {
|
|
|
|
|
proc.kill()
|
2026-03-07 15:56:21 -07:00
|
|
|
} catch (err) {
|
|
|
|
|
log("[cli/install] Failed to kill timed out bun install process:", err)
|
2026-02-08 15:01:42 +09:00
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
timedOut: true,
|
2026-03-08 09:15:13 -06:00
|
|
|
error: `bun install timed out after ${BUN_INSTALL_TIMEOUT_SECONDS} seconds. Try running manually: cd "${cacheDir}" && bun i`,
|
2026-02-08 15:01:42 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (proc.exitCode !== 0) {
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
2026-02-08 21:57:34 +09:00
|
|
|
error: `bun install failed with exit code ${proc.exitCode}`,
|
2026-02-08 15:01:42 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { success: true }
|
|
|
|
|
} catch (err) {
|
|
|
|
|
const message = err instanceof Error ? err.message : String(err)
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: `bun install failed: ${message}. Is bun installed? Try: curl -fsSL https://bun.sh/install | bash`,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|