import { imageExists, isPodmanAvailable, MOZART_DEV_IMAGE, MOZART_IMAGE } from "./podman.ts"; import { PROJECT_ROOT } from "./root.ts"; type Platform = "darwin" | "linux" | "win32"; function getPlatform(): Platform { const p = process.platform; if (p === "darwin" || p === "linux" || p === "win32") return p; throw new Error(`Unsupported platform: ${p}`); } async function run(cmd: string[], opts?: { inherit?: boolean }): Promise<{ code: number; stdout: string }> { const proc = Bun.spawn(cmd, { stdout: opts?.inherit ? "inherit" : "pipe", stderr: opts?.inherit ? "inherit" : "pipe", }); const code = await proc.exited; let stdout = ""; if (!opts?.inherit && proc.stdout && typeof proc.stdout !== "number") { stdout = await new Response(proc.stdout).text(); } return { code, stdout }; } async function commandExists(name: string): Promise { try { const { code } = await run(["which", name]); return code === 0; } catch { return false; } } async function installPodman(platform: Platform): Promise { switch (platform) { case "darwin": { if (!(await commandExists("brew"))) { console.log(" Homebrew not found. Installing Homebrew first..."); const { code } = await run( [ "bash", "-c", '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"', ], { inherit: true }, ); if (code !== 0) throw new Error("Failed to install Homebrew"); } console.log(" → brew install podman"); const { code } = await run(["brew", "install", "podman"], { inherit: true }); if (code !== 0) throw new Error("Failed to install Podman via Homebrew"); break; } case "linux": { if (await commandExists("apt")) { console.log(" → sudo apt install -y podman"); const { code } = await run(["sudo", "apt", "install", "-y", "podman"], { inherit: true }); if (code !== 0) throw new Error("Failed to install Podman via apt"); } else if (await commandExists("dnf")) { console.log(" → sudo dnf install -y podman"); const { code } = await run(["sudo", "dnf", "install", "-y", "podman"], { inherit: true }); if (code !== 0) throw new Error("Failed to install Podman via dnf"); } else { throw new Error( "No supported package manager found (apt or dnf). Install Podman manually: https://podman.io/docs/installation", ); } break; } case "win32": { if (await commandExists("choco")) { console.log(" → choco install podman -y"); const { code } = await run(["choco", "install", "podman", "-y"], { inherit: true }); if (code !== 0) throw new Error("Failed to install Podman via Chocolatey"); } else { throw new Error("Chocolatey not found. Install Podman manually: https://podman.io/docs/installation"); } break; } } } async function needsMachine(platform: Platform): Promise { return platform === "darwin" || platform === "win32"; } async function isMachineRunning(): Promise { try { const { code, stdout } = await run(["podman", "machine", "inspect", "--format", "{{.State}}"]); return code === 0 && stdout.trim().toLowerCase() === "running"; } catch { return false; } } async function machineExists(): Promise { try { const { code } = await run(["podman", "machine", "inspect"]); return code === 0; } catch { return false; } } export async function runSetup(): Promise { const platform = getPlatform(); const platformName = platform === "darwin" ? "macOS" : platform === "linux" ? "Linux" : "Windows"; console.log("[1/4] Checking for Podman..."); if (await isPodmanAvailable()) { const { stdout } = await run(["podman", "--version"]); console.log(` Podman found: ${stdout.trim()}`); } else { console.log(` Podman not found. Installing for ${platformName}...`); await installPodman(platform); if (!(await isPodmanAvailable())) { throw new Error( "Podman installation completed but podman binary not found in PATH. You may need to restart your shell.", ); } console.log(" Podman installed successfully."); } if (await needsMachine(platform)) { console.log("[2/4] Setting up Podman machine..."); if (!(await machineExists())) { console.log(" → podman machine init"); const { code } = await run(["podman", "machine", "init"], { inherit: true }); if (code !== 0) throw new Error("Failed to initialize Podman machine"); } else { console.log(" Machine already initialized."); } console.log("[3/4] Starting Podman machine..."); if (!(await isMachineRunning())) { console.log(" → podman machine start"); const { code } = await run(["podman", "machine", "start"], { inherit: true }); if (code !== 0) throw new Error("Failed to start Podman machine"); } else { console.log(" Machine already running."); } } else { console.log("[2/4] Podman machine not needed on Linux."); console.log("[3/4] Skipping machine start."); } console.log("[4/5] Building Mozart agent image..."); if (await imageExists(MOZART_IMAGE)) { console.log(` Image ${MOZART_IMAGE} already exists.`); console.log(" (Run 'mozart setup --rebuild' to force rebuild.)"); } else { console.log(` → podman build -t ${MOZART_IMAGE} .`); const proc = Bun.spawn(["podman", "build", "-t", MOZART_IMAGE, "-f", "Containerfile", "."], { cwd: PROJECT_ROOT, stdout: "inherit", stderr: "inherit", }); const code = await proc.exited; if (code !== 0) throw new Error("Failed to build Mozart agent image"); } console.log("[5/5] Building dev base image..."); if (await imageExists(MOZART_DEV_IMAGE)) { console.log(` Image ${MOZART_DEV_IMAGE} already exists.`); } else { console.log(` → podman build -t ${MOZART_DEV_IMAGE} -f Containerfile.dev .`); const proc = Bun.spawn(["podman", "build", "-t", MOZART_DEV_IMAGE, "-f", "Containerfile.dev", "."], { cwd: PROJECT_ROOT, stdout: "inherit", stderr: "inherit", }); const code = await proc.exited; if (code !== 0) throw new Error("Failed to build dev base image"); } console.log("\n✓ Mozart is ready. Run 'mozart up' to start your agents."); } export async function runSetupRebuild(): Promise { console.log(`Rebuilding Mozart agent image...`); console.log(`→ podman build -t ${MOZART_IMAGE} --no-cache .`); const proc = Bun.spawn(["podman", "build", "-t", MOZART_IMAGE, "--no-cache", "-f", "Containerfile", "."], { cwd: PROJECT_ROOT, stdout: "inherit", stderr: "inherit", }); const code = await proc.exited; if (code !== 0) throw new Error("Failed to rebuild Mozart agent image"); console.log(`Rebuilding dev image...`); console.log(`→ podman build -t ${MOZART_DEV_IMAGE} --no-cache -f Containerfile.dev .`); const baseProc = Bun.spawn( ["podman", "build", "-t", MOZART_DEV_IMAGE, "--no-cache", "-f", "Containerfile.dev", "."], { cwd: PROJECT_ROOT, stdout: "inherit", stderr: "inherit" }, ); const baseCode = await baseProc.exited; if (baseCode !== 0) throw new Error("Failed to rebuild dev base image"); console.log("✓ Images rebuilt successfully."); }