From f0ef3070faf62b44b13a64321f10d78519258dee Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 20 Apr 2026 16:57:34 +0100 Subject: [PATCH] refactor: share codex app-server client factory --- .../codex/src/app-server/client-factory.ts | 26 +++++++++++++++++ extensions/codex/src/app-server/compact.ts | 27 ++++++------------ .../codex/src/app-server/run-attempt.ts | 28 +++++++------------ .../app-server/transport-websocket.test.ts | 6 ++-- 4 files changed, 48 insertions(+), 39 deletions(-) create mode 100644 extensions/codex/src/app-server/client-factory.ts diff --git a/extensions/codex/src/app-server/client-factory.ts b/extensions/codex/src/app-server/client-factory.ts new file mode 100644 index 00000000000..a28b2ad275f --- /dev/null +++ b/extensions/codex/src/app-server/client-factory.ts @@ -0,0 +1,26 @@ +import type { CodexAppServerClient } from "./client.js"; +import type { CodexAppServerStartOptions } from "./config.js"; +import { getSharedCodexAppServerClient } from "./shared-client.js"; + +export type CodexAppServerClientFactory = ( + startOptions?: CodexAppServerStartOptions, + authProfileId?: string, +) => Promise; + +export const defaultCodexAppServerClientFactory: CodexAppServerClientFactory = ( + startOptions, + authProfileId, +) => getSharedCodexAppServerClient({ startOptions, authProfileId }); + +export function createCodexAppServerClientFactoryTestHooks( + setFactory: (factory: CodexAppServerClientFactory) => void, +) { + return { + setCodexAppServerClientFactoryForTests(factory: CodexAppServerClientFactory): void { + setFactory(factory); + }, + resetCodexAppServerClientFactoryForTests(): void { + setFactory(defaultCodexAppServerClientFactory); + }, + } as const; +} diff --git a/extensions/codex/src/app-server/compact.ts b/extensions/codex/src/app-server/compact.ts index bbf804e45d5..f842532138e 100644 --- a/extensions/codex/src/app-server/compact.ts +++ b/extensions/codex/src/app-server/compact.ts @@ -3,16 +3,14 @@ import { type CompactEmbeddedPiSessionParams, type EmbeddedPiCompactResult, } from "openclaw/plugin-sdk/agent-harness"; +import { + createCodexAppServerClientFactoryTestHooks, + defaultCodexAppServerClientFactory, +} from "./client-factory.js"; import type { CodexAppServerClient, CodexServerNotificationHandler } from "./client.js"; -import { resolveCodexAppServerRuntimeOptions, type CodexAppServerStartOptions } from "./config.js"; +import { resolveCodexAppServerRuntimeOptions } from "./config.js"; import { isJsonObject, type CodexServerNotification, type JsonObject } from "./protocol.js"; import { readCodexAppServerBinding } from "./session-binding.js"; -import { getSharedCodexAppServerClient } from "./shared-client.js"; - -type CodexAppServerClientFactory = ( - startOptions?: CodexAppServerStartOptions, - authProfileId?: string, -) => Promise; type CodexNativeCompactionCompletion = { signal: "thread/compacted" | "item/completed"; turnId?: string; @@ -26,8 +24,7 @@ type CodexNativeCompactionWaiter = { const DEFAULT_CODEX_COMPACTION_WAIT_TIMEOUT_MS = 5 * 60 * 1000; -let clientFactory: CodexAppServerClientFactory = (startOptions, authProfileId) => - getSharedCodexAppServerClient({ startOptions, authProfileId }); +let clientFactory = defaultCodexAppServerClientFactory; export async function maybeCompactCodexAppServerSession( params: CompactEmbeddedPiSessionParams, @@ -219,12 +216,6 @@ function formatCompactionError(error: unknown): string { return String(error); } -export const __testing = { - setCodexAppServerClientFactoryForTests(factory: CodexAppServerClientFactory): void { - clientFactory = factory; - }, - resetCodexAppServerClientFactoryForTests(): void { - clientFactory = (startOptions, authProfileId) => - getSharedCodexAppServerClient({ startOptions, authProfileId }); - }, -} as const; +export const __testing = createCodexAppServerClientFactoryTestHooks((factory) => { + clientFactory = factory; +}); diff --git a/extensions/codex/src/app-server/run-attempt.ts b/extensions/codex/src/app-server/run-attempt.ts index 151265b5777..9613b3e3a60 100644 --- a/extensions/codex/src/app-server/run-attempt.ts +++ b/extensions/codex/src/app-server/run-attempt.ts @@ -18,8 +18,12 @@ import { type EmbeddedRunAttemptResult, } from "openclaw/plugin-sdk/agent-harness"; import { handleCodexAppServerApprovalRequest } from "./approval-bridge.js"; +import { + createCodexAppServerClientFactoryTestHooks, + defaultCodexAppServerClientFactory, +} from "./client-factory.js"; import { isCodexAppServerApprovalRequest, type CodexAppServerClient } from "./client.js"; -import { resolveCodexAppServerRuntimeOptions, type CodexAppServerStartOptions } from "./config.js"; +import { resolveCodexAppServerRuntimeOptions } from "./config.js"; import { createCodexDynamicToolBridge } from "./dynamic-tools.js"; import { CodexAppServerEventProjector } from "./event-projector.js"; import { @@ -31,17 +35,11 @@ import { type JsonValue, } from "./protocol.js"; import { readCodexAppServerBinding, type CodexAppServerThreadBinding } from "./session-binding.js"; -import { clearSharedCodexAppServerClient, getSharedCodexAppServerClient } from "./shared-client.js"; +import { clearSharedCodexAppServerClient } from "./shared-client.js"; import { buildTurnStartParams, startOrResumeThread } from "./thread-lifecycle.js"; import { mirrorCodexAppServerTranscript } from "./transcript-mirror.js"; -type CodexAppServerClientFactory = ( - startOptions?: CodexAppServerStartOptions, - authProfileId?: string, -) => Promise; - -let clientFactory: CodexAppServerClientFactory = (startOptions, authProfileId) => - getSharedCodexAppServerClient({ startOptions, authProfileId }); +let clientFactory = defaultCodexAppServerClientFactory; export async function runCodexAppServerAttempt( params: EmbeddedRunAttemptParams, @@ -487,12 +485,6 @@ function handleApprovalRequest(params: { }); } -export const __testing = { - setCodexAppServerClientFactoryForTests(factory: CodexAppServerClientFactory): void { - clientFactory = factory; - }, - resetCodexAppServerClientFactoryForTests(): void { - clientFactory = (startOptions, authProfileId) => - getSharedCodexAppServerClient({ startOptions, authProfileId }); - }, -} as const; +export const __testing = createCodexAppServerClientFactoryTestHooks((factory) => { + clientFactory = factory; +}); diff --git a/extensions/codex/src/app-server/transport-websocket.test.ts b/extensions/codex/src/app-server/transport-websocket.test.ts index b552f113672..14c805f8ad4 100644 --- a/extensions/codex/src/app-server/transport-websocket.test.ts +++ b/extensions/codex/src/app-server/transport-websocket.test.ts @@ -61,11 +61,11 @@ describe("Codex app-server websocket transport", () => { }); function rawDataToText(data: RawData): string { - if (Buffer.isBuffer(data)) { - return data.toString("utf8"); - } if (Array.isArray(data)) { return Buffer.concat(data).toString("utf8"); } + if (data instanceof ArrayBuffer) { + return Buffer.from(new Uint8Array(data)).toString("utf8"); + } return Buffer.from(data).toString("utf8"); }