/** * pi-sandbox extension entry point. * * Registers the sandbox_run tool and the /sandbox-build and /sandbox-status * commands. Networking is off by default and each network-enabled run requires * one-run human approval. */ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { buildSandboxImage, formatStatus, getSandboxStatus, resolveDockerfileDir } from "./image.ts"; import { registerSandboxTool } from "./tool.ts"; import { IMAGE_TAG } from "./types.ts"; export default function (pi: ExtensionAPI) { // The LLM-facing execution tool. registerSandboxTool(pi); // Build / rebuild the sandbox image from the bundled Dockerfile. pi.registerCommand("sandbox-build", { description: `Build or rebuild the sandbox image (${IMAGE_TAG}) from the bundled Dockerfile`, handler: async (_args, ctx) => { ctx.ui.setStatus("sandbox", ctx.ui.theme.fg("accent", `Building ${IMAGE_TAG}...`)); try { const { tag } = await buildSandboxImage({ onStatus: (line) => ctx.ui.setStatus("sandbox", ctx.ui.theme.fg("accent", line)), }); ctx.ui.notify(`Built sandbox image ${tag}`, "info"); } catch (err) { ctx.ui.notify(`Image build failed: ${err instanceof Error ? err.message : err}`, "error"); } finally { ctx.ui.setStatus("sandbox", undefined); } }, }); // Inspect Docker + sandbox-image readiness. pi.registerCommand("sandbox-status", { description: "Check Docker CLI, daemon, and sandbox image readiness", handler: async (_args, ctx) => { const status = await getSandboxStatus(); ctx.ui.notify(formatStatus(status), status.ready ? "info" : "warning"); }, }); // Surface the Dockerfile path for debugging / manual builds. pi.registerCommand("sandbox-dockerfile", { description: "Print the path to the bundled sandbox Dockerfile", handler: async (_args, ctx) => { ctx.ui.notify(`Dockerfile: ${resolveDockerfileDir()}`, "info"); }, }); }