




















@@ -109,31 +109,26 @@ function createStartOptions(
109109};
110110}
111111112-async function writeCodexCliAuthFile(codexHome: string): Promise<void> {
113-await fs.mkdir(codexHome, { recursive: true });
114-await fs.writeFile(
115-path.join(codexHome, "auth.json"),
116-JSON.stringify({
117-tokens: {
118-access_token: "cli-access-token",
119-refresh_token: "cli-refresh-token",
120-account_id: "cli-account-123",
121-},
122-}),
123-);
124-}
125-126112describe("bridgeCodexAppServerStartOptions", () => {
127-it("clears an inherited OpenAI API key when local Codex CLI OAuth is available", async () => {
113+it("clears inherited API-key env vars when the default Codex profile is subscription auth", async () => {
128114const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-app-server-"));
129-const codexHome = path.join(agentDir, "codex-home");
130115const startOptions = createStartOptions({
131-env: { CODEX_HOME: "/tmp/source-codex-home", EXISTING: "1" },
116+env: { EXISTING: "1" },
132117clearEnv: ["FOO"],
133118});
134-vi.stubEnv("CODEX_HOME", codexHome);
135119try {
136-await writeCodexCliAuthFile(codexHome);
120+upsertAuthProfile({
121+ agentDir,
122+profileId: "openai-codex:default",
123+credential: {
124+type: "oauth",
125+provider: "openai-codex",
126+access: "access-token",
127+refresh: "refresh-token",
128+expires: Date.now() + 24 * 60 * 60_000,
129+accountId: "account-123",
130+},
131+});
137132138133await expect(
139134bridgeCodexAppServerStartOptions({
@@ -142,7 +137,7 @@ describe("bridgeCodexAppServerStartOptions", () => {
142137}),
143138).resolves.toEqual({
144139 ...startOptions,
145-clearEnv: ["FOO", "OPENAI_API_KEY"],
140+clearEnv: ["FOO", "CODEX_API_KEY", "OPENAI_API_KEY"],
146141});
147142expect(startOptions.clearEnv).toEqual(["FOO"]);
148143await expect(fs.access(path.join(agentDir, "harness-auth"))).rejects.toMatchObject({
@@ -178,7 +173,7 @@ describe("bridgeCodexAppServerStartOptions", () => {
178173}),
179174).resolves.toEqual({
180175 ...startOptions,
181-clearEnv: ["FOO", "OPENAI_API_KEY"],
176+clearEnv: ["FOO", "CODEX_API_KEY", "OPENAI_API_KEY"],
182177});
183178} finally {
184179await fs.rm(agentDir, { recursive: true, force: true });
@@ -207,7 +202,7 @@ describe("bridgeCodexAppServerStartOptions", () => {
207202}),
208203).resolves.toEqual({
209204 ...startOptions,
210-clearEnv: ["FOO", "OPENAI_API_KEY"],
205+clearEnv: ["FOO", "CODEX_API_KEY", "OPENAI_API_KEY"],
211206});
212207} finally {
213208await fs.rm(agentDir, { recursive: true, force: true });
@@ -380,6 +375,105 @@ describe("bridgeCodexAppServerStartOptions", () => {
380375}
381376});
382377378+it("falls back to CODEX_API_KEY when no auth profile and no Codex account is available", async () => {
379+const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-app-server-"));
380+const request = vi.fn(async (method: string) => {
381+if (method === "account/read") {
382+return { account: null, requiresOpenaiAuth: true };
383+}
384+return { type: "apiKey" };
385+});
386+vi.stubEnv("CODEX_API_KEY", "codex-env-api-key");
387+vi.stubEnv("OPENAI_API_KEY", "openai-env-api-key");
388+try {
389+await applyCodexAppServerAuthProfile({
390+client: { request } as never,
391+ agentDir,
392+});
393+394+expect(request).toHaveBeenNthCalledWith(1, "account/read", { refreshToken: false });
395+expect(request).toHaveBeenNthCalledWith(2, "account/login/start", {
396+type: "apiKey",
397+apiKey: "codex-env-api-key",
398+});
399+} finally {
400+await fs.rm(agentDir, { recursive: true, force: true });
401+}
402+});
403+404+it("falls back to OPENAI_API_KEY when CODEX_API_KEY is not set", async () => {
405+const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-app-server-"));
406+const request = vi.fn(async (method: string) => {
407+if (method === "account/read") {
408+return { account: null, requiresOpenaiAuth: true };
409+}
410+return { type: "apiKey" };
411+});
412+vi.stubEnv("CODEX_API_KEY", "");
413+vi.stubEnv("OPENAI_API_KEY", "openai-env-api-key");
414+try {
415+await applyCodexAppServerAuthProfile({
416+client: { request } as never,
417+ agentDir,
418+});
419+420+expect(request).toHaveBeenNthCalledWith(1, "account/read", { refreshToken: false });
421+expect(request).toHaveBeenNthCalledWith(2, "account/login/start", {
422+type: "apiKey",
423+apiKey: "openai-env-api-key",
424+});
425+} finally {
426+await fs.rm(agentDir, { recursive: true, force: true });
427+}
428+});
429+430+it("keeps an existing app-server ChatGPT account over env API-key fallback", async () => {
431+const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-app-server-"));
432+const request = vi.fn(async (method: string) => {
433+if (method === "account/read") {
434+return {
435+account: { type: "chatgpt", email: "codex@example.test", planType: "plus" },
436+requiresOpenaiAuth: true,
437+};
438+}
439+return { type: "apiKey" };
440+});
441+vi.stubEnv("CODEX_API_KEY", "codex-env-api-key");
442+try {
443+await applyCodexAppServerAuthProfile({
444+client: { request } as never,
445+ agentDir,
446+});
447+448+expect(request).toHaveBeenCalledTimes(1);
449+expect(request).toHaveBeenCalledWith("account/read", { refreshToken: false });
450+} finally {
451+await fs.rm(agentDir, { recursive: true, force: true });
452+}
453+});
454+455+it("skips env API-key fallback when app-server does not require OpenAI auth", async () => {
456+const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-app-server-"));
457+const request = vi.fn(async (method: string) => {
458+if (method === "account/read") {
459+return { account: null, requiresOpenaiAuth: false };
460+}
461+return { type: "apiKey" };
462+});
463+vi.stubEnv("CODEX_API_KEY", "codex-env-api-key");
464+try {
465+await applyCodexAppServerAuthProfile({
466+client: { request } as never,
467+ agentDir,
468+});
469+470+expect(request).toHaveBeenCalledTimes(1);
471+expect(request).toHaveBeenCalledWith("account/read", { refreshToken: false });
472+} finally {
473+await fs.rm(agentDir, { recursive: true, force: true });
474+}
475+});
476+383477it("applies an OpenAI Codex token profile backed by a secret ref", async () => {
384478const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-app-server-"));
385479const request = vi.fn(async () => ({ type: "chatgptAuthTokens" }));
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。