@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.js
CHANGED
|
@@ -1170,14 +1170,14 @@ import { setSubscriptionRegistryForOutbound } from "@omnicross/core/outbound-api
|
|
|
1170
1170
|
import { getSharedAccountHealth as getSharedAccountHealth4 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
1171
1171
|
import {
|
|
1172
1172
|
__resetSharedAccountAllowanceStoreForTests,
|
|
1173
|
-
AccountAllowanceStore as
|
|
1173
|
+
AccountAllowanceStore as AccountAllowanceStore8,
|
|
1174
1174
|
setSharedAccountAllowanceStore
|
|
1175
1175
|
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
1176
1176
|
import {
|
|
1177
1177
|
__resetSharedAccountAllowanceSchedulingForTests,
|
|
1178
1178
|
getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling5
|
|
1179
1179
|
} from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
1180
|
-
import { fetchUpstream as
|
|
1180
|
+
import { fetchUpstream as fetchUpstream13, setUpstreamProxyResolver } from "@omnicross/core/pipeline/upstreamFetch";
|
|
1181
1181
|
import { __resetSharedIdentityStoreForTests } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
|
|
1182
1182
|
import { setGeminiCodeAssistResolver } from "@omnicross/core/ports/gemini-code-assist-resolver";
|
|
1183
1183
|
import {
|
|
@@ -1382,9 +1382,188 @@ function handleKimiOAuthStatus(sessionId, deps) {
|
|
|
1382
1382
|
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
1383
1383
|
}
|
|
1384
1384
|
|
|
1385
|
+
// src/admin/accountsGrokOAuth.ts
|
|
1386
|
+
import { grokOAuth } from "@omnicross/subscriptions";
|
|
1387
|
+
function err3(status, message) {
|
|
1388
|
+
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
1389
|
+
}
|
|
1390
|
+
var DEFAULT_GROK_OAUTH_TTL_MS = 15 * 6e4;
|
|
1391
|
+
async function handleGrokOAuthStart(deps) {
|
|
1392
|
+
if (deps.grokSessions.isBusy()) {
|
|
1393
|
+
return err3(409, "a grok sign-in is already in progress \u2014 finish it in the browser or cancel it");
|
|
1394
|
+
}
|
|
1395
|
+
const fetchImpl = deps.oauthExchangeFetch("grok");
|
|
1396
|
+
let tokenEndpoint;
|
|
1397
|
+
try {
|
|
1398
|
+
tokenEndpoint = await grokOAuth.resolveGrokTokenEndpoint(fetchImpl);
|
|
1399
|
+
} catch (e) {
|
|
1400
|
+
const reason = e instanceof Error ? e.message : "OIDC discovery failed";
|
|
1401
|
+
return err3(502, `grok token-endpoint discovery failed: ${reason}`);
|
|
1402
|
+
}
|
|
1403
|
+
let authorization;
|
|
1404
|
+
try {
|
|
1405
|
+
authorization = await grokOAuth.requestGrokDeviceAuthorization(fetchImpl);
|
|
1406
|
+
} catch (e) {
|
|
1407
|
+
const reason = e instanceof Error ? e.message : "device authorization failed";
|
|
1408
|
+
return err3(502, `grok device authorization failed: ${reason}`);
|
|
1409
|
+
}
|
|
1410
|
+
const { sessionId, signal } = deps.grokSessions.begin();
|
|
1411
|
+
void runGrokDevicePoll(sessionId, tokenEndpoint, authorization.deviceCode, signal, deps).catch((e) => {
|
|
1412
|
+
const reason = e instanceof Error ? e.message : "grok sign-in failed";
|
|
1413
|
+
deps.grokSessions.settle(sessionId, "error", reason);
|
|
1414
|
+
});
|
|
1415
|
+
return {
|
|
1416
|
+
status: 200,
|
|
1417
|
+
body: {
|
|
1418
|
+
authUrl: authorization.verificationUriComplete ?? authorization.verificationUri,
|
|
1419
|
+
userCode: authorization.userCode,
|
|
1420
|
+
sessionId
|
|
1421
|
+
}
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
async function runGrokDevicePoll(sessionId, tokenEndpoint, deviceCode, signal, deps) {
|
|
1425
|
+
const fetchImpl = deps.oauthExchangeFetch("grok");
|
|
1426
|
+
const result = await grokOAuth.awaitGrokDeviceToken(
|
|
1427
|
+
{ userCode: "", deviceCode, verificationUri: "" },
|
|
1428
|
+
tokenEndpoint,
|
|
1429
|
+
fetchImpl,
|
|
1430
|
+
{
|
|
1431
|
+
deadlineMs: DEFAULT_GROK_OAUTH_TTL_MS,
|
|
1432
|
+
sleep: (ms) => new Promise((resolve11, reject) => {
|
|
1433
|
+
const onAbort = () => {
|
|
1434
|
+
clearTimeout(timer);
|
|
1435
|
+
reject(new Error("login: cancelled"));
|
|
1436
|
+
};
|
|
1437
|
+
const timer = setTimeout(() => {
|
|
1438
|
+
signal.removeEventListener("abort", onAbort);
|
|
1439
|
+
resolve11();
|
|
1440
|
+
}, ms);
|
|
1441
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1442
|
+
})
|
|
1443
|
+
}
|
|
1444
|
+
);
|
|
1445
|
+
const block = {
|
|
1446
|
+
authMethod: "oauth",
|
|
1447
|
+
status: "authorized",
|
|
1448
|
+
accessToken: result.accessToken,
|
|
1449
|
+
refreshToken: result.refreshToken,
|
|
1450
|
+
expiresAt: new Date(Date.now() + result.expiresIn * 1e3).toISOString(),
|
|
1451
|
+
accountId: grokOAuth.grokAccountIdFromAccessToken(result.accessToken),
|
|
1452
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1453
|
+
};
|
|
1454
|
+
await deps.subscriptionAccountAppender.appendProviderAccount("grok", block);
|
|
1455
|
+
deps.grokSessions.settle(sessionId, "done");
|
|
1456
|
+
}
|
|
1457
|
+
function handleGrokOAuthCancel(sessionId, deps) {
|
|
1458
|
+
if (!deps.grokSessions.cancel(sessionId)) return err3(404, "unknown or expired grok sign-in session");
|
|
1459
|
+
return { status: 200, body: { ok: true } };
|
|
1460
|
+
}
|
|
1461
|
+
function handleGrokOAuthStatus(sessionId, deps) {
|
|
1462
|
+
const s = deps.grokSessions.get(sessionId);
|
|
1463
|
+
if (!s) return err3(404, "unknown or expired grok sign-in session");
|
|
1464
|
+
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
// src/admin/accountsCopilotOAuth.ts
|
|
1468
|
+
import { copilotOAuth } from "@omnicross/subscriptions";
|
|
1469
|
+
function err4(status, message) {
|
|
1470
|
+
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
1471
|
+
}
|
|
1472
|
+
var DEFAULT_COPILOT_OAUTH_TTL_MS = 15 * 6e4;
|
|
1473
|
+
async function handleCopilotOAuthStart(deps, enterpriseUrlInput) {
|
|
1474
|
+
if (deps.copilotSessions.isBusy()) {
|
|
1475
|
+
return err4(409, "a copilot sign-in is already in progress \u2014 finish it in the browser or cancel it");
|
|
1476
|
+
}
|
|
1477
|
+
let enterpriseUrl;
|
|
1478
|
+
if (typeof enterpriseUrlInput === "string" && enterpriseUrlInput.trim()) {
|
|
1479
|
+
try {
|
|
1480
|
+
enterpriseUrl = copilotOAuth.normalizeCopilotEnterpriseDomain(enterpriseUrlInput);
|
|
1481
|
+
} catch (e) {
|
|
1482
|
+
const reason = e instanceof Error ? e.message : "invalid GitHub Enterprise domain";
|
|
1483
|
+
return err4(400, `copilot ${reason}`);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
const fetchImpl = deps.oauthExchangeFetch("copilot");
|
|
1487
|
+
let authorization;
|
|
1488
|
+
try {
|
|
1489
|
+
authorization = await copilotOAuth.requestCopilotDeviceAuthorization(fetchImpl, enterpriseUrl);
|
|
1490
|
+
} catch (e) {
|
|
1491
|
+
const reason = e instanceof Error ? e.message : "device authorization failed";
|
|
1492
|
+
return err4(502, `copilot device authorization failed: ${reason}`);
|
|
1493
|
+
}
|
|
1494
|
+
const { sessionId, signal } = deps.copilotSessions.begin();
|
|
1495
|
+
void runCopilotDevicePoll(sessionId, authorization.deviceCode, signal, deps, enterpriseUrl).catch((e) => {
|
|
1496
|
+
const reason = e instanceof Error ? e.message : "copilot sign-in failed";
|
|
1497
|
+
deps.copilotSessions.settle(sessionId, "error", reason);
|
|
1498
|
+
});
|
|
1499
|
+
return {
|
|
1500
|
+
status: 200,
|
|
1501
|
+
body: {
|
|
1502
|
+
authUrl: authorization.verificationUri,
|
|
1503
|
+
userCode: authorization.userCode,
|
|
1504
|
+
sessionId,
|
|
1505
|
+
...enterpriseUrl ? { enterpriseUrl } : {}
|
|
1506
|
+
}
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
async function runCopilotDevicePoll(sessionId, deviceCode, signal, deps, enterpriseUrl) {
|
|
1510
|
+
const fetchImpl = deps.oauthExchangeFetch("copilot");
|
|
1511
|
+
const result = await copilotOAuth.awaitCopilotDeviceToken(
|
|
1512
|
+
{ userCode: "", deviceCode, verificationUri: "", interval: 5, expiresIn: 900 },
|
|
1513
|
+
fetchImpl,
|
|
1514
|
+
{
|
|
1515
|
+
deadlineMs: DEFAULT_COPILOT_OAUTH_TTL_MS,
|
|
1516
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
1517
|
+
sleep: (ms) => new Promise((resolve11, reject) => {
|
|
1518
|
+
const onAbort = () => {
|
|
1519
|
+
clearTimeout(timer);
|
|
1520
|
+
reject(new Error("login: cancelled"));
|
|
1521
|
+
};
|
|
1522
|
+
const timer = setTimeout(() => {
|
|
1523
|
+
signal.removeEventListener("abort", onAbort);
|
|
1524
|
+
resolve11();
|
|
1525
|
+
}, ms);
|
|
1526
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1527
|
+
})
|
|
1528
|
+
}
|
|
1529
|
+
);
|
|
1530
|
+
const identity = await copilotOAuth.fetchCopilotIdentity(result.accessToken, fetchImpl, enterpriseUrl);
|
|
1531
|
+
const apiEndpoint = await copilotOAuth.discoverCopilotApiEndpoint(result.accessToken, fetchImpl, enterpriseUrl);
|
|
1532
|
+
await copilotOAuth.enableAllCopilotModels(
|
|
1533
|
+
result.accessToken,
|
|
1534
|
+
{ apiEndpoint, ...enterpriseUrl ? { enterpriseUrl } : {} },
|
|
1535
|
+
fetchImpl
|
|
1536
|
+
);
|
|
1537
|
+
const block = {
|
|
1538
|
+
authMethod: "oauth",
|
|
1539
|
+
status: "authorized",
|
|
1540
|
+
accessToken: result.accessToken,
|
|
1541
|
+
refreshToken: result.accessToken,
|
|
1542
|
+
expiresAt: new Date(Date.now() + copilotOAuth.COPILOT_FAR_FUTURE_MS).toISOString(),
|
|
1543
|
+
...identity.accountId ? { accountId: identity.accountId } : {},
|
|
1544
|
+
...identity.email ? { email: identity.email } : {},
|
|
1545
|
+
...apiEndpoint ? { apiEndpoint } : {},
|
|
1546
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
1547
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1548
|
+
};
|
|
1549
|
+
await deps.subscriptionAccountAppender.appendProviderAccount("copilot", block);
|
|
1550
|
+
deps.copilotSessions.settle(sessionId, "done");
|
|
1551
|
+
}
|
|
1552
|
+
function handleCopilotOAuthCancel(sessionId, deps) {
|
|
1553
|
+
if (!deps.copilotSessions.cancel(sessionId)) {
|
|
1554
|
+
return err4(404, "unknown or expired copilot sign-in session");
|
|
1555
|
+
}
|
|
1556
|
+
return { status: 200, body: { ok: true } };
|
|
1557
|
+
}
|
|
1558
|
+
function handleCopilotOAuthStatus(sessionId, deps) {
|
|
1559
|
+
const s = deps.copilotSessions.get(sessionId);
|
|
1560
|
+
if (!s) return err4(404, "unknown or expired copilot sign-in session");
|
|
1561
|
+
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1385
1564
|
// src/allowance/AccountAllowanceService.ts
|
|
1386
1565
|
import {
|
|
1387
|
-
getSharedAccountAllowanceStore as
|
|
1566
|
+
getSharedAccountAllowanceStore as getSharedAccountAllowanceStore7
|
|
1388
1567
|
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
1389
1568
|
import {
|
|
1390
1569
|
getSharedAccountAllowanceScheduling
|
|
@@ -1972,10 +2151,491 @@ function parseKimiUsagePayload(payload, now) {
|
|
|
1972
2151
|
}
|
|
1973
2152
|
}
|
|
1974
2153
|
}
|
|
1975
|
-
return [...byId.values()].sort((a, b) => (a.windowMinutes ?? Infinity) - (b.windowMinutes ?? Infinity)).slice(0, 4);
|
|
2154
|
+
return [...byId.values()].sort((a, b) => (a.windowMinutes ?? Infinity) - (b.windowMinutes ?? Infinity)).slice(0, 4);
|
|
2155
|
+
}
|
|
2156
|
+
var KimiAllowanceCollector = class {
|
|
2157
|
+
constructor(credentials, store = getSharedAccountAllowanceStore3(), fetchImpl = (url, init, accountId) => fetchUpstream3(url, init, { providerId: "kimi", accountId, redactBodies: true }), now = Date.now) {
|
|
2158
|
+
this.credentials = credentials;
|
|
2159
|
+
this.store = store;
|
|
2160
|
+
this.fetchImpl = fetchImpl;
|
|
2161
|
+
this.now = now;
|
|
2162
|
+
}
|
|
2163
|
+
credentials;
|
|
2164
|
+
store;
|
|
2165
|
+
fetchImpl;
|
|
2166
|
+
now;
|
|
2167
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
2168
|
+
async collectMany(accounts, options = {}) {
|
|
2169
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
2170
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
2171
|
+
}
|
|
2172
|
+
collect(account, options = {}) {
|
|
2173
|
+
const now = this.now();
|
|
2174
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
2175
|
+
const existing = this.store.get("kimi", account.id, now);
|
|
2176
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
2177
|
+
return Promise.resolve(existing);
|
|
2178
|
+
}
|
|
2179
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
2180
|
+
this.store.set(snapshot);
|
|
2181
|
+
return Promise.resolve(snapshot);
|
|
2182
|
+
}
|
|
2183
|
+
const cached = this.store.get("kimi", account.id, now);
|
|
2184
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
2185
|
+
return Promise.resolve(cached);
|
|
2186
|
+
}
|
|
2187
|
+
const running = this.inFlight.get(account.id);
|
|
2188
|
+
if (running) return running;
|
|
2189
|
+
const promise = this.fetchAccount(account.id, account.tokens).catch(() => this.failureSnapshot(account.id, "kimi_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
2190
|
+
this.inFlight.set(account.id, promise);
|
|
2191
|
+
return promise;
|
|
2192
|
+
}
|
|
2193
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
2194
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
2195
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
2196
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
2197
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
2198
|
+
}
|
|
2199
|
+
async fetchAccount(accountId, tokens) {
|
|
2200
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
|
|
2201
|
+
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
2202
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
2203
|
+
if (response.status === 401) {
|
|
2204
|
+
const refreshed = await this.credentials.refreshAccountToken("kimi", accountId);
|
|
2205
|
+
if (!refreshed) return this.failureSnapshot(accountId, "kimi_usage_unauthorized", this.now());
|
|
2206
|
+
accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
|
|
2207
|
+
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
2208
|
+
response = await this.request(accountId, accessToken, tokens);
|
|
2209
|
+
}
|
|
2210
|
+
if (response.status === 403) {
|
|
2211
|
+
const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "kimi_usage_unsupported");
|
|
2212
|
+
this.store.set(snapshot2);
|
|
2213
|
+
return snapshot2;
|
|
2214
|
+
}
|
|
2215
|
+
if (!response.ok) return this.failureSnapshot(accountId, "kimi_usage_http_error", this.now());
|
|
2216
|
+
let payload;
|
|
2217
|
+
try {
|
|
2218
|
+
payload = await response.json();
|
|
2219
|
+
} catch {
|
|
2220
|
+
return this.failureSnapshot(accountId, "kimi_usage_invalid_response", this.now());
|
|
2221
|
+
}
|
|
2222
|
+
const now = this.now();
|
|
2223
|
+
const windows = parseKimiUsagePayload(payload, now);
|
|
2224
|
+
const snapshot = {
|
|
2225
|
+
providerId: "kimi",
|
|
2226
|
+
accountId,
|
|
2227
|
+
source: "oauth-usage-api",
|
|
2228
|
+
observedAt: new Date(now).toISOString(),
|
|
2229
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2230
|
+
windows: windows.length > 0 ? windows : [
|
|
2231
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2232
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2233
|
+
],
|
|
2234
|
+
...windows.length > 0 ? {} : { lastErrorCode: "kimi_usage_invalid_response" }
|
|
2235
|
+
};
|
|
2236
|
+
this.store.set(snapshot);
|
|
2237
|
+
return snapshot;
|
|
2238
|
+
}
|
|
2239
|
+
request(accountId, accessToken, tokens) {
|
|
2240
|
+
return this.fetchImpl(KIMI_USAGE_URL, {
|
|
2241
|
+
method: "GET",
|
|
2242
|
+
headers: {
|
|
2243
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2244
|
+
Accept: "application/json",
|
|
2245
|
+
...kimiFingerprintHeaders(tokens.deviceId)
|
|
2246
|
+
},
|
|
2247
|
+
signal: AbortSignal.timeout(15e3)
|
|
2248
|
+
}, accountId);
|
|
2249
|
+
}
|
|
2250
|
+
failureSnapshot(accountId, code, now) {
|
|
2251
|
+
const existing = this.store.get("kimi", accountId, now);
|
|
2252
|
+
const snapshot = existing ? {
|
|
2253
|
+
...existing,
|
|
2254
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2255
|
+
windows: existing.windows.map((window) => ({
|
|
2256
|
+
...window,
|
|
2257
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2258
|
+
})),
|
|
2259
|
+
lastErrorCode: code
|
|
2260
|
+
} : {
|
|
2261
|
+
providerId: "kimi",
|
|
2262
|
+
accountId,
|
|
2263
|
+
source: "oauth-usage-api",
|
|
2264
|
+
observedAt: new Date(now).toISOString(),
|
|
2265
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2266
|
+
windows: [
|
|
2267
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2268
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2269
|
+
],
|
|
2270
|
+
lastErrorCode: code
|
|
2271
|
+
};
|
|
2272
|
+
this.store.set(snapshot);
|
|
2273
|
+
return snapshot;
|
|
2274
|
+
}
|
|
2275
|
+
unsupportedSnapshot(accountId, now, code = "kimi_usage_unsupported_auth") {
|
|
2276
|
+
return {
|
|
2277
|
+
providerId: "kimi",
|
|
2278
|
+
accountId,
|
|
2279
|
+
source: "oauth-usage-api",
|
|
2280
|
+
observedAt: new Date(now).toISOString(),
|
|
2281
|
+
windows: [
|
|
2282
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unsupported" },
|
|
2283
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2284
|
+
],
|
|
2285
|
+
lastErrorCode: code
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
};
|
|
2289
|
+
|
|
2290
|
+
// src/allowance/GrokAllowanceCollector.ts
|
|
2291
|
+
import {
|
|
2292
|
+
getSharedAccountAllowanceStore as getSharedAccountAllowanceStore4
|
|
2293
|
+
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
2294
|
+
import { fetchUpstream as fetchUpstream4 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
2295
|
+
var GROK_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2296
|
+
var GROK_BILLING_BASE = "https://cli-chat-proxy.grok.com";
|
|
2297
|
+
var GROK_BILLING_CREDITS_URL = `${GROK_BILLING_BASE}/v1/billing?format=credits`;
|
|
2298
|
+
var GROK_BILLING_MONTHLY_URL = `${GROK_BILLING_BASE}/v1/billing`;
|
|
2299
|
+
function isRecord2(value) {
|
|
2300
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2301
|
+
}
|
|
2302
|
+
function finiteNumber3(value) {
|
|
2303
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
2304
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
2305
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
2306
|
+
}
|
|
2307
|
+
function percent(value) {
|
|
2308
|
+
const parsed = finiteNumber3(value);
|
|
2309
|
+
return parsed !== void 0 && parsed <= 100 ? parsed : void 0;
|
|
2310
|
+
}
|
|
2311
|
+
function onDemandAmount(value) {
|
|
2312
|
+
return isRecord2(value) ? finiteNumber3(value["val"]) : void 0;
|
|
2313
|
+
}
|
|
2314
|
+
function confirmsNoMonthlyQuota(raw) {
|
|
2315
|
+
const limit = onDemandAmount(raw["monthlyLimit"]);
|
|
2316
|
+
if (limit !== void 0) return limit === 0;
|
|
2317
|
+
return parseWeeklyConfig(raw)?.inferredPercent === true;
|
|
2318
|
+
}
|
|
2319
|
+
function parseWeeklyConfig(raw) {
|
|
2320
|
+
const period = isRecord2(raw["currentPeriod"]) ? raw["currentPeriod"] : void 0;
|
|
2321
|
+
if (!period) return null;
|
|
2322
|
+
const start = typeof period["start"] === "string" ? Date.parse(period["start"]) : Number.NaN;
|
|
2323
|
+
const end = typeof period["end"] === "string" ? Date.parse(period["end"]) : Number.NaN;
|
|
2324
|
+
const type = typeof period["type"] === "string" ? period["type"] : "";
|
|
2325
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
|
|
2326
|
+
if (!type.toUpperCase().includes("WEEK")) return null;
|
|
2327
|
+
const inferred = raw["creditUsagePercent"] === void 0 || raw["creditUsagePercent"] === null;
|
|
2328
|
+
let creditUsagePercent;
|
|
2329
|
+
if (inferred) {
|
|
2330
|
+
creditUsagePercent = end > Date.now() ? 0 : void 0;
|
|
2331
|
+
} else {
|
|
2332
|
+
creditUsagePercent = percent(raw["creditUsagePercent"]);
|
|
2333
|
+
}
|
|
2334
|
+
if (creditUsagePercent === void 0) return null;
|
|
2335
|
+
return {
|
|
2336
|
+
creditUsagePercent,
|
|
2337
|
+
inferredPercent: inferred,
|
|
2338
|
+
resetsAtMs: end,
|
|
2339
|
+
unified: raw["isUnifiedBillingUser"] === true
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
function parseMonthlyConfig(raw) {
|
|
2343
|
+
const start = typeof raw["billingPeriodStart"] === "string" ? Date.parse(raw["billingPeriodStart"]) : Number.NaN;
|
|
2344
|
+
const end = typeof raw["billingPeriodEnd"] === "string" ? Date.parse(raw["billingPeriodEnd"]) : Number.NaN;
|
|
2345
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
|
|
2346
|
+
const limit = onDemandAmount(raw["monthlyLimit"]);
|
|
2347
|
+
const used = onDemandAmount(raw["used"]);
|
|
2348
|
+
if (limit === void 0 || limit <= 0 || used === void 0) return null;
|
|
2349
|
+
return { used, limit, periodStartMs: start, periodEndMs: end };
|
|
2350
|
+
}
|
|
2351
|
+
function secondsUntil4(instant, now) {
|
|
2352
|
+
if (!instant) return void 0;
|
|
2353
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2354
|
+
}
|
|
2355
|
+
var MINUTE_MS2 = 6e4;
|
|
2356
|
+
var DAY_MS2 = 864e5;
|
|
2357
|
+
var WEEK_MINUTES = 7 * 24 * 60;
|
|
2358
|
+
function weeklyWindow(config, now) {
|
|
2359
|
+
const resetsAt = new Date(config.resetsAtMs).toISOString();
|
|
2360
|
+
return {
|
|
2361
|
+
id: "seven-day",
|
|
2362
|
+
label: "7 days",
|
|
2363
|
+
scope: "all",
|
|
2364
|
+
usedPercent: config.creditUsagePercent,
|
|
2365
|
+
windowMinutes: WEEK_MINUTES,
|
|
2366
|
+
resetsAt,
|
|
2367
|
+
remainingSeconds: secondsUntil4(resetsAt, now),
|
|
2368
|
+
state: "fresh"
|
|
2369
|
+
};
|
|
2370
|
+
}
|
|
2371
|
+
function monthlyWindow(config, now) {
|
|
2372
|
+
const resetsAt = new Date(config.periodEndMs).toISOString();
|
|
2373
|
+
const days = Math.max(1, Math.round((config.periodEndMs - config.periodStartMs) / DAY_MS2));
|
|
2374
|
+
return {
|
|
2375
|
+
id: "thirty-day",
|
|
2376
|
+
label: days === 30 || days === 31 ? "30 days" : `${days} days`,
|
|
2377
|
+
scope: "all",
|
|
2378
|
+
usedPercent: Math.round(Math.min(100, config.used / config.limit * 100) * 10) / 10,
|
|
2379
|
+
windowMinutes: Math.round((config.periodEndMs - config.periodStartMs) / MINUTE_MS2),
|
|
2380
|
+
resetsAt,
|
|
2381
|
+
remainingSeconds: secondsUntil4(resetsAt, now),
|
|
2382
|
+
state: "fresh"
|
|
2383
|
+
};
|
|
2384
|
+
}
|
|
2385
|
+
function onDemandWindow(raw) {
|
|
2386
|
+
const cap = onDemandAmount(raw["onDemandCap"]);
|
|
2387
|
+
const used = onDemandAmount(raw["onDemandUsed"]);
|
|
2388
|
+
if (cap === void 0 || cap <= 0 || used === void 0) return null;
|
|
2389
|
+
return {
|
|
2390
|
+
id: "on-demand",
|
|
2391
|
+
label: "On-demand",
|
|
2392
|
+
scope: "all",
|
|
2393
|
+
usedPercent: Math.round(Math.min(100, used / cap * 100) * 10) / 10,
|
|
2394
|
+
state: "fresh"
|
|
2395
|
+
};
|
|
2396
|
+
}
|
|
2397
|
+
async function probeBilling(url, accessToken, accountId, fetchImpl) {
|
|
2398
|
+
try {
|
|
2399
|
+
const response = await fetchImpl(url, {
|
|
2400
|
+
method: "GET",
|
|
2401
|
+
headers: {
|
|
2402
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2403
|
+
Accept: "application/json",
|
|
2404
|
+
"X-XAI-Token-Auth": "xai-grok-cli"
|
|
2405
|
+
},
|
|
2406
|
+
redirect: "error",
|
|
2407
|
+
signal: AbortSignal.timeout(15e3)
|
|
2408
|
+
}, accountId);
|
|
2409
|
+
if (!response.ok) return { status: response.status, payload: null };
|
|
2410
|
+
const payload = await response.json();
|
|
2411
|
+
return { status: response.status, payload: isRecord2(payload) ? payload : null };
|
|
2412
|
+
} catch {
|
|
2413
|
+
return { status: 0, payload: null };
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
function parseGrokBillingPayloads(creditsPayload, monthlyPayload, now) {
|
|
2417
|
+
const creditsConfig = isRecord2(creditsPayload?.["config"]) ? creditsPayload["config"] : null;
|
|
2418
|
+
const monthlyConfig = isRecord2(monthlyPayload?.["config"]) ? monthlyPayload["config"] : null;
|
|
2419
|
+
let weekly = creditsConfig ? parseWeeklyConfig(creditsConfig) : null;
|
|
2420
|
+
const unifiedFlag = creditsConfig?.["isUnifiedBillingUser"] === true;
|
|
2421
|
+
let monthly = monthlyConfig ? parseMonthlyConfig(monthlyConfig) : null;
|
|
2422
|
+
if (weekly?.inferredPercent && unifiedFlag) {
|
|
2423
|
+
if (monthly) {
|
|
2424
|
+
weekly = null;
|
|
2425
|
+
} else if (!monthlyConfig || !confirmsNoMonthlyQuota(monthlyConfig)) {
|
|
2426
|
+
weekly = null;
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
const windows = [];
|
|
2430
|
+
if (weekly) windows.push(weeklyWindow(weekly, now));
|
|
2431
|
+
if (monthly) windows.push(monthlyWindow(monthly, now));
|
|
2432
|
+
const onDemandSource = monthly && monthlyConfig ? monthlyConfig : creditsConfig;
|
|
2433
|
+
const onDemand = onDemandSource ? onDemandWindow(onDemandSource) : null;
|
|
2434
|
+
if (onDemand) windows.push(onDemand);
|
|
2435
|
+
return windows.length > 0 ? windows : null;
|
|
2436
|
+
}
|
|
2437
|
+
var GrokAllowanceCollector = class {
|
|
2438
|
+
constructor(credentials, store = getSharedAccountAllowanceStore4(), fetchImpl = (url, init, accountId) => fetchUpstream4(url, init, { providerId: "grok", accountId, redactBodies: true }), now = Date.now) {
|
|
2439
|
+
this.credentials = credentials;
|
|
2440
|
+
this.store = store;
|
|
2441
|
+
this.fetchImpl = fetchImpl;
|
|
2442
|
+
this.now = now;
|
|
2443
|
+
}
|
|
2444
|
+
credentials;
|
|
2445
|
+
store;
|
|
2446
|
+
fetchImpl;
|
|
2447
|
+
now;
|
|
2448
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
2449
|
+
async collectMany(accounts, options = {}) {
|
|
2450
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
2451
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
2452
|
+
}
|
|
2453
|
+
collect(account, options = {}) {
|
|
2454
|
+
const now = this.now();
|
|
2455
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
2456
|
+
const existing = this.store.get("grok", account.id, now);
|
|
2457
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
2458
|
+
return Promise.resolve(existing);
|
|
2459
|
+
}
|
|
2460
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
2461
|
+
this.store.set(snapshot);
|
|
2462
|
+
return Promise.resolve(snapshot);
|
|
2463
|
+
}
|
|
2464
|
+
const cached = this.store.get("grok", account.id, now);
|
|
2465
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
2466
|
+
return Promise.resolve(cached);
|
|
2467
|
+
}
|
|
2468
|
+
const running = this.inFlight.get(account.id);
|
|
2469
|
+
if (running) return running;
|
|
2470
|
+
const promise = this.fetchAccount(account.id).catch(() => this.failureSnapshot(account.id, "grok_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
2471
|
+
this.inFlight.set(account.id, promise);
|
|
2472
|
+
return promise;
|
|
2473
|
+
}
|
|
2474
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
2475
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
2476
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
2477
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
2478
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
2479
|
+
}
|
|
2480
|
+
async fetchAccount(accountId) {
|
|
2481
|
+
const probe = async () => {
|
|
2482
|
+
const accessToken = await this.credentials.getAccessTokenForAccount("grok", accountId);
|
|
2483
|
+
if (!accessToken) return { unauthorized: true, windows: null };
|
|
2484
|
+
const credits = await probeBilling(GROK_BILLING_CREDITS_URL, accessToken, accountId, this.fetchImpl);
|
|
2485
|
+
if (credits.status === 401 || credits.status === 403) return { unauthorized: true, windows: null };
|
|
2486
|
+
const creditsConfig = isRecord2(credits.payload?.["config"]) ? credits.payload["config"] : null;
|
|
2487
|
+
const weekly = creditsConfig ? parseWeeklyConfig(creditsConfig) : null;
|
|
2488
|
+
const monthly = !weekly || creditsConfig?.["isUnifiedBillingUser"] === true ? await probeBilling(GROK_BILLING_MONTHLY_URL, accessToken, accountId, this.fetchImpl) : { status: 200, payload: null };
|
|
2489
|
+
if (monthly.status === 401 || monthly.status === 403) return { unauthorized: true, windows: null };
|
|
2490
|
+
return {
|
|
2491
|
+
unauthorized: false,
|
|
2492
|
+
windows: parseGrokBillingPayloads(credits.payload, monthly.payload, this.now())
|
|
2493
|
+
};
|
|
2494
|
+
};
|
|
2495
|
+
let result = await probe();
|
|
2496
|
+
if (result.unauthorized) {
|
|
2497
|
+
const refreshed = await this.credentials.refreshAccountToken("grok", accountId);
|
|
2498
|
+
if (!refreshed) return this.failureSnapshot(accountId, "grok_usage_unauthorized", this.now());
|
|
2499
|
+
result = await probe();
|
|
2500
|
+
if (result.unauthorized) {
|
|
2501
|
+
return this.failureSnapshot(accountId, "grok_usage_unauthorized", this.now());
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
const now = this.now();
|
|
2505
|
+
if (result.windows && result.windows.length > 0) {
|
|
2506
|
+
const snapshot = {
|
|
2507
|
+
providerId: "grok",
|
|
2508
|
+
accountId,
|
|
2509
|
+
source: "oauth-usage-api",
|
|
2510
|
+
observedAt: new Date(now).toISOString(),
|
|
2511
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2512
|
+
windows: result.windows
|
|
2513
|
+
};
|
|
2514
|
+
this.store.set(snapshot);
|
|
2515
|
+
return snapshot;
|
|
2516
|
+
}
|
|
2517
|
+
return this.failureSnapshot(accountId, "grok_usage_invalid_response", now);
|
|
2518
|
+
}
|
|
2519
|
+
failureSnapshot(accountId, code, now) {
|
|
2520
|
+
const existing = this.store.get("grok", accountId, now);
|
|
2521
|
+
const snapshot = existing ? {
|
|
2522
|
+
...existing,
|
|
2523
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2524
|
+
windows: existing.windows.map((window) => ({
|
|
2525
|
+
...window,
|
|
2526
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2527
|
+
})),
|
|
2528
|
+
lastErrorCode: code
|
|
2529
|
+
} : {
|
|
2530
|
+
providerId: "grok",
|
|
2531
|
+
accountId,
|
|
2532
|
+
source: "oauth-usage-api",
|
|
2533
|
+
observedAt: new Date(now).toISOString(),
|
|
2534
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2535
|
+
windows: [
|
|
2536
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" },
|
|
2537
|
+
{ id: "thirty-day", label: "30 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2538
|
+
],
|
|
2539
|
+
lastErrorCode: code
|
|
2540
|
+
};
|
|
2541
|
+
this.store.set(snapshot);
|
|
2542
|
+
return snapshot;
|
|
2543
|
+
}
|
|
2544
|
+
unsupportedSnapshot(accountId, now) {
|
|
2545
|
+
return {
|
|
2546
|
+
providerId: "grok",
|
|
2547
|
+
accountId,
|
|
2548
|
+
source: "oauth-usage-api",
|
|
2549
|
+
observedAt: new Date(now).toISOString(),
|
|
2550
|
+
windows: [
|
|
2551
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" },
|
|
2552
|
+
{ id: "thirty-day", label: "30 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2553
|
+
],
|
|
2554
|
+
lastErrorCode: "grok_usage_unsupported_auth"
|
|
2555
|
+
};
|
|
2556
|
+
}
|
|
2557
|
+
};
|
|
2558
|
+
|
|
2559
|
+
// src/allowance/CopilotAllowanceCollector.ts
|
|
2560
|
+
import {
|
|
2561
|
+
getSharedAccountAllowanceStore as getSharedAccountAllowanceStore5
|
|
2562
|
+
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
2563
|
+
import { fetchUpstream as fetchUpstream5 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
2564
|
+
import { COPILOT_GITHUB_HEADERS, copilotGitHubApiBase } from "@omnicross/subscriptions";
|
|
2565
|
+
var COPILOT_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2566
|
+
function isRecord3(value) {
|
|
2567
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2568
|
+
}
|
|
2569
|
+
function finiteNumber4(value) {
|
|
2570
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
2571
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
2572
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
2573
|
+
}
|
|
2574
|
+
function booleanValue(value) {
|
|
2575
|
+
if (typeof value === "boolean") return value;
|
|
2576
|
+
if (value === "true") return true;
|
|
2577
|
+
if (value === "false") return false;
|
|
2578
|
+
return void 0;
|
|
2579
|
+
}
|
|
2580
|
+
function parseQuotaDetail(value) {
|
|
2581
|
+
if (!isRecord3(value)) return null;
|
|
2582
|
+
const entitlement = finiteNumber4(value["entitlement"]);
|
|
2583
|
+
const remaining = finiteNumber4(value["remaining"]);
|
|
2584
|
+
const percentRemaining = finiteNumber4(value["percent_remaining"]);
|
|
2585
|
+
const unlimited = booleanValue(value["unlimited"]);
|
|
2586
|
+
if (entitlement === void 0 || remaining === void 0 || percentRemaining === void 0 || unlimited === void 0) {
|
|
2587
|
+
return null;
|
|
2588
|
+
}
|
|
2589
|
+
return { entitlement, remaining, percentRemaining, unlimited };
|
|
2590
|
+
}
|
|
2591
|
+
function secondsUntil5(instant, now) {
|
|
2592
|
+
if (!instant) return void 0;
|
|
2593
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2594
|
+
}
|
|
2595
|
+
function parseCopilotUserPayload(payload, now) {
|
|
2596
|
+
if (!isRecord3(payload)) return null;
|
|
2597
|
+
const snapshots = isRecord3(payload["quota_snapshots"]) ? payload["quota_snapshots"] : void 0;
|
|
2598
|
+
if (!snapshots) return null;
|
|
2599
|
+
const resetRaw = payload["quota_reset_date"];
|
|
2600
|
+
const resetsAt = typeof resetRaw === "string" && resetRaw.trim() && Number.isFinite(Date.parse(resetRaw)) ? new Date(Date.parse(resetRaw)).toISOString() : void 0;
|
|
2601
|
+
const windows = [];
|
|
2602
|
+
const premium = parseQuotaDetail(snapshots["premium_interactions"]);
|
|
2603
|
+
if (premium) {
|
|
2604
|
+
const usedPercent = premium.unlimited ? 0 : premium.entitlement > 0 ? Math.round(Math.min(100, (premium.entitlement - premium.remaining) / premium.entitlement * 100) * 10) / 10 : finiteNumber4(premium.percentRemaining) !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - premium.percentRemaining)) * 10) / 10 : null;
|
|
2605
|
+
if (usedPercent !== null) {
|
|
2606
|
+
windows.push({
|
|
2607
|
+
id: "thirty-day",
|
|
2608
|
+
label: "Monthly",
|
|
2609
|
+
scope: "all",
|
|
2610
|
+
usedPercent,
|
|
2611
|
+
windowMinutes: 30 * 24 * 60,
|
|
2612
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2613
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
2614
|
+
state: "fresh"
|
|
2615
|
+
});
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
const chat = parseQuotaDetail(snapshots["chat"]);
|
|
2619
|
+
if (chat && !chat.unlimited && chat.entitlement > 0) {
|
|
2620
|
+
const usedPercent = Math.round(Math.min(100, (chat.entitlement - chat.remaining) / chat.entitlement * 100) * 10) / 10;
|
|
2621
|
+
windows.push({
|
|
2622
|
+
id: "chat-monthly",
|
|
2623
|
+
label: "Chat (monthly)",
|
|
2624
|
+
scope: "all",
|
|
2625
|
+
usedPercent,
|
|
2626
|
+
windowMinutes: 30 * 24 * 60,
|
|
2627
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2628
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
2629
|
+
state: "fresh"
|
|
2630
|
+
});
|
|
2631
|
+
}
|
|
2632
|
+
return windows.length > 0 ? windows : null;
|
|
1976
2633
|
}
|
|
1977
|
-
|
|
1978
|
-
|
|
2634
|
+
function githubApiBase(tokens) {
|
|
2635
|
+
return copilotGitHubApiBase(tokens.enterpriseUrl);
|
|
2636
|
+
}
|
|
2637
|
+
var CopilotAllowanceCollector = class {
|
|
2638
|
+
constructor(credentials, store = getSharedAccountAllowanceStore5(), fetchImpl = (url, init, accountId) => fetchUpstream5(url, init, { providerId: "copilot", accountId, redactBodies: true }), now = Date.now) {
|
|
1979
2639
|
this.credentials = credentials;
|
|
1980
2640
|
this.store = store;
|
|
1981
2641
|
this.fetchImpl = fetchImpl;
|
|
@@ -1987,13 +2647,15 @@ var KimiAllowanceCollector = class {
|
|
|
1987
2647
|
now;
|
|
1988
2648
|
inFlight = /* @__PURE__ */ new Map();
|
|
1989
2649
|
async collectMany(accounts, options = {}) {
|
|
1990
|
-
const settled = await Promise.allSettled(
|
|
2650
|
+
const settled = await Promise.allSettled(
|
|
2651
|
+
accounts.map((account) => this.collect(account, options))
|
|
2652
|
+
);
|
|
1991
2653
|
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
1992
2654
|
}
|
|
1993
2655
|
collect(account, options = {}) {
|
|
1994
2656
|
const now = this.now();
|
|
1995
2657
|
if (account.tokens.authMethod !== "oauth") {
|
|
1996
|
-
const existing = this.store.get("
|
|
2658
|
+
const existing = this.store.get("copilot", account.id, now);
|
|
1997
2659
|
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
1998
2660
|
return Promise.resolve(existing);
|
|
1999
2661
|
}
|
|
@@ -2001,13 +2663,13 @@ var KimiAllowanceCollector = class {
|
|
|
2001
2663
|
this.store.set(snapshot);
|
|
2002
2664
|
return Promise.resolve(snapshot);
|
|
2003
2665
|
}
|
|
2004
|
-
const cached = this.store.get("
|
|
2666
|
+
const cached = this.store.get("copilot", account.id, now);
|
|
2005
2667
|
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
2006
2668
|
return Promise.resolve(cached);
|
|
2007
2669
|
}
|
|
2008
2670
|
const running = this.inFlight.get(account.id);
|
|
2009
2671
|
if (running) return running;
|
|
2010
|
-
const promise = this.fetchAccount(account.id, account.tokens).catch(() => this.failureSnapshot(account.id, "
|
|
2672
|
+
const promise = this.fetchAccount(account.id, account.tokens).catch(() => this.failureSnapshot(account.id, "copilot_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
2011
2673
|
this.inFlight.set(account.id, promise);
|
|
2012
2674
|
return promise;
|
|
2013
2675
|
}
|
|
@@ -2018,101 +2680,97 @@ var KimiAllowanceCollector = class {
|
|
|
2018
2680
|
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
2019
2681
|
}
|
|
2020
2682
|
async fetchAccount(accountId, tokens) {
|
|
2021
|
-
let accessToken = await this.credentials.getAccessTokenForAccount("
|
|
2022
|
-
if (!accessToken) return this.failureSnapshot(accountId, "
|
|
2683
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
|
|
2684
|
+
if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
|
|
2023
2685
|
let response = await this.request(accountId, accessToken, tokens);
|
|
2024
|
-
if (response.status === 401) {
|
|
2025
|
-
const refreshed = await this.credentials.refreshAccountToken("
|
|
2026
|
-
if (!refreshed) return this.failureSnapshot(accountId, "
|
|
2027
|
-
accessToken = await this.credentials.getAccessTokenForAccount("
|
|
2028
|
-
if (!accessToken) return this.failureSnapshot(accountId, "
|
|
2686
|
+
if (response.status === 401 || response.status === 403) {
|
|
2687
|
+
const refreshed = await this.credentials.refreshAccountToken("copilot", accountId);
|
|
2688
|
+
if (!refreshed) return this.failureSnapshot(accountId, "copilot_usage_unauthorized", this.now());
|
|
2689
|
+
accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
|
|
2690
|
+
if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
|
|
2029
2691
|
response = await this.request(accountId, accessToken, tokens);
|
|
2692
|
+
if (response.status === 401 || response.status === 403) {
|
|
2693
|
+
return this.failureSnapshot(accountId, "copilot_usage_unauthorized", this.now());
|
|
2694
|
+
}
|
|
2030
2695
|
}
|
|
2031
|
-
if (response.
|
|
2032
|
-
const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "kimi_usage_unsupported");
|
|
2033
|
-
this.store.set(snapshot2);
|
|
2034
|
-
return snapshot2;
|
|
2035
|
-
}
|
|
2036
|
-
if (!response.ok) return this.failureSnapshot(accountId, "kimi_usage_http_error", this.now());
|
|
2696
|
+
if (!response.ok) return this.failureSnapshot(accountId, "copilot_usage_http_error", this.now());
|
|
2037
2697
|
let payload;
|
|
2038
2698
|
try {
|
|
2039
2699
|
payload = await response.json();
|
|
2040
2700
|
} catch {
|
|
2041
|
-
return this.failureSnapshot(accountId, "
|
|
2701
|
+
return this.failureSnapshot(accountId, "copilot_usage_invalid_response", this.now());
|
|
2042
2702
|
}
|
|
2043
2703
|
const now = this.now();
|
|
2044
|
-
const windows =
|
|
2704
|
+
const windows = parseCopilotUserPayload(payload, now);
|
|
2045
2705
|
const snapshot = {
|
|
2046
|
-
providerId: "
|
|
2706
|
+
providerId: "copilot",
|
|
2047
2707
|
accountId,
|
|
2048
2708
|
source: "oauth-usage-api",
|
|
2049
2709
|
observedAt: new Date(now).toISOString(),
|
|
2050
|
-
expiresAt: new Date(now +
|
|
2051
|
-
windows: windows
|
|
2052
|
-
{ id: "
|
|
2053
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2710
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2711
|
+
windows: windows ?? [
|
|
2712
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2054
2713
|
],
|
|
2055
|
-
...windows
|
|
2714
|
+
...windows ? {} : { lastErrorCode: "copilot_usage_invalid_response" }
|
|
2056
2715
|
};
|
|
2057
2716
|
this.store.set(snapshot);
|
|
2058
2717
|
return snapshot;
|
|
2059
2718
|
}
|
|
2060
2719
|
request(accountId, accessToken, tokens) {
|
|
2061
|
-
return this.fetchImpl(
|
|
2720
|
+
return this.fetchImpl(`${githubApiBase(tokens)}/copilot_internal/user`, {
|
|
2062
2721
|
method: "GET",
|
|
2063
2722
|
headers: {
|
|
2064
2723
|
Authorization: `Bearer ${accessToken}`,
|
|
2065
2724
|
Accept: "application/json",
|
|
2066
|
-
|
|
2725
|
+
"Content-Type": "application/json",
|
|
2726
|
+
...COPILOT_GITHUB_HEADERS
|
|
2067
2727
|
},
|
|
2068
2728
|
signal: AbortSignal.timeout(15e3)
|
|
2069
2729
|
}, accountId);
|
|
2070
2730
|
}
|
|
2071
2731
|
failureSnapshot(accountId, code, now) {
|
|
2072
|
-
const existing = this.store.get("
|
|
2732
|
+
const existing = this.store.get("copilot", accountId, now);
|
|
2073
2733
|
const snapshot = existing ? {
|
|
2074
2734
|
...existing,
|
|
2075
|
-
expiresAt: new Date(now +
|
|
2735
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2076
2736
|
windows: existing.windows.map((window) => ({
|
|
2077
2737
|
...window,
|
|
2078
2738
|
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
2079
2739
|
})),
|
|
2080
2740
|
lastErrorCode: code
|
|
2081
2741
|
} : {
|
|
2082
|
-
providerId: "
|
|
2742
|
+
providerId: "copilot",
|
|
2083
2743
|
accountId,
|
|
2084
2744
|
source: "oauth-usage-api",
|
|
2085
2745
|
observedAt: new Date(now).toISOString(),
|
|
2086
|
-
expiresAt: new Date(now +
|
|
2746
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
2087
2747
|
windows: [
|
|
2088
|
-
{ id: "
|
|
2089
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2748
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
|
|
2090
2749
|
],
|
|
2091
2750
|
lastErrorCode: code
|
|
2092
2751
|
};
|
|
2093
2752
|
this.store.set(snapshot);
|
|
2094
2753
|
return snapshot;
|
|
2095
2754
|
}
|
|
2096
|
-
unsupportedSnapshot(accountId, now
|
|
2755
|
+
unsupportedSnapshot(accountId, now) {
|
|
2097
2756
|
return {
|
|
2098
|
-
providerId: "
|
|
2757
|
+
providerId: "copilot",
|
|
2099
2758
|
accountId,
|
|
2100
2759
|
source: "oauth-usage-api",
|
|
2101
2760
|
observedAt: new Date(now).toISOString(),
|
|
2102
2761
|
windows: [
|
|
2103
|
-
{ id: "
|
|
2104
|
-
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2762
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unsupported" }
|
|
2105
2763
|
],
|
|
2106
|
-
lastErrorCode:
|
|
2764
|
+
lastErrorCode: "copilot_usage_unsupported_auth"
|
|
2107
2765
|
};
|
|
2108
2766
|
}
|
|
2109
2767
|
};
|
|
2110
2768
|
|
|
2111
2769
|
// src/allowance/OpenCodeGoAllowanceCollector.ts
|
|
2112
2770
|
import {
|
|
2113
|
-
getSharedAccountAllowanceStore as
|
|
2771
|
+
getSharedAccountAllowanceStore as getSharedAccountAllowanceStore6
|
|
2114
2772
|
} from "@omnicross/core/pipeline/AccountAllowanceStore";
|
|
2115
|
-
import { fetchUpstream as
|
|
2773
|
+
import { fetchUpstream as fetchUpstream6 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
2116
2774
|
import { normalizeOpenCodeGoBaseUrl } from "@omnicross/subscriptions";
|
|
2117
2775
|
var OPENCODEGO_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
2118
2776
|
var OPENCODEGO_DEFAULT_GO_BASE = "https://opencode.ai/zen/go";
|
|
@@ -2126,7 +2784,7 @@ function isoInstant2(value) {
|
|
|
2126
2784
|
const time = Date.parse(value);
|
|
2127
2785
|
return Number.isFinite(time) ? new Date(time).toISOString() : void 0;
|
|
2128
2786
|
}
|
|
2129
|
-
function
|
|
2787
|
+
function secondsUntil6(instant, now) {
|
|
2130
2788
|
if (!instant) return void 0;
|
|
2131
2789
|
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
2132
2790
|
}
|
|
@@ -2141,12 +2799,12 @@ function windowFromPayload3(id, label, minutes, payload, now) {
|
|
|
2141
2799
|
usedPercent,
|
|
2142
2800
|
windowMinutes: minutes,
|
|
2143
2801
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
2144
|
-
remainingSeconds:
|
|
2802
|
+
remainingSeconds: secondsUntil6(resetsAt, now),
|
|
2145
2803
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
2146
2804
|
};
|
|
2147
2805
|
}
|
|
2148
2806
|
var OpenCodeGoAllowanceCollector = class {
|
|
2149
|
-
constructor(credentials, store =
|
|
2807
|
+
constructor(credentials, store = getSharedAccountAllowanceStore6(), fetchImpl = (url, init, accountId) => fetchUpstream6(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
|
|
2150
2808
|
this.credentials = credentials;
|
|
2151
2809
|
this.store = store;
|
|
2152
2810
|
this.fetchImpl = fetchImpl;
|
|
@@ -2251,7 +2909,7 @@ function codexUnavailable(accountId, now) {
|
|
|
2251
2909
|
};
|
|
2252
2910
|
}
|
|
2253
2911
|
var AccountAllowanceService = class {
|
|
2254
|
-
constructor(credentials, store =
|
|
2912
|
+
constructor(credentials, store = getSharedAccountAllowanceStore7(), collector, codexCollector, kimiCollector, opencodegoCollector, grokCollector, copilotCollector, now = Date.now) {
|
|
2255
2913
|
this.credentials = credentials;
|
|
2256
2914
|
this.store = store;
|
|
2257
2915
|
this.now = now;
|
|
@@ -2259,6 +2917,8 @@ var AccountAllowanceService = class {
|
|
|
2259
2917
|
this.codexCollector = codexCollector ?? new CodexAllowanceCollector(credentials, store);
|
|
2260
2918
|
this.kimiCollector = kimiCollector ?? new KimiAllowanceCollector(credentials, store);
|
|
2261
2919
|
this.opencodegoCollector = opencodegoCollector ?? new OpenCodeGoAllowanceCollector(credentials, store);
|
|
2920
|
+
this.grokCollector = grokCollector ?? new GrokAllowanceCollector(credentials, store);
|
|
2921
|
+
this.copilotCollector = copilotCollector ?? new CopilotAllowanceCollector(credentials, store);
|
|
2262
2922
|
}
|
|
2263
2923
|
credentials;
|
|
2264
2924
|
store;
|
|
@@ -2266,6 +2926,8 @@ var AccountAllowanceService = class {
|
|
|
2266
2926
|
claudeCollector;
|
|
2267
2927
|
codexCollector;
|
|
2268
2928
|
kimiCollector;
|
|
2929
|
+
grokCollector;
|
|
2930
|
+
copilotCollector;
|
|
2269
2931
|
opencodegoCollector;
|
|
2270
2932
|
/**
|
|
2271
2933
|
* Read all/filtered snapshots. Claude's and Codex's five-minute caches are
|
|
@@ -2300,11 +2962,23 @@ var AccountAllowanceService = class {
|
|
|
2300
2962
|
(account) => !filter.accountId || account.id === filter.accountId
|
|
2301
2963
|
);
|
|
2302
2964
|
if (wantsOpenCodeGo) await this.opencodegoCollector.collectMany(opencodegoAccounts);
|
|
2965
|
+
const wantsGrok = !filter.providerId || filter.providerId === "grok";
|
|
2966
|
+
const grokAccounts = (config.grokAccounts ?? []).filter(
|
|
2967
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
2968
|
+
);
|
|
2969
|
+
if (wantsGrok) await this.grokCollector.collectMany(grokAccounts);
|
|
2970
|
+
const wantsCopilot = !filter.providerId || filter.providerId === "copilot";
|
|
2971
|
+
const copilotAccounts = (config.copilotAccounts ?? []).filter(
|
|
2972
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
2973
|
+
);
|
|
2974
|
+
if (wantsCopilot) await this.copilotCollector.collectMany(copilotAccounts);
|
|
2303
2975
|
const known = /* @__PURE__ */ new Set();
|
|
2304
2976
|
if (wantsClaude) for (const account of claudeAccounts) known.add(`claude\0${account.id}`);
|
|
2305
2977
|
if (wantsCodex) for (const account of codexAccounts) known.add(`codex\0${account.id}`);
|
|
2306
2978
|
if (wantsKimi) for (const account of kimiAccounts) known.add(`kimi\0${account.id}`);
|
|
2307
2979
|
if (wantsOpenCodeGo) for (const account of opencodegoAccounts) known.add(`opencodego\0${account.id}`);
|
|
2980
|
+
if (wantsGrok) for (const account of grokAccounts) known.add(`grok\0${account.id}`);
|
|
2981
|
+
if (wantsCopilot) for (const account of copilotAccounts) known.add(`copilot\0${account.id}`);
|
|
2308
2982
|
return this.store.list(filter).filter((snapshot) => known.has(`${snapshot.providerId}\0${snapshot.accountId}`));
|
|
2309
2983
|
}
|
|
2310
2984
|
knownAccounts(config) {
|
|
@@ -2312,7 +2986,9 @@ var AccountAllowanceService = class {
|
|
|
2312
2986
|
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
2313
2987
|
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id })),
|
|
2314
2988
|
...(config.kimiAccounts ?? []).map((account) => ({ providerId: "kimi", accountId: account.id })),
|
|
2315
|
-
...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id }))
|
|
2989
|
+
...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id })),
|
|
2990
|
+
...(config.grokAccounts ?? []).map((account) => ({ providerId: "grok", accountId: account.id })),
|
|
2991
|
+
...(config.copilotAccounts ?? []).map((account) => ({ providerId: "copilot", accountId: account.id }))
|
|
2316
2992
|
];
|
|
2317
2993
|
}
|
|
2318
2994
|
/** Force-refresh Claude usage for one account or every stored Claude account. */
|
|
@@ -2355,6 +3031,24 @@ var AccountAllowanceService = class {
|
|
|
2355
3031
|
);
|
|
2356
3032
|
return this.kimiCollector.collectMany(accounts, { force: true });
|
|
2357
3033
|
}
|
|
3034
|
+
/** Force-refresh Copilot usage (copilot_internal/user) for one/all accounts. */
|
|
3035
|
+
async refreshCopilot(accountId) {
|
|
3036
|
+
const config = await this.credentials.getFullConfig();
|
|
3037
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
3038
|
+
const accounts = (config.copilotAccounts ?? []).filter(
|
|
3039
|
+
(account) => !accountId || account.id === accountId
|
|
3040
|
+
);
|
|
3041
|
+
return this.copilotCollector.collectMany(accounts, { force: true });
|
|
3042
|
+
}
|
|
3043
|
+
/** Force-refresh Grok usage (CLI billing proxy) for one/all accounts. */
|
|
3044
|
+
async refreshGrok(accountId) {
|
|
3045
|
+
const config = await this.credentials.getFullConfig();
|
|
3046
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
3047
|
+
const accounts = (config.grokAccounts ?? []).filter(
|
|
3048
|
+
(account) => !accountId || account.id === accountId
|
|
3049
|
+
);
|
|
3050
|
+
return this.grokCollector.collectMany(accounts, { force: true });
|
|
3051
|
+
}
|
|
2358
3052
|
/**
|
|
2359
3053
|
* Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
|
|
2360
3054
|
* collectors preserve their cache + per-account in-flight coalescing; a tick
|
|
@@ -2369,6 +3063,8 @@ var AccountAllowanceService = class {
|
|
|
2369
3063
|
await this.codexCollector.collectMany(config.codexAccounts ?? [], { refreshAheadMs });
|
|
2370
3064
|
await this.kimiCollector.collectMany(config.kimiAccounts ?? [], { refreshAheadMs });
|
|
2371
3065
|
await this.opencodegoCollector.collectMany(config.opencodegoAccounts ?? [], { refreshAheadMs });
|
|
3066
|
+
await this.grokCollector.collectMany(config.grokAccounts ?? [], { refreshAheadMs });
|
|
3067
|
+
await this.copilotCollector.collectMany(config.copilotAccounts ?? [], { refreshAheadMs });
|
|
2372
3068
|
}
|
|
2373
3069
|
/** Remove a cache row as soon as an account is deleted by the admin path. */
|
|
2374
3070
|
removeAccountSnapshot(providerId, accountId) {
|
|
@@ -2798,7 +3494,8 @@ import {
|
|
|
2798
3494
|
} from "@omnicross/contracts/image-generation-types";
|
|
2799
3495
|
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling2 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
2800
3496
|
import { getSharedAccountHealth } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
2801
|
-
import { fetchUpstream as
|
|
3497
|
+
import { fetchUpstream as fetchUpstream7 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
3498
|
+
import { mergeExtraHeaders } from "@omnicross/core";
|
|
2802
3499
|
|
|
2803
3500
|
// src/image-generation/imagesConfigValidation.ts
|
|
2804
3501
|
import { validateImagesServerConfig } from "@omnicross/core/outbound-api";
|
|
@@ -3111,6 +3808,7 @@ async function applyServerConfigTransaction(current, next, deps) {
|
|
|
3111
3808
|
|
|
3112
3809
|
// src/config.ts
|
|
3113
3810
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
3811
|
+
import { EXTRA_HEADER_RESERVED_NAMES } from "@omnicross/core";
|
|
3114
3812
|
var DEFAULT_ADMIN_PORT = 8766;
|
|
3115
3813
|
function validateAdmin(raw) {
|
|
3116
3814
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
@@ -3167,6 +3865,18 @@ var FORMAT_AXIS_TRANSFORMERS = [
|
|
|
3167
3865
|
"openai-response",
|
|
3168
3866
|
"gemini-code-assist"
|
|
3169
3867
|
];
|
|
3868
|
+
function validateExtraHeaders(raw) {
|
|
3869
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
3870
|
+
const reserved = EXTRA_HEADER_RESERVED_NAMES;
|
|
3871
|
+
const out = {};
|
|
3872
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
3873
|
+
if (!name.trim()) continue;
|
|
3874
|
+
if (typeof value !== "string") continue;
|
|
3875
|
+
if (reserved.has(name.toLowerCase())) continue;
|
|
3876
|
+
out[name] = value;
|
|
3877
|
+
}
|
|
3878
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
3879
|
+
}
|
|
3170
3880
|
function validateApiKeys(raw) {
|
|
3171
3881
|
if (!Array.isArray(raw)) return void 0;
|
|
3172
3882
|
const out = [];
|
|
@@ -3380,6 +4090,9 @@ function validateProvider(raw, index) {
|
|
|
3380
4090
|
apiVersion,
|
|
3381
4091
|
maxConcurrency,
|
|
3382
4092
|
modelsEndpoint,
|
|
4093
|
+
// Static extra headers: load-guard (reserved names dropped), collapse-to-
|
|
4094
|
+
// undefined; enforced by the outbound header funnel + admin probes.
|
|
4095
|
+
extraHeaders: validateExtraHeaders(p["extraHeaders"]),
|
|
3383
4096
|
// Provider transformer config (app-parity child 5): load-guard, collapse-to-
|
|
3384
4097
|
// undefined; non-secret; ENFORCED via resolveTransformerChain (parity-2 child 2).
|
|
3385
4098
|
// Format-axis entries are stripped by `migrateFormatAxis` — `use[]` is the
|
|
@@ -4344,7 +5057,10 @@ function mapPresetToProvider(preset, opts) {
|
|
|
4344
5057
|
apiFormat: resolved.format,
|
|
4345
5058
|
baseUrl: opts.baseUrlOverride ?? preset.api_base_url,
|
|
4346
5059
|
apiKey: opts.key,
|
|
4347
|
-
models: Array.isArray(preset.models) ? preset.models : void 0
|
|
5060
|
+
models: Array.isArray(preset.models) ? preset.models : void 0,
|
|
5061
|
+
// Static identity headers (e.g. the Cline client set) survive the mapping —
|
|
5062
|
+
// the CLI-seeded row needs them as much as an admin-API-created one.
|
|
5063
|
+
extraHeaders: preset.extraHeaders
|
|
4348
5064
|
};
|
|
4349
5065
|
return { provider };
|
|
4350
5066
|
}
|
|
@@ -4369,7 +5085,8 @@ function listMappablePresets() {
|
|
|
4369
5085
|
description: preset.description,
|
|
4370
5086
|
features: preset.features,
|
|
4371
5087
|
website: preset.website,
|
|
4372
|
-
modelsEndpoint: preset.modelsEndpoint
|
|
5088
|
+
modelsEndpoint: preset.modelsEndpoint,
|
|
5089
|
+
extraHeaders: preset.extraHeaders
|
|
4373
5090
|
});
|
|
4374
5091
|
}
|
|
4375
5092
|
return { mappable, excluded };
|
|
@@ -4541,7 +5258,9 @@ var VALID_PROVIDER_IDS = [
|
|
|
4541
5258
|
"codex",
|
|
4542
5259
|
"gemini",
|
|
4543
5260
|
"opencodego",
|
|
4544
|
-
"kimi"
|
|
5261
|
+
"kimi",
|
|
5262
|
+
"grok",
|
|
5263
|
+
"copilot"
|
|
4545
5264
|
];
|
|
4546
5265
|
function asSubscriptionProviderId(id) {
|
|
4547
5266
|
return VALID_PROVIDER_IDS.includes(id) ? id : null;
|
|
@@ -4689,6 +5408,40 @@ function validateKimi(body) {
|
|
|
4689
5408
|
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "deviceId", "lastRefreshedAt", "errorMessage"]);
|
|
4690
5409
|
return out;
|
|
4691
5410
|
}
|
|
5411
|
+
function validateGrok(body) {
|
|
5412
|
+
const authMethod = str(body["authMethod"]);
|
|
5413
|
+
const status = str(body["status"]);
|
|
5414
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
5415
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
5416
|
+
const out = {
|
|
5417
|
+
authMethod,
|
|
5418
|
+
status
|
|
5419
|
+
};
|
|
5420
|
+
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "lastRefreshedAt", "errorMessage"]);
|
|
5421
|
+
return out;
|
|
5422
|
+
}
|
|
5423
|
+
function validateCopilot(body) {
|
|
5424
|
+
const authMethod = str(body["authMethod"]);
|
|
5425
|
+
const status = str(body["status"]);
|
|
5426
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
5427
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
5428
|
+
const out = {
|
|
5429
|
+
authMethod,
|
|
5430
|
+
status
|
|
5431
|
+
};
|
|
5432
|
+
copyOptional(out, body, [
|
|
5433
|
+
"accessToken",
|
|
5434
|
+
"refreshToken",
|
|
5435
|
+
"expiresAt",
|
|
5436
|
+
"accountId",
|
|
5437
|
+
"email",
|
|
5438
|
+
"apiEndpoint",
|
|
5439
|
+
"enterpriseUrl",
|
|
5440
|
+
"lastRefreshedAt",
|
|
5441
|
+
"errorMessage"
|
|
5442
|
+
]);
|
|
5443
|
+
return out;
|
|
5444
|
+
}
|
|
4692
5445
|
function validateOpenCodeGo(body) {
|
|
4693
5446
|
const authMethod = str(body["authMethod"]);
|
|
4694
5447
|
const status = str(body["status"]);
|
|
@@ -4726,6 +5479,10 @@ function validateTokenBody(providerId, body) {
|
|
|
4726
5479
|
return validateOpenCodeGo(body);
|
|
4727
5480
|
case "kimi":
|
|
4728
5481
|
return validateKimi(body);
|
|
5482
|
+
case "grok":
|
|
5483
|
+
return validateGrok(body);
|
|
5484
|
+
case "copilot":
|
|
5485
|
+
return validateCopilot(body);
|
|
4729
5486
|
default:
|
|
4730
5487
|
return null;
|
|
4731
5488
|
}
|
|
@@ -4755,12 +5512,12 @@ async function statusEntryFor(reader, providerId) {
|
|
|
4755
5512
|
|
|
4756
5513
|
// src/admin/accountsOAuth.ts
|
|
4757
5514
|
var OAUTH_HTTP_PROVIDERS = /* @__PURE__ */ new Set(["claude", "gemini"]);
|
|
4758
|
-
function
|
|
5515
|
+
function err5(status, message) {
|
|
4759
5516
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
4760
5517
|
}
|
|
4761
5518
|
function handleOAuthStart(providerId, deps) {
|
|
4762
5519
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
4763
|
-
return
|
|
5520
|
+
return err5(400, `oauth not available for provider '${providerId}'`);
|
|
4764
5521
|
}
|
|
4765
5522
|
const flow = providerId === "claude" ? claudeOAuth : geminiOAuth;
|
|
4766
5523
|
const { authUrl, codeVerifier, state } = flow.generateAuthParams();
|
|
@@ -4769,23 +5526,23 @@ function handleOAuthStart(providerId, deps) {
|
|
|
4769
5526
|
}
|
|
4770
5527
|
async function handleOAuthComplete(providerId, body, deps) {
|
|
4771
5528
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
4772
|
-
return
|
|
5529
|
+
return err5(400, `oauth not available for provider '${providerId}'`);
|
|
4773
5530
|
}
|
|
4774
5531
|
const sessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : "";
|
|
4775
5532
|
const rawCode = typeof body["code"] === "string" ? body["code"] : "";
|
|
4776
|
-
if (!sessionId) return
|
|
4777
|
-
if (!rawCode) return
|
|
5533
|
+
if (!sessionId) return err5(400, "oauth complete requires { sessionId }");
|
|
5534
|
+
if (!rawCode) return err5(400, "oauth complete requires { code }");
|
|
4778
5535
|
const session = deps.oauthSessions.peek(sessionId);
|
|
4779
|
-
if (!session) return
|
|
5536
|
+
if (!session) return err5(410, "oauth session is unknown, expired, or already used");
|
|
4780
5537
|
if (session.providerId !== providerId) {
|
|
4781
|
-
return
|
|
5538
|
+
return err5(400, `oauth session does not match provider '${providerId}'`);
|
|
4782
5539
|
}
|
|
4783
5540
|
let code = rawCode.trim();
|
|
4784
5541
|
if (providerId === "claude") {
|
|
4785
5542
|
const [splitCode, pastedState] = code.split("#");
|
|
4786
|
-
if (!splitCode) return
|
|
5543
|
+
if (!splitCode) return err5(400, "no authorization code was provided");
|
|
4787
5544
|
if (pastedState && pastedState !== session.state) {
|
|
4788
|
-
return
|
|
5545
|
+
return err5(400, "oauth state did not match (possible CSRF) \u2014 aborting");
|
|
4789
5546
|
}
|
|
4790
5547
|
code = splitCode;
|
|
4791
5548
|
}
|
|
@@ -4795,7 +5552,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
4795
5552
|
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
|
|
4796
5553
|
} catch (exchangeError) {
|
|
4797
5554
|
const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
|
|
4798
|
-
return
|
|
5555
|
+
return err5(502, `oauth token exchange failed for '${providerId}': ${reason}`);
|
|
4799
5556
|
}
|
|
4800
5557
|
deps.oauthSessions.consume(sessionId);
|
|
4801
5558
|
const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
|
|
@@ -5133,8 +5890,8 @@ function errBody(message) {
|
|
|
5133
5890
|
return { error: { type: "admin_api_error", message } };
|
|
5134
5891
|
}
|
|
5135
5892
|
var defaultCommandRunner = (command) => new Promise((resolve11) => {
|
|
5136
|
-
exec(command, { timeout: 18e4 }, (
|
|
5137
|
-
if (
|
|
5893
|
+
exec(command, { timeout: 18e4 }, (err8, _stdout, stderr) => {
|
|
5894
|
+
if (err8) resolve11({ ok: false, error: stderr.trim() || err8.message });
|
|
5138
5895
|
else resolve11({ ok: true });
|
|
5139
5896
|
});
|
|
5140
5897
|
});
|
|
@@ -5180,8 +5937,8 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
5180
5937
|
providerId: typeof body["providerId"] === "string" ? body["providerId"] : void 0,
|
|
5181
5938
|
model: typeof body["model"] === "string" ? body["model"] : void 0
|
|
5182
5939
|
});
|
|
5183
|
-
} catch (
|
|
5184
|
-
return { status: 400, body: errBody(
|
|
5940
|
+
} catch (err8) {
|
|
5941
|
+
return { status: 400, body: errBody(err8 instanceof Error ? err8.message : "no launch target") };
|
|
5185
5942
|
}
|
|
5186
5943
|
const id = randomUUID2();
|
|
5187
5944
|
let leaseId2;
|
|
@@ -5209,9 +5966,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
5209
5966
|
} else {
|
|
5210
5967
|
launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
|
|
5211
5968
|
}
|
|
5212
|
-
} catch (
|
|
5213
|
-
const status =
|
|
5214
|
-
return { status, body: errBody(
|
|
5969
|
+
} catch (err8) {
|
|
5970
|
+
const status = err8 instanceof RouteLeaseError2 ? err8.status : 400;
|
|
5971
|
+
return { status, body: errBody(err8 instanceof Error ? err8.message : "failed to build launch env") };
|
|
5215
5972
|
}
|
|
5216
5973
|
const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
|
|
5217
5974
|
const opener = ctx.opener ?? defaultTerminalOpener;
|
|
@@ -5239,9 +5996,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
5239
5996
|
onFailure: onSessionEnd
|
|
5240
5997
|
});
|
|
5241
5998
|
if (cleanup) openerCleanup = cleanup;
|
|
5242
|
-
} catch (
|
|
5999
|
+
} catch (err8) {
|
|
5243
6000
|
onSessionEnd();
|
|
5244
|
-
return { status: 500, body: errBody(
|
|
6001
|
+
return { status: 500, body: errBody(err8 instanceof Error ? err8.message : "failed to open terminal") };
|
|
5245
6002
|
}
|
|
5246
6003
|
if (ended) {
|
|
5247
6004
|
openerCleanup?.();
|
|
@@ -5804,7 +6561,7 @@ async function handleSearchQuery(req, res, deps) {
|
|
|
5804
6561
|
// src/admin/searchAdminView.ts
|
|
5805
6562
|
var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
|
|
5806
6563
|
var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
|
|
5807
|
-
function
|
|
6564
|
+
function isRecord4(value) {
|
|
5808
6565
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5809
6566
|
}
|
|
5810
6567
|
function redactSearchServerConfig(search) {
|
|
@@ -5854,13 +6611,13 @@ function resolveSecretField(entry, field, stored) {
|
|
|
5854
6611
|
else delete entry[field];
|
|
5855
6612
|
}
|
|
5856
6613
|
function preserveSearchSecrets(incoming, current) {
|
|
5857
|
-
if (!
|
|
6614
|
+
if (!isRecord4(incoming)) return incoming;
|
|
5858
6615
|
const section = { ...incoming };
|
|
5859
6616
|
const providersValue = section["providers"];
|
|
5860
|
-
if (!
|
|
6617
|
+
if (!isRecord4(providersValue)) return section;
|
|
5861
6618
|
const providers = {};
|
|
5862
6619
|
for (const [id, entryValue] of Object.entries(providersValue)) {
|
|
5863
|
-
if (!
|
|
6620
|
+
if (!isRecord4(entryValue)) {
|
|
5864
6621
|
providers[id] = entryValue;
|
|
5865
6622
|
continue;
|
|
5866
6623
|
}
|
|
@@ -5938,7 +6695,7 @@ function parseKeyPolicyBody(body) {
|
|
|
5938
6695
|
var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
|
|
5939
6696
|
var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
|
|
5940
6697
|
var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
|
|
5941
|
-
function
|
|
6698
|
+
function isRecord5(value) {
|
|
5942
6699
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
5943
6700
|
}
|
|
5944
6701
|
function nonBlank(value) {
|
|
@@ -5958,7 +6715,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5958
6715
|
const ids = /* @__PURE__ */ new Set();
|
|
5959
6716
|
raw.forEach((entry, index) => {
|
|
5960
6717
|
const path2 = `bindings[${index}]`;
|
|
5961
|
-
if (!
|
|
6718
|
+
if (!isRecord5(entry)) {
|
|
5962
6719
|
errors.push(`${path2} must be an object`);
|
|
5963
6720
|
return;
|
|
5964
6721
|
}
|
|
@@ -5987,12 +6744,12 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
5987
6744
|
} else if (entry.modelMappings.length > 100) {
|
|
5988
6745
|
errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
|
|
5989
6746
|
} else if (entry.modelMappings.some(
|
|
5990
|
-
(mapping) => !
|
|
6747
|
+
(mapping) => !isRecord5(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
|
|
5991
6748
|
)) {
|
|
5992
6749
|
errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
|
|
5993
6750
|
}
|
|
5994
6751
|
}
|
|
5995
|
-
if (!
|
|
6752
|
+
if (!isRecord5(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
|
|
5996
6753
|
errors.push(`${path2}.target is invalid`);
|
|
5997
6754
|
} else {
|
|
5998
6755
|
if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
|
|
@@ -6007,7 +6764,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
6007
6764
|
}
|
|
6008
6765
|
}
|
|
6009
6766
|
if (entry.modelMap !== void 0) {
|
|
6010
|
-
if (!
|
|
6767
|
+
if (!isRecord5(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
|
|
6011
6768
|
errors.push(`${path2}.modelMap must contain string values`);
|
|
6012
6769
|
}
|
|
6013
6770
|
}
|
|
@@ -6302,7 +7059,9 @@ var PROVIDER_KEYS = {
|
|
|
6302
7059
|
accounts: "opencodegoAccounts",
|
|
6303
7060
|
active: "activeOpencodegoAccountId"
|
|
6304
7061
|
},
|
|
6305
|
-
kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" }
|
|
7062
|
+
kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" },
|
|
7063
|
+
grok: { block: "grok", accounts: "grokAccounts", active: "activeGrokAccountId" },
|
|
7064
|
+
copilot: { block: "copilot", accounts: "copilotAccounts", active: "activeCopilotAccountId" }
|
|
6306
7065
|
};
|
|
6307
7066
|
function clone(value) {
|
|
6308
7067
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -6824,7 +7583,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
|
|
|
6824
7583
|
}
|
|
6825
7584
|
|
|
6826
7585
|
// src/admin/adminMigration.ts
|
|
6827
|
-
function
|
|
7586
|
+
function err6(status, message) {
|
|
6828
7587
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
6829
7588
|
}
|
|
6830
7589
|
async function handleExport(body, deps) {
|
|
@@ -6834,30 +7593,30 @@ async function handleExport(body, deps) {
|
|
|
6834
7593
|
return { status: 200, body: { pack, version: BUNDLE_VERSION } };
|
|
6835
7594
|
} catch (error) {
|
|
6836
7595
|
if (error instanceof WeakPassphraseError) {
|
|
6837
|
-
return
|
|
7596
|
+
return err6(400, error.message);
|
|
6838
7597
|
}
|
|
6839
|
-
return
|
|
7598
|
+
return err6(500, "failed to build the migration pack");
|
|
6840
7599
|
}
|
|
6841
7600
|
}
|
|
6842
7601
|
async function handleImport(body, deps) {
|
|
6843
7602
|
const blob = typeof body["blob"] === "string" ? body["blob"] : "";
|
|
6844
7603
|
const passphrase = typeof body["passphrase"] === "string" ? body["passphrase"] : "";
|
|
6845
7604
|
const mode = body["mode"] === "overwrite" ? "overwrite" : "merge";
|
|
6846
|
-
if (!blob) return
|
|
7605
|
+
if (!blob) return err6(400, "import requires { blob }");
|
|
6847
7606
|
try {
|
|
6848
7607
|
const counts = await applyImport(blob, passphrase, mode, deps, deps.parseProviderInput);
|
|
6849
7608
|
return { status: 200, body: counts };
|
|
6850
7609
|
} catch (error) {
|
|
6851
7610
|
if (error instanceof WeakPassphraseError) {
|
|
6852
|
-
return
|
|
7611
|
+
return err6(400, error.message);
|
|
6853
7612
|
}
|
|
6854
|
-
return
|
|
7613
|
+
return err6(400, error instanceof Error ? error.message : "import failed");
|
|
6855
7614
|
}
|
|
6856
7615
|
}
|
|
6857
7616
|
|
|
6858
7617
|
// src/admin/usagePricing.ts
|
|
6859
7618
|
import { getSharedUsageThroughputTracker } from "@omnicross/core/usage";
|
|
6860
|
-
var
|
|
7619
|
+
var err7 = (status, message) => ({
|
|
6861
7620
|
status,
|
|
6862
7621
|
body: { error: { type: "admin_api_error", message } }
|
|
6863
7622
|
});
|
|
@@ -6870,7 +7629,7 @@ function parseRange(query2) {
|
|
|
6870
7629
|
const startTs = parseFiniteInt(query2.get("startTs"));
|
|
6871
7630
|
const endTs = parseFiniteInt(query2.get("endTs"));
|
|
6872
7631
|
if (startTs === null || endTs === null) {
|
|
6873
|
-
return
|
|
7632
|
+
return err7(400, "startTs and endTs are required finite-integer unix-millis query params");
|
|
6874
7633
|
}
|
|
6875
7634
|
return { startTs, endTs };
|
|
6876
7635
|
}
|
|
@@ -6895,14 +7654,14 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
6895
7654
|
case "timeseries": {
|
|
6896
7655
|
const bucket = query2.get("bucket");
|
|
6897
7656
|
if (bucket !== "hour" && bucket !== "day" && bucket !== "month") {
|
|
6898
|
-
return
|
|
7657
|
+
return err7(400, "bucket must be one of 'hour', 'day', 'month'");
|
|
6899
7658
|
}
|
|
6900
7659
|
const now = Date.now();
|
|
6901
7660
|
const clamped = { startTs: range.startTs, endTs: Math.min(range.endTs, now) };
|
|
6902
7661
|
if (clamped.startTs < clamped.endTs) {
|
|
6903
7662
|
const projected = Math.ceil((clamped.endTs - clamped.startTs) / BUCKET_SPAN_MS[bucket]) + 1;
|
|
6904
7663
|
if (projected > MAX_TIMESERIES_BUCKETS) {
|
|
6905
|
-
return
|
|
7664
|
+
return err7(
|
|
6906
7665
|
400,
|
|
6907
7666
|
`requested range projects ~${projected} '${bucket}' buckets (max ${MAX_TIMESERIES_BUCKETS}); narrow the range or use a coarser bucket`
|
|
6908
7667
|
);
|
|
@@ -6925,7 +7684,7 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
6925
7684
|
};
|
|
6926
7685
|
}
|
|
6927
7686
|
default:
|
|
6928
|
-
return
|
|
7687
|
+
return err7(404, `unknown usage view '${view ?? ""}'`);
|
|
6929
7688
|
}
|
|
6930
7689
|
}
|
|
6931
7690
|
function poolKeyLabels(cfg) {
|
|
@@ -6974,7 +7733,7 @@ async function handlePricingList(deps) {
|
|
|
6974
7733
|
async function handlePricingUpsert(body, deps) {
|
|
6975
7734
|
const input = parsePricingEntryInput(body);
|
|
6976
7735
|
if (!input) {
|
|
6977
|
-
return
|
|
7736
|
+
return err7(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
|
|
6978
7737
|
}
|
|
6979
7738
|
const entry = await deps.pricingEngine.upsertManual(input);
|
|
6980
7739
|
return { status: 200, body: { entry } };
|
|
@@ -6983,7 +7742,7 @@ async function handlePricingDelete(query2, deps) {
|
|
|
6983
7742
|
const providerId = query2.get("providerId")?.trim() ?? "";
|
|
6984
7743
|
const modelId = query2.get("modelId")?.trim() ?? "";
|
|
6985
7744
|
if (!providerId || !modelId) {
|
|
6986
|
-
return
|
|
7745
|
+
return err7(400, "delete requires providerId and modelId query params");
|
|
6987
7746
|
}
|
|
6988
7747
|
const deleted = await deps.pricingStore.delete(providerId, modelId);
|
|
6989
7748
|
if (deleted) await deps.pricingEngine.invalidateCache();
|
|
@@ -7003,13 +7762,13 @@ async function handlePricingFetchLatest(deps) {
|
|
|
7003
7762
|
}
|
|
7004
7763
|
};
|
|
7005
7764
|
} catch (e) {
|
|
7006
|
-
return
|
|
7765
|
+
return err7(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
7007
7766
|
}
|
|
7008
7767
|
}
|
|
7009
7768
|
async function handlePricingResolveConflicts(body, deps) {
|
|
7010
7769
|
const raw = body["resolutions"];
|
|
7011
7770
|
if (!Array.isArray(raw)) {
|
|
7012
|
-
return
|
|
7771
|
+
return err7(400, "resolve-conflicts requires { resolutions: [...] }");
|
|
7013
7772
|
}
|
|
7014
7773
|
const currentRows = await deps.pricingStore.getAll();
|
|
7015
7774
|
const userEditedKeys = new Set(
|
|
@@ -7019,21 +7778,21 @@ async function handlePricingResolveConflicts(body, deps) {
|
|
|
7019
7778
|
const pendingIncoming = /* @__PURE__ */ new Map();
|
|
7020
7779
|
let staleCount = 0;
|
|
7021
7780
|
for (const item of raw) {
|
|
7022
|
-
if (!item || typeof item !== "object") return
|
|
7781
|
+
if (!item || typeof item !== "object") return err7(400, "invalid resolution entry");
|
|
7023
7782
|
const r = item;
|
|
7024
7783
|
const action = r["action"];
|
|
7025
7784
|
if (action !== "overwrite" && action !== "skip") {
|
|
7026
|
-
return
|
|
7785
|
+
return err7(400, "resolution action must be 'overwrite' or 'skip'");
|
|
7027
7786
|
}
|
|
7028
7787
|
const providerId = typeof r["providerId"] === "string" && r["providerId"].trim() ? r["providerId"].trim() : "";
|
|
7029
7788
|
const modelId = typeof r["modelId"] === "string" && r["modelId"].trim() ? r["modelId"].trim() : "";
|
|
7030
7789
|
if (!providerId || !modelId) {
|
|
7031
|
-
return
|
|
7790
|
+
return err7(400, "each resolution requires top-level providerId and modelId");
|
|
7032
7791
|
}
|
|
7033
7792
|
const incoming = parsePricingEntryInput(r["incoming"]);
|
|
7034
|
-
if (!incoming) return
|
|
7793
|
+
if (!incoming) return err7(400, "each resolution must echo a valid incoming pricing entry");
|
|
7035
7794
|
if (incoming.providerId !== providerId || incoming.modelId !== modelId) {
|
|
7036
|
-
return
|
|
7795
|
+
return err7(400, "resolution providerId/modelId must match the echoed incoming entry");
|
|
7037
7796
|
}
|
|
7038
7797
|
const key = `${providerId}::${modelId}`;
|
|
7039
7798
|
if (action === "overwrite" && !userEditedKeys.has(key)) {
|
|
@@ -7078,7 +7837,7 @@ function query(req) {
|
|
|
7078
7837
|
}
|
|
7079
7838
|
function allowanceProvider(value) {
|
|
7080
7839
|
if (!value) return void 0;
|
|
7081
|
-
return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" ? value : null;
|
|
7840
|
+
return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" || value === "grok" || value === "copilot" ? value : null;
|
|
7082
7841
|
}
|
|
7083
7842
|
async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
7084
7843
|
if (!service) return writeError2(res, 501, "account allowance service is not available");
|
|
@@ -7093,7 +7852,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7093
7852
|
const pathProvider = rest.length >= 2 ? rest[0] : null;
|
|
7094
7853
|
const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
|
|
7095
7854
|
if (providerId === null) {
|
|
7096
|
-
return writeError2(res, 400, "providerId must be claude, codex, kimi, or
|
|
7855
|
+
return writeError2(res, 400, "providerId must be claude, codex, kimi, opencodego, grok, or copilot");
|
|
7097
7856
|
}
|
|
7098
7857
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
7099
7858
|
const allowances = await service.list({ providerId, accountId });
|
|
@@ -7135,6 +7894,26 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7135
7894
|
}
|
|
7136
7895
|
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7137
7896
|
}
|
|
7897
|
+
if (requestedProvider === "copilot") {
|
|
7898
|
+
if (!service.refreshCopilot) {
|
|
7899
|
+
return writeError2(res, 501, "copilot allowance refresh is not available");
|
|
7900
|
+
}
|
|
7901
|
+
const allowances2 = await service.refreshCopilot(accountId);
|
|
7902
|
+
if (accountId && allowances2.length === 0) {
|
|
7903
|
+
return writeError2(res, 404, `Copilot account '${accountId}' not found`);
|
|
7904
|
+
}
|
|
7905
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7906
|
+
}
|
|
7907
|
+
if (requestedProvider === "grok") {
|
|
7908
|
+
if (!service.refreshGrok) {
|
|
7909
|
+
return writeError2(res, 501, "grok allowance refresh is not available");
|
|
7910
|
+
}
|
|
7911
|
+
const allowances2 = await service.refreshGrok(accountId);
|
|
7912
|
+
if (accountId && allowances2.length === 0) {
|
|
7913
|
+
return writeError2(res, 404, `Grok account '${accountId}' not found`);
|
|
7914
|
+
}
|
|
7915
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7916
|
+
}
|
|
7138
7917
|
const allowances = await service.refreshClaude(accountId);
|
|
7139
7918
|
if (accountId && allowances.length === 0) {
|
|
7140
7919
|
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
@@ -7236,6 +8015,9 @@ function toProviderView(row) {
|
|
|
7236
8015
|
apiVersion: row.apiVersion,
|
|
7237
8016
|
maxConcurrency: row.maxConcurrency,
|
|
7238
8017
|
modelsEndpoint: row.modelsEndpoint,
|
|
8018
|
+
// Static extra headers round-trip VERBATIM (non-secret identity values;
|
|
8019
|
+
// auth/content names were already dropped at the write/load gate).
|
|
8020
|
+
extraHeaders: row.extraHeaders,
|
|
7239
8021
|
// app-parity child 5: transformer config round-trips VERBATIM (non-secret —
|
|
7240
8022
|
// transform-rule names + options, no key material; absent stays absent).
|
|
7241
8023
|
transformer: row.transformer,
|
|
@@ -7305,8 +8087,8 @@ async function handleAdminApi(req, res, path2, deps) {
|
|
|
7305
8087
|
default:
|
|
7306
8088
|
return writeJsonError(res, 404, `unknown admin resource '${resource}'`);
|
|
7307
8089
|
}
|
|
7308
|
-
} catch (
|
|
7309
|
-
writeJsonError(res, 500,
|
|
8090
|
+
} catch (err8) {
|
|
8091
|
+
writeJsonError(res, 500, err8 instanceof Error ? err8.message : String(err8));
|
|
7310
8092
|
}
|
|
7311
8093
|
}
|
|
7312
8094
|
function requestQuery(req) {
|
|
@@ -7464,6 +8246,9 @@ async function handleProviderReorder(req, res, cfg, deps) {
|
|
|
7464
8246
|
persistProviders(cfg, deps);
|
|
7465
8247
|
return writeJson4(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
|
|
7466
8248
|
}
|
|
8249
|
+
function expandRowExtraHeaders(row) {
|
|
8250
|
+
return mergeExtraHeaders({}, row.extraHeaders);
|
|
8251
|
+
}
|
|
7467
8252
|
async function handleDiscoverModels(res, id, cfg) {
|
|
7468
8253
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
7469
8254
|
const row = cfg.providers.find((p) => p.id === id);
|
|
@@ -7477,7 +8262,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
7477
8262
|
try {
|
|
7478
8263
|
const headers = { Accept: "application/json" };
|
|
7479
8264
|
if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
|
|
7480
|
-
|
|
8265
|
+
Object.assign(headers, expandRowExtraHeaders(row));
|
|
8266
|
+
const response = await fetchUpstream7(url, { method: "GET", headers }, { providerId: "byo" });
|
|
7481
8267
|
if (!response.ok) {
|
|
7482
8268
|
const text = await response.text().catch(() => "");
|
|
7483
8269
|
let message = text.slice(0, 300);
|
|
@@ -7494,8 +8280,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
7494
8280
|
const data = await response.json();
|
|
7495
8281
|
const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
|
|
7496
8282
|
return writeJson4(res, 200, { models });
|
|
7497
|
-
} catch (
|
|
7498
|
-
const message =
|
|
8283
|
+
} catch (err8) {
|
|
8284
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
7499
8285
|
return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
|
|
7500
8286
|
}
|
|
7501
8287
|
}
|
|
@@ -7534,9 +8320,10 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
7534
8320
|
messages: [{ role: "user", content: prompt }]
|
|
7535
8321
|
};
|
|
7536
8322
|
}
|
|
8323
|
+
Object.assign(headers, expandRowExtraHeaders(row));
|
|
7537
8324
|
const startedAt = Date.now();
|
|
7538
8325
|
try {
|
|
7539
|
-
const response = await
|
|
8326
|
+
const response = await fetchUpstream7(
|
|
7540
8327
|
url,
|
|
7541
8328
|
{ method: "POST", headers, body: JSON.stringify(payload) },
|
|
7542
8329
|
{ providerId: "byo" }
|
|
@@ -7558,8 +8345,8 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
7558
8345
|
latencyMs,
|
|
7559
8346
|
sample: extractSampleText(text, row.apiFormat)
|
|
7560
8347
|
});
|
|
7561
|
-
} catch (
|
|
7562
|
-
const message =
|
|
8348
|
+
} catch (err8) {
|
|
8349
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
7563
8350
|
return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
|
|
7564
8351
|
}
|
|
7565
8352
|
}
|
|
@@ -7841,6 +8628,7 @@ function parseProviderInput(body, existing) {
|
|
|
7841
8628
|
const apiVersion = typeof body["apiVersion"] === "string" && body["apiVersion"].length > 0 ? body["apiVersion"] : body["apiVersion"] === null ? void 0 : existing?.apiVersion;
|
|
7842
8629
|
const modelsEndpoint = typeof body["modelsEndpoint"] === "string" && body["modelsEndpoint"].length > 0 ? body["modelsEndpoint"] : body["modelsEndpoint"] === null ? void 0 : existing?.modelsEndpoint;
|
|
7843
8630
|
const maxConcurrency = typeof body["maxConcurrency"] === "number" && Number.isFinite(body["maxConcurrency"]) ? body["maxConcurrency"] : body["maxConcurrency"] === null ? void 0 : existing?.maxConcurrency;
|
|
8631
|
+
const extraHeaders = body["extraHeaders"] === null ? void 0 : body["extraHeaders"] === void 0 ? existing?.extraHeaders : validateExtraHeaders(body["extraHeaders"]);
|
|
7844
8632
|
const transformer = body["transformer"] === null ? void 0 : parseTransformerInput(body["transformer"], existing?.transformer);
|
|
7845
8633
|
const codingPlan = body["codingPlan"] === null ? void 0 : body["codingPlan"] === void 0 ? existing?.codingPlan : body["codingPlan"] && typeof body["codingPlan"] === "object" && !Array.isArray(body["codingPlan"]) ? parseCodingPlanInput(body["codingPlan"], existing?.codingPlan) : existing?.codingPlan;
|
|
7846
8634
|
const apiModes = body["apiModes"] === null ? void 0 : parseApiModesInput(body["apiModes"], existing?.apiModes);
|
|
@@ -7866,6 +8654,7 @@ function parseProviderInput(body, existing) {
|
|
|
7866
8654
|
apiVersion,
|
|
7867
8655
|
maxConcurrency,
|
|
7868
8656
|
modelsEndpoint,
|
|
8657
|
+
extraHeaders,
|
|
7869
8658
|
transformer: migrated.transformer,
|
|
7870
8659
|
codingPlan,
|
|
7871
8660
|
apiModes,
|
|
@@ -7887,7 +8676,10 @@ function handlePresets(res, method) {
|
|
|
7887
8676
|
description: p.description,
|
|
7888
8677
|
features: p.features,
|
|
7889
8678
|
website: p.website,
|
|
7890
|
-
modelsEndpoint: p.modelsEndpoint
|
|
8679
|
+
modelsEndpoint: p.modelsEndpoint,
|
|
8680
|
+
// Static extra headers ride along so `addFromPreset` can seed them onto the
|
|
8681
|
+
// row (the write gateway re-validates via the shared allowlist).
|
|
8682
|
+
extraHeaders: p.extraHeaders
|
|
7891
8683
|
}));
|
|
7892
8684
|
return writeJson4(res, 200, { presets, excluded });
|
|
7893
8685
|
}
|
|
@@ -8369,12 +9161,12 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
8369
9161
|
}
|
|
8370
9162
|
return writeJson4(res, 200, { ok: true, affected: result.affected });
|
|
8371
9163
|
}
|
|
8372
|
-
if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi") && rest[1] === "oauth" && rest[3] === "status") {
|
|
8373
|
-
const result = rest[0] === "codex" ? handleCodexOAuthStatus(rest[2], deps) : handleKimiOAuthStatus(rest[2], deps);
|
|
9164
|
+
if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[3] === "status") {
|
|
9165
|
+
const result = rest[0] === "codex" ? handleCodexOAuthStatus(rest[2], deps) : rest[0] === "kimi" ? handleKimiOAuthStatus(rest[2], deps) : rest[0] === "grok" ? handleGrokOAuthStatus(rest[2], deps) : handleCopilotOAuthStatus(rest[2], deps);
|
|
8374
9166
|
return writeJson4(res, result.status, result.body);
|
|
8375
9167
|
}
|
|
8376
|
-
if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi") && rest[1] === "oauth" && rest[2]) {
|
|
8377
|
-
const result = rest[0] === "codex" ? handleCodexOAuthCancel(rest[2], deps) : handleKimiOAuthCancel(rest[2], deps);
|
|
9168
|
+
if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[2]) {
|
|
9169
|
+
const result = rest[0] === "codex" ? handleCodexOAuthCancel(rest[2], deps) : rest[0] === "kimi" ? handleKimiOAuthCancel(rest[2], deps) : rest[0] === "grok" ? handleGrokOAuthCancel(rest[2], deps) : handleCopilotOAuthCancel(rest[2], deps);
|
|
8378
9170
|
return writeJson4(res, result.status, result.body);
|
|
8379
9171
|
}
|
|
8380
9172
|
if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
|
|
@@ -8435,6 +9227,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
8435
9227
|
const result2 = await handleKimiOAuthStart(deps);
|
|
8436
9228
|
return writeJson4(res, result2.status, result2.body);
|
|
8437
9229
|
}
|
|
9230
|
+
if (providerId === "grok") {
|
|
9231
|
+
const result2 = await handleGrokOAuthStart(deps);
|
|
9232
|
+
return writeJson4(res, result2.status, result2.body);
|
|
9233
|
+
}
|
|
9234
|
+
if (providerId === "copilot") {
|
|
9235
|
+
const body2 = await readJsonBody4(req);
|
|
9236
|
+
const result2 = await handleCopilotOAuthStart(deps, body2["enterpriseUrl"]);
|
|
9237
|
+
return writeJson4(res, result2.status, result2.body);
|
|
9238
|
+
}
|
|
8438
9239
|
const result = handleOAuthStart(providerId, deps);
|
|
8439
9240
|
return writeJson4(res, result.status, result.body);
|
|
8440
9241
|
}
|
|
@@ -8929,12 +9730,12 @@ async function handlePlayground(req, res, method, deps) {
|
|
|
8929
9730
|
const payload = body["body"];
|
|
8930
9731
|
const status = deps.outboundApiServer.getStatus();
|
|
8931
9732
|
if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
|
|
8932
|
-
const path2 = resolvePlaygroundPath(endpoint,
|
|
9733
|
+
const path2 = resolvePlaygroundPath(endpoint, isRecord6(payload) ? payload : {});
|
|
8933
9734
|
if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
|
|
8934
9735
|
const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
|
|
8935
9736
|
await proxyToOutbound(res, status.port, path2, key, upstreamBody);
|
|
8936
9737
|
}
|
|
8937
|
-
function
|
|
9738
|
+
function isRecord6(v) {
|
|
8938
9739
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
8939
9740
|
}
|
|
8940
9741
|
function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
@@ -8963,8 +9764,8 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
|
8963
9764
|
});
|
|
8964
9765
|
}
|
|
8965
9766
|
);
|
|
8966
|
-
upstream.on("error", (
|
|
8967
|
-
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${
|
|
9767
|
+
upstream.on("error", (err8) => {
|
|
9768
|
+
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err8.message}`);
|
|
8968
9769
|
else res.end();
|
|
8969
9770
|
resolve11();
|
|
8970
9771
|
});
|
|
@@ -9069,7 +9870,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
9069
9870
|
}
|
|
9070
9871
|
|
|
9071
9872
|
// src/admin/version.ts
|
|
9072
|
-
var DAEMON_VERSION = true ? "0.
|
|
9873
|
+
var DAEMON_VERSION = true ? "0.4.0" : "0.0.0-dev";
|
|
9073
9874
|
|
|
9074
9875
|
// src/admin/AdminServer.ts
|
|
9075
9876
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -9112,13 +9913,13 @@ var AdminServer = class {
|
|
|
9112
9913
|
const server = http2.createServer((req, res) => {
|
|
9113
9914
|
this.onRequest(req, res);
|
|
9114
9915
|
});
|
|
9115
|
-
const onError = (
|
|
9116
|
-
if (
|
|
9916
|
+
const onError = (err8) => {
|
|
9917
|
+
if (err8.code === "EADDRINUSE" && port !== 0) {
|
|
9117
9918
|
server.removeListener("error", onError);
|
|
9118
9919
|
this.listen(bindAddr, 0).then(resolve11, reject);
|
|
9119
9920
|
return;
|
|
9120
9921
|
}
|
|
9121
|
-
reject(
|
|
9922
|
+
reject(err8);
|
|
9122
9923
|
};
|
|
9123
9924
|
server.on("error", onError);
|
|
9124
9925
|
server.listen(port, bindAddr, () => {
|
|
@@ -9136,8 +9937,8 @@ var AdminServer = class {
|
|
|
9136
9937
|
}
|
|
9137
9938
|
/** Per-request handler: auth gate (when a token is set) → routing. */
|
|
9138
9939
|
onRequest(req, res) {
|
|
9139
|
-
void this.dispatch(req, res).catch((
|
|
9140
|
-
const message =
|
|
9940
|
+
void this.dispatch(req, res).catch((err8) => {
|
|
9941
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
9141
9942
|
this.deps.logger.error("[AdminServer] unhandled error:", message);
|
|
9142
9943
|
if (!res.headersSent) {
|
|
9143
9944
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
@@ -9401,18 +10202,18 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
9401
10202
|
return;
|
|
9402
10203
|
}
|
|
9403
10204
|
signal?.addEventListener("abort", abort, { once: true });
|
|
9404
|
-
server.on("error", (
|
|
10205
|
+
server.on("error", (err8) => {
|
|
9405
10206
|
if (settled) return;
|
|
9406
10207
|
settled = true;
|
|
9407
10208
|
clearTimeout(timer);
|
|
9408
|
-
if (
|
|
10209
|
+
if (err8.code === "EADDRINUSE") {
|
|
9409
10210
|
reject(
|
|
9410
10211
|
new Error(
|
|
9411
10212
|
`login: cannot bind ${LOOPBACK_HOST}:${LOOPBACK_PORT} (address in use) \u2014 another codex login or process is holding the port`
|
|
9412
10213
|
)
|
|
9413
10214
|
);
|
|
9414
10215
|
} else {
|
|
9415
|
-
reject(
|
|
10216
|
+
reject(err8);
|
|
9416
10217
|
}
|
|
9417
10218
|
});
|
|
9418
10219
|
const timer = setTimeout(() => {
|
|
@@ -9488,21 +10289,22 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
|
|
|
9488
10289
|
}
|
|
9489
10290
|
|
|
9490
10291
|
// src/allowance/ProviderKeyQuotaService.ts
|
|
9491
|
-
import {
|
|
10292
|
+
import { mergeExtraHeaders as mergeExtraHeaders2 } from "@omnicross/core";
|
|
10293
|
+
import { fetchUpstream as fetchUpstream8 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
9492
10294
|
|
|
9493
10295
|
// src/allowance/ProviderKeyQuota.ts
|
|
9494
|
-
var
|
|
9495
|
-
var HOUR_MS2 = 60 *
|
|
9496
|
-
var
|
|
9497
|
-
var WEEK_MS = 7 *
|
|
9498
|
-
var MONTH_MS = 30 *
|
|
9499
|
-
function
|
|
10296
|
+
var MINUTE_MS3 = 6e4;
|
|
10297
|
+
var HOUR_MS2 = 60 * MINUTE_MS3;
|
|
10298
|
+
var DAY_MS3 = 24 * HOUR_MS2;
|
|
10299
|
+
var WEEK_MS = 7 * DAY_MS3;
|
|
10300
|
+
var MONTH_MS = 30 * DAY_MS3;
|
|
10301
|
+
function finiteNumber5(value) {
|
|
9500
10302
|
if (value === null || value === void 0 || value === "") return void 0;
|
|
9501
10303
|
const parsed = typeof value === "number" ? value : Number(value);
|
|
9502
10304
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
9503
10305
|
}
|
|
9504
10306
|
function finitePercent4(value) {
|
|
9505
|
-
const parsed =
|
|
10307
|
+
const parsed = finiteNumber5(value);
|
|
9506
10308
|
return parsed !== void 0 && parsed <= 100 ? parsed : null;
|
|
9507
10309
|
}
|
|
9508
10310
|
function isoInstant3(value) {
|
|
@@ -9510,18 +10312,18 @@ function isoInstant3(value) {
|
|
|
9510
10312
|
const time = Date.parse(value);
|
|
9511
10313
|
if (Number.isFinite(time)) return new Date(time).toISOString();
|
|
9512
10314
|
}
|
|
9513
|
-
const numeric =
|
|
10315
|
+
const numeric = finiteNumber5(value);
|
|
9514
10316
|
if (numeric !== void 0 && numeric > 1e9) {
|
|
9515
10317
|
const ms = numeric > 1e12 ? numeric : numeric * 1e3;
|
|
9516
10318
|
return new Date(ms).toISOString();
|
|
9517
10319
|
}
|
|
9518
10320
|
return void 0;
|
|
9519
10321
|
}
|
|
9520
|
-
function
|
|
10322
|
+
function secondsUntil7(instant, now) {
|
|
9521
10323
|
if (!instant) return void 0;
|
|
9522
10324
|
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
9523
10325
|
}
|
|
9524
|
-
function
|
|
10326
|
+
function isRecord7(value) {
|
|
9525
10327
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
9526
10328
|
}
|
|
9527
10329
|
function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
@@ -9544,6 +10346,7 @@ function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
|
9544
10346
|
}
|
|
9545
10347
|
if (host === "api.code.umans.ai") return "umans";
|
|
9546
10348
|
if (host === "api.synthetic.new") return "synthetic";
|
|
10349
|
+
if (host === "api.cline.bot") return "cline-pass";
|
|
9547
10350
|
return null;
|
|
9548
10351
|
}
|
|
9549
10352
|
function providerKeyQuotaUrl(adapter, baseUrl) {
|
|
@@ -9551,6 +10354,7 @@ function providerKeyQuotaUrl(adapter, baseUrl) {
|
|
|
9551
10354
|
if (adapter === "zai") return `${origin}/api/monitor/usage/quota/limit`;
|
|
9552
10355
|
if (adapter === "minimax-token-plan") return `${origin}/v1/token_plan/remains`;
|
|
9553
10356
|
if (adapter === "umans") return `${origin}/v1/usage`;
|
|
10357
|
+
if (adapter === "cline-pass") return `${origin}/api/v1/users/me/plan/usage-limits`;
|
|
9554
10358
|
return `${origin}/v2/quotas`;
|
|
9555
10359
|
}
|
|
9556
10360
|
function providerKeyQuotaAuthHeader(adapter, key) {
|
|
@@ -9562,7 +10366,7 @@ function zaiWindowDurationMs(item) {
|
|
|
9562
10366
|
case 3:
|
|
9563
10367
|
return count * HOUR_MS2;
|
|
9564
10368
|
case 4:
|
|
9565
|
-
return count *
|
|
10369
|
+
return count * DAY_MS3;
|
|
9566
10370
|
case 5:
|
|
9567
10371
|
return count * MONTH_MS;
|
|
9568
10372
|
case 6:
|
|
@@ -9575,8 +10379,8 @@ function zaiWindowIdLabel(durationMs) {
|
|
|
9575
10379
|
if (durationMs === WEEK_MS) return { id: "seven-day", label: "7 days" };
|
|
9576
10380
|
if (durationMs === 5 * HOUR_MS2) return { id: "five-hour", label: "5 hours" };
|
|
9577
10381
|
if (durationMs === MONTH_MS) return { id: "thirty-day", label: "30 days" };
|
|
9578
|
-
if (durationMs !== void 0 && durationMs %
|
|
9579
|
-
const days = durationMs /
|
|
10382
|
+
if (durationMs !== void 0 && durationMs % DAY_MS3 === 0) {
|
|
10383
|
+
const days = durationMs / DAY_MS3;
|
|
9580
10384
|
return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}` };
|
|
9581
10385
|
}
|
|
9582
10386
|
if (durationMs !== void 0 && durationMs % HOUR_MS2 === 0) {
|
|
@@ -9586,23 +10390,23 @@ function zaiWindowIdLabel(durationMs) {
|
|
|
9586
10390
|
return { id: "quota", label: "Quota" };
|
|
9587
10391
|
}
|
|
9588
10392
|
function parseZaiQuotaPayload(payload, now) {
|
|
9589
|
-
if (!
|
|
9590
|
-
const data =
|
|
10393
|
+
if (!isRecord7(payload)) return null;
|
|
10394
|
+
const data = isRecord7(payload["data"]) ? payload["data"] : payload;
|
|
9591
10395
|
if (payload["success"] === false) return null;
|
|
9592
10396
|
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
9593
10397
|
const byWindow = /* @__PURE__ */ new Map();
|
|
9594
10398
|
for (const raw of limits) {
|
|
9595
|
-
if (!
|
|
10399
|
+
if (!isRecord7(raw)) continue;
|
|
9596
10400
|
const item = raw;
|
|
9597
10401
|
if (item.type === void 0) continue;
|
|
9598
10402
|
const details = raw["usageDetails"];
|
|
9599
|
-
if (Array.isArray(details) && details.some((d) =>
|
|
10403
|
+
if (Array.isArray(details) && details.some((d) => isRecord7(d) && d["modelCode"] === "zread")) {
|
|
9600
10404
|
continue;
|
|
9601
10405
|
}
|
|
9602
10406
|
const durationMs = zaiWindowDurationMs(item);
|
|
9603
10407
|
const { id, label } = zaiWindowIdLabel(durationMs);
|
|
9604
|
-
const limit =
|
|
9605
|
-
const used =
|
|
10408
|
+
const limit = finiteNumber5(item.usage);
|
|
10409
|
+
const used = finiteNumber5(item.currentValue);
|
|
9606
10410
|
const fromAbsolute = limit !== void 0 && used !== void 0 && limit > 0 ? Math.min(100, used / limit * 100) : void 0;
|
|
9607
10411
|
const fromPercentage = finitePercent4(item.percentage) ?? void 0;
|
|
9608
10412
|
const usedPercent = fromAbsolute !== void 0 ? Math.round(fromAbsolute * 10) / 10 : fromPercentage;
|
|
@@ -9613,9 +10417,9 @@ function parseZaiQuotaPayload(payload, now) {
|
|
|
9613
10417
|
label,
|
|
9614
10418
|
scope: "all",
|
|
9615
10419
|
usedPercent,
|
|
9616
|
-
...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs /
|
|
10420
|
+
...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs / MINUTE_MS3) } : {},
|
|
9617
10421
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9618
|
-
remainingSeconds:
|
|
10422
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9619
10423
|
state: "fresh"
|
|
9620
10424
|
};
|
|
9621
10425
|
const existing = byWindow.get(id);
|
|
@@ -9629,21 +10433,21 @@ function parseZaiQuotaPayload(payload, now) {
|
|
|
9629
10433
|
var MINIMAX_STATUS_EXHAUSTED = 2;
|
|
9630
10434
|
var MINIMAX_SHARED_BUCKET = "general";
|
|
9631
10435
|
function parseMiniMaxBucket(value) {
|
|
9632
|
-
if (!
|
|
10436
|
+
if (!isRecord7(value)) return null;
|
|
9633
10437
|
const modelName = typeof value["model_name"] === "string" ? value["model_name"].trim() : "";
|
|
9634
10438
|
if (!modelName) return null;
|
|
9635
10439
|
const instant = (v) => {
|
|
9636
|
-
const n =
|
|
10440
|
+
const n = finiteNumber5(v);
|
|
9637
10441
|
return n !== void 0 && n > 1e9 ? n > 1e12 ? n : n * 1e3 : void 0;
|
|
9638
10442
|
};
|
|
9639
10443
|
return {
|
|
9640
10444
|
modelName,
|
|
9641
10445
|
intervalEnd: instant(value["end_time"]),
|
|
9642
|
-
intervalRemainingPercent:
|
|
9643
|
-
intervalStatus:
|
|
10446
|
+
intervalRemainingPercent: finiteNumber5(value["current_interval_remaining_percent"]),
|
|
10447
|
+
intervalStatus: finiteNumber5(value["current_interval_status"]),
|
|
9644
10448
|
weeklyEnd: instant(value["weekly_end_time"]),
|
|
9645
|
-
weeklyRemainingPercent:
|
|
9646
|
-
weeklyStatus:
|
|
10449
|
+
weeklyRemainingPercent: finiteNumber5(value["current_weekly_remaining_percent"]),
|
|
10450
|
+
weeklyStatus: finiteNumber5(value["current_weekly_status"])
|
|
9647
10451
|
};
|
|
9648
10452
|
}
|
|
9649
10453
|
function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, status, now) {
|
|
@@ -9656,14 +10460,14 @@ function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, s
|
|
|
9656
10460
|
usedPercent,
|
|
9657
10461
|
...windowMinutes !== void 0 ? { windowMinutes } : {},
|
|
9658
10462
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9659
|
-
remainingSeconds:
|
|
10463
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9660
10464
|
state: usedPercent !== null ? "fresh" : "unavailable"
|
|
9661
10465
|
};
|
|
9662
10466
|
}
|
|
9663
10467
|
function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
9664
|
-
if (!
|
|
10468
|
+
if (!isRecord7(payload)) return null;
|
|
9665
10469
|
const baseResp = payload["base_resp"];
|
|
9666
|
-
if (!
|
|
10470
|
+
if (!isRecord7(baseResp) || baseResp["status_code"] !== 0) return null;
|
|
9667
10471
|
const buckets = Array.isArray(payload["model_remains"]) ? payload["model_remains"] : [];
|
|
9668
10472
|
let general = null;
|
|
9669
10473
|
for (const raw of buckets) {
|
|
@@ -9687,7 +10491,7 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
|
9687
10491
|
minimaxWindow(
|
|
9688
10492
|
"seven-day",
|
|
9689
10493
|
"7 days",
|
|
9690
|
-
Math.round(WEEK_MS /
|
|
10494
|
+
Math.round(WEEK_MS / MINUTE_MS3),
|
|
9691
10495
|
general.weeklyEnd,
|
|
9692
10496
|
general.weeklyRemainingPercent,
|
|
9693
10497
|
general.weeklyStatus,
|
|
@@ -9696,15 +10500,15 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
|
9696
10500
|
];
|
|
9697
10501
|
}
|
|
9698
10502
|
function parseUmansUsagePayload(payload, now) {
|
|
9699
|
-
if (!
|
|
9700
|
-
const limits =
|
|
9701
|
-
const requests = limits &&
|
|
9702
|
-
const usage =
|
|
9703
|
-
const window =
|
|
9704
|
-
const hardCap =
|
|
9705
|
-
const softLimit =
|
|
9706
|
-
const requestsInWindow =
|
|
9707
|
-
const weightedInWindow =
|
|
10503
|
+
if (!isRecord7(payload)) return null;
|
|
10504
|
+
const limits = isRecord7(payload["limits"]) ? payload["limits"] : void 0;
|
|
10505
|
+
const requests = limits && isRecord7(limits["requests"]) ? limits["requests"] : void 0;
|
|
10506
|
+
const usage = isRecord7(payload["usage"]) ? payload["usage"] : void 0;
|
|
10507
|
+
const window = isRecord7(payload["window"]) ? payload["window"] : void 0;
|
|
10508
|
+
const hardCap = finiteNumber5(requests?.["hard_cap"]);
|
|
10509
|
+
const softLimit = finiteNumber5(requests?.["limit"]);
|
|
10510
|
+
const requestsInWindow = finiteNumber5(usage?.["requests_in_window"]);
|
|
10511
|
+
const weightedInWindow = finiteNumber5(usage?.["weighted_in_window"]);
|
|
9708
10512
|
const resetsAt = isoInstant3(window?.["resets_at"]);
|
|
9709
10513
|
let usedPercent = null;
|
|
9710
10514
|
if (hardCap !== void 0 && hardCap > 0 && requestsInWindow !== void 0) {
|
|
@@ -9721,19 +10525,19 @@ function parseUmansUsagePayload(payload, now) {
|
|
|
9721
10525
|
usedPercent,
|
|
9722
10526
|
windowMinutes: 5 * 60,
|
|
9723
10527
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9724
|
-
remainingSeconds:
|
|
10528
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9725
10529
|
state: "fresh"
|
|
9726
10530
|
}
|
|
9727
10531
|
];
|
|
9728
10532
|
}
|
|
9729
10533
|
function parseSyntheticQuotasPayload(payload, now) {
|
|
9730
|
-
if (!
|
|
9731
|
-
const fiveHour =
|
|
9732
|
-
const weekly =
|
|
10534
|
+
if (!isRecord7(payload)) return null;
|
|
10535
|
+
const fiveHour = isRecord7(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
|
|
10536
|
+
const weekly = isRecord7(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
|
|
9733
10537
|
const windows = [];
|
|
9734
10538
|
if (fiveHour) {
|
|
9735
|
-
const max =
|
|
9736
|
-
const remaining =
|
|
10539
|
+
const max = finiteNumber5(fiveHour["max"]);
|
|
10540
|
+
const remaining = finiteNumber5(fiveHour["remaining"]);
|
|
9737
10541
|
const usedPercent = max !== void 0 && max > 0 && remaining !== void 0 ? Math.round(Math.min(100, (max - remaining) / max * 100) * 10) / 10 : null;
|
|
9738
10542
|
const resetsAt = isoInstant3(fiveHour["nextTickAt"]);
|
|
9739
10543
|
windows.push({
|
|
@@ -9743,12 +10547,12 @@ function parseSyntheticQuotasPayload(payload, now) {
|
|
|
9743
10547
|
usedPercent,
|
|
9744
10548
|
windowMinutes: 5 * 60,
|
|
9745
10549
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9746
|
-
remainingSeconds:
|
|
10550
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9747
10551
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
9748
10552
|
});
|
|
9749
10553
|
}
|
|
9750
10554
|
if (weekly) {
|
|
9751
|
-
const percentRemaining =
|
|
10555
|
+
const percentRemaining = finiteNumber5(weekly["percentRemaining"]);
|
|
9752
10556
|
const usedPercent = percentRemaining !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - percentRemaining)) * 10) / 10 : null;
|
|
9753
10557
|
const resetsAt = isoInstant3(weekly["nextRegenAt"]);
|
|
9754
10558
|
windows.push({
|
|
@@ -9758,12 +10562,42 @@ function parseSyntheticQuotasPayload(payload, now) {
|
|
|
9758
10562
|
usedPercent,
|
|
9759
10563
|
windowMinutes: 7 * 24 * 60,
|
|
9760
10564
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9761
|
-
remainingSeconds:
|
|
10565
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9762
10566
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
9763
10567
|
});
|
|
9764
10568
|
}
|
|
9765
10569
|
return windows.length > 0 ? windows : null;
|
|
9766
10570
|
}
|
|
10571
|
+
var CLINE_WINDOW_CONFIG = {
|
|
10572
|
+
five_hour: { id: "five-hour", label: "5 hours", minutes: 5 * 60 },
|
|
10573
|
+
weekly: { id: "seven-day", label: "7 days", minutes: 7 * 24 * 60 },
|
|
10574
|
+
monthly: { id: "thirty-day", label: "30 days", minutes: 30 * 24 * 60 }
|
|
10575
|
+
};
|
|
10576
|
+
function parseClinePassUsageLimitsPayload(payload, now) {
|
|
10577
|
+
if (!isRecord7(payload)) return null;
|
|
10578
|
+
const data = isRecord7(payload["data"]) ? payload["data"] : payload;
|
|
10579
|
+
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
10580
|
+
const windows = [];
|
|
10581
|
+
for (const raw of limits) {
|
|
10582
|
+
if (!isRecord7(raw)) continue;
|
|
10583
|
+
const config = CLINE_WINDOW_CONFIG[typeof raw["type"] === "string" ? raw["type"] : ""];
|
|
10584
|
+
if (!config) continue;
|
|
10585
|
+
const usedPercent = finitePercent4(raw["percentUsed"]);
|
|
10586
|
+
if (usedPercent === null) continue;
|
|
10587
|
+
const resetsAt = isoInstant3(raw["resetsAt"]);
|
|
10588
|
+
windows.push({
|
|
10589
|
+
id: config.id,
|
|
10590
|
+
label: config.label,
|
|
10591
|
+
scope: "all",
|
|
10592
|
+
usedPercent,
|
|
10593
|
+
windowMinutes: config.minutes,
|
|
10594
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
10595
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
10596
|
+
state: "fresh"
|
|
10597
|
+
});
|
|
10598
|
+
}
|
|
10599
|
+
return windows.length > 0 ? windows : null;
|
|
10600
|
+
}
|
|
9767
10601
|
|
|
9768
10602
|
// src/allowance/ProviderKeyQuotaService.ts
|
|
9769
10603
|
function parseQuotaPayload(adapter, payload, now) {
|
|
@@ -9776,6 +10610,8 @@ function parseQuotaPayload(adapter, payload, now) {
|
|
|
9776
10610
|
return parseUmansUsagePayload(payload, now);
|
|
9777
10611
|
case "synthetic":
|
|
9778
10612
|
return parseSyntheticQuotasPayload(payload, now);
|
|
10613
|
+
case "cline-pass":
|
|
10614
|
+
return parseClinePassUsageLimitsPayload(payload, now);
|
|
9779
10615
|
}
|
|
9780
10616
|
}
|
|
9781
10617
|
var PROVIDER_KEY_QUOTA_CACHE_MS = 5 * 6e4;
|
|
@@ -9795,7 +10631,7 @@ function rowKeyEntries(row) {
|
|
|
9795
10631
|
return [];
|
|
9796
10632
|
}
|
|
9797
10633
|
var ProviderKeyQuotaService = class {
|
|
9798
|
-
constructor(box, fetchImpl = (url, init) =>
|
|
10634
|
+
constructor(box, fetchImpl = (url, init) => fetchUpstream8(url, init, { redactBodies: true }), now = Date.now) {
|
|
9799
10635
|
this.box = box;
|
|
9800
10636
|
this.fetchImpl = fetchImpl;
|
|
9801
10637
|
this.now = now;
|
|
@@ -9857,7 +10693,10 @@ var ProviderKeyQuotaService = class {
|
|
|
9857
10693
|
headers: {
|
|
9858
10694
|
Authorization: providerKeyQuotaAuthHeader(adapter, key),
|
|
9859
10695
|
Accept: "application/json",
|
|
9860
|
-
"Content-Type": "application/json"
|
|
10696
|
+
"Content-Type": "application/json",
|
|
10697
|
+
// The row's static identity headers ride along — the Cline usage
|
|
10698
|
+
// endpoint sits behind the SAME client-identity 403 gate as inference.
|
|
10699
|
+
...mergeExtraHeaders2({}, row.extraHeaders)
|
|
9861
10700
|
},
|
|
9862
10701
|
signal: AbortSignal.timeout(15e3)
|
|
9863
10702
|
});
|
|
@@ -14629,6 +15468,10 @@ function toLLMProvider(row) {
|
|
|
14629
15468
|
// `parseProviderInput`), so customizations are preserved (the row value wins).
|
|
14630
15469
|
apiModes: row.apiModes,
|
|
14631
15470
|
selectedApiModeId: row.selectedApiModeId,
|
|
15471
|
+
// Static extra request headers ride along verbatim (load-guarded — no
|
|
15472
|
+
// auth/content names); core's `getProviderHeaders` merges them into every
|
|
15473
|
+
// BYO request, and the same-format relay path inherits that funnel.
|
|
15474
|
+
extraHeaders: row.extraHeaders,
|
|
14632
15475
|
// Official-Anthropic signature handling only matters for the Anthropic
|
|
14633
15476
|
// ingress (deferred → 502); leave it off for the BYO transform path.
|
|
14634
15477
|
isOfficial: false
|
|
@@ -16578,12 +17421,13 @@ import { existsSync as existsSync24, mkdirSync as mkdirSync6, readFileSync as re
|
|
|
16578
17421
|
import { dirname as dirname15 } from "path";
|
|
16579
17422
|
import { getSharedAccountHealth as getSharedAccountHealth2 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
16580
17423
|
import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling3 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
|
|
16581
|
-
import { fetchUpstream as
|
|
17424
|
+
import { fetchUpstream as fetchUpstream9 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
16582
17425
|
import { getSharedIdentityStore as getSharedIdentityStore2 } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
|
|
16583
17426
|
import {
|
|
16584
17427
|
claudeOAuth as claudeOAuth2,
|
|
16585
17428
|
codexOAuth as codexOAuth2,
|
|
16586
17429
|
geminiOAuth as geminiOAuth2,
|
|
17430
|
+
grokOAuth as grokOAuth2,
|
|
16587
17431
|
kimiOAuth as kimiOAuth2
|
|
16588
17432
|
} from "@omnicross/subscriptions";
|
|
16589
17433
|
|
|
@@ -16733,7 +17577,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16733
17577
|
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
16734
17578
|
*/
|
|
16735
17579
|
buildRefreshFetch(providerId, accountId) {
|
|
16736
|
-
return this.fetchImpl ?? ((url, init) =>
|
|
17580
|
+
return this.fetchImpl ?? ((url, init) => fetchUpstream9(url, init, { providerId, accountId, redactBodies: true }));
|
|
16737
17581
|
}
|
|
16738
17582
|
/**
|
|
16739
17583
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -16774,7 +17618,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16774
17618
|
* other hot reads. Never returns token material.
|
|
16775
17619
|
*/
|
|
16776
17620
|
getAccountProxy(providerId, accountId) {
|
|
16777
|
-
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi") {
|
|
17621
|
+
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi" && providerId !== "grok" && providerId !== "copilot") {
|
|
16778
17622
|
return void 0;
|
|
16779
17623
|
}
|
|
16780
17624
|
return getAccountProxy(this.readConfig(), providerId, accountId);
|
|
@@ -16793,7 +17637,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16793
17637
|
const fingerprintOn = identityStore.isEnabled();
|
|
16794
17638
|
const now = Date.now();
|
|
16795
17639
|
const out = {};
|
|
16796
|
-
for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi"]) {
|
|
17640
|
+
for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi", "grok", "copilot"]) {
|
|
16797
17641
|
const sanitized = sanitizeAccounts(config, provider);
|
|
16798
17642
|
if (sanitized.length === 0) continue;
|
|
16799
17643
|
for (const account of sanitized) {
|
|
@@ -16992,6 +17836,66 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
16992
17836
|
}
|
|
16993
17837
|
});
|
|
16994
17838
|
}
|
|
17839
|
+
/**
|
|
17840
|
+
* Refresh the Grok (xAI SuperGrok) OAuth access token. The token endpoint is
|
|
17841
|
+
* resolved through OIDC discovery on every refresh (process-cached 1h by the
|
|
17842
|
+
* flow module) so a rotated endpoint document is picked up without a daemon
|
|
17843
|
+
* restart. HONEST `false` when no refresh_token.
|
|
17844
|
+
*/
|
|
17845
|
+
async refreshGrokToken() {
|
|
17846
|
+
return this.coalesce("grok:active", async () => {
|
|
17847
|
+
const config = this.readConfig();
|
|
17848
|
+
const active = getActiveAccount(config, "grok");
|
|
17849
|
+
const grok = active?.tokens;
|
|
17850
|
+
if (!active || !grok?.refreshToken) return false;
|
|
17851
|
+
const capturedId = active.id;
|
|
17852
|
+
this.materializeMigration(config);
|
|
17853
|
+
const refreshFetch = this.buildRefreshFetch("grok", capturedId);
|
|
17854
|
+
try {
|
|
17855
|
+
const tokenEndpoint = await grokOAuth2.resolveGrokTokenEndpoint(refreshFetch);
|
|
17856
|
+
const result = await grokOAuth2.refreshGrokAccessToken(grok.refreshToken, tokenEndpoint, refreshFetch);
|
|
17857
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
17858
|
+
const next = {
|
|
17859
|
+
...grok,
|
|
17860
|
+
accessToken: result.accessToken,
|
|
17861
|
+
refreshToken: result.refreshToken,
|
|
17862
|
+
expiresAt,
|
|
17863
|
+
status: "authorized",
|
|
17864
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17865
|
+
errorMessage: void 0,
|
|
17866
|
+
syncWarning: void 0
|
|
17867
|
+
};
|
|
17868
|
+
this.writeBackById("grok", capturedId, next);
|
|
17869
|
+
return true;
|
|
17870
|
+
} catch (error) {
|
|
17871
|
+
this.markExpiredById("grok", capturedId, grok, error);
|
|
17872
|
+
return false;
|
|
17873
|
+
}
|
|
17874
|
+
});
|
|
17875
|
+
}
|
|
17876
|
+
/**
|
|
17877
|
+
* "Refresh" a GitHub Copilot token — there is nothing to refresh (ghu_
|
|
17878
|
+
* tokens are long-lived with no exchange endpoint). A call here means the
|
|
17879
|
+
* strategy saw a 401 (the token was revoked); mark the account `expired`
|
|
17880
|
+
* with a re-authenticate message and return `false` (the proxy then declines
|
|
17881
|
+
* the retry instead of looping on a dead token).
|
|
17882
|
+
*/
|
|
17883
|
+
async refreshCopilotToken() {
|
|
17884
|
+
return this.coalesce("copilot:active", async () => {
|
|
17885
|
+
const config = this.readConfig();
|
|
17886
|
+
const active = getActiveAccount(config, "copilot");
|
|
17887
|
+
const copilot = active?.tokens;
|
|
17888
|
+
if (!active || !copilot?.accessToken) return false;
|
|
17889
|
+
this.materializeMigration(config);
|
|
17890
|
+
this.markExpiredById(
|
|
17891
|
+
"copilot",
|
|
17892
|
+
active.id,
|
|
17893
|
+
copilot,
|
|
17894
|
+
new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account")
|
|
17895
|
+
);
|
|
17896
|
+
return false;
|
|
17897
|
+
});
|
|
17898
|
+
}
|
|
16995
17899
|
/**
|
|
16996
17900
|
* Refresh a SPECIFIC managed account by id (background scheduler sweep and
|
|
16997
17901
|
* account-pool resolution). It uses only that account's stored refresh
|
|
@@ -17044,7 +17948,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
17044
17948
|
}
|
|
17045
17949
|
const oauth = account.tokens;
|
|
17046
17950
|
if (!oauth.accessToken) return null;
|
|
17047
|
-
if (providerId === "codex" || providerId === "gemini" || providerId === "kimi") {
|
|
17951
|
+
if (providerId === "codex" || providerId === "gemini" || providerId === "kimi" || providerId === "grok" || providerId === "copilot") {
|
|
17048
17952
|
const expiresAtMs = oauth.expiresAt ? Date.parse(oauth.expiresAt) : 0;
|
|
17049
17953
|
const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - ACCOUNT_REFRESH_LEAD_MS;
|
|
17050
17954
|
if (expiringSoon && oauth.refreshToken) {
|
|
@@ -17148,6 +18052,18 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
17148
18052
|
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
17149
18053
|
};
|
|
17150
18054
|
}
|
|
18055
|
+
if (provider === "grok") {
|
|
18056
|
+
const tokenEndpoint = await grokOAuth2.resolveGrokTokenEndpoint(refreshFetch);
|
|
18057
|
+
const r2 = await grokOAuth2.refreshGrokAccessToken(refreshToken, tokenEndpoint, refreshFetch);
|
|
18058
|
+
return {
|
|
18059
|
+
accessToken: r2.accessToken,
|
|
18060
|
+
refreshToken: r2.refreshToken,
|
|
18061
|
+
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
18062
|
+
};
|
|
18063
|
+
}
|
|
18064
|
+
if (provider === "copilot") {
|
|
18065
|
+
throw new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account");
|
|
18066
|
+
}
|
|
17151
18067
|
const flow = provider === "claude" ? claudeOAuth2 : provider === "codex" ? codexOAuth2 : geminiOAuth2;
|
|
17152
18068
|
const r = await flow.refreshAccessToken(refreshToken, refreshFetch);
|
|
17153
18069
|
return {
|
|
@@ -17391,7 +18307,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
17391
18307
|
};
|
|
17392
18308
|
|
|
17393
18309
|
// src/AccountHealthProbeScheduler.ts
|
|
17394
|
-
import { fetchUpstream as
|
|
18310
|
+
import { fetchUpstream as fetchUpstream10 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
17395
18311
|
|
|
17396
18312
|
// src/probe/CodexGenerationProbe.ts
|
|
17397
18313
|
import {
|
|
@@ -17537,7 +18453,16 @@ var PROVIDER_PROBE_PLANS = {
|
|
|
17537
18453
|
// Kimi's `GET /coding/v1/usages` is a verified FREE authed GET (the allowance
|
|
17538
18454
|
// collector uses it), but the probe path also needs the fingerprint headers —
|
|
17539
18455
|
// keep the probe local until the collector covers the health surface.
|
|
17540
|
-
kimi: { kind: "local" }
|
|
18456
|
+
kimi: { kind: "local" },
|
|
18457
|
+
// Grok's billing proxy is a verified FREE authed GET (the allowance collector
|
|
18458
|
+
// uses it) but it REJECTS non-OAuth credentials and sits on a separate host
|
|
18459
|
+
// with its own product-gate header — keep the probe local, the collector
|
|
18460
|
+
// owns the health surface.
|
|
18461
|
+
grok: { kind: "local" },
|
|
18462
|
+
// The Copilot quota endpoint (copilot_internal/user) is a verified FREE
|
|
18463
|
+
// authed GET but lives on api.github.com with its own auth dialect and a
|
|
18464
|
+
// monthly-only window — the allowance collector owns the health surface.
|
|
18465
|
+
copilot: { kind: "local" }
|
|
17541
18466
|
};
|
|
17542
18467
|
function probePlanFor(providerId) {
|
|
17543
18468
|
return PROVIDER_PROBE_PLANS[providerId] ?? { kind: "local" };
|
|
@@ -17559,7 +18484,7 @@ var AccountHealthProbeScheduler = class {
|
|
|
17559
18484
|
this.logger = logger;
|
|
17560
18485
|
this.config = config;
|
|
17561
18486
|
this.now = opts.now ?? Date.now;
|
|
17562
|
-
this.fetchImpl = opts.fetchImpl ??
|
|
18487
|
+
this.fetchImpl = opts.fetchImpl ?? fetchUpstream10;
|
|
17563
18488
|
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
17564
18489
|
this.planFor = opts.planFor ?? probePlanFor;
|
|
17565
18490
|
}
|
|
@@ -18116,7 +19041,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
18116
19041
|
}
|
|
18117
19042
|
|
|
18118
19043
|
// src/audit/AuditPruneSweeper.ts
|
|
18119
|
-
var
|
|
19044
|
+
var DAY_MS4 = 24 * 60 * 6e4;
|
|
18120
19045
|
var SWEEP_INTERVAL_MS2 = 60 * 6e4;
|
|
18121
19046
|
var ARCHIVE_BATCH = 64;
|
|
18122
19047
|
var AuditPruneSweeper = class {
|
|
@@ -18180,7 +19105,7 @@ var AuditPruneSweeper = class {
|
|
|
18180
19105
|
this.sweeping = true;
|
|
18181
19106
|
try {
|
|
18182
19107
|
if (!existsSync26(this.auditDir)) return 0;
|
|
18183
|
-
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) *
|
|
19108
|
+
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS4;
|
|
18184
19109
|
let removed = 0;
|
|
18185
19110
|
for (const name of readdirSync8(this.auditDir)) {
|
|
18186
19111
|
const dateMs = auditFileDateMs(name);
|
|
@@ -18437,7 +19362,7 @@ async function closeAll(writers) {
|
|
|
18437
19362
|
// src/usage/UsagePruneSweeper.ts
|
|
18438
19363
|
import { unlink as unlink3 } from "fs/promises";
|
|
18439
19364
|
import { join as join24 } from "path";
|
|
18440
|
-
var
|
|
19365
|
+
var DAY_MS5 = 24 * 60 * 6e4;
|
|
18441
19366
|
var SWEEP_INTERVAL_MS3 = 60 * 6e4;
|
|
18442
19367
|
var DEFAULT_USAGE_RETENTION_DAYS = 90;
|
|
18443
19368
|
var UsagePruneSweeper = class {
|
|
@@ -18494,7 +19419,7 @@ var UsagePruneSweeper = class {
|
|
|
18494
19419
|
this.sweeping = true;
|
|
18495
19420
|
try {
|
|
18496
19421
|
const retentionDays = this.config.retentionDays ?? DEFAULT_USAGE_RETENTION_DAYS;
|
|
18497
|
-
const cutoff = this.todayMidnight() - (retentionDays - 1) *
|
|
19422
|
+
const cutoff = this.todayMidnight() - (retentionDays - 1) * DAY_MS5;
|
|
18498
19423
|
let removed = 0;
|
|
18499
19424
|
for (const entry of await listUsageDays(this.usageDir)) {
|
|
18500
19425
|
if (!entry.hasShard) continue;
|
|
@@ -18722,7 +19647,7 @@ var AuditWriter = class {
|
|
|
18722
19647
|
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync8 } from "fs";
|
|
18723
19648
|
import { createHmac as createHmac5 } from "crypto";
|
|
18724
19649
|
import { join as join27 } from "path";
|
|
18725
|
-
import { fetchUpstream as
|
|
19650
|
+
import { fetchUpstream as fetchUpstream11 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
18726
19651
|
|
|
18727
19652
|
// src/billing/billingFiles.ts
|
|
18728
19653
|
var BILLING_FILE_RE = /^billing-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
|
|
@@ -18745,7 +19670,7 @@ var BillingPublisher = class {
|
|
|
18745
19670
|
constructor(billingDir, logger, opts = {}) {
|
|
18746
19671
|
this.billingDir = billingDir;
|
|
18747
19672
|
this.logger = logger;
|
|
18748
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) =>
|
|
19673
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream11(url, init));
|
|
18749
19674
|
this.defer = opts.defer ?? ((fn) => setTimeout(fn, 0));
|
|
18750
19675
|
this.timeoutMs = opts.timeoutMs ?? BILLING_POST_TIMEOUT_MS;
|
|
18751
19676
|
this.now = opts.now ?? Date.now;
|
|
@@ -18995,7 +19920,7 @@ var BillingRetrySweeper = class {
|
|
|
18995
19920
|
// src/TokenRefreshScheduler.ts
|
|
18996
19921
|
var REFRESH_LEAD_MS2 = 5 * 6e4;
|
|
18997
19922
|
var SWEEP_INTERVAL_MS5 = 6e4;
|
|
18998
|
-
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
|
|
19923
|
+
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
|
|
18999
19924
|
var TokenRefreshScheduler = class {
|
|
19000
19925
|
constructor(store, logger, intervalMs = SWEEP_INTERVAL_MS5, leadMs = REFRESH_LEAD_MS2) {
|
|
19001
19926
|
this.store = store;
|
|
@@ -19080,6 +20005,12 @@ var TokenRefreshScheduler = class {
|
|
|
19080
20005
|
return this.store.refreshGeminiToken();
|
|
19081
20006
|
case "kimi":
|
|
19082
20007
|
return this.store.refreshKimiToken();
|
|
20008
|
+
case "grok":
|
|
20009
|
+
return this.store.refreshGrokToken();
|
|
20010
|
+
// ghu_ tokens never near-expire (far-future expiresAt), so the sweep
|
|
20011
|
+
// never reaches this — the branch exists for union totality.
|
|
20012
|
+
case "copilot":
|
|
20013
|
+
return this.store.refreshCopilotToken();
|
|
19083
20014
|
}
|
|
19084
20015
|
}
|
|
19085
20016
|
};
|
|
@@ -19158,7 +20089,7 @@ function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
|
19158
20089
|
|
|
19159
20090
|
// src/webhook/WebhookDispatcher.ts
|
|
19160
20091
|
import { createHmac as createHmac6 } from "crypto";
|
|
19161
|
-
import { fetchUpstream as
|
|
20092
|
+
import { fetchUpstream as fetchUpstream12 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
19162
20093
|
var WEBHOOK_MAX_ATTEMPTS = 3;
|
|
19163
20094
|
var WEBHOOK_QUEUE_MAX = 1e3;
|
|
19164
20095
|
var WEBHOOK_SEND_TIMEOUT_MS = 1e4;
|
|
@@ -19178,7 +20109,7 @@ var WebhookDispatcher = class {
|
|
|
19178
20109
|
sleep;
|
|
19179
20110
|
now;
|
|
19180
20111
|
constructor(opts = {}) {
|
|
19181
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) =>
|
|
20112
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => fetchUpstream12(url, init));
|
|
19182
20113
|
this.logger = opts.logger;
|
|
19183
20114
|
this.maxAttempts = opts.maxAttempts ?? WEBHOOK_MAX_ATTEMPTS;
|
|
19184
20115
|
this.queueMax = opts.queueMax ?? WEBHOOK_QUEUE_MAX;
|
|
@@ -19264,8 +20195,8 @@ var WebhookDispatcher = class {
|
|
|
19264
20195
|
signal: AbortSignal.timeout(this.timeoutMs)
|
|
19265
20196
|
});
|
|
19266
20197
|
return res.ok ? { ok: true, status: res.status } : { ok: false, status: res.status };
|
|
19267
|
-
} catch (
|
|
19268
|
-
return { ok: false, error:
|
|
20198
|
+
} catch (err8) {
|
|
20199
|
+
return { ok: false, error: err8 instanceof Error ? err8.message : String(err8) };
|
|
19269
20200
|
}
|
|
19270
20201
|
}
|
|
19271
20202
|
/**
|
|
@@ -19402,7 +20333,7 @@ function buildDaemon(config, paths) {
|
|
|
19402
20333
|
setSecretBox(secretBox3);
|
|
19403
20334
|
setSecretBox2(secretBox3);
|
|
19404
20335
|
const decryptedConfig = decryptConfigSecrets(config, secretBox3);
|
|
19405
|
-
const accountAllowanceStore = new
|
|
20336
|
+
const accountAllowanceStore = new AccountAllowanceStore8(
|
|
19406
20337
|
Date.now,
|
|
19407
20338
|
void 0,
|
|
19408
20339
|
new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
|
|
@@ -19464,7 +20395,7 @@ function buildDaemon(config, paths) {
|
|
|
19464
20395
|
const pricingEngine = new PricingEngine(pricingStore, logger, {
|
|
19465
20396
|
// Catalog egress follows the same global/env proxy policy as every other
|
|
19466
20397
|
// daemon upstream call; no provider/account override applies here.
|
|
19467
|
-
fetchImpl: ((input, init) =>
|
|
20398
|
+
fetchImpl: ((input, init) => fetchUpstream13(String(input), init ?? {}))
|
|
19468
20399
|
});
|
|
19469
20400
|
const pricingRefreshScheduler = new PricingRefreshScheduler(
|
|
19470
20401
|
pricingEngine,
|
|
@@ -19749,7 +20680,7 @@ function buildDaemon(config, paths) {
|
|
|
19749
20680
|
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
19750
20681
|
// excluded from the upstream trace, so a failing login left no evidence.
|
|
19751
20682
|
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
19752
|
-
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) =>
|
|
20683
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => fetchUpstream13(url, init, { providerId, redactBodies: true }),
|
|
19753
20684
|
subscriptionAccountAppender: credentialStore,
|
|
19754
20685
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
19755
20686
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -19761,6 +20692,9 @@ function buildDaemon(config, paths) {
|
|
|
19761
20692
|
// paste; the app shows the verification URL + user code and polls the
|
|
19762
20693
|
// token-free status). Token captured + persisted daemon-side.
|
|
19763
20694
|
kimiSessions: new CodexOAuthSessionStore(),
|
|
20695
|
+
// Grok interactive OAuth — the same async DEVICE-CODE shape as kimi.
|
|
20696
|
+
grokSessions: new CodexOAuthSessionStore(),
|
|
20697
|
+
copilotSessions: new CodexOAuthSessionStore(),
|
|
19764
20698
|
// Migration pack (app-parity child 6, design D2/D3) — the concrete credential
|
|
19765
20699
|
// store provides BOTH the full DECRYPTED read (`getFullConfig`, export) and
|
|
19766
20700
|
// the multi-account append (`appendProviderAccount`, import re-encrypts at-
|
|
@@ -19819,7 +20753,7 @@ function buildDaemon(config, paths) {
|
|
|
19819
20753
|
});
|
|
19820
20754
|
const webhookDispatcher = new WebhookDispatcher({
|
|
19821
20755
|
logger,
|
|
19822
|
-
fetchImpl: (url, init) =>
|
|
20756
|
+
fetchImpl: (url, init) => fetchUpstream13(url, init)
|
|
19823
20757
|
});
|
|
19824
20758
|
setWebhookRuntime(webhookDispatcher, getSharedAccountHealth4());
|
|
19825
20759
|
const auditWriter = new AuditWriter(auditDir, logger);
|
|
@@ -20133,11 +21067,11 @@ async function runLiveProbe(url, key, fetchImpl = fetch) {
|
|
|
20133
21067
|
status: res.status,
|
|
20134
21068
|
estimateHeader: res.headers.get("x-omnicross-count-estimate")
|
|
20135
21069
|
};
|
|
20136
|
-
} catch (
|
|
21070
|
+
} catch (err8) {
|
|
20137
21071
|
return {
|
|
20138
21072
|
status: null,
|
|
20139
21073
|
estimateHeader: null,
|
|
20140
|
-
error:
|
|
21074
|
+
error: err8 instanceof Error ? err8.message : String(err8)
|
|
20141
21075
|
};
|
|
20142
21076
|
}
|
|
20143
21077
|
}
|
|
@@ -20547,9 +21481,9 @@ async function runLaunch(argv, deps) {
|
|
|
20547
21481
|
await daemon.llmConfig.ready();
|
|
20548
21482
|
await daemon.migrateUsageStore();
|
|
20549
21483
|
await daemon.providerProxy.start();
|
|
20550
|
-
} catch (
|
|
21484
|
+
} catch (err8) {
|
|
20551
21485
|
await shutdownLaunchDaemon(daemon);
|
|
20552
|
-
throw
|
|
21486
|
+
throw err8;
|
|
20553
21487
|
}
|
|
20554
21488
|
let launch;
|
|
20555
21489
|
try {
|
|
@@ -20557,9 +21491,9 @@ async function runLaunch(argv, deps) {
|
|
|
20557
21491
|
providerId: values.provider,
|
|
20558
21492
|
model: values.model
|
|
20559
21493
|
});
|
|
20560
|
-
} catch (
|
|
21494
|
+
} catch (err8) {
|
|
20561
21495
|
await shutdownLaunchDaemon(daemon);
|
|
20562
|
-
throw
|
|
21496
|
+
throw err8;
|
|
20563
21497
|
}
|
|
20564
21498
|
try {
|
|
20565
21499
|
const plan = buildCliSpawnPlan({
|
|
@@ -20664,9 +21598,9 @@ function spawnCliInherit(plan) {
|
|
|
20664
21598
|
process.removeListener("SIGINT", onSignal);
|
|
20665
21599
|
process.removeListener("SIGTERM", onSignal);
|
|
20666
21600
|
};
|
|
20667
|
-
child.on("error", (
|
|
21601
|
+
child.on("error", (err8) => {
|
|
20668
21602
|
detach();
|
|
20669
|
-
if (
|
|
21603
|
+
if (err8.code === "ENOENT") {
|
|
20670
21604
|
reject(
|
|
20671
21605
|
new Error(
|
|
20672
21606
|
`launch: "${plan.command}" not found on PATH \u2014 install the CLI first.`
|
|
@@ -20674,7 +21608,7 @@ function spawnCliInherit(plan) {
|
|
|
20674
21608
|
);
|
|
20675
21609
|
return;
|
|
20676
21610
|
}
|
|
20677
|
-
reject(
|
|
21611
|
+
reject(err8);
|
|
20678
21612
|
});
|
|
20679
21613
|
child.on("exit", (code, signal) => {
|
|
20680
21614
|
detach();
|
|
@@ -20687,14 +21621,16 @@ function spawnCliInherit(plan) {
|
|
|
20687
21621
|
import { spawn as spawn3 } from "child_process";
|
|
20688
21622
|
import { createInterface as createInterface2 } from "readline";
|
|
20689
21623
|
import { parseArgs as parseArgs7 } from "util";
|
|
20690
|
-
import { fetchUpstream as
|
|
21624
|
+
import { fetchUpstream as fetchUpstream14, setUpstreamProxyResolver as setUpstreamProxyResolver2 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
20691
21625
|
import {
|
|
20692
21626
|
claudeOAuth as claudeOAuth3,
|
|
20693
21627
|
codexOAuth as codexOAuth3,
|
|
21628
|
+
copilotOAuth as copilotOAuth3,
|
|
20694
21629
|
geminiOAuth as geminiOAuth3,
|
|
21630
|
+
grokOAuth as grokOAuth3,
|
|
20695
21631
|
kimiOAuth as kimiOAuth3
|
|
20696
21632
|
} from "@omnicross/subscriptions";
|
|
20697
|
-
var PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
|
|
21633
|
+
var PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
|
|
20698
21634
|
async function runLogin(argv, deps) {
|
|
20699
21635
|
const { values, positionals } = parseArgs7({
|
|
20700
21636
|
args: argv,
|
|
@@ -20702,7 +21638,9 @@ async function runLogin(argv, deps) {
|
|
|
20702
21638
|
config: { type: "string", short: "c" },
|
|
20703
21639
|
"master-key-file": { type: "string" },
|
|
20704
21640
|
// Optional user label for the appended account (multi-account).
|
|
20705
|
-
label: { type: "string" }
|
|
21641
|
+
label: { type: "string" },
|
|
21642
|
+
// Optional GitHub Enterprise domain for `login copilot` (GHE accounts).
|
|
21643
|
+
enterprise: { type: "string" }
|
|
20706
21644
|
},
|
|
20707
21645
|
allowPositionals: true
|
|
20708
21646
|
});
|
|
@@ -20716,11 +21654,17 @@ async function runLogin(argv, deps) {
|
|
|
20716
21654
|
if (!values.config) {
|
|
20717
21655
|
throw new Error("login: --config <path> is required");
|
|
20718
21656
|
}
|
|
21657
|
+
if (values.enterprise !== void 0 && provider !== "copilot") {
|
|
21658
|
+
throw new Error("login: --enterprise is only supported for the copilot provider");
|
|
21659
|
+
}
|
|
21660
|
+
const enterpriseDomain = values.enterprise !== void 0 ? copilotOAuth3.normalizeCopilotEnterpriseDomain(values.enterprise) : void 0;
|
|
20719
21661
|
const resolved = {
|
|
20720
21662
|
openBrowser: deps?.openBrowser ?? openBrowser,
|
|
20721
21663
|
promptPaste: deps?.promptPaste ?? promptPaste,
|
|
20722
21664
|
awaitLoopback: deps?.awaitLoopback ?? ((state) => awaitLoopbackCode(state)),
|
|
20723
21665
|
awaitKimiDevice: deps?.awaitKimiDevice ?? ((fetchImpl) => runKimiDeviceFlow(fetchImpl, resolvedOpenBrowser)),
|
|
21666
|
+
awaitGrokDevice: deps?.awaitGrokDevice ?? ((fetchImpl) => runGrokDeviceFlow(fetchImpl, resolvedOpenBrowser)),
|
|
21667
|
+
awaitCopilotDevice: deps?.awaitCopilotDevice ?? ((fetchImpl, enterpriseUrl) => runCopilotDeviceFlow(fetchImpl, resolvedOpenBrowser, enterpriseUrl)),
|
|
20724
21668
|
tokensFetch: deps?.tokensFetch
|
|
20725
21669
|
};
|
|
20726
21670
|
const resolvedOpenBrowser = resolved.openBrowser;
|
|
@@ -20729,14 +21673,15 @@ async function runLogin(argv, deps) {
|
|
|
20729
21673
|
setUpstreamProxyResolver2(createUpstreamProxyResolver());
|
|
20730
21674
|
try {
|
|
20731
21675
|
const tokensPath = defaultTokensPath(values.config);
|
|
20732
|
-
const exchangeFetch = resolved.tokensFetch ?? ((url, init) =>
|
|
21676
|
+
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => fetchUpstream14(url, init, { providerId: provider, redactBodies: true }));
|
|
20733
21677
|
const store = new JsonSubscriptionCredentialStore(tokensPath, box, exchangeFetch);
|
|
20734
21678
|
const expiresAt = await runProviderLogin(
|
|
20735
21679
|
provider,
|
|
20736
21680
|
store,
|
|
20737
21681
|
resolved,
|
|
20738
21682
|
exchangeFetch,
|
|
20739
|
-
values.label
|
|
21683
|
+
values.label,
|
|
21684
|
+
enterpriseDomain
|
|
20740
21685
|
);
|
|
20741
21686
|
console.info(`Logged in to '${provider}' \u2192 ${tokensPath}`);
|
|
20742
21687
|
console.info(` token: [stored, encrypted] expiresAt: ${expiresAt ?? "n/a"}`);
|
|
@@ -20745,10 +21690,12 @@ async function runLogin(argv, deps) {
|
|
|
20745
21690
|
setUpstreamProxyResolver2(null);
|
|
20746
21691
|
}
|
|
20747
21692
|
}
|
|
20748
|
-
async function runProviderLogin(provider, store, deps, exchangeFetch, label) {
|
|
21693
|
+
async function runProviderLogin(provider, store, deps, exchangeFetch, label, enterpriseUrl) {
|
|
20749
21694
|
if (provider === "codex") return loginCodex(store, deps, exchangeFetch, label);
|
|
20750
21695
|
if (provider === "claude") return loginClaude(store, deps, exchangeFetch, label);
|
|
20751
21696
|
if (provider === "kimi") return loginKimi(store, deps, exchangeFetch, label);
|
|
21697
|
+
if (provider === "grok") return loginGrok(store, deps, exchangeFetch, label);
|
|
21698
|
+
if (provider === "copilot") return loginCopilot(store, deps, exchangeFetch, label, enterpriseUrl);
|
|
20752
21699
|
return loginGemini(store, deps, exchangeFetch, label);
|
|
20753
21700
|
}
|
|
20754
21701
|
async function loginCodex(store, deps, exchangeFetch, label) {
|
|
@@ -20858,6 +21805,90 @@ async function loginKimi(store, deps, exchangeFetch, label) {
|
|
|
20858
21805
|
logMasked("kimi", result.accessToken);
|
|
20859
21806
|
return expiresAt;
|
|
20860
21807
|
}
|
|
21808
|
+
async function runGrokDeviceFlow(exchangeFetch, openBrowserFn) {
|
|
21809
|
+
const tokenEndpoint = await grokOAuth3.resolveGrokTokenEndpoint(exchangeFetch);
|
|
21810
|
+
const authorization = await grokOAuth3.requestGrokDeviceAuthorization(exchangeFetch);
|
|
21811
|
+
const url = authorization.verificationUriComplete ?? authorization.verificationUri;
|
|
21812
|
+
console.info("Open this URL in your browser and approve the request:");
|
|
21813
|
+
console.info(` ${url}`);
|
|
21814
|
+
if (!authorization.verificationUriComplete) {
|
|
21815
|
+
console.info(` Then enter this code: ${authorization.userCode}`);
|
|
21816
|
+
}
|
|
21817
|
+
await openBrowserFn(url).catch(() => false);
|
|
21818
|
+
const result = await grokOAuth3.awaitGrokDeviceToken(authorization, tokenEndpoint, exchangeFetch, {
|
|
21819
|
+
onPending: () => process.stdout.write(".")
|
|
21820
|
+
});
|
|
21821
|
+
console.info("");
|
|
21822
|
+
return {
|
|
21823
|
+
...result,
|
|
21824
|
+
accountId: grokOAuth3.grokAccountIdFromAccessToken(result.accessToken)
|
|
21825
|
+
};
|
|
21826
|
+
}
|
|
21827
|
+
async function loginGrok(store, deps, exchangeFetch, label) {
|
|
21828
|
+
const result = await deps.awaitGrokDevice(exchangeFetch);
|
|
21829
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
21830
|
+
const block = {
|
|
21831
|
+
authMethod: "oauth",
|
|
21832
|
+
status: "authorized",
|
|
21833
|
+
accessToken: result.accessToken,
|
|
21834
|
+
refreshToken: result.refreshToken,
|
|
21835
|
+
expiresAt,
|
|
21836
|
+
...result.accountId ? { accountId: result.accountId } : {},
|
|
21837
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21838
|
+
};
|
|
21839
|
+
await store.appendProviderAccount("grok", block, label);
|
|
21840
|
+
logMasked("grok", result.accessToken);
|
|
21841
|
+
return expiresAt;
|
|
21842
|
+
}
|
|
21843
|
+
async function runCopilotDeviceFlow(exchangeFetch, openBrowserFn, enterpriseUrl) {
|
|
21844
|
+
if (enterpriseUrl) console.info(`Using GitHub Enterprise host: ${enterpriseUrl}`);
|
|
21845
|
+
const authorization = await copilotOAuth3.requestCopilotDeviceAuthorization(exchangeFetch, enterpriseUrl);
|
|
21846
|
+
const url = authorization.verificationUri;
|
|
21847
|
+
console.info("Open this URL in your browser and approve the request:");
|
|
21848
|
+
console.info(` ${url}`);
|
|
21849
|
+
console.info(` Then enter this code: ${authorization.userCode}`);
|
|
21850
|
+
await openBrowserFn(url).catch(() => false);
|
|
21851
|
+
const result = await copilotOAuth3.awaitCopilotDeviceToken(authorization, exchangeFetch, {
|
|
21852
|
+
onPending: () => process.stdout.write("."),
|
|
21853
|
+
...enterpriseUrl ? { enterpriseUrl } : {}
|
|
21854
|
+
});
|
|
21855
|
+
console.info("");
|
|
21856
|
+
const identity = await copilotOAuth3.fetchCopilotIdentity(result.accessToken, exchangeFetch, enterpriseUrl);
|
|
21857
|
+
const apiEndpoint = await copilotOAuth3.discoverCopilotApiEndpoint(result.accessToken, exchangeFetch, enterpriseUrl);
|
|
21858
|
+
console.info("Enabling Copilot models (policy)...");
|
|
21859
|
+
await copilotOAuth3.enableAllCopilotModels(
|
|
21860
|
+
result.accessToken,
|
|
21861
|
+
{ apiEndpoint, ...enterpriseUrl ? { enterpriseUrl } : {} },
|
|
21862
|
+
exchangeFetch
|
|
21863
|
+
);
|
|
21864
|
+
return {
|
|
21865
|
+
accessToken: result.accessToken,
|
|
21866
|
+
expiresIn: Math.floor(copilotOAuth3.COPILOT_FAR_FUTURE_MS / 1e3),
|
|
21867
|
+
...identity,
|
|
21868
|
+
...apiEndpoint ? { apiEndpoint } : {}
|
|
21869
|
+
};
|
|
21870
|
+
}
|
|
21871
|
+
async function loginCopilot(store, deps, exchangeFetch, label, enterpriseUrl) {
|
|
21872
|
+
const result = await deps.awaitCopilotDevice(exchangeFetch, enterpriseUrl);
|
|
21873
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
21874
|
+
const block = {
|
|
21875
|
+
authMethod: "oauth",
|
|
21876
|
+
status: "authorized",
|
|
21877
|
+
accessToken: result.accessToken,
|
|
21878
|
+
// ghu_ tokens have no refresh lifecycle — the same token doubles as the
|
|
21879
|
+
// stored refresh credential so generic refresh paths stay well-formed.
|
|
21880
|
+
refreshToken: result.accessToken,
|
|
21881
|
+
expiresAt,
|
|
21882
|
+
...result.accountId ? { accountId: result.accountId } : {},
|
|
21883
|
+
...result.email ? { email: result.email } : {},
|
|
21884
|
+
...result.apiEndpoint ? { apiEndpoint: result.apiEndpoint } : {},
|
|
21885
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
21886
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21887
|
+
};
|
|
21888
|
+
await store.appendProviderAccount("copilot", block, label);
|
|
21889
|
+
logMasked("copilot", result.accessToken);
|
|
21890
|
+
return expiresAt;
|
|
21891
|
+
}
|
|
20861
21892
|
function isLoginProvider(value) {
|
|
20862
21893
|
return PROVIDERS2.includes(value);
|
|
20863
21894
|
}
|
|
@@ -21563,7 +22594,7 @@ async function main() {
|
|
|
21563
22594
|
process.exitCode = 1;
|
|
21564
22595
|
}
|
|
21565
22596
|
}
|
|
21566
|
-
main().catch((
|
|
21567
|
-
console.error(
|
|
22597
|
+
main().catch((err8) => {
|
|
22598
|
+
console.error(err8 instanceof Error ? err8.message : String(err8));
|
|
21568
22599
|
process.exitCode = 1;
|
|
21569
22600
|
});
|