@deep-assistant/agent
Version:
A minimal, public domain AI CLI agent compatible with OpenCode's JSON interface. Bun-only runtime.
97 lines (88 loc) • 2.73 kB
text/typescript
import z from "zod"
import { Global } from "../global"
import { Log } from "../util/log"
import path from "path"
import { NamedError } from "../util/error"
import { readableStreamToText } from "bun"
export namespace BunProc {
const log = Log.create({ service: "bun" })
export async function run(cmd: string[], options?: Bun.SpawnOptions.OptionsObject<any, any, any>) {
log.info("running", {
cmd: [which(), ...cmd],
...options,
})
const result = Bun.spawn([which(), ...cmd], {
...options,
stdout: "pipe",
stderr: "pipe",
env: {
...process.env,
...options?.env,
BUN_BE_BUN: "1",
},
})
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,
})
if (code !== 0) {
throw new Error(`Command failed with exit code ${result.exitCode}`)
}
return result
}
export function which() {
return process.execPath
}
export const InstallFailedError = NamedError.create(
"BunInstallFailedError",
z.object({
pkg: z.string(),
version: z.string(),
}),
)
export async function install(pkg: string, version = "latest") {
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
})
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(
{ pkg, version },
{
cause: e,
},
)
})
parsed.dependencies[pkg] = version
await Bun.write(pkgjson.name!, JSON.stringify(parsed, null, 2))
return mod
}
}