@omnicross/daemon 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1504 -269
- package/dist/cli.js +1475 -228
- package/dist/index.cjs +1400 -263
- package/dist/index.d.cts +215 -8
- package/dist/index.d.ts +215 -8
- package/dist/index.js +1373 -226
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { dirname as dirname17 } from "path";
|
|
|
4
4
|
import { DEFAULT_AUDIT_CONFIG } from "@omnicross/contracts/audit-types";
|
|
5
5
|
import { DEFAULT_BILLING_CONFIG } from "@omnicross/contracts/billing-types";
|
|
6
6
|
import { OpenAIOperationRegistry } from "@omnicross/core";
|
|
7
|
-
import { getGeminiCodeAssistProjectResolver } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
|
|
7
|
+
import { getGeminiCodeAssistProjectResolver as getGeminiCodeAssistProjectResolver2 } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
|
|
8
8
|
import { ApiKeyPoolService } from "@omnicross/core/completion/ApiKeyPoolService";
|
|
9
9
|
import {
|
|
10
10
|
__resetOutboundApiServerForTests,
|
|
@@ -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
|
|
21
|
+
AccountAllowanceStore as AccountAllowanceStore9,
|
|
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
|
|
28
|
+
import { fetchUpstream as fetchUpstream14, 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
|
|
414
|
+
getSharedAccountAllowanceStore as getSharedAccountAllowanceStore8
|
|
236
415
|
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
237
416
|
import {
|
|
238
417
|
getSharedAccountAllowanceScheduling
|
|
@@ -806,42 +985,705 @@ function parseKimiUsagePayload(payload, now) {
|
|
|
806
985
|
const window = windowFromRow({ ...row, resetsAtMs: row.resetsAtMs }, { id: "seven-day", label: "7 days", minutes: 10080 }, now);
|
|
807
986
|
byId.set("seven-day", window);
|
|
808
987
|
}
|
|
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
|
-
}
|
|
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
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
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
|
+
});
|
|
1479
|
+
}
|
|
1480
|
+
return windows.length > 0 ? windows : null;
|
|
1481
|
+
}
|
|
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) {
|
|
1487
|
+
this.credentials = credentials;
|
|
1488
|
+
this.store = store;
|
|
1489
|
+
this.fetchImpl = fetchImpl;
|
|
1490
|
+
this.now = now;
|
|
1491
|
+
}
|
|
1492
|
+
credentials;
|
|
1493
|
+
store;
|
|
1494
|
+
fetchImpl;
|
|
1495
|
+
now;
|
|
1496
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
1497
|
+
async collectMany(accounts, options = {}) {
|
|
1498
|
+
const settled = await Promise.allSettled(
|
|
1499
|
+
accounts.map((account) => this.collect(account, options))
|
|
1500
|
+
);
|
|
1501
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
1502
|
+
}
|
|
1503
|
+
collect(account, options = {}) {
|
|
1504
|
+
const now = this.now();
|
|
1505
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
1506
|
+
const existing = this.store.get("copilot", account.id, now);
|
|
1507
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
1508
|
+
return Promise.resolve(existing);
|
|
1509
|
+
}
|
|
1510
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
1511
|
+
this.store.set(snapshot);
|
|
1512
|
+
return Promise.resolve(snapshot);
|
|
1513
|
+
}
|
|
1514
|
+
const cached = this.store.get("copilot", account.id, now);
|
|
1515
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
1516
|
+
return Promise.resolve(cached);
|
|
1517
|
+
}
|
|
1518
|
+
const running = this.inFlight.get(account.id);
|
|
1519
|
+
if (running) return running;
|
|
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));
|
|
1521
|
+
this.inFlight.set(account.id, promise);
|
|
1522
|
+
return promise;
|
|
1523
|
+
}
|
|
1524
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
1525
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
1526
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
1527
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
1528
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
1529
|
+
}
|
|
1530
|
+
async fetchAccount(accountId, tokens) {
|
|
1531
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
|
|
1532
|
+
if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
|
|
1533
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
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());
|
|
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
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
if (!response.ok) return this.failureSnapshot(accountId, "copilot_usage_http_error", this.now());
|
|
1545
|
+
let payload;
|
|
1546
|
+
try {
|
|
1547
|
+
payload = await response.json();
|
|
1548
|
+
} catch {
|
|
1549
|
+
return this.failureSnapshot(accountId, "copilot_usage_invalid_response", this.now());
|
|
1550
|
+
}
|
|
1551
|
+
const now = this.now();
|
|
1552
|
+
const windows = parseCopilotUserPayload(payload, now);
|
|
1553
|
+
const snapshot = {
|
|
1554
|
+
providerId: "copilot",
|
|
1555
|
+
accountId,
|
|
1556
|
+
source: "oauth-usage-api",
|
|
1557
|
+
observedAt: new Date(now).toISOString(),
|
|
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" }
|
|
1561
|
+
],
|
|
1562
|
+
...windows ? {} : { lastErrorCode: "copilot_usage_invalid_response" }
|
|
1563
|
+
};
|
|
1564
|
+
this.store.set(snapshot);
|
|
1565
|
+
return snapshot;
|
|
1566
|
+
}
|
|
1567
|
+
request(accountId, accessToken, tokens) {
|
|
1568
|
+
return this.fetchImpl(`${githubApiBase(tokens)}/copilot_internal/user`, {
|
|
1569
|
+
method: "GET",
|
|
1570
|
+
headers: {
|
|
1571
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1572
|
+
Accept: "application/json",
|
|
1573
|
+
"Content-Type": "application/json",
|
|
1574
|
+
...COPILOT_GITHUB_HEADERS
|
|
1575
|
+
},
|
|
1576
|
+
signal: AbortSignal.timeout(15e3)
|
|
1577
|
+
}, accountId);
|
|
1578
|
+
}
|
|
1579
|
+
failureSnapshot(accountId, code, now) {
|
|
1580
|
+
const existing = this.store.get("copilot", accountId, now);
|
|
1581
|
+
const snapshot = existing ? {
|
|
1582
|
+
...existing,
|
|
1583
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1584
|
+
windows: existing.windows.map((window) => ({
|
|
1585
|
+
...window,
|
|
1586
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
1587
|
+
})),
|
|
1588
|
+
lastErrorCode: code
|
|
1589
|
+
} : {
|
|
1590
|
+
providerId: "copilot",
|
|
1591
|
+
accountId,
|
|
1592
|
+
source: "oauth-usage-api",
|
|
1593
|
+
observedAt: new Date(now).toISOString(),
|
|
1594
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1595
|
+
windows: [
|
|
1596
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
|
|
1597
|
+
],
|
|
1598
|
+
lastErrorCode: code
|
|
1599
|
+
};
|
|
1600
|
+
this.store.set(snapshot);
|
|
1601
|
+
return snapshot;
|
|
1602
|
+
}
|
|
1603
|
+
unsupportedSnapshot(accountId, now) {
|
|
1604
|
+
return {
|
|
1605
|
+
providerId: "copilot",
|
|
1606
|
+
accountId,
|
|
1607
|
+
source: "oauth-usage-api",
|
|
1608
|
+
observedAt: new Date(now).toISOString(),
|
|
1609
|
+
windows: [
|
|
1610
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unsupported" }
|
|
1611
|
+
],
|
|
1612
|
+
lastErrorCode: "copilot_usage_unsupported_auth"
|
|
1613
|
+
};
|
|
822
1614
|
}
|
|
823
|
-
|
|
1615
|
+
};
|
|
1616
|
+
|
|
1617
|
+
// src/allowance/GeminiAllowanceCollector.ts
|
|
1618
|
+
import { getGeminiCodeAssistProjectResolver } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
|
|
1619
|
+
import {
|
|
1620
|
+
getSharedAccountAllowanceStore as getSharedAccountAllowanceStore6
|
|
1621
|
+
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
1622
|
+
import { fetchUpstream as fetchUpstream6 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
1623
|
+
import {
|
|
1624
|
+
getGeminiCliIdentityHeaders,
|
|
1625
|
+
resolveCodeAssistEndpoint
|
|
1626
|
+
} from "@omnicross/core/transformer/transformers";
|
|
1627
|
+
var GEMINI_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
1628
|
+
function isRecord4(value) {
|
|
1629
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
824
1630
|
}
|
|
825
|
-
|
|
826
|
-
|
|
1631
|
+
function secondsUntil6(instant, now) {
|
|
1632
|
+
if (!instant) return void 0;
|
|
1633
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
1634
|
+
}
|
|
1635
|
+
function parseGeminiQuotaPayload(payload, now) {
|
|
1636
|
+
if (!isRecord4(payload)) return null;
|
|
1637
|
+
const buckets = Array.isArray(payload["buckets"]) ? payload["buckets"] : [];
|
|
1638
|
+
const windows = [];
|
|
1639
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1640
|
+
for (const raw of buckets) {
|
|
1641
|
+
if (!isRecord4(raw)) continue;
|
|
1642
|
+
const modelId = typeof raw["modelId"] === "string" && raw["modelId"].trim() ? raw["modelId"].trim() : void 0;
|
|
1643
|
+
const id = `gemini:${modelId ?? "all"}`;
|
|
1644
|
+
if (seen.has(id)) continue;
|
|
1645
|
+
seen.add(id);
|
|
1646
|
+
const fractionRaw = typeof raw["remainingFraction"] === "number" ? raw["remainingFraction"] : Number(raw["remainingFraction"]);
|
|
1647
|
+
const usedPercent = Number.isFinite(fractionRaw) ? Math.round(Math.min(100, Math.max(0, (1 - Math.min(1, Math.max(0, fractionRaw))) * 100)) * 10) / 10 : null;
|
|
1648
|
+
const resetRaw = typeof raw["resetTime"] === "string" && raw["resetTime"].trim() ? raw["resetTime"] : void 0;
|
|
1649
|
+
const resetsAt = resetRaw !== void 0 && Number.isFinite(Date.parse(resetRaw)) ? new Date(Date.parse(resetRaw)).toISOString() : void 0;
|
|
1650
|
+
windows.push({
|
|
1651
|
+
id,
|
|
1652
|
+
label: modelId ? `Gemini ${modelId}` : "Gemini quota",
|
|
1653
|
+
scope: modelId ? "model-family" : "all",
|
|
1654
|
+
...modelId ? { modelFamily: modelId } : {},
|
|
1655
|
+
usedPercent,
|
|
1656
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
1657
|
+
remainingSeconds: secondsUntil6(resetsAt, now),
|
|
1658
|
+
state: "fresh"
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
return windows.length > 0 ? windows : null;
|
|
1662
|
+
}
|
|
1663
|
+
var GeminiAllowanceCollector = class {
|
|
1664
|
+
constructor(credentials, store = getSharedAccountAllowanceStore6(), fetchImpl = (url, init, accountId) => fetchUpstream6(url, init, { providerId: "gemini", accountId, redactBodies: true }), now = Date.now, projectResolver = getGeminiCodeAssistProjectResolver()) {
|
|
827
1665
|
this.credentials = credentials;
|
|
828
1666
|
this.store = store;
|
|
829
1667
|
this.fetchImpl = fetchImpl;
|
|
830
1668
|
this.now = now;
|
|
1669
|
+
this.projectResolver = projectResolver;
|
|
831
1670
|
}
|
|
832
1671
|
credentials;
|
|
833
1672
|
store;
|
|
834
1673
|
fetchImpl;
|
|
835
1674
|
now;
|
|
1675
|
+
projectResolver;
|
|
836
1676
|
inFlight = /* @__PURE__ */ new Map();
|
|
837
1677
|
async collectMany(accounts, options = {}) {
|
|
838
|
-
const settled = await Promise.allSettled(
|
|
1678
|
+
const settled = await Promise.allSettled(
|
|
1679
|
+
accounts.map((account) => this.collect(account, options))
|
|
1680
|
+
);
|
|
839
1681
|
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
840
1682
|
}
|
|
841
1683
|
collect(account, options = {}) {
|
|
842
1684
|
const now = this.now();
|
|
843
1685
|
if (account.tokens.authMethod !== "oauth") {
|
|
844
|
-
const existing = this.store.get("
|
|
1686
|
+
const existing = this.store.get("gemini", account.id, now);
|
|
845
1687
|
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
846
1688
|
return Promise.resolve(existing);
|
|
847
1689
|
}
|
|
@@ -849,13 +1691,13 @@ var KimiAllowanceCollector = class {
|
|
|
849
1691
|
this.store.set(snapshot);
|
|
850
1692
|
return Promise.resolve(snapshot);
|
|
851
1693
|
}
|
|
852
|
-
const cached = this.store.get("
|
|
1694
|
+
const cached = this.store.get("gemini", account.id, now);
|
|
853
1695
|
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
854
1696
|
return Promise.resolve(cached);
|
|
855
1697
|
}
|
|
856
1698
|
const running = this.inFlight.get(account.id);
|
|
857
1699
|
if (running) return running;
|
|
858
|
-
const promise = this.fetchAccount(account.id
|
|
1700
|
+
const promise = this.fetchAccount(account.id).catch(() => this.failureSnapshot(account.id, "gemini_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
859
1701
|
this.inFlight.set(account.id, promise);
|
|
860
1702
|
return promise;
|
|
861
1703
|
}
|
|
@@ -865,102 +1707,105 @@ var KimiAllowanceCollector = class {
|
|
|
865
1707
|
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
866
1708
|
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
867
1709
|
}
|
|
868
|
-
async fetchAccount(accountId
|
|
869
|
-
let accessToken = await this.credentials.getAccessTokenForAccount("
|
|
870
|
-
if (!accessToken) return this.failureSnapshot(accountId, "
|
|
871
|
-
let
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
877
|
-
response = await this.request(accountId, accessToken, tokens);
|
|
1710
|
+
async fetchAccount(accountId) {
|
|
1711
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("gemini", accountId);
|
|
1712
|
+
if (!accessToken) return this.failureSnapshot(accountId, "gemini_usage_token_unavailable", this.now());
|
|
1713
|
+
let project;
|
|
1714
|
+
try {
|
|
1715
|
+
project = await this.projectResolver.resolveProject(accessToken);
|
|
1716
|
+
} catch {
|
|
1717
|
+
project = void 0;
|
|
878
1718
|
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
this.
|
|
882
|
-
return
|
|
1719
|
+
let response = await this.request(accountId, accessToken, project);
|
|
1720
|
+
if (response.status === 401 || response.status === 403) {
|
|
1721
|
+
const refreshed = await this.credentials.refreshAccountToken("gemini", accountId);
|
|
1722
|
+
if (!refreshed) return this.failureSnapshot(accountId, "gemini_usage_unauthorized", this.now());
|
|
1723
|
+
accessToken = await this.credentials.getAccessTokenForAccount("gemini", accountId);
|
|
1724
|
+
if (!accessToken) return this.failureSnapshot(accountId, "gemini_usage_token_unavailable", this.now());
|
|
1725
|
+
response = await this.request(accountId, accessToken, project);
|
|
1726
|
+
if (response.status === 401 || response.status === 403) {
|
|
1727
|
+
return this.failureSnapshot(accountId, "gemini_usage_unauthorized", this.now());
|
|
1728
|
+
}
|
|
883
1729
|
}
|
|
884
|
-
if (!response.ok) return this.failureSnapshot(accountId, "
|
|
1730
|
+
if (!response.ok) return this.failureSnapshot(accountId, "gemini_usage_http_error", this.now());
|
|
885
1731
|
let payload;
|
|
886
1732
|
try {
|
|
887
1733
|
payload = await response.json();
|
|
888
1734
|
} catch {
|
|
889
|
-
return this.failureSnapshot(accountId, "
|
|
1735
|
+
return this.failureSnapshot(accountId, "gemini_usage_invalid_response", this.now());
|
|
890
1736
|
}
|
|
891
1737
|
const now = this.now();
|
|
892
|
-
const windows =
|
|
1738
|
+
const windows = parseGeminiQuotaPayload(payload, now);
|
|
893
1739
|
const snapshot = {
|
|
894
|
-
providerId: "
|
|
1740
|
+
providerId: "gemini",
|
|
895
1741
|
accountId,
|
|
896
1742
|
source: "oauth-usage-api",
|
|
897
1743
|
observedAt: new Date(now).toISOString(),
|
|
898
|
-
expiresAt: new Date(now +
|
|
899
|
-
windows: windows
|
|
900
|
-
{ id: "
|
|
901
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
1744
|
+
expiresAt: new Date(now + GEMINI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1745
|
+
windows: windows ?? [
|
|
1746
|
+
{ id: "gemini-quota", label: "Gemini quota", scope: "all", usedPercent: null, state: "unavailable" }
|
|
902
1747
|
],
|
|
903
|
-
...windows
|
|
1748
|
+
...windows ? {} : { lastErrorCode: "gemini_usage_invalid_response" }
|
|
904
1749
|
};
|
|
905
1750
|
this.store.set(snapshot);
|
|
906
1751
|
return snapshot;
|
|
907
1752
|
}
|
|
908
|
-
request(accountId, accessToken,
|
|
909
|
-
return this.fetchImpl(
|
|
910
|
-
method: "
|
|
1753
|
+
request(accountId, accessToken, project) {
|
|
1754
|
+
return this.fetchImpl(`${resolveCodeAssistEndpoint()}/v1internal:retrieveUserQuota`, {
|
|
1755
|
+
method: "POST",
|
|
911
1756
|
headers: {
|
|
912
1757
|
Authorization: `Bearer ${accessToken}`,
|
|
913
1758
|
Accept: "application/json",
|
|
914
|
-
|
|
1759
|
+
"Content-Type": "application/json",
|
|
1760
|
+
...getGeminiCliIdentityHeaders()
|
|
915
1761
|
},
|
|
1762
|
+
body: JSON.stringify(project ? { project } : {}),
|
|
916
1763
|
signal: AbortSignal.timeout(15e3)
|
|
917
1764
|
}, accountId);
|
|
918
1765
|
}
|
|
919
1766
|
failureSnapshot(accountId, code, now) {
|
|
920
|
-
const existing = this.store.get("
|
|
1767
|
+
const existing = this.store.get("gemini", accountId, now);
|
|
921
1768
|
const snapshot = existing ? {
|
|
922
1769
|
...existing,
|
|
923
|
-
expiresAt: new Date(now +
|
|
1770
|
+
expiresAt: new Date(now + GEMINI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
924
1771
|
windows: existing.windows.map((window) => ({
|
|
925
1772
|
...window,
|
|
926
1773
|
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
927
1774
|
})),
|
|
928
1775
|
lastErrorCode: code
|
|
929
1776
|
} : {
|
|
930
|
-
providerId: "
|
|
1777
|
+
providerId: "gemini",
|
|
931
1778
|
accountId,
|
|
932
1779
|
source: "oauth-usage-api",
|
|
933
1780
|
observedAt: new Date(now).toISOString(),
|
|
934
|
-
expiresAt: new Date(now +
|
|
1781
|
+
expiresAt: new Date(now + GEMINI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
935
1782
|
windows: [
|
|
936
|
-
{ id: "
|
|
937
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
1783
|
+
{ id: "gemini-quota", label: "Gemini quota", scope: "all", usedPercent: null, state: "unavailable" }
|
|
938
1784
|
],
|
|
939
1785
|
lastErrorCode: code
|
|
940
1786
|
};
|
|
941
1787
|
this.store.set(snapshot);
|
|
942
1788
|
return snapshot;
|
|
943
1789
|
}
|
|
944
|
-
unsupportedSnapshot(accountId, now
|
|
1790
|
+
unsupportedSnapshot(accountId, now) {
|
|
945
1791
|
return {
|
|
946
|
-
providerId: "
|
|
1792
|
+
providerId: "gemini",
|
|
947
1793
|
accountId,
|
|
948
1794
|
source: "oauth-usage-api",
|
|
949
1795
|
observedAt: new Date(now).toISOString(),
|
|
950
1796
|
windows: [
|
|
951
|
-
{ id: "
|
|
952
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
1797
|
+
{ id: "gemini-quota", label: "Gemini quota", scope: "all", usedPercent: null, state: "unsupported" }
|
|
953
1798
|
],
|
|
954
|
-
lastErrorCode:
|
|
1799
|
+
lastErrorCode: "gemini_usage_unsupported_auth"
|
|
955
1800
|
};
|
|
956
1801
|
}
|
|
957
1802
|
};
|
|
958
1803
|
|
|
959
1804
|
// src/allowance/OpenCodeGoAllowanceCollector.ts
|
|
960
1805
|
import {
|
|
961
|
-
getSharedAccountAllowanceStore as
|
|
1806
|
+
getSharedAccountAllowanceStore as getSharedAccountAllowanceStore7
|
|
962
1807
|
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
963
|
-
import { fetchUpstream as
|
|
1808
|
+
import { fetchUpstream as fetchUpstream7 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
964
1809
|
import { normalizeOpenCodeGoBaseUrl } from "@omnicross/subscriptions";
|
|
965
1810
|
var OPENCODEGO_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
966
1811
|
var OPENCODEGO_DEFAULT_GO_BASE = "https://opencode.ai/zen/go";
|
|
@@ -974,7 +1819,7 @@ function isoInstant2(value) {
|
|
|
974
1819
|
const time = Date.parse(value);
|
|
975
1820
|
return Number.isFinite(time) ? new Date(time).toISOString() : void 0;
|
|
976
1821
|
}
|
|
977
|
-
function
|
|
1822
|
+
function secondsUntil7(instant, now) {
|
|
978
1823
|
if (!instant) return void 0;
|
|
979
1824
|
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
980
1825
|
}
|
|
@@ -989,12 +1834,12 @@ function windowFromPayload3(id, label, minutes, payload, now) {
|
|
|
989
1834
|
usedPercent,
|
|
990
1835
|
windowMinutes: minutes,
|
|
991
1836
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
992
|
-
remainingSeconds:
|
|
1837
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
993
1838
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
994
1839
|
};
|
|
995
1840
|
}
|
|
996
1841
|
var OpenCodeGoAllowanceCollector = class {
|
|
997
|
-
constructor(credentials, store =
|
|
1842
|
+
constructor(credentials, store = getSharedAccountAllowanceStore7(), fetchImpl = (url, init, accountId) => fetchUpstream7(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
|
|
998
1843
|
this.credentials = credentials;
|
|
999
1844
|
this.store = store;
|
|
1000
1845
|
this.fetchImpl = fetchImpl;
|
|
@@ -1099,7 +1944,7 @@ function codexUnavailable(accountId, now) {
|
|
|
1099
1944
|
};
|
|
1100
1945
|
}
|
|
1101
1946
|
var AccountAllowanceService = class {
|
|
1102
|
-
constructor(credentials, store =
|
|
1947
|
+
constructor(credentials, store = getSharedAccountAllowanceStore8(), collector, codexCollector, kimiCollector, opencodegoCollector, grokCollector, copilotCollector, geminiCollector, now = Date.now) {
|
|
1103
1948
|
this.credentials = credentials;
|
|
1104
1949
|
this.store = store;
|
|
1105
1950
|
this.now = now;
|
|
@@ -1107,6 +1952,9 @@ var AccountAllowanceService = class {
|
|
|
1107
1952
|
this.codexCollector = codexCollector ?? new CodexAllowanceCollector(credentials, store);
|
|
1108
1953
|
this.kimiCollector = kimiCollector ?? new KimiAllowanceCollector(credentials, store);
|
|
1109
1954
|
this.opencodegoCollector = opencodegoCollector ?? new OpenCodeGoAllowanceCollector(credentials, store);
|
|
1955
|
+
this.grokCollector = grokCollector ?? new GrokAllowanceCollector(credentials, store);
|
|
1956
|
+
this.copilotCollector = copilotCollector ?? new CopilotAllowanceCollector(credentials, store);
|
|
1957
|
+
this.geminiCollector = geminiCollector ?? new GeminiAllowanceCollector(credentials, store);
|
|
1110
1958
|
}
|
|
1111
1959
|
credentials;
|
|
1112
1960
|
store;
|
|
@@ -1114,7 +1962,10 @@ var AccountAllowanceService = class {
|
|
|
1114
1962
|
claudeCollector;
|
|
1115
1963
|
codexCollector;
|
|
1116
1964
|
kimiCollector;
|
|
1965
|
+
grokCollector;
|
|
1966
|
+
copilotCollector;
|
|
1117
1967
|
opencodegoCollector;
|
|
1968
|
+
geminiCollector;
|
|
1118
1969
|
/**
|
|
1119
1970
|
* Read all/filtered snapshots. Claude's and Codex's five-minute caches are
|
|
1120
1971
|
* refreshed lazily on read (Codex polls `/backend-api/wham/usage`; the
|
|
@@ -1148,11 +1999,29 @@ var AccountAllowanceService = class {
|
|
|
1148
1999
|
(account) => !filter.accountId || account.id === filter.accountId
|
|
1149
2000
|
);
|
|
1150
2001
|
if (wantsOpenCodeGo) await this.opencodegoCollector.collectMany(opencodegoAccounts);
|
|
2002
|
+
const wantsGrok = !filter.providerId || filter.providerId === "grok";
|
|
2003
|
+
const grokAccounts = (config.grokAccounts ?? []).filter(
|
|
2004
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
2005
|
+
);
|
|
2006
|
+
if (wantsGrok) await this.grokCollector.collectMany(grokAccounts);
|
|
2007
|
+
const wantsCopilot = !filter.providerId || filter.providerId === "copilot";
|
|
2008
|
+
const copilotAccounts = (config.copilotAccounts ?? []).filter(
|
|
2009
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
2010
|
+
);
|
|
2011
|
+
if (wantsCopilot) await this.copilotCollector.collectMany(copilotAccounts);
|
|
2012
|
+
const wantsGemini = !filter.providerId || filter.providerId === "gemini";
|
|
2013
|
+
const geminiAccounts = (config.geminiAccounts ?? []).filter(
|
|
2014
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
2015
|
+
);
|
|
2016
|
+
if (wantsGemini) await this.geminiCollector.collectMany(geminiAccounts);
|
|
1151
2017
|
const known = /* @__PURE__ */ new Set();
|
|
1152
2018
|
if (wantsClaude) for (const account of claudeAccounts) known.add(`claude\0${account.id}`);
|
|
1153
2019
|
if (wantsCodex) for (const account of codexAccounts) known.add(`codex\0${account.id}`);
|
|
1154
2020
|
if (wantsKimi) for (const account of kimiAccounts) known.add(`kimi\0${account.id}`);
|
|
1155
2021
|
if (wantsOpenCodeGo) for (const account of opencodegoAccounts) known.add(`opencodego\0${account.id}`);
|
|
2022
|
+
if (wantsGrok) for (const account of grokAccounts) known.add(`grok\0${account.id}`);
|
|
2023
|
+
if (wantsCopilot) for (const account of copilotAccounts) known.add(`copilot\0${account.id}`);
|
|
2024
|
+
if (wantsGemini) for (const account of geminiAccounts) known.add(`gemini\0${account.id}`);
|
|
1156
2025
|
return this.store.list(filter).filter((snapshot) => known.has(`${snapshot.providerId}\0${snapshot.accountId}`));
|
|
1157
2026
|
}
|
|
1158
2027
|
knownAccounts(config) {
|
|
@@ -1160,7 +2029,10 @@ var AccountAllowanceService = class {
|
|
|
1160
2029
|
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
1161
2030
|
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id })),
|
|
1162
2031
|
...(config.kimiAccounts ?? []).map((account) => ({ providerId: "kimi", accountId: account.id })),
|
|
1163
|
-
...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id }))
|
|
2032
|
+
...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id })),
|
|
2033
|
+
...(config.grokAccounts ?? []).map((account) => ({ providerId: "grok", accountId: account.id })),
|
|
2034
|
+
...(config.copilotAccounts ?? []).map((account) => ({ providerId: "copilot", accountId: account.id })),
|
|
2035
|
+
...(config.geminiAccounts ?? []).map((account) => ({ providerId: "gemini", accountId: account.id }))
|
|
1164
2036
|
];
|
|
1165
2037
|
}
|
|
1166
2038
|
/** Force-refresh Claude usage for one account or every stored Claude account. */
|
|
@@ -1203,6 +2075,33 @@ var AccountAllowanceService = class {
|
|
|
1203
2075
|
);
|
|
1204
2076
|
return this.kimiCollector.collectMany(accounts, { force: true });
|
|
1205
2077
|
}
|
|
2078
|
+
/** Force-refresh Copilot usage (copilot_internal/user) for one/all accounts. */
|
|
2079
|
+
async refreshCopilot(accountId) {
|
|
2080
|
+
const config = await this.credentials.getFullConfig();
|
|
2081
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
2082
|
+
const accounts = (config.copilotAccounts ?? []).filter(
|
|
2083
|
+
(account) => !accountId || account.id === accountId
|
|
2084
|
+
);
|
|
2085
|
+
return this.copilotCollector.collectMany(accounts, { force: true });
|
|
2086
|
+
}
|
|
2087
|
+
/** Force-refresh Grok usage (CLI billing proxy) for one/all accounts. */
|
|
2088
|
+
async refreshGrok(accountId) {
|
|
2089
|
+
const config = await this.credentials.getFullConfig();
|
|
2090
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
2091
|
+
const accounts = (config.grokAccounts ?? []).filter(
|
|
2092
|
+
(account) => !accountId || account.id === accountId
|
|
2093
|
+
);
|
|
2094
|
+
return this.grokCollector.collectMany(accounts, { force: true });
|
|
2095
|
+
}
|
|
2096
|
+
/** Force-refresh Gemini usage (Code Assist retrieveUserQuota) for one/all accounts. */
|
|
2097
|
+
async refreshGemini(accountId) {
|
|
2098
|
+
const config = await this.credentials.getFullConfig();
|
|
2099
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
2100
|
+
const accounts = (config.geminiAccounts ?? []).filter(
|
|
2101
|
+
(account) => !accountId || account.id === accountId
|
|
2102
|
+
);
|
|
2103
|
+
return this.geminiCollector.collectMany(accounts, { force: true });
|
|
2104
|
+
}
|
|
1206
2105
|
/**
|
|
1207
2106
|
* Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
|
|
1208
2107
|
* collectors preserve their cache + per-account in-flight coalescing; a tick
|
|
@@ -1217,6 +2116,9 @@ var AccountAllowanceService = class {
|
|
|
1217
2116
|
await this.codexCollector.collectMany(config.codexAccounts ?? [], { refreshAheadMs });
|
|
1218
2117
|
await this.kimiCollector.collectMany(config.kimiAccounts ?? [], { refreshAheadMs });
|
|
1219
2118
|
await this.opencodegoCollector.collectMany(config.opencodegoAccounts ?? [], { refreshAheadMs });
|
|
2119
|
+
await this.grokCollector.collectMany(config.grokAccounts ?? [], { refreshAheadMs });
|
|
2120
|
+
await this.copilotCollector.collectMany(config.copilotAccounts ?? [], { refreshAheadMs });
|
|
2121
|
+
await this.geminiCollector.collectMany(config.geminiAccounts ?? [], { refreshAheadMs });
|
|
1220
2122
|
}
|
|
1221
2123
|
/** Remove a cache row as soon as an account is deleted by the admin path. */
|
|
1222
2124
|
removeAccountSnapshot(providerId, accountId) {
|
|
@@ -1653,7 +2555,8 @@ import {
|
|
|
1653
2555
|
} from "@omnicross/contracts/image-generation-types";
|
|
1654
2556
|
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling2 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
1655
2557
|
import { getSharedAccountHealth } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
1656
|
-
import { fetchUpstream as
|
|
2558
|
+
import { fetchUpstream as fetchUpstream8 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
2559
|
+
import { mergeExtraHeaders } from "@omnicross/core";
|
|
1657
2560
|
|
|
1658
2561
|
// src/image-generation/imagesConfigValidation.ts
|
|
1659
2562
|
import { validateImagesServerConfig } from "@omnicross/core/outbound-api";
|
|
@@ -1966,6 +2869,7 @@ async function applyServerConfigTransaction(current, next, deps) {
|
|
|
1966
2869
|
|
|
1967
2870
|
// src/config.ts
|
|
1968
2871
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
2872
|
+
import { EXTRA_HEADER_RESERVED_NAMES } from "@omnicross/core";
|
|
1969
2873
|
|
|
1970
2874
|
// src/secrets/envelope.ts
|
|
1971
2875
|
import { createCipheriv, createDecipheriv, randomBytes as randomBytes2 } from "crypto";
|
|
@@ -2377,6 +3281,18 @@ var FORMAT_AXIS_TRANSFORMERS = [
|
|
|
2377
3281
|
"openai-response",
|
|
2378
3282
|
"gemini-code-assist"
|
|
2379
3283
|
];
|
|
3284
|
+
function validateExtraHeaders(raw) {
|
|
3285
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
3286
|
+
const reserved = EXTRA_HEADER_RESERVED_NAMES;
|
|
3287
|
+
const out = {};
|
|
3288
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
3289
|
+
if (!name.trim()) continue;
|
|
3290
|
+
if (typeof value !== "string") continue;
|
|
3291
|
+
if (reserved.has(name.toLowerCase())) continue;
|
|
3292
|
+
out[name] = value;
|
|
3293
|
+
}
|
|
3294
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
3295
|
+
}
|
|
2380
3296
|
function validateApiKeys(raw) {
|
|
2381
3297
|
if (!Array.isArray(raw)) return void 0;
|
|
2382
3298
|
const out = [];
|
|
@@ -2590,6 +3506,9 @@ function validateProvider(raw, index) {
|
|
|
2590
3506
|
apiVersion,
|
|
2591
3507
|
maxConcurrency,
|
|
2592
3508
|
modelsEndpoint,
|
|
3509
|
+
// Static extra headers: load-guard (reserved names dropped), collapse-to-
|
|
3510
|
+
// undefined; enforced by the outbound header funnel + admin probes.
|
|
3511
|
+
extraHeaders: validateExtraHeaders(p["extraHeaders"]),
|
|
2593
3512
|
// Provider transformer config (app-parity child 5): load-guard, collapse-to-
|
|
2594
3513
|
// undefined; non-secret; ENFORCED via resolveTransformerChain (parity-2 child 2).
|
|
2595
3514
|
// Format-axis entries are stripped by `migrateFormatAxis` — `use[]` is the
|
|
@@ -3559,7 +4478,8 @@ function listMappablePresets() {
|
|
|
3559
4478
|
description: preset.description,
|
|
3560
4479
|
features: preset.features,
|
|
3561
4480
|
website: preset.website,
|
|
3562
|
-
modelsEndpoint: preset.modelsEndpoint
|
|
4481
|
+
modelsEndpoint: preset.modelsEndpoint,
|
|
4482
|
+
extraHeaders: preset.extraHeaders
|
|
3563
4483
|
});
|
|
3564
4484
|
}
|
|
3565
4485
|
return { mappable, excluded };
|
|
@@ -3731,7 +4651,9 @@ var VALID_PROVIDER_IDS = [
|
|
|
3731
4651
|
"codex",
|
|
3732
4652
|
"gemini",
|
|
3733
4653
|
"opencodego",
|
|
3734
|
-
"kimi"
|
|
4654
|
+
"kimi",
|
|
4655
|
+
"grok",
|
|
4656
|
+
"copilot"
|
|
3735
4657
|
];
|
|
3736
4658
|
function asSubscriptionProviderId(id) {
|
|
3737
4659
|
return VALID_PROVIDER_IDS.includes(id) ? id : null;
|
|
@@ -3879,6 +4801,40 @@ function validateKimi(body) {
|
|
|
3879
4801
|
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "deviceId", "lastRefreshedAt", "errorMessage"]);
|
|
3880
4802
|
return out;
|
|
3881
4803
|
}
|
|
4804
|
+
function validateGrok(body) {
|
|
4805
|
+
const authMethod = str(body["authMethod"]);
|
|
4806
|
+
const status = str(body["status"]);
|
|
4807
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
4808
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
4809
|
+
const out = {
|
|
4810
|
+
authMethod,
|
|
4811
|
+
status
|
|
4812
|
+
};
|
|
4813
|
+
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "lastRefreshedAt", "errorMessage"]);
|
|
4814
|
+
return out;
|
|
4815
|
+
}
|
|
4816
|
+
function validateCopilot(body) {
|
|
4817
|
+
const authMethod = str(body["authMethod"]);
|
|
4818
|
+
const status = str(body["status"]);
|
|
4819
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
4820
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
4821
|
+
const out = {
|
|
4822
|
+
authMethod,
|
|
4823
|
+
status
|
|
4824
|
+
};
|
|
4825
|
+
copyOptional(out, body, [
|
|
4826
|
+
"accessToken",
|
|
4827
|
+
"refreshToken",
|
|
4828
|
+
"expiresAt",
|
|
4829
|
+
"accountId",
|
|
4830
|
+
"email",
|
|
4831
|
+
"apiEndpoint",
|
|
4832
|
+
"enterpriseUrl",
|
|
4833
|
+
"lastRefreshedAt",
|
|
4834
|
+
"errorMessage"
|
|
4835
|
+
]);
|
|
4836
|
+
return out;
|
|
4837
|
+
}
|
|
3882
4838
|
function validateOpenCodeGo(body) {
|
|
3883
4839
|
const authMethod = str(body["authMethod"]);
|
|
3884
4840
|
const status = str(body["status"]);
|
|
@@ -3916,6 +4872,10 @@ function validateTokenBody(providerId, body) {
|
|
|
3916
4872
|
return validateOpenCodeGo(body);
|
|
3917
4873
|
case "kimi":
|
|
3918
4874
|
return validateKimi(body);
|
|
4875
|
+
case "grok":
|
|
4876
|
+
return validateGrok(body);
|
|
4877
|
+
case "copilot":
|
|
4878
|
+
return validateCopilot(body);
|
|
3919
4879
|
default:
|
|
3920
4880
|
return null;
|
|
3921
4881
|
}
|
|
@@ -3945,12 +4905,12 @@ async function statusEntryFor(reader, providerId) {
|
|
|
3945
4905
|
|
|
3946
4906
|
// src/admin/accountsOAuth.ts
|
|
3947
4907
|
var OAUTH_HTTP_PROVIDERS = /* @__PURE__ */ new Set(["claude", "gemini"]);
|
|
3948
|
-
function
|
|
4908
|
+
function err5(status, message) {
|
|
3949
4909
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
3950
4910
|
}
|
|
3951
4911
|
function handleOAuthStart(providerId, deps) {
|
|
3952
4912
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
3953
|
-
return
|
|
4913
|
+
return err5(400, `oauth not available for provider '${providerId}'`);
|
|
3954
4914
|
}
|
|
3955
4915
|
const flow = providerId === "claude" ? claudeOAuth : geminiOAuth;
|
|
3956
4916
|
const { authUrl, codeVerifier, state } = flow.generateAuthParams();
|
|
@@ -3959,23 +4919,23 @@ function handleOAuthStart(providerId, deps) {
|
|
|
3959
4919
|
}
|
|
3960
4920
|
async function handleOAuthComplete(providerId, body, deps) {
|
|
3961
4921
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
3962
|
-
return
|
|
4922
|
+
return err5(400, `oauth not available for provider '${providerId}'`);
|
|
3963
4923
|
}
|
|
3964
4924
|
const sessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : "";
|
|
3965
4925
|
const rawCode = typeof body["code"] === "string" ? body["code"] : "";
|
|
3966
|
-
if (!sessionId) return
|
|
3967
|
-
if (!rawCode) return
|
|
4926
|
+
if (!sessionId) return err5(400, "oauth complete requires { sessionId }");
|
|
4927
|
+
if (!rawCode) return err5(400, "oauth complete requires { code }");
|
|
3968
4928
|
const session = deps.oauthSessions.peek(sessionId);
|
|
3969
|
-
if (!session) return
|
|
4929
|
+
if (!session) return err5(410, "oauth session is unknown, expired, or already used");
|
|
3970
4930
|
if (session.providerId !== providerId) {
|
|
3971
|
-
return
|
|
4931
|
+
return err5(400, `oauth session does not match provider '${providerId}'`);
|
|
3972
4932
|
}
|
|
3973
4933
|
let code = rawCode.trim();
|
|
3974
4934
|
if (providerId === "claude") {
|
|
3975
4935
|
const [splitCode, pastedState] = code.split("#");
|
|
3976
|
-
if (!splitCode) return
|
|
4936
|
+
if (!splitCode) return err5(400, "no authorization code was provided");
|
|
3977
4937
|
if (pastedState && pastedState !== session.state) {
|
|
3978
|
-
return
|
|
4938
|
+
return err5(400, "oauth state did not match (possible CSRF) \u2014 aborting");
|
|
3979
4939
|
}
|
|
3980
4940
|
code = splitCode;
|
|
3981
4941
|
}
|
|
@@ -3985,7 +4945,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
3985
4945
|
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
|
|
3986
4946
|
} catch (exchangeError) {
|
|
3987
4947
|
const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
|
|
3988
|
-
return
|
|
4948
|
+
return err5(502, `oauth token exchange failed for '${providerId}': ${reason}`);
|
|
3989
4949
|
}
|
|
3990
4950
|
deps.oauthSessions.consume(sessionId);
|
|
3991
4951
|
const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
|
|
@@ -4323,8 +5283,8 @@ function errBody(message) {
|
|
|
4323
5283
|
return { error: { type: "admin_api_error", message } };
|
|
4324
5284
|
}
|
|
4325
5285
|
var defaultCommandRunner = (command) => new Promise((resolve10) => {
|
|
4326
|
-
exec(command, { timeout: 18e4 }, (
|
|
4327
|
-
if (
|
|
5286
|
+
exec(command, { timeout: 18e4 }, (err8, _stdout, stderr) => {
|
|
5287
|
+
if (err8) resolve10({ ok: false, error: stderr.trim() || err8.message });
|
|
4328
5288
|
else resolve10({ ok: true });
|
|
4329
5289
|
});
|
|
4330
5290
|
});
|
|
@@ -4370,8 +5330,8 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
4370
5330
|
providerId: typeof body["providerId"] === "string" ? body["providerId"] : void 0,
|
|
4371
5331
|
model: typeof body["model"] === "string" ? body["model"] : void 0
|
|
4372
5332
|
});
|
|
4373
|
-
} catch (
|
|
4374
|
-
return { status: 400, body: errBody(
|
|
5333
|
+
} catch (err8) {
|
|
5334
|
+
return { status: 400, body: errBody(err8 instanceof Error ? err8.message : "no launch target") };
|
|
4375
5335
|
}
|
|
4376
5336
|
const id = randomUUID2();
|
|
4377
5337
|
let leaseId2;
|
|
@@ -4399,9 +5359,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
4399
5359
|
} else {
|
|
4400
5360
|
launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
|
|
4401
5361
|
}
|
|
4402
|
-
} catch (
|
|
4403
|
-
const status =
|
|
4404
|
-
return { status, body: errBody(
|
|
5362
|
+
} catch (err8) {
|
|
5363
|
+
const status = err8 instanceof RouteLeaseError2 ? err8.status : 400;
|
|
5364
|
+
return { status, body: errBody(err8 instanceof Error ? err8.message : "failed to build launch env") };
|
|
4405
5365
|
}
|
|
4406
5366
|
const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
|
|
4407
5367
|
const opener = ctx.opener ?? defaultTerminalOpener;
|
|
@@ -4429,9 +5389,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
4429
5389
|
onFailure: onSessionEnd
|
|
4430
5390
|
});
|
|
4431
5391
|
if (cleanup) openerCleanup = cleanup;
|
|
4432
|
-
} catch (
|
|
5392
|
+
} catch (err8) {
|
|
4433
5393
|
onSessionEnd();
|
|
4434
|
-
return { status: 500, body: errBody(
|
|
5394
|
+
return { status: 500, body: errBody(err8 instanceof Error ? err8.message : "failed to open terminal") };
|
|
4435
5395
|
}
|
|
4436
5396
|
if (ended) {
|
|
4437
5397
|
openerCleanup?.();
|
|
@@ -4980,7 +5940,7 @@ async function handleSearchQuery(req, res, deps) {
|
|
|
4980
5940
|
// src/admin/searchAdminView.ts
|
|
4981
5941
|
var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
|
|
4982
5942
|
var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
|
|
4983
|
-
function
|
|
5943
|
+
function isRecord5(value) {
|
|
4984
5944
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4985
5945
|
}
|
|
4986
5946
|
function redactSearchServerConfig(search) {
|
|
@@ -5030,13 +5990,13 @@ function resolveSecretField(entry, field, stored) {
|
|
|
5030
5990
|
else delete entry[field];
|
|
5031
5991
|
}
|
|
5032
5992
|
function preserveSearchSecrets(incoming, current) {
|
|
5033
|
-
if (!
|
|
5993
|
+
if (!isRecord5(incoming)) return incoming;
|
|
5034
5994
|
const section = { ...incoming };
|
|
5035
5995
|
const providersValue = section["providers"];
|
|
5036
|
-
if (!
|
|
5996
|
+
if (!isRecord5(providersValue)) return section;
|
|
5037
5997
|
const providers = {};
|
|
5038
5998
|
for (const [id, entryValue] of Object.entries(providersValue)) {
|
|
5039
|
-
if (!
|
|
5999
|
+
if (!isRecord5(entryValue)) {
|
|
5040
6000
|
providers[id] = entryValue;
|
|
5041
6001
|
continue;
|
|
5042
6002
|
}
|
|
@@ -5114,7 +6074,7 @@ function parseKeyPolicyBody(body) {
|
|
|
5114
6074
|
var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
|
|
5115
6075
|
var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
|
|
5116
6076
|
var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
|
|
5117
|
-
function
|
|
6077
|
+
function isRecord6(value) {
|
|
5118
6078
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
5119
6079
|
}
|
|
5120
6080
|
function nonBlank(value) {
|
|
@@ -5134,7 +6094,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5134
6094
|
const ids = /* @__PURE__ */ new Set();
|
|
5135
6095
|
raw.forEach((entry, index) => {
|
|
5136
6096
|
const path2 = `bindings[${index}]`;
|
|
5137
|
-
if (!
|
|
6097
|
+
if (!isRecord6(entry)) {
|
|
5138
6098
|
errors.push(`${path2} must be an object`);
|
|
5139
6099
|
return;
|
|
5140
6100
|
}
|
|
@@ -5163,12 +6123,12 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5163
6123
|
} else if (entry.modelMappings.length > 100) {
|
|
5164
6124
|
errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
|
|
5165
6125
|
} else if (entry.modelMappings.some(
|
|
5166
|
-
(mapping) => !
|
|
6126
|
+
(mapping) => !isRecord6(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
|
|
5167
6127
|
)) {
|
|
5168
6128
|
errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
|
|
5169
6129
|
}
|
|
5170
6130
|
}
|
|
5171
|
-
if (!
|
|
6131
|
+
if (!isRecord6(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
|
|
5172
6132
|
errors.push(`${path2}.target is invalid`);
|
|
5173
6133
|
} else {
|
|
5174
6134
|
if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
|
|
@@ -5183,7 +6143,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5183
6143
|
}
|
|
5184
6144
|
}
|
|
5185
6145
|
if (entry.modelMap !== void 0) {
|
|
5186
|
-
if (!
|
|
6146
|
+
if (!isRecord6(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
|
|
5187
6147
|
errors.push(`${path2}.modelMap must contain string values`);
|
|
5188
6148
|
}
|
|
5189
6149
|
}
|
|
@@ -5494,7 +6454,9 @@ var PROVIDER_KEYS = {
|
|
|
5494
6454
|
accounts: "opencodegoAccounts",
|
|
5495
6455
|
active: "activeOpencodegoAccountId"
|
|
5496
6456
|
},
|
|
5497
|
-
kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" }
|
|
6457
|
+
kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" },
|
|
6458
|
+
grok: { block: "grok", accounts: "grokAccounts", active: "activeGrokAccountId" },
|
|
6459
|
+
copilot: { block: "copilot", accounts: "copilotAccounts", active: "activeCopilotAccountId" }
|
|
5498
6460
|
};
|
|
5499
6461
|
function clone(value) {
|
|
5500
6462
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -6016,7 +6978,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
|
|
|
6016
6978
|
}
|
|
6017
6979
|
|
|
6018
6980
|
// src/admin/adminMigration.ts
|
|
6019
|
-
function
|
|
6981
|
+
function err6(status, message) {
|
|
6020
6982
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
6021
6983
|
}
|
|
6022
6984
|
async function handleExport(body, deps) {
|
|
@@ -6026,30 +6988,30 @@ async function handleExport(body, deps) {
|
|
|
6026
6988
|
return { status: 200, body: { pack, version: BUNDLE_VERSION } };
|
|
6027
6989
|
} catch (error) {
|
|
6028
6990
|
if (error instanceof WeakPassphraseError) {
|
|
6029
|
-
return
|
|
6991
|
+
return err6(400, error.message);
|
|
6030
6992
|
}
|
|
6031
|
-
return
|
|
6993
|
+
return err6(500, "failed to build the migration pack");
|
|
6032
6994
|
}
|
|
6033
6995
|
}
|
|
6034
6996
|
async function handleImport(body, deps) {
|
|
6035
6997
|
const blob = typeof body["blob"] === "string" ? body["blob"] : "";
|
|
6036
6998
|
const passphrase = typeof body["passphrase"] === "string" ? body["passphrase"] : "";
|
|
6037
6999
|
const mode = body["mode"] === "overwrite" ? "overwrite" : "merge";
|
|
6038
|
-
if (!blob) return
|
|
7000
|
+
if (!blob) return err6(400, "import requires { blob }");
|
|
6039
7001
|
try {
|
|
6040
7002
|
const counts = await applyImport(blob, passphrase, mode, deps, deps.parseProviderInput);
|
|
6041
7003
|
return { status: 200, body: counts };
|
|
6042
7004
|
} catch (error) {
|
|
6043
7005
|
if (error instanceof WeakPassphraseError) {
|
|
6044
|
-
return
|
|
7006
|
+
return err6(400, error.message);
|
|
6045
7007
|
}
|
|
6046
|
-
return
|
|
7008
|
+
return err6(400, error instanceof Error ? error.message : "import failed");
|
|
6047
7009
|
}
|
|
6048
7010
|
}
|
|
6049
7011
|
|
|
6050
7012
|
// src/admin/usagePricing.ts
|
|
6051
7013
|
import { getSharedUsageThroughputTracker } from "@omnicross/core/usage";
|
|
6052
|
-
var
|
|
7014
|
+
var err7 = (status, message) => ({
|
|
6053
7015
|
status,
|
|
6054
7016
|
body: { error: { type: "admin_api_error", message } }
|
|
6055
7017
|
});
|
|
@@ -6062,7 +7024,7 @@ function parseRange(query2) {
|
|
|
6062
7024
|
const startTs = parseFiniteInt(query2.get("startTs"));
|
|
6063
7025
|
const endTs = parseFiniteInt(query2.get("endTs"));
|
|
6064
7026
|
if (startTs === null || endTs === null) {
|
|
6065
|
-
return
|
|
7027
|
+
return err7(400, "startTs and endTs are required finite-integer unix-millis query params");
|
|
6066
7028
|
}
|
|
6067
7029
|
return { startTs, endTs };
|
|
6068
7030
|
}
|
|
@@ -6087,14 +7049,14 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
6087
7049
|
case "timeseries": {
|
|
6088
7050
|
const bucket = query2.get("bucket");
|
|
6089
7051
|
if (bucket !== "hour" && bucket !== "day" && bucket !== "month") {
|
|
6090
|
-
return
|
|
7052
|
+
return err7(400, "bucket must be one of 'hour', 'day', 'month'");
|
|
6091
7053
|
}
|
|
6092
7054
|
const now = Date.now();
|
|
6093
7055
|
const clamped = { startTs: range.startTs, endTs: Math.min(range.endTs, now) };
|
|
6094
7056
|
if (clamped.startTs < clamped.endTs) {
|
|
6095
7057
|
const projected = Math.ceil((clamped.endTs - clamped.startTs) / BUCKET_SPAN_MS[bucket]) + 1;
|
|
6096
7058
|
if (projected > MAX_TIMESERIES_BUCKETS) {
|
|
6097
|
-
return
|
|
7059
|
+
return err7(
|
|
6098
7060
|
400,
|
|
6099
7061
|
`requested range projects ~${projected} '${bucket}' buckets (max ${MAX_TIMESERIES_BUCKETS}); narrow the range or use a coarser bucket`
|
|
6100
7062
|
);
|
|
@@ -6117,7 +7079,7 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
6117
7079
|
};
|
|
6118
7080
|
}
|
|
6119
7081
|
default:
|
|
6120
|
-
return
|
|
7082
|
+
return err7(404, `unknown usage view '${view ?? ""}'`);
|
|
6121
7083
|
}
|
|
6122
7084
|
}
|
|
6123
7085
|
function poolKeyLabels(cfg) {
|
|
@@ -6166,7 +7128,7 @@ async function handlePricingList(deps) {
|
|
|
6166
7128
|
async function handlePricingUpsert(body, deps) {
|
|
6167
7129
|
const input = parsePricingEntryInput(body);
|
|
6168
7130
|
if (!input) {
|
|
6169
|
-
return
|
|
7131
|
+
return err7(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
|
|
6170
7132
|
}
|
|
6171
7133
|
const entry = await deps.pricingEngine.upsertManual(input);
|
|
6172
7134
|
return { status: 200, body: { entry } };
|
|
@@ -6175,7 +7137,7 @@ async function handlePricingDelete(query2, deps) {
|
|
|
6175
7137
|
const providerId = query2.get("providerId")?.trim() ?? "";
|
|
6176
7138
|
const modelId = query2.get("modelId")?.trim() ?? "";
|
|
6177
7139
|
if (!providerId || !modelId) {
|
|
6178
|
-
return
|
|
7140
|
+
return err7(400, "delete requires providerId and modelId query params");
|
|
6179
7141
|
}
|
|
6180
7142
|
const deleted = await deps.pricingStore.delete(providerId, modelId);
|
|
6181
7143
|
if (deleted) await deps.pricingEngine.invalidateCache();
|
|
@@ -6195,13 +7157,13 @@ async function handlePricingFetchLatest(deps) {
|
|
|
6195
7157
|
}
|
|
6196
7158
|
};
|
|
6197
7159
|
} catch (e) {
|
|
6198
|
-
return
|
|
7160
|
+
return err7(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
6199
7161
|
}
|
|
6200
7162
|
}
|
|
6201
7163
|
async function handlePricingResolveConflicts(body, deps) {
|
|
6202
7164
|
const raw = body["resolutions"];
|
|
6203
7165
|
if (!Array.isArray(raw)) {
|
|
6204
|
-
return
|
|
7166
|
+
return err7(400, "resolve-conflicts requires { resolutions: [...] }");
|
|
6205
7167
|
}
|
|
6206
7168
|
const currentRows = await deps.pricingStore.getAll();
|
|
6207
7169
|
const userEditedKeys = new Set(
|
|
@@ -6211,21 +7173,21 @@ async function handlePricingResolveConflicts(body, deps) {
|
|
|
6211
7173
|
const pendingIncoming = /* @__PURE__ */ new Map();
|
|
6212
7174
|
let staleCount = 0;
|
|
6213
7175
|
for (const item of raw) {
|
|
6214
|
-
if (!item || typeof item !== "object") return
|
|
7176
|
+
if (!item || typeof item !== "object") return err7(400, "invalid resolution entry");
|
|
6215
7177
|
const r = item;
|
|
6216
7178
|
const action = r["action"];
|
|
6217
7179
|
if (action !== "overwrite" && action !== "skip") {
|
|
6218
|
-
return
|
|
7180
|
+
return err7(400, "resolution action must be 'overwrite' or 'skip'");
|
|
6219
7181
|
}
|
|
6220
7182
|
const providerId = typeof r["providerId"] === "string" && r["providerId"].trim() ? r["providerId"].trim() : "";
|
|
6221
7183
|
const modelId = typeof r["modelId"] === "string" && r["modelId"].trim() ? r["modelId"].trim() : "";
|
|
6222
7184
|
if (!providerId || !modelId) {
|
|
6223
|
-
return
|
|
7185
|
+
return err7(400, "each resolution requires top-level providerId and modelId");
|
|
6224
7186
|
}
|
|
6225
7187
|
const incoming = parsePricingEntryInput(r["incoming"]);
|
|
6226
|
-
if (!incoming) return
|
|
7188
|
+
if (!incoming) return err7(400, "each resolution must echo a valid incoming pricing entry");
|
|
6227
7189
|
if (incoming.providerId !== providerId || incoming.modelId !== modelId) {
|
|
6228
|
-
return
|
|
7190
|
+
return err7(400, "resolution providerId/modelId must match the echoed incoming entry");
|
|
6229
7191
|
}
|
|
6230
7192
|
const key = `${providerId}::${modelId}`;
|
|
6231
7193
|
if (action === "overwrite" && !userEditedKeys.has(key)) {
|
|
@@ -6270,7 +7232,7 @@ function query(req) {
|
|
|
6270
7232
|
}
|
|
6271
7233
|
function allowanceProvider(value) {
|
|
6272
7234
|
if (!value) return void 0;
|
|
6273
|
-
return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" ? value : null;
|
|
7235
|
+
return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" || value === "grok" || value === "copilot" || value === "gemini" ? value : null;
|
|
6274
7236
|
}
|
|
6275
7237
|
async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
6276
7238
|
if (!service) return writeError2(res, 501, "account allowance service is not available");
|
|
@@ -6285,7 +7247,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
6285
7247
|
const pathProvider = rest.length >= 2 ? rest[0] : null;
|
|
6286
7248
|
const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
|
|
6287
7249
|
if (providerId === null) {
|
|
6288
|
-
return writeError2(res, 400, "providerId must be claude, codex, kimi, or
|
|
7250
|
+
return writeError2(res, 400, "providerId must be claude, codex, kimi, opencodego, grok, copilot, or gemini");
|
|
6289
7251
|
}
|
|
6290
7252
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
6291
7253
|
const allowances = await service.list({ providerId, accountId });
|
|
@@ -6327,6 +7289,36 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
6327
7289
|
}
|
|
6328
7290
|
return writeJson3(res, 200, { allowances: allowances2 });
|
|
6329
7291
|
}
|
|
7292
|
+
if (requestedProvider === "copilot") {
|
|
7293
|
+
if (!service.refreshCopilot) {
|
|
7294
|
+
return writeError2(res, 501, "copilot allowance refresh is not available");
|
|
7295
|
+
}
|
|
7296
|
+
const allowances2 = await service.refreshCopilot(accountId);
|
|
7297
|
+
if (accountId && allowances2.length === 0) {
|
|
7298
|
+
return writeError2(res, 404, `Copilot account '${accountId}' not found`);
|
|
7299
|
+
}
|
|
7300
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7301
|
+
}
|
|
7302
|
+
if (requestedProvider === "grok") {
|
|
7303
|
+
if (!service.refreshGrok) {
|
|
7304
|
+
return writeError2(res, 501, "grok allowance refresh is not available");
|
|
7305
|
+
}
|
|
7306
|
+
const allowances2 = await service.refreshGrok(accountId);
|
|
7307
|
+
if (accountId && allowances2.length === 0) {
|
|
7308
|
+
return writeError2(res, 404, `Grok account '${accountId}' not found`);
|
|
7309
|
+
}
|
|
7310
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7311
|
+
}
|
|
7312
|
+
if (requestedProvider === "gemini") {
|
|
7313
|
+
if (!service.refreshGemini) {
|
|
7314
|
+
return writeError2(res, 501, "gemini allowance refresh is not available");
|
|
7315
|
+
}
|
|
7316
|
+
const allowances2 = await service.refreshGemini(accountId);
|
|
7317
|
+
if (accountId && allowances2.length === 0) {
|
|
7318
|
+
return writeError2(res, 404, `Gemini account '${accountId}' not found`);
|
|
7319
|
+
}
|
|
7320
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7321
|
+
}
|
|
6330
7322
|
const allowances = await service.refreshClaude(accountId);
|
|
6331
7323
|
if (accountId && allowances.length === 0) {
|
|
6332
7324
|
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
@@ -6428,6 +7420,9 @@ function toProviderView(row) {
|
|
|
6428
7420
|
apiVersion: row.apiVersion,
|
|
6429
7421
|
maxConcurrency: row.maxConcurrency,
|
|
6430
7422
|
modelsEndpoint: row.modelsEndpoint,
|
|
7423
|
+
// Static extra headers round-trip VERBATIM (non-secret identity values;
|
|
7424
|
+
// auth/content names were already dropped at the write/load gate).
|
|
7425
|
+
extraHeaders: row.extraHeaders,
|
|
6431
7426
|
// app-parity child 5: transformer config round-trips VERBATIM (non-secret —
|
|
6432
7427
|
// transform-rule names + options, no key material; absent stays absent).
|
|
6433
7428
|
transformer: row.transformer,
|
|
@@ -6497,8 +7492,8 @@ async function handleAdminApi(req, res, path2, deps) {
|
|
|
6497
7492
|
default:
|
|
6498
7493
|
return writeJsonError(res, 404, `unknown admin resource '${resource}'`);
|
|
6499
7494
|
}
|
|
6500
|
-
} catch (
|
|
6501
|
-
writeJsonError(res, 500,
|
|
7495
|
+
} catch (err8) {
|
|
7496
|
+
writeJsonError(res, 500, err8 instanceof Error ? err8.message : String(err8));
|
|
6502
7497
|
}
|
|
6503
7498
|
}
|
|
6504
7499
|
function requestQuery(req) {
|
|
@@ -6656,6 +7651,9 @@ async function handleProviderReorder(req, res, cfg, deps) {
|
|
|
6656
7651
|
persistProviders(cfg, deps);
|
|
6657
7652
|
return writeJson4(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
|
|
6658
7653
|
}
|
|
7654
|
+
function expandRowExtraHeaders(row) {
|
|
7655
|
+
return mergeExtraHeaders({}, row.extraHeaders);
|
|
7656
|
+
}
|
|
6659
7657
|
async function handleDiscoverModels(res, id, cfg) {
|
|
6660
7658
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
6661
7659
|
const row = cfg.providers.find((p) => p.id === id);
|
|
@@ -6669,7 +7667,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
6669
7667
|
try {
|
|
6670
7668
|
const headers = { Accept: "application/json" };
|
|
6671
7669
|
if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
|
|
6672
|
-
|
|
7670
|
+
Object.assign(headers, expandRowExtraHeaders(row));
|
|
7671
|
+
const response = await fetchUpstream8(url, { method: "GET", headers }, { providerId: "byo" });
|
|
6673
7672
|
if (!response.ok) {
|
|
6674
7673
|
const text = await response.text().catch(() => "");
|
|
6675
7674
|
let message = text.slice(0, 300);
|
|
@@ -6686,8 +7685,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
6686
7685
|
const data = await response.json();
|
|
6687
7686
|
const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
|
|
6688
7687
|
return writeJson4(res, 200, { models });
|
|
6689
|
-
} catch (
|
|
6690
|
-
const message =
|
|
7688
|
+
} catch (err8) {
|
|
7689
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
6691
7690
|
return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
|
|
6692
7691
|
}
|
|
6693
7692
|
}
|
|
@@ -6726,9 +7725,10 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
6726
7725
|
messages: [{ role: "user", content: prompt }]
|
|
6727
7726
|
};
|
|
6728
7727
|
}
|
|
7728
|
+
Object.assign(headers, expandRowExtraHeaders(row));
|
|
6729
7729
|
const startedAt = Date.now();
|
|
6730
7730
|
try {
|
|
6731
|
-
const response = await
|
|
7731
|
+
const response = await fetchUpstream8(
|
|
6732
7732
|
url,
|
|
6733
7733
|
{ method: "POST", headers, body: JSON.stringify(payload) },
|
|
6734
7734
|
{ providerId: "byo" }
|
|
@@ -6750,8 +7750,8 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
6750
7750
|
latencyMs,
|
|
6751
7751
|
sample: extractSampleText(text, row.apiFormat)
|
|
6752
7752
|
});
|
|
6753
|
-
} catch (
|
|
6754
|
-
const message =
|
|
7753
|
+
} catch (err8) {
|
|
7754
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
6755
7755
|
return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
|
|
6756
7756
|
}
|
|
6757
7757
|
}
|
|
@@ -7033,6 +8033,7 @@ function parseProviderInput(body, existing) {
|
|
|
7033
8033
|
const apiVersion = typeof body["apiVersion"] === "string" && body["apiVersion"].length > 0 ? body["apiVersion"] : body["apiVersion"] === null ? void 0 : existing?.apiVersion;
|
|
7034
8034
|
const modelsEndpoint = typeof body["modelsEndpoint"] === "string" && body["modelsEndpoint"].length > 0 ? body["modelsEndpoint"] : body["modelsEndpoint"] === null ? void 0 : existing?.modelsEndpoint;
|
|
7035
8035
|
const maxConcurrency = typeof body["maxConcurrency"] === "number" && Number.isFinite(body["maxConcurrency"]) ? body["maxConcurrency"] : body["maxConcurrency"] === null ? void 0 : existing?.maxConcurrency;
|
|
8036
|
+
const extraHeaders = body["extraHeaders"] === null ? void 0 : body["extraHeaders"] === void 0 ? existing?.extraHeaders : validateExtraHeaders(body["extraHeaders"]);
|
|
7036
8037
|
const transformer = body["transformer"] === null ? void 0 : parseTransformerInput(body["transformer"], existing?.transformer);
|
|
7037
8038
|
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
8039
|
const apiModes = body["apiModes"] === null ? void 0 : parseApiModesInput(body["apiModes"], existing?.apiModes);
|
|
@@ -7058,6 +8059,7 @@ function parseProviderInput(body, existing) {
|
|
|
7058
8059
|
apiVersion,
|
|
7059
8060
|
maxConcurrency,
|
|
7060
8061
|
modelsEndpoint,
|
|
8062
|
+
extraHeaders,
|
|
7061
8063
|
transformer: migrated.transformer,
|
|
7062
8064
|
codingPlan,
|
|
7063
8065
|
apiModes,
|
|
@@ -7079,7 +8081,10 @@ function handlePresets(res, method) {
|
|
|
7079
8081
|
description: p.description,
|
|
7080
8082
|
features: p.features,
|
|
7081
8083
|
website: p.website,
|
|
7082
|
-
modelsEndpoint: p.modelsEndpoint
|
|
8084
|
+
modelsEndpoint: p.modelsEndpoint,
|
|
8085
|
+
// Static extra headers ride along so `addFromPreset` can seed them onto the
|
|
8086
|
+
// row (the write gateway re-validates via the shared allowlist).
|
|
8087
|
+
extraHeaders: p.extraHeaders
|
|
7083
8088
|
}));
|
|
7084
8089
|
return writeJson4(res, 200, { presets, excluded });
|
|
7085
8090
|
}
|
|
@@ -7561,12 +8566,12 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
7561
8566
|
}
|
|
7562
8567
|
return writeJson4(res, 200, { ok: true, affected: result.affected });
|
|
7563
8568
|
}
|
|
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);
|
|
8569
|
+
if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[3] === "status") {
|
|
8570
|
+
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
8571
|
return writeJson4(res, result.status, result.body);
|
|
7567
8572
|
}
|
|
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);
|
|
8573
|
+
if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[2]) {
|
|
8574
|
+
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
8575
|
return writeJson4(res, result.status, result.body);
|
|
7571
8576
|
}
|
|
7572
8577
|
if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
|
|
@@ -7627,6 +8632,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
7627
8632
|
const result2 = await handleKimiOAuthStart(deps);
|
|
7628
8633
|
return writeJson4(res, result2.status, result2.body);
|
|
7629
8634
|
}
|
|
8635
|
+
if (providerId === "grok") {
|
|
8636
|
+
const result2 = await handleGrokOAuthStart(deps);
|
|
8637
|
+
return writeJson4(res, result2.status, result2.body);
|
|
8638
|
+
}
|
|
8639
|
+
if (providerId === "copilot") {
|
|
8640
|
+
const body2 = await readJsonBody4(req);
|
|
8641
|
+
const result2 = await handleCopilotOAuthStart(deps, body2["enterpriseUrl"]);
|
|
8642
|
+
return writeJson4(res, result2.status, result2.body);
|
|
8643
|
+
}
|
|
7630
8644
|
const result = handleOAuthStart(providerId, deps);
|
|
7631
8645
|
return writeJson4(res, result.status, result.body);
|
|
7632
8646
|
}
|
|
@@ -8121,12 +9135,12 @@ async function handlePlayground(req, res, method, deps) {
|
|
|
8121
9135
|
const payload = body["body"];
|
|
8122
9136
|
const status = deps.outboundApiServer.getStatus();
|
|
8123
9137
|
if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
|
|
8124
|
-
const path2 = resolvePlaygroundPath(endpoint,
|
|
9138
|
+
const path2 = resolvePlaygroundPath(endpoint, isRecord7(payload) ? payload : {});
|
|
8125
9139
|
if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
|
|
8126
9140
|
const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
|
|
8127
9141
|
await proxyToOutbound(res, status.port, path2, key, upstreamBody);
|
|
8128
9142
|
}
|
|
8129
|
-
function
|
|
9143
|
+
function isRecord7(v) {
|
|
8130
9144
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
8131
9145
|
}
|
|
8132
9146
|
function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
@@ -8155,8 +9169,8 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
|
8155
9169
|
});
|
|
8156
9170
|
}
|
|
8157
9171
|
);
|
|
8158
|
-
upstream.on("error", (
|
|
8159
|
-
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${
|
|
9172
|
+
upstream.on("error", (err8) => {
|
|
9173
|
+
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err8.message}`);
|
|
8160
9174
|
else res.end();
|
|
8161
9175
|
resolve10();
|
|
8162
9176
|
});
|
|
@@ -8261,7 +9275,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
8261
9275
|
}
|
|
8262
9276
|
|
|
8263
9277
|
// src/admin/version.ts
|
|
8264
|
-
var DAEMON_VERSION = true ? "0.
|
|
9278
|
+
var DAEMON_VERSION = true ? "0.4.1" : "0.0.0-dev";
|
|
8265
9279
|
|
|
8266
9280
|
// src/admin/AdminServer.ts
|
|
8267
9281
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -8304,13 +9318,13 @@ var AdminServer = class {
|
|
|
8304
9318
|
const server = http2.createServer((req, res) => {
|
|
8305
9319
|
this.onRequest(req, res);
|
|
8306
9320
|
});
|
|
8307
|
-
const onError = (
|
|
8308
|
-
if (
|
|
9321
|
+
const onError = (err8) => {
|
|
9322
|
+
if (err8.code === "EADDRINUSE" && port !== 0) {
|
|
8309
9323
|
server.removeListener("error", onError);
|
|
8310
9324
|
this.listen(bindAddr, 0).then(resolve10, reject);
|
|
8311
9325
|
return;
|
|
8312
9326
|
}
|
|
8313
|
-
reject(
|
|
9327
|
+
reject(err8);
|
|
8314
9328
|
};
|
|
8315
9329
|
server.on("error", onError);
|
|
8316
9330
|
server.listen(port, bindAddr, () => {
|
|
@@ -8328,8 +9342,8 @@ var AdminServer = class {
|
|
|
8328
9342
|
}
|
|
8329
9343
|
/** Per-request handler: auth gate (when a token is set) → routing. */
|
|
8330
9344
|
onRequest(req, res) {
|
|
8331
|
-
void this.dispatch(req, res).catch((
|
|
8332
|
-
const message =
|
|
9345
|
+
void this.dispatch(req, res).catch((err8) => {
|
|
9346
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
8333
9347
|
this.deps.logger.error("[AdminServer] unhandled error:", message);
|
|
8334
9348
|
if (!res.headersSent) {
|
|
8335
9349
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
@@ -8593,18 +9607,18 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
8593
9607
|
return;
|
|
8594
9608
|
}
|
|
8595
9609
|
signal?.addEventListener("abort", abort, { once: true });
|
|
8596
|
-
server.on("error", (
|
|
9610
|
+
server.on("error", (err8) => {
|
|
8597
9611
|
if (settled) return;
|
|
8598
9612
|
settled = true;
|
|
8599
9613
|
clearTimeout(timer);
|
|
8600
|
-
if (
|
|
9614
|
+
if (err8.code === "EADDRINUSE") {
|
|
8601
9615
|
reject(
|
|
8602
9616
|
new Error(
|
|
8603
9617
|
`login: cannot bind ${LOOPBACK_HOST}:${LOOPBACK_PORT} (address in use) \u2014 another codex login or process is holding the port`
|
|
8604
9618
|
)
|
|
8605
9619
|
);
|
|
8606
9620
|
} else {
|
|
8607
|
-
reject(
|
|
9621
|
+
reject(err8);
|
|
8608
9622
|
}
|
|
8609
9623
|
});
|
|
8610
9624
|
const timer = setTimeout(() => {
|
|
@@ -8680,21 +9694,22 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
|
|
|
8680
9694
|
}
|
|
8681
9695
|
|
|
8682
9696
|
// src/allowance/ProviderKeyQuotaService.ts
|
|
8683
|
-
import {
|
|
9697
|
+
import { mergeExtraHeaders as mergeExtraHeaders2 } from "@omnicross/core";
|
|
9698
|
+
import { fetchUpstream as fetchUpstream9 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
8684
9699
|
|
|
8685
9700
|
// src/allowance/ProviderKeyQuota.ts
|
|
8686
|
-
var
|
|
8687
|
-
var HOUR_MS2 = 60 *
|
|
8688
|
-
var
|
|
8689
|
-
var WEEK_MS = 7 *
|
|
8690
|
-
var MONTH_MS = 30 *
|
|
8691
|
-
function
|
|
9701
|
+
var MINUTE_MS3 = 6e4;
|
|
9702
|
+
var HOUR_MS2 = 60 * MINUTE_MS3;
|
|
9703
|
+
var DAY_MS3 = 24 * HOUR_MS2;
|
|
9704
|
+
var WEEK_MS = 7 * DAY_MS3;
|
|
9705
|
+
var MONTH_MS = 30 * DAY_MS3;
|
|
9706
|
+
function finiteNumber5(value) {
|
|
8692
9707
|
if (value === null || value === void 0 || value === "") return void 0;
|
|
8693
9708
|
const parsed = typeof value === "number" ? value : Number(value);
|
|
8694
9709
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
8695
9710
|
}
|
|
8696
9711
|
function finitePercent4(value) {
|
|
8697
|
-
const parsed =
|
|
9712
|
+
const parsed = finiteNumber5(value);
|
|
8698
9713
|
return parsed !== void 0 && parsed <= 100 ? parsed : null;
|
|
8699
9714
|
}
|
|
8700
9715
|
function isoInstant3(value) {
|
|
@@ -8702,18 +9717,18 @@ function isoInstant3(value) {
|
|
|
8702
9717
|
const time = Date.parse(value);
|
|
8703
9718
|
if (Number.isFinite(time)) return new Date(time).toISOString();
|
|
8704
9719
|
}
|
|
8705
|
-
const numeric =
|
|
9720
|
+
const numeric = finiteNumber5(value);
|
|
8706
9721
|
if (numeric !== void 0 && numeric > 1e9) {
|
|
8707
9722
|
const ms = numeric > 1e12 ? numeric : numeric * 1e3;
|
|
8708
9723
|
return new Date(ms).toISOString();
|
|
8709
9724
|
}
|
|
8710
9725
|
return void 0;
|
|
8711
9726
|
}
|
|
8712
|
-
function
|
|
9727
|
+
function secondsUntil8(instant, now) {
|
|
8713
9728
|
if (!instant) return void 0;
|
|
8714
9729
|
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
8715
9730
|
}
|
|
8716
|
-
function
|
|
9731
|
+
function isRecord8(value) {
|
|
8717
9732
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
8718
9733
|
}
|
|
8719
9734
|
function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
@@ -8726,7 +9741,7 @@ function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
|
8726
9741
|
}
|
|
8727
9742
|
const host = url.hostname.toLowerCase();
|
|
8728
9743
|
const path2 = url.pathname.toLowerCase();
|
|
8729
|
-
if ((host === "api.z.ai" || host === "open.bigmodel.cn") && path2.includes("/coding")) {
|
|
9744
|
+
if ((host === "api.z.ai" || host === "open.bigmodel.cn") && (path2.includes("/coding") || path2.includes("/anthropic"))) {
|
|
8730
9745
|
return "zai";
|
|
8731
9746
|
}
|
|
8732
9747
|
if ((host === "api.minimax.io" || host === "api.minimaxi.com") && // Token Plan rides the plain openai `/v1` (chat completions) surface; the
|
|
@@ -8736,6 +9751,7 @@ function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
|
8736
9751
|
}
|
|
8737
9752
|
if (host === "api.code.umans.ai") return "umans";
|
|
8738
9753
|
if (host === "api.synthetic.new") return "synthetic";
|
|
9754
|
+
if (host === "api.cline.bot") return "cline-pass";
|
|
8739
9755
|
return null;
|
|
8740
9756
|
}
|
|
8741
9757
|
function providerKeyQuotaUrl(adapter, baseUrl) {
|
|
@@ -8743,6 +9759,7 @@ function providerKeyQuotaUrl(adapter, baseUrl) {
|
|
|
8743
9759
|
if (adapter === "zai") return `${origin}/api/monitor/usage/quota/limit`;
|
|
8744
9760
|
if (adapter === "minimax-token-plan") return `${origin}/v1/token_plan/remains`;
|
|
8745
9761
|
if (adapter === "umans") return `${origin}/v1/usage`;
|
|
9762
|
+
if (adapter === "cline-pass") return `${origin}/api/v1/users/me/plan/usage-limits`;
|
|
8746
9763
|
return `${origin}/v2/quotas`;
|
|
8747
9764
|
}
|
|
8748
9765
|
function providerKeyQuotaAuthHeader(adapter, key) {
|
|
@@ -8754,7 +9771,7 @@ function zaiWindowDurationMs(item) {
|
|
|
8754
9771
|
case 3:
|
|
8755
9772
|
return count * HOUR_MS2;
|
|
8756
9773
|
case 4:
|
|
8757
|
-
return count *
|
|
9774
|
+
return count * DAY_MS3;
|
|
8758
9775
|
case 5:
|
|
8759
9776
|
return count * MONTH_MS;
|
|
8760
9777
|
case 6:
|
|
@@ -8767,8 +9784,8 @@ function zaiWindowIdLabel(durationMs) {
|
|
|
8767
9784
|
if (durationMs === WEEK_MS) return { id: "seven-day", label: "7 days" };
|
|
8768
9785
|
if (durationMs === 5 * HOUR_MS2) return { id: "five-hour", label: "5 hours" };
|
|
8769
9786
|
if (durationMs === MONTH_MS) return { id: "thirty-day", label: "30 days" };
|
|
8770
|
-
if (durationMs !== void 0 && durationMs %
|
|
8771
|
-
const days = durationMs /
|
|
9787
|
+
if (durationMs !== void 0 && durationMs % DAY_MS3 === 0) {
|
|
9788
|
+
const days = durationMs / DAY_MS3;
|
|
8772
9789
|
return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}` };
|
|
8773
9790
|
}
|
|
8774
9791
|
if (durationMs !== void 0 && durationMs % HOUR_MS2 === 0) {
|
|
@@ -8778,23 +9795,23 @@ function zaiWindowIdLabel(durationMs) {
|
|
|
8778
9795
|
return { id: "quota", label: "Quota" };
|
|
8779
9796
|
}
|
|
8780
9797
|
function parseZaiQuotaPayload(payload, now) {
|
|
8781
|
-
if (!
|
|
8782
|
-
const data =
|
|
9798
|
+
if (!isRecord8(payload)) return null;
|
|
9799
|
+
const data = isRecord8(payload["data"]) ? payload["data"] : payload;
|
|
8783
9800
|
if (payload["success"] === false) return null;
|
|
8784
9801
|
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
8785
9802
|
const byWindow = /* @__PURE__ */ new Map();
|
|
8786
9803
|
for (const raw of limits) {
|
|
8787
|
-
if (!
|
|
9804
|
+
if (!isRecord8(raw)) continue;
|
|
8788
9805
|
const item = raw;
|
|
8789
9806
|
if (item.type === void 0) continue;
|
|
8790
9807
|
const details = raw["usageDetails"];
|
|
8791
|
-
if (Array.isArray(details) && details.some((d) =>
|
|
9808
|
+
if (Array.isArray(details) && details.some((d) => isRecord8(d) && d["modelCode"] === "zread")) {
|
|
8792
9809
|
continue;
|
|
8793
9810
|
}
|
|
8794
9811
|
const durationMs = zaiWindowDurationMs(item);
|
|
8795
9812
|
const { id, label } = zaiWindowIdLabel(durationMs);
|
|
8796
|
-
const limit =
|
|
8797
|
-
const used =
|
|
9813
|
+
const limit = finiteNumber5(item.usage);
|
|
9814
|
+
const used = finiteNumber5(item.currentValue);
|
|
8798
9815
|
const fromAbsolute = limit !== void 0 && used !== void 0 && limit > 0 ? Math.min(100, used / limit * 100) : void 0;
|
|
8799
9816
|
const fromPercentage = finitePercent4(item.percentage) ?? void 0;
|
|
8800
9817
|
const usedPercent = fromAbsolute !== void 0 ? Math.round(fromAbsolute * 10) / 10 : fromPercentage;
|
|
@@ -8805,9 +9822,9 @@ function parseZaiQuotaPayload(payload, now) {
|
|
|
8805
9822
|
label,
|
|
8806
9823
|
scope: "all",
|
|
8807
9824
|
usedPercent,
|
|
8808
|
-
...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs /
|
|
9825
|
+
...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs / MINUTE_MS3) } : {},
|
|
8809
9826
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
8810
|
-
remainingSeconds:
|
|
9827
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
8811
9828
|
state: "fresh"
|
|
8812
9829
|
};
|
|
8813
9830
|
const existing = byWindow.get(id);
|
|
@@ -8821,21 +9838,21 @@ function parseZaiQuotaPayload(payload, now) {
|
|
|
8821
9838
|
var MINIMAX_STATUS_EXHAUSTED = 2;
|
|
8822
9839
|
var MINIMAX_SHARED_BUCKET = "general";
|
|
8823
9840
|
function parseMiniMaxBucket(value) {
|
|
8824
|
-
if (!
|
|
9841
|
+
if (!isRecord8(value)) return null;
|
|
8825
9842
|
const modelName = typeof value["model_name"] === "string" ? value["model_name"].trim() : "";
|
|
8826
9843
|
if (!modelName) return null;
|
|
8827
9844
|
const instant = (v) => {
|
|
8828
|
-
const n =
|
|
9845
|
+
const n = finiteNumber5(v);
|
|
8829
9846
|
return n !== void 0 && n > 1e9 ? n > 1e12 ? n : n * 1e3 : void 0;
|
|
8830
9847
|
};
|
|
8831
9848
|
return {
|
|
8832
9849
|
modelName,
|
|
8833
9850
|
intervalEnd: instant(value["end_time"]),
|
|
8834
|
-
intervalRemainingPercent:
|
|
8835
|
-
intervalStatus:
|
|
9851
|
+
intervalRemainingPercent: finiteNumber5(value["current_interval_remaining_percent"]),
|
|
9852
|
+
intervalStatus: finiteNumber5(value["current_interval_status"]),
|
|
8836
9853
|
weeklyEnd: instant(value["weekly_end_time"]),
|
|
8837
|
-
weeklyRemainingPercent:
|
|
8838
|
-
weeklyStatus:
|
|
9854
|
+
weeklyRemainingPercent: finiteNumber5(value["current_weekly_remaining_percent"]),
|
|
9855
|
+
weeklyStatus: finiteNumber5(value["current_weekly_status"])
|
|
8839
9856
|
};
|
|
8840
9857
|
}
|
|
8841
9858
|
function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, status, now) {
|
|
@@ -8848,14 +9865,14 @@ function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, s
|
|
|
8848
9865
|
usedPercent,
|
|
8849
9866
|
...windowMinutes !== void 0 ? { windowMinutes } : {},
|
|
8850
9867
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
8851
|
-
remainingSeconds:
|
|
9868
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
8852
9869
|
state: usedPercent !== null ? "fresh" : "unavailable"
|
|
8853
9870
|
};
|
|
8854
9871
|
}
|
|
8855
9872
|
function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
8856
|
-
if (!
|
|
9873
|
+
if (!isRecord8(payload)) return null;
|
|
8857
9874
|
const baseResp = payload["base_resp"];
|
|
8858
|
-
if (!
|
|
9875
|
+
if (!isRecord8(baseResp) || baseResp["status_code"] !== 0) return null;
|
|
8859
9876
|
const buckets = Array.isArray(payload["model_remains"]) ? payload["model_remains"] : [];
|
|
8860
9877
|
let general = null;
|
|
8861
9878
|
for (const raw of buckets) {
|
|
@@ -8879,7 +9896,7 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
|
8879
9896
|
minimaxWindow(
|
|
8880
9897
|
"seven-day",
|
|
8881
9898
|
"7 days",
|
|
8882
|
-
Math.round(WEEK_MS /
|
|
9899
|
+
Math.round(WEEK_MS / MINUTE_MS3),
|
|
8883
9900
|
general.weeklyEnd,
|
|
8884
9901
|
general.weeklyRemainingPercent,
|
|
8885
9902
|
general.weeklyStatus,
|
|
@@ -8888,15 +9905,15 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
|
8888
9905
|
];
|
|
8889
9906
|
}
|
|
8890
9907
|
function parseUmansUsagePayload(payload, now) {
|
|
8891
|
-
if (!
|
|
8892
|
-
const limits =
|
|
8893
|
-
const requests = limits &&
|
|
8894
|
-
const usage =
|
|
8895
|
-
const window =
|
|
8896
|
-
const hardCap =
|
|
8897
|
-
const softLimit =
|
|
8898
|
-
const requestsInWindow =
|
|
8899
|
-
const weightedInWindow =
|
|
9908
|
+
if (!isRecord8(payload)) return null;
|
|
9909
|
+
const limits = isRecord8(payload["limits"]) ? payload["limits"] : void 0;
|
|
9910
|
+
const requests = limits && isRecord8(limits["requests"]) ? limits["requests"] : void 0;
|
|
9911
|
+
const usage = isRecord8(payload["usage"]) ? payload["usage"] : void 0;
|
|
9912
|
+
const window = isRecord8(payload["window"]) ? payload["window"] : void 0;
|
|
9913
|
+
const hardCap = finiteNumber5(requests?.["hard_cap"]);
|
|
9914
|
+
const softLimit = finiteNumber5(requests?.["limit"]);
|
|
9915
|
+
const requestsInWindow = finiteNumber5(usage?.["requests_in_window"]);
|
|
9916
|
+
const weightedInWindow = finiteNumber5(usage?.["weighted_in_window"]);
|
|
8900
9917
|
const resetsAt = isoInstant3(window?.["resets_at"]);
|
|
8901
9918
|
let usedPercent = null;
|
|
8902
9919
|
if (hardCap !== void 0 && hardCap > 0 && requestsInWindow !== void 0) {
|
|
@@ -8913,19 +9930,19 @@ function parseUmansUsagePayload(payload, now) {
|
|
|
8913
9930
|
usedPercent,
|
|
8914
9931
|
windowMinutes: 5 * 60,
|
|
8915
9932
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
8916
|
-
remainingSeconds:
|
|
9933
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
8917
9934
|
state: "fresh"
|
|
8918
9935
|
}
|
|
8919
9936
|
];
|
|
8920
9937
|
}
|
|
8921
9938
|
function parseSyntheticQuotasPayload(payload, now) {
|
|
8922
|
-
if (!
|
|
8923
|
-
const fiveHour =
|
|
8924
|
-
const weekly =
|
|
9939
|
+
if (!isRecord8(payload)) return null;
|
|
9940
|
+
const fiveHour = isRecord8(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
|
|
9941
|
+
const weekly = isRecord8(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
|
|
8925
9942
|
const windows = [];
|
|
8926
9943
|
if (fiveHour) {
|
|
8927
|
-
const max =
|
|
8928
|
-
const remaining =
|
|
9944
|
+
const max = finiteNumber5(fiveHour["max"]);
|
|
9945
|
+
const remaining = finiteNumber5(fiveHour["remaining"]);
|
|
8929
9946
|
const usedPercent = max !== void 0 && max > 0 && remaining !== void 0 ? Math.round(Math.min(100, (max - remaining) / max * 100) * 10) / 10 : null;
|
|
8930
9947
|
const resetsAt = isoInstant3(fiveHour["nextTickAt"]);
|
|
8931
9948
|
windows.push({
|
|
@@ -8935,12 +9952,12 @@ function parseSyntheticQuotasPayload(payload, now) {
|
|
|
8935
9952
|
usedPercent,
|
|
8936
9953
|
windowMinutes: 5 * 60,
|
|
8937
9954
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
8938
|
-
remainingSeconds:
|
|
9955
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
8939
9956
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
8940
9957
|
});
|
|
8941
9958
|
}
|
|
8942
9959
|
if (weekly) {
|
|
8943
|
-
const percentRemaining =
|
|
9960
|
+
const percentRemaining = finiteNumber5(weekly["percentRemaining"]);
|
|
8944
9961
|
const usedPercent = percentRemaining !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - percentRemaining)) * 10) / 10 : null;
|
|
8945
9962
|
const resetsAt = isoInstant3(weekly["nextRegenAt"]);
|
|
8946
9963
|
windows.push({
|
|
@@ -8950,12 +9967,42 @@ function parseSyntheticQuotasPayload(payload, now) {
|
|
|
8950
9967
|
usedPercent,
|
|
8951
9968
|
windowMinutes: 7 * 24 * 60,
|
|
8952
9969
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
8953
|
-
remainingSeconds:
|
|
9970
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
8954
9971
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
8955
9972
|
});
|
|
8956
9973
|
}
|
|
8957
9974
|
return windows.length > 0 ? windows : null;
|
|
8958
9975
|
}
|
|
9976
|
+
var CLINE_WINDOW_CONFIG = {
|
|
9977
|
+
five_hour: { id: "five-hour", label: "5 hours", minutes: 5 * 60 },
|
|
9978
|
+
weekly: { id: "seven-day", label: "7 days", minutes: 7 * 24 * 60 },
|
|
9979
|
+
monthly: { id: "thirty-day", label: "30 days", minutes: 30 * 24 * 60 }
|
|
9980
|
+
};
|
|
9981
|
+
function parseClinePassUsageLimitsPayload(payload, now) {
|
|
9982
|
+
if (!isRecord8(payload)) return null;
|
|
9983
|
+
const data = isRecord8(payload["data"]) ? payload["data"] : payload;
|
|
9984
|
+
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
9985
|
+
const windows = [];
|
|
9986
|
+
for (const raw of limits) {
|
|
9987
|
+
if (!isRecord8(raw)) continue;
|
|
9988
|
+
const config = CLINE_WINDOW_CONFIG[typeof raw["type"] === "string" ? raw["type"] : ""];
|
|
9989
|
+
if (!config) continue;
|
|
9990
|
+
const usedPercent = finitePercent4(raw["percentUsed"]);
|
|
9991
|
+
if (usedPercent === null) continue;
|
|
9992
|
+
const resetsAt = isoInstant3(raw["resetsAt"]);
|
|
9993
|
+
windows.push({
|
|
9994
|
+
id: config.id,
|
|
9995
|
+
label: config.label,
|
|
9996
|
+
scope: "all",
|
|
9997
|
+
usedPercent,
|
|
9998
|
+
windowMinutes: config.minutes,
|
|
9999
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
10000
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
10001
|
+
state: "fresh"
|
|
10002
|
+
});
|
|
10003
|
+
}
|
|
10004
|
+
return windows.length > 0 ? windows : null;
|
|
10005
|
+
}
|
|
8959
10006
|
|
|
8960
10007
|
// src/allowance/ProviderKeyQuotaService.ts
|
|
8961
10008
|
function parseQuotaPayload(adapter, payload, now) {
|
|
@@ -8968,6 +10015,8 @@ function parseQuotaPayload(adapter, payload, now) {
|
|
|
8968
10015
|
return parseUmansUsagePayload(payload, now);
|
|
8969
10016
|
case "synthetic":
|
|
8970
10017
|
return parseSyntheticQuotasPayload(payload, now);
|
|
10018
|
+
case "cline-pass":
|
|
10019
|
+
return parseClinePassUsageLimitsPayload(payload, now);
|
|
8971
10020
|
}
|
|
8972
10021
|
}
|
|
8973
10022
|
var PROVIDER_KEY_QUOTA_CACHE_MS = 5 * 6e4;
|
|
@@ -8987,7 +10036,7 @@ function rowKeyEntries(row) {
|
|
|
8987
10036
|
return [];
|
|
8988
10037
|
}
|
|
8989
10038
|
var ProviderKeyQuotaService = class {
|
|
8990
|
-
constructor(box, fetchImpl = (url, init) =>
|
|
10039
|
+
constructor(box, fetchImpl = (url, init) => fetchUpstream9(url, init, { redactBodies: true }), now = Date.now) {
|
|
8991
10040
|
this.box = box;
|
|
8992
10041
|
this.fetchImpl = fetchImpl;
|
|
8993
10042
|
this.now = now;
|
|
@@ -9049,7 +10098,10 @@ var ProviderKeyQuotaService = class {
|
|
|
9049
10098
|
headers: {
|
|
9050
10099
|
Authorization: providerKeyQuotaAuthHeader(adapter, key),
|
|
9051
10100
|
Accept: "application/json",
|
|
9052
|
-
"Content-Type": "application/json"
|
|
10101
|
+
"Content-Type": "application/json",
|
|
10102
|
+
// The row's static identity headers ride along — the Cline usage
|
|
10103
|
+
// endpoint sits behind the SAME client-identity 403 gate as inference.
|
|
10104
|
+
...mergeExtraHeaders2({}, row.extraHeaders)
|
|
9053
10105
|
},
|
|
9054
10106
|
signal: AbortSignal.timeout(15e3)
|
|
9055
10107
|
});
|
|
@@ -13854,6 +14906,10 @@ function toLLMProvider(row) {
|
|
|
13854
14906
|
// `parseProviderInput`), so customizations are preserved (the row value wins).
|
|
13855
14907
|
apiModes: row.apiModes,
|
|
13856
14908
|
selectedApiModeId: row.selectedApiModeId,
|
|
14909
|
+
// Static extra request headers ride along verbatim (load-guarded — no
|
|
14910
|
+
// auth/content names); core's `getProviderHeaders` merges them into every
|
|
14911
|
+
// BYO request, and the same-format relay path inherits that funnel.
|
|
14912
|
+
extraHeaders: row.extraHeaders,
|
|
13857
14913
|
// Official-Anthropic signature handling only matters for the Anthropic
|
|
13858
14914
|
// ingress (deferred → 502); leave it off for the BYO transform path.
|
|
13859
14915
|
isOfficial: false
|
|
@@ -15803,12 +16859,13 @@ import { existsSync as existsSync22, mkdirSync as mkdirSync6, readFileSync as re
|
|
|
15803
16859
|
import { dirname as dirname15 } from "path";
|
|
15804
16860
|
import { getSharedAccountHealth as getSharedAccountHealth2 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
15805
16861
|
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling3 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
15806
|
-
import { fetchUpstream as
|
|
16862
|
+
import { fetchUpstream as fetchUpstream10 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
15807
16863
|
import { getSharedIdentityStore as getSharedIdentityStore2 } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
|
|
15808
16864
|
import {
|
|
15809
16865
|
claudeOAuth as claudeOAuth2,
|
|
15810
16866
|
codexOAuth as codexOAuth2,
|
|
15811
16867
|
geminiOAuth as geminiOAuth2,
|
|
16868
|
+
grokOAuth as grokOAuth2,
|
|
15812
16869
|
kimiOAuth as kimiOAuth2
|
|
15813
16870
|
} from "@omnicross/subscriptions";
|
|
15814
16871
|
|
|
@@ -15958,7 +17015,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15958
17015
|
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
15959
17016
|
*/
|
|
15960
17017
|
buildRefreshFetch(providerId, accountId) {
|
|
15961
|
-
return this.fetchImpl ?? ((url, init) =>
|
|
17018
|
+
return this.fetchImpl ?? ((url, init) => fetchUpstream10(url, init, { providerId, accountId, redactBodies: true }));
|
|
15962
17019
|
}
|
|
15963
17020
|
/**
|
|
15964
17021
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -15999,7 +17056,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15999
17056
|
* other hot reads. Never returns token material.
|
|
16000
17057
|
*/
|
|
16001
17058
|
getAccountProxy(providerId, accountId) {
|
|
16002
|
-
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi") {
|
|
17059
|
+
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi" && providerId !== "grok" && providerId !== "copilot") {
|
|
16003
17060
|
return void 0;
|
|
16004
17061
|
}
|
|
16005
17062
|
return getAccountProxy(this.readConfig(), providerId, accountId);
|
|
@@ -16018,7 +17075,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16018
17075
|
const fingerprintOn = identityStore.isEnabled();
|
|
16019
17076
|
const now = Date.now();
|
|
16020
17077
|
const out = {};
|
|
16021
|
-
for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi"]) {
|
|
17078
|
+
for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi", "grok", "copilot"]) {
|
|
16022
17079
|
const sanitized = sanitizeAccounts(config, provider);
|
|
16023
17080
|
if (sanitized.length === 0) continue;
|
|
16024
17081
|
for (const account of sanitized) {
|
|
@@ -16217,6 +17274,66 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16217
17274
|
}
|
|
16218
17275
|
});
|
|
16219
17276
|
}
|
|
17277
|
+
/**
|
|
17278
|
+
* Refresh the Grok (xAI SuperGrok) OAuth access token. The token endpoint is
|
|
17279
|
+
* resolved through OIDC discovery on every refresh (process-cached 1h by the
|
|
17280
|
+
* flow module) so a rotated endpoint document is picked up without a daemon
|
|
17281
|
+
* restart. HONEST `false` when no refresh_token.
|
|
17282
|
+
*/
|
|
17283
|
+
async refreshGrokToken() {
|
|
17284
|
+
return this.coalesce("grok:active", async () => {
|
|
17285
|
+
const config = this.readConfig();
|
|
17286
|
+
const active = getActiveAccount(config, "grok");
|
|
17287
|
+
const grok = active?.tokens;
|
|
17288
|
+
if (!active || !grok?.refreshToken) return false;
|
|
17289
|
+
const capturedId = active.id;
|
|
17290
|
+
this.materializeMigration(config);
|
|
17291
|
+
const refreshFetch = this.buildRefreshFetch("grok", capturedId);
|
|
17292
|
+
try {
|
|
17293
|
+
const tokenEndpoint = await grokOAuth2.resolveGrokTokenEndpoint(refreshFetch);
|
|
17294
|
+
const result = await grokOAuth2.refreshGrokAccessToken(grok.refreshToken, tokenEndpoint, refreshFetch);
|
|
17295
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
17296
|
+
const next = {
|
|
17297
|
+
...grok,
|
|
17298
|
+
accessToken: result.accessToken,
|
|
17299
|
+
refreshToken: result.refreshToken,
|
|
17300
|
+
expiresAt,
|
|
17301
|
+
status: "authorized",
|
|
17302
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17303
|
+
errorMessage: void 0,
|
|
17304
|
+
syncWarning: void 0
|
|
17305
|
+
};
|
|
17306
|
+
this.writeBackById("grok", capturedId, next);
|
|
17307
|
+
return true;
|
|
17308
|
+
} catch (error) {
|
|
17309
|
+
this.markExpiredById("grok", capturedId, grok, error);
|
|
17310
|
+
return false;
|
|
17311
|
+
}
|
|
17312
|
+
});
|
|
17313
|
+
}
|
|
17314
|
+
/**
|
|
17315
|
+
* "Refresh" a GitHub Copilot token — there is nothing to refresh (ghu_
|
|
17316
|
+
* tokens are long-lived with no exchange endpoint). A call here means the
|
|
17317
|
+
* strategy saw a 401 (the token was revoked); mark the account `expired`
|
|
17318
|
+
* with a re-authenticate message and return `false` (the proxy then declines
|
|
17319
|
+
* the retry instead of looping on a dead token).
|
|
17320
|
+
*/
|
|
17321
|
+
async refreshCopilotToken() {
|
|
17322
|
+
return this.coalesce("copilot:active", async () => {
|
|
17323
|
+
const config = this.readConfig();
|
|
17324
|
+
const active = getActiveAccount(config, "copilot");
|
|
17325
|
+
const copilot = active?.tokens;
|
|
17326
|
+
if (!active || !copilot?.accessToken) return false;
|
|
17327
|
+
this.materializeMigration(config);
|
|
17328
|
+
this.markExpiredById(
|
|
17329
|
+
"copilot",
|
|
17330
|
+
active.id,
|
|
17331
|
+
copilot,
|
|
17332
|
+
new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account")
|
|
17333
|
+
);
|
|
17334
|
+
return false;
|
|
17335
|
+
});
|
|
17336
|
+
}
|
|
16220
17337
|
/**
|
|
16221
17338
|
* Refresh a SPECIFIC managed account by id (background scheduler sweep and
|
|
16222
17339
|
* account-pool resolution). It uses only that account's stored refresh
|
|
@@ -16269,7 +17386,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16269
17386
|
}
|
|
16270
17387
|
const oauth = account.tokens;
|
|
16271
17388
|
if (!oauth.accessToken) return null;
|
|
16272
|
-
if (providerId === "codex" || providerId === "gemini" || providerId === "kimi") {
|
|
17389
|
+
if (providerId === "codex" || providerId === "gemini" || providerId === "kimi" || providerId === "grok" || providerId === "copilot") {
|
|
16273
17390
|
const expiresAtMs = oauth.expiresAt ? Date.parse(oauth.expiresAt) : 0;
|
|
16274
17391
|
const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - ACCOUNT_REFRESH_LEAD_MS;
|
|
16275
17392
|
if (expiringSoon && oauth.refreshToken) {
|
|
@@ -16373,6 +17490,18 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16373
17490
|
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
16374
17491
|
};
|
|
16375
17492
|
}
|
|
17493
|
+
if (provider === "grok") {
|
|
17494
|
+
const tokenEndpoint = await grokOAuth2.resolveGrokTokenEndpoint(refreshFetch);
|
|
17495
|
+
const r2 = await grokOAuth2.refreshGrokAccessToken(refreshToken, tokenEndpoint, refreshFetch);
|
|
17496
|
+
return {
|
|
17497
|
+
accessToken: r2.accessToken,
|
|
17498
|
+
refreshToken: r2.refreshToken,
|
|
17499
|
+
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
17500
|
+
};
|
|
17501
|
+
}
|
|
17502
|
+
if (provider === "copilot") {
|
|
17503
|
+
throw new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account");
|
|
17504
|
+
}
|
|
16376
17505
|
const flow = provider === "claude" ? claudeOAuth2 : provider === "codex" ? codexOAuth2 : geminiOAuth2;
|
|
16377
17506
|
const r = await flow.refreshAccessToken(refreshToken, refreshFetch);
|
|
16378
17507
|
return {
|
|
@@ -16616,7 +17745,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16616
17745
|
};
|
|
16617
17746
|
|
|
16618
17747
|
// src/AccountHealthProbeScheduler.ts
|
|
16619
|
-
import { fetchUpstream as
|
|
17748
|
+
import { fetchUpstream as fetchUpstream11 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
16620
17749
|
|
|
16621
17750
|
// src/probe/CodexGenerationProbe.ts
|
|
16622
17751
|
import {
|
|
@@ -16762,7 +17891,16 @@ var PROVIDER_PROBE_PLANS = {
|
|
|
16762
17891
|
// Kimi's `GET /coding/v1/usages` is a verified FREE authed GET (the allowance
|
|
16763
17892
|
// collector uses it), but the probe path also needs the fingerprint headers —
|
|
16764
17893
|
// keep the probe local until the collector covers the health surface.
|
|
16765
|
-
kimi: { kind: "local" }
|
|
17894
|
+
kimi: { kind: "local" },
|
|
17895
|
+
// Grok's billing proxy is a verified FREE authed GET (the allowance collector
|
|
17896
|
+
// uses it) but it REJECTS non-OAuth credentials and sits on a separate host
|
|
17897
|
+
// with its own product-gate header — keep the probe local, the collector
|
|
17898
|
+
// owns the health surface.
|
|
17899
|
+
grok: { kind: "local" },
|
|
17900
|
+
// The Copilot quota endpoint (copilot_internal/user) is a verified FREE
|
|
17901
|
+
// authed GET but lives on api.github.com with its own auth dialect and a
|
|
17902
|
+
// monthly-only window — the allowance collector owns the health surface.
|
|
17903
|
+
copilot: { kind: "local" }
|
|
16766
17904
|
};
|
|
16767
17905
|
function probePlanFor(providerId) {
|
|
16768
17906
|
return PROVIDER_PROBE_PLANS[providerId] ?? { kind: "local" };
|
|
@@ -16784,7 +17922,7 @@ var AccountHealthProbeScheduler = class {
|
|
|
16784
17922
|
this.logger = logger;
|
|
16785
17923
|
this.config = config;
|
|
16786
17924
|
this.now = opts.now ?? Date.now;
|
|
16787
|
-
this.fetchImpl = opts.fetchImpl ??
|
|
17925
|
+
this.fetchImpl = opts.fetchImpl ?? fetchUpstream11;
|
|
16788
17926
|
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
16789
17927
|
this.planFor = opts.planFor ?? probePlanFor;
|
|
16790
17928
|
}
|
|
@@ -17692,7 +18830,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
17692
18830
|
}
|
|
17693
18831
|
|
|
17694
18832
|
// src/audit/AuditPruneSweeper.ts
|
|
17695
|
-
var
|
|
18833
|
+
var DAY_MS4 = 24 * 60 * 6e4;
|
|
17696
18834
|
var SWEEP_INTERVAL_MS2 = 60 * 6e4;
|
|
17697
18835
|
var ARCHIVE_BATCH = 64;
|
|
17698
18836
|
var AuditPruneSweeper = class {
|
|
@@ -17756,7 +18894,7 @@ var AuditPruneSweeper = class {
|
|
|
17756
18894
|
this.sweeping = true;
|
|
17757
18895
|
try {
|
|
17758
18896
|
if (!existsSync25(this.auditDir)) return 0;
|
|
17759
|
-
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) *
|
|
18897
|
+
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS4;
|
|
17760
18898
|
let removed = 0;
|
|
17761
18899
|
for (const name of readdirSync6(this.auditDir)) {
|
|
17762
18900
|
const dateMs = auditFileDateMs(name);
|
|
@@ -18013,7 +19151,7 @@ async function closeAll(writers) {
|
|
|
18013
19151
|
// src/usage/UsagePruneSweeper.ts
|
|
18014
19152
|
import { unlink as unlink3 } from "fs/promises";
|
|
18015
19153
|
import { join as join22 } from "path";
|
|
18016
|
-
var
|
|
19154
|
+
var DAY_MS5 = 24 * 60 * 6e4;
|
|
18017
19155
|
var SWEEP_INTERVAL_MS3 = 60 * 6e4;
|
|
18018
19156
|
var DEFAULT_USAGE_RETENTION_DAYS = 90;
|
|
18019
19157
|
var UsagePruneSweeper = class {
|
|
@@ -18070,7 +19208,7 @@ var UsagePruneSweeper = class {
|
|
|
18070
19208
|
this.sweeping = true;
|
|
18071
19209
|
try {
|
|
18072
19210
|
const retentionDays = this.config.retentionDays ?? DEFAULT_USAGE_RETENTION_DAYS;
|
|
18073
|
-
const cutoff = this.todayMidnight() - (retentionDays - 1) *
|
|
19211
|
+
const cutoff = this.todayMidnight() - (retentionDays - 1) * DAY_MS5;
|
|
18074
19212
|
let removed = 0;
|
|
18075
19213
|
for (const entry of await listUsageDays(this.usageDir)) {
|
|
18076
19214
|
if (!entry.hasShard) continue;
|
|
@@ -18489,7 +19627,7 @@ var AuditWriter = class {
|
|
|
18489
19627
|
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync8 } from "fs";
|
|
18490
19628
|
import { createHmac as createHmac5 } from "crypto";
|
|
18491
19629
|
import { join as join26 } from "path";
|
|
18492
|
-
import { fetchUpstream as
|
|
19630
|
+
import { fetchUpstream as fetchUpstream12 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
18493
19631
|
|
|
18494
19632
|
// src/billing/billingFiles.ts
|
|
18495
19633
|
var BILLING_FILE_RE = /^billing-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
|
|
@@ -18512,7 +19650,7 @@ var BillingPublisher = class {
|
|
|
18512
19650
|
constructor(billingDir, logger, opts = {}) {
|
|
18513
19651
|
this.billingDir = billingDir;
|
|
18514
19652
|
this.logger = logger;
|
|
18515
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) =>
|
|
19653
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream12(url, init));
|
|
18516
19654
|
this.defer = opts.defer ?? ((fn) => setTimeout(fn, 0));
|
|
18517
19655
|
this.timeoutMs = opts.timeoutMs ?? BILLING_POST_TIMEOUT_MS;
|
|
18518
19656
|
this.now = opts.now ?? Date.now;
|
|
@@ -18762,7 +19900,7 @@ var BillingRetrySweeper = class {
|
|
|
18762
19900
|
// src/TokenRefreshScheduler.ts
|
|
18763
19901
|
var REFRESH_LEAD_MS2 = 5 * 6e4;
|
|
18764
19902
|
var SWEEP_INTERVAL_MS5 = 6e4;
|
|
18765
|
-
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
|
|
19903
|
+
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
|
|
18766
19904
|
var TokenRefreshScheduler = class {
|
|
18767
19905
|
constructor(store, logger, intervalMs = SWEEP_INTERVAL_MS5, leadMs = REFRESH_LEAD_MS2) {
|
|
18768
19906
|
this.store = store;
|
|
@@ -18847,6 +19985,12 @@ var TokenRefreshScheduler = class {
|
|
|
18847
19985
|
return this.store.refreshGeminiToken();
|
|
18848
19986
|
case "kimi":
|
|
18849
19987
|
return this.store.refreshKimiToken();
|
|
19988
|
+
case "grok":
|
|
19989
|
+
return this.store.refreshGrokToken();
|
|
19990
|
+
// ghu_ tokens never near-expire (far-future expiresAt), so the sweep
|
|
19991
|
+
// never reaches this — the branch exists for union totality.
|
|
19992
|
+
case "copilot":
|
|
19993
|
+
return this.store.refreshCopilotToken();
|
|
18850
19994
|
}
|
|
18851
19995
|
}
|
|
18852
19996
|
};
|
|
@@ -18925,7 +20069,7 @@ function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
|
18925
20069
|
|
|
18926
20070
|
// src/webhook/WebhookDispatcher.ts
|
|
18927
20071
|
import { createHmac as createHmac6 } from "crypto";
|
|
18928
|
-
import { fetchUpstream as
|
|
20072
|
+
import { fetchUpstream as fetchUpstream13 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
18929
20073
|
var WEBHOOK_MAX_ATTEMPTS = 3;
|
|
18930
20074
|
var WEBHOOK_QUEUE_MAX = 1e3;
|
|
18931
20075
|
var WEBHOOK_SEND_TIMEOUT_MS = 1e4;
|
|
@@ -18945,7 +20089,7 @@ var WebhookDispatcher = class {
|
|
|
18945
20089
|
sleep;
|
|
18946
20090
|
now;
|
|
18947
20091
|
constructor(opts = {}) {
|
|
18948
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) =>
|
|
20092
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream13(url, init));
|
|
18949
20093
|
this.logger = opts.logger;
|
|
18950
20094
|
this.maxAttempts = opts.maxAttempts ?? WEBHOOK_MAX_ATTEMPTS;
|
|
18951
20095
|
this.queueMax = opts.queueMax ?? WEBHOOK_QUEUE_MAX;
|
|
@@ -19031,8 +20175,8 @@ var WebhookDispatcher = class {
|
|
|
19031
20175
|
signal: AbortSignal.timeout(this.timeoutMs)
|
|
19032
20176
|
});
|
|
19033
20177
|
return res.ok ? { ok: true, status: res.status } : { ok: false, status: res.status };
|
|
19034
|
-
} catch (
|
|
19035
|
-
return { ok: false, error:
|
|
20178
|
+
} catch (err8) {
|
|
20179
|
+
return { ok: false, error: err8 instanceof Error ? err8.message : String(err8) };
|
|
19036
20180
|
}
|
|
19037
20181
|
}
|
|
19038
20182
|
/**
|
|
@@ -19174,7 +20318,7 @@ function buildDaemon(config, paths) {
|
|
|
19174
20318
|
setSecretBox(secretBox3);
|
|
19175
20319
|
setSecretBox2(secretBox3);
|
|
19176
20320
|
const decryptedConfig = decryptConfigSecrets(config, secretBox3);
|
|
19177
|
-
const accountAllowanceStore = new
|
|
20321
|
+
const accountAllowanceStore = new AccountAllowanceStore9(
|
|
19178
20322
|
Date.now,
|
|
19179
20323
|
void 0,
|
|
19180
20324
|
new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
|
|
@@ -19217,7 +20361,7 @@ function buildDaemon(config, paths) {
|
|
|
19217
20361
|
getAccountProxy: (providerId, accountId) => credentialStore.getAccountProxy(providerId, accountId)
|
|
19218
20362
|
})
|
|
19219
20363
|
);
|
|
19220
|
-
setGeminiCodeAssistResolver(
|
|
20364
|
+
setGeminiCodeAssistResolver(getGeminiCodeAssistProjectResolver2());
|
|
19221
20365
|
const autoDisableStore = new AutoDisableStore();
|
|
19222
20366
|
const providerKeyQuotaService = new ProviderKeyQuotaService(secretBox3);
|
|
19223
20367
|
const apiKeyPool = new ApiKeyPoolService(
|
|
@@ -19236,7 +20380,7 @@ function buildDaemon(config, paths) {
|
|
|
19236
20380
|
const pricingEngine = new PricingEngine(pricingStore, logger, {
|
|
19237
20381
|
// Catalog egress follows the same global/env proxy policy as every other
|
|
19238
20382
|
// daemon upstream call; no provider/account override applies here.
|
|
19239
|
-
fetchImpl: ((input, init) =>
|
|
20383
|
+
fetchImpl: ((input, init) => fetchUpstream14(String(input), init ?? {}))
|
|
19240
20384
|
});
|
|
19241
20385
|
const pricingRefreshScheduler = new PricingRefreshScheduler(
|
|
19242
20386
|
pricingEngine,
|
|
@@ -19521,7 +20665,7 @@ function buildDaemon(config, paths) {
|
|
|
19521
20665
|
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
19522
20666
|
// excluded from the upstream trace, so a failing login left no evidence.
|
|
19523
20667
|
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
19524
|
-
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) =>
|
|
20668
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => fetchUpstream14(url, init, { providerId, redactBodies: true }),
|
|
19525
20669
|
subscriptionAccountAppender: credentialStore,
|
|
19526
20670
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
19527
20671
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -19533,6 +20677,9 @@ function buildDaemon(config, paths) {
|
|
|
19533
20677
|
// paste; the app shows the verification URL + user code and polls the
|
|
19534
20678
|
// token-free status). Token captured + persisted daemon-side.
|
|
19535
20679
|
kimiSessions: new CodexOAuthSessionStore(),
|
|
20680
|
+
// Grok interactive OAuth — the same async DEVICE-CODE shape as kimi.
|
|
20681
|
+
grokSessions: new CodexOAuthSessionStore(),
|
|
20682
|
+
copilotSessions: new CodexOAuthSessionStore(),
|
|
19536
20683
|
// Migration pack (app-parity child 6, design D2/D3) — the concrete credential
|
|
19537
20684
|
// store provides BOTH the full DECRYPTED read (`getFullConfig`, export) and
|
|
19538
20685
|
// the multi-account append (`appendProviderAccount`, import re-encrypts at-
|
|
@@ -19591,7 +20738,7 @@ function buildDaemon(config, paths) {
|
|
|
19591
20738
|
});
|
|
19592
20739
|
const webhookDispatcher = new WebhookDispatcher({
|
|
19593
20740
|
logger,
|
|
19594
|
-
fetchImpl: (url, init) =>
|
|
20741
|
+
fetchImpl: (url, init) => fetchUpstream14(url, init)
|
|
19595
20742
|
});
|
|
19596
20743
|
setWebhookRuntime(webhookDispatcher, getSharedAccountHealth4());
|
|
19597
20744
|
const auditWriter = new AuditWriter(auditDir, logger);
|