add vm create and vm start commands, refactor ensure to delegate to them

This commit is contained in:
Chris Wanstrath 2026-02-19 19:51:33 -08:00
parent e23e84655d
commit 9aac8298c9
2 changed files with 58 additions and 11 deletions

View File

@ -602,6 +602,33 @@ program
const vmCmd = program.command("vm").description("Manage the sandlot VM")
vmCmd
.command("create")
.description("Create and provision the VM")
.action(async () => {
const spin = spinner("Creating VM")
try {
await vm.create((msg) => { spin.text = msg })
spin.succeed("VM created")
} catch (err) {
spin.fail(String((err as Error).message ?? err))
process.exit(1)
}
})
vmCmd
.command("start")
.description("Start the VM")
.action(async () => {
try {
await vm.start()
console.log("✔ VM started")
} catch (err) {
console.error(`${(err as Error).message ?? err}`)
process.exit(1)
}
})
vmCmd
.command("shell")
.description("Open a shell in the VM")

View File

@ -25,22 +25,15 @@ function requireContainer(): void {
}
}
/** Ensure the sandlot container exists and is running. Creates and provisions on first use. */
export async function ensure(log?: (msg: string) => void): Promise<void> {
/** Create and provision the container from scratch. Fails if it already exists. */
export async function create(log?: (msg: string) => void): Promise<void> {
requireContainer()
// Ensure the container daemon is running
await $`container system start`.nothrow().quiet()
const s = await status()
if (s === "running") return
if (s === "stopped") {
await $`container start ${CONTAINER_NAME}`.quiet()
return
if (s !== "missing") {
throw new Error("Container already exists. Use 'sandlot vm destroy' first to recreate it.")
}
// Create from scratch
const home = homedir()
log?.("Pulling image & creating container")
await $`container run -d --name ${CONTAINER_NAME} -m 4G --mount type=bind,source=${home}/dev,target=/host,readonly -v ${home}/.sandlot:/sandlot ubuntu:24.04 sleep infinity`.quiet()
@ -112,6 +105,33 @@ echo '${claudeJson}' > ~/.claude.json
`}`.quiet()
}
/** Start a stopped container. */
export async function start(): Promise<void> {
requireContainer()
const s = await status()
if (s === "running") return
if (s === "missing") throw new Error("Container does not exist. Use 'sandlot vm create' first.")
await $`container start ${CONTAINER_NAME}`.quiet()
}
/** Ensure the sandlot container exists and is running. Creates and provisions on first use. */
export async function ensure(log?: (msg: string) => void): Promise<void> {
requireContainer()
// Ensure the container daemon is running
await $`container system start`.nothrow().quiet()
const s = await status()
if (s === "running") return
if (s === "stopped") {
await start()
return
}
await create(log)
}
/** Check container status. */
export async function status(): Promise<"running" | "stopped" | "missing"> {
const result = await $`container list --format json --all`.nothrow().quiet().text()