@omnicross/daemon 0.3.1 → 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.
package/dist/cli.js CHANGED
@@ -1170,14 +1170,14 @@ import { setSubscriptionRegistryForOutbound } from "@omnicross/core/outbound-api
1170
1170
  import { getSharedAccountHealth as getSharedAccountHealth4 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
1171
1171
  import {
1172
1172
  __resetSharedAccountAllowanceStoreForTests,
1173
- AccountAllowanceStore as AccountAllowanceStore6,
1173
+ AccountAllowanceStore as AccountAllowanceStore8,
1174
1174
  setSharedAccountAllowanceStore
1175
1175
  } from "@omnicross/core/pipeline/AccountAllowanceStore";
1176
1176
  import {
1177
1177
  __resetSharedAccountAllowanceSchedulingForTests,
1178
1178
  getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling5
1179
1179
  } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
1180
- import { fetchUpstream as fetchUpstream11, setUpstreamProxyResolver } from "@omnicross/core/pipeline/upstreamFetch";
1180
+ import { fetchUpstream as fetchUpstream13, setUpstreamProxyResolver } from "@omnicross/core/pipeline/upstreamFetch";
1181
1181
  import { __resetSharedIdentityStoreForTests } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
1182
1182
  import { setGeminiCodeAssistResolver } from "@omnicross/core/ports/gemini-code-assist-resolver";
1183
1183
  import {
@@ -1382,9 +1382,188 @@ function handleKimiOAuthStatus(sessionId, deps) {
1382
1382
  return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
1383
1383
  }
1384
1384
 
1385
+ // src/admin/accountsGrokOAuth.ts
1386
+ import { grokOAuth } from "@omnicross/subscriptions";
1387
+ function err3(status, message) {
1388
+ return { status, body: { error: { type: "admin_api_error", message } } };
1389
+ }
1390
+ var DEFAULT_GROK_OAUTH_TTL_MS = 15 * 6e4;
1391
+ async function handleGrokOAuthStart(deps) {
1392
+ if (deps.grokSessions.isBusy()) {
1393
+ return err3(409, "a grok sign-in is already in progress \u2014 finish it in the browser or cancel it");
1394
+ }
1395
+ const fetchImpl = deps.oauthExchangeFetch("grok");
1396
+ let tokenEndpoint;
1397
+ try {
1398
+ tokenEndpoint = await grokOAuth.resolveGrokTokenEndpoint(fetchImpl);
1399
+ } catch (e) {
1400
+ const reason = e instanceof Error ? e.message : "OIDC discovery failed";
1401
+ return err3(502, `grok token-endpoint discovery failed: ${reason}`);
1402
+ }
1403
+ let authorization;
1404
+ try {
1405
+ authorization = await grokOAuth.requestGrokDeviceAuthorization(fetchImpl);
1406
+ } catch (e) {
1407
+ const reason = e instanceof Error ? e.message : "device authorization failed";
1408
+ return err3(502, `grok device authorization failed: ${reason}`);
1409
+ }
1410
+ const { sessionId, signal } = deps.grokSessions.begin();
1411
+ void runGrokDevicePoll(sessionId, tokenEndpoint, authorization.deviceCode, signal, deps).catch((e) => {
1412
+ const reason = e instanceof Error ? e.message : "grok sign-in failed";
1413
+ deps.grokSessions.settle(sessionId, "error", reason);
1414
+ });
1415
+ return {
1416
+ status: 200,
1417
+ body: {
1418
+ authUrl: authorization.verificationUriComplete ?? authorization.verificationUri,
1419
+ userCode: authorization.userCode,
1420
+ sessionId
1421
+ }
1422
+ };
1423
+ }
1424
+ async function runGrokDevicePoll(sessionId, tokenEndpoint, deviceCode, signal, deps) {
1425
+ const fetchImpl = deps.oauthExchangeFetch("grok");
1426
+ const result = await grokOAuth.awaitGrokDeviceToken(
1427
+ { userCode: "", deviceCode, verificationUri: "" },
1428
+ tokenEndpoint,
1429
+ fetchImpl,
1430
+ {
1431
+ deadlineMs: DEFAULT_GROK_OAUTH_TTL_MS,
1432
+ sleep: (ms) => new Promise((resolve11, reject) => {
1433
+ const onAbort = () => {
1434
+ clearTimeout(timer);
1435
+ reject(new Error("login: cancelled"));
1436
+ };
1437
+ const timer = setTimeout(() => {
1438
+ signal.removeEventListener("abort", onAbort);
1439
+ resolve11();
1440
+ }, ms);
1441
+ signal.addEventListener("abort", onAbort, { once: true });
1442
+ })
1443
+ }
1444
+ );
1445
+ const block = {
1446
+ authMethod: "oauth",
1447
+ status: "authorized",
1448
+ accessToken: result.accessToken,
1449
+ refreshToken: result.refreshToken,
1450
+ expiresAt: new Date(Date.now() + result.expiresIn * 1e3).toISOString(),
1451
+ accountId: grokOAuth.grokAccountIdFromAccessToken(result.accessToken),
1452
+ lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
1453
+ };
1454
+ await deps.subscriptionAccountAppender.appendProviderAccount("grok", block);
1455
+ deps.grokSessions.settle(sessionId, "done");
1456
+ }
1457
+ function handleGrokOAuthCancel(sessionId, deps) {
1458
+ if (!deps.grokSessions.cancel(sessionId)) return err3(404, "unknown or expired grok sign-in session");
1459
+ return { status: 200, body: { ok: true } };
1460
+ }
1461
+ function handleGrokOAuthStatus(sessionId, deps) {
1462
+ const s = deps.grokSessions.get(sessionId);
1463
+ if (!s) return err3(404, "unknown or expired grok sign-in session");
1464
+ return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
1465
+ }
1466
+
1467
+ // src/admin/accountsCopilotOAuth.ts
1468
+ import { copilotOAuth } from "@omnicross/subscriptions";
1469
+ function err4(status, message) {
1470
+ return { status, body: { error: { type: "admin_api_error", message } } };
1471
+ }
1472
+ var DEFAULT_COPILOT_OAUTH_TTL_MS = 15 * 6e4;
1473
+ async function handleCopilotOAuthStart(deps, enterpriseUrlInput) {
1474
+ if (deps.copilotSessions.isBusy()) {
1475
+ return err4(409, "a copilot sign-in is already in progress \u2014 finish it in the browser or cancel it");
1476
+ }
1477
+ let enterpriseUrl;
1478
+ if (typeof enterpriseUrlInput === "string" && enterpriseUrlInput.trim()) {
1479
+ try {
1480
+ enterpriseUrl = copilotOAuth.normalizeCopilotEnterpriseDomain(enterpriseUrlInput);
1481
+ } catch (e) {
1482
+ const reason = e instanceof Error ? e.message : "invalid GitHub Enterprise domain";
1483
+ return err4(400, `copilot ${reason}`);
1484
+ }
1485
+ }
1486
+ const fetchImpl = deps.oauthExchangeFetch("copilot");
1487
+ let authorization;
1488
+ try {
1489
+ authorization = await copilotOAuth.requestCopilotDeviceAuthorization(fetchImpl, enterpriseUrl);
1490
+ } catch (e) {
1491
+ const reason = e instanceof Error ? e.message : "device authorization failed";
1492
+ return err4(502, `copilot device authorization failed: ${reason}`);
1493
+ }
1494
+ const { sessionId, signal } = deps.copilotSessions.begin();
1495
+ void runCopilotDevicePoll(sessionId, authorization.deviceCode, signal, deps, enterpriseUrl).catch((e) => {
1496
+ const reason = e instanceof Error ? e.message : "copilot sign-in failed";
1497
+ deps.copilotSessions.settle(sessionId, "error", reason);
1498
+ });
1499
+ return {
1500
+ status: 200,
1501
+ body: {
1502
+ authUrl: authorization.verificationUri,
1503
+ userCode: authorization.userCode,
1504
+ sessionId,
1505
+ ...enterpriseUrl ? { enterpriseUrl } : {}
1506
+ }
1507
+ };
1508
+ }
1509
+ async function runCopilotDevicePoll(sessionId, deviceCode, signal, deps, enterpriseUrl) {
1510
+ const fetchImpl = deps.oauthExchangeFetch("copilot");
1511
+ const result = await copilotOAuth.awaitCopilotDeviceToken(
1512
+ { userCode: "", deviceCode, verificationUri: "", interval: 5, expiresIn: 900 },
1513
+ fetchImpl,
1514
+ {
1515
+ deadlineMs: DEFAULT_COPILOT_OAUTH_TTL_MS,
1516
+ ...enterpriseUrl ? { enterpriseUrl } : {},
1517
+ sleep: (ms) => new Promise((resolve11, reject) => {
1518
+ const onAbort = () => {
1519
+ clearTimeout(timer);
1520
+ reject(new Error("login: cancelled"));
1521
+ };
1522
+ const timer = setTimeout(() => {
1523
+ signal.removeEventListener("abort", onAbort);
1524
+ resolve11();
1525
+ }, ms);
1526
+ signal.addEventListener("abort", onAbort, { once: true });
1527
+ })
1528
+ }
1529
+ );
1530
+ const identity = await copilotOAuth.fetchCopilotIdentity(result.accessToken, fetchImpl, enterpriseUrl);
1531
+ const apiEndpoint = await copilotOAuth.discoverCopilotApiEndpoint(result.accessToken, fetchImpl, enterpriseUrl);
1532
+ await copilotOAuth.enableAllCopilotModels(
1533
+ result.accessToken,
1534
+ { apiEndpoint, ...enterpriseUrl ? { enterpriseUrl } : {} },
1535
+ fetchImpl
1536
+ );
1537
+ const block = {
1538
+ authMethod: "oauth",
1539
+ status: "authorized",
1540
+ accessToken: result.accessToken,
1541
+ refreshToken: result.accessToken,
1542
+ expiresAt: new Date(Date.now() + copilotOAuth.COPILOT_FAR_FUTURE_MS).toISOString(),
1543
+ ...identity.accountId ? { accountId: identity.accountId } : {},
1544
+ ...identity.email ? { email: identity.email } : {},
1545
+ ...apiEndpoint ? { apiEndpoint } : {},
1546
+ ...enterpriseUrl ? { enterpriseUrl } : {},
1547
+ lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
1548
+ };
1549
+ await deps.subscriptionAccountAppender.appendProviderAccount("copilot", block);
1550
+ deps.copilotSessions.settle(sessionId, "done");
1551
+ }
1552
+ function handleCopilotOAuthCancel(sessionId, deps) {
1553
+ if (!deps.copilotSessions.cancel(sessionId)) {
1554
+ return err4(404, "unknown or expired copilot sign-in session");
1555
+ }
1556
+ return { status: 200, body: { ok: true } };
1557
+ }
1558
+ function handleCopilotOAuthStatus(sessionId, deps) {
1559
+ const s = deps.copilotSessions.get(sessionId);
1560
+ if (!s) return err4(404, "unknown or expired copilot sign-in session");
1561
+ return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
1562
+ }
1563
+
1385
1564
  // src/allowance/AccountAllowanceService.ts
1386
1565
  import {
1387
- getSharedAccountAllowanceStore as getSharedAccountAllowanceStore5
1566
+ getSharedAccountAllowanceStore as getSharedAccountAllowanceStore7
1388
1567
  } from "@omnicross/core/pipeline/AccountAllowanceStore";
1389
1568
  import {
1390
1569
  getSharedAccountAllowanceScheduling
@@ -1972,10 +2151,491 @@ function parseKimiUsagePayload(payload, now) {
1972
2151
  }
1973
2152
  }
1974
2153
  }
1975
- return [...byId.values()].sort((a, b) => (a.windowMinutes ?? Infinity) - (b.windowMinutes ?? Infinity)).slice(0, 4);
2154
+ return [...byId.values()].sort((a, b) => (a.windowMinutes ?? Infinity) - (b.windowMinutes ?? Infinity)).slice(0, 4);
2155
+ }
2156
+ var KimiAllowanceCollector = class {
2157
+ constructor(credentials, store = getSharedAccountAllowanceStore3(), fetchImpl = (url, init, accountId) => fetchUpstream3(url, init, { providerId: "kimi", accountId, redactBodies: true }), now = Date.now) {
2158
+ this.credentials = credentials;
2159
+ this.store = store;
2160
+ this.fetchImpl = fetchImpl;
2161
+ this.now = now;
2162
+ }
2163
+ credentials;
2164
+ store;
2165
+ fetchImpl;
2166
+ now;
2167
+ inFlight = /* @__PURE__ */ new Map();
2168
+ async collectMany(accounts, options = {}) {
2169
+ const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
2170
+ return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
2171
+ }
2172
+ collect(account, options = {}) {
2173
+ const now = this.now();
2174
+ if (account.tokens.authMethod !== "oauth") {
2175
+ const existing = this.store.get("kimi", account.id, now);
2176
+ if (existing?.windows.every((window) => window.state === "unsupported")) {
2177
+ return Promise.resolve(existing);
2178
+ }
2179
+ const snapshot = this.unsupportedSnapshot(account.id, now);
2180
+ this.store.set(snapshot);
2181
+ return Promise.resolve(snapshot);
2182
+ }
2183
+ const cached = this.store.get("kimi", account.id, now);
2184
+ if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
2185
+ return Promise.resolve(cached);
2186
+ }
2187
+ const running = this.inFlight.get(account.id);
2188
+ if (running) return running;
2189
+ const promise = this.fetchAccount(account.id, account.tokens).catch(() => this.failureSnapshot(account.id, "kimi_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
2190
+ this.inFlight.set(account.id, promise);
2191
+ return promise;
2192
+ }
2193
+ isCacheValid(snapshot, now, refreshAheadMs) {
2194
+ if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
2195
+ const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
2196
+ const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
2197
+ return Number.isFinite(expiresAt) && expiresAt > now + ahead;
2198
+ }
2199
+ async fetchAccount(accountId, tokens) {
2200
+ let accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
2201
+ if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
2202
+ let response = await this.request(accountId, accessToken, tokens);
2203
+ if (response.status === 401) {
2204
+ const refreshed = await this.credentials.refreshAccountToken("kimi", accountId);
2205
+ if (!refreshed) return this.failureSnapshot(accountId, "kimi_usage_unauthorized", this.now());
2206
+ accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
2207
+ if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
2208
+ response = await this.request(accountId, accessToken, tokens);
2209
+ }
2210
+ if (response.status === 403) {
2211
+ const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "kimi_usage_unsupported");
2212
+ this.store.set(snapshot2);
2213
+ return snapshot2;
2214
+ }
2215
+ if (!response.ok) return this.failureSnapshot(accountId, "kimi_usage_http_error", this.now());
2216
+ let payload;
2217
+ try {
2218
+ payload = await response.json();
2219
+ } catch {
2220
+ return this.failureSnapshot(accountId, "kimi_usage_invalid_response", this.now());
2221
+ }
2222
+ const now = this.now();
2223
+ const windows = parseKimiUsagePayload(payload, now);
2224
+ const snapshot = {
2225
+ providerId: "kimi",
2226
+ accountId,
2227
+ source: "oauth-usage-api",
2228
+ observedAt: new Date(now).toISOString(),
2229
+ expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
2230
+ windows: windows.length > 0 ? windows : [
2231
+ { id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
2232
+ { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
2233
+ ],
2234
+ ...windows.length > 0 ? {} : { lastErrorCode: "kimi_usage_invalid_response" }
2235
+ };
2236
+ this.store.set(snapshot);
2237
+ return snapshot;
2238
+ }
2239
+ request(accountId, accessToken, tokens) {
2240
+ return this.fetchImpl(KIMI_USAGE_URL, {
2241
+ method: "GET",
2242
+ headers: {
2243
+ Authorization: `Bearer ${accessToken}`,
2244
+ Accept: "application/json",
2245
+ ...kimiFingerprintHeaders(tokens.deviceId)
2246
+ },
2247
+ signal: AbortSignal.timeout(15e3)
2248
+ }, accountId);
2249
+ }
2250
+ failureSnapshot(accountId, code, now) {
2251
+ const existing = this.store.get("kimi", accountId, now);
2252
+ const snapshot = existing ? {
2253
+ ...existing,
2254
+ expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
2255
+ windows: existing.windows.map((window) => ({
2256
+ ...window,
2257
+ state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
2258
+ })),
2259
+ lastErrorCode: code
2260
+ } : {
2261
+ providerId: "kimi",
2262
+ accountId,
2263
+ source: "oauth-usage-api",
2264
+ observedAt: new Date(now).toISOString(),
2265
+ expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
2266
+ windows: [
2267
+ { id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
2268
+ { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
2269
+ ],
2270
+ lastErrorCode: code
2271
+ };
2272
+ this.store.set(snapshot);
2273
+ return snapshot;
2274
+ }
2275
+ unsupportedSnapshot(accountId, now, code = "kimi_usage_unsupported_auth") {
2276
+ return {
2277
+ providerId: "kimi",
2278
+ accountId,
2279
+ source: "oauth-usage-api",
2280
+ observedAt: new Date(now).toISOString(),
2281
+ windows: [
2282
+ { id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unsupported" },
2283
+ { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
2284
+ ],
2285
+ lastErrorCode: code
2286
+ };
2287
+ }
2288
+ };
2289
+
2290
+ // src/allowance/GrokAllowanceCollector.ts
2291
+ import {
2292
+ getSharedAccountAllowanceStore as getSharedAccountAllowanceStore4
2293
+ } from "@omnicross/core/pipeline/AccountAllowanceStore";
2294
+ import { fetchUpstream as fetchUpstream4 } from "@omnicross/core/pipeline/upstreamFetch";
2295
+ var GROK_ALLOWANCE_CACHE_MS = 5 * 6e4;
2296
+ var GROK_BILLING_BASE = "https://cli-chat-proxy.grok.com";
2297
+ var GROK_BILLING_CREDITS_URL = `${GROK_BILLING_BASE}/v1/billing?format=credits`;
2298
+ var GROK_BILLING_MONTHLY_URL = `${GROK_BILLING_BASE}/v1/billing`;
2299
+ function isRecord2(value) {
2300
+ return !!value && typeof value === "object" && !Array.isArray(value);
2301
+ }
2302
+ function finiteNumber3(value) {
2303
+ if (value === null || value === void 0 || value === "") return void 0;
2304
+ const parsed = typeof value === "number" ? value : Number(value);
2305
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
2306
+ }
2307
+ function percent(value) {
2308
+ const parsed = finiteNumber3(value);
2309
+ return parsed !== void 0 && parsed <= 100 ? parsed : void 0;
2310
+ }
2311
+ function onDemandAmount(value) {
2312
+ return isRecord2(value) ? finiteNumber3(value["val"]) : void 0;
2313
+ }
2314
+ function confirmsNoMonthlyQuota(raw) {
2315
+ const limit = onDemandAmount(raw["monthlyLimit"]);
2316
+ if (limit !== void 0) return limit === 0;
2317
+ return parseWeeklyConfig(raw)?.inferredPercent === true;
2318
+ }
2319
+ function parseWeeklyConfig(raw) {
2320
+ const period = isRecord2(raw["currentPeriod"]) ? raw["currentPeriod"] : void 0;
2321
+ if (!period) return null;
2322
+ const start = typeof period["start"] === "string" ? Date.parse(period["start"]) : Number.NaN;
2323
+ const end = typeof period["end"] === "string" ? Date.parse(period["end"]) : Number.NaN;
2324
+ const type = typeof period["type"] === "string" ? period["type"] : "";
2325
+ if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
2326
+ if (!type.toUpperCase().includes("WEEK")) return null;
2327
+ const inferred = raw["creditUsagePercent"] === void 0 || raw["creditUsagePercent"] === null;
2328
+ let creditUsagePercent;
2329
+ if (inferred) {
2330
+ creditUsagePercent = end > Date.now() ? 0 : void 0;
2331
+ } else {
2332
+ creditUsagePercent = percent(raw["creditUsagePercent"]);
2333
+ }
2334
+ if (creditUsagePercent === void 0) return null;
2335
+ return {
2336
+ creditUsagePercent,
2337
+ inferredPercent: inferred,
2338
+ resetsAtMs: end,
2339
+ unified: raw["isUnifiedBillingUser"] === true
2340
+ };
2341
+ }
2342
+ function parseMonthlyConfig(raw) {
2343
+ const start = typeof raw["billingPeriodStart"] === "string" ? Date.parse(raw["billingPeriodStart"]) : Number.NaN;
2344
+ const end = typeof raw["billingPeriodEnd"] === "string" ? Date.parse(raw["billingPeriodEnd"]) : Number.NaN;
2345
+ if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
2346
+ const limit = onDemandAmount(raw["monthlyLimit"]);
2347
+ const used = onDemandAmount(raw["used"]);
2348
+ if (limit === void 0 || limit <= 0 || used === void 0) return null;
2349
+ return { used, limit, periodStartMs: start, periodEndMs: end };
2350
+ }
2351
+ function secondsUntil4(instant, now) {
2352
+ if (!instant) return void 0;
2353
+ return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
2354
+ }
2355
+ var MINUTE_MS2 = 6e4;
2356
+ var DAY_MS2 = 864e5;
2357
+ var WEEK_MINUTES = 7 * 24 * 60;
2358
+ function weeklyWindow(config, now) {
2359
+ const resetsAt = new Date(config.resetsAtMs).toISOString();
2360
+ return {
2361
+ id: "seven-day",
2362
+ label: "7 days",
2363
+ scope: "all",
2364
+ usedPercent: config.creditUsagePercent,
2365
+ windowMinutes: WEEK_MINUTES,
2366
+ resetsAt,
2367
+ remainingSeconds: secondsUntil4(resetsAt, now),
2368
+ state: "fresh"
2369
+ };
2370
+ }
2371
+ function monthlyWindow(config, now) {
2372
+ const resetsAt = new Date(config.periodEndMs).toISOString();
2373
+ const days = Math.max(1, Math.round((config.periodEndMs - config.periodStartMs) / DAY_MS2));
2374
+ return {
2375
+ id: "thirty-day",
2376
+ label: days === 30 || days === 31 ? "30 days" : `${days} days`,
2377
+ scope: "all",
2378
+ usedPercent: Math.round(Math.min(100, config.used / config.limit * 100) * 10) / 10,
2379
+ windowMinutes: Math.round((config.periodEndMs - config.periodStartMs) / MINUTE_MS2),
2380
+ resetsAt,
2381
+ remainingSeconds: secondsUntil4(resetsAt, now),
2382
+ state: "fresh"
2383
+ };
2384
+ }
2385
+ function onDemandWindow(raw) {
2386
+ const cap = onDemandAmount(raw["onDemandCap"]);
2387
+ const used = onDemandAmount(raw["onDemandUsed"]);
2388
+ if (cap === void 0 || cap <= 0 || used === void 0) return null;
2389
+ return {
2390
+ id: "on-demand",
2391
+ label: "On-demand",
2392
+ scope: "all",
2393
+ usedPercent: Math.round(Math.min(100, used / cap * 100) * 10) / 10,
2394
+ state: "fresh"
2395
+ };
2396
+ }
2397
+ async function probeBilling(url, accessToken, accountId, fetchImpl) {
2398
+ try {
2399
+ const response = await fetchImpl(url, {
2400
+ method: "GET",
2401
+ headers: {
2402
+ Authorization: `Bearer ${accessToken}`,
2403
+ Accept: "application/json",
2404
+ "X-XAI-Token-Auth": "xai-grok-cli"
2405
+ },
2406
+ redirect: "error",
2407
+ signal: AbortSignal.timeout(15e3)
2408
+ }, accountId);
2409
+ if (!response.ok) return { status: response.status, payload: null };
2410
+ const payload = await response.json();
2411
+ return { status: response.status, payload: isRecord2(payload) ? payload : null };
2412
+ } catch {
2413
+ return { status: 0, payload: null };
2414
+ }
2415
+ }
2416
+ function parseGrokBillingPayloads(creditsPayload, monthlyPayload, now) {
2417
+ const creditsConfig = isRecord2(creditsPayload?.["config"]) ? creditsPayload["config"] : null;
2418
+ const monthlyConfig = isRecord2(monthlyPayload?.["config"]) ? monthlyPayload["config"] : null;
2419
+ let weekly = creditsConfig ? parseWeeklyConfig(creditsConfig) : null;
2420
+ const unifiedFlag = creditsConfig?.["isUnifiedBillingUser"] === true;
2421
+ let monthly = monthlyConfig ? parseMonthlyConfig(monthlyConfig) : null;
2422
+ if (weekly?.inferredPercent && unifiedFlag) {
2423
+ if (monthly) {
2424
+ weekly = null;
2425
+ } else if (!monthlyConfig || !confirmsNoMonthlyQuota(monthlyConfig)) {
2426
+ weekly = null;
2427
+ }
2428
+ }
2429
+ const windows = [];
2430
+ if (weekly) windows.push(weeklyWindow(weekly, now));
2431
+ if (monthly) windows.push(monthlyWindow(monthly, now));
2432
+ const onDemandSource = monthly && monthlyConfig ? monthlyConfig : creditsConfig;
2433
+ const onDemand = onDemandSource ? onDemandWindow(onDemandSource) : null;
2434
+ if (onDemand) windows.push(onDemand);
2435
+ return windows.length > 0 ? windows : null;
2436
+ }
2437
+ var GrokAllowanceCollector = class {
2438
+ constructor(credentials, store = getSharedAccountAllowanceStore4(), fetchImpl = (url, init, accountId) => fetchUpstream4(url, init, { providerId: "grok", accountId, redactBodies: true }), now = Date.now) {
2439
+ this.credentials = credentials;
2440
+ this.store = store;
2441
+ this.fetchImpl = fetchImpl;
2442
+ this.now = now;
2443
+ }
2444
+ credentials;
2445
+ store;
2446
+ fetchImpl;
2447
+ now;
2448
+ inFlight = /* @__PURE__ */ new Map();
2449
+ async collectMany(accounts, options = {}) {
2450
+ const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
2451
+ return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
2452
+ }
2453
+ collect(account, options = {}) {
2454
+ const now = this.now();
2455
+ if (account.tokens.authMethod !== "oauth") {
2456
+ const existing = this.store.get("grok", account.id, now);
2457
+ if (existing?.windows.every((window) => window.state === "unsupported")) {
2458
+ return Promise.resolve(existing);
2459
+ }
2460
+ const snapshot = this.unsupportedSnapshot(account.id, now);
2461
+ this.store.set(snapshot);
2462
+ return Promise.resolve(snapshot);
2463
+ }
2464
+ const cached = this.store.get("grok", account.id, now);
2465
+ if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
2466
+ return Promise.resolve(cached);
2467
+ }
2468
+ const running = this.inFlight.get(account.id);
2469
+ if (running) return running;
2470
+ const promise = this.fetchAccount(account.id).catch(() => this.failureSnapshot(account.id, "grok_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
2471
+ this.inFlight.set(account.id, promise);
2472
+ return promise;
2473
+ }
2474
+ isCacheValid(snapshot, now, refreshAheadMs) {
2475
+ if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
2476
+ const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
2477
+ const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
2478
+ return Number.isFinite(expiresAt) && expiresAt > now + ahead;
2479
+ }
2480
+ async fetchAccount(accountId) {
2481
+ const probe = async () => {
2482
+ const accessToken = await this.credentials.getAccessTokenForAccount("grok", accountId);
2483
+ if (!accessToken) return { unauthorized: true, windows: null };
2484
+ const credits = await probeBilling(GROK_BILLING_CREDITS_URL, accessToken, accountId, this.fetchImpl);
2485
+ if (credits.status === 401 || credits.status === 403) return { unauthorized: true, windows: null };
2486
+ const creditsConfig = isRecord2(credits.payload?.["config"]) ? credits.payload["config"] : null;
2487
+ const weekly = creditsConfig ? parseWeeklyConfig(creditsConfig) : null;
2488
+ const monthly = !weekly || creditsConfig?.["isUnifiedBillingUser"] === true ? await probeBilling(GROK_BILLING_MONTHLY_URL, accessToken, accountId, this.fetchImpl) : { status: 200, payload: null };
2489
+ if (monthly.status === 401 || monthly.status === 403) return { unauthorized: true, windows: null };
2490
+ return {
2491
+ unauthorized: false,
2492
+ windows: parseGrokBillingPayloads(credits.payload, monthly.payload, this.now())
2493
+ };
2494
+ };
2495
+ let result = await probe();
2496
+ if (result.unauthorized) {
2497
+ const refreshed = await this.credentials.refreshAccountToken("grok", accountId);
2498
+ if (!refreshed) return this.failureSnapshot(accountId, "grok_usage_unauthorized", this.now());
2499
+ result = await probe();
2500
+ if (result.unauthorized) {
2501
+ return this.failureSnapshot(accountId, "grok_usage_unauthorized", this.now());
2502
+ }
2503
+ }
2504
+ const now = this.now();
2505
+ if (result.windows && result.windows.length > 0) {
2506
+ const snapshot = {
2507
+ providerId: "grok",
2508
+ accountId,
2509
+ source: "oauth-usage-api",
2510
+ observedAt: new Date(now).toISOString(),
2511
+ expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
2512
+ windows: result.windows
2513
+ };
2514
+ this.store.set(snapshot);
2515
+ return snapshot;
2516
+ }
2517
+ return this.failureSnapshot(accountId, "grok_usage_invalid_response", now);
2518
+ }
2519
+ failureSnapshot(accountId, code, now) {
2520
+ const existing = this.store.get("grok", accountId, now);
2521
+ const snapshot = existing ? {
2522
+ ...existing,
2523
+ expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
2524
+ windows: existing.windows.map((window) => ({
2525
+ ...window,
2526
+ state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
2527
+ })),
2528
+ lastErrorCode: code
2529
+ } : {
2530
+ providerId: "grok",
2531
+ accountId,
2532
+ source: "oauth-usage-api",
2533
+ observedAt: new Date(now).toISOString(),
2534
+ expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
2535
+ windows: [
2536
+ { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" },
2537
+ { id: "thirty-day", label: "30 days", scope: "all", usedPercent: null, state: "unavailable" }
2538
+ ],
2539
+ lastErrorCode: code
2540
+ };
2541
+ this.store.set(snapshot);
2542
+ return snapshot;
2543
+ }
2544
+ unsupportedSnapshot(accountId, now) {
2545
+ return {
2546
+ providerId: "grok",
2547
+ accountId,
2548
+ source: "oauth-usage-api",
2549
+ observedAt: new Date(now).toISOString(),
2550
+ windows: [
2551
+ { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" },
2552
+ { id: "thirty-day", label: "30 days", scope: "all", usedPercent: null, state: "unsupported" }
2553
+ ],
2554
+ lastErrorCode: "grok_usage_unsupported_auth"
2555
+ };
2556
+ }
2557
+ };
2558
+
2559
+ // src/allowance/CopilotAllowanceCollector.ts
2560
+ import {
2561
+ getSharedAccountAllowanceStore as getSharedAccountAllowanceStore5
2562
+ } from "@omnicross/core/pipeline/AccountAllowanceStore";
2563
+ import { fetchUpstream as fetchUpstream5 } from "@omnicross/core/pipeline/upstreamFetch";
2564
+ import { COPILOT_GITHUB_HEADERS, copilotGitHubApiBase } from "@omnicross/subscriptions";
2565
+ var COPILOT_ALLOWANCE_CACHE_MS = 5 * 6e4;
2566
+ function isRecord3(value) {
2567
+ return !!value && typeof value === "object" && !Array.isArray(value);
2568
+ }
2569
+ function finiteNumber4(value) {
2570
+ if (value === null || value === void 0 || value === "") return void 0;
2571
+ const parsed = typeof value === "number" ? value : Number(value);
2572
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
2573
+ }
2574
+ function booleanValue(value) {
2575
+ if (typeof value === "boolean") return value;
2576
+ if (value === "true") return true;
2577
+ if (value === "false") return false;
2578
+ return void 0;
2579
+ }
2580
+ function parseQuotaDetail(value) {
2581
+ if (!isRecord3(value)) return null;
2582
+ const entitlement = finiteNumber4(value["entitlement"]);
2583
+ const remaining = finiteNumber4(value["remaining"]);
2584
+ const percentRemaining = finiteNumber4(value["percent_remaining"]);
2585
+ const unlimited = booleanValue(value["unlimited"]);
2586
+ if (entitlement === void 0 || remaining === void 0 || percentRemaining === void 0 || unlimited === void 0) {
2587
+ return null;
2588
+ }
2589
+ return { entitlement, remaining, percentRemaining, unlimited };
2590
+ }
2591
+ function secondsUntil5(instant, now) {
2592
+ if (!instant) return void 0;
2593
+ return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
2594
+ }
2595
+ function parseCopilotUserPayload(payload, now) {
2596
+ if (!isRecord3(payload)) return null;
2597
+ const snapshots = isRecord3(payload["quota_snapshots"]) ? payload["quota_snapshots"] : void 0;
2598
+ if (!snapshots) return null;
2599
+ const resetRaw = payload["quota_reset_date"];
2600
+ const resetsAt = typeof resetRaw === "string" && resetRaw.trim() && Number.isFinite(Date.parse(resetRaw)) ? new Date(Date.parse(resetRaw)).toISOString() : void 0;
2601
+ const windows = [];
2602
+ const premium = parseQuotaDetail(snapshots["premium_interactions"]);
2603
+ if (premium) {
2604
+ const usedPercent = premium.unlimited ? 0 : premium.entitlement > 0 ? Math.round(Math.min(100, (premium.entitlement - premium.remaining) / premium.entitlement * 100) * 10) / 10 : finiteNumber4(premium.percentRemaining) !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - premium.percentRemaining)) * 10) / 10 : null;
2605
+ if (usedPercent !== null) {
2606
+ windows.push({
2607
+ id: "thirty-day",
2608
+ label: "Monthly",
2609
+ scope: "all",
2610
+ usedPercent,
2611
+ windowMinutes: 30 * 24 * 60,
2612
+ ...resetsAt !== void 0 ? { resetsAt } : {},
2613
+ remainingSeconds: secondsUntil5(resetsAt, now),
2614
+ state: "fresh"
2615
+ });
2616
+ }
2617
+ }
2618
+ const chat = parseQuotaDetail(snapshots["chat"]);
2619
+ if (chat && !chat.unlimited && chat.entitlement > 0) {
2620
+ const usedPercent = Math.round(Math.min(100, (chat.entitlement - chat.remaining) / chat.entitlement * 100) * 10) / 10;
2621
+ windows.push({
2622
+ id: "chat-monthly",
2623
+ label: "Chat (monthly)",
2624
+ scope: "all",
2625
+ usedPercent,
2626
+ windowMinutes: 30 * 24 * 60,
2627
+ ...resetsAt !== void 0 ? { resetsAt } : {},
2628
+ remainingSeconds: secondsUntil5(resetsAt, now),
2629
+ state: "fresh"
2630
+ });
2631
+ }
2632
+ return windows.length > 0 ? windows : null;
1976
2633
  }
1977
- var KimiAllowanceCollector = class {
1978
- constructor(credentials, store = getSharedAccountAllowanceStore3(), fetchImpl = (url, init, accountId) => fetchUpstream3(url, init, { providerId: "kimi", accountId, redactBodies: true }), now = Date.now) {
2634
+ function githubApiBase(tokens) {
2635
+ return copilotGitHubApiBase(tokens.enterpriseUrl);
2636
+ }
2637
+ var CopilotAllowanceCollector = class {
2638
+ constructor(credentials, store = getSharedAccountAllowanceStore5(), fetchImpl = (url, init, accountId) => fetchUpstream5(url, init, { providerId: "copilot", accountId, redactBodies: true }), now = Date.now) {
1979
2639
  this.credentials = credentials;
1980
2640
  this.store = store;
1981
2641
  this.fetchImpl = fetchImpl;
@@ -1987,13 +2647,15 @@ var KimiAllowanceCollector = class {
1987
2647
  now;
1988
2648
  inFlight = /* @__PURE__ */ new Map();
1989
2649
  async collectMany(accounts, options = {}) {
1990
- const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
2650
+ const settled = await Promise.allSettled(
2651
+ accounts.map((account) => this.collect(account, options))
2652
+ );
1991
2653
  return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
1992
2654
  }
1993
2655
  collect(account, options = {}) {
1994
2656
  const now = this.now();
1995
2657
  if (account.tokens.authMethod !== "oauth") {
1996
- const existing = this.store.get("kimi", account.id, now);
2658
+ const existing = this.store.get("copilot", account.id, now);
1997
2659
  if (existing?.windows.every((window) => window.state === "unsupported")) {
1998
2660
  return Promise.resolve(existing);
1999
2661
  }
@@ -2001,13 +2663,13 @@ var KimiAllowanceCollector = class {
2001
2663
  this.store.set(snapshot);
2002
2664
  return Promise.resolve(snapshot);
2003
2665
  }
2004
- const cached = this.store.get("kimi", account.id, now);
2666
+ const cached = this.store.get("copilot", account.id, now);
2005
2667
  if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
2006
2668
  return Promise.resolve(cached);
2007
2669
  }
2008
2670
  const running = this.inFlight.get(account.id);
2009
2671
  if (running) return running;
2010
- const promise = this.fetchAccount(account.id, account.tokens).catch(() => this.failureSnapshot(account.id, "kimi_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
2672
+ const promise = this.fetchAccount(account.id, account.tokens).catch(() => this.failureSnapshot(account.id, "copilot_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
2011
2673
  this.inFlight.set(account.id, promise);
2012
2674
  return promise;
2013
2675
  }
@@ -2018,101 +2680,97 @@ var KimiAllowanceCollector = class {
2018
2680
  return Number.isFinite(expiresAt) && expiresAt > now + ahead;
2019
2681
  }
2020
2682
  async fetchAccount(accountId, tokens) {
2021
- let accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
2022
- if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
2683
+ let accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
2684
+ if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
2023
2685
  let response = await this.request(accountId, accessToken, tokens);
2024
- if (response.status === 401) {
2025
- const refreshed = await this.credentials.refreshAccountToken("kimi", accountId);
2026
- if (!refreshed) return this.failureSnapshot(accountId, "kimi_usage_unauthorized", this.now());
2027
- accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
2028
- if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
2686
+ if (response.status === 401 || response.status === 403) {
2687
+ const refreshed = await this.credentials.refreshAccountToken("copilot", accountId);
2688
+ if (!refreshed) return this.failureSnapshot(accountId, "copilot_usage_unauthorized", this.now());
2689
+ accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
2690
+ if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
2029
2691
  response = await this.request(accountId, accessToken, tokens);
2692
+ if (response.status === 401 || response.status === 403) {
2693
+ return this.failureSnapshot(accountId, "copilot_usage_unauthorized", this.now());
2694
+ }
2030
2695
  }
2031
- if (response.status === 403) {
2032
- const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "kimi_usage_unsupported");
2033
- this.store.set(snapshot2);
2034
- return snapshot2;
2035
- }
2036
- if (!response.ok) return this.failureSnapshot(accountId, "kimi_usage_http_error", this.now());
2696
+ if (!response.ok) return this.failureSnapshot(accountId, "copilot_usage_http_error", this.now());
2037
2697
  let payload;
2038
2698
  try {
2039
2699
  payload = await response.json();
2040
2700
  } catch {
2041
- return this.failureSnapshot(accountId, "kimi_usage_invalid_response", this.now());
2701
+ return this.failureSnapshot(accountId, "copilot_usage_invalid_response", this.now());
2042
2702
  }
2043
2703
  const now = this.now();
2044
- const windows = parseKimiUsagePayload(payload, now);
2704
+ const windows = parseCopilotUserPayload(payload, now);
2045
2705
  const snapshot = {
2046
- providerId: "kimi",
2706
+ providerId: "copilot",
2047
2707
  accountId,
2048
2708
  source: "oauth-usage-api",
2049
2709
  observedAt: new Date(now).toISOString(),
2050
- expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
2051
- windows: windows.length > 0 ? windows : [
2052
- { id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
2053
- { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
2710
+ expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
2711
+ windows: windows ?? [
2712
+ { id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
2054
2713
  ],
2055
- ...windows.length > 0 ? {} : { lastErrorCode: "kimi_usage_invalid_response" }
2714
+ ...windows ? {} : { lastErrorCode: "copilot_usage_invalid_response" }
2056
2715
  };
2057
2716
  this.store.set(snapshot);
2058
2717
  return snapshot;
2059
2718
  }
2060
2719
  request(accountId, accessToken, tokens) {
2061
- return this.fetchImpl(KIMI_USAGE_URL, {
2720
+ return this.fetchImpl(`${githubApiBase(tokens)}/copilot_internal/user`, {
2062
2721
  method: "GET",
2063
2722
  headers: {
2064
2723
  Authorization: `Bearer ${accessToken}`,
2065
2724
  Accept: "application/json",
2066
- ...kimiFingerprintHeaders(tokens.deviceId)
2725
+ "Content-Type": "application/json",
2726
+ ...COPILOT_GITHUB_HEADERS
2067
2727
  },
2068
2728
  signal: AbortSignal.timeout(15e3)
2069
2729
  }, accountId);
2070
2730
  }
2071
2731
  failureSnapshot(accountId, code, now) {
2072
- const existing = this.store.get("kimi", accountId, now);
2732
+ const existing = this.store.get("copilot", accountId, now);
2073
2733
  const snapshot = existing ? {
2074
2734
  ...existing,
2075
- expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
2735
+ expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
2076
2736
  windows: existing.windows.map((window) => ({
2077
2737
  ...window,
2078
2738
  state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
2079
2739
  })),
2080
2740
  lastErrorCode: code
2081
2741
  } : {
2082
- providerId: "kimi",
2742
+ providerId: "copilot",
2083
2743
  accountId,
2084
2744
  source: "oauth-usage-api",
2085
2745
  observedAt: new Date(now).toISOString(),
2086
- expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
2746
+ expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
2087
2747
  windows: [
2088
- { id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
2089
- { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
2748
+ { id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
2090
2749
  ],
2091
2750
  lastErrorCode: code
2092
2751
  };
2093
2752
  this.store.set(snapshot);
2094
2753
  return snapshot;
2095
2754
  }
2096
- unsupportedSnapshot(accountId, now, code = "kimi_usage_unsupported_auth") {
2755
+ unsupportedSnapshot(accountId, now) {
2097
2756
  return {
2098
- providerId: "kimi",
2757
+ providerId: "copilot",
2099
2758
  accountId,
2100
2759
  source: "oauth-usage-api",
2101
2760
  observedAt: new Date(now).toISOString(),
2102
2761
  windows: [
2103
- { id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unsupported" },
2104
- { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
2762
+ { id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unsupported" }
2105
2763
  ],
2106
- lastErrorCode: code
2764
+ lastErrorCode: "copilot_usage_unsupported_auth"
2107
2765
  };
2108
2766
  }
2109
2767
  };
2110
2768
 
2111
2769
  // src/allowance/OpenCodeGoAllowanceCollector.ts
2112
2770
  import {
2113
- getSharedAccountAllowanceStore as getSharedAccountAllowanceStore4
2771
+ getSharedAccountAllowanceStore as getSharedAccountAllowanceStore6
2114
2772
  } from "@omnicross/core/pipeline/AccountAllowanceStore";
2115
- import { fetchUpstream as fetchUpstream4 } from "@omnicross/core/pipeline/upstreamFetch";
2773
+ import { fetchUpstream as fetchUpstream6 } from "@omnicross/core/pipeline/upstreamFetch";
2116
2774
  import { normalizeOpenCodeGoBaseUrl } from "@omnicross/subscriptions";
2117
2775
  var OPENCODEGO_ALLOWANCE_CACHE_MS = 5 * 6e4;
2118
2776
  var OPENCODEGO_DEFAULT_GO_BASE = "https://opencode.ai/zen/go";
@@ -2126,7 +2784,7 @@ function isoInstant2(value) {
2126
2784
  const time = Date.parse(value);
2127
2785
  return Number.isFinite(time) ? new Date(time).toISOString() : void 0;
2128
2786
  }
2129
- function secondsUntil4(instant, now) {
2787
+ function secondsUntil6(instant, now) {
2130
2788
  if (!instant) return void 0;
2131
2789
  return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
2132
2790
  }
@@ -2141,12 +2799,12 @@ function windowFromPayload3(id, label, minutes, payload, now) {
2141
2799
  usedPercent,
2142
2800
  windowMinutes: minutes,
2143
2801
  ...resetsAt !== void 0 ? { resetsAt } : {},
2144
- remainingSeconds: secondsUntil4(resetsAt, now),
2802
+ remainingSeconds: secondsUntil6(resetsAt, now),
2145
2803
  state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
2146
2804
  };
2147
2805
  }
2148
2806
  var OpenCodeGoAllowanceCollector = class {
2149
- constructor(credentials, store = getSharedAccountAllowanceStore4(), fetchImpl = (url, init, accountId) => fetchUpstream4(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
2807
+ constructor(credentials, store = getSharedAccountAllowanceStore6(), fetchImpl = (url, init, accountId) => fetchUpstream6(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
2150
2808
  this.credentials = credentials;
2151
2809
  this.store = store;
2152
2810
  this.fetchImpl = fetchImpl;
@@ -2251,7 +2909,7 @@ function codexUnavailable(accountId, now) {
2251
2909
  };
2252
2910
  }
2253
2911
  var AccountAllowanceService = class {
2254
- constructor(credentials, store = getSharedAccountAllowanceStore5(), collector, codexCollector, kimiCollector, opencodegoCollector, now = Date.now) {
2912
+ constructor(credentials, store = getSharedAccountAllowanceStore7(), collector, codexCollector, kimiCollector, opencodegoCollector, grokCollector, copilotCollector, now = Date.now) {
2255
2913
  this.credentials = credentials;
2256
2914
  this.store = store;
2257
2915
  this.now = now;
@@ -2259,6 +2917,8 @@ var AccountAllowanceService = class {
2259
2917
  this.codexCollector = codexCollector ?? new CodexAllowanceCollector(credentials, store);
2260
2918
  this.kimiCollector = kimiCollector ?? new KimiAllowanceCollector(credentials, store);
2261
2919
  this.opencodegoCollector = opencodegoCollector ?? new OpenCodeGoAllowanceCollector(credentials, store);
2920
+ this.grokCollector = grokCollector ?? new GrokAllowanceCollector(credentials, store);
2921
+ this.copilotCollector = copilotCollector ?? new CopilotAllowanceCollector(credentials, store);
2262
2922
  }
2263
2923
  credentials;
2264
2924
  store;
@@ -2266,6 +2926,8 @@ var AccountAllowanceService = class {
2266
2926
  claudeCollector;
2267
2927
  codexCollector;
2268
2928
  kimiCollector;
2929
+ grokCollector;
2930
+ copilotCollector;
2269
2931
  opencodegoCollector;
2270
2932
  /**
2271
2933
  * Read all/filtered snapshots. Claude's and Codex's five-minute caches are
@@ -2300,11 +2962,23 @@ var AccountAllowanceService = class {
2300
2962
  (account) => !filter.accountId || account.id === filter.accountId
2301
2963
  );
2302
2964
  if (wantsOpenCodeGo) await this.opencodegoCollector.collectMany(opencodegoAccounts);
2965
+ const wantsGrok = !filter.providerId || filter.providerId === "grok";
2966
+ const grokAccounts = (config.grokAccounts ?? []).filter(
2967
+ (account) => !filter.accountId || account.id === filter.accountId
2968
+ );
2969
+ if (wantsGrok) await this.grokCollector.collectMany(grokAccounts);
2970
+ const wantsCopilot = !filter.providerId || filter.providerId === "copilot";
2971
+ const copilotAccounts = (config.copilotAccounts ?? []).filter(
2972
+ (account) => !filter.accountId || account.id === filter.accountId
2973
+ );
2974
+ if (wantsCopilot) await this.copilotCollector.collectMany(copilotAccounts);
2303
2975
  const known = /* @__PURE__ */ new Set();
2304
2976
  if (wantsClaude) for (const account of claudeAccounts) known.add(`claude\0${account.id}`);
2305
2977
  if (wantsCodex) for (const account of codexAccounts) known.add(`codex\0${account.id}`);
2306
2978
  if (wantsKimi) for (const account of kimiAccounts) known.add(`kimi\0${account.id}`);
2307
2979
  if (wantsOpenCodeGo) for (const account of opencodegoAccounts) known.add(`opencodego\0${account.id}`);
2980
+ if (wantsGrok) for (const account of grokAccounts) known.add(`grok\0${account.id}`);
2981
+ if (wantsCopilot) for (const account of copilotAccounts) known.add(`copilot\0${account.id}`);
2308
2982
  return this.store.list(filter).filter((snapshot) => known.has(`${snapshot.providerId}\0${snapshot.accountId}`));
2309
2983
  }
2310
2984
  knownAccounts(config) {
@@ -2312,7 +2986,9 @@ var AccountAllowanceService = class {
2312
2986
  ...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
2313
2987
  ...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id })),
2314
2988
  ...(config.kimiAccounts ?? []).map((account) => ({ providerId: "kimi", accountId: account.id })),
2315
- ...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id }))
2989
+ ...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id })),
2990
+ ...(config.grokAccounts ?? []).map((account) => ({ providerId: "grok", accountId: account.id })),
2991
+ ...(config.copilotAccounts ?? []).map((account) => ({ providerId: "copilot", accountId: account.id }))
2316
2992
  ];
2317
2993
  }
2318
2994
  /** Force-refresh Claude usage for one account or every stored Claude account. */
@@ -2355,6 +3031,24 @@ var AccountAllowanceService = class {
2355
3031
  );
2356
3032
  return this.kimiCollector.collectMany(accounts, { force: true });
2357
3033
  }
3034
+ /** Force-refresh Copilot usage (copilot_internal/user) for one/all accounts. */
3035
+ async refreshCopilot(accountId) {
3036
+ const config = await this.credentials.getFullConfig();
3037
+ this.store.pruneToKnownAccounts(this.knownAccounts(config));
3038
+ const accounts = (config.copilotAccounts ?? []).filter(
3039
+ (account) => !accountId || account.id === accountId
3040
+ );
3041
+ return this.copilotCollector.collectMany(accounts, { force: true });
3042
+ }
3043
+ /** Force-refresh Grok usage (CLI billing proxy) for one/all accounts. */
3044
+ async refreshGrok(accountId) {
3045
+ const config = await this.credentials.getFullConfig();
3046
+ this.store.pruneToKnownAccounts(this.knownAccounts(config));
3047
+ const accounts = (config.grokAccounts ?? []).filter(
3048
+ (account) => !accountId || account.id === accountId
3049
+ );
3050
+ return this.grokCollector.collectMany(accounts, { force: true });
3051
+ }
2358
3052
  /**
2359
3053
  * Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
2360
3054
  * collectors preserve their cache + per-account in-flight coalescing; a tick
@@ -2369,6 +3063,8 @@ var AccountAllowanceService = class {
2369
3063
  await this.codexCollector.collectMany(config.codexAccounts ?? [], { refreshAheadMs });
2370
3064
  await this.kimiCollector.collectMany(config.kimiAccounts ?? [], { refreshAheadMs });
2371
3065
  await this.opencodegoCollector.collectMany(config.opencodegoAccounts ?? [], { refreshAheadMs });
3066
+ await this.grokCollector.collectMany(config.grokAccounts ?? [], { refreshAheadMs });
3067
+ await this.copilotCollector.collectMany(config.copilotAccounts ?? [], { refreshAheadMs });
2372
3068
  }
2373
3069
  /** Remove a cache row as soon as an account is deleted by the admin path. */
2374
3070
  removeAccountSnapshot(providerId, accountId) {
@@ -2798,7 +3494,8 @@ import {
2798
3494
  } from "@omnicross/contracts/image-generation-types";
2799
3495
  import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling2 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
2800
3496
  import { getSharedAccountHealth } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
2801
- import { fetchUpstream as fetchUpstream5 } from "@omnicross/core/pipeline/upstreamFetch";
3497
+ import { fetchUpstream as fetchUpstream7 } from "@omnicross/core/pipeline/upstreamFetch";
3498
+ import { mergeExtraHeaders } from "@omnicross/core";
2802
3499
 
2803
3500
  // src/image-generation/imagesConfigValidation.ts
2804
3501
  import { validateImagesServerConfig } from "@omnicross/core/outbound-api";
@@ -3111,6 +3808,7 @@ async function applyServerConfigTransaction(current, next, deps) {
3111
3808
 
3112
3809
  // src/config.ts
3113
3810
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
3811
+ import { EXTRA_HEADER_RESERVED_NAMES } from "@omnicross/core";
3114
3812
  var DEFAULT_ADMIN_PORT = 8766;
3115
3813
  function validateAdmin(raw) {
3116
3814
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
@@ -3167,6 +3865,18 @@ var FORMAT_AXIS_TRANSFORMERS = [
3167
3865
  "openai-response",
3168
3866
  "gemini-code-assist"
3169
3867
  ];
3868
+ function validateExtraHeaders(raw) {
3869
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
3870
+ const reserved = EXTRA_HEADER_RESERVED_NAMES;
3871
+ const out = {};
3872
+ for (const [name, value] of Object.entries(raw)) {
3873
+ if (!name.trim()) continue;
3874
+ if (typeof value !== "string") continue;
3875
+ if (reserved.has(name.toLowerCase())) continue;
3876
+ out[name] = value;
3877
+ }
3878
+ return Object.keys(out).length > 0 ? out : void 0;
3879
+ }
3170
3880
  function validateApiKeys(raw) {
3171
3881
  if (!Array.isArray(raw)) return void 0;
3172
3882
  const out = [];
@@ -3380,6 +4090,9 @@ function validateProvider(raw, index) {
3380
4090
  apiVersion,
3381
4091
  maxConcurrency,
3382
4092
  modelsEndpoint,
4093
+ // Static extra headers: load-guard (reserved names dropped), collapse-to-
4094
+ // undefined; enforced by the outbound header funnel + admin probes.
4095
+ extraHeaders: validateExtraHeaders(p["extraHeaders"]),
3383
4096
  // Provider transformer config (app-parity child 5): load-guard, collapse-to-
3384
4097
  // undefined; non-secret; ENFORCED via resolveTransformerChain (parity-2 child 2).
3385
4098
  // Format-axis entries are stripped by `migrateFormatAxis` — `use[]` is the
@@ -4344,7 +5057,10 @@ function mapPresetToProvider(preset, opts) {
4344
5057
  apiFormat: resolved.format,
4345
5058
  baseUrl: opts.baseUrlOverride ?? preset.api_base_url,
4346
5059
  apiKey: opts.key,
4347
- models: Array.isArray(preset.models) ? preset.models : void 0
5060
+ models: Array.isArray(preset.models) ? preset.models : void 0,
5061
+ // Static identity headers (e.g. the Cline client set) survive the mapping —
5062
+ // the CLI-seeded row needs them as much as an admin-API-created one.
5063
+ extraHeaders: preset.extraHeaders
4348
5064
  };
4349
5065
  return { provider };
4350
5066
  }
@@ -4369,7 +5085,8 @@ function listMappablePresets() {
4369
5085
  description: preset.description,
4370
5086
  features: preset.features,
4371
5087
  website: preset.website,
4372
- modelsEndpoint: preset.modelsEndpoint
5088
+ modelsEndpoint: preset.modelsEndpoint,
5089
+ extraHeaders: preset.extraHeaders
4373
5090
  });
4374
5091
  }
4375
5092
  return { mappable, excluded };
@@ -4541,7 +5258,9 @@ var VALID_PROVIDER_IDS = [
4541
5258
  "codex",
4542
5259
  "gemini",
4543
5260
  "opencodego",
4544
- "kimi"
5261
+ "kimi",
5262
+ "grok",
5263
+ "copilot"
4545
5264
  ];
4546
5265
  function asSubscriptionProviderId(id) {
4547
5266
  return VALID_PROVIDER_IDS.includes(id) ? id : null;
@@ -4689,6 +5408,40 @@ function validateKimi(body) {
4689
5408
  copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "deviceId", "lastRefreshedAt", "errorMessage"]);
4690
5409
  return out;
4691
5410
  }
5411
+ function validateGrok(body) {
5412
+ const authMethod = str(body["authMethod"]);
5413
+ const status = str(body["status"]);
5414
+ if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
5415
+ if (!status || !TOKEN_STATUSES.has(status)) return null;
5416
+ const out = {
5417
+ authMethod,
5418
+ status
5419
+ };
5420
+ copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "lastRefreshedAt", "errorMessage"]);
5421
+ return out;
5422
+ }
5423
+ function validateCopilot(body) {
5424
+ const authMethod = str(body["authMethod"]);
5425
+ const status = str(body["status"]);
5426
+ if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
5427
+ if (!status || !TOKEN_STATUSES.has(status)) return null;
5428
+ const out = {
5429
+ authMethod,
5430
+ status
5431
+ };
5432
+ copyOptional(out, body, [
5433
+ "accessToken",
5434
+ "refreshToken",
5435
+ "expiresAt",
5436
+ "accountId",
5437
+ "email",
5438
+ "apiEndpoint",
5439
+ "enterpriseUrl",
5440
+ "lastRefreshedAt",
5441
+ "errorMessage"
5442
+ ]);
5443
+ return out;
5444
+ }
4692
5445
  function validateOpenCodeGo(body) {
4693
5446
  const authMethod = str(body["authMethod"]);
4694
5447
  const status = str(body["status"]);
@@ -4726,6 +5479,10 @@ function validateTokenBody(providerId, body) {
4726
5479
  return validateOpenCodeGo(body);
4727
5480
  case "kimi":
4728
5481
  return validateKimi(body);
5482
+ case "grok":
5483
+ return validateGrok(body);
5484
+ case "copilot":
5485
+ return validateCopilot(body);
4729
5486
  default:
4730
5487
  return null;
4731
5488
  }
@@ -4755,12 +5512,12 @@ async function statusEntryFor(reader, providerId) {
4755
5512
 
4756
5513
  // src/admin/accountsOAuth.ts
4757
5514
  var OAUTH_HTTP_PROVIDERS = /* @__PURE__ */ new Set(["claude", "gemini"]);
4758
- function err3(status, message) {
5515
+ function err5(status, message) {
4759
5516
  return { status, body: { error: { type: "admin_api_error", message } } };
4760
5517
  }
4761
5518
  function handleOAuthStart(providerId, deps) {
4762
5519
  if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
4763
- return err3(400, `oauth not available for provider '${providerId}'`);
5520
+ return err5(400, `oauth not available for provider '${providerId}'`);
4764
5521
  }
4765
5522
  const flow = providerId === "claude" ? claudeOAuth : geminiOAuth;
4766
5523
  const { authUrl, codeVerifier, state } = flow.generateAuthParams();
@@ -4769,23 +5526,23 @@ function handleOAuthStart(providerId, deps) {
4769
5526
  }
4770
5527
  async function handleOAuthComplete(providerId, body, deps) {
4771
5528
  if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
4772
- return err3(400, `oauth not available for provider '${providerId}'`);
5529
+ return err5(400, `oauth not available for provider '${providerId}'`);
4773
5530
  }
4774
5531
  const sessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : "";
4775
5532
  const rawCode = typeof body["code"] === "string" ? body["code"] : "";
4776
- if (!sessionId) return err3(400, "oauth complete requires { sessionId }");
4777
- if (!rawCode) return err3(400, "oauth complete requires { code }");
5533
+ if (!sessionId) return err5(400, "oauth complete requires { sessionId }");
5534
+ if (!rawCode) return err5(400, "oauth complete requires { code }");
4778
5535
  const session = deps.oauthSessions.peek(sessionId);
4779
- if (!session) return err3(410, "oauth session is unknown, expired, or already used");
5536
+ if (!session) return err5(410, "oauth session is unknown, expired, or already used");
4780
5537
  if (session.providerId !== providerId) {
4781
- return err3(400, `oauth session does not match provider '${providerId}'`);
5538
+ return err5(400, `oauth session does not match provider '${providerId}'`);
4782
5539
  }
4783
5540
  let code = rawCode.trim();
4784
5541
  if (providerId === "claude") {
4785
5542
  const [splitCode, pastedState] = code.split("#");
4786
- if (!splitCode) return err3(400, "no authorization code was provided");
5543
+ if (!splitCode) return err5(400, "no authorization code was provided");
4787
5544
  if (pastedState && pastedState !== session.state) {
4788
- return err3(400, "oauth state did not match (possible CSRF) \u2014 aborting");
5545
+ return err5(400, "oauth state did not match (possible CSRF) \u2014 aborting");
4789
5546
  }
4790
5547
  code = splitCode;
4791
5548
  }
@@ -4795,7 +5552,7 @@ async function handleOAuthComplete(providerId, body, deps) {
4795
5552
  block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
4796
5553
  } catch (exchangeError) {
4797
5554
  const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
4798
- return err3(502, `oauth token exchange failed for '${providerId}': ${reason}`);
5555
+ return err5(502, `oauth token exchange failed for '${providerId}': ${reason}`);
4799
5556
  }
4800
5557
  deps.oauthSessions.consume(sessionId);
4801
5558
  const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
@@ -5133,8 +5890,8 @@ function errBody(message) {
5133
5890
  return { error: { type: "admin_api_error", message } };
5134
5891
  }
5135
5892
  var defaultCommandRunner = (command) => new Promise((resolve11) => {
5136
- exec(command, { timeout: 18e4 }, (err6, _stdout, stderr) => {
5137
- if (err6) resolve11({ ok: false, error: stderr.trim() || err6.message });
5893
+ exec(command, { timeout: 18e4 }, (err8, _stdout, stderr) => {
5894
+ if (err8) resolve11({ ok: false, error: stderr.trim() || err8.message });
5138
5895
  else resolve11({ ok: true });
5139
5896
  });
5140
5897
  });
@@ -5180,8 +5937,8 @@ async function handleCliLaunch(cli, body, ctx) {
5180
5937
  providerId: typeof body["providerId"] === "string" ? body["providerId"] : void 0,
5181
5938
  model: typeof body["model"] === "string" ? body["model"] : void 0
5182
5939
  });
5183
- } catch (err6) {
5184
- return { status: 400, body: errBody(err6 instanceof Error ? err6.message : "no launch target") };
5940
+ } catch (err8) {
5941
+ return { status: 400, body: errBody(err8 instanceof Error ? err8.message : "no launch target") };
5185
5942
  }
5186
5943
  const id = randomUUID2();
5187
5944
  let leaseId2;
@@ -5209,9 +5966,9 @@ async function handleCliLaunch(cli, body, ctx) {
5209
5966
  } else {
5210
5967
  launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
5211
5968
  }
5212
- } catch (err6) {
5213
- const status = err6 instanceof RouteLeaseError2 ? err6.status : 400;
5214
- return { status, body: errBody(err6 instanceof Error ? err6.message : "failed to build launch env") };
5969
+ } catch (err8) {
5970
+ const status = err8 instanceof RouteLeaseError2 ? err8.status : 400;
5971
+ return { status, body: errBody(err8 instanceof Error ? err8.message : "failed to build launch env") };
5215
5972
  }
5216
5973
  const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
5217
5974
  const opener = ctx.opener ?? defaultTerminalOpener;
@@ -5239,9 +5996,9 @@ async function handleCliLaunch(cli, body, ctx) {
5239
5996
  onFailure: onSessionEnd
5240
5997
  });
5241
5998
  if (cleanup) openerCleanup = cleanup;
5242
- } catch (err6) {
5999
+ } catch (err8) {
5243
6000
  onSessionEnd();
5244
- return { status: 500, body: errBody(err6 instanceof Error ? err6.message : "failed to open terminal") };
6001
+ return { status: 500, body: errBody(err8 instanceof Error ? err8.message : "failed to open terminal") };
5245
6002
  }
5246
6003
  if (ended) {
5247
6004
  openerCleanup?.();
@@ -5804,7 +6561,7 @@ async function handleSearchQuery(req, res, deps) {
5804
6561
  // src/admin/searchAdminView.ts
5805
6562
  var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
5806
6563
  var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
5807
- function isRecord2(value) {
6564
+ function isRecord4(value) {
5808
6565
  return value !== null && typeof value === "object" && !Array.isArray(value);
5809
6566
  }
5810
6567
  function redactSearchServerConfig(search) {
@@ -5854,13 +6611,13 @@ function resolveSecretField(entry, field, stored) {
5854
6611
  else delete entry[field];
5855
6612
  }
5856
6613
  function preserveSearchSecrets(incoming, current) {
5857
- if (!isRecord2(incoming)) return incoming;
6614
+ if (!isRecord4(incoming)) return incoming;
5858
6615
  const section = { ...incoming };
5859
6616
  const providersValue = section["providers"];
5860
- if (!isRecord2(providersValue)) return section;
6617
+ if (!isRecord4(providersValue)) return section;
5861
6618
  const providers = {};
5862
6619
  for (const [id, entryValue] of Object.entries(providersValue)) {
5863
- if (!isRecord2(entryValue)) {
6620
+ if (!isRecord4(entryValue)) {
5864
6621
  providers[id] = entryValue;
5865
6622
  continue;
5866
6623
  }
@@ -5938,7 +6695,7 @@ function parseKeyPolicyBody(body) {
5938
6695
  var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
5939
6696
  var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
5940
6697
  var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
5941
- function isRecord3(value) {
6698
+ function isRecord5(value) {
5942
6699
  return !!value && typeof value === "object" && !Array.isArray(value);
5943
6700
  }
5944
6701
  function nonBlank(value) {
@@ -5958,7 +6715,7 @@ function validateGatewayBindingsSegment(patch) {
5958
6715
  const ids = /* @__PURE__ */ new Set();
5959
6716
  raw.forEach((entry, index) => {
5960
6717
  const path2 = `bindings[${index}]`;
5961
- if (!isRecord3(entry)) {
6718
+ if (!isRecord5(entry)) {
5962
6719
  errors.push(`${path2} must be an object`);
5963
6720
  return;
5964
6721
  }
@@ -5987,12 +6744,12 @@ function validateGatewayBindingsSegment(patch) {
5987
6744
  } else if (entry.modelMappings.length > 100) {
5988
6745
  errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
5989
6746
  } else if (entry.modelMappings.some(
5990
- (mapping) => !isRecord3(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
6747
+ (mapping) => !isRecord5(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
5991
6748
  )) {
5992
6749
  errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
5993
6750
  }
5994
6751
  }
5995
- if (!isRecord3(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
6752
+ if (!isRecord5(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
5996
6753
  errors.push(`${path2}.target is invalid`);
5997
6754
  } else {
5998
6755
  if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
@@ -6007,7 +6764,7 @@ function validateGatewayBindingsSegment(patch) {
6007
6764
  }
6008
6765
  }
6009
6766
  if (entry.modelMap !== void 0) {
6010
- if (!isRecord3(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
6767
+ if (!isRecord5(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
6011
6768
  errors.push(`${path2}.modelMap must contain string values`);
6012
6769
  }
6013
6770
  }
@@ -6302,7 +7059,9 @@ var PROVIDER_KEYS = {
6302
7059
  accounts: "opencodegoAccounts",
6303
7060
  active: "activeOpencodegoAccountId"
6304
7061
  },
6305
- kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" }
7062
+ kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" },
7063
+ grok: { block: "grok", accounts: "grokAccounts", active: "activeGrokAccountId" },
7064
+ copilot: { block: "copilot", accounts: "copilotAccounts", active: "activeCopilotAccountId" }
6306
7065
  };
6307
7066
  function clone(value) {
6308
7067
  return JSON.parse(JSON.stringify(value));
@@ -6824,7 +7583,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
6824
7583
  }
6825
7584
 
6826
7585
  // src/admin/adminMigration.ts
6827
- function err4(status, message) {
7586
+ function err6(status, message) {
6828
7587
  return { status, body: { error: { type: "admin_api_error", message } } };
6829
7588
  }
6830
7589
  async function handleExport(body, deps) {
@@ -6834,30 +7593,30 @@ async function handleExport(body, deps) {
6834
7593
  return { status: 200, body: { pack, version: BUNDLE_VERSION } };
6835
7594
  } catch (error) {
6836
7595
  if (error instanceof WeakPassphraseError) {
6837
- return err4(400, error.message);
7596
+ return err6(400, error.message);
6838
7597
  }
6839
- return err4(500, "failed to build the migration pack");
7598
+ return err6(500, "failed to build the migration pack");
6840
7599
  }
6841
7600
  }
6842
7601
  async function handleImport(body, deps) {
6843
7602
  const blob = typeof body["blob"] === "string" ? body["blob"] : "";
6844
7603
  const passphrase = typeof body["passphrase"] === "string" ? body["passphrase"] : "";
6845
7604
  const mode = body["mode"] === "overwrite" ? "overwrite" : "merge";
6846
- if (!blob) return err4(400, "import requires { blob }");
7605
+ if (!blob) return err6(400, "import requires { blob }");
6847
7606
  try {
6848
7607
  const counts = await applyImport(blob, passphrase, mode, deps, deps.parseProviderInput);
6849
7608
  return { status: 200, body: counts };
6850
7609
  } catch (error) {
6851
7610
  if (error instanceof WeakPassphraseError) {
6852
- return err4(400, error.message);
7611
+ return err6(400, error.message);
6853
7612
  }
6854
- return err4(400, error instanceof Error ? error.message : "import failed");
7613
+ return err6(400, error instanceof Error ? error.message : "import failed");
6855
7614
  }
6856
7615
  }
6857
7616
 
6858
7617
  // src/admin/usagePricing.ts
6859
7618
  import { getSharedUsageThroughputTracker } from "@omnicross/core/usage";
6860
- var err5 = (status, message) => ({
7619
+ var err7 = (status, message) => ({
6861
7620
  status,
6862
7621
  body: { error: { type: "admin_api_error", message } }
6863
7622
  });
@@ -6870,7 +7629,7 @@ function parseRange(query2) {
6870
7629
  const startTs = parseFiniteInt(query2.get("startTs"));
6871
7630
  const endTs = parseFiniteInt(query2.get("endTs"));
6872
7631
  if (startTs === null || endTs === null) {
6873
- return err5(400, "startTs and endTs are required finite-integer unix-millis query params");
7632
+ return err7(400, "startTs and endTs are required finite-integer unix-millis query params");
6874
7633
  }
6875
7634
  return { startTs, endTs };
6876
7635
  }
@@ -6895,14 +7654,14 @@ async function handleUsageGet(view, query2, deps) {
6895
7654
  case "timeseries": {
6896
7655
  const bucket = query2.get("bucket");
6897
7656
  if (bucket !== "hour" && bucket !== "day" && bucket !== "month") {
6898
- return err5(400, "bucket must be one of 'hour', 'day', 'month'");
7657
+ return err7(400, "bucket must be one of 'hour', 'day', 'month'");
6899
7658
  }
6900
7659
  const now = Date.now();
6901
7660
  const clamped = { startTs: range.startTs, endTs: Math.min(range.endTs, now) };
6902
7661
  if (clamped.startTs < clamped.endTs) {
6903
7662
  const projected = Math.ceil((clamped.endTs - clamped.startTs) / BUCKET_SPAN_MS[bucket]) + 1;
6904
7663
  if (projected > MAX_TIMESERIES_BUCKETS) {
6905
- return err5(
7664
+ return err7(
6906
7665
  400,
6907
7666
  `requested range projects ~${projected} '${bucket}' buckets (max ${MAX_TIMESERIES_BUCKETS}); narrow the range or use a coarser bucket`
6908
7667
  );
@@ -6925,7 +7684,7 @@ async function handleUsageGet(view, query2, deps) {
6925
7684
  };
6926
7685
  }
6927
7686
  default:
6928
- return err5(404, `unknown usage view '${view ?? ""}'`);
7687
+ return err7(404, `unknown usage view '${view ?? ""}'`);
6929
7688
  }
6930
7689
  }
6931
7690
  function poolKeyLabels(cfg) {
@@ -6974,7 +7733,7 @@ async function handlePricingList(deps) {
6974
7733
  async function handlePricingUpsert(body, deps) {
6975
7734
  const input = parsePricingEntryInput(body);
6976
7735
  if (!input) {
6977
- return err5(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
7736
+ return err7(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
6978
7737
  }
6979
7738
  const entry = await deps.pricingEngine.upsertManual(input);
6980
7739
  return { status: 200, body: { entry } };
@@ -6983,7 +7742,7 @@ async function handlePricingDelete(query2, deps) {
6983
7742
  const providerId = query2.get("providerId")?.trim() ?? "";
6984
7743
  const modelId = query2.get("modelId")?.trim() ?? "";
6985
7744
  if (!providerId || !modelId) {
6986
- return err5(400, "delete requires providerId and modelId query params");
7745
+ return err7(400, "delete requires providerId and modelId query params");
6987
7746
  }
6988
7747
  const deleted = await deps.pricingStore.delete(providerId, modelId);
6989
7748
  if (deleted) await deps.pricingEngine.invalidateCache();
@@ -7003,13 +7762,13 @@ async function handlePricingFetchLatest(deps) {
7003
7762
  }
7004
7763
  };
7005
7764
  } catch (e) {
7006
- return err5(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
7765
+ return err7(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
7007
7766
  }
7008
7767
  }
7009
7768
  async function handlePricingResolveConflicts(body, deps) {
7010
7769
  const raw = body["resolutions"];
7011
7770
  if (!Array.isArray(raw)) {
7012
- return err5(400, "resolve-conflicts requires { resolutions: [...] }");
7771
+ return err7(400, "resolve-conflicts requires { resolutions: [...] }");
7013
7772
  }
7014
7773
  const currentRows = await deps.pricingStore.getAll();
7015
7774
  const userEditedKeys = new Set(
@@ -7019,21 +7778,21 @@ async function handlePricingResolveConflicts(body, deps) {
7019
7778
  const pendingIncoming = /* @__PURE__ */ new Map();
7020
7779
  let staleCount = 0;
7021
7780
  for (const item of raw) {
7022
- if (!item || typeof item !== "object") return err5(400, "invalid resolution entry");
7781
+ if (!item || typeof item !== "object") return err7(400, "invalid resolution entry");
7023
7782
  const r = item;
7024
7783
  const action = r["action"];
7025
7784
  if (action !== "overwrite" && action !== "skip") {
7026
- return err5(400, "resolution action must be 'overwrite' or 'skip'");
7785
+ return err7(400, "resolution action must be 'overwrite' or 'skip'");
7027
7786
  }
7028
7787
  const providerId = typeof r["providerId"] === "string" && r["providerId"].trim() ? r["providerId"].trim() : "";
7029
7788
  const modelId = typeof r["modelId"] === "string" && r["modelId"].trim() ? r["modelId"].trim() : "";
7030
7789
  if (!providerId || !modelId) {
7031
- return err5(400, "each resolution requires top-level providerId and modelId");
7790
+ return err7(400, "each resolution requires top-level providerId and modelId");
7032
7791
  }
7033
7792
  const incoming = parsePricingEntryInput(r["incoming"]);
7034
- if (!incoming) return err5(400, "each resolution must echo a valid incoming pricing entry");
7793
+ if (!incoming) return err7(400, "each resolution must echo a valid incoming pricing entry");
7035
7794
  if (incoming.providerId !== providerId || incoming.modelId !== modelId) {
7036
- return err5(400, "resolution providerId/modelId must match the echoed incoming entry");
7795
+ return err7(400, "resolution providerId/modelId must match the echoed incoming entry");
7037
7796
  }
7038
7797
  const key = `${providerId}::${modelId}`;
7039
7798
  if (action === "overwrite" && !userEditedKeys.has(key)) {
@@ -7078,7 +7837,7 @@ function query(req) {
7078
7837
  }
7079
7838
  function allowanceProvider(value) {
7080
7839
  if (!value) return void 0;
7081
- return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" ? value : null;
7840
+ return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" || value === "grok" || value === "copilot" ? value : null;
7082
7841
  }
7083
7842
  async function handleAccountAllowanceApi(req, res, method, rest, service) {
7084
7843
  if (!service) return writeError2(res, 501, "account allowance service is not available");
@@ -7093,7 +7852,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
7093
7852
  const pathProvider = rest.length >= 2 ? rest[0] : null;
7094
7853
  const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
7095
7854
  if (providerId === null) {
7096
- return writeError2(res, 400, "providerId must be claude, codex, kimi, or opencodego");
7855
+ return writeError2(res, 400, "providerId must be claude, codex, kimi, opencodego, grok, or copilot");
7097
7856
  }
7098
7857
  const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
7099
7858
  const allowances = await service.list({ providerId, accountId });
@@ -7135,6 +7894,26 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
7135
7894
  }
7136
7895
  return writeJson3(res, 200, { allowances: allowances2 });
7137
7896
  }
7897
+ if (requestedProvider === "copilot") {
7898
+ if (!service.refreshCopilot) {
7899
+ return writeError2(res, 501, "copilot allowance refresh is not available");
7900
+ }
7901
+ const allowances2 = await service.refreshCopilot(accountId);
7902
+ if (accountId && allowances2.length === 0) {
7903
+ return writeError2(res, 404, `Copilot account '${accountId}' not found`);
7904
+ }
7905
+ return writeJson3(res, 200, { allowances: allowances2 });
7906
+ }
7907
+ if (requestedProvider === "grok") {
7908
+ if (!service.refreshGrok) {
7909
+ return writeError2(res, 501, "grok allowance refresh is not available");
7910
+ }
7911
+ const allowances2 = await service.refreshGrok(accountId);
7912
+ if (accountId && allowances2.length === 0) {
7913
+ return writeError2(res, 404, `Grok account '${accountId}' not found`);
7914
+ }
7915
+ return writeJson3(res, 200, { allowances: allowances2 });
7916
+ }
7138
7917
  const allowances = await service.refreshClaude(accountId);
7139
7918
  if (accountId && allowances.length === 0) {
7140
7919
  return writeError2(res, 404, `Claude account '${accountId}' not found`);
@@ -7236,6 +8015,9 @@ function toProviderView(row) {
7236
8015
  apiVersion: row.apiVersion,
7237
8016
  maxConcurrency: row.maxConcurrency,
7238
8017
  modelsEndpoint: row.modelsEndpoint,
8018
+ // Static extra headers round-trip VERBATIM (non-secret identity values;
8019
+ // auth/content names were already dropped at the write/load gate).
8020
+ extraHeaders: row.extraHeaders,
7239
8021
  // app-parity child 5: transformer config round-trips VERBATIM (non-secret —
7240
8022
  // transform-rule names + options, no key material; absent stays absent).
7241
8023
  transformer: row.transformer,
@@ -7305,8 +8087,8 @@ async function handleAdminApi(req, res, path2, deps) {
7305
8087
  default:
7306
8088
  return writeJsonError(res, 404, `unknown admin resource '${resource}'`);
7307
8089
  }
7308
- } catch (err6) {
7309
- writeJsonError(res, 500, err6 instanceof Error ? err6.message : String(err6));
8090
+ } catch (err8) {
8091
+ writeJsonError(res, 500, err8 instanceof Error ? err8.message : String(err8));
7310
8092
  }
7311
8093
  }
7312
8094
  function requestQuery(req) {
@@ -7464,6 +8246,9 @@ async function handleProviderReorder(req, res, cfg, deps) {
7464
8246
  persistProviders(cfg, deps);
7465
8247
  return writeJson4(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
7466
8248
  }
8249
+ function expandRowExtraHeaders(row) {
8250
+ return mergeExtraHeaders({}, row.extraHeaders);
8251
+ }
7467
8252
  async function handleDiscoverModels(res, id, cfg) {
7468
8253
  if (!id) return writeJsonError(res, 400, "provider id required in path");
7469
8254
  const row = cfg.providers.find((p) => p.id === id);
@@ -7477,7 +8262,8 @@ async function handleDiscoverModels(res, id, cfg) {
7477
8262
  try {
7478
8263
  const headers = { Accept: "application/json" };
7479
8264
  if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
7480
- const response = await fetchUpstream5(url, { method: "GET", headers }, { providerId: "byo" });
8265
+ Object.assign(headers, expandRowExtraHeaders(row));
8266
+ const response = await fetchUpstream7(url, { method: "GET", headers }, { providerId: "byo" });
7481
8267
  if (!response.ok) {
7482
8268
  const text = await response.text().catch(() => "");
7483
8269
  let message = text.slice(0, 300);
@@ -7494,8 +8280,8 @@ async function handleDiscoverModels(res, id, cfg) {
7494
8280
  const data = await response.json();
7495
8281
  const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
7496
8282
  return writeJson4(res, 200, { models });
7497
- } catch (err6) {
7498
- const message = err6 instanceof Error ? err6.message : String(err6);
8283
+ } catch (err8) {
8284
+ const message = err8 instanceof Error ? err8.message : String(err8);
7499
8285
  return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
7500
8286
  }
7501
8287
  }
@@ -7534,9 +8320,10 @@ async function handleTestModel(req, res, id, cfg) {
7534
8320
  messages: [{ role: "user", content: prompt }]
7535
8321
  };
7536
8322
  }
8323
+ Object.assign(headers, expandRowExtraHeaders(row));
7537
8324
  const startedAt = Date.now();
7538
8325
  try {
7539
- const response = await fetchUpstream5(
8326
+ const response = await fetchUpstream7(
7540
8327
  url,
7541
8328
  { method: "POST", headers, body: JSON.stringify(payload) },
7542
8329
  { providerId: "byo" }
@@ -7558,8 +8345,8 @@ async function handleTestModel(req, res, id, cfg) {
7558
8345
  latencyMs,
7559
8346
  sample: extractSampleText(text, row.apiFormat)
7560
8347
  });
7561
- } catch (err6) {
7562
- const message = err6 instanceof Error ? err6.message : String(err6);
8348
+ } catch (err8) {
8349
+ const message = err8 instanceof Error ? err8.message : String(err8);
7563
8350
  return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
7564
8351
  }
7565
8352
  }
@@ -7841,6 +8628,7 @@ function parseProviderInput(body, existing) {
7841
8628
  const apiVersion = typeof body["apiVersion"] === "string" && body["apiVersion"].length > 0 ? body["apiVersion"] : body["apiVersion"] === null ? void 0 : existing?.apiVersion;
7842
8629
  const modelsEndpoint = typeof body["modelsEndpoint"] === "string" && body["modelsEndpoint"].length > 0 ? body["modelsEndpoint"] : body["modelsEndpoint"] === null ? void 0 : existing?.modelsEndpoint;
7843
8630
  const maxConcurrency = typeof body["maxConcurrency"] === "number" && Number.isFinite(body["maxConcurrency"]) ? body["maxConcurrency"] : body["maxConcurrency"] === null ? void 0 : existing?.maxConcurrency;
8631
+ const extraHeaders = body["extraHeaders"] === null ? void 0 : body["extraHeaders"] === void 0 ? existing?.extraHeaders : validateExtraHeaders(body["extraHeaders"]);
7844
8632
  const transformer = body["transformer"] === null ? void 0 : parseTransformerInput(body["transformer"], existing?.transformer);
7845
8633
  const codingPlan = body["codingPlan"] === null ? void 0 : body["codingPlan"] === void 0 ? existing?.codingPlan : body["codingPlan"] && typeof body["codingPlan"] === "object" && !Array.isArray(body["codingPlan"]) ? parseCodingPlanInput(body["codingPlan"], existing?.codingPlan) : existing?.codingPlan;
7846
8634
  const apiModes = body["apiModes"] === null ? void 0 : parseApiModesInput(body["apiModes"], existing?.apiModes);
@@ -7866,6 +8654,7 @@ function parseProviderInput(body, existing) {
7866
8654
  apiVersion,
7867
8655
  maxConcurrency,
7868
8656
  modelsEndpoint,
8657
+ extraHeaders,
7869
8658
  transformer: migrated.transformer,
7870
8659
  codingPlan,
7871
8660
  apiModes,
@@ -7887,7 +8676,10 @@ function handlePresets(res, method) {
7887
8676
  description: p.description,
7888
8677
  features: p.features,
7889
8678
  website: p.website,
7890
- modelsEndpoint: p.modelsEndpoint
8679
+ modelsEndpoint: p.modelsEndpoint,
8680
+ // Static extra headers ride along so `addFromPreset` can seed them onto the
8681
+ // row (the write gateway re-validates via the shared allowlist).
8682
+ extraHeaders: p.extraHeaders
7891
8683
  }));
7892
8684
  return writeJson4(res, 200, { presets, excluded });
7893
8685
  }
@@ -8369,12 +9161,12 @@ async function handleAccounts(req, res, method, rest, deps) {
8369
9161
  }
8370
9162
  return writeJson4(res, 200, { ok: true, affected: result.affected });
8371
9163
  }
8372
- if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi") && rest[1] === "oauth" && rest[3] === "status") {
8373
- const result = rest[0] === "codex" ? handleCodexOAuthStatus(rest[2], deps) : handleKimiOAuthStatus(rest[2], deps);
9164
+ if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[3] === "status") {
9165
+ const result = rest[0] === "codex" ? handleCodexOAuthStatus(rest[2], deps) : rest[0] === "kimi" ? handleKimiOAuthStatus(rest[2], deps) : rest[0] === "grok" ? handleGrokOAuthStatus(rest[2], deps) : handleCopilotOAuthStatus(rest[2], deps);
8374
9166
  return writeJson4(res, result.status, result.body);
8375
9167
  }
8376
- if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi") && rest[1] === "oauth" && rest[2]) {
8377
- const result = rest[0] === "codex" ? handleCodexOAuthCancel(rest[2], deps) : handleKimiOAuthCancel(rest[2], deps);
9168
+ if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[2]) {
9169
+ const result = rest[0] === "codex" ? handleCodexOAuthCancel(rest[2], deps) : rest[0] === "kimi" ? handleKimiOAuthCancel(rest[2], deps) : rest[0] === "grok" ? handleGrokOAuthCancel(rest[2], deps) : handleCopilotOAuthCancel(rest[2], deps);
8378
9170
  return writeJson4(res, result.status, result.body);
8379
9171
  }
8380
9172
  if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
@@ -8435,6 +9227,15 @@ async function handleAccounts(req, res, method, rest, deps) {
8435
9227
  const result2 = await handleKimiOAuthStart(deps);
8436
9228
  return writeJson4(res, result2.status, result2.body);
8437
9229
  }
9230
+ if (providerId === "grok") {
9231
+ const result2 = await handleGrokOAuthStart(deps);
9232
+ return writeJson4(res, result2.status, result2.body);
9233
+ }
9234
+ if (providerId === "copilot") {
9235
+ const body2 = await readJsonBody4(req);
9236
+ const result2 = await handleCopilotOAuthStart(deps, body2["enterpriseUrl"]);
9237
+ return writeJson4(res, result2.status, result2.body);
9238
+ }
8438
9239
  const result = handleOAuthStart(providerId, deps);
8439
9240
  return writeJson4(res, result.status, result.body);
8440
9241
  }
@@ -8929,12 +9730,12 @@ async function handlePlayground(req, res, method, deps) {
8929
9730
  const payload = body["body"];
8930
9731
  const status = deps.outboundApiServer.getStatus();
8931
9732
  if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
8932
- const path2 = resolvePlaygroundPath(endpoint, isRecord4(payload) ? payload : {});
9733
+ const path2 = resolvePlaygroundPath(endpoint, isRecord6(payload) ? payload : {});
8933
9734
  if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
8934
9735
  const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
8935
9736
  await proxyToOutbound(res, status.port, path2, key, upstreamBody);
8936
9737
  }
8937
- function isRecord4(v) {
9738
+ function isRecord6(v) {
8938
9739
  return !!v && typeof v === "object" && !Array.isArray(v);
8939
9740
  }
8940
9741
  function proxyToOutbound(res, outboundPort, path2, key, body) {
@@ -8963,8 +9764,8 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
8963
9764
  });
8964
9765
  }
8965
9766
  );
8966
- upstream.on("error", (err6) => {
8967
- if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err6.message}`);
9767
+ upstream.on("error", (err8) => {
9768
+ if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err8.message}`);
8968
9769
  else res.end();
8969
9770
  resolve11();
8970
9771
  });
@@ -9069,7 +9870,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
9069
9870
  }
9070
9871
 
9071
9872
  // src/admin/version.ts
9072
- var DAEMON_VERSION = true ? "0.3.1" : "0.0.0-dev";
9873
+ var DAEMON_VERSION = true ? "0.4.0" : "0.0.0-dev";
9073
9874
 
9074
9875
  // src/admin/AdminServer.ts
9075
9876
  var LOOPBACK_ADDR = "127.0.0.1";
@@ -9112,13 +9913,13 @@ var AdminServer = class {
9112
9913
  const server = http2.createServer((req, res) => {
9113
9914
  this.onRequest(req, res);
9114
9915
  });
9115
- const onError = (err6) => {
9116
- if (err6.code === "EADDRINUSE" && port !== 0) {
9916
+ const onError = (err8) => {
9917
+ if (err8.code === "EADDRINUSE" && port !== 0) {
9117
9918
  server.removeListener("error", onError);
9118
9919
  this.listen(bindAddr, 0).then(resolve11, reject);
9119
9920
  return;
9120
9921
  }
9121
- reject(err6);
9922
+ reject(err8);
9122
9923
  };
9123
9924
  server.on("error", onError);
9124
9925
  server.listen(port, bindAddr, () => {
@@ -9136,8 +9937,8 @@ var AdminServer = class {
9136
9937
  }
9137
9938
  /** Per-request handler: auth gate (when a token is set) → routing. */
9138
9939
  onRequest(req, res) {
9139
- void this.dispatch(req, res).catch((err6) => {
9140
- const message = err6 instanceof Error ? err6.message : String(err6);
9940
+ void this.dispatch(req, res).catch((err8) => {
9941
+ const message = err8 instanceof Error ? err8.message : String(err8);
9141
9942
  this.deps.logger.error("[AdminServer] unhandled error:", message);
9142
9943
  if (!res.headersSent) {
9143
9944
  res.writeHead(500, { "Content-Type": "application/json" });
@@ -9401,18 +10202,18 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
9401
10202
  return;
9402
10203
  }
9403
10204
  signal?.addEventListener("abort", abort, { once: true });
9404
- server.on("error", (err6) => {
10205
+ server.on("error", (err8) => {
9405
10206
  if (settled) return;
9406
10207
  settled = true;
9407
10208
  clearTimeout(timer);
9408
- if (err6.code === "EADDRINUSE") {
10209
+ if (err8.code === "EADDRINUSE") {
9409
10210
  reject(
9410
10211
  new Error(
9411
10212
  `login: cannot bind ${LOOPBACK_HOST}:${LOOPBACK_PORT} (address in use) \u2014 another codex login or process is holding the port`
9412
10213
  )
9413
10214
  );
9414
10215
  } else {
9415
- reject(err6);
10216
+ reject(err8);
9416
10217
  }
9417
10218
  });
9418
10219
  const timer = setTimeout(() => {
@@ -9488,21 +10289,22 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
9488
10289
  }
9489
10290
 
9490
10291
  // src/allowance/ProviderKeyQuotaService.ts
9491
- import { fetchUpstream as fetchUpstream6 } from "@omnicross/core/pipeline/upstreamFetch";
10292
+ import { mergeExtraHeaders as mergeExtraHeaders2 } from "@omnicross/core";
10293
+ import { fetchUpstream as fetchUpstream8 } from "@omnicross/core/pipeline/upstreamFetch";
9492
10294
 
9493
10295
  // src/allowance/ProviderKeyQuota.ts
9494
- var MINUTE_MS2 = 6e4;
9495
- var HOUR_MS2 = 60 * MINUTE_MS2;
9496
- var DAY_MS2 = 24 * HOUR_MS2;
9497
- var WEEK_MS = 7 * DAY_MS2;
9498
- var MONTH_MS = 30 * DAY_MS2;
9499
- function finiteNumber3(value) {
10296
+ var MINUTE_MS3 = 6e4;
10297
+ var HOUR_MS2 = 60 * MINUTE_MS3;
10298
+ var DAY_MS3 = 24 * HOUR_MS2;
10299
+ var WEEK_MS = 7 * DAY_MS3;
10300
+ var MONTH_MS = 30 * DAY_MS3;
10301
+ function finiteNumber5(value) {
9500
10302
  if (value === null || value === void 0 || value === "") return void 0;
9501
10303
  const parsed = typeof value === "number" ? value : Number(value);
9502
10304
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
9503
10305
  }
9504
10306
  function finitePercent4(value) {
9505
- const parsed = finiteNumber3(value);
10307
+ const parsed = finiteNumber5(value);
9506
10308
  return parsed !== void 0 && parsed <= 100 ? parsed : null;
9507
10309
  }
9508
10310
  function isoInstant3(value) {
@@ -9510,18 +10312,18 @@ function isoInstant3(value) {
9510
10312
  const time = Date.parse(value);
9511
10313
  if (Number.isFinite(time)) return new Date(time).toISOString();
9512
10314
  }
9513
- const numeric = finiteNumber3(value);
10315
+ const numeric = finiteNumber5(value);
9514
10316
  if (numeric !== void 0 && numeric > 1e9) {
9515
10317
  const ms = numeric > 1e12 ? numeric : numeric * 1e3;
9516
10318
  return new Date(ms).toISOString();
9517
10319
  }
9518
10320
  return void 0;
9519
10321
  }
9520
- function secondsUntil5(instant, now) {
10322
+ function secondsUntil7(instant, now) {
9521
10323
  if (!instant) return void 0;
9522
10324
  return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
9523
10325
  }
9524
- function isRecord5(value) {
10326
+ function isRecord7(value) {
9525
10327
  return !!value && typeof value === "object" && !Array.isArray(value);
9526
10328
  }
9527
10329
  function detectProviderKeyQuotaAdapter(baseUrl) {
@@ -9544,6 +10346,7 @@ function detectProviderKeyQuotaAdapter(baseUrl) {
9544
10346
  }
9545
10347
  if (host === "api.code.umans.ai") return "umans";
9546
10348
  if (host === "api.synthetic.new") return "synthetic";
10349
+ if (host === "api.cline.bot") return "cline-pass";
9547
10350
  return null;
9548
10351
  }
9549
10352
  function providerKeyQuotaUrl(adapter, baseUrl) {
@@ -9551,6 +10354,7 @@ function providerKeyQuotaUrl(adapter, baseUrl) {
9551
10354
  if (adapter === "zai") return `${origin}/api/monitor/usage/quota/limit`;
9552
10355
  if (adapter === "minimax-token-plan") return `${origin}/v1/token_plan/remains`;
9553
10356
  if (adapter === "umans") return `${origin}/v1/usage`;
10357
+ if (adapter === "cline-pass") return `${origin}/api/v1/users/me/plan/usage-limits`;
9554
10358
  return `${origin}/v2/quotas`;
9555
10359
  }
9556
10360
  function providerKeyQuotaAuthHeader(adapter, key) {
@@ -9562,7 +10366,7 @@ function zaiWindowDurationMs(item) {
9562
10366
  case 3:
9563
10367
  return count * HOUR_MS2;
9564
10368
  case 4:
9565
- return count * DAY_MS2;
10369
+ return count * DAY_MS3;
9566
10370
  case 5:
9567
10371
  return count * MONTH_MS;
9568
10372
  case 6:
@@ -9575,8 +10379,8 @@ function zaiWindowIdLabel(durationMs) {
9575
10379
  if (durationMs === WEEK_MS) return { id: "seven-day", label: "7 days" };
9576
10380
  if (durationMs === 5 * HOUR_MS2) return { id: "five-hour", label: "5 hours" };
9577
10381
  if (durationMs === MONTH_MS) return { id: "thirty-day", label: "30 days" };
9578
- if (durationMs !== void 0 && durationMs % DAY_MS2 === 0) {
9579
- const days = durationMs / DAY_MS2;
10382
+ if (durationMs !== void 0 && durationMs % DAY_MS3 === 0) {
10383
+ const days = durationMs / DAY_MS3;
9580
10384
  return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}` };
9581
10385
  }
9582
10386
  if (durationMs !== void 0 && durationMs % HOUR_MS2 === 0) {
@@ -9586,23 +10390,23 @@ function zaiWindowIdLabel(durationMs) {
9586
10390
  return { id: "quota", label: "Quota" };
9587
10391
  }
9588
10392
  function parseZaiQuotaPayload(payload, now) {
9589
- if (!isRecord5(payload)) return null;
9590
- const data = isRecord5(payload["data"]) ? payload["data"] : payload;
10393
+ if (!isRecord7(payload)) return null;
10394
+ const data = isRecord7(payload["data"]) ? payload["data"] : payload;
9591
10395
  if (payload["success"] === false) return null;
9592
10396
  const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
9593
10397
  const byWindow = /* @__PURE__ */ new Map();
9594
10398
  for (const raw of limits) {
9595
- if (!isRecord5(raw)) continue;
10399
+ if (!isRecord7(raw)) continue;
9596
10400
  const item = raw;
9597
10401
  if (item.type === void 0) continue;
9598
10402
  const details = raw["usageDetails"];
9599
- if (Array.isArray(details) && details.some((d) => isRecord5(d) && d["modelCode"] === "zread")) {
10403
+ if (Array.isArray(details) && details.some((d) => isRecord7(d) && d["modelCode"] === "zread")) {
9600
10404
  continue;
9601
10405
  }
9602
10406
  const durationMs = zaiWindowDurationMs(item);
9603
10407
  const { id, label } = zaiWindowIdLabel(durationMs);
9604
- const limit = finiteNumber3(item.usage);
9605
- const used = finiteNumber3(item.currentValue);
10408
+ const limit = finiteNumber5(item.usage);
10409
+ const used = finiteNumber5(item.currentValue);
9606
10410
  const fromAbsolute = limit !== void 0 && used !== void 0 && limit > 0 ? Math.min(100, used / limit * 100) : void 0;
9607
10411
  const fromPercentage = finitePercent4(item.percentage) ?? void 0;
9608
10412
  const usedPercent = fromAbsolute !== void 0 ? Math.round(fromAbsolute * 10) / 10 : fromPercentage;
@@ -9613,9 +10417,9 @@ function parseZaiQuotaPayload(payload, now) {
9613
10417
  label,
9614
10418
  scope: "all",
9615
10419
  usedPercent,
9616
- ...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs / MINUTE_MS2) } : {},
10420
+ ...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs / MINUTE_MS3) } : {},
9617
10421
  ...resetsAt !== void 0 ? { resetsAt } : {},
9618
- remainingSeconds: secondsUntil5(resetsAt, now),
10422
+ remainingSeconds: secondsUntil7(resetsAt, now),
9619
10423
  state: "fresh"
9620
10424
  };
9621
10425
  const existing = byWindow.get(id);
@@ -9629,21 +10433,21 @@ function parseZaiQuotaPayload(payload, now) {
9629
10433
  var MINIMAX_STATUS_EXHAUSTED = 2;
9630
10434
  var MINIMAX_SHARED_BUCKET = "general";
9631
10435
  function parseMiniMaxBucket(value) {
9632
- if (!isRecord5(value)) return null;
10436
+ if (!isRecord7(value)) return null;
9633
10437
  const modelName = typeof value["model_name"] === "string" ? value["model_name"].trim() : "";
9634
10438
  if (!modelName) return null;
9635
10439
  const instant = (v) => {
9636
- const n = finiteNumber3(v);
10440
+ const n = finiteNumber5(v);
9637
10441
  return n !== void 0 && n > 1e9 ? n > 1e12 ? n : n * 1e3 : void 0;
9638
10442
  };
9639
10443
  return {
9640
10444
  modelName,
9641
10445
  intervalEnd: instant(value["end_time"]),
9642
- intervalRemainingPercent: finiteNumber3(value["current_interval_remaining_percent"]),
9643
- intervalStatus: finiteNumber3(value["current_interval_status"]),
10446
+ intervalRemainingPercent: finiteNumber5(value["current_interval_remaining_percent"]),
10447
+ intervalStatus: finiteNumber5(value["current_interval_status"]),
9644
10448
  weeklyEnd: instant(value["weekly_end_time"]),
9645
- weeklyRemainingPercent: finiteNumber3(value["current_weekly_remaining_percent"]),
9646
- weeklyStatus: finiteNumber3(value["current_weekly_status"])
10449
+ weeklyRemainingPercent: finiteNumber5(value["current_weekly_remaining_percent"]),
10450
+ weeklyStatus: finiteNumber5(value["current_weekly_status"])
9647
10451
  };
9648
10452
  }
9649
10453
  function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, status, now) {
@@ -9656,14 +10460,14 @@ function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, s
9656
10460
  usedPercent,
9657
10461
  ...windowMinutes !== void 0 ? { windowMinutes } : {},
9658
10462
  ...resetsAt !== void 0 ? { resetsAt } : {},
9659
- remainingSeconds: secondsUntil5(resetsAt, now),
10463
+ remainingSeconds: secondsUntil7(resetsAt, now),
9660
10464
  state: usedPercent !== null ? "fresh" : "unavailable"
9661
10465
  };
9662
10466
  }
9663
10467
  function parseMiniMaxTokenPlanPayload(payload, now) {
9664
- if (!isRecord5(payload)) return null;
10468
+ if (!isRecord7(payload)) return null;
9665
10469
  const baseResp = payload["base_resp"];
9666
- if (!isRecord5(baseResp) || baseResp["status_code"] !== 0) return null;
10470
+ if (!isRecord7(baseResp) || baseResp["status_code"] !== 0) return null;
9667
10471
  const buckets = Array.isArray(payload["model_remains"]) ? payload["model_remains"] : [];
9668
10472
  let general = null;
9669
10473
  for (const raw of buckets) {
@@ -9687,7 +10491,7 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
9687
10491
  minimaxWindow(
9688
10492
  "seven-day",
9689
10493
  "7 days",
9690
- Math.round(WEEK_MS / MINUTE_MS2),
10494
+ Math.round(WEEK_MS / MINUTE_MS3),
9691
10495
  general.weeklyEnd,
9692
10496
  general.weeklyRemainingPercent,
9693
10497
  general.weeklyStatus,
@@ -9696,15 +10500,15 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
9696
10500
  ];
9697
10501
  }
9698
10502
  function parseUmansUsagePayload(payload, now) {
9699
- if (!isRecord5(payload)) return null;
9700
- const limits = isRecord5(payload["limits"]) ? payload["limits"] : void 0;
9701
- const requests = limits && isRecord5(limits["requests"]) ? limits["requests"] : void 0;
9702
- const usage = isRecord5(payload["usage"]) ? payload["usage"] : void 0;
9703
- const window = isRecord5(payload["window"]) ? payload["window"] : void 0;
9704
- const hardCap = finiteNumber3(requests?.["hard_cap"]);
9705
- const softLimit = finiteNumber3(requests?.["limit"]);
9706
- const requestsInWindow = finiteNumber3(usage?.["requests_in_window"]);
9707
- const weightedInWindow = finiteNumber3(usage?.["weighted_in_window"]);
10503
+ if (!isRecord7(payload)) return null;
10504
+ const limits = isRecord7(payload["limits"]) ? payload["limits"] : void 0;
10505
+ const requests = limits && isRecord7(limits["requests"]) ? limits["requests"] : void 0;
10506
+ const usage = isRecord7(payload["usage"]) ? payload["usage"] : void 0;
10507
+ const window = isRecord7(payload["window"]) ? payload["window"] : void 0;
10508
+ const hardCap = finiteNumber5(requests?.["hard_cap"]);
10509
+ const softLimit = finiteNumber5(requests?.["limit"]);
10510
+ const requestsInWindow = finiteNumber5(usage?.["requests_in_window"]);
10511
+ const weightedInWindow = finiteNumber5(usage?.["weighted_in_window"]);
9708
10512
  const resetsAt = isoInstant3(window?.["resets_at"]);
9709
10513
  let usedPercent = null;
9710
10514
  if (hardCap !== void 0 && hardCap > 0 && requestsInWindow !== void 0) {
@@ -9721,19 +10525,19 @@ function parseUmansUsagePayload(payload, now) {
9721
10525
  usedPercent,
9722
10526
  windowMinutes: 5 * 60,
9723
10527
  ...resetsAt !== void 0 ? { resetsAt } : {},
9724
- remainingSeconds: secondsUntil5(resetsAt, now),
10528
+ remainingSeconds: secondsUntil7(resetsAt, now),
9725
10529
  state: "fresh"
9726
10530
  }
9727
10531
  ];
9728
10532
  }
9729
10533
  function parseSyntheticQuotasPayload(payload, now) {
9730
- if (!isRecord5(payload)) return null;
9731
- const fiveHour = isRecord5(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
9732
- const weekly = isRecord5(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
10534
+ if (!isRecord7(payload)) return null;
10535
+ const fiveHour = isRecord7(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
10536
+ const weekly = isRecord7(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
9733
10537
  const windows = [];
9734
10538
  if (fiveHour) {
9735
- const max = finiteNumber3(fiveHour["max"]);
9736
- const remaining = finiteNumber3(fiveHour["remaining"]);
10539
+ const max = finiteNumber5(fiveHour["max"]);
10540
+ const remaining = finiteNumber5(fiveHour["remaining"]);
9737
10541
  const usedPercent = max !== void 0 && max > 0 && remaining !== void 0 ? Math.round(Math.min(100, (max - remaining) / max * 100) * 10) / 10 : null;
9738
10542
  const resetsAt = isoInstant3(fiveHour["nextTickAt"]);
9739
10543
  windows.push({
@@ -9743,12 +10547,12 @@ function parseSyntheticQuotasPayload(payload, now) {
9743
10547
  usedPercent,
9744
10548
  windowMinutes: 5 * 60,
9745
10549
  ...resetsAt !== void 0 ? { resetsAt } : {},
9746
- remainingSeconds: secondsUntil5(resetsAt, now),
10550
+ remainingSeconds: secondsUntil7(resetsAt, now),
9747
10551
  state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
9748
10552
  });
9749
10553
  }
9750
10554
  if (weekly) {
9751
- const percentRemaining = finiteNumber3(weekly["percentRemaining"]);
10555
+ const percentRemaining = finiteNumber5(weekly["percentRemaining"]);
9752
10556
  const usedPercent = percentRemaining !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - percentRemaining)) * 10) / 10 : null;
9753
10557
  const resetsAt = isoInstant3(weekly["nextRegenAt"]);
9754
10558
  windows.push({
@@ -9758,12 +10562,42 @@ function parseSyntheticQuotasPayload(payload, now) {
9758
10562
  usedPercent,
9759
10563
  windowMinutes: 7 * 24 * 60,
9760
10564
  ...resetsAt !== void 0 ? { resetsAt } : {},
9761
- remainingSeconds: secondsUntil5(resetsAt, now),
10565
+ remainingSeconds: secondsUntil7(resetsAt, now),
9762
10566
  state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
9763
10567
  });
9764
10568
  }
9765
10569
  return windows.length > 0 ? windows : null;
9766
10570
  }
10571
+ var CLINE_WINDOW_CONFIG = {
10572
+ five_hour: { id: "five-hour", label: "5 hours", minutes: 5 * 60 },
10573
+ weekly: { id: "seven-day", label: "7 days", minutes: 7 * 24 * 60 },
10574
+ monthly: { id: "thirty-day", label: "30 days", minutes: 30 * 24 * 60 }
10575
+ };
10576
+ function parseClinePassUsageLimitsPayload(payload, now) {
10577
+ if (!isRecord7(payload)) return null;
10578
+ const data = isRecord7(payload["data"]) ? payload["data"] : payload;
10579
+ const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
10580
+ const windows = [];
10581
+ for (const raw of limits) {
10582
+ if (!isRecord7(raw)) continue;
10583
+ const config = CLINE_WINDOW_CONFIG[typeof raw["type"] === "string" ? raw["type"] : ""];
10584
+ if (!config) continue;
10585
+ const usedPercent = finitePercent4(raw["percentUsed"]);
10586
+ if (usedPercent === null) continue;
10587
+ const resetsAt = isoInstant3(raw["resetsAt"]);
10588
+ windows.push({
10589
+ id: config.id,
10590
+ label: config.label,
10591
+ scope: "all",
10592
+ usedPercent,
10593
+ windowMinutes: config.minutes,
10594
+ ...resetsAt !== void 0 ? { resetsAt } : {},
10595
+ remainingSeconds: secondsUntil7(resetsAt, now),
10596
+ state: "fresh"
10597
+ });
10598
+ }
10599
+ return windows.length > 0 ? windows : null;
10600
+ }
9767
10601
 
9768
10602
  // src/allowance/ProviderKeyQuotaService.ts
9769
10603
  function parseQuotaPayload(adapter, payload, now) {
@@ -9776,6 +10610,8 @@ function parseQuotaPayload(adapter, payload, now) {
9776
10610
  return parseUmansUsagePayload(payload, now);
9777
10611
  case "synthetic":
9778
10612
  return parseSyntheticQuotasPayload(payload, now);
10613
+ case "cline-pass":
10614
+ return parseClinePassUsageLimitsPayload(payload, now);
9779
10615
  }
9780
10616
  }
9781
10617
  var PROVIDER_KEY_QUOTA_CACHE_MS = 5 * 6e4;
@@ -9795,7 +10631,7 @@ function rowKeyEntries(row) {
9795
10631
  return [];
9796
10632
  }
9797
10633
  var ProviderKeyQuotaService = class {
9798
- constructor(box, fetchImpl = (url, init) => fetchUpstream6(url, init, { redactBodies: true }), now = Date.now) {
10634
+ constructor(box, fetchImpl = (url, init) => fetchUpstream8(url, init, { redactBodies: true }), now = Date.now) {
9799
10635
  this.box = box;
9800
10636
  this.fetchImpl = fetchImpl;
9801
10637
  this.now = now;
@@ -9857,7 +10693,10 @@ var ProviderKeyQuotaService = class {
9857
10693
  headers: {
9858
10694
  Authorization: providerKeyQuotaAuthHeader(adapter, key),
9859
10695
  Accept: "application/json",
9860
- "Content-Type": "application/json"
10696
+ "Content-Type": "application/json",
10697
+ // The row's static identity headers ride along — the Cline usage
10698
+ // endpoint sits behind the SAME client-identity 403 gate as inference.
10699
+ ...mergeExtraHeaders2({}, row.extraHeaders)
9861
10700
  },
9862
10701
  signal: AbortSignal.timeout(15e3)
9863
10702
  });
@@ -14629,6 +15468,10 @@ function toLLMProvider(row) {
14629
15468
  // `parseProviderInput`), so customizations are preserved (the row value wins).
14630
15469
  apiModes: row.apiModes,
14631
15470
  selectedApiModeId: row.selectedApiModeId,
15471
+ // Static extra request headers ride along verbatim (load-guarded — no
15472
+ // auth/content names); core's `getProviderHeaders` merges them into every
15473
+ // BYO request, and the same-format relay path inherits that funnel.
15474
+ extraHeaders: row.extraHeaders,
14632
15475
  // Official-Anthropic signature handling only matters for the Anthropic
14633
15476
  // ingress (deferred → 502); leave it off for the BYO transform path.
14634
15477
  isOfficial: false
@@ -16578,12 +17421,13 @@ import { existsSync as existsSync24, mkdirSync as mkdirSync6, readFileSync as re
16578
17421
  import { dirname as dirname15 } from "path";
16579
17422
  import { getSharedAccountHealth as getSharedAccountHealth2 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
16580
17423
  import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling3 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
16581
- import { fetchUpstream as fetchUpstream7 } from "@omnicross/core/pipeline/upstreamFetch";
17424
+ import { fetchUpstream as fetchUpstream9 } from "@omnicross/core/pipeline/upstreamFetch";
16582
17425
  import { getSharedIdentityStore as getSharedIdentityStore2 } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
16583
17426
  import {
16584
17427
  claudeOAuth as claudeOAuth2,
16585
17428
  codexOAuth as codexOAuth2,
16586
17429
  geminiOAuth as geminiOAuth2,
17430
+ grokOAuth as grokOAuth2,
16587
17431
  kimiOAuth as kimiOAuth2
16588
17432
  } from "@omnicross/subscriptions";
16589
17433
 
@@ -16733,7 +17577,7 @@ var JsonSubscriptionCredentialStore = class {
16733
17577
  * a plaintext token pair into `upstream-trace.jsonl`.
16734
17578
  */
16735
17579
  buildRefreshFetch(providerId, accountId) {
16736
- return this.fetchImpl ?? ((url, init) => fetchUpstream7(url, init, { providerId, accountId, redactBodies: true }));
17580
+ return this.fetchImpl ?? ((url, init) => fetchUpstream9(url, init, { providerId, accountId, redactBodies: true }));
16737
17581
  }
16738
17582
  /**
16739
17583
  * In-flight refresh coalescing. OAuth refresh tokens are
@@ -16774,7 +17618,7 @@ var JsonSubscriptionCredentialStore = class {
16774
17618
  * other hot reads. Never returns token material.
16775
17619
  */
16776
17620
  getAccountProxy(providerId, accountId) {
16777
- if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi") {
17621
+ if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi" && providerId !== "grok" && providerId !== "copilot") {
16778
17622
  return void 0;
16779
17623
  }
16780
17624
  return getAccountProxy(this.readConfig(), providerId, accountId);
@@ -16793,7 +17637,7 @@ var JsonSubscriptionCredentialStore = class {
16793
17637
  const fingerprintOn = identityStore.isEnabled();
16794
17638
  const now = Date.now();
16795
17639
  const out = {};
16796
- for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi"]) {
17640
+ for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi", "grok", "copilot"]) {
16797
17641
  const sanitized = sanitizeAccounts(config, provider);
16798
17642
  if (sanitized.length === 0) continue;
16799
17643
  for (const account of sanitized) {
@@ -16992,6 +17836,66 @@ var JsonSubscriptionCredentialStore = class {
16992
17836
  }
16993
17837
  });
16994
17838
  }
17839
+ /**
17840
+ * Refresh the Grok (xAI SuperGrok) OAuth access token. The token endpoint is
17841
+ * resolved through OIDC discovery on every refresh (process-cached 1h by the
17842
+ * flow module) so a rotated endpoint document is picked up without a daemon
17843
+ * restart. HONEST `false` when no refresh_token.
17844
+ */
17845
+ async refreshGrokToken() {
17846
+ return this.coalesce("grok:active", async () => {
17847
+ const config = this.readConfig();
17848
+ const active = getActiveAccount(config, "grok");
17849
+ const grok = active?.tokens;
17850
+ if (!active || !grok?.refreshToken) return false;
17851
+ const capturedId = active.id;
17852
+ this.materializeMigration(config);
17853
+ const refreshFetch = this.buildRefreshFetch("grok", capturedId);
17854
+ try {
17855
+ const tokenEndpoint = await grokOAuth2.resolveGrokTokenEndpoint(refreshFetch);
17856
+ const result = await grokOAuth2.refreshGrokAccessToken(grok.refreshToken, tokenEndpoint, refreshFetch);
17857
+ const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
17858
+ const next = {
17859
+ ...grok,
17860
+ accessToken: result.accessToken,
17861
+ refreshToken: result.refreshToken,
17862
+ expiresAt,
17863
+ status: "authorized",
17864
+ lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
17865
+ errorMessage: void 0,
17866
+ syncWarning: void 0
17867
+ };
17868
+ this.writeBackById("grok", capturedId, next);
17869
+ return true;
17870
+ } catch (error) {
17871
+ this.markExpiredById("grok", capturedId, grok, error);
17872
+ return false;
17873
+ }
17874
+ });
17875
+ }
17876
+ /**
17877
+ * "Refresh" a GitHub Copilot token — there is nothing to refresh (ghu_
17878
+ * tokens are long-lived with no exchange endpoint). A call here means the
17879
+ * strategy saw a 401 (the token was revoked); mark the account `expired`
17880
+ * with a re-authenticate message and return `false` (the proxy then declines
17881
+ * the retry instead of looping on a dead token).
17882
+ */
17883
+ async refreshCopilotToken() {
17884
+ return this.coalesce("copilot:active", async () => {
17885
+ const config = this.readConfig();
17886
+ const active = getActiveAccount(config, "copilot");
17887
+ const copilot = active?.tokens;
17888
+ if (!active || !copilot?.accessToken) return false;
17889
+ this.materializeMigration(config);
17890
+ this.markExpiredById(
17891
+ "copilot",
17892
+ active.id,
17893
+ copilot,
17894
+ new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account")
17895
+ );
17896
+ return false;
17897
+ });
17898
+ }
16995
17899
  /**
16996
17900
  * Refresh a SPECIFIC managed account by id (background scheduler sweep and
16997
17901
  * account-pool resolution). It uses only that account's stored refresh
@@ -17044,7 +17948,7 @@ var JsonSubscriptionCredentialStore = class {
17044
17948
  }
17045
17949
  const oauth = account.tokens;
17046
17950
  if (!oauth.accessToken) return null;
17047
- if (providerId === "codex" || providerId === "gemini" || providerId === "kimi") {
17951
+ if (providerId === "codex" || providerId === "gemini" || providerId === "kimi" || providerId === "grok" || providerId === "copilot") {
17048
17952
  const expiresAtMs = oauth.expiresAt ? Date.parse(oauth.expiresAt) : 0;
17049
17953
  const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - ACCOUNT_REFRESH_LEAD_MS;
17050
17954
  if (expiringSoon && oauth.refreshToken) {
@@ -17148,6 +18052,18 @@ var JsonSubscriptionCredentialStore = class {
17148
18052
  expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
17149
18053
  };
17150
18054
  }
18055
+ if (provider === "grok") {
18056
+ const tokenEndpoint = await grokOAuth2.resolveGrokTokenEndpoint(refreshFetch);
18057
+ const r2 = await grokOAuth2.refreshGrokAccessToken(refreshToken, tokenEndpoint, refreshFetch);
18058
+ return {
18059
+ accessToken: r2.accessToken,
18060
+ refreshToken: r2.refreshToken,
18061
+ expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
18062
+ };
18063
+ }
18064
+ if (provider === "copilot") {
18065
+ throw new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account");
18066
+ }
17151
18067
  const flow = provider === "claude" ? claudeOAuth2 : provider === "codex" ? codexOAuth2 : geminiOAuth2;
17152
18068
  const r = await flow.refreshAccessToken(refreshToken, refreshFetch);
17153
18069
  return {
@@ -17391,7 +18307,7 @@ var JsonSubscriptionCredentialStore = class {
17391
18307
  };
17392
18308
 
17393
18309
  // src/AccountHealthProbeScheduler.ts
17394
- import { fetchUpstream as fetchUpstream8 } from "@omnicross/core/pipeline/upstreamFetch";
18310
+ import { fetchUpstream as fetchUpstream10 } from "@omnicross/core/pipeline/upstreamFetch";
17395
18311
 
17396
18312
  // src/probe/CodexGenerationProbe.ts
17397
18313
  import {
@@ -17537,7 +18453,16 @@ var PROVIDER_PROBE_PLANS = {
17537
18453
  // Kimi's `GET /coding/v1/usages` is a verified FREE authed GET (the allowance
17538
18454
  // collector uses it), but the probe path also needs the fingerprint headers —
17539
18455
  // keep the probe local until the collector covers the health surface.
17540
- kimi: { kind: "local" }
18456
+ kimi: { kind: "local" },
18457
+ // Grok's billing proxy is a verified FREE authed GET (the allowance collector
18458
+ // uses it) but it REJECTS non-OAuth credentials and sits on a separate host
18459
+ // with its own product-gate header — keep the probe local, the collector
18460
+ // owns the health surface.
18461
+ grok: { kind: "local" },
18462
+ // The Copilot quota endpoint (copilot_internal/user) is a verified FREE
18463
+ // authed GET but lives on api.github.com with its own auth dialect and a
18464
+ // monthly-only window — the allowance collector owns the health surface.
18465
+ copilot: { kind: "local" }
17541
18466
  };
17542
18467
  function probePlanFor(providerId) {
17543
18468
  return PROVIDER_PROBE_PLANS[providerId] ?? { kind: "local" };
@@ -17559,7 +18484,7 @@ var AccountHealthProbeScheduler = class {
17559
18484
  this.logger = logger;
17560
18485
  this.config = config;
17561
18486
  this.now = opts.now ?? Date.now;
17562
- this.fetchImpl = opts.fetchImpl ?? fetchUpstream8;
18487
+ this.fetchImpl = opts.fetchImpl ?? fetchUpstream10;
17563
18488
  this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
17564
18489
  this.planFor = opts.planFor ?? probePlanFor;
17565
18490
  }
@@ -18116,7 +19041,7 @@ async function readAuditStats(auditDir, query2 = {}) {
18116
19041
  }
18117
19042
 
18118
19043
  // src/audit/AuditPruneSweeper.ts
18119
- var DAY_MS3 = 24 * 60 * 6e4;
19044
+ var DAY_MS4 = 24 * 60 * 6e4;
18120
19045
  var SWEEP_INTERVAL_MS2 = 60 * 6e4;
18121
19046
  var ARCHIVE_BATCH = 64;
18122
19047
  var AuditPruneSweeper = class {
@@ -18180,7 +19105,7 @@ var AuditPruneSweeper = class {
18180
19105
  this.sweeping = true;
18181
19106
  try {
18182
19107
  if (!existsSync26(this.auditDir)) return 0;
18183
- const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS3;
19108
+ const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS4;
18184
19109
  let removed = 0;
18185
19110
  for (const name of readdirSync8(this.auditDir)) {
18186
19111
  const dateMs = auditFileDateMs(name);
@@ -18437,7 +19362,7 @@ async function closeAll(writers) {
18437
19362
  // src/usage/UsagePruneSweeper.ts
18438
19363
  import { unlink as unlink3 } from "fs/promises";
18439
19364
  import { join as join24 } from "path";
18440
- var DAY_MS4 = 24 * 60 * 6e4;
19365
+ var DAY_MS5 = 24 * 60 * 6e4;
18441
19366
  var SWEEP_INTERVAL_MS3 = 60 * 6e4;
18442
19367
  var DEFAULT_USAGE_RETENTION_DAYS = 90;
18443
19368
  var UsagePruneSweeper = class {
@@ -18494,7 +19419,7 @@ var UsagePruneSweeper = class {
18494
19419
  this.sweeping = true;
18495
19420
  try {
18496
19421
  const retentionDays = this.config.retentionDays ?? DEFAULT_USAGE_RETENTION_DAYS;
18497
- const cutoff = this.todayMidnight() - (retentionDays - 1) * DAY_MS4;
19422
+ const cutoff = this.todayMidnight() - (retentionDays - 1) * DAY_MS5;
18498
19423
  let removed = 0;
18499
19424
  for (const entry of await listUsageDays(this.usageDir)) {
18500
19425
  if (!entry.hasShard) continue;
@@ -18722,7 +19647,7 @@ var AuditWriter = class {
18722
19647
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync8 } from "fs";
18723
19648
  import { createHmac as createHmac5 } from "crypto";
18724
19649
  import { join as join27 } from "path";
18725
- import { fetchUpstream as fetchUpstream9 } from "@omnicross/core/pipeline/upstreamFetch";
19650
+ import { fetchUpstream as fetchUpstream11 } from "@omnicross/core/pipeline/upstreamFetch";
18726
19651
 
18727
19652
  // src/billing/billingFiles.ts
18728
19653
  var BILLING_FILE_RE = /^billing-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
@@ -18745,7 +19670,7 @@ var BillingPublisher = class {
18745
19670
  constructor(billingDir, logger, opts = {}) {
18746
19671
  this.billingDir = billingDir;
18747
19672
  this.logger = logger;
18748
- this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream9(url, init));
19673
+ this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream11(url, init));
18749
19674
  this.defer = opts.defer ?? ((fn) => setTimeout(fn, 0));
18750
19675
  this.timeoutMs = opts.timeoutMs ?? BILLING_POST_TIMEOUT_MS;
18751
19676
  this.now = opts.now ?? Date.now;
@@ -18995,7 +19920,7 @@ var BillingRetrySweeper = class {
18995
19920
  // src/TokenRefreshScheduler.ts
18996
19921
  var REFRESH_LEAD_MS2 = 5 * 6e4;
18997
19922
  var SWEEP_INTERVAL_MS5 = 6e4;
18998
- var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
19923
+ var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
18999
19924
  var TokenRefreshScheduler = class {
19000
19925
  constructor(store, logger, intervalMs = SWEEP_INTERVAL_MS5, leadMs = REFRESH_LEAD_MS2) {
19001
19926
  this.store = store;
@@ -19080,6 +20005,12 @@ var TokenRefreshScheduler = class {
19080
20005
  return this.store.refreshGeminiToken();
19081
20006
  case "kimi":
19082
20007
  return this.store.refreshKimiToken();
20008
+ case "grok":
20009
+ return this.store.refreshGrokToken();
20010
+ // ghu_ tokens never near-expire (far-future expiresAt), so the sweep
20011
+ // never reaches this — the branch exists for union totality.
20012
+ case "copilot":
20013
+ return this.store.refreshCopilotToken();
19083
20014
  }
19084
20015
  }
19085
20016
  };
@@ -19158,7 +20089,7 @@ function createRouteLeaseSubscriptionPreflight(credentials) {
19158
20089
 
19159
20090
  // src/webhook/WebhookDispatcher.ts
19160
20091
  import { createHmac as createHmac6 } from "crypto";
19161
- import { fetchUpstream as fetchUpstream10 } from "@omnicross/core/pipeline/upstreamFetch";
20092
+ import { fetchUpstream as fetchUpstream12 } from "@omnicross/core/pipeline/upstreamFetch";
19162
20093
  var WEBHOOK_MAX_ATTEMPTS = 3;
19163
20094
  var WEBHOOK_QUEUE_MAX = 1e3;
19164
20095
  var WEBHOOK_SEND_TIMEOUT_MS = 1e4;
@@ -19178,7 +20109,7 @@ var WebhookDispatcher = class {
19178
20109
  sleep;
19179
20110
  now;
19180
20111
  constructor(opts = {}) {
19181
- this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream10(url, init));
20112
+ this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream12(url, init));
19182
20113
  this.logger = opts.logger;
19183
20114
  this.maxAttempts = opts.maxAttempts ?? WEBHOOK_MAX_ATTEMPTS;
19184
20115
  this.queueMax = opts.queueMax ?? WEBHOOK_QUEUE_MAX;
@@ -19264,8 +20195,8 @@ var WebhookDispatcher = class {
19264
20195
  signal: AbortSignal.timeout(this.timeoutMs)
19265
20196
  });
19266
20197
  return res.ok ? { ok: true, status: res.status } : { ok: false, status: res.status };
19267
- } catch (err6) {
19268
- return { ok: false, error: err6 instanceof Error ? err6.message : String(err6) };
20198
+ } catch (err8) {
20199
+ return { ok: false, error: err8 instanceof Error ? err8.message : String(err8) };
19269
20200
  }
19270
20201
  }
19271
20202
  /**
@@ -19402,7 +20333,7 @@ function buildDaemon(config, paths) {
19402
20333
  setSecretBox(secretBox3);
19403
20334
  setSecretBox2(secretBox3);
19404
20335
  const decryptedConfig = decryptConfigSecrets(config, secretBox3);
19405
- const accountAllowanceStore = new AccountAllowanceStore6(
20336
+ const accountAllowanceStore = new AccountAllowanceStore8(
19406
20337
  Date.now,
19407
20338
  void 0,
19408
20339
  new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
@@ -19464,7 +20395,7 @@ function buildDaemon(config, paths) {
19464
20395
  const pricingEngine = new PricingEngine(pricingStore, logger, {
19465
20396
  // Catalog egress follows the same global/env proxy policy as every other
19466
20397
  // daemon upstream call; no provider/account override applies here.
19467
- fetchImpl: ((input, init) => fetchUpstream11(String(input), init ?? {}))
20398
+ fetchImpl: ((input, init) => fetchUpstream13(String(input), init ?? {}))
19468
20399
  });
19469
20400
  const pricingRefreshScheduler = new PricingRefreshScheduler(
19470
20401
  pricingEngine,
@@ -19749,7 +20680,7 @@ function buildDaemon(config, paths) {
19749
20680
  // — `server.proxy.byProvider[...]` was silently skipped — and the call was
19750
20681
  // excluded from the upstream trace, so a failing login left no evidence.
19751
20682
  // `redactBodies` keeps the code/verifier + minted token out of that trace.
19752
- oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => fetchUpstream11(url, init, { providerId, redactBodies: true }),
20683
+ oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => fetchUpstream13(url, init, { providerId, redactBodies: true }),
19753
20684
  subscriptionAccountAppender: credentialStore,
19754
20685
  // Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
19755
20686
  // + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
@@ -19761,6 +20692,9 @@ function buildDaemon(config, paths) {
19761
20692
  // paste; the app shows the verification URL + user code and polls the
19762
20693
  // token-free status). Token captured + persisted daemon-side.
19763
20694
  kimiSessions: new CodexOAuthSessionStore(),
20695
+ // Grok interactive OAuth — the same async DEVICE-CODE shape as kimi.
20696
+ grokSessions: new CodexOAuthSessionStore(),
20697
+ copilotSessions: new CodexOAuthSessionStore(),
19764
20698
  // Migration pack (app-parity child 6, design D2/D3) — the concrete credential
19765
20699
  // store provides BOTH the full DECRYPTED read (`getFullConfig`, export) and
19766
20700
  // the multi-account append (`appendProviderAccount`, import re-encrypts at-
@@ -19819,7 +20753,7 @@ function buildDaemon(config, paths) {
19819
20753
  });
19820
20754
  const webhookDispatcher = new WebhookDispatcher({
19821
20755
  logger,
19822
- fetchImpl: (url, init) => fetchUpstream11(url, init)
20756
+ fetchImpl: (url, init) => fetchUpstream13(url, init)
19823
20757
  });
19824
20758
  setWebhookRuntime(webhookDispatcher, getSharedAccountHealth4());
19825
20759
  const auditWriter = new AuditWriter(auditDir, logger);
@@ -20133,11 +21067,11 @@ async function runLiveProbe(url, key, fetchImpl = fetch) {
20133
21067
  status: res.status,
20134
21068
  estimateHeader: res.headers.get("x-omnicross-count-estimate")
20135
21069
  };
20136
- } catch (err6) {
21070
+ } catch (err8) {
20137
21071
  return {
20138
21072
  status: null,
20139
21073
  estimateHeader: null,
20140
- error: err6 instanceof Error ? err6.message : String(err6)
21074
+ error: err8 instanceof Error ? err8.message : String(err8)
20141
21075
  };
20142
21076
  }
20143
21077
  }
@@ -20547,9 +21481,9 @@ async function runLaunch(argv, deps) {
20547
21481
  await daemon.llmConfig.ready();
20548
21482
  await daemon.migrateUsageStore();
20549
21483
  await daemon.providerProxy.start();
20550
- } catch (err6) {
21484
+ } catch (err8) {
20551
21485
  await shutdownLaunchDaemon(daemon);
20552
- throw err6;
21486
+ throw err8;
20553
21487
  }
20554
21488
  let launch;
20555
21489
  try {
@@ -20557,9 +21491,9 @@ async function runLaunch(argv, deps) {
20557
21491
  providerId: values.provider,
20558
21492
  model: values.model
20559
21493
  });
20560
- } catch (err6) {
21494
+ } catch (err8) {
20561
21495
  await shutdownLaunchDaemon(daemon);
20562
- throw err6;
21496
+ throw err8;
20563
21497
  }
20564
21498
  try {
20565
21499
  const plan = buildCliSpawnPlan({
@@ -20664,9 +21598,9 @@ function spawnCliInherit(plan) {
20664
21598
  process.removeListener("SIGINT", onSignal);
20665
21599
  process.removeListener("SIGTERM", onSignal);
20666
21600
  };
20667
- child.on("error", (err6) => {
21601
+ child.on("error", (err8) => {
20668
21602
  detach();
20669
- if (err6.code === "ENOENT") {
21603
+ if (err8.code === "ENOENT") {
20670
21604
  reject(
20671
21605
  new Error(
20672
21606
  `launch: "${plan.command}" not found on PATH \u2014 install the CLI first.`
@@ -20674,7 +21608,7 @@ function spawnCliInherit(plan) {
20674
21608
  );
20675
21609
  return;
20676
21610
  }
20677
- reject(err6);
21611
+ reject(err8);
20678
21612
  });
20679
21613
  child.on("exit", (code, signal) => {
20680
21614
  detach();
@@ -20687,14 +21621,16 @@ function spawnCliInherit(plan) {
20687
21621
  import { spawn as spawn3 } from "child_process";
20688
21622
  import { createInterface as createInterface2 } from "readline";
20689
21623
  import { parseArgs as parseArgs7 } from "util";
20690
- import { fetchUpstream as fetchUpstream12, setUpstreamProxyResolver as setUpstreamProxyResolver2 } from "@omnicross/core/pipeline/upstreamFetch";
21624
+ import { fetchUpstream as fetchUpstream14, setUpstreamProxyResolver as setUpstreamProxyResolver2 } from "@omnicross/core/pipeline/upstreamFetch";
20691
21625
  import {
20692
21626
  claudeOAuth as claudeOAuth3,
20693
21627
  codexOAuth as codexOAuth3,
21628
+ copilotOAuth as copilotOAuth3,
20694
21629
  geminiOAuth as geminiOAuth3,
21630
+ grokOAuth as grokOAuth3,
20695
21631
  kimiOAuth as kimiOAuth3
20696
21632
  } from "@omnicross/subscriptions";
20697
- var PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
21633
+ var PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
20698
21634
  async function runLogin(argv, deps) {
20699
21635
  const { values, positionals } = parseArgs7({
20700
21636
  args: argv,
@@ -20702,7 +21638,9 @@ async function runLogin(argv, deps) {
20702
21638
  config: { type: "string", short: "c" },
20703
21639
  "master-key-file": { type: "string" },
20704
21640
  // Optional user label for the appended account (multi-account).
20705
- label: { type: "string" }
21641
+ label: { type: "string" },
21642
+ // Optional GitHub Enterprise domain for `login copilot` (GHE accounts).
21643
+ enterprise: { type: "string" }
20706
21644
  },
20707
21645
  allowPositionals: true
20708
21646
  });
@@ -20716,11 +21654,17 @@ async function runLogin(argv, deps) {
20716
21654
  if (!values.config) {
20717
21655
  throw new Error("login: --config <path> is required");
20718
21656
  }
21657
+ if (values.enterprise !== void 0 && provider !== "copilot") {
21658
+ throw new Error("login: --enterprise is only supported for the copilot provider");
21659
+ }
21660
+ const enterpriseDomain = values.enterprise !== void 0 ? copilotOAuth3.normalizeCopilotEnterpriseDomain(values.enterprise) : void 0;
20719
21661
  const resolved = {
20720
21662
  openBrowser: deps?.openBrowser ?? openBrowser,
20721
21663
  promptPaste: deps?.promptPaste ?? promptPaste,
20722
21664
  awaitLoopback: deps?.awaitLoopback ?? ((state) => awaitLoopbackCode(state)),
20723
21665
  awaitKimiDevice: deps?.awaitKimiDevice ?? ((fetchImpl) => runKimiDeviceFlow(fetchImpl, resolvedOpenBrowser)),
21666
+ awaitGrokDevice: deps?.awaitGrokDevice ?? ((fetchImpl) => runGrokDeviceFlow(fetchImpl, resolvedOpenBrowser)),
21667
+ awaitCopilotDevice: deps?.awaitCopilotDevice ?? ((fetchImpl, enterpriseUrl) => runCopilotDeviceFlow(fetchImpl, resolvedOpenBrowser, enterpriseUrl)),
20724
21668
  tokensFetch: deps?.tokensFetch
20725
21669
  };
20726
21670
  const resolvedOpenBrowser = resolved.openBrowser;
@@ -20729,14 +21673,15 @@ async function runLogin(argv, deps) {
20729
21673
  setUpstreamProxyResolver2(createUpstreamProxyResolver());
20730
21674
  try {
20731
21675
  const tokensPath = defaultTokensPath(values.config);
20732
- const exchangeFetch = resolved.tokensFetch ?? ((url, init) => fetchUpstream12(url, init, { providerId: provider, redactBodies: true }));
21676
+ const exchangeFetch = resolved.tokensFetch ?? ((url, init) => fetchUpstream14(url, init, { providerId: provider, redactBodies: true }));
20733
21677
  const store = new JsonSubscriptionCredentialStore(tokensPath, box, exchangeFetch);
20734
21678
  const expiresAt = await runProviderLogin(
20735
21679
  provider,
20736
21680
  store,
20737
21681
  resolved,
20738
21682
  exchangeFetch,
20739
- values.label
21683
+ values.label,
21684
+ enterpriseDomain
20740
21685
  );
20741
21686
  console.info(`Logged in to '${provider}' \u2192 ${tokensPath}`);
20742
21687
  console.info(` token: [stored, encrypted] expiresAt: ${expiresAt ?? "n/a"}`);
@@ -20745,10 +21690,12 @@ async function runLogin(argv, deps) {
20745
21690
  setUpstreamProxyResolver2(null);
20746
21691
  }
20747
21692
  }
20748
- async function runProviderLogin(provider, store, deps, exchangeFetch, label) {
21693
+ async function runProviderLogin(provider, store, deps, exchangeFetch, label, enterpriseUrl) {
20749
21694
  if (provider === "codex") return loginCodex(store, deps, exchangeFetch, label);
20750
21695
  if (provider === "claude") return loginClaude(store, deps, exchangeFetch, label);
20751
21696
  if (provider === "kimi") return loginKimi(store, deps, exchangeFetch, label);
21697
+ if (provider === "grok") return loginGrok(store, deps, exchangeFetch, label);
21698
+ if (provider === "copilot") return loginCopilot(store, deps, exchangeFetch, label, enterpriseUrl);
20752
21699
  return loginGemini(store, deps, exchangeFetch, label);
20753
21700
  }
20754
21701
  async function loginCodex(store, deps, exchangeFetch, label) {
@@ -20858,6 +21805,90 @@ async function loginKimi(store, deps, exchangeFetch, label) {
20858
21805
  logMasked("kimi", result.accessToken);
20859
21806
  return expiresAt;
20860
21807
  }
21808
+ async function runGrokDeviceFlow(exchangeFetch, openBrowserFn) {
21809
+ const tokenEndpoint = await grokOAuth3.resolveGrokTokenEndpoint(exchangeFetch);
21810
+ const authorization = await grokOAuth3.requestGrokDeviceAuthorization(exchangeFetch);
21811
+ const url = authorization.verificationUriComplete ?? authorization.verificationUri;
21812
+ console.info("Open this URL in your browser and approve the request:");
21813
+ console.info(` ${url}`);
21814
+ if (!authorization.verificationUriComplete) {
21815
+ console.info(` Then enter this code: ${authorization.userCode}`);
21816
+ }
21817
+ await openBrowserFn(url).catch(() => false);
21818
+ const result = await grokOAuth3.awaitGrokDeviceToken(authorization, tokenEndpoint, exchangeFetch, {
21819
+ onPending: () => process.stdout.write(".")
21820
+ });
21821
+ console.info("");
21822
+ return {
21823
+ ...result,
21824
+ accountId: grokOAuth3.grokAccountIdFromAccessToken(result.accessToken)
21825
+ };
21826
+ }
21827
+ async function loginGrok(store, deps, exchangeFetch, label) {
21828
+ const result = await deps.awaitGrokDevice(exchangeFetch);
21829
+ const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
21830
+ const block = {
21831
+ authMethod: "oauth",
21832
+ status: "authorized",
21833
+ accessToken: result.accessToken,
21834
+ refreshToken: result.refreshToken,
21835
+ expiresAt,
21836
+ ...result.accountId ? { accountId: result.accountId } : {},
21837
+ lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
21838
+ };
21839
+ await store.appendProviderAccount("grok", block, label);
21840
+ logMasked("grok", result.accessToken);
21841
+ return expiresAt;
21842
+ }
21843
+ async function runCopilotDeviceFlow(exchangeFetch, openBrowserFn, enterpriseUrl) {
21844
+ if (enterpriseUrl) console.info(`Using GitHub Enterprise host: ${enterpriseUrl}`);
21845
+ const authorization = await copilotOAuth3.requestCopilotDeviceAuthorization(exchangeFetch, enterpriseUrl);
21846
+ const url = authorization.verificationUri;
21847
+ console.info("Open this URL in your browser and approve the request:");
21848
+ console.info(` ${url}`);
21849
+ console.info(` Then enter this code: ${authorization.userCode}`);
21850
+ await openBrowserFn(url).catch(() => false);
21851
+ const result = await copilotOAuth3.awaitCopilotDeviceToken(authorization, exchangeFetch, {
21852
+ onPending: () => process.stdout.write("."),
21853
+ ...enterpriseUrl ? { enterpriseUrl } : {}
21854
+ });
21855
+ console.info("");
21856
+ const identity = await copilotOAuth3.fetchCopilotIdentity(result.accessToken, exchangeFetch, enterpriseUrl);
21857
+ const apiEndpoint = await copilotOAuth3.discoverCopilotApiEndpoint(result.accessToken, exchangeFetch, enterpriseUrl);
21858
+ console.info("Enabling Copilot models (policy)...");
21859
+ await copilotOAuth3.enableAllCopilotModels(
21860
+ result.accessToken,
21861
+ { apiEndpoint, ...enterpriseUrl ? { enterpriseUrl } : {} },
21862
+ exchangeFetch
21863
+ );
21864
+ return {
21865
+ accessToken: result.accessToken,
21866
+ expiresIn: Math.floor(copilotOAuth3.COPILOT_FAR_FUTURE_MS / 1e3),
21867
+ ...identity,
21868
+ ...apiEndpoint ? { apiEndpoint } : {}
21869
+ };
21870
+ }
21871
+ async function loginCopilot(store, deps, exchangeFetch, label, enterpriseUrl) {
21872
+ const result = await deps.awaitCopilotDevice(exchangeFetch, enterpriseUrl);
21873
+ const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
21874
+ const block = {
21875
+ authMethod: "oauth",
21876
+ status: "authorized",
21877
+ accessToken: result.accessToken,
21878
+ // ghu_ tokens have no refresh lifecycle — the same token doubles as the
21879
+ // stored refresh credential so generic refresh paths stay well-formed.
21880
+ refreshToken: result.accessToken,
21881
+ expiresAt,
21882
+ ...result.accountId ? { accountId: result.accountId } : {},
21883
+ ...result.email ? { email: result.email } : {},
21884
+ ...result.apiEndpoint ? { apiEndpoint: result.apiEndpoint } : {},
21885
+ ...enterpriseUrl ? { enterpriseUrl } : {},
21886
+ lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
21887
+ };
21888
+ await store.appendProviderAccount("copilot", block, label);
21889
+ logMasked("copilot", result.accessToken);
21890
+ return expiresAt;
21891
+ }
20861
21892
  function isLoginProvider(value) {
20862
21893
  return PROVIDERS2.includes(value);
20863
21894
  }
@@ -21563,7 +22594,7 @@ async function main() {
21563
22594
  process.exitCode = 1;
21564
22595
  }
21565
22596
  }
21566
- main().catch((err6) => {
21567
- console.error(err6 instanceof Error ? err6.message : String(err6));
22597
+ main().catch((err8) => {
22598
+ console.error(err8 instanceof Error ? err8.message : String(err8));
21568
22599
  process.exitCode = 1;
21569
22600
  });