UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

710 lines (709 loc) 27.1 kB
import { n as VERSION } from "./version-Crcn9X9T.js"; import { t as log } from "./logger-B8odRltZ.js"; import { a as resolveWindowsSpawnProgram, r as materializeWindowsSpawnProgram } from "./windows-spawn-C3eNecff.js"; import "./agent-harness-runtime-zRYzuZJP.js"; import { d as resolveCodexAppServerRuntimeOptions } from "./config-i2AzQJy8.js"; import { n as isRpcResponse } from "./protocol-oeJQu4rs.js"; import { spawn } from "node:child_process"; import { PassThrough, Writable } from "node:stream"; import WebSocket from "ws"; import { createInterface } from "node:readline"; import { EventEmitter } from "node:events"; //#region extensions/codex/src/app-server/transport-stdio.ts /** * Creates and configures stdio-backed Codex app-server transports, including * Windows spawn normalization and environment filtering. */ const UNSAFE_ENVIRONMENT_KEYS = new Set([ "__proto__", "constructor", "prototype" ]); const DEFAULT_SPAWN_RUNTIME = { platform: process.platform, env: process.env, execPath: process.execPath }; /** Resolves the concrete command/argv/shell settings used to spawn Codex app-server. */ function resolveCodexAppServerSpawnInvocation(options, runtime = DEFAULT_SPAWN_RUNTIME) { if (options.commandSource === "managed") throw new Error("Managed Codex app-server start options must be resolved before spawn."); const resolved = materializeWindowsSpawnProgram(resolveWindowsSpawnProgram({ command: options.command, platform: runtime.platform, env: runtime.env, execPath: runtime.execPath, packageName: "@openai/codex" }), options.args); return { command: resolved.command, args: resolved.argv, shell: resolved.shell, windowsHide: resolved.windowsHide }; } /** Merges app-server environment overrides while honoring clearEnv and unsafe key filtering. */ function resolveCodexAppServerSpawnEnv(options, baseEnv = process.env, platform = process.platform) { const env = Object.create(null); copySafeEnvironmentEntries(env, baseEnv); copySafeEnvironmentEntries(env, options.env ?? {}); const keysToClear = normalizedEnvironmentKeys(options.clearEnv ?? []); if (platform === "win32") { const lowerCaseKeysToClear = new Set(keysToClear.map((key) => key.toLowerCase())); for (const candidate of Object.keys(env)) if (lowerCaseKeysToClear.has(candidate.toLowerCase())) delete env[candidate]; } else for (const key of keysToClear) delete env[key]; return env; } function normalizedEnvironmentKeys(rawKeys) { const keys = []; for (const rawKey of rawKeys) { const key = rawKey.trim(); if (key.length > 0) keys.push(key); } return keys; } function copySafeEnvironmentEntries(target, source) { for (const [key, value] of Object.entries(source)) { if (UNSAFE_ENVIRONMENT_KEYS.has(key)) continue; target[key] = value; } } /** Spawns the Codex app-server process and returns the shared transport interface. */ function createStdioTransport(options) { const env = resolveCodexAppServerSpawnEnv(options); const invocation = resolveCodexAppServerSpawnInvocation(options, { platform: process.platform, env, execPath: process.execPath }); return spawn(invocation.command, invocation.args, { env, detached: process.platform !== "win32", shell: invocation.shell, stdio: [ "pipe", "pipe", "pipe" ], windowsHide: invocation.windowsHide }); } //#endregion //#region extensions/codex/src/app-server/transport-websocket.ts /** * Adapts a remote Codex app-server WebSocket endpoint to the shared stdio-like * transport interface. */ /** Opens a WebSocket app-server transport and maps newline-delimited frames to stdout/stdin. */ function createWebSocketTransport(options) { if (!options.url) throw new Error("codex app-server websocket transport requires plugins.entries.codex.config.appServer.url"); const events = new EventEmitter(); const stdout = new PassThrough(); const stderr = new PassThrough(); const headers = { ...options.headers, ...options.authToken ? { Authorization: `Bearer ${options.authToken}` } : {} }; const socket = new WebSocket(options.url, { headers }); const pendingFrames = []; let killed = false; const sendFrame = (frame) => { const trimmed = frame.trim(); if (!trimmed) return; if (socket.readyState === WebSocket.OPEN) { socket.send(trimmed); return; } pendingFrames.push(trimmed); }; socket.once("open", () => { for (const frame of pendingFrames.splice(0)) socket.send(frame); }); socket.once("error", (error) => events.emit("error", error)); socket.once("close", (code, reason) => { killed = true; events.emit("exit", code, reason.toString("utf8")); }); socket.on("message", (data) => { const text = websocketFrameToText(data); stdout.write(text.endsWith("\n") ? text : `${text}\n`); }); const stdin = new Writable({ write(chunk, _encoding, callback) { for (const frame of chunk.toString("utf8").split("\n")) sendFrame(frame); callback(); } }); const closeSocket = () => { if (socket.readyState === WebSocket.CLOSED || socket.readyState === WebSocket.CLOSING) return; socket.close(); }; stdin.once("finish", closeSocket); stdin.once("close", closeSocket); return { stdin, stdout, stderr, get killed() { return killed; }, kill: () => { killed = true; socket.close(); }, once: (event, listener) => events.once(event, listener) }; } function websocketFrameToText(data) { if (typeof data === "string") return data; if (Buffer.isBuffer(data)) return data.toString("utf8"); if (Array.isArray(data)) return Buffer.concat(data).toString("utf8"); return Buffer.from(data).toString("utf8"); } //#endregion //#region extensions/codex/src/app-server/transport.ts /** Starts graceful transport shutdown and schedules a force kill fallback. */ function closeCodexAppServerTransport(child, options = {}) { child.stdin.end?.(); child.stdin.destroy?.(); const forceKillDelayMs = options.forceKillDelayMs ?? 1e3; const forceKill = setTimeout(() => { if (hasCodexAppServerTransportExited(child)) return; signalCodexAppServerTransport(child, "SIGKILL"); }, Math.max(1, forceKillDelayMs)); forceKill.unref?.(); child.once("exit", () => { clearTimeout(forceKill); child.stdout.destroy?.(); child.stderr.destroy?.(); }); child.unref?.(); child.stdout.unref?.(); child.stderr.unref?.(); child.stdin.unref?.(); } /** Closes a transport and waits briefly for an exit event. */ async function closeCodexAppServerTransportAndWait(child, options = {}) { if (!hasCodexAppServerTransportExited(child)) closeCodexAppServerTransport(child, options); return await waitForCodexAppServerTransportExit(child, options.exitTimeoutMs ?? 2e3); } function hasCodexAppServerTransportExited(child) { return child.exitCode !== null && child.exitCode !== void 0 ? true : child.signalCode !== null && child.signalCode !== void 0; } async function waitForCodexAppServerTransportExit(child, timeoutMs) { if (hasCodexAppServerTransportExited(child)) return true; return await new Promise((resolve) => { let settled = false; const onExit = () => { if (settled) return; settled = true; clearTimeout(timeout); resolve(true); }; const timeout = setTimeout(() => { if (settled) return; settled = true; child.off?.("exit", onExit); resolve(false); }, Math.max(1, timeoutMs)); child.once("exit", onExit); }); } function signalCodexAppServerTransport(child, signal) { if (child.pid && process.platform !== "win32") try { process.kill(-child.pid, signal); return; } catch {} child.kill?.(signal); } //#endregion //#region extensions/codex/src/app-server/version.ts /** * Version and package pins for the managed Codex app-server runtime. */ /** Minimum Codex app-server version supported by the OpenClaw Codex bridge. */ const MIN_CODEX_APP_SERVER_VERSION = "0.125.0"; /** Minimum Codex app-server version that supports sandbox exec-server environments. */ const MIN_CODEX_SANDBOX_EXEC_SERVER_APP_SERVER_VERSION = "0.132.0"; /** npm package name for the managed Codex app-server binary. */ const MANAGED_CODEX_APP_SERVER_PACKAGE = "@openai/codex"; //#endregion //#region extensions/codex/src/app-server/client.ts /** * JSON-RPC client for Codex app-server transports, including request/response * routing, notification fanout, server request handlers, and version checks. */ const CODEX_APP_SERVER_PARSE_LOG_MAX = 500; const CODEX_APP_SERVER_PARSE_BUFFER_MAX = 1e6; const CODEX_APP_SERVER_PARSE_BUFFER_MAX_LINES = 1e3; const CODEX_DYNAMIC_TOOL_SERVER_REQUEST_TIMEOUT_MS = 6e5; const CODEX_APP_SERVER_STDERR_TAIL_MAX = 2e3; const UNPAIRED_SURROGATE_RE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g; /** RPC error wrapper that preserves app-server error code and data. */ var CodexAppServerRpcError = class extends Error { constructor(error, method) { super(formatCodexAppServerRpcErrorMessage(error, method)); this.name = "CodexAppServerRpcError"; this.code = error.code; this.data = error.data; } }; function formatCodexAppServerRpcErrorMessage(error, method) { const message = error.message || `${method} failed`; const detail = readCodexAppServerRpcReloginDetail(error.data); return detail && !message.includes(detail) ? `${message}: ${detail}` : message; } function readCodexAppServerRpcReloginDetail(data) { const record = isJsonObject(data) ? data : void 0; const nested = isJsonObject(record?.error) ? record.error : record; if (!nested) return; const isRelogin = nested.action === "relogin" || nested.reason === "cloudRequirements" && nested.errorCode === "Auth"; const detail = typeof nested.detail === "string" ? nested.detail.trim() : ""; return isRelogin && detail ? detail : void 0; } function isJsonObject(value) { return Boolean(value && typeof value === "object" && !Array.isArray(value)); } /** Returns true for errors that mean the app-server transport is closed. */ function isCodexAppServerConnectionClosedError(error) { if (!(error instanceof Error)) return false; return error.message === "codex app-server client is closed" || error.message.startsWith("codex app-server exited:"); } /** Stateful app-server JSON-RPC client over stdio or websocket transport. */ var CodexAppServerClient = class CodexAppServerClient { constructor(child) { this.pending = /* @__PURE__ */ new Map(); this.requestHandlers = /* @__PURE__ */ new Set(); this.notificationHandlers = /* @__PURE__ */ new Set(); this.closeHandlers = /* @__PURE__ */ new Set(); this.nextId = 1; this.initialized = false; this.closed = false; this.stderrTail = ""; this.child = child; this.lines = createInterface({ input: child.stdout }); this.lines.on("line", (line) => this.handleLine(line)); child.stderr.on("data", (chunk) => { const text = chunk.toString("utf8"); this.stderrTail = appendBoundedTail(this.stderrTail, text, CODEX_APP_SERVER_STDERR_TAIL_MAX); const trimmed = text.trim(); if (trimmed) log.debug(`codex app-server stderr: ${trimmed}`); }); child.once("error", (error) => this.closeWithError(error instanceof Error ? error : new Error(String(error)))); child.once("exit", (code, signal) => { this.closeWithError(buildCodexAppServerExitError(code, signal, this.stderrTail)); }); child.stdin.on?.("error", (error) => this.closeWithError(error instanceof Error ? error : new Error(String(error)))); } /** Starts a new app-server client using resolved runtime start options. */ static start(options) { const defaults = resolveCodexAppServerRuntimeOptions().start; const startOptions = { ...defaults, ...options, headers: options?.headers ?? defaults.headers }; if (startOptions.transport === "stdio" && startOptions.commandSource === "managed") throw new Error("Managed Codex app-server start options must be resolved before spawn."); if (startOptions.transport === "websocket") return new CodexAppServerClient(createWebSocketTransport(startOptions)); return new CodexAppServerClient(createStdioTransport(startOptions)); } /** Builds a client around a fake transport for tests. */ static fromTransportForTests(child) { return new CodexAppServerClient(child); } /** Performs the app-server initialize handshake and validates protocol version. */ async initialize() { if (this.initialized) return; const response = await this.request("initialize", { clientInfo: { name: "openclaw", title: "OpenClaw", version: VERSION }, capabilities: { experimentalApi: true } }); this.serverVersion = assertSupportedCodexAppServerVersion(response); this.notify("initialized"); this.initialized = true; } /** Returns the version detected during initialize. */ getServerVersion() { return this.serverVersion; } request(method, params, optionsInput) { let options = optionsInput; options ??= {}; if (this.closed) return Promise.reject(this.closeError ?? /* @__PURE__ */ new Error("codex app-server client is closed")); if (options.signal?.aborted) return Promise.reject(/* @__PURE__ */ new Error(`${method} aborted`)); const id = this.nextId++; const message = { id, method, params }; return new Promise((resolve, reject) => { let timeout; let cleanupAbort; const cleanup = () => { if (timeout) { clearTimeout(timeout); timeout = void 0; } cleanupAbort?.(); cleanupAbort = void 0; }; const rejectPending = (error) => { if (!this.pending.has(id)) return; this.pending.delete(id); cleanup(); reject(error); }; if (options.timeoutMs && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0) { timeout = setTimeout(() => rejectPending(/* @__PURE__ */ new Error(`${method} timed out`)), Math.max(100, options.timeoutMs)); timeout.unref?.(); } if (options.signal) { const abortListener = () => rejectPending(/* @__PURE__ */ new Error(`${method} aborted`)); options.signal.addEventListener("abort", abortListener, { once: true }); cleanupAbort = () => options.signal?.removeEventListener("abort", abortListener); } this.pending.set(id, { method, resolve: (value) => { cleanup(); resolve(value); }, reject: (error) => { cleanup(); reject(error); }, cleanup }); if (options.signal?.aborted) { rejectPending(/* @__PURE__ */ new Error(`${method} aborted`)); return; } try { this.writeMessage(message, (error) => rejectPending(error)); } catch (error) { rejectPending(error instanceof Error ? error : new Error(String(error))); } }); } /** Sends a fire-and-forget JSON-RPC notification to the app-server. */ notify(method, params) { this.writeMessage({ method, params }); } /** Registers a handler for app-server requests sent back to OpenClaw. */ addRequestHandler(handler) { this.requestHandlers.add(handler); return () => this.requestHandlers.delete(handler); } /** Registers a notification handler and returns its disposer. */ addNotificationHandler(handler) { this.notificationHandlers.add(handler); return () => this.notificationHandlers.delete(handler); } /** Installs a lease-count provider used to route unscoped notifications. */ setActiveSharedLeaseCountProviderForUnscopedNotifications(provider) { this.activeSharedLeaseCountProvider = provider; } /** Reads the active shared-client lease count when available. */ getActiveSharedLeaseCountForUnscopedNotifications() { return this.activeSharedLeaseCountProvider?.(); } /** Registers a close handler and returns its disposer. */ addCloseHandler(handler) { this.closeHandlers.add(handler); return () => this.closeHandlers.delete(handler); } /** Closes the transport without waiting for process/socket shutdown. */ close() { if (!this.markClosed(/* @__PURE__ */ new Error("codex app-server client is closed"))) return; closeCodexAppServerTransport(this.child); } /** Closes the transport and waits for shutdown according to transport policy. */ async closeAndWait(options) { this.markClosed(/* @__PURE__ */ new Error("codex app-server client is closed")); await closeCodexAppServerTransportAndWait(this.child, options); } writeMessage(message, onError) { if (this.closed) return; const id = "id" in message ? message.id : void 0; const method = "method" in message ? message.method : void 0; this.child.stdin.write(`${stringifyCodexAppServerMessage(message)}\n`, (error) => { if (error) { log.warn("codex app-server write failed", { error, id, method }); onError?.(error); } }); } handleLine(line) { const rawLine = line.endsWith("\r") ? line.slice(0, -1) : line; if (this.pendingParse) { this.handlePendingParseLine(rawLine); return; } const trimmed = rawLine.trim(); if (!trimmed) return; let parsed; try { parsed = JSON.parse(trimmed); } catch (error) { if (shouldBufferCodexAppServerParseFailure(trimmed, error)) { this.pendingParse = { text: trimmed, lineCount: 1, firstError: error }; return; } logCodexAppServerParseFailure(trimmed, error, 1); return; } this.handleParsedMessage(parsed); } handlePendingParseLine(line) { const pending = this.pendingParse; if (!pending) return; const candidate = `${pending.text}\\n${line}`; let parsed; try { parsed = JSON.parse(candidate); } catch (error) { const lineCount = pending.lineCount + 1; if (shouldBufferCodexAppServerParseFailure(candidate.trim(), error) && candidate.length <= CODEX_APP_SERVER_PARSE_BUFFER_MAX && lineCount <= CODEX_APP_SERVER_PARSE_BUFFER_MAX_LINES) { this.pendingParse = { text: candidate, lineCount, firstError: pending.firstError }; return; } this.pendingParse = void 0; logCodexAppServerParseFailure(candidate, error, lineCount); return; } this.pendingParse = void 0; this.handleParsedMessage(parsed); } handleParsedMessage(parsed) { if (!parsed || typeof parsed !== "object") return; const message = parsed; if (isRpcResponse(message)) { this.handleResponse(message); return; } if (!("method" in message)) return; if ("id" in message && message.id !== void 0) { this.handleServerRequest({ id: message.id, method: message.method, params: message.params }); return; } this.handleNotification({ method: message.method, params: message.params }); } handleResponse(response) { const pending = this.pending.get(response.id); if (!pending) return; this.pending.delete(response.id); if (response.error) { pending.reject(new CodexAppServerRpcError(response.error, pending.method)); return; } pending.resolve(response.result); } async handleServerRequest(request) { try { const result = await this.runServerRequestHandlers(request); if (result !== void 0) { this.writeMessage({ id: request.id, result }); return; } this.writeMessage({ id: request.id, result: defaultServerRequestResponse(request) }); } catch (error) { this.writeMessage({ id: request.id, error: { message: error instanceof Error ? error.message : String(error) } }); } } async runServerRequestHandlers(request) { const timeoutResponse = timeoutServerRequestResponse(request); if (!timeoutResponse) return await this.runServerRequestHandlersWithoutTimeout(request); let timeout; try { return await Promise.race([this.runServerRequestHandlersWithoutTimeout(request), new Promise((resolve) => { timeout = setTimeout(() => { log.warn("codex app-server server request timed out", { id: request.id, method: request.method, timeoutMs: CODEX_DYNAMIC_TOOL_SERVER_REQUEST_TIMEOUT_MS }); resolve(timeoutResponse); }, CODEX_DYNAMIC_TOOL_SERVER_REQUEST_TIMEOUT_MS); timeout.unref?.(); })]); } finally { if (timeout) clearTimeout(timeout); } } async runServerRequestHandlersWithoutTimeout(request) { for (const handler of this.requestHandlers) { const result = await handler(request); if (result !== void 0) return result; } } handleNotification(notification) { for (const handler of this.notificationHandlers) Promise.resolve(handler(notification)).catch((error) => { log.warn("codex app-server notification handler failed", { error }); }); } closeWithError(error) { if (this.markClosed(error)) closeCodexAppServerTransport(this.child); } markClosed(error) { if (this.closed) return false; this.closed = true; this.closeError = error; this.lines.close(); this.rejectPendingRequests(error); return true; } rejectPendingRequests(error) { for (const pending of this.pending.values()) { pending.cleanup(); pending.reject(error); } this.pending.clear(); for (const handler of this.closeHandlers) handler(this); } }; function defaultServerRequestResponse(request) { if (request.method === "item/tool/call") return { contentItems: [{ type: "inputText", text: "OpenClaw did not register a handler for this app-server tool call." }], success: false }; if (request.method === "item/commandExecution/requestApproval" || request.method === "item/fileChange/requestApproval") return { decision: "decline" }; if (request.method === "item/permissions/requestApproval") return { permissions: {}, scope: "turn" }; if (isCodexAppServerApprovalRequest(request.method)) return { decision: "decline", reason: "OpenClaw codex app-server bridge does not grant native approvals yet." }; if (request.method === "item/tool/requestUserInput") return { answers: {} }; if (request.method === "mcpServer/elicitation/request") return { action: "decline" }; return {}; } function stringifyCodexAppServerMessage(message) { return JSON.stringify(message, (_key, value) => typeof value === "string" ? value.replace(UNPAIRED_SURROGATE_RE, "") : value) ?? "null"; } function timeoutServerRequestResponse(request) { if (request.method !== "item/tool/call") return; return { contentItems: [{ type: "inputText", text: `OpenClaw dynamic tool call timed out after ${CODEX_DYNAMIC_TOOL_SERVER_REQUEST_TIMEOUT_MS}ms before sending a response to Codex.` }], success: false }; } function assertSupportedCodexAppServerVersion(response) { const detectedVersion = readCodexVersionFromUserAgent(response.userAgent); if (!detectedVersion) throw new Error(`Codex app-server ${MIN_CODEX_APP_SERVER_VERSION} or newer is required, but OpenClaw could not determine the running Codex version. Update the configured Codex app-server binary, or remove custom command overrides to use the managed binary.`); if (compareCodexAppServerVersions(detectedVersion, "0.125.0") < 0) throw new Error(`Codex app-server ${MIN_CODEX_APP_SERVER_VERSION} or newer is required, but detected ${detectedVersion}. Update the configured Codex app-server binary, or remove custom command overrides to use the managed binary.`); return detectedVersion; } /** Extracts the Codex version from the app-server initialize user-agent field. */ function readCodexVersionFromUserAgent(userAgent) { return (userAgent?.match(/^[^/]+\/(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)(?:[\s(]|$)/))?.[1]; } /** Compares stable Codex app-server versions for protocol floor checks. */ function compareCodexAppServerVersions(left, right) { const leftVersion = parseVersionForComparison(left); const rightVersion = parseVersionForComparison(right); const leftParts = leftVersion.parts; const rightParts = rightVersion.parts; for (let index = 0; index < Math.max(leftParts.length, rightParts.length); index += 1) { const leftPart = leftParts[index] ?? 0; const rightPart = rightParts[index] ?? 0; if (leftPart !== rightPart) return leftPart < rightPart ? -1 : 1; } if (leftVersion.unstableSuffix && !rightVersion.unstableSuffix) return -1; if (!leftVersion.unstableSuffix && rightVersion.unstableSuffix) return 1; return 0; } function parseVersionForComparison(version) { const hasBuildMetadata = version.includes("+"); const [withoutBuild = version] = version.split("+", 1); const prereleaseIndex = withoutBuild.indexOf("-"); return { parts: (prereleaseIndex >= 0 ? withoutBuild.slice(0, prereleaseIndex) : withoutBuild).split(".").map((part) => Number.parseInt(part, 10)).map((part) => Number.isFinite(part) ? part : 0), unstableSuffix: prereleaseIndex >= 0 || hasBuildMetadata }; } function redactCodexAppServerLinePreview(value) { const redacted = value.replace(/\s+/g, " ").trim().replace(/(Bearer\s+)[A-Za-z0-9._~+/-]+/gi, "$1<redacted>").replace(/("(?:api_?key|authorization|token|access_token|refresh_token)"\s*:\s*")([^"]+)(")/gi, "$1<redacted>$3").replace(/\b([a-z0-9_]*(?:api_?key|authorization|access_token|refresh_token|token))(\s*=\s*)(["']?)[^\s"']+(\3)/gi, "$1$2$3<redacted>$4"); return redacted.length > CODEX_APP_SERVER_PARSE_LOG_MAX ? `${redacted.slice(0, CODEX_APP_SERVER_PARSE_LOG_MAX)}...` : redacted; } function appendBoundedTail(current, next, maxLength) { const combined = `${current}${next}`; return combined.length > maxLength ? combined.slice(combined.length - maxLength) : combined; } function buildCodexAppServerExitError(code, signal, stderrTail) { const stderrPreview = redactCodexAppServerLinePreview(stderrTail); const suffix = stderrPreview ? ` stderr=${JSON.stringify(stderrPreview)}` : ""; return /* @__PURE__ */ new Error(`codex app-server exited: code=${formatExitValue(code)} signal=${formatExitValue(signal)}${suffix}`); } function shouldBufferCodexAppServerParseFailure(value, error) { if (!value.startsWith("{") && !value.startsWith("[")) return false; const message = error instanceof Error ? error.message : String(error); return message.includes("Unterminated string") || message.includes("Unexpected end of JSON input"); } function logCodexAppServerParseFailure(value, error, fragmentCount) { const linePreview = redactCodexAppServerLinePreview(value); const suffix = fragmentCount > 1 ? ` fragments=${fragmentCount}` : ""; log.warn("failed to parse codex app-server message", { error, errorMessage: error instanceof Error ? error.message : String(error), fragmentCount, linePreview, consoleMessage: `failed to parse codex app-server message${suffix}: preview=${JSON.stringify(linePreview)}` }); } const CODEX_APP_SERVER_APPROVAL_REQUEST_METHODS = new Set([ "item/commandExecution/requestApproval", "item/fileChange/requestApproval", "item/permissions/requestApproval" ]); /** Returns true for app-server approval request methods OpenClaw can answer. */ function isCodexAppServerApprovalRequest(method) { return CODEX_APP_SERVER_APPROVAL_REQUEST_METHODS.has(method); } function formatExitValue(value) { if (value === null || value === void 0) return "null"; if (typeof value === "string" || typeof value === "number") return String(value); return "unknown"; } //#endregion export { isCodexAppServerConnectionClosedError as a, resolveCodexAppServerSpawnEnv as c, isCodexAppServerApprovalRequest as i, CodexAppServerRpcError as n, MANAGED_CODEX_APP_SERVER_PACKAGE as o, compareCodexAppServerVersions as r, MIN_CODEX_SANDBOX_EXEC_SERVER_APP_SERVER_VERSION as s, CodexAppServerClient as t };