Compare commits

...

4 Commits

View File

@ -1,4 +1,5 @@
import { $ } from "bun" import { $ } from "bun"
import { existsSync } from "fs"
import { homedir } from "os" import { homedir } from "os"
import { dirname, join } from "path" import { dirname, join } from "path"
import { getApiKey } from "./env.ts" import { getApiKey } from "./env.ts"
@ -47,17 +48,16 @@ async function run(cmd: ReturnType<typeof $>, step: string): Promise<void> {
// ── create() helpers (internal) ────────────────────────────────────── // ── create() helpers (internal) ──────────────────────────────────────
/** Check which host source directories exist. */ /** Check which host source directories exist. */
async function hostMounts(home: string): Promise<{ dev: boolean; code: boolean }> { function hostMounts(home: string): { dev: boolean; code: boolean } {
const [dev, code] = await Promise.all([ return {
Bun.file(`${home}/dev`).exists().catch(() => false), dev: existsSync(`${home}/dev`),
Bun.file(`${home}/code`).exists().catch(() => false), code: existsSync(`${home}/code`),
]) }
return { dev, code }
} }
/** Pull the image and start the container in detached mode. */ /** Pull the image and start the container in detached mode. */
async function createContainer(home: string): Promise<void> { async function createContainer(home: string): Promise<void> {
const mounts = await hostMounts(home) const mounts = hostMounts(home)
const args = ["container", "run", "-d", "--name", CONTAINER_NAME, "-m", "4G"] 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.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.code) args.push("--mount", `type=bind,source=${home}/code,target=/host/code,readonly`)
@ -83,7 +83,7 @@ async function installPackages(cached: boolean): Promise<void> {
/** Create symlinks so git worktree absolute paths from the host resolve inside the container. */ /** Create symlinks so git worktree absolute paths from the host resolve inside the container. */
async function createHostSymlinks(home: string): Promise<void> { async function createHostSymlinks(home: string): Promise<void> {
const mounts = await hostMounts(home) const mounts = hostMounts(home)
const cmds = [`mkdir -p '${home}'`, `ln -s /sandlot '${home}/.sandlot'`] const cmds = [`mkdir -p '${home}'`, `ln -s /sandlot '${home}/.sandlot'`]
if (mounts.dev) cmds.push(`ln -s /host/dev '${home}/dev'`) if (mounts.dev) cmds.push(`ln -s /host/dev '${home}/dev'`)
if (mounts.code) cmds.push(`ln -s /host/code '${home}/code'`) if (mounts.code) cmds.push(`ln -s /host/code '${home}/code'`)
@ -163,7 +163,7 @@ async function configureEnvironment(home: string, log?: (msg: string) => void, a
const settingsJson = JSON.stringify(apiKey const settingsJson = JSON.stringify(apiKey
? { apiKeyHelper: "~/.claude/api-key-helper.sh", skipDangerousModePermissionPrompt: true, hooks } ? { apiKeyHelper: "~/.claude/api-key-helper.sh", skipDangerousModePermissionPrompt: true, hooks }
: { skipDangerousModePermissionPrompt: true, hooks }) : { skipDangerousModePermissionPrompt: true, hooks })
const claudeJson = JSON.stringify({ hasCompletedOnboarding: true, effortCalloutDismissed: true }) const claudeJson = JSON.stringify({ hasCompletedOnboarding: true, effortCalloutDismissed: true, projects: { "/": { hasTrustDialogAccepted: true } } })
// Write the helper script to a temp file and copy it in so the key // Write the helper script to a temp file and copy it in so the key
// never appears in a process argument visible in `ps`. // never appears in a process argument visible in `ps`.
@ -268,7 +268,7 @@ export async function status(): Promise<"running" | "stopped" | "missing"> {
/** Launch claude in the container at the given workdir. */ /** 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 }> { export async function claude(workdir: string, opts?: { prompt?: string; print?: string; continue?: boolean }): Promise<{ exitCode: number; output?: string }> {
const cwd = containerPath(workdir) const cwd = containerPath(workdir)
const mounts = await hostMounts(homedir()) const mounts = hostMounts(homedir())
const systemPromptLines = [ const systemPromptLines = [
"You are running inside a sandlot container (Apple Container, ubuntu:24.04).", "You are running inside a sandlot container (Apple Container, ubuntu:24.04).",
`Your working directory is ${cwd}, a git worktree managed by sandlot.`, `Your working directory is ${cwd}, a git worktree managed by sandlot.`,