























@@ -172,6 +172,42 @@ function createCodexOAuthAuthStore() {
172172};
173173}
174174175+function createCodexApiKeyAuthStore() {
176+return {
177+version: 1 as const,
178+profiles: {
179+"openai-codex:manual": {
180+type: "api_key" as const,
181+provider: "openai-codex",
182+key: "codex-api-key",
183+},
184+},
185+};
186+}
187+188+function createCodexTokenAuthStore() {
189+return {
190+version: 1 as const,
191+profiles: {
192+"openai-codex:token": {
193+type: "token" as const,
194+provider: "openai-codex",
195+token: "codex-token",
196+},
197+},
198+};
199+}
200+201+function createMixedCodexAuthStore() {
202+return {
203+version: 1 as const,
204+profiles: {
205+ ...createCodexTokenAuthStore().profiles,
206+ ...createCodexApiKeyAuthStore().profiles,
207+},
208+};
209+}
210+175211type MockWithCalls = {
176212mock: {
177213calls: readonly (readonly unknown[])[];
@@ -841,6 +877,180 @@ describe("openai image generation provider", () => {
841877});
842878});
843879880+it("does not treat Codex API key profiles as configured Codex OAuth image auth", async () => {
881+mockGeneratedPngResponse();
882+resolveApiKeyForProviderMock.mockImplementation(async (params?: { provider?: string }) => {
883+if (params?.provider === "openai") {
884+return { apiKey: "openai-key", source: "profile:openai", mode: "api-key" };
885+}
886+throw new Error(`Unexpected auth provider ${params?.provider ?? ""}`);
887+});
888+889+const provider = buildOpenAIImageGenerationProvider();
890+await provider.generateImage({
891+provider: "openai",
892+model: "gpt-image-2",
893+prompt: "Draw with OpenAI auth despite a Codex API key profile",
894+cfg: {},
895+authStore: createCodexApiKeyAuthStore(),
896+});
897+898+expect(resolveApiKeyForProviderMock).toHaveBeenCalledTimes(1);
899+expect(authResolutionCall().provider).toBe("openai");
900+expect(jsonRequestCall().url).toBe("https://api.openai.com/v1/images/generations");
901+expect(httpConfigCall().provider).toBe("openai");
902+expect(logInfoMock).not.toHaveBeenCalledWith(
903+expect.stringContaining("transport=codex-responses"),
904+);
905+});
906+907+it("uses native OpenAI image requests when only Codex API key auth is available", async () => {
908+mockGeneratedPngResponse();
909+resolveApiKeyForProviderMock.mockImplementation(async (params?: { provider?: string }) => {
910+if (params?.provider === "openai") {
911+return {};
912+}
913+if (params?.provider === "openai-codex") {
914+return {
915+apiKey: "codex-api-key",
916+source: "profile:openai-codex:manual",
917+mode: "api-key",
918+};
919+}
920+return {};
921+});
922+923+const provider = buildOpenAIImageGenerationProvider();
924+await provider.generateImage({
925+provider: "openai",
926+model: "gpt-image-2",
927+prompt: "Draw through Codex API key auth",
928+cfg: {},
929+authStore: createCodexApiKeyAuthStore(),
930+});
931+932+expect(resolveApiKeyForProviderMock).toHaveBeenCalledTimes(2);
933+expect(authResolutionCall(0).provider).toBe("openai");
934+expect(authResolutionCall(1).provider).toBe("openai-codex");
935+const configCall = httpConfigCall();
936+expect(configCall.defaultBaseUrl).toBe("https://api.openai.com/v1");
937+expect(configCall.defaultHeaders).toEqual({
938+Authorization: "Bearer codex-api-key",
939+});
940+expect(configCall.provider).toBe("openai");
941+expect(configCall.api).toBeUndefined();
942+expect(jsonRequestCall().url).toBe("https://api.openai.com/v1/images/generations");
943+expect(postMultipartRequestMock).not.toHaveBeenCalled();
944+expect(logInfoMock).not.toHaveBeenCalledWith(
945+expect.stringContaining("transport=codex-responses"),
946+);
947+});
948+949+it("uses native OpenAI image requests when mixed Codex profiles resolve to an API key", async () => {
950+mockGeneratedPngResponse();
951+resolveApiKeyForProviderMock.mockImplementation(async (params?: { provider?: string }) => {
952+if (params?.provider === "openai-codex") {
953+return {
954+apiKey: "codex-api-key",
955+source: "profile:openai-codex:manual",
956+mode: "api-key",
957+};
958+}
959+throw new Error(`Unexpected auth provider ${params?.provider ?? ""}`);
960+});
961+962+const provider = buildOpenAIImageGenerationProvider();
963+await provider.generateImage({
964+provider: "openai",
965+model: "gpt-image-2",
966+prompt: "Draw through resolved Codex API key auth",
967+cfg: {},
968+authStore: createMixedCodexAuthStore(),
969+});
970+971+expect(resolveApiKeyForProviderMock).toHaveBeenCalledTimes(1);
972+expect(authResolutionCall().provider).toBe("openai-codex");
973+expect(httpConfigCall().defaultBaseUrl).toBe("https://api.openai.com/v1");
974+expect(httpConfigCall().defaultHeaders).toEqual({
975+Authorization: "Bearer codex-api-key",
976+});
977+expect(httpConfigCall().provider).toBe("openai");
978+expect(jsonRequestCall().url).toBe("https://api.openai.com/v1/images/generations");
979+expect(logInfoMock).not.toHaveBeenCalledWith(
980+expect.stringContaining("transport=codex-responses"),
981+);
982+});
983+984+it("keeps Codex token auth on the Codex image transport", async () => {
985+mockCodexImageStream({ imageData: "codex-token-image" });
986+resolveApiKeyForProviderMock.mockImplementation(async (params?: { provider?: string }) => {
987+if (params?.provider === "openai") {
988+return {};
989+}
990+if (params?.provider === "openai-codex") {
991+return {
992+apiKey: "codex-token",
993+source: "profile:openai-codex:token",
994+mode: "token",
995+};
996+}
997+return {};
998+});
999+1000+const provider = buildOpenAIImageGenerationProvider();
1001+const result = await provider.generateImage({
1002+provider: "openai",
1003+model: "gpt-image-2",
1004+prompt: "Draw through Codex token auth",
1005+cfg: {},
1006+authStore: createCodexTokenAuthStore(),
1007+});
1008+1009+expect(resolveApiKeyForProviderMock).toHaveBeenCalledTimes(1);
1010+expect(authResolutionCall().provider).toBe("openai-codex");
1011+expect(httpConfigCall().defaultBaseUrl).toBe("https://chatgpt.com/backend-api/codex");
1012+expect(httpConfigCall().provider).toBe("openai-codex");
1013+expect(httpConfigCall().api).toBe("openai-codex-responses");
1014+expect(jsonRequestCall().url).toBe("https://chatgpt.com/backend-api/codex/responses");
1015+expect(postMultipartRequestMock).not.toHaveBeenCalled();
1016+expect(result.images[0]?.buffer).toEqual(Buffer.from("codex-token-image"));
1017+});
1018+1019+it("uses configured Codex token auth before probing an available OpenAI API key", async () => {
1020+mockCodexImageStream({ imageData: "codex-token-image" });
1021+resolveApiKeyForProviderMock.mockImplementation(async (params?: { provider?: string }) => {
1022+if (params?.provider === "openai") {
1023+return { apiKey: "openai-key", source: "OPENAI_API_KEY", mode: "api-key" };
1024+}
1025+if (params?.provider === "openai-codex") {
1026+return {
1027+apiKey: "codex-token",
1028+source: "profile:openai-codex:token",
1029+mode: "token",
1030+};
1031+}
1032+return {};
1033+});
1034+1035+const provider = buildOpenAIImageGenerationProvider();
1036+await provider.generateImage({
1037+provider: "openai",
1038+model: "gpt-image-2",
1039+prompt: "Draw using configured Codex token auth",
1040+cfg: {},
1041+authStore: createCodexTokenAuthStore(),
1042+});
1043+1044+expect(resolveApiKeyForProviderMock).toHaveBeenCalledTimes(1);
1045+expect(authResolutionCall().provider).toBe("openai-codex");
1046+expect(
1047+resolveApiKeyForProviderMock.mock.calls.some(
1048+([call]) => (call as AuthResolutionCall).provider === "openai",
1049+),
1050+).toBe(false);
1051+expect(jsonRequestCall().url).toBe("https://chatgpt.com/backend-api/codex/responses");
1052+});
1053+8441054it("routes transparent default-model Codex OAuth requests to the alpha-capable image model", async () => {
8451055mockCodexAuthOnly();
8461056mockCodexImageStream({ imageData: "codex-transparent-image" });
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。