@omnicross/daemon 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1299 -275
- package/dist/cli.js +1255 -224
- package/dist/index.cjs +1194 -268
- package/dist/index.d.cts +160 -8
- package/dist/index.d.ts +160 -8
- package/dist/index.js +1191 -260
- package/package.json +6 -6
package/dist/cli.cjs
CHANGED
|
@@ -1170,22 +1170,22 @@ var import_node_fs36 = require("fs");
|
|
|
1170
1170
|
var import_node_path36 = require("path");
|
|
1171
1171
|
var import_audit_types = require("@omnicross/contracts/audit-types");
|
|
1172
1172
|
var import_billing_types = require("@omnicross/contracts/billing-types");
|
|
1173
|
-
var
|
|
1173
|
+
var import_core7 = require("@omnicross/core");
|
|
1174
1174
|
var import_GeminiCodeAssistProjectResolver = require("@omnicross/core/auth/GeminiCodeAssistProjectResolver");
|
|
1175
1175
|
var import_ApiKeyPoolService = require("@omnicross/core/completion/ApiKeyPoolService");
|
|
1176
1176
|
var import_outbound_api10 = require("@omnicross/core/outbound-api");
|
|
1177
1177
|
var import_subscriptionRegistryPort = require("@omnicross/core/outbound-api/subscriptionRegistryPort");
|
|
1178
1178
|
var import_SubscriptionAccountHealth4 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
1179
|
-
var
|
|
1179
|
+
var import_AccountAllowanceStore9 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1180
1180
|
var import_AccountAllowanceScheduling5 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
1181
|
-
var
|
|
1181
|
+
var import_upstreamFetch15 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
1182
1182
|
var import_SubscriptionIdentityStore3 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
1183
1183
|
var import_gemini_code_assist_resolver = require("@omnicross/core/ports/gemini-code-assist-resolver");
|
|
1184
1184
|
var import_provider_proxy4 = require("@omnicross/core/provider-proxy");
|
|
1185
1185
|
var import_cli_launcher2 = require("@omnicross/cli-launcher");
|
|
1186
1186
|
var import_outbound_api11 = require("@omnicross/core/outbound-api");
|
|
1187
1187
|
var import_usage2 = require("@omnicross/core/usage");
|
|
1188
|
-
var
|
|
1188
|
+
var import_subscriptions12 = require("@omnicross/subscriptions");
|
|
1189
1189
|
|
|
1190
1190
|
// src/admin/accountsCodexOAuth.ts
|
|
1191
1191
|
var import_node_crypto3 = __toESM(require("crypto"), 1);
|
|
@@ -1367,8 +1367,187 @@ function handleKimiOAuthStatus(sessionId, deps) {
|
|
|
1367
1367
|
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
1368
1368
|
}
|
|
1369
1369
|
|
|
1370
|
+
// src/admin/accountsGrokOAuth.ts
|
|
1371
|
+
var import_subscriptions3 = require("@omnicross/subscriptions");
|
|
1372
|
+
function err3(status, message) {
|
|
1373
|
+
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
1374
|
+
}
|
|
1375
|
+
var DEFAULT_GROK_OAUTH_TTL_MS = 15 * 6e4;
|
|
1376
|
+
async function handleGrokOAuthStart(deps) {
|
|
1377
|
+
if (deps.grokSessions.isBusy()) {
|
|
1378
|
+
return err3(409, "a grok sign-in is already in progress \u2014 finish it in the browser or cancel it");
|
|
1379
|
+
}
|
|
1380
|
+
const fetchImpl = deps.oauthExchangeFetch("grok");
|
|
1381
|
+
let tokenEndpoint;
|
|
1382
|
+
try {
|
|
1383
|
+
tokenEndpoint = await import_subscriptions3.grokOAuth.resolveGrokTokenEndpoint(fetchImpl);
|
|
1384
|
+
} catch (e) {
|
|
1385
|
+
const reason = e instanceof Error ? e.message : "OIDC discovery failed";
|
|
1386
|
+
return err3(502, `grok token-endpoint discovery failed: ${reason}`);
|
|
1387
|
+
}
|
|
1388
|
+
let authorization;
|
|
1389
|
+
try {
|
|
1390
|
+
authorization = await import_subscriptions3.grokOAuth.requestGrokDeviceAuthorization(fetchImpl);
|
|
1391
|
+
} catch (e) {
|
|
1392
|
+
const reason = e instanceof Error ? e.message : "device authorization failed";
|
|
1393
|
+
return err3(502, `grok device authorization failed: ${reason}`);
|
|
1394
|
+
}
|
|
1395
|
+
const { sessionId, signal } = deps.grokSessions.begin();
|
|
1396
|
+
void runGrokDevicePoll(sessionId, tokenEndpoint, authorization.deviceCode, signal, deps).catch((e) => {
|
|
1397
|
+
const reason = e instanceof Error ? e.message : "grok sign-in failed";
|
|
1398
|
+
deps.grokSessions.settle(sessionId, "error", reason);
|
|
1399
|
+
});
|
|
1400
|
+
return {
|
|
1401
|
+
status: 200,
|
|
1402
|
+
body: {
|
|
1403
|
+
authUrl: authorization.verificationUriComplete ?? authorization.verificationUri,
|
|
1404
|
+
userCode: authorization.userCode,
|
|
1405
|
+
sessionId
|
|
1406
|
+
}
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
async function runGrokDevicePoll(sessionId, tokenEndpoint, deviceCode, signal, deps) {
|
|
1410
|
+
const fetchImpl = deps.oauthExchangeFetch("grok");
|
|
1411
|
+
const result = await import_subscriptions3.grokOAuth.awaitGrokDeviceToken(
|
|
1412
|
+
{ userCode: "", deviceCode, verificationUri: "" },
|
|
1413
|
+
tokenEndpoint,
|
|
1414
|
+
fetchImpl,
|
|
1415
|
+
{
|
|
1416
|
+
deadlineMs: DEFAULT_GROK_OAUTH_TTL_MS,
|
|
1417
|
+
sleep: (ms) => new Promise((resolve11, reject) => {
|
|
1418
|
+
const onAbort = () => {
|
|
1419
|
+
clearTimeout(timer);
|
|
1420
|
+
reject(new Error("login: cancelled"));
|
|
1421
|
+
};
|
|
1422
|
+
const timer = setTimeout(() => {
|
|
1423
|
+
signal.removeEventListener("abort", onAbort);
|
|
1424
|
+
resolve11();
|
|
1425
|
+
}, ms);
|
|
1426
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1427
|
+
})
|
|
1428
|
+
}
|
|
1429
|
+
);
|
|
1430
|
+
const block = {
|
|
1431
|
+
authMethod: "oauth",
|
|
1432
|
+
status: "authorized",
|
|
1433
|
+
accessToken: result.accessToken,
|
|
1434
|
+
refreshToken: result.refreshToken,
|
|
1435
|
+
expiresAt: new Date(Date.now() + result.expiresIn * 1e3).toISOString(),
|
|
1436
|
+
accountId: import_subscriptions3.grokOAuth.grokAccountIdFromAccessToken(result.accessToken),
|
|
1437
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1438
|
+
};
|
|
1439
|
+
await deps.subscriptionAccountAppender.appendProviderAccount("grok", block);
|
|
1440
|
+
deps.grokSessions.settle(sessionId, "done");
|
|
1441
|
+
}
|
|
1442
|
+
function handleGrokOAuthCancel(sessionId, deps) {
|
|
1443
|
+
if (!deps.grokSessions.cancel(sessionId)) return err3(404, "unknown or expired grok sign-in session");
|
|
1444
|
+
return { status: 200, body: { ok: true } };
|
|
1445
|
+
}
|
|
1446
|
+
function handleGrokOAuthStatus(sessionId, deps) {
|
|
1447
|
+
const s = deps.grokSessions.get(sessionId);
|
|
1448
|
+
if (!s) return err3(404, "unknown or expired grok sign-in session");
|
|
1449
|
+
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
// src/admin/accountsCopilotOAuth.ts
|
|
1453
|
+
var import_subscriptions4 = require("@omnicross/subscriptions");
|
|
1454
|
+
function err4(status, message) {
|
|
1455
|
+
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
1456
|
+
}
|
|
1457
|
+
var DEFAULT_COPILOT_OAUTH_TTL_MS = 15 * 6e4;
|
|
1458
|
+
async function handleCopilotOAuthStart(deps, enterpriseUrlInput) {
|
|
1459
|
+
if (deps.copilotSessions.isBusy()) {
|
|
1460
|
+
return err4(409, "a copilot sign-in is already in progress \u2014 finish it in the browser or cancel it");
|
|
1461
|
+
}
|
|
1462
|
+
let enterpriseUrl;
|
|
1463
|
+
if (typeof enterpriseUrlInput === "string" && enterpriseUrlInput.trim()) {
|
|
1464
|
+
try {
|
|
1465
|
+
enterpriseUrl = import_subscriptions4.copilotOAuth.normalizeCopilotEnterpriseDomain(enterpriseUrlInput);
|
|
1466
|
+
} catch (e) {
|
|
1467
|
+
const reason = e instanceof Error ? e.message : "invalid GitHub Enterprise domain";
|
|
1468
|
+
return err4(400, `copilot ${reason}`);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
const fetchImpl = deps.oauthExchangeFetch("copilot");
|
|
1472
|
+
let authorization;
|
|
1473
|
+
try {
|
|
1474
|
+
authorization = await import_subscriptions4.copilotOAuth.requestCopilotDeviceAuthorization(fetchImpl, enterpriseUrl);
|
|
1475
|
+
} catch (e) {
|
|
1476
|
+
const reason = e instanceof Error ? e.message : "device authorization failed";
|
|
1477
|
+
return err4(502, `copilot device authorization failed: ${reason}`);
|
|
1478
|
+
}
|
|
1479
|
+
const { sessionId, signal } = deps.copilotSessions.begin();
|
|
1480
|
+
void runCopilotDevicePoll(sessionId, authorization.deviceCode, signal, deps, enterpriseUrl).catch((e) => {
|
|
1481
|
+
const reason = e instanceof Error ? e.message : "copilot sign-in failed";
|
|
1482
|
+
deps.copilotSessions.settle(sessionId, "error", reason);
|
|
1483
|
+
});
|
|
1484
|
+
return {
|
|
1485
|
+
status: 200,
|
|
1486
|
+
body: {
|
|
1487
|
+
authUrl: authorization.verificationUri,
|
|
1488
|
+
userCode: authorization.userCode,
|
|
1489
|
+
sessionId,
|
|
1490
|
+
...enterpriseUrl ? { enterpriseUrl } : {}
|
|
1491
|
+
}
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
async function runCopilotDevicePoll(sessionId, deviceCode, signal, deps, enterpriseUrl) {
|
|
1495
|
+
const fetchImpl = deps.oauthExchangeFetch("copilot");
|
|
1496
|
+
const result = await import_subscriptions4.copilotOAuth.awaitCopilotDeviceToken(
|
|
1497
|
+
{ userCode: "", deviceCode, verificationUri: "", interval: 5, expiresIn: 900 },
|
|
1498
|
+
fetchImpl,
|
|
1499
|
+
{
|
|
1500
|
+
deadlineMs: DEFAULT_COPILOT_OAUTH_TTL_MS,
|
|
1501
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
1502
|
+
sleep: (ms) => new Promise((resolve11, reject) => {
|
|
1503
|
+
const onAbort = () => {
|
|
1504
|
+
clearTimeout(timer);
|
|
1505
|
+
reject(new Error("login: cancelled"));
|
|
1506
|
+
};
|
|
1507
|
+
const timer = setTimeout(() => {
|
|
1508
|
+
signal.removeEventListener("abort", onAbort);
|
|
1509
|
+
resolve11();
|
|
1510
|
+
}, ms);
|
|
1511
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1512
|
+
})
|
|
1513
|
+
}
|
|
1514
|
+
);
|
|
1515
|
+
const identity = await import_subscriptions4.copilotOAuth.fetchCopilotIdentity(result.accessToken, fetchImpl, enterpriseUrl);
|
|
1516
|
+
const apiEndpoint = await import_subscriptions4.copilotOAuth.discoverCopilotApiEndpoint(result.accessToken, fetchImpl, enterpriseUrl);
|
|
1517
|
+
await import_subscriptions4.copilotOAuth.enableAllCopilotModels(
|
|
1518
|
+
result.accessToken,
|
|
1519
|
+
{ apiEndpoint, ...enterpriseUrl ? { enterpriseUrl } : {} },
|
|
1520
|
+
fetchImpl
|
|
1521
|
+
);
|
|
1522
|
+
const block = {
|
|
1523
|
+
authMethod: "oauth",
|
|
1524
|
+
status: "authorized",
|
|
1525
|
+
accessToken: result.accessToken,
|
|
1526
|
+
refreshToken: result.accessToken,
|
|
1527
|
+
expiresAt: new Date(Date.now() + import_subscriptions4.copilotOAuth.COPILOT_FAR_FUTURE_MS).toISOString(),
|
|
1528
|
+
...identity.accountId ? { accountId: identity.accountId } : {},
|
|
1529
|
+
...identity.email ? { email: identity.email } : {},
|
|
1530
|
+
...apiEndpoint ? { apiEndpoint } : {},
|
|
1531
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
1532
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1533
|
+
};
|
|
1534
|
+
await deps.subscriptionAccountAppender.appendProviderAccount("copilot", block);
|
|
1535
|
+
deps.copilotSessions.settle(sessionId, "done");
|
|
1536
|
+
}
|
|
1537
|
+
function handleCopilotOAuthCancel(sessionId, deps) {
|
|
1538
|
+
if (!deps.copilotSessions.cancel(sessionId)) {
|
|
1539
|
+
return err4(404, "unknown or expired copilot sign-in session");
|
|
1540
|
+
}
|
|
1541
|
+
return { status: 200, body: { ok: true } };
|
|
1542
|
+
}
|
|
1543
|
+
function handleCopilotOAuthStatus(sessionId, deps) {
|
|
1544
|
+
const s = deps.copilotSessions.get(sessionId);
|
|
1545
|
+
if (!s) return err4(404, "unknown or expired copilot sign-in session");
|
|
1546
|
+
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1370
1549
|
// src/allowance/AccountAllowanceService.ts
|
|
1371
|
-
var
|
|
1550
|
+
var import_AccountAllowanceStore7 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1372
1551
|
var import_AccountAllowanceScheduling = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
1373
1552
|
|
|
1374
1553
|
// src/allowance/ClaudeAllowanceCollector.ts
|
|
@@ -1839,7 +2018,7 @@ var CodexAllowanceCollector = class {
|
|
|
1839
2018
|
// src/allowance/KimiAllowanceCollector.ts
|
|
1840
2019
|
var import_AccountAllowanceStore3 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1841
2020
|
var import_upstreamFetch3 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
1842
|
-
var
|
|
2021
|
+
var import_subscriptions5 = require("@omnicross/subscriptions");
|
|
1843
2022
|
var KIMI_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
1844
2023
|
var KIMI_USAGE_URL = "https://api.kimi.com/coding/v1/usages";
|
|
1845
2024
|
function finiteNumber2(value) {
|
|
@@ -1994,97 +2173,572 @@ var KimiAllowanceCollector = class {
|
|
|
1994
2173
|
let accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
|
|
1995
2174
|
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
1996
2175
|
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());
|
|
2176
|
+
if (response.status === 401) {
|
|
2177
|
+
const refreshed = await this.credentials.refreshAccountToken("kimi", accountId);
|
|
2178
|
+
if (!refreshed) return this.failureSnapshot(accountId, "kimi_usage_unauthorized", this.now());
|
|
2179
|
+
accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
|
|
2180
|
+
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
2181
|
+
response = await this.request(accountId, accessToken, tokens);
|
|
2182
|
+
}
|
|
2183
|
+
if (response.status === 403) {
|
|
2184
|
+
const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "kimi_usage_unsupported");
|
|
2185
|
+
this.store.set(snapshot2);
|
|
2186
|
+
return snapshot2;
|
|
2187
|
+
}
|
|
2188
|
+
if (!response.ok) return this.failureSnapshot(accountId, "kimi_usage_http_error", this.now());
|
|
2189
|
+
let payload;
|
|
2190
|
+
try {
|
|
2191
|
+
payload = await response.json();
|
|
2192
|
+
} catch {
|
|
2193
|
+
return this.failureSnapshot(accountId, "kimi_usage_invalid_response", this.now());
|
|
2194
|
+
}
|
|
2195
|
+
const now = this.now();
|
|
2196
|
+
const windows = parseKimiUsagePayload(payload, now);
|
|
2197
|
+
const snapshot = {
|
|
2198
|
+
providerId: "kimi",
|
|
2199
|
+
accountId,
|
|
2200
|
+
source: "oauth-usage-api",
|
|
2201
|
+
observedAt: new Date(now).toISOString(),
|
|
2202
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2203
|
+
windows: windows.length > 0 ? windows : [
|
|
2204
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2205
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2206
|
+
],
|
|
2207
|
+
...windows.length > 0 ? {} : { lastErrorCode: "kimi_usage_invalid_response" }
|
|
2208
|
+
};
|
|
2209
|
+
this.store.set(snapshot);
|
|
2210
|
+
return snapshot;
|
|
2211
|
+
}
|
|
2212
|
+
request(accountId, accessToken, tokens) {
|
|
2213
|
+
return this.fetchImpl(KIMI_USAGE_URL, {
|
|
2214
|
+
method: "GET",
|
|
2215
|
+
headers: {
|
|
2216
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2217
|
+
Accept: "application/json",
|
|
2218
|
+
...(0, import_subscriptions5.kimiFingerprintHeaders)(tokens.deviceId)
|
|
2219
|
+
},
|
|
2220
|
+
signal: AbortSignal.timeout(15e3)
|
|
2221
|
+
}, accountId);
|
|
2222
|
+
}
|
|
2223
|
+
failureSnapshot(accountId, code, now) {
|
|
2224
|
+
const existing = this.store.get("kimi", accountId, now);
|
|
2225
|
+
const snapshot = existing ? {
|
|
2226
|
+
...existing,
|
|
2227
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2228
|
+
windows: existing.windows.map((window) => ({
|
|
2229
|
+
...window,
|
|
2230
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2231
|
+
})),
|
|
2232
|
+
lastErrorCode: code
|
|
2233
|
+
} : {
|
|
2234
|
+
providerId: "kimi",
|
|
2235
|
+
accountId,
|
|
2236
|
+
source: "oauth-usage-api",
|
|
2237
|
+
observedAt: new Date(now).toISOString(),
|
|
2238
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2239
|
+
windows: [
|
|
2240
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2241
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2242
|
+
],
|
|
2243
|
+
lastErrorCode: code
|
|
2244
|
+
};
|
|
2245
|
+
this.store.set(snapshot);
|
|
2246
|
+
return snapshot;
|
|
2247
|
+
}
|
|
2248
|
+
unsupportedSnapshot(accountId, now, code = "kimi_usage_unsupported_auth") {
|
|
2249
|
+
return {
|
|
2250
|
+
providerId: "kimi",
|
|
2251
|
+
accountId,
|
|
2252
|
+
source: "oauth-usage-api",
|
|
2253
|
+
observedAt: new Date(now).toISOString(),
|
|
2254
|
+
windows: [
|
|
2255
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unsupported" },
|
|
2256
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2257
|
+
],
|
|
2258
|
+
lastErrorCode: code
|
|
2259
|
+
};
|
|
2260
|
+
}
|
|
2261
|
+
};
|
|
2262
|
+
|
|
2263
|
+
// src/allowance/GrokAllowanceCollector.ts
|
|
2264
|
+
var import_AccountAllowanceStore4 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
2265
|
+
var import_upstreamFetch4 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
2266
|
+
var GROK_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2267
|
+
var GROK_BILLING_BASE = "https://cli-chat-proxy.grok.com";
|
|
2268
|
+
var GROK_BILLING_CREDITS_URL = `${GROK_BILLING_BASE}/v1/billing?format=credits`;
|
|
2269
|
+
var GROK_BILLING_MONTHLY_URL = `${GROK_BILLING_BASE}/v1/billing`;
|
|
2270
|
+
function isRecord2(value) {
|
|
2271
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2272
|
+
}
|
|
2273
|
+
function finiteNumber3(value) {
|
|
2274
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
2275
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
2276
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
2277
|
+
}
|
|
2278
|
+
function percent(value) {
|
|
2279
|
+
const parsed = finiteNumber3(value);
|
|
2280
|
+
return parsed !== void 0 && parsed <= 100 ? parsed : void 0;
|
|
2281
|
+
}
|
|
2282
|
+
function onDemandAmount(value) {
|
|
2283
|
+
return isRecord2(value) ? finiteNumber3(value["val"]) : void 0;
|
|
2284
|
+
}
|
|
2285
|
+
function confirmsNoMonthlyQuota(raw) {
|
|
2286
|
+
const limit = onDemandAmount(raw["monthlyLimit"]);
|
|
2287
|
+
if (limit !== void 0) return limit === 0;
|
|
2288
|
+
return parseWeeklyConfig(raw)?.inferredPercent === true;
|
|
2289
|
+
}
|
|
2290
|
+
function parseWeeklyConfig(raw) {
|
|
2291
|
+
const period = isRecord2(raw["currentPeriod"]) ? raw["currentPeriod"] : void 0;
|
|
2292
|
+
if (!period) return null;
|
|
2293
|
+
const start = typeof period["start"] === "string" ? Date.parse(period["start"]) : Number.NaN;
|
|
2294
|
+
const end = typeof period["end"] === "string" ? Date.parse(period["end"]) : Number.NaN;
|
|
2295
|
+
const type = typeof period["type"] === "string" ? period["type"] : "";
|
|
2296
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
|
|
2297
|
+
if (!type.toUpperCase().includes("WEEK")) return null;
|
|
2298
|
+
const inferred = raw["creditUsagePercent"] === void 0 || raw["creditUsagePercent"] === null;
|
|
2299
|
+
let creditUsagePercent;
|
|
2300
|
+
if (inferred) {
|
|
2301
|
+
creditUsagePercent = end > Date.now() ? 0 : void 0;
|
|
2302
|
+
} else {
|
|
2303
|
+
creditUsagePercent = percent(raw["creditUsagePercent"]);
|
|
2304
|
+
}
|
|
2305
|
+
if (creditUsagePercent === void 0) return null;
|
|
2306
|
+
return {
|
|
2307
|
+
creditUsagePercent,
|
|
2308
|
+
inferredPercent: inferred,
|
|
2309
|
+
resetsAtMs: end,
|
|
2310
|
+
unified: raw["isUnifiedBillingUser"] === true
|
|
2311
|
+
};
|
|
2312
|
+
}
|
|
2313
|
+
function parseMonthlyConfig(raw) {
|
|
2314
|
+
const start = typeof raw["billingPeriodStart"] === "string" ? Date.parse(raw["billingPeriodStart"]) : Number.NaN;
|
|
2315
|
+
const end = typeof raw["billingPeriodEnd"] === "string" ? Date.parse(raw["billingPeriodEnd"]) : Number.NaN;
|
|
2316
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
|
|
2317
|
+
const limit = onDemandAmount(raw["monthlyLimit"]);
|
|
2318
|
+
const used = onDemandAmount(raw["used"]);
|
|
2319
|
+
if (limit === void 0 || limit <= 0 || used === void 0) return null;
|
|
2320
|
+
return { used, limit, periodStartMs: start, periodEndMs: end };
|
|
2321
|
+
}
|
|
2322
|
+
function secondsUntil4(instant, now) {
|
|
2323
|
+
if (!instant) return void 0;
|
|
2324
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2325
|
+
}
|
|
2326
|
+
var MINUTE_MS2 = 6e4;
|
|
2327
|
+
var DAY_MS2 = 864e5;
|
|
2328
|
+
var WEEK_MINUTES = 7 * 24 * 60;
|
|
2329
|
+
function weeklyWindow(config, now) {
|
|
2330
|
+
const resetsAt = new Date(config.resetsAtMs).toISOString();
|
|
2331
|
+
return {
|
|
2332
|
+
id: "seven-day",
|
|
2333
|
+
label: "7 days",
|
|
2334
|
+
scope: "all",
|
|
2335
|
+
usedPercent: config.creditUsagePercent,
|
|
2336
|
+
windowMinutes: WEEK_MINUTES,
|
|
2337
|
+
resetsAt,
|
|
2338
|
+
remainingSeconds: secondsUntil4(resetsAt, now),
|
|
2339
|
+
state: "fresh"
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
function monthlyWindow(config, now) {
|
|
2343
|
+
const resetsAt = new Date(config.periodEndMs).toISOString();
|
|
2344
|
+
const days = Math.max(1, Math.round((config.periodEndMs - config.periodStartMs) / DAY_MS2));
|
|
2345
|
+
return {
|
|
2346
|
+
id: "thirty-day",
|
|
2347
|
+
label: days === 30 || days === 31 ? "30 days" : `${days} days`,
|
|
2348
|
+
scope: "all",
|
|
2349
|
+
usedPercent: Math.round(Math.min(100, config.used / config.limit * 100) * 10) / 10,
|
|
2350
|
+
windowMinutes: Math.round((config.periodEndMs - config.periodStartMs) / MINUTE_MS2),
|
|
2351
|
+
resetsAt,
|
|
2352
|
+
remainingSeconds: secondsUntil4(resetsAt, now),
|
|
2353
|
+
state: "fresh"
|
|
2354
|
+
};
|
|
2355
|
+
}
|
|
2356
|
+
function onDemandWindow(raw) {
|
|
2357
|
+
const cap = onDemandAmount(raw["onDemandCap"]);
|
|
2358
|
+
const used = onDemandAmount(raw["onDemandUsed"]);
|
|
2359
|
+
if (cap === void 0 || cap <= 0 || used === void 0) return null;
|
|
2360
|
+
return {
|
|
2361
|
+
id: "on-demand",
|
|
2362
|
+
label: "On-demand",
|
|
2363
|
+
scope: "all",
|
|
2364
|
+
usedPercent: Math.round(Math.min(100, used / cap * 100) * 10) / 10,
|
|
2365
|
+
state: "fresh"
|
|
2366
|
+
};
|
|
2367
|
+
}
|
|
2368
|
+
async function probeBilling(url, accessToken, accountId, fetchImpl) {
|
|
2369
|
+
try {
|
|
2370
|
+
const response = await fetchImpl(url, {
|
|
2371
|
+
method: "GET",
|
|
2372
|
+
headers: {
|
|
2373
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2374
|
+
Accept: "application/json",
|
|
2375
|
+
"X-XAI-Token-Auth": "xai-grok-cli"
|
|
2376
|
+
},
|
|
2377
|
+
redirect: "error",
|
|
2378
|
+
signal: AbortSignal.timeout(15e3)
|
|
2379
|
+
}, accountId);
|
|
2380
|
+
if (!response.ok) return { status: response.status, payload: null };
|
|
2381
|
+
const payload = await response.json();
|
|
2382
|
+
return { status: response.status, payload: isRecord2(payload) ? payload : null };
|
|
2383
|
+
} catch {
|
|
2384
|
+
return { status: 0, payload: null };
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
function parseGrokBillingPayloads(creditsPayload, monthlyPayload, now) {
|
|
2388
|
+
const creditsConfig = isRecord2(creditsPayload?.["config"]) ? creditsPayload["config"] : null;
|
|
2389
|
+
const monthlyConfig = isRecord2(monthlyPayload?.["config"]) ? monthlyPayload["config"] : null;
|
|
2390
|
+
let weekly = creditsConfig ? parseWeeklyConfig(creditsConfig) : null;
|
|
2391
|
+
const unifiedFlag = creditsConfig?.["isUnifiedBillingUser"] === true;
|
|
2392
|
+
let monthly = monthlyConfig ? parseMonthlyConfig(monthlyConfig) : null;
|
|
2393
|
+
if (weekly?.inferredPercent && unifiedFlag) {
|
|
2394
|
+
if (monthly) {
|
|
2395
|
+
weekly = null;
|
|
2396
|
+
} else if (!monthlyConfig || !confirmsNoMonthlyQuota(monthlyConfig)) {
|
|
2397
|
+
weekly = null;
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
const windows = [];
|
|
2401
|
+
if (weekly) windows.push(weeklyWindow(weekly, now));
|
|
2402
|
+
if (monthly) windows.push(monthlyWindow(monthly, now));
|
|
2403
|
+
const onDemandSource = monthly && monthlyConfig ? monthlyConfig : creditsConfig;
|
|
2404
|
+
const onDemand = onDemandSource ? onDemandWindow(onDemandSource) : null;
|
|
2405
|
+
if (onDemand) windows.push(onDemand);
|
|
2406
|
+
return windows.length > 0 ? windows : null;
|
|
2407
|
+
}
|
|
2408
|
+
var GrokAllowanceCollector = class {
|
|
2409
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore4.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch4.fetchUpstream)(url, init, { providerId: "grok", accountId, redactBodies: true }), now = Date.now) {
|
|
2410
|
+
this.credentials = credentials;
|
|
2411
|
+
this.store = store;
|
|
2412
|
+
this.fetchImpl = fetchImpl;
|
|
2413
|
+
this.now = now;
|
|
2414
|
+
}
|
|
2415
|
+
credentials;
|
|
2416
|
+
store;
|
|
2417
|
+
fetchImpl;
|
|
2418
|
+
now;
|
|
2419
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
2420
|
+
async collectMany(accounts, options = {}) {
|
|
2421
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
2422
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
2423
|
+
}
|
|
2424
|
+
collect(account, options = {}) {
|
|
2425
|
+
const now = this.now();
|
|
2426
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
2427
|
+
const existing = this.store.get("grok", account.id, now);
|
|
2428
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
2429
|
+
return Promise.resolve(existing);
|
|
2430
|
+
}
|
|
2431
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
2432
|
+
this.store.set(snapshot);
|
|
2433
|
+
return Promise.resolve(snapshot);
|
|
2434
|
+
}
|
|
2435
|
+
const cached = this.store.get("grok", account.id, now);
|
|
2436
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
2437
|
+
return Promise.resolve(cached);
|
|
2438
|
+
}
|
|
2439
|
+
const running = this.inFlight.get(account.id);
|
|
2440
|
+
if (running) return running;
|
|
2441
|
+
const promise = this.fetchAccount(account.id).catch(() => this.failureSnapshot(account.id, "grok_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
2442
|
+
this.inFlight.set(account.id, promise);
|
|
2443
|
+
return promise;
|
|
2444
|
+
}
|
|
2445
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
2446
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
2447
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
2448
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
2449
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
2450
|
+
}
|
|
2451
|
+
async fetchAccount(accountId) {
|
|
2452
|
+
const probe = async () => {
|
|
2453
|
+
const accessToken = await this.credentials.getAccessTokenForAccount("grok", accountId);
|
|
2454
|
+
if (!accessToken) return { unauthorized: true, windows: null };
|
|
2455
|
+
const credits = await probeBilling(GROK_BILLING_CREDITS_URL, accessToken, accountId, this.fetchImpl);
|
|
2456
|
+
if (credits.status === 401 || credits.status === 403) return { unauthorized: true, windows: null };
|
|
2457
|
+
const creditsConfig = isRecord2(credits.payload?.["config"]) ? credits.payload["config"] : null;
|
|
2458
|
+
const weekly = creditsConfig ? parseWeeklyConfig(creditsConfig) : null;
|
|
2459
|
+
const monthly = !weekly || creditsConfig?.["isUnifiedBillingUser"] === true ? await probeBilling(GROK_BILLING_MONTHLY_URL, accessToken, accountId, this.fetchImpl) : { status: 200, payload: null };
|
|
2460
|
+
if (monthly.status === 401 || monthly.status === 403) return { unauthorized: true, windows: null };
|
|
2461
|
+
return {
|
|
2462
|
+
unauthorized: false,
|
|
2463
|
+
windows: parseGrokBillingPayloads(credits.payload, monthly.payload, this.now())
|
|
2464
|
+
};
|
|
2465
|
+
};
|
|
2466
|
+
let result = await probe();
|
|
2467
|
+
if (result.unauthorized) {
|
|
2468
|
+
const refreshed = await this.credentials.refreshAccountToken("grok", accountId);
|
|
2469
|
+
if (!refreshed) return this.failureSnapshot(accountId, "grok_usage_unauthorized", this.now());
|
|
2470
|
+
result = await probe();
|
|
2471
|
+
if (result.unauthorized) {
|
|
2472
|
+
return this.failureSnapshot(accountId, "grok_usage_unauthorized", this.now());
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
const now = this.now();
|
|
2476
|
+
if (result.windows && result.windows.length > 0) {
|
|
2477
|
+
const snapshot = {
|
|
2478
|
+
providerId: "grok",
|
|
2479
|
+
accountId,
|
|
2480
|
+
source: "oauth-usage-api",
|
|
2481
|
+
observedAt: new Date(now).toISOString(),
|
|
2482
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2483
|
+
windows: result.windows
|
|
2484
|
+
};
|
|
2485
|
+
this.store.set(snapshot);
|
|
2486
|
+
return snapshot;
|
|
2487
|
+
}
|
|
2488
|
+
return this.failureSnapshot(accountId, "grok_usage_invalid_response", now);
|
|
2489
|
+
}
|
|
2490
|
+
failureSnapshot(accountId, code, now) {
|
|
2491
|
+
const existing = this.store.get("grok", accountId, now);
|
|
2492
|
+
const snapshot = existing ? {
|
|
2493
|
+
...existing,
|
|
2494
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2495
|
+
windows: existing.windows.map((window) => ({
|
|
2496
|
+
...window,
|
|
2497
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2498
|
+
})),
|
|
2499
|
+
lastErrorCode: code
|
|
2500
|
+
} : {
|
|
2501
|
+
providerId: "grok",
|
|
2502
|
+
accountId,
|
|
2503
|
+
source: "oauth-usage-api",
|
|
2504
|
+
observedAt: new Date(now).toISOString(),
|
|
2505
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2506
|
+
windows: [
|
|
2507
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2508
|
+
{ id: "thirty-day", label: "30 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2509
|
+
],
|
|
2510
|
+
lastErrorCode: code
|
|
2511
|
+
};
|
|
2512
|
+
this.store.set(snapshot);
|
|
2513
|
+
return snapshot;
|
|
2514
|
+
}
|
|
2515
|
+
unsupportedSnapshot(accountId, now) {
|
|
2516
|
+
return {
|
|
2517
|
+
providerId: "grok",
|
|
2518
|
+
accountId,
|
|
2519
|
+
source: "oauth-usage-api",
|
|
2520
|
+
observedAt: new Date(now).toISOString(),
|
|
2521
|
+
windows: [
|
|
2522
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" },
|
|
2523
|
+
{ id: "thirty-day", label: "30 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2524
|
+
],
|
|
2525
|
+
lastErrorCode: "grok_usage_unsupported_auth"
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
};
|
|
2529
|
+
|
|
2530
|
+
// src/allowance/CopilotAllowanceCollector.ts
|
|
2531
|
+
var import_AccountAllowanceStore5 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
2532
|
+
var import_upstreamFetch5 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
2533
|
+
var import_subscriptions6 = require("@omnicross/subscriptions");
|
|
2534
|
+
var COPILOT_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2535
|
+
function isRecord3(value) {
|
|
2536
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2537
|
+
}
|
|
2538
|
+
function finiteNumber4(value) {
|
|
2539
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
2540
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
2541
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
2542
|
+
}
|
|
2543
|
+
function booleanValue(value) {
|
|
2544
|
+
if (typeof value === "boolean") return value;
|
|
2545
|
+
if (value === "true") return true;
|
|
2546
|
+
if (value === "false") return false;
|
|
2547
|
+
return void 0;
|
|
2548
|
+
}
|
|
2549
|
+
function parseQuotaDetail(value) {
|
|
2550
|
+
if (!isRecord3(value)) return null;
|
|
2551
|
+
const entitlement = finiteNumber4(value["entitlement"]);
|
|
2552
|
+
const remaining = finiteNumber4(value["remaining"]);
|
|
2553
|
+
const percentRemaining = finiteNumber4(value["percent_remaining"]);
|
|
2554
|
+
const unlimited = booleanValue(value["unlimited"]);
|
|
2555
|
+
if (entitlement === void 0 || remaining === void 0 || percentRemaining === void 0 || unlimited === void 0) {
|
|
2556
|
+
return null;
|
|
2557
|
+
}
|
|
2558
|
+
return { entitlement, remaining, percentRemaining, unlimited };
|
|
2559
|
+
}
|
|
2560
|
+
function secondsUntil5(instant, now) {
|
|
2561
|
+
if (!instant) return void 0;
|
|
2562
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2563
|
+
}
|
|
2564
|
+
function parseCopilotUserPayload(payload, now) {
|
|
2565
|
+
if (!isRecord3(payload)) return null;
|
|
2566
|
+
const snapshots = isRecord3(payload["quota_snapshots"]) ? payload["quota_snapshots"] : void 0;
|
|
2567
|
+
if (!snapshots) return null;
|
|
2568
|
+
const resetRaw = payload["quota_reset_date"];
|
|
2569
|
+
const resetsAt = typeof resetRaw === "string" && resetRaw.trim() && Number.isFinite(Date.parse(resetRaw)) ? new Date(Date.parse(resetRaw)).toISOString() : void 0;
|
|
2570
|
+
const windows = [];
|
|
2571
|
+
const premium = parseQuotaDetail(snapshots["premium_interactions"]);
|
|
2572
|
+
if (premium) {
|
|
2573
|
+
const usedPercent = premium.unlimited ? 0 : premium.entitlement > 0 ? Math.round(Math.min(100, (premium.entitlement - premium.remaining) / premium.entitlement * 100) * 10) / 10 : finiteNumber4(premium.percentRemaining) !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - premium.percentRemaining)) * 10) / 10 : null;
|
|
2574
|
+
if (usedPercent !== null) {
|
|
2575
|
+
windows.push({
|
|
2576
|
+
id: "thirty-day",
|
|
2577
|
+
label: "Monthly",
|
|
2578
|
+
scope: "all",
|
|
2579
|
+
usedPercent,
|
|
2580
|
+
windowMinutes: 30 * 24 * 60,
|
|
2581
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2582
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
2583
|
+
state: "fresh"
|
|
2584
|
+
});
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
const chat = parseQuotaDetail(snapshots["chat"]);
|
|
2588
|
+
if (chat && !chat.unlimited && chat.entitlement > 0) {
|
|
2589
|
+
const usedPercent = Math.round(Math.min(100, (chat.entitlement - chat.remaining) / chat.entitlement * 100) * 10) / 10;
|
|
2590
|
+
windows.push({
|
|
2591
|
+
id: "chat-monthly",
|
|
2592
|
+
label: "Chat (monthly)",
|
|
2593
|
+
scope: "all",
|
|
2594
|
+
usedPercent,
|
|
2595
|
+
windowMinutes: 30 * 24 * 60,
|
|
2596
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2597
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
2598
|
+
state: "fresh"
|
|
2599
|
+
});
|
|
2600
|
+
}
|
|
2601
|
+
return windows.length > 0 ? windows : null;
|
|
2602
|
+
}
|
|
2603
|
+
function githubApiBase(tokens) {
|
|
2604
|
+
return (0, import_subscriptions6.copilotGitHubApiBase)(tokens.enterpriseUrl);
|
|
2605
|
+
}
|
|
2606
|
+
var CopilotAllowanceCollector = class {
|
|
2607
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore5.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch5.fetchUpstream)(url, init, { providerId: "copilot", accountId, redactBodies: true }), now = Date.now) {
|
|
2608
|
+
this.credentials = credentials;
|
|
2609
|
+
this.store = store;
|
|
2610
|
+
this.fetchImpl = fetchImpl;
|
|
2611
|
+
this.now = now;
|
|
2612
|
+
}
|
|
2613
|
+
credentials;
|
|
2614
|
+
store;
|
|
2615
|
+
fetchImpl;
|
|
2616
|
+
now;
|
|
2617
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
2618
|
+
async collectMany(accounts, options = {}) {
|
|
2619
|
+
const settled = await Promise.allSettled(
|
|
2620
|
+
accounts.map((account) => this.collect(account, options))
|
|
2621
|
+
);
|
|
2622
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
2623
|
+
}
|
|
2624
|
+
collect(account, options = {}) {
|
|
2625
|
+
const now = this.now();
|
|
2626
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
2627
|
+
const existing = this.store.get("copilot", account.id, now);
|
|
2628
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
2629
|
+
return Promise.resolve(existing);
|
|
2630
|
+
}
|
|
2631
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
2632
|
+
this.store.set(snapshot);
|
|
2633
|
+
return Promise.resolve(snapshot);
|
|
2634
|
+
}
|
|
2635
|
+
const cached = this.store.get("copilot", account.id, now);
|
|
2636
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
2637
|
+
return Promise.resolve(cached);
|
|
2638
|
+
}
|
|
2639
|
+
const running = this.inFlight.get(account.id);
|
|
2640
|
+
if (running) return running;
|
|
2641
|
+
const promise = this.fetchAccount(account.id, account.tokens).catch(() => this.failureSnapshot(account.id, "copilot_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
2642
|
+
this.inFlight.set(account.id, promise);
|
|
2643
|
+
return promise;
|
|
2644
|
+
}
|
|
2645
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
2646
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
2647
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
2648
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
2649
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
2650
|
+
}
|
|
2651
|
+
async fetchAccount(accountId, tokens) {
|
|
2652
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
|
|
2653
|
+
if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
|
|
2654
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
2655
|
+
if (response.status === 401 || response.status === 403) {
|
|
2656
|
+
const refreshed = await this.credentials.refreshAccountToken("copilot", accountId);
|
|
2657
|
+
if (!refreshed) return this.failureSnapshot(accountId, "copilot_usage_unauthorized", this.now());
|
|
2658
|
+
accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
|
|
2659
|
+
if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
|
|
2002
2660
|
response = await this.request(accountId, accessToken, tokens);
|
|
2661
|
+
if (response.status === 401 || response.status === 403) {
|
|
2662
|
+
return this.failureSnapshot(accountId, "copilot_usage_unauthorized", this.now());
|
|
2663
|
+
}
|
|
2003
2664
|
}
|
|
2004
|
-
if (response.
|
|
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());
|
|
2665
|
+
if (!response.ok) return this.failureSnapshot(accountId, "copilot_usage_http_error", this.now());
|
|
2010
2666
|
let payload;
|
|
2011
2667
|
try {
|
|
2012
2668
|
payload = await response.json();
|
|
2013
2669
|
} catch {
|
|
2014
|
-
return this.failureSnapshot(accountId, "
|
|
2670
|
+
return this.failureSnapshot(accountId, "copilot_usage_invalid_response", this.now());
|
|
2015
2671
|
}
|
|
2016
2672
|
const now = this.now();
|
|
2017
|
-
const windows =
|
|
2673
|
+
const windows = parseCopilotUserPayload(payload, now);
|
|
2018
2674
|
const snapshot = {
|
|
2019
|
-
providerId: "
|
|
2675
|
+
providerId: "copilot",
|
|
2020
2676
|
accountId,
|
|
2021
2677
|
source: "oauth-usage-api",
|
|
2022
2678
|
observedAt: new Date(now).toISOString(),
|
|
2023
|
-
expiresAt: new Date(now +
|
|
2024
|
-
windows: windows
|
|
2025
|
-
{ id: "
|
|
2026
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2679
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2680
|
+
windows: windows ?? [
|
|
2681
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2027
2682
|
],
|
|
2028
|
-
...windows
|
|
2683
|
+
...windows ? {} : { lastErrorCode: "copilot_usage_invalid_response" }
|
|
2029
2684
|
};
|
|
2030
2685
|
this.store.set(snapshot);
|
|
2031
2686
|
return snapshot;
|
|
2032
2687
|
}
|
|
2033
2688
|
request(accountId, accessToken, tokens) {
|
|
2034
|
-
return this.fetchImpl(
|
|
2689
|
+
return this.fetchImpl(`${githubApiBase(tokens)}/copilot_internal/user`, {
|
|
2035
2690
|
method: "GET",
|
|
2036
2691
|
headers: {
|
|
2037
2692
|
Authorization: `Bearer ${accessToken}`,
|
|
2038
2693
|
Accept: "application/json",
|
|
2039
|
-
|
|
2694
|
+
"Content-Type": "application/json",
|
|
2695
|
+
...import_subscriptions6.COPILOT_GITHUB_HEADERS
|
|
2040
2696
|
},
|
|
2041
2697
|
signal: AbortSignal.timeout(15e3)
|
|
2042
2698
|
}, accountId);
|
|
2043
2699
|
}
|
|
2044
2700
|
failureSnapshot(accountId, code, now) {
|
|
2045
|
-
const existing = this.store.get("
|
|
2701
|
+
const existing = this.store.get("copilot", accountId, now);
|
|
2046
2702
|
const snapshot = existing ? {
|
|
2047
2703
|
...existing,
|
|
2048
|
-
expiresAt: new Date(now +
|
|
2704
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2049
2705
|
windows: existing.windows.map((window) => ({
|
|
2050
2706
|
...window,
|
|
2051
2707
|
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2052
2708
|
})),
|
|
2053
2709
|
lastErrorCode: code
|
|
2054
2710
|
} : {
|
|
2055
|
-
providerId: "
|
|
2711
|
+
providerId: "copilot",
|
|
2056
2712
|
accountId,
|
|
2057
2713
|
source: "oauth-usage-api",
|
|
2058
2714
|
observedAt: new Date(now).toISOString(),
|
|
2059
|
-
expiresAt: new Date(now +
|
|
2715
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2060
2716
|
windows: [
|
|
2061
|
-
{ id: "
|
|
2062
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2717
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2063
2718
|
],
|
|
2064
2719
|
lastErrorCode: code
|
|
2065
2720
|
};
|
|
2066
2721
|
this.store.set(snapshot);
|
|
2067
2722
|
return snapshot;
|
|
2068
2723
|
}
|
|
2069
|
-
unsupportedSnapshot(accountId, now
|
|
2724
|
+
unsupportedSnapshot(accountId, now) {
|
|
2070
2725
|
return {
|
|
2071
|
-
providerId: "
|
|
2726
|
+
providerId: "copilot",
|
|
2072
2727
|
accountId,
|
|
2073
2728
|
source: "oauth-usage-api",
|
|
2074
2729
|
observedAt: new Date(now).toISOString(),
|
|
2075
2730
|
windows: [
|
|
2076
|
-
{ id: "
|
|
2077
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2731
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2078
2732
|
],
|
|
2079
|
-
lastErrorCode:
|
|
2733
|
+
lastErrorCode: "copilot_usage_unsupported_auth"
|
|
2080
2734
|
};
|
|
2081
2735
|
}
|
|
2082
2736
|
};
|
|
2083
2737
|
|
|
2084
2738
|
// src/allowance/OpenCodeGoAllowanceCollector.ts
|
|
2085
|
-
var
|
|
2086
|
-
var
|
|
2087
|
-
var
|
|
2739
|
+
var import_AccountAllowanceStore6 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
2740
|
+
var import_upstreamFetch6 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
2741
|
+
var import_subscriptions7 = require("@omnicross/subscriptions");
|
|
2088
2742
|
var OPENCODEGO_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2089
2743
|
var OPENCODEGO_DEFAULT_GO_BASE = "https://opencode.ai/zen/go";
|
|
2090
2744
|
function finitePercent3(value) {
|
|
@@ -2097,7 +2751,7 @@ function isoInstant2(value) {
|
|
|
2097
2751
|
const time = Date.parse(value);
|
|
2098
2752
|
return Number.isFinite(time) ? new Date(time).toISOString() : void 0;
|
|
2099
2753
|
}
|
|
2100
|
-
function
|
|
2754
|
+
function secondsUntil6(instant, now) {
|
|
2101
2755
|
if (!instant) return void 0;
|
|
2102
2756
|
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2103
2757
|
}
|
|
@@ -2112,12 +2766,12 @@ function windowFromPayload3(id, label, minutes, payload, now) {
|
|
|
2112
2766
|
usedPercent,
|
|
2113
2767
|
windowMinutes: minutes,
|
|
2114
2768
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2115
|
-
remainingSeconds:
|
|
2769
|
+
remainingSeconds: secondsUntil6(resetsAt, now),
|
|
2116
2770
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
2117
2771
|
};
|
|
2118
2772
|
}
|
|
2119
2773
|
var OpenCodeGoAllowanceCollector = class {
|
|
2120
|
-
constructor(credentials, store = (0,
|
|
2774
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore6.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch6.fetchUpstream)(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
|
|
2121
2775
|
this.credentials = credentials;
|
|
2122
2776
|
this.store = store;
|
|
2123
2777
|
this.fetchImpl = fetchImpl;
|
|
@@ -2147,7 +2801,7 @@ var OpenCodeGoAllowanceCollector = class {
|
|
|
2147
2801
|
async fetchAccount(account) {
|
|
2148
2802
|
const apiKey = await this.credentials.getAccessTokenForAccount("opencodego", account.id);
|
|
2149
2803
|
if (!apiKey) return this.failureSnapshot(account.id, this.now());
|
|
2150
|
-
const base = account.tokens.baseUrl ? (0,
|
|
2804
|
+
const base = account.tokens.baseUrl ? (0, import_subscriptions7.normalizeOpenCodeGoBaseUrl)(account.tokens.baseUrl) : OPENCODEGO_DEFAULT_GO_BASE;
|
|
2151
2805
|
const response = await this.fetchImpl(`${base}/v1/usage`, {
|
|
2152
2806
|
method: "GET",
|
|
2153
2807
|
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
@@ -2222,7 +2876,7 @@ function codexUnavailable(accountId, now) {
|
|
|
2222
2876
|
};
|
|
2223
2877
|
}
|
|
2224
2878
|
var AccountAllowanceService = class {
|
|
2225
|
-
constructor(credentials, store = (0,
|
|
2879
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore7.getSharedAccountAllowanceStore)(), collector, codexCollector, kimiCollector, opencodegoCollector, grokCollector, copilotCollector, now = Date.now) {
|
|
2226
2880
|
this.credentials = credentials;
|
|
2227
2881
|
this.store = store;
|
|
2228
2882
|
this.now = now;
|
|
@@ -2230,6 +2884,8 @@ var AccountAllowanceService = class {
|
|
|
2230
2884
|
this.codexCollector = codexCollector ?? new CodexAllowanceCollector(credentials, store);
|
|
2231
2885
|
this.kimiCollector = kimiCollector ?? new KimiAllowanceCollector(credentials, store);
|
|
2232
2886
|
this.opencodegoCollector = opencodegoCollector ?? new OpenCodeGoAllowanceCollector(credentials, store);
|
|
2887
|
+
this.grokCollector = grokCollector ?? new GrokAllowanceCollector(credentials, store);
|
|
2888
|
+
this.copilotCollector = copilotCollector ?? new CopilotAllowanceCollector(credentials, store);
|
|
2233
2889
|
}
|
|
2234
2890
|
credentials;
|
|
2235
2891
|
store;
|
|
@@ -2237,6 +2893,8 @@ var AccountAllowanceService = class {
|
|
|
2237
2893
|
claudeCollector;
|
|
2238
2894
|
codexCollector;
|
|
2239
2895
|
kimiCollector;
|
|
2896
|
+
grokCollector;
|
|
2897
|
+
copilotCollector;
|
|
2240
2898
|
opencodegoCollector;
|
|
2241
2899
|
/**
|
|
2242
2900
|
* Read all/filtered snapshots. Claude's and Codex's five-minute caches are
|
|
@@ -2271,11 +2929,23 @@ var AccountAllowanceService = class {
|
|
|
2271
2929
|
(account) => !filter.accountId || account.id === filter.accountId
|
|
2272
2930
|
);
|
|
2273
2931
|
if (wantsOpenCodeGo) await this.opencodegoCollector.collectMany(opencodegoAccounts);
|
|
2932
|
+
const wantsGrok = !filter.providerId || filter.providerId === "grok";
|
|
2933
|
+
const grokAccounts = (config.grokAccounts ?? []).filter(
|
|
2934
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
2935
|
+
);
|
|
2936
|
+
if (wantsGrok) await this.grokCollector.collectMany(grokAccounts);
|
|
2937
|
+
const wantsCopilot = !filter.providerId || filter.providerId === "copilot";
|
|
2938
|
+
const copilotAccounts = (config.copilotAccounts ?? []).filter(
|
|
2939
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
2940
|
+
);
|
|
2941
|
+
if (wantsCopilot) await this.copilotCollector.collectMany(copilotAccounts);
|
|
2274
2942
|
const known = /* @__PURE__ */ new Set();
|
|
2275
2943
|
if (wantsClaude) for (const account of claudeAccounts) known.add(`claude\0${account.id}`);
|
|
2276
2944
|
if (wantsCodex) for (const account of codexAccounts) known.add(`codex\0${account.id}`);
|
|
2277
2945
|
if (wantsKimi) for (const account of kimiAccounts) known.add(`kimi\0${account.id}`);
|
|
2278
2946
|
if (wantsOpenCodeGo) for (const account of opencodegoAccounts) known.add(`opencodego\0${account.id}`);
|
|
2947
|
+
if (wantsGrok) for (const account of grokAccounts) known.add(`grok\0${account.id}`);
|
|
2948
|
+
if (wantsCopilot) for (const account of copilotAccounts) known.add(`copilot\0${account.id}`);
|
|
2279
2949
|
return this.store.list(filter).filter((snapshot) => known.has(`${snapshot.providerId}\0${snapshot.accountId}`));
|
|
2280
2950
|
}
|
|
2281
2951
|
knownAccounts(config) {
|
|
@@ -2283,7 +2953,9 @@ var AccountAllowanceService = class {
|
|
|
2283
2953
|
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
2284
2954
|
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id })),
|
|
2285
2955
|
...(config.kimiAccounts ?? []).map((account) => ({ providerId: "kimi", accountId: account.id })),
|
|
2286
|
-
...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id }))
|
|
2956
|
+
...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id })),
|
|
2957
|
+
...(config.grokAccounts ?? []).map((account) => ({ providerId: "grok", accountId: account.id })),
|
|
2958
|
+
...(config.copilotAccounts ?? []).map((account) => ({ providerId: "copilot", accountId: account.id }))
|
|
2287
2959
|
];
|
|
2288
2960
|
}
|
|
2289
2961
|
/** Force-refresh Claude usage for one account or every stored Claude account. */
|
|
@@ -2326,6 +2998,24 @@ var AccountAllowanceService = class {
|
|
|
2326
2998
|
);
|
|
2327
2999
|
return this.kimiCollector.collectMany(accounts, { force: true });
|
|
2328
3000
|
}
|
|
3001
|
+
/** Force-refresh Copilot usage (copilot_internal/user) for one/all accounts. */
|
|
3002
|
+
async refreshCopilot(accountId) {
|
|
3003
|
+
const config = await this.credentials.getFullConfig();
|
|
3004
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
3005
|
+
const accounts = (config.copilotAccounts ?? []).filter(
|
|
3006
|
+
(account) => !accountId || account.id === accountId
|
|
3007
|
+
);
|
|
3008
|
+
return this.copilotCollector.collectMany(accounts, { force: true });
|
|
3009
|
+
}
|
|
3010
|
+
/** Force-refresh Grok usage (CLI billing proxy) for one/all accounts. */
|
|
3011
|
+
async refreshGrok(accountId) {
|
|
3012
|
+
const config = await this.credentials.getFullConfig();
|
|
3013
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
3014
|
+
const accounts = (config.grokAccounts ?? []).filter(
|
|
3015
|
+
(account) => !accountId || account.id === accountId
|
|
3016
|
+
);
|
|
3017
|
+
return this.grokCollector.collectMany(accounts, { force: true });
|
|
3018
|
+
}
|
|
2329
3019
|
/**
|
|
2330
3020
|
* Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
|
|
2331
3021
|
* collectors preserve their cache + per-account in-flight coalescing; a tick
|
|
@@ -2340,6 +3030,8 @@ var AccountAllowanceService = class {
|
|
|
2340
3030
|
await this.codexCollector.collectMany(config.codexAccounts ?? [], { refreshAheadMs });
|
|
2341
3031
|
await this.kimiCollector.collectMany(config.kimiAccounts ?? [], { refreshAheadMs });
|
|
2342
3032
|
await this.opencodegoCollector.collectMany(config.opencodegoAccounts ?? [], { refreshAheadMs });
|
|
3033
|
+
await this.grokCollector.collectMany(config.grokAccounts ?? [], { refreshAheadMs });
|
|
3034
|
+
await this.copilotCollector.collectMany(config.copilotAccounts ?? [], { refreshAheadMs });
|
|
2343
3035
|
}
|
|
2344
3036
|
/** Remove a cache row as soon as an account is deleted by the admin path. */
|
|
2345
3037
|
removeAccountSnapshot(providerId, accountId) {
|
|
@@ -2434,7 +3126,7 @@ var ClaudeAllowanceRefreshScheduler = class {
|
|
|
2434
3126
|
var import_node_crypto4 = require("crypto");
|
|
2435
3127
|
var import_node_fs6 = require("fs");
|
|
2436
3128
|
var import_node_path6 = require("path");
|
|
2437
|
-
var
|
|
3129
|
+
var import_AccountAllowanceStore8 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
2438
3130
|
var ACCOUNT_ALLOWANCE_CACHE_VERSION = 1;
|
|
2439
3131
|
var MAX_PERSISTED_ALLOWANCE_SNAPSHOTS = 256;
|
|
2440
3132
|
var MAX_ALLOWANCE_CACHE_BYTES = 1e6;
|
|
@@ -2463,7 +3155,7 @@ var JsonAccountAllowancePersistence = class {
|
|
|
2463
3155
|
save(snapshots) {
|
|
2464
3156
|
const rows = [];
|
|
2465
3157
|
for (const snapshot of snapshots) {
|
|
2466
|
-
const normalized2 = (0,
|
|
3158
|
+
const normalized2 = (0, import_AccountAllowanceStore8.normalizeAccountAllowanceSnapshot)(snapshot);
|
|
2467
3159
|
if (!normalized2) continue;
|
|
2468
3160
|
rows.push(normalized2);
|
|
2469
3161
|
if (rows.length >= MAX_PERSISTED_ALLOWANCE_SNAPSHOTS) break;
|
|
@@ -2739,7 +3431,8 @@ var import_outbound_api5 = require("@omnicross/core/outbound-api");
|
|
|
2739
3431
|
var import_image_generation_types = require("@omnicross/contracts/image-generation-types");
|
|
2740
3432
|
var import_AccountAllowanceScheduling2 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
2741
3433
|
var import_SubscriptionAccountHealth = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
2742
|
-
var
|
|
3434
|
+
var import_upstreamFetch9 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
3435
|
+
var import_core3 = require("@omnicross/core");
|
|
2743
3436
|
|
|
2744
3437
|
// src/image-generation/imagesConfigValidation.ts
|
|
2745
3438
|
var import_outbound_api = require("@omnicross/core/outbound-api");
|
|
@@ -3035,6 +3728,7 @@ async function applyServerConfigTransaction(current, next, deps) {
|
|
|
3035
3728
|
|
|
3036
3729
|
// src/config.ts
|
|
3037
3730
|
var import_node_fs8 = require("fs");
|
|
3731
|
+
var import_core = require("@omnicross/core");
|
|
3038
3732
|
var DEFAULT_ADMIN_PORT = 8766;
|
|
3039
3733
|
function validateAdmin(raw) {
|
|
3040
3734
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
@@ -3091,6 +3785,18 @@ var FORMAT_AXIS_TRANSFORMERS = [
|
|
|
3091
3785
|
"openai-response",
|
|
3092
3786
|
"gemini-code-assist"
|
|
3093
3787
|
];
|
|
3788
|
+
function validateExtraHeaders(raw) {
|
|
3789
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
3790
|
+
const reserved = import_core.EXTRA_HEADER_RESERVED_NAMES;
|
|
3791
|
+
const out = {};
|
|
3792
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
3793
|
+
if (!name.trim()) continue;
|
|
3794
|
+
if (typeof value !== "string") continue;
|
|
3795
|
+
if (reserved.has(name.toLowerCase())) continue;
|
|
3796
|
+
out[name] = value;
|
|
3797
|
+
}
|
|
3798
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
3799
|
+
}
|
|
3094
3800
|
function validateApiKeys(raw) {
|
|
3095
3801
|
if (!Array.isArray(raw)) return void 0;
|
|
3096
3802
|
const out = [];
|
|
@@ -3304,6 +4010,9 @@ function validateProvider(raw, index) {
|
|
|
3304
4010
|
apiVersion,
|
|
3305
4011
|
maxConcurrency,
|
|
3306
4012
|
modelsEndpoint,
|
|
4013
|
+
// Static extra headers: load-guard (reserved names dropped), collapse-to-
|
|
4014
|
+
// undefined; enforced by the outbound header funnel + admin probes.
|
|
4015
|
+
extraHeaders: validateExtraHeaders(p["extraHeaders"]),
|
|
3307
4016
|
// Provider transformer config (app-parity child 5): load-guard, collapse-to-
|
|
3308
4017
|
// undefined; non-secret; ENFORCED via resolveTransformerChain (parity-2 child 2).
|
|
3309
4018
|
// Format-axis entries are stripped by `migrateFormatAxis` — `use[]` is the
|
|
@@ -3373,7 +4082,7 @@ var import_node_crypto6 = require("crypto");
|
|
|
3373
4082
|
var import_node_fs10 = require("fs");
|
|
3374
4083
|
var import_node_os3 = require("os");
|
|
3375
4084
|
var import_node_path10 = require("path");
|
|
3376
|
-
var
|
|
4085
|
+
var import_core2 = require("@omnicross/core");
|
|
3377
4086
|
|
|
3378
4087
|
// src/integrations/codexAuthHelper.ts
|
|
3379
4088
|
var import_node_path8 = require("path");
|
|
@@ -3854,7 +4563,7 @@ var IntegrationManager = class {
|
|
|
3854
4563
|
if (!secret) {
|
|
3855
4564
|
throw new IntegrationConflictError("The selected access key cannot be revealed and cannot power a CLI integration.");
|
|
3856
4565
|
}
|
|
3857
|
-
const effective = [...(0,
|
|
4566
|
+
const effective = [...(0, import_core2.effectiveOutboundPermissions)(row.allowedEndpoints)];
|
|
3858
4567
|
const previousPermissions = row.allowedEndpoints === void 0 ? [...effective] : [...row.allowedEndpoints];
|
|
3859
4568
|
const nextPermissions = [...effective];
|
|
3860
4569
|
for (const required of REQUIRED_PERMISSIONS[client]) {
|
|
@@ -3952,7 +4661,7 @@ var IntegrationManager = class {
|
|
|
3952
4661
|
return { binding, row, secret, created: false };
|
|
3953
4662
|
}
|
|
3954
4663
|
async createManagedClientKey(client, state) {
|
|
3955
|
-
const created = await (0,
|
|
4664
|
+
const created = await (0, import_core2.createIntegrationKey)(
|
|
3956
4665
|
this.options.keyDb,
|
|
3957
4666
|
`Omnicross ${displayClient(client)} integration`,
|
|
3958
4667
|
[...REQUIRED_PERMISSIONS[client]]
|
|
@@ -4044,7 +4753,7 @@ var IntegrationManager = class {
|
|
|
4044
4753
|
const row = rows.find((candidate) => candidate.id === keyId);
|
|
4045
4754
|
if (!row) return { usable: false, message: "The bound access key no longer exists." };
|
|
4046
4755
|
const secret = legacy?.secret ?? await this.options.keyDb.outboundApiKeysReveal(keyId) ?? void 0;
|
|
4047
|
-
const allowedEndpoints = [...(0,
|
|
4756
|
+
const allowedEndpoints = [...(0, import_core2.effectiveOutboundPermissions)(row.allowedEndpoints)];
|
|
4048
4757
|
const status = {
|
|
4049
4758
|
id: row.id,
|
|
4050
4759
|
name: row.name,
|
|
@@ -4088,7 +4797,7 @@ var IntegrationManager = class {
|
|
|
4088
4797
|
}
|
|
4089
4798
|
};
|
|
4090
4799
|
function hasRequiredPermissions(row, client) {
|
|
4091
|
-
const allowed = (0,
|
|
4800
|
+
const allowed = (0, import_core2.effectiveOutboundPermissions)(row.allowedEndpoints);
|
|
4092
4801
|
return REQUIRED_PERMISSIONS[client].every((permission) => allowed.includes(permission));
|
|
4093
4802
|
}
|
|
4094
4803
|
function samePermissions(a, b) {
|
|
@@ -4257,7 +4966,10 @@ function mapPresetToProvider(preset, opts) {
|
|
|
4257
4966
|
apiFormat: resolved.format,
|
|
4258
4967
|
baseUrl: opts.baseUrlOverride ?? preset.api_base_url,
|
|
4259
4968
|
apiKey: opts.key,
|
|
4260
|
-
models: Array.isArray(preset.models) ? preset.models : void 0
|
|
4969
|
+
models: Array.isArray(preset.models) ? preset.models : void 0,
|
|
4970
|
+
// Static identity headers (e.g. the Cline client set) survive the mapping —
|
|
4971
|
+
// the CLI-seeded row needs them as much as an admin-API-created one.
|
|
4972
|
+
extraHeaders: preset.extraHeaders
|
|
4261
4973
|
};
|
|
4262
4974
|
return { provider };
|
|
4263
4975
|
}
|
|
@@ -4282,7 +4994,8 @@ function listMappablePresets() {
|
|
|
4282
4994
|
description: preset.description,
|
|
4283
4995
|
features: preset.features,
|
|
4284
4996
|
website: preset.website,
|
|
4285
|
-
modelsEndpoint: preset.modelsEndpoint
|
|
4997
|
+
modelsEndpoint: preset.modelsEndpoint,
|
|
4998
|
+
extraHeaders: preset.extraHeaders
|
|
4286
4999
|
});
|
|
4287
5000
|
}
|
|
4288
5001
|
return { mappable, excluded };
|
|
@@ -4372,11 +5085,11 @@ function preserveOutboundProxySecrets(incoming, current) {
|
|
|
4372
5085
|
}
|
|
4373
5086
|
|
|
4374
5087
|
// src/proxy/upstreamProxyResolver.ts
|
|
4375
|
-
var
|
|
5088
|
+
var import_upstreamFetch7 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
4376
5089
|
var serverProxy;
|
|
4377
5090
|
function setServerProxyConfig(proxy) {
|
|
4378
5091
|
serverProxy = proxy;
|
|
4379
|
-
(0,
|
|
5092
|
+
(0, import_upstreamFetch7.bumpUpstreamProxyGeneration)();
|
|
4380
5093
|
}
|
|
4381
5094
|
function getServerProxyConfig() {
|
|
4382
5095
|
return serverProxy;
|
|
@@ -4444,7 +5157,7 @@ function createUpstreamProxyResolver(src = {}) {
|
|
|
4444
5157
|
}
|
|
4445
5158
|
|
|
4446
5159
|
// src/admin/accountsOAuth.ts
|
|
4447
|
-
var
|
|
5160
|
+
var import_subscriptions8 = require("@omnicross/subscriptions");
|
|
4448
5161
|
|
|
4449
5162
|
// src/admin/accountsWrite.ts
|
|
4450
5163
|
var VALID_PROVIDER_IDS = [
|
|
@@ -4452,7 +5165,9 @@ var VALID_PROVIDER_IDS = [
|
|
|
4452
5165
|
"codex",
|
|
4453
5166
|
"gemini",
|
|
4454
5167
|
"opencodego",
|
|
4455
|
-
"kimi"
|
|
5168
|
+
"kimi",
|
|
5169
|
+
"grok",
|
|
5170
|
+
"copilot"
|
|
4456
5171
|
];
|
|
4457
5172
|
function asSubscriptionProviderId(id) {
|
|
4458
5173
|
return VALID_PROVIDER_IDS.includes(id) ? id : null;
|
|
@@ -4600,6 +5315,40 @@ function validateKimi(body) {
|
|
|
4600
5315
|
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "deviceId", "lastRefreshedAt", "errorMessage"]);
|
|
4601
5316
|
return out;
|
|
4602
5317
|
}
|
|
5318
|
+
function validateGrok(body) {
|
|
5319
|
+
const authMethod = str(body["authMethod"]);
|
|
5320
|
+
const status = str(body["status"]);
|
|
5321
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
5322
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
5323
|
+
const out = {
|
|
5324
|
+
authMethod,
|
|
5325
|
+
status
|
|
5326
|
+
};
|
|
5327
|
+
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "lastRefreshedAt", "errorMessage"]);
|
|
5328
|
+
return out;
|
|
5329
|
+
}
|
|
5330
|
+
function validateCopilot(body) {
|
|
5331
|
+
const authMethod = str(body["authMethod"]);
|
|
5332
|
+
const status = str(body["status"]);
|
|
5333
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
5334
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
5335
|
+
const out = {
|
|
5336
|
+
authMethod,
|
|
5337
|
+
status
|
|
5338
|
+
};
|
|
5339
|
+
copyOptional(out, body, [
|
|
5340
|
+
"accessToken",
|
|
5341
|
+
"refreshToken",
|
|
5342
|
+
"expiresAt",
|
|
5343
|
+
"accountId",
|
|
5344
|
+
"email",
|
|
5345
|
+
"apiEndpoint",
|
|
5346
|
+
"enterpriseUrl",
|
|
5347
|
+
"lastRefreshedAt",
|
|
5348
|
+
"errorMessage"
|
|
5349
|
+
]);
|
|
5350
|
+
return out;
|
|
5351
|
+
}
|
|
4603
5352
|
function validateOpenCodeGo(body) {
|
|
4604
5353
|
const authMethod = str(body["authMethod"]);
|
|
4605
5354
|
const status = str(body["status"]);
|
|
@@ -4637,6 +5386,10 @@ function validateTokenBody(providerId, body) {
|
|
|
4637
5386
|
return validateOpenCodeGo(body);
|
|
4638
5387
|
case "kimi":
|
|
4639
5388
|
return validateKimi(body);
|
|
5389
|
+
case "grok":
|
|
5390
|
+
return validateGrok(body);
|
|
5391
|
+
case "copilot":
|
|
5392
|
+
return validateCopilot(body);
|
|
4640
5393
|
default:
|
|
4641
5394
|
return null;
|
|
4642
5395
|
}
|
|
@@ -4666,37 +5419,37 @@ async function statusEntryFor(reader, providerId) {
|
|
|
4666
5419
|
|
|
4667
5420
|
// src/admin/accountsOAuth.ts
|
|
4668
5421
|
var OAUTH_HTTP_PROVIDERS = /* @__PURE__ */ new Set(["claude", "gemini"]);
|
|
4669
|
-
function
|
|
5422
|
+
function err5(status, message) {
|
|
4670
5423
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
4671
5424
|
}
|
|
4672
5425
|
function handleOAuthStart(providerId, deps) {
|
|
4673
5426
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
4674
|
-
return
|
|
5427
|
+
return err5(400, `oauth not available for provider '${providerId}'`);
|
|
4675
5428
|
}
|
|
4676
|
-
const flow = providerId === "claude" ?
|
|
5429
|
+
const flow = providerId === "claude" ? import_subscriptions8.claudeOAuth : import_subscriptions8.geminiOAuth;
|
|
4677
5430
|
const { authUrl, codeVerifier, state } = flow.generateAuthParams();
|
|
4678
5431
|
const sessionId = deps.oauthSessions.put({ providerId, codeVerifier, state });
|
|
4679
5432
|
return { status: 200, body: { authUrl, sessionId } };
|
|
4680
5433
|
}
|
|
4681
5434
|
async function handleOAuthComplete(providerId, body, deps) {
|
|
4682
5435
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
4683
|
-
return
|
|
5436
|
+
return err5(400, `oauth not available for provider '${providerId}'`);
|
|
4684
5437
|
}
|
|
4685
5438
|
const sessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : "";
|
|
4686
5439
|
const rawCode = typeof body["code"] === "string" ? body["code"] : "";
|
|
4687
|
-
if (!sessionId) return
|
|
4688
|
-
if (!rawCode) return
|
|
5440
|
+
if (!sessionId) return err5(400, "oauth complete requires { sessionId }");
|
|
5441
|
+
if (!rawCode) return err5(400, "oauth complete requires { code }");
|
|
4689
5442
|
const session = deps.oauthSessions.peek(sessionId);
|
|
4690
|
-
if (!session) return
|
|
5443
|
+
if (!session) return err5(410, "oauth session is unknown, expired, or already used");
|
|
4691
5444
|
if (session.providerId !== providerId) {
|
|
4692
|
-
return
|
|
5445
|
+
return err5(400, `oauth session does not match provider '${providerId}'`);
|
|
4693
5446
|
}
|
|
4694
5447
|
let code = rawCode.trim();
|
|
4695
5448
|
if (providerId === "claude") {
|
|
4696
5449
|
const [splitCode, pastedState] = code.split("#");
|
|
4697
|
-
if (!splitCode) return
|
|
5450
|
+
if (!splitCode) return err5(400, "no authorization code was provided");
|
|
4698
5451
|
if (pastedState && pastedState !== session.state) {
|
|
4699
|
-
return
|
|
5452
|
+
return err5(400, "oauth state did not match (possible CSRF) \u2014 aborting");
|
|
4700
5453
|
}
|
|
4701
5454
|
code = splitCode;
|
|
4702
5455
|
}
|
|
@@ -4706,7 +5459,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
4706
5459
|
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
|
|
4707
5460
|
} catch (exchangeError) {
|
|
4708
5461
|
const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
|
|
4709
|
-
return
|
|
5462
|
+
return err5(502, `oauth token exchange failed for '${providerId}': ${reason}`);
|
|
4710
5463
|
}
|
|
4711
5464
|
deps.oauthSessions.consume(sessionId);
|
|
4712
5465
|
const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
|
|
@@ -4715,7 +5468,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
4715
5468
|
return { status: 200, body: status ? { account: status } : { ok: true } };
|
|
4716
5469
|
}
|
|
4717
5470
|
async function exchangeClaude(code, codeVerifier, state, exchangeFetch) {
|
|
4718
|
-
const result = await
|
|
5471
|
+
const result = await import_subscriptions8.claudeOAuth.exchangeCodeForTokens(
|
|
4719
5472
|
{ authorizationCode: code, codeVerifier, state },
|
|
4720
5473
|
exchangeFetch
|
|
4721
5474
|
);
|
|
@@ -4731,7 +5484,7 @@ async function exchangeClaude(code, codeVerifier, state, exchangeFetch) {
|
|
|
4731
5484
|
};
|
|
4732
5485
|
}
|
|
4733
5486
|
async function exchangeGemini(code, codeVerifier, exchangeFetch) {
|
|
4734
|
-
const result = await
|
|
5487
|
+
const result = await import_subscriptions8.geminiOAuth.exchangeCodeForTokens(code, codeVerifier, exchangeFetch);
|
|
4735
5488
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
4736
5489
|
return {
|
|
4737
5490
|
authMethod: "oauth",
|
|
@@ -5036,8 +5789,8 @@ function errBody(message) {
|
|
|
5036
5789
|
return { error: { type: "admin_api_error", message } };
|
|
5037
5790
|
}
|
|
5038
5791
|
var defaultCommandRunner = (command) => new Promise((resolve11) => {
|
|
5039
|
-
(0, import_node_child_process.exec)(command, { timeout: 18e4 }, (
|
|
5040
|
-
if (
|
|
5792
|
+
(0, import_node_child_process.exec)(command, { timeout: 18e4 }, (err8, _stdout, stderr) => {
|
|
5793
|
+
if (err8) resolve11({ ok: false, error: stderr.trim() || err8.message });
|
|
5041
5794
|
else resolve11({ ok: true });
|
|
5042
5795
|
});
|
|
5043
5796
|
});
|
|
@@ -5083,8 +5836,8 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
5083
5836
|
providerId: typeof body["providerId"] === "string" ? body["providerId"] : void 0,
|
|
5084
5837
|
model: typeof body["model"] === "string" ? body["model"] : void 0
|
|
5085
5838
|
});
|
|
5086
|
-
} catch (
|
|
5087
|
-
return { status: 400, body: errBody(
|
|
5839
|
+
} catch (err8) {
|
|
5840
|
+
return { status: 400, body: errBody(err8 instanceof Error ? err8.message : "no launch target") };
|
|
5088
5841
|
}
|
|
5089
5842
|
const id = (0, import_node_crypto7.randomUUID)();
|
|
5090
5843
|
let leaseId2;
|
|
@@ -5112,9 +5865,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
5112
5865
|
} else {
|
|
5113
5866
|
launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
|
|
5114
5867
|
}
|
|
5115
|
-
} catch (
|
|
5116
|
-
const status =
|
|
5117
|
-
return { status, body: errBody(
|
|
5868
|
+
} catch (err8) {
|
|
5869
|
+
const status = err8 instanceof import_provider_proxy2.RouteLeaseError ? err8.status : 400;
|
|
5870
|
+
return { status, body: errBody(err8 instanceof Error ? err8.message : "failed to build launch env") };
|
|
5118
5871
|
}
|
|
5119
5872
|
const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
|
|
5120
5873
|
const opener = ctx.opener ?? defaultTerminalOpener;
|
|
@@ -5142,9 +5895,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
5142
5895
|
onFailure: onSessionEnd
|
|
5143
5896
|
});
|
|
5144
5897
|
if (cleanup) openerCleanup = cleanup;
|
|
5145
|
-
} catch (
|
|
5898
|
+
} catch (err8) {
|
|
5146
5899
|
onSessionEnd();
|
|
5147
|
-
return { status: 500, body: errBody(
|
|
5900
|
+
return { status: 500, body: errBody(err8 instanceof Error ? err8.message : "failed to open terminal") };
|
|
5148
5901
|
}
|
|
5149
5902
|
if (ended) {
|
|
5150
5903
|
openerCleanup?.();
|
|
@@ -5399,7 +6152,7 @@ async function runSearchLiveChecks(contributions, now = () => (/* @__PURE__ */ n
|
|
|
5399
6152
|
}
|
|
5400
6153
|
|
|
5401
6154
|
// src/search/SearchAssembly.ts
|
|
5402
|
-
var
|
|
6155
|
+
var import_upstreamFetch8 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
5403
6156
|
var import_search = require("@omnicross/core/search");
|
|
5404
6157
|
var import_api2 = require("@omnicross/core/search/api");
|
|
5405
6158
|
var import_http2 = require("@omnicross/core/search/http");
|
|
@@ -5417,7 +6170,7 @@ function searchPolicyFrom(config) {
|
|
|
5417
6170
|
};
|
|
5418
6171
|
}
|
|
5419
6172
|
function resolveSearchUpstreamDispatcher(url) {
|
|
5420
|
-
return (0,
|
|
6173
|
+
return (0, import_upstreamFetch8.resolveUpstreamDispatcher)({ url });
|
|
5421
6174
|
}
|
|
5422
6175
|
var searchUpstreamProxyConfig = createUpstreamProxyResolver();
|
|
5423
6176
|
function resolveSearchUpstreamProxyConfig(url) {
|
|
@@ -5699,7 +6452,7 @@ async function handleSearchQuery(req, res, deps) {
|
|
|
5699
6452
|
// src/admin/searchAdminView.ts
|
|
5700
6453
|
var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
|
|
5701
6454
|
var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
|
|
5702
|
-
function
|
|
6455
|
+
function isRecord4(value) {
|
|
5703
6456
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5704
6457
|
}
|
|
5705
6458
|
function redactSearchServerConfig(search) {
|
|
@@ -5749,13 +6502,13 @@ function resolveSecretField(entry, field, stored) {
|
|
|
5749
6502
|
else delete entry[field];
|
|
5750
6503
|
}
|
|
5751
6504
|
function preserveSearchSecrets(incoming, current) {
|
|
5752
|
-
if (!
|
|
6505
|
+
if (!isRecord4(incoming)) return incoming;
|
|
5753
6506
|
const section = { ...incoming };
|
|
5754
6507
|
const providersValue = section["providers"];
|
|
5755
|
-
if (!
|
|
6508
|
+
if (!isRecord4(providersValue)) return section;
|
|
5756
6509
|
const providers = {};
|
|
5757
6510
|
for (const [id, entryValue] of Object.entries(providersValue)) {
|
|
5758
|
-
if (!
|
|
6511
|
+
if (!isRecord4(entryValue)) {
|
|
5759
6512
|
providers[id] = entryValue;
|
|
5760
6513
|
continue;
|
|
5761
6514
|
}
|
|
@@ -5833,7 +6586,7 @@ function parseKeyPolicyBody(body) {
|
|
|
5833
6586
|
var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
|
|
5834
6587
|
var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
|
|
5835
6588
|
var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
|
|
5836
|
-
function
|
|
6589
|
+
function isRecord5(value) {
|
|
5837
6590
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
5838
6591
|
}
|
|
5839
6592
|
function nonBlank(value) {
|
|
@@ -5853,7 +6606,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5853
6606
|
const ids = /* @__PURE__ */ new Set();
|
|
5854
6607
|
raw.forEach((entry, index) => {
|
|
5855
6608
|
const path2 = `bindings[${index}]`;
|
|
5856
|
-
if (!
|
|
6609
|
+
if (!isRecord5(entry)) {
|
|
5857
6610
|
errors.push(`${path2} must be an object`);
|
|
5858
6611
|
return;
|
|
5859
6612
|
}
|
|
@@ -5882,12 +6635,12 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5882
6635
|
} else if (entry.modelMappings.length > 100) {
|
|
5883
6636
|
errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
|
|
5884
6637
|
} else if (entry.modelMappings.some(
|
|
5885
|
-
(mapping) => !
|
|
6638
|
+
(mapping) => !isRecord5(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
|
|
5886
6639
|
)) {
|
|
5887
6640
|
errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
|
|
5888
6641
|
}
|
|
5889
6642
|
}
|
|
5890
|
-
if (!
|
|
6643
|
+
if (!isRecord5(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
|
|
5891
6644
|
errors.push(`${path2}.target is invalid`);
|
|
5892
6645
|
} else {
|
|
5893
6646
|
if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
|
|
@@ -5902,7 +6655,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5902
6655
|
}
|
|
5903
6656
|
}
|
|
5904
6657
|
if (entry.modelMap !== void 0) {
|
|
5905
|
-
if (!
|
|
6658
|
+
if (!isRecord5(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
|
|
5906
6659
|
errors.push(`${path2}.modelMap must contain string values`);
|
|
5907
6660
|
}
|
|
5908
6661
|
}
|
|
@@ -6185,7 +6938,9 @@ var PROVIDER_KEYS = {
|
|
|
6185
6938
|
accounts: "opencodegoAccounts",
|
|
6186
6939
|
active: "activeOpencodegoAccountId"
|
|
6187
6940
|
},
|
|
6188
|
-
kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" }
|
|
6941
|
+
kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" },
|
|
6942
|
+
grok: { block: "grok", accounts: "grokAccounts", active: "activeGrokAccountId" },
|
|
6943
|
+
copilot: { block: "copilot", accounts: "copilotAccounts", active: "activeCopilotAccountId" }
|
|
6189
6944
|
};
|
|
6190
6945
|
function clone(value) {
|
|
6191
6946
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -6707,7 +7462,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
|
|
|
6707
7462
|
}
|
|
6708
7463
|
|
|
6709
7464
|
// src/admin/adminMigration.ts
|
|
6710
|
-
function
|
|
7465
|
+
function err6(status, message) {
|
|
6711
7466
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
6712
7467
|
}
|
|
6713
7468
|
async function handleExport(body, deps) {
|
|
@@ -6717,30 +7472,30 @@ async function handleExport(body, deps) {
|
|
|
6717
7472
|
return { status: 200, body: { pack, version: BUNDLE_VERSION } };
|
|
6718
7473
|
} catch (error) {
|
|
6719
7474
|
if (error instanceof WeakPassphraseError) {
|
|
6720
|
-
return
|
|
7475
|
+
return err6(400, error.message);
|
|
6721
7476
|
}
|
|
6722
|
-
return
|
|
7477
|
+
return err6(500, "failed to build the migration pack");
|
|
6723
7478
|
}
|
|
6724
7479
|
}
|
|
6725
7480
|
async function handleImport(body, deps) {
|
|
6726
7481
|
const blob = typeof body["blob"] === "string" ? body["blob"] : "";
|
|
6727
7482
|
const passphrase = typeof body["passphrase"] === "string" ? body["passphrase"] : "";
|
|
6728
7483
|
const mode = body["mode"] === "overwrite" ? "overwrite" : "merge";
|
|
6729
|
-
if (!blob) return
|
|
7484
|
+
if (!blob) return err6(400, "import requires { blob }");
|
|
6730
7485
|
try {
|
|
6731
7486
|
const counts = await applyImport(blob, passphrase, mode, deps, deps.parseProviderInput);
|
|
6732
7487
|
return { status: 200, body: counts };
|
|
6733
7488
|
} catch (error) {
|
|
6734
7489
|
if (error instanceof WeakPassphraseError) {
|
|
6735
|
-
return
|
|
7490
|
+
return err6(400, error.message);
|
|
6736
7491
|
}
|
|
6737
|
-
return
|
|
7492
|
+
return err6(400, error instanceof Error ? error.message : "import failed");
|
|
6738
7493
|
}
|
|
6739
7494
|
}
|
|
6740
7495
|
|
|
6741
7496
|
// src/admin/usagePricing.ts
|
|
6742
7497
|
var import_usage = require("@omnicross/core/usage");
|
|
6743
|
-
var
|
|
7498
|
+
var err7 = (status, message) => ({
|
|
6744
7499
|
status,
|
|
6745
7500
|
body: { error: { type: "admin_api_error", message } }
|
|
6746
7501
|
});
|
|
@@ -6753,7 +7508,7 @@ function parseRange(query2) {
|
|
|
6753
7508
|
const startTs = parseFiniteInt(query2.get("startTs"));
|
|
6754
7509
|
const endTs = parseFiniteInt(query2.get("endTs"));
|
|
6755
7510
|
if (startTs === null || endTs === null) {
|
|
6756
|
-
return
|
|
7511
|
+
return err7(400, "startTs and endTs are required finite-integer unix-millis query params");
|
|
6757
7512
|
}
|
|
6758
7513
|
return { startTs, endTs };
|
|
6759
7514
|
}
|
|
@@ -6778,14 +7533,14 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
6778
7533
|
case "timeseries": {
|
|
6779
7534
|
const bucket = query2.get("bucket");
|
|
6780
7535
|
if (bucket !== "hour" && bucket !== "day" && bucket !== "month") {
|
|
6781
|
-
return
|
|
7536
|
+
return err7(400, "bucket must be one of 'hour', 'day', 'month'");
|
|
6782
7537
|
}
|
|
6783
7538
|
const now = Date.now();
|
|
6784
7539
|
const clamped = { startTs: range.startTs, endTs: Math.min(range.endTs, now) };
|
|
6785
7540
|
if (clamped.startTs < clamped.endTs) {
|
|
6786
7541
|
const projected = Math.ceil((clamped.endTs - clamped.startTs) / BUCKET_SPAN_MS[bucket]) + 1;
|
|
6787
7542
|
if (projected > MAX_TIMESERIES_BUCKETS) {
|
|
6788
|
-
return
|
|
7543
|
+
return err7(
|
|
6789
7544
|
400,
|
|
6790
7545
|
`requested range projects ~${projected} '${bucket}' buckets (max ${MAX_TIMESERIES_BUCKETS}); narrow the range or use a coarser bucket`
|
|
6791
7546
|
);
|
|
@@ -6808,7 +7563,7 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
6808
7563
|
};
|
|
6809
7564
|
}
|
|
6810
7565
|
default:
|
|
6811
|
-
return
|
|
7566
|
+
return err7(404, `unknown usage view '${view ?? ""}'`);
|
|
6812
7567
|
}
|
|
6813
7568
|
}
|
|
6814
7569
|
function poolKeyLabels(cfg) {
|
|
@@ -6857,7 +7612,7 @@ async function handlePricingList(deps) {
|
|
|
6857
7612
|
async function handlePricingUpsert(body, deps) {
|
|
6858
7613
|
const input = parsePricingEntryInput(body);
|
|
6859
7614
|
if (!input) {
|
|
6860
|
-
return
|
|
7615
|
+
return err7(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
|
|
6861
7616
|
}
|
|
6862
7617
|
const entry = await deps.pricingEngine.upsertManual(input);
|
|
6863
7618
|
return { status: 200, body: { entry } };
|
|
@@ -6866,7 +7621,7 @@ async function handlePricingDelete(query2, deps) {
|
|
|
6866
7621
|
const providerId = query2.get("providerId")?.trim() ?? "";
|
|
6867
7622
|
const modelId = query2.get("modelId")?.trim() ?? "";
|
|
6868
7623
|
if (!providerId || !modelId) {
|
|
6869
|
-
return
|
|
7624
|
+
return err7(400, "delete requires providerId and modelId query params");
|
|
6870
7625
|
}
|
|
6871
7626
|
const deleted = await deps.pricingStore.delete(providerId, modelId);
|
|
6872
7627
|
if (deleted) await deps.pricingEngine.invalidateCache();
|
|
@@ -6886,13 +7641,13 @@ async function handlePricingFetchLatest(deps) {
|
|
|
6886
7641
|
}
|
|
6887
7642
|
};
|
|
6888
7643
|
} catch (e) {
|
|
6889
|
-
return
|
|
7644
|
+
return err7(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
6890
7645
|
}
|
|
6891
7646
|
}
|
|
6892
7647
|
async function handlePricingResolveConflicts(body, deps) {
|
|
6893
7648
|
const raw = body["resolutions"];
|
|
6894
7649
|
if (!Array.isArray(raw)) {
|
|
6895
|
-
return
|
|
7650
|
+
return err7(400, "resolve-conflicts requires { resolutions: [...] }");
|
|
6896
7651
|
}
|
|
6897
7652
|
const currentRows = await deps.pricingStore.getAll();
|
|
6898
7653
|
const userEditedKeys = new Set(
|
|
@@ -6902,21 +7657,21 @@ async function handlePricingResolveConflicts(body, deps) {
|
|
|
6902
7657
|
const pendingIncoming = /* @__PURE__ */ new Map();
|
|
6903
7658
|
let staleCount = 0;
|
|
6904
7659
|
for (const item of raw) {
|
|
6905
|
-
if (!item || typeof item !== "object") return
|
|
7660
|
+
if (!item || typeof item !== "object") return err7(400, "invalid resolution entry");
|
|
6906
7661
|
const r = item;
|
|
6907
7662
|
const action = r["action"];
|
|
6908
7663
|
if (action !== "overwrite" && action !== "skip") {
|
|
6909
|
-
return
|
|
7664
|
+
return err7(400, "resolution action must be 'overwrite' or 'skip'");
|
|
6910
7665
|
}
|
|
6911
7666
|
const providerId = typeof r["providerId"] === "string" && r["providerId"].trim() ? r["providerId"].trim() : "";
|
|
6912
7667
|
const modelId = typeof r["modelId"] === "string" && r["modelId"].trim() ? r["modelId"].trim() : "";
|
|
6913
7668
|
if (!providerId || !modelId) {
|
|
6914
|
-
return
|
|
7669
|
+
return err7(400, "each resolution requires top-level providerId and modelId");
|
|
6915
7670
|
}
|
|
6916
7671
|
const incoming = parsePricingEntryInput(r["incoming"]);
|
|
6917
|
-
if (!incoming) return
|
|
7672
|
+
if (!incoming) return err7(400, "each resolution must echo a valid incoming pricing entry");
|
|
6918
7673
|
if (incoming.providerId !== providerId || incoming.modelId !== modelId) {
|
|
6919
|
-
return
|
|
7674
|
+
return err7(400, "resolution providerId/modelId must match the echoed incoming entry");
|
|
6920
7675
|
}
|
|
6921
7676
|
const key = `${providerId}::${modelId}`;
|
|
6922
7677
|
if (action === "overwrite" && !userEditedKeys.has(key)) {
|
|
@@ -6961,7 +7716,7 @@ function query(req) {
|
|
|
6961
7716
|
}
|
|
6962
7717
|
function allowanceProvider(value) {
|
|
6963
7718
|
if (!value) return void 0;
|
|
6964
|
-
return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" ? value : null;
|
|
7719
|
+
return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" || value === "grok" || value === "copilot" ? value : null;
|
|
6965
7720
|
}
|
|
6966
7721
|
async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
6967
7722
|
if (!service) return writeError2(res, 501, "account allowance service is not available");
|
|
@@ -6976,7 +7731,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
6976
7731
|
const pathProvider = rest.length >= 2 ? rest[0] : null;
|
|
6977
7732
|
const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
|
|
6978
7733
|
if (providerId === null) {
|
|
6979
|
-
return writeError2(res, 400, "providerId must be claude, codex, kimi, or
|
|
7734
|
+
return writeError2(res, 400, "providerId must be claude, codex, kimi, opencodego, grok, or copilot");
|
|
6980
7735
|
}
|
|
6981
7736
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
6982
7737
|
const allowances = await service.list({ providerId, accountId });
|
|
@@ -7018,6 +7773,26 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7018
7773
|
}
|
|
7019
7774
|
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7020
7775
|
}
|
|
7776
|
+
if (requestedProvider === "copilot") {
|
|
7777
|
+
if (!service.refreshCopilot) {
|
|
7778
|
+
return writeError2(res, 501, "copilot allowance refresh is not available");
|
|
7779
|
+
}
|
|
7780
|
+
const allowances2 = await service.refreshCopilot(accountId);
|
|
7781
|
+
if (accountId && allowances2.length === 0) {
|
|
7782
|
+
return writeError2(res, 404, `Copilot account '${accountId}' not found`);
|
|
7783
|
+
}
|
|
7784
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7785
|
+
}
|
|
7786
|
+
if (requestedProvider === "grok") {
|
|
7787
|
+
if (!service.refreshGrok) {
|
|
7788
|
+
return writeError2(res, 501, "grok allowance refresh is not available");
|
|
7789
|
+
}
|
|
7790
|
+
const allowances2 = await service.refreshGrok(accountId);
|
|
7791
|
+
if (accountId && allowances2.length === 0) {
|
|
7792
|
+
return writeError2(res, 404, `Grok account '${accountId}' not found`);
|
|
7793
|
+
}
|
|
7794
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7795
|
+
}
|
|
7021
7796
|
const allowances = await service.refreshClaude(accountId);
|
|
7022
7797
|
if (accountId && allowances.length === 0) {
|
|
7023
7798
|
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
@@ -7116,6 +7891,9 @@ function toProviderView(row) {
|
|
|
7116
7891
|
apiVersion: row.apiVersion,
|
|
7117
7892
|
maxConcurrency: row.maxConcurrency,
|
|
7118
7893
|
modelsEndpoint: row.modelsEndpoint,
|
|
7894
|
+
// Static extra headers round-trip VERBATIM (non-secret identity values;
|
|
7895
|
+
// auth/content names were already dropped at the write/load gate).
|
|
7896
|
+
extraHeaders: row.extraHeaders,
|
|
7119
7897
|
// app-parity child 5: transformer config round-trips VERBATIM (non-secret —
|
|
7120
7898
|
// transform-rule names + options, no key material; absent stays absent).
|
|
7121
7899
|
transformer: row.transformer,
|
|
@@ -7185,8 +7963,8 @@ async function handleAdminApi(req, res, path2, deps) {
|
|
|
7185
7963
|
default:
|
|
7186
7964
|
return writeJsonError(res, 404, `unknown admin resource '${resource}'`);
|
|
7187
7965
|
}
|
|
7188
|
-
} catch (
|
|
7189
|
-
writeJsonError(res, 500,
|
|
7966
|
+
} catch (err8) {
|
|
7967
|
+
writeJsonError(res, 500, err8 instanceof Error ? err8.message : String(err8));
|
|
7190
7968
|
}
|
|
7191
7969
|
}
|
|
7192
7970
|
function requestQuery(req) {
|
|
@@ -7344,6 +8122,9 @@ async function handleProviderReorder(req, res, cfg, deps) {
|
|
|
7344
8122
|
persistProviders(cfg, deps);
|
|
7345
8123
|
return writeJson4(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
|
|
7346
8124
|
}
|
|
8125
|
+
function expandRowExtraHeaders(row) {
|
|
8126
|
+
return (0, import_core3.mergeExtraHeaders)({}, row.extraHeaders);
|
|
8127
|
+
}
|
|
7347
8128
|
async function handleDiscoverModels(res, id, cfg) {
|
|
7348
8129
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
7349
8130
|
const row = cfg.providers.find((p) => p.id === id);
|
|
@@ -7357,7 +8138,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
7357
8138
|
try {
|
|
7358
8139
|
const headers = { Accept: "application/json" };
|
|
7359
8140
|
if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
|
|
7360
|
-
|
|
8141
|
+
Object.assign(headers, expandRowExtraHeaders(row));
|
|
8142
|
+
const response = await (0, import_upstreamFetch9.fetchUpstream)(url, { method: "GET", headers }, { providerId: "byo" });
|
|
7361
8143
|
if (!response.ok) {
|
|
7362
8144
|
const text = await response.text().catch(() => "");
|
|
7363
8145
|
let message = text.slice(0, 300);
|
|
@@ -7374,8 +8156,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
7374
8156
|
const data = await response.json();
|
|
7375
8157
|
const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
|
|
7376
8158
|
return writeJson4(res, 200, { models });
|
|
7377
|
-
} catch (
|
|
7378
|
-
const message =
|
|
8159
|
+
} catch (err8) {
|
|
8160
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
7379
8161
|
return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
|
|
7380
8162
|
}
|
|
7381
8163
|
}
|
|
@@ -7414,9 +8196,10 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
7414
8196
|
messages: [{ role: "user", content: prompt }]
|
|
7415
8197
|
};
|
|
7416
8198
|
}
|
|
8199
|
+
Object.assign(headers, expandRowExtraHeaders(row));
|
|
7417
8200
|
const startedAt = Date.now();
|
|
7418
8201
|
try {
|
|
7419
|
-
const response = await (0,
|
|
8202
|
+
const response = await (0, import_upstreamFetch9.fetchUpstream)(
|
|
7420
8203
|
url,
|
|
7421
8204
|
{ method: "POST", headers, body: JSON.stringify(payload) },
|
|
7422
8205
|
{ providerId: "byo" }
|
|
@@ -7438,8 +8221,8 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
7438
8221
|
latencyMs,
|
|
7439
8222
|
sample: extractSampleText(text, row.apiFormat)
|
|
7440
8223
|
});
|
|
7441
|
-
} catch (
|
|
7442
|
-
const message =
|
|
8224
|
+
} catch (err8) {
|
|
8225
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
7443
8226
|
return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
|
|
7444
8227
|
}
|
|
7445
8228
|
}
|
|
@@ -7721,6 +8504,7 @@ function parseProviderInput(body, existing) {
|
|
|
7721
8504
|
const apiVersion = typeof body["apiVersion"] === "string" && body["apiVersion"].length > 0 ? body["apiVersion"] : body["apiVersion"] === null ? void 0 : existing?.apiVersion;
|
|
7722
8505
|
const modelsEndpoint = typeof body["modelsEndpoint"] === "string" && body["modelsEndpoint"].length > 0 ? body["modelsEndpoint"] : body["modelsEndpoint"] === null ? void 0 : existing?.modelsEndpoint;
|
|
7723
8506
|
const maxConcurrency = typeof body["maxConcurrency"] === "number" && Number.isFinite(body["maxConcurrency"]) ? body["maxConcurrency"] : body["maxConcurrency"] === null ? void 0 : existing?.maxConcurrency;
|
|
8507
|
+
const extraHeaders = body["extraHeaders"] === null ? void 0 : body["extraHeaders"] === void 0 ? existing?.extraHeaders : validateExtraHeaders(body["extraHeaders"]);
|
|
7724
8508
|
const transformer = body["transformer"] === null ? void 0 : parseTransformerInput(body["transformer"], existing?.transformer);
|
|
7725
8509
|
const codingPlan = body["codingPlan"] === null ? void 0 : body["codingPlan"] === void 0 ? existing?.codingPlan : body["codingPlan"] && typeof body["codingPlan"] === "object" && !Array.isArray(body["codingPlan"]) ? parseCodingPlanInput(body["codingPlan"], existing?.codingPlan) : existing?.codingPlan;
|
|
7726
8510
|
const apiModes = body["apiModes"] === null ? void 0 : parseApiModesInput(body["apiModes"], existing?.apiModes);
|
|
@@ -7746,6 +8530,7 @@ function parseProviderInput(body, existing) {
|
|
|
7746
8530
|
apiVersion,
|
|
7747
8531
|
maxConcurrency,
|
|
7748
8532
|
modelsEndpoint,
|
|
8533
|
+
extraHeaders,
|
|
7749
8534
|
transformer: migrated.transformer,
|
|
7750
8535
|
codingPlan,
|
|
7751
8536
|
apiModes,
|
|
@@ -7767,7 +8552,10 @@ function handlePresets(res, method) {
|
|
|
7767
8552
|
description: p.description,
|
|
7768
8553
|
features: p.features,
|
|
7769
8554
|
website: p.website,
|
|
7770
|
-
modelsEndpoint: p.modelsEndpoint
|
|
8555
|
+
modelsEndpoint: p.modelsEndpoint,
|
|
8556
|
+
// Static extra headers ride along so `addFromPreset` can seed them onto the
|
|
8557
|
+
// row (the write gateway re-validates via the shared allowlist).
|
|
8558
|
+
extraHeaders: p.extraHeaders
|
|
7771
8559
|
}));
|
|
7772
8560
|
return writeJson4(res, 200, { presets, excluded });
|
|
7773
8561
|
}
|
|
@@ -8249,12 +9037,12 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
8249
9037
|
}
|
|
8250
9038
|
return writeJson4(res, 200, { ok: true, affected: result.affected });
|
|
8251
9039
|
}
|
|
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);
|
|
9040
|
+
if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[3] === "status") {
|
|
9041
|
+
const result = rest[0] === "codex" ? handleCodexOAuthStatus(rest[2], deps) : rest[0] === "kimi" ? handleKimiOAuthStatus(rest[2], deps) : rest[0] === "grok" ? handleGrokOAuthStatus(rest[2], deps) : handleCopilotOAuthStatus(rest[2], deps);
|
|
8254
9042
|
return writeJson4(res, result.status, result.body);
|
|
8255
9043
|
}
|
|
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);
|
|
9044
|
+
if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[2]) {
|
|
9045
|
+
const result = rest[0] === "codex" ? handleCodexOAuthCancel(rest[2], deps) : rest[0] === "kimi" ? handleKimiOAuthCancel(rest[2], deps) : rest[0] === "grok" ? handleGrokOAuthCancel(rest[2], deps) : handleCopilotOAuthCancel(rest[2], deps);
|
|
8258
9046
|
return writeJson4(res, result.status, result.body);
|
|
8259
9047
|
}
|
|
8260
9048
|
if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
|
|
@@ -8315,6 +9103,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
8315
9103
|
const result2 = await handleKimiOAuthStart(deps);
|
|
8316
9104
|
return writeJson4(res, result2.status, result2.body);
|
|
8317
9105
|
}
|
|
9106
|
+
if (providerId === "grok") {
|
|
9107
|
+
const result2 = await handleGrokOAuthStart(deps);
|
|
9108
|
+
return writeJson4(res, result2.status, result2.body);
|
|
9109
|
+
}
|
|
9110
|
+
if (providerId === "copilot") {
|
|
9111
|
+
const body2 = await readJsonBody4(req);
|
|
9112
|
+
const result2 = await handleCopilotOAuthStart(deps, body2["enterpriseUrl"]);
|
|
9113
|
+
return writeJson4(res, result2.status, result2.body);
|
|
9114
|
+
}
|
|
8318
9115
|
const result = handleOAuthStart(providerId, deps);
|
|
8319
9116
|
return writeJson4(res, result.status, result.body);
|
|
8320
9117
|
}
|
|
@@ -8809,12 +9606,12 @@ async function handlePlayground(req, res, method, deps) {
|
|
|
8809
9606
|
const payload = body["body"];
|
|
8810
9607
|
const status = deps.outboundApiServer.getStatus();
|
|
8811
9608
|
if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
|
|
8812
|
-
const path2 = resolvePlaygroundPath(endpoint,
|
|
9609
|
+
const path2 = resolvePlaygroundPath(endpoint, isRecord6(payload) ? payload : {});
|
|
8813
9610
|
if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
|
|
8814
9611
|
const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
|
|
8815
9612
|
await proxyToOutbound(res, status.port, path2, key, upstreamBody);
|
|
8816
9613
|
}
|
|
8817
|
-
function
|
|
9614
|
+
function isRecord6(v) {
|
|
8818
9615
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
8819
9616
|
}
|
|
8820
9617
|
function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
@@ -8843,8 +9640,8 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
|
8843
9640
|
});
|
|
8844
9641
|
}
|
|
8845
9642
|
);
|
|
8846
|
-
upstream.on("error", (
|
|
8847
|
-
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${
|
|
9643
|
+
upstream.on("error", (err8) => {
|
|
9644
|
+
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err8.message}`);
|
|
8848
9645
|
else res.end();
|
|
8849
9646
|
resolve11();
|
|
8850
9647
|
});
|
|
@@ -8950,7 +9747,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
8950
9747
|
}
|
|
8951
9748
|
|
|
8952
9749
|
// src/admin/version.ts
|
|
8953
|
-
var DAEMON_VERSION = true ? "0.
|
|
9750
|
+
var DAEMON_VERSION = true ? "0.4.0" : "0.0.0-dev";
|
|
8954
9751
|
|
|
8955
9752
|
// src/admin/AdminServer.ts
|
|
8956
9753
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -8993,13 +9790,13 @@ var AdminServer = class {
|
|
|
8993
9790
|
const server = import_node_http2.default.createServer((req, res) => {
|
|
8994
9791
|
this.onRequest(req, res);
|
|
8995
9792
|
});
|
|
8996
|
-
const onError = (
|
|
8997
|
-
if (
|
|
9793
|
+
const onError = (err8) => {
|
|
9794
|
+
if (err8.code === "EADDRINUSE" && port !== 0) {
|
|
8998
9795
|
server.removeListener("error", onError);
|
|
8999
9796
|
this.listen(bindAddr, 0).then(resolve11, reject);
|
|
9000
9797
|
return;
|
|
9001
9798
|
}
|
|
9002
|
-
reject(
|
|
9799
|
+
reject(err8);
|
|
9003
9800
|
};
|
|
9004
9801
|
server.on("error", onError);
|
|
9005
9802
|
server.listen(port, bindAddr, () => {
|
|
@@ -9017,8 +9814,8 @@ var AdminServer = class {
|
|
|
9017
9814
|
}
|
|
9018
9815
|
/** Per-request handler: auth gate (when a token is set) → routing. */
|
|
9019
9816
|
onRequest(req, res) {
|
|
9020
|
-
void this.dispatch(req, res).catch((
|
|
9021
|
-
const message =
|
|
9817
|
+
void this.dispatch(req, res).catch((err8) => {
|
|
9818
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
9022
9819
|
this.deps.logger.error("[AdminServer] unhandled error:", message);
|
|
9023
9820
|
if (!res.headersSent) {
|
|
9024
9821
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
@@ -9282,18 +10079,18 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
9282
10079
|
return;
|
|
9283
10080
|
}
|
|
9284
10081
|
signal?.addEventListener("abort", abort, { once: true });
|
|
9285
|
-
server.on("error", (
|
|
10082
|
+
server.on("error", (err8) => {
|
|
9286
10083
|
if (settled) return;
|
|
9287
10084
|
settled = true;
|
|
9288
10085
|
clearTimeout(timer);
|
|
9289
|
-
if (
|
|
10086
|
+
if (err8.code === "EADDRINUSE") {
|
|
9290
10087
|
reject(
|
|
9291
10088
|
new Error(
|
|
9292
10089
|
`login: cannot bind ${LOOPBACK_HOST}:${LOOPBACK_PORT} (address in use) \u2014 another codex login or process is holding the port`
|
|
9293
10090
|
)
|
|
9294
10091
|
);
|
|
9295
10092
|
} else {
|
|
9296
|
-
reject(
|
|
10093
|
+
reject(err8);
|
|
9297
10094
|
}
|
|
9298
10095
|
});
|
|
9299
10096
|
const timer = setTimeout(() => {
|
|
@@ -9369,21 +10166,22 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
|
|
|
9369
10166
|
}
|
|
9370
10167
|
|
|
9371
10168
|
// src/allowance/ProviderKeyQuotaService.ts
|
|
9372
|
-
var
|
|
10169
|
+
var import_core4 = require("@omnicross/core");
|
|
10170
|
+
var import_upstreamFetch10 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
9373
10171
|
|
|
9374
10172
|
// src/allowance/ProviderKeyQuota.ts
|
|
9375
|
-
var
|
|
9376
|
-
var HOUR_MS2 = 60 *
|
|
9377
|
-
var
|
|
9378
|
-
var WEEK_MS = 7 *
|
|
9379
|
-
var MONTH_MS = 30 *
|
|
9380
|
-
function
|
|
10173
|
+
var MINUTE_MS3 = 6e4;
|
|
10174
|
+
var HOUR_MS2 = 60 * MINUTE_MS3;
|
|
10175
|
+
var DAY_MS3 = 24 * HOUR_MS2;
|
|
10176
|
+
var WEEK_MS = 7 * DAY_MS3;
|
|
10177
|
+
var MONTH_MS = 30 * DAY_MS3;
|
|
10178
|
+
function finiteNumber5(value) {
|
|
9381
10179
|
if (value === null || value === void 0 || value === "") return void 0;
|
|
9382
10180
|
const parsed = typeof value === "number" ? value : Number(value);
|
|
9383
10181
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
9384
10182
|
}
|
|
9385
10183
|
function finitePercent4(value) {
|
|
9386
|
-
const parsed =
|
|
10184
|
+
const parsed = finiteNumber5(value);
|
|
9387
10185
|
return parsed !== void 0 && parsed <= 100 ? parsed : null;
|
|
9388
10186
|
}
|
|
9389
10187
|
function isoInstant3(value) {
|
|
@@ -9391,18 +10189,18 @@ function isoInstant3(value) {
|
|
|
9391
10189
|
const time = Date.parse(value);
|
|
9392
10190
|
if (Number.isFinite(time)) return new Date(time).toISOString();
|
|
9393
10191
|
}
|
|
9394
|
-
const numeric =
|
|
10192
|
+
const numeric = finiteNumber5(value);
|
|
9395
10193
|
if (numeric !== void 0 && numeric > 1e9) {
|
|
9396
10194
|
const ms = numeric > 1e12 ? numeric : numeric * 1e3;
|
|
9397
10195
|
return new Date(ms).toISOString();
|
|
9398
10196
|
}
|
|
9399
10197
|
return void 0;
|
|
9400
10198
|
}
|
|
9401
|
-
function
|
|
10199
|
+
function secondsUntil7(instant, now) {
|
|
9402
10200
|
if (!instant) return void 0;
|
|
9403
10201
|
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
9404
10202
|
}
|
|
9405
|
-
function
|
|
10203
|
+
function isRecord7(value) {
|
|
9406
10204
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
9407
10205
|
}
|
|
9408
10206
|
function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
@@ -9425,6 +10223,7 @@ function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
|
9425
10223
|
}
|
|
9426
10224
|
if (host === "api.code.umans.ai") return "umans";
|
|
9427
10225
|
if (host === "api.synthetic.new") return "synthetic";
|
|
10226
|
+
if (host === "api.cline.bot") return "cline-pass";
|
|
9428
10227
|
return null;
|
|
9429
10228
|
}
|
|
9430
10229
|
function providerKeyQuotaUrl(adapter, baseUrl) {
|
|
@@ -9432,6 +10231,7 @@ function providerKeyQuotaUrl(adapter, baseUrl) {
|
|
|
9432
10231
|
if (adapter === "zai") return `${origin}/api/monitor/usage/quota/limit`;
|
|
9433
10232
|
if (adapter === "minimax-token-plan") return `${origin}/v1/token_plan/remains`;
|
|
9434
10233
|
if (adapter === "umans") return `${origin}/v1/usage`;
|
|
10234
|
+
if (adapter === "cline-pass") return `${origin}/api/v1/users/me/plan/usage-limits`;
|
|
9435
10235
|
return `${origin}/v2/quotas`;
|
|
9436
10236
|
}
|
|
9437
10237
|
function providerKeyQuotaAuthHeader(adapter, key) {
|
|
@@ -9443,7 +10243,7 @@ function zaiWindowDurationMs(item) {
|
|
|
9443
10243
|
case 3:
|
|
9444
10244
|
return count * HOUR_MS2;
|
|
9445
10245
|
case 4:
|
|
9446
|
-
return count *
|
|
10246
|
+
return count * DAY_MS3;
|
|
9447
10247
|
case 5:
|
|
9448
10248
|
return count * MONTH_MS;
|
|
9449
10249
|
case 6:
|
|
@@ -9456,8 +10256,8 @@ function zaiWindowIdLabel(durationMs) {
|
|
|
9456
10256
|
if (durationMs === WEEK_MS) return { id: "seven-day", label: "7 days" };
|
|
9457
10257
|
if (durationMs === 5 * HOUR_MS2) return { id: "five-hour", label: "5 hours" };
|
|
9458
10258
|
if (durationMs === MONTH_MS) return { id: "thirty-day", label: "30 days" };
|
|
9459
|
-
if (durationMs !== void 0 && durationMs %
|
|
9460
|
-
const days = durationMs /
|
|
10259
|
+
if (durationMs !== void 0 && durationMs % DAY_MS3 === 0) {
|
|
10260
|
+
const days = durationMs / DAY_MS3;
|
|
9461
10261
|
return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}` };
|
|
9462
10262
|
}
|
|
9463
10263
|
if (durationMs !== void 0 && durationMs % HOUR_MS2 === 0) {
|
|
@@ -9467,23 +10267,23 @@ function zaiWindowIdLabel(durationMs) {
|
|
|
9467
10267
|
return { id: "quota", label: "Quota" };
|
|
9468
10268
|
}
|
|
9469
10269
|
function parseZaiQuotaPayload(payload, now) {
|
|
9470
|
-
if (!
|
|
9471
|
-
const data =
|
|
10270
|
+
if (!isRecord7(payload)) return null;
|
|
10271
|
+
const data = isRecord7(payload["data"]) ? payload["data"] : payload;
|
|
9472
10272
|
if (payload["success"] === false) return null;
|
|
9473
10273
|
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
9474
10274
|
const byWindow = /* @__PURE__ */ new Map();
|
|
9475
10275
|
for (const raw of limits) {
|
|
9476
|
-
if (!
|
|
10276
|
+
if (!isRecord7(raw)) continue;
|
|
9477
10277
|
const item = raw;
|
|
9478
10278
|
if (item.type === void 0) continue;
|
|
9479
10279
|
const details = raw["usageDetails"];
|
|
9480
|
-
if (Array.isArray(details) && details.some((d) =>
|
|
10280
|
+
if (Array.isArray(details) && details.some((d) => isRecord7(d) && d["modelCode"] === "zread")) {
|
|
9481
10281
|
continue;
|
|
9482
10282
|
}
|
|
9483
10283
|
const durationMs = zaiWindowDurationMs(item);
|
|
9484
10284
|
const { id, label } = zaiWindowIdLabel(durationMs);
|
|
9485
|
-
const limit =
|
|
9486
|
-
const used =
|
|
10285
|
+
const limit = finiteNumber5(item.usage);
|
|
10286
|
+
const used = finiteNumber5(item.currentValue);
|
|
9487
10287
|
const fromAbsolute = limit !== void 0 && used !== void 0 && limit > 0 ? Math.min(100, used / limit * 100) : void 0;
|
|
9488
10288
|
const fromPercentage = finitePercent4(item.percentage) ?? void 0;
|
|
9489
10289
|
const usedPercent = fromAbsolute !== void 0 ? Math.round(fromAbsolute * 10) / 10 : fromPercentage;
|
|
@@ -9494,9 +10294,9 @@ function parseZaiQuotaPayload(payload, now) {
|
|
|
9494
10294
|
label,
|
|
9495
10295
|
scope: "all",
|
|
9496
10296
|
usedPercent,
|
|
9497
|
-
...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs /
|
|
10297
|
+
...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs / MINUTE_MS3) } : {},
|
|
9498
10298
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9499
|
-
remainingSeconds:
|
|
10299
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9500
10300
|
state: "fresh"
|
|
9501
10301
|
};
|
|
9502
10302
|
const existing = byWindow.get(id);
|
|
@@ -9510,21 +10310,21 @@ function parseZaiQuotaPayload(payload, now) {
|
|
|
9510
10310
|
var MINIMAX_STATUS_EXHAUSTED = 2;
|
|
9511
10311
|
var MINIMAX_SHARED_BUCKET = "general";
|
|
9512
10312
|
function parseMiniMaxBucket(value) {
|
|
9513
|
-
if (!
|
|
10313
|
+
if (!isRecord7(value)) return null;
|
|
9514
10314
|
const modelName = typeof value["model_name"] === "string" ? value["model_name"].trim() : "";
|
|
9515
10315
|
if (!modelName) return null;
|
|
9516
10316
|
const instant = (v) => {
|
|
9517
|
-
const n =
|
|
10317
|
+
const n = finiteNumber5(v);
|
|
9518
10318
|
return n !== void 0 && n > 1e9 ? n > 1e12 ? n : n * 1e3 : void 0;
|
|
9519
10319
|
};
|
|
9520
10320
|
return {
|
|
9521
10321
|
modelName,
|
|
9522
10322
|
intervalEnd: instant(value["end_time"]),
|
|
9523
|
-
intervalRemainingPercent:
|
|
9524
|
-
intervalStatus:
|
|
10323
|
+
intervalRemainingPercent: finiteNumber5(value["current_interval_remaining_percent"]),
|
|
10324
|
+
intervalStatus: finiteNumber5(value["current_interval_status"]),
|
|
9525
10325
|
weeklyEnd: instant(value["weekly_end_time"]),
|
|
9526
|
-
weeklyRemainingPercent:
|
|
9527
|
-
weeklyStatus:
|
|
10326
|
+
weeklyRemainingPercent: finiteNumber5(value["current_weekly_remaining_percent"]),
|
|
10327
|
+
weeklyStatus: finiteNumber5(value["current_weekly_status"])
|
|
9528
10328
|
};
|
|
9529
10329
|
}
|
|
9530
10330
|
function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, status, now) {
|
|
@@ -9537,14 +10337,14 @@ function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, s
|
|
|
9537
10337
|
usedPercent,
|
|
9538
10338
|
...windowMinutes !== void 0 ? { windowMinutes } : {},
|
|
9539
10339
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9540
|
-
remainingSeconds:
|
|
10340
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9541
10341
|
state: usedPercent !== null ? "fresh" : "unavailable"
|
|
9542
10342
|
};
|
|
9543
10343
|
}
|
|
9544
10344
|
function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
9545
|
-
if (!
|
|
10345
|
+
if (!isRecord7(payload)) return null;
|
|
9546
10346
|
const baseResp = payload["base_resp"];
|
|
9547
|
-
if (!
|
|
10347
|
+
if (!isRecord7(baseResp) || baseResp["status_code"] !== 0) return null;
|
|
9548
10348
|
const buckets = Array.isArray(payload["model_remains"]) ? payload["model_remains"] : [];
|
|
9549
10349
|
let general = null;
|
|
9550
10350
|
for (const raw of buckets) {
|
|
@@ -9568,7 +10368,7 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
|
9568
10368
|
minimaxWindow(
|
|
9569
10369
|
"seven-day",
|
|
9570
10370
|
"7 days",
|
|
9571
|
-
Math.round(WEEK_MS /
|
|
10371
|
+
Math.round(WEEK_MS / MINUTE_MS3),
|
|
9572
10372
|
general.weeklyEnd,
|
|
9573
10373
|
general.weeklyRemainingPercent,
|
|
9574
10374
|
general.weeklyStatus,
|
|
@@ -9577,15 +10377,15 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
|
9577
10377
|
];
|
|
9578
10378
|
}
|
|
9579
10379
|
function parseUmansUsagePayload(payload, now) {
|
|
9580
|
-
if (!
|
|
9581
|
-
const limits =
|
|
9582
|
-
const requests = limits &&
|
|
9583
|
-
const usage =
|
|
9584
|
-
const window =
|
|
9585
|
-
const hardCap =
|
|
9586
|
-
const softLimit =
|
|
9587
|
-
const requestsInWindow =
|
|
9588
|
-
const weightedInWindow =
|
|
10380
|
+
if (!isRecord7(payload)) return null;
|
|
10381
|
+
const limits = isRecord7(payload["limits"]) ? payload["limits"] : void 0;
|
|
10382
|
+
const requests = limits && isRecord7(limits["requests"]) ? limits["requests"] : void 0;
|
|
10383
|
+
const usage = isRecord7(payload["usage"]) ? payload["usage"] : void 0;
|
|
10384
|
+
const window = isRecord7(payload["window"]) ? payload["window"] : void 0;
|
|
10385
|
+
const hardCap = finiteNumber5(requests?.["hard_cap"]);
|
|
10386
|
+
const softLimit = finiteNumber5(requests?.["limit"]);
|
|
10387
|
+
const requestsInWindow = finiteNumber5(usage?.["requests_in_window"]);
|
|
10388
|
+
const weightedInWindow = finiteNumber5(usage?.["weighted_in_window"]);
|
|
9589
10389
|
const resetsAt = isoInstant3(window?.["resets_at"]);
|
|
9590
10390
|
let usedPercent = null;
|
|
9591
10391
|
if (hardCap !== void 0 && hardCap > 0 && requestsInWindow !== void 0) {
|
|
@@ -9602,19 +10402,19 @@ function parseUmansUsagePayload(payload, now) {
|
|
|
9602
10402
|
usedPercent,
|
|
9603
10403
|
windowMinutes: 5 * 60,
|
|
9604
10404
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9605
|
-
remainingSeconds:
|
|
10405
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9606
10406
|
state: "fresh"
|
|
9607
10407
|
}
|
|
9608
10408
|
];
|
|
9609
10409
|
}
|
|
9610
10410
|
function parseSyntheticQuotasPayload(payload, now) {
|
|
9611
|
-
if (!
|
|
9612
|
-
const fiveHour =
|
|
9613
|
-
const weekly =
|
|
10411
|
+
if (!isRecord7(payload)) return null;
|
|
10412
|
+
const fiveHour = isRecord7(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
|
|
10413
|
+
const weekly = isRecord7(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
|
|
9614
10414
|
const windows = [];
|
|
9615
10415
|
if (fiveHour) {
|
|
9616
|
-
const max =
|
|
9617
|
-
const remaining =
|
|
10416
|
+
const max = finiteNumber5(fiveHour["max"]);
|
|
10417
|
+
const remaining = finiteNumber5(fiveHour["remaining"]);
|
|
9618
10418
|
const usedPercent = max !== void 0 && max > 0 && remaining !== void 0 ? Math.round(Math.min(100, (max - remaining) / max * 100) * 10) / 10 : null;
|
|
9619
10419
|
const resetsAt = isoInstant3(fiveHour["nextTickAt"]);
|
|
9620
10420
|
windows.push({
|
|
@@ -9624,12 +10424,12 @@ function parseSyntheticQuotasPayload(payload, now) {
|
|
|
9624
10424
|
usedPercent,
|
|
9625
10425
|
windowMinutes: 5 * 60,
|
|
9626
10426
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9627
|
-
remainingSeconds:
|
|
10427
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9628
10428
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
9629
10429
|
});
|
|
9630
10430
|
}
|
|
9631
10431
|
if (weekly) {
|
|
9632
|
-
const percentRemaining =
|
|
10432
|
+
const percentRemaining = finiteNumber5(weekly["percentRemaining"]);
|
|
9633
10433
|
const usedPercent = percentRemaining !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - percentRemaining)) * 10) / 10 : null;
|
|
9634
10434
|
const resetsAt = isoInstant3(weekly["nextRegenAt"]);
|
|
9635
10435
|
windows.push({
|
|
@@ -9639,12 +10439,42 @@ function parseSyntheticQuotasPayload(payload, now) {
|
|
|
9639
10439
|
usedPercent,
|
|
9640
10440
|
windowMinutes: 7 * 24 * 60,
|
|
9641
10441
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9642
|
-
remainingSeconds:
|
|
10442
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9643
10443
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
9644
10444
|
});
|
|
9645
10445
|
}
|
|
9646
10446
|
return windows.length > 0 ? windows : null;
|
|
9647
10447
|
}
|
|
10448
|
+
var CLINE_WINDOW_CONFIG = {
|
|
10449
|
+
five_hour: { id: "five-hour", label: "5 hours", minutes: 5 * 60 },
|
|
10450
|
+
weekly: { id: "seven-day", label: "7 days", minutes: 7 * 24 * 60 },
|
|
10451
|
+
monthly: { id: "thirty-day", label: "30 days", minutes: 30 * 24 * 60 }
|
|
10452
|
+
};
|
|
10453
|
+
function parseClinePassUsageLimitsPayload(payload, now) {
|
|
10454
|
+
if (!isRecord7(payload)) return null;
|
|
10455
|
+
const data = isRecord7(payload["data"]) ? payload["data"] : payload;
|
|
10456
|
+
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
10457
|
+
const windows = [];
|
|
10458
|
+
for (const raw of limits) {
|
|
10459
|
+
if (!isRecord7(raw)) continue;
|
|
10460
|
+
const config = CLINE_WINDOW_CONFIG[typeof raw["type"] === "string" ? raw["type"] : ""];
|
|
10461
|
+
if (!config) continue;
|
|
10462
|
+
const usedPercent = finitePercent4(raw["percentUsed"]);
|
|
10463
|
+
if (usedPercent === null) continue;
|
|
10464
|
+
const resetsAt = isoInstant3(raw["resetsAt"]);
|
|
10465
|
+
windows.push({
|
|
10466
|
+
id: config.id,
|
|
10467
|
+
label: config.label,
|
|
10468
|
+
scope: "all",
|
|
10469
|
+
usedPercent,
|
|
10470
|
+
windowMinutes: config.minutes,
|
|
10471
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
10472
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
10473
|
+
state: "fresh"
|
|
10474
|
+
});
|
|
10475
|
+
}
|
|
10476
|
+
return windows.length > 0 ? windows : null;
|
|
10477
|
+
}
|
|
9648
10478
|
|
|
9649
10479
|
// src/allowance/ProviderKeyQuotaService.ts
|
|
9650
10480
|
function parseQuotaPayload(adapter, payload, now) {
|
|
@@ -9657,6 +10487,8 @@ function parseQuotaPayload(adapter, payload, now) {
|
|
|
9657
10487
|
return parseUmansUsagePayload(payload, now);
|
|
9658
10488
|
case "synthetic":
|
|
9659
10489
|
return parseSyntheticQuotasPayload(payload, now);
|
|
10490
|
+
case "cline-pass":
|
|
10491
|
+
return parseClinePassUsageLimitsPayload(payload, now);
|
|
9660
10492
|
}
|
|
9661
10493
|
}
|
|
9662
10494
|
var PROVIDER_KEY_QUOTA_CACHE_MS = 5 * 6e4;
|
|
@@ -9676,7 +10508,7 @@ function rowKeyEntries(row) {
|
|
|
9676
10508
|
return [];
|
|
9677
10509
|
}
|
|
9678
10510
|
var ProviderKeyQuotaService = class {
|
|
9679
|
-
constructor(box, fetchImpl = (url, init) => (0,
|
|
10511
|
+
constructor(box, fetchImpl = (url, init) => (0, import_upstreamFetch10.fetchUpstream)(url, init, { redactBodies: true }), now = Date.now) {
|
|
9680
10512
|
this.box = box;
|
|
9681
10513
|
this.fetchImpl = fetchImpl;
|
|
9682
10514
|
this.now = now;
|
|
@@ -9738,7 +10570,10 @@ var ProviderKeyQuotaService = class {
|
|
|
9738
10570
|
headers: {
|
|
9739
10571
|
Authorization: providerKeyQuotaAuthHeader(adapter, key),
|
|
9740
10572
|
Accept: "application/json",
|
|
9741
|
-
"Content-Type": "application/json"
|
|
10573
|
+
"Content-Type": "application/json",
|
|
10574
|
+
// The row's static identity headers ride along — the Cline usage
|
|
10575
|
+
// endpoint sits behind the SAME client-identity 403 gate as inference.
|
|
10576
|
+
...(0, import_core4.mergeExtraHeaders)({}, row.extraHeaders)
|
|
9742
10577
|
},
|
|
9743
10578
|
signal: AbortSignal.timeout(15e3)
|
|
9744
10579
|
});
|
|
@@ -9776,7 +10611,7 @@ var ProviderKeyQuotaService = class {
|
|
|
9776
10611
|
// src/image-generation/ImageDoctorService.ts
|
|
9777
10612
|
var import_image_generation = require("@omnicross/core/image-generation");
|
|
9778
10613
|
var import_outbound_api7 = require("@omnicross/core/outbound-api");
|
|
9779
|
-
var
|
|
10614
|
+
var import_subscriptions9 = require("@omnicross/subscriptions");
|
|
9780
10615
|
|
|
9781
10616
|
// src/image-generation/FileCodexImageCapabilityEvidenceSource.ts
|
|
9782
10617
|
var import_node_crypto13 = require("crypto");
|
|
@@ -10214,7 +11049,7 @@ function createImageDoctorService(options) {
|
|
|
10214
11049
|
paths,
|
|
10215
11050
|
ttlMs: config.evidenceTtlMs
|
|
10216
11051
|
}));
|
|
10217
|
-
const createLiveVerifier = options.createLiveVerifier ?? ((strategy, config) => (0,
|
|
11052
|
+
const createLiveVerifier = options.createLiveVerifier ?? ((strategy, config) => (0, import_subscriptions9.createCodexImageLiveVerifier)({
|
|
10218
11053
|
authStrategy: strategy,
|
|
10219
11054
|
generationTimeoutMs: config.queue.generationTimeoutMs
|
|
10220
11055
|
}));
|
|
@@ -10576,7 +11411,7 @@ var ImageCleanupService = class {
|
|
|
10576
11411
|
var import_node_crypto16 = require("crypto");
|
|
10577
11412
|
var import_image_generation5 = require("@omnicross/core/image-generation");
|
|
10578
11413
|
var import_outbound_api8 = require("@omnicross/core/outbound-api");
|
|
10579
|
-
var
|
|
11414
|
+
var import_subscriptions10 = require("@omnicross/subscriptions");
|
|
10580
11415
|
|
|
10581
11416
|
// src/image-generation/ImageApiRuntimeResolver.ts
|
|
10582
11417
|
var import_node_crypto14 = require("crypto");
|
|
@@ -11107,7 +11942,7 @@ function createImageRuntimeGeneration(options) {
|
|
|
11107
11942
|
now: options.now ?? Date.now,
|
|
11108
11943
|
referenceStore: options.storage.referenceStore,
|
|
11109
11944
|
stateStore: options.storage.stateStore
|
|
11110
|
-
}) : (0,
|
|
11945
|
+
}) : (0, import_subscriptions10.createCodexSubscriptionImageProvider)({
|
|
11111
11946
|
authStrategy,
|
|
11112
11947
|
evidenceSource: generationEvidenceSource,
|
|
11113
11948
|
executionScheduler: scheduler,
|
|
@@ -14251,7 +15086,7 @@ var ImageRuntimeManager = class {
|
|
|
14251
15086
|
};
|
|
14252
15087
|
|
|
14253
15088
|
// src/ports/ConfigFileProviderConfigSource.ts
|
|
14254
|
-
var
|
|
15089
|
+
var import_core5 = require("@omnicross/core");
|
|
14255
15090
|
var EMPTY_CHAIN = {
|
|
14256
15091
|
providerTransformers: [],
|
|
14257
15092
|
modelTransformers: []
|
|
@@ -14276,8 +15111,8 @@ var ConfigFileProviderConfigSource = class {
|
|
|
14276
15111
|
reloadHook;
|
|
14277
15112
|
constructor(config) {
|
|
14278
15113
|
for (const p of config.providers) this.providers.set(p.id, p);
|
|
14279
|
-
this.transformerService = new
|
|
14280
|
-
void (0,
|
|
15114
|
+
this.transformerService = new import_core5.TransformerService();
|
|
15115
|
+
void (0, import_core5.registerBuiltinTransformers)(this.transformerService);
|
|
14281
15116
|
}
|
|
14282
15117
|
// ── Reload hook (key-pool design D4) ───────────────────────────────────────
|
|
14283
15118
|
/**
|
|
@@ -14298,7 +15133,7 @@ var ConfigFileProviderConfigSource = class {
|
|
|
14298
15133
|
}
|
|
14299
15134
|
/** Await the built-in transformer registration (tests await this before dispatch). */
|
|
14300
15135
|
async ready() {
|
|
14301
|
-
await (0,
|
|
15136
|
+
await (0, import_core5.registerBuiltinTransformers)(this.transformerService);
|
|
14302
15137
|
}
|
|
14303
15138
|
// ── Hot-reload seam (admin dashboard, RT3 design D6) ───────────────────────
|
|
14304
15139
|
/**
|
|
@@ -14409,6 +15244,10 @@ function toLLMProvider(row) {
|
|
|
14409
15244
|
// `parseProviderInput`), so customizations are preserved (the row value wins).
|
|
14410
15245
|
apiModes: row.apiModes,
|
|
14411
15246
|
selectedApiModeId: row.selectedApiModeId,
|
|
15247
|
+
// Static extra request headers ride along verbatim (load-guarded — no
|
|
15248
|
+
// auth/content names); core's `getProviderHeaders` merges them into every
|
|
15249
|
+
// BYO request, and the same-format relay path inherits that funnel.
|
|
15250
|
+
extraHeaders: row.extraHeaders,
|
|
14412
15251
|
// Official-Anthropic signature handling only matters for the Anthropic
|
|
14413
15252
|
// ingress (deferred → 502); leave it off for the BYO transform path.
|
|
14414
15253
|
isOfficial: false
|
|
@@ -15774,7 +16613,7 @@ function bucketLabel(bucketStartTs, bucket) {
|
|
|
15774
16613
|
|
|
15775
16614
|
// src/ports/JsonOutboundKeyDb.ts
|
|
15776
16615
|
var import_node_fs23 = require("fs");
|
|
15777
|
-
var
|
|
16616
|
+
var import_core6 = require("@omnicross/core");
|
|
15778
16617
|
|
|
15779
16618
|
// src/ports/atomicFile.ts
|
|
15780
16619
|
var import_node_crypto22 = require("crypto");
|
|
@@ -15896,7 +16735,7 @@ var JsonOutboundKeyDb = class {
|
|
|
15896
16735
|
});
|
|
15897
16736
|
}
|
|
15898
16737
|
async outboundApiKeysSetPermissions(id, permissions) {
|
|
15899
|
-
const exact = (0,
|
|
16738
|
+
const exact = (0, import_core6.validateOutboundPermissions)(permissions);
|
|
15900
16739
|
return this.mutateRow(id, (row) => {
|
|
15901
16740
|
if (row.revokedAt !== null) return false;
|
|
15902
16741
|
row.allowedEndpoints = [...exact];
|
|
@@ -16333,9 +17172,9 @@ var import_node_fs28 = require("fs");
|
|
|
16333
17172
|
var import_node_path27 = require("path");
|
|
16334
17173
|
var import_SubscriptionAccountHealth2 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
16335
17174
|
var import_AccountAllowanceScheduling3 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
16336
|
-
var
|
|
17175
|
+
var import_upstreamFetch11 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
16337
17176
|
var import_SubscriptionIdentityStore2 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
16338
|
-
var
|
|
17177
|
+
var import_subscriptions11 = require("@omnicross/subscriptions");
|
|
16339
17178
|
|
|
16340
17179
|
// src/ports/account-sync.ts
|
|
16341
17180
|
function viewOf(tokens) {
|
|
@@ -16483,7 +17322,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16483
17322
|
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
16484
17323
|
*/
|
|
16485
17324
|
buildRefreshFetch(providerId, accountId) {
|
|
16486
|
-
return this.fetchImpl ?? ((url, init) => (0,
|
|
17325
|
+
return this.fetchImpl ?? ((url, init) => (0, import_upstreamFetch11.fetchUpstream)(url, init, { providerId, accountId, redactBodies: true }));
|
|
16487
17326
|
}
|
|
16488
17327
|
/**
|
|
16489
17328
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -16524,7 +17363,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16524
17363
|
* other hot reads. Never returns token material.
|
|
16525
17364
|
*/
|
|
16526
17365
|
getAccountProxy(providerId, accountId) {
|
|
16527
|
-
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi") {
|
|
17366
|
+
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi" && providerId !== "grok" && providerId !== "copilot") {
|
|
16528
17367
|
return void 0;
|
|
16529
17368
|
}
|
|
16530
17369
|
return getAccountProxy(this.readConfig(), providerId, accountId);
|
|
@@ -16543,7 +17382,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16543
17382
|
const fingerprintOn = identityStore.isEnabled();
|
|
16544
17383
|
const now = Date.now();
|
|
16545
17384
|
const out = {};
|
|
16546
|
-
for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi"]) {
|
|
17385
|
+
for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi", "grok", "copilot"]) {
|
|
16547
17386
|
const sanitized = sanitizeAccounts(config, provider);
|
|
16548
17387
|
if (sanitized.length === 0) continue;
|
|
16549
17388
|
for (const account of sanitized) {
|
|
@@ -16609,7 +17448,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16609
17448
|
this.materializeMigration(config);
|
|
16610
17449
|
const refreshFetch = this.buildRefreshFetch("claude", capturedId);
|
|
16611
17450
|
try {
|
|
16612
|
-
const result = await
|
|
17451
|
+
const result = await import_subscriptions11.claudeOAuth.refreshAccessToken(claude.refreshToken, refreshFetch);
|
|
16613
17452
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
16614
17453
|
const next = {
|
|
16615
17454
|
...claude,
|
|
@@ -16644,7 +17483,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16644
17483
|
this.materializeMigration(config);
|
|
16645
17484
|
const refreshFetch = this.buildRefreshFetch("codex", capturedId);
|
|
16646
17485
|
try {
|
|
16647
|
-
const result = await
|
|
17486
|
+
const result = await import_subscriptions11.codexOAuth.refreshAccessToken(codex.refreshToken, refreshFetch);
|
|
16648
17487
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
16649
17488
|
const next = {
|
|
16650
17489
|
...codex,
|
|
@@ -16682,7 +17521,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16682
17521
|
this.materializeMigration(config);
|
|
16683
17522
|
const refreshFetch = this.buildRefreshFetch("gemini", capturedId);
|
|
16684
17523
|
try {
|
|
16685
|
-
const result = await
|
|
17524
|
+
const result = await import_subscriptions11.geminiOAuth.refreshAccessToken(gemini.refreshToken, refreshFetch);
|
|
16686
17525
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
16687
17526
|
const next = {
|
|
16688
17527
|
...gemini,
|
|
@@ -16718,10 +17557,10 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16718
17557
|
this.materializeMigration(config);
|
|
16719
17558
|
const refreshFetch = this.buildRefreshFetch("kimi", capturedId);
|
|
16720
17559
|
try {
|
|
16721
|
-
const result = await
|
|
17560
|
+
const result = await import_subscriptions11.kimiOAuth.refreshAccessToken(
|
|
16722
17561
|
kimi.refreshToken,
|
|
16723
17562
|
refreshFetch,
|
|
16724
|
-
|
|
17563
|
+
import_subscriptions11.kimiOAuth.kimiFingerprintHeaders(kimi.deviceId)
|
|
16725
17564
|
);
|
|
16726
17565
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
16727
17566
|
const next = {
|
|
@@ -16742,6 +17581,66 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16742
17581
|
}
|
|
16743
17582
|
});
|
|
16744
17583
|
}
|
|
17584
|
+
/**
|
|
17585
|
+
* Refresh the Grok (xAI SuperGrok) OAuth access token. The token endpoint is
|
|
17586
|
+
* resolved through OIDC discovery on every refresh (process-cached 1h by the
|
|
17587
|
+
* flow module) so a rotated endpoint document is picked up without a daemon
|
|
17588
|
+
* restart. HONEST `false` when no refresh_token.
|
|
17589
|
+
*/
|
|
17590
|
+
async refreshGrokToken() {
|
|
17591
|
+
return this.coalesce("grok:active", async () => {
|
|
17592
|
+
const config = this.readConfig();
|
|
17593
|
+
const active = getActiveAccount(config, "grok");
|
|
17594
|
+
const grok = active?.tokens;
|
|
17595
|
+
if (!active || !grok?.refreshToken) return false;
|
|
17596
|
+
const capturedId = active.id;
|
|
17597
|
+
this.materializeMigration(config);
|
|
17598
|
+
const refreshFetch = this.buildRefreshFetch("grok", capturedId);
|
|
17599
|
+
try {
|
|
17600
|
+
const tokenEndpoint = await import_subscriptions11.grokOAuth.resolveGrokTokenEndpoint(refreshFetch);
|
|
17601
|
+
const result = await import_subscriptions11.grokOAuth.refreshGrokAccessToken(grok.refreshToken, tokenEndpoint, refreshFetch);
|
|
17602
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
17603
|
+
const next = {
|
|
17604
|
+
...grok,
|
|
17605
|
+
accessToken: result.accessToken,
|
|
17606
|
+
refreshToken: result.refreshToken,
|
|
17607
|
+
expiresAt,
|
|
17608
|
+
status: "authorized",
|
|
17609
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17610
|
+
errorMessage: void 0,
|
|
17611
|
+
syncWarning: void 0
|
|
17612
|
+
};
|
|
17613
|
+
this.writeBackById("grok", capturedId, next);
|
|
17614
|
+
return true;
|
|
17615
|
+
} catch (error) {
|
|
17616
|
+
this.markExpiredById("grok", capturedId, grok, error);
|
|
17617
|
+
return false;
|
|
17618
|
+
}
|
|
17619
|
+
});
|
|
17620
|
+
}
|
|
17621
|
+
/**
|
|
17622
|
+
* "Refresh" a GitHub Copilot token — there is nothing to refresh (ghu_
|
|
17623
|
+
* tokens are long-lived with no exchange endpoint). A call here means the
|
|
17624
|
+
* strategy saw a 401 (the token was revoked); mark the account `expired`
|
|
17625
|
+
* with a re-authenticate message and return `false` (the proxy then declines
|
|
17626
|
+
* the retry instead of looping on a dead token).
|
|
17627
|
+
*/
|
|
17628
|
+
async refreshCopilotToken() {
|
|
17629
|
+
return this.coalesce("copilot:active", async () => {
|
|
17630
|
+
const config = this.readConfig();
|
|
17631
|
+
const active = getActiveAccount(config, "copilot");
|
|
17632
|
+
const copilot = active?.tokens;
|
|
17633
|
+
if (!active || !copilot?.accessToken) return false;
|
|
17634
|
+
this.materializeMigration(config);
|
|
17635
|
+
this.markExpiredById(
|
|
17636
|
+
"copilot",
|
|
17637
|
+
active.id,
|
|
17638
|
+
copilot,
|
|
17639
|
+
new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account")
|
|
17640
|
+
);
|
|
17641
|
+
return false;
|
|
17642
|
+
});
|
|
17643
|
+
}
|
|
16745
17644
|
/**
|
|
16746
17645
|
* Refresh a SPECIFIC managed account by id (background scheduler sweep and
|
|
16747
17646
|
* account-pool resolution). It uses only that account's stored refresh
|
|
@@ -16794,7 +17693,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16794
17693
|
}
|
|
16795
17694
|
const oauth = account.tokens;
|
|
16796
17695
|
if (!oauth.accessToken) return null;
|
|
16797
|
-
if (providerId === "codex" || providerId === "gemini" || providerId === "kimi") {
|
|
17696
|
+
if (providerId === "codex" || providerId === "gemini" || providerId === "kimi" || providerId === "grok" || providerId === "copilot") {
|
|
16798
17697
|
const expiresAtMs = oauth.expiresAt ? Date.parse(oauth.expiresAt) : 0;
|
|
16799
17698
|
const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - ACCOUNT_REFRESH_LEAD_MS;
|
|
16800
17699
|
if (expiringSoon && oauth.refreshToken) {
|
|
@@ -16887,10 +17786,10 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16887
17786
|
if (provider === "kimi") {
|
|
16888
17787
|
const account = accountId ? getAccountById(this.readConfig(), "kimi", accountId) : void 0;
|
|
16889
17788
|
const deviceId = account?.tokens?.deviceId;
|
|
16890
|
-
const r2 = await
|
|
17789
|
+
const r2 = await import_subscriptions11.kimiOAuth.refreshAccessToken(
|
|
16891
17790
|
refreshToken,
|
|
16892
17791
|
refreshFetch,
|
|
16893
|
-
|
|
17792
|
+
import_subscriptions11.kimiOAuth.kimiFingerprintHeaders(deviceId)
|
|
16894
17793
|
);
|
|
16895
17794
|
return {
|
|
16896
17795
|
accessToken: r2.accessToken,
|
|
@@ -16898,7 +17797,19 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16898
17797
|
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
16899
17798
|
};
|
|
16900
17799
|
}
|
|
16901
|
-
|
|
17800
|
+
if (provider === "grok") {
|
|
17801
|
+
const tokenEndpoint = await import_subscriptions11.grokOAuth.resolveGrokTokenEndpoint(refreshFetch);
|
|
17802
|
+
const r2 = await import_subscriptions11.grokOAuth.refreshGrokAccessToken(refreshToken, tokenEndpoint, refreshFetch);
|
|
17803
|
+
return {
|
|
17804
|
+
accessToken: r2.accessToken,
|
|
17805
|
+
refreshToken: r2.refreshToken,
|
|
17806
|
+
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
17807
|
+
};
|
|
17808
|
+
}
|
|
17809
|
+
if (provider === "copilot") {
|
|
17810
|
+
throw new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account");
|
|
17811
|
+
}
|
|
17812
|
+
const flow = provider === "claude" ? import_subscriptions11.claudeOAuth : provider === "codex" ? import_subscriptions11.codexOAuth : import_subscriptions11.geminiOAuth;
|
|
16902
17813
|
const r = await flow.refreshAccessToken(refreshToken, refreshFetch);
|
|
16903
17814
|
return {
|
|
16904
17815
|
accessToken: r.accessToken,
|
|
@@ -17141,7 +18052,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
17141
18052
|
};
|
|
17142
18053
|
|
|
17143
18054
|
// src/AccountHealthProbeScheduler.ts
|
|
17144
|
-
var
|
|
18055
|
+
var import_upstreamFetch12 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
17145
18056
|
|
|
17146
18057
|
// src/probe/CodexGenerationProbe.ts
|
|
17147
18058
|
var import_codexCliHeaders = require("@omnicross/core/provider-proxy/identity/codexCliHeaders");
|
|
@@ -17284,7 +18195,16 @@ var PROVIDER_PROBE_PLANS = {
|
|
|
17284
18195
|
// Kimi's `GET /coding/v1/usages` is a verified FREE authed GET (the allowance
|
|
17285
18196
|
// collector uses it), but the probe path also needs the fingerprint headers —
|
|
17286
18197
|
// keep the probe local until the collector covers the health surface.
|
|
17287
|
-
kimi: { kind: "local" }
|
|
18198
|
+
kimi: { kind: "local" },
|
|
18199
|
+
// Grok's billing proxy is a verified FREE authed GET (the allowance collector
|
|
18200
|
+
// uses it) but it REJECTS non-OAuth credentials and sits on a separate host
|
|
18201
|
+
// with its own product-gate header — keep the probe local, the collector
|
|
18202
|
+
// owns the health surface.
|
|
18203
|
+
grok: { kind: "local" },
|
|
18204
|
+
// The Copilot quota endpoint (copilot_internal/user) is a verified FREE
|
|
18205
|
+
// authed GET but lives on api.github.com with its own auth dialect and a
|
|
18206
|
+
// monthly-only window — the allowance collector owns the health surface.
|
|
18207
|
+
copilot: { kind: "local" }
|
|
17288
18208
|
};
|
|
17289
18209
|
function probePlanFor(providerId) {
|
|
17290
18210
|
return PROVIDER_PROBE_PLANS[providerId] ?? { kind: "local" };
|
|
@@ -17306,7 +18226,7 @@ var AccountHealthProbeScheduler = class {
|
|
|
17306
18226
|
this.logger = logger;
|
|
17307
18227
|
this.config = config;
|
|
17308
18228
|
this.now = opts.now ?? Date.now;
|
|
17309
|
-
this.fetchImpl = opts.fetchImpl ??
|
|
18229
|
+
this.fetchImpl = opts.fetchImpl ?? import_upstreamFetch12.fetchUpstream;
|
|
17310
18230
|
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
17311
18231
|
this.planFor = opts.planFor ?? probePlanFor;
|
|
17312
18232
|
}
|
|
@@ -17856,7 +18776,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
17856
18776
|
}
|
|
17857
18777
|
|
|
17858
18778
|
// src/audit/AuditPruneSweeper.ts
|
|
17859
|
-
var
|
|
18779
|
+
var DAY_MS4 = 24 * 60 * 6e4;
|
|
17860
18780
|
var SWEEP_INTERVAL_MS2 = 60 * 6e4;
|
|
17861
18781
|
var ARCHIVE_BATCH = 64;
|
|
17862
18782
|
var AuditPruneSweeper = class {
|
|
@@ -17920,7 +18840,7 @@ var AuditPruneSweeper = class {
|
|
|
17920
18840
|
this.sweeping = true;
|
|
17921
18841
|
try {
|
|
17922
18842
|
if (!(0, import_node_fs30.existsSync)(this.auditDir)) return 0;
|
|
17923
|
-
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) *
|
|
18843
|
+
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS4;
|
|
17924
18844
|
let removed = 0;
|
|
17925
18845
|
for (const name of (0, import_node_fs30.readdirSync)(this.auditDir)) {
|
|
17926
18846
|
const dateMs = auditFileDateMs(name);
|
|
@@ -18177,7 +19097,7 @@ async function closeAll(writers) {
|
|
|
18177
19097
|
// src/usage/UsagePruneSweeper.ts
|
|
18178
19098
|
var import_promises8 = require("fs/promises");
|
|
18179
19099
|
var import_node_path31 = require("path");
|
|
18180
|
-
var
|
|
19100
|
+
var DAY_MS5 = 24 * 60 * 6e4;
|
|
18181
19101
|
var SWEEP_INTERVAL_MS3 = 60 * 6e4;
|
|
18182
19102
|
var DEFAULT_USAGE_RETENTION_DAYS = 90;
|
|
18183
19103
|
var UsagePruneSweeper = class {
|
|
@@ -18234,7 +19154,7 @@ var UsagePruneSweeper = class {
|
|
|
18234
19154
|
this.sweeping = true;
|
|
18235
19155
|
try {
|
|
18236
19156
|
const retentionDays = this.config.retentionDays ?? DEFAULT_USAGE_RETENTION_DAYS;
|
|
18237
|
-
const cutoff = this.todayMidnight() - (retentionDays - 1) *
|
|
19157
|
+
const cutoff = this.todayMidnight() - (retentionDays - 1) * DAY_MS5;
|
|
18238
19158
|
let removed = 0;
|
|
18239
19159
|
for (const entry of await listUsageDays(this.usageDir)) {
|
|
18240
19160
|
if (!entry.hasShard) continue;
|
|
@@ -18462,7 +19382,7 @@ var AuditWriter = class {
|
|
|
18462
19382
|
var import_node_fs34 = require("fs");
|
|
18463
19383
|
var import_node_crypto24 = require("crypto");
|
|
18464
19384
|
var import_node_path34 = require("path");
|
|
18465
|
-
var
|
|
19385
|
+
var import_upstreamFetch13 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
18466
19386
|
|
|
18467
19387
|
// src/billing/billingFiles.ts
|
|
18468
19388
|
var BILLING_FILE_RE = /^billing-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
|
|
@@ -18485,7 +19405,7 @@ var BillingPublisher = class {
|
|
|
18485
19405
|
constructor(billingDir, logger, opts = {}) {
|
|
18486
19406
|
this.billingDir = billingDir;
|
|
18487
19407
|
this.logger = logger;
|
|
18488
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
19408
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch13.fetchUpstream)(url, init));
|
|
18489
19409
|
this.defer = opts.defer ?? ((fn) => setTimeout(fn, 0));
|
|
18490
19410
|
this.timeoutMs = opts.timeoutMs ?? BILLING_POST_TIMEOUT_MS;
|
|
18491
19411
|
this.now = opts.now ?? Date.now;
|
|
@@ -18735,7 +19655,7 @@ var BillingRetrySweeper = class {
|
|
|
18735
19655
|
// src/TokenRefreshScheduler.ts
|
|
18736
19656
|
var REFRESH_LEAD_MS2 = 5 * 6e4;
|
|
18737
19657
|
var SWEEP_INTERVAL_MS5 = 6e4;
|
|
18738
|
-
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
|
|
19658
|
+
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
|
|
18739
19659
|
var TokenRefreshScheduler = class {
|
|
18740
19660
|
constructor(store, logger, intervalMs = SWEEP_INTERVAL_MS5, leadMs = REFRESH_LEAD_MS2) {
|
|
18741
19661
|
this.store = store;
|
|
@@ -18820,6 +19740,12 @@ var TokenRefreshScheduler = class {
|
|
|
18820
19740
|
return this.store.refreshGeminiToken();
|
|
18821
19741
|
case "kimi":
|
|
18822
19742
|
return this.store.refreshKimiToken();
|
|
19743
|
+
case "grok":
|
|
19744
|
+
return this.store.refreshGrokToken();
|
|
19745
|
+
// ghu_ tokens never near-expire (far-future expiresAt), so the sweep
|
|
19746
|
+
// never reaches this — the branch exists for union totality.
|
|
19747
|
+
case "copilot":
|
|
19748
|
+
return this.store.refreshCopilotToken();
|
|
18823
19749
|
}
|
|
18824
19750
|
}
|
|
18825
19751
|
};
|
|
@@ -18896,7 +19822,7 @@ function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
|
18896
19822
|
|
|
18897
19823
|
// src/webhook/WebhookDispatcher.ts
|
|
18898
19824
|
var import_node_crypto25 = require("crypto");
|
|
18899
|
-
var
|
|
19825
|
+
var import_upstreamFetch14 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
18900
19826
|
var WEBHOOK_MAX_ATTEMPTS = 3;
|
|
18901
19827
|
var WEBHOOK_QUEUE_MAX = 1e3;
|
|
18902
19828
|
var WEBHOOK_SEND_TIMEOUT_MS = 1e4;
|
|
@@ -18916,7 +19842,7 @@ var WebhookDispatcher = class {
|
|
|
18916
19842
|
sleep;
|
|
18917
19843
|
now;
|
|
18918
19844
|
constructor(opts = {}) {
|
|
18919
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
19845
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch14.fetchUpstream)(url, init));
|
|
18920
19846
|
this.logger = opts.logger;
|
|
18921
19847
|
this.maxAttempts = opts.maxAttempts ?? WEBHOOK_MAX_ATTEMPTS;
|
|
18922
19848
|
this.queueMax = opts.queueMax ?? WEBHOOK_QUEUE_MAX;
|
|
@@ -19002,8 +19928,8 @@ var WebhookDispatcher = class {
|
|
|
19002
19928
|
signal: AbortSignal.timeout(this.timeoutMs)
|
|
19003
19929
|
});
|
|
19004
19930
|
return res.ok ? { ok: true, status: res.status } : { ok: false, status: res.status };
|
|
19005
|
-
} catch (
|
|
19006
|
-
return { ok: false, error:
|
|
19931
|
+
} catch (err8) {
|
|
19932
|
+
return { ok: false, error: err8 instanceof Error ? err8.message : String(err8) };
|
|
19007
19933
|
}
|
|
19008
19934
|
}
|
|
19009
19935
|
/**
|
|
@@ -19066,7 +19992,7 @@ function feishuText(event) {
|
|
|
19066
19992
|
// src/bootstrap.ts
|
|
19067
19993
|
var activeImageRuntimeBootstrapSession;
|
|
19068
19994
|
function createImageRuntimeBootstrapSession(initialGeneration) {
|
|
19069
|
-
const openAIOperationRegistry = new
|
|
19995
|
+
const openAIOperationRegistry = new import_core7.OpenAIOperationRegistry();
|
|
19070
19996
|
const imageRuntimeManager = new ImageRuntimeManager(initialGeneration);
|
|
19071
19997
|
const unregisterContributions = [];
|
|
19072
19998
|
try {
|
|
@@ -19140,12 +20066,12 @@ function buildDaemon(config, paths) {
|
|
|
19140
20066
|
setSecretBox(secretBox3);
|
|
19141
20067
|
setSecretBox2(secretBox3);
|
|
19142
20068
|
const decryptedConfig = decryptConfigSecrets(config, secretBox3);
|
|
19143
|
-
const accountAllowanceStore = new
|
|
20069
|
+
const accountAllowanceStore = new import_AccountAllowanceStore9.AccountAllowanceStore(
|
|
19144
20070
|
Date.now,
|
|
19145
20071
|
void 0,
|
|
19146
20072
|
new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
|
|
19147
20073
|
);
|
|
19148
|
-
(0,
|
|
20074
|
+
(0, import_AccountAllowanceStore9.setSharedAccountAllowanceStore)(accountAllowanceStore);
|
|
19149
20075
|
(0, import_AccountAllowanceScheduling5.getSharedAccountAllowanceScheduling)().configure(
|
|
19150
20076
|
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
19151
20077
|
);
|
|
@@ -19170,15 +20096,15 @@ function buildDaemon(config, paths) {
|
|
|
19170
20096
|
claudeAllowanceRefreshScheduler.configure(
|
|
19171
20097
|
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
19172
20098
|
);
|
|
19173
|
-
const subscriptionAccounts = new
|
|
19174
|
-
(0,
|
|
19175
|
-
const subscriptionRegistry = new
|
|
20099
|
+
const subscriptionAccounts = new import_subscriptions12.SubscriptionAccountService(credentialStore);
|
|
20100
|
+
(0, import_subscriptions12.setSubscriptionAccountService)(subscriptionAccounts);
|
|
20101
|
+
const subscriptionRegistry = new import_subscriptions12.SubscriptionProviderRegistry(
|
|
19176
20102
|
subscriptionAccounts,
|
|
19177
20103
|
credentialStore
|
|
19178
20104
|
);
|
|
19179
|
-
(0,
|
|
20105
|
+
(0, import_subscriptions12.setSubscriptionProviderRegistry)(subscriptionRegistry);
|
|
19180
20106
|
setServerProxyConfig(decryptedConfig.server?.proxy);
|
|
19181
|
-
(0,
|
|
20107
|
+
(0, import_upstreamFetch15.setUpstreamProxyResolver)(
|
|
19182
20108
|
createUpstreamProxyResolver({
|
|
19183
20109
|
getAccountProxy: (providerId, accountId) => credentialStore.getAccountProxy(providerId, accountId)
|
|
19184
20110
|
})
|
|
@@ -19202,7 +20128,7 @@ function buildDaemon(config, paths) {
|
|
|
19202
20128
|
const pricingEngine = new import_usage2.PricingEngine(pricingStore, logger, {
|
|
19203
20129
|
// Catalog egress follows the same global/env proxy policy as every other
|
|
19204
20130
|
// daemon upstream call; no provider/account override applies here.
|
|
19205
|
-
fetchImpl: ((input, init) => (0,
|
|
20131
|
+
fetchImpl: ((input, init) => (0, import_upstreamFetch15.fetchUpstream)(String(input), init ?? {}))
|
|
19206
20132
|
});
|
|
19207
20133
|
const pricingRefreshScheduler = new PricingRefreshScheduler(
|
|
19208
20134
|
pricingEngine,
|
|
@@ -19487,7 +20413,7 @@ function buildDaemon(config, paths) {
|
|
|
19487
20413
|
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
19488
20414
|
// excluded from the upstream trace, so a failing login left no evidence.
|
|
19489
20415
|
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
19490
|
-
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0,
|
|
20416
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0, import_upstreamFetch15.fetchUpstream)(url, init, { providerId, redactBodies: true }),
|
|
19491
20417
|
subscriptionAccountAppender: credentialStore,
|
|
19492
20418
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
19493
20419
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -19499,6 +20425,9 @@ function buildDaemon(config, paths) {
|
|
|
19499
20425
|
// paste; the app shows the verification URL + user code and polls the
|
|
19500
20426
|
// token-free status). Token captured + persisted daemon-side.
|
|
19501
20427
|
kimiSessions: new CodexOAuthSessionStore(),
|
|
20428
|
+
// Grok interactive OAuth — the same async DEVICE-CODE shape as kimi.
|
|
20429
|
+
grokSessions: new CodexOAuthSessionStore(),
|
|
20430
|
+
copilotSessions: new CodexOAuthSessionStore(),
|
|
19502
20431
|
// Migration pack (app-parity child 6, design D2/D3) — the concrete credential
|
|
19503
20432
|
// store provides BOTH the full DECRYPTED read (`getFullConfig`, export) and
|
|
19504
20433
|
// the multi-account append (`appendProviderAccount`, import re-encrypts at-
|
|
@@ -19557,7 +20486,7 @@ function buildDaemon(config, paths) {
|
|
|
19557
20486
|
});
|
|
19558
20487
|
const webhookDispatcher = new WebhookDispatcher({
|
|
19559
20488
|
logger,
|
|
19560
|
-
fetchImpl: (url, init) => (0,
|
|
20489
|
+
fetchImpl: (url, init) => (0, import_upstreamFetch15.fetchUpstream)(url, init)
|
|
19561
20490
|
});
|
|
19562
20491
|
setWebhookRuntime(webhookDispatcher, (0, import_SubscriptionAccountHealth4.getSharedAccountHealth)());
|
|
19563
20492
|
const auditWriter = new AuditWriter(auditDir, logger);
|
|
@@ -19871,11 +20800,11 @@ async function runLiveProbe(url, key, fetchImpl = fetch) {
|
|
|
19871
20800
|
status: res.status,
|
|
19872
20801
|
estimateHeader: res.headers.get("x-omnicross-count-estimate")
|
|
19873
20802
|
};
|
|
19874
|
-
} catch (
|
|
20803
|
+
} catch (err8) {
|
|
19875
20804
|
return {
|
|
19876
20805
|
status: null,
|
|
19877
20806
|
estimateHeader: null,
|
|
19878
|
-
error:
|
|
20807
|
+
error: err8 instanceof Error ? err8.message : String(err8)
|
|
19879
20808
|
};
|
|
19880
20809
|
}
|
|
19881
20810
|
}
|
|
@@ -20282,9 +21211,9 @@ async function runLaunch(argv, deps) {
|
|
|
20282
21211
|
await daemon.llmConfig.ready();
|
|
20283
21212
|
await daemon.migrateUsageStore();
|
|
20284
21213
|
await daemon.providerProxy.start();
|
|
20285
|
-
} catch (
|
|
21214
|
+
} catch (err8) {
|
|
20286
21215
|
await shutdownLaunchDaemon(daemon);
|
|
20287
|
-
throw
|
|
21216
|
+
throw err8;
|
|
20288
21217
|
}
|
|
20289
21218
|
let launch;
|
|
20290
21219
|
try {
|
|
@@ -20292,9 +21221,9 @@ async function runLaunch(argv, deps) {
|
|
|
20292
21221
|
providerId: values.provider,
|
|
20293
21222
|
model: values.model
|
|
20294
21223
|
});
|
|
20295
|
-
} catch (
|
|
21224
|
+
} catch (err8) {
|
|
20296
21225
|
await shutdownLaunchDaemon(daemon);
|
|
20297
|
-
throw
|
|
21226
|
+
throw err8;
|
|
20298
21227
|
}
|
|
20299
21228
|
try {
|
|
20300
21229
|
const plan = buildCliSpawnPlan({
|
|
@@ -20399,9 +21328,9 @@ function spawnCliInherit(plan) {
|
|
|
20399
21328
|
process.removeListener("SIGINT", onSignal);
|
|
20400
21329
|
process.removeListener("SIGTERM", onSignal);
|
|
20401
21330
|
};
|
|
20402
|
-
child.on("error", (
|
|
21331
|
+
child.on("error", (err8) => {
|
|
20403
21332
|
detach();
|
|
20404
|
-
if (
|
|
21333
|
+
if (err8.code === "ENOENT") {
|
|
20405
21334
|
reject(
|
|
20406
21335
|
new Error(
|
|
20407
21336
|
`launch: "${plan.command}" not found on PATH \u2014 install the CLI first.`
|
|
@@ -20409,7 +21338,7 @@ function spawnCliInherit(plan) {
|
|
|
20409
21338
|
);
|
|
20410
21339
|
return;
|
|
20411
21340
|
}
|
|
20412
|
-
reject(
|
|
21341
|
+
reject(err8);
|
|
20413
21342
|
});
|
|
20414
21343
|
child.on("exit", (code, signal) => {
|
|
20415
21344
|
detach();
|
|
@@ -20422,9 +21351,9 @@ function spawnCliInherit(plan) {
|
|
|
20422
21351
|
var import_node_child_process3 = require("child_process");
|
|
20423
21352
|
var import_node_readline2 = require("readline");
|
|
20424
21353
|
var import_node_util7 = require("util");
|
|
20425
|
-
var
|
|
20426
|
-
var
|
|
20427
|
-
var PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
|
|
21354
|
+
var import_upstreamFetch16 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
21355
|
+
var import_subscriptions13 = require("@omnicross/subscriptions");
|
|
21356
|
+
var PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
|
|
20428
21357
|
async function runLogin(argv, deps) {
|
|
20429
21358
|
const { values, positionals } = (0, import_node_util7.parseArgs)({
|
|
20430
21359
|
args: argv,
|
|
@@ -20432,7 +21361,9 @@ async function runLogin(argv, deps) {
|
|
|
20432
21361
|
config: { type: "string", short: "c" },
|
|
20433
21362
|
"master-key-file": { type: "string" },
|
|
20434
21363
|
// Optional user label for the appended account (multi-account).
|
|
20435
|
-
label: { type: "string" }
|
|
21364
|
+
label: { type: "string" },
|
|
21365
|
+
// Optional GitHub Enterprise domain for `login copilot` (GHE accounts).
|
|
21366
|
+
enterprise: { type: "string" }
|
|
20436
21367
|
},
|
|
20437
21368
|
allowPositionals: true
|
|
20438
21369
|
});
|
|
@@ -20446,46 +21377,55 @@ async function runLogin(argv, deps) {
|
|
|
20446
21377
|
if (!values.config) {
|
|
20447
21378
|
throw new Error("login: --config <path> is required");
|
|
20448
21379
|
}
|
|
21380
|
+
if (values.enterprise !== void 0 && provider !== "copilot") {
|
|
21381
|
+
throw new Error("login: --enterprise is only supported for the copilot provider");
|
|
21382
|
+
}
|
|
21383
|
+
const enterpriseDomain = values.enterprise !== void 0 ? import_subscriptions13.copilotOAuth.normalizeCopilotEnterpriseDomain(values.enterprise) : void 0;
|
|
20449
21384
|
const resolved = {
|
|
20450
21385
|
openBrowser: deps?.openBrowser ?? openBrowser,
|
|
20451
21386
|
promptPaste: deps?.promptPaste ?? promptPaste,
|
|
20452
21387
|
awaitLoopback: deps?.awaitLoopback ?? ((state) => awaitLoopbackCode(state)),
|
|
20453
21388
|
awaitKimiDevice: deps?.awaitKimiDevice ?? ((fetchImpl) => runKimiDeviceFlow(fetchImpl, resolvedOpenBrowser)),
|
|
21389
|
+
awaitGrokDevice: deps?.awaitGrokDevice ?? ((fetchImpl) => runGrokDeviceFlow(fetchImpl, resolvedOpenBrowser)),
|
|
21390
|
+
awaitCopilotDevice: deps?.awaitCopilotDevice ?? ((fetchImpl, enterpriseUrl) => runCopilotDeviceFlow(fetchImpl, resolvedOpenBrowser, enterpriseUrl)),
|
|
20454
21391
|
tokensFetch: deps?.tokensFetch
|
|
20455
21392
|
};
|
|
20456
21393
|
const resolvedOpenBrowser = resolved.openBrowser;
|
|
20457
21394
|
const box = resolveSecretBox(values["master-key-file"]);
|
|
20458
21395
|
setSecretBox(box);
|
|
20459
|
-
(0,
|
|
21396
|
+
(0, import_upstreamFetch16.setUpstreamProxyResolver)(createUpstreamProxyResolver());
|
|
20460
21397
|
try {
|
|
20461
21398
|
const tokensPath = defaultTokensPath(values.config);
|
|
20462
|
-
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => (0,
|
|
21399
|
+
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => (0, import_upstreamFetch16.fetchUpstream)(url, init, { providerId: provider, redactBodies: true }));
|
|
20463
21400
|
const store = new JsonSubscriptionCredentialStore(tokensPath, box, exchangeFetch);
|
|
20464
21401
|
const expiresAt = await runProviderLogin(
|
|
20465
21402
|
provider,
|
|
20466
21403
|
store,
|
|
20467
21404
|
resolved,
|
|
20468
21405
|
exchangeFetch,
|
|
20469
|
-
values.label
|
|
21406
|
+
values.label,
|
|
21407
|
+
enterpriseDomain
|
|
20470
21408
|
);
|
|
20471
21409
|
console.info(`Logged in to '${provider}' \u2192 ${tokensPath}`);
|
|
20472
21410
|
console.info(` token: [stored, encrypted] expiresAt: ${expiresAt ?? "n/a"}`);
|
|
20473
21411
|
} finally {
|
|
20474
21412
|
setSecretBox(null);
|
|
20475
|
-
(0,
|
|
21413
|
+
(0, import_upstreamFetch16.setUpstreamProxyResolver)(null);
|
|
20476
21414
|
}
|
|
20477
21415
|
}
|
|
20478
|
-
async function runProviderLogin(provider, store, deps, exchangeFetch, label) {
|
|
21416
|
+
async function runProviderLogin(provider, store, deps, exchangeFetch, label, enterpriseUrl) {
|
|
20479
21417
|
if (provider === "codex") return loginCodex(store, deps, exchangeFetch, label);
|
|
20480
21418
|
if (provider === "claude") return loginClaude(store, deps, exchangeFetch, label);
|
|
20481
21419
|
if (provider === "kimi") return loginKimi(store, deps, exchangeFetch, label);
|
|
21420
|
+
if (provider === "grok") return loginGrok(store, deps, exchangeFetch, label);
|
|
21421
|
+
if (provider === "copilot") return loginCopilot(store, deps, exchangeFetch, label, enterpriseUrl);
|
|
20482
21422
|
return loginGemini(store, deps, exchangeFetch, label);
|
|
20483
21423
|
}
|
|
20484
21424
|
async function loginCodex(store, deps, exchangeFetch, label) {
|
|
20485
|
-
const { authUrl, codeVerifier, state } =
|
|
21425
|
+
const { authUrl, codeVerifier, state } = import_subscriptions13.codexOAuth.generateAuthParams();
|
|
20486
21426
|
await presentUrl(authUrl, deps);
|
|
20487
21427
|
const code = await deps.awaitLoopback(state);
|
|
20488
|
-
const result = await
|
|
21428
|
+
const result = await import_subscriptions13.codexOAuth.exchangeCodeForTokens(
|
|
20489
21429
|
{ authorizationCode: code, codeVerifier, state },
|
|
20490
21430
|
exchangeFetch
|
|
20491
21431
|
);
|
|
@@ -20504,7 +21444,7 @@ async function loginCodex(store, deps, exchangeFetch, label) {
|
|
|
20504
21444
|
return expiresAt;
|
|
20505
21445
|
}
|
|
20506
21446
|
async function loginClaude(store, deps, exchangeFetch, label) {
|
|
20507
|
-
const { authUrl, codeVerifier, state } =
|
|
21447
|
+
const { authUrl, codeVerifier, state } = import_subscriptions13.claudeOAuth.generateAuthParams();
|
|
20508
21448
|
await presentUrl(authUrl, deps);
|
|
20509
21449
|
const pasted = (await deps.promptPaste("Paste the authorization code (code#state): ")).trim();
|
|
20510
21450
|
const [code, pastedState] = pasted.split("#");
|
|
@@ -20512,7 +21452,7 @@ async function loginClaude(store, deps, exchangeFetch, label) {
|
|
|
20512
21452
|
if (pastedState && pastedState !== state) {
|
|
20513
21453
|
throw new Error("login: pasted state did not match (possible CSRF) \u2014 aborting");
|
|
20514
21454
|
}
|
|
20515
|
-
const result = await
|
|
21455
|
+
const result = await import_subscriptions13.claudeOAuth.exchangeCodeForTokens(
|
|
20516
21456
|
{ authorizationCode: code, codeVerifier, state },
|
|
20517
21457
|
exchangeFetch
|
|
20518
21458
|
);
|
|
@@ -20531,11 +21471,11 @@ async function loginClaude(store, deps, exchangeFetch, label) {
|
|
|
20531
21471
|
return expiresAt;
|
|
20532
21472
|
}
|
|
20533
21473
|
async function loginGemini(store, deps, exchangeFetch, label) {
|
|
20534
|
-
const { authUrl, codeVerifier } =
|
|
21474
|
+
const { authUrl, codeVerifier } = import_subscriptions13.geminiOAuth.generateAuthParams();
|
|
20535
21475
|
await presentUrl(authUrl, deps);
|
|
20536
21476
|
const code = (await deps.promptPaste("Paste the authorization code: ")).trim();
|
|
20537
21477
|
if (!code) throw new Error("login: no authorization code was pasted");
|
|
20538
|
-
const result = await
|
|
21478
|
+
const result = await import_subscriptions13.geminiOAuth.exchangeCodeForTokens(code, codeVerifier, exchangeFetch);
|
|
20539
21479
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
20540
21480
|
const block = {
|
|
20541
21481
|
authMethod: "oauth",
|
|
@@ -20550,9 +21490,9 @@ async function loginGemini(store, deps, exchangeFetch, label) {
|
|
|
20550
21490
|
return expiresAt;
|
|
20551
21491
|
}
|
|
20552
21492
|
async function runKimiDeviceFlow(exchangeFetch, openBrowserFn) {
|
|
20553
|
-
const deviceId =
|
|
20554
|
-
const fingerprint =
|
|
20555
|
-
const authorization = await
|
|
21493
|
+
const deviceId = import_subscriptions13.kimiOAuth.generateKimiDeviceId();
|
|
21494
|
+
const fingerprint = import_subscriptions13.kimiOAuth.kimiFingerprintHeaders(deviceId);
|
|
21495
|
+
const authorization = await import_subscriptions13.kimiOAuth.requestDeviceAuthorization(exchangeFetch, fingerprint);
|
|
20556
21496
|
const url = authorization.verificationUriComplete ?? authorization.verificationUri;
|
|
20557
21497
|
console.info("Open this URL in your browser and approve the request:");
|
|
20558
21498
|
console.info(` ${url}`);
|
|
@@ -20560,14 +21500,14 @@ async function runKimiDeviceFlow(exchangeFetch, openBrowserFn) {
|
|
|
20560
21500
|
console.info(` Then enter this code: ${authorization.userCode}`);
|
|
20561
21501
|
}
|
|
20562
21502
|
await openBrowserFn(url).catch(() => false);
|
|
20563
|
-
const result = await
|
|
21503
|
+
const result = await import_subscriptions13.kimiOAuth.awaitDeviceToken(authorization, exchangeFetch, {
|
|
20564
21504
|
fingerprint,
|
|
20565
21505
|
onPending: () => process.stdout.write(".")
|
|
20566
21506
|
});
|
|
20567
21507
|
console.info("");
|
|
20568
21508
|
return {
|
|
20569
21509
|
...result,
|
|
20570
|
-
accountId:
|
|
21510
|
+
accountId: import_subscriptions13.kimiOAuth.kimiAccountIdFromAccessToken(result.accessToken),
|
|
20571
21511
|
deviceId
|
|
20572
21512
|
};
|
|
20573
21513
|
}
|
|
@@ -20588,6 +21528,90 @@ async function loginKimi(store, deps, exchangeFetch, label) {
|
|
|
20588
21528
|
logMasked("kimi", result.accessToken);
|
|
20589
21529
|
return expiresAt;
|
|
20590
21530
|
}
|
|
21531
|
+
async function runGrokDeviceFlow(exchangeFetch, openBrowserFn) {
|
|
21532
|
+
const tokenEndpoint = await import_subscriptions13.grokOAuth.resolveGrokTokenEndpoint(exchangeFetch);
|
|
21533
|
+
const authorization = await import_subscriptions13.grokOAuth.requestGrokDeviceAuthorization(exchangeFetch);
|
|
21534
|
+
const url = authorization.verificationUriComplete ?? authorization.verificationUri;
|
|
21535
|
+
console.info("Open this URL in your browser and approve the request:");
|
|
21536
|
+
console.info(` ${url}`);
|
|
21537
|
+
if (!authorization.verificationUriComplete) {
|
|
21538
|
+
console.info(` Then enter this code: ${authorization.userCode}`);
|
|
21539
|
+
}
|
|
21540
|
+
await openBrowserFn(url).catch(() => false);
|
|
21541
|
+
const result = await import_subscriptions13.grokOAuth.awaitGrokDeviceToken(authorization, tokenEndpoint, exchangeFetch, {
|
|
21542
|
+
onPending: () => process.stdout.write(".")
|
|
21543
|
+
});
|
|
21544
|
+
console.info("");
|
|
21545
|
+
return {
|
|
21546
|
+
...result,
|
|
21547
|
+
accountId: import_subscriptions13.grokOAuth.grokAccountIdFromAccessToken(result.accessToken)
|
|
21548
|
+
};
|
|
21549
|
+
}
|
|
21550
|
+
async function loginGrok(store, deps, exchangeFetch, label) {
|
|
21551
|
+
const result = await deps.awaitGrokDevice(exchangeFetch);
|
|
21552
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
21553
|
+
const block = {
|
|
21554
|
+
authMethod: "oauth",
|
|
21555
|
+
status: "authorized",
|
|
21556
|
+
accessToken: result.accessToken,
|
|
21557
|
+
refreshToken: result.refreshToken,
|
|
21558
|
+
expiresAt,
|
|
21559
|
+
...result.accountId ? { accountId: result.accountId } : {},
|
|
21560
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21561
|
+
};
|
|
21562
|
+
await store.appendProviderAccount("grok", block, label);
|
|
21563
|
+
logMasked("grok", result.accessToken);
|
|
21564
|
+
return expiresAt;
|
|
21565
|
+
}
|
|
21566
|
+
async function runCopilotDeviceFlow(exchangeFetch, openBrowserFn, enterpriseUrl) {
|
|
21567
|
+
if (enterpriseUrl) console.info(`Using GitHub Enterprise host: ${enterpriseUrl}`);
|
|
21568
|
+
const authorization = await import_subscriptions13.copilotOAuth.requestCopilotDeviceAuthorization(exchangeFetch, enterpriseUrl);
|
|
21569
|
+
const url = authorization.verificationUri;
|
|
21570
|
+
console.info("Open this URL in your browser and approve the request:");
|
|
21571
|
+
console.info(` ${url}`);
|
|
21572
|
+
console.info(` Then enter this code: ${authorization.userCode}`);
|
|
21573
|
+
await openBrowserFn(url).catch(() => false);
|
|
21574
|
+
const result = await import_subscriptions13.copilotOAuth.awaitCopilotDeviceToken(authorization, exchangeFetch, {
|
|
21575
|
+
onPending: () => process.stdout.write("."),
|
|
21576
|
+
...enterpriseUrl ? { enterpriseUrl } : {}
|
|
21577
|
+
});
|
|
21578
|
+
console.info("");
|
|
21579
|
+
const identity = await import_subscriptions13.copilotOAuth.fetchCopilotIdentity(result.accessToken, exchangeFetch, enterpriseUrl);
|
|
21580
|
+
const apiEndpoint = await import_subscriptions13.copilotOAuth.discoverCopilotApiEndpoint(result.accessToken, exchangeFetch, enterpriseUrl);
|
|
21581
|
+
console.info("Enabling Copilot models (policy)...");
|
|
21582
|
+
await import_subscriptions13.copilotOAuth.enableAllCopilotModels(
|
|
21583
|
+
result.accessToken,
|
|
21584
|
+
{ apiEndpoint, ...enterpriseUrl ? { enterpriseUrl } : {} },
|
|
21585
|
+
exchangeFetch
|
|
21586
|
+
);
|
|
21587
|
+
return {
|
|
21588
|
+
accessToken: result.accessToken,
|
|
21589
|
+
expiresIn: Math.floor(import_subscriptions13.copilotOAuth.COPILOT_FAR_FUTURE_MS / 1e3),
|
|
21590
|
+
...identity,
|
|
21591
|
+
...apiEndpoint ? { apiEndpoint } : {}
|
|
21592
|
+
};
|
|
21593
|
+
}
|
|
21594
|
+
async function loginCopilot(store, deps, exchangeFetch, label, enterpriseUrl) {
|
|
21595
|
+
const result = await deps.awaitCopilotDevice(exchangeFetch, enterpriseUrl);
|
|
21596
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
21597
|
+
const block = {
|
|
21598
|
+
authMethod: "oauth",
|
|
21599
|
+
status: "authorized",
|
|
21600
|
+
accessToken: result.accessToken,
|
|
21601
|
+
// ghu_ tokens have no refresh lifecycle — the same token doubles as the
|
|
21602
|
+
// stored refresh credential so generic refresh paths stay well-formed.
|
|
21603
|
+
refreshToken: result.accessToken,
|
|
21604
|
+
expiresAt,
|
|
21605
|
+
...result.accountId ? { accountId: result.accountId } : {},
|
|
21606
|
+
...result.email ? { email: result.email } : {},
|
|
21607
|
+
...result.apiEndpoint ? { apiEndpoint: result.apiEndpoint } : {},
|
|
21608
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
21609
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21610
|
+
};
|
|
21611
|
+
await store.appendProviderAccount("copilot", block, label);
|
|
21612
|
+
logMasked("copilot", result.accessToken);
|
|
21613
|
+
return expiresAt;
|
|
21614
|
+
}
|
|
20591
21615
|
function isLoginProvider(value) {
|
|
20592
21616
|
return PROVIDERS2.includes(value);
|
|
20593
21617
|
}
|
|
@@ -21293,7 +22317,7 @@ async function main() {
|
|
|
21293
22317
|
process.exitCode = 1;
|
|
21294
22318
|
}
|
|
21295
22319
|
}
|
|
21296
|
-
main().catch((
|
|
21297
|
-
console.error(
|
|
22320
|
+
main().catch((err8) => {
|
|
22321
|
+
console.error(err8 instanceof Error ? err8.message : String(err8));
|
|
21298
22322
|
process.exitCode = 1;
|
|
21299
22323
|
});
|