mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:20:43 +00:00
104 lines
3.2 KiB
TypeScript
104 lines
3.2 KiB
TypeScript
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;
|
|
}): Promise<CodexAppServerClient> {
|
|
const state = getSharedCodexAppServerClientState();
|
|
const startOptions = options?.startOptions ?? resolveCodexAppServerRuntimeOptions().start;
|
|
const key = codexAppServerStartOptionsKey(startOptions);
|
|
if (state.key && state.key !== key) {
|
|
clearSharedCodexAppServerClient();
|
|
}
|
|
state.key = key;
|
|
state.promise ??= (async () => {
|
|
const client = CodexAppServerClient.start(startOptions);
|
|
state.client = client;
|
|
client.addCloseHandler(clearSharedClientIfCurrent);
|
|
try {
|
|
await client.initialize();
|
|
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(
|
|
state.promise,
|
|
options?.timeoutMs ?? 0,
|
|
"codex app-server initialize timed out",
|
|
);
|
|
} catch (error) {
|
|
clearSharedCodexAppServerClient();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
export async function createIsolatedCodexAppServerClient(options?: {
|
|
startOptions?: CodexAppServerStartOptions;
|
|
timeoutMs?: number;
|
|
}): Promise<CodexAppServerClient> {
|
|
const startOptions = options?.startOptions ?? resolveCodexAppServerRuntimeOptions().start;
|
|
const client = CodexAppServerClient.start(startOptions);
|
|
const initialize = client.initialize();
|
|
try {
|
|
await withTimeout(initialize, options?.timeoutMs ?? 0, "codex app-server initialize timed out");
|
|
return client;
|
|
} catch (error) {
|
|
client.close();
|
|
await 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;
|
|
}
|