refactor(sandbox): use extracted display and formatter modules

Update sandbox.ts to import and use functions from:
- sandbox-display.ts for all UI output
- sandbox-formatters.ts for data formatting

Removes 141 LOC of display/formatting code from sandbox.ts,
reducing it from 351 to 210 LOC (-40%).

Core business logic now clearer and more focused.
This commit is contained in:
sheeek
2026-01-09 10:10:32 +01:00
committed by Peter Steinberger
parent 81c55be19b
commit 1c757ae35e

View File

@@ -9,6 +9,13 @@ import {
removeSandboxContainer,
} from "../agents/sandbox.js";
import type { RuntimeEnv } from "../runtime.js";
import {
displayBrowsers,
displayContainers,
displayRecreatePreview,
displayRecreateResult,
displaySummary,
} from "./sandbox-display.js";
// --- Types ---
@@ -74,7 +81,7 @@ export async function sandboxRecreateCommand(
return;
}
displayRecreatePreview(filtered, runtime);
displayRecreatePreview(filtered.containers, filtered.browsers, runtime);
if (!opts.force && !(await confirmRecreate())) {
runtime.log("Cancelled.");
@@ -138,118 +145,6 @@ function createAgentMatcher(agentId: string) {
item.sessionKey.startsWith(`${agentPrefix}:`);
}
// --- Display Functions ---
function displayContainers(
containers: SandboxContainerInfo[],
runtime: RuntimeEnv,
): void {
if (containers.length === 0) {
runtime.log("No sandbox containers found.");
return;
}
runtime.log("\n📦 Sandbox Containers:\n");
for (const container of containers) {
runtime.log(` ${container.containerName}`);
runtime.log(` Status: ${formatStatus(container.running)}`);
runtime.log(` Image: ${container.image} ${formatImageMatch(container.imageMatch)}`);
runtime.log(` Age: ${formatAge(Date.now() - container.createdAtMs)}`);
runtime.log(` Idle: ${formatAge(Date.now() - container.lastUsedAtMs)}`);
runtime.log(` Session: ${container.sessionKey}`);
runtime.log("");
}
}
function displayBrowsers(
browsers: SandboxBrowserInfo[],
runtime: RuntimeEnv,
): void {
if (browsers.length === 0) {
runtime.log("No sandbox browser containers found.");
return;
}
runtime.log("\n🌐 Sandbox Browser Containers:\n");
for (const browser of browsers) {
runtime.log(` ${browser.containerName}`);
runtime.log(` Status: ${formatStatus(browser.running)}`);
runtime.log(` Image: ${browser.image} ${formatImageMatch(browser.imageMatch)}`);
runtime.log(` CDP: ${browser.cdpPort}`);
if (browser.noVncPort) {
runtime.log(` noVNC: ${browser.noVncPort}`);
}
runtime.log(` Age: ${formatAge(Date.now() - browser.createdAtMs)}`);
runtime.log(` Idle: ${formatAge(Date.now() - browser.lastUsedAtMs)}`);
runtime.log(` Session: ${browser.sessionKey}`);
runtime.log("");
}
}
function displaySummary(
containers: SandboxContainerInfo[],
browsers: SandboxBrowserInfo[],
runtime: RuntimeEnv,
): void {
const totalCount = containers.length + browsers.length;
const runningCount = countRunning(containers) + countRunning(browsers);
const mismatchCount = countMismatches(containers) + countMismatches(browsers);
runtime.log(`Total: ${totalCount} (${runningCount} running)`);
if (mismatchCount > 0) {
runtime.log(
`\n⚠ ${mismatchCount} container(s) with image mismatch detected.`,
);
runtime.log(
` Run 'clawd sandbox recreate --all' to update all containers.`,
);
}
}
function displayRecreatePreview(
filtered: FilteredContainers,
runtime: RuntimeEnv,
): void {
runtime.log("\nContainers to be recreated:\n");
if (filtered.containers.length > 0) {
runtime.log("📦 Sandbox Containers:");
for (const container of filtered.containers) {
runtime.log(
` - ${container.containerName} (${formatSimpleStatus(container.running)})`,
);
}
}
if (filtered.browsers.length > 0) {
runtime.log("\n🌐 Browser Containers:");
for (const browser of filtered.browsers) {
runtime.log(
` - ${browser.containerName} (${formatSimpleStatus(browser.running)})`,
);
}
}
const total = filtered.containers.length + filtered.browsers.length;
runtime.log(`\nTotal: ${total} container(s)`);
}
function displayRecreateResult(
result: { successCount: number; failCount: number },
runtime: RuntimeEnv,
): void {
runtime.log(
`\nDone: ${result.successCount} removed, ${result.failCount} failed`,
);
if (result.successCount > 0) {
runtime.log(
"\nContainers will be automatically recreated when the agent is next used.",
);
}
}
// --- Container Operations ---
async function confirmRecreate(): Promise<boolean> {
@@ -313,39 +208,3 @@ async function removeContainer(
return { success: false };
}
}
// --- Formatting Helpers ---
function formatStatus(running: boolean): string {
return running ? "🟢 running" : "⚫ stopped";
}
function formatSimpleStatus(running: boolean): string {
return running ? "running" : "stopped";
}
function formatImageMatch(matches: boolean): string {
return matches ? "✓" : "⚠️ mismatch";
}
function formatAge(ms: number): string {
const seconds = Math.floor(ms / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (days > 0) return `${days}d ${hours % 24}h`;
if (hours > 0) return `${hours}h ${minutes % 60}m`;
if (minutes > 0) return `${minutes}m`;
return `${seconds}s`;
}
// --- Counting Helpers ---
function countRunning(items: ContainerItem[]): number {
return items.filter((item) => item.running).length;
}
function countMismatches(items: ContainerItem[]): number {
return items.filter((item) => !item.imageMatch).length;
}