@nick3/copilot-api 1.7.1 → 1.9.15
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/README.md +111 -87
- package/README.zh-CN.md +111 -84
- package/dist/{account-B4EBsn8H.js → account-DjCbqJ2Q.js} +20 -24
- package/dist/account-DjCbqJ2Q.js.map +1 -0
- package/dist/admin/assets/index-BRnD4-DB.js +110 -0
- package/dist/admin/assets/index-CBMFCvqO.css +1 -0
- package/dist/admin/index.html +2 -2
- package/dist/{auth-xiepl3lk.js → auth--I1utaB6.js} +103 -112
- package/dist/auth--I1utaB6.js.map +1 -0
- package/dist/{check-usage-3sFXqP0F.js → check-usage-DHvjdha4.js} +6 -7
- package/dist/{check-usage-3sFXqP0F.js.map → check-usage-DHvjdha4.js.map} +1 -1
- package/dist/{debug-BJfZVBB7.js → debug-BMo6ltbp.js} +6 -6
- package/dist/debug-BMo6ltbp.js.map +1 -0
- package/dist/{get-copilot-token-COIPGosP.js → get-copilot-token-ZbmbVF0I.js} +9 -5
- package/dist/get-copilot-token-ZbmbVF0I.js.map +1 -0
- package/dist/main.js +6 -7
- package/dist/main.js.map +1 -1
- package/dist/{paths-DGlr310R.js → paths-CclKwouX.js} +3 -5
- package/dist/{paths-DGlr310R.js.map → paths-CclKwouX.js.map} +1 -1
- package/dist/{poll-access-token-c2IYJJHM.js → poll-access-token-CIPDXrcm.js} +9 -21
- package/dist/poll-access-token-CIPDXrcm.js.map +1 -0
- package/dist/{accounts-manager-Ca9IG0Fv.js → quota-refresh-scheduler-runtime-XD2fDa2K.js} +434 -51
- package/dist/quota-refresh-scheduler-runtime-XD2fDa2K.js.map +1 -0
- package/dist/{request-outbound-qyTeXbzy.js → request-outbound-CxvpSkOn.js} +14 -9
- package/dist/request-outbound-CxvpSkOn.js.map +1 -0
- package/dist/request-outbound-Cy6huWjK.js +2 -0
- package/dist/{server-DT8b_Z5j.js → server-BDCnb3Ao.js} +1760 -975
- package/dist/server-BDCnb3Ao.js.map +1 -0
- package/dist/{start-Dayo0B2n.js → start-DkBnp9d8.js} +12 -13
- package/dist/start-DkBnp9d8.js.map +1 -0
- package/package.json +20 -7
- package/dist/account-B4EBsn8H.js.map +0 -1
- package/dist/accounts-manager-Ca9IG0Fv.js.map +0 -1
- package/dist/admin/assets/index-8eGib92I.js +0 -107
- package/dist/admin/assets/index-B2qj1asn.css +0 -1
- package/dist/auth-xiepl3lk.js.map +0 -1
- package/dist/debug-BJfZVBB7.js.map +0 -1
- package/dist/get-copilot-token-COIPGosP.js.map +0 -1
- package/dist/poll-access-token-c2IYJJHM.js.map +0 -1
- package/dist/request-outbound-DhI9-SrV.js +0 -4
- package/dist/request-outbound-qyTeXbzy.js.map +0 -1
- package/dist/server-DT8b_Z5j.js.map +0 -1
- package/dist/start-Dayo0B2n.js.map +0 -1
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { O as accountFromState, _ as HTTPError, g as getCopilotUsage, j as consumeOutboundHeadersSnapshot, m as getGitHubUser, x as copilotModelsHeaders, y as copilotBaseUrl } from "./poll-access-token-
|
|
2
|
-
import { b as getCurrentIdentityEnvironment, c as isAccountEnabled, f as readLegacyToken, h as saveAccountToken, i as ensureAccountClientIdentity, l as listAccountsFromRegistry, o as hasLegacyToken, r as addAccountToRegistry, s as hasRegistry, u as loadAccountToken, v as buildIdentityKey, y as createAccountSessionId } from "./account-
|
|
3
|
-
import { t as PATHS } from "./paths-
|
|
4
|
-
import { t as getCopilotToken } from "./get-copilot-token-
|
|
5
|
-
import { c as getAdminDbUserVersion, i as getRequestOutboundStore, o as getAdminDb, s as getAdminDbPath } from "./request-outbound-
|
|
1
|
+
import { M as requestContext, O as accountFromState, _ as HTTPError, g as getCopilotUsage, j as consumeOutboundHeadersSnapshot, m as getGitHubUser, x as copilotModelsHeaders, y as copilotBaseUrl } from "./poll-access-token-CIPDXrcm.js";
|
|
2
|
+
import { b as getCurrentIdentityEnvironment, c as isAccountEnabled, f as readLegacyToken, h as saveAccountToken, i as ensureAccountClientIdentity, l as listAccountsFromRegistry, o as hasLegacyToken, r as addAccountToRegistry, s as hasRegistry, u as loadAccountToken, v as buildIdentityKey, y as createAccountSessionId } from "./account-DjCbqJ2Q.js";
|
|
3
|
+
import { t as PATHS } from "./paths-CclKwouX.js";
|
|
4
|
+
import { t as getCopilotToken } from "./get-copilot-token-ZbmbVF0I.js";
|
|
5
|
+
import { c as getAdminDbUserVersion, i as getRequestOutboundStore, o as getAdminDb, s as getAdminDbPath } from "./request-outbound-CxvpSkOn.js";
|
|
6
6
|
import consola, { consola as consola$1 } from "consola";
|
|
7
7
|
import fs from "node:fs/promises";
|
|
8
8
|
import fs$1 from "node:fs";
|
|
9
|
-
|
|
10
9
|
//#region src/lib/config.ts
|
|
11
10
|
const PROVIDER_TYPE_ANTHROPIC = "anthropic";
|
|
12
11
|
const gpt5ExplorationPrompt = `## Exploration and reading files
|
|
@@ -15,6 +14,14 @@ const gpt5ExplorationPrompt = `## Exploration and reading files
|
|
|
15
14
|
- **multi_tool_use.parallel** Use multi_tool_use.parallel to parallelize tool calls and only this.
|
|
16
15
|
- **Only make sequential calls if you truly cannot know the next file without seeing a result first.**
|
|
17
16
|
- **Workflow:** (a) plan all needed reads → (b) issue one parallel batch → (c) analyze results → (d) repeat if new, unpredictable reads arise.`;
|
|
17
|
+
const DEFAULT_QUOTA_REFRESH_CONFIG = {
|
|
18
|
+
enabled: true,
|
|
19
|
+
intervalMinutes: 360,
|
|
20
|
+
startupDelaySeconds: 60,
|
|
21
|
+
staggerMinSeconds: 2,
|
|
22
|
+
staggerMaxSeconds: 5
|
|
23
|
+
};
|
|
24
|
+
const MIN_QUOTA_REFRESH_INTERVAL_MINUTES = 30;
|
|
18
25
|
const gpt5CommentaryPrompt = `# Working with the user
|
|
19
26
|
|
|
20
27
|
You interact with the user through a terminal. You have 2 ways of communicating with the users:
|
|
@@ -69,7 +76,8 @@ const defaultConfig = {
|
|
|
69
76
|
capture4xx: false,
|
|
70
77
|
capture5xx: false,
|
|
71
78
|
captureOther: false
|
|
72
|
-
}
|
|
79
|
+
},
|
|
80
|
+
quotaRefresh: DEFAULT_QUOTA_REFRESH_CONFIG
|
|
73
81
|
};
|
|
74
82
|
let cachedConfig = null;
|
|
75
83
|
function isPlainObject(value) {
|
|
@@ -85,6 +93,23 @@ function normalizeNonNegativeNumber(value) {
|
|
|
85
93
|
if (value < 0) return void 0;
|
|
86
94
|
return value;
|
|
87
95
|
}
|
|
96
|
+
function normalizeQuotaRefreshIntervalMinutes(value) {
|
|
97
|
+
const minutes = normalizeNonNegativeNumber(value) ?? DEFAULT_QUOTA_REFRESH_CONFIG.intervalMinutes;
|
|
98
|
+
if (minutes > 0 && minutes < MIN_QUOTA_REFRESH_INTERVAL_MINUTES) return MIN_QUOTA_REFRESH_INTERVAL_MINUTES;
|
|
99
|
+
return minutes;
|
|
100
|
+
}
|
|
101
|
+
function normalizeQuotaRefreshConfig(value) {
|
|
102
|
+
const raw = isPlainObject(value) ? value : {};
|
|
103
|
+
const staggerMinSeconds = normalizeNonNegativeNumber(raw.staggerMinSeconds) ?? DEFAULT_QUOTA_REFRESH_CONFIG.staggerMinSeconds;
|
|
104
|
+
const rawStaggerMaxSeconds = normalizeNonNegativeNumber(raw.staggerMaxSeconds) ?? DEFAULT_QUOTA_REFRESH_CONFIG.staggerMaxSeconds;
|
|
105
|
+
return {
|
|
106
|
+
enabled: typeof raw.enabled === "boolean" ? raw.enabled : DEFAULT_QUOTA_REFRESH_CONFIG.enabled,
|
|
107
|
+
intervalMinutes: normalizeQuotaRefreshIntervalMinutes(raw.intervalMinutes),
|
|
108
|
+
startupDelaySeconds: normalizeNonNegativeNumber(raw.startupDelaySeconds) ?? DEFAULT_QUOTA_REFRESH_CONFIG.startupDelaySeconds,
|
|
109
|
+
staggerMinSeconds,
|
|
110
|
+
staggerMaxSeconds: Math.max(staggerMinSeconds, rawStaggerMaxSeconds)
|
|
111
|
+
};
|
|
112
|
+
}
|
|
88
113
|
const LOG_LEVELS = new Set([
|
|
89
114
|
"error",
|
|
90
115
|
"warn",
|
|
@@ -251,6 +276,20 @@ function mergeDefaultDevMode(config) {
|
|
|
251
276
|
changed: true
|
|
252
277
|
};
|
|
253
278
|
}
|
|
279
|
+
function mergeDefaultQuotaRefresh(config) {
|
|
280
|
+
const quotaRefresh = normalizeQuotaRefreshConfig(config.quotaRefresh);
|
|
281
|
+
if (JSON.stringify(config.quotaRefresh) === JSON.stringify(quotaRefresh)) return {
|
|
282
|
+
mergedConfig: config,
|
|
283
|
+
changed: false
|
|
284
|
+
};
|
|
285
|
+
return {
|
|
286
|
+
mergedConfig: {
|
|
287
|
+
...config,
|
|
288
|
+
quotaRefresh
|
|
289
|
+
},
|
|
290
|
+
changed: true
|
|
291
|
+
};
|
|
292
|
+
}
|
|
254
293
|
function applyConfigMerges(config, mergeFns) {
|
|
255
294
|
return mergeFns.reduce((acc, mergeFn) => {
|
|
256
295
|
const result = mergeFn(acc.mergedConfig);
|
|
@@ -271,7 +310,8 @@ function mergeConfigWithDefaults() {
|
|
|
271
310
|
mergeDefaultModelRefreshInterval,
|
|
272
311
|
mergeDefaultSessionAffinityRetention,
|
|
273
312
|
mergeDefaultLogLevel,
|
|
274
|
-
mergeDefaultDevMode
|
|
313
|
+
mergeDefaultDevMode,
|
|
314
|
+
mergeDefaultQuotaRefresh
|
|
275
315
|
]);
|
|
276
316
|
if (changed) try {
|
|
277
317
|
fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(mergedConfig, null, 2)}\n`, "utf8");
|
|
@@ -295,8 +335,8 @@ function normalizeAliasTarget(value) {
|
|
|
295
335
|
}
|
|
296
336
|
function normalizeAliasSpec(value) {
|
|
297
337
|
if (typeof value === "string") {
|
|
298
|
-
const normalizedTarget
|
|
299
|
-
return normalizedTarget
|
|
338
|
+
const normalizedTarget = normalizeAliasTarget(value);
|
|
339
|
+
return normalizedTarget ? { target: normalizedTarget } : null;
|
|
300
340
|
}
|
|
301
341
|
if (!value || typeof value !== "object") return null;
|
|
302
342
|
const targetValue = value.target;
|
|
@@ -402,6 +442,9 @@ function getModelRefreshIntervalMs() {
|
|
|
402
442
|
if (!Number.isFinite(hours) || hours <= 0) return 0;
|
|
403
443
|
return hours * 60 * 60 * 1e3;
|
|
404
444
|
}
|
|
445
|
+
function getQuotaRefreshConfig() {
|
|
446
|
+
return normalizeQuotaRefreshConfig(getConfig().quotaRefresh);
|
|
447
|
+
}
|
|
405
448
|
function getSessionAffinityRetentionDays() {
|
|
406
449
|
return normalizeNonNegativeNumber(getConfig().sessionAffinityRetentionDays) ?? defaultConfig.sessionAffinityRetentionDays ?? 7;
|
|
407
450
|
}
|
|
@@ -435,11 +478,15 @@ function isForceAgentEnabled() {
|
|
|
435
478
|
function normalizeProviderBaseUrl(url) {
|
|
436
479
|
return url.trim().replace(/\/+$/u, "");
|
|
437
480
|
}
|
|
438
|
-
function
|
|
439
|
-
|
|
481
|
+
function getDefaultProviderAuthType(providerType) {
|
|
482
|
+
return providerType === "openai-compatible" ? "authorization" : "x-api-key";
|
|
483
|
+
}
|
|
484
|
+
function resolveProviderAuthType(providerName, authType, providerType) {
|
|
485
|
+
if (authType === void 0) return getDefaultProviderAuthType(providerType);
|
|
486
|
+
if (authType === "x-api-key") return "x-api-key";
|
|
440
487
|
if (authType === "authorization") return authType;
|
|
441
|
-
consola.warn(`Provider ${providerName} has invalid authType '${authType}',
|
|
442
|
-
return
|
|
488
|
+
consola.warn(`Provider ${providerName} has invalid authType '${authType}', falling back to ${getDefaultProviderAuthType(providerType)}`);
|
|
489
|
+
return getDefaultProviderAuthType(providerType);
|
|
443
490
|
}
|
|
444
491
|
function getProviderConfig(name) {
|
|
445
492
|
const providerName = name.trim();
|
|
@@ -447,21 +494,21 @@ function getProviderConfig(name) {
|
|
|
447
494
|
const provider = getConfig().providers?.[providerName];
|
|
448
495
|
if (!provider) return null;
|
|
449
496
|
if (provider.enabled === false) return null;
|
|
450
|
-
|
|
451
|
-
|
|
497
|
+
const type = provider.type ?? "anthropic";
|
|
498
|
+
if (type !== "anthropic" && type !== "openai-compatible") {
|
|
499
|
+
consola.warn(`Provider ${providerName} is ignored because type '${type}' is not supported`);
|
|
452
500
|
return null;
|
|
453
501
|
}
|
|
454
502
|
const baseUrl = normalizeProviderBaseUrl(provider.baseUrl ?? "");
|
|
455
503
|
const apiKey = (provider.apiKey ?? "").trim();
|
|
456
|
-
const authType = resolveProviderAuthType(providerName, provider.authType);
|
|
457
|
-
if (!authType) return null;
|
|
504
|
+
const authType = resolveProviderAuthType(providerName, provider.authType, type);
|
|
458
505
|
if (!baseUrl || !apiKey) {
|
|
459
506
|
consola.warn(`Provider ${providerName} is enabled but missing baseUrl or apiKey`);
|
|
460
507
|
return null;
|
|
461
508
|
}
|
|
462
509
|
return {
|
|
463
510
|
name: providerName,
|
|
464
|
-
type
|
|
511
|
+
type,
|
|
465
512
|
baseUrl,
|
|
466
513
|
apiKey,
|
|
467
514
|
authType,
|
|
@@ -481,7 +528,6 @@ function isResponsesApiWebSearchEnabled() {
|
|
|
481
528
|
function getClaudeTokenMultiplier() {
|
|
482
529
|
return getConfig().claudeTokenMultiplier ?? 1.15;
|
|
483
530
|
}
|
|
484
|
-
|
|
485
531
|
//#endregion
|
|
486
532
|
//#region src/lib/account-affinity.ts
|
|
487
533
|
const DEFAULT_MAX_ENTRIES$1 = 1e4;
|
|
@@ -514,6 +560,31 @@ var AccountAffinityCache = class {
|
|
|
514
560
|
this.setMemory(key, accountId);
|
|
515
561
|
return accountId;
|
|
516
562
|
}
|
|
563
|
+
getMany(keys) {
|
|
564
|
+
const result = /* @__PURE__ */ new Map();
|
|
565
|
+
const missingKeys = new Array();
|
|
566
|
+
const now = Date.now();
|
|
567
|
+
for (const key of keys) {
|
|
568
|
+
if (result.has(key)) continue;
|
|
569
|
+
const entry = this.cache.get(key);
|
|
570
|
+
if (!entry) {
|
|
571
|
+
missingKeys.push(key);
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
if (now >= entry.expiresAt) {
|
|
575
|
+
this.cache.delete(key);
|
|
576
|
+
missingKeys.push(key);
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
result.set(key, entry.accountId);
|
|
580
|
+
}
|
|
581
|
+
if (missingKeys.length === 0) return result;
|
|
582
|
+
const persistentEntries = this.readPersistentEntries(missingKeys);
|
|
583
|
+
for (const [key, accountId] of persistentEntries) this.setMemory(key, accountId);
|
|
584
|
+
if (result.size === 0) return persistentEntries;
|
|
585
|
+
for (const [key, accountId] of persistentEntries) result.set(key, accountId);
|
|
586
|
+
return result;
|
|
587
|
+
}
|
|
517
588
|
/** Record a successful account mapping. Refreshes TTL and moves the entry to the newest position. */
|
|
518
589
|
set(key, accountId) {
|
|
519
590
|
this.setMemory(key, accountId);
|
|
@@ -538,7 +609,7 @@ var AccountAffinityCache = class {
|
|
|
538
609
|
get size() {
|
|
539
610
|
return this.cache.size;
|
|
540
611
|
}
|
|
541
|
-
getPersistentStore() {
|
|
612
|
+
getPersistentStore(options = {}) {
|
|
542
613
|
if (this.persistentStore) return this.persistentStore;
|
|
543
614
|
if (!this.persistentStoreProvider) return;
|
|
544
615
|
try {
|
|
@@ -546,7 +617,10 @@ var AccountAffinityCache = class {
|
|
|
546
617
|
if (store) this.persistentStore = store;
|
|
547
618
|
return store;
|
|
548
619
|
} catch (error) {
|
|
549
|
-
|
|
620
|
+
if (options.throwOnProviderFailure) {
|
|
621
|
+
consola.error("Failed to resolve affinity persistence store:", error);
|
|
622
|
+
throw new Error("Affinity persistence store provider failed");
|
|
623
|
+
}
|
|
550
624
|
consola.warn("Failed to resolve affinity persistence store:", error);
|
|
551
625
|
return;
|
|
552
626
|
}
|
|
@@ -561,6 +635,27 @@ var AccountAffinityCache = class {
|
|
|
561
635
|
return;
|
|
562
636
|
}
|
|
563
637
|
}
|
|
638
|
+
readPersistentEntries(keys) {
|
|
639
|
+
const store = this.getPersistentStore({ throwOnProviderFailure: true });
|
|
640
|
+
if (!store) return /* @__PURE__ */ new Map();
|
|
641
|
+
if (store.getMany) try {
|
|
642
|
+
return store.getMany(keys);
|
|
643
|
+
} catch (error) {
|
|
644
|
+
consola.warn("Failed to batch-read affinity mappings from persistent store:", error);
|
|
645
|
+
throw new Error(`Affinity persistent store batch lookup failed for ${keys.length} keys`);
|
|
646
|
+
}
|
|
647
|
+
const result = /* @__PURE__ */ new Map();
|
|
648
|
+
const failedKeys = new Array();
|
|
649
|
+
for (const key of keys) try {
|
|
650
|
+
const accountId = store.get(key);
|
|
651
|
+
if (accountId) result.set(key, accountId);
|
|
652
|
+
} catch (error) {
|
|
653
|
+
failedKeys.push(key);
|
|
654
|
+
consola.warn("Failed to read affinity mapping from persistent store:", error);
|
|
655
|
+
}
|
|
656
|
+
if (failedKeys.length > 0) throw new Error(`Affinity persistent store lookup failed for ${failedKeys.length}/${keys.length} keys`);
|
|
657
|
+
return result;
|
|
658
|
+
}
|
|
564
659
|
writePersistentEntry(key, accountId) {
|
|
565
660
|
const store = this.getPersistentStore();
|
|
566
661
|
if (!store) return;
|
|
@@ -627,7 +722,6 @@ function isAffinityAccountUsable(accountId, accounts) {
|
|
|
627
722
|
if (account.failed) return void 0;
|
|
628
723
|
return account;
|
|
629
724
|
}
|
|
630
|
-
|
|
631
725
|
//#endregion
|
|
632
726
|
//#region src/lib/dev-mode.ts
|
|
633
727
|
function isDevModeEnabled() {
|
|
@@ -642,7 +736,6 @@ function isCapture5xxEnabled() {
|
|
|
642
736
|
function isCaptureOtherEnabled() {
|
|
643
737
|
return getConfig().devMode?.captureOther === true;
|
|
644
738
|
}
|
|
645
|
-
|
|
646
739
|
//#endregion
|
|
647
740
|
//#region src/services/copilot/copilot-fetch.ts
|
|
648
741
|
const pendingCaptures = /* @__PURE__ */ new Map();
|
|
@@ -669,14 +762,14 @@ async function flushPendingCapture(requestId) {
|
|
|
669
762
|
function snapshotHeaders(init) {
|
|
670
763
|
const headers = init.headers;
|
|
671
764
|
if (headers instanceof Headers) {
|
|
672
|
-
const out
|
|
673
|
-
for (const [key, value] of headers.entries()) out
|
|
674
|
-
return out
|
|
765
|
+
const out = {};
|
|
766
|
+
for (const [key, value] of headers.entries()) out[key] = value;
|
|
767
|
+
return out;
|
|
675
768
|
}
|
|
676
769
|
if (Array.isArray(headers)) {
|
|
677
|
-
const out
|
|
678
|
-
for (const [key, value] of headers) out
|
|
679
|
-
return out
|
|
770
|
+
const out = {};
|
|
771
|
+
for (const [key, value] of headers) out[key] = value;
|
|
772
|
+
return out;
|
|
680
773
|
}
|
|
681
774
|
const out = {};
|
|
682
775
|
for (const [key, value] of Object.entries(headers ?? {})) out[key] = value;
|
|
@@ -725,7 +818,7 @@ async function copilotFetch(input, init, ctx) {
|
|
|
725
818
|
headers: snapshotHeaders(init),
|
|
726
819
|
...snapshotBody(init)
|
|
727
820
|
};
|
|
728
|
-
const response = await fetch(input, init);
|
|
821
|
+
const response = await (ctx.fetchImpl ?? requestContext.getStore()?.fetchImpl ?? fetch)(input, init);
|
|
729
822
|
if (!(ctx.requestId !== void 0 && ctx.capturable !== false && shouldCaptureStatus(response.status))) return response;
|
|
730
823
|
const contentType = response.headers.get("content-type") ?? "";
|
|
731
824
|
const isSSE = contentType.includes("text/event-stream");
|
|
@@ -801,7 +894,6 @@ function persistNow({ requestSnapshot, status, responseBody, responseBodyKind, r
|
|
|
801
894
|
consola.debug("copilotFetch persist failed", error);
|
|
802
895
|
}
|
|
803
896
|
}
|
|
804
|
-
|
|
805
897
|
//#endregion
|
|
806
898
|
//#region src/services/copilot/get-models.ts
|
|
807
899
|
const getModels = async (account, options) => {
|
|
@@ -826,7 +918,6 @@ const getModels = async (account, options) => {
|
|
|
826
918
|
} catch {}
|
|
827
919
|
return models;
|
|
828
920
|
};
|
|
829
|
-
|
|
830
921
|
//#endregion
|
|
831
922
|
//#region src/lib/accounts-manager-auth.ts
|
|
832
923
|
const takeAuthSnapshot = (account) => ({
|
|
@@ -895,7 +986,6 @@ const applyUnauthorizedIfCurrent = (account, snapshot, reason) => {
|
|
|
895
986
|
setAccountFailedState(account, reason);
|
|
896
987
|
return true;
|
|
897
988
|
};
|
|
898
|
-
|
|
899
989
|
//#endregion
|
|
900
990
|
//#region src/lib/accounts-manager-quota.ts
|
|
901
991
|
const getCostUnits = (model) => {
|
|
@@ -930,10 +1020,6 @@ const releasePremiumReservation = (account, reservation) => {
|
|
|
930
1020
|
account.premiumReserved = Math.max(0, nextReserved);
|
|
931
1021
|
if (reservations.size === 0) account.premiumReservations = void 0;
|
|
932
1022
|
};
|
|
933
|
-
|
|
934
|
-
//#endregion
|
|
935
|
-
//#region src/lib/stats-store.ts
|
|
936
|
-
const DEFAULT_STATS_RETENTION_DAYS = 180;
|
|
937
1023
|
function toLocalDateString(ms) {
|
|
938
1024
|
const d = new Date(ms);
|
|
939
1025
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
@@ -1082,7 +1168,7 @@ var StatsStore = class {
|
|
|
1082
1168
|
});
|
|
1083
1169
|
return this.mergeMetricsAndConsumption(dailyMetrics, byAccountMetrics, consumption);
|
|
1084
1170
|
}
|
|
1085
|
-
cleanupStatsRetention(retentionDays =
|
|
1171
|
+
cleanupStatsRetention(retentionDays = 180) {
|
|
1086
1172
|
try {
|
|
1087
1173
|
const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
1088
1174
|
const cutoffDate = toLocalDateString(cutoffMs);
|
|
@@ -1151,7 +1237,6 @@ var StatsStore = class {
|
|
|
1151
1237
|
};
|
|
1152
1238
|
}
|
|
1153
1239
|
};
|
|
1154
|
-
|
|
1155
1240
|
//#endregion
|
|
1156
1241
|
//#region src/lib/request-history.ts
|
|
1157
1242
|
const DEFAULT_RETENTION_DAYS = 35;
|
|
@@ -1299,6 +1384,8 @@ function buildInsertArgs(record) {
|
|
|
1299
1384
|
toDbNull(record.affinityKeyUsed),
|
|
1300
1385
|
toDbNull(record.affinityKeySource),
|
|
1301
1386
|
toDbNull(record.selectionReason),
|
|
1387
|
+
toDbNull(record.responsesItemOwnerLookupKeysJson),
|
|
1388
|
+
toDbNull(record.responsesItemOwnerRecordedKeysJson),
|
|
1302
1389
|
toDbNull(record.tokensInput),
|
|
1303
1390
|
toDbNull(record.tokensOutput),
|
|
1304
1391
|
toDbNull(record.tokensTotal),
|
|
@@ -1359,6 +1446,8 @@ var RequestHistoryStore = class {
|
|
|
1359
1446
|
"affinity_key_used",
|
|
1360
1447
|
"affinity_key_source",
|
|
1361
1448
|
"selection_reason",
|
|
1449
|
+
"responses_item_owner_lookup_keys_json",
|
|
1450
|
+
"responses_item_owner_recorded_keys_json",
|
|
1362
1451
|
"tokens_input",
|
|
1363
1452
|
"tokens_output",
|
|
1364
1453
|
"tokens_total",
|
|
@@ -1535,7 +1624,7 @@ var RequestHistoryStore = class {
|
|
|
1535
1624
|
} catch (error) {
|
|
1536
1625
|
consola.debug("Failed to cleanup request_log retention", error);
|
|
1537
1626
|
}
|
|
1538
|
-
import("./request-outbound-
|
|
1627
|
+
import("./request-outbound-Cy6huWjK.js").then((m) => m.getRequestOutboundStore().cleanupOrphans()).catch(() => {});
|
|
1539
1628
|
}
|
|
1540
1629
|
meta() {
|
|
1541
1630
|
return {
|
|
@@ -1627,15 +1716,18 @@ function extractResponsesUsageFromResult(result) {
|
|
|
1627
1716
|
function getStatsStore() {
|
|
1628
1717
|
return getStatsStoreInstance();
|
|
1629
1718
|
}
|
|
1630
|
-
|
|
1631
1719
|
//#endregion
|
|
1632
1720
|
//#region src/lib/session-affinity-store.ts
|
|
1633
1721
|
const DAY_MS = 1440 * 60 * 1e3;
|
|
1634
1722
|
const DEFAULT_MAX_AGE_MS = 7 * DAY_MS;
|
|
1635
1723
|
const CLEANUP_INTERVAL_MS = DAY_MS;
|
|
1724
|
+
const MAX_BATCH_KEYS = 500;
|
|
1636
1725
|
const maybeUnref = (timer) => {
|
|
1637
1726
|
timer.unref();
|
|
1638
1727
|
};
|
|
1728
|
+
function* chunks(values, size) {
|
|
1729
|
+
for (let index = 0; index < values.length; index += size) yield values.slice(index, index + size);
|
|
1730
|
+
}
|
|
1639
1731
|
var SessionAffinityStore = class {
|
|
1640
1732
|
db;
|
|
1641
1733
|
constructor(db) {
|
|
@@ -1657,6 +1749,65 @@ var SessionAffinityStore = class {
|
|
|
1657
1749
|
}
|
|
1658
1750
|
return row.account_id;
|
|
1659
1751
|
}
|
|
1752
|
+
getMany(cacheKeys) {
|
|
1753
|
+
const uniqueKeys = [...new Set(cacheKeys)].filter(Boolean);
|
|
1754
|
+
const result = /* @__PURE__ */ new Map();
|
|
1755
|
+
let chunkIndex = 0;
|
|
1756
|
+
for (const keys of chunks(uniqueKeys, MAX_BATCH_KEYS)) {
|
|
1757
|
+
try {
|
|
1758
|
+
this.readManyChunk(keys, result);
|
|
1759
|
+
} catch (error) {
|
|
1760
|
+
consola.error("Failed to batch-read session affinity mappings", {
|
|
1761
|
+
chunkIndex,
|
|
1762
|
+
chunkKeyCount: keys.length,
|
|
1763
|
+
error,
|
|
1764
|
+
totalKeyCount: uniqueKeys.length
|
|
1765
|
+
});
|
|
1766
|
+
throw error;
|
|
1767
|
+
}
|
|
1768
|
+
chunkIndex += 1;
|
|
1769
|
+
}
|
|
1770
|
+
if (result.size === 0) return result;
|
|
1771
|
+
const now = Date.now();
|
|
1772
|
+
let touchChunkIndex = 0;
|
|
1773
|
+
const touchedKeyCount = result.size;
|
|
1774
|
+
for (const keys of chunks([...result.keys()], MAX_BATCH_KEYS)) {
|
|
1775
|
+
try {
|
|
1776
|
+
this.touchManyChunk(keys, now);
|
|
1777
|
+
} catch (error) {
|
|
1778
|
+
consola.warn("Failed to batch-update session affinity last_used_at_ms", {
|
|
1779
|
+
chunkIndex: touchChunkIndex,
|
|
1780
|
+
chunkKeyCount: keys.length,
|
|
1781
|
+
error,
|
|
1782
|
+
touchedKeyCount
|
|
1783
|
+
});
|
|
1784
|
+
}
|
|
1785
|
+
touchChunkIndex += 1;
|
|
1786
|
+
}
|
|
1787
|
+
return result;
|
|
1788
|
+
}
|
|
1789
|
+
readManyChunk(cacheKeys, result) {
|
|
1790
|
+
if (cacheKeys.length === 0) return;
|
|
1791
|
+
const placeholders = cacheKeys.map(() => "?").join(", ");
|
|
1792
|
+
const rows = this.db.query(`SELECT cache_key, account_id FROM session_affinity
|
|
1793
|
+
WHERE cache_key IN (${placeholders});`).all(...cacheKeys);
|
|
1794
|
+
for (const row of rows) {
|
|
1795
|
+
if (!row.cache_key || !row.account_id) {
|
|
1796
|
+
consola.error("Invalid session affinity row returned from database", {
|
|
1797
|
+
hasAccountId: Boolean(row.account_id),
|
|
1798
|
+
hasCacheKey: Boolean(row.cache_key)
|
|
1799
|
+
});
|
|
1800
|
+
throw new Error("Invalid session affinity row returned from database");
|
|
1801
|
+
}
|
|
1802
|
+
result.set(row.cache_key, row.account_id);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
touchManyChunk(cacheKeys, now) {
|
|
1806
|
+
if (cacheKeys.length === 0) return;
|
|
1807
|
+
const placeholders = cacheKeys.map(() => "?").join(", ");
|
|
1808
|
+
this.db.query(`UPDATE session_affinity SET last_used_at_ms = ?
|
|
1809
|
+
WHERE cache_key IN (${placeholders});`).run(now, ...cacheKeys);
|
|
1810
|
+
}
|
|
1660
1811
|
set(cacheKey, accountId) {
|
|
1661
1812
|
const now = Date.now();
|
|
1662
1813
|
try {
|
|
@@ -1724,7 +1875,6 @@ function applySharedSessionAffinityRetention(retentionMs = getSessionAffinityRet
|
|
|
1724
1875
|
consola.warn("Failed to apply session affinity retention:", error);
|
|
1725
1876
|
}
|
|
1726
1877
|
}
|
|
1727
|
-
|
|
1728
1878
|
//#endregion
|
|
1729
1879
|
//#region src/lib/session-ownership.ts
|
|
1730
1880
|
const DEFAULT_MAX_ENTRIES = 1e4;
|
|
@@ -1770,7 +1920,6 @@ var SessionOwnershipCache = class {
|
|
|
1770
1920
|
this.cache.clear();
|
|
1771
1921
|
}
|
|
1772
1922
|
};
|
|
1773
|
-
|
|
1774
1923
|
//#endregion
|
|
1775
1924
|
//#region src/lib/accounts-manager.ts
|
|
1776
1925
|
/** Quota cache TTL in milliseconds (45 seconds) for pre-request selection. */
|
|
@@ -1798,6 +1947,10 @@ function getInitialSelectionReason(cacheKey, rotationStart) {
|
|
|
1798
1947
|
function preserveSubagentSelectionReason(initialSelectionReason, nextSelectionReason) {
|
|
1799
1948
|
return initialSelectionReason.startsWith("subagent_") ? initialSelectionReason : nextSelectionReason;
|
|
1800
1949
|
}
|
|
1950
|
+
function normalizeCacheKeys(keys) {
|
|
1951
|
+
if (!keys) return [];
|
|
1952
|
+
return [...new Set(keys.map((key) => key.trim()).filter(Boolean))];
|
|
1953
|
+
}
|
|
1801
1954
|
/** Manages multiple GitHub Copilot accounts at runtime. */
|
|
1802
1955
|
var AccountsManager = class {
|
|
1803
1956
|
accounts = /* @__PURE__ */ new Map();
|
|
@@ -1873,6 +2026,12 @@ var AccountsManager = class {
|
|
|
1873
2026
|
this.modelsRefreshIntervalMs = Number.isFinite(intervalMs) && intervalMs > 0 ? intervalMs : 0;
|
|
1874
2027
|
this.scheduleModelsRefresh();
|
|
1875
2028
|
}
|
|
2029
|
+
getQuotaRefreshAccounts() {
|
|
2030
|
+
return this.getOrderedEnabledAccounts().filter((account) => !this.isAccountFailed(account) && account.copilotToken !== void 0);
|
|
2031
|
+
}
|
|
2032
|
+
refreshAccountQuota(account) {
|
|
2033
|
+
return this.refreshQuota(account);
|
|
2034
|
+
}
|
|
1876
2035
|
computeTokenRefreshDelayMs(refreshInSeconds) {
|
|
1877
2036
|
const baseDelay = Math.max((refreshInSeconds - 60) * 1e3, 1e3);
|
|
1878
2037
|
const jitter = Math.floor(Math.random() * TOKEN_REFRESH_JITTER_MS);
|
|
@@ -2309,6 +2468,15 @@ var AccountsManager = class {
|
|
|
2309
2468
|
async selectAccountForRequest(candidates, context) {
|
|
2310
2469
|
if (candidates.length === 0) throw new Error("selectAccountForRequest requires at least one candidate");
|
|
2311
2470
|
const orderedAccounts = this.getOrderedEnabledAccounts();
|
|
2471
|
+
const itemOwnerSelection = await this.selectPreferredResponsesItemOwner({
|
|
2472
|
+
ownershipKeys: context?.responsesItemOwnershipKeys,
|
|
2473
|
+
orderedAccounts,
|
|
2474
|
+
candidates
|
|
2475
|
+
});
|
|
2476
|
+
if (itemOwnerSelection.result) {
|
|
2477
|
+
this.attachConfirmOwnership(itemOwnerSelection.result, context?.ownershipWriteSessionId);
|
|
2478
|
+
return itemOwnerSelection.result;
|
|
2479
|
+
}
|
|
2312
2480
|
const ownerSelection = await this.selectPreferredSessionOwner({
|
|
2313
2481
|
lookupSessionId: context?.ownershipLookupSessionId,
|
|
2314
2482
|
orderedAccounts,
|
|
@@ -2389,6 +2557,12 @@ var AccountsManager = class {
|
|
|
2389
2557
|
if (!normalizedCacheKey) return;
|
|
2390
2558
|
this.affinityCache.delete(normalizedCacheKey);
|
|
2391
2559
|
}
|
|
2560
|
+
recordResponsesItemOwnership(ownershipKeys, accountId) {
|
|
2561
|
+
if (!this.accountAffinityEnabled) return;
|
|
2562
|
+
const normalizedAccountId = accountId.trim();
|
|
2563
|
+
if (!normalizedAccountId) return;
|
|
2564
|
+
for (const key of normalizeCacheKeys(ownershipKeys)) this.affinityCache.set(key, normalizedAccountId);
|
|
2565
|
+
}
|
|
2392
2566
|
attachConfirmOwnership(result, ownershipWriteSessionId) {
|
|
2393
2567
|
const rootSessionId = ownershipWriteSessionId?.trim();
|
|
2394
2568
|
if (!rootSessionId) return;
|
|
@@ -2398,6 +2572,52 @@ var AccountsManager = class {
|
|
|
2398
2572
|
this.sessionOwnership.set(rootSessionId, result.account.id);
|
|
2399
2573
|
};
|
|
2400
2574
|
}
|
|
2575
|
+
async selectPreferredResponsesItemOwner(params) {
|
|
2576
|
+
if (!this.accountAffinityEnabled) return {};
|
|
2577
|
+
const { orderedAccounts, candidates } = params;
|
|
2578
|
+
const ownershipKeys = normalizeCacheKeys(params.ownershipKeys);
|
|
2579
|
+
if (ownershipKeys.length === 0) return {};
|
|
2580
|
+
let ownershipMappings;
|
|
2581
|
+
try {
|
|
2582
|
+
ownershipMappings = this.affinityCache.getMany(ownershipKeys);
|
|
2583
|
+
} catch (error) {
|
|
2584
|
+
consola.warn("Failed to read Responses item owner mappings; falling back", {
|
|
2585
|
+
error,
|
|
2586
|
+
ownershipKeyCount: ownershipKeys.length
|
|
2587
|
+
});
|
|
2588
|
+
return {};
|
|
2589
|
+
}
|
|
2590
|
+
const accountIds = /* @__PURE__ */ new Map();
|
|
2591
|
+
for (const key of ownershipKeys) {
|
|
2592
|
+
const accountId = ownershipMappings.get(key);
|
|
2593
|
+
if (!accountId) continue;
|
|
2594
|
+
accountIds.set(accountId, key);
|
|
2595
|
+
if (accountIds.size > 1) {
|
|
2596
|
+
consola.warn("Conflicting Responses item owner mappings; falling back", {
|
|
2597
|
+
accountIds: [...accountIds.keys()],
|
|
2598
|
+
ownershipKeyCount: ownershipKeys.length
|
|
2599
|
+
});
|
|
2600
|
+
return {};
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
if (accountIds.size === 0) return {};
|
|
2604
|
+
const [[accountId, ownerKey]] = [...accountIds];
|
|
2605
|
+
const ownerResult = await this.tryAffinityAccount(accountId, orderedAccounts, candidates);
|
|
2606
|
+
if (!ownerResult) {
|
|
2607
|
+
if (this.isMissingOrDisabledAccount(accountId, orderedAccounts)) this.deleteResponsesItemOwnershipMappings(ownershipKeys, accountId);
|
|
2608
|
+
return {};
|
|
2609
|
+
}
|
|
2610
|
+
ownerResult.affinityHit = true;
|
|
2611
|
+
ownerResult.affinityCacheKey = ownerKey;
|
|
2612
|
+
ownerResult.selectionReason = "responses_item_owner_hit";
|
|
2613
|
+
return { result: ownerResult };
|
|
2614
|
+
}
|
|
2615
|
+
isMissingOrDisabledAccount(accountId, orderedAccounts) {
|
|
2616
|
+
return !orderedAccounts.some((account) => account.id === accountId);
|
|
2617
|
+
}
|
|
2618
|
+
deleteResponsesItemOwnershipMappings(ownershipKeys, accountId) {
|
|
2619
|
+
for (const key of ownershipKeys) if (this.affinityCache.get(key) === accountId) this.affinityCache.delete(key);
|
|
2620
|
+
}
|
|
2401
2621
|
async selectPreferredSessionOwner(params) {
|
|
2402
2622
|
const { lookupSessionId, orderedAccounts, candidates } = params;
|
|
2403
2623
|
if (lookupSessionId === void 0) return {};
|
|
@@ -2463,13 +2683,13 @@ var AccountsManager = class {
|
|
|
2463
2683
|
if (scoredAccounts.length > 0) {
|
|
2464
2684
|
const scoredAccountsInSelectionOrder = this.shuffleWithinEqualRemainingBuckets(scoredAccounts).map(({ account }) => account);
|
|
2465
2685
|
const scoredAccountSet = new Set(scoredAccountsInSelectionOrder);
|
|
2466
|
-
const unknownQuotaAccounts
|
|
2467
|
-
const unlimitedAccounts
|
|
2686
|
+
const unknownQuotaAccounts = orderedAccounts.filter((account) => !scoredAccountSet.has(account) && !account.unlimited);
|
|
2687
|
+
const unlimitedAccounts = orderedAccounts.filter((account) => !scoredAccountSet.has(account) && account.unlimited);
|
|
2468
2688
|
return {
|
|
2469
2689
|
accounts: [
|
|
2470
2690
|
...scoredAccountsInSelectionOrder,
|
|
2471
|
-
...unknownQuotaAccounts
|
|
2472
|
-
...unlimitedAccounts
|
|
2691
|
+
...unknownQuotaAccounts,
|
|
2692
|
+
...unlimitedAccounts
|
|
2473
2693
|
],
|
|
2474
2694
|
premiumRemainingOrderedAccountIds: new Set(scoredAccountsInSelectionOrder.map((account) => account.id))
|
|
2475
2695
|
};
|
|
@@ -2868,7 +3088,170 @@ var AccountsManager = class {
|
|
|
2868
3088
|
};
|
|
2869
3089
|
/** Singleton instance of AccountsManager */
|
|
2870
3090
|
const accountsManager = new AccountsManager({ persistentAffinityStore: getSharedSessionAffinityStore });
|
|
2871
|
-
|
|
2872
3091
|
//#endregion
|
|
2873
|
-
|
|
2874
|
-
|
|
3092
|
+
//#region src/lib/accounts-manager-quota-scheduler.ts
|
|
3093
|
+
const QUOTA_REFRESH_FRESHNESS_GUARD_MS = 300 * 1e3;
|
|
3094
|
+
const QUOTA_REFRESH_INTERVAL_JITTER_RATIO = .1;
|
|
3095
|
+
const defaultTimer = {
|
|
3096
|
+
setTimer(callback, delayMs) {
|
|
3097
|
+
return setTimeout(callback, delayMs);
|
|
3098
|
+
},
|
|
3099
|
+
clearTimer(handle) {
|
|
3100
|
+
clearTimeout(handle);
|
|
3101
|
+
}
|
|
3102
|
+
};
|
|
3103
|
+
function secondsToMs(seconds) {
|
|
3104
|
+
return seconds * 1e3;
|
|
3105
|
+
}
|
|
3106
|
+
function minutesToMs(minutes) {
|
|
3107
|
+
return minutes * 60 * 1e3;
|
|
3108
|
+
}
|
|
3109
|
+
function isSchedulingEnabled(config) {
|
|
3110
|
+
return config.enabled && config.intervalMinutes > 0;
|
|
3111
|
+
}
|
|
3112
|
+
var QuotaRefreshScheduler = class {
|
|
3113
|
+
config;
|
|
3114
|
+
logger;
|
|
3115
|
+
manager;
|
|
3116
|
+
now;
|
|
3117
|
+
random;
|
|
3118
|
+
timer;
|
|
3119
|
+
generation = 0;
|
|
3120
|
+
pendingDelay;
|
|
3121
|
+
roundTimer;
|
|
3122
|
+
stopped = true;
|
|
3123
|
+
constructor(options) {
|
|
3124
|
+
this.config = options.config;
|
|
3125
|
+
this.logger = options.logger ?? consola;
|
|
3126
|
+
this.manager = options.manager;
|
|
3127
|
+
this.now = options.now ?? Date.now;
|
|
3128
|
+
this.random = options.random ?? Math.random;
|
|
3129
|
+
this.timer = options.timer ?? defaultTimer;
|
|
3130
|
+
}
|
|
3131
|
+
start(config = this.config) {
|
|
3132
|
+
this.config = config;
|
|
3133
|
+
this.stop();
|
|
3134
|
+
if (!isSchedulingEnabled(this.config)) return;
|
|
3135
|
+
this.stopped = false;
|
|
3136
|
+
this.scheduleRound(secondsToMs(this.config.startupDelaySeconds), this.generation);
|
|
3137
|
+
}
|
|
3138
|
+
stop() {
|
|
3139
|
+
this.generation += 1;
|
|
3140
|
+
this.stopped = true;
|
|
3141
|
+
this.clearRoundTimer();
|
|
3142
|
+
this.clearPendingDelay();
|
|
3143
|
+
}
|
|
3144
|
+
updateConfig(config) {
|
|
3145
|
+
this.start(config);
|
|
3146
|
+
}
|
|
3147
|
+
isActiveGeneration(generation) {
|
|
3148
|
+
return !this.stopped && this.generation === generation;
|
|
3149
|
+
}
|
|
3150
|
+
scheduleRound(delayMs, generation) {
|
|
3151
|
+
this.clearRoundTimer();
|
|
3152
|
+
const handle = this.timer.setTimer(() => {
|
|
3153
|
+
if (this.roundTimer === handle) this.roundTimer = void 0;
|
|
3154
|
+
if (this.isActiveGeneration(generation)) this.runRound(generation);
|
|
3155
|
+
}, Math.max(0, delayMs));
|
|
3156
|
+
this.roundTimer = handle;
|
|
3157
|
+
}
|
|
3158
|
+
scheduleNextRound(generation) {
|
|
3159
|
+
if (!this.isActiveGeneration(generation) || !isSchedulingEnabled(this.config)) return;
|
|
3160
|
+
const intervalMs = minutesToMs(this.config.intervalMinutes);
|
|
3161
|
+
this.scheduleRound(this.withJitter(intervalMs), generation);
|
|
3162
|
+
}
|
|
3163
|
+
withJitter(intervalMs) {
|
|
3164
|
+
const jitterMs = Math.floor(intervalMs * QUOTA_REFRESH_INTERVAL_JITTER_RATIO * (this.random() * 2 - 1));
|
|
3165
|
+
return Math.max(0, intervalMs + jitterMs);
|
|
3166
|
+
}
|
|
3167
|
+
getStaggerDelayMs() {
|
|
3168
|
+
const minMs = secondsToMs(this.config.staggerMinSeconds);
|
|
3169
|
+
const maxMs = secondsToMs(this.config.staggerMaxSeconds);
|
|
3170
|
+
if (maxMs <= minMs) return minMs;
|
|
3171
|
+
return minMs + Math.floor(this.random() * (maxMs - minMs));
|
|
3172
|
+
}
|
|
3173
|
+
isFresh(account) {
|
|
3174
|
+
if (account.lastQuotaFetch === void 0) return false;
|
|
3175
|
+
return this.now() - account.lastQuotaFetch < QUOTA_REFRESH_FRESHNESS_GUARD_MS;
|
|
3176
|
+
}
|
|
3177
|
+
async schedulerDelay(delayMs) {
|
|
3178
|
+
this.clearPendingDelay();
|
|
3179
|
+
await new Promise((resolve) => {
|
|
3180
|
+
const handle = this.timer.setTimer(() => {
|
|
3181
|
+
if (this.pendingDelay?.handle === handle) this.pendingDelay = void 0;
|
|
3182
|
+
resolve();
|
|
3183
|
+
}, Math.max(0, delayMs));
|
|
3184
|
+
this.pendingDelay = {
|
|
3185
|
+
handle,
|
|
3186
|
+
resolve
|
|
3187
|
+
};
|
|
3188
|
+
});
|
|
3189
|
+
}
|
|
3190
|
+
clearRoundTimer() {
|
|
3191
|
+
if (this.roundTimer === void 0) return;
|
|
3192
|
+
this.timer.clearTimer(this.roundTimer);
|
|
3193
|
+
this.roundTimer = void 0;
|
|
3194
|
+
}
|
|
3195
|
+
clearPendingDelay() {
|
|
3196
|
+
if (!this.pendingDelay) return;
|
|
3197
|
+
const { handle, resolve } = this.pendingDelay;
|
|
3198
|
+
this.pendingDelay = void 0;
|
|
3199
|
+
this.timer.clearTimer(handle);
|
|
3200
|
+
resolve();
|
|
3201
|
+
}
|
|
3202
|
+
async runRound(generation) {
|
|
3203
|
+
try {
|
|
3204
|
+
const accounts = this.manager.getQuotaRefreshAccounts();
|
|
3205
|
+
for (const [index, account] of accounts.entries()) {
|
|
3206
|
+
if (!this.isActiveGeneration(generation)) return;
|
|
3207
|
+
if (this.isFresh(account)) continue;
|
|
3208
|
+
try {
|
|
3209
|
+
await this.manager.refreshAccountQuota(account);
|
|
3210
|
+
} catch (error) {
|
|
3211
|
+
if (this.isActiveGeneration(generation)) this.logger.debug("Background quota refresh failed", {
|
|
3212
|
+
accountId: account.id,
|
|
3213
|
+
error
|
|
3214
|
+
});
|
|
3215
|
+
}
|
|
3216
|
+
if (!this.isActiveGeneration(generation)) return;
|
|
3217
|
+
if (index < accounts.length - 1) {
|
|
3218
|
+
await this.schedulerDelay(this.getStaggerDelayMs());
|
|
3219
|
+
if (!this.isActiveGeneration(generation)) return;
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
} catch (error) {
|
|
3223
|
+
if (this.isActiveGeneration(generation)) this.logger.error("Background quota refresh round failed", error);
|
|
3224
|
+
} finally {
|
|
3225
|
+
this.scheduleNextRound(generation);
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
};
|
|
3229
|
+
//#endregion
|
|
3230
|
+
//#region src/lib/quota-refresh-scheduler-runtime.ts
|
|
3231
|
+
const quotaRefreshScheduler = new QuotaRefreshScheduler({
|
|
3232
|
+
config: getQuotaRefreshConfig(),
|
|
3233
|
+
manager: accountsManager
|
|
3234
|
+
});
|
|
3235
|
+
const registeredShutdownRuntimes = /* @__PURE__ */ new WeakSet();
|
|
3236
|
+
let isQuotaRefreshSchedulerStarted = false;
|
|
3237
|
+
function startQuotaRefreshSchedulerFromConfig() {
|
|
3238
|
+
isQuotaRefreshSchedulerStarted = true;
|
|
3239
|
+
quotaRefreshScheduler.start(getQuotaRefreshConfig());
|
|
3240
|
+
}
|
|
3241
|
+
function stopQuotaRefreshScheduler() {
|
|
3242
|
+
isQuotaRefreshSchedulerStarted = false;
|
|
3243
|
+
quotaRefreshScheduler.stop();
|
|
3244
|
+
}
|
|
3245
|
+
function registerQuotaRefreshSchedulerShutdownCleanup(runtime = process) {
|
|
3246
|
+
if (registeredShutdownRuntimes.has(runtime)) return;
|
|
3247
|
+
registeredShutdownRuntimes.add(runtime);
|
|
3248
|
+
runtime.once("exit", () => stopQuotaRefreshScheduler());
|
|
3249
|
+
}
|
|
3250
|
+
function updateQuotaRefreshSchedulerFromConfig() {
|
|
3251
|
+
if (!isQuotaRefreshSchedulerStarted) return;
|
|
3252
|
+
quotaRefreshScheduler.updateConfig(getQuotaRefreshConfig());
|
|
3253
|
+
}
|
|
3254
|
+
//#endregion
|
|
3255
|
+
export { getReasoningEffortForModel as A, shouldCompactUseSmallModel as B, getConfig as C, getModelAliasesInfo as D, getModelAliases as E, isMessagesApiEnabled as F, isResponsesApiContextManagementModel as I, isResponsesApiWebSearchEnabled as L, isAccountAffinityEnabled as M, isForceAgentEnabled as N, getModelRefreshIntervalMs as O, isMessageStartInputTokensFallbackEnabled as P, mergeConfigWithDefaults as R, getClaudeTokenMultiplier as S, getLogLevel as T, flushPendingCapture as _, accountsManager as a, getAliasTargetSet as b, extractResponsesUsageFromStreamEvent as c, getStatsStore as d, normalizeChatCompletionsUsage as f, copilotFetch as g, toLocalDateString as h, updateQuotaRefreshSchedulerFromConfig as i, getSmallModel as j, getProviderConfig as k, getClientIpInfo as l, normalizeMessagesUsage as m, startQuotaRefreshSchedulerFromConfig as n, applySharedSessionAffinityRetention as o, normalizeEmbeddingsUsage as p, stopQuotaRefreshScheduler as r, extractResponsesUsageFromResult as s, registerQuotaRefreshSchedulerShutdownCleanup as t, getRequestHistoryStore as u, isDevModeEnabled as v, getExtraPromptForModel as w, getAnthropicApiKey as x, PROVIDER_TYPE_ANTHROPIC as y, resolveModelAlias as z };
|
|
3256
|
+
|
|
3257
|
+
//# sourceMappingURL=quota-refresh-scheduler-runtime-XD2fDa2K.js.map
|