From 65603df7d210a014bb25e54799571812d809d726 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Tue, 24 Feb 2026 23:44:22 -0800 Subject: [PATCH 1/2] Make hostMounts sync, drop readonly mounts --- src/vm.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vm.ts b/src/vm.ts index 07fa03d..b74dfc3 100644 --- a/src/vm.ts +++ b/src/vm.ts @@ -1,4 +1,5 @@ import { $ } from "bun" +import { existsSync } from "fs" import { homedir } from "os" import { dirname, join } from "path" import { getApiKey } from "./env.ts" @@ -47,20 +48,19 @@ async function run(cmd: ReturnType, step: string): Promise { // ── create() helpers (internal) ────────────────────────────────────── /** Check which host source directories exist. */ -async function hostMounts(home: string): Promise<{ dev: boolean; code: boolean }> { - const [dev, code] = await Promise.all([ - Bun.file(`${home}/dev`).exists().catch(() => false), - Bun.file(`${home}/code`).exists().catch(() => false), - ]) - return { dev, code } +function hostMounts(home: string): { dev: boolean; code: boolean } { + return { + dev: existsSync(`${home}/dev`), + code: existsSync(`${home}/code`), + } } /** Pull the image and start the container in detached mode. */ async function createContainer(home: string): Promise { - const mounts = await hostMounts(home) + const mounts = hostMounts(home) const args = ["container", "run", "-d", "--name", CONTAINER_NAME, "-m", "4G"] - if (mounts.dev) args.push("--mount", `type=bind,source=${home}/dev,target=/host/dev,readonly`) - if (mounts.code) args.push("--mount", `type=bind,source=${home}/code,target=/host/code,readonly`) + if (mounts.dev) args.push("--mount", `type=bind,source=${home}/dev,target=/host/dev`) + if (mounts.code) args.push("--mount", `type=bind,source=${home}/code,target=/host/code`) args.push("-v", `${home}/.sandlot:/sandlot`, "ubuntu:24.04", "sleep", "infinity") const prepared = DEBUG ? $`${args}`.nothrow() : $`${args}`.nothrow().quiet() const result = await prepared @@ -83,7 +83,7 @@ async function installPackages(cached: boolean): Promise { /** Create symlinks so git worktree absolute paths from the host resolve inside the container. */ async function createHostSymlinks(home: string): Promise { - const mounts = await hostMounts(home) + const mounts = hostMounts(home) const cmds = [`mkdir -p '${home}'`, `ln -s /sandlot '${home}/.sandlot'`] if (mounts.dev) cmds.push(`ln -s /host/dev '${home}/dev'`) if (mounts.code) cmds.push(`ln -s /host/code '${home}/code'`) @@ -268,13 +268,13 @@ export async function status(): Promise<"running" | "stopped" | "missing"> { /** Launch claude in the container at the given workdir. */ export async function claude(workdir: string, opts?: { prompt?: string; print?: string; continue?: boolean }): Promise<{ exitCode: number; output?: string }> { const cwd = containerPath(workdir) - const mounts = await hostMounts(homedir()) + const mounts = hostMounts(homedir()) const systemPromptLines = [ "You are running inside a sandlot container (Apple Container, ubuntu:24.04).", `Your working directory is ${cwd}, a git worktree managed by sandlot.`, ] - if (mounts.dev) systemPromptLines.push("The host's ~/dev is mounted read-only at /host/dev.") - if (mounts.code) systemPromptLines.push("The host's ~/code is mounted read-only at /host/code.") + if (mounts.dev) systemPromptLines.push("The host's ~/dev is mounted at /host/dev.") + if (mounts.code) systemPromptLines.push("The host's ~/code is mounted at /host/code.") systemPromptLines.push( "The host's ~/.sandlot is mounted at /sandlot.", "Bun is installed at ~/.local/bin/bun. Use bun instead of node/npm.", From aab4f35c89183572b998a55ff3deca6042381a89 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Wed, 25 Feb 2026 12:28:16 -0800 Subject: [PATCH 2/2] Mount host dirs read-only in container --- src/vm.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vm.ts b/src/vm.ts index b74dfc3..35f7a51 100644 --- a/src/vm.ts +++ b/src/vm.ts @@ -59,8 +59,8 @@ function hostMounts(home: string): { dev: boolean; code: boolean } { async function createContainer(home: string): Promise { const mounts = hostMounts(home) const args = ["container", "run", "-d", "--name", CONTAINER_NAME, "-m", "4G"] - if (mounts.dev) args.push("--mount", `type=bind,source=${home}/dev,target=/host/dev`) - if (mounts.code) args.push("--mount", `type=bind,source=${home}/code,target=/host/code`) + if (mounts.dev) args.push("--mount", `type=bind,source=${home}/dev,target=/host/dev,readonly`) + if (mounts.code) args.push("--mount", `type=bind,source=${home}/code,target=/host/code,readonly`) args.push("-v", `${home}/.sandlot:/sandlot`, "ubuntu:24.04", "sleep", "infinity") const prepared = DEBUG ? $`${args}`.nothrow() : $`${args}`.nothrow().quiet() const result = await prepared @@ -273,8 +273,8 @@ export async function claude(workdir: string, opts?: { prompt?: string; print?: "You are running inside a sandlot container (Apple Container, ubuntu:24.04).", `Your working directory is ${cwd}, a git worktree managed by sandlot.`, ] - if (mounts.dev) systemPromptLines.push("The host's ~/dev is mounted at /host/dev.") - if (mounts.code) systemPromptLines.push("The host's ~/code is mounted at /host/code.") + if (mounts.dev) systemPromptLines.push("The host's ~/dev is mounted read-only at /host/dev.") + if (mounts.code) systemPromptLines.push("The host's ~/code is mounted read-only at /host/code.") systemPromptLines.push( "The host's ~/.sandlot is mounted at /sandlot.", "Bun is installed at ~/.local/bin/bun. Use bun instead of node/npm.",