@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 +2 -0
- package/dist/auth.d.ts +1 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +52 -38
- package/dist/images.d.ts +7 -0
- package/dist/images.d.ts.map +1 -0
- package/dist/images.js +187 -0
- package/dist/models.d.ts +32 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +131 -0
- package/dist/runtime.d.ts +6 -3
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +275 -21
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/auth.d.ts
CHANGED
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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),
|
package/dist/images.d.ts
ADDED
|
@@ -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
|
+
};
|
package/dist/models.d.ts
ADDED
|
@@ -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>;
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"
|
|
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":
|
|
147
|
+
"Content-Type": isForm
|
|
148
|
+
? "application/x-www-form-urlencoded"
|
|
149
|
+
: "application/json",
|
|
132
150
|
},
|
|
133
|
-
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
287
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
374
|
-
headers.
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
headers.
|
|
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
|
|
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
|
};
|