@omnicross/subscriptions 0.3.0 → 0.4.0

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.
@@ -0,0 +1,1109 @@
1
+ import {
2
+ __export
3
+ } from "./chunk-MLKGABMK.js";
4
+
5
+ // src/oauth/flows/copilot.ts
6
+ var copilot_exports = {};
7
+ __export(copilot_exports, {
8
+ COPILOT_API_HEADERS: () => COPILOT_API_HEADERS,
9
+ COPILOT_FAR_FUTURE_MS: () => COPILOT_FAR_FUTURE_MS,
10
+ COPILOT_GITHUB_HEADERS: () => COPILOT_GITHUB_HEADERS,
11
+ COPILOT_OAUTH_CONFIG: () => COPILOT_OAUTH_CONFIG,
12
+ COPILOT_POLL_INITIAL_MULTIPLIER: () => COPILOT_POLL_INITIAL_MULTIPLIER,
13
+ COPILOT_POLL_SLOW_DOWN_MULTIPLIER: () => COPILOT_POLL_SLOW_DOWN_MULTIPLIER,
14
+ awaitCopilotDeviceToken: () => awaitCopilotDeviceToken,
15
+ copilotOAuthUrls: () => copilotOAuthUrls,
16
+ discoverCopilotApiEndpoint: () => discoverCopilotApiEndpoint,
17
+ enableAllCopilotModels: () => enableAllCopilotModels,
18
+ enableCopilotModel: () => enableCopilotModel,
19
+ fetchCopilotIdentity: () => fetchCopilotIdentity,
20
+ normalizeCopilotEnterpriseDomain: () => normalizeCopilotEnterpriseDomain,
21
+ pollCopilotDeviceToken: () => pollCopilotDeviceToken,
22
+ refreshCopilotToken: () => refreshCopilotToken,
23
+ requestCopilotDeviceAuthorization: () => requestCopilotDeviceAuthorization
24
+ });
25
+
26
+ // src/copilot/models.ts
27
+ var WIRES = {
28
+ anthropic: /* @__PURE__ */ new Set([
29
+ "claude-haiku-4.5",
30
+ "claude-sonnet-4",
31
+ "claude-sonnet-4.5",
32
+ "claude-sonnet-4.6",
33
+ "claude-sonnet-5",
34
+ "claude-opus-4.5",
35
+ "claude-opus-4.6",
36
+ "claude-opus-4.7",
37
+ "claude-opus-4.8",
38
+ "claude-opus-5",
39
+ "claude-fable-5"
40
+ ]),
41
+ chat: /* @__PURE__ */ new Set([
42
+ "gpt-4o",
43
+ "gpt-4.1",
44
+ "grok-code-fast-1",
45
+ "gemini-2.5-pro",
46
+ "gemini-3.5-flash",
47
+ "gemini-3-flash-preview",
48
+ "gemini-3-pro-preview",
49
+ "gemini-3.6-flash",
50
+ "gemini-3.7-flash",
51
+ "gemini-3.1-pro-preview",
52
+ "raptor-mini",
53
+ "kimi-k2.7-code",
54
+ "kimi-k3"
55
+ ]),
56
+ responses: /* @__PURE__ */ new Set([
57
+ "gpt-5",
58
+ "gpt-5-mini",
59
+ "gpt-5.1",
60
+ "gpt-5.1-codex",
61
+ "gpt-5.1-codex-max",
62
+ "gpt-5.1-codex-mini",
63
+ "gpt-5.2",
64
+ "gpt-5.2-codex",
65
+ "gpt-5.3-codex",
66
+ "gpt-5.4",
67
+ "gpt-5.4-mini",
68
+ "gpt-5.4-nano",
69
+ "gpt-5.5",
70
+ "gpt-5.6-luna",
71
+ "gpt-5.6-sol",
72
+ "gpt-5.6-terra",
73
+ "grok-4.5",
74
+ "grok-4.6",
75
+ "mai-code-1-flash-picker",
76
+ "mai-code-1.1-flash"
77
+ ])
78
+ };
79
+ function copilotWireFor(modelId) {
80
+ if (WIRES.anthropic.has(modelId)) return "anthropic";
81
+ if (WIRES.chat.has(modelId)) return "chat";
82
+ return "responses";
83
+ }
84
+ function copilotPathFor(wire) {
85
+ if (wire === "anthropic") return "/v1/messages";
86
+ if (wire === "chat") return "/v1/chat/completions";
87
+ return "/v1/responses";
88
+ }
89
+ function copilotTransformerNamesForWire(wire) {
90
+ if (wire === "anthropic") return ["anthropic"];
91
+ if (wire === "chat") return ["openai"];
92
+ return ["openai-response"];
93
+ }
94
+ function copilotWireModelIds() {
95
+ return [...WIRES.anthropic, ...WIRES.chat, ...WIRES.responses];
96
+ }
97
+ function copilotBaseUrl(config) {
98
+ const endpoint = config?.apiEndpoint?.trim();
99
+ if (endpoint) return endpoint.replace(/\/+$/, "");
100
+ const enterprise = config?.enterpriseUrl?.trim().toLowerCase();
101
+ if (enterprise) {
102
+ const host = enterprise.startsWith("copilot-api.") ? enterprise : `copilot-api.${enterprise}`;
103
+ return `https://${host}`;
104
+ }
105
+ return "https://api.githubcopilot.com";
106
+ }
107
+ function copilotGitHubApiBase(enterpriseUrl) {
108
+ const enterprise = enterpriseUrl?.trim().toLowerCase();
109
+ if (!enterprise) return "https://api.github.com";
110
+ if (enterprise.startsWith("http://") || enterprise.startsWith("https://")) {
111
+ return enterprise.replace(/\/+$/, "");
112
+ }
113
+ if (enterprise.startsWith("api.")) return `https://${enterprise}`;
114
+ return `https://api.${enterprise}`;
115
+ }
116
+
117
+ // src/oauth/flows/copilot.ts
118
+ var COPILOT_OAUTH_CONFIG = {
119
+ clientId: "Ov23ctDVkRmgkPke0Mmm",
120
+ scope: "read:user",
121
+ deviceEndpoint: "https://github.com/login/device/code",
122
+ tokenEndpoint: "https://github.com/login/oauth/access_token"
123
+ };
124
+ var PUBLIC_GITHUB_HOSTS = /* @__PURE__ */ new Set([
125
+ "github.com",
126
+ "www.github.com",
127
+ "api.github.com"
128
+ ]);
129
+ function normalizeCopilotEnterpriseDomain(input) {
130
+ const trimmed = input?.trim();
131
+ if (!trimmed) return void 0;
132
+ let hostname2 = null;
133
+ try {
134
+ hostname2 = new URL(trimmed.includes("://") ? trimmed : `https://${trimmed}`).hostname;
135
+ } catch {
136
+ hostname2 = null;
137
+ }
138
+ const host = (hostname2 ?? trimmed).toLowerCase();
139
+ if (!host || PUBLIC_GITHUB_HOSTS.has(host)) return void 0;
140
+ if (!/^[a-z0-9][a-z0-9.-]*$/.test(host)) {
141
+ throw new Error(`invalid GitHub Enterprise domain '${trimmed}'`);
142
+ }
143
+ return host;
144
+ }
145
+ function copilotOAuthUrls(enterpriseUrl) {
146
+ const host = normalizeCopilotEnterpriseDomain(enterpriseUrl) ?? "github.com";
147
+ return {
148
+ deviceEndpoint: `https://${host}/login/device/code`,
149
+ tokenEndpoint: `https://${host}/login/oauth/access_token`
150
+ };
151
+ }
152
+ var COPILOT_POLL_INITIAL_MULTIPLIER = 1.2;
153
+ var COPILOT_POLL_SLOW_DOWN_MULTIPLIER = 1.4;
154
+ var COPILOT_CLI_VERSION = "1.0.82";
155
+ var COPILOT_CLI_USER_AGENT = `copilot/${COPILOT_CLI_VERSION}`;
156
+ var OAUTH_HEADERS = {
157
+ Accept: "application/json",
158
+ "Content-Type": "application/x-www-form-urlencoded",
159
+ "User-Agent": "copilot-developer-action/0.0.1"
160
+ };
161
+ var COPILOT_GITHUB_HEADERS = {
162
+ "User-Agent": COPILOT_CLI_USER_AGENT
163
+ };
164
+ var COPILOT_API_HEADERS = {
165
+ ...COPILOT_GITHUB_HEADERS,
166
+ "Editor-Version": COPILOT_CLI_USER_AGENT,
167
+ "Copilot-Integration-Id": "copilot-developer-cli",
168
+ "Copilot-Harness-Id": "copilot-sdk",
169
+ "Openai-Intent": "conversation-agent",
170
+ "X-GitHub-Api-Version": "2026-08-01",
171
+ // Agent-initiated CLI traffic — the official CLI's own classification and
172
+ // the 0-premium-multiplier billing tier GitHub defines for it.
173
+ "X-Initiator": "agent",
174
+ "X-Interaction-Type": "conversation-agent"
175
+ };
176
+ var COPILOT_FAR_FUTURE_MS = 10 * 365.25 * 24 * 60 * 60 * 1e3;
177
+ function isRecord(value) {
178
+ return !!value && typeof value === "object" && !Array.isArray(value);
179
+ }
180
+ async function postForm(fetchImpl, url, params, timeoutMs = 3e4) {
181
+ const response = await fetchImpl(url, {
182
+ method: "POST",
183
+ headers: OAUTH_HEADERS,
184
+ body: params.toString(),
185
+ signal: AbortSignal.timeout(timeoutMs)
186
+ });
187
+ const text = await response.text();
188
+ let body;
189
+ try {
190
+ const parsed = JSON.parse(text);
191
+ body = isRecord(parsed) ? parsed : {};
192
+ } catch {
193
+ body = {};
194
+ }
195
+ return { status: response.status, body };
196
+ }
197
+ async function requestCopilotDeviceAuthorization(fetchImpl, enterpriseUrl) {
198
+ const { status, body } = await postForm(fetchImpl, copilotOAuthUrls(enterpriseUrl).deviceEndpoint, new URLSearchParams({
199
+ client_id: COPILOT_OAUTH_CONFIG.clientId,
200
+ scope: COPILOT_OAUTH_CONFIG.scope
201
+ }));
202
+ const userCode = typeof body["user_code"] === "string" ? body["user_code"] : void 0;
203
+ const deviceCode = typeof body["device_code"] === "string" ? body["device_code"] : void 0;
204
+ const verificationUri = typeof body["verification_uri"] === "string" ? body["verification_uri"] : void 0;
205
+ const interval = typeof body["interval"] === "number" ? body["interval"] : void 0;
206
+ const expiresIn = typeof body["expires_in"] === "number" ? body["expires_in"] : void 0;
207
+ if (status >= 400 || !userCode || !deviceCode || !verificationUri || !interval || !expiresIn) {
208
+ throw new Error(
209
+ typeof body["error_description"] === "string" && body["error_description"] ? body["error_description"] : `device authorization failed (HTTP ${status})`
210
+ );
211
+ }
212
+ return { userCode, deviceCode, verificationUri, interval, expiresIn };
213
+ }
214
+ async function pollCopilotDeviceToken(deviceCode, fetchImpl, enterpriseUrl) {
215
+ const { status, body } = await postForm(fetchImpl, copilotOAuthUrls(enterpriseUrl).tokenEndpoint, new URLSearchParams({
216
+ client_id: COPILOT_OAUTH_CONFIG.clientId,
217
+ device_code: deviceCode,
218
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
219
+ }));
220
+ const accessToken = typeof body["access_token"] === "string" ? body["access_token"] : void 0;
221
+ if (accessToken) return { state: "done", accessToken };
222
+ const error = typeof body["error"] === "string" ? body["error"] : void 0;
223
+ if (error === "authorization_pending") return { state: "pending" };
224
+ if (error === "slow_down") {
225
+ const interval = typeof body["interval"] === "number" && body["interval"] > 0 ? body["interval"] : void 0;
226
+ return { state: "slowDown", ...interval !== void 0 ? { intervalSeconds: interval } : {} };
227
+ }
228
+ if (status < 400 && !error) return { state: "pending" };
229
+ const message = typeof body["error_description"] === "string" && body["error_description"] ? body["error_description"] : error ?? `device token poll failed (HTTP ${status})`;
230
+ return { state: "failed", message };
231
+ }
232
+ async function awaitCopilotDeviceToken(authorization, fetchImpl, options = {}) {
233
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
234
+ const deadline = Date.now() + (options.deadlineMs ?? 15 * 6e4);
235
+ let intervalMs = Math.max(1e3, authorization.interval * 1e3);
236
+ let multiplier = COPILOT_POLL_INITIAL_MULTIPLIER;
237
+ for (; ; ) {
238
+ const result = await pollCopilotDeviceToken(
239
+ authorization.deviceCode,
240
+ fetchImpl,
241
+ options.enterpriseUrl
242
+ );
243
+ if (result.state === "done") return { accessToken: result.accessToken };
244
+ if (result.state === "failed") throw new Error(result.message);
245
+ if (result.state === "slowDown") {
246
+ if (result.intervalSeconds) intervalMs = Math.max(1e3, result.intervalSeconds * 1e3);
247
+ else intervalMs += 5e3;
248
+ multiplier = COPILOT_POLL_SLOW_DOWN_MULTIPLIER;
249
+ }
250
+ options.onPending?.();
251
+ const waitMs = Math.ceil(intervalMs * multiplier);
252
+ if (Date.now() + waitMs > deadline) throw new Error("device authorization timed out");
253
+ await sleep(waitMs);
254
+ }
255
+ }
256
+ async function fetchCopilotIdentity(accessToken, fetchImpl, enterpriseUrl) {
257
+ try {
258
+ const response = await fetchImpl(`${copilotGitHubApiBase(enterpriseUrl)}/user`, {
259
+ method: "GET",
260
+ headers: {
261
+ Accept: "application/vnd.github+json",
262
+ Authorization: `Bearer ${accessToken}`,
263
+ ...COPILOT_GITHUB_HEADERS
264
+ },
265
+ signal: AbortSignal.timeout(15e3)
266
+ });
267
+ if (!response.ok) return {};
268
+ const payload = await response.json();
269
+ if (!isRecord(payload)) return {};
270
+ const accountId = typeof payload["login"] === "string" && payload["login"].trim() ? payload["login"].trim() : void 0;
271
+ const email = typeof payload["email"] === "string" && payload["email"].trim() ? payload["email"].trim().toLowerCase() : void 0;
272
+ return { ...accountId ? { accountId } : {}, ...email ? { email } : {} };
273
+ } catch {
274
+ return {};
275
+ }
276
+ }
277
+ async function discoverCopilotApiEndpoint(accessToken, fetchImpl, enterpriseUrl) {
278
+ try {
279
+ const response = await fetchImpl(`${copilotGitHubApiBase(enterpriseUrl)}/copilot_internal/user`, {
280
+ method: "GET",
281
+ headers: {
282
+ Accept: "application/json",
283
+ Authorization: `token ${accessToken}`,
284
+ ...COPILOT_GITHUB_HEADERS
285
+ },
286
+ signal: AbortSignal.timeout(15e3)
287
+ });
288
+ if (!response.ok) return void 0;
289
+ const payload = await response.json();
290
+ if (!isRecord(payload) || !isRecord(payload["endpoints"])) return void 0;
291
+ const endpoint = payload["endpoints"]["api"];
292
+ if (typeof endpoint !== "string" || !endpoint.startsWith("https://")) return void 0;
293
+ return endpoint.replace(/\/+$/, "");
294
+ } catch {
295
+ return void 0;
296
+ }
297
+ }
298
+ async function enableCopilotModel(accessToken, modelId, baseUrl, fetchImpl) {
299
+ try {
300
+ const response = await fetchImpl(`${baseUrl}/models/${encodeURIComponent(modelId)}/policy`, {
301
+ method: "POST",
302
+ headers: {
303
+ "Content-Type": "application/json",
304
+ Authorization: `Bearer ${accessToken}`,
305
+ ...COPILOT_API_HEADERS,
306
+ "Openai-Intent": "chat-policy",
307
+ "X-Initiator": "user",
308
+ "X-Interaction-Type": "chat-policy"
309
+ },
310
+ body: JSON.stringify({ state: "enabled" }),
311
+ signal: AbortSignal.timeout(15e3)
312
+ });
313
+ return response.ok;
314
+ } catch {
315
+ return false;
316
+ }
317
+ }
318
+ async function enableAllCopilotModels(accessToken, config, fetchImpl, onProgress) {
319
+ const baseUrl = copilotBaseUrl(config);
320
+ const ids = copilotWireModelIds();
321
+ const BATCH = 5;
322
+ for (let i = 0; i < ids.length; i += BATCH) {
323
+ await Promise.all(
324
+ ids.slice(i, i + BATCH).map(async (modelId) => {
325
+ const ok = await enableCopilotModel(accessToken, modelId, baseUrl, fetchImpl);
326
+ onProgress?.(modelId, ok);
327
+ })
328
+ );
329
+ }
330
+ }
331
+ function refreshCopilotToken(accessToken) {
332
+ return {
333
+ accessToken,
334
+ refreshToken: accessToken,
335
+ expiresIn: Math.floor(COPILOT_FAR_FUTURE_MS / 1e3)
336
+ };
337
+ }
338
+
339
+ // src/oauth/flows/kimi.ts
340
+ var kimi_exports = {};
341
+ __export(kimi_exports, {
342
+ KIMI_CLI_VERSION: () => KIMI_CLI_VERSION,
343
+ KIMI_OAUTH_CONFIG: () => KIMI_OAUTH_CONFIG,
344
+ awaitDeviceToken: () => awaitDeviceToken,
345
+ generateKimiDeviceId: () => generateKimiDeviceId,
346
+ kimiAccountIdFromAccessToken: () => kimiAccountIdFromAccessToken,
347
+ kimiFingerprintHeaders: () => kimiFingerprintHeaders,
348
+ pollDeviceToken: () => pollDeviceToken,
349
+ refreshAccessToken: () => refreshAccessToken,
350
+ requestDeviceAuthorization: () => requestDeviceAuthorization
351
+ });
352
+ import crypto from "crypto";
353
+ import * as os from "os";
354
+ var KIMI_OAUTH_CONFIG = {
355
+ clientId: "17e5f671-d194-4dfb-9706-5516cb48c098",
356
+ deviceAuthorizationEndpoint: "https://auth.kimi.com/api/oauth/device_authorization",
357
+ tokenEndpoint: "https://auth.kimi.com/api/oauth/token"
358
+ };
359
+ var KIMI_CLI_VERSION = process.env["KIMI_CLI_VERSION"] ?? "1.0.0";
360
+ function sanitizeHeaderValue(value, fallback = "") {
361
+ const sanitized = value.replace(/[^\x20-\x7E]/g, "").trim();
362
+ return sanitized || fallback;
363
+ }
364
+ function deviceModel() {
365
+ const platform2 = os.platform();
366
+ const label = platform2 === "darwin" ? "macOS" : platform2 === "win32" ? "Windows" : platform2 === "linux" ? "Linux" : platform2;
367
+ return [label, os.release(), os.arch()].filter(Boolean).join(" ").trim();
368
+ }
369
+ function kimiFingerprintHeaders(deviceId) {
370
+ return {
371
+ "User-Agent": `KimiCLI/${KIMI_CLI_VERSION}`,
372
+ "X-Msh-Platform": "kimi_cli",
373
+ "X-Msh-Version": KIMI_CLI_VERSION,
374
+ "X-Msh-Device-Name": sanitizeHeaderValue(os.hostname(), "unknown"),
375
+ "X-Msh-Device-Model": sanitizeHeaderValue(deviceModel(), "unknown"),
376
+ "X-Msh-Os-Version": sanitizeHeaderValue(os.version(), "unknown"),
377
+ ...deviceId ? { "X-Msh-Device-Id": sanitizeHeaderValue(deviceId) } : {}
378
+ };
379
+ }
380
+ function generateKimiDeviceId() {
381
+ return crypto.randomUUID().replace(/-/g, "");
382
+ }
383
+ async function postFormRaw(fetchImpl, url, params, headers, timeoutMs = 3e4) {
384
+ const controller = new AbortController();
385
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
386
+ let response;
387
+ try {
388
+ response = await fetchImpl(url, {
389
+ method: "POST",
390
+ headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json", ...headers },
391
+ body: params.toString(),
392
+ signal: controller.signal
393
+ });
394
+ } catch (error) {
395
+ if (controller.signal.aborted) throw new Error("token endpoint timed out");
396
+ throw error;
397
+ } finally {
398
+ clearTimeout(timer);
399
+ }
400
+ const text = await response.text();
401
+ let body;
402
+ try {
403
+ const parsed = JSON.parse(text);
404
+ body = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
405
+ } catch {
406
+ body = {};
407
+ }
408
+ return { status: response.status, body };
409
+ }
410
+ async function requestDeviceAuthorization(fetchImpl, fingerprint) {
411
+ const { status, body } = await postFormRaw(
412
+ fetchImpl,
413
+ KIMI_OAUTH_CONFIG.deviceAuthorizationEndpoint,
414
+ new URLSearchParams({ client_id: KIMI_OAUTH_CONFIG.clientId }),
415
+ fingerprint ?? {}
416
+ );
417
+ const userCode = typeof body["user_code"] === "string" ? body["user_code"] : void 0;
418
+ const deviceCode = typeof body["device_code"] === "string" ? body["device_code"] : void 0;
419
+ const verificationUri = typeof body["verification_uri"] === "string" ? body["verification_uri"] : void 0;
420
+ if (status >= 400 || !userCode || !deviceCode || !verificationUri) {
421
+ const message = typeof body["error_description"] === "string" ? body["error_description"] : typeof body["msg"] === "string" ? body["msg"] : `device authorization failed (HTTP ${status})`;
422
+ throw new Error(message);
423
+ }
424
+ return {
425
+ userCode,
426
+ deviceCode,
427
+ verificationUri,
428
+ ...typeof body["verification_uri_complete"] === "string" ? { verificationUriComplete: body["verification_uri_complete"] } : {},
429
+ ...typeof body["interval"] === "number" ? { interval: body["interval"] } : {},
430
+ ...typeof body["expires_in"] === "number" ? { expiresIn: body["expires_in"] } : {}
431
+ };
432
+ }
433
+ async function pollDeviceToken(deviceCode, fetchImpl, fingerprint) {
434
+ const { status, body } = await postFormRaw(
435
+ fetchImpl,
436
+ KIMI_OAUTH_CONFIG.tokenEndpoint,
437
+ new URLSearchParams({
438
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
439
+ client_id: KIMI_OAUTH_CONFIG.clientId,
440
+ device_code: deviceCode
441
+ }),
442
+ fingerprint ?? {}
443
+ );
444
+ const accessToken = typeof body["access_token"] === "string" ? body["access_token"] : void 0;
445
+ const refreshToken = typeof body["refresh_token"] === "string" ? body["refresh_token"] : void 0;
446
+ if (accessToken && refreshToken) {
447
+ const expiresIn = typeof body["expires_in"] === "number" && body["expires_in"] > 0 ? body["expires_in"] : 3600;
448
+ return { state: "done", accessToken, refreshToken, expiresIn };
449
+ }
450
+ const error = typeof body["error"] === "string" ? body["error"] : void 0;
451
+ if (error === "authorization_pending") return { state: "pending" };
452
+ if (error === "slow_down") return { state: "pending", intervalSeconds: 5 };
453
+ if (status < 400 && !error) return { state: "pending" };
454
+ const message = typeof body["error_description"] === "string" && body["error_description"] ? body["error_description"] : error ?? `device token poll failed (HTTP ${status})`;
455
+ return { state: "failed", message };
456
+ }
457
+ async function awaitDeviceToken(authorization, fetchImpl, options = {}) {
458
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
459
+ const deadline = Date.now() + (options.deadlineMs ?? 15 * 6e4);
460
+ const baseIntervalMs = options.intervalMs ?? (authorization.interval ?? 5) * 1e3;
461
+ let intervalMs = Math.max(1e3, baseIntervalMs);
462
+ for (; ; ) {
463
+ const result = await pollDeviceToken(authorization.deviceCode, fetchImpl, options.fingerprint);
464
+ if (result.state === "done") return result;
465
+ if (result.state === "failed") throw new Error(result.message);
466
+ if (result.state === "pending" && result.intervalSeconds) intervalMs += result.intervalSeconds * 1e3;
467
+ options.onPending?.();
468
+ if (Date.now() + intervalMs > deadline) throw new Error("device authorization timed out");
469
+ await sleep(intervalMs);
470
+ }
471
+ }
472
+ async function refreshAccessToken(refreshToken, fetchImpl, fingerprint) {
473
+ const { status, body } = await postFormRaw(
474
+ fetchImpl,
475
+ KIMI_OAUTH_CONFIG.tokenEndpoint,
476
+ new URLSearchParams({
477
+ grant_type: "refresh_token",
478
+ client_id: KIMI_OAUTH_CONFIG.clientId,
479
+ refresh_token: refreshToken
480
+ }),
481
+ fingerprint ?? {}
482
+ );
483
+ const accessToken = typeof body["access_token"] === "string" ? body["access_token"] : void 0;
484
+ if (status >= 400 || !accessToken) {
485
+ const message = typeof body["error_description"] === "string" && body["error_description"] ? body["error_description"] : typeof body["error"] === "string" ? body["error"] : `refresh failed (HTTP ${status})`;
486
+ throw new Error(message);
487
+ }
488
+ return {
489
+ accessToken,
490
+ // Kimi rotates the refresh token; keep the old one when the response omits it.
491
+ refreshToken: typeof body["refresh_token"] === "string" && body["refresh_token"] ? body["refresh_token"] : refreshToken,
492
+ expiresIn: typeof body["expires_in"] === "number" && body["expires_in"] > 0 ? body["expires_in"] : 3600
493
+ };
494
+ }
495
+ function kimiAccountIdFromAccessToken(accessToken) {
496
+ const parts = accessToken.split(".");
497
+ if (parts.length !== 3) return void 0;
498
+ try {
499
+ const json = Buffer.from(parts[1], "base64url").toString("utf8");
500
+ const claims = JSON.parse(json);
501
+ if (!claims || typeof claims !== "object" || Array.isArray(claims)) return void 0;
502
+ const record = claims;
503
+ for (const key of ["user_id", "sub"]) {
504
+ const value = record[key];
505
+ if (typeof value === "string" && value.trim()) return value.trim();
506
+ }
507
+ return void 0;
508
+ } catch {
509
+ return void 0;
510
+ }
511
+ }
512
+
513
+ // src/oauth/flows/claude.ts
514
+ var claude_exports = {};
515
+ __export(claude_exports, {
516
+ exchangeCodeForTokens: () => exchangeCodeForTokens,
517
+ exchangeSetupTokenCode: () => exchangeSetupTokenCode,
518
+ generateAuthParams: () => generateAuthParams,
519
+ generateSetupTokenParams: () => generateSetupTokenParams,
520
+ refreshAccessToken: () => refreshAccessToken2
521
+ });
522
+ import crypto2 from "crypto";
523
+
524
+ // src/oauth/fetchPort.ts
525
+ function errorMessage(error, errorDescription) {
526
+ if (errorDescription) return errorDescription;
527
+ if (typeof error === "string") return error;
528
+ if (error && typeof error === "object") {
529
+ const e = error;
530
+ if (typeof e.message === "string" && e.message) return e.message;
531
+ if (typeof e.error_description === "string" && e.error_description) {
532
+ return e.error_description;
533
+ }
534
+ return JSON.stringify(error);
535
+ }
536
+ return String(error);
537
+ }
538
+ function withStatus(message, response) {
539
+ return response.ok ? message : `${message} (HTTP ${response.status})`;
540
+ }
541
+ var DEFAULT_TOKEN_TIMEOUT_MS = 3e4;
542
+ async function fetchWithTimeout(fetchImpl, url, init, timeoutMs) {
543
+ const controller = new AbortController();
544
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
545
+ try {
546
+ return await fetchImpl(url, { ...init, signal: controller.signal });
547
+ } catch (e) {
548
+ if (controller.signal.aborted) {
549
+ throw new Error(`token endpoint timed out after ${Math.round(timeoutMs / 1e3)}s`);
550
+ }
551
+ throw e;
552
+ } finally {
553
+ clearTimeout(timer);
554
+ }
555
+ }
556
+ async function postForm2(fetchImpl, url, params, parseErrorMessage, timeoutMs = DEFAULT_TOKEN_TIMEOUT_MS) {
557
+ const response = await fetchWithTimeout(
558
+ fetchImpl,
559
+ url,
560
+ {
561
+ method: "POST",
562
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
563
+ body: params.toString()
564
+ },
565
+ timeoutMs
566
+ );
567
+ const responseData = await response.text();
568
+ let data;
569
+ try {
570
+ data = JSON.parse(responseData);
571
+ } catch {
572
+ throw new Error(withStatus(parseErrorMessage, response));
573
+ }
574
+ if (data.error) {
575
+ throw new Error(withStatus(errorMessage(data.error, data.error_description), response));
576
+ }
577
+ return data;
578
+ }
579
+ async function postJson(fetchImpl, url, body, parseErrorMessage, extraHeaders = {}, timeoutMs = DEFAULT_TOKEN_TIMEOUT_MS) {
580
+ const response = await fetchWithTimeout(
581
+ fetchImpl,
582
+ url,
583
+ {
584
+ method: "POST",
585
+ headers: { "Content-Type": "application/json", ...extraHeaders },
586
+ body: JSON.stringify(body)
587
+ },
588
+ timeoutMs
589
+ );
590
+ const responseData = await response.text();
591
+ let data;
592
+ try {
593
+ data = JSON.parse(responseData);
594
+ } catch {
595
+ throw new Error(withStatus(parseErrorMessage, response));
596
+ }
597
+ if (data.error) {
598
+ throw new Error(withStatus(errorMessage(data.error, data.error_description), response));
599
+ }
600
+ if (!response.ok) {
601
+ throw new Error(withStatus("token endpoint rejected the request", response));
602
+ }
603
+ return data;
604
+ }
605
+
606
+ // src/oauth/flows/claude.ts
607
+ var CLAUDE_TOKEN_HEADERS = {
608
+ "User-Agent": "claude-cli/1.0.56 (external, cli)",
609
+ Accept: "application/json, text/plain, */*",
610
+ "Accept-Language": "en-US,en;q=0.9",
611
+ Referer: "https://claude.ai/",
612
+ Origin: "https://claude.ai"
613
+ };
614
+ var CLAUDE_OAUTH_CONFIG = {
615
+ clientId: "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
616
+ authorizationEndpoint: "https://claude.ai/oauth/authorize",
617
+ // The token endpoint moved to platform.claude.com alongside the OAuth
618
+ // callback (the old console.anthropic.com/v1/oauth/token now returns
619
+ // Anthropic's standard 404 `{"error":{"type":"not_found_error","message":
620
+ // "Not found"}}` — observed 2026-06). The redirect_uri MUST match what the
621
+ // client is registered for AND match between authorize + token exchange.
622
+ // Scopes mirror the live Claude Code authorize URL.
623
+ tokenEndpoint: "https://platform.claude.com/v1/oauth/token",
624
+ redirectUri: "https://platform.claude.com/oauth/code/callback",
625
+ scopes: [
626
+ "org:create_api_key",
627
+ "user:profile",
628
+ "user:inference",
629
+ "user:sessions:claude_code",
630
+ "user:mcp_servers",
631
+ "user:file_upload"
632
+ ]
633
+ };
634
+ var SETUP_TOKEN_CONFIG = {
635
+ scopes: ["user:inference"]
636
+ // Only inference permission, no API key creation
637
+ };
638
+ function generatePkce() {
639
+ const codeVerifier = crypto2.randomBytes(32).toString("base64url");
640
+ const codeChallenge = crypto2.createHash("sha256").update(codeVerifier).digest("base64url");
641
+ const state = crypto2.randomBytes(16).toString("hex");
642
+ return { codeVerifier, codeChallenge, state };
643
+ }
644
+ function generateAuthParams() {
645
+ const { codeVerifier, codeChallenge, state } = generatePkce();
646
+ const params = new URLSearchParams({
647
+ code: "true",
648
+ client_id: CLAUDE_OAUTH_CONFIG.clientId,
649
+ response_type: "code",
650
+ redirect_uri: CLAUDE_OAUTH_CONFIG.redirectUri,
651
+ scope: CLAUDE_OAUTH_CONFIG.scopes.join(" "),
652
+ code_challenge: codeChallenge,
653
+ code_challenge_method: "S256",
654
+ state
655
+ });
656
+ const authUrl = `${CLAUDE_OAUTH_CONFIG.authorizationEndpoint}?${params.toString()}`;
657
+ return { authUrl, codeVerifier, state };
658
+ }
659
+ function generateSetupTokenParams() {
660
+ const { codeVerifier, codeChallenge, state } = generatePkce();
661
+ const params = new URLSearchParams({
662
+ code: "true",
663
+ client_id: CLAUDE_OAUTH_CONFIG.clientId,
664
+ response_type: "code",
665
+ redirect_uri: CLAUDE_OAUTH_CONFIG.redirectUri,
666
+ scope: SETUP_TOKEN_CONFIG.scopes.join(" "),
667
+ code_challenge: codeChallenge,
668
+ code_challenge_method: "S256",
669
+ state
670
+ });
671
+ const authUrl = `${CLAUDE_OAUTH_CONFIG.authorizationEndpoint}?${params.toString()}`;
672
+ return { authUrl, codeVerifier, state };
673
+ }
674
+ async function exchangeCodeForTokens(request, fetchImpl) {
675
+ const { authorizationCode, codeVerifier, state } = request;
676
+ const code = authorizationCode.split("#")[0]?.split("&")[0] ?? authorizationCode;
677
+ const data = await postJson(
678
+ fetchImpl,
679
+ CLAUDE_OAUTH_CONFIG.tokenEndpoint,
680
+ {
681
+ grant_type: "authorization_code",
682
+ client_id: CLAUDE_OAUTH_CONFIG.clientId,
683
+ code,
684
+ code_verifier: codeVerifier,
685
+ redirect_uri: CLAUDE_OAUTH_CONFIG.redirectUri,
686
+ state
687
+ },
688
+ "Failed to parse token response",
689
+ CLAUDE_TOKEN_HEADERS
690
+ );
691
+ return {
692
+ accessToken: data.access_token,
693
+ // The authorization_code grant always returns a refresh_token; the original
694
+ // helper read it from an untyped `data` and declared the field `string`.
695
+ refreshToken: data.refresh_token,
696
+ expiresIn: data.expires_in,
697
+ scopes: data.scope?.split(" ") || CLAUDE_OAUTH_CONFIG.scopes
698
+ };
699
+ }
700
+ async function exchangeSetupTokenCode(request, fetchImpl) {
701
+ const { authorizationCode, codeVerifier, state } = request;
702
+ const code = authorizationCode.split("#")[0]?.split("&")[0] ?? authorizationCode;
703
+ const data = await postJson(
704
+ fetchImpl,
705
+ CLAUDE_OAUTH_CONFIG.tokenEndpoint,
706
+ {
707
+ grant_type: "authorization_code",
708
+ client_id: CLAUDE_OAUTH_CONFIG.clientId,
709
+ code,
710
+ code_verifier: codeVerifier,
711
+ redirect_uri: CLAUDE_OAUTH_CONFIG.redirectUri,
712
+ state
713
+ },
714
+ "Failed to parse setup token response",
715
+ CLAUDE_TOKEN_HEADERS
716
+ );
717
+ return {
718
+ accessToken: data.access_token,
719
+ expiresIn: data.expires_in,
720
+ scopes: data.scope?.split(" ") || SETUP_TOKEN_CONFIG.scopes
721
+ };
722
+ }
723
+ async function refreshAccessToken2(refreshToken, fetchImpl) {
724
+ const data = await postJson(
725
+ fetchImpl,
726
+ CLAUDE_OAUTH_CONFIG.tokenEndpoint,
727
+ {
728
+ grant_type: "refresh_token",
729
+ client_id: CLAUDE_OAUTH_CONFIG.clientId,
730
+ refresh_token: refreshToken
731
+ },
732
+ "Failed to parse refresh response",
733
+ CLAUDE_TOKEN_HEADERS
734
+ );
735
+ return {
736
+ accessToken: data.access_token,
737
+ refreshToken: data.refresh_token || refreshToken,
738
+ expiresIn: data.expires_in
739
+ };
740
+ }
741
+
742
+ // src/oauth/flows/codex.ts
743
+ var codex_exports = {};
744
+ __export(codex_exports, {
745
+ exchangeCodeForTokens: () => exchangeCodeForTokens2,
746
+ generateAuthParams: () => generateAuthParams2,
747
+ refreshAccessToken: () => refreshAccessToken3
748
+ });
749
+ import crypto3 from "crypto";
750
+ var CODEX_OAUTH_CONFIG = {
751
+ clientId: "app_EMoamEEZ73f0CkXaXp7hrann",
752
+ authorizationEndpoint: "https://auth.openai.com/oauth/authorize",
753
+ tokenEndpoint: "https://auth.openai.com/oauth/token",
754
+ redirectUri: "http://localhost:1455/auth/callback",
755
+ scopes: ["openid", "profile", "email", "offline_access"]
756
+ };
757
+ function generateAuthParams2() {
758
+ const codeVerifier = crypto3.randomBytes(64).toString("hex");
759
+ const codeChallenge = crypto3.createHash("sha256").update(codeVerifier).digest("base64url");
760
+ const state = crypto3.randomBytes(16).toString("hex");
761
+ const params = new URLSearchParams({
762
+ response_type: "code",
763
+ client_id: CODEX_OAUTH_CONFIG.clientId,
764
+ redirect_uri: CODEX_OAUTH_CONFIG.redirectUri,
765
+ scope: CODEX_OAUTH_CONFIG.scopes.join(" "),
766
+ code_challenge: codeChallenge,
767
+ code_challenge_method: "S256",
768
+ state
769
+ });
770
+ const authUrl = `${CODEX_OAUTH_CONFIG.authorizationEndpoint}?${params.toString()}`;
771
+ return { authUrl, codeVerifier, state };
772
+ }
773
+ async function exchangeCodeForTokens2(request, fetchImpl) {
774
+ const { authorizationCode, codeVerifier } = request;
775
+ const params = new URLSearchParams({
776
+ grant_type: "authorization_code",
777
+ client_id: CODEX_OAUTH_CONFIG.clientId,
778
+ code: authorizationCode,
779
+ code_verifier: codeVerifier,
780
+ redirect_uri: CODEX_OAUTH_CONFIG.redirectUri
781
+ });
782
+ const data = await postForm2(
783
+ fetchImpl,
784
+ CODEX_OAUTH_CONFIG.tokenEndpoint,
785
+ params,
786
+ "Failed to parse token response"
787
+ );
788
+ return {
789
+ accessToken: data.access_token,
790
+ // authorization_code grant returns both; the original helper read them from
791
+ // an untyped `data` and declared the fields `string`.
792
+ refreshToken: data.refresh_token,
793
+ idToken: data.id_token,
794
+ expiresIn: data.expires_in
795
+ };
796
+ }
797
+ async function refreshAccessToken3(refreshToken, fetchImpl) {
798
+ const params = new URLSearchParams({
799
+ grant_type: "refresh_token",
800
+ client_id: CODEX_OAUTH_CONFIG.clientId,
801
+ refresh_token: refreshToken,
802
+ scope: "openid profile email"
803
+ });
804
+ const data = await postForm2(
805
+ fetchImpl,
806
+ CODEX_OAUTH_CONFIG.tokenEndpoint,
807
+ params,
808
+ "Failed to parse refresh response"
809
+ );
810
+ return {
811
+ accessToken: data.access_token,
812
+ idToken: data.id_token,
813
+ refreshToken: data.refresh_token || refreshToken,
814
+ expiresIn: data.expires_in || 3600
815
+ };
816
+ }
817
+
818
+ // src/oauth/flows/gemini.ts
819
+ var gemini_exports = {};
820
+ __export(gemini_exports, {
821
+ exchangeCodeForTokens: () => exchangeCodeForTokens3,
822
+ generateAuthParams: () => generateAuthParams3,
823
+ refreshAccessToken: () => refreshAccessToken4
824
+ });
825
+ import crypto4 from "crypto";
826
+ var GEMINI_OAUTH_CONFIG = {
827
+ clientId: "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com",
828
+ // The Gemini CLI's *public* installed-app OAuth client secret (mirrors the
829
+ // upstream CLI). Per Google's OAuth docs, native-app client secrets are not
830
+ // treated as confidential — not a leaked key.
831
+ clientSecret: "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",
832
+ // allowlist-secret
833
+ authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
834
+ tokenEndpoint: "https://oauth2.googleapis.com/token",
835
+ redirectUri: "urn:ietf:wg:oauth:2.0:oob",
836
+ scopes: ["https://www.googleapis.com/auth/cloud-platform"]
837
+ };
838
+ function generateAuthParams3() {
839
+ const codeVerifier = crypto4.randomBytes(32).toString("base64url");
840
+ const codeChallenge = crypto4.createHash("sha256").update(codeVerifier).digest("base64url");
841
+ const state = crypto4.randomBytes(16).toString("hex");
842
+ const params = new URLSearchParams({
843
+ client_id: GEMINI_OAUTH_CONFIG.clientId,
844
+ redirect_uri: GEMINI_OAUTH_CONFIG.redirectUri,
845
+ scope: GEMINI_OAUTH_CONFIG.scopes.join(" "),
846
+ response_type: "code",
847
+ code_challenge: codeChallenge,
848
+ code_challenge_method: "S256",
849
+ state,
850
+ access_type: "offline",
851
+ prompt: "consent"
852
+ });
853
+ const authUrl = `${GEMINI_OAUTH_CONFIG.authorizationEndpoint}?${params.toString()}`;
854
+ return { authUrl, codeVerifier, state };
855
+ }
856
+ async function exchangeCodeForTokens3(authorizationCode, codeVerifier, fetchImpl) {
857
+ const params = new URLSearchParams({
858
+ grant_type: "authorization_code",
859
+ client_id: GEMINI_OAUTH_CONFIG.clientId,
860
+ client_secret: GEMINI_OAUTH_CONFIG.clientSecret,
861
+ code: authorizationCode,
862
+ code_verifier: codeVerifier,
863
+ redirect_uri: GEMINI_OAUTH_CONFIG.redirectUri
864
+ });
865
+ const data = await postForm2(
866
+ fetchImpl,
867
+ GEMINI_OAUTH_CONFIG.tokenEndpoint,
868
+ params,
869
+ "Failed to parse token response"
870
+ );
871
+ return {
872
+ accessToken: data.access_token,
873
+ // authorization_code grant returns a refresh_token; the original helper read
874
+ // it from an untyped `data` and declared the field `string`.
875
+ refreshToken: data.refresh_token,
876
+ expiresIn: data.expires_in
877
+ };
878
+ }
879
+ async function refreshAccessToken4(refreshToken, fetchImpl) {
880
+ const params = new URLSearchParams({
881
+ grant_type: "refresh_token",
882
+ client_id: GEMINI_OAUTH_CONFIG.clientId,
883
+ client_secret: GEMINI_OAUTH_CONFIG.clientSecret,
884
+ refresh_token: refreshToken
885
+ });
886
+ const data = await postForm2(
887
+ fetchImpl,
888
+ GEMINI_OAUTH_CONFIG.tokenEndpoint,
889
+ params,
890
+ "Failed to parse refresh response"
891
+ );
892
+ return {
893
+ accessToken: data.access_token,
894
+ expiresIn: data.expires_in
895
+ };
896
+ }
897
+
898
+ // src/oauth/flows/grok.ts
899
+ var grok_exports = {};
900
+ __export(grok_exports, {
901
+ GROK_OAUTH_CONFIG: () => GROK_OAUTH_CONFIG,
902
+ awaitGrokDeviceToken: () => awaitGrokDeviceToken,
903
+ grokAccountIdFromAccessToken: () => grokAccountIdFromAccessToken,
904
+ isGrokAuthHostname: () => isGrokAuthHostname,
905
+ pollGrokDeviceToken: () => pollGrokDeviceToken,
906
+ refreshGrokAccessToken: () => refreshGrokAccessToken,
907
+ requestGrokDeviceAuthorization: () => requestGrokDeviceAuthorization,
908
+ resetGrokDiscoveryCache: () => resetGrokDiscoveryCache,
909
+ resolveGrokTokenEndpoint: () => resolveGrokTokenEndpoint,
910
+ validateGrokAuthEndpoint: () => validateGrokAuthEndpoint
911
+ });
912
+ var GROK_OAUTH_CONFIG = {
913
+ clientId: "b1a00492-073a-47ea-816f-4c329264a828",
914
+ deviceAuthorizationEndpoint: process.env["GROK_OAUTH_DEVICE_ENDPOINT"] ?? "https://auth.x.ai/oauth2/device/code",
915
+ discoveryUrl: process.env["GROK_OAUTH_DISCOVERY_URL"] ?? "https://auth.x.ai/.well-known/openid-configuration",
916
+ /** The CLI's full scope set — the token must carry `grok-cli:access` for inference. */
917
+ scopes: ["openid", "profile", "email", "offline_access", "grok-cli:access", "api:access"]
918
+ };
919
+ var DISCOVERY_CACHE_MS = 60 * 60 * 1e3;
920
+ function isRecord2(value) {
921
+ return !!value && typeof value === "object" && !Array.isArray(value);
922
+ }
923
+ function isGrokAuthHostname(host) {
924
+ return host === "x.ai" || host.endsWith(".x.ai");
925
+ }
926
+ function validateGrokAuthEndpoint(url, field) {
927
+ let parsed;
928
+ try {
929
+ parsed = new URL(url);
930
+ } catch {
931
+ throw new Error(`Invalid Grok ${field}: ${url}`);
932
+ }
933
+ if (parsed.protocol !== "https:" || !isGrokAuthHostname(parsed.hostname.toLowerCase())) {
934
+ throw new Error(`Invalid Grok ${field}: ${url}`);
935
+ }
936
+ return url;
937
+ }
938
+ var cachedDiscovery;
939
+ function resetGrokDiscoveryCache() {
940
+ cachedDiscovery = void 0;
941
+ }
942
+ async function resolveGrokTokenEndpoint(fetchImpl, timeoutMs = 15e3) {
943
+ if (cachedDiscovery && Date.now() - cachedDiscovery.resolvedAt < DISCOVERY_CACHE_MS) {
944
+ return cachedDiscovery.tokenEndpoint;
945
+ }
946
+ let response;
947
+ try {
948
+ response = await fetchImpl(GROK_OAUTH_CONFIG.discoveryUrl, {
949
+ method: "GET",
950
+ headers: { Accept: "application/json" },
951
+ signal: AbortSignal.timeout(timeoutMs)
952
+ });
953
+ } catch (error) {
954
+ throw new Error(
955
+ `Grok OIDC discovery failed: ${error instanceof Error ? error.message : String(error)}`
956
+ );
957
+ }
958
+ if (!response.ok) throw new Error(`Grok OIDC discovery returned HTTP ${response.status}`);
959
+ let payload;
960
+ try {
961
+ payload = await response.json();
962
+ } catch {
963
+ throw new Error("Grok OIDC discovery returned invalid JSON");
964
+ }
965
+ const tokenEndpoint = isRecord2(payload) && typeof payload["token_endpoint"] === "string" ? payload["token_endpoint"].trim() : "";
966
+ if (!tokenEndpoint) throw new Error("Grok OIDC discovery response missing token_endpoint");
967
+ validateGrokAuthEndpoint(tokenEndpoint, "token_endpoint");
968
+ cachedDiscovery = { tokenEndpoint, resolvedAt: Date.now() };
969
+ return tokenEndpoint;
970
+ }
971
+ async function postFormRaw2(fetchImpl, url, params, timeoutMs = 3e4) {
972
+ const controller = new AbortController();
973
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
974
+ let response;
975
+ try {
976
+ response = await fetchImpl(url, {
977
+ method: "POST",
978
+ headers: {
979
+ "Content-Type": "application/x-www-form-urlencoded",
980
+ Accept: "application/json"
981
+ },
982
+ body: params.toString(),
983
+ signal: controller.signal
984
+ });
985
+ } catch (error) {
986
+ if (controller.signal.aborted) throw new Error("token endpoint timed out");
987
+ throw error;
988
+ } finally {
989
+ clearTimeout(timer);
990
+ }
991
+ const text = await response.text();
992
+ let body;
993
+ try {
994
+ const parsed = JSON.parse(text);
995
+ body = isRecord2(parsed) ? parsed : {};
996
+ } catch {
997
+ body = {};
998
+ }
999
+ return { status: response.status, body };
1000
+ }
1001
+ async function requestGrokDeviceAuthorization(fetchImpl) {
1002
+ const { status, body } = await postFormRaw2(
1003
+ fetchImpl,
1004
+ GROK_OAUTH_CONFIG.deviceAuthorizationEndpoint,
1005
+ new URLSearchParams({
1006
+ client_id: GROK_OAUTH_CONFIG.clientId,
1007
+ scope: GROK_OAUTH_CONFIG.scopes.join(" ")
1008
+ })
1009
+ );
1010
+ const userCode = typeof body["user_code"] === "string" ? body["user_code"] : void 0;
1011
+ const deviceCode = typeof body["device_code"] === "string" ? body["device_code"] : void 0;
1012
+ const verificationUri = typeof body["verification_uri"] === "string" ? body["verification_uri"] : void 0;
1013
+ if (status >= 400 || !userCode || !deviceCode || !verificationUri) {
1014
+ const message = typeof body["error_description"] === "string" && body["error_description"] ? body["error_description"] : typeof body["error"] === "string" ? body["error"] : `device authorization failed (HTTP ${status})`;
1015
+ throw new Error(message);
1016
+ }
1017
+ return {
1018
+ userCode,
1019
+ deviceCode,
1020
+ verificationUri,
1021
+ ...typeof body["verification_uri_complete"] === "string" ? { verificationUriComplete: body["verification_uri_complete"] } : {},
1022
+ ...typeof body["interval"] === "number" ? { interval: body["interval"] } : {},
1023
+ ...typeof body["expires_in"] === "number" ? { expiresIn: body["expires_in"] } : {}
1024
+ };
1025
+ }
1026
+ async function pollGrokDeviceToken(deviceCode, tokenEndpoint, fetchImpl) {
1027
+ const { status, body } = await postFormRaw2(fetchImpl, tokenEndpoint, new URLSearchParams({
1028
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
1029
+ client_id: GROK_OAUTH_CONFIG.clientId,
1030
+ device_code: deviceCode
1031
+ }));
1032
+ const accessToken = typeof body["access_token"] === "string" ? body["access_token"] : void 0;
1033
+ const refreshToken = typeof body["refresh_token"] === "string" ? body["refresh_token"] : void 0;
1034
+ if (accessToken && refreshToken) {
1035
+ const expiresIn = typeof body["expires_in"] === "number" && body["expires_in"] > 0 ? body["expires_in"] : 3600;
1036
+ return { state: "done", accessToken, refreshToken, expiresIn };
1037
+ }
1038
+ const error = typeof body["error"] === "string" ? body["error"] : void 0;
1039
+ if (error === "authorization_pending") return { state: "pending" };
1040
+ if (error === "slow_down") return { state: "pending", intervalSeconds: 5 };
1041
+ if (status < 400 && !error) return { state: "pending" };
1042
+ const message = typeof body["error_description"] === "string" && body["error_description"] ? body["error_description"] : error ?? `device token poll failed (HTTP ${status})`;
1043
+ return { state: "failed", message };
1044
+ }
1045
+ async function awaitGrokDeviceToken(authorization, tokenEndpoint, fetchImpl, options = {}) {
1046
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1047
+ const deadline = Date.now() + (options.deadlineMs ?? 15 * 6e4);
1048
+ const baseIntervalMs = options.intervalMs ?? (authorization.interval ?? 5) * 1e3;
1049
+ let intervalMs = Math.max(1e3, baseIntervalMs);
1050
+ for (; ; ) {
1051
+ const result = await pollGrokDeviceToken(authorization.deviceCode, tokenEndpoint, fetchImpl);
1052
+ if (result.state === "done") return result;
1053
+ if (result.state === "failed") throw new Error(result.message);
1054
+ if (result.state === "pending" && result.intervalSeconds) {
1055
+ intervalMs += result.intervalSeconds * 1e3;
1056
+ }
1057
+ options.onPending?.();
1058
+ if (Date.now() + intervalMs > deadline) throw new Error("device authorization timed out");
1059
+ await sleep(intervalMs);
1060
+ }
1061
+ }
1062
+ async function refreshGrokAccessToken(refreshToken, tokenEndpoint, fetchImpl) {
1063
+ const { status, body } = await postFormRaw2(fetchImpl, tokenEndpoint, new URLSearchParams({
1064
+ grant_type: "refresh_token",
1065
+ client_id: GROK_OAUTH_CONFIG.clientId,
1066
+ refresh_token: refreshToken
1067
+ }));
1068
+ const accessToken = typeof body["access_token"] === "string" ? body["access_token"] : void 0;
1069
+ if (status >= 400 || !accessToken) {
1070
+ const message = typeof body["error_description"] === "string" && body["error_description"] ? body["error_description"] : typeof body["error"] === "string" ? body["error"] : `refresh failed (HTTP ${status})`;
1071
+ throw new Error(message);
1072
+ }
1073
+ return {
1074
+ accessToken,
1075
+ // Keep the old refresh token when the response omits one (no proven rotation).
1076
+ refreshToken: typeof body["refresh_token"] === "string" && body["refresh_token"] ? body["refresh_token"] : refreshToken,
1077
+ expiresIn: typeof body["expires_in"] === "number" && body["expires_in"] > 0 ? body["expires_in"] : 3600
1078
+ };
1079
+ }
1080
+ function grokAccountIdFromAccessToken(accessToken) {
1081
+ const parts = accessToken.split(".");
1082
+ if (parts.length !== 3) return void 0;
1083
+ try {
1084
+ const json = Buffer.from(parts[1], "base64url").toString("utf8");
1085
+ const claims = JSON.parse(json);
1086
+ if (!isRecord2(claims)) return void 0;
1087
+ const sub = claims["sub"];
1088
+ return typeof sub === "string" && sub.trim() ? sub.trim() : void 0;
1089
+ } catch {
1090
+ return void 0;
1091
+ }
1092
+ }
1093
+
1094
+ export {
1095
+ copilotWireFor,
1096
+ copilotPathFor,
1097
+ copilotTransformerNamesForWire,
1098
+ copilotBaseUrl,
1099
+ copilotGitHubApiBase,
1100
+ COPILOT_GITHUB_HEADERS,
1101
+ COPILOT_API_HEADERS,
1102
+ copilot_exports,
1103
+ kimiFingerprintHeaders,
1104
+ kimi_exports,
1105
+ claude_exports,
1106
+ codex_exports,
1107
+ gemini_exports,
1108
+ grok_exports
1109
+ };