



















@@ -1,6 +1,7 @@
11import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
22import { WebSocketServer, type RawData } from "ws";
33import { CodexAppServerClient, MIN_CODEX_APP_SERVER_VERSION } from "./client.js";
4+import { codexAppServerStartOptionsKey } from "./config.js";
45import { createClientHarness } from "./test-support.js";
5667const mocks = vi.hoisted(() => ({
@@ -274,6 +275,41 @@ describe("shared Codex app-server client", () => {
274275expect(applyCall?.authProfileId).toBe("openai-codex:work");
275276});
276277278+it("migrates legacy singleton global state into the keyed registry", async () => {
279+const legacy = createClientHarness();
280+const next = createClientHarness();
281+const startOptions = {
282+transport: "websocket" as const,
283+command: "codex",
284+args: [],
285+url: "ws://127.0.0.1:39175",
286+authToken: "tok-legacy",
287+headers: {},
288+};
289+const key = codexAppServerStartOptionsKey(startOptions, {
290+agentDir: "/tmp/openclaw-agent",
291+});
292+const globalState = globalThis as typeof globalThis & {
293+[key: symbol]: unknown;
294+};
295+globalState[Symbol.for("openclaw.codexAppServerClientState")] = {
296+ key,
297+client: legacy.client,
298+promise: Promise.resolve(legacy.client),
299+};
300+301+await expect(getSharedCodexAppServerClient({ startOptions })).resolves.toBe(legacy.client);
302+303+legacy.client.close();
304+const startSpy = vi.spyOn(CodexAppServerClient, "start").mockReturnValue(next.client);
305+const list = listCodexAppServerModels({ timeoutMs: 1000, startOptions });
306+await sendInitializeResult(next, "openclaw/0.125.0 (macOS; test)");
307+await sendEmptyModelList(next);
308+309+await expect(list).resolves.toEqual({ models: [] });
310+expect(startSpy).toHaveBeenCalledTimes(1);
311+});
312+277313it("keeps an active shared client alive when another agent dir uses a different key", async () => {
278314const first = createClientHarness();
279315const second = createClientHarness();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。