feat: add sandbox CLI commands for container management

Add 'clawd sandbox list' and 'clawd sandbox recreate' commands to manage
sandbox containers. This fixes the issue where containers continue using
old images/configs after updates.

Problem:
- When sandbox Docker images or configs are updated, existing containers
  keep running with old settings
- Containers are only recreated after 24h inactivity (pruning)
- If agents are used regularly, old containers run indefinitely

Solution:
- 'clawd sandbox list': Show all containers with status, age, and image match
- 'clawd sandbox recreate': Force container removal (recreated on next use)
  - Supports --all, --session, --agent, --browser filters
  - Requires confirmation unless --force is used

Implementation:
- Added helper functions to sandbox.ts (list/remove containers)
- Created sandbox-cli.ts following existing CLI patterns
- Created commands/sandbox.ts with list and recreate logic
- Integrated into program.ts

Use case: After updating sandbox images or changing sandbox config,
run 'clawd sandbox recreate --all' to ensure fresh containers.
This commit is contained in:
sheeek
2026-01-09 09:40:14 +01:00
committed by Peter Steinberger
parent ae6f268987
commit 5e78d5a21f
4 changed files with 455 additions and 0 deletions

82
src/cli/sandbox-cli.ts Normal file
View File

@@ -0,0 +1,82 @@
import type { Command } from "commander";
import {
sandboxListCommand,
sandboxRecreateCommand,
} from "../commands/sandbox.js";
import { defaultRuntime } from "../runtime.js";
export function registerSandboxCli(program: Command) {
const sandbox = program
.command("sandbox")
.description("Manage sandbox containers (Docker-based agent isolation)");
sandbox
.command("list")
.description("List sandbox containers and their status")
.option("--browser", "List browser containers only", false)
.option("--json", "Output JSON", false)
.action(async (opts) => {
try {
await sandboxListCommand(
{
browser: Boolean(opts.browser),
json: Boolean(opts.json),
},
defaultRuntime,
);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
sandbox
.command("recreate")
.description("Recreate sandbox containers (e.g., after image updates)")
.option("--all", "Recreate all sandbox containers", false)
.option("--session <key>", "Recreate container for specific session")
.option("--agent <id>", "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(
{
all: Boolean(opts.all),
session: opts.session as string | undefined,
agent: opts.agent as string | undefined,
browser: Boolean(opts.browser),
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);
}
});
}