mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
docs: document codex sandbox exec server
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Test helpers for standing up fake sandbox contexts and driving the Codex
|
||||
* sandbox exec-server JSON-RPC/WebSocket protocol.
|
||||
*/
|
||||
import type { SandboxContext } from "openclaw/plugin-sdk/sandbox";
|
||||
import { vi } from "vitest";
|
||||
import WebSocket from "ws";
|
||||
@@ -8,6 +12,7 @@ type RpcResponse = {
|
||||
error?: { message: string };
|
||||
};
|
||||
|
||||
/** Builds a minimal enabled sandbox context with overridable backend and fs bridge hooks. */
|
||||
export function createSandboxContext(overrides: {
|
||||
buildExecSpec?: NonNullable<SandboxContext["backend"]>["buildExecSpec"];
|
||||
finalizeExec?: NonNullable<SandboxContext["backend"]>["finalizeExec"];
|
||||
@@ -72,6 +77,7 @@ export function createSandboxContext(overrides: {
|
||||
} as unknown as SandboxContext;
|
||||
}
|
||||
|
||||
/** Creates a fake Codex app-server client with a configurable server version. */
|
||||
export function createClient(options: { serverVersion?: string } = {}) {
|
||||
return {
|
||||
getServerVersion: vi.fn(() => options.serverVersion ?? "0.132.0"),
|
||||
@@ -79,6 +85,7 @@ export function createClient(options: { serverVersion?: string } = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
/** Reads the registered exec-server URL from a fake client's environment/add call. */
|
||||
export function execServerUrlFromClient(
|
||||
client: ReturnType<typeof createClient>,
|
||||
callIndex = 0,
|
||||
@@ -94,6 +101,7 @@ export function execServerUrlFromClient(
|
||||
return execServerUrl;
|
||||
}
|
||||
|
||||
/** Builds a Codex-style managed filesystem sandbox context for RPC params. */
|
||||
export function codexFsSandboxContext(params: {
|
||||
entries: Array<{ path: unknown; access: "read" | "write" | "none" | "deny" }>;
|
||||
cwd?: string;
|
||||
@@ -114,6 +122,7 @@ export function codexFsSandboxContext(params: {
|
||||
};
|
||||
}
|
||||
|
||||
/** Builds a Codex filesystem special-path selector for tests. */
|
||||
export function specialPath(kind: string, subpath?: string): unknown {
|
||||
return {
|
||||
type: "special",
|
||||
@@ -124,6 +133,7 @@ export function specialPath(kind: string, subpath?: string): unknown {
|
||||
};
|
||||
}
|
||||
|
||||
/** Builds a Codex filesystem glob-path selector for tests. */
|
||||
export function globPath(pattern: string): unknown {
|
||||
return {
|
||||
type: "glob_pattern",
|
||||
@@ -131,6 +141,7 @@ export function globPath(pattern: string): unknown {
|
||||
};
|
||||
}
|
||||
|
||||
/** Opens a WebSocket connection and resolves only after the socket is ready. */
|
||||
export function openSocket(url: string): Promise<WebSocket> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const socket = new WebSocket(url);
|
||||
@@ -139,6 +150,7 @@ export function openSocket(url: string): Promise<WebSocket> {
|
||||
});
|
||||
}
|
||||
|
||||
/** Collects server-originated JSON-RPC notifications seen by a WebSocket. */
|
||||
export function collectNotifications(
|
||||
socket: WebSocket,
|
||||
): Array<{ method: string; params?: unknown }> {
|
||||
@@ -156,6 +168,7 @@ export function collectNotifications(
|
||||
return notifications;
|
||||
}
|
||||
|
||||
/** Polls process/read until the managed sandbox process reports closed. */
|
||||
export async function readUntilClosed(
|
||||
socket: WebSocket,
|
||||
processId: string,
|
||||
@@ -189,12 +202,14 @@ export async function readUntilClosed(
|
||||
throw new Error(`process ${processId} did not close`);
|
||||
}
|
||||
|
||||
/** Resolves with the WebSocket close code once the socket closes. */
|
||||
export function waitForSocketClose(socket: WebSocket): Promise<{ code: number }> {
|
||||
return new Promise((resolve) => {
|
||||
socket.once("close", (code) => resolve({ code }));
|
||||
});
|
||||
}
|
||||
|
||||
/** Waits until the requested number of streaming HTTP body-delta notifications arrive. */
|
||||
export async function waitForHttpBodyDeltas(
|
||||
notifications: Array<{ method: string; params?: unknown }>,
|
||||
count: number,
|
||||
@@ -213,10 +228,12 @@ export async function waitForHttpBodyDeltas(
|
||||
throw new Error(`expected ${count} http body deltas`);
|
||||
}
|
||||
|
||||
/** Quotes a value for POSIX shell snippets embedded in sandbox test commands. */
|
||||
export function shellQuote(value: string): string {
|
||||
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
||||
}
|
||||
|
||||
/** Sends one JSON-RPC request and resolves/rejects from the matching response id. */
|
||||
export function rpc(socket: WebSocket, method: string, params: unknown): Promise<unknown> {
|
||||
const id = Math.floor(Math.random() * 1_000_000);
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Hosts the local OpenClaw sandbox exec-server that Codex app-server native
|
||||
* execution can register as an external environment.
|
||||
*/
|
||||
import { createHash, randomUUID } from "node:crypto";
|
||||
import { once } from "node:events";
|
||||
import type { IncomingMessage } from "node:http";
|
||||
@@ -37,6 +41,7 @@ import type {
|
||||
} from "./sandbox-exec-server/types.js";
|
||||
import { MIN_CODEX_SANDBOX_EXEC_SERVER_APP_SERVER_VERSION } from "./version.js";
|
||||
|
||||
/** Codex environment metadata registered for one sandbox exec-server lease. */
|
||||
export type CodexSandboxExecEnvironment = {
|
||||
environmentId: string;
|
||||
cwd: string;
|
||||
@@ -44,6 +49,7 @@ export type CodexSandboxExecEnvironment = {
|
||||
|
||||
const SANDBOX_EXEC_SERVERS = new Map<string, Promise<OpenClawExecServer>>();
|
||||
|
||||
/** Closes all cached sandbox exec-server instances for deterministic tests. */
|
||||
export async function closeCodexSandboxExecServersForTests(): Promise<void> {
|
||||
const servers = await Promise.allSettled(SANDBOX_EXEC_SERVERS.values());
|
||||
SANDBOX_EXEC_SERVERS.clear();
|
||||
@@ -57,6 +63,7 @@ export async function closeCodexSandboxExecServersForTests(): Promise<void> {
|
||||
);
|
||||
}
|
||||
|
||||
/** Starts or reuses a sandbox exec-server and registers it with Codex app-server. */
|
||||
export async function ensureCodexSandboxExecServerEnvironment(params: {
|
||||
client: CodexAppServerClient;
|
||||
sandbox: SandboxContext | null;
|
||||
@@ -99,6 +106,7 @@ export async function ensureCodexSandboxExecServerEnvironment(params: {
|
||||
};
|
||||
}
|
||||
|
||||
/** Releases the sandbox exec-server lease associated with a sandbox runtime. */
|
||||
export async function releaseCodexSandboxExecServerEnvironment(
|
||||
sandbox: SandboxContext | null | undefined,
|
||||
): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user