import fs from "node:fs/promises"; import type { OpenClawConfig } from "../../config/config.js"; import type { SandboxContext, SandboxWorkspaceInfo } from "./types.js"; import { DEFAULT_BROWSER_EVALUATE_ENABLED } from "../../browser/constants.js"; import { defaultRuntime } from "../../runtime.js"; import { resolveUserPath } from "../../utils.js"; import { syncSkillsToWorkspace } from "../skills.js"; import { DEFAULT_AGENT_WORKSPACE_DIR } from "../workspace.js"; import { ensureSandboxBrowser } from "./browser.js"; import { resolveSandboxConfigForAgent } from "./config.js"; import { ensureSandboxContainer } from "./docker.js"; import { maybePruneSandboxes } from "./prune.js"; import { resolveSandboxRuntimeStatus } from "./runtime-status.js"; import { resolveSandboxScopeKey, resolveSandboxWorkspaceDir } from "./shared.js"; import { ensureSandboxWorkspace } from "./workspace.js"; export async function resolveSandboxContext(params: { config?: OpenClawConfig; sessionKey?: string; workspaceDir?: string; }): Promise { const rawSessionKey = params.sessionKey?.trim(); if (!rawSessionKey) { return null; } const runtime = resolveSandboxRuntimeStatus({ cfg: params.config, sessionKey: rawSessionKey, }); if (!runtime.sandboxed) { return null; } const cfg = resolveSandboxConfigForAgent(params.config, runtime.agentId); await maybePruneSandboxes(cfg); const agentWorkspaceDir = resolveUserPath( params.workspaceDir?.trim() || DEFAULT_AGENT_WORKSPACE_DIR, ); const workspaceRoot = resolveUserPath(cfg.workspaceRoot); const scopeKey = resolveSandboxScopeKey(cfg.scope, rawSessionKey); const sandboxWorkspaceDir = cfg.scope === "shared" ? workspaceRoot : resolveSandboxWorkspaceDir(workspaceRoot, scopeKey); const workspaceDir = cfg.workspaceAccess === "rw" ? agentWorkspaceDir : sandboxWorkspaceDir; if (workspaceDir === sandboxWorkspaceDir) { await ensureSandboxWorkspace( sandboxWorkspaceDir, agentWorkspaceDir, params.config?.agents?.defaults?.skipBootstrap, ); if (cfg.workspaceAccess !== "rw") { try { await syncSkillsToWorkspace({ sourceWorkspaceDir: agentWorkspaceDir, targetWorkspaceDir: sandboxWorkspaceDir, config: params.config, }); } catch (error) { const message = error instanceof Error ? error.message : JSON.stringify(error); defaultRuntime.error?.(`Sandbox skill sync failed: ${message}`); } } } else { await fs.mkdir(workspaceDir, { recursive: true }); } const containerName = await ensureSandboxContainer({ sessionKey: rawSessionKey, workspaceDir, agentWorkspaceDir, cfg, }); const evaluateEnabled = params.config?.browser?.evaluateEnabled ?? DEFAULT_BROWSER_EVALUATE_ENABLED; const browser = await ensureSandboxBrowser({ scopeKey, workspaceDir, agentWorkspaceDir, cfg, evaluateEnabled, }); return { enabled: true, sessionKey: rawSessionKey, workspaceDir, agentWorkspaceDir, workspaceAccess: cfg.workspaceAccess, containerName, containerWorkdir: cfg.docker.workdir, docker: cfg.docker, tools: cfg.tools, browserAllowHostControl: cfg.browser.allowHostControl, browser: browser ?? undefined, }; } export async function ensureSandboxWorkspaceForSession(params: { config?: OpenClawConfig; sessionKey?: string; workspaceDir?: string; }): Promise { const rawSessionKey = params.sessionKey?.trim(); if (!rawSessionKey) { return null; } const runtime = resolveSandboxRuntimeStatus({ cfg: params.config, sessionKey: rawSessionKey, }); if (!runtime.sandboxed) { return null; } const cfg = resolveSandboxConfigForAgent(params.config, runtime.agentId); const agentWorkspaceDir = resolveUserPath( params.workspaceDir?.trim() || DEFAULT_AGENT_WORKSPACE_DIR, ); const workspaceRoot = resolveUserPath(cfg.workspaceRoot); const scopeKey = resolveSandboxScopeKey(cfg.scope, rawSessionKey); const sandboxWorkspaceDir = cfg.scope === "shared" ? workspaceRoot : resolveSandboxWorkspaceDir(workspaceRoot, scopeKey); const workspaceDir = cfg.workspaceAccess === "rw" ? agentWorkspaceDir : sandboxWorkspaceDir; if (workspaceDir === sandboxWorkspaceDir) { await ensureSandboxWorkspace( sandboxWorkspaceDir, agentWorkspaceDir, params.config?.agents?.defaults?.skipBootstrap, ); if (cfg.workspaceAccess !== "rw") { try { await syncSkillsToWorkspace({ sourceWorkspaceDir: agentWorkspaceDir, targetWorkspaceDir: sandboxWorkspaceDir, config: params.config, }); } catch (error) { const message = error instanceof Error ? error.message : JSON.stringify(error); defaultRuntime.error?.(`Sandbox skill sync failed: ${message}`); } } } else { await fs.mkdir(workspaceDir, { recursive: true }); } return { workspaceDir, containerWorkdir: cfg.docker.workdir, }; }