@omnicross/daemon 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +2787 -359
- package/dist/cli.js +2743 -297
- package/dist/index.cjs +2717 -429
- package/dist/index.d.cts +486 -28
- package/dist/index.d.ts +486 -28
- package/dist/index.js +2583 -284
- package/package.json +6 -6
package/dist/cli.cjs
CHANGED
|
@@ -1166,26 +1166,26 @@ var import_http4 = require("@omnicross/core/search/http");
|
|
|
1166
1166
|
var import_search3 = require("@omnicross/core/search");
|
|
1167
1167
|
|
|
1168
1168
|
// src/bootstrap.ts
|
|
1169
|
-
var
|
|
1169
|
+
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
|
|
1173
|
+
var import_core7 = require("@omnicross/core");
|
|
1174
1174
|
var import_GeminiCodeAssistProjectResolver = 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_AccountAllowanceStore9 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1180
1180
|
var import_AccountAllowanceScheduling5 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
1181
|
-
var
|
|
1181
|
+
var import_upstreamFetch15 = 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);
|
|
@@ -1292,8 +1292,262 @@ function handleCodexOAuthStatus(sessionId, deps) {
|
|
|
1292
1292
|
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
1293
1293
|
}
|
|
1294
1294
|
|
|
1295
|
+
// src/admin/accountsKimiOAuth.ts
|
|
1296
|
+
var import_subscriptions2 = require("@omnicross/subscriptions");
|
|
1297
|
+
function err2(status, message) {
|
|
1298
|
+
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
1299
|
+
}
|
|
1300
|
+
var DEFAULT_KIMI_OAUTH_TTL_MS = 15 * 6e4;
|
|
1301
|
+
async function handleKimiOAuthStart(deps) {
|
|
1302
|
+
if (deps.kimiSessions.isBusy()) {
|
|
1303
|
+
return err2(409, "a kimi sign-in is already in progress \u2014 finish it in the browser or cancel it");
|
|
1304
|
+
}
|
|
1305
|
+
const fetchImpl = deps.oauthExchangeFetch("kimi");
|
|
1306
|
+
const deviceId = import_subscriptions2.kimiOAuth.generateKimiDeviceId();
|
|
1307
|
+
const fingerprint = import_subscriptions2.kimiOAuth.kimiFingerprintHeaders(deviceId);
|
|
1308
|
+
let authorization;
|
|
1309
|
+
try {
|
|
1310
|
+
authorization = await import_subscriptions2.kimiOAuth.requestDeviceAuthorization(fetchImpl, fingerprint);
|
|
1311
|
+
} catch (e) {
|
|
1312
|
+
const reason = e instanceof Error ? e.message : "device authorization failed";
|
|
1313
|
+
return err2(502, `kimi device authorization failed: ${reason}`);
|
|
1314
|
+
}
|
|
1315
|
+
const { sessionId, signal } = deps.kimiSessions.begin();
|
|
1316
|
+
void runKimiDevicePoll(sessionId, authorization.deviceCode, deviceId, fingerprint, signal, deps).catch(() => deps.kimiSessions.settle(sessionId, "error", "kimi sign-in failed"));
|
|
1317
|
+
return {
|
|
1318
|
+
status: 200,
|
|
1319
|
+
body: {
|
|
1320
|
+
authUrl: authorization.verificationUriComplete ?? authorization.verificationUri,
|
|
1321
|
+
userCode: authorization.userCode,
|
|
1322
|
+
sessionId
|
|
1323
|
+
}
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
async function runKimiDevicePoll(sessionId, deviceCode, deviceId, fingerprint, signal, deps) {
|
|
1327
|
+
const fetchImpl = deps.oauthExchangeFetch("kimi");
|
|
1328
|
+
const result = await import_subscriptions2.kimiOAuth.awaitDeviceToken(
|
|
1329
|
+
{ userCode: "", deviceCode, verificationUri: "" },
|
|
1330
|
+
fetchImpl,
|
|
1331
|
+
{
|
|
1332
|
+
fingerprint,
|
|
1333
|
+
deadlineMs: DEFAULT_KIMI_OAUTH_TTL_MS,
|
|
1334
|
+
sleep: (ms) => new Promise((resolve11, reject) => {
|
|
1335
|
+
const onAbort = () => {
|
|
1336
|
+
clearTimeout(timer);
|
|
1337
|
+
reject(new Error("login: cancelled"));
|
|
1338
|
+
};
|
|
1339
|
+
const timer = setTimeout(() => {
|
|
1340
|
+
signal.removeEventListener("abort", onAbort);
|
|
1341
|
+
resolve11();
|
|
1342
|
+
}, ms);
|
|
1343
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1344
|
+
})
|
|
1345
|
+
}
|
|
1346
|
+
);
|
|
1347
|
+
const block = {
|
|
1348
|
+
authMethod: "oauth",
|
|
1349
|
+
status: "authorized",
|
|
1350
|
+
accessToken: result.accessToken,
|
|
1351
|
+
refreshToken: result.refreshToken,
|
|
1352
|
+
expiresAt: new Date(Date.now() + result.expiresIn * 1e3).toISOString(),
|
|
1353
|
+
accountId: import_subscriptions2.kimiOAuth.kimiAccountIdFromAccessToken(result.accessToken),
|
|
1354
|
+
deviceId,
|
|
1355
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1356
|
+
};
|
|
1357
|
+
await deps.subscriptionAccountAppender.appendProviderAccount("kimi", block);
|
|
1358
|
+
deps.kimiSessions.settle(sessionId, "done");
|
|
1359
|
+
}
|
|
1360
|
+
function handleKimiOAuthCancel(sessionId, deps) {
|
|
1361
|
+
if (!deps.kimiSessions.cancel(sessionId)) return err2(404, "unknown or expired kimi sign-in session");
|
|
1362
|
+
return { status: 200, body: { ok: true } };
|
|
1363
|
+
}
|
|
1364
|
+
function handleKimiOAuthStatus(sessionId, deps) {
|
|
1365
|
+
const s = deps.kimiSessions.get(sessionId);
|
|
1366
|
+
if (!s) return err2(404, "unknown or expired kimi sign-in session");
|
|
1367
|
+
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
1368
|
+
}
|
|
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
|
+
|
|
1295
1549
|
// src/allowance/AccountAllowanceService.ts
|
|
1296
|
-
var
|
|
1550
|
+
var import_AccountAllowanceStore7 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1297
1551
|
var import_AccountAllowanceScheduling = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
1298
1552
|
|
|
1299
1553
|
// src/allowance/ClaudeAllowanceCollector.ts
|
|
@@ -1320,13 +1574,11 @@ function secondsUntil(instant, now) {
|
|
|
1320
1574
|
function windowFromPayload(id, payload, now) {
|
|
1321
1575
|
const usedPercent = finitePercent(payload?.utilization);
|
|
1322
1576
|
const resetsAt = isoInstant(payload?.resets_at);
|
|
1323
|
-
const isSonnet = id === "seven-day-sonnet";
|
|
1324
1577
|
const isFiveHour = id === "five-hour";
|
|
1325
1578
|
return {
|
|
1326
1579
|
id,
|
|
1327
|
-
label: isFiveHour ? "5 hours" :
|
|
1328
|
-
scope:
|
|
1329
|
-
modelFamily: isSonnet ? "sonnet" : void 0,
|
|
1580
|
+
label: isFiveHour ? "5 hours" : "7 days",
|
|
1581
|
+
scope: "all",
|
|
1330
1582
|
usedPercent,
|
|
1331
1583
|
windowMinutes: isFiveHour ? 5 * 60 : 7 * 24 * 60,
|
|
1332
1584
|
resetsAt,
|
|
@@ -1334,6 +1586,44 @@ function windowFromPayload(id, payload, now) {
|
|
|
1334
1586
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
1335
1587
|
};
|
|
1336
1588
|
}
|
|
1589
|
+
function limitEntryWindow(entries, kind) {
|
|
1590
|
+
const entry = entries.find((candidate) => candidate.kind === kind);
|
|
1591
|
+
if (!entry) return void 0;
|
|
1592
|
+
return { utilization: entry.percent, resets_at: entry.resets_at };
|
|
1593
|
+
}
|
|
1594
|
+
function slugifyDisplayName(name) {
|
|
1595
|
+
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1596
|
+
}
|
|
1597
|
+
function scopedWeeklyWindows(entries, now) {
|
|
1598
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1599
|
+
const windows = [];
|
|
1600
|
+
for (const entry of entries) {
|
|
1601
|
+
if (entry.kind !== "weekly_scoped") continue;
|
|
1602
|
+
const displayName = typeof entry.scope?.model?.display_name === "string" && entry.scope.model.display_name.trim() ? entry.scope.model.display_name.trim() : void 0;
|
|
1603
|
+
if (!displayName) continue;
|
|
1604
|
+
const slug = slugifyDisplayName(displayName);
|
|
1605
|
+
if (!slug || seen.has(slug)) continue;
|
|
1606
|
+
seen.add(slug);
|
|
1607
|
+
const usedPercent = finitePercent(entry.percent);
|
|
1608
|
+
const resetsAt = isoInstant(entry.resets_at);
|
|
1609
|
+
windows.push({
|
|
1610
|
+
id: `seven-day-${slug}`,
|
|
1611
|
+
label: `7 days \xB7 ${displayName}`,
|
|
1612
|
+
scope: "model-family",
|
|
1613
|
+
modelFamily: slug,
|
|
1614
|
+
usedPercent,
|
|
1615
|
+
windowMinutes: 7 * 24 * 60,
|
|
1616
|
+
resetsAt,
|
|
1617
|
+
remainingSeconds: secondsUntil(resetsAt, now),
|
|
1618
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
return windows;
|
|
1622
|
+
}
|
|
1623
|
+
function parseLimitEntries(raw) {
|
|
1624
|
+
if (!Array.isArray(raw)) return [];
|
|
1625
|
+
return raw.filter((entry) => !!entry && typeof entry === "object");
|
|
1626
|
+
}
|
|
1337
1627
|
function emptyClaudeWindows(state) {
|
|
1338
1628
|
return [
|
|
1339
1629
|
{
|
|
@@ -1351,15 +1641,6 @@ function emptyClaudeWindows(state) {
|
|
|
1351
1641
|
usedPercent: null,
|
|
1352
1642
|
windowMinutes: 7 * 24 * 60,
|
|
1353
1643
|
state
|
|
1354
|
-
},
|
|
1355
|
-
{
|
|
1356
|
-
id: "seven-day-sonnet",
|
|
1357
|
-
label: "7 days \xB7 Sonnet",
|
|
1358
|
-
scope: "model-family",
|
|
1359
|
-
modelFamily: "sonnet",
|
|
1360
|
-
usedPercent: null,
|
|
1361
|
-
windowMinutes: 7 * 24 * 60,
|
|
1362
|
-
state
|
|
1363
1644
|
}
|
|
1364
1645
|
];
|
|
1365
1646
|
}
|
|
@@ -1440,6 +1721,9 @@ var ClaudeAllowanceCollector = class {
|
|
|
1440
1721
|
}
|
|
1441
1722
|
const now = this.now();
|
|
1442
1723
|
const usage = payload;
|
|
1724
|
+
const limitEntries = parseLimitEntries(usage.limits);
|
|
1725
|
+
const fiveHour = usage.five_hour ?? limitEntryWindow(limitEntries, "session");
|
|
1726
|
+
const sevenDay = usage.seven_day ?? limitEntryWindow(limitEntries, "weekly_all");
|
|
1443
1727
|
const snapshot = {
|
|
1444
1728
|
providerId: "claude",
|
|
1445
1729
|
accountId,
|
|
@@ -1447,10 +1731,10 @@ var ClaudeAllowanceCollector = class {
|
|
|
1447
1731
|
observedAt: new Date(now).toISOString(),
|
|
1448
1732
|
expiresAt: new Date(now + CLAUDE_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1449
1733
|
windows: [
|
|
1450
|
-
windowFromPayload("five-hour",
|
|
1451
|
-
windowFromPayload("seven-day",
|
|
1452
|
-
|
|
1453
|
-
]
|
|
1734
|
+
windowFromPayload("five-hour", fiveHour, now),
|
|
1735
|
+
windowFromPayload("seven-day", sevenDay, now),
|
|
1736
|
+
...scopedWeeklyWindows(limitEntries, now)
|
|
1737
|
+
].slice(0, 8)
|
|
1454
1738
|
};
|
|
1455
1739
|
this.store.set(snapshot);
|
|
1456
1740
|
return snapshot;
|
|
@@ -1501,9 +1785,1079 @@ var ClaudeAllowanceCollector = class {
|
|
|
1501
1785
|
accountId,
|
|
1502
1786
|
source: "oauth-usage-api",
|
|
1503
1787
|
observedAt: new Date(now).toISOString(),
|
|
1504
|
-
windows: emptyClaudeWindows("unsupported"),
|
|
1788
|
+
windows: emptyClaudeWindows("unsupported"),
|
|
1789
|
+
lastErrorCode: code
|
|
1790
|
+
};
|
|
1791
|
+
}
|
|
1792
|
+
};
|
|
1793
|
+
|
|
1794
|
+
// src/allowance/CodexAllowanceCollector.ts
|
|
1795
|
+
var import_AccountAllowanceStore2 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1796
|
+
var import_upstreamFetch2 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
1797
|
+
var CODEX_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
1798
|
+
var CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
1799
|
+
var CODEX_CLI_USER_AGENT = "codex_cli_rs/0.144.5";
|
|
1800
|
+
function finiteNumber(value) {
|
|
1801
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
1802
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
1803
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
|
1804
|
+
}
|
|
1805
|
+
function finitePercent2(value) {
|
|
1806
|
+
const parsed = finiteNumber(value);
|
|
1807
|
+
return parsed !== null && parsed <= 100 ? parsed : null;
|
|
1808
|
+
}
|
|
1809
|
+
function epochMs(value) {
|
|
1810
|
+
return value > 1e11 ? value : value * 1e3;
|
|
1811
|
+
}
|
|
1812
|
+
function secondsUntil2(instant, now) {
|
|
1813
|
+
if (!instant) return void 0;
|
|
1814
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
1815
|
+
}
|
|
1816
|
+
function decodeJwtClaims(token) {
|
|
1817
|
+
const parts = token.split(".");
|
|
1818
|
+
if (parts.length !== 3) return void 0;
|
|
1819
|
+
try {
|
|
1820
|
+
const json2 = Buffer.from(parts[1], "base64url").toString("utf8");
|
|
1821
|
+
const parsed = JSON.parse(json2);
|
|
1822
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
1823
|
+
} catch {
|
|
1824
|
+
return void 0;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
function chatgptAccountIdFromClaims(claims) {
|
|
1828
|
+
const auth = claims?.["https://api.openai.com/auth"];
|
|
1829
|
+
if (!auth || typeof auth !== "object") return void 0;
|
|
1830
|
+
const accountId = auth.chatgpt_account_id;
|
|
1831
|
+
return typeof accountId === "string" && accountId.trim() ? accountId.trim() : void 0;
|
|
1832
|
+
}
|
|
1833
|
+
function resolveCodexChatGptAccountId(tokens) {
|
|
1834
|
+
if (tokens.accountId?.trim()) return tokens.accountId.trim();
|
|
1835
|
+
if (tokens.idToken) {
|
|
1836
|
+
const fromIdToken = chatgptAccountIdFromClaims(decodeJwtClaims(tokens.idToken));
|
|
1837
|
+
if (fromIdToken) return fromIdToken;
|
|
1838
|
+
}
|
|
1839
|
+
if (tokens.accessToken) {
|
|
1840
|
+
return chatgptAccountIdFromClaims(decodeJwtClaims(tokens.accessToken));
|
|
1841
|
+
}
|
|
1842
|
+
return void 0;
|
|
1843
|
+
}
|
|
1844
|
+
function windowFromPayload2(id, payload, now) {
|
|
1845
|
+
const usedPercent = finitePercent2(payload?.used_percent);
|
|
1846
|
+
const resetAtSeconds = finiteNumber(payload?.reset_at);
|
|
1847
|
+
const resetAfterSeconds = finiteNumber(payload?.reset_after_seconds);
|
|
1848
|
+
const windowSeconds = finiteNumber(payload?.limit_window_seconds);
|
|
1849
|
+
const resetsAt = resetAtSeconds !== null && resetAtSeconds > 0 ? new Date(epochMs(resetAtSeconds)).toISOString() : resetAfterSeconds !== null && resetAfterSeconds > 0 ? new Date(now + resetAfterSeconds * 1e3).toISOString() : void 0;
|
|
1850
|
+
const windowMinutes = windowSeconds !== null && windowSeconds > 0 ? Math.round(windowSeconds / 60) : void 0;
|
|
1851
|
+
return {
|
|
1852
|
+
id,
|
|
1853
|
+
label: id === "primary" ? "Primary" : "Secondary",
|
|
1854
|
+
scope: "all",
|
|
1855
|
+
usedPercent,
|
|
1856
|
+
...windowMinutes !== void 0 ? { windowMinutes } : {},
|
|
1857
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
1858
|
+
remainingSeconds: secondsUntil2(resetsAt, now),
|
|
1859
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
var CodexAllowanceCollector = class {
|
|
1863
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore2.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch2.fetchUpstream)(url, init, { providerId: "codex", accountId, redactBodies: true }), now = Date.now) {
|
|
1864
|
+
this.credentials = credentials;
|
|
1865
|
+
this.store = store;
|
|
1866
|
+
this.fetchImpl = fetchImpl;
|
|
1867
|
+
this.now = now;
|
|
1868
|
+
}
|
|
1869
|
+
credentials;
|
|
1870
|
+
store;
|
|
1871
|
+
fetchImpl;
|
|
1872
|
+
now;
|
|
1873
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
1874
|
+
async collectMany(accounts, options = {}) {
|
|
1875
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
1876
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
1877
|
+
}
|
|
1878
|
+
collect(account, options = {}) {
|
|
1879
|
+
const now = this.now();
|
|
1880
|
+
const unsupported = account.tokens.authMethod !== "oauth";
|
|
1881
|
+
if (unsupported) {
|
|
1882
|
+
const existing = this.store.get("codex", account.id, now);
|
|
1883
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
1884
|
+
return Promise.resolve(existing);
|
|
1885
|
+
}
|
|
1886
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
1887
|
+
this.store.set(snapshot);
|
|
1888
|
+
return Promise.resolve(snapshot);
|
|
1889
|
+
}
|
|
1890
|
+
const cached = this.store.get("codex", account.id, now);
|
|
1891
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
1892
|
+
return Promise.resolve(cached);
|
|
1893
|
+
}
|
|
1894
|
+
const running = this.inFlight.get(account.id);
|
|
1895
|
+
if (running) return running;
|
|
1896
|
+
const promise = this.fetchAccount(account.id, account.tokens).catch(() => this.failureSnapshot(account.id, "codex_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
1897
|
+
this.inFlight.set(account.id, promise);
|
|
1898
|
+
return promise;
|
|
1899
|
+
}
|
|
1900
|
+
/**
|
|
1901
|
+
* A response-header snapshot stays a valid cache hit only while fresh; an
|
|
1902
|
+
* active oauth-usage snapshot is honored on the same 5-minute cadence as
|
|
1903
|
+
* Claude's (the poll is cheap and quota is the scheduling input).
|
|
1904
|
+
*/
|
|
1905
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
1906
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
1907
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
1908
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
1909
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
1910
|
+
}
|
|
1911
|
+
async fetchAccount(accountId, tokens) {
|
|
1912
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("codex", accountId);
|
|
1913
|
+
if (!accessToken) {
|
|
1914
|
+
return this.failureSnapshot(accountId, "codex_usage_token_unavailable", this.now());
|
|
1915
|
+
}
|
|
1916
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
1917
|
+
if (response.status === 401) {
|
|
1918
|
+
const refreshed = await this.credentials.refreshAccountToken("codex", accountId);
|
|
1919
|
+
if (!refreshed) {
|
|
1920
|
+
return this.failureSnapshot(accountId, "codex_usage_unauthorized", this.now());
|
|
1921
|
+
}
|
|
1922
|
+
accessToken = await this.credentials.getAccessTokenForAccount("codex", accountId);
|
|
1923
|
+
if (!accessToken) {
|
|
1924
|
+
return this.failureSnapshot(accountId, "codex_usage_token_unavailable", this.now());
|
|
1925
|
+
}
|
|
1926
|
+
response = await this.request(accountId, accessToken, tokens);
|
|
1927
|
+
}
|
|
1928
|
+
if (response.status === 403) {
|
|
1929
|
+
const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "codex_usage_unsupported");
|
|
1930
|
+
this.store.set(snapshot2);
|
|
1931
|
+
return snapshot2;
|
|
1932
|
+
}
|
|
1933
|
+
if (!response.ok) {
|
|
1934
|
+
return this.failureSnapshot(accountId, "codex_usage_http_error", this.now());
|
|
1935
|
+
}
|
|
1936
|
+
let payload;
|
|
1937
|
+
try {
|
|
1938
|
+
payload = await response.json();
|
|
1939
|
+
} catch {
|
|
1940
|
+
return this.failureSnapshot(accountId, "codex_usage_invalid_response", this.now());
|
|
1941
|
+
}
|
|
1942
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
1943
|
+
return this.failureSnapshot(accountId, "codex_usage_invalid_response", this.now());
|
|
1944
|
+
}
|
|
1945
|
+
const now = this.now();
|
|
1946
|
+
const usage = payload.rate_limit;
|
|
1947
|
+
const previous = this.store.get("codex", accountId, now);
|
|
1948
|
+
const snapshot = {
|
|
1949
|
+
providerId: "codex",
|
|
1950
|
+
accountId,
|
|
1951
|
+
source: "oauth-usage-api",
|
|
1952
|
+
observedAt: new Date(now).toISOString(),
|
|
1953
|
+
expiresAt: new Date(now + CODEX_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1954
|
+
windows: [
|
|
1955
|
+
windowFromPayload2("primary", usage?.primary_window ?? void 0, now),
|
|
1956
|
+
windowFromPayload2("secondary", usage?.secondary_window ?? void 0, now)
|
|
1957
|
+
],
|
|
1958
|
+
// The wham payload has no ratio field; keep the passively-observed value.
|
|
1959
|
+
...previous?.primaryOverSecondaryLimitPercent !== void 0 ? { primaryOverSecondaryLimitPercent: previous.primaryOverSecondaryLimitPercent } : {}
|
|
1960
|
+
};
|
|
1961
|
+
this.store.set(snapshot);
|
|
1962
|
+
return snapshot;
|
|
1963
|
+
}
|
|
1964
|
+
request(accountId, accessToken, tokens) {
|
|
1965
|
+
const headers = {
|
|
1966
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1967
|
+
Accept: "application/json",
|
|
1968
|
+
"User-Agent": CODEX_CLI_USER_AGENT
|
|
1969
|
+
};
|
|
1970
|
+
const chatgptAccountId = resolveCodexChatGptAccountId(tokens);
|
|
1971
|
+
if (chatgptAccountId) headers["ChatGPT-Account-Id"] = chatgptAccountId;
|
|
1972
|
+
return this.fetchImpl(CODEX_USAGE_URL, {
|
|
1973
|
+
method: "GET",
|
|
1974
|
+
headers,
|
|
1975
|
+
signal: AbortSignal.timeout(15e3)
|
|
1976
|
+
}, accountId);
|
|
1977
|
+
}
|
|
1978
|
+
failureSnapshot(accountId, code, now) {
|
|
1979
|
+
const existing = this.store.get("codex", accountId, now);
|
|
1980
|
+
const snapshot = existing ? {
|
|
1981
|
+
...existing,
|
|
1982
|
+
expiresAt: new Date(now + CODEX_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1983
|
+
windows: existing.windows.map((window) => ({
|
|
1984
|
+
...window,
|
|
1985
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
1986
|
+
})),
|
|
1987
|
+
lastErrorCode: code
|
|
1988
|
+
} : {
|
|
1989
|
+
providerId: "codex",
|
|
1990
|
+
accountId,
|
|
1991
|
+
source: "oauth-usage-api",
|
|
1992
|
+
observedAt: new Date(now).toISOString(),
|
|
1993
|
+
expiresAt: new Date(now + CODEX_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1994
|
+
windows: [
|
|
1995
|
+
{ id: "primary", label: "Primary", scope: "all", usedPercent: null, state: "unavailable" },
|
|
1996
|
+
{ id: "secondary", label: "Secondary", scope: "all", usedPercent: null, state: "unavailable" }
|
|
1997
|
+
],
|
|
1998
|
+
lastErrorCode: code
|
|
1999
|
+
};
|
|
2000
|
+
this.store.set(snapshot);
|
|
2001
|
+
return snapshot;
|
|
2002
|
+
}
|
|
2003
|
+
unsupportedSnapshot(accountId, now, code = "codex_usage_unsupported_auth") {
|
|
2004
|
+
return {
|
|
2005
|
+
providerId: "codex",
|
|
2006
|
+
accountId,
|
|
2007
|
+
source: "oauth-usage-api",
|
|
2008
|
+
observedAt: new Date(now).toISOString(),
|
|
2009
|
+
windows: [
|
|
2010
|
+
{ id: "primary", label: "Primary", scope: "all", usedPercent: null, state: "unsupported" },
|
|
2011
|
+
{ id: "secondary", label: "Secondary", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2012
|
+
],
|
|
2013
|
+
lastErrorCode: code
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
};
|
|
2017
|
+
|
|
2018
|
+
// src/allowance/KimiAllowanceCollector.ts
|
|
2019
|
+
var import_AccountAllowanceStore3 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
2020
|
+
var import_upstreamFetch3 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
2021
|
+
var import_subscriptions5 = require("@omnicross/subscriptions");
|
|
2022
|
+
var KIMI_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2023
|
+
var KIMI_USAGE_URL = "https://api.kimi.com/coding/v1/usages";
|
|
2024
|
+
function finiteNumber2(value) {
|
|
2025
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
2026
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
2027
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
2028
|
+
}
|
|
2029
|
+
function isRecord(value) {
|
|
2030
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2031
|
+
}
|
|
2032
|
+
function parseResetMs(row, nowMs) {
|
|
2033
|
+
for (const key of ["reset_at", "resetAt", "reset_time", "resetTime"]) {
|
|
2034
|
+
const value = row[key];
|
|
2035
|
+
if (typeof value === "string" && value.trim()) {
|
|
2036
|
+
const parsed = Date.parse(value);
|
|
2037
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
2038
|
+
}
|
|
2039
|
+
const numeric = finiteNumber2(value);
|
|
2040
|
+
if (numeric !== void 0 && numeric > 1e9) {
|
|
2041
|
+
return numeric > 1e12 ? numeric : numeric * 1e3;
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
for (const key of ["reset_in", "resetIn", "ttl", "window"]) {
|
|
2045
|
+
const seconds = finiteNumber2(row[key]);
|
|
2046
|
+
if (seconds !== void 0) return nowMs + seconds * 1e3;
|
|
2047
|
+
}
|
|
2048
|
+
return void 0;
|
|
2049
|
+
}
|
|
2050
|
+
var MINUTE_MS = 6e4;
|
|
2051
|
+
var HOUR_MS = 36e5;
|
|
2052
|
+
var DAY_MS = 864e5;
|
|
2053
|
+
function canonicalWindow(durationMs) {
|
|
2054
|
+
if (durationMs === 5 * HOUR_MS) return { id: "five-hour", label: "5 hours", minutes: 300 };
|
|
2055
|
+
if (durationMs === 7 * DAY_MS) return { id: "seven-day", label: "7 days", minutes: 10080 };
|
|
2056
|
+
if (durationMs > 0 && durationMs % DAY_MS === 0) {
|
|
2057
|
+
const days = durationMs / DAY_MS;
|
|
2058
|
+
return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}`, minutes: Math.round(durationMs / MINUTE_MS) };
|
|
2059
|
+
}
|
|
2060
|
+
if (durationMs > 0 && durationMs % HOUR_MS === 0) {
|
|
2061
|
+
const hours = durationMs / HOUR_MS;
|
|
2062
|
+
return { id: `${hours}h`, label: `${hours} hour${hours === 1 ? "" : "s"}`, minutes: Math.round(durationMs / MINUTE_MS) };
|
|
2063
|
+
}
|
|
2064
|
+
return void 0;
|
|
2065
|
+
}
|
|
2066
|
+
function secondsUntil3(instant, now) {
|
|
2067
|
+
if (!instant) return void 0;
|
|
2068
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2069
|
+
}
|
|
2070
|
+
function windowFromRow(row, fallback, now) {
|
|
2071
|
+
const usedPercent = row?.limit !== void 0 && row.limit > 0 && row.used !== void 0 ? Math.round(Math.min(100, row.used / row.limit * 100) * 10) / 10 : null;
|
|
2072
|
+
const resetsAt = row?.resetsAtMs !== void 0 ? new Date(row.resetsAtMs).toISOString() : void 0;
|
|
2073
|
+
return {
|
|
2074
|
+
id: fallback.id,
|
|
2075
|
+
label: fallback.label,
|
|
2076
|
+
scope: "all",
|
|
2077
|
+
usedPercent,
|
|
2078
|
+
windowMinutes: fallback.minutes,
|
|
2079
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2080
|
+
remainingSeconds: secondsUntil3(resetsAt, now),
|
|
2081
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
2082
|
+
};
|
|
2083
|
+
}
|
|
2084
|
+
function parseKimiUsagePayload(payload, now) {
|
|
2085
|
+
if (!isRecord(payload)) return [];
|
|
2086
|
+
const byId = /* @__PURE__ */ new Map();
|
|
2087
|
+
const rowFrom = (data) => {
|
|
2088
|
+
const limit = finiteNumber2(data["limit"]);
|
|
2089
|
+
let used = finiteNumber2(data["used"]);
|
|
2090
|
+
const remaining = finiteNumber2(data["remaining"]);
|
|
2091
|
+
if (used === void 0 && remaining !== void 0 && limit !== void 0) {
|
|
2092
|
+
used = limit - remaining;
|
|
2093
|
+
}
|
|
2094
|
+
let windowDurationMs;
|
|
2095
|
+
const windowData = isRecord(data["window"]) ? data["window"] : void 0;
|
|
2096
|
+
const duration = finiteNumber2(windowData?.["duration"]);
|
|
2097
|
+
const timeUnit = typeof windowData?.["timeUnit"] === "string" ? windowData["timeUnit"].toUpperCase() : "";
|
|
2098
|
+
if (duration !== void 0) {
|
|
2099
|
+
if (timeUnit.includes("MINUTE")) windowDurationMs = duration * MINUTE_MS;
|
|
2100
|
+
else if (timeUnit.includes("HOUR")) windowDurationMs = duration * HOUR_MS;
|
|
2101
|
+
else if (timeUnit.includes("DAY")) windowDurationMs = duration * DAY_MS;
|
|
2102
|
+
else if (timeUnit.includes("WEEK")) windowDurationMs = duration * 7 * DAY_MS;
|
|
2103
|
+
else if (timeUnit.includes("SECOND")) windowDurationMs = duration * 1e3;
|
|
2104
|
+
}
|
|
2105
|
+
const resetsAtMs = parseResetMs(windowData && parseResetMs(windowData, now) !== void 0 ? windowData : data, now);
|
|
2106
|
+
return { used, limit, remaining, ...resetsAtMs !== void 0 ? { resetsAtMs } : {}, ...windowDurationMs !== void 0 ? { windowDurationMs } : {} };
|
|
2107
|
+
};
|
|
2108
|
+
if (isRecord(payload["usage"])) {
|
|
2109
|
+
const row = rowFrom(payload["usage"]);
|
|
2110
|
+
const window = windowFromRow({ ...row, resetsAtMs: row.resetsAtMs }, { id: "seven-day", label: "7 days", minutes: 10080 }, now);
|
|
2111
|
+
byId.set("seven-day", window);
|
|
2112
|
+
}
|
|
2113
|
+
if (Array.isArray(payload["limits"])) {
|
|
2114
|
+
for (const item of payload["limits"]) {
|
|
2115
|
+
if (!isRecord(item)) continue;
|
|
2116
|
+
const detail = isRecord(item["detail"]) ? item["detail"] : item;
|
|
2117
|
+
const row = rowFrom(detail);
|
|
2118
|
+
const canonical = row.windowDurationMs !== void 0 ? canonicalWindow(row.windowDurationMs) : void 0;
|
|
2119
|
+
if (!canonical) continue;
|
|
2120
|
+
const window = windowFromRow(row, canonical, now);
|
|
2121
|
+
const existing = byId.get(canonical.id);
|
|
2122
|
+
if (!existing || (window.usedPercent ?? 0) > (existing.usedPercent ?? 0)) {
|
|
2123
|
+
byId.set(canonical.id, window);
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
return [...byId.values()].sort((a, b) => (a.windowMinutes ?? Infinity) - (b.windowMinutes ?? Infinity)).slice(0, 4);
|
|
2128
|
+
}
|
|
2129
|
+
var KimiAllowanceCollector = class {
|
|
2130
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore3.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch3.fetchUpstream)(url, init, { providerId: "kimi", accountId, redactBodies: true }), now = Date.now) {
|
|
2131
|
+
this.credentials = credentials;
|
|
2132
|
+
this.store = store;
|
|
2133
|
+
this.fetchImpl = fetchImpl;
|
|
2134
|
+
this.now = now;
|
|
2135
|
+
}
|
|
2136
|
+
credentials;
|
|
2137
|
+
store;
|
|
2138
|
+
fetchImpl;
|
|
2139
|
+
now;
|
|
2140
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
2141
|
+
async collectMany(accounts, options = {}) {
|
|
2142
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
2143
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
2144
|
+
}
|
|
2145
|
+
collect(account, options = {}) {
|
|
2146
|
+
const now = this.now();
|
|
2147
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
2148
|
+
const existing = this.store.get("kimi", account.id, now);
|
|
2149
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
2150
|
+
return Promise.resolve(existing);
|
|
2151
|
+
}
|
|
2152
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
2153
|
+
this.store.set(snapshot);
|
|
2154
|
+
return Promise.resolve(snapshot);
|
|
2155
|
+
}
|
|
2156
|
+
const cached = this.store.get("kimi", account.id, now);
|
|
2157
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
2158
|
+
return Promise.resolve(cached);
|
|
2159
|
+
}
|
|
2160
|
+
const running = this.inFlight.get(account.id);
|
|
2161
|
+
if (running) return running;
|
|
2162
|
+
const promise = this.fetchAccount(account.id, account.tokens).catch(() => this.failureSnapshot(account.id, "kimi_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
2163
|
+
this.inFlight.set(account.id, promise);
|
|
2164
|
+
return promise;
|
|
2165
|
+
}
|
|
2166
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
2167
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
2168
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
2169
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
2170
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
2171
|
+
}
|
|
2172
|
+
async fetchAccount(accountId, tokens) {
|
|
2173
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
|
|
2174
|
+
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
2175
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
2176
|
+
if (response.status === 401) {
|
|
2177
|
+
const refreshed = await this.credentials.refreshAccountToken("kimi", accountId);
|
|
2178
|
+
if (!refreshed) return this.failureSnapshot(accountId, "kimi_usage_unauthorized", this.now());
|
|
2179
|
+
accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
|
|
2180
|
+
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
2181
|
+
response = await this.request(accountId, accessToken, tokens);
|
|
2182
|
+
}
|
|
2183
|
+
if (response.status === 403) {
|
|
2184
|
+
const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "kimi_usage_unsupported");
|
|
2185
|
+
this.store.set(snapshot2);
|
|
2186
|
+
return snapshot2;
|
|
2187
|
+
}
|
|
2188
|
+
if (!response.ok) return this.failureSnapshot(accountId, "kimi_usage_http_error", this.now());
|
|
2189
|
+
let payload;
|
|
2190
|
+
try {
|
|
2191
|
+
payload = await response.json();
|
|
2192
|
+
} catch {
|
|
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/OpenCodeGoAllowanceCollector.ts
|
|
2739
|
+
var import_AccountAllowanceStore6 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
2740
|
+
var import_upstreamFetch6 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
2741
|
+
var import_subscriptions7 = require("@omnicross/subscriptions");
|
|
2742
|
+
var OPENCODEGO_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2743
|
+
var OPENCODEGO_DEFAULT_GO_BASE = "https://opencode.ai/zen/go";
|
|
2744
|
+
function finitePercent3(value) {
|
|
2745
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
2746
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
2747
|
+
return Number.isFinite(parsed) && parsed >= 0 && parsed <= 100 ? parsed : null;
|
|
2748
|
+
}
|
|
2749
|
+
function isoInstant2(value) {
|
|
2750
|
+
if (typeof value !== "string" || !value.trim()) return void 0;
|
|
2751
|
+
const time = Date.parse(value);
|
|
2752
|
+
return Number.isFinite(time) ? new Date(time).toISOString() : void 0;
|
|
2753
|
+
}
|
|
2754
|
+
function secondsUntil6(instant, now) {
|
|
2755
|
+
if (!instant) return void 0;
|
|
2756
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2757
|
+
}
|
|
2758
|
+
function windowFromPayload3(id, label, minutes, payload, now) {
|
|
2759
|
+
const statusRateLimited = payload?.status === "rate-limited";
|
|
2760
|
+
const usedPercent = statusRateLimited ? 100 : finitePercent3(payload?.percent);
|
|
2761
|
+
const resetsAt = isoInstant2(payload?.resetsAt);
|
|
2762
|
+
return {
|
|
2763
|
+
id,
|
|
2764
|
+
label,
|
|
2765
|
+
scope: "all",
|
|
2766
|
+
usedPercent,
|
|
2767
|
+
windowMinutes: minutes,
|
|
2768
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2769
|
+
remainingSeconds: secondsUntil6(resetsAt, now),
|
|
2770
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
2771
|
+
};
|
|
2772
|
+
}
|
|
2773
|
+
var OpenCodeGoAllowanceCollector = class {
|
|
2774
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore6.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch6.fetchUpstream)(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
|
|
2775
|
+
this.credentials = credentials;
|
|
2776
|
+
this.store = store;
|
|
2777
|
+
this.fetchImpl = fetchImpl;
|
|
2778
|
+
this.now = now;
|
|
2779
|
+
}
|
|
2780
|
+
credentials;
|
|
2781
|
+
store;
|
|
2782
|
+
fetchImpl;
|
|
2783
|
+
now;
|
|
2784
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
2785
|
+
async collectMany(accounts, options = {}) {
|
|
2786
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
2787
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
2788
|
+
}
|
|
2789
|
+
collect(account, options = {}) {
|
|
2790
|
+
const now = this.now();
|
|
2791
|
+
const cached = this.store.get("opencodego", account.id, now);
|
|
2792
|
+
if (!options.force && cached && (cached.windows.every((window) => window.state === "unsupported") || cached.expiresAt && Date.parse(cached.expiresAt) > now + (options.refreshAheadMs ?? 0))) {
|
|
2793
|
+
return Promise.resolve(cached);
|
|
2794
|
+
}
|
|
2795
|
+
const running = this.inFlight.get(account.id);
|
|
2796
|
+
if (running) return running;
|
|
2797
|
+
const promise = this.fetchAccount(account).catch(() => this.failureSnapshot(account.id, this.now())).finally(() => this.inFlight.delete(account.id));
|
|
2798
|
+
this.inFlight.set(account.id, promise);
|
|
2799
|
+
return promise;
|
|
2800
|
+
}
|
|
2801
|
+
async fetchAccount(account) {
|
|
2802
|
+
const apiKey = await this.credentials.getAccessTokenForAccount("opencodego", account.id);
|
|
2803
|
+
if (!apiKey) return this.failureSnapshot(account.id, this.now());
|
|
2804
|
+
const base = account.tokens.baseUrl ? (0, import_subscriptions7.normalizeOpenCodeGoBaseUrl)(account.tokens.baseUrl) : OPENCODEGO_DEFAULT_GO_BASE;
|
|
2805
|
+
const response = await this.fetchImpl(`${base}/v1/usage`, {
|
|
2806
|
+
method: "GET",
|
|
2807
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
2808
|
+
signal: AbortSignal.timeout(15e3)
|
|
2809
|
+
}, account.id);
|
|
2810
|
+
if (response.status === 401 || response.status === 403) {
|
|
2811
|
+
return this.failureSnapshot(account.id, this.now(), "opencodego_usage_unauthorized");
|
|
2812
|
+
}
|
|
2813
|
+
if (!response.ok) return this.failureSnapshot(account.id, this.now());
|
|
2814
|
+
let payload;
|
|
2815
|
+
try {
|
|
2816
|
+
payload = await response.json();
|
|
2817
|
+
} catch {
|
|
2818
|
+
return this.failureSnapshot(account.id, this.now());
|
|
2819
|
+
}
|
|
2820
|
+
const usage = payload && typeof payload === "object" && !Array.isArray(payload) ? payload.usage : void 0;
|
|
2821
|
+
const now = this.now();
|
|
2822
|
+
const snapshot = {
|
|
2823
|
+
providerId: "opencodego",
|
|
2824
|
+
accountId: account.id,
|
|
2825
|
+
source: "oauth-usage-api",
|
|
2826
|
+
observedAt: new Date(now).toISOString(),
|
|
2827
|
+
expiresAt: new Date(now + OPENCODEGO_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2828
|
+
// Monthly deliberately omitted (module doc).
|
|
2829
|
+
windows: [
|
|
2830
|
+
windowFromPayload3("five-hour", "5 hours", 5 * 60, usage?.rolling ?? void 0, now),
|
|
2831
|
+
windowFromPayload3("seven-day", "7 days", 7 * 24 * 60, usage?.weekly ?? void 0, now)
|
|
2832
|
+
]
|
|
2833
|
+
};
|
|
2834
|
+
this.store.set(snapshot);
|
|
2835
|
+
return snapshot;
|
|
2836
|
+
}
|
|
2837
|
+
failureSnapshot(accountId, now, code = "opencodego_usage_request_failed") {
|
|
2838
|
+
const existing = this.store.get("opencodego", accountId, now);
|
|
2839
|
+
const snapshot = existing ? {
|
|
2840
|
+
...existing,
|
|
2841
|
+
expiresAt: new Date(now + OPENCODEGO_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2842
|
+
windows: existing.windows.map((window) => ({
|
|
2843
|
+
...window,
|
|
2844
|
+
state: window.usedPercent !== null || window.resetsAt ? "stale" : window.state
|
|
2845
|
+
})),
|
|
2846
|
+
lastErrorCode: code
|
|
2847
|
+
} : {
|
|
2848
|
+
providerId: "opencodego",
|
|
2849
|
+
accountId,
|
|
2850
|
+
source: "oauth-usage-api",
|
|
2851
|
+
observedAt: new Date(now).toISOString(),
|
|
2852
|
+
expiresAt: new Date(now + OPENCODEGO_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2853
|
+
windows: [
|
|
2854
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2855
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2856
|
+
],
|
|
1505
2857
|
lastErrorCode: code
|
|
1506
2858
|
};
|
|
2859
|
+
this.store.set(snapshot);
|
|
2860
|
+
return snapshot;
|
|
1507
2861
|
}
|
|
1508
2862
|
};
|
|
1509
2863
|
|
|
@@ -1522,26 +2876,34 @@ function codexUnavailable(accountId, now) {
|
|
|
1522
2876
|
};
|
|
1523
2877
|
}
|
|
1524
2878
|
var AccountAllowanceService = class {
|
|
1525
|
-
constructor(credentials, store = (0,
|
|
2879
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore7.getSharedAccountAllowanceStore)(), collector, codexCollector, kimiCollector, opencodegoCollector, grokCollector, copilotCollector, now = Date.now) {
|
|
1526
2880
|
this.credentials = credentials;
|
|
1527
2881
|
this.store = store;
|
|
1528
2882
|
this.now = now;
|
|
1529
2883
|
this.claudeCollector = collector ?? new ClaudeAllowanceCollector(credentials, store);
|
|
2884
|
+
this.codexCollector = codexCollector ?? new CodexAllowanceCollector(credentials, store);
|
|
2885
|
+
this.kimiCollector = kimiCollector ?? new KimiAllowanceCollector(credentials, store);
|
|
2886
|
+
this.opencodegoCollector = opencodegoCollector ?? new OpenCodeGoAllowanceCollector(credentials, store);
|
|
2887
|
+
this.grokCollector = grokCollector ?? new GrokAllowanceCollector(credentials, store);
|
|
2888
|
+
this.copilotCollector = copilotCollector ?? new CopilotAllowanceCollector(credentials, store);
|
|
1530
2889
|
}
|
|
1531
2890
|
credentials;
|
|
1532
2891
|
store;
|
|
1533
2892
|
now;
|
|
1534
2893
|
claudeCollector;
|
|
2894
|
+
codexCollector;
|
|
2895
|
+
kimiCollector;
|
|
2896
|
+
grokCollector;
|
|
2897
|
+
copilotCollector;
|
|
2898
|
+
opencodegoCollector;
|
|
1535
2899
|
/**
|
|
1536
|
-
* Read all/filtered snapshots. Claude's five-minute
|
|
1537
|
-
*
|
|
2900
|
+
* Read all/filtered snapshots. Claude's and Codex's five-minute caches are
|
|
2901
|
+
* refreshed lazily on read (Codex polls `/backend-api/wham/usage`; the
|
|
2902
|
+
* passive `x-codex-*` header tap still feeds mid-flight updates).
|
|
1538
2903
|
*/
|
|
1539
2904
|
async list(filter = {}) {
|
|
1540
2905
|
const config = await this.credentials.getFullConfig();
|
|
1541
|
-
this.store.pruneToKnownAccounts(
|
|
1542
|
-
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
1543
|
-
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id }))
|
|
1544
|
-
]);
|
|
2906
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1545
2907
|
const wantsClaude = !filter.providerId || filter.providerId === "claude";
|
|
1546
2908
|
const claudeAccounts = (config.claudeAccounts ?? []).filter(
|
|
1547
2909
|
(account) => !filter.accountId || account.id === filter.accountId
|
|
@@ -1552,39 +2914,124 @@ var AccountAllowanceService = class {
|
|
|
1552
2914
|
(account) => !filter.accountId || account.id === filter.accountId
|
|
1553
2915
|
);
|
|
1554
2916
|
if (wantsCodex) {
|
|
2917
|
+
await this.codexCollector.collectMany(codexAccounts);
|
|
1555
2918
|
for (const account of codexAccounts) {
|
|
1556
2919
|
if (!this.store.get("codex", account.id)) this.store.set(codexUnavailable(account.id, this.now()));
|
|
1557
2920
|
}
|
|
1558
2921
|
}
|
|
2922
|
+
const wantsKimi = !filter.providerId || filter.providerId === "kimi";
|
|
2923
|
+
const kimiAccounts = (config.kimiAccounts ?? []).filter(
|
|
2924
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
2925
|
+
);
|
|
2926
|
+
if (wantsKimi) await this.kimiCollector.collectMany(kimiAccounts);
|
|
2927
|
+
const wantsOpenCodeGo = !filter.providerId || filter.providerId === "opencodego";
|
|
2928
|
+
const opencodegoAccounts = (config.opencodegoAccounts ?? []).filter(
|
|
2929
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
2930
|
+
);
|
|
2931
|
+
if (wantsOpenCodeGo) await this.opencodegoCollector.collectMany(opencodegoAccounts);
|
|
2932
|
+
const wantsGrok = !filter.providerId || filter.providerId === "grok";
|
|
2933
|
+
const grokAccounts = (config.grokAccounts ?? []).filter(
|
|
2934
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
2935
|
+
);
|
|
2936
|
+
if (wantsGrok) await this.grokCollector.collectMany(grokAccounts);
|
|
2937
|
+
const wantsCopilot = !filter.providerId || filter.providerId === "copilot";
|
|
2938
|
+
const copilotAccounts = (config.copilotAccounts ?? []).filter(
|
|
2939
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
2940
|
+
);
|
|
2941
|
+
if (wantsCopilot) await this.copilotCollector.collectMany(copilotAccounts);
|
|
1559
2942
|
const known = /* @__PURE__ */ new Set();
|
|
1560
2943
|
if (wantsClaude) for (const account of claudeAccounts) known.add(`claude\0${account.id}`);
|
|
1561
2944
|
if (wantsCodex) for (const account of codexAccounts) known.add(`codex\0${account.id}`);
|
|
2945
|
+
if (wantsKimi) for (const account of kimiAccounts) known.add(`kimi\0${account.id}`);
|
|
2946
|
+
if (wantsOpenCodeGo) for (const account of opencodegoAccounts) known.add(`opencodego\0${account.id}`);
|
|
2947
|
+
if (wantsGrok) for (const account of grokAccounts) known.add(`grok\0${account.id}`);
|
|
2948
|
+
if (wantsCopilot) for (const account of copilotAccounts) known.add(`copilot\0${account.id}`);
|
|
1562
2949
|
return this.store.list(filter).filter((snapshot) => known.has(`${snapshot.providerId}\0${snapshot.accountId}`));
|
|
1563
2950
|
}
|
|
2951
|
+
knownAccounts(config) {
|
|
2952
|
+
return [
|
|
2953
|
+
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
2954
|
+
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id })),
|
|
2955
|
+
...(config.kimiAccounts ?? []).map((account) => ({ providerId: "kimi", accountId: account.id })),
|
|
2956
|
+
...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id })),
|
|
2957
|
+
...(config.grokAccounts ?? []).map((account) => ({ providerId: "grok", accountId: account.id })),
|
|
2958
|
+
...(config.copilotAccounts ?? []).map((account) => ({ providerId: "copilot", accountId: account.id }))
|
|
2959
|
+
];
|
|
2960
|
+
}
|
|
1564
2961
|
/** Force-refresh Claude usage for one account or every stored Claude account. */
|
|
1565
2962
|
async refreshClaude(accountId) {
|
|
1566
2963
|
const config = await this.credentials.getFullConfig();
|
|
1567
|
-
this.store.pruneToKnownAccounts(
|
|
1568
|
-
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
1569
|
-
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id }))
|
|
1570
|
-
]);
|
|
2964
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1571
2965
|
const accounts = (config.claudeAccounts ?? []).filter(
|
|
1572
2966
|
(account) => !accountId || account.id === accountId
|
|
1573
2967
|
);
|
|
1574
2968
|
return this.claudeCollector.collectMany(accounts, { force: true });
|
|
1575
2969
|
}
|
|
1576
2970
|
/**
|
|
1577
|
-
*
|
|
1578
|
-
*
|
|
1579
|
-
*
|
|
2971
|
+
* Force-refresh Codex usage (`/backend-api/wham/usage`) for one account or
|
|
2972
|
+
* every stored Codex account. Replaces the old probe-request workaround —
|
|
2973
|
+
* no quota is spent reading the usage endpoint.
|
|
2974
|
+
*/
|
|
2975
|
+
async refreshCodex(accountId) {
|
|
2976
|
+
const config = await this.credentials.getFullConfig();
|
|
2977
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
2978
|
+
const accounts = (config.codexAccounts ?? []).filter(
|
|
2979
|
+
(account) => !accountId || account.id === accountId
|
|
2980
|
+
);
|
|
2981
|
+
return this.codexCollector.collectMany(accounts, { force: true });
|
|
2982
|
+
}
|
|
2983
|
+
/** Force-refresh OpenCodeGo usage (`{go}/v1/usage`) for one/all accounts. */
|
|
2984
|
+
async refreshOpenCodeGo(accountId) {
|
|
2985
|
+
const config = await this.credentials.getFullConfig();
|
|
2986
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
2987
|
+
const accounts = (config.opencodegoAccounts ?? []).filter(
|
|
2988
|
+
(account) => !accountId || account.id === accountId
|
|
2989
|
+
);
|
|
2990
|
+
return this.opencodegoCollector.collectMany(accounts, { force: true });
|
|
2991
|
+
}
|
|
2992
|
+
/** Force-refresh Kimi usage (`/coding/v1/usages`) for one/all accounts. */
|
|
2993
|
+
async refreshKimi(accountId) {
|
|
2994
|
+
const config = await this.credentials.getFullConfig();
|
|
2995
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
2996
|
+
const accounts = (config.kimiAccounts ?? []).filter(
|
|
2997
|
+
(account) => !accountId || account.id === accountId
|
|
2998
|
+
);
|
|
2999
|
+
return this.kimiCollector.collectMany(accounts, { force: true });
|
|
3000
|
+
}
|
|
3001
|
+
/** Force-refresh Copilot usage (copilot_internal/user) for one/all accounts. */
|
|
3002
|
+
async refreshCopilot(accountId) {
|
|
3003
|
+
const config = await this.credentials.getFullConfig();
|
|
3004
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
3005
|
+
const accounts = (config.copilotAccounts ?? []).filter(
|
|
3006
|
+
(account) => !accountId || account.id === accountId
|
|
3007
|
+
);
|
|
3008
|
+
return this.copilotCollector.collectMany(accounts, { force: true });
|
|
3009
|
+
}
|
|
3010
|
+
/** Force-refresh Grok usage (CLI billing proxy) for one/all accounts. */
|
|
3011
|
+
async refreshGrok(accountId) {
|
|
3012
|
+
const config = await this.credentials.getFullConfig();
|
|
3013
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
3014
|
+
const accounts = (config.grokAccounts ?? []).filter(
|
|
3015
|
+
(account) => !accountId || account.id === accountId
|
|
3016
|
+
);
|
|
3017
|
+
return this.grokCollector.collectMany(accounts, { force: true });
|
|
3018
|
+
}
|
|
3019
|
+
/**
|
|
3020
|
+
* Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
|
|
3021
|
+
* collectors preserve their cache + per-account in-flight coalescing; a tick
|
|
3022
|
+
* normally performs no network I/O. (Codex joined the warm path when it
|
|
3023
|
+
* gained an active `/wham/usage` collector — the passive `x-codex-*` header
|
|
3024
|
+
* tap alone could not keep the policy fed while idle.)
|
|
1580
3025
|
*/
|
|
1581
3026
|
async maintainClaudeCache(refreshAheadMs) {
|
|
1582
3027
|
const config = await this.credentials.getFullConfig();
|
|
1583
|
-
this.store.pruneToKnownAccounts(
|
|
1584
|
-
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
1585
|
-
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id }))
|
|
1586
|
-
]);
|
|
3028
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1587
3029
|
await this.claudeCollector.collectMany(config.claudeAccounts ?? [], { refreshAheadMs });
|
|
3030
|
+
await this.codexCollector.collectMany(config.codexAccounts ?? [], { refreshAheadMs });
|
|
3031
|
+
await this.kimiCollector.collectMany(config.kimiAccounts ?? [], { refreshAheadMs });
|
|
3032
|
+
await this.opencodegoCollector.collectMany(config.opencodegoAccounts ?? [], { refreshAheadMs });
|
|
3033
|
+
await this.grokCollector.collectMany(config.grokAccounts ?? [], { refreshAheadMs });
|
|
3034
|
+
await this.copilotCollector.collectMany(config.copilotAccounts ?? [], { refreshAheadMs });
|
|
1588
3035
|
}
|
|
1589
3036
|
/** Remove a cache row as soon as an account is deleted by the admin path. */
|
|
1590
3037
|
removeAccountSnapshot(providerId, accountId) {
|
|
@@ -1679,7 +3126,7 @@ var ClaudeAllowanceRefreshScheduler = class {
|
|
|
1679
3126
|
var import_node_crypto4 = require("crypto");
|
|
1680
3127
|
var import_node_fs6 = require("fs");
|
|
1681
3128
|
var import_node_path6 = require("path");
|
|
1682
|
-
var
|
|
3129
|
+
var import_AccountAllowanceStore8 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1683
3130
|
var ACCOUNT_ALLOWANCE_CACHE_VERSION = 1;
|
|
1684
3131
|
var MAX_PERSISTED_ALLOWANCE_SNAPSHOTS = 256;
|
|
1685
3132
|
var MAX_ALLOWANCE_CACHE_BYTES = 1e6;
|
|
@@ -1708,7 +3155,7 @@ var JsonAccountAllowancePersistence = class {
|
|
|
1708
3155
|
save(snapshots) {
|
|
1709
3156
|
const rows = [];
|
|
1710
3157
|
for (const snapshot of snapshots) {
|
|
1711
|
-
const normalized2 = (0,
|
|
3158
|
+
const normalized2 = (0, import_AccountAllowanceStore8.normalizeAccountAllowanceSnapshot)(snapshot);
|
|
1712
3159
|
if (!normalized2) continue;
|
|
1713
3160
|
rows.push(normalized2);
|
|
1714
3161
|
if (rows.length >= MAX_PERSISTED_ALLOWANCE_SNAPSHOTS) break;
|
|
@@ -1984,7 +3431,8 @@ var import_outbound_api5 = require("@omnicross/core/outbound-api");
|
|
|
1984
3431
|
var import_image_generation_types = require("@omnicross/contracts/image-generation-types");
|
|
1985
3432
|
var import_AccountAllowanceScheduling2 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
1986
3433
|
var import_SubscriptionAccountHealth = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
1987
|
-
var
|
|
3434
|
+
var import_upstreamFetch9 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
3435
|
+
var import_core3 = require("@omnicross/core");
|
|
1988
3436
|
|
|
1989
3437
|
// src/image-generation/imagesConfigValidation.ts
|
|
1990
3438
|
var import_outbound_api = require("@omnicross/core/outbound-api");
|
|
@@ -2280,6 +3728,7 @@ async function applyServerConfigTransaction(current, next, deps) {
|
|
|
2280
3728
|
|
|
2281
3729
|
// src/config.ts
|
|
2282
3730
|
var import_node_fs8 = require("fs");
|
|
3731
|
+
var import_core = require("@omnicross/core");
|
|
2283
3732
|
var DEFAULT_ADMIN_PORT = 8766;
|
|
2284
3733
|
function validateAdmin(raw) {
|
|
2285
3734
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
@@ -2336,6 +3785,18 @@ var FORMAT_AXIS_TRANSFORMERS = [
|
|
|
2336
3785
|
"openai-response",
|
|
2337
3786
|
"gemini-code-assist"
|
|
2338
3787
|
];
|
|
3788
|
+
function validateExtraHeaders(raw) {
|
|
3789
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
3790
|
+
const reserved = import_core.EXTRA_HEADER_RESERVED_NAMES;
|
|
3791
|
+
const out = {};
|
|
3792
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
3793
|
+
if (!name.trim()) continue;
|
|
3794
|
+
if (typeof value !== "string") continue;
|
|
3795
|
+
if (reserved.has(name.toLowerCase())) continue;
|
|
3796
|
+
out[name] = value;
|
|
3797
|
+
}
|
|
3798
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
3799
|
+
}
|
|
2339
3800
|
function validateApiKeys(raw) {
|
|
2340
3801
|
if (!Array.isArray(raw)) return void 0;
|
|
2341
3802
|
const out = [];
|
|
@@ -2549,6 +4010,9 @@ function validateProvider(raw, index) {
|
|
|
2549
4010
|
apiVersion,
|
|
2550
4011
|
maxConcurrency,
|
|
2551
4012
|
modelsEndpoint,
|
|
4013
|
+
// Static extra headers: load-guard (reserved names dropped), collapse-to-
|
|
4014
|
+
// undefined; enforced by the outbound header funnel + admin probes.
|
|
4015
|
+
extraHeaders: validateExtraHeaders(p["extraHeaders"]),
|
|
2552
4016
|
// Provider transformer config (app-parity child 5): load-guard, collapse-to-
|
|
2553
4017
|
// undefined; non-secret; ENFORCED via resolveTransformerChain (parity-2 child 2).
|
|
2554
4018
|
// Format-axis entries are stripped by `migrateFormatAxis` — `use[]` is the
|
|
@@ -2618,7 +4082,7 @@ var import_node_crypto6 = require("crypto");
|
|
|
2618
4082
|
var import_node_fs10 = require("fs");
|
|
2619
4083
|
var import_node_os3 = require("os");
|
|
2620
4084
|
var import_node_path10 = require("path");
|
|
2621
|
-
var
|
|
4085
|
+
var import_core2 = require("@omnicross/core");
|
|
2622
4086
|
|
|
2623
4087
|
// src/integrations/codexAuthHelper.ts
|
|
2624
4088
|
var import_node_path8 = require("path");
|
|
@@ -3099,7 +4563,7 @@ var IntegrationManager = class {
|
|
|
3099
4563
|
if (!secret) {
|
|
3100
4564
|
throw new IntegrationConflictError("The selected access key cannot be revealed and cannot power a CLI integration.");
|
|
3101
4565
|
}
|
|
3102
|
-
const effective = [...(0,
|
|
4566
|
+
const effective = [...(0, import_core2.effectiveOutboundPermissions)(row.allowedEndpoints)];
|
|
3103
4567
|
const previousPermissions = row.allowedEndpoints === void 0 ? [...effective] : [...row.allowedEndpoints];
|
|
3104
4568
|
const nextPermissions = [...effective];
|
|
3105
4569
|
for (const required of REQUIRED_PERMISSIONS[client]) {
|
|
@@ -3197,7 +4661,7 @@ var IntegrationManager = class {
|
|
|
3197
4661
|
return { binding, row, secret, created: false };
|
|
3198
4662
|
}
|
|
3199
4663
|
async createManagedClientKey(client, state) {
|
|
3200
|
-
const created = await (0,
|
|
4664
|
+
const created = await (0, import_core2.createIntegrationKey)(
|
|
3201
4665
|
this.options.keyDb,
|
|
3202
4666
|
`Omnicross ${displayClient(client)} integration`,
|
|
3203
4667
|
[...REQUIRED_PERMISSIONS[client]]
|
|
@@ -3289,7 +4753,7 @@ var IntegrationManager = class {
|
|
|
3289
4753
|
const row = rows.find((candidate) => candidate.id === keyId);
|
|
3290
4754
|
if (!row) return { usable: false, message: "The bound access key no longer exists." };
|
|
3291
4755
|
const secret = legacy?.secret ?? await this.options.keyDb.outboundApiKeysReveal(keyId) ?? void 0;
|
|
3292
|
-
const allowedEndpoints = [...(0,
|
|
4756
|
+
const allowedEndpoints = [...(0, import_core2.effectiveOutboundPermissions)(row.allowedEndpoints)];
|
|
3293
4757
|
const status = {
|
|
3294
4758
|
id: row.id,
|
|
3295
4759
|
name: row.name,
|
|
@@ -3333,7 +4797,7 @@ var IntegrationManager = class {
|
|
|
3333
4797
|
}
|
|
3334
4798
|
};
|
|
3335
4799
|
function hasRequiredPermissions(row, client) {
|
|
3336
|
-
const allowed = (0,
|
|
4800
|
+
const allowed = (0, import_core2.effectiveOutboundPermissions)(row.allowedEndpoints);
|
|
3337
4801
|
return REQUIRED_PERMISSIONS[client].every((permission) => allowed.includes(permission));
|
|
3338
4802
|
}
|
|
3339
4803
|
function samePermissions(a, b) {
|
|
@@ -3502,7 +4966,10 @@ function mapPresetToProvider(preset, opts) {
|
|
|
3502
4966
|
apiFormat: resolved.format,
|
|
3503
4967
|
baseUrl: opts.baseUrlOverride ?? preset.api_base_url,
|
|
3504
4968
|
apiKey: opts.key,
|
|
3505
|
-
models: Array.isArray(preset.models) ? preset.models : void 0
|
|
4969
|
+
models: Array.isArray(preset.models) ? preset.models : void 0,
|
|
4970
|
+
// Static identity headers (e.g. the Cline client set) survive the mapping —
|
|
4971
|
+
// the CLI-seeded row needs them as much as an admin-API-created one.
|
|
4972
|
+
extraHeaders: preset.extraHeaders
|
|
3506
4973
|
};
|
|
3507
4974
|
return { provider };
|
|
3508
4975
|
}
|
|
@@ -3527,7 +4994,8 @@ function listMappablePresets() {
|
|
|
3527
4994
|
description: preset.description,
|
|
3528
4995
|
features: preset.features,
|
|
3529
4996
|
website: preset.website,
|
|
3530
|
-
modelsEndpoint: preset.modelsEndpoint
|
|
4997
|
+
modelsEndpoint: preset.modelsEndpoint,
|
|
4998
|
+
extraHeaders: preset.extraHeaders
|
|
3531
4999
|
});
|
|
3532
5000
|
}
|
|
3533
5001
|
return { mappable, excluded };
|
|
@@ -3617,11 +5085,11 @@ function preserveOutboundProxySecrets(incoming, current) {
|
|
|
3617
5085
|
}
|
|
3618
5086
|
|
|
3619
5087
|
// src/proxy/upstreamProxyResolver.ts
|
|
3620
|
-
var
|
|
5088
|
+
var import_upstreamFetch7 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
3621
5089
|
var serverProxy;
|
|
3622
5090
|
function setServerProxyConfig(proxy) {
|
|
3623
5091
|
serverProxy = proxy;
|
|
3624
|
-
(0,
|
|
5092
|
+
(0, import_upstreamFetch7.bumpUpstreamProxyGeneration)();
|
|
3625
5093
|
}
|
|
3626
5094
|
function getServerProxyConfig() {
|
|
3627
5095
|
return serverProxy;
|
|
@@ -3689,14 +5157,17 @@ function createUpstreamProxyResolver(src = {}) {
|
|
|
3689
5157
|
}
|
|
3690
5158
|
|
|
3691
5159
|
// src/admin/accountsOAuth.ts
|
|
3692
|
-
var
|
|
5160
|
+
var import_subscriptions8 = require("@omnicross/subscriptions");
|
|
3693
5161
|
|
|
3694
5162
|
// src/admin/accountsWrite.ts
|
|
3695
5163
|
var VALID_PROVIDER_IDS = [
|
|
3696
5164
|
"claude",
|
|
3697
5165
|
"codex",
|
|
3698
5166
|
"gemini",
|
|
3699
|
-
"opencodego"
|
|
5167
|
+
"opencodego",
|
|
5168
|
+
"kimi",
|
|
5169
|
+
"grok",
|
|
5170
|
+
"copilot"
|
|
3700
5171
|
];
|
|
3701
5172
|
function asSubscriptionProviderId(id) {
|
|
3702
5173
|
return VALID_PROVIDER_IDS.includes(id) ? id : null;
|
|
@@ -3832,6 +5303,52 @@ function validateGemini(body) {
|
|
|
3832
5303
|
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "lastRefreshedAt", "errorMessage"]);
|
|
3833
5304
|
return out;
|
|
3834
5305
|
}
|
|
5306
|
+
function validateKimi(body) {
|
|
5307
|
+
const authMethod = str(body["authMethod"]);
|
|
5308
|
+
const status = str(body["status"]);
|
|
5309
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
5310
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
5311
|
+
const out = {
|
|
5312
|
+
authMethod,
|
|
5313
|
+
status
|
|
5314
|
+
};
|
|
5315
|
+
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "deviceId", "lastRefreshedAt", "errorMessage"]);
|
|
5316
|
+
return out;
|
|
5317
|
+
}
|
|
5318
|
+
function validateGrok(body) {
|
|
5319
|
+
const authMethod = str(body["authMethod"]);
|
|
5320
|
+
const status = str(body["status"]);
|
|
5321
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
5322
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
5323
|
+
const out = {
|
|
5324
|
+
authMethod,
|
|
5325
|
+
status
|
|
5326
|
+
};
|
|
5327
|
+
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "lastRefreshedAt", "errorMessage"]);
|
|
5328
|
+
return out;
|
|
5329
|
+
}
|
|
5330
|
+
function validateCopilot(body) {
|
|
5331
|
+
const authMethod = str(body["authMethod"]);
|
|
5332
|
+
const status = str(body["status"]);
|
|
5333
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
5334
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
5335
|
+
const out = {
|
|
5336
|
+
authMethod,
|
|
5337
|
+
status
|
|
5338
|
+
};
|
|
5339
|
+
copyOptional(out, body, [
|
|
5340
|
+
"accessToken",
|
|
5341
|
+
"refreshToken",
|
|
5342
|
+
"expiresAt",
|
|
5343
|
+
"accountId",
|
|
5344
|
+
"email",
|
|
5345
|
+
"apiEndpoint",
|
|
5346
|
+
"enterpriseUrl",
|
|
5347
|
+
"lastRefreshedAt",
|
|
5348
|
+
"errorMessage"
|
|
5349
|
+
]);
|
|
5350
|
+
return out;
|
|
5351
|
+
}
|
|
3835
5352
|
function validateOpenCodeGo(body) {
|
|
3836
5353
|
const authMethod = str(body["authMethod"]);
|
|
3837
5354
|
const status = str(body["status"]);
|
|
@@ -3867,6 +5384,12 @@ function validateTokenBody(providerId, body) {
|
|
|
3867
5384
|
return validateGemini(body);
|
|
3868
5385
|
case "opencodego":
|
|
3869
5386
|
return validateOpenCodeGo(body);
|
|
5387
|
+
case "kimi":
|
|
5388
|
+
return validateKimi(body);
|
|
5389
|
+
case "grok":
|
|
5390
|
+
return validateGrok(body);
|
|
5391
|
+
case "copilot":
|
|
5392
|
+
return validateCopilot(body);
|
|
3870
5393
|
default:
|
|
3871
5394
|
return null;
|
|
3872
5395
|
}
|
|
@@ -3896,37 +5419,37 @@ async function statusEntryFor(reader, providerId) {
|
|
|
3896
5419
|
|
|
3897
5420
|
// src/admin/accountsOAuth.ts
|
|
3898
5421
|
var OAUTH_HTTP_PROVIDERS = /* @__PURE__ */ new Set(["claude", "gemini"]);
|
|
3899
|
-
function
|
|
5422
|
+
function err5(status, message) {
|
|
3900
5423
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
3901
5424
|
}
|
|
3902
5425
|
function handleOAuthStart(providerId, deps) {
|
|
3903
5426
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
3904
|
-
return
|
|
5427
|
+
return err5(400, `oauth not available for provider '${providerId}'`);
|
|
3905
5428
|
}
|
|
3906
|
-
const flow = providerId === "claude" ?
|
|
5429
|
+
const flow = providerId === "claude" ? import_subscriptions8.claudeOAuth : import_subscriptions8.geminiOAuth;
|
|
3907
5430
|
const { authUrl, codeVerifier, state } = flow.generateAuthParams();
|
|
3908
5431
|
const sessionId = deps.oauthSessions.put({ providerId, codeVerifier, state });
|
|
3909
5432
|
return { status: 200, body: { authUrl, sessionId } };
|
|
3910
5433
|
}
|
|
3911
5434
|
async function handleOAuthComplete(providerId, body, deps) {
|
|
3912
5435
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
3913
|
-
return
|
|
5436
|
+
return err5(400, `oauth not available for provider '${providerId}'`);
|
|
3914
5437
|
}
|
|
3915
5438
|
const sessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : "";
|
|
3916
5439
|
const rawCode = typeof body["code"] === "string" ? body["code"] : "";
|
|
3917
|
-
if (!sessionId) return
|
|
3918
|
-
if (!rawCode) return
|
|
5440
|
+
if (!sessionId) return err5(400, "oauth complete requires { sessionId }");
|
|
5441
|
+
if (!rawCode) return err5(400, "oauth complete requires { code }");
|
|
3919
5442
|
const session = deps.oauthSessions.peek(sessionId);
|
|
3920
|
-
if (!session) return
|
|
5443
|
+
if (!session) return err5(410, "oauth session is unknown, expired, or already used");
|
|
3921
5444
|
if (session.providerId !== providerId) {
|
|
3922
|
-
return
|
|
5445
|
+
return err5(400, `oauth session does not match provider '${providerId}'`);
|
|
3923
5446
|
}
|
|
3924
5447
|
let code = rawCode.trim();
|
|
3925
5448
|
if (providerId === "claude") {
|
|
3926
5449
|
const [splitCode, pastedState] = code.split("#");
|
|
3927
|
-
if (!splitCode) return
|
|
5450
|
+
if (!splitCode) return err5(400, "no authorization code was provided");
|
|
3928
5451
|
if (pastedState && pastedState !== session.state) {
|
|
3929
|
-
return
|
|
5452
|
+
return err5(400, "oauth state did not match (possible CSRF) \u2014 aborting");
|
|
3930
5453
|
}
|
|
3931
5454
|
code = splitCode;
|
|
3932
5455
|
}
|
|
@@ -3936,7 +5459,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
3936
5459
|
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
|
|
3937
5460
|
} catch (exchangeError) {
|
|
3938
5461
|
const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
|
|
3939
|
-
return
|
|
5462
|
+
return err5(502, `oauth token exchange failed for '${providerId}': ${reason}`);
|
|
3940
5463
|
}
|
|
3941
5464
|
deps.oauthSessions.consume(sessionId);
|
|
3942
5465
|
const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
|
|
@@ -3945,7 +5468,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
3945
5468
|
return { status: 200, body: status ? { account: status } : { ok: true } };
|
|
3946
5469
|
}
|
|
3947
5470
|
async function exchangeClaude(code, codeVerifier, state, exchangeFetch) {
|
|
3948
|
-
const result = await
|
|
5471
|
+
const result = await import_subscriptions8.claudeOAuth.exchangeCodeForTokens(
|
|
3949
5472
|
{ authorizationCode: code, codeVerifier, state },
|
|
3950
5473
|
exchangeFetch
|
|
3951
5474
|
);
|
|
@@ -3961,7 +5484,7 @@ async function exchangeClaude(code, codeVerifier, state, exchangeFetch) {
|
|
|
3961
5484
|
};
|
|
3962
5485
|
}
|
|
3963
5486
|
async function exchangeGemini(code, codeVerifier, exchangeFetch) {
|
|
3964
|
-
const result = await
|
|
5487
|
+
const result = await import_subscriptions8.geminiOAuth.exchangeCodeForTokens(code, codeVerifier, exchangeFetch);
|
|
3965
5488
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
3966
5489
|
return {
|
|
3967
5490
|
authMethod: "oauth",
|
|
@@ -4266,8 +5789,8 @@ function errBody(message) {
|
|
|
4266
5789
|
return { error: { type: "admin_api_error", message } };
|
|
4267
5790
|
}
|
|
4268
5791
|
var defaultCommandRunner = (command) => new Promise((resolve11) => {
|
|
4269
|
-
(0, import_node_child_process.exec)(command, { timeout: 18e4 }, (
|
|
4270
|
-
if (
|
|
5792
|
+
(0, import_node_child_process.exec)(command, { timeout: 18e4 }, (err8, _stdout, stderr) => {
|
|
5793
|
+
if (err8) resolve11({ ok: false, error: stderr.trim() || err8.message });
|
|
4271
5794
|
else resolve11({ ok: true });
|
|
4272
5795
|
});
|
|
4273
5796
|
});
|
|
@@ -4313,8 +5836,8 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
4313
5836
|
providerId: typeof body["providerId"] === "string" ? body["providerId"] : void 0,
|
|
4314
5837
|
model: typeof body["model"] === "string" ? body["model"] : void 0
|
|
4315
5838
|
});
|
|
4316
|
-
} catch (
|
|
4317
|
-
return { status: 400, body: errBody(
|
|
5839
|
+
} catch (err8) {
|
|
5840
|
+
return { status: 400, body: errBody(err8 instanceof Error ? err8.message : "no launch target") };
|
|
4318
5841
|
}
|
|
4319
5842
|
const id = (0, import_node_crypto7.randomUUID)();
|
|
4320
5843
|
let leaseId2;
|
|
@@ -4342,9 +5865,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
4342
5865
|
} else {
|
|
4343
5866
|
launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
|
|
4344
5867
|
}
|
|
4345
|
-
} catch (
|
|
4346
|
-
const status =
|
|
4347
|
-
return { status, body: errBody(
|
|
5868
|
+
} catch (err8) {
|
|
5869
|
+
const status = err8 instanceof import_provider_proxy2.RouteLeaseError ? err8.status : 400;
|
|
5870
|
+
return { status, body: errBody(err8 instanceof Error ? err8.message : "failed to build launch env") };
|
|
4348
5871
|
}
|
|
4349
5872
|
const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
|
|
4350
5873
|
const opener = ctx.opener ?? defaultTerminalOpener;
|
|
@@ -4372,9 +5895,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
4372
5895
|
onFailure: onSessionEnd
|
|
4373
5896
|
});
|
|
4374
5897
|
if (cleanup) openerCleanup = cleanup;
|
|
4375
|
-
} catch (
|
|
5898
|
+
} catch (err8) {
|
|
4376
5899
|
onSessionEnd();
|
|
4377
|
-
return { status: 500, body: errBody(
|
|
5900
|
+
return { status: 500, body: errBody(err8 instanceof Error ? err8.message : "failed to open terminal") };
|
|
4378
5901
|
}
|
|
4379
5902
|
if (ended) {
|
|
4380
5903
|
openerCleanup?.();
|
|
@@ -4629,7 +6152,7 @@ async function runSearchLiveChecks(contributions, now = () => (/* @__PURE__ */ n
|
|
|
4629
6152
|
}
|
|
4630
6153
|
|
|
4631
6154
|
// src/search/SearchAssembly.ts
|
|
4632
|
-
var
|
|
6155
|
+
var import_upstreamFetch8 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
4633
6156
|
var import_search = require("@omnicross/core/search");
|
|
4634
6157
|
var import_api2 = require("@omnicross/core/search/api");
|
|
4635
6158
|
var import_http2 = require("@omnicross/core/search/http");
|
|
@@ -4647,7 +6170,7 @@ function searchPolicyFrom(config) {
|
|
|
4647
6170
|
};
|
|
4648
6171
|
}
|
|
4649
6172
|
function resolveSearchUpstreamDispatcher(url) {
|
|
4650
|
-
return (0,
|
|
6173
|
+
return (0, import_upstreamFetch8.resolveUpstreamDispatcher)({ url });
|
|
4651
6174
|
}
|
|
4652
6175
|
var searchUpstreamProxyConfig = createUpstreamProxyResolver();
|
|
4653
6176
|
function resolveSearchUpstreamProxyConfig(url) {
|
|
@@ -4929,7 +6452,7 @@ async function handleSearchQuery(req, res, deps) {
|
|
|
4929
6452
|
// src/admin/searchAdminView.ts
|
|
4930
6453
|
var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
|
|
4931
6454
|
var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
|
|
4932
|
-
function
|
|
6455
|
+
function isRecord4(value) {
|
|
4933
6456
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4934
6457
|
}
|
|
4935
6458
|
function redactSearchServerConfig(search) {
|
|
@@ -4979,13 +6502,13 @@ function resolveSecretField(entry, field, stored) {
|
|
|
4979
6502
|
else delete entry[field];
|
|
4980
6503
|
}
|
|
4981
6504
|
function preserveSearchSecrets(incoming, current) {
|
|
4982
|
-
if (!
|
|
6505
|
+
if (!isRecord4(incoming)) return incoming;
|
|
4983
6506
|
const section = { ...incoming };
|
|
4984
6507
|
const providersValue = section["providers"];
|
|
4985
|
-
if (!
|
|
6508
|
+
if (!isRecord4(providersValue)) return section;
|
|
4986
6509
|
const providers = {};
|
|
4987
6510
|
for (const [id, entryValue] of Object.entries(providersValue)) {
|
|
4988
|
-
if (!
|
|
6511
|
+
if (!isRecord4(entryValue)) {
|
|
4989
6512
|
providers[id] = entryValue;
|
|
4990
6513
|
continue;
|
|
4991
6514
|
}
|
|
@@ -5063,7 +6586,7 @@ function parseKeyPolicyBody(body) {
|
|
|
5063
6586
|
var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
|
|
5064
6587
|
var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
|
|
5065
6588
|
var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
|
|
5066
|
-
function
|
|
6589
|
+
function isRecord5(value) {
|
|
5067
6590
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
5068
6591
|
}
|
|
5069
6592
|
function nonBlank(value) {
|
|
@@ -5083,7 +6606,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5083
6606
|
const ids = /* @__PURE__ */ new Set();
|
|
5084
6607
|
raw.forEach((entry, index) => {
|
|
5085
6608
|
const path2 = `bindings[${index}]`;
|
|
5086
|
-
if (!
|
|
6609
|
+
if (!isRecord5(entry)) {
|
|
5087
6610
|
errors.push(`${path2} must be an object`);
|
|
5088
6611
|
return;
|
|
5089
6612
|
}
|
|
@@ -5112,12 +6635,12 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5112
6635
|
} else if (entry.modelMappings.length > 100) {
|
|
5113
6636
|
errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
|
|
5114
6637
|
} else if (entry.modelMappings.some(
|
|
5115
|
-
(mapping) => !
|
|
6638
|
+
(mapping) => !isRecord5(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
|
|
5116
6639
|
)) {
|
|
5117
6640
|
errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
|
|
5118
6641
|
}
|
|
5119
6642
|
}
|
|
5120
|
-
if (!
|
|
6643
|
+
if (!isRecord5(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
|
|
5121
6644
|
errors.push(`${path2}.target is invalid`);
|
|
5122
6645
|
} else {
|
|
5123
6646
|
if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
|
|
@@ -5132,7 +6655,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5132
6655
|
}
|
|
5133
6656
|
}
|
|
5134
6657
|
if (entry.modelMap !== void 0) {
|
|
5135
|
-
if (!
|
|
6658
|
+
if (!isRecord5(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
|
|
5136
6659
|
errors.push(`${path2}.modelMap must contain string values`);
|
|
5137
6660
|
}
|
|
5138
6661
|
}
|
|
@@ -5414,7 +6937,10 @@ var PROVIDER_KEYS = {
|
|
|
5414
6937
|
block: "opencodego",
|
|
5415
6938
|
accounts: "opencodegoAccounts",
|
|
5416
6939
|
active: "activeOpencodegoAccountId"
|
|
5417
|
-
}
|
|
6940
|
+
},
|
|
6941
|
+
kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" },
|
|
6942
|
+
grok: { block: "grok", accounts: "grokAccounts", active: "activeGrokAccountId" },
|
|
6943
|
+
copilot: { block: "copilot", accounts: "copilotAccounts", active: "activeCopilotAccountId" }
|
|
5418
6944
|
};
|
|
5419
6945
|
function clone(value) {
|
|
5420
6946
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -5936,7 +7462,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
|
|
|
5936
7462
|
}
|
|
5937
7463
|
|
|
5938
7464
|
// src/admin/adminMigration.ts
|
|
5939
|
-
function
|
|
7465
|
+
function err6(status, message) {
|
|
5940
7466
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
5941
7467
|
}
|
|
5942
7468
|
async function handleExport(body, deps) {
|
|
@@ -5946,30 +7472,30 @@ async function handleExport(body, deps) {
|
|
|
5946
7472
|
return { status: 200, body: { pack, version: BUNDLE_VERSION } };
|
|
5947
7473
|
} catch (error) {
|
|
5948
7474
|
if (error instanceof WeakPassphraseError) {
|
|
5949
|
-
return
|
|
7475
|
+
return err6(400, error.message);
|
|
5950
7476
|
}
|
|
5951
|
-
return
|
|
7477
|
+
return err6(500, "failed to build the migration pack");
|
|
5952
7478
|
}
|
|
5953
7479
|
}
|
|
5954
7480
|
async function handleImport(body, deps) {
|
|
5955
7481
|
const blob = typeof body["blob"] === "string" ? body["blob"] : "";
|
|
5956
7482
|
const passphrase = typeof body["passphrase"] === "string" ? body["passphrase"] : "";
|
|
5957
7483
|
const mode = body["mode"] === "overwrite" ? "overwrite" : "merge";
|
|
5958
|
-
if (!blob) return
|
|
7484
|
+
if (!blob) return err6(400, "import requires { blob }");
|
|
5959
7485
|
try {
|
|
5960
7486
|
const counts = await applyImport(blob, passphrase, mode, deps, deps.parseProviderInput);
|
|
5961
7487
|
return { status: 200, body: counts };
|
|
5962
7488
|
} catch (error) {
|
|
5963
7489
|
if (error instanceof WeakPassphraseError) {
|
|
5964
|
-
return
|
|
7490
|
+
return err6(400, error.message);
|
|
5965
7491
|
}
|
|
5966
|
-
return
|
|
7492
|
+
return err6(400, error instanceof Error ? error.message : "import failed");
|
|
5967
7493
|
}
|
|
5968
7494
|
}
|
|
5969
7495
|
|
|
5970
7496
|
// src/admin/usagePricing.ts
|
|
5971
7497
|
var import_usage = require("@omnicross/core/usage");
|
|
5972
|
-
var
|
|
7498
|
+
var err7 = (status, message) => ({
|
|
5973
7499
|
status,
|
|
5974
7500
|
body: { error: { type: "admin_api_error", message } }
|
|
5975
7501
|
});
|
|
@@ -5982,7 +7508,7 @@ function parseRange(query2) {
|
|
|
5982
7508
|
const startTs = parseFiniteInt(query2.get("startTs"));
|
|
5983
7509
|
const endTs = parseFiniteInt(query2.get("endTs"));
|
|
5984
7510
|
if (startTs === null || endTs === null) {
|
|
5985
|
-
return
|
|
7511
|
+
return err7(400, "startTs and endTs are required finite-integer unix-millis query params");
|
|
5986
7512
|
}
|
|
5987
7513
|
return { startTs, endTs };
|
|
5988
7514
|
}
|
|
@@ -6007,14 +7533,14 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
6007
7533
|
case "timeseries": {
|
|
6008
7534
|
const bucket = query2.get("bucket");
|
|
6009
7535
|
if (bucket !== "hour" && bucket !== "day" && bucket !== "month") {
|
|
6010
|
-
return
|
|
7536
|
+
return err7(400, "bucket must be one of 'hour', 'day', 'month'");
|
|
6011
7537
|
}
|
|
6012
7538
|
const now = Date.now();
|
|
6013
7539
|
const clamped = { startTs: range.startTs, endTs: Math.min(range.endTs, now) };
|
|
6014
7540
|
if (clamped.startTs < clamped.endTs) {
|
|
6015
7541
|
const projected = Math.ceil((clamped.endTs - clamped.startTs) / BUCKET_SPAN_MS[bucket]) + 1;
|
|
6016
7542
|
if (projected > MAX_TIMESERIES_BUCKETS) {
|
|
6017
|
-
return
|
|
7543
|
+
return err7(
|
|
6018
7544
|
400,
|
|
6019
7545
|
`requested range projects ~${projected} '${bucket}' buckets (max ${MAX_TIMESERIES_BUCKETS}); narrow the range or use a coarser bucket`
|
|
6020
7546
|
);
|
|
@@ -6037,7 +7563,7 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
6037
7563
|
};
|
|
6038
7564
|
}
|
|
6039
7565
|
default:
|
|
6040
|
-
return
|
|
7566
|
+
return err7(404, `unknown usage view '${view ?? ""}'`);
|
|
6041
7567
|
}
|
|
6042
7568
|
}
|
|
6043
7569
|
function poolKeyLabels(cfg) {
|
|
@@ -6086,7 +7612,7 @@ async function handlePricingList(deps) {
|
|
|
6086
7612
|
async function handlePricingUpsert(body, deps) {
|
|
6087
7613
|
const input = parsePricingEntryInput(body);
|
|
6088
7614
|
if (!input) {
|
|
6089
|
-
return
|
|
7615
|
+
return err7(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
|
|
6090
7616
|
}
|
|
6091
7617
|
const entry = await deps.pricingEngine.upsertManual(input);
|
|
6092
7618
|
return { status: 200, body: { entry } };
|
|
@@ -6095,7 +7621,7 @@ async function handlePricingDelete(query2, deps) {
|
|
|
6095
7621
|
const providerId = query2.get("providerId")?.trim() ?? "";
|
|
6096
7622
|
const modelId = query2.get("modelId")?.trim() ?? "";
|
|
6097
7623
|
if (!providerId || !modelId) {
|
|
6098
|
-
return
|
|
7624
|
+
return err7(400, "delete requires providerId and modelId query params");
|
|
6099
7625
|
}
|
|
6100
7626
|
const deleted = await deps.pricingStore.delete(providerId, modelId);
|
|
6101
7627
|
if (deleted) await deps.pricingEngine.invalidateCache();
|
|
@@ -6115,13 +7641,13 @@ async function handlePricingFetchLatest(deps) {
|
|
|
6115
7641
|
}
|
|
6116
7642
|
};
|
|
6117
7643
|
} catch (e) {
|
|
6118
|
-
return
|
|
7644
|
+
return err7(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
6119
7645
|
}
|
|
6120
7646
|
}
|
|
6121
7647
|
async function handlePricingResolveConflicts(body, deps) {
|
|
6122
7648
|
const raw = body["resolutions"];
|
|
6123
7649
|
if (!Array.isArray(raw)) {
|
|
6124
|
-
return
|
|
7650
|
+
return err7(400, "resolve-conflicts requires { resolutions: [...] }");
|
|
6125
7651
|
}
|
|
6126
7652
|
const currentRows = await deps.pricingStore.getAll();
|
|
6127
7653
|
const userEditedKeys = new Set(
|
|
@@ -6131,21 +7657,21 @@ async function handlePricingResolveConflicts(body, deps) {
|
|
|
6131
7657
|
const pendingIncoming = /* @__PURE__ */ new Map();
|
|
6132
7658
|
let staleCount = 0;
|
|
6133
7659
|
for (const item of raw) {
|
|
6134
|
-
if (!item || typeof item !== "object") return
|
|
7660
|
+
if (!item || typeof item !== "object") return err7(400, "invalid resolution entry");
|
|
6135
7661
|
const r = item;
|
|
6136
7662
|
const action = r["action"];
|
|
6137
7663
|
if (action !== "overwrite" && action !== "skip") {
|
|
6138
|
-
return
|
|
7664
|
+
return err7(400, "resolution action must be 'overwrite' or 'skip'");
|
|
6139
7665
|
}
|
|
6140
7666
|
const providerId = typeof r["providerId"] === "string" && r["providerId"].trim() ? r["providerId"].trim() : "";
|
|
6141
7667
|
const modelId = typeof r["modelId"] === "string" && r["modelId"].trim() ? r["modelId"].trim() : "";
|
|
6142
7668
|
if (!providerId || !modelId) {
|
|
6143
|
-
return
|
|
7669
|
+
return err7(400, "each resolution requires top-level providerId and modelId");
|
|
6144
7670
|
}
|
|
6145
7671
|
const incoming = parsePricingEntryInput(r["incoming"]);
|
|
6146
|
-
if (!incoming) return
|
|
7672
|
+
if (!incoming) return err7(400, "each resolution must echo a valid incoming pricing entry");
|
|
6147
7673
|
if (incoming.providerId !== providerId || incoming.modelId !== modelId) {
|
|
6148
|
-
return
|
|
7674
|
+
return err7(400, "resolution providerId/modelId must match the echoed incoming entry");
|
|
6149
7675
|
}
|
|
6150
7676
|
const key = `${providerId}::${modelId}`;
|
|
6151
7677
|
if (action === "overwrite" && !userEditedKeys.has(key)) {
|
|
@@ -6190,7 +7716,7 @@ function query(req) {
|
|
|
6190
7716
|
}
|
|
6191
7717
|
function allowanceProvider(value) {
|
|
6192
7718
|
if (!value) return void 0;
|
|
6193
|
-
return value === "claude" || value === "codex" ? value : null;
|
|
7719
|
+
return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" || value === "grok" || value === "copilot" ? value : null;
|
|
6194
7720
|
}
|
|
6195
7721
|
async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
6196
7722
|
if (!service) return writeError2(res, 501, "account allowance service is not available");
|
|
@@ -6204,7 +7730,9 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
6204
7730
|
const params = query(req);
|
|
6205
7731
|
const pathProvider = rest.length >= 2 ? rest[0] : null;
|
|
6206
7732
|
const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
|
|
6207
|
-
if (providerId === null)
|
|
7733
|
+
if (providerId === null) {
|
|
7734
|
+
return writeError2(res, 400, "providerId must be claude, codex, kimi, opencodego, grok, or copilot");
|
|
7735
|
+
}
|
|
6208
7736
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
6209
7737
|
const allowances = await service.list({ providerId, accountId });
|
|
6210
7738
|
return writeJson3(res, 200, { allowances });
|
|
@@ -6214,10 +7742,57 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
6214
7742
|
const requestedProvider = allowanceProvider(
|
|
6215
7743
|
typeof body["providerId"] === "string" ? body["providerId"] : "claude"
|
|
6216
7744
|
);
|
|
6217
|
-
if (requestedProvider !== "claude") {
|
|
6218
|
-
return writeError2(res, 400, "only Claude allowances support explicit refresh");
|
|
6219
|
-
}
|
|
6220
7745
|
const accountId = typeof body["accountId"] === "string" && body["accountId"].trim() ? body["accountId"].trim() : void 0;
|
|
7746
|
+
if (requestedProvider === "codex") {
|
|
7747
|
+
if (!service.refreshCodex) {
|
|
7748
|
+
return writeError2(res, 501, "codex allowance refresh is not available");
|
|
7749
|
+
}
|
|
7750
|
+
const allowances2 = await service.refreshCodex(accountId);
|
|
7751
|
+
if (accountId && allowances2.length === 0) {
|
|
7752
|
+
return writeError2(res, 404, `Codex account '${accountId}' not found`);
|
|
7753
|
+
}
|
|
7754
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7755
|
+
}
|
|
7756
|
+
if (requestedProvider === "kimi") {
|
|
7757
|
+
if (!service.refreshKimi) {
|
|
7758
|
+
return writeError2(res, 501, "kimi allowance refresh is not available");
|
|
7759
|
+
}
|
|
7760
|
+
const allowances2 = await service.refreshKimi(accountId);
|
|
7761
|
+
if (accountId && allowances2.length === 0) {
|
|
7762
|
+
return writeError2(res, 404, `Kimi account '${accountId}' not found`);
|
|
7763
|
+
}
|
|
7764
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7765
|
+
}
|
|
7766
|
+
if (requestedProvider === "opencodego") {
|
|
7767
|
+
if (!service.refreshOpenCodeGo) {
|
|
7768
|
+
return writeError2(res, 501, "opencodego allowance refresh is not available");
|
|
7769
|
+
}
|
|
7770
|
+
const allowances2 = await service.refreshOpenCodeGo(accountId);
|
|
7771
|
+
if (accountId && allowances2.length === 0) {
|
|
7772
|
+
return writeError2(res, 404, `OpenCodeGo account '${accountId}' not found`);
|
|
7773
|
+
}
|
|
7774
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7775
|
+
}
|
|
7776
|
+
if (requestedProvider === "copilot") {
|
|
7777
|
+
if (!service.refreshCopilot) {
|
|
7778
|
+
return writeError2(res, 501, "copilot allowance refresh is not available");
|
|
7779
|
+
}
|
|
7780
|
+
const allowances2 = await service.refreshCopilot(accountId);
|
|
7781
|
+
if (accountId && allowances2.length === 0) {
|
|
7782
|
+
return writeError2(res, 404, `Copilot account '${accountId}' not found`);
|
|
7783
|
+
}
|
|
7784
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7785
|
+
}
|
|
7786
|
+
if (requestedProvider === "grok") {
|
|
7787
|
+
if (!service.refreshGrok) {
|
|
7788
|
+
return writeError2(res, 501, "grok allowance refresh is not available");
|
|
7789
|
+
}
|
|
7790
|
+
const allowances2 = await service.refreshGrok(accountId);
|
|
7791
|
+
if (accountId && allowances2.length === 0) {
|
|
7792
|
+
return writeError2(res, 404, `Grok account '${accountId}' not found`);
|
|
7793
|
+
}
|
|
7794
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7795
|
+
}
|
|
6221
7796
|
const allowances = await service.refreshClaude(accountId);
|
|
6222
7797
|
if (accountId && allowances.length === 0) {
|
|
6223
7798
|
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
@@ -6316,6 +7891,9 @@ function toProviderView(row) {
|
|
|
6316
7891
|
apiVersion: row.apiVersion,
|
|
6317
7892
|
maxConcurrency: row.maxConcurrency,
|
|
6318
7893
|
modelsEndpoint: row.modelsEndpoint,
|
|
7894
|
+
// Static extra headers round-trip VERBATIM (non-secret identity values;
|
|
7895
|
+
// auth/content names were already dropped at the write/load gate).
|
|
7896
|
+
extraHeaders: row.extraHeaders,
|
|
6319
7897
|
// app-parity child 5: transformer config round-trips VERBATIM (non-secret —
|
|
6320
7898
|
// transform-rule names + options, no key material; absent stays absent).
|
|
6321
7899
|
transformer: row.transformer,
|
|
@@ -6385,8 +7963,8 @@ async function handleAdminApi(req, res, path2, deps) {
|
|
|
6385
7963
|
default:
|
|
6386
7964
|
return writeJsonError(res, 404, `unknown admin resource '${resource}'`);
|
|
6387
7965
|
}
|
|
6388
|
-
} catch (
|
|
6389
|
-
writeJsonError(res, 500,
|
|
7966
|
+
} catch (err8) {
|
|
7967
|
+
writeJsonError(res, 500, err8 instanceof Error ? err8.message : String(err8));
|
|
6390
7968
|
}
|
|
6391
7969
|
}
|
|
6392
7970
|
function requestQuery(req) {
|
|
@@ -6456,6 +8034,9 @@ async function handleProviders(req, res, method, rest, deps) {
|
|
|
6456
8034
|
if (method === "POST" && rest.length === 4 && rest[1] === "keys" && rest[3] === "enabled") {
|
|
6457
8035
|
return await handleToggleProviderKey(req, res, rest[0], rest[2], cfg, deps);
|
|
6458
8036
|
}
|
|
8037
|
+
if (method === "POST" && rest.length === 5 && rest[1] === "keys" && rest[3] === "quota" && rest[4] === "refresh") {
|
|
8038
|
+
return await handleProviderKeyQuotaRefresh(res, rest[0], rest[2], cfg, deps);
|
|
8039
|
+
}
|
|
6459
8040
|
if (method === "PUT" && rest.length === 3 && rest[1] === "keys") {
|
|
6460
8041
|
return await handleUpdateProviderKey(req, res, rest[0], rest[2], cfg, deps);
|
|
6461
8042
|
}
|
|
@@ -6541,6 +8122,9 @@ async function handleProviderReorder(req, res, cfg, deps) {
|
|
|
6541
8122
|
persistProviders(cfg, deps);
|
|
6542
8123
|
return writeJson4(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
|
|
6543
8124
|
}
|
|
8125
|
+
function expandRowExtraHeaders(row) {
|
|
8126
|
+
return (0, import_core3.mergeExtraHeaders)({}, row.extraHeaders);
|
|
8127
|
+
}
|
|
6544
8128
|
async function handleDiscoverModels(res, id, cfg) {
|
|
6545
8129
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
6546
8130
|
const row = cfg.providers.find((p) => p.id === id);
|
|
@@ -6554,7 +8138,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
6554
8138
|
try {
|
|
6555
8139
|
const headers = { Accept: "application/json" };
|
|
6556
8140
|
if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
|
|
6557
|
-
|
|
8141
|
+
Object.assign(headers, expandRowExtraHeaders(row));
|
|
8142
|
+
const response = await (0, import_upstreamFetch9.fetchUpstream)(url, { method: "GET", headers }, { providerId: "byo" });
|
|
6558
8143
|
if (!response.ok) {
|
|
6559
8144
|
const text = await response.text().catch(() => "");
|
|
6560
8145
|
let message = text.slice(0, 300);
|
|
@@ -6571,8 +8156,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
6571
8156
|
const data = await response.json();
|
|
6572
8157
|
const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
|
|
6573
8158
|
return writeJson4(res, 200, { models });
|
|
6574
|
-
} catch (
|
|
6575
|
-
const message =
|
|
8159
|
+
} catch (err8) {
|
|
8160
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
6576
8161
|
return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
|
|
6577
8162
|
}
|
|
6578
8163
|
}
|
|
@@ -6611,9 +8196,10 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
6611
8196
|
messages: [{ role: "user", content: prompt }]
|
|
6612
8197
|
};
|
|
6613
8198
|
}
|
|
8199
|
+
Object.assign(headers, expandRowExtraHeaders(row));
|
|
6614
8200
|
const startedAt = Date.now();
|
|
6615
8201
|
try {
|
|
6616
|
-
const response = await (0,
|
|
8202
|
+
const response = await (0, import_upstreamFetch9.fetchUpstream)(
|
|
6617
8203
|
url,
|
|
6618
8204
|
{ method: "POST", headers, body: JSON.stringify(payload) },
|
|
6619
8205
|
{ providerId: "byo" }
|
|
@@ -6635,8 +8221,8 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
6635
8221
|
latencyMs,
|
|
6636
8222
|
sample: extractSampleText(text, row.apiFormat)
|
|
6637
8223
|
});
|
|
6638
|
-
} catch (
|
|
6639
|
-
const message =
|
|
8224
|
+
} catch (err8) {
|
|
8225
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
6640
8226
|
return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
|
|
6641
8227
|
}
|
|
6642
8228
|
}
|
|
@@ -6678,7 +8264,30 @@ async function handleProviderKeys(res, id, cfg, deps) {
|
|
|
6678
8264
|
const row = cfg.providers.find((p) => p.id === id);
|
|
6679
8265
|
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
6680
8266
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
6681
|
-
|
|
8267
|
+
const views = toPoolKeyView(row, cooldown, deps);
|
|
8268
|
+
if (deps.providerKeyQuota) {
|
|
8269
|
+
const quotas = await Promise.allSettled(
|
|
8270
|
+
views.map((view) => deps.providerKeyQuota.quotaFor(row, view.id))
|
|
8271
|
+
);
|
|
8272
|
+
views.forEach((view, index) => {
|
|
8273
|
+
const settled = quotas[index];
|
|
8274
|
+
if (settled.status === "fulfilled" && settled.value) view.quota = settled.value;
|
|
8275
|
+
});
|
|
8276
|
+
}
|
|
8277
|
+
return writeJson4(res, 200, { keys: views });
|
|
8278
|
+
}
|
|
8279
|
+
async function handleProviderKeyQuotaRefresh(res, id, keyId, cfg, deps) {
|
|
8280
|
+
if (!deps.providerKeyQuota) return writeJsonError(res, 501, "provider key quota is not available");
|
|
8281
|
+
if (!id || !keyId) return writeJsonError(res, 400, "provider id and key id required in path");
|
|
8282
|
+
const row = cfg.providers.find((p) => p.id === id);
|
|
8283
|
+
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
8284
|
+
try {
|
|
8285
|
+
const quota = await deps.providerKeyQuota.quotaFor(row, keyId, { force: true });
|
|
8286
|
+
if (!quota) return writeJsonError(res, 404, `no quota endpoint for key '${keyId}'`);
|
|
8287
|
+
return writeJson4(res, 200, { quota });
|
|
8288
|
+
} catch {
|
|
8289
|
+
return writeJsonError(res, 502, "quota refresh failed");
|
|
8290
|
+
}
|
|
6682
8291
|
}
|
|
6683
8292
|
function parsePoolKeyInput(body, existing) {
|
|
6684
8293
|
const out = {};
|
|
@@ -6895,6 +8504,7 @@ function parseProviderInput(body, existing) {
|
|
|
6895
8504
|
const apiVersion = typeof body["apiVersion"] === "string" && body["apiVersion"].length > 0 ? body["apiVersion"] : body["apiVersion"] === null ? void 0 : existing?.apiVersion;
|
|
6896
8505
|
const modelsEndpoint = typeof body["modelsEndpoint"] === "string" && body["modelsEndpoint"].length > 0 ? body["modelsEndpoint"] : body["modelsEndpoint"] === null ? void 0 : existing?.modelsEndpoint;
|
|
6897
8506
|
const maxConcurrency = typeof body["maxConcurrency"] === "number" && Number.isFinite(body["maxConcurrency"]) ? body["maxConcurrency"] : body["maxConcurrency"] === null ? void 0 : existing?.maxConcurrency;
|
|
8507
|
+
const extraHeaders = body["extraHeaders"] === null ? void 0 : body["extraHeaders"] === void 0 ? existing?.extraHeaders : validateExtraHeaders(body["extraHeaders"]);
|
|
6898
8508
|
const transformer = body["transformer"] === null ? void 0 : parseTransformerInput(body["transformer"], existing?.transformer);
|
|
6899
8509
|
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;
|
|
6900
8510
|
const apiModes = body["apiModes"] === null ? void 0 : parseApiModesInput(body["apiModes"], existing?.apiModes);
|
|
@@ -6920,6 +8530,7 @@ function parseProviderInput(body, existing) {
|
|
|
6920
8530
|
apiVersion,
|
|
6921
8531
|
maxConcurrency,
|
|
6922
8532
|
modelsEndpoint,
|
|
8533
|
+
extraHeaders,
|
|
6923
8534
|
transformer: migrated.transformer,
|
|
6924
8535
|
codingPlan,
|
|
6925
8536
|
apiModes,
|
|
@@ -6941,7 +8552,10 @@ function handlePresets(res, method) {
|
|
|
6941
8552
|
description: p.description,
|
|
6942
8553
|
features: p.features,
|
|
6943
8554
|
website: p.website,
|
|
6944
|
-
modelsEndpoint: p.modelsEndpoint
|
|
8555
|
+
modelsEndpoint: p.modelsEndpoint,
|
|
8556
|
+
// Static extra headers ride along so `addFromPreset` can seed them onto the
|
|
8557
|
+
// row (the write gateway re-validates via the shared allowlist).
|
|
8558
|
+
extraHeaders: p.extraHeaders
|
|
6945
8559
|
}));
|
|
6946
8560
|
return writeJson4(res, 200, { presets, excluded });
|
|
6947
8561
|
}
|
|
@@ -7423,12 +9037,12 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
7423
9037
|
}
|
|
7424
9038
|
return writeJson4(res, 200, { ok: true, affected: result.affected });
|
|
7425
9039
|
}
|
|
7426
|
-
if (method === "GET" && rest[0] === "codex" && rest[1] === "oauth" && rest[3] === "status") {
|
|
7427
|
-
const result = handleCodexOAuthStatus(rest[2], deps);
|
|
9040
|
+
if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[3] === "status") {
|
|
9041
|
+
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);
|
|
7428
9042
|
return writeJson4(res, result.status, result.body);
|
|
7429
9043
|
}
|
|
7430
|
-
if (method === "DELETE" && rest[0] === "codex" && rest[1] === "oauth" && rest[2]) {
|
|
7431
|
-
const result = handleCodexOAuthCancel(rest[2], deps);
|
|
9044
|
+
if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[2]) {
|
|
9045
|
+
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);
|
|
7432
9046
|
return writeJson4(res, result.status, result.body);
|
|
7433
9047
|
}
|
|
7434
9048
|
if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
|
|
@@ -7481,7 +9095,24 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
7481
9095
|
return writeJsonError(res, 400, `unknown subscription provider '${rest[0] ?? ""}'`);
|
|
7482
9096
|
}
|
|
7483
9097
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "start") {
|
|
7484
|
-
|
|
9098
|
+
if (providerId === "codex") {
|
|
9099
|
+
const result2 = handleCodexOAuthStart(deps);
|
|
9100
|
+
return writeJson4(res, result2.status, result2.body);
|
|
9101
|
+
}
|
|
9102
|
+
if (providerId === "kimi") {
|
|
9103
|
+
const result2 = await handleKimiOAuthStart(deps);
|
|
9104
|
+
return writeJson4(res, result2.status, result2.body);
|
|
9105
|
+
}
|
|
9106
|
+
if (providerId === "grok") {
|
|
9107
|
+
const result2 = await handleGrokOAuthStart(deps);
|
|
9108
|
+
return writeJson4(res, result2.status, result2.body);
|
|
9109
|
+
}
|
|
9110
|
+
if (providerId === "copilot") {
|
|
9111
|
+
const body2 = await readJsonBody4(req);
|
|
9112
|
+
const result2 = await handleCopilotOAuthStart(deps, body2["enterpriseUrl"]);
|
|
9113
|
+
return writeJson4(res, result2.status, result2.body);
|
|
9114
|
+
}
|
|
9115
|
+
const result = handleOAuthStart(providerId, deps);
|
|
7485
9116
|
return writeJson4(res, result.status, result.body);
|
|
7486
9117
|
}
|
|
7487
9118
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "complete") {
|
|
@@ -7975,12 +9606,12 @@ async function handlePlayground(req, res, method, deps) {
|
|
|
7975
9606
|
const payload = body["body"];
|
|
7976
9607
|
const status = deps.outboundApiServer.getStatus();
|
|
7977
9608
|
if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
|
|
7978
|
-
const path2 = resolvePlaygroundPath(endpoint,
|
|
9609
|
+
const path2 = resolvePlaygroundPath(endpoint, isRecord6(payload) ? payload : {});
|
|
7979
9610
|
if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
|
|
7980
9611
|
const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
|
|
7981
9612
|
await proxyToOutbound(res, status.port, path2, key, upstreamBody);
|
|
7982
9613
|
}
|
|
7983
|
-
function
|
|
9614
|
+
function isRecord6(v) {
|
|
7984
9615
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
7985
9616
|
}
|
|
7986
9617
|
function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
@@ -8009,8 +9640,8 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
|
8009
9640
|
});
|
|
8010
9641
|
}
|
|
8011
9642
|
);
|
|
8012
|
-
upstream.on("error", (
|
|
8013
|
-
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${
|
|
9643
|
+
upstream.on("error", (err8) => {
|
|
9644
|
+
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err8.message}`);
|
|
8014
9645
|
else res.end();
|
|
8015
9646
|
resolve11();
|
|
8016
9647
|
});
|
|
@@ -8116,7 +9747,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
8116
9747
|
}
|
|
8117
9748
|
|
|
8118
9749
|
// src/admin/version.ts
|
|
8119
|
-
var DAEMON_VERSION = true ? "0.
|
|
9750
|
+
var DAEMON_VERSION = true ? "0.4.0" : "0.0.0-dev";
|
|
8120
9751
|
|
|
8121
9752
|
// src/admin/AdminServer.ts
|
|
8122
9753
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -8159,13 +9790,13 @@ var AdminServer = class {
|
|
|
8159
9790
|
const server = import_node_http2.default.createServer((req, res) => {
|
|
8160
9791
|
this.onRequest(req, res);
|
|
8161
9792
|
});
|
|
8162
|
-
const onError = (
|
|
8163
|
-
if (
|
|
9793
|
+
const onError = (err8) => {
|
|
9794
|
+
if (err8.code === "EADDRINUSE" && port !== 0) {
|
|
8164
9795
|
server.removeListener("error", onError);
|
|
8165
9796
|
this.listen(bindAddr, 0).then(resolve11, reject);
|
|
8166
9797
|
return;
|
|
8167
9798
|
}
|
|
8168
|
-
reject(
|
|
9799
|
+
reject(err8);
|
|
8169
9800
|
};
|
|
8170
9801
|
server.on("error", onError);
|
|
8171
9802
|
server.listen(port, bindAddr, () => {
|
|
@@ -8183,8 +9814,8 @@ var AdminServer = class {
|
|
|
8183
9814
|
}
|
|
8184
9815
|
/** Per-request handler: auth gate (when a token is set) → routing. */
|
|
8185
9816
|
onRequest(req, res) {
|
|
8186
|
-
void this.dispatch(req, res).catch((
|
|
8187
|
-
const message =
|
|
9817
|
+
void this.dispatch(req, res).catch((err8) => {
|
|
9818
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
8188
9819
|
this.deps.logger.error("[AdminServer] unhandled error:", message);
|
|
8189
9820
|
if (!res.headersSent) {
|
|
8190
9821
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
@@ -8448,18 +10079,18 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
8448
10079
|
return;
|
|
8449
10080
|
}
|
|
8450
10081
|
signal?.addEventListener("abort", abort, { once: true });
|
|
8451
|
-
server.on("error", (
|
|
10082
|
+
server.on("error", (err8) => {
|
|
8452
10083
|
if (settled) return;
|
|
8453
10084
|
settled = true;
|
|
8454
10085
|
clearTimeout(timer);
|
|
8455
|
-
if (
|
|
10086
|
+
if (err8.code === "EADDRINUSE") {
|
|
8456
10087
|
reject(
|
|
8457
10088
|
new Error(
|
|
8458
10089
|
`login: cannot bind ${LOOPBACK_HOST}:${LOOPBACK_PORT} (address in use) \u2014 another codex login or process is holding the port`
|
|
8459
10090
|
)
|
|
8460
10091
|
);
|
|
8461
10092
|
} else {
|
|
8462
|
-
reject(
|
|
10093
|
+
reject(err8);
|
|
8463
10094
|
}
|
|
8464
10095
|
});
|
|
8465
10096
|
const timer = setTimeout(() => {
|
|
@@ -8489,55 +10120,498 @@ var AutoDisableStore = class {
|
|
|
8489
10120
|
clear() {
|
|
8490
10121
|
this.records.clear();
|
|
8491
10122
|
}
|
|
8492
|
-
};
|
|
8493
|
-
|
|
8494
|
-
// src/pool/loadPoolKeys.ts
|
|
8495
|
-
var secretBox2 = null;
|
|
8496
|
-
function setSecretBox2(box) {
|
|
8497
|
-
secretBox2 = box;
|
|
10123
|
+
};
|
|
10124
|
+
|
|
10125
|
+
// src/pool/loadPoolKeys.ts
|
|
10126
|
+
var secretBox2 = null;
|
|
10127
|
+
function setSecretBox2(box) {
|
|
10128
|
+
secretBox2 = box;
|
|
10129
|
+
}
|
|
10130
|
+
function readKeyValue(rawKey) {
|
|
10131
|
+
return secretBox2 ? secretBox2.decryptMaybe(rawKey) : rawKey;
|
|
10132
|
+
}
|
|
10133
|
+
function normalizeEntry(providerId, entry, sortOrder, autoDisabled) {
|
|
10134
|
+
const enabledInConfig = entry.enabled !== false;
|
|
10135
|
+
const enabled = enabledInConfig && !autoDisabled.isDisabled(entry.id);
|
|
10136
|
+
return {
|
|
10137
|
+
id: entry.id,
|
|
10138
|
+
providerId,
|
|
10139
|
+
label: entry.label && entry.label.length > 0 ? entry.label : entry.id,
|
|
10140
|
+
apiKey: readKeyValue(entry.apiKey),
|
|
10141
|
+
enabled,
|
|
10142
|
+
weight: typeof entry.weight === "number" && Number.isFinite(entry.weight) ? entry.weight : 1,
|
|
10143
|
+
sortOrder
|
|
10144
|
+
};
|
|
10145
|
+
}
|
|
10146
|
+
function createPoolKeysLoader(getProviderRow, autoDisabled) {
|
|
10147
|
+
return async (providerId) => {
|
|
10148
|
+
const row = getProviderRow(providerId);
|
|
10149
|
+
if (!row) return [];
|
|
10150
|
+
const pool = (row.apiKeys ?? []).filter((k) => k.apiKey.length > 0);
|
|
10151
|
+
if (pool.length > 0) {
|
|
10152
|
+
return pool.map((entry, i) => normalizeEntry(providerId, entry, i, autoDisabled));
|
|
10153
|
+
}
|
|
10154
|
+
if (row.apiKey.length > 0) {
|
|
10155
|
+
return [
|
|
10156
|
+
normalizeEntry(
|
|
10157
|
+
providerId,
|
|
10158
|
+
{ id: `${providerId}:default`, apiKey: row.apiKey, weight: 1, enabled: true },
|
|
10159
|
+
0,
|
|
10160
|
+
autoDisabled
|
|
10161
|
+
)
|
|
10162
|
+
];
|
|
10163
|
+
}
|
|
10164
|
+
return [];
|
|
10165
|
+
};
|
|
10166
|
+
}
|
|
10167
|
+
|
|
10168
|
+
// src/allowance/ProviderKeyQuotaService.ts
|
|
10169
|
+
var import_core4 = require("@omnicross/core");
|
|
10170
|
+
var import_upstreamFetch10 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
10171
|
+
|
|
10172
|
+
// src/allowance/ProviderKeyQuota.ts
|
|
10173
|
+
var MINUTE_MS3 = 6e4;
|
|
10174
|
+
var HOUR_MS2 = 60 * MINUTE_MS3;
|
|
10175
|
+
var DAY_MS3 = 24 * HOUR_MS2;
|
|
10176
|
+
var WEEK_MS = 7 * DAY_MS3;
|
|
10177
|
+
var MONTH_MS = 30 * DAY_MS3;
|
|
10178
|
+
function finiteNumber5(value) {
|
|
10179
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
10180
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
10181
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
10182
|
+
}
|
|
10183
|
+
function finitePercent4(value) {
|
|
10184
|
+
const parsed = finiteNumber5(value);
|
|
10185
|
+
return parsed !== void 0 && parsed <= 100 ? parsed : null;
|
|
10186
|
+
}
|
|
10187
|
+
function isoInstant3(value) {
|
|
10188
|
+
if (typeof value === "string" && value.trim()) {
|
|
10189
|
+
const time = Date.parse(value);
|
|
10190
|
+
if (Number.isFinite(time)) return new Date(time).toISOString();
|
|
10191
|
+
}
|
|
10192
|
+
const numeric = finiteNumber5(value);
|
|
10193
|
+
if (numeric !== void 0 && numeric > 1e9) {
|
|
10194
|
+
const ms = numeric > 1e12 ? numeric : numeric * 1e3;
|
|
10195
|
+
return new Date(ms).toISOString();
|
|
10196
|
+
}
|
|
10197
|
+
return void 0;
|
|
10198
|
+
}
|
|
10199
|
+
function secondsUntil7(instant, now) {
|
|
10200
|
+
if (!instant) return void 0;
|
|
10201
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
10202
|
+
}
|
|
10203
|
+
function isRecord7(value) {
|
|
10204
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
10205
|
+
}
|
|
10206
|
+
function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
10207
|
+
if (!baseUrl) return null;
|
|
10208
|
+
let url;
|
|
10209
|
+
try {
|
|
10210
|
+
url = new URL(baseUrl);
|
|
10211
|
+
} catch {
|
|
10212
|
+
return null;
|
|
10213
|
+
}
|
|
10214
|
+
const host = url.hostname.toLowerCase();
|
|
10215
|
+
const path2 = url.pathname.toLowerCase();
|
|
10216
|
+
if ((host === "api.z.ai" || host === "open.bigmodel.cn") && path2.includes("/coding")) {
|
|
10217
|
+
return "zai";
|
|
10218
|
+
}
|
|
10219
|
+
if ((host === "api.minimax.io" || host === "api.minimaxi.com") && // Token Plan rides the plain openai `/v1` (chat completions) surface; the
|
|
10220
|
+
// anthropic `/anthropic` rows are excluded (their usage impl is unverified).
|
|
10221
|
+
(path2 === "/v1" || path2 === "/v1/" || path2 === "" || path2 === "/")) {
|
|
10222
|
+
return "minimax-token-plan";
|
|
10223
|
+
}
|
|
10224
|
+
if (host === "api.code.umans.ai") return "umans";
|
|
10225
|
+
if (host === "api.synthetic.new") return "synthetic";
|
|
10226
|
+
if (host === "api.cline.bot") return "cline-pass";
|
|
10227
|
+
return null;
|
|
10228
|
+
}
|
|
10229
|
+
function providerKeyQuotaUrl(adapter, baseUrl) {
|
|
10230
|
+
const origin = new URL(baseUrl).origin;
|
|
10231
|
+
if (adapter === "zai") return `${origin}/api/monitor/usage/quota/limit`;
|
|
10232
|
+
if (adapter === "minimax-token-plan") return `${origin}/v1/token_plan/remains`;
|
|
10233
|
+
if (adapter === "umans") return `${origin}/v1/usage`;
|
|
10234
|
+
if (adapter === "cline-pass") return `${origin}/api/v1/users/me/plan/usage-limits`;
|
|
10235
|
+
return `${origin}/v2/quotas`;
|
|
10236
|
+
}
|
|
10237
|
+
function providerKeyQuotaAuthHeader(adapter, key) {
|
|
10238
|
+
return adapter === "zai" ? key : `Bearer ${key}`;
|
|
10239
|
+
}
|
|
10240
|
+
function zaiWindowDurationMs(item) {
|
|
10241
|
+
const count = item.number !== void 0 && item.number > 0 ? item.number : 1;
|
|
10242
|
+
switch (item.unit) {
|
|
10243
|
+
case 3:
|
|
10244
|
+
return count * HOUR_MS2;
|
|
10245
|
+
case 4:
|
|
10246
|
+
return count * DAY_MS3;
|
|
10247
|
+
case 5:
|
|
10248
|
+
return count * MONTH_MS;
|
|
10249
|
+
case 6:
|
|
10250
|
+
return WEEK_MS;
|
|
10251
|
+
default:
|
|
10252
|
+
return void 0;
|
|
10253
|
+
}
|
|
10254
|
+
}
|
|
10255
|
+
function zaiWindowIdLabel(durationMs) {
|
|
10256
|
+
if (durationMs === WEEK_MS) return { id: "seven-day", label: "7 days" };
|
|
10257
|
+
if (durationMs === 5 * HOUR_MS2) return { id: "five-hour", label: "5 hours" };
|
|
10258
|
+
if (durationMs === MONTH_MS) return { id: "thirty-day", label: "30 days" };
|
|
10259
|
+
if (durationMs !== void 0 && durationMs % DAY_MS3 === 0) {
|
|
10260
|
+
const days = durationMs / DAY_MS3;
|
|
10261
|
+
return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}` };
|
|
10262
|
+
}
|
|
10263
|
+
if (durationMs !== void 0 && durationMs % HOUR_MS2 === 0) {
|
|
10264
|
+
const hours = durationMs / HOUR_MS2;
|
|
10265
|
+
return { id: `${hours}h`, label: `${hours} hour${hours === 1 ? "" : "s"}` };
|
|
10266
|
+
}
|
|
10267
|
+
return { id: "quota", label: "Quota" };
|
|
10268
|
+
}
|
|
10269
|
+
function parseZaiQuotaPayload(payload, now) {
|
|
10270
|
+
if (!isRecord7(payload)) return null;
|
|
10271
|
+
const data = isRecord7(payload["data"]) ? payload["data"] : payload;
|
|
10272
|
+
if (payload["success"] === false) return null;
|
|
10273
|
+
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
10274
|
+
const byWindow = /* @__PURE__ */ new Map();
|
|
10275
|
+
for (const raw of limits) {
|
|
10276
|
+
if (!isRecord7(raw)) continue;
|
|
10277
|
+
const item = raw;
|
|
10278
|
+
if (item.type === void 0) continue;
|
|
10279
|
+
const details = raw["usageDetails"];
|
|
10280
|
+
if (Array.isArray(details) && details.some((d) => isRecord7(d) && d["modelCode"] === "zread")) {
|
|
10281
|
+
continue;
|
|
10282
|
+
}
|
|
10283
|
+
const durationMs = zaiWindowDurationMs(item);
|
|
10284
|
+
const { id, label } = zaiWindowIdLabel(durationMs);
|
|
10285
|
+
const limit = finiteNumber5(item.usage);
|
|
10286
|
+
const used = finiteNumber5(item.currentValue);
|
|
10287
|
+
const fromAbsolute = limit !== void 0 && used !== void 0 && limit > 0 ? Math.min(100, used / limit * 100) : void 0;
|
|
10288
|
+
const fromPercentage = finitePercent4(item.percentage) ?? void 0;
|
|
10289
|
+
const usedPercent = fromAbsolute !== void 0 ? Math.round(fromAbsolute * 10) / 10 : fromPercentage;
|
|
10290
|
+
if (usedPercent === void 0) continue;
|
|
10291
|
+
const resetsAt = isoInstant3(item.nextResetTime);
|
|
10292
|
+
const candidate = {
|
|
10293
|
+
id,
|
|
10294
|
+
label,
|
|
10295
|
+
scope: "all",
|
|
10296
|
+
usedPercent,
|
|
10297
|
+
...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs / MINUTE_MS3) } : {},
|
|
10298
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
10299
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
10300
|
+
state: "fresh"
|
|
10301
|
+
};
|
|
10302
|
+
const existing = byWindow.get(id);
|
|
10303
|
+
if (!existing || (candidate.usedPercent ?? 0) > (existing.usedPercent ?? 0)) {
|
|
10304
|
+
byWindow.set(id, candidate);
|
|
10305
|
+
}
|
|
10306
|
+
}
|
|
10307
|
+
const windows = [...byWindow.values()].sort((a, b) => (a.windowMinutes ?? Number.POSITIVE_INFINITY) - (b.windowMinutes ?? Number.POSITIVE_INFINITY));
|
|
10308
|
+
return windows.length > 0 ? windows.slice(0, 4) : null;
|
|
10309
|
+
}
|
|
10310
|
+
var MINIMAX_STATUS_EXHAUSTED = 2;
|
|
10311
|
+
var MINIMAX_SHARED_BUCKET = "general";
|
|
10312
|
+
function parseMiniMaxBucket(value) {
|
|
10313
|
+
if (!isRecord7(value)) return null;
|
|
10314
|
+
const modelName = typeof value["model_name"] === "string" ? value["model_name"].trim() : "";
|
|
10315
|
+
if (!modelName) return null;
|
|
10316
|
+
const instant = (v) => {
|
|
10317
|
+
const n = finiteNumber5(v);
|
|
10318
|
+
return n !== void 0 && n > 1e9 ? n > 1e12 ? n : n * 1e3 : void 0;
|
|
10319
|
+
};
|
|
10320
|
+
return {
|
|
10321
|
+
modelName,
|
|
10322
|
+
intervalEnd: instant(value["end_time"]),
|
|
10323
|
+
intervalRemainingPercent: finiteNumber5(value["current_interval_remaining_percent"]),
|
|
10324
|
+
intervalStatus: finiteNumber5(value["current_interval_status"]),
|
|
10325
|
+
weeklyEnd: instant(value["weekly_end_time"]),
|
|
10326
|
+
weeklyRemainingPercent: finiteNumber5(value["current_weekly_remaining_percent"]),
|
|
10327
|
+
weeklyStatus: finiteNumber5(value["current_weekly_status"])
|
|
10328
|
+
};
|
|
10329
|
+
}
|
|
10330
|
+
function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, status, now) {
|
|
10331
|
+
const usedPercent = status === MINIMAX_STATUS_EXHAUSTED ? 100 : remainingPercent !== void 0 ? Math.round((100 - remainingPercent) * 10) / 10 : null;
|
|
10332
|
+
const resetsAt = resetsAtMs !== void 0 ? new Date(resetsAtMs).toISOString() : void 0;
|
|
10333
|
+
return {
|
|
10334
|
+
id,
|
|
10335
|
+
label,
|
|
10336
|
+
scope: "all",
|
|
10337
|
+
usedPercent,
|
|
10338
|
+
...windowMinutes !== void 0 ? { windowMinutes } : {},
|
|
10339
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
10340
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
10341
|
+
state: usedPercent !== null ? "fresh" : "unavailable"
|
|
10342
|
+
};
|
|
10343
|
+
}
|
|
10344
|
+
function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
10345
|
+
if (!isRecord7(payload)) return null;
|
|
10346
|
+
const baseResp = payload["base_resp"];
|
|
10347
|
+
if (!isRecord7(baseResp) || baseResp["status_code"] !== 0) return null;
|
|
10348
|
+
const buckets = Array.isArray(payload["model_remains"]) ? payload["model_remains"] : [];
|
|
10349
|
+
let general = null;
|
|
10350
|
+
for (const raw of buckets) {
|
|
10351
|
+
const bucket = parseMiniMaxBucket(raw);
|
|
10352
|
+
if (bucket?.modelName === MINIMAX_SHARED_BUCKET) {
|
|
10353
|
+
general = bucket;
|
|
10354
|
+
break;
|
|
10355
|
+
}
|
|
10356
|
+
}
|
|
10357
|
+
if (!general) return null;
|
|
10358
|
+
return [
|
|
10359
|
+
minimaxWindow(
|
|
10360
|
+
"five-hour",
|
|
10361
|
+
"5 hours",
|
|
10362
|
+
5 * 60,
|
|
10363
|
+
general.intervalEnd,
|
|
10364
|
+
general.intervalRemainingPercent,
|
|
10365
|
+
general.intervalStatus,
|
|
10366
|
+
now
|
|
10367
|
+
),
|
|
10368
|
+
minimaxWindow(
|
|
10369
|
+
"seven-day",
|
|
10370
|
+
"7 days",
|
|
10371
|
+
Math.round(WEEK_MS / MINUTE_MS3),
|
|
10372
|
+
general.weeklyEnd,
|
|
10373
|
+
general.weeklyRemainingPercent,
|
|
10374
|
+
general.weeklyStatus,
|
|
10375
|
+
now
|
|
10376
|
+
)
|
|
10377
|
+
];
|
|
10378
|
+
}
|
|
10379
|
+
function parseUmansUsagePayload(payload, now) {
|
|
10380
|
+
if (!isRecord7(payload)) return null;
|
|
10381
|
+
const limits = isRecord7(payload["limits"]) ? payload["limits"] : void 0;
|
|
10382
|
+
const requests = limits && isRecord7(limits["requests"]) ? limits["requests"] : void 0;
|
|
10383
|
+
const usage = isRecord7(payload["usage"]) ? payload["usage"] : void 0;
|
|
10384
|
+
const window = isRecord7(payload["window"]) ? payload["window"] : void 0;
|
|
10385
|
+
const hardCap = finiteNumber5(requests?.["hard_cap"]);
|
|
10386
|
+
const softLimit = finiteNumber5(requests?.["limit"]);
|
|
10387
|
+
const requestsInWindow = finiteNumber5(usage?.["requests_in_window"]);
|
|
10388
|
+
const weightedInWindow = finiteNumber5(usage?.["weighted_in_window"]);
|
|
10389
|
+
const resetsAt = isoInstant3(window?.["resets_at"]);
|
|
10390
|
+
let usedPercent = null;
|
|
10391
|
+
if (hardCap !== void 0 && hardCap > 0 && requestsInWindow !== void 0) {
|
|
10392
|
+
usedPercent = Math.round(Math.min(100, requestsInWindow / hardCap * 100) * 10) / 10;
|
|
10393
|
+
} else if (softLimit !== void 0 && softLimit > 0 && weightedInWindow !== void 0) {
|
|
10394
|
+
usedPercent = Math.round(Math.min(100, weightedInWindow / softLimit * 100) * 10) / 10;
|
|
10395
|
+
}
|
|
10396
|
+
if (usedPercent === null && resetsAt === void 0) return null;
|
|
10397
|
+
return [
|
|
10398
|
+
{
|
|
10399
|
+
id: "five-hour",
|
|
10400
|
+
label: "5 hours",
|
|
10401
|
+
scope: "all",
|
|
10402
|
+
usedPercent,
|
|
10403
|
+
windowMinutes: 5 * 60,
|
|
10404
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
10405
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
10406
|
+
state: "fresh"
|
|
10407
|
+
}
|
|
10408
|
+
];
|
|
10409
|
+
}
|
|
10410
|
+
function parseSyntheticQuotasPayload(payload, now) {
|
|
10411
|
+
if (!isRecord7(payload)) return null;
|
|
10412
|
+
const fiveHour = isRecord7(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
|
|
10413
|
+
const weekly = isRecord7(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
|
|
10414
|
+
const windows = [];
|
|
10415
|
+
if (fiveHour) {
|
|
10416
|
+
const max = finiteNumber5(fiveHour["max"]);
|
|
10417
|
+
const remaining = finiteNumber5(fiveHour["remaining"]);
|
|
10418
|
+
const usedPercent = max !== void 0 && max > 0 && remaining !== void 0 ? Math.round(Math.min(100, (max - remaining) / max * 100) * 10) / 10 : null;
|
|
10419
|
+
const resetsAt = isoInstant3(fiveHour["nextTickAt"]);
|
|
10420
|
+
windows.push({
|
|
10421
|
+
id: "five-hour",
|
|
10422
|
+
label: "5 hours",
|
|
10423
|
+
scope: "all",
|
|
10424
|
+
usedPercent,
|
|
10425
|
+
windowMinutes: 5 * 60,
|
|
10426
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
10427
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
10428
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
10429
|
+
});
|
|
10430
|
+
}
|
|
10431
|
+
if (weekly) {
|
|
10432
|
+
const percentRemaining = finiteNumber5(weekly["percentRemaining"]);
|
|
10433
|
+
const usedPercent = percentRemaining !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - percentRemaining)) * 10) / 10 : null;
|
|
10434
|
+
const resetsAt = isoInstant3(weekly["nextRegenAt"]);
|
|
10435
|
+
windows.push({
|
|
10436
|
+
id: "seven-day",
|
|
10437
|
+
label: "7 days",
|
|
10438
|
+
scope: "all",
|
|
10439
|
+
usedPercent,
|
|
10440
|
+
windowMinutes: 7 * 24 * 60,
|
|
10441
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
10442
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
10443
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
10444
|
+
});
|
|
10445
|
+
}
|
|
10446
|
+
return windows.length > 0 ? windows : null;
|
|
8498
10447
|
}
|
|
8499
|
-
|
|
8500
|
-
|
|
10448
|
+
var CLINE_WINDOW_CONFIG = {
|
|
10449
|
+
five_hour: { id: "five-hour", label: "5 hours", minutes: 5 * 60 },
|
|
10450
|
+
weekly: { id: "seven-day", label: "7 days", minutes: 7 * 24 * 60 },
|
|
10451
|
+
monthly: { id: "thirty-day", label: "30 days", minutes: 30 * 24 * 60 }
|
|
10452
|
+
};
|
|
10453
|
+
function parseClinePassUsageLimitsPayload(payload, now) {
|
|
10454
|
+
if (!isRecord7(payload)) return null;
|
|
10455
|
+
const data = isRecord7(payload["data"]) ? payload["data"] : payload;
|
|
10456
|
+
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
10457
|
+
const windows = [];
|
|
10458
|
+
for (const raw of limits) {
|
|
10459
|
+
if (!isRecord7(raw)) continue;
|
|
10460
|
+
const config = CLINE_WINDOW_CONFIG[typeof raw["type"] === "string" ? raw["type"] : ""];
|
|
10461
|
+
if (!config) continue;
|
|
10462
|
+
const usedPercent = finitePercent4(raw["percentUsed"]);
|
|
10463
|
+
if (usedPercent === null) continue;
|
|
10464
|
+
const resetsAt = isoInstant3(raw["resetsAt"]);
|
|
10465
|
+
windows.push({
|
|
10466
|
+
id: config.id,
|
|
10467
|
+
label: config.label,
|
|
10468
|
+
scope: "all",
|
|
10469
|
+
usedPercent,
|
|
10470
|
+
windowMinutes: config.minutes,
|
|
10471
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
10472
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
10473
|
+
state: "fresh"
|
|
10474
|
+
});
|
|
10475
|
+
}
|
|
10476
|
+
return windows.length > 0 ? windows : null;
|
|
8501
10477
|
}
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
10478
|
+
|
|
10479
|
+
// src/allowance/ProviderKeyQuotaService.ts
|
|
10480
|
+
function parseQuotaPayload(adapter, payload, now) {
|
|
10481
|
+
switch (adapter) {
|
|
10482
|
+
case "zai":
|
|
10483
|
+
return parseZaiQuotaPayload(payload, now);
|
|
10484
|
+
case "minimax-token-plan":
|
|
10485
|
+
return parseMiniMaxTokenPlanPayload(payload, now);
|
|
10486
|
+
case "umans":
|
|
10487
|
+
return parseUmansUsagePayload(payload, now);
|
|
10488
|
+
case "synthetic":
|
|
10489
|
+
return parseSyntheticQuotasPayload(payload, now);
|
|
10490
|
+
case "cline-pass":
|
|
10491
|
+
return parseClinePassUsageLimitsPayload(payload, now);
|
|
10492
|
+
}
|
|
10493
|
+
}
|
|
10494
|
+
var PROVIDER_KEY_QUOTA_CACHE_MS = 5 * 6e4;
|
|
10495
|
+
function resolvedBaseUrl(row) {
|
|
10496
|
+
const modes = row.apiModes ?? [];
|
|
10497
|
+
const selected = row.selectedApiModeId ? modes.find((mode) => mode.id === row.selectedApiModeId) : void 0;
|
|
10498
|
+
const fallback = modes[0];
|
|
10499
|
+
const modeBase = selected?.baseUrl ?? fallback?.baseUrl;
|
|
10500
|
+
return modeBase ?? row.codingPlan?.baseUrl ?? row.baseUrl;
|
|
10501
|
+
}
|
|
10502
|
+
function rowKeyEntries(row) {
|
|
10503
|
+
const pool = (row.apiKeys ?? []).filter((entry) => entry.apiKey.length > 0);
|
|
10504
|
+
if (pool.length > 0) return pool.map((entry) => ({ id: entry.id, apiKey: entry.apiKey }));
|
|
10505
|
+
if (row.apiKey.length > 0) {
|
|
10506
|
+
return [{ id: `${row.id}:default`, apiKey: row.apiKey }];
|
|
10507
|
+
}
|
|
10508
|
+
return [];
|
|
8514
10509
|
}
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
10510
|
+
var ProviderKeyQuotaService = class {
|
|
10511
|
+
constructor(box, fetchImpl = (url, init) => (0, import_upstreamFetch10.fetchUpstream)(url, init, { redactBodies: true }), now = Date.now) {
|
|
10512
|
+
this.box = box;
|
|
10513
|
+
this.fetchImpl = fetchImpl;
|
|
10514
|
+
this.now = now;
|
|
10515
|
+
}
|
|
10516
|
+
box;
|
|
10517
|
+
fetchImpl;
|
|
10518
|
+
now;
|
|
10519
|
+
cache = /* @__PURE__ */ new Map();
|
|
10520
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
10521
|
+
/**
|
|
10522
|
+
* Quota for one key of a provider row, or `null` when the row has no quota
|
|
10523
|
+
* adapter / no such key. Cache-first; concurrent reads share one flight.
|
|
10524
|
+
*/
|
|
10525
|
+
async quotaFor(row, keyId, options = {}) {
|
|
10526
|
+
const adapter = detectProviderKeyQuotaAdapter(resolvedBaseUrl(row));
|
|
10527
|
+
if (!adapter) return null;
|
|
10528
|
+
const entry = rowKeyEntries(row).find((candidate) => candidate.id === keyId);
|
|
10529
|
+
if (!entry) return null;
|
|
10530
|
+
const cacheKey = `${row.id}\0${keyId}`;
|
|
10531
|
+
const now = this.now();
|
|
10532
|
+
const cached = this.cache.get(cacheKey);
|
|
10533
|
+
if (!options.force && cached && Date.parse(cached.expiresAt) > now) return cached;
|
|
10534
|
+
const running = this.inFlight.get(cacheKey);
|
|
10535
|
+
if (running) return running;
|
|
10536
|
+
const promise = this.fetchQuota(adapter, row, entry.apiKey, cacheKey).catch((error) => {
|
|
10537
|
+
void error;
|
|
10538
|
+
const previous = this.cache.get(cacheKey);
|
|
10539
|
+
if (previous) {
|
|
10540
|
+
const degraded = {
|
|
10541
|
+
...previous,
|
|
10542
|
+
expiresAt: new Date(now + PROVIDER_KEY_QUOTA_CACHE_MS).toISOString(),
|
|
10543
|
+
windows: previous.windows.map((window) => ({
|
|
10544
|
+
...window,
|
|
10545
|
+
state: window.usedPercent !== null || window.resetsAt ? "stale" : window.state
|
|
10546
|
+
})),
|
|
10547
|
+
errorCode: "quota_request_failed"
|
|
10548
|
+
};
|
|
10549
|
+
this.cache.set(cacheKey, degraded);
|
|
10550
|
+
return degraded;
|
|
10551
|
+
}
|
|
10552
|
+
return null;
|
|
10553
|
+
}).finally(() => this.inFlight.delete(cacheKey));
|
|
10554
|
+
this.inFlight.set(cacheKey, promise);
|
|
10555
|
+
return promise;
|
|
10556
|
+
}
|
|
10557
|
+
/** Drop cached rows for a provider (key added/removed/rotated). */
|
|
10558
|
+
invalidateProvider(providerRowId) {
|
|
10559
|
+
for (const key of this.cache.keys()) {
|
|
10560
|
+
if (key.startsWith(`${providerRowId}\0`)) this.cache.delete(key);
|
|
8522
10561
|
}
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
10562
|
+
}
|
|
10563
|
+
async fetchQuota(adapter, row, rawKey, cacheKey) {
|
|
10564
|
+
const baseUrl = resolvedBaseUrl(row);
|
|
10565
|
+
const url = providerKeyQuotaUrl(adapter, baseUrl);
|
|
10566
|
+
const key = this.box.decryptMaybe(rawKey);
|
|
10567
|
+
const now = this.now();
|
|
10568
|
+
const response = await this.fetchImpl(url, {
|
|
10569
|
+
method: "GET",
|
|
10570
|
+
headers: {
|
|
10571
|
+
Authorization: providerKeyQuotaAuthHeader(adapter, key),
|
|
10572
|
+
Accept: "application/json",
|
|
10573
|
+
"Content-Type": "application/json",
|
|
10574
|
+
// The row's static identity headers ride along — the Cline usage
|
|
10575
|
+
// endpoint sits behind the SAME client-identity 403 gate as inference.
|
|
10576
|
+
...(0, import_core4.mergeExtraHeaders)({}, row.extraHeaders)
|
|
10577
|
+
},
|
|
10578
|
+
signal: AbortSignal.timeout(15e3)
|
|
10579
|
+
});
|
|
10580
|
+
if (response.status === 401 || response.status === 403) {
|
|
10581
|
+
const snapshot2 = {
|
|
10582
|
+
adapter,
|
|
10583
|
+
observedAt: new Date(now).toISOString(),
|
|
10584
|
+
expiresAt: new Date(now + PROVIDER_KEY_QUOTA_CACHE_MS).toISOString(),
|
|
10585
|
+
windows: [],
|
|
10586
|
+
errorCode: "quota_unauthorized"
|
|
10587
|
+
};
|
|
10588
|
+
this.cache.set(cacheKey, snapshot2);
|
|
10589
|
+
return snapshot2;
|
|
8532
10590
|
}
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
10591
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
10592
|
+
let payload;
|
|
10593
|
+
try {
|
|
10594
|
+
payload = await response.json();
|
|
10595
|
+
} catch {
|
|
10596
|
+
throw new Error("invalid JSON");
|
|
10597
|
+
}
|
|
10598
|
+
const windows = parseQuotaPayload(adapter, payload, now);
|
|
10599
|
+
const snapshot = {
|
|
10600
|
+
adapter,
|
|
10601
|
+
observedAt: new Date(now).toISOString(),
|
|
10602
|
+
expiresAt: new Date(now + PROVIDER_KEY_QUOTA_CACHE_MS).toISOString(),
|
|
10603
|
+
windows: windows ?? [],
|
|
10604
|
+
...windows ? {} : { errorCode: "quota_unavailable" }
|
|
10605
|
+
};
|
|
10606
|
+
this.cache.set(cacheKey, snapshot);
|
|
10607
|
+
return snapshot;
|
|
10608
|
+
}
|
|
10609
|
+
};
|
|
8536
10610
|
|
|
8537
10611
|
// src/image-generation/ImageDoctorService.ts
|
|
8538
10612
|
var import_image_generation = require("@omnicross/core/image-generation");
|
|
8539
10613
|
var import_outbound_api7 = require("@omnicross/core/outbound-api");
|
|
8540
|
-
var
|
|
10614
|
+
var import_subscriptions9 = require("@omnicross/subscriptions");
|
|
8541
10615
|
|
|
8542
10616
|
// src/image-generation/FileCodexImageCapabilityEvidenceSource.ts
|
|
8543
10617
|
var import_node_crypto13 = require("crypto");
|
|
@@ -8975,7 +11049,7 @@ function createImageDoctorService(options) {
|
|
|
8975
11049
|
paths,
|
|
8976
11050
|
ttlMs: config.evidenceTtlMs
|
|
8977
11051
|
}));
|
|
8978
|
-
const createLiveVerifier = options.createLiveVerifier ?? ((strategy, config) => (0,
|
|
11052
|
+
const createLiveVerifier = options.createLiveVerifier ?? ((strategy, config) => (0, import_subscriptions9.createCodexImageLiveVerifier)({
|
|
8979
11053
|
authStrategy: strategy,
|
|
8980
11054
|
generationTimeoutMs: config.queue.generationTimeoutMs
|
|
8981
11055
|
}));
|
|
@@ -9337,7 +11411,7 @@ var ImageCleanupService = class {
|
|
|
9337
11411
|
var import_node_crypto16 = require("crypto");
|
|
9338
11412
|
var import_image_generation5 = require("@omnicross/core/image-generation");
|
|
9339
11413
|
var import_outbound_api8 = require("@omnicross/core/outbound-api");
|
|
9340
|
-
var
|
|
11414
|
+
var import_subscriptions10 = require("@omnicross/subscriptions");
|
|
9341
11415
|
|
|
9342
11416
|
// src/image-generation/ImageApiRuntimeResolver.ts
|
|
9343
11417
|
var import_node_crypto14 = require("crypto");
|
|
@@ -9868,7 +11942,7 @@ function createImageRuntimeGeneration(options) {
|
|
|
9868
11942
|
now: options.now ?? Date.now,
|
|
9869
11943
|
referenceStore: options.storage.referenceStore,
|
|
9870
11944
|
stateStore: options.storage.stateStore
|
|
9871
|
-
}) : (0,
|
|
11945
|
+
}) : (0, import_subscriptions10.createCodexSubscriptionImageProvider)({
|
|
9872
11946
|
authStrategy,
|
|
9873
11947
|
evidenceSource: generationEvidenceSource,
|
|
9874
11948
|
executionScheduler: scheduler,
|
|
@@ -13012,7 +15086,7 @@ var ImageRuntimeManager = class {
|
|
|
13012
15086
|
};
|
|
13013
15087
|
|
|
13014
15088
|
// src/ports/ConfigFileProviderConfigSource.ts
|
|
13015
|
-
var
|
|
15089
|
+
var import_core5 = require("@omnicross/core");
|
|
13016
15090
|
var EMPTY_CHAIN = {
|
|
13017
15091
|
providerTransformers: [],
|
|
13018
15092
|
modelTransformers: []
|
|
@@ -13037,8 +15111,8 @@ var ConfigFileProviderConfigSource = class {
|
|
|
13037
15111
|
reloadHook;
|
|
13038
15112
|
constructor(config) {
|
|
13039
15113
|
for (const p of config.providers) this.providers.set(p.id, p);
|
|
13040
|
-
this.transformerService = new
|
|
13041
|
-
void (0,
|
|
15114
|
+
this.transformerService = new import_core5.TransformerService();
|
|
15115
|
+
void (0, import_core5.registerBuiltinTransformers)(this.transformerService);
|
|
13042
15116
|
}
|
|
13043
15117
|
// ── Reload hook (key-pool design D4) ───────────────────────────────────────
|
|
13044
15118
|
/**
|
|
@@ -13059,7 +15133,7 @@ var ConfigFileProviderConfigSource = class {
|
|
|
13059
15133
|
}
|
|
13060
15134
|
/** Await the built-in transformer registration (tests await this before dispatch). */
|
|
13061
15135
|
async ready() {
|
|
13062
|
-
await (0,
|
|
15136
|
+
await (0, import_core5.registerBuiltinTransformers)(this.transformerService);
|
|
13063
15137
|
}
|
|
13064
15138
|
// ── Hot-reload seam (admin dashboard, RT3 design D6) ───────────────────────
|
|
13065
15139
|
/**
|
|
@@ -13170,6 +15244,10 @@ function toLLMProvider(row) {
|
|
|
13170
15244
|
// `parseProviderInput`), so customizations are preserved (the row value wins).
|
|
13171
15245
|
apiModes: row.apiModes,
|
|
13172
15246
|
selectedApiModeId: row.selectedApiModeId,
|
|
15247
|
+
// Static extra request headers ride along verbatim (load-guarded — no
|
|
15248
|
+
// auth/content names); core's `getProviderHeaders` merges them into every
|
|
15249
|
+
// BYO request, and the same-format relay path inherits that funnel.
|
|
15250
|
+
extraHeaders: row.extraHeaders,
|
|
13173
15251
|
// Official-Anthropic signature handling only matters for the Anthropic
|
|
13174
15252
|
// ingress (deferred → 502); leave it off for the BYO transform path.
|
|
13175
15253
|
isOfficial: false
|
|
@@ -14534,10 +16612,13 @@ function bucketLabel(bucketStartTs, bucket) {
|
|
|
14534
16612
|
}
|
|
14535
16613
|
|
|
14536
16614
|
// src/ports/JsonOutboundKeyDb.ts
|
|
16615
|
+
var import_node_fs23 = require("fs");
|
|
16616
|
+
var import_core6 = require("@omnicross/core");
|
|
16617
|
+
|
|
16618
|
+
// src/ports/atomicFile.ts
|
|
14537
16619
|
var import_node_crypto22 = require("crypto");
|
|
14538
16620
|
var import_node_fs22 = require("fs");
|
|
14539
16621
|
var import_node_path25 = require("path");
|
|
14540
|
-
var import_core3 = require("@omnicross/core");
|
|
14541
16622
|
function atomicReplaceUtf8(targetPath, contents) {
|
|
14542
16623
|
const tempPath = (0, import_node_path25.join)(
|
|
14543
16624
|
(0, import_node_path25.dirname)(targetPath),
|
|
@@ -14567,6 +16648,8 @@ function atomicReplaceUtf8(targetPath, contents) {
|
|
|
14567
16648
|
throw error;
|
|
14568
16649
|
}
|
|
14569
16650
|
}
|
|
16651
|
+
|
|
16652
|
+
// src/ports/JsonOutboundKeyDb.ts
|
|
14570
16653
|
var JsonOutboundKeyDb = class {
|
|
14571
16654
|
/**
|
|
14572
16655
|
* @param secretBox OPTIONAL reversible-secret codec. When present, a created
|
|
@@ -14652,7 +16735,7 @@ var JsonOutboundKeyDb = class {
|
|
|
14652
16735
|
});
|
|
14653
16736
|
}
|
|
14654
16737
|
async outboundApiKeysSetPermissions(id, permissions) {
|
|
14655
|
-
const exact = (0,
|
|
16738
|
+
const exact = (0, import_core6.validateOutboundPermissions)(permissions);
|
|
14656
16739
|
return this.mutateRow(id, (row) => {
|
|
14657
16740
|
if (row.revokedAt !== null) return false;
|
|
14658
16741
|
row.allowedEndpoints = [...exact];
|
|
@@ -14709,9 +16792,9 @@ var JsonOutboundKeyDb = class {
|
|
|
14709
16792
|
}
|
|
14710
16793
|
/** Read the key rows, tolerating a missing/corrupt file (→ empty list). */
|
|
14711
16794
|
readRows() {
|
|
14712
|
-
if (!(0,
|
|
16795
|
+
if (!(0, import_node_fs23.existsSync)(this.keysPath)) return [];
|
|
14713
16796
|
try {
|
|
14714
|
-
const parsed = JSON.parse((0,
|
|
16797
|
+
const parsed = JSON.parse((0, import_node_fs23.readFileSync)(this.keysPath, "utf8"));
|
|
14715
16798
|
return Array.isArray(parsed) ? parsed : [];
|
|
14716
16799
|
} catch {
|
|
14717
16800
|
return [];
|
|
@@ -14728,7 +16811,7 @@ function applyPolicyField(row, field, value) {
|
|
|
14728
16811
|
}
|
|
14729
16812
|
|
|
14730
16813
|
// src/ports/JsonPricingStore.ts
|
|
14731
|
-
var
|
|
16814
|
+
var import_node_fs24 = require("fs");
|
|
14732
16815
|
var import_node_crypto23 = require("crypto");
|
|
14733
16816
|
var JsonPricingStore = class {
|
|
14734
16817
|
constructor(pricingPath) {
|
|
@@ -14743,9 +16826,9 @@ var JsonPricingStore = class {
|
|
|
14743
16826
|
* otherwise unusable pricing table after a crash or manual file edit.
|
|
14744
16827
|
*/
|
|
14745
16828
|
hasUsableSnapshot() {
|
|
14746
|
-
if (!(0,
|
|
16829
|
+
if (!(0, import_node_fs24.existsSync)(this.pricingPath)) return false;
|
|
14747
16830
|
try {
|
|
14748
|
-
const parsed = JSON.parse((0,
|
|
16831
|
+
const parsed = JSON.parse((0, import_node_fs24.readFileSync)(this.pricingPath, "utf8"));
|
|
14749
16832
|
return Array.isArray(parsed) && parsed.some(isUsablePricingRow);
|
|
14750
16833
|
} catch {
|
|
14751
16834
|
return false;
|
|
@@ -14858,9 +16941,9 @@ var JsonPricingStore = class {
|
|
|
14858
16941
|
}
|
|
14859
16942
|
/** Read the pricing rows, tolerating a missing/corrupt file (→ empty list). */
|
|
14860
16943
|
readRows() {
|
|
14861
|
-
if (!(0,
|
|
16944
|
+
if (!(0, import_node_fs24.existsSync)(this.pricingPath)) return [];
|
|
14862
16945
|
try {
|
|
14863
|
-
const parsed = JSON.parse((0,
|
|
16946
|
+
const parsed = JSON.parse((0, import_node_fs24.readFileSync)(this.pricingPath, "utf8"));
|
|
14864
16947
|
return Array.isArray(parsed) ? parsed : [];
|
|
14865
16948
|
} catch {
|
|
14866
16949
|
return [];
|
|
@@ -14869,18 +16952,18 @@ var JsonPricingStore = class {
|
|
|
14869
16952
|
writeRows(rows) {
|
|
14870
16953
|
const temporaryPath = `${this.pricingPath}.${process.pid}.${(0, import_node_crypto23.randomUUID)()}.tmp`;
|
|
14871
16954
|
try {
|
|
14872
|
-
(0,
|
|
16955
|
+
(0, import_node_fs24.writeFileSync)(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
|
|
14873
16956
|
encoding: "utf8",
|
|
14874
16957
|
flag: "wx"
|
|
14875
16958
|
});
|
|
14876
16959
|
this.replaceFile(temporaryPath);
|
|
14877
16960
|
} finally {
|
|
14878
|
-
(0,
|
|
16961
|
+
(0, import_node_fs24.rmSync)(temporaryPath, { force: true });
|
|
14879
16962
|
}
|
|
14880
16963
|
}
|
|
14881
16964
|
/** Isolated for deterministic failure testing; never removes the target. */
|
|
14882
16965
|
replaceFile(temporaryPath) {
|
|
14883
|
-
(0,
|
|
16966
|
+
(0, import_node_fs24.renameSync)(temporaryPath, this.pricingPath);
|
|
14884
16967
|
}
|
|
14885
16968
|
};
|
|
14886
16969
|
function isUsablePricingRow(value) {
|
|
@@ -14890,7 +16973,7 @@ function isUsablePricingRow(value) {
|
|
|
14890
16973
|
}
|
|
14891
16974
|
|
|
14892
16975
|
// src/pricing/PricingRefreshScheduler.ts
|
|
14893
|
-
var
|
|
16976
|
+
var import_node_fs25 = require("fs");
|
|
14894
16977
|
var EMPTY_STATE2 = {
|
|
14895
16978
|
lastAttemptAt: null,
|
|
14896
16979
|
lastSuccessAt: null,
|
|
@@ -14928,9 +17011,9 @@ var PricingRefreshScheduler = class {
|
|
|
14928
17011
|
this.timer = null;
|
|
14929
17012
|
}
|
|
14930
17013
|
getState() {
|
|
14931
|
-
if (!(0,
|
|
17014
|
+
if (!(0, import_node_fs25.existsSync)(this.statePath)) return { ...EMPTY_STATE2, sources: [] };
|
|
14932
17015
|
try {
|
|
14933
|
-
const value = JSON.parse((0,
|
|
17016
|
+
const value = JSON.parse((0, import_node_fs25.readFileSync)(this.statePath, "utf8"));
|
|
14934
17017
|
return {
|
|
14935
17018
|
lastAttemptAt: finiteOrNull(value.lastAttemptAt),
|
|
14936
17019
|
lastSuccessAt: finiteOrNull(value.lastSuccessAt),
|
|
@@ -14983,9 +17066,9 @@ var PricingRefreshScheduler = class {
|
|
|
14983
17066
|
}
|
|
14984
17067
|
writeState(state) {
|
|
14985
17068
|
const temporaryPath = `${this.statePath}.tmp`;
|
|
14986
|
-
(0,
|
|
17069
|
+
(0, import_node_fs25.writeFileSync)(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
14987
17070
|
`, "utf8");
|
|
14988
|
-
(0,
|
|
17071
|
+
(0, import_node_fs25.renameSync)(temporaryPath, this.statePath);
|
|
14989
17072
|
}
|
|
14990
17073
|
};
|
|
14991
17074
|
function finiteOrNull(value) {
|
|
@@ -14993,7 +17076,7 @@ function finiteOrNull(value) {
|
|
|
14993
17076
|
}
|
|
14994
17077
|
|
|
14995
17078
|
// src/ports/JsonVoucherDb.ts
|
|
14996
|
-
var
|
|
17079
|
+
var import_node_fs26 = require("fs");
|
|
14997
17080
|
var JsonVoucherDb = class {
|
|
14998
17081
|
constructor(vouchersPath) {
|
|
14999
17082
|
this.vouchersPath = vouchersPath;
|
|
@@ -15071,27 +17154,27 @@ var JsonVoucherDb = class {
|
|
|
15071
17154
|
}
|
|
15072
17155
|
/** Read the voucher rows, tolerating a missing/corrupt file (→ empty list). */
|
|
15073
17156
|
readRows() {
|
|
15074
|
-
if (!(0,
|
|
17157
|
+
if (!(0, import_node_fs26.existsSync)(this.vouchersPath)) return [];
|
|
15075
17158
|
try {
|
|
15076
|
-
const parsed = JSON.parse((0,
|
|
17159
|
+
const parsed = JSON.parse((0, import_node_fs26.readFileSync)(this.vouchersPath, "utf8"));
|
|
15077
17160
|
return Array.isArray(parsed) ? parsed : [];
|
|
15078
17161
|
} catch {
|
|
15079
17162
|
return [];
|
|
15080
17163
|
}
|
|
15081
17164
|
}
|
|
15082
17165
|
writeRows(rows) {
|
|
15083
|
-
(0,
|
|
17166
|
+
(0, import_node_fs26.writeFileSync)(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
|
|
15084
17167
|
}
|
|
15085
17168
|
};
|
|
15086
17169
|
|
|
15087
17170
|
// src/ports/JsonSubscriptionCredentialStore.ts
|
|
15088
|
-
var
|
|
17171
|
+
var import_node_fs28 = require("fs");
|
|
15089
17172
|
var import_node_path27 = require("path");
|
|
15090
17173
|
var import_SubscriptionAccountHealth2 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
15091
17174
|
var import_AccountAllowanceScheduling3 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
15092
|
-
var
|
|
17175
|
+
var import_upstreamFetch11 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
15093
17176
|
var import_SubscriptionIdentityStore2 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
15094
|
-
var
|
|
17177
|
+
var import_subscriptions11 = require("@omnicross/subscriptions");
|
|
15095
17178
|
|
|
15096
17179
|
// src/ports/account-sync.ts
|
|
15097
17180
|
function viewOf(tokens) {
|
|
@@ -15135,7 +17218,7 @@ function findDuplicateCredentialIds(accounts) {
|
|
|
15135
17218
|
}
|
|
15136
17219
|
|
|
15137
17220
|
// src/ports/external-cli-credentials.ts
|
|
15138
|
-
var
|
|
17221
|
+
var import_node_fs27 = require("fs");
|
|
15139
17222
|
var import_node_os5 = require("os");
|
|
15140
17223
|
var import_node_path26 = require("path");
|
|
15141
17224
|
function externalStorePath(provider, home = (0, import_node_os5.homedir)()) {
|
|
@@ -15188,10 +17271,10 @@ function parseCodexTokensEnvelope(raw) {
|
|
|
15188
17271
|
}
|
|
15189
17272
|
function readExternalCliCredentials(provider, home = (0, import_node_os5.homedir)()) {
|
|
15190
17273
|
const path2 = externalStorePath(provider, home);
|
|
15191
|
-
if (!(0,
|
|
17274
|
+
if (!(0, import_node_fs27.existsSync)(path2)) return null;
|
|
15192
17275
|
let raw;
|
|
15193
17276
|
try {
|
|
15194
|
-
const parsed = JSON.parse((0,
|
|
17277
|
+
const parsed = JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8"));
|
|
15195
17278
|
raw = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
15196
17279
|
} catch {
|
|
15197
17280
|
return null;
|
|
@@ -15214,16 +17297,18 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15214
17297
|
* as on relay refresh egresses from the SAME proxy IP as the
|
|
15215
17298
|
* account's traffic. NOT used by any read/write path.
|
|
15216
17299
|
*/
|
|
15217
|
-
constructor(tokensPath, box, fetchImpl = void 0, externalCliReader = readExternalCliCredentials) {
|
|
17300
|
+
constructor(tokensPath, box, fetchImpl = void 0, externalCliReader = readExternalCliCredentials, atomicReplace = atomicReplaceUtf8) {
|
|
15218
17301
|
this.tokensPath = tokensPath;
|
|
15219
17302
|
this.box = box;
|
|
15220
17303
|
this.fetchImpl = fetchImpl;
|
|
15221
17304
|
this.externalCliReader = externalCliReader;
|
|
17305
|
+
this.atomicReplace = atomicReplace;
|
|
15222
17306
|
}
|
|
15223
17307
|
tokensPath;
|
|
15224
17308
|
box;
|
|
15225
17309
|
fetchImpl;
|
|
15226
17310
|
externalCliReader;
|
|
17311
|
+
atomicReplace;
|
|
15227
17312
|
/**
|
|
15228
17313
|
* The proxy-aware `FetchLike` for one refresh round-trip (upstream-proxy M1). A
|
|
15229
17314
|
* TEST-injected `fetchImpl` is returned verbatim; otherwise the refresh routes
|
|
@@ -15237,7 +17322,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15237
17322
|
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
15238
17323
|
*/
|
|
15239
17324
|
buildRefreshFetch(providerId, accountId) {
|
|
15240
|
-
return this.fetchImpl ?? ((url, init) => (0,
|
|
17325
|
+
return this.fetchImpl ?? ((url, init) => (0, import_upstreamFetch11.fetchUpstream)(url, init, { providerId, accountId, redactBodies: true }));
|
|
15241
17326
|
}
|
|
15242
17327
|
/**
|
|
15243
17328
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -15278,7 +17363,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15278
17363
|
* other hot reads. Never returns token material.
|
|
15279
17364
|
*/
|
|
15280
17365
|
getAccountProxy(providerId, accountId) {
|
|
15281
|
-
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego") {
|
|
17366
|
+
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi" && providerId !== "grok" && providerId !== "copilot") {
|
|
15282
17367
|
return void 0;
|
|
15283
17368
|
}
|
|
15284
17369
|
return getAccountProxy(this.readConfig(), providerId, accountId);
|
|
@@ -15297,7 +17382,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15297
17382
|
const fingerprintOn = identityStore.isEnabled();
|
|
15298
17383
|
const now = Date.now();
|
|
15299
17384
|
const out = {};
|
|
15300
|
-
for (const provider of ["claude", "codex", "gemini", "opencodego"]) {
|
|
17385
|
+
for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi", "grok", "copilot"]) {
|
|
15301
17386
|
const sanitized = sanitizeAccounts(config, provider);
|
|
15302
17387
|
if (sanitized.length === 0) continue;
|
|
15303
17388
|
for (const account of sanitized) {
|
|
@@ -15363,7 +17448,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15363
17448
|
this.materializeMigration(config);
|
|
15364
17449
|
const refreshFetch = this.buildRefreshFetch("claude", capturedId);
|
|
15365
17450
|
try {
|
|
15366
|
-
const result = await
|
|
17451
|
+
const result = await import_subscriptions11.claudeOAuth.refreshAccessToken(claude.refreshToken, refreshFetch);
|
|
15367
17452
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
15368
17453
|
const next = {
|
|
15369
17454
|
...claude,
|
|
@@ -15398,7 +17483,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15398
17483
|
this.materializeMigration(config);
|
|
15399
17484
|
const refreshFetch = this.buildRefreshFetch("codex", capturedId);
|
|
15400
17485
|
try {
|
|
15401
|
-
const result = await
|
|
17486
|
+
const result = await import_subscriptions11.codexOAuth.refreshAccessToken(codex.refreshToken, refreshFetch);
|
|
15402
17487
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
15403
17488
|
const next = {
|
|
15404
17489
|
...codex,
|
|
@@ -15436,7 +17521,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15436
17521
|
this.materializeMigration(config);
|
|
15437
17522
|
const refreshFetch = this.buildRefreshFetch("gemini", capturedId);
|
|
15438
17523
|
try {
|
|
15439
|
-
const result = await
|
|
17524
|
+
const result = await import_subscriptions11.geminiOAuth.refreshAccessToken(gemini.refreshToken, refreshFetch);
|
|
15440
17525
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
15441
17526
|
const next = {
|
|
15442
17527
|
...gemini,
|
|
@@ -15455,6 +17540,107 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15455
17540
|
}
|
|
15456
17541
|
});
|
|
15457
17542
|
}
|
|
17543
|
+
/**
|
|
17544
|
+
* Refresh the Kimi Code (Moonshot) OAuth access token (device-flow grant).
|
|
17545
|
+
* Kimi ROTATES the refresh token, so the response's pair is written back
|
|
17546
|
+
* whole; the account's stable `deviceId` (fingerprint header input) is
|
|
17547
|
+
* preserved. The refresh call carries the CLI fingerprint headers. HONEST
|
|
17548
|
+
* `false` when no refresh_token.
|
|
17549
|
+
*/
|
|
17550
|
+
async refreshKimiToken() {
|
|
17551
|
+
return this.coalesce("kimi:active", async () => {
|
|
17552
|
+
const config = this.readConfig();
|
|
17553
|
+
const active = getActiveAccount(config, "kimi");
|
|
17554
|
+
const kimi = active?.tokens;
|
|
17555
|
+
if (!active || !kimi?.refreshToken) return false;
|
|
17556
|
+
const capturedId = active.id;
|
|
17557
|
+
this.materializeMigration(config);
|
|
17558
|
+
const refreshFetch = this.buildRefreshFetch("kimi", capturedId);
|
|
17559
|
+
try {
|
|
17560
|
+
const result = await import_subscriptions11.kimiOAuth.refreshAccessToken(
|
|
17561
|
+
kimi.refreshToken,
|
|
17562
|
+
refreshFetch,
|
|
17563
|
+
import_subscriptions11.kimiOAuth.kimiFingerprintHeaders(kimi.deviceId)
|
|
17564
|
+
);
|
|
17565
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
17566
|
+
const next = {
|
|
17567
|
+
...kimi,
|
|
17568
|
+
accessToken: result.accessToken,
|
|
17569
|
+
refreshToken: result.refreshToken,
|
|
17570
|
+
expiresAt,
|
|
17571
|
+
status: "authorized",
|
|
17572
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17573
|
+
errorMessage: void 0,
|
|
17574
|
+
syncWarning: void 0
|
|
17575
|
+
};
|
|
17576
|
+
this.writeBackById("kimi", capturedId, next);
|
|
17577
|
+
return true;
|
|
17578
|
+
} catch (error) {
|
|
17579
|
+
this.markExpiredById("kimi", capturedId, kimi, error);
|
|
17580
|
+
return false;
|
|
17581
|
+
}
|
|
17582
|
+
});
|
|
17583
|
+
}
|
|
17584
|
+
/**
|
|
17585
|
+
* Refresh the Grok (xAI SuperGrok) OAuth access token. The token endpoint is
|
|
17586
|
+
* resolved through OIDC discovery on every refresh (process-cached 1h by the
|
|
17587
|
+
* flow module) so a rotated endpoint document is picked up without a daemon
|
|
17588
|
+
* restart. HONEST `false` when no refresh_token.
|
|
17589
|
+
*/
|
|
17590
|
+
async refreshGrokToken() {
|
|
17591
|
+
return this.coalesce("grok:active", async () => {
|
|
17592
|
+
const config = this.readConfig();
|
|
17593
|
+
const active = getActiveAccount(config, "grok");
|
|
17594
|
+
const grok = active?.tokens;
|
|
17595
|
+
if (!active || !grok?.refreshToken) return false;
|
|
17596
|
+
const capturedId = active.id;
|
|
17597
|
+
this.materializeMigration(config);
|
|
17598
|
+
const refreshFetch = this.buildRefreshFetch("grok", capturedId);
|
|
17599
|
+
try {
|
|
17600
|
+
const tokenEndpoint = await import_subscriptions11.grokOAuth.resolveGrokTokenEndpoint(refreshFetch);
|
|
17601
|
+
const result = await import_subscriptions11.grokOAuth.refreshGrokAccessToken(grok.refreshToken, tokenEndpoint, refreshFetch);
|
|
17602
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
17603
|
+
const next = {
|
|
17604
|
+
...grok,
|
|
17605
|
+
accessToken: result.accessToken,
|
|
17606
|
+
refreshToken: result.refreshToken,
|
|
17607
|
+
expiresAt,
|
|
17608
|
+
status: "authorized",
|
|
17609
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17610
|
+
errorMessage: void 0,
|
|
17611
|
+
syncWarning: void 0
|
|
17612
|
+
};
|
|
17613
|
+
this.writeBackById("grok", capturedId, next);
|
|
17614
|
+
return true;
|
|
17615
|
+
} catch (error) {
|
|
17616
|
+
this.markExpiredById("grok", capturedId, grok, error);
|
|
17617
|
+
return false;
|
|
17618
|
+
}
|
|
17619
|
+
});
|
|
17620
|
+
}
|
|
17621
|
+
/**
|
|
17622
|
+
* "Refresh" a GitHub Copilot token — there is nothing to refresh (ghu_
|
|
17623
|
+
* tokens are long-lived with no exchange endpoint). A call here means the
|
|
17624
|
+
* strategy saw a 401 (the token was revoked); mark the account `expired`
|
|
17625
|
+
* with a re-authenticate message and return `false` (the proxy then declines
|
|
17626
|
+
* the retry instead of looping on a dead token).
|
|
17627
|
+
*/
|
|
17628
|
+
async refreshCopilotToken() {
|
|
17629
|
+
return this.coalesce("copilot:active", async () => {
|
|
17630
|
+
const config = this.readConfig();
|
|
17631
|
+
const active = getActiveAccount(config, "copilot");
|
|
17632
|
+
const copilot = active?.tokens;
|
|
17633
|
+
if (!active || !copilot?.accessToken) return false;
|
|
17634
|
+
this.materializeMigration(config);
|
|
17635
|
+
this.markExpiredById(
|
|
17636
|
+
"copilot",
|
|
17637
|
+
active.id,
|
|
17638
|
+
copilot,
|
|
17639
|
+
new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account")
|
|
17640
|
+
);
|
|
17641
|
+
return false;
|
|
17642
|
+
});
|
|
17643
|
+
}
|
|
15458
17644
|
/**
|
|
15459
17645
|
* Refresh a SPECIFIC managed account by id (background scheduler sweep and
|
|
15460
17646
|
* account-pool resolution). It uses only that account's stored refresh
|
|
@@ -15507,7 +17693,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15507
17693
|
}
|
|
15508
17694
|
const oauth = account.tokens;
|
|
15509
17695
|
if (!oauth.accessToken) return null;
|
|
15510
|
-
if (providerId === "codex" || providerId === "gemini") {
|
|
17696
|
+
if (providerId === "codex" || providerId === "gemini" || providerId === "kimi" || providerId === "grok" || providerId === "copilot") {
|
|
15511
17697
|
const expiresAtMs = oauth.expiresAt ? Date.parse(oauth.expiresAt) : 0;
|
|
15512
17698
|
const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - ACCOUNT_REFRESH_LEAD_MS;
|
|
15513
17699
|
if (expiringSoon && oauth.refreshToken) {
|
|
@@ -15596,8 +17782,35 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15596
17782
|
}
|
|
15597
17783
|
/** Dispatch one OAuth refresh round-trip to the provider's shared flow. */
|
|
15598
17784
|
async refreshUpstream(provider, refreshToken, accountId) {
|
|
15599
|
-
const
|
|
15600
|
-
|
|
17785
|
+
const refreshFetch = this.buildRefreshFetch(provider, accountId);
|
|
17786
|
+
if (provider === "kimi") {
|
|
17787
|
+
const account = accountId ? getAccountById(this.readConfig(), "kimi", accountId) : void 0;
|
|
17788
|
+
const deviceId = account?.tokens?.deviceId;
|
|
17789
|
+
const r2 = await import_subscriptions11.kimiOAuth.refreshAccessToken(
|
|
17790
|
+
refreshToken,
|
|
17791
|
+
refreshFetch,
|
|
17792
|
+
import_subscriptions11.kimiOAuth.kimiFingerprintHeaders(deviceId)
|
|
17793
|
+
);
|
|
17794
|
+
return {
|
|
17795
|
+
accessToken: r2.accessToken,
|
|
17796
|
+
refreshToken: r2.refreshToken,
|
|
17797
|
+
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
17798
|
+
};
|
|
17799
|
+
}
|
|
17800
|
+
if (provider === "grok") {
|
|
17801
|
+
const tokenEndpoint = await import_subscriptions11.grokOAuth.resolveGrokTokenEndpoint(refreshFetch);
|
|
17802
|
+
const r2 = await import_subscriptions11.grokOAuth.refreshGrokAccessToken(refreshToken, tokenEndpoint, refreshFetch);
|
|
17803
|
+
return {
|
|
17804
|
+
accessToken: r2.accessToken,
|
|
17805
|
+
refreshToken: r2.refreshToken,
|
|
17806
|
+
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
17807
|
+
};
|
|
17808
|
+
}
|
|
17809
|
+
if (provider === "copilot") {
|
|
17810
|
+
throw new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account");
|
|
17811
|
+
}
|
|
17812
|
+
const flow = provider === "claude" ? import_subscriptions11.claudeOAuth : provider === "codex" ? import_subscriptions11.codexOAuth : import_subscriptions11.geminiOAuth;
|
|
17813
|
+
const r = await flow.refreshAccessToken(refreshToken, refreshFetch);
|
|
15601
17814
|
return {
|
|
15602
17815
|
accessToken: r.accessToken,
|
|
15603
17816
|
refreshToken: r.refreshToken,
|
|
@@ -15760,42 +17973,86 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15760
17973
|
/** Write the merged config to disk as pretty JSON (mkdir parent if needed).
|
|
15761
17974
|
* Encrypt-on-write: the token-material fields are encrypted (legacy plaintext
|
|
15762
17975
|
* `enc:v1:`; already-`enc:`/`$ENV` untouched) before serializing, so any
|
|
15763
|
-
* write incl. child 4's future refresh writes lands encrypted.
|
|
17976
|
+
* write incl. child 4's future refresh writes lands encrypted.
|
|
17977
|
+
* ATOMIC: temp + fsync + rename (`atomicReplaceUtf8`) — a failed or
|
|
17978
|
+
* interrupted write discards only the temp file; the prior `tokens.json`
|
|
17979
|
+
* survives byte-equal (bare `writeFileSync` truncate-writes lost every
|
|
17980
|
+
* account on a mid-write failure, 2026-09-06). */
|
|
15764
17981
|
persist(config) {
|
|
15765
|
-
(0,
|
|
17982
|
+
(0, import_node_fs28.mkdirSync)((0, import_node_path27.dirname)(this.tokensPath), { recursive: true });
|
|
15766
17983
|
const encrypted = encryptTokens(config, this.box);
|
|
15767
|
-
|
|
17984
|
+
this.atomicReplace(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n");
|
|
15768
17985
|
}
|
|
15769
17986
|
/**
|
|
15770
|
-
* Read + parse `tokens.json`,
|
|
15771
|
-
*
|
|
15772
|
-
* subscription bearer path is byte-identical).
|
|
17987
|
+
* Read + parse `tokens.json`, then DECRYPT the token-material fields so every
|
|
17988
|
+
* getter returns plaintext (the subscription bearer path is byte-identical).
|
|
15773
17989
|
*
|
|
15774
|
-
*
|
|
15775
|
-
* file
|
|
15776
|
-
*
|
|
15777
|
-
*
|
|
15778
|
-
*
|
|
15779
|
-
*
|
|
15780
|
-
* `
|
|
17990
|
+
* A MISSING file is a legitimate first-boot state → minimal `{ updatedAt: '' }`.
|
|
17991
|
+
* A file that EXISTS but cannot be parsed as a JSON object is CORRUPT →
|
|
17992
|
+
* `quarantineCorrupt` moves it aside (once) before the empty config is
|
|
17993
|
+
* returned, so the unreadable accounts survive for manual recovery.
|
|
17994
|
+
*
|
|
17995
|
+
* The DECRYPT runs OUTSIDE any try, so a wrong/missing master key or a
|
|
17996
|
+
* tampered `enc:` envelope FAILS FAST with the box's clear, secret-free
|
|
17997
|
+
* error (secrets spec "/ UX": SHALL fail-fast, SHALL NOT a swallowed
|
|
17998
|
+
* decrypt would report "no tokens" and silently send the WRONG bearer
|
|
17999
|
+
* upstream 401). Mirrors `config.ts loadConfig`, which decrypts outside
|
|
18000
|
+
* its parse try.
|
|
15781
18001
|
*/
|
|
15782
18002
|
readConfig() {
|
|
15783
|
-
if (!(0,
|
|
18003
|
+
if (!(0, import_node_fs28.existsSync)(this.tokensPath)) return { updatedAt: "" };
|
|
15784
18004
|
let parsed;
|
|
15785
18005
|
try {
|
|
15786
|
-
const raw = JSON.parse((0,
|
|
15787
|
-
|
|
18006
|
+
const raw = JSON.parse((0, import_node_fs28.readFileSync)(this.tokensPath, "utf8"));
|
|
18007
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
18008
|
+
return this.quarantineCorrupt("parsed JSON is not an object");
|
|
18009
|
+
}
|
|
18010
|
+
parsed = raw;
|
|
15788
18011
|
} catch {
|
|
15789
|
-
|
|
18012
|
+
return this.quarantineCorrupt("unparseable JSON");
|
|
15790
18013
|
}
|
|
15791
|
-
if (!parsed) return { updatedAt: "" };
|
|
15792
18014
|
const decrypted = decryptTokens(parsed, this.box);
|
|
15793
18015
|
return migrateLazily(decrypted);
|
|
15794
18016
|
}
|
|
18017
|
+
/** One-shot latch: a corrupt file is quarantined (or found unmovable) at
|
|
18018
|
+
* most once per process, so the hot read path never re-attempts or re-logs. */
|
|
18019
|
+
corruptQuarantined = false;
|
|
18020
|
+
/**
|
|
18021
|
+
* Quarantine a present-but-corrupt `tokens.json`, then treat it as empty.
|
|
18022
|
+
*
|
|
18023
|
+
* Renames the file to a sibling `tokens.json.corrupt-<stamp>` backup and
|
|
18024
|
+
* logs loudly (the daemon's stderr log; secret-free — reason + paths only).
|
|
18025
|
+
* The daemon KEEPS SERVING (API-key routing is unaffected; subscription
|
|
18026
|
+
* routing reports no credential, same as an absent file) while the corrupt
|
|
18027
|
+
* bytes survive for manual recovery — and, critically, the NEXT persist
|
|
18028
|
+
* (e.g. the user re-logging in) can no longer overwrite the only copy of
|
|
18029
|
+
* the old accounts, which is exactly how the 2026-09-06 incident turned a
|
|
18030
|
+
* recoverable truncated file into permanent account loss.
|
|
18031
|
+
*
|
|
18032
|
+
* Best-effort: if the rename fails (file locked, permissions), the corrupt
|
|
18033
|
+
* file is left in place and every later read still tolerates it as empty;
|
|
18034
|
+
* the latch still trips so the attempt + log happen exactly once.
|
|
18035
|
+
*/
|
|
18036
|
+
quarantineCorrupt(reason) {
|
|
18037
|
+
if (!this.corruptQuarantined) {
|
|
18038
|
+
this.corruptQuarantined = true;
|
|
18039
|
+
const backup = `${this.tokensPath}.corrupt-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
18040
|
+
let moved = false;
|
|
18041
|
+
try {
|
|
18042
|
+
(0, import_node_fs28.renameSync)(this.tokensPath, backup);
|
|
18043
|
+
moved = true;
|
|
18044
|
+
} catch {
|
|
18045
|
+
}
|
|
18046
|
+
console.error(
|
|
18047
|
+
`[JsonSubscriptionCredentialStore] tokens.json is corrupt (${reason}); ` + (moved ? `moved to '${backup}' and treated as empty \u2014 recover accounts from that backup before re-adding them` : `could not move '${this.tokensPath}' \u2014 treated as empty`)
|
|
18048
|
+
);
|
|
18049
|
+
}
|
|
18050
|
+
return { updatedAt: "" };
|
|
18051
|
+
}
|
|
15795
18052
|
};
|
|
15796
18053
|
|
|
15797
18054
|
// src/AccountHealthProbeScheduler.ts
|
|
15798
|
-
var
|
|
18055
|
+
var import_upstreamFetch12 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
15799
18056
|
|
|
15800
18057
|
// src/probe/CodexGenerationProbe.ts
|
|
15801
18058
|
var import_codexCliHeaders = require("@omnicross/core/provider-proxy/identity/codexCliHeaders");
|
|
@@ -15934,7 +18191,20 @@ var PROVIDER_PROBE_PLANS = {
|
|
|
15934
18191
|
// billable/wrong endpoint). Upgrade to `{ kind:'upstream' }` once verified.
|
|
15935
18192
|
codex: { kind: "local" },
|
|
15936
18193
|
gemini: { kind: "local" },
|
|
15937
|
-
opencodego: { kind: "local" }
|
|
18194
|
+
opencodego: { kind: "local" },
|
|
18195
|
+
// Kimi's `GET /coding/v1/usages` is a verified FREE authed GET (the allowance
|
|
18196
|
+
// collector uses it), but the probe path also needs the fingerprint headers —
|
|
18197
|
+
// keep the probe local until the collector covers the health surface.
|
|
18198
|
+
kimi: { kind: "local" },
|
|
18199
|
+
// Grok's billing proxy is a verified FREE authed GET (the allowance collector
|
|
18200
|
+
// uses it) but it REJECTS non-OAuth credentials and sits on a separate host
|
|
18201
|
+
// with its own product-gate header — keep the probe local, the collector
|
|
18202
|
+
// owns the health surface.
|
|
18203
|
+
grok: { kind: "local" },
|
|
18204
|
+
// The Copilot quota endpoint (copilot_internal/user) is a verified FREE
|
|
18205
|
+
// authed GET but lives on api.github.com with its own auth dialect and a
|
|
18206
|
+
// monthly-only window — the allowance collector owns the health surface.
|
|
18207
|
+
copilot: { kind: "local" }
|
|
15938
18208
|
};
|
|
15939
18209
|
function probePlanFor(providerId) {
|
|
15940
18210
|
return PROVIDER_PROBE_PLANS[providerId] ?? { kind: "local" };
|
|
@@ -15956,7 +18226,7 @@ var AccountHealthProbeScheduler = class {
|
|
|
15956
18226
|
this.logger = logger;
|
|
15957
18227
|
this.config = config;
|
|
15958
18228
|
this.now = opts.now ?? Date.now;
|
|
15959
|
-
this.fetchImpl = opts.fetchImpl ??
|
|
18229
|
+
this.fetchImpl = opts.fetchImpl ?? import_upstreamFetch12.fetchUpstream;
|
|
15960
18230
|
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
15961
18231
|
this.planFor = opts.planFor ?? probePlanFor;
|
|
15962
18232
|
}
|
|
@@ -16300,13 +18570,13 @@ var AccountHealthSweeper = class {
|
|
|
16300
18570
|
};
|
|
16301
18571
|
|
|
16302
18572
|
// src/audit/AuditPruneSweeper.ts
|
|
16303
|
-
var
|
|
18573
|
+
var import_node_fs30 = require("fs");
|
|
16304
18574
|
var import_node_path29 = require("path");
|
|
16305
18575
|
var import_promises6 = require("stream/promises");
|
|
16306
18576
|
var import_node_zlib2 = require("zlib");
|
|
16307
18577
|
|
|
16308
18578
|
// src/audit/auditStats.ts
|
|
16309
|
-
var
|
|
18579
|
+
var import_node_fs29 = require("fs");
|
|
16310
18580
|
var import_node_path28 = require("path");
|
|
16311
18581
|
var SIDECAR_VERSION = 1;
|
|
16312
18582
|
var META_PREFIX_BYTES = 64 * 1024;
|
|
@@ -16315,9 +18585,9 @@ function auditStatsFileName(auditFile) {
|
|
|
16315
18585
|
return auditFile.replace(/\.jsonl$/, ".stats.json");
|
|
16316
18586
|
}
|
|
16317
18587
|
function readPersisted(path2) {
|
|
16318
|
-
if (!(0,
|
|
18588
|
+
if (!(0, import_node_fs29.existsSync)(path2)) return null;
|
|
16319
18589
|
try {
|
|
16320
|
-
const value = JSON.parse((0,
|
|
18590
|
+
const value = JSON.parse((0, import_node_fs29.readFileSync)(path2, "utf8"));
|
|
16321
18591
|
if (value.version !== SIDECAR_VERSION || !Number.isSafeInteger(value.auditBytes) || (value.auditBytes ?? -1) < 0 || !Number.isSafeInteger(value.requestCount) || (value.requestCount ?? -1) < 0 || !Number.isSafeInteger(value.errorCount) || (value.errorCount ?? -1) < 0 || (value.errorCount ?? 0) > (value.requestCount ?? -1) || typeof value.complete !== "boolean" || value.minTs !== null && !Number.isFinite(value.minTs) || value.maxTs !== null && !Number.isFinite(value.maxTs)) {
|
|
16322
18592
|
return null;
|
|
16323
18593
|
}
|
|
@@ -16347,7 +18617,7 @@ function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfte
|
|
|
16347
18617
|
minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
|
|
16348
18618
|
maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
|
|
16349
18619
|
};
|
|
16350
|
-
(0,
|
|
18620
|
+
(0, import_node_fs29.writeFileSync)(statsPath, JSON.stringify(next), "utf8");
|
|
16351
18621
|
}
|
|
16352
18622
|
function queryCovers(stats, from, to) {
|
|
16353
18623
|
return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
|
|
@@ -16405,7 +18675,7 @@ async function scanAuditFile(auditPath, startByte, auditBytes, from, to) {
|
|
|
16405
18675
|
prefixTruncated = false;
|
|
16406
18676
|
};
|
|
16407
18677
|
if (auditBytes > startByte) {
|
|
16408
|
-
const stream = (0,
|
|
18678
|
+
const stream = (0, import_node_fs29.createReadStream)(auditPath, {
|
|
16409
18679
|
start: startByte,
|
|
16410
18680
|
end: auditBytes - 1,
|
|
16411
18681
|
highWaterMark: READ_CHUNK_BYTES2
|
|
@@ -16458,12 +18728,12 @@ function mergePersistedStats(previous, appended) {
|
|
|
16458
18728
|
};
|
|
16459
18729
|
}
|
|
16460
18730
|
async function readAuditStats(auditDir, query2 = {}) {
|
|
16461
|
-
if (!(0,
|
|
18731
|
+
if (!(0, import_node_fs29.existsSync)(auditDir)) return { requestCount: 0, errorCount: 0, complete: true };
|
|
16462
18732
|
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
16463
18733
|
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
16464
18734
|
let sources;
|
|
16465
18735
|
try {
|
|
16466
|
-
sources = (0,
|
|
18736
|
+
sources = (0, import_node_fs29.readdirSync)(auditDir).filter((name) => fileOverlaps(name, from, to)).sort().map(
|
|
16467
18737
|
(name) => AUDIT_DAY_DIR_RE.test(name) ? {
|
|
16468
18738
|
auditPath: (0, import_node_path28.join)(auditDir, name, AUDIT_META_FILE),
|
|
16469
18739
|
statsPath: (0, import_node_path28.join)(auditDir, name, auditStatsFileName(AUDIT_META_FILE))
|
|
@@ -16471,14 +18741,14 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
16471
18741
|
auditPath: (0, import_node_path28.join)(auditDir, name),
|
|
16472
18742
|
statsPath: (0, import_node_path28.join)(auditDir, auditStatsFileName(name))
|
|
16473
18743
|
}
|
|
16474
|
-
).filter((source) => (0,
|
|
18744
|
+
).filter((source) => (0, import_node_fs29.existsSync)(source.auditPath));
|
|
16475
18745
|
} catch {
|
|
16476
18746
|
return { requestCount: 0, errorCount: 0, complete: false };
|
|
16477
18747
|
}
|
|
16478
18748
|
const total = { requestCount: 0, errorCount: 0, complete: true };
|
|
16479
18749
|
for (const { auditPath, statsPath } of sources) {
|
|
16480
18750
|
try {
|
|
16481
|
-
const auditBytes = (0,
|
|
18751
|
+
const auditBytes = (0, import_node_fs29.statSync)(auditPath).size;
|
|
16482
18752
|
const persisted = readPersisted(statsPath);
|
|
16483
18753
|
if (persisted && persisted.complete && persisted.auditBytes === auditBytes && queryCovers(persisted, from, to)) {
|
|
16484
18754
|
total.requestCount += persisted.requestCount;
|
|
@@ -16497,7 +18767,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
16497
18767
|
total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
|
|
16498
18768
|
total.complete = total.complete && scanned.filtered.complete;
|
|
16499
18769
|
const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
|
|
16500
|
-
if (current.complete) (0,
|
|
18770
|
+
if (current.complete) (0, import_node_fs29.writeFileSync)(statsPath, JSON.stringify(current), "utf8");
|
|
16501
18771
|
} catch {
|
|
16502
18772
|
total.complete = false;
|
|
16503
18773
|
}
|
|
@@ -16506,7 +18776,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
16506
18776
|
}
|
|
16507
18777
|
|
|
16508
18778
|
// src/audit/AuditPruneSweeper.ts
|
|
16509
|
-
var
|
|
18779
|
+
var DAY_MS4 = 24 * 60 * 6e4;
|
|
16510
18780
|
var SWEEP_INTERVAL_MS2 = 60 * 6e4;
|
|
16511
18781
|
var ARCHIVE_BATCH = 64;
|
|
16512
18782
|
var AuditPruneSweeper = class {
|
|
@@ -16569,19 +18839,19 @@ var AuditPruneSweeper = class {
|
|
|
16569
18839
|
if (!this.config.enabled || this.sweeping) return 0;
|
|
16570
18840
|
this.sweeping = true;
|
|
16571
18841
|
try {
|
|
16572
|
-
if (!(0,
|
|
16573
|
-
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) *
|
|
18842
|
+
if (!(0, import_node_fs30.existsSync)(this.auditDir)) return 0;
|
|
18843
|
+
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS4;
|
|
16574
18844
|
let removed = 0;
|
|
16575
|
-
for (const name of (0,
|
|
18845
|
+
for (const name of (0, import_node_fs30.readdirSync)(this.auditDir)) {
|
|
16576
18846
|
const dateMs = auditFileDateMs(name);
|
|
16577
18847
|
if (dateMs === null || dateMs >= cutoff) continue;
|
|
16578
18848
|
try {
|
|
16579
18849
|
if (isAuditDayDir(name)) {
|
|
16580
|
-
(0,
|
|
18850
|
+
(0, import_node_fs30.rmSync)((0, import_node_path29.join)(this.auditDir, name), { recursive: true, force: true });
|
|
16581
18851
|
} else {
|
|
16582
|
-
(0,
|
|
18852
|
+
(0, import_node_fs30.unlinkSync)((0, import_node_path29.join)(this.auditDir, name));
|
|
16583
18853
|
const statsPath = (0, import_node_path29.join)(this.auditDir, auditStatsFileName(name));
|
|
16584
|
-
if ((0,
|
|
18854
|
+
if ((0, import_node_fs30.existsSync)(statsPath)) (0, import_node_fs30.unlinkSync)(statsPath);
|
|
16585
18855
|
}
|
|
16586
18856
|
removed += 1;
|
|
16587
18857
|
} catch (error) {
|
|
@@ -16611,10 +18881,10 @@ var AuditPruneSweeper = class {
|
|
|
16611
18881
|
if (!this.config.enabled || this.archiving) return 0;
|
|
16612
18882
|
this.archiving = true;
|
|
16613
18883
|
try {
|
|
16614
|
-
if (!(0,
|
|
18884
|
+
if (!(0, import_node_fs30.existsSync)(this.auditDir)) return 0;
|
|
16615
18885
|
const today = this.todayMidnight();
|
|
16616
18886
|
let compressed = 0;
|
|
16617
|
-
for (const name of (0,
|
|
18887
|
+
for (const name of (0, import_node_fs30.readdirSync)(this.auditDir)) {
|
|
16618
18888
|
if (compressed >= ARCHIVE_BATCH) break;
|
|
16619
18889
|
const dateMs = auditFileDateMs(name);
|
|
16620
18890
|
if (dateMs === null || dateMs >= today || !isAuditDayDir(name)) continue;
|
|
@@ -16655,7 +18925,7 @@ var AuditPruneSweeper = class {
|
|
|
16655
18925
|
async archiveDay(bodiesPath, budget) {
|
|
16656
18926
|
let shards;
|
|
16657
18927
|
try {
|
|
16658
|
-
shards = (0,
|
|
18928
|
+
shards = (0, import_node_fs30.readdirSync)(bodiesPath).filter((file) => file.endsWith(".jsonl"));
|
|
16659
18929
|
} catch {
|
|
16660
18930
|
return 0;
|
|
16661
18931
|
}
|
|
@@ -16665,16 +18935,16 @@ var AuditPruneSweeper = class {
|
|
|
16665
18935
|
const source = (0, import_node_path29.join)(bodiesPath, shard);
|
|
16666
18936
|
const target = `${source}.gz`;
|
|
16667
18937
|
try {
|
|
16668
|
-
if ((0,
|
|
16669
|
-
(0,
|
|
18938
|
+
if ((0, import_node_fs30.existsSync)(target)) {
|
|
18939
|
+
(0, import_node_fs30.unlinkSync)(source);
|
|
16670
18940
|
continue;
|
|
16671
18941
|
}
|
|
16672
|
-
await (0, import_promises6.pipeline)((0,
|
|
16673
|
-
(0,
|
|
18942
|
+
await (0, import_promises6.pipeline)((0, import_node_fs30.createReadStream)(source), (0, import_node_zlib2.createGzip)(), (0, import_node_fs30.createWriteStream)(target));
|
|
18943
|
+
(0, import_node_fs30.unlinkSync)(source);
|
|
16674
18944
|
compressed += 1;
|
|
16675
18945
|
} catch (error) {
|
|
16676
18946
|
try {
|
|
16677
|
-
if ((0,
|
|
18947
|
+
if ((0, import_node_fs30.existsSync)(target)) (0, import_node_fs30.unlinkSync)(target);
|
|
16678
18948
|
} catch {
|
|
16679
18949
|
}
|
|
16680
18950
|
this.logger.warn("[AuditPruneSweeper] failed to archive audit body shard", {
|
|
@@ -16688,7 +18958,7 @@ var AuditPruneSweeper = class {
|
|
|
16688
18958
|
};
|
|
16689
18959
|
|
|
16690
18960
|
// src/usage/usageMigrate.ts
|
|
16691
|
-
var
|
|
18961
|
+
var import_node_fs31 = require("fs");
|
|
16692
18962
|
var import_promises7 = require("fs/promises");
|
|
16693
18963
|
var import_node_path30 = require("path");
|
|
16694
18964
|
var import_node_readline = require("readline");
|
|
@@ -16735,7 +19005,7 @@ async function migrateLegacyUsageEvents(opts) {
|
|
|
16735
19005
|
let skipped = 0;
|
|
16736
19006
|
try {
|
|
16737
19007
|
const reader = (0, import_node_readline.createInterface)({
|
|
16738
|
-
input: (0,
|
|
19008
|
+
input: (0, import_node_fs31.createReadStream)(eventsPath, { encoding: "utf8" }),
|
|
16739
19009
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
16740
19010
|
});
|
|
16741
19011
|
for await (const line of reader) {
|
|
@@ -16827,7 +19097,7 @@ async function closeAll(writers) {
|
|
|
16827
19097
|
// src/usage/UsagePruneSweeper.ts
|
|
16828
19098
|
var import_promises8 = require("fs/promises");
|
|
16829
19099
|
var import_node_path31 = require("path");
|
|
16830
|
-
var
|
|
19100
|
+
var DAY_MS5 = 24 * 60 * 6e4;
|
|
16831
19101
|
var SWEEP_INTERVAL_MS3 = 60 * 6e4;
|
|
16832
19102
|
var DEFAULT_USAGE_RETENTION_DAYS = 90;
|
|
16833
19103
|
var UsagePruneSweeper = class {
|
|
@@ -16884,7 +19154,7 @@ var UsagePruneSweeper = class {
|
|
|
16884
19154
|
this.sweeping = true;
|
|
16885
19155
|
try {
|
|
16886
19156
|
const retentionDays = this.config.retentionDays ?? DEFAULT_USAGE_RETENTION_DAYS;
|
|
16887
|
-
const cutoff = this.todayMidnight() - (retentionDays - 1) *
|
|
19157
|
+
const cutoff = this.todayMidnight() - (retentionDays - 1) * DAY_MS5;
|
|
16888
19158
|
let removed = 0;
|
|
16889
19159
|
for (const entry of await listUsageDays(this.usageDir)) {
|
|
16890
19160
|
if (!entry.hasShard) continue;
|
|
@@ -16942,7 +19212,7 @@ var UsagePruneSweeper = class {
|
|
|
16942
19212
|
};
|
|
16943
19213
|
|
|
16944
19214
|
// src/audit/auditReader.ts
|
|
16945
|
-
var
|
|
19215
|
+
var import_node_fs32 = require("fs");
|
|
16946
19216
|
var import_node_path32 = require("path");
|
|
16947
19217
|
var DEFAULT_LIMIT = 200;
|
|
16948
19218
|
var MAX_LIMIT = 2e3;
|
|
@@ -16950,7 +19220,7 @@ var OVERSCAN = 256;
|
|
|
16950
19220
|
function daySources(auditDir) {
|
|
16951
19221
|
let names;
|
|
16952
19222
|
try {
|
|
16953
|
-
names = (0,
|
|
19223
|
+
names = (0, import_node_fs32.readdirSync)(auditDir);
|
|
16954
19224
|
} catch {
|
|
16955
19225
|
return [];
|
|
16956
19226
|
}
|
|
@@ -16960,7 +19230,7 @@ function daySources(auditDir) {
|
|
|
16960
19230
|
if (dateMs === null) continue;
|
|
16961
19231
|
if (AUDIT_DAY_DIR_RE.test(name)) {
|
|
16962
19232
|
const path2 = (0, import_node_path32.join)(auditDir, name, AUDIT_META_FILE);
|
|
16963
|
-
if ((0,
|
|
19233
|
+
if ((0, import_node_fs32.existsSync)(path2)) sources.push({ path: path2, dateMs });
|
|
16964
19234
|
} else if (AUDIT_FILE_RE.test(name)) {
|
|
16965
19235
|
sources.push({ path: (0, import_node_path32.join)(auditDir, name), dateMs });
|
|
16966
19236
|
}
|
|
@@ -16978,7 +19248,7 @@ function toMetaRecord(record) {
|
|
|
16978
19248
|
return { ...meta, hasBody: true };
|
|
16979
19249
|
}
|
|
16980
19250
|
function readAuditRecords(auditDir, query2 = {}) {
|
|
16981
|
-
if (!(0,
|
|
19251
|
+
if (!(0, import_node_fs32.existsSync)(auditDir)) return [];
|
|
16982
19252
|
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
16983
19253
|
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
16984
19254
|
const limit = Math.min(MAX_LIMIT, Math.max(1, Math.trunc(query2.limit ?? DEFAULT_LIMIT)));
|
|
@@ -17006,7 +19276,7 @@ function readAuditRecords(auditDir, query2 = {}) {
|
|
|
17006
19276
|
}
|
|
17007
19277
|
|
|
17008
19278
|
// src/audit/AuditWriter.ts
|
|
17009
|
-
var
|
|
19279
|
+
var import_node_fs33 = require("fs");
|
|
17010
19280
|
var import_node_path33 = require("path");
|
|
17011
19281
|
var AuditWriter = class {
|
|
17012
19282
|
constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
|
|
@@ -17054,7 +19324,7 @@ var AuditWriter = class {
|
|
|
17054
19324
|
/** Create a directory once per process and remember it. */
|
|
17055
19325
|
ensureDir(path2) {
|
|
17056
19326
|
if (!this.ensuredDirs.has(path2)) {
|
|
17057
|
-
(0,
|
|
19327
|
+
(0, import_node_fs33.mkdirSync)(path2, { recursive: true });
|
|
17058
19328
|
this.ensuredDirs.add(path2);
|
|
17059
19329
|
}
|
|
17060
19330
|
return path2;
|
|
@@ -17064,8 +19334,8 @@ var AuditWriter = class {
|
|
|
17064
19334
|
const { requestBody: _req, responseBody: _res, ...meta } = record;
|
|
17065
19335
|
const file = (0, import_node_path33.join)(dayPath, AUDIT_META_FILE);
|
|
17066
19336
|
const line = JSON.stringify(meta) + "\n";
|
|
17067
|
-
const bytesBefore = (0,
|
|
17068
|
-
(0,
|
|
19337
|
+
const bytesBefore = (0, import_node_fs33.existsSync)(file) ? (0, import_node_fs33.statSync)(file).size : 0;
|
|
19338
|
+
(0, import_node_fs33.appendFileSync)(file, line, "utf8");
|
|
17069
19339
|
try {
|
|
17070
19340
|
updateAuditStatsAfterAppend(
|
|
17071
19341
|
file,
|
|
@@ -17097,7 +19367,7 @@ var AuditWriter = class {
|
|
|
17097
19367
|
const line = encodeBodyEntry(record, sessionKey, dayDir, this.bases);
|
|
17098
19368
|
if (line === null) return;
|
|
17099
19369
|
const bodiesPath = this.ensureDir((0, import_node_path33.join)(dayPath, AUDIT_BODIES_DIR));
|
|
17100
|
-
(0,
|
|
19370
|
+
(0, import_node_fs33.appendFileSync)((0, import_node_path33.join)(bodiesPath, auditBodyFileName(sessionKey)), line + "\n", "utf8");
|
|
17101
19371
|
} catch (error) {
|
|
17102
19372
|
this.bases.forget(sessionKey);
|
|
17103
19373
|
this.logger.warn("[AuditWriter] failed to append audit body shard", {
|
|
@@ -17109,10 +19379,10 @@ var AuditWriter = class {
|
|
|
17109
19379
|
};
|
|
17110
19380
|
|
|
17111
19381
|
// src/billing/BillingPublisher.ts
|
|
17112
|
-
var
|
|
19382
|
+
var import_node_fs34 = require("fs");
|
|
17113
19383
|
var import_node_crypto24 = require("crypto");
|
|
17114
19384
|
var import_node_path34 = require("path");
|
|
17115
|
-
var
|
|
19385
|
+
var import_upstreamFetch13 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
17116
19386
|
|
|
17117
19387
|
// src/billing/billingFiles.ts
|
|
17118
19388
|
var BILLING_FILE_RE = /^billing-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
|
|
@@ -17135,7 +19405,7 @@ var BillingPublisher = class {
|
|
|
17135
19405
|
constructor(billingDir, logger, opts = {}) {
|
|
17136
19406
|
this.billingDir = billingDir;
|
|
17137
19407
|
this.logger = logger;
|
|
17138
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
19408
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch13.fetchUpstream)(url, init));
|
|
17139
19409
|
this.defer = opts.defer ?? ((fn) => setTimeout(fn, 0));
|
|
17140
19410
|
this.timeoutMs = opts.timeoutMs ?? BILLING_POST_TIMEOUT_MS;
|
|
17141
19411
|
this.now = opts.now ?? Date.now;
|
|
@@ -17183,7 +19453,7 @@ var BillingPublisher = class {
|
|
|
17183
19453
|
appendNow(event) {
|
|
17184
19454
|
this.ensureDir();
|
|
17185
19455
|
const file = (0, import_node_path34.join)(this.billingDir, billingFileName(event.ts));
|
|
17186
|
-
(0,
|
|
19456
|
+
(0, import_node_fs34.appendFileSync)(file, JSON.stringify(event) + "\n", "utf8");
|
|
17187
19457
|
}
|
|
17188
19458
|
/**
|
|
17189
19459
|
* One best-effort delivery attempt for an event ALREADY in the ledger. POSTs the
|
|
@@ -17233,7 +19503,7 @@ var BillingPublisher = class {
|
|
|
17233
19503
|
try {
|
|
17234
19504
|
this.ensureDir();
|
|
17235
19505
|
const file = (0, import_node_path34.join)(this.billingDir, deliveredFileName(event.ts));
|
|
17236
|
-
(0,
|
|
19506
|
+
(0, import_node_fs34.appendFileSync)(file, JSON.stringify({ id: event.id, deliveredAt: this.now() }) + "\n", "utf8");
|
|
17237
19507
|
} catch (error) {
|
|
17238
19508
|
this.logger.warn("[BillingPublisher] failed to append delivery marker", {
|
|
17239
19509
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -17242,20 +19512,20 @@ var BillingPublisher = class {
|
|
|
17242
19512
|
}
|
|
17243
19513
|
ensureDir() {
|
|
17244
19514
|
if (this.dirEnsured) return;
|
|
17245
|
-
(0,
|
|
19515
|
+
(0, import_node_fs34.mkdirSync)(this.billingDir, { recursive: true });
|
|
17246
19516
|
this.dirEnsured = true;
|
|
17247
19517
|
}
|
|
17248
19518
|
};
|
|
17249
19519
|
|
|
17250
19520
|
// src/billing/billingReader.ts
|
|
17251
|
-
var
|
|
19521
|
+
var import_node_fs35 = require("fs");
|
|
17252
19522
|
var import_node_path35 = require("path");
|
|
17253
19523
|
function readBillingLedger(billingDir) {
|
|
17254
19524
|
const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
|
|
17255
|
-
if (!(0,
|
|
19525
|
+
if (!(0, import_node_fs35.existsSync)(billingDir)) return view;
|
|
17256
19526
|
let files;
|
|
17257
19527
|
try {
|
|
17258
|
-
files = (0,
|
|
19528
|
+
files = (0, import_node_fs35.readdirSync)(billingDir);
|
|
17259
19529
|
} catch {
|
|
17260
19530
|
return view;
|
|
17261
19531
|
}
|
|
@@ -17286,7 +19556,7 @@ function readBillingStatus(billingDir) {
|
|
|
17286
19556
|
function parseLines(dir, file) {
|
|
17287
19557
|
let raw;
|
|
17288
19558
|
try {
|
|
17289
|
-
raw = (0,
|
|
19559
|
+
raw = (0, import_node_fs35.readFileSync)((0, import_node_path35.join)(dir, file), "utf8");
|
|
17290
19560
|
} catch {
|
|
17291
19561
|
return [];
|
|
17292
19562
|
}
|
|
@@ -17385,7 +19655,7 @@ var BillingRetrySweeper = class {
|
|
|
17385
19655
|
// src/TokenRefreshScheduler.ts
|
|
17386
19656
|
var REFRESH_LEAD_MS2 = 5 * 6e4;
|
|
17387
19657
|
var SWEEP_INTERVAL_MS5 = 6e4;
|
|
17388
|
-
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini"];
|
|
19658
|
+
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
|
|
17389
19659
|
var TokenRefreshScheduler = class {
|
|
17390
19660
|
constructor(store, logger, intervalMs = SWEEP_INTERVAL_MS5, leadMs = REFRESH_LEAD_MS2) {
|
|
17391
19661
|
this.store = store;
|
|
@@ -17468,6 +19738,14 @@ var TokenRefreshScheduler = class {
|
|
|
17468
19738
|
return this.store.refreshCodexToken();
|
|
17469
19739
|
case "gemini":
|
|
17470
19740
|
return this.store.refreshGeminiToken();
|
|
19741
|
+
case "kimi":
|
|
19742
|
+
return this.store.refreshKimiToken();
|
|
19743
|
+
case "grok":
|
|
19744
|
+
return this.store.refreshGrokToken();
|
|
19745
|
+
// ghu_ tokens never near-expire (far-future expiresAt), so the sweep
|
|
19746
|
+
// never reaches this — the branch exists for union totality.
|
|
19747
|
+
case "copilot":
|
|
19748
|
+
return this.store.refreshCopilotToken();
|
|
17471
19749
|
}
|
|
17472
19750
|
}
|
|
17473
19751
|
};
|
|
@@ -17544,7 +19822,7 @@ function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
|
17544
19822
|
|
|
17545
19823
|
// src/webhook/WebhookDispatcher.ts
|
|
17546
19824
|
var import_node_crypto25 = require("crypto");
|
|
17547
|
-
var
|
|
19825
|
+
var import_upstreamFetch14 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
17548
19826
|
var WEBHOOK_MAX_ATTEMPTS = 3;
|
|
17549
19827
|
var WEBHOOK_QUEUE_MAX = 1e3;
|
|
17550
19828
|
var WEBHOOK_SEND_TIMEOUT_MS = 1e4;
|
|
@@ -17564,7 +19842,7 @@ var WebhookDispatcher = class {
|
|
|
17564
19842
|
sleep;
|
|
17565
19843
|
now;
|
|
17566
19844
|
constructor(opts = {}) {
|
|
17567
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
19845
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch14.fetchUpstream)(url, init));
|
|
17568
19846
|
this.logger = opts.logger;
|
|
17569
19847
|
this.maxAttempts = opts.maxAttempts ?? WEBHOOK_MAX_ATTEMPTS;
|
|
17570
19848
|
this.queueMax = opts.queueMax ?? WEBHOOK_QUEUE_MAX;
|
|
@@ -17650,8 +19928,8 @@ var WebhookDispatcher = class {
|
|
|
17650
19928
|
signal: AbortSignal.timeout(this.timeoutMs)
|
|
17651
19929
|
});
|
|
17652
19930
|
return res.ok ? { ok: true, status: res.status } : { ok: false, status: res.status };
|
|
17653
|
-
} catch (
|
|
17654
|
-
return { ok: false, error:
|
|
19931
|
+
} catch (err8) {
|
|
19932
|
+
return { ok: false, error: err8 instanceof Error ? err8.message : String(err8) };
|
|
17655
19933
|
}
|
|
17656
19934
|
}
|
|
17657
19935
|
/**
|
|
@@ -17714,7 +19992,7 @@ function feishuText(event) {
|
|
|
17714
19992
|
// src/bootstrap.ts
|
|
17715
19993
|
var activeImageRuntimeBootstrapSession;
|
|
17716
19994
|
function createImageRuntimeBootstrapSession(initialGeneration) {
|
|
17717
|
-
const openAIOperationRegistry = new
|
|
19995
|
+
const openAIOperationRegistry = new import_core7.OpenAIOperationRegistry();
|
|
17718
19996
|
const imageRuntimeManager = new ImageRuntimeManager(initialGeneration);
|
|
17719
19997
|
const unregisterContributions = [];
|
|
17720
19998
|
try {
|
|
@@ -17770,7 +20048,7 @@ function installImageRuntimeBootstrapSession(initialGeneration) {
|
|
|
17770
20048
|
function resolveLoggingConfig(configured, configPath) {
|
|
17771
20049
|
const file = configured?.file ?? defaultDaemonLogPath(configPath);
|
|
17772
20050
|
try {
|
|
17773
|
-
(0,
|
|
20051
|
+
(0, import_node_fs36.mkdirSync)(configured?.file ? (0, import_node_path36.dirname)(configured.file) : defaultLogDir(configPath), {
|
|
17774
20052
|
recursive: true
|
|
17775
20053
|
});
|
|
17776
20054
|
} catch {
|
|
@@ -17788,12 +20066,12 @@ function buildDaemon(config, paths) {
|
|
|
17788
20066
|
setSecretBox(secretBox3);
|
|
17789
20067
|
setSecretBox2(secretBox3);
|
|
17790
20068
|
const decryptedConfig = decryptConfigSecrets(config, secretBox3);
|
|
17791
|
-
const accountAllowanceStore = new
|
|
20069
|
+
const accountAllowanceStore = new import_AccountAllowanceStore9.AccountAllowanceStore(
|
|
17792
20070
|
Date.now,
|
|
17793
20071
|
void 0,
|
|
17794
20072
|
new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
|
|
17795
20073
|
);
|
|
17796
|
-
(0,
|
|
20074
|
+
(0, import_AccountAllowanceStore9.setSharedAccountAllowanceStore)(accountAllowanceStore);
|
|
17797
20075
|
(0, import_AccountAllowanceScheduling5.getSharedAccountAllowanceScheduling)().configure(
|
|
17798
20076
|
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
17799
20077
|
);
|
|
@@ -17818,21 +20096,22 @@ function buildDaemon(config, paths) {
|
|
|
17818
20096
|
claudeAllowanceRefreshScheduler.configure(
|
|
17819
20097
|
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
17820
20098
|
);
|
|
17821
|
-
const subscriptionAccounts = new
|
|
17822
|
-
(0,
|
|
17823
|
-
const subscriptionRegistry = new
|
|
20099
|
+
const subscriptionAccounts = new import_subscriptions12.SubscriptionAccountService(credentialStore);
|
|
20100
|
+
(0, import_subscriptions12.setSubscriptionAccountService)(subscriptionAccounts);
|
|
20101
|
+
const subscriptionRegistry = new import_subscriptions12.SubscriptionProviderRegistry(
|
|
17824
20102
|
subscriptionAccounts,
|
|
17825
20103
|
credentialStore
|
|
17826
20104
|
);
|
|
17827
|
-
(0,
|
|
20105
|
+
(0, import_subscriptions12.setSubscriptionProviderRegistry)(subscriptionRegistry);
|
|
17828
20106
|
setServerProxyConfig(decryptedConfig.server?.proxy);
|
|
17829
|
-
(0,
|
|
20107
|
+
(0, import_upstreamFetch15.setUpstreamProxyResolver)(
|
|
17830
20108
|
createUpstreamProxyResolver({
|
|
17831
20109
|
getAccountProxy: (providerId, accountId) => credentialStore.getAccountProxy(providerId, accountId)
|
|
17832
20110
|
})
|
|
17833
20111
|
);
|
|
17834
20112
|
(0, import_gemini_code_assist_resolver.setGeminiCodeAssistResolver)((0, import_GeminiCodeAssistProjectResolver.getGeminiCodeAssistProjectResolver)());
|
|
17835
20113
|
const autoDisableStore = new AutoDisableStore();
|
|
20114
|
+
const providerKeyQuotaService = new ProviderKeyQuotaService(secretBox3);
|
|
17836
20115
|
const apiKeyPool = new import_ApiKeyPoolService.ApiKeyPoolService(
|
|
17837
20116
|
createPoolKeysLoader((id) => llmConfig.getProviderRow(id), autoDisableStore),
|
|
17838
20117
|
resolveEnvKey,
|
|
@@ -17849,7 +20128,7 @@ function buildDaemon(config, paths) {
|
|
|
17849
20128
|
const pricingEngine = new import_usage2.PricingEngine(pricingStore, logger, {
|
|
17850
20129
|
// Catalog egress follows the same global/env proxy policy as every other
|
|
17851
20130
|
// daemon upstream call; no provider/account override applies here.
|
|
17852
|
-
fetchImpl: ((input, init) => (0,
|
|
20131
|
+
fetchImpl: ((input, init) => (0, import_upstreamFetch15.fetchUpstream)(String(input), init ?? {}))
|
|
17853
20132
|
});
|
|
17854
20133
|
const pricingRefreshScheduler = new PricingRefreshScheduler(
|
|
17855
20134
|
pricingEngine,
|
|
@@ -18113,6 +20392,11 @@ function buildDaemon(config, paths) {
|
|
|
18113
20392
|
// values themselves NEVER leave (masked via `maskProviderApiKey`).
|
|
18114
20393
|
apiKeyPool,
|
|
18115
20394
|
autoDisableStore,
|
|
20395
|
+
// BYO provider-key quota (Z.AI coding plan, MiniMax Token Plan, …): a
|
|
20396
|
+
// read-through cached same-key usage probe surfaced on the keys view. The
|
|
20397
|
+
// key plaintext is resolved + decrypted inside the service and never
|
|
20398
|
+
// crosses back out.
|
|
20399
|
+
providerKeyQuota: providerKeyQuotaService,
|
|
18116
20400
|
// Interactive OAuth login over admin HTTP (app-parity child 4, design
|
|
18117
20401
|
// D1/D2-a). The in-memory pending-session store (NEVER serialized), the
|
|
18118
20402
|
// injected token-exchange fetch (global `fetch` here; mocked in tests), and a
|
|
@@ -18129,7 +20413,7 @@ function buildDaemon(config, paths) {
|
|
|
18129
20413
|
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
18130
20414
|
// excluded from the upstream trace, so a failing login left no evidence.
|
|
18131
20415
|
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
18132
|
-
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0,
|
|
20416
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0, import_upstreamFetch15.fetchUpstream)(url, init, { providerId, redactBodies: true }),
|
|
18133
20417
|
subscriptionAccountAppender: credentialStore,
|
|
18134
20418
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
18135
20419
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -18137,6 +20421,13 @@ function buildDaemon(config, paths) {
|
|
|
18137
20421
|
// can inject a mock so no real port is bound.
|
|
18138
20422
|
codexSessions: new CodexOAuthSessionStore(),
|
|
18139
20423
|
codexAwaitLoopback: paths.codexAwaitLoopback ?? ((state, timeoutMs, signal) => awaitLoopbackCode(state, timeoutMs, signal)),
|
|
20424
|
+
// Kimi interactive OAuth — the async DEVICE-CODE flow store (no port, no
|
|
20425
|
+
// paste; the app shows the verification URL + user code and polls the
|
|
20426
|
+
// token-free status). Token captured + persisted daemon-side.
|
|
20427
|
+
kimiSessions: new CodexOAuthSessionStore(),
|
|
20428
|
+
// Grok interactive OAuth — the same async DEVICE-CODE shape as kimi.
|
|
20429
|
+
grokSessions: new CodexOAuthSessionStore(),
|
|
20430
|
+
copilotSessions: new CodexOAuthSessionStore(),
|
|
18140
20431
|
// Migration pack (app-parity child 6, design D2/D3) — the concrete credential
|
|
18141
20432
|
// store provides BOTH the full DECRYPTED read (`getFullConfig`, export) and
|
|
18142
20433
|
// the multi-account append (`appendProviderAccount`, import re-encrypts at-
|
|
@@ -18195,7 +20486,7 @@ function buildDaemon(config, paths) {
|
|
|
18195
20486
|
});
|
|
18196
20487
|
const webhookDispatcher = new WebhookDispatcher({
|
|
18197
20488
|
logger,
|
|
18198
|
-
fetchImpl: (url, init) => (0,
|
|
20489
|
+
fetchImpl: (url, init) => (0, import_upstreamFetch15.fetchUpstream)(url, init)
|
|
18199
20490
|
});
|
|
18200
20491
|
setWebhookRuntime(webhookDispatcher, (0, import_SubscriptionAccountHealth4.getSharedAccountHealth)());
|
|
18201
20492
|
const auditWriter = new AuditWriter(auditDir, logger);
|
|
@@ -18272,8 +20563,8 @@ function buildDaemon(config, paths) {
|
|
|
18272
20563
|
}
|
|
18273
20564
|
function isTokensStoreReadable(tokensPath) {
|
|
18274
20565
|
try {
|
|
18275
|
-
if (!(0,
|
|
18276
|
-
(0,
|
|
20566
|
+
if (!(0, import_node_fs36.existsSync)(tokensPath)) return true;
|
|
20567
|
+
(0, import_node_fs36.accessSync)(tokensPath, import_node_fs36.constants.R_OK);
|
|
18277
20568
|
return true;
|
|
18278
20569
|
} catch {
|
|
18279
20570
|
return false;
|
|
@@ -18509,11 +20800,11 @@ async function runLiveProbe(url, key, fetchImpl = fetch) {
|
|
|
18509
20800
|
status: res.status,
|
|
18510
20801
|
estimateHeader: res.headers.get("x-omnicross-count-estimate")
|
|
18511
20802
|
};
|
|
18512
|
-
} catch (
|
|
20803
|
+
} catch (err8) {
|
|
18513
20804
|
return {
|
|
18514
20805
|
status: null,
|
|
18515
20806
|
estimateHeader: null,
|
|
18516
|
-
error:
|
|
20807
|
+
error: err8 instanceof Error ? err8.message : String(err8)
|
|
18517
20808
|
};
|
|
18518
20809
|
}
|
|
18519
20810
|
}
|
|
@@ -18601,7 +20892,7 @@ async function runDoctor(argv, fetchImpl = fetch) {
|
|
|
18601
20892
|
}
|
|
18602
20893
|
|
|
18603
20894
|
// src/commands/import-ccr.ts
|
|
18604
|
-
var
|
|
20895
|
+
var import_node_fs37 = require("fs");
|
|
18605
20896
|
var import_node_util3 = require("util");
|
|
18606
20897
|
|
|
18607
20898
|
// src/ccr-import.ts
|
|
@@ -18696,7 +20987,7 @@ async function runImportCcr(argv) {
|
|
|
18696
20987
|
const outPath = values.out ?? "omnicross.config.json";
|
|
18697
20988
|
let raw;
|
|
18698
20989
|
try {
|
|
18699
|
-
raw = JSON.parse((0,
|
|
20990
|
+
raw = JSON.parse((0, import_node_fs37.readFileSync)(ccrPath, "utf8"));
|
|
18700
20991
|
} catch {
|
|
18701
20992
|
throw new Error(`import-ccr: cannot read or parse '${ccrPath}'`);
|
|
18702
20993
|
}
|
|
@@ -18834,7 +21125,7 @@ async function keysRevoke(db, id) {
|
|
|
18834
21125
|
// src/commands/launch.ts
|
|
18835
21126
|
var import_node_child_process2 = require("child_process");
|
|
18836
21127
|
var import_node_crypto26 = require("crypto");
|
|
18837
|
-
var
|
|
21128
|
+
var import_node_fs38 = require("fs");
|
|
18838
21129
|
var import_node_path38 = require("path");
|
|
18839
21130
|
var import_node_util6 = require("util");
|
|
18840
21131
|
var import_cli_launcher3 = require("@omnicross/cli-launcher");
|
|
@@ -18879,7 +21170,7 @@ function resolveInPathDefault(candidate) {
|
|
|
18879
21170
|
const segments = (process.env["PATH"] ?? "").split(import_node_path38.delimiter).filter(Boolean);
|
|
18880
21171
|
for (const seg of segments) {
|
|
18881
21172
|
const full = (0, import_node_path38.join)(seg, candidate);
|
|
18882
|
-
if ((0,
|
|
21173
|
+
if ((0, import_node_fs38.existsSync)(full)) return full;
|
|
18883
21174
|
}
|
|
18884
21175
|
return null;
|
|
18885
21176
|
}
|
|
@@ -18920,9 +21211,9 @@ async function runLaunch(argv, deps) {
|
|
|
18920
21211
|
await daemon.llmConfig.ready();
|
|
18921
21212
|
await daemon.migrateUsageStore();
|
|
18922
21213
|
await daemon.providerProxy.start();
|
|
18923
|
-
} catch (
|
|
21214
|
+
} catch (err8) {
|
|
18924
21215
|
await shutdownLaunchDaemon(daemon);
|
|
18925
|
-
throw
|
|
21216
|
+
throw err8;
|
|
18926
21217
|
}
|
|
18927
21218
|
let launch;
|
|
18928
21219
|
try {
|
|
@@ -18930,9 +21221,9 @@ async function runLaunch(argv, deps) {
|
|
|
18930
21221
|
providerId: values.provider,
|
|
18931
21222
|
model: values.model
|
|
18932
21223
|
});
|
|
18933
|
-
} catch (
|
|
21224
|
+
} catch (err8) {
|
|
18934
21225
|
await shutdownLaunchDaemon(daemon);
|
|
18935
|
-
throw
|
|
21226
|
+
throw err8;
|
|
18936
21227
|
}
|
|
18937
21228
|
try {
|
|
18938
21229
|
const plan = buildCliSpawnPlan({
|
|
@@ -19037,9 +21328,9 @@ function spawnCliInherit(plan) {
|
|
|
19037
21328
|
process.removeListener("SIGINT", onSignal);
|
|
19038
21329
|
process.removeListener("SIGTERM", onSignal);
|
|
19039
21330
|
};
|
|
19040
|
-
child.on("error", (
|
|
21331
|
+
child.on("error", (err8) => {
|
|
19041
21332
|
detach();
|
|
19042
|
-
if (
|
|
21333
|
+
if (err8.code === "ENOENT") {
|
|
19043
21334
|
reject(
|
|
19044
21335
|
new Error(
|
|
19045
21336
|
`launch: "${plan.command}" not found on PATH \u2014 install the CLI first.`
|
|
@@ -19047,7 +21338,7 @@ function spawnCliInherit(plan) {
|
|
|
19047
21338
|
);
|
|
19048
21339
|
return;
|
|
19049
21340
|
}
|
|
19050
|
-
reject(
|
|
21341
|
+
reject(err8);
|
|
19051
21342
|
});
|
|
19052
21343
|
child.on("exit", (code, signal) => {
|
|
19053
21344
|
detach();
|
|
@@ -19060,9 +21351,9 @@ function spawnCliInherit(plan) {
|
|
|
19060
21351
|
var import_node_child_process3 = require("child_process");
|
|
19061
21352
|
var import_node_readline2 = require("readline");
|
|
19062
21353
|
var import_node_util7 = require("util");
|
|
19063
|
-
var
|
|
19064
|
-
var
|
|
19065
|
-
var PROVIDERS2 = ["claude", "codex", "gemini"];
|
|
21354
|
+
var import_upstreamFetch16 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
21355
|
+
var import_subscriptions13 = require("@omnicross/subscriptions");
|
|
21356
|
+
var PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
|
|
19066
21357
|
async function runLogin(argv, deps) {
|
|
19067
21358
|
const { values, positionals } = (0, import_node_util7.parseArgs)({
|
|
19068
21359
|
args: argv,
|
|
@@ -19070,7 +21361,9 @@ async function runLogin(argv, deps) {
|
|
|
19070
21361
|
config: { type: "string", short: "c" },
|
|
19071
21362
|
"master-key-file": { type: "string" },
|
|
19072
21363
|
// Optional user label for the appended account (multi-account).
|
|
19073
|
-
label: { type: "string" }
|
|
21364
|
+
label: { type: "string" },
|
|
21365
|
+
// Optional GitHub Enterprise domain for `login copilot` (GHE accounts).
|
|
21366
|
+
enterprise: { type: "string" }
|
|
19074
21367
|
},
|
|
19075
21368
|
allowPositionals: true
|
|
19076
21369
|
});
|
|
@@ -19084,43 +21377,55 @@ async function runLogin(argv, deps) {
|
|
|
19084
21377
|
if (!values.config) {
|
|
19085
21378
|
throw new Error("login: --config <path> is required");
|
|
19086
21379
|
}
|
|
21380
|
+
if (values.enterprise !== void 0 && provider !== "copilot") {
|
|
21381
|
+
throw new Error("login: --enterprise is only supported for the copilot provider");
|
|
21382
|
+
}
|
|
21383
|
+
const enterpriseDomain = values.enterprise !== void 0 ? import_subscriptions13.copilotOAuth.normalizeCopilotEnterpriseDomain(values.enterprise) : void 0;
|
|
19087
21384
|
const resolved = {
|
|
19088
21385
|
openBrowser: deps?.openBrowser ?? openBrowser,
|
|
19089
21386
|
promptPaste: deps?.promptPaste ?? promptPaste,
|
|
19090
21387
|
awaitLoopback: deps?.awaitLoopback ?? ((state) => awaitLoopbackCode(state)),
|
|
21388
|
+
awaitKimiDevice: deps?.awaitKimiDevice ?? ((fetchImpl) => runKimiDeviceFlow(fetchImpl, resolvedOpenBrowser)),
|
|
21389
|
+
awaitGrokDevice: deps?.awaitGrokDevice ?? ((fetchImpl) => runGrokDeviceFlow(fetchImpl, resolvedOpenBrowser)),
|
|
21390
|
+
awaitCopilotDevice: deps?.awaitCopilotDevice ?? ((fetchImpl, enterpriseUrl) => runCopilotDeviceFlow(fetchImpl, resolvedOpenBrowser, enterpriseUrl)),
|
|
19091
21391
|
tokensFetch: deps?.tokensFetch
|
|
19092
21392
|
};
|
|
21393
|
+
const resolvedOpenBrowser = resolved.openBrowser;
|
|
19093
21394
|
const box = resolveSecretBox(values["master-key-file"]);
|
|
19094
21395
|
setSecretBox(box);
|
|
19095
|
-
(0,
|
|
21396
|
+
(0, import_upstreamFetch16.setUpstreamProxyResolver)(createUpstreamProxyResolver());
|
|
19096
21397
|
try {
|
|
19097
21398
|
const tokensPath = defaultTokensPath(values.config);
|
|
19098
|
-
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => (0,
|
|
21399
|
+
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => (0, import_upstreamFetch16.fetchUpstream)(url, init, { providerId: provider, redactBodies: true }));
|
|
19099
21400
|
const store = new JsonSubscriptionCredentialStore(tokensPath, box, exchangeFetch);
|
|
19100
21401
|
const expiresAt = await runProviderLogin(
|
|
19101
21402
|
provider,
|
|
19102
21403
|
store,
|
|
19103
21404
|
resolved,
|
|
19104
21405
|
exchangeFetch,
|
|
19105
|
-
values.label
|
|
21406
|
+
values.label,
|
|
21407
|
+
enterpriseDomain
|
|
19106
21408
|
);
|
|
19107
21409
|
console.info(`Logged in to '${provider}' \u2192 ${tokensPath}`);
|
|
19108
21410
|
console.info(` token: [stored, encrypted] expiresAt: ${expiresAt ?? "n/a"}`);
|
|
19109
21411
|
} finally {
|
|
19110
21412
|
setSecretBox(null);
|
|
19111
|
-
(0,
|
|
21413
|
+
(0, import_upstreamFetch16.setUpstreamProxyResolver)(null);
|
|
19112
21414
|
}
|
|
19113
21415
|
}
|
|
19114
|
-
async function runProviderLogin(provider, store, deps, exchangeFetch, label) {
|
|
21416
|
+
async function runProviderLogin(provider, store, deps, exchangeFetch, label, enterpriseUrl) {
|
|
19115
21417
|
if (provider === "codex") return loginCodex(store, deps, exchangeFetch, label);
|
|
19116
21418
|
if (provider === "claude") return loginClaude(store, deps, exchangeFetch, label);
|
|
21419
|
+
if (provider === "kimi") return loginKimi(store, deps, exchangeFetch, label);
|
|
21420
|
+
if (provider === "grok") return loginGrok(store, deps, exchangeFetch, label);
|
|
21421
|
+
if (provider === "copilot") return loginCopilot(store, deps, exchangeFetch, label, enterpriseUrl);
|
|
19117
21422
|
return loginGemini(store, deps, exchangeFetch, label);
|
|
19118
21423
|
}
|
|
19119
21424
|
async function loginCodex(store, deps, exchangeFetch, label) {
|
|
19120
|
-
const { authUrl, codeVerifier, state } =
|
|
21425
|
+
const { authUrl, codeVerifier, state } = import_subscriptions13.codexOAuth.generateAuthParams();
|
|
19121
21426
|
await presentUrl(authUrl, deps);
|
|
19122
21427
|
const code = await deps.awaitLoopback(state);
|
|
19123
|
-
const result = await
|
|
21428
|
+
const result = await import_subscriptions13.codexOAuth.exchangeCodeForTokens(
|
|
19124
21429
|
{ authorizationCode: code, codeVerifier, state },
|
|
19125
21430
|
exchangeFetch
|
|
19126
21431
|
);
|
|
@@ -19139,7 +21444,7 @@ async function loginCodex(store, deps, exchangeFetch, label) {
|
|
|
19139
21444
|
return expiresAt;
|
|
19140
21445
|
}
|
|
19141
21446
|
async function loginClaude(store, deps, exchangeFetch, label) {
|
|
19142
|
-
const { authUrl, codeVerifier, state } =
|
|
21447
|
+
const { authUrl, codeVerifier, state } = import_subscriptions13.claudeOAuth.generateAuthParams();
|
|
19143
21448
|
await presentUrl(authUrl, deps);
|
|
19144
21449
|
const pasted = (await deps.promptPaste("Paste the authorization code (code#state): ")).trim();
|
|
19145
21450
|
const [code, pastedState] = pasted.split("#");
|
|
@@ -19147,7 +21452,7 @@ async function loginClaude(store, deps, exchangeFetch, label) {
|
|
|
19147
21452
|
if (pastedState && pastedState !== state) {
|
|
19148
21453
|
throw new Error("login: pasted state did not match (possible CSRF) \u2014 aborting");
|
|
19149
21454
|
}
|
|
19150
|
-
const result = await
|
|
21455
|
+
const result = await import_subscriptions13.claudeOAuth.exchangeCodeForTokens(
|
|
19151
21456
|
{ authorizationCode: code, codeVerifier, state },
|
|
19152
21457
|
exchangeFetch
|
|
19153
21458
|
);
|
|
@@ -19166,11 +21471,11 @@ async function loginClaude(store, deps, exchangeFetch, label) {
|
|
|
19166
21471
|
return expiresAt;
|
|
19167
21472
|
}
|
|
19168
21473
|
async function loginGemini(store, deps, exchangeFetch, label) {
|
|
19169
|
-
const { authUrl, codeVerifier } =
|
|
21474
|
+
const { authUrl, codeVerifier } = import_subscriptions13.geminiOAuth.generateAuthParams();
|
|
19170
21475
|
await presentUrl(authUrl, deps);
|
|
19171
21476
|
const code = (await deps.promptPaste("Paste the authorization code: ")).trim();
|
|
19172
21477
|
if (!code) throw new Error("login: no authorization code was pasted");
|
|
19173
|
-
const result = await
|
|
21478
|
+
const result = await import_subscriptions13.geminiOAuth.exchangeCodeForTokens(code, codeVerifier, exchangeFetch);
|
|
19174
21479
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
19175
21480
|
const block = {
|
|
19176
21481
|
authMethod: "oauth",
|
|
@@ -19184,6 +21489,129 @@ async function loginGemini(store, deps, exchangeFetch, label) {
|
|
|
19184
21489
|
logMasked("gemini", result.accessToken);
|
|
19185
21490
|
return expiresAt;
|
|
19186
21491
|
}
|
|
21492
|
+
async function runKimiDeviceFlow(exchangeFetch, openBrowserFn) {
|
|
21493
|
+
const deviceId = import_subscriptions13.kimiOAuth.generateKimiDeviceId();
|
|
21494
|
+
const fingerprint = import_subscriptions13.kimiOAuth.kimiFingerprintHeaders(deviceId);
|
|
21495
|
+
const authorization = await import_subscriptions13.kimiOAuth.requestDeviceAuthorization(exchangeFetch, fingerprint);
|
|
21496
|
+
const url = authorization.verificationUriComplete ?? authorization.verificationUri;
|
|
21497
|
+
console.info("Open this URL in your browser and approve the request:");
|
|
21498
|
+
console.info(` ${url}`);
|
|
21499
|
+
if (!authorization.verificationUriComplete) {
|
|
21500
|
+
console.info(` Then enter this code: ${authorization.userCode}`);
|
|
21501
|
+
}
|
|
21502
|
+
await openBrowserFn(url).catch(() => false);
|
|
21503
|
+
const result = await import_subscriptions13.kimiOAuth.awaitDeviceToken(authorization, exchangeFetch, {
|
|
21504
|
+
fingerprint,
|
|
21505
|
+
onPending: () => process.stdout.write(".")
|
|
21506
|
+
});
|
|
21507
|
+
console.info("");
|
|
21508
|
+
return {
|
|
21509
|
+
...result,
|
|
21510
|
+
accountId: import_subscriptions13.kimiOAuth.kimiAccountIdFromAccessToken(result.accessToken),
|
|
21511
|
+
deviceId
|
|
21512
|
+
};
|
|
21513
|
+
}
|
|
21514
|
+
async function loginKimi(store, deps, exchangeFetch, label) {
|
|
21515
|
+
const result = await deps.awaitKimiDevice(exchangeFetch);
|
|
21516
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
21517
|
+
const block = {
|
|
21518
|
+
authMethod: "oauth",
|
|
21519
|
+
status: "authorized",
|
|
21520
|
+
accessToken: result.accessToken,
|
|
21521
|
+
refreshToken: result.refreshToken,
|
|
21522
|
+
expiresAt,
|
|
21523
|
+
...result.accountId ? { accountId: result.accountId } : {},
|
|
21524
|
+
deviceId: result.deviceId,
|
|
21525
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21526
|
+
};
|
|
21527
|
+
await store.appendProviderAccount("kimi", block, label);
|
|
21528
|
+
logMasked("kimi", result.accessToken);
|
|
21529
|
+
return expiresAt;
|
|
21530
|
+
}
|
|
21531
|
+
async function runGrokDeviceFlow(exchangeFetch, openBrowserFn) {
|
|
21532
|
+
const tokenEndpoint = await import_subscriptions13.grokOAuth.resolveGrokTokenEndpoint(exchangeFetch);
|
|
21533
|
+
const authorization = await import_subscriptions13.grokOAuth.requestGrokDeviceAuthorization(exchangeFetch);
|
|
21534
|
+
const url = authorization.verificationUriComplete ?? authorization.verificationUri;
|
|
21535
|
+
console.info("Open this URL in your browser and approve the request:");
|
|
21536
|
+
console.info(` ${url}`);
|
|
21537
|
+
if (!authorization.verificationUriComplete) {
|
|
21538
|
+
console.info(` Then enter this code: ${authorization.userCode}`);
|
|
21539
|
+
}
|
|
21540
|
+
await openBrowserFn(url).catch(() => false);
|
|
21541
|
+
const result = await import_subscriptions13.grokOAuth.awaitGrokDeviceToken(authorization, tokenEndpoint, exchangeFetch, {
|
|
21542
|
+
onPending: () => process.stdout.write(".")
|
|
21543
|
+
});
|
|
21544
|
+
console.info("");
|
|
21545
|
+
return {
|
|
21546
|
+
...result,
|
|
21547
|
+
accountId: import_subscriptions13.grokOAuth.grokAccountIdFromAccessToken(result.accessToken)
|
|
21548
|
+
};
|
|
21549
|
+
}
|
|
21550
|
+
async function loginGrok(store, deps, exchangeFetch, label) {
|
|
21551
|
+
const result = await deps.awaitGrokDevice(exchangeFetch);
|
|
21552
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
21553
|
+
const block = {
|
|
21554
|
+
authMethod: "oauth",
|
|
21555
|
+
status: "authorized",
|
|
21556
|
+
accessToken: result.accessToken,
|
|
21557
|
+
refreshToken: result.refreshToken,
|
|
21558
|
+
expiresAt,
|
|
21559
|
+
...result.accountId ? { accountId: result.accountId } : {},
|
|
21560
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21561
|
+
};
|
|
21562
|
+
await store.appendProviderAccount("grok", block, label);
|
|
21563
|
+
logMasked("grok", result.accessToken);
|
|
21564
|
+
return expiresAt;
|
|
21565
|
+
}
|
|
21566
|
+
async function runCopilotDeviceFlow(exchangeFetch, openBrowserFn, enterpriseUrl) {
|
|
21567
|
+
if (enterpriseUrl) console.info(`Using GitHub Enterprise host: ${enterpriseUrl}`);
|
|
21568
|
+
const authorization = await import_subscriptions13.copilotOAuth.requestCopilotDeviceAuthorization(exchangeFetch, enterpriseUrl);
|
|
21569
|
+
const url = authorization.verificationUri;
|
|
21570
|
+
console.info("Open this URL in your browser and approve the request:");
|
|
21571
|
+
console.info(` ${url}`);
|
|
21572
|
+
console.info(` Then enter this code: ${authorization.userCode}`);
|
|
21573
|
+
await openBrowserFn(url).catch(() => false);
|
|
21574
|
+
const result = await import_subscriptions13.copilotOAuth.awaitCopilotDeviceToken(authorization, exchangeFetch, {
|
|
21575
|
+
onPending: () => process.stdout.write("."),
|
|
21576
|
+
...enterpriseUrl ? { enterpriseUrl } : {}
|
|
21577
|
+
});
|
|
21578
|
+
console.info("");
|
|
21579
|
+
const identity = await import_subscriptions13.copilotOAuth.fetchCopilotIdentity(result.accessToken, exchangeFetch, enterpriseUrl);
|
|
21580
|
+
const apiEndpoint = await import_subscriptions13.copilotOAuth.discoverCopilotApiEndpoint(result.accessToken, exchangeFetch, enterpriseUrl);
|
|
21581
|
+
console.info("Enabling Copilot models (policy)...");
|
|
21582
|
+
await import_subscriptions13.copilotOAuth.enableAllCopilotModels(
|
|
21583
|
+
result.accessToken,
|
|
21584
|
+
{ apiEndpoint, ...enterpriseUrl ? { enterpriseUrl } : {} },
|
|
21585
|
+
exchangeFetch
|
|
21586
|
+
);
|
|
21587
|
+
return {
|
|
21588
|
+
accessToken: result.accessToken,
|
|
21589
|
+
expiresIn: Math.floor(import_subscriptions13.copilotOAuth.COPILOT_FAR_FUTURE_MS / 1e3),
|
|
21590
|
+
...identity,
|
|
21591
|
+
...apiEndpoint ? { apiEndpoint } : {}
|
|
21592
|
+
};
|
|
21593
|
+
}
|
|
21594
|
+
async function loginCopilot(store, deps, exchangeFetch, label, enterpriseUrl) {
|
|
21595
|
+
const result = await deps.awaitCopilotDevice(exchangeFetch, enterpriseUrl);
|
|
21596
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
21597
|
+
const block = {
|
|
21598
|
+
authMethod: "oauth",
|
|
21599
|
+
status: "authorized",
|
|
21600
|
+
accessToken: result.accessToken,
|
|
21601
|
+
// ghu_ tokens have no refresh lifecycle — the same token doubles as the
|
|
21602
|
+
// stored refresh credential so generic refresh paths stay well-formed.
|
|
21603
|
+
refreshToken: result.accessToken,
|
|
21604
|
+
expiresAt,
|
|
21605
|
+
...result.accountId ? { accountId: result.accountId } : {},
|
|
21606
|
+
...result.email ? { email: result.email } : {},
|
|
21607
|
+
...result.apiEndpoint ? { apiEndpoint: result.apiEndpoint } : {},
|
|
21608
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
21609
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21610
|
+
};
|
|
21611
|
+
await store.appendProviderAccount("copilot", block, label);
|
|
21612
|
+
logMasked("copilot", result.accessToken);
|
|
21613
|
+
return expiresAt;
|
|
21614
|
+
}
|
|
19187
21615
|
function isLoginProvider(value) {
|
|
19188
21616
|
return PROVIDERS2.includes(value);
|
|
19189
21617
|
}
|
|
@@ -19381,7 +21809,7 @@ function providersRmKey(configPath, providerId, keyId) {
|
|
|
19381
21809
|
}
|
|
19382
21810
|
|
|
19383
21811
|
// src/commands/secrets.ts
|
|
19384
|
-
var
|
|
21812
|
+
var import_node_fs39 = require("fs");
|
|
19385
21813
|
var import_node_util9 = require("util");
|
|
19386
21814
|
async function runSecrets(argv) {
|
|
19387
21815
|
const { values, positionals } = (0, import_node_util9.parseArgs)({
|
|
@@ -19454,12 +21882,12 @@ function secretsStatus(args) {
|
|
|
19454
21882
|
reportField("admin.token", cfg.admin.token);
|
|
19455
21883
|
}
|
|
19456
21884
|
const tokensPath = defaultTokensPath(args.config);
|
|
19457
|
-
if ((0,
|
|
21885
|
+
if ((0, import_node_fs39.existsSync)(tokensPath)) {
|
|
19458
21886
|
console.info(`Secret status for ${tokensPath}:`);
|
|
19459
21887
|
reportTokenFields(tokensPath);
|
|
19460
21888
|
}
|
|
19461
21889
|
const integrationsPath = defaultIntegrationsPath(args.config);
|
|
19462
|
-
if ((0,
|
|
21890
|
+
if ((0, import_node_fs39.existsSync)(integrationsPath)) {
|
|
19463
21891
|
const state = readRawJson(integrationsPath);
|
|
19464
21892
|
const key = state.gatewayKey;
|
|
19465
21893
|
if (key && typeof key === "object" && !Array.isArray(key)) {
|
|
@@ -19513,8 +21941,8 @@ async function secretsRotate(args) {
|
|
|
19513
21941
|
const integrationsPath = defaultIntegrationsPath(args.config);
|
|
19514
21942
|
try {
|
|
19515
21943
|
cfg = loadConfig(args.config);
|
|
19516
|
-
if ((0,
|
|
19517
|
-
if ((0,
|
|
21944
|
+
if ((0, import_node_fs39.existsSync)(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, oldBox);
|
|
21945
|
+
if ((0, import_node_fs39.existsSync)(integrationsPath)) {
|
|
19518
21946
|
integrationsPlain = new IntegrationStateStore(integrationsPath, oldBox).load();
|
|
19519
21947
|
}
|
|
19520
21948
|
} finally {
|
|
@@ -19549,20 +21977,20 @@ function secretsDecrypt(args) {
|
|
|
19549
21977
|
let tokensPlain = null;
|
|
19550
21978
|
try {
|
|
19551
21979
|
cfg = loadConfig(args.config);
|
|
19552
|
-
if ((0,
|
|
21980
|
+
if ((0, import_node_fs39.existsSync)(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, box);
|
|
19553
21981
|
} finally {
|
|
19554
21982
|
setSecretBox(null);
|
|
19555
21983
|
}
|
|
19556
21984
|
saveConfig(args.config, cfg);
|
|
19557
21985
|
if (tokensPlain) {
|
|
19558
|
-
(
|
|
21986
|
+
atomicReplaceUtf8(tokensPath, JSON.stringify(tokensPlain, null, 2) + "\n");
|
|
19559
21987
|
}
|
|
19560
21988
|
console.info(`Decrypted secrets to plaintext in ${args.config}` + tokensSuffix(args.config));
|
|
19561
21989
|
}
|
|
19562
21990
|
function readRawConfig(path2) {
|
|
19563
21991
|
let parsed;
|
|
19564
21992
|
try {
|
|
19565
|
-
parsed = JSON.parse((0,
|
|
21993
|
+
parsed = JSON.parse((0, import_node_fs39.readFileSync)(path2, "utf8"));
|
|
19566
21994
|
} catch {
|
|
19567
21995
|
throw new Error(`secrets: cannot read or parse '${path2}'`);
|
|
19568
21996
|
}
|
|
@@ -19570,7 +21998,7 @@ function readRawConfig(path2) {
|
|
|
19570
21998
|
}
|
|
19571
21999
|
function readRawJson(path2) {
|
|
19572
22000
|
try {
|
|
19573
|
-
const parsed = JSON.parse((0,
|
|
22001
|
+
const parsed = JSON.parse((0, import_node_fs39.readFileSync)(path2, "utf8"));
|
|
19574
22002
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
19575
22003
|
return parsed;
|
|
19576
22004
|
}
|
|
@@ -19580,13 +22008,13 @@ function readRawJson(path2) {
|
|
|
19580
22008
|
}
|
|
19581
22009
|
function encryptTokensFileInPlace(configPath, box) {
|
|
19582
22010
|
const tokensPath = defaultTokensPath(configPath);
|
|
19583
|
-
if (!(0,
|
|
22011
|
+
if (!(0, import_node_fs39.existsSync)(tokensPath)) return;
|
|
19584
22012
|
const plain = decryptTokensFile(tokensPath, box);
|
|
19585
22013
|
writeTokensEncrypted(tokensPath, plain, box);
|
|
19586
22014
|
}
|
|
19587
22015
|
function rewriteIntegrationState(configPath, readBox, writeBox) {
|
|
19588
22016
|
const path2 = defaultIntegrationsPath(configPath);
|
|
19589
|
-
if (!(0,
|
|
22017
|
+
if (!(0, import_node_fs39.existsSync)(path2)) return;
|
|
19590
22018
|
const state = new IntegrationStateStore(path2, readBox).load();
|
|
19591
22019
|
new IntegrationStateStore(path2, writeBox).save(state);
|
|
19592
22020
|
}
|
|
@@ -19599,7 +22027,7 @@ function writeTokensEncrypted(tokensPath, plain, box) {
|
|
|
19599
22027
|
{ updatedAt: "", ...plain },
|
|
19600
22028
|
box
|
|
19601
22029
|
);
|
|
19602
|
-
(
|
|
22030
|
+
atomicReplaceUtf8(tokensPath, JSON.stringify(encrypted, null, 2) + "\n");
|
|
19603
22031
|
}
|
|
19604
22032
|
var TOKEN_FIELDS2 = {
|
|
19605
22033
|
claude: ["accessToken", "refreshToken"],
|
|
@@ -19622,7 +22050,7 @@ function walkTokens(raw, fn) {
|
|
|
19622
22050
|
return next;
|
|
19623
22051
|
}
|
|
19624
22052
|
function tokensSuffix(configPath) {
|
|
19625
|
-
return (0,
|
|
22053
|
+
return (0, import_node_fs39.existsSync)(defaultTokensPath(configPath)) ? " (+ tokens.json)" : "";
|
|
19626
22054
|
}
|
|
19627
22055
|
|
|
19628
22056
|
// src/commands/start.ts
|
|
@@ -19889,7 +22317,7 @@ async function main() {
|
|
|
19889
22317
|
process.exitCode = 1;
|
|
19890
22318
|
}
|
|
19891
22319
|
}
|
|
19892
|
-
main().catch((
|
|
19893
|
-
console.error(
|
|
22320
|
+
main().catch((err8) => {
|
|
22321
|
+
console.error(err8 instanceof Error ? err8.message : String(err8));
|
|
19894
22322
|
process.exitCode = 1;
|
|
19895
22323
|
});
|