diff --git a/src/cli/sandbox-cli.ts b/src/cli/sandbox-cli.ts index 232a1ebd5..0f7317b8a 100644 --- a/src/cli/sandbox-cli.ts +++ b/src/cli/sandbox-cli.ts @@ -6,55 +6,123 @@ import { } from "../commands/sandbox.js"; import { defaultRuntime } from "../runtime.js"; +// --- Types --- + +type CommandOptions = Record; + +// --- Helpers --- + +const EXAMPLES = { + main: ` +Examples: + clawdbot sandbox list # List all sandbox containers + clawdbot sandbox list --browser # List only browser containers + clawdbot sandbox recreate --all # Recreate all containers + clawdbot sandbox recreate --session main # Recreate specific session + clawdbot sandbox recreate --agent mybot # Recreate agent containers`, + + list: ` +Examples: + clawdbot sandbox list # List all sandbox containers + clawdbot sandbox list --browser # List only browser containers + clawdbot sandbox list --json # JSON output + +Output includes: + • Container name and status (running/stopped) + • Docker image and whether it matches current config + • Age (time since creation) + • Idle time (time since last use) + • Associated session/agent ID`, + + recreate: ` +Examples: + clawdbot sandbox recreate --all # Recreate all containers + clawdbot sandbox recreate --session main # Specific session + clawdbot sandbox recreate --agent mybot # Specific agent (includes sub-agents) + clawdbot sandbox recreate --browser --all # All browser containers only + clawdbot sandbox recreate --all --force # Skip confirmation + +Why use this? + After updating Docker images or sandbox configuration, existing containers + continue running with old settings. This command removes them so they'll be + recreated automatically with current config when next needed. + +Filter options: + --all Remove all sandbox containers + --session Remove container for specific session key + --agent Remove containers for agent (includes agent:id:* variants) + +Modifiers: + --browser Only affect browser containers (not regular sandbox) + --force Skip confirmation prompt`, +}; + +function createRunner( + commandFn: (opts: CommandOptions, runtime: typeof defaultRuntime) => Promise, +) { + return async (opts: CommandOptions) => { + try { + await commandFn(opts, defaultRuntime); + } catch (err) { + defaultRuntime.error(String(err)); + defaultRuntime.exit(1); + } + }; +} + +function normalizeOptions(opts: CommandOptions): CommandOptions { + const normalized: CommandOptions = {}; + for (const [key, value] of Object.entries(opts)) { + normalized[key] = typeof value === "boolean" ? value : value; + } + return normalized; +} + +// --- Registration --- + export function registerSandboxCli(program: Command) { const sandbox = program .command("sandbox") - .description("Manage sandbox containers (Docker-based agent isolation)"); + .description("Manage sandbox containers (Docker-based agent isolation)") + .addHelpText("after", EXAMPLES.main) + .action(() => { + sandbox.help({ error: true }); + }); + + // --- List Command --- sandbox .command("list") .description("List sandbox containers and their status") + .option("--json", "Output result as JSON", false) .option("--browser", "List browser containers only", false) - .option("--json", "Output JSON", false) - .action(async (opts) => { - try { - await sandboxListCommand( + .addHelpText("after", EXAMPLES.list) + .action( + createRunner((opts) => + sandboxListCommand( { browser: Boolean(opts.browser), json: Boolean(opts.json), }, defaultRuntime, - ); - } catch (err) { - defaultRuntime.error(String(err)); - defaultRuntime.exit(1); - } - }); + ), + ), + ); + + // --- Recreate Command --- sandbox .command("recreate") - .description("Recreate sandbox containers (e.g., after image updates)") + .description("Remove containers to force recreation with updated config") .option("--all", "Recreate all sandbox containers", false) .option("--session ", "Recreate container for specific session") .option("--agent ", "Recreate containers for specific agent") .option("--browser", "Only recreate browser containers", false) .option("--force", "Skip confirmation prompt", false) - .addHelpText( - "after", - ` -Examples: - clawd sandbox recreate --all # Recreate all sandbox containers - clawd sandbox recreate --session main # Recreate container for main session - clawd sandbox recreate --agent mybot # Recreate containers for 'mybot' agent - clawd sandbox recreate --browser # Only recreate browser containers - clawd sandbox recreate --all --force # Skip confirmation - -Use this command after updating sandbox images or changing sandbox configuration -to ensure containers use the latest settings.`, - ) - .action(async (opts) => { - try { - await sandboxRecreateCommand( + .addHelpText("after", EXAMPLES.recreate) + .action( + createRunner((opts) => + sandboxRecreateCommand( { all: Boolean(opts.all), session: opts.session as string | undefined, @@ -63,20 +131,7 @@ to ensure containers use the latest settings.`, force: Boolean(opts.force), }, defaultRuntime, - ); - } catch (err) { - defaultRuntime.error(String(err)); - defaultRuntime.exit(1); - } - }); - - // Default action shows list - sandbox.action(async () => { - try { - await sandboxListCommand({ browser: false, json: false }, defaultRuntime); - } catch (err) { - defaultRuntime.error(String(err)); - defaultRuntime.exit(1); - } - }); + ), + ), + ); }