mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 03:45:46 -06:00
docs: document sandbox lifecycle registry
This commit is contained in:
@@ -13,6 +13,9 @@ import {
|
||||
} from "./registry.js";
|
||||
import { resolveSandboxAgentId } from "./shared.js";
|
||||
|
||||
/**
|
||||
* CLI-facing sandbox management helpers for listing and removing runtime/browser containers.
|
||||
*/
|
||||
export type SandboxContainerInfo = SandboxRegistryEntry & {
|
||||
running: boolean;
|
||||
imageMatch: boolean;
|
||||
@@ -32,6 +35,7 @@ function toBrowserDockerRuntimeEntry(entry: SandboxBrowserRegistryEntry): Sandbo
|
||||
};
|
||||
}
|
||||
|
||||
/** Lists registered sandbox containers with live backend status and config-label match state. */
|
||||
export async function listSandboxContainers(): Promise<SandboxContainerInfo[]> {
|
||||
const config = getRuntimeConfig();
|
||||
const registry = await readRegistry();
|
||||
@@ -65,6 +69,7 @@ export async function listSandboxContainers(): Promise<SandboxContainerInfo[]> {
|
||||
return results;
|
||||
}
|
||||
|
||||
/** Lists registered browser sandbox containers with live Docker status. */
|
||||
export async function listSandboxBrowsers(): Promise<SandboxBrowserInfo[]> {
|
||||
const config = getRuntimeConfig();
|
||||
const registry = await readBrowserRegistry();
|
||||
@@ -88,6 +93,7 @@ export async function listSandboxBrowsers(): Promise<SandboxBrowserInfo[]> {
|
||||
return results;
|
||||
}
|
||||
|
||||
/** Removes one sandbox container from its backend and registry. */
|
||||
export async function removeSandboxContainer(containerName: string): Promise<void> {
|
||||
const config = getRuntimeConfig();
|
||||
const registry = await readRegistry();
|
||||
@@ -103,6 +109,7 @@ export async function removeSandboxContainer(containerName: string): Promise<voi
|
||||
await removeRegistryEntry(containerName);
|
||||
}
|
||||
|
||||
/** Removes one browser sandbox container, registry entry, and any in-process bridge server. */
|
||||
export async function removeSandboxBrowserContainer(containerName: string): Promise<void> {
|
||||
const config = getRuntimeConfig();
|
||||
const registry = await readBrowserRegistry();
|
||||
|
||||
@@ -12,6 +12,12 @@ import {
|
||||
} from "./constants.js";
|
||||
import { hashTextSha256 } from "./hash.js";
|
||||
|
||||
/**
|
||||
* Persistent sandbox registry storage for runtime and browser containers.
|
||||
*
|
||||
* Entries are sharded by container-name hash to avoid a single hot JSON file while preserving
|
||||
* migration support for older monolithic registry files.
|
||||
*/
|
||||
export type SandboxRegistryEntry = {
|
||||
containerName: string;
|
||||
backendId?: string;
|
||||
@@ -122,6 +128,7 @@ async function readLegacyRegistryFile(registryPath: string): Promise<RegistryFil
|
||||
}
|
||||
}
|
||||
|
||||
/** Reads all registered sandbox runtime containers from the sharded registry. */
|
||||
export async function readRegistry(): Promise<SandboxRegistry> {
|
||||
const entries = await readShardedEntries<SandboxRegistryEntry>(SANDBOX_CONTAINERS_DIR);
|
||||
return {
|
||||
@@ -139,6 +146,8 @@ async function withEntryLock<T>(
|
||||
fn: () => Promise<T>,
|
||||
): Promise<T> {
|
||||
const entryPath = shardedEntryFilePath(dir, containerName);
|
||||
// Entry-level locks keep independent container updates concurrent while serializing writes for
|
||||
// the same container hash path.
|
||||
const lock = await acquireSessionWriteLock({
|
||||
sessionFile: entryPath,
|
||||
allowReentrant: false,
|
||||
@@ -280,6 +289,7 @@ function legacyRegistryTargets(): LegacyRegistryTarget[] {
|
||||
];
|
||||
}
|
||||
|
||||
/** Inspects old monolithic registry files without mutating them. */
|
||||
export async function inspectLegacySandboxRegistryFiles(): Promise<
|
||||
LegacySandboxRegistryInspection[]
|
||||
> {
|
||||
@@ -307,6 +317,7 @@ export async function inspectLegacySandboxRegistryFiles(): Promise<
|
||||
return inspections;
|
||||
}
|
||||
|
||||
/** Migrates old monolithic registry files into sharded entry files when present. */
|
||||
export async function migrateLegacySandboxRegistryFiles(): Promise<
|
||||
LegacySandboxRegistryMigrationResult[]
|
||||
> {
|
||||
@@ -317,6 +328,7 @@ export async function migrateLegacySandboxRegistryFiles(): Promise<
|
||||
return results;
|
||||
}
|
||||
|
||||
/** Reads one registered sandbox runtime container by container name. */
|
||||
export async function readRegistryEntry(
|
||||
containerName: string,
|
||||
): Promise<SandboxRegistryEntry | null> {
|
||||
@@ -324,6 +336,7 @@ export async function readRegistryEntry(
|
||||
return entry ? normalizeSandboxRegistryEntry(entry) : null;
|
||||
}
|
||||
|
||||
/** Creates or updates one sandbox runtime registry entry, preserving immutable creation fields. */
|
||||
export async function updateRegistry(entry: SandboxRegistryEntry) {
|
||||
await withEntryLock(SANDBOX_CONTAINERS_DIR, entry.containerName, async () => {
|
||||
const existing = await readShardedEntry<SandboxRegistryEntry>(
|
||||
@@ -342,16 +355,19 @@ export async function updateRegistry(entry: SandboxRegistryEntry) {
|
||||
});
|
||||
}
|
||||
|
||||
/** Removes one sandbox runtime registry entry by container name. */
|
||||
export async function removeRegistryEntry(containerName: string) {
|
||||
await withEntryLock(SANDBOX_CONTAINERS_DIR, containerName, async () => {
|
||||
await removeShardedEntry(SANDBOX_CONTAINERS_DIR, containerName);
|
||||
});
|
||||
}
|
||||
|
||||
/** Reads all registered browser sandbox containers from the sharded registry. */
|
||||
export async function readBrowserRegistry(): Promise<SandboxBrowserRegistry> {
|
||||
return { entries: await readShardedEntries<SandboxBrowserRegistryEntry>(SANDBOX_BROWSERS_DIR) };
|
||||
}
|
||||
|
||||
/** Creates or updates one browser sandbox registry entry, preserving immutable creation fields. */
|
||||
export async function updateBrowserRegistry(entry: SandboxBrowserRegistryEntry) {
|
||||
await withEntryLock(SANDBOX_BROWSERS_DIR, entry.containerName, async () => {
|
||||
const existing = await readShardedEntry<SandboxBrowserRegistryEntry>(
|
||||
@@ -367,6 +383,7 @@ export async function updateBrowserRegistry(entry: SandboxBrowserRegistryEntry)
|
||||
});
|
||||
}
|
||||
|
||||
/** Removes one browser sandbox registry entry by container name. */
|
||||
export async function removeBrowserRegistryEntry(containerName: string) {
|
||||
await withEntryLock(SANDBOX_BROWSERS_DIR, containerName, async () => {
|
||||
await removeShardedEntry(SANDBOX_BROWSERS_DIR, containerName);
|
||||
|
||||
@@ -5,6 +5,10 @@ import { resolveUserPath } from "../../utils.js";
|
||||
import { resolveAgentIdFromSessionKey } from "../agent-scope.js";
|
||||
import { hashTextSha256 } from "./hash.js";
|
||||
|
||||
/**
|
||||
* Shared sandbox naming and scope helpers used by runtime, registry, and CLI management code.
|
||||
*/
|
||||
/** Converts an arbitrary session key into a bounded filesystem/container-safe slug. */
|
||||
export function slugifySessionKey(value: string) {
|
||||
const trimmed = value.trim() || "session";
|
||||
const hash = hashTextSha256(trimmed).slice(0, 8);
|
||||
@@ -15,12 +19,14 @@ export function slugifySessionKey(value: string) {
|
||||
return `${base}-${hash}`;
|
||||
}
|
||||
|
||||
/** Resolves the per-session sandbox workspace directory under the configured sandbox root. */
|
||||
export function resolveSandboxWorkspaceDir(root: string, sessionKey: string) {
|
||||
const resolvedRoot = resolveUserPath(root);
|
||||
const slug = slugifySessionKey(sessionKey);
|
||||
return path.join(resolvedRoot, slug);
|
||||
}
|
||||
|
||||
/** Resolves the registry scope key for session-, agent-, or shared-scope sandbox lifetimes. */
|
||||
export function resolveSandboxScopeKey(scope: "session" | "agent" | "shared", sessionKey: string) {
|
||||
const trimmed = sessionKey.trim() || "main";
|
||||
if (scope === "shared") {
|
||||
@@ -33,6 +39,7 @@ export function resolveSandboxScopeKey(scope: "session" | "agent" | "shared", se
|
||||
return `agent:${agentId}`;
|
||||
}
|
||||
|
||||
/** Extracts the agent id represented by a sandbox scope key, when one exists. */
|
||||
export function resolveSandboxAgentId(scopeKey: string): string | undefined {
|
||||
const trimmed = scopeKey.trim();
|
||||
if (!trimmed || trimmed === "shared") {
|
||||
|
||||
Reference in New Issue
Block a user