fix(sandbox): compare list to config images (#563) - thanks @pasogott

This commit is contained in:
Peter Steinberger
2026-01-09 13:29:47 +01:00
parent 7883491ce2
commit 201c879772
3 changed files with 38 additions and 6 deletions

View File

@@ -14,8 +14,12 @@ import {
resolveProfile, resolveProfile,
} from "../browser/config.js"; } from "../browser/config.js";
import { DEFAULT_CLAWD_BROWSER_COLOR } from "../browser/constants.js"; import { DEFAULT_CLAWD_BROWSER_COLOR } from "../browser/constants.js";
import type { ClawdbotConfig } from "../config/config.js"; import {
import { STATE_DIR_CLAWDBOT } from "../config/config.js"; type ClawdbotConfig,
loadConfig,
STATE_DIR_CLAWDBOT,
} from "../config/config.js";
import { normalizeAgentId } from "../routing/session-key.js";
import { defaultRuntime } from "../runtime.js"; import { defaultRuntime } from "../runtime.js";
import { resolveUserPath } from "../utils.js"; import { resolveUserPath } from "../utils.js";
import { resolveAgentIdFromSessionKey } from "./agent-scope.js"; import { resolveAgentIdFromSessionKey } from "./agent-scope.js";
@@ -329,6 +333,14 @@ function resolveSandboxScopeKey(scope: SandboxScope, sessionKey: string) {
return `agent:${agentId}`; return `agent:${agentId}`;
} }
function resolveSandboxAgentId(scopeKey: string): string | undefined {
const trimmed = scopeKey.trim();
if (!trimmed || trimmed === "shared") return undefined;
const parts = trimmed.split(":").filter(Boolean);
if (parts[0] === "agent" && parts[1]) return normalizeAgentId(parts[1]);
return resolveAgentIdFromSessionKey(trimmed);
}
export function resolveSandboxConfigForAgent( export function resolveSandboxConfigForAgent(
cfg?: ClawdbotConfig, cfg?: ClawdbotConfig,
agentId?: string, agentId?: string,
@@ -1159,6 +1171,7 @@ export type SandboxBrowserInfo = SandboxBrowserRegistryEntry & {
}; };
export async function listSandboxContainers(): Promise<SandboxContainerInfo[]> { export async function listSandboxContainers(): Promise<SandboxContainerInfo[]> {
const config = loadConfig();
const registry = await readRegistry(); const registry = await readRegistry();
const results: SandboxContainerInfo[] = []; const results: SandboxContainerInfo[] = [];
@@ -1179,10 +1192,14 @@ export async function listSandboxContainers(): Promise<SandboxContainerInfo[]> {
// ignore // ignore
} }
} }
const agentId = resolveSandboxAgentId(entry.sessionKey);
const configuredImage = resolveSandboxConfigForAgent(config, agentId).docker
.image;
results.push({ results.push({
...entry, ...entry,
image: actualImage,
running: state.running, running: state.running,
imageMatch: actualImage === entry.image, imageMatch: actualImage === configuredImage,
}); });
} }
@@ -1190,6 +1207,7 @@ export async function listSandboxContainers(): Promise<SandboxContainerInfo[]> {
} }
export async function listSandboxBrowsers(): Promise<SandboxBrowserInfo[]> { export async function listSandboxBrowsers(): Promise<SandboxBrowserInfo[]> {
const config = loadConfig();
const registry = await readBrowserRegistry(); const registry = await readBrowserRegistry();
const results: SandboxBrowserInfo[] = []; const results: SandboxBrowserInfo[] = [];
@@ -1209,10 +1227,14 @@ export async function listSandboxBrowsers(): Promise<SandboxBrowserInfo[]> {
// ignore // ignore
} }
} }
const agentId = resolveSandboxAgentId(entry.sessionKey);
const configuredImage = resolveSandboxConfigForAgent(config, agentId)
.browser.image;
results.push({ results.push({
...entry, ...entry,
image: actualImage,
running: state.running, running: state.running,
imageMatch: actualImage === entry.image, imageMatch: actualImage === configuredImage,
}); });
} }

View File

@@ -221,6 +221,8 @@ describe("sandboxRecreateCommand", () => {
"Please specify --all, --session <key>, or --agent <id>", "Please specify --all, --session <key>, or --agent <id>",
); );
expect(runtime.exit).toHaveBeenCalledWith(1); expect(runtime.exit).toHaveBeenCalledWith(1);
expect(mocks.listSandboxContainers).not.toHaveBeenCalled();
expect(mocks.listSandboxBrowsers).not.toHaveBeenCalled();
}); });
it("should error if multiple filters specified", async () => { it("should error if multiple filters specified", async () => {
@@ -234,6 +236,8 @@ describe("sandboxRecreateCommand", () => {
"Please specify only one of: --all, --session, --agent", "Please specify only one of: --all, --session, --agent",
); );
expect(runtime.exit).toHaveBeenCalledWith(1); expect(runtime.exit).toHaveBeenCalledWith(1);
expect(mocks.listSandboxContainers).not.toHaveBeenCalled();
expect(mocks.listSandboxBrowsers).not.toHaveBeenCalled();
}); });
}); });

View File

@@ -72,7 +72,9 @@ export async function sandboxRecreateCommand(
opts: SandboxRecreateOptions, opts: SandboxRecreateOptions,
runtime: RuntimeEnv, runtime: RuntimeEnv,
): Promise<void> { ): Promise<void> {
validateRecreateOptions(opts, runtime); if (!validateRecreateOptions(opts, runtime)) {
return;
}
const filtered = await fetchAndFilterContainers(opts); const filtered = await fetchAndFilterContainers(opts);
@@ -101,10 +103,11 @@ export async function sandboxRecreateCommand(
function validateRecreateOptions( function validateRecreateOptions(
opts: SandboxRecreateOptions, opts: SandboxRecreateOptions,
runtime: RuntimeEnv, runtime: RuntimeEnv,
): void { ): boolean {
if (!opts.all && !opts.session && !opts.agent) { if (!opts.all && !opts.session && !opts.agent) {
runtime.error("Please specify --all, --session <key>, or --agent <id>"); runtime.error("Please specify --all, --session <key>, or --agent <id>");
runtime.exit(1); runtime.exit(1);
return false;
} }
const exclusiveCount = [opts.all, opts.session, opts.agent].filter( const exclusiveCount = [opts.all, opts.session, opts.agent].filter(
@@ -113,7 +116,10 @@ function validateRecreateOptions(
if (exclusiveCount > 1) { if (exclusiveCount > 1) {
runtime.error("Please specify only one of: --all, --session, --agent"); runtime.error("Please specify only one of: --all, --session, --agent");
runtime.exit(1); runtime.exit(1);
return false;
} }
return true;
} }
// --- Filtering --- // --- Filtering ---