diff --git a/extensions/codex/src/app-server/capabilities.ts b/extensions/codex/src/app-server/capabilities.ts index 0428f29d1793..1571b2c8f84b 100644 --- a/extensions/codex/src/app-server/capabilities.ts +++ b/extensions/codex/src/app-server/capabilities.ts @@ -1,5 +1,9 @@ +/** + * Capability helpers for optional Codex app-server control-plane methods. + */ import { CodexAppServerRpcError } from "./client.js"; +/** Known app-server methods used by OpenClaw control surfaces. */ export const CODEX_CONTROL_METHODS = { account: "account/read", compact: "thread/compact/start", @@ -13,8 +17,10 @@ export const CODEX_CONTROL_METHODS = { } as const; type CodexControlName = keyof typeof CODEX_CONTROL_METHODS; +/** App-server method name from the known control method map. */ export type CodexControlMethod = (typeof CODEX_CONTROL_METHODS)[CodexControlName]; +/** Formats unsupported control calls differently from ordinary RPC failures. */ export function describeControlFailure(error: unknown): string { if (isUnsupportedControlError(error)) { return "unsupported by this Codex app-server"; diff --git a/extensions/codex/src/app-server/client-factory.ts b/extensions/codex/src/app-server/client-factory.ts index ffe5b1aaedac..2a453d9b931b 100644 --- a/extensions/codex/src/app-server/client-factory.ts +++ b/extensions/codex/src/app-server/client-factory.ts @@ -1,3 +1,6 @@ +/** + * Lazy factories for shared and leased Codex app-server clients. + */ import type { resolveCodexAppServerAuthProfileIdForAgent } from "./auth-bridge.js"; import type { CodexAppServerClient } from "./client.js"; import type { CodexAppServerStartOptions } from "./config.js"; @@ -6,6 +9,7 @@ type AuthProfileOrderConfig = Parameters< typeof resolveCodexAppServerAuthProfileIdForAgent >[0]["config"]; +/** Factory signature used by Codex attempt startup to acquire a client. */ export type CodexAppServerClientFactory = ( startOptions?: CodexAppServerStartOptions, authProfileId?: string, @@ -24,6 +28,7 @@ const loadSharedClientModule = async () => { return await sharedClientModulePromise; }; +/** Returns the process-shared app-server client for normal attempt reuse. */ export const defaultCodexAppServerClientFactory: CodexAppServerClientFactory = ( startOptions, authProfileId, @@ -42,6 +47,7 @@ export const defaultCodexAppServerClientFactory: CodexAppServerClientFactory = ( }), ); +/** Returns a leased shared client so startup can release ownership explicitly. */ export const defaultLeasedCodexAppServerClientFactory: CodexAppServerClientFactory = ( startOptions, authProfileId, diff --git a/extensions/codex/src/app-server/client.ts b/extensions/codex/src/app-server/client.ts index 3acdc8924876..3898ee9be723 100644 --- a/extensions/codex/src/app-server/client.ts +++ b/extensions/codex/src/app-server/client.ts @@ -1,3 +1,7 @@ +/** + * JSON-RPC client for Codex app-server transports, including request/response + * routing, notification fanout, server request handlers, and version checks. + */ import { createInterface, type Interface as ReadlineInterface } from "node:readline"; import { embeddedAgentLog, OPENCLAW_VERSION } from "openclaw/plugin-sdk/agent-harness-runtime"; import { resolveCodexAppServerRuntimeOptions, type CodexAppServerStartOptions } from "./config.js"; @@ -23,6 +27,7 @@ import { } from "./transport.js"; import { MIN_CODEX_APP_SERVER_VERSION } from "./version.js"; +/** Minimum supported Codex app-server version exported for callers/tests. */ export { MIN_CODEX_APP_SERVER_VERSION } from "./version.js"; const CODEX_APP_SERVER_PARSE_LOG_MAX = 500; const CODEX_APP_SERVER_PARSE_BUFFER_MAX = 1_000_000; @@ -39,6 +44,7 @@ type PendingRequest = { cleanup: () => void; }; +/** RPC error wrapper that preserves app-server error code and data. */ export class CodexAppServerRpcError extends Error { readonly code?: number; readonly data?: JsonValue; @@ -77,6 +83,7 @@ function isJsonObject(value: unknown): value is { [key: string]: JsonValue } { return Boolean(value && typeof value === "object" && !Array.isArray(value)); } +/** Returns true for errors that mean the app-server transport is closed. */ export function isCodexAppServerConnectionClosedError(error: unknown): boolean { if (!(error instanceof Error)) { return false; @@ -91,10 +98,12 @@ type CodexServerRequestHandler = ( request: Required> & { params?: JsonValue }, ) => Promise | JsonValue | undefined; +/** Notification handler registered on a Codex app-server client. */ export type CodexServerNotificationHandler = ( notification: CodexServerNotification, ) => Promise | void; +/** Stateful app-server JSON-RPC client over stdio or websocket transport. */ export class CodexAppServerClient { private readonly child: CodexAppServerTransport; private readonly lines: ReadlineInterface; @@ -144,6 +153,7 @@ export class CodexAppServerClient { ); } + /** Starts a new app-server client using resolved runtime start options. */ static start(options?: Partial): CodexAppServerClient { const defaults = resolveCodexAppServerRuntimeOptions().start; const startOptions = { @@ -160,10 +170,12 @@ export class CodexAppServerClient { return new CodexAppServerClient(createStdioTransport(startOptions)); } + /** Builds a client around a fake transport for tests. */ static fromTransportForTests(child: CodexAppServerTransport): CodexAppServerClient { return new CodexAppServerClient(child); } + /** Performs the app-server initialize handshake and validates protocol version. */ async initialize(): Promise { if (this.initialized) { return; @@ -185,6 +197,7 @@ export class CodexAppServerClient { this.initialized = true; } + /** Returns the version detected during initialize. */ getServerVersion(): string | undefined { return this.serverVersion; } @@ -269,35 +282,42 @@ export class CodexAppServerClient { }); } + /** Sends a fire-and-forget JSON-RPC notification to the app-server. */ notify(method: string, params?: JsonValue): void { this.writeMessage({ method, params }); } + /** Registers a handler for app-server requests sent back to OpenClaw. */ addRequestHandler(handler: CodexServerRequestHandler): () => void { this.requestHandlers.add(handler); return () => this.requestHandlers.delete(handler); } + /** Registers a notification handler and returns its disposer. */ addNotificationHandler(handler: CodexServerNotificationHandler): () => void { this.notificationHandlers.add(handler); return () => this.notificationHandlers.delete(handler); } + /** Installs a lease-count provider used to route unscoped notifications. */ setActiveSharedLeaseCountProviderForUnscopedNotifications( provider: (() => number | undefined) | undefined, ): void { this.activeSharedLeaseCountProvider = provider; } + /** Reads the active shared-client lease count when available. */ getActiveSharedLeaseCountForUnscopedNotifications(): number | undefined { return this.activeSharedLeaseCountProvider?.(); } + /** Registers a close handler and returns its disposer. */ addCloseHandler(handler: (client: CodexAppServerClient) => void): () => void { this.closeHandlers.add(handler); return () => this.closeHandlers.delete(handler); } + /** Closes the transport without waiting for process/socket shutdown. */ close(): void { if (!this.markClosed(new Error("codex app-server client is closed"))) { return; @@ -305,6 +325,7 @@ export class CodexAppServerClient { closeCodexAppServerTransport(this.child); } + /** Closes the transport and waits for shutdown according to transport policy. */ async closeAndWait(options?: { exitTimeoutMs?: number; forceKillDelayMs?: number; @@ -602,6 +623,7 @@ function assertSupportedCodexAppServerVersion(response: CodexInitializeResponse) return detectedVersion; } +/** Extracts the Codex version from the app-server initialize user-agent field. */ export function readCodexVersionFromUserAgent(userAgent: string | undefined): string | undefined { // Codex returns `/ ...`; the originator can be // OpenClaw, Codex Desktop, or an env override, so only the slash-delimited @@ -612,6 +634,7 @@ export function readCodexVersionFromUserAgent(userAgent: string | undefined): st return match?.[1]; } +/** Compares stable Codex app-server versions for protocol floor checks. */ export function compareCodexAppServerVersions(left: string, right: string): number { const leftVersion = parseVersionForComparison(left); const rightVersion = parseVersionForComparison(right); @@ -712,6 +735,7 @@ const CODEX_APP_SERVER_APPROVAL_REQUEST_METHODS = new Set([ "item/permissions/requestApproval", ]); +/** Returns true for app-server approval request methods OpenClaw can answer. */ export function isCodexAppServerApprovalRequest(method: string): boolean { return CODEX_APP_SERVER_APPROVAL_REQUEST_METHODS.has(method); } @@ -726,6 +750,7 @@ function formatExitValue(value: unknown): string { return "unknown"; } +/** Test-only access to transport close helpers and parser redaction internals. */ export const testing = { closeCodexAppServerTransport, closeCodexAppServerTransportAndWait, diff --git a/extensions/codex/src/app-server/computer-use.ts b/extensions/codex/src/app-server/computer-use.ts index a80695ece157..788330801080 100644 --- a/extensions/codex/src/app-server/computer-use.ts +++ b/extensions/codex/src/app-server/computer-use.ts @@ -1,3 +1,7 @@ +/** + * Computer Use plugin/MCP readiness checks and optional install flow for Codex + * app-server sessions. + */ import { existsSync } from "node:fs"; import { describeControlFailure } from "./capabilities.js"; import type { CodexAppServerClient } from "./client.js"; @@ -18,6 +22,7 @@ import type { } from "./protocol.js"; import { requestCodexAppServerJson } from "./request.js"; +/** Minimal app-server request function needed by Computer Use setup. */ export type CodexComputerUseRequest = ( method: string, params?: unknown, @@ -34,6 +39,7 @@ type CodexComputerUseStatusReason = | "check_failed" | "auto_install_blocked"; +/** Readiness status for Codex Computer Use plugin and MCP server wiring. */ export type CodexComputerUseStatus = { enabled: boolean; ready: boolean; @@ -59,6 +65,7 @@ class CodexComputerUseSetupError extends Error { } } +/** Inputs for checking, ensuring, or installing Codex Computer Use support. */ export type CodexComputerUseSetupParams = { pluginConfig?: unknown; overrides?: Partial; @@ -102,6 +109,7 @@ const COMPUTER_USE_MARKETPLACE_NAME_PRIORITY = ["openai-bundled", "openai-curate const DEFAULT_CODEX_BUNDLED_MARKETPLACE_PATH = "/Applications/Codex.app/Contents/Resources/plugins/openai-bundled"; +/** Reads Computer Use readiness without installing or mutating app-server state. */ export async function readCodexComputerUseStatus( params: CodexComputerUseSetupParams = {}, ): Promise { @@ -124,6 +132,10 @@ export async function readCodexComputerUseStatus( } } +/** + * Ensures Computer Use is ready when enabled, optionally installing when config + * allows safe auto-install. + */ export async function ensureCodexComputerUse( params: CodexComputerUseSetupParams = {}, ): Promise { @@ -160,6 +172,7 @@ export async function ensureCodexComputerUse( return status; } +/** Forces Computer Use plugin installation and returns the ready status. */ export async function installCodexComputerUse( params: CodexComputerUseSetupParams = {}, ): Promise {