@omnicross/daemon 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1707 -303
- package/dist/cli.js +1644 -229
- package/dist/index.cjs +1655 -293
- package/dist/index.d.cts +334 -28
- package/dist/index.d.ts +334 -28
- package/dist/index.js +1598 -230
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -58,7 +58,7 @@ __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");
|
|
@@ -68,16 +68,16 @@ var import_ApiKeyPoolService = require("@omnicross/core/completion/ApiKeyPoolSer
|
|
|
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_AccountAllowanceStore7 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
72
72
|
var import_AccountAllowanceScheduling5 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
73
|
-
var
|
|
73
|
+
var import_upstreamFetch13 = 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_subscriptions9 = require("@omnicross/subscriptions");
|
|
81
81
|
|
|
82
82
|
// src/admin/accountsCodexOAuth.ts
|
|
83
83
|
var import_node_crypto = __toESM(require("crypto"), 1);
|
|
@@ -184,8 +184,83 @@ 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
|
+
|
|
187
262
|
// src/allowance/AccountAllowanceService.ts
|
|
188
|
-
var
|
|
263
|
+
var import_AccountAllowanceStore5 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
189
264
|
var import_AccountAllowanceScheduling = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
190
265
|
|
|
191
266
|
// src/allowance/ClaudeAllowanceCollector.ts
|
|
@@ -212,13 +287,11 @@ function secondsUntil(instant, now) {
|
|
|
212
287
|
function windowFromPayload(id, payload, now) {
|
|
213
288
|
const usedPercent = finitePercent(payload?.utilization);
|
|
214
289
|
const resetsAt = isoInstant(payload?.resets_at);
|
|
215
|
-
const isSonnet = id === "seven-day-sonnet";
|
|
216
290
|
const isFiveHour = id === "five-hour";
|
|
217
291
|
return {
|
|
218
292
|
id,
|
|
219
|
-
label: isFiveHour ? "5 hours" :
|
|
220
|
-
scope:
|
|
221
|
-
modelFamily: isSonnet ? "sonnet" : void 0,
|
|
293
|
+
label: isFiveHour ? "5 hours" : "7 days",
|
|
294
|
+
scope: "all",
|
|
222
295
|
usedPercent,
|
|
223
296
|
windowMinutes: isFiveHour ? 5 * 60 : 7 * 24 * 60,
|
|
224
297
|
resetsAt,
|
|
@@ -226,6 +299,44 @@ function windowFromPayload(id, payload, now) {
|
|
|
226
299
|
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
227
300
|
};
|
|
228
301
|
}
|
|
302
|
+
function limitEntryWindow(entries, kind) {
|
|
303
|
+
const entry = entries.find((candidate) => candidate.kind === kind);
|
|
304
|
+
if (!entry) return void 0;
|
|
305
|
+
return { utilization: entry.percent, resets_at: entry.resets_at };
|
|
306
|
+
}
|
|
307
|
+
function slugifyDisplayName(name) {
|
|
308
|
+
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
309
|
+
}
|
|
310
|
+
function scopedWeeklyWindows(entries, now) {
|
|
311
|
+
const seen = /* @__PURE__ */ new Set();
|
|
312
|
+
const windows = [];
|
|
313
|
+
for (const entry of entries) {
|
|
314
|
+
if (entry.kind !== "weekly_scoped") continue;
|
|
315
|
+
const displayName = typeof entry.scope?.model?.display_name === "string" && entry.scope.model.display_name.trim() ? entry.scope.model.display_name.trim() : void 0;
|
|
316
|
+
if (!displayName) continue;
|
|
317
|
+
const slug = slugifyDisplayName(displayName);
|
|
318
|
+
if (!slug || seen.has(slug)) continue;
|
|
319
|
+
seen.add(slug);
|
|
320
|
+
const usedPercent = finitePercent(entry.percent);
|
|
321
|
+
const resetsAt = isoInstant(entry.resets_at);
|
|
322
|
+
windows.push({
|
|
323
|
+
id: `seven-day-${slug}`,
|
|
324
|
+
label: `7 days \xB7 ${displayName}`,
|
|
325
|
+
scope: "model-family",
|
|
326
|
+
modelFamily: slug,
|
|
327
|
+
usedPercent,
|
|
328
|
+
windowMinutes: 7 * 24 * 60,
|
|
329
|
+
resetsAt,
|
|
330
|
+
remainingSeconds: secondsUntil(resetsAt, now),
|
|
331
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
return windows;
|
|
335
|
+
}
|
|
336
|
+
function parseLimitEntries(raw) {
|
|
337
|
+
if (!Array.isArray(raw)) return [];
|
|
338
|
+
return raw.filter((entry) => !!entry && typeof entry === "object");
|
|
339
|
+
}
|
|
229
340
|
function emptyClaudeWindows(state) {
|
|
230
341
|
return [
|
|
231
342
|
{
|
|
@@ -243,15 +354,6 @@ function emptyClaudeWindows(state) {
|
|
|
243
354
|
usedPercent: null,
|
|
244
355
|
windowMinutes: 7 * 24 * 60,
|
|
245
356
|
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
357
|
}
|
|
256
358
|
];
|
|
257
359
|
}
|
|
@@ -332,6 +434,9 @@ var ClaudeAllowanceCollector = class {
|
|
|
332
434
|
}
|
|
333
435
|
const now = this.now();
|
|
334
436
|
const usage = payload;
|
|
437
|
+
const limitEntries = parseLimitEntries(usage.limits);
|
|
438
|
+
const fiveHour = usage.five_hour ?? limitEntryWindow(limitEntries, "session");
|
|
439
|
+
const sevenDay = usage.seven_day ?? limitEntryWindow(limitEntries, "weekly_all");
|
|
335
440
|
const snapshot = {
|
|
336
441
|
providerId: "claude",
|
|
337
442
|
accountId,
|
|
@@ -339,10 +444,10 @@ var ClaudeAllowanceCollector = class {
|
|
|
339
444
|
observedAt: new Date(now).toISOString(),
|
|
340
445
|
expiresAt: new Date(now + CLAUDE_ALLOWANCE_CACHE_MS).toISOString(),
|
|
341
446
|
windows: [
|
|
342
|
-
windowFromPayload("five-hour",
|
|
343
|
-
windowFromPayload("seven-day",
|
|
344
|
-
|
|
345
|
-
]
|
|
447
|
+
windowFromPayload("five-hour", fiveHour, now),
|
|
448
|
+
windowFromPayload("seven-day", sevenDay, now),
|
|
449
|
+
...scopedWeeklyWindows(limitEntries, now)
|
|
450
|
+
].slice(0, 8)
|
|
346
451
|
};
|
|
347
452
|
this.store.set(snapshot);
|
|
348
453
|
return snapshot;
|
|
@@ -399,6 +504,601 @@ var ClaudeAllowanceCollector = class {
|
|
|
399
504
|
}
|
|
400
505
|
};
|
|
401
506
|
|
|
507
|
+
// src/allowance/CodexAllowanceCollector.ts
|
|
508
|
+
var import_AccountAllowanceStore2 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
509
|
+
var import_upstreamFetch2 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
510
|
+
var CODEX_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
511
|
+
var CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
512
|
+
var CODEX_CLI_USER_AGENT = "codex_cli_rs/0.144.5";
|
|
513
|
+
function finiteNumber(value) {
|
|
514
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
515
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
516
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
|
517
|
+
}
|
|
518
|
+
function finitePercent2(value) {
|
|
519
|
+
const parsed = finiteNumber(value);
|
|
520
|
+
return parsed !== null && parsed <= 100 ? parsed : null;
|
|
521
|
+
}
|
|
522
|
+
function epochMs(value) {
|
|
523
|
+
return value > 1e11 ? value : value * 1e3;
|
|
524
|
+
}
|
|
525
|
+
function secondsUntil2(instant, now) {
|
|
526
|
+
if (!instant) return void 0;
|
|
527
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
528
|
+
}
|
|
529
|
+
function decodeJwtClaims(token) {
|
|
530
|
+
const parts = token.split(".");
|
|
531
|
+
if (parts.length !== 3) return void 0;
|
|
532
|
+
try {
|
|
533
|
+
const json2 = Buffer.from(parts[1], "base64url").toString("utf8");
|
|
534
|
+
const parsed = JSON.parse(json2);
|
|
535
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
536
|
+
} catch {
|
|
537
|
+
return void 0;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
function chatgptAccountIdFromClaims(claims) {
|
|
541
|
+
const auth = claims?.["https://api.openai.com/auth"];
|
|
542
|
+
if (!auth || typeof auth !== "object") return void 0;
|
|
543
|
+
const accountId = auth.chatgpt_account_id;
|
|
544
|
+
return typeof accountId === "string" && accountId.trim() ? accountId.trim() : void 0;
|
|
545
|
+
}
|
|
546
|
+
function resolveCodexChatGptAccountId(tokens) {
|
|
547
|
+
if (tokens.accountId?.trim()) return tokens.accountId.trim();
|
|
548
|
+
if (tokens.idToken) {
|
|
549
|
+
const fromIdToken = chatgptAccountIdFromClaims(decodeJwtClaims(tokens.idToken));
|
|
550
|
+
if (fromIdToken) return fromIdToken;
|
|
551
|
+
}
|
|
552
|
+
if (tokens.accessToken) {
|
|
553
|
+
return chatgptAccountIdFromClaims(decodeJwtClaims(tokens.accessToken));
|
|
554
|
+
}
|
|
555
|
+
return void 0;
|
|
556
|
+
}
|
|
557
|
+
function windowFromPayload2(id, payload, now) {
|
|
558
|
+
const usedPercent = finitePercent2(payload?.used_percent);
|
|
559
|
+
const resetAtSeconds = finiteNumber(payload?.reset_at);
|
|
560
|
+
const resetAfterSeconds = finiteNumber(payload?.reset_after_seconds);
|
|
561
|
+
const windowSeconds = finiteNumber(payload?.limit_window_seconds);
|
|
562
|
+
const resetsAt = resetAtSeconds !== null && resetAtSeconds > 0 ? new Date(epochMs(resetAtSeconds)).toISOString() : resetAfterSeconds !== null && resetAfterSeconds > 0 ? new Date(now + resetAfterSeconds * 1e3).toISOString() : void 0;
|
|
563
|
+
const windowMinutes = windowSeconds !== null && windowSeconds > 0 ? Math.round(windowSeconds / 60) : void 0;
|
|
564
|
+
return {
|
|
565
|
+
id,
|
|
566
|
+
label: id === "primary" ? "Primary" : "Secondary",
|
|
567
|
+
scope: "all",
|
|
568
|
+
usedPercent,
|
|
569
|
+
...windowMinutes !== void 0 ? { windowMinutes } : {},
|
|
570
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
571
|
+
remainingSeconds: secondsUntil2(resetsAt, now),
|
|
572
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
var CodexAllowanceCollector = class {
|
|
576
|
+
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) {
|
|
577
|
+
this.credentials = credentials;
|
|
578
|
+
this.store = store;
|
|
579
|
+
this.fetchImpl = fetchImpl;
|
|
580
|
+
this.now = now;
|
|
581
|
+
}
|
|
582
|
+
credentials;
|
|
583
|
+
store;
|
|
584
|
+
fetchImpl;
|
|
585
|
+
now;
|
|
586
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
587
|
+
async collectMany(accounts, options = {}) {
|
|
588
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
589
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
590
|
+
}
|
|
591
|
+
collect(account, options = {}) {
|
|
592
|
+
const now = this.now();
|
|
593
|
+
const unsupported = account.tokens.authMethod !== "oauth";
|
|
594
|
+
if (unsupported) {
|
|
595
|
+
const existing = this.store.get("codex", account.id, now);
|
|
596
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
597
|
+
return Promise.resolve(existing);
|
|
598
|
+
}
|
|
599
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
600
|
+
this.store.set(snapshot);
|
|
601
|
+
return Promise.resolve(snapshot);
|
|
602
|
+
}
|
|
603
|
+
const cached = this.store.get("codex", account.id, now);
|
|
604
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
605
|
+
return Promise.resolve(cached);
|
|
606
|
+
}
|
|
607
|
+
const running = this.inFlight.get(account.id);
|
|
608
|
+
if (running) return running;
|
|
609
|
+
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));
|
|
610
|
+
this.inFlight.set(account.id, promise);
|
|
611
|
+
return promise;
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* A response-header snapshot stays a valid cache hit only while fresh; an
|
|
615
|
+
* active oauth-usage snapshot is honored on the same 5-minute cadence as
|
|
616
|
+
* Claude's (the poll is cheap and quota is the scheduling input).
|
|
617
|
+
*/
|
|
618
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
619
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
620
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
621
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
622
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
623
|
+
}
|
|
624
|
+
async fetchAccount(accountId, tokens) {
|
|
625
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("codex", accountId);
|
|
626
|
+
if (!accessToken) {
|
|
627
|
+
return this.failureSnapshot(accountId, "codex_usage_token_unavailable", this.now());
|
|
628
|
+
}
|
|
629
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
630
|
+
if (response.status === 401) {
|
|
631
|
+
const refreshed = await this.credentials.refreshAccountToken("codex", accountId);
|
|
632
|
+
if (!refreshed) {
|
|
633
|
+
return this.failureSnapshot(accountId, "codex_usage_unauthorized", this.now());
|
|
634
|
+
}
|
|
635
|
+
accessToken = await this.credentials.getAccessTokenForAccount("codex", accountId);
|
|
636
|
+
if (!accessToken) {
|
|
637
|
+
return this.failureSnapshot(accountId, "codex_usage_token_unavailable", this.now());
|
|
638
|
+
}
|
|
639
|
+
response = await this.request(accountId, accessToken, tokens);
|
|
640
|
+
}
|
|
641
|
+
if (response.status === 403) {
|
|
642
|
+
const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "codex_usage_unsupported");
|
|
643
|
+
this.store.set(snapshot2);
|
|
644
|
+
return snapshot2;
|
|
645
|
+
}
|
|
646
|
+
if (!response.ok) {
|
|
647
|
+
return this.failureSnapshot(accountId, "codex_usage_http_error", this.now());
|
|
648
|
+
}
|
|
649
|
+
let payload;
|
|
650
|
+
try {
|
|
651
|
+
payload = await response.json();
|
|
652
|
+
} catch {
|
|
653
|
+
return this.failureSnapshot(accountId, "codex_usage_invalid_response", this.now());
|
|
654
|
+
}
|
|
655
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
656
|
+
return this.failureSnapshot(accountId, "codex_usage_invalid_response", this.now());
|
|
657
|
+
}
|
|
658
|
+
const now = this.now();
|
|
659
|
+
const usage = payload.rate_limit;
|
|
660
|
+
const previous = this.store.get("codex", accountId, now);
|
|
661
|
+
const snapshot = {
|
|
662
|
+
providerId: "codex",
|
|
663
|
+
accountId,
|
|
664
|
+
source: "oauth-usage-api",
|
|
665
|
+
observedAt: new Date(now).toISOString(),
|
|
666
|
+
expiresAt: new Date(now + CODEX_ALLOWANCE_CACHE_MS).toISOString(),
|
|
667
|
+
windows: [
|
|
668
|
+
windowFromPayload2("primary", usage?.primary_window ?? void 0, now),
|
|
669
|
+
windowFromPayload2("secondary", usage?.secondary_window ?? void 0, now)
|
|
670
|
+
],
|
|
671
|
+
// The wham payload has no ratio field; keep the passively-observed value.
|
|
672
|
+
...previous?.primaryOverSecondaryLimitPercent !== void 0 ? { primaryOverSecondaryLimitPercent: previous.primaryOverSecondaryLimitPercent } : {}
|
|
673
|
+
};
|
|
674
|
+
this.store.set(snapshot);
|
|
675
|
+
return snapshot;
|
|
676
|
+
}
|
|
677
|
+
request(accountId, accessToken, tokens) {
|
|
678
|
+
const headers = {
|
|
679
|
+
Authorization: `Bearer ${accessToken}`,
|
|
680
|
+
Accept: "application/json",
|
|
681
|
+
"User-Agent": CODEX_CLI_USER_AGENT
|
|
682
|
+
};
|
|
683
|
+
const chatgptAccountId = resolveCodexChatGptAccountId(tokens);
|
|
684
|
+
if (chatgptAccountId) headers["ChatGPT-Account-Id"] = chatgptAccountId;
|
|
685
|
+
return this.fetchImpl(CODEX_USAGE_URL, {
|
|
686
|
+
method: "GET",
|
|
687
|
+
headers,
|
|
688
|
+
signal: AbortSignal.timeout(15e3)
|
|
689
|
+
}, accountId);
|
|
690
|
+
}
|
|
691
|
+
failureSnapshot(accountId, code, now) {
|
|
692
|
+
const existing = this.store.get("codex", accountId, now);
|
|
693
|
+
const snapshot = existing ? {
|
|
694
|
+
...existing,
|
|
695
|
+
expiresAt: new Date(now + CODEX_ALLOWANCE_CACHE_MS).toISOString(),
|
|
696
|
+
windows: existing.windows.map((window) => ({
|
|
697
|
+
...window,
|
|
698
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
699
|
+
})),
|
|
700
|
+
lastErrorCode: code
|
|
701
|
+
} : {
|
|
702
|
+
providerId: "codex",
|
|
703
|
+
accountId,
|
|
704
|
+
source: "oauth-usage-api",
|
|
705
|
+
observedAt: new Date(now).toISOString(),
|
|
706
|
+
expiresAt: new Date(now + CODEX_ALLOWANCE_CACHE_MS).toISOString(),
|
|
707
|
+
windows: [
|
|
708
|
+
{ id: "primary", label: "Primary", scope: "all", usedPercent: null, state: "unavailable" },
|
|
709
|
+
{ id: "secondary", label: "Secondary", scope: "all", usedPercent: null, state: "unavailable" }
|
|
710
|
+
],
|
|
711
|
+
lastErrorCode: code
|
|
712
|
+
};
|
|
713
|
+
this.store.set(snapshot);
|
|
714
|
+
return snapshot;
|
|
715
|
+
}
|
|
716
|
+
unsupportedSnapshot(accountId, now, code = "codex_usage_unsupported_auth") {
|
|
717
|
+
return {
|
|
718
|
+
providerId: "codex",
|
|
719
|
+
accountId,
|
|
720
|
+
source: "oauth-usage-api",
|
|
721
|
+
observedAt: new Date(now).toISOString(),
|
|
722
|
+
windows: [
|
|
723
|
+
{ id: "primary", label: "Primary", scope: "all", usedPercent: null, state: "unsupported" },
|
|
724
|
+
{ id: "secondary", label: "Secondary", scope: "all", usedPercent: null, state: "unsupported" }
|
|
725
|
+
],
|
|
726
|
+
lastErrorCode: code
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
// src/allowance/KimiAllowanceCollector.ts
|
|
732
|
+
var import_AccountAllowanceStore3 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
733
|
+
var import_upstreamFetch3 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
734
|
+
var import_subscriptions3 = require("@omnicross/subscriptions");
|
|
735
|
+
var KIMI_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
736
|
+
var KIMI_USAGE_URL = "https://api.kimi.com/coding/v1/usages";
|
|
737
|
+
function finiteNumber2(value) {
|
|
738
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
739
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
740
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
741
|
+
}
|
|
742
|
+
function isRecord(value) {
|
|
743
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
744
|
+
}
|
|
745
|
+
function parseResetMs(row, nowMs) {
|
|
746
|
+
for (const key of ["reset_at", "resetAt", "reset_time", "resetTime"]) {
|
|
747
|
+
const value = row[key];
|
|
748
|
+
if (typeof value === "string" && value.trim()) {
|
|
749
|
+
const parsed = Date.parse(value);
|
|
750
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
751
|
+
}
|
|
752
|
+
const numeric = finiteNumber2(value);
|
|
753
|
+
if (numeric !== void 0 && numeric > 1e9) {
|
|
754
|
+
return numeric > 1e12 ? numeric : numeric * 1e3;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
for (const key of ["reset_in", "resetIn", "ttl", "window"]) {
|
|
758
|
+
const seconds = finiteNumber2(row[key]);
|
|
759
|
+
if (seconds !== void 0) return nowMs + seconds * 1e3;
|
|
760
|
+
}
|
|
761
|
+
return void 0;
|
|
762
|
+
}
|
|
763
|
+
var MINUTE_MS = 6e4;
|
|
764
|
+
var HOUR_MS = 36e5;
|
|
765
|
+
var DAY_MS = 864e5;
|
|
766
|
+
function canonicalWindow(durationMs) {
|
|
767
|
+
if (durationMs === 5 * HOUR_MS) return { id: "five-hour", label: "5 hours", minutes: 300 };
|
|
768
|
+
if (durationMs === 7 * DAY_MS) return { id: "seven-day", label: "7 days", minutes: 10080 };
|
|
769
|
+
if (durationMs > 0 && durationMs % DAY_MS === 0) {
|
|
770
|
+
const days = durationMs / DAY_MS;
|
|
771
|
+
return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}`, minutes: Math.round(durationMs / MINUTE_MS) };
|
|
772
|
+
}
|
|
773
|
+
if (durationMs > 0 && durationMs % HOUR_MS === 0) {
|
|
774
|
+
const hours = durationMs / HOUR_MS;
|
|
775
|
+
return { id: `${hours}h`, label: `${hours} hour${hours === 1 ? "" : "s"}`, minutes: Math.round(durationMs / MINUTE_MS) };
|
|
776
|
+
}
|
|
777
|
+
return void 0;
|
|
778
|
+
}
|
|
779
|
+
function secondsUntil3(instant, now) {
|
|
780
|
+
if (!instant) return void 0;
|
|
781
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
782
|
+
}
|
|
783
|
+
function windowFromRow(row, fallback, now) {
|
|
784
|
+
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;
|
|
785
|
+
const resetsAt = row?.resetsAtMs !== void 0 ? new Date(row.resetsAtMs).toISOString() : void 0;
|
|
786
|
+
return {
|
|
787
|
+
id: fallback.id,
|
|
788
|
+
label: fallback.label,
|
|
789
|
+
scope: "all",
|
|
790
|
+
usedPercent,
|
|
791
|
+
windowMinutes: fallback.minutes,
|
|
792
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
793
|
+
remainingSeconds: secondsUntil3(resetsAt, now),
|
|
794
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
function parseKimiUsagePayload(payload, now) {
|
|
798
|
+
if (!isRecord(payload)) return [];
|
|
799
|
+
const byId = /* @__PURE__ */ new Map();
|
|
800
|
+
const rowFrom = (data) => {
|
|
801
|
+
const limit = finiteNumber2(data["limit"]);
|
|
802
|
+
let used = finiteNumber2(data["used"]);
|
|
803
|
+
const remaining = finiteNumber2(data["remaining"]);
|
|
804
|
+
if (used === void 0 && remaining !== void 0 && limit !== void 0) {
|
|
805
|
+
used = limit - remaining;
|
|
806
|
+
}
|
|
807
|
+
let windowDurationMs;
|
|
808
|
+
const windowData = isRecord(data["window"]) ? data["window"] : void 0;
|
|
809
|
+
const duration = finiteNumber2(windowData?.["duration"]);
|
|
810
|
+
const timeUnit = typeof windowData?.["timeUnit"] === "string" ? windowData["timeUnit"].toUpperCase() : "";
|
|
811
|
+
if (duration !== void 0) {
|
|
812
|
+
if (timeUnit.includes("MINUTE")) windowDurationMs = duration * MINUTE_MS;
|
|
813
|
+
else if (timeUnit.includes("HOUR")) windowDurationMs = duration * HOUR_MS;
|
|
814
|
+
else if (timeUnit.includes("DAY")) windowDurationMs = duration * DAY_MS;
|
|
815
|
+
else if (timeUnit.includes("WEEK")) windowDurationMs = duration * 7 * DAY_MS;
|
|
816
|
+
else if (timeUnit.includes("SECOND")) windowDurationMs = duration * 1e3;
|
|
817
|
+
}
|
|
818
|
+
const resetsAtMs = parseResetMs(windowData && parseResetMs(windowData, now) !== void 0 ? windowData : data, now);
|
|
819
|
+
return { used, limit, remaining, ...resetsAtMs !== void 0 ? { resetsAtMs } : {}, ...windowDurationMs !== void 0 ? { windowDurationMs } : {} };
|
|
820
|
+
};
|
|
821
|
+
if (isRecord(payload["usage"])) {
|
|
822
|
+
const row = rowFrom(payload["usage"]);
|
|
823
|
+
const window = windowFromRow({ ...row, resetsAtMs: row.resetsAtMs }, { id: "seven-day", label: "7 days", minutes: 10080 }, now);
|
|
824
|
+
byId.set("seven-day", window);
|
|
825
|
+
}
|
|
826
|
+
if (Array.isArray(payload["limits"])) {
|
|
827
|
+
for (const item of payload["limits"]) {
|
|
828
|
+
if (!isRecord(item)) continue;
|
|
829
|
+
const detail = isRecord(item["detail"]) ? item["detail"] : item;
|
|
830
|
+
const row = rowFrom(detail);
|
|
831
|
+
const canonical = row.windowDurationMs !== void 0 ? canonicalWindow(row.windowDurationMs) : void 0;
|
|
832
|
+
if (!canonical) continue;
|
|
833
|
+
const window = windowFromRow(row, canonical, now);
|
|
834
|
+
const existing = byId.get(canonical.id);
|
|
835
|
+
if (!existing || (window.usedPercent ?? 0) > (existing.usedPercent ?? 0)) {
|
|
836
|
+
byId.set(canonical.id, window);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
return [...byId.values()].sort((a, b) => (a.windowMinutes ?? Infinity) - (b.windowMinutes ?? Infinity)).slice(0, 4);
|
|
841
|
+
}
|
|
842
|
+
var KimiAllowanceCollector = class {
|
|
843
|
+
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) {
|
|
844
|
+
this.credentials = credentials;
|
|
845
|
+
this.store = store;
|
|
846
|
+
this.fetchImpl = fetchImpl;
|
|
847
|
+
this.now = now;
|
|
848
|
+
}
|
|
849
|
+
credentials;
|
|
850
|
+
store;
|
|
851
|
+
fetchImpl;
|
|
852
|
+
now;
|
|
853
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
854
|
+
async collectMany(accounts, options = {}) {
|
|
855
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
856
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
857
|
+
}
|
|
858
|
+
collect(account, options = {}) {
|
|
859
|
+
const now = this.now();
|
|
860
|
+
if (account.tokens.authMethod !== "oauth") {
|
|
861
|
+
const existing = this.store.get("kimi", account.id, now);
|
|
862
|
+
if (existing?.windows.every((window) => window.state === "unsupported")) {
|
|
863
|
+
return Promise.resolve(existing);
|
|
864
|
+
}
|
|
865
|
+
const snapshot = this.unsupportedSnapshot(account.id, now);
|
|
866
|
+
this.store.set(snapshot);
|
|
867
|
+
return Promise.resolve(snapshot);
|
|
868
|
+
}
|
|
869
|
+
const cached = this.store.get("kimi", account.id, now);
|
|
870
|
+
if (!options.force && cached && this.isCacheValid(cached, now, options.refreshAheadMs)) {
|
|
871
|
+
return Promise.resolve(cached);
|
|
872
|
+
}
|
|
873
|
+
const running = this.inFlight.get(account.id);
|
|
874
|
+
if (running) return running;
|
|
875
|
+
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));
|
|
876
|
+
this.inFlight.set(account.id, promise);
|
|
877
|
+
return promise;
|
|
878
|
+
}
|
|
879
|
+
isCacheValid(snapshot, now, refreshAheadMs) {
|
|
880
|
+
if (snapshot.windows.every((window) => window.state === "unsupported")) return true;
|
|
881
|
+
const expiresAt = snapshot.expiresAt ? Date.parse(snapshot.expiresAt) : 0;
|
|
882
|
+
const ahead = typeof refreshAheadMs === "number" && Number.isFinite(refreshAheadMs) ? Math.max(0, refreshAheadMs) : 0;
|
|
883
|
+
return Number.isFinite(expiresAt) && expiresAt > now + ahead;
|
|
884
|
+
}
|
|
885
|
+
async fetchAccount(accountId, tokens) {
|
|
886
|
+
let accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
|
|
887
|
+
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
888
|
+
let response = await this.request(accountId, accessToken, tokens);
|
|
889
|
+
if (response.status === 401) {
|
|
890
|
+
const refreshed = await this.credentials.refreshAccountToken("kimi", accountId);
|
|
891
|
+
if (!refreshed) return this.failureSnapshot(accountId, "kimi_usage_unauthorized", this.now());
|
|
892
|
+
accessToken = await this.credentials.getAccessTokenForAccount("kimi", accountId);
|
|
893
|
+
if (!accessToken) return this.failureSnapshot(accountId, "kimi_usage_token_unavailable", this.now());
|
|
894
|
+
response = await this.request(accountId, accessToken, tokens);
|
|
895
|
+
}
|
|
896
|
+
if (response.status === 403) {
|
|
897
|
+
const snapshot2 = this.unsupportedSnapshot(accountId, this.now(), "kimi_usage_unsupported");
|
|
898
|
+
this.store.set(snapshot2);
|
|
899
|
+
return snapshot2;
|
|
900
|
+
}
|
|
901
|
+
if (!response.ok) return this.failureSnapshot(accountId, "kimi_usage_http_error", this.now());
|
|
902
|
+
let payload;
|
|
903
|
+
try {
|
|
904
|
+
payload = await response.json();
|
|
905
|
+
} catch {
|
|
906
|
+
return this.failureSnapshot(accountId, "kimi_usage_invalid_response", this.now());
|
|
907
|
+
}
|
|
908
|
+
const now = this.now();
|
|
909
|
+
const windows = parseKimiUsagePayload(payload, now);
|
|
910
|
+
const snapshot = {
|
|
911
|
+
providerId: "kimi",
|
|
912
|
+
accountId,
|
|
913
|
+
source: "oauth-usage-api",
|
|
914
|
+
observedAt: new Date(now).toISOString(),
|
|
915
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
916
|
+
windows: windows.length > 0 ? windows : [
|
|
917
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
918
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
919
|
+
],
|
|
920
|
+
...windows.length > 0 ? {} : { lastErrorCode: "kimi_usage_invalid_response" }
|
|
921
|
+
};
|
|
922
|
+
this.store.set(snapshot);
|
|
923
|
+
return snapshot;
|
|
924
|
+
}
|
|
925
|
+
request(accountId, accessToken, tokens) {
|
|
926
|
+
return this.fetchImpl(KIMI_USAGE_URL, {
|
|
927
|
+
method: "GET",
|
|
928
|
+
headers: {
|
|
929
|
+
Authorization: `Bearer ${accessToken}`,
|
|
930
|
+
Accept: "application/json",
|
|
931
|
+
...(0, import_subscriptions3.kimiFingerprintHeaders)(tokens.deviceId)
|
|
932
|
+
},
|
|
933
|
+
signal: AbortSignal.timeout(15e3)
|
|
934
|
+
}, accountId);
|
|
935
|
+
}
|
|
936
|
+
failureSnapshot(accountId, code, now) {
|
|
937
|
+
const existing = this.store.get("kimi", accountId, now);
|
|
938
|
+
const snapshot = existing ? {
|
|
939
|
+
...existing,
|
|
940
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
941
|
+
windows: existing.windows.map((window) => ({
|
|
942
|
+
...window,
|
|
943
|
+
state: window.state === "unsupported" ? "unsupported" : window.usedPercent !== null || window.resetsAt ? "stale" : "unavailable"
|
|
944
|
+
})),
|
|
945
|
+
lastErrorCode: code
|
|
946
|
+
} : {
|
|
947
|
+
providerId: "kimi",
|
|
948
|
+
accountId,
|
|
949
|
+
source: "oauth-usage-api",
|
|
950
|
+
observedAt: new Date(now).toISOString(),
|
|
951
|
+
expiresAt: new Date(now + KIMI_ALLOWANCE_CACHE_MS).toISOString(),
|
|
952
|
+
windows: [
|
|
953
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
954
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
955
|
+
],
|
|
956
|
+
lastErrorCode: code
|
|
957
|
+
};
|
|
958
|
+
this.store.set(snapshot);
|
|
959
|
+
return snapshot;
|
|
960
|
+
}
|
|
961
|
+
unsupportedSnapshot(accountId, now, code = "kimi_usage_unsupported_auth") {
|
|
962
|
+
return {
|
|
963
|
+
providerId: "kimi",
|
|
964
|
+
accountId,
|
|
965
|
+
source: "oauth-usage-api",
|
|
966
|
+
observedAt: new Date(now).toISOString(),
|
|
967
|
+
windows: [
|
|
968
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unsupported" },
|
|
969
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unsupported" }
|
|
970
|
+
],
|
|
971
|
+
lastErrorCode: code
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
|
|
976
|
+
// src/allowance/OpenCodeGoAllowanceCollector.ts
|
|
977
|
+
var import_AccountAllowanceStore4 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
978
|
+
var import_upstreamFetch4 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
979
|
+
var import_subscriptions4 = require("@omnicross/subscriptions");
|
|
980
|
+
var OPENCODEGO_ALLOWANCE_CACHE_MS = 5 * 6e4;
|
|
981
|
+
var OPENCODEGO_DEFAULT_GO_BASE = "https://opencode.ai/zen/go";
|
|
982
|
+
function finitePercent3(value) {
|
|
983
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
984
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
985
|
+
return Number.isFinite(parsed) && parsed >= 0 && parsed <= 100 ? parsed : null;
|
|
986
|
+
}
|
|
987
|
+
function isoInstant2(value) {
|
|
988
|
+
if (typeof value !== "string" || !value.trim()) return void 0;
|
|
989
|
+
const time = Date.parse(value);
|
|
990
|
+
return Number.isFinite(time) ? new Date(time).toISOString() : void 0;
|
|
991
|
+
}
|
|
992
|
+
function secondsUntil4(instant, now) {
|
|
993
|
+
if (!instant) return void 0;
|
|
994
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
995
|
+
}
|
|
996
|
+
function windowFromPayload3(id, label, minutes, payload, now) {
|
|
997
|
+
const statusRateLimited = payload?.status === "rate-limited";
|
|
998
|
+
const usedPercent = statusRateLimited ? 100 : finitePercent3(payload?.percent);
|
|
999
|
+
const resetsAt = isoInstant2(payload?.resetsAt);
|
|
1000
|
+
return {
|
|
1001
|
+
id,
|
|
1002
|
+
label,
|
|
1003
|
+
scope: "all",
|
|
1004
|
+
usedPercent,
|
|
1005
|
+
windowMinutes: minutes,
|
|
1006
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
1007
|
+
remainingSeconds: secondsUntil4(resetsAt, now),
|
|
1008
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
var OpenCodeGoAllowanceCollector = class {
|
|
1012
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore4.getSharedAccountAllowanceStore)(), fetchImpl = (url, init, accountId) => (0, import_upstreamFetch4.fetchUpstream)(url, init, { providerId: "opencodego", accountId, redactBodies: true }), now = Date.now) {
|
|
1013
|
+
this.credentials = credentials;
|
|
1014
|
+
this.store = store;
|
|
1015
|
+
this.fetchImpl = fetchImpl;
|
|
1016
|
+
this.now = now;
|
|
1017
|
+
}
|
|
1018
|
+
credentials;
|
|
1019
|
+
store;
|
|
1020
|
+
fetchImpl;
|
|
1021
|
+
now;
|
|
1022
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
1023
|
+
async collectMany(accounts, options = {}) {
|
|
1024
|
+
const settled = await Promise.allSettled(accounts.map((account) => this.collect(account, options)));
|
|
1025
|
+
return settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
|
|
1026
|
+
}
|
|
1027
|
+
collect(account, options = {}) {
|
|
1028
|
+
const now = this.now();
|
|
1029
|
+
const cached = this.store.get("opencodego", account.id, now);
|
|
1030
|
+
if (!options.force && cached && (cached.windows.every((window) => window.state === "unsupported") || cached.expiresAt && Date.parse(cached.expiresAt) > now + (options.refreshAheadMs ?? 0))) {
|
|
1031
|
+
return Promise.resolve(cached);
|
|
1032
|
+
}
|
|
1033
|
+
const running = this.inFlight.get(account.id);
|
|
1034
|
+
if (running) return running;
|
|
1035
|
+
const promise = this.fetchAccount(account).catch(() => this.failureSnapshot(account.id, this.now())).finally(() => this.inFlight.delete(account.id));
|
|
1036
|
+
this.inFlight.set(account.id, promise);
|
|
1037
|
+
return promise;
|
|
1038
|
+
}
|
|
1039
|
+
async fetchAccount(account) {
|
|
1040
|
+
const apiKey = await this.credentials.getAccessTokenForAccount("opencodego", account.id);
|
|
1041
|
+
if (!apiKey) return this.failureSnapshot(account.id, this.now());
|
|
1042
|
+
const base = account.tokens.baseUrl ? (0, import_subscriptions4.normalizeOpenCodeGoBaseUrl)(account.tokens.baseUrl) : OPENCODEGO_DEFAULT_GO_BASE;
|
|
1043
|
+
const response = await this.fetchImpl(`${base}/v1/usage`, {
|
|
1044
|
+
method: "GET",
|
|
1045
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
1046
|
+
signal: AbortSignal.timeout(15e3)
|
|
1047
|
+
}, account.id);
|
|
1048
|
+
if (response.status === 401 || response.status === 403) {
|
|
1049
|
+
return this.failureSnapshot(account.id, this.now(), "opencodego_usage_unauthorized");
|
|
1050
|
+
}
|
|
1051
|
+
if (!response.ok) return this.failureSnapshot(account.id, this.now());
|
|
1052
|
+
let payload;
|
|
1053
|
+
try {
|
|
1054
|
+
payload = await response.json();
|
|
1055
|
+
} catch {
|
|
1056
|
+
return this.failureSnapshot(account.id, this.now());
|
|
1057
|
+
}
|
|
1058
|
+
const usage = payload && typeof payload === "object" && !Array.isArray(payload) ? payload.usage : void 0;
|
|
1059
|
+
const now = this.now();
|
|
1060
|
+
const snapshot = {
|
|
1061
|
+
providerId: "opencodego",
|
|
1062
|
+
accountId: account.id,
|
|
1063
|
+
source: "oauth-usage-api",
|
|
1064
|
+
observedAt: new Date(now).toISOString(),
|
|
1065
|
+
expiresAt: new Date(now + OPENCODEGO_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1066
|
+
// Monthly deliberately omitted (module doc).
|
|
1067
|
+
windows: [
|
|
1068
|
+
windowFromPayload3("five-hour", "5 hours", 5 * 60, usage?.rolling ?? void 0, now),
|
|
1069
|
+
windowFromPayload3("seven-day", "7 days", 7 * 24 * 60, usage?.weekly ?? void 0, now)
|
|
1070
|
+
]
|
|
1071
|
+
};
|
|
1072
|
+
this.store.set(snapshot);
|
|
1073
|
+
return snapshot;
|
|
1074
|
+
}
|
|
1075
|
+
failureSnapshot(accountId, now, code = "opencodego_usage_request_failed") {
|
|
1076
|
+
const existing = this.store.get("opencodego", accountId, now);
|
|
1077
|
+
const snapshot = existing ? {
|
|
1078
|
+
...existing,
|
|
1079
|
+
expiresAt: new Date(now + OPENCODEGO_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1080
|
+
windows: existing.windows.map((window) => ({
|
|
1081
|
+
...window,
|
|
1082
|
+
state: window.usedPercent !== null || window.resetsAt ? "stale" : window.state
|
|
1083
|
+
})),
|
|
1084
|
+
lastErrorCode: code
|
|
1085
|
+
} : {
|
|
1086
|
+
providerId: "opencodego",
|
|
1087
|
+
accountId,
|
|
1088
|
+
source: "oauth-usage-api",
|
|
1089
|
+
observedAt: new Date(now).toISOString(),
|
|
1090
|
+
expiresAt: new Date(now + OPENCODEGO_ALLOWANCE_CACHE_MS).toISOString(),
|
|
1091
|
+
windows: [
|
|
1092
|
+
{ id: "five-hour", label: "5 hours", scope: "all", usedPercent: null, state: "unavailable" },
|
|
1093
|
+
{ id: "seven-day", label: "7 days", scope: "all", usedPercent: null, state: "unavailable" }
|
|
1094
|
+
],
|
|
1095
|
+
lastErrorCode: code
|
|
1096
|
+
};
|
|
1097
|
+
this.store.set(snapshot);
|
|
1098
|
+
return snapshot;
|
|
1099
|
+
}
|
|
1100
|
+
};
|
|
1101
|
+
|
|
402
1102
|
// src/allowance/AccountAllowanceService.ts
|
|
403
1103
|
function codexUnavailable(accountId, now) {
|
|
404
1104
|
return {
|
|
@@ -414,26 +1114,30 @@ function codexUnavailable(accountId, now) {
|
|
|
414
1114
|
};
|
|
415
1115
|
}
|
|
416
1116
|
var AccountAllowanceService = class {
|
|
417
|
-
constructor(credentials, store = (0,
|
|
1117
|
+
constructor(credentials, store = (0, import_AccountAllowanceStore5.getSharedAccountAllowanceStore)(), collector, codexCollector, kimiCollector, opencodegoCollector, now = Date.now) {
|
|
418
1118
|
this.credentials = credentials;
|
|
419
1119
|
this.store = store;
|
|
420
1120
|
this.now = now;
|
|
421
1121
|
this.claudeCollector = collector ?? new ClaudeAllowanceCollector(credentials, store);
|
|
1122
|
+
this.codexCollector = codexCollector ?? new CodexAllowanceCollector(credentials, store);
|
|
1123
|
+
this.kimiCollector = kimiCollector ?? new KimiAllowanceCollector(credentials, store);
|
|
1124
|
+
this.opencodegoCollector = opencodegoCollector ?? new OpenCodeGoAllowanceCollector(credentials, store);
|
|
422
1125
|
}
|
|
423
1126
|
credentials;
|
|
424
1127
|
store;
|
|
425
1128
|
now;
|
|
426
1129
|
claudeCollector;
|
|
1130
|
+
codexCollector;
|
|
1131
|
+
kimiCollector;
|
|
1132
|
+
opencodegoCollector;
|
|
427
1133
|
/**
|
|
428
|
-
* Read all/filtered snapshots. Claude's five-minute
|
|
429
|
-
*
|
|
1134
|
+
* Read all/filtered snapshots. Claude's and Codex's five-minute caches are
|
|
1135
|
+
* refreshed lazily on read (Codex polls `/backend-api/wham/usage`; the
|
|
1136
|
+
* passive `x-codex-*` header tap still feeds mid-flight updates).
|
|
430
1137
|
*/
|
|
431
1138
|
async list(filter = {}) {
|
|
432
1139
|
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
|
-
]);
|
|
1140
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
437
1141
|
const wantsClaude = !filter.providerId || filter.providerId === "claude";
|
|
438
1142
|
const claudeAccounts = (config.claudeAccounts ?? []).filter(
|
|
439
1143
|
(account) => !filter.accountId || account.id === filter.accountId
|
|
@@ -444,39 +1148,90 @@ var AccountAllowanceService = class {
|
|
|
444
1148
|
(account) => !filter.accountId || account.id === filter.accountId
|
|
445
1149
|
);
|
|
446
1150
|
if (wantsCodex) {
|
|
1151
|
+
await this.codexCollector.collectMany(codexAccounts);
|
|
447
1152
|
for (const account of codexAccounts) {
|
|
448
1153
|
if (!this.store.get("codex", account.id)) this.store.set(codexUnavailable(account.id, this.now()));
|
|
449
1154
|
}
|
|
450
1155
|
}
|
|
1156
|
+
const wantsKimi = !filter.providerId || filter.providerId === "kimi";
|
|
1157
|
+
const kimiAccounts = (config.kimiAccounts ?? []).filter(
|
|
1158
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
1159
|
+
);
|
|
1160
|
+
if (wantsKimi) await this.kimiCollector.collectMany(kimiAccounts);
|
|
1161
|
+
const wantsOpenCodeGo = !filter.providerId || filter.providerId === "opencodego";
|
|
1162
|
+
const opencodegoAccounts = (config.opencodegoAccounts ?? []).filter(
|
|
1163
|
+
(account) => !filter.accountId || account.id === filter.accountId
|
|
1164
|
+
);
|
|
1165
|
+
if (wantsOpenCodeGo) await this.opencodegoCollector.collectMany(opencodegoAccounts);
|
|
451
1166
|
const known = /* @__PURE__ */ new Set();
|
|
452
1167
|
if (wantsClaude) for (const account of claudeAccounts) known.add(`claude\0${account.id}`);
|
|
453
1168
|
if (wantsCodex) for (const account of codexAccounts) known.add(`codex\0${account.id}`);
|
|
1169
|
+
if (wantsKimi) for (const account of kimiAccounts) known.add(`kimi\0${account.id}`);
|
|
1170
|
+
if (wantsOpenCodeGo) for (const account of opencodegoAccounts) known.add(`opencodego\0${account.id}`);
|
|
454
1171
|
return this.store.list(filter).filter((snapshot) => known.has(`${snapshot.providerId}\0${snapshot.accountId}`));
|
|
455
1172
|
}
|
|
1173
|
+
knownAccounts(config) {
|
|
1174
|
+
return [
|
|
1175
|
+
...(config.claudeAccounts ?? []).map((account) => ({ providerId: "claude", accountId: account.id })),
|
|
1176
|
+
...(config.codexAccounts ?? []).map((account) => ({ providerId: "codex", accountId: account.id })),
|
|
1177
|
+
...(config.kimiAccounts ?? []).map((account) => ({ providerId: "kimi", accountId: account.id })),
|
|
1178
|
+
...(config.opencodegoAccounts ?? []).map((account) => ({ providerId: "opencodego", accountId: account.id }))
|
|
1179
|
+
];
|
|
1180
|
+
}
|
|
456
1181
|
/** Force-refresh Claude usage for one account or every stored Claude account. */
|
|
457
1182
|
async refreshClaude(accountId) {
|
|
458
1183
|
const config = await this.credentials.getFullConfig();
|
|
459
|
-
this.store.pruneToKnownAccounts(
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
1184
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1185
|
+
const accounts = (config.claudeAccounts ?? []).filter(
|
|
1186
|
+
(account) => !accountId || account.id === accountId
|
|
1187
|
+
);
|
|
1188
|
+
return this.claudeCollector.collectMany(accounts, { force: true });
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Force-refresh Codex usage (`/backend-api/wham/usage`) for one account or
|
|
1192
|
+
* every stored Codex account. Replaces the old probe-request workaround —
|
|
1193
|
+
* no quota is spent reading the usage endpoint.
|
|
1194
|
+
*/
|
|
1195
|
+
async refreshCodex(accountId) {
|
|
1196
|
+
const config = await this.credentials.getFullConfig();
|
|
1197
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1198
|
+
const accounts = (config.codexAccounts ?? []).filter(
|
|
1199
|
+
(account) => !accountId || account.id === accountId
|
|
1200
|
+
);
|
|
1201
|
+
return this.codexCollector.collectMany(accounts, { force: true });
|
|
1202
|
+
}
|
|
1203
|
+
/** Force-refresh OpenCodeGo usage (`{go}/v1/usage`) for one/all accounts. */
|
|
1204
|
+
async refreshOpenCodeGo(accountId) {
|
|
1205
|
+
const config = await this.credentials.getFullConfig();
|
|
1206
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1207
|
+
const accounts = (config.opencodegoAccounts ?? []).filter(
|
|
1208
|
+
(account) => !accountId || account.id === accountId
|
|
1209
|
+
);
|
|
1210
|
+
return this.opencodegoCollector.collectMany(accounts, { force: true });
|
|
1211
|
+
}
|
|
1212
|
+
/** Force-refresh Kimi usage (`/coding/v1/usages`) for one/all accounts. */
|
|
1213
|
+
async refreshKimi(accountId) {
|
|
1214
|
+
const config = await this.credentials.getFullConfig();
|
|
1215
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
1216
|
+
const accounts = (config.kimiAccounts ?? []).filter(
|
|
464
1217
|
(account) => !accountId || account.id === accountId
|
|
465
1218
|
);
|
|
466
|
-
return this.
|
|
1219
|
+
return this.kimiCollector.collectMany(accounts, { force: true });
|
|
467
1220
|
}
|
|
468
1221
|
/**
|
|
469
|
-
* Keep Claude snapshots warm for allowance-aware routing.
|
|
470
|
-
*
|
|
471
|
-
*
|
|
1222
|
+
* Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
|
|
1223
|
+
* collectors preserve their cache + per-account in-flight coalescing; a tick
|
|
1224
|
+
* normally performs no network I/O. (Codex joined the warm path when it
|
|
1225
|
+
* gained an active `/wham/usage` collector — the passive `x-codex-*` header
|
|
1226
|
+
* tap alone could not keep the policy fed while idle.)
|
|
472
1227
|
*/
|
|
473
1228
|
async maintainClaudeCache(refreshAheadMs) {
|
|
474
1229
|
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
|
-
]);
|
|
1230
|
+
this.store.pruneToKnownAccounts(this.knownAccounts(config));
|
|
479
1231
|
await this.claudeCollector.collectMany(config.claudeAccounts ?? [], { refreshAheadMs });
|
|
1232
|
+
await this.codexCollector.collectMany(config.codexAccounts ?? [], { refreshAheadMs });
|
|
1233
|
+
await this.kimiCollector.collectMany(config.kimiAccounts ?? [], { refreshAheadMs });
|
|
1234
|
+
await this.opencodegoCollector.collectMany(config.opencodegoAccounts ?? [], { refreshAheadMs });
|
|
480
1235
|
}
|
|
481
1236
|
/** Remove a cache row as soon as an account is deleted by the admin path. */
|
|
482
1237
|
removeAccountSnapshot(providerId, accountId) {
|
|
@@ -571,7 +1326,7 @@ var ClaudeAllowanceRefreshScheduler = class {
|
|
|
571
1326
|
var import_node_crypto2 = require("crypto");
|
|
572
1327
|
var import_node_fs = require("fs");
|
|
573
1328
|
var import_node_path = require("path");
|
|
574
|
-
var
|
|
1329
|
+
var import_AccountAllowanceStore6 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
575
1330
|
var ACCOUNT_ALLOWANCE_CACHE_VERSION = 1;
|
|
576
1331
|
var MAX_PERSISTED_ALLOWANCE_SNAPSHOTS = 256;
|
|
577
1332
|
var MAX_ALLOWANCE_CACHE_BYTES = 1e6;
|
|
@@ -600,7 +1355,7 @@ var JsonAccountAllowancePersistence = class {
|
|
|
600
1355
|
save(snapshots) {
|
|
601
1356
|
const rows = [];
|
|
602
1357
|
for (const snapshot of snapshots) {
|
|
603
|
-
const normalized2 = (0,
|
|
1358
|
+
const normalized2 = (0, import_AccountAllowanceStore6.normalizeAccountAllowanceSnapshot)(snapshot);
|
|
604
1359
|
if (!normalized2) continue;
|
|
605
1360
|
rows.push(normalized2);
|
|
606
1361
|
if (rows.length >= MAX_PERSISTED_ALLOWANCE_SNAPSHOTS) break;
|
|
@@ -883,7 +1638,7 @@ var import_outbound_api5 = require("@omnicross/core/outbound-api");
|
|
|
883
1638
|
var import_image_generation_types = require("@omnicross/contracts/image-generation-types");
|
|
884
1639
|
var import_AccountAllowanceScheduling2 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
885
1640
|
var import_SubscriptionAccountHealth = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
886
|
-
var
|
|
1641
|
+
var import_upstreamFetch7 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
887
1642
|
|
|
888
1643
|
// src/image-generation/imagesConfigValidation.ts
|
|
889
1644
|
var import_outbound_api = require("@omnicross/core/outbound-api");
|
|
@@ -2851,11 +3606,11 @@ function preserveOutboundProxySecrets(incoming, current) {
|
|
|
2851
3606
|
}
|
|
2852
3607
|
|
|
2853
3608
|
// src/proxy/upstreamProxyResolver.ts
|
|
2854
|
-
var
|
|
3609
|
+
var import_upstreamFetch5 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
2855
3610
|
var serverProxy;
|
|
2856
3611
|
function setServerProxyConfig(proxy) {
|
|
2857
3612
|
serverProxy = proxy;
|
|
2858
|
-
(0,
|
|
3613
|
+
(0, import_upstreamFetch5.bumpUpstreamProxyGeneration)();
|
|
2859
3614
|
}
|
|
2860
3615
|
function getServerProxyConfig() {
|
|
2861
3616
|
return serverProxy;
|
|
@@ -2923,14 +3678,15 @@ function createUpstreamProxyResolver(src = {}) {
|
|
|
2923
3678
|
}
|
|
2924
3679
|
|
|
2925
3680
|
// src/admin/accountsOAuth.ts
|
|
2926
|
-
var
|
|
3681
|
+
var import_subscriptions5 = require("@omnicross/subscriptions");
|
|
2927
3682
|
|
|
2928
3683
|
// src/admin/accountsWrite.ts
|
|
2929
3684
|
var VALID_PROVIDER_IDS = [
|
|
2930
3685
|
"claude",
|
|
2931
3686
|
"codex",
|
|
2932
3687
|
"gemini",
|
|
2933
|
-
"opencodego"
|
|
3688
|
+
"opencodego",
|
|
3689
|
+
"kimi"
|
|
2934
3690
|
];
|
|
2935
3691
|
function asSubscriptionProviderId(id) {
|
|
2936
3692
|
return VALID_PROVIDER_IDS.includes(id) ? id : null;
|
|
@@ -3066,6 +3822,18 @@ function validateGemini(body) {
|
|
|
3066
3822
|
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "lastRefreshedAt", "errorMessage"]);
|
|
3067
3823
|
return out;
|
|
3068
3824
|
}
|
|
3825
|
+
function validateKimi(body) {
|
|
3826
|
+
const authMethod = str(body["authMethod"]);
|
|
3827
|
+
const status = str(body["status"]);
|
|
3828
|
+
if (!authMethod || !OAUTH_AUTH_METHODS.has(authMethod)) return null;
|
|
3829
|
+
if (!status || !TOKEN_STATUSES.has(status)) return null;
|
|
3830
|
+
const out = {
|
|
3831
|
+
authMethod,
|
|
3832
|
+
status
|
|
3833
|
+
};
|
|
3834
|
+
copyOptional(out, body, ["accessToken", "refreshToken", "expiresAt", "accountId", "deviceId", "lastRefreshedAt", "errorMessage"]);
|
|
3835
|
+
return out;
|
|
3836
|
+
}
|
|
3069
3837
|
function validateOpenCodeGo(body) {
|
|
3070
3838
|
const authMethod = str(body["authMethod"]);
|
|
3071
3839
|
const status = str(body["status"]);
|
|
@@ -3101,6 +3869,8 @@ function validateTokenBody(providerId, body) {
|
|
|
3101
3869
|
return validateGemini(body);
|
|
3102
3870
|
case "opencodego":
|
|
3103
3871
|
return validateOpenCodeGo(body);
|
|
3872
|
+
case "kimi":
|
|
3873
|
+
return validateKimi(body);
|
|
3104
3874
|
default:
|
|
3105
3875
|
return null;
|
|
3106
3876
|
}
|
|
@@ -3130,37 +3900,37 @@ async function statusEntryFor(reader, providerId) {
|
|
|
3130
3900
|
|
|
3131
3901
|
// src/admin/accountsOAuth.ts
|
|
3132
3902
|
var OAUTH_HTTP_PROVIDERS = /* @__PURE__ */ new Set(["claude", "gemini"]);
|
|
3133
|
-
function
|
|
3903
|
+
function err3(status, message) {
|
|
3134
3904
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
3135
3905
|
}
|
|
3136
3906
|
function handleOAuthStart(providerId, deps) {
|
|
3137
3907
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
3138
|
-
return
|
|
3908
|
+
return err3(400, `oauth not available for provider '${providerId}'`);
|
|
3139
3909
|
}
|
|
3140
|
-
const flow = providerId === "claude" ?
|
|
3910
|
+
const flow = providerId === "claude" ? import_subscriptions5.claudeOAuth : import_subscriptions5.geminiOAuth;
|
|
3141
3911
|
const { authUrl, codeVerifier, state } = flow.generateAuthParams();
|
|
3142
3912
|
const sessionId = deps.oauthSessions.put({ providerId, codeVerifier, state });
|
|
3143
3913
|
return { status: 200, body: { authUrl, sessionId } };
|
|
3144
3914
|
}
|
|
3145
3915
|
async function handleOAuthComplete(providerId, body, deps) {
|
|
3146
3916
|
if (!OAUTH_HTTP_PROVIDERS.has(providerId)) {
|
|
3147
|
-
return
|
|
3917
|
+
return err3(400, `oauth not available for provider '${providerId}'`);
|
|
3148
3918
|
}
|
|
3149
3919
|
const sessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : "";
|
|
3150
3920
|
const rawCode = typeof body["code"] === "string" ? body["code"] : "";
|
|
3151
|
-
if (!sessionId) return
|
|
3152
|
-
if (!rawCode) return
|
|
3921
|
+
if (!sessionId) return err3(400, "oauth complete requires { sessionId }");
|
|
3922
|
+
if (!rawCode) return err3(400, "oauth complete requires { code }");
|
|
3153
3923
|
const session = deps.oauthSessions.peek(sessionId);
|
|
3154
|
-
if (!session) return
|
|
3924
|
+
if (!session) return err3(410, "oauth session is unknown, expired, or already used");
|
|
3155
3925
|
if (session.providerId !== providerId) {
|
|
3156
|
-
return
|
|
3926
|
+
return err3(400, `oauth session does not match provider '${providerId}'`);
|
|
3157
3927
|
}
|
|
3158
3928
|
let code = rawCode.trim();
|
|
3159
3929
|
if (providerId === "claude") {
|
|
3160
3930
|
const [splitCode, pastedState] = code.split("#");
|
|
3161
|
-
if (!splitCode) return
|
|
3931
|
+
if (!splitCode) return err3(400, "no authorization code was provided");
|
|
3162
3932
|
if (pastedState && pastedState !== session.state) {
|
|
3163
|
-
return
|
|
3933
|
+
return err3(400, "oauth state did not match (possible CSRF) \u2014 aborting");
|
|
3164
3934
|
}
|
|
3165
3935
|
code = splitCode;
|
|
3166
3936
|
}
|
|
@@ -3170,7 +3940,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
3170
3940
|
block = providerId === "claude" ? await exchangeClaude(code, session.codeVerifier, session.state, exchangeFetch) : await exchangeGemini(code, session.codeVerifier, exchangeFetch);
|
|
3171
3941
|
} catch (exchangeError) {
|
|
3172
3942
|
const reason = exchangeError instanceof Error ? exchangeError.message : "token exchange failed";
|
|
3173
|
-
return
|
|
3943
|
+
return err3(502, `oauth token exchange failed for '${providerId}': ${reason}`);
|
|
3174
3944
|
}
|
|
3175
3945
|
deps.oauthSessions.consume(sessionId);
|
|
3176
3946
|
const label = typeof body["label"] === "string" && body["label"].trim() ? body["label"].trim() : void 0;
|
|
@@ -3179,7 +3949,7 @@ async function handleOAuthComplete(providerId, body, deps) {
|
|
|
3179
3949
|
return { status: 200, body: status ? { account: status } : { ok: true } };
|
|
3180
3950
|
}
|
|
3181
3951
|
async function exchangeClaude(code, codeVerifier, state, exchangeFetch) {
|
|
3182
|
-
const result = await
|
|
3952
|
+
const result = await import_subscriptions5.claudeOAuth.exchangeCodeForTokens(
|
|
3183
3953
|
{ authorizationCode: code, codeVerifier, state },
|
|
3184
3954
|
exchangeFetch
|
|
3185
3955
|
);
|
|
@@ -3195,7 +3965,7 @@ async function exchangeClaude(code, codeVerifier, state, exchangeFetch) {
|
|
|
3195
3965
|
};
|
|
3196
3966
|
}
|
|
3197
3967
|
async function exchangeGemini(code, codeVerifier, exchangeFetch) {
|
|
3198
|
-
const result = await
|
|
3968
|
+
const result = await import_subscriptions5.geminiOAuth.exchangeCodeForTokens(code, codeVerifier, exchangeFetch);
|
|
3199
3969
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
3200
3970
|
return {
|
|
3201
3971
|
authMethod: "oauth",
|
|
@@ -3500,8 +4270,8 @@ function errBody(message) {
|
|
|
3500
4270
|
return { error: { type: "admin_api_error", message } };
|
|
3501
4271
|
}
|
|
3502
4272
|
var defaultCommandRunner = (command) => new Promise((resolve10) => {
|
|
3503
|
-
(0, import_node_child_process.exec)(command, { timeout: 18e4 }, (
|
|
3504
|
-
if (
|
|
4273
|
+
(0, import_node_child_process.exec)(command, { timeout: 18e4 }, (err6, _stdout, stderr) => {
|
|
4274
|
+
if (err6) resolve10({ ok: false, error: stderr.trim() || err6.message });
|
|
3505
4275
|
else resolve10({ ok: true });
|
|
3506
4276
|
});
|
|
3507
4277
|
});
|
|
@@ -3547,8 +4317,8 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
3547
4317
|
providerId: typeof body["providerId"] === "string" ? body["providerId"] : void 0,
|
|
3548
4318
|
model: typeof body["model"] === "string" ? body["model"] : void 0
|
|
3549
4319
|
});
|
|
3550
|
-
} catch (
|
|
3551
|
-
return { status: 400, body: errBody(
|
|
4320
|
+
} catch (err6) {
|
|
4321
|
+
return { status: 400, body: errBody(err6 instanceof Error ? err6.message : "no launch target") };
|
|
3552
4322
|
}
|
|
3553
4323
|
const id = (0, import_node_crypto7.randomUUID)();
|
|
3554
4324
|
let leaseId2;
|
|
@@ -3576,9 +4346,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
3576
4346
|
} else {
|
|
3577
4347
|
launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
|
|
3578
4348
|
}
|
|
3579
|
-
} catch (
|
|
3580
|
-
const status =
|
|
3581
|
-
return { status, body: errBody(
|
|
4349
|
+
} catch (err6) {
|
|
4350
|
+
const status = err6 instanceof import_provider_proxy2.RouteLeaseError ? err6.status : 400;
|
|
4351
|
+
return { status, body: errBody(err6 instanceof Error ? err6.message : "failed to build launch env") };
|
|
3582
4352
|
}
|
|
3583
4353
|
const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
|
|
3584
4354
|
const opener = ctx.opener ?? defaultTerminalOpener;
|
|
@@ -3606,9 +4376,9 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
3606
4376
|
onFailure: onSessionEnd
|
|
3607
4377
|
});
|
|
3608
4378
|
if (cleanup) openerCleanup = cleanup;
|
|
3609
|
-
} catch (
|
|
4379
|
+
} catch (err6) {
|
|
3610
4380
|
onSessionEnd();
|
|
3611
|
-
return { status: 500, body: errBody(
|
|
4381
|
+
return { status: 500, body: errBody(err6 instanceof Error ? err6.message : "failed to open terminal") };
|
|
3612
4382
|
}
|
|
3613
4383
|
if (ended) {
|
|
3614
4384
|
openerCleanup?.();
|
|
@@ -3849,7 +4619,7 @@ function classifySearchFailure(stage, code) {
|
|
|
3849
4619
|
}
|
|
3850
4620
|
|
|
3851
4621
|
// src/search/SearchAssembly.ts
|
|
3852
|
-
var
|
|
4622
|
+
var import_upstreamFetch6 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
3853
4623
|
var import_search = require("@omnicross/core/search");
|
|
3854
4624
|
var import_api2 = require("@omnicross/core/search/api");
|
|
3855
4625
|
var import_http2 = require("@omnicross/core/search/http");
|
|
@@ -3867,7 +4637,7 @@ function searchPolicyFrom(config) {
|
|
|
3867
4637
|
};
|
|
3868
4638
|
}
|
|
3869
4639
|
function resolveSearchUpstreamDispatcher(url) {
|
|
3870
|
-
return (0,
|
|
4640
|
+
return (0, import_upstreamFetch6.resolveUpstreamDispatcher)({ url });
|
|
3871
4641
|
}
|
|
3872
4642
|
var searchUpstreamProxyConfig = createUpstreamProxyResolver();
|
|
3873
4643
|
function resolveSearchUpstreamProxyConfig(url) {
|
|
@@ -4149,7 +4919,7 @@ async function handleSearchQuery(req, res, deps) {
|
|
|
4149
4919
|
// src/admin/searchAdminView.ts
|
|
4150
4920
|
var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
|
|
4151
4921
|
var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
|
|
4152
|
-
function
|
|
4922
|
+
function isRecord2(value) {
|
|
4153
4923
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4154
4924
|
}
|
|
4155
4925
|
function redactSearchServerConfig(search) {
|
|
@@ -4199,13 +4969,13 @@ function resolveSecretField(entry, field, stored) {
|
|
|
4199
4969
|
else delete entry[field];
|
|
4200
4970
|
}
|
|
4201
4971
|
function preserveSearchSecrets(incoming, current) {
|
|
4202
|
-
if (!
|
|
4972
|
+
if (!isRecord2(incoming)) return incoming;
|
|
4203
4973
|
const section = { ...incoming };
|
|
4204
4974
|
const providersValue = section["providers"];
|
|
4205
|
-
if (!
|
|
4975
|
+
if (!isRecord2(providersValue)) return section;
|
|
4206
4976
|
const providers = {};
|
|
4207
4977
|
for (const [id, entryValue] of Object.entries(providersValue)) {
|
|
4208
|
-
if (!
|
|
4978
|
+
if (!isRecord2(entryValue)) {
|
|
4209
4979
|
providers[id] = entryValue;
|
|
4210
4980
|
continue;
|
|
4211
4981
|
}
|
|
@@ -4283,7 +5053,7 @@ function parseKeyPolicyBody(body) {
|
|
|
4283
5053
|
var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
|
|
4284
5054
|
var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
|
|
4285
5055
|
var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
|
|
4286
|
-
function
|
|
5056
|
+
function isRecord3(value) {
|
|
4287
5057
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
4288
5058
|
}
|
|
4289
5059
|
function nonBlank(value) {
|
|
@@ -4303,7 +5073,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
4303
5073
|
const ids = /* @__PURE__ */ new Set();
|
|
4304
5074
|
raw.forEach((entry, index) => {
|
|
4305
5075
|
const path2 = `bindings[${index}]`;
|
|
4306
|
-
if (!
|
|
5076
|
+
if (!isRecord3(entry)) {
|
|
4307
5077
|
errors.push(`${path2} must be an object`);
|
|
4308
5078
|
return;
|
|
4309
5079
|
}
|
|
@@ -4332,12 +5102,12 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
4332
5102
|
} else if (entry.modelMappings.length > 100) {
|
|
4333
5103
|
errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
|
|
4334
5104
|
} else if (entry.modelMappings.some(
|
|
4335
|
-
(mapping) => !
|
|
5105
|
+
(mapping) => !isRecord3(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
|
|
4336
5106
|
)) {
|
|
4337
5107
|
errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
|
|
4338
5108
|
}
|
|
4339
5109
|
}
|
|
4340
|
-
if (!
|
|
5110
|
+
if (!isRecord3(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
|
|
4341
5111
|
errors.push(`${path2}.target is invalid`);
|
|
4342
5112
|
} else {
|
|
4343
5113
|
if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
|
|
@@ -4352,7 +5122,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
4352
5122
|
}
|
|
4353
5123
|
}
|
|
4354
5124
|
if (entry.modelMap !== void 0) {
|
|
4355
|
-
if (!
|
|
5125
|
+
if (!isRecord3(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
|
|
4356
5126
|
errors.push(`${path2}.modelMap must contain string values`);
|
|
4357
5127
|
}
|
|
4358
5128
|
}
|
|
@@ -4650,7 +5420,8 @@ var PROVIDER_KEYS = {
|
|
|
4650
5420
|
block: "opencodego",
|
|
4651
5421
|
accounts: "opencodegoAccounts",
|
|
4652
5422
|
active: "activeOpencodegoAccountId"
|
|
4653
|
-
}
|
|
5423
|
+
},
|
|
5424
|
+
kimi: { block: "kimi", accounts: "kimiAccounts", active: "activeKimiAccountId" }
|
|
4654
5425
|
};
|
|
4655
5426
|
function clone(value) {
|
|
4656
5427
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -5172,7 +5943,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
|
|
|
5172
5943
|
}
|
|
5173
5944
|
|
|
5174
5945
|
// src/admin/adminMigration.ts
|
|
5175
|
-
function
|
|
5946
|
+
function err4(status, message) {
|
|
5176
5947
|
return { status, body: { error: { type: "admin_api_error", message } } };
|
|
5177
5948
|
}
|
|
5178
5949
|
async function handleExport(body, deps) {
|
|
@@ -5182,30 +5953,30 @@ async function handleExport(body, deps) {
|
|
|
5182
5953
|
return { status: 200, body: { pack, version: BUNDLE_VERSION } };
|
|
5183
5954
|
} catch (error) {
|
|
5184
5955
|
if (error instanceof WeakPassphraseError) {
|
|
5185
|
-
return
|
|
5956
|
+
return err4(400, error.message);
|
|
5186
5957
|
}
|
|
5187
|
-
return
|
|
5958
|
+
return err4(500, "failed to build the migration pack");
|
|
5188
5959
|
}
|
|
5189
5960
|
}
|
|
5190
5961
|
async function handleImport(body, deps) {
|
|
5191
5962
|
const blob = typeof body["blob"] === "string" ? body["blob"] : "";
|
|
5192
5963
|
const passphrase = typeof body["passphrase"] === "string" ? body["passphrase"] : "";
|
|
5193
5964
|
const mode = body["mode"] === "overwrite" ? "overwrite" : "merge";
|
|
5194
|
-
if (!blob) return
|
|
5965
|
+
if (!blob) return err4(400, "import requires { blob }");
|
|
5195
5966
|
try {
|
|
5196
5967
|
const counts = await applyImport(blob, passphrase, mode, deps, deps.parseProviderInput);
|
|
5197
5968
|
return { status: 200, body: counts };
|
|
5198
5969
|
} catch (error) {
|
|
5199
5970
|
if (error instanceof WeakPassphraseError) {
|
|
5200
|
-
return
|
|
5971
|
+
return err4(400, error.message);
|
|
5201
5972
|
}
|
|
5202
|
-
return
|
|
5973
|
+
return err4(400, error instanceof Error ? error.message : "import failed");
|
|
5203
5974
|
}
|
|
5204
5975
|
}
|
|
5205
5976
|
|
|
5206
5977
|
// src/admin/usagePricing.ts
|
|
5207
5978
|
var import_usage = require("@omnicross/core/usage");
|
|
5208
|
-
var
|
|
5979
|
+
var err5 = (status, message) => ({
|
|
5209
5980
|
status,
|
|
5210
5981
|
body: { error: { type: "admin_api_error", message } }
|
|
5211
5982
|
});
|
|
@@ -5218,7 +5989,7 @@ function parseRange(query2) {
|
|
|
5218
5989
|
const startTs = parseFiniteInt(query2.get("startTs"));
|
|
5219
5990
|
const endTs = parseFiniteInt(query2.get("endTs"));
|
|
5220
5991
|
if (startTs === null || endTs === null) {
|
|
5221
|
-
return
|
|
5992
|
+
return err5(400, "startTs and endTs are required finite-integer unix-millis query params");
|
|
5222
5993
|
}
|
|
5223
5994
|
return { startTs, endTs };
|
|
5224
5995
|
}
|
|
@@ -5243,14 +6014,14 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
5243
6014
|
case "timeseries": {
|
|
5244
6015
|
const bucket = query2.get("bucket");
|
|
5245
6016
|
if (bucket !== "hour" && bucket !== "day" && bucket !== "month") {
|
|
5246
|
-
return
|
|
6017
|
+
return err5(400, "bucket must be one of 'hour', 'day', 'month'");
|
|
5247
6018
|
}
|
|
5248
6019
|
const now = Date.now();
|
|
5249
6020
|
const clamped = { startTs: range.startTs, endTs: Math.min(range.endTs, now) };
|
|
5250
6021
|
if (clamped.startTs < clamped.endTs) {
|
|
5251
6022
|
const projected = Math.ceil((clamped.endTs - clamped.startTs) / BUCKET_SPAN_MS[bucket]) + 1;
|
|
5252
6023
|
if (projected > MAX_TIMESERIES_BUCKETS) {
|
|
5253
|
-
return
|
|
6024
|
+
return err5(
|
|
5254
6025
|
400,
|
|
5255
6026
|
`requested range projects ~${projected} '${bucket}' buckets (max ${MAX_TIMESERIES_BUCKETS}); narrow the range or use a coarser bucket`
|
|
5256
6027
|
);
|
|
@@ -5273,7 +6044,7 @@ async function handleUsageGet(view, query2, deps) {
|
|
|
5273
6044
|
};
|
|
5274
6045
|
}
|
|
5275
6046
|
default:
|
|
5276
|
-
return
|
|
6047
|
+
return err5(404, `unknown usage view '${view ?? ""}'`);
|
|
5277
6048
|
}
|
|
5278
6049
|
}
|
|
5279
6050
|
function poolKeyLabels(cfg) {
|
|
@@ -5322,7 +6093,7 @@ async function handlePricingList(deps) {
|
|
|
5322
6093
|
async function handlePricingUpsert(body, deps) {
|
|
5323
6094
|
const input = parsePricingEntryInput(body);
|
|
5324
6095
|
if (!input) {
|
|
5325
|
-
return
|
|
6096
|
+
return err5(400, "invalid pricing entry (providerId, modelId, finite numeric inputPricePer1m/outputPricePer1m required)");
|
|
5326
6097
|
}
|
|
5327
6098
|
const entry = await deps.pricingEngine.upsertManual(input);
|
|
5328
6099
|
return { status: 200, body: { entry } };
|
|
@@ -5331,7 +6102,7 @@ async function handlePricingDelete(query2, deps) {
|
|
|
5331
6102
|
const providerId = query2.get("providerId")?.trim() ?? "";
|
|
5332
6103
|
const modelId = query2.get("modelId")?.trim() ?? "";
|
|
5333
6104
|
if (!providerId || !modelId) {
|
|
5334
|
-
return
|
|
6105
|
+
return err5(400, "delete requires providerId and modelId query params");
|
|
5335
6106
|
}
|
|
5336
6107
|
const deleted = await deps.pricingStore.delete(providerId, modelId);
|
|
5337
6108
|
if (deleted) await deps.pricingEngine.invalidateCache();
|
|
@@ -5351,13 +6122,13 @@ async function handlePricingFetchLatest(deps) {
|
|
|
5351
6122
|
}
|
|
5352
6123
|
};
|
|
5353
6124
|
} catch (e) {
|
|
5354
|
-
return
|
|
6125
|
+
return err5(502, `pricing-source fetch failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
5355
6126
|
}
|
|
5356
6127
|
}
|
|
5357
6128
|
async function handlePricingResolveConflicts(body, deps) {
|
|
5358
6129
|
const raw = body["resolutions"];
|
|
5359
6130
|
if (!Array.isArray(raw)) {
|
|
5360
|
-
return
|
|
6131
|
+
return err5(400, "resolve-conflicts requires { resolutions: [...] }");
|
|
5361
6132
|
}
|
|
5362
6133
|
const currentRows = await deps.pricingStore.getAll();
|
|
5363
6134
|
const userEditedKeys = new Set(
|
|
@@ -5367,21 +6138,21 @@ async function handlePricingResolveConflicts(body, deps) {
|
|
|
5367
6138
|
const pendingIncoming = /* @__PURE__ */ new Map();
|
|
5368
6139
|
let staleCount = 0;
|
|
5369
6140
|
for (const item of raw) {
|
|
5370
|
-
if (!item || typeof item !== "object") return
|
|
6141
|
+
if (!item || typeof item !== "object") return err5(400, "invalid resolution entry");
|
|
5371
6142
|
const r = item;
|
|
5372
6143
|
const action = r["action"];
|
|
5373
6144
|
if (action !== "overwrite" && action !== "skip") {
|
|
5374
|
-
return
|
|
6145
|
+
return err5(400, "resolution action must be 'overwrite' or 'skip'");
|
|
5375
6146
|
}
|
|
5376
6147
|
const providerId = typeof r["providerId"] === "string" && r["providerId"].trim() ? r["providerId"].trim() : "";
|
|
5377
6148
|
const modelId = typeof r["modelId"] === "string" && r["modelId"].trim() ? r["modelId"].trim() : "";
|
|
5378
6149
|
if (!providerId || !modelId) {
|
|
5379
|
-
return
|
|
6150
|
+
return err5(400, "each resolution requires top-level providerId and modelId");
|
|
5380
6151
|
}
|
|
5381
6152
|
const incoming = parsePricingEntryInput(r["incoming"]);
|
|
5382
|
-
if (!incoming) return
|
|
6153
|
+
if (!incoming) return err5(400, "each resolution must echo a valid incoming pricing entry");
|
|
5383
6154
|
if (incoming.providerId !== providerId || incoming.modelId !== modelId) {
|
|
5384
|
-
return
|
|
6155
|
+
return err5(400, "resolution providerId/modelId must match the echoed incoming entry");
|
|
5385
6156
|
}
|
|
5386
6157
|
const key = `${providerId}::${modelId}`;
|
|
5387
6158
|
if (action === "overwrite" && !userEditedKeys.has(key)) {
|
|
@@ -5426,7 +6197,7 @@ function query(req) {
|
|
|
5426
6197
|
}
|
|
5427
6198
|
function allowanceProvider(value) {
|
|
5428
6199
|
if (!value) return void 0;
|
|
5429
|
-
return value === "claude" || value === "codex" ? value : null;
|
|
6200
|
+
return value === "claude" || value === "codex" || value === "kimi" || value === "opencodego" ? value : null;
|
|
5430
6201
|
}
|
|
5431
6202
|
async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
5432
6203
|
if (!service) return writeError2(res, 501, "account allowance service is not available");
|
|
@@ -5440,7 +6211,9 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
5440
6211
|
const params = query(req);
|
|
5441
6212
|
const pathProvider = rest.length >= 2 ? rest[0] : null;
|
|
5442
6213
|
const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
|
|
5443
|
-
if (providerId === null)
|
|
6214
|
+
if (providerId === null) {
|
|
6215
|
+
return writeError2(res, 400, "providerId must be claude, codex, kimi, or opencodego");
|
|
6216
|
+
}
|
|
5444
6217
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
5445
6218
|
const allowances = await service.list({ providerId, accountId });
|
|
5446
6219
|
return writeJson3(res, 200, { allowances });
|
|
@@ -5450,10 +6223,37 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
5450
6223
|
const requestedProvider = allowanceProvider(
|
|
5451
6224
|
typeof body["providerId"] === "string" ? body["providerId"] : "claude"
|
|
5452
6225
|
);
|
|
5453
|
-
if (requestedProvider !== "claude") {
|
|
5454
|
-
return writeError2(res, 400, "only Claude allowances support explicit refresh");
|
|
5455
|
-
}
|
|
5456
6226
|
const accountId = typeof body["accountId"] === "string" && body["accountId"].trim() ? body["accountId"].trim() : void 0;
|
|
6227
|
+
if (requestedProvider === "codex") {
|
|
6228
|
+
if (!service.refreshCodex) {
|
|
6229
|
+
return writeError2(res, 501, "codex allowance refresh is not available");
|
|
6230
|
+
}
|
|
6231
|
+
const allowances2 = await service.refreshCodex(accountId);
|
|
6232
|
+
if (accountId && allowances2.length === 0) {
|
|
6233
|
+
return writeError2(res, 404, `Codex account '${accountId}' not found`);
|
|
6234
|
+
}
|
|
6235
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
6236
|
+
}
|
|
6237
|
+
if (requestedProvider === "kimi") {
|
|
6238
|
+
if (!service.refreshKimi) {
|
|
6239
|
+
return writeError2(res, 501, "kimi allowance refresh is not available");
|
|
6240
|
+
}
|
|
6241
|
+
const allowances2 = await service.refreshKimi(accountId);
|
|
6242
|
+
if (accountId && allowances2.length === 0) {
|
|
6243
|
+
return writeError2(res, 404, `Kimi account '${accountId}' not found`);
|
|
6244
|
+
}
|
|
6245
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
6246
|
+
}
|
|
6247
|
+
if (requestedProvider === "opencodego") {
|
|
6248
|
+
if (!service.refreshOpenCodeGo) {
|
|
6249
|
+
return writeError2(res, 501, "opencodego allowance refresh is not available");
|
|
6250
|
+
}
|
|
6251
|
+
const allowances2 = await service.refreshOpenCodeGo(accountId);
|
|
6252
|
+
if (accountId && allowances2.length === 0) {
|
|
6253
|
+
return writeError2(res, 404, `OpenCodeGo account '${accountId}' not found`);
|
|
6254
|
+
}
|
|
6255
|
+
return writeJson3(res, 200, { allowances: allowances2 });
|
|
6256
|
+
}
|
|
5457
6257
|
const allowances = await service.refreshClaude(accountId);
|
|
5458
6258
|
if (accountId && allowances.length === 0) {
|
|
5459
6259
|
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
@@ -5621,8 +6421,8 @@ async function handleAdminApi(req, res, path2, deps) {
|
|
|
5621
6421
|
default:
|
|
5622
6422
|
return writeJsonError(res, 404, `unknown admin resource '${resource}'`);
|
|
5623
6423
|
}
|
|
5624
|
-
} catch (
|
|
5625
|
-
writeJsonError(res, 500,
|
|
6424
|
+
} catch (err6) {
|
|
6425
|
+
writeJsonError(res, 500, err6 instanceof Error ? err6.message : String(err6));
|
|
5626
6426
|
}
|
|
5627
6427
|
}
|
|
5628
6428
|
function requestQuery(req) {
|
|
@@ -5692,6 +6492,9 @@ async function handleProviders(req, res, method, rest, deps) {
|
|
|
5692
6492
|
if (method === "POST" && rest.length === 4 && rest[1] === "keys" && rest[3] === "enabled") {
|
|
5693
6493
|
return await handleToggleProviderKey(req, res, rest[0], rest[2], cfg, deps);
|
|
5694
6494
|
}
|
|
6495
|
+
if (method === "POST" && rest.length === 5 && rest[1] === "keys" && rest[3] === "quota" && rest[4] === "refresh") {
|
|
6496
|
+
return await handleProviderKeyQuotaRefresh(res, rest[0], rest[2], cfg, deps);
|
|
6497
|
+
}
|
|
5695
6498
|
if (method === "PUT" && rest.length === 3 && rest[1] === "keys") {
|
|
5696
6499
|
return await handleUpdateProviderKey(req, res, rest[0], rest[2], cfg, deps);
|
|
5697
6500
|
}
|
|
@@ -5790,7 +6593,7 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
5790
6593
|
try {
|
|
5791
6594
|
const headers = { Accept: "application/json" };
|
|
5792
6595
|
if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
|
|
5793
|
-
const response = await (0,
|
|
6596
|
+
const response = await (0, import_upstreamFetch7.fetchUpstream)(url, { method: "GET", headers }, { providerId: "byo" });
|
|
5794
6597
|
if (!response.ok) {
|
|
5795
6598
|
const text = await response.text().catch(() => "");
|
|
5796
6599
|
let message = text.slice(0, 300);
|
|
@@ -5807,8 +6610,8 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
5807
6610
|
const data = await response.json();
|
|
5808
6611
|
const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
|
|
5809
6612
|
return writeJson4(res, 200, { models });
|
|
5810
|
-
} catch (
|
|
5811
|
-
const message =
|
|
6613
|
+
} catch (err6) {
|
|
6614
|
+
const message = err6 instanceof Error ? err6.message : String(err6);
|
|
5812
6615
|
return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
|
|
5813
6616
|
}
|
|
5814
6617
|
}
|
|
@@ -5849,7 +6652,7 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
5849
6652
|
}
|
|
5850
6653
|
const startedAt = Date.now();
|
|
5851
6654
|
try {
|
|
5852
|
-
const response = await (0,
|
|
6655
|
+
const response = await (0, import_upstreamFetch7.fetchUpstream)(
|
|
5853
6656
|
url,
|
|
5854
6657
|
{ method: "POST", headers, body: JSON.stringify(payload) },
|
|
5855
6658
|
{ providerId: "byo" }
|
|
@@ -5871,8 +6674,8 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
5871
6674
|
latencyMs,
|
|
5872
6675
|
sample: extractSampleText(text, row.apiFormat)
|
|
5873
6676
|
});
|
|
5874
|
-
} catch (
|
|
5875
|
-
const message =
|
|
6677
|
+
} catch (err6) {
|
|
6678
|
+
const message = err6 instanceof Error ? err6.message : String(err6);
|
|
5876
6679
|
return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
|
|
5877
6680
|
}
|
|
5878
6681
|
}
|
|
@@ -5914,7 +6717,30 @@ async function handleProviderKeys(res, id, cfg, deps) {
|
|
|
5914
6717
|
const row = cfg.providers.find((p) => p.id === id);
|
|
5915
6718
|
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
5916
6719
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
5917
|
-
|
|
6720
|
+
const views = toPoolKeyView(row, cooldown, deps);
|
|
6721
|
+
if (deps.providerKeyQuota) {
|
|
6722
|
+
const quotas = await Promise.allSettled(
|
|
6723
|
+
views.map((view) => deps.providerKeyQuota.quotaFor(row, view.id))
|
|
6724
|
+
);
|
|
6725
|
+
views.forEach((view, index) => {
|
|
6726
|
+
const settled = quotas[index];
|
|
6727
|
+
if (settled.status === "fulfilled" && settled.value) view.quota = settled.value;
|
|
6728
|
+
});
|
|
6729
|
+
}
|
|
6730
|
+
return writeJson4(res, 200, { keys: views });
|
|
6731
|
+
}
|
|
6732
|
+
async function handleProviderKeyQuotaRefresh(res, id, keyId, cfg, deps) {
|
|
6733
|
+
if (!deps.providerKeyQuota) return writeJsonError(res, 501, "provider key quota is not available");
|
|
6734
|
+
if (!id || !keyId) return writeJsonError(res, 400, "provider id and key id required in path");
|
|
6735
|
+
const row = cfg.providers.find((p) => p.id === id);
|
|
6736
|
+
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
6737
|
+
try {
|
|
6738
|
+
const quota = await deps.providerKeyQuota.quotaFor(row, keyId, { force: true });
|
|
6739
|
+
if (!quota) return writeJsonError(res, 404, `no quota endpoint for key '${keyId}'`);
|
|
6740
|
+
return writeJson4(res, 200, { quota });
|
|
6741
|
+
} catch {
|
|
6742
|
+
return writeJsonError(res, 502, "quota refresh failed");
|
|
6743
|
+
}
|
|
5918
6744
|
}
|
|
5919
6745
|
function parsePoolKeyInput(body, existing) {
|
|
5920
6746
|
const out = {};
|
|
@@ -6659,12 +7485,12 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6659
7485
|
}
|
|
6660
7486
|
return writeJson4(res, 200, { ok: true, affected: result.affected });
|
|
6661
7487
|
}
|
|
6662
|
-
if (method === "GET" && rest[0] === "codex" && rest[1] === "oauth" && rest[3] === "status") {
|
|
6663
|
-
const result = handleCodexOAuthStatus(rest[2], deps);
|
|
7488
|
+
if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi") && rest[1] === "oauth" && rest[3] === "status") {
|
|
7489
|
+
const result = rest[0] === "codex" ? handleCodexOAuthStatus(rest[2], deps) : handleKimiOAuthStatus(rest[2], deps);
|
|
6664
7490
|
return writeJson4(res, result.status, result.body);
|
|
6665
7491
|
}
|
|
6666
|
-
if (method === "DELETE" && rest[0] === "codex" && rest[1] === "oauth" && rest[2]) {
|
|
6667
|
-
const result = handleCodexOAuthCancel(rest[2], deps);
|
|
7492
|
+
if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi") && rest[1] === "oauth" && rest[2]) {
|
|
7493
|
+
const result = rest[0] === "codex" ? handleCodexOAuthCancel(rest[2], deps) : handleKimiOAuthCancel(rest[2], deps);
|
|
6668
7494
|
return writeJson4(res, result.status, result.body);
|
|
6669
7495
|
}
|
|
6670
7496
|
if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
|
|
@@ -6717,7 +7543,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6717
7543
|
return writeJsonError(res, 400, `unknown subscription provider '${rest[0] ?? ""}'`);
|
|
6718
7544
|
}
|
|
6719
7545
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "start") {
|
|
6720
|
-
|
|
7546
|
+
if (providerId === "codex") {
|
|
7547
|
+
const result2 = handleCodexOAuthStart(deps);
|
|
7548
|
+
return writeJson4(res, result2.status, result2.body);
|
|
7549
|
+
}
|
|
7550
|
+
if (providerId === "kimi") {
|
|
7551
|
+
const result2 = await handleKimiOAuthStart(deps);
|
|
7552
|
+
return writeJson4(res, result2.status, result2.body);
|
|
7553
|
+
}
|
|
7554
|
+
const result = handleOAuthStart(providerId, deps);
|
|
6721
7555
|
return writeJson4(res, result.status, result.body);
|
|
6722
7556
|
}
|
|
6723
7557
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "complete") {
|
|
@@ -7211,12 +8045,12 @@ async function handlePlayground(req, res, method, deps) {
|
|
|
7211
8045
|
const payload = body["body"];
|
|
7212
8046
|
const status = deps.outboundApiServer.getStatus();
|
|
7213
8047
|
if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
|
|
7214
|
-
const path2 = resolvePlaygroundPath(endpoint,
|
|
8048
|
+
const path2 = resolvePlaygroundPath(endpoint, isRecord4(payload) ? payload : {});
|
|
7215
8049
|
if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
|
|
7216
8050
|
const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
|
|
7217
8051
|
await proxyToOutbound(res, status.port, path2, key, upstreamBody);
|
|
7218
8052
|
}
|
|
7219
|
-
function
|
|
8053
|
+
function isRecord4(v) {
|
|
7220
8054
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
7221
8055
|
}
|
|
7222
8056
|
function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
@@ -7245,8 +8079,8 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
|
7245
8079
|
});
|
|
7246
8080
|
}
|
|
7247
8081
|
);
|
|
7248
|
-
upstream.on("error", (
|
|
7249
|
-
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${
|
|
8082
|
+
upstream.on("error", (err6) => {
|
|
8083
|
+
if (!res.headersSent) writeJsonError(res, 502, `playground proxy failed: ${err6.message}`);
|
|
7250
8084
|
else res.end();
|
|
7251
8085
|
resolve10();
|
|
7252
8086
|
});
|
|
@@ -7352,7 +8186,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
7352
8186
|
}
|
|
7353
8187
|
|
|
7354
8188
|
// src/admin/version.ts
|
|
7355
|
-
var DAEMON_VERSION = true ? "0.3.
|
|
8189
|
+
var DAEMON_VERSION = true ? "0.3.1" : "0.0.0-dev";
|
|
7356
8190
|
|
|
7357
8191
|
// src/admin/AdminServer.ts
|
|
7358
8192
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -7395,13 +8229,13 @@ var AdminServer = class {
|
|
|
7395
8229
|
const server = import_node_http2.default.createServer((req, res) => {
|
|
7396
8230
|
this.onRequest(req, res);
|
|
7397
8231
|
});
|
|
7398
|
-
const onError = (
|
|
7399
|
-
if (
|
|
8232
|
+
const onError = (err6) => {
|
|
8233
|
+
if (err6.code === "EADDRINUSE" && port !== 0) {
|
|
7400
8234
|
server.removeListener("error", onError);
|
|
7401
8235
|
this.listen(bindAddr, 0).then(resolve10, reject);
|
|
7402
8236
|
return;
|
|
7403
8237
|
}
|
|
7404
|
-
reject(
|
|
8238
|
+
reject(err6);
|
|
7405
8239
|
};
|
|
7406
8240
|
server.on("error", onError);
|
|
7407
8241
|
server.listen(port, bindAddr, () => {
|
|
@@ -7419,8 +8253,8 @@ var AdminServer = class {
|
|
|
7419
8253
|
}
|
|
7420
8254
|
/** Per-request handler: auth gate (when a token is set) → routing. */
|
|
7421
8255
|
onRequest(req, res) {
|
|
7422
|
-
void this.dispatch(req, res).catch((
|
|
7423
|
-
const message =
|
|
8256
|
+
void this.dispatch(req, res).catch((err6) => {
|
|
8257
|
+
const message = err6 instanceof Error ? err6.message : String(err6);
|
|
7424
8258
|
this.deps.logger.error("[AdminServer] unhandled error:", message);
|
|
7425
8259
|
if (!res.headersSent) {
|
|
7426
8260
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
@@ -7684,18 +8518,18 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
7684
8518
|
return;
|
|
7685
8519
|
}
|
|
7686
8520
|
signal?.addEventListener("abort", abort, { once: true });
|
|
7687
|
-
server.on("error", (
|
|
8521
|
+
server.on("error", (err6) => {
|
|
7688
8522
|
if (settled) return;
|
|
7689
8523
|
settled = true;
|
|
7690
8524
|
clearTimeout(timer);
|
|
7691
|
-
if (
|
|
8525
|
+
if (err6.code === "EADDRINUSE") {
|
|
7692
8526
|
reject(
|
|
7693
8527
|
new Error(
|
|
7694
8528
|
`login: cannot bind ${LOOPBACK_HOST}:${LOOPBACK_PORT} (address in use) \u2014 another codex login or process is holding the port`
|
|
7695
8529
|
)
|
|
7696
8530
|
);
|
|
7697
8531
|
} else {
|
|
7698
|
-
reject(
|
|
8532
|
+
reject(err6);
|
|
7699
8533
|
}
|
|
7700
8534
|
});
|
|
7701
8535
|
const timer = setTimeout(() => {
|
|
@@ -7770,6 +8604,411 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
|
|
|
7770
8604
|
};
|
|
7771
8605
|
}
|
|
7772
8606
|
|
|
8607
|
+
// src/allowance/ProviderKeyQuotaService.ts
|
|
8608
|
+
var import_upstreamFetch8 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
8609
|
+
|
|
8610
|
+
// src/allowance/ProviderKeyQuota.ts
|
|
8611
|
+
var MINUTE_MS2 = 6e4;
|
|
8612
|
+
var HOUR_MS2 = 60 * MINUTE_MS2;
|
|
8613
|
+
var DAY_MS2 = 24 * HOUR_MS2;
|
|
8614
|
+
var WEEK_MS = 7 * DAY_MS2;
|
|
8615
|
+
var MONTH_MS = 30 * DAY_MS2;
|
|
8616
|
+
function finiteNumber3(value) {
|
|
8617
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
8618
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
8619
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
8620
|
+
}
|
|
8621
|
+
function finitePercent4(value) {
|
|
8622
|
+
const parsed = finiteNumber3(value);
|
|
8623
|
+
return parsed !== void 0 && parsed <= 100 ? parsed : null;
|
|
8624
|
+
}
|
|
8625
|
+
function isoInstant3(value) {
|
|
8626
|
+
if (typeof value === "string" && value.trim()) {
|
|
8627
|
+
const time = Date.parse(value);
|
|
8628
|
+
if (Number.isFinite(time)) return new Date(time).toISOString();
|
|
8629
|
+
}
|
|
8630
|
+
const numeric = finiteNumber3(value);
|
|
8631
|
+
if (numeric !== void 0 && numeric > 1e9) {
|
|
8632
|
+
const ms = numeric > 1e12 ? numeric : numeric * 1e3;
|
|
8633
|
+
return new Date(ms).toISOString();
|
|
8634
|
+
}
|
|
8635
|
+
return void 0;
|
|
8636
|
+
}
|
|
8637
|
+
function secondsUntil5(instant, now) {
|
|
8638
|
+
if (!instant) return void 0;
|
|
8639
|
+
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
8640
|
+
}
|
|
8641
|
+
function isRecord5(value) {
|
|
8642
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
8643
|
+
}
|
|
8644
|
+
function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
8645
|
+
if (!baseUrl) return null;
|
|
8646
|
+
let url;
|
|
8647
|
+
try {
|
|
8648
|
+
url = new URL(baseUrl);
|
|
8649
|
+
} catch {
|
|
8650
|
+
return null;
|
|
8651
|
+
}
|
|
8652
|
+
const host = url.hostname.toLowerCase();
|
|
8653
|
+
const path2 = url.pathname.toLowerCase();
|
|
8654
|
+
if ((host === "api.z.ai" || host === "open.bigmodel.cn") && path2.includes("/coding")) {
|
|
8655
|
+
return "zai";
|
|
8656
|
+
}
|
|
8657
|
+
if ((host === "api.minimax.io" || host === "api.minimaxi.com") && // Token Plan rides the plain openai `/v1` (chat completions) surface; the
|
|
8658
|
+
// anthropic `/anthropic` rows are excluded (their usage impl is unverified).
|
|
8659
|
+
(path2 === "/v1" || path2 === "/v1/" || path2 === "" || path2 === "/")) {
|
|
8660
|
+
return "minimax-token-plan";
|
|
8661
|
+
}
|
|
8662
|
+
if (host === "api.code.umans.ai") return "umans";
|
|
8663
|
+
if (host === "api.synthetic.new") return "synthetic";
|
|
8664
|
+
return null;
|
|
8665
|
+
}
|
|
8666
|
+
function providerKeyQuotaUrl(adapter, baseUrl) {
|
|
8667
|
+
const origin = new URL(baseUrl).origin;
|
|
8668
|
+
if (adapter === "zai") return `${origin}/api/monitor/usage/quota/limit`;
|
|
8669
|
+
if (adapter === "minimax-token-plan") return `${origin}/v1/token_plan/remains`;
|
|
8670
|
+
if (adapter === "umans") return `${origin}/v1/usage`;
|
|
8671
|
+
return `${origin}/v2/quotas`;
|
|
8672
|
+
}
|
|
8673
|
+
function providerKeyQuotaAuthHeader(adapter, key) {
|
|
8674
|
+
return adapter === "zai" ? key : `Bearer ${key}`;
|
|
8675
|
+
}
|
|
8676
|
+
function zaiWindowDurationMs(item) {
|
|
8677
|
+
const count = item.number !== void 0 && item.number > 0 ? item.number : 1;
|
|
8678
|
+
switch (item.unit) {
|
|
8679
|
+
case 3:
|
|
8680
|
+
return count * HOUR_MS2;
|
|
8681
|
+
case 4:
|
|
8682
|
+
return count * DAY_MS2;
|
|
8683
|
+
case 5:
|
|
8684
|
+
return count * MONTH_MS;
|
|
8685
|
+
case 6:
|
|
8686
|
+
return WEEK_MS;
|
|
8687
|
+
default:
|
|
8688
|
+
return void 0;
|
|
8689
|
+
}
|
|
8690
|
+
}
|
|
8691
|
+
function zaiWindowIdLabel(durationMs) {
|
|
8692
|
+
if (durationMs === WEEK_MS) return { id: "seven-day", label: "7 days" };
|
|
8693
|
+
if (durationMs === 5 * HOUR_MS2) return { id: "five-hour", label: "5 hours" };
|
|
8694
|
+
if (durationMs === MONTH_MS) return { id: "thirty-day", label: "30 days" };
|
|
8695
|
+
if (durationMs !== void 0 && durationMs % DAY_MS2 === 0) {
|
|
8696
|
+
const days = durationMs / DAY_MS2;
|
|
8697
|
+
return { id: `${days}d`, label: `${days} day${days === 1 ? "" : "s"}` };
|
|
8698
|
+
}
|
|
8699
|
+
if (durationMs !== void 0 && durationMs % HOUR_MS2 === 0) {
|
|
8700
|
+
const hours = durationMs / HOUR_MS2;
|
|
8701
|
+
return { id: `${hours}h`, label: `${hours} hour${hours === 1 ? "" : "s"}` };
|
|
8702
|
+
}
|
|
8703
|
+
return { id: "quota", label: "Quota" };
|
|
8704
|
+
}
|
|
8705
|
+
function parseZaiQuotaPayload(payload, now) {
|
|
8706
|
+
if (!isRecord5(payload)) return null;
|
|
8707
|
+
const data = isRecord5(payload["data"]) ? payload["data"] : payload;
|
|
8708
|
+
if (payload["success"] === false) return null;
|
|
8709
|
+
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
8710
|
+
const byWindow = /* @__PURE__ */ new Map();
|
|
8711
|
+
for (const raw of limits) {
|
|
8712
|
+
if (!isRecord5(raw)) continue;
|
|
8713
|
+
const item = raw;
|
|
8714
|
+
if (item.type === void 0) continue;
|
|
8715
|
+
const details = raw["usageDetails"];
|
|
8716
|
+
if (Array.isArray(details) && details.some((d) => isRecord5(d) && d["modelCode"] === "zread")) {
|
|
8717
|
+
continue;
|
|
8718
|
+
}
|
|
8719
|
+
const durationMs = zaiWindowDurationMs(item);
|
|
8720
|
+
const { id, label } = zaiWindowIdLabel(durationMs);
|
|
8721
|
+
const limit = finiteNumber3(item.usage);
|
|
8722
|
+
const used = finiteNumber3(item.currentValue);
|
|
8723
|
+
const fromAbsolute = limit !== void 0 && used !== void 0 && limit > 0 ? Math.min(100, used / limit * 100) : void 0;
|
|
8724
|
+
const fromPercentage = finitePercent4(item.percentage) ?? void 0;
|
|
8725
|
+
const usedPercent = fromAbsolute !== void 0 ? Math.round(fromAbsolute * 10) / 10 : fromPercentage;
|
|
8726
|
+
if (usedPercent === void 0) continue;
|
|
8727
|
+
const resetsAt = isoInstant3(item.nextResetTime);
|
|
8728
|
+
const candidate = {
|
|
8729
|
+
id,
|
|
8730
|
+
label,
|
|
8731
|
+
scope: "all",
|
|
8732
|
+
usedPercent,
|
|
8733
|
+
...durationMs !== void 0 ? { windowMinutes: Math.round(durationMs / MINUTE_MS2) } : {},
|
|
8734
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
8735
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
8736
|
+
state: "fresh"
|
|
8737
|
+
};
|
|
8738
|
+
const existing = byWindow.get(id);
|
|
8739
|
+
if (!existing || (candidate.usedPercent ?? 0) > (existing.usedPercent ?? 0)) {
|
|
8740
|
+
byWindow.set(id, candidate);
|
|
8741
|
+
}
|
|
8742
|
+
}
|
|
8743
|
+
const windows = [...byWindow.values()].sort((a, b) => (a.windowMinutes ?? Number.POSITIVE_INFINITY) - (b.windowMinutes ?? Number.POSITIVE_INFINITY));
|
|
8744
|
+
return windows.length > 0 ? windows.slice(0, 4) : null;
|
|
8745
|
+
}
|
|
8746
|
+
var MINIMAX_STATUS_EXHAUSTED = 2;
|
|
8747
|
+
var MINIMAX_SHARED_BUCKET = "general";
|
|
8748
|
+
function parseMiniMaxBucket(value) {
|
|
8749
|
+
if (!isRecord5(value)) return null;
|
|
8750
|
+
const modelName = typeof value["model_name"] === "string" ? value["model_name"].trim() : "";
|
|
8751
|
+
if (!modelName) return null;
|
|
8752
|
+
const instant = (v) => {
|
|
8753
|
+
const n = finiteNumber3(v);
|
|
8754
|
+
return n !== void 0 && n > 1e9 ? n > 1e12 ? n : n * 1e3 : void 0;
|
|
8755
|
+
};
|
|
8756
|
+
return {
|
|
8757
|
+
modelName,
|
|
8758
|
+
intervalEnd: instant(value["end_time"]),
|
|
8759
|
+
intervalRemainingPercent: finiteNumber3(value["current_interval_remaining_percent"]),
|
|
8760
|
+
intervalStatus: finiteNumber3(value["current_interval_status"]),
|
|
8761
|
+
weeklyEnd: instant(value["weekly_end_time"]),
|
|
8762
|
+
weeklyRemainingPercent: finiteNumber3(value["current_weekly_remaining_percent"]),
|
|
8763
|
+
weeklyStatus: finiteNumber3(value["current_weekly_status"])
|
|
8764
|
+
};
|
|
8765
|
+
}
|
|
8766
|
+
function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, status, now) {
|
|
8767
|
+
const usedPercent = status === MINIMAX_STATUS_EXHAUSTED ? 100 : remainingPercent !== void 0 ? Math.round((100 - remainingPercent) * 10) / 10 : null;
|
|
8768
|
+
const resetsAt = resetsAtMs !== void 0 ? new Date(resetsAtMs).toISOString() : void 0;
|
|
8769
|
+
return {
|
|
8770
|
+
id,
|
|
8771
|
+
label,
|
|
8772
|
+
scope: "all",
|
|
8773
|
+
usedPercent,
|
|
8774
|
+
...windowMinutes !== void 0 ? { windowMinutes } : {},
|
|
8775
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
8776
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
8777
|
+
state: usedPercent !== null ? "fresh" : "unavailable"
|
|
8778
|
+
};
|
|
8779
|
+
}
|
|
8780
|
+
function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
8781
|
+
if (!isRecord5(payload)) return null;
|
|
8782
|
+
const baseResp = payload["base_resp"];
|
|
8783
|
+
if (!isRecord5(baseResp) || baseResp["status_code"] !== 0) return null;
|
|
8784
|
+
const buckets = Array.isArray(payload["model_remains"]) ? payload["model_remains"] : [];
|
|
8785
|
+
let general = null;
|
|
8786
|
+
for (const raw of buckets) {
|
|
8787
|
+
const bucket = parseMiniMaxBucket(raw);
|
|
8788
|
+
if (bucket?.modelName === MINIMAX_SHARED_BUCKET) {
|
|
8789
|
+
general = bucket;
|
|
8790
|
+
break;
|
|
8791
|
+
}
|
|
8792
|
+
}
|
|
8793
|
+
if (!general) return null;
|
|
8794
|
+
return [
|
|
8795
|
+
minimaxWindow(
|
|
8796
|
+
"five-hour",
|
|
8797
|
+
"5 hours",
|
|
8798
|
+
5 * 60,
|
|
8799
|
+
general.intervalEnd,
|
|
8800
|
+
general.intervalRemainingPercent,
|
|
8801
|
+
general.intervalStatus,
|
|
8802
|
+
now
|
|
8803
|
+
),
|
|
8804
|
+
minimaxWindow(
|
|
8805
|
+
"seven-day",
|
|
8806
|
+
"7 days",
|
|
8807
|
+
Math.round(WEEK_MS / MINUTE_MS2),
|
|
8808
|
+
general.weeklyEnd,
|
|
8809
|
+
general.weeklyRemainingPercent,
|
|
8810
|
+
general.weeklyStatus,
|
|
8811
|
+
now
|
|
8812
|
+
)
|
|
8813
|
+
];
|
|
8814
|
+
}
|
|
8815
|
+
function parseUmansUsagePayload(payload, now) {
|
|
8816
|
+
if (!isRecord5(payload)) return null;
|
|
8817
|
+
const limits = isRecord5(payload["limits"]) ? payload["limits"] : void 0;
|
|
8818
|
+
const requests = limits && isRecord5(limits["requests"]) ? limits["requests"] : void 0;
|
|
8819
|
+
const usage = isRecord5(payload["usage"]) ? payload["usage"] : void 0;
|
|
8820
|
+
const window = isRecord5(payload["window"]) ? payload["window"] : void 0;
|
|
8821
|
+
const hardCap = finiteNumber3(requests?.["hard_cap"]);
|
|
8822
|
+
const softLimit = finiteNumber3(requests?.["limit"]);
|
|
8823
|
+
const requestsInWindow = finiteNumber3(usage?.["requests_in_window"]);
|
|
8824
|
+
const weightedInWindow = finiteNumber3(usage?.["weighted_in_window"]);
|
|
8825
|
+
const resetsAt = isoInstant3(window?.["resets_at"]);
|
|
8826
|
+
let usedPercent = null;
|
|
8827
|
+
if (hardCap !== void 0 && hardCap > 0 && requestsInWindow !== void 0) {
|
|
8828
|
+
usedPercent = Math.round(Math.min(100, requestsInWindow / hardCap * 100) * 10) / 10;
|
|
8829
|
+
} else if (softLimit !== void 0 && softLimit > 0 && weightedInWindow !== void 0) {
|
|
8830
|
+
usedPercent = Math.round(Math.min(100, weightedInWindow / softLimit * 100) * 10) / 10;
|
|
8831
|
+
}
|
|
8832
|
+
if (usedPercent === null && resetsAt === void 0) return null;
|
|
8833
|
+
return [
|
|
8834
|
+
{
|
|
8835
|
+
id: "five-hour",
|
|
8836
|
+
label: "5 hours",
|
|
8837
|
+
scope: "all",
|
|
8838
|
+
usedPercent,
|
|
8839
|
+
windowMinutes: 5 * 60,
|
|
8840
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
8841
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
8842
|
+
state: "fresh"
|
|
8843
|
+
}
|
|
8844
|
+
];
|
|
8845
|
+
}
|
|
8846
|
+
function parseSyntheticQuotasPayload(payload, now) {
|
|
8847
|
+
if (!isRecord5(payload)) return null;
|
|
8848
|
+
const fiveHour = isRecord5(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
|
|
8849
|
+
const weekly = isRecord5(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
|
|
8850
|
+
const windows = [];
|
|
8851
|
+
if (fiveHour) {
|
|
8852
|
+
const max = finiteNumber3(fiveHour["max"]);
|
|
8853
|
+
const remaining = finiteNumber3(fiveHour["remaining"]);
|
|
8854
|
+
const usedPercent = max !== void 0 && max > 0 && remaining !== void 0 ? Math.round(Math.min(100, (max - remaining) / max * 100) * 10) / 10 : null;
|
|
8855
|
+
const resetsAt = isoInstant3(fiveHour["nextTickAt"]);
|
|
8856
|
+
windows.push({
|
|
8857
|
+
id: "five-hour",
|
|
8858
|
+
label: "5 hours",
|
|
8859
|
+
scope: "all",
|
|
8860
|
+
usedPercent,
|
|
8861
|
+
windowMinutes: 5 * 60,
|
|
8862
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
8863
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
8864
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
8865
|
+
});
|
|
8866
|
+
}
|
|
8867
|
+
if (weekly) {
|
|
8868
|
+
const percentRemaining = finiteNumber3(weekly["percentRemaining"]);
|
|
8869
|
+
const usedPercent = percentRemaining !== void 0 ? Math.round(Math.min(100, Math.max(0, 100 - percentRemaining)) * 10) / 10 : null;
|
|
8870
|
+
const resetsAt = isoInstant3(weekly["nextRegenAt"]);
|
|
8871
|
+
windows.push({
|
|
8872
|
+
id: "seven-day",
|
|
8873
|
+
label: "7 days",
|
|
8874
|
+
scope: "all",
|
|
8875
|
+
usedPercent,
|
|
8876
|
+
windowMinutes: 7 * 24 * 60,
|
|
8877
|
+
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
8878
|
+
remainingSeconds: secondsUntil5(resetsAt, now),
|
|
8879
|
+
state: usedPercent !== null || resetsAt ? "fresh" : "unavailable"
|
|
8880
|
+
});
|
|
8881
|
+
}
|
|
8882
|
+
return windows.length > 0 ? windows : null;
|
|
8883
|
+
}
|
|
8884
|
+
|
|
8885
|
+
// src/allowance/ProviderKeyQuotaService.ts
|
|
8886
|
+
function parseQuotaPayload(adapter, payload, now) {
|
|
8887
|
+
switch (adapter) {
|
|
8888
|
+
case "zai":
|
|
8889
|
+
return parseZaiQuotaPayload(payload, now);
|
|
8890
|
+
case "minimax-token-plan":
|
|
8891
|
+
return parseMiniMaxTokenPlanPayload(payload, now);
|
|
8892
|
+
case "umans":
|
|
8893
|
+
return parseUmansUsagePayload(payload, now);
|
|
8894
|
+
case "synthetic":
|
|
8895
|
+
return parseSyntheticQuotasPayload(payload, now);
|
|
8896
|
+
}
|
|
8897
|
+
}
|
|
8898
|
+
var PROVIDER_KEY_QUOTA_CACHE_MS = 5 * 6e4;
|
|
8899
|
+
function resolvedBaseUrl(row) {
|
|
8900
|
+
const modes = row.apiModes ?? [];
|
|
8901
|
+
const selected = row.selectedApiModeId ? modes.find((mode) => mode.id === row.selectedApiModeId) : void 0;
|
|
8902
|
+
const fallback = modes[0];
|
|
8903
|
+
const modeBase = selected?.baseUrl ?? fallback?.baseUrl;
|
|
8904
|
+
return modeBase ?? row.codingPlan?.baseUrl ?? row.baseUrl;
|
|
8905
|
+
}
|
|
8906
|
+
function rowKeyEntries(row) {
|
|
8907
|
+
const pool = (row.apiKeys ?? []).filter((entry) => entry.apiKey.length > 0);
|
|
8908
|
+
if (pool.length > 0) return pool.map((entry) => ({ id: entry.id, apiKey: entry.apiKey }));
|
|
8909
|
+
if (row.apiKey.length > 0) {
|
|
8910
|
+
return [{ id: `${row.id}:default`, apiKey: row.apiKey }];
|
|
8911
|
+
}
|
|
8912
|
+
return [];
|
|
8913
|
+
}
|
|
8914
|
+
var ProviderKeyQuotaService = class {
|
|
8915
|
+
constructor(box, fetchImpl = (url, init) => (0, import_upstreamFetch8.fetchUpstream)(url, init, { redactBodies: true }), now = Date.now) {
|
|
8916
|
+
this.box = box;
|
|
8917
|
+
this.fetchImpl = fetchImpl;
|
|
8918
|
+
this.now = now;
|
|
8919
|
+
}
|
|
8920
|
+
box;
|
|
8921
|
+
fetchImpl;
|
|
8922
|
+
now;
|
|
8923
|
+
cache = /* @__PURE__ */ new Map();
|
|
8924
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
8925
|
+
/**
|
|
8926
|
+
* Quota for one key of a provider row, or `null` when the row has no quota
|
|
8927
|
+
* adapter / no such key. Cache-first; concurrent reads share one flight.
|
|
8928
|
+
*/
|
|
8929
|
+
async quotaFor(row, keyId, options = {}) {
|
|
8930
|
+
const adapter = detectProviderKeyQuotaAdapter(resolvedBaseUrl(row));
|
|
8931
|
+
if (!adapter) return null;
|
|
8932
|
+
const entry = rowKeyEntries(row).find((candidate) => candidate.id === keyId);
|
|
8933
|
+
if (!entry) return null;
|
|
8934
|
+
const cacheKey = `${row.id}\0${keyId}`;
|
|
8935
|
+
const now = this.now();
|
|
8936
|
+
const cached = this.cache.get(cacheKey);
|
|
8937
|
+
if (!options.force && cached && Date.parse(cached.expiresAt) > now) return cached;
|
|
8938
|
+
const running = this.inFlight.get(cacheKey);
|
|
8939
|
+
if (running) return running;
|
|
8940
|
+
const promise = this.fetchQuota(adapter, row, entry.apiKey, cacheKey).catch((error) => {
|
|
8941
|
+
void error;
|
|
8942
|
+
const previous = this.cache.get(cacheKey);
|
|
8943
|
+
if (previous) {
|
|
8944
|
+
const degraded = {
|
|
8945
|
+
...previous,
|
|
8946
|
+
expiresAt: new Date(now + PROVIDER_KEY_QUOTA_CACHE_MS).toISOString(),
|
|
8947
|
+
windows: previous.windows.map((window) => ({
|
|
8948
|
+
...window,
|
|
8949
|
+
state: window.usedPercent !== null || window.resetsAt ? "stale" : window.state
|
|
8950
|
+
})),
|
|
8951
|
+
errorCode: "quota_request_failed"
|
|
8952
|
+
};
|
|
8953
|
+
this.cache.set(cacheKey, degraded);
|
|
8954
|
+
return degraded;
|
|
8955
|
+
}
|
|
8956
|
+
return null;
|
|
8957
|
+
}).finally(() => this.inFlight.delete(cacheKey));
|
|
8958
|
+
this.inFlight.set(cacheKey, promise);
|
|
8959
|
+
return promise;
|
|
8960
|
+
}
|
|
8961
|
+
/** Drop cached rows for a provider (key added/removed/rotated). */
|
|
8962
|
+
invalidateProvider(providerRowId) {
|
|
8963
|
+
for (const key of this.cache.keys()) {
|
|
8964
|
+
if (key.startsWith(`${providerRowId}\0`)) this.cache.delete(key);
|
|
8965
|
+
}
|
|
8966
|
+
}
|
|
8967
|
+
async fetchQuota(adapter, row, rawKey, cacheKey) {
|
|
8968
|
+
const baseUrl = resolvedBaseUrl(row);
|
|
8969
|
+
const url = providerKeyQuotaUrl(adapter, baseUrl);
|
|
8970
|
+
const key = this.box.decryptMaybe(rawKey);
|
|
8971
|
+
const now = this.now();
|
|
8972
|
+
const response = await this.fetchImpl(url, {
|
|
8973
|
+
method: "GET",
|
|
8974
|
+
headers: {
|
|
8975
|
+
Authorization: providerKeyQuotaAuthHeader(adapter, key),
|
|
8976
|
+
Accept: "application/json",
|
|
8977
|
+
"Content-Type": "application/json"
|
|
8978
|
+
},
|
|
8979
|
+
signal: AbortSignal.timeout(15e3)
|
|
8980
|
+
});
|
|
8981
|
+
if (response.status === 401 || response.status === 403) {
|
|
8982
|
+
const snapshot2 = {
|
|
8983
|
+
adapter,
|
|
8984
|
+
observedAt: new Date(now).toISOString(),
|
|
8985
|
+
expiresAt: new Date(now + PROVIDER_KEY_QUOTA_CACHE_MS).toISOString(),
|
|
8986
|
+
windows: [],
|
|
8987
|
+
errorCode: "quota_unauthorized"
|
|
8988
|
+
};
|
|
8989
|
+
this.cache.set(cacheKey, snapshot2);
|
|
8990
|
+
return snapshot2;
|
|
8991
|
+
}
|
|
8992
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
8993
|
+
let payload;
|
|
8994
|
+
try {
|
|
8995
|
+
payload = await response.json();
|
|
8996
|
+
} catch {
|
|
8997
|
+
throw new Error("invalid JSON");
|
|
8998
|
+
}
|
|
8999
|
+
const windows = parseQuotaPayload(adapter, payload, now);
|
|
9000
|
+
const snapshot = {
|
|
9001
|
+
adapter,
|
|
9002
|
+
observedAt: new Date(now).toISOString(),
|
|
9003
|
+
expiresAt: new Date(now + PROVIDER_KEY_QUOTA_CACHE_MS).toISOString(),
|
|
9004
|
+
windows: windows ?? [],
|
|
9005
|
+
...windows ? {} : { errorCode: "quota_unavailable" }
|
|
9006
|
+
};
|
|
9007
|
+
this.cache.set(cacheKey, snapshot);
|
|
9008
|
+
return snapshot;
|
|
9009
|
+
}
|
|
9010
|
+
};
|
|
9011
|
+
|
|
7773
9012
|
// src/commands/paths.ts
|
|
7774
9013
|
var import_node_path9 = require("path");
|
|
7775
9014
|
function defaultVouchersPath(configPath) {
|
|
@@ -7806,7 +9045,7 @@ function defaultBillingDir(configPath) {
|
|
|
7806
9045
|
// src/image-generation/ImageDoctorService.ts
|
|
7807
9046
|
var import_image_generation = require("@omnicross/core/image-generation");
|
|
7808
9047
|
var import_outbound_api7 = require("@omnicross/core/outbound-api");
|
|
7809
|
-
var
|
|
9048
|
+
var import_subscriptions6 = require("@omnicross/subscriptions");
|
|
7810
9049
|
|
|
7811
9050
|
// src/image-generation/FileCodexImageCapabilityEvidenceSource.ts
|
|
7812
9051
|
var import_node_crypto13 = require("crypto");
|
|
@@ -8244,7 +9483,7 @@ function createImageDoctorService(options) {
|
|
|
8244
9483
|
paths,
|
|
8245
9484
|
ttlMs: config.evidenceTtlMs
|
|
8246
9485
|
}));
|
|
8247
|
-
const createLiveVerifier = options.createLiveVerifier ?? ((strategy, config) => (0,
|
|
9486
|
+
const createLiveVerifier = options.createLiveVerifier ?? ((strategy, config) => (0, import_subscriptions6.createCodexImageLiveVerifier)({
|
|
8248
9487
|
authStrategy: strategy,
|
|
8249
9488
|
generationTimeoutMs: config.queue.generationTimeoutMs
|
|
8250
9489
|
}));
|
|
@@ -8606,7 +9845,7 @@ var ImageCleanupService = class {
|
|
|
8606
9845
|
var import_node_crypto16 = require("crypto");
|
|
8607
9846
|
var import_image_generation5 = require("@omnicross/core/image-generation");
|
|
8608
9847
|
var import_outbound_api8 = require("@omnicross/core/outbound-api");
|
|
8609
|
-
var
|
|
9848
|
+
var import_subscriptions7 = require("@omnicross/subscriptions");
|
|
8610
9849
|
|
|
8611
9850
|
// src/image-generation/ImageApiRuntimeResolver.ts
|
|
8612
9851
|
var import_node_crypto14 = require("crypto");
|
|
@@ -9137,7 +10376,7 @@ function createImageRuntimeGeneration(options) {
|
|
|
9137
10376
|
now: options.now ?? Date.now,
|
|
9138
10377
|
referenceStore: options.storage.referenceStore,
|
|
9139
10378
|
stateStore: options.storage.stateStore
|
|
9140
|
-
}) : (0,
|
|
10379
|
+
}) : (0, import_subscriptions7.createCodexSubscriptionImageProvider)({
|
|
9141
10380
|
authStrategy,
|
|
9142
10381
|
evidenceSource: generationEvidenceSource,
|
|
9143
10382
|
executionScheduler: scheduler,
|
|
@@ -13803,10 +15042,13 @@ function bucketLabel(bucketStartTs, bucket) {
|
|
|
13803
15042
|
}
|
|
13804
15043
|
|
|
13805
15044
|
// src/ports/JsonOutboundKeyDb.ts
|
|
15045
|
+
var import_node_fs19 = require("fs");
|
|
15046
|
+
var import_core3 = require("@omnicross/core");
|
|
15047
|
+
|
|
15048
|
+
// src/ports/atomicFile.ts
|
|
13806
15049
|
var import_node_crypto22 = require("crypto");
|
|
13807
15050
|
var import_node_fs18 = require("fs");
|
|
13808
15051
|
var import_node_path22 = require("path");
|
|
13809
|
-
var import_core3 = require("@omnicross/core");
|
|
13810
15052
|
function atomicReplaceUtf8(targetPath, contents) {
|
|
13811
15053
|
const tempPath = (0, import_node_path22.join)(
|
|
13812
15054
|
(0, import_node_path22.dirname)(targetPath),
|
|
@@ -13836,6 +15078,8 @@ function atomicReplaceUtf8(targetPath, contents) {
|
|
|
13836
15078
|
throw error;
|
|
13837
15079
|
}
|
|
13838
15080
|
}
|
|
15081
|
+
|
|
15082
|
+
// src/ports/JsonOutboundKeyDb.ts
|
|
13839
15083
|
var JsonOutboundKeyDb = class {
|
|
13840
15084
|
/**
|
|
13841
15085
|
* @param secretBox OPTIONAL reversible-secret codec. When present, a created
|
|
@@ -13978,9 +15222,9 @@ var JsonOutboundKeyDb = class {
|
|
|
13978
15222
|
}
|
|
13979
15223
|
/** Read the key rows, tolerating a missing/corrupt file (→ empty list). */
|
|
13980
15224
|
readRows() {
|
|
13981
|
-
if (!(0,
|
|
15225
|
+
if (!(0, import_node_fs19.existsSync)(this.keysPath)) return [];
|
|
13982
15226
|
try {
|
|
13983
|
-
const parsed = JSON.parse((0,
|
|
15227
|
+
const parsed = JSON.parse((0, import_node_fs19.readFileSync)(this.keysPath, "utf8"));
|
|
13984
15228
|
return Array.isArray(parsed) ? parsed : [];
|
|
13985
15229
|
} catch {
|
|
13986
15230
|
return [];
|
|
@@ -13997,7 +15241,7 @@ function applyPolicyField(row, field, value) {
|
|
|
13997
15241
|
}
|
|
13998
15242
|
|
|
13999
15243
|
// src/ports/JsonPricingStore.ts
|
|
14000
|
-
var
|
|
15244
|
+
var import_node_fs20 = require("fs");
|
|
14001
15245
|
var import_node_crypto23 = require("crypto");
|
|
14002
15246
|
var JsonPricingStore = class {
|
|
14003
15247
|
constructor(pricingPath) {
|
|
@@ -14012,9 +15256,9 @@ var JsonPricingStore = class {
|
|
|
14012
15256
|
* otherwise unusable pricing table after a crash or manual file edit.
|
|
14013
15257
|
*/
|
|
14014
15258
|
hasUsableSnapshot() {
|
|
14015
|
-
if (!(0,
|
|
15259
|
+
if (!(0, import_node_fs20.existsSync)(this.pricingPath)) return false;
|
|
14016
15260
|
try {
|
|
14017
|
-
const parsed = JSON.parse((0,
|
|
15261
|
+
const parsed = JSON.parse((0, import_node_fs20.readFileSync)(this.pricingPath, "utf8"));
|
|
14018
15262
|
return Array.isArray(parsed) && parsed.some(isUsablePricingRow);
|
|
14019
15263
|
} catch {
|
|
14020
15264
|
return false;
|
|
@@ -14127,9 +15371,9 @@ var JsonPricingStore = class {
|
|
|
14127
15371
|
}
|
|
14128
15372
|
/** Read the pricing rows, tolerating a missing/corrupt file (→ empty list). */
|
|
14129
15373
|
readRows() {
|
|
14130
|
-
if (!(0,
|
|
15374
|
+
if (!(0, import_node_fs20.existsSync)(this.pricingPath)) return [];
|
|
14131
15375
|
try {
|
|
14132
|
-
const parsed = JSON.parse((0,
|
|
15376
|
+
const parsed = JSON.parse((0, import_node_fs20.readFileSync)(this.pricingPath, "utf8"));
|
|
14133
15377
|
return Array.isArray(parsed) ? parsed : [];
|
|
14134
15378
|
} catch {
|
|
14135
15379
|
return [];
|
|
@@ -14138,18 +15382,18 @@ var JsonPricingStore = class {
|
|
|
14138
15382
|
writeRows(rows) {
|
|
14139
15383
|
const temporaryPath = `${this.pricingPath}.${process.pid}.${(0, import_node_crypto23.randomUUID)()}.tmp`;
|
|
14140
15384
|
try {
|
|
14141
|
-
(0,
|
|
15385
|
+
(0, import_node_fs20.writeFileSync)(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
|
|
14142
15386
|
encoding: "utf8",
|
|
14143
15387
|
flag: "wx"
|
|
14144
15388
|
});
|
|
14145
15389
|
this.replaceFile(temporaryPath);
|
|
14146
15390
|
} finally {
|
|
14147
|
-
(0,
|
|
15391
|
+
(0, import_node_fs20.rmSync)(temporaryPath, { force: true });
|
|
14148
15392
|
}
|
|
14149
15393
|
}
|
|
14150
15394
|
/** Isolated for deterministic failure testing; never removes the target. */
|
|
14151
15395
|
replaceFile(temporaryPath) {
|
|
14152
|
-
(0,
|
|
15396
|
+
(0, import_node_fs20.renameSync)(temporaryPath, this.pricingPath);
|
|
14153
15397
|
}
|
|
14154
15398
|
};
|
|
14155
15399
|
function isUsablePricingRow(value) {
|
|
@@ -14159,7 +15403,7 @@ function isUsablePricingRow(value) {
|
|
|
14159
15403
|
}
|
|
14160
15404
|
|
|
14161
15405
|
// src/pricing/PricingRefreshScheduler.ts
|
|
14162
|
-
var
|
|
15406
|
+
var import_node_fs21 = require("fs");
|
|
14163
15407
|
var EMPTY_STATE2 = {
|
|
14164
15408
|
lastAttemptAt: null,
|
|
14165
15409
|
lastSuccessAt: null,
|
|
@@ -14197,9 +15441,9 @@ var PricingRefreshScheduler = class {
|
|
|
14197
15441
|
this.timer = null;
|
|
14198
15442
|
}
|
|
14199
15443
|
getState() {
|
|
14200
|
-
if (!(0,
|
|
15444
|
+
if (!(0, import_node_fs21.existsSync)(this.statePath)) return { ...EMPTY_STATE2, sources: [] };
|
|
14201
15445
|
try {
|
|
14202
|
-
const value = JSON.parse((0,
|
|
15446
|
+
const value = JSON.parse((0, import_node_fs21.readFileSync)(this.statePath, "utf8"));
|
|
14203
15447
|
return {
|
|
14204
15448
|
lastAttemptAt: finiteOrNull(value.lastAttemptAt),
|
|
14205
15449
|
lastSuccessAt: finiteOrNull(value.lastSuccessAt),
|
|
@@ -14252,9 +15496,9 @@ var PricingRefreshScheduler = class {
|
|
|
14252
15496
|
}
|
|
14253
15497
|
writeState(state) {
|
|
14254
15498
|
const temporaryPath = `${this.statePath}.tmp`;
|
|
14255
|
-
(0,
|
|
15499
|
+
(0, import_node_fs21.writeFileSync)(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
14256
15500
|
`, "utf8");
|
|
14257
|
-
(0,
|
|
15501
|
+
(0, import_node_fs21.renameSync)(temporaryPath, this.statePath);
|
|
14258
15502
|
}
|
|
14259
15503
|
};
|
|
14260
15504
|
function finiteOrNull(value) {
|
|
@@ -14262,7 +15506,7 @@ function finiteOrNull(value) {
|
|
|
14262
15506
|
}
|
|
14263
15507
|
|
|
14264
15508
|
// src/ports/JsonVoucherDb.ts
|
|
14265
|
-
var
|
|
15509
|
+
var import_node_fs22 = require("fs");
|
|
14266
15510
|
var JsonVoucherDb = class {
|
|
14267
15511
|
constructor(vouchersPath) {
|
|
14268
15512
|
this.vouchersPath = vouchersPath;
|
|
@@ -14340,27 +15584,27 @@ var JsonVoucherDb = class {
|
|
|
14340
15584
|
}
|
|
14341
15585
|
/** Read the voucher rows, tolerating a missing/corrupt file (→ empty list). */
|
|
14342
15586
|
readRows() {
|
|
14343
|
-
if (!(0,
|
|
15587
|
+
if (!(0, import_node_fs22.existsSync)(this.vouchersPath)) return [];
|
|
14344
15588
|
try {
|
|
14345
|
-
const parsed = JSON.parse((0,
|
|
15589
|
+
const parsed = JSON.parse((0, import_node_fs22.readFileSync)(this.vouchersPath, "utf8"));
|
|
14346
15590
|
return Array.isArray(parsed) ? parsed : [];
|
|
14347
15591
|
} catch {
|
|
14348
15592
|
return [];
|
|
14349
15593
|
}
|
|
14350
15594
|
}
|
|
14351
15595
|
writeRows(rows) {
|
|
14352
|
-
(0,
|
|
15596
|
+
(0, import_node_fs22.writeFileSync)(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
|
|
14353
15597
|
}
|
|
14354
15598
|
};
|
|
14355
15599
|
|
|
14356
15600
|
// src/ports/JsonSubscriptionCredentialStore.ts
|
|
14357
|
-
var
|
|
15601
|
+
var import_node_fs24 = require("fs");
|
|
14358
15602
|
var import_node_path24 = require("path");
|
|
14359
15603
|
var import_SubscriptionAccountHealth2 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
14360
15604
|
var import_AccountAllowanceScheduling3 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
14361
|
-
var
|
|
15605
|
+
var import_upstreamFetch9 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
14362
15606
|
var import_SubscriptionIdentityStore2 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
14363
|
-
var
|
|
15607
|
+
var import_subscriptions8 = require("@omnicross/subscriptions");
|
|
14364
15608
|
|
|
14365
15609
|
// src/ports/account-sync.ts
|
|
14366
15610
|
function viewOf(tokens) {
|
|
@@ -14404,7 +15648,7 @@ function findDuplicateCredentialIds(accounts) {
|
|
|
14404
15648
|
}
|
|
14405
15649
|
|
|
14406
15650
|
// src/ports/external-cli-credentials.ts
|
|
14407
|
-
var
|
|
15651
|
+
var import_node_fs23 = require("fs");
|
|
14408
15652
|
var import_node_os5 = require("os");
|
|
14409
15653
|
var import_node_path23 = require("path");
|
|
14410
15654
|
function externalStorePath(provider, home = (0, import_node_os5.homedir)()) {
|
|
@@ -14457,10 +15701,10 @@ function parseCodexTokensEnvelope(raw) {
|
|
|
14457
15701
|
}
|
|
14458
15702
|
function readExternalCliCredentials(provider, home = (0, import_node_os5.homedir)()) {
|
|
14459
15703
|
const path2 = externalStorePath(provider, home);
|
|
14460
|
-
if (!(0,
|
|
15704
|
+
if (!(0, import_node_fs23.existsSync)(path2)) return null;
|
|
14461
15705
|
let raw;
|
|
14462
15706
|
try {
|
|
14463
|
-
const parsed = JSON.parse((0,
|
|
15707
|
+
const parsed = JSON.parse((0, import_node_fs23.readFileSync)(path2, "utf8"));
|
|
14464
15708
|
raw = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
14465
15709
|
} catch {
|
|
14466
15710
|
return null;
|
|
@@ -14483,16 +15727,18 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14483
15727
|
* as on relay refresh egresses from the SAME proxy IP as the
|
|
14484
15728
|
* account's traffic. NOT used by any read/write path.
|
|
14485
15729
|
*/
|
|
14486
|
-
constructor(tokensPath, box, fetchImpl = void 0, externalCliReader = readExternalCliCredentials) {
|
|
15730
|
+
constructor(tokensPath, box, fetchImpl = void 0, externalCliReader = readExternalCliCredentials, atomicReplace = atomicReplaceUtf8) {
|
|
14487
15731
|
this.tokensPath = tokensPath;
|
|
14488
15732
|
this.box = box;
|
|
14489
15733
|
this.fetchImpl = fetchImpl;
|
|
14490
15734
|
this.externalCliReader = externalCliReader;
|
|
15735
|
+
this.atomicReplace = atomicReplace;
|
|
14491
15736
|
}
|
|
14492
15737
|
tokensPath;
|
|
14493
15738
|
box;
|
|
14494
15739
|
fetchImpl;
|
|
14495
15740
|
externalCliReader;
|
|
15741
|
+
atomicReplace;
|
|
14496
15742
|
/**
|
|
14497
15743
|
* The proxy-aware `FetchLike` for one refresh round-trip (upstream-proxy M1). A
|
|
14498
15744
|
* TEST-injected `fetchImpl` is returned verbatim; otherwise the refresh routes
|
|
@@ -14506,7 +15752,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14506
15752
|
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
14507
15753
|
*/
|
|
14508
15754
|
buildRefreshFetch(providerId, accountId) {
|
|
14509
|
-
return this.fetchImpl ?? ((url, init) => (0,
|
|
15755
|
+
return this.fetchImpl ?? ((url, init) => (0, import_upstreamFetch9.fetchUpstream)(url, init, { providerId, accountId, redactBodies: true }));
|
|
14510
15756
|
}
|
|
14511
15757
|
/**
|
|
14512
15758
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -14547,7 +15793,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14547
15793
|
* other hot reads. Never returns token material.
|
|
14548
15794
|
*/
|
|
14549
15795
|
getAccountProxy(providerId, accountId) {
|
|
14550
|
-
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego") {
|
|
15796
|
+
if (providerId !== "claude" && providerId !== "codex" && providerId !== "gemini" && providerId !== "opencodego" && providerId !== "kimi") {
|
|
14551
15797
|
return void 0;
|
|
14552
15798
|
}
|
|
14553
15799
|
return getAccountProxy(this.readConfig(), providerId, accountId);
|
|
@@ -14566,7 +15812,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14566
15812
|
const fingerprintOn = identityStore.isEnabled();
|
|
14567
15813
|
const now = Date.now();
|
|
14568
15814
|
const out = {};
|
|
14569
|
-
for (const provider of ["claude", "codex", "gemini", "opencodego"]) {
|
|
15815
|
+
for (const provider of ["claude", "codex", "gemini", "opencodego", "kimi"]) {
|
|
14570
15816
|
const sanitized = sanitizeAccounts(config, provider);
|
|
14571
15817
|
if (sanitized.length === 0) continue;
|
|
14572
15818
|
for (const account of sanitized) {
|
|
@@ -14632,7 +15878,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14632
15878
|
this.materializeMigration(config);
|
|
14633
15879
|
const refreshFetch = this.buildRefreshFetch("claude", capturedId);
|
|
14634
15880
|
try {
|
|
14635
|
-
const result = await
|
|
15881
|
+
const result = await import_subscriptions8.claudeOAuth.refreshAccessToken(claude.refreshToken, refreshFetch);
|
|
14636
15882
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
14637
15883
|
const next = {
|
|
14638
15884
|
...claude,
|
|
@@ -14667,7 +15913,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14667
15913
|
this.materializeMigration(config);
|
|
14668
15914
|
const refreshFetch = this.buildRefreshFetch("codex", capturedId);
|
|
14669
15915
|
try {
|
|
14670
|
-
const result = await
|
|
15916
|
+
const result = await import_subscriptions8.codexOAuth.refreshAccessToken(codex.refreshToken, refreshFetch);
|
|
14671
15917
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
14672
15918
|
const next = {
|
|
14673
15919
|
...codex,
|
|
@@ -14705,7 +15951,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14705
15951
|
this.materializeMigration(config);
|
|
14706
15952
|
const refreshFetch = this.buildRefreshFetch("gemini", capturedId);
|
|
14707
15953
|
try {
|
|
14708
|
-
const result = await
|
|
15954
|
+
const result = await import_subscriptions8.geminiOAuth.refreshAccessToken(gemini.refreshToken, refreshFetch);
|
|
14709
15955
|
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
14710
15956
|
const next = {
|
|
14711
15957
|
...gemini,
|
|
@@ -14724,6 +15970,47 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14724
15970
|
}
|
|
14725
15971
|
});
|
|
14726
15972
|
}
|
|
15973
|
+
/**
|
|
15974
|
+
* Refresh the Kimi Code (Moonshot) OAuth access token (device-flow grant).
|
|
15975
|
+
* Kimi ROTATES the refresh token, so the response's pair is written back
|
|
15976
|
+
* whole; the account's stable `deviceId` (fingerprint header input) is
|
|
15977
|
+
* preserved. The refresh call carries the CLI fingerprint headers. HONEST
|
|
15978
|
+
* `false` when no refresh_token.
|
|
15979
|
+
*/
|
|
15980
|
+
async refreshKimiToken() {
|
|
15981
|
+
return this.coalesce("kimi:active", async () => {
|
|
15982
|
+
const config = this.readConfig();
|
|
15983
|
+
const active = getActiveAccount(config, "kimi");
|
|
15984
|
+
const kimi = active?.tokens;
|
|
15985
|
+
if (!active || !kimi?.refreshToken) return false;
|
|
15986
|
+
const capturedId = active.id;
|
|
15987
|
+
this.materializeMigration(config);
|
|
15988
|
+
const refreshFetch = this.buildRefreshFetch("kimi", capturedId);
|
|
15989
|
+
try {
|
|
15990
|
+
const result = await import_subscriptions8.kimiOAuth.refreshAccessToken(
|
|
15991
|
+
kimi.refreshToken,
|
|
15992
|
+
refreshFetch,
|
|
15993
|
+
import_subscriptions8.kimiOAuth.kimiFingerprintHeaders(kimi.deviceId)
|
|
15994
|
+
);
|
|
15995
|
+
const expiresAt = new Date(Date.now() + result.expiresIn * 1e3).toISOString();
|
|
15996
|
+
const next = {
|
|
15997
|
+
...kimi,
|
|
15998
|
+
accessToken: result.accessToken,
|
|
15999
|
+
refreshToken: result.refreshToken,
|
|
16000
|
+
expiresAt,
|
|
16001
|
+
status: "authorized",
|
|
16002
|
+
lastRefreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16003
|
+
errorMessage: void 0,
|
|
16004
|
+
syncWarning: void 0
|
|
16005
|
+
};
|
|
16006
|
+
this.writeBackById("kimi", capturedId, next);
|
|
16007
|
+
return true;
|
|
16008
|
+
} catch (error) {
|
|
16009
|
+
this.markExpiredById("kimi", capturedId, kimi, error);
|
|
16010
|
+
return false;
|
|
16011
|
+
}
|
|
16012
|
+
});
|
|
16013
|
+
}
|
|
14727
16014
|
/**
|
|
14728
16015
|
* Refresh a SPECIFIC managed account by id (background scheduler sweep and
|
|
14729
16016
|
* account-pool resolution). It uses only that account's stored refresh
|
|
@@ -14776,7 +16063,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14776
16063
|
}
|
|
14777
16064
|
const oauth = account.tokens;
|
|
14778
16065
|
if (!oauth.accessToken) return null;
|
|
14779
|
-
if (providerId === "codex" || providerId === "gemini") {
|
|
16066
|
+
if (providerId === "codex" || providerId === "gemini" || providerId === "kimi") {
|
|
14780
16067
|
const expiresAtMs = oauth.expiresAt ? Date.parse(oauth.expiresAt) : 0;
|
|
14781
16068
|
const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - ACCOUNT_REFRESH_LEAD_MS;
|
|
14782
16069
|
if (expiringSoon && oauth.refreshToken) {
|
|
@@ -14865,8 +16152,23 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14865
16152
|
}
|
|
14866
16153
|
/** Dispatch one OAuth refresh round-trip to the provider's shared flow. */
|
|
14867
16154
|
async refreshUpstream(provider, refreshToken, accountId) {
|
|
14868
|
-
const
|
|
14869
|
-
|
|
16155
|
+
const refreshFetch = this.buildRefreshFetch(provider, accountId);
|
|
16156
|
+
if (provider === "kimi") {
|
|
16157
|
+
const account = accountId ? getAccountById(this.readConfig(), "kimi", accountId) : void 0;
|
|
16158
|
+
const deviceId = account?.tokens?.deviceId;
|
|
16159
|
+
const r2 = await import_subscriptions8.kimiOAuth.refreshAccessToken(
|
|
16160
|
+
refreshToken,
|
|
16161
|
+
refreshFetch,
|
|
16162
|
+
import_subscriptions8.kimiOAuth.kimiFingerprintHeaders(deviceId)
|
|
16163
|
+
);
|
|
16164
|
+
return {
|
|
16165
|
+
accessToken: r2.accessToken,
|
|
16166
|
+
refreshToken: r2.refreshToken,
|
|
16167
|
+
expiresAt: new Date(Date.now() + r2.expiresIn * 1e3).toISOString()
|
|
16168
|
+
};
|
|
16169
|
+
}
|
|
16170
|
+
const flow = provider === "claude" ? import_subscriptions8.claudeOAuth : provider === "codex" ? import_subscriptions8.codexOAuth : import_subscriptions8.geminiOAuth;
|
|
16171
|
+
const r = await flow.refreshAccessToken(refreshToken, refreshFetch);
|
|
14870
16172
|
return {
|
|
14871
16173
|
accessToken: r.accessToken,
|
|
14872
16174
|
refreshToken: r.refreshToken,
|
|
@@ -15029,42 +16331,86 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15029
16331
|
/** Write the merged config to disk as pretty JSON (mkdir parent if needed).
|
|
15030
16332
|
* Encrypt-on-write: the token-material fields are encrypted (legacy plaintext
|
|
15031
16333
|
* `enc:v1:`; already-`enc:`/`$ENV` untouched) before serializing, so any
|
|
15032
|
-
* write incl. child 4's future refresh writes lands encrypted.
|
|
16334
|
+
* write incl. child 4's future refresh writes lands encrypted.
|
|
16335
|
+
* ATOMIC: temp + fsync + rename (`atomicReplaceUtf8`) — a failed or
|
|
16336
|
+
* interrupted write discards only the temp file; the prior `tokens.json`
|
|
16337
|
+
* survives byte-equal (bare `writeFileSync` truncate-writes lost every
|
|
16338
|
+
* account on a mid-write failure, 2026-09-06). */
|
|
15033
16339
|
persist(config) {
|
|
15034
|
-
(0,
|
|
16340
|
+
(0, import_node_fs24.mkdirSync)((0, import_node_path24.dirname)(this.tokensPath), { recursive: true });
|
|
15035
16341
|
const encrypted = encryptTokens(config, this.box);
|
|
15036
|
-
|
|
16342
|
+
this.atomicReplace(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n");
|
|
15037
16343
|
}
|
|
15038
16344
|
/**
|
|
15039
|
-
* Read + parse `tokens.json`,
|
|
15040
|
-
*
|
|
15041
|
-
*
|
|
16345
|
+
* Read + parse `tokens.json`, then DECRYPT the token-material fields so every
|
|
16346
|
+
* getter returns plaintext (the subscription bearer path is byte-identical).
|
|
16347
|
+
*
|
|
16348
|
+
* A MISSING file is a legitimate first-boot state → minimal `{ updatedAt: '' }`.
|
|
16349
|
+
* A file that EXISTS but cannot be parsed as a JSON object is CORRUPT →
|
|
16350
|
+
* `quarantineCorrupt` moves it aside (once) before the empty config is
|
|
16351
|
+
* returned, so the unreadable accounts survive for manual recovery.
|
|
15042
16352
|
*
|
|
15043
|
-
* The
|
|
15044
|
-
*
|
|
15045
|
-
*
|
|
15046
|
-
*
|
|
15047
|
-
*
|
|
15048
|
-
*
|
|
15049
|
-
* `config.ts loadConfig`, which decrypts outside its parse try.
|
|
16353
|
+
* The DECRYPT runs OUTSIDE any try, so a wrong/missing master key or a
|
|
16354
|
+
* tampered `enc:` envelope FAILS FAST with the box's clear, secret-free
|
|
16355
|
+
* error (secrets spec "/ UX": SHALL fail-fast, SHALL NOT a swallowed
|
|
16356
|
+
* decrypt would report "no tokens" and silently send the WRONG bearer
|
|
16357
|
+
* upstream 401). Mirrors `config.ts loadConfig`, which decrypts outside
|
|
16358
|
+
* its parse try.
|
|
15050
16359
|
*/
|
|
15051
16360
|
readConfig() {
|
|
15052
|
-
if (!(0,
|
|
16361
|
+
if (!(0, import_node_fs24.existsSync)(this.tokensPath)) return { updatedAt: "" };
|
|
15053
16362
|
let parsed;
|
|
15054
16363
|
try {
|
|
15055
|
-
const raw = JSON.parse((0,
|
|
15056
|
-
|
|
16364
|
+
const raw = JSON.parse((0, import_node_fs24.readFileSync)(this.tokensPath, "utf8"));
|
|
16365
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
16366
|
+
return this.quarantineCorrupt("parsed JSON is not an object");
|
|
16367
|
+
}
|
|
16368
|
+
parsed = raw;
|
|
15057
16369
|
} catch {
|
|
15058
|
-
|
|
16370
|
+
return this.quarantineCorrupt("unparseable JSON");
|
|
15059
16371
|
}
|
|
15060
|
-
if (!parsed) return { updatedAt: "" };
|
|
15061
16372
|
const decrypted = decryptTokens(parsed, this.box);
|
|
15062
16373
|
return migrateLazily(decrypted);
|
|
15063
16374
|
}
|
|
16375
|
+
/** One-shot latch: a corrupt file is quarantined (or found unmovable) at
|
|
16376
|
+
* most once per process, so the hot read path never re-attempts or re-logs. */
|
|
16377
|
+
corruptQuarantined = false;
|
|
16378
|
+
/**
|
|
16379
|
+
* Quarantine a present-but-corrupt `tokens.json`, then treat it as empty.
|
|
16380
|
+
*
|
|
16381
|
+
* Renames the file to a sibling `tokens.json.corrupt-<stamp>` backup and
|
|
16382
|
+
* logs loudly (the daemon's stderr log; secret-free — reason + paths only).
|
|
16383
|
+
* The daemon KEEPS SERVING (API-key routing is unaffected; subscription
|
|
16384
|
+
* routing reports no credential, same as an absent file) while the corrupt
|
|
16385
|
+
* bytes survive for manual recovery — and, critically, the NEXT persist
|
|
16386
|
+
* (e.g. the user re-logging in) can no longer overwrite the only copy of
|
|
16387
|
+
* the old accounts, which is exactly how the 2026-09-06 incident turned a
|
|
16388
|
+
* recoverable truncated file into permanent account loss.
|
|
16389
|
+
*
|
|
16390
|
+
* Best-effort: if the rename fails (file locked, permissions), the corrupt
|
|
16391
|
+
* file is left in place and every later read still tolerates it as empty;
|
|
16392
|
+
* the latch still trips so the attempt + log happen exactly once.
|
|
16393
|
+
*/
|
|
16394
|
+
quarantineCorrupt(reason) {
|
|
16395
|
+
if (!this.corruptQuarantined) {
|
|
16396
|
+
this.corruptQuarantined = true;
|
|
16397
|
+
const backup = `${this.tokensPath}.corrupt-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
16398
|
+
let moved = false;
|
|
16399
|
+
try {
|
|
16400
|
+
(0, import_node_fs24.renameSync)(this.tokensPath, backup);
|
|
16401
|
+
moved = true;
|
|
16402
|
+
} catch {
|
|
16403
|
+
}
|
|
16404
|
+
console.error(
|
|
16405
|
+
`[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`)
|
|
16406
|
+
);
|
|
16407
|
+
}
|
|
16408
|
+
return { updatedAt: "" };
|
|
16409
|
+
}
|
|
15064
16410
|
};
|
|
15065
16411
|
|
|
15066
16412
|
// src/AccountHealthProbeScheduler.ts
|
|
15067
|
-
var
|
|
16413
|
+
var import_upstreamFetch10 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
15068
16414
|
|
|
15069
16415
|
// src/probe/CodexGenerationProbe.ts
|
|
15070
16416
|
var import_codexCliHeaders = require("@omnicross/core/provider-proxy/identity/codexCliHeaders");
|
|
@@ -15203,7 +16549,11 @@ var PROVIDER_PROBE_PLANS = {
|
|
|
15203
16549
|
// billable/wrong endpoint). Upgrade to `{ kind:'upstream' }` once verified.
|
|
15204
16550
|
codex: { kind: "local" },
|
|
15205
16551
|
gemini: { kind: "local" },
|
|
15206
|
-
opencodego: { kind: "local" }
|
|
16552
|
+
opencodego: { kind: "local" },
|
|
16553
|
+
// Kimi's `GET /coding/v1/usages` is a verified FREE authed GET (the allowance
|
|
16554
|
+
// collector uses it), but the probe path also needs the fingerprint headers —
|
|
16555
|
+
// keep the probe local until the collector covers the health surface.
|
|
16556
|
+
kimi: { kind: "local" }
|
|
15207
16557
|
};
|
|
15208
16558
|
function probePlanFor(providerId) {
|
|
15209
16559
|
return PROVIDER_PROBE_PLANS[providerId] ?? { kind: "local" };
|
|
@@ -15225,7 +16575,7 @@ var AccountHealthProbeScheduler = class {
|
|
|
15225
16575
|
this.logger = logger;
|
|
15226
16576
|
this.config = config;
|
|
15227
16577
|
this.now = opts.now ?? Date.now;
|
|
15228
|
-
this.fetchImpl = opts.fetchImpl ??
|
|
16578
|
+
this.fetchImpl = opts.fetchImpl ?? import_upstreamFetch10.fetchUpstream;
|
|
15229
16579
|
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
15230
16580
|
this.planFor = opts.planFor ?? probePlanFor;
|
|
15231
16581
|
}
|
|
@@ -15569,13 +16919,13 @@ var AccountHealthSweeper = class {
|
|
|
15569
16919
|
};
|
|
15570
16920
|
|
|
15571
16921
|
// src/audit/AuditPruneSweeper.ts
|
|
15572
|
-
var
|
|
16922
|
+
var import_node_fs27 = require("fs");
|
|
15573
16923
|
var import_node_path27 = require("path");
|
|
15574
16924
|
var import_promises6 = require("stream/promises");
|
|
15575
16925
|
var import_node_zlib = require("zlib");
|
|
15576
16926
|
|
|
15577
16927
|
// src/audit/auditDictionary.ts
|
|
15578
|
-
var
|
|
16928
|
+
var import_node_fs25 = require("fs");
|
|
15579
16929
|
var import_node_path25 = require("path");
|
|
15580
16930
|
|
|
15581
16931
|
// src/audit/auditBodyStore.ts
|
|
@@ -15808,7 +17158,7 @@ function parseEntries(raw) {
|
|
|
15808
17158
|
}
|
|
15809
17159
|
function plainShards(bodiesPath) {
|
|
15810
17160
|
try {
|
|
15811
|
-
return (0,
|
|
17161
|
+
return (0, import_node_fs25.readdirSync)(bodiesPath).filter(
|
|
15812
17162
|
(file) => file.endsWith(".jsonl") && isSafeSessionKey(file.slice(0, -".jsonl".length))
|
|
15813
17163
|
);
|
|
15814
17164
|
} catch {
|
|
@@ -15833,9 +17183,9 @@ function chooseDictionary(anchors) {
|
|
|
15833
17183
|
var EMPTY = { shards: 0, anchors: 0, savedBytes: 0 };
|
|
15834
17184
|
function compactAuditDay(dayPath) {
|
|
15835
17185
|
const bodiesPath = (0, import_node_path25.join)(dayPath, AUDIT_BODIES_DIR);
|
|
15836
|
-
if (!(0,
|
|
17186
|
+
if (!(0, import_node_fs25.existsSync)(bodiesPath)) return EMPTY;
|
|
15837
17187
|
const dictPath = (0, import_node_path25.join)(bodiesPath, AUDIT_DICT_FILE);
|
|
15838
|
-
if ((0,
|
|
17188
|
+
if ((0, import_node_fs25.existsSync)(dictPath) || (0, import_node_fs25.existsSync)(`${dictPath}.gz`)) return EMPTY;
|
|
15839
17189
|
const shardFiles = plainShards(bodiesPath);
|
|
15840
17190
|
if (shardFiles.length < 2) return EMPTY;
|
|
15841
17191
|
const loaded = /* @__PURE__ */ new Map();
|
|
@@ -15843,7 +17193,7 @@ function compactAuditDay(dayPath) {
|
|
|
15843
17193
|
for (const file of shardFiles) {
|
|
15844
17194
|
let entries;
|
|
15845
17195
|
try {
|
|
15846
|
-
entries = parseEntries((0,
|
|
17196
|
+
entries = parseEntries((0, import_node_fs25.readFileSync)((0, import_node_path25.join)(bodiesPath, file), "utf8"));
|
|
15847
17197
|
} catch {
|
|
15848
17198
|
continue;
|
|
15849
17199
|
}
|
|
@@ -15860,7 +17210,7 @@ function compactAuditDay(dayPath) {
|
|
|
15860
17210
|
ts: 0,
|
|
15861
17211
|
req: { base: null, anchor: "dict", pre: 0, suf: 0, ins: dictionary }
|
|
15862
17212
|
};
|
|
15863
|
-
(0,
|
|
17213
|
+
(0, import_node_fs25.writeFileSync)(dictPath, JSON.stringify(dictEntry) + "\n", "utf8");
|
|
15864
17214
|
const result = { shards: 0, anchors: 0, savedBytes: 0 };
|
|
15865
17215
|
for (const [file, entries] of loaded) {
|
|
15866
17216
|
let changed = false;
|
|
@@ -15880,11 +17230,11 @@ function compactAuditDay(dayPath) {
|
|
|
15880
17230
|
const target = (0, import_node_path25.join)(bodiesPath, file);
|
|
15881
17231
|
const temp = `${target}.compacting`;
|
|
15882
17232
|
try {
|
|
15883
|
-
(0,
|
|
15884
|
-
(0,
|
|
17233
|
+
(0, import_node_fs25.writeFileSync)(temp, rewritten.map((e) => JSON.stringify(e)).join("\n") + "\n", "utf8");
|
|
17234
|
+
(0, import_node_fs25.renameSync)(temp, target);
|
|
15885
17235
|
} catch {
|
|
15886
17236
|
try {
|
|
15887
|
-
if ((0,
|
|
17237
|
+
if ((0, import_node_fs25.existsSync)(temp)) (0, import_node_fs25.unlinkSync)(temp);
|
|
15888
17238
|
} catch {
|
|
15889
17239
|
}
|
|
15890
17240
|
continue;
|
|
@@ -15895,7 +17245,7 @@ function compactAuditDay(dayPath) {
|
|
|
15895
17245
|
}
|
|
15896
17246
|
if (result.shards === 0) {
|
|
15897
17247
|
try {
|
|
15898
|
-
(0,
|
|
17248
|
+
(0, import_node_fs25.unlinkSync)(dictPath);
|
|
15899
17249
|
} catch {
|
|
15900
17250
|
}
|
|
15901
17251
|
}
|
|
@@ -15903,11 +17253,11 @@ function compactAuditDay(dayPath) {
|
|
|
15903
17253
|
}
|
|
15904
17254
|
function compactAllClosedAuditDays(auditDir, now = Date.now) {
|
|
15905
17255
|
const run = { days: 0, shards: 0, savedBytes: 0 };
|
|
15906
|
-
if (!(0,
|
|
17256
|
+
if (!(0, import_node_fs25.existsSync)(auditDir)) return run;
|
|
15907
17257
|
const today = auditDayDirName(now());
|
|
15908
17258
|
let names;
|
|
15909
17259
|
try {
|
|
15910
|
-
names = (0,
|
|
17260
|
+
names = (0, import_node_fs25.readdirSync)(auditDir).filter(isAuditDayDir).sort();
|
|
15911
17261
|
} catch {
|
|
15912
17262
|
return run;
|
|
15913
17263
|
}
|
|
@@ -15926,7 +17276,7 @@ function compactAllClosedAuditDays(auditDir, now = Date.now) {
|
|
|
15926
17276
|
}
|
|
15927
17277
|
|
|
15928
17278
|
// src/audit/auditStats.ts
|
|
15929
|
-
var
|
|
17279
|
+
var import_node_fs26 = require("fs");
|
|
15930
17280
|
var import_node_path26 = require("path");
|
|
15931
17281
|
var SIDECAR_VERSION = 1;
|
|
15932
17282
|
var META_PREFIX_BYTES = 64 * 1024;
|
|
@@ -15935,9 +17285,9 @@ function auditStatsFileName(auditFile) {
|
|
|
15935
17285
|
return auditFile.replace(/\.jsonl$/, ".stats.json");
|
|
15936
17286
|
}
|
|
15937
17287
|
function readPersisted(path2) {
|
|
15938
|
-
if (!(0,
|
|
17288
|
+
if (!(0, import_node_fs26.existsSync)(path2)) return null;
|
|
15939
17289
|
try {
|
|
15940
|
-
const value = JSON.parse((0,
|
|
17290
|
+
const value = JSON.parse((0, import_node_fs26.readFileSync)(path2, "utf8"));
|
|
15941
17291
|
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
17292
|
return null;
|
|
15943
17293
|
}
|
|
@@ -15967,7 +17317,7 @@ function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfte
|
|
|
15967
17317
|
minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
|
|
15968
17318
|
maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
|
|
15969
17319
|
};
|
|
15970
|
-
(0,
|
|
17320
|
+
(0, import_node_fs26.writeFileSync)(statsPath, JSON.stringify(next), "utf8");
|
|
15971
17321
|
}
|
|
15972
17322
|
function queryCovers(stats, from, to) {
|
|
15973
17323
|
return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
|
|
@@ -16025,7 +17375,7 @@ async function scanAuditFile(auditPath, startByte, auditBytes, from, to) {
|
|
|
16025
17375
|
prefixTruncated = false;
|
|
16026
17376
|
};
|
|
16027
17377
|
if (auditBytes > startByte) {
|
|
16028
|
-
const stream = (0,
|
|
17378
|
+
const stream = (0, import_node_fs26.createReadStream)(auditPath, {
|
|
16029
17379
|
start: startByte,
|
|
16030
17380
|
end: auditBytes - 1,
|
|
16031
17381
|
highWaterMark: READ_CHUNK_BYTES2
|
|
@@ -16078,12 +17428,12 @@ function mergePersistedStats(previous, appended) {
|
|
|
16078
17428
|
};
|
|
16079
17429
|
}
|
|
16080
17430
|
async function readAuditStats(auditDir, query2 = {}) {
|
|
16081
|
-
if (!(0,
|
|
17431
|
+
if (!(0, import_node_fs26.existsSync)(auditDir)) return { requestCount: 0, errorCount: 0, complete: true };
|
|
16082
17432
|
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
16083
17433
|
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
16084
17434
|
let sources;
|
|
16085
17435
|
try {
|
|
16086
|
-
sources = (0,
|
|
17436
|
+
sources = (0, import_node_fs26.readdirSync)(auditDir).filter((name) => fileOverlaps(name, from, to)).sort().map(
|
|
16087
17437
|
(name) => AUDIT_DAY_DIR_RE.test(name) ? {
|
|
16088
17438
|
auditPath: (0, import_node_path26.join)(auditDir, name, AUDIT_META_FILE),
|
|
16089
17439
|
statsPath: (0, import_node_path26.join)(auditDir, name, auditStatsFileName(AUDIT_META_FILE))
|
|
@@ -16091,14 +17441,14 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
16091
17441
|
auditPath: (0, import_node_path26.join)(auditDir, name),
|
|
16092
17442
|
statsPath: (0, import_node_path26.join)(auditDir, auditStatsFileName(name))
|
|
16093
17443
|
}
|
|
16094
|
-
).filter((source) => (0,
|
|
17444
|
+
).filter((source) => (0, import_node_fs26.existsSync)(source.auditPath));
|
|
16095
17445
|
} catch {
|
|
16096
17446
|
return { requestCount: 0, errorCount: 0, complete: false };
|
|
16097
17447
|
}
|
|
16098
17448
|
const total = { requestCount: 0, errorCount: 0, complete: true };
|
|
16099
17449
|
for (const { auditPath, statsPath } of sources) {
|
|
16100
17450
|
try {
|
|
16101
|
-
const auditBytes = (0,
|
|
17451
|
+
const auditBytes = (0, import_node_fs26.statSync)(auditPath).size;
|
|
16102
17452
|
const persisted = readPersisted(statsPath);
|
|
16103
17453
|
if (persisted && persisted.complete && persisted.auditBytes === auditBytes && queryCovers(persisted, from, to)) {
|
|
16104
17454
|
total.requestCount += persisted.requestCount;
|
|
@@ -16117,7 +17467,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
16117
17467
|
total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
|
|
16118
17468
|
total.complete = total.complete && scanned.filtered.complete;
|
|
16119
17469
|
const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
|
|
16120
|
-
if (current.complete) (0,
|
|
17470
|
+
if (current.complete) (0, import_node_fs26.writeFileSync)(statsPath, JSON.stringify(current), "utf8");
|
|
16121
17471
|
} catch {
|
|
16122
17472
|
total.complete = false;
|
|
16123
17473
|
}
|
|
@@ -16126,7 +17476,7 @@ async function readAuditStats(auditDir, query2 = {}) {
|
|
|
16126
17476
|
}
|
|
16127
17477
|
|
|
16128
17478
|
// src/audit/AuditPruneSweeper.ts
|
|
16129
|
-
var
|
|
17479
|
+
var DAY_MS3 = 24 * 60 * 6e4;
|
|
16130
17480
|
var SWEEP_INTERVAL_MS2 = 60 * 6e4;
|
|
16131
17481
|
var ARCHIVE_BATCH = 64;
|
|
16132
17482
|
var AuditPruneSweeper = class {
|
|
@@ -16189,19 +17539,19 @@ var AuditPruneSweeper = class {
|
|
|
16189
17539
|
if (!this.config.enabled || this.sweeping) return 0;
|
|
16190
17540
|
this.sweeping = true;
|
|
16191
17541
|
try {
|
|
16192
|
-
if (!(0,
|
|
16193
|
-
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) *
|
|
17542
|
+
if (!(0, import_node_fs27.existsSync)(this.auditDir)) return 0;
|
|
17543
|
+
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS3;
|
|
16194
17544
|
let removed = 0;
|
|
16195
|
-
for (const name of (0,
|
|
17545
|
+
for (const name of (0, import_node_fs27.readdirSync)(this.auditDir)) {
|
|
16196
17546
|
const dateMs = auditFileDateMs(name);
|
|
16197
17547
|
if (dateMs === null || dateMs >= cutoff) continue;
|
|
16198
17548
|
try {
|
|
16199
17549
|
if (isAuditDayDir(name)) {
|
|
16200
|
-
(0,
|
|
17550
|
+
(0, import_node_fs27.rmSync)((0, import_node_path27.join)(this.auditDir, name), { recursive: true, force: true });
|
|
16201
17551
|
} else {
|
|
16202
|
-
(0,
|
|
17552
|
+
(0, import_node_fs27.unlinkSync)((0, import_node_path27.join)(this.auditDir, name));
|
|
16203
17553
|
const statsPath = (0, import_node_path27.join)(this.auditDir, auditStatsFileName(name));
|
|
16204
|
-
if ((0,
|
|
17554
|
+
if ((0, import_node_fs27.existsSync)(statsPath)) (0, import_node_fs27.unlinkSync)(statsPath);
|
|
16205
17555
|
}
|
|
16206
17556
|
removed += 1;
|
|
16207
17557
|
} catch (error) {
|
|
@@ -16231,10 +17581,10 @@ var AuditPruneSweeper = class {
|
|
|
16231
17581
|
if (!this.config.enabled || this.archiving) return 0;
|
|
16232
17582
|
this.archiving = true;
|
|
16233
17583
|
try {
|
|
16234
|
-
if (!(0,
|
|
17584
|
+
if (!(0, import_node_fs27.existsSync)(this.auditDir)) return 0;
|
|
16235
17585
|
const today = this.todayMidnight();
|
|
16236
17586
|
let compressed = 0;
|
|
16237
|
-
for (const name of (0,
|
|
17587
|
+
for (const name of (0, import_node_fs27.readdirSync)(this.auditDir)) {
|
|
16238
17588
|
if (compressed >= ARCHIVE_BATCH) break;
|
|
16239
17589
|
const dateMs = auditFileDateMs(name);
|
|
16240
17590
|
if (dateMs === null || dateMs >= today || !isAuditDayDir(name)) continue;
|
|
@@ -16275,7 +17625,7 @@ var AuditPruneSweeper = class {
|
|
|
16275
17625
|
async archiveDay(bodiesPath, budget) {
|
|
16276
17626
|
let shards;
|
|
16277
17627
|
try {
|
|
16278
|
-
shards = (0,
|
|
17628
|
+
shards = (0, import_node_fs27.readdirSync)(bodiesPath).filter((file) => file.endsWith(".jsonl"));
|
|
16279
17629
|
} catch {
|
|
16280
17630
|
return 0;
|
|
16281
17631
|
}
|
|
@@ -16285,16 +17635,16 @@ var AuditPruneSweeper = class {
|
|
|
16285
17635
|
const source = (0, import_node_path27.join)(bodiesPath, shard);
|
|
16286
17636
|
const target = `${source}.gz`;
|
|
16287
17637
|
try {
|
|
16288
|
-
if ((0,
|
|
16289
|
-
(0,
|
|
17638
|
+
if ((0, import_node_fs27.existsSync)(target)) {
|
|
17639
|
+
(0, import_node_fs27.unlinkSync)(source);
|
|
16290
17640
|
continue;
|
|
16291
17641
|
}
|
|
16292
|
-
await (0, import_promises6.pipeline)((0,
|
|
16293
|
-
(0,
|
|
17642
|
+
await (0, import_promises6.pipeline)((0, import_node_fs27.createReadStream)(source), (0, import_node_zlib.createGzip)(), (0, import_node_fs27.createWriteStream)(target));
|
|
17643
|
+
(0, import_node_fs27.unlinkSync)(source);
|
|
16294
17644
|
compressed += 1;
|
|
16295
17645
|
} catch (error) {
|
|
16296
17646
|
try {
|
|
16297
|
-
if ((0,
|
|
17647
|
+
if ((0, import_node_fs27.existsSync)(target)) (0, import_node_fs27.unlinkSync)(target);
|
|
16298
17648
|
} catch {
|
|
16299
17649
|
}
|
|
16300
17650
|
this.logger.warn("[AuditPruneSweeper] failed to archive audit body shard", {
|
|
@@ -16308,7 +17658,7 @@ var AuditPruneSweeper = class {
|
|
|
16308
17658
|
};
|
|
16309
17659
|
|
|
16310
17660
|
// src/usage/usageMigrate.ts
|
|
16311
|
-
var
|
|
17661
|
+
var import_node_fs28 = require("fs");
|
|
16312
17662
|
var import_promises7 = require("fs/promises");
|
|
16313
17663
|
var import_node_path28 = require("path");
|
|
16314
17664
|
var import_node_readline = require("readline");
|
|
@@ -16355,7 +17705,7 @@ async function migrateLegacyUsageEvents(opts) {
|
|
|
16355
17705
|
let skipped = 0;
|
|
16356
17706
|
try {
|
|
16357
17707
|
const reader = (0, import_node_readline.createInterface)({
|
|
16358
|
-
input: (0,
|
|
17708
|
+
input: (0, import_node_fs28.createReadStream)(eventsPath, { encoding: "utf8" }),
|
|
16359
17709
|
crlfDelay: Number.POSITIVE_INFINITY
|
|
16360
17710
|
});
|
|
16361
17711
|
for await (const line of reader) {
|
|
@@ -16447,7 +17797,7 @@ async function closeAll(writers) {
|
|
|
16447
17797
|
// src/usage/UsagePruneSweeper.ts
|
|
16448
17798
|
var import_promises8 = require("fs/promises");
|
|
16449
17799
|
var import_node_path29 = require("path");
|
|
16450
|
-
var
|
|
17800
|
+
var DAY_MS4 = 24 * 60 * 6e4;
|
|
16451
17801
|
var SWEEP_INTERVAL_MS3 = 60 * 6e4;
|
|
16452
17802
|
var DEFAULT_USAGE_RETENTION_DAYS = 90;
|
|
16453
17803
|
var UsagePruneSweeper = class {
|
|
@@ -16504,7 +17854,7 @@ var UsagePruneSweeper = class {
|
|
|
16504
17854
|
this.sweeping = true;
|
|
16505
17855
|
try {
|
|
16506
17856
|
const retentionDays = this.config.retentionDays ?? DEFAULT_USAGE_RETENTION_DAYS;
|
|
16507
|
-
const cutoff = this.todayMidnight() - (retentionDays - 1) *
|
|
17857
|
+
const cutoff = this.todayMidnight() - (retentionDays - 1) * DAY_MS4;
|
|
16508
17858
|
let removed = 0;
|
|
16509
17859
|
for (const entry of await listUsageDays(this.usageDir)) {
|
|
16510
17860
|
if (!entry.hasShard) continue;
|
|
@@ -16562,12 +17912,12 @@ var UsagePruneSweeper = class {
|
|
|
16562
17912
|
};
|
|
16563
17913
|
|
|
16564
17914
|
// src/audit/auditBodyReader.ts
|
|
16565
|
-
var
|
|
17915
|
+
var import_node_fs30 = require("fs");
|
|
16566
17916
|
var import_node_path30 = require("path");
|
|
16567
17917
|
var import_node_zlib2 = require("zlib");
|
|
16568
17918
|
|
|
16569
17919
|
// src/audit/auditJsonl.ts
|
|
16570
|
-
var
|
|
17920
|
+
var import_node_fs29 = require("fs");
|
|
16571
17921
|
var WINDOW_BYTES = 1 << 20;
|
|
16572
17922
|
var MAX_LINE_BYTES = 32 * 1024 * 1024;
|
|
16573
17923
|
var NEWLINE2 = 10;
|
|
@@ -16575,9 +17925,9 @@ function forEachLineFromTail(path2, onLine) {
|
|
|
16575
17925
|
let fd;
|
|
16576
17926
|
let end;
|
|
16577
17927
|
try {
|
|
16578
|
-
end = (0,
|
|
17928
|
+
end = (0, import_node_fs29.statSync)(path2).size;
|
|
16579
17929
|
if (end === 0) return;
|
|
16580
|
-
fd = (0,
|
|
17930
|
+
fd = (0, import_node_fs29.openSync)(path2, "r");
|
|
16581
17931
|
} catch {
|
|
16582
17932
|
return;
|
|
16583
17933
|
}
|
|
@@ -16588,7 +17938,7 @@ function forEachLineFromTail(path2, onLine) {
|
|
|
16588
17938
|
const window = Buffer.allocUnsafe(end - start);
|
|
16589
17939
|
let read;
|
|
16590
17940
|
try {
|
|
16591
|
-
read = (0,
|
|
17941
|
+
read = (0, import_node_fs29.readSync)(fd, window, 0, end - start, start);
|
|
16592
17942
|
} catch {
|
|
16593
17943
|
return;
|
|
16594
17944
|
}
|
|
@@ -16616,7 +17966,7 @@ function forEachLineFromTail(path2, onLine) {
|
|
|
16616
17966
|
}
|
|
16617
17967
|
} finally {
|
|
16618
17968
|
try {
|
|
16619
|
-
(0,
|
|
17969
|
+
(0, import_node_fs29.closeSync)(fd);
|
|
16620
17970
|
} catch {
|
|
16621
17971
|
}
|
|
16622
17972
|
}
|
|
@@ -16626,10 +17976,10 @@ function forEachLineFromTail(path2, onLine) {
|
|
|
16626
17976
|
function candidateDays(auditDir, ts) {
|
|
16627
17977
|
if (typeof ts === "number" && Number.isFinite(ts)) {
|
|
16628
17978
|
const named = auditDayDirName(ts);
|
|
16629
|
-
if ((0,
|
|
17979
|
+
if ((0, import_node_fs30.existsSync)((0, import_node_path30.join)(auditDir, named))) return [named];
|
|
16630
17980
|
}
|
|
16631
17981
|
try {
|
|
16632
|
-
return (0,
|
|
17982
|
+
return (0, import_node_fs30.readdirSync)(auditDir).filter(isAuditDayDir).sort().reverse();
|
|
16633
17983
|
} catch {
|
|
16634
17984
|
return [];
|
|
16635
17985
|
}
|
|
@@ -16637,9 +17987,9 @@ function candidateDays(auditDir, ts) {
|
|
|
16637
17987
|
function readShard(auditDir, day, sessionKey) {
|
|
16638
17988
|
const base = (0, import_node_path30.join)(auditDir, day, AUDIT_BODIES_DIR, auditBodyFileName(sessionKey));
|
|
16639
17989
|
try {
|
|
16640
|
-
if ((0,
|
|
17990
|
+
if ((0, import_node_fs30.existsSync)(base)) return (0, import_node_fs30.readFileSync)(base, "utf8");
|
|
16641
17991
|
const gz = `${base}.gz`;
|
|
16642
|
-
if ((0,
|
|
17992
|
+
if ((0, import_node_fs30.existsSync)(gz)) return (0, import_node_zlib2.gunzipSync)((0, import_node_fs30.readFileSync)(gz)).toString("utf8");
|
|
16643
17993
|
} catch {
|
|
16644
17994
|
return null;
|
|
16645
17995
|
}
|
|
@@ -16672,8 +18022,8 @@ function withDictionary(auditDir, day, entries) {
|
|
|
16672
18022
|
const base = (0, import_node_path30.join)(auditDir, day, AUDIT_BODIES_DIR, AUDIT_DICT_FILE);
|
|
16673
18023
|
let raw = null;
|
|
16674
18024
|
try {
|
|
16675
|
-
if ((0,
|
|
16676
|
-
else if ((0,
|
|
18025
|
+
if ((0, import_node_fs30.existsSync)(base)) raw = (0, import_node_fs30.readFileSync)(base, "utf8");
|
|
18026
|
+
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
18027
|
} catch {
|
|
16678
18028
|
return entries;
|
|
16679
18029
|
}
|
|
@@ -16706,7 +18056,7 @@ function reconstructRequest(entries, entry) {
|
|
|
16706
18056
|
}
|
|
16707
18057
|
function readAuditBody(auditDir, query2) {
|
|
16708
18058
|
if (!isSafeSessionKey(query2.sessionKey) || !query2.id) return {};
|
|
16709
|
-
if (!(0,
|
|
18059
|
+
if (!(0, import_node_fs30.existsSync)(auditDir)) return {};
|
|
16710
18060
|
for (const day of candidateDays(auditDir, query2.ts)) {
|
|
16711
18061
|
const raw = readShard(auditDir, day, query2.sessionKey);
|
|
16712
18062
|
if (raw === null) continue;
|
|
@@ -16724,7 +18074,7 @@ function readAuditBody(auditDir, query2) {
|
|
|
16724
18074
|
function readLegacyInlineBody(auditDir, id) {
|
|
16725
18075
|
let names;
|
|
16726
18076
|
try {
|
|
16727
|
-
names = (0,
|
|
18077
|
+
names = (0, import_node_fs30.readdirSync)(auditDir).filter((name) => AUDIT_FILE_RE.test(name)).sort().reverse();
|
|
16728
18078
|
} catch {
|
|
16729
18079
|
return {};
|
|
16730
18080
|
}
|
|
@@ -16753,7 +18103,7 @@ function readLegacyInlineBody(auditDir, id) {
|
|
|
16753
18103
|
}
|
|
16754
18104
|
|
|
16755
18105
|
// src/audit/auditReader.ts
|
|
16756
|
-
var
|
|
18106
|
+
var import_node_fs31 = require("fs");
|
|
16757
18107
|
var import_node_path31 = require("path");
|
|
16758
18108
|
var DEFAULT_LIMIT = 200;
|
|
16759
18109
|
var MAX_LIMIT = 2e3;
|
|
@@ -16761,7 +18111,7 @@ var OVERSCAN = 256;
|
|
|
16761
18111
|
function daySources(auditDir) {
|
|
16762
18112
|
let names;
|
|
16763
18113
|
try {
|
|
16764
|
-
names = (0,
|
|
18114
|
+
names = (0, import_node_fs31.readdirSync)(auditDir);
|
|
16765
18115
|
} catch {
|
|
16766
18116
|
return [];
|
|
16767
18117
|
}
|
|
@@ -16771,7 +18121,7 @@ function daySources(auditDir) {
|
|
|
16771
18121
|
if (dateMs === null) continue;
|
|
16772
18122
|
if (AUDIT_DAY_DIR_RE.test(name)) {
|
|
16773
18123
|
const path2 = (0, import_node_path31.join)(auditDir, name, AUDIT_META_FILE);
|
|
16774
|
-
if ((0,
|
|
18124
|
+
if ((0, import_node_fs31.existsSync)(path2)) sources.push({ path: path2, dateMs });
|
|
16775
18125
|
} else if (AUDIT_FILE_RE.test(name)) {
|
|
16776
18126
|
sources.push({ path: (0, import_node_path31.join)(auditDir, name), dateMs });
|
|
16777
18127
|
}
|
|
@@ -16789,7 +18139,7 @@ function toMetaRecord(record) {
|
|
|
16789
18139
|
return { ...meta, hasBody: true };
|
|
16790
18140
|
}
|
|
16791
18141
|
function readAuditRecords(auditDir, query2 = {}) {
|
|
16792
|
-
if (!(0,
|
|
18142
|
+
if (!(0, import_node_fs31.existsSync)(auditDir)) return [];
|
|
16793
18143
|
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
16794
18144
|
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
16795
18145
|
const limit = Math.min(MAX_LIMIT, Math.max(1, Math.trunc(query2.limit ?? DEFAULT_LIMIT)));
|
|
@@ -16817,7 +18167,7 @@ function readAuditRecords(auditDir, query2 = {}) {
|
|
|
16817
18167
|
}
|
|
16818
18168
|
|
|
16819
18169
|
// src/audit/AuditWriter.ts
|
|
16820
|
-
var
|
|
18170
|
+
var import_node_fs32 = require("fs");
|
|
16821
18171
|
var import_node_path32 = require("path");
|
|
16822
18172
|
var AuditWriter = class {
|
|
16823
18173
|
constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
|
|
@@ -16865,7 +18215,7 @@ var AuditWriter = class {
|
|
|
16865
18215
|
/** Create a directory once per process and remember it. */
|
|
16866
18216
|
ensureDir(path2) {
|
|
16867
18217
|
if (!this.ensuredDirs.has(path2)) {
|
|
16868
|
-
(0,
|
|
18218
|
+
(0, import_node_fs32.mkdirSync)(path2, { recursive: true });
|
|
16869
18219
|
this.ensuredDirs.add(path2);
|
|
16870
18220
|
}
|
|
16871
18221
|
return path2;
|
|
@@ -16875,8 +18225,8 @@ var AuditWriter = class {
|
|
|
16875
18225
|
const { requestBody: _req, responseBody: _res, ...meta } = record;
|
|
16876
18226
|
const file = (0, import_node_path32.join)(dayPath, AUDIT_META_FILE);
|
|
16877
18227
|
const line = JSON.stringify(meta) + "\n";
|
|
16878
|
-
const bytesBefore = (0,
|
|
16879
|
-
(0,
|
|
18228
|
+
const bytesBefore = (0, import_node_fs32.existsSync)(file) ? (0, import_node_fs32.statSync)(file).size : 0;
|
|
18229
|
+
(0, import_node_fs32.appendFileSync)(file, line, "utf8");
|
|
16880
18230
|
try {
|
|
16881
18231
|
updateAuditStatsAfterAppend(
|
|
16882
18232
|
file,
|
|
@@ -16908,7 +18258,7 @@ var AuditWriter = class {
|
|
|
16908
18258
|
const line = encodeBodyEntry(record, sessionKey, dayDir, this.bases);
|
|
16909
18259
|
if (line === null) return;
|
|
16910
18260
|
const bodiesPath = this.ensureDir((0, import_node_path32.join)(dayPath, AUDIT_BODIES_DIR));
|
|
16911
|
-
(0,
|
|
18261
|
+
(0, import_node_fs32.appendFileSync)((0, import_node_path32.join)(bodiesPath, auditBodyFileName(sessionKey)), line + "\n", "utf8");
|
|
16912
18262
|
} catch (error) {
|
|
16913
18263
|
this.bases.forget(sessionKey);
|
|
16914
18264
|
this.logger.warn("[AuditWriter] failed to append audit body shard", {
|
|
@@ -16920,10 +18270,10 @@ var AuditWriter = class {
|
|
|
16920
18270
|
};
|
|
16921
18271
|
|
|
16922
18272
|
// src/billing/BillingPublisher.ts
|
|
16923
|
-
var
|
|
18273
|
+
var import_node_fs33 = require("fs");
|
|
16924
18274
|
var import_node_crypto24 = require("crypto");
|
|
16925
18275
|
var import_node_path33 = require("path");
|
|
16926
|
-
var
|
|
18276
|
+
var import_upstreamFetch11 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
16927
18277
|
|
|
16928
18278
|
// src/billing/billingFiles.ts
|
|
16929
18279
|
var BILLING_FILE_RE = /^billing-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
|
|
@@ -16946,7 +18296,7 @@ var BillingPublisher = class {
|
|
|
16946
18296
|
constructor(billingDir, logger, opts = {}) {
|
|
16947
18297
|
this.billingDir = billingDir;
|
|
16948
18298
|
this.logger = logger;
|
|
16949
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
18299
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch11.fetchUpstream)(url, init));
|
|
16950
18300
|
this.defer = opts.defer ?? ((fn) => setTimeout(fn, 0));
|
|
16951
18301
|
this.timeoutMs = opts.timeoutMs ?? BILLING_POST_TIMEOUT_MS;
|
|
16952
18302
|
this.now = opts.now ?? Date.now;
|
|
@@ -16994,7 +18344,7 @@ var BillingPublisher = class {
|
|
|
16994
18344
|
appendNow(event) {
|
|
16995
18345
|
this.ensureDir();
|
|
16996
18346
|
const file = (0, import_node_path33.join)(this.billingDir, billingFileName(event.ts));
|
|
16997
|
-
(0,
|
|
18347
|
+
(0, import_node_fs33.appendFileSync)(file, JSON.stringify(event) + "\n", "utf8");
|
|
16998
18348
|
}
|
|
16999
18349
|
/**
|
|
17000
18350
|
* One best-effort delivery attempt for an event ALREADY in the ledger. POSTs the
|
|
@@ -17044,7 +18394,7 @@ var BillingPublisher = class {
|
|
|
17044
18394
|
try {
|
|
17045
18395
|
this.ensureDir();
|
|
17046
18396
|
const file = (0, import_node_path33.join)(this.billingDir, deliveredFileName(event.ts));
|
|
17047
|
-
(0,
|
|
18397
|
+
(0, import_node_fs33.appendFileSync)(file, JSON.stringify({ id: event.id, deliveredAt: this.now() }) + "\n", "utf8");
|
|
17048
18398
|
} catch (error) {
|
|
17049
18399
|
this.logger.warn("[BillingPublisher] failed to append delivery marker", {
|
|
17050
18400
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -17053,20 +18403,20 @@ var BillingPublisher = class {
|
|
|
17053
18403
|
}
|
|
17054
18404
|
ensureDir() {
|
|
17055
18405
|
if (this.dirEnsured) return;
|
|
17056
|
-
(0,
|
|
18406
|
+
(0, import_node_fs33.mkdirSync)(this.billingDir, { recursive: true });
|
|
17057
18407
|
this.dirEnsured = true;
|
|
17058
18408
|
}
|
|
17059
18409
|
};
|
|
17060
18410
|
|
|
17061
18411
|
// src/billing/billingReader.ts
|
|
17062
|
-
var
|
|
18412
|
+
var import_node_fs34 = require("fs");
|
|
17063
18413
|
var import_node_path34 = require("path");
|
|
17064
18414
|
function readBillingLedger(billingDir) {
|
|
17065
18415
|
const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
|
|
17066
|
-
if (!(0,
|
|
18416
|
+
if (!(0, import_node_fs34.existsSync)(billingDir)) return view;
|
|
17067
18417
|
let files;
|
|
17068
18418
|
try {
|
|
17069
|
-
files = (0,
|
|
18419
|
+
files = (0, import_node_fs34.readdirSync)(billingDir);
|
|
17070
18420
|
} catch {
|
|
17071
18421
|
return view;
|
|
17072
18422
|
}
|
|
@@ -17097,7 +18447,7 @@ function readBillingStatus(billingDir) {
|
|
|
17097
18447
|
function parseLines(dir, file) {
|
|
17098
18448
|
let raw;
|
|
17099
18449
|
try {
|
|
17100
|
-
raw = (0,
|
|
18450
|
+
raw = (0, import_node_fs34.readFileSync)((0, import_node_path34.join)(dir, file), "utf8");
|
|
17101
18451
|
} catch {
|
|
17102
18452
|
return [];
|
|
17103
18453
|
}
|
|
@@ -17196,7 +18546,7 @@ var BillingRetrySweeper = class {
|
|
|
17196
18546
|
// src/TokenRefreshScheduler.ts
|
|
17197
18547
|
var REFRESH_LEAD_MS2 = 5 * 6e4;
|
|
17198
18548
|
var SWEEP_INTERVAL_MS5 = 6e4;
|
|
17199
|
-
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini"];
|
|
18549
|
+
var OAUTH_PROVIDERS2 = ["claude", "codex", "gemini", "kimi"];
|
|
17200
18550
|
var TokenRefreshScheduler = class {
|
|
17201
18551
|
constructor(store, logger, intervalMs = SWEEP_INTERVAL_MS5, leadMs = REFRESH_LEAD_MS2) {
|
|
17202
18552
|
this.store = store;
|
|
@@ -17279,6 +18629,8 @@ var TokenRefreshScheduler = class {
|
|
|
17279
18629
|
return this.store.refreshCodexToken();
|
|
17280
18630
|
case "gemini":
|
|
17281
18631
|
return this.store.refreshGeminiToken();
|
|
18632
|
+
case "kimi":
|
|
18633
|
+
return this.store.refreshKimiToken();
|
|
17282
18634
|
}
|
|
17283
18635
|
}
|
|
17284
18636
|
};
|
|
@@ -17355,7 +18707,7 @@ function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
|
17355
18707
|
|
|
17356
18708
|
// src/webhook/WebhookDispatcher.ts
|
|
17357
18709
|
var import_node_crypto25 = require("crypto");
|
|
17358
|
-
var
|
|
18710
|
+
var import_upstreamFetch12 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
17359
18711
|
var WEBHOOK_MAX_ATTEMPTS = 3;
|
|
17360
18712
|
var WEBHOOK_QUEUE_MAX = 1e3;
|
|
17361
18713
|
var WEBHOOK_SEND_TIMEOUT_MS = 1e4;
|
|
@@ -17375,7 +18727,7 @@ var WebhookDispatcher = class {
|
|
|
17375
18727
|
sleep;
|
|
17376
18728
|
now;
|
|
17377
18729
|
constructor(opts = {}) {
|
|
17378
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
18730
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch12.fetchUpstream)(url, init));
|
|
17379
18731
|
this.logger = opts.logger;
|
|
17380
18732
|
this.maxAttempts = opts.maxAttempts ?? WEBHOOK_MAX_ATTEMPTS;
|
|
17381
18733
|
this.queueMax = opts.queueMax ?? WEBHOOK_QUEUE_MAX;
|
|
@@ -17461,8 +18813,8 @@ var WebhookDispatcher = class {
|
|
|
17461
18813
|
signal: AbortSignal.timeout(this.timeoutMs)
|
|
17462
18814
|
});
|
|
17463
18815
|
return res.ok ? { ok: true, status: res.status } : { ok: false, status: res.status };
|
|
17464
|
-
} catch (
|
|
17465
|
-
return { ok: false, error:
|
|
18816
|
+
} catch (err6) {
|
|
18817
|
+
return { ok: false, error: err6 instanceof Error ? err6.message : String(err6) };
|
|
17466
18818
|
}
|
|
17467
18819
|
}
|
|
17468
18820
|
/**
|
|
@@ -17586,7 +18938,7 @@ function resetImageRuntimeBootstrapSession() {
|
|
|
17586
18938
|
function resolveLoggingConfig(configured, configPath) {
|
|
17587
18939
|
const file = configured?.file ?? defaultDaemonLogPath(configPath);
|
|
17588
18940
|
try {
|
|
17589
|
-
(0,
|
|
18941
|
+
(0, import_node_fs35.mkdirSync)(configured?.file ? (0, import_node_path35.dirname)(configured.file) : defaultLogDir(configPath), {
|
|
17590
18942
|
recursive: true
|
|
17591
18943
|
});
|
|
17592
18944
|
} catch {
|
|
@@ -17604,12 +18956,12 @@ function buildDaemon(config, paths) {
|
|
|
17604
18956
|
setSecretBox(secretBox3);
|
|
17605
18957
|
setSecretBox2(secretBox3);
|
|
17606
18958
|
const decryptedConfig = decryptConfigSecrets(config, secretBox3);
|
|
17607
|
-
const accountAllowanceStore = new
|
|
18959
|
+
const accountAllowanceStore = new import_AccountAllowanceStore7.AccountAllowanceStore(
|
|
17608
18960
|
Date.now,
|
|
17609
18961
|
void 0,
|
|
17610
18962
|
new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
|
|
17611
18963
|
);
|
|
17612
|
-
(0,
|
|
18964
|
+
(0, import_AccountAllowanceStore7.setSharedAccountAllowanceStore)(accountAllowanceStore);
|
|
17613
18965
|
(0, import_AccountAllowanceScheduling5.getSharedAccountAllowanceScheduling)().configure(
|
|
17614
18966
|
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
17615
18967
|
);
|
|
@@ -17634,21 +18986,22 @@ function buildDaemon(config, paths) {
|
|
|
17634
18986
|
claudeAllowanceRefreshScheduler.configure(
|
|
17635
18987
|
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
17636
18988
|
);
|
|
17637
|
-
const subscriptionAccounts = new
|
|
17638
|
-
(0,
|
|
17639
|
-
const subscriptionRegistry = new
|
|
18989
|
+
const subscriptionAccounts = new import_subscriptions9.SubscriptionAccountService(credentialStore);
|
|
18990
|
+
(0, import_subscriptions9.setSubscriptionAccountService)(subscriptionAccounts);
|
|
18991
|
+
const subscriptionRegistry = new import_subscriptions9.SubscriptionProviderRegistry(
|
|
17640
18992
|
subscriptionAccounts,
|
|
17641
18993
|
credentialStore
|
|
17642
18994
|
);
|
|
17643
|
-
(0,
|
|
18995
|
+
(0, import_subscriptions9.setSubscriptionProviderRegistry)(subscriptionRegistry);
|
|
17644
18996
|
setServerProxyConfig(decryptedConfig.server?.proxy);
|
|
17645
|
-
(0,
|
|
18997
|
+
(0, import_upstreamFetch13.setUpstreamProxyResolver)(
|
|
17646
18998
|
createUpstreamProxyResolver({
|
|
17647
18999
|
getAccountProxy: (providerId, accountId) => credentialStore.getAccountProxy(providerId, accountId)
|
|
17648
19000
|
})
|
|
17649
19001
|
);
|
|
17650
19002
|
(0, import_gemini_code_assist_resolver.setGeminiCodeAssistResolver)((0, import_GeminiCodeAssistProjectResolver.getGeminiCodeAssistProjectResolver)());
|
|
17651
19003
|
const autoDisableStore = new AutoDisableStore();
|
|
19004
|
+
const providerKeyQuotaService = new ProviderKeyQuotaService(secretBox3);
|
|
17652
19005
|
const apiKeyPool = new import_ApiKeyPoolService.ApiKeyPoolService(
|
|
17653
19006
|
createPoolKeysLoader((id) => llmConfig.getProviderRow(id), autoDisableStore),
|
|
17654
19007
|
resolveEnvKey,
|
|
@@ -17665,7 +19018,7 @@ function buildDaemon(config, paths) {
|
|
|
17665
19018
|
const pricingEngine = new import_usage2.PricingEngine(pricingStore, logger, {
|
|
17666
19019
|
// Catalog egress follows the same global/env proxy policy as every other
|
|
17667
19020
|
// daemon upstream call; no provider/account override applies here.
|
|
17668
|
-
fetchImpl: ((input, init) => (0,
|
|
19021
|
+
fetchImpl: ((input, init) => (0, import_upstreamFetch13.fetchUpstream)(String(input), init ?? {}))
|
|
17669
19022
|
});
|
|
17670
19023
|
const pricingRefreshScheduler = new PricingRefreshScheduler(
|
|
17671
19024
|
pricingEngine,
|
|
@@ -17929,6 +19282,11 @@ function buildDaemon(config, paths) {
|
|
|
17929
19282
|
// values themselves NEVER leave (masked via `maskProviderApiKey`).
|
|
17930
19283
|
apiKeyPool,
|
|
17931
19284
|
autoDisableStore,
|
|
19285
|
+
// BYO provider-key quota (Z.AI coding plan, MiniMax Token Plan, …): a
|
|
19286
|
+
// read-through cached same-key usage probe surfaced on the keys view. The
|
|
19287
|
+
// key plaintext is resolved + decrypted inside the service and never
|
|
19288
|
+
// crosses back out.
|
|
19289
|
+
providerKeyQuota: providerKeyQuotaService,
|
|
17932
19290
|
// Interactive OAuth login over admin HTTP (app-parity child 4, design
|
|
17933
19291
|
// D1/D2-a). The in-memory pending-session store (NEVER serialized), the
|
|
17934
19292
|
// injected token-exchange fetch (global `fetch` here; mocked in tests), and a
|
|
@@ -17945,7 +19303,7 @@ function buildDaemon(config, paths) {
|
|
|
17945
19303
|
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
17946
19304
|
// excluded from the upstream trace, so a failing login left no evidence.
|
|
17947
19305
|
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
17948
|
-
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0,
|
|
19306
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0, import_upstreamFetch13.fetchUpstream)(url, init, { providerId, redactBodies: true }),
|
|
17949
19307
|
subscriptionAccountAppender: credentialStore,
|
|
17950
19308
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
17951
19309
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -17953,6 +19311,10 @@ function buildDaemon(config, paths) {
|
|
|
17953
19311
|
// can inject a mock so no real port is bound.
|
|
17954
19312
|
codexSessions: new CodexOAuthSessionStore(),
|
|
17955
19313
|
codexAwaitLoopback: paths.codexAwaitLoopback ?? ((state, timeoutMs, signal) => awaitLoopbackCode(state, timeoutMs, signal)),
|
|
19314
|
+
// Kimi interactive OAuth — the async DEVICE-CODE flow store (no port, no
|
|
19315
|
+
// paste; the app shows the verification URL + user code and polls the
|
|
19316
|
+
// token-free status). Token captured + persisted daemon-side.
|
|
19317
|
+
kimiSessions: new CodexOAuthSessionStore(),
|
|
17956
19318
|
// Migration pack (app-parity child 6, design D2/D3) — the concrete credential
|
|
17957
19319
|
// store provides BOTH the full DECRYPTED read (`getFullConfig`, export) and
|
|
17958
19320
|
// the multi-account append (`appendProviderAccount`, import re-encrypts at-
|
|
@@ -18011,7 +19373,7 @@ function buildDaemon(config, paths) {
|
|
|
18011
19373
|
});
|
|
18012
19374
|
const webhookDispatcher = new WebhookDispatcher({
|
|
18013
19375
|
logger,
|
|
18014
|
-
fetchImpl: (url, init) => (0,
|
|
19376
|
+
fetchImpl: (url, init) => (0, import_upstreamFetch13.fetchUpstream)(url, init)
|
|
18015
19377
|
});
|
|
18016
19378
|
setWebhookRuntime(webhookDispatcher, (0, import_SubscriptionAccountHealth4.getSharedAccountHealth)());
|
|
18017
19379
|
const auditWriter = new AuditWriter(auditDir, logger);
|
|
@@ -18091,9 +19453,9 @@ function resetDaemonSingletonsForTests() {
|
|
|
18091
19453
|
(0, import_provider_proxy4.__resetProviderProxyForTests)();
|
|
18092
19454
|
(0, import_outbound_api10.__resetOutboundApiServerForTests)();
|
|
18093
19455
|
(0, import_subscriptionRegistryPort.setSubscriptionRegistryForOutbound)(null);
|
|
18094
|
-
(0,
|
|
18095
|
-
(0,
|
|
18096
|
-
(0,
|
|
19456
|
+
(0, import_subscriptions9.setSubscriptionProviderRegistry)(null);
|
|
19457
|
+
(0, import_subscriptions9.setSubscriptionAccountService)(null);
|
|
19458
|
+
(0, import_upstreamFetch13.setUpstreamProxyResolver)(null);
|
|
18097
19459
|
setServerProxyConfig(void 0);
|
|
18098
19460
|
(0, import_gemini_code_assist_resolver.setGeminiCodeAssistResolver)(null);
|
|
18099
19461
|
setSecretBox(null);
|
|
@@ -18102,14 +19464,14 @@ function resetDaemonSingletonsForTests() {
|
|
|
18102
19464
|
resetAuditRuntimeForTests();
|
|
18103
19465
|
resetBillingRuntimeForTests();
|
|
18104
19466
|
(0, import_SubscriptionIdentityStore3.__resetSharedIdentityStoreForTests)();
|
|
18105
|
-
(0,
|
|
19467
|
+
(0, import_AccountAllowanceStore7.__resetSharedAccountAllowanceStoreForTests)();
|
|
18106
19468
|
(0, import_AccountAllowanceScheduling5.__resetSharedAccountAllowanceSchedulingForTests)();
|
|
18107
19469
|
(0, import_usage2.__resetSharedUsageThroughputTrackerForTests)();
|
|
18108
19470
|
}
|
|
18109
19471
|
function isTokensStoreReadable(tokensPath) {
|
|
18110
19472
|
try {
|
|
18111
|
-
if (!(0,
|
|
18112
|
-
(0,
|
|
19473
|
+
if (!(0, import_node_fs35.existsSync)(tokensPath)) return true;
|
|
19474
|
+
(0, import_node_fs35.accessSync)(tokensPath, import_node_fs35.constants.R_OK);
|
|
18113
19475
|
return true;
|
|
18114
19476
|
} catch {
|
|
18115
19477
|
return false;
|