UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

168 lines (167 loc) • 7.74 kB
import { c as normalizeOptionalLowercaseString } from "./string-coerce-1VuiLaaR.mjs"; import { r as resolveGlobalMap } from "./global-singleton-0m-JziJW.mjs"; import { t as SandboxRuntimeRetiredError } from "./provisioning-error-bD2pXu_s.mjs"; import { f as reserveSandboxRegistryEntry, h as withSandboxRegistryEntryLock, m as updateRegistry, n as assertSandboxRegistryEntryCurrent, r as completeSandboxRegistryReservation } from "./registry-BmXc2WeW.mjs"; import { d as dockerSandboxBackendManager, f as podmanSandboxBackendManager, l as createDockerSandboxBackend, u as createPodmanSandboxBackend } from "./fs-bridge-stat-parse-BcjuGmYw.mjs"; import { m as resolveRemoteShellRuntimePaths, n as createSshSandboxBackend, r as sshSandboxBackendManager } from "./ssh-backend-BwUE1Ule.mjs"; //#region src/agents/sandbox/backend.ts const SANDBOX_BACKEND_FACTORIES_STATE_KEY = Symbol.for("openclaw.sandboxBackendFactories"); function getSandboxBackendFactories() { return resolveGlobalMap(SANDBOX_BACKEND_FACTORIES_STATE_KEY); } function normalizeSandboxBackendId(id) { const normalized = normalizeOptionalLowercaseString(id); if (!normalized) throw new Error("Sandbox backend id must not be empty."); return normalized; } /** Register or replace a sandbox backend and return a restore callback. */ function registerSandboxBackend(id, registration) { const normalizedId = normalizeSandboxBackendId(id); const resolved = typeof registration === "function" ? { factory: registration } : registration; const factories = getSandboxBackendFactories(); const generation = { registration: resolved, previous: factories.get(normalizedId), retired: false }; factories.set(normalizedId, generation); return () => { if (generation.retired) return; generation.retired = true; if (factories.get(normalizedId) !== generation) return; let previous = generation.previous; while (previous?.retired) previous = previous.previous; if (previous) { factories.set(normalizedId, previous); return; } factories.delete(normalizedId); }; } function getSandboxBackendFactory(id) { const registration = resolveSandboxBackendRegistration(id); if (!registration) return null; if (!registration.reserveRuntimeId) return registration.factory; const factory = registration.factory; return async (params) => { const { runtimeId, assertRuntimeCurrent } = params; if (!runtimeId || !assertRuntimeCurrent) throw new Error(`Sandbox backend "${id}" requires a registry-reserved runtime and its current owner.`); assertRuntimeCurrent(); return factory({ ...params, runtimeId, assertRuntimeCurrent }); }; } function getSandboxBackendManager(id) { return resolveSandboxBackendRegistration(id)?.manager ?? null; } /** Include legacy rows in the lifecycle selected by the currently registered backend. */ function usesSandboxRuntimeReservations(id) { return resolveSandboxBackendRegistration(id)?.reserveRuntimeId !== void 0; } function getSandboxBackendWorkdirResolver(id) { return resolveSandboxBackendRegistration(id)?.resolveWorkdir ?? null; } /** Read static backend capabilities without provisioning a sandbox runtime. */ function getSandboxBackendCapabilities(id) { return resolveSandboxBackendRegistration(id)?.capabilities; } function requireSandboxBackendFactory(id) { const factory = getSandboxBackendFactory(id); if (factory) return factory; throw new Error([`Sandbox backend "${id}" is not registered.`, "Load the plugin that provides it, or set agents.defaults.sandbox.backend=docker."].join("\n")); } /** Create and publish a backend, reserving provider IDs only for opted-in factories. */ async function createSandboxBackend(params, operatorAuthority, githubIdentity, guard) { params.assertRuntimeCurrent?.(); const factory = requireSandboxBackendFactory(params.cfg.backend); if (githubIdentity && factory !== createDockerSandboxBackend && factory !== createPodmanSandboxBackend) throw new Error("Sandbox GitHub identity requires the built-in Docker or Podman backend."); const reserveRuntimeId = resolveSandboxBackendRegistration(params.cfg.backend)?.reserveRuntimeId; const toEntry = (backend) => ({ containerName: backend.runtimeId, backendId: backend.id, runtimeLabel: backend.runtimeLabel, sessionKey: params.scopeKey, createdAtMs: Date.now(), lastUsedAtMs: Date.now(), image: backend.configLabel ?? params.cfg.docker.image, configLabelKind: backend.configLabelKind ?? "Image" }); if (!reserveRuntimeId) { const backend = factory === createDockerSandboxBackend ? await createDockerSandboxBackend(params, operatorAuthority, githubIdentity) : factory === createPodmanSandboxBackend ? await createPodmanSandboxBackend(params, operatorAuthority, githubIdentity) : await factory(params); await updateRegistry(toEntry(backend), { ...guard, beforeLegacyApply: params.assertRuntimeCurrent }); return backend; } for (let attempt = 0;; attempt++) { params.assertRuntimeCurrent?.(); const reservation = reserveSandboxRegistryEntry({ containerName: reserveRuntimeId(params), backendId: params.cfg.backend, sessionKey: params.scopeKey, createdAtMs: Date.now(), lastUsedAtMs: Date.now(), image: params.cfg.docker.image, workspaceDir: params.workspaceDir }); try { return await withSandboxRegistryEntryLock(reservation, async () => { const assertCurrent = () => { params.assertRuntimeCurrent?.(); assertSandboxRegistryEntryCurrent(reservation); }; assertCurrent(); try { const backend = await factory({ ...params, workspaceDir: reservation.workspaceDir ?? params.workspaceDir, runtimeId: reservation.containerName, assertRuntimeCurrent: assertCurrent }); if (backend.runtimeId !== reservation.containerName || backend.id !== reservation.backendId) throw new Error("Sandbox backend returned a runtime outside its reserved generation."); await completeSandboxRegistryReservation(toEntry(backend), false, { ...guard, beforeLegacyApply: assertCurrent }); return backend; } catch (error) { if (error instanceof SandboxRuntimeRetiredError && error.runtimeId === reservation.containerName) await completeSandboxRegistryReservation(reservation, true, { ...guard, beforeLegacyApply: assertCurrent }); throw error; } }); } catch (error) { if (!(error instanceof SandboxRuntimeRetiredError) || error.runtimeId !== reservation.containerName || attempt > 0) throw error; } } } const builtinSandboxBackends = /* @__PURE__ */ new Map(); builtinSandboxBackends.set("docker", { factory: createDockerSandboxBackend, manager: dockerSandboxBackendManager, resolveWorkdir: ({ cfg }) => cfg.docker.workdir, capabilities: { readOnlyResourceMounts: true } }); builtinSandboxBackends.set("podman", { factory: createPodmanSandboxBackend, manager: podmanSandboxBackendManager, resolveWorkdir: ({ cfg }) => cfg.docker.workdir, capabilities: { readOnlyResourceMounts: true } }); builtinSandboxBackends.set("ssh", { factory: createSshSandboxBackend, manager: sshSandboxBackendManager, resolveWorkdir: ({ cfg, scopeKey }) => resolveRemoteShellRuntimePaths(cfg.ssh.workspaceRoot, scopeKey).remoteWorkspaceDir }); function resolveSandboxBackendRegistration(id) { const normalizedId = normalizeSandboxBackendId(id); return getSandboxBackendFactories().get(normalizedId)?.registration ?? builtinSandboxBackends.get(normalizedId); } //#endregion export { getSandboxBackendWorkdirResolver as a, usesSandboxRuntimeReservations as c, getSandboxBackendManager as i, getSandboxBackendCapabilities as n, registerSandboxBackend as o, getSandboxBackendFactory as r, requireSandboxBackendFactory as s, createSandboxBackend as t };