@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/cli.js
CHANGED
|
@@ -1156,7 +1156,7 @@ import { dirname as dirname17 } from "path";
|
|
|
1156
1156
|
import { DEFAULT_AUDIT_CONFIG } from "@omnicross/contracts/audit-types";
|
|
1157
1157
|
import { DEFAULT_BILLING_CONFIG } from "@omnicross/contracts/billing-types";
|
|
1158
1158
|
import { OpenAIOperationRegistry } from "@omnicross/core";
|
|
1159
|
-
import { getGeminiCodeAssistProjectResolver } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
|
|
1159
|
+
import { getGeminiCodeAssistProjectResolver as getGeminiCodeAssistProjectResolver2 } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
|
|
1160
1160
|
import { ApiKeyPoolService } from "@omnicross/core/completion/ApiKeyPoolService";
|
|
1161
1161
|
import {
|
|
1162
1162
|
__resetOutboundApiServerForTests,
|
|
@@ -1170,14 +1170,14 @@ import { setSubscriptionRegistryForOutbound } from "@omnicross/core/outbound-api
|
|
|
1170
1170
|
import { getSharedAccountHealth as getSharedAccountHealth4 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
1171
1171
|
import {
|
|
1172
1172
|
__resetSharedAccountAllowanceStoreForTests,
|
|
1173
|
-
AccountAllowanceStore as
|
|
1173
|
+
AccountAllowanceStore as AccountAllowanceStore9,
|
|
1174
1174
|
setSharedAccountAllowanceStore
|
|
1175
1175
|
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
1176
1176
|
import {
|
|
1177
1177
|
__resetSharedAccountAllowanceSchedulingForTests,
|
|
1178
1178
|
getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling5
|
|
1179
1179
|
} from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
1180
|
-
import { fetchUpstream as
|
|
1180
|
+
import { fetchUpstream as fetchUpstream14, setUpstreamProxyResolver } from "@omnicross/core/pipeline/upstreamFetch";
|
|
1181
1181
|
import { __resetSharedIdentityStoreForTests } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
|
|
1182
1182
|
import { setGeminiCodeAssistResolver } from "@omnicross/core/ports/gemini-code-assist-resolver";
|
|
1183
1183
|
import {
|
|
@@ -1382,9 +1382,188 @@ function handleKimiOAuthStatus(sessionId, deps) {
|
|
|
1382
1382
|
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
1383
1383
|
}
|
|
1384
1384
|
|
|
1385
|
+
// src/admin/accountsGrokOAuth.ts
|
|
1386
|
+
import { grokOAuth } from "@omnicross/subscriptions";
|
|
1387
|
+
function err3(status, message) {
|
|
1388
|
+
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
1389
|
+
}
|
|
1390
|
+
var DEFAULT_GROK_OAUTH_TTL_MS = 15 * 6e4;
|
|
1391
|
+
async function handleGrokOAuthStart(deps) {
|
|
1392
|
+
if (deps.grokSessions.isBusy()) {
|
|
1393
|
+
return err3(409, "a grok sign-in is already in progress \u2014 finish it in the browser or cancel it");
|
|
1394
|
+
}
|
|
1395
|
+
const fetchImpl = deps.oauthExchangeFetch("grok");
|
|
1396
|
+
let tokenEndpoint;
|
|
1397
|
+
try {
|
|
1398
|
+
tokenEndpoint = await grokOAuth.resolveGrokTokenEndpoint(fetchImpl);
|
|
1399
|
+
} catch (e) {
|
|
1400
|
+
const reason = e instanceof Error ? e.message : "OIDC discovery failed";
|
|
1401
|
+
return err3(502, `grok token-endpoint discovery failed: ${reason}`);
|
|
1402
|
+
}
|
|
1403
|
+
let authorization;
|
|
1404
|
+
try {
|
|
1405
|
+
authorization = await grokOAuth.requestGrokDeviceAuthorization(fetchImpl);
|
|
1406
|
+
} catch (e) {
|
|
1407
|
+
const reason = e instanceof Error ? e.message : "device authorization failed";
|
|
1408
|
+
return err3(502, `grok device authorization failed: ${reason}`);
|
|
1409
|
+
}
|
|
1410
|
+
const { sessionId, signal } = deps.grokSessions.begin();
|
|
1411
|
+
void runGrokDevicePoll(sessionId, tokenEndpoint, authorization.deviceCode, signal, deps).catch((e) => {
|
|
1412
|
+
const reason = e instanceof Error ? e.message : "grok sign-in failed";
|
|
1413
|
+
deps.grokSessions.settle(sessionId, "error", reason);
|
|
1414
|
+
});
|
|
1415
|
+
return {
|
|
1416
|
+
status: 200,
|
|
1417
|
+
body: {
|
|
1418
|
+
authUrl: authorization.verificationUriComplete ?? authorization.verificationUri,
|
|
1419
|
+
userCode: authorization.userCode,
|
|
1420
|
+
sessionId
|
|
1421
|
+
}
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
async function runGrokDevicePoll(sessionId, tokenEndpoint, deviceCode, signal, deps) {
|
|
1425
|
+
const fetchImpl = deps.oauthExchangeFetch("grok");
|
|
1426
|
+
const result = await grokOAuth.awaitGrokDeviceToken(
|
|
1427
|
+
{ userCode: "", deviceCode, verificationUri: "" },
|
|
1428
|
+
tokenEndpoint,
|
|
1429
|
+
fetchImpl,
|
|
1430
|
+
{
|
|
1431
|
+
deadlineMs: DEFAULT_GROK_OAUTH_TTL_MS,
|
|
1432
|
+
sleep: (ms) => new Promise((resolve11, reject) => {
|
|
1433
|
+
const onAbort = () => {
|
|
1434
|
+
clearTimeout(timer);
|
|
1435
|
+
reject(new Error("login: cancelled"));
|
|
1436
|
+
};
|
|
1437
|
+
const timer = setTimeout(() => {
|
|
1438
|
+
signal.removeEventListener("abort", onAbort);
|
|
1439
|
+
resolve11();
|
|
1440
|
+
}, ms);
|
|
1441
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1442
|
+
})
|
|
1443
|
+
}
|
|
1444
|
+
);
|
|
1445
|
+
const block = {
|
|
1446
|
+
authMethod: "oauth",
|
|
1447
|
+
status: "authorized",
|
|
1448
|
+
accessToken: result.accessToken,
|
|
1449
|
+
refreshToken: result.refreshToken,
|
|
1450
|
+
expiresAt: new Date(Date.now() + result.expiresIn * 1e3).toISOString(),
|
|
1451
|
+
accountId: grokOAuth.grokAccountIdFromAccessToken(result.accessToken),
|
|
1452
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1453
|
+
};
|
|
1454
|
+
await deps.subscriptionAccountAppender.appendProviderAccount("grok", block);
|
|
1455
|
+
deps.grokSessions.settle(sessionId, "done");
|
|
1456
|
+
}
|
|
1457
|
+
function handleGrokOAuthCancel(sessionId, deps) {
|
|
1458
|
+
if (!deps.grokSessions.cancel(sessionId)) return err3(404, "unknown or expired grok sign-in session");
|
|
1459
|
+
return { status: 200, body: { ok: true } };
|
|
1460
|
+
}
|
|
1461
|
+
function handleGrokOAuthStatus(sessionId, deps) {
|
|
1462
|
+
const s = deps.grokSessions.get(sessionId);
|
|
1463
|
+
if (!s) return err3(404, "unknown or expired grok sign-in session");
|
|
1464
|
+
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
// src/admin/accountsCopilotOAuth.ts
|
|
1468
|
+
import { copilotOAuth } from "@omnicross/subscriptions";
|
|
1469
|
+
function err4(status, message) {
|
|
1470
|
+
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
1471
|
+
}
|
|
1472
|
+
var DEFAULT_COPILOT_OAUTH_TTL_MS = 15 * 6e4;
|
|
1473
|
+
async function handleCopilotOAuthStart(deps, enterpriseUrlInput) {
|
|
1474
|
+
if (deps.copilotSessions.isBusy()) {
|
|
1475
|
+
return err4(409, "a copilot sign-in is already in progress \u2014 finish it in the browser or cancel it");
|
|
1476
|
+
}
|
|
1477
|
+
let enterpriseUrl;
|
|
1478
|
+
if (typeof enterpriseUrlInput === "string" && enterpriseUrlInput.trim()) {
|
|
1479
|
+
try {
|
|
1480
|
+
enterpriseUrl = copilotOAuth.normalizeCopilotEnterpriseDomain(enterpriseUrlInput);
|
|
1481
|
+
} catch (e) {
|
|
1482
|
+
const reason = e instanceof Error ? e.message : "invalid GitHub Enterprise domain";
|
|
1483
|
+
return err4(400, `copilot ${reason}`);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
const fetchImpl = deps.oauthExchangeFetch("copilot");
|
|
1487
|
+
let authorization;
|
|
1488
|
+
try {
|
|
1489
|
+
authorization = await copilotOAuth.requestCopilotDeviceAuthorization(fetchImpl, enterpriseUrl);
|
|
1490
|
+
} catch (e) {
|
|
1491
|
+
const reason = e instanceof Error ? e.message : "device authorization failed";
|
|
1492
|
+
return err4(502, `copilot device authorization failed: ${reason}`);
|
|
1493
|
+
}
|
|
1494
|
+
const { sessionId, signal } = deps.copilotSessions.begin();
|
|
1495
|
+
void runCopilotDevicePoll(sessionId, authorization.deviceCode, signal, deps, enterpriseUrl).catch((e) => {
|
|
1496
|
+
const reason = e instanceof Error ? e.message : "copilot sign-in failed";
|
|
1497
|
+
deps.copilotSessions.settle(sessionId, "error", reason);
|
|
1498
|
+
});
|
|
1499
|
+
return {
|
|
1500
|
+
status: 200,
|
|
1501
|
+
body: {
|
|
1502
|
+
authUrl: authorization.verificationUri,
|
|
1503
|
+
userCode: authorization.userCode,
|
|
1504
|
+
sessionId,
|
|
1505
|
+
...enterpriseUrl ? { enterpriseUrl } : {}
|
|
1506
|
+
}
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
async function runCopilotDevicePoll(sessionId, deviceCode, signal, deps, enterpriseUrl) {
|
|
1510
|
+
const fetchImpl = deps.oauthExchangeFetch("copilot");
|
|
1511
|
+
const result = await copilotOAuth.awaitCopilotDeviceToken(
|
|
1512
|
+
{ userCode: "", deviceCode, verificationUri: "", interval: 5, expiresIn: 900 },
|
|
1513
|
+
fetchImpl,
|
|
1514
|
+
{
|
|
1515
|
+
deadlineMs: DEFAULT_COPILOT_OAUTH_TTL_MS,
|
|
1516
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
1517
|
+
sleep: (ms) => new Promise((resolve11, reject) => {
|
|
1518
|
+
const onAbort = () => {
|
|
1519
|
+
clearTimeout(timer);
|
|
1520
|
+
reject(new Error("login: cancelled"));
|
|
1521
|
+
};
|
|
1522
|
+
const timer = setTimeout(() => {
|
|
1523
|
+
signal.removeEventListener("abort", onAbort);
|
|
1524
|
+
resolve11();
|
|
1525
|
+
}, ms);
|
|
1526
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1527
|
+
})
|
|
1528
|
+
}
|
|
1529
|
+
);
|
|
1530
|
+
const identity = await copilotOAuth.fetchCopilotIdentity(result.accessToken, fetchImpl, enterpriseUrl);
|
|
1531
|
+
const apiEndpoint = await copilotOAuth.discoverCopilotApiEndpoint(result.accessToken, fetchImpl, enterpriseUrl);
|
|
1532
|
+
await copilotOAuth.enableAllCopilotModels(
|
|
1533
|
+
result.accessToken,
|
|
1534
|
+
{ apiEndpoint, ...enterpriseUrl ? { enterpriseUrl } : {} },
|
|
1535
|
+
fetchImpl
|
|
1536
|
+
);
|
|
1537
|
+
const block = {
|
|
1538
|
+
authMethod: "oauth",
|
|
1539
|
+
status: "authorized",
|
|
1540
|
+
accessToken: result.accessToken,
|
|
1541
|
+
refreshToken: result.accessToken,
|
|
1542
|
+
expiresAt: new Date(Date.now() + copilotOAuth.COPILOT_FAR_FUTURE_MS).toISOString(),
|
|
1543
|
+
...identity.accountId ? { accountId: identity.accountId } : {},
|
|
1544
|
+
...identity.email ? { email: identity.email } : {},
|
|
1545
|
+
...apiEndpoint ? { apiEndpoint } : {},
|
|
1546
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
1547
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1548
|
+
};
|
|
1549
|
+
await deps.subscriptionAccountAppender.appendProviderAccount("copilot", block);
|
|
1550
|
+
deps.copilotSessions.settle(sessionId, "done");
|
|
1551
|
+
}
|
|
1552
|
+
function handleCopilotOAuthCancel(sessionId, deps) {
|
|
1553
|
+
if (!deps.copilotSessions.cancel(sessionId)) {
|
|
1554
|
+
return err4(404, "unknown or expired copilot sign-in session");
|
|
1555
|
+
}
|
|
1556
|
+
return { status: 200, body: { ok: true } };
|
|
1557
|
+
}
|
|
1558
|
+
function handleCopilotOAuthStatus(sessionId, deps) {
|
|
1559
|
+
const s = deps.copilotSessions.get(sessionId);
|
|
1560
|
+
if (!s) return err4(404, "unknown or expired copilot sign-in session");
|
|
1561
|
+
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1385
1564
|
// src/allowance/AccountAllowanceService.ts
|
|
1386
1565
|
import {
|
|
1387
|
-
getSharedAccountAllowanceStore as
|
|
1566
|
+
getSharedAccountAllowanceStore as getSharedAccountAllowanceStore8
|
|
1388
1567
|
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
1389
1568
|
import {
|
|
1390
1569
|
getSharedAccountAllowanceScheduling
|
|
@@ -1987,13 +2166,676 @@ var KimiAllowanceCollector = class {
|
|
|
1987
2166
|
now;
|
|
1988
2167
|
inFlight = /* @__PURE__ */ new Map();
|
|
1989
2168
|
async collectMany(accounts, options = {}) {
|
|
1990
|
-
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
2169
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
2170
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
2171
|
+
}
|
|
2172
|
+
collect(account, options = {}) {
|
|
2173
|
+
const now = this.now();
|
|
2174
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
2175
|
+
const existing = this.store.get("kimi", account.id, now);
|
|
2176
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
2177
|
+
return Promise.resolve(existing);
|
|
2178
|
+
}
|
|
2179
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
2180
|
+
this.store.set(snapshot);
|
|
2181
|
+
return Promise.resolve(snapshot);
|
|
2182
|
+
}
|
|
2183
|
+
const cached = this.store.get("kimi", account.id, now);
|
|
2184
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
2185
|
+
return Promise.resolve(cached);
|
|
2186
|
+
}
|
|
2187
|
+
const running = this.inFlight.get(account.id);
|
|
2188
|
+
if (running) return running;
|
|
2189
|
+
const promise = this.fetchAccount(account.id, account.tokens).catch(() => this.failureSnapshot(account.id, "kimi_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
2190
|
+
this.inFlight.set(account.id, promise);
|
|
2191
|
+
return promise;
|
|
2192
|
+
}
|
|
2193
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
2194
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
2195
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
2196
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
2197
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
2198
|
+
}
|
|
2199
|
+
async fetchAccount(accountId, tokens) {
|
|
2200
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
|
|
2201
|
+
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
2202
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
2203
|
+
if (response.status === 401) {
|
|
2204
|
+
const refreshed = await this.credentials.refreshAccountToken("kimi", accountId);
|
|
2205
|
+
if (!refreshed) return this.failureSnapshot(accountId, "kimi_usage_unauthorized", this.now());
|
|
2206
|
+
accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
|
|
2207
|
+
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
2208
|
+
response = await this.request(accountId, accessToken, tokens);
|
|
2209
|
+
}
|
|
2210
|
+
if (response.status === 403) {
|
|
2211
|
+
const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "kimi_usage_unsupported");
|
|
2212
|
+
this.store.set(snapshot2);
|
|
2213
|
+
return snapshot2;
|
|
2214
|
+
}
|
|
2215
|
+
if (!response.ok) return this.failureSnapshot(accountId, "kimi_usage_http_error", this.now());
|
|
2216
|
+
let payload;
|
|
2217
|
+
try {
|
|
2218
|
+
payload = await response.json();
|
|
2219
|
+
} catch {
|
|
2220
|
+
return this.failureSnapshot(accountId, "kimi_usage_invalid_response", this.now());
|
|
2221
|
+
}
|
|
2222
|
+
const now = this.now();
|
|
2223
|
+
const windows = parseKimiUsagePayload(payload, now);
|
|
2224
|
+
const snapshot = {
|
|
2225
|
+
providerId: "kimi",
|
|
2226
|
+
accountId,
|
|
2227
|
+
source: "oauth-usage-api",
|
|
2228
|
+
observedAt: new Date(now).toISOString(),
|
|
2229
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2230
|
+
windows: windows.length > 0 ? windows : [
|
|
2231
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2232
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2233
|
+
],
|
|
2234
|
+
...windows.length > 0 ? {} : { lastErrorCode: "kimi_usage_invalid_response" }
|
|
2235
|
+
};
|
|
2236
|
+
this.store.set(snapshot);
|
|
2237
|
+
return snapshot;
|
|
2238
|
+
}
|
|
2239
|
+
request(accountId, accessToken, tokens) {
|
|
2240
|
+
return this.fetchImpl(KIMI_USAGE_URL, {
|
|
2241
|
+
method: "GET",
|
|
2242
|
+
headers: {
|
|
2243
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2244
|
+
Accept: "application/json",
|
|
2245
|
+
...kimiFingerprintHeaders(tokens.deviceId)
|
|
2246
|
+
},
|
|
2247
|
+
signal: AbortSignal.timeout(15e3)
|
|
2248
|
+
}, accountId);
|
|
2249
|
+
}
|
|
2250
|
+
failureSnapshot(accountId, code, now) {
|
|
2251
|
+
const existing = this.store.get("kimi", accountId, now);
|
|
2252
|
+
const snapshot = existing ? {
|
|
2253
|
+
...existing,
|
|
2254
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2255
|
+
windows: existing.windows.map((window) => ({
|
|
2256
|
+
...window,
|
|
2257
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2258
|
+
})),
|
|
2259
|
+
lastErrorCode: code
|
|
2260
|
+
} : {
|
|
2261
|
+
providerId: "kimi",
|
|
2262
|
+
accountId,
|
|
2263
|
+
source: "oauth-usage-api",
|
|
2264
|
+
observedAt: new Date(now).toISOString(),
|
|
2265
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2266
|
+
windows: [
|
|
2267
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2268
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2269
|
+
],
|
|
2270
|
+
lastErrorCode: code
|
|
2271
|
+
};
|
|
2272
|
+
this.store.set(snapshot);
|
|
2273
|
+
return snapshot;
|
|
2274
|
+
}
|
|
2275
|
+
unsupportedSnapshot(accountId, now, code = "kimi_usage_unsupported_auth") {
|
|
2276
|
+
return {
|
|
2277
|
+
providerId: "kimi",
|
|
2278
|
+
accountId,
|
|
2279
|
+
source: "oauth-usage-api",
|
|
2280
|
+
observedAt: new Date(now).toISOString(),
|
|
2281
|
+
windows: [
|
|
2282
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unsupported" },
|
|
2283
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2284
|
+
],
|
|
2285
|
+
lastErrorCode: code
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
};
|
|
2289
|
+
|
|
2290
|
+
// src/allowance/GrokAllowanceCollector.ts
|
|
2291
|
+
import {
|
|
2292
|
+
getSharedAccountAllowanceStore as getSharedAccountAllowanceStore4
|
|
2293
|
+
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
2294
|
+
import { fetchUpstream as fetchUpstream4 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
2295
|
+
var GROK_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2296
|
+
var GROK_BILLING_BASE = "https://cli-chat-proxy.grok.com";
|
|
2297
|
+
var GROK_BILLING_CREDITS_URL = `${GROK_BILLING_BASE}/v1/billing?format=credits`;
|
|
2298
|
+
var GROK_BILLING_MONTHLY_URL = `${GROK_BILLING_BASE}/v1/billing`;
|
|
2299
|
+
function isRecord2(value) {
|
|
2300
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2301
|
+
}
|
|
2302
|
+
function finiteNumber3(value) {
|
|
2303
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
2304
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
2305
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
2306
|
+
}
|
|
2307
|
+
function percent(value) {
|
|
2308
|
+
const parsed = finiteNumber3(value);
|
|
2309
|
+
return parsed !== void 0 && parsed <= 100 ? parsed : void 0;
|
|
2310
|
+
}
|
|
2311
|
+
function onDemandAmount(value) {
|
|
2312
|
+
return isRecord2(value) ? finiteNumber3(value["val"]) : void 0;
|
|
2313
|
+
}
|
|
2314
|
+
function confirmsNoMonthlyQuota(raw) {
|
|
2315
|
+
const limit = onDemandAmount(raw["monthlyLimit"]);
|
|
2316
|
+
if (limit !== void 0) return limit === 0;
|
|
2317
|
+
return parseWeeklyConfig(raw)?.inferredPercent === true;
|
|
2318
|
+
}
|
|
2319
|
+
function parseWeeklyConfig(raw) {
|
|
2320
|
+
const period = isRecord2(raw["currentPeriod"]) ? raw["currentPeriod"] : void 0;
|
|
2321
|
+
if (!period) return null;
|
|
2322
|
+
const start = typeof period["start"] === "string" ? Date.parse(period["start"]) : Number.NaN;
|
|
2323
|
+
const end = typeof period["end"] === "string" ? Date.parse(period["end"]) : Number.NaN;
|
|
2324
|
+
const type = typeof period["type"] === "string" ? period["type"] : "";
|
|
2325
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
|
|
2326
|
+
if (!type.toUpperCase().includes("WEEK")) return null;
|
|
2327
|
+
const inferred = raw["creditUsagePercent"] === void 0 || raw["creditUsagePercent"] === null;
|
|
2328
|
+
let creditUsagePercent;
|
|
2329
|
+
if (inferred) {
|
|
2330
|
+
creditUsagePercent = end > Date.now() ? 0 : void 0;
|
|
2331
|
+
} else {
|
|
2332
|
+
creditUsagePercent = percent(raw["creditUsagePercent"]);
|
|
2333
|
+
}
|
|
2334
|
+
if (creditUsagePercent === void 0) return null;
|
|
2335
|
+
return {
|
|
2336
|
+
creditUsagePercent,
|
|
2337
|
+
inferredPercent: inferred,
|
|
2338
|
+
resetsAtMs: end,
|
|
2339
|
+
unified: raw["isUnifiedBillingUser"] === true
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
function parseMonthlyConfig(raw) {
|
|
2343
|
+
const start = typeof raw["billingPeriodStart"] === "string" ? Date.parse(raw["billingPeriodStart"]) : Number.NaN;
|
|
2344
|
+
const end = typeof raw["billingPeriodEnd"] === "string" ? Date.parse(raw["billingPeriodEnd"]) : Number.NaN;
|
|
2345
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
|
|
2346
|
+
const limit = onDemandAmount(raw["monthlyLimit"]);
|
|
2347
|
+
const used = onDemandAmount(raw["used"]);
|
|
2348
|
+
if (limit === void 0 || limit <= 0 || used === void 0) return null;
|
|
2349
|
+
return { used, limit, periodStartMs: start, periodEndMs: end };
|
|
2350
|
+
}
|
|
2351
|
+
function secondsUntil4(instant, now) {
|
|
2352
|
+
if (!instant) return void 0;
|
|
2353
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2354
|
+
}
|
|
2355
|
+
var MINUTE_MS2 = 6e4;
|
|
2356
|
+
var DAY_MS2 = 864e5;
|
|
2357
|
+
var WEEK_MINUTES = 7 * 24 * 60;
|
|
2358
|
+
function weeklyWindow(config, now) {
|
|
2359
|
+
const resetsAt = new Date(config.resetsAtMs).toISOString();
|
|
2360
|
+
return {
|
|
2361
|
+
id: "seven-day",
|
|
2362
|
+
label: "7 days",
|
|
2363
|
+
scope: "all",
|
|
2364
|
+
usedPercent: config.creditUsagePercent,
|
|
2365
|
+
windowMinutes: WEEK_MINUTES,
|
|
2366
|
+
resetsAt,
|
|
2367
|
+
remainingSeconds: secondsUntil4(resetsAt, now),
|
|
2368
|
+
state: "fresh"
|
|
2369
|
+
};
|
|
2370
|
+
}
|
|
2371
|
+
function monthlyWindow(config, now) {
|
|
2372
|
+
const resetsAt = new Date(config.periodEndMs).toISOString();
|
|
2373
|
+
const days = Math.max(1, Math.round((config.periodEndMs - config.periodStartMs) / DAY_MS2));
|
|
2374
|
+
return {
|
|
2375
|
+
id: "thirty-day",
|
|
2376
|
+
label: days === 30 || days === 31 ? "30 days" : `${days} days`,
|
|
2377
|
+
scope: "all",
|
|
2378
|
+
usedPercent: Math.round(Math.min(100, config.used / config.limit * 100) * 10) / 10,
|
|
2379
|
+
windowMinutes: Math.round((config.periodEndMs - config.periodStartMs) / MINUTE_MS2),
|
|
2380
|
+
resetsAt,
|
|
2381
|
+
remainingSeconds: secondsUntil4(resetsAt, now),
|
|
2382
|
+
state: "fresh"
|
|
2383
|
+
};
|
|
2384
|
+
}
|
|
2385
|
+
function onDemandWindow(raw) {
|
|
2386
|
+
const cap = onDemandAmount(raw["onDemandCap"]);
|
|
2387
|
+
const used = onDemandAmount(raw["onDemandUsed"]);
|
|
2388
|
+
if (cap === void 0 || cap <= 0 || used === void 0) return null;
|
|
2389
|
+
return {
|
|
2390
|
+
id: "on-demand",
|
|
2391
|
+
label: "On-demand",
|
|
2392
|
+
scope: "all",
|
|
2393
|
+
usedPercent: Math.round(Math.min(100, used / cap * 100) * 10) / 10,
|
|
2394
|
+
state: "fresh"
|
|
2395
|
+
};
|
|
2396
|
+
}
|
|
2397
|
+
async function probeBilling(url, accessToken, accountId, fetchImpl) {
|
|
2398
|
+
try {
|
|
2399
|
+
const response = await fetchImpl(url, {
|
|
2400
|
+
method: "GET",
|
|
2401
|
+
headers: {
|
|
2402
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2403
|
+
Accept: "application/json",
|
|
2404
|
+
"X-XAI-Token-Auth": "xai-grok-cli"
|
|
2405
|
+
},
|
|
2406
|
+
redirect: "error",
|
|
2407
|
+
signal: AbortSignal.timeout(15e3)
|
|
2408
|
+
}, accountId);
|
|
2409
|
+
if (!response.ok) return { status: response.status, payload: null };
|
|
2410
|
+
const payload = await response.json();
|
|
2411
|
+
return { status: response.status, payload: isRecord2(payload) ? payload : null };
|
|
2412
|
+
} catch {
|
|
2413
|
+
return { status: 0, payload: null };
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
function parseGrokBillingPayloads(creditsPayload, monthlyPayload, now) {
|
|
2417
|
+
const creditsConfig = isRecord2(creditsPayload?.["config"]) ? creditsPayload["config"] : null;
|
|
2418
|
+
const monthlyConfig = isRecord2(monthlyPayload?.["config"]) ? monthlyPayload["config"] : null;
|
|
2419
|
+
let weekly = creditsConfig ? parseWeeklyConfig(creditsConfig) : null;
|
|
2420
|
+
const unifiedFlag = creditsConfig?.["isUnifiedBillingUser"] === true;
|
|
2421
|
+
let monthly = monthlyConfig ? parseMonthlyConfig(monthlyConfig) : null;
|
|
2422
|
+
if (weekly?.inferredPercent && unifiedFlag) {
|
|
2423
|
+
if (monthly) {
|
|
2424
|
+
weekly = null;
|
|
2425
|
+
} else if (!monthlyConfig || !confirmsNoMonthlyQuota(monthlyConfig)) {
|
|
2426
|
+
weekly = null;
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
const windows = [];
|
|
2430
|
+
if (weekly) windows.push(weeklyWindow(weekly, now));
|
|
2431
|
+
if (monthly) windows.push(monthlyWindow(monthly, now));
|
|
2432
|
+
const onDemandSource = monthly && monthlyConfig ? monthlyConfig : creditsConfig;
|
|
2433
|
+
const onDemand = onDemandSource ? onDemandWindow(onDemandSource) : null;
|
|
2434
|
+
if (onDemand) windows.push(onDemand);
|
|
2435
|
+
return windows.length > 0 ? windows : null;
|
|
2436
|
+
}
|
|
2437
|
+
var GrokAllowanceCollector = class {
|
|
2438
|
+
constructor(credentials, store = getSharedAccountAllowanceStore4(), fetchImpl = (url, init, accountId) => fetchUpstream4(url, init, { providerId: "grok", accountId, redactBodies: true }), now = Date.now) {
|
|
2439
|
+
this.credentials = credentials;
|
|
2440
|
+
this.store = store;
|
|
2441
|
+
this.fetchImpl = fetchImpl;
|
|
2442
|
+
this.now = now;
|
|
2443
|
+
}
|
|
2444
|
+
credentials;
|
|
2445
|
+
store;
|
|
2446
|
+
fetchImpl;
|
|
2447
|
+
now;
|
|
2448
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
2449
|
+
async collectMany(accounts, options = {}) {
|
|
2450
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
2451
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
2452
|
+
}
|
|
2453
|
+
collect(account, options = {}) {
|
|
2454
|
+
const now = this.now();
|
|
2455
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
2456
|
+
const existing = this.store.get("grok", account.id, now);
|
|
2457
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
2458
|
+
return Promise.resolve(existing);
|
|
2459
|
+
}
|
|
2460
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
2461
|
+
this.store.set(snapshot);
|
|
2462
|
+
return Promise.resolve(snapshot);
|
|
2463
|
+
}
|
|
2464
|
+
const cached = this.store.get("grok", account.id, now);
|
|
2465
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
2466
|
+
return Promise.resolve(cached);
|
|
2467
|
+
}
|
|
2468
|
+
const running = this.inFlight.get(account.id);
|
|
2469
|
+
if (running) return running;
|
|
2470
|
+
const promise = this.fetchAccount(account.id).catch(() => this.failureSnapshot(account.id, "grok_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
2471
|
+
this.inFlight.set(account.id, promise);
|
|
2472
|
+
return promise;
|
|
2473
|
+
}
|
|
2474
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
2475
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
2476
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
2477
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
2478
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
2479
|
+
}
|
|
2480
|
+
async fetchAccount(accountId) {
|
|
2481
|
+
const probe = async () => {
|
|
2482
|
+
const accessToken = await this.credentials.getAccessTokenForAccount("grok", accountId);
|
|
2483
|
+
if (!accessToken) return { unauthorized: true, windows: null };
|
|
2484
|
+
const credits = await probeBilling(GROK_BILLING_CREDITS_URL, accessToken, accountId, this.fetchImpl);
|
|
2485
|
+
if (credits.status === 401 || credits.status === 403) return { unauthorized: true, windows: null };
|
|
2486
|
+
const creditsConfig = isRecord2(credits.payload?.["config"]) ? credits.payload["config"] : null;
|
|
2487
|
+
const weekly = creditsConfig ? parseWeeklyConfig(creditsConfig) : null;
|
|
2488
|
+
const monthly = !weekly || creditsConfig?.["isUnifiedBillingUser"] === true ? await probeBilling(GROK_BILLING_MONTHLY_URL, accessToken, accountId, this.fetchImpl) : { status: 200, payload: null };
|
|
2489
|
+
if (monthly.status === 401 || monthly.status === 403) return { unauthorized: true, windows: null };
|
|
2490
|
+
return {
|
|
2491
|
+
unauthorized: false,
|
|
2492
|
+
windows: parseGrokBillingPayloads(credits.payload, monthly.payload, this.now())
|
|
2493
|
+
};
|
|
2494
|
+
};
|
|
2495
|
+
let result = await probe();
|
|
2496
|
+
if (result.unauthorized) {
|
|
2497
|
+
const refreshed = await this.credentials.refreshAccountToken("grok", accountId);
|
|
2498
|
+
if (!refreshed) return this.failureSnapshot(accountId, "grok_usage_unauthorized", this.now());
|
|
2499
|
+
result = await probe();
|
|
2500
|
+
if (result.unauthorized) {
|
|
2501
|
+
return this.failureSnapshot(accountId, "grok_usage_unauthorized", this.now());
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
const now = this.now();
|
|
2505
|
+
if (result.windows && result.windows.length > 0) {
|
|
2506
|
+
const snapshot = {
|
|
2507
|
+
providerId: "grok",
|
|
2508
|
+
accountId,
|
|
2509
|
+
source: "oauth-usage-api",
|
|
2510
|
+
observedAt: new Date(now).toISOString(),
|
|
2511
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2512
|
+
windows: result.windows
|
|
2513
|
+
};
|
|
2514
|
+
this.store.set(snapshot);
|
|
2515
|
+
return snapshot;
|
|
2516
|
+
}
|
|
2517
|
+
return this.failureSnapshot(accountId, "grok_usage_invalid_response", now);
|
|
2518
|
+
}
|
|
2519
|
+
failureSnapshot(accountId, code, now) {
|
|
2520
|
+
const existing = this.store.get("grok", accountId, now);
|
|
2521
|
+
const snapshot = existing ? {
|
|
2522
|
+
...existing,
|
|
2523
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2524
|
+
windows: existing.windows.map((window) => ({
|
|
2525
|
+
...window,
|
|
2526
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2527
|
+
})),
|
|
2528
|
+
lastErrorCode: code
|
|
2529
|
+
} : {
|
|
2530
|
+
providerId: "grok",
|
|
2531
|
+
accountId,
|
|
2532
|
+
source: "oauth-usage-api",
|
|
2533
|
+
observedAt: new Date(now).toISOString(),
|
|
2534
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2535
|
+
windows: [
|
|
2536
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2537
|
+
{ id: "thirty-day", label: "30 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2538
|
+
],
|
|
2539
|
+
lastErrorCode: code
|
|
2540
|
+
};
|
|
2541
|
+
this.store.set(snapshot);
|
|
2542
|
+
return snapshot;
|
|
2543
|
+
}
|
|
2544
|
+
unsupportedSnapshot(accountId, now) {
|
|
2545
|
+
return {
|
|
2546
|
+
providerId: "grok",
|
|
2547
|
+
accountId,
|
|
2548
|
+
source: "oauth-usage-api",
|
|
2549
|
+
observedAt: new Date(now).toISOString(),
|
|
2550
|
+
windows: [
|
|
2551
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" },
|
|
2552
|
+
{ id: "thirty-day", label: "30 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2553
|
+
],
|
|
2554
|
+
lastErrorCode: "grok_usage_unsupported_auth"
|
|
2555
|
+
};
|
|
2556
|
+
}
|
|
2557
|
+
};
|
|
2558
|
+
|
|
2559
|
+
// src/allowance/CopilotAllowanceCollector.ts
|
|
2560
|
+
import {
|
|
2561
|
+
getSharedAccountAllowanceStore as getSharedAccountAllowanceStore5
|
|
2562
|
+
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
2563
|
+
import { fetchUpstream as fetchUpstream5 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
2564
|
+
import { COPILOT_GITHUB_HEADERS, copilotGitHubApiBase } from "@omnicross/subscriptions";
|
|
2565
|
+
var COPILOT_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2566
|
+
function isRecord3(value) {
|
|
2567
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2568
|
+
}
|
|
2569
|
+
function finiteNumber4(value) {
|
|
2570
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
2571
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
2572
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
2573
|
+
}
|
|
2574
|
+
function booleanValue(value) {
|
|
2575
|
+
if (typeof value === "boolean") return value;
|
|
2576
|
+
if (value === "true") return true;
|
|
2577
|
+
if (value === "false") return false;
|
|
2578
|
+
return void 0;
|
|
2579
|
+
}
|
|
2580
|
+
function parseQuotaDetail(value) {
|
|
2581
|
+
if (!isRecord3(value)) return null;
|
|
2582
|
+
const entitlement = finiteNumber4(value["entitlement"]);
|
|
2583
|
+
const remaining = finiteNumber4(value["remaining"]);
|
|
2584
|
+
const percentRemaining = finiteNumber4(value["percent_remaining"]);
|
|
2585
|
+
const unlimited = booleanValue(value["unlimited"]);
|
|
2586
|
+
if (entitlement === void 0 || remaining === void 0 || percentRemaining === void 0 || unlimited === void 0) {
|
|
2587
|
+
return null;
|
|
2588
|
+
}
|
|
2589
|
+
return { entitlement, remaining, percentRemaining, unlimited };
|
|
2590
|
+
}
|
|
2591
|
+
function secondsUntil5(instant, now) {
|
|
2592
|
+
if (!instant) return void 0;
|
|
2593
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2594
|
+
}
|
|
2595
|
+
function parseCopilotUserPayload(payload, now) {
|
|
2596
|
+
if (!isRecord3(payload)) return null;
|
|
2597
|
+
const snapshots = isRecord3(payload["quota_snapshots"]) ? payload["quota_snapshots"] : void 0;
|
|
2598
|
+
if (!snapshots) return null;
|
|
2599
|
+
const resetRaw = payload["quota_reset_date"];
|
|
2600
|
+
const resetsAt = typeof resetRaw === "string" && resetRaw.trim() && Number.isFinite(Date.parse(resetRaw)) ? new Date(Date.parse(resetRaw)).toISOString() : void 0;
|
|
2601
|
+
const windows = [];
|
|
2602
|
+
const premium = parseQuotaDetail(snapshots["premium_interactions"]);
|
|
2603
|
+
if (premium) {
|
|
2604
|
+
const usedPercent = premium.unlimited ? 0 : premium.entitlement > 0 ? Math.round(Math.min(100, (premium.entitlement - premium.remaining) / premium.entitlement * 100) * 10) / 10 : finiteNumber4(premium.percentRemaining) !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - premium.percentRemaining)) * 10) / 10 : null;
|
|
2605
|
+
if (usedPercent !== null) {
|
|
2606
|
+
windows.push({
|
|
2607
|
+
id: "thirty-day",
|
|
2608
|
+
label: "Monthly",
|
|
2609
|
+
scope: "all",
|
|
2610
|
+
usedPercent,
|
|
2611
|
+
windowMinutes: 30 * 24 * 60,
|
|
2612
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2613
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
2614
|
+
state: "fresh"
|
|
2615
|
+
});
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
const chat = parseQuotaDetail(snapshots["chat"]);
|
|
2619
|
+
if (chat && !chat.unlimited && chat.entitlement > 0) {
|
|
2620
|
+
const usedPercent = Math.round(Math.min(100, (chat.entitlement - chat.remaining) / chat.entitlement * 100) * 10) / 10;
|
|
2621
|
+
windows.push({
|
|
2622
|
+
id: "chat-monthly",
|
|
2623
|
+
label: "Chat (monthly)",
|
|
2624
|
+
scope: "all",
|
|
2625
|
+
usedPercent,
|
|
2626
|
+
windowMinutes: 30 * 24 * 60,
|
|
2627
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2628
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
2629
|
+
state: "fresh"
|
|
2630
|
+
});
|
|
2631
|
+
}
|
|
2632
|
+
return windows.length > 0 ? windows : null;
|
|
2633
|
+
}
|
|
2634
|
+
function githubApiBase(tokens) {
|
|
2635
|
+
return copilotGitHubApiBase(tokens.enterpriseUrl);
|
|
2636
|
+
}
|
|
2637
|
+
var CopilotAllowanceCollector = class {
|
|
2638
|
+
constructor(credentials, store = getSharedAccountAllowanceStore5(), fetchImpl = (url, init, accountId) => fetchUpstream5(url, init, { providerId: "copilot", accountId, redactBodies: true }), now = Date.now) {
|
|
2639
|
+
this.credentials = credentials;
|
|
2640
|
+
this.store = store;
|
|
2641
|
+
this.fetchImpl = fetchImpl;
|
|
2642
|
+
this.now = now;
|
|
2643
|
+
}
|
|
2644
|
+
credentials;
|
|
2645
|
+
store;
|
|
2646
|
+
fetchImpl;
|
|
2647
|
+
now;
|
|
2648
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
2649
|
+
async collectMany(accounts, options = {}) {
|
|
2650
|
+
const settled = await Promise.allSettled(
|
|
2651
|
+
accounts.map((account) => this.collect(account, options))
|
|
2652
|
+
);
|
|
2653
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
2654
|
+
}
|
|
2655
|
+
collect(account, options = {}) {
|
|
2656
|
+
const now = this.now();
|
|
2657
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
2658
|
+
const existing = this.store.get("copilot", account.id, now);
|
|
2659
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
2660
|
+
return Promise.resolve(existing);
|
|
2661
|
+
}
|
|
2662
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
2663
|
+
this.store.set(snapshot);
|
|
2664
|
+
return Promise.resolve(snapshot);
|
|
2665
|
+
}
|
|
2666
|
+
const cached = this.store.get("copilot", account.id, now);
|
|
2667
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
2668
|
+
return Promise.resolve(cached);
|
|
2669
|
+
}
|
|
2670
|
+
const running = this.inFlight.get(account.id);
|
|
2671
|
+
if (running) return running;
|
|
2672
|
+
const promise = this.fetchAccount(account.id, account.tokens).catch(() => this.failureSnapshot(account.id, "copilot_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
2673
|
+
this.inFlight.set(account.id, promise);
|
|
2674
|
+
return promise;
|
|
2675
|
+
}
|
|
2676
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
2677
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
2678
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
2679
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
2680
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
2681
|
+
}
|
|
2682
|
+
async fetchAccount(accountId, tokens) {
|
|
2683
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
|
|
2684
|
+
if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
|
|
2685
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
2686
|
+
if (response.status === 401 || response.status === 403) {
|
|
2687
|
+
const refreshed = await this.credentials.refreshAccountToken("copilot", accountId);
|
|
2688
|
+
if (!refreshed) return this.failureSnapshot(accountId, "copilot_usage_unauthorized", this.now());
|
|
2689
|
+
accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
|
|
2690
|
+
if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
|
|
2691
|
+
response = await this.request(accountId, accessToken, tokens);
|
|
2692
|
+
if (response.status === 401 || response.status === 403) {
|
|
2693
|
+
return this.failureSnapshot(accountId, "copilot_usage_unauthorized", this.now());
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
if (!response.ok) return this.failureSnapshot(accountId, "copilot_usage_http_error", this.now());
|
|
2697
|
+
let payload;
|
|
2698
|
+
try {
|
|
2699
|
+
payload = await response.json();
|
|
2700
|
+
} catch {
|
|
2701
|
+
return this.failureSnapshot(accountId, "copilot_usage_invalid_response", this.now());
|
|
2702
|
+
}
|
|
2703
|
+
const now = this.now();
|
|
2704
|
+
const windows = parseCopilotUserPayload(payload, now);
|
|
2705
|
+
const snapshot = {
|
|
2706
|
+
providerId: "copilot",
|
|
2707
|
+
accountId,
|
|
2708
|
+
source: "oauth-usage-api",
|
|
2709
|
+
observedAt: new Date(now).toISOString(),
|
|
2710
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2711
|
+
windows: windows ?? [
|
|
2712
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2713
|
+
],
|
|
2714
|
+
...windows ? {} : { lastErrorCode: "copilot_usage_invalid_response" }
|
|
2715
|
+
};
|
|
2716
|
+
this.store.set(snapshot);
|
|
2717
|
+
return snapshot;
|
|
2718
|
+
}
|
|
2719
|
+
request(accountId, accessToken, tokens) {
|
|
2720
|
+
return this.fetchImpl(`${githubApiBase(tokens)}/copilot_internal/user`, {
|
|
2721
|
+
method: "GET",
|
|
2722
|
+
headers: {
|
|
2723
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2724
|
+
Accept: "application/json",
|
|
2725
|
+
"Content-Type": "application/json",
|
|
2726
|
+
...COPILOT_GITHUB_HEADERS
|
|
2727
|
+
},
|
|
2728
|
+
signal: AbortSignal.timeout(15e3)
|
|
2729
|
+
}, accountId);
|
|
2730
|
+
}
|
|
2731
|
+
failureSnapshot(accountId, code, now) {
|
|
2732
|
+
const existing = this.store.get("copilot", accountId, now);
|
|
2733
|
+
const snapshot = existing ? {
|
|
2734
|
+
...existing,
|
|
2735
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2736
|
+
windows: existing.windows.map((window) => ({
|
|
2737
|
+
...window,
|
|
2738
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2739
|
+
})),
|
|
2740
|
+
lastErrorCode: code
|
|
2741
|
+
} : {
|
|
2742
|
+
providerId: "copilot",
|
|
2743
|
+
accountId,
|
|
2744
|
+
source: "oauth-usage-api",
|
|
2745
|
+
observedAt: new Date(now).toISOString(),
|
|
2746
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2747
|
+
windows: [
|
|
2748
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2749
|
+
],
|
|
2750
|
+
lastErrorCode: code
|
|
2751
|
+
};
|
|
2752
|
+
this.store.set(snapshot);
|
|
2753
|
+
return snapshot;
|
|
2754
|
+
}
|
|
2755
|
+
unsupportedSnapshot(accountId, now) {
|
|
2756
|
+
return {
|
|
2757
|
+
providerId: "copilot",
|
|
2758
|
+
accountId,
|
|
2759
|
+
source: "oauth-usage-api",
|
|
2760
|
+
observedAt: new Date(now).toISOString(),
|
|
2761
|
+
windows: [
|
|
2762
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2763
|
+
],
|
|
2764
|
+
lastErrorCode: "copilot_usage_unsupported_auth"
|
|
2765
|
+
};
|
|
2766
|
+
}
|
|
2767
|
+
};
|
|
2768
|
+
|
|
2769
|
+
// src/allowance/GeminiAllowanceCollector.ts
|
|
2770
|
+
import { getGeminiCodeAssistProjectResolver } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
|
|
2771
|
+
import {
|
|
2772
|
+
getSharedAccountAllowanceStore as getSharedAccountAllowanceStore6
|
|
2773
|
+
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
2774
|
+
import { fetchUpstream as fetchUpstream6 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
2775
|
+
import {
|
|
2776
|
+
getGeminiCliIdentityHeaders,
|
|
2777
|
+
resolveCodeAssistEndpoint
|
|
2778
|
+
} from "@omnicross/core/transformer/transformers";
|
|
2779
|
+
var GEMINI_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2780
|
+
function isRecord4(value) {
|
|
2781
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2782
|
+
}
|
|
2783
|
+
function secondsUntil6(instant, now) {
|
|
2784
|
+
if (!instant) return void 0;
|
|
2785
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2786
|
+
}
|
|
2787
|
+
function parseGeminiQuotaPayload(payload, now) {
|
|
2788
|
+
if (!isRecord4(payload)) return null;
|
|
2789
|
+
const buckets = Array.isArray(payload["buckets"]) ? payload["buckets"] : [];
|
|
2790
|
+
const windows = [];
|
|
2791
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2792
|
+
for (const raw of buckets) {
|
|
2793
|
+
if (!isRecord4(raw)) continue;
|
|
2794
|
+
const modelId = typeof raw["modelId"] === "string" && raw["modelId"].trim() ? raw["modelId"].trim() : void 0;
|
|
2795
|
+
const id = `gemini:${modelId ?? "all"}`;
|
|
2796
|
+
if (seen.has(id)) continue;
|
|
2797
|
+
seen.add(id);
|
|
2798
|
+
const fractionRaw = typeof raw["remainingFraction"] === "number" ? raw["remainingFraction"] : Number(raw["remainingFraction"]);
|
|
2799
|
+
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;
|
|
2800
|
+
const resetRaw = typeof raw["resetTime"] === "string" && raw["resetTime"].trim() ? raw["resetTime"] : void 0;
|
|
2801
|
+
const resetsAt = resetRaw !== void 0 && Number.isFinite(Date.parse(resetRaw)) ? new Date(Date.parse(resetRaw)).toISOString() : void 0;
|
|
2802
|
+
windows.push({
|
|
2803
|
+
id,
|
|
2804
|
+
label: modelId ? `Gemini ${modelId}` : "Gemini quota",
|
|
2805
|
+
scope: modelId ? "model-family" : "all",
|
|
2806
|
+
...modelId ? { modelFamily: modelId } : {},
|
|
2807
|
+
usedPercent,
|
|
2808
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2809
|
+
remainingSeconds: secondsUntil6(resetsAt, now),
|
|
2810
|
+
state: "fresh"
|
|
2811
|
+
});
|
|
2812
|
+
}
|
|
2813
|
+
return windows.length > 0 ? windows : null;
|
|
2814
|
+
}
|
|
2815
|
+
var GeminiAllowanceCollector = class {
|
|
2816
|
+
constructor(credentials, store = getSharedAccountAllowanceStore6(), fetchImpl = (url, init, accountId) => fetchUpstream6(url, init, { providerId: "gemini", accountId, redactBodies: true }), now = Date.now, projectResolver = getGeminiCodeAssistProjectResolver()) {
|
|
2817
|
+
this.credentials = credentials;
|
|
2818
|
+
this.store = store;
|
|
2819
|
+
this.fetchImpl = fetchImpl;
|
|
2820
|
+
this.now = now;
|
|
2821
|
+
this.projectResolver = projectResolver;
|
|
2822
|
+
}
|
|
2823
|
+
credentials;
|
|
2824
|
+
store;
|
|
2825
|
+
fetchImpl;
|
|
2826
|
+
now;
|
|
2827
|
+
projectResolver;
|
|
2828
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
2829
|
+
async collectMany(accounts, options = {}) {
|
|
2830
|
+
const settled = await Promise.allSettled(
|
|
2831
|
+
accounts.map((account) => this.collect(account, options))
|
|
2832
|
+
);
|
|
1991
2833
|
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
1992
2834
|
}
|
|
1993
2835
|
collect(account, options = {}) {
|
|
1994
2836
|
const now = this.now();
|
|
1995
2837
|
if (account.tokens.authMethod !== "oauth") {
|
|
1996
|
-
const existing = this.store.get("
|
|
2838
|
+
const existing = this.store.get("gemini", account.id, now);
|
|
1997
2839
|
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
1998
2840
|
return Promise.resolve(existing);
|
|
1999
2841
|
}
|
|
@@ -2001,13 +2843,13 @@ var KimiAllowanceCollector = class {
|
|
|
2001
2843
|
this.store.set(snapshot);
|
|
2002
2844
|
return Promise.resolve(snapshot);
|
|
2003
2845
|
}
|
|
2004
|
-
const cached = this.store.get("
|
|
2846
|
+
const cached = this.store.get("gemini", account.id, now);
|
|
2005
2847
|
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
2006
2848
|
return Promise.resolve(cached);
|
|
2007
2849
|
}
|
|
2008
2850
|
const running = this.inFlight.get(account.id);
|
|
2009
2851
|
if (running) return running;
|
|
2010
|
-
const promise = this.fetchAccount(account.id
|
|
2852
|
+
const promise = this.fetchAccount(account.id).catch(() => this.failureSnapshot(account.id, "gemini_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
2011
2853
|
this.inFlight.set(account.id, promise);
|
|
2012
2854
|
return promise;
|
|
2013
2855
|
}
|
|
@@ -2017,102 +2859,105 @@ var KimiAllowanceCollector = class {
|
|
|
2017
2859
|
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
2018
2860
|
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
2019
2861
|
}
|
|
2020
|
-
async fetchAccount(accountId
|
|
2021
|
-
let accessToken = await this.credentials.getAccessTokenForAccount("
|
|
2022
|
-
if (!accessToken) return this.failureSnapshot(accountId, "
|
|
2023
|
-
let
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
2029
|
-
response = await this.request(accountId, accessToken, tokens);
|
|
2862
|
+
async fetchAccount(accountId) {
|
|
2863
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("gemini", accountId);
|
|
2864
|
+
if (!accessToken) return this.failureSnapshot(accountId, "gemini_usage_token_unavailable", this.now());
|
|
2865
|
+
let project;
|
|
2866
|
+
try {
|
|
2867
|
+
project = await this.projectResolver.resolveProject(accessToken);
|
|
2868
|
+
} catch {
|
|
2869
|
+
project = void 0;
|
|
2030
2870
|
}
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
this.
|
|
2034
|
-
return
|
|
2871
|
+
let response = await this.request(accountId, accessToken, project);
|
|
2872
|
+
if (response.status === 401 || response.status === 403) {
|
|
2873
|
+
const refreshed = await this.credentials.refreshAccountToken("gemini", accountId);
|
|
2874
|
+
if (!refreshed) return this.failureSnapshot(accountId, "gemini_usage_unauthorized", this.now());
|
|
2875
|
+
accessToken = await this.credentials.getAccessTokenForAccount("gemini", accountId);
|
|
2876
|
+
if (!accessToken) return this.failureSnapshot(accountId, "gemini_usage_token_unavailable", this.now());
|
|
2877
|
+
response = await this.request(accountId, accessToken, project);
|
|
2878
|
+
if (response.status === 401 || response.status === 403) {
|
|
2879
|
+
return this.failureSnapshot(accountId, "gemini_usage_unauthorized", this.now());
|
|
2880
|
+
}
|
|
2035
2881
|
}
|
|
2036
|
-
if (!response.ok) return this.failureSnapshot(accountId, "
|
|
2882
|
+
if (!response.ok) return this.failureSnapshot(accountId, "gemini_usage_http_error", this.now());
|
|
2037
2883
|
let payload;
|
|
2038
2884
|
try {
|
|
2039
2885
|
payload = await response.json();
|
|
2040
2886
|
} catch {
|
|
2041
|
-
return this.failureSnapshot(accountId, "
|
|
2887
|
+
return this.failureSnapshot(accountId, "gemini_usage_invalid_response", this.now());
|
|
2042
2888
|
}
|
|
2043
2889
|
const now = this.now();
|
|
2044
|
-
const windows =
|
|
2890
|
+
const windows = parseGeminiQuotaPayload(payload, now);
|
|
2045
2891
|
const snapshot = {
|
|
2046
|
-
providerId: "
|
|
2892
|
+
providerId: "gemini",
|
|
2047
2893
|
accountId,
|
|
2048
2894
|
source: "oauth-usage-api",
|
|
2049
2895
|
observedAt: new Date(now).toISOString(),
|
|
2050
|
-
expiresAt: new Date(now +
|
|
2051
|
-
windows: windows
|
|
2052
|
-
{ id: "
|
|
2053
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2896
|
+
expiresAt: new Date(now + GEMINI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2897
|
+
windows: windows ?? [
|
|
2898
|
+
{ id: "gemini-quota", label: "Gemini quota", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2054
2899
|
],
|
|
2055
|
-
...windows
|
|
2900
|
+
...windows ? {} : { lastErrorCode: "gemini_usage_invalid_response" }
|
|
2056
2901
|
};
|
|
2057
2902
|
this.store.set(snapshot);
|
|
2058
2903
|
return snapshot;
|
|
2059
2904
|
}
|
|
2060
|
-
request(accountId, accessToken,
|
|
2061
|
-
return this.fetchImpl(
|
|
2062
|
-
method: "
|
|
2905
|
+
request(accountId, accessToken, project) {
|
|
2906
|
+
return this.fetchImpl(`${resolveCodeAssistEndpoint()}/v1internal:retrieveUserQuota`, {
|
|
2907
|
+
method: "POST",
|
|
2063
2908
|
headers: {
|
|
2064
2909
|
Authorization: `Bearer ${accessToken}`,
|
|
2065
2910
|
Accept: "application/json",
|
|
2066
|
-
|
|
2911
|
+
"Content-Type": "application/json",
|
|
2912
|
+
...getGeminiCliIdentityHeaders()
|
|
2067
2913
|
},
|
|
2914
|
+
body: JSON.stringify(project ? { project } : {}),
|
|
2068
2915
|
signal: AbortSignal.timeout(15e3)
|
|
2069
2916
|
}, accountId);
|
|
2070
2917
|
}
|
|
2071
2918
|
failureSnapshot(accountId, code, now) {
|
|
2072
|
-
const existing = this.store.get("
|
|
2919
|
+
const existing = this.store.get("gemini", accountId, now);
|
|
2073
2920
|
const snapshot = existing ? {
|
|
2074
2921
|
...existing,
|
|
2075
|
-
expiresAt: new Date(now +
|
|
2922
|
+
expiresAt: new Date(now + GEMINI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2076
2923
|
windows: existing.windows.map((window) => ({
|
|
2077
2924
|
...window,
|
|
2078
2925
|
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2079
2926
|
})),
|
|
2080
2927
|
lastErrorCode: code
|
|
2081
2928
|
} : {
|
|
2082
|
-
providerId: "
|
|
2929
|
+
providerId: "gemini",
|
|
2083
2930
|
accountId,
|
|
2084
2931
|
source: "oauth-usage-api",
|
|
2085
2932
|
observedAt: new Date(now).toISOString(),
|
|
2086
|
-
expiresAt: new Date(now +
|
|
2933
|
+
expiresAt: new Date(now + GEMINI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2087
2934
|
windows: [
|
|
2088
|
-
{ id: "
|
|
2089
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2935
|
+
{ id: "gemini-quota", label: "Gemini quota", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2090
2936
|
],
|
|
2091
2937
|
lastErrorCode: code
|
|
2092
2938
|
};
|
|
2093
2939
|
this.store.set(snapshot);
|
|
2094
2940
|
return snapshot;
|
|
2095
2941
|
}
|
|
2096
|
-
unsupportedSnapshot(accountId, now
|
|
2942
|
+
unsupportedSnapshot(accountId, now) {
|
|
2097
2943
|
return {
|
|
2098
|
-
providerId: "
|
|
2944
|
+
providerId: "gemini",
|
|
2099
2945
|
accountId,
|
|
2100
2946
|
source: "oauth-usage-api",
|
|
2101
2947
|
observedAt: new Date(now).toISOString(),
|
|
2102
2948
|
windows: [
|
|
2103
|
-
{ id: "
|
|
2104
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2949
|
+
{ id: "gemini-quota", label: "Gemini quota", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2105
2950
|
],
|
|
2106
|
-
lastErrorCode:
|
|
2951
|
+
lastErrorCode: "gemini_usage_unsupported_auth"
|
|
2107
2952
|
};
|
|
2108
2953
|
}
|
|
2109
2954
|
};
|
|
2110
2955
|
|
|
2111
2956
|
// src/allowance/OpenCodeGoAllowanceCollector.ts
|
|
2112
2957
|
import {
|
|
2113
|
-
getSharedAccountAllowanceStore as
|
|
2958
|
+
getSharedAccountAllowanceStore as getSharedAccountAllowanceStore7
|
|
2114
2959
|
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
2115
|
-
import { fetchUpstream as
|
|
2960
|
+
import { fetchUpstream as fetchUpstream7 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
2116
2961
|
import { normalizeOpenCodeGoBaseUrl } from "@omnicross/subscriptions";
|
|
2117
2962
|
var OPENCODEGO_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2118
2963
|
var OPENCODEGO_DEFAULT_GO_BASE = "https://opencode.ai/zen/go";
|
|
@@ -2126,7 +2971,7 @@ function isoInstant2(value) {
|
|
|
2126
2971
|
const time = Date.parse(value);
|
|
2127
2972
|
return Number.isFinite(time) ? new Date(time).toISOString() : void 0;
|
|
2128
2973
|
}
|
|
2129
|
-
function
|
|
2974
|
+
function secondsUntil7(instant, now) {
|
|
2130
2975
|
if (!instant) return void 0;
|
|
2131
2976
|
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2132
2977
|
}
|
|
@@ -2141,12 +2986,12 @@ function windowFromPayload3(id, label, minutes, payload, now) {
|
|
|
2141
2986
|
usedPercent,
|
|
2142
2987
|
windowMinutes: minutes,
|
|
2143
2988
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2144
|
-
remainingSeconds:
|
|
2989
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
2145
2990
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
2146
2991
|
};
|
|
2147
2992
|
}
|
|
2148
2993
|
var OpenCodeGoAllowanceCollector = class {
|
|
2149
|
-
constructor(credentials, store =
|
|
2994
|
+
constructor(credentials, store = getSharedAccountAllowanceStore7(), fetchImpl = (url, init, accountId) => fetchUpstream7(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
|
|
2150
2995
|
this.credentials = credentials;
|
|
2151
2996
|
this.store = store;
|
|
2152
2997
|
this.fetchImpl = fetchImpl;
|
|
@@ -2251,7 +3096,7 @@ function codexUnavailable(accountId, now) {
|
|
|
2251
3096
|
};
|
|
2252
3097
|
}
|
|
2253
3098
|
var AccountAllowanceService = class {
|
|
2254
|
-
constructor(credentials, store =
|
|
3099
|
+
constructor(credentials, store = getSharedAccountAllowanceStore8(), collector, codexCollector, kimiCollector, opencodegoCollector, grokCollector, copilotCollector, geminiCollector, now = Date.now) {
|
|
2255
3100
|
this.credentials = credentials;
|
|
2256
3101
|
this.store = store;
|
|
2257
3102
|
this.now = now;
|
|
@@ -2259,6 +3104,9 @@ var AccountAllowanceService = class {
|
|
|
2259
3104
|
this.codexCollector = codexCollector ?? new CodexAllowanceCollector(credentials, store);
|
|
2260
3105
|
this.kimiCollector = kimiCollector ?? new KimiAllowanceCollector(credentials, store);
|
|
2261
3106
|
this.opencodegoCollector = opencodegoCollector ?? new OpenCodeGoAllowanceCollector(credentials, store);
|
|
3107
|
+
this.grokCollector = grokCollector ?? new GrokAllowanceCollector(credentials, store);
|
|
3108
|
+
this.copilotCollector = copilotCollector ?? new CopilotAllowanceCollector(credentials, store);
|
|
3109
|
+
this.geminiCollector = geminiCollector ?? new GeminiAllowanceCollector(credentials, store);
|
|
2262
3110
|
}
|
|
2263
3111
|
credentials;
|
|
2264
3112
|
store;
|
|
@@ -2266,7 +3114,10 @@ var AccountAllowanceService = class {
|
|
|
2266
3114
|
claudeCollector;
|
|
2267
3115
|
codexCollector;
|
|
2268
3116
|
kimiCollector;
|
|
3117
|
+
grokCollector;
|
|
3118
|
+
copilotCollector;
|
|
2269
3119
|
opencodegoCollector;
|
|
3120
|
+
geminiCollector;
|
|
2270
3121
|
/**
|
|
2271
3122
|
* Read all/filtered snapshots. Claude's and Codex's five-minute caches are
|
|
2272
3123
|
* refreshed lazily on read (Codex polls `/backend-api/wham/usage`; the
|
|
@@ -2300,11 +3151,29 @@ var AccountAllowanceService = class {
|
|
|
2300
3151
|
(account) => !filter.accountId || account.id === filter.accountId
|
|
2301
3152
|
);
|
|
2302
3153
|
if (wantsOpenCodeGo) await this.opencodegoCollector.collectMany(opencodegoAccounts);
|
|
3154
|
+
const wantsGrok = !filter.providerId || filter.providerId === "grok";
|
|
3155
|
+
const grokAccounts = (config.grokAccounts ?? []).filter(
|
|
3156
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
3157
|
+
);
|
|
3158
|
+
if (wantsGrok) await this.grokCollector.collectMany(grokAccounts);
|
|
3159
|
+
const wantsCopilot = !filter.providerId || filter.providerId === "copilot";
|
|
3160
|
+
const copilotAccounts = (config.copilotAccounts ?? []).filter(
|
|
3161
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
3162
|
+
);
|
|
3163
|
+
if (wantsCopilot) await this.copilotCollector.collectMany(copilotAccounts);
|
|
3164
|
+
const wantsGemini = !filter.providerId || filter.providerId === "gemini";
|
|
3165
|
+
const geminiAccounts = (config.geminiAccounts ?? []).filter(
|
|
3166
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
3167
|
+
);
|
|
3168
|
+
if (wantsGemini) await this.geminiCollector.collectMany(geminiAccounts);
|
|
2303
3169
|
const known = /* @__PURE__ */ new Set();
|
|
2304
3170
|
if (wantsClaude) for (const account of claudeAccounts) known.add(`claude\0${account.id}`);
|
|
2305
3171
|
if (wantsCodex) for (const account of codexAccounts) known.add(`codex\0${account.id}`);
|
|
2306
3172
|
if (wantsKimi) for (const account of kimiAccounts) known.add(`kimi\0${account.id}`);
|
|
2307
3173
|
if (wantsOpenCodeGo) for (const account of opencodegoAccounts) known.add(`opencodego\0${account.id}`);
|
|
3174
|
+
if (wantsGrok) for (const account of grokAccounts) known.add(`grok\0${account.id}`);
|
|
3175
|
+
if (wantsCopilot) for (const account of copilotAccounts) known.add(`copilot\0${account.id}`);
|
|
3176
|
+
if (wantsGemini) for (const account of geminiAccounts) known.add(`gemini\0${account.id}`);
|
|
2308
3177
|
return this.store.list(filter).filter((snapshot) => known.has(`${snapshot.providerId}\0${snapshot.accountId}`));
|
|
2309
3178
|
}
|
|
2310
3179
|
knownAccounts(config) {
|
|
@@ -2312,7 +3181,10 @@ var AccountAllowanceService = class {
|
|
|
2312
3181
|
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
2313
3182
|
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id })),
|
|
2314
3183
|
...(config.kimiAccounts ?? []).map((account) => ({ providerId: "kimi", accountId: account.id })),
|
|
2315
|
-
...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id }))
|
|
3184
|
+
...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id })),
|
|
3185
|
+
...(config.grokAccounts ?? []).map((account) => ({ providerId: "grok", accountId: account.id })),
|
|
3186
|
+
...(config.copilotAccounts ?? []).map((account) => ({ providerId: "copilot", accountId: account.id })),
|
|
3187
|
+
...(config.geminiAccounts ?? []).map((account) => ({ providerId: "gemini", accountId: account.id }))
|
|
2316
3188
|
];
|
|
2317
3189
|
}
|
|
2318
3190
|
/** Force-refresh Claude usage for one account or every stored Claude account. */
|
|
@@ -2355,6 +3227,33 @@ var AccountAllowanceService = class {
|
|
|
2355
3227
|
);
|
|
2356
3228
|
return this.kimiCollector.collectMany(accounts, { force: true });
|
|
2357
3229
|
}
|
|
3230
|
+
/** Force-refresh Copilot usage (copilot_internal/user) for one/all accounts. */
|
|
3231
|
+
async refreshCopilot(accountId) {
|
|
3232
|
+
const config = await this.credentials.getFullConfig();
|
|
3233
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
3234
|
+
const accounts = (config.copilotAccounts ?? []).filter(
|
|
3235
|
+
(account) => !accountId || account.id === accountId
|
|
3236
|
+
);
|
|
3237
|
+
return this.copilotCollector.collectMany(accounts, { force: true });
|
|
3238
|
+
}
|
|
3239
|
+
/** Force-refresh Grok usage (CLI billing proxy) for one/all accounts. */
|
|
3240
|
+
async refreshGrok(accountId) {
|
|
3241
|
+
const config = await this.credentials.getFullConfig();
|
|
3242
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
3243
|
+
const accounts = (config.grokAccounts ?? []).filter(
|
|
3244
|
+
(account) => !accountId || account.id === accountId
|
|
3245
|
+
);
|
|
3246
|
+
return this.grokCollector.collectMany(accounts, { force: true });
|
|
3247
|
+
}
|
|
3248
|
+
/** Force-refresh Gemini usage (Code Assist retrieveUserQuota) for one/all accounts. */
|
|
3249
|
+
async refreshGemini(accountId) {
|
|
3250
|
+
const config = await this.credentials.getFullConfig();
|
|
3251
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
3252
|
+
const accounts = (config.geminiAccounts ?? []).filter(
|
|
3253
|
+
(account) => !accountId || account.id === accountId
|
|
3254
|
+
);
|
|
3255
|
+
return this.geminiCollector.collectMany(accounts, { force: true });
|
|
3256
|
+
}
|
|
2358
3257
|
/**
|
|
2359
3258
|
* Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
|
|
2360
3259
|
* collectors preserve their cache + per-account in-flight coalescing; a tick
|
|
@@ -2369,6 +3268,9 @@ var AccountAllowanceService = class {
|
|
|
2369
3268
|
await this.codexCollector.collectMany(config.codexAccounts ?? [], { refreshAheadMs });
|
|
2370
3269
|
await this.kimiCollector.collectMany(config.kimiAccounts ?? [], { refreshAheadMs });
|
|
2371
3270
|
await this.opencodegoCollector.collectMany(config.opencodegoAccounts ?? [], { refreshAheadMs });
|
|
3271
|
+
await this.grokCollector.collectMany(config.grokAccounts ?? [], { refreshAheadMs });
|
|
3272
|
+
await this.copilotCollector.collectMany(config.copilotAccounts ?? [], { refreshAheadMs });
|
|
3273
|
+
await this.geminiCollector.collectMany(config.geminiAccounts ?? [], { refreshAheadMs });
|
|
2372
3274
|
}
|
|
2373
3275
|
/** Remove a cache row as soon as an account is deleted by the admin path. */
|
|
2374
3276
|
removeAccountSnapshot(providerId, accountId) {
|
|
@@ -2798,7 +3700,8 @@ import {
|
|
|
2798
3700
|
} from "@omnicross/contracts/image-generation-types";
|
|
2799
3701
|
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling2 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
2800
3702
|
import { getSharedAccountHealth } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
2801
|
-
import { fetchUpstream as
|
|
3703
|
+
import { fetchUpstream as fetchUpstream8 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
3704
|
+
import { mergeExtraHeaders } from "@omnicross/core";
|
|
2802
3705
|
|
|
2803
3706
|
// src/image-generation/imagesConfigValidation.ts
|
|
2804
3707
|
import { validateImagesServerConfig } from "@omnicross/core/outbound-api";
|
|
@@ -3111,6 +4014,7 @@ async function applyServerConfigTransaction(current, next, deps) {
|
|
|
3111
4014
|
|
|
3112
4015
|
// src/config.ts
|
|
3113
4016
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
4017
|
+
import { EXTRA_HEADER_RESERVED_NAMES } from "@omnicross/core";
|
|
3114
4018
|
var DEFAULT_ADMIN_PORT = 8766;
|
|
3115
4019
|
function validateAdmin(raw) {
|
|
3116
4020
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
@@ -3167,6 +4071,18 @@ var FORMAT_AXIS_TRANSFORMERS = [
|
|
|
3167
4071
|
"openai-response",
|
|
3168
4072
|
"gemini-code-assist"
|
|
3169
4073
|
];
|
|
4074
|
+
function validateExtraHeaders(raw) {
|
|
4075
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
4076
|
+
const reserved = EXTRA_HEADER_RESERVED_NAMES;
|
|
4077
|
+
const out = {};
|
|
4078
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
4079
|
+
if (!name.trim()) continue;
|
|
4080
|
+
if (typeof value !== "string") continue;
|
|
4081
|
+
if (reserved.has(name.toLowerCase())) continue;
|
|
4082
|
+
out[name] = value;
|
|
4083
|
+
}
|
|
4084
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
4085
|
+
}
|
|
3170
4086
|
function validateApiKeys(raw) {
|
|
3171
4087
|
if (!Array.isArray(raw)) return void 0;
|
|
3172
4088
|
const out = [];
|
|
@@ -3380,6 +4296,9 @@ function validateProvider(raw, index) {
|
|
|
3380
4296
|
apiVersion,
|
|
3381
4297
|
maxConcurrency,
|
|
3382
4298
|
modelsEndpoint,
|
|
4299
|
+
// Static extra headers: load-guard (reserved names dropped), collapse-to-
|
|
4300
|
+
// undefined; enforced by the outbound header funnel + admin probes.
|
|
4301
|
+
extraHeaders: validateExtraHeaders(p["extraHeaders"]),
|
|
3383
4302
|
// Provider transformer config (app-parity child 5): load-guard, collapse-to-
|
|
3384
4303
|
// undefined; non-secret; ENFORCED via resolveTransformerChain (parity-2 child 2).
|
|
3385
4304
|
// Format-axis entries are stripped by `migrateFormatAxis` — `use[]` is the
|
|
@@ -4344,7 +5263,10 @@ function mapPresetToProvider(preset, opts) {
|
|
|
4344
5263
|
apiFormat: resolved.format,
|
|
4345
5264
|
baseUrl: opts.baseUrlOverride ?? preset.api_base_url,
|
|
4346
5265
|
apiKey: opts.key,
|
|
4347
|
-
models: Array.isArray(preset.models) ? preset.models : void 0
|
|
5266
|
+
models: Array.isArray(preset.models) ? preset.models : void 0,
|
|
5267
|
+
// Static identity headers (e.g. the Cline client set) survive the mapping —
|
|
5268
|
+
// the CLI-seeded row needs them as much as an admin-API-created one.
|
|
5269
|
+
extraHeaders: preset.extraHeaders
|
|
4348
5270
|
};
|
|
4349
5271
|
return { provider };
|
|
4350
5272
|
}
|
|
@@ -4369,7 +5291,8 @@ function listMappablePresets() {
|
|
|
4369
5291
|
description: preset.description,
|
|
4370
5292
|
features: preset.features,
|
|
4371
5293
|
website: preset.website,
|
|
4372
|
-
modelsEndpoint: preset.modelsEndpoint
|
|
5294
|
+
modelsEndpoint: preset.modelsEndpoint,
|
|
5295
|
+
extraHeaders: preset.extraHeaders
|
|
4373
5296
|
});
|
|
4374
5297
|
}
|
|
4375
5298
|
return { mappable, excluded };
|
|
@@ -4541,7 +5464,9 @@ var VALID_PROVIDER_IDS = [
|
|
|
4541
5464
|
"codex",
|
|
4542
5465
|
"gemini",
|
|
4543
5466
|
"opencodego",
|
|
4544
|
-
"kimi"
|
|
5467
|
+
"kimi",
|
|
5468
|
+
"grok",
|
|
5469
|
+
"copilot"
|
|
4545
5470
|
];
|
|
4546
5471
|
function asSubscriptionProviderId(id) {
|
|
4547
5472
|
return VALID_PROVIDER_IDS.includes(id) ? id : null;
|
|
@@ -4689,6 +5614,40 @@ function validateKimi(body) {
|
|
|
4689
5614
|
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "deviceId", "lastRefreshedAt", "errorMessage"]);
|
|
4690
5615
|
return out;
|
|
4691
5616
|
}
|
|
5617
|
+
function validateGrok(body) {
|
|
5618
|
+
const authMethod = str(body["authMethod"]);
|
|
5619
|
+
const status = str(body["status"]);
|
|
5620
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
5621
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
5622
|
+
const out = {
|
|
5623
|
+
authMethod,
|
|
5624
|
+
status
|
|
5625
|
+
};
|
|
5626
|
+
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "lastRefreshedAt", "errorMessage"]);
|
|
5627
|
+
return out;
|
|
5628
|
+
}
|
|
5629
|
+
function validateCopilot(body) {
|
|
5630
|
+
const authMethod = str(body["authMethod"]);
|
|
5631
|
+
const status = str(body["status"]);
|
|
5632
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
5633
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
5634
|
+
const out = {
|
|
5635
|
+
authMethod,
|
|
5636
|
+
status
|
|
5637
|
+
};
|
|
5638
|
+
copyOptional(out, body, [
|
|
5639
|
+
"accessToken",
|
|
5640
|
+
"refreshToken",
|
|
5641
|
+
"expiresAt",
|
|
5642
|
+
"accountId",
|
|
5643
|
+
"email",
|
|
5644
|
+
"apiEndpoint",
|
|
5645
|
+
"enterpriseUrl",
|
|
5646
|
+
"lastRefreshedAt",
|
|
5647
|
+
"errorMessage"
|
|
5648
|
+
]);
|
|
5649
|
+
return out;
|
|
5650
|
+
}
|
|
4692
5651
|
function validateOpenCodeGo(body) {
|
|
4693
5652
|
const authMethod = str(body["authMethod"]);
|
|
4694
5653
|
const status = str(body["status"]);
|
|
@@ -4726,6 +5685,10 @@ function validateTokenBody(providerId, body) {
|
|
|
4726
5685
|
return validateOpenCodeGo(body);
|
|
4727
5686
|
case "kimi":
|
|
4728
5687
|
return validateKimi(body);
|
|
5688
|
+
case "grok":
|
|
5689
|
+
return validateGrok(body);
|
|
5690
|
+
case "copilot":
|
|
5691
|
+
return validateCopilot(body);
|
|
4729
5692
|
default:
|
|
4730
5693
|
return null;
|
|
4731
5694
|
}
|
|
@@ -4755,12 +5718,12 @@ async function statusEntryFor(reader, providerId) {
|
|
|
4755
5718
|
|
|
4756
5719
|
// src/admin/accountsOAuth.ts
|
|
4757
5720
|
var OAUTH_HTTP_PROVIDERS = /* @__PURE__ */ new Set(["claude", "gemini"]);
|
|
4758
|
-
function
|
|
5721
|
+
function err5(status, message) {
|
|
4759
5722
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
4760
5723
|
}
|
|
4761
5724
|
function handleOAuthStart(providerId, deps) {
|
|
4762
5725
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
4763
|
-
return
|
|
5726
|
+
return err5(400, `oauth not available for provider '${providerId}'`);
|
|
4764
5727
|
}
|
|
4765
5728
|
const flow = providerId === "claude" ? claudeOAuth : geminiOAuth;
|
|
4766
5729
|
const { authUrl, codeVerifier, state } = flow.generateAuthParams();
|
|
@@ -4769,23 +5732,23 @@ function handleOAuthStart(providerId, deps) {
|
|
|
4769
5732
|
}
|
|
4770
5733
|
async function handleOAuthComplete(providerId, body, deps) {
|
|
4771
5734
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
4772
|
-
return
|
|
5735
|
+
return err5(400, `oauth not available for provider '${providerId}'`);
|
|
4773
5736
|
}
|
|
4774
5737
|
const sessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : "";
|
|
4775
5738
|
const rawCode = typeof body["code"] === "string" ? body["code"] : "";
|
|
4776
|
-
if (!sessionId) return
|
|
4777
|
-
if (!rawCode) return
|
|
5739
|
+
if (!sessionId) return err5(400, "oauth complete requires { sessionId }");
|
|
5740
|
+
if (!rawCode) return err5(400, "oauth complete requires { code }");
|
|
4778
5741
|
const session = deps.oauthSessions.peek(sessionId);
|
|
4779
|
-
if (!session) return
|
|
5742
|
+
if (!session) return err5(410, "oauth session is unknown, expired, or already used");
|
|
4780
5743
|
if (session.providerId !== providerId) {
|
|
4781
|
-
return
|
|
5744
|
+
return err5(400, `oauth session does not match provider '${providerId}'`);
|
|
4782
5745
|
}
|
|
4783
5746
|
let code = rawCode.trim();
|
|
4784
5747
|
if (providerId === "claude") {
|
|
4785
5748
|
const [splitCode, pastedState] = code.split("#");
|
|
4786
|
-
if (!splitCode) return
|
|
5749
|
+
if (!splitCode) return err5(400, "no authorization code was provided");
|
|
4787
5750
|
if (pastedState && pastedState !== session.state) {
|
|
4788
|
-
return
|
|
5751
|
+
return err5(400, "oauth state did not match (possible CSRF) \u2014 aborting");
|
|
4789
5752
|
}
|
|
4790
5753
|
code = splitCode;
|
|
4791
5754
|
}
|
|
@@ -4795,7 +5758,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
4795
5758
|
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
|
|
4796
5759
|
} catch (exchangeError) {
|
|
4797
5760
|
const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
|
|
4798
|
-
return
|
|
5761
|
+
return err5(502, `oauth token exchange failed for '${providerId}': ${reason}`);
|
|
4799
5762
|
}
|
|
4800
5763
|
deps.oauthSessions.consume(sessionId);
|
|
4801
5764
|
const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
|
|
@@ -5133,8 +6096,8 @@ function errBody(message) {
|
|
|
5133
6096
|
return { error: { type: "admin_api_error", message } };
|
|
5134
6097
|
}
|
|
5135
6098
|
var defaultCommandRunner = (command) => new Promise((resolve11) => {
|
|
5136
|
-
exec(command, { timeout: 18e4 }, (
|
|
5137
|
-
if (
|
|
6099
|
+
exec(command, { timeout: 18e4 }, (err8, _stdout, stderr) => {
|
|
6100
|
+
if (err8) resolve11({ ok: false, error: stderr.trim() || err8.message });
|
|
5138
6101
|
else resolve11({ ok: true });
|
|
5139
6102
|
});
|
|
5140
6103
|
});
|
|
@@ -5180,8 +6143,8 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
5180
6143
|
providerId: typeof body["providerId"] === "string" ? body["providerId"] : void 0,
|
|
5181
6144
|
model: typeof body["model"] === "string" ? body["model"] : void 0
|
|
5182
6145
|
});
|
|
5183
|
-
} catch (
|
|
5184
|
-
return { status: 400, body: errBody(
|
|
6146
|
+
} catch (err8) {
|
|
6147
|
+
return { status: 400, body: errBody(err8 instanceof Error ? err8.message : "no launch target") };
|
|
5185
6148
|
}
|
|
5186
6149
|
const id = randomUUID2();
|
|
5187
6150
|
let leaseId2;
|
|
@@ -5209,9 +6172,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
5209
6172
|
} else {
|
|
5210
6173
|
launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
|
|
5211
6174
|
}
|
|
5212
|
-
} catch (
|
|
5213
|
-
const status =
|
|
5214
|
-
return { status, body: errBody(
|
|
6175
|
+
} catch (err8) {
|
|
6176
|
+
const status = err8 instanceof RouteLeaseError2 ? err8.status : 400;
|
|
6177
|
+
return { status, body: errBody(err8 instanceof Error ? err8.message : "failed to build launch env") };
|
|
5215
6178
|
}
|
|
5216
6179
|
const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
|
|
5217
6180
|
const opener = ctx.opener ?? defaultTerminalOpener;
|
|
@@ -5239,9 +6202,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
5239
6202
|
onFailure: onSessionEnd
|
|
5240
6203
|
});
|
|
5241
6204
|
if (cleanup) openerCleanup = cleanup;
|
|
5242
|
-
} catch (
|
|
6205
|
+
} catch (err8) {
|
|
5243
6206
|
onSessionEnd();
|
|
5244
|
-
return { status: 500, body: errBody(
|
|
6207
|
+
return { status: 500, body: errBody(err8 instanceof Error ? err8.message : "failed to open terminal") };
|
|
5245
6208
|
}
|
|
5246
6209
|
if (ended) {
|
|
5247
6210
|
openerCleanup?.();
|
|
@@ -5804,7 +6767,7 @@ async function handleSearchQuery(req, res, deps) {
|
|
|
5804
6767
|
// src/admin/searchAdminView.ts
|
|
5805
6768
|
var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
|
|
5806
6769
|
var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
|
|
5807
|
-
function
|
|
6770
|
+
function isRecord5(value) {
|
|
5808
6771
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5809
6772
|
}
|
|
5810
6773
|
function redactSearchServerConfig(search) {
|
|
@@ -5854,13 +6817,13 @@ function resolveSecretField(entry, field, stored) {
|
|
|
5854
6817
|
else delete entry[field];
|
|
5855
6818
|
}
|
|
5856
6819
|
function preserveSearchSecrets(incoming, current) {
|
|
5857
|
-
if (!
|
|
6820
|
+
if (!isRecord5(incoming)) return incoming;
|
|
5858
6821
|
const section = { ...incoming };
|
|
5859
6822
|
const providersValue = section["providers"];
|
|
5860
|
-
if (!
|
|
6823
|
+
if (!isRecord5(providersValue)) return section;
|
|
5861
6824
|
const providers = {};
|
|
5862
6825
|
for (const [id, entryValue] of Object.entries(providersValue)) {
|
|
5863
|
-
if (!
|
|
6826
|
+
if (!isRecord5(entryValue)) {
|
|
5864
6827
|
providers[id] = entryValue;
|
|
5865
6828
|
continue;
|
|
5866
6829
|
}
|
|
@@ -5938,7 +6901,7 @@ function parseKeyPolicyBody(body) {
|
|
|
5938
6901
|
var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
|
|
5939
6902
|
var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
|
|
5940
6903
|
var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
|
|
5941
|
-
function
|
|
6904
|
+
function isRecord6(value) {
|
|
5942
6905
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
5943
6906
|
}
|
|
5944
6907
|
function nonBlank(value) {
|
|
@@ -5958,7 +6921,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5958
6921
|
const ids = /* @__PURE__ */ new Set();
|
|
5959
6922
|
raw.forEach((entry, index) => {
|
|
5960
6923
|
const path2 = `bindings[${index}]`;
|
|
5961
|
-
if (!
|
|
6924
|
+
if (!isRecord6(entry)) {
|
|
5962
6925
|
errors.push(`${path2} must be an object`);
|
|
5963
6926
|
return;
|
|
5964
6927
|
}
|
|
@@ -5987,12 +6950,12 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5987
6950
|
} else if (entry.modelMappings.length > 100) {
|
|
5988
6951
|
errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
|
|
5989
6952
|
} else if (entry.modelMappings.some(
|
|
5990
|
-
(mapping) => !
|
|
6953
|
+
(mapping) => !isRecord6(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
|
|
5991
6954
|
)) {
|
|
5992
6955
|
errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
|
|
5993
6956
|
}
|
|
5994
6957
|
}
|
|
5995
|
-
if (!
|
|
6958
|
+
if (!isRecord6(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
|
|
5996
6959
|
errors.push(`${path2}.target is invalid`);
|
|
5997
6960
|
} else {
|
|
5998
6961
|
if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
|
|
@@ -6007,7 +6970,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
6007
6970
|
}
|
|
6008
6971
|
}
|
|
6009
6972
|
if (entry.modelMap !== void 0) {
|
|
6010
|
-
if (!
|
|
6973
|
+
if (!isRecord6(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
|
|
6011
6974
|
errors.push(`${path2}.modelMap must contain string values`);
|
|
6012
6975
|
}
|
|
6013
6976
|
}
|
|
@@ -6302,7 +7265,9 @@ var PROVIDER_KEYS = {
|
|
|
6302
7265
|
accounts: "opencodegoAccounts",
|
|
6303
7266
|
active: "activeOpencodegoAccountId"
|
|
6304
7267
|
},
|
|
6305
|
-
kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" }
|
|
7268
|
+
kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" },
|
|
7269
|
+
grok: { block: "grok", accounts: "grokAccounts", active: "activeGrokAccountId" },
|
|
7270
|
+
copilot: { block: "copilot", accounts: "copilotAccounts", active: "activeCopilotAccountId" }
|
|
6306
7271
|
};
|
|
6307
7272
|
function clone(value) {
|
|
6308
7273
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -6824,7 +7789,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
|
|
|
6824
7789
|
}
|
|
6825
7790
|
|
|
6826
7791
|
// src/admin/adminMigration.ts
|
|
6827
|
-
function
|
|
7792
|
+
function err6(status, message) {
|
|
6828
7793
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
6829
7794
|
}
|
|
6830
7795
|
async function handleExport(body, deps) {
|
|
@@ -6834,30 +7799,30 @@ async function handleExport(body, deps) {
|
|
|
6834
7799
|
return { status: 200, body: { pack, version: BUNDLE_VERSION } };
|
|
6835
7800
|
} catch (error) {
|
|
6836
7801
|
if (error instanceof WeakPassphraseError) {
|
|
6837
|
-
return
|
|
7802
|
+
return err6(400, error.message);
|
|
6838
7803
|
}
|
|
6839
|
-
return
|
|
7804
|
+
return err6(500, "failed to build the migration pack");
|
|
6840
7805
|
}
|
|
6841
7806
|
}
|
|
6842
7807
|
async function handleImport(body, deps) {
|
|
6843
7808
|
const blob = typeof body["blob"] === "string" ? body["blob"] : "";
|
|
6844
7809
|
const passphrase = typeof body["passphrase"] === "string" ? body["passphrase"] : "";
|
|
6845
7810
|
const mode = body["mode"] === "overwrite" ? "overwrite" : "merge";
|
|
6846
|
-
if (!blob) return
|
|
7811
|
+
if (!blob) return err6(400, "import requires { blob }");
|
|
6847
7812
|
try {
|
|
6848
7813
|
const counts = await applyImport(blob, passphrase, mode, deps, deps.parseProviderInput);
|
|
6849
7814
|
return { status: 200, body: counts };
|
|
6850
7815
|
} catch (error) {
|
|
6851
7816
|
if (error instanceof WeakPassphraseError) {
|
|
6852
|
-
return
|
|
7817
|
+
return err6(400, error.message);
|
|
6853
7818
|
}
|
|
6854
|
-
return
|
|
7819
|
+
return err6(400, error instanceof Error ? error.message : "import failed");
|
|
6855
7820
|
}
|
|
6856
7821
|
}
|
|
6857
7822
|
|
|
6858
7823
|
// src/admin/usagePricing.ts
|
|
6859
7824
|
import { getSharedUsageThroughputTracker } from "@omnicross/core/usage";
|
|
6860
|
-
var
|
|
7825
|
+
var err7 = (status, message) => ({
|
|
6861
7826
|
status,
|
|
6862
7827
|
body: { error: { type: "admin_api_error", message } }
|
|
6863
7828
|
});
|
|
@@ -6870,7 +7835,7 @@ function parseRange(query2) {
|
|
|
6870
7835
|
const startTs = parseFiniteInt(query2.get("startTs"));
|
|
6871
7836
|
const endTs = parseFiniteInt(query2.get("endTs"));
|
|
6872
7837
|
if (startTs === null || endTs === null) {
|
|
6873
|
-
return
|
|
7838
|
+
return err7(400, "startTs and endTs are required finite-integer unix-millis query params");
|
|
6874
7839
|
}
|
|
6875
7840
|
return { startTs, endTs };
|
|
6876
7841
|
}
|
|
@@ -6895,14 +7860,14 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
6895
7860
|
case "timeseries": {
|
|
6896
7861
|
const bucket = query2.get("bucket");
|
|
6897
7862
|
if (bucket !== "hour" && bucket !== "day" && bucket !== "month") {
|
|
6898
|
-
return
|
|
7863
|
+
return err7(400, "bucket must be one of 'hour', 'day', 'month'");
|
|
6899
7864
|
}
|
|
6900
7865
|
const now = Date.now();
|
|
6901
7866
|
const clamped = { startTs: range.startTs, endTs: Math.min(range.endTs, now) };
|
|
6902
7867
|
if (clamped.startTs < clamped.endTs) {
|
|
6903
7868
|
const projected = Math.ceil((clamped.endTs - clamped.startTs) / BUCKET_SPAN_MS[bucket]) + 1;
|
|
6904
7869
|
if (projected > MAX_TIMESERIES_BUCKETS) {
|
|
6905
|
-
return
|
|
7870
|
+
return err7(
|
|
6906
7871
|
400,
|
|
6907
7872
|
`requested range projects ~${projected} '${bucket}' buckets (max ${MAX_TIMESERIES_BUCKETS}); narrow the range or use a coarser bucket`
|
|
6908
7873
|
);
|
|
@@ -6925,7 +7890,7 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
6925
7890
|
};
|
|
6926
7891
|
}
|
|
6927
7892
|
default:
|
|
6928
|
-
return
|
|
7893
|
+
return err7(404, `unknown usage view '${view ?? ""}'`);
|
|
6929
7894
|
}
|
|
6930
7895
|
}
|
|
6931
7896
|
function poolKeyLabels(cfg) {
|
|
@@ -6974,7 +7939,7 @@ async function handlePricingList(deps) {
|
|
|
6974
7939
|
async function handlePricingUpsert(body, deps) {
|
|
6975
7940
|
const input = parsePricingEntryInput(body);
|
|
6976
7941
|
if (!input) {
|
|
6977
|
-
return
|
|
7942
|
+
return err7(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
|
|
6978
7943
|
}
|
|
6979
7944
|
const entry = await deps.pricingEngine.upsertManual(input);
|
|
6980
7945
|
return { status: 200, body: { entry } };
|
|
@@ -6983,7 +7948,7 @@ async function handlePricingDelete(query2, deps) {
|
|
|
6983
7948
|
const providerId = query2.get("providerId")?.trim() ?? "";
|
|
6984
7949
|
const modelId = query2.get("modelId")?.trim() ?? "";
|
|
6985
7950
|
if (!providerId || !modelId) {
|
|
6986
|
-
return
|
|
7951
|
+
return err7(400, "delete requires providerId and modelId query params");
|
|
6987
7952
|
}
|
|
6988
7953
|
const deleted = await deps.pricingStore.delete(providerId, modelId);
|
|
6989
7954
|
if (deleted) await deps.pricingEngine.invalidateCache();
|
|
@@ -7003,13 +7968,13 @@ async function handlePricingFetchLatest(deps) {
|
|
|
7003
7968
|
}
|
|
7004
7969
|
};
|
|
7005
7970
|
} catch (e) {
|
|
7006
|
-
return
|
|
7971
|
+
return err7(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
7007
7972
|
}
|
|
7008
7973
|
}
|
|
7009
7974
|
async function handlePricingResolveConflicts(body, deps) {
|
|
7010
7975
|
const raw = body["resolutions"];
|
|
7011
7976
|
if (!Array.isArray(raw)) {
|
|
7012
|
-
return
|
|
7977
|
+
return err7(400, "resolve-conflicts requires { resolutions: [...] }");
|
|
7013
7978
|
}
|
|
7014
7979
|
const currentRows = await deps.pricingStore.getAll();
|
|
7015
7980
|
const userEditedKeys = new Set(
|
|
@@ -7019,21 +7984,21 @@ async function handlePricingResolveConflicts(body, deps) {
|
|
|
7019
7984
|
const pendingIncoming = /* @__PURE__ */ new Map();
|
|
7020
7985
|
let staleCount = 0;
|
|
7021
7986
|
for (const item of raw) {
|
|
7022
|
-
if (!item || typeof item !== "object") return
|
|
7987
|
+
if (!item || typeof item !== "object") return err7(400, "invalid resolution entry");
|
|
7023
7988
|
const r = item;
|
|
7024
7989
|
const action = r["action"];
|
|
7025
7990
|
if (action !== "overwrite" && action !== "skip") {
|
|
7026
|
-
return
|
|
7991
|
+
return err7(400, "resolution action must be 'overwrite' or 'skip'");
|
|
7027
7992
|
}
|
|
7028
7993
|
const providerId = typeof r["providerId"] === "string" && r["providerId"].trim() ? r["providerId"].trim() : "";
|
|
7029
7994
|
const modelId = typeof r["modelId"] === "string" && r["modelId"].trim() ? r["modelId"].trim() : "";
|
|
7030
7995
|
if (!providerId || !modelId) {
|
|
7031
|
-
return
|
|
7996
|
+
return err7(400, "each resolution requires top-level providerId and modelId");
|
|
7032
7997
|
}
|
|
7033
7998
|
const incoming = parsePricingEntryInput(r["incoming"]);
|
|
7034
|
-
if (!incoming) return
|
|
7999
|
+
if (!incoming) return err7(400, "each resolution must echo a valid incoming pricing entry");
|
|
7035
8000
|
if (incoming.providerId !== providerId || incoming.modelId !== modelId) {
|
|
7036
|
-
return
|
|
8001
|
+
return err7(400, "resolution providerId/modelId must match the echoed incoming entry");
|
|
7037
8002
|
}
|
|
7038
8003
|
const key = `${providerId}::${modelId}`;
|
|
7039
8004
|
if (action === "overwrite" && !userEditedKeys.has(key)) {
|
|
@@ -7078,7 +8043,7 @@ function query(req) {
|
|
|
7078
8043
|
}
|
|
7079
8044
|
function allowanceProvider(value) {
|
|
7080
8045
|
if (!value) return void 0;
|
|
7081
|
-
return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" ? value : null;
|
|
8046
|
+
return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" || value === "grok" || value === "copilot" || value === "gemini" ? value : null;
|
|
7082
8047
|
}
|
|
7083
8048
|
async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
7084
8049
|
if (!service) return writeError2(res, 501, "account allowance service is not available");
|
|
@@ -7093,7 +8058,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7093
8058
|
const pathProvider = rest.length >= 2 ? rest[0] : null;
|
|
7094
8059
|
const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
|
|
7095
8060
|
if (providerId === null) {
|
|
7096
|
-
return writeError2(res, 400, "providerId must be claude, codex, kimi, or
|
|
8061
|
+
return writeError2(res, 400, "providerId must be claude, codex, kimi, opencodego, grok, copilot, or gemini");
|
|
7097
8062
|
}
|
|
7098
8063
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
7099
8064
|
const allowances = await service.list({ providerId, accountId });
|
|
@@ -7135,6 +8100,36 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7135
8100
|
}
|
|
7136
8101
|
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7137
8102
|
}
|
|
8103
|
+
if (requestedProvider === "copilot") {
|
|
8104
|
+
if (!service.refreshCopilot) {
|
|
8105
|
+
return writeError2(res, 501, "copilot allowance refresh is not available");
|
|
8106
|
+
}
|
|
8107
|
+
const allowances2 = await service.refreshCopilot(accountId);
|
|
8108
|
+
if (accountId && allowances2.length === 0) {
|
|
8109
|
+
return writeError2(res, 404, `Copilot account '${accountId}' not found`);
|
|
8110
|
+
}
|
|
8111
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
8112
|
+
}
|
|
8113
|
+
if (requestedProvider === "grok") {
|
|
8114
|
+
if (!service.refreshGrok) {
|
|
8115
|
+
return writeError2(res, 501, "grok allowance refresh is not available");
|
|
8116
|
+
}
|
|
8117
|
+
const allowances2 = await service.refreshGrok(accountId);
|
|
8118
|
+
if (accountId && allowances2.length === 0) {
|
|
8119
|
+
return writeError2(res, 404, `Grok account '${accountId}' not found`);
|
|
8120
|
+
}
|
|
8121
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
8122
|
+
}
|
|
8123
|
+
if (requestedProvider === "gemini") {
|
|
8124
|
+
if (!service.refreshGemini) {
|
|
8125
|
+
return writeError2(res, 501, "gemini allowance refresh is not available");
|
|
8126
|
+
}
|
|
8127
|
+
const allowances2 = await service.refreshGemini(accountId);
|
|
8128
|
+
if (accountId && allowances2.length === 0) {
|
|
8129
|
+
return writeError2(res, 404, `Gemini account '${accountId}' not found`);
|
|
8130
|
+
}
|
|
8131
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
8132
|
+
}
|
|
7138
8133
|
const allowances = await service.refreshClaude(accountId);
|
|
7139
8134
|
if (accountId && allowances.length === 0) {
|
|
7140
8135
|
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
@@ -7236,6 +8231,9 @@ function toProviderView(row) {
|
|
|
7236
8231
|
apiVersion: row.apiVersion,
|
|
7237
8232
|
maxConcurrency: row.maxConcurrency,
|
|
7238
8233
|
modelsEndpoint: row.modelsEndpoint,
|
|
8234
|
+
// Static extra headers round-trip VERBATIM (non-secret identity values;
|
|
8235
|
+
// auth/content names were already dropped at the write/load gate).
|
|
8236
|
+
extraHeaders: row.extraHeaders,
|
|
7239
8237
|
// app-parity child 5: transformer config round-trips VERBATIM (non-secret —
|
|
7240
8238
|
// transform-rule names + options, no key material; absent stays absent).
|
|
7241
8239
|
transformer: row.transformer,
|
|
@@ -7305,8 +8303,8 @@ async function handleAdminApi(req, res, path2, deps) {
|
|
|
7305
8303
|
default:
|
|
7306
8304
|
return writeJsonError(res, 404, `unknown admin resource '${resource}'`);
|
|
7307
8305
|
}
|
|
7308
|
-
} catch (
|
|
7309
|
-
writeJsonError(res, 500,
|
|
8306
|
+
} catch (err8) {
|
|
8307
|
+
writeJsonError(res, 500, err8 instanceof Error ? err8.message : String(err8));
|
|
7310
8308
|
}
|
|
7311
8309
|
}
|
|
7312
8310
|
function requestQuery(req) {
|
|
@@ -7464,6 +8462,9 @@ async function handleProviderReorder(req, res, cfg, deps) {
|
|
|
7464
8462
|
persistProviders(cfg, deps);
|
|
7465
8463
|
return writeJson4(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
|
|
7466
8464
|
}
|
|
8465
|
+
function expandRowExtraHeaders(row) {
|
|
8466
|
+
return mergeExtraHeaders({}, row.extraHeaders);
|
|
8467
|
+
}
|
|
7467
8468
|
async function handleDiscoverModels(res, id, cfg) {
|
|
7468
8469
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
7469
8470
|
const row = cfg.providers.find((p) => p.id === id);
|
|
@@ -7477,7 +8478,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
7477
8478
|
try {
|
|
7478
8479
|
const headers = { Accept: "application/json" };
|
|
7479
8480
|
if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
|
|
7480
|
-
|
|
8481
|
+
Object.assign(headers, expandRowExtraHeaders(row));
|
|
8482
|
+
const response = await fetchUpstream8(url, { method: "GET", headers }, { providerId: "byo" });
|
|
7481
8483
|
if (!response.ok) {
|
|
7482
8484
|
const text = await response.text().catch(() => "");
|
|
7483
8485
|
let message = text.slice(0, 300);
|
|
@@ -7494,8 +8496,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
7494
8496
|
const data = await response.json();
|
|
7495
8497
|
const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
|
|
7496
8498
|
return writeJson4(res, 200, { models });
|
|
7497
|
-
} catch (
|
|
7498
|
-
const message =
|
|
8499
|
+
} catch (err8) {
|
|
8500
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
7499
8501
|
return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
|
|
7500
8502
|
}
|
|
7501
8503
|
}
|
|
@@ -7534,9 +8536,10 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
7534
8536
|
messages: [{ role: "user", content: prompt }]
|
|
7535
8537
|
};
|
|
7536
8538
|
}
|
|
8539
|
+
Object.assign(headers, expandRowExtraHeaders(row));
|
|
7537
8540
|
const startedAt = Date.now();
|
|
7538
8541
|
try {
|
|
7539
|
-
const response = await
|
|
8542
|
+
const response = await fetchUpstream8(
|
|
7540
8543
|
url,
|
|
7541
8544
|
{ method: "POST", headers, body: JSON.stringify(payload) },
|
|
7542
8545
|
{ providerId: "byo" }
|
|
@@ -7558,8 +8561,8 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
7558
8561
|
latencyMs,
|
|
7559
8562
|
sample: extractSampleText(text, row.apiFormat)
|
|
7560
8563
|
});
|
|
7561
|
-
} catch (
|
|
7562
|
-
const message =
|
|
8564
|
+
} catch (err8) {
|
|
8565
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
7563
8566
|
return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
|
|
7564
8567
|
}
|
|
7565
8568
|
}
|
|
@@ -7841,6 +8844,7 @@ function parseProviderInput(body, existing) {
|
|
|
7841
8844
|
const apiVersion = typeof body["apiVersion"] === "string" && body["apiVersion"].length > 0 ? body["apiVersion"] : body["apiVersion"] === null ? void 0 : existing?.apiVersion;
|
|
7842
8845
|
const modelsEndpoint = typeof body["modelsEndpoint"] === "string" && body["modelsEndpoint"].length > 0 ? body["modelsEndpoint"] : body["modelsEndpoint"] === null ? void 0 : existing?.modelsEndpoint;
|
|
7843
8846
|
const maxConcurrency = typeof body["maxConcurrency"] === "number" && Number.isFinite(body["maxConcurrency"]) ? body["maxConcurrency"] : body["maxConcurrency"] === null ? void 0 : existing?.maxConcurrency;
|
|
8847
|
+
const extraHeaders = body["extraHeaders"] === null ? void 0 : body["extraHeaders"] === void 0 ? existing?.extraHeaders : validateExtraHeaders(body["extraHeaders"]);
|
|
7844
8848
|
const transformer = body["transformer"] === null ? void 0 : parseTransformerInput(body["transformer"], existing?.transformer);
|
|
7845
8849
|
const codingPlan = body["codingPlan"] === null ? void 0 : body["codingPlan"] === void 0 ? existing?.codingPlan : body["codingPlan"] && typeof body["codingPlan"] === "object" && !Array.isArray(body["codingPlan"]) ? parseCodingPlanInput(body["codingPlan"], existing?.codingPlan) : existing?.codingPlan;
|
|
7846
8850
|
const apiModes = body["apiModes"] === null ? void 0 : parseApiModesInput(body["apiModes"], existing?.apiModes);
|
|
@@ -7866,6 +8870,7 @@ function parseProviderInput(body, existing) {
|
|
|
7866
8870
|
apiVersion,
|
|
7867
8871
|
maxConcurrency,
|
|
7868
8872
|
modelsEndpoint,
|
|
8873
|
+
extraHeaders,
|
|
7869
8874
|
transformer: migrated.transformer,
|
|
7870
8875
|
codingPlan,
|
|
7871
8876
|
apiModes,
|
|
@@ -7887,7 +8892,10 @@ function handlePresets(res, method) {
|
|
|
7887
8892
|
description: p.description,
|
|
7888
8893
|
features: p.features,
|
|
7889
8894
|
website: p.website,
|
|
7890
|
-
modelsEndpoint: p.modelsEndpoint
|
|
8895
|
+
modelsEndpoint: p.modelsEndpoint,
|
|
8896
|
+
// Static extra headers ride along so `addFromPreset` can seed them onto the
|
|
8897
|
+
// row (the write gateway re-validates via the shared allowlist).
|
|
8898
|
+
extraHeaders: p.extraHeaders
|
|
7891
8899
|
}));
|
|
7892
8900
|
return writeJson4(res, 200, { presets, excluded });
|
|
7893
8901
|
}
|
|
@@ -8369,12 +9377,12 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
8369
9377
|
}
|
|
8370
9378
|
return writeJson4(res, 200, { ok: true, affected: result.affected });
|
|
8371
9379
|
}
|
|
8372
|
-
if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi") && rest[1] === "oauth" && rest[3] === "status") {
|
|
8373
|
-
const result = rest[0] === "codex" ? handleCodexOAuthStatus(rest[2], deps) : handleKimiOAuthStatus(rest[2], deps);
|
|
9380
|
+
if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[3] === "status") {
|
|
9381
|
+
const result = rest[0] === "codex" ? handleCodexOAuthStatus(rest[2], deps) : rest[0] === "kimi" ? handleKimiOAuthStatus(rest[2], deps) : rest[0] === "grok" ? handleGrokOAuthStatus(rest[2], deps) : handleCopilotOAuthStatus(rest[2], deps);
|
|
8374
9382
|
return writeJson4(res, result.status, result.body);
|
|
8375
9383
|
}
|
|
8376
|
-
if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi") && rest[1] === "oauth" && rest[2]) {
|
|
8377
|
-
const result = rest[0] === "codex" ? handleCodexOAuthCancel(rest[2], deps) : handleKimiOAuthCancel(rest[2], deps);
|
|
9384
|
+
if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[2]) {
|
|
9385
|
+
const result = rest[0] === "codex" ? handleCodexOAuthCancel(rest[2], deps) : rest[0] === "kimi" ? handleKimiOAuthCancel(rest[2], deps) : rest[0] === "grok" ? handleGrokOAuthCancel(rest[2], deps) : handleCopilotOAuthCancel(rest[2], deps);
|
|
8378
9386
|
return writeJson4(res, result.status, result.body);
|
|
8379
9387
|
}
|
|
8380
9388
|
if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
|
|
@@ -8435,6 +9443,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
8435
9443
|
const result2 = await handleKimiOAuthStart(deps);
|
|
8436
9444
|
return writeJson4(res, result2.status, result2.body);
|
|
8437
9445
|
}
|
|
9446
|
+
if (providerId === "grok") {
|
|
9447
|
+
const result2 = await handleGrokOAuthStart(deps);
|
|
9448
|
+
return writeJson4(res, result2.status, result2.body);
|
|
9449
|
+
}
|
|
9450
|
+
if (providerId === "copilot") {
|
|
9451
|
+
const body2 = await readJsonBody4(req);
|
|
9452
|
+
const result2 = await handleCopilotOAuthStart(deps, body2["enterpriseUrl"]);
|
|
9453
|
+
return writeJson4(res, result2.status, result2.body);
|
|
9454
|
+
}
|
|
8438
9455
|
const result = handleOAuthStart(providerId, deps);
|
|
8439
9456
|
return writeJson4(res, result.status, result.body);
|
|
8440
9457
|
}
|
|
@@ -8929,12 +9946,12 @@ async function handlePlayground(req, res, method, deps) {
|
|
|
8929
9946
|
const payload = body["body"];
|
|
8930
9947
|
const status = deps.outboundApiServer.getStatus();
|
|
8931
9948
|
if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
|
|
8932
|
-
const path2 = resolvePlaygroundPath(endpoint,
|
|
9949
|
+
const path2 = resolvePlaygroundPath(endpoint, isRecord7(payload) ? payload : {});
|
|
8933
9950
|
if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
|
|
8934
9951
|
const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
|
|
8935
9952
|
await proxyToOutbound(res, status.port, path2, key, upstreamBody);
|
|
8936
9953
|
}
|
|
8937
|
-
function
|
|
9954
|
+
function isRecord7(v) {
|
|
8938
9955
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
8939
9956
|
}
|
|
8940
9957
|
function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
@@ -8963,8 +9980,8 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
|
8963
9980
|
});
|
|
8964
9981
|
}
|
|
8965
9982
|
);
|
|
8966
|
-
upstream.on("error", (
|
|
8967
|
-
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${
|
|
9983
|
+
upstream.on("error", (err8) => {
|
|
9984
|
+
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err8.message}`);
|
|
8968
9985
|
else res.end();
|
|
8969
9986
|
resolve11();
|
|
8970
9987
|
});
|
|
@@ -9069,7 +10086,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
9069
10086
|
}
|
|
9070
10087
|
|
|
9071
10088
|
// src/admin/version.ts
|
|
9072
|
-
var DAEMON_VERSION = true ? "0.
|
|
10089
|
+
var DAEMON_VERSION = true ? "0.4.1" : "0.0.0-dev";
|
|
9073
10090
|
|
|
9074
10091
|
// src/admin/AdminServer.ts
|
|
9075
10092
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -9112,13 +10129,13 @@ var AdminServer = class {
|
|
|
9112
10129
|
const server = http2.createServer((req, res) => {
|
|
9113
10130
|
this.onRequest(req, res);
|
|
9114
10131
|
});
|
|
9115
|
-
const onError = (
|
|
9116
|
-
if (
|
|
10132
|
+
const onError = (err8) => {
|
|
10133
|
+
if (err8.code === "EADDRINUSE" && port !== 0) {
|
|
9117
10134
|
server.removeListener("error", onError);
|
|
9118
10135
|
this.listen(bindAddr, 0).then(resolve11, reject);
|
|
9119
10136
|
return;
|
|
9120
10137
|
}
|
|
9121
|
-
reject(
|
|
10138
|
+
reject(err8);
|
|
9122
10139
|
};
|
|
9123
10140
|
server.on("error", onError);
|
|
9124
10141
|
server.listen(port, bindAddr, () => {
|
|
@@ -9136,8 +10153,8 @@ var AdminServer = class {
|
|
|
9136
10153
|
}
|
|
9137
10154
|
/** Per-request handler: auth gate (when a token is set) → routing. */
|
|
9138
10155
|
onRequest(req, res) {
|
|
9139
|
-
void this.dispatch(req, res).catch((
|
|
9140
|
-
const message =
|
|
10156
|
+
void this.dispatch(req, res).catch((err8) => {
|
|
10157
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
9141
10158
|
this.deps.logger.error("[AdminServer] unhandled error:", message);
|
|
9142
10159
|
if (!res.headersSent) {
|
|
9143
10160
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
@@ -9401,18 +10418,18 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
9401
10418
|
return;
|
|
9402
10419
|
}
|
|
9403
10420
|
signal?.addEventListener("abort", abort, { once: true });
|
|
9404
|
-
server.on("error", (
|
|
10421
|
+
server.on("error", (err8) => {
|
|
9405
10422
|
if (settled) return;
|
|
9406
10423
|
settled = true;
|
|
9407
10424
|
clearTimeout(timer);
|
|
9408
|
-
if (
|
|
10425
|
+
if (err8.code === "EADDRINUSE") {
|
|
9409
10426
|
reject(
|
|
9410
10427
|
new Error(
|
|
9411
10428
|
`login: cannot bind ${LOOPBACK_HOST}:${LOOPBACK_PORT} (address in use) \u2014 another codex login or process is holding the port`
|
|
9412
10429
|
)
|
|
9413
10430
|
);
|
|
9414
10431
|
} else {
|
|
9415
|
-
reject(
|
|
10432
|
+
reject(err8);
|
|
9416
10433
|
}
|
|
9417
10434
|
});
|
|
9418
10435
|
const timer = setTimeout(() => {
|
|
@@ -9488,21 +10505,22 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
|
|
|
9488
10505
|
}
|
|
9489
10506
|
|
|
9490
10507
|
// src/allowance/ProviderKeyQuotaService.ts
|
|
9491
|
-
import {
|
|
10508
|
+
import { mergeExtraHeaders as mergeExtraHeaders2 } from "@omnicross/core";
|
|
10509
|
+
import { fetchUpstream as fetchUpstream9 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
9492
10510
|
|
|
9493
10511
|
// src/allowance/ProviderKeyQuota.ts
|
|
9494
|
-
var
|
|
9495
|
-
var HOUR_MS2 = 60 *
|
|
9496
|
-
var
|
|
9497
|
-
var WEEK_MS = 7 *
|
|
9498
|
-
var MONTH_MS = 30 *
|
|
9499
|
-
function
|
|
10512
|
+
var MINUTE_MS3 = 6e4;
|
|
10513
|
+
var HOUR_MS2 = 60 * MINUTE_MS3;
|
|
10514
|
+
var DAY_MS3 = 24 * HOUR_MS2;
|
|
10515
|
+
var WEEK_MS = 7 * DAY_MS3;
|
|
10516
|
+
var MONTH_MS = 30 * DAY_MS3;
|
|
10517
|
+
function finiteNumber5(value) {
|
|
9500
10518
|
if (value === null || value === void 0 || value === "") return void 0;
|
|
9501
10519
|
const parsed = typeof value === "number" ? value : Number(value);
|
|
9502
10520
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
9503
10521
|
}
|
|
9504
10522
|
function finitePercent4(value) {
|
|
9505
|
-
const parsed =
|
|
10523
|
+
const parsed = finiteNumber5(value);
|
|
9506
10524
|
return parsed !== void 0 && parsed <= 100 ? parsed : null;
|
|
9507
10525
|
}
|
|
9508
10526
|
function isoInstant3(value) {
|
|
@@ -9510,18 +10528,18 @@ function isoInstant3(value) {
|
|
|
9510
10528
|
const time = Date.parse(value);
|
|
9511
10529
|
if (Number.isFinite(time)) return new Date(time).toISOString();
|
|
9512
10530
|
}
|
|
9513
|
-
const numeric =
|
|
10531
|
+
const numeric = finiteNumber5(value);
|
|
9514
10532
|
if (numeric !== void 0 && numeric > 1e9) {
|
|
9515
10533
|
const ms = numeric > 1e12 ? numeric : numeric * 1e3;
|
|
9516
10534
|
return new Date(ms).toISOString();
|
|
9517
10535
|
}
|
|
9518
10536
|
return void 0;
|
|
9519
10537
|
}
|
|
9520
|
-
function
|
|
10538
|
+
function secondsUntil8(instant, now) {
|
|
9521
10539
|
if (!instant) return void 0;
|
|
9522
10540
|
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
9523
10541
|
}
|
|
9524
|
-
function
|
|
10542
|
+
function isRecord8(value) {
|
|
9525
10543
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
9526
10544
|
}
|
|
9527
10545
|
function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
@@ -9534,7 +10552,7 @@ function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
|
9534
10552
|
}
|
|
9535
10553
|
const host = url.hostname.toLowerCase();
|
|
9536
10554
|
const path2 = url.pathname.toLowerCase();
|
|
9537
|
-
if ((host === "api.z.ai" || host === "open.bigmodel.cn") && path2.includes("/coding")) {
|
|
10555
|
+
if ((host === "api.z.ai" || host === "open.bigmodel.cn") && (path2.includes("/coding") || path2.includes("/anthropic"))) {
|
|
9538
10556
|
return "zai";
|
|
9539
10557
|
}
|
|
9540
10558
|
if ((host === "api.minimax.io" || host === "api.minimaxi.com") && // Token Plan rides the plain openai `/v1` (chat completions) surface; the
|
|
@@ -9544,6 +10562,7 @@ function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
|
9544
10562
|
}
|
|
9545
10563
|
if (host === "api.code.umans.ai") return "umans";
|
|
9546
10564
|
if (host === "api.synthetic.new") return "synthetic";
|
|
10565
|
+
if (host === "api.cline.bot") return "cline-pass";
|
|
9547
10566
|
return null;
|
|
9548
10567
|
}
|
|
9549
10568
|
function providerKeyQuotaUrl(adapter, baseUrl) {
|
|
@@ -9551,6 +10570,7 @@ function providerKeyQuotaUrl(adapter, baseUrl) {
|
|
|
9551
10570
|
if (adapter === "zai") return `${origin}/api/monitor/usage/quota/limit`;
|
|
9552
10571
|
if (adapter === "minimax-token-plan") return `${origin}/v1/token_plan/remains`;
|
|
9553
10572
|
if (adapter === "umans") return `${origin}/v1/usage`;
|
|
10573
|
+
if (adapter === "cline-pass") return `${origin}/api/v1/users/me/plan/usage-limits`;
|
|
9554
10574
|
return `${origin}/v2/quotas`;
|
|
9555
10575
|
}
|
|
9556
10576
|
function providerKeyQuotaAuthHeader(adapter, key) {
|
|
@@ -9562,7 +10582,7 @@ function zaiWindowDurationMs(item) {
|
|
|
9562
10582
|
case 3:
|
|
9563
10583
|
return count * HOUR_MS2;
|
|
9564
10584
|
case 4:
|
|
9565
|
-
return count *
|
|
10585
|
+
return count * DAY_MS3;
|
|
9566
10586
|
case 5:
|
|
9567
10587
|
return count * MONTH_MS;
|
|
9568
10588
|
case 6:
|
|
@@ -9575,8 +10595,8 @@ function zaiWindowIdLabel(durationMs) {
|
|
|
9575
10595
|
if (durationMs === WEEK_MS) return { id: "seven-day", label: "7 days" };
|
|
9576
10596
|
if (durationMs === 5 * HOUR_MS2) return { id: "five-hour", label: "5 hours" };
|
|
9577
10597
|
if (durationMs === MONTH_MS) return { id: "thirty-day", label: "30 days" };
|
|
9578
|
-
if (durationMs !== void 0 && durationMs %
|
|
9579
|
-
const days = durationMs /
|
|
10598
|
+
if (durationMs !== void 0 && durationMs % DAY_MS3 === 0) {
|
|
10599
|
+
const days = durationMs / DAY_MS3;
|
|
9580
10600
|
return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}` };
|
|
9581
10601
|
}
|
|
9582
10602
|
if (durationMs !== void 0 && durationMs % HOUR_MS2 === 0) {
|
|
@@ -9586,23 +10606,23 @@ function zaiWindowIdLabel(durationMs) {
|
|
|
9586
10606
|
return { id: "quota", label: "Quota" };
|
|
9587
10607
|
}
|
|
9588
10608
|
function parseZaiQuotaPayload(payload, now) {
|
|
9589
|
-
if (!
|
|
9590
|
-
const data =
|
|
10609
|
+
if (!isRecord8(payload)) return null;
|
|
10610
|
+
const data = isRecord8(payload["data"]) ? payload["data"] : payload;
|
|
9591
10611
|
if (payload["success"] === false) return null;
|
|
9592
10612
|
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
9593
10613
|
const byWindow = /* @__PURE__ */ new Map();
|
|
9594
10614
|
for (const raw of limits) {
|
|
9595
|
-
if (!
|
|
10615
|
+
if (!isRecord8(raw)) continue;
|
|
9596
10616
|
const item = raw;
|
|
9597
10617
|
if (item.type === void 0) continue;
|
|
9598
10618
|
const details = raw["usageDetails"];
|
|
9599
|
-
if (Array.isArray(details) && details.some((d) =>
|
|
10619
|
+
if (Array.isArray(details) && details.some((d) => isRecord8(d) && d["modelCode"] === "zread")) {
|
|
9600
10620
|
continue;
|
|
9601
10621
|
}
|
|
9602
10622
|
const durationMs = zaiWindowDurationMs(item);
|
|
9603
10623
|
const { id, label } = zaiWindowIdLabel(durationMs);
|
|
9604
|
-
const limit =
|
|
9605
|
-
const used =
|
|
10624
|
+
const limit = finiteNumber5(item.usage);
|
|
10625
|
+
const used = finiteNumber5(item.currentValue);
|
|
9606
10626
|
const fromAbsolute = limit !== void 0 && used !== void 0 && limit > 0 ? Math.min(100, used / limit * 100) : void 0;
|
|
9607
10627
|
const fromPercentage = finitePercent4(item.percentage) ?? void 0;
|
|
9608
10628
|
const usedPercent = fromAbsolute !== void 0 ? Math.round(fromAbsolute * 10) / 10 : fromPercentage;
|
|
@@ -9613,9 +10633,9 @@ function parseZaiQuotaPayload(payload, now) {
|
|
|
9613
10633
|
label,
|
|
9614
10634
|
scope: "all",
|
|
9615
10635
|
usedPercent,
|
|
9616
|
-
...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs /
|
|
10636
|
+
...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs / MINUTE_MS3) } : {},
|
|
9617
10637
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9618
|
-
remainingSeconds:
|
|
10638
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
9619
10639
|
state: "fresh"
|
|
9620
10640
|
};
|
|
9621
10641
|
const existing = byWindow.get(id);
|
|
@@ -9629,21 +10649,21 @@ function parseZaiQuotaPayload(payload, now) {
|
|
|
9629
10649
|
var MINIMAX_STATUS_EXHAUSTED = 2;
|
|
9630
10650
|
var MINIMAX_SHARED_BUCKET = "general";
|
|
9631
10651
|
function parseMiniMaxBucket(value) {
|
|
9632
|
-
if (!
|
|
10652
|
+
if (!isRecord8(value)) return null;
|
|
9633
10653
|
const modelName = typeof value["model_name"] === "string" ? value["model_name"].trim() : "";
|
|
9634
10654
|
if (!modelName) return null;
|
|
9635
10655
|
const instant = (v) => {
|
|
9636
|
-
const n =
|
|
10656
|
+
const n = finiteNumber5(v);
|
|
9637
10657
|
return n !== void 0 && n > 1e9 ? n > 1e12 ? n : n * 1e3 : void 0;
|
|
9638
10658
|
};
|
|
9639
10659
|
return {
|
|
9640
10660
|
modelName,
|
|
9641
10661
|
intervalEnd: instant(value["end_time"]),
|
|
9642
|
-
intervalRemainingPercent:
|
|
9643
|
-
intervalStatus:
|
|
10662
|
+
intervalRemainingPercent: finiteNumber5(value["current_interval_remaining_percent"]),
|
|
10663
|
+
intervalStatus: finiteNumber5(value["current_interval_status"]),
|
|
9644
10664
|
weeklyEnd: instant(value["weekly_end_time"]),
|
|
9645
|
-
weeklyRemainingPercent:
|
|
9646
|
-
weeklyStatus:
|
|
10665
|
+
weeklyRemainingPercent: finiteNumber5(value["current_weekly_remaining_percent"]),
|
|
10666
|
+
weeklyStatus: finiteNumber5(value["current_weekly_status"])
|
|
9647
10667
|
};
|
|
9648
10668
|
}
|
|
9649
10669
|
function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, status, now) {
|
|
@@ -9656,14 +10676,14 @@ function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, s
|
|
|
9656
10676
|
usedPercent,
|
|
9657
10677
|
...windowMinutes !== void 0 ? { windowMinutes } : {},
|
|
9658
10678
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9659
|
-
remainingSeconds:
|
|
10679
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
9660
10680
|
state: usedPercent !== null ? "fresh" : "unavailable"
|
|
9661
10681
|
};
|
|
9662
10682
|
}
|
|
9663
10683
|
function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
9664
|
-
if (!
|
|
10684
|
+
if (!isRecord8(payload)) return null;
|
|
9665
10685
|
const baseResp = payload["base_resp"];
|
|
9666
|
-
if (!
|
|
10686
|
+
if (!isRecord8(baseResp) || baseResp["status_code"] !== 0) return null;
|
|
9667
10687
|
const buckets = Array.isArray(payload["model_remains"]) ? payload["model_remains"] : [];
|
|
9668
10688
|
let general = null;
|
|
9669
10689
|
for (const raw of buckets) {
|
|
@@ -9687,7 +10707,7 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
|
9687
10707
|
minimaxWindow(
|
|
9688
10708
|
"seven-day",
|
|
9689
10709
|
"7 days",
|
|
9690
|
-
Math.round(WEEK_MS /
|
|
10710
|
+
Math.round(WEEK_MS / MINUTE_MS3),
|
|
9691
10711
|
general.weeklyEnd,
|
|
9692
10712
|
general.weeklyRemainingPercent,
|
|
9693
10713
|
general.weeklyStatus,
|
|
@@ -9696,15 +10716,15 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
|
9696
10716
|
];
|
|
9697
10717
|
}
|
|
9698
10718
|
function parseUmansUsagePayload(payload, now) {
|
|
9699
|
-
if (!
|
|
9700
|
-
const limits =
|
|
9701
|
-
const requests = limits &&
|
|
9702
|
-
const usage =
|
|
9703
|
-
const window =
|
|
9704
|
-
const hardCap =
|
|
9705
|
-
const softLimit =
|
|
9706
|
-
const requestsInWindow =
|
|
9707
|
-
const weightedInWindow =
|
|
10719
|
+
if (!isRecord8(payload)) return null;
|
|
10720
|
+
const limits = isRecord8(payload["limits"]) ? payload["limits"] : void 0;
|
|
10721
|
+
const requests = limits && isRecord8(limits["requests"]) ? limits["requests"] : void 0;
|
|
10722
|
+
const usage = isRecord8(payload["usage"]) ? payload["usage"] : void 0;
|
|
10723
|
+
const window = isRecord8(payload["window"]) ? payload["window"] : void 0;
|
|
10724
|
+
const hardCap = finiteNumber5(requests?.["hard_cap"]);
|
|
10725
|
+
const softLimit = finiteNumber5(requests?.["limit"]);
|
|
10726
|
+
const requestsInWindow = finiteNumber5(usage?.["requests_in_window"]);
|
|
10727
|
+
const weightedInWindow = finiteNumber5(usage?.["weighted_in_window"]);
|
|
9708
10728
|
const resetsAt = isoInstant3(window?.["resets_at"]);
|
|
9709
10729
|
let usedPercent = null;
|
|
9710
10730
|
if (hardCap !== void 0 && hardCap > 0 && requestsInWindow !== void 0) {
|
|
@@ -9721,19 +10741,19 @@ function parseUmansUsagePayload(payload, now) {
|
|
|
9721
10741
|
usedPercent,
|
|
9722
10742
|
windowMinutes: 5 * 60,
|
|
9723
10743
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9724
|
-
remainingSeconds:
|
|
10744
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
9725
10745
|
state: "fresh"
|
|
9726
10746
|
}
|
|
9727
10747
|
];
|
|
9728
10748
|
}
|
|
9729
10749
|
function parseSyntheticQuotasPayload(payload, now) {
|
|
9730
|
-
if (!
|
|
9731
|
-
const fiveHour =
|
|
9732
|
-
const weekly =
|
|
10750
|
+
if (!isRecord8(payload)) return null;
|
|
10751
|
+
const fiveHour = isRecord8(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
|
|
10752
|
+
const weekly = isRecord8(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
|
|
9733
10753
|
const windows = [];
|
|
9734
10754
|
if (fiveHour) {
|
|
9735
|
-
const max =
|
|
9736
|
-
const remaining =
|
|
10755
|
+
const max = finiteNumber5(fiveHour["max"]);
|
|
10756
|
+
const remaining = finiteNumber5(fiveHour["remaining"]);
|
|
9737
10757
|
const usedPercent = max !== void 0 && max > 0 && remaining !== void 0 ? Math.round(Math.min(100, (max - remaining) / max * 100) * 10) / 10 : null;
|
|
9738
10758
|
const resetsAt = isoInstant3(fiveHour["nextTickAt"]);
|
|
9739
10759
|
windows.push({
|
|
@@ -9743,12 +10763,12 @@ function parseSyntheticQuotasPayload(payload, now) {
|
|
|
9743
10763
|
usedPercent,
|
|
9744
10764
|
windowMinutes: 5 * 60,
|
|
9745
10765
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9746
|
-
remainingSeconds:
|
|
10766
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
9747
10767
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
9748
10768
|
});
|
|
9749
10769
|
}
|
|
9750
10770
|
if (weekly) {
|
|
9751
|
-
const percentRemaining =
|
|
10771
|
+
const percentRemaining = finiteNumber5(weekly["percentRemaining"]);
|
|
9752
10772
|
const usedPercent = percentRemaining !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - percentRemaining)) * 10) / 10 : null;
|
|
9753
10773
|
const resetsAt = isoInstant3(weekly["nextRegenAt"]);
|
|
9754
10774
|
windows.push({
|
|
@@ -9758,12 +10778,42 @@ function parseSyntheticQuotasPayload(payload, now) {
|
|
|
9758
10778
|
usedPercent,
|
|
9759
10779
|
windowMinutes: 7 * 24 * 60,
|
|
9760
10780
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9761
|
-
remainingSeconds:
|
|
10781
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
9762
10782
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
9763
10783
|
});
|
|
9764
10784
|
}
|
|
9765
10785
|
return windows.length > 0 ? windows : null;
|
|
9766
10786
|
}
|
|
10787
|
+
var CLINE_WINDOW_CONFIG = {
|
|
10788
|
+
five_hour: { id: "five-hour", label: "5 hours", minutes: 5 * 60 },
|
|
10789
|
+
weekly: { id: "seven-day", label: "7 days", minutes: 7 * 24 * 60 },
|
|
10790
|
+
monthly: { id: "thirty-day", label: "30 days", minutes: 30 * 24 * 60 }
|
|
10791
|
+
};
|
|
10792
|
+
function parseClinePassUsageLimitsPayload(payload, now) {
|
|
10793
|
+
if (!isRecord8(payload)) return null;
|
|
10794
|
+
const data = isRecord8(payload["data"]) ? payload["data"] : payload;
|
|
10795
|
+
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
10796
|
+
const windows = [];
|
|
10797
|
+
for (const raw of limits) {
|
|
10798
|
+
if (!isRecord8(raw)) continue;
|
|
10799
|
+
const config = CLINE_WINDOW_CONFIG[typeof raw["type"] === "string" ? raw["type"] : ""];
|
|
10800
|
+
if (!config) continue;
|
|
10801
|
+
const usedPercent = finitePercent4(raw["percentUsed"]);
|
|
10802
|
+
if (usedPercent === null) continue;
|
|
10803
|
+
const resetsAt = isoInstant3(raw["resetsAt"]);
|
|
10804
|
+
windows.push({
|
|
10805
|
+
id: config.id,
|
|
10806
|
+
label: config.label,
|
|
10807
|
+
scope: "all",
|
|
10808
|
+
usedPercent,
|
|
10809
|
+
windowMinutes: config.minutes,
|
|
10810
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
10811
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
10812
|
+
state: "fresh"
|
|
10813
|
+
});
|
|
10814
|
+
}
|
|
10815
|
+
return windows.length > 0 ? windows : null;
|
|
10816
|
+
}
|
|
9767
10817
|
|
|
9768
10818
|
// src/allowance/ProviderKeyQuotaService.ts
|
|
9769
10819
|
function parseQuotaPayload(adapter, payload, now) {
|
|
@@ -9776,6 +10826,8 @@ function parseQuotaPayload(adapter, payload, now) {
|
|
|
9776
10826
|
return parseUmansUsagePayload(payload, now);
|
|
9777
10827
|
case "synthetic":
|
|
9778
10828
|
return parseSyntheticQuotasPayload(payload, now);
|
|
10829
|
+
case "cline-pass":
|
|
10830
|
+
return parseClinePassUsageLimitsPayload(payload, now);
|
|
9779
10831
|
}
|
|
9780
10832
|
}
|
|
9781
10833
|
var PROVIDER_KEY_QUOTA_CACHE_MS = 5 * 6e4;
|
|
@@ -9795,7 +10847,7 @@ function rowKeyEntries(row) {
|
|
|
9795
10847
|
return [];
|
|
9796
10848
|
}
|
|
9797
10849
|
var ProviderKeyQuotaService = class {
|
|
9798
|
-
constructor(box, fetchImpl = (url, init) =>
|
|
10850
|
+
constructor(box, fetchImpl = (url, init) => fetchUpstream9(url, init, { redactBodies: true }), now = Date.now) {
|
|
9799
10851
|
this.box = box;
|
|
9800
10852
|
this.fetchImpl = fetchImpl;
|
|
9801
10853
|
this.now = now;
|
|
@@ -9857,7 +10909,10 @@ var ProviderKeyQuotaService = class {
|
|
|
9857
10909
|
headers: {
|
|
9858
10910
|
Authorization: providerKeyQuotaAuthHeader(adapter, key),
|
|
9859
10911
|
Accept: "application/json",
|
|
9860
|
-
"Content-Type": "application/json"
|
|
10912
|
+
"Content-Type": "application/json",
|
|
10913
|
+
// The row's static identity headers ride along — the Cline usage
|
|
10914
|
+
// endpoint sits behind the SAME client-identity 403 gate as inference.
|
|
10915
|
+
...mergeExtraHeaders2({}, row.extraHeaders)
|
|
9861
10916
|
},
|
|
9862
10917
|
signal: AbortSignal.timeout(15e3)
|
|
9863
10918
|
});
|
|
@@ -14629,6 +15684,10 @@ function toLLMProvider(row) {
|
|
|
14629
15684
|
// `parseProviderInput`), so customizations are preserved (the row value wins).
|
|
14630
15685
|
apiModes: row.apiModes,
|
|
14631
15686
|
selectedApiModeId: row.selectedApiModeId,
|
|
15687
|
+
// Static extra request headers ride along verbatim (load-guarded — no
|
|
15688
|
+
// auth/content names); core's `getProviderHeaders` merges them into every
|
|
15689
|
+
// BYO request, and the same-format relay path inherits that funnel.
|
|
15690
|
+
extraHeaders: row.extraHeaders,
|
|
14632
15691
|
// Official-Anthropic signature handling only matters for the Anthropic
|
|
14633
15692
|
// ingress (deferred → 502); leave it off for the BYO transform path.
|
|
14634
15693
|
isOfficial: false
|
|
@@ -16578,12 +17637,13 @@ import { existsSync as existsSync24, mkdirSync as mkdirSync6, readFileSync as re
|
|
|
16578
17637
|
import { dirname as dirname15 } from "path";
|
|
16579
17638
|
import { getSharedAccountHealth as getSharedAccountHealth2 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
16580
17639
|
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling3 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
16581
|
-
import { fetchUpstream as
|
|
17640
|
+
import { fetchUpstream as fetchUpstream10 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
16582
17641
|
import { getSharedIdentityStore as getSharedIdentityStore2 } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
|
|
16583
17642
|
import {
|
|
16584
17643
|
claudeOAuth as claudeOAuth2,
|
|
16585
17644
|
codexOAuth as codexOAuth2,
|
|
16586
17645
|
geminiOAuth as geminiOAuth2,
|
|
17646
|
+
grokOAuth as grokOAuth2,
|
|
16587
17647
|
kimiOAuth as kimiOAuth2
|
|
16588
17648
|
} from "@omnicross/subscriptions";
|
|
16589
17649
|
|
|
@@ -16733,7 +17793,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16733
17793
|
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
16734
17794
|
*/
|
|
16735
17795
|
buildRefreshFetch(providerId, accountId) {
|
|
16736
|
-
return this.fetchImpl ?? ((url, init) =>
|
|
17796
|
+
return this.fetchImpl ?? ((url, init) => fetchUpstream10(url, init, { providerId, accountId, redactBodies: true }));
|
|
16737
17797
|
}
|
|
16738
17798
|
/**
|
|
16739
17799
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -16774,7 +17834,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16774
17834
|
* other hot reads. Never returns token material.
|
|
16775
17835
|
*/
|
|
16776
17836
|
getAccountProxy(providerId, accountId) {
|
|
16777
|
-
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi") {
|
|
17837
|
+
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi" && providerId !== "grok" && providerId !== "copilot") {
|
|
16778
17838
|
return void 0;
|
|
16779
17839
|
}
|
|
16780
17840
|
return getAccountProxy(this.readConfig(), providerId, accountId);
|
|
@@ -16793,7 +17853,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16793
17853
|
const fingerprintOn = identityStore.isEnabled();
|
|
16794
17854
|
const now = Date.now();
|
|
16795
17855
|
const out = {};
|
|
16796
|
-
for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi"]) {
|
|
17856
|
+
for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi", "grok", "copilot"]) {
|
|
16797
17857
|
const sanitized = sanitizeAccounts(config, provider);
|
|
16798
17858
|
if (sanitized.length === 0) continue;
|
|
16799
17859
|
for (const account of sanitized) {
|
|
@@ -16992,6 +18052,66 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16992
18052
|
}
|
|
16993
18053
|
});
|
|
16994
18054
|
}
|
|
18055
|
+
/**
|
|
18056
|
+
* Refresh the Grok (xAI SuperGrok) OAuth access token. The token endpoint is
|
|
18057
|
+
* resolved through OIDC discovery on every refresh (process-cached 1h by the
|
|
18058
|
+
* flow module) so a rotated endpoint document is picked up without a daemon
|
|
18059
|
+
* restart. HONEST `false` when no refresh_token.
|
|
18060
|
+
*/
|
|
18061
|
+
async refreshGrokToken() {
|
|
18062
|
+
return this.coalesce("grok:active", async () => {
|
|
18063
|
+
const config = this.readConfig();
|
|
18064
|
+
const active = getActiveAccount(config, "grok");
|
|
18065
|
+
const grok = active?.tokens;
|
|
18066
|
+
if (!active || !grok?.refreshToken) return false;
|
|
18067
|
+
const capturedId = active.id;
|
|
18068
|
+
this.materializeMigration(config);
|
|
18069
|
+
const refreshFetch = this.buildRefreshFetch("grok", capturedId);
|
|
18070
|
+
try {
|
|
18071
|
+
const tokenEndpoint = await grokOAuth2.resolveGrokTokenEndpoint(refreshFetch);
|
|
18072
|
+
const result = await grokOAuth2.refreshGrokAccessToken(grok.refreshToken, tokenEndpoint, refreshFetch);
|
|
18073
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
18074
|
+
const next = {
|
|
18075
|
+
...grok,
|
|
18076
|
+
accessToken: result.accessToken,
|
|
18077
|
+
refreshToken: result.refreshToken,
|
|
18078
|
+
expiresAt,
|
|
18079
|
+
status: "authorized",
|
|
18080
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18081
|
+
errorMessage: void 0,
|
|
18082
|
+
syncWarning: void 0
|
|
18083
|
+
};
|
|
18084
|
+
this.writeBackById("grok", capturedId, next);
|
|
18085
|
+
return true;
|
|
18086
|
+
} catch (error) {
|
|
18087
|
+
this.markExpiredById("grok", capturedId, grok, error);
|
|
18088
|
+
return false;
|
|
18089
|
+
}
|
|
18090
|
+
});
|
|
18091
|
+
}
|
|
18092
|
+
/**
|
|
18093
|
+
* "Refresh" a GitHub Copilot token — there is nothing to refresh (ghu_
|
|
18094
|
+
* tokens are long-lived with no exchange endpoint). A call here means the
|
|
18095
|
+
* strategy saw a 401 (the token was revoked); mark the account `expired`
|
|
18096
|
+
* with a re-authenticate message and return `false` (the proxy then declines
|
|
18097
|
+
* the retry instead of looping on a dead token).
|
|
18098
|
+
*/
|
|
18099
|
+
async refreshCopilotToken() {
|
|
18100
|
+
return this.coalesce("copilot:active", async () => {
|
|
18101
|
+
const config = this.readConfig();
|
|
18102
|
+
const active = getActiveAccount(config, "copilot");
|
|
18103
|
+
const copilot = active?.tokens;
|
|
18104
|
+
if (!active || !copilot?.accessToken) return false;
|
|
18105
|
+
this.materializeMigration(config);
|
|
18106
|
+
this.markExpiredById(
|
|
18107
|
+
"copilot",
|
|
18108
|
+
active.id,
|
|
18109
|
+
copilot,
|
|
18110
|
+
new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account")
|
|
18111
|
+
);
|
|
18112
|
+
return false;
|
|
18113
|
+
});
|
|
18114
|
+
}
|
|
16995
18115
|
/**
|
|
16996
18116
|
* Refresh a SPECIFIC managed account by id (background scheduler sweep and
|
|
16997
18117
|
* account-pool resolution). It uses only that account's stored refresh
|
|
@@ -17044,7 +18164,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
17044
18164
|
}
|
|
17045
18165
|
const oauth = account.tokens;
|
|
17046
18166
|
if (!oauth.accessToken) return null;
|
|
17047
|
-
if (providerId === "codex" || providerId === "gemini" || providerId === "kimi") {
|
|
18167
|
+
if (providerId === "codex" || providerId === "gemini" || providerId === "kimi" || providerId === "grok" || providerId === "copilot") {
|
|
17048
18168
|
const expiresAtMs = oauth.expiresAt ? Date.parse(oauth.expiresAt) : 0;
|
|
17049
18169
|
const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - ACCOUNT_REFRESH_LEAD_MS;
|
|
17050
18170
|
if (expiringSoon && oauth.refreshToken) {
|
|
@@ -17148,6 +18268,18 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
17148
18268
|
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
17149
18269
|
};
|
|
17150
18270
|
}
|
|
18271
|
+
if (provider === "grok") {
|
|
18272
|
+
const tokenEndpoint = await grokOAuth2.resolveGrokTokenEndpoint(refreshFetch);
|
|
18273
|
+
const r2 = await grokOAuth2.refreshGrokAccessToken(refreshToken, tokenEndpoint, refreshFetch);
|
|
18274
|
+
return {
|
|
18275
|
+
accessToken: r2.accessToken,
|
|
18276
|
+
refreshToken: r2.refreshToken,
|
|
18277
|
+
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
18278
|
+
};
|
|
18279
|
+
}
|
|
18280
|
+
if (provider === "copilot") {
|
|
18281
|
+
throw new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account");
|
|
18282
|
+
}
|
|
17151
18283
|
const flow = provider === "claude" ? claudeOAuth2 : provider === "codex" ? codexOAuth2 : geminiOAuth2;
|
|
17152
18284
|
const r = await flow.refreshAccessToken(refreshToken, refreshFetch);
|
|
17153
18285
|
return {
|
|
@@ -17391,7 +18523,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
17391
18523
|
};
|
|
17392
18524
|
|
|
17393
18525
|
// src/AccountHealthProbeScheduler.ts
|
|
17394
|
-
import { fetchUpstream as
|
|
18526
|
+
import { fetchUpstream as fetchUpstream11 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
17395
18527
|
|
|
17396
18528
|
// src/probe/CodexGenerationProbe.ts
|
|
17397
18529
|
import {
|
|
@@ -17537,7 +18669,16 @@ var PROVIDER_PROBE_PLANS = {
|
|
|
17537
18669
|
// Kimi's `GET /coding/v1/usages` is a verified FREE authed GET (the allowance
|
|
17538
18670
|
// collector uses it), but the probe path also needs the fingerprint headers —
|
|
17539
18671
|
// keep the probe local until the collector covers the health surface.
|
|
17540
|
-
kimi: { kind: "local" }
|
|
18672
|
+
kimi: { kind: "local" },
|
|
18673
|
+
// Grok's billing proxy is a verified FREE authed GET (the allowance collector
|
|
18674
|
+
// uses it) but it REJECTS non-OAuth credentials and sits on a separate host
|
|
18675
|
+
// with its own product-gate header — keep the probe local, the collector
|
|
18676
|
+
// owns the health surface.
|
|
18677
|
+
grok: { kind: "local" },
|
|
18678
|
+
// The Copilot quota endpoint (copilot_internal/user) is a verified FREE
|
|
18679
|
+
// authed GET but lives on api.github.com with its own auth dialect and a
|
|
18680
|
+
// monthly-only window — the allowance collector owns the health surface.
|
|
18681
|
+
copilot: { kind: "local" }
|
|
17541
18682
|
};
|
|
17542
18683
|
function probePlanFor(providerId) {
|
|
17543
18684
|
return PROVIDER_PROBE_PLANS[providerId] ?? { kind: "local" };
|
|
@@ -17559,7 +18700,7 @@ var AccountHealthProbeScheduler = class {
|
|
|
17559
18700
|
this.logger = logger;
|
|
17560
18701
|
this.config = config;
|
|
17561
18702
|
this.now = opts.now ?? Date.now;
|
|
17562
|
-
this.fetchImpl = opts.fetchImpl ??
|
|
18703
|
+
this.fetchImpl = opts.fetchImpl ?? fetchUpstream11;
|
|
17563
18704
|
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
17564
18705
|
this.planFor = opts.planFor ?? probePlanFor;
|
|
17565
18706
|
}
|
|
@@ -18116,7 +19257,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
18116
19257
|
}
|
|
18117
19258
|
|
|
18118
19259
|
// src/audit/AuditPruneSweeper.ts
|
|
18119
|
-
var
|
|
19260
|
+
var DAY_MS4 = 24 * 60 * 6e4;
|
|
18120
19261
|
var SWEEP_INTERVAL_MS2 = 60 * 6e4;
|
|
18121
19262
|
var ARCHIVE_BATCH = 64;
|
|
18122
19263
|
var AuditPruneSweeper = class {
|
|
@@ -18180,7 +19321,7 @@ var AuditPruneSweeper = class {
|
|
|
18180
19321
|
this.sweeping = true;
|
|
18181
19322
|
try {
|
|
18182
19323
|
if (!existsSync26(this.auditDir)) return 0;
|
|
18183
|
-
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) *
|
|
19324
|
+
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS4;
|
|
18184
19325
|
let removed = 0;
|
|
18185
19326
|
for (const name of readdirSync8(this.auditDir)) {
|
|
18186
19327
|
const dateMs = auditFileDateMs(name);
|
|
@@ -18437,7 +19578,7 @@ async function closeAll(writers) {
|
|
|
18437
19578
|
// src/usage/UsagePruneSweeper.ts
|
|
18438
19579
|
import { unlink as unlink3 } from "fs/promises";
|
|
18439
19580
|
import { join as join24 } from "path";
|
|
18440
|
-
var
|
|
19581
|
+
var DAY_MS5 = 24 * 60 * 6e4;
|
|
18441
19582
|
var SWEEP_INTERVAL_MS3 = 60 * 6e4;
|
|
18442
19583
|
var DEFAULT_USAGE_RETENTION_DAYS = 90;
|
|
18443
19584
|
var UsagePruneSweeper = class {
|
|
@@ -18494,7 +19635,7 @@ var UsagePruneSweeper = class {
|
|
|
18494
19635
|
this.sweeping = true;
|
|
18495
19636
|
try {
|
|
18496
19637
|
const retentionDays = this.config.retentionDays ?? DEFAULT_USAGE_RETENTION_DAYS;
|
|
18497
|
-
const cutoff = this.todayMidnight() - (retentionDays - 1) *
|
|
19638
|
+
const cutoff = this.todayMidnight() - (retentionDays - 1) * DAY_MS5;
|
|
18498
19639
|
let removed = 0;
|
|
18499
19640
|
for (const entry of await listUsageDays(this.usageDir)) {
|
|
18500
19641
|
if (!entry.hasShard) continue;
|
|
@@ -18722,7 +19863,7 @@ var AuditWriter = class {
|
|
|
18722
19863
|
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync8 } from "fs";
|
|
18723
19864
|
import { createHmac as createHmac5 } from "crypto";
|
|
18724
19865
|
import { join as join27 } from "path";
|
|
18725
|
-
import { fetchUpstream as
|
|
19866
|
+
import { fetchUpstream as fetchUpstream12 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
18726
19867
|
|
|
18727
19868
|
// src/billing/billingFiles.ts
|
|
18728
19869
|
var BILLING_FILE_RE = /^billing-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
|
|
@@ -18745,7 +19886,7 @@ var BillingPublisher = class {
|
|
|
18745
19886
|
constructor(billingDir, logger, opts = {}) {
|
|
18746
19887
|
this.billingDir = billingDir;
|
|
18747
19888
|
this.logger = logger;
|
|
18748
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) =>
|
|
19889
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream12(url, init));
|
|
18749
19890
|
this.defer = opts.defer ?? ((fn) => setTimeout(fn, 0));
|
|
18750
19891
|
this.timeoutMs = opts.timeoutMs ?? BILLING_POST_TIMEOUT_MS;
|
|
18751
19892
|
this.now = opts.now ?? Date.now;
|
|
@@ -18995,7 +20136,7 @@ var BillingRetrySweeper = class {
|
|
|
18995
20136
|
// src/TokenRefreshScheduler.ts
|
|
18996
20137
|
var REFRESH_LEAD_MS2 = 5 * 6e4;
|
|
18997
20138
|
var SWEEP_INTERVAL_MS5 = 6e4;
|
|
18998
|
-
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
|
|
20139
|
+
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
|
|
18999
20140
|
var TokenRefreshScheduler = class {
|
|
19000
20141
|
constructor(store, logger, intervalMs = SWEEP_INTERVAL_MS5, leadMs = REFRESH_LEAD_MS2) {
|
|
19001
20142
|
this.store = store;
|
|
@@ -19080,6 +20221,12 @@ var TokenRefreshScheduler = class {
|
|
|
19080
20221
|
return this.store.refreshGeminiToken();
|
|
19081
20222
|
case "kimi":
|
|
19082
20223
|
return this.store.refreshKimiToken();
|
|
20224
|
+
case "grok":
|
|
20225
|
+
return this.store.refreshGrokToken();
|
|
20226
|
+
// ghu_ tokens never near-expire (far-future expiresAt), so the sweep
|
|
20227
|
+
// never reaches this — the branch exists for union totality.
|
|
20228
|
+
case "copilot":
|
|
20229
|
+
return this.store.refreshCopilotToken();
|
|
19083
20230
|
}
|
|
19084
20231
|
}
|
|
19085
20232
|
};
|
|
@@ -19158,7 +20305,7 @@ function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
|
19158
20305
|
|
|
19159
20306
|
// src/webhook/WebhookDispatcher.ts
|
|
19160
20307
|
import { createHmac as createHmac6 } from "crypto";
|
|
19161
|
-
import { fetchUpstream as
|
|
20308
|
+
import { fetchUpstream as fetchUpstream13 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
19162
20309
|
var WEBHOOK_MAX_ATTEMPTS = 3;
|
|
19163
20310
|
var WEBHOOK_QUEUE_MAX = 1e3;
|
|
19164
20311
|
var WEBHOOK_SEND_TIMEOUT_MS = 1e4;
|
|
@@ -19178,7 +20325,7 @@ var WebhookDispatcher = class {
|
|
|
19178
20325
|
sleep;
|
|
19179
20326
|
now;
|
|
19180
20327
|
constructor(opts = {}) {
|
|
19181
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) =>
|
|
20328
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream13(url, init));
|
|
19182
20329
|
this.logger = opts.logger;
|
|
19183
20330
|
this.maxAttempts = opts.maxAttempts ?? WEBHOOK_MAX_ATTEMPTS;
|
|
19184
20331
|
this.queueMax = opts.queueMax ?? WEBHOOK_QUEUE_MAX;
|
|
@@ -19264,8 +20411,8 @@ var WebhookDispatcher = class {
|
|
|
19264
20411
|
signal: AbortSignal.timeout(this.timeoutMs)
|
|
19265
20412
|
});
|
|
19266
20413
|
return res.ok ? { ok: true, status: res.status } : { ok: false, status: res.status };
|
|
19267
|
-
} catch (
|
|
19268
|
-
return { ok: false, error:
|
|
20414
|
+
} catch (err8) {
|
|
20415
|
+
return { ok: false, error: err8 instanceof Error ? err8.message : String(err8) };
|
|
19269
20416
|
}
|
|
19270
20417
|
}
|
|
19271
20418
|
/**
|
|
@@ -19402,7 +20549,7 @@ function buildDaemon(config, paths) {
|
|
|
19402
20549
|
setSecretBox(secretBox3);
|
|
19403
20550
|
setSecretBox2(secretBox3);
|
|
19404
20551
|
const decryptedConfig = decryptConfigSecrets(config, secretBox3);
|
|
19405
|
-
const accountAllowanceStore = new
|
|
20552
|
+
const accountAllowanceStore = new AccountAllowanceStore9(
|
|
19406
20553
|
Date.now,
|
|
19407
20554
|
void 0,
|
|
19408
20555
|
new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
|
|
@@ -19445,7 +20592,7 @@ function buildDaemon(config, paths) {
|
|
|
19445
20592
|
getAccountProxy: (providerId, accountId) => credentialStore.getAccountProxy(providerId, accountId)
|
|
19446
20593
|
})
|
|
19447
20594
|
);
|
|
19448
|
-
setGeminiCodeAssistResolver(
|
|
20595
|
+
setGeminiCodeAssistResolver(getGeminiCodeAssistProjectResolver2());
|
|
19449
20596
|
const autoDisableStore = new AutoDisableStore();
|
|
19450
20597
|
const providerKeyQuotaService = new ProviderKeyQuotaService(secretBox3);
|
|
19451
20598
|
const apiKeyPool = new ApiKeyPoolService(
|
|
@@ -19464,7 +20611,7 @@ function buildDaemon(config, paths) {
|
|
|
19464
20611
|
const pricingEngine = new PricingEngine(pricingStore, logger, {
|
|
19465
20612
|
// Catalog egress follows the same global/env proxy policy as every other
|
|
19466
20613
|
// daemon upstream call; no provider/account override applies here.
|
|
19467
|
-
fetchImpl: ((input, init) =>
|
|
20614
|
+
fetchImpl: ((input, init) => fetchUpstream14(String(input), init ?? {}))
|
|
19468
20615
|
});
|
|
19469
20616
|
const pricingRefreshScheduler = new PricingRefreshScheduler(
|
|
19470
20617
|
pricingEngine,
|
|
@@ -19749,7 +20896,7 @@ function buildDaemon(config, paths) {
|
|
|
19749
20896
|
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
19750
20897
|
// excluded from the upstream trace, so a failing login left no evidence.
|
|
19751
20898
|
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
19752
|
-
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) =>
|
|
20899
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => fetchUpstream14(url, init, { providerId, redactBodies: true }),
|
|
19753
20900
|
subscriptionAccountAppender: credentialStore,
|
|
19754
20901
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
19755
20902
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -19761,6 +20908,9 @@ function buildDaemon(config, paths) {
|
|
|
19761
20908
|
// paste; the app shows the verification URL + user code and polls the
|
|
19762
20909
|
// token-free status). Token captured + persisted daemon-side.
|
|
19763
20910
|
kimiSessions: new CodexOAuthSessionStore(),
|
|
20911
|
+
// Grok interactive OAuth — the same async DEVICE-CODE shape as kimi.
|
|
20912
|
+
grokSessions: new CodexOAuthSessionStore(),
|
|
20913
|
+
copilotSessions: new CodexOAuthSessionStore(),
|
|
19764
20914
|
// Migration pack (app-parity child 6, design D2/D3) — the concrete credential
|
|
19765
20915
|
// store provides BOTH the full DECRYPTED read (`getFullConfig`, export) and
|
|
19766
20916
|
// the multi-account append (`appendProviderAccount`, import re-encrypts at-
|
|
@@ -19819,7 +20969,7 @@ function buildDaemon(config, paths) {
|
|
|
19819
20969
|
});
|
|
19820
20970
|
const webhookDispatcher = new WebhookDispatcher({
|
|
19821
20971
|
logger,
|
|
19822
|
-
fetchImpl: (url, init) =>
|
|
20972
|
+
fetchImpl: (url, init) => fetchUpstream14(url, init)
|
|
19823
20973
|
});
|
|
19824
20974
|
setWebhookRuntime(webhookDispatcher, getSharedAccountHealth4());
|
|
19825
20975
|
const auditWriter = new AuditWriter(auditDir, logger);
|
|
@@ -20133,11 +21283,11 @@ async function runLiveProbe(url, key, fetchImpl = fetch) {
|
|
|
20133
21283
|
status: res.status,
|
|
20134
21284
|
estimateHeader: res.headers.get("x-omnicross-count-estimate")
|
|
20135
21285
|
};
|
|
20136
|
-
} catch (
|
|
21286
|
+
} catch (err8) {
|
|
20137
21287
|
return {
|
|
20138
21288
|
status: null,
|
|
20139
21289
|
estimateHeader: null,
|
|
20140
|
-
error:
|
|
21290
|
+
error: err8 instanceof Error ? err8.message : String(err8)
|
|
20141
21291
|
};
|
|
20142
21292
|
}
|
|
20143
21293
|
}
|
|
@@ -20547,9 +21697,9 @@ async function runLaunch(argv, deps) {
|
|
|
20547
21697
|
await daemon.llmConfig.ready();
|
|
20548
21698
|
await daemon.migrateUsageStore();
|
|
20549
21699
|
await daemon.providerProxy.start();
|
|
20550
|
-
} catch (
|
|
21700
|
+
} catch (err8) {
|
|
20551
21701
|
await shutdownLaunchDaemon(daemon);
|
|
20552
|
-
throw
|
|
21702
|
+
throw err8;
|
|
20553
21703
|
}
|
|
20554
21704
|
let launch;
|
|
20555
21705
|
try {
|
|
@@ -20557,9 +21707,9 @@ async function runLaunch(argv, deps) {
|
|
|
20557
21707
|
providerId: values.provider,
|
|
20558
21708
|
model: values.model
|
|
20559
21709
|
});
|
|
20560
|
-
} catch (
|
|
21710
|
+
} catch (err8) {
|
|
20561
21711
|
await shutdownLaunchDaemon(daemon);
|
|
20562
|
-
throw
|
|
21712
|
+
throw err8;
|
|
20563
21713
|
}
|
|
20564
21714
|
try {
|
|
20565
21715
|
const plan = buildCliSpawnPlan({
|
|
@@ -20664,9 +21814,9 @@ function spawnCliInherit(plan) {
|
|
|
20664
21814
|
process.removeListener("SIGINT", onSignal);
|
|
20665
21815
|
process.removeListener("SIGTERM", onSignal);
|
|
20666
21816
|
};
|
|
20667
|
-
child.on("error", (
|
|
21817
|
+
child.on("error", (err8) => {
|
|
20668
21818
|
detach();
|
|
20669
|
-
if (
|
|
21819
|
+
if (err8.code === "ENOENT") {
|
|
20670
21820
|
reject(
|
|
20671
21821
|
new Error(
|
|
20672
21822
|
`launch: "${plan.command}" not found on PATH \u2014 install the CLI first.`
|
|
@@ -20674,7 +21824,7 @@ function spawnCliInherit(plan) {
|
|
|
20674
21824
|
);
|
|
20675
21825
|
return;
|
|
20676
21826
|
}
|
|
20677
|
-
reject(
|
|
21827
|
+
reject(err8);
|
|
20678
21828
|
});
|
|
20679
21829
|
child.on("exit", (code, signal) => {
|
|
20680
21830
|
detach();
|
|
@@ -20687,14 +21837,16 @@ function spawnCliInherit(plan) {
|
|
|
20687
21837
|
import { spawn as spawn3 } from "child_process";
|
|
20688
21838
|
import { createInterface as createInterface2 } from "readline";
|
|
20689
21839
|
import { parseArgs as parseArgs7 } from "util";
|
|
20690
|
-
import { fetchUpstream as
|
|
21840
|
+
import { fetchUpstream as fetchUpstream15, setUpstreamProxyResolver as setUpstreamProxyResolver2 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
20691
21841
|
import {
|
|
20692
21842
|
claudeOAuth as claudeOAuth3,
|
|
20693
21843
|
codexOAuth as codexOAuth3,
|
|
21844
|
+
copilotOAuth as copilotOAuth3,
|
|
20694
21845
|
geminiOAuth as geminiOAuth3,
|
|
21846
|
+
grokOAuth as grokOAuth3,
|
|
20695
21847
|
kimiOAuth as kimiOAuth3
|
|
20696
21848
|
} from "@omnicross/subscriptions";
|
|
20697
|
-
var PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
|
|
21849
|
+
var PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
|
|
20698
21850
|
async function runLogin(argv, deps) {
|
|
20699
21851
|
const { values, positionals } = parseArgs7({
|
|
20700
21852
|
args: argv,
|
|
@@ -20702,7 +21854,9 @@ async function runLogin(argv, deps) {
|
|
|
20702
21854
|
config: { type: "string", short: "c" },
|
|
20703
21855
|
"master-key-file": { type: "string" },
|
|
20704
21856
|
// Optional user label for the appended account (multi-account).
|
|
20705
|
-
label: { type: "string" }
|
|
21857
|
+
label: { type: "string" },
|
|
21858
|
+
// Optional GitHub Enterprise domain for `login copilot` (GHE accounts).
|
|
21859
|
+
enterprise: { type: "string" }
|
|
20706
21860
|
},
|
|
20707
21861
|
allowPositionals: true
|
|
20708
21862
|
});
|
|
@@ -20716,11 +21870,17 @@ async function runLogin(argv, deps) {
|
|
|
20716
21870
|
if (!values.config) {
|
|
20717
21871
|
throw new Error("login: --config <path> is required");
|
|
20718
21872
|
}
|
|
21873
|
+
if (values.enterprise !== void 0 && provider !== "copilot") {
|
|
21874
|
+
throw new Error("login: --enterprise is only supported for the copilot provider");
|
|
21875
|
+
}
|
|
21876
|
+
const enterpriseDomain = values.enterprise !== void 0 ? copilotOAuth3.normalizeCopilotEnterpriseDomain(values.enterprise) : void 0;
|
|
20719
21877
|
const resolved = {
|
|
20720
21878
|
openBrowser: deps?.openBrowser ?? openBrowser,
|
|
20721
21879
|
promptPaste: deps?.promptPaste ?? promptPaste,
|
|
20722
21880
|
awaitLoopback: deps?.awaitLoopback ?? ((state) => awaitLoopbackCode(state)),
|
|
20723
21881
|
awaitKimiDevice: deps?.awaitKimiDevice ?? ((fetchImpl) => runKimiDeviceFlow(fetchImpl, resolvedOpenBrowser)),
|
|
21882
|
+
awaitGrokDevice: deps?.awaitGrokDevice ?? ((fetchImpl) => runGrokDeviceFlow(fetchImpl, resolvedOpenBrowser)),
|
|
21883
|
+
awaitCopilotDevice: deps?.awaitCopilotDevice ?? ((fetchImpl, enterpriseUrl) => runCopilotDeviceFlow(fetchImpl, resolvedOpenBrowser, enterpriseUrl)),
|
|
20724
21884
|
tokensFetch: deps?.tokensFetch
|
|
20725
21885
|
};
|
|
20726
21886
|
const resolvedOpenBrowser = resolved.openBrowser;
|
|
@@ -20729,14 +21889,15 @@ async function runLogin(argv, deps) {
|
|
|
20729
21889
|
setUpstreamProxyResolver2(createUpstreamProxyResolver());
|
|
20730
21890
|
try {
|
|
20731
21891
|
const tokensPath = defaultTokensPath(values.config);
|
|
20732
|
-
const exchangeFetch = resolved.tokensFetch ?? ((url, init) =>
|
|
21892
|
+
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => fetchUpstream15(url, init, { providerId: provider, redactBodies: true }));
|
|
20733
21893
|
const store = new JsonSubscriptionCredentialStore(tokensPath, box, exchangeFetch);
|
|
20734
21894
|
const expiresAt = await runProviderLogin(
|
|
20735
21895
|
provider,
|
|
20736
21896
|
store,
|
|
20737
21897
|
resolved,
|
|
20738
21898
|
exchangeFetch,
|
|
20739
|
-
values.label
|
|
21899
|
+
values.label,
|
|
21900
|
+
enterpriseDomain
|
|
20740
21901
|
);
|
|
20741
21902
|
console.info(`Logged in to '${provider}' \u2192 ${tokensPath}`);
|
|
20742
21903
|
console.info(` token: [stored, encrypted] expiresAt: ${expiresAt ?? "n/a"}`);
|
|
@@ -20745,10 +21906,12 @@ async function runLogin(argv, deps) {
|
|
|
20745
21906
|
setUpstreamProxyResolver2(null);
|
|
20746
21907
|
}
|
|
20747
21908
|
}
|
|
20748
|
-
async function runProviderLogin(provider, store, deps, exchangeFetch, label) {
|
|
21909
|
+
async function runProviderLogin(provider, store, deps, exchangeFetch, label, enterpriseUrl) {
|
|
20749
21910
|
if (provider === "codex") return loginCodex(store, deps, exchangeFetch, label);
|
|
20750
21911
|
if (provider === "claude") return loginClaude(store, deps, exchangeFetch, label);
|
|
20751
21912
|
if (provider === "kimi") return loginKimi(store, deps, exchangeFetch, label);
|
|
21913
|
+
if (provider === "grok") return loginGrok(store, deps, exchangeFetch, label);
|
|
21914
|
+
if (provider === "copilot") return loginCopilot(store, deps, exchangeFetch, label, enterpriseUrl);
|
|
20752
21915
|
return loginGemini(store, deps, exchangeFetch, label);
|
|
20753
21916
|
}
|
|
20754
21917
|
async function loginCodex(store, deps, exchangeFetch, label) {
|
|
@@ -20858,6 +22021,90 @@ async function loginKimi(store, deps, exchangeFetch, label) {
|
|
|
20858
22021
|
logMasked("kimi", result.accessToken);
|
|
20859
22022
|
return expiresAt;
|
|
20860
22023
|
}
|
|
22024
|
+
async function runGrokDeviceFlow(exchangeFetch, openBrowserFn) {
|
|
22025
|
+
const tokenEndpoint = await grokOAuth3.resolveGrokTokenEndpoint(exchangeFetch);
|
|
22026
|
+
const authorization = await grokOAuth3.requestGrokDeviceAuthorization(exchangeFetch);
|
|
22027
|
+
const url = authorization.verificationUriComplete ?? authorization.verificationUri;
|
|
22028
|
+
console.info("Open this URL in your browser and approve the request:");
|
|
22029
|
+
console.info(` ${url}`);
|
|
22030
|
+
if (!authorization.verificationUriComplete) {
|
|
22031
|
+
console.info(` Then enter this code: ${authorization.userCode}`);
|
|
22032
|
+
}
|
|
22033
|
+
await openBrowserFn(url).catch(() => false);
|
|
22034
|
+
const result = await grokOAuth3.awaitGrokDeviceToken(authorization, tokenEndpoint, exchangeFetch, {
|
|
22035
|
+
onPending: () => process.stdout.write(".")
|
|
22036
|
+
});
|
|
22037
|
+
console.info("");
|
|
22038
|
+
return {
|
|
22039
|
+
...result,
|
|
22040
|
+
accountId: grokOAuth3.grokAccountIdFromAccessToken(result.accessToken)
|
|
22041
|
+
};
|
|
22042
|
+
}
|
|
22043
|
+
async function loginGrok(store, deps, exchangeFetch, label) {
|
|
22044
|
+
const result = await deps.awaitGrokDevice(exchangeFetch);
|
|
22045
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
22046
|
+
const block = {
|
|
22047
|
+
authMethod: "oauth",
|
|
22048
|
+
status: "authorized",
|
|
22049
|
+
accessToken: result.accessToken,
|
|
22050
|
+
refreshToken: result.refreshToken,
|
|
22051
|
+
expiresAt,
|
|
22052
|
+
...result.accountId ? { accountId: result.accountId } : {},
|
|
22053
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22054
|
+
};
|
|
22055
|
+
await store.appendProviderAccount("grok", block, label);
|
|
22056
|
+
logMasked("grok", result.accessToken);
|
|
22057
|
+
return expiresAt;
|
|
22058
|
+
}
|
|
22059
|
+
async function runCopilotDeviceFlow(exchangeFetch, openBrowserFn, enterpriseUrl) {
|
|
22060
|
+
if (enterpriseUrl) console.info(`Using GitHub Enterprise host: ${enterpriseUrl}`);
|
|
22061
|
+
const authorization = await copilotOAuth3.requestCopilotDeviceAuthorization(exchangeFetch, enterpriseUrl);
|
|
22062
|
+
const url = authorization.verificationUri;
|
|
22063
|
+
console.info("Open this URL in your browser and approve the request:");
|
|
22064
|
+
console.info(` ${url}`);
|
|
22065
|
+
console.info(` Then enter this code: ${authorization.userCode}`);
|
|
22066
|
+
await openBrowserFn(url).catch(() => false);
|
|
22067
|
+
const result = await copilotOAuth3.awaitCopilotDeviceToken(authorization, exchangeFetch, {
|
|
22068
|
+
onPending: () => process.stdout.write("."),
|
|
22069
|
+
...enterpriseUrl ? { enterpriseUrl } : {}
|
|
22070
|
+
});
|
|
22071
|
+
console.info("");
|
|
22072
|
+
const identity = await copilotOAuth3.fetchCopilotIdentity(result.accessToken, exchangeFetch, enterpriseUrl);
|
|
22073
|
+
const apiEndpoint = await copilotOAuth3.discoverCopilotApiEndpoint(result.accessToken, exchangeFetch, enterpriseUrl);
|
|
22074
|
+
console.info("Enabling Copilot models (policy)...");
|
|
22075
|
+
await copilotOAuth3.enableAllCopilotModels(
|
|
22076
|
+
result.accessToken,
|
|
22077
|
+
{ apiEndpoint, ...enterpriseUrl ? { enterpriseUrl } : {} },
|
|
22078
|
+
exchangeFetch
|
|
22079
|
+
);
|
|
22080
|
+
return {
|
|
22081
|
+
accessToken: result.accessToken,
|
|
22082
|
+
expiresIn: Math.floor(copilotOAuth3.COPILOT_FAR_FUTURE_MS / 1e3),
|
|
22083
|
+
...identity,
|
|
22084
|
+
...apiEndpoint ? { apiEndpoint } : {}
|
|
22085
|
+
};
|
|
22086
|
+
}
|
|
22087
|
+
async function loginCopilot(store, deps, exchangeFetch, label, enterpriseUrl) {
|
|
22088
|
+
const result = await deps.awaitCopilotDevice(exchangeFetch, enterpriseUrl);
|
|
22089
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
22090
|
+
const block = {
|
|
22091
|
+
authMethod: "oauth",
|
|
22092
|
+
status: "authorized",
|
|
22093
|
+
accessToken: result.accessToken,
|
|
22094
|
+
// ghu_ tokens have no refresh lifecycle — the same token doubles as the
|
|
22095
|
+
// stored refresh credential so generic refresh paths stay well-formed.
|
|
22096
|
+
refreshToken: result.accessToken,
|
|
22097
|
+
expiresAt,
|
|
22098
|
+
...result.accountId ? { accountId: result.accountId } : {},
|
|
22099
|
+
...result.email ? { email: result.email } : {},
|
|
22100
|
+
...result.apiEndpoint ? { apiEndpoint: result.apiEndpoint } : {},
|
|
22101
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
22102
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22103
|
+
};
|
|
22104
|
+
await store.appendProviderAccount("copilot", block, label);
|
|
22105
|
+
logMasked("copilot", result.accessToken);
|
|
22106
|
+
return expiresAt;
|
|
22107
|
+
}
|
|
20861
22108
|
function isLoginProvider(value) {
|
|
20862
22109
|
return PROVIDERS2.includes(value);
|
|
20863
22110
|
}
|
|
@@ -21563,7 +22810,7 @@ async function main() {
|
|
|
21563
22810
|
process.exitCode = 1;
|
|
21564
22811
|
}
|
|
21565
22812
|
}
|
|
21566
|
-
main().catch((
|
|
21567
|
-
console.error(
|
|
22813
|
+
main().catch((err8) => {
|
|
22814
|
+
console.error(err8 instanceof Error ? err8.message : String(err8));
|
|
21568
22815
|
process.exitCode = 1;
|
|
21569
22816
|
});
|