@omnicross/daemon 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1707 -303
- package/dist/cli.js +1644 -229
- package/dist/index.cjs +1655 -293
- package/dist/index.d.cts +334 -28
- package/dist/index.d.ts +334 -28
- package/dist/index.js +1598 -230
- package/package.json +6 -6
package/dist/cli.cjs
CHANGED
|
@@ -1166,7 +1166,7 @@ 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");
|
|
@@ -1176,16 +1176,16 @@ var import_ApiKeyPoolService = require("@omnicross/core/completion/ApiKeyPoolSer
|
|
|
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_AccountAllowanceStore7 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1180
1180
|
var import_AccountAllowanceScheduling5 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
1181
|
-
var
|
|
1181
|
+
var import_upstreamFetch13 = 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_subscriptions9 = require("@omnicross/subscriptions");
|
|
1189
1189
|
|
|
1190
1190
|
// src/admin/accountsCodexOAuth.ts
|
|
1191
1191
|
var import_node_crypto3 = __toESM(require("crypto"), 1);
|
|
@@ -1292,8 +1292,83 @@ 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
|
+
|
|
1295
1370
|
// src/allowance/AccountAllowanceService.ts
|
|
1296
|
-
var
|
|
1371
|
+
var import_AccountAllowanceStore5 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1297
1372
|
var import_AccountAllowanceScheduling = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
1298
1373
|
|
|
1299
1374
|
// src/allowance/ClaudeAllowanceCollector.ts
|
|
@@ -1320,13 +1395,11 @@ function secondsUntil(instant, now) {
|
|
|
1320
1395
|
function windowFromPayload(id, payload, now) {
|
|
1321
1396
|
const usedPercent = finitePercent(payload?.utilization);
|
|
1322
1397
|
const resetsAt = isoInstant(payload?.resets_at);
|
|
1323
|
-
const isSonnet = id === "seven-day-sonnet";
|
|
1324
1398
|
const isFiveHour = id === "five-hour";
|
|
1325
1399
|
return {
|
|
1326
1400
|
id,
|
|
1327
|
-
label: isFiveHour ? "5 hours" :
|
|
1328
|
-
scope:
|
|
1329
|
-
modelFamily: isSonnet ? "sonnet" : void 0,
|
|
1401
|
+
label: isFiveHour ? "5 hours" : "7 days",
|
|
1402
|
+
scope: "all",
|
|
1330
1403
|
usedPercent,
|
|
1331
1404
|
windowMinutes: isFiveHour ? 5 * 60 : 7 * 24 * 60,
|
|
1332
1405
|
resetsAt,
|
|
@@ -1334,6 +1407,44 @@ function windowFromPayload(id, payload, now) {
|
|
|
1334
1407
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
1335
1408
|
};
|
|
1336
1409
|
}
|
|
1410
|
+
function limitEntryWindow(entries, kind) {
|
|
1411
|
+
const entry = entries.find((candidate) => candidate.kind === kind);
|
|
1412
|
+
if (!entry) return void 0;
|
|
1413
|
+
return { utilization: entry.percent, resets_at: entry.resets_at };
|
|
1414
|
+
}
|
|
1415
|
+
function slugifyDisplayName(name) {
|
|
1416
|
+
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1417
|
+
}
|
|
1418
|
+
function scopedWeeklyWindows(entries, now) {
|
|
1419
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1420
|
+
const windows = [];
|
|
1421
|
+
for (const entry of entries) {
|
|
1422
|
+
if (entry.kind !== "weekly_scoped") continue;
|
|
1423
|
+
const displayName = typeof entry.scope?.model?.display_name === "string" && entry.scope.model.display_name.trim() ? entry.scope.model.display_name.trim() : void 0;
|
|
1424
|
+
if (!displayName) continue;
|
|
1425
|
+
const slug = slugifyDisplayName(displayName);
|
|
1426
|
+
if (!slug || seen.has(slug)) continue;
|
|
1427
|
+
seen.add(slug);
|
|
1428
|
+
const usedPercent = finitePercent(entry.percent);
|
|
1429
|
+
const resetsAt = isoInstant(entry.resets_at);
|
|
1430
|
+
windows.push({
|
|
1431
|
+
id: `seven-day-${slug}`,
|
|
1432
|
+
label: `7 days \xB7 ${displayName}`,
|
|
1433
|
+
scope: "model-family",
|
|
1434
|
+
modelFamily: slug,
|
|
1435
|
+
usedPercent,
|
|
1436
|
+
windowMinutes: 7 * 24 * 60,
|
|
1437
|
+
resetsAt,
|
|
1438
|
+
remainingSeconds: secondsUntil(resetsAt, now),
|
|
1439
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
return windows;
|
|
1443
|
+
}
|
|
1444
|
+
function parseLimitEntries(raw) {
|
|
1445
|
+
if (!Array.isArray(raw)) return [];
|
|
1446
|
+
return raw.filter((entry) => !!entry && typeof entry === "object");
|
|
1447
|
+
}
|
|
1337
1448
|
function emptyClaudeWindows(state) {
|
|
1338
1449
|
return [
|
|
1339
1450
|
{
|
|
@@ -1351,15 +1462,6 @@ function emptyClaudeWindows(state) {
|
|
|
1351
1462
|
usedPercent: null,
|
|
1352
1463
|
windowMinutes: 7 * 24 * 60,
|
|
1353
1464
|
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
1465
|
}
|
|
1364
1466
|
];
|
|
1365
1467
|
}
|
|
@@ -1440,6 +1542,9 @@ var ClaudeAllowanceCollector = class {
|
|
|
1440
1542
|
}
|
|
1441
1543
|
const now = this.now();
|
|
1442
1544
|
const usage = payload;
|
|
1545
|
+
const limitEntries = parseLimitEntries(usage.limits);
|
|
1546
|
+
const fiveHour = usage.five_hour ?? limitEntryWindow(limitEntries, "session");
|
|
1547
|
+
const sevenDay = usage.seven_day ?? limitEntryWindow(limitEntries, "weekly_all");
|
|
1443
1548
|
const snapshot = {
|
|
1444
1549
|
providerId: "claude",
|
|
1445
1550
|
accountId,
|
|
@@ -1447,10 +1552,10 @@ var ClaudeAllowanceCollector = class {
|
|
|
1447
1552
|
observedAt: new Date(now).toISOString(),
|
|
1448
1553
|
expiresAt: new Date(now + CLAUDE_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1449
1554
|
windows: [
|
|
1450
|
-
windowFromPayload("five-hour",
|
|
1451
|
-
windowFromPayload("seven-day",
|
|
1452
|
-
|
|
1453
|
-
]
|
|
1555
|
+
windowFromPayload("five-hour", fiveHour, now),
|
|
1556
|
+
windowFromPayload("seven-day", sevenDay, now),
|
|
1557
|
+
...scopedWeeklyWindows(limitEntries, now)
|
|
1558
|
+
].slice(0, 8)
|
|
1454
1559
|
};
|
|
1455
1560
|
this.store.set(snapshot);
|
|
1456
1561
|
return snapshot;
|
|
@@ -1507,6 +1612,601 @@ var ClaudeAllowanceCollector = class {
|
|
|
1507
1612
|
}
|
|
1508
1613
|
};
|
|
1509
1614
|
|
|
1615
|
+
// src/allowance/CodexAllowanceCollector.ts
|
|
1616
|
+
var import_AccountAllowanceStore2 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1617
|
+
var import_upstreamFetch2 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
1618
|
+
var CODEX_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
1619
|
+
var CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
1620
|
+
var CODEX_CLI_USER_AGENT = "codex_cli_rs/0.144.5";
|
|
1621
|
+
function finiteNumber(value) {
|
|
1622
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
1623
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
1624
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
|
1625
|
+
}
|
|
1626
|
+
function finitePercent2(value) {
|
|
1627
|
+
const parsed = finiteNumber(value);
|
|
1628
|
+
return parsed !== null && parsed <= 100 ? parsed : null;
|
|
1629
|
+
}
|
|
1630
|
+
function epochMs(value) {
|
|
1631
|
+
return value > 1e11 ? value : value * 1e3;
|
|
1632
|
+
}
|
|
1633
|
+
function secondsUntil2(instant, now) {
|
|
1634
|
+
if (!instant) return void 0;
|
|
1635
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
1636
|
+
}
|
|
1637
|
+
function decodeJwtClaims(token) {
|
|
1638
|
+
const parts = token.split(".");
|
|
1639
|
+
if (parts.length !== 3) return void 0;
|
|
1640
|
+
try {
|
|
1641
|
+
const json2 = Buffer.from(parts[1], "base64url").toString("utf8");
|
|
1642
|
+
const parsed = JSON.parse(json2);
|
|
1643
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
1644
|
+
} catch {
|
|
1645
|
+
return void 0;
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
function chatgptAccountIdFromClaims(claims) {
|
|
1649
|
+
const auth = claims?.["https://api.openai.com/auth"];
|
|
1650
|
+
if (!auth || typeof auth !== "object") return void 0;
|
|
1651
|
+
const accountId = auth.chatgpt_account_id;
|
|
1652
|
+
return typeof accountId === "string" && accountId.trim() ? accountId.trim() : void 0;
|
|
1653
|
+
}
|
|
1654
|
+
function resolveCodexChatGptAccountId(tokens) {
|
|
1655
|
+
if (tokens.accountId?.trim()) return tokens.accountId.trim();
|
|
1656
|
+
if (tokens.idToken) {
|
|
1657
|
+
const fromIdToken = chatgptAccountIdFromClaims(decodeJwtClaims(tokens.idToken));
|
|
1658
|
+
if (fromIdToken) return fromIdToken;
|
|
1659
|
+
}
|
|
1660
|
+
if (tokens.accessToken) {
|
|
1661
|
+
return chatgptAccountIdFromClaims(decodeJwtClaims(tokens.accessToken));
|
|
1662
|
+
}
|
|
1663
|
+
return void 0;
|
|
1664
|
+
}
|
|
1665
|
+
function windowFromPayload2(id, payload, now) {
|
|
1666
|
+
const usedPercent = finitePercent2(payload?.used_percent);
|
|
1667
|
+
const resetAtSeconds = finiteNumber(payload?.reset_at);
|
|
1668
|
+
const resetAfterSeconds = finiteNumber(payload?.reset_after_seconds);
|
|
1669
|
+
const windowSeconds = finiteNumber(payload?.limit_window_seconds);
|
|
1670
|
+
const resetsAt = resetAtSeconds !== null && resetAtSeconds > 0 ? new Date(epochMs(resetAtSeconds)).toISOString() : resetAfterSeconds !== null && resetAfterSeconds > 0 ? new Date(now + resetAfterSeconds * 1e3).toISOString() : void 0;
|
|
1671
|
+
const windowMinutes = windowSeconds !== null && windowSeconds > 0 ? Math.round(windowSeconds / 60) : void 0;
|
|
1672
|
+
return {
|
|
1673
|
+
id,
|
|
1674
|
+
label: id === "primary" ? "Primary" : "Secondary",
|
|
1675
|
+
scope: "all",
|
|
1676
|
+
usedPercent,
|
|
1677
|
+
...windowMinutes !== void 0 ? { windowMinutes } : {},
|
|
1678
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
1679
|
+
remainingSeconds: secondsUntil2(resetsAt, now),
|
|
1680
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
var CodexAllowanceCollector = class {
|
|
1684
|
+
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) {
|
|
1685
|
+
this.credentials = credentials;
|
|
1686
|
+
this.store = store;
|
|
1687
|
+
this.fetchImpl = fetchImpl;
|
|
1688
|
+
this.now = now;
|
|
1689
|
+
}
|
|
1690
|
+
credentials;
|
|
1691
|
+
store;
|
|
1692
|
+
fetchImpl;
|
|
1693
|
+
now;
|
|
1694
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
1695
|
+
async collectMany(accounts, options = {}) {
|
|
1696
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
1697
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
1698
|
+
}
|
|
1699
|
+
collect(account, options = {}) {
|
|
1700
|
+
const now = this.now();
|
|
1701
|
+
const unsupported = account.tokens.authMethod !== "oauth";
|
|
1702
|
+
if (unsupported) {
|
|
1703
|
+
const existing = this.store.get("codex", account.id, now);
|
|
1704
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
1705
|
+
return Promise.resolve(existing);
|
|
1706
|
+
}
|
|
1707
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
1708
|
+
this.store.set(snapshot);
|
|
1709
|
+
return Promise.resolve(snapshot);
|
|
1710
|
+
}
|
|
1711
|
+
const cached = this.store.get("codex", account.id, now);
|
|
1712
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
1713
|
+
return Promise.resolve(cached);
|
|
1714
|
+
}
|
|
1715
|
+
const running = this.inFlight.get(account.id);
|
|
1716
|
+
if (running) return running;
|
|
1717
|
+
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));
|
|
1718
|
+
this.inFlight.set(account.id, promise);
|
|
1719
|
+
return promise;
|
|
1720
|
+
}
|
|
1721
|
+
/**
|
|
1722
|
+
* A response-header snapshot stays a valid cache hit only while fresh; an
|
|
1723
|
+
* active oauth-usage snapshot is honored on the same 5-minute cadence as
|
|
1724
|
+
* Claude's (the poll is cheap and quota is the scheduling input).
|
|
1725
|
+
*/
|
|
1726
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
1727
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
1728
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
1729
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
1730
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
1731
|
+
}
|
|
1732
|
+
async fetchAccount(accountId, tokens) {
|
|
1733
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("codex", accountId);
|
|
1734
|
+
if (!accessToken) {
|
|
1735
|
+
return this.failureSnapshot(accountId, "codex_usage_token_unavailable", this.now());
|
|
1736
|
+
}
|
|
1737
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
1738
|
+
if (response.status === 401) {
|
|
1739
|
+
const refreshed = await this.credentials.refreshAccountToken("codex", accountId);
|
|
1740
|
+
if (!refreshed) {
|
|
1741
|
+
return this.failureSnapshot(accountId, "codex_usage_unauthorized", this.now());
|
|
1742
|
+
}
|
|
1743
|
+
accessToken = await this.credentials.getAccessTokenForAccount("codex", accountId);
|
|
1744
|
+
if (!accessToken) {
|
|
1745
|
+
return this.failureSnapshot(accountId, "codex_usage_token_unavailable", this.now());
|
|
1746
|
+
}
|
|
1747
|
+
response = await this.request(accountId, accessToken, tokens);
|
|
1748
|
+
}
|
|
1749
|
+
if (response.status === 403) {
|
|
1750
|
+
const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "codex_usage_unsupported");
|
|
1751
|
+
this.store.set(snapshot2);
|
|
1752
|
+
return snapshot2;
|
|
1753
|
+
}
|
|
1754
|
+
if (!response.ok) {
|
|
1755
|
+
return this.failureSnapshot(accountId, "codex_usage_http_error", this.now());
|
|
1756
|
+
}
|
|
1757
|
+
let payload;
|
|
1758
|
+
try {
|
|
1759
|
+
payload = await response.json();
|
|
1760
|
+
} catch {
|
|
1761
|
+
return this.failureSnapshot(accountId, "codex_usage_invalid_response", this.now());
|
|
1762
|
+
}
|
|
1763
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
1764
|
+
return this.failureSnapshot(accountId, "codex_usage_invalid_response", this.now());
|
|
1765
|
+
}
|
|
1766
|
+
const now = this.now();
|
|
1767
|
+
const usage = payload.rate_limit;
|
|
1768
|
+
const previous = this.store.get("codex", accountId, now);
|
|
1769
|
+
const snapshot = {
|
|
1770
|
+
providerId: "codex",
|
|
1771
|
+
accountId,
|
|
1772
|
+
source: "oauth-usage-api",
|
|
1773
|
+
observedAt: new Date(now).toISOString(),
|
|
1774
|
+
expiresAt: new Date(now + CODEX_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1775
|
+
windows: [
|
|
1776
|
+
windowFromPayload2("primary", usage?.primary_window ?? void 0, now),
|
|
1777
|
+
windowFromPayload2("secondary", usage?.secondary_window ?? void 0, now)
|
|
1778
|
+
],
|
|
1779
|
+
// The wham payload has no ratio field; keep the passively-observed value.
|
|
1780
|
+
...previous?.primaryOverSecondaryLimitPercent !== void 0 ? { primaryOverSecondaryLimitPercent: previous.primaryOverSecondaryLimitPercent } : {}
|
|
1781
|
+
};
|
|
1782
|
+
this.store.set(snapshot);
|
|
1783
|
+
return snapshot;
|
|
1784
|
+
}
|
|
1785
|
+
request(accountId, accessToken, tokens) {
|
|
1786
|
+
const headers = {
|
|
1787
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1788
|
+
Accept: "application/json",
|
|
1789
|
+
"User-Agent": CODEX_CLI_USER_AGENT
|
|
1790
|
+
};
|
|
1791
|
+
const chatgptAccountId = resolveCodexChatGptAccountId(tokens);
|
|
1792
|
+
if (chatgptAccountId) headers["ChatGPT-Account-Id"] = chatgptAccountId;
|
|
1793
|
+
return this.fetchImpl(CODEX_USAGE_URL, {
|
|
1794
|
+
method: "GET",
|
|
1795
|
+
headers,
|
|
1796
|
+
signal: AbortSignal.timeout(15e3)
|
|
1797
|
+
}, accountId);
|
|
1798
|
+
}
|
|
1799
|
+
failureSnapshot(accountId, code, now) {
|
|
1800
|
+
const existing = this.store.get("codex", accountId, now);
|
|
1801
|
+
const snapshot = existing ? {
|
|
1802
|
+
...existing,
|
|
1803
|
+
expiresAt: new Date(now + CODEX_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1804
|
+
windows: existing.windows.map((window) => ({
|
|
1805
|
+
...window,
|
|
1806
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
1807
|
+
})),
|
|
1808
|
+
lastErrorCode: code
|
|
1809
|
+
} : {
|
|
1810
|
+
providerId: "codex",
|
|
1811
|
+
accountId,
|
|
1812
|
+
source: "oauth-usage-api",
|
|
1813
|
+
observedAt: new Date(now).toISOString(),
|
|
1814
|
+
expiresAt: new Date(now + CODEX_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1815
|
+
windows: [
|
|
1816
|
+
{ id: "primary", label: "Primary", scope: "all", usedPercent: null, state: "unavailable" },
|
|
1817
|
+
{ id: "secondary", label: "Secondary", scope: "all", usedPercent: null, state: "unavailable" }
|
|
1818
|
+
],
|
|
1819
|
+
lastErrorCode: code
|
|
1820
|
+
};
|
|
1821
|
+
this.store.set(snapshot);
|
|
1822
|
+
return snapshot;
|
|
1823
|
+
}
|
|
1824
|
+
unsupportedSnapshot(accountId, now, code = "codex_usage_unsupported_auth") {
|
|
1825
|
+
return {
|
|
1826
|
+
providerId: "codex",
|
|
1827
|
+
accountId,
|
|
1828
|
+
source: "oauth-usage-api",
|
|
1829
|
+
observedAt: new Date(now).toISOString(),
|
|
1830
|
+
windows: [
|
|
1831
|
+
{ id: "primary", label: "Primary", scope: "all", usedPercent: null, state: "unsupported" },
|
|
1832
|
+
{ id: "secondary", label: "Secondary", scope: "all", usedPercent: null, state: "unsupported" }
|
|
1833
|
+
],
|
|
1834
|
+
lastErrorCode: code
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
};
|
|
1838
|
+
|
|
1839
|
+
// src/allowance/KimiAllowanceCollector.ts
|
|
1840
|
+
var import_AccountAllowanceStore3 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1841
|
+
var import_upstreamFetch3 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
1842
|
+
var import_subscriptions3 = require("@omnicross/subscriptions");
|
|
1843
|
+
var KIMI_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
1844
|
+
var KIMI_USAGE_URL = "https://api.kimi.com/coding/v1/usages";
|
|
1845
|
+
function finiteNumber2(value) {
|
|
1846
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
1847
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
1848
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
1849
|
+
}
|
|
1850
|
+
function isRecord(value) {
|
|
1851
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1852
|
+
}
|
|
1853
|
+
function parseResetMs(row, nowMs) {
|
|
1854
|
+
for (const key of ["reset_at", "resetAt", "reset_time", "resetTime"]) {
|
|
1855
|
+
const value = row[key];
|
|
1856
|
+
if (typeof value === "string" && value.trim()) {
|
|
1857
|
+
const parsed = Date.parse(value);
|
|
1858
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
1859
|
+
}
|
|
1860
|
+
const numeric = finiteNumber2(value);
|
|
1861
|
+
if (numeric !== void 0 && numeric > 1e9) {
|
|
1862
|
+
return numeric > 1e12 ? numeric : numeric * 1e3;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
for (const key of ["reset_in", "resetIn", "ttl", "window"]) {
|
|
1866
|
+
const seconds = finiteNumber2(row[key]);
|
|
1867
|
+
if (seconds !== void 0) return nowMs + seconds * 1e3;
|
|
1868
|
+
}
|
|
1869
|
+
return void 0;
|
|
1870
|
+
}
|
|
1871
|
+
var MINUTE_MS = 6e4;
|
|
1872
|
+
var HOUR_MS = 36e5;
|
|
1873
|
+
var DAY_MS = 864e5;
|
|
1874
|
+
function canonicalWindow(durationMs) {
|
|
1875
|
+
if (durationMs === 5 * HOUR_MS) return { id: "five-hour", label: "5 hours", minutes: 300 };
|
|
1876
|
+
if (durationMs === 7 * DAY_MS) return { id: "seven-day", label: "7 days", minutes: 10080 };
|
|
1877
|
+
if (durationMs > 0 && durationMs % DAY_MS === 0) {
|
|
1878
|
+
const days = durationMs / DAY_MS;
|
|
1879
|
+
return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}`, minutes: Math.round(durationMs / MINUTE_MS) };
|
|
1880
|
+
}
|
|
1881
|
+
if (durationMs > 0 && durationMs % HOUR_MS === 0) {
|
|
1882
|
+
const hours = durationMs / HOUR_MS;
|
|
1883
|
+
return { id: `${hours}h`, label: `${hours} hour${hours === 1 ? "" : "s"}`, minutes: Math.round(durationMs / MINUTE_MS) };
|
|
1884
|
+
}
|
|
1885
|
+
return void 0;
|
|
1886
|
+
}
|
|
1887
|
+
function secondsUntil3(instant, now) {
|
|
1888
|
+
if (!instant) return void 0;
|
|
1889
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
1890
|
+
}
|
|
1891
|
+
function windowFromRow(row, fallback, now) {
|
|
1892
|
+
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;
|
|
1893
|
+
const resetsAt = row?.resetsAtMs !== void 0 ? new Date(row.resetsAtMs).toISOString() : void 0;
|
|
1894
|
+
return {
|
|
1895
|
+
id: fallback.id,
|
|
1896
|
+
label: fallback.label,
|
|
1897
|
+
scope: "all",
|
|
1898
|
+
usedPercent,
|
|
1899
|
+
windowMinutes: fallback.minutes,
|
|
1900
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
1901
|
+
remainingSeconds: secondsUntil3(resetsAt, now),
|
|
1902
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
function parseKimiUsagePayload(payload, now) {
|
|
1906
|
+
if (!isRecord(payload)) return [];
|
|
1907
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1908
|
+
const rowFrom = (data) => {
|
|
1909
|
+
const limit = finiteNumber2(data["limit"]);
|
|
1910
|
+
let used = finiteNumber2(data["used"]);
|
|
1911
|
+
const remaining = finiteNumber2(data["remaining"]);
|
|
1912
|
+
if (used === void 0 && remaining !== void 0 && limit !== void 0) {
|
|
1913
|
+
used = limit - remaining;
|
|
1914
|
+
}
|
|
1915
|
+
let windowDurationMs;
|
|
1916
|
+
const windowData = isRecord(data["window"]) ? data["window"] : void 0;
|
|
1917
|
+
const duration = finiteNumber2(windowData?.["duration"]);
|
|
1918
|
+
const timeUnit = typeof windowData?.["timeUnit"] === "string" ? windowData["timeUnit"].toUpperCase() : "";
|
|
1919
|
+
if (duration !== void 0) {
|
|
1920
|
+
if (timeUnit.includes("MINUTE")) windowDurationMs = duration * MINUTE_MS;
|
|
1921
|
+
else if (timeUnit.includes("HOUR")) windowDurationMs = duration * HOUR_MS;
|
|
1922
|
+
else if (timeUnit.includes("DAY")) windowDurationMs = duration * DAY_MS;
|
|
1923
|
+
else if (timeUnit.includes("WEEK")) windowDurationMs = duration * 7 * DAY_MS;
|
|
1924
|
+
else if (timeUnit.includes("SECOND")) windowDurationMs = duration * 1e3;
|
|
1925
|
+
}
|
|
1926
|
+
const resetsAtMs = parseResetMs(windowData && parseResetMs(windowData, now) !== void 0 ? windowData : data, now);
|
|
1927
|
+
return { used, limit, remaining, ...resetsAtMs !== void 0 ? { resetsAtMs } : {}, ...windowDurationMs !== void 0 ? { windowDurationMs } : {} };
|
|
1928
|
+
};
|
|
1929
|
+
if (isRecord(payload["usage"])) {
|
|
1930
|
+
const row = rowFrom(payload["usage"]);
|
|
1931
|
+
const window = windowFromRow({ ...row, resetsAtMs: row.resetsAtMs }, { id: "seven-day", label: "7 days", minutes: 10080 }, now);
|
|
1932
|
+
byId.set("seven-day", window);
|
|
1933
|
+
}
|
|
1934
|
+
if (Array.isArray(payload["limits"])) {
|
|
1935
|
+
for (const item of payload["limits"]) {
|
|
1936
|
+
if (!isRecord(item)) continue;
|
|
1937
|
+
const detail = isRecord(item["detail"]) ? item["detail"] : item;
|
|
1938
|
+
const row = rowFrom(detail);
|
|
1939
|
+
const canonical = row.windowDurationMs !== void 0 ? canonicalWindow(row.windowDurationMs) : void 0;
|
|
1940
|
+
if (!canonical) continue;
|
|
1941
|
+
const window = windowFromRow(row, canonical, now);
|
|
1942
|
+
const existing = byId.get(canonical.id);
|
|
1943
|
+
if (!existing || (window.usedPercent ?? 0) > (existing.usedPercent ?? 0)) {
|
|
1944
|
+
byId.set(canonical.id, window);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
return [...byId.values()].sort((a, b) => (a.windowMinutes ?? Infinity) - (b.windowMinutes ?? Infinity)).slice(0, 4);
|
|
1949
|
+
}
|
|
1950
|
+
var KimiAllowanceCollector = class {
|
|
1951
|
+
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) {
|
|
1952
|
+
this.credentials = credentials;
|
|
1953
|
+
this.store = store;
|
|
1954
|
+
this.fetchImpl = fetchImpl;
|
|
1955
|
+
this.now = now;
|
|
1956
|
+
}
|
|
1957
|
+
credentials;
|
|
1958
|
+
store;
|
|
1959
|
+
fetchImpl;
|
|
1960
|
+
now;
|
|
1961
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
1962
|
+
async collectMany(accounts, options = {}) {
|
|
1963
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
1964
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
1965
|
+
}
|
|
1966
|
+
collect(account, options = {}) {
|
|
1967
|
+
const now = this.now();
|
|
1968
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
1969
|
+
const existing = this.store.get("kimi", account.id, now);
|
|
1970
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
1971
|
+
return Promise.resolve(existing);
|
|
1972
|
+
}
|
|
1973
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
1974
|
+
this.store.set(snapshot);
|
|
1975
|
+
return Promise.resolve(snapshot);
|
|
1976
|
+
}
|
|
1977
|
+
const cached = this.store.get("kimi", account.id, now);
|
|
1978
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
1979
|
+
return Promise.resolve(cached);
|
|
1980
|
+
}
|
|
1981
|
+
const running = this.inFlight.get(account.id);
|
|
1982
|
+
if (running) return running;
|
|
1983
|
+
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));
|
|
1984
|
+
this.inFlight.set(account.id, promise);
|
|
1985
|
+
return promise;
|
|
1986
|
+
}
|
|
1987
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
1988
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
1989
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
1990
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
1991
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
1992
|
+
}
|
|
1993
|
+
async fetchAccount(accountId, tokens) {
|
|
1994
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
|
|
1995
|
+
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
1996
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
1997
|
+
if (response.status === 401) {
|
|
1998
|
+
const refreshed = await this.credentials.refreshAccountToken("kimi", accountId);
|
|
1999
|
+
if (!refreshed) return this.failureSnapshot(accountId, "kimi_usage_unauthorized", this.now());
|
|
2000
|
+
accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
|
|
2001
|
+
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
2002
|
+
response = await this.request(accountId, accessToken, tokens);
|
|
2003
|
+
}
|
|
2004
|
+
if (response.status === 403) {
|
|
2005
|
+
const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "kimi_usage_unsupported");
|
|
2006
|
+
this.store.set(snapshot2);
|
|
2007
|
+
return snapshot2;
|
|
2008
|
+
}
|
|
2009
|
+
if (!response.ok) return this.failureSnapshot(accountId, "kimi_usage_http_error", this.now());
|
|
2010
|
+
let payload;
|
|
2011
|
+
try {
|
|
2012
|
+
payload = await response.json();
|
|
2013
|
+
} catch {
|
|
2014
|
+
return this.failureSnapshot(accountId, "kimi_usage_invalid_response", this.now());
|
|
2015
|
+
}
|
|
2016
|
+
const now = this.now();
|
|
2017
|
+
const windows = parseKimiUsagePayload(payload, now);
|
|
2018
|
+
const snapshot = {
|
|
2019
|
+
providerId: "kimi",
|
|
2020
|
+
accountId,
|
|
2021
|
+
source: "oauth-usage-api",
|
|
2022
|
+
observedAt: new Date(now).toISOString(),
|
|
2023
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2024
|
+
windows: windows.length > 0 ? windows : [
|
|
2025
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2026
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2027
|
+
],
|
|
2028
|
+
...windows.length > 0 ? {} : { lastErrorCode: "kimi_usage_invalid_response" }
|
|
2029
|
+
};
|
|
2030
|
+
this.store.set(snapshot);
|
|
2031
|
+
return snapshot;
|
|
2032
|
+
}
|
|
2033
|
+
request(accountId, accessToken, tokens) {
|
|
2034
|
+
return this.fetchImpl(KIMI_USAGE_URL, {
|
|
2035
|
+
method: "GET",
|
|
2036
|
+
headers: {
|
|
2037
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2038
|
+
Accept: "application/json",
|
|
2039
|
+
...(0, import_subscriptions3.kimiFingerprintHeaders)(tokens.deviceId)
|
|
2040
|
+
},
|
|
2041
|
+
signal: AbortSignal.timeout(15e3)
|
|
2042
|
+
}, accountId);
|
|
2043
|
+
}
|
|
2044
|
+
failureSnapshot(accountId, code, now) {
|
|
2045
|
+
const existing = this.store.get("kimi", accountId, now);
|
|
2046
|
+
const snapshot = existing ? {
|
|
2047
|
+
...existing,
|
|
2048
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2049
|
+
windows: existing.windows.map((window) => ({
|
|
2050
|
+
...window,
|
|
2051
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2052
|
+
})),
|
|
2053
|
+
lastErrorCode: code
|
|
2054
|
+
} : {
|
|
2055
|
+
providerId: "kimi",
|
|
2056
|
+
accountId,
|
|
2057
|
+
source: "oauth-usage-api",
|
|
2058
|
+
observedAt: new Date(now).toISOString(),
|
|
2059
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2060
|
+
windows: [
|
|
2061
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2062
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2063
|
+
],
|
|
2064
|
+
lastErrorCode: code
|
|
2065
|
+
};
|
|
2066
|
+
this.store.set(snapshot);
|
|
2067
|
+
return snapshot;
|
|
2068
|
+
}
|
|
2069
|
+
unsupportedSnapshot(accountId, now, code = "kimi_usage_unsupported_auth") {
|
|
2070
|
+
return {
|
|
2071
|
+
providerId: "kimi",
|
|
2072
|
+
accountId,
|
|
2073
|
+
source: "oauth-usage-api",
|
|
2074
|
+
observedAt: new Date(now).toISOString(),
|
|
2075
|
+
windows: [
|
|
2076
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unsupported" },
|
|
2077
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2078
|
+
],
|
|
2079
|
+
lastErrorCode: code
|
|
2080
|
+
};
|
|
2081
|
+
}
|
|
2082
|
+
};
|
|
2083
|
+
|
|
2084
|
+
// src/allowance/OpenCodeGoAllowanceCollector.ts
|
|
2085
|
+
var import_AccountAllowanceStore4 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
2086
|
+
var import_upstreamFetch4 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
2087
|
+
var import_subscriptions4 = require("@omnicross/subscriptions");
|
|
2088
|
+
var OPENCODEGO_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2089
|
+
var OPENCODEGO_DEFAULT_GO_BASE = "https://opencode.ai/zen/go";
|
|
2090
|
+
function finitePercent3(value) {
|
|
2091
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
2092
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
2093
|
+
return Number.isFinite(parsed) && parsed >= 0 && parsed <= 100 ? parsed : null;
|
|
2094
|
+
}
|
|
2095
|
+
function isoInstant2(value) {
|
|
2096
|
+
if (typeof value !== "string" || !value.trim()) return void 0;
|
|
2097
|
+
const time = Date.parse(value);
|
|
2098
|
+
return Number.isFinite(time) ? new Date(time).toISOString() : void 0;
|
|
2099
|
+
}
|
|
2100
|
+
function secondsUntil4(instant, now) {
|
|
2101
|
+
if (!instant) return void 0;
|
|
2102
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2103
|
+
}
|
|
2104
|
+
function windowFromPayload3(id, label, minutes, payload, now) {
|
|
2105
|
+
const statusRateLimited = payload?.status === "rate-limited";
|
|
2106
|
+
const usedPercent = statusRateLimited ? 100 : finitePercent3(payload?.percent);
|
|
2107
|
+
const resetsAt = isoInstant2(payload?.resetsAt);
|
|
2108
|
+
return {
|
|
2109
|
+
id,
|
|
2110
|
+
label,
|
|
2111
|
+
scope: "all",
|
|
2112
|
+
usedPercent,
|
|
2113
|
+
windowMinutes: minutes,
|
|
2114
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2115
|
+
remainingSeconds: secondsUntil4(resetsAt, now),
|
|
2116
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
2119
|
+
var OpenCodeGoAllowanceCollector = class {
|
|
2120
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore4.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch4.fetchUpstream)(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
|
|
2121
|
+
this.credentials = credentials;
|
|
2122
|
+
this.store = store;
|
|
2123
|
+
this.fetchImpl = fetchImpl;
|
|
2124
|
+
this.now = now;
|
|
2125
|
+
}
|
|
2126
|
+
credentials;
|
|
2127
|
+
store;
|
|
2128
|
+
fetchImpl;
|
|
2129
|
+
now;
|
|
2130
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
2131
|
+
async collectMany(accounts, options = {}) {
|
|
2132
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
2133
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
2134
|
+
}
|
|
2135
|
+
collect(account, options = {}) {
|
|
2136
|
+
const now = this.now();
|
|
2137
|
+
const cached = this.store.get("opencodego", account.id, now);
|
|
2138
|
+
if (!options.force && cached && (cached.windows.every((window) => window.state === "unsupported") || cached.expiresAt && Date.parse(cached.expiresAt) > now + (options.refreshAheadMs ?? 0))) {
|
|
2139
|
+
return Promise.resolve(cached);
|
|
2140
|
+
}
|
|
2141
|
+
const running = this.inFlight.get(account.id);
|
|
2142
|
+
if (running) return running;
|
|
2143
|
+
const promise = this.fetchAccount(account).catch(() => this.failureSnapshot(account.id, this.now())).finally(() => this.inFlight.delete(account.id));
|
|
2144
|
+
this.inFlight.set(account.id, promise);
|
|
2145
|
+
return promise;
|
|
2146
|
+
}
|
|
2147
|
+
async fetchAccount(account) {
|
|
2148
|
+
const apiKey = await this.credentials.getAccessTokenForAccount("opencodego", account.id);
|
|
2149
|
+
if (!apiKey) return this.failureSnapshot(account.id, this.now());
|
|
2150
|
+
const base = account.tokens.baseUrl ? (0, import_subscriptions4.normalizeOpenCodeGoBaseUrl)(account.tokens.baseUrl) : OPENCODEGO_DEFAULT_GO_BASE;
|
|
2151
|
+
const response = await this.fetchImpl(`${base}/v1/usage`, {
|
|
2152
|
+
method: "GET",
|
|
2153
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
2154
|
+
signal: AbortSignal.timeout(15e3)
|
|
2155
|
+
}, account.id);
|
|
2156
|
+
if (response.status === 401 || response.status === 403) {
|
|
2157
|
+
return this.failureSnapshot(account.id, this.now(), "opencodego_usage_unauthorized");
|
|
2158
|
+
}
|
|
2159
|
+
if (!response.ok) return this.failureSnapshot(account.id, this.now());
|
|
2160
|
+
let payload;
|
|
2161
|
+
try {
|
|
2162
|
+
payload = await response.json();
|
|
2163
|
+
} catch {
|
|
2164
|
+
return this.failureSnapshot(account.id, this.now());
|
|
2165
|
+
}
|
|
2166
|
+
const usage = payload && typeof payload === "object" && !Array.isArray(payload) ? payload.usage : void 0;
|
|
2167
|
+
const now = this.now();
|
|
2168
|
+
const snapshot = {
|
|
2169
|
+
providerId: "opencodego",
|
|
2170
|
+
accountId: account.id,
|
|
2171
|
+
source: "oauth-usage-api",
|
|
2172
|
+
observedAt: new Date(now).toISOString(),
|
|
2173
|
+
expiresAt: new Date(now + OPENCODEGO_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2174
|
+
// Monthly deliberately omitted (module doc).
|
|
2175
|
+
windows: [
|
|
2176
|
+
windowFromPayload3("five-hour", "5 hours", 5 * 60, usage?.rolling ?? void 0, now),
|
|
2177
|
+
windowFromPayload3("seven-day", "7 days", 7 * 24 * 60, usage?.weekly ?? void 0, now)
|
|
2178
|
+
]
|
|
2179
|
+
};
|
|
2180
|
+
this.store.set(snapshot);
|
|
2181
|
+
return snapshot;
|
|
2182
|
+
}
|
|
2183
|
+
failureSnapshot(accountId, now, code = "opencodego_usage_request_failed") {
|
|
2184
|
+
const existing = this.store.get("opencodego", accountId, now);
|
|
2185
|
+
const snapshot = existing ? {
|
|
2186
|
+
...existing,
|
|
2187
|
+
expiresAt: new Date(now + OPENCODEGO_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2188
|
+
windows: existing.windows.map((window) => ({
|
|
2189
|
+
...window,
|
|
2190
|
+
state: window.usedPercent !== null || window.resetsAt ? "stale" : window.state
|
|
2191
|
+
})),
|
|
2192
|
+
lastErrorCode: code
|
|
2193
|
+
} : {
|
|
2194
|
+
providerId: "opencodego",
|
|
2195
|
+
accountId,
|
|
2196
|
+
source: "oauth-usage-api",
|
|
2197
|
+
observedAt: new Date(now).toISOString(),
|
|
2198
|
+
expiresAt: new Date(now + OPENCODEGO_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2199
|
+
windows: [
|
|
2200
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2201
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2202
|
+
],
|
|
2203
|
+
lastErrorCode: code
|
|
2204
|
+
};
|
|
2205
|
+
this.store.set(snapshot);
|
|
2206
|
+
return snapshot;
|
|
2207
|
+
}
|
|
2208
|
+
};
|
|
2209
|
+
|
|
1510
2210
|
// src/allowance/AccountAllowanceService.ts
|
|
1511
2211
|
function codexUnavailable(accountId, now) {
|
|
1512
2212
|
return {
|
|
@@ -1522,26 +2222,30 @@ function codexUnavailable(accountId, now) {
|
|
|
1522
2222
|
};
|
|
1523
2223
|
}
|
|
1524
2224
|
var AccountAllowanceService = class {
|
|
1525
|
-
constructor(credentials, store = (0,
|
|
2225
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore5.getSharedAccountAllowanceStore)(), collector, codexCollector, kimiCollector, opencodegoCollector, now = Date.now) {
|
|
1526
2226
|
this.credentials = credentials;
|
|
1527
2227
|
this.store = store;
|
|
1528
2228
|
this.now = now;
|
|
1529
2229
|
this.claudeCollector = collector ?? new ClaudeAllowanceCollector(credentials, store);
|
|
2230
|
+
this.codexCollector = codexCollector ?? new CodexAllowanceCollector(credentials, store);
|
|
2231
|
+
this.kimiCollector = kimiCollector ?? new KimiAllowanceCollector(credentials, store);
|
|
2232
|
+
this.opencodegoCollector = opencodegoCollector ?? new OpenCodeGoAllowanceCollector(credentials, store);
|
|
1530
2233
|
}
|
|
1531
2234
|
credentials;
|
|
1532
2235
|
store;
|
|
1533
2236
|
now;
|
|
1534
2237
|
claudeCollector;
|
|
2238
|
+
codexCollector;
|
|
2239
|
+
kimiCollector;
|
|
2240
|
+
opencodegoCollector;
|
|
1535
2241
|
/**
|
|
1536
|
-
* Read all/filtered snapshots. Claude's five-minute
|
|
1537
|
-
*
|
|
2242
|
+
* Read all/filtered snapshots. Claude's and Codex's five-minute caches are
|
|
2243
|
+
* refreshed lazily on read (Codex polls `/backend-api/wham/usage`; the
|
|
2244
|
+
* passive `x-codex-*` header tap still feeds mid-flight updates).
|
|
1538
2245
|
*/
|
|
1539
2246
|
async list(filter = {}) {
|
|
1540
2247
|
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
|
-
]);
|
|
2248
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1545
2249
|
const wantsClaude = !filter.providerId || filter.providerId === "claude";
|
|
1546
2250
|
const claudeAccounts = (config.claudeAccounts ?? []).filter(
|
|
1547
2251
|
(account) => !filter.accountId || account.id === filter.accountId
|
|
@@ -1552,39 +2256,90 @@ var AccountAllowanceService = class {
|
|
|
1552
2256
|
(account) => !filter.accountId || account.id === filter.accountId
|
|
1553
2257
|
);
|
|
1554
2258
|
if (wantsCodex) {
|
|
2259
|
+
await this.codexCollector.collectMany(codexAccounts);
|
|
1555
2260
|
for (const account of codexAccounts) {
|
|
1556
2261
|
if (!this.store.get("codex", account.id)) this.store.set(codexUnavailable(account.id, this.now()));
|
|
1557
2262
|
}
|
|
1558
2263
|
}
|
|
2264
|
+
const wantsKimi = !filter.providerId || filter.providerId === "kimi";
|
|
2265
|
+
const kimiAccounts = (config.kimiAccounts ?? []).filter(
|
|
2266
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
2267
|
+
);
|
|
2268
|
+
if (wantsKimi) await this.kimiCollector.collectMany(kimiAccounts);
|
|
2269
|
+
const wantsOpenCodeGo = !filter.providerId || filter.providerId === "opencodego";
|
|
2270
|
+
const opencodegoAccounts = (config.opencodegoAccounts ?? []).filter(
|
|
2271
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
2272
|
+
);
|
|
2273
|
+
if (wantsOpenCodeGo) await this.opencodegoCollector.collectMany(opencodegoAccounts);
|
|
1559
2274
|
const known = /* @__PURE__ */ new Set();
|
|
1560
2275
|
if (wantsClaude) for (const account of claudeAccounts) known.add(`claude\0${account.id}`);
|
|
1561
2276
|
if (wantsCodex) for (const account of codexAccounts) known.add(`codex\0${account.id}`);
|
|
2277
|
+
if (wantsKimi) for (const account of kimiAccounts) known.add(`kimi\0${account.id}`);
|
|
2278
|
+
if (wantsOpenCodeGo) for (const account of opencodegoAccounts) known.add(`opencodego\0${account.id}`);
|
|
1562
2279
|
return this.store.list(filter).filter((snapshot) => known.has(`${snapshot.providerId}\0${snapshot.accountId}`));
|
|
1563
2280
|
}
|
|
2281
|
+
knownAccounts(config) {
|
|
2282
|
+
return [
|
|
2283
|
+
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
2284
|
+
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id })),
|
|
2285
|
+
...(config.kimiAccounts ?? []).map((account) => ({ providerId: "kimi", accountId: account.id })),
|
|
2286
|
+
...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id }))
|
|
2287
|
+
];
|
|
2288
|
+
}
|
|
1564
2289
|
/** Force-refresh Claude usage for one account or every stored Claude account. */
|
|
1565
2290
|
async refreshClaude(accountId) {
|
|
1566
2291
|
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
|
-
]);
|
|
2292
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1571
2293
|
const accounts = (config.claudeAccounts ?? []).filter(
|
|
1572
2294
|
(account) => !accountId || account.id === accountId
|
|
1573
2295
|
);
|
|
1574
2296
|
return this.claudeCollector.collectMany(accounts, { force: true });
|
|
1575
2297
|
}
|
|
1576
2298
|
/**
|
|
1577
|
-
*
|
|
1578
|
-
*
|
|
1579
|
-
*
|
|
2299
|
+
* Force-refresh Codex usage (`/backend-api/wham/usage`) for one account or
|
|
2300
|
+
* every stored Codex account. Replaces the old probe-request workaround —
|
|
2301
|
+
* no quota is spent reading the usage endpoint.
|
|
2302
|
+
*/
|
|
2303
|
+
async refreshCodex(accountId) {
|
|
2304
|
+
const config = await this.credentials.getFullConfig();
|
|
2305
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
2306
|
+
const accounts = (config.codexAccounts ?? []).filter(
|
|
2307
|
+
(account) => !accountId || account.id === accountId
|
|
2308
|
+
);
|
|
2309
|
+
return this.codexCollector.collectMany(accounts, { force: true });
|
|
2310
|
+
}
|
|
2311
|
+
/** Force-refresh OpenCodeGo usage (`{go}/v1/usage`) for one/all accounts. */
|
|
2312
|
+
async refreshOpenCodeGo(accountId) {
|
|
2313
|
+
const config = await this.credentials.getFullConfig();
|
|
2314
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
2315
|
+
const accounts = (config.opencodegoAccounts ?? []).filter(
|
|
2316
|
+
(account) => !accountId || account.id === accountId
|
|
2317
|
+
);
|
|
2318
|
+
return this.opencodegoCollector.collectMany(accounts, { force: true });
|
|
2319
|
+
}
|
|
2320
|
+
/** Force-refresh Kimi usage (`/coding/v1/usages`) for one/all accounts. */
|
|
2321
|
+
async refreshKimi(accountId) {
|
|
2322
|
+
const config = await this.credentials.getFullConfig();
|
|
2323
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
2324
|
+
const accounts = (config.kimiAccounts ?? []).filter(
|
|
2325
|
+
(account) => !accountId || account.id === accountId
|
|
2326
|
+
);
|
|
2327
|
+
return this.kimiCollector.collectMany(accounts, { force: true });
|
|
2328
|
+
}
|
|
2329
|
+
/**
|
|
2330
|
+
* Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
|
|
2331
|
+
* collectors preserve their cache + per-account in-flight coalescing; a tick
|
|
2332
|
+
* normally performs no network I/O. (Codex joined the warm path when it
|
|
2333
|
+
* gained an active `/wham/usage` collector — the passive `x-codex-*` header
|
|
2334
|
+
* tap alone could not keep the policy fed while idle.)
|
|
1580
2335
|
*/
|
|
1581
2336
|
async maintainClaudeCache(refreshAheadMs) {
|
|
1582
2337
|
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
|
-
]);
|
|
2338
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1587
2339
|
await this.claudeCollector.collectMany(config.claudeAccounts ?? [], { refreshAheadMs });
|
|
2340
|
+
await this.codexCollector.collectMany(config.codexAccounts ?? [], { refreshAheadMs });
|
|
2341
|
+
await this.kimiCollector.collectMany(config.kimiAccounts ?? [], { refreshAheadMs });
|
|
2342
|
+
await this.opencodegoCollector.collectMany(config.opencodegoAccounts ?? [], { refreshAheadMs });
|
|
1588
2343
|
}
|
|
1589
2344
|
/** Remove a cache row as soon as an account is deleted by the admin path. */
|
|
1590
2345
|
removeAccountSnapshot(providerId, accountId) {
|
|
@@ -1679,7 +2434,7 @@ var ClaudeAllowanceRefreshScheduler = class {
|
|
|
1679
2434
|
var import_node_crypto4 = require("crypto");
|
|
1680
2435
|
var import_node_fs6 = require("fs");
|
|
1681
2436
|
var import_node_path6 = require("path");
|
|
1682
|
-
var
|
|
2437
|
+
var import_AccountAllowanceStore6 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1683
2438
|
var ACCOUNT_ALLOWANCE_CACHE_VERSION = 1;
|
|
1684
2439
|
var MAX_PERSISTED_ALLOWANCE_SNAPSHOTS = 256;
|
|
1685
2440
|
var MAX_ALLOWANCE_CACHE_BYTES = 1e6;
|
|
@@ -1708,7 +2463,7 @@ var JsonAccountAllowancePersistence = class {
|
|
|
1708
2463
|
save(snapshots) {
|
|
1709
2464
|
const rows = [];
|
|
1710
2465
|
for (const snapshot of snapshots) {
|
|
1711
|
-
const normalized2 = (0,
|
|
2466
|
+
const normalized2 = (0, import_AccountAllowanceStore6.normalizeAccountAllowanceSnapshot)(snapshot);
|
|
1712
2467
|
if (!normalized2) continue;
|
|
1713
2468
|
rows.push(normalized2);
|
|
1714
2469
|
if (rows.length >= MAX_PERSISTED_ALLOWANCE_SNAPSHOTS) break;
|
|
@@ -1984,7 +2739,7 @@ var import_outbound_api5 = require("@omnicross/core/outbound-api");
|
|
|
1984
2739
|
var import_image_generation_types = require("@omnicross/contracts/image-generation-types");
|
|
1985
2740
|
var import_AccountAllowanceScheduling2 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
1986
2741
|
var import_SubscriptionAccountHealth = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
1987
|
-
var
|
|
2742
|
+
var import_upstreamFetch7 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
1988
2743
|
|
|
1989
2744
|
// src/image-generation/imagesConfigValidation.ts
|
|
1990
2745
|
var import_outbound_api = require("@omnicross/core/outbound-api");
|
|
@@ -3617,11 +4372,11 @@ function preserveOutboundProxySecrets(incoming, current) {
|
|
|
3617
4372
|
}
|
|
3618
4373
|
|
|
3619
4374
|
// src/proxy/upstreamProxyResolver.ts
|
|
3620
|
-
var
|
|
4375
|
+
var import_upstreamFetch5 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
3621
4376
|
var serverProxy;
|
|
3622
4377
|
function setServerProxyConfig(proxy) {
|
|
3623
4378
|
serverProxy = proxy;
|
|
3624
|
-
(0,
|
|
4379
|
+
(0, import_upstreamFetch5.bumpUpstreamProxyGeneration)();
|
|
3625
4380
|
}
|
|
3626
4381
|
function getServerProxyConfig() {
|
|
3627
4382
|
return serverProxy;
|
|
@@ -3689,14 +4444,15 @@ function createUpstreamProxyResolver(src = {}) {
|
|
|
3689
4444
|
}
|
|
3690
4445
|
|
|
3691
4446
|
// src/admin/accountsOAuth.ts
|
|
3692
|
-
var
|
|
4447
|
+
var import_subscriptions5 = require("@omnicross/subscriptions");
|
|
3693
4448
|
|
|
3694
4449
|
// src/admin/accountsWrite.ts
|
|
3695
4450
|
var VALID_PROVIDER_IDS = [
|
|
3696
4451
|
"claude",
|
|
3697
4452
|
"codex",
|
|
3698
4453
|
"gemini",
|
|
3699
|
-
"opencodego"
|
|
4454
|
+
"opencodego",
|
|
4455
|
+
"kimi"
|
|
3700
4456
|
];
|
|
3701
4457
|
function asSubscriptionProviderId(id) {
|
|
3702
4458
|
return VALID_PROVIDER_IDS.includes(id) ? id : null;
|
|
@@ -3820,7 +4576,19 @@ function validateCodex(body) {
|
|
|
3820
4576
|
]);
|
|
3821
4577
|
return out;
|
|
3822
4578
|
}
|
|
3823
|
-
function validateGemini(body) {
|
|
4579
|
+
function validateGemini(body) {
|
|
4580
|
+
const authMethod = str(body["authMethod"]);
|
|
4581
|
+
const status = str(body["status"]);
|
|
4582
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
4583
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
4584
|
+
const out = {
|
|
4585
|
+
authMethod,
|
|
4586
|
+
status
|
|
4587
|
+
};
|
|
4588
|
+
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "lastRefreshedAt", "errorMessage"]);
|
|
4589
|
+
return out;
|
|
4590
|
+
}
|
|
4591
|
+
function validateKimi(body) {
|
|
3824
4592
|
const authMethod = str(body["authMethod"]);
|
|
3825
4593
|
const status = str(body["status"]);
|
|
3826
4594
|
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
@@ -3829,7 +4597,7 @@ function validateGemini(body) {
|
|
|
3829
4597
|
authMethod,
|
|
3830
4598
|
status
|
|
3831
4599
|
};
|
|
3832
|
-
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "lastRefreshedAt", "errorMessage"]);
|
|
4600
|
+
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "deviceId", "lastRefreshedAt", "errorMessage"]);
|
|
3833
4601
|
return out;
|
|
3834
4602
|
}
|
|
3835
4603
|
function validateOpenCodeGo(body) {
|
|
@@ -3867,6 +4635,8 @@ function validateTokenBody(providerId, body) {
|
|
|
3867
4635
|
return validateGemini(body);
|
|
3868
4636
|
case "opencodego":
|
|
3869
4637
|
return validateOpenCodeGo(body);
|
|
4638
|
+
case "kimi":
|
|
4639
|
+
return validateKimi(body);
|
|
3870
4640
|
default:
|
|
3871
4641
|
return null;
|
|
3872
4642
|
}
|
|
@@ -3896,37 +4666,37 @@ async function statusEntryFor(reader, providerId) {
|
|
|
3896
4666
|
|
|
3897
4667
|
// src/admin/accountsOAuth.ts
|
|
3898
4668
|
var OAUTH_HTTP_PROVIDERS = /* @__PURE__ */ new Set(["claude", "gemini"]);
|
|
3899
|
-
function
|
|
4669
|
+
function err3(status, message) {
|
|
3900
4670
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
3901
4671
|
}
|
|
3902
4672
|
function handleOAuthStart(providerId, deps) {
|
|
3903
4673
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
3904
|
-
return
|
|
4674
|
+
return err3(400, `oauth not available for provider '${providerId}'`);
|
|
3905
4675
|
}
|
|
3906
|
-
const flow = providerId === "claude" ?
|
|
4676
|
+
const flow = providerId === "claude" ? import_subscriptions5.claudeOAuth : import_subscriptions5.geminiOAuth;
|
|
3907
4677
|
const { authUrl, codeVerifier, state } = flow.generateAuthParams();
|
|
3908
4678
|
const sessionId = deps.oauthSessions.put({ providerId, codeVerifier, state });
|
|
3909
4679
|
return { status: 200, body: { authUrl, sessionId } };
|
|
3910
4680
|
}
|
|
3911
4681
|
async function handleOAuthComplete(providerId, body, deps) {
|
|
3912
4682
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
3913
|
-
return
|
|
4683
|
+
return err3(400, `oauth not available for provider '${providerId}'`);
|
|
3914
4684
|
}
|
|
3915
4685
|
const sessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : "";
|
|
3916
4686
|
const rawCode = typeof body["code"] === "string" ? body["code"] : "";
|
|
3917
|
-
if (!sessionId) return
|
|
3918
|
-
if (!rawCode) return
|
|
4687
|
+
if (!sessionId) return err3(400, "oauth complete requires { sessionId }");
|
|
4688
|
+
if (!rawCode) return err3(400, "oauth complete requires { code }");
|
|
3919
4689
|
const session = deps.oauthSessions.peek(sessionId);
|
|
3920
|
-
if (!session) return
|
|
4690
|
+
if (!session) return err3(410, "oauth session is unknown, expired, or already used");
|
|
3921
4691
|
if (session.providerId !== providerId) {
|
|
3922
|
-
return
|
|
4692
|
+
return err3(400, `oauth session does not match provider '${providerId}'`);
|
|
3923
4693
|
}
|
|
3924
4694
|
let code = rawCode.trim();
|
|
3925
4695
|
if (providerId === "claude") {
|
|
3926
4696
|
const [splitCode, pastedState] = code.split("#");
|
|
3927
|
-
if (!splitCode) return
|
|
4697
|
+
if (!splitCode) return err3(400, "no authorization code was provided");
|
|
3928
4698
|
if (pastedState && pastedState !== session.state) {
|
|
3929
|
-
return
|
|
4699
|
+
return err3(400, "oauth state did not match (possible CSRF) \u2014 aborting");
|
|
3930
4700
|
}
|
|
3931
4701
|
code = splitCode;
|
|
3932
4702
|
}
|
|
@@ -3936,7 +4706,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
3936
4706
|
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
|
|
3937
4707
|
} catch (exchangeError) {
|
|
3938
4708
|
const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
|
|
3939
|
-
return
|
|
4709
|
+
return err3(502, `oauth token exchange failed for '${providerId}': ${reason}`);
|
|
3940
4710
|
}
|
|
3941
4711
|
deps.oauthSessions.consume(sessionId);
|
|
3942
4712
|
const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
|
|
@@ -3945,7 +4715,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
3945
4715
|
return { status: 200, body: status ? { account: status } : { ok: true } };
|
|
3946
4716
|
}
|
|
3947
4717
|
async function exchangeClaude(code, codeVerifier, state, exchangeFetch) {
|
|
3948
|
-
const result = await
|
|
4718
|
+
const result = await import_subscriptions5.claudeOAuth.exchangeCodeForTokens(
|
|
3949
4719
|
{ authorizationCode: code, codeVerifier, state },
|
|
3950
4720
|
exchangeFetch
|
|
3951
4721
|
);
|
|
@@ -3961,7 +4731,7 @@ async function exchangeClaude(code, codeVerifier, state, exchangeFetch) {
|
|
|
3961
4731
|
};
|
|
3962
4732
|
}
|
|
3963
4733
|
async function exchangeGemini(code, codeVerifier, exchangeFetch) {
|
|
3964
|
-
const result = await
|
|
4734
|
+
const result = await import_subscriptions5.geminiOAuth.exchangeCodeForTokens(code, codeVerifier, exchangeFetch);
|
|
3965
4735
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
3966
4736
|
return {
|
|
3967
4737
|
authMethod: "oauth",
|
|
@@ -4266,8 +5036,8 @@ function errBody(message) {
|
|
|
4266
5036
|
return { error: { type: "admin_api_error", message } };
|
|
4267
5037
|
}
|
|
4268
5038
|
var defaultCommandRunner = (command) => new Promise((resolve11) => {
|
|
4269
|
-
(0, import_node_child_process.exec)(command, { timeout: 18e4 }, (
|
|
4270
|
-
if (
|
|
5039
|
+
(0, import_node_child_process.exec)(command, { timeout: 18e4 }, (err6, _stdout, stderr) => {
|
|
5040
|
+
if (err6) resolve11({ ok: false, error: stderr.trim() || err6.message });
|
|
4271
5041
|
else resolve11({ ok: true });
|
|
4272
5042
|
});
|
|
4273
5043
|
});
|
|
@@ -4313,8 +5083,8 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
4313
5083
|
providerId: typeof body["providerId"] === "string" ? body["providerId"] : void 0,
|
|
4314
5084
|
model: typeof body["model"] === "string" ? body["model"] : void 0
|
|
4315
5085
|
});
|
|
4316
|
-
} catch (
|
|
4317
|
-
return { status: 400, body: errBody(
|
|
5086
|
+
} catch (err6) {
|
|
5087
|
+
return { status: 400, body: errBody(err6 instanceof Error ? err6.message : "no launch target") };
|
|
4318
5088
|
}
|
|
4319
5089
|
const id = (0, import_node_crypto7.randomUUID)();
|
|
4320
5090
|
let leaseId2;
|
|
@@ -4342,9 +5112,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
4342
5112
|
} else {
|
|
4343
5113
|
launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
|
|
4344
5114
|
}
|
|
4345
|
-
} catch (
|
|
4346
|
-
const status =
|
|
4347
|
-
return { status, body: errBody(
|
|
5115
|
+
} catch (err6) {
|
|
5116
|
+
const status = err6 instanceof import_provider_proxy2.RouteLeaseError ? err6.status : 400;
|
|
5117
|
+
return { status, body: errBody(err6 instanceof Error ? err6.message : "failed to build launch env") };
|
|
4348
5118
|
}
|
|
4349
5119
|
const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
|
|
4350
5120
|
const opener = ctx.opener ?? defaultTerminalOpener;
|
|
@@ -4372,9 +5142,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
4372
5142
|
onFailure: onSessionEnd
|
|
4373
5143
|
});
|
|
4374
5144
|
if (cleanup) openerCleanup = cleanup;
|
|
4375
|
-
} catch (
|
|
5145
|
+
} catch (err6) {
|
|
4376
5146
|
onSessionEnd();
|
|
4377
|
-
return { status: 500, body: errBody(
|
|
5147
|
+
return { status: 500, body: errBody(err6 instanceof Error ? err6.message : "failed to open terminal") };
|
|
4378
5148
|
}
|
|
4379
5149
|
if (ended) {
|
|
4380
5150
|
openerCleanup?.();
|
|
@@ -4629,7 +5399,7 @@ async function runSearchLiveChecks(contributions, now = () => (/* @__PURE__ */ n
|
|
|
4629
5399
|
}
|
|
4630
5400
|
|
|
4631
5401
|
// src/search/SearchAssembly.ts
|
|
4632
|
-
var
|
|
5402
|
+
var import_upstreamFetch6 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
4633
5403
|
var import_search = require("@omnicross/core/search");
|
|
4634
5404
|
var import_api2 = require("@omnicross/core/search/api");
|
|
4635
5405
|
var import_http2 = require("@omnicross/core/search/http");
|
|
@@ -4647,7 +5417,7 @@ function searchPolicyFrom(config) {
|
|
|
4647
5417
|
};
|
|
4648
5418
|
}
|
|
4649
5419
|
function resolveSearchUpstreamDispatcher(url) {
|
|
4650
|
-
return (0,
|
|
5420
|
+
return (0, import_upstreamFetch6.resolveUpstreamDispatcher)({ url });
|
|
4651
5421
|
}
|
|
4652
5422
|
var searchUpstreamProxyConfig = createUpstreamProxyResolver();
|
|
4653
5423
|
function resolveSearchUpstreamProxyConfig(url) {
|
|
@@ -4929,7 +5699,7 @@ async function handleSearchQuery(req, res, deps) {
|
|
|
4929
5699
|
// src/admin/searchAdminView.ts
|
|
4930
5700
|
var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
|
|
4931
5701
|
var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
|
|
4932
|
-
function
|
|
5702
|
+
function isRecord2(value) {
|
|
4933
5703
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4934
5704
|
}
|
|
4935
5705
|
function redactSearchServerConfig(search) {
|
|
@@ -4979,13 +5749,13 @@ function resolveSecretField(entry, field, stored) {
|
|
|
4979
5749
|
else delete entry[field];
|
|
4980
5750
|
}
|
|
4981
5751
|
function preserveSearchSecrets(incoming, current) {
|
|
4982
|
-
if (!
|
|
5752
|
+
if (!isRecord2(incoming)) return incoming;
|
|
4983
5753
|
const section = { ...incoming };
|
|
4984
5754
|
const providersValue = section["providers"];
|
|
4985
|
-
if (!
|
|
5755
|
+
if (!isRecord2(providersValue)) return section;
|
|
4986
5756
|
const providers = {};
|
|
4987
5757
|
for (const [id, entryValue] of Object.entries(providersValue)) {
|
|
4988
|
-
if (!
|
|
5758
|
+
if (!isRecord2(entryValue)) {
|
|
4989
5759
|
providers[id] = entryValue;
|
|
4990
5760
|
continue;
|
|
4991
5761
|
}
|
|
@@ -5063,7 +5833,7 @@ function parseKeyPolicyBody(body) {
|
|
|
5063
5833
|
var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
|
|
5064
5834
|
var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
|
|
5065
5835
|
var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
|
|
5066
|
-
function
|
|
5836
|
+
function isRecord3(value) {
|
|
5067
5837
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
5068
5838
|
}
|
|
5069
5839
|
function nonBlank(value) {
|
|
@@ -5083,7 +5853,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5083
5853
|
const ids = /* @__PURE__ */ new Set();
|
|
5084
5854
|
raw.forEach((entry, index) => {
|
|
5085
5855
|
const path2 = `bindings[${index}]`;
|
|
5086
|
-
if (!
|
|
5856
|
+
if (!isRecord3(entry)) {
|
|
5087
5857
|
errors.push(`${path2} must be an object`);
|
|
5088
5858
|
return;
|
|
5089
5859
|
}
|
|
@@ -5112,12 +5882,12 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5112
5882
|
} else if (entry.modelMappings.length > 100) {
|
|
5113
5883
|
errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
|
|
5114
5884
|
} else if (entry.modelMappings.some(
|
|
5115
|
-
(mapping) => !
|
|
5885
|
+
(mapping) => !isRecord3(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
|
|
5116
5886
|
)) {
|
|
5117
5887
|
errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
|
|
5118
5888
|
}
|
|
5119
5889
|
}
|
|
5120
|
-
if (!
|
|
5890
|
+
if (!isRecord3(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
|
|
5121
5891
|
errors.push(`${path2}.target is invalid`);
|
|
5122
5892
|
} else {
|
|
5123
5893
|
if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
|
|
@@ -5132,7 +5902,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5132
5902
|
}
|
|
5133
5903
|
}
|
|
5134
5904
|
if (entry.modelMap !== void 0) {
|
|
5135
|
-
if (!
|
|
5905
|
+
if (!isRecord3(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
|
|
5136
5906
|
errors.push(`${path2}.modelMap must contain string values`);
|
|
5137
5907
|
}
|
|
5138
5908
|
}
|
|
@@ -5414,7 +6184,8 @@ var PROVIDER_KEYS = {
|
|
|
5414
6184
|
block: "opencodego",
|
|
5415
6185
|
accounts: "opencodegoAccounts",
|
|
5416
6186
|
active: "activeOpencodegoAccountId"
|
|
5417
|
-
}
|
|
6187
|
+
},
|
|
6188
|
+
kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" }
|
|
5418
6189
|
};
|
|
5419
6190
|
function clone(value) {
|
|
5420
6191
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -5936,7 +6707,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
|
|
|
5936
6707
|
}
|
|
5937
6708
|
|
|
5938
6709
|
// src/admin/adminMigration.ts
|
|
5939
|
-
function
|
|
6710
|
+
function err4(status, message) {
|
|
5940
6711
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
5941
6712
|
}
|
|
5942
6713
|
async function handleExport(body, deps) {
|
|
@@ -5946,30 +6717,30 @@ async function handleExport(body, deps) {
|
|
|
5946
6717
|
return { status: 200, body: { pack, version: BUNDLE_VERSION } };
|
|
5947
6718
|
} catch (error) {
|
|
5948
6719
|
if (error instanceof WeakPassphraseError) {
|
|
5949
|
-
return
|
|
6720
|
+
return err4(400, error.message);
|
|
5950
6721
|
}
|
|
5951
|
-
return
|
|
6722
|
+
return err4(500, "failed to build the migration pack");
|
|
5952
6723
|
}
|
|
5953
6724
|
}
|
|
5954
6725
|
async function handleImport(body, deps) {
|
|
5955
6726
|
const blob = typeof body["blob"] === "string" ? body["blob"] : "";
|
|
5956
6727
|
const passphrase = typeof body["passphrase"] === "string" ? body["passphrase"] : "";
|
|
5957
6728
|
const mode = body["mode"] === "overwrite" ? "overwrite" : "merge";
|
|
5958
|
-
if (!blob) return
|
|
6729
|
+
if (!blob) return err4(400, "import requires { blob }");
|
|
5959
6730
|
try {
|
|
5960
6731
|
const counts = await applyImport(blob, passphrase, mode, deps, deps.parseProviderInput);
|
|
5961
6732
|
return { status: 200, body: counts };
|
|
5962
6733
|
} catch (error) {
|
|
5963
6734
|
if (error instanceof WeakPassphraseError) {
|
|
5964
|
-
return
|
|
6735
|
+
return err4(400, error.message);
|
|
5965
6736
|
}
|
|
5966
|
-
return
|
|
6737
|
+
return err4(400, error instanceof Error ? error.message : "import failed");
|
|
5967
6738
|
}
|
|
5968
6739
|
}
|
|
5969
6740
|
|
|
5970
6741
|
// src/admin/usagePricing.ts
|
|
5971
6742
|
var import_usage = require("@omnicross/core/usage");
|
|
5972
|
-
var
|
|
6743
|
+
var err5 = (status, message) => ({
|
|
5973
6744
|
status,
|
|
5974
6745
|
body: { error: { type: "admin_api_error", message } }
|
|
5975
6746
|
});
|
|
@@ -5982,7 +6753,7 @@ function parseRange(query2) {
|
|
|
5982
6753
|
const startTs = parseFiniteInt(query2.get("startTs"));
|
|
5983
6754
|
const endTs = parseFiniteInt(query2.get("endTs"));
|
|
5984
6755
|
if (startTs === null || endTs === null) {
|
|
5985
|
-
return
|
|
6756
|
+
return err5(400, "startTs and endTs are required finite-integer unix-millis query params");
|
|
5986
6757
|
}
|
|
5987
6758
|
return { startTs, endTs };
|
|
5988
6759
|
}
|
|
@@ -6007,14 +6778,14 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
6007
6778
|
case "timeseries": {
|
|
6008
6779
|
const bucket = query2.get("bucket");
|
|
6009
6780
|
if (bucket !== "hour" && bucket !== "day" && bucket !== "month") {
|
|
6010
|
-
return
|
|
6781
|
+
return err5(400, "bucket must be one of 'hour', 'day', 'month'");
|
|
6011
6782
|
}
|
|
6012
6783
|
const now = Date.now();
|
|
6013
6784
|
const clamped = { startTs: range.startTs, endTs: Math.min(range.endTs, now) };
|
|
6014
6785
|
if (clamped.startTs < clamped.endTs) {
|
|
6015
6786
|
const projected = Math.ceil((clamped.endTs - clamped.startTs) / BUCKET_SPAN_MS[bucket]) + 1;
|
|
6016
6787
|
if (projected > MAX_TIMESERIES_BUCKETS) {
|
|
6017
|
-
return
|
|
6788
|
+
return err5(
|
|
6018
6789
|
400,
|
|
6019
6790
|
`requested range projects ~${projected} '${bucket}' buckets (max ${MAX_TIMESERIES_BUCKETS}); narrow the range or use a coarser bucket`
|
|
6020
6791
|
);
|
|
@@ -6037,7 +6808,7 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
6037
6808
|
};
|
|
6038
6809
|
}
|
|
6039
6810
|
default:
|
|
6040
|
-
return
|
|
6811
|
+
return err5(404, `unknown usage view '${view ?? ""}'`);
|
|
6041
6812
|
}
|
|
6042
6813
|
}
|
|
6043
6814
|
function poolKeyLabels(cfg) {
|
|
@@ -6086,7 +6857,7 @@ async function handlePricingList(deps) {
|
|
|
6086
6857
|
async function handlePricingUpsert(body, deps) {
|
|
6087
6858
|
const input = parsePricingEntryInput(body);
|
|
6088
6859
|
if (!input) {
|
|
6089
|
-
return
|
|
6860
|
+
return err5(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
|
|
6090
6861
|
}
|
|
6091
6862
|
const entry = await deps.pricingEngine.upsertManual(input);
|
|
6092
6863
|
return { status: 200, body: { entry } };
|
|
@@ -6095,7 +6866,7 @@ async function handlePricingDelete(query2, deps) {
|
|
|
6095
6866
|
const providerId = query2.get("providerId")?.trim() ?? "";
|
|
6096
6867
|
const modelId = query2.get("modelId")?.trim() ?? "";
|
|
6097
6868
|
if (!providerId || !modelId) {
|
|
6098
|
-
return
|
|
6869
|
+
return err5(400, "delete requires providerId and modelId query params");
|
|
6099
6870
|
}
|
|
6100
6871
|
const deleted = await deps.pricingStore.delete(providerId, modelId);
|
|
6101
6872
|
if (deleted) await deps.pricingEngine.invalidateCache();
|
|
@@ -6115,13 +6886,13 @@ async function handlePricingFetchLatest(deps) {
|
|
|
6115
6886
|
}
|
|
6116
6887
|
};
|
|
6117
6888
|
} catch (e) {
|
|
6118
|
-
return
|
|
6889
|
+
return err5(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
6119
6890
|
}
|
|
6120
6891
|
}
|
|
6121
6892
|
async function handlePricingResolveConflicts(body, deps) {
|
|
6122
6893
|
const raw = body["resolutions"];
|
|
6123
6894
|
if (!Array.isArray(raw)) {
|
|
6124
|
-
return
|
|
6895
|
+
return err5(400, "resolve-conflicts requires { resolutions: [...] }");
|
|
6125
6896
|
}
|
|
6126
6897
|
const currentRows = await deps.pricingStore.getAll();
|
|
6127
6898
|
const userEditedKeys = new Set(
|
|
@@ -6131,21 +6902,21 @@ async function handlePricingResolveConflicts(body, deps) {
|
|
|
6131
6902
|
const pendingIncoming = /* @__PURE__ */ new Map();
|
|
6132
6903
|
let staleCount = 0;
|
|
6133
6904
|
for (const item of raw) {
|
|
6134
|
-
if (!item || typeof item !== "object") return
|
|
6905
|
+
if (!item || typeof item !== "object") return err5(400, "invalid resolution entry");
|
|
6135
6906
|
const r = item;
|
|
6136
6907
|
const action = r["action"];
|
|
6137
6908
|
if (action !== "overwrite" && action !== "skip") {
|
|
6138
|
-
return
|
|
6909
|
+
return err5(400, "resolution action must be 'overwrite' or 'skip'");
|
|
6139
6910
|
}
|
|
6140
6911
|
const providerId = typeof r["providerId"] === "string" && r["providerId"].trim() ? r["providerId"].trim() : "";
|
|
6141
6912
|
const modelId = typeof r["modelId"] === "string" && r["modelId"].trim() ? r["modelId"].trim() : "";
|
|
6142
6913
|
if (!providerId || !modelId) {
|
|
6143
|
-
return
|
|
6914
|
+
return err5(400, "each resolution requires top-level providerId and modelId");
|
|
6144
6915
|
}
|
|
6145
6916
|
const incoming = parsePricingEntryInput(r["incoming"]);
|
|
6146
|
-
if (!incoming) return
|
|
6917
|
+
if (!incoming) return err5(400, "each resolution must echo a valid incoming pricing entry");
|
|
6147
6918
|
if (incoming.providerId !== providerId || incoming.modelId !== modelId) {
|
|
6148
|
-
return
|
|
6919
|
+
return err5(400, "resolution providerId/modelId must match the echoed incoming entry");
|
|
6149
6920
|
}
|
|
6150
6921
|
const key = `${providerId}::${modelId}`;
|
|
6151
6922
|
if (action === "overwrite" && !userEditedKeys.has(key)) {
|
|
@@ -6190,7 +6961,7 @@ function query(req) {
|
|
|
6190
6961
|
}
|
|
6191
6962
|
function allowanceProvider(value) {
|
|
6192
6963
|
if (!value) return void 0;
|
|
6193
|
-
return value === "claude" || value === "codex" ? value : null;
|
|
6964
|
+
return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" ? value : null;
|
|
6194
6965
|
}
|
|
6195
6966
|
async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
6196
6967
|
if (!service) return writeError2(res, 501, "account allowance service is not available");
|
|
@@ -6204,7 +6975,9 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
6204
6975
|
const params = query(req);
|
|
6205
6976
|
const pathProvider = rest.length >= 2 ? rest[0] : null;
|
|
6206
6977
|
const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
|
|
6207
|
-
if (providerId === null)
|
|
6978
|
+
if (providerId === null) {
|
|
6979
|
+
return writeError2(res, 400, "providerId must be claude, codex, kimi, or opencodego");
|
|
6980
|
+
}
|
|
6208
6981
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
6209
6982
|
const allowances = await service.list({ providerId, accountId });
|
|
6210
6983
|
return writeJson3(res, 200, { allowances });
|
|
@@ -6214,10 +6987,37 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
6214
6987
|
const requestedProvider = allowanceProvider(
|
|
6215
6988
|
typeof body["providerId"] === "string" ? body["providerId"] : "claude"
|
|
6216
6989
|
);
|
|
6217
|
-
if (requestedProvider !== "claude") {
|
|
6218
|
-
return writeError2(res, 400, "only Claude allowances support explicit refresh");
|
|
6219
|
-
}
|
|
6220
6990
|
const accountId = typeof body["accountId"] === "string" && body["accountId"].trim() ? body["accountId"].trim() : void 0;
|
|
6991
|
+
if (requestedProvider === "codex") {
|
|
6992
|
+
if (!service.refreshCodex) {
|
|
6993
|
+
return writeError2(res, 501, "codex allowance refresh is not available");
|
|
6994
|
+
}
|
|
6995
|
+
const allowances2 = await service.refreshCodex(accountId);
|
|
6996
|
+
if (accountId && allowances2.length === 0) {
|
|
6997
|
+
return writeError2(res, 404, `Codex account '${accountId}' not found`);
|
|
6998
|
+
}
|
|
6999
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7000
|
+
}
|
|
7001
|
+
if (requestedProvider === "kimi") {
|
|
7002
|
+
if (!service.refreshKimi) {
|
|
7003
|
+
return writeError2(res, 501, "kimi allowance refresh is not available");
|
|
7004
|
+
}
|
|
7005
|
+
const allowances2 = await service.refreshKimi(accountId);
|
|
7006
|
+
if (accountId && allowances2.length === 0) {
|
|
7007
|
+
return writeError2(res, 404, `Kimi account '${accountId}' not found`);
|
|
7008
|
+
}
|
|
7009
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7010
|
+
}
|
|
7011
|
+
if (requestedProvider === "opencodego") {
|
|
7012
|
+
if (!service.refreshOpenCodeGo) {
|
|
7013
|
+
return writeError2(res, 501, "opencodego allowance refresh is not available");
|
|
7014
|
+
}
|
|
7015
|
+
const allowances2 = await service.refreshOpenCodeGo(accountId);
|
|
7016
|
+
if (accountId && allowances2.length === 0) {
|
|
7017
|
+
return writeError2(res, 404, `OpenCodeGo account '${accountId}' not found`);
|
|
7018
|
+
}
|
|
7019
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7020
|
+
}
|
|
6221
7021
|
const allowances = await service.refreshClaude(accountId);
|
|
6222
7022
|
if (accountId && allowances.length === 0) {
|
|
6223
7023
|
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
@@ -6385,8 +7185,8 @@ async function handleAdminApi(req, res, path2, deps) {
|
|
|
6385
7185
|
default:
|
|
6386
7186
|
return writeJsonError(res, 404, `unknown admin resource '${resource}'`);
|
|
6387
7187
|
}
|
|
6388
|
-
} catch (
|
|
6389
|
-
writeJsonError(res, 500,
|
|
7188
|
+
} catch (err6) {
|
|
7189
|
+
writeJsonError(res, 500, err6 instanceof Error ? err6.message : String(err6));
|
|
6390
7190
|
}
|
|
6391
7191
|
}
|
|
6392
7192
|
function requestQuery(req) {
|
|
@@ -6456,6 +7256,9 @@ async function handleProviders(req, res, method, rest, deps) {
|
|
|
6456
7256
|
if (method === "POST" && rest.length === 4 && rest[1] === "keys" && rest[3] === "enabled") {
|
|
6457
7257
|
return await handleToggleProviderKey(req, res, rest[0], rest[2], cfg, deps);
|
|
6458
7258
|
}
|
|
7259
|
+
if (method === "POST" && rest.length === 5 && rest[1] === "keys" && rest[3] === "quota" && rest[4] === "refresh") {
|
|
7260
|
+
return await handleProviderKeyQuotaRefresh(res, rest[0], rest[2], cfg, deps);
|
|
7261
|
+
}
|
|
6459
7262
|
if (method === "PUT" && rest.length === 3 && rest[1] === "keys") {
|
|
6460
7263
|
return await handleUpdateProviderKey(req, res, rest[0], rest[2], cfg, deps);
|
|
6461
7264
|
}
|
|
@@ -6554,7 +7357,7 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
6554
7357
|
try {
|
|
6555
7358
|
const headers = { Accept: "application/json" };
|
|
6556
7359
|
if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
|
|
6557
|
-
const response = await (0,
|
|
7360
|
+
const response = await (0, import_upstreamFetch7.fetchUpstream)(url, { method: "GET", headers }, { providerId: "byo" });
|
|
6558
7361
|
if (!response.ok) {
|
|
6559
7362
|
const text = await response.text().catch(() => "");
|
|
6560
7363
|
let message = text.slice(0, 300);
|
|
@@ -6571,8 +7374,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
6571
7374
|
const data = await response.json();
|
|
6572
7375
|
const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
|
|
6573
7376
|
return writeJson4(res, 200, { models });
|
|
6574
|
-
} catch (
|
|
6575
|
-
const message =
|
|
7377
|
+
} catch (err6) {
|
|
7378
|
+
const message = err6 instanceof Error ? err6.message : String(err6);
|
|
6576
7379
|
return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
|
|
6577
7380
|
}
|
|
6578
7381
|
}
|
|
@@ -6613,7 +7416,7 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
6613
7416
|
}
|
|
6614
7417
|
const startedAt = Date.now();
|
|
6615
7418
|
try {
|
|
6616
|
-
const response = await (0,
|
|
7419
|
+
const response = await (0, import_upstreamFetch7.fetchUpstream)(
|
|
6617
7420
|
url,
|
|
6618
7421
|
{ method: "POST", headers, body: JSON.stringify(payload) },
|
|
6619
7422
|
{ providerId: "byo" }
|
|
@@ -6635,8 +7438,8 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
6635
7438
|
latencyMs,
|
|
6636
7439
|
sample: extractSampleText(text, row.apiFormat)
|
|
6637
7440
|
});
|
|
6638
|
-
} catch (
|
|
6639
|
-
const message =
|
|
7441
|
+
} catch (err6) {
|
|
7442
|
+
const message = err6 instanceof Error ? err6.message : String(err6);
|
|
6640
7443
|
return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
|
|
6641
7444
|
}
|
|
6642
7445
|
}
|
|
@@ -6678,7 +7481,30 @@ async function handleProviderKeys(res, id, cfg, deps) {
|
|
|
6678
7481
|
const row = cfg.providers.find((p) => p.id === id);
|
|
6679
7482
|
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
6680
7483
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
6681
|
-
|
|
7484
|
+
const views = toPoolKeyView(row, cooldown, deps);
|
|
7485
|
+
if (deps.providerKeyQuota) {
|
|
7486
|
+
const quotas = await Promise.allSettled(
|
|
7487
|
+
views.map((view) => deps.providerKeyQuota.quotaFor(row, view.id))
|
|
7488
|
+
);
|
|
7489
|
+
views.forEach((view, index) => {
|
|
7490
|
+
const settled = quotas[index];
|
|
7491
|
+
if (settled.status === "fulfilled" && settled.value) view.quota = settled.value;
|
|
7492
|
+
});
|
|
7493
|
+
}
|
|
7494
|
+
return writeJson4(res, 200, { keys: views });
|
|
7495
|
+
}
|
|
7496
|
+
async function handleProviderKeyQuotaRefresh(res, id, keyId, cfg, deps) {
|
|
7497
|
+
if (!deps.providerKeyQuota) return writeJsonError(res, 501, "provider key quota is not available");
|
|
7498
|
+
if (!id || !keyId) return writeJsonError(res, 400, "provider id and key id required in path");
|
|
7499
|
+
const row = cfg.providers.find((p) => p.id === id);
|
|
7500
|
+
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
7501
|
+
try {
|
|
7502
|
+
const quota = await deps.providerKeyQuota.quotaFor(row, keyId, { force: true });
|
|
7503
|
+
if (!quota) return writeJsonError(res, 404, `no quota endpoint for key '${keyId}'`);
|
|
7504
|
+
return writeJson4(res, 200, { quota });
|
|
7505
|
+
} catch {
|
|
7506
|
+
return writeJsonError(res, 502, "quota refresh failed");
|
|
7507
|
+
}
|
|
6682
7508
|
}
|
|
6683
7509
|
function parsePoolKeyInput(body, existing) {
|
|
6684
7510
|
const out = {};
|
|
@@ -7423,12 +8249,12 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
7423
8249
|
}
|
|
7424
8250
|
return writeJson4(res, 200, { ok: true, affected: result.affected });
|
|
7425
8251
|
}
|
|
7426
|
-
if (method === "GET" && rest[0] === "codex" && rest[1] === "oauth" && rest[3] === "status") {
|
|
7427
|
-
const result = handleCodexOAuthStatus(rest[2], deps);
|
|
8252
|
+
if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi") && rest[1] === "oauth" && rest[3] === "status") {
|
|
8253
|
+
const result = rest[0] === "codex" ? handleCodexOAuthStatus(rest[2], deps) : handleKimiOAuthStatus(rest[2], deps);
|
|
7428
8254
|
return writeJson4(res, result.status, result.body);
|
|
7429
8255
|
}
|
|
7430
|
-
if (method === "DELETE" && rest[0] === "codex" && rest[1] === "oauth" && rest[2]) {
|
|
7431
|
-
const result = handleCodexOAuthCancel(rest[2], deps);
|
|
8256
|
+
if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi") && rest[1] === "oauth" && rest[2]) {
|
|
8257
|
+
const result = rest[0] === "codex" ? handleCodexOAuthCancel(rest[2], deps) : handleKimiOAuthCancel(rest[2], deps);
|
|
7432
8258
|
return writeJson4(res, result.status, result.body);
|
|
7433
8259
|
}
|
|
7434
8260
|
if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
|
|
@@ -7481,7 +8307,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
7481
8307
|
return writeJsonError(res, 400, `unknown subscription provider '${rest[0] ?? ""}'`);
|
|
7482
8308
|
}
|
|
7483
8309
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "start") {
|
|
7484
|
-
|
|
8310
|
+
if (providerId === "codex") {
|
|
8311
|
+
const result2 = handleCodexOAuthStart(deps);
|
|
8312
|
+
return writeJson4(res, result2.status, result2.body);
|
|
8313
|
+
}
|
|
8314
|
+
if (providerId === "kimi") {
|
|
8315
|
+
const result2 = await handleKimiOAuthStart(deps);
|
|
8316
|
+
return writeJson4(res, result2.status, result2.body);
|
|
8317
|
+
}
|
|
8318
|
+
const result = handleOAuthStart(providerId, deps);
|
|
7485
8319
|
return writeJson4(res, result.status, result.body);
|
|
7486
8320
|
}
|
|
7487
8321
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "complete") {
|
|
@@ -7975,12 +8809,12 @@ async function handlePlayground(req, res, method, deps) {
|
|
|
7975
8809
|
const payload = body["body"];
|
|
7976
8810
|
const status = deps.outboundApiServer.getStatus();
|
|
7977
8811
|
if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
|
|
7978
|
-
const path2 = resolvePlaygroundPath(endpoint,
|
|
8812
|
+
const path2 = resolvePlaygroundPath(endpoint, isRecord4(payload) ? payload : {});
|
|
7979
8813
|
if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
|
|
7980
8814
|
const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
|
|
7981
8815
|
await proxyToOutbound(res, status.port, path2, key, upstreamBody);
|
|
7982
8816
|
}
|
|
7983
|
-
function
|
|
8817
|
+
function isRecord4(v) {
|
|
7984
8818
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
7985
8819
|
}
|
|
7986
8820
|
function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
@@ -8009,8 +8843,8 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
|
8009
8843
|
});
|
|
8010
8844
|
}
|
|
8011
8845
|
);
|
|
8012
|
-
upstream.on("error", (
|
|
8013
|
-
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${
|
|
8846
|
+
upstream.on("error", (err6) => {
|
|
8847
|
+
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err6.message}`);
|
|
8014
8848
|
else res.end();
|
|
8015
8849
|
resolve11();
|
|
8016
8850
|
});
|
|
@@ -8116,7 +8950,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
8116
8950
|
}
|
|
8117
8951
|
|
|
8118
8952
|
// src/admin/version.ts
|
|
8119
|
-
var DAEMON_VERSION = true ? "0.3.
|
|
8953
|
+
var DAEMON_VERSION = true ? "0.3.1" : "0.0.0-dev";
|
|
8120
8954
|
|
|
8121
8955
|
// src/admin/AdminServer.ts
|
|
8122
8956
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -8159,13 +8993,13 @@ var AdminServer = class {
|
|
|
8159
8993
|
const server = import_node_http2.default.createServer((req, res) => {
|
|
8160
8994
|
this.onRequest(req, res);
|
|
8161
8995
|
});
|
|
8162
|
-
const onError = (
|
|
8163
|
-
if (
|
|
8996
|
+
const onError = (err6) => {
|
|
8997
|
+
if (err6.code === "EADDRINUSE" && port !== 0) {
|
|
8164
8998
|
server.removeListener("error", onError);
|
|
8165
8999
|
this.listen(bindAddr, 0).then(resolve11, reject);
|
|
8166
9000
|
return;
|
|
8167
9001
|
}
|
|
8168
|
-
reject(
|
|
9002
|
+
reject(err6);
|
|
8169
9003
|
};
|
|
8170
9004
|
server.on("error", onError);
|
|
8171
9005
|
server.listen(port, bindAddr, () => {
|
|
@@ -8183,8 +9017,8 @@ var AdminServer = class {
|
|
|
8183
9017
|
}
|
|
8184
9018
|
/** Per-request handler: auth gate (when a token is set) → routing. */
|
|
8185
9019
|
onRequest(req, res) {
|
|
8186
|
-
void this.dispatch(req, res).catch((
|
|
8187
|
-
const message =
|
|
9020
|
+
void this.dispatch(req, res).catch((err6) => {
|
|
9021
|
+
const message = err6 instanceof Error ? err6.message : String(err6);
|
|
8188
9022
|
this.deps.logger.error("[AdminServer] unhandled error:", message);
|
|
8189
9023
|
if (!res.headersSent) {
|
|
8190
9024
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
@@ -8448,18 +9282,18 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
8448
9282
|
return;
|
|
8449
9283
|
}
|
|
8450
9284
|
signal?.addEventListener("abort", abort, { once: true });
|
|
8451
|
-
server.on("error", (
|
|
9285
|
+
server.on("error", (err6) => {
|
|
8452
9286
|
if (settled) return;
|
|
8453
9287
|
settled = true;
|
|
8454
9288
|
clearTimeout(timer);
|
|
8455
|
-
if (
|
|
9289
|
+
if (err6.code === "EADDRINUSE") {
|
|
8456
9290
|
reject(
|
|
8457
9291
|
new Error(
|
|
8458
9292
|
`login: cannot bind ${LOOPBACK_HOST}:${LOOPBACK_PORT} (address in use) \u2014 another codex login or process is holding the port`
|
|
8459
9293
|
)
|
|
8460
9294
|
);
|
|
8461
9295
|
} else {
|
|
8462
|
-
reject(
|
|
9296
|
+
reject(err6);
|
|
8463
9297
|
}
|
|
8464
9298
|
});
|
|
8465
9299
|
const timer = setTimeout(() => {
|
|
@@ -8534,10 +9368,415 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
|
|
|
8534
9368
|
};
|
|
8535
9369
|
}
|
|
8536
9370
|
|
|
9371
|
+
// src/allowance/ProviderKeyQuotaService.ts
|
|
9372
|
+
var import_upstreamFetch8 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
9373
|
+
|
|
9374
|
+
// src/allowance/ProviderKeyQuota.ts
|
|
9375
|
+
var MINUTE_MS2 = 6e4;
|
|
9376
|
+
var HOUR_MS2 = 60 * MINUTE_MS2;
|
|
9377
|
+
var DAY_MS2 = 24 * HOUR_MS2;
|
|
9378
|
+
var WEEK_MS = 7 * DAY_MS2;
|
|
9379
|
+
var MONTH_MS = 30 * DAY_MS2;
|
|
9380
|
+
function finiteNumber3(value) {
|
|
9381
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
9382
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
9383
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
9384
|
+
}
|
|
9385
|
+
function finitePercent4(value) {
|
|
9386
|
+
const parsed = finiteNumber3(value);
|
|
9387
|
+
return parsed !== void 0 && parsed <= 100 ? parsed : null;
|
|
9388
|
+
}
|
|
9389
|
+
function isoInstant3(value) {
|
|
9390
|
+
if (typeof value === "string" && value.trim()) {
|
|
9391
|
+
const time = Date.parse(value);
|
|
9392
|
+
if (Number.isFinite(time)) return new Date(time).toISOString();
|
|
9393
|
+
}
|
|
9394
|
+
const numeric = finiteNumber3(value);
|
|
9395
|
+
if (numeric !== void 0 && numeric > 1e9) {
|
|
9396
|
+
const ms = numeric > 1e12 ? numeric : numeric * 1e3;
|
|
9397
|
+
return new Date(ms).toISOString();
|
|
9398
|
+
}
|
|
9399
|
+
return void 0;
|
|
9400
|
+
}
|
|
9401
|
+
function secondsUntil5(instant, now) {
|
|
9402
|
+
if (!instant) return void 0;
|
|
9403
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
9404
|
+
}
|
|
9405
|
+
function isRecord5(value) {
|
|
9406
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
9407
|
+
}
|
|
9408
|
+
function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
9409
|
+
if (!baseUrl) return null;
|
|
9410
|
+
let url;
|
|
9411
|
+
try {
|
|
9412
|
+
url = new URL(baseUrl);
|
|
9413
|
+
} catch {
|
|
9414
|
+
return null;
|
|
9415
|
+
}
|
|
9416
|
+
const host = url.hostname.toLowerCase();
|
|
9417
|
+
const path2 = url.pathname.toLowerCase();
|
|
9418
|
+
if ((host === "api.z.ai" || host === "open.bigmodel.cn") && path2.includes("/coding")) {
|
|
9419
|
+
return "zai";
|
|
9420
|
+
}
|
|
9421
|
+
if ((host === "api.minimax.io" || host === "api.minimaxi.com") && // Token Plan rides the plain openai `/v1` (chat completions) surface; the
|
|
9422
|
+
// anthropic `/anthropic` rows are excluded (their usage impl is unverified).
|
|
9423
|
+
(path2 === "/v1" || path2 === "/v1/" || path2 === "" || path2 === "/")) {
|
|
9424
|
+
return "minimax-token-plan";
|
|
9425
|
+
}
|
|
9426
|
+
if (host === "api.code.umans.ai") return "umans";
|
|
9427
|
+
if (host === "api.synthetic.new") return "synthetic";
|
|
9428
|
+
return null;
|
|
9429
|
+
}
|
|
9430
|
+
function providerKeyQuotaUrl(adapter, baseUrl) {
|
|
9431
|
+
const origin = new URL(baseUrl).origin;
|
|
9432
|
+
if (adapter === "zai") return `${origin}/api/monitor/usage/quota/limit`;
|
|
9433
|
+
if (adapter === "minimax-token-plan") return `${origin}/v1/token_plan/remains`;
|
|
9434
|
+
if (adapter === "umans") return `${origin}/v1/usage`;
|
|
9435
|
+
return `${origin}/v2/quotas`;
|
|
9436
|
+
}
|
|
9437
|
+
function providerKeyQuotaAuthHeader(adapter, key) {
|
|
9438
|
+
return adapter === "zai" ? key : `Bearer ${key}`;
|
|
9439
|
+
}
|
|
9440
|
+
function zaiWindowDurationMs(item) {
|
|
9441
|
+
const count = item.number !== void 0 && item.number > 0 ? item.number : 1;
|
|
9442
|
+
switch (item.unit) {
|
|
9443
|
+
case 3:
|
|
9444
|
+
return count * HOUR_MS2;
|
|
9445
|
+
case 4:
|
|
9446
|
+
return count * DAY_MS2;
|
|
9447
|
+
case 5:
|
|
9448
|
+
return count * MONTH_MS;
|
|
9449
|
+
case 6:
|
|
9450
|
+
return WEEK_MS;
|
|
9451
|
+
default:
|
|
9452
|
+
return void 0;
|
|
9453
|
+
}
|
|
9454
|
+
}
|
|
9455
|
+
function zaiWindowIdLabel(durationMs) {
|
|
9456
|
+
if (durationMs === WEEK_MS) return { id: "seven-day", label: "7 days" };
|
|
9457
|
+
if (durationMs === 5 * HOUR_MS2) return { id: "five-hour", label: "5 hours" };
|
|
9458
|
+
if (durationMs === MONTH_MS) return { id: "thirty-day", label: "30 days" };
|
|
9459
|
+
if (durationMs !== void 0 && durationMs % DAY_MS2 === 0) {
|
|
9460
|
+
const days = durationMs / DAY_MS2;
|
|
9461
|
+
return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}` };
|
|
9462
|
+
}
|
|
9463
|
+
if (durationMs !== void 0 && durationMs % HOUR_MS2 === 0) {
|
|
9464
|
+
const hours = durationMs / HOUR_MS2;
|
|
9465
|
+
return { id: `${hours}h`, label: `${hours} hour${hours === 1 ? "" : "s"}` };
|
|
9466
|
+
}
|
|
9467
|
+
return { id: "quota", label: "Quota" };
|
|
9468
|
+
}
|
|
9469
|
+
function parseZaiQuotaPayload(payload, now) {
|
|
9470
|
+
if (!isRecord5(payload)) return null;
|
|
9471
|
+
const data = isRecord5(payload["data"]) ? payload["data"] : payload;
|
|
9472
|
+
if (payload["success"] === false) return null;
|
|
9473
|
+
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
9474
|
+
const byWindow = /* @__PURE__ */ new Map();
|
|
9475
|
+
for (const raw of limits) {
|
|
9476
|
+
if (!isRecord5(raw)) continue;
|
|
9477
|
+
const item = raw;
|
|
9478
|
+
if (item.type === void 0) continue;
|
|
9479
|
+
const details = raw["usageDetails"];
|
|
9480
|
+
if (Array.isArray(details) && details.some((d) => isRecord5(d) && d["modelCode"] === "zread")) {
|
|
9481
|
+
continue;
|
|
9482
|
+
}
|
|
9483
|
+
const durationMs = zaiWindowDurationMs(item);
|
|
9484
|
+
const { id, label } = zaiWindowIdLabel(durationMs);
|
|
9485
|
+
const limit = finiteNumber3(item.usage);
|
|
9486
|
+
const used = finiteNumber3(item.currentValue);
|
|
9487
|
+
const fromAbsolute = limit !== void 0 && used !== void 0 && limit > 0 ? Math.min(100, used / limit * 100) : void 0;
|
|
9488
|
+
const fromPercentage = finitePercent4(item.percentage) ?? void 0;
|
|
9489
|
+
const usedPercent = fromAbsolute !== void 0 ? Math.round(fromAbsolute * 10) / 10 : fromPercentage;
|
|
9490
|
+
if (usedPercent === void 0) continue;
|
|
9491
|
+
const resetsAt = isoInstant3(item.nextResetTime);
|
|
9492
|
+
const candidate = {
|
|
9493
|
+
id,
|
|
9494
|
+
label,
|
|
9495
|
+
scope: "all",
|
|
9496
|
+
usedPercent,
|
|
9497
|
+
...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs / MINUTE_MS2) } : {},
|
|
9498
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9499
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
9500
|
+
state: "fresh"
|
|
9501
|
+
};
|
|
9502
|
+
const existing = byWindow.get(id);
|
|
9503
|
+
if (!existing || (candidate.usedPercent ?? 0) > (existing.usedPercent ?? 0)) {
|
|
9504
|
+
byWindow.set(id, candidate);
|
|
9505
|
+
}
|
|
9506
|
+
}
|
|
9507
|
+
const windows = [...byWindow.values()].sort((a, b) => (a.windowMinutes ?? Number.POSITIVE_INFINITY) - (b.windowMinutes ?? Number.POSITIVE_INFINITY));
|
|
9508
|
+
return windows.length > 0 ? windows.slice(0, 4) : null;
|
|
9509
|
+
}
|
|
9510
|
+
var MINIMAX_STATUS_EXHAUSTED = 2;
|
|
9511
|
+
var MINIMAX_SHARED_BUCKET = "general";
|
|
9512
|
+
function parseMiniMaxBucket(value) {
|
|
9513
|
+
if (!isRecord5(value)) return null;
|
|
9514
|
+
const modelName = typeof value["model_name"] === "string" ? value["model_name"].trim() : "";
|
|
9515
|
+
if (!modelName) return null;
|
|
9516
|
+
const instant = (v) => {
|
|
9517
|
+
const n = finiteNumber3(v);
|
|
9518
|
+
return n !== void 0 && n > 1e9 ? n > 1e12 ? n : n * 1e3 : void 0;
|
|
9519
|
+
};
|
|
9520
|
+
return {
|
|
9521
|
+
modelName,
|
|
9522
|
+
intervalEnd: instant(value["end_time"]),
|
|
9523
|
+
intervalRemainingPercent: finiteNumber3(value["current_interval_remaining_percent"]),
|
|
9524
|
+
intervalStatus: finiteNumber3(value["current_interval_status"]),
|
|
9525
|
+
weeklyEnd: instant(value["weekly_end_time"]),
|
|
9526
|
+
weeklyRemainingPercent: finiteNumber3(value["current_weekly_remaining_percent"]),
|
|
9527
|
+
weeklyStatus: finiteNumber3(value["current_weekly_status"])
|
|
9528
|
+
};
|
|
9529
|
+
}
|
|
9530
|
+
function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, status, now) {
|
|
9531
|
+
const usedPercent = status === MINIMAX_STATUS_EXHAUSTED ? 100 : remainingPercent !== void 0 ? Math.round((100 - remainingPercent) * 10) / 10 : null;
|
|
9532
|
+
const resetsAt = resetsAtMs !== void 0 ? new Date(resetsAtMs).toISOString() : void 0;
|
|
9533
|
+
return {
|
|
9534
|
+
id,
|
|
9535
|
+
label,
|
|
9536
|
+
scope: "all",
|
|
9537
|
+
usedPercent,
|
|
9538
|
+
...windowMinutes !== void 0 ? { windowMinutes } : {},
|
|
9539
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9540
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
9541
|
+
state: usedPercent !== null ? "fresh" : "unavailable"
|
|
9542
|
+
};
|
|
9543
|
+
}
|
|
9544
|
+
function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
9545
|
+
if (!isRecord5(payload)) return null;
|
|
9546
|
+
const baseResp = payload["base_resp"];
|
|
9547
|
+
if (!isRecord5(baseResp) || baseResp["status_code"] !== 0) return null;
|
|
9548
|
+
const buckets = Array.isArray(payload["model_remains"]) ? payload["model_remains"] : [];
|
|
9549
|
+
let general = null;
|
|
9550
|
+
for (const raw of buckets) {
|
|
9551
|
+
const bucket = parseMiniMaxBucket(raw);
|
|
9552
|
+
if (bucket?.modelName === MINIMAX_SHARED_BUCKET) {
|
|
9553
|
+
general = bucket;
|
|
9554
|
+
break;
|
|
9555
|
+
}
|
|
9556
|
+
}
|
|
9557
|
+
if (!general) return null;
|
|
9558
|
+
return [
|
|
9559
|
+
minimaxWindow(
|
|
9560
|
+
"five-hour",
|
|
9561
|
+
"5 hours",
|
|
9562
|
+
5 * 60,
|
|
9563
|
+
general.intervalEnd,
|
|
9564
|
+
general.intervalRemainingPercent,
|
|
9565
|
+
general.intervalStatus,
|
|
9566
|
+
now
|
|
9567
|
+
),
|
|
9568
|
+
minimaxWindow(
|
|
9569
|
+
"seven-day",
|
|
9570
|
+
"7 days",
|
|
9571
|
+
Math.round(WEEK_MS / MINUTE_MS2),
|
|
9572
|
+
general.weeklyEnd,
|
|
9573
|
+
general.weeklyRemainingPercent,
|
|
9574
|
+
general.weeklyStatus,
|
|
9575
|
+
now
|
|
9576
|
+
)
|
|
9577
|
+
];
|
|
9578
|
+
}
|
|
9579
|
+
function parseUmansUsagePayload(payload, now) {
|
|
9580
|
+
if (!isRecord5(payload)) return null;
|
|
9581
|
+
const limits = isRecord5(payload["limits"]) ? payload["limits"] : void 0;
|
|
9582
|
+
const requests = limits && isRecord5(limits["requests"]) ? limits["requests"] : void 0;
|
|
9583
|
+
const usage = isRecord5(payload["usage"]) ? payload["usage"] : void 0;
|
|
9584
|
+
const window = isRecord5(payload["window"]) ? payload["window"] : void 0;
|
|
9585
|
+
const hardCap = finiteNumber3(requests?.["hard_cap"]);
|
|
9586
|
+
const softLimit = finiteNumber3(requests?.["limit"]);
|
|
9587
|
+
const requestsInWindow = finiteNumber3(usage?.["requests_in_window"]);
|
|
9588
|
+
const weightedInWindow = finiteNumber3(usage?.["weighted_in_window"]);
|
|
9589
|
+
const resetsAt = isoInstant3(window?.["resets_at"]);
|
|
9590
|
+
let usedPercent = null;
|
|
9591
|
+
if (hardCap !== void 0 && hardCap > 0 && requestsInWindow !== void 0) {
|
|
9592
|
+
usedPercent = Math.round(Math.min(100, requestsInWindow / hardCap * 100) * 10) / 10;
|
|
9593
|
+
} else if (softLimit !== void 0 && softLimit > 0 && weightedInWindow !== void 0) {
|
|
9594
|
+
usedPercent = Math.round(Math.min(100, weightedInWindow / softLimit * 100) * 10) / 10;
|
|
9595
|
+
}
|
|
9596
|
+
if (usedPercent === null && resetsAt === void 0) return null;
|
|
9597
|
+
return [
|
|
9598
|
+
{
|
|
9599
|
+
id: "five-hour",
|
|
9600
|
+
label: "5 hours",
|
|
9601
|
+
scope: "all",
|
|
9602
|
+
usedPercent,
|
|
9603
|
+
windowMinutes: 5 * 60,
|
|
9604
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9605
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
9606
|
+
state: "fresh"
|
|
9607
|
+
}
|
|
9608
|
+
];
|
|
9609
|
+
}
|
|
9610
|
+
function parseSyntheticQuotasPayload(payload, now) {
|
|
9611
|
+
if (!isRecord5(payload)) return null;
|
|
9612
|
+
const fiveHour = isRecord5(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
|
|
9613
|
+
const weekly = isRecord5(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
|
|
9614
|
+
const windows = [];
|
|
9615
|
+
if (fiveHour) {
|
|
9616
|
+
const max = finiteNumber3(fiveHour["max"]);
|
|
9617
|
+
const remaining = finiteNumber3(fiveHour["remaining"]);
|
|
9618
|
+
const usedPercent = max !== void 0 && max > 0 && remaining !== void 0 ? Math.round(Math.min(100, (max - remaining) / max * 100) * 10) / 10 : null;
|
|
9619
|
+
const resetsAt = isoInstant3(fiveHour["nextTickAt"]);
|
|
9620
|
+
windows.push({
|
|
9621
|
+
id: "five-hour",
|
|
9622
|
+
label: "5 hours",
|
|
9623
|
+
scope: "all",
|
|
9624
|
+
usedPercent,
|
|
9625
|
+
windowMinutes: 5 * 60,
|
|
9626
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9627
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
9628
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
9629
|
+
});
|
|
9630
|
+
}
|
|
9631
|
+
if (weekly) {
|
|
9632
|
+
const percentRemaining = finiteNumber3(weekly["percentRemaining"]);
|
|
9633
|
+
const usedPercent = percentRemaining !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - percentRemaining)) * 10) / 10 : null;
|
|
9634
|
+
const resetsAt = isoInstant3(weekly["nextRegenAt"]);
|
|
9635
|
+
windows.push({
|
|
9636
|
+
id: "seven-day",
|
|
9637
|
+
label: "7 days",
|
|
9638
|
+
scope: "all",
|
|
9639
|
+
usedPercent,
|
|
9640
|
+
windowMinutes: 7 * 24 * 60,
|
|
9641
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9642
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
9643
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
9644
|
+
});
|
|
9645
|
+
}
|
|
9646
|
+
return windows.length > 0 ? windows : null;
|
|
9647
|
+
}
|
|
9648
|
+
|
|
9649
|
+
// src/allowance/ProviderKeyQuotaService.ts
|
|
9650
|
+
function parseQuotaPayload(adapter, payload, now) {
|
|
9651
|
+
switch (adapter) {
|
|
9652
|
+
case "zai":
|
|
9653
|
+
return parseZaiQuotaPayload(payload, now);
|
|
9654
|
+
case "minimax-token-plan":
|
|
9655
|
+
return parseMiniMaxTokenPlanPayload(payload, now);
|
|
9656
|
+
case "umans":
|
|
9657
|
+
return parseUmansUsagePayload(payload, now);
|
|
9658
|
+
case "synthetic":
|
|
9659
|
+
return parseSyntheticQuotasPayload(payload, now);
|
|
9660
|
+
}
|
|
9661
|
+
}
|
|
9662
|
+
var PROVIDER_KEY_QUOTA_CACHE_MS = 5 * 6e4;
|
|
9663
|
+
function resolvedBaseUrl(row) {
|
|
9664
|
+
const modes = row.apiModes ?? [];
|
|
9665
|
+
const selected = row.selectedApiModeId ? modes.find((mode) => mode.id === row.selectedApiModeId) : void 0;
|
|
9666
|
+
const fallback = modes[0];
|
|
9667
|
+
const modeBase = selected?.baseUrl ?? fallback?.baseUrl;
|
|
9668
|
+
return modeBase ?? row.codingPlan?.baseUrl ?? row.baseUrl;
|
|
9669
|
+
}
|
|
9670
|
+
function rowKeyEntries(row) {
|
|
9671
|
+
const pool = (row.apiKeys ?? []).filter((entry) => entry.apiKey.length > 0);
|
|
9672
|
+
if (pool.length > 0) return pool.map((entry) => ({ id: entry.id, apiKey: entry.apiKey }));
|
|
9673
|
+
if (row.apiKey.length > 0) {
|
|
9674
|
+
return [{ id: `${row.id}:default`, apiKey: row.apiKey }];
|
|
9675
|
+
}
|
|
9676
|
+
return [];
|
|
9677
|
+
}
|
|
9678
|
+
var ProviderKeyQuotaService = class {
|
|
9679
|
+
constructor(box, fetchImpl = (url, init) => (0, import_upstreamFetch8.fetchUpstream)(url, init, { redactBodies: true }), now = Date.now) {
|
|
9680
|
+
this.box = box;
|
|
9681
|
+
this.fetchImpl = fetchImpl;
|
|
9682
|
+
this.now = now;
|
|
9683
|
+
}
|
|
9684
|
+
box;
|
|
9685
|
+
fetchImpl;
|
|
9686
|
+
now;
|
|
9687
|
+
cache = /* @__PURE__ */ new Map();
|
|
9688
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
9689
|
+
/**
|
|
9690
|
+
* Quota for one key of a provider row, or `null` when the row has no quota
|
|
9691
|
+
* adapter / no such key. Cache-first; concurrent reads share one flight.
|
|
9692
|
+
*/
|
|
9693
|
+
async quotaFor(row, keyId, options = {}) {
|
|
9694
|
+
const adapter = detectProviderKeyQuotaAdapter(resolvedBaseUrl(row));
|
|
9695
|
+
if (!adapter) return null;
|
|
9696
|
+
const entry = rowKeyEntries(row).find((candidate) => candidate.id === keyId);
|
|
9697
|
+
if (!entry) return null;
|
|
9698
|
+
const cacheKey = `${row.id}\0${keyId}`;
|
|
9699
|
+
const now = this.now();
|
|
9700
|
+
const cached = this.cache.get(cacheKey);
|
|
9701
|
+
if (!options.force && cached && Date.parse(cached.expiresAt) > now) return cached;
|
|
9702
|
+
const running = this.inFlight.get(cacheKey);
|
|
9703
|
+
if (running) return running;
|
|
9704
|
+
const promise = this.fetchQuota(adapter, row, entry.apiKey, cacheKey).catch((error) => {
|
|
9705
|
+
void error;
|
|
9706
|
+
const previous = this.cache.get(cacheKey);
|
|
9707
|
+
if (previous) {
|
|
9708
|
+
const degraded = {
|
|
9709
|
+
...previous,
|
|
9710
|
+
expiresAt: new Date(now + PROVIDER_KEY_QUOTA_CACHE_MS).toISOString(),
|
|
9711
|
+
windows: previous.windows.map((window) => ({
|
|
9712
|
+
...window,
|
|
9713
|
+
state: window.usedPercent !== null || window.resetsAt ? "stale" : window.state
|
|
9714
|
+
})),
|
|
9715
|
+
errorCode: "quota_request_failed"
|
|
9716
|
+
};
|
|
9717
|
+
this.cache.set(cacheKey, degraded);
|
|
9718
|
+
return degraded;
|
|
9719
|
+
}
|
|
9720
|
+
return null;
|
|
9721
|
+
}).finally(() => this.inFlight.delete(cacheKey));
|
|
9722
|
+
this.inFlight.set(cacheKey, promise);
|
|
9723
|
+
return promise;
|
|
9724
|
+
}
|
|
9725
|
+
/** Drop cached rows for a provider (key added/removed/rotated). */
|
|
9726
|
+
invalidateProvider(providerRowId) {
|
|
9727
|
+
for (const key of this.cache.keys()) {
|
|
9728
|
+
if (key.startsWith(`${providerRowId}\0`)) this.cache.delete(key);
|
|
9729
|
+
}
|
|
9730
|
+
}
|
|
9731
|
+
async fetchQuota(adapter, row, rawKey, cacheKey) {
|
|
9732
|
+
const baseUrl = resolvedBaseUrl(row);
|
|
9733
|
+
const url = providerKeyQuotaUrl(adapter, baseUrl);
|
|
9734
|
+
const key = this.box.decryptMaybe(rawKey);
|
|
9735
|
+
const now = this.now();
|
|
9736
|
+
const response = await this.fetchImpl(url, {
|
|
9737
|
+
method: "GET",
|
|
9738
|
+
headers: {
|
|
9739
|
+
Authorization: providerKeyQuotaAuthHeader(adapter, key),
|
|
9740
|
+
Accept: "application/json",
|
|
9741
|
+
"Content-Type": "application/json"
|
|
9742
|
+
},
|
|
9743
|
+
signal: AbortSignal.timeout(15e3)
|
|
9744
|
+
});
|
|
9745
|
+
if (response.status === 401 || response.status === 403) {
|
|
9746
|
+
const snapshot2 = {
|
|
9747
|
+
adapter,
|
|
9748
|
+
observedAt: new Date(now).toISOString(),
|
|
9749
|
+
expiresAt: new Date(now + PROVIDER_KEY_QUOTA_CACHE_MS).toISOString(),
|
|
9750
|
+
windows: [],
|
|
9751
|
+
errorCode: "quota_unauthorized"
|
|
9752
|
+
};
|
|
9753
|
+
this.cache.set(cacheKey, snapshot2);
|
|
9754
|
+
return snapshot2;
|
|
9755
|
+
}
|
|
9756
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
9757
|
+
let payload;
|
|
9758
|
+
try {
|
|
9759
|
+
payload = await response.json();
|
|
9760
|
+
} catch {
|
|
9761
|
+
throw new Error("invalid JSON");
|
|
9762
|
+
}
|
|
9763
|
+
const windows = parseQuotaPayload(adapter, payload, now);
|
|
9764
|
+
const snapshot = {
|
|
9765
|
+
adapter,
|
|
9766
|
+
observedAt: new Date(now).toISOString(),
|
|
9767
|
+
expiresAt: new Date(now + PROVIDER_KEY_QUOTA_CACHE_MS).toISOString(),
|
|
9768
|
+
windows: windows ?? [],
|
|
9769
|
+
...windows ? {} : { errorCode: "quota_unavailable" }
|
|
9770
|
+
};
|
|
9771
|
+
this.cache.set(cacheKey, snapshot);
|
|
9772
|
+
return snapshot;
|
|
9773
|
+
}
|
|
9774
|
+
};
|
|
9775
|
+
|
|
8537
9776
|
// src/image-generation/ImageDoctorService.ts
|
|
8538
9777
|
var import_image_generation = require("@omnicross/core/image-generation");
|
|
8539
9778
|
var import_outbound_api7 = require("@omnicross/core/outbound-api");
|
|
8540
|
-
var
|
|
9779
|
+
var import_subscriptions6 = require("@omnicross/subscriptions");
|
|
8541
9780
|
|
|
8542
9781
|
// src/image-generation/FileCodexImageCapabilityEvidenceSource.ts
|
|
8543
9782
|
var import_node_crypto13 = require("crypto");
|
|
@@ -8975,7 +10214,7 @@ function createImageDoctorService(options) {
|
|
|
8975
10214
|
paths,
|
|
8976
10215
|
ttlMs: config.evidenceTtlMs
|
|
8977
10216
|
}));
|
|
8978
|
-
const createLiveVerifier = options.createLiveVerifier ?? ((strategy, config) => (0,
|
|
10217
|
+
const createLiveVerifier = options.createLiveVerifier ?? ((strategy, config) => (0, import_subscriptions6.createCodexImageLiveVerifier)({
|
|
8979
10218
|
authStrategy: strategy,
|
|
8980
10219
|
generationTimeoutMs: config.queue.generationTimeoutMs
|
|
8981
10220
|
}));
|
|
@@ -9337,7 +10576,7 @@ var ImageCleanupService = class {
|
|
|
9337
10576
|
var import_node_crypto16 = require("crypto");
|
|
9338
10577
|
var import_image_generation5 = require("@omnicross/core/image-generation");
|
|
9339
10578
|
var import_outbound_api8 = require("@omnicross/core/outbound-api");
|
|
9340
|
-
var
|
|
10579
|
+
var import_subscriptions7 = require("@omnicross/subscriptions");
|
|
9341
10580
|
|
|
9342
10581
|
// src/image-generation/ImageApiRuntimeResolver.ts
|
|
9343
10582
|
var import_node_crypto14 = require("crypto");
|
|
@@ -9868,7 +11107,7 @@ function createImageRuntimeGeneration(options) {
|
|
|
9868
11107
|
now: options.now ?? Date.now,
|
|
9869
11108
|
referenceStore: options.storage.referenceStore,
|
|
9870
11109
|
stateStore: options.storage.stateStore
|
|
9871
|
-
}) : (0,
|
|
11110
|
+
}) : (0, import_subscriptions7.createCodexSubscriptionImageProvider)({
|
|
9872
11111
|
authStrategy,
|
|
9873
11112
|
evidenceSource: generationEvidenceSource,
|
|
9874
11113
|
executionScheduler: scheduler,
|
|
@@ -14534,10 +15773,13 @@ function bucketLabel(bucketStartTs, bucket) {
|
|
|
14534
15773
|
}
|
|
14535
15774
|
|
|
14536
15775
|
// src/ports/JsonOutboundKeyDb.ts
|
|
15776
|
+
var import_node_fs23 = require("fs");
|
|
15777
|
+
var import_core3 = require("@omnicross/core");
|
|
15778
|
+
|
|
15779
|
+
// src/ports/atomicFile.ts
|
|
14537
15780
|
var import_node_crypto22 = require("crypto");
|
|
14538
15781
|
var import_node_fs22 = require("fs");
|
|
14539
15782
|
var import_node_path25 = require("path");
|
|
14540
|
-
var import_core3 = require("@omnicross/core");
|
|
14541
15783
|
function atomicReplaceUtf8(targetPath, contents) {
|
|
14542
15784
|
const tempPath = (0, import_node_path25.join)(
|
|
14543
15785
|
(0, import_node_path25.dirname)(targetPath),
|
|
@@ -14567,6 +15809,8 @@ function atomicReplaceUtf8(targetPath, contents) {
|
|
|
14567
15809
|
throw error;
|
|
14568
15810
|
}
|
|
14569
15811
|
}
|
|
15812
|
+
|
|
15813
|
+
// src/ports/JsonOutboundKeyDb.ts
|
|
14570
15814
|
var JsonOutboundKeyDb = class {
|
|
14571
15815
|
/**
|
|
14572
15816
|
* @param secretBox OPTIONAL reversible-secret codec. When present, a created
|
|
@@ -14709,9 +15953,9 @@ var JsonOutboundKeyDb = class {
|
|
|
14709
15953
|
}
|
|
14710
15954
|
/** Read the key rows, tolerating a missing/corrupt file (→ empty list). */
|
|
14711
15955
|
readRows() {
|
|
14712
|
-
if (!(0,
|
|
15956
|
+
if (!(0, import_node_fs23.existsSync)(this.keysPath)) return [];
|
|
14713
15957
|
try {
|
|
14714
|
-
const parsed = JSON.parse((0,
|
|
15958
|
+
const parsed = JSON.parse((0, import_node_fs23.readFileSync)(this.keysPath, "utf8"));
|
|
14715
15959
|
return Array.isArray(parsed) ? parsed : [];
|
|
14716
15960
|
} catch {
|
|
14717
15961
|
return [];
|
|
@@ -14728,7 +15972,7 @@ function applyPolicyField(row, field, value) {
|
|
|
14728
15972
|
}
|
|
14729
15973
|
|
|
14730
15974
|
// src/ports/JsonPricingStore.ts
|
|
14731
|
-
var
|
|
15975
|
+
var import_node_fs24 = require("fs");
|
|
14732
15976
|
var import_node_crypto23 = require("crypto");
|
|
14733
15977
|
var JsonPricingStore = class {
|
|
14734
15978
|
constructor(pricingPath) {
|
|
@@ -14743,9 +15987,9 @@ var JsonPricingStore = class {
|
|
|
14743
15987
|
* otherwise unusable pricing table after a crash or manual file edit.
|
|
14744
15988
|
*/
|
|
14745
15989
|
hasUsableSnapshot() {
|
|
14746
|
-
if (!(0,
|
|
15990
|
+
if (!(0, import_node_fs24.existsSync)(this.pricingPath)) return false;
|
|
14747
15991
|
try {
|
|
14748
|
-
const parsed = JSON.parse((0,
|
|
15992
|
+
const parsed = JSON.parse((0, import_node_fs24.readFileSync)(this.pricingPath, "utf8"));
|
|
14749
15993
|
return Array.isArray(parsed) && parsed.some(isUsablePricingRow);
|
|
14750
15994
|
} catch {
|
|
14751
15995
|
return false;
|
|
@@ -14858,9 +16102,9 @@ var JsonPricingStore = class {
|
|
|
14858
16102
|
}
|
|
14859
16103
|
/** Read the pricing rows, tolerating a missing/corrupt file (→ empty list). */
|
|
14860
16104
|
readRows() {
|
|
14861
|
-
if (!(0,
|
|
16105
|
+
if (!(0, import_node_fs24.existsSync)(this.pricingPath)) return [];
|
|
14862
16106
|
try {
|
|
14863
|
-
const parsed = JSON.parse((0,
|
|
16107
|
+
const parsed = JSON.parse((0, import_node_fs24.readFileSync)(this.pricingPath, "utf8"));
|
|
14864
16108
|
return Array.isArray(parsed) ? parsed : [];
|
|
14865
16109
|
} catch {
|
|
14866
16110
|
return [];
|
|
@@ -14869,18 +16113,18 @@ var JsonPricingStore = class {
|
|
|
14869
16113
|
writeRows(rows) {
|
|
14870
16114
|
const temporaryPath = `${this.pricingPath}.${process.pid}.${(0, import_node_crypto23.randomUUID)()}.tmp`;
|
|
14871
16115
|
try {
|
|
14872
|
-
(0,
|
|
16116
|
+
(0, import_node_fs24.writeFileSync)(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
|
|
14873
16117
|
encoding: "utf8",
|
|
14874
16118
|
flag: "wx"
|
|
14875
16119
|
});
|
|
14876
16120
|
this.replaceFile(temporaryPath);
|
|
14877
16121
|
} finally {
|
|
14878
|
-
(0,
|
|
16122
|
+
(0, import_node_fs24.rmSync)(temporaryPath, { force: true });
|
|
14879
16123
|
}
|
|
14880
16124
|
}
|
|
14881
16125
|
/** Isolated for deterministic failure testing; never removes the target. */
|
|
14882
16126
|
replaceFile(temporaryPath) {
|
|
14883
|
-
(0,
|
|
16127
|
+
(0, import_node_fs24.renameSync)(temporaryPath, this.pricingPath);
|
|
14884
16128
|
}
|
|
14885
16129
|
};
|
|
14886
16130
|
function isUsablePricingRow(value) {
|
|
@@ -14890,7 +16134,7 @@ function isUsablePricingRow(value) {
|
|
|
14890
16134
|
}
|
|
14891
16135
|
|
|
14892
16136
|
// src/pricing/PricingRefreshScheduler.ts
|
|
14893
|
-
var
|
|
16137
|
+
var import_node_fs25 = require("fs");
|
|
14894
16138
|
var EMPTY_STATE2 = {
|
|
14895
16139
|
lastAttemptAt: null,
|
|
14896
16140
|
lastSuccessAt: null,
|
|
@@ -14928,9 +16172,9 @@ var PricingRefreshScheduler = class {
|
|
|
14928
16172
|
this.timer = null;
|
|
14929
16173
|
}
|
|
14930
16174
|
getState() {
|
|
14931
|
-
if (!(0,
|
|
16175
|
+
if (!(0, import_node_fs25.existsSync)(this.statePath)) return { ...EMPTY_STATE2, sources: [] };
|
|
14932
16176
|
try {
|
|
14933
|
-
const value = JSON.parse((0,
|
|
16177
|
+
const value = JSON.parse((0, import_node_fs25.readFileSync)(this.statePath, "utf8"));
|
|
14934
16178
|
return {
|
|
14935
16179
|
lastAttemptAt: finiteOrNull(value.lastAttemptAt),
|
|
14936
16180
|
lastSuccessAt: finiteOrNull(value.lastSuccessAt),
|
|
@@ -14983,9 +16227,9 @@ var PricingRefreshScheduler = class {
|
|
|
14983
16227
|
}
|
|
14984
16228
|
writeState(state) {
|
|
14985
16229
|
const temporaryPath = `${this.statePath}.tmp`;
|
|
14986
|
-
(0,
|
|
16230
|
+
(0, import_node_fs25.writeFileSync)(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
14987
16231
|
`, "utf8");
|
|
14988
|
-
(0,
|
|
16232
|
+
(0, import_node_fs25.renameSync)(temporaryPath, this.statePath);
|
|
14989
16233
|
}
|
|
14990
16234
|
};
|
|
14991
16235
|
function finiteOrNull(value) {
|
|
@@ -14993,7 +16237,7 @@ function finiteOrNull(value) {
|
|
|
14993
16237
|
}
|
|
14994
16238
|
|
|
14995
16239
|
// src/ports/JsonVoucherDb.ts
|
|
14996
|
-
var
|
|
16240
|
+
var import_node_fs26 = require("fs");
|
|
14997
16241
|
var JsonVoucherDb = class {
|
|
14998
16242
|
constructor(vouchersPath) {
|
|
14999
16243
|
this.vouchersPath = vouchersPath;
|
|
@@ -15071,27 +16315,27 @@ var JsonVoucherDb = class {
|
|
|
15071
16315
|
}
|
|
15072
16316
|
/** Read the voucher rows, tolerating a missing/corrupt file (→ empty list). */
|
|
15073
16317
|
readRows() {
|
|
15074
|
-
if (!(0,
|
|
16318
|
+
if (!(0, import_node_fs26.existsSync)(this.vouchersPath)) return [];
|
|
15075
16319
|
try {
|
|
15076
|
-
const parsed = JSON.parse((0,
|
|
16320
|
+
const parsed = JSON.parse((0, import_node_fs26.readFileSync)(this.vouchersPath, "utf8"));
|
|
15077
16321
|
return Array.isArray(parsed) ? parsed : [];
|
|
15078
16322
|
} catch {
|
|
15079
16323
|
return [];
|
|
15080
16324
|
}
|
|
15081
16325
|
}
|
|
15082
16326
|
writeRows(rows) {
|
|
15083
|
-
(0,
|
|
16327
|
+
(0, import_node_fs26.writeFileSync)(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
|
|
15084
16328
|
}
|
|
15085
16329
|
};
|
|
15086
16330
|
|
|
15087
16331
|
// src/ports/JsonSubscriptionCredentialStore.ts
|
|
15088
|
-
var
|
|
16332
|
+
var import_node_fs28 = require("fs");
|
|
15089
16333
|
var import_node_path27 = require("path");
|
|
15090
16334
|
var import_SubscriptionAccountHealth2 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
15091
16335
|
var import_AccountAllowanceScheduling3 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
15092
|
-
var
|
|
16336
|
+
var import_upstreamFetch9 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
15093
16337
|
var import_SubscriptionIdentityStore2 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
15094
|
-
var
|
|
16338
|
+
var import_subscriptions8 = require("@omnicross/subscriptions");
|
|
15095
16339
|
|
|
15096
16340
|
// src/ports/account-sync.ts
|
|
15097
16341
|
function viewOf(tokens) {
|
|
@@ -15135,7 +16379,7 @@ function findDuplicateCredentialIds(accounts) {
|
|
|
15135
16379
|
}
|
|
15136
16380
|
|
|
15137
16381
|
// src/ports/external-cli-credentials.ts
|
|
15138
|
-
var
|
|
16382
|
+
var import_node_fs27 = require("fs");
|
|
15139
16383
|
var import_node_os5 = require("os");
|
|
15140
16384
|
var import_node_path26 = require("path");
|
|
15141
16385
|
function externalStorePath(provider, home = (0, import_node_os5.homedir)()) {
|
|
@@ -15188,10 +16432,10 @@ function parseCodexTokensEnvelope(raw) {
|
|
|
15188
16432
|
}
|
|
15189
16433
|
function readExternalCliCredentials(provider, home = (0, import_node_os5.homedir)()) {
|
|
15190
16434
|
const path2 = externalStorePath(provider, home);
|
|
15191
|
-
if (!(0,
|
|
16435
|
+
if (!(0, import_node_fs27.existsSync)(path2)) return null;
|
|
15192
16436
|
let raw;
|
|
15193
16437
|
try {
|
|
15194
|
-
const parsed = JSON.parse((0,
|
|
16438
|
+
const parsed = JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8"));
|
|
15195
16439
|
raw = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
15196
16440
|
} catch {
|
|
15197
16441
|
return null;
|
|
@@ -15214,16 +16458,18 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15214
16458
|
* as on relay refresh egresses from the SAME proxy IP as the
|
|
15215
16459
|
* account's traffic. NOT used by any read/write path.
|
|
15216
16460
|
*/
|
|
15217
|
-
constructor(tokensPath, box, fetchImpl = void 0, externalCliReader = readExternalCliCredentials) {
|
|
16461
|
+
constructor(tokensPath, box, fetchImpl = void 0, externalCliReader = readExternalCliCredentials, atomicReplace = atomicReplaceUtf8) {
|
|
15218
16462
|
this.tokensPath = tokensPath;
|
|
15219
16463
|
this.box = box;
|
|
15220
16464
|
this.fetchImpl = fetchImpl;
|
|
15221
16465
|
this.externalCliReader = externalCliReader;
|
|
16466
|
+
this.atomicReplace = atomicReplace;
|
|
15222
16467
|
}
|
|
15223
16468
|
tokensPath;
|
|
15224
16469
|
box;
|
|
15225
16470
|
fetchImpl;
|
|
15226
16471
|
externalCliReader;
|
|
16472
|
+
atomicReplace;
|
|
15227
16473
|
/**
|
|
15228
16474
|
* The proxy-aware `FetchLike` for one refresh round-trip (upstream-proxy M1). A
|
|
15229
16475
|
* TEST-injected `fetchImpl` is returned verbatim; otherwise the refresh routes
|
|
@@ -15237,7 +16483,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15237
16483
|
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
15238
16484
|
*/
|
|
15239
16485
|
buildRefreshFetch(providerId, accountId) {
|
|
15240
|
-
return this.fetchImpl ?? ((url, init) => (0,
|
|
16486
|
+
return this.fetchImpl ?? ((url, init) => (0, import_upstreamFetch9.fetchUpstream)(url, init, { providerId, accountId, redactBodies: true }));
|
|
15241
16487
|
}
|
|
15242
16488
|
/**
|
|
15243
16489
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -15278,7 +16524,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15278
16524
|
* other hot reads. Never returns token material.
|
|
15279
16525
|
*/
|
|
15280
16526
|
getAccountProxy(providerId, accountId) {
|
|
15281
|
-
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego") {
|
|
16527
|
+
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi") {
|
|
15282
16528
|
return void 0;
|
|
15283
16529
|
}
|
|
15284
16530
|
return getAccountProxy(this.readConfig(), providerId, accountId);
|
|
@@ -15297,7 +16543,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15297
16543
|
const fingerprintOn = identityStore.isEnabled();
|
|
15298
16544
|
const now = Date.now();
|
|
15299
16545
|
const out = {};
|
|
15300
|
-
for (const provider of ["claude", "codex", "gemini", "opencodego"]) {
|
|
16546
|
+
for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi"]) {
|
|
15301
16547
|
const sanitized = sanitizeAccounts(config, provider);
|
|
15302
16548
|
if (sanitized.length === 0) continue;
|
|
15303
16549
|
for (const account of sanitized) {
|
|
@@ -15363,7 +16609,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15363
16609
|
this.materializeMigration(config);
|
|
15364
16610
|
const refreshFetch = this.buildRefreshFetch("claude", capturedId);
|
|
15365
16611
|
try {
|
|
15366
|
-
const result = await
|
|
16612
|
+
const result = await import_subscriptions8.claudeOAuth.refreshAccessToken(claude.refreshToken, refreshFetch);
|
|
15367
16613
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
15368
16614
|
const next = {
|
|
15369
16615
|
...claude,
|
|
@@ -15398,7 +16644,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15398
16644
|
this.materializeMigration(config);
|
|
15399
16645
|
const refreshFetch = this.buildRefreshFetch("codex", capturedId);
|
|
15400
16646
|
try {
|
|
15401
|
-
const result = await
|
|
16647
|
+
const result = await import_subscriptions8.codexOAuth.refreshAccessToken(codex.refreshToken, refreshFetch);
|
|
15402
16648
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
15403
16649
|
const next = {
|
|
15404
16650
|
...codex,
|
|
@@ -15436,7 +16682,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15436
16682
|
this.materializeMigration(config);
|
|
15437
16683
|
const refreshFetch = this.buildRefreshFetch("gemini", capturedId);
|
|
15438
16684
|
try {
|
|
15439
|
-
const result = await
|
|
16685
|
+
const result = await import_subscriptions8.geminiOAuth.refreshAccessToken(gemini.refreshToken, refreshFetch);
|
|
15440
16686
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
15441
16687
|
const next = {
|
|
15442
16688
|
...gemini,
|
|
@@ -15455,6 +16701,47 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15455
16701
|
}
|
|
15456
16702
|
});
|
|
15457
16703
|
}
|
|
16704
|
+
/**
|
|
16705
|
+
* Refresh the Kimi Code (Moonshot) OAuth access token (device-flow grant).
|
|
16706
|
+
* Kimi ROTATES the refresh token, so the response's pair is written back
|
|
16707
|
+
* whole; the account's stable `deviceId` (fingerprint header input) is
|
|
16708
|
+
* preserved. The refresh call carries the CLI fingerprint headers. HONEST
|
|
16709
|
+
* `false` when no refresh_token.
|
|
16710
|
+
*/
|
|
16711
|
+
async refreshKimiToken() {
|
|
16712
|
+
return this.coalesce("kimi:active", async () => {
|
|
16713
|
+
const config = this.readConfig();
|
|
16714
|
+
const active = getActiveAccount(config, "kimi");
|
|
16715
|
+
const kimi = active?.tokens;
|
|
16716
|
+
if (!active || !kimi?.refreshToken) return false;
|
|
16717
|
+
const capturedId = active.id;
|
|
16718
|
+
this.materializeMigration(config);
|
|
16719
|
+
const refreshFetch = this.buildRefreshFetch("kimi", capturedId);
|
|
16720
|
+
try {
|
|
16721
|
+
const result = await import_subscriptions8.kimiOAuth.refreshAccessToken(
|
|
16722
|
+
kimi.refreshToken,
|
|
16723
|
+
refreshFetch,
|
|
16724
|
+
import_subscriptions8.kimiOAuth.kimiFingerprintHeaders(kimi.deviceId)
|
|
16725
|
+
);
|
|
16726
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
16727
|
+
const next = {
|
|
16728
|
+
...kimi,
|
|
16729
|
+
accessToken: result.accessToken,
|
|
16730
|
+
refreshToken: result.refreshToken,
|
|
16731
|
+
expiresAt,
|
|
16732
|
+
status: "authorized",
|
|
16733
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16734
|
+
errorMessage: void 0,
|
|
16735
|
+
syncWarning: void 0
|
|
16736
|
+
};
|
|
16737
|
+
this.writeBackById("kimi", capturedId, next);
|
|
16738
|
+
return true;
|
|
16739
|
+
} catch (error) {
|
|
16740
|
+
this.markExpiredById("kimi", capturedId, kimi, error);
|
|
16741
|
+
return false;
|
|
16742
|
+
}
|
|
16743
|
+
});
|
|
16744
|
+
}
|
|
15458
16745
|
/**
|
|
15459
16746
|
* Refresh a SPECIFIC managed account by id (background scheduler sweep and
|
|
15460
16747
|
* account-pool resolution). It uses only that account's stored refresh
|
|
@@ -15507,7 +16794,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15507
16794
|
}
|
|
15508
16795
|
const oauth = account.tokens;
|
|
15509
16796
|
if (!oauth.accessToken) return null;
|
|
15510
|
-
if (providerId === "codex" || providerId === "gemini") {
|
|
16797
|
+
if (providerId === "codex" || providerId === "gemini" || providerId === "kimi") {
|
|
15511
16798
|
const expiresAtMs = oauth.expiresAt ? Date.parse(oauth.expiresAt) : 0;
|
|
15512
16799
|
const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - ACCOUNT_REFRESH_LEAD_MS;
|
|
15513
16800
|
if (expiringSoon && oauth.refreshToken) {
|
|
@@ -15596,8 +16883,23 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15596
16883
|
}
|
|
15597
16884
|
/** Dispatch one OAuth refresh round-trip to the provider's shared flow. */
|
|
15598
16885
|
async refreshUpstream(provider, refreshToken, accountId) {
|
|
15599
|
-
const
|
|
15600
|
-
|
|
16886
|
+
const refreshFetch = this.buildRefreshFetch(provider, accountId);
|
|
16887
|
+
if (provider === "kimi") {
|
|
16888
|
+
const account = accountId ? getAccountById(this.readConfig(), "kimi", accountId) : void 0;
|
|
16889
|
+
const deviceId = account?.tokens?.deviceId;
|
|
16890
|
+
const r2 = await import_subscriptions8.kimiOAuth.refreshAccessToken(
|
|
16891
|
+
refreshToken,
|
|
16892
|
+
refreshFetch,
|
|
16893
|
+
import_subscriptions8.kimiOAuth.kimiFingerprintHeaders(deviceId)
|
|
16894
|
+
);
|
|
16895
|
+
return {
|
|
16896
|
+
accessToken: r2.accessToken,
|
|
16897
|
+
refreshToken: r2.refreshToken,
|
|
16898
|
+
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
16899
|
+
};
|
|
16900
|
+
}
|
|
16901
|
+
const flow = provider === "claude" ? import_subscriptions8.claudeOAuth : provider === "codex" ? import_subscriptions8.codexOAuth : import_subscriptions8.geminiOAuth;
|
|
16902
|
+
const r = await flow.refreshAccessToken(refreshToken, refreshFetch);
|
|
15601
16903
|
return {
|
|
15602
16904
|
accessToken: r.accessToken,
|
|
15603
16905
|
refreshToken: r.refreshToken,
|
|
@@ -15760,42 +17062,86 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15760
17062
|
/** Write the merged config to disk as pretty JSON (mkdir parent if needed).
|
|
15761
17063
|
* Encrypt-on-write: the token-material fields are encrypted (legacy plaintext
|
|
15762
17064
|
* `enc:v1:`; already-`enc:`/`$ENV` untouched) before serializing, so any
|
|
15763
|
-
* write incl. child 4's future refresh writes lands encrypted.
|
|
17065
|
+
* write incl. child 4's future refresh writes lands encrypted.
|
|
17066
|
+
* ATOMIC: temp + fsync + rename (`atomicReplaceUtf8`) — a failed or
|
|
17067
|
+
* interrupted write discards only the temp file; the prior `tokens.json`
|
|
17068
|
+
* survives byte-equal (bare `writeFileSync` truncate-writes lost every
|
|
17069
|
+
* account on a mid-write failure, 2026-09-06). */
|
|
15764
17070
|
persist(config) {
|
|
15765
|
-
(0,
|
|
17071
|
+
(0, import_node_fs28.mkdirSync)((0, import_node_path27.dirname)(this.tokensPath), { recursive: true });
|
|
15766
17072
|
const encrypted = encryptTokens(config, this.box);
|
|
15767
|
-
|
|
17073
|
+
this.atomicReplace(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n");
|
|
15768
17074
|
}
|
|
15769
17075
|
/**
|
|
15770
|
-
* Read + parse `tokens.json`,
|
|
15771
|
-
*
|
|
15772
|
-
*
|
|
17076
|
+
* Read + parse `tokens.json`, then DECRYPT the token-material fields so every
|
|
17077
|
+
* getter returns plaintext (the subscription bearer path is byte-identical).
|
|
17078
|
+
*
|
|
17079
|
+
* A MISSING file is a legitimate first-boot state → minimal `{ updatedAt: '' }`.
|
|
17080
|
+
* A file that EXISTS but cannot be parsed as a JSON object is CORRUPT →
|
|
17081
|
+
* `quarantineCorrupt` moves it aside (once) before the empty config is
|
|
17082
|
+
* returned, so the unreadable accounts survive for manual recovery.
|
|
15773
17083
|
*
|
|
15774
|
-
* The
|
|
15775
|
-
*
|
|
15776
|
-
*
|
|
15777
|
-
*
|
|
15778
|
-
*
|
|
15779
|
-
*
|
|
15780
|
-
* `config.ts loadConfig`, which decrypts outside its parse try.
|
|
17084
|
+
* The DECRYPT runs OUTSIDE any try, so a wrong/missing master key or a
|
|
17085
|
+
* tampered `enc:` envelope FAILS FAST with the box's clear, secret-free
|
|
17086
|
+
* error (secrets spec "/ UX": SHALL fail-fast, SHALL NOT a swallowed
|
|
17087
|
+
* decrypt would report "no tokens" and silently send the WRONG bearer
|
|
17088
|
+
* upstream 401). Mirrors `config.ts loadConfig`, which decrypts outside
|
|
17089
|
+
* its parse try.
|
|
15781
17090
|
*/
|
|
15782
17091
|
readConfig() {
|
|
15783
|
-
if (!(0,
|
|
17092
|
+
if (!(0, import_node_fs28.existsSync)(this.tokensPath)) return { updatedAt: "" };
|
|
15784
17093
|
let parsed;
|
|
15785
17094
|
try {
|
|
15786
|
-
const raw = JSON.parse((0,
|
|
15787
|
-
|
|
17095
|
+
const raw = JSON.parse((0, import_node_fs28.readFileSync)(this.tokensPath, "utf8"));
|
|
17096
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
17097
|
+
return this.quarantineCorrupt("parsed JSON is not an object");
|
|
17098
|
+
}
|
|
17099
|
+
parsed = raw;
|
|
15788
17100
|
} catch {
|
|
15789
|
-
|
|
17101
|
+
return this.quarantineCorrupt("unparseable JSON");
|
|
15790
17102
|
}
|
|
15791
|
-
if (!parsed) return { updatedAt: "" };
|
|
15792
17103
|
const decrypted = decryptTokens(parsed, this.box);
|
|
15793
17104
|
return migrateLazily(decrypted);
|
|
15794
17105
|
}
|
|
17106
|
+
/** One-shot latch: a corrupt file is quarantined (or found unmovable) at
|
|
17107
|
+
* most once per process, so the hot read path never re-attempts or re-logs. */
|
|
17108
|
+
corruptQuarantined = false;
|
|
17109
|
+
/**
|
|
17110
|
+
* Quarantine a present-but-corrupt `tokens.json`, then treat it as empty.
|
|
17111
|
+
*
|
|
17112
|
+
* Renames the file to a sibling `tokens.json.corrupt-<stamp>` backup and
|
|
17113
|
+
* logs loudly (the daemon's stderr log; secret-free — reason + paths only).
|
|
17114
|
+
* The daemon KEEPS SERVING (API-key routing is unaffected; subscription
|
|
17115
|
+
* routing reports no credential, same as an absent file) while the corrupt
|
|
17116
|
+
* bytes survive for manual recovery — and, critically, the NEXT persist
|
|
17117
|
+
* (e.g. the user re-logging in) can no longer overwrite the only copy of
|
|
17118
|
+
* the old accounts, which is exactly how the 2026-09-06 incident turned a
|
|
17119
|
+
* recoverable truncated file into permanent account loss.
|
|
17120
|
+
*
|
|
17121
|
+
* Best-effort: if the rename fails (file locked, permissions), the corrupt
|
|
17122
|
+
* file is left in place and every later read still tolerates it as empty;
|
|
17123
|
+
* the latch still trips so the attempt + log happen exactly once.
|
|
17124
|
+
*/
|
|
17125
|
+
quarantineCorrupt(reason) {
|
|
17126
|
+
if (!this.corruptQuarantined) {
|
|
17127
|
+
this.corruptQuarantined = true;
|
|
17128
|
+
const backup = `${this.tokensPath}.corrupt-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
17129
|
+
let moved = false;
|
|
17130
|
+
try {
|
|
17131
|
+
(0, import_node_fs28.renameSync)(this.tokensPath, backup);
|
|
17132
|
+
moved = true;
|
|
17133
|
+
} catch {
|
|
17134
|
+
}
|
|
17135
|
+
console.error(
|
|
17136
|
+
`[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`)
|
|
17137
|
+
);
|
|
17138
|
+
}
|
|
17139
|
+
return { updatedAt: "" };
|
|
17140
|
+
}
|
|
15795
17141
|
};
|
|
15796
17142
|
|
|
15797
17143
|
// src/AccountHealthProbeScheduler.ts
|
|
15798
|
-
var
|
|
17144
|
+
var import_upstreamFetch10 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
15799
17145
|
|
|
15800
17146
|
// src/probe/CodexGenerationProbe.ts
|
|
15801
17147
|
var import_codexCliHeaders = require("@omnicross/core/provider-proxy/identity/codexCliHeaders");
|
|
@@ -15934,7 +17280,11 @@ var PROVIDER_PROBE_PLANS = {
|
|
|
15934
17280
|
// billable/wrong endpoint). Upgrade to `{ kind:'upstream' }` once verified.
|
|
15935
17281
|
codex: { kind: "local" },
|
|
15936
17282
|
gemini: { kind: "local" },
|
|
15937
|
-
opencodego: { kind: "local" }
|
|
17283
|
+
opencodego: { kind: "local" },
|
|
17284
|
+
// Kimi's `GET /coding/v1/usages` is a verified FREE authed GET (the allowance
|
|
17285
|
+
// collector uses it), but the probe path also needs the fingerprint headers —
|
|
17286
|
+
// keep the probe local until the collector covers the health surface.
|
|
17287
|
+
kimi: { kind: "local" }
|
|
15938
17288
|
};
|
|
15939
17289
|
function probePlanFor(providerId) {
|
|
15940
17290
|
return PROVIDER_PROBE_PLANS[providerId] ?? { kind: "local" };
|
|
@@ -15956,7 +17306,7 @@ var AccountHealthProbeScheduler = class {
|
|
|
15956
17306
|
this.logger = logger;
|
|
15957
17307
|
this.config = config;
|
|
15958
17308
|
this.now = opts.now ?? Date.now;
|
|
15959
|
-
this.fetchImpl = opts.fetchImpl ??
|
|
17309
|
+
this.fetchImpl = opts.fetchImpl ?? import_upstreamFetch10.fetchUpstream;
|
|
15960
17310
|
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
15961
17311
|
this.planFor = opts.planFor ?? probePlanFor;
|
|
15962
17312
|
}
|
|
@@ -16300,13 +17650,13 @@ var AccountHealthSweeper = class {
|
|
|
16300
17650
|
};
|
|
16301
17651
|
|
|
16302
17652
|
// src/audit/AuditPruneSweeper.ts
|
|
16303
|
-
var
|
|
17653
|
+
var import_node_fs30 = require("fs");
|
|
16304
17654
|
var import_node_path29 = require("path");
|
|
16305
17655
|
var import_promises6 = require("stream/promises");
|
|
16306
17656
|
var import_node_zlib2 = require("zlib");
|
|
16307
17657
|
|
|
16308
17658
|
// src/audit/auditStats.ts
|
|
16309
|
-
var
|
|
17659
|
+
var import_node_fs29 = require("fs");
|
|
16310
17660
|
var import_node_path28 = require("path");
|
|
16311
17661
|
var SIDECAR_VERSION = 1;
|
|
16312
17662
|
var META_PREFIX_BYTES = 64 * 1024;
|
|
@@ -16315,9 +17665,9 @@ function auditStatsFileName(auditFile) {
|
|
|
16315
17665
|
return auditFile.replace(/\.jsonl$/, ".stats.json");
|
|
16316
17666
|
}
|
|
16317
17667
|
function readPersisted(path2) {
|
|
16318
|
-
if (!(0,
|
|
17668
|
+
if (!(0, import_node_fs29.existsSync)(path2)) return null;
|
|
16319
17669
|
try {
|
|
16320
|
-
const value = JSON.parse((0,
|
|
17670
|
+
const value = JSON.parse((0, import_node_fs29.readFileSync)(path2, "utf8"));
|
|
16321
17671
|
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
17672
|
return null;
|
|
16323
17673
|
}
|
|
@@ -16347,7 +17697,7 @@ function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfte
|
|
|
16347
17697
|
minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
|
|
16348
17698
|
maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
|
|
16349
17699
|
};
|
|
16350
|
-
(0,
|
|
17700
|
+
(0, import_node_fs29.writeFileSync)(statsPath, JSON.stringify(next), "utf8");
|
|
16351
17701
|
}
|
|
16352
17702
|
function queryCovers(stats, from, to) {
|
|
16353
17703
|
return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
|
|
@@ -16405,7 +17755,7 @@ async function scanAuditFile(auditPath, startByte, auditBytes, from, to) {
|
|
|
16405
17755
|
prefixTruncated = false;
|
|
16406
17756
|
};
|
|
16407
17757
|
if (auditBytes > startByte) {
|
|
16408
|
-
const stream = (0,
|
|
17758
|
+
const stream = (0, import_node_fs29.createReadStream)(auditPath, {
|
|
16409
17759
|
start: startByte,
|
|
16410
17760
|
end: auditBytes - 1,
|
|
16411
17761
|
highWaterMark: READ_CHUNK_BYTES2
|
|
@@ -16458,12 +17808,12 @@ function mergePersistedStats(previous, appended) {
|
|
|
16458
17808
|
};
|
|
16459
17809
|
}
|
|
16460
17810
|
async function readAuditStats(auditDir, query2 = {}) {
|
|
16461
|
-
if (!(0,
|
|
17811
|
+
if (!(0, import_node_fs29.existsSync)(auditDir)) return { requestCount: 0, errorCount: 0, complete: true };
|
|
16462
17812
|
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
16463
17813
|
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
16464
17814
|
let sources;
|
|
16465
17815
|
try {
|
|
16466
|
-
sources = (0,
|
|
17816
|
+
sources = (0, import_node_fs29.readdirSync)(auditDir).filter((name) => fileOverlaps(name, from, to)).sort().map(
|
|
16467
17817
|
(name) => AUDIT_DAY_DIR_RE.test(name) ? {
|
|
16468
17818
|
auditPath: (0, import_node_path28.join)(auditDir, name, AUDIT_META_FILE),
|
|
16469
17819
|
statsPath: (0, import_node_path28.join)(auditDir, name, auditStatsFileName(AUDIT_META_FILE))
|
|
@@ -16471,14 +17821,14 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
16471
17821
|
auditPath: (0, import_node_path28.join)(auditDir, name),
|
|
16472
17822
|
statsPath: (0, import_node_path28.join)(auditDir, auditStatsFileName(name))
|
|
16473
17823
|
}
|
|
16474
|
-
).filter((source) => (0,
|
|
17824
|
+
).filter((source) => (0, import_node_fs29.existsSync)(source.auditPath));
|
|
16475
17825
|
} catch {
|
|
16476
17826
|
return { requestCount: 0, errorCount: 0, complete: false };
|
|
16477
17827
|
}
|
|
16478
17828
|
const total = { requestCount: 0, errorCount: 0, complete: true };
|
|
16479
17829
|
for (const { auditPath, statsPath } of sources) {
|
|
16480
17830
|
try {
|
|
16481
|
-
const auditBytes = (0,
|
|
17831
|
+
const auditBytes = (0, import_node_fs29.statSync)(auditPath).size;
|
|
16482
17832
|
const persisted = readPersisted(statsPath);
|
|
16483
17833
|
if (persisted && persisted.complete && persisted.auditBytes === auditBytes && queryCovers(persisted, from, to)) {
|
|
16484
17834
|
total.requestCount += persisted.requestCount;
|
|
@@ -16497,7 +17847,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
16497
17847
|
total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
|
|
16498
17848
|
total.complete = total.complete && scanned.filtered.complete;
|
|
16499
17849
|
const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
|
|
16500
|
-
if (current.complete) (0,
|
|
17850
|
+
if (current.complete) (0, import_node_fs29.writeFileSync)(statsPath, JSON.stringify(current), "utf8");
|
|
16501
17851
|
} catch {
|
|
16502
17852
|
total.complete = false;
|
|
16503
17853
|
}
|
|
@@ -16506,7 +17856,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
16506
17856
|
}
|
|
16507
17857
|
|
|
16508
17858
|
// src/audit/AuditPruneSweeper.ts
|
|
16509
|
-
var
|
|
17859
|
+
var DAY_MS3 = 24 * 60 * 6e4;
|
|
16510
17860
|
var SWEEP_INTERVAL_MS2 = 60 * 6e4;
|
|
16511
17861
|
var ARCHIVE_BATCH = 64;
|
|
16512
17862
|
var AuditPruneSweeper = class {
|
|
@@ -16569,19 +17919,19 @@ var AuditPruneSweeper = class {
|
|
|
16569
17919
|
if (!this.config.enabled || this.sweeping) return 0;
|
|
16570
17920
|
this.sweeping = true;
|
|
16571
17921
|
try {
|
|
16572
|
-
if (!(0,
|
|
16573
|
-
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) *
|
|
17922
|
+
if (!(0, import_node_fs30.existsSync)(this.auditDir)) return 0;
|
|
17923
|
+
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS3;
|
|
16574
17924
|
let removed = 0;
|
|
16575
|
-
for (const name of (0,
|
|
17925
|
+
for (const name of (0, import_node_fs30.readdirSync)(this.auditDir)) {
|
|
16576
17926
|
const dateMs = auditFileDateMs(name);
|
|
16577
17927
|
if (dateMs === null || dateMs >= cutoff) continue;
|
|
16578
17928
|
try {
|
|
16579
17929
|
if (isAuditDayDir(name)) {
|
|
16580
|
-
(0,
|
|
17930
|
+
(0, import_node_fs30.rmSync)((0, import_node_path29.join)(this.auditDir, name), { recursive: true, force: true });
|
|
16581
17931
|
} else {
|
|
16582
|
-
(0,
|
|
17932
|
+
(0, import_node_fs30.unlinkSync)((0, import_node_path29.join)(this.auditDir, name));
|
|
16583
17933
|
const statsPath = (0, import_node_path29.join)(this.auditDir, auditStatsFileName(name));
|
|
16584
|
-
if ((0,
|
|
17934
|
+
if ((0, import_node_fs30.existsSync)(statsPath)) (0, import_node_fs30.unlinkSync)(statsPath);
|
|
16585
17935
|
}
|
|
16586
17936
|
removed += 1;
|
|
16587
17937
|
} catch (error) {
|
|
@@ -16611,10 +17961,10 @@ var AuditPruneSweeper = class {
|
|
|
16611
17961
|
if (!this.config.enabled || this.archiving) return 0;
|
|
16612
17962
|
this.archiving = true;
|
|
16613
17963
|
try {
|
|
16614
|
-
if (!(0,
|
|
17964
|
+
if (!(0, import_node_fs30.existsSync)(this.auditDir)) return 0;
|
|
16615
17965
|
const today = this.todayMidnight();
|
|
16616
17966
|
let compressed = 0;
|
|
16617
|
-
for (const name of (0,
|
|
17967
|
+
for (const name of (0, import_node_fs30.readdirSync)(this.auditDir)) {
|
|
16618
17968
|
if (compressed >= ARCHIVE_BATCH) break;
|
|
16619
17969
|
const dateMs = auditFileDateMs(name);
|
|
16620
17970
|
if (dateMs === null || dateMs >= today || !isAuditDayDir(name)) continue;
|
|
@@ -16655,7 +18005,7 @@ var AuditPruneSweeper = class {
|
|
|
16655
18005
|
async archiveDay(bodiesPath, budget) {
|
|
16656
18006
|
let shards;
|
|
16657
18007
|
try {
|
|
16658
|
-
shards = (0,
|
|
18008
|
+
shards = (0, import_node_fs30.readdirSync)(bodiesPath).filter((file) => file.endsWith(".jsonl"));
|
|
16659
18009
|
} catch {
|
|
16660
18010
|
return 0;
|
|
16661
18011
|
}
|
|
@@ -16665,16 +18015,16 @@ var AuditPruneSweeper = class {
|
|
|
16665
18015
|
const source = (0, import_node_path29.join)(bodiesPath, shard);
|
|
16666
18016
|
const target = `${source}.gz`;
|
|
16667
18017
|
try {
|
|
16668
|
-
if ((0,
|
|
16669
|
-
(0,
|
|
18018
|
+
if ((0, import_node_fs30.existsSync)(target)) {
|
|
18019
|
+
(0, import_node_fs30.unlinkSync)(source);
|
|
16670
18020
|
continue;
|
|
16671
18021
|
}
|
|
16672
|
-
await (0, import_promises6.pipeline)((0,
|
|
16673
|
-
(0,
|
|
18022
|
+
await (0, import_promises6.pipeline)((0, import_node_fs30.createReadStream)(source), (0, import_node_zlib2.createGzip)(), (0, import_node_fs30.createWriteStream)(target));
|
|
18023
|
+
(0, import_node_fs30.unlinkSync)(source);
|
|
16674
18024
|
compressed += 1;
|
|
16675
18025
|
} catch (error) {
|
|
16676
18026
|
try {
|
|
16677
|
-
if ((0,
|
|
18027
|
+
if ((0, import_node_fs30.existsSync)(target)) (0, import_node_fs30.unlinkSync)(target);
|
|
16678
18028
|
} catch {
|
|
16679
18029
|
}
|
|
16680
18030
|
this.logger.warn("[AuditPruneSweeper] failed to archive audit body shard", {
|
|
@@ -16688,7 +18038,7 @@ var AuditPruneSweeper = class {
|
|
|
16688
18038
|
};
|
|
16689
18039
|
|
|
16690
18040
|
// src/usage/usageMigrate.ts
|
|
16691
|
-
var
|
|
18041
|
+
var import_node_fs31 = require("fs");
|
|
16692
18042
|
var import_promises7 = require("fs/promises");
|
|
16693
18043
|
var import_node_path30 = require("path");
|
|
16694
18044
|
var import_node_readline = require("readline");
|
|
@@ -16735,7 +18085,7 @@ async function migrateLegacyUsageEvents(opts) {
|
|
|
16735
18085
|
let skipped = 0;
|
|
16736
18086
|
try {
|
|
16737
18087
|
const reader = (0, import_node_readline.createInterface)({
|
|
16738
|
-
input: (0,
|
|
18088
|
+
input: (0, import_node_fs31.createReadStream)(eventsPath, { encoding: "utf8" }),
|
|
16739
18089
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
16740
18090
|
});
|
|
16741
18091
|
for await (const line of reader) {
|
|
@@ -16827,7 +18177,7 @@ async function closeAll(writers) {
|
|
|
16827
18177
|
// src/usage/UsagePruneSweeper.ts
|
|
16828
18178
|
var import_promises8 = require("fs/promises");
|
|
16829
18179
|
var import_node_path31 = require("path");
|
|
16830
|
-
var
|
|
18180
|
+
var DAY_MS4 = 24 * 60 * 6e4;
|
|
16831
18181
|
var SWEEP_INTERVAL_MS3 = 60 * 6e4;
|
|
16832
18182
|
var DEFAULT_USAGE_RETENTION_DAYS = 90;
|
|
16833
18183
|
var UsagePruneSweeper = class {
|
|
@@ -16884,7 +18234,7 @@ var UsagePruneSweeper = class {
|
|
|
16884
18234
|
this.sweeping = true;
|
|
16885
18235
|
try {
|
|
16886
18236
|
const retentionDays = this.config.retentionDays ?? DEFAULT_USAGE_RETENTION_DAYS;
|
|
16887
|
-
const cutoff = this.todayMidnight() - (retentionDays - 1) *
|
|
18237
|
+
const cutoff = this.todayMidnight() - (retentionDays - 1) * DAY_MS4;
|
|
16888
18238
|
let removed = 0;
|
|
16889
18239
|
for (const entry of await listUsageDays(this.usageDir)) {
|
|
16890
18240
|
if (!entry.hasShard) continue;
|
|
@@ -16942,7 +18292,7 @@ var UsagePruneSweeper = class {
|
|
|
16942
18292
|
};
|
|
16943
18293
|
|
|
16944
18294
|
// src/audit/auditReader.ts
|
|
16945
|
-
var
|
|
18295
|
+
var import_node_fs32 = require("fs");
|
|
16946
18296
|
var import_node_path32 = require("path");
|
|
16947
18297
|
var DEFAULT_LIMIT = 200;
|
|
16948
18298
|
var MAX_LIMIT = 2e3;
|
|
@@ -16950,7 +18300,7 @@ var OVERSCAN = 256;
|
|
|
16950
18300
|
function daySources(auditDir) {
|
|
16951
18301
|
let names;
|
|
16952
18302
|
try {
|
|
16953
|
-
names = (0,
|
|
18303
|
+
names = (0, import_node_fs32.readdirSync)(auditDir);
|
|
16954
18304
|
} catch {
|
|
16955
18305
|
return [];
|
|
16956
18306
|
}
|
|
@@ -16960,7 +18310,7 @@ function daySources(auditDir) {
|
|
|
16960
18310
|
if (dateMs === null) continue;
|
|
16961
18311
|
if (AUDIT_DAY_DIR_RE.test(name)) {
|
|
16962
18312
|
const path2 = (0, import_node_path32.join)(auditDir, name, AUDIT_META_FILE);
|
|
16963
|
-
if ((0,
|
|
18313
|
+
if ((0, import_node_fs32.existsSync)(path2)) sources.push({ path: path2, dateMs });
|
|
16964
18314
|
} else if (AUDIT_FILE_RE.test(name)) {
|
|
16965
18315
|
sources.push({ path: (0, import_node_path32.join)(auditDir, name), dateMs });
|
|
16966
18316
|
}
|
|
@@ -16978,7 +18328,7 @@ function toMetaRecord(record) {
|
|
|
16978
18328
|
return { ...meta, hasBody: true };
|
|
16979
18329
|
}
|
|
16980
18330
|
function readAuditRecords(auditDir, query2 = {}) {
|
|
16981
|
-
if (!(0,
|
|
18331
|
+
if (!(0, import_node_fs32.existsSync)(auditDir)) return [];
|
|
16982
18332
|
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
16983
18333
|
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
16984
18334
|
const limit = Math.min(MAX_LIMIT, Math.max(1, Math.trunc(query2.limit ?? DEFAULT_LIMIT)));
|
|
@@ -17006,7 +18356,7 @@ function readAuditRecords(auditDir, query2 = {}) {
|
|
|
17006
18356
|
}
|
|
17007
18357
|
|
|
17008
18358
|
// src/audit/AuditWriter.ts
|
|
17009
|
-
var
|
|
18359
|
+
var import_node_fs33 = require("fs");
|
|
17010
18360
|
var import_node_path33 = require("path");
|
|
17011
18361
|
var AuditWriter = class {
|
|
17012
18362
|
constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
|
|
@@ -17054,7 +18404,7 @@ var AuditWriter = class {
|
|
|
17054
18404
|
/** Create a directory once per process and remember it. */
|
|
17055
18405
|
ensureDir(path2) {
|
|
17056
18406
|
if (!this.ensuredDirs.has(path2)) {
|
|
17057
|
-
(0,
|
|
18407
|
+
(0, import_node_fs33.mkdirSync)(path2, { recursive: true });
|
|
17058
18408
|
this.ensuredDirs.add(path2);
|
|
17059
18409
|
}
|
|
17060
18410
|
return path2;
|
|
@@ -17064,8 +18414,8 @@ var AuditWriter = class {
|
|
|
17064
18414
|
const { requestBody: _req, responseBody: _res, ...meta } = record;
|
|
17065
18415
|
const file = (0, import_node_path33.join)(dayPath, AUDIT_META_FILE);
|
|
17066
18416
|
const line = JSON.stringify(meta) + "\n";
|
|
17067
|
-
const bytesBefore = (0,
|
|
17068
|
-
(0,
|
|
18417
|
+
const bytesBefore = (0, import_node_fs33.existsSync)(file) ? (0, import_node_fs33.statSync)(file).size : 0;
|
|
18418
|
+
(0, import_node_fs33.appendFileSync)(file, line, "utf8");
|
|
17069
18419
|
try {
|
|
17070
18420
|
updateAuditStatsAfterAppend(
|
|
17071
18421
|
file,
|
|
@@ -17097,7 +18447,7 @@ var AuditWriter = class {
|
|
|
17097
18447
|
const line = encodeBodyEntry(record, sessionKey, dayDir, this.bases);
|
|
17098
18448
|
if (line === null) return;
|
|
17099
18449
|
const bodiesPath = this.ensureDir((0, import_node_path33.join)(dayPath, AUDIT_BODIES_DIR));
|
|
17100
|
-
(0,
|
|
18450
|
+
(0, import_node_fs33.appendFileSync)((0, import_node_path33.join)(bodiesPath, auditBodyFileName(sessionKey)), line + "\n", "utf8");
|
|
17101
18451
|
} catch (error) {
|
|
17102
18452
|
this.bases.forget(sessionKey);
|
|
17103
18453
|
this.logger.warn("[AuditWriter] failed to append audit body shard", {
|
|
@@ -17109,10 +18459,10 @@ var AuditWriter = class {
|
|
|
17109
18459
|
};
|
|
17110
18460
|
|
|
17111
18461
|
// src/billing/BillingPublisher.ts
|
|
17112
|
-
var
|
|
18462
|
+
var import_node_fs34 = require("fs");
|
|
17113
18463
|
var import_node_crypto24 = require("crypto");
|
|
17114
18464
|
var import_node_path34 = require("path");
|
|
17115
|
-
var
|
|
18465
|
+
var import_upstreamFetch11 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
17116
18466
|
|
|
17117
18467
|
// src/billing/billingFiles.ts
|
|
17118
18468
|
var BILLING_FILE_RE = /^billing-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
|
|
@@ -17135,7 +18485,7 @@ var BillingPublisher = class {
|
|
|
17135
18485
|
constructor(billingDir, logger, opts = {}) {
|
|
17136
18486
|
this.billingDir = billingDir;
|
|
17137
18487
|
this.logger = logger;
|
|
17138
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
18488
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch11.fetchUpstream)(url, init));
|
|
17139
18489
|
this.defer = opts.defer ?? ((fn) => setTimeout(fn, 0));
|
|
17140
18490
|
this.timeoutMs = opts.timeoutMs ?? BILLING_POST_TIMEOUT_MS;
|
|
17141
18491
|
this.now = opts.now ?? Date.now;
|
|
@@ -17183,7 +18533,7 @@ var BillingPublisher = class {
|
|
|
17183
18533
|
appendNow(event) {
|
|
17184
18534
|
this.ensureDir();
|
|
17185
18535
|
const file = (0, import_node_path34.join)(this.billingDir, billingFileName(event.ts));
|
|
17186
|
-
(0,
|
|
18536
|
+
(0, import_node_fs34.appendFileSync)(file, JSON.stringify(event) + "\n", "utf8");
|
|
17187
18537
|
}
|
|
17188
18538
|
/**
|
|
17189
18539
|
* One best-effort delivery attempt for an event ALREADY in the ledger. POSTs the
|
|
@@ -17233,7 +18583,7 @@ var BillingPublisher = class {
|
|
|
17233
18583
|
try {
|
|
17234
18584
|
this.ensureDir();
|
|
17235
18585
|
const file = (0, import_node_path34.join)(this.billingDir, deliveredFileName(event.ts));
|
|
17236
|
-
(0,
|
|
18586
|
+
(0, import_node_fs34.appendFileSync)(file, JSON.stringify({ id: event.id, deliveredAt: this.now() }) + "\n", "utf8");
|
|
17237
18587
|
} catch (error) {
|
|
17238
18588
|
this.logger.warn("[BillingPublisher] failed to append delivery marker", {
|
|
17239
18589
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -17242,20 +18592,20 @@ var BillingPublisher = class {
|
|
|
17242
18592
|
}
|
|
17243
18593
|
ensureDir() {
|
|
17244
18594
|
if (this.dirEnsured) return;
|
|
17245
|
-
(0,
|
|
18595
|
+
(0, import_node_fs34.mkdirSync)(this.billingDir, { recursive: true });
|
|
17246
18596
|
this.dirEnsured = true;
|
|
17247
18597
|
}
|
|
17248
18598
|
};
|
|
17249
18599
|
|
|
17250
18600
|
// src/billing/billingReader.ts
|
|
17251
|
-
var
|
|
18601
|
+
var import_node_fs35 = require("fs");
|
|
17252
18602
|
var import_node_path35 = require("path");
|
|
17253
18603
|
function readBillingLedger(billingDir) {
|
|
17254
18604
|
const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
|
|
17255
|
-
if (!(0,
|
|
18605
|
+
if (!(0, import_node_fs35.existsSync)(billingDir)) return view;
|
|
17256
18606
|
let files;
|
|
17257
18607
|
try {
|
|
17258
|
-
files = (0,
|
|
18608
|
+
files = (0, import_node_fs35.readdirSync)(billingDir);
|
|
17259
18609
|
} catch {
|
|
17260
18610
|
return view;
|
|
17261
18611
|
}
|
|
@@ -17286,7 +18636,7 @@ function readBillingStatus(billingDir) {
|
|
|
17286
18636
|
function parseLines(dir, file) {
|
|
17287
18637
|
let raw;
|
|
17288
18638
|
try {
|
|
17289
|
-
raw = (0,
|
|
18639
|
+
raw = (0, import_node_fs35.readFileSync)((0, import_node_path35.join)(dir, file), "utf8");
|
|
17290
18640
|
} catch {
|
|
17291
18641
|
return [];
|
|
17292
18642
|
}
|
|
@@ -17385,7 +18735,7 @@ var BillingRetrySweeper = class {
|
|
|
17385
18735
|
// src/TokenRefreshScheduler.ts
|
|
17386
18736
|
var REFRESH_LEAD_MS2 = 5 * 6e4;
|
|
17387
18737
|
var SWEEP_INTERVAL_MS5 = 6e4;
|
|
17388
|
-
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini"];
|
|
18738
|
+
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
|
|
17389
18739
|
var TokenRefreshScheduler = class {
|
|
17390
18740
|
constructor(store, logger, intervalMs = SWEEP_INTERVAL_MS5, leadMs = REFRESH_LEAD_MS2) {
|
|
17391
18741
|
this.store = store;
|
|
@@ -17468,6 +18818,8 @@ var TokenRefreshScheduler = class {
|
|
|
17468
18818
|
return this.store.refreshCodexToken();
|
|
17469
18819
|
case "gemini":
|
|
17470
18820
|
return this.store.refreshGeminiToken();
|
|
18821
|
+
case "kimi":
|
|
18822
|
+
return this.store.refreshKimiToken();
|
|
17471
18823
|
}
|
|
17472
18824
|
}
|
|
17473
18825
|
};
|
|
@@ -17544,7 +18896,7 @@ function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
|
17544
18896
|
|
|
17545
18897
|
// src/webhook/WebhookDispatcher.ts
|
|
17546
18898
|
var import_node_crypto25 = require("crypto");
|
|
17547
|
-
var
|
|
18899
|
+
var import_upstreamFetch12 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
17548
18900
|
var WEBHOOK_MAX_ATTEMPTS = 3;
|
|
17549
18901
|
var WEBHOOK_QUEUE_MAX = 1e3;
|
|
17550
18902
|
var WEBHOOK_SEND_TIMEOUT_MS = 1e4;
|
|
@@ -17564,7 +18916,7 @@ var WebhookDispatcher = class {
|
|
|
17564
18916
|
sleep;
|
|
17565
18917
|
now;
|
|
17566
18918
|
constructor(opts = {}) {
|
|
17567
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
18919
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch12.fetchUpstream)(url, init));
|
|
17568
18920
|
this.logger = opts.logger;
|
|
17569
18921
|
this.maxAttempts = opts.maxAttempts ?? WEBHOOK_MAX_ATTEMPTS;
|
|
17570
18922
|
this.queueMax = opts.queueMax ?? WEBHOOK_QUEUE_MAX;
|
|
@@ -17650,8 +19002,8 @@ var WebhookDispatcher = class {
|
|
|
17650
19002
|
signal: AbortSignal.timeout(this.timeoutMs)
|
|
17651
19003
|
});
|
|
17652
19004
|
return res.ok ? { ok: true, status: res.status } : { ok: false, status: res.status };
|
|
17653
|
-
} catch (
|
|
17654
|
-
return { ok: false, error:
|
|
19005
|
+
} catch (err6) {
|
|
19006
|
+
return { ok: false, error: err6 instanceof Error ? err6.message : String(err6) };
|
|
17655
19007
|
}
|
|
17656
19008
|
}
|
|
17657
19009
|
/**
|
|
@@ -17770,7 +19122,7 @@ function installImageRuntimeBootstrapSession(initialGeneration) {
|
|
|
17770
19122
|
function resolveLoggingConfig(configured, configPath) {
|
|
17771
19123
|
const file = configured?.file ?? defaultDaemonLogPath(configPath);
|
|
17772
19124
|
try {
|
|
17773
|
-
(0,
|
|
19125
|
+
(0, import_node_fs36.mkdirSync)(configured?.file ? (0, import_node_path36.dirname)(configured.file) : defaultLogDir(configPath), {
|
|
17774
19126
|
recursive: true
|
|
17775
19127
|
});
|
|
17776
19128
|
} catch {
|
|
@@ -17788,12 +19140,12 @@ function buildDaemon(config, paths) {
|
|
|
17788
19140
|
setSecretBox(secretBox3);
|
|
17789
19141
|
setSecretBox2(secretBox3);
|
|
17790
19142
|
const decryptedConfig = decryptConfigSecrets(config, secretBox3);
|
|
17791
|
-
const accountAllowanceStore = new
|
|
19143
|
+
const accountAllowanceStore = new import_AccountAllowanceStore7.AccountAllowanceStore(
|
|
17792
19144
|
Date.now,
|
|
17793
19145
|
void 0,
|
|
17794
19146
|
new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
|
|
17795
19147
|
);
|
|
17796
|
-
(0,
|
|
19148
|
+
(0, import_AccountAllowanceStore7.setSharedAccountAllowanceStore)(accountAllowanceStore);
|
|
17797
19149
|
(0, import_AccountAllowanceScheduling5.getSharedAccountAllowanceScheduling)().configure(
|
|
17798
19150
|
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
17799
19151
|
);
|
|
@@ -17818,21 +19170,22 @@ function buildDaemon(config, paths) {
|
|
|
17818
19170
|
claudeAllowanceRefreshScheduler.configure(
|
|
17819
19171
|
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
17820
19172
|
);
|
|
17821
|
-
const subscriptionAccounts = new
|
|
17822
|
-
(0,
|
|
17823
|
-
const subscriptionRegistry = new
|
|
19173
|
+
const subscriptionAccounts = new import_subscriptions9.SubscriptionAccountService(credentialStore);
|
|
19174
|
+
(0, import_subscriptions9.setSubscriptionAccountService)(subscriptionAccounts);
|
|
19175
|
+
const subscriptionRegistry = new import_subscriptions9.SubscriptionProviderRegistry(
|
|
17824
19176
|
subscriptionAccounts,
|
|
17825
19177
|
credentialStore
|
|
17826
19178
|
);
|
|
17827
|
-
(0,
|
|
19179
|
+
(0, import_subscriptions9.setSubscriptionProviderRegistry)(subscriptionRegistry);
|
|
17828
19180
|
setServerProxyConfig(decryptedConfig.server?.proxy);
|
|
17829
|
-
(0,
|
|
19181
|
+
(0, import_upstreamFetch13.setUpstreamProxyResolver)(
|
|
17830
19182
|
createUpstreamProxyResolver({
|
|
17831
19183
|
getAccountProxy: (providerId, accountId) => credentialStore.getAccountProxy(providerId, accountId)
|
|
17832
19184
|
})
|
|
17833
19185
|
);
|
|
17834
19186
|
(0, import_gemini_code_assist_resolver.setGeminiCodeAssistResolver)((0, import_GeminiCodeAssistProjectResolver.getGeminiCodeAssistProjectResolver)());
|
|
17835
19187
|
const autoDisableStore = new AutoDisableStore();
|
|
19188
|
+
const providerKeyQuotaService = new ProviderKeyQuotaService(secretBox3);
|
|
17836
19189
|
const apiKeyPool = new import_ApiKeyPoolService.ApiKeyPoolService(
|
|
17837
19190
|
createPoolKeysLoader((id) => llmConfig.getProviderRow(id), autoDisableStore),
|
|
17838
19191
|
resolveEnvKey,
|
|
@@ -17849,7 +19202,7 @@ function buildDaemon(config, paths) {
|
|
|
17849
19202
|
const pricingEngine = new import_usage2.PricingEngine(pricingStore, logger, {
|
|
17850
19203
|
// Catalog egress follows the same global/env proxy policy as every other
|
|
17851
19204
|
// daemon upstream call; no provider/account override applies here.
|
|
17852
|
-
fetchImpl: ((input, init) => (0,
|
|
19205
|
+
fetchImpl: ((input, init) => (0, import_upstreamFetch13.fetchUpstream)(String(input), init ?? {}))
|
|
17853
19206
|
});
|
|
17854
19207
|
const pricingRefreshScheduler = new PricingRefreshScheduler(
|
|
17855
19208
|
pricingEngine,
|
|
@@ -18113,6 +19466,11 @@ function buildDaemon(config, paths) {
|
|
|
18113
19466
|
// values themselves NEVER leave (masked via `maskProviderApiKey`).
|
|
18114
19467
|
apiKeyPool,
|
|
18115
19468
|
autoDisableStore,
|
|
19469
|
+
// BYO provider-key quota (Z.AI coding plan, MiniMax Token Plan, …): a
|
|
19470
|
+
// read-through cached same-key usage probe surfaced on the keys view. The
|
|
19471
|
+
// key plaintext is resolved + decrypted inside the service and never
|
|
19472
|
+
// crosses back out.
|
|
19473
|
+
providerKeyQuota: providerKeyQuotaService,
|
|
18116
19474
|
// Interactive OAuth login over admin HTTP (app-parity child 4, design
|
|
18117
19475
|
// D1/D2-a). The in-memory pending-session store (NEVER serialized), the
|
|
18118
19476
|
// injected token-exchange fetch (global `fetch` here; mocked in tests), and a
|
|
@@ -18129,7 +19487,7 @@ function buildDaemon(config, paths) {
|
|
|
18129
19487
|
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
18130
19488
|
// excluded from the upstream trace, so a failing login left no evidence.
|
|
18131
19489
|
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
18132
|
-
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0,
|
|
19490
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0, import_upstreamFetch13.fetchUpstream)(url, init, { providerId, redactBodies: true }),
|
|
18133
19491
|
subscriptionAccountAppender: credentialStore,
|
|
18134
19492
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
18135
19493
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -18137,6 +19495,10 @@ function buildDaemon(config, paths) {
|
|
|
18137
19495
|
// can inject a mock so no real port is bound.
|
|
18138
19496
|
codexSessions: new CodexOAuthSessionStore(),
|
|
18139
19497
|
codexAwaitLoopback: paths.codexAwaitLoopback ?? ((state, timeoutMs, signal) => awaitLoopbackCode(state, timeoutMs, signal)),
|
|
19498
|
+
// Kimi interactive OAuth — the async DEVICE-CODE flow store (no port, no
|
|
19499
|
+
// paste; the app shows the verification URL + user code and polls the
|
|
19500
|
+
// token-free status). Token captured + persisted daemon-side.
|
|
19501
|
+
kimiSessions: new CodexOAuthSessionStore(),
|
|
18140
19502
|
// Migration pack (app-parity child 6, design D2/D3) — the concrete credential
|
|
18141
19503
|
// store provides BOTH the full DECRYPTED read (`getFullConfig`, export) and
|
|
18142
19504
|
// the multi-account append (`appendProviderAccount`, import re-encrypts at-
|
|
@@ -18195,7 +19557,7 @@ function buildDaemon(config, paths) {
|
|
|
18195
19557
|
});
|
|
18196
19558
|
const webhookDispatcher = new WebhookDispatcher({
|
|
18197
19559
|
logger,
|
|
18198
|
-
fetchImpl: (url, init) => (0,
|
|
19560
|
+
fetchImpl: (url, init) => (0, import_upstreamFetch13.fetchUpstream)(url, init)
|
|
18199
19561
|
});
|
|
18200
19562
|
setWebhookRuntime(webhookDispatcher, (0, import_SubscriptionAccountHealth4.getSharedAccountHealth)());
|
|
18201
19563
|
const auditWriter = new AuditWriter(auditDir, logger);
|
|
@@ -18272,8 +19634,8 @@ function buildDaemon(config, paths) {
|
|
|
18272
19634
|
}
|
|
18273
19635
|
function isTokensStoreReadable(tokensPath) {
|
|
18274
19636
|
try {
|
|
18275
|
-
if (!(0,
|
|
18276
|
-
(0,
|
|
19637
|
+
if (!(0, import_node_fs36.existsSync)(tokensPath)) return true;
|
|
19638
|
+
(0, import_node_fs36.accessSync)(tokensPath, import_node_fs36.constants.R_OK);
|
|
18277
19639
|
return true;
|
|
18278
19640
|
} catch {
|
|
18279
19641
|
return false;
|
|
@@ -18509,11 +19871,11 @@ async function runLiveProbe(url, key, fetchImpl = fetch) {
|
|
|
18509
19871
|
status: res.status,
|
|
18510
19872
|
estimateHeader: res.headers.get("x-omnicross-count-estimate")
|
|
18511
19873
|
};
|
|
18512
|
-
} catch (
|
|
19874
|
+
} catch (err6) {
|
|
18513
19875
|
return {
|
|
18514
19876
|
status: null,
|
|
18515
19877
|
estimateHeader: null,
|
|
18516
|
-
error:
|
|
19878
|
+
error: err6 instanceof Error ? err6.message : String(err6)
|
|
18517
19879
|
};
|
|
18518
19880
|
}
|
|
18519
19881
|
}
|
|
@@ -18601,7 +19963,7 @@ async function runDoctor(argv, fetchImpl = fetch) {
|
|
|
18601
19963
|
}
|
|
18602
19964
|
|
|
18603
19965
|
// src/commands/import-ccr.ts
|
|
18604
|
-
var
|
|
19966
|
+
var import_node_fs37 = require("fs");
|
|
18605
19967
|
var import_node_util3 = require("util");
|
|
18606
19968
|
|
|
18607
19969
|
// src/ccr-import.ts
|
|
@@ -18696,7 +20058,7 @@ async function runImportCcr(argv) {
|
|
|
18696
20058
|
const outPath = values.out ?? "omnicross.config.json";
|
|
18697
20059
|
let raw;
|
|
18698
20060
|
try {
|
|
18699
|
-
raw = JSON.parse((0,
|
|
20061
|
+
raw = JSON.parse((0, import_node_fs37.readFileSync)(ccrPath, "utf8"));
|
|
18700
20062
|
} catch {
|
|
18701
20063
|
throw new Error(`import-ccr: cannot read or parse '${ccrPath}'`);
|
|
18702
20064
|
}
|
|
@@ -18834,7 +20196,7 @@ async function keysRevoke(db, id) {
|
|
|
18834
20196
|
// src/commands/launch.ts
|
|
18835
20197
|
var import_node_child_process2 = require("child_process");
|
|
18836
20198
|
var import_node_crypto26 = require("crypto");
|
|
18837
|
-
var
|
|
20199
|
+
var import_node_fs38 = require("fs");
|
|
18838
20200
|
var import_node_path38 = require("path");
|
|
18839
20201
|
var import_node_util6 = require("util");
|
|
18840
20202
|
var import_cli_launcher3 = require("@omnicross/cli-launcher");
|
|
@@ -18879,7 +20241,7 @@ function resolveInPathDefault(candidate) {
|
|
|
18879
20241
|
const segments = (process.env["PATH"] ?? "").split(import_node_path38.delimiter).filter(Boolean);
|
|
18880
20242
|
for (const seg of segments) {
|
|
18881
20243
|
const full = (0, import_node_path38.join)(seg, candidate);
|
|
18882
|
-
if ((0,
|
|
20244
|
+
if ((0, import_node_fs38.existsSync)(full)) return full;
|
|
18883
20245
|
}
|
|
18884
20246
|
return null;
|
|
18885
20247
|
}
|
|
@@ -18920,9 +20282,9 @@ async function runLaunch(argv, deps) {
|
|
|
18920
20282
|
await daemon.llmConfig.ready();
|
|
18921
20283
|
await daemon.migrateUsageStore();
|
|
18922
20284
|
await daemon.providerProxy.start();
|
|
18923
|
-
} catch (
|
|
20285
|
+
} catch (err6) {
|
|
18924
20286
|
await shutdownLaunchDaemon(daemon);
|
|
18925
|
-
throw
|
|
20287
|
+
throw err6;
|
|
18926
20288
|
}
|
|
18927
20289
|
let launch;
|
|
18928
20290
|
try {
|
|
@@ -18930,9 +20292,9 @@ async function runLaunch(argv, deps) {
|
|
|
18930
20292
|
providerId: values.provider,
|
|
18931
20293
|
model: values.model
|
|
18932
20294
|
});
|
|
18933
|
-
} catch (
|
|
20295
|
+
} catch (err6) {
|
|
18934
20296
|
await shutdownLaunchDaemon(daemon);
|
|
18935
|
-
throw
|
|
20297
|
+
throw err6;
|
|
18936
20298
|
}
|
|
18937
20299
|
try {
|
|
18938
20300
|
const plan = buildCliSpawnPlan({
|
|
@@ -19037,9 +20399,9 @@ function spawnCliInherit(plan) {
|
|
|
19037
20399
|
process.removeListener("SIGINT", onSignal);
|
|
19038
20400
|
process.removeListener("SIGTERM", onSignal);
|
|
19039
20401
|
};
|
|
19040
|
-
child.on("error", (
|
|
20402
|
+
child.on("error", (err6) => {
|
|
19041
20403
|
detach();
|
|
19042
|
-
if (
|
|
20404
|
+
if (err6.code === "ENOENT") {
|
|
19043
20405
|
reject(
|
|
19044
20406
|
new Error(
|
|
19045
20407
|
`launch: "${plan.command}" not found on PATH \u2014 install the CLI first.`
|
|
@@ -19047,7 +20409,7 @@ function spawnCliInherit(plan) {
|
|
|
19047
20409
|
);
|
|
19048
20410
|
return;
|
|
19049
20411
|
}
|
|
19050
|
-
reject(
|
|
20412
|
+
reject(err6);
|
|
19051
20413
|
});
|
|
19052
20414
|
child.on("exit", (code, signal) => {
|
|
19053
20415
|
detach();
|
|
@@ -19060,9 +20422,9 @@ function spawnCliInherit(plan) {
|
|
|
19060
20422
|
var import_node_child_process3 = require("child_process");
|
|
19061
20423
|
var import_node_readline2 = require("readline");
|
|
19062
20424
|
var import_node_util7 = require("util");
|
|
19063
|
-
var
|
|
19064
|
-
var
|
|
19065
|
-
var PROVIDERS2 = ["claude", "codex", "gemini"];
|
|
20425
|
+
var import_upstreamFetch14 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
20426
|
+
var import_subscriptions10 = require("@omnicross/subscriptions");
|
|
20427
|
+
var PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
|
|
19066
20428
|
async function runLogin(argv, deps) {
|
|
19067
20429
|
const { values, positionals } = (0, import_node_util7.parseArgs)({
|
|
19068
20430
|
args: argv,
|
|
@@ -19088,14 +20450,16 @@ async function runLogin(argv, deps) {
|
|
|
19088
20450
|
openBrowser: deps?.openBrowser ?? openBrowser,
|
|
19089
20451
|
promptPaste: deps?.promptPaste ?? promptPaste,
|
|
19090
20452
|
awaitLoopback: deps?.awaitLoopback ?? ((state) => awaitLoopbackCode(state)),
|
|
20453
|
+
awaitKimiDevice: deps?.awaitKimiDevice ?? ((fetchImpl) => runKimiDeviceFlow(fetchImpl, resolvedOpenBrowser)),
|
|
19091
20454
|
tokensFetch: deps?.tokensFetch
|
|
19092
20455
|
};
|
|
20456
|
+
const resolvedOpenBrowser = resolved.openBrowser;
|
|
19093
20457
|
const box = resolveSecretBox(values["master-key-file"]);
|
|
19094
20458
|
setSecretBox(box);
|
|
19095
|
-
(0,
|
|
20459
|
+
(0, import_upstreamFetch14.setUpstreamProxyResolver)(createUpstreamProxyResolver());
|
|
19096
20460
|
try {
|
|
19097
20461
|
const tokensPath = defaultTokensPath(values.config);
|
|
19098
|
-
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => (0,
|
|
20462
|
+
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => (0, import_upstreamFetch14.fetchUpstream)(url, init, { providerId: provider, redactBodies: true }));
|
|
19099
20463
|
const store = new JsonSubscriptionCredentialStore(tokensPath, box, exchangeFetch);
|
|
19100
20464
|
const expiresAt = await runProviderLogin(
|
|
19101
20465
|
provider,
|
|
@@ -19108,19 +20472,20 @@ async function runLogin(argv, deps) {
|
|
|
19108
20472
|
console.info(` token: [stored, encrypted] expiresAt: ${expiresAt ?? "n/a"}`);
|
|
19109
20473
|
} finally {
|
|
19110
20474
|
setSecretBox(null);
|
|
19111
|
-
(0,
|
|
20475
|
+
(0, import_upstreamFetch14.setUpstreamProxyResolver)(null);
|
|
19112
20476
|
}
|
|
19113
20477
|
}
|
|
19114
20478
|
async function runProviderLogin(provider, store, deps, exchangeFetch, label) {
|
|
19115
20479
|
if (provider === "codex") return loginCodex(store, deps, exchangeFetch, label);
|
|
19116
20480
|
if (provider === "claude") return loginClaude(store, deps, exchangeFetch, label);
|
|
20481
|
+
if (provider === "kimi") return loginKimi(store, deps, exchangeFetch, label);
|
|
19117
20482
|
return loginGemini(store, deps, exchangeFetch, label);
|
|
19118
20483
|
}
|
|
19119
20484
|
async function loginCodex(store, deps, exchangeFetch, label) {
|
|
19120
|
-
const { authUrl, codeVerifier, state } =
|
|
20485
|
+
const { authUrl, codeVerifier, state } = import_subscriptions10.codexOAuth.generateAuthParams();
|
|
19121
20486
|
await presentUrl(authUrl, deps);
|
|
19122
20487
|
const code = await deps.awaitLoopback(state);
|
|
19123
|
-
const result = await
|
|
20488
|
+
const result = await import_subscriptions10.codexOAuth.exchangeCodeForTokens(
|
|
19124
20489
|
{ authorizationCode: code, codeVerifier, state },
|
|
19125
20490
|
exchangeFetch
|
|
19126
20491
|
);
|
|
@@ -19139,7 +20504,7 @@ async function loginCodex(store, deps, exchangeFetch, label) {
|
|
|
19139
20504
|
return expiresAt;
|
|
19140
20505
|
}
|
|
19141
20506
|
async function loginClaude(store, deps, exchangeFetch, label) {
|
|
19142
|
-
const { authUrl, codeVerifier, state } =
|
|
20507
|
+
const { authUrl, codeVerifier, state } = import_subscriptions10.claudeOAuth.generateAuthParams();
|
|
19143
20508
|
await presentUrl(authUrl, deps);
|
|
19144
20509
|
const pasted = (await deps.promptPaste("Paste the authorization code (code#state): ")).trim();
|
|
19145
20510
|
const [code, pastedState] = pasted.split("#");
|
|
@@ -19147,7 +20512,7 @@ async function loginClaude(store, deps, exchangeFetch, label) {
|
|
|
19147
20512
|
if (pastedState && pastedState !== state) {
|
|
19148
20513
|
throw new Error("login: pasted state did not match (possible CSRF) \u2014 aborting");
|
|
19149
20514
|
}
|
|
19150
|
-
const result = await
|
|
20515
|
+
const result = await import_subscriptions10.claudeOAuth.exchangeCodeForTokens(
|
|
19151
20516
|
{ authorizationCode: code, codeVerifier, state },
|
|
19152
20517
|
exchangeFetch
|
|
19153
20518
|
);
|
|
@@ -19166,11 +20531,11 @@ async function loginClaude(store, deps, exchangeFetch, label) {
|
|
|
19166
20531
|
return expiresAt;
|
|
19167
20532
|
}
|
|
19168
20533
|
async function loginGemini(store, deps, exchangeFetch, label) {
|
|
19169
|
-
const { authUrl, codeVerifier } =
|
|
20534
|
+
const { authUrl, codeVerifier } = import_subscriptions10.geminiOAuth.generateAuthParams();
|
|
19170
20535
|
await presentUrl(authUrl, deps);
|
|
19171
20536
|
const code = (await deps.promptPaste("Paste the authorization code: ")).trim();
|
|
19172
20537
|
if (!code) throw new Error("login: no authorization code was pasted");
|
|
19173
|
-
const result = await
|
|
20538
|
+
const result = await import_subscriptions10.geminiOAuth.exchangeCodeForTokens(code, codeVerifier, exchangeFetch);
|
|
19174
20539
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
19175
20540
|
const block = {
|
|
19176
20541
|
authMethod: "oauth",
|
|
@@ -19184,6 +20549,45 @@ async function loginGemini(store, deps, exchangeFetch, label) {
|
|
|
19184
20549
|
logMasked("gemini", result.accessToken);
|
|
19185
20550
|
return expiresAt;
|
|
19186
20551
|
}
|
|
20552
|
+
async function runKimiDeviceFlow(exchangeFetch, openBrowserFn) {
|
|
20553
|
+
const deviceId = import_subscriptions10.kimiOAuth.generateKimiDeviceId();
|
|
20554
|
+
const fingerprint = import_subscriptions10.kimiOAuth.kimiFingerprintHeaders(deviceId);
|
|
20555
|
+
const authorization = await import_subscriptions10.kimiOAuth.requestDeviceAuthorization(exchangeFetch, fingerprint);
|
|
20556
|
+
const url = authorization.verificationUriComplete ?? authorization.verificationUri;
|
|
20557
|
+
console.info("Open this URL in your browser and approve the request:");
|
|
20558
|
+
console.info(` ${url}`);
|
|
20559
|
+
if (!authorization.verificationUriComplete) {
|
|
20560
|
+
console.info(` Then enter this code: ${authorization.userCode}`);
|
|
20561
|
+
}
|
|
20562
|
+
await openBrowserFn(url).catch(() => false);
|
|
20563
|
+
const result = await import_subscriptions10.kimiOAuth.awaitDeviceToken(authorization, exchangeFetch, {
|
|
20564
|
+
fingerprint,
|
|
20565
|
+
onPending: () => process.stdout.write(".")
|
|
20566
|
+
});
|
|
20567
|
+
console.info("");
|
|
20568
|
+
return {
|
|
20569
|
+
...result,
|
|
20570
|
+
accountId: import_subscriptions10.kimiOAuth.kimiAccountIdFromAccessToken(result.accessToken),
|
|
20571
|
+
deviceId
|
|
20572
|
+
};
|
|
20573
|
+
}
|
|
20574
|
+
async function loginKimi(store, deps, exchangeFetch, label) {
|
|
20575
|
+
const result = await deps.awaitKimiDevice(exchangeFetch);
|
|
20576
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
20577
|
+
const block = {
|
|
20578
|
+
authMethod: "oauth",
|
|
20579
|
+
status: "authorized",
|
|
20580
|
+
accessToken: result.accessToken,
|
|
20581
|
+
refreshToken: result.refreshToken,
|
|
20582
|
+
expiresAt,
|
|
20583
|
+
...result.accountId ? { accountId: result.accountId } : {},
|
|
20584
|
+
deviceId: result.deviceId,
|
|
20585
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
20586
|
+
};
|
|
20587
|
+
await store.appendProviderAccount("kimi", block, label);
|
|
20588
|
+
logMasked("kimi", result.accessToken);
|
|
20589
|
+
return expiresAt;
|
|
20590
|
+
}
|
|
19187
20591
|
function isLoginProvider(value) {
|
|
19188
20592
|
return PROVIDERS2.includes(value);
|
|
19189
20593
|
}
|
|
@@ -19381,7 +20785,7 @@ function providersRmKey(configPath, providerId, keyId) {
|
|
|
19381
20785
|
}
|
|
19382
20786
|
|
|
19383
20787
|
// src/commands/secrets.ts
|
|
19384
|
-
var
|
|
20788
|
+
var import_node_fs39 = require("fs");
|
|
19385
20789
|
var import_node_util9 = require("util");
|
|
19386
20790
|
async function runSecrets(argv) {
|
|
19387
20791
|
const { values, positionals } = (0, import_node_util9.parseArgs)({
|
|
@@ -19454,12 +20858,12 @@ function secretsStatus(args) {
|
|
|
19454
20858
|
reportField("admin.token", cfg.admin.token);
|
|
19455
20859
|
}
|
|
19456
20860
|
const tokensPath = defaultTokensPath(args.config);
|
|
19457
|
-
if ((0,
|
|
20861
|
+
if ((0, import_node_fs39.existsSync)(tokensPath)) {
|
|
19458
20862
|
console.info(`Secret status for ${tokensPath}:`);
|
|
19459
20863
|
reportTokenFields(tokensPath);
|
|
19460
20864
|
}
|
|
19461
20865
|
const integrationsPath = defaultIntegrationsPath(args.config);
|
|
19462
|
-
if ((0,
|
|
20866
|
+
if ((0, import_node_fs39.existsSync)(integrationsPath)) {
|
|
19463
20867
|
const state = readRawJson(integrationsPath);
|
|
19464
20868
|
const key = state.gatewayKey;
|
|
19465
20869
|
if (key && typeof key === "object" && !Array.isArray(key)) {
|
|
@@ -19513,8 +20917,8 @@ async function secretsRotate(args) {
|
|
|
19513
20917
|
const integrationsPath = defaultIntegrationsPath(args.config);
|
|
19514
20918
|
try {
|
|
19515
20919
|
cfg = loadConfig(args.config);
|
|
19516
|
-
if ((0,
|
|
19517
|
-
if ((0,
|
|
20920
|
+
if ((0, import_node_fs39.existsSync)(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, oldBox);
|
|
20921
|
+
if ((0, import_node_fs39.existsSync)(integrationsPath)) {
|
|
19518
20922
|
integrationsPlain = new IntegrationStateStore(integrationsPath, oldBox).load();
|
|
19519
20923
|
}
|
|
19520
20924
|
} finally {
|
|
@@ -19549,20 +20953,20 @@ function secretsDecrypt(args) {
|
|
|
19549
20953
|
let tokensPlain = null;
|
|
19550
20954
|
try {
|
|
19551
20955
|
cfg = loadConfig(args.config);
|
|
19552
|
-
if ((0,
|
|
20956
|
+
if ((0, import_node_fs39.existsSync)(tokensPath)) tokensPlain = decryptTokensFile(tokensPath, box);
|
|
19553
20957
|
} finally {
|
|
19554
20958
|
setSecretBox(null);
|
|
19555
20959
|
}
|
|
19556
20960
|
saveConfig(args.config, cfg);
|
|
19557
20961
|
if (tokensPlain) {
|
|
19558
|
-
(
|
|
20962
|
+
atomicReplaceUtf8(tokensPath, JSON.stringify(tokensPlain, null, 2) + "\n");
|
|
19559
20963
|
}
|
|
19560
20964
|
console.info(`Decrypted secrets to plaintext in ${args.config}` + tokensSuffix(args.config));
|
|
19561
20965
|
}
|
|
19562
20966
|
function readRawConfig(path2) {
|
|
19563
20967
|
let parsed;
|
|
19564
20968
|
try {
|
|
19565
|
-
parsed = JSON.parse((0,
|
|
20969
|
+
parsed = JSON.parse((0, import_node_fs39.readFileSync)(path2, "utf8"));
|
|
19566
20970
|
} catch {
|
|
19567
20971
|
throw new Error(`secrets: cannot read or parse '${path2}'`);
|
|
19568
20972
|
}
|
|
@@ -19570,7 +20974,7 @@ function readRawConfig(path2) {
|
|
|
19570
20974
|
}
|
|
19571
20975
|
function readRawJson(path2) {
|
|
19572
20976
|
try {
|
|
19573
|
-
const parsed = JSON.parse((0,
|
|
20977
|
+
const parsed = JSON.parse((0, import_node_fs39.readFileSync)(path2, "utf8"));
|
|
19574
20978
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
19575
20979
|
return parsed;
|
|
19576
20980
|
}
|
|
@@ -19580,13 +20984,13 @@ function readRawJson(path2) {
|
|
|
19580
20984
|
}
|
|
19581
20985
|
function encryptTokensFileInPlace(configPath, box) {
|
|
19582
20986
|
const tokensPath = defaultTokensPath(configPath);
|
|
19583
|
-
if (!(0,
|
|
20987
|
+
if (!(0, import_node_fs39.existsSync)(tokensPath)) return;
|
|
19584
20988
|
const plain = decryptTokensFile(tokensPath, box);
|
|
19585
20989
|
writeTokensEncrypted(tokensPath, plain, box);
|
|
19586
20990
|
}
|
|
19587
20991
|
function rewriteIntegrationState(configPath, readBox, writeBox) {
|
|
19588
20992
|
const path2 = defaultIntegrationsPath(configPath);
|
|
19589
|
-
if (!(0,
|
|
20993
|
+
if (!(0, import_node_fs39.existsSync)(path2)) return;
|
|
19590
20994
|
const state = new IntegrationStateStore(path2, readBox).load();
|
|
19591
20995
|
new IntegrationStateStore(path2, writeBox).save(state);
|
|
19592
20996
|
}
|
|
@@ -19599,7 +21003,7 @@ function writeTokensEncrypted(tokensPath, plain, box) {
|
|
|
19599
21003
|
{ updatedAt: "", ...plain },
|
|
19600
21004
|
box
|
|
19601
21005
|
);
|
|
19602
|
-
(
|
|
21006
|
+
atomicReplaceUtf8(tokensPath, JSON.stringify(encrypted, null, 2) + "\n");
|
|
19603
21007
|
}
|
|
19604
21008
|
var TOKEN_FIELDS2 = {
|
|
19605
21009
|
claude: ["accessToken", "refreshToken"],
|
|
@@ -19622,7 +21026,7 @@ function walkTokens(raw, fn) {
|
|
|
19622
21026
|
return next;
|
|
19623
21027
|
}
|
|
19624
21028
|
function tokensSuffix(configPath) {
|
|
19625
|
-
return (0,
|
|
21029
|
+
return (0, import_node_fs39.existsSync)(defaultTokensPath(configPath)) ? " (+ tokens.json)" : "";
|
|
19626
21030
|
}
|
|
19627
21031
|
|
|
19628
21032
|
// src/commands/start.ts
|
|
@@ -19889,7 +21293,7 @@ async function main() {
|
|
|
19889
21293
|
process.exitCode = 1;
|
|
19890
21294
|
}
|
|
19891
21295
|
}
|
|
19892
|
-
main().catch((
|
|
19893
|
-
console.error(
|
|
21296
|
+
main().catch((err6) => {
|
|
21297
|
+
console.error(err6 instanceof Error ? err6.message : String(err6));
|
|
19894
21298
|
process.exitCode = 1;
|
|
19895
21299
|
});
|