import { resolveOpenClawAgentDir } from "openclaw/plugin-sdk/provider-auth" ;
import { applyCodexAppServerAuthProfile, bridgeCodexAppServerStartOptions } from "./auth-bridge.js" ;
import { CodexAppServerClient } from "./client.js" ;
import {
codexAppServerStartOptionsKey,
resolveCodexAppServerRuntimeOptions,
type CodexAppServerStartOptions,
} from "./config.js" ;
import { withTimeout } from "./timeout.js" ;
type SharedCodexAppServerClientState = {
client?: CodexAppServerClient;
promise?: Promise<CodexAppServerClient>;
key?: string;
};
const SHARED_CODEX_APP_SERVER_CLIENT_STATE = Symbol.for ("openclaw.codexAppServerClientState" );
function getSharedCodexAppServerClientState(): SharedCodexAppServerClientState {
const globalState = globalThis as typeof globalThis & {
[SHARED_CODEX_APP_SERVER_CLIENT_STATE]?: SharedCodexAppServerClientState;
};
globalState[SHARED_CODEX_APP_SERVER_CLIENT_STATE] ??= {};
return globalState[SHARED_CODEX_APP_SERVER_CLIENT_STATE];
}
export async function getSharedCodexAppServerClient(options?: {
startOptions?: CodexAppServerStartOptions;
timeoutMs?: number;
authProfileId?: string;
}): Promise<CodexAppServerClient> {
const state = getSharedCodexAppServerClientState();
const startOptions = await bridgeCodexAppServerStartOptions({
startOptions: options?.startOptions ?? resolveCodexAppServerRuntimeOptions().start,
agentDir: resolveOpenClawAgentDir(),
authProfileId: options?.authProfileId,
});
const key = codexAppServerStartOptionsKey(startOptions, {
authProfileId: options?.authProfileId,
});
if (state.key && state.key !== key) {
clearSharedCodexAppServerClient();
}
state.key = key;
const sharedPromise =
state.promise ??
(state.promise = (async () => {
const client = CodexAppServerClient.start(startOptions);
state.client = client;
client.addCloseHandler(clearSharedClientIfCurrent);
try {
await client.initialize();
await applyCodexAppServerAuthProfile({
client,
agentDir: resolveOpenClawAgentDir(),
authProfileId: options?.authProfileId,
});
return client;
} catch (error) {
// Startup failures happen before callers own the shared client, so close
// the child here instead of leaving a rejected daemon attached to stdio.
client.close();
throw error;
}
})());
try {
return await withTimeout(
sharedPromise,
options?.timeoutMs ?? 0 ,
"codex app-server initialize timed out" ,
);
} catch (error) {
if (state.promise === sharedPromise && state.key === key) {
clearSharedCodexAppServerClient();
}
throw error;
}
}
export async function createIsolatedCodexAppServerClient(options?: {
startOptions?: CodexAppServerStartOptions;
timeoutMs?: number;
authProfileId?: string;
}): Promise<CodexAppServerClient> {
const startOptions = await bridgeCodexAppServerStartOptions({
startOptions: options?.startOptions ?? resolveCodexAppServerRuntimeOptions().start,
agentDir: resolveOpenClawAgentDir(),
authProfileId: options?.authProfileId,
});
const client = CodexAppServerClient.start(startOptions);
const initialize = client.initialize();
try {
await withTimeout(initialize, options?.timeoutMs ?? 0 , "codex app-server initialize timed out" );
await applyCodexAppServerAuthProfile({
client,
agentDir: resolveOpenClawAgentDir(),
authProfileId: options?.authProfileId,
});
return client;
} catch (error) {
client.close();
void initialize.catch (() => undefined);
throw error;
}
}
export function resetSharedCodexAppServerClientForTests(): void {
const state = getSharedCodexAppServerClientState();
state.client = undefined;
state.promise = undefined;
state.key = undefined;
}
export function clearSharedCodexAppServerClient(): void {
const state = getSharedCodexAppServerClientState();
const client = state.client;
state.client = undefined;
state.promise = undefined;
state.key = undefined;
client?.close();
}
function clearSharedClientIfCurrent(client: CodexAppServerClient): void {
const state = getSharedCodexAppServerClientState();
if (state.client !== client) {
return ;
}
state.client = undefined;
state.promise = undefined;
state.key = undefined;
}
Messung V0.5 in Prozent C=95 H=95 G=94
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-06-05)
¤
*© Formatika GbR, Deutschland