@openai-oauth/core 2.0.0-beta.0 → 2.0.0-beta.2
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/dist/auth.d.ts +1 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +52 -38
- 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 +4 -3
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +251 -20
- package/package.json +1 -1
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/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 & {
|
|
@@ -109,11 +110,11 @@ export type CodexOAuthClient = {
|
|
|
109
110
|
export type NormalizeCodexResponsesBodyOptions = {
|
|
110
111
|
instructions?: string;
|
|
111
112
|
forceStream?: boolean;
|
|
112
|
-
storeResponses?: boolean;
|
|
113
113
|
};
|
|
114
114
|
export declare const usesServerReplayState: (value: Record<string, unknown>) => boolean;
|
|
115
115
|
export declare const parseJwtClaims: (token: string | undefined) => Record<string, unknown> | undefined;
|
|
116
116
|
export declare const deriveAccountId: (idToken: string | undefined) => string | undefined;
|
|
117
|
+
export declare const deriveChatGptAccountIsFedRamp: (token: string | undefined) => boolean;
|
|
117
118
|
export declare const createOpenAIOAuthRequest: (options: OpenAIOAuthRequestOptions) => Promise<OpenAIOAuthRequest>;
|
|
118
119
|
export declare const exchangeOpenAIOAuthCode: (options: ExchangeOpenAIOAuthCodeOptions) => Promise<OpenAIOAuthTokenResponse>;
|
|
119
120
|
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":"AAMA,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAEhD,OAAO,EACN,+BAA+B,EAC/B,uBAAuB,EACvB,KAAK,eAAe,GACpB,MAAM,UAAU,CAAA;AACjB,OAAO,EACN,mBAAmB,EACnB,KAAK,0BAA0B,EAC/B,KAAK,2BAA2B,GAChC,MAAM,YAAY,CAAA;AAInB,eAAO,MAAM,sBAAsB,0CAA0C,CAAA;AAC7E,eAAO,MAAM,kCAAkC,kCACf,CAAA;AAChC,eAAO,MAAM,8BAA8B,iCAAiC,CAAA;AAC5E,eAAO,MAAM,2BAA2B,4BAA4B,CAAA;AACpE,eAAO,MAAM,0BAA0B,wCAAwC,CAAA;AAM/E,MAAM,MAAM,aAAa,GAAG,OAAO,KAAK,CAAA;AAExC,MAAM,MAAM,kBAAkB,GAAG;IAChC,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,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,uBAAuB,GAChC,kBAAkB,GAClB,CAAC,MAAM,OAAO,CAAC,kBAAkB,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,CAAA;AAEzD,MAAM,MAAM,WAAW,GAAG;IACzB,IAAI,EAAE,cAAc,CAAA;IACpB,UAAU,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAA;IAChD,cAAc,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAA;IACpD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IAC1B,GAAG,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAA;IACzC,GAAG,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,uBAAuB,CAAC,EAAE,OAAO,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAA;CACnE,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAChC,gBAAgB,EAAE,MAAM,CAAA;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACtC,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,GAAG,EAAE,OAAO,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,8BAA8B,GAAG;IAC5C,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,MAAM,CAAC,EAAE,WAAW,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,+BAA+B,GAAG;IAC7C,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,MAAM,CAAC,EAAE,WAAW,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG;IACvC,IAAI,EAAE,uBAAuB,CAAA;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAA;CAC5C,CAAA;AAED,MAAM,MAAM,2BAA2B,GAAG,yBAAyB,GAAG;IACrE,aAAa,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,mBAAmB,CAAA;IACzB,QAAQ,EAAE,eAAe,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,aAAa,CAAA;IACpB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAChE,YAAY,EAAE;QACb,SAAS,EAAE,IAAI,CAAA;QACf,eAAe,EAAE,IAAI,CAAA;QACrB,MAAM,EAAE,IAAI,CAAA;QACZ,SAAS,EAAE,IAAI,CAAA;KACf,CAAA;CACD,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,aAAa,CAAA;IACpB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CAChE,CAAA;AAUD,MAAM,MAAM,kCAAkC,GAAG;IAChD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAuCD,eAAO,MAAM,qBAAqB,GACjC,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAeF,CAAA;AAaD,eAAO,MAAM,cAAc,GAC1B,OAAO,MAAM,GAAG,SAAS,KACvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAkB5B,CAAA;AAED,eAAO,MAAM,eAAe,GAC3B,SAAS,MAAM,GAAG,SAAS,KACzB,MAAM,GAAG,SA+BX,CAAA;AAED,eAAO,MAAM,6BAA6B,GACzC,OAAO,MAAM,GAAG,SAAS,KACvB,OAQF,CAAA;AA+FD,eAAO,MAAM,wBAAwB,GACpC,SAAS,yBAAyB,KAChC,OAAO,CAAC,kBAAkB,CA4C5B,CAAA;AAED,eAAO,MAAM,uBAAuB,GACnC,SAAS,8BAA8B,KACrC,OAAO,CAAC,wBAAwB,CAchC,CAAA;AAEH,eAAO,MAAM,wBAAwB,GACpC,SAAS,+BAA+B,KACtC,OAAO,CAAC,wBAAwB,CAYhC,CAAA;AAsGH,eAAO,MAAM,2BAA2B,QAAO,MACpB,CAAA;AA4G3B,eAAO,MAAM,2BAA2B,GACvC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,UAAS,kCAAuC,KAC9C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAuD,CAAA;AAqPhF,eAAO,MAAM,qBAAqB,GACjC,UAAU,yBAAyB,KACjC,aA4EF,CAAA;AAcD,eAAO,MAAM,sBAAsB,GAClC,UAAU,yBAAyB,KACjC,gBAUF,CAAA;AAED,eAAO,MAAM,0BAA0B,GACtC,UAAU,2BAA2B,KACnC,oBAkBF,CAAA"}
|
package/dist/runtime.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { withoutTrailingSlash } from "@ai-sdk/provider-utils";
|
|
2
|
+
import { fetchCodexModelCatalog, isPublicCodexModel, } from "./models.js";
|
|
2
3
|
import { CodexResponsesState } from "./state.js";
|
|
3
4
|
export { collectCompletedResponseFromSse, iterateServerSentEvents, } from "./sse.js";
|
|
4
5
|
export { CodexResponsesState, } from "./state.js";
|
|
@@ -9,6 +10,9 @@ export const DEFAULT_OPENAI_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
|
9
10
|
export const DEFAULT_OPENAI_OAUTH_ISSUER = "https://auth.openai.com";
|
|
10
11
|
export const DEFAULT_OPENAI_OAUTH_SCOPE = "openid profile email offline_access";
|
|
11
12
|
const DEFAULT_CODEX_INSTRUCTIONS = "";
|
|
13
|
+
const MODEL_CATALOG_TTL_MS = 5 * 60 * 1000;
|
|
14
|
+
const MODEL_CATALOG_FAILURE_TTL_MS = 60 * 1000;
|
|
15
|
+
const RESPONSES_LITE_HEADER = "x-openai-internal-codex-responses-lite";
|
|
12
16
|
const textEncoder = new TextEncoder();
|
|
13
17
|
const bytesToBase64Url = (bytes) => {
|
|
14
18
|
let binary = "";
|
|
@@ -100,6 +104,14 @@ export const deriveAccountId = (idToken) => {
|
|
|
100
104
|
}
|
|
101
105
|
return undefined;
|
|
102
106
|
};
|
|
107
|
+
export const deriveChatGptAccountIsFedRamp = (token) => {
|
|
108
|
+
const claims = parseJwtClaims(token);
|
|
109
|
+
if (!claims) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
const authClaim = claims["https://api.openai.com/auth"];
|
|
113
|
+
return isRecord(authClaim) && authClaim.chatgpt_account_is_fedramp === true;
|
|
114
|
+
};
|
|
103
115
|
const resolveTokenUrl = (issuer, tokenUrl) => tokenUrl ?? `${trimTrailingSlash(issuer)}/oauth/token`;
|
|
104
116
|
const toTokenResponse = (payload) => {
|
|
105
117
|
if (!isRecord(payload)) {
|
|
@@ -120,23 +132,50 @@ const toTokenResponse = (payload) => {
|
|
|
120
132
|
idToken,
|
|
121
133
|
expiresIn,
|
|
122
134
|
accountId: deriveAccountId(idToken) ?? deriveAccountId(accessToken),
|
|
135
|
+
isFedRamp: deriveChatGptAccountIsFedRamp(idToken) ||
|
|
136
|
+
deriveChatGptAccountIsFedRamp(accessToken),
|
|
123
137
|
raw: payload,
|
|
124
138
|
};
|
|
125
139
|
};
|
|
126
140
|
const requestOpenAIOAuthTokens = async (options) => {
|
|
127
141
|
const issuer = options.issuer ?? DEFAULT_OPENAI_OAUTH_ISSUER;
|
|
142
|
+
const isForm = options.encoding === "form";
|
|
128
143
|
const response = await pickFetch(options.fetch)(resolveTokenUrl(issuer, options.tokenUrl), {
|
|
129
144
|
method: "POST",
|
|
130
145
|
headers: {
|
|
131
|
-
"Content-Type":
|
|
146
|
+
"Content-Type": isForm
|
|
147
|
+
? "application/x-www-form-urlencoded"
|
|
148
|
+
: "application/json",
|
|
132
149
|
},
|
|
133
|
-
body:
|
|
150
|
+
body: isForm
|
|
151
|
+
? new URLSearchParams(options.body).toString()
|
|
152
|
+
: JSON.stringify(options.body),
|
|
134
153
|
signal: options.signal,
|
|
135
154
|
});
|
|
155
|
+
const bodyText = await response.text();
|
|
136
156
|
if (!response.ok) {
|
|
137
|
-
|
|
157
|
+
let detail = "";
|
|
158
|
+
try {
|
|
159
|
+
const parsed = JSON.parse(bodyText);
|
|
160
|
+
if (isRecord(parsed)) {
|
|
161
|
+
const message = parsed.error_description ?? parsed.message ?? parsed.detail;
|
|
162
|
+
if (typeof message === "string") {
|
|
163
|
+
detail = ` ${message}`;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch { }
|
|
168
|
+
throw new Error(`OpenAI OAuth token request failed with HTTP ${response.status}.${detail}`);
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
return toTokenResponse(JSON.parse(bodyText));
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
if (error instanceof SyntaxError) {
|
|
175
|
+
throw new Error("OpenAI OAuth token response was not valid JSON.");
|
|
176
|
+
}
|
|
177
|
+
throw error;
|
|
138
178
|
}
|
|
139
|
-
return toTokenResponse(await response.json());
|
|
140
179
|
};
|
|
141
180
|
export const createOpenAIOAuthRequest = async (options) => {
|
|
142
181
|
const state = options.state ?? randomURLSafeString(24);
|
|
@@ -175,6 +214,7 @@ export const exchangeOpenAIOAuthCode = (options) => requestOpenAIOAuthTokens({
|
|
|
175
214
|
tokenUrl: options.tokenUrl,
|
|
176
215
|
fetch: options.fetch,
|
|
177
216
|
signal: options.signal,
|
|
217
|
+
encoding: "form",
|
|
178
218
|
body: {
|
|
179
219
|
grant_type: "authorization_code",
|
|
180
220
|
code: options.code,
|
|
@@ -188,11 +228,11 @@ export const refreshOpenAIOAuthTokens = (options) => requestOpenAIOAuthTokens({
|
|
|
188
228
|
tokenUrl: options.tokenUrl,
|
|
189
229
|
fetch: options.fetch,
|
|
190
230
|
signal: options.signal,
|
|
231
|
+
encoding: "json",
|
|
191
232
|
body: {
|
|
192
233
|
grant_type: "refresh_token",
|
|
193
234
|
refresh_token: options.refreshToken,
|
|
194
235
|
client_id: options.clientId ?? DEFAULT_OPENAI_OAUTH_CLIENT_ID,
|
|
195
|
-
scope: DEFAULT_OPENAI_OAUTH_SCOPE,
|
|
196
236
|
},
|
|
197
237
|
});
|
|
198
238
|
const pickFetch = (customFetch) => {
|
|
@@ -277,22 +317,90 @@ const decodeBody = async (body) => {
|
|
|
277
317
|
return undefined;
|
|
278
318
|
};
|
|
279
319
|
export const getDefaultCodexInstructions = () => DEFAULT_CODEX_INSTRUCTIONS;
|
|
280
|
-
|
|
320
|
+
const normalizeResponsesInput = (input) => typeof input === "string"
|
|
321
|
+
? [
|
|
322
|
+
{
|
|
323
|
+
role: "user",
|
|
324
|
+
content: [{ type: "input_text", text: input }],
|
|
325
|
+
},
|
|
326
|
+
]
|
|
327
|
+
: input;
|
|
328
|
+
const addEncryptedReasoningContent = (include) => {
|
|
329
|
+
const values = Array.isArray(include)
|
|
330
|
+
? include.filter((value) => typeof value === "string")
|
|
331
|
+
: [];
|
|
332
|
+
if (!values.includes("reasoning.encrypted_content")) {
|
|
333
|
+
values.push("reasoning.encrypted_content");
|
|
334
|
+
}
|
|
335
|
+
return values;
|
|
336
|
+
};
|
|
337
|
+
const applyModelDefaults = (normalized, modelInfo) => {
|
|
338
|
+
if (!modelInfo) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const reasoning = isRecord(normalized.reasoning)
|
|
342
|
+
? { ...normalized.reasoning }
|
|
343
|
+
: {};
|
|
344
|
+
if (reasoning.effort === undefined &&
|
|
345
|
+
modelInfo.defaultReasoningLevel !== undefined) {
|
|
346
|
+
reasoning.effort = modelInfo.defaultReasoningLevel;
|
|
347
|
+
}
|
|
348
|
+
if (modelInfo.useResponsesLite) {
|
|
349
|
+
reasoning.context = "all_turns";
|
|
350
|
+
}
|
|
351
|
+
if (Object.keys(reasoning).length > 0) {
|
|
352
|
+
normalized.reasoning = reasoning;
|
|
353
|
+
}
|
|
354
|
+
if (modelInfo.supportVerbosity && modelInfo.defaultVerbosity !== undefined) {
|
|
355
|
+
const text = isRecord(normalized.text) ? { ...normalized.text } : {};
|
|
356
|
+
if (text.verbosity === undefined) {
|
|
357
|
+
text.verbosity = modelInfo.defaultVerbosity;
|
|
358
|
+
}
|
|
359
|
+
normalized.text = text;
|
|
360
|
+
}
|
|
361
|
+
if (!modelInfo.useResponsesLite) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const input = Array.isArray(normalized.input) ? [...normalized.input] : [];
|
|
365
|
+
const prefix = [];
|
|
366
|
+
const tools = Array.isArray(normalized.tools) ? normalized.tools : [];
|
|
367
|
+
if (tools.length > 0 &&
|
|
368
|
+
!input.some((item) => isRecord(item) && item.type === "additional_tools")) {
|
|
369
|
+
prefix.push({
|
|
370
|
+
type: "additional_tools",
|
|
371
|
+
role: "developer",
|
|
372
|
+
tools,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
if (typeof normalized.instructions === "string" && normalized.instructions) {
|
|
376
|
+
prefix.push({
|
|
377
|
+
role: "developer",
|
|
378
|
+
content: [{ type: "input_text", text: normalized.instructions }],
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
normalized.input = [...prefix, ...input];
|
|
382
|
+
normalized.instructions = "";
|
|
383
|
+
normalized.parallel_tool_calls = false;
|
|
384
|
+
delete normalized.tools;
|
|
385
|
+
};
|
|
386
|
+
const normalizeCodexResponsesBodyInternal = (body, options = {}) => {
|
|
281
387
|
const normalized = { ...body };
|
|
282
388
|
const instructions = typeof normalized.instructions === "string"
|
|
283
389
|
? normalized.instructions
|
|
284
390
|
: (options.instructions ?? getDefaultCodexInstructions());
|
|
285
391
|
normalized.instructions = instructions;
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
392
|
+
normalized.input = normalizeResponsesInput(normalized.input);
|
|
393
|
+
normalized.store = false;
|
|
394
|
+
normalized.include = addEncryptedReasoningContent(normalized.include);
|
|
395
|
+
applyModelDefaults(normalized, options.modelInfo);
|
|
289
396
|
if (options.forceStream) {
|
|
290
397
|
normalized.stream = true;
|
|
291
398
|
}
|
|
292
399
|
delete normalized.max_output_tokens;
|
|
293
400
|
return normalized;
|
|
294
401
|
};
|
|
295
|
-
const
|
|
402
|
+
export const normalizeCodexResponsesBody = (body, options = {}) => normalizeCodexResponsesBodyInternal(body, options);
|
|
403
|
+
const prepareResponsesRequestBody = async (pathname, headers, body, settings, state, auth, resolveModelInfo) => {
|
|
296
404
|
if (!pathname.endsWith("/responses")) {
|
|
297
405
|
return { body };
|
|
298
406
|
}
|
|
@@ -311,10 +419,18 @@ const prepareResponsesRequestBody = async (pathname, headers, body, settings, st
|
|
|
311
419
|
Array.isArray(parsed)) {
|
|
312
420
|
return { body };
|
|
313
421
|
}
|
|
314
|
-
const
|
|
422
|
+
const wantsStream = parsed.stream === true;
|
|
423
|
+
const modelInfo = typeof parsed.model === "string"
|
|
424
|
+
? await resolveModelInfo(auth, parsed.model)
|
|
425
|
+
: undefined;
|
|
426
|
+
const normalized = normalizeCodexResponsesBodyInternal(parsed, {
|
|
427
|
+
forceStream: true,
|
|
315
428
|
instructions: settings.instructions,
|
|
316
|
-
|
|
429
|
+
modelInfo,
|
|
317
430
|
});
|
|
431
|
+
if (modelInfo?.useResponsesLite) {
|
|
432
|
+
headers.set(RESPONSES_LITE_HEADER, "true");
|
|
433
|
+
}
|
|
318
434
|
if (state?.requiresCachedState(normalized)) {
|
|
319
435
|
await state.waitForPendingCaptures();
|
|
320
436
|
}
|
|
@@ -322,6 +438,7 @@ const prepareResponsesRequestBody = async (pathname, headers, body, settings, st
|
|
|
322
438
|
return {
|
|
323
439
|
body: JSON.stringify(expanded),
|
|
324
440
|
requestBody: expanded,
|
|
441
|
+
wantsStream,
|
|
325
442
|
};
|
|
326
443
|
}
|
|
327
444
|
catch {
|
|
@@ -348,6 +465,101 @@ const captureResponsesState = (response, requestBody, state) => {
|
|
|
348
465
|
headers: new Headers(response.headers),
|
|
349
466
|
});
|
|
350
467
|
};
|
|
468
|
+
const finalizeResponsesResponse = async (response, prepared, state) => {
|
|
469
|
+
if (prepared.requestBody == null ||
|
|
470
|
+
prepared.wantsStream == null ||
|
|
471
|
+
!response.ok ||
|
|
472
|
+
response.body == null) {
|
|
473
|
+
return response;
|
|
474
|
+
}
|
|
475
|
+
if (prepared.wantsStream) {
|
|
476
|
+
return captureResponsesState(response, prepared.requestBody, state);
|
|
477
|
+
}
|
|
478
|
+
const completed = await collectCompletedResponseFromSse(response.body);
|
|
479
|
+
state?.rememberResponse(completed, prepared.requestBody);
|
|
480
|
+
const headers = new Headers(response.headers);
|
|
481
|
+
headers.delete("content-encoding");
|
|
482
|
+
headers.delete("content-length");
|
|
483
|
+
headers.set("content-type", "application/json");
|
|
484
|
+
return new Response(JSON.stringify(completed), {
|
|
485
|
+
status: response.status,
|
|
486
|
+
statusText: response.statusText,
|
|
487
|
+
headers,
|
|
488
|
+
});
|
|
489
|
+
};
|
|
490
|
+
const applyAuthHeaders = (headers, auth) => {
|
|
491
|
+
headers.delete("authorization");
|
|
492
|
+
headers.delete("chatgpt-account-id");
|
|
493
|
+
headers.delete("openai-beta");
|
|
494
|
+
headers.delete(RESPONSES_LITE_HEADER);
|
|
495
|
+
headers.delete("x-openai-fedramp");
|
|
496
|
+
headers.set("Authorization", `Bearer ${auth.accessToken}`);
|
|
497
|
+
headers.set("chatgpt-account-id", auth.accountId);
|
|
498
|
+
const isFedRamp = auth.isFedRamp ??
|
|
499
|
+
(deriveChatGptAccountIsFedRamp(auth.idToken) ||
|
|
500
|
+
deriveChatGptAccountIsFedRamp(auth.accessToken));
|
|
501
|
+
if (isFedRamp) {
|
|
502
|
+
headers.set("X-OpenAI-Fedramp", "true");
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
const createModelCatalogResolver = (fetch, baseURL, settings) => {
|
|
506
|
+
const cache = new Map();
|
|
507
|
+
const inflight = new Map();
|
|
508
|
+
return async (auth) => {
|
|
509
|
+
const cacheKey = `${auth.accountId}:${auth.isFedRamp === true}`;
|
|
510
|
+
const cached = cache.get(cacheKey);
|
|
511
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
512
|
+
return cached.result;
|
|
513
|
+
}
|
|
514
|
+
let loading = inflight.get(cacheKey);
|
|
515
|
+
if (!loading) {
|
|
516
|
+
loading = (async () => {
|
|
517
|
+
try {
|
|
518
|
+
const models = await fetchCodexModelCatalog({
|
|
519
|
+
request: async (path, init) => {
|
|
520
|
+
const headers = new Headers(settings.headers);
|
|
521
|
+
new Headers(init?.headers).forEach((value, key) => {
|
|
522
|
+
headers.set(key, value);
|
|
523
|
+
});
|
|
524
|
+
applyAuthHeaders(headers, auth);
|
|
525
|
+
return fetch(new URL(path.replace(/^\//, ""), `${baseURL}/`).toString(), {
|
|
526
|
+
...init,
|
|
527
|
+
method: init?.method ?? "GET",
|
|
528
|
+
headers,
|
|
529
|
+
});
|
|
530
|
+
},
|
|
531
|
+
}, {
|
|
532
|
+
codexVersion: settings.codexVersion,
|
|
533
|
+
fetchImpl: fetch,
|
|
534
|
+
});
|
|
535
|
+
const result = { models };
|
|
536
|
+
cache.set(cacheKey, {
|
|
537
|
+
expiresAt: Date.now() + MODEL_CATALOG_TTL_MS,
|
|
538
|
+
result,
|
|
539
|
+
});
|
|
540
|
+
return result;
|
|
541
|
+
}
|
|
542
|
+
catch (error) {
|
|
543
|
+
const result = {
|
|
544
|
+
models: [],
|
|
545
|
+
error: error instanceof Error
|
|
546
|
+
? error
|
|
547
|
+
: new Error("Failed to load models from Codex."),
|
|
548
|
+
};
|
|
549
|
+
cache.set(cacheKey, {
|
|
550
|
+
expiresAt: Date.now() + MODEL_CATALOG_FAILURE_TTL_MS,
|
|
551
|
+
result,
|
|
552
|
+
});
|
|
553
|
+
return result;
|
|
554
|
+
}
|
|
555
|
+
})().finally(() => {
|
|
556
|
+
inflight.delete(cacheKey);
|
|
557
|
+
});
|
|
558
|
+
inflight.set(cacheKey, loading);
|
|
559
|
+
}
|
|
560
|
+
return loading;
|
|
561
|
+
};
|
|
562
|
+
};
|
|
351
563
|
const resolveAuth = async (source) => {
|
|
352
564
|
const auth = typeof source === "function" ? await source() : source;
|
|
353
565
|
if (!auth) {
|
|
@@ -361,29 +573,48 @@ export const createCodexOAuthFetch = (settings) => {
|
|
|
361
573
|
const responsesState = settings.responsesState === false
|
|
362
574
|
? undefined
|
|
363
575
|
: (settings.responsesState ?? new CodexResponsesState());
|
|
576
|
+
const resolveModelCatalog = createModelCatalogResolver(fetch, baseURL, settings);
|
|
577
|
+
const resolveModelInfo = async (auth, model) => (await resolveModelCatalog(auth)).models.find((entry) => entry.slug === model);
|
|
364
578
|
return async (input, init) => {
|
|
365
579
|
const request = await readRequestParts(input, init);
|
|
366
580
|
const targetUrl = resolveTargetUrl(request.url, baseURL);
|
|
367
581
|
const target = new URL(targetUrl);
|
|
368
582
|
const auth = await resolveAuth(settings.auth);
|
|
583
|
+
if ((request.method ?? "GET").toUpperCase() === "GET" &&
|
|
584
|
+
target.pathname.endsWith("/models") &&
|
|
585
|
+
!target.searchParams.has("client_version")) {
|
|
586
|
+
const catalog = await resolveModelCatalog(auth);
|
|
587
|
+
const models = catalog.models.filter(isPublicCodexModel);
|
|
588
|
+
if (models.length === 0) {
|
|
589
|
+
return Response.json({
|
|
590
|
+
error: {
|
|
591
|
+
message: catalog.error?.message ?? "Failed to load models from Codex.",
|
|
592
|
+
},
|
|
593
|
+
}, { status: 502 });
|
|
594
|
+
}
|
|
595
|
+
return Response.json({
|
|
596
|
+
object: "list",
|
|
597
|
+
data: models.map((model) => ({
|
|
598
|
+
id: model.slug,
|
|
599
|
+
object: "model",
|
|
600
|
+
created: 0,
|
|
601
|
+
owned_by: "codex-oauth",
|
|
602
|
+
})),
|
|
603
|
+
});
|
|
604
|
+
}
|
|
369
605
|
const headers = new Headers(settings.headers);
|
|
370
606
|
request.headers.forEach((value, key) => {
|
|
371
607
|
headers.set(key, value);
|
|
372
608
|
});
|
|
373
|
-
headers
|
|
374
|
-
headers.
|
|
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);
|
|
609
|
+
applyAuthHeaders(headers, auth);
|
|
610
|
+
const preparedBody = await prepareResponsesRequestBody(target.pathname, headers, request.body, settings, responsesState, auth, resolveModelInfo);
|
|
380
611
|
const response = await fetch(target.toString(), {
|
|
381
612
|
method: request.method ?? init?.method,
|
|
382
613
|
headers,
|
|
383
614
|
body: preparedBody.body,
|
|
384
615
|
signal: request.signal ?? undefined,
|
|
385
616
|
});
|
|
386
|
-
return
|
|
617
|
+
return finalizeResponsesResponse(response, preparedBody, responsesState);
|
|
387
618
|
};
|
|
388
619
|
};
|
|
389
620
|
const resolveOpenAICompatibleUrl = (path, baseURL) => {
|