@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.cjs
CHANGED
|
@@ -1170,22 +1170,22 @@ var import_node_fs36 = require("fs");
|
|
|
1170
1170
|
var import_node_path36 = require("path");
|
|
1171
1171
|
var import_audit_types = require("@omnicross/contracts/audit-types");
|
|
1172
1172
|
var import_billing_types = require("@omnicross/contracts/billing-types");
|
|
1173
|
-
var
|
|
1174
|
-
var
|
|
1173
|
+
var import_core7 = require("@omnicross/core");
|
|
1174
|
+
var import_GeminiCodeAssistProjectResolver2 = require("@omnicross/core/auth/GeminiCodeAssistProjectResolver");
|
|
1175
1175
|
var import_ApiKeyPoolService = require("@omnicross/core/completion/ApiKeyPoolService");
|
|
1176
1176
|
var import_outbound_api10 = require("@omnicross/core/outbound-api");
|
|
1177
1177
|
var import_subscriptionRegistryPort = require("@omnicross/core/outbound-api/subscriptionRegistryPort");
|
|
1178
1178
|
var import_SubscriptionAccountHealth4 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
1179
|
-
var
|
|
1179
|
+
var import_AccountAllowanceStore10 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1180
1180
|
var import_AccountAllowanceScheduling5 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
1181
|
-
var
|
|
1181
|
+
var import_upstreamFetch16 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
1182
1182
|
var import_SubscriptionIdentityStore3 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
1183
1183
|
var import_gemini_code_assist_resolver = require("@omnicross/core/ports/gemini-code-assist-resolver");
|
|
1184
1184
|
var import_provider_proxy4 = require("@omnicross/core/provider-proxy");
|
|
1185
1185
|
var import_cli_launcher2 = require("@omnicross/cli-launcher");
|
|
1186
1186
|
var import_outbound_api11 = require("@omnicross/core/outbound-api");
|
|
1187
1187
|
var import_usage2 = require("@omnicross/core/usage");
|
|
1188
|
-
var
|
|
1188
|
+
var import_subscriptions12 = require("@omnicross/subscriptions");
|
|
1189
1189
|
|
|
1190
1190
|
// src/admin/accountsCodexOAuth.ts
|
|
1191
1191
|
var import_node_crypto3 = __toESM(require("crypto"), 1);
|
|
@@ -1367,8 +1367,187 @@ function handleKimiOAuthStatus(sessionId, deps) {
|
|
|
1367
1367
|
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
1368
1368
|
}
|
|
1369
1369
|
|
|
1370
|
+
// src/admin/accountsGrokOAuth.ts
|
|
1371
|
+
var import_subscriptions3 = require("@omnicross/subscriptions");
|
|
1372
|
+
function err3(status, message) {
|
|
1373
|
+
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
1374
|
+
}
|
|
1375
|
+
var DEFAULT_GROK_OAUTH_TTL_MS = 15 * 6e4;
|
|
1376
|
+
async function handleGrokOAuthStart(deps) {
|
|
1377
|
+
if (deps.grokSessions.isBusy()) {
|
|
1378
|
+
return err3(409, "a grok sign-in is already in progress \u2014 finish it in the browser or cancel it");
|
|
1379
|
+
}
|
|
1380
|
+
const fetchImpl = deps.oauthExchangeFetch("grok");
|
|
1381
|
+
let tokenEndpoint;
|
|
1382
|
+
try {
|
|
1383
|
+
tokenEndpoint = await import_subscriptions3.grokOAuth.resolveGrokTokenEndpoint(fetchImpl);
|
|
1384
|
+
} catch (e) {
|
|
1385
|
+
const reason = e instanceof Error ? e.message : "OIDC discovery failed";
|
|
1386
|
+
return err3(502, `grok token-endpoint discovery failed: ${reason}`);
|
|
1387
|
+
}
|
|
1388
|
+
let authorization;
|
|
1389
|
+
try {
|
|
1390
|
+
authorization = await import_subscriptions3.grokOAuth.requestGrokDeviceAuthorization(fetchImpl);
|
|
1391
|
+
} catch (e) {
|
|
1392
|
+
const reason = e instanceof Error ? e.message : "device authorization failed";
|
|
1393
|
+
return err3(502, `grok device authorization failed: ${reason}`);
|
|
1394
|
+
}
|
|
1395
|
+
const { sessionId, signal } = deps.grokSessions.begin();
|
|
1396
|
+
void runGrokDevicePoll(sessionId, tokenEndpoint, authorization.deviceCode, signal, deps).catch((e) => {
|
|
1397
|
+
const reason = e instanceof Error ? e.message : "grok sign-in failed";
|
|
1398
|
+
deps.grokSessions.settle(sessionId, "error", reason);
|
|
1399
|
+
});
|
|
1400
|
+
return {
|
|
1401
|
+
status: 200,
|
|
1402
|
+
body: {
|
|
1403
|
+
authUrl: authorization.verificationUriComplete ?? authorization.verificationUri,
|
|
1404
|
+
userCode: authorization.userCode,
|
|
1405
|
+
sessionId
|
|
1406
|
+
}
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
async function runGrokDevicePoll(sessionId, tokenEndpoint, deviceCode, signal, deps) {
|
|
1410
|
+
const fetchImpl = deps.oauthExchangeFetch("grok");
|
|
1411
|
+
const result = await import_subscriptions3.grokOAuth.awaitGrokDeviceToken(
|
|
1412
|
+
{ userCode: "", deviceCode, verificationUri: "" },
|
|
1413
|
+
tokenEndpoint,
|
|
1414
|
+
fetchImpl,
|
|
1415
|
+
{
|
|
1416
|
+
deadlineMs: DEFAULT_GROK_OAUTH_TTL_MS,
|
|
1417
|
+
sleep: (ms) => new Promise((resolve11, reject) => {
|
|
1418
|
+
const onAbort = () => {
|
|
1419
|
+
clearTimeout(timer);
|
|
1420
|
+
reject(new Error("login: cancelled"));
|
|
1421
|
+
};
|
|
1422
|
+
const timer = setTimeout(() => {
|
|
1423
|
+
signal.removeEventListener("abort", onAbort);
|
|
1424
|
+
resolve11();
|
|
1425
|
+
}, ms);
|
|
1426
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1427
|
+
})
|
|
1428
|
+
}
|
|
1429
|
+
);
|
|
1430
|
+
const block = {
|
|
1431
|
+
authMethod: "oauth",
|
|
1432
|
+
status: "authorized",
|
|
1433
|
+
accessToken: result.accessToken,
|
|
1434
|
+
refreshToken: result.refreshToken,
|
|
1435
|
+
expiresAt: new Date(Date.now() + result.expiresIn * 1e3).toISOString(),
|
|
1436
|
+
accountId: import_subscriptions3.grokOAuth.grokAccountIdFromAccessToken(result.accessToken),
|
|
1437
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1438
|
+
};
|
|
1439
|
+
await deps.subscriptionAccountAppender.appendProviderAccount("grok", block);
|
|
1440
|
+
deps.grokSessions.settle(sessionId, "done");
|
|
1441
|
+
}
|
|
1442
|
+
function handleGrokOAuthCancel(sessionId, deps) {
|
|
1443
|
+
if (!deps.grokSessions.cancel(sessionId)) return err3(404, "unknown or expired grok sign-in session");
|
|
1444
|
+
return { status: 200, body: { ok: true } };
|
|
1445
|
+
}
|
|
1446
|
+
function handleGrokOAuthStatus(sessionId, deps) {
|
|
1447
|
+
const s = deps.grokSessions.get(sessionId);
|
|
1448
|
+
if (!s) return err3(404, "unknown or expired grok sign-in session");
|
|
1449
|
+
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
// src/admin/accountsCopilotOAuth.ts
|
|
1453
|
+
var import_subscriptions4 = require("@omnicross/subscriptions");
|
|
1454
|
+
function err4(status, message) {
|
|
1455
|
+
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
1456
|
+
}
|
|
1457
|
+
var DEFAULT_COPILOT_OAUTH_TTL_MS = 15 * 6e4;
|
|
1458
|
+
async function handleCopilotOAuthStart(deps, enterpriseUrlInput) {
|
|
1459
|
+
if (deps.copilotSessions.isBusy()) {
|
|
1460
|
+
return err4(409, "a copilot sign-in is already in progress \u2014 finish it in the browser or cancel it");
|
|
1461
|
+
}
|
|
1462
|
+
let enterpriseUrl;
|
|
1463
|
+
if (typeof enterpriseUrlInput === "string" && enterpriseUrlInput.trim()) {
|
|
1464
|
+
try {
|
|
1465
|
+
enterpriseUrl = import_subscriptions4.copilotOAuth.normalizeCopilotEnterpriseDomain(enterpriseUrlInput);
|
|
1466
|
+
} catch (e) {
|
|
1467
|
+
const reason = e instanceof Error ? e.message : "invalid GitHub Enterprise domain";
|
|
1468
|
+
return err4(400, `copilot ${reason}`);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
const fetchImpl = deps.oauthExchangeFetch("copilot");
|
|
1472
|
+
let authorization;
|
|
1473
|
+
try {
|
|
1474
|
+
authorization = await import_subscriptions4.copilotOAuth.requestCopilotDeviceAuthorization(fetchImpl, enterpriseUrl);
|
|
1475
|
+
} catch (e) {
|
|
1476
|
+
const reason = e instanceof Error ? e.message : "device authorization failed";
|
|
1477
|
+
return err4(502, `copilot device authorization failed: ${reason}`);
|
|
1478
|
+
}
|
|
1479
|
+
const { sessionId, signal } = deps.copilotSessions.begin();
|
|
1480
|
+
void runCopilotDevicePoll(sessionId, authorization.deviceCode, signal, deps, enterpriseUrl).catch((e) => {
|
|
1481
|
+
const reason = e instanceof Error ? e.message : "copilot sign-in failed";
|
|
1482
|
+
deps.copilotSessions.settle(sessionId, "error", reason);
|
|
1483
|
+
});
|
|
1484
|
+
return {
|
|
1485
|
+
status: 200,
|
|
1486
|
+
body: {
|
|
1487
|
+
authUrl: authorization.verificationUri,
|
|
1488
|
+
userCode: authorization.userCode,
|
|
1489
|
+
sessionId,
|
|
1490
|
+
...enterpriseUrl ? { enterpriseUrl } : {}
|
|
1491
|
+
}
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
async function runCopilotDevicePoll(sessionId, deviceCode, signal, deps, enterpriseUrl) {
|
|
1495
|
+
const fetchImpl = deps.oauthExchangeFetch("copilot");
|
|
1496
|
+
const result = await import_subscriptions4.copilotOAuth.awaitCopilotDeviceToken(
|
|
1497
|
+
{ userCode: "", deviceCode, verificationUri: "", interval: 5, expiresIn: 900 },
|
|
1498
|
+
fetchImpl,
|
|
1499
|
+
{
|
|
1500
|
+
deadlineMs: DEFAULT_COPILOT_OAUTH_TTL_MS,
|
|
1501
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
1502
|
+
sleep: (ms) => new Promise((resolve11, reject) => {
|
|
1503
|
+
const onAbort = () => {
|
|
1504
|
+
clearTimeout(timer);
|
|
1505
|
+
reject(new Error("login: cancelled"));
|
|
1506
|
+
};
|
|
1507
|
+
const timer = setTimeout(() => {
|
|
1508
|
+
signal.removeEventListener("abort", onAbort);
|
|
1509
|
+
resolve11();
|
|
1510
|
+
}, ms);
|
|
1511
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1512
|
+
})
|
|
1513
|
+
}
|
|
1514
|
+
);
|
|
1515
|
+
const identity = await import_subscriptions4.copilotOAuth.fetchCopilotIdentity(result.accessToken, fetchImpl, enterpriseUrl);
|
|
1516
|
+
const apiEndpoint = await import_subscriptions4.copilotOAuth.discoverCopilotApiEndpoint(result.accessToken, fetchImpl, enterpriseUrl);
|
|
1517
|
+
await import_subscriptions4.copilotOAuth.enableAllCopilotModels(
|
|
1518
|
+
result.accessToken,
|
|
1519
|
+
{ apiEndpoint, ...enterpriseUrl ? { enterpriseUrl } : {} },
|
|
1520
|
+
fetchImpl
|
|
1521
|
+
);
|
|
1522
|
+
const block = {
|
|
1523
|
+
authMethod: "oauth",
|
|
1524
|
+
status: "authorized",
|
|
1525
|
+
accessToken: result.accessToken,
|
|
1526
|
+
refreshToken: result.accessToken,
|
|
1527
|
+
expiresAt: new Date(Date.now() + import_subscriptions4.copilotOAuth.COPILOT_FAR_FUTURE_MS).toISOString(),
|
|
1528
|
+
...identity.accountId ? { accountId: identity.accountId } : {},
|
|
1529
|
+
...identity.email ? { email: identity.email } : {},
|
|
1530
|
+
...apiEndpoint ? { apiEndpoint } : {},
|
|
1531
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
1532
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1533
|
+
};
|
|
1534
|
+
await deps.subscriptionAccountAppender.appendProviderAccount("copilot", block);
|
|
1535
|
+
deps.copilotSessions.settle(sessionId, "done");
|
|
1536
|
+
}
|
|
1537
|
+
function handleCopilotOAuthCancel(sessionId, deps) {
|
|
1538
|
+
if (!deps.copilotSessions.cancel(sessionId)) {
|
|
1539
|
+
return err4(404, "unknown or expired copilot sign-in session");
|
|
1540
|
+
}
|
|
1541
|
+
return { status: 200, body: { ok: true } };
|
|
1542
|
+
}
|
|
1543
|
+
function handleCopilotOAuthStatus(sessionId, deps) {
|
|
1544
|
+
const s = deps.copilotSessions.get(sessionId);
|
|
1545
|
+
if (!s) return err4(404, "unknown or expired copilot sign-in session");
|
|
1546
|
+
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1370
1549
|
// src/allowance/AccountAllowanceService.ts
|
|
1371
|
-
var
|
|
1550
|
+
var import_AccountAllowanceStore8 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1372
1551
|
var import_AccountAllowanceScheduling = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
1373
1552
|
|
|
1374
1553
|
// src/allowance/ClaudeAllowanceCollector.ts
|
|
@@ -1839,7 +2018,7 @@ var CodexAllowanceCollector = class {
|
|
|
1839
2018
|
// src/allowance/KimiAllowanceCollector.ts
|
|
1840
2019
|
var import_AccountAllowanceStore3 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1841
2020
|
var import_upstreamFetch3 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
1842
|
-
var
|
|
2021
|
+
var import_subscriptions5 = require("@omnicross/subscriptions");
|
|
1843
2022
|
var KIMI_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
1844
2023
|
var KIMI_USAGE_URL = "https://api.kimi.com/coding/v1/usages";
|
|
1845
2024
|
function finiteNumber2(value) {
|
|
@@ -2011,80 +2190,737 @@ var KimiAllowanceCollector = class {
|
|
|
2011
2190
|
try {
|
|
2012
2191
|
payload = await response.json();
|
|
2013
2192
|
} catch {
|
|
2014
|
-
return this.failureSnapshot(accountId, "kimi_usage_invalid_response", this.now());
|
|
2193
|
+
return this.failureSnapshot(accountId, "kimi_usage_invalid_response", this.now());
|
|
2194
|
+
}
|
|
2195
|
+
const now = this.now();
|
|
2196
|
+
const windows = parseKimiUsagePayload(payload, now);
|
|
2197
|
+
const snapshot = {
|
|
2198
|
+
providerId: "kimi",
|
|
2199
|
+
accountId,
|
|
2200
|
+
source: "oauth-usage-api",
|
|
2201
|
+
observedAt: new Date(now).toISOString(),
|
|
2202
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2203
|
+
windows: windows.length > 0 ? windows : [
|
|
2204
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2205
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2206
|
+
],
|
|
2207
|
+
...windows.length > 0 ? {} : { lastErrorCode: "kimi_usage_invalid_response" }
|
|
2208
|
+
};
|
|
2209
|
+
this.store.set(snapshot);
|
|
2210
|
+
return snapshot;
|
|
2211
|
+
}
|
|
2212
|
+
request(accountId, accessToken, tokens) {
|
|
2213
|
+
return this.fetchImpl(KIMI_USAGE_URL, {
|
|
2214
|
+
method: "GET",
|
|
2215
|
+
headers: {
|
|
2216
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2217
|
+
Accept: "application/json",
|
|
2218
|
+
...(0, import_subscriptions5.kimiFingerprintHeaders)(tokens.deviceId)
|
|
2219
|
+
},
|
|
2220
|
+
signal: AbortSignal.timeout(15e3)
|
|
2221
|
+
}, accountId);
|
|
2222
|
+
}
|
|
2223
|
+
failureSnapshot(accountId, code, now) {
|
|
2224
|
+
const existing = this.store.get("kimi", accountId, now);
|
|
2225
|
+
const snapshot = existing ? {
|
|
2226
|
+
...existing,
|
|
2227
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2228
|
+
windows: existing.windows.map((window) => ({
|
|
2229
|
+
...window,
|
|
2230
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2231
|
+
})),
|
|
2232
|
+
lastErrorCode: code
|
|
2233
|
+
} : {
|
|
2234
|
+
providerId: "kimi",
|
|
2235
|
+
accountId,
|
|
2236
|
+
source: "oauth-usage-api",
|
|
2237
|
+
observedAt: new Date(now).toISOString(),
|
|
2238
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2239
|
+
windows: [
|
|
2240
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2241
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2242
|
+
],
|
|
2243
|
+
lastErrorCode: code
|
|
2244
|
+
};
|
|
2245
|
+
this.store.set(snapshot);
|
|
2246
|
+
return snapshot;
|
|
2247
|
+
}
|
|
2248
|
+
unsupportedSnapshot(accountId, now, code = "kimi_usage_unsupported_auth") {
|
|
2249
|
+
return {
|
|
2250
|
+
providerId: "kimi",
|
|
2251
|
+
accountId,
|
|
2252
|
+
source: "oauth-usage-api",
|
|
2253
|
+
observedAt: new Date(now).toISOString(),
|
|
2254
|
+
windows: [
|
|
2255
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unsupported" },
|
|
2256
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2257
|
+
],
|
|
2258
|
+
lastErrorCode: code
|
|
2259
|
+
};
|
|
2260
|
+
}
|
|
2261
|
+
};
|
|
2262
|
+
|
|
2263
|
+
// src/allowance/GrokAllowanceCollector.ts
|
|
2264
|
+
var import_AccountAllowanceStore4 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
2265
|
+
var import_upstreamFetch4 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
2266
|
+
var GROK_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2267
|
+
var GROK_BILLING_BASE = "https://cli-chat-proxy.grok.com";
|
|
2268
|
+
var GROK_BILLING_CREDITS_URL = `${GROK_BILLING_BASE}/v1/billing?format=credits`;
|
|
2269
|
+
var GROK_BILLING_MONTHLY_URL = `${GROK_BILLING_BASE}/v1/billing`;
|
|
2270
|
+
function isRecord2(value) {
|
|
2271
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2272
|
+
}
|
|
2273
|
+
function finiteNumber3(value) {
|
|
2274
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
2275
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
2276
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
2277
|
+
}
|
|
2278
|
+
function percent(value) {
|
|
2279
|
+
const parsed = finiteNumber3(value);
|
|
2280
|
+
return parsed !== void 0 && parsed <= 100 ? parsed : void 0;
|
|
2281
|
+
}
|
|
2282
|
+
function onDemandAmount(value) {
|
|
2283
|
+
return isRecord2(value) ? finiteNumber3(value["val"]) : void 0;
|
|
2284
|
+
}
|
|
2285
|
+
function confirmsNoMonthlyQuota(raw) {
|
|
2286
|
+
const limit = onDemandAmount(raw["monthlyLimit"]);
|
|
2287
|
+
if (limit !== void 0) return limit === 0;
|
|
2288
|
+
return parseWeeklyConfig(raw)?.inferredPercent === true;
|
|
2289
|
+
}
|
|
2290
|
+
function parseWeeklyConfig(raw) {
|
|
2291
|
+
const period = isRecord2(raw["currentPeriod"]) ? raw["currentPeriod"] : void 0;
|
|
2292
|
+
if (!period) return null;
|
|
2293
|
+
const start = typeof period["start"] === "string" ? Date.parse(period["start"]) : Number.NaN;
|
|
2294
|
+
const end = typeof period["end"] === "string" ? Date.parse(period["end"]) : Number.NaN;
|
|
2295
|
+
const type = typeof period["type"] === "string" ? period["type"] : "";
|
|
2296
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
|
|
2297
|
+
if (!type.toUpperCase().includes("WEEK")) return null;
|
|
2298
|
+
const inferred = raw["creditUsagePercent"] === void 0 || raw["creditUsagePercent"] === null;
|
|
2299
|
+
let creditUsagePercent;
|
|
2300
|
+
if (inferred) {
|
|
2301
|
+
creditUsagePercent = end > Date.now() ? 0 : void 0;
|
|
2302
|
+
} else {
|
|
2303
|
+
creditUsagePercent = percent(raw["creditUsagePercent"]);
|
|
2304
|
+
}
|
|
2305
|
+
if (creditUsagePercent === void 0) return null;
|
|
2306
|
+
return {
|
|
2307
|
+
creditUsagePercent,
|
|
2308
|
+
inferredPercent: inferred,
|
|
2309
|
+
resetsAtMs: end,
|
|
2310
|
+
unified: raw["isUnifiedBillingUser"] === true
|
|
2311
|
+
};
|
|
2312
|
+
}
|
|
2313
|
+
function parseMonthlyConfig(raw) {
|
|
2314
|
+
const start = typeof raw["billingPeriodStart"] === "string" ? Date.parse(raw["billingPeriodStart"]) : Number.NaN;
|
|
2315
|
+
const end = typeof raw["billingPeriodEnd"] === "string" ? Date.parse(raw["billingPeriodEnd"]) : Number.NaN;
|
|
2316
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
|
|
2317
|
+
const limit = onDemandAmount(raw["monthlyLimit"]);
|
|
2318
|
+
const used = onDemandAmount(raw["used"]);
|
|
2319
|
+
if (limit === void 0 || limit <= 0 || used === void 0) return null;
|
|
2320
|
+
return { used, limit, periodStartMs: start, periodEndMs: end };
|
|
2321
|
+
}
|
|
2322
|
+
function secondsUntil4(instant, now) {
|
|
2323
|
+
if (!instant) return void 0;
|
|
2324
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2325
|
+
}
|
|
2326
|
+
var MINUTE_MS2 = 6e4;
|
|
2327
|
+
var DAY_MS2 = 864e5;
|
|
2328
|
+
var WEEK_MINUTES = 7 * 24 * 60;
|
|
2329
|
+
function weeklyWindow(config, now) {
|
|
2330
|
+
const resetsAt = new Date(config.resetsAtMs).toISOString();
|
|
2331
|
+
return {
|
|
2332
|
+
id: "seven-day",
|
|
2333
|
+
label: "7 days",
|
|
2334
|
+
scope: "all",
|
|
2335
|
+
usedPercent: config.creditUsagePercent,
|
|
2336
|
+
windowMinutes: WEEK_MINUTES,
|
|
2337
|
+
resetsAt,
|
|
2338
|
+
remainingSeconds: secondsUntil4(resetsAt, now),
|
|
2339
|
+
state: "fresh"
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
function monthlyWindow(config, now) {
|
|
2343
|
+
const resetsAt = new Date(config.periodEndMs).toISOString();
|
|
2344
|
+
const days = Math.max(1, Math.round((config.periodEndMs - config.periodStartMs) / DAY_MS2));
|
|
2345
|
+
return {
|
|
2346
|
+
id: "thirty-day",
|
|
2347
|
+
label: days === 30 || days === 31 ? "30 days" : `${days} days`,
|
|
2348
|
+
scope: "all",
|
|
2349
|
+
usedPercent: Math.round(Math.min(100, config.used / config.limit * 100) * 10) / 10,
|
|
2350
|
+
windowMinutes: Math.round((config.periodEndMs - config.periodStartMs) / MINUTE_MS2),
|
|
2351
|
+
resetsAt,
|
|
2352
|
+
remainingSeconds: secondsUntil4(resetsAt, now),
|
|
2353
|
+
state: "fresh"
|
|
2354
|
+
};
|
|
2355
|
+
}
|
|
2356
|
+
function onDemandWindow(raw) {
|
|
2357
|
+
const cap = onDemandAmount(raw["onDemandCap"]);
|
|
2358
|
+
const used = onDemandAmount(raw["onDemandUsed"]);
|
|
2359
|
+
if (cap === void 0 || cap <= 0 || used === void 0) return null;
|
|
2360
|
+
return {
|
|
2361
|
+
id: "on-demand",
|
|
2362
|
+
label: "On-demand",
|
|
2363
|
+
scope: "all",
|
|
2364
|
+
usedPercent: Math.round(Math.min(100, used / cap * 100) * 10) / 10,
|
|
2365
|
+
state: "fresh"
|
|
2366
|
+
};
|
|
2367
|
+
}
|
|
2368
|
+
async function probeBilling(url, accessToken, accountId, fetchImpl) {
|
|
2369
|
+
try {
|
|
2370
|
+
const response = await fetchImpl(url, {
|
|
2371
|
+
method: "GET",
|
|
2372
|
+
headers: {
|
|
2373
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2374
|
+
Accept: "application/json",
|
|
2375
|
+
"X-XAI-Token-Auth": "xai-grok-cli"
|
|
2376
|
+
},
|
|
2377
|
+
redirect: "error",
|
|
2378
|
+
signal: AbortSignal.timeout(15e3)
|
|
2379
|
+
}, accountId);
|
|
2380
|
+
if (!response.ok) return { status: response.status, payload: null };
|
|
2381
|
+
const payload = await response.json();
|
|
2382
|
+
return { status: response.status, payload: isRecord2(payload) ? payload : null };
|
|
2383
|
+
} catch {
|
|
2384
|
+
return { status: 0, payload: null };
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
function parseGrokBillingPayloads(creditsPayload, monthlyPayload, now) {
|
|
2388
|
+
const creditsConfig = isRecord2(creditsPayload?.["config"]) ? creditsPayload["config"] : null;
|
|
2389
|
+
const monthlyConfig = isRecord2(monthlyPayload?.["config"]) ? monthlyPayload["config"] : null;
|
|
2390
|
+
let weekly = creditsConfig ? parseWeeklyConfig(creditsConfig) : null;
|
|
2391
|
+
const unifiedFlag = creditsConfig?.["isUnifiedBillingUser"] === true;
|
|
2392
|
+
let monthly = monthlyConfig ? parseMonthlyConfig(monthlyConfig) : null;
|
|
2393
|
+
if (weekly?.inferredPercent && unifiedFlag) {
|
|
2394
|
+
if (monthly) {
|
|
2395
|
+
weekly = null;
|
|
2396
|
+
} else if (!monthlyConfig || !confirmsNoMonthlyQuota(monthlyConfig)) {
|
|
2397
|
+
weekly = null;
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
const windows = [];
|
|
2401
|
+
if (weekly) windows.push(weeklyWindow(weekly, now));
|
|
2402
|
+
if (monthly) windows.push(monthlyWindow(monthly, now));
|
|
2403
|
+
const onDemandSource = monthly && monthlyConfig ? monthlyConfig : creditsConfig;
|
|
2404
|
+
const onDemand = onDemandSource ? onDemandWindow(onDemandSource) : null;
|
|
2405
|
+
if (onDemand) windows.push(onDemand);
|
|
2406
|
+
return windows.length > 0 ? windows : null;
|
|
2407
|
+
}
|
|
2408
|
+
var GrokAllowanceCollector = class {
|
|
2409
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore4.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch4.fetchUpstream)(url, init, { providerId: "grok", accountId, redactBodies: true }), now = Date.now) {
|
|
2410
|
+
this.credentials = credentials;
|
|
2411
|
+
this.store = store;
|
|
2412
|
+
this.fetchImpl = fetchImpl;
|
|
2413
|
+
this.now = now;
|
|
2414
|
+
}
|
|
2415
|
+
credentials;
|
|
2416
|
+
store;
|
|
2417
|
+
fetchImpl;
|
|
2418
|
+
now;
|
|
2419
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
2420
|
+
async collectMany(accounts, options = {}) {
|
|
2421
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
2422
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
2423
|
+
}
|
|
2424
|
+
collect(account, options = {}) {
|
|
2425
|
+
const now = this.now();
|
|
2426
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
2427
|
+
const existing = this.store.get("grok", account.id, now);
|
|
2428
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
2429
|
+
return Promise.resolve(existing);
|
|
2430
|
+
}
|
|
2431
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
2432
|
+
this.store.set(snapshot);
|
|
2433
|
+
return Promise.resolve(snapshot);
|
|
2434
|
+
}
|
|
2435
|
+
const cached = this.store.get("grok", account.id, now);
|
|
2436
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
2437
|
+
return Promise.resolve(cached);
|
|
2438
|
+
}
|
|
2439
|
+
const running = this.inFlight.get(account.id);
|
|
2440
|
+
if (running) return running;
|
|
2441
|
+
const promise = this.fetchAccount(account.id).catch(() => this.failureSnapshot(account.id, "grok_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
2442
|
+
this.inFlight.set(account.id, promise);
|
|
2443
|
+
return promise;
|
|
2444
|
+
}
|
|
2445
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
2446
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
2447
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
2448
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
2449
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
2450
|
+
}
|
|
2451
|
+
async fetchAccount(accountId) {
|
|
2452
|
+
const probe = async () => {
|
|
2453
|
+
const accessToken = await this.credentials.getAccessTokenForAccount("grok", accountId);
|
|
2454
|
+
if (!accessToken) return { unauthorized: true, windows: null };
|
|
2455
|
+
const credits = await probeBilling(GROK_BILLING_CREDITS_URL, accessToken, accountId, this.fetchImpl);
|
|
2456
|
+
if (credits.status === 401 || credits.status === 403) return { unauthorized: true, windows: null };
|
|
2457
|
+
const creditsConfig = isRecord2(credits.payload?.["config"]) ? credits.payload["config"] : null;
|
|
2458
|
+
const weekly = creditsConfig ? parseWeeklyConfig(creditsConfig) : null;
|
|
2459
|
+
const monthly = !weekly || creditsConfig?.["isUnifiedBillingUser"] === true ? await probeBilling(GROK_BILLING_MONTHLY_URL, accessToken, accountId, this.fetchImpl) : { status: 200, payload: null };
|
|
2460
|
+
if (monthly.status === 401 || monthly.status === 403) return { unauthorized: true, windows: null };
|
|
2461
|
+
return {
|
|
2462
|
+
unauthorized: false,
|
|
2463
|
+
windows: parseGrokBillingPayloads(credits.payload, monthly.payload, this.now())
|
|
2464
|
+
};
|
|
2465
|
+
};
|
|
2466
|
+
let result = await probe();
|
|
2467
|
+
if (result.unauthorized) {
|
|
2468
|
+
const refreshed = await this.credentials.refreshAccountToken("grok", accountId);
|
|
2469
|
+
if (!refreshed) return this.failureSnapshot(accountId, "grok_usage_unauthorized", this.now());
|
|
2470
|
+
result = await probe();
|
|
2471
|
+
if (result.unauthorized) {
|
|
2472
|
+
return this.failureSnapshot(accountId, "grok_usage_unauthorized", this.now());
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
const now = this.now();
|
|
2476
|
+
if (result.windows && result.windows.length > 0) {
|
|
2477
|
+
const snapshot = {
|
|
2478
|
+
providerId: "grok",
|
|
2479
|
+
accountId,
|
|
2480
|
+
source: "oauth-usage-api",
|
|
2481
|
+
observedAt: new Date(now).toISOString(),
|
|
2482
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2483
|
+
windows: result.windows
|
|
2484
|
+
};
|
|
2485
|
+
this.store.set(snapshot);
|
|
2486
|
+
return snapshot;
|
|
2487
|
+
}
|
|
2488
|
+
return this.failureSnapshot(accountId, "grok_usage_invalid_response", now);
|
|
2489
|
+
}
|
|
2490
|
+
failureSnapshot(accountId, code, now) {
|
|
2491
|
+
const existing = this.store.get("grok", accountId, now);
|
|
2492
|
+
const snapshot = existing ? {
|
|
2493
|
+
...existing,
|
|
2494
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2495
|
+
windows: existing.windows.map((window) => ({
|
|
2496
|
+
...window,
|
|
2497
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2498
|
+
})),
|
|
2499
|
+
lastErrorCode: code
|
|
2500
|
+
} : {
|
|
2501
|
+
providerId: "grok",
|
|
2502
|
+
accountId,
|
|
2503
|
+
source: "oauth-usage-api",
|
|
2504
|
+
observedAt: new Date(now).toISOString(),
|
|
2505
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2506
|
+
windows: [
|
|
2507
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2508
|
+
{ id: "thirty-day", label: "30 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2509
|
+
],
|
|
2510
|
+
lastErrorCode: code
|
|
2511
|
+
};
|
|
2512
|
+
this.store.set(snapshot);
|
|
2513
|
+
return snapshot;
|
|
2514
|
+
}
|
|
2515
|
+
unsupportedSnapshot(accountId, now) {
|
|
2516
|
+
return {
|
|
2517
|
+
providerId: "grok",
|
|
2518
|
+
accountId,
|
|
2519
|
+
source: "oauth-usage-api",
|
|
2520
|
+
observedAt: new Date(now).toISOString(),
|
|
2521
|
+
windows: [
|
|
2522
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" },
|
|
2523
|
+
{ id: "thirty-day", label: "30 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2524
|
+
],
|
|
2525
|
+
lastErrorCode: "grok_usage_unsupported_auth"
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
};
|
|
2529
|
+
|
|
2530
|
+
// src/allowance/CopilotAllowanceCollector.ts
|
|
2531
|
+
var import_AccountAllowanceStore5 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
2532
|
+
var import_upstreamFetch5 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
2533
|
+
var import_subscriptions6 = require("@omnicross/subscriptions");
|
|
2534
|
+
var COPILOT_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2535
|
+
function isRecord3(value) {
|
|
2536
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2537
|
+
}
|
|
2538
|
+
function finiteNumber4(value) {
|
|
2539
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
2540
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
2541
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
2542
|
+
}
|
|
2543
|
+
function booleanValue(value) {
|
|
2544
|
+
if (typeof value === "boolean") return value;
|
|
2545
|
+
if (value === "true") return true;
|
|
2546
|
+
if (value === "false") return false;
|
|
2547
|
+
return void 0;
|
|
2548
|
+
}
|
|
2549
|
+
function parseQuotaDetail(value) {
|
|
2550
|
+
if (!isRecord3(value)) return null;
|
|
2551
|
+
const entitlement = finiteNumber4(value["entitlement"]);
|
|
2552
|
+
const remaining = finiteNumber4(value["remaining"]);
|
|
2553
|
+
const percentRemaining = finiteNumber4(value["percent_remaining"]);
|
|
2554
|
+
const unlimited = booleanValue(value["unlimited"]);
|
|
2555
|
+
if (entitlement === void 0 || remaining === void 0 || percentRemaining === void 0 || unlimited === void 0) {
|
|
2556
|
+
return null;
|
|
2557
|
+
}
|
|
2558
|
+
return { entitlement, remaining, percentRemaining, unlimited };
|
|
2559
|
+
}
|
|
2560
|
+
function secondsUntil5(instant, now) {
|
|
2561
|
+
if (!instant) return void 0;
|
|
2562
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2563
|
+
}
|
|
2564
|
+
function parseCopilotUserPayload(payload, now) {
|
|
2565
|
+
if (!isRecord3(payload)) return null;
|
|
2566
|
+
const snapshots = isRecord3(payload["quota_snapshots"]) ? payload["quota_snapshots"] : void 0;
|
|
2567
|
+
if (!snapshots) return null;
|
|
2568
|
+
const resetRaw = payload["quota_reset_date"];
|
|
2569
|
+
const resetsAt = typeof resetRaw === "string" && resetRaw.trim() && Number.isFinite(Date.parse(resetRaw)) ? new Date(Date.parse(resetRaw)).toISOString() : void 0;
|
|
2570
|
+
const windows = [];
|
|
2571
|
+
const premium = parseQuotaDetail(snapshots["premium_interactions"]);
|
|
2572
|
+
if (premium) {
|
|
2573
|
+
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;
|
|
2574
|
+
if (usedPercent !== null) {
|
|
2575
|
+
windows.push({
|
|
2576
|
+
id: "thirty-day",
|
|
2577
|
+
label: "Monthly",
|
|
2578
|
+
scope: "all",
|
|
2579
|
+
usedPercent,
|
|
2580
|
+
windowMinutes: 30 * 24 * 60,
|
|
2581
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2582
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
2583
|
+
state: "fresh"
|
|
2584
|
+
});
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
const chat = parseQuotaDetail(snapshots["chat"]);
|
|
2588
|
+
if (chat && !chat.unlimited && chat.entitlement > 0) {
|
|
2589
|
+
const usedPercent = Math.round(Math.min(100, (chat.entitlement - chat.remaining) / chat.entitlement * 100) * 10) / 10;
|
|
2590
|
+
windows.push({
|
|
2591
|
+
id: "chat-monthly",
|
|
2592
|
+
label: "Chat (monthly)",
|
|
2593
|
+
scope: "all",
|
|
2594
|
+
usedPercent,
|
|
2595
|
+
windowMinutes: 30 * 24 * 60,
|
|
2596
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2597
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
2598
|
+
state: "fresh"
|
|
2599
|
+
});
|
|
2600
|
+
}
|
|
2601
|
+
return windows.length > 0 ? windows : null;
|
|
2602
|
+
}
|
|
2603
|
+
function githubApiBase(tokens) {
|
|
2604
|
+
return (0, import_subscriptions6.copilotGitHubApiBase)(tokens.enterpriseUrl);
|
|
2605
|
+
}
|
|
2606
|
+
var CopilotAllowanceCollector = class {
|
|
2607
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore5.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch5.fetchUpstream)(url, init, { providerId: "copilot", accountId, redactBodies: true }), now = Date.now) {
|
|
2608
|
+
this.credentials = credentials;
|
|
2609
|
+
this.store = store;
|
|
2610
|
+
this.fetchImpl = fetchImpl;
|
|
2611
|
+
this.now = now;
|
|
2612
|
+
}
|
|
2613
|
+
credentials;
|
|
2614
|
+
store;
|
|
2615
|
+
fetchImpl;
|
|
2616
|
+
now;
|
|
2617
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
2618
|
+
async collectMany(accounts, options = {}) {
|
|
2619
|
+
const settled = await Promise.allSettled(
|
|
2620
|
+
accounts.map((account) => this.collect(account, options))
|
|
2621
|
+
);
|
|
2622
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
2623
|
+
}
|
|
2624
|
+
collect(account, options = {}) {
|
|
2625
|
+
const now = this.now();
|
|
2626
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
2627
|
+
const existing = this.store.get("copilot", account.id, now);
|
|
2628
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
2629
|
+
return Promise.resolve(existing);
|
|
2630
|
+
}
|
|
2631
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
2632
|
+
this.store.set(snapshot);
|
|
2633
|
+
return Promise.resolve(snapshot);
|
|
2634
|
+
}
|
|
2635
|
+
const cached = this.store.get("copilot", account.id, now);
|
|
2636
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
2637
|
+
return Promise.resolve(cached);
|
|
2638
|
+
}
|
|
2639
|
+
const running = this.inFlight.get(account.id);
|
|
2640
|
+
if (running) return running;
|
|
2641
|
+
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));
|
|
2642
|
+
this.inFlight.set(account.id, promise);
|
|
2643
|
+
return promise;
|
|
2644
|
+
}
|
|
2645
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
2646
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
2647
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
2648
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
2649
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
2650
|
+
}
|
|
2651
|
+
async fetchAccount(accountId, tokens) {
|
|
2652
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
|
|
2653
|
+
if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
|
|
2654
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
2655
|
+
if (response.status === 401 || response.status === 403) {
|
|
2656
|
+
const refreshed = await this.credentials.refreshAccountToken("copilot", accountId);
|
|
2657
|
+
if (!refreshed) return this.failureSnapshot(accountId, "copilot_usage_unauthorized", this.now());
|
|
2658
|
+
accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
|
|
2659
|
+
if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
|
|
2660
|
+
response = await this.request(accountId, accessToken, tokens);
|
|
2661
|
+
if (response.status === 401 || response.status === 403) {
|
|
2662
|
+
return this.failureSnapshot(accountId, "copilot_usage_unauthorized", this.now());
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
if (!response.ok) return this.failureSnapshot(accountId, "copilot_usage_http_error", this.now());
|
|
2666
|
+
let payload;
|
|
2667
|
+
try {
|
|
2668
|
+
payload = await response.json();
|
|
2669
|
+
} catch {
|
|
2670
|
+
return this.failureSnapshot(accountId, "copilot_usage_invalid_response", this.now());
|
|
2671
|
+
}
|
|
2672
|
+
const now = this.now();
|
|
2673
|
+
const windows = parseCopilotUserPayload(payload, now);
|
|
2674
|
+
const snapshot = {
|
|
2675
|
+
providerId: "copilot",
|
|
2676
|
+
accountId,
|
|
2677
|
+
source: "oauth-usage-api",
|
|
2678
|
+
observedAt: new Date(now).toISOString(),
|
|
2679
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2680
|
+
windows: windows ?? [
|
|
2681
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2682
|
+
],
|
|
2683
|
+
...windows ? {} : { lastErrorCode: "copilot_usage_invalid_response" }
|
|
2684
|
+
};
|
|
2685
|
+
this.store.set(snapshot);
|
|
2686
|
+
return snapshot;
|
|
2687
|
+
}
|
|
2688
|
+
request(accountId, accessToken, tokens) {
|
|
2689
|
+
return this.fetchImpl(`${githubApiBase(tokens)}/copilot_internal/user`, {
|
|
2690
|
+
method: "GET",
|
|
2691
|
+
headers: {
|
|
2692
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2693
|
+
Accept: "application/json",
|
|
2694
|
+
"Content-Type": "application/json",
|
|
2695
|
+
...import_subscriptions6.COPILOT_GITHUB_HEADERS
|
|
2696
|
+
},
|
|
2697
|
+
signal: AbortSignal.timeout(15e3)
|
|
2698
|
+
}, accountId);
|
|
2699
|
+
}
|
|
2700
|
+
failureSnapshot(accountId, code, now) {
|
|
2701
|
+
const existing = this.store.get("copilot", accountId, now);
|
|
2702
|
+
const snapshot = existing ? {
|
|
2703
|
+
...existing,
|
|
2704
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2705
|
+
windows: existing.windows.map((window) => ({
|
|
2706
|
+
...window,
|
|
2707
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2708
|
+
})),
|
|
2709
|
+
lastErrorCode: code
|
|
2710
|
+
} : {
|
|
2711
|
+
providerId: "copilot",
|
|
2712
|
+
accountId,
|
|
2713
|
+
source: "oauth-usage-api",
|
|
2714
|
+
observedAt: new Date(now).toISOString(),
|
|
2715
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2716
|
+
windows: [
|
|
2717
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2718
|
+
],
|
|
2719
|
+
lastErrorCode: code
|
|
2720
|
+
};
|
|
2721
|
+
this.store.set(snapshot);
|
|
2722
|
+
return snapshot;
|
|
2723
|
+
}
|
|
2724
|
+
unsupportedSnapshot(accountId, now) {
|
|
2725
|
+
return {
|
|
2726
|
+
providerId: "copilot",
|
|
2727
|
+
accountId,
|
|
2728
|
+
source: "oauth-usage-api",
|
|
2729
|
+
observedAt: new Date(now).toISOString(),
|
|
2730
|
+
windows: [
|
|
2731
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2732
|
+
],
|
|
2733
|
+
lastErrorCode: "copilot_usage_unsupported_auth"
|
|
2734
|
+
};
|
|
2735
|
+
}
|
|
2736
|
+
};
|
|
2737
|
+
|
|
2738
|
+
// src/allowance/GeminiAllowanceCollector.ts
|
|
2739
|
+
var import_GeminiCodeAssistProjectResolver = require("@omnicross/core/auth/GeminiCodeAssistProjectResolver");
|
|
2740
|
+
var import_AccountAllowanceStore6 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
2741
|
+
var import_upstreamFetch6 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
2742
|
+
var import_transformers = require("@omnicross/core/transformer/transformers");
|
|
2743
|
+
var GEMINI_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2744
|
+
function isRecord4(value) {
|
|
2745
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2746
|
+
}
|
|
2747
|
+
function secondsUntil6(instant, now) {
|
|
2748
|
+
if (!instant) return void 0;
|
|
2749
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2750
|
+
}
|
|
2751
|
+
function parseGeminiQuotaPayload(payload, now) {
|
|
2752
|
+
if (!isRecord4(payload)) return null;
|
|
2753
|
+
const buckets = Array.isArray(payload["buckets"]) ? payload["buckets"] : [];
|
|
2754
|
+
const windows = [];
|
|
2755
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2756
|
+
for (const raw of buckets) {
|
|
2757
|
+
if (!isRecord4(raw)) continue;
|
|
2758
|
+
const modelId = typeof raw["modelId"] === "string" && raw["modelId"].trim() ? raw["modelId"].trim() : void 0;
|
|
2759
|
+
const id = `gemini:${modelId ?? "all"}`;
|
|
2760
|
+
if (seen.has(id)) continue;
|
|
2761
|
+
seen.add(id);
|
|
2762
|
+
const fractionRaw = typeof raw["remainingFraction"] === "number" ? raw["remainingFraction"] : Number(raw["remainingFraction"]);
|
|
2763
|
+
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;
|
|
2764
|
+
const resetRaw = typeof raw["resetTime"] === "string" && raw["resetTime"].trim() ? raw["resetTime"] : void 0;
|
|
2765
|
+
const resetsAt = resetRaw !== void 0 && Number.isFinite(Date.parse(resetRaw)) ? new Date(Date.parse(resetRaw)).toISOString() : void 0;
|
|
2766
|
+
windows.push({
|
|
2767
|
+
id,
|
|
2768
|
+
label: modelId ? `Gemini ${modelId}` : "Gemini quota",
|
|
2769
|
+
scope: modelId ? "model-family" : "all",
|
|
2770
|
+
...modelId ? { modelFamily: modelId } : {},
|
|
2771
|
+
usedPercent,
|
|
2772
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2773
|
+
remainingSeconds: secondsUntil6(resetsAt, now),
|
|
2774
|
+
state: "fresh"
|
|
2775
|
+
});
|
|
2776
|
+
}
|
|
2777
|
+
return windows.length > 0 ? windows : null;
|
|
2778
|
+
}
|
|
2779
|
+
var GeminiAllowanceCollector = class {
|
|
2780
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore6.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch6.fetchUpstream)(url, init, { providerId: "gemini", accountId, redactBodies: true }), now = Date.now, projectResolver = (0, import_GeminiCodeAssistProjectResolver.getGeminiCodeAssistProjectResolver)()) {
|
|
2781
|
+
this.credentials = credentials;
|
|
2782
|
+
this.store = store;
|
|
2783
|
+
this.fetchImpl = fetchImpl;
|
|
2784
|
+
this.now = now;
|
|
2785
|
+
this.projectResolver = projectResolver;
|
|
2786
|
+
}
|
|
2787
|
+
credentials;
|
|
2788
|
+
store;
|
|
2789
|
+
fetchImpl;
|
|
2790
|
+
now;
|
|
2791
|
+
projectResolver;
|
|
2792
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
2793
|
+
async collectMany(accounts, options = {}) {
|
|
2794
|
+
const settled = await Promise.allSettled(
|
|
2795
|
+
accounts.map((account) => this.collect(account, options))
|
|
2796
|
+
);
|
|
2797
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
2798
|
+
}
|
|
2799
|
+
collect(account, options = {}) {
|
|
2800
|
+
const now = this.now();
|
|
2801
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
2802
|
+
const existing = this.store.get("gemini", account.id, now);
|
|
2803
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
2804
|
+
return Promise.resolve(existing);
|
|
2805
|
+
}
|
|
2806
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
2807
|
+
this.store.set(snapshot);
|
|
2808
|
+
return Promise.resolve(snapshot);
|
|
2809
|
+
}
|
|
2810
|
+
const cached = this.store.get("gemini", account.id, now);
|
|
2811
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
2812
|
+
return Promise.resolve(cached);
|
|
2813
|
+
}
|
|
2814
|
+
const running = this.inFlight.get(account.id);
|
|
2815
|
+
if (running) return running;
|
|
2816
|
+
const promise = this.fetchAccount(account.id).catch(() => this.failureSnapshot(account.id, "gemini_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
2817
|
+
this.inFlight.set(account.id, promise);
|
|
2818
|
+
return promise;
|
|
2819
|
+
}
|
|
2820
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
2821
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
2822
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
2823
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
2824
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
2825
|
+
}
|
|
2826
|
+
async fetchAccount(accountId) {
|
|
2827
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("gemini", accountId);
|
|
2828
|
+
if (!accessToken) return this.failureSnapshot(accountId, "gemini_usage_token_unavailable", this.now());
|
|
2829
|
+
let project;
|
|
2830
|
+
try {
|
|
2831
|
+
project = await this.projectResolver.resolveProject(accessToken);
|
|
2832
|
+
} catch {
|
|
2833
|
+
project = void 0;
|
|
2834
|
+
}
|
|
2835
|
+
let response = await this.request(accountId, accessToken, project);
|
|
2836
|
+
if (response.status === 401 || response.status === 403) {
|
|
2837
|
+
const refreshed = await this.credentials.refreshAccountToken("gemini", accountId);
|
|
2838
|
+
if (!refreshed) return this.failureSnapshot(accountId, "gemini_usage_unauthorized", this.now());
|
|
2839
|
+
accessToken = await this.credentials.getAccessTokenForAccount("gemini", accountId);
|
|
2840
|
+
if (!accessToken) return this.failureSnapshot(accountId, "gemini_usage_token_unavailable", this.now());
|
|
2841
|
+
response = await this.request(accountId, accessToken, project);
|
|
2842
|
+
if (response.status === 401 || response.status === 403) {
|
|
2843
|
+
return this.failureSnapshot(accountId, "gemini_usage_unauthorized", this.now());
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
if (!response.ok) return this.failureSnapshot(accountId, "gemini_usage_http_error", this.now());
|
|
2847
|
+
let payload;
|
|
2848
|
+
try {
|
|
2849
|
+
payload = await response.json();
|
|
2850
|
+
} catch {
|
|
2851
|
+
return this.failureSnapshot(accountId, "gemini_usage_invalid_response", this.now());
|
|
2015
2852
|
}
|
|
2016
2853
|
const now = this.now();
|
|
2017
|
-
const windows =
|
|
2854
|
+
const windows = parseGeminiQuotaPayload(payload, now);
|
|
2018
2855
|
const snapshot = {
|
|
2019
|
-
providerId: "
|
|
2856
|
+
providerId: "gemini",
|
|
2020
2857
|
accountId,
|
|
2021
2858
|
source: "oauth-usage-api",
|
|
2022
2859
|
observedAt: new Date(now).toISOString(),
|
|
2023
|
-
expiresAt: new Date(now +
|
|
2024
|
-
windows: windows
|
|
2025
|
-
{ id: "
|
|
2026
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2860
|
+
expiresAt: new Date(now + GEMINI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2861
|
+
windows: windows ?? [
|
|
2862
|
+
{ id: "gemini-quota", label: "Gemini quota", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2027
2863
|
],
|
|
2028
|
-
...windows
|
|
2864
|
+
...windows ? {} : { lastErrorCode: "gemini_usage_invalid_response" }
|
|
2029
2865
|
};
|
|
2030
2866
|
this.store.set(snapshot);
|
|
2031
2867
|
return snapshot;
|
|
2032
2868
|
}
|
|
2033
|
-
request(accountId, accessToken,
|
|
2034
|
-
return this.fetchImpl(
|
|
2035
|
-
method: "
|
|
2869
|
+
request(accountId, accessToken, project) {
|
|
2870
|
+
return this.fetchImpl(`${(0, import_transformers.resolveCodeAssistEndpoint)()}/v1internal:retrieveUserQuota`, {
|
|
2871
|
+
method: "POST",
|
|
2036
2872
|
headers: {
|
|
2037
2873
|
Authorization: `Bearer ${accessToken}`,
|
|
2038
2874
|
Accept: "application/json",
|
|
2039
|
-
|
|
2875
|
+
"Content-Type": "application/json",
|
|
2876
|
+
...(0, import_transformers.getGeminiCliIdentityHeaders)()
|
|
2040
2877
|
},
|
|
2878
|
+
body: JSON.stringify(project ? { project } : {}),
|
|
2041
2879
|
signal: AbortSignal.timeout(15e3)
|
|
2042
2880
|
}, accountId);
|
|
2043
2881
|
}
|
|
2044
2882
|
failureSnapshot(accountId, code, now) {
|
|
2045
|
-
const existing = this.store.get("
|
|
2883
|
+
const existing = this.store.get("gemini", accountId, now);
|
|
2046
2884
|
const snapshot = existing ? {
|
|
2047
2885
|
...existing,
|
|
2048
|
-
expiresAt: new Date(now +
|
|
2886
|
+
expiresAt: new Date(now + GEMINI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2049
2887
|
windows: existing.windows.map((window) => ({
|
|
2050
2888
|
...window,
|
|
2051
2889
|
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2052
2890
|
})),
|
|
2053
2891
|
lastErrorCode: code
|
|
2054
2892
|
} : {
|
|
2055
|
-
providerId: "
|
|
2893
|
+
providerId: "gemini",
|
|
2056
2894
|
accountId,
|
|
2057
2895
|
source: "oauth-usage-api",
|
|
2058
2896
|
observedAt: new Date(now).toISOString(),
|
|
2059
|
-
expiresAt: new Date(now +
|
|
2897
|
+
expiresAt: new Date(now + GEMINI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2060
2898
|
windows: [
|
|
2061
|
-
{ id: "
|
|
2062
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2899
|
+
{ id: "gemini-quota", label: "Gemini quota", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2063
2900
|
],
|
|
2064
2901
|
lastErrorCode: code
|
|
2065
2902
|
};
|
|
2066
2903
|
this.store.set(snapshot);
|
|
2067
2904
|
return snapshot;
|
|
2068
2905
|
}
|
|
2069
|
-
unsupportedSnapshot(accountId, now
|
|
2906
|
+
unsupportedSnapshot(accountId, now) {
|
|
2070
2907
|
return {
|
|
2071
|
-
providerId: "
|
|
2908
|
+
providerId: "gemini",
|
|
2072
2909
|
accountId,
|
|
2073
2910
|
source: "oauth-usage-api",
|
|
2074
2911
|
observedAt: new Date(now).toISOString(),
|
|
2075
2912
|
windows: [
|
|
2076
|
-
{ id: "
|
|
2077
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2913
|
+
{ id: "gemini-quota", label: "Gemini quota", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2078
2914
|
],
|
|
2079
|
-
lastErrorCode:
|
|
2915
|
+
lastErrorCode: "gemini_usage_unsupported_auth"
|
|
2080
2916
|
};
|
|
2081
2917
|
}
|
|
2082
2918
|
};
|
|
2083
2919
|
|
|
2084
2920
|
// src/allowance/OpenCodeGoAllowanceCollector.ts
|
|
2085
|
-
var
|
|
2086
|
-
var
|
|
2087
|
-
var
|
|
2921
|
+
var import_AccountAllowanceStore7 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
2922
|
+
var import_upstreamFetch7 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
2923
|
+
var import_subscriptions7 = require("@omnicross/subscriptions");
|
|
2088
2924
|
var OPENCODEGO_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2089
2925
|
var OPENCODEGO_DEFAULT_GO_BASE = "https://opencode.ai/zen/go";
|
|
2090
2926
|
function finitePercent3(value) {
|
|
@@ -2097,7 +2933,7 @@ function isoInstant2(value) {
|
|
|
2097
2933
|
const time = Date.parse(value);
|
|
2098
2934
|
return Number.isFinite(time) ? new Date(time).toISOString() : void 0;
|
|
2099
2935
|
}
|
|
2100
|
-
function
|
|
2936
|
+
function secondsUntil7(instant, now) {
|
|
2101
2937
|
if (!instant) return void 0;
|
|
2102
2938
|
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2103
2939
|
}
|
|
@@ -2112,12 +2948,12 @@ function windowFromPayload3(id, label, minutes, payload, now) {
|
|
|
2112
2948
|
usedPercent,
|
|
2113
2949
|
windowMinutes: minutes,
|
|
2114
2950
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2115
|
-
remainingSeconds:
|
|
2951
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
2116
2952
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
2117
2953
|
};
|
|
2118
2954
|
}
|
|
2119
2955
|
var OpenCodeGoAllowanceCollector = class {
|
|
2120
|
-
constructor(credentials, store = (0,
|
|
2956
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore7.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch7.fetchUpstream)(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
|
|
2121
2957
|
this.credentials = credentials;
|
|
2122
2958
|
this.store = store;
|
|
2123
2959
|
this.fetchImpl = fetchImpl;
|
|
@@ -2147,7 +2983,7 @@ var OpenCodeGoAllowanceCollector = class {
|
|
|
2147
2983
|
async fetchAccount(account) {
|
|
2148
2984
|
const apiKey = await this.credentials.getAccessTokenForAccount("opencodego", account.id);
|
|
2149
2985
|
if (!apiKey) return this.failureSnapshot(account.id, this.now());
|
|
2150
|
-
const base = account.tokens.baseUrl ? (0,
|
|
2986
|
+
const base = account.tokens.baseUrl ? (0, import_subscriptions7.normalizeOpenCodeGoBaseUrl)(account.tokens.baseUrl) : OPENCODEGO_DEFAULT_GO_BASE;
|
|
2151
2987
|
const response = await this.fetchImpl(`${base}/v1/usage`, {
|
|
2152
2988
|
method: "GET",
|
|
2153
2989
|
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
@@ -2222,7 +3058,7 @@ function codexUnavailable(accountId, now) {
|
|
|
2222
3058
|
};
|
|
2223
3059
|
}
|
|
2224
3060
|
var AccountAllowanceService = class {
|
|
2225
|
-
constructor(credentials, store = (0,
|
|
3061
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore8.getSharedAccountAllowanceStore)(), collector, codexCollector, kimiCollector, opencodegoCollector, grokCollector, copilotCollector, geminiCollector, now = Date.now) {
|
|
2226
3062
|
this.credentials = credentials;
|
|
2227
3063
|
this.store = store;
|
|
2228
3064
|
this.now = now;
|
|
@@ -2230,6 +3066,9 @@ var AccountAllowanceService = class {
|
|
|
2230
3066
|
this.codexCollector = codexCollector ?? new CodexAllowanceCollector(credentials, store);
|
|
2231
3067
|
this.kimiCollector = kimiCollector ?? new KimiAllowanceCollector(credentials, store);
|
|
2232
3068
|
this.opencodegoCollector = opencodegoCollector ?? new OpenCodeGoAllowanceCollector(credentials, store);
|
|
3069
|
+
this.grokCollector = grokCollector ?? new GrokAllowanceCollector(credentials, store);
|
|
3070
|
+
this.copilotCollector = copilotCollector ?? new CopilotAllowanceCollector(credentials, store);
|
|
3071
|
+
this.geminiCollector = geminiCollector ?? new GeminiAllowanceCollector(credentials, store);
|
|
2233
3072
|
}
|
|
2234
3073
|
credentials;
|
|
2235
3074
|
store;
|
|
@@ -2237,7 +3076,10 @@ var AccountAllowanceService = class {
|
|
|
2237
3076
|
claudeCollector;
|
|
2238
3077
|
codexCollector;
|
|
2239
3078
|
kimiCollector;
|
|
3079
|
+
grokCollector;
|
|
3080
|
+
copilotCollector;
|
|
2240
3081
|
opencodegoCollector;
|
|
3082
|
+
geminiCollector;
|
|
2241
3083
|
/**
|
|
2242
3084
|
* Read all/filtered snapshots. Claude's and Codex's five-minute caches are
|
|
2243
3085
|
* refreshed lazily on read (Codex polls `/backend-api/wham/usage`; the
|
|
@@ -2271,11 +3113,29 @@ var AccountAllowanceService = class {
|
|
|
2271
3113
|
(account) => !filter.accountId || account.id === filter.accountId
|
|
2272
3114
|
);
|
|
2273
3115
|
if (wantsOpenCodeGo) await this.opencodegoCollector.collectMany(opencodegoAccounts);
|
|
3116
|
+
const wantsGrok = !filter.providerId || filter.providerId === "grok";
|
|
3117
|
+
const grokAccounts = (config.grokAccounts ?? []).filter(
|
|
3118
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
3119
|
+
);
|
|
3120
|
+
if (wantsGrok) await this.grokCollector.collectMany(grokAccounts);
|
|
3121
|
+
const wantsCopilot = !filter.providerId || filter.providerId === "copilot";
|
|
3122
|
+
const copilotAccounts = (config.copilotAccounts ?? []).filter(
|
|
3123
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
3124
|
+
);
|
|
3125
|
+
if (wantsCopilot) await this.copilotCollector.collectMany(copilotAccounts);
|
|
3126
|
+
const wantsGemini = !filter.providerId || filter.providerId === "gemini";
|
|
3127
|
+
const geminiAccounts = (config.geminiAccounts ?? []).filter(
|
|
3128
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
3129
|
+
);
|
|
3130
|
+
if (wantsGemini) await this.geminiCollector.collectMany(geminiAccounts);
|
|
2274
3131
|
const known = /* @__PURE__ */ new Set();
|
|
2275
3132
|
if (wantsClaude) for (const account of claudeAccounts) known.add(`claude\0${account.id}`);
|
|
2276
3133
|
if (wantsCodex) for (const account of codexAccounts) known.add(`codex\0${account.id}`);
|
|
2277
3134
|
if (wantsKimi) for (const account of kimiAccounts) known.add(`kimi\0${account.id}`);
|
|
2278
3135
|
if (wantsOpenCodeGo) for (const account of opencodegoAccounts) known.add(`opencodego\0${account.id}`);
|
|
3136
|
+
if (wantsGrok) for (const account of grokAccounts) known.add(`grok\0${account.id}`);
|
|
3137
|
+
if (wantsCopilot) for (const account of copilotAccounts) known.add(`copilot\0${account.id}`);
|
|
3138
|
+
if (wantsGemini) for (const account of geminiAccounts) known.add(`gemini\0${account.id}`);
|
|
2279
3139
|
return this.store.list(filter).filter((snapshot) => known.has(`${snapshot.providerId}\0${snapshot.accountId}`));
|
|
2280
3140
|
}
|
|
2281
3141
|
knownAccounts(config) {
|
|
@@ -2283,7 +3143,10 @@ var AccountAllowanceService = class {
|
|
|
2283
3143
|
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
2284
3144
|
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id })),
|
|
2285
3145
|
...(config.kimiAccounts ?? []).map((account) => ({ providerId: "kimi", accountId: account.id })),
|
|
2286
|
-
...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id }))
|
|
3146
|
+
...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id })),
|
|
3147
|
+
...(config.grokAccounts ?? []).map((account) => ({ providerId: "grok", accountId: account.id })),
|
|
3148
|
+
...(config.copilotAccounts ?? []).map((account) => ({ providerId: "copilot", accountId: account.id })),
|
|
3149
|
+
...(config.geminiAccounts ?? []).map((account) => ({ providerId: "gemini", accountId: account.id }))
|
|
2287
3150
|
];
|
|
2288
3151
|
}
|
|
2289
3152
|
/** Force-refresh Claude usage for one account or every stored Claude account. */
|
|
@@ -2326,6 +3189,33 @@ var AccountAllowanceService = class {
|
|
|
2326
3189
|
);
|
|
2327
3190
|
return this.kimiCollector.collectMany(accounts, { force: true });
|
|
2328
3191
|
}
|
|
3192
|
+
/** Force-refresh Copilot usage (copilot_internal/user) for one/all accounts. */
|
|
3193
|
+
async refreshCopilot(accountId) {
|
|
3194
|
+
const config = await this.credentials.getFullConfig();
|
|
3195
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
3196
|
+
const accounts = (config.copilotAccounts ?? []).filter(
|
|
3197
|
+
(account) => !accountId || account.id === accountId
|
|
3198
|
+
);
|
|
3199
|
+
return this.copilotCollector.collectMany(accounts, { force: true });
|
|
3200
|
+
}
|
|
3201
|
+
/** Force-refresh Grok usage (CLI billing proxy) for one/all accounts. */
|
|
3202
|
+
async refreshGrok(accountId) {
|
|
3203
|
+
const config = await this.credentials.getFullConfig();
|
|
3204
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
3205
|
+
const accounts = (config.grokAccounts ?? []).filter(
|
|
3206
|
+
(account) => !accountId || account.id === accountId
|
|
3207
|
+
);
|
|
3208
|
+
return this.grokCollector.collectMany(accounts, { force: true });
|
|
3209
|
+
}
|
|
3210
|
+
/** Force-refresh Gemini usage (Code Assist retrieveUserQuota) for one/all accounts. */
|
|
3211
|
+
async refreshGemini(accountId) {
|
|
3212
|
+
const config = await this.credentials.getFullConfig();
|
|
3213
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
3214
|
+
const accounts = (config.geminiAccounts ?? []).filter(
|
|
3215
|
+
(account) => !accountId || account.id === accountId
|
|
3216
|
+
);
|
|
3217
|
+
return this.geminiCollector.collectMany(accounts, { force: true });
|
|
3218
|
+
}
|
|
2329
3219
|
/**
|
|
2330
3220
|
* Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
|
|
2331
3221
|
* collectors preserve their cache + per-account in-flight coalescing; a tick
|
|
@@ -2340,6 +3230,9 @@ var AccountAllowanceService = class {
|
|
|
2340
3230
|
await this.codexCollector.collectMany(config.codexAccounts ?? [], { refreshAheadMs });
|
|
2341
3231
|
await this.kimiCollector.collectMany(config.kimiAccounts ?? [], { refreshAheadMs });
|
|
2342
3232
|
await this.opencodegoCollector.collectMany(config.opencodegoAccounts ?? [], { refreshAheadMs });
|
|
3233
|
+
await this.grokCollector.collectMany(config.grokAccounts ?? [], { refreshAheadMs });
|
|
3234
|
+
await this.copilotCollector.collectMany(config.copilotAccounts ?? [], { refreshAheadMs });
|
|
3235
|
+
await this.geminiCollector.collectMany(config.geminiAccounts ?? [], { refreshAheadMs });
|
|
2343
3236
|
}
|
|
2344
3237
|
/** Remove a cache row as soon as an account is deleted by the admin path. */
|
|
2345
3238
|
removeAccountSnapshot(providerId, accountId) {
|
|
@@ -2434,7 +3327,7 @@ var ClaudeAllowanceRefreshScheduler = class {
|
|
|
2434
3327
|
var import_node_crypto4 = require("crypto");
|
|
2435
3328
|
var import_node_fs6 = require("fs");
|
|
2436
3329
|
var import_node_path6 = require("path");
|
|
2437
|
-
var
|
|
3330
|
+
var import_AccountAllowanceStore9 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
2438
3331
|
var ACCOUNT_ALLOWANCE_CACHE_VERSION = 1;
|
|
2439
3332
|
var MAX_PERSISTED_ALLOWANCE_SNAPSHOTS = 256;
|
|
2440
3333
|
var MAX_ALLOWANCE_CACHE_BYTES = 1e6;
|
|
@@ -2463,7 +3356,7 @@ var JsonAccountAllowancePersistence = class {
|
|
|
2463
3356
|
save(snapshots) {
|
|
2464
3357
|
const rows = [];
|
|
2465
3358
|
for (const snapshot of snapshots) {
|
|
2466
|
-
const normalized2 = (0,
|
|
3359
|
+
const normalized2 = (0, import_AccountAllowanceStore9.normalizeAccountAllowanceSnapshot)(snapshot);
|
|
2467
3360
|
if (!normalized2) continue;
|
|
2468
3361
|
rows.push(normalized2);
|
|
2469
3362
|
if (rows.length >= MAX_PERSISTED_ALLOWANCE_SNAPSHOTS) break;
|
|
@@ -2739,7 +3632,8 @@ var import_outbound_api5 = require("@omnicross/core/outbound-api");
|
|
|
2739
3632
|
var import_image_generation_types = require("@omnicross/contracts/image-generation-types");
|
|
2740
3633
|
var import_AccountAllowanceScheduling2 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
2741
3634
|
var import_SubscriptionAccountHealth = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
2742
|
-
var
|
|
3635
|
+
var import_upstreamFetch10 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
3636
|
+
var import_core3 = require("@omnicross/core");
|
|
2743
3637
|
|
|
2744
3638
|
// src/image-generation/imagesConfigValidation.ts
|
|
2745
3639
|
var import_outbound_api = require("@omnicross/core/outbound-api");
|
|
@@ -3035,6 +3929,7 @@ async function applyServerConfigTransaction(current, next, deps) {
|
|
|
3035
3929
|
|
|
3036
3930
|
// src/config.ts
|
|
3037
3931
|
var import_node_fs8 = require("fs");
|
|
3932
|
+
var import_core = require("@omnicross/core");
|
|
3038
3933
|
var DEFAULT_ADMIN_PORT = 8766;
|
|
3039
3934
|
function validateAdmin(raw) {
|
|
3040
3935
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
@@ -3091,6 +3986,18 @@ var FORMAT_AXIS_TRANSFORMERS = [
|
|
|
3091
3986
|
"openai-response",
|
|
3092
3987
|
"gemini-code-assist"
|
|
3093
3988
|
];
|
|
3989
|
+
function validateExtraHeaders(raw) {
|
|
3990
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
3991
|
+
const reserved = import_core.EXTRA_HEADER_RESERVED_NAMES;
|
|
3992
|
+
const out = {};
|
|
3993
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
3994
|
+
if (!name.trim()) continue;
|
|
3995
|
+
if (typeof value !== "string") continue;
|
|
3996
|
+
if (reserved.has(name.toLowerCase())) continue;
|
|
3997
|
+
out[name] = value;
|
|
3998
|
+
}
|
|
3999
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
4000
|
+
}
|
|
3094
4001
|
function validateApiKeys(raw) {
|
|
3095
4002
|
if (!Array.isArray(raw)) return void 0;
|
|
3096
4003
|
const out = [];
|
|
@@ -3304,6 +4211,9 @@ function validateProvider(raw, index) {
|
|
|
3304
4211
|
apiVersion,
|
|
3305
4212
|
maxConcurrency,
|
|
3306
4213
|
modelsEndpoint,
|
|
4214
|
+
// Static extra headers: load-guard (reserved names dropped), collapse-to-
|
|
4215
|
+
// undefined; enforced by the outbound header funnel + admin probes.
|
|
4216
|
+
extraHeaders: validateExtraHeaders(p["extraHeaders"]),
|
|
3307
4217
|
// Provider transformer config (app-parity child 5): load-guard, collapse-to-
|
|
3308
4218
|
// undefined; non-secret; ENFORCED via resolveTransformerChain (parity-2 child 2).
|
|
3309
4219
|
// Format-axis entries are stripped by `migrateFormatAxis` — `use[]` is the
|
|
@@ -3373,7 +4283,7 @@ var import_node_crypto6 = require("crypto");
|
|
|
3373
4283
|
var import_node_fs10 = require("fs");
|
|
3374
4284
|
var import_node_os3 = require("os");
|
|
3375
4285
|
var import_node_path10 = require("path");
|
|
3376
|
-
var
|
|
4286
|
+
var import_core2 = require("@omnicross/core");
|
|
3377
4287
|
|
|
3378
4288
|
// src/integrations/codexAuthHelper.ts
|
|
3379
4289
|
var import_node_path8 = require("path");
|
|
@@ -3854,7 +4764,7 @@ var IntegrationManager = class {
|
|
|
3854
4764
|
if (!secret) {
|
|
3855
4765
|
throw new IntegrationConflictError("The selected access key cannot be revealed and cannot power a CLI integration.");
|
|
3856
4766
|
}
|
|
3857
|
-
const effective = [...(0,
|
|
4767
|
+
const effective = [...(0, import_core2.effectiveOutboundPermissions)(row.allowedEndpoints)];
|
|
3858
4768
|
const previousPermissions = row.allowedEndpoints === void 0 ? [...effective] : [...row.allowedEndpoints];
|
|
3859
4769
|
const nextPermissions = [...effective];
|
|
3860
4770
|
for (const required of REQUIRED_PERMISSIONS[client]) {
|
|
@@ -3952,7 +4862,7 @@ var IntegrationManager = class {
|
|
|
3952
4862
|
return { binding, row, secret, created: false };
|
|
3953
4863
|
}
|
|
3954
4864
|
async createManagedClientKey(client, state) {
|
|
3955
|
-
const created = await (0,
|
|
4865
|
+
const created = await (0, import_core2.createIntegrationKey)(
|
|
3956
4866
|
this.options.keyDb,
|
|
3957
4867
|
`Omnicross ${displayClient(client)} integration`,
|
|
3958
4868
|
[...REQUIRED_PERMISSIONS[client]]
|
|
@@ -4044,7 +4954,7 @@ var IntegrationManager = class {
|
|
|
4044
4954
|
const row = rows.find((candidate) => candidate.id === keyId);
|
|
4045
4955
|
if (!row) return { usable: false, message: "The bound access key no longer exists." };
|
|
4046
4956
|
const secret = legacy?.secret ?? await this.options.keyDb.outboundApiKeysReveal(keyId) ?? void 0;
|
|
4047
|
-
const allowedEndpoints = [...(0,
|
|
4957
|
+
const allowedEndpoints = [...(0, import_core2.effectiveOutboundPermissions)(row.allowedEndpoints)];
|
|
4048
4958
|
const status = {
|
|
4049
4959
|
id: row.id,
|
|
4050
4960
|
name: row.name,
|
|
@@ -4088,7 +4998,7 @@ var IntegrationManager = class {
|
|
|
4088
4998
|
}
|
|
4089
4999
|
};
|
|
4090
5000
|
function hasRequiredPermissions(row, client) {
|
|
4091
|
-
const allowed = (0,
|
|
5001
|
+
const allowed = (0, import_core2.effectiveOutboundPermissions)(row.allowedEndpoints);
|
|
4092
5002
|
return REQUIRED_PERMISSIONS[client].every((permission) => allowed.includes(permission));
|
|
4093
5003
|
}
|
|
4094
5004
|
function samePermissions(a, b) {
|
|
@@ -4257,7 +5167,10 @@ function mapPresetToProvider(preset, opts) {
|
|
|
4257
5167
|
apiFormat: resolved.format,
|
|
4258
5168
|
baseUrl: opts.baseUrlOverride ?? preset.api_base_url,
|
|
4259
5169
|
apiKey: opts.key,
|
|
4260
|
-
models: Array.isArray(preset.models) ? preset.models : void 0
|
|
5170
|
+
models: Array.isArray(preset.models) ? preset.models : void 0,
|
|
5171
|
+
// Static identity headers (e.g. the Cline client set) survive the mapping —
|
|
5172
|
+
// the CLI-seeded row needs them as much as an admin-API-created one.
|
|
5173
|
+
extraHeaders: preset.extraHeaders
|
|
4261
5174
|
};
|
|
4262
5175
|
return { provider };
|
|
4263
5176
|
}
|
|
@@ -4282,7 +5195,8 @@ function listMappablePresets() {
|
|
|
4282
5195
|
description: preset.description,
|
|
4283
5196
|
features: preset.features,
|
|
4284
5197
|
website: preset.website,
|
|
4285
|
-
modelsEndpoint: preset.modelsEndpoint
|
|
5198
|
+
modelsEndpoint: preset.modelsEndpoint,
|
|
5199
|
+
extraHeaders: preset.extraHeaders
|
|
4286
5200
|
});
|
|
4287
5201
|
}
|
|
4288
5202
|
return { mappable, excluded };
|
|
@@ -4372,11 +5286,11 @@ function preserveOutboundProxySecrets(incoming, current) {
|
|
|
4372
5286
|
}
|
|
4373
5287
|
|
|
4374
5288
|
// src/proxy/upstreamProxyResolver.ts
|
|
4375
|
-
var
|
|
5289
|
+
var import_upstreamFetch8 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
4376
5290
|
var serverProxy;
|
|
4377
5291
|
function setServerProxyConfig(proxy) {
|
|
4378
5292
|
serverProxy = proxy;
|
|
4379
|
-
(0,
|
|
5293
|
+
(0, import_upstreamFetch8.bumpUpstreamProxyGeneration)();
|
|
4380
5294
|
}
|
|
4381
5295
|
function getServerProxyConfig() {
|
|
4382
5296
|
return serverProxy;
|
|
@@ -4444,7 +5358,7 @@ function createUpstreamProxyResolver(src = {}) {
|
|
|
4444
5358
|
}
|
|
4445
5359
|
|
|
4446
5360
|
// src/admin/accountsOAuth.ts
|
|
4447
|
-
var
|
|
5361
|
+
var import_subscriptions8 = require("@omnicross/subscriptions");
|
|
4448
5362
|
|
|
4449
5363
|
// src/admin/accountsWrite.ts
|
|
4450
5364
|
var VALID_PROVIDER_IDS = [
|
|
@@ -4452,7 +5366,9 @@ var VALID_PROVIDER_IDS = [
|
|
|
4452
5366
|
"codex",
|
|
4453
5367
|
"gemini",
|
|
4454
5368
|
"opencodego",
|
|
4455
|
-
"kimi"
|
|
5369
|
+
"kimi",
|
|
5370
|
+
"grok",
|
|
5371
|
+
"copilot"
|
|
4456
5372
|
];
|
|
4457
5373
|
function asSubscriptionProviderId(id) {
|
|
4458
5374
|
return VALID_PROVIDER_IDS.includes(id) ? id : null;
|
|
@@ -4600,6 +5516,40 @@ function validateKimi(body) {
|
|
|
4600
5516
|
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "deviceId", "lastRefreshedAt", "errorMessage"]);
|
|
4601
5517
|
return out;
|
|
4602
5518
|
}
|
|
5519
|
+
function validateGrok(body) {
|
|
5520
|
+
const authMethod = str(body["authMethod"]);
|
|
5521
|
+
const status = str(body["status"]);
|
|
5522
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
5523
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
5524
|
+
const out = {
|
|
5525
|
+
authMethod,
|
|
5526
|
+
status
|
|
5527
|
+
};
|
|
5528
|
+
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "lastRefreshedAt", "errorMessage"]);
|
|
5529
|
+
return out;
|
|
5530
|
+
}
|
|
5531
|
+
function validateCopilot(body) {
|
|
5532
|
+
const authMethod = str(body["authMethod"]);
|
|
5533
|
+
const status = str(body["status"]);
|
|
5534
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
5535
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
5536
|
+
const out = {
|
|
5537
|
+
authMethod,
|
|
5538
|
+
status
|
|
5539
|
+
};
|
|
5540
|
+
copyOptional(out, body, [
|
|
5541
|
+
"accessToken",
|
|
5542
|
+
"refreshToken",
|
|
5543
|
+
"expiresAt",
|
|
5544
|
+
"accountId",
|
|
5545
|
+
"email",
|
|
5546
|
+
"apiEndpoint",
|
|
5547
|
+
"enterpriseUrl",
|
|
5548
|
+
"lastRefreshedAt",
|
|
5549
|
+
"errorMessage"
|
|
5550
|
+
]);
|
|
5551
|
+
return out;
|
|
5552
|
+
}
|
|
4603
5553
|
function validateOpenCodeGo(body) {
|
|
4604
5554
|
const authMethod = str(body["authMethod"]);
|
|
4605
5555
|
const status = str(body["status"]);
|
|
@@ -4637,6 +5587,10 @@ function validateTokenBody(providerId, body) {
|
|
|
4637
5587
|
return validateOpenCodeGo(body);
|
|
4638
5588
|
case "kimi":
|
|
4639
5589
|
return validateKimi(body);
|
|
5590
|
+
case "grok":
|
|
5591
|
+
return validateGrok(body);
|
|
5592
|
+
case "copilot":
|
|
5593
|
+
return validateCopilot(body);
|
|
4640
5594
|
default:
|
|
4641
5595
|
return null;
|
|
4642
5596
|
}
|
|
@@ -4666,37 +5620,37 @@ async function statusEntryFor(reader, providerId) {
|
|
|
4666
5620
|
|
|
4667
5621
|
// src/admin/accountsOAuth.ts
|
|
4668
5622
|
var OAUTH_HTTP_PROVIDERS = /* @__PURE__ */ new Set(["claude", "gemini"]);
|
|
4669
|
-
function
|
|
5623
|
+
function err5(status, message) {
|
|
4670
5624
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
4671
5625
|
}
|
|
4672
5626
|
function handleOAuthStart(providerId, deps) {
|
|
4673
5627
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
4674
|
-
return
|
|
5628
|
+
return err5(400, `oauth not available for provider '${providerId}'`);
|
|
4675
5629
|
}
|
|
4676
|
-
const flow = providerId === "claude" ?
|
|
5630
|
+
const flow = providerId === "claude" ? import_subscriptions8.claudeOAuth : import_subscriptions8.geminiOAuth;
|
|
4677
5631
|
const { authUrl, codeVerifier, state } = flow.generateAuthParams();
|
|
4678
5632
|
const sessionId = deps.oauthSessions.put({ providerId, codeVerifier, state });
|
|
4679
5633
|
return { status: 200, body: { authUrl, sessionId } };
|
|
4680
5634
|
}
|
|
4681
5635
|
async function handleOAuthComplete(providerId, body, deps) {
|
|
4682
5636
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
4683
|
-
return
|
|
5637
|
+
return err5(400, `oauth not available for provider '${providerId}'`);
|
|
4684
5638
|
}
|
|
4685
5639
|
const sessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : "";
|
|
4686
5640
|
const rawCode = typeof body["code"] === "string" ? body["code"] : "";
|
|
4687
|
-
if (!sessionId) return
|
|
4688
|
-
if (!rawCode) return
|
|
5641
|
+
if (!sessionId) return err5(400, "oauth complete requires { sessionId }");
|
|
5642
|
+
if (!rawCode) return err5(400, "oauth complete requires { code }");
|
|
4689
5643
|
const session = deps.oauthSessions.peek(sessionId);
|
|
4690
|
-
if (!session) return
|
|
5644
|
+
if (!session) return err5(410, "oauth session is unknown, expired, or already used");
|
|
4691
5645
|
if (session.providerId !== providerId) {
|
|
4692
|
-
return
|
|
5646
|
+
return err5(400, `oauth session does not match provider '${providerId}'`);
|
|
4693
5647
|
}
|
|
4694
5648
|
let code = rawCode.trim();
|
|
4695
5649
|
if (providerId === "claude") {
|
|
4696
5650
|
const [splitCode, pastedState] = code.split("#");
|
|
4697
|
-
if (!splitCode) return
|
|
5651
|
+
if (!splitCode) return err5(400, "no authorization code was provided");
|
|
4698
5652
|
if (pastedState && pastedState !== session.state) {
|
|
4699
|
-
return
|
|
5653
|
+
return err5(400, "oauth state did not match (possible CSRF) \u2014 aborting");
|
|
4700
5654
|
}
|
|
4701
5655
|
code = splitCode;
|
|
4702
5656
|
}
|
|
@@ -4706,7 +5660,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
4706
5660
|
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
|
|
4707
5661
|
} catch (exchangeError) {
|
|
4708
5662
|
const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
|
|
4709
|
-
return
|
|
5663
|
+
return err5(502, `oauth token exchange failed for '${providerId}': ${reason}`);
|
|
4710
5664
|
}
|
|
4711
5665
|
deps.oauthSessions.consume(sessionId);
|
|
4712
5666
|
const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
|
|
@@ -4715,7 +5669,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
4715
5669
|
return { status: 200, body: status ? { account: status } : { ok: true } };
|
|
4716
5670
|
}
|
|
4717
5671
|
async function exchangeClaude(code, codeVerifier, state, exchangeFetch) {
|
|
4718
|
-
const result = await
|
|
5672
|
+
const result = await import_subscriptions8.claudeOAuth.exchangeCodeForTokens(
|
|
4719
5673
|
{ authorizationCode: code, codeVerifier, state },
|
|
4720
5674
|
exchangeFetch
|
|
4721
5675
|
);
|
|
@@ -4731,7 +5685,7 @@ async function exchangeClaude(code, codeVerifier, state, exchangeFetch) {
|
|
|
4731
5685
|
};
|
|
4732
5686
|
}
|
|
4733
5687
|
async function exchangeGemini(code, codeVerifier, exchangeFetch) {
|
|
4734
|
-
const result = await
|
|
5688
|
+
const result = await import_subscriptions8.geminiOAuth.exchangeCodeForTokens(code, codeVerifier, exchangeFetch);
|
|
4735
5689
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
4736
5690
|
return {
|
|
4737
5691
|
authMethod: "oauth",
|
|
@@ -5036,8 +5990,8 @@ function errBody(message) {
|
|
|
5036
5990
|
return { error: { type: "admin_api_error", message } };
|
|
5037
5991
|
}
|
|
5038
5992
|
var defaultCommandRunner = (command) => new Promise((resolve11) => {
|
|
5039
|
-
(0, import_node_child_process.exec)(command, { timeout: 18e4 }, (
|
|
5040
|
-
if (
|
|
5993
|
+
(0, import_node_child_process.exec)(command, { timeout: 18e4 }, (err8, _stdout, stderr) => {
|
|
5994
|
+
if (err8) resolve11({ ok: false, error: stderr.trim() || err8.message });
|
|
5041
5995
|
else resolve11({ ok: true });
|
|
5042
5996
|
});
|
|
5043
5997
|
});
|
|
@@ -5083,8 +6037,8 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
5083
6037
|
providerId: typeof body["providerId"] === "string" ? body["providerId"] : void 0,
|
|
5084
6038
|
model: typeof body["model"] === "string" ? body["model"] : void 0
|
|
5085
6039
|
});
|
|
5086
|
-
} catch (
|
|
5087
|
-
return { status: 400, body: errBody(
|
|
6040
|
+
} catch (err8) {
|
|
6041
|
+
return { status: 400, body: errBody(err8 instanceof Error ? err8.message : "no launch target") };
|
|
5088
6042
|
}
|
|
5089
6043
|
const id = (0, import_node_crypto7.randomUUID)();
|
|
5090
6044
|
let leaseId2;
|
|
@@ -5112,9 +6066,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
5112
6066
|
} else {
|
|
5113
6067
|
launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
|
|
5114
6068
|
}
|
|
5115
|
-
} catch (
|
|
5116
|
-
const status =
|
|
5117
|
-
return { status, body: errBody(
|
|
6069
|
+
} catch (err8) {
|
|
6070
|
+
const status = err8 instanceof import_provider_proxy2.RouteLeaseError ? err8.status : 400;
|
|
6071
|
+
return { status, body: errBody(err8 instanceof Error ? err8.message : "failed to build launch env") };
|
|
5118
6072
|
}
|
|
5119
6073
|
const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
|
|
5120
6074
|
const opener = ctx.opener ?? defaultTerminalOpener;
|
|
@@ -5142,9 +6096,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
5142
6096
|
onFailure: onSessionEnd
|
|
5143
6097
|
});
|
|
5144
6098
|
if (cleanup) openerCleanup = cleanup;
|
|
5145
|
-
} catch (
|
|
6099
|
+
} catch (err8) {
|
|
5146
6100
|
onSessionEnd();
|
|
5147
|
-
return { status: 500, body: errBody(
|
|
6101
|
+
return { status: 500, body: errBody(err8 instanceof Error ? err8.message : "failed to open terminal") };
|
|
5148
6102
|
}
|
|
5149
6103
|
if (ended) {
|
|
5150
6104
|
openerCleanup?.();
|
|
@@ -5399,7 +6353,7 @@ async function runSearchLiveChecks(contributions, now = () => (/* @__PURE__ */ n
|
|
|
5399
6353
|
}
|
|
5400
6354
|
|
|
5401
6355
|
// src/search/SearchAssembly.ts
|
|
5402
|
-
var
|
|
6356
|
+
var import_upstreamFetch9 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
5403
6357
|
var import_search = require("@omnicross/core/search");
|
|
5404
6358
|
var import_api2 = require("@omnicross/core/search/api");
|
|
5405
6359
|
var import_http2 = require("@omnicross/core/search/http");
|
|
@@ -5417,7 +6371,7 @@ function searchPolicyFrom(config) {
|
|
|
5417
6371
|
};
|
|
5418
6372
|
}
|
|
5419
6373
|
function resolveSearchUpstreamDispatcher(url) {
|
|
5420
|
-
return (0,
|
|
6374
|
+
return (0, import_upstreamFetch9.resolveUpstreamDispatcher)({ url });
|
|
5421
6375
|
}
|
|
5422
6376
|
var searchUpstreamProxyConfig = createUpstreamProxyResolver();
|
|
5423
6377
|
function resolveSearchUpstreamProxyConfig(url) {
|
|
@@ -5699,7 +6653,7 @@ async function handleSearchQuery(req, res, deps) {
|
|
|
5699
6653
|
// src/admin/searchAdminView.ts
|
|
5700
6654
|
var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
|
|
5701
6655
|
var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
|
|
5702
|
-
function
|
|
6656
|
+
function isRecord5(value) {
|
|
5703
6657
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5704
6658
|
}
|
|
5705
6659
|
function redactSearchServerConfig(search) {
|
|
@@ -5749,13 +6703,13 @@ function resolveSecretField(entry, field, stored) {
|
|
|
5749
6703
|
else delete entry[field];
|
|
5750
6704
|
}
|
|
5751
6705
|
function preserveSearchSecrets(incoming, current) {
|
|
5752
|
-
if (!
|
|
6706
|
+
if (!isRecord5(incoming)) return incoming;
|
|
5753
6707
|
const section = { ...incoming };
|
|
5754
6708
|
const providersValue = section["providers"];
|
|
5755
|
-
if (!
|
|
6709
|
+
if (!isRecord5(providersValue)) return section;
|
|
5756
6710
|
const providers = {};
|
|
5757
6711
|
for (const [id, entryValue] of Object.entries(providersValue)) {
|
|
5758
|
-
if (!
|
|
6712
|
+
if (!isRecord5(entryValue)) {
|
|
5759
6713
|
providers[id] = entryValue;
|
|
5760
6714
|
continue;
|
|
5761
6715
|
}
|
|
@@ -5833,7 +6787,7 @@ function parseKeyPolicyBody(body) {
|
|
|
5833
6787
|
var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
|
|
5834
6788
|
var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
|
|
5835
6789
|
var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
|
|
5836
|
-
function
|
|
6790
|
+
function isRecord6(value) {
|
|
5837
6791
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
5838
6792
|
}
|
|
5839
6793
|
function nonBlank(value) {
|
|
@@ -5853,7 +6807,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5853
6807
|
const ids = /* @__PURE__ */ new Set();
|
|
5854
6808
|
raw.forEach((entry, index) => {
|
|
5855
6809
|
const path2 = `bindings[${index}]`;
|
|
5856
|
-
if (!
|
|
6810
|
+
if (!isRecord6(entry)) {
|
|
5857
6811
|
errors.push(`${path2} must be an object`);
|
|
5858
6812
|
return;
|
|
5859
6813
|
}
|
|
@@ -5882,12 +6836,12 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5882
6836
|
} else if (entry.modelMappings.length > 100) {
|
|
5883
6837
|
errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
|
|
5884
6838
|
} else if (entry.modelMappings.some(
|
|
5885
|
-
(mapping) => !
|
|
6839
|
+
(mapping) => !isRecord6(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
|
|
5886
6840
|
)) {
|
|
5887
6841
|
errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
|
|
5888
6842
|
}
|
|
5889
6843
|
}
|
|
5890
|
-
if (!
|
|
6844
|
+
if (!isRecord6(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
|
|
5891
6845
|
errors.push(`${path2}.target is invalid`);
|
|
5892
6846
|
} else {
|
|
5893
6847
|
if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
|
|
@@ -5902,7 +6856,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5902
6856
|
}
|
|
5903
6857
|
}
|
|
5904
6858
|
if (entry.modelMap !== void 0) {
|
|
5905
|
-
if (!
|
|
6859
|
+
if (!isRecord6(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
|
|
5906
6860
|
errors.push(`${path2}.modelMap must contain string values`);
|
|
5907
6861
|
}
|
|
5908
6862
|
}
|
|
@@ -6185,7 +7139,9 @@ var PROVIDER_KEYS = {
|
|
|
6185
7139
|
accounts: "opencodegoAccounts",
|
|
6186
7140
|
active: "activeOpencodegoAccountId"
|
|
6187
7141
|
},
|
|
6188
|
-
kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" }
|
|
7142
|
+
kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" },
|
|
7143
|
+
grok: { block: "grok", accounts: "grokAccounts", active: "activeGrokAccountId" },
|
|
7144
|
+
copilot: { block: "copilot", accounts: "copilotAccounts", active: "activeCopilotAccountId" }
|
|
6189
7145
|
};
|
|
6190
7146
|
function clone(value) {
|
|
6191
7147
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -6707,7 +7663,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
|
|
|
6707
7663
|
}
|
|
6708
7664
|
|
|
6709
7665
|
// src/admin/adminMigration.ts
|
|
6710
|
-
function
|
|
7666
|
+
function err6(status, message) {
|
|
6711
7667
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
6712
7668
|
}
|
|
6713
7669
|
async function handleExport(body, deps) {
|
|
@@ -6717,30 +7673,30 @@ async function handleExport(body, deps) {
|
|
|
6717
7673
|
return { status: 200, body: { pack, version: BUNDLE_VERSION } };
|
|
6718
7674
|
} catch (error) {
|
|
6719
7675
|
if (error instanceof WeakPassphraseError) {
|
|
6720
|
-
return
|
|
7676
|
+
return err6(400, error.message);
|
|
6721
7677
|
}
|
|
6722
|
-
return
|
|
7678
|
+
return err6(500, "failed to build the migration pack");
|
|
6723
7679
|
}
|
|
6724
7680
|
}
|
|
6725
7681
|
async function handleImport(body, deps) {
|
|
6726
7682
|
const blob = typeof body["blob"] === "string" ? body["blob"] : "";
|
|
6727
7683
|
const passphrase = typeof body["passphrase"] === "string" ? body["passphrase"] : "";
|
|
6728
7684
|
const mode = body["mode"] === "overwrite" ? "overwrite" : "merge";
|
|
6729
|
-
if (!blob) return
|
|
7685
|
+
if (!blob) return err6(400, "import requires { blob }");
|
|
6730
7686
|
try {
|
|
6731
7687
|
const counts = await applyImport(blob, passphrase, mode, deps, deps.parseProviderInput);
|
|
6732
7688
|
return { status: 200, body: counts };
|
|
6733
7689
|
} catch (error) {
|
|
6734
7690
|
if (error instanceof WeakPassphraseError) {
|
|
6735
|
-
return
|
|
7691
|
+
return err6(400, error.message);
|
|
6736
7692
|
}
|
|
6737
|
-
return
|
|
7693
|
+
return err6(400, error instanceof Error ? error.message : "import failed");
|
|
6738
7694
|
}
|
|
6739
7695
|
}
|
|
6740
7696
|
|
|
6741
7697
|
// src/admin/usagePricing.ts
|
|
6742
7698
|
var import_usage = require("@omnicross/core/usage");
|
|
6743
|
-
var
|
|
7699
|
+
var err7 = (status, message) => ({
|
|
6744
7700
|
status,
|
|
6745
7701
|
body: { error: { type: "admin_api_error", message } }
|
|
6746
7702
|
});
|
|
@@ -6753,7 +7709,7 @@ function parseRange(query2) {
|
|
|
6753
7709
|
const startTs = parseFiniteInt(query2.get("startTs"));
|
|
6754
7710
|
const endTs = parseFiniteInt(query2.get("endTs"));
|
|
6755
7711
|
if (startTs === null || endTs === null) {
|
|
6756
|
-
return
|
|
7712
|
+
return err7(400, "startTs and endTs are required finite-integer unix-millis query params");
|
|
6757
7713
|
}
|
|
6758
7714
|
return { startTs, endTs };
|
|
6759
7715
|
}
|
|
@@ -6778,14 +7734,14 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
6778
7734
|
case "timeseries": {
|
|
6779
7735
|
const bucket = query2.get("bucket");
|
|
6780
7736
|
if (bucket !== "hour" && bucket !== "day" && bucket !== "month") {
|
|
6781
|
-
return
|
|
7737
|
+
return err7(400, "bucket must be one of 'hour', 'day', 'month'");
|
|
6782
7738
|
}
|
|
6783
7739
|
const now = Date.now();
|
|
6784
7740
|
const clamped = { startTs: range.startTs, endTs: Math.min(range.endTs, now) };
|
|
6785
7741
|
if (clamped.startTs < clamped.endTs) {
|
|
6786
7742
|
const projected = Math.ceil((clamped.endTs - clamped.startTs) / BUCKET_SPAN_MS[bucket]) + 1;
|
|
6787
7743
|
if (projected > MAX_TIMESERIES_BUCKETS) {
|
|
6788
|
-
return
|
|
7744
|
+
return err7(
|
|
6789
7745
|
400,
|
|
6790
7746
|
`requested range projects ~${projected} '${bucket}' buckets (max ${MAX_TIMESERIES_BUCKETS}); narrow the range or use a coarser bucket`
|
|
6791
7747
|
);
|
|
@@ -6808,7 +7764,7 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
6808
7764
|
};
|
|
6809
7765
|
}
|
|
6810
7766
|
default:
|
|
6811
|
-
return
|
|
7767
|
+
return err7(404, `unknown usage view '${view ?? ""}'`);
|
|
6812
7768
|
}
|
|
6813
7769
|
}
|
|
6814
7770
|
function poolKeyLabels(cfg) {
|
|
@@ -6857,7 +7813,7 @@ async function handlePricingList(deps) {
|
|
|
6857
7813
|
async function handlePricingUpsert(body, deps) {
|
|
6858
7814
|
const input = parsePricingEntryInput(body);
|
|
6859
7815
|
if (!input) {
|
|
6860
|
-
return
|
|
7816
|
+
return err7(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
|
|
6861
7817
|
}
|
|
6862
7818
|
const entry = await deps.pricingEngine.upsertManual(input);
|
|
6863
7819
|
return { status: 200, body: { entry } };
|
|
@@ -6866,7 +7822,7 @@ async function handlePricingDelete(query2, deps) {
|
|
|
6866
7822
|
const providerId = query2.get("providerId")?.trim() ?? "";
|
|
6867
7823
|
const modelId = query2.get("modelId")?.trim() ?? "";
|
|
6868
7824
|
if (!providerId || !modelId) {
|
|
6869
|
-
return
|
|
7825
|
+
return err7(400, "delete requires providerId and modelId query params");
|
|
6870
7826
|
}
|
|
6871
7827
|
const deleted = await deps.pricingStore.delete(providerId, modelId);
|
|
6872
7828
|
if (deleted) await deps.pricingEngine.invalidateCache();
|
|
@@ -6886,13 +7842,13 @@ async function handlePricingFetchLatest(deps) {
|
|
|
6886
7842
|
}
|
|
6887
7843
|
};
|
|
6888
7844
|
} catch (e) {
|
|
6889
|
-
return
|
|
7845
|
+
return err7(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
6890
7846
|
}
|
|
6891
7847
|
}
|
|
6892
7848
|
async function handlePricingResolveConflicts(body, deps) {
|
|
6893
7849
|
const raw = body["resolutions"];
|
|
6894
7850
|
if (!Array.isArray(raw)) {
|
|
6895
|
-
return
|
|
7851
|
+
return err7(400, "resolve-conflicts requires { resolutions: [...] }");
|
|
6896
7852
|
}
|
|
6897
7853
|
const currentRows = await deps.pricingStore.getAll();
|
|
6898
7854
|
const userEditedKeys = new Set(
|
|
@@ -6902,21 +7858,21 @@ async function handlePricingResolveConflicts(body, deps) {
|
|
|
6902
7858
|
const pendingIncoming = /* @__PURE__ */ new Map();
|
|
6903
7859
|
let staleCount = 0;
|
|
6904
7860
|
for (const item of raw) {
|
|
6905
|
-
if (!item || typeof item !== "object") return
|
|
7861
|
+
if (!item || typeof item !== "object") return err7(400, "invalid resolution entry");
|
|
6906
7862
|
const r = item;
|
|
6907
7863
|
const action = r["action"];
|
|
6908
7864
|
if (action !== "overwrite" && action !== "skip") {
|
|
6909
|
-
return
|
|
7865
|
+
return err7(400, "resolution action must be 'overwrite' or 'skip'");
|
|
6910
7866
|
}
|
|
6911
7867
|
const providerId = typeof r["providerId"] === "string" && r["providerId"].trim() ? r["providerId"].trim() : "";
|
|
6912
7868
|
const modelId = typeof r["modelId"] === "string" && r["modelId"].trim() ? r["modelId"].trim() : "";
|
|
6913
7869
|
if (!providerId || !modelId) {
|
|
6914
|
-
return
|
|
7870
|
+
return err7(400, "each resolution requires top-level providerId and modelId");
|
|
6915
7871
|
}
|
|
6916
7872
|
const incoming = parsePricingEntryInput(r["incoming"]);
|
|
6917
|
-
if (!incoming) return
|
|
7873
|
+
if (!incoming) return err7(400, "each resolution must echo a valid incoming pricing entry");
|
|
6918
7874
|
if (incoming.providerId !== providerId || incoming.modelId !== modelId) {
|
|
6919
|
-
return
|
|
7875
|
+
return err7(400, "resolution providerId/modelId must match the echoed incoming entry");
|
|
6920
7876
|
}
|
|
6921
7877
|
const key = `${providerId}::${modelId}`;
|
|
6922
7878
|
if (action === "overwrite" && !userEditedKeys.has(key)) {
|
|
@@ -6961,7 +7917,7 @@ function query(req) {
|
|
|
6961
7917
|
}
|
|
6962
7918
|
function allowanceProvider(value) {
|
|
6963
7919
|
if (!value) return void 0;
|
|
6964
|
-
return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" ? value : null;
|
|
7920
|
+
return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" || value === "grok" || value === "copilot" || value === "gemini" ? value : null;
|
|
6965
7921
|
}
|
|
6966
7922
|
async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
6967
7923
|
if (!service) return writeError2(res, 501, "account allowance service is not available");
|
|
@@ -6976,7 +7932,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
6976
7932
|
const pathProvider = rest.length >= 2 ? rest[0] : null;
|
|
6977
7933
|
const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
|
|
6978
7934
|
if (providerId === null) {
|
|
6979
|
-
return writeError2(res, 400, "providerId must be claude, codex, kimi, or
|
|
7935
|
+
return writeError2(res, 400, "providerId must be claude, codex, kimi, opencodego, grok, copilot, or gemini");
|
|
6980
7936
|
}
|
|
6981
7937
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
6982
7938
|
const allowances = await service.list({ providerId, accountId });
|
|
@@ -7018,6 +7974,36 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7018
7974
|
}
|
|
7019
7975
|
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7020
7976
|
}
|
|
7977
|
+
if (requestedProvider === "copilot") {
|
|
7978
|
+
if (!service.refreshCopilot) {
|
|
7979
|
+
return writeError2(res, 501, "copilot allowance refresh is not available");
|
|
7980
|
+
}
|
|
7981
|
+
const allowances2 = await service.refreshCopilot(accountId);
|
|
7982
|
+
if (accountId && allowances2.length === 0) {
|
|
7983
|
+
return writeError2(res, 404, `Copilot account '${accountId}' not found`);
|
|
7984
|
+
}
|
|
7985
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7986
|
+
}
|
|
7987
|
+
if (requestedProvider === "grok") {
|
|
7988
|
+
if (!service.refreshGrok) {
|
|
7989
|
+
return writeError2(res, 501, "grok allowance refresh is not available");
|
|
7990
|
+
}
|
|
7991
|
+
const allowances2 = await service.refreshGrok(accountId);
|
|
7992
|
+
if (accountId && allowances2.length === 0) {
|
|
7993
|
+
return writeError2(res, 404, `Grok account '${accountId}' not found`);
|
|
7994
|
+
}
|
|
7995
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7996
|
+
}
|
|
7997
|
+
if (requestedProvider === "gemini") {
|
|
7998
|
+
if (!service.refreshGemini) {
|
|
7999
|
+
return writeError2(res, 501, "gemini allowance refresh is not available");
|
|
8000
|
+
}
|
|
8001
|
+
const allowances2 = await service.refreshGemini(accountId);
|
|
8002
|
+
if (accountId && allowances2.length === 0) {
|
|
8003
|
+
return writeError2(res, 404, `Gemini account '${accountId}' not found`);
|
|
8004
|
+
}
|
|
8005
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
8006
|
+
}
|
|
7021
8007
|
const allowances = await service.refreshClaude(accountId);
|
|
7022
8008
|
if (accountId && allowances.length === 0) {
|
|
7023
8009
|
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
@@ -7116,6 +8102,9 @@ function toProviderView(row) {
|
|
|
7116
8102
|
apiVersion: row.apiVersion,
|
|
7117
8103
|
maxConcurrency: row.maxConcurrency,
|
|
7118
8104
|
modelsEndpoint: row.modelsEndpoint,
|
|
8105
|
+
// Static extra headers round-trip VERBATIM (non-secret identity values;
|
|
8106
|
+
// auth/content names were already dropped at the write/load gate).
|
|
8107
|
+
extraHeaders: row.extraHeaders,
|
|
7119
8108
|
// app-parity child 5: transformer config round-trips VERBATIM (non-secret —
|
|
7120
8109
|
// transform-rule names + options, no key material; absent stays absent).
|
|
7121
8110
|
transformer: row.transformer,
|
|
@@ -7185,8 +8174,8 @@ async function handleAdminApi(req, res, path2, deps) {
|
|
|
7185
8174
|
default:
|
|
7186
8175
|
return writeJsonError(res, 404, `unknown admin resource '${resource}'`);
|
|
7187
8176
|
}
|
|
7188
|
-
} catch (
|
|
7189
|
-
writeJsonError(res, 500,
|
|
8177
|
+
} catch (err8) {
|
|
8178
|
+
writeJsonError(res, 500, err8 instanceof Error ? err8.message : String(err8));
|
|
7190
8179
|
}
|
|
7191
8180
|
}
|
|
7192
8181
|
function requestQuery(req) {
|
|
@@ -7344,6 +8333,9 @@ async function handleProviderReorder(req, res, cfg, deps) {
|
|
|
7344
8333
|
persistProviders(cfg, deps);
|
|
7345
8334
|
return writeJson4(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
|
|
7346
8335
|
}
|
|
8336
|
+
function expandRowExtraHeaders(row) {
|
|
8337
|
+
return (0, import_core3.mergeExtraHeaders)({}, row.extraHeaders);
|
|
8338
|
+
}
|
|
7347
8339
|
async function handleDiscoverModels(res, id, cfg) {
|
|
7348
8340
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
7349
8341
|
const row = cfg.providers.find((p) => p.id === id);
|
|
@@ -7357,7 +8349,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
7357
8349
|
try {
|
|
7358
8350
|
const headers = { Accept: "application/json" };
|
|
7359
8351
|
if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
|
|
7360
|
-
|
|
8352
|
+
Object.assign(headers, expandRowExtraHeaders(row));
|
|
8353
|
+
const response = await (0, import_upstreamFetch10.fetchUpstream)(url, { method: "GET", headers }, { providerId: "byo" });
|
|
7361
8354
|
if (!response.ok) {
|
|
7362
8355
|
const text = await response.text().catch(() => "");
|
|
7363
8356
|
let message = text.slice(0, 300);
|
|
@@ -7374,8 +8367,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
7374
8367
|
const data = await response.json();
|
|
7375
8368
|
const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
|
|
7376
8369
|
return writeJson4(res, 200, { models });
|
|
7377
|
-
} catch (
|
|
7378
|
-
const message =
|
|
8370
|
+
} catch (err8) {
|
|
8371
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
7379
8372
|
return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
|
|
7380
8373
|
}
|
|
7381
8374
|
}
|
|
@@ -7414,9 +8407,10 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
7414
8407
|
messages: [{ role: "user", content: prompt }]
|
|
7415
8408
|
};
|
|
7416
8409
|
}
|
|
8410
|
+
Object.assign(headers, expandRowExtraHeaders(row));
|
|
7417
8411
|
const startedAt = Date.now();
|
|
7418
8412
|
try {
|
|
7419
|
-
const response = await (0,
|
|
8413
|
+
const response = await (0, import_upstreamFetch10.fetchUpstream)(
|
|
7420
8414
|
url,
|
|
7421
8415
|
{ method: "POST", headers, body: JSON.stringify(payload) },
|
|
7422
8416
|
{ providerId: "byo" }
|
|
@@ -7438,8 +8432,8 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
7438
8432
|
latencyMs,
|
|
7439
8433
|
sample: extractSampleText(text, row.apiFormat)
|
|
7440
8434
|
});
|
|
7441
|
-
} catch (
|
|
7442
|
-
const message =
|
|
8435
|
+
} catch (err8) {
|
|
8436
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
7443
8437
|
return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
|
|
7444
8438
|
}
|
|
7445
8439
|
}
|
|
@@ -7721,6 +8715,7 @@ function parseProviderInput(body, existing) {
|
|
|
7721
8715
|
const apiVersion = typeof body["apiVersion"] === "string" && body["apiVersion"].length > 0 ? body["apiVersion"] : body["apiVersion"] === null ? void 0 : existing?.apiVersion;
|
|
7722
8716
|
const modelsEndpoint = typeof body["modelsEndpoint"] === "string" && body["modelsEndpoint"].length > 0 ? body["modelsEndpoint"] : body["modelsEndpoint"] === null ? void 0 : existing?.modelsEndpoint;
|
|
7723
8717
|
const maxConcurrency = typeof body["maxConcurrency"] === "number" && Number.isFinite(body["maxConcurrency"]) ? body["maxConcurrency"] : body["maxConcurrency"] === null ? void 0 : existing?.maxConcurrency;
|
|
8718
|
+
const extraHeaders = body["extraHeaders"] === null ? void 0 : body["extraHeaders"] === void 0 ? existing?.extraHeaders : validateExtraHeaders(body["extraHeaders"]);
|
|
7724
8719
|
const transformer = body["transformer"] === null ? void 0 : parseTransformerInput(body["transformer"], existing?.transformer);
|
|
7725
8720
|
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;
|
|
7726
8721
|
const apiModes = body["apiModes"] === null ? void 0 : parseApiModesInput(body["apiModes"], existing?.apiModes);
|
|
@@ -7746,6 +8741,7 @@ function parseProviderInput(body, existing) {
|
|
|
7746
8741
|
apiVersion,
|
|
7747
8742
|
maxConcurrency,
|
|
7748
8743
|
modelsEndpoint,
|
|
8744
|
+
extraHeaders,
|
|
7749
8745
|
transformer: migrated.transformer,
|
|
7750
8746
|
codingPlan,
|
|
7751
8747
|
apiModes,
|
|
@@ -7767,7 +8763,10 @@ function handlePresets(res, method) {
|
|
|
7767
8763
|
description: p.description,
|
|
7768
8764
|
features: p.features,
|
|
7769
8765
|
website: p.website,
|
|
7770
|
-
modelsEndpoint: p.modelsEndpoint
|
|
8766
|
+
modelsEndpoint: p.modelsEndpoint,
|
|
8767
|
+
// Static extra headers ride along so `addFromPreset` can seed them onto the
|
|
8768
|
+
// row (the write gateway re-validates via the shared allowlist).
|
|
8769
|
+
extraHeaders: p.extraHeaders
|
|
7771
8770
|
}));
|
|
7772
8771
|
return writeJson4(res, 200, { presets, excluded });
|
|
7773
8772
|
}
|
|
@@ -8249,12 +9248,12 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
8249
9248
|
}
|
|
8250
9249
|
return writeJson4(res, 200, { ok: true, affected: result.affected });
|
|
8251
9250
|
}
|
|
8252
|
-
if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi") && rest[1] === "oauth" && rest[3] === "status") {
|
|
8253
|
-
const result = rest[0] === "codex" ? handleCodexOAuthStatus(rest[2], deps) : handleKimiOAuthStatus(rest[2], deps);
|
|
9251
|
+
if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[3] === "status") {
|
|
9252
|
+
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);
|
|
8254
9253
|
return writeJson4(res, result.status, result.body);
|
|
8255
9254
|
}
|
|
8256
|
-
if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi") && rest[1] === "oauth" && rest[2]) {
|
|
8257
|
-
const result = rest[0] === "codex" ? handleCodexOAuthCancel(rest[2], deps) : handleKimiOAuthCancel(rest[2], deps);
|
|
9255
|
+
if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[2]) {
|
|
9256
|
+
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);
|
|
8258
9257
|
return writeJson4(res, result.status, result.body);
|
|
8259
9258
|
}
|
|
8260
9259
|
if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
|
|
@@ -8315,6 +9314,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
8315
9314
|
const result2 = await handleKimiOAuthStart(deps);
|
|
8316
9315
|
return writeJson4(res, result2.status, result2.body);
|
|
8317
9316
|
}
|
|
9317
|
+
if (providerId === "grok") {
|
|
9318
|
+
const result2 = await handleGrokOAuthStart(deps);
|
|
9319
|
+
return writeJson4(res, result2.status, result2.body);
|
|
9320
|
+
}
|
|
9321
|
+
if (providerId === "copilot") {
|
|
9322
|
+
const body2 = await readJsonBody4(req);
|
|
9323
|
+
const result2 = await handleCopilotOAuthStart(deps, body2["enterpriseUrl"]);
|
|
9324
|
+
return writeJson4(res, result2.status, result2.body);
|
|
9325
|
+
}
|
|
8318
9326
|
const result = handleOAuthStart(providerId, deps);
|
|
8319
9327
|
return writeJson4(res, result.status, result.body);
|
|
8320
9328
|
}
|
|
@@ -8809,12 +9817,12 @@ async function handlePlayground(req, res, method, deps) {
|
|
|
8809
9817
|
const payload = body["body"];
|
|
8810
9818
|
const status = deps.outboundApiServer.getStatus();
|
|
8811
9819
|
if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
|
|
8812
|
-
const path2 = resolvePlaygroundPath(endpoint,
|
|
9820
|
+
const path2 = resolvePlaygroundPath(endpoint, isRecord7(payload) ? payload : {});
|
|
8813
9821
|
if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
|
|
8814
9822
|
const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
|
|
8815
9823
|
await proxyToOutbound(res, status.port, path2, key, upstreamBody);
|
|
8816
9824
|
}
|
|
8817
|
-
function
|
|
9825
|
+
function isRecord7(v) {
|
|
8818
9826
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
8819
9827
|
}
|
|
8820
9828
|
function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
@@ -8843,8 +9851,8 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
|
8843
9851
|
});
|
|
8844
9852
|
}
|
|
8845
9853
|
);
|
|
8846
|
-
upstream.on("error", (
|
|
8847
|
-
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${
|
|
9854
|
+
upstream.on("error", (err8) => {
|
|
9855
|
+
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err8.message}`);
|
|
8848
9856
|
else res.end();
|
|
8849
9857
|
resolve11();
|
|
8850
9858
|
});
|
|
@@ -8950,7 +9958,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
8950
9958
|
}
|
|
8951
9959
|
|
|
8952
9960
|
// src/admin/version.ts
|
|
8953
|
-
var DAEMON_VERSION = true ? "0.
|
|
9961
|
+
var DAEMON_VERSION = true ? "0.4.1" : "0.0.0-dev";
|
|
8954
9962
|
|
|
8955
9963
|
// src/admin/AdminServer.ts
|
|
8956
9964
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -8993,13 +10001,13 @@ var AdminServer = class {
|
|
|
8993
10001
|
const server = import_node_http2.default.createServer((req, res) => {
|
|
8994
10002
|
this.onRequest(req, res);
|
|
8995
10003
|
});
|
|
8996
|
-
const onError = (
|
|
8997
|
-
if (
|
|
10004
|
+
const onError = (err8) => {
|
|
10005
|
+
if (err8.code === "EADDRINUSE" && port !== 0) {
|
|
8998
10006
|
server.removeListener("error", onError);
|
|
8999
10007
|
this.listen(bindAddr, 0).then(resolve11, reject);
|
|
9000
10008
|
return;
|
|
9001
10009
|
}
|
|
9002
|
-
reject(
|
|
10010
|
+
reject(err8);
|
|
9003
10011
|
};
|
|
9004
10012
|
server.on("error", onError);
|
|
9005
10013
|
server.listen(port, bindAddr, () => {
|
|
@@ -9017,8 +10025,8 @@ var AdminServer = class {
|
|
|
9017
10025
|
}
|
|
9018
10026
|
/** Per-request handler: auth gate (when a token is set) → routing. */
|
|
9019
10027
|
onRequest(req, res) {
|
|
9020
|
-
void this.dispatch(req, res).catch((
|
|
9021
|
-
const message =
|
|
10028
|
+
void this.dispatch(req, res).catch((err8) => {
|
|
10029
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
9022
10030
|
this.deps.logger.error("[AdminServer] unhandled error:", message);
|
|
9023
10031
|
if (!res.headersSent) {
|
|
9024
10032
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
@@ -9282,18 +10290,18 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
9282
10290
|
return;
|
|
9283
10291
|
}
|
|
9284
10292
|
signal?.addEventListener("abort", abort, { once: true });
|
|
9285
|
-
server.on("error", (
|
|
10293
|
+
server.on("error", (err8) => {
|
|
9286
10294
|
if (settled) return;
|
|
9287
10295
|
settled = true;
|
|
9288
10296
|
clearTimeout(timer);
|
|
9289
|
-
if (
|
|
10297
|
+
if (err8.code === "EADDRINUSE") {
|
|
9290
10298
|
reject(
|
|
9291
10299
|
new Error(
|
|
9292
10300
|
`login: cannot bind ${LOOPBACK_HOST}:${LOOPBACK_PORT} (address in use) \u2014 another codex login or process is holding the port`
|
|
9293
10301
|
)
|
|
9294
10302
|
);
|
|
9295
10303
|
} else {
|
|
9296
|
-
reject(
|
|
10304
|
+
reject(err8);
|
|
9297
10305
|
}
|
|
9298
10306
|
});
|
|
9299
10307
|
const timer = setTimeout(() => {
|
|
@@ -9369,21 +10377,22 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
|
|
|
9369
10377
|
}
|
|
9370
10378
|
|
|
9371
10379
|
// src/allowance/ProviderKeyQuotaService.ts
|
|
9372
|
-
var
|
|
10380
|
+
var import_core4 = require("@omnicross/core");
|
|
10381
|
+
var import_upstreamFetch11 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
9373
10382
|
|
|
9374
10383
|
// src/allowance/ProviderKeyQuota.ts
|
|
9375
|
-
var
|
|
9376
|
-
var HOUR_MS2 = 60 *
|
|
9377
|
-
var
|
|
9378
|
-
var WEEK_MS = 7 *
|
|
9379
|
-
var MONTH_MS = 30 *
|
|
9380
|
-
function
|
|
10384
|
+
var MINUTE_MS3 = 6e4;
|
|
10385
|
+
var HOUR_MS2 = 60 * MINUTE_MS3;
|
|
10386
|
+
var DAY_MS3 = 24 * HOUR_MS2;
|
|
10387
|
+
var WEEK_MS = 7 * DAY_MS3;
|
|
10388
|
+
var MONTH_MS = 30 * DAY_MS3;
|
|
10389
|
+
function finiteNumber5(value) {
|
|
9381
10390
|
if (value === null || value === void 0 || value === "") return void 0;
|
|
9382
10391
|
const parsed = typeof value === "number" ? value : Number(value);
|
|
9383
10392
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
9384
10393
|
}
|
|
9385
10394
|
function finitePercent4(value) {
|
|
9386
|
-
const parsed =
|
|
10395
|
+
const parsed = finiteNumber5(value);
|
|
9387
10396
|
return parsed !== void 0 && parsed <= 100 ? parsed : null;
|
|
9388
10397
|
}
|
|
9389
10398
|
function isoInstant3(value) {
|
|
@@ -9391,18 +10400,18 @@ function isoInstant3(value) {
|
|
|
9391
10400
|
const time = Date.parse(value);
|
|
9392
10401
|
if (Number.isFinite(time)) return new Date(time).toISOString();
|
|
9393
10402
|
}
|
|
9394
|
-
const numeric =
|
|
10403
|
+
const numeric = finiteNumber5(value);
|
|
9395
10404
|
if (numeric !== void 0 && numeric > 1e9) {
|
|
9396
10405
|
const ms = numeric > 1e12 ? numeric : numeric * 1e3;
|
|
9397
10406
|
return new Date(ms).toISOString();
|
|
9398
10407
|
}
|
|
9399
10408
|
return void 0;
|
|
9400
10409
|
}
|
|
9401
|
-
function
|
|
10410
|
+
function secondsUntil8(instant, now) {
|
|
9402
10411
|
if (!instant) return void 0;
|
|
9403
10412
|
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
9404
10413
|
}
|
|
9405
|
-
function
|
|
10414
|
+
function isRecord8(value) {
|
|
9406
10415
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
9407
10416
|
}
|
|
9408
10417
|
function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
@@ -9415,7 +10424,7 @@ function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
|
9415
10424
|
}
|
|
9416
10425
|
const host = url.hostname.toLowerCase();
|
|
9417
10426
|
const path2 = url.pathname.toLowerCase();
|
|
9418
|
-
if ((host === "api.z.ai" || host === "open.bigmodel.cn") && path2.includes("/coding")) {
|
|
10427
|
+
if ((host === "api.z.ai" || host === "open.bigmodel.cn") && (path2.includes("/coding") || path2.includes("/anthropic"))) {
|
|
9419
10428
|
return "zai";
|
|
9420
10429
|
}
|
|
9421
10430
|
if ((host === "api.minimax.io" || host === "api.minimaxi.com") && // Token Plan rides the plain openai `/v1` (chat completions) surface; the
|
|
@@ -9425,6 +10434,7 @@ function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
|
9425
10434
|
}
|
|
9426
10435
|
if (host === "api.code.umans.ai") return "umans";
|
|
9427
10436
|
if (host === "api.synthetic.new") return "synthetic";
|
|
10437
|
+
if (host === "api.cline.bot") return "cline-pass";
|
|
9428
10438
|
return null;
|
|
9429
10439
|
}
|
|
9430
10440
|
function providerKeyQuotaUrl(adapter, baseUrl) {
|
|
@@ -9432,6 +10442,7 @@ function providerKeyQuotaUrl(adapter, baseUrl) {
|
|
|
9432
10442
|
if (adapter === "zai") return `${origin}/api/monitor/usage/quota/limit`;
|
|
9433
10443
|
if (adapter === "minimax-token-plan") return `${origin}/v1/token_plan/remains`;
|
|
9434
10444
|
if (adapter === "umans") return `${origin}/v1/usage`;
|
|
10445
|
+
if (adapter === "cline-pass") return `${origin}/api/v1/users/me/plan/usage-limits`;
|
|
9435
10446
|
return `${origin}/v2/quotas`;
|
|
9436
10447
|
}
|
|
9437
10448
|
function providerKeyQuotaAuthHeader(adapter, key) {
|
|
@@ -9443,7 +10454,7 @@ function zaiWindowDurationMs(item) {
|
|
|
9443
10454
|
case 3:
|
|
9444
10455
|
return count * HOUR_MS2;
|
|
9445
10456
|
case 4:
|
|
9446
|
-
return count *
|
|
10457
|
+
return count * DAY_MS3;
|
|
9447
10458
|
case 5:
|
|
9448
10459
|
return count * MONTH_MS;
|
|
9449
10460
|
case 6:
|
|
@@ -9456,8 +10467,8 @@ function zaiWindowIdLabel(durationMs) {
|
|
|
9456
10467
|
if (durationMs === WEEK_MS) return { id: "seven-day", label: "7 days" };
|
|
9457
10468
|
if (durationMs === 5 * HOUR_MS2) return { id: "five-hour", label: "5 hours" };
|
|
9458
10469
|
if (durationMs === MONTH_MS) return { id: "thirty-day", label: "30 days" };
|
|
9459
|
-
if (durationMs !== void 0 && durationMs %
|
|
9460
|
-
const days = durationMs /
|
|
10470
|
+
if (durationMs !== void 0 && durationMs % DAY_MS3 === 0) {
|
|
10471
|
+
const days = durationMs / DAY_MS3;
|
|
9461
10472
|
return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}` };
|
|
9462
10473
|
}
|
|
9463
10474
|
if (durationMs !== void 0 && durationMs % HOUR_MS2 === 0) {
|
|
@@ -9467,23 +10478,23 @@ function zaiWindowIdLabel(durationMs) {
|
|
|
9467
10478
|
return { id: "quota", label: "Quota" };
|
|
9468
10479
|
}
|
|
9469
10480
|
function parseZaiQuotaPayload(payload, now) {
|
|
9470
|
-
if (!
|
|
9471
|
-
const data =
|
|
10481
|
+
if (!isRecord8(payload)) return null;
|
|
10482
|
+
const data = isRecord8(payload["data"]) ? payload["data"] : payload;
|
|
9472
10483
|
if (payload["success"] === false) return null;
|
|
9473
10484
|
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
9474
10485
|
const byWindow = /* @__PURE__ */ new Map();
|
|
9475
10486
|
for (const raw of limits) {
|
|
9476
|
-
if (!
|
|
10487
|
+
if (!isRecord8(raw)) continue;
|
|
9477
10488
|
const item = raw;
|
|
9478
10489
|
if (item.type === void 0) continue;
|
|
9479
10490
|
const details = raw["usageDetails"];
|
|
9480
|
-
if (Array.isArray(details) && details.some((d) =>
|
|
10491
|
+
if (Array.isArray(details) && details.some((d) => isRecord8(d) && d["modelCode"] === "zread")) {
|
|
9481
10492
|
continue;
|
|
9482
10493
|
}
|
|
9483
10494
|
const durationMs = zaiWindowDurationMs(item);
|
|
9484
10495
|
const { id, label } = zaiWindowIdLabel(durationMs);
|
|
9485
|
-
const limit =
|
|
9486
|
-
const used =
|
|
10496
|
+
const limit = finiteNumber5(item.usage);
|
|
10497
|
+
const used = finiteNumber5(item.currentValue);
|
|
9487
10498
|
const fromAbsolute = limit !== void 0 && used !== void 0 && limit > 0 ? Math.min(100, used / limit * 100) : void 0;
|
|
9488
10499
|
const fromPercentage = finitePercent4(item.percentage) ?? void 0;
|
|
9489
10500
|
const usedPercent = fromAbsolute !== void 0 ? Math.round(fromAbsolute * 10) / 10 : fromPercentage;
|
|
@@ -9494,9 +10505,9 @@ function parseZaiQuotaPayload(payload, now) {
|
|
|
9494
10505
|
label,
|
|
9495
10506
|
scope: "all",
|
|
9496
10507
|
usedPercent,
|
|
9497
|
-
...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs /
|
|
10508
|
+
...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs / MINUTE_MS3) } : {},
|
|
9498
10509
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9499
|
-
remainingSeconds:
|
|
10510
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
9500
10511
|
state: "fresh"
|
|
9501
10512
|
};
|
|
9502
10513
|
const existing = byWindow.get(id);
|
|
@@ -9510,21 +10521,21 @@ function parseZaiQuotaPayload(payload, now) {
|
|
|
9510
10521
|
var MINIMAX_STATUS_EXHAUSTED = 2;
|
|
9511
10522
|
var MINIMAX_SHARED_BUCKET = "general";
|
|
9512
10523
|
function parseMiniMaxBucket(value) {
|
|
9513
|
-
if (!
|
|
10524
|
+
if (!isRecord8(value)) return null;
|
|
9514
10525
|
const modelName = typeof value["model_name"] === "string" ? value["model_name"].trim() : "";
|
|
9515
10526
|
if (!modelName) return null;
|
|
9516
10527
|
const instant = (v) => {
|
|
9517
|
-
const n =
|
|
10528
|
+
const n = finiteNumber5(v);
|
|
9518
10529
|
return n !== void 0 && n > 1e9 ? n > 1e12 ? n : n * 1e3 : void 0;
|
|
9519
10530
|
};
|
|
9520
10531
|
return {
|
|
9521
10532
|
modelName,
|
|
9522
10533
|
intervalEnd: instant(value["end_time"]),
|
|
9523
|
-
intervalRemainingPercent:
|
|
9524
|
-
intervalStatus:
|
|
10534
|
+
intervalRemainingPercent: finiteNumber5(value["current_interval_remaining_percent"]),
|
|
10535
|
+
intervalStatus: finiteNumber5(value["current_interval_status"]),
|
|
9525
10536
|
weeklyEnd: instant(value["weekly_end_time"]),
|
|
9526
|
-
weeklyRemainingPercent:
|
|
9527
|
-
weeklyStatus:
|
|
10537
|
+
weeklyRemainingPercent: finiteNumber5(value["current_weekly_remaining_percent"]),
|
|
10538
|
+
weeklyStatus: finiteNumber5(value["current_weekly_status"])
|
|
9528
10539
|
};
|
|
9529
10540
|
}
|
|
9530
10541
|
function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, status, now) {
|
|
@@ -9537,14 +10548,14 @@ function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, s
|
|
|
9537
10548
|
usedPercent,
|
|
9538
10549
|
...windowMinutes !== void 0 ? { windowMinutes } : {},
|
|
9539
10550
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9540
|
-
remainingSeconds:
|
|
10551
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
9541
10552
|
state: usedPercent !== null ? "fresh" : "unavailable"
|
|
9542
10553
|
};
|
|
9543
10554
|
}
|
|
9544
10555
|
function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
9545
|
-
if (!
|
|
10556
|
+
if (!isRecord8(payload)) return null;
|
|
9546
10557
|
const baseResp = payload["base_resp"];
|
|
9547
|
-
if (!
|
|
10558
|
+
if (!isRecord8(baseResp) || baseResp["status_code"] !== 0) return null;
|
|
9548
10559
|
const buckets = Array.isArray(payload["model_remains"]) ? payload["model_remains"] : [];
|
|
9549
10560
|
let general = null;
|
|
9550
10561
|
for (const raw of buckets) {
|
|
@@ -9568,7 +10579,7 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
|
9568
10579
|
minimaxWindow(
|
|
9569
10580
|
"seven-day",
|
|
9570
10581
|
"7 days",
|
|
9571
|
-
Math.round(WEEK_MS /
|
|
10582
|
+
Math.round(WEEK_MS / MINUTE_MS3),
|
|
9572
10583
|
general.weeklyEnd,
|
|
9573
10584
|
general.weeklyRemainingPercent,
|
|
9574
10585
|
general.weeklyStatus,
|
|
@@ -9577,15 +10588,15 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
|
9577
10588
|
];
|
|
9578
10589
|
}
|
|
9579
10590
|
function parseUmansUsagePayload(payload, now) {
|
|
9580
|
-
if (!
|
|
9581
|
-
const limits =
|
|
9582
|
-
const requests = limits &&
|
|
9583
|
-
const usage =
|
|
9584
|
-
const window =
|
|
9585
|
-
const hardCap =
|
|
9586
|
-
const softLimit =
|
|
9587
|
-
const requestsInWindow =
|
|
9588
|
-
const weightedInWindow =
|
|
10591
|
+
if (!isRecord8(payload)) return null;
|
|
10592
|
+
const limits = isRecord8(payload["limits"]) ? payload["limits"] : void 0;
|
|
10593
|
+
const requests = limits && isRecord8(limits["requests"]) ? limits["requests"] : void 0;
|
|
10594
|
+
const usage = isRecord8(payload["usage"]) ? payload["usage"] : void 0;
|
|
10595
|
+
const window = isRecord8(payload["window"]) ? payload["window"] : void 0;
|
|
10596
|
+
const hardCap = finiteNumber5(requests?.["hard_cap"]);
|
|
10597
|
+
const softLimit = finiteNumber5(requests?.["limit"]);
|
|
10598
|
+
const requestsInWindow = finiteNumber5(usage?.["requests_in_window"]);
|
|
10599
|
+
const weightedInWindow = finiteNumber5(usage?.["weighted_in_window"]);
|
|
9589
10600
|
const resetsAt = isoInstant3(window?.["resets_at"]);
|
|
9590
10601
|
let usedPercent = null;
|
|
9591
10602
|
if (hardCap !== void 0 && hardCap > 0 && requestsInWindow !== void 0) {
|
|
@@ -9602,19 +10613,19 @@ function parseUmansUsagePayload(payload, now) {
|
|
|
9602
10613
|
usedPercent,
|
|
9603
10614
|
windowMinutes: 5 * 60,
|
|
9604
10615
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9605
|
-
remainingSeconds:
|
|
10616
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
9606
10617
|
state: "fresh"
|
|
9607
10618
|
}
|
|
9608
10619
|
];
|
|
9609
10620
|
}
|
|
9610
10621
|
function parseSyntheticQuotasPayload(payload, now) {
|
|
9611
|
-
if (!
|
|
9612
|
-
const fiveHour =
|
|
9613
|
-
const weekly =
|
|
10622
|
+
if (!isRecord8(payload)) return null;
|
|
10623
|
+
const fiveHour = isRecord8(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
|
|
10624
|
+
const weekly = isRecord8(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
|
|
9614
10625
|
const windows = [];
|
|
9615
10626
|
if (fiveHour) {
|
|
9616
|
-
const max =
|
|
9617
|
-
const remaining =
|
|
10627
|
+
const max = finiteNumber5(fiveHour["max"]);
|
|
10628
|
+
const remaining = finiteNumber5(fiveHour["remaining"]);
|
|
9618
10629
|
const usedPercent = max !== void 0 && max > 0 && remaining !== void 0 ? Math.round(Math.min(100, (max - remaining) / max * 100) * 10) / 10 : null;
|
|
9619
10630
|
const resetsAt = isoInstant3(fiveHour["nextTickAt"]);
|
|
9620
10631
|
windows.push({
|
|
@@ -9624,12 +10635,12 @@ function parseSyntheticQuotasPayload(payload, now) {
|
|
|
9624
10635
|
usedPercent,
|
|
9625
10636
|
windowMinutes: 5 * 60,
|
|
9626
10637
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9627
|
-
remainingSeconds:
|
|
10638
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
9628
10639
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
9629
10640
|
});
|
|
9630
10641
|
}
|
|
9631
10642
|
if (weekly) {
|
|
9632
|
-
const percentRemaining =
|
|
10643
|
+
const percentRemaining = finiteNumber5(weekly["percentRemaining"]);
|
|
9633
10644
|
const usedPercent = percentRemaining !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - percentRemaining)) * 10) / 10 : null;
|
|
9634
10645
|
const resetsAt = isoInstant3(weekly["nextRegenAt"]);
|
|
9635
10646
|
windows.push({
|
|
@@ -9639,12 +10650,42 @@ function parseSyntheticQuotasPayload(payload, now) {
|
|
|
9639
10650
|
usedPercent,
|
|
9640
10651
|
windowMinutes: 7 * 24 * 60,
|
|
9641
10652
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9642
|
-
remainingSeconds:
|
|
10653
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
9643
10654
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
9644
10655
|
});
|
|
9645
10656
|
}
|
|
9646
10657
|
return windows.length > 0 ? windows : null;
|
|
9647
10658
|
}
|
|
10659
|
+
var CLINE_WINDOW_CONFIG = {
|
|
10660
|
+
five_hour: { id: "five-hour", label: "5 hours", minutes: 5 * 60 },
|
|
10661
|
+
weekly: { id: "seven-day", label: "7 days", minutes: 7 * 24 * 60 },
|
|
10662
|
+
monthly: { id: "thirty-day", label: "30 days", minutes: 30 * 24 * 60 }
|
|
10663
|
+
};
|
|
10664
|
+
function parseClinePassUsageLimitsPayload(payload, now) {
|
|
10665
|
+
if (!isRecord8(payload)) return null;
|
|
10666
|
+
const data = isRecord8(payload["data"]) ? payload["data"] : payload;
|
|
10667
|
+
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
10668
|
+
const windows = [];
|
|
10669
|
+
for (const raw of limits) {
|
|
10670
|
+
if (!isRecord8(raw)) continue;
|
|
10671
|
+
const config = CLINE_WINDOW_CONFIG[typeof raw["type"] === "string" ? raw["type"] : ""];
|
|
10672
|
+
if (!config) continue;
|
|
10673
|
+
const usedPercent = finitePercent4(raw["percentUsed"]);
|
|
10674
|
+
if (usedPercent === null) continue;
|
|
10675
|
+
const resetsAt = isoInstant3(raw["resetsAt"]);
|
|
10676
|
+
windows.push({
|
|
10677
|
+
id: config.id,
|
|
10678
|
+
label: config.label,
|
|
10679
|
+
scope: "all",
|
|
10680
|
+
usedPercent,
|
|
10681
|
+
windowMinutes: config.minutes,
|
|
10682
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
10683
|
+
remainingSeconds: secondsUntil8(resetsAt, now),
|
|
10684
|
+
state: "fresh"
|
|
10685
|
+
});
|
|
10686
|
+
}
|
|
10687
|
+
return windows.length > 0 ? windows : null;
|
|
10688
|
+
}
|
|
9648
10689
|
|
|
9649
10690
|
// src/allowance/ProviderKeyQuotaService.ts
|
|
9650
10691
|
function parseQuotaPayload(adapter, payload, now) {
|
|
@@ -9657,6 +10698,8 @@ function parseQuotaPayload(adapter, payload, now) {
|
|
|
9657
10698
|
return parseUmansUsagePayload(payload, now);
|
|
9658
10699
|
case "synthetic":
|
|
9659
10700
|
return parseSyntheticQuotasPayload(payload, now);
|
|
10701
|
+
case "cline-pass":
|
|
10702
|
+
return parseClinePassUsageLimitsPayload(payload, now);
|
|
9660
10703
|
}
|
|
9661
10704
|
}
|
|
9662
10705
|
var PROVIDER_KEY_QUOTA_CACHE_MS = 5 * 6e4;
|
|
@@ -9676,7 +10719,7 @@ function rowKeyEntries(row) {
|
|
|
9676
10719
|
return [];
|
|
9677
10720
|
}
|
|
9678
10721
|
var ProviderKeyQuotaService = class {
|
|
9679
|
-
constructor(box, fetchImpl = (url, init) => (0,
|
|
10722
|
+
constructor(box, fetchImpl = (url, init) => (0, import_upstreamFetch11.fetchUpstream)(url, init, { redactBodies: true }), now = Date.now) {
|
|
9680
10723
|
this.box = box;
|
|
9681
10724
|
this.fetchImpl = fetchImpl;
|
|
9682
10725
|
this.now = now;
|
|
@@ -9738,7 +10781,10 @@ var ProviderKeyQuotaService = class {
|
|
|
9738
10781
|
headers: {
|
|
9739
10782
|
Authorization: providerKeyQuotaAuthHeader(adapter, key),
|
|
9740
10783
|
Accept: "application/json",
|
|
9741
|
-
"Content-Type": "application/json"
|
|
10784
|
+
"Content-Type": "application/json",
|
|
10785
|
+
// The row's static identity headers ride along — the Cline usage
|
|
10786
|
+
// endpoint sits behind the SAME client-identity 403 gate as inference.
|
|
10787
|
+
...(0, import_core4.mergeExtraHeaders)({}, row.extraHeaders)
|
|
9742
10788
|
},
|
|
9743
10789
|
signal: AbortSignal.timeout(15e3)
|
|
9744
10790
|
});
|
|
@@ -9776,7 +10822,7 @@ var ProviderKeyQuotaService = class {
|
|
|
9776
10822
|
// src/image-generation/ImageDoctorService.ts
|
|
9777
10823
|
var import_image_generation = require("@omnicross/core/image-generation");
|
|
9778
10824
|
var import_outbound_api7 = require("@omnicross/core/outbound-api");
|
|
9779
|
-
var
|
|
10825
|
+
var import_subscriptions9 = require("@omnicross/subscriptions");
|
|
9780
10826
|
|
|
9781
10827
|
// src/image-generation/FileCodexImageCapabilityEvidenceSource.ts
|
|
9782
10828
|
var import_node_crypto13 = require("crypto");
|
|
@@ -10214,7 +11260,7 @@ function createImageDoctorService(options) {
|
|
|
10214
11260
|
paths,
|
|
10215
11261
|
ttlMs: config.evidenceTtlMs
|
|
10216
11262
|
}));
|
|
10217
|
-
const createLiveVerifier = options.createLiveVerifier ?? ((strategy, config) => (0,
|
|
11263
|
+
const createLiveVerifier = options.createLiveVerifier ?? ((strategy, config) => (0, import_subscriptions9.createCodexImageLiveVerifier)({
|
|
10218
11264
|
authStrategy: strategy,
|
|
10219
11265
|
generationTimeoutMs: config.queue.generationTimeoutMs
|
|
10220
11266
|
}));
|
|
@@ -10576,7 +11622,7 @@ var ImageCleanupService = class {
|
|
|
10576
11622
|
var import_node_crypto16 = require("crypto");
|
|
10577
11623
|
var import_image_generation5 = require("@omnicross/core/image-generation");
|
|
10578
11624
|
var import_outbound_api8 = require("@omnicross/core/outbound-api");
|
|
10579
|
-
var
|
|
11625
|
+
var import_subscriptions10 = require("@omnicross/subscriptions");
|
|
10580
11626
|
|
|
10581
11627
|
// src/image-generation/ImageApiRuntimeResolver.ts
|
|
10582
11628
|
var import_node_crypto14 = require("crypto");
|
|
@@ -11107,7 +12153,7 @@ function createImageRuntimeGeneration(options) {
|
|
|
11107
12153
|
now: options.now ?? Date.now,
|
|
11108
12154
|
referenceStore: options.storage.referenceStore,
|
|
11109
12155
|
stateStore: options.storage.stateStore
|
|
11110
|
-
}) : (0,
|
|
12156
|
+
}) : (0, import_subscriptions10.createCodexSubscriptionImageProvider)({
|
|
11111
12157
|
authStrategy,
|
|
11112
12158
|
evidenceSource: generationEvidenceSource,
|
|
11113
12159
|
executionScheduler: scheduler,
|
|
@@ -14251,7 +15297,7 @@ var ImageRuntimeManager = class {
|
|
|
14251
15297
|
};
|
|
14252
15298
|
|
|
14253
15299
|
// src/ports/ConfigFileProviderConfigSource.ts
|
|
14254
|
-
var
|
|
15300
|
+
var import_core5 = require("@omnicross/core");
|
|
14255
15301
|
var EMPTY_CHAIN = {
|
|
14256
15302
|
providerTransformers: [],
|
|
14257
15303
|
modelTransformers: []
|
|
@@ -14276,8 +15322,8 @@ var ConfigFileProviderConfigSource = class {
|
|
|
14276
15322
|
reloadHook;
|
|
14277
15323
|
constructor(config) {
|
|
14278
15324
|
for (const p of config.providers) this.providers.set(p.id, p);
|
|
14279
|
-
this.transformerService = new
|
|
14280
|
-
void (0,
|
|
15325
|
+
this.transformerService = new import_core5.TransformerService();
|
|
15326
|
+
void (0, import_core5.registerBuiltinTransformers)(this.transformerService);
|
|
14281
15327
|
}
|
|
14282
15328
|
// ── Reload hook (key-pool design D4) ───────────────────────────────────────
|
|
14283
15329
|
/**
|
|
@@ -14298,7 +15344,7 @@ var ConfigFileProviderConfigSource = class {
|
|
|
14298
15344
|
}
|
|
14299
15345
|
/** Await the built-in transformer registration (tests await this before dispatch). */
|
|
14300
15346
|
async ready() {
|
|
14301
|
-
await (0,
|
|
15347
|
+
await (0, import_core5.registerBuiltinTransformers)(this.transformerService);
|
|
14302
15348
|
}
|
|
14303
15349
|
// ── Hot-reload seam (admin dashboard, RT3 design D6) ───────────────────────
|
|
14304
15350
|
/**
|
|
@@ -14409,6 +15455,10 @@ function toLLMProvider(row) {
|
|
|
14409
15455
|
// `parseProviderInput`), so customizations are preserved (the row value wins).
|
|
14410
15456
|
apiModes: row.apiModes,
|
|
14411
15457
|
selectedApiModeId: row.selectedApiModeId,
|
|
15458
|
+
// Static extra request headers ride along verbatim (load-guarded — no
|
|
15459
|
+
// auth/content names); core's `getProviderHeaders` merges them into every
|
|
15460
|
+
// BYO request, and the same-format relay path inherits that funnel.
|
|
15461
|
+
extraHeaders: row.extraHeaders,
|
|
14412
15462
|
// Official-Anthropic signature handling only matters for the Anthropic
|
|
14413
15463
|
// ingress (deferred → 502); leave it off for the BYO transform path.
|
|
14414
15464
|
isOfficial: false
|
|
@@ -15774,7 +16824,7 @@ function bucketLabel(bucketStartTs, bucket) {
|
|
|
15774
16824
|
|
|
15775
16825
|
// src/ports/JsonOutboundKeyDb.ts
|
|
15776
16826
|
var import_node_fs23 = require("fs");
|
|
15777
|
-
var
|
|
16827
|
+
var import_core6 = require("@omnicross/core");
|
|
15778
16828
|
|
|
15779
16829
|
// src/ports/atomicFile.ts
|
|
15780
16830
|
var import_node_crypto22 = require("crypto");
|
|
@@ -15896,7 +16946,7 @@ var JsonOutboundKeyDb = class {
|
|
|
15896
16946
|
});
|
|
15897
16947
|
}
|
|
15898
16948
|
async outboundApiKeysSetPermissions(id, permissions) {
|
|
15899
|
-
const exact = (0,
|
|
16949
|
+
const exact = (0, import_core6.validateOutboundPermissions)(permissions);
|
|
15900
16950
|
return this.mutateRow(id, (row) => {
|
|
15901
16951
|
if (row.revokedAt !== null) return false;
|
|
15902
16952
|
row.allowedEndpoints = [...exact];
|
|
@@ -16333,9 +17383,9 @@ var import_node_fs28 = require("fs");
|
|
|
16333
17383
|
var import_node_path27 = require("path");
|
|
16334
17384
|
var import_SubscriptionAccountHealth2 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
16335
17385
|
var import_AccountAllowanceScheduling3 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
16336
|
-
var
|
|
17386
|
+
var import_upstreamFetch12 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
16337
17387
|
var import_SubscriptionIdentityStore2 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
16338
|
-
var
|
|
17388
|
+
var import_subscriptions11 = require("@omnicross/subscriptions");
|
|
16339
17389
|
|
|
16340
17390
|
// src/ports/account-sync.ts
|
|
16341
17391
|
function viewOf(tokens) {
|
|
@@ -16483,7 +17533,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16483
17533
|
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
16484
17534
|
*/
|
|
16485
17535
|
buildRefreshFetch(providerId, accountId) {
|
|
16486
|
-
return this.fetchImpl ?? ((url, init) => (0,
|
|
17536
|
+
return this.fetchImpl ?? ((url, init) => (0, import_upstreamFetch12.fetchUpstream)(url, init, { providerId, accountId, redactBodies: true }));
|
|
16487
17537
|
}
|
|
16488
17538
|
/**
|
|
16489
17539
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -16524,7 +17574,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16524
17574
|
* other hot reads. Never returns token material.
|
|
16525
17575
|
*/
|
|
16526
17576
|
getAccountProxy(providerId, accountId) {
|
|
16527
|
-
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi") {
|
|
17577
|
+
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi" && providerId !== "grok" && providerId !== "copilot") {
|
|
16528
17578
|
return void 0;
|
|
16529
17579
|
}
|
|
16530
17580
|
return getAccountProxy(this.readConfig(), providerId, accountId);
|
|
@@ -16543,7 +17593,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16543
17593
|
const fingerprintOn = identityStore.isEnabled();
|
|
16544
17594
|
const now = Date.now();
|
|
16545
17595
|
const out = {};
|
|
16546
|
-
for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi"]) {
|
|
17596
|
+
for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi", "grok", "copilot"]) {
|
|
16547
17597
|
const sanitized = sanitizeAccounts(config, provider);
|
|
16548
17598
|
if (sanitized.length === 0) continue;
|
|
16549
17599
|
for (const account of sanitized) {
|
|
@@ -16609,7 +17659,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16609
17659
|
this.materializeMigration(config);
|
|
16610
17660
|
const refreshFetch = this.buildRefreshFetch("claude", capturedId);
|
|
16611
17661
|
try {
|
|
16612
|
-
const result = await
|
|
17662
|
+
const result = await import_subscriptions11.claudeOAuth.refreshAccessToken(claude.refreshToken, refreshFetch);
|
|
16613
17663
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
16614
17664
|
const next = {
|
|
16615
17665
|
...claude,
|
|
@@ -16644,7 +17694,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16644
17694
|
this.materializeMigration(config);
|
|
16645
17695
|
const refreshFetch = this.buildRefreshFetch("codex", capturedId);
|
|
16646
17696
|
try {
|
|
16647
|
-
const result = await
|
|
17697
|
+
const result = await import_subscriptions11.codexOAuth.refreshAccessToken(codex.refreshToken, refreshFetch);
|
|
16648
17698
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
16649
17699
|
const next = {
|
|
16650
17700
|
...codex,
|
|
@@ -16682,7 +17732,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16682
17732
|
this.materializeMigration(config);
|
|
16683
17733
|
const refreshFetch = this.buildRefreshFetch("gemini", capturedId);
|
|
16684
17734
|
try {
|
|
16685
|
-
const result = await
|
|
17735
|
+
const result = await import_subscriptions11.geminiOAuth.refreshAccessToken(gemini.refreshToken, refreshFetch);
|
|
16686
17736
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
16687
17737
|
const next = {
|
|
16688
17738
|
...gemini,
|
|
@@ -16718,10 +17768,10 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16718
17768
|
this.materializeMigration(config);
|
|
16719
17769
|
const refreshFetch = this.buildRefreshFetch("kimi", capturedId);
|
|
16720
17770
|
try {
|
|
16721
|
-
const result = await
|
|
17771
|
+
const result = await import_subscriptions11.kimiOAuth.refreshAccessToken(
|
|
16722
17772
|
kimi.refreshToken,
|
|
16723
17773
|
refreshFetch,
|
|
16724
|
-
|
|
17774
|
+
import_subscriptions11.kimiOAuth.kimiFingerprintHeaders(kimi.deviceId)
|
|
16725
17775
|
);
|
|
16726
17776
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
16727
17777
|
const next = {
|
|
@@ -16742,6 +17792,66 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16742
17792
|
}
|
|
16743
17793
|
});
|
|
16744
17794
|
}
|
|
17795
|
+
/**
|
|
17796
|
+
* Refresh the Grok (xAI SuperGrok) OAuth access token. The token endpoint is
|
|
17797
|
+
* resolved through OIDC discovery on every refresh (process-cached 1h by the
|
|
17798
|
+
* flow module) so a rotated endpoint document is picked up without a daemon
|
|
17799
|
+
* restart. HONEST `false` when no refresh_token.
|
|
17800
|
+
*/
|
|
17801
|
+
async refreshGrokToken() {
|
|
17802
|
+
return this.coalesce("grok:active", async () => {
|
|
17803
|
+
const config = this.readConfig();
|
|
17804
|
+
const active = getActiveAccount(config, "grok");
|
|
17805
|
+
const grok = active?.tokens;
|
|
17806
|
+
if (!active || !grok?.refreshToken) return false;
|
|
17807
|
+
const capturedId = active.id;
|
|
17808
|
+
this.materializeMigration(config);
|
|
17809
|
+
const refreshFetch = this.buildRefreshFetch("grok", capturedId);
|
|
17810
|
+
try {
|
|
17811
|
+
const tokenEndpoint = await import_subscriptions11.grokOAuth.resolveGrokTokenEndpoint(refreshFetch);
|
|
17812
|
+
const result = await import_subscriptions11.grokOAuth.refreshGrokAccessToken(grok.refreshToken, tokenEndpoint, refreshFetch);
|
|
17813
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
17814
|
+
const next = {
|
|
17815
|
+
...grok,
|
|
17816
|
+
accessToken: result.accessToken,
|
|
17817
|
+
refreshToken: result.refreshToken,
|
|
17818
|
+
expiresAt,
|
|
17819
|
+
status: "authorized",
|
|
17820
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17821
|
+
errorMessage: void 0,
|
|
17822
|
+
syncWarning: void 0
|
|
17823
|
+
};
|
|
17824
|
+
this.writeBackById("grok", capturedId, next);
|
|
17825
|
+
return true;
|
|
17826
|
+
} catch (error) {
|
|
17827
|
+
this.markExpiredById("grok", capturedId, grok, error);
|
|
17828
|
+
return false;
|
|
17829
|
+
}
|
|
17830
|
+
});
|
|
17831
|
+
}
|
|
17832
|
+
/**
|
|
17833
|
+
* "Refresh" a GitHub Copilot token — there is nothing to refresh (ghu_
|
|
17834
|
+
* tokens are long-lived with no exchange endpoint). A call here means the
|
|
17835
|
+
* strategy saw a 401 (the token was revoked); mark the account `expired`
|
|
17836
|
+
* with a re-authenticate message and return `false` (the proxy then declines
|
|
17837
|
+
* the retry instead of looping on a dead token).
|
|
17838
|
+
*/
|
|
17839
|
+
async refreshCopilotToken() {
|
|
17840
|
+
return this.coalesce("copilot:active", async () => {
|
|
17841
|
+
const config = this.readConfig();
|
|
17842
|
+
const active = getActiveAccount(config, "copilot");
|
|
17843
|
+
const copilot = active?.tokens;
|
|
17844
|
+
if (!active || !copilot?.accessToken) return false;
|
|
17845
|
+
this.materializeMigration(config);
|
|
17846
|
+
this.markExpiredById(
|
|
17847
|
+
"copilot",
|
|
17848
|
+
active.id,
|
|
17849
|
+
copilot,
|
|
17850
|
+
new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account")
|
|
17851
|
+
);
|
|
17852
|
+
return false;
|
|
17853
|
+
});
|
|
17854
|
+
}
|
|
16745
17855
|
/**
|
|
16746
17856
|
* Refresh a SPECIFIC managed account by id (background scheduler sweep and
|
|
16747
17857
|
* account-pool resolution). It uses only that account's stored refresh
|
|
@@ -16794,7 +17904,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16794
17904
|
}
|
|
16795
17905
|
const oauth = account.tokens;
|
|
16796
17906
|
if (!oauth.accessToken) return null;
|
|
16797
|
-
if (providerId === "codex" || providerId === "gemini" || providerId === "kimi") {
|
|
17907
|
+
if (providerId === "codex" || providerId === "gemini" || providerId === "kimi" || providerId === "grok" || providerId === "copilot") {
|
|
16798
17908
|
const expiresAtMs = oauth.expiresAt ? Date.parse(oauth.expiresAt) : 0;
|
|
16799
17909
|
const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - ACCOUNT_REFRESH_LEAD_MS;
|
|
16800
17910
|
if (expiringSoon && oauth.refreshToken) {
|
|
@@ -16887,10 +17997,10 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16887
17997
|
if (provider === "kimi") {
|
|
16888
17998
|
const account = accountId ? getAccountById(this.readConfig(), "kimi", accountId) : void 0;
|
|
16889
17999
|
const deviceId = account?.tokens?.deviceId;
|
|
16890
|
-
const r2 = await
|
|
18000
|
+
const r2 = await import_subscriptions11.kimiOAuth.refreshAccessToken(
|
|
16891
18001
|
refreshToken,
|
|
16892
18002
|
refreshFetch,
|
|
16893
|
-
|
|
18003
|
+
import_subscriptions11.kimiOAuth.kimiFingerprintHeaders(deviceId)
|
|
16894
18004
|
);
|
|
16895
18005
|
return {
|
|
16896
18006
|
accessToken: r2.accessToken,
|
|
@@ -16898,7 +18008,19 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16898
18008
|
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
16899
18009
|
};
|
|
16900
18010
|
}
|
|
16901
|
-
|
|
18011
|
+
if (provider === "grok") {
|
|
18012
|
+
const tokenEndpoint = await import_subscriptions11.grokOAuth.resolveGrokTokenEndpoint(refreshFetch);
|
|
18013
|
+
const r2 = await import_subscriptions11.grokOAuth.refreshGrokAccessToken(refreshToken, tokenEndpoint, refreshFetch);
|
|
18014
|
+
return {
|
|
18015
|
+
accessToken: r2.accessToken,
|
|
18016
|
+
refreshToken: r2.refreshToken,
|
|
18017
|
+
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
18018
|
+
};
|
|
18019
|
+
}
|
|
18020
|
+
if (provider === "copilot") {
|
|
18021
|
+
throw new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account");
|
|
18022
|
+
}
|
|
18023
|
+
const flow = provider === "claude" ? import_subscriptions11.claudeOAuth : provider === "codex" ? import_subscriptions11.codexOAuth : import_subscriptions11.geminiOAuth;
|
|
16902
18024
|
const r = await flow.refreshAccessToken(refreshToken, refreshFetch);
|
|
16903
18025
|
return {
|
|
16904
18026
|
accessToken: r.accessToken,
|
|
@@ -17141,7 +18263,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
17141
18263
|
};
|
|
17142
18264
|
|
|
17143
18265
|
// src/AccountHealthProbeScheduler.ts
|
|
17144
|
-
var
|
|
18266
|
+
var import_upstreamFetch13 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
17145
18267
|
|
|
17146
18268
|
// src/probe/CodexGenerationProbe.ts
|
|
17147
18269
|
var import_codexCliHeaders = require("@omnicross/core/provider-proxy/identity/codexCliHeaders");
|
|
@@ -17284,7 +18406,16 @@ var PROVIDER_PROBE_PLANS = {
|
|
|
17284
18406
|
// Kimi's `GET /coding/v1/usages` is a verified FREE authed GET (the allowance
|
|
17285
18407
|
// collector uses it), but the probe path also needs the fingerprint headers —
|
|
17286
18408
|
// keep the probe local until the collector covers the health surface.
|
|
17287
|
-
kimi: { kind: "local" }
|
|
18409
|
+
kimi: { kind: "local" },
|
|
18410
|
+
// Grok's billing proxy is a verified FREE authed GET (the allowance collector
|
|
18411
|
+
// uses it) but it REJECTS non-OAuth credentials and sits on a separate host
|
|
18412
|
+
// with its own product-gate header — keep the probe local, the collector
|
|
18413
|
+
// owns the health surface.
|
|
18414
|
+
grok: { kind: "local" },
|
|
18415
|
+
// The Copilot quota endpoint (copilot_internal/user) is a verified FREE
|
|
18416
|
+
// authed GET but lives on api.github.com with its own auth dialect and a
|
|
18417
|
+
// monthly-only window — the allowance collector owns the health surface.
|
|
18418
|
+
copilot: { kind: "local" }
|
|
17288
18419
|
};
|
|
17289
18420
|
function probePlanFor(providerId) {
|
|
17290
18421
|
return PROVIDER_PROBE_PLANS[providerId] ?? { kind: "local" };
|
|
@@ -17306,7 +18437,7 @@ var AccountHealthProbeScheduler = class {
|
|
|
17306
18437
|
this.logger = logger;
|
|
17307
18438
|
this.config = config;
|
|
17308
18439
|
this.now = opts.now ?? Date.now;
|
|
17309
|
-
this.fetchImpl = opts.fetchImpl ??
|
|
18440
|
+
this.fetchImpl = opts.fetchImpl ?? import_upstreamFetch13.fetchUpstream;
|
|
17310
18441
|
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
17311
18442
|
this.planFor = opts.planFor ?? probePlanFor;
|
|
17312
18443
|
}
|
|
@@ -17856,7 +18987,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
17856
18987
|
}
|
|
17857
18988
|
|
|
17858
18989
|
// src/audit/AuditPruneSweeper.ts
|
|
17859
|
-
var
|
|
18990
|
+
var DAY_MS4 = 24 * 60 * 6e4;
|
|
17860
18991
|
var SWEEP_INTERVAL_MS2 = 60 * 6e4;
|
|
17861
18992
|
var ARCHIVE_BATCH = 64;
|
|
17862
18993
|
var AuditPruneSweeper = class {
|
|
@@ -17920,7 +19051,7 @@ var AuditPruneSweeper = class {
|
|
|
17920
19051
|
this.sweeping = true;
|
|
17921
19052
|
try {
|
|
17922
19053
|
if (!(0, import_node_fs30.existsSync)(this.auditDir)) return 0;
|
|
17923
|
-
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) *
|
|
19054
|
+
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS4;
|
|
17924
19055
|
let removed = 0;
|
|
17925
19056
|
for (const name of (0, import_node_fs30.readdirSync)(this.auditDir)) {
|
|
17926
19057
|
const dateMs = auditFileDateMs(name);
|
|
@@ -18177,7 +19308,7 @@ async function closeAll(writers) {
|
|
|
18177
19308
|
// src/usage/UsagePruneSweeper.ts
|
|
18178
19309
|
var import_promises8 = require("fs/promises");
|
|
18179
19310
|
var import_node_path31 = require("path");
|
|
18180
|
-
var
|
|
19311
|
+
var DAY_MS5 = 24 * 60 * 6e4;
|
|
18181
19312
|
var SWEEP_INTERVAL_MS3 = 60 * 6e4;
|
|
18182
19313
|
var DEFAULT_USAGE_RETENTION_DAYS = 90;
|
|
18183
19314
|
var UsagePruneSweeper = class {
|
|
@@ -18234,7 +19365,7 @@ var UsagePruneSweeper = class {
|
|
|
18234
19365
|
this.sweeping = true;
|
|
18235
19366
|
try {
|
|
18236
19367
|
const retentionDays = this.config.retentionDays ?? DEFAULT_USAGE_RETENTION_DAYS;
|
|
18237
|
-
const cutoff = this.todayMidnight() - (retentionDays - 1) *
|
|
19368
|
+
const cutoff = this.todayMidnight() - (retentionDays - 1) * DAY_MS5;
|
|
18238
19369
|
let removed = 0;
|
|
18239
19370
|
for (const entry of await listUsageDays(this.usageDir)) {
|
|
18240
19371
|
if (!entry.hasShard) continue;
|
|
@@ -18462,7 +19593,7 @@ var AuditWriter = class {
|
|
|
18462
19593
|
var import_node_fs34 = require("fs");
|
|
18463
19594
|
var import_node_crypto24 = require("crypto");
|
|
18464
19595
|
var import_node_path34 = require("path");
|
|
18465
|
-
var
|
|
19596
|
+
var import_upstreamFetch14 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
18466
19597
|
|
|
18467
19598
|
// src/billing/billingFiles.ts
|
|
18468
19599
|
var BILLING_FILE_RE = /^billing-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
|
|
@@ -18485,7 +19616,7 @@ var BillingPublisher = class {
|
|
|
18485
19616
|
constructor(billingDir, logger, opts = {}) {
|
|
18486
19617
|
this.billingDir = billingDir;
|
|
18487
19618
|
this.logger = logger;
|
|
18488
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
19619
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch14.fetchUpstream)(url, init));
|
|
18489
19620
|
this.defer = opts.defer ?? ((fn) => setTimeout(fn, 0));
|
|
18490
19621
|
this.timeoutMs = opts.timeoutMs ?? BILLING_POST_TIMEOUT_MS;
|
|
18491
19622
|
this.now = opts.now ?? Date.now;
|
|
@@ -18735,7 +19866,7 @@ var BillingRetrySweeper = class {
|
|
|
18735
19866
|
// src/TokenRefreshScheduler.ts
|
|
18736
19867
|
var REFRESH_LEAD_MS2 = 5 * 6e4;
|
|
18737
19868
|
var SWEEP_INTERVAL_MS5 = 6e4;
|
|
18738
|
-
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
|
|
19869
|
+
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
|
|
18739
19870
|
var TokenRefreshScheduler = class {
|
|
18740
19871
|
constructor(store, logger, intervalMs = SWEEP_INTERVAL_MS5, leadMs = REFRESH_LEAD_MS2) {
|
|
18741
19872
|
this.store = store;
|
|
@@ -18820,6 +19951,12 @@ var TokenRefreshScheduler = class {
|
|
|
18820
19951
|
return this.store.refreshGeminiToken();
|
|
18821
19952
|
case "kimi":
|
|
18822
19953
|
return this.store.refreshKimiToken();
|
|
19954
|
+
case "grok":
|
|
19955
|
+
return this.store.refreshGrokToken();
|
|
19956
|
+
// ghu_ tokens never near-expire (far-future expiresAt), so the sweep
|
|
19957
|
+
// never reaches this — the branch exists for union totality.
|
|
19958
|
+
case "copilot":
|
|
19959
|
+
return this.store.refreshCopilotToken();
|
|
18823
19960
|
}
|
|
18824
19961
|
}
|
|
18825
19962
|
};
|
|
@@ -18896,7 +20033,7 @@ function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
|
18896
20033
|
|
|
18897
20034
|
// src/webhook/WebhookDispatcher.ts
|
|
18898
20035
|
var import_node_crypto25 = require("crypto");
|
|
18899
|
-
var
|
|
20036
|
+
var import_upstreamFetch15 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
18900
20037
|
var WEBHOOK_MAX_ATTEMPTS = 3;
|
|
18901
20038
|
var WEBHOOK_QUEUE_MAX = 1e3;
|
|
18902
20039
|
var WEBHOOK_SEND_TIMEOUT_MS = 1e4;
|
|
@@ -18916,7 +20053,7 @@ var WebhookDispatcher = class {
|
|
|
18916
20053
|
sleep;
|
|
18917
20054
|
now;
|
|
18918
20055
|
constructor(opts = {}) {
|
|
18919
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
20056
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch15.fetchUpstream)(url, init));
|
|
18920
20057
|
this.logger = opts.logger;
|
|
18921
20058
|
this.maxAttempts = opts.maxAttempts ?? WEBHOOK_MAX_ATTEMPTS;
|
|
18922
20059
|
this.queueMax = opts.queueMax ?? WEBHOOK_QUEUE_MAX;
|
|
@@ -19002,8 +20139,8 @@ var WebhookDispatcher = class {
|
|
|
19002
20139
|
signal: AbortSignal.timeout(this.timeoutMs)
|
|
19003
20140
|
});
|
|
19004
20141
|
return res.ok ? { ok: true, status: res.status } : { ok: false, status: res.status };
|
|
19005
|
-
} catch (
|
|
19006
|
-
return { ok: false, error:
|
|
20142
|
+
} catch (err8) {
|
|
20143
|
+
return { ok: false, error: err8 instanceof Error ? err8.message : String(err8) };
|
|
19007
20144
|
}
|
|
19008
20145
|
}
|
|
19009
20146
|
/**
|
|
@@ -19066,7 +20203,7 @@ function feishuText(event) {
|
|
|
19066
20203
|
// src/bootstrap.ts
|
|
19067
20204
|
var activeImageRuntimeBootstrapSession;
|
|
19068
20205
|
function createImageRuntimeBootstrapSession(initialGeneration) {
|
|
19069
|
-
const openAIOperationRegistry = new
|
|
20206
|
+
const openAIOperationRegistry = new import_core7.OpenAIOperationRegistry();
|
|
19070
20207
|
const imageRuntimeManager = new ImageRuntimeManager(initialGeneration);
|
|
19071
20208
|
const unregisterContributions = [];
|
|
19072
20209
|
try {
|
|
@@ -19140,12 +20277,12 @@ function buildDaemon(config, paths) {
|
|
|
19140
20277
|
setSecretBox(secretBox3);
|
|
19141
20278
|
setSecretBox2(secretBox3);
|
|
19142
20279
|
const decryptedConfig = decryptConfigSecrets(config, secretBox3);
|
|
19143
|
-
const accountAllowanceStore = new
|
|
20280
|
+
const accountAllowanceStore = new import_AccountAllowanceStore10.AccountAllowanceStore(
|
|
19144
20281
|
Date.now,
|
|
19145
20282
|
void 0,
|
|
19146
20283
|
new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
|
|
19147
20284
|
);
|
|
19148
|
-
(0,
|
|
20285
|
+
(0, import_AccountAllowanceStore10.setSharedAccountAllowanceStore)(accountAllowanceStore);
|
|
19149
20286
|
(0, import_AccountAllowanceScheduling5.getSharedAccountAllowanceScheduling)().configure(
|
|
19150
20287
|
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
19151
20288
|
);
|
|
@@ -19170,20 +20307,20 @@ function buildDaemon(config, paths) {
|
|
|
19170
20307
|
claudeAllowanceRefreshScheduler.configure(
|
|
19171
20308
|
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
19172
20309
|
);
|
|
19173
|
-
const subscriptionAccounts = new
|
|
19174
|
-
(0,
|
|
19175
|
-
const subscriptionRegistry = new
|
|
20310
|
+
const subscriptionAccounts = new import_subscriptions12.SubscriptionAccountService(credentialStore);
|
|
20311
|
+
(0, import_subscriptions12.setSubscriptionAccountService)(subscriptionAccounts);
|
|
20312
|
+
const subscriptionRegistry = new import_subscriptions12.SubscriptionProviderRegistry(
|
|
19176
20313
|
subscriptionAccounts,
|
|
19177
20314
|
credentialStore
|
|
19178
20315
|
);
|
|
19179
|
-
(0,
|
|
20316
|
+
(0, import_subscriptions12.setSubscriptionProviderRegistry)(subscriptionRegistry);
|
|
19180
20317
|
setServerProxyConfig(decryptedConfig.server?.proxy);
|
|
19181
|
-
(0,
|
|
20318
|
+
(0, import_upstreamFetch16.setUpstreamProxyResolver)(
|
|
19182
20319
|
createUpstreamProxyResolver({
|
|
19183
20320
|
getAccountProxy: (providerId, accountId) => credentialStore.getAccountProxy(providerId, accountId)
|
|
19184
20321
|
})
|
|
19185
20322
|
);
|
|
19186
|
-
(0, import_gemini_code_assist_resolver.setGeminiCodeAssistResolver)((0,
|
|
20323
|
+
(0, import_gemini_code_assist_resolver.setGeminiCodeAssistResolver)((0, import_GeminiCodeAssistProjectResolver2.getGeminiCodeAssistProjectResolver)());
|
|
19187
20324
|
const autoDisableStore = new AutoDisableStore();
|
|
19188
20325
|
const providerKeyQuotaService = new ProviderKeyQuotaService(secretBox3);
|
|
19189
20326
|
const apiKeyPool = new import_ApiKeyPoolService.ApiKeyPoolService(
|
|
@@ -19202,7 +20339,7 @@ function buildDaemon(config, paths) {
|
|
|
19202
20339
|
const pricingEngine = new import_usage2.PricingEngine(pricingStore, logger, {
|
|
19203
20340
|
// Catalog egress follows the same global/env proxy policy as every other
|
|
19204
20341
|
// daemon upstream call; no provider/account override applies here.
|
|
19205
|
-
fetchImpl: ((input, init) => (0,
|
|
20342
|
+
fetchImpl: ((input, init) => (0, import_upstreamFetch16.fetchUpstream)(String(input), init ?? {}))
|
|
19206
20343
|
});
|
|
19207
20344
|
const pricingRefreshScheduler = new PricingRefreshScheduler(
|
|
19208
20345
|
pricingEngine,
|
|
@@ -19487,7 +20624,7 @@ function buildDaemon(config, paths) {
|
|
|
19487
20624
|
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
19488
20625
|
// excluded from the upstream trace, so a failing login left no evidence.
|
|
19489
20626
|
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
19490
|
-
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0,
|
|
20627
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0, import_upstreamFetch16.fetchUpstream)(url, init, { providerId, redactBodies: true }),
|
|
19491
20628
|
subscriptionAccountAppender: credentialStore,
|
|
19492
20629
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
19493
20630
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -19499,6 +20636,9 @@ function buildDaemon(config, paths) {
|
|
|
19499
20636
|
// paste; the app shows the verification URL + user code and polls the
|
|
19500
20637
|
// token-free status). Token captured + persisted daemon-side.
|
|
19501
20638
|
kimiSessions: new CodexOAuthSessionStore(),
|
|
20639
|
+
// Grok interactive OAuth — the same async DEVICE-CODE shape as kimi.
|
|
20640
|
+
grokSessions: new CodexOAuthSessionStore(),
|
|
20641
|
+
copilotSessions: new CodexOAuthSessionStore(),
|
|
19502
20642
|
// Migration pack (app-parity child 6, design D2/D3) — the concrete credential
|
|
19503
20643
|
// store provides BOTH the full DECRYPTED read (`getFullConfig`, export) and
|
|
19504
20644
|
// the multi-account append (`appendProviderAccount`, import re-encrypts at-
|
|
@@ -19557,7 +20697,7 @@ function buildDaemon(config, paths) {
|
|
|
19557
20697
|
});
|
|
19558
20698
|
const webhookDispatcher = new WebhookDispatcher({
|
|
19559
20699
|
logger,
|
|
19560
|
-
fetchImpl: (url, init) => (0,
|
|
20700
|
+
fetchImpl: (url, init) => (0, import_upstreamFetch16.fetchUpstream)(url, init)
|
|
19561
20701
|
});
|
|
19562
20702
|
setWebhookRuntime(webhookDispatcher, (0, import_SubscriptionAccountHealth4.getSharedAccountHealth)());
|
|
19563
20703
|
const auditWriter = new AuditWriter(auditDir, logger);
|
|
@@ -19871,11 +21011,11 @@ async function runLiveProbe(url, key, fetchImpl = fetch) {
|
|
|
19871
21011
|
status: res.status,
|
|
19872
21012
|
estimateHeader: res.headers.get("x-omnicross-count-estimate")
|
|
19873
21013
|
};
|
|
19874
|
-
} catch (
|
|
21014
|
+
} catch (err8) {
|
|
19875
21015
|
return {
|
|
19876
21016
|
status: null,
|
|
19877
21017
|
estimateHeader: null,
|
|
19878
|
-
error:
|
|
21018
|
+
error: err8 instanceof Error ? err8.message : String(err8)
|
|
19879
21019
|
};
|
|
19880
21020
|
}
|
|
19881
21021
|
}
|
|
@@ -20282,9 +21422,9 @@ async function runLaunch(argv, deps) {
|
|
|
20282
21422
|
await daemon.llmConfig.ready();
|
|
20283
21423
|
await daemon.migrateUsageStore();
|
|
20284
21424
|
await daemon.providerProxy.start();
|
|
20285
|
-
} catch (
|
|
21425
|
+
} catch (err8) {
|
|
20286
21426
|
await shutdownLaunchDaemon(daemon);
|
|
20287
|
-
throw
|
|
21427
|
+
throw err8;
|
|
20288
21428
|
}
|
|
20289
21429
|
let launch;
|
|
20290
21430
|
try {
|
|
@@ -20292,9 +21432,9 @@ async function runLaunch(argv, deps) {
|
|
|
20292
21432
|
providerId: values.provider,
|
|
20293
21433
|
model: values.model
|
|
20294
21434
|
});
|
|
20295
|
-
} catch (
|
|
21435
|
+
} catch (err8) {
|
|
20296
21436
|
await shutdownLaunchDaemon(daemon);
|
|
20297
|
-
throw
|
|
21437
|
+
throw err8;
|
|
20298
21438
|
}
|
|
20299
21439
|
try {
|
|
20300
21440
|
const plan = buildCliSpawnPlan({
|
|
@@ -20399,9 +21539,9 @@ function spawnCliInherit(plan) {
|
|
|
20399
21539
|
process.removeListener("SIGINT", onSignal);
|
|
20400
21540
|
process.removeListener("SIGTERM", onSignal);
|
|
20401
21541
|
};
|
|
20402
|
-
child.on("error", (
|
|
21542
|
+
child.on("error", (err8) => {
|
|
20403
21543
|
detach();
|
|
20404
|
-
if (
|
|
21544
|
+
if (err8.code === "ENOENT") {
|
|
20405
21545
|
reject(
|
|
20406
21546
|
new Error(
|
|
20407
21547
|
`launch: "${plan.command}" not found on PATH \u2014 install the CLI first.`
|
|
@@ -20409,7 +21549,7 @@ function spawnCliInherit(plan) {
|
|
|
20409
21549
|
);
|
|
20410
21550
|
return;
|
|
20411
21551
|
}
|
|
20412
|
-
reject(
|
|
21552
|
+
reject(err8);
|
|
20413
21553
|
});
|
|
20414
21554
|
child.on("exit", (code, signal) => {
|
|
20415
21555
|
detach();
|
|
@@ -20422,9 +21562,9 @@ function spawnCliInherit(plan) {
|
|
|
20422
21562
|
var import_node_child_process3 = require("child_process");
|
|
20423
21563
|
var import_node_readline2 = require("readline");
|
|
20424
21564
|
var import_node_util7 = require("util");
|
|
20425
|
-
var
|
|
20426
|
-
var
|
|
20427
|
-
var PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
|
|
21565
|
+
var import_upstreamFetch17 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
21566
|
+
var import_subscriptions13 = require("@omnicross/subscriptions");
|
|
21567
|
+
var PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
|
|
20428
21568
|
async function runLogin(argv, deps) {
|
|
20429
21569
|
const { values, positionals } = (0, import_node_util7.parseArgs)({
|
|
20430
21570
|
args: argv,
|
|
@@ -20432,7 +21572,9 @@ async function runLogin(argv, deps) {
|
|
|
20432
21572
|
config: { type: "string", short: "c" },
|
|
20433
21573
|
"master-key-file": { type: "string" },
|
|
20434
21574
|
// Optional user label for the appended account (multi-account).
|
|
20435
|
-
label: { type: "string" }
|
|
21575
|
+
label: { type: "string" },
|
|
21576
|
+
// Optional GitHub Enterprise domain for `login copilot` (GHE accounts).
|
|
21577
|
+
enterprise: { type: "string" }
|
|
20436
21578
|
},
|
|
20437
21579
|
allowPositionals: true
|
|
20438
21580
|
});
|
|
@@ -20446,46 +21588,55 @@ async function runLogin(argv, deps) {
|
|
|
20446
21588
|
if (!values.config) {
|
|
20447
21589
|
throw new Error("login: --config <path> is required");
|
|
20448
21590
|
}
|
|
21591
|
+
if (values.enterprise !== void 0 && provider !== "copilot") {
|
|
21592
|
+
throw new Error("login: --enterprise is only supported for the copilot provider");
|
|
21593
|
+
}
|
|
21594
|
+
const enterpriseDomain = values.enterprise !== void 0 ? import_subscriptions13.copilotOAuth.normalizeCopilotEnterpriseDomain(values.enterprise) : void 0;
|
|
20449
21595
|
const resolved = {
|
|
20450
21596
|
openBrowser: deps?.openBrowser ?? openBrowser,
|
|
20451
21597
|
promptPaste: deps?.promptPaste ?? promptPaste,
|
|
20452
21598
|
awaitLoopback: deps?.awaitLoopback ?? ((state) => awaitLoopbackCode(state)),
|
|
20453
21599
|
awaitKimiDevice: deps?.awaitKimiDevice ?? ((fetchImpl) => runKimiDeviceFlow(fetchImpl, resolvedOpenBrowser)),
|
|
21600
|
+
awaitGrokDevice: deps?.awaitGrokDevice ?? ((fetchImpl) => runGrokDeviceFlow(fetchImpl, resolvedOpenBrowser)),
|
|
21601
|
+
awaitCopilotDevice: deps?.awaitCopilotDevice ?? ((fetchImpl, enterpriseUrl) => runCopilotDeviceFlow(fetchImpl, resolvedOpenBrowser, enterpriseUrl)),
|
|
20454
21602
|
tokensFetch: deps?.tokensFetch
|
|
20455
21603
|
};
|
|
20456
21604
|
const resolvedOpenBrowser = resolved.openBrowser;
|
|
20457
21605
|
const box = resolveSecretBox(values["master-key-file"]);
|
|
20458
21606
|
setSecretBox(box);
|
|
20459
|
-
(0,
|
|
21607
|
+
(0, import_upstreamFetch17.setUpstreamProxyResolver)(createUpstreamProxyResolver());
|
|
20460
21608
|
try {
|
|
20461
21609
|
const tokensPath = defaultTokensPath(values.config);
|
|
20462
|
-
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => (0,
|
|
21610
|
+
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => (0, import_upstreamFetch17.fetchUpstream)(url, init, { providerId: provider, redactBodies: true }));
|
|
20463
21611
|
const store = new JsonSubscriptionCredentialStore(tokensPath, box, exchangeFetch);
|
|
20464
21612
|
const expiresAt = await runProviderLogin(
|
|
20465
21613
|
provider,
|
|
20466
21614
|
store,
|
|
20467
21615
|
resolved,
|
|
20468
21616
|
exchangeFetch,
|
|
20469
|
-
values.label
|
|
21617
|
+
values.label,
|
|
21618
|
+
enterpriseDomain
|
|
20470
21619
|
);
|
|
20471
21620
|
console.info(`Logged in to '${provider}' \u2192 ${tokensPath}`);
|
|
20472
21621
|
console.info(` token: [stored, encrypted] expiresAt: ${expiresAt ?? "n/a"}`);
|
|
20473
21622
|
} finally {
|
|
20474
21623
|
setSecretBox(null);
|
|
20475
|
-
(0,
|
|
21624
|
+
(0, import_upstreamFetch17.setUpstreamProxyResolver)(null);
|
|
20476
21625
|
}
|
|
20477
21626
|
}
|
|
20478
|
-
async function runProviderLogin(provider, store, deps, exchangeFetch, label) {
|
|
21627
|
+
async function runProviderLogin(provider, store, deps, exchangeFetch, label, enterpriseUrl) {
|
|
20479
21628
|
if (provider === "codex") return loginCodex(store, deps, exchangeFetch, label);
|
|
20480
21629
|
if (provider === "claude") return loginClaude(store, deps, exchangeFetch, label);
|
|
20481
21630
|
if (provider === "kimi") return loginKimi(store, deps, exchangeFetch, label);
|
|
21631
|
+
if (provider === "grok") return loginGrok(store, deps, exchangeFetch, label);
|
|
21632
|
+
if (provider === "copilot") return loginCopilot(store, deps, exchangeFetch, label, enterpriseUrl);
|
|
20482
21633
|
return loginGemini(store, deps, exchangeFetch, label);
|
|
20483
21634
|
}
|
|
20484
21635
|
async function loginCodex(store, deps, exchangeFetch, label) {
|
|
20485
|
-
const { authUrl, codeVerifier, state } =
|
|
21636
|
+
const { authUrl, codeVerifier, state } = import_subscriptions13.codexOAuth.generateAuthParams();
|
|
20486
21637
|
await presentUrl(authUrl, deps);
|
|
20487
21638
|
const code = await deps.awaitLoopback(state);
|
|
20488
|
-
const result = await
|
|
21639
|
+
const result = await import_subscriptions13.codexOAuth.exchangeCodeForTokens(
|
|
20489
21640
|
{ authorizationCode: code, codeVerifier, state },
|
|
20490
21641
|
exchangeFetch
|
|
20491
21642
|
);
|
|
@@ -20504,7 +21655,7 @@ async function loginCodex(store, deps, exchangeFetch, label) {
|
|
|
20504
21655
|
return expiresAt;
|
|
20505
21656
|
}
|
|
20506
21657
|
async function loginClaude(store, deps, exchangeFetch, label) {
|
|
20507
|
-
const { authUrl, codeVerifier, state } =
|
|
21658
|
+
const { authUrl, codeVerifier, state } = import_subscriptions13.claudeOAuth.generateAuthParams();
|
|
20508
21659
|
await presentUrl(authUrl, deps);
|
|
20509
21660
|
const pasted = (await deps.promptPaste("Paste the authorization code (code#state): ")).trim();
|
|
20510
21661
|
const [code, pastedState] = pasted.split("#");
|
|
@@ -20512,7 +21663,7 @@ async function loginClaude(store, deps, exchangeFetch, label) {
|
|
|
20512
21663
|
if (pastedState && pastedState !== state) {
|
|
20513
21664
|
throw new Error("login: pasted state did not match (possible CSRF) \u2014 aborting");
|
|
20514
21665
|
}
|
|
20515
|
-
const result = await
|
|
21666
|
+
const result = await import_subscriptions13.claudeOAuth.exchangeCodeForTokens(
|
|
20516
21667
|
{ authorizationCode: code, codeVerifier, state },
|
|
20517
21668
|
exchangeFetch
|
|
20518
21669
|
);
|
|
@@ -20531,11 +21682,11 @@ async function loginClaude(store, deps, exchangeFetch, label) {
|
|
|
20531
21682
|
return expiresAt;
|
|
20532
21683
|
}
|
|
20533
21684
|
async function loginGemini(store, deps, exchangeFetch, label) {
|
|
20534
|
-
const { authUrl, codeVerifier } =
|
|
21685
|
+
const { authUrl, codeVerifier } = import_subscriptions13.geminiOAuth.generateAuthParams();
|
|
20535
21686
|
await presentUrl(authUrl, deps);
|
|
20536
21687
|
const code = (await deps.promptPaste("Paste the authorization code: ")).trim();
|
|
20537
21688
|
if (!code) throw new Error("login: no authorization code was pasted");
|
|
20538
|
-
const result = await
|
|
21689
|
+
const result = await import_subscriptions13.geminiOAuth.exchangeCodeForTokens(code, codeVerifier, exchangeFetch);
|
|
20539
21690
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
20540
21691
|
const block = {
|
|
20541
21692
|
authMethod: "oauth",
|
|
@@ -20550,9 +21701,9 @@ async function loginGemini(store, deps, exchangeFetch, label) {
|
|
|
20550
21701
|
return expiresAt;
|
|
20551
21702
|
}
|
|
20552
21703
|
async function runKimiDeviceFlow(exchangeFetch, openBrowserFn) {
|
|
20553
|
-
const deviceId =
|
|
20554
|
-
const fingerprint =
|
|
20555
|
-
const authorization = await
|
|
21704
|
+
const deviceId = import_subscriptions13.kimiOAuth.generateKimiDeviceId();
|
|
21705
|
+
const fingerprint = import_subscriptions13.kimiOAuth.kimiFingerprintHeaders(deviceId);
|
|
21706
|
+
const authorization = await import_subscriptions13.kimiOAuth.requestDeviceAuthorization(exchangeFetch, fingerprint);
|
|
20556
21707
|
const url = authorization.verificationUriComplete ?? authorization.verificationUri;
|
|
20557
21708
|
console.info("Open this URL in your browser and approve the request:");
|
|
20558
21709
|
console.info(` ${url}`);
|
|
@@ -20560,14 +21711,14 @@ async function runKimiDeviceFlow(exchangeFetch, openBrowserFn) {
|
|
|
20560
21711
|
console.info(` Then enter this code: ${authorization.userCode}`);
|
|
20561
21712
|
}
|
|
20562
21713
|
await openBrowserFn(url).catch(() => false);
|
|
20563
|
-
const result = await
|
|
21714
|
+
const result = await import_subscriptions13.kimiOAuth.awaitDeviceToken(authorization, exchangeFetch, {
|
|
20564
21715
|
fingerprint,
|
|
20565
21716
|
onPending: () => process.stdout.write(".")
|
|
20566
21717
|
});
|
|
20567
21718
|
console.info("");
|
|
20568
21719
|
return {
|
|
20569
21720
|
...result,
|
|
20570
|
-
accountId:
|
|
21721
|
+
accountId: import_subscriptions13.kimiOAuth.kimiAccountIdFromAccessToken(result.accessToken),
|
|
20571
21722
|
deviceId
|
|
20572
21723
|
};
|
|
20573
21724
|
}
|
|
@@ -20588,6 +21739,90 @@ async function loginKimi(store, deps, exchangeFetch, label) {
|
|
|
20588
21739
|
logMasked("kimi", result.accessToken);
|
|
20589
21740
|
return expiresAt;
|
|
20590
21741
|
}
|
|
21742
|
+
async function runGrokDeviceFlow(exchangeFetch, openBrowserFn) {
|
|
21743
|
+
const tokenEndpoint = await import_subscriptions13.grokOAuth.resolveGrokTokenEndpoint(exchangeFetch);
|
|
21744
|
+
const authorization = await import_subscriptions13.grokOAuth.requestGrokDeviceAuthorization(exchangeFetch);
|
|
21745
|
+
const url = authorization.verificationUriComplete ?? authorization.verificationUri;
|
|
21746
|
+
console.info("Open this URL in your browser and approve the request:");
|
|
21747
|
+
console.info(` ${url}`);
|
|
21748
|
+
if (!authorization.verificationUriComplete) {
|
|
21749
|
+
console.info(` Then enter this code: ${authorization.userCode}`);
|
|
21750
|
+
}
|
|
21751
|
+
await openBrowserFn(url).catch(() => false);
|
|
21752
|
+
const result = await import_subscriptions13.grokOAuth.awaitGrokDeviceToken(authorization, tokenEndpoint, exchangeFetch, {
|
|
21753
|
+
onPending: () => process.stdout.write(".")
|
|
21754
|
+
});
|
|
21755
|
+
console.info("");
|
|
21756
|
+
return {
|
|
21757
|
+
...result,
|
|
21758
|
+
accountId: import_subscriptions13.grokOAuth.grokAccountIdFromAccessToken(result.accessToken)
|
|
21759
|
+
};
|
|
21760
|
+
}
|
|
21761
|
+
async function loginGrok(store, deps, exchangeFetch, label) {
|
|
21762
|
+
const result = await deps.awaitGrokDevice(exchangeFetch);
|
|
21763
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
21764
|
+
const block = {
|
|
21765
|
+
authMethod: "oauth",
|
|
21766
|
+
status: "authorized",
|
|
21767
|
+
accessToken: result.accessToken,
|
|
21768
|
+
refreshToken: result.refreshToken,
|
|
21769
|
+
expiresAt,
|
|
21770
|
+
...result.accountId ? { accountId: result.accountId } : {},
|
|
21771
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21772
|
+
};
|
|
21773
|
+
await store.appendProviderAccount("grok", block, label);
|
|
21774
|
+
logMasked("grok", result.accessToken);
|
|
21775
|
+
return expiresAt;
|
|
21776
|
+
}
|
|
21777
|
+
async function runCopilotDeviceFlow(exchangeFetch, openBrowserFn, enterpriseUrl) {
|
|
21778
|
+
if (enterpriseUrl) console.info(`Using GitHub Enterprise host: ${enterpriseUrl}`);
|
|
21779
|
+
const authorization = await import_subscriptions13.copilotOAuth.requestCopilotDeviceAuthorization(exchangeFetch, enterpriseUrl);
|
|
21780
|
+
const url = authorization.verificationUri;
|
|
21781
|
+
console.info("Open this URL in your browser and approve the request:");
|
|
21782
|
+
console.info(` ${url}`);
|
|
21783
|
+
console.info(` Then enter this code: ${authorization.userCode}`);
|
|
21784
|
+
await openBrowserFn(url).catch(() => false);
|
|
21785
|
+
const result = await import_subscriptions13.copilotOAuth.awaitCopilotDeviceToken(authorization, exchangeFetch, {
|
|
21786
|
+
onPending: () => process.stdout.write("."),
|
|
21787
|
+
...enterpriseUrl ? { enterpriseUrl } : {}
|
|
21788
|
+
});
|
|
21789
|
+
console.info("");
|
|
21790
|
+
const identity = await import_subscriptions13.copilotOAuth.fetchCopilotIdentity(result.accessToken, exchangeFetch, enterpriseUrl);
|
|
21791
|
+
const apiEndpoint = await import_subscriptions13.copilotOAuth.discoverCopilotApiEndpoint(result.accessToken, exchangeFetch, enterpriseUrl);
|
|
21792
|
+
console.info("Enabling Copilot models (policy)...");
|
|
21793
|
+
await import_subscriptions13.copilotOAuth.enableAllCopilotModels(
|
|
21794
|
+
result.accessToken,
|
|
21795
|
+
{ apiEndpoint, ...enterpriseUrl ? { enterpriseUrl } : {} },
|
|
21796
|
+
exchangeFetch
|
|
21797
|
+
);
|
|
21798
|
+
return {
|
|
21799
|
+
accessToken: result.accessToken,
|
|
21800
|
+
expiresIn: Math.floor(import_subscriptions13.copilotOAuth.COPILOT_FAR_FUTURE_MS / 1e3),
|
|
21801
|
+
...identity,
|
|
21802
|
+
...apiEndpoint ? { apiEndpoint } : {}
|
|
21803
|
+
};
|
|
21804
|
+
}
|
|
21805
|
+
async function loginCopilot(store, deps, exchangeFetch, label, enterpriseUrl) {
|
|
21806
|
+
const result = await deps.awaitCopilotDevice(exchangeFetch, enterpriseUrl);
|
|
21807
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
21808
|
+
const block = {
|
|
21809
|
+
authMethod: "oauth",
|
|
21810
|
+
status: "authorized",
|
|
21811
|
+
accessToken: result.accessToken,
|
|
21812
|
+
// ghu_ tokens have no refresh lifecycle — the same token doubles as the
|
|
21813
|
+
// stored refresh credential so generic refresh paths stay well-formed.
|
|
21814
|
+
refreshToken: result.accessToken,
|
|
21815
|
+
expiresAt,
|
|
21816
|
+
...result.accountId ? { accountId: result.accountId } : {},
|
|
21817
|
+
...result.email ? { email: result.email } : {},
|
|
21818
|
+
...result.apiEndpoint ? { apiEndpoint: result.apiEndpoint } : {},
|
|
21819
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
21820
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21821
|
+
};
|
|
21822
|
+
await store.appendProviderAccount("copilot", block, label);
|
|
21823
|
+
logMasked("copilot", result.accessToken);
|
|
21824
|
+
return expiresAt;
|
|
21825
|
+
}
|
|
20591
21826
|
function isLoginProvider(value) {
|
|
20592
21827
|
return PROVIDERS2.includes(value);
|
|
20593
21828
|
}
|
|
@@ -21293,7 +22528,7 @@ async function main() {
|
|
|
21293
22528
|
process.exitCode = 1;
|
|
21294
22529
|
}
|
|
21295
22530
|
}
|
|
21296
|
-
main().catch((
|
|
21297
|
-
console.error(
|
|
22531
|
+
main().catch((err8) => {
|
|
22532
|
+
console.error(err8 instanceof Error ? err8.message : String(err8));
|
|
21298
22533
|
process.exitCode = 1;
|
|
21299
22534
|
});
|