@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/index.js CHANGED
@@ -18,14 +18,14 @@ import { setSubscriptionRegistryForOutbound } from "@omnicross/core/outbound-api
18
18
  import { getSharedAccountHealth as getSharedAccountHealth4 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
19
19
  import {
20
20
  __resetSharedAccountAllowanceStoreForTests,
21
- AccountAllowanceStore as AccountAllowanceStore6,
21
+ AccountAllowanceStore as AccountAllowanceStore8,
22
22
  setSharedAccountAllowanceStore
23
23
  } from "@omnicross/core/pipeline/AccountAllowanceStore";
24
24
  import {
25
25
  __resetSharedAccountAllowanceSchedulingForTests,
26
26
  getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling5
27
27
  } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
28
- import { fetchUpstream as fetchUpstream11, setUpstreamProxyResolver } from "@omnicross/core/pipeline/upstreamFetch";
28
+ import { fetchUpstream as fetchUpstream13, setUpstreamProxyResolver } from "@omnicross/core/pipeline/upstreamFetch";
29
29
  import { __resetSharedIdentityStoreForTests } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
30
30
  import { setGeminiCodeAssistResolver } from "@omnicross/core/ports/gemini-code-assist-resolver";
31
31
  import {
@@ -230,9 +230,188 @@ function handleKimiOAuthStatus(sessionId, deps) {
230
230
  return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
231
231
  }
232
232
 
233
+ // src/admin/accountsGrokOAuth.ts
234
+ import { grokOAuth } from "@omnicross/subscriptions";
235
+ function err3(status, message) {
236
+ return { status, body: { error: { type: "admin_api_error", message } } };
237
+ }
238
+ var DEFAULT_GROK_OAUTH_TTL_MS = 15 * 6e4;
239
+ async function handleGrokOAuthStart(deps) {
240
+ if (deps.grokSessions.isBusy()) {
241
+ return err3(409, "a grok sign-in is already in progress \u2014 finish it in the browser or cancel it");
242
+ }
243
+ const fetchImpl = deps.oauthExchangeFetch("grok");
244
+ let tokenEndpoint;
245
+ try {
246
+ tokenEndpoint = await grokOAuth.resolveGrokTokenEndpoint(fetchImpl);
247
+ } catch (e) {
248
+ const reason = e instanceof Error ? e.message : "OIDC discovery failed";
249
+ return err3(502, `grok token-endpoint discovery failed: ${reason}`);
250
+ }
251
+ let authorization;
252
+ try {
253
+ authorization = await grokOAuth.requestGrokDeviceAuthorization(fetchImpl);
254
+ } catch (e) {
255
+ const reason = e instanceof Error ? e.message : "device authorization failed";
256
+ return err3(502, `grok device authorization failed: ${reason}`);
257
+ }
258
+ const { sessionId, signal } = deps.grokSessions.begin();
259
+ void runGrokDevicePoll(sessionId, tokenEndpoint, authorization.deviceCode, signal, deps).catch((e) => {
260
+ const reason = e instanceof Error ? e.message : "grok sign-in failed";
261
+ deps.grokSessions.settle(sessionId, "error", reason);
262
+ });
263
+ return {
264
+ status: 200,
265
+ body: {
266
+ authUrl: authorization.verificationUriComplete ?? authorization.verificationUri,
267
+ userCode: authorization.userCode,
268
+ sessionId
269
+ }
270
+ };
271
+ }
272
+ async function runGrokDevicePoll(sessionId, tokenEndpoint, deviceCode, signal, deps) {
273
+ const fetchImpl = deps.oauthExchangeFetch("grok");
274
+ const result = await grokOAuth.awaitGrokDeviceToken(
275
+ { userCode: "", deviceCode, verificationUri: "" },
276
+ tokenEndpoint,
277
+ fetchImpl,
278
+ {
279
+ deadlineMs: DEFAULT_GROK_OAUTH_TTL_MS,
280
+ sleep: (ms) => new Promise((resolve10, reject) => {
281
+ const onAbort = () => {
282
+ clearTimeout(timer);
283
+ reject(new Error("login: cancelled"));
284
+ };
285
+ const timer = setTimeout(() => {
286
+ signal.removeEventListener("abort", onAbort);
287
+ resolve10();
288
+ }, ms);
289
+ signal.addEventListener("abort", onAbort, { once: true });
290
+ })
291
+ }
292
+ );
293
+ const block = {
294
+ authMethod: "oauth",
295
+ status: "authorized",
296
+ accessToken: result.accessToken,
297
+ refreshToken: result.refreshToken,
298
+ expiresAt: new Date(Date.now() + result.expiresIn * 1e3).toISOString(),
299
+ accountId: grokOAuth.grokAccountIdFromAccessToken(result.accessToken),
300
+ lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
301
+ };
302
+ await deps.subscriptionAccountAppender.appendProviderAccount("grok", block);
303
+ deps.grokSessions.settle(sessionId, "done");
304
+ }
305
+ function handleGrokOAuthCancel(sessionId, deps) {
306
+ if (!deps.grokSessions.cancel(sessionId)) return err3(404, "unknown or expired grok sign-in session");
307
+ return { status: 200, body: { ok: true } };
308
+ }
309
+ function handleGrokOAuthStatus(sessionId, deps) {
310
+ const s = deps.grokSessions.get(sessionId);
311
+ if (!s) return err3(404, "unknown or expired grok sign-in session");
312
+ return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
313
+ }
314
+
315
+ // src/admin/accountsCopilotOAuth.ts
316
+ import { copilotOAuth } from "@omnicross/subscriptions";
317
+ function err4(status, message) {
318
+ return { status, body: { error: { type: "admin_api_error", message } } };
319
+ }
320
+ var DEFAULT_COPILOT_OAUTH_TTL_MS = 15 * 6e4;
321
+ async function handleCopilotOAuthStart(deps, enterpriseUrlInput) {
322
+ if (deps.copilotSessions.isBusy()) {
323
+ return err4(409, "a copilot sign-in is already in progress \u2014 finish it in the browser or cancel it");
324
+ }
325
+ let enterpriseUrl;
326
+ if (typeof enterpriseUrlInput === "string" && enterpriseUrlInput.trim()) {
327
+ try {
328
+ enterpriseUrl = copilotOAuth.normalizeCopilotEnterpriseDomain(enterpriseUrlInput);
329
+ } catch (e) {
330
+ const reason = e instanceof Error ? e.message : "invalid GitHub Enterprise domain";
331
+ return err4(400, `copilot ${reason}`);
332
+ }
333
+ }
334
+ const fetchImpl = deps.oauthExchangeFetch("copilot");
335
+ let authorization;
336
+ try {
337
+ authorization = await copilotOAuth.requestCopilotDeviceAuthorization(fetchImpl, enterpriseUrl);
338
+ } catch (e) {
339
+ const reason = e instanceof Error ? e.message : "device authorization failed";
340
+ return err4(502, `copilot device authorization failed: ${reason}`);
341
+ }
342
+ const { sessionId, signal } = deps.copilotSessions.begin();
343
+ void runCopilotDevicePoll(sessionId, authorization.deviceCode, signal, deps, enterpriseUrl).catch((e) => {
344
+ const reason = e instanceof Error ? e.message : "copilot sign-in failed";
345
+ deps.copilotSessions.settle(sessionId, "error", reason);
346
+ });
347
+ return {
348
+ status: 200,
349
+ body: {
350
+ authUrl: authorization.verificationUri,
351
+ userCode: authorization.userCode,
352
+ sessionId,
353
+ ...enterpriseUrl ? { enterpriseUrl } : {}
354
+ }
355
+ };
356
+ }
357
+ async function runCopilotDevicePoll(sessionId, deviceCode, signal, deps, enterpriseUrl) {
358
+ const fetchImpl = deps.oauthExchangeFetch("copilot");
359
+ const result = await copilotOAuth.awaitCopilotDeviceToken(
360
+ { userCode: "", deviceCode, verificationUri: "", interval: 5, expiresIn: 900 },
361
+ fetchImpl,
362
+ {
363
+ deadlineMs: DEFAULT_COPILOT_OAUTH_TTL_MS,
364
+ ...enterpriseUrl ? { enterpriseUrl } : {},
365
+ sleep: (ms) => new Promise((resolve10, reject) => {
366
+ const onAbort = () => {
367
+ clearTimeout(timer);
368
+ reject(new Error("login: cancelled"));
369
+ };
370
+ const timer = setTimeout(() => {
371
+ signal.removeEventListener("abort", onAbort);
372
+ resolve10();
373
+ }, ms);
374
+ signal.addEventListener("abort", onAbort, { once: true });
375
+ })
376
+ }
377
+ );
378
+ const identity = await copilotOAuth.fetchCopilotIdentity(result.accessToken, fetchImpl, enterpriseUrl);
379
+ const apiEndpoint = await copilotOAuth.discoverCopilotApiEndpoint(result.accessToken, fetchImpl, enterpriseUrl);
380
+ await copilotOAuth.enableAllCopilotModels(
381
+ result.accessToken,
382
+ { apiEndpoint, ...enterpriseUrl ? { enterpriseUrl } : {} },
383
+ fetchImpl
384
+ );
385
+ const block = {
386
+ authMethod: "oauth",
387
+ status: "authorized",
388
+ accessToken: result.accessToken,
389
+ refreshToken: result.accessToken,
390
+ expiresAt: new Date(Date.now() + copilotOAuth.COPILOT_FAR_FUTURE_MS).toISOString(),
391
+ ...identity.accountId ? { accountId: identity.accountId } : {},
392
+ ...identity.email ? { email: identity.email } : {},
393
+ ...apiEndpoint ? { apiEndpoint } : {},
394
+ ...enterpriseUrl ? { enterpriseUrl } : {},
395
+ lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
396
+ };
397
+ await deps.subscriptionAccountAppender.appendProviderAccount("copilot", block);
398
+ deps.copilotSessions.settle(sessionId, "done");
399
+ }
400
+ function handleCopilotOAuthCancel(sessionId, deps) {
401
+ if (!deps.copilotSessions.cancel(sessionId)) {
402
+ return err4(404, "unknown or expired copilot sign-in session");
403
+ }
404
+ return { status: 200, body: { ok: true } };
405
+ }
406
+ function handleCopilotOAuthStatus(sessionId, deps) {
407
+ const s = deps.copilotSessions.get(sessionId);
408
+ if (!s) return err4(404, "unknown or expired copilot sign-in session");
409
+ return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
410
+ }
411
+
233
412
  // src/allowance/AccountAllowanceService.ts
234
413
  import {
235
- getSharedAccountAllowanceStore as getSharedAccountAllowanceStore5
414
+ getSharedAccountAllowanceStore as getSharedAccountAllowanceStore7
236
415
  } from "@omnicross/core/pipeline/AccountAllowanceStore";
237
416
  import {
238
417
  getSharedAccountAllowanceScheduling
@@ -763,67 +942,548 @@ function secondsUntil3(instant, now) {
763
942
  if (!instant) return void 0;
764
943
  return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
765
944
  }
766
- function windowFromRow(row, fallback, now) {
767
- const usedPercent = row?.limit !== void 0 && row.limit > 0 && row.used !== void 0 ? Math.round(Math.min(100, row.used / row.limit * 100) * 10) / 10 : null;
768
- const resetsAt = row?.resetsAtMs !== void 0 ? new Date(row.resetsAtMs).toISOString() : void 0;
769
- return {
770
- id: fallback.id,
771
- label: fallback.label,
772
- scope: "all",
773
- usedPercent,
774
- windowMinutes: fallback.minutes,
775
- ...resetsAt !== void 0 ? { resetsAt } : {},
776
- remainingSeconds: secondsUntil3(resetsAt, now),
777
- state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
778
- };
779
- }
780
- function parseKimiUsagePayload(payload, now) {
781
- if (!isRecord(payload)) return [];
782
- const byId = /* @__PURE__ */ new Map();
783
- const rowFrom = (data) => {
784
- const limit = finiteNumber2(data["limit"]);
785
- let used = finiteNumber2(data["used"]);
786
- const remaining = finiteNumber2(data["remaining"]);
787
- if (used === void 0 && remaining !== void 0 && limit !== void 0) {
788
- used = limit - remaining;
789
- }
790
- let windowDurationMs;
791
- const windowData = isRecord(data["window"]) ? data["window"] : void 0;
792
- const duration = finiteNumber2(windowData?.["duration"]);
793
- const timeUnit = typeof windowData?.["timeUnit"] === "string" ? windowData["timeUnit"].toUpperCase() : "";
794
- if (duration !== void 0) {
795
- if (timeUnit.includes("MINUTE")) windowDurationMs = duration * MINUTE_MS;
796
- else if (timeUnit.includes("HOUR")) windowDurationMs = duration * HOUR_MS;
797
- else if (timeUnit.includes("DAY")) windowDurationMs = duration * DAY_MS;
798
- else if (timeUnit.includes("WEEK")) windowDurationMs = duration * 7 * DAY_MS;
799
- else if (timeUnit.includes("SECOND")) windowDurationMs = duration * 1e3;
945
+ function windowFromRow(row, fallback, now) {
946
+ const usedPercent = row?.limit !== void 0 && row.limit > 0 && row.used !== void 0 ? Math.round(Math.min(100, row.used / row.limit * 100) * 10) / 10 : null;
947
+ const resetsAt = row?.resetsAtMs !== void 0 ? new Date(row.resetsAtMs).toISOString() : void 0;
948
+ return {
949
+ id: fallback.id,
950
+ label: fallback.label,
951
+ scope: "all",
952
+ usedPercent,
953
+ windowMinutes: fallback.minutes,
954
+ ...resetsAt !== void 0 ? { resetsAt } : {},
955
+ remainingSeconds: secondsUntil3(resetsAt, now),
956
+ state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
957
+ };
958
+ }
959
+ function parseKimiUsagePayload(payload, now) {
960
+ if (!isRecord(payload)) return [];
961
+ const byId = /* @__PURE__ */ new Map();
962
+ const rowFrom = (data) => {
963
+ const limit = finiteNumber2(data["limit"]);
964
+ let used = finiteNumber2(data["used"]);
965
+ const remaining = finiteNumber2(data["remaining"]);
966
+ if (used === void 0 && remaining !== void 0 && limit !== void 0) {
967
+ used = limit - remaining;
968
+ }
969
+ let windowDurationMs;
970
+ const windowData = isRecord(data["window"]) ? data["window"] : void 0;
971
+ const duration = finiteNumber2(windowData?.["duration"]);
972
+ const timeUnit = typeof windowData?.["timeUnit"] === "string" ? windowData["timeUnit"].toUpperCase() : "";
973
+ if (duration !== void 0) {
974
+ if (timeUnit.includes("MINUTE")) windowDurationMs = duration * MINUTE_MS;
975
+ else if (timeUnit.includes("HOUR")) windowDurationMs = duration * HOUR_MS;
976
+ else if (timeUnit.includes("DAY")) windowDurationMs = duration * DAY_MS;
977
+ else if (timeUnit.includes("WEEK")) windowDurationMs = duration * 7 * DAY_MS;
978
+ else if (timeUnit.includes("SECOND")) windowDurationMs = duration * 1e3;
979
+ }
980
+ const resetsAtMs = parseResetMs(windowData && parseResetMs(windowData, now) !== void 0 ? windowData : data, now);
981
+ return { used, limit, remaining, ...resetsAtMs !== void 0 ? { resetsAtMs } : {}, ...windowDurationMs !== void 0 ? { windowDurationMs } : {} };
982
+ };
983
+ if (isRecord(payload["usage"])) {
984
+ const row = rowFrom(payload["usage"]);
985
+ const window = windowFromRow({ ...row, resetsAtMs: row.resetsAtMs }, { id: "seven-day", label: "7 days", minutes: 10080 }, now);
986
+ byId.set("seven-day", window);
987
+ }
988
+ if (Array.isArray(payload["limits"])) {
989
+ for (const item of payload["limits"]) {
990
+ if (!isRecord(item)) continue;
991
+ const detail = isRecord(item["detail"]) ? item["detail"] : item;
992
+ const row = rowFrom(detail);
993
+ const canonical = row.windowDurationMs !== void 0 ? canonicalWindow(row.windowDurationMs) : void 0;
994
+ if (!canonical) continue;
995
+ const window = windowFromRow(row, canonical, now);
996
+ const existing = byId.get(canonical.id);
997
+ if (!existing || (window.usedPercent ?? 0) > (existing.usedPercent ?? 0)) {
998
+ byId.set(canonical.id, window);
999
+ }
1000
+ }
1001
+ }
1002
+ return [...byId.values()].sort((a, b) => (a.windowMinutes ?? Infinity) - (b.windowMinutes ?? Infinity)).slice(0, 4);
1003
+ }
1004
+ var KimiAllowanceCollector = class {
1005
+ constructor(credentials, store = getSharedAccountAllowanceStore3(), fetchImpl = (url, init, accountId) => fetchUpstream3(url, init, { providerId: "kimi", accountId, redactBodies: true }), now = Date.now) {
1006
+ this.credentials = credentials;
1007
+ this.store = store;
1008
+ this.fetchImpl = fetchImpl;
1009
+ this.now = now;
1010
+ }
1011
+ credentials;
1012
+ store;
1013
+ fetchImpl;
1014
+ now;
1015
+ inFlight = /* @__PURE__ */ new Map();
1016
+ async collectMany(accounts, options = {}) {
1017
+ const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
1018
+ return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
1019
+ }
1020
+ collect(account, options = {}) {
1021
+ const now = this.now();
1022
+ if (account.tokens.authMethod !== "oauth") {
1023
+ const existing = this.store.get("kimi", account.id, now);
1024
+ if (existing?.windows.every((window) => window.state === "unsupported")) {
1025
+ return Promise.resolve(existing);
1026
+ }
1027
+ const snapshot = this.unsupportedSnapshot(account.id, now);
1028
+ this.store.set(snapshot);
1029
+ return Promise.resolve(snapshot);
1030
+ }
1031
+ const cached = this.store.get("kimi", account.id, now);
1032
+ if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
1033
+ return Promise.resolve(cached);
1034
+ }
1035
+ const running = this.inFlight.get(account.id);
1036
+ if (running) return running;
1037
+ 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));
1038
+ this.inFlight.set(account.id, promise);
1039
+ return promise;
1040
+ }
1041
+ isCacheValid(snapshot, now, refreshAheadMs) {
1042
+ if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
1043
+ const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
1044
+ const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
1045
+ return Number.isFinite(expiresAt) && expiresAt > now + ahead;
1046
+ }
1047
+ async fetchAccount(accountId, tokens) {
1048
+ let accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
1049
+ if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
1050
+ let response = await this.request(accountId, accessToken, tokens);
1051
+ if (response.status === 401) {
1052
+ const refreshed = await this.credentials.refreshAccountToken("kimi", accountId);
1053
+ if (!refreshed) return this.failureSnapshot(accountId, "kimi_usage_unauthorized", this.now());
1054
+ accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
1055
+ if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
1056
+ response = await this.request(accountId, accessToken, tokens);
1057
+ }
1058
+ if (response.status === 403) {
1059
+ const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "kimi_usage_unsupported");
1060
+ this.store.set(snapshot2);
1061
+ return snapshot2;
1062
+ }
1063
+ if (!response.ok) return this.failureSnapshot(accountId, "kimi_usage_http_error", this.now());
1064
+ let payload;
1065
+ try {
1066
+ payload = await response.json();
1067
+ } catch {
1068
+ return this.failureSnapshot(accountId, "kimi_usage_invalid_response", this.now());
1069
+ }
1070
+ const now = this.now();
1071
+ const windows = parseKimiUsagePayload(payload, now);
1072
+ const snapshot = {
1073
+ providerId: "kimi",
1074
+ accountId,
1075
+ source: "oauth-usage-api",
1076
+ observedAt: new Date(now).toISOString(),
1077
+ expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
1078
+ windows: windows.length > 0 ? windows : [
1079
+ { id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
1080
+ { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
1081
+ ],
1082
+ ...windows.length > 0 ? {} : { lastErrorCode: "kimi_usage_invalid_response" }
1083
+ };
1084
+ this.store.set(snapshot);
1085
+ return snapshot;
1086
+ }
1087
+ request(accountId, accessToken, tokens) {
1088
+ return this.fetchImpl(KIMI_USAGE_URL, {
1089
+ method: "GET",
1090
+ headers: {
1091
+ Authorization: `Bearer ${accessToken}`,
1092
+ Accept: "application/json",
1093
+ ...kimiFingerprintHeaders(tokens.deviceId)
1094
+ },
1095
+ signal: AbortSignal.timeout(15e3)
1096
+ }, accountId);
1097
+ }
1098
+ failureSnapshot(accountId, code, now) {
1099
+ const existing = this.store.get("kimi", accountId, now);
1100
+ const snapshot = existing ? {
1101
+ ...existing,
1102
+ expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
1103
+ windows: existing.windows.map((window) => ({
1104
+ ...window,
1105
+ state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
1106
+ })),
1107
+ lastErrorCode: code
1108
+ } : {
1109
+ providerId: "kimi",
1110
+ accountId,
1111
+ source: "oauth-usage-api",
1112
+ observedAt: new Date(now).toISOString(),
1113
+ expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
1114
+ windows: [
1115
+ { id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
1116
+ { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
1117
+ ],
1118
+ lastErrorCode: code
1119
+ };
1120
+ this.store.set(snapshot);
1121
+ return snapshot;
1122
+ }
1123
+ unsupportedSnapshot(accountId, now, code = "kimi_usage_unsupported_auth") {
1124
+ return {
1125
+ providerId: "kimi",
1126
+ accountId,
1127
+ source: "oauth-usage-api",
1128
+ observedAt: new Date(now).toISOString(),
1129
+ windows: [
1130
+ { id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unsupported" },
1131
+ { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
1132
+ ],
1133
+ lastErrorCode: code
1134
+ };
1135
+ }
1136
+ };
1137
+
1138
+ // src/allowance/GrokAllowanceCollector.ts
1139
+ import {
1140
+ getSharedAccountAllowanceStore as getSharedAccountAllowanceStore4
1141
+ } from "@omnicross/core/pipeline/AccountAllowanceStore";
1142
+ import { fetchUpstream as fetchUpstream4 } from "@omnicross/core/pipeline/upstreamFetch";
1143
+ var GROK_ALLOWANCE_CACHE_MS = 5 * 6e4;
1144
+ var GROK_BILLING_BASE = "https://cli-chat-proxy.grok.com";
1145
+ var GROK_BILLING_CREDITS_URL = `${GROK_BILLING_BASE}/v1/billing?format=credits`;
1146
+ var GROK_BILLING_MONTHLY_URL = `${GROK_BILLING_BASE}/v1/billing`;
1147
+ function isRecord2(value) {
1148
+ return !!value && typeof value === "object" && !Array.isArray(value);
1149
+ }
1150
+ function finiteNumber3(value) {
1151
+ if (value === null || value === void 0 || value === "") return void 0;
1152
+ const parsed = typeof value === "number" ? value : Number(value);
1153
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
1154
+ }
1155
+ function percent(value) {
1156
+ const parsed = finiteNumber3(value);
1157
+ return parsed !== void 0 && parsed <= 100 ? parsed : void 0;
1158
+ }
1159
+ function onDemandAmount(value) {
1160
+ return isRecord2(value) ? finiteNumber3(value["val"]) : void 0;
1161
+ }
1162
+ function confirmsNoMonthlyQuota(raw) {
1163
+ const limit = onDemandAmount(raw["monthlyLimit"]);
1164
+ if (limit !== void 0) return limit === 0;
1165
+ return parseWeeklyConfig(raw)?.inferredPercent === true;
1166
+ }
1167
+ function parseWeeklyConfig(raw) {
1168
+ const period = isRecord2(raw["currentPeriod"]) ? raw["currentPeriod"] : void 0;
1169
+ if (!period) return null;
1170
+ const start = typeof period["start"] === "string" ? Date.parse(period["start"]) : Number.NaN;
1171
+ const end = typeof period["end"] === "string" ? Date.parse(period["end"]) : Number.NaN;
1172
+ const type = typeof period["type"] === "string" ? period["type"] : "";
1173
+ if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
1174
+ if (!type.toUpperCase().includes("WEEK")) return null;
1175
+ const inferred = raw["creditUsagePercent"] === void 0 || raw["creditUsagePercent"] === null;
1176
+ let creditUsagePercent;
1177
+ if (inferred) {
1178
+ creditUsagePercent = end > Date.now() ? 0 : void 0;
1179
+ } else {
1180
+ creditUsagePercent = percent(raw["creditUsagePercent"]);
1181
+ }
1182
+ if (creditUsagePercent === void 0) return null;
1183
+ return {
1184
+ creditUsagePercent,
1185
+ inferredPercent: inferred,
1186
+ resetsAtMs: end,
1187
+ unified: raw["isUnifiedBillingUser"] === true
1188
+ };
1189
+ }
1190
+ function parseMonthlyConfig(raw) {
1191
+ const start = typeof raw["billingPeriodStart"] === "string" ? Date.parse(raw["billingPeriodStart"]) : Number.NaN;
1192
+ const end = typeof raw["billingPeriodEnd"] === "string" ? Date.parse(raw["billingPeriodEnd"]) : Number.NaN;
1193
+ if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
1194
+ const limit = onDemandAmount(raw["monthlyLimit"]);
1195
+ const used = onDemandAmount(raw["used"]);
1196
+ if (limit === void 0 || limit <= 0 || used === void 0) return null;
1197
+ return { used, limit, periodStartMs: start, periodEndMs: end };
1198
+ }
1199
+ function secondsUntil4(instant, now) {
1200
+ if (!instant) return void 0;
1201
+ return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
1202
+ }
1203
+ var MINUTE_MS2 = 6e4;
1204
+ var DAY_MS2 = 864e5;
1205
+ var WEEK_MINUTES = 7 * 24 * 60;
1206
+ function weeklyWindow(config, now) {
1207
+ const resetsAt = new Date(config.resetsAtMs).toISOString();
1208
+ return {
1209
+ id: "seven-day",
1210
+ label: "7 days",
1211
+ scope: "all",
1212
+ usedPercent: config.creditUsagePercent,
1213
+ windowMinutes: WEEK_MINUTES,
1214
+ resetsAt,
1215
+ remainingSeconds: secondsUntil4(resetsAt, now),
1216
+ state: "fresh"
1217
+ };
1218
+ }
1219
+ function monthlyWindow(config, now) {
1220
+ const resetsAt = new Date(config.periodEndMs).toISOString();
1221
+ const days = Math.max(1, Math.round((config.periodEndMs - config.periodStartMs) / DAY_MS2));
1222
+ return {
1223
+ id: "thirty-day",
1224
+ label: days === 30 || days === 31 ? "30 days" : `${days} days`,
1225
+ scope: "all",
1226
+ usedPercent: Math.round(Math.min(100, config.used / config.limit * 100) * 10) / 10,
1227
+ windowMinutes: Math.round((config.periodEndMs - config.periodStartMs) / MINUTE_MS2),
1228
+ resetsAt,
1229
+ remainingSeconds: secondsUntil4(resetsAt, now),
1230
+ state: "fresh"
1231
+ };
1232
+ }
1233
+ function onDemandWindow(raw) {
1234
+ const cap = onDemandAmount(raw["onDemandCap"]);
1235
+ const used = onDemandAmount(raw["onDemandUsed"]);
1236
+ if (cap === void 0 || cap <= 0 || used === void 0) return null;
1237
+ return {
1238
+ id: "on-demand",
1239
+ label: "On-demand",
1240
+ scope: "all",
1241
+ usedPercent: Math.round(Math.min(100, used / cap * 100) * 10) / 10,
1242
+ state: "fresh"
1243
+ };
1244
+ }
1245
+ async function probeBilling(url, accessToken, accountId, fetchImpl) {
1246
+ try {
1247
+ const response = await fetchImpl(url, {
1248
+ method: "GET",
1249
+ headers: {
1250
+ Authorization: `Bearer ${accessToken}`,
1251
+ Accept: "application/json",
1252
+ "X-XAI-Token-Auth": "xai-grok-cli"
1253
+ },
1254
+ redirect: "error",
1255
+ signal: AbortSignal.timeout(15e3)
1256
+ }, accountId);
1257
+ if (!response.ok) return { status: response.status, payload: null };
1258
+ const payload = await response.json();
1259
+ return { status: response.status, payload: isRecord2(payload) ? payload : null };
1260
+ } catch {
1261
+ return { status: 0, payload: null };
1262
+ }
1263
+ }
1264
+ function parseGrokBillingPayloads(creditsPayload, monthlyPayload, now) {
1265
+ const creditsConfig = isRecord2(creditsPayload?.["config"]) ? creditsPayload["config"] : null;
1266
+ const monthlyConfig = isRecord2(monthlyPayload?.["config"]) ? monthlyPayload["config"] : null;
1267
+ let weekly = creditsConfig ? parseWeeklyConfig(creditsConfig) : null;
1268
+ const unifiedFlag = creditsConfig?.["isUnifiedBillingUser"] === true;
1269
+ let monthly = monthlyConfig ? parseMonthlyConfig(monthlyConfig) : null;
1270
+ if (weekly?.inferredPercent && unifiedFlag) {
1271
+ if (monthly) {
1272
+ weekly = null;
1273
+ } else if (!monthlyConfig || !confirmsNoMonthlyQuota(monthlyConfig)) {
1274
+ weekly = null;
1275
+ }
1276
+ }
1277
+ const windows = [];
1278
+ if (weekly) windows.push(weeklyWindow(weekly, now));
1279
+ if (monthly) windows.push(monthlyWindow(monthly, now));
1280
+ const onDemandSource = monthly && monthlyConfig ? monthlyConfig : creditsConfig;
1281
+ const onDemand = onDemandSource ? onDemandWindow(onDemandSource) : null;
1282
+ if (onDemand) windows.push(onDemand);
1283
+ return windows.length > 0 ? windows : null;
1284
+ }
1285
+ var GrokAllowanceCollector = class {
1286
+ constructor(credentials, store = getSharedAccountAllowanceStore4(), fetchImpl = (url, init, accountId) => fetchUpstream4(url, init, { providerId: "grok", accountId, redactBodies: true }), now = Date.now) {
1287
+ this.credentials = credentials;
1288
+ this.store = store;
1289
+ this.fetchImpl = fetchImpl;
1290
+ this.now = now;
1291
+ }
1292
+ credentials;
1293
+ store;
1294
+ fetchImpl;
1295
+ now;
1296
+ inFlight = /* @__PURE__ */ new Map();
1297
+ async collectMany(accounts, options = {}) {
1298
+ const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
1299
+ return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
1300
+ }
1301
+ collect(account, options = {}) {
1302
+ const now = this.now();
1303
+ if (account.tokens.authMethod !== "oauth") {
1304
+ const existing = this.store.get("grok", account.id, now);
1305
+ if (existing?.windows.every((window) => window.state === "unsupported")) {
1306
+ return Promise.resolve(existing);
1307
+ }
1308
+ const snapshot = this.unsupportedSnapshot(account.id, now);
1309
+ this.store.set(snapshot);
1310
+ return Promise.resolve(snapshot);
1311
+ }
1312
+ const cached = this.store.get("grok", account.id, now);
1313
+ if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
1314
+ return Promise.resolve(cached);
1315
+ }
1316
+ const running = this.inFlight.get(account.id);
1317
+ if (running) return running;
1318
+ const promise = this.fetchAccount(account.id).catch(() => this.failureSnapshot(account.id, "grok_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
1319
+ this.inFlight.set(account.id, promise);
1320
+ return promise;
1321
+ }
1322
+ isCacheValid(snapshot, now, refreshAheadMs) {
1323
+ if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
1324
+ const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
1325
+ const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
1326
+ return Number.isFinite(expiresAt) && expiresAt > now + ahead;
1327
+ }
1328
+ async fetchAccount(accountId) {
1329
+ const probe = async () => {
1330
+ const accessToken = await this.credentials.getAccessTokenForAccount("grok", accountId);
1331
+ if (!accessToken) return { unauthorized: true, windows: null };
1332
+ const credits = await probeBilling(GROK_BILLING_CREDITS_URL, accessToken, accountId, this.fetchImpl);
1333
+ if (credits.status === 401 || credits.status === 403) return { unauthorized: true, windows: null };
1334
+ const creditsConfig = isRecord2(credits.payload?.["config"]) ? credits.payload["config"] : null;
1335
+ const weekly = creditsConfig ? parseWeeklyConfig(creditsConfig) : null;
1336
+ const monthly = !weekly || creditsConfig?.["isUnifiedBillingUser"] === true ? await probeBilling(GROK_BILLING_MONTHLY_URL, accessToken, accountId, this.fetchImpl) : { status: 200, payload: null };
1337
+ if (monthly.status === 401 || monthly.status === 403) return { unauthorized: true, windows: null };
1338
+ return {
1339
+ unauthorized: false,
1340
+ windows: parseGrokBillingPayloads(credits.payload, monthly.payload, this.now())
1341
+ };
1342
+ };
1343
+ let result = await probe();
1344
+ if (result.unauthorized) {
1345
+ const refreshed = await this.credentials.refreshAccountToken("grok", accountId);
1346
+ if (!refreshed) return this.failureSnapshot(accountId, "grok_usage_unauthorized", this.now());
1347
+ result = await probe();
1348
+ if (result.unauthorized) {
1349
+ return this.failureSnapshot(accountId, "grok_usage_unauthorized", this.now());
1350
+ }
1351
+ }
1352
+ const now = this.now();
1353
+ if (result.windows && result.windows.length > 0) {
1354
+ const snapshot = {
1355
+ providerId: "grok",
1356
+ accountId,
1357
+ source: "oauth-usage-api",
1358
+ observedAt: new Date(now).toISOString(),
1359
+ expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
1360
+ windows: result.windows
1361
+ };
1362
+ this.store.set(snapshot);
1363
+ return snapshot;
1364
+ }
1365
+ return this.failureSnapshot(accountId, "grok_usage_invalid_response", now);
1366
+ }
1367
+ failureSnapshot(accountId, code, now) {
1368
+ const existing = this.store.get("grok", accountId, now);
1369
+ const snapshot = existing ? {
1370
+ ...existing,
1371
+ expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
1372
+ windows: existing.windows.map((window) => ({
1373
+ ...window,
1374
+ state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
1375
+ })),
1376
+ lastErrorCode: code
1377
+ } : {
1378
+ providerId: "grok",
1379
+ accountId,
1380
+ source: "oauth-usage-api",
1381
+ observedAt: new Date(now).toISOString(),
1382
+ expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
1383
+ windows: [
1384
+ { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" },
1385
+ { id: "thirty-day", label: "30 days", scope: "all", usedPercent: null, state: "unavailable" }
1386
+ ],
1387
+ lastErrorCode: code
1388
+ };
1389
+ this.store.set(snapshot);
1390
+ return snapshot;
1391
+ }
1392
+ unsupportedSnapshot(accountId, now) {
1393
+ return {
1394
+ providerId: "grok",
1395
+ accountId,
1396
+ source: "oauth-usage-api",
1397
+ observedAt: new Date(now).toISOString(),
1398
+ windows: [
1399
+ { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" },
1400
+ { id: "thirty-day", label: "30 days", scope: "all", usedPercent: null, state: "unsupported" }
1401
+ ],
1402
+ lastErrorCode: "grok_usage_unsupported_auth"
1403
+ };
1404
+ }
1405
+ };
1406
+
1407
+ // src/allowance/CopilotAllowanceCollector.ts
1408
+ import {
1409
+ getSharedAccountAllowanceStore as getSharedAccountAllowanceStore5
1410
+ } from "@omnicross/core/pipeline/AccountAllowanceStore";
1411
+ import { fetchUpstream as fetchUpstream5 } from "@omnicross/core/pipeline/upstreamFetch";
1412
+ import { COPILOT_GITHUB_HEADERS, copilotGitHubApiBase } from "@omnicross/subscriptions";
1413
+ var COPILOT_ALLOWANCE_CACHE_MS = 5 * 6e4;
1414
+ function isRecord3(value) {
1415
+ return !!value && typeof value === "object" && !Array.isArray(value);
1416
+ }
1417
+ function finiteNumber4(value) {
1418
+ if (value === null || value === void 0 || value === "") return void 0;
1419
+ const parsed = typeof value === "number" ? value : Number(value);
1420
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
1421
+ }
1422
+ function booleanValue(value) {
1423
+ if (typeof value === "boolean") return value;
1424
+ if (value === "true") return true;
1425
+ if (value === "false") return false;
1426
+ return void 0;
1427
+ }
1428
+ function parseQuotaDetail(value) {
1429
+ if (!isRecord3(value)) return null;
1430
+ const entitlement = finiteNumber4(value["entitlement"]);
1431
+ const remaining = finiteNumber4(value["remaining"]);
1432
+ const percentRemaining = finiteNumber4(value["percent_remaining"]);
1433
+ const unlimited = booleanValue(value["unlimited"]);
1434
+ if (entitlement === void 0 || remaining === void 0 || percentRemaining === void 0 || unlimited === void 0) {
1435
+ return null;
1436
+ }
1437
+ return { entitlement, remaining, percentRemaining, unlimited };
1438
+ }
1439
+ function secondsUntil5(instant, now) {
1440
+ if (!instant) return void 0;
1441
+ return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
1442
+ }
1443
+ function parseCopilotUserPayload(payload, now) {
1444
+ if (!isRecord3(payload)) return null;
1445
+ const snapshots = isRecord3(payload["quota_snapshots"]) ? payload["quota_snapshots"] : void 0;
1446
+ if (!snapshots) return null;
1447
+ const resetRaw = payload["quota_reset_date"];
1448
+ const resetsAt = typeof resetRaw === "string" && resetRaw.trim() && Number.isFinite(Date.parse(resetRaw)) ? new Date(Date.parse(resetRaw)).toISOString() : void 0;
1449
+ const windows = [];
1450
+ const premium = parseQuotaDetail(snapshots["premium_interactions"]);
1451
+ if (premium) {
1452
+ 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;
1453
+ if (usedPercent !== null) {
1454
+ windows.push({
1455
+ id: "thirty-day",
1456
+ label: "Monthly",
1457
+ scope: "all",
1458
+ usedPercent,
1459
+ windowMinutes: 30 * 24 * 60,
1460
+ ...resetsAt !== void 0 ? { resetsAt } : {},
1461
+ remainingSeconds: secondsUntil5(resetsAt, now),
1462
+ state: "fresh"
1463
+ });
800
1464
  }
801
- const resetsAtMs = parseResetMs(windowData && parseResetMs(windowData, now) !== void 0 ? windowData : data, now);
802
- return { used, limit, remaining, ...resetsAtMs !== void 0 ? { resetsAtMs } : {}, ...windowDurationMs !== void 0 ? { windowDurationMs } : {} };
803
- };
804
- if (isRecord(payload["usage"])) {
805
- const row = rowFrom(payload["usage"]);
806
- const window = windowFromRow({ ...row, resetsAtMs: row.resetsAtMs }, { id: "seven-day", label: "7 days", minutes: 10080 }, now);
807
- byId.set("seven-day", window);
808
1465
  }
809
- if (Array.isArray(payload["limits"])) {
810
- for (const item of payload["limits"]) {
811
- if (!isRecord(item)) continue;
812
- const detail = isRecord(item["detail"]) ? item["detail"] : item;
813
- const row = rowFrom(detail);
814
- const canonical = row.windowDurationMs !== void 0 ? canonicalWindow(row.windowDurationMs) : void 0;
815
- if (!canonical) continue;
816
- const window = windowFromRow(row, canonical, now);
817
- const existing = byId.get(canonical.id);
818
- if (!existing || (window.usedPercent ?? 0) > (existing.usedPercent ?? 0)) {
819
- byId.set(canonical.id, window);
820
- }
821
- }
1466
+ const chat = parseQuotaDetail(snapshots["chat"]);
1467
+ if (chat && !chat.unlimited && chat.entitlement > 0) {
1468
+ const usedPercent = Math.round(Math.min(100, (chat.entitlement - chat.remaining) / chat.entitlement * 100) * 10) / 10;
1469
+ windows.push({
1470
+ id: "chat-monthly",
1471
+ label: "Chat (monthly)",
1472
+ scope: "all",
1473
+ usedPercent,
1474
+ windowMinutes: 30 * 24 * 60,
1475
+ ...resetsAt !== void 0 ? { resetsAt } : {},
1476
+ remainingSeconds: secondsUntil5(resetsAt, now),
1477
+ state: "fresh"
1478
+ });
822
1479
  }
823
- return [...byId.values()].sort((a, b) => (a.windowMinutes ?? Infinity) - (b.windowMinutes ?? Infinity)).slice(0, 4);
1480
+ return windows.length > 0 ? windows : null;
824
1481
  }
825
- var KimiAllowanceCollector = class {
826
- constructor(credentials, store = getSharedAccountAllowanceStore3(), fetchImpl = (url, init, accountId) => fetchUpstream3(url, init, { providerId: "kimi", accountId, redactBodies: true }), now = Date.now) {
1482
+ function githubApiBase(tokens) {
1483
+ return copilotGitHubApiBase(tokens.enterpriseUrl);
1484
+ }
1485
+ var CopilotAllowanceCollector = class {
1486
+ constructor(credentials, store = getSharedAccountAllowanceStore5(), fetchImpl = (url, init, accountId) => fetchUpstream5(url, init, { providerId: "copilot", accountId, redactBodies: true }), now = Date.now) {
827
1487
  this.credentials = credentials;
828
1488
  this.store = store;
829
1489
  this.fetchImpl = fetchImpl;
@@ -835,13 +1495,15 @@ var KimiAllowanceCollector = class {
835
1495
  now;
836
1496
  inFlight = /* @__PURE__ */ new Map();
837
1497
  async collectMany(accounts, options = {}) {
838
- const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
1498
+ const settled = await Promise.allSettled(
1499
+ accounts.map((account) => this.collect(account, options))
1500
+ );
839
1501
  return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
840
1502
  }
841
1503
  collect(account, options = {}) {
842
1504
  const now = this.now();
843
1505
  if (account.tokens.authMethod !== "oauth") {
844
- const existing = this.store.get("kimi", account.id, now);
1506
+ const existing = this.store.get("copilot", account.id, now);
845
1507
  if (existing?.windows.every((window) => window.state === "unsupported")) {
846
1508
  return Promise.resolve(existing);
847
1509
  }
@@ -849,13 +1511,13 @@ var KimiAllowanceCollector = class {
849
1511
  this.store.set(snapshot);
850
1512
  return Promise.resolve(snapshot);
851
1513
  }
852
- const cached = this.store.get("kimi", account.id, now);
1514
+ const cached = this.store.get("copilot", account.id, now);
853
1515
  if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
854
1516
  return Promise.resolve(cached);
855
1517
  }
856
1518
  const running = this.inFlight.get(account.id);
857
1519
  if (running) return running;
858
- 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));
1520
+ 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));
859
1521
  this.inFlight.set(account.id, promise);
860
1522
  return promise;
861
1523
  }
@@ -866,101 +1528,97 @@ var KimiAllowanceCollector = class {
866
1528
  return Number.isFinite(expiresAt) && expiresAt > now + ahead;
867
1529
  }
868
1530
  async fetchAccount(accountId, tokens) {
869
- let accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
870
- if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
1531
+ let accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
1532
+ if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
871
1533
  let response = await this.request(accountId, accessToken, tokens);
872
- if (response.status === 401) {
873
- const refreshed = await this.credentials.refreshAccountToken("kimi", accountId);
874
- if (!refreshed) return this.failureSnapshot(accountId, "kimi_usage_unauthorized", this.now());
875
- accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
876
- if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
1534
+ if (response.status === 401 || response.status === 403) {
1535
+ const refreshed = await this.credentials.refreshAccountToken("copilot", accountId);
1536
+ if (!refreshed) return this.failureSnapshot(accountId, "copilot_usage_unauthorized", this.now());
1537
+ accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
1538
+ if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
877
1539
  response = await this.request(accountId, accessToken, tokens);
1540
+ if (response.status === 401 || response.status === 403) {
1541
+ return this.failureSnapshot(accountId, "copilot_usage_unauthorized", this.now());
1542
+ }
878
1543
  }
879
- if (response.status === 403) {
880
- const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "kimi_usage_unsupported");
881
- this.store.set(snapshot2);
882
- return snapshot2;
883
- }
884
- if (!response.ok) return this.failureSnapshot(accountId, "kimi_usage_http_error", this.now());
1544
+ if (!response.ok) return this.failureSnapshot(accountId, "copilot_usage_http_error", this.now());
885
1545
  let payload;
886
1546
  try {
887
1547
  payload = await response.json();
888
1548
  } catch {
889
- return this.failureSnapshot(accountId, "kimi_usage_invalid_response", this.now());
1549
+ return this.failureSnapshot(accountId, "copilot_usage_invalid_response", this.now());
890
1550
  }
891
1551
  const now = this.now();
892
- const windows = parseKimiUsagePayload(payload, now);
1552
+ const windows = parseCopilotUserPayload(payload, now);
893
1553
  const snapshot = {
894
- providerId: "kimi",
1554
+ providerId: "copilot",
895
1555
  accountId,
896
1556
  source: "oauth-usage-api",
897
1557
  observedAt: new Date(now).toISOString(),
898
- expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
899
- windows: windows.length > 0 ? windows : [
900
- { id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
901
- { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
1558
+ expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
1559
+ windows: windows ?? [
1560
+ { id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
902
1561
  ],
903
- ...windows.length > 0 ? {} : { lastErrorCode: "kimi_usage_invalid_response" }
1562
+ ...windows ? {} : { lastErrorCode: "copilot_usage_invalid_response" }
904
1563
  };
905
1564
  this.store.set(snapshot);
906
1565
  return snapshot;
907
1566
  }
908
1567
  request(accountId, accessToken, tokens) {
909
- return this.fetchImpl(KIMI_USAGE_URL, {
1568
+ return this.fetchImpl(`${githubApiBase(tokens)}/copilot_internal/user`, {
910
1569
  method: "GET",
911
1570
  headers: {
912
1571
  Authorization: `Bearer ${accessToken}`,
913
1572
  Accept: "application/json",
914
- ...kimiFingerprintHeaders(tokens.deviceId)
1573
+ "Content-Type": "application/json",
1574
+ ...COPILOT_GITHUB_HEADERS
915
1575
  },
916
1576
  signal: AbortSignal.timeout(15e3)
917
1577
  }, accountId);
918
1578
  }
919
1579
  failureSnapshot(accountId, code, now) {
920
- const existing = this.store.get("kimi", accountId, now);
1580
+ const existing = this.store.get("copilot", accountId, now);
921
1581
  const snapshot = existing ? {
922
1582
  ...existing,
923
- expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
1583
+ expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
924
1584
  windows: existing.windows.map((window) => ({
925
1585
  ...window,
926
1586
  state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
927
1587
  })),
928
1588
  lastErrorCode: code
929
1589
  } : {
930
- providerId: "kimi",
1590
+ providerId: "copilot",
931
1591
  accountId,
932
1592
  source: "oauth-usage-api",
933
1593
  observedAt: new Date(now).toISOString(),
934
- expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
1594
+ expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
935
1595
  windows: [
936
- { id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
937
- { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
1596
+ { id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
938
1597
  ],
939
1598
  lastErrorCode: code
940
1599
  };
941
1600
  this.store.set(snapshot);
942
1601
  return snapshot;
943
1602
  }
944
- unsupportedSnapshot(accountId, now, code = "kimi_usage_unsupported_auth") {
1603
+ unsupportedSnapshot(accountId, now) {
945
1604
  return {
946
- providerId: "kimi",
1605
+ providerId: "copilot",
947
1606
  accountId,
948
1607
  source: "oauth-usage-api",
949
1608
  observedAt: new Date(now).toISOString(),
950
1609
  windows: [
951
- { id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unsupported" },
952
- { id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
1610
+ { id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unsupported" }
953
1611
  ],
954
- lastErrorCode: code
1612
+ lastErrorCode: "copilot_usage_unsupported_auth"
955
1613
  };
956
1614
  }
957
1615
  };
958
1616
 
959
1617
  // src/allowance/OpenCodeGoAllowanceCollector.ts
960
1618
  import {
961
- getSharedAccountAllowanceStore as getSharedAccountAllowanceStore4
1619
+ getSharedAccountAllowanceStore as getSharedAccountAllowanceStore6
962
1620
  } from "@omnicross/core/pipeline/AccountAllowanceStore";
963
- import { fetchUpstream as fetchUpstream4 } from "@omnicross/core/pipeline/upstreamFetch";
1621
+ import { fetchUpstream as fetchUpstream6 } from "@omnicross/core/pipeline/upstreamFetch";
964
1622
  import { normalizeOpenCodeGoBaseUrl } from "@omnicross/subscriptions";
965
1623
  var OPENCODEGO_ALLOWANCE_CACHE_MS = 5 * 6e4;
966
1624
  var OPENCODEGO_DEFAULT_GO_BASE = "https://opencode.ai/zen/go";
@@ -974,7 +1632,7 @@ function isoInstant2(value) {
974
1632
  const time = Date.parse(value);
975
1633
  return Number.isFinite(time) ? new Date(time).toISOString() : void 0;
976
1634
  }
977
- function secondsUntil4(instant, now) {
1635
+ function secondsUntil6(instant, now) {
978
1636
  if (!instant) return void 0;
979
1637
  return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
980
1638
  }
@@ -989,12 +1647,12 @@ function windowFromPayload3(id, label, minutes, payload, now) {
989
1647
  usedPercent,
990
1648
  windowMinutes: minutes,
991
1649
  ...resetsAt !== void 0 ? { resetsAt } : {},
992
- remainingSeconds: secondsUntil4(resetsAt, now),
1650
+ remainingSeconds: secondsUntil6(resetsAt, now),
993
1651
  state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
994
1652
  };
995
1653
  }
996
1654
  var OpenCodeGoAllowanceCollector = class {
997
- constructor(credentials, store = getSharedAccountAllowanceStore4(), fetchImpl = (url, init, accountId) => fetchUpstream4(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
1655
+ constructor(credentials, store = getSharedAccountAllowanceStore6(), fetchImpl = (url, init, accountId) => fetchUpstream6(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
998
1656
  this.credentials = credentials;
999
1657
  this.store = store;
1000
1658
  this.fetchImpl = fetchImpl;
@@ -1099,7 +1757,7 @@ function codexUnavailable(accountId, now) {
1099
1757
  };
1100
1758
  }
1101
1759
  var AccountAllowanceService = class {
1102
- constructor(credentials, store = getSharedAccountAllowanceStore5(), collector, codexCollector, kimiCollector, opencodegoCollector, now = Date.now) {
1760
+ constructor(credentials, store = getSharedAccountAllowanceStore7(), collector, codexCollector, kimiCollector, opencodegoCollector, grokCollector, copilotCollector, now = Date.now) {
1103
1761
  this.credentials = credentials;
1104
1762
  this.store = store;
1105
1763
  this.now = now;
@@ -1107,6 +1765,8 @@ var AccountAllowanceService = class {
1107
1765
  this.codexCollector = codexCollector ?? new CodexAllowanceCollector(credentials, store);
1108
1766
  this.kimiCollector = kimiCollector ?? new KimiAllowanceCollector(credentials, store);
1109
1767
  this.opencodegoCollector = opencodegoCollector ?? new OpenCodeGoAllowanceCollector(credentials, store);
1768
+ this.grokCollector = grokCollector ?? new GrokAllowanceCollector(credentials, store);
1769
+ this.copilotCollector = copilotCollector ?? new CopilotAllowanceCollector(credentials, store);
1110
1770
  }
1111
1771
  credentials;
1112
1772
  store;
@@ -1114,6 +1774,8 @@ var AccountAllowanceService = class {
1114
1774
  claudeCollector;
1115
1775
  codexCollector;
1116
1776
  kimiCollector;
1777
+ grokCollector;
1778
+ copilotCollector;
1117
1779
  opencodegoCollector;
1118
1780
  /**
1119
1781
  * Read all/filtered snapshots. Claude's and Codex's five-minute caches are
@@ -1148,11 +1810,23 @@ var AccountAllowanceService = class {
1148
1810
  (account) => !filter.accountId || account.id === filter.accountId
1149
1811
  );
1150
1812
  if (wantsOpenCodeGo) await this.opencodegoCollector.collectMany(opencodegoAccounts);
1813
+ const wantsGrok = !filter.providerId || filter.providerId === "grok";
1814
+ const grokAccounts = (config.grokAccounts ?? []).filter(
1815
+ (account) => !filter.accountId || account.id === filter.accountId
1816
+ );
1817
+ if (wantsGrok) await this.grokCollector.collectMany(grokAccounts);
1818
+ const wantsCopilot = !filter.providerId || filter.providerId === "copilot";
1819
+ const copilotAccounts = (config.copilotAccounts ?? []).filter(
1820
+ (account) => !filter.accountId || account.id === filter.accountId
1821
+ );
1822
+ if (wantsCopilot) await this.copilotCollector.collectMany(copilotAccounts);
1151
1823
  const known = /* @__PURE__ */ new Set();
1152
1824
  if (wantsClaude) for (const account of claudeAccounts) known.add(`claude\0${account.id}`);
1153
1825
  if (wantsCodex) for (const account of codexAccounts) known.add(`codex\0${account.id}`);
1154
1826
  if (wantsKimi) for (const account of kimiAccounts) known.add(`kimi\0${account.id}`);
1155
1827
  if (wantsOpenCodeGo) for (const account of opencodegoAccounts) known.add(`opencodego\0${account.id}`);
1828
+ if (wantsGrok) for (const account of grokAccounts) known.add(`grok\0${account.id}`);
1829
+ if (wantsCopilot) for (const account of copilotAccounts) known.add(`copilot\0${account.id}`);
1156
1830
  return this.store.list(filter).filter((snapshot) => known.has(`${snapshot.providerId}\0${snapshot.accountId}`));
1157
1831
  }
1158
1832
  knownAccounts(config) {
@@ -1160,7 +1834,9 @@ var AccountAllowanceService = class {
1160
1834
  ...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
1161
1835
  ...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id })),
1162
1836
  ...(config.kimiAccounts ?? []).map((account) => ({ providerId: "kimi", accountId: account.id })),
1163
- ...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id }))
1837
+ ...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id })),
1838
+ ...(config.grokAccounts ?? []).map((account) => ({ providerId: "grok", accountId: account.id })),
1839
+ ...(config.copilotAccounts ?? []).map((account) => ({ providerId: "copilot", accountId: account.id }))
1164
1840
  ];
1165
1841
  }
1166
1842
  /** Force-refresh Claude usage for one account or every stored Claude account. */
@@ -1203,6 +1879,24 @@ var AccountAllowanceService = class {
1203
1879
  );
1204
1880
  return this.kimiCollector.collectMany(accounts, { force: true });
1205
1881
  }
1882
+ /** Force-refresh Copilot usage (copilot_internal/user) for one/all accounts. */
1883
+ async refreshCopilot(accountId) {
1884
+ const config = await this.credentials.getFullConfig();
1885
+ this.store.pruneToKnownAccounts(this.knownAccounts(config));
1886
+ const accounts = (config.copilotAccounts ?? []).filter(
1887
+ (account) => !accountId || account.id === accountId
1888
+ );
1889
+ return this.copilotCollector.collectMany(accounts, { force: true });
1890
+ }
1891
+ /** Force-refresh Grok usage (CLI billing proxy) for one/all accounts. */
1892
+ async refreshGrok(accountId) {
1893
+ const config = await this.credentials.getFullConfig();
1894
+ this.store.pruneToKnownAccounts(this.knownAccounts(config));
1895
+ const accounts = (config.grokAccounts ?? []).filter(
1896
+ (account) => !accountId || account.id === accountId
1897
+ );
1898
+ return this.grokCollector.collectMany(accounts, { force: true });
1899
+ }
1206
1900
  /**
1207
1901
  * Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
1208
1902
  * collectors preserve their cache + per-account in-flight coalescing; a tick
@@ -1217,6 +1911,8 @@ var AccountAllowanceService = class {
1217
1911
  await this.codexCollector.collectMany(config.codexAccounts ?? [], { refreshAheadMs });
1218
1912
  await this.kimiCollector.collectMany(config.kimiAccounts ?? [], { refreshAheadMs });
1219
1913
  await this.opencodegoCollector.collectMany(config.opencodegoAccounts ?? [], { refreshAheadMs });
1914
+ await this.grokCollector.collectMany(config.grokAccounts ?? [], { refreshAheadMs });
1915
+ await this.copilotCollector.collectMany(config.copilotAccounts ?? [], { refreshAheadMs });
1220
1916
  }
1221
1917
  /** Remove a cache row as soon as an account is deleted by the admin path. */
1222
1918
  removeAccountSnapshot(providerId, accountId) {
@@ -1653,7 +2349,8 @@ import {
1653
2349
  } from "@omnicross/contracts/image-generation-types";
1654
2350
  import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling2 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
1655
2351
  import { getSharedAccountHealth } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
1656
- import { fetchUpstream as fetchUpstream5 } from "@omnicross/core/pipeline/upstreamFetch";
2352
+ import { fetchUpstream as fetchUpstream7 } from "@omnicross/core/pipeline/upstreamFetch";
2353
+ import { mergeExtraHeaders } from "@omnicross/core";
1657
2354
 
1658
2355
  // src/image-generation/imagesConfigValidation.ts
1659
2356
  import { validateImagesServerConfig } from "@omnicross/core/outbound-api";
@@ -1966,6 +2663,7 @@ async function applyServerConfigTransaction(current, next, deps) {
1966
2663
 
1967
2664
  // src/config.ts
1968
2665
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
2666
+ import { EXTRA_HEADER_RESERVED_NAMES } from "@omnicross/core";
1969
2667
 
1970
2668
  // src/secrets/envelope.ts
1971
2669
  import { createCipheriv, createDecipheriv, randomBytes as randomBytes2 } from "crypto";
@@ -2377,6 +3075,18 @@ var FORMAT_AXIS_TRANSFORMERS = [
2377
3075
  "openai-response",
2378
3076
  "gemini-code-assist"
2379
3077
  ];
3078
+ function validateExtraHeaders(raw) {
3079
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
3080
+ const reserved = EXTRA_HEADER_RESERVED_NAMES;
3081
+ const out = {};
3082
+ for (const [name, value] of Object.entries(raw)) {
3083
+ if (!name.trim()) continue;
3084
+ if (typeof value !== "string") continue;
3085
+ if (reserved.has(name.toLowerCase())) continue;
3086
+ out[name] = value;
3087
+ }
3088
+ return Object.keys(out).length > 0 ? out : void 0;
3089
+ }
2380
3090
  function validateApiKeys(raw) {
2381
3091
  if (!Array.isArray(raw)) return void 0;
2382
3092
  const out = [];
@@ -2590,6 +3300,9 @@ function validateProvider(raw, index) {
2590
3300
  apiVersion,
2591
3301
  maxConcurrency,
2592
3302
  modelsEndpoint,
3303
+ // Static extra headers: load-guard (reserved names dropped), collapse-to-
3304
+ // undefined; enforced by the outbound header funnel + admin probes.
3305
+ extraHeaders: validateExtraHeaders(p["extraHeaders"]),
2593
3306
  // Provider transformer config (app-parity child 5): load-guard, collapse-to-
2594
3307
  // undefined; non-secret; ENFORCED via resolveTransformerChain (parity-2 child 2).
2595
3308
  // Format-axis entries are stripped by `migrateFormatAxis` — `use[]` is the
@@ -3559,7 +4272,8 @@ function listMappablePresets() {
3559
4272
  description: preset.description,
3560
4273
  features: preset.features,
3561
4274
  website: preset.website,
3562
- modelsEndpoint: preset.modelsEndpoint
4275
+ modelsEndpoint: preset.modelsEndpoint,
4276
+ extraHeaders: preset.extraHeaders
3563
4277
  });
3564
4278
  }
3565
4279
  return { mappable, excluded };
@@ -3731,7 +4445,9 @@ var VALID_PROVIDER_IDS = [
3731
4445
  "codex",
3732
4446
  "gemini",
3733
4447
  "opencodego",
3734
- "kimi"
4448
+ "kimi",
4449
+ "grok",
4450
+ "copilot"
3735
4451
  ];
3736
4452
  function asSubscriptionProviderId(id) {
3737
4453
  return VALID_PROVIDER_IDS.includes(id) ? id : null;
@@ -3879,6 +4595,40 @@ function validateKimi(body) {
3879
4595
  copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "deviceId", "lastRefreshedAt", "errorMessage"]);
3880
4596
  return out;
3881
4597
  }
4598
+ function validateGrok(body) {
4599
+ const authMethod = str(body["authMethod"]);
4600
+ const status = str(body["status"]);
4601
+ if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
4602
+ if (!status || !TOKEN_STATUSES.has(status)) return null;
4603
+ const out = {
4604
+ authMethod,
4605
+ status
4606
+ };
4607
+ copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "lastRefreshedAt", "errorMessage"]);
4608
+ return out;
4609
+ }
4610
+ function validateCopilot(body) {
4611
+ const authMethod = str(body["authMethod"]);
4612
+ const status = str(body["status"]);
4613
+ if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
4614
+ if (!status || !TOKEN_STATUSES.has(status)) return null;
4615
+ const out = {
4616
+ authMethod,
4617
+ status
4618
+ };
4619
+ copyOptional(out, body, [
4620
+ "accessToken",
4621
+ "refreshToken",
4622
+ "expiresAt",
4623
+ "accountId",
4624
+ "email",
4625
+ "apiEndpoint",
4626
+ "enterpriseUrl",
4627
+ "lastRefreshedAt",
4628
+ "errorMessage"
4629
+ ]);
4630
+ return out;
4631
+ }
3882
4632
  function validateOpenCodeGo(body) {
3883
4633
  const authMethod = str(body["authMethod"]);
3884
4634
  const status = str(body["status"]);
@@ -3916,6 +4666,10 @@ function validateTokenBody(providerId, body) {
3916
4666
  return validateOpenCodeGo(body);
3917
4667
  case "kimi":
3918
4668
  return validateKimi(body);
4669
+ case "grok":
4670
+ return validateGrok(body);
4671
+ case "copilot":
4672
+ return validateCopilot(body);
3919
4673
  default:
3920
4674
  return null;
3921
4675
  }
@@ -3945,12 +4699,12 @@ async function statusEntryFor(reader, providerId) {
3945
4699
 
3946
4700
  // src/admin/accountsOAuth.ts
3947
4701
  var OAUTH_HTTP_PROVIDERS = /* @__PURE__ */ new Set(["claude", "gemini"]);
3948
- function err3(status, message) {
4702
+ function err5(status, message) {
3949
4703
  return { status, body: { error: { type: "admin_api_error", message } } };
3950
4704
  }
3951
4705
  function handleOAuthStart(providerId, deps) {
3952
4706
  if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
3953
- return err3(400, `oauth not available for provider '${providerId}'`);
4707
+ return err5(400, `oauth not available for provider '${providerId}'`);
3954
4708
  }
3955
4709
  const flow = providerId === "claude" ? claudeOAuth : geminiOAuth;
3956
4710
  const { authUrl, codeVerifier, state } = flow.generateAuthParams();
@@ -3959,23 +4713,23 @@ function handleOAuthStart(providerId, deps) {
3959
4713
  }
3960
4714
  async function handleOAuthComplete(providerId, body, deps) {
3961
4715
  if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
3962
- return err3(400, `oauth not available for provider '${providerId}'`);
4716
+ return err5(400, `oauth not available for provider '${providerId}'`);
3963
4717
  }
3964
4718
  const sessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : "";
3965
4719
  const rawCode = typeof body["code"] === "string" ? body["code"] : "";
3966
- if (!sessionId) return err3(400, "oauth complete requires { sessionId }");
3967
- if (!rawCode) return err3(400, "oauth complete requires { code }");
4720
+ if (!sessionId) return err5(400, "oauth complete requires { sessionId }");
4721
+ if (!rawCode) return err5(400, "oauth complete requires { code }");
3968
4722
  const session = deps.oauthSessions.peek(sessionId);
3969
- if (!session) return err3(410, "oauth session is unknown, expired, or already used");
4723
+ if (!session) return err5(410, "oauth session is unknown, expired, or already used");
3970
4724
  if (session.providerId !== providerId) {
3971
- return err3(400, `oauth session does not match provider '${providerId}'`);
4725
+ return err5(400, `oauth session does not match provider '${providerId}'`);
3972
4726
  }
3973
4727
  let code = rawCode.trim();
3974
4728
  if (providerId === "claude") {
3975
4729
  const [splitCode, pastedState] = code.split("#");
3976
- if (!splitCode) return err3(400, "no authorization code was provided");
4730
+ if (!splitCode) return err5(400, "no authorization code was provided");
3977
4731
  if (pastedState && pastedState !== session.state) {
3978
- return err3(400, "oauth state did not match (possible CSRF) \u2014 aborting");
4732
+ return err5(400, "oauth state did not match (possible CSRF) \u2014 aborting");
3979
4733
  }
3980
4734
  code = splitCode;
3981
4735
  }
@@ -3985,7 +4739,7 @@ async function handleOAuthComplete(providerId, body, deps) {
3985
4739
  block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
3986
4740
  } catch (exchangeError) {
3987
4741
  const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
3988
- return err3(502, `oauth token exchange failed for '${providerId}': ${reason}`);
4742
+ return err5(502, `oauth token exchange failed for '${providerId}': ${reason}`);
3989
4743
  }
3990
4744
  deps.oauthSessions.consume(sessionId);
3991
4745
  const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
@@ -4323,8 +5077,8 @@ function errBody(message) {
4323
5077
  return { error: { type: "admin_api_error", message } };
4324
5078
  }
4325
5079
  var defaultCommandRunner = (command) => new Promise((resolve10) => {
4326
- exec(command, { timeout: 18e4 }, (err6, _stdout, stderr) => {
4327
- if (err6) resolve10({ ok: false, error: stderr.trim() || err6.message });
5080
+ exec(command, { timeout: 18e4 }, (err8, _stdout, stderr) => {
5081
+ if (err8) resolve10({ ok: false, error: stderr.trim() || err8.message });
4328
5082
  else resolve10({ ok: true });
4329
5083
  });
4330
5084
  });
@@ -4370,8 +5124,8 @@ async function handleCliLaunch(cli, body, ctx) {
4370
5124
  providerId: typeof body["providerId"] === "string" ? body["providerId"] : void 0,
4371
5125
  model: typeof body["model"] === "string" ? body["model"] : void 0
4372
5126
  });
4373
- } catch (err6) {
4374
- return { status: 400, body: errBody(err6 instanceof Error ? err6.message : "no launch target") };
5127
+ } catch (err8) {
5128
+ return { status: 400, body: errBody(err8 instanceof Error ? err8.message : "no launch target") };
4375
5129
  }
4376
5130
  const id = randomUUID2();
4377
5131
  let leaseId2;
@@ -4399,9 +5153,9 @@ async function handleCliLaunch(cli, body, ctx) {
4399
5153
  } else {
4400
5154
  launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
4401
5155
  }
4402
- } catch (err6) {
4403
- const status = err6 instanceof RouteLeaseError2 ? err6.status : 400;
4404
- return { status, body: errBody(err6 instanceof Error ? err6.message : "failed to build launch env") };
5156
+ } catch (err8) {
5157
+ const status = err8 instanceof RouteLeaseError2 ? err8.status : 400;
5158
+ return { status, body: errBody(err8 instanceof Error ? err8.message : "failed to build launch env") };
4405
5159
  }
4406
5160
  const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
4407
5161
  const opener = ctx.opener ?? defaultTerminalOpener;
@@ -4429,9 +5183,9 @@ async function handleCliLaunch(cli, body, ctx) {
4429
5183
  onFailure: onSessionEnd
4430
5184
  });
4431
5185
  if (cleanup) openerCleanup = cleanup;
4432
- } catch (err6) {
5186
+ } catch (err8) {
4433
5187
  onSessionEnd();
4434
- return { status: 500, body: errBody(err6 instanceof Error ? err6.message : "failed to open terminal") };
5188
+ return { status: 500, body: errBody(err8 instanceof Error ? err8.message : "failed to open terminal") };
4435
5189
  }
4436
5190
  if (ended) {
4437
5191
  openerCleanup?.();
@@ -4980,7 +5734,7 @@ async function handleSearchQuery(req, res, deps) {
4980
5734
  // src/admin/searchAdminView.ts
4981
5735
  var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
4982
5736
  var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
4983
- function isRecord2(value) {
5737
+ function isRecord4(value) {
4984
5738
  return value !== null && typeof value === "object" && !Array.isArray(value);
4985
5739
  }
4986
5740
  function redactSearchServerConfig(search) {
@@ -5030,13 +5784,13 @@ function resolveSecretField(entry, field, stored) {
5030
5784
  else delete entry[field];
5031
5785
  }
5032
5786
  function preserveSearchSecrets(incoming, current) {
5033
- if (!isRecord2(incoming)) return incoming;
5787
+ if (!isRecord4(incoming)) return incoming;
5034
5788
  const section = { ...incoming };
5035
5789
  const providersValue = section["providers"];
5036
- if (!isRecord2(providersValue)) return section;
5790
+ if (!isRecord4(providersValue)) return section;
5037
5791
  const providers = {};
5038
5792
  for (const [id, entryValue] of Object.entries(providersValue)) {
5039
- if (!isRecord2(entryValue)) {
5793
+ if (!isRecord4(entryValue)) {
5040
5794
  providers[id] = entryValue;
5041
5795
  continue;
5042
5796
  }
@@ -5114,7 +5868,7 @@ function parseKeyPolicyBody(body) {
5114
5868
  var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
5115
5869
  var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
5116
5870
  var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
5117
- function isRecord3(value) {
5871
+ function isRecord5(value) {
5118
5872
  return !!value && typeof value === "object" && !Array.isArray(value);
5119
5873
  }
5120
5874
  function nonBlank(value) {
@@ -5134,7 +5888,7 @@ function validateGatewayBindingsSegment(patch) {
5134
5888
  const ids = /* @__PURE__ */ new Set();
5135
5889
  raw.forEach((entry, index) => {
5136
5890
  const path2 = `bindings[${index}]`;
5137
- if (!isRecord3(entry)) {
5891
+ if (!isRecord5(entry)) {
5138
5892
  errors.push(`${path2} must be an object`);
5139
5893
  return;
5140
5894
  }
@@ -5163,12 +5917,12 @@ function validateGatewayBindingsSegment(patch) {
5163
5917
  } else if (entry.modelMappings.length > 100) {
5164
5918
  errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
5165
5919
  } else if (entry.modelMappings.some(
5166
- (mapping) => !isRecord3(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
5920
+ (mapping) => !isRecord5(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
5167
5921
  )) {
5168
5922
  errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
5169
5923
  }
5170
5924
  }
5171
- if (!isRecord3(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
5925
+ if (!isRecord5(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
5172
5926
  errors.push(`${path2}.target is invalid`);
5173
5927
  } else {
5174
5928
  if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
@@ -5183,7 +5937,7 @@ function validateGatewayBindingsSegment(patch) {
5183
5937
  }
5184
5938
  }
5185
5939
  if (entry.modelMap !== void 0) {
5186
- if (!isRecord3(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
5940
+ if (!isRecord5(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
5187
5941
  errors.push(`${path2}.modelMap must contain string values`);
5188
5942
  }
5189
5943
  }
@@ -5494,7 +6248,9 @@ var PROVIDER_KEYS = {
5494
6248
  accounts: "opencodegoAccounts",
5495
6249
  active: "activeOpencodegoAccountId"
5496
6250
  },
5497
- kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" }
6251
+ kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" },
6252
+ grok: { block: "grok", accounts: "grokAccounts", active: "activeGrokAccountId" },
6253
+ copilot: { block: "copilot", accounts: "copilotAccounts", active: "activeCopilotAccountId" }
5498
6254
  };
5499
6255
  function clone(value) {
5500
6256
  return JSON.parse(JSON.stringify(value));
@@ -6016,7 +6772,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
6016
6772
  }
6017
6773
 
6018
6774
  // src/admin/adminMigration.ts
6019
- function err4(status, message) {
6775
+ function err6(status, message) {
6020
6776
  return { status, body: { error: { type: "admin_api_error", message } } };
6021
6777
  }
6022
6778
  async function handleExport(body, deps) {
@@ -6026,30 +6782,30 @@ async function handleExport(body, deps) {
6026
6782
  return { status: 200, body: { pack, version: BUNDLE_VERSION } };
6027
6783
  } catch (error) {
6028
6784
  if (error instanceof WeakPassphraseError) {
6029
- return err4(400, error.message);
6785
+ return err6(400, error.message);
6030
6786
  }
6031
- return err4(500, "failed to build the migration pack");
6787
+ return err6(500, "failed to build the migration pack");
6032
6788
  }
6033
6789
  }
6034
6790
  async function handleImport(body, deps) {
6035
6791
  const blob = typeof body["blob"] === "string" ? body["blob"] : "";
6036
6792
  const passphrase = typeof body["passphrase"] === "string" ? body["passphrase"] : "";
6037
6793
  const mode = body["mode"] === "overwrite" ? "overwrite" : "merge";
6038
- if (!blob) return err4(400, "import requires { blob }");
6794
+ if (!blob) return err6(400, "import requires { blob }");
6039
6795
  try {
6040
6796
  const counts = await applyImport(blob, passphrase, mode, deps, deps.parseProviderInput);
6041
6797
  return { status: 200, body: counts };
6042
6798
  } catch (error) {
6043
6799
  if (error instanceof WeakPassphraseError) {
6044
- return err4(400, error.message);
6800
+ return err6(400, error.message);
6045
6801
  }
6046
- return err4(400, error instanceof Error ? error.message : "import failed");
6802
+ return err6(400, error instanceof Error ? error.message : "import failed");
6047
6803
  }
6048
6804
  }
6049
6805
 
6050
6806
  // src/admin/usagePricing.ts
6051
6807
  import { getSharedUsageThroughputTracker } from "@omnicross/core/usage";
6052
- var err5 = (status, message) => ({
6808
+ var err7 = (status, message) => ({
6053
6809
  status,
6054
6810
  body: { error: { type: "admin_api_error", message } }
6055
6811
  });
@@ -6062,7 +6818,7 @@ function parseRange(query2) {
6062
6818
  const startTs = parseFiniteInt(query2.get("startTs"));
6063
6819
  const endTs = parseFiniteInt(query2.get("endTs"));
6064
6820
  if (startTs === null || endTs === null) {
6065
- return err5(400, "startTs and endTs are required finite-integer unix-millis query params");
6821
+ return err7(400, "startTs and endTs are required finite-integer unix-millis query params");
6066
6822
  }
6067
6823
  return { startTs, endTs };
6068
6824
  }
@@ -6087,14 +6843,14 @@ async function handleUsageGet(view, query2, deps) {
6087
6843
  case "timeseries": {
6088
6844
  const bucket = query2.get("bucket");
6089
6845
  if (bucket !== "hour" && bucket !== "day" && bucket !== "month") {
6090
- return err5(400, "bucket must be one of 'hour', 'day', 'month'");
6846
+ return err7(400, "bucket must be one of 'hour', 'day', 'month'");
6091
6847
  }
6092
6848
  const now = Date.now();
6093
6849
  const clamped = { startTs: range.startTs, endTs: Math.min(range.endTs, now) };
6094
6850
  if (clamped.startTs < clamped.endTs) {
6095
6851
  const projected = Math.ceil((clamped.endTs - clamped.startTs) / BUCKET_SPAN_MS[bucket]) + 1;
6096
6852
  if (projected > MAX_TIMESERIES_BUCKETS) {
6097
- return err5(
6853
+ return err7(
6098
6854
  400,
6099
6855
  `requested range projects ~${projected} '${bucket}' buckets (max ${MAX_TIMESERIES_BUCKETS}); narrow the range or use a coarser bucket`
6100
6856
  );
@@ -6117,7 +6873,7 @@ async function handleUsageGet(view, query2, deps) {
6117
6873
  };
6118
6874
  }
6119
6875
  default:
6120
- return err5(404, `unknown usage view '${view ?? ""}'`);
6876
+ return err7(404, `unknown usage view '${view ?? ""}'`);
6121
6877
  }
6122
6878
  }
6123
6879
  function poolKeyLabels(cfg) {
@@ -6166,7 +6922,7 @@ async function handlePricingList(deps) {
6166
6922
  async function handlePricingUpsert(body, deps) {
6167
6923
  const input = parsePricingEntryInput(body);
6168
6924
  if (!input) {
6169
- return err5(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
6925
+ return err7(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
6170
6926
  }
6171
6927
  const entry = await deps.pricingEngine.upsertManual(input);
6172
6928
  return { status: 200, body: { entry } };
@@ -6175,7 +6931,7 @@ async function handlePricingDelete(query2, deps) {
6175
6931
  const providerId = query2.get("providerId")?.trim() ?? "";
6176
6932
  const modelId = query2.get("modelId")?.trim() ?? "";
6177
6933
  if (!providerId || !modelId) {
6178
- return err5(400, "delete requires providerId and modelId query params");
6934
+ return err7(400, "delete requires providerId and modelId query params");
6179
6935
  }
6180
6936
  const deleted = await deps.pricingStore.delete(providerId, modelId);
6181
6937
  if (deleted) await deps.pricingEngine.invalidateCache();
@@ -6195,13 +6951,13 @@ async function handlePricingFetchLatest(deps) {
6195
6951
  }
6196
6952
  };
6197
6953
  } catch (e) {
6198
- return err5(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
6954
+ return err7(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
6199
6955
  }
6200
6956
  }
6201
6957
  async function handlePricingResolveConflicts(body, deps) {
6202
6958
  const raw = body["resolutions"];
6203
6959
  if (!Array.isArray(raw)) {
6204
- return err5(400, "resolve-conflicts requires { resolutions: [...] }");
6960
+ return err7(400, "resolve-conflicts requires { resolutions: [...] }");
6205
6961
  }
6206
6962
  const currentRows = await deps.pricingStore.getAll();
6207
6963
  const userEditedKeys = new Set(
@@ -6211,21 +6967,21 @@ async function handlePricingResolveConflicts(body, deps) {
6211
6967
  const pendingIncoming = /* @__PURE__ */ new Map();
6212
6968
  let staleCount = 0;
6213
6969
  for (const item of raw) {
6214
- if (!item || typeof item !== "object") return err5(400, "invalid resolution entry");
6970
+ if (!item || typeof item !== "object") return err7(400, "invalid resolution entry");
6215
6971
  const r = item;
6216
6972
  const action = r["action"];
6217
6973
  if (action !== "overwrite" && action !== "skip") {
6218
- return err5(400, "resolution action must be 'overwrite' or 'skip'");
6974
+ return err7(400, "resolution action must be 'overwrite' or 'skip'");
6219
6975
  }
6220
6976
  const providerId = typeof r["providerId"] === "string" && r["providerId"].trim() ? r["providerId"].trim() : "";
6221
6977
  const modelId = typeof r["modelId"] === "string" && r["modelId"].trim() ? r["modelId"].trim() : "";
6222
6978
  if (!providerId || !modelId) {
6223
- return err5(400, "each resolution requires top-level providerId and modelId");
6979
+ return err7(400, "each resolution requires top-level providerId and modelId");
6224
6980
  }
6225
6981
  const incoming = parsePricingEntryInput(r["incoming"]);
6226
- if (!incoming) return err5(400, "each resolution must echo a valid incoming pricing entry");
6982
+ if (!incoming) return err7(400, "each resolution must echo a valid incoming pricing entry");
6227
6983
  if (incoming.providerId !== providerId || incoming.modelId !== modelId) {
6228
- return err5(400, "resolution providerId/modelId must match the echoed incoming entry");
6984
+ return err7(400, "resolution providerId/modelId must match the echoed incoming entry");
6229
6985
  }
6230
6986
  const key = `${providerId}::${modelId}`;
6231
6987
  if (action === "overwrite" && !userEditedKeys.has(key)) {
@@ -6270,7 +7026,7 @@ function query(req) {
6270
7026
  }
6271
7027
  function allowanceProvider(value) {
6272
7028
  if (!value) return void 0;
6273
- return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" ? value : null;
7029
+ return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" || value === "grok" || value === "copilot" ? value : null;
6274
7030
  }
6275
7031
  async function handleAccountAllowanceApi(req, res, method, rest, service) {
6276
7032
  if (!service) return writeError2(res, 501, "account allowance service is not available");
@@ -6285,7 +7041,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
6285
7041
  const pathProvider = rest.length >= 2 ? rest[0] : null;
6286
7042
  const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
6287
7043
  if (providerId === null) {
6288
- return writeError2(res, 400, "providerId must be claude, codex, kimi, or opencodego");
7044
+ return writeError2(res, 400, "providerId must be claude, codex, kimi, opencodego, grok, or copilot");
6289
7045
  }
6290
7046
  const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
6291
7047
  const allowances = await service.list({ providerId, accountId });
@@ -6327,6 +7083,26 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
6327
7083
  }
6328
7084
  return writeJson3(res, 200, { allowances: allowances2 });
6329
7085
  }
7086
+ if (requestedProvider === "copilot") {
7087
+ if (!service.refreshCopilot) {
7088
+ return writeError2(res, 501, "copilot allowance refresh is not available");
7089
+ }
7090
+ const allowances2 = await service.refreshCopilot(accountId);
7091
+ if (accountId && allowances2.length === 0) {
7092
+ return writeError2(res, 404, `Copilot account '${accountId}' not found`);
7093
+ }
7094
+ return writeJson3(res, 200, { allowances: allowances2 });
7095
+ }
7096
+ if (requestedProvider === "grok") {
7097
+ if (!service.refreshGrok) {
7098
+ return writeError2(res, 501, "grok allowance refresh is not available");
7099
+ }
7100
+ const allowances2 = await service.refreshGrok(accountId);
7101
+ if (accountId && allowances2.length === 0) {
7102
+ return writeError2(res, 404, `Grok account '${accountId}' not found`);
7103
+ }
7104
+ return writeJson3(res, 200, { allowances: allowances2 });
7105
+ }
6330
7106
  const allowances = await service.refreshClaude(accountId);
6331
7107
  if (accountId && allowances.length === 0) {
6332
7108
  return writeError2(res, 404, `Claude account '${accountId}' not found`);
@@ -6428,6 +7204,9 @@ function toProviderView(row) {
6428
7204
  apiVersion: row.apiVersion,
6429
7205
  maxConcurrency: row.maxConcurrency,
6430
7206
  modelsEndpoint: row.modelsEndpoint,
7207
+ // Static extra headers round-trip VERBATIM (non-secret identity values;
7208
+ // auth/content names were already dropped at the write/load gate).
7209
+ extraHeaders: row.extraHeaders,
6431
7210
  // app-parity child 5: transformer config round-trips VERBATIM (non-secret —
6432
7211
  // transform-rule names + options, no key material; absent stays absent).
6433
7212
  transformer: row.transformer,
@@ -6497,8 +7276,8 @@ async function handleAdminApi(req, res, path2, deps) {
6497
7276
  default:
6498
7277
  return writeJsonError(res, 404, `unknown admin resource '${resource}'`);
6499
7278
  }
6500
- } catch (err6) {
6501
- writeJsonError(res, 500, err6 instanceof Error ? err6.message : String(err6));
7279
+ } catch (err8) {
7280
+ writeJsonError(res, 500, err8 instanceof Error ? err8.message : String(err8));
6502
7281
  }
6503
7282
  }
6504
7283
  function requestQuery(req) {
@@ -6656,6 +7435,9 @@ async function handleProviderReorder(req, res, cfg, deps) {
6656
7435
  persistProviders(cfg, deps);
6657
7436
  return writeJson4(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
6658
7437
  }
7438
+ function expandRowExtraHeaders(row) {
7439
+ return mergeExtraHeaders({}, row.extraHeaders);
7440
+ }
6659
7441
  async function handleDiscoverModels(res, id, cfg) {
6660
7442
  if (!id) return writeJsonError(res, 400, "provider id required in path");
6661
7443
  const row = cfg.providers.find((p) => p.id === id);
@@ -6669,7 +7451,8 @@ async function handleDiscoverModels(res, id, cfg) {
6669
7451
  try {
6670
7452
  const headers = { Accept: "application/json" };
6671
7453
  if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
6672
- const response = await fetchUpstream5(url, { method: "GET", headers }, { providerId: "byo" });
7454
+ Object.assign(headers, expandRowExtraHeaders(row));
7455
+ const response = await fetchUpstream7(url, { method: "GET", headers }, { providerId: "byo" });
6673
7456
  if (!response.ok) {
6674
7457
  const text = await response.text().catch(() => "");
6675
7458
  let message = text.slice(0, 300);
@@ -6686,8 +7469,8 @@ async function handleDiscoverModels(res, id, cfg) {
6686
7469
  const data = await response.json();
6687
7470
  const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
6688
7471
  return writeJson4(res, 200, { models });
6689
- } catch (err6) {
6690
- const message = err6 instanceof Error ? err6.message : String(err6);
7472
+ } catch (err8) {
7473
+ const message = err8 instanceof Error ? err8.message : String(err8);
6691
7474
  return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
6692
7475
  }
6693
7476
  }
@@ -6726,9 +7509,10 @@ async function handleTestModel(req, res, id, cfg) {
6726
7509
  messages: [{ role: "user", content: prompt }]
6727
7510
  };
6728
7511
  }
7512
+ Object.assign(headers, expandRowExtraHeaders(row));
6729
7513
  const startedAt = Date.now();
6730
7514
  try {
6731
- const response = await fetchUpstream5(
7515
+ const response = await fetchUpstream7(
6732
7516
  url,
6733
7517
  { method: "POST", headers, body: JSON.stringify(payload) },
6734
7518
  { providerId: "byo" }
@@ -6750,8 +7534,8 @@ async function handleTestModel(req, res, id, cfg) {
6750
7534
  latencyMs,
6751
7535
  sample: extractSampleText(text, row.apiFormat)
6752
7536
  });
6753
- } catch (err6) {
6754
- const message = err6 instanceof Error ? err6.message : String(err6);
7537
+ } catch (err8) {
7538
+ const message = err8 instanceof Error ? err8.message : String(err8);
6755
7539
  return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
6756
7540
  }
6757
7541
  }
@@ -7033,6 +7817,7 @@ function parseProviderInput(body, existing) {
7033
7817
  const apiVersion = typeof body["apiVersion"] === "string" && body["apiVersion"].length > 0 ? body["apiVersion"] : body["apiVersion"] === null ? void 0 : existing?.apiVersion;
7034
7818
  const modelsEndpoint = typeof body["modelsEndpoint"] === "string" && body["modelsEndpoint"].length > 0 ? body["modelsEndpoint"] : body["modelsEndpoint"] === null ? void 0 : existing?.modelsEndpoint;
7035
7819
  const maxConcurrency = typeof body["maxConcurrency"] === "number" && Number.isFinite(body["maxConcurrency"]) ? body["maxConcurrency"] : body["maxConcurrency"] === null ? void 0 : existing?.maxConcurrency;
7820
+ const extraHeaders = body["extraHeaders"] === null ? void 0 : body["extraHeaders"] === void 0 ? existing?.extraHeaders : validateExtraHeaders(body["extraHeaders"]);
7036
7821
  const transformer = body["transformer"] === null ? void 0 : parseTransformerInput(body["transformer"], existing?.transformer);
7037
7822
  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;
7038
7823
  const apiModes = body["apiModes"] === null ? void 0 : parseApiModesInput(body["apiModes"], existing?.apiModes);
@@ -7058,6 +7843,7 @@ function parseProviderInput(body, existing) {
7058
7843
  apiVersion,
7059
7844
  maxConcurrency,
7060
7845
  modelsEndpoint,
7846
+ extraHeaders,
7061
7847
  transformer: migrated.transformer,
7062
7848
  codingPlan,
7063
7849
  apiModes,
@@ -7079,7 +7865,10 @@ function handlePresets(res, method) {
7079
7865
  description: p.description,
7080
7866
  features: p.features,
7081
7867
  website: p.website,
7082
- modelsEndpoint: p.modelsEndpoint
7868
+ modelsEndpoint: p.modelsEndpoint,
7869
+ // Static extra headers ride along so `addFromPreset` can seed them onto the
7870
+ // row (the write gateway re-validates via the shared allowlist).
7871
+ extraHeaders: p.extraHeaders
7083
7872
  }));
7084
7873
  return writeJson4(res, 200, { presets, excluded });
7085
7874
  }
@@ -7561,12 +8350,12 @@ async function handleAccounts(req, res, method, rest, deps) {
7561
8350
  }
7562
8351
  return writeJson4(res, 200, { ok: true, affected: result.affected });
7563
8352
  }
7564
- if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi") && rest[1] === "oauth" && rest[3] === "status") {
7565
- const result = rest[0] === "codex" ? handleCodexOAuthStatus(rest[2], deps) : handleKimiOAuthStatus(rest[2], deps);
8353
+ if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[3] === "status") {
8354
+ 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);
7566
8355
  return writeJson4(res, result.status, result.body);
7567
8356
  }
7568
- if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi") && rest[1] === "oauth" && rest[2]) {
7569
- const result = rest[0] === "codex" ? handleCodexOAuthCancel(rest[2], deps) : handleKimiOAuthCancel(rest[2], deps);
8357
+ if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[2]) {
8358
+ 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);
7570
8359
  return writeJson4(res, result.status, result.body);
7571
8360
  }
7572
8361
  if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
@@ -7627,6 +8416,15 @@ async function handleAccounts(req, res, method, rest, deps) {
7627
8416
  const result2 = await handleKimiOAuthStart(deps);
7628
8417
  return writeJson4(res, result2.status, result2.body);
7629
8418
  }
8419
+ if (providerId === "grok") {
8420
+ const result2 = await handleGrokOAuthStart(deps);
8421
+ return writeJson4(res, result2.status, result2.body);
8422
+ }
8423
+ if (providerId === "copilot") {
8424
+ const body2 = await readJsonBody4(req);
8425
+ const result2 = await handleCopilotOAuthStart(deps, body2["enterpriseUrl"]);
8426
+ return writeJson4(res, result2.status, result2.body);
8427
+ }
7630
8428
  const result = handleOAuthStart(providerId, deps);
7631
8429
  return writeJson4(res, result.status, result.body);
7632
8430
  }
@@ -8121,12 +8919,12 @@ async function handlePlayground(req, res, method, deps) {
8121
8919
  const payload = body["body"];
8122
8920
  const status = deps.outboundApiServer.getStatus();
8123
8921
  if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
8124
- const path2 = resolvePlaygroundPath(endpoint, isRecord4(payload) ? payload : {});
8922
+ const path2 = resolvePlaygroundPath(endpoint, isRecord6(payload) ? payload : {});
8125
8923
  if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
8126
8924
  const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
8127
8925
  await proxyToOutbound(res, status.port, path2, key, upstreamBody);
8128
8926
  }
8129
- function isRecord4(v) {
8927
+ function isRecord6(v) {
8130
8928
  return !!v && typeof v === "object" && !Array.isArray(v);
8131
8929
  }
8132
8930
  function proxyToOutbound(res, outboundPort, path2, key, body) {
@@ -8155,8 +8953,8 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
8155
8953
  });
8156
8954
  }
8157
8955
  );
8158
- upstream.on("error", (err6) => {
8159
- if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err6.message}`);
8956
+ upstream.on("error", (err8) => {
8957
+ if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err8.message}`);
8160
8958
  else res.end();
8161
8959
  resolve10();
8162
8960
  });
@@ -8261,7 +9059,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
8261
9059
  }
8262
9060
 
8263
9061
  // src/admin/version.ts
8264
- var DAEMON_VERSION = true ? "0.3.1" : "0.0.0-dev";
9062
+ var DAEMON_VERSION = true ? "0.4.0" : "0.0.0-dev";
8265
9063
 
8266
9064
  // src/admin/AdminServer.ts
8267
9065
  var LOOPBACK_ADDR = "127.0.0.1";
@@ -8304,13 +9102,13 @@ var AdminServer = class {
8304
9102
  const server = http2.createServer((req, res) => {
8305
9103
  this.onRequest(req, res);
8306
9104
  });
8307
- const onError = (err6) => {
8308
- if (err6.code === "EADDRINUSE" && port !== 0) {
9105
+ const onError = (err8) => {
9106
+ if (err8.code === "EADDRINUSE" && port !== 0) {
8309
9107
  server.removeListener("error", onError);
8310
9108
  this.listen(bindAddr, 0).then(resolve10, reject);
8311
9109
  return;
8312
9110
  }
8313
- reject(err6);
9111
+ reject(err8);
8314
9112
  };
8315
9113
  server.on("error", onError);
8316
9114
  server.listen(port, bindAddr, () => {
@@ -8328,8 +9126,8 @@ var AdminServer = class {
8328
9126
  }
8329
9127
  /** Per-request handler: auth gate (when a token is set) → routing. */
8330
9128
  onRequest(req, res) {
8331
- void this.dispatch(req, res).catch((err6) => {
8332
- const message = err6 instanceof Error ? err6.message : String(err6);
9129
+ void this.dispatch(req, res).catch((err8) => {
9130
+ const message = err8 instanceof Error ? err8.message : String(err8);
8333
9131
  this.deps.logger.error("[AdminServer] unhandled error:", message);
8334
9132
  if (!res.headersSent) {
8335
9133
  res.writeHead(500, { "Content-Type": "application/json" });
@@ -8593,18 +9391,18 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
8593
9391
  return;
8594
9392
  }
8595
9393
  signal?.addEventListener("abort", abort, { once: true });
8596
- server.on("error", (err6) => {
9394
+ server.on("error", (err8) => {
8597
9395
  if (settled) return;
8598
9396
  settled = true;
8599
9397
  clearTimeout(timer);
8600
- if (err6.code === "EADDRINUSE") {
9398
+ if (err8.code === "EADDRINUSE") {
8601
9399
  reject(
8602
9400
  new Error(
8603
9401
  `login: cannot bind ${LOOPBACK_HOST}:${LOOPBACK_PORT} (address in use) \u2014 another codex login or process is holding the port`
8604
9402
  )
8605
9403
  );
8606
9404
  } else {
8607
- reject(err6);
9405
+ reject(err8);
8608
9406
  }
8609
9407
  });
8610
9408
  const timer = setTimeout(() => {
@@ -8680,21 +9478,22 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
8680
9478
  }
8681
9479
 
8682
9480
  // src/allowance/ProviderKeyQuotaService.ts
8683
- import { fetchUpstream as fetchUpstream6 } from "@omnicross/core/pipeline/upstreamFetch";
9481
+ import { mergeExtraHeaders as mergeExtraHeaders2 } from "@omnicross/core";
9482
+ import { fetchUpstream as fetchUpstream8 } from "@omnicross/core/pipeline/upstreamFetch";
8684
9483
 
8685
9484
  // src/allowance/ProviderKeyQuota.ts
8686
- var MINUTE_MS2 = 6e4;
8687
- var HOUR_MS2 = 60 * MINUTE_MS2;
8688
- var DAY_MS2 = 24 * HOUR_MS2;
8689
- var WEEK_MS = 7 * DAY_MS2;
8690
- var MONTH_MS = 30 * DAY_MS2;
8691
- function finiteNumber3(value) {
9485
+ var MINUTE_MS3 = 6e4;
9486
+ var HOUR_MS2 = 60 * MINUTE_MS3;
9487
+ var DAY_MS3 = 24 * HOUR_MS2;
9488
+ var WEEK_MS = 7 * DAY_MS3;
9489
+ var MONTH_MS = 30 * DAY_MS3;
9490
+ function finiteNumber5(value) {
8692
9491
  if (value === null || value === void 0 || value === "") return void 0;
8693
9492
  const parsed = typeof value === "number" ? value : Number(value);
8694
9493
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
8695
9494
  }
8696
9495
  function finitePercent4(value) {
8697
- const parsed = finiteNumber3(value);
9496
+ const parsed = finiteNumber5(value);
8698
9497
  return parsed !== void 0 && parsed <= 100 ? parsed : null;
8699
9498
  }
8700
9499
  function isoInstant3(value) {
@@ -8702,18 +9501,18 @@ function isoInstant3(value) {
8702
9501
  const time = Date.parse(value);
8703
9502
  if (Number.isFinite(time)) return new Date(time).toISOString();
8704
9503
  }
8705
- const numeric = finiteNumber3(value);
9504
+ const numeric = finiteNumber5(value);
8706
9505
  if (numeric !== void 0 && numeric > 1e9) {
8707
9506
  const ms = numeric > 1e12 ? numeric : numeric * 1e3;
8708
9507
  return new Date(ms).toISOString();
8709
9508
  }
8710
9509
  return void 0;
8711
9510
  }
8712
- function secondsUntil5(instant, now) {
9511
+ function secondsUntil7(instant, now) {
8713
9512
  if (!instant) return void 0;
8714
9513
  return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
8715
9514
  }
8716
- function isRecord5(value) {
9515
+ function isRecord7(value) {
8717
9516
  return !!value && typeof value === "object" && !Array.isArray(value);
8718
9517
  }
8719
9518
  function detectProviderKeyQuotaAdapter(baseUrl) {
@@ -8736,6 +9535,7 @@ function detectProviderKeyQuotaAdapter(baseUrl) {
8736
9535
  }
8737
9536
  if (host === "api.code.umans.ai") return "umans";
8738
9537
  if (host === "api.synthetic.new") return "synthetic";
9538
+ if (host === "api.cline.bot") return "cline-pass";
8739
9539
  return null;
8740
9540
  }
8741
9541
  function providerKeyQuotaUrl(adapter, baseUrl) {
@@ -8743,6 +9543,7 @@ function providerKeyQuotaUrl(adapter, baseUrl) {
8743
9543
  if (adapter === "zai") return `${origin}/api/monitor/usage/quota/limit`;
8744
9544
  if (adapter === "minimax-token-plan") return `${origin}/v1/token_plan/remains`;
8745
9545
  if (adapter === "umans") return `${origin}/v1/usage`;
9546
+ if (adapter === "cline-pass") return `${origin}/api/v1/users/me/plan/usage-limits`;
8746
9547
  return `${origin}/v2/quotas`;
8747
9548
  }
8748
9549
  function providerKeyQuotaAuthHeader(adapter, key) {
@@ -8754,7 +9555,7 @@ function zaiWindowDurationMs(item) {
8754
9555
  case 3:
8755
9556
  return count * HOUR_MS2;
8756
9557
  case 4:
8757
- return count * DAY_MS2;
9558
+ return count * DAY_MS3;
8758
9559
  case 5:
8759
9560
  return count * MONTH_MS;
8760
9561
  case 6:
@@ -8767,8 +9568,8 @@ function zaiWindowIdLabel(durationMs) {
8767
9568
  if (durationMs === WEEK_MS) return { id: "seven-day", label: "7 days" };
8768
9569
  if (durationMs === 5 * HOUR_MS2) return { id: "five-hour", label: "5 hours" };
8769
9570
  if (durationMs === MONTH_MS) return { id: "thirty-day", label: "30 days" };
8770
- if (durationMs !== void 0 && durationMs % DAY_MS2 === 0) {
8771
- const days = durationMs / DAY_MS2;
9571
+ if (durationMs !== void 0 && durationMs % DAY_MS3 === 0) {
9572
+ const days = durationMs / DAY_MS3;
8772
9573
  return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}` };
8773
9574
  }
8774
9575
  if (durationMs !== void 0 && durationMs % HOUR_MS2 === 0) {
@@ -8778,23 +9579,23 @@ function zaiWindowIdLabel(durationMs) {
8778
9579
  return { id: "quota", label: "Quota" };
8779
9580
  }
8780
9581
  function parseZaiQuotaPayload(payload, now) {
8781
- if (!isRecord5(payload)) return null;
8782
- const data = isRecord5(payload["data"]) ? payload["data"] : payload;
9582
+ if (!isRecord7(payload)) return null;
9583
+ const data = isRecord7(payload["data"]) ? payload["data"] : payload;
8783
9584
  if (payload["success"] === false) return null;
8784
9585
  const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
8785
9586
  const byWindow = /* @__PURE__ */ new Map();
8786
9587
  for (const raw of limits) {
8787
- if (!isRecord5(raw)) continue;
9588
+ if (!isRecord7(raw)) continue;
8788
9589
  const item = raw;
8789
9590
  if (item.type === void 0) continue;
8790
9591
  const details = raw["usageDetails"];
8791
- if (Array.isArray(details) && details.some((d) => isRecord5(d) && d["modelCode"] === "zread")) {
9592
+ if (Array.isArray(details) && details.some((d) => isRecord7(d) && d["modelCode"] === "zread")) {
8792
9593
  continue;
8793
9594
  }
8794
9595
  const durationMs = zaiWindowDurationMs(item);
8795
9596
  const { id, label } = zaiWindowIdLabel(durationMs);
8796
- const limit = finiteNumber3(item.usage);
8797
- const used = finiteNumber3(item.currentValue);
9597
+ const limit = finiteNumber5(item.usage);
9598
+ const used = finiteNumber5(item.currentValue);
8798
9599
  const fromAbsolute = limit !== void 0 && used !== void 0 && limit > 0 ? Math.min(100, used / limit * 100) : void 0;
8799
9600
  const fromPercentage = finitePercent4(item.percentage) ?? void 0;
8800
9601
  const usedPercent = fromAbsolute !== void 0 ? Math.round(fromAbsolute * 10) / 10 : fromPercentage;
@@ -8805,9 +9606,9 @@ function parseZaiQuotaPayload(payload, now) {
8805
9606
  label,
8806
9607
  scope: "all",
8807
9608
  usedPercent,
8808
- ...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs / MINUTE_MS2) } : {},
9609
+ ...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs / MINUTE_MS3) } : {},
8809
9610
  ...resetsAt !== void 0 ? { resetsAt } : {},
8810
- remainingSeconds: secondsUntil5(resetsAt, now),
9611
+ remainingSeconds: secondsUntil7(resetsAt, now),
8811
9612
  state: "fresh"
8812
9613
  };
8813
9614
  const existing = byWindow.get(id);
@@ -8821,21 +9622,21 @@ function parseZaiQuotaPayload(payload, now) {
8821
9622
  var MINIMAX_STATUS_EXHAUSTED = 2;
8822
9623
  var MINIMAX_SHARED_BUCKET = "general";
8823
9624
  function parseMiniMaxBucket(value) {
8824
- if (!isRecord5(value)) return null;
9625
+ if (!isRecord7(value)) return null;
8825
9626
  const modelName = typeof value["model_name"] === "string" ? value["model_name"].trim() : "";
8826
9627
  if (!modelName) return null;
8827
9628
  const instant = (v) => {
8828
- const n = finiteNumber3(v);
9629
+ const n = finiteNumber5(v);
8829
9630
  return n !== void 0 && n > 1e9 ? n > 1e12 ? n : n * 1e3 : void 0;
8830
9631
  };
8831
9632
  return {
8832
9633
  modelName,
8833
9634
  intervalEnd: instant(value["end_time"]),
8834
- intervalRemainingPercent: finiteNumber3(value["current_interval_remaining_percent"]),
8835
- intervalStatus: finiteNumber3(value["current_interval_status"]),
9635
+ intervalRemainingPercent: finiteNumber5(value["current_interval_remaining_percent"]),
9636
+ intervalStatus: finiteNumber5(value["current_interval_status"]),
8836
9637
  weeklyEnd: instant(value["weekly_end_time"]),
8837
- weeklyRemainingPercent: finiteNumber3(value["current_weekly_remaining_percent"]),
8838
- weeklyStatus: finiteNumber3(value["current_weekly_status"])
9638
+ weeklyRemainingPercent: finiteNumber5(value["current_weekly_remaining_percent"]),
9639
+ weeklyStatus: finiteNumber5(value["current_weekly_status"])
8839
9640
  };
8840
9641
  }
8841
9642
  function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, status, now) {
@@ -8848,14 +9649,14 @@ function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, s
8848
9649
  usedPercent,
8849
9650
  ...windowMinutes !== void 0 ? { windowMinutes } : {},
8850
9651
  ...resetsAt !== void 0 ? { resetsAt } : {},
8851
- remainingSeconds: secondsUntil5(resetsAt, now),
9652
+ remainingSeconds: secondsUntil7(resetsAt, now),
8852
9653
  state: usedPercent !== null ? "fresh" : "unavailable"
8853
9654
  };
8854
9655
  }
8855
9656
  function parseMiniMaxTokenPlanPayload(payload, now) {
8856
- if (!isRecord5(payload)) return null;
9657
+ if (!isRecord7(payload)) return null;
8857
9658
  const baseResp = payload["base_resp"];
8858
- if (!isRecord5(baseResp) || baseResp["status_code"] !== 0) return null;
9659
+ if (!isRecord7(baseResp) || baseResp["status_code"] !== 0) return null;
8859
9660
  const buckets = Array.isArray(payload["model_remains"]) ? payload["model_remains"] : [];
8860
9661
  let general = null;
8861
9662
  for (const raw of buckets) {
@@ -8879,7 +9680,7 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
8879
9680
  minimaxWindow(
8880
9681
  "seven-day",
8881
9682
  "7 days",
8882
- Math.round(WEEK_MS / MINUTE_MS2),
9683
+ Math.round(WEEK_MS / MINUTE_MS3),
8883
9684
  general.weeklyEnd,
8884
9685
  general.weeklyRemainingPercent,
8885
9686
  general.weeklyStatus,
@@ -8888,15 +9689,15 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
8888
9689
  ];
8889
9690
  }
8890
9691
  function parseUmansUsagePayload(payload, now) {
8891
- if (!isRecord5(payload)) return null;
8892
- const limits = isRecord5(payload["limits"]) ? payload["limits"] : void 0;
8893
- const requests = limits && isRecord5(limits["requests"]) ? limits["requests"] : void 0;
8894
- const usage = isRecord5(payload["usage"]) ? payload["usage"] : void 0;
8895
- const window = isRecord5(payload["window"]) ? payload["window"] : void 0;
8896
- const hardCap = finiteNumber3(requests?.["hard_cap"]);
8897
- const softLimit = finiteNumber3(requests?.["limit"]);
8898
- const requestsInWindow = finiteNumber3(usage?.["requests_in_window"]);
8899
- const weightedInWindow = finiteNumber3(usage?.["weighted_in_window"]);
9692
+ if (!isRecord7(payload)) return null;
9693
+ const limits = isRecord7(payload["limits"]) ? payload["limits"] : void 0;
9694
+ const requests = limits && isRecord7(limits["requests"]) ? limits["requests"] : void 0;
9695
+ const usage = isRecord7(payload["usage"]) ? payload["usage"] : void 0;
9696
+ const window = isRecord7(payload["window"]) ? payload["window"] : void 0;
9697
+ const hardCap = finiteNumber5(requests?.["hard_cap"]);
9698
+ const softLimit = finiteNumber5(requests?.["limit"]);
9699
+ const requestsInWindow = finiteNumber5(usage?.["requests_in_window"]);
9700
+ const weightedInWindow = finiteNumber5(usage?.["weighted_in_window"]);
8900
9701
  const resetsAt = isoInstant3(window?.["resets_at"]);
8901
9702
  let usedPercent = null;
8902
9703
  if (hardCap !== void 0 && hardCap > 0 && requestsInWindow !== void 0) {
@@ -8913,19 +9714,19 @@ function parseUmansUsagePayload(payload, now) {
8913
9714
  usedPercent,
8914
9715
  windowMinutes: 5 * 60,
8915
9716
  ...resetsAt !== void 0 ? { resetsAt } : {},
8916
- remainingSeconds: secondsUntil5(resetsAt, now),
9717
+ remainingSeconds: secondsUntil7(resetsAt, now),
8917
9718
  state: "fresh"
8918
9719
  }
8919
9720
  ];
8920
9721
  }
8921
9722
  function parseSyntheticQuotasPayload(payload, now) {
8922
- if (!isRecord5(payload)) return null;
8923
- const fiveHour = isRecord5(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
8924
- const weekly = isRecord5(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
9723
+ if (!isRecord7(payload)) return null;
9724
+ const fiveHour = isRecord7(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
9725
+ const weekly = isRecord7(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
8925
9726
  const windows = [];
8926
9727
  if (fiveHour) {
8927
- const max = finiteNumber3(fiveHour["max"]);
8928
- const remaining = finiteNumber3(fiveHour["remaining"]);
9728
+ const max = finiteNumber5(fiveHour["max"]);
9729
+ const remaining = finiteNumber5(fiveHour["remaining"]);
8929
9730
  const usedPercent = max !== void 0 && max > 0 && remaining !== void 0 ? Math.round(Math.min(100, (max - remaining) / max * 100) * 10) / 10 : null;
8930
9731
  const resetsAt = isoInstant3(fiveHour["nextTickAt"]);
8931
9732
  windows.push({
@@ -8935,12 +9736,12 @@ function parseSyntheticQuotasPayload(payload, now) {
8935
9736
  usedPercent,
8936
9737
  windowMinutes: 5 * 60,
8937
9738
  ...resetsAt !== void 0 ? { resetsAt } : {},
8938
- remainingSeconds: secondsUntil5(resetsAt, now),
9739
+ remainingSeconds: secondsUntil7(resetsAt, now),
8939
9740
  state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
8940
9741
  });
8941
9742
  }
8942
9743
  if (weekly) {
8943
- const percentRemaining = finiteNumber3(weekly["percentRemaining"]);
9744
+ const percentRemaining = finiteNumber5(weekly["percentRemaining"]);
8944
9745
  const usedPercent = percentRemaining !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - percentRemaining)) * 10) / 10 : null;
8945
9746
  const resetsAt = isoInstant3(weekly["nextRegenAt"]);
8946
9747
  windows.push({
@@ -8950,12 +9751,42 @@ function parseSyntheticQuotasPayload(payload, now) {
8950
9751
  usedPercent,
8951
9752
  windowMinutes: 7 * 24 * 60,
8952
9753
  ...resetsAt !== void 0 ? { resetsAt } : {},
8953
- remainingSeconds: secondsUntil5(resetsAt, now),
9754
+ remainingSeconds: secondsUntil7(resetsAt, now),
8954
9755
  state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
8955
9756
  });
8956
9757
  }
8957
9758
  return windows.length > 0 ? windows : null;
8958
9759
  }
9760
+ var CLINE_WINDOW_CONFIG = {
9761
+ five_hour: { id: "five-hour", label: "5 hours", minutes: 5 * 60 },
9762
+ weekly: { id: "seven-day", label: "7 days", minutes: 7 * 24 * 60 },
9763
+ monthly: { id: "thirty-day", label: "30 days", minutes: 30 * 24 * 60 }
9764
+ };
9765
+ function parseClinePassUsageLimitsPayload(payload, now) {
9766
+ if (!isRecord7(payload)) return null;
9767
+ const data = isRecord7(payload["data"]) ? payload["data"] : payload;
9768
+ const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
9769
+ const windows = [];
9770
+ for (const raw of limits) {
9771
+ if (!isRecord7(raw)) continue;
9772
+ const config = CLINE_WINDOW_CONFIG[typeof raw["type"] === "string" ? raw["type"] : ""];
9773
+ if (!config) continue;
9774
+ const usedPercent = finitePercent4(raw["percentUsed"]);
9775
+ if (usedPercent === null) continue;
9776
+ const resetsAt = isoInstant3(raw["resetsAt"]);
9777
+ windows.push({
9778
+ id: config.id,
9779
+ label: config.label,
9780
+ scope: "all",
9781
+ usedPercent,
9782
+ windowMinutes: config.minutes,
9783
+ ...resetsAt !== void 0 ? { resetsAt } : {},
9784
+ remainingSeconds: secondsUntil7(resetsAt, now),
9785
+ state: "fresh"
9786
+ });
9787
+ }
9788
+ return windows.length > 0 ? windows : null;
9789
+ }
8959
9790
 
8960
9791
  // src/allowance/ProviderKeyQuotaService.ts
8961
9792
  function parseQuotaPayload(adapter, payload, now) {
@@ -8968,6 +9799,8 @@ function parseQuotaPayload(adapter, payload, now) {
8968
9799
  return parseUmansUsagePayload(payload, now);
8969
9800
  case "synthetic":
8970
9801
  return parseSyntheticQuotasPayload(payload, now);
9802
+ case "cline-pass":
9803
+ return parseClinePassUsageLimitsPayload(payload, now);
8971
9804
  }
8972
9805
  }
8973
9806
  var PROVIDER_KEY_QUOTA_CACHE_MS = 5 * 6e4;
@@ -8987,7 +9820,7 @@ function rowKeyEntries(row) {
8987
9820
  return [];
8988
9821
  }
8989
9822
  var ProviderKeyQuotaService = class {
8990
- constructor(box, fetchImpl = (url, init) => fetchUpstream6(url, init, { redactBodies: true }), now = Date.now) {
9823
+ constructor(box, fetchImpl = (url, init) => fetchUpstream8(url, init, { redactBodies: true }), now = Date.now) {
8991
9824
  this.box = box;
8992
9825
  this.fetchImpl = fetchImpl;
8993
9826
  this.now = now;
@@ -9049,7 +9882,10 @@ var ProviderKeyQuotaService = class {
9049
9882
  headers: {
9050
9883
  Authorization: providerKeyQuotaAuthHeader(adapter, key),
9051
9884
  Accept: "application/json",
9052
- "Content-Type": "application/json"
9885
+ "Content-Type": "application/json",
9886
+ // The row's static identity headers ride along — the Cline usage
9887
+ // endpoint sits behind the SAME client-identity 403 gate as inference.
9888
+ ...mergeExtraHeaders2({}, row.extraHeaders)
9053
9889
  },
9054
9890
  signal: AbortSignal.timeout(15e3)
9055
9891
  });
@@ -13854,6 +14690,10 @@ function toLLMProvider(row) {
13854
14690
  // `parseProviderInput`), so customizations are preserved (the row value wins).
13855
14691
  apiModes: row.apiModes,
13856
14692
  selectedApiModeId: row.selectedApiModeId,
14693
+ // Static extra request headers ride along verbatim (load-guarded — no
14694
+ // auth/content names); core's `getProviderHeaders` merges them into every
14695
+ // BYO request, and the same-format relay path inherits that funnel.
14696
+ extraHeaders: row.extraHeaders,
13857
14697
  // Official-Anthropic signature handling only matters for the Anthropic
13858
14698
  // ingress (deferred → 502); leave it off for the BYO transform path.
13859
14699
  isOfficial: false
@@ -15803,12 +16643,13 @@ import { existsSync as existsSync22, mkdirSync as mkdirSync6, readFileSync as re
15803
16643
  import { dirname as dirname15 } from "path";
15804
16644
  import { getSharedAccountHealth as getSharedAccountHealth2 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
15805
16645
  import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling3 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
15806
- import { fetchUpstream as fetchUpstream7 } from "@omnicross/core/pipeline/upstreamFetch";
16646
+ import { fetchUpstream as fetchUpstream9 } from "@omnicross/core/pipeline/upstreamFetch";
15807
16647
  import { getSharedIdentityStore as getSharedIdentityStore2 } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
15808
16648
  import {
15809
16649
  claudeOAuth as claudeOAuth2,
15810
16650
  codexOAuth as codexOAuth2,
15811
16651
  geminiOAuth as geminiOAuth2,
16652
+ grokOAuth as grokOAuth2,
15812
16653
  kimiOAuth as kimiOAuth2
15813
16654
  } from "@omnicross/subscriptions";
15814
16655
 
@@ -15958,7 +16799,7 @@ var JsonSubscriptionCredentialStore = class {
15958
16799
  * a plaintext token pair into `upstream-trace.jsonl`.
15959
16800
  */
15960
16801
  buildRefreshFetch(providerId, accountId) {
15961
- return this.fetchImpl ?? ((url, init) => fetchUpstream7(url, init, { providerId, accountId, redactBodies: true }));
16802
+ return this.fetchImpl ?? ((url, init) => fetchUpstream9(url, init, { providerId, accountId, redactBodies: true }));
15962
16803
  }
15963
16804
  /**
15964
16805
  * In-flight refresh coalescing. OAuth refresh tokens are
@@ -15999,7 +16840,7 @@ var JsonSubscriptionCredentialStore = class {
15999
16840
  * other hot reads. Never returns token material.
16000
16841
  */
16001
16842
  getAccountProxy(providerId, accountId) {
16002
- if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi") {
16843
+ if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi" && providerId !== "grok" && providerId !== "copilot") {
16003
16844
  return void 0;
16004
16845
  }
16005
16846
  return getAccountProxy(this.readConfig(), providerId, accountId);
@@ -16018,7 +16859,7 @@ var JsonSubscriptionCredentialStore = class {
16018
16859
  const fingerprintOn = identityStore.isEnabled();
16019
16860
  const now = Date.now();
16020
16861
  const out = {};
16021
- for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi"]) {
16862
+ for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi", "grok", "copilot"]) {
16022
16863
  const sanitized = sanitizeAccounts(config, provider);
16023
16864
  if (sanitized.length === 0) continue;
16024
16865
  for (const account of sanitized) {
@@ -16217,6 +17058,66 @@ var JsonSubscriptionCredentialStore = class {
16217
17058
  }
16218
17059
  });
16219
17060
  }
17061
+ /**
17062
+ * Refresh the Grok (xAI SuperGrok) OAuth access token. The token endpoint is
17063
+ * resolved through OIDC discovery on every refresh (process-cached 1h by the
17064
+ * flow module) so a rotated endpoint document is picked up without a daemon
17065
+ * restart. HONEST `false` when no refresh_token.
17066
+ */
17067
+ async refreshGrokToken() {
17068
+ return this.coalesce("grok:active", async () => {
17069
+ const config = this.readConfig();
17070
+ const active = getActiveAccount(config, "grok");
17071
+ const grok = active?.tokens;
17072
+ if (!active || !grok?.refreshToken) return false;
17073
+ const capturedId = active.id;
17074
+ this.materializeMigration(config);
17075
+ const refreshFetch = this.buildRefreshFetch("grok", capturedId);
17076
+ try {
17077
+ const tokenEndpoint = await grokOAuth2.resolveGrokTokenEndpoint(refreshFetch);
17078
+ const result = await grokOAuth2.refreshGrokAccessToken(grok.refreshToken, tokenEndpoint, refreshFetch);
17079
+ const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
17080
+ const next = {
17081
+ ...grok,
17082
+ accessToken: result.accessToken,
17083
+ refreshToken: result.refreshToken,
17084
+ expiresAt,
17085
+ status: "authorized",
17086
+ lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
17087
+ errorMessage: void 0,
17088
+ syncWarning: void 0
17089
+ };
17090
+ this.writeBackById("grok", capturedId, next);
17091
+ return true;
17092
+ } catch (error) {
17093
+ this.markExpiredById("grok", capturedId, grok, error);
17094
+ return false;
17095
+ }
17096
+ });
17097
+ }
17098
+ /**
17099
+ * "Refresh" a GitHub Copilot token — there is nothing to refresh (ghu_
17100
+ * tokens are long-lived with no exchange endpoint). A call here means the
17101
+ * strategy saw a 401 (the token was revoked); mark the account `expired`
17102
+ * with a re-authenticate message and return `false` (the proxy then declines
17103
+ * the retry instead of looping on a dead token).
17104
+ */
17105
+ async refreshCopilotToken() {
17106
+ return this.coalesce("copilot:active", async () => {
17107
+ const config = this.readConfig();
17108
+ const active = getActiveAccount(config, "copilot");
17109
+ const copilot = active?.tokens;
17110
+ if (!active || !copilot?.accessToken) return false;
17111
+ this.materializeMigration(config);
17112
+ this.markExpiredById(
17113
+ "copilot",
17114
+ active.id,
17115
+ copilot,
17116
+ new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account")
17117
+ );
17118
+ return false;
17119
+ });
17120
+ }
16220
17121
  /**
16221
17122
  * Refresh a SPECIFIC managed account by id (background scheduler sweep and
16222
17123
  * account-pool resolution). It uses only that account's stored refresh
@@ -16269,7 +17170,7 @@ var JsonSubscriptionCredentialStore = class {
16269
17170
  }
16270
17171
  const oauth = account.tokens;
16271
17172
  if (!oauth.accessToken) return null;
16272
- if (providerId === "codex" || providerId === "gemini" || providerId === "kimi") {
17173
+ if (providerId === "codex" || providerId === "gemini" || providerId === "kimi" || providerId === "grok" || providerId === "copilot") {
16273
17174
  const expiresAtMs = oauth.expiresAt ? Date.parse(oauth.expiresAt) : 0;
16274
17175
  const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - ACCOUNT_REFRESH_LEAD_MS;
16275
17176
  if (expiringSoon && oauth.refreshToken) {
@@ -16373,6 +17274,18 @@ var JsonSubscriptionCredentialStore = class {
16373
17274
  expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
16374
17275
  };
16375
17276
  }
17277
+ if (provider === "grok") {
17278
+ const tokenEndpoint = await grokOAuth2.resolveGrokTokenEndpoint(refreshFetch);
17279
+ const r2 = await grokOAuth2.refreshGrokAccessToken(refreshToken, tokenEndpoint, refreshFetch);
17280
+ return {
17281
+ accessToken: r2.accessToken,
17282
+ refreshToken: r2.refreshToken,
17283
+ expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
17284
+ };
17285
+ }
17286
+ if (provider === "copilot") {
17287
+ throw new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account");
17288
+ }
16376
17289
  const flow = provider === "claude" ? claudeOAuth2 : provider === "codex" ? codexOAuth2 : geminiOAuth2;
16377
17290
  const r = await flow.refreshAccessToken(refreshToken, refreshFetch);
16378
17291
  return {
@@ -16616,7 +17529,7 @@ var JsonSubscriptionCredentialStore = class {
16616
17529
  };
16617
17530
 
16618
17531
  // src/AccountHealthProbeScheduler.ts
16619
- import { fetchUpstream as fetchUpstream8 } from "@omnicross/core/pipeline/upstreamFetch";
17532
+ import { fetchUpstream as fetchUpstream10 } from "@omnicross/core/pipeline/upstreamFetch";
16620
17533
 
16621
17534
  // src/probe/CodexGenerationProbe.ts
16622
17535
  import {
@@ -16762,7 +17675,16 @@ var PROVIDER_PROBE_PLANS = {
16762
17675
  // Kimi's `GET /coding/v1/usages` is a verified FREE authed GET (the allowance
16763
17676
  // collector uses it), but the probe path also needs the fingerprint headers —
16764
17677
  // keep the probe local until the collector covers the health surface.
16765
- kimi: { kind: "local" }
17678
+ kimi: { kind: "local" },
17679
+ // Grok's billing proxy is a verified FREE authed GET (the allowance collector
17680
+ // uses it) but it REJECTS non-OAuth credentials and sits on a separate host
17681
+ // with its own product-gate header — keep the probe local, the collector
17682
+ // owns the health surface.
17683
+ grok: { kind: "local" },
17684
+ // The Copilot quota endpoint (copilot_internal/user) is a verified FREE
17685
+ // authed GET but lives on api.github.com with its own auth dialect and a
17686
+ // monthly-only window — the allowance collector owns the health surface.
17687
+ copilot: { kind: "local" }
16766
17688
  };
16767
17689
  function probePlanFor(providerId) {
16768
17690
  return PROVIDER_PROBE_PLANS[providerId] ?? { kind: "local" };
@@ -16784,7 +17706,7 @@ var AccountHealthProbeScheduler = class {
16784
17706
  this.logger = logger;
16785
17707
  this.config = config;
16786
17708
  this.now = opts.now ?? Date.now;
16787
- this.fetchImpl = opts.fetchImpl ?? fetchUpstream8;
17709
+ this.fetchImpl = opts.fetchImpl ?? fetchUpstream10;
16788
17710
  this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
16789
17711
  this.planFor = opts.planFor ?? probePlanFor;
16790
17712
  }
@@ -17692,7 +18614,7 @@ async function readAuditStats(auditDir, query2 = {}) {
17692
18614
  }
17693
18615
 
17694
18616
  // src/audit/AuditPruneSweeper.ts
17695
- var DAY_MS3 = 24 * 60 * 6e4;
18617
+ var DAY_MS4 = 24 * 60 * 6e4;
17696
18618
  var SWEEP_INTERVAL_MS2 = 60 * 6e4;
17697
18619
  var ARCHIVE_BATCH = 64;
17698
18620
  var AuditPruneSweeper = class {
@@ -17756,7 +18678,7 @@ var AuditPruneSweeper = class {
17756
18678
  this.sweeping = true;
17757
18679
  try {
17758
18680
  if (!existsSync25(this.auditDir)) return 0;
17759
- const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS3;
18681
+ const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS4;
17760
18682
  let removed = 0;
17761
18683
  for (const name of readdirSync6(this.auditDir)) {
17762
18684
  const dateMs = auditFileDateMs(name);
@@ -18013,7 +18935,7 @@ async function closeAll(writers) {
18013
18935
  // src/usage/UsagePruneSweeper.ts
18014
18936
  import { unlink as unlink3 } from "fs/promises";
18015
18937
  import { join as join22 } from "path";
18016
- var DAY_MS4 = 24 * 60 * 6e4;
18938
+ var DAY_MS5 = 24 * 60 * 6e4;
18017
18939
  var SWEEP_INTERVAL_MS3 = 60 * 6e4;
18018
18940
  var DEFAULT_USAGE_RETENTION_DAYS = 90;
18019
18941
  var UsagePruneSweeper = class {
@@ -18070,7 +18992,7 @@ var UsagePruneSweeper = class {
18070
18992
  this.sweeping = true;
18071
18993
  try {
18072
18994
  const retentionDays = this.config.retentionDays ?? DEFAULT_USAGE_RETENTION_DAYS;
18073
- const cutoff = this.todayMidnight() - (retentionDays - 1) * DAY_MS4;
18995
+ const cutoff = this.todayMidnight() - (retentionDays - 1) * DAY_MS5;
18074
18996
  let removed = 0;
18075
18997
  for (const entry of await listUsageDays(this.usageDir)) {
18076
18998
  if (!entry.hasShard) continue;
@@ -18489,7 +19411,7 @@ var AuditWriter = class {
18489
19411
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync8 } from "fs";
18490
19412
  import { createHmac as createHmac5 } from "crypto";
18491
19413
  import { join as join26 } from "path";
18492
- import { fetchUpstream as fetchUpstream9 } from "@omnicross/core/pipeline/upstreamFetch";
19414
+ import { fetchUpstream as fetchUpstream11 } from "@omnicross/core/pipeline/upstreamFetch";
18493
19415
 
18494
19416
  // src/billing/billingFiles.ts
18495
19417
  var BILLING_FILE_RE = /^billing-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
@@ -18512,7 +19434,7 @@ var BillingPublisher = class {
18512
19434
  constructor(billingDir, logger, opts = {}) {
18513
19435
  this.billingDir = billingDir;
18514
19436
  this.logger = logger;
18515
- this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream9(url, init));
19437
+ this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream11(url, init));
18516
19438
  this.defer = opts.defer ?? ((fn) => setTimeout(fn, 0));
18517
19439
  this.timeoutMs = opts.timeoutMs ?? BILLING_POST_TIMEOUT_MS;
18518
19440
  this.now = opts.now ?? Date.now;
@@ -18762,7 +19684,7 @@ var BillingRetrySweeper = class {
18762
19684
  // src/TokenRefreshScheduler.ts
18763
19685
  var REFRESH_LEAD_MS2 = 5 * 6e4;
18764
19686
  var SWEEP_INTERVAL_MS5 = 6e4;
18765
- var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
19687
+ var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
18766
19688
  var TokenRefreshScheduler = class {
18767
19689
  constructor(store, logger, intervalMs = SWEEP_INTERVAL_MS5, leadMs = REFRESH_LEAD_MS2) {
18768
19690
  this.store = store;
@@ -18847,6 +19769,12 @@ var TokenRefreshScheduler = class {
18847
19769
  return this.store.refreshGeminiToken();
18848
19770
  case "kimi":
18849
19771
  return this.store.refreshKimiToken();
19772
+ case "grok":
19773
+ return this.store.refreshGrokToken();
19774
+ // ghu_ tokens never near-expire (far-future expiresAt), so the sweep
19775
+ // never reaches this — the branch exists for union totality.
19776
+ case "copilot":
19777
+ return this.store.refreshCopilotToken();
18850
19778
  }
18851
19779
  }
18852
19780
  };
@@ -18925,7 +19853,7 @@ function createRouteLeaseSubscriptionPreflight(credentials) {
18925
19853
 
18926
19854
  // src/webhook/WebhookDispatcher.ts
18927
19855
  import { createHmac as createHmac6 } from "crypto";
18928
- import { fetchUpstream as fetchUpstream10 } from "@omnicross/core/pipeline/upstreamFetch";
19856
+ import { fetchUpstream as fetchUpstream12 } from "@omnicross/core/pipeline/upstreamFetch";
18929
19857
  var WEBHOOK_MAX_ATTEMPTS = 3;
18930
19858
  var WEBHOOK_QUEUE_MAX = 1e3;
18931
19859
  var WEBHOOK_SEND_TIMEOUT_MS = 1e4;
@@ -18945,7 +19873,7 @@ var WebhookDispatcher = class {
18945
19873
  sleep;
18946
19874
  now;
18947
19875
  constructor(opts = {}) {
18948
- this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream10(url, init));
19876
+ this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream12(url, init));
18949
19877
  this.logger = opts.logger;
18950
19878
  this.maxAttempts = opts.maxAttempts ?? WEBHOOK_MAX_ATTEMPTS;
18951
19879
  this.queueMax = opts.queueMax ?? WEBHOOK_QUEUE_MAX;
@@ -19031,8 +19959,8 @@ var WebhookDispatcher = class {
19031
19959
  signal: AbortSignal.timeout(this.timeoutMs)
19032
19960
  });
19033
19961
  return res.ok ? { ok: true, status: res.status } : { ok: false, status: res.status };
19034
- } catch (err6) {
19035
- return { ok: false, error: err6 instanceof Error ? err6.message : String(err6) };
19962
+ } catch (err8) {
19963
+ return { ok: false, error: err8 instanceof Error ? err8.message : String(err8) };
19036
19964
  }
19037
19965
  }
19038
19966
  /**
@@ -19174,7 +20102,7 @@ function buildDaemon(config, paths) {
19174
20102
  setSecretBox(secretBox3);
19175
20103
  setSecretBox2(secretBox3);
19176
20104
  const decryptedConfig = decryptConfigSecrets(config, secretBox3);
19177
- const accountAllowanceStore = new AccountAllowanceStore6(
20105
+ const accountAllowanceStore = new AccountAllowanceStore8(
19178
20106
  Date.now,
19179
20107
  void 0,
19180
20108
  new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
@@ -19236,7 +20164,7 @@ function buildDaemon(config, paths) {
19236
20164
  const pricingEngine = new PricingEngine(pricingStore, logger, {
19237
20165
  // Catalog egress follows the same global/env proxy policy as every other
19238
20166
  // daemon upstream call; no provider/account override applies here.
19239
- fetchImpl: ((input, init) => fetchUpstream11(String(input), init ?? {}))
20167
+ fetchImpl: ((input, init) => fetchUpstream13(String(input), init ?? {}))
19240
20168
  });
19241
20169
  const pricingRefreshScheduler = new PricingRefreshScheduler(
19242
20170
  pricingEngine,
@@ -19521,7 +20449,7 @@ function buildDaemon(config, paths) {
19521
20449
  // — `server.proxy.byProvider[...]` was silently skipped — and the call was
19522
20450
  // excluded from the upstream trace, so a failing login left no evidence.
19523
20451
  // `redactBodies` keeps the code/verifier + minted token out of that trace.
19524
- oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => fetchUpstream11(url, init, { providerId, redactBodies: true }),
20452
+ oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => fetchUpstream13(url, init, { providerId, redactBodies: true }),
19525
20453
  subscriptionAccountAppender: credentialStore,
19526
20454
  // Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
19527
20455
  // + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
@@ -19533,6 +20461,9 @@ function buildDaemon(config, paths) {
19533
20461
  // paste; the app shows the verification URL + user code and polls the
19534
20462
  // token-free status). Token captured + persisted daemon-side.
19535
20463
  kimiSessions: new CodexOAuthSessionStore(),
20464
+ // Grok interactive OAuth — the same async DEVICE-CODE shape as kimi.
20465
+ grokSessions: new CodexOAuthSessionStore(),
20466
+ copilotSessions: new CodexOAuthSessionStore(),
19536
20467
  // Migration pack (app-parity child 6, design D2/D3) — the concrete credential
19537
20468
  // store provides BOTH the full DECRYPTED read (`getFullConfig`, export) and
19538
20469
  // the multi-account append (`appendProviderAccount`, import re-encrypts at-
@@ -19591,7 +20522,7 @@ function buildDaemon(config, paths) {
19591
20522
  });
19592
20523
  const webhookDispatcher = new WebhookDispatcher({
19593
20524
  logger,
19594
- fetchImpl: (url, init) => fetchUpstream11(url, init)
20525
+ fetchImpl: (url, init) => fetchUpstream13(url, init)
19595
20526
  });
19596
20527
  setWebhookRuntime(webhookDispatcher, getSharedAccountHealth4());
19597
20528
  const auditWriter = new AuditWriter(auditDir, logger);