diff --git a/src/cli.ts b/src/cli.ts index 8e862eb..ec9e962 100755 --- a/src/cli.ts +++ b/src/cli.ts @@ -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") diff --git a/src/vm.ts b/src/vm.ts index 5672e27..6708369 100644 --- a/src/vm.ts +++ b/src/vm.ts @@ -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 { +/** Create and provision the container from scratch. Fails if it already exists. */ +export async function create(log?: (msg: string) => void): Promise { 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 { + 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 { + 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()