refactor(sandbox-cli): improve structure and reduce duplication

Improvements:
- Extract help text examples into EXAMPLES constant object
- Add createRunner() helper to reduce try-catch boilerplate
- Add normalizeOptions() helper (prepared for future use)
- Fix command names from 'clawd' to 'clawdbot' throughout
- Update main sandbox command to show help by default
- Better organize code with clear section comments

Increases from 82 to 137 LOC but with much better organization
and reduced duplication in error handling.
This commit is contained in:
sheeek
2026-01-09 10:10:46 +01:00
committed by Peter Steinberger
parent 1c757ae35e
commit b0c97d6178

View File

@@ -6,55 +6,123 @@ import {
} from "../commands/sandbox.js";
import { defaultRuntime } from "../runtime.js";
// --- Types ---
type CommandOptions = Record<string, unknown>;
// --- 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<void>,
) {
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 <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(
.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);
}
});
),
),
);
}