@openai-oauth/core 2.0.0-beta.1 → 2.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,6 +27,8 @@ const baseURL = transport.baseURL;
27
27
  const fetch = transport.fetch;
28
28
  ```
29
29
 
30
+ The transport supports Responses, Chat Completions, model discovery, image generation, and multipart image editing.
31
+
30
32
  Create an OAuth request:
31
33
 
32
34
  ```ts
package/dist/auth.d.ts CHANGED
@@ -2,6 +2,7 @@ import { type FetchFunction, type OpenAIOAuthTokenResponse } from "./runtime.js"
2
2
  export type EffectiveAuth = {
3
3
  accessToken: string;
4
4
  accountId: string;
5
+ isFedRamp?: boolean;
5
6
  idToken?: string;
6
7
  refreshToken?: string;
7
8
  sourcePath?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAGA,OAAO,EAIN,KAAK,aAAa,EAClB,KAAK,wBAAwB,EAG7B,MAAM,cAAc,CAAA;AAuBrB,MAAM,MAAM,aAAa,GAAG;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,EAAE,aAAa,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IACnC,KAAK,EAAE,wBAAwB,CAAA;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,aAAa,CAAA;CACnB,CAAA;AAkDD,eAAO,MAAM,uBAAuB,GACnC,MAAM,IAAI,CAAC,aAAa,EAAE,aAAa,GAAG,aAAa,CAAC,EACxD,MAAK,IAAiB,KACpB,OAA4E,CAAA;AAgB/E,eAAO,MAAM,yBAAyB,GAAI,eAAe,MAAM,KAAG,MAAM,EAmBvE,CAAA;AAgFD,eAAO,MAAM,wBAAwB,GAAI,eAAe,MAAM,KAAG,MAUhE,CAAA;AAED,eAAO,MAAM,cAAc,GAC1B,SAAS,qBAAqB,KAC5B,OAAO,CAAC,eAAe,CAsCzB,CAAA;AAyCD,eAAO,MAAM,cAAc,GAC1B,SAAS,iBAAiB,KACxB,OAAO,CAAC,aAAa,CAkFvB,CAAA"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAGA,OAAO,EAKN,KAAK,aAAa,EAClB,KAAK,wBAAwB,EAG7B,MAAM,cAAc,CAAA;AAwBrB,MAAM,MAAM,aAAa,GAAG;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,EAAE,aAAa,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IACnC,KAAK,EAAE,wBAAwB,CAAA;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,aAAa,CAAA;CACnB,CAAA;AAmDD,eAAO,MAAM,uBAAuB,GACnC,MAAM,IAAI,CAAC,aAAa,EAAE,aAAa,GAAG,aAAa,CAAC,EACxD,MAAK,IAAiB,KACpB,OAA4E,CAAA;AAgB/E,eAAO,MAAM,yBAAyB,GAAI,eAAe,MAAM,KAAG,MAAM,EAmBvE,CAAA;AAsFD,eAAO,MAAM,wBAAwB,GAAI,eAAe,MAAM,KAAG,MAUhE,CAAA;AAED,eAAO,MAAM,cAAc,GAC1B,SAAS,qBAAqB,KAC5B,OAAO,CAAC,eAAe,CAwCzB,CAAA;AAsCD,eAAO,MAAM,cAAc,GAC1B,SAAS,iBAAiB,KACxB,OAAO,CAAC,aAAa,CA0FvB,CAAA"}
package/dist/auth.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { promises as fs } from "node:fs";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
- import { DEFAULT_OPENAI_OAUTH_CLIENT_ID, DEFAULT_OPENAI_OAUTH_ISSUER, deriveAccountId, parseJwtClaims, refreshOpenAIOAuthTokens, } from "./runtime.js";
4
+ import { DEFAULT_OPENAI_OAUTH_CLIENT_ID, DEFAULT_OPENAI_OAUTH_ISSUER, deriveAccountId, deriveChatGptAccountIsFedRamp, parseJwtClaims, refreshOpenAIOAuthTokens, } from "./runtime.js";
5
5
  const DEFAULT_AUTH_CLIENT_ID = process.env.CHATGPT_LOCAL_CLIENT_ID ?? DEFAULT_OPENAI_OAUTH_CLIENT_ID;
6
6
  const DEFAULT_AUTH_ISSUER = process.env.CHATGPT_LOCAL_ISSUER ?? DEFAULT_OPENAI_OAUTH_ISSUER;
7
7
  const AUTH_FILENAME = "auth.json";
@@ -70,11 +70,14 @@ const resolveWritePath = (preferred) => {
70
70
  return path.join(os.homedir(), ".chatgpt-local", AUTH_FILENAME);
71
71
  };
72
72
  const toAuthFile = (input) => {
73
- const auth = {};
73
+ const auth = { ...input };
74
74
  const tokensValue = input.tokens;
75
75
  if (typeof input.OPENAI_API_KEY === "string" && input.OPENAI_API_KEY) {
76
76
  auth.OPENAI_API_KEY = input.OPENAI_API_KEY;
77
77
  }
78
+ else {
79
+ delete auth.OPENAI_API_KEY;
80
+ }
78
81
  if (isRecord(tokensValue) && Object.keys(tokensValue).length > 0) {
79
82
  auth.tokens = {
80
83
  id_token: typeof tokensValue.id_token === "string"
@@ -91,9 +94,15 @@ const toAuthFile = (input) => {
91
94
  : undefined,
92
95
  };
93
96
  }
97
+ else {
98
+ delete auth.tokens;
99
+ }
94
100
  if (typeof input.last_refresh === "string" && input.last_refresh) {
95
101
  auth.last_refresh = input.last_refresh;
96
102
  }
103
+ else {
104
+ delete auth.last_refresh;
105
+ }
97
106
  return auth;
98
107
  };
99
108
  const readAuthFile = async (candidates) => {
@@ -139,6 +148,7 @@ export const saveAuthTokens = async (options) => {
139
148
  }
140
149
  await writeAuthFile(filePath, {
141
150
  ...existing,
151
+ auth_mode: "chatgpt",
142
152
  tokens: {
143
153
  id_token: options.token.idToken,
144
154
  access_token: options.token.accessToken,
@@ -152,6 +162,7 @@ export const saveAuthTokens = async (options) => {
152
162
  auth: {
153
163
  accessToken: options.token.accessToken,
154
164
  accountId,
165
+ isFedRamp: options.token.isFedRamp,
155
166
  idToken: options.token.idToken,
156
167
  refreshToken: options.token.refreshToken,
157
168
  sourcePath: filePath,
@@ -160,24 +171,20 @@ export const saveAuthTokens = async (options) => {
160
171
  };
161
172
  };
162
173
  const refreshChatGptTokens = async (refreshToken, clientId, issuer, tokenUrl, fetchFn) => {
163
- try {
164
- const refreshed = await refreshOpenAIOAuthTokens({
165
- refreshToken,
166
- clientId,
167
- issuer,
168
- tokenUrl,
169
- fetch: fetchFn,
170
- });
171
- return {
172
- accessToken: refreshed.accessToken,
173
- idToken: refreshed.idToken,
174
- refreshToken: refreshed.refreshToken ?? refreshToken,
175
- accountId: refreshed.accountId,
176
- };
177
- }
178
- catch {
179
- return undefined;
180
- }
174
+ const refreshed = await refreshOpenAIOAuthTokens({
175
+ refreshToken,
176
+ clientId,
177
+ issuer,
178
+ tokenUrl,
179
+ fetch: fetchFn,
180
+ });
181
+ return {
182
+ accessToken: refreshed.accessToken,
183
+ idToken: refreshed.idToken,
184
+ refreshToken: refreshed.refreshToken ?? refreshToken,
185
+ accountId: refreshed.accountId,
186
+ isFedRamp: refreshed.isFedRamp,
187
+ };
181
188
  };
182
189
  const normalizeTokens = (tokens) => {
183
190
  const maybeString = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
@@ -200,30 +207,36 @@ export const loadAuthTokens = async (options) => {
200
207
  let idToken = tokens.id_token;
201
208
  let refreshToken = tokens.refresh_token;
202
209
  let accountId = tokens.account_id ?? deriveAccountId(idToken);
210
+ let isFedRamp = deriveChatGptAccountIsFedRamp(idToken) ||
211
+ deriveChatGptAccountIsFedRamp(accessToken);
203
212
  let lastRefresh = authData.last_refresh;
204
213
  const needsRefresh = ensureFresh &&
205
214
  typeof refreshToken === "string" &&
206
215
  shouldRefreshAccessToken(accessToken, lastRefresh, now());
207
216
  if (needsRefresh && typeof refreshToken === "string") {
208
217
  const refreshed = await refreshChatGptTokens(refreshToken, clientId, issuer, tokenUrl, fetch);
209
- if (refreshed) {
210
- accessToken = refreshed.accessToken;
211
- idToken = refreshed.idToken ?? idToken;
212
- refreshToken = refreshed.refreshToken ?? refreshToken;
213
- accountId = refreshed.accountId ?? accountId;
214
- lastRefresh = now().toISOString();
215
- const writePath = resolveWritePath(readResult.path ?? authFilePath);
216
- await writeAuthFile(writePath, {
217
- ...authData,
218
- tokens: {
219
- id_token: idToken,
220
- access_token: accessToken,
221
- refresh_token: refreshToken,
222
- account_id: accountId,
223
- },
224
- last_refresh: lastRefresh,
225
- });
226
- }
218
+ accessToken = refreshed.accessToken;
219
+ idToken = refreshed.idToken ?? idToken;
220
+ refreshToken = refreshed.refreshToken ?? refreshToken;
221
+ accountId = refreshed.accountId ?? accountId;
222
+ isFedRamp =
223
+ isFedRamp ||
224
+ refreshed.isFedRamp === true ||
225
+ deriveChatGptAccountIsFedRamp(idToken) ||
226
+ deriveChatGptAccountIsFedRamp(accessToken);
227
+ lastRefresh = now().toISOString();
228
+ const writePath = resolveWritePath(readResult.path ?? authFilePath);
229
+ await writeAuthFile(writePath, {
230
+ ...authData,
231
+ auth_mode: "chatgpt",
232
+ tokens: {
233
+ id_token: idToken,
234
+ access_token: accessToken,
235
+ refresh_token: refreshToken,
236
+ account_id: accountId,
237
+ },
238
+ last_refresh: lastRefresh,
239
+ });
227
240
  }
228
241
  if (typeof accessToken !== "string" || accessToken.length === 0) {
229
242
  throw new Error("ChatGPT access token not found. Run `codex login` to create auth.json.");
@@ -234,6 +247,7 @@ export const loadAuthTokens = async (options) => {
234
247
  return {
235
248
  accessToken,
236
249
  accountId,
250
+ isFedRamp,
237
251
  idToken,
238
252
  refreshToken,
239
253
  sourcePath: readResult.path ?? resolveWritePath(authFilePath),
@@ -0,0 +1,7 @@
1
+ export declare const CODEX_IMAGE_MODEL = "gpt-image-2";
2
+ export type PreparedCodexImageRequest = {
3
+ body: BodyInit | null | undefined;
4
+ response?: Response;
5
+ };
6
+ export declare const prepareCodexImageRequest: (pathname: string, headers: Headers, body: BodyInit | null | undefined) => Promise<PreparedCodexImageRequest>;
7
+ //# sourceMappingURL=images.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../src/images.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,gBAAgB,CAAA;AAY9C,MAAM,MAAM,yBAAyB,GAAG;IACvC,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,EAAE,QAAQ,CAAA;CACnB,CAAA;AAsLD,eAAO,MAAM,wBAAwB,GACpC,UAAU,MAAM,EAChB,SAAS,OAAO,EAChB,MAAM,QAAQ,GAAG,IAAI,GAAG,SAAS,KAC/B,OAAO,CAAC,yBAAyB,CA6CnC,CAAA"}
package/dist/images.js ADDED
@@ -0,0 +1,187 @@
1
+ export const CODEX_IMAGE_MODEL = "gpt-image-2";
2
+ const MAX_REFERENCE_IMAGES = 5;
3
+ const MAX_REFERENCE_IMAGE_BYTES = 50 * 1024 * 1024;
4
+ const unsupportedOptions = [
5
+ "input_fidelity",
6
+ "moderation",
7
+ "output_compression",
8
+ "output_format",
9
+ "partial_images",
10
+ ];
11
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
12
+ const errorResponse = (message) => Response.json({
13
+ error: {
14
+ message,
15
+ type: "invalid_request_error",
16
+ },
17
+ }, { status: 400 });
18
+ const bytesToBase64 = (bytes) => {
19
+ let binary = "";
20
+ const chunkSize = 0x8000;
21
+ for (let offset = 0; offset < bytes.length; offset += chunkSize) {
22
+ binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
23
+ }
24
+ return btoa(binary);
25
+ };
26
+ const fileToDataUrl = async (file) => {
27
+ const bytes = new Uint8Array(await file.arrayBuffer());
28
+ return `data:${file.type || "image/png"};base64,${bytesToBase64(bytes)}`;
29
+ };
30
+ const decodeBody = async (body) => {
31
+ if (typeof body === "string")
32
+ return body;
33
+ if (body instanceof Blob)
34
+ return body.text();
35
+ if (body instanceof ArrayBuffer)
36
+ return new TextDecoder().decode(body);
37
+ if (ArrayBuffer.isView(body))
38
+ return new TextDecoder().decode(body);
39
+ return undefined;
40
+ };
41
+ const normalizeGeneration = (body) => {
42
+ if (body.stream === true) {
43
+ return {
44
+ body: undefined,
45
+ response: errorResponse("Streaming image generation is not supported by ChatGPT OAuth."),
46
+ };
47
+ }
48
+ if (typeof body.prompt !== "string" || body.prompt.length === 0) {
49
+ return {
50
+ body: undefined,
51
+ response: errorResponse("`prompt` must be a non-empty string."),
52
+ };
53
+ }
54
+ const unsupported = unsupportedOptions.find((key) => body[key] !== undefined);
55
+ if (unsupported) {
56
+ return {
57
+ body: undefined,
58
+ response: errorResponse(`\`${unsupported}\` is not supported by ChatGPT OAuth image generation.`),
59
+ };
60
+ }
61
+ if (body.response_format !== undefined &&
62
+ body.response_format !== "b64_json") {
63
+ return {
64
+ body: undefined,
65
+ response: errorResponse("ChatGPT OAuth image generation only returns `b64_json`."),
66
+ };
67
+ }
68
+ const normalized = {
69
+ model: typeof body.model === "string" && body.model.length > 0
70
+ ? body.model
71
+ : CODEX_IMAGE_MODEL,
72
+ prompt: body.prompt,
73
+ };
74
+ for (const key of ["background", "n", "quality", "size"]) {
75
+ if (body[key] !== undefined)
76
+ normalized[key] = body[key];
77
+ }
78
+ return { body: JSON.stringify(normalized) };
79
+ };
80
+ const normalizeEdit = async (body) => {
81
+ if (body.get("stream") === "true") {
82
+ return {
83
+ body: undefined,
84
+ response: errorResponse("Streaming image editing is not supported by ChatGPT OAuth."),
85
+ };
86
+ }
87
+ if (body.has("mask")) {
88
+ return {
89
+ body: undefined,
90
+ response: errorResponse("Image masks are not supported by ChatGPT OAuth."),
91
+ };
92
+ }
93
+ const prompt = body.get("prompt");
94
+ if (typeof prompt !== "string" || prompt.length === 0) {
95
+ return {
96
+ body: undefined,
97
+ response: errorResponse("`prompt` must be a non-empty string."),
98
+ };
99
+ }
100
+ const files = [...body.getAll("image"), ...body.getAll("image[]")].filter((value) => typeof value !== "string");
101
+ if (files.length === 0 || files.length > MAX_REFERENCE_IMAGES) {
102
+ return {
103
+ body: undefined,
104
+ response: errorResponse(files.length === 0
105
+ ? "At least one `image` is required."
106
+ : "ChatGPT OAuth supports at most 5 reference images."),
107
+ };
108
+ }
109
+ const oversized = files.find((file) => file.size > MAX_REFERENCE_IMAGE_BYTES);
110
+ if (oversized) {
111
+ return {
112
+ body: undefined,
113
+ response: errorResponse(`Reference image \`${oversized.name}\` exceeds the 50 MB limit.`),
114
+ };
115
+ }
116
+ const unsupported = unsupportedOptions.find((key) => body.has(key));
117
+ if (unsupported) {
118
+ return {
119
+ body: undefined,
120
+ response: errorResponse(`\`${unsupported}\` is not supported by ChatGPT OAuth image editing.`),
121
+ };
122
+ }
123
+ const responseFormat = body.get("response_format");
124
+ if (responseFormat !== null && responseFormat !== "b64_json") {
125
+ return {
126
+ body: undefined,
127
+ response: errorResponse("ChatGPT OAuth image editing only returns `b64_json`."),
128
+ };
129
+ }
130
+ const model = body.get("model");
131
+ const normalized = {
132
+ images: await Promise.all(files.map(async (file) => ({ image_url: await fileToDataUrl(file) }))),
133
+ model: typeof model === "string" && model ? model : CODEX_IMAGE_MODEL,
134
+ prompt,
135
+ };
136
+ const n = body.get("n");
137
+ if (typeof n === "string" && n) {
138
+ const parsed = Number(n);
139
+ if (Number.isFinite(parsed))
140
+ normalized.n = parsed;
141
+ }
142
+ for (const key of ["background", "quality", "size"]) {
143
+ const value = body.get(key);
144
+ if (typeof value === "string" && value)
145
+ normalized[key] = value;
146
+ }
147
+ return { body: JSON.stringify(normalized) };
148
+ };
149
+ export const prepareCodexImageRequest = async (pathname, headers, body) => {
150
+ if (pathname.endsWith("/images/generations")) {
151
+ const bodyText = await decodeBody(body);
152
+ if (bodyText === undefined) {
153
+ return {
154
+ body: undefined,
155
+ response: errorResponse("Image generation requires a JSON request body."),
156
+ };
157
+ }
158
+ try {
159
+ const parsed = JSON.parse(bodyText);
160
+ if (!isRecord(parsed)) {
161
+ return {
162
+ body: undefined,
163
+ response: errorResponse("Image generation request body must be a JSON object."),
164
+ };
165
+ }
166
+ headers.set("content-type", "application/json");
167
+ return normalizeGeneration(parsed);
168
+ }
169
+ catch {
170
+ return {
171
+ body: undefined,
172
+ response: errorResponse("Image generation request is invalid JSON."),
173
+ };
174
+ }
175
+ }
176
+ if (pathname.endsWith("/images/edits")) {
177
+ if (!(body instanceof FormData)) {
178
+ return {
179
+ body: undefined,
180
+ response: errorResponse("Image editing requires a multipart/form-data request body."),
181
+ };
182
+ }
183
+ headers.set("content-type", "application/json");
184
+ return normalizeEdit(body);
185
+ }
186
+ return { body };
187
+ };
@@ -0,0 +1,32 @@
1
+ export declare const DEFAULT_CODEX_CLIENT_VERSION = "0.144.1";
2
+ type FetchLike = typeof fetch;
3
+ export type CodexModelInfo = {
4
+ slug: string;
5
+ visibility?: string;
6
+ supportedInApi?: boolean;
7
+ minimalClientVersion?: string;
8
+ useResponsesLite?: boolean;
9
+ preferWebsockets?: boolean;
10
+ supportVerbosity?: boolean;
11
+ defaultVerbosity?: string;
12
+ defaultReasoningLevel?: string;
13
+ defaultReasoningSummary?: string;
14
+ supportsParallelToolCalls?: boolean;
15
+ availableInPlans?: string[];
16
+ raw: Record<string, unknown>;
17
+ };
18
+ export type ResolveCodexClientVersionOptions = {
19
+ codexVersion?: string;
20
+ fetchImpl?: FetchLike;
21
+ onWarning?: (message: string) => void;
22
+ };
23
+ export type FetchCodexModelCatalogOptions = ResolveCodexClientVersionOptions;
24
+ export type CodexModelCatalogClient = {
25
+ request(path: string, init?: RequestInit): Promise<Response>;
26
+ };
27
+ export declare const resolveCodexClientVersion: (options?: ResolveCodexClientVersionOptions) => Promise<string>;
28
+ export declare const resetCodexClientVersionCache: () => void;
29
+ export declare const fetchCodexModelCatalog: (client: CodexModelCatalogClient, options?: FetchCodexModelCatalogOptions) => Promise<CodexModelInfo[]>;
30
+ export declare const isPublicCodexModel: (model: CodexModelInfo) => boolean;
31
+ export {};
32
+ //# sourceMappingURL=models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,4BAA4B,YAAY,CAAA;AAKrD,KAAK,SAAS,GAAG,OAAO,KAAK,CAAA;AAE7B,MAAM,MAAM,cAAc,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,yBAAyB,CAAC,EAAE,OAAO,CAAA;IACnC,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC5B,CAAA;AAED,MAAM,MAAM,gCAAgC,GAAG;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACrC,CAAA;AAED,MAAM,MAAM,6BAA6B,GAAG,gCAAgC,CAAA;AAE5E,MAAM,MAAM,uBAAuB,GAAG;IACrC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CAC5D,CAAA;AAiFD,eAAO,MAAM,yBAAyB,GACrC,UAAS,gCAAqC,KAC5C,OAAO,CAAC,MAAM,CA4ChB,CAAA;AAED,eAAO,MAAM,4BAA4B,QAAO,IAI/C,CAAA;AAED,eAAO,MAAM,sBAAsB,GAClC,QAAQ,uBAAuB,EAC/B,UAAS,6BAAkC,KACzC,OAAO,CAAC,cAAc,EAAE,CA8B1B,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,OAAO,cAAc,KAAG,OAEK,CAAA"}
package/dist/models.js ADDED
@@ -0,0 +1,131 @@
1
+ export const DEFAULT_CODEX_CLIENT_VERSION = "0.144.1";
2
+ const CODEX_VERSION_CACHE_TTL_MS = 60 * 60 * 1000;
3
+ const CODEX_REGISTRY_URL = "https://registry.npmjs.org/@openai/codex/latest";
4
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
5
+ const normalizeVersion = (value) => {
6
+ if (typeof value !== "string") {
7
+ return undefined;
8
+ }
9
+ return value.trim().match(/\b\d+\.\d+\.\d+\b/)?.[0];
10
+ };
11
+ const optionalString = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
12
+ const optionalBoolean = (value) => typeof value === "boolean" ? value : undefined;
13
+ const optionalStrings = (value) => Array.isArray(value)
14
+ ? value.filter((item) => typeof item === "string")
15
+ : undefined;
16
+ const toCodexModelInfo = (value) => {
17
+ if (!isRecord(value)) {
18
+ return undefined;
19
+ }
20
+ const slug = optionalString(value.slug);
21
+ if (!slug) {
22
+ return undefined;
23
+ }
24
+ return {
25
+ slug,
26
+ visibility: optionalString(value.visibility),
27
+ supportedInApi: optionalBoolean(value.supported_in_api),
28
+ minimalClientVersion: optionalString(value.minimal_client_version),
29
+ useResponsesLite: optionalBoolean(value.use_responses_lite),
30
+ preferWebsockets: optionalBoolean(value.prefer_websockets),
31
+ supportVerbosity: optionalBoolean(value.support_verbosity),
32
+ defaultVerbosity: optionalString(value.default_verbosity),
33
+ defaultReasoningLevel: optionalString(value.default_reasoning_level),
34
+ defaultReasoningSummary: optionalString(value.default_reasoning_summary),
35
+ supportsParallelToolCalls: optionalBoolean(value.supports_parallel_tool_calls),
36
+ availableInPlans: optionalStrings(value.available_in_plans),
37
+ raw: value,
38
+ };
39
+ };
40
+ const toUpstreamErrorMessage = (bodyText) => {
41
+ if (!bodyText) {
42
+ return "Failed to load models from Codex.";
43
+ }
44
+ try {
45
+ const parsed = JSON.parse(bodyText);
46
+ if (isRecord(parsed)) {
47
+ if (typeof parsed.detail === "string" && parsed.detail.length > 0) {
48
+ return parsed.detail;
49
+ }
50
+ if (isRecord(parsed.error) && typeof parsed.error.message === "string") {
51
+ return parsed.error.message;
52
+ }
53
+ }
54
+ }
55
+ catch { }
56
+ return bodyText;
57
+ };
58
+ let cachedCodexClientVersion;
59
+ let codexClientVersionCacheExpiresAt = 0;
60
+ let inflightCodexClientVersion;
61
+ export const resolveCodexClientVersion = async (options = {}) => {
62
+ const explicitVersion = normalizeVersion(options.codexVersion);
63
+ if (explicitVersion) {
64
+ return explicitVersion;
65
+ }
66
+ const now = Date.now();
67
+ if (cachedCodexClientVersion && now < codexClientVersionCacheExpiresAt) {
68
+ return cachedCodexClientVersion;
69
+ }
70
+ if (inflightCodexClientVersion) {
71
+ return inflightCodexClientVersion;
72
+ }
73
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
74
+ inflightCodexClientVersion = (async () => {
75
+ try {
76
+ const response = await fetchImpl(CODEX_REGISTRY_URL, {
77
+ headers: { accept: "application/json" },
78
+ });
79
+ if (response.ok) {
80
+ const parsed = (await response.json());
81
+ const version = normalizeVersion(parsed.version);
82
+ if (version) {
83
+ cachedCodexClientVersion = version;
84
+ codexClientVersionCacheExpiresAt =
85
+ Date.now() + CODEX_VERSION_CACHE_TTL_MS;
86
+ return version;
87
+ }
88
+ }
89
+ }
90
+ catch { }
91
+ options.onWarning?.(`Could not determine the latest Codex version. Falling back to ${DEFAULT_CODEX_CLIENT_VERSION}. Pass a version explicitly if you need to override it.`);
92
+ cachedCodexClientVersion = DEFAULT_CODEX_CLIENT_VERSION;
93
+ codexClientVersionCacheExpiresAt = Date.now() + CODEX_VERSION_CACHE_TTL_MS;
94
+ return DEFAULT_CODEX_CLIENT_VERSION;
95
+ })().finally(() => {
96
+ inflightCodexClientVersion = undefined;
97
+ });
98
+ return inflightCodexClientVersion;
99
+ };
100
+ export const resetCodexClientVersionCache = () => {
101
+ cachedCodexClientVersion = undefined;
102
+ codexClientVersionCacheExpiresAt = 0;
103
+ inflightCodexClientVersion = undefined;
104
+ };
105
+ export const fetchCodexModelCatalog = async (client, options = {}) => {
106
+ const clientVersion = await resolveCodexClientVersion(options);
107
+ const response = await client.request(`/models?client_version=${encodeURIComponent(clientVersion)}`);
108
+ const bodyText = await response.text();
109
+ if (!response.ok) {
110
+ throw new Error(toUpstreamErrorMessage(bodyText));
111
+ }
112
+ let parsed;
113
+ try {
114
+ parsed = JSON.parse(bodyText);
115
+ }
116
+ catch {
117
+ throw new Error("Codex returned an invalid models response.");
118
+ }
119
+ if (!isRecord(parsed) || !Array.isArray(parsed.models)) {
120
+ throw new Error("Codex returned a malformed models response.");
121
+ }
122
+ const models = parsed.models
123
+ .map(toCodexModelInfo)
124
+ .filter((model) => model !== undefined);
125
+ if (models.length === 0) {
126
+ throw new Error("Codex returned an empty models list.");
127
+ }
128
+ return models;
129
+ };
130
+ export const isPublicCodexModel = (model) => model.supportedInApi !== false &&
131
+ (model.visibility === undefined || model.visibility === "list");
package/dist/runtime.d.ts CHANGED
@@ -10,6 +10,7 @@ export type FetchFunction = typeof fetch;
10
10
  export type OpenAIOAuthSession = {
11
11
  accessToken: string;
12
12
  accountId: string;
13
+ isFedRamp?: boolean;
13
14
  idToken?: string;
14
15
  refreshToken?: string;
15
16
  expiresAt?: string;
@@ -25,7 +26,6 @@ export type OpenAIOAuth = {
25
26
  headers?: Record<string, string>;
26
27
  instructions?: string;
27
28
  openAIBaseURL?: string;
28
- storeResponses?: boolean;
29
29
  };
30
30
  export type SessionStore = {
31
31
  get(): Promise<OpenAIOAuthSession | null>;
@@ -56,6 +56,7 @@ export type OpenAIOAuthTokenResponse = {
56
56
  idToken?: string;
57
57
  expiresIn?: number;
58
58
  accountId?: string;
59
+ isFedRamp?: boolean;
59
60
  raw: unknown;
60
61
  };
61
62
  export type ExchangeOpenAIOAuthCodeOptions = {
@@ -79,10 +80,10 @@ export type RefreshOpenAIOAuthTokensOptions = {
79
80
  export type CodexOAuthRuntimeSettings = {
80
81
  auth: OpenAIOAuthSessionInput;
81
82
  baseURL?: string;
83
+ codexVersion?: string;
82
84
  fetch?: FetchFunction;
83
85
  headers?: Record<string, string>;
84
86
  instructions?: string;
85
- storeResponses?: boolean;
86
87
  responsesState?: CodexResponsesState | false;
87
88
  };
88
89
  export type OpenAIOAuthTransportOptions = CodexOAuthRuntimeSettings & {
@@ -99,6 +100,8 @@ export type OpenAIOAuthTransport = {
99
100
  chatCompletions: true;
100
101
  models: true;
101
102
  streaming: true;
103
+ imageGeneration: true;
104
+ imageEditing: true;
102
105
  };
103
106
  };
104
107
  export type CodexOAuthClient = {
@@ -109,11 +112,11 @@ export type CodexOAuthClient = {
109
112
  export type NormalizeCodexResponsesBodyOptions = {
110
113
  instructions?: string;
111
114
  forceStream?: boolean;
112
- storeResponses?: boolean;
113
115
  };
114
116
  export declare const usesServerReplayState: (value: Record<string, unknown>) => boolean;
115
117
  export declare const parseJwtClaims: (token: string | undefined) => Record<string, unknown> | undefined;
116
118
  export declare const deriveAccountId: (idToken: string | undefined) => string | undefined;
119
+ export declare const deriveChatGptAccountIsFedRamp: (token: string | undefined) => boolean;
117
120
  export declare const createOpenAIOAuthRequest: (options: OpenAIOAuthRequestOptions) => Promise<OpenAIOAuthRequest>;
118
121
  export declare const exchangeOpenAIOAuthCode: (options: ExchangeOpenAIOAuthCodeOptions) => Promise<OpenAIOAuthTokenResponse>;
119
122
  export declare const refreshOpenAIOAuthTokens: (options: RefreshOpenAIOAuthTokensOptions) => Promise<OpenAIOAuthTokenResponse>;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
package/dist/runtime.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { withoutTrailingSlash } from "@ai-sdk/provider-utils";
2
+ import { CODEX_IMAGE_MODEL, prepareCodexImageRequest } from "./images.js";
3
+ import { fetchCodexModelCatalog, isPublicCodexModel, } from "./models.js";
2
4
  import { CodexResponsesState } from "./state.js";
3
5
  export { collectCompletedResponseFromSse, iterateServerSentEvents, } from "./sse.js";
4
6
  export { CodexResponsesState, } from "./state.js";
@@ -9,6 +11,9 @@ export const DEFAULT_OPENAI_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
9
11
  export const DEFAULT_OPENAI_OAUTH_ISSUER = "https://auth.openai.com";
10
12
  export const DEFAULT_OPENAI_OAUTH_SCOPE = "openid profile email offline_access";
11
13
  const DEFAULT_CODEX_INSTRUCTIONS = "";
14
+ const MODEL_CATALOG_TTL_MS = 5 * 60 * 1000;
15
+ const MODEL_CATALOG_FAILURE_TTL_MS = 60 * 1000;
16
+ const RESPONSES_LITE_HEADER = "x-openai-internal-codex-responses-lite";
12
17
  const textEncoder = new TextEncoder();
13
18
  const bytesToBase64Url = (bytes) => {
14
19
  let binary = "";
@@ -100,6 +105,14 @@ export const deriveAccountId = (idToken) => {
100
105
  }
101
106
  return undefined;
102
107
  };
108
+ export const deriveChatGptAccountIsFedRamp = (token) => {
109
+ const claims = parseJwtClaims(token);
110
+ if (!claims) {
111
+ return false;
112
+ }
113
+ const authClaim = claims["https://api.openai.com/auth"];
114
+ return isRecord(authClaim) && authClaim.chatgpt_account_is_fedramp === true;
115
+ };
103
116
  const resolveTokenUrl = (issuer, tokenUrl) => tokenUrl ?? `${trimTrailingSlash(issuer)}/oauth/token`;
104
117
  const toTokenResponse = (payload) => {
105
118
  if (!isRecord(payload)) {
@@ -120,23 +133,50 @@ const toTokenResponse = (payload) => {
120
133
  idToken,
121
134
  expiresIn,
122
135
  accountId: deriveAccountId(idToken) ?? deriveAccountId(accessToken),
136
+ isFedRamp: deriveChatGptAccountIsFedRamp(idToken) ||
137
+ deriveChatGptAccountIsFedRamp(accessToken),
123
138
  raw: payload,
124
139
  };
125
140
  };
126
141
  const requestOpenAIOAuthTokens = async (options) => {
127
142
  const issuer = options.issuer ?? DEFAULT_OPENAI_OAUTH_ISSUER;
143
+ const isForm = options.encoding === "form";
128
144
  const response = await pickFetch(options.fetch)(resolveTokenUrl(issuer, options.tokenUrl), {
129
145
  method: "POST",
130
146
  headers: {
131
- "Content-Type": "application/json",
147
+ "Content-Type": isForm
148
+ ? "application/x-www-form-urlencoded"
149
+ : "application/json",
132
150
  },
133
- body: JSON.stringify(options.body),
151
+ body: isForm
152
+ ? new URLSearchParams(options.body).toString()
153
+ : JSON.stringify(options.body),
134
154
  signal: options.signal,
135
155
  });
156
+ const bodyText = await response.text();
136
157
  if (!response.ok) {
137
- throw new Error(`OpenAI OAuth token request failed with HTTP ${response.status}.`);
158
+ let detail = "";
159
+ try {
160
+ const parsed = JSON.parse(bodyText);
161
+ if (isRecord(parsed)) {
162
+ const message = parsed.error_description ?? parsed.message ?? parsed.detail;
163
+ if (typeof message === "string") {
164
+ detail = ` ${message}`;
165
+ }
166
+ }
167
+ }
168
+ catch { }
169
+ throw new Error(`OpenAI OAuth token request failed with HTTP ${response.status}.${detail}`);
170
+ }
171
+ try {
172
+ return toTokenResponse(JSON.parse(bodyText));
173
+ }
174
+ catch (error) {
175
+ if (error instanceof SyntaxError) {
176
+ throw new Error("OpenAI OAuth token response was not valid JSON.");
177
+ }
178
+ throw error;
138
179
  }
139
- return toTokenResponse(await response.json());
140
180
  };
141
181
  export const createOpenAIOAuthRequest = async (options) => {
142
182
  const state = options.state ?? randomURLSafeString(24);
@@ -175,6 +215,7 @@ export const exchangeOpenAIOAuthCode = (options) => requestOpenAIOAuthTokens({
175
215
  tokenUrl: options.tokenUrl,
176
216
  fetch: options.fetch,
177
217
  signal: options.signal,
218
+ encoding: "form",
178
219
  body: {
179
220
  grant_type: "authorization_code",
180
221
  code: options.code,
@@ -188,11 +229,11 @@ export const refreshOpenAIOAuthTokens = (options) => requestOpenAIOAuthTokens({
188
229
  tokenUrl: options.tokenUrl,
189
230
  fetch: options.fetch,
190
231
  signal: options.signal,
232
+ encoding: "json",
191
233
  body: {
192
234
  grant_type: "refresh_token",
193
235
  refresh_token: options.refreshToken,
194
236
  client_id: options.clientId ?? DEFAULT_OPENAI_OAUTH_CLIENT_ID,
195
- scope: DEFAULT_OPENAI_OAUTH_SCOPE,
196
237
  },
197
238
  });
198
239
  const pickFetch = (customFetch) => {
@@ -240,7 +281,11 @@ const readRequestParts = async (input, init) => {
240
281
  method: init?.method ?? input.method,
241
282
  headers,
242
283
  body: init?.body ??
243
- (input.body == null ? undefined : await input.clone().text()),
284
+ (input.body == null
285
+ ? undefined
286
+ : input.headers.get("content-type")?.includes("multipart/form-data")
287
+ ? await input.clone().formData()
288
+ : await input.clone().text()),
244
289
  signal: init?.signal ?? input.signal,
245
290
  };
246
291
  }
@@ -277,22 +322,90 @@ const decodeBody = async (body) => {
277
322
  return undefined;
278
323
  };
279
324
  export const getDefaultCodexInstructions = () => DEFAULT_CODEX_INSTRUCTIONS;
280
- export const normalizeCodexResponsesBody = (body, options = {}) => {
325
+ const normalizeResponsesInput = (input) => typeof input === "string"
326
+ ? [
327
+ {
328
+ role: "user",
329
+ content: [{ type: "input_text", text: input }],
330
+ },
331
+ ]
332
+ : input;
333
+ const addEncryptedReasoningContent = (include) => {
334
+ const values = Array.isArray(include)
335
+ ? include.filter((value) => typeof value === "string")
336
+ : [];
337
+ if (!values.includes("reasoning.encrypted_content")) {
338
+ values.push("reasoning.encrypted_content");
339
+ }
340
+ return values;
341
+ };
342
+ const applyModelDefaults = (normalized, modelInfo) => {
343
+ if (!modelInfo) {
344
+ return;
345
+ }
346
+ const reasoning = isRecord(normalized.reasoning)
347
+ ? { ...normalized.reasoning }
348
+ : {};
349
+ if (reasoning.effort === undefined &&
350
+ modelInfo.defaultReasoningLevel !== undefined) {
351
+ reasoning.effort = modelInfo.defaultReasoningLevel;
352
+ }
353
+ if (modelInfo.useResponsesLite) {
354
+ reasoning.context = "all_turns";
355
+ }
356
+ if (Object.keys(reasoning).length > 0) {
357
+ normalized.reasoning = reasoning;
358
+ }
359
+ if (modelInfo.supportVerbosity && modelInfo.defaultVerbosity !== undefined) {
360
+ const text = isRecord(normalized.text) ? { ...normalized.text } : {};
361
+ if (text.verbosity === undefined) {
362
+ text.verbosity = modelInfo.defaultVerbosity;
363
+ }
364
+ normalized.text = text;
365
+ }
366
+ if (!modelInfo.useResponsesLite) {
367
+ return;
368
+ }
369
+ const input = Array.isArray(normalized.input) ? [...normalized.input] : [];
370
+ const prefix = [];
371
+ const tools = Array.isArray(normalized.tools) ? normalized.tools : [];
372
+ if (tools.length > 0 &&
373
+ !input.some((item) => isRecord(item) && item.type === "additional_tools")) {
374
+ prefix.push({
375
+ type: "additional_tools",
376
+ role: "developer",
377
+ tools,
378
+ });
379
+ }
380
+ if (typeof normalized.instructions === "string" && normalized.instructions) {
381
+ prefix.push({
382
+ role: "developer",
383
+ content: [{ type: "input_text", text: normalized.instructions }],
384
+ });
385
+ }
386
+ normalized.input = [...prefix, ...input];
387
+ normalized.instructions = "";
388
+ normalized.parallel_tool_calls = false;
389
+ delete normalized.tools;
390
+ };
391
+ const normalizeCodexResponsesBodyInternal = (body, options = {}) => {
281
392
  const normalized = { ...body };
282
393
  const instructions = typeof normalized.instructions === "string"
283
394
  ? normalized.instructions
284
395
  : (options.instructions ?? getDefaultCodexInstructions());
285
396
  normalized.instructions = instructions;
286
- if (normalized.store === undefined) {
287
- normalized.store = options.storeResponses ?? false;
288
- }
397
+ normalized.input = normalizeResponsesInput(normalized.input);
398
+ normalized.store = false;
399
+ normalized.include = addEncryptedReasoningContent(normalized.include);
400
+ applyModelDefaults(normalized, options.modelInfo);
289
401
  if (options.forceStream) {
290
402
  normalized.stream = true;
291
403
  }
292
404
  delete normalized.max_output_tokens;
293
405
  return normalized;
294
406
  };
295
- const prepareResponsesRequestBody = async (pathname, headers, body, settings, state) => {
407
+ export const normalizeCodexResponsesBody = (body, options = {}) => normalizeCodexResponsesBodyInternal(body, options);
408
+ const prepareResponsesRequestBody = async (pathname, headers, body, settings, state, auth, resolveModelInfo) => {
296
409
  if (!pathname.endsWith("/responses")) {
297
410
  return { body };
298
411
  }
@@ -311,10 +424,18 @@ const prepareResponsesRequestBody = async (pathname, headers, body, settings, st
311
424
  Array.isArray(parsed)) {
312
425
  return { body };
313
426
  }
314
- const normalized = normalizeCodexResponsesBody(parsed, {
427
+ const wantsStream = parsed.stream === true;
428
+ const modelInfo = typeof parsed.model === "string"
429
+ ? await resolveModelInfo(auth, parsed.model)
430
+ : undefined;
431
+ const normalized = normalizeCodexResponsesBodyInternal(parsed, {
432
+ forceStream: true,
315
433
  instructions: settings.instructions,
316
- storeResponses: settings.storeResponses,
434
+ modelInfo,
317
435
  });
436
+ if (modelInfo?.useResponsesLite) {
437
+ headers.set(RESPONSES_LITE_HEADER, "true");
438
+ }
318
439
  if (state?.requiresCachedState(normalized)) {
319
440
  await state.waitForPendingCaptures();
320
441
  }
@@ -322,6 +443,7 @@ const prepareResponsesRequestBody = async (pathname, headers, body, settings, st
322
443
  return {
323
444
  body: JSON.stringify(expanded),
324
445
  requestBody: expanded,
446
+ wantsStream,
325
447
  };
326
448
  }
327
449
  catch {
@@ -348,6 +470,101 @@ const captureResponsesState = (response, requestBody, state) => {
348
470
  headers: new Headers(response.headers),
349
471
  });
350
472
  };
473
+ const finalizeResponsesResponse = async (response, prepared, state) => {
474
+ if (prepared.requestBody == null ||
475
+ prepared.wantsStream == null ||
476
+ !response.ok ||
477
+ response.body == null) {
478
+ return response;
479
+ }
480
+ if (prepared.wantsStream) {
481
+ return captureResponsesState(response, prepared.requestBody, state);
482
+ }
483
+ const completed = await collectCompletedResponseFromSse(response.body);
484
+ state?.rememberResponse(completed, prepared.requestBody);
485
+ const headers = new Headers(response.headers);
486
+ headers.delete("content-encoding");
487
+ headers.delete("content-length");
488
+ headers.set("content-type", "application/json");
489
+ return new Response(JSON.stringify(completed), {
490
+ status: response.status,
491
+ statusText: response.statusText,
492
+ headers,
493
+ });
494
+ };
495
+ const applyAuthHeaders = (headers, auth) => {
496
+ headers.delete("authorization");
497
+ headers.delete("chatgpt-account-id");
498
+ headers.delete("openai-beta");
499
+ headers.delete(RESPONSES_LITE_HEADER);
500
+ headers.delete("x-openai-fedramp");
501
+ headers.set("Authorization", `Bearer ${auth.accessToken}`);
502
+ headers.set("chatgpt-account-id", auth.accountId);
503
+ const isFedRamp = auth.isFedRamp ??
504
+ (deriveChatGptAccountIsFedRamp(auth.idToken) ||
505
+ deriveChatGptAccountIsFedRamp(auth.accessToken));
506
+ if (isFedRamp) {
507
+ headers.set("X-OpenAI-Fedramp", "true");
508
+ }
509
+ };
510
+ const createModelCatalogResolver = (fetch, baseURL, settings) => {
511
+ const cache = new Map();
512
+ const inflight = new Map();
513
+ return async (auth) => {
514
+ const cacheKey = `${auth.accountId}:${auth.isFedRamp === true}`;
515
+ const cached = cache.get(cacheKey);
516
+ if (cached && cached.expiresAt > Date.now()) {
517
+ return cached.result;
518
+ }
519
+ let loading = inflight.get(cacheKey);
520
+ if (!loading) {
521
+ loading = (async () => {
522
+ try {
523
+ const models = await fetchCodexModelCatalog({
524
+ request: async (path, init) => {
525
+ const headers = new Headers(settings.headers);
526
+ new Headers(init?.headers).forEach((value, key) => {
527
+ headers.set(key, value);
528
+ });
529
+ applyAuthHeaders(headers, auth);
530
+ return fetch(new URL(path.replace(/^\//, ""), `${baseURL}/`).toString(), {
531
+ ...init,
532
+ method: init?.method ?? "GET",
533
+ headers,
534
+ });
535
+ },
536
+ }, {
537
+ codexVersion: settings.codexVersion,
538
+ fetchImpl: fetch,
539
+ });
540
+ const result = { models };
541
+ cache.set(cacheKey, {
542
+ expiresAt: Date.now() + MODEL_CATALOG_TTL_MS,
543
+ result,
544
+ });
545
+ return result;
546
+ }
547
+ catch (error) {
548
+ const result = {
549
+ models: [],
550
+ error: error instanceof Error
551
+ ? error
552
+ : new Error("Failed to load models from Codex."),
553
+ };
554
+ cache.set(cacheKey, {
555
+ expiresAt: Date.now() + MODEL_CATALOG_FAILURE_TTL_MS,
556
+ result,
557
+ });
558
+ return result;
559
+ }
560
+ })().finally(() => {
561
+ inflight.delete(cacheKey);
562
+ });
563
+ inflight.set(cacheKey, loading);
564
+ }
565
+ return loading;
566
+ };
567
+ };
351
568
  const resolveAuth = async (source) => {
352
569
  const auth = typeof source === "function" ? await source() : source;
353
570
  if (!auth) {
@@ -361,29 +578,64 @@ export const createCodexOAuthFetch = (settings) => {
361
578
  const responsesState = settings.responsesState === false
362
579
  ? undefined
363
580
  : (settings.responsesState ?? new CodexResponsesState());
581
+ const resolveModelCatalog = createModelCatalogResolver(fetch, baseURL, settings);
582
+ const resolveModelInfo = async (auth, model) => (await resolveModelCatalog(auth)).models.find((entry) => entry.slug === model);
364
583
  return async (input, init) => {
365
584
  const request = await readRequestParts(input, init);
366
585
  const targetUrl = resolveTargetUrl(request.url, baseURL);
367
586
  const target = new URL(targetUrl);
368
587
  const auth = await resolveAuth(settings.auth);
588
+ if ((request.method ?? "GET").toUpperCase() === "GET" &&
589
+ target.pathname.endsWith("/models") &&
590
+ !target.searchParams.has("client_version")) {
591
+ const catalog = await resolveModelCatalog(auth);
592
+ const models = catalog.models.filter(isPublicCodexModel);
593
+ if (models.length === 0) {
594
+ return Response.json({
595
+ error: {
596
+ message: catalog.error?.message ?? "Failed to load models from Codex.",
597
+ },
598
+ }, { status: 502 });
599
+ }
600
+ return Response.json({
601
+ object: "list",
602
+ data: [
603
+ ...models.map((model) => ({
604
+ id: model.slug,
605
+ object: "model",
606
+ created: 0,
607
+ owned_by: "codex-oauth",
608
+ })),
609
+ ...(models.some((model) => model.slug === CODEX_IMAGE_MODEL)
610
+ ? []
611
+ : [
612
+ {
613
+ id: CODEX_IMAGE_MODEL,
614
+ object: "model",
615
+ created: 0,
616
+ owned_by: "codex-oauth",
617
+ },
618
+ ]),
619
+ ],
620
+ });
621
+ }
369
622
  const headers = new Headers(settings.headers);
370
623
  request.headers.forEach((value, key) => {
371
624
  headers.set(key, value);
372
625
  });
373
- headers.delete("authorization");
374
- headers.delete("chatgpt-account-id");
375
- headers.delete("openai-beta");
376
- headers.set("Authorization", `Bearer ${auth.accessToken}`);
377
- headers.set("chatgpt-account-id", auth.accountId);
378
- headers.set("OpenAI-Beta", "responses=experimental");
379
- const preparedBody = await prepareResponsesRequestBody(target.pathname, headers, request.body, settings, responsesState);
626
+ applyAuthHeaders(headers, auth);
627
+ const preparedImage = await prepareCodexImageRequest(target.pathname, headers, request.body);
628
+ if (preparedImage.response) {
629
+ return preparedImage.response;
630
+ }
631
+ const preparedBody = await prepareResponsesRequestBody(target.pathname, headers, preparedImage.body, settings, responsesState, auth, resolveModelInfo);
380
632
  const response = await fetch(target.toString(), {
381
633
  method: request.method ?? init?.method,
382
634
  headers,
383
635
  body: preparedBody.body,
384
636
  signal: request.signal ?? undefined,
385
637
  });
386
- return captureResponsesState(response, preparedBody.requestBody, responsesState);
638
+ return finalizeResponsesResponse(response, preparedBody, responsesState);
387
639
  };
388
640
  };
389
641
  const resolveOpenAICompatibleUrl = (path, baseURL) => {
@@ -418,6 +670,8 @@ export const createOpenAIOAuthTransport = (settings) => {
418
670
  chatCompletions: true,
419
671
  models: true,
420
672
  streaming: true,
673
+ imageGeneration: true,
674
+ imageEditing: true,
421
675
  },
422
676
  };
423
677
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openai-oauth/core",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.0.0-beta.3",
4
4
  "description": "Core auth and in-memory OpenAI-compatible transport for openai-oauth.",
5
5
  "keywords": [
6
6
  "openai",