@omnicross/daemon 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +2787 -359
- package/dist/cli.js +2743 -297
- package/dist/index.cjs +2717 -429
- package/dist/index.d.cts +486 -28
- package/dist/index.d.ts +486 -28
- package/dist/index.js +2583 -284
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -58,26 +58,26 @@ __export(src_exports, {
|
|
|
58
58
|
module.exports = __toCommonJS(src_exports);
|
|
59
59
|
|
|
60
60
|
// src/bootstrap.ts
|
|
61
|
-
var
|
|
61
|
+
var import_node_fs35 = require("fs");
|
|
62
62
|
var import_node_path35 = require("path");
|
|
63
63
|
var import_audit_types = require("@omnicross/contracts/audit-types");
|
|
64
64
|
var import_billing_types = require("@omnicross/contracts/billing-types");
|
|
65
|
-
var
|
|
65
|
+
var import_core7 = require("@omnicross/core");
|
|
66
66
|
var import_GeminiCodeAssistProjectResolver = require("@omnicross/core/auth/GeminiCodeAssistProjectResolver");
|
|
67
67
|
var import_ApiKeyPoolService = require("@omnicross/core/completion/ApiKeyPoolService");
|
|
68
68
|
var import_outbound_api10 = require("@omnicross/core/outbound-api");
|
|
69
69
|
var import_subscriptionRegistryPort = require("@omnicross/core/outbound-api/subscriptionRegistryPort");
|
|
70
70
|
var import_SubscriptionAccountHealth4 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
71
|
-
var
|
|
71
|
+
var import_AccountAllowanceStore9 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
72
72
|
var import_AccountAllowanceScheduling5 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
73
|
-
var
|
|
73
|
+
var import_upstreamFetch15 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
74
74
|
var import_SubscriptionIdentityStore3 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
75
75
|
var import_gemini_code_assist_resolver = require("@omnicross/core/ports/gemini-code-assist-resolver");
|
|
76
76
|
var import_provider_proxy4 = require("@omnicross/core/provider-proxy");
|
|
77
77
|
var import_cli_launcher2 = require("@omnicross/cli-launcher");
|
|
78
78
|
var import_outbound_api11 = require("@omnicross/core/outbound-api");
|
|
79
79
|
var import_usage2 = require("@omnicross/core/usage");
|
|
80
|
-
var
|
|
80
|
+
var import_subscriptions12 = require("@omnicross/subscriptions");
|
|
81
81
|
|
|
82
82
|
// src/admin/accountsCodexOAuth.ts
|
|
83
83
|
var import_node_crypto = __toESM(require("crypto"), 1);
|
|
@@ -184,93 +184,1494 @@ function handleCodexOAuthStatus(sessionId, deps) {
|
|
|
184
184
|
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
// src/admin/accountsKimiOAuth.ts
|
|
188
|
+
var import_subscriptions2 = require("@omnicross/subscriptions");
|
|
189
|
+
function err2(status, message) {
|
|
190
|
+
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
191
|
+
}
|
|
192
|
+
var DEFAULT_KIMI_OAUTH_TTL_MS = 15 * 6e4;
|
|
193
|
+
async function handleKimiOAuthStart(deps) {
|
|
194
|
+
if (deps.kimiSessions.isBusy()) {
|
|
195
|
+
return err2(409, "a kimi sign-in is already in progress \u2014 finish it in the browser or cancel it");
|
|
196
|
+
}
|
|
197
|
+
const fetchImpl = deps.oauthExchangeFetch("kimi");
|
|
198
|
+
const deviceId = import_subscriptions2.kimiOAuth.generateKimiDeviceId();
|
|
199
|
+
const fingerprint = import_subscriptions2.kimiOAuth.kimiFingerprintHeaders(deviceId);
|
|
200
|
+
let authorization;
|
|
201
|
+
try {
|
|
202
|
+
authorization = await import_subscriptions2.kimiOAuth.requestDeviceAuthorization(fetchImpl, fingerprint);
|
|
203
|
+
} catch (e) {
|
|
204
|
+
const reason = e instanceof Error ? e.message : "device authorization failed";
|
|
205
|
+
return err2(502, `kimi device authorization failed: ${reason}`);
|
|
206
|
+
}
|
|
207
|
+
const { sessionId, signal } = deps.kimiSessions.begin();
|
|
208
|
+
void runKimiDevicePoll(sessionId, authorization.deviceCode, deviceId, fingerprint, signal, deps).catch(() => deps.kimiSessions.settle(sessionId, "error", "kimi sign-in failed"));
|
|
209
|
+
return {
|
|
210
|
+
status: 200,
|
|
211
|
+
body: {
|
|
212
|
+
authUrl: authorization.verificationUriComplete ?? authorization.verificationUri,
|
|
213
|
+
userCode: authorization.userCode,
|
|
214
|
+
sessionId
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
async function runKimiDevicePoll(sessionId, deviceCode, deviceId, fingerprint, signal, deps) {
|
|
219
|
+
const fetchImpl = deps.oauthExchangeFetch("kimi");
|
|
220
|
+
const result = await import_subscriptions2.kimiOAuth.awaitDeviceToken(
|
|
221
|
+
{ userCode: "", deviceCode, verificationUri: "" },
|
|
222
|
+
fetchImpl,
|
|
223
|
+
{
|
|
224
|
+
fingerprint,
|
|
225
|
+
deadlineMs: DEFAULT_KIMI_OAUTH_TTL_MS,
|
|
226
|
+
sleep: (ms) => new Promise((resolve10, reject) => {
|
|
227
|
+
const onAbort = () => {
|
|
228
|
+
clearTimeout(timer);
|
|
229
|
+
reject(new Error("login: cancelled"));
|
|
230
|
+
};
|
|
231
|
+
const timer = setTimeout(() => {
|
|
232
|
+
signal.removeEventListener("abort", onAbort);
|
|
233
|
+
resolve10();
|
|
234
|
+
}, ms);
|
|
235
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
);
|
|
239
|
+
const block = {
|
|
240
|
+
authMethod: "oauth",
|
|
241
|
+
status: "authorized",
|
|
242
|
+
accessToken: result.accessToken,
|
|
243
|
+
refreshToken: result.refreshToken,
|
|
244
|
+
expiresAt: new Date(Date.now() + result.expiresIn * 1e3).toISOString(),
|
|
245
|
+
accountId: import_subscriptions2.kimiOAuth.kimiAccountIdFromAccessToken(result.accessToken),
|
|
246
|
+
deviceId,
|
|
247
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
248
|
+
};
|
|
249
|
+
await deps.subscriptionAccountAppender.appendProviderAccount("kimi", block);
|
|
250
|
+
deps.kimiSessions.settle(sessionId, "done");
|
|
251
|
+
}
|
|
252
|
+
function handleKimiOAuthCancel(sessionId, deps) {
|
|
253
|
+
if (!deps.kimiSessions.cancel(sessionId)) return err2(404, "unknown or expired kimi sign-in session");
|
|
254
|
+
return { status: 200, body: { ok: true } };
|
|
255
|
+
}
|
|
256
|
+
function handleKimiOAuthStatus(sessionId, deps) {
|
|
257
|
+
const s = deps.kimiSessions.get(sessionId);
|
|
258
|
+
if (!s) return err2(404, "unknown or expired kimi sign-in session");
|
|
259
|
+
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// src/admin/accountsGrokOAuth.ts
|
|
263
|
+
var import_subscriptions3 = require("@omnicross/subscriptions");
|
|
264
|
+
function err3(status, message) {
|
|
265
|
+
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
266
|
+
}
|
|
267
|
+
var DEFAULT_GROK_OAUTH_TTL_MS = 15 * 6e4;
|
|
268
|
+
async function handleGrokOAuthStart(deps) {
|
|
269
|
+
if (deps.grokSessions.isBusy()) {
|
|
270
|
+
return err3(409, "a grok sign-in is already in progress \u2014 finish it in the browser or cancel it");
|
|
271
|
+
}
|
|
272
|
+
const fetchImpl = deps.oauthExchangeFetch("grok");
|
|
273
|
+
let tokenEndpoint;
|
|
274
|
+
try {
|
|
275
|
+
tokenEndpoint = await import_subscriptions3.grokOAuth.resolveGrokTokenEndpoint(fetchImpl);
|
|
276
|
+
} catch (e) {
|
|
277
|
+
const reason = e instanceof Error ? e.message : "OIDC discovery failed";
|
|
278
|
+
return err3(502, `grok token-endpoint discovery failed: ${reason}`);
|
|
279
|
+
}
|
|
280
|
+
let authorization;
|
|
281
|
+
try {
|
|
282
|
+
authorization = await import_subscriptions3.grokOAuth.requestGrokDeviceAuthorization(fetchImpl);
|
|
283
|
+
} catch (e) {
|
|
284
|
+
const reason = e instanceof Error ? e.message : "device authorization failed";
|
|
285
|
+
return err3(502, `grok device authorization failed: ${reason}`);
|
|
286
|
+
}
|
|
287
|
+
const { sessionId, signal } = deps.grokSessions.begin();
|
|
288
|
+
void runGrokDevicePoll(sessionId, tokenEndpoint, authorization.deviceCode, signal, deps).catch((e) => {
|
|
289
|
+
const reason = e instanceof Error ? e.message : "grok sign-in failed";
|
|
290
|
+
deps.grokSessions.settle(sessionId, "error", reason);
|
|
291
|
+
});
|
|
292
|
+
return {
|
|
293
|
+
status: 200,
|
|
294
|
+
body: {
|
|
295
|
+
authUrl: authorization.verificationUriComplete ?? authorization.verificationUri,
|
|
296
|
+
userCode: authorization.userCode,
|
|
297
|
+
sessionId
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
async function runGrokDevicePoll(sessionId, tokenEndpoint, deviceCode, signal, deps) {
|
|
302
|
+
const fetchImpl = deps.oauthExchangeFetch("grok");
|
|
303
|
+
const result = await import_subscriptions3.grokOAuth.awaitGrokDeviceToken(
|
|
304
|
+
{ userCode: "", deviceCode, verificationUri: "" },
|
|
305
|
+
tokenEndpoint,
|
|
306
|
+
fetchImpl,
|
|
307
|
+
{
|
|
308
|
+
deadlineMs: DEFAULT_GROK_OAUTH_TTL_MS,
|
|
309
|
+
sleep: (ms) => new Promise((resolve10, reject) => {
|
|
310
|
+
const onAbort = () => {
|
|
311
|
+
clearTimeout(timer);
|
|
312
|
+
reject(new Error("login: cancelled"));
|
|
313
|
+
};
|
|
314
|
+
const timer = setTimeout(() => {
|
|
315
|
+
signal.removeEventListener("abort", onAbort);
|
|
316
|
+
resolve10();
|
|
317
|
+
}, ms);
|
|
318
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
);
|
|
322
|
+
const block = {
|
|
323
|
+
authMethod: "oauth",
|
|
324
|
+
status: "authorized",
|
|
325
|
+
accessToken: result.accessToken,
|
|
326
|
+
refreshToken: result.refreshToken,
|
|
327
|
+
expiresAt: new Date(Date.now() + result.expiresIn * 1e3).toISOString(),
|
|
328
|
+
accountId: import_subscriptions3.grokOAuth.grokAccountIdFromAccessToken(result.accessToken),
|
|
329
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
330
|
+
};
|
|
331
|
+
await deps.subscriptionAccountAppender.appendProviderAccount("grok", block);
|
|
332
|
+
deps.grokSessions.settle(sessionId, "done");
|
|
333
|
+
}
|
|
334
|
+
function handleGrokOAuthCancel(sessionId, deps) {
|
|
335
|
+
if (!deps.grokSessions.cancel(sessionId)) return err3(404, "unknown or expired grok sign-in session");
|
|
336
|
+
return { status: 200, body: { ok: true } };
|
|
337
|
+
}
|
|
338
|
+
function handleGrokOAuthStatus(sessionId, deps) {
|
|
339
|
+
const s = deps.grokSessions.get(sessionId);
|
|
340
|
+
if (!s) return err3(404, "unknown or expired grok sign-in session");
|
|
341
|
+
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/admin/accountsCopilotOAuth.ts
|
|
345
|
+
var import_subscriptions4 = require("@omnicross/subscriptions");
|
|
346
|
+
function err4(status, message) {
|
|
347
|
+
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
348
|
+
}
|
|
349
|
+
var DEFAULT_COPILOT_OAUTH_TTL_MS = 15 * 6e4;
|
|
350
|
+
async function handleCopilotOAuthStart(deps, enterpriseUrlInput) {
|
|
351
|
+
if (deps.copilotSessions.isBusy()) {
|
|
352
|
+
return err4(409, "a copilot sign-in is already in progress \u2014 finish it in the browser or cancel it");
|
|
353
|
+
}
|
|
354
|
+
let enterpriseUrl;
|
|
355
|
+
if (typeof enterpriseUrlInput === "string" && enterpriseUrlInput.trim()) {
|
|
356
|
+
try {
|
|
357
|
+
enterpriseUrl = import_subscriptions4.copilotOAuth.normalizeCopilotEnterpriseDomain(enterpriseUrlInput);
|
|
358
|
+
} catch (e) {
|
|
359
|
+
const reason = e instanceof Error ? e.message : "invalid GitHub Enterprise domain";
|
|
360
|
+
return err4(400, `copilot ${reason}`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
const fetchImpl = deps.oauthExchangeFetch("copilot");
|
|
364
|
+
let authorization;
|
|
365
|
+
try {
|
|
366
|
+
authorization = await import_subscriptions4.copilotOAuth.requestCopilotDeviceAuthorization(fetchImpl, enterpriseUrl);
|
|
367
|
+
} catch (e) {
|
|
368
|
+
const reason = e instanceof Error ? e.message : "device authorization failed";
|
|
369
|
+
return err4(502, `copilot device authorization failed: ${reason}`);
|
|
370
|
+
}
|
|
371
|
+
const { sessionId, signal } = deps.copilotSessions.begin();
|
|
372
|
+
void runCopilotDevicePoll(sessionId, authorization.deviceCode, signal, deps, enterpriseUrl).catch((e) => {
|
|
373
|
+
const reason = e instanceof Error ? e.message : "copilot sign-in failed";
|
|
374
|
+
deps.copilotSessions.settle(sessionId, "error", reason);
|
|
375
|
+
});
|
|
376
|
+
return {
|
|
377
|
+
status: 200,
|
|
378
|
+
body: {
|
|
379
|
+
authUrl: authorization.verificationUri,
|
|
380
|
+
userCode: authorization.userCode,
|
|
381
|
+
sessionId,
|
|
382
|
+
...enterpriseUrl ? { enterpriseUrl } : {}
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
async function runCopilotDevicePoll(sessionId, deviceCode, signal, deps, enterpriseUrl) {
|
|
387
|
+
const fetchImpl = deps.oauthExchangeFetch("copilot");
|
|
388
|
+
const result = await import_subscriptions4.copilotOAuth.awaitCopilotDeviceToken(
|
|
389
|
+
{ userCode: "", deviceCode, verificationUri: "", interval: 5, expiresIn: 900 },
|
|
390
|
+
fetchImpl,
|
|
391
|
+
{
|
|
392
|
+
deadlineMs: DEFAULT_COPILOT_OAUTH_TTL_MS,
|
|
393
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
394
|
+
sleep: (ms) => new Promise((resolve10, reject) => {
|
|
395
|
+
const onAbort = () => {
|
|
396
|
+
clearTimeout(timer);
|
|
397
|
+
reject(new Error("login: cancelled"));
|
|
398
|
+
};
|
|
399
|
+
const timer = setTimeout(() => {
|
|
400
|
+
signal.removeEventListener("abort", onAbort);
|
|
401
|
+
resolve10();
|
|
402
|
+
}, ms);
|
|
403
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
404
|
+
})
|
|
405
|
+
}
|
|
406
|
+
);
|
|
407
|
+
const identity = await import_subscriptions4.copilotOAuth.fetchCopilotIdentity(result.accessToken, fetchImpl, enterpriseUrl);
|
|
408
|
+
const apiEndpoint = await import_subscriptions4.copilotOAuth.discoverCopilotApiEndpoint(result.accessToken, fetchImpl, enterpriseUrl);
|
|
409
|
+
await import_subscriptions4.copilotOAuth.enableAllCopilotModels(
|
|
410
|
+
result.accessToken,
|
|
411
|
+
{ apiEndpoint, ...enterpriseUrl ? { enterpriseUrl } : {} },
|
|
412
|
+
fetchImpl
|
|
413
|
+
);
|
|
414
|
+
const block = {
|
|
415
|
+
authMethod: "oauth",
|
|
416
|
+
status: "authorized",
|
|
417
|
+
accessToken: result.accessToken,
|
|
418
|
+
refreshToken: result.accessToken,
|
|
419
|
+
expiresAt: new Date(Date.now() + import_subscriptions4.copilotOAuth.COPILOT_FAR_FUTURE_MS).toISOString(),
|
|
420
|
+
...identity.accountId ? { accountId: identity.accountId } : {},
|
|
421
|
+
...identity.email ? { email: identity.email } : {},
|
|
422
|
+
...apiEndpoint ? { apiEndpoint } : {},
|
|
423
|
+
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
424
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
425
|
+
};
|
|
426
|
+
await deps.subscriptionAccountAppender.appendProviderAccount("copilot", block);
|
|
427
|
+
deps.copilotSessions.settle(sessionId, "done");
|
|
428
|
+
}
|
|
429
|
+
function handleCopilotOAuthCancel(sessionId, deps) {
|
|
430
|
+
if (!deps.copilotSessions.cancel(sessionId)) {
|
|
431
|
+
return err4(404, "unknown or expired copilot sign-in session");
|
|
432
|
+
}
|
|
433
|
+
return { status: 200, body: { ok: true } };
|
|
434
|
+
}
|
|
435
|
+
function handleCopilotOAuthStatus(sessionId, deps) {
|
|
436
|
+
const s = deps.copilotSessions.get(sessionId);
|
|
437
|
+
if (!s) return err4(404, "unknown or expired copilot sign-in session");
|
|
438
|
+
return { status: 200, body: { state: s.status, ...s.error ? { message: s.error } : {} } };
|
|
439
|
+
}
|
|
440
|
+
|
|
187
441
|
// src/allowance/AccountAllowanceService.ts
|
|
188
|
-
var
|
|
442
|
+
var import_AccountAllowanceStore7 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
189
443
|
var import_AccountAllowanceScheduling = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
190
444
|
|
|
191
|
-
// src/allowance/ClaudeAllowanceCollector.ts
|
|
192
|
-
var import_AccountAllowanceStore = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
193
|
-
var import_upstreamFetch = require("@omnicross/core/pipeline/upstreamFetch");
|
|
194
|
-
var import_fingerprintHeaders = require("@omnicross/core/provider-proxy/identity/fingerprintHeaders");
|
|
195
|
-
var import_SubscriptionIdentityStore = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
196
|
-
var CLAUDE_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
197
|
-
var CLAUDE_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
198
|
-
function finitePercent(value) {
|
|
445
|
+
// src/allowance/ClaudeAllowanceCollector.ts
|
|
446
|
+
var import_AccountAllowanceStore = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
447
|
+
var import_upstreamFetch = require("@omnicross/core/pipeline/upstreamFetch");
|
|
448
|
+
var import_fingerprintHeaders = require("@omnicross/core/provider-proxy/identity/fingerprintHeaders");
|
|
449
|
+
var import_SubscriptionIdentityStore = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
450
|
+
var CLAUDE_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
451
|
+
var CLAUDE_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
452
|
+
function finitePercent(value) {
|
|
453
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
454
|
+
const number = typeof value === "number" ? value : Number(value);
|
|
455
|
+
return Number.isFinite(number) && number >= 0 && number <= 100 ? number : null;
|
|
456
|
+
}
|
|
457
|
+
function isoInstant(value) {
|
|
458
|
+
if (typeof value !== "string" || !value.trim()) return void 0;
|
|
459
|
+
const time = Date.parse(value);
|
|
460
|
+
return Number.isFinite(time) ? new Date(time).toISOString() : void 0;
|
|
461
|
+
}
|
|
462
|
+
function secondsUntil(instant, now) {
|
|
463
|
+
if (!instant) return void 0;
|
|
464
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
465
|
+
}
|
|
466
|
+
function windowFromPayload(id, payload, now) {
|
|
467
|
+
const usedPercent = finitePercent(payload?.utilization);
|
|
468
|
+
const resetsAt = isoInstant(payload?.resets_at);
|
|
469
|
+
const isFiveHour = id === "five-hour";
|
|
470
|
+
return {
|
|
471
|
+
id,
|
|
472
|
+
label: isFiveHour ? "5 hours" : "7 days",
|
|
473
|
+
scope: "all",
|
|
474
|
+
usedPercent,
|
|
475
|
+
windowMinutes: isFiveHour ? 5 * 60 : 7 * 24 * 60,
|
|
476
|
+
resetsAt,
|
|
477
|
+
remainingSeconds: secondsUntil(resetsAt, now),
|
|
478
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
function limitEntryWindow(entries, kind) {
|
|
482
|
+
const entry = entries.find((candidate) => candidate.kind === kind);
|
|
483
|
+
if (!entry) return void 0;
|
|
484
|
+
return { utilization: entry.percent, resets_at: entry.resets_at };
|
|
485
|
+
}
|
|
486
|
+
function slugifyDisplayName(name) {
|
|
487
|
+
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
488
|
+
}
|
|
489
|
+
function scopedWeeklyWindows(entries, now) {
|
|
490
|
+
const seen = /* @__PURE__ */ new Set();
|
|
491
|
+
const windows = [];
|
|
492
|
+
for (const entry of entries) {
|
|
493
|
+
if (entry.kind !== "weekly_scoped") continue;
|
|
494
|
+
const displayName = typeof entry.scope?.model?.display_name === "string" && entry.scope.model.display_name.trim() ? entry.scope.model.display_name.trim() : void 0;
|
|
495
|
+
if (!displayName) continue;
|
|
496
|
+
const slug = slugifyDisplayName(displayName);
|
|
497
|
+
if (!slug || seen.has(slug)) continue;
|
|
498
|
+
seen.add(slug);
|
|
499
|
+
const usedPercent = finitePercent(entry.percent);
|
|
500
|
+
const resetsAt = isoInstant(entry.resets_at);
|
|
501
|
+
windows.push({
|
|
502
|
+
id: `seven-day-${slug}`,
|
|
503
|
+
label: `7 days \xB7 ${displayName}`,
|
|
504
|
+
scope: "model-family",
|
|
505
|
+
modelFamily: slug,
|
|
506
|
+
usedPercent,
|
|
507
|
+
windowMinutes: 7 * 24 * 60,
|
|
508
|
+
resetsAt,
|
|
509
|
+
remainingSeconds: secondsUntil(resetsAt, now),
|
|
510
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
return windows;
|
|
514
|
+
}
|
|
515
|
+
function parseLimitEntries(raw) {
|
|
516
|
+
if (!Array.isArray(raw)) return [];
|
|
517
|
+
return raw.filter((entry) => !!entry && typeof entry === "object");
|
|
518
|
+
}
|
|
519
|
+
function emptyClaudeWindows(state) {
|
|
520
|
+
return [
|
|
521
|
+
{
|
|
522
|
+
id: "five-hour",
|
|
523
|
+
label: "5 hours",
|
|
524
|
+
scope: "all",
|
|
525
|
+
usedPercent: null,
|
|
526
|
+
windowMinutes: 5 * 60,
|
|
527
|
+
state
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
id: "seven-day",
|
|
531
|
+
label: "7 days",
|
|
532
|
+
scope: "all",
|
|
533
|
+
usedPercent: null,
|
|
534
|
+
windowMinutes: 7 * 24 * 60,
|
|
535
|
+
state
|
|
536
|
+
}
|
|
537
|
+
];
|
|
538
|
+
}
|
|
539
|
+
function hasHeader(headers, name) {
|
|
540
|
+
const wanted = name.toLowerCase();
|
|
541
|
+
return Object.keys(headers).some((key) => key.toLowerCase() === wanted);
|
|
542
|
+
}
|
|
543
|
+
var ClaudeAllowanceCollector = class {
|
|
544
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch.fetchUpstream)(url, init, { providerId: "claude", accountId }), identityStore = (0, import_SubscriptionIdentityStore.getSharedIdentityStore)(), now = Date.now) {
|
|
545
|
+
this.credentials = credentials;
|
|
546
|
+
this.store = store;
|
|
547
|
+
this.fetchImpl = fetchImpl;
|
|
548
|
+
this.identityStore = identityStore;
|
|
549
|
+
this.now = now;
|
|
550
|
+
}
|
|
551
|
+
credentials;
|
|
552
|
+
store;
|
|
553
|
+
fetchImpl;
|
|
554
|
+
identityStore;
|
|
555
|
+
now;
|
|
556
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
557
|
+
async collectMany(accounts, options = {}) {
|
|
558
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
559
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
560
|
+
}
|
|
561
|
+
collect(account, options = {}) {
|
|
562
|
+
const now = this.now();
|
|
563
|
+
const unsupported = account.tokens.isSetupToken || account.tokens.authMethod !== "oauth";
|
|
564
|
+
if (unsupported) {
|
|
565
|
+
const existing = this.store.get("claude", account.id, now);
|
|
566
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) return Promise.resolve(existing);
|
|
567
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
568
|
+
this.store.set(snapshot);
|
|
569
|
+
return Promise.resolve(snapshot);
|
|
570
|
+
}
|
|
571
|
+
const cached = this.store.get("claude", account.id, now);
|
|
572
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) return Promise.resolve(cached);
|
|
573
|
+
const running = this.inFlight.get(account.id);
|
|
574
|
+
if (running) return running;
|
|
575
|
+
const promise = this.fetchAccount(account.id).catch(() => this.failureSnapshot(account.id, "claude_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
576
|
+
this.inFlight.set(account.id, promise);
|
|
577
|
+
return promise;
|
|
578
|
+
}
|
|
579
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
580
|
+
if (snapshot.source !== "oauth-usage-api") return false;
|
|
581
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
582
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
583
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
584
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
585
|
+
}
|
|
586
|
+
async fetchAccount(accountId) {
|
|
587
|
+
let token = await this.credentials.getAccessTokenForAccount("claude", accountId);
|
|
588
|
+
if (!token) return this.failureSnapshot(accountId, "claude_usage_token_unavailable", this.now());
|
|
589
|
+
let response = await this.request(accountId, token);
|
|
590
|
+
if (response.status === 401) {
|
|
591
|
+
const refreshed = await this.credentials.refreshAccountToken("claude", accountId);
|
|
592
|
+
if (!refreshed) return this.failureSnapshot(accountId, "claude_usage_unauthorized", this.now());
|
|
593
|
+
token = await this.credentials.getAccessTokenForAccount("claude", accountId);
|
|
594
|
+
if (!token) return this.failureSnapshot(accountId, "claude_usage_token_unavailable", this.now());
|
|
595
|
+
response = await this.request(accountId, token);
|
|
596
|
+
}
|
|
597
|
+
if (response.status === 403) {
|
|
598
|
+
const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "claude_usage_unsupported");
|
|
599
|
+
this.store.set(snapshot2);
|
|
600
|
+
return snapshot2;
|
|
601
|
+
}
|
|
602
|
+
if (!response.ok) {
|
|
603
|
+
return this.failureSnapshot(accountId, "claude_usage_http_error", this.now());
|
|
604
|
+
}
|
|
605
|
+
let payload;
|
|
606
|
+
try {
|
|
607
|
+
payload = await response.json();
|
|
608
|
+
} catch {
|
|
609
|
+
return this.failureSnapshot(accountId, "claude_usage_invalid_response", this.now());
|
|
610
|
+
}
|
|
611
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
612
|
+
return this.failureSnapshot(accountId, "claude_usage_invalid_response", this.now());
|
|
613
|
+
}
|
|
614
|
+
const now = this.now();
|
|
615
|
+
const usage = payload;
|
|
616
|
+
const limitEntries = parseLimitEntries(usage.limits);
|
|
617
|
+
const fiveHour = usage.five_hour ?? limitEntryWindow(limitEntries, "session");
|
|
618
|
+
const sevenDay = usage.seven_day ?? limitEntryWindow(limitEntries, "weekly_all");
|
|
619
|
+
const snapshot = {
|
|
620
|
+
providerId: "claude",
|
|
621
|
+
accountId,
|
|
622
|
+
source: "oauth-usage-api",
|
|
623
|
+
observedAt: new Date(now).toISOString(),
|
|
624
|
+
expiresAt: new Date(now + CLAUDE_ALLOWANCE_CACHE_MS).toISOString(),
|
|
625
|
+
windows: [
|
|
626
|
+
windowFromPayload("five-hour", fiveHour, now),
|
|
627
|
+
windowFromPayload("seven-day", sevenDay, now),
|
|
628
|
+
...scopedWeeklyWindows(limitEntries, now)
|
|
629
|
+
].slice(0, 8)
|
|
630
|
+
};
|
|
631
|
+
this.store.set(snapshot);
|
|
632
|
+
return snapshot;
|
|
633
|
+
}
|
|
634
|
+
request(accountId, token) {
|
|
635
|
+
const headers = {
|
|
636
|
+
Authorization: `Bearer ${token}`,
|
|
637
|
+
Accept: "application/json",
|
|
638
|
+
"Content-Type": "application/json",
|
|
639
|
+
"anthropic-beta": "oauth-2025-04-20",
|
|
640
|
+
"Accept-Language": "en-US,en;q=0.9"
|
|
641
|
+
};
|
|
642
|
+
(0, import_fingerprintHeaders.applyFingerprint)(this.identityStore, headers, "claude", accountId, void 0);
|
|
643
|
+
if (!hasHeader(headers, "user-agent")) {
|
|
644
|
+
headers["User-Agent"] = "claude-cli/2.0.53 (external, cli)";
|
|
645
|
+
}
|
|
646
|
+
return this.fetchImpl(CLAUDE_USAGE_URL, {
|
|
647
|
+
method: "GET",
|
|
648
|
+
headers,
|
|
649
|
+
signal: AbortSignal.timeout(15e3)
|
|
650
|
+
}, accountId);
|
|
651
|
+
}
|
|
652
|
+
failureSnapshot(accountId, code, now) {
|
|
653
|
+
const existing = this.store.get("claude", accountId, now);
|
|
654
|
+
const snapshot = existing ? {
|
|
655
|
+
...existing,
|
|
656
|
+
expiresAt: new Date(now + CLAUDE_ALLOWANCE_CACHE_MS).toISOString(),
|
|
657
|
+
windows: existing.windows.map((window) => ({
|
|
658
|
+
...window,
|
|
659
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
660
|
+
})),
|
|
661
|
+
lastErrorCode: code
|
|
662
|
+
} : {
|
|
663
|
+
providerId: "claude",
|
|
664
|
+
accountId,
|
|
665
|
+
source: "oauth-usage-api",
|
|
666
|
+
observedAt: new Date(now).toISOString(),
|
|
667
|
+
expiresAt: new Date(now + CLAUDE_ALLOWANCE_CACHE_MS).toISOString(),
|
|
668
|
+
windows: emptyClaudeWindows("unavailable"),
|
|
669
|
+
lastErrorCode: code
|
|
670
|
+
};
|
|
671
|
+
this.store.set(snapshot);
|
|
672
|
+
return snapshot;
|
|
673
|
+
}
|
|
674
|
+
unsupportedSnapshot(accountId, now, code = "claude_usage_unsupported_auth") {
|
|
675
|
+
return {
|
|
676
|
+
providerId: "claude",
|
|
677
|
+
accountId,
|
|
678
|
+
source: "oauth-usage-api",
|
|
679
|
+
observedAt: new Date(now).toISOString(),
|
|
680
|
+
windows: emptyClaudeWindows("unsupported"),
|
|
681
|
+
lastErrorCode: code
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
// src/allowance/CodexAllowanceCollector.ts
|
|
687
|
+
var import_AccountAllowanceStore2 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
688
|
+
var import_upstreamFetch2 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
689
|
+
var CODEX_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
690
|
+
var CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
691
|
+
var CODEX_CLI_USER_AGENT = "codex_cli_rs/0.144.5";
|
|
692
|
+
function finiteNumber(value) {
|
|
693
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
694
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
695
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
|
696
|
+
}
|
|
697
|
+
function finitePercent2(value) {
|
|
698
|
+
const parsed = finiteNumber(value);
|
|
699
|
+
return parsed !== null && parsed <= 100 ? parsed : null;
|
|
700
|
+
}
|
|
701
|
+
function epochMs(value) {
|
|
702
|
+
return value > 1e11 ? value : value * 1e3;
|
|
703
|
+
}
|
|
704
|
+
function secondsUntil2(instant, now) {
|
|
705
|
+
if (!instant) return void 0;
|
|
706
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
707
|
+
}
|
|
708
|
+
function decodeJwtClaims(token) {
|
|
709
|
+
const parts = token.split(".");
|
|
710
|
+
if (parts.length !== 3) return void 0;
|
|
711
|
+
try {
|
|
712
|
+
const json2 = Buffer.from(parts[1], "base64url").toString("utf8");
|
|
713
|
+
const parsed = JSON.parse(json2);
|
|
714
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
715
|
+
} catch {
|
|
716
|
+
return void 0;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
function chatgptAccountIdFromClaims(claims) {
|
|
720
|
+
const auth = claims?.["https://api.openai.com/auth"];
|
|
721
|
+
if (!auth || typeof auth !== "object") return void 0;
|
|
722
|
+
const accountId = auth.chatgpt_account_id;
|
|
723
|
+
return typeof accountId === "string" && accountId.trim() ? accountId.trim() : void 0;
|
|
724
|
+
}
|
|
725
|
+
function resolveCodexChatGptAccountId(tokens) {
|
|
726
|
+
if (tokens.accountId?.trim()) return tokens.accountId.trim();
|
|
727
|
+
if (tokens.idToken) {
|
|
728
|
+
const fromIdToken = chatgptAccountIdFromClaims(decodeJwtClaims(tokens.idToken));
|
|
729
|
+
if (fromIdToken) return fromIdToken;
|
|
730
|
+
}
|
|
731
|
+
if (tokens.accessToken) {
|
|
732
|
+
return chatgptAccountIdFromClaims(decodeJwtClaims(tokens.accessToken));
|
|
733
|
+
}
|
|
734
|
+
return void 0;
|
|
735
|
+
}
|
|
736
|
+
function windowFromPayload2(id, payload, now) {
|
|
737
|
+
const usedPercent = finitePercent2(payload?.used_percent);
|
|
738
|
+
const resetAtSeconds = finiteNumber(payload?.reset_at);
|
|
739
|
+
const resetAfterSeconds = finiteNumber(payload?.reset_after_seconds);
|
|
740
|
+
const windowSeconds = finiteNumber(payload?.limit_window_seconds);
|
|
741
|
+
const resetsAt = resetAtSeconds !== null && resetAtSeconds > 0 ? new Date(epochMs(resetAtSeconds)).toISOString() : resetAfterSeconds !== null && resetAfterSeconds > 0 ? new Date(now + resetAfterSeconds * 1e3).toISOString() : void 0;
|
|
742
|
+
const windowMinutes = windowSeconds !== null && windowSeconds > 0 ? Math.round(windowSeconds / 60) : void 0;
|
|
743
|
+
return {
|
|
744
|
+
id,
|
|
745
|
+
label: id === "primary" ? "Primary" : "Secondary",
|
|
746
|
+
scope: "all",
|
|
747
|
+
usedPercent,
|
|
748
|
+
...windowMinutes !== void 0 ? { windowMinutes } : {},
|
|
749
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
750
|
+
remainingSeconds: secondsUntil2(resetsAt, now),
|
|
751
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
var CodexAllowanceCollector = class {
|
|
755
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore2.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch2.fetchUpstream)(url, init, { providerId: "codex", accountId, redactBodies: true }), now = Date.now) {
|
|
756
|
+
this.credentials = credentials;
|
|
757
|
+
this.store = store;
|
|
758
|
+
this.fetchImpl = fetchImpl;
|
|
759
|
+
this.now = now;
|
|
760
|
+
}
|
|
761
|
+
credentials;
|
|
762
|
+
store;
|
|
763
|
+
fetchImpl;
|
|
764
|
+
now;
|
|
765
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
766
|
+
async collectMany(accounts, options = {}) {
|
|
767
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
768
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
769
|
+
}
|
|
770
|
+
collect(account, options = {}) {
|
|
771
|
+
const now = this.now();
|
|
772
|
+
const unsupported = account.tokens.authMethod !== "oauth";
|
|
773
|
+
if (unsupported) {
|
|
774
|
+
const existing = this.store.get("codex", account.id, now);
|
|
775
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
776
|
+
return Promise.resolve(existing);
|
|
777
|
+
}
|
|
778
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
779
|
+
this.store.set(snapshot);
|
|
780
|
+
return Promise.resolve(snapshot);
|
|
781
|
+
}
|
|
782
|
+
const cached = this.store.get("codex", account.id, now);
|
|
783
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
784
|
+
return Promise.resolve(cached);
|
|
785
|
+
}
|
|
786
|
+
const running = this.inFlight.get(account.id);
|
|
787
|
+
if (running) return running;
|
|
788
|
+
const promise = this.fetchAccount(account.id, account.tokens).catch(() => this.failureSnapshot(account.id, "codex_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
789
|
+
this.inFlight.set(account.id, promise);
|
|
790
|
+
return promise;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* A response-header snapshot stays a valid cache hit only while fresh; an
|
|
794
|
+
* active oauth-usage snapshot is honored on the same 5-minute cadence as
|
|
795
|
+
* Claude's (the poll is cheap and quota is the scheduling input).
|
|
796
|
+
*/
|
|
797
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
798
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
799
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
800
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
801
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
802
|
+
}
|
|
803
|
+
async fetchAccount(accountId, tokens) {
|
|
804
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("codex", accountId);
|
|
805
|
+
if (!accessToken) {
|
|
806
|
+
return this.failureSnapshot(accountId, "codex_usage_token_unavailable", this.now());
|
|
807
|
+
}
|
|
808
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
809
|
+
if (response.status === 401) {
|
|
810
|
+
const refreshed = await this.credentials.refreshAccountToken("codex", accountId);
|
|
811
|
+
if (!refreshed) {
|
|
812
|
+
return this.failureSnapshot(accountId, "codex_usage_unauthorized", this.now());
|
|
813
|
+
}
|
|
814
|
+
accessToken = await this.credentials.getAccessTokenForAccount("codex", accountId);
|
|
815
|
+
if (!accessToken) {
|
|
816
|
+
return this.failureSnapshot(accountId, "codex_usage_token_unavailable", this.now());
|
|
817
|
+
}
|
|
818
|
+
response = await this.request(accountId, accessToken, tokens);
|
|
819
|
+
}
|
|
820
|
+
if (response.status === 403) {
|
|
821
|
+
const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "codex_usage_unsupported");
|
|
822
|
+
this.store.set(snapshot2);
|
|
823
|
+
return snapshot2;
|
|
824
|
+
}
|
|
825
|
+
if (!response.ok) {
|
|
826
|
+
return this.failureSnapshot(accountId, "codex_usage_http_error", this.now());
|
|
827
|
+
}
|
|
828
|
+
let payload;
|
|
829
|
+
try {
|
|
830
|
+
payload = await response.json();
|
|
831
|
+
} catch {
|
|
832
|
+
return this.failureSnapshot(accountId, "codex_usage_invalid_response", this.now());
|
|
833
|
+
}
|
|
834
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
835
|
+
return this.failureSnapshot(accountId, "codex_usage_invalid_response", this.now());
|
|
836
|
+
}
|
|
837
|
+
const now = this.now();
|
|
838
|
+
const usage = payload.rate_limit;
|
|
839
|
+
const previous = this.store.get("codex", accountId, now);
|
|
840
|
+
const snapshot = {
|
|
841
|
+
providerId: "codex",
|
|
842
|
+
accountId,
|
|
843
|
+
source: "oauth-usage-api",
|
|
844
|
+
observedAt: new Date(now).toISOString(),
|
|
845
|
+
expiresAt: new Date(now + CODEX_ALLOWANCE_CACHE_MS).toISOString(),
|
|
846
|
+
windows: [
|
|
847
|
+
windowFromPayload2("primary", usage?.primary_window ?? void 0, now),
|
|
848
|
+
windowFromPayload2("secondary", usage?.secondary_window ?? void 0, now)
|
|
849
|
+
],
|
|
850
|
+
// The wham payload has no ratio field; keep the passively-observed value.
|
|
851
|
+
...previous?.primaryOverSecondaryLimitPercent !== void 0 ? { primaryOverSecondaryLimitPercent: previous.primaryOverSecondaryLimitPercent } : {}
|
|
852
|
+
};
|
|
853
|
+
this.store.set(snapshot);
|
|
854
|
+
return snapshot;
|
|
855
|
+
}
|
|
856
|
+
request(accountId, accessToken, tokens) {
|
|
857
|
+
const headers = {
|
|
858
|
+
Authorization: `Bearer ${accessToken}`,
|
|
859
|
+
Accept: "application/json",
|
|
860
|
+
"User-Agent": CODEX_CLI_USER_AGENT
|
|
861
|
+
};
|
|
862
|
+
const chatgptAccountId = resolveCodexChatGptAccountId(tokens);
|
|
863
|
+
if (chatgptAccountId) headers["ChatGPT-Account-Id"] = chatgptAccountId;
|
|
864
|
+
return this.fetchImpl(CODEX_USAGE_URL, {
|
|
865
|
+
method: "GET",
|
|
866
|
+
headers,
|
|
867
|
+
signal: AbortSignal.timeout(15e3)
|
|
868
|
+
}, accountId);
|
|
869
|
+
}
|
|
870
|
+
failureSnapshot(accountId, code, now) {
|
|
871
|
+
const existing = this.store.get("codex", accountId, now);
|
|
872
|
+
const snapshot = existing ? {
|
|
873
|
+
...existing,
|
|
874
|
+
expiresAt: new Date(now + CODEX_ALLOWANCE_CACHE_MS).toISOString(),
|
|
875
|
+
windows: existing.windows.map((window) => ({
|
|
876
|
+
...window,
|
|
877
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
878
|
+
})),
|
|
879
|
+
lastErrorCode: code
|
|
880
|
+
} : {
|
|
881
|
+
providerId: "codex",
|
|
882
|
+
accountId,
|
|
883
|
+
source: "oauth-usage-api",
|
|
884
|
+
observedAt: new Date(now).toISOString(),
|
|
885
|
+
expiresAt: new Date(now + CODEX_ALLOWANCE_CACHE_MS).toISOString(),
|
|
886
|
+
windows: [
|
|
887
|
+
{ id: "primary", label: "Primary", scope: "all", usedPercent: null, state: "unavailable" },
|
|
888
|
+
{ id: "secondary", label: "Secondary", scope: "all", usedPercent: null, state: "unavailable" }
|
|
889
|
+
],
|
|
890
|
+
lastErrorCode: code
|
|
891
|
+
};
|
|
892
|
+
this.store.set(snapshot);
|
|
893
|
+
return snapshot;
|
|
894
|
+
}
|
|
895
|
+
unsupportedSnapshot(accountId, now, code = "codex_usage_unsupported_auth") {
|
|
896
|
+
return {
|
|
897
|
+
providerId: "codex",
|
|
898
|
+
accountId,
|
|
899
|
+
source: "oauth-usage-api",
|
|
900
|
+
observedAt: new Date(now).toISOString(),
|
|
901
|
+
windows: [
|
|
902
|
+
{ id: "primary", label: "Primary", scope: "all", usedPercent: null, state: "unsupported" },
|
|
903
|
+
{ id: "secondary", label: "Secondary", scope: "all", usedPercent: null, state: "unsupported" }
|
|
904
|
+
],
|
|
905
|
+
lastErrorCode: code
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
// src/allowance/KimiAllowanceCollector.ts
|
|
911
|
+
var import_AccountAllowanceStore3 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
912
|
+
var import_upstreamFetch3 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
913
|
+
var import_subscriptions5 = require("@omnicross/subscriptions");
|
|
914
|
+
var KIMI_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
915
|
+
var KIMI_USAGE_URL = "https://api.kimi.com/coding/v1/usages";
|
|
916
|
+
function finiteNumber2(value) {
|
|
917
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
918
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
919
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
920
|
+
}
|
|
921
|
+
function isRecord(value) {
|
|
922
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
923
|
+
}
|
|
924
|
+
function parseResetMs(row, nowMs) {
|
|
925
|
+
for (const key of ["reset_at", "resetAt", "reset_time", "resetTime"]) {
|
|
926
|
+
const value = row[key];
|
|
927
|
+
if (typeof value === "string" && value.trim()) {
|
|
928
|
+
const parsed = Date.parse(value);
|
|
929
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
930
|
+
}
|
|
931
|
+
const numeric = finiteNumber2(value);
|
|
932
|
+
if (numeric !== void 0 && numeric > 1e9) {
|
|
933
|
+
return numeric > 1e12 ? numeric : numeric * 1e3;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
for (const key of ["reset_in", "resetIn", "ttl", "window"]) {
|
|
937
|
+
const seconds = finiteNumber2(row[key]);
|
|
938
|
+
if (seconds !== void 0) return nowMs + seconds * 1e3;
|
|
939
|
+
}
|
|
940
|
+
return void 0;
|
|
941
|
+
}
|
|
942
|
+
var MINUTE_MS = 6e4;
|
|
943
|
+
var HOUR_MS = 36e5;
|
|
944
|
+
var DAY_MS = 864e5;
|
|
945
|
+
function canonicalWindow(durationMs) {
|
|
946
|
+
if (durationMs === 5 * HOUR_MS) return { id: "five-hour", label: "5 hours", minutes: 300 };
|
|
947
|
+
if (durationMs === 7 * DAY_MS) return { id: "seven-day", label: "7 days", minutes: 10080 };
|
|
948
|
+
if (durationMs > 0 && durationMs % DAY_MS === 0) {
|
|
949
|
+
const days = durationMs / DAY_MS;
|
|
950
|
+
return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}`, minutes: Math.round(durationMs / MINUTE_MS) };
|
|
951
|
+
}
|
|
952
|
+
if (durationMs > 0 && durationMs % HOUR_MS === 0) {
|
|
953
|
+
const hours = durationMs / HOUR_MS;
|
|
954
|
+
return { id: `${hours}h`, label: `${hours} hour${hours === 1 ? "" : "s"}`, minutes: Math.round(durationMs / MINUTE_MS) };
|
|
955
|
+
}
|
|
956
|
+
return void 0;
|
|
957
|
+
}
|
|
958
|
+
function secondsUntil3(instant, now) {
|
|
959
|
+
if (!instant) return void 0;
|
|
960
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
961
|
+
}
|
|
962
|
+
function windowFromRow(row, fallback, now) {
|
|
963
|
+
const usedPercent = row?.limit !== void 0 && row.limit > 0 && row.used !== void 0 ? Math.round(Math.min(100, row.used / row.limit * 100) * 10) / 10 : null;
|
|
964
|
+
const resetsAt = row?.resetsAtMs !== void 0 ? new Date(row.resetsAtMs).toISOString() : void 0;
|
|
965
|
+
return {
|
|
966
|
+
id: fallback.id,
|
|
967
|
+
label: fallback.label,
|
|
968
|
+
scope: "all",
|
|
969
|
+
usedPercent,
|
|
970
|
+
windowMinutes: fallback.minutes,
|
|
971
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
972
|
+
remainingSeconds: secondsUntil3(resetsAt, now),
|
|
973
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
function parseKimiUsagePayload(payload, now) {
|
|
977
|
+
if (!isRecord(payload)) return [];
|
|
978
|
+
const byId = /* @__PURE__ */ new Map();
|
|
979
|
+
const rowFrom = (data) => {
|
|
980
|
+
const limit = finiteNumber2(data["limit"]);
|
|
981
|
+
let used = finiteNumber2(data["used"]);
|
|
982
|
+
const remaining = finiteNumber2(data["remaining"]);
|
|
983
|
+
if (used === void 0 && remaining !== void 0 && limit !== void 0) {
|
|
984
|
+
used = limit - remaining;
|
|
985
|
+
}
|
|
986
|
+
let windowDurationMs;
|
|
987
|
+
const windowData = isRecord(data["window"]) ? data["window"] : void 0;
|
|
988
|
+
const duration = finiteNumber2(windowData?.["duration"]);
|
|
989
|
+
const timeUnit = typeof windowData?.["timeUnit"] === "string" ? windowData["timeUnit"].toUpperCase() : "";
|
|
990
|
+
if (duration !== void 0) {
|
|
991
|
+
if (timeUnit.includes("MINUTE")) windowDurationMs = duration * MINUTE_MS;
|
|
992
|
+
else if (timeUnit.includes("HOUR")) windowDurationMs = duration * HOUR_MS;
|
|
993
|
+
else if (timeUnit.includes("DAY")) windowDurationMs = duration * DAY_MS;
|
|
994
|
+
else if (timeUnit.includes("WEEK")) windowDurationMs = duration * 7 * DAY_MS;
|
|
995
|
+
else if (timeUnit.includes("SECOND")) windowDurationMs = duration * 1e3;
|
|
996
|
+
}
|
|
997
|
+
const resetsAtMs = parseResetMs(windowData && parseResetMs(windowData, now) !== void 0 ? windowData : data, now);
|
|
998
|
+
return { used, limit, remaining, ...resetsAtMs !== void 0 ? { resetsAtMs } : {}, ...windowDurationMs !== void 0 ? { windowDurationMs } : {} };
|
|
999
|
+
};
|
|
1000
|
+
if (isRecord(payload["usage"])) {
|
|
1001
|
+
const row = rowFrom(payload["usage"]);
|
|
1002
|
+
const window = windowFromRow({ ...row, resetsAtMs: row.resetsAtMs }, { id: "seven-day", label: "7 days", minutes: 10080 }, now);
|
|
1003
|
+
byId.set("seven-day", window);
|
|
1004
|
+
}
|
|
1005
|
+
if (Array.isArray(payload["limits"])) {
|
|
1006
|
+
for (const item of payload["limits"]) {
|
|
1007
|
+
if (!isRecord(item)) continue;
|
|
1008
|
+
const detail = isRecord(item["detail"]) ? item["detail"] : item;
|
|
1009
|
+
const row = rowFrom(detail);
|
|
1010
|
+
const canonical = row.windowDurationMs !== void 0 ? canonicalWindow(row.windowDurationMs) : void 0;
|
|
1011
|
+
if (!canonical) continue;
|
|
1012
|
+
const window = windowFromRow(row, canonical, now);
|
|
1013
|
+
const existing = byId.get(canonical.id);
|
|
1014
|
+
if (!existing || (window.usedPercent ?? 0) > (existing.usedPercent ?? 0)) {
|
|
1015
|
+
byId.set(canonical.id, window);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return [...byId.values()].sort((a, b) => (a.windowMinutes ?? Infinity) - (b.windowMinutes ?? Infinity)).slice(0, 4);
|
|
1020
|
+
}
|
|
1021
|
+
var KimiAllowanceCollector = class {
|
|
1022
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore3.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch3.fetchUpstream)(url, init, { providerId: "kimi", accountId, redactBodies: true }), now = Date.now) {
|
|
1023
|
+
this.credentials = credentials;
|
|
1024
|
+
this.store = store;
|
|
1025
|
+
this.fetchImpl = fetchImpl;
|
|
1026
|
+
this.now = now;
|
|
1027
|
+
}
|
|
1028
|
+
credentials;
|
|
1029
|
+
store;
|
|
1030
|
+
fetchImpl;
|
|
1031
|
+
now;
|
|
1032
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
1033
|
+
async collectMany(accounts, options = {}) {
|
|
1034
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
1035
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
1036
|
+
}
|
|
1037
|
+
collect(account, options = {}) {
|
|
1038
|
+
const now = this.now();
|
|
1039
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
1040
|
+
const existing = this.store.get("kimi", account.id, now);
|
|
1041
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
1042
|
+
return Promise.resolve(existing);
|
|
1043
|
+
}
|
|
1044
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
1045
|
+
this.store.set(snapshot);
|
|
1046
|
+
return Promise.resolve(snapshot);
|
|
1047
|
+
}
|
|
1048
|
+
const cached = this.store.get("kimi", account.id, now);
|
|
1049
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
1050
|
+
return Promise.resolve(cached);
|
|
1051
|
+
}
|
|
1052
|
+
const running = this.inFlight.get(account.id);
|
|
1053
|
+
if (running) return running;
|
|
1054
|
+
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));
|
|
1055
|
+
this.inFlight.set(account.id, promise);
|
|
1056
|
+
return promise;
|
|
1057
|
+
}
|
|
1058
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
1059
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
1060
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
1061
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
1062
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
1063
|
+
}
|
|
1064
|
+
async fetchAccount(accountId, tokens) {
|
|
1065
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
|
|
1066
|
+
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
1067
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
1068
|
+
if (response.status === 401) {
|
|
1069
|
+
const refreshed = await this.credentials.refreshAccountToken("kimi", accountId);
|
|
1070
|
+
if (!refreshed) return this.failureSnapshot(accountId, "kimi_usage_unauthorized", this.now());
|
|
1071
|
+
accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
|
|
1072
|
+
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
1073
|
+
response = await this.request(accountId, accessToken, tokens);
|
|
1074
|
+
}
|
|
1075
|
+
if (response.status === 403) {
|
|
1076
|
+
const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "kimi_usage_unsupported");
|
|
1077
|
+
this.store.set(snapshot2);
|
|
1078
|
+
return snapshot2;
|
|
1079
|
+
}
|
|
1080
|
+
if (!response.ok) return this.failureSnapshot(accountId, "kimi_usage_http_error", this.now());
|
|
1081
|
+
let payload;
|
|
1082
|
+
try {
|
|
1083
|
+
payload = await response.json();
|
|
1084
|
+
} catch {
|
|
1085
|
+
return this.failureSnapshot(accountId, "kimi_usage_invalid_response", this.now());
|
|
1086
|
+
}
|
|
1087
|
+
const now = this.now();
|
|
1088
|
+
const windows = parseKimiUsagePayload(payload, now);
|
|
1089
|
+
const snapshot = {
|
|
1090
|
+
providerId: "kimi",
|
|
1091
|
+
accountId,
|
|
1092
|
+
source: "oauth-usage-api",
|
|
1093
|
+
observedAt: new Date(now).toISOString(),
|
|
1094
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1095
|
+
windows: windows.length > 0 ? windows : [
|
|
1096
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
1097
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
1098
|
+
],
|
|
1099
|
+
...windows.length > 0 ? {} : { lastErrorCode: "kimi_usage_invalid_response" }
|
|
1100
|
+
};
|
|
1101
|
+
this.store.set(snapshot);
|
|
1102
|
+
return snapshot;
|
|
1103
|
+
}
|
|
1104
|
+
request(accountId, accessToken, tokens) {
|
|
1105
|
+
return this.fetchImpl(KIMI_USAGE_URL, {
|
|
1106
|
+
method: "GET",
|
|
1107
|
+
headers: {
|
|
1108
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1109
|
+
Accept: "application/json",
|
|
1110
|
+
...(0, import_subscriptions5.kimiFingerprintHeaders)(tokens.deviceId)
|
|
1111
|
+
},
|
|
1112
|
+
signal: AbortSignal.timeout(15e3)
|
|
1113
|
+
}, accountId);
|
|
1114
|
+
}
|
|
1115
|
+
failureSnapshot(accountId, code, now) {
|
|
1116
|
+
const existing = this.store.get("kimi", accountId, now);
|
|
1117
|
+
const snapshot = existing ? {
|
|
1118
|
+
...existing,
|
|
1119
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1120
|
+
windows: existing.windows.map((window) => ({
|
|
1121
|
+
...window,
|
|
1122
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
1123
|
+
})),
|
|
1124
|
+
lastErrorCode: code
|
|
1125
|
+
} : {
|
|
1126
|
+
providerId: "kimi",
|
|
1127
|
+
accountId,
|
|
1128
|
+
source: "oauth-usage-api",
|
|
1129
|
+
observedAt: new Date(now).toISOString(),
|
|
1130
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1131
|
+
windows: [
|
|
1132
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
1133
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
1134
|
+
],
|
|
1135
|
+
lastErrorCode: code
|
|
1136
|
+
};
|
|
1137
|
+
this.store.set(snapshot);
|
|
1138
|
+
return snapshot;
|
|
1139
|
+
}
|
|
1140
|
+
unsupportedSnapshot(accountId, now, code = "kimi_usage_unsupported_auth") {
|
|
1141
|
+
return {
|
|
1142
|
+
providerId: "kimi",
|
|
1143
|
+
accountId,
|
|
1144
|
+
source: "oauth-usage-api",
|
|
1145
|
+
observedAt: new Date(now).toISOString(),
|
|
1146
|
+
windows: [
|
|
1147
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unsupported" },
|
|
1148
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
1149
|
+
],
|
|
1150
|
+
lastErrorCode: code
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
|
|
1155
|
+
// src/allowance/GrokAllowanceCollector.ts
|
|
1156
|
+
var import_AccountAllowanceStore4 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1157
|
+
var import_upstreamFetch4 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
1158
|
+
var GROK_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
1159
|
+
var GROK_BILLING_BASE = "https://cli-chat-proxy.grok.com";
|
|
1160
|
+
var GROK_BILLING_CREDITS_URL = `${GROK_BILLING_BASE}/v1/billing?format=credits`;
|
|
1161
|
+
var GROK_BILLING_MONTHLY_URL = `${GROK_BILLING_BASE}/v1/billing`;
|
|
1162
|
+
function isRecord2(value) {
|
|
1163
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1164
|
+
}
|
|
1165
|
+
function finiteNumber3(value) {
|
|
1166
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
1167
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
1168
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
1169
|
+
}
|
|
1170
|
+
function percent(value) {
|
|
1171
|
+
const parsed = finiteNumber3(value);
|
|
1172
|
+
return parsed !== void 0 && parsed <= 100 ? parsed : void 0;
|
|
1173
|
+
}
|
|
1174
|
+
function onDemandAmount(value) {
|
|
1175
|
+
return isRecord2(value) ? finiteNumber3(value["val"]) : void 0;
|
|
1176
|
+
}
|
|
1177
|
+
function confirmsNoMonthlyQuota(raw) {
|
|
1178
|
+
const limit = onDemandAmount(raw["monthlyLimit"]);
|
|
1179
|
+
if (limit !== void 0) return limit === 0;
|
|
1180
|
+
return parseWeeklyConfig(raw)?.inferredPercent === true;
|
|
1181
|
+
}
|
|
1182
|
+
function parseWeeklyConfig(raw) {
|
|
1183
|
+
const period = isRecord2(raw["currentPeriod"]) ? raw["currentPeriod"] : void 0;
|
|
1184
|
+
if (!period) return null;
|
|
1185
|
+
const start = typeof period["start"] === "string" ? Date.parse(period["start"]) : Number.NaN;
|
|
1186
|
+
const end = typeof period["end"] === "string" ? Date.parse(period["end"]) : Number.NaN;
|
|
1187
|
+
const type = typeof period["type"] === "string" ? period["type"] : "";
|
|
1188
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
|
|
1189
|
+
if (!type.toUpperCase().includes("WEEK")) return null;
|
|
1190
|
+
const inferred = raw["creditUsagePercent"] === void 0 || raw["creditUsagePercent"] === null;
|
|
1191
|
+
let creditUsagePercent;
|
|
1192
|
+
if (inferred) {
|
|
1193
|
+
creditUsagePercent = end > Date.now() ? 0 : void 0;
|
|
1194
|
+
} else {
|
|
1195
|
+
creditUsagePercent = percent(raw["creditUsagePercent"]);
|
|
1196
|
+
}
|
|
1197
|
+
if (creditUsagePercent === void 0) return null;
|
|
1198
|
+
return {
|
|
1199
|
+
creditUsagePercent,
|
|
1200
|
+
inferredPercent: inferred,
|
|
1201
|
+
resetsAtMs: end,
|
|
1202
|
+
unified: raw["isUnifiedBillingUser"] === true
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
function parseMonthlyConfig(raw) {
|
|
1206
|
+
const start = typeof raw["billingPeriodStart"] === "string" ? Date.parse(raw["billingPeriodStart"]) : Number.NaN;
|
|
1207
|
+
const end = typeof raw["billingPeriodEnd"] === "string" ? Date.parse(raw["billingPeriodEnd"]) : Number.NaN;
|
|
1208
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return null;
|
|
1209
|
+
const limit = onDemandAmount(raw["monthlyLimit"]);
|
|
1210
|
+
const used = onDemandAmount(raw["used"]);
|
|
1211
|
+
if (limit === void 0 || limit <= 0 || used === void 0) return null;
|
|
1212
|
+
return { used, limit, periodStartMs: start, periodEndMs: end };
|
|
1213
|
+
}
|
|
1214
|
+
function secondsUntil4(instant, now) {
|
|
1215
|
+
if (!instant) return void 0;
|
|
1216
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
1217
|
+
}
|
|
1218
|
+
var MINUTE_MS2 = 6e4;
|
|
1219
|
+
var DAY_MS2 = 864e5;
|
|
1220
|
+
var WEEK_MINUTES = 7 * 24 * 60;
|
|
1221
|
+
function weeklyWindow(config, now) {
|
|
1222
|
+
const resetsAt = new Date(config.resetsAtMs).toISOString();
|
|
1223
|
+
return {
|
|
1224
|
+
id: "seven-day",
|
|
1225
|
+
label: "7 days",
|
|
1226
|
+
scope: "all",
|
|
1227
|
+
usedPercent: config.creditUsagePercent,
|
|
1228
|
+
windowMinutes: WEEK_MINUTES,
|
|
1229
|
+
resetsAt,
|
|
1230
|
+
remainingSeconds: secondsUntil4(resetsAt, now),
|
|
1231
|
+
state: "fresh"
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
function monthlyWindow(config, now) {
|
|
1235
|
+
const resetsAt = new Date(config.periodEndMs).toISOString();
|
|
1236
|
+
const days = Math.max(1, Math.round((config.periodEndMs - config.periodStartMs) / DAY_MS2));
|
|
1237
|
+
return {
|
|
1238
|
+
id: "thirty-day",
|
|
1239
|
+
label: days === 30 || days === 31 ? "30 days" : `${days} days`,
|
|
1240
|
+
scope: "all",
|
|
1241
|
+
usedPercent: Math.round(Math.min(100, config.used / config.limit * 100) * 10) / 10,
|
|
1242
|
+
windowMinutes: Math.round((config.periodEndMs - config.periodStartMs) / MINUTE_MS2),
|
|
1243
|
+
resetsAt,
|
|
1244
|
+
remainingSeconds: secondsUntil4(resetsAt, now),
|
|
1245
|
+
state: "fresh"
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
function onDemandWindow(raw) {
|
|
1249
|
+
const cap = onDemandAmount(raw["onDemandCap"]);
|
|
1250
|
+
const used = onDemandAmount(raw["onDemandUsed"]);
|
|
1251
|
+
if (cap === void 0 || cap <= 0 || used === void 0) return null;
|
|
1252
|
+
return {
|
|
1253
|
+
id: "on-demand",
|
|
1254
|
+
label: "On-demand",
|
|
1255
|
+
scope: "all",
|
|
1256
|
+
usedPercent: Math.round(Math.min(100, used / cap * 100) * 10) / 10,
|
|
1257
|
+
state: "fresh"
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
async function probeBilling(url, accessToken, accountId, fetchImpl) {
|
|
1261
|
+
try {
|
|
1262
|
+
const response = await fetchImpl(url, {
|
|
1263
|
+
method: "GET",
|
|
1264
|
+
headers: {
|
|
1265
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1266
|
+
Accept: "application/json",
|
|
1267
|
+
"X-XAI-Token-Auth": "xai-grok-cli"
|
|
1268
|
+
},
|
|
1269
|
+
redirect: "error",
|
|
1270
|
+
signal: AbortSignal.timeout(15e3)
|
|
1271
|
+
}, accountId);
|
|
1272
|
+
if (!response.ok) return { status: response.status, payload: null };
|
|
1273
|
+
const payload = await response.json();
|
|
1274
|
+
return { status: response.status, payload: isRecord2(payload) ? payload : null };
|
|
1275
|
+
} catch {
|
|
1276
|
+
return { status: 0, payload: null };
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
function parseGrokBillingPayloads(creditsPayload, monthlyPayload, now) {
|
|
1280
|
+
const creditsConfig = isRecord2(creditsPayload?.["config"]) ? creditsPayload["config"] : null;
|
|
1281
|
+
const monthlyConfig = isRecord2(monthlyPayload?.["config"]) ? monthlyPayload["config"] : null;
|
|
1282
|
+
let weekly = creditsConfig ? parseWeeklyConfig(creditsConfig) : null;
|
|
1283
|
+
const unifiedFlag = creditsConfig?.["isUnifiedBillingUser"] === true;
|
|
1284
|
+
let monthly = monthlyConfig ? parseMonthlyConfig(monthlyConfig) : null;
|
|
1285
|
+
if (weekly?.inferredPercent && unifiedFlag) {
|
|
1286
|
+
if (monthly) {
|
|
1287
|
+
weekly = null;
|
|
1288
|
+
} else if (!monthlyConfig || !confirmsNoMonthlyQuota(monthlyConfig)) {
|
|
1289
|
+
weekly = null;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
const windows = [];
|
|
1293
|
+
if (weekly) windows.push(weeklyWindow(weekly, now));
|
|
1294
|
+
if (monthly) windows.push(monthlyWindow(monthly, now));
|
|
1295
|
+
const onDemandSource = monthly && monthlyConfig ? monthlyConfig : creditsConfig;
|
|
1296
|
+
const onDemand = onDemandSource ? onDemandWindow(onDemandSource) : null;
|
|
1297
|
+
if (onDemand) windows.push(onDemand);
|
|
1298
|
+
return windows.length > 0 ? windows : null;
|
|
1299
|
+
}
|
|
1300
|
+
var GrokAllowanceCollector = class {
|
|
1301
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore4.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch4.fetchUpstream)(url, init, { providerId: "grok", accountId, redactBodies: true }), now = Date.now) {
|
|
1302
|
+
this.credentials = credentials;
|
|
1303
|
+
this.store = store;
|
|
1304
|
+
this.fetchImpl = fetchImpl;
|
|
1305
|
+
this.now = now;
|
|
1306
|
+
}
|
|
1307
|
+
credentials;
|
|
1308
|
+
store;
|
|
1309
|
+
fetchImpl;
|
|
1310
|
+
now;
|
|
1311
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
1312
|
+
async collectMany(accounts, options = {}) {
|
|
1313
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
1314
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
1315
|
+
}
|
|
1316
|
+
collect(account, options = {}) {
|
|
1317
|
+
const now = this.now();
|
|
1318
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
1319
|
+
const existing = this.store.get("grok", account.id, now);
|
|
1320
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
1321
|
+
return Promise.resolve(existing);
|
|
1322
|
+
}
|
|
1323
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
1324
|
+
this.store.set(snapshot);
|
|
1325
|
+
return Promise.resolve(snapshot);
|
|
1326
|
+
}
|
|
1327
|
+
const cached = this.store.get("grok", account.id, now);
|
|
1328
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
1329
|
+
return Promise.resolve(cached);
|
|
1330
|
+
}
|
|
1331
|
+
const running = this.inFlight.get(account.id);
|
|
1332
|
+
if (running) return running;
|
|
1333
|
+
const promise = this.fetchAccount(account.id).catch(() => this.failureSnapshot(account.id, "grok_usage_request_failed", this.now())).finally(() => this.inFlight.delete(account.id));
|
|
1334
|
+
this.inFlight.set(account.id, promise);
|
|
1335
|
+
return promise;
|
|
1336
|
+
}
|
|
1337
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
1338
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
1339
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
1340
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
1341
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
1342
|
+
}
|
|
1343
|
+
async fetchAccount(accountId) {
|
|
1344
|
+
const probe = async () => {
|
|
1345
|
+
const accessToken = await this.credentials.getAccessTokenForAccount("grok", accountId);
|
|
1346
|
+
if (!accessToken) return { unauthorized: true, windows: null };
|
|
1347
|
+
const credits = await probeBilling(GROK_BILLING_CREDITS_URL, accessToken, accountId, this.fetchImpl);
|
|
1348
|
+
if (credits.status === 401 || credits.status === 403) return { unauthorized: true, windows: null };
|
|
1349
|
+
const creditsConfig = isRecord2(credits.payload?.["config"]) ? credits.payload["config"] : null;
|
|
1350
|
+
const weekly = creditsConfig ? parseWeeklyConfig(creditsConfig) : null;
|
|
1351
|
+
const monthly = !weekly || creditsConfig?.["isUnifiedBillingUser"] === true ? await probeBilling(GROK_BILLING_MONTHLY_URL, accessToken, accountId, this.fetchImpl) : { status: 200, payload: null };
|
|
1352
|
+
if (monthly.status === 401 || monthly.status === 403) return { unauthorized: true, windows: null };
|
|
1353
|
+
return {
|
|
1354
|
+
unauthorized: false,
|
|
1355
|
+
windows: parseGrokBillingPayloads(credits.payload, monthly.payload, this.now())
|
|
1356
|
+
};
|
|
1357
|
+
};
|
|
1358
|
+
let result = await probe();
|
|
1359
|
+
if (result.unauthorized) {
|
|
1360
|
+
const refreshed = await this.credentials.refreshAccountToken("grok", accountId);
|
|
1361
|
+
if (!refreshed) return this.failureSnapshot(accountId, "grok_usage_unauthorized", this.now());
|
|
1362
|
+
result = await probe();
|
|
1363
|
+
if (result.unauthorized) {
|
|
1364
|
+
return this.failureSnapshot(accountId, "grok_usage_unauthorized", this.now());
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
const now = this.now();
|
|
1368
|
+
if (result.windows && result.windows.length > 0) {
|
|
1369
|
+
const snapshot = {
|
|
1370
|
+
providerId: "grok",
|
|
1371
|
+
accountId,
|
|
1372
|
+
source: "oauth-usage-api",
|
|
1373
|
+
observedAt: new Date(now).toISOString(),
|
|
1374
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1375
|
+
windows: result.windows
|
|
1376
|
+
};
|
|
1377
|
+
this.store.set(snapshot);
|
|
1378
|
+
return snapshot;
|
|
1379
|
+
}
|
|
1380
|
+
return this.failureSnapshot(accountId, "grok_usage_invalid_response", now);
|
|
1381
|
+
}
|
|
1382
|
+
failureSnapshot(accountId, code, now) {
|
|
1383
|
+
const existing = this.store.get("grok", accountId, now);
|
|
1384
|
+
const snapshot = existing ? {
|
|
1385
|
+
...existing,
|
|
1386
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1387
|
+
windows: existing.windows.map((window) => ({
|
|
1388
|
+
...window,
|
|
1389
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
1390
|
+
})),
|
|
1391
|
+
lastErrorCode: code
|
|
1392
|
+
} : {
|
|
1393
|
+
providerId: "grok",
|
|
1394
|
+
accountId,
|
|
1395
|
+
source: "oauth-usage-api",
|
|
1396
|
+
observedAt: new Date(now).toISOString(),
|
|
1397
|
+
expiresAt: new Date(now + GROK_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1398
|
+
windows: [
|
|
1399
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" },
|
|
1400
|
+
{ id: "thirty-day", label: "30 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
1401
|
+
],
|
|
1402
|
+
lastErrorCode: code
|
|
1403
|
+
};
|
|
1404
|
+
this.store.set(snapshot);
|
|
1405
|
+
return snapshot;
|
|
1406
|
+
}
|
|
1407
|
+
unsupportedSnapshot(accountId, now) {
|
|
1408
|
+
return {
|
|
1409
|
+
providerId: "grok",
|
|
1410
|
+
accountId,
|
|
1411
|
+
source: "oauth-usage-api",
|
|
1412
|
+
observedAt: new Date(now).toISOString(),
|
|
1413
|
+
windows: [
|
|
1414
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" },
|
|
1415
|
+
{ id: "thirty-day", label: "30 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
1416
|
+
],
|
|
1417
|
+
lastErrorCode: "grok_usage_unsupported_auth"
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
};
|
|
1421
|
+
|
|
1422
|
+
// src/allowance/CopilotAllowanceCollector.ts
|
|
1423
|
+
var import_AccountAllowanceStore5 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1424
|
+
var import_upstreamFetch5 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
1425
|
+
var import_subscriptions6 = require("@omnicross/subscriptions");
|
|
1426
|
+
var COPILOT_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
1427
|
+
function isRecord3(value) {
|
|
1428
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1429
|
+
}
|
|
1430
|
+
function finiteNumber4(value) {
|
|
1431
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
1432
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
1433
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
1434
|
+
}
|
|
1435
|
+
function booleanValue(value) {
|
|
1436
|
+
if (typeof value === "boolean") return value;
|
|
1437
|
+
if (value === "true") return true;
|
|
1438
|
+
if (value === "false") return false;
|
|
1439
|
+
return void 0;
|
|
1440
|
+
}
|
|
1441
|
+
function parseQuotaDetail(value) {
|
|
1442
|
+
if (!isRecord3(value)) return null;
|
|
1443
|
+
const entitlement = finiteNumber4(value["entitlement"]);
|
|
1444
|
+
const remaining = finiteNumber4(value["remaining"]);
|
|
1445
|
+
const percentRemaining = finiteNumber4(value["percent_remaining"]);
|
|
1446
|
+
const unlimited = booleanValue(value["unlimited"]);
|
|
1447
|
+
if (entitlement === void 0 || remaining === void 0 || percentRemaining === void 0 || unlimited === void 0) {
|
|
1448
|
+
return null;
|
|
1449
|
+
}
|
|
1450
|
+
return { entitlement, remaining, percentRemaining, unlimited };
|
|
1451
|
+
}
|
|
1452
|
+
function secondsUntil5(instant, now) {
|
|
1453
|
+
if (!instant) return void 0;
|
|
1454
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
1455
|
+
}
|
|
1456
|
+
function parseCopilotUserPayload(payload, now) {
|
|
1457
|
+
if (!isRecord3(payload)) return null;
|
|
1458
|
+
const snapshots = isRecord3(payload["quota_snapshots"]) ? payload["quota_snapshots"] : void 0;
|
|
1459
|
+
if (!snapshots) return null;
|
|
1460
|
+
const resetRaw = payload["quota_reset_date"];
|
|
1461
|
+
const resetsAt = typeof resetRaw === "string" && resetRaw.trim() && Number.isFinite(Date.parse(resetRaw)) ? new Date(Date.parse(resetRaw)).toISOString() : void 0;
|
|
1462
|
+
const windows = [];
|
|
1463
|
+
const premium = parseQuotaDetail(snapshots["premium_interactions"]);
|
|
1464
|
+
if (premium) {
|
|
1465
|
+
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;
|
|
1466
|
+
if (usedPercent !== null) {
|
|
1467
|
+
windows.push({
|
|
1468
|
+
id: "thirty-day",
|
|
1469
|
+
label: "Monthly",
|
|
1470
|
+
scope: "all",
|
|
1471
|
+
usedPercent,
|
|
1472
|
+
windowMinutes: 30 * 24 * 60,
|
|
1473
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
1474
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
1475
|
+
state: "fresh"
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
const chat = parseQuotaDetail(snapshots["chat"]);
|
|
1480
|
+
if (chat && !chat.unlimited && chat.entitlement > 0) {
|
|
1481
|
+
const usedPercent = Math.round(Math.min(100, (chat.entitlement - chat.remaining) / chat.entitlement * 100) * 10) / 10;
|
|
1482
|
+
windows.push({
|
|
1483
|
+
id: "chat-monthly",
|
|
1484
|
+
label: "Chat (monthly)",
|
|
1485
|
+
scope: "all",
|
|
1486
|
+
usedPercent,
|
|
1487
|
+
windowMinutes: 30 * 24 * 60,
|
|
1488
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
1489
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
1490
|
+
state: "fresh"
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
return windows.length > 0 ? windows : null;
|
|
1494
|
+
}
|
|
1495
|
+
function githubApiBase(tokens) {
|
|
1496
|
+
return (0, import_subscriptions6.copilotGitHubApiBase)(tokens.enterpriseUrl);
|
|
1497
|
+
}
|
|
1498
|
+
var CopilotAllowanceCollector = class {
|
|
1499
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore5.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch5.fetchUpstream)(url, init, { providerId: "copilot", accountId, redactBodies: true }), now = Date.now) {
|
|
1500
|
+
this.credentials = credentials;
|
|
1501
|
+
this.store = store;
|
|
1502
|
+
this.fetchImpl = fetchImpl;
|
|
1503
|
+
this.now = now;
|
|
1504
|
+
}
|
|
1505
|
+
credentials;
|
|
1506
|
+
store;
|
|
1507
|
+
fetchImpl;
|
|
1508
|
+
now;
|
|
1509
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
1510
|
+
async collectMany(accounts, options = {}) {
|
|
1511
|
+
const settled = await Promise.allSettled(
|
|
1512
|
+
accounts.map((account) => this.collect(account, options))
|
|
1513
|
+
);
|
|
1514
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
1515
|
+
}
|
|
1516
|
+
collect(account, options = {}) {
|
|
1517
|
+
const now = this.now();
|
|
1518
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
1519
|
+
const existing = this.store.get("copilot", account.id, now);
|
|
1520
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
1521
|
+
return Promise.resolve(existing);
|
|
1522
|
+
}
|
|
1523
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
1524
|
+
this.store.set(snapshot);
|
|
1525
|
+
return Promise.resolve(snapshot);
|
|
1526
|
+
}
|
|
1527
|
+
const cached = this.store.get("copilot", account.id, now);
|
|
1528
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
1529
|
+
return Promise.resolve(cached);
|
|
1530
|
+
}
|
|
1531
|
+
const running = this.inFlight.get(account.id);
|
|
1532
|
+
if (running) return running;
|
|
1533
|
+
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));
|
|
1534
|
+
this.inFlight.set(account.id, promise);
|
|
1535
|
+
return promise;
|
|
1536
|
+
}
|
|
1537
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
1538
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
1539
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
1540
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
1541
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
1542
|
+
}
|
|
1543
|
+
async fetchAccount(accountId, tokens) {
|
|
1544
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
|
|
1545
|
+
if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
|
|
1546
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
1547
|
+
if (response.status === 401 || response.status === 403) {
|
|
1548
|
+
const refreshed = await this.credentials.refreshAccountToken("copilot", accountId);
|
|
1549
|
+
if (!refreshed) return this.failureSnapshot(accountId, "copilot_usage_unauthorized", this.now());
|
|
1550
|
+
accessToken = await this.credentials.getAccessTokenForAccount("copilot", accountId);
|
|
1551
|
+
if (!accessToken) return this.failureSnapshot(accountId, "copilot_usage_token_unavailable", this.now());
|
|
1552
|
+
response = await this.request(accountId, accessToken, tokens);
|
|
1553
|
+
if (response.status === 401 || response.status === 403) {
|
|
1554
|
+
return this.failureSnapshot(accountId, "copilot_usage_unauthorized", this.now());
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
if (!response.ok) return this.failureSnapshot(accountId, "copilot_usage_http_error", this.now());
|
|
1558
|
+
let payload;
|
|
1559
|
+
try {
|
|
1560
|
+
payload = await response.json();
|
|
1561
|
+
} catch {
|
|
1562
|
+
return this.failureSnapshot(accountId, "copilot_usage_invalid_response", this.now());
|
|
1563
|
+
}
|
|
1564
|
+
const now = this.now();
|
|
1565
|
+
const windows = parseCopilotUserPayload(payload, now);
|
|
1566
|
+
const snapshot = {
|
|
1567
|
+
providerId: "copilot",
|
|
1568
|
+
accountId,
|
|
1569
|
+
source: "oauth-usage-api",
|
|
1570
|
+
observedAt: new Date(now).toISOString(),
|
|
1571
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1572
|
+
windows: windows ?? [
|
|
1573
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
|
|
1574
|
+
],
|
|
1575
|
+
...windows ? {} : { lastErrorCode: "copilot_usage_invalid_response" }
|
|
1576
|
+
};
|
|
1577
|
+
this.store.set(snapshot);
|
|
1578
|
+
return snapshot;
|
|
1579
|
+
}
|
|
1580
|
+
request(accountId, accessToken, tokens) {
|
|
1581
|
+
return this.fetchImpl(`${githubApiBase(tokens)}/copilot_internal/user`, {
|
|
1582
|
+
method: "GET",
|
|
1583
|
+
headers: {
|
|
1584
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1585
|
+
Accept: "application/json",
|
|
1586
|
+
"Content-Type": "application/json",
|
|
1587
|
+
...import_subscriptions6.COPILOT_GITHUB_HEADERS
|
|
1588
|
+
},
|
|
1589
|
+
signal: AbortSignal.timeout(15e3)
|
|
1590
|
+
}, accountId);
|
|
1591
|
+
}
|
|
1592
|
+
failureSnapshot(accountId, code, now) {
|
|
1593
|
+
const existing = this.store.get("copilot", accountId, now);
|
|
1594
|
+
const snapshot = existing ? {
|
|
1595
|
+
...existing,
|
|
1596
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1597
|
+
windows: existing.windows.map((window) => ({
|
|
1598
|
+
...window,
|
|
1599
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
1600
|
+
})),
|
|
1601
|
+
lastErrorCode: code
|
|
1602
|
+
} : {
|
|
1603
|
+
providerId: "copilot",
|
|
1604
|
+
accountId,
|
|
1605
|
+
source: "oauth-usage-api",
|
|
1606
|
+
observedAt: new Date(now).toISOString(),
|
|
1607
|
+
expiresAt: new Date(now + COPILOT_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1608
|
+
windows: [
|
|
1609
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unavailable" }
|
|
1610
|
+
],
|
|
1611
|
+
lastErrorCode: code
|
|
1612
|
+
};
|
|
1613
|
+
this.store.set(snapshot);
|
|
1614
|
+
return snapshot;
|
|
1615
|
+
}
|
|
1616
|
+
unsupportedSnapshot(accountId, now) {
|
|
1617
|
+
return {
|
|
1618
|
+
providerId: "copilot",
|
|
1619
|
+
accountId,
|
|
1620
|
+
source: "oauth-usage-api",
|
|
1621
|
+
observedAt: new Date(now).toISOString(),
|
|
1622
|
+
windows: [
|
|
1623
|
+
{ id: "thirty-day", label: "Monthly", scope: "all", usedPercent: null, state: "unsupported" }
|
|
1624
|
+
],
|
|
1625
|
+
lastErrorCode: "copilot_usage_unsupported_auth"
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
};
|
|
1629
|
+
|
|
1630
|
+
// src/allowance/OpenCodeGoAllowanceCollector.ts
|
|
1631
|
+
var import_AccountAllowanceStore6 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1632
|
+
var import_upstreamFetch6 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
1633
|
+
var import_subscriptions7 = require("@omnicross/subscriptions");
|
|
1634
|
+
var OPENCODEGO_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
1635
|
+
var OPENCODEGO_DEFAULT_GO_BASE = "https://opencode.ai/zen/go";
|
|
1636
|
+
function finitePercent3(value) {
|
|
199
1637
|
if (value === null || value === void 0 || value === "") return null;
|
|
200
|
-
const
|
|
201
|
-
return Number.isFinite(
|
|
1638
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
1639
|
+
return Number.isFinite(parsed) && parsed >= 0 && parsed <= 100 ? parsed : null;
|
|
202
1640
|
}
|
|
203
|
-
function
|
|
1641
|
+
function isoInstant2(value) {
|
|
204
1642
|
if (typeof value !== "string" || !value.trim()) return void 0;
|
|
205
1643
|
const time = Date.parse(value);
|
|
206
1644
|
return Number.isFinite(time) ? new Date(time).toISOString() : void 0;
|
|
207
1645
|
}
|
|
208
|
-
function
|
|
1646
|
+
function secondsUntil6(instant, now) {
|
|
209
1647
|
if (!instant) return void 0;
|
|
210
1648
|
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
211
1649
|
}
|
|
212
|
-
function
|
|
213
|
-
const
|
|
214
|
-
const
|
|
215
|
-
const
|
|
216
|
-
const isFiveHour = id === "five-hour";
|
|
1650
|
+
function windowFromPayload3(id, label, minutes, payload, now) {
|
|
1651
|
+
const statusRateLimited = payload?.status === "rate-limited";
|
|
1652
|
+
const usedPercent = statusRateLimited ? 100 : finitePercent3(payload?.percent);
|
|
1653
|
+
const resetsAt = isoInstant2(payload?.resetsAt);
|
|
217
1654
|
return {
|
|
218
1655
|
id,
|
|
219
|
-
label
|
|
220
|
-
scope:
|
|
221
|
-
modelFamily: isSonnet ? "sonnet" : void 0,
|
|
1656
|
+
label,
|
|
1657
|
+
scope: "all",
|
|
222
1658
|
usedPercent,
|
|
223
|
-
windowMinutes:
|
|
224
|
-
resetsAt,
|
|
225
|
-
remainingSeconds:
|
|
1659
|
+
windowMinutes: minutes,
|
|
1660
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
1661
|
+
remainingSeconds: secondsUntil6(resetsAt, now),
|
|
226
1662
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
227
1663
|
};
|
|
228
1664
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
{
|
|
232
|
-
id: "five-hour",
|
|
233
|
-
label: "5 hours",
|
|
234
|
-
scope: "all",
|
|
235
|
-
usedPercent: null,
|
|
236
|
-
windowMinutes: 5 * 60,
|
|
237
|
-
state
|
|
238
|
-
},
|
|
239
|
-
{
|
|
240
|
-
id: "seven-day",
|
|
241
|
-
label: "7 days",
|
|
242
|
-
scope: "all",
|
|
243
|
-
usedPercent: null,
|
|
244
|
-
windowMinutes: 7 * 24 * 60,
|
|
245
|
-
state
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
id: "seven-day-sonnet",
|
|
249
|
-
label: "7 days \xB7 Sonnet",
|
|
250
|
-
scope: "model-family",
|
|
251
|
-
modelFamily: "sonnet",
|
|
252
|
-
usedPercent: null,
|
|
253
|
-
windowMinutes: 7 * 24 * 60,
|
|
254
|
-
state
|
|
255
|
-
}
|
|
256
|
-
];
|
|
257
|
-
}
|
|
258
|
-
function hasHeader(headers, name) {
|
|
259
|
-
const wanted = name.toLowerCase();
|
|
260
|
-
return Object.keys(headers).some((key) => key.toLowerCase() === wanted);
|
|
261
|
-
}
|
|
262
|
-
var ClaudeAllowanceCollector = class {
|
|
263
|
-
constructor(credentials, store = (0, import_AccountAllowanceStore.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch.fetchUpstream)(url, init, { providerId: "claude", accountId }), identityStore = (0, import_SubscriptionIdentityStore.getSharedIdentityStore)(), now = Date.now) {
|
|
1665
|
+
var OpenCodeGoAllowanceCollector = class {
|
|
1666
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore6.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch6.fetchUpstream)(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
|
|
264
1667
|
this.credentials = credentials;
|
|
265
1668
|
this.store = store;
|
|
266
1669
|
this.fetchImpl = fetchImpl;
|
|
267
|
-
this.identityStore = identityStore;
|
|
268
1670
|
this.now = now;
|
|
269
1671
|
}
|
|
270
1672
|
credentials;
|
|
271
1673
|
store;
|
|
272
1674
|
fetchImpl;
|
|
273
|
-
identityStore;
|
|
274
1675
|
now;
|
|
275
1676
|
inFlight = /* @__PURE__ */ new Map();
|
|
276
1677
|
async collectMany(accounts, options = {}) {
|
|
@@ -279,124 +1680,77 @@ var ClaudeAllowanceCollector = class {
|
|
|
279
1680
|
}
|
|
280
1681
|
collect(account, options = {}) {
|
|
281
1682
|
const now = this.now();
|
|
282
|
-
const
|
|
283
|
-
if (unsupported) {
|
|
284
|
-
|
|
285
|
-
if (existing?.windows.every((window) => window.state === "unsupported")) return Promise.resolve(existing);
|
|
286
|
-
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
287
|
-
this.store.set(snapshot);
|
|
288
|
-
return Promise.resolve(snapshot);
|
|
1683
|
+
const cached = this.store.get("opencodego", account.id, now);
|
|
1684
|
+
if (!options.force && cached && (cached.windows.every((window) => window.state === "unsupported") || cached.expiresAt && Date.parse(cached.expiresAt) > now + (options.refreshAheadMs ?? 0))) {
|
|
1685
|
+
return Promise.resolve(cached);
|
|
289
1686
|
}
|
|
290
|
-
const cached = this.store.get("claude", account.id, now);
|
|
291
|
-
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) return Promise.resolve(cached);
|
|
292
1687
|
const running = this.inFlight.get(account.id);
|
|
293
1688
|
if (running) return running;
|
|
294
|
-
const promise = this.fetchAccount(account
|
|
1689
|
+
const promise = this.fetchAccount(account).catch(() => this.failureSnapshot(account.id, this.now())).finally(() => this.inFlight.delete(account.id));
|
|
295
1690
|
this.inFlight.set(account.id, promise);
|
|
296
1691
|
return promise;
|
|
297
1692
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (
|
|
301
|
-
const
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (
|
|
308
|
-
|
|
309
|
-
if (response.status === 401) {
|
|
310
|
-
const refreshed = await this.credentials.refreshAccountToken("claude", accountId);
|
|
311
|
-
if (!refreshed) return this.failureSnapshot(accountId, "claude_usage_unauthorized", this.now());
|
|
312
|
-
token = await this.credentials.getAccessTokenForAccount("claude", accountId);
|
|
313
|
-
if (!token) return this.failureSnapshot(accountId, "claude_usage_token_unavailable", this.now());
|
|
314
|
-
response = await this.request(accountId, token);
|
|
315
|
-
}
|
|
316
|
-
if (response.status === 403) {
|
|
317
|
-
const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "claude_usage_unsupported");
|
|
318
|
-
this.store.set(snapshot2);
|
|
319
|
-
return snapshot2;
|
|
320
|
-
}
|
|
321
|
-
if (!response.ok) {
|
|
322
|
-
return this.failureSnapshot(accountId, "claude_usage_http_error", this.now());
|
|
1693
|
+
async fetchAccount(account) {
|
|
1694
|
+
const apiKey = await this.credentials.getAccessTokenForAccount("opencodego", account.id);
|
|
1695
|
+
if (!apiKey) return this.failureSnapshot(account.id, this.now());
|
|
1696
|
+
const base = account.tokens.baseUrl ? (0, import_subscriptions7.normalizeOpenCodeGoBaseUrl)(account.tokens.baseUrl) : OPENCODEGO_DEFAULT_GO_BASE;
|
|
1697
|
+
const response = await this.fetchImpl(`${base}/v1/usage`, {
|
|
1698
|
+
method: "GET",
|
|
1699
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
1700
|
+
signal: AbortSignal.timeout(15e3)
|
|
1701
|
+
}, account.id);
|
|
1702
|
+
if (response.status === 401 || response.status === 403) {
|
|
1703
|
+
return this.failureSnapshot(account.id, this.now(), "opencodego_usage_unauthorized");
|
|
323
1704
|
}
|
|
1705
|
+
if (!response.ok) return this.failureSnapshot(account.id, this.now());
|
|
324
1706
|
let payload;
|
|
325
1707
|
try {
|
|
326
1708
|
payload = await response.json();
|
|
327
1709
|
} catch {
|
|
328
|
-
return this.failureSnapshot(
|
|
329
|
-
}
|
|
330
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
331
|
-
return this.failureSnapshot(accountId, "claude_usage_invalid_response", this.now());
|
|
1710
|
+
return this.failureSnapshot(account.id, this.now());
|
|
332
1711
|
}
|
|
1712
|
+
const usage = payload && typeof payload === "object" && !Array.isArray(payload) ? payload.usage : void 0;
|
|
333
1713
|
const now = this.now();
|
|
334
|
-
const usage = payload;
|
|
335
1714
|
const snapshot = {
|
|
336
|
-
providerId: "
|
|
337
|
-
accountId,
|
|
1715
|
+
providerId: "opencodego",
|
|
1716
|
+
accountId: account.id,
|
|
338
1717
|
source: "oauth-usage-api",
|
|
339
1718
|
observedAt: new Date(now).toISOString(),
|
|
340
|
-
expiresAt: new Date(now +
|
|
1719
|
+
expiresAt: new Date(now + OPENCODEGO_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1720
|
+
// Monthly deliberately omitted (module doc).
|
|
341
1721
|
windows: [
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
windowFromPayload("seven-day-sonnet", usage.seven_day_sonnet, now)
|
|
1722
|
+
windowFromPayload3("five-hour", "5 hours", 5 * 60, usage?.rolling ?? void 0, now),
|
|
1723
|
+
windowFromPayload3("seven-day", "7 days", 7 * 24 * 60, usage?.weekly ?? void 0, now)
|
|
345
1724
|
]
|
|
346
1725
|
};
|
|
347
1726
|
this.store.set(snapshot);
|
|
348
1727
|
return snapshot;
|
|
349
1728
|
}
|
|
350
|
-
|
|
351
|
-
const
|
|
352
|
-
Authorization: `Bearer ${token}`,
|
|
353
|
-
Accept: "application/json",
|
|
354
|
-
"Content-Type": "application/json",
|
|
355
|
-
"anthropic-beta": "oauth-2025-04-20",
|
|
356
|
-
"Accept-Language": "en-US,en;q=0.9"
|
|
357
|
-
};
|
|
358
|
-
(0, import_fingerprintHeaders.applyFingerprint)(this.identityStore, headers, "claude", accountId, void 0);
|
|
359
|
-
if (!hasHeader(headers, "user-agent")) {
|
|
360
|
-
headers["User-Agent"] = "claude-cli/2.0.53 (external, cli)";
|
|
361
|
-
}
|
|
362
|
-
return this.fetchImpl(CLAUDE_USAGE_URL, {
|
|
363
|
-
method: "GET",
|
|
364
|
-
headers,
|
|
365
|
-
signal: AbortSignal.timeout(15e3)
|
|
366
|
-
}, accountId);
|
|
367
|
-
}
|
|
368
|
-
failureSnapshot(accountId, code, now) {
|
|
369
|
-
const existing = this.store.get("claude", accountId, now);
|
|
1729
|
+
failureSnapshot(accountId, now, code = "opencodego_usage_request_failed") {
|
|
1730
|
+
const existing = this.store.get("opencodego", accountId, now);
|
|
370
1731
|
const snapshot = existing ? {
|
|
371
1732
|
...existing,
|
|
372
|
-
expiresAt: new Date(now +
|
|
1733
|
+
expiresAt: new Date(now + OPENCODEGO_ALLOWANCE_CACHE_MS).toISOString(),
|
|
373
1734
|
windows: existing.windows.map((window) => ({
|
|
374
1735
|
...window,
|
|
375
|
-
state: window.
|
|
1736
|
+
state: window.usedPercent !== null || window.resetsAt ? "stale" : window.state
|
|
376
1737
|
})),
|
|
377
1738
|
lastErrorCode: code
|
|
378
1739
|
} : {
|
|
379
|
-
providerId: "
|
|
1740
|
+
providerId: "opencodego",
|
|
380
1741
|
accountId,
|
|
381
1742
|
source: "oauth-usage-api",
|
|
382
1743
|
observedAt: new Date(now).toISOString(),
|
|
383
|
-
expiresAt: new Date(now +
|
|
384
|
-
windows:
|
|
1744
|
+
expiresAt: new Date(now + OPENCODEGO_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1745
|
+
windows: [
|
|
1746
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
1747
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
1748
|
+
],
|
|
385
1749
|
lastErrorCode: code
|
|
386
1750
|
};
|
|
387
1751
|
this.store.set(snapshot);
|
|
388
1752
|
return snapshot;
|
|
389
1753
|
}
|
|
390
|
-
unsupportedSnapshot(accountId, now, code = "claude_usage_unsupported_auth") {
|
|
391
|
-
return {
|
|
392
|
-
providerId: "claude",
|
|
393
|
-
accountId,
|
|
394
|
-
source: "oauth-usage-api",
|
|
395
|
-
observedAt: new Date(now).toISOString(),
|
|
396
|
-
windows: emptyClaudeWindows("unsupported"),
|
|
397
|
-
lastErrorCode: code
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
1754
|
};
|
|
401
1755
|
|
|
402
1756
|
// src/allowance/AccountAllowanceService.ts
|
|
@@ -414,26 +1768,34 @@ function codexUnavailable(accountId, now) {
|
|
|
414
1768
|
};
|
|
415
1769
|
}
|
|
416
1770
|
var AccountAllowanceService = class {
|
|
417
|
-
constructor(credentials, store = (0,
|
|
1771
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore7.getSharedAccountAllowanceStore)(), collector, codexCollector, kimiCollector, opencodegoCollector, grokCollector, copilotCollector, now = Date.now) {
|
|
418
1772
|
this.credentials = credentials;
|
|
419
1773
|
this.store = store;
|
|
420
1774
|
this.now = now;
|
|
421
1775
|
this.claudeCollector = collector ?? new ClaudeAllowanceCollector(credentials, store);
|
|
1776
|
+
this.codexCollector = codexCollector ?? new CodexAllowanceCollector(credentials, store);
|
|
1777
|
+
this.kimiCollector = kimiCollector ?? new KimiAllowanceCollector(credentials, store);
|
|
1778
|
+
this.opencodegoCollector = opencodegoCollector ?? new OpenCodeGoAllowanceCollector(credentials, store);
|
|
1779
|
+
this.grokCollector = grokCollector ?? new GrokAllowanceCollector(credentials, store);
|
|
1780
|
+
this.copilotCollector = copilotCollector ?? new CopilotAllowanceCollector(credentials, store);
|
|
422
1781
|
}
|
|
423
1782
|
credentials;
|
|
424
1783
|
store;
|
|
425
1784
|
now;
|
|
426
1785
|
claudeCollector;
|
|
1786
|
+
codexCollector;
|
|
1787
|
+
kimiCollector;
|
|
1788
|
+
grokCollector;
|
|
1789
|
+
copilotCollector;
|
|
1790
|
+
opencodegoCollector;
|
|
427
1791
|
/**
|
|
428
|
-
* Read all/filtered snapshots. Claude's five-minute
|
|
429
|
-
*
|
|
1792
|
+
* Read all/filtered snapshots. Claude's and Codex's five-minute caches are
|
|
1793
|
+
* refreshed lazily on read (Codex polls `/backend-api/wham/usage`; the
|
|
1794
|
+
* passive `x-codex-*` header tap still feeds mid-flight updates).
|
|
430
1795
|
*/
|
|
431
1796
|
async list(filter = {}) {
|
|
432
1797
|
const config = await this.credentials.getFullConfig();
|
|
433
|
-
this.store.pruneToKnownAccounts(
|
|
434
|
-
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
435
|
-
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id }))
|
|
436
|
-
]);
|
|
1798
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
437
1799
|
const wantsClaude = !filter.providerId || filter.providerId === "claude";
|
|
438
1800
|
const claudeAccounts = (config.claudeAccounts ?? []).filter(
|
|
439
1801
|
(account) => !filter.accountId || account.id === filter.accountId
|
|
@@ -444,39 +1806,124 @@ var AccountAllowanceService = class {
|
|
|
444
1806
|
(account) => !filter.accountId || account.id === filter.accountId
|
|
445
1807
|
);
|
|
446
1808
|
if (wantsCodex) {
|
|
1809
|
+
await this.codexCollector.collectMany(codexAccounts);
|
|
447
1810
|
for (const account of codexAccounts) {
|
|
448
1811
|
if (!this.store.get("codex", account.id)) this.store.set(codexUnavailable(account.id, this.now()));
|
|
449
1812
|
}
|
|
450
1813
|
}
|
|
1814
|
+
const wantsKimi = !filter.providerId || filter.providerId === "kimi";
|
|
1815
|
+
const kimiAccounts = (config.kimiAccounts ?? []).filter(
|
|
1816
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
1817
|
+
);
|
|
1818
|
+
if (wantsKimi) await this.kimiCollector.collectMany(kimiAccounts);
|
|
1819
|
+
const wantsOpenCodeGo = !filter.providerId || filter.providerId === "opencodego";
|
|
1820
|
+
const opencodegoAccounts = (config.opencodegoAccounts ?? []).filter(
|
|
1821
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
1822
|
+
);
|
|
1823
|
+
if (wantsOpenCodeGo) await this.opencodegoCollector.collectMany(opencodegoAccounts);
|
|
1824
|
+
const wantsGrok = !filter.providerId || filter.providerId === "grok";
|
|
1825
|
+
const grokAccounts = (config.grokAccounts ?? []).filter(
|
|
1826
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
1827
|
+
);
|
|
1828
|
+
if (wantsGrok) await this.grokCollector.collectMany(grokAccounts);
|
|
1829
|
+
const wantsCopilot = !filter.providerId || filter.providerId === "copilot";
|
|
1830
|
+
const copilotAccounts = (config.copilotAccounts ?? []).filter(
|
|
1831
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
1832
|
+
);
|
|
1833
|
+
if (wantsCopilot) await this.copilotCollector.collectMany(copilotAccounts);
|
|
451
1834
|
const known = /* @__PURE__ */ new Set();
|
|
452
1835
|
if (wantsClaude) for (const account of claudeAccounts) known.add(`claude\0${account.id}`);
|
|
453
1836
|
if (wantsCodex) for (const account of codexAccounts) known.add(`codex\0${account.id}`);
|
|
1837
|
+
if (wantsKimi) for (const account of kimiAccounts) known.add(`kimi\0${account.id}`);
|
|
1838
|
+
if (wantsOpenCodeGo) for (const account of opencodegoAccounts) known.add(`opencodego\0${account.id}`);
|
|
1839
|
+
if (wantsGrok) for (const account of grokAccounts) known.add(`grok\0${account.id}`);
|
|
1840
|
+
if (wantsCopilot) for (const account of copilotAccounts) known.add(`copilot\0${account.id}`);
|
|
454
1841
|
return this.store.list(filter).filter((snapshot) => known.has(`${snapshot.providerId}\0${snapshot.accountId}`));
|
|
455
1842
|
}
|
|
1843
|
+
knownAccounts(config) {
|
|
1844
|
+
return [
|
|
1845
|
+
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
1846
|
+
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id })),
|
|
1847
|
+
...(config.kimiAccounts ?? []).map((account) => ({ providerId: "kimi", accountId: account.id })),
|
|
1848
|
+
...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id })),
|
|
1849
|
+
...(config.grokAccounts ?? []).map((account) => ({ providerId: "grok", accountId: account.id })),
|
|
1850
|
+
...(config.copilotAccounts ?? []).map((account) => ({ providerId: "copilot", accountId: account.id }))
|
|
1851
|
+
];
|
|
1852
|
+
}
|
|
456
1853
|
/** Force-refresh Claude usage for one account or every stored Claude account. */
|
|
457
1854
|
async refreshClaude(accountId) {
|
|
458
1855
|
const config = await this.credentials.getFullConfig();
|
|
459
|
-
this.store.pruneToKnownAccounts(
|
|
460
|
-
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
461
|
-
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id }))
|
|
462
|
-
]);
|
|
1856
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
463
1857
|
const accounts = (config.claudeAccounts ?? []).filter(
|
|
464
1858
|
(account) => !accountId || account.id === accountId
|
|
465
1859
|
);
|
|
466
1860
|
return this.claudeCollector.collectMany(accounts, { force: true });
|
|
467
1861
|
}
|
|
468
1862
|
/**
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
*
|
|
1863
|
+
* Force-refresh Codex usage (`/backend-api/wham/usage`) for one account or
|
|
1864
|
+
* every stored Codex account. Replaces the old probe-request workaround —
|
|
1865
|
+
* no quota is spent reading the usage endpoint.
|
|
1866
|
+
*/
|
|
1867
|
+
async refreshCodex(accountId) {
|
|
1868
|
+
const config = await this.credentials.getFullConfig();
|
|
1869
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1870
|
+
const accounts = (config.codexAccounts ?? []).filter(
|
|
1871
|
+
(account) => !accountId || account.id === accountId
|
|
1872
|
+
);
|
|
1873
|
+
return this.codexCollector.collectMany(accounts, { force: true });
|
|
1874
|
+
}
|
|
1875
|
+
/** Force-refresh OpenCodeGo usage (`{go}/v1/usage`) for one/all accounts. */
|
|
1876
|
+
async refreshOpenCodeGo(accountId) {
|
|
1877
|
+
const config = await this.credentials.getFullConfig();
|
|
1878
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1879
|
+
const accounts = (config.opencodegoAccounts ?? []).filter(
|
|
1880
|
+
(account) => !accountId || account.id === accountId
|
|
1881
|
+
);
|
|
1882
|
+
return this.opencodegoCollector.collectMany(accounts, { force: true });
|
|
1883
|
+
}
|
|
1884
|
+
/** Force-refresh Kimi usage (`/coding/v1/usages`) for one/all accounts. */
|
|
1885
|
+
async refreshKimi(accountId) {
|
|
1886
|
+
const config = await this.credentials.getFullConfig();
|
|
1887
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1888
|
+
const accounts = (config.kimiAccounts ?? []).filter(
|
|
1889
|
+
(account) => !accountId || account.id === accountId
|
|
1890
|
+
);
|
|
1891
|
+
return this.kimiCollector.collectMany(accounts, { force: true });
|
|
1892
|
+
}
|
|
1893
|
+
/** Force-refresh Copilot usage (copilot_internal/user) for one/all accounts. */
|
|
1894
|
+
async refreshCopilot(accountId) {
|
|
1895
|
+
const config = await this.credentials.getFullConfig();
|
|
1896
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1897
|
+
const accounts = (config.copilotAccounts ?? []).filter(
|
|
1898
|
+
(account) => !accountId || account.id === accountId
|
|
1899
|
+
);
|
|
1900
|
+
return this.copilotCollector.collectMany(accounts, { force: true });
|
|
1901
|
+
}
|
|
1902
|
+
/** Force-refresh Grok usage (CLI billing proxy) for one/all accounts. */
|
|
1903
|
+
async refreshGrok(accountId) {
|
|
1904
|
+
const config = await this.credentials.getFullConfig();
|
|
1905
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1906
|
+
const accounts = (config.grokAccounts ?? []).filter(
|
|
1907
|
+
(account) => !accountId || account.id === accountId
|
|
1908
|
+
);
|
|
1909
|
+
return this.grokCollector.collectMany(accounts, { force: true });
|
|
1910
|
+
}
|
|
1911
|
+
/**
|
|
1912
|
+
* Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
|
|
1913
|
+
* collectors preserve their cache + per-account in-flight coalescing; a tick
|
|
1914
|
+
* normally performs no network I/O. (Codex joined the warm path when it
|
|
1915
|
+
* gained an active `/wham/usage` collector — the passive `x-codex-*` header
|
|
1916
|
+
* tap alone could not keep the policy fed while idle.)
|
|
472
1917
|
*/
|
|
473
1918
|
async maintainClaudeCache(refreshAheadMs) {
|
|
474
1919
|
const config = await this.credentials.getFullConfig();
|
|
475
|
-
this.store.pruneToKnownAccounts(
|
|
476
|
-
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
477
|
-
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id }))
|
|
478
|
-
]);
|
|
1920
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
479
1921
|
await this.claudeCollector.collectMany(config.claudeAccounts ?? [], { refreshAheadMs });
|
|
1922
|
+
await this.codexCollector.collectMany(config.codexAccounts ?? [], { refreshAheadMs });
|
|
1923
|
+
await this.kimiCollector.collectMany(config.kimiAccounts ?? [], { refreshAheadMs });
|
|
1924
|
+
await this.opencodegoCollector.collectMany(config.opencodegoAccounts ?? [], { refreshAheadMs });
|
|
1925
|
+
await this.grokCollector.collectMany(config.grokAccounts ?? [], { refreshAheadMs });
|
|
1926
|
+
await this.copilotCollector.collectMany(config.copilotAccounts ?? [], { refreshAheadMs });
|
|
480
1927
|
}
|
|
481
1928
|
/** Remove a cache row as soon as an account is deleted by the admin path. */
|
|
482
1929
|
removeAccountSnapshot(providerId, accountId) {
|
|
@@ -571,7 +2018,7 @@ var ClaudeAllowanceRefreshScheduler = class {
|
|
|
571
2018
|
var import_node_crypto2 = require("crypto");
|
|
572
2019
|
var import_node_fs = require("fs");
|
|
573
2020
|
var import_node_path = require("path");
|
|
574
|
-
var
|
|
2021
|
+
var import_AccountAllowanceStore8 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
575
2022
|
var ACCOUNT_ALLOWANCE_CACHE_VERSION = 1;
|
|
576
2023
|
var MAX_PERSISTED_ALLOWANCE_SNAPSHOTS = 256;
|
|
577
2024
|
var MAX_ALLOWANCE_CACHE_BYTES = 1e6;
|
|
@@ -600,7 +2047,7 @@ var JsonAccountAllowancePersistence = class {
|
|
|
600
2047
|
save(snapshots) {
|
|
601
2048
|
const rows = [];
|
|
602
2049
|
for (const snapshot of snapshots) {
|
|
603
|
-
const normalized2 = (0,
|
|
2050
|
+
const normalized2 = (0, import_AccountAllowanceStore8.normalizeAccountAllowanceSnapshot)(snapshot);
|
|
604
2051
|
if (!normalized2) continue;
|
|
605
2052
|
rows.push(normalized2);
|
|
606
2053
|
if (rows.length >= MAX_PERSISTED_ALLOWANCE_SNAPSHOTS) break;
|
|
@@ -883,7 +2330,8 @@ var import_outbound_api5 = require("@omnicross/core/outbound-api");
|
|
|
883
2330
|
var import_image_generation_types = require("@omnicross/contracts/image-generation-types");
|
|
884
2331
|
var import_AccountAllowanceScheduling2 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
885
2332
|
var import_SubscriptionAccountHealth = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
886
|
-
var
|
|
2333
|
+
var import_upstreamFetch9 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
2334
|
+
var import_core3 = require("@omnicross/core");
|
|
887
2335
|
|
|
888
2336
|
// src/image-generation/imagesConfigValidation.ts
|
|
889
2337
|
var import_outbound_api = require("@omnicross/core/outbound-api");
|
|
@@ -1179,6 +2627,7 @@ async function applyServerConfigTransaction(current, next, deps) {
|
|
|
1179
2627
|
|
|
1180
2628
|
// src/config.ts
|
|
1181
2629
|
var import_node_fs4 = require("fs");
|
|
2630
|
+
var import_core = require("@omnicross/core");
|
|
1182
2631
|
|
|
1183
2632
|
// src/secrets/envelope.ts
|
|
1184
2633
|
var import_node_crypto4 = require("crypto");
|
|
@@ -1590,6 +3039,18 @@ var FORMAT_AXIS_TRANSFORMERS = [
|
|
|
1590
3039
|
"openai-response",
|
|
1591
3040
|
"gemini-code-assist"
|
|
1592
3041
|
];
|
|
3042
|
+
function validateExtraHeaders(raw) {
|
|
3043
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
3044
|
+
const reserved = import_core.EXTRA_HEADER_RESERVED_NAMES;
|
|
3045
|
+
const out = {};
|
|
3046
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
3047
|
+
if (!name.trim()) continue;
|
|
3048
|
+
if (typeof value !== "string") continue;
|
|
3049
|
+
if (reserved.has(name.toLowerCase())) continue;
|
|
3050
|
+
out[name] = value;
|
|
3051
|
+
}
|
|
3052
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
3053
|
+
}
|
|
1593
3054
|
function validateApiKeys(raw) {
|
|
1594
3055
|
if (!Array.isArray(raw)) return void 0;
|
|
1595
3056
|
const out = [];
|
|
@@ -1803,6 +3264,9 @@ function validateProvider(raw, index) {
|
|
|
1803
3264
|
apiVersion,
|
|
1804
3265
|
maxConcurrency,
|
|
1805
3266
|
modelsEndpoint,
|
|
3267
|
+
// Static extra headers: load-guard (reserved names dropped), collapse-to-
|
|
3268
|
+
// undefined; enforced by the outbound header funnel + admin probes.
|
|
3269
|
+
extraHeaders: validateExtraHeaders(p["extraHeaders"]),
|
|
1806
3270
|
// Provider transformer config (app-parity child 5): load-guard, collapse-to-
|
|
1807
3271
|
// undefined; non-secret; ENFORCED via resolveTransformerChain (parity-2 child 2).
|
|
1808
3272
|
// Format-axis entries are stripped by `migrateFormatAxis` — `use[]` is the
|
|
@@ -1872,7 +3336,7 @@ var import_node_crypto6 = require("crypto");
|
|
|
1872
3336
|
var import_node_fs6 = require("fs");
|
|
1873
3337
|
var import_node_os3 = require("os");
|
|
1874
3338
|
var import_node_path6 = require("path");
|
|
1875
|
-
var
|
|
3339
|
+
var import_core2 = require("@omnicross/core");
|
|
1876
3340
|
|
|
1877
3341
|
// src/integrations/codexAuthHelper.ts
|
|
1878
3342
|
var import_node_path4 = require("path");
|
|
@@ -2353,7 +3817,7 @@ var IntegrationManager = class {
|
|
|
2353
3817
|
if (!secret) {
|
|
2354
3818
|
throw new IntegrationConflictError("The selected access key cannot be revealed and cannot power a CLI integration.");
|
|
2355
3819
|
}
|
|
2356
|
-
const effective = [...(0,
|
|
3820
|
+
const effective = [...(0, import_core2.effectiveOutboundPermissions)(row.allowedEndpoints)];
|
|
2357
3821
|
const previousPermissions = row.allowedEndpoints === void 0 ? [...effective] : [...row.allowedEndpoints];
|
|
2358
3822
|
const nextPermissions = [...effective];
|
|
2359
3823
|
for (const required of REQUIRED_PERMISSIONS[client]) {
|
|
@@ -2451,7 +3915,7 @@ var IntegrationManager = class {
|
|
|
2451
3915
|
return { binding, row, secret, created: false };
|
|
2452
3916
|
}
|
|
2453
3917
|
async createManagedClientKey(client, state) {
|
|
2454
|
-
const created = await (0,
|
|
3918
|
+
const created = await (0, import_core2.createIntegrationKey)(
|
|
2455
3919
|
this.options.keyDb,
|
|
2456
3920
|
`Omnicross ${displayClient(client)} integration`,
|
|
2457
3921
|
[...REQUIRED_PERMISSIONS[client]]
|
|
@@ -2543,7 +4007,7 @@ var IntegrationManager = class {
|
|
|
2543
4007
|
const row = rows.find((candidate) => candidate.id === keyId);
|
|
2544
4008
|
if (!row) return { usable: false, message: "The bound access key no longer exists." };
|
|
2545
4009
|
const secret = legacy?.secret ?? await this.options.keyDb.outboundApiKeysReveal(keyId) ?? void 0;
|
|
2546
|
-
const allowedEndpoints = [...(0,
|
|
4010
|
+
const allowedEndpoints = [...(0, import_core2.effectiveOutboundPermissions)(row.allowedEndpoints)];
|
|
2547
4011
|
const status = {
|
|
2548
4012
|
id: row.id,
|
|
2549
4013
|
name: row.name,
|
|
@@ -2587,7 +4051,7 @@ var IntegrationManager = class {
|
|
|
2587
4051
|
}
|
|
2588
4052
|
};
|
|
2589
4053
|
function hasRequiredPermissions(row, client) {
|
|
2590
|
-
const allowed = (0,
|
|
4054
|
+
const allowed = (0, import_core2.effectiveOutboundPermissions)(row.allowedEndpoints);
|
|
2591
4055
|
return REQUIRED_PERMISSIONS[client].every((permission) => allowed.includes(permission));
|
|
2592
4056
|
}
|
|
2593
4057
|
function samePermissions(a, b) {
|
|
@@ -2761,7 +4225,8 @@ function listMappablePresets() {
|
|
|
2761
4225
|
description: preset.description,
|
|
2762
4226
|
features: preset.features,
|
|
2763
4227
|
website: preset.website,
|
|
2764
|
-
modelsEndpoint: preset.modelsEndpoint
|
|
4228
|
+
modelsEndpoint: preset.modelsEndpoint,
|
|
4229
|
+
extraHeaders: preset.extraHeaders
|
|
2765
4230
|
});
|
|
2766
4231
|
}
|
|
2767
4232
|
return { mappable, excluded };
|
|
@@ -2851,11 +4316,11 @@ function preserveOutboundProxySecrets(incoming, current) {
|
|
|
2851
4316
|
}
|
|
2852
4317
|
|
|
2853
4318
|
// src/proxy/upstreamProxyResolver.ts
|
|
2854
|
-
var
|
|
4319
|
+
var import_upstreamFetch7 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
2855
4320
|
var serverProxy;
|
|
2856
4321
|
function setServerProxyConfig(proxy) {
|
|
2857
4322
|
serverProxy = proxy;
|
|
2858
|
-
(0,
|
|
4323
|
+
(0, import_upstreamFetch7.bumpUpstreamProxyGeneration)();
|
|
2859
4324
|
}
|
|
2860
4325
|
function getServerProxyConfig() {
|
|
2861
4326
|
return serverProxy;
|
|
@@ -2923,14 +4388,17 @@ function createUpstreamProxyResolver(src = {}) {
|
|
|
2923
4388
|
}
|
|
2924
4389
|
|
|
2925
4390
|
// src/admin/accountsOAuth.ts
|
|
2926
|
-
var
|
|
4391
|
+
var import_subscriptions8 = require("@omnicross/subscriptions");
|
|
2927
4392
|
|
|
2928
4393
|
// src/admin/accountsWrite.ts
|
|
2929
4394
|
var VALID_PROVIDER_IDS = [
|
|
2930
4395
|
"claude",
|
|
2931
4396
|
"codex",
|
|
2932
4397
|
"gemini",
|
|
2933
|
-
"opencodego"
|
|
4398
|
+
"opencodego",
|
|
4399
|
+
"kimi",
|
|
4400
|
+
"grok",
|
|
4401
|
+
"copilot"
|
|
2934
4402
|
];
|
|
2935
4403
|
function asSubscriptionProviderId(id) {
|
|
2936
4404
|
return VALID_PROVIDER_IDS.includes(id) ? id : null;
|
|
@@ -3066,6 +4534,52 @@ function validateGemini(body) {
|
|
|
3066
4534
|
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "lastRefreshedAt", "errorMessage"]);
|
|
3067
4535
|
return out;
|
|
3068
4536
|
}
|
|
4537
|
+
function validateKimi(body) {
|
|
4538
|
+
const authMethod = str(body["authMethod"]);
|
|
4539
|
+
const status = str(body["status"]);
|
|
4540
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
4541
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
4542
|
+
const out = {
|
|
4543
|
+
authMethod,
|
|
4544
|
+
status
|
|
4545
|
+
};
|
|
4546
|
+
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "deviceId", "lastRefreshedAt", "errorMessage"]);
|
|
4547
|
+
return out;
|
|
4548
|
+
}
|
|
4549
|
+
function validateGrok(body) {
|
|
4550
|
+
const authMethod = str(body["authMethod"]);
|
|
4551
|
+
const status = str(body["status"]);
|
|
4552
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
4553
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
4554
|
+
const out = {
|
|
4555
|
+
authMethod,
|
|
4556
|
+
status
|
|
4557
|
+
};
|
|
4558
|
+
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "lastRefreshedAt", "errorMessage"]);
|
|
4559
|
+
return out;
|
|
4560
|
+
}
|
|
4561
|
+
function validateCopilot(body) {
|
|
4562
|
+
const authMethod = str(body["authMethod"]);
|
|
4563
|
+
const status = str(body["status"]);
|
|
4564
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
4565
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
4566
|
+
const out = {
|
|
4567
|
+
authMethod,
|
|
4568
|
+
status
|
|
4569
|
+
};
|
|
4570
|
+
copyOptional(out, body, [
|
|
4571
|
+
"accessToken",
|
|
4572
|
+
"refreshToken",
|
|
4573
|
+
"expiresAt",
|
|
4574
|
+
"accountId",
|
|
4575
|
+
"email",
|
|
4576
|
+
"apiEndpoint",
|
|
4577
|
+
"enterpriseUrl",
|
|
4578
|
+
"lastRefreshedAt",
|
|
4579
|
+
"errorMessage"
|
|
4580
|
+
]);
|
|
4581
|
+
return out;
|
|
4582
|
+
}
|
|
3069
4583
|
function validateOpenCodeGo(body) {
|
|
3070
4584
|
const authMethod = str(body["authMethod"]);
|
|
3071
4585
|
const status = str(body["status"]);
|
|
@@ -3101,6 +4615,12 @@ function validateTokenBody(providerId, body) {
|
|
|
3101
4615
|
return validateGemini(body);
|
|
3102
4616
|
case "opencodego":
|
|
3103
4617
|
return validateOpenCodeGo(body);
|
|
4618
|
+
case "kimi":
|
|
4619
|
+
return validateKimi(body);
|
|
4620
|
+
case "grok":
|
|
4621
|
+
return validateGrok(body);
|
|
4622
|
+
case "copilot":
|
|
4623
|
+
return validateCopilot(body);
|
|
3104
4624
|
default:
|
|
3105
4625
|
return null;
|
|
3106
4626
|
}
|
|
@@ -3130,37 +4650,37 @@ async function statusEntryFor(reader, providerId) {
|
|
|
3130
4650
|
|
|
3131
4651
|
// src/admin/accountsOAuth.ts
|
|
3132
4652
|
var OAUTH_HTTP_PROVIDERS = /* @__PURE__ */ new Set(["claude", "gemini"]);
|
|
3133
|
-
function
|
|
4653
|
+
function err5(status, message) {
|
|
3134
4654
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
3135
4655
|
}
|
|
3136
4656
|
function handleOAuthStart(providerId, deps) {
|
|
3137
4657
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
3138
|
-
return
|
|
4658
|
+
return err5(400, `oauth not available for provider '${providerId}'`);
|
|
3139
4659
|
}
|
|
3140
|
-
const flow = providerId === "claude" ?
|
|
4660
|
+
const flow = providerId === "claude" ? import_subscriptions8.claudeOAuth : import_subscriptions8.geminiOAuth;
|
|
3141
4661
|
const { authUrl, codeVerifier, state } = flow.generateAuthParams();
|
|
3142
4662
|
const sessionId = deps.oauthSessions.put({ providerId, codeVerifier, state });
|
|
3143
4663
|
return { status: 200, body: { authUrl, sessionId } };
|
|
3144
4664
|
}
|
|
3145
4665
|
async function handleOAuthComplete(providerId, body, deps) {
|
|
3146
4666
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
3147
|
-
return
|
|
4667
|
+
return err5(400, `oauth not available for provider '${providerId}'`);
|
|
3148
4668
|
}
|
|
3149
4669
|
const sessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : "";
|
|
3150
4670
|
const rawCode = typeof body["code"] === "string" ? body["code"] : "";
|
|
3151
|
-
if (!sessionId) return
|
|
3152
|
-
if (!rawCode) return
|
|
4671
|
+
if (!sessionId) return err5(400, "oauth complete requires { sessionId }");
|
|
4672
|
+
if (!rawCode) return err5(400, "oauth complete requires { code }");
|
|
3153
4673
|
const session = deps.oauthSessions.peek(sessionId);
|
|
3154
|
-
if (!session) return
|
|
4674
|
+
if (!session) return err5(410, "oauth session is unknown, expired, or already used");
|
|
3155
4675
|
if (session.providerId !== providerId) {
|
|
3156
|
-
return
|
|
4676
|
+
return err5(400, `oauth session does not match provider '${providerId}'`);
|
|
3157
4677
|
}
|
|
3158
4678
|
let code = rawCode.trim();
|
|
3159
4679
|
if (providerId === "claude") {
|
|
3160
4680
|
const [splitCode, pastedState] = code.split("#");
|
|
3161
|
-
if (!splitCode) return
|
|
4681
|
+
if (!splitCode) return err5(400, "no authorization code was provided");
|
|
3162
4682
|
if (pastedState && pastedState !== session.state) {
|
|
3163
|
-
return
|
|
4683
|
+
return err5(400, "oauth state did not match (possible CSRF) \u2014 aborting");
|
|
3164
4684
|
}
|
|
3165
4685
|
code = splitCode;
|
|
3166
4686
|
}
|
|
@@ -3170,7 +4690,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
3170
4690
|
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
|
|
3171
4691
|
} catch (exchangeError) {
|
|
3172
4692
|
const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
|
|
3173
|
-
return
|
|
4693
|
+
return err5(502, `oauth token exchange failed for '${providerId}': ${reason}`);
|
|
3174
4694
|
}
|
|
3175
4695
|
deps.oauthSessions.consume(sessionId);
|
|
3176
4696
|
const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
|
|
@@ -3179,7 +4699,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
3179
4699
|
return { status: 200, body: status ? { account: status } : { ok: true } };
|
|
3180
4700
|
}
|
|
3181
4701
|
async function exchangeClaude(code, codeVerifier, state, exchangeFetch) {
|
|
3182
|
-
const result = await
|
|
4702
|
+
const result = await import_subscriptions8.claudeOAuth.exchangeCodeForTokens(
|
|
3183
4703
|
{ authorizationCode: code, codeVerifier, state },
|
|
3184
4704
|
exchangeFetch
|
|
3185
4705
|
);
|
|
@@ -3195,7 +4715,7 @@ async function exchangeClaude(code, codeVerifier, state, exchangeFetch) {
|
|
|
3195
4715
|
};
|
|
3196
4716
|
}
|
|
3197
4717
|
async function exchangeGemini(code, codeVerifier, exchangeFetch) {
|
|
3198
|
-
const result = await
|
|
4718
|
+
const result = await import_subscriptions8.geminiOAuth.exchangeCodeForTokens(code, codeVerifier, exchangeFetch);
|
|
3199
4719
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
3200
4720
|
return {
|
|
3201
4721
|
authMethod: "oauth",
|
|
@@ -3500,8 +5020,8 @@ function errBody(message) {
|
|
|
3500
5020
|
return { error: { type: "admin_api_error", message } };
|
|
3501
5021
|
}
|
|
3502
5022
|
var defaultCommandRunner = (command) => new Promise((resolve10) => {
|
|
3503
|
-
(0, import_node_child_process.exec)(command, { timeout: 18e4 }, (
|
|
3504
|
-
if (
|
|
5023
|
+
(0, import_node_child_process.exec)(command, { timeout: 18e4 }, (err8, _stdout, stderr) => {
|
|
5024
|
+
if (err8) resolve10({ ok: false, error: stderr.trim() || err8.message });
|
|
3505
5025
|
else resolve10({ ok: true });
|
|
3506
5026
|
});
|
|
3507
5027
|
});
|
|
@@ -3547,8 +5067,8 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
3547
5067
|
providerId: typeof body["providerId"] === "string" ? body["providerId"] : void 0,
|
|
3548
5068
|
model: typeof body["model"] === "string" ? body["model"] : void 0
|
|
3549
5069
|
});
|
|
3550
|
-
} catch (
|
|
3551
|
-
return { status: 400, body: errBody(
|
|
5070
|
+
} catch (err8) {
|
|
5071
|
+
return { status: 400, body: errBody(err8 instanceof Error ? err8.message : "no launch target") };
|
|
3552
5072
|
}
|
|
3553
5073
|
const id = (0, import_node_crypto7.randomUUID)();
|
|
3554
5074
|
let leaseId2;
|
|
@@ -3576,9 +5096,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
3576
5096
|
} else {
|
|
3577
5097
|
launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
|
|
3578
5098
|
}
|
|
3579
|
-
} catch (
|
|
3580
|
-
const status =
|
|
3581
|
-
return { status, body: errBody(
|
|
5099
|
+
} catch (err8) {
|
|
5100
|
+
const status = err8 instanceof import_provider_proxy2.RouteLeaseError ? err8.status : 400;
|
|
5101
|
+
return { status, body: errBody(err8 instanceof Error ? err8.message : "failed to build launch env") };
|
|
3582
5102
|
}
|
|
3583
5103
|
const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
|
|
3584
5104
|
const opener = ctx.opener ?? defaultTerminalOpener;
|
|
@@ -3606,9 +5126,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
3606
5126
|
onFailure: onSessionEnd
|
|
3607
5127
|
});
|
|
3608
5128
|
if (cleanup) openerCleanup = cleanup;
|
|
3609
|
-
} catch (
|
|
5129
|
+
} catch (err8) {
|
|
3610
5130
|
onSessionEnd();
|
|
3611
|
-
return { status: 500, body: errBody(
|
|
5131
|
+
return { status: 500, body: errBody(err8 instanceof Error ? err8.message : "failed to open terminal") };
|
|
3612
5132
|
}
|
|
3613
5133
|
if (ended) {
|
|
3614
5134
|
openerCleanup?.();
|
|
@@ -3849,7 +5369,7 @@ function classifySearchFailure(stage, code) {
|
|
|
3849
5369
|
}
|
|
3850
5370
|
|
|
3851
5371
|
// src/search/SearchAssembly.ts
|
|
3852
|
-
var
|
|
5372
|
+
var import_upstreamFetch8 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
3853
5373
|
var import_search = require("@omnicross/core/search");
|
|
3854
5374
|
var import_api2 = require("@omnicross/core/search/api");
|
|
3855
5375
|
var import_http2 = require("@omnicross/core/search/http");
|
|
@@ -3867,7 +5387,7 @@ function searchPolicyFrom(config) {
|
|
|
3867
5387
|
};
|
|
3868
5388
|
}
|
|
3869
5389
|
function resolveSearchUpstreamDispatcher(url) {
|
|
3870
|
-
return (0,
|
|
5390
|
+
return (0, import_upstreamFetch8.resolveUpstreamDispatcher)({ url });
|
|
3871
5391
|
}
|
|
3872
5392
|
var searchUpstreamProxyConfig = createUpstreamProxyResolver();
|
|
3873
5393
|
function resolveSearchUpstreamProxyConfig(url) {
|
|
@@ -4149,7 +5669,7 @@ async function handleSearchQuery(req, res, deps) {
|
|
|
4149
5669
|
// src/admin/searchAdminView.ts
|
|
4150
5670
|
var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
|
|
4151
5671
|
var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
|
|
4152
|
-
function
|
|
5672
|
+
function isRecord4(value) {
|
|
4153
5673
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4154
5674
|
}
|
|
4155
5675
|
function redactSearchServerConfig(search) {
|
|
@@ -4199,13 +5719,13 @@ function resolveSecretField(entry, field, stored) {
|
|
|
4199
5719
|
else delete entry[field];
|
|
4200
5720
|
}
|
|
4201
5721
|
function preserveSearchSecrets(incoming, current) {
|
|
4202
|
-
if (!
|
|
5722
|
+
if (!isRecord4(incoming)) return incoming;
|
|
4203
5723
|
const section = { ...incoming };
|
|
4204
5724
|
const providersValue = section["providers"];
|
|
4205
|
-
if (!
|
|
5725
|
+
if (!isRecord4(providersValue)) return section;
|
|
4206
5726
|
const providers = {};
|
|
4207
5727
|
for (const [id, entryValue] of Object.entries(providersValue)) {
|
|
4208
|
-
if (!
|
|
5728
|
+
if (!isRecord4(entryValue)) {
|
|
4209
5729
|
providers[id] = entryValue;
|
|
4210
5730
|
continue;
|
|
4211
5731
|
}
|
|
@@ -4283,7 +5803,7 @@ function parseKeyPolicyBody(body) {
|
|
|
4283
5803
|
var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
|
|
4284
5804
|
var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
|
|
4285
5805
|
var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
|
|
4286
|
-
function
|
|
5806
|
+
function isRecord5(value) {
|
|
4287
5807
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
4288
5808
|
}
|
|
4289
5809
|
function nonBlank(value) {
|
|
@@ -4303,7 +5823,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
4303
5823
|
const ids = /* @__PURE__ */ new Set();
|
|
4304
5824
|
raw.forEach((entry, index) => {
|
|
4305
5825
|
const path2 = `bindings[${index}]`;
|
|
4306
|
-
if (!
|
|
5826
|
+
if (!isRecord5(entry)) {
|
|
4307
5827
|
errors.push(`${path2} must be an object`);
|
|
4308
5828
|
return;
|
|
4309
5829
|
}
|
|
@@ -4332,12 +5852,12 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
4332
5852
|
} else if (entry.modelMappings.length > 100) {
|
|
4333
5853
|
errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
|
|
4334
5854
|
} else if (entry.modelMappings.some(
|
|
4335
|
-
(mapping) => !
|
|
5855
|
+
(mapping) => !isRecord5(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
|
|
4336
5856
|
)) {
|
|
4337
5857
|
errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
|
|
4338
5858
|
}
|
|
4339
5859
|
}
|
|
4340
|
-
if (!
|
|
5860
|
+
if (!isRecord5(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
|
|
4341
5861
|
errors.push(`${path2}.target is invalid`);
|
|
4342
5862
|
} else {
|
|
4343
5863
|
if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
|
|
@@ -4352,7 +5872,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
4352
5872
|
}
|
|
4353
5873
|
}
|
|
4354
5874
|
if (entry.modelMap !== void 0) {
|
|
4355
|
-
if (!
|
|
5875
|
+
if (!isRecord5(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
|
|
4356
5876
|
errors.push(`${path2}.modelMap must contain string values`);
|
|
4357
5877
|
}
|
|
4358
5878
|
}
|
|
@@ -4650,7 +6170,10 @@ var PROVIDER_KEYS = {
|
|
|
4650
6170
|
block: "opencodego",
|
|
4651
6171
|
accounts: "opencodegoAccounts",
|
|
4652
6172
|
active: "activeOpencodegoAccountId"
|
|
4653
|
-
}
|
|
6173
|
+
},
|
|
6174
|
+
kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" },
|
|
6175
|
+
grok: { block: "grok", accounts: "grokAccounts", active: "activeGrokAccountId" },
|
|
6176
|
+
copilot: { block: "copilot", accounts: "copilotAccounts", active: "activeCopilotAccountId" }
|
|
4654
6177
|
};
|
|
4655
6178
|
function clone(value) {
|
|
4656
6179
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -5172,7 +6695,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
|
|
|
5172
6695
|
}
|
|
5173
6696
|
|
|
5174
6697
|
// src/admin/adminMigration.ts
|
|
5175
|
-
function
|
|
6698
|
+
function err6(status, message) {
|
|
5176
6699
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
5177
6700
|
}
|
|
5178
6701
|
async function handleExport(body, deps) {
|
|
@@ -5182,30 +6705,30 @@ async function handleExport(body, deps) {
|
|
|
5182
6705
|
return { status: 200, body: { pack, version: BUNDLE_VERSION } };
|
|
5183
6706
|
} catch (error) {
|
|
5184
6707
|
if (error instanceof WeakPassphraseError) {
|
|
5185
|
-
return
|
|
6708
|
+
return err6(400, error.message);
|
|
5186
6709
|
}
|
|
5187
|
-
return
|
|
6710
|
+
return err6(500, "failed to build the migration pack");
|
|
5188
6711
|
}
|
|
5189
6712
|
}
|
|
5190
6713
|
async function handleImport(body, deps) {
|
|
5191
6714
|
const blob = typeof body["blob"] === "string" ? body["blob"] : "";
|
|
5192
6715
|
const passphrase = typeof body["passphrase"] === "string" ? body["passphrase"] : "";
|
|
5193
6716
|
const mode = body["mode"] === "overwrite" ? "overwrite" : "merge";
|
|
5194
|
-
if (!blob) return
|
|
6717
|
+
if (!blob) return err6(400, "import requires { blob }");
|
|
5195
6718
|
try {
|
|
5196
6719
|
const counts = await applyImport(blob, passphrase, mode, deps, deps.parseProviderInput);
|
|
5197
6720
|
return { status: 200, body: counts };
|
|
5198
6721
|
} catch (error) {
|
|
5199
6722
|
if (error instanceof WeakPassphraseError) {
|
|
5200
|
-
return
|
|
6723
|
+
return err6(400, error.message);
|
|
5201
6724
|
}
|
|
5202
|
-
return
|
|
6725
|
+
return err6(400, error instanceof Error ? error.message : "import failed");
|
|
5203
6726
|
}
|
|
5204
6727
|
}
|
|
5205
6728
|
|
|
5206
6729
|
// src/admin/usagePricing.ts
|
|
5207
6730
|
var import_usage = require("@omnicross/core/usage");
|
|
5208
|
-
var
|
|
6731
|
+
var err7 = (status, message) => ({
|
|
5209
6732
|
status,
|
|
5210
6733
|
body: { error: { type: "admin_api_error", message } }
|
|
5211
6734
|
});
|
|
@@ -5218,7 +6741,7 @@ function parseRange(query2) {
|
|
|
5218
6741
|
const startTs = parseFiniteInt(query2.get("startTs"));
|
|
5219
6742
|
const endTs = parseFiniteInt(query2.get("endTs"));
|
|
5220
6743
|
if (startTs === null || endTs === null) {
|
|
5221
|
-
return
|
|
6744
|
+
return err7(400, "startTs and endTs are required finite-integer unix-millis query params");
|
|
5222
6745
|
}
|
|
5223
6746
|
return { startTs, endTs };
|
|
5224
6747
|
}
|
|
@@ -5243,14 +6766,14 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
5243
6766
|
case "timeseries": {
|
|
5244
6767
|
const bucket = query2.get("bucket");
|
|
5245
6768
|
if (bucket !== "hour" && bucket !== "day" && bucket !== "month") {
|
|
5246
|
-
return
|
|
6769
|
+
return err7(400, "bucket must be one of 'hour', 'day', 'month'");
|
|
5247
6770
|
}
|
|
5248
6771
|
const now = Date.now();
|
|
5249
6772
|
const clamped = { startTs: range.startTs, endTs: Math.min(range.endTs, now) };
|
|
5250
6773
|
if (clamped.startTs < clamped.endTs) {
|
|
5251
6774
|
const projected = Math.ceil((clamped.endTs - clamped.startTs) / BUCKET_SPAN_MS[bucket]) + 1;
|
|
5252
6775
|
if (projected > MAX_TIMESERIES_BUCKETS) {
|
|
5253
|
-
return
|
|
6776
|
+
return err7(
|
|
5254
6777
|
400,
|
|
5255
6778
|
`requested range projects ~${projected} '${bucket}' buckets (max ${MAX_TIMESERIES_BUCKETS}); narrow the range or use a coarser bucket`
|
|
5256
6779
|
);
|
|
@@ -5273,7 +6796,7 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
5273
6796
|
};
|
|
5274
6797
|
}
|
|
5275
6798
|
default:
|
|
5276
|
-
return
|
|
6799
|
+
return err7(404, `unknown usage view '${view ?? ""}'`);
|
|
5277
6800
|
}
|
|
5278
6801
|
}
|
|
5279
6802
|
function poolKeyLabels(cfg) {
|
|
@@ -5322,7 +6845,7 @@ async function handlePricingList(deps) {
|
|
|
5322
6845
|
async function handlePricingUpsert(body, deps) {
|
|
5323
6846
|
const input = parsePricingEntryInput(body);
|
|
5324
6847
|
if (!input) {
|
|
5325
|
-
return
|
|
6848
|
+
return err7(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
|
|
5326
6849
|
}
|
|
5327
6850
|
const entry = await deps.pricingEngine.upsertManual(input);
|
|
5328
6851
|
return { status: 200, body: { entry } };
|
|
@@ -5331,7 +6854,7 @@ async function handlePricingDelete(query2, deps) {
|
|
|
5331
6854
|
const providerId = query2.get("providerId")?.trim() ?? "";
|
|
5332
6855
|
const modelId = query2.get("modelId")?.trim() ?? "";
|
|
5333
6856
|
if (!providerId || !modelId) {
|
|
5334
|
-
return
|
|
6857
|
+
return err7(400, "delete requires providerId and modelId query params");
|
|
5335
6858
|
}
|
|
5336
6859
|
const deleted = await deps.pricingStore.delete(providerId, modelId);
|
|
5337
6860
|
if (deleted) await deps.pricingEngine.invalidateCache();
|
|
@@ -5351,13 +6874,13 @@ async function handlePricingFetchLatest(deps) {
|
|
|
5351
6874
|
}
|
|
5352
6875
|
};
|
|
5353
6876
|
} catch (e) {
|
|
5354
|
-
return
|
|
6877
|
+
return err7(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
5355
6878
|
}
|
|
5356
6879
|
}
|
|
5357
6880
|
async function handlePricingResolveConflicts(body, deps) {
|
|
5358
6881
|
const raw = body["resolutions"];
|
|
5359
6882
|
if (!Array.isArray(raw)) {
|
|
5360
|
-
return
|
|
6883
|
+
return err7(400, "resolve-conflicts requires { resolutions: [...] }");
|
|
5361
6884
|
}
|
|
5362
6885
|
const currentRows = await deps.pricingStore.getAll();
|
|
5363
6886
|
const userEditedKeys = new Set(
|
|
@@ -5367,21 +6890,21 @@ async function handlePricingResolveConflicts(body, deps) {
|
|
|
5367
6890
|
const pendingIncoming = /* @__PURE__ */ new Map();
|
|
5368
6891
|
let staleCount = 0;
|
|
5369
6892
|
for (const item of raw) {
|
|
5370
|
-
if (!item || typeof item !== "object") return
|
|
6893
|
+
if (!item || typeof item !== "object") return err7(400, "invalid resolution entry");
|
|
5371
6894
|
const r = item;
|
|
5372
6895
|
const action = r["action"];
|
|
5373
6896
|
if (action !== "overwrite" && action !== "skip") {
|
|
5374
|
-
return
|
|
6897
|
+
return err7(400, "resolution action must be 'overwrite' or 'skip'");
|
|
5375
6898
|
}
|
|
5376
6899
|
const providerId = typeof r["providerId"] === "string" && r["providerId"].trim() ? r["providerId"].trim() : "";
|
|
5377
6900
|
const modelId = typeof r["modelId"] === "string" && r["modelId"].trim() ? r["modelId"].trim() : "";
|
|
5378
6901
|
if (!providerId || !modelId) {
|
|
5379
|
-
return
|
|
6902
|
+
return err7(400, "each resolution requires top-level providerId and modelId");
|
|
5380
6903
|
}
|
|
5381
6904
|
const incoming = parsePricingEntryInput(r["incoming"]);
|
|
5382
|
-
if (!incoming) return
|
|
6905
|
+
if (!incoming) return err7(400, "each resolution must echo a valid incoming pricing entry");
|
|
5383
6906
|
if (incoming.providerId !== providerId || incoming.modelId !== modelId) {
|
|
5384
|
-
return
|
|
6907
|
+
return err7(400, "resolution providerId/modelId must match the echoed incoming entry");
|
|
5385
6908
|
}
|
|
5386
6909
|
const key = `${providerId}::${modelId}`;
|
|
5387
6910
|
if (action === "overwrite" && !userEditedKeys.has(key)) {
|
|
@@ -5426,7 +6949,7 @@ function query(req) {
|
|
|
5426
6949
|
}
|
|
5427
6950
|
function allowanceProvider(value) {
|
|
5428
6951
|
if (!value) return void 0;
|
|
5429
|
-
return value === "claude" || value === "codex" ? value : null;
|
|
6952
|
+
return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" || value === "grok" || value === "copilot" ? value : null;
|
|
5430
6953
|
}
|
|
5431
6954
|
async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
5432
6955
|
if (!service) return writeError2(res, 501, "account allowance service is not available");
|
|
@@ -5440,7 +6963,9 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
5440
6963
|
const params = query(req);
|
|
5441
6964
|
const pathProvider = rest.length >= 2 ? rest[0] : null;
|
|
5442
6965
|
const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
|
|
5443
|
-
if (providerId === null)
|
|
6966
|
+
if (providerId === null) {
|
|
6967
|
+
return writeError2(res, 400, "providerId must be claude, codex, kimi, opencodego, grok, or copilot");
|
|
6968
|
+
}
|
|
5444
6969
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
5445
6970
|
const allowances = await service.list({ providerId, accountId });
|
|
5446
6971
|
return writeJson3(res, 200, { allowances });
|
|
@@ -5450,10 +6975,57 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
5450
6975
|
const requestedProvider = allowanceProvider(
|
|
5451
6976
|
typeof body["providerId"] === "string" ? body["providerId"] : "claude"
|
|
5452
6977
|
);
|
|
5453
|
-
|
|
5454
|
-
|
|
6978
|
+
const accountId = typeof body["accountId"] === "string" && body["accountId"].trim() ? body["accountId"].trim() : void 0;
|
|
6979
|
+
if (requestedProvider === "codex") {
|
|
6980
|
+
if (!service.refreshCodex) {
|
|
6981
|
+
return writeError2(res, 501, "codex allowance refresh is not available");
|
|
6982
|
+
}
|
|
6983
|
+
const allowances2 = await service.refreshCodex(accountId);
|
|
6984
|
+
if (accountId && allowances2.length === 0) {
|
|
6985
|
+
return writeError2(res, 404, `Codex account '${accountId}' not found`);
|
|
6986
|
+
}
|
|
6987
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
6988
|
+
}
|
|
6989
|
+
if (requestedProvider === "kimi") {
|
|
6990
|
+
if (!service.refreshKimi) {
|
|
6991
|
+
return writeError2(res, 501, "kimi allowance refresh is not available");
|
|
6992
|
+
}
|
|
6993
|
+
const allowances2 = await service.refreshKimi(accountId);
|
|
6994
|
+
if (accountId && allowances2.length === 0) {
|
|
6995
|
+
return writeError2(res, 404, `Kimi account '${accountId}' not found`);
|
|
6996
|
+
}
|
|
6997
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
6998
|
+
}
|
|
6999
|
+
if (requestedProvider === "opencodego") {
|
|
7000
|
+
if (!service.refreshOpenCodeGo) {
|
|
7001
|
+
return writeError2(res, 501, "opencodego allowance refresh is not available");
|
|
7002
|
+
}
|
|
7003
|
+
const allowances2 = await service.refreshOpenCodeGo(accountId);
|
|
7004
|
+
if (accountId && allowances2.length === 0) {
|
|
7005
|
+
return writeError2(res, 404, `OpenCodeGo account '${accountId}' not found`);
|
|
7006
|
+
}
|
|
7007
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7008
|
+
}
|
|
7009
|
+
if (requestedProvider === "copilot") {
|
|
7010
|
+
if (!service.refreshCopilot) {
|
|
7011
|
+
return writeError2(res, 501, "copilot allowance refresh is not available");
|
|
7012
|
+
}
|
|
7013
|
+
const allowances2 = await service.refreshCopilot(accountId);
|
|
7014
|
+
if (accountId && allowances2.length === 0) {
|
|
7015
|
+
return writeError2(res, 404, `Copilot account '${accountId}' not found`);
|
|
7016
|
+
}
|
|
7017
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
7018
|
+
}
|
|
7019
|
+
if (requestedProvider === "grok") {
|
|
7020
|
+
if (!service.refreshGrok) {
|
|
7021
|
+
return writeError2(res, 501, "grok allowance refresh is not available");
|
|
7022
|
+
}
|
|
7023
|
+
const allowances2 = await service.refreshGrok(accountId);
|
|
7024
|
+
if (accountId && allowances2.length === 0) {
|
|
7025
|
+
return writeError2(res, 404, `Grok account '${accountId}' not found`);
|
|
7026
|
+
}
|
|
7027
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
5455
7028
|
}
|
|
5456
|
-
const accountId = typeof body["accountId"] === "string" && body["accountId"].trim() ? body["accountId"].trim() : void 0;
|
|
5457
7029
|
const allowances = await service.refreshClaude(accountId);
|
|
5458
7030
|
if (accountId && allowances.length === 0) {
|
|
5459
7031
|
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
@@ -5552,6 +7124,9 @@ function toProviderView(row) {
|
|
|
5552
7124
|
apiVersion: row.apiVersion,
|
|
5553
7125
|
maxConcurrency: row.maxConcurrency,
|
|
5554
7126
|
modelsEndpoint: row.modelsEndpoint,
|
|
7127
|
+
// Static extra headers round-trip VERBATIM (non-secret identity values;
|
|
7128
|
+
// auth/content names were already dropped at the write/load gate).
|
|
7129
|
+
extraHeaders: row.extraHeaders,
|
|
5555
7130
|
// app-parity child 5: transformer config round-trips VERBATIM (non-secret —
|
|
5556
7131
|
// transform-rule names + options, no key material; absent stays absent).
|
|
5557
7132
|
transformer: row.transformer,
|
|
@@ -5621,8 +7196,8 @@ async function handleAdminApi(req, res, path2, deps) {
|
|
|
5621
7196
|
default:
|
|
5622
7197
|
return writeJsonError(res, 404, `unknown admin resource '${resource}'`);
|
|
5623
7198
|
}
|
|
5624
|
-
} catch (
|
|
5625
|
-
writeJsonError(res, 500,
|
|
7199
|
+
} catch (err8) {
|
|
7200
|
+
writeJsonError(res, 500, err8 instanceof Error ? err8.message : String(err8));
|
|
5626
7201
|
}
|
|
5627
7202
|
}
|
|
5628
7203
|
function requestQuery(req) {
|
|
@@ -5692,6 +7267,9 @@ async function handleProviders(req, res, method, rest, deps) {
|
|
|
5692
7267
|
if (method === "POST" && rest.length === 4 && rest[1] === "keys" && rest[3] === "enabled") {
|
|
5693
7268
|
return await handleToggleProviderKey(req, res, rest[0], rest[2], cfg, deps);
|
|
5694
7269
|
}
|
|
7270
|
+
if (method === "POST" && rest.length === 5 && rest[1] === "keys" && rest[3] === "quota" && rest[4] === "refresh") {
|
|
7271
|
+
return await handleProviderKeyQuotaRefresh(res, rest[0], rest[2], cfg, deps);
|
|
7272
|
+
}
|
|
5695
7273
|
if (method === "PUT" && rest.length === 3 && rest[1] === "keys") {
|
|
5696
7274
|
return await handleUpdateProviderKey(req, res, rest[0], rest[2], cfg, deps);
|
|
5697
7275
|
}
|
|
@@ -5777,6 +7355,9 @@ async function handleProviderReorder(req, res, cfg, deps) {
|
|
|
5777
7355
|
persistProviders(cfg, deps);
|
|
5778
7356
|
return writeJson4(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
|
|
5779
7357
|
}
|
|
7358
|
+
function expandRowExtraHeaders(row) {
|
|
7359
|
+
return (0, import_core3.mergeExtraHeaders)({}, row.extraHeaders);
|
|
7360
|
+
}
|
|
5780
7361
|
async function handleDiscoverModels(res, id, cfg) {
|
|
5781
7362
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
5782
7363
|
const row = cfg.providers.find((p) => p.id === id);
|
|
@@ -5790,7 +7371,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
5790
7371
|
try {
|
|
5791
7372
|
const headers = { Accept: "application/json" };
|
|
5792
7373
|
if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
|
|
5793
|
-
|
|
7374
|
+
Object.assign(headers, expandRowExtraHeaders(row));
|
|
7375
|
+
const response = await (0, import_upstreamFetch9.fetchUpstream)(url, { method: "GET", headers }, { providerId: "byo" });
|
|
5794
7376
|
if (!response.ok) {
|
|
5795
7377
|
const text = await response.text().catch(() => "");
|
|
5796
7378
|
let message = text.slice(0, 300);
|
|
@@ -5807,8 +7389,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
5807
7389
|
const data = await response.json();
|
|
5808
7390
|
const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
|
|
5809
7391
|
return writeJson4(res, 200, { models });
|
|
5810
|
-
} catch (
|
|
5811
|
-
const message =
|
|
7392
|
+
} catch (err8) {
|
|
7393
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
5812
7394
|
return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
|
|
5813
7395
|
}
|
|
5814
7396
|
}
|
|
@@ -5847,9 +7429,10 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
5847
7429
|
messages: [{ role: "user", content: prompt }]
|
|
5848
7430
|
};
|
|
5849
7431
|
}
|
|
7432
|
+
Object.assign(headers, expandRowExtraHeaders(row));
|
|
5850
7433
|
const startedAt = Date.now();
|
|
5851
7434
|
try {
|
|
5852
|
-
const response = await (0,
|
|
7435
|
+
const response = await (0, import_upstreamFetch9.fetchUpstream)(
|
|
5853
7436
|
url,
|
|
5854
7437
|
{ method: "POST", headers, body: JSON.stringify(payload) },
|
|
5855
7438
|
{ providerId: "byo" }
|
|
@@ -5871,8 +7454,8 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
5871
7454
|
latencyMs,
|
|
5872
7455
|
sample: extractSampleText(text, row.apiFormat)
|
|
5873
7456
|
});
|
|
5874
|
-
} catch (
|
|
5875
|
-
const message =
|
|
7457
|
+
} catch (err8) {
|
|
7458
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
5876
7459
|
return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
|
|
5877
7460
|
}
|
|
5878
7461
|
}
|
|
@@ -5914,7 +7497,30 @@ async function handleProviderKeys(res, id, cfg, deps) {
|
|
|
5914
7497
|
const row = cfg.providers.find((p) => p.id === id);
|
|
5915
7498
|
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
5916
7499
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
5917
|
-
|
|
7500
|
+
const views = toPoolKeyView(row, cooldown, deps);
|
|
7501
|
+
if (deps.providerKeyQuota) {
|
|
7502
|
+
const quotas = await Promise.allSettled(
|
|
7503
|
+
views.map((view) => deps.providerKeyQuota.quotaFor(row, view.id))
|
|
7504
|
+
);
|
|
7505
|
+
views.forEach((view, index) => {
|
|
7506
|
+
const settled = quotas[index];
|
|
7507
|
+
if (settled.status === "fulfilled" && settled.value) view.quota = settled.value;
|
|
7508
|
+
});
|
|
7509
|
+
}
|
|
7510
|
+
return writeJson4(res, 200, { keys: views });
|
|
7511
|
+
}
|
|
7512
|
+
async function handleProviderKeyQuotaRefresh(res, id, keyId, cfg, deps) {
|
|
7513
|
+
if (!deps.providerKeyQuota) return writeJsonError(res, 501, "provider key quota is not available");
|
|
7514
|
+
if (!id || !keyId) return writeJsonError(res, 400, "provider id and key id required in path");
|
|
7515
|
+
const row = cfg.providers.find((p) => p.id === id);
|
|
7516
|
+
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
7517
|
+
try {
|
|
7518
|
+
const quota = await deps.providerKeyQuota.quotaFor(row, keyId, { force: true });
|
|
7519
|
+
if (!quota) return writeJsonError(res, 404, `no quota endpoint for key '${keyId}'`);
|
|
7520
|
+
return writeJson4(res, 200, { quota });
|
|
7521
|
+
} catch {
|
|
7522
|
+
return writeJsonError(res, 502, "quota refresh failed");
|
|
7523
|
+
}
|
|
5918
7524
|
}
|
|
5919
7525
|
function parsePoolKeyInput(body, existing) {
|
|
5920
7526
|
const out = {};
|
|
@@ -6131,6 +7737,7 @@ function parseProviderInput(body, existing) {
|
|
|
6131
7737
|
const apiVersion = typeof body["apiVersion"] === "string" && body["apiVersion"].length > 0 ? body["apiVersion"] : body["apiVersion"] === null ? void 0 : existing?.apiVersion;
|
|
6132
7738
|
const modelsEndpoint = typeof body["modelsEndpoint"] === "string" && body["modelsEndpoint"].length > 0 ? body["modelsEndpoint"] : body["modelsEndpoint"] === null ? void 0 : existing?.modelsEndpoint;
|
|
6133
7739
|
const maxConcurrency = typeof body["maxConcurrency"] === "number" && Number.isFinite(body["maxConcurrency"]) ? body["maxConcurrency"] : body["maxConcurrency"] === null ? void 0 : existing?.maxConcurrency;
|
|
7740
|
+
const extraHeaders = body["extraHeaders"] === null ? void 0 : body["extraHeaders"] === void 0 ? existing?.extraHeaders : validateExtraHeaders(body["extraHeaders"]);
|
|
6134
7741
|
const transformer = body["transformer"] === null ? void 0 : parseTransformerInput(body["transformer"], existing?.transformer);
|
|
6135
7742
|
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;
|
|
6136
7743
|
const apiModes = body["apiModes"] === null ? void 0 : parseApiModesInput(body["apiModes"], existing?.apiModes);
|
|
@@ -6156,6 +7763,7 @@ function parseProviderInput(body, existing) {
|
|
|
6156
7763
|
apiVersion,
|
|
6157
7764
|
maxConcurrency,
|
|
6158
7765
|
modelsEndpoint,
|
|
7766
|
+
extraHeaders,
|
|
6159
7767
|
transformer: migrated.transformer,
|
|
6160
7768
|
codingPlan,
|
|
6161
7769
|
apiModes,
|
|
@@ -6177,7 +7785,10 @@ function handlePresets(res, method) {
|
|
|
6177
7785
|
description: p.description,
|
|
6178
7786
|
features: p.features,
|
|
6179
7787
|
website: p.website,
|
|
6180
|
-
modelsEndpoint: p.modelsEndpoint
|
|
7788
|
+
modelsEndpoint: p.modelsEndpoint,
|
|
7789
|
+
// Static extra headers ride along so `addFromPreset` can seed them onto the
|
|
7790
|
+
// row (the write gateway re-validates via the shared allowlist).
|
|
7791
|
+
extraHeaders: p.extraHeaders
|
|
6181
7792
|
}));
|
|
6182
7793
|
return writeJson4(res, 200, { presets, excluded });
|
|
6183
7794
|
}
|
|
@@ -6659,12 +8270,12 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6659
8270
|
}
|
|
6660
8271
|
return writeJson4(res, 200, { ok: true, affected: result.affected });
|
|
6661
8272
|
}
|
|
6662
|
-
if (method === "GET" && rest[0] === "codex" && rest[1] === "oauth" && rest[3] === "status") {
|
|
6663
|
-
const result = handleCodexOAuthStatus(rest[2], deps);
|
|
8273
|
+
if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[3] === "status") {
|
|
8274
|
+
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);
|
|
6664
8275
|
return writeJson4(res, result.status, result.body);
|
|
6665
8276
|
}
|
|
6666
|
-
if (method === "DELETE" && rest[0] === "codex" && rest[1] === "oauth" && rest[2]) {
|
|
6667
|
-
const result = handleCodexOAuthCancel(rest[2], deps);
|
|
8277
|
+
if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot") && rest[1] === "oauth" && rest[2]) {
|
|
8278
|
+
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);
|
|
6668
8279
|
return writeJson4(res, result.status, result.body);
|
|
6669
8280
|
}
|
|
6670
8281
|
if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
|
|
@@ -6717,7 +8328,24 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6717
8328
|
return writeJsonError(res, 400, `unknown subscription provider '${rest[0] ?? ""}'`);
|
|
6718
8329
|
}
|
|
6719
8330
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "start") {
|
|
6720
|
-
|
|
8331
|
+
if (providerId === "codex") {
|
|
8332
|
+
const result2 = handleCodexOAuthStart(deps);
|
|
8333
|
+
return writeJson4(res, result2.status, result2.body);
|
|
8334
|
+
}
|
|
8335
|
+
if (providerId === "kimi") {
|
|
8336
|
+
const result2 = await handleKimiOAuthStart(deps);
|
|
8337
|
+
return writeJson4(res, result2.status, result2.body);
|
|
8338
|
+
}
|
|
8339
|
+
if (providerId === "grok") {
|
|
8340
|
+
const result2 = await handleGrokOAuthStart(deps);
|
|
8341
|
+
return writeJson4(res, result2.status, result2.body);
|
|
8342
|
+
}
|
|
8343
|
+
if (providerId === "copilot") {
|
|
8344
|
+
const body2 = await readJsonBody4(req);
|
|
8345
|
+
const result2 = await handleCopilotOAuthStart(deps, body2["enterpriseUrl"]);
|
|
8346
|
+
return writeJson4(res, result2.status, result2.body);
|
|
8347
|
+
}
|
|
8348
|
+
const result = handleOAuthStart(providerId, deps);
|
|
6721
8349
|
return writeJson4(res, result.status, result.body);
|
|
6722
8350
|
}
|
|
6723
8351
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "complete") {
|
|
@@ -7211,12 +8839,12 @@ async function handlePlayground(req, res, method, deps) {
|
|
|
7211
8839
|
const payload = body["body"];
|
|
7212
8840
|
const status = deps.outboundApiServer.getStatus();
|
|
7213
8841
|
if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
|
|
7214
|
-
const path2 = resolvePlaygroundPath(endpoint,
|
|
8842
|
+
const path2 = resolvePlaygroundPath(endpoint, isRecord6(payload) ? payload : {});
|
|
7215
8843
|
if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
|
|
7216
8844
|
const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
|
|
7217
8845
|
await proxyToOutbound(res, status.port, path2, key, upstreamBody);
|
|
7218
8846
|
}
|
|
7219
|
-
function
|
|
8847
|
+
function isRecord6(v) {
|
|
7220
8848
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
7221
8849
|
}
|
|
7222
8850
|
function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
@@ -7245,8 +8873,8 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
|
7245
8873
|
});
|
|
7246
8874
|
}
|
|
7247
8875
|
);
|
|
7248
|
-
upstream.on("error", (
|
|
7249
|
-
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${
|
|
8876
|
+
upstream.on("error", (err8) => {
|
|
8877
|
+
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err8.message}`);
|
|
7250
8878
|
else res.end();
|
|
7251
8879
|
resolve10();
|
|
7252
8880
|
});
|
|
@@ -7352,7 +8980,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
7352
8980
|
}
|
|
7353
8981
|
|
|
7354
8982
|
// src/admin/version.ts
|
|
7355
|
-
var DAEMON_VERSION = true ? "0.
|
|
8983
|
+
var DAEMON_VERSION = true ? "0.4.0" : "0.0.0-dev";
|
|
7356
8984
|
|
|
7357
8985
|
// src/admin/AdminServer.ts
|
|
7358
8986
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -7395,13 +9023,13 @@ var AdminServer = class {
|
|
|
7395
9023
|
const server = import_node_http2.default.createServer((req, res) => {
|
|
7396
9024
|
this.onRequest(req, res);
|
|
7397
9025
|
});
|
|
7398
|
-
const onError = (
|
|
7399
|
-
if (
|
|
9026
|
+
const onError = (err8) => {
|
|
9027
|
+
if (err8.code === "EADDRINUSE" && port !== 0) {
|
|
7400
9028
|
server.removeListener("error", onError);
|
|
7401
9029
|
this.listen(bindAddr, 0).then(resolve10, reject);
|
|
7402
9030
|
return;
|
|
7403
9031
|
}
|
|
7404
|
-
reject(
|
|
9032
|
+
reject(err8);
|
|
7405
9033
|
};
|
|
7406
9034
|
server.on("error", onError);
|
|
7407
9035
|
server.listen(port, bindAddr, () => {
|
|
@@ -7419,8 +9047,8 @@ var AdminServer = class {
|
|
|
7419
9047
|
}
|
|
7420
9048
|
/** Per-request handler: auth gate (when a token is set) → routing. */
|
|
7421
9049
|
onRequest(req, res) {
|
|
7422
|
-
void this.dispatch(req, res).catch((
|
|
7423
|
-
const message =
|
|
9050
|
+
void this.dispatch(req, res).catch((err8) => {
|
|
9051
|
+
const message = err8 instanceof Error ? err8.message : String(err8);
|
|
7424
9052
|
this.deps.logger.error("[AdminServer] unhandled error:", message);
|
|
7425
9053
|
if (!res.headersSent) {
|
|
7426
9054
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
@@ -7684,18 +9312,18 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
7684
9312
|
return;
|
|
7685
9313
|
}
|
|
7686
9314
|
signal?.addEventListener("abort", abort, { once: true });
|
|
7687
|
-
server.on("error", (
|
|
9315
|
+
server.on("error", (err8) => {
|
|
7688
9316
|
if (settled) return;
|
|
7689
9317
|
settled = true;
|
|
7690
9318
|
clearTimeout(timer);
|
|
7691
|
-
if (
|
|
9319
|
+
if (err8.code === "EADDRINUSE") {
|
|
7692
9320
|
reject(
|
|
7693
9321
|
new Error(
|
|
7694
9322
|
`login: cannot bind ${LOOPBACK_HOST}:${LOOPBACK_PORT} (address in use) \u2014 another codex login or process is holding the port`
|
|
7695
9323
|
)
|
|
7696
9324
|
);
|
|
7697
9325
|
} else {
|
|
7698
|
-
reject(
|
|
9326
|
+
reject(err8);
|
|
7699
9327
|
}
|
|
7700
9328
|
});
|
|
7701
9329
|
const timer = setTimeout(() => {
|
|
@@ -7770,6 +9398,449 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
|
|
|
7770
9398
|
};
|
|
7771
9399
|
}
|
|
7772
9400
|
|
|
9401
|
+
// src/allowance/ProviderKeyQuotaService.ts
|
|
9402
|
+
var import_core4 = require("@omnicross/core");
|
|
9403
|
+
var import_upstreamFetch10 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
9404
|
+
|
|
9405
|
+
// src/allowance/ProviderKeyQuota.ts
|
|
9406
|
+
var MINUTE_MS3 = 6e4;
|
|
9407
|
+
var HOUR_MS2 = 60 * MINUTE_MS3;
|
|
9408
|
+
var DAY_MS3 = 24 * HOUR_MS2;
|
|
9409
|
+
var WEEK_MS = 7 * DAY_MS3;
|
|
9410
|
+
var MONTH_MS = 30 * DAY_MS3;
|
|
9411
|
+
function finiteNumber5(value) {
|
|
9412
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
9413
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
9414
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
9415
|
+
}
|
|
9416
|
+
function finitePercent4(value) {
|
|
9417
|
+
const parsed = finiteNumber5(value);
|
|
9418
|
+
return parsed !== void 0 && parsed <= 100 ? parsed : null;
|
|
9419
|
+
}
|
|
9420
|
+
function isoInstant3(value) {
|
|
9421
|
+
if (typeof value === "string" && value.trim()) {
|
|
9422
|
+
const time = Date.parse(value);
|
|
9423
|
+
if (Number.isFinite(time)) return new Date(time).toISOString();
|
|
9424
|
+
}
|
|
9425
|
+
const numeric = finiteNumber5(value);
|
|
9426
|
+
if (numeric !== void 0 && numeric > 1e9) {
|
|
9427
|
+
const ms = numeric > 1e12 ? numeric : numeric * 1e3;
|
|
9428
|
+
return new Date(ms).toISOString();
|
|
9429
|
+
}
|
|
9430
|
+
return void 0;
|
|
9431
|
+
}
|
|
9432
|
+
function secondsUntil7(instant, now) {
|
|
9433
|
+
if (!instant) return void 0;
|
|
9434
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
9435
|
+
}
|
|
9436
|
+
function isRecord7(value) {
|
|
9437
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
9438
|
+
}
|
|
9439
|
+
function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
9440
|
+
if (!baseUrl) return null;
|
|
9441
|
+
let url;
|
|
9442
|
+
try {
|
|
9443
|
+
url = new URL(baseUrl);
|
|
9444
|
+
} catch {
|
|
9445
|
+
return null;
|
|
9446
|
+
}
|
|
9447
|
+
const host = url.hostname.toLowerCase();
|
|
9448
|
+
const path2 = url.pathname.toLowerCase();
|
|
9449
|
+
if ((host === "api.z.ai" || host === "open.bigmodel.cn") && path2.includes("/coding")) {
|
|
9450
|
+
return "zai";
|
|
9451
|
+
}
|
|
9452
|
+
if ((host === "api.minimax.io" || host === "api.minimaxi.com") && // Token Plan rides the plain openai `/v1` (chat completions) surface; the
|
|
9453
|
+
// anthropic `/anthropic` rows are excluded (their usage impl is unverified).
|
|
9454
|
+
(path2 === "/v1" || path2 === "/v1/" || path2 === "" || path2 === "/")) {
|
|
9455
|
+
return "minimax-token-plan";
|
|
9456
|
+
}
|
|
9457
|
+
if (host === "api.code.umans.ai") return "umans";
|
|
9458
|
+
if (host === "api.synthetic.new") return "synthetic";
|
|
9459
|
+
if (host === "api.cline.bot") return "cline-pass";
|
|
9460
|
+
return null;
|
|
9461
|
+
}
|
|
9462
|
+
function providerKeyQuotaUrl(adapter, baseUrl) {
|
|
9463
|
+
const origin = new URL(baseUrl).origin;
|
|
9464
|
+
if (adapter === "zai") return `${origin}/api/monitor/usage/quota/limit`;
|
|
9465
|
+
if (adapter === "minimax-token-plan") return `${origin}/v1/token_plan/remains`;
|
|
9466
|
+
if (adapter === "umans") return `${origin}/v1/usage`;
|
|
9467
|
+
if (adapter === "cline-pass") return `${origin}/api/v1/users/me/plan/usage-limits`;
|
|
9468
|
+
return `${origin}/v2/quotas`;
|
|
9469
|
+
}
|
|
9470
|
+
function providerKeyQuotaAuthHeader(adapter, key) {
|
|
9471
|
+
return adapter === "zai" ? key : `Bearer ${key}`;
|
|
9472
|
+
}
|
|
9473
|
+
function zaiWindowDurationMs(item) {
|
|
9474
|
+
const count = item.number !== void 0 && item.number > 0 ? item.number : 1;
|
|
9475
|
+
switch (item.unit) {
|
|
9476
|
+
case 3:
|
|
9477
|
+
return count * HOUR_MS2;
|
|
9478
|
+
case 4:
|
|
9479
|
+
return count * DAY_MS3;
|
|
9480
|
+
case 5:
|
|
9481
|
+
return count * MONTH_MS;
|
|
9482
|
+
case 6:
|
|
9483
|
+
return WEEK_MS;
|
|
9484
|
+
default:
|
|
9485
|
+
return void 0;
|
|
9486
|
+
}
|
|
9487
|
+
}
|
|
9488
|
+
function zaiWindowIdLabel(durationMs) {
|
|
9489
|
+
if (durationMs === WEEK_MS) return { id: "seven-day", label: "7 days" };
|
|
9490
|
+
if (durationMs === 5 * HOUR_MS2) return { id: "five-hour", label: "5 hours" };
|
|
9491
|
+
if (durationMs === MONTH_MS) return { id: "thirty-day", label: "30 days" };
|
|
9492
|
+
if (durationMs !== void 0 && durationMs % DAY_MS3 === 0) {
|
|
9493
|
+
const days = durationMs / DAY_MS3;
|
|
9494
|
+
return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}` };
|
|
9495
|
+
}
|
|
9496
|
+
if (durationMs !== void 0 && durationMs % HOUR_MS2 === 0) {
|
|
9497
|
+
const hours = durationMs / HOUR_MS2;
|
|
9498
|
+
return { id: `${hours}h`, label: `${hours} hour${hours === 1 ? "" : "s"}` };
|
|
9499
|
+
}
|
|
9500
|
+
return { id: "quota", label: "Quota" };
|
|
9501
|
+
}
|
|
9502
|
+
function parseZaiQuotaPayload(payload, now) {
|
|
9503
|
+
if (!isRecord7(payload)) return null;
|
|
9504
|
+
const data = isRecord7(payload["data"]) ? payload["data"] : payload;
|
|
9505
|
+
if (payload["success"] === false) return null;
|
|
9506
|
+
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
9507
|
+
const byWindow = /* @__PURE__ */ new Map();
|
|
9508
|
+
for (const raw of limits) {
|
|
9509
|
+
if (!isRecord7(raw)) continue;
|
|
9510
|
+
const item = raw;
|
|
9511
|
+
if (item.type === void 0) continue;
|
|
9512
|
+
const details = raw["usageDetails"];
|
|
9513
|
+
if (Array.isArray(details) && details.some((d) => isRecord7(d) && d["modelCode"] === "zread")) {
|
|
9514
|
+
continue;
|
|
9515
|
+
}
|
|
9516
|
+
const durationMs = zaiWindowDurationMs(item);
|
|
9517
|
+
const { id, label } = zaiWindowIdLabel(durationMs);
|
|
9518
|
+
const limit = finiteNumber5(item.usage);
|
|
9519
|
+
const used = finiteNumber5(item.currentValue);
|
|
9520
|
+
const fromAbsolute = limit !== void 0 && used !== void 0 && limit > 0 ? Math.min(100, used / limit * 100) : void 0;
|
|
9521
|
+
const fromPercentage = finitePercent4(item.percentage) ?? void 0;
|
|
9522
|
+
const usedPercent = fromAbsolute !== void 0 ? Math.round(fromAbsolute * 10) / 10 : fromPercentage;
|
|
9523
|
+
if (usedPercent === void 0) continue;
|
|
9524
|
+
const resetsAt = isoInstant3(item.nextResetTime);
|
|
9525
|
+
const candidate = {
|
|
9526
|
+
id,
|
|
9527
|
+
label,
|
|
9528
|
+
scope: "all",
|
|
9529
|
+
usedPercent,
|
|
9530
|
+
...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs / MINUTE_MS3) } : {},
|
|
9531
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9532
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9533
|
+
state: "fresh"
|
|
9534
|
+
};
|
|
9535
|
+
const existing = byWindow.get(id);
|
|
9536
|
+
if (!existing || (candidate.usedPercent ?? 0) > (existing.usedPercent ?? 0)) {
|
|
9537
|
+
byWindow.set(id, candidate);
|
|
9538
|
+
}
|
|
9539
|
+
}
|
|
9540
|
+
const windows = [...byWindow.values()].sort((a, b) => (a.windowMinutes ?? Number.POSITIVE_INFINITY) - (b.windowMinutes ?? Number.POSITIVE_INFINITY));
|
|
9541
|
+
return windows.length > 0 ? windows.slice(0, 4) : null;
|
|
9542
|
+
}
|
|
9543
|
+
var MINIMAX_STATUS_EXHAUSTED = 2;
|
|
9544
|
+
var MINIMAX_SHARED_BUCKET = "general";
|
|
9545
|
+
function parseMiniMaxBucket(value) {
|
|
9546
|
+
if (!isRecord7(value)) return null;
|
|
9547
|
+
const modelName = typeof value["model_name"] === "string" ? value["model_name"].trim() : "";
|
|
9548
|
+
if (!modelName) return null;
|
|
9549
|
+
const instant = (v) => {
|
|
9550
|
+
const n = finiteNumber5(v);
|
|
9551
|
+
return n !== void 0 && n > 1e9 ? n > 1e12 ? n : n * 1e3 : void 0;
|
|
9552
|
+
};
|
|
9553
|
+
return {
|
|
9554
|
+
modelName,
|
|
9555
|
+
intervalEnd: instant(value["end_time"]),
|
|
9556
|
+
intervalRemainingPercent: finiteNumber5(value["current_interval_remaining_percent"]),
|
|
9557
|
+
intervalStatus: finiteNumber5(value["current_interval_status"]),
|
|
9558
|
+
weeklyEnd: instant(value["weekly_end_time"]),
|
|
9559
|
+
weeklyRemainingPercent: finiteNumber5(value["current_weekly_remaining_percent"]),
|
|
9560
|
+
weeklyStatus: finiteNumber5(value["current_weekly_status"])
|
|
9561
|
+
};
|
|
9562
|
+
}
|
|
9563
|
+
function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, status, now) {
|
|
9564
|
+
const usedPercent = status === MINIMAX_STATUS_EXHAUSTED ? 100 : remainingPercent !== void 0 ? Math.round((100 - remainingPercent) * 10) / 10 : null;
|
|
9565
|
+
const resetsAt = resetsAtMs !== void 0 ? new Date(resetsAtMs).toISOString() : void 0;
|
|
9566
|
+
return {
|
|
9567
|
+
id,
|
|
9568
|
+
label,
|
|
9569
|
+
scope: "all",
|
|
9570
|
+
usedPercent,
|
|
9571
|
+
...windowMinutes !== void 0 ? { windowMinutes } : {},
|
|
9572
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9573
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9574
|
+
state: usedPercent !== null ? "fresh" : "unavailable"
|
|
9575
|
+
};
|
|
9576
|
+
}
|
|
9577
|
+
function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
9578
|
+
if (!isRecord7(payload)) return null;
|
|
9579
|
+
const baseResp = payload["base_resp"];
|
|
9580
|
+
if (!isRecord7(baseResp) || baseResp["status_code"] !== 0) return null;
|
|
9581
|
+
const buckets = Array.isArray(payload["model_remains"]) ? payload["model_remains"] : [];
|
|
9582
|
+
let general = null;
|
|
9583
|
+
for (const raw of buckets) {
|
|
9584
|
+
const bucket = parseMiniMaxBucket(raw);
|
|
9585
|
+
if (bucket?.modelName === MINIMAX_SHARED_BUCKET) {
|
|
9586
|
+
general = bucket;
|
|
9587
|
+
break;
|
|
9588
|
+
}
|
|
9589
|
+
}
|
|
9590
|
+
if (!general) return null;
|
|
9591
|
+
return [
|
|
9592
|
+
minimaxWindow(
|
|
9593
|
+
"five-hour",
|
|
9594
|
+
"5 hours",
|
|
9595
|
+
5 * 60,
|
|
9596
|
+
general.intervalEnd,
|
|
9597
|
+
general.intervalRemainingPercent,
|
|
9598
|
+
general.intervalStatus,
|
|
9599
|
+
now
|
|
9600
|
+
),
|
|
9601
|
+
minimaxWindow(
|
|
9602
|
+
"seven-day",
|
|
9603
|
+
"7 days",
|
|
9604
|
+
Math.round(WEEK_MS / MINUTE_MS3),
|
|
9605
|
+
general.weeklyEnd,
|
|
9606
|
+
general.weeklyRemainingPercent,
|
|
9607
|
+
general.weeklyStatus,
|
|
9608
|
+
now
|
|
9609
|
+
)
|
|
9610
|
+
];
|
|
9611
|
+
}
|
|
9612
|
+
function parseUmansUsagePayload(payload, now) {
|
|
9613
|
+
if (!isRecord7(payload)) return null;
|
|
9614
|
+
const limits = isRecord7(payload["limits"]) ? payload["limits"] : void 0;
|
|
9615
|
+
const requests = limits && isRecord7(limits["requests"]) ? limits["requests"] : void 0;
|
|
9616
|
+
const usage = isRecord7(payload["usage"]) ? payload["usage"] : void 0;
|
|
9617
|
+
const window = isRecord7(payload["window"]) ? payload["window"] : void 0;
|
|
9618
|
+
const hardCap = finiteNumber5(requests?.["hard_cap"]);
|
|
9619
|
+
const softLimit = finiteNumber5(requests?.["limit"]);
|
|
9620
|
+
const requestsInWindow = finiteNumber5(usage?.["requests_in_window"]);
|
|
9621
|
+
const weightedInWindow = finiteNumber5(usage?.["weighted_in_window"]);
|
|
9622
|
+
const resetsAt = isoInstant3(window?.["resets_at"]);
|
|
9623
|
+
let usedPercent = null;
|
|
9624
|
+
if (hardCap !== void 0 && hardCap > 0 && requestsInWindow !== void 0) {
|
|
9625
|
+
usedPercent = Math.round(Math.min(100, requestsInWindow / hardCap * 100) * 10) / 10;
|
|
9626
|
+
} else if (softLimit !== void 0 && softLimit > 0 && weightedInWindow !== void 0) {
|
|
9627
|
+
usedPercent = Math.round(Math.min(100, weightedInWindow / softLimit * 100) * 10) / 10;
|
|
9628
|
+
}
|
|
9629
|
+
if (usedPercent === null && resetsAt === void 0) return null;
|
|
9630
|
+
return [
|
|
9631
|
+
{
|
|
9632
|
+
id: "five-hour",
|
|
9633
|
+
label: "5 hours",
|
|
9634
|
+
scope: "all",
|
|
9635
|
+
usedPercent,
|
|
9636
|
+
windowMinutes: 5 * 60,
|
|
9637
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9638
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9639
|
+
state: "fresh"
|
|
9640
|
+
}
|
|
9641
|
+
];
|
|
9642
|
+
}
|
|
9643
|
+
function parseSyntheticQuotasPayload(payload, now) {
|
|
9644
|
+
if (!isRecord7(payload)) return null;
|
|
9645
|
+
const fiveHour = isRecord7(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
|
|
9646
|
+
const weekly = isRecord7(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
|
|
9647
|
+
const windows = [];
|
|
9648
|
+
if (fiveHour) {
|
|
9649
|
+
const max = finiteNumber5(fiveHour["max"]);
|
|
9650
|
+
const remaining = finiteNumber5(fiveHour["remaining"]);
|
|
9651
|
+
const usedPercent = max !== void 0 && max > 0 && remaining !== void 0 ? Math.round(Math.min(100, (max - remaining) / max * 100) * 10) / 10 : null;
|
|
9652
|
+
const resetsAt = isoInstant3(fiveHour["nextTickAt"]);
|
|
9653
|
+
windows.push({
|
|
9654
|
+
id: "five-hour",
|
|
9655
|
+
label: "5 hours",
|
|
9656
|
+
scope: "all",
|
|
9657
|
+
usedPercent,
|
|
9658
|
+
windowMinutes: 5 * 60,
|
|
9659
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9660
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9661
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
9662
|
+
});
|
|
9663
|
+
}
|
|
9664
|
+
if (weekly) {
|
|
9665
|
+
const percentRemaining = finiteNumber5(weekly["percentRemaining"]);
|
|
9666
|
+
const usedPercent = percentRemaining !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - percentRemaining)) * 10) / 10 : null;
|
|
9667
|
+
const resetsAt = isoInstant3(weekly["nextRegenAt"]);
|
|
9668
|
+
windows.push({
|
|
9669
|
+
id: "seven-day",
|
|
9670
|
+
label: "7 days",
|
|
9671
|
+
scope: "all",
|
|
9672
|
+
usedPercent,
|
|
9673
|
+
windowMinutes: 7 * 24 * 60,
|
|
9674
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9675
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9676
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
9677
|
+
});
|
|
9678
|
+
}
|
|
9679
|
+
return windows.length > 0 ? windows : null;
|
|
9680
|
+
}
|
|
9681
|
+
var CLINE_WINDOW_CONFIG = {
|
|
9682
|
+
five_hour: { id: "five-hour", label: "5 hours", minutes: 5 * 60 },
|
|
9683
|
+
weekly: { id: "seven-day", label: "7 days", minutes: 7 * 24 * 60 },
|
|
9684
|
+
monthly: { id: "thirty-day", label: "30 days", minutes: 30 * 24 * 60 }
|
|
9685
|
+
};
|
|
9686
|
+
function parseClinePassUsageLimitsPayload(payload, now) {
|
|
9687
|
+
if (!isRecord7(payload)) return null;
|
|
9688
|
+
const data = isRecord7(payload["data"]) ? payload["data"] : payload;
|
|
9689
|
+
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
9690
|
+
const windows = [];
|
|
9691
|
+
for (const raw of limits) {
|
|
9692
|
+
if (!isRecord7(raw)) continue;
|
|
9693
|
+
const config = CLINE_WINDOW_CONFIG[typeof raw["type"] === "string" ? raw["type"] : ""];
|
|
9694
|
+
if (!config) continue;
|
|
9695
|
+
const usedPercent = finitePercent4(raw["percentUsed"]);
|
|
9696
|
+
if (usedPercent === null) continue;
|
|
9697
|
+
const resetsAt = isoInstant3(raw["resetsAt"]);
|
|
9698
|
+
windows.push({
|
|
9699
|
+
id: config.id,
|
|
9700
|
+
label: config.label,
|
|
9701
|
+
scope: "all",
|
|
9702
|
+
usedPercent,
|
|
9703
|
+
windowMinutes: config.minutes,
|
|
9704
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
9705
|
+
remainingSeconds: secondsUntil7(resetsAt, now),
|
|
9706
|
+
state: "fresh"
|
|
9707
|
+
});
|
|
9708
|
+
}
|
|
9709
|
+
return windows.length > 0 ? windows : null;
|
|
9710
|
+
}
|
|
9711
|
+
|
|
9712
|
+
// src/allowance/ProviderKeyQuotaService.ts
|
|
9713
|
+
function parseQuotaPayload(adapter, payload, now) {
|
|
9714
|
+
switch (adapter) {
|
|
9715
|
+
case "zai":
|
|
9716
|
+
return parseZaiQuotaPayload(payload, now);
|
|
9717
|
+
case "minimax-token-plan":
|
|
9718
|
+
return parseMiniMaxTokenPlanPayload(payload, now);
|
|
9719
|
+
case "umans":
|
|
9720
|
+
return parseUmansUsagePayload(payload, now);
|
|
9721
|
+
case "synthetic":
|
|
9722
|
+
return parseSyntheticQuotasPayload(payload, now);
|
|
9723
|
+
case "cline-pass":
|
|
9724
|
+
return parseClinePassUsageLimitsPayload(payload, now);
|
|
9725
|
+
}
|
|
9726
|
+
}
|
|
9727
|
+
var PROVIDER_KEY_QUOTA_CACHE_MS = 5 * 6e4;
|
|
9728
|
+
function resolvedBaseUrl(row) {
|
|
9729
|
+
const modes = row.apiModes ?? [];
|
|
9730
|
+
const selected = row.selectedApiModeId ? modes.find((mode) => mode.id === row.selectedApiModeId) : void 0;
|
|
9731
|
+
const fallback = modes[0];
|
|
9732
|
+
const modeBase = selected?.baseUrl ?? fallback?.baseUrl;
|
|
9733
|
+
return modeBase ?? row.codingPlan?.baseUrl ?? row.baseUrl;
|
|
9734
|
+
}
|
|
9735
|
+
function rowKeyEntries(row) {
|
|
9736
|
+
const pool = (row.apiKeys ?? []).filter((entry) => entry.apiKey.length > 0);
|
|
9737
|
+
if (pool.length > 0) return pool.map((entry) => ({ id: entry.id, apiKey: entry.apiKey }));
|
|
9738
|
+
if (row.apiKey.length > 0) {
|
|
9739
|
+
return [{ id: `${row.id}:default`, apiKey: row.apiKey }];
|
|
9740
|
+
}
|
|
9741
|
+
return [];
|
|
9742
|
+
}
|
|
9743
|
+
var ProviderKeyQuotaService = class {
|
|
9744
|
+
constructor(box, fetchImpl = (url, init) => (0, import_upstreamFetch10.fetchUpstream)(url, init, { redactBodies: true }), now = Date.now) {
|
|
9745
|
+
this.box = box;
|
|
9746
|
+
this.fetchImpl = fetchImpl;
|
|
9747
|
+
this.now = now;
|
|
9748
|
+
}
|
|
9749
|
+
box;
|
|
9750
|
+
fetchImpl;
|
|
9751
|
+
now;
|
|
9752
|
+
cache = /* @__PURE__ */ new Map();
|
|
9753
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
9754
|
+
/**
|
|
9755
|
+
* Quota for one key of a provider row, or `null` when the row has no quota
|
|
9756
|
+
* adapter / no such key. Cache-first; concurrent reads share one flight.
|
|
9757
|
+
*/
|
|
9758
|
+
async quotaFor(row, keyId, options = {}) {
|
|
9759
|
+
const adapter = detectProviderKeyQuotaAdapter(resolvedBaseUrl(row));
|
|
9760
|
+
if (!adapter) return null;
|
|
9761
|
+
const entry = rowKeyEntries(row).find((candidate) => candidate.id === keyId);
|
|
9762
|
+
if (!entry) return null;
|
|
9763
|
+
const cacheKey = `${row.id}\0${keyId}`;
|
|
9764
|
+
const now = this.now();
|
|
9765
|
+
const cached = this.cache.get(cacheKey);
|
|
9766
|
+
if (!options.force && cached && Date.parse(cached.expiresAt) > now) return cached;
|
|
9767
|
+
const running = this.inFlight.get(cacheKey);
|
|
9768
|
+
if (running) return running;
|
|
9769
|
+
const promise = this.fetchQuota(adapter, row, entry.apiKey, cacheKey).catch((error) => {
|
|
9770
|
+
void error;
|
|
9771
|
+
const previous = this.cache.get(cacheKey);
|
|
9772
|
+
if (previous) {
|
|
9773
|
+
const degraded = {
|
|
9774
|
+
...previous,
|
|
9775
|
+
expiresAt: new Date(now + PROVIDER_KEY_QUOTA_CACHE_MS).toISOString(),
|
|
9776
|
+
windows: previous.windows.map((window) => ({
|
|
9777
|
+
...window,
|
|
9778
|
+
state: window.usedPercent !== null || window.resetsAt ? "stale" : window.state
|
|
9779
|
+
})),
|
|
9780
|
+
errorCode: "quota_request_failed"
|
|
9781
|
+
};
|
|
9782
|
+
this.cache.set(cacheKey, degraded);
|
|
9783
|
+
return degraded;
|
|
9784
|
+
}
|
|
9785
|
+
return null;
|
|
9786
|
+
}).finally(() => this.inFlight.delete(cacheKey));
|
|
9787
|
+
this.inFlight.set(cacheKey, promise);
|
|
9788
|
+
return promise;
|
|
9789
|
+
}
|
|
9790
|
+
/** Drop cached rows for a provider (key added/removed/rotated). */
|
|
9791
|
+
invalidateProvider(providerRowId) {
|
|
9792
|
+
for (const key of this.cache.keys()) {
|
|
9793
|
+
if (key.startsWith(`${providerRowId}\0`)) this.cache.delete(key);
|
|
9794
|
+
}
|
|
9795
|
+
}
|
|
9796
|
+
async fetchQuota(adapter, row, rawKey, cacheKey) {
|
|
9797
|
+
const baseUrl = resolvedBaseUrl(row);
|
|
9798
|
+
const url = providerKeyQuotaUrl(adapter, baseUrl);
|
|
9799
|
+
const key = this.box.decryptMaybe(rawKey);
|
|
9800
|
+
const now = this.now();
|
|
9801
|
+
const response = await this.fetchImpl(url, {
|
|
9802
|
+
method: "GET",
|
|
9803
|
+
headers: {
|
|
9804
|
+
Authorization: providerKeyQuotaAuthHeader(adapter, key),
|
|
9805
|
+
Accept: "application/json",
|
|
9806
|
+
"Content-Type": "application/json",
|
|
9807
|
+
// The row's static identity headers ride along — the Cline usage
|
|
9808
|
+
// endpoint sits behind the SAME client-identity 403 gate as inference.
|
|
9809
|
+
...(0, import_core4.mergeExtraHeaders)({}, row.extraHeaders)
|
|
9810
|
+
},
|
|
9811
|
+
signal: AbortSignal.timeout(15e3)
|
|
9812
|
+
});
|
|
9813
|
+
if (response.status === 401 || response.status === 403) {
|
|
9814
|
+
const snapshot2 = {
|
|
9815
|
+
adapter,
|
|
9816
|
+
observedAt: new Date(now).toISOString(),
|
|
9817
|
+
expiresAt: new Date(now + PROVIDER_KEY_QUOTA_CACHE_MS).toISOString(),
|
|
9818
|
+
windows: [],
|
|
9819
|
+
errorCode: "quota_unauthorized"
|
|
9820
|
+
};
|
|
9821
|
+
this.cache.set(cacheKey, snapshot2);
|
|
9822
|
+
return snapshot2;
|
|
9823
|
+
}
|
|
9824
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
9825
|
+
let payload;
|
|
9826
|
+
try {
|
|
9827
|
+
payload = await response.json();
|
|
9828
|
+
} catch {
|
|
9829
|
+
throw new Error("invalid JSON");
|
|
9830
|
+
}
|
|
9831
|
+
const windows = parseQuotaPayload(adapter, payload, now);
|
|
9832
|
+
const snapshot = {
|
|
9833
|
+
adapter,
|
|
9834
|
+
observedAt: new Date(now).toISOString(),
|
|
9835
|
+
expiresAt: new Date(now + PROVIDER_KEY_QUOTA_CACHE_MS).toISOString(),
|
|
9836
|
+
windows: windows ?? [],
|
|
9837
|
+
...windows ? {} : { errorCode: "quota_unavailable" }
|
|
9838
|
+
};
|
|
9839
|
+
this.cache.set(cacheKey, snapshot);
|
|
9840
|
+
return snapshot;
|
|
9841
|
+
}
|
|
9842
|
+
};
|
|
9843
|
+
|
|
7773
9844
|
// src/commands/paths.ts
|
|
7774
9845
|
var import_node_path9 = require("path");
|
|
7775
9846
|
function defaultVouchersPath(configPath) {
|
|
@@ -7806,7 +9877,7 @@ function defaultBillingDir(configPath) {
|
|
|
7806
9877
|
// src/image-generation/ImageDoctorService.ts
|
|
7807
9878
|
var import_image_generation = require("@omnicross/core/image-generation");
|
|
7808
9879
|
var import_outbound_api7 = require("@omnicross/core/outbound-api");
|
|
7809
|
-
var
|
|
9880
|
+
var import_subscriptions9 = require("@omnicross/subscriptions");
|
|
7810
9881
|
|
|
7811
9882
|
// src/image-generation/FileCodexImageCapabilityEvidenceSource.ts
|
|
7812
9883
|
var import_node_crypto13 = require("crypto");
|
|
@@ -8244,7 +10315,7 @@ function createImageDoctorService(options) {
|
|
|
8244
10315
|
paths,
|
|
8245
10316
|
ttlMs: config.evidenceTtlMs
|
|
8246
10317
|
}));
|
|
8247
|
-
const createLiveVerifier = options.createLiveVerifier ?? ((strategy, config) => (0,
|
|
10318
|
+
const createLiveVerifier = options.createLiveVerifier ?? ((strategy, config) => (0, import_subscriptions9.createCodexImageLiveVerifier)({
|
|
8248
10319
|
authStrategy: strategy,
|
|
8249
10320
|
generationTimeoutMs: config.queue.generationTimeoutMs
|
|
8250
10321
|
}));
|
|
@@ -8606,7 +10677,7 @@ var ImageCleanupService = class {
|
|
|
8606
10677
|
var import_node_crypto16 = require("crypto");
|
|
8607
10678
|
var import_image_generation5 = require("@omnicross/core/image-generation");
|
|
8608
10679
|
var import_outbound_api8 = require("@omnicross/core/outbound-api");
|
|
8609
|
-
var
|
|
10680
|
+
var import_subscriptions10 = require("@omnicross/subscriptions");
|
|
8610
10681
|
|
|
8611
10682
|
// src/image-generation/ImageApiRuntimeResolver.ts
|
|
8612
10683
|
var import_node_crypto14 = require("crypto");
|
|
@@ -9137,7 +11208,7 @@ function createImageRuntimeGeneration(options) {
|
|
|
9137
11208
|
now: options.now ?? Date.now,
|
|
9138
11209
|
referenceStore: options.storage.referenceStore,
|
|
9139
11210
|
stateStore: options.storage.stateStore
|
|
9140
|
-
}) : (0,
|
|
11211
|
+
}) : (0, import_subscriptions10.createCodexSubscriptionImageProvider)({
|
|
9141
11212
|
authStrategy,
|
|
9142
11213
|
evidenceSource: generationEvidenceSource,
|
|
9143
11214
|
executionScheduler: scheduler,
|
|
@@ -12281,7 +14352,7 @@ var ImageRuntimeManager = class {
|
|
|
12281
14352
|
};
|
|
12282
14353
|
|
|
12283
14354
|
// src/ports/ConfigFileProviderConfigSource.ts
|
|
12284
|
-
var
|
|
14355
|
+
var import_core5 = require("@omnicross/core");
|
|
12285
14356
|
var EMPTY_CHAIN = {
|
|
12286
14357
|
providerTransformers: [],
|
|
12287
14358
|
modelTransformers: []
|
|
@@ -12306,8 +14377,8 @@ var ConfigFileProviderConfigSource = class {
|
|
|
12306
14377
|
reloadHook;
|
|
12307
14378
|
constructor(config) {
|
|
12308
14379
|
for (const p of config.providers) this.providers.set(p.id, p);
|
|
12309
|
-
this.transformerService = new
|
|
12310
|
-
void (0,
|
|
14380
|
+
this.transformerService = new import_core5.TransformerService();
|
|
14381
|
+
void (0, import_core5.registerBuiltinTransformers)(this.transformerService);
|
|
12311
14382
|
}
|
|
12312
14383
|
// ── Reload hook (key-pool design D4) ───────────────────────────────────────
|
|
12313
14384
|
/**
|
|
@@ -12328,7 +14399,7 @@ var ConfigFileProviderConfigSource = class {
|
|
|
12328
14399
|
}
|
|
12329
14400
|
/** Await the built-in transformer registration (tests await this before dispatch). */
|
|
12330
14401
|
async ready() {
|
|
12331
|
-
await (0,
|
|
14402
|
+
await (0, import_core5.registerBuiltinTransformers)(this.transformerService);
|
|
12332
14403
|
}
|
|
12333
14404
|
// ── Hot-reload seam (admin dashboard, RT3 design D6) ───────────────────────
|
|
12334
14405
|
/**
|
|
@@ -12439,6 +14510,10 @@ function toLLMProvider(row) {
|
|
|
12439
14510
|
// `parseProviderInput`), so customizations are preserved (the row value wins).
|
|
12440
14511
|
apiModes: row.apiModes,
|
|
12441
14512
|
selectedApiModeId: row.selectedApiModeId,
|
|
14513
|
+
// Static extra request headers ride along verbatim (load-guarded — no
|
|
14514
|
+
// auth/content names); core's `getProviderHeaders` merges them into every
|
|
14515
|
+
// BYO request, and the same-format relay path inherits that funnel.
|
|
14516
|
+
extraHeaders: row.extraHeaders,
|
|
12442
14517
|
// Official-Anthropic signature handling only matters for the Anthropic
|
|
12443
14518
|
// ingress (deferred → 502); leave it off for the BYO transform path.
|
|
12444
14519
|
isOfficial: false
|
|
@@ -13803,10 +15878,13 @@ function bucketLabel(bucketStartTs, bucket) {
|
|
|
13803
15878
|
}
|
|
13804
15879
|
|
|
13805
15880
|
// src/ports/JsonOutboundKeyDb.ts
|
|
15881
|
+
var import_node_fs19 = require("fs");
|
|
15882
|
+
var import_core6 = require("@omnicross/core");
|
|
15883
|
+
|
|
15884
|
+
// src/ports/atomicFile.ts
|
|
13806
15885
|
var import_node_crypto22 = require("crypto");
|
|
13807
15886
|
var import_node_fs18 = require("fs");
|
|
13808
15887
|
var import_node_path22 = require("path");
|
|
13809
|
-
var import_core3 = require("@omnicross/core");
|
|
13810
15888
|
function atomicReplaceUtf8(targetPath, contents) {
|
|
13811
15889
|
const tempPath = (0, import_node_path22.join)(
|
|
13812
15890
|
(0, import_node_path22.dirname)(targetPath),
|
|
@@ -13836,6 +15914,8 @@ function atomicReplaceUtf8(targetPath, contents) {
|
|
|
13836
15914
|
throw error;
|
|
13837
15915
|
}
|
|
13838
15916
|
}
|
|
15917
|
+
|
|
15918
|
+
// src/ports/JsonOutboundKeyDb.ts
|
|
13839
15919
|
var JsonOutboundKeyDb = class {
|
|
13840
15920
|
/**
|
|
13841
15921
|
* @param secretBox OPTIONAL reversible-secret codec. When present, a created
|
|
@@ -13921,7 +16001,7 @@ var JsonOutboundKeyDb = class {
|
|
|
13921
16001
|
});
|
|
13922
16002
|
}
|
|
13923
16003
|
async outboundApiKeysSetPermissions(id, permissions) {
|
|
13924
|
-
const exact = (0,
|
|
16004
|
+
const exact = (0, import_core6.validateOutboundPermissions)(permissions);
|
|
13925
16005
|
return this.mutateRow(id, (row) => {
|
|
13926
16006
|
if (row.revokedAt !== null) return false;
|
|
13927
16007
|
row.allowedEndpoints = [...exact];
|
|
@@ -13978,9 +16058,9 @@ var JsonOutboundKeyDb = class {
|
|
|
13978
16058
|
}
|
|
13979
16059
|
/** Read the key rows, tolerating a missing/corrupt file (→ empty list). */
|
|
13980
16060
|
readRows() {
|
|
13981
|
-
if (!(0,
|
|
16061
|
+
if (!(0, import_node_fs19.existsSync)(this.keysPath)) return [];
|
|
13982
16062
|
try {
|
|
13983
|
-
const parsed = JSON.parse((0,
|
|
16063
|
+
const parsed = JSON.parse((0, import_node_fs19.readFileSync)(this.keysPath, "utf8"));
|
|
13984
16064
|
return Array.isArray(parsed) ? parsed : [];
|
|
13985
16065
|
} catch {
|
|
13986
16066
|
return [];
|
|
@@ -13997,7 +16077,7 @@ function applyPolicyField(row, field, value) {
|
|
|
13997
16077
|
}
|
|
13998
16078
|
|
|
13999
16079
|
// src/ports/JsonPricingStore.ts
|
|
14000
|
-
var
|
|
16080
|
+
var import_node_fs20 = require("fs");
|
|
14001
16081
|
var import_node_crypto23 = require("crypto");
|
|
14002
16082
|
var JsonPricingStore = class {
|
|
14003
16083
|
constructor(pricingPath) {
|
|
@@ -14012,9 +16092,9 @@ var JsonPricingStore = class {
|
|
|
14012
16092
|
* otherwise unusable pricing table after a crash or manual file edit.
|
|
14013
16093
|
*/
|
|
14014
16094
|
hasUsableSnapshot() {
|
|
14015
|
-
if (!(0,
|
|
16095
|
+
if (!(0, import_node_fs20.existsSync)(this.pricingPath)) return false;
|
|
14016
16096
|
try {
|
|
14017
|
-
const parsed = JSON.parse((0,
|
|
16097
|
+
const parsed = JSON.parse((0, import_node_fs20.readFileSync)(this.pricingPath, "utf8"));
|
|
14018
16098
|
return Array.isArray(parsed) && parsed.some(isUsablePricingRow);
|
|
14019
16099
|
} catch {
|
|
14020
16100
|
return false;
|
|
@@ -14127,9 +16207,9 @@ var JsonPricingStore = class {
|
|
|
14127
16207
|
}
|
|
14128
16208
|
/** Read the pricing rows, tolerating a missing/corrupt file (→ empty list). */
|
|
14129
16209
|
readRows() {
|
|
14130
|
-
if (!(0,
|
|
16210
|
+
if (!(0, import_node_fs20.existsSync)(this.pricingPath)) return [];
|
|
14131
16211
|
try {
|
|
14132
|
-
const parsed = JSON.parse((0,
|
|
16212
|
+
const parsed = JSON.parse((0, import_node_fs20.readFileSync)(this.pricingPath, "utf8"));
|
|
14133
16213
|
return Array.isArray(parsed) ? parsed : [];
|
|
14134
16214
|
} catch {
|
|
14135
16215
|
return [];
|
|
@@ -14138,18 +16218,18 @@ var JsonPricingStore = class {
|
|
|
14138
16218
|
writeRows(rows) {
|
|
14139
16219
|
const temporaryPath = `${this.pricingPath}.${process.pid}.${(0, import_node_crypto23.randomUUID)()}.tmp`;
|
|
14140
16220
|
try {
|
|
14141
|
-
(0,
|
|
16221
|
+
(0, import_node_fs20.writeFileSync)(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
|
|
14142
16222
|
encoding: "utf8",
|
|
14143
16223
|
flag: "wx"
|
|
14144
16224
|
});
|
|
14145
16225
|
this.replaceFile(temporaryPath);
|
|
14146
16226
|
} finally {
|
|
14147
|
-
(0,
|
|
16227
|
+
(0, import_node_fs20.rmSync)(temporaryPath, { force: true });
|
|
14148
16228
|
}
|
|
14149
16229
|
}
|
|
14150
16230
|
/** Isolated for deterministic failure testing; never removes the target. */
|
|
14151
16231
|
replaceFile(temporaryPath) {
|
|
14152
|
-
(0,
|
|
16232
|
+
(0, import_node_fs20.renameSync)(temporaryPath, this.pricingPath);
|
|
14153
16233
|
}
|
|
14154
16234
|
};
|
|
14155
16235
|
function isUsablePricingRow(value) {
|
|
@@ -14159,7 +16239,7 @@ function isUsablePricingRow(value) {
|
|
|
14159
16239
|
}
|
|
14160
16240
|
|
|
14161
16241
|
// src/pricing/PricingRefreshScheduler.ts
|
|
14162
|
-
var
|
|
16242
|
+
var import_node_fs21 = require("fs");
|
|
14163
16243
|
var EMPTY_STATE2 = {
|
|
14164
16244
|
lastAttemptAt: null,
|
|
14165
16245
|
lastSuccessAt: null,
|
|
@@ -14197,9 +16277,9 @@ var PricingRefreshScheduler = class {
|
|
|
14197
16277
|
this.timer = null;
|
|
14198
16278
|
}
|
|
14199
16279
|
getState() {
|
|
14200
|
-
if (!(0,
|
|
16280
|
+
if (!(0, import_node_fs21.existsSync)(this.statePath)) return { ...EMPTY_STATE2, sources: [] };
|
|
14201
16281
|
try {
|
|
14202
|
-
const value = JSON.parse((0,
|
|
16282
|
+
const value = JSON.parse((0, import_node_fs21.readFileSync)(this.statePath, "utf8"));
|
|
14203
16283
|
return {
|
|
14204
16284
|
lastAttemptAt: finiteOrNull(value.lastAttemptAt),
|
|
14205
16285
|
lastSuccessAt: finiteOrNull(value.lastSuccessAt),
|
|
@@ -14252,9 +16332,9 @@ var PricingRefreshScheduler = class {
|
|
|
14252
16332
|
}
|
|
14253
16333
|
writeState(state) {
|
|
14254
16334
|
const temporaryPath = `${this.statePath}.tmp`;
|
|
14255
|
-
(0,
|
|
16335
|
+
(0, import_node_fs21.writeFileSync)(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
14256
16336
|
`, "utf8");
|
|
14257
|
-
(0,
|
|
16337
|
+
(0, import_node_fs21.renameSync)(temporaryPath, this.statePath);
|
|
14258
16338
|
}
|
|
14259
16339
|
};
|
|
14260
16340
|
function finiteOrNull(value) {
|
|
@@ -14262,7 +16342,7 @@ function finiteOrNull(value) {
|
|
|
14262
16342
|
}
|
|
14263
16343
|
|
|
14264
16344
|
// src/ports/JsonVoucherDb.ts
|
|
14265
|
-
var
|
|
16345
|
+
var import_node_fs22 = require("fs");
|
|
14266
16346
|
var JsonVoucherDb = class {
|
|
14267
16347
|
constructor(vouchersPath) {
|
|
14268
16348
|
this.vouchersPath = vouchersPath;
|
|
@@ -14340,27 +16420,27 @@ var JsonVoucherDb = class {
|
|
|
14340
16420
|
}
|
|
14341
16421
|
/** Read the voucher rows, tolerating a missing/corrupt file (→ empty list). */
|
|
14342
16422
|
readRows() {
|
|
14343
|
-
if (!(0,
|
|
16423
|
+
if (!(0, import_node_fs22.existsSync)(this.vouchersPath)) return [];
|
|
14344
16424
|
try {
|
|
14345
|
-
const parsed = JSON.parse((0,
|
|
16425
|
+
const parsed = JSON.parse((0, import_node_fs22.readFileSync)(this.vouchersPath, "utf8"));
|
|
14346
16426
|
return Array.isArray(parsed) ? parsed : [];
|
|
14347
16427
|
} catch {
|
|
14348
16428
|
return [];
|
|
14349
16429
|
}
|
|
14350
16430
|
}
|
|
14351
16431
|
writeRows(rows) {
|
|
14352
|
-
(0,
|
|
16432
|
+
(0, import_node_fs22.writeFileSync)(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
|
|
14353
16433
|
}
|
|
14354
16434
|
};
|
|
14355
16435
|
|
|
14356
16436
|
// src/ports/JsonSubscriptionCredentialStore.ts
|
|
14357
|
-
var
|
|
16437
|
+
var import_node_fs24 = require("fs");
|
|
14358
16438
|
var import_node_path24 = require("path");
|
|
14359
16439
|
var import_SubscriptionAccountHealth2 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
14360
16440
|
var import_AccountAllowanceScheduling3 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
14361
|
-
var
|
|
16441
|
+
var import_upstreamFetch11 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
14362
16442
|
var import_SubscriptionIdentityStore2 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
14363
|
-
var
|
|
16443
|
+
var import_subscriptions11 = require("@omnicross/subscriptions");
|
|
14364
16444
|
|
|
14365
16445
|
// src/ports/account-sync.ts
|
|
14366
16446
|
function viewOf(tokens) {
|
|
@@ -14404,7 +16484,7 @@ function findDuplicateCredentialIds(accounts) {
|
|
|
14404
16484
|
}
|
|
14405
16485
|
|
|
14406
16486
|
// src/ports/external-cli-credentials.ts
|
|
14407
|
-
var
|
|
16487
|
+
var import_node_fs23 = require("fs");
|
|
14408
16488
|
var import_node_os5 = require("os");
|
|
14409
16489
|
var import_node_path23 = require("path");
|
|
14410
16490
|
function externalStorePath(provider, home = (0, import_node_os5.homedir)()) {
|
|
@@ -14457,10 +16537,10 @@ function parseCodexTokensEnvelope(raw) {
|
|
|
14457
16537
|
}
|
|
14458
16538
|
function readExternalCliCredentials(provider, home = (0, import_node_os5.homedir)()) {
|
|
14459
16539
|
const path2 = externalStorePath(provider, home);
|
|
14460
|
-
if (!(0,
|
|
16540
|
+
if (!(0, import_node_fs23.existsSync)(path2)) return null;
|
|
14461
16541
|
let raw;
|
|
14462
16542
|
try {
|
|
14463
|
-
const parsed = JSON.parse((0,
|
|
16543
|
+
const parsed = JSON.parse((0, import_node_fs23.readFileSync)(path2, "utf8"));
|
|
14464
16544
|
raw = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
14465
16545
|
} catch {
|
|
14466
16546
|
return null;
|
|
@@ -14483,16 +16563,18 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14483
16563
|
* as on relay refresh egresses from the SAME proxy IP as the
|
|
14484
16564
|
* account's traffic. NOT used by any read/write path.
|
|
14485
16565
|
*/
|
|
14486
|
-
constructor(tokensPath, box, fetchImpl = void 0, externalCliReader = readExternalCliCredentials) {
|
|
16566
|
+
constructor(tokensPath, box, fetchImpl = void 0, externalCliReader = readExternalCliCredentials, atomicReplace = atomicReplaceUtf8) {
|
|
14487
16567
|
this.tokensPath = tokensPath;
|
|
14488
16568
|
this.box = box;
|
|
14489
16569
|
this.fetchImpl = fetchImpl;
|
|
14490
16570
|
this.externalCliReader = externalCliReader;
|
|
16571
|
+
this.atomicReplace = atomicReplace;
|
|
14491
16572
|
}
|
|
14492
16573
|
tokensPath;
|
|
14493
16574
|
box;
|
|
14494
16575
|
fetchImpl;
|
|
14495
16576
|
externalCliReader;
|
|
16577
|
+
atomicReplace;
|
|
14496
16578
|
/**
|
|
14497
16579
|
* The proxy-aware `FetchLike` for one refresh round-trip (upstream-proxy M1). A
|
|
14498
16580
|
* TEST-injected `fetchImpl` is returned verbatim; otherwise the refresh routes
|
|
@@ -14506,7 +16588,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14506
16588
|
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
14507
16589
|
*/
|
|
14508
16590
|
buildRefreshFetch(providerId, accountId) {
|
|
14509
|
-
return this.fetchImpl ?? ((url, init) => (0,
|
|
16591
|
+
return this.fetchImpl ?? ((url, init) => (0, import_upstreamFetch11.fetchUpstream)(url, init, { providerId, accountId, redactBodies: true }));
|
|
14510
16592
|
}
|
|
14511
16593
|
/**
|
|
14512
16594
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -14547,7 +16629,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14547
16629
|
* other hot reads. Never returns token material.
|
|
14548
16630
|
*/
|
|
14549
16631
|
getAccountProxy(providerId, accountId) {
|
|
14550
|
-
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego") {
|
|
16632
|
+
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi" && providerId !== "grok" && providerId !== "copilot") {
|
|
14551
16633
|
return void 0;
|
|
14552
16634
|
}
|
|
14553
16635
|
return getAccountProxy(this.readConfig(), providerId, accountId);
|
|
@@ -14566,7 +16648,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14566
16648
|
const fingerprintOn = identityStore.isEnabled();
|
|
14567
16649
|
const now = Date.now();
|
|
14568
16650
|
const out = {};
|
|
14569
|
-
for (const provider of ["claude", "codex", "gemini", "opencodego"]) {
|
|
16651
|
+
for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi", "grok", "copilot"]) {
|
|
14570
16652
|
const sanitized = sanitizeAccounts(config, provider);
|
|
14571
16653
|
if (sanitized.length === 0) continue;
|
|
14572
16654
|
for (const account of sanitized) {
|
|
@@ -14632,7 +16714,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14632
16714
|
this.materializeMigration(config);
|
|
14633
16715
|
const refreshFetch = this.buildRefreshFetch("claude", capturedId);
|
|
14634
16716
|
try {
|
|
14635
|
-
const result = await
|
|
16717
|
+
const result = await import_subscriptions11.claudeOAuth.refreshAccessToken(claude.refreshToken, refreshFetch);
|
|
14636
16718
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
14637
16719
|
const next = {
|
|
14638
16720
|
...claude,
|
|
@@ -14667,7 +16749,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14667
16749
|
this.materializeMigration(config);
|
|
14668
16750
|
const refreshFetch = this.buildRefreshFetch("codex", capturedId);
|
|
14669
16751
|
try {
|
|
14670
|
-
const result = await
|
|
16752
|
+
const result = await import_subscriptions11.codexOAuth.refreshAccessToken(codex.refreshToken, refreshFetch);
|
|
14671
16753
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
14672
16754
|
const next = {
|
|
14673
16755
|
...codex,
|
|
@@ -14705,7 +16787,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14705
16787
|
this.materializeMigration(config);
|
|
14706
16788
|
const refreshFetch = this.buildRefreshFetch("gemini", capturedId);
|
|
14707
16789
|
try {
|
|
14708
|
-
const result = await
|
|
16790
|
+
const result = await import_subscriptions11.geminiOAuth.refreshAccessToken(gemini.refreshToken, refreshFetch);
|
|
14709
16791
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
14710
16792
|
const next = {
|
|
14711
16793
|
...gemini,
|
|
@@ -14724,6 +16806,107 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14724
16806
|
}
|
|
14725
16807
|
});
|
|
14726
16808
|
}
|
|
16809
|
+
/**
|
|
16810
|
+
* Refresh the Kimi Code (Moonshot) OAuth access token (device-flow grant).
|
|
16811
|
+
* Kimi ROTATES the refresh token, so the response's pair is written back
|
|
16812
|
+
* whole; the account's stable `deviceId` (fingerprint header input) is
|
|
16813
|
+
* preserved. The refresh call carries the CLI fingerprint headers. HONEST
|
|
16814
|
+
* `false` when no refresh_token.
|
|
16815
|
+
*/
|
|
16816
|
+
async refreshKimiToken() {
|
|
16817
|
+
return this.coalesce("kimi:active", async () => {
|
|
16818
|
+
const config = this.readConfig();
|
|
16819
|
+
const active = getActiveAccount(config, "kimi");
|
|
16820
|
+
const kimi = active?.tokens;
|
|
16821
|
+
if (!active || !kimi?.refreshToken) return false;
|
|
16822
|
+
const capturedId = active.id;
|
|
16823
|
+
this.materializeMigration(config);
|
|
16824
|
+
const refreshFetch = this.buildRefreshFetch("kimi", capturedId);
|
|
16825
|
+
try {
|
|
16826
|
+
const result = await import_subscriptions11.kimiOAuth.refreshAccessToken(
|
|
16827
|
+
kimi.refreshToken,
|
|
16828
|
+
refreshFetch,
|
|
16829
|
+
import_subscriptions11.kimiOAuth.kimiFingerprintHeaders(kimi.deviceId)
|
|
16830
|
+
);
|
|
16831
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
16832
|
+
const next = {
|
|
16833
|
+
...kimi,
|
|
16834
|
+
accessToken: result.accessToken,
|
|
16835
|
+
refreshToken: result.refreshToken,
|
|
16836
|
+
expiresAt,
|
|
16837
|
+
status: "authorized",
|
|
16838
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16839
|
+
errorMessage: void 0,
|
|
16840
|
+
syncWarning: void 0
|
|
16841
|
+
};
|
|
16842
|
+
this.writeBackById("kimi", capturedId, next);
|
|
16843
|
+
return true;
|
|
16844
|
+
} catch (error) {
|
|
16845
|
+
this.markExpiredById("kimi", capturedId, kimi, error);
|
|
16846
|
+
return false;
|
|
16847
|
+
}
|
|
16848
|
+
});
|
|
16849
|
+
}
|
|
16850
|
+
/**
|
|
16851
|
+
* Refresh the Grok (xAI SuperGrok) OAuth access token. The token endpoint is
|
|
16852
|
+
* resolved through OIDC discovery on every refresh (process-cached 1h by the
|
|
16853
|
+
* flow module) so a rotated endpoint document is picked up without a daemon
|
|
16854
|
+
* restart. HONEST `false` when no refresh_token.
|
|
16855
|
+
*/
|
|
16856
|
+
async refreshGrokToken() {
|
|
16857
|
+
return this.coalesce("grok:active", async () => {
|
|
16858
|
+
const config = this.readConfig();
|
|
16859
|
+
const active = getActiveAccount(config, "grok");
|
|
16860
|
+
const grok = active?.tokens;
|
|
16861
|
+
if (!active || !grok?.refreshToken) return false;
|
|
16862
|
+
const capturedId = active.id;
|
|
16863
|
+
this.materializeMigration(config);
|
|
16864
|
+
const refreshFetch = this.buildRefreshFetch("grok", capturedId);
|
|
16865
|
+
try {
|
|
16866
|
+
const tokenEndpoint = await import_subscriptions11.grokOAuth.resolveGrokTokenEndpoint(refreshFetch);
|
|
16867
|
+
const result = await import_subscriptions11.grokOAuth.refreshGrokAccessToken(grok.refreshToken, tokenEndpoint, refreshFetch);
|
|
16868
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
16869
|
+
const next = {
|
|
16870
|
+
...grok,
|
|
16871
|
+
accessToken: result.accessToken,
|
|
16872
|
+
refreshToken: result.refreshToken,
|
|
16873
|
+
expiresAt,
|
|
16874
|
+
status: "authorized",
|
|
16875
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16876
|
+
errorMessage: void 0,
|
|
16877
|
+
syncWarning: void 0
|
|
16878
|
+
};
|
|
16879
|
+
this.writeBackById("grok", capturedId, next);
|
|
16880
|
+
return true;
|
|
16881
|
+
} catch (error) {
|
|
16882
|
+
this.markExpiredById("grok", capturedId, grok, error);
|
|
16883
|
+
return false;
|
|
16884
|
+
}
|
|
16885
|
+
});
|
|
16886
|
+
}
|
|
16887
|
+
/**
|
|
16888
|
+
* "Refresh" a GitHub Copilot token — there is nothing to refresh (ghu_
|
|
16889
|
+
* tokens are long-lived with no exchange endpoint). A call here means the
|
|
16890
|
+
* strategy saw a 401 (the token was revoked); mark the account `expired`
|
|
16891
|
+
* with a re-authenticate message and return `false` (the proxy then declines
|
|
16892
|
+
* the retry instead of looping on a dead token).
|
|
16893
|
+
*/
|
|
16894
|
+
async refreshCopilotToken() {
|
|
16895
|
+
return this.coalesce("copilot:active", async () => {
|
|
16896
|
+
const config = this.readConfig();
|
|
16897
|
+
const active = getActiveAccount(config, "copilot");
|
|
16898
|
+
const copilot = active?.tokens;
|
|
16899
|
+
if (!active || !copilot?.accessToken) return false;
|
|
16900
|
+
this.materializeMigration(config);
|
|
16901
|
+
this.markExpiredById(
|
|
16902
|
+
"copilot",
|
|
16903
|
+
active.id,
|
|
16904
|
+
copilot,
|
|
16905
|
+
new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account")
|
|
16906
|
+
);
|
|
16907
|
+
return false;
|
|
16908
|
+
});
|
|
16909
|
+
}
|
|
14727
16910
|
/**
|
|
14728
16911
|
* Refresh a SPECIFIC managed account by id (background scheduler sweep and
|
|
14729
16912
|
* account-pool resolution). It uses only that account's stored refresh
|
|
@@ -14776,7 +16959,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14776
16959
|
}
|
|
14777
16960
|
const oauth = account.tokens;
|
|
14778
16961
|
if (!oauth.accessToken) return null;
|
|
14779
|
-
if (providerId === "codex" || providerId === "gemini") {
|
|
16962
|
+
if (providerId === "codex" || providerId === "gemini" || providerId === "kimi" || providerId === "grok" || providerId === "copilot") {
|
|
14780
16963
|
const expiresAtMs = oauth.expiresAt ? Date.parse(oauth.expiresAt) : 0;
|
|
14781
16964
|
const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - ACCOUNT_REFRESH_LEAD_MS;
|
|
14782
16965
|
if (expiringSoon && oauth.refreshToken) {
|
|
@@ -14865,8 +17048,35 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14865
17048
|
}
|
|
14866
17049
|
/** Dispatch one OAuth refresh round-trip to the provider's shared flow. */
|
|
14867
17050
|
async refreshUpstream(provider, refreshToken, accountId) {
|
|
14868
|
-
const
|
|
14869
|
-
|
|
17051
|
+
const refreshFetch = this.buildRefreshFetch(provider, accountId);
|
|
17052
|
+
if (provider === "kimi") {
|
|
17053
|
+
const account = accountId ? getAccountById(this.readConfig(), "kimi", accountId) : void 0;
|
|
17054
|
+
const deviceId = account?.tokens?.deviceId;
|
|
17055
|
+
const r2 = await import_subscriptions11.kimiOAuth.refreshAccessToken(
|
|
17056
|
+
refreshToken,
|
|
17057
|
+
refreshFetch,
|
|
17058
|
+
import_subscriptions11.kimiOAuth.kimiFingerprintHeaders(deviceId)
|
|
17059
|
+
);
|
|
17060
|
+
return {
|
|
17061
|
+
accessToken: r2.accessToken,
|
|
17062
|
+
refreshToken: r2.refreshToken,
|
|
17063
|
+
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
17064
|
+
};
|
|
17065
|
+
}
|
|
17066
|
+
if (provider === "grok") {
|
|
17067
|
+
const tokenEndpoint = await import_subscriptions11.grokOAuth.resolveGrokTokenEndpoint(refreshFetch);
|
|
17068
|
+
const r2 = await import_subscriptions11.grokOAuth.refreshGrokAccessToken(refreshToken, tokenEndpoint, refreshFetch);
|
|
17069
|
+
return {
|
|
17070
|
+
accessToken: r2.accessToken,
|
|
17071
|
+
refreshToken: r2.refreshToken,
|
|
17072
|
+
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
17073
|
+
};
|
|
17074
|
+
}
|
|
17075
|
+
if (provider === "copilot") {
|
|
17076
|
+
throw new Error("GitHub Copilot tokens cannot be refreshed \u2014 re-authenticate the account");
|
|
17077
|
+
}
|
|
17078
|
+
const flow = provider === "claude" ? import_subscriptions11.claudeOAuth : provider === "codex" ? import_subscriptions11.codexOAuth : import_subscriptions11.geminiOAuth;
|
|
17079
|
+
const r = await flow.refreshAccessToken(refreshToken, refreshFetch);
|
|
14870
17080
|
return {
|
|
14871
17081
|
accessToken: r.accessToken,
|
|
14872
17082
|
refreshToken: r.refreshToken,
|
|
@@ -15029,42 +17239,86 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15029
17239
|
/** Write the merged config to disk as pretty JSON (mkdir parent if needed).
|
|
15030
17240
|
* Encrypt-on-write: the token-material fields are encrypted (legacy plaintext
|
|
15031
17241
|
* `enc:v1:`; already-`enc:`/`$ENV` untouched) before serializing, so any
|
|
15032
|
-
* write incl. child 4's future refresh writes lands encrypted.
|
|
17242
|
+
* write incl. child 4's future refresh writes lands encrypted.
|
|
17243
|
+
* ATOMIC: temp + fsync + rename (`atomicReplaceUtf8`) — a failed or
|
|
17244
|
+
* interrupted write discards only the temp file; the prior `tokens.json`
|
|
17245
|
+
* survives byte-equal (bare `writeFileSync` truncate-writes lost every
|
|
17246
|
+
* account on a mid-write failure, 2026-09-06). */
|
|
15033
17247
|
persist(config) {
|
|
15034
|
-
(0,
|
|
17248
|
+
(0, import_node_fs24.mkdirSync)((0, import_node_path24.dirname)(this.tokensPath), { recursive: true });
|
|
15035
17249
|
const encrypted = encryptTokens(config, this.box);
|
|
15036
|
-
|
|
17250
|
+
this.atomicReplace(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n");
|
|
15037
17251
|
}
|
|
15038
17252
|
/**
|
|
15039
|
-
* Read + parse `tokens.json`,
|
|
15040
|
-
*
|
|
15041
|
-
*
|
|
17253
|
+
* Read + parse `tokens.json`, then DECRYPT the token-material fields so every
|
|
17254
|
+
* getter returns plaintext (the subscription bearer path is byte-identical).
|
|
17255
|
+
*
|
|
17256
|
+
* A MISSING file is a legitimate first-boot state → minimal `{ updatedAt: '' }`.
|
|
17257
|
+
* A file that EXISTS but cannot be parsed as a JSON object is CORRUPT →
|
|
17258
|
+
* `quarantineCorrupt` moves it aside (once) before the empty config is
|
|
17259
|
+
* returned, so the unreadable accounts survive for manual recovery.
|
|
15042
17260
|
*
|
|
15043
|
-
* The
|
|
15044
|
-
*
|
|
15045
|
-
*
|
|
15046
|
-
*
|
|
15047
|
-
*
|
|
15048
|
-
*
|
|
15049
|
-
* `config.ts loadConfig`, which decrypts outside its parse try.
|
|
17261
|
+
* The DECRYPT runs OUTSIDE any try, so a wrong/missing master key or a
|
|
17262
|
+
* tampered `enc:` envelope FAILS FAST with the box's clear, secret-free
|
|
17263
|
+
* error (secrets spec "/ UX": SHALL fail-fast, SHALL NOT a swallowed
|
|
17264
|
+
* decrypt would report "no tokens" and silently send the WRONG bearer
|
|
17265
|
+
* upstream 401). Mirrors `config.ts loadConfig`, which decrypts outside
|
|
17266
|
+
* its parse try.
|
|
15050
17267
|
*/
|
|
15051
17268
|
readConfig() {
|
|
15052
|
-
if (!(0,
|
|
17269
|
+
if (!(0, import_node_fs24.existsSync)(this.tokensPath)) return { updatedAt: "" };
|
|
15053
17270
|
let parsed;
|
|
15054
17271
|
try {
|
|
15055
|
-
const raw = JSON.parse((0,
|
|
15056
|
-
|
|
17272
|
+
const raw = JSON.parse((0, import_node_fs24.readFileSync)(this.tokensPath, "utf8"));
|
|
17273
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
17274
|
+
return this.quarantineCorrupt("parsed JSON is not an object");
|
|
17275
|
+
}
|
|
17276
|
+
parsed = raw;
|
|
15057
17277
|
} catch {
|
|
15058
|
-
|
|
17278
|
+
return this.quarantineCorrupt("unparseable JSON");
|
|
15059
17279
|
}
|
|
15060
|
-
if (!parsed) return { updatedAt: "" };
|
|
15061
17280
|
const decrypted = decryptTokens(parsed, this.box);
|
|
15062
17281
|
return migrateLazily(decrypted);
|
|
15063
17282
|
}
|
|
17283
|
+
/** One-shot latch: a corrupt file is quarantined (or found unmovable) at
|
|
17284
|
+
* most once per process, so the hot read path never re-attempts or re-logs. */
|
|
17285
|
+
corruptQuarantined = false;
|
|
17286
|
+
/**
|
|
17287
|
+
* Quarantine a present-but-corrupt `tokens.json`, then treat it as empty.
|
|
17288
|
+
*
|
|
17289
|
+
* Renames the file to a sibling `tokens.json.corrupt-<stamp>` backup and
|
|
17290
|
+
* logs loudly (the daemon's stderr log; secret-free — reason + paths only).
|
|
17291
|
+
* The daemon KEEPS SERVING (API-key routing is unaffected; subscription
|
|
17292
|
+
* routing reports no credential, same as an absent file) while the corrupt
|
|
17293
|
+
* bytes survive for manual recovery — and, critically, the NEXT persist
|
|
17294
|
+
* (e.g. the user re-logging in) can no longer overwrite the only copy of
|
|
17295
|
+
* the old accounts, which is exactly how the 2026-09-06 incident turned a
|
|
17296
|
+
* recoverable truncated file into permanent account loss.
|
|
17297
|
+
*
|
|
17298
|
+
* Best-effort: if the rename fails (file locked, permissions), the corrupt
|
|
17299
|
+
* file is left in place and every later read still tolerates it as empty;
|
|
17300
|
+
* the latch still trips so the attempt + log happen exactly once.
|
|
17301
|
+
*/
|
|
17302
|
+
quarantineCorrupt(reason) {
|
|
17303
|
+
if (!this.corruptQuarantined) {
|
|
17304
|
+
this.corruptQuarantined = true;
|
|
17305
|
+
const backup = `${this.tokensPath}.corrupt-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
17306
|
+
let moved = false;
|
|
17307
|
+
try {
|
|
17308
|
+
(0, import_node_fs24.renameSync)(this.tokensPath, backup);
|
|
17309
|
+
moved = true;
|
|
17310
|
+
} catch {
|
|
17311
|
+
}
|
|
17312
|
+
console.error(
|
|
17313
|
+
`[JsonSubscriptionCredentialStore] tokens.json is corrupt (${reason}); ` + (moved ? `moved to '${backup}' and treated as empty \u2014 recover accounts from that backup before re-adding them` : `could not move '${this.tokensPath}' \u2014 treated as empty`)
|
|
17314
|
+
);
|
|
17315
|
+
}
|
|
17316
|
+
return { updatedAt: "" };
|
|
17317
|
+
}
|
|
15064
17318
|
};
|
|
15065
17319
|
|
|
15066
17320
|
// src/AccountHealthProbeScheduler.ts
|
|
15067
|
-
var
|
|
17321
|
+
var import_upstreamFetch12 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
15068
17322
|
|
|
15069
17323
|
// src/probe/CodexGenerationProbe.ts
|
|
15070
17324
|
var import_codexCliHeaders = require("@omnicross/core/provider-proxy/identity/codexCliHeaders");
|
|
@@ -15203,7 +17457,20 @@ var PROVIDER_PROBE_PLANS = {
|
|
|
15203
17457
|
// billable/wrong endpoint). Upgrade to `{ kind:'upstream' }` once verified.
|
|
15204
17458
|
codex: { kind: "local" },
|
|
15205
17459
|
gemini: { kind: "local" },
|
|
15206
|
-
opencodego: { kind: "local" }
|
|
17460
|
+
opencodego: { kind: "local" },
|
|
17461
|
+
// Kimi's `GET /coding/v1/usages` is a verified FREE authed GET (the allowance
|
|
17462
|
+
// collector uses it), but the probe path also needs the fingerprint headers —
|
|
17463
|
+
// keep the probe local until the collector covers the health surface.
|
|
17464
|
+
kimi: { kind: "local" },
|
|
17465
|
+
// Grok's billing proxy is a verified FREE authed GET (the allowance collector
|
|
17466
|
+
// uses it) but it REJECTS non-OAuth credentials and sits on a separate host
|
|
17467
|
+
// with its own product-gate header — keep the probe local, the collector
|
|
17468
|
+
// owns the health surface.
|
|
17469
|
+
grok: { kind: "local" },
|
|
17470
|
+
// The Copilot quota endpoint (copilot_internal/user) is a verified FREE
|
|
17471
|
+
// authed GET but lives on api.github.com with its own auth dialect and a
|
|
17472
|
+
// monthly-only window — the allowance collector owns the health surface.
|
|
17473
|
+
copilot: { kind: "local" }
|
|
15207
17474
|
};
|
|
15208
17475
|
function probePlanFor(providerId) {
|
|
15209
17476
|
return PROVIDER_PROBE_PLANS[providerId] ?? { kind: "local" };
|
|
@@ -15225,7 +17492,7 @@ var AccountHealthProbeScheduler = class {
|
|
|
15225
17492
|
this.logger = logger;
|
|
15226
17493
|
this.config = config;
|
|
15227
17494
|
this.now = opts.now ?? Date.now;
|
|
15228
|
-
this.fetchImpl = opts.fetchImpl ??
|
|
17495
|
+
this.fetchImpl = opts.fetchImpl ?? import_upstreamFetch12.fetchUpstream;
|
|
15229
17496
|
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
15230
17497
|
this.planFor = opts.planFor ?? probePlanFor;
|
|
15231
17498
|
}
|
|
@@ -15569,13 +17836,13 @@ var AccountHealthSweeper = class {
|
|
|
15569
17836
|
};
|
|
15570
17837
|
|
|
15571
17838
|
// src/audit/AuditPruneSweeper.ts
|
|
15572
|
-
var
|
|
17839
|
+
var import_node_fs27 = require("fs");
|
|
15573
17840
|
var import_node_path27 = require("path");
|
|
15574
17841
|
var import_promises6 = require("stream/promises");
|
|
15575
17842
|
var import_node_zlib = require("zlib");
|
|
15576
17843
|
|
|
15577
17844
|
// src/audit/auditDictionary.ts
|
|
15578
|
-
var
|
|
17845
|
+
var import_node_fs25 = require("fs");
|
|
15579
17846
|
var import_node_path25 = require("path");
|
|
15580
17847
|
|
|
15581
17848
|
// src/audit/auditBodyStore.ts
|
|
@@ -15808,7 +18075,7 @@ function parseEntries(raw) {
|
|
|
15808
18075
|
}
|
|
15809
18076
|
function plainShards(bodiesPath) {
|
|
15810
18077
|
try {
|
|
15811
|
-
return (0,
|
|
18078
|
+
return (0, import_node_fs25.readdirSync)(bodiesPath).filter(
|
|
15812
18079
|
(file) => file.endsWith(".jsonl") && isSafeSessionKey(file.slice(0, -".jsonl".length))
|
|
15813
18080
|
);
|
|
15814
18081
|
} catch {
|
|
@@ -15833,9 +18100,9 @@ function chooseDictionary(anchors) {
|
|
|
15833
18100
|
var EMPTY = { shards: 0, anchors: 0, savedBytes: 0 };
|
|
15834
18101
|
function compactAuditDay(dayPath) {
|
|
15835
18102
|
const bodiesPath = (0, import_node_path25.join)(dayPath, AUDIT_BODIES_DIR);
|
|
15836
|
-
if (!(0,
|
|
18103
|
+
if (!(0, import_node_fs25.existsSync)(bodiesPath)) return EMPTY;
|
|
15837
18104
|
const dictPath = (0, import_node_path25.join)(bodiesPath, AUDIT_DICT_FILE);
|
|
15838
|
-
if ((0,
|
|
18105
|
+
if ((0, import_node_fs25.existsSync)(dictPath) || (0, import_node_fs25.existsSync)(`${dictPath}.gz`)) return EMPTY;
|
|
15839
18106
|
const shardFiles = plainShards(bodiesPath);
|
|
15840
18107
|
if (shardFiles.length < 2) return EMPTY;
|
|
15841
18108
|
const loaded = /* @__PURE__ */ new Map();
|
|
@@ -15843,7 +18110,7 @@ function compactAuditDay(dayPath) {
|
|
|
15843
18110
|
for (const file of shardFiles) {
|
|
15844
18111
|
let entries;
|
|
15845
18112
|
try {
|
|
15846
|
-
entries = parseEntries((0,
|
|
18113
|
+
entries = parseEntries((0, import_node_fs25.readFileSync)((0, import_node_path25.join)(bodiesPath, file), "utf8"));
|
|
15847
18114
|
} catch {
|
|
15848
18115
|
continue;
|
|
15849
18116
|
}
|
|
@@ -15860,7 +18127,7 @@ function compactAuditDay(dayPath) {
|
|
|
15860
18127
|
ts: 0,
|
|
15861
18128
|
req: { base: null, anchor: "dict", pre: 0, suf: 0, ins: dictionary }
|
|
15862
18129
|
};
|
|
15863
|
-
(0,
|
|
18130
|
+
(0, import_node_fs25.writeFileSync)(dictPath, JSON.stringify(dictEntry) + "\n", "utf8");
|
|
15864
18131
|
const result = { shards: 0, anchors: 0, savedBytes: 0 };
|
|
15865
18132
|
for (const [file, entries] of loaded) {
|
|
15866
18133
|
let changed = false;
|
|
@@ -15880,11 +18147,11 @@ function compactAuditDay(dayPath) {
|
|
|
15880
18147
|
const target = (0, import_node_path25.join)(bodiesPath, file);
|
|
15881
18148
|
const temp = `${target}.compacting`;
|
|
15882
18149
|
try {
|
|
15883
|
-
(0,
|
|
15884
|
-
(0,
|
|
18150
|
+
(0, import_node_fs25.writeFileSync)(temp, rewritten.map((e) => JSON.stringify(e)).join("\n") + "\n", "utf8");
|
|
18151
|
+
(0, import_node_fs25.renameSync)(temp, target);
|
|
15885
18152
|
} catch {
|
|
15886
18153
|
try {
|
|
15887
|
-
if ((0,
|
|
18154
|
+
if ((0, import_node_fs25.existsSync)(temp)) (0, import_node_fs25.unlinkSync)(temp);
|
|
15888
18155
|
} catch {
|
|
15889
18156
|
}
|
|
15890
18157
|
continue;
|
|
@@ -15895,7 +18162,7 @@ function compactAuditDay(dayPath) {
|
|
|
15895
18162
|
}
|
|
15896
18163
|
if (result.shards === 0) {
|
|
15897
18164
|
try {
|
|
15898
|
-
(0,
|
|
18165
|
+
(0, import_node_fs25.unlinkSync)(dictPath);
|
|
15899
18166
|
} catch {
|
|
15900
18167
|
}
|
|
15901
18168
|
}
|
|
@@ -15903,11 +18170,11 @@ function compactAuditDay(dayPath) {
|
|
|
15903
18170
|
}
|
|
15904
18171
|
function compactAllClosedAuditDays(auditDir, now = Date.now) {
|
|
15905
18172
|
const run = { days: 0, shards: 0, savedBytes: 0 };
|
|
15906
|
-
if (!(0,
|
|
18173
|
+
if (!(0, import_node_fs25.existsSync)(auditDir)) return run;
|
|
15907
18174
|
const today = auditDayDirName(now());
|
|
15908
18175
|
let names;
|
|
15909
18176
|
try {
|
|
15910
|
-
names = (0,
|
|
18177
|
+
names = (0, import_node_fs25.readdirSync)(auditDir).filter(isAuditDayDir).sort();
|
|
15911
18178
|
} catch {
|
|
15912
18179
|
return run;
|
|
15913
18180
|
}
|
|
@@ -15926,7 +18193,7 @@ function compactAllClosedAuditDays(auditDir, now = Date.now) {
|
|
|
15926
18193
|
}
|
|
15927
18194
|
|
|
15928
18195
|
// src/audit/auditStats.ts
|
|
15929
|
-
var
|
|
18196
|
+
var import_node_fs26 = require("fs");
|
|
15930
18197
|
var import_node_path26 = require("path");
|
|
15931
18198
|
var SIDECAR_VERSION = 1;
|
|
15932
18199
|
var META_PREFIX_BYTES = 64 * 1024;
|
|
@@ -15935,9 +18202,9 @@ function auditStatsFileName(auditFile) {
|
|
|
15935
18202
|
return auditFile.replace(/\.jsonl$/, ".stats.json");
|
|
15936
18203
|
}
|
|
15937
18204
|
function readPersisted(path2) {
|
|
15938
|
-
if (!(0,
|
|
18205
|
+
if (!(0, import_node_fs26.existsSync)(path2)) return null;
|
|
15939
18206
|
try {
|
|
15940
|
-
const value = JSON.parse((0,
|
|
18207
|
+
const value = JSON.parse((0, import_node_fs26.readFileSync)(path2, "utf8"));
|
|
15941
18208
|
if (value.version !== SIDECAR_VERSION || !Number.isSafeInteger(value.auditBytes) || (value.auditBytes ?? -1) < 0 || !Number.isSafeInteger(value.requestCount) || (value.requestCount ?? -1) < 0 || !Number.isSafeInteger(value.errorCount) || (value.errorCount ?? -1) < 0 || (value.errorCount ?? 0) > (value.requestCount ?? -1) || typeof value.complete !== "boolean" || value.minTs !== null && !Number.isFinite(value.minTs) || value.maxTs !== null && !Number.isFinite(value.maxTs)) {
|
|
15942
18209
|
return null;
|
|
15943
18210
|
}
|
|
@@ -15967,7 +18234,7 @@ function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfte
|
|
|
15967
18234
|
minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
|
|
15968
18235
|
maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
|
|
15969
18236
|
};
|
|
15970
|
-
(0,
|
|
18237
|
+
(0, import_node_fs26.writeFileSync)(statsPath, JSON.stringify(next), "utf8");
|
|
15971
18238
|
}
|
|
15972
18239
|
function queryCovers(stats, from, to) {
|
|
15973
18240
|
return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
|
|
@@ -16025,7 +18292,7 @@ async function scanAuditFile(auditPath, startByte, auditBytes, from, to) {
|
|
|
16025
18292
|
prefixTruncated = false;
|
|
16026
18293
|
};
|
|
16027
18294
|
if (auditBytes > startByte) {
|
|
16028
|
-
const stream = (0,
|
|
18295
|
+
const stream = (0, import_node_fs26.createReadStream)(auditPath, {
|
|
16029
18296
|
start: startByte,
|
|
16030
18297
|
end: auditBytes - 1,
|
|
16031
18298
|
highWaterMark: READ_CHUNK_BYTES2
|
|
@@ -16078,12 +18345,12 @@ function mergePersistedStats(previous, appended) {
|
|
|
16078
18345
|
};
|
|
16079
18346
|
}
|
|
16080
18347
|
async function readAuditStats(auditDir, query2 = {}) {
|
|
16081
|
-
if (!(0,
|
|
18348
|
+
if (!(0, import_node_fs26.existsSync)(auditDir)) return { requestCount: 0, errorCount: 0, complete: true };
|
|
16082
18349
|
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
16083
18350
|
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
16084
18351
|
let sources;
|
|
16085
18352
|
try {
|
|
16086
|
-
sources = (0,
|
|
18353
|
+
sources = (0, import_node_fs26.readdirSync)(auditDir).filter((name) => fileOverlaps(name, from, to)).sort().map(
|
|
16087
18354
|
(name) => AUDIT_DAY_DIR_RE.test(name) ? {
|
|
16088
18355
|
auditPath: (0, import_node_path26.join)(auditDir, name, AUDIT_META_FILE),
|
|
16089
18356
|
statsPath: (0, import_node_path26.join)(auditDir, name, auditStatsFileName(AUDIT_META_FILE))
|
|
@@ -16091,14 +18358,14 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
16091
18358
|
auditPath: (0, import_node_path26.join)(auditDir, name),
|
|
16092
18359
|
statsPath: (0, import_node_path26.join)(auditDir, auditStatsFileName(name))
|
|
16093
18360
|
}
|
|
16094
|
-
).filter((source) => (0,
|
|
18361
|
+
).filter((source) => (0, import_node_fs26.existsSync)(source.auditPath));
|
|
16095
18362
|
} catch {
|
|
16096
18363
|
return { requestCount: 0, errorCount: 0, complete: false };
|
|
16097
18364
|
}
|
|
16098
18365
|
const total = { requestCount: 0, errorCount: 0, complete: true };
|
|
16099
18366
|
for (const { auditPath, statsPath } of sources) {
|
|
16100
18367
|
try {
|
|
16101
|
-
const auditBytes = (0,
|
|
18368
|
+
const auditBytes = (0, import_node_fs26.statSync)(auditPath).size;
|
|
16102
18369
|
const persisted = readPersisted(statsPath);
|
|
16103
18370
|
if (persisted && persisted.complete && persisted.auditBytes === auditBytes && queryCovers(persisted, from, to)) {
|
|
16104
18371
|
total.requestCount += persisted.requestCount;
|
|
@@ -16117,7 +18384,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
16117
18384
|
total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
|
|
16118
18385
|
total.complete = total.complete && scanned.filtered.complete;
|
|
16119
18386
|
const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
|
|
16120
|
-
if (current.complete) (0,
|
|
18387
|
+
if (current.complete) (0, import_node_fs26.writeFileSync)(statsPath, JSON.stringify(current), "utf8");
|
|
16121
18388
|
} catch {
|
|
16122
18389
|
total.complete = false;
|
|
16123
18390
|
}
|
|
@@ -16126,7 +18393,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
16126
18393
|
}
|
|
16127
18394
|
|
|
16128
18395
|
// src/audit/AuditPruneSweeper.ts
|
|
16129
|
-
var
|
|
18396
|
+
var DAY_MS4 = 24 * 60 * 6e4;
|
|
16130
18397
|
var SWEEP_INTERVAL_MS2 = 60 * 6e4;
|
|
16131
18398
|
var ARCHIVE_BATCH = 64;
|
|
16132
18399
|
var AuditPruneSweeper = class {
|
|
@@ -16189,19 +18456,19 @@ var AuditPruneSweeper = class {
|
|
|
16189
18456
|
if (!this.config.enabled || this.sweeping) return 0;
|
|
16190
18457
|
this.sweeping = true;
|
|
16191
18458
|
try {
|
|
16192
|
-
if (!(0,
|
|
16193
|
-
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) *
|
|
18459
|
+
if (!(0, import_node_fs27.existsSync)(this.auditDir)) return 0;
|
|
18460
|
+
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS4;
|
|
16194
18461
|
let removed = 0;
|
|
16195
|
-
for (const name of (0,
|
|
18462
|
+
for (const name of (0, import_node_fs27.readdirSync)(this.auditDir)) {
|
|
16196
18463
|
const dateMs = auditFileDateMs(name);
|
|
16197
18464
|
if (dateMs === null || dateMs >= cutoff) continue;
|
|
16198
18465
|
try {
|
|
16199
18466
|
if (isAuditDayDir(name)) {
|
|
16200
|
-
(0,
|
|
18467
|
+
(0, import_node_fs27.rmSync)((0, import_node_path27.join)(this.auditDir, name), { recursive: true, force: true });
|
|
16201
18468
|
} else {
|
|
16202
|
-
(0,
|
|
18469
|
+
(0, import_node_fs27.unlinkSync)((0, import_node_path27.join)(this.auditDir, name));
|
|
16203
18470
|
const statsPath = (0, import_node_path27.join)(this.auditDir, auditStatsFileName(name));
|
|
16204
|
-
if ((0,
|
|
18471
|
+
if ((0, import_node_fs27.existsSync)(statsPath)) (0, import_node_fs27.unlinkSync)(statsPath);
|
|
16205
18472
|
}
|
|
16206
18473
|
removed += 1;
|
|
16207
18474
|
} catch (error) {
|
|
@@ -16231,10 +18498,10 @@ var AuditPruneSweeper = class {
|
|
|
16231
18498
|
if (!this.config.enabled || this.archiving) return 0;
|
|
16232
18499
|
this.archiving = true;
|
|
16233
18500
|
try {
|
|
16234
|
-
if (!(0,
|
|
18501
|
+
if (!(0, import_node_fs27.existsSync)(this.auditDir)) return 0;
|
|
16235
18502
|
const today = this.todayMidnight();
|
|
16236
18503
|
let compressed = 0;
|
|
16237
|
-
for (const name of (0,
|
|
18504
|
+
for (const name of (0, import_node_fs27.readdirSync)(this.auditDir)) {
|
|
16238
18505
|
if (compressed >= ARCHIVE_BATCH) break;
|
|
16239
18506
|
const dateMs = auditFileDateMs(name);
|
|
16240
18507
|
if (dateMs === null || dateMs >= today || !isAuditDayDir(name)) continue;
|
|
@@ -16275,7 +18542,7 @@ var AuditPruneSweeper = class {
|
|
|
16275
18542
|
async archiveDay(bodiesPath, budget) {
|
|
16276
18543
|
let shards;
|
|
16277
18544
|
try {
|
|
16278
|
-
shards = (0,
|
|
18545
|
+
shards = (0, import_node_fs27.readdirSync)(bodiesPath).filter((file) => file.endsWith(".jsonl"));
|
|
16279
18546
|
} catch {
|
|
16280
18547
|
return 0;
|
|
16281
18548
|
}
|
|
@@ -16285,16 +18552,16 @@ var AuditPruneSweeper = class {
|
|
|
16285
18552
|
const source = (0, import_node_path27.join)(bodiesPath, shard);
|
|
16286
18553
|
const target = `${source}.gz`;
|
|
16287
18554
|
try {
|
|
16288
|
-
if ((0,
|
|
16289
|
-
(0,
|
|
18555
|
+
if ((0, import_node_fs27.existsSync)(target)) {
|
|
18556
|
+
(0, import_node_fs27.unlinkSync)(source);
|
|
16290
18557
|
continue;
|
|
16291
18558
|
}
|
|
16292
|
-
await (0, import_promises6.pipeline)((0,
|
|
16293
|
-
(0,
|
|
18559
|
+
await (0, import_promises6.pipeline)((0, import_node_fs27.createReadStream)(source), (0, import_node_zlib.createGzip)(), (0, import_node_fs27.createWriteStream)(target));
|
|
18560
|
+
(0, import_node_fs27.unlinkSync)(source);
|
|
16294
18561
|
compressed += 1;
|
|
16295
18562
|
} catch (error) {
|
|
16296
18563
|
try {
|
|
16297
|
-
if ((0,
|
|
18564
|
+
if ((0, import_node_fs27.existsSync)(target)) (0, import_node_fs27.unlinkSync)(target);
|
|
16298
18565
|
} catch {
|
|
16299
18566
|
}
|
|
16300
18567
|
this.logger.warn("[AuditPruneSweeper] failed to archive audit body shard", {
|
|
@@ -16308,7 +18575,7 @@ var AuditPruneSweeper = class {
|
|
|
16308
18575
|
};
|
|
16309
18576
|
|
|
16310
18577
|
// src/usage/usageMigrate.ts
|
|
16311
|
-
var
|
|
18578
|
+
var import_node_fs28 = require("fs");
|
|
16312
18579
|
var import_promises7 = require("fs/promises");
|
|
16313
18580
|
var import_node_path28 = require("path");
|
|
16314
18581
|
var import_node_readline = require("readline");
|
|
@@ -16355,7 +18622,7 @@ async function migrateLegacyUsageEvents(opts) {
|
|
|
16355
18622
|
let skipped = 0;
|
|
16356
18623
|
try {
|
|
16357
18624
|
const reader = (0, import_node_readline.createInterface)({
|
|
16358
|
-
input: (0,
|
|
18625
|
+
input: (0, import_node_fs28.createReadStream)(eventsPath, { encoding: "utf8" }),
|
|
16359
18626
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
16360
18627
|
});
|
|
16361
18628
|
for await (const line of reader) {
|
|
@@ -16447,7 +18714,7 @@ async function closeAll(writers) {
|
|
|
16447
18714
|
// src/usage/UsagePruneSweeper.ts
|
|
16448
18715
|
var import_promises8 = require("fs/promises");
|
|
16449
18716
|
var import_node_path29 = require("path");
|
|
16450
|
-
var
|
|
18717
|
+
var DAY_MS5 = 24 * 60 * 6e4;
|
|
16451
18718
|
var SWEEP_INTERVAL_MS3 = 60 * 6e4;
|
|
16452
18719
|
var DEFAULT_USAGE_RETENTION_DAYS = 90;
|
|
16453
18720
|
var UsagePruneSweeper = class {
|
|
@@ -16504,7 +18771,7 @@ var UsagePruneSweeper = class {
|
|
|
16504
18771
|
this.sweeping = true;
|
|
16505
18772
|
try {
|
|
16506
18773
|
const retentionDays = this.config.retentionDays ?? DEFAULT_USAGE_RETENTION_DAYS;
|
|
16507
|
-
const cutoff = this.todayMidnight() - (retentionDays - 1) *
|
|
18774
|
+
const cutoff = this.todayMidnight() - (retentionDays - 1) * DAY_MS5;
|
|
16508
18775
|
let removed = 0;
|
|
16509
18776
|
for (const entry of await listUsageDays(this.usageDir)) {
|
|
16510
18777
|
if (!entry.hasShard) continue;
|
|
@@ -16562,12 +18829,12 @@ var UsagePruneSweeper = class {
|
|
|
16562
18829
|
};
|
|
16563
18830
|
|
|
16564
18831
|
// src/audit/auditBodyReader.ts
|
|
16565
|
-
var
|
|
18832
|
+
var import_node_fs30 = require("fs");
|
|
16566
18833
|
var import_node_path30 = require("path");
|
|
16567
18834
|
var import_node_zlib2 = require("zlib");
|
|
16568
18835
|
|
|
16569
18836
|
// src/audit/auditJsonl.ts
|
|
16570
|
-
var
|
|
18837
|
+
var import_node_fs29 = require("fs");
|
|
16571
18838
|
var WINDOW_BYTES = 1 << 20;
|
|
16572
18839
|
var MAX_LINE_BYTES = 32 * 1024 * 1024;
|
|
16573
18840
|
var NEWLINE2 = 10;
|
|
@@ -16575,9 +18842,9 @@ function forEachLineFromTail(path2, onLine) {
|
|
|
16575
18842
|
let fd;
|
|
16576
18843
|
let end;
|
|
16577
18844
|
try {
|
|
16578
|
-
end = (0,
|
|
18845
|
+
end = (0, import_node_fs29.statSync)(path2).size;
|
|
16579
18846
|
if (end === 0) return;
|
|
16580
|
-
fd = (0,
|
|
18847
|
+
fd = (0, import_node_fs29.openSync)(path2, "r");
|
|
16581
18848
|
} catch {
|
|
16582
18849
|
return;
|
|
16583
18850
|
}
|
|
@@ -16588,7 +18855,7 @@ function forEachLineFromTail(path2, onLine) {
|
|
|
16588
18855
|
const window = Buffer.allocUnsafe(end - start);
|
|
16589
18856
|
let read;
|
|
16590
18857
|
try {
|
|
16591
|
-
read = (0,
|
|
18858
|
+
read = (0, import_node_fs29.readSync)(fd, window, 0, end - start, start);
|
|
16592
18859
|
} catch {
|
|
16593
18860
|
return;
|
|
16594
18861
|
}
|
|
@@ -16616,7 +18883,7 @@ function forEachLineFromTail(path2, onLine) {
|
|
|
16616
18883
|
}
|
|
16617
18884
|
} finally {
|
|
16618
18885
|
try {
|
|
16619
|
-
(0,
|
|
18886
|
+
(0, import_node_fs29.closeSync)(fd);
|
|
16620
18887
|
} catch {
|
|
16621
18888
|
}
|
|
16622
18889
|
}
|
|
@@ -16626,10 +18893,10 @@ function forEachLineFromTail(path2, onLine) {
|
|
|
16626
18893
|
function candidateDays(auditDir, ts) {
|
|
16627
18894
|
if (typeof ts === "number" && Number.isFinite(ts)) {
|
|
16628
18895
|
const named = auditDayDirName(ts);
|
|
16629
|
-
if ((0,
|
|
18896
|
+
if ((0, import_node_fs30.existsSync)((0, import_node_path30.join)(auditDir, named))) return [named];
|
|
16630
18897
|
}
|
|
16631
18898
|
try {
|
|
16632
|
-
return (0,
|
|
18899
|
+
return (0, import_node_fs30.readdirSync)(auditDir).filter(isAuditDayDir).sort().reverse();
|
|
16633
18900
|
} catch {
|
|
16634
18901
|
return [];
|
|
16635
18902
|
}
|
|
@@ -16637,9 +18904,9 @@ function candidateDays(auditDir, ts) {
|
|
|
16637
18904
|
function readShard(auditDir, day, sessionKey) {
|
|
16638
18905
|
const base = (0, import_node_path30.join)(auditDir, day, AUDIT_BODIES_DIR, auditBodyFileName(sessionKey));
|
|
16639
18906
|
try {
|
|
16640
|
-
if ((0,
|
|
18907
|
+
if ((0, import_node_fs30.existsSync)(base)) return (0, import_node_fs30.readFileSync)(base, "utf8");
|
|
16641
18908
|
const gz = `${base}.gz`;
|
|
16642
|
-
if ((0,
|
|
18909
|
+
if ((0, import_node_fs30.existsSync)(gz)) return (0, import_node_zlib2.gunzipSync)((0, import_node_fs30.readFileSync)(gz)).toString("utf8");
|
|
16643
18910
|
} catch {
|
|
16644
18911
|
return null;
|
|
16645
18912
|
}
|
|
@@ -16672,8 +18939,8 @@ function withDictionary(auditDir, day, entries) {
|
|
|
16672
18939
|
const base = (0, import_node_path30.join)(auditDir, day, AUDIT_BODIES_DIR, AUDIT_DICT_FILE);
|
|
16673
18940
|
let raw = null;
|
|
16674
18941
|
try {
|
|
16675
|
-
if ((0,
|
|
16676
|
-
else if ((0,
|
|
18942
|
+
if ((0, import_node_fs30.existsSync)(base)) raw = (0, import_node_fs30.readFileSync)(base, "utf8");
|
|
18943
|
+
else if ((0, import_node_fs30.existsSync)(`${base}.gz`)) raw = (0, import_node_zlib2.gunzipSync)((0, import_node_fs30.readFileSync)(`${base}.gz`)).toString("utf8");
|
|
16677
18944
|
} catch {
|
|
16678
18945
|
return entries;
|
|
16679
18946
|
}
|
|
@@ -16706,7 +18973,7 @@ function reconstructRequest(entries, entry) {
|
|
|
16706
18973
|
}
|
|
16707
18974
|
function readAuditBody(auditDir, query2) {
|
|
16708
18975
|
if (!isSafeSessionKey(query2.sessionKey) || !query2.id) return {};
|
|
16709
|
-
if (!(0,
|
|
18976
|
+
if (!(0, import_node_fs30.existsSync)(auditDir)) return {};
|
|
16710
18977
|
for (const day of candidateDays(auditDir, query2.ts)) {
|
|
16711
18978
|
const raw = readShard(auditDir, day, query2.sessionKey);
|
|
16712
18979
|
if (raw === null) continue;
|
|
@@ -16724,7 +18991,7 @@ function readAuditBody(auditDir, query2) {
|
|
|
16724
18991
|
function readLegacyInlineBody(auditDir, id) {
|
|
16725
18992
|
let names;
|
|
16726
18993
|
try {
|
|
16727
|
-
names = (0,
|
|
18994
|
+
names = (0, import_node_fs30.readdirSync)(auditDir).filter((name) => AUDIT_FILE_RE.test(name)).sort().reverse();
|
|
16728
18995
|
} catch {
|
|
16729
18996
|
return {};
|
|
16730
18997
|
}
|
|
@@ -16753,7 +19020,7 @@ function readLegacyInlineBody(auditDir, id) {
|
|
|
16753
19020
|
}
|
|
16754
19021
|
|
|
16755
19022
|
// src/audit/auditReader.ts
|
|
16756
|
-
var
|
|
19023
|
+
var import_node_fs31 = require("fs");
|
|
16757
19024
|
var import_node_path31 = require("path");
|
|
16758
19025
|
var DEFAULT_LIMIT = 200;
|
|
16759
19026
|
var MAX_LIMIT = 2e3;
|
|
@@ -16761,7 +19028,7 @@ var OVERSCAN = 256;
|
|
|
16761
19028
|
function daySources(auditDir) {
|
|
16762
19029
|
let names;
|
|
16763
19030
|
try {
|
|
16764
|
-
names = (0,
|
|
19031
|
+
names = (0, import_node_fs31.readdirSync)(auditDir);
|
|
16765
19032
|
} catch {
|
|
16766
19033
|
return [];
|
|
16767
19034
|
}
|
|
@@ -16771,7 +19038,7 @@ function daySources(auditDir) {
|
|
|
16771
19038
|
if (dateMs === null) continue;
|
|
16772
19039
|
if (AUDIT_DAY_DIR_RE.test(name)) {
|
|
16773
19040
|
const path2 = (0, import_node_path31.join)(auditDir, name, AUDIT_META_FILE);
|
|
16774
|
-
if ((0,
|
|
19041
|
+
if ((0, import_node_fs31.existsSync)(path2)) sources.push({ path: path2, dateMs });
|
|
16775
19042
|
} else if (AUDIT_FILE_RE.test(name)) {
|
|
16776
19043
|
sources.push({ path: (0, import_node_path31.join)(auditDir, name), dateMs });
|
|
16777
19044
|
}
|
|
@@ -16789,7 +19056,7 @@ function toMetaRecord(record) {
|
|
|
16789
19056
|
return { ...meta, hasBody: true };
|
|
16790
19057
|
}
|
|
16791
19058
|
function readAuditRecords(auditDir, query2 = {}) {
|
|
16792
|
-
if (!(0,
|
|
19059
|
+
if (!(0, import_node_fs31.existsSync)(auditDir)) return [];
|
|
16793
19060
|
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
16794
19061
|
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
16795
19062
|
const limit = Math.min(MAX_LIMIT, Math.max(1, Math.trunc(query2.limit ?? DEFAULT_LIMIT)));
|
|
@@ -16817,7 +19084,7 @@ function readAuditRecords(auditDir, query2 = {}) {
|
|
|
16817
19084
|
}
|
|
16818
19085
|
|
|
16819
19086
|
// src/audit/AuditWriter.ts
|
|
16820
|
-
var
|
|
19087
|
+
var import_node_fs32 = require("fs");
|
|
16821
19088
|
var import_node_path32 = require("path");
|
|
16822
19089
|
var AuditWriter = class {
|
|
16823
19090
|
constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
|
|
@@ -16865,7 +19132,7 @@ var AuditWriter = class {
|
|
|
16865
19132
|
/** Create a directory once per process and remember it. */
|
|
16866
19133
|
ensureDir(path2) {
|
|
16867
19134
|
if (!this.ensuredDirs.has(path2)) {
|
|
16868
|
-
(0,
|
|
19135
|
+
(0, import_node_fs32.mkdirSync)(path2, { recursive: true });
|
|
16869
19136
|
this.ensuredDirs.add(path2);
|
|
16870
19137
|
}
|
|
16871
19138
|
return path2;
|
|
@@ -16875,8 +19142,8 @@ var AuditWriter = class {
|
|
|
16875
19142
|
const { requestBody: _req, responseBody: _res, ...meta } = record;
|
|
16876
19143
|
const file = (0, import_node_path32.join)(dayPath, AUDIT_META_FILE);
|
|
16877
19144
|
const line = JSON.stringify(meta) + "\n";
|
|
16878
|
-
const bytesBefore = (0,
|
|
16879
|
-
(0,
|
|
19145
|
+
const bytesBefore = (0, import_node_fs32.existsSync)(file) ? (0, import_node_fs32.statSync)(file).size : 0;
|
|
19146
|
+
(0, import_node_fs32.appendFileSync)(file, line, "utf8");
|
|
16880
19147
|
try {
|
|
16881
19148
|
updateAuditStatsAfterAppend(
|
|
16882
19149
|
file,
|
|
@@ -16908,7 +19175,7 @@ var AuditWriter = class {
|
|
|
16908
19175
|
const line = encodeBodyEntry(record, sessionKey, dayDir, this.bases);
|
|
16909
19176
|
if (line === null) return;
|
|
16910
19177
|
const bodiesPath = this.ensureDir((0, import_node_path32.join)(dayPath, AUDIT_BODIES_DIR));
|
|
16911
|
-
(0,
|
|
19178
|
+
(0, import_node_fs32.appendFileSync)((0, import_node_path32.join)(bodiesPath, auditBodyFileName(sessionKey)), line + "\n", "utf8");
|
|
16912
19179
|
} catch (error) {
|
|
16913
19180
|
this.bases.forget(sessionKey);
|
|
16914
19181
|
this.logger.warn("[AuditWriter] failed to append audit body shard", {
|
|
@@ -16920,10 +19187,10 @@ var AuditWriter = class {
|
|
|
16920
19187
|
};
|
|
16921
19188
|
|
|
16922
19189
|
// src/billing/BillingPublisher.ts
|
|
16923
|
-
var
|
|
19190
|
+
var import_node_fs33 = require("fs");
|
|
16924
19191
|
var import_node_crypto24 = require("crypto");
|
|
16925
19192
|
var import_node_path33 = require("path");
|
|
16926
|
-
var
|
|
19193
|
+
var import_upstreamFetch13 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
16927
19194
|
|
|
16928
19195
|
// src/billing/billingFiles.ts
|
|
16929
19196
|
var BILLING_FILE_RE = /^billing-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
|
|
@@ -16946,7 +19213,7 @@ var BillingPublisher = class {
|
|
|
16946
19213
|
constructor(billingDir, logger, opts = {}) {
|
|
16947
19214
|
this.billingDir = billingDir;
|
|
16948
19215
|
this.logger = logger;
|
|
16949
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
19216
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch13.fetchUpstream)(url, init));
|
|
16950
19217
|
this.defer = opts.defer ?? ((fn) => setTimeout(fn, 0));
|
|
16951
19218
|
this.timeoutMs = opts.timeoutMs ?? BILLING_POST_TIMEOUT_MS;
|
|
16952
19219
|
this.now = opts.now ?? Date.now;
|
|
@@ -16994,7 +19261,7 @@ var BillingPublisher = class {
|
|
|
16994
19261
|
appendNow(event) {
|
|
16995
19262
|
this.ensureDir();
|
|
16996
19263
|
const file = (0, import_node_path33.join)(this.billingDir, billingFileName(event.ts));
|
|
16997
|
-
(0,
|
|
19264
|
+
(0, import_node_fs33.appendFileSync)(file, JSON.stringify(event) + "\n", "utf8");
|
|
16998
19265
|
}
|
|
16999
19266
|
/**
|
|
17000
19267
|
* One best-effort delivery attempt for an event ALREADY in the ledger. POSTs the
|
|
@@ -17044,7 +19311,7 @@ var BillingPublisher = class {
|
|
|
17044
19311
|
try {
|
|
17045
19312
|
this.ensureDir();
|
|
17046
19313
|
const file = (0, import_node_path33.join)(this.billingDir, deliveredFileName(event.ts));
|
|
17047
|
-
(0,
|
|
19314
|
+
(0, import_node_fs33.appendFileSync)(file, JSON.stringify({ id: event.id, deliveredAt: this.now() }) + "\n", "utf8");
|
|
17048
19315
|
} catch (error) {
|
|
17049
19316
|
this.logger.warn("[BillingPublisher] failed to append delivery marker", {
|
|
17050
19317
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -17053,20 +19320,20 @@ var BillingPublisher = class {
|
|
|
17053
19320
|
}
|
|
17054
19321
|
ensureDir() {
|
|
17055
19322
|
if (this.dirEnsured) return;
|
|
17056
|
-
(0,
|
|
19323
|
+
(0, import_node_fs33.mkdirSync)(this.billingDir, { recursive: true });
|
|
17057
19324
|
this.dirEnsured = true;
|
|
17058
19325
|
}
|
|
17059
19326
|
};
|
|
17060
19327
|
|
|
17061
19328
|
// src/billing/billingReader.ts
|
|
17062
|
-
var
|
|
19329
|
+
var import_node_fs34 = require("fs");
|
|
17063
19330
|
var import_node_path34 = require("path");
|
|
17064
19331
|
function readBillingLedger(billingDir) {
|
|
17065
19332
|
const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
|
|
17066
|
-
if (!(0,
|
|
19333
|
+
if (!(0, import_node_fs34.existsSync)(billingDir)) return view;
|
|
17067
19334
|
let files;
|
|
17068
19335
|
try {
|
|
17069
|
-
files = (0,
|
|
19336
|
+
files = (0, import_node_fs34.readdirSync)(billingDir);
|
|
17070
19337
|
} catch {
|
|
17071
19338
|
return view;
|
|
17072
19339
|
}
|
|
@@ -17097,7 +19364,7 @@ function readBillingStatus(billingDir) {
|
|
|
17097
19364
|
function parseLines(dir, file) {
|
|
17098
19365
|
let raw;
|
|
17099
19366
|
try {
|
|
17100
|
-
raw = (0,
|
|
19367
|
+
raw = (0, import_node_fs34.readFileSync)((0, import_node_path34.join)(dir, file), "utf8");
|
|
17101
19368
|
} catch {
|
|
17102
19369
|
return [];
|
|
17103
19370
|
}
|
|
@@ -17196,7 +19463,7 @@ var BillingRetrySweeper = class {
|
|
|
17196
19463
|
// src/TokenRefreshScheduler.ts
|
|
17197
19464
|
var REFRESH_LEAD_MS2 = 5 * 6e4;
|
|
17198
19465
|
var SWEEP_INTERVAL_MS5 = 6e4;
|
|
17199
|
-
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini"];
|
|
19466
|
+
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi", "grok", "copilot"];
|
|
17200
19467
|
var TokenRefreshScheduler = class {
|
|
17201
19468
|
constructor(store, logger, intervalMs = SWEEP_INTERVAL_MS5, leadMs = REFRESH_LEAD_MS2) {
|
|
17202
19469
|
this.store = store;
|
|
@@ -17279,6 +19546,14 @@ var TokenRefreshScheduler = class {
|
|
|
17279
19546
|
return this.store.refreshCodexToken();
|
|
17280
19547
|
case "gemini":
|
|
17281
19548
|
return this.store.refreshGeminiToken();
|
|
19549
|
+
case "kimi":
|
|
19550
|
+
return this.store.refreshKimiToken();
|
|
19551
|
+
case "grok":
|
|
19552
|
+
return this.store.refreshGrokToken();
|
|
19553
|
+
// ghu_ tokens never near-expire (far-future expiresAt), so the sweep
|
|
19554
|
+
// never reaches this — the branch exists for union totality.
|
|
19555
|
+
case "copilot":
|
|
19556
|
+
return this.store.refreshCopilotToken();
|
|
17282
19557
|
}
|
|
17283
19558
|
}
|
|
17284
19559
|
};
|
|
@@ -17355,7 +19630,7 @@ function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
|
17355
19630
|
|
|
17356
19631
|
// src/webhook/WebhookDispatcher.ts
|
|
17357
19632
|
var import_node_crypto25 = require("crypto");
|
|
17358
|
-
var
|
|
19633
|
+
var import_upstreamFetch14 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
17359
19634
|
var WEBHOOK_MAX_ATTEMPTS = 3;
|
|
17360
19635
|
var WEBHOOK_QUEUE_MAX = 1e3;
|
|
17361
19636
|
var WEBHOOK_SEND_TIMEOUT_MS = 1e4;
|
|
@@ -17375,7 +19650,7 @@ var WebhookDispatcher = class {
|
|
|
17375
19650
|
sleep;
|
|
17376
19651
|
now;
|
|
17377
19652
|
constructor(opts = {}) {
|
|
17378
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
19653
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch14.fetchUpstream)(url, init));
|
|
17379
19654
|
this.logger = opts.logger;
|
|
17380
19655
|
this.maxAttempts = opts.maxAttempts ?? WEBHOOK_MAX_ATTEMPTS;
|
|
17381
19656
|
this.queueMax = opts.queueMax ?? WEBHOOK_QUEUE_MAX;
|
|
@@ -17461,8 +19736,8 @@ var WebhookDispatcher = class {
|
|
|
17461
19736
|
signal: AbortSignal.timeout(this.timeoutMs)
|
|
17462
19737
|
});
|
|
17463
19738
|
return res.ok ? { ok: true, status: res.status } : { ok: false, status: res.status };
|
|
17464
|
-
} catch (
|
|
17465
|
-
return { ok: false, error:
|
|
19739
|
+
} catch (err8) {
|
|
19740
|
+
return { ok: false, error: err8 instanceof Error ? err8.message : String(err8) };
|
|
17466
19741
|
}
|
|
17467
19742
|
}
|
|
17468
19743
|
/**
|
|
@@ -17525,7 +19800,7 @@ function feishuText(event) {
|
|
|
17525
19800
|
// src/bootstrap.ts
|
|
17526
19801
|
var activeImageRuntimeBootstrapSession;
|
|
17527
19802
|
function createImageRuntimeBootstrapSession(initialGeneration) {
|
|
17528
|
-
const openAIOperationRegistry = new
|
|
19803
|
+
const openAIOperationRegistry = new import_core7.OpenAIOperationRegistry();
|
|
17529
19804
|
const imageRuntimeManager = new ImageRuntimeManager(initialGeneration);
|
|
17530
19805
|
const unregisterContributions = [];
|
|
17531
19806
|
try {
|
|
@@ -17586,7 +19861,7 @@ function resetImageRuntimeBootstrapSession() {
|
|
|
17586
19861
|
function resolveLoggingConfig(configured, configPath) {
|
|
17587
19862
|
const file = configured?.file ?? defaultDaemonLogPath(configPath);
|
|
17588
19863
|
try {
|
|
17589
|
-
(0,
|
|
19864
|
+
(0, import_node_fs35.mkdirSync)(configured?.file ? (0, import_node_path35.dirname)(configured.file) : defaultLogDir(configPath), {
|
|
17590
19865
|
recursive: true
|
|
17591
19866
|
});
|
|
17592
19867
|
} catch {
|
|
@@ -17604,12 +19879,12 @@ function buildDaemon(config, paths) {
|
|
|
17604
19879
|
setSecretBox(secretBox3);
|
|
17605
19880
|
setSecretBox2(secretBox3);
|
|
17606
19881
|
const decryptedConfig = decryptConfigSecrets(config, secretBox3);
|
|
17607
|
-
const accountAllowanceStore = new
|
|
19882
|
+
const accountAllowanceStore = new import_AccountAllowanceStore9.AccountAllowanceStore(
|
|
17608
19883
|
Date.now,
|
|
17609
19884
|
void 0,
|
|
17610
19885
|
new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
|
|
17611
19886
|
);
|
|
17612
|
-
(0,
|
|
19887
|
+
(0, import_AccountAllowanceStore9.setSharedAccountAllowanceStore)(accountAllowanceStore);
|
|
17613
19888
|
(0, import_AccountAllowanceScheduling5.getSharedAccountAllowanceScheduling)().configure(
|
|
17614
19889
|
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
17615
19890
|
);
|
|
@@ -17634,21 +19909,22 @@ function buildDaemon(config, paths) {
|
|
|
17634
19909
|
claudeAllowanceRefreshScheduler.configure(
|
|
17635
19910
|
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
17636
19911
|
);
|
|
17637
|
-
const subscriptionAccounts = new
|
|
17638
|
-
(0,
|
|
17639
|
-
const subscriptionRegistry = new
|
|
19912
|
+
const subscriptionAccounts = new import_subscriptions12.SubscriptionAccountService(credentialStore);
|
|
19913
|
+
(0, import_subscriptions12.setSubscriptionAccountService)(subscriptionAccounts);
|
|
19914
|
+
const subscriptionRegistry = new import_subscriptions12.SubscriptionProviderRegistry(
|
|
17640
19915
|
subscriptionAccounts,
|
|
17641
19916
|
credentialStore
|
|
17642
19917
|
);
|
|
17643
|
-
(0,
|
|
19918
|
+
(0, import_subscriptions12.setSubscriptionProviderRegistry)(subscriptionRegistry);
|
|
17644
19919
|
setServerProxyConfig(decryptedConfig.server?.proxy);
|
|
17645
|
-
(0,
|
|
19920
|
+
(0, import_upstreamFetch15.setUpstreamProxyResolver)(
|
|
17646
19921
|
createUpstreamProxyResolver({
|
|
17647
19922
|
getAccountProxy: (providerId, accountId) => credentialStore.getAccountProxy(providerId, accountId)
|
|
17648
19923
|
})
|
|
17649
19924
|
);
|
|
17650
19925
|
(0, import_gemini_code_assist_resolver.setGeminiCodeAssistResolver)((0, import_GeminiCodeAssistProjectResolver.getGeminiCodeAssistProjectResolver)());
|
|
17651
19926
|
const autoDisableStore = new AutoDisableStore();
|
|
19927
|
+
const providerKeyQuotaService = new ProviderKeyQuotaService(secretBox3);
|
|
17652
19928
|
const apiKeyPool = new import_ApiKeyPoolService.ApiKeyPoolService(
|
|
17653
19929
|
createPoolKeysLoader((id) => llmConfig.getProviderRow(id), autoDisableStore),
|
|
17654
19930
|
resolveEnvKey,
|
|
@@ -17665,7 +19941,7 @@ function buildDaemon(config, paths) {
|
|
|
17665
19941
|
const pricingEngine = new import_usage2.PricingEngine(pricingStore, logger, {
|
|
17666
19942
|
// Catalog egress follows the same global/env proxy policy as every other
|
|
17667
19943
|
// daemon upstream call; no provider/account override applies here.
|
|
17668
|
-
fetchImpl: ((input, init) => (0,
|
|
19944
|
+
fetchImpl: ((input, init) => (0, import_upstreamFetch15.fetchUpstream)(String(input), init ?? {}))
|
|
17669
19945
|
});
|
|
17670
19946
|
const pricingRefreshScheduler = new PricingRefreshScheduler(
|
|
17671
19947
|
pricingEngine,
|
|
@@ -17929,6 +20205,11 @@ function buildDaemon(config, paths) {
|
|
|
17929
20205
|
// values themselves NEVER leave (masked via `maskProviderApiKey`).
|
|
17930
20206
|
apiKeyPool,
|
|
17931
20207
|
autoDisableStore,
|
|
20208
|
+
// BYO provider-key quota (Z.AI coding plan, MiniMax Token Plan, …): a
|
|
20209
|
+
// read-through cached same-key usage probe surfaced on the keys view. The
|
|
20210
|
+
// key plaintext is resolved + decrypted inside the service and never
|
|
20211
|
+
// crosses back out.
|
|
20212
|
+
providerKeyQuota: providerKeyQuotaService,
|
|
17932
20213
|
// Interactive OAuth login over admin HTTP (app-parity child 4, design
|
|
17933
20214
|
// D1/D2-a). The in-memory pending-session store (NEVER serialized), the
|
|
17934
20215
|
// injected token-exchange fetch (global `fetch` here; mocked in tests), and a
|
|
@@ -17945,7 +20226,7 @@ function buildDaemon(config, paths) {
|
|
|
17945
20226
|
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
17946
20227
|
// excluded from the upstream trace, so a failing login left no evidence.
|
|
17947
20228
|
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
17948
|
-
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0,
|
|
20229
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0, import_upstreamFetch15.fetchUpstream)(url, init, { providerId, redactBodies: true }),
|
|
17949
20230
|
subscriptionAccountAppender: credentialStore,
|
|
17950
20231
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
17951
20232
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -17953,6 +20234,13 @@ function buildDaemon(config, paths) {
|
|
|
17953
20234
|
// can inject a mock so no real port is bound.
|
|
17954
20235
|
codexSessions: new CodexOAuthSessionStore(),
|
|
17955
20236
|
codexAwaitLoopback: paths.codexAwaitLoopback ?? ((state, timeoutMs, signal) => awaitLoopbackCode(state, timeoutMs, signal)),
|
|
20237
|
+
// Kimi interactive OAuth — the async DEVICE-CODE flow store (no port, no
|
|
20238
|
+
// paste; the app shows the verification URL + user code and polls the
|
|
20239
|
+
// token-free status). Token captured + persisted daemon-side.
|
|
20240
|
+
kimiSessions: new CodexOAuthSessionStore(),
|
|
20241
|
+
// Grok interactive OAuth — the same async DEVICE-CODE shape as kimi.
|
|
20242
|
+
grokSessions: new CodexOAuthSessionStore(),
|
|
20243
|
+
copilotSessions: new CodexOAuthSessionStore(),
|
|
17956
20244
|
// Migration pack (app-parity child 6, design D2/D3) — the concrete credential
|
|
17957
20245
|
// store provides BOTH the full DECRYPTED read (`getFullConfig`, export) and
|
|
17958
20246
|
// the multi-account append (`appendProviderAccount`, import re-encrypts at-
|
|
@@ -18011,7 +20299,7 @@ function buildDaemon(config, paths) {
|
|
|
18011
20299
|
});
|
|
18012
20300
|
const webhookDispatcher = new WebhookDispatcher({
|
|
18013
20301
|
logger,
|
|
18014
|
-
fetchImpl: (url, init) => (0,
|
|
20302
|
+
fetchImpl: (url, init) => (0, import_upstreamFetch15.fetchUpstream)(url, init)
|
|
18015
20303
|
});
|
|
18016
20304
|
setWebhookRuntime(webhookDispatcher, (0, import_SubscriptionAccountHealth4.getSharedAccountHealth)());
|
|
18017
20305
|
const auditWriter = new AuditWriter(auditDir, logger);
|
|
@@ -18091,9 +20379,9 @@ function resetDaemonSingletonsForTests() {
|
|
|
18091
20379
|
(0, import_provider_proxy4.__resetProviderProxyForTests)();
|
|
18092
20380
|
(0, import_outbound_api10.__resetOutboundApiServerForTests)();
|
|
18093
20381
|
(0, import_subscriptionRegistryPort.setSubscriptionRegistryForOutbound)(null);
|
|
18094
|
-
(0,
|
|
18095
|
-
(0,
|
|
18096
|
-
(0,
|
|
20382
|
+
(0, import_subscriptions12.setSubscriptionProviderRegistry)(null);
|
|
20383
|
+
(0, import_subscriptions12.setSubscriptionAccountService)(null);
|
|
20384
|
+
(0, import_upstreamFetch15.setUpstreamProxyResolver)(null);
|
|
18097
20385
|
setServerProxyConfig(void 0);
|
|
18098
20386
|
(0, import_gemini_code_assist_resolver.setGeminiCodeAssistResolver)(null);
|
|
18099
20387
|
setSecretBox(null);
|
|
@@ -18102,14 +20390,14 @@ function resetDaemonSingletonsForTests() {
|
|
|
18102
20390
|
resetAuditRuntimeForTests();
|
|
18103
20391
|
resetBillingRuntimeForTests();
|
|
18104
20392
|
(0, import_SubscriptionIdentityStore3.__resetSharedIdentityStoreForTests)();
|
|
18105
|
-
(0,
|
|
20393
|
+
(0, import_AccountAllowanceStore9.__resetSharedAccountAllowanceStoreForTests)();
|
|
18106
20394
|
(0, import_AccountAllowanceScheduling5.__resetSharedAccountAllowanceSchedulingForTests)();
|
|
18107
20395
|
(0, import_usage2.__resetSharedUsageThroughputTrackerForTests)();
|
|
18108
20396
|
}
|
|
18109
20397
|
function isTokensStoreReadable(tokensPath) {
|
|
18110
20398
|
try {
|
|
18111
|
-
if (!(0,
|
|
18112
|
-
(0,
|
|
20399
|
+
if (!(0, import_node_fs35.existsSync)(tokensPath)) return true;
|
|
20400
|
+
(0, import_node_fs35.accessSync)(tokensPath, import_node_fs35.constants.R_OK);
|
|
18113
20401
|
return true;
|
|
18114
20402
|
} catch {
|
|
18115
20403
|
return false;
|