cloudaxe-opencode/packages/opencode/src/bun/index.ts

94 lines
2.6 KiB
TypeScript
Raw Normal View History

import z from "zod"
2025-06-13 03:39:52 +00:00
import { Global } from "../global"
2025-05-31 18:41:00 +00:00
import { Log } from "../util/log"
2025-06-13 03:39:52 +00:00
import path from "path"
import { NamedError } from "../util/error"
import { readableStreamToText } from "bun"
2025-06-13 03:39:52 +00:00
2025-05-28 16:53:22 +00:00
export namespace BunProc {
2025-05-31 18:41:00 +00:00
const log = Log.create({ service: "bun" })
2025-05-28 16:53:22 +00:00
export async function run(cmd: string[], options?: Bun.SpawnOptions.OptionsObject<any, any, any>) {
2025-05-28 16:53:22 +00:00
log.info("running", {
2025-06-12 15:00:37 +00:00
cmd: [which(), ...cmd],
...options,
2025-05-31 18:41:00 +00:00
})
2025-06-24 18:59:27 +00:00
const result = Bun.spawn([which(), ...cmd], {
2025-05-28 16:53:22 +00:00
...options,
2025-06-12 17:39:03 +00:00
stdout: "pipe",
stderr: "pipe",
2025-05-28 16:53:22 +00:00
env: {
...process.env,
...options?.env,
2025-06-08 16:46:26 +00:00
BUN_BE_BUN: "1",
2025-05-28 16:53:22 +00:00
},
2025-05-31 18:41:00 +00:00
})
const code = await result.exited
const stdout = result.stdout
? typeof result.stdout === "number"
? result.stdout
: await readableStreamToText(result.stdout)
: undefined
const stderr = result.stderr
? typeof result.stderr === "number"
? result.stderr
: await readableStreamToText(result.stderr)
: undefined
log.info("done", {
code,
stdout,
stderr,
})
2025-06-08 16:46:26 +00:00
if (code !== 0) {
2025-05-31 18:41:00 +00:00
throw new Error(`Command failed with exit code ${result.exitCode}`)
}
return result
2025-05-28 16:53:22 +00:00
}
2025-06-12 15:00:37 +00:00
export function which() {
return process.execPath
2025-06-12 15:00:37 +00:00
}
2025-06-13 03:39:52 +00:00
export const InstallFailedError = NamedError.create(
"BunInstallFailedError",
z.object({
pkg: z.string(),
version: z.string(),
}),
)
2025-06-13 03:39:52 +00:00
export async function install(pkg: string, version = "latest") {
2025-06-14 22:43:58 +00:00
const mod = path.join(Global.Path.cache, "node_modules", pkg)
const pkgjson = Bun.file(path.join(Global.Path.cache, "package.json"))
const parsed = await pkgjson.json().catch(async () => {
const result = { dependencies: {} }
await Bun.write(pkgjson.name!, JSON.stringify(result, null, 2))
return result
})
2025-06-14 22:43:58 +00:00
if (parsed.dependencies[pkg] === version) return mod
// Build command arguments
const args = ["add", "--force", "--exact", "--cwd", Global.Path.cache, pkg + "@" + version]
// Let Bun handle registry resolution:
// - If .npmrc files exist, Bun will use them automatically
// - If no .npmrc files exist, Bun will default to https://registry.npmjs.org
// - No need to pass --registry flag
log.info("installing package using Bun's default registry resolution", { pkg, version })
await BunProc.run(args, {
cwd: Global.Path.cache,
}).catch((e) => {
throw new InstallFailedError(
2025-06-14 22:43:58 +00:00
{ pkg, version },
{
cause: e,
},
)
})
parsed.dependencies[pkg] = version
await Bun.write(pkgjson.name!, JSON.stringify(parsed, null, 2))
2025-06-14 22:43:58 +00:00
return mod
2025-06-13 03:39:52 +00:00
}
2025-05-28 16:53:22 +00:00
}