@nick3/copilot-api 1.9.2 → 1.10.4
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 +114 -89
- package/README.zh-CN.md +114 -86
- package/dist/{account-MllYSdRC.js → account-COtMmvzU.js} +20 -24
- package/dist/account-COtMmvzU.js.map +1 -0
- package/dist/admin/assets/index-CBMFCvqO.css +1 -0
- package/dist/admin/assets/index-DG4TRVMu.js +110 -0
- package/dist/admin/index.html +2 -2
- package/dist/{auth-DZoQA-kn.js → auth-B0y-2njL.js} +103 -112
- package/dist/auth-B0y-2njL.js.map +1 -0
- package/dist/{check-usage-DEbsehjH.js → check-usage-DdevqHE5.js} +6 -7
- package/dist/{check-usage-DEbsehjH.js.map → check-usage-DdevqHE5.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-4mCKt94e.js → get-copilot-token-8Rm-rVsp.js} +3 -4
- package/dist/{get-copilot-token-4mCKt94e.js.map → get-copilot-token-8Rm-rVsp.js.map} +1 -1
- 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-Dvk6Ho0R.js → poll-access-token-BAgM2-7k.js} +65 -21
- package/dist/poll-access-token-BAgM2-7k.js.map +1 -0
- package/dist/{accounts-manager-BM66oT38.js → proxy-BwmADhKh.js} +494 -52
- package/dist/proxy-BwmADhKh.js.map +1 -0
- package/dist/request-outbound-BJjWS_jF.js +2 -0
- package/dist/{request-outbound-qyTeXbzy.js → request-outbound-Pu1kp2x8.js} +16 -9
- package/dist/request-outbound-Pu1kp2x8.js.map +1 -0
- package/dist/{server-DR9ZR_MN.js → server-DxQsi1x2.js} +2106 -935
- package/dist/server-DxQsi1x2.js.map +1 -0
- package/dist/{start-DDhYUFQR.js → start-8QHzPrcg.js} +11 -60
- package/dist/start-8QHzPrcg.js.map +1 -0
- package/package.json +20 -7
- package/dist/account-MllYSdRC.js.map +0 -1
- package/dist/accounts-manager-BM66oT38.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-DZoQA-kn.js.map +0 -1
- package/dist/debug-BJfZVBB7.js.map +0 -1
- package/dist/poll-access-token-Dvk6Ho0R.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-DR9ZR_MN.js.map +0 -1
- package/dist/start-DDhYUFQR.js.map +0 -1
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
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 consumeOutboundHeadersSnapshot, N as requestContext, _ as HTTPError, g as getCopilotUsage, k as accountFromState, m as getGitHubUser, x as copilotModelsHeaders, y as copilotBaseUrl } from "./poll-access-token-BAgM2-7k.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-COtMmvzU.js";
|
|
3
|
+
import { t as PATHS } from "./paths-CclKwouX.js";
|
|
4
|
+
import { t as getCopilotToken } from "./get-copilot-token-8Rm-rVsp.js";
|
|
5
|
+
import { c as getAdminDbUserVersion, i as getRequestOutboundStore, o as getAdminDb, s as getAdminDbPath } from "./request-outbound-Pu1kp2x8.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
|
-
|
|
9
|
+
import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
|
|
10
|
+
import { getProxyForUrl } from "proxy-from-env";
|
|
10
11
|
//#region src/lib/config.ts
|
|
11
12
|
const PROVIDER_TYPE_ANTHROPIC = "anthropic";
|
|
12
13
|
const gpt5ExplorationPrompt = `## Exploration and reading files
|
|
@@ -15,6 +16,14 @@ const gpt5ExplorationPrompt = `## Exploration and reading files
|
|
|
15
16
|
- **multi_tool_use.parallel** Use multi_tool_use.parallel to parallelize tool calls and only this.
|
|
16
17
|
- **Only make sequential calls if you truly cannot know the next file without seeing a result first.**
|
|
17
18
|
- **Workflow:** (a) plan all needed reads → (b) issue one parallel batch → (c) analyze results → (d) repeat if new, unpredictable reads arise.`;
|
|
19
|
+
const DEFAULT_QUOTA_REFRESH_CONFIG = {
|
|
20
|
+
enabled: true,
|
|
21
|
+
intervalMinutes: 360,
|
|
22
|
+
startupDelaySeconds: 60,
|
|
23
|
+
staggerMinSeconds: 2,
|
|
24
|
+
staggerMaxSeconds: 5
|
|
25
|
+
};
|
|
26
|
+
const MIN_QUOTA_REFRESH_INTERVAL_MINUTES = 30;
|
|
18
27
|
const gpt5CommentaryPrompt = `# Working with the user
|
|
19
28
|
|
|
20
29
|
You interact with the user through a terminal. You have 2 ways of communicating with the users:
|
|
@@ -62,6 +71,7 @@ const defaultConfig = {
|
|
|
62
71
|
modelRefreshIntervalHours: 24,
|
|
63
72
|
sessionAffinityRetentionDays: 7,
|
|
64
73
|
useMessagesApi: true,
|
|
74
|
+
useResponsesApiWebSocket: true,
|
|
65
75
|
useResponsesApiWebSearch: true,
|
|
66
76
|
logLevel: "info",
|
|
67
77
|
devMode: {
|
|
@@ -69,7 +79,8 @@ const defaultConfig = {
|
|
|
69
79
|
capture4xx: false,
|
|
70
80
|
capture5xx: false,
|
|
71
81
|
captureOther: false
|
|
72
|
-
}
|
|
82
|
+
},
|
|
83
|
+
quotaRefresh: DEFAULT_QUOTA_REFRESH_CONFIG
|
|
73
84
|
};
|
|
74
85
|
let cachedConfig = null;
|
|
75
86
|
function isPlainObject(value) {
|
|
@@ -85,6 +96,23 @@ function normalizeNonNegativeNumber(value) {
|
|
|
85
96
|
if (value < 0) return void 0;
|
|
86
97
|
return value;
|
|
87
98
|
}
|
|
99
|
+
function normalizeQuotaRefreshIntervalMinutes(value) {
|
|
100
|
+
const minutes = normalizeNonNegativeNumber(value) ?? DEFAULT_QUOTA_REFRESH_CONFIG.intervalMinutes;
|
|
101
|
+
if (minutes > 0 && minutes < MIN_QUOTA_REFRESH_INTERVAL_MINUTES) return MIN_QUOTA_REFRESH_INTERVAL_MINUTES;
|
|
102
|
+
return minutes;
|
|
103
|
+
}
|
|
104
|
+
function normalizeQuotaRefreshConfig(value) {
|
|
105
|
+
const raw = isPlainObject(value) ? value : {};
|
|
106
|
+
const staggerMinSeconds = normalizeNonNegativeNumber(raw.staggerMinSeconds) ?? DEFAULT_QUOTA_REFRESH_CONFIG.staggerMinSeconds;
|
|
107
|
+
const rawStaggerMaxSeconds = normalizeNonNegativeNumber(raw.staggerMaxSeconds) ?? DEFAULT_QUOTA_REFRESH_CONFIG.staggerMaxSeconds;
|
|
108
|
+
return {
|
|
109
|
+
enabled: typeof raw.enabled === "boolean" ? raw.enabled : DEFAULT_QUOTA_REFRESH_CONFIG.enabled,
|
|
110
|
+
intervalMinutes: normalizeQuotaRefreshIntervalMinutes(raw.intervalMinutes),
|
|
111
|
+
startupDelaySeconds: normalizeNonNegativeNumber(raw.startupDelaySeconds) ?? DEFAULT_QUOTA_REFRESH_CONFIG.startupDelaySeconds,
|
|
112
|
+
staggerMinSeconds,
|
|
113
|
+
staggerMaxSeconds: Math.max(staggerMinSeconds, rawStaggerMaxSeconds)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
88
116
|
const LOG_LEVELS = new Set([
|
|
89
117
|
"error",
|
|
90
118
|
"warn",
|
|
@@ -251,6 +279,20 @@ function mergeDefaultDevMode(config) {
|
|
|
251
279
|
changed: true
|
|
252
280
|
};
|
|
253
281
|
}
|
|
282
|
+
function mergeDefaultQuotaRefresh(config) {
|
|
283
|
+
const quotaRefresh = normalizeQuotaRefreshConfig(config.quotaRefresh);
|
|
284
|
+
if (JSON.stringify(config.quotaRefresh) === JSON.stringify(quotaRefresh)) return {
|
|
285
|
+
mergedConfig: config,
|
|
286
|
+
changed: false
|
|
287
|
+
};
|
|
288
|
+
return {
|
|
289
|
+
mergedConfig: {
|
|
290
|
+
...config,
|
|
291
|
+
quotaRefresh
|
|
292
|
+
},
|
|
293
|
+
changed: true
|
|
294
|
+
};
|
|
295
|
+
}
|
|
254
296
|
function applyConfigMerges(config, mergeFns) {
|
|
255
297
|
return mergeFns.reduce((acc, mergeFn) => {
|
|
256
298
|
const result = mergeFn(acc.mergedConfig);
|
|
@@ -271,7 +313,8 @@ function mergeConfigWithDefaults() {
|
|
|
271
313
|
mergeDefaultModelRefreshInterval,
|
|
272
314
|
mergeDefaultSessionAffinityRetention,
|
|
273
315
|
mergeDefaultLogLevel,
|
|
274
|
-
mergeDefaultDevMode
|
|
316
|
+
mergeDefaultDevMode,
|
|
317
|
+
mergeDefaultQuotaRefresh
|
|
275
318
|
]);
|
|
276
319
|
if (changed) try {
|
|
277
320
|
fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(mergedConfig, null, 2)}\n`, "utf8");
|
|
@@ -282,7 +325,7 @@ function mergeConfigWithDefaults() {
|
|
|
282
325
|
return mergedConfig;
|
|
283
326
|
}
|
|
284
327
|
function getConfig() {
|
|
285
|
-
cachedConfig ??= readConfigFromDisk();
|
|
328
|
+
cachedConfig ??= mergeDefaultConfig(readConfigFromDisk()).mergedConfig;
|
|
286
329
|
return cachedConfig;
|
|
287
330
|
}
|
|
288
331
|
function normalizeAliasKey(value) {
|
|
@@ -295,8 +338,8 @@ function normalizeAliasTarget(value) {
|
|
|
295
338
|
}
|
|
296
339
|
function normalizeAliasSpec(value) {
|
|
297
340
|
if (typeof value === "string") {
|
|
298
|
-
const normalizedTarget
|
|
299
|
-
return normalizedTarget
|
|
341
|
+
const normalizedTarget = normalizeAliasTarget(value);
|
|
342
|
+
return normalizedTarget ? { target: normalizedTarget } : null;
|
|
300
343
|
}
|
|
301
344
|
if (!value || typeof value !== "object") return null;
|
|
302
345
|
const targetValue = value.target;
|
|
@@ -402,6 +445,9 @@ function getModelRefreshIntervalMs() {
|
|
|
402
445
|
if (!Number.isFinite(hours) || hours <= 0) return 0;
|
|
403
446
|
return hours * 60 * 60 * 1e3;
|
|
404
447
|
}
|
|
448
|
+
function getQuotaRefreshConfig() {
|
|
449
|
+
return normalizeQuotaRefreshConfig(getConfig().quotaRefresh);
|
|
450
|
+
}
|
|
405
451
|
function getSessionAffinityRetentionDays() {
|
|
406
452
|
return normalizeNonNegativeNumber(getConfig().sessionAffinityRetentionDays) ?? defaultConfig.sessionAffinityRetentionDays ?? 7;
|
|
407
453
|
}
|
|
@@ -435,11 +481,15 @@ function isForceAgentEnabled() {
|
|
|
435
481
|
function normalizeProviderBaseUrl(url) {
|
|
436
482
|
return url.trim().replace(/\/+$/u, "");
|
|
437
483
|
}
|
|
438
|
-
function
|
|
439
|
-
|
|
484
|
+
function getDefaultProviderAuthType(providerType) {
|
|
485
|
+
return providerType === "openai-compatible" ? "authorization" : "x-api-key";
|
|
486
|
+
}
|
|
487
|
+
function resolveProviderAuthType(providerName, authType, providerType) {
|
|
488
|
+
if (authType === void 0) return getDefaultProviderAuthType(providerType);
|
|
489
|
+
if (authType === "x-api-key") return "x-api-key";
|
|
440
490
|
if (authType === "authorization") return authType;
|
|
441
|
-
consola.warn(`Provider ${providerName} has invalid authType '${authType}',
|
|
442
|
-
return
|
|
491
|
+
consola.warn(`Provider ${providerName} has invalid authType '${authType}', falling back to ${getDefaultProviderAuthType(providerType)}`);
|
|
492
|
+
return getDefaultProviderAuthType(providerType);
|
|
443
493
|
}
|
|
444
494
|
function getProviderConfig(name) {
|
|
445
495
|
const providerName = name.trim();
|
|
@@ -447,21 +497,21 @@ function getProviderConfig(name) {
|
|
|
447
497
|
const provider = getConfig().providers?.[providerName];
|
|
448
498
|
if (!provider) return null;
|
|
449
499
|
if (provider.enabled === false) return null;
|
|
450
|
-
|
|
451
|
-
|
|
500
|
+
const type = provider.type ?? "anthropic";
|
|
501
|
+
if (type !== "anthropic" && type !== "openai-compatible") {
|
|
502
|
+
consola.warn(`Provider ${providerName} is ignored because type '${type}' is not supported`);
|
|
452
503
|
return null;
|
|
453
504
|
}
|
|
454
505
|
const baseUrl = normalizeProviderBaseUrl(provider.baseUrl ?? "");
|
|
455
506
|
const apiKey = (provider.apiKey ?? "").trim();
|
|
456
|
-
const authType = resolveProviderAuthType(providerName, provider.authType);
|
|
457
|
-
if (!authType) return null;
|
|
507
|
+
const authType = resolveProviderAuthType(providerName, provider.authType, type);
|
|
458
508
|
if (!baseUrl || !apiKey) {
|
|
459
509
|
consola.warn(`Provider ${providerName} is enabled but missing baseUrl or apiKey`);
|
|
460
510
|
return null;
|
|
461
511
|
}
|
|
462
512
|
return {
|
|
463
513
|
name: providerName,
|
|
464
|
-
type
|
|
514
|
+
type,
|
|
465
515
|
baseUrl,
|
|
466
516
|
apiKey,
|
|
467
517
|
authType,
|
|
@@ -472,6 +522,9 @@ function getProviderConfig(name) {
|
|
|
472
522
|
function isMessagesApiEnabled() {
|
|
473
523
|
return getConfig().useMessagesApi ?? true;
|
|
474
524
|
}
|
|
525
|
+
function isResponsesApiWebSocketEnabled() {
|
|
526
|
+
return getConfig().useResponsesApiWebSocket ?? true;
|
|
527
|
+
}
|
|
475
528
|
function getAnthropicApiKey() {
|
|
476
529
|
return getConfig().anthropicApiKey ?? process.env.ANTHROPIC_API_KEY ?? void 0;
|
|
477
530
|
}
|
|
@@ -481,7 +534,6 @@ function isResponsesApiWebSearchEnabled() {
|
|
|
481
534
|
function getClaudeTokenMultiplier() {
|
|
482
535
|
return getConfig().claudeTokenMultiplier ?? 1.15;
|
|
483
536
|
}
|
|
484
|
-
|
|
485
537
|
//#endregion
|
|
486
538
|
//#region src/lib/account-affinity.ts
|
|
487
539
|
const DEFAULT_MAX_ENTRIES$1 = 1e4;
|
|
@@ -514,6 +566,31 @@ var AccountAffinityCache = class {
|
|
|
514
566
|
this.setMemory(key, accountId);
|
|
515
567
|
return accountId;
|
|
516
568
|
}
|
|
569
|
+
getMany(keys) {
|
|
570
|
+
const result = /* @__PURE__ */ new Map();
|
|
571
|
+
const missingKeys = new Array();
|
|
572
|
+
const now = Date.now();
|
|
573
|
+
for (const key of keys) {
|
|
574
|
+
if (result.has(key)) continue;
|
|
575
|
+
const entry = this.cache.get(key);
|
|
576
|
+
if (!entry) {
|
|
577
|
+
missingKeys.push(key);
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
if (now >= entry.expiresAt) {
|
|
581
|
+
this.cache.delete(key);
|
|
582
|
+
missingKeys.push(key);
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
result.set(key, entry.accountId);
|
|
586
|
+
}
|
|
587
|
+
if (missingKeys.length === 0) return result;
|
|
588
|
+
const persistentEntries = this.readPersistentEntries(missingKeys);
|
|
589
|
+
for (const [key, accountId] of persistentEntries) this.setMemory(key, accountId);
|
|
590
|
+
if (result.size === 0) return persistentEntries;
|
|
591
|
+
for (const [key, accountId] of persistentEntries) result.set(key, accountId);
|
|
592
|
+
return result;
|
|
593
|
+
}
|
|
517
594
|
/** Record a successful account mapping. Refreshes TTL and moves the entry to the newest position. */
|
|
518
595
|
set(key, accountId) {
|
|
519
596
|
this.setMemory(key, accountId);
|
|
@@ -538,7 +615,7 @@ var AccountAffinityCache = class {
|
|
|
538
615
|
get size() {
|
|
539
616
|
return this.cache.size;
|
|
540
617
|
}
|
|
541
|
-
getPersistentStore() {
|
|
618
|
+
getPersistentStore(options = {}) {
|
|
542
619
|
if (this.persistentStore) return this.persistentStore;
|
|
543
620
|
if (!this.persistentStoreProvider) return;
|
|
544
621
|
try {
|
|
@@ -546,7 +623,10 @@ var AccountAffinityCache = class {
|
|
|
546
623
|
if (store) this.persistentStore = store;
|
|
547
624
|
return store;
|
|
548
625
|
} catch (error) {
|
|
549
|
-
|
|
626
|
+
if (options.throwOnProviderFailure) {
|
|
627
|
+
consola.error("Failed to resolve affinity persistence store:", error);
|
|
628
|
+
throw new Error("Affinity persistence store provider failed");
|
|
629
|
+
}
|
|
550
630
|
consola.warn("Failed to resolve affinity persistence store:", error);
|
|
551
631
|
return;
|
|
552
632
|
}
|
|
@@ -561,6 +641,27 @@ var AccountAffinityCache = class {
|
|
|
561
641
|
return;
|
|
562
642
|
}
|
|
563
643
|
}
|
|
644
|
+
readPersistentEntries(keys) {
|
|
645
|
+
const store = this.getPersistentStore({ throwOnProviderFailure: true });
|
|
646
|
+
if (!store) return /* @__PURE__ */ new Map();
|
|
647
|
+
if (store.getMany) try {
|
|
648
|
+
return store.getMany(keys);
|
|
649
|
+
} catch (error) {
|
|
650
|
+
consola.warn("Failed to batch-read affinity mappings from persistent store:", error);
|
|
651
|
+
throw new Error(`Affinity persistent store batch lookup failed for ${keys.length} keys`);
|
|
652
|
+
}
|
|
653
|
+
const result = /* @__PURE__ */ new Map();
|
|
654
|
+
const failedKeys = new Array();
|
|
655
|
+
for (const key of keys) try {
|
|
656
|
+
const accountId = store.get(key);
|
|
657
|
+
if (accountId) result.set(key, accountId);
|
|
658
|
+
} catch (error) {
|
|
659
|
+
failedKeys.push(key);
|
|
660
|
+
consola.warn("Failed to read affinity mapping from persistent store:", error);
|
|
661
|
+
}
|
|
662
|
+
if (failedKeys.length > 0) throw new Error(`Affinity persistent store lookup failed for ${failedKeys.length}/${keys.length} keys`);
|
|
663
|
+
return result;
|
|
664
|
+
}
|
|
564
665
|
writePersistentEntry(key, accountId) {
|
|
565
666
|
const store = this.getPersistentStore();
|
|
566
667
|
if (!store) return;
|
|
@@ -627,7 +728,6 @@ function isAffinityAccountUsable(accountId, accounts) {
|
|
|
627
728
|
if (account.failed) return void 0;
|
|
628
729
|
return account;
|
|
629
730
|
}
|
|
630
|
-
|
|
631
731
|
//#endregion
|
|
632
732
|
//#region src/lib/dev-mode.ts
|
|
633
733
|
function isDevModeEnabled() {
|
|
@@ -642,7 +742,6 @@ function isCapture5xxEnabled() {
|
|
|
642
742
|
function isCaptureOtherEnabled() {
|
|
643
743
|
return getConfig().devMode?.captureOther === true;
|
|
644
744
|
}
|
|
645
|
-
|
|
646
745
|
//#endregion
|
|
647
746
|
//#region src/services/copilot/copilot-fetch.ts
|
|
648
747
|
const pendingCaptures = /* @__PURE__ */ new Map();
|
|
@@ -669,14 +768,14 @@ async function flushPendingCapture(requestId) {
|
|
|
669
768
|
function snapshotHeaders(init) {
|
|
670
769
|
const headers = init.headers;
|
|
671
770
|
if (headers instanceof Headers) {
|
|
672
|
-
const out
|
|
673
|
-
for (const [key, value] of headers.entries()) out
|
|
674
|
-
return out
|
|
771
|
+
const out = {};
|
|
772
|
+
for (const [key, value] of headers.entries()) out[key] = value;
|
|
773
|
+
return out;
|
|
675
774
|
}
|
|
676
775
|
if (Array.isArray(headers)) {
|
|
677
|
-
const out
|
|
678
|
-
for (const [key, value] of headers) out
|
|
679
|
-
return out
|
|
776
|
+
const out = {};
|
|
777
|
+
for (const [key, value] of headers) out[key] = value;
|
|
778
|
+
return out;
|
|
680
779
|
}
|
|
681
780
|
const out = {};
|
|
682
781
|
for (const [key, value] of Object.entries(headers ?? {})) out[key] = value;
|
|
@@ -725,7 +824,7 @@ async function copilotFetch(input, init, ctx) {
|
|
|
725
824
|
headers: snapshotHeaders(init),
|
|
726
825
|
...snapshotBody(init)
|
|
727
826
|
};
|
|
728
|
-
const response = await fetch(input, init);
|
|
827
|
+
const response = await (ctx.fetchImpl ?? requestContext.getStore()?.fetchImpl ?? fetch)(input, init);
|
|
729
828
|
if (!(ctx.requestId !== void 0 && ctx.capturable !== false && shouldCaptureStatus(response.status))) return response;
|
|
730
829
|
const contentType = response.headers.get("content-type") ?? "";
|
|
731
830
|
const isSSE = contentType.includes("text/event-stream");
|
|
@@ -801,7 +900,6 @@ function persistNow({ requestSnapshot, status, responseBody, responseBodyKind, r
|
|
|
801
900
|
consola.debug("copilotFetch persist failed", error);
|
|
802
901
|
}
|
|
803
902
|
}
|
|
804
|
-
|
|
805
903
|
//#endregion
|
|
806
904
|
//#region src/services/copilot/get-models.ts
|
|
807
905
|
const getModels = async (account, options) => {
|
|
@@ -826,7 +924,6 @@ const getModels = async (account, options) => {
|
|
|
826
924
|
} catch {}
|
|
827
925
|
return models;
|
|
828
926
|
};
|
|
829
|
-
|
|
830
927
|
//#endregion
|
|
831
928
|
//#region src/lib/accounts-manager-auth.ts
|
|
832
929
|
const takeAuthSnapshot = (account) => ({
|
|
@@ -895,7 +992,6 @@ const applyUnauthorizedIfCurrent = (account, snapshot, reason) => {
|
|
|
895
992
|
setAccountFailedState(account, reason);
|
|
896
993
|
return true;
|
|
897
994
|
};
|
|
898
|
-
|
|
899
995
|
//#endregion
|
|
900
996
|
//#region src/lib/accounts-manager-quota.ts
|
|
901
997
|
const getCostUnits = (model) => {
|
|
@@ -930,10 +1026,6 @@ const releasePremiumReservation = (account, reservation) => {
|
|
|
930
1026
|
account.premiumReserved = Math.max(0, nextReserved);
|
|
931
1027
|
if (reservations.size === 0) account.premiumReservations = void 0;
|
|
932
1028
|
};
|
|
933
|
-
|
|
934
|
-
//#endregion
|
|
935
|
-
//#region src/lib/stats-store.ts
|
|
936
|
-
const DEFAULT_STATS_RETENTION_DAYS = 180;
|
|
937
1029
|
function toLocalDateString(ms) {
|
|
938
1030
|
const d = new Date(ms);
|
|
939
1031
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
@@ -1082,7 +1174,7 @@ var StatsStore = class {
|
|
|
1082
1174
|
});
|
|
1083
1175
|
return this.mergeMetricsAndConsumption(dailyMetrics, byAccountMetrics, consumption);
|
|
1084
1176
|
}
|
|
1085
|
-
cleanupStatsRetention(retentionDays =
|
|
1177
|
+
cleanupStatsRetention(retentionDays = 180) {
|
|
1086
1178
|
try {
|
|
1087
1179
|
const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
1088
1180
|
const cutoffDate = toLocalDateString(cutoffMs);
|
|
@@ -1151,7 +1243,6 @@ var StatsStore = class {
|
|
|
1151
1243
|
};
|
|
1152
1244
|
}
|
|
1153
1245
|
};
|
|
1154
|
-
|
|
1155
1246
|
//#endregion
|
|
1156
1247
|
//#region src/lib/request-history.ts
|
|
1157
1248
|
const DEFAULT_RETENTION_DAYS = 35;
|
|
@@ -1299,6 +1390,8 @@ function buildInsertArgs(record) {
|
|
|
1299
1390
|
toDbNull(record.affinityKeyUsed),
|
|
1300
1391
|
toDbNull(record.affinityKeySource),
|
|
1301
1392
|
toDbNull(record.selectionReason),
|
|
1393
|
+
toDbNull(record.responsesItemOwnerLookupKeysJson),
|
|
1394
|
+
toDbNull(record.responsesItemOwnerRecordedKeysJson),
|
|
1302
1395
|
toDbNull(record.tokensInput),
|
|
1303
1396
|
toDbNull(record.tokensOutput),
|
|
1304
1397
|
toDbNull(record.tokensTotal),
|
|
@@ -1359,6 +1452,8 @@ var RequestHistoryStore = class {
|
|
|
1359
1452
|
"affinity_key_used",
|
|
1360
1453
|
"affinity_key_source",
|
|
1361
1454
|
"selection_reason",
|
|
1455
|
+
"responses_item_owner_lookup_keys_json",
|
|
1456
|
+
"responses_item_owner_recorded_keys_json",
|
|
1362
1457
|
"tokens_input",
|
|
1363
1458
|
"tokens_output",
|
|
1364
1459
|
"tokens_total",
|
|
@@ -1535,7 +1630,7 @@ var RequestHistoryStore = class {
|
|
|
1535
1630
|
} catch (error) {
|
|
1536
1631
|
consola.debug("Failed to cleanup request_log retention", error);
|
|
1537
1632
|
}
|
|
1538
|
-
import("./request-outbound-
|
|
1633
|
+
import("./request-outbound-BJjWS_jF.js").then((m) => m.getRequestOutboundStore().cleanupOrphans()).catch(() => {});
|
|
1539
1634
|
}
|
|
1540
1635
|
meta() {
|
|
1541
1636
|
return {
|
|
@@ -1627,15 +1722,18 @@ function extractResponsesUsageFromResult(result) {
|
|
|
1627
1722
|
function getStatsStore() {
|
|
1628
1723
|
return getStatsStoreInstance();
|
|
1629
1724
|
}
|
|
1630
|
-
|
|
1631
1725
|
//#endregion
|
|
1632
1726
|
//#region src/lib/session-affinity-store.ts
|
|
1633
1727
|
const DAY_MS = 1440 * 60 * 1e3;
|
|
1634
1728
|
const DEFAULT_MAX_AGE_MS = 7 * DAY_MS;
|
|
1635
1729
|
const CLEANUP_INTERVAL_MS = DAY_MS;
|
|
1730
|
+
const MAX_BATCH_KEYS = 500;
|
|
1636
1731
|
const maybeUnref = (timer) => {
|
|
1637
1732
|
timer.unref();
|
|
1638
1733
|
};
|
|
1734
|
+
function* chunks(values, size) {
|
|
1735
|
+
for (let index = 0; index < values.length; index += size) yield values.slice(index, index + size);
|
|
1736
|
+
}
|
|
1639
1737
|
var SessionAffinityStore = class {
|
|
1640
1738
|
db;
|
|
1641
1739
|
constructor(db) {
|
|
@@ -1657,6 +1755,65 @@ var SessionAffinityStore = class {
|
|
|
1657
1755
|
}
|
|
1658
1756
|
return row.account_id;
|
|
1659
1757
|
}
|
|
1758
|
+
getMany(cacheKeys) {
|
|
1759
|
+
const uniqueKeys = [...new Set(cacheKeys)].filter(Boolean);
|
|
1760
|
+
const result = /* @__PURE__ */ new Map();
|
|
1761
|
+
let chunkIndex = 0;
|
|
1762
|
+
for (const keys of chunks(uniqueKeys, MAX_BATCH_KEYS)) {
|
|
1763
|
+
try {
|
|
1764
|
+
this.readManyChunk(keys, result);
|
|
1765
|
+
} catch (error) {
|
|
1766
|
+
consola.error("Failed to batch-read session affinity mappings", {
|
|
1767
|
+
chunkIndex,
|
|
1768
|
+
chunkKeyCount: keys.length,
|
|
1769
|
+
error,
|
|
1770
|
+
totalKeyCount: uniqueKeys.length
|
|
1771
|
+
});
|
|
1772
|
+
throw error;
|
|
1773
|
+
}
|
|
1774
|
+
chunkIndex += 1;
|
|
1775
|
+
}
|
|
1776
|
+
if (result.size === 0) return result;
|
|
1777
|
+
const now = Date.now();
|
|
1778
|
+
let touchChunkIndex = 0;
|
|
1779
|
+
const touchedKeyCount = result.size;
|
|
1780
|
+
for (const keys of chunks([...result.keys()], MAX_BATCH_KEYS)) {
|
|
1781
|
+
try {
|
|
1782
|
+
this.touchManyChunk(keys, now);
|
|
1783
|
+
} catch (error) {
|
|
1784
|
+
consola.warn("Failed to batch-update session affinity last_used_at_ms", {
|
|
1785
|
+
chunkIndex: touchChunkIndex,
|
|
1786
|
+
chunkKeyCount: keys.length,
|
|
1787
|
+
error,
|
|
1788
|
+
touchedKeyCount
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
touchChunkIndex += 1;
|
|
1792
|
+
}
|
|
1793
|
+
return result;
|
|
1794
|
+
}
|
|
1795
|
+
readManyChunk(cacheKeys, result) {
|
|
1796
|
+
if (cacheKeys.length === 0) return;
|
|
1797
|
+
const placeholders = cacheKeys.map(() => "?").join(", ");
|
|
1798
|
+
const rows = this.db.query(`SELECT cache_key, account_id FROM session_affinity
|
|
1799
|
+
WHERE cache_key IN (${placeholders});`).all(...cacheKeys);
|
|
1800
|
+
for (const row of rows) {
|
|
1801
|
+
if (!row.cache_key || !row.account_id) {
|
|
1802
|
+
consola.error("Invalid session affinity row returned from database", {
|
|
1803
|
+
hasAccountId: Boolean(row.account_id),
|
|
1804
|
+
hasCacheKey: Boolean(row.cache_key)
|
|
1805
|
+
});
|
|
1806
|
+
throw new Error("Invalid session affinity row returned from database");
|
|
1807
|
+
}
|
|
1808
|
+
result.set(row.cache_key, row.account_id);
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
touchManyChunk(cacheKeys, now) {
|
|
1812
|
+
if (cacheKeys.length === 0) return;
|
|
1813
|
+
const placeholders = cacheKeys.map(() => "?").join(", ");
|
|
1814
|
+
this.db.query(`UPDATE session_affinity SET last_used_at_ms = ?
|
|
1815
|
+
WHERE cache_key IN (${placeholders});`).run(now, ...cacheKeys);
|
|
1816
|
+
}
|
|
1660
1817
|
set(cacheKey, accountId) {
|
|
1661
1818
|
const now = Date.now();
|
|
1662
1819
|
try {
|
|
@@ -1724,7 +1881,6 @@ function applySharedSessionAffinityRetention(retentionMs = getSessionAffinityRet
|
|
|
1724
1881
|
consola.warn("Failed to apply session affinity retention:", error);
|
|
1725
1882
|
}
|
|
1726
1883
|
}
|
|
1727
|
-
|
|
1728
1884
|
//#endregion
|
|
1729
1885
|
//#region src/lib/session-ownership.ts
|
|
1730
1886
|
const DEFAULT_MAX_ENTRIES = 1e4;
|
|
@@ -1770,7 +1926,6 @@ var SessionOwnershipCache = class {
|
|
|
1770
1926
|
this.cache.clear();
|
|
1771
1927
|
}
|
|
1772
1928
|
};
|
|
1773
|
-
|
|
1774
1929
|
//#endregion
|
|
1775
1930
|
//#region src/lib/accounts-manager.ts
|
|
1776
1931
|
/** Quota cache TTL in milliseconds (45 seconds) for pre-request selection. */
|
|
@@ -1798,6 +1953,10 @@ function getInitialSelectionReason(cacheKey, rotationStart) {
|
|
|
1798
1953
|
function preserveSubagentSelectionReason(initialSelectionReason, nextSelectionReason) {
|
|
1799
1954
|
return initialSelectionReason.startsWith("subagent_") ? initialSelectionReason : nextSelectionReason;
|
|
1800
1955
|
}
|
|
1956
|
+
function normalizeCacheKeys(keys) {
|
|
1957
|
+
if (!keys) return [];
|
|
1958
|
+
return [...new Set(keys.map((key) => key.trim()).filter(Boolean))];
|
|
1959
|
+
}
|
|
1801
1960
|
/** Manages multiple GitHub Copilot accounts at runtime. */
|
|
1802
1961
|
var AccountsManager = class {
|
|
1803
1962
|
accounts = /* @__PURE__ */ new Map();
|
|
@@ -1873,6 +2032,12 @@ var AccountsManager = class {
|
|
|
1873
2032
|
this.modelsRefreshIntervalMs = Number.isFinite(intervalMs) && intervalMs > 0 ? intervalMs : 0;
|
|
1874
2033
|
this.scheduleModelsRefresh();
|
|
1875
2034
|
}
|
|
2035
|
+
getQuotaRefreshAccounts() {
|
|
2036
|
+
return this.getOrderedEnabledAccounts().filter((account) => !this.isAccountFailed(account) && account.copilotToken !== void 0);
|
|
2037
|
+
}
|
|
2038
|
+
refreshAccountQuota(account) {
|
|
2039
|
+
return this.refreshQuota(account);
|
|
2040
|
+
}
|
|
1876
2041
|
computeTokenRefreshDelayMs(refreshInSeconds) {
|
|
1877
2042
|
const baseDelay = Math.max((refreshInSeconds - 60) * 1e3, 1e3);
|
|
1878
2043
|
const jitter = Math.floor(Math.random() * TOKEN_REFRESH_JITTER_MS);
|
|
@@ -2309,6 +2474,15 @@ var AccountsManager = class {
|
|
|
2309
2474
|
async selectAccountForRequest(candidates, context) {
|
|
2310
2475
|
if (candidates.length === 0) throw new Error("selectAccountForRequest requires at least one candidate");
|
|
2311
2476
|
const orderedAccounts = this.getOrderedEnabledAccounts();
|
|
2477
|
+
const itemOwnerSelection = await this.selectPreferredResponsesItemOwner({
|
|
2478
|
+
ownershipKeys: context?.responsesItemOwnershipKeys,
|
|
2479
|
+
orderedAccounts,
|
|
2480
|
+
candidates
|
|
2481
|
+
});
|
|
2482
|
+
if (itemOwnerSelection.result) {
|
|
2483
|
+
this.attachConfirmOwnership(itemOwnerSelection.result, context?.ownershipWriteSessionId);
|
|
2484
|
+
return itemOwnerSelection.result;
|
|
2485
|
+
}
|
|
2312
2486
|
const ownerSelection = await this.selectPreferredSessionOwner({
|
|
2313
2487
|
lookupSessionId: context?.ownershipLookupSessionId,
|
|
2314
2488
|
orderedAccounts,
|
|
@@ -2389,6 +2563,12 @@ var AccountsManager = class {
|
|
|
2389
2563
|
if (!normalizedCacheKey) return;
|
|
2390
2564
|
this.affinityCache.delete(normalizedCacheKey);
|
|
2391
2565
|
}
|
|
2566
|
+
recordResponsesItemOwnership(ownershipKeys, accountId) {
|
|
2567
|
+
if (!this.accountAffinityEnabled) return;
|
|
2568
|
+
const normalizedAccountId = accountId.trim();
|
|
2569
|
+
if (!normalizedAccountId) return;
|
|
2570
|
+
for (const key of normalizeCacheKeys(ownershipKeys)) this.affinityCache.set(key, normalizedAccountId);
|
|
2571
|
+
}
|
|
2392
2572
|
attachConfirmOwnership(result, ownershipWriteSessionId) {
|
|
2393
2573
|
const rootSessionId = ownershipWriteSessionId?.trim();
|
|
2394
2574
|
if (!rootSessionId) return;
|
|
@@ -2398,6 +2578,52 @@ var AccountsManager = class {
|
|
|
2398
2578
|
this.sessionOwnership.set(rootSessionId, result.account.id);
|
|
2399
2579
|
};
|
|
2400
2580
|
}
|
|
2581
|
+
async selectPreferredResponsesItemOwner(params) {
|
|
2582
|
+
if (!this.accountAffinityEnabled) return {};
|
|
2583
|
+
const { orderedAccounts, candidates } = params;
|
|
2584
|
+
const ownershipKeys = normalizeCacheKeys(params.ownershipKeys);
|
|
2585
|
+
if (ownershipKeys.length === 0) return {};
|
|
2586
|
+
let ownershipMappings;
|
|
2587
|
+
try {
|
|
2588
|
+
ownershipMappings = this.affinityCache.getMany(ownershipKeys);
|
|
2589
|
+
} catch (error) {
|
|
2590
|
+
consola.warn("Failed to read Responses item owner mappings; falling back", {
|
|
2591
|
+
error,
|
|
2592
|
+
ownershipKeyCount: ownershipKeys.length
|
|
2593
|
+
});
|
|
2594
|
+
return {};
|
|
2595
|
+
}
|
|
2596
|
+
const accountIds = /* @__PURE__ */ new Map();
|
|
2597
|
+
for (const key of ownershipKeys) {
|
|
2598
|
+
const accountId = ownershipMappings.get(key);
|
|
2599
|
+
if (!accountId) continue;
|
|
2600
|
+
accountIds.set(accountId, key);
|
|
2601
|
+
if (accountIds.size > 1) {
|
|
2602
|
+
consola.warn("Conflicting Responses item owner mappings; falling back", {
|
|
2603
|
+
accountIds: [...accountIds.keys()],
|
|
2604
|
+
ownershipKeyCount: ownershipKeys.length
|
|
2605
|
+
});
|
|
2606
|
+
return {};
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
if (accountIds.size === 0) return {};
|
|
2610
|
+
const [[accountId, ownerKey]] = [...accountIds];
|
|
2611
|
+
const ownerResult = await this.tryAffinityAccount(accountId, orderedAccounts, candidates);
|
|
2612
|
+
if (!ownerResult) {
|
|
2613
|
+
if (this.isMissingOrDisabledAccount(accountId, orderedAccounts)) this.deleteResponsesItemOwnershipMappings(ownershipKeys, accountId);
|
|
2614
|
+
return {};
|
|
2615
|
+
}
|
|
2616
|
+
ownerResult.affinityHit = true;
|
|
2617
|
+
ownerResult.affinityCacheKey = ownerKey;
|
|
2618
|
+
ownerResult.selectionReason = "responses_item_owner_hit";
|
|
2619
|
+
return { result: ownerResult };
|
|
2620
|
+
}
|
|
2621
|
+
isMissingOrDisabledAccount(accountId, orderedAccounts) {
|
|
2622
|
+
return !orderedAccounts.some((account) => account.id === accountId);
|
|
2623
|
+
}
|
|
2624
|
+
deleteResponsesItemOwnershipMappings(ownershipKeys, accountId) {
|
|
2625
|
+
for (const key of ownershipKeys) if (this.affinityCache.get(key) === accountId) this.affinityCache.delete(key);
|
|
2626
|
+
}
|
|
2401
2627
|
async selectPreferredSessionOwner(params) {
|
|
2402
2628
|
const { lookupSessionId, orderedAccounts, candidates } = params;
|
|
2403
2629
|
if (lookupSessionId === void 0) return {};
|
|
@@ -2463,13 +2689,13 @@ var AccountsManager = class {
|
|
|
2463
2689
|
if (scoredAccounts.length > 0) {
|
|
2464
2690
|
const scoredAccountsInSelectionOrder = this.shuffleWithinEqualRemainingBuckets(scoredAccounts).map(({ account }) => account);
|
|
2465
2691
|
const scoredAccountSet = new Set(scoredAccountsInSelectionOrder);
|
|
2466
|
-
const unknownQuotaAccounts
|
|
2467
|
-
const unlimitedAccounts
|
|
2692
|
+
const unknownQuotaAccounts = orderedAccounts.filter((account) => !scoredAccountSet.has(account) && !account.unlimited);
|
|
2693
|
+
const unlimitedAccounts = orderedAccounts.filter((account) => !scoredAccountSet.has(account) && account.unlimited);
|
|
2468
2694
|
return {
|
|
2469
2695
|
accounts: [
|
|
2470
2696
|
...scoredAccountsInSelectionOrder,
|
|
2471
|
-
...unknownQuotaAccounts
|
|
2472
|
-
...unlimitedAccounts
|
|
2697
|
+
...unknownQuotaAccounts,
|
|
2698
|
+
...unlimitedAccounts
|
|
2473
2699
|
],
|
|
2474
2700
|
premiumRemainingOrderedAccountIds: new Set(scoredAccountsInSelectionOrder.map((account) => account.id))
|
|
2475
2701
|
};
|
|
@@ -2868,7 +3094,223 @@ var AccountsManager = class {
|
|
|
2868
3094
|
};
|
|
2869
3095
|
/** Singleton instance of AccountsManager */
|
|
2870
3096
|
const accountsManager = new AccountsManager({ persistentAffinityStore: getSharedSessionAffinityStore });
|
|
2871
|
-
|
|
2872
3097
|
//#endregion
|
|
2873
|
-
|
|
2874
|
-
|
|
3098
|
+
//#region src/lib/accounts-manager-quota-scheduler.ts
|
|
3099
|
+
const QUOTA_REFRESH_FRESHNESS_GUARD_MS = 300 * 1e3;
|
|
3100
|
+
const QUOTA_REFRESH_INTERVAL_JITTER_RATIO = .1;
|
|
3101
|
+
const defaultTimer = {
|
|
3102
|
+
setTimer(callback, delayMs) {
|
|
3103
|
+
return setTimeout(callback, delayMs);
|
|
3104
|
+
},
|
|
3105
|
+
clearTimer(handle) {
|
|
3106
|
+
clearTimeout(handle);
|
|
3107
|
+
}
|
|
3108
|
+
};
|
|
3109
|
+
function secondsToMs(seconds) {
|
|
3110
|
+
return seconds * 1e3;
|
|
3111
|
+
}
|
|
3112
|
+
function minutesToMs(minutes) {
|
|
3113
|
+
return minutes * 60 * 1e3;
|
|
3114
|
+
}
|
|
3115
|
+
function isSchedulingEnabled(config) {
|
|
3116
|
+
return config.enabled && config.intervalMinutes > 0;
|
|
3117
|
+
}
|
|
3118
|
+
var QuotaRefreshScheduler = class {
|
|
3119
|
+
config;
|
|
3120
|
+
logger;
|
|
3121
|
+
manager;
|
|
3122
|
+
now;
|
|
3123
|
+
random;
|
|
3124
|
+
timer;
|
|
3125
|
+
generation = 0;
|
|
3126
|
+
pendingDelay;
|
|
3127
|
+
roundTimer;
|
|
3128
|
+
stopped = true;
|
|
3129
|
+
constructor(options) {
|
|
3130
|
+
this.config = options.config;
|
|
3131
|
+
this.logger = options.logger ?? consola;
|
|
3132
|
+
this.manager = options.manager;
|
|
3133
|
+
this.now = options.now ?? Date.now;
|
|
3134
|
+
this.random = options.random ?? Math.random;
|
|
3135
|
+
this.timer = options.timer ?? defaultTimer;
|
|
3136
|
+
}
|
|
3137
|
+
start(config = this.config) {
|
|
3138
|
+
this.config = config;
|
|
3139
|
+
this.stop();
|
|
3140
|
+
if (!isSchedulingEnabled(this.config)) return;
|
|
3141
|
+
this.stopped = false;
|
|
3142
|
+
this.scheduleRound(secondsToMs(this.config.startupDelaySeconds), this.generation);
|
|
3143
|
+
}
|
|
3144
|
+
stop() {
|
|
3145
|
+
this.generation += 1;
|
|
3146
|
+
this.stopped = true;
|
|
3147
|
+
this.clearRoundTimer();
|
|
3148
|
+
this.clearPendingDelay();
|
|
3149
|
+
}
|
|
3150
|
+
updateConfig(config) {
|
|
3151
|
+
this.start(config);
|
|
3152
|
+
}
|
|
3153
|
+
isActiveGeneration(generation) {
|
|
3154
|
+
return !this.stopped && this.generation === generation;
|
|
3155
|
+
}
|
|
3156
|
+
scheduleRound(delayMs, generation) {
|
|
3157
|
+
this.clearRoundTimer();
|
|
3158
|
+
const handle = this.timer.setTimer(() => {
|
|
3159
|
+
if (this.roundTimer === handle) this.roundTimer = void 0;
|
|
3160
|
+
if (this.isActiveGeneration(generation)) this.runRound(generation);
|
|
3161
|
+
}, Math.max(0, delayMs));
|
|
3162
|
+
this.roundTimer = handle;
|
|
3163
|
+
}
|
|
3164
|
+
scheduleNextRound(generation) {
|
|
3165
|
+
if (!this.isActiveGeneration(generation) || !isSchedulingEnabled(this.config)) return;
|
|
3166
|
+
const intervalMs = minutesToMs(this.config.intervalMinutes);
|
|
3167
|
+
this.scheduleRound(this.withJitter(intervalMs), generation);
|
|
3168
|
+
}
|
|
3169
|
+
withJitter(intervalMs) {
|
|
3170
|
+
const jitterMs = Math.floor(intervalMs * QUOTA_REFRESH_INTERVAL_JITTER_RATIO * (this.random() * 2 - 1));
|
|
3171
|
+
return Math.max(0, intervalMs + jitterMs);
|
|
3172
|
+
}
|
|
3173
|
+
getStaggerDelayMs() {
|
|
3174
|
+
const minMs = secondsToMs(this.config.staggerMinSeconds);
|
|
3175
|
+
const maxMs = secondsToMs(this.config.staggerMaxSeconds);
|
|
3176
|
+
if (maxMs <= minMs) return minMs;
|
|
3177
|
+
return minMs + Math.floor(this.random() * (maxMs - minMs));
|
|
3178
|
+
}
|
|
3179
|
+
isFresh(account) {
|
|
3180
|
+
if (account.lastQuotaFetch === void 0) return false;
|
|
3181
|
+
return this.now() - account.lastQuotaFetch < QUOTA_REFRESH_FRESHNESS_GUARD_MS;
|
|
3182
|
+
}
|
|
3183
|
+
async schedulerDelay(delayMs) {
|
|
3184
|
+
this.clearPendingDelay();
|
|
3185
|
+
await new Promise((resolve) => {
|
|
3186
|
+
const handle = this.timer.setTimer(() => {
|
|
3187
|
+
if (this.pendingDelay?.handle === handle) this.pendingDelay = void 0;
|
|
3188
|
+
resolve();
|
|
3189
|
+
}, Math.max(0, delayMs));
|
|
3190
|
+
this.pendingDelay = {
|
|
3191
|
+
handle,
|
|
3192
|
+
resolve
|
|
3193
|
+
};
|
|
3194
|
+
});
|
|
3195
|
+
}
|
|
3196
|
+
clearRoundTimer() {
|
|
3197
|
+
if (this.roundTimer === void 0) return;
|
|
3198
|
+
this.timer.clearTimer(this.roundTimer);
|
|
3199
|
+
this.roundTimer = void 0;
|
|
3200
|
+
}
|
|
3201
|
+
clearPendingDelay() {
|
|
3202
|
+
if (!this.pendingDelay) return;
|
|
3203
|
+
const { handle, resolve } = this.pendingDelay;
|
|
3204
|
+
this.pendingDelay = void 0;
|
|
3205
|
+
this.timer.clearTimer(handle);
|
|
3206
|
+
resolve();
|
|
3207
|
+
}
|
|
3208
|
+
async runRound(generation) {
|
|
3209
|
+
try {
|
|
3210
|
+
const accounts = this.manager.getQuotaRefreshAccounts();
|
|
3211
|
+
for (const [index, account] of accounts.entries()) {
|
|
3212
|
+
if (!this.isActiveGeneration(generation)) return;
|
|
3213
|
+
if (this.isFresh(account)) continue;
|
|
3214
|
+
try {
|
|
3215
|
+
await this.manager.refreshAccountQuota(account);
|
|
3216
|
+
} catch (error) {
|
|
3217
|
+
if (this.isActiveGeneration(generation)) this.logger.debug("Background quota refresh failed", {
|
|
3218
|
+
accountId: account.id,
|
|
3219
|
+
error
|
|
3220
|
+
});
|
|
3221
|
+
}
|
|
3222
|
+
if (!this.isActiveGeneration(generation)) return;
|
|
3223
|
+
if (index < accounts.length - 1) {
|
|
3224
|
+
await this.schedulerDelay(this.getStaggerDelayMs());
|
|
3225
|
+
if (!this.isActiveGeneration(generation)) return;
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
} catch (error) {
|
|
3229
|
+
if (this.isActiveGeneration(generation)) this.logger.error("Background quota refresh round failed", error);
|
|
3230
|
+
} finally {
|
|
3231
|
+
this.scheduleNextRound(generation);
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
};
|
|
3235
|
+
//#endregion
|
|
3236
|
+
//#region src/lib/quota-refresh-scheduler-runtime.ts
|
|
3237
|
+
const quotaRefreshScheduler = new QuotaRefreshScheduler({
|
|
3238
|
+
config: getQuotaRefreshConfig(),
|
|
3239
|
+
manager: accountsManager
|
|
3240
|
+
});
|
|
3241
|
+
const registeredShutdownRuntimes = /* @__PURE__ */ new WeakSet();
|
|
3242
|
+
let isQuotaRefreshSchedulerStarted = false;
|
|
3243
|
+
function startQuotaRefreshSchedulerFromConfig() {
|
|
3244
|
+
isQuotaRefreshSchedulerStarted = true;
|
|
3245
|
+
quotaRefreshScheduler.start(getQuotaRefreshConfig());
|
|
3246
|
+
}
|
|
3247
|
+
function stopQuotaRefreshScheduler() {
|
|
3248
|
+
isQuotaRefreshSchedulerStarted = false;
|
|
3249
|
+
quotaRefreshScheduler.stop();
|
|
3250
|
+
}
|
|
3251
|
+
function registerQuotaRefreshSchedulerShutdownCleanup(runtime = process) {
|
|
3252
|
+
if (registeredShutdownRuntimes.has(runtime)) return;
|
|
3253
|
+
registeredShutdownRuntimes.add(runtime);
|
|
3254
|
+
runtime.once("exit", () => stopQuotaRefreshScheduler());
|
|
3255
|
+
}
|
|
3256
|
+
function updateQuotaRefreshSchedulerFromConfig() {
|
|
3257
|
+
if (!isQuotaRefreshSchedulerStarted) return;
|
|
3258
|
+
quotaRefreshScheduler.updateConfig(getQuotaRefreshConfig());
|
|
3259
|
+
}
|
|
3260
|
+
//#endregion
|
|
3261
|
+
//#region src/lib/proxy.ts
|
|
3262
|
+
let proxyEnvDispatcher;
|
|
3263
|
+
function getProxyEnvDispatcher() {
|
|
3264
|
+
return proxyEnvDispatcher;
|
|
3265
|
+
}
|
|
3266
|
+
function initProxyFromEnv() {
|
|
3267
|
+
try {
|
|
3268
|
+
const direct = new Agent();
|
|
3269
|
+
const proxies = /* @__PURE__ */ new Map();
|
|
3270
|
+
proxyEnvDispatcher = {
|
|
3271
|
+
dispatch(options, handler) {
|
|
3272
|
+
try {
|
|
3273
|
+
const origin = typeof options.origin === "string" ? new URL(options.origin) : options.origin;
|
|
3274
|
+
const raw = getProxyForUrl(origin.toString());
|
|
3275
|
+
const proxyUrl = raw && raw.length > 0 ? raw : void 0;
|
|
3276
|
+
if (!proxyUrl) {
|
|
3277
|
+
consola.debug(`HTTP proxy bypass: ${origin.hostname}`);
|
|
3278
|
+
return direct.dispatch(options, handler);
|
|
3279
|
+
}
|
|
3280
|
+
let agent = proxies.get(proxyUrl);
|
|
3281
|
+
if (!agent) {
|
|
3282
|
+
agent = new ProxyAgent(proxyUrl);
|
|
3283
|
+
proxies.set(proxyUrl, agent);
|
|
3284
|
+
}
|
|
3285
|
+
let label = proxyUrl;
|
|
3286
|
+
try {
|
|
3287
|
+
const u = new URL(proxyUrl);
|
|
3288
|
+
label = `${u.protocol}//${u.host}`;
|
|
3289
|
+
} catch {}
|
|
3290
|
+
consola.debug(`HTTP proxy route: ${origin.hostname} via ${label}`);
|
|
3291
|
+
return agent.dispatch(options, handler);
|
|
3292
|
+
} catch {
|
|
3293
|
+
return direct.dispatch(options, handler);
|
|
3294
|
+
}
|
|
3295
|
+
},
|
|
3296
|
+
close() {
|
|
3297
|
+
return direct.close();
|
|
3298
|
+
},
|
|
3299
|
+
destroy() {
|
|
3300
|
+
return direct.destroy();
|
|
3301
|
+
}
|
|
3302
|
+
};
|
|
3303
|
+
if (typeof Bun !== "undefined") {
|
|
3304
|
+
consola.debug("WebSocket proxy configured from environment (per-URL)");
|
|
3305
|
+
return;
|
|
3306
|
+
}
|
|
3307
|
+
setGlobalDispatcher(proxyEnvDispatcher);
|
|
3308
|
+
consola.debug("HTTP proxy configured from environment (per-URL)");
|
|
3309
|
+
} catch (err) {
|
|
3310
|
+
consola.debug("Proxy setup skipped:", err);
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
//#endregion
|
|
3314
|
+
export { getModelRefreshIntervalMs as A, isResponsesApiWebSocketEnabled as B, getAnthropicApiKey as C, getLogLevel as D, getExtraPromptForModel as E, isForceAgentEnabled as F, resolveModelAlias as H, isMessageStartInputTokensFallbackEnabled as I, isMessagesApiEnabled as L, getReasoningEffortForModel as M, getSmallModel as N, getModelAliases as O, isAccountAffinityEnabled as P, isResponsesApiContextManagementModel as R, getAliasTargetSet as S, getConfig as T, shouldCompactUseSmallModel as U, mergeConfigWithDefaults as V, toLocalDateString as _, stopQuotaRefreshScheduler as a, isDevModeEnabled as b, applySharedSessionAffinityRetention as c, getClientIpInfo as d, getRequestHistoryStore as f, normalizeMessagesUsage as g, normalizeEmbeddingsUsage as h, startQuotaRefreshSchedulerFromConfig as i, getProviderConfig as j, getModelAliasesInfo as k, extractResponsesUsageFromResult as l, normalizeChatCompletionsUsage as m, initProxyFromEnv as n, updateQuotaRefreshSchedulerFromConfig as o, getStatsStore as p, registerQuotaRefreshSchedulerShutdownCleanup as r, accountsManager as s, getProxyEnvDispatcher as t, extractResponsesUsageFromStreamEvent as u, copilotFetch as v, getClaudeTokenMultiplier as w, PROVIDER_TYPE_ANTHROPIC as x, flushPendingCapture as y, isResponsesApiWebSearchEnabled as z };
|
|
3315
|
+
|
|
3316
|
+
//# sourceMappingURL=proxy-BwmADhKh.js.map
|