@vostride/agent-qa-subscription-auth 0.2.3 → 0.2.5
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/index.d.ts +36 -40
- package/dist/index.js +562 -652
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,715 +1,625 @@
|
|
|
1
|
+
import { createAuthFetch, generatePKCE, generateState } from "@vostride/agent-qa-core";
|
|
2
|
+
import { createServer } from "node:http";
|
|
1
3
|
// src/anthropic.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
var AUTH_BASES = {
|
|
13
|
-
max: "https://claude.ai/oauth/authorize",
|
|
14
|
-
console: "https://console.anthropic.com/oauth/authorize"
|
|
4
|
+
const ANTHROPIC_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
5
|
+
const ANTHROPIC_TOKEN_URL = "https://console.anthropic.com/v1/oauth/token";
|
|
6
|
+
const ANTHROPIC_REDIRECT_URI = "https://console.anthropic.com/oauth/code/callback";
|
|
7
|
+
const ANTHROPIC_SCOPES = "org:create_api_key user:profile user:inference";
|
|
8
|
+
const ANTHROPIC_BETA_FLAGS = "oauth-2025-04-20,interleaved-thinking-2025-05-14";
|
|
9
|
+
const SYSTEM_PREFIX = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
10
|
+
const TOOL_PREFIX = "mcp_";
|
|
11
|
+
const AUTH_BASES = {
|
|
12
|
+
max: "https://claude.ai/oauth/authorize",
|
|
13
|
+
console: "https://console.anthropic.com/oauth/authorize"
|
|
15
14
|
};
|
|
16
15
|
function anthropicAuthorizeUrl(options) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
28
|
-
return `${base}?${params.toString()}`;
|
|
16
|
+
return `${AUTH_BASES[options.mode]}?${new URLSearchParams({
|
|
17
|
+
code: "true",
|
|
18
|
+
client_id: ANTHROPIC_CLIENT_ID,
|
|
19
|
+
response_type: "code",
|
|
20
|
+
redirect_uri: ANTHROPIC_REDIRECT_URI,
|
|
21
|
+
scope: ANTHROPIC_SCOPES,
|
|
22
|
+
code_challenge: options.pkce.challenge,
|
|
23
|
+
code_challenge_method: "S256",
|
|
24
|
+
state: options.pkce.verifier
|
|
25
|
+
}).toString()}`;
|
|
29
26
|
}
|
|
30
27
|
function startAnthropicAuth() {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
const pkce = generatePKCE();
|
|
29
|
+
return {
|
|
30
|
+
authorizeUrl: anthropicAuthorizeUrl({
|
|
31
|
+
mode: "max",
|
|
32
|
+
pkce
|
|
33
|
+
}),
|
|
34
|
+
sessionState: { verifier: pkce.verifier }
|
|
35
|
+
};
|
|
36
36
|
}
|
|
37
37
|
function readVerifier(sessionState) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
throw new Error("Anthropic subscription auth session is missing PKCE verifier.");
|
|
38
|
+
if (sessionState && typeof sessionState === "object" && typeof sessionState.verifier === "string") return sessionState.verifier;
|
|
39
|
+
throw new Error("Anthropic subscription auth session is missing PKCE verifier.");
|
|
42
40
|
}
|
|
43
41
|
async function exchangeAnthropicManualCode(ctx) {
|
|
44
|
-
|
|
42
|
+
return await anthropicExchangeCode(ctx.code, { verifier: readVerifier(ctx.sessionState) });
|
|
45
43
|
}
|
|
46
44
|
async function anthropicExchangeCode(codeWithState, pkce) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
expires: Date.now() + json.expires_in * 1e3
|
|
72
|
-
};
|
|
45
|
+
const [code, state] = codeWithState.split("#");
|
|
46
|
+
const res = await globalThis.fetch(ANTHROPIC_TOKEN_URL, {
|
|
47
|
+
method: "POST",
|
|
48
|
+
headers: { "Content-Type": "application/json" },
|
|
49
|
+
body: JSON.stringify({
|
|
50
|
+
code,
|
|
51
|
+
state: state ?? pkce.verifier,
|
|
52
|
+
grant_type: "authorization_code",
|
|
53
|
+
client_id: ANTHROPIC_CLIENT_ID,
|
|
54
|
+
redirect_uri: ANTHROPIC_REDIRECT_URI,
|
|
55
|
+
code_verifier: pkce.verifier
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
if (!res.ok) {
|
|
59
|
+
const text = await res.text();
|
|
60
|
+
throw new Error(`Anthropic token exchange failed: ${text}`);
|
|
61
|
+
}
|
|
62
|
+
const json = await res.json();
|
|
63
|
+
if (typeof json.access_token !== "string" || typeof json.refresh_token !== "string" || typeof json.expires_in !== "number") throw new Error("Anthropic token exchange response is missing required fields");
|
|
64
|
+
return {
|
|
65
|
+
access: json.access_token,
|
|
66
|
+
refresh: json.refresh_token,
|
|
67
|
+
expires: Date.now() + json.expires_in * 1e3
|
|
68
|
+
};
|
|
73
69
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
expires: Date.now() + json.expires_in * 1e3
|
|
96
|
-
};
|
|
70
|
+
const anthropicRefreshTokens = async (_provider, refreshToken) => {
|
|
71
|
+
const res = await globalThis.fetch(ANTHROPIC_TOKEN_URL, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers: { "Content-Type": "application/json" },
|
|
74
|
+
body: JSON.stringify({
|
|
75
|
+
grant_type: "refresh_token",
|
|
76
|
+
refresh_token: refreshToken,
|
|
77
|
+
client_id: ANTHROPIC_CLIENT_ID
|
|
78
|
+
})
|
|
79
|
+
});
|
|
80
|
+
if (!res.ok) {
|
|
81
|
+
const text = await res.text();
|
|
82
|
+
throw new Error(`Anthropic token refresh failed: ${text}`);
|
|
83
|
+
}
|
|
84
|
+
const json = await res.json();
|
|
85
|
+
if (typeof json.access_token !== "string" || typeof json.expires_in !== "number") throw new Error("Anthropic token refresh response is missing required fields");
|
|
86
|
+
return {
|
|
87
|
+
access: json.access_token,
|
|
88
|
+
refresh: typeof json.refresh_token === "string" ? json.refresh_token : refreshToken,
|
|
89
|
+
expires: Date.now() + json.expires_in * 1e3
|
|
90
|
+
};
|
|
97
91
|
};
|
|
98
92
|
function prefixToolName(name) {
|
|
99
|
-
|
|
93
|
+
return typeof name === "string" && !name.startsWith(TOOL_PREFIX) ? `${TOOL_PREFIX}${name}` : name;
|
|
100
94
|
}
|
|
101
95
|
function stripToolNamePrefix(name) {
|
|
102
|
-
|
|
96
|
+
return typeof name === "string" && name.startsWith(TOOL_PREFIX) ? name.slice(4) : name;
|
|
103
97
|
}
|
|
104
98
|
function createAnthropicAuthFetch(getTokens, onRefreshed) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
} catch {
|
|
215
|
-
return new Response(rawText, {
|
|
216
|
-
status: response.status,
|
|
217
|
-
statusText: response.statusText,
|
|
218
|
-
headers: headers2
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
const stream = response.body.pipeThrough(createAnthropicToolNameTransform());
|
|
223
|
-
return new Response(stream, {
|
|
224
|
-
status: response.status,
|
|
225
|
-
statusText: response.statusText,
|
|
226
|
-
headers: response.headers
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
return response;
|
|
230
|
-
};
|
|
99
|
+
return async (input, init) => {
|
|
100
|
+
let tokens = await getTokens();
|
|
101
|
+
if (tokens.expires < Date.now()) {
|
|
102
|
+
tokens = await anthropicRefreshTokens("anthropic-subscription", tokens.refresh);
|
|
103
|
+
await onRefreshed(tokens);
|
|
104
|
+
}
|
|
105
|
+
const headers = new Headers();
|
|
106
|
+
if (input instanceof Request) input.headers.forEach((value, key) => {
|
|
107
|
+
headers.set(key, value);
|
|
108
|
+
});
|
|
109
|
+
if (init?.headers) {
|
|
110
|
+
if (init.headers instanceof Headers) init.headers.forEach((value, key) => {
|
|
111
|
+
headers.set(key, value);
|
|
112
|
+
});
|
|
113
|
+
else if (Array.isArray(init.headers)) {
|
|
114
|
+
for (const [key, value] of init.headers) if (value !== void 0) headers.set(key, String(value));
|
|
115
|
+
} else for (const [key, value] of Object.entries(init.headers)) if (value !== void 0) headers.set(key, String(value));
|
|
116
|
+
}
|
|
117
|
+
const incoming = (headers.get("anthropic-beta") || "").split(",").map((b) => b.trim()).filter(Boolean);
|
|
118
|
+
const required = ANTHROPIC_BETA_FLAGS.split(",");
|
|
119
|
+
const merged = [.../* @__PURE__ */ new Set([...required, ...incoming])].join(",");
|
|
120
|
+
headers.delete("authorization");
|
|
121
|
+
headers.delete("x-api-key");
|
|
122
|
+
headers.set("Authorization", `Bearer ${tokens.access}`);
|
|
123
|
+
headers.set("anthropic-beta", merged);
|
|
124
|
+
headers.set("user-agent", "claude-cli/2.1.2 (external, cli)");
|
|
125
|
+
let body = init?.body;
|
|
126
|
+
if (body && typeof body === "string") try {
|
|
127
|
+
const parsed = JSON.parse(body);
|
|
128
|
+
if (parsed.system && Array.isArray(parsed.system)) parsed.system = [{
|
|
129
|
+
type: "text",
|
|
130
|
+
text: SYSTEM_PREFIX
|
|
131
|
+
}, ...parsed.system];
|
|
132
|
+
else if (typeof parsed.system === "string") parsed.system = SYSTEM_PREFIX + "\n\n" + parsed.system;
|
|
133
|
+
else if (!parsed.system) parsed.system = [{
|
|
134
|
+
type: "text",
|
|
135
|
+
text: SYSTEM_PREFIX
|
|
136
|
+
}];
|
|
137
|
+
if (parsed.tools && Array.isArray(parsed.tools)) parsed.tools = parsed.tools.map((tool) => ({
|
|
138
|
+
...tool,
|
|
139
|
+
name: prefixToolName(tool.name)
|
|
140
|
+
}));
|
|
141
|
+
if (parsed.messages && Array.isArray(parsed.messages)) parsed.messages = parsed.messages.map((msg) => {
|
|
142
|
+
if (msg.content && Array.isArray(msg.content)) msg.content = msg.content.map((block) => {
|
|
143
|
+
if (block.type === "tool_use" && block.name) return {
|
|
144
|
+
...block,
|
|
145
|
+
name: prefixToolName(block.name)
|
|
146
|
+
};
|
|
147
|
+
return block;
|
|
148
|
+
});
|
|
149
|
+
return msg;
|
|
150
|
+
});
|
|
151
|
+
body = JSON.stringify(parsed);
|
|
152
|
+
} catch {}
|
|
153
|
+
let url;
|
|
154
|
+
if (typeof input === "string") url = input;
|
|
155
|
+
else if (input instanceof URL) url = input.toString();
|
|
156
|
+
else url = input.url;
|
|
157
|
+
try {
|
|
158
|
+
const parsed = new URL(url);
|
|
159
|
+
if (parsed.pathname === "/v1/messages" && !parsed.searchParams.has("beta")) {
|
|
160
|
+
parsed.searchParams.set("beta", "true");
|
|
161
|
+
url = parsed.toString();
|
|
162
|
+
}
|
|
163
|
+
} catch {}
|
|
164
|
+
const response = await globalThis.fetch(url, {
|
|
165
|
+
...init,
|
|
166
|
+
body,
|
|
167
|
+
headers
|
|
168
|
+
});
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
const text = await response.text();
|
|
171
|
+
return new Response(text, {
|
|
172
|
+
status: response.status,
|
|
173
|
+
statusText: response.statusText,
|
|
174
|
+
headers: response.headers
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
if (response.body) {
|
|
178
|
+
if ((response.headers.get("content-type") ?? "").includes("application/json")) {
|
|
179
|
+
const rawText = await response.text();
|
|
180
|
+
const headers = new Headers(response.headers);
|
|
181
|
+
headers.delete("content-length");
|
|
182
|
+
headers.delete("content-encoding");
|
|
183
|
+
try {
|
|
184
|
+
const json = JSON.parse(rawText);
|
|
185
|
+
rewriteToolUseNames(json);
|
|
186
|
+
return new Response(JSON.stringify(json), {
|
|
187
|
+
status: response.status,
|
|
188
|
+
statusText: response.statusText,
|
|
189
|
+
headers
|
|
190
|
+
});
|
|
191
|
+
} catch {
|
|
192
|
+
return new Response(rawText, {
|
|
193
|
+
status: response.status,
|
|
194
|
+
statusText: response.statusText,
|
|
195
|
+
headers
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const stream = response.body.pipeThrough(createAnthropicToolNameTransform());
|
|
200
|
+
return new Response(stream, {
|
|
201
|
+
status: response.status,
|
|
202
|
+
statusText: response.statusText,
|
|
203
|
+
headers: response.headers
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return response;
|
|
207
|
+
};
|
|
231
208
|
}
|
|
232
209
|
function createAnthropicToolNameTransform() {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
210
|
+
const decoder = new TextDecoder();
|
|
211
|
+
const encoder = new TextEncoder();
|
|
212
|
+
let pending = "";
|
|
213
|
+
return new TransformStream({
|
|
214
|
+
transform(chunk, controller) {
|
|
215
|
+
pending += decoder.decode(chunk, { stream: true });
|
|
216
|
+
let newlineIndex = pending.indexOf("\n");
|
|
217
|
+
while (newlineIndex !== -1) {
|
|
218
|
+
const line = pending.slice(0, newlineIndex + 1);
|
|
219
|
+
pending = pending.slice(newlineIndex + 1);
|
|
220
|
+
controller.enqueue(encoder.encode(rewriteAnthropicEventLine(line)));
|
|
221
|
+
newlineIndex = pending.indexOf("\n");
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
flush(controller) {
|
|
225
|
+
pending += decoder.decode();
|
|
226
|
+
if (pending) {
|
|
227
|
+
controller.enqueue(encoder.encode(rewriteAnthropicEventLine(pending)));
|
|
228
|
+
pending = "";
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
});
|
|
255
232
|
}
|
|
256
233
|
function rewriteAnthropicEventLine(line) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
234
|
+
const match = /^(data:\s*)(.*?)(\r?\n?)$/.exec(line);
|
|
235
|
+
if (!match) return line;
|
|
236
|
+
const [, prefix, jsonText, suffix] = match;
|
|
237
|
+
if (!jsonText || jsonText === "[DONE]") return line;
|
|
238
|
+
try {
|
|
239
|
+
const event = JSON.parse(jsonText);
|
|
240
|
+
rewriteToolUseNames(event);
|
|
241
|
+
return `${prefix}${JSON.stringify(event)}${suffix}`;
|
|
242
|
+
} catch {
|
|
243
|
+
return line;
|
|
244
|
+
}
|
|
268
245
|
}
|
|
269
246
|
function rewriteToolUseNames(value) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
for (const item of Object.values(record)) {
|
|
280
|
-
rewriteToolUseNames(item);
|
|
281
|
-
}
|
|
247
|
+
if (!value || typeof value !== "object") return;
|
|
248
|
+
if (Array.isArray(value)) {
|
|
249
|
+
for (const item of value) rewriteToolUseNames(item);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const record = value;
|
|
253
|
+
if (record.type === "tool_use") record.name = stripToolNamePrefix(record.name);
|
|
254
|
+
for (const item of Object.values(record)) rewriteToolUseNames(item);
|
|
282
255
|
}
|
|
283
|
-
|
|
284
256
|
// src/openai-codex.ts
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
var CODEX_AUTH_BASE = "https://auth.openai.com";
|
|
293
|
-
var CODEX_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
294
|
-
var CODEX_SCOPES = "openid profile email offline_access";
|
|
295
|
-
var CODEX_API_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
296
|
-
var CODEX_DEFAULT_PORT = 1455;
|
|
297
|
-
var CODEX_CALLBACK_HOST = "localhost";
|
|
298
|
-
var CODEX_LISTEN_HOST = "127.0.0.1";
|
|
257
|
+
const CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
258
|
+
const CODEX_AUTH_BASE = "https://auth.openai.com";
|
|
259
|
+
const CODEX_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
260
|
+
const CODEX_SCOPES = "openid profile email offline_access";
|
|
261
|
+
const CODEX_API_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
262
|
+
const CODEX_DEFAULT_PORT = 1455;
|
|
263
|
+
const CODEX_CALLBACK_HOST = "localhost";
|
|
299
264
|
function decodeJwtClaims(token) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
265
|
+
const parts = token.split(".");
|
|
266
|
+
if (parts.length < 3) throw new Error("Invalid JWT: expected 3 segments");
|
|
267
|
+
const payload = parts[1];
|
|
268
|
+
const decoded = Buffer.from(payload, "base64url").toString("utf-8");
|
|
269
|
+
return JSON.parse(decoded);
|
|
305
270
|
}
|
|
306
271
|
function extractAccountId(idToken) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
272
|
+
try {
|
|
273
|
+
const claims = decodeJwtClaims(idToken);
|
|
274
|
+
const authClaim = claims["https://api.openai.com/auth"];
|
|
275
|
+
if (authClaim && typeof authClaim === "object") {
|
|
276
|
+
const userIdStr = authClaim.user_id ?? authClaim.account_id;
|
|
277
|
+
if (userIdStr) return userIdStr;
|
|
278
|
+
}
|
|
279
|
+
return claims.sub ?? void 0;
|
|
280
|
+
} catch {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
318
283
|
}
|
|
319
284
|
async function codexExchangeCode(code, redirectUri, pkce) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
accountId
|
|
346
|
-
};
|
|
285
|
+
const body = new URLSearchParams({
|
|
286
|
+
grant_type: "authorization_code",
|
|
287
|
+
client_id: CODEX_CLIENT_ID,
|
|
288
|
+
code,
|
|
289
|
+
redirect_uri: redirectUri,
|
|
290
|
+
code_verifier: pkce.verifier
|
|
291
|
+
});
|
|
292
|
+
const res = await globalThis.fetch(CODEX_TOKEN_URL, {
|
|
293
|
+
method: "POST",
|
|
294
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
295
|
+
body: body.toString()
|
|
296
|
+
});
|
|
297
|
+
if (!res.ok) {
|
|
298
|
+
const text = await res.text();
|
|
299
|
+
throw new Error(`Codex token exchange failed: ${text}`);
|
|
300
|
+
}
|
|
301
|
+
const json = await res.json();
|
|
302
|
+
if (typeof json.access_token !== "string" || typeof json.refresh_token !== "string" || typeof json.expires_in !== "number") throw new Error("Codex token exchange response is missing required fields");
|
|
303
|
+
const accountId = typeof json.id_token === "string" ? extractAccountId(json.id_token) : void 0;
|
|
304
|
+
return {
|
|
305
|
+
access: json.access_token,
|
|
306
|
+
refresh: json.refresh_token,
|
|
307
|
+
expires: Date.now() + json.expires_in * 1e3,
|
|
308
|
+
accountId
|
|
309
|
+
};
|
|
347
310
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
accountId
|
|
373
|
-
};
|
|
311
|
+
const codexRefreshTokens = async (_provider, refreshToken) => {
|
|
312
|
+
const body = new URLSearchParams({
|
|
313
|
+
grant_type: "refresh_token",
|
|
314
|
+
client_id: CODEX_CLIENT_ID,
|
|
315
|
+
refresh_token: refreshToken
|
|
316
|
+
});
|
|
317
|
+
const res = await globalThis.fetch(CODEX_TOKEN_URL, {
|
|
318
|
+
method: "POST",
|
|
319
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
320
|
+
body: body.toString()
|
|
321
|
+
});
|
|
322
|
+
if (!res.ok) {
|
|
323
|
+
const text = await res.text();
|
|
324
|
+
throw new Error(`Codex token refresh failed: ${text}`);
|
|
325
|
+
}
|
|
326
|
+
const json = await res.json();
|
|
327
|
+
if (typeof json.access_token !== "string" || typeof json.expires_in !== "number") throw new Error("Codex token refresh response is missing required fields");
|
|
328
|
+
const accountId = typeof json.id_token === "string" ? extractAccountId(json.id_token) : void 0;
|
|
329
|
+
return {
|
|
330
|
+
access: json.access_token,
|
|
331
|
+
refresh: typeof json.refresh_token === "string" ? json.refresh_token : refreshToken,
|
|
332
|
+
expires: Date.now() + json.expires_in * 1e3,
|
|
333
|
+
accountId
|
|
334
|
+
};
|
|
374
335
|
};
|
|
375
336
|
async function codexBrowserAuth(options) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
server.close();
|
|
445
|
-
};
|
|
446
|
-
return { authorizeUrl, waitForCallback, cleanup };
|
|
337
|
+
const port = options?.port ?? 1455;
|
|
338
|
+
const listenHost = options?.listenHost ?? "127.0.0.1";
|
|
339
|
+
const redirectUri = `http://${CODEX_CALLBACK_HOST}:${port}/auth/callback`;
|
|
340
|
+
const pkce = generatePKCE();
|
|
341
|
+
const state = generateState();
|
|
342
|
+
let server;
|
|
343
|
+
let resolveTokens;
|
|
344
|
+
let rejectTokens;
|
|
345
|
+
const waitForCallback = new Promise((resolve, reject) => {
|
|
346
|
+
resolveTokens = resolve;
|
|
347
|
+
rejectTokens = reject;
|
|
348
|
+
});
|
|
349
|
+
server = createServer(async (req, res) => {
|
|
350
|
+
const url = new URL(req.url ?? "/", redirectUri);
|
|
351
|
+
if (url.pathname === "/auth/callback") {
|
|
352
|
+
const code = url.searchParams.get("code");
|
|
353
|
+
if (url.searchParams.get("state") !== state) {
|
|
354
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
355
|
+
res.end("<html><body>State mismatch. Please try again.</body></html>");
|
|
356
|
+
rejectTokens(/* @__PURE__ */ new Error("OAuth state mismatch"));
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
if (!code) {
|
|
360
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
361
|
+
res.end("<html><body>Missing authorization code.</body></html>");
|
|
362
|
+
rejectTokens(/* @__PURE__ */ new Error("Missing authorization code"));
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
try {
|
|
366
|
+
const tokens = await codexExchangeCode(code, redirectUri, { verifier: pkce.verifier });
|
|
367
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
368
|
+
res.end("<html><body>Authentication successful! You can close this tab.</body></html>");
|
|
369
|
+
resolveTokens(tokens);
|
|
370
|
+
} catch (err) {
|
|
371
|
+
res.writeHead(500, { "Content-Type": "text/html" });
|
|
372
|
+
res.end("<html><body>Token exchange failed.</body></html>");
|
|
373
|
+
rejectTokens(err instanceof Error ? err : /* @__PURE__ */ new Error("Token exchange failed"));
|
|
374
|
+
}
|
|
375
|
+
} else {
|
|
376
|
+
res.writeHead(404);
|
|
377
|
+
res.end();
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
await new Promise((resolve, reject) => {
|
|
381
|
+
server.once("error", reject);
|
|
382
|
+
server.listen(port, listenHost, () => {
|
|
383
|
+
server.off("error", reject);
|
|
384
|
+
resolve();
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
const params = new URLSearchParams({
|
|
388
|
+
response_type: "code",
|
|
389
|
+
client_id: CODEX_CLIENT_ID,
|
|
390
|
+
redirect_uri: redirectUri,
|
|
391
|
+
scope: CODEX_SCOPES,
|
|
392
|
+
state,
|
|
393
|
+
code_challenge: pkce.challenge,
|
|
394
|
+
code_challenge_method: "S256"
|
|
395
|
+
});
|
|
396
|
+
const authorizeUrl = `${CODEX_AUTH_BASE}/oauth/authorize?${params.toString()}`;
|
|
397
|
+
const cleanup = () => {
|
|
398
|
+
server.close();
|
|
399
|
+
};
|
|
400
|
+
return {
|
|
401
|
+
authorizeUrl,
|
|
402
|
+
waitForCallback,
|
|
403
|
+
cleanup
|
|
404
|
+
};
|
|
447
405
|
}
|
|
448
406
|
function createCodexAuthFetch(getTokens, onRefreshed) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
const response = await authFetch(input, { ...init, body });
|
|
485
|
-
if (forceStream && response.ok && response.body) {
|
|
486
|
-
return bufferCodexSSEResponse(response);
|
|
487
|
-
}
|
|
488
|
-
return response;
|
|
489
|
-
};
|
|
407
|
+
const authFetch = createAuthFetch({
|
|
408
|
+
provider: "openai-codex",
|
|
409
|
+
getTokens,
|
|
410
|
+
refreshTokens: codexRefreshTokens,
|
|
411
|
+
onTokensRefreshed: onRefreshed,
|
|
412
|
+
headerTransform: (headers, tokens) => {
|
|
413
|
+
if (tokens.accountId) headers.set("ChatGPT-Account-Id", tokens.accountId);
|
|
414
|
+
},
|
|
415
|
+
urlTransform: (url) => {
|
|
416
|
+
if (url.includes("/v1/responses") || url.includes("/chat/completions")) return CODEX_API_URL;
|
|
417
|
+
return url;
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
return async (input, init) => {
|
|
421
|
+
const inputUrl = extractRequestUrl(input);
|
|
422
|
+
let forceStream = false;
|
|
423
|
+
let body = init?.body;
|
|
424
|
+
if (isCodexRequestUrl(inputUrl) && body && typeof body === "string") try {
|
|
425
|
+
const parsed = JSON.parse(body);
|
|
426
|
+
if (!parsed.stream) {
|
|
427
|
+
parsed.stream = true;
|
|
428
|
+
forceStream = true;
|
|
429
|
+
}
|
|
430
|
+
if (parsed.text?.format?.strict) parsed.text.format.strict = false;
|
|
431
|
+
body = JSON.stringify(parsed);
|
|
432
|
+
} catch {}
|
|
433
|
+
const response = await authFetch(input, {
|
|
434
|
+
...init,
|
|
435
|
+
body
|
|
436
|
+
});
|
|
437
|
+
if (forceStream && response.ok && response.body) return bufferCodexSSEResponse(response);
|
|
438
|
+
return response;
|
|
439
|
+
};
|
|
490
440
|
}
|
|
491
441
|
function extractRequestUrl(input) {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
442
|
+
if (typeof input === "string") return input;
|
|
443
|
+
if (input instanceof URL) return input.toString();
|
|
444
|
+
return input.url;
|
|
495
445
|
}
|
|
496
446
|
function isCodexRequestUrl(url) {
|
|
497
|
-
|
|
447
|
+
return url.includes("/v1/responses") || url.includes("/chat/completions") || url.includes("chatgpt.com/backend-api/codex");
|
|
498
448
|
}
|
|
499
449
|
async function bufferCodexSSEResponse(response) {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
} finally {
|
|
561
|
-
clearTimeout(overallTimeout);
|
|
562
|
-
}
|
|
563
|
-
for (const [outputIndex, item] of pendingOutputItems) {
|
|
564
|
-
if (!completedOutputItems.has(outputIndex)) {
|
|
565
|
-
completedOutputItems.set(
|
|
566
|
-
outputIndex,
|
|
567
|
-
hydrateOutputItem(item, outputIndex, functionCallArgumentDeltas, customToolInputDeltas)
|
|
568
|
-
);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
const bufferedOutput = [...completedOutputItems.entries()].sort(([left], [right]) => left - right).map(([, item]) => item);
|
|
572
|
-
const body = JSON.stringify(
|
|
573
|
-
ensureResponseMetadata(
|
|
574
|
-
completedResponse ? mergeBufferedOutputItems(completedResponse, bufferedOutput, outputText) : createBufferedResponse(bufferedOutput, outputText)
|
|
575
|
-
)
|
|
576
|
-
);
|
|
577
|
-
return new Response(body, {
|
|
578
|
-
status: 200,
|
|
579
|
-
headers: { "content-type": "application/json" }
|
|
580
|
-
});
|
|
450
|
+
const reader = response.body.getReader();
|
|
451
|
+
const decoder = new TextDecoder();
|
|
452
|
+
let buffer = "";
|
|
453
|
+
let completedResponse = null;
|
|
454
|
+
let outputText = "";
|
|
455
|
+
const pendingOutputItems = /* @__PURE__ */ new Map();
|
|
456
|
+
const completedOutputItems = /* @__PURE__ */ new Map();
|
|
457
|
+
const functionCallArgumentDeltas = /* @__PURE__ */ new Map();
|
|
458
|
+
const customToolInputDeltas = /* @__PURE__ */ new Map();
|
|
459
|
+
const overallTimeout = setTimeout(() => {
|
|
460
|
+
try {
|
|
461
|
+
reader.cancel();
|
|
462
|
+
} catch {}
|
|
463
|
+
}, 12e4);
|
|
464
|
+
try {
|
|
465
|
+
while (true) {
|
|
466
|
+
const chunkPromise = reader.read();
|
|
467
|
+
const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve({
|
|
468
|
+
done: true,
|
|
469
|
+
value: void 0
|
|
470
|
+
}), 3e4));
|
|
471
|
+
const { done, value } = await Promise.race([chunkPromise, timeoutPromise]);
|
|
472
|
+
if (done) break;
|
|
473
|
+
buffer += decoder.decode(value, { stream: true });
|
|
474
|
+
const lines = buffer.split("\n");
|
|
475
|
+
buffer = lines.pop() ?? "";
|
|
476
|
+
for (const line of lines) {
|
|
477
|
+
if (!line.startsWith("data: ")) continue;
|
|
478
|
+
const data = line.slice(6).trim();
|
|
479
|
+
if (data === "[DONE]") continue;
|
|
480
|
+
try {
|
|
481
|
+
const event = JSON.parse(data);
|
|
482
|
+
if (!isJSONRecord(event) || typeof event.type !== "string") continue;
|
|
483
|
+
if (event.type === "response.output_text.delta" && typeof event.delta === "string") outputText += event.delta;
|
|
484
|
+
if (event.type === "response.output_item.added" && typeof event.output_index === "number" && isJSONRecord(event.item)) pendingOutputItems.set(event.output_index, { ...event.item });
|
|
485
|
+
if (event.type === "response.function_call_arguments.delta" && typeof event.output_index === "number" && typeof event.delta === "string") {
|
|
486
|
+
const prev = functionCallArgumentDeltas.get(event.output_index) ?? "";
|
|
487
|
+
functionCallArgumentDeltas.set(event.output_index, prev + event.delta);
|
|
488
|
+
}
|
|
489
|
+
if (event.type === "response.custom_tool_call_input.delta" && typeof event.output_index === "number" && typeof event.delta === "string") {
|
|
490
|
+
const prev = customToolInputDeltas.get(event.output_index) ?? "";
|
|
491
|
+
customToolInputDeltas.set(event.output_index, prev + event.delta);
|
|
492
|
+
}
|
|
493
|
+
if (event.type === "response.output_item.done" && typeof event.output_index === "number" && isJSONRecord(event.item)) completedOutputItems.set(event.output_index, hydrateOutputItem(event.item, event.output_index, functionCallArgumentDeltas, customToolInputDeltas));
|
|
494
|
+
if ((event.type === "response.completed" || event.type === "response.incomplete") && isJSONRecord(event.response)) completedResponse = event.response;
|
|
495
|
+
} catch {}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
} finally {
|
|
499
|
+
clearTimeout(overallTimeout);
|
|
500
|
+
}
|
|
501
|
+
for (const [outputIndex, item] of pendingOutputItems) if (!completedOutputItems.has(outputIndex)) completedOutputItems.set(outputIndex, hydrateOutputItem(item, outputIndex, functionCallArgumentDeltas, customToolInputDeltas));
|
|
502
|
+
const bufferedOutput = [...completedOutputItems.entries()].sort(([left], [right]) => left - right).map(([, item]) => item);
|
|
503
|
+
const body = JSON.stringify(ensureResponseMetadata(completedResponse ? mergeBufferedOutputItems(completedResponse, bufferedOutput, outputText) : createBufferedResponse(bufferedOutput, outputText)));
|
|
504
|
+
return new Response(body, {
|
|
505
|
+
status: 200,
|
|
506
|
+
headers: { "content-type": "application/json" }
|
|
507
|
+
});
|
|
581
508
|
}
|
|
582
509
|
function isJSONRecord(value) {
|
|
583
|
-
|
|
510
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
584
511
|
}
|
|
585
512
|
function hydrateOutputItem(item, outputIndex, functionCallArgumentDeltas, customToolInputDeltas) {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
hydrated.input = safeParseJSON(input) ?? input;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
return hydrated;
|
|
513
|
+
const hydrated = { ...item };
|
|
514
|
+
if (hydrated.type === "function_call" && typeof hydrated.arguments !== "string") hydrated.arguments = functionCallArgumentDeltas.get(outputIndex) ?? "";
|
|
515
|
+
if (hydrated.type === "custom_tool_call" && hydrated.input == null) {
|
|
516
|
+
const input = customToolInputDeltas.get(outputIndex);
|
|
517
|
+
if (input !== void 0) hydrated.input = safeParseJSON(input) ?? input;
|
|
518
|
+
}
|
|
519
|
+
return hydrated;
|
|
597
520
|
}
|
|
598
521
|
function mergeBufferedOutputItems(response, bufferedOutput, outputText) {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
522
|
+
const existingOutput = Array.isArray(response.output) ? response.output.filter(isJSONRecord) : [];
|
|
523
|
+
if (bufferedOutput.length === 0) {
|
|
524
|
+
if (existingOutput.length > 0) return response;
|
|
525
|
+
return {
|
|
526
|
+
...response,
|
|
527
|
+
output: [buildTextOutput(outputText)]
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
const mergedOutput = [...existingOutput];
|
|
531
|
+
const seenKeys = new Set(existingOutput.map(getOutputItemKey).filter((key) => key !== null));
|
|
532
|
+
for (const item of bufferedOutput) {
|
|
533
|
+
const key = getOutputItemKey(item);
|
|
534
|
+
if (key && seenKeys.has(key)) continue;
|
|
535
|
+
if (key) seenKeys.add(key);
|
|
536
|
+
mergedOutput.push(item);
|
|
537
|
+
}
|
|
538
|
+
return {
|
|
539
|
+
...response,
|
|
540
|
+
output: mergedOutput
|
|
541
|
+
};
|
|
613
542
|
}
|
|
614
543
|
function createBufferedResponse(bufferedOutput, outputText) {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
544
|
+
return {
|
|
545
|
+
id: "codex-buffered",
|
|
546
|
+
output: bufferedOutput.length > 0 ? bufferedOutput : [buildTextOutput(outputText)],
|
|
547
|
+
status: "completed"
|
|
548
|
+
};
|
|
620
549
|
}
|
|
621
550
|
function ensureResponseMetadata(response) {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
551
|
+
if (typeof response.created_at === "number") return response;
|
|
552
|
+
return {
|
|
553
|
+
...response,
|
|
554
|
+
created_at: Math.floor(Date.now() / 1e3)
|
|
555
|
+
};
|
|
627
556
|
}
|
|
628
557
|
function buildTextOutput(outputText) {
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
558
|
+
return {
|
|
559
|
+
type: "message",
|
|
560
|
+
role: "assistant",
|
|
561
|
+
content: [{
|
|
562
|
+
type: "output_text",
|
|
563
|
+
text: outputText,
|
|
564
|
+
annotations: []
|
|
565
|
+
}]
|
|
566
|
+
};
|
|
634
567
|
}
|
|
635
568
|
function getOutputItemKey(item) {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
569
|
+
const type = typeof item.type === "string" ? item.type : "unknown";
|
|
570
|
+
if (typeof item.id === "string") return `${type}:${item.id}`;
|
|
571
|
+
if (typeof item.call_id === "string") return `${type}:${item.call_id}`;
|
|
572
|
+
return null;
|
|
640
573
|
}
|
|
641
574
|
function safeParseJSON(text) {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
575
|
+
try {
|
|
576
|
+
return JSON.parse(text);
|
|
577
|
+
} catch {
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
647
580
|
}
|
|
648
|
-
|
|
649
581
|
// src/index.ts
|
|
650
582
|
function createSubscriptionAuthPlugin() {
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
583
|
+
return { providers: [{
|
|
584
|
+
providerId: "openai-subscription",
|
|
585
|
+
credentialProviderId: "openai-subscription-oauth",
|
|
586
|
+
label: "OpenAI subscription",
|
|
587
|
+
modelAdapter: "openai-responses",
|
|
588
|
+
dashboardAuth: {
|
|
589
|
+
mode: "browser-poll",
|
|
590
|
+
buttonLabel: "Login with OpenAI subscription"
|
|
591
|
+
},
|
|
592
|
+
async startAuth() {
|
|
593
|
+
const result = await codexBrowserAuth();
|
|
594
|
+
return {
|
|
595
|
+
authorizeUrl: result.authorizeUrl,
|
|
596
|
+
waitForTokens: result.waitForCallback,
|
|
597
|
+
cleanup: result.cleanup
|
|
598
|
+
};
|
|
599
|
+
},
|
|
600
|
+
createAuthFetch({ getTokens, onRefreshed }) {
|
|
601
|
+
return createCodexAuthFetch(getTokens, onRefreshed);
|
|
602
|
+
}
|
|
603
|
+
}, {
|
|
604
|
+
providerId: "anthropic-subscription",
|
|
605
|
+
credentialProviderId: "anthropic-subscription",
|
|
606
|
+
label: "Anthropic subscription",
|
|
607
|
+
modelAdapter: "anthropic-messages",
|
|
608
|
+
dashboardAuth: {
|
|
609
|
+
mode: "manual-code",
|
|
610
|
+
buttonLabel: "Login with Anthropic subscription"
|
|
611
|
+
},
|
|
612
|
+
async startAuth() {
|
|
613
|
+
return startAnthropicAuth();
|
|
614
|
+
},
|
|
615
|
+
async exchangeCode(ctx) {
|
|
616
|
+
return await exchangeAnthropicManualCode(ctx);
|
|
617
|
+
},
|
|
618
|
+
createAuthFetch({ getTokens, onRefreshed }) {
|
|
619
|
+
return createAnthropicAuthFetch(getTokens, onRefreshed);
|
|
620
|
+
}
|
|
621
|
+
}] };
|
|
689
622
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
ANTHROPIC_BETA_FLAGS,
|
|
693
|
-
ANTHROPIC_CLIENT_ID,
|
|
694
|
-
ANTHROPIC_REDIRECT_URI,
|
|
695
|
-
ANTHROPIC_SCOPES,
|
|
696
|
-
ANTHROPIC_TOKEN_URL,
|
|
697
|
-
CODEX_API_URL,
|
|
698
|
-
CODEX_AUTH_BASE,
|
|
699
|
-
CODEX_CLIENT_ID,
|
|
700
|
-
CODEX_DEFAULT_PORT,
|
|
701
|
-
CODEX_SCOPES,
|
|
702
|
-
CODEX_TOKEN_URL,
|
|
703
|
-
anthropicAuthorizeUrl,
|
|
704
|
-
anthropicExchangeCode,
|
|
705
|
-
anthropicRefreshTokens,
|
|
706
|
-
codexBrowserAuth,
|
|
707
|
-
codexExchangeCode,
|
|
708
|
-
codexRefreshTokens,
|
|
709
|
-
createAnthropicAuthFetch,
|
|
710
|
-
createCodexAuthFetch,
|
|
711
|
-
createSubscriptionAuthPlugin,
|
|
712
|
-
decodeJwtClaims,
|
|
713
|
-
index_default as default
|
|
714
|
-
};
|
|
623
|
+
export { ANTHROPIC_BETA_FLAGS, ANTHROPIC_CLIENT_ID, ANTHROPIC_REDIRECT_URI, ANTHROPIC_SCOPES, ANTHROPIC_TOKEN_URL, CODEX_API_URL, CODEX_AUTH_BASE, CODEX_CLIENT_ID, CODEX_DEFAULT_PORT, CODEX_SCOPES, CODEX_TOKEN_URL, anthropicAuthorizeUrl, anthropicExchangeCode, anthropicRefreshTokens, codexBrowserAuth, codexExchangeCode, codexRefreshTokens, createAnthropicAuthFetch, createCodexAuthFetch, createSubscriptionAuthPlugin, createSubscriptionAuthPlugin as default, decodeJwtClaims };
|
|
624
|
+
|
|
715
625
|
//# sourceMappingURL=index.js.map
|