Files
clawdbot/src/agents/sandbox/prune.ts
Peter Steinberger c379191f80 chore: migrate to oxlint and oxfmt
Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
2026-01-14 15:02:19 +00:00

96 lines
3.0 KiB
TypeScript

import { stopBrowserBridgeServer } from "../../browser/bridge-server.js";
import { defaultRuntime } from "../../runtime.js";
import { BROWSER_BRIDGES } from "./browser-bridges.js";
import { dockerContainerState, execDocker } from "./docker.js";
import {
readBrowserRegistry,
readRegistry,
removeBrowserRegistryEntry,
removeRegistryEntry,
} from "./registry.js";
import type { SandboxConfig } from "./types.js";
let lastPruneAtMs = 0;
async function pruneSandboxContainers(cfg: SandboxConfig) {
const now = Date.now();
const idleHours = cfg.prune.idleHours;
const maxAgeDays = cfg.prune.maxAgeDays;
if (idleHours === 0 && maxAgeDays === 0) return;
const registry = await readRegistry();
for (const entry of registry.entries) {
const idleMs = now - entry.lastUsedAtMs;
const ageMs = now - entry.createdAtMs;
if (
(idleHours > 0 && idleMs > idleHours * 60 * 60 * 1000) ||
(maxAgeDays > 0 && ageMs > maxAgeDays * 24 * 60 * 60 * 1000)
) {
try {
await execDocker(["rm", "-f", entry.containerName], {
allowFailure: true,
});
} catch {
// ignore prune failures
} finally {
await removeRegistryEntry(entry.containerName);
}
}
}
}
async function pruneSandboxBrowsers(cfg: SandboxConfig) {
const now = Date.now();
const idleHours = cfg.prune.idleHours;
const maxAgeDays = cfg.prune.maxAgeDays;
if (idleHours === 0 && maxAgeDays === 0) return;
const registry = await readBrowserRegistry();
for (const entry of registry.entries) {
const idleMs = now - entry.lastUsedAtMs;
const ageMs = now - entry.createdAtMs;
if (
(idleHours > 0 && idleMs > idleHours * 60 * 60 * 1000) ||
(maxAgeDays > 0 && ageMs > maxAgeDays * 24 * 60 * 60 * 1000)
) {
try {
await execDocker(["rm", "-f", entry.containerName], {
allowFailure: true,
});
} catch {
// ignore prune failures
} finally {
await removeBrowserRegistryEntry(entry.containerName);
const bridge = BROWSER_BRIDGES.get(entry.sessionKey);
if (bridge?.containerName === entry.containerName) {
await stopBrowserBridgeServer(bridge.bridge.server).catch(() => undefined);
BROWSER_BRIDGES.delete(entry.sessionKey);
}
}
}
}
}
export async function maybePruneSandboxes(cfg: SandboxConfig) {
const now = Date.now();
if (now - lastPruneAtMs < 5 * 60 * 1000) return;
lastPruneAtMs = now;
try {
await pruneSandboxContainers(cfg);
await pruneSandboxBrowsers(cfg);
} catch (error) {
const message =
error instanceof Error
? error.message
: typeof error === "string"
? error
: JSON.stringify(error);
defaultRuntime.error?.(`Sandbox prune failed: ${message ?? "unknown error"}`);
}
}
export async function ensureDockerContainerIsRunning(containerName: string) {
const state = await dockerContainerState(containerName);
if (state.exists && !state.running) {
await execDocker(["start", containerName]);
}
}