diff --git a/extensions/openshell/src/fs-bridge.ts b/extensions/openshell/src/fs-bridge.ts index f779e423523..7f6c9dfb0e2 100644 --- a/extensions/openshell/src/fs-bridge.ts +++ b/extensions/openshell/src/fs-bridge.ts @@ -1,7 +1,6 @@ import fsPromises from "node:fs/promises"; import path from "node:path"; import type { - SandboxContext, SandboxFsBridge, SandboxFsStat, SandboxResolvedPath, @@ -10,6 +9,10 @@ import { createWritableRenameTargetResolver } from "openclaw/plugin-sdk/sandbox" import type { OpenShellSandboxBackend } from "./backend.js"; import { movePathWithCopyFallback } from "./mirror.js"; +type OpenShellFsBridgeContext = Parameters< + NonNullable +>[0]["sandbox"]; + type ResolvedMountPath = SandboxResolvedPath & { mountHostRoot: string; writable: boolean; @@ -17,7 +20,7 @@ type ResolvedMountPath = SandboxResolvedPath & { }; export function createOpenShellFsBridge(params: { - sandbox: SandboxContext; + sandbox: OpenShellFsBridgeContext; backend: OpenShellSandboxBackend; }): SandboxFsBridge { return new OpenShellFsBridge(params.sandbox, params.backend); @@ -30,7 +33,7 @@ class OpenShellFsBridge implements SandboxFsBridge { ); constructor( - private readonly sandbox: SandboxContext, + private readonly sandbox: OpenShellFsBridgeContext, private readonly backend: OpenShellSandboxBackend, ) {} diff --git a/src/agents/sandbox/backend-handle.types.ts b/src/agents/sandbox/backend-handle.types.ts new file mode 100644 index 00000000000..0a4a14fec03 --- /dev/null +++ b/src/agents/sandbox/backend-handle.types.ts @@ -0,0 +1,65 @@ +import type { SandboxFsBridge } from "./fs-bridge.types.js"; + +export type SandboxBackendId = string; + +export type SandboxBackendExecSpec = { + argv: string[]; + env: NodeJS.ProcessEnv; + stdinMode: "pipe-open" | "pipe-closed"; + finalizeToken?: unknown; +}; + +export type SandboxBackendCommandParams = { + script: string; + args?: string[]; + stdin?: Buffer | string; + allowFailure?: boolean; + signal?: AbortSignal; +}; + +export type SandboxBackendCommandResult = { + stdout: Buffer; + stderr: Buffer; + code: number; +}; + +export type SandboxFsBridgeContext = { + workspaceDir: string; + agentWorkspaceDir: string; + workspaceAccess: "none" | "ro" | "rw"; + containerName: string; + containerWorkdir: string; + docker: { + binds?: string[]; + }; + backend?: { + runShellCommand(params: SandboxBackendCommandParams): Promise; + }; +}; + +export type SandboxBackendHandle = { + id: SandboxBackendId; + runtimeId: string; + runtimeLabel: string; + workdir: string; + env?: Record; + configLabel?: string; + configLabelKind?: string; + capabilities?: { + browser?: boolean; + }; + buildExecSpec(params: { + command: string; + workdir?: string; + env: Record; + usePty: boolean; + }): Promise; + finalizeExec?: (params: { + status: "completed" | "failed"; + exitCode: number | null; + timedOut: boolean; + token?: unknown; + }) => Promise; + runShellCommand(params: SandboxBackendCommandParams): Promise; + createFsBridge?: (params: { sandbox: SandboxFsBridgeContext }) => SandboxFsBridge; +}; diff --git a/src/agents/sandbox/backend.ts b/src/agents/sandbox/backend.ts index b29db432844..8dd97530c8f 100644 --- a/src/agents/sandbox/backend.ts +++ b/src/agents/sandbox/backend.ts @@ -1,58 +1,17 @@ import type { OpenClawConfig } from "../../config/config.js"; import { normalizeOptionalLowercaseString } from "../../shared/string-coerce.js"; -import type { SandboxFsBridge } from "./fs-bridge.types.js"; +import type { SandboxBackendHandle, SandboxBackendId } from "./backend-handle.types.js"; import type { SandboxRegistryEntry } from "./registry.js"; -import type { SandboxConfig, SandboxContext } from "./types.js"; +import type { SandboxConfig } from "./types.js"; -export type SandboxBackendId = string; - -export type SandboxBackendExecSpec = { - argv: string[]; - env: NodeJS.ProcessEnv; - stdinMode: "pipe-open" | "pipe-closed"; - finalizeToken?: unknown; -}; - -export type SandboxBackendCommandParams = { - script: string; - args?: string[]; - stdin?: Buffer | string; - allowFailure?: boolean; - signal?: AbortSignal; -}; - -export type SandboxBackendCommandResult = { - stdout: Buffer; - stderr: Buffer; - code: number; -}; - -export type SandboxBackendHandle = { - id: SandboxBackendId; - runtimeId: string; - runtimeLabel: string; - workdir: string; - env?: Record; - configLabel?: string; - configLabelKind?: string; - capabilities?: { - browser?: boolean; - }; - buildExecSpec(params: { - command: string; - workdir?: string; - env: Record; - usePty: boolean; - }): Promise; - finalizeExec?: (params: { - status: "completed" | "failed"; - exitCode: number | null; - timedOut: boolean; - token?: unknown; - }) => Promise; - runShellCommand(params: SandboxBackendCommandParams): Promise; - createFsBridge?: (params: { sandbox: SandboxContext }) => SandboxFsBridge; -}; +export type { + SandboxBackendCommandParams, + SandboxBackendCommandResult, + SandboxBackendExecSpec, + SandboxBackendHandle, + SandboxBackendId, + SandboxFsBridgeContext, +} from "./backend-handle.types.js"; export type SandboxBackendRuntimeInfo = { running: boolean; diff --git a/src/agents/sandbox/fs-bridge.ts b/src/agents/sandbox/fs-bridge.ts index 7a06bd69856..900e7ef1481 100644 --- a/src/agents/sandbox/fs-bridge.ts +++ b/src/agents/sandbox/fs-bridge.ts @@ -1,6 +1,9 @@ import fs from "node:fs"; import { normalizeOptionalLowercaseString } from "../../shared/string-coerce.js"; -import type { SandboxBackendCommandResult } from "./backend.js"; +import type { + SandboxBackendCommandResult, + SandboxFsBridgeContext, +} from "./backend-handle.types.js"; import { runDockerSandboxShellCommand } from "./docker-backend.js"; import { buildPinnedMkdirpPlan, @@ -16,7 +19,7 @@ import { resolveSandboxFsPathWithMounts, type SandboxResolvedFsPath, } from "./fs-paths.js"; -import type { SandboxContext, SandboxWorkspaceAccess } from "./types.js"; +import type { SandboxWorkspaceAccess } from "./types.js"; type RunCommandOptions = { args?: string[]; @@ -27,16 +30,18 @@ type RunCommandOptions = { export type { SandboxFsBridge, SandboxFsStat, SandboxResolvedPath } from "./fs-bridge.types.js"; -export function createSandboxFsBridge(params: { sandbox: SandboxContext }): SandboxFsBridge { +export function createSandboxFsBridge(params: { + sandbox: SandboxFsBridgeContext; +}): SandboxFsBridge { return new SandboxFsBridgeImpl(params.sandbox); } class SandboxFsBridgeImpl implements SandboxFsBridge { - private readonly sandbox: SandboxContext; + private readonly sandbox: SandboxFsBridgeContext; private readonly mounts: ReturnType; private readonly pathGuard: SandboxFsPathGuard; - constructor(sandbox: SandboxContext) { + constructor(sandbox: SandboxFsBridgeContext) { this.sandbox = sandbox; this.mounts = buildSandboxFsMounts(sandbox); const mountsByContainer = [...this.mounts].toSorted( diff --git a/src/agents/sandbox/fs-paths.ts b/src/agents/sandbox/fs-paths.ts index cac63201a39..a319ed67116 100644 --- a/src/agents/sandbox/fs-paths.ts +++ b/src/agents/sandbox/fs-paths.ts @@ -1,11 +1,11 @@ import path from "node:path"; import { normalizeOptionalLowercaseString } from "../../shared/string-coerce.js"; import { resolveSandboxInputPath, resolveSandboxPath } from "../sandbox-paths.js"; +import type { SandboxFsBridgeContext } from "./backend-handle.types.js"; import { splitSandboxBindSpec } from "./bind-spec.js"; import { SANDBOX_AGENT_WORKSPACE_MOUNT } from "./constants.js"; import { resolveSandboxHostPathViaExistingAncestor } from "./host-paths.js"; import { isPathInsideContainerRoot, normalizeContainerPath } from "./path-utils.js"; -import type { SandboxContext } from "./types.js"; export type SandboxFsMount = { hostRoot: string; @@ -58,7 +58,7 @@ export function parseSandboxBindMount(spec: string): ParsedBindMount | null { }; } -export function buildSandboxFsMounts(sandbox: SandboxContext): SandboxFsMount[] { +export function buildSandboxFsMounts(sandbox: SandboxFsBridgeContext): SandboxFsMount[] { const mounts: SandboxFsMount[] = [ { hostRoot: path.resolve(sandbox.workspaceDir), diff --git a/src/agents/sandbox/remote-fs-bridge.ts b/src/agents/sandbox/remote-fs-bridge.ts index 7ac45654786..a4686f112f3 100644 --- a/src/agents/sandbox/remote-fs-bridge.ts +++ b/src/agents/sandbox/remote-fs-bridge.ts @@ -1,6 +1,10 @@ import path from "node:path"; import { isPathInside } from "../../infra/path-guards.js"; -import type { SandboxBackendCommandParams, SandboxBackendCommandResult } from "./backend.js"; +import type { + SandboxBackendCommandParams, + SandboxBackendCommandResult, + SandboxFsBridgeContext, +} from "./backend-handle.types.js"; import { SANDBOX_PINNED_MUTATION_PYTHON } from "./fs-bridge-mutation-helper.js"; import { createWritableRenameTargetResolver } from "./fs-bridge-rename-targets.js"; import type { SandboxFsBridge, SandboxFsStat, SandboxResolvedPath } from "./fs-bridge.types.js"; @@ -8,7 +12,6 @@ import { isPathInsideContainerRoot, normalizeContainerPath as normalizeSandboxContainerPath, } from "./path-utils.js"; -import type { SandboxContext } from "./types.js"; type ResolvedRemotePath = SandboxResolvedPath & { writable: boolean; @@ -29,7 +32,7 @@ export type RemoteShellSandboxHandle = { }; export function createRemoteShellSandboxFsBridge(params: { - sandbox: SandboxContext; + sandbox: SandboxFsBridgeContext; runtime: RemoteShellSandboxHandle; }): SandboxFsBridge { return new RemoteShellSandboxFsBridge(params.sandbox, params.runtime); @@ -42,7 +45,7 @@ class RemoteShellSandboxFsBridge implements SandboxFsBridge { ); constructor( - private readonly sandbox: SandboxContext, + private readonly sandbox: SandboxFsBridgeContext, private readonly runtime: RemoteShellSandboxHandle, ) {} diff --git a/src/agents/sandbox/types.ts b/src/agents/sandbox/types.ts index 1280101442f..654a3e9ba90 100644 --- a/src/agents/sandbox/types.ts +++ b/src/agents/sandbox/types.ts @@ -1,4 +1,4 @@ -import type { SandboxBackendHandle, SandboxBackendId } from "./backend.js"; +import type { SandboxBackendHandle, SandboxBackendId } from "./backend-handle.types.js"; import type { SandboxFsBridge } from "./fs-bridge.types.js"; import type { SandboxDockerConfig } from "./types.docker.js";