docs: document codex app-server runtime utilities

This commit is contained in:
Peter Steinberger
2026-06-04 08:45:03 -04:00
parent b91ed087c8
commit 22efdfa904
7 changed files with 54 additions and 0 deletions

View File

@@ -1,3 +1,7 @@
/**
* Resolves the managed Codex app-server binary shipped with or installed beside
* the Codex plugin before stdio startup.
*/
import { constants as fsConstants, readFileSync } from "node:fs";
import { access } from "node:fs/promises";
import { createRequire } from "node:module";
@@ -20,6 +24,7 @@ type ResolveManagedCodexAppServerOptions = {
pathExists?: (filePath: string, platform: NodeJS.Platform) => Promise<boolean>;
};
/** Rewrites managed stdio start options to point at an executable Codex binary path. */
export async function resolveManagedCodexAppServerStartOptions(
startOptions: CodexAppServerStartOptions,
options: ResolveManagedCodexAppServerOptions = {},
@@ -47,6 +52,7 @@ export async function resolveManagedCodexAppServerStartOptions(
};
}
/** Returns the preferred and fallback managed Codex binary paths for a plugin root. */
export function resolveManagedCodexAppServerPaths(params: {
platform?: NodeJS.Platform;
pluginRoot?: string;
@@ -170,6 +176,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
/** Internal helpers exposed for managed-binary path-resolution tests. */
export const testing = {
resolveDefaultCodexPluginRoot,
};

View File

@@ -1,3 +1,7 @@
/**
* Blocks direct Codex app-server requests that would bypass OpenClaw sandbox or
* node-exec routing guarantees.
*/
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { resolveSandboxRuntimeStatus } from "openclaw/plugin-sdk/sandbox";
import {
@@ -65,6 +69,7 @@ const NODE_EXEC_BLOCKED_CONTROL_PLANE_METHODS = new Set<string>([
"config/mcpServer/reload",
]);
/** Returns a block message when a direct app-server method would bypass OpenClaw execution policy. */
export function resolveCodexAppServerDirectSandboxBypassBlock(params: {
method: string;
requestParams?: unknown;
@@ -117,6 +122,7 @@ export function resolveCodexAppServerDirectSandboxBypassBlock(params: {
return sandboxBlock;
}
/** Resolves the generic native-execution block for sandboxed or node-hosted sessions. */
export function resolveCodexNativeExecutionBlock(params: {
config?: OpenClawConfig;
sessionKey?: string;
@@ -126,6 +132,7 @@ export function resolveCodexNativeExecutionBlock(params: {
return resolveCodexNativeSandboxBlock(params) ?? resolveCodexNativeNodeExecBlock(params);
}
/** Returns a block message when native Codex execution cannot honor active sandboxing. */
export function resolveCodexNativeSandboxBlock(params: {
config?: OpenClawConfig;
sessionKey?: string;

View File

@@ -1,3 +1,7 @@
/**
* Persists and normalizes the Codex app-server thread binding associated with
* an OpenClaw session file.
*/
import fs from "node:fs/promises";
import { embeddedAgentLog } from "openclaw/plugin-sdk/agent-harness-runtime";
import {
@@ -21,6 +25,7 @@ const PUBLIC_OPENAI_MODEL_PROVIDER = "openai";
type ProviderAuthAliasLookupParams = Parameters<typeof resolveProviderIdForAuth>[1];
type ProviderAuthAliasConfig = NonNullable<ProviderAuthAliasLookupParams>["config"];
/** Inputs needed to resolve whether a binding's auth profile is native Codex/OpenAI auth. */
export type CodexAppServerAuthProfileLookup = {
authProfileId?: string;
authProfileStore?: AuthProfileStore;
@@ -28,6 +33,7 @@ export type CodexAppServerAuthProfileLookup = {
config?: ProviderAuthAliasConfig;
};
/** Durable sidecar binding connecting an OpenClaw session file to a Codex thread. */
export type CodexAppServerThreadBinding = {
schemaVersion: 1;
threadId: string;
@@ -53,6 +59,7 @@ export type CodexAppServerThreadBinding = {
updatedAt: string;
};
/** Context-engine state persisted with a Codex app-server thread binding. */
export type CodexAppServerContextEngineBinding = {
schemaVersion: 1;
engineId: string;
@@ -60,6 +67,7 @@ export type CodexAppServerContextEngineBinding = {
projection?: CodexAppServerContextEngineProjectionBinding;
};
/** Context-engine projection metadata used to guard resumed native threads. */
export type CodexAppServerContextEngineProjectionBinding = {
schemaVersion: 1;
mode: "thread_bootstrap";
@@ -67,10 +75,12 @@ export type CodexAppServerContextEngineProjectionBinding = {
fingerprint?: string;
};
/** Returns the JSON sidecar path for the Codex app-server binding beside a session file. */
export function resolveCodexAppServerBindingPath(sessionFile: string): string {
return `${sessionFile}.codex-app-server.json`;
}
/** Reads and normalizes a Codex app-server binding sidecar, returning undefined on stale data. */
export async function readCodexAppServerBinding(
sessionFile: string,
lookup: Omit<CodexAppServerAuthProfileLookup, "authProfileId"> = {},
@@ -148,6 +158,7 @@ export async function readCodexAppServerBinding(
}
}
/** Writes the Codex app-server binding sidecar with normalized provider/auth metadata. */
export async function writeCodexAppServerBinding(
sessionFile: string,
binding: Omit<
@@ -293,6 +304,7 @@ function readPluginAppPolicyContext(value: unknown): PluginAppPolicyContext | un
};
}
/** Removes the Codex app-server binding sidecar if present. */
export async function clearCodexAppServerBinding(
sessionFile: string,
_lookup: Omit<CodexAppServerAuthProfileLookup, "authProfileId"> = {},
@@ -306,6 +318,7 @@ export async function clearCodexAppServerBinding(
}
}
/** Clears a binding only when it still points at the expected Codex thread id. */
export async function clearCodexAppServerBindingForThread(
sessionFile: string,
threadId: string,
@@ -331,6 +344,7 @@ function isNotFound(error: unknown): boolean {
return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
}
/** Returns true when an auth profile uses native Codex/OpenAI app-server auth. */
export function isCodexAppServerNativeAuthProfile(
lookup: CodexAppServerAuthProfileLookup,
): boolean {
@@ -356,6 +370,7 @@ export function isCodexAppServerNativeAuthProfile(
}
}
/** Hides redundant OpenAI provider attribution for native Codex auth bindings. */
export function normalizeCodexAppServerBindingModelProvider(params: {
authProfileId?: string;
modelProvider?: string;

View File

@@ -1,3 +1,7 @@
/**
* Guards Codex app-server thread reuse during startup by rotating bindings when
* native transcripts exceed byte or token budgets.
*/
import type { Dirent } from "node:fs";
import fs from "node:fs/promises";
import path from "node:path";
@@ -320,6 +324,7 @@ function hasContextEngineThreadBootstrapProjection(binding: CodexAppServerThread
return binding.contextEngine?.projection?.mode === "thread_bootstrap";
}
/** Clears and drops a binding when the native Codex thread is too large to resume safely. */
export async function rotateOversizedCodexAppServerStartupBinding(params: {
binding: CodexAppServerThreadBinding | undefined;
sessionFile: string;
@@ -426,6 +431,7 @@ export async function rotateOversizedCodexAppServerStartupBinding(params: {
return binding;
}
/** Internal sizing helpers exposed for startup-binding regression tests. */
export const testing = {
parseCodexAppServerByteLimit,
readCodexAppServerRolloutTokenSnapshotLine,

View File

@@ -1,3 +1,7 @@
/**
* Creates and configures stdio-backed Codex app-server transports, including
* Windows spawn normalization and environment filtering.
*/
import { spawn } from "node:child_process";
import {
materializeWindowsSpawnProgram,
@@ -20,6 +24,7 @@ const DEFAULT_SPAWN_RUNTIME: CodexAppServerSpawnRuntime = {
execPath: process.execPath,
};
/** Resolves the concrete command/argv/shell settings used to spawn Codex app-server. */
export function resolveCodexAppServerSpawnInvocation(
options: CodexAppServerStartOptions,
runtime: CodexAppServerSpawnRuntime = DEFAULT_SPAWN_RUNTIME,
@@ -43,6 +48,7 @@ export function resolveCodexAppServerSpawnInvocation(
};
}
/** Merges app-server environment overrides while honoring clearEnv and unsafe key filtering. */
export function resolveCodexAppServerSpawnEnv(
options: Pick<CodexAppServerStartOptions, "env" | "clearEnv">,
baseEnv: NodeJS.ProcessEnv = process.env,
@@ -90,6 +96,7 @@ function copySafeEnvironmentEntries(
}
}
/** Spawns the Codex app-server process and returns the shared transport interface. */
export function createStdioTransport(options: CodexAppServerStartOptions): CodexAppServerTransport {
const env = resolveCodexAppServerSpawnEnv(options);
const invocation = resolveCodexAppServerSpawnInvocation(options, {

View File

@@ -1,9 +1,14 @@
/**
* Adapts a remote Codex app-server WebSocket endpoint to the shared stdio-like
* transport interface.
*/
import { EventEmitter } from "node:events";
import { PassThrough, Writable } from "node:stream";
import WebSocket, { type RawData } from "ws";
import type { CodexAppServerStartOptions } from "./config.js";
import type { CodexAppServerTransport } from "./transport.js";
/** Opens a WebSocket app-server transport and maps newline-delimited frames to stdout/stdin. */
export function createWebSocketTransport(
options: CodexAppServerStartOptions,
): CodexAppServerTransport {

View File

@@ -1,3 +1,8 @@
/**
* Shared transport lifecycle helpers for stdio and WebSocket Codex app-server
* connections.
*/
/** Child-process-like transport shape consumed by the Codex app-server client. */
export type CodexAppServerTransport = {
stdin: {
write: (data: string, callback?: (error?: Error | null) => void) => unknown;
@@ -24,6 +29,7 @@ export type CodexAppServerTransport = {
off?: (event: string, listener: (...args: unknown[]) => void) => unknown;
};
/** Starts graceful transport shutdown and schedules a force kill fallback. */
export function closeCodexAppServerTransport(
child: CodexAppServerTransport,
options: { forceKillDelayMs?: number } = {},
@@ -52,6 +58,7 @@ export function closeCodexAppServerTransport(
child.stdin.unref?.();
}
/** Closes a transport and waits briefly for an exit event. */
export async function closeCodexAppServerTransportAndWait(
child: CodexAppServerTransport,
options: { exitTimeoutMs?: number; forceKillDelayMs?: number } = {},