mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-23 07:51:33 +00:00
192 lines
7.3 KiB
TypeScript
192 lines
7.3 KiB
TypeScript
import type { OpenClawConfig } from "../../config/config.js";
|
|
import { resolveAgentConfig } from "../agent-scope.js";
|
|
import {
|
|
DEFAULT_SANDBOX_BROWSER_AUTOSTART_TIMEOUT_MS,
|
|
DEFAULT_SANDBOX_BROWSER_CDP_PORT,
|
|
DEFAULT_SANDBOX_BROWSER_IMAGE,
|
|
DEFAULT_SANDBOX_BROWSER_NOVNC_PORT,
|
|
DEFAULT_SANDBOX_BROWSER_PREFIX,
|
|
DEFAULT_SANDBOX_BROWSER_VNC_PORT,
|
|
DEFAULT_SANDBOX_CONTAINER_PREFIX,
|
|
DEFAULT_SANDBOX_IDLE_HOURS,
|
|
DEFAULT_SANDBOX_IMAGE,
|
|
DEFAULT_SANDBOX_MAX_AGE_DAYS,
|
|
DEFAULT_SANDBOX_WORKDIR,
|
|
DEFAULT_SANDBOX_WORKSPACE_ROOT,
|
|
} from "./constants.js";
|
|
import { resolveSandboxToolPolicyForAgent } from "./tool-policy.js";
|
|
import type {
|
|
SandboxBrowserConfig,
|
|
SandboxConfig,
|
|
SandboxDockerConfig,
|
|
SandboxPruneConfig,
|
|
SandboxScope,
|
|
} from "./types.js";
|
|
|
|
export function resolveSandboxBrowserDockerCreateConfig(params: {
|
|
docker: SandboxDockerConfig;
|
|
browser: SandboxBrowserConfig;
|
|
}): SandboxDockerConfig {
|
|
const base: SandboxDockerConfig = {
|
|
...params.docker,
|
|
// Browser container needs network access for Chrome, downloads, etc.
|
|
network: "bridge",
|
|
// For hashing and consistency, treat browser image as the docker image even though we
|
|
// pass it separately as the final `docker create` argument.
|
|
image: params.browser.image,
|
|
};
|
|
return params.browser.binds !== undefined ? { ...base, binds: params.browser.binds } : base;
|
|
}
|
|
|
|
export function resolveSandboxScope(params: {
|
|
scope?: SandboxScope;
|
|
perSession?: boolean;
|
|
}): SandboxScope {
|
|
if (params.scope) {
|
|
return params.scope;
|
|
}
|
|
if (typeof params.perSession === "boolean") {
|
|
return params.perSession ? "session" : "shared";
|
|
}
|
|
return "agent";
|
|
}
|
|
|
|
export function resolveSandboxDockerConfig(params: {
|
|
scope: SandboxScope;
|
|
globalDocker?: Partial<SandboxDockerConfig>;
|
|
agentDocker?: Partial<SandboxDockerConfig>;
|
|
}): SandboxDockerConfig {
|
|
const agentDocker = params.scope === "shared" ? undefined : params.agentDocker;
|
|
const globalDocker = params.globalDocker;
|
|
|
|
const env = agentDocker?.env
|
|
? { ...(globalDocker?.env ?? { LANG: "C.UTF-8" }), ...agentDocker.env }
|
|
: (globalDocker?.env ?? { LANG: "C.UTF-8" });
|
|
|
|
const ulimits = agentDocker?.ulimits
|
|
? { ...globalDocker?.ulimits, ...agentDocker.ulimits }
|
|
: globalDocker?.ulimits;
|
|
|
|
const binds = [...(globalDocker?.binds ?? []), ...(agentDocker?.binds ?? [])];
|
|
|
|
return {
|
|
image: agentDocker?.image ?? globalDocker?.image ?? DEFAULT_SANDBOX_IMAGE,
|
|
containerPrefix:
|
|
agentDocker?.containerPrefix ??
|
|
globalDocker?.containerPrefix ??
|
|
DEFAULT_SANDBOX_CONTAINER_PREFIX,
|
|
workdir: agentDocker?.workdir ?? globalDocker?.workdir ?? DEFAULT_SANDBOX_WORKDIR,
|
|
readOnlyRoot: agentDocker?.readOnlyRoot ?? globalDocker?.readOnlyRoot ?? true,
|
|
tmpfs: agentDocker?.tmpfs ?? globalDocker?.tmpfs ?? ["/tmp", "/var/tmp", "/run"],
|
|
network: agentDocker?.network ?? globalDocker?.network ?? "none",
|
|
user: agentDocker?.user ?? globalDocker?.user,
|
|
capDrop: agentDocker?.capDrop ?? globalDocker?.capDrop ?? ["ALL"],
|
|
env,
|
|
setupCommand: agentDocker?.setupCommand ?? globalDocker?.setupCommand,
|
|
pidsLimit: agentDocker?.pidsLimit ?? globalDocker?.pidsLimit,
|
|
memory: agentDocker?.memory ?? globalDocker?.memory,
|
|
memorySwap: agentDocker?.memorySwap ?? globalDocker?.memorySwap,
|
|
cpus: agentDocker?.cpus ?? globalDocker?.cpus,
|
|
ulimits,
|
|
seccompProfile: agentDocker?.seccompProfile ?? globalDocker?.seccompProfile,
|
|
apparmorProfile: agentDocker?.apparmorProfile ?? globalDocker?.apparmorProfile,
|
|
dns: agentDocker?.dns ?? globalDocker?.dns,
|
|
extraHosts: agentDocker?.extraHosts ?? globalDocker?.extraHosts,
|
|
binds: binds.length ? binds : undefined,
|
|
};
|
|
}
|
|
|
|
export function resolveSandboxBrowserConfig(params: {
|
|
scope: SandboxScope;
|
|
globalBrowser?: Partial<SandboxBrowserConfig>;
|
|
agentBrowser?: Partial<SandboxBrowserConfig>;
|
|
}): SandboxBrowserConfig {
|
|
const agentBrowser = params.scope === "shared" ? undefined : params.agentBrowser;
|
|
const globalBrowser = params.globalBrowser;
|
|
const binds = [...(globalBrowser?.binds ?? []), ...(agentBrowser?.binds ?? [])];
|
|
// Treat `binds: []` as an explicit override, so it can disable `docker.binds` for the browser container.
|
|
const bindsConfigured = globalBrowser?.binds !== undefined || agentBrowser?.binds !== undefined;
|
|
return {
|
|
enabled: agentBrowser?.enabled ?? globalBrowser?.enabled ?? false,
|
|
image: agentBrowser?.image ?? globalBrowser?.image ?? DEFAULT_SANDBOX_BROWSER_IMAGE,
|
|
containerPrefix:
|
|
agentBrowser?.containerPrefix ??
|
|
globalBrowser?.containerPrefix ??
|
|
DEFAULT_SANDBOX_BROWSER_PREFIX,
|
|
cdpPort: agentBrowser?.cdpPort ?? globalBrowser?.cdpPort ?? DEFAULT_SANDBOX_BROWSER_CDP_PORT,
|
|
vncPort: agentBrowser?.vncPort ?? globalBrowser?.vncPort ?? DEFAULT_SANDBOX_BROWSER_VNC_PORT,
|
|
noVncPort:
|
|
agentBrowser?.noVncPort ?? globalBrowser?.noVncPort ?? DEFAULT_SANDBOX_BROWSER_NOVNC_PORT,
|
|
headless: agentBrowser?.headless ?? globalBrowser?.headless ?? false,
|
|
enableNoVnc: agentBrowser?.enableNoVnc ?? globalBrowser?.enableNoVnc ?? true,
|
|
allowHostControl: agentBrowser?.allowHostControl ?? globalBrowser?.allowHostControl ?? false,
|
|
autoStart: agentBrowser?.autoStart ?? globalBrowser?.autoStart ?? true,
|
|
autoStartTimeoutMs:
|
|
agentBrowser?.autoStartTimeoutMs ??
|
|
globalBrowser?.autoStartTimeoutMs ??
|
|
DEFAULT_SANDBOX_BROWSER_AUTOSTART_TIMEOUT_MS,
|
|
binds: bindsConfigured ? binds : undefined,
|
|
};
|
|
}
|
|
|
|
export function resolveSandboxPruneConfig(params: {
|
|
scope: SandboxScope;
|
|
globalPrune?: Partial<SandboxPruneConfig>;
|
|
agentPrune?: Partial<SandboxPruneConfig>;
|
|
}): SandboxPruneConfig {
|
|
const agentPrune = params.scope === "shared" ? undefined : params.agentPrune;
|
|
const globalPrune = params.globalPrune;
|
|
return {
|
|
idleHours: agentPrune?.idleHours ?? globalPrune?.idleHours ?? DEFAULT_SANDBOX_IDLE_HOURS,
|
|
maxAgeDays: agentPrune?.maxAgeDays ?? globalPrune?.maxAgeDays ?? DEFAULT_SANDBOX_MAX_AGE_DAYS,
|
|
};
|
|
}
|
|
|
|
export function resolveSandboxConfigForAgent(
|
|
cfg?: OpenClawConfig,
|
|
agentId?: string,
|
|
): SandboxConfig {
|
|
const agent = cfg?.agents?.defaults?.sandbox;
|
|
|
|
// Agent-specific sandbox config overrides global
|
|
let agentSandbox: typeof agent | undefined;
|
|
const agentConfig = cfg && agentId ? resolveAgentConfig(cfg, agentId) : undefined;
|
|
if (agentConfig?.sandbox) {
|
|
agentSandbox = agentConfig.sandbox;
|
|
}
|
|
|
|
const scope = resolveSandboxScope({
|
|
scope: agentSandbox?.scope ?? agent?.scope,
|
|
perSession: agentSandbox?.perSession ?? agent?.perSession,
|
|
});
|
|
|
|
const toolPolicy = resolveSandboxToolPolicyForAgent(cfg, agentId);
|
|
|
|
return {
|
|
mode: agentSandbox?.mode ?? agent?.mode ?? "off",
|
|
scope,
|
|
workspaceAccess: agentSandbox?.workspaceAccess ?? agent?.workspaceAccess ?? "none",
|
|
workspaceRoot:
|
|
agentSandbox?.workspaceRoot ?? agent?.workspaceRoot ?? DEFAULT_SANDBOX_WORKSPACE_ROOT,
|
|
docker: resolveSandboxDockerConfig({
|
|
scope,
|
|
globalDocker: agent?.docker,
|
|
agentDocker: agentSandbox?.docker,
|
|
}),
|
|
browser: resolveSandboxBrowserConfig({
|
|
scope,
|
|
globalBrowser: agent?.browser,
|
|
agentBrowser: agentSandbox?.browser,
|
|
}),
|
|
tools: {
|
|
allow: toolPolicy.allow,
|
|
deny: toolPolicy.deny,
|
|
},
|
|
prune: resolveSandboxPruneConfig({
|
|
scope,
|
|
globalPrune: agent?.prune,
|
|
agentPrune: agentSandbox?.prune,
|
|
}),
|
|
};
|
|
}
|