@cortexkit/opencode-antigravity-auth 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -8
- package/dist/index.js +1299 -431
- package/dist/index.js.map +4 -4
- package/dist/src/plugin/accounts.d.ts +29 -9
- package/dist/src/plugin/accounts.d.ts.map +1 -1
- package/dist/src/plugin/accounts.js +253 -52
- package/dist/src/plugin/accounts.js.map +1 -1
- package/dist/src/plugin/agy-request-metadata.d.ts +2 -0
- package/dist/src/plugin/agy-request-metadata.d.ts.map +1 -0
- package/dist/src/plugin/agy-request-metadata.js +2 -0
- package/dist/src/plugin/agy-request-metadata.js.map +1 -0
- package/dist/src/plugin/auth-doctor.d.ts +1 -1
- package/dist/src/plugin/auth-doctor.d.ts.map +1 -1
- package/dist/src/plugin/auth-doctor.js +9 -0
- package/dist/src/plugin/auth-doctor.js.map +1 -1
- package/dist/src/plugin/image-saver.d.ts +2 -21
- package/dist/src/plugin/image-saver.d.ts.map +1 -1
- package/dist/src/plugin/image-saver.js +29 -63
- package/dist/src/plugin/image-saver.js.map +1 -1
- package/dist/src/plugin/request-helpers.d.ts +1 -0
- package/dist/src/plugin/request-helpers.d.ts.map +1 -1
- package/dist/src/plugin/request-helpers.js +9 -4
- package/dist/src/plugin/request-helpers.js.map +1 -1
- package/dist/src/plugin/request.d.ts +6 -0
- package/dist/src/plugin/request.d.ts.map +1 -1
- package/dist/src/plugin/request.js +230 -57
- package/dist/src/plugin/request.js.map +1 -1
- package/dist/src/plugin/session-context.d.ts +21 -0
- package/dist/src/plugin/session-context.d.ts.map +1 -0
- package/dist/src/plugin/session-context.js +63 -0
- package/dist/src/plugin/session-context.js.map +1 -0
- package/dist/src/plugin/storage.d.ts +6 -0
- package/dist/src/plugin/storage.d.ts.map +1 -1
- package/dist/src/plugin/storage.js +14 -3
- package/dist/src/plugin/storage.js.map +1 -1
- package/dist/src/plugin/ui/auth-menu.d.ts +1 -1
- package/dist/src/plugin/ui/auth-menu.d.ts.map +1 -1
- package/dist/src/plugin/ui/auth-menu.js +3 -1
- package/dist/src/plugin/ui/auth-menu.js.map +1 -1
- package/dist/src/plugin/ui/quota-status.d.ts.map +1 -1
- package/dist/src/plugin/ui/quota-status.js +2 -1
- package/dist/src/plugin/ui/quota-status.js.map +1 -1
- package/dist/src/plugin.d.ts +16 -0
- package/dist/src/plugin.d.ts.map +1 -1
- package/dist/src/plugin.js +261 -101
- package/dist/src/plugin.js.map +1 -1
- package/package.json +2 -2
|
@@ -42,6 +42,10 @@ export interface ManagedAccount {
|
|
|
42
42
|
verificationRequiredAt?: number;
|
|
43
43
|
verificationRequiredReason?: string;
|
|
44
44
|
verificationUrl?: string;
|
|
45
|
+
accountIneligible?: boolean;
|
|
46
|
+
accountIneligibleAt?: number;
|
|
47
|
+
accountIneligibleReason?: string;
|
|
48
|
+
eligibilityStateUpdatedAt?: number;
|
|
45
49
|
/** Daily request counts per model family */
|
|
46
50
|
dailyRequestCounts?: {
|
|
47
51
|
date: string;
|
|
@@ -63,6 +67,10 @@ export interface ManagedAccount {
|
|
|
63
67
|
*/
|
|
64
68
|
export declare function resolveQuotaGroup(family: ModelFamily, model?: string | null): QuotaGroup;
|
|
65
69
|
export declare function computeSoftQuotaCacheTtlMs(ttlConfig: "auto" | number, refreshIntervalMinutes: number): number;
|
|
70
|
+
export interface AccountSessionIdentity {
|
|
71
|
+
id: string;
|
|
72
|
+
parentId?: string | null;
|
|
73
|
+
}
|
|
66
74
|
/**
|
|
67
75
|
* In-memory multi-account manager with sticky account selection.
|
|
68
76
|
*
|
|
@@ -85,22 +93,33 @@ export declare class AccountManager {
|
|
|
85
93
|
private sessionStartTime;
|
|
86
94
|
private sessionRequestCounts;
|
|
87
95
|
private sessionUsedAccounts;
|
|
96
|
+
private requestSessionStates;
|
|
88
97
|
static loadFromDisk(authFallback?: OAuthAuthDetails): Promise<AccountManager>;
|
|
89
98
|
constructor(authFallback?: OAuthAuthDetails, stored?: AccountStorageV4 | null);
|
|
90
99
|
getAccountCount(): number;
|
|
91
100
|
getTotalAccountCount(): number;
|
|
92
101
|
getEnabledAccounts(): ManagedAccount[];
|
|
102
|
+
private getEffectiveSoftQuotaThreshold;
|
|
93
103
|
getAccountsSnapshot(): ManagedAccount[];
|
|
94
|
-
|
|
95
|
-
|
|
104
|
+
private getRequestSessionState;
|
|
105
|
+
private pruneRequestSessionStates;
|
|
106
|
+
private getActiveIndex;
|
|
107
|
+
private setActiveIndex;
|
|
108
|
+
private getCursor;
|
|
109
|
+
private advanceCursor;
|
|
110
|
+
private getUsedAccounts;
|
|
111
|
+
private preferAccountOutsideParent;
|
|
112
|
+
deleteSessionState(sessionId: string): void;
|
|
113
|
+
getCurrentAccountForFamily(family: ModelFamily, identity?: AccountSessionIdentity): ManagedAccount | null;
|
|
114
|
+
markSwitched(account: ManagedAccount, reason: "rate-limit" | "initial" | "rotation", family: ModelFamily, identity?: AccountSessionIdentity): void;
|
|
96
115
|
/**
|
|
97
116
|
* Check if we should show an account switch toast.
|
|
98
117
|
* Debounces repeated toasts for the same account.
|
|
99
118
|
*/
|
|
100
119
|
shouldShowAccountToast(accountIndex: number, debounceMs?: number): boolean;
|
|
101
120
|
markToastShown(accountIndex: number): void;
|
|
102
|
-
getCurrentOrNextForFamily(family: ModelFamily, model?: string | null, strategy?: AccountSelectionStrategy, headerStyle?: HeaderStyle, pidOffsetEnabled?: boolean, softQuotaThresholdPercent?: number, softQuotaCacheTtlMs?: number): ManagedAccount | null;
|
|
103
|
-
getNextForFamily(family: ModelFamily, model?: string | null, headerStyle?: HeaderStyle, softQuotaThresholdPercent?: number, softQuotaCacheTtlMs?: number): ManagedAccount | null;
|
|
121
|
+
getCurrentOrNextForFamily(family: ModelFamily, model?: string | null, strategy?: AccountSelectionStrategy, headerStyle?: HeaderStyle, pidOffsetEnabled?: boolean, softQuotaThresholdPercent?: number, softQuotaCacheTtlMs?: number, identity?: AccountSessionIdentity): ManagedAccount | null;
|
|
122
|
+
getNextForFamily(family: ModelFamily, model?: string | null, headerStyle?: HeaderStyle, softQuotaThresholdPercent?: number, softQuotaCacheTtlMs?: number, identity?: AccountSessionIdentity): ManagedAccount | null;
|
|
104
123
|
markRateLimited(account: ManagedAccount, retryAfterMs: number, family: ModelFamily, headerStyle?: HeaderStyle, model?: string | null): void;
|
|
105
124
|
/**
|
|
106
125
|
* Mark an account as used after a successful API request.
|
|
@@ -108,10 +127,10 @@ export declare class AccountManager {
|
|
|
108
127
|
* Should be called AFTER request completion, not during account selection.
|
|
109
128
|
*/
|
|
110
129
|
markAccountUsed(accountIndex: number): void;
|
|
111
|
-
recordSessionUsage(accountIndex: number): void;
|
|
112
|
-
wasUsedInSession(accountIndex: number): boolean;
|
|
113
|
-
shouldProactivelyRotate(family: ModelFamily, model: string | null | undefined, thresholdPercent: number, cacheTtlMs: number): boolean;
|
|
114
|
-
proactivelyRotateForFamily(family: ModelFamily, model: string | null | undefined, headerStyle: HeaderStyle, softQuotaThresholdPercent: number, softQuotaCacheTtlMs: number): ManagedAccount | null;
|
|
130
|
+
recordSessionUsage(accountIndex: number, identity?: AccountSessionIdentity): void;
|
|
131
|
+
wasUsedInSession(accountIndex: number, identity?: AccountSessionIdentity): boolean;
|
|
132
|
+
shouldProactivelyRotate(family: ModelFamily, model: string | null | undefined, thresholdPercent: number, cacheTtlMs: number, identity?: AccountSessionIdentity): boolean;
|
|
133
|
+
proactivelyRotateForFamily(family: ModelFamily, model: string | null | undefined, headerStyle: HeaderStyle, softQuotaThresholdPercent: number, softQuotaCacheTtlMs: number, identity?: AccountSessionIdentity): ManagedAccount | null;
|
|
115
134
|
markRateLimitedWithReason(account: ManagedAccount, family: ModelFamily, headerStyle: HeaderStyle, model: string | null | undefined, reason: RateLimitReason, retryAfterMs?: number | null, failureTtlMs?: number): number;
|
|
116
135
|
markRequestSuccess(account: ManagedAccount): void;
|
|
117
136
|
clearAllRateLimitsForFamily(family: ModelFamily, model?: string | null): void;
|
|
@@ -140,7 +159,8 @@ export declare class AccountManager {
|
|
|
140
159
|
hasOtherAccountWithAntigravityAvailable(currentAccountIndex: number, family: ModelFamily, model?: string | null): boolean;
|
|
141
160
|
setAccountEnabled(accountIndex: number, enabled: boolean): boolean;
|
|
142
161
|
markAccountVerificationRequired(accountIndex: number, reason?: string, verifyUrl?: string): boolean;
|
|
143
|
-
|
|
162
|
+
markAccountIneligible(accountIndex: number, reason?: string): boolean;
|
|
163
|
+
clearAccountAccessBlocks(accountIndex: number, enableAccount?: boolean): boolean;
|
|
144
164
|
removeAccountByIndex(accountIndex: number): boolean;
|
|
145
165
|
removeAccount(account: ManagedAccount): boolean;
|
|
146
166
|
updateFromAuth(account: ManagedAccount, auth: OAuthAuthDetails): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../../src/plugin/accounts.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../../src/plugin/accounts.ts"],"names":[],"mappings":"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"}
|
|
@@ -193,6 +193,8 @@ export function computeSoftQuotaCacheTtlMs(ttlConfig, refreshIntervalMinutes) {
|
|
|
193
193
|
}
|
|
194
194
|
return ttlConfig * 60 * 1000;
|
|
195
195
|
}
|
|
196
|
+
const ACCOUNT_SESSION_STATE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
197
|
+
const MAX_ACCOUNT_SESSION_STATES = 256;
|
|
196
198
|
/**
|
|
197
199
|
* In-memory multi-account manager with sticky account selection.
|
|
198
200
|
*
|
|
@@ -221,6 +223,7 @@ export class AccountManager {
|
|
|
221
223
|
sessionStartTime = Date.now();
|
|
222
224
|
sessionRequestCounts = new Map();
|
|
223
225
|
sessionUsedAccounts = new Set();
|
|
226
|
+
requestSessionStates = new Map();
|
|
224
227
|
static async loadFromDisk(authFallback) {
|
|
225
228
|
const stored = await loadAccounts();
|
|
226
229
|
return new AccountManager(authFallback, stored);
|
|
@@ -266,9 +269,14 @@ export class AccountManager {
|
|
|
266
269
|
cachedQuota: acc.cachedQuota,
|
|
267
270
|
cachedQuotaUpdatedAt: acc.cachedQuotaUpdatedAt,
|
|
268
271
|
dailyRequestCounts: acc.dailyRequestCounts,
|
|
269
|
-
verificationRequired: acc.verificationRequired,
|
|
272
|
+
verificationRequired: acc.verificationRequired,
|
|
273
|
+
verificationRequiredAt: acc.verificationRequiredAt,
|
|
270
274
|
verificationRequiredReason: acc.verificationRequiredReason,
|
|
271
275
|
verificationUrl: acc.verificationUrl,
|
|
276
|
+
accountIneligible: acc.accountIneligible,
|
|
277
|
+
accountIneligibleAt: acc.accountIneligibleAt,
|
|
278
|
+
accountIneligibleReason: acc.accountIneligibleReason,
|
|
279
|
+
eligibilityStateUpdatedAt: acc.eligibilityStateUpdatedAt,
|
|
272
280
|
};
|
|
273
281
|
})
|
|
274
282
|
.filter((a) => a !== null);
|
|
@@ -350,11 +358,111 @@ export class AccountManager {
|
|
|
350
358
|
getEnabledAccounts() {
|
|
351
359
|
return this.accounts.filter((account) => account.enabled !== false);
|
|
352
360
|
}
|
|
361
|
+
getEffectiveSoftQuotaThreshold(thresholdPercent) {
|
|
362
|
+
// Soft-quota protection only has a purpose when another enabled account
|
|
363
|
+
// exists to rotate to. Never block the sole usable account.
|
|
364
|
+
return this.getEnabledAccounts().length > 1 ? thresholdPercent : 100;
|
|
365
|
+
}
|
|
353
366
|
getAccountsSnapshot() {
|
|
354
367
|
return this.accounts.map((a) => ({ ...a, parts: { ...a.parts }, rateLimitResetTimes: { ...a.rateLimitResetTimes } }));
|
|
355
368
|
}
|
|
356
|
-
|
|
357
|
-
const
|
|
369
|
+
getRequestSessionState(identity) {
|
|
370
|
+
const now = nowMs();
|
|
371
|
+
this.pruneRequestSessionStates(now, identity.id);
|
|
372
|
+
const existing = this.requestSessionStates.get(identity.id);
|
|
373
|
+
if (existing) {
|
|
374
|
+
existing.lastAccessedAt = now;
|
|
375
|
+
if (identity.parentId) {
|
|
376
|
+
existing.parentId = identity.parentId;
|
|
377
|
+
}
|
|
378
|
+
return existing;
|
|
379
|
+
}
|
|
380
|
+
const state = {
|
|
381
|
+
parentId: identity.parentId ?? null,
|
|
382
|
+
currentAccountIndexByFamily: { claude: -1, gemini: -1 },
|
|
383
|
+
cursorByFamily: { ...this.cursorByFamily },
|
|
384
|
+
offsetAppliedByFamily: { claude: false, gemini: false },
|
|
385
|
+
usedAccounts: new Set(),
|
|
386
|
+
lastAccessedAt: now,
|
|
387
|
+
};
|
|
388
|
+
this.requestSessionStates.set(identity.id, state);
|
|
389
|
+
return state;
|
|
390
|
+
}
|
|
391
|
+
pruneRequestSessionStates(now, preservedId) {
|
|
392
|
+
const expiry = now - ACCOUNT_SESSION_STATE_TTL_MS;
|
|
393
|
+
for (const [id, state] of this.requestSessionStates) {
|
|
394
|
+
if (id !== preservedId && state.lastAccessedAt < expiry) {
|
|
395
|
+
this.requestSessionStates.delete(id);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (this.requestSessionStates.size < MAX_ACCOUNT_SESSION_STATES || this.requestSessionStates.has(preservedId)) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
let oldestId = null;
|
|
402
|
+
let oldestAccess = Number.POSITIVE_INFINITY;
|
|
403
|
+
for (const [id, state] of this.requestSessionStates) {
|
|
404
|
+
if (id !== preservedId && state.lastAccessedAt < oldestAccess) {
|
|
405
|
+
oldestId = id;
|
|
406
|
+
oldestAccess = state.lastAccessedAt;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (oldestId) {
|
|
410
|
+
this.requestSessionStates.delete(oldestId);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
getActiveIndex(family, identity) {
|
|
414
|
+
return identity
|
|
415
|
+
? this.getRequestSessionState(identity).currentAccountIndexByFamily[family]
|
|
416
|
+
: this.currentAccountIndexByFamily[family];
|
|
417
|
+
}
|
|
418
|
+
setActiveIndex(family, index, identity) {
|
|
419
|
+
if (!identity) {
|
|
420
|
+
this.currentAccountIndexByFamily[family] = index;
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const state = this.getRequestSessionState(identity);
|
|
424
|
+
state.currentAccountIndexByFamily[family] = index;
|
|
425
|
+
if (!state.parentId) {
|
|
426
|
+
// Preserve a useful persisted starting point without coupling active root sessions.
|
|
427
|
+
this.currentAccountIndexByFamily[family] = index;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
getCursor(family, identity) {
|
|
431
|
+
return identity
|
|
432
|
+
? this.getRequestSessionState(identity).cursorByFamily[family]
|
|
433
|
+
: this.cursorByFamily[family];
|
|
434
|
+
}
|
|
435
|
+
advanceCursor(family, identity) {
|
|
436
|
+
const nextGlobalCursor = this.cursorByFamily[family] + 1;
|
|
437
|
+
this.cursorByFamily[family] = nextGlobalCursor;
|
|
438
|
+
if (identity) {
|
|
439
|
+
this.getRequestSessionState(identity).cursorByFamily[family] += 1;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
getUsedAccounts(identity) {
|
|
443
|
+
return identity ? this.getRequestSessionState(identity).usedAccounts : this.sessionUsedAccounts;
|
|
444
|
+
}
|
|
445
|
+
preferAccountOutsideParent(accounts, family, identity) {
|
|
446
|
+
if (!identity) {
|
|
447
|
+
return accounts;
|
|
448
|
+
}
|
|
449
|
+
const parentId = this.getRequestSessionState(identity).parentId;
|
|
450
|
+
if (!parentId) {
|
|
451
|
+
return accounts;
|
|
452
|
+
}
|
|
453
|
+
const parentState = this.requestSessionStates.get(parentId);
|
|
454
|
+
const parentIndex = parentState?.currentAccountIndexByFamily[family] ?? -1;
|
|
455
|
+
if (parentIndex < 0) {
|
|
456
|
+
return accounts;
|
|
457
|
+
}
|
|
458
|
+
const isolated = accounts.filter((account) => account.index !== parentIndex);
|
|
459
|
+
return isolated.length > 0 ? isolated : accounts;
|
|
460
|
+
}
|
|
461
|
+
deleteSessionState(sessionId) {
|
|
462
|
+
this.requestSessionStates.delete(sessionId);
|
|
463
|
+
}
|
|
464
|
+
getCurrentAccountForFamily(family, identity) {
|
|
465
|
+
const currentIndex = this.getActiveIndex(family, identity);
|
|
358
466
|
if (currentIndex >= 0 && currentIndex < this.accounts.length) {
|
|
359
467
|
const account = this.accounts[currentIndex] ?? null;
|
|
360
468
|
// Only return account if it's enabled - disabled accounts should not be selected
|
|
@@ -364,9 +472,9 @@ export class AccountManager {
|
|
|
364
472
|
}
|
|
365
473
|
return null;
|
|
366
474
|
}
|
|
367
|
-
markSwitched(account, reason, family) {
|
|
475
|
+
markSwitched(account, reason, family, identity) {
|
|
368
476
|
account.lastSwitchReason = reason;
|
|
369
|
-
this.
|
|
477
|
+
this.setActiveIndex(family, account.index, identity);
|
|
370
478
|
}
|
|
371
479
|
/**
|
|
372
480
|
* Check if we should show an account switch toast.
|
|
@@ -383,41 +491,56 @@ export class AccountManager {
|
|
|
383
491
|
this.lastToastAccountIndex = accountIndex;
|
|
384
492
|
this.lastToastTime = nowMs();
|
|
385
493
|
}
|
|
386
|
-
getCurrentOrNextForFamily(family, model, strategy = 'sticky', headerStyle = 'antigravity', pidOffsetEnabled = false, softQuotaThresholdPercent = 100, softQuotaCacheTtlMs = 10 * 60 * 1000) {
|
|
494
|
+
getCurrentOrNextForFamily(family, model, strategy = 'sticky', headerStyle = 'antigravity', pidOffsetEnabled = false, softQuotaThresholdPercent = 100, softQuotaCacheTtlMs = 10 * 60 * 1000, identity) {
|
|
387
495
|
const quotaKey = getQuotaKey(family, headerStyle, model);
|
|
496
|
+
const effectiveSoftQuotaThreshold = this.getEffectiveSoftQuotaThreshold(softQuotaThresholdPercent);
|
|
497
|
+
// OpenCode may run many root and child sessions concurrently in one plugin
|
|
498
|
+
// process. Pin each exact session until its account becomes unavailable.
|
|
499
|
+
if (identity) {
|
|
500
|
+
const pinned = this.getCurrentAccountForFamily(family, identity);
|
|
501
|
+
if (pinned) {
|
|
502
|
+
clearExpiredRateLimits(pinned);
|
|
503
|
+
const unavailable = isRateLimitedForHeaderStyle(pinned, family, headerStyle, model) ||
|
|
504
|
+
isOverSoftQuotaThreshold(pinned, family, effectiveSoftQuotaThreshold, softQuotaCacheTtlMs, model) ||
|
|
505
|
+
this.isAccountCoolingDown(pinned);
|
|
506
|
+
if (!unavailable) {
|
|
507
|
+
this.markTouchedForQuota(pinned, quotaKey);
|
|
508
|
+
return pinned;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
388
512
|
if (strategy === 'round-robin') {
|
|
389
|
-
const next = this.getNextForFamily(family, model, headerStyle,
|
|
513
|
+
const next = this.getNextForFamily(family, model, headerStyle, effectiveSoftQuotaThreshold, softQuotaCacheTtlMs, identity);
|
|
390
514
|
if (next) {
|
|
391
515
|
this.markTouchedForQuota(next, quotaKey);
|
|
392
|
-
this.
|
|
516
|
+
this.setActiveIndex(family, next.index, identity);
|
|
393
517
|
}
|
|
394
518
|
return next;
|
|
395
519
|
}
|
|
396
520
|
if (strategy === 'hybrid') {
|
|
397
521
|
const healthTracker = getHealthTracker();
|
|
398
522
|
const tokenTracker = getTokenTracker();
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
.map(acc => {
|
|
523
|
+
const eligibleAccounts = this.preferAccountOutsideParent(this.accounts.filter(acc => acc.enabled !== false), family, identity);
|
|
524
|
+
const accountsWithMetrics = eligibleAccounts.map(acc => {
|
|
402
525
|
clearExpiredRateLimits(acc);
|
|
403
526
|
return {
|
|
404
527
|
index: acc.index,
|
|
405
528
|
lastUsed: acc.lastUsed,
|
|
406
529
|
healthScore: healthTracker.getScore(acc.index),
|
|
407
530
|
isRateLimited: isRateLimitedForFamily(acc, family, model) ||
|
|
408
|
-
isOverSoftQuotaThreshold(acc, family,
|
|
531
|
+
isOverSoftQuotaThreshold(acc, family, effectiveSoftQuotaThreshold, softQuotaCacheTtlMs, model),
|
|
409
532
|
isCoolingDown: this.isAccountCoolingDown(acc),
|
|
410
533
|
};
|
|
411
534
|
});
|
|
412
535
|
// Get current account index for stickiness
|
|
413
|
-
const currentIndex = this.
|
|
536
|
+
const currentIndex = this.getActiveIndex(family, identity);
|
|
414
537
|
const selectedIndex = selectHybridAccount(accountsWithMetrics, tokenTracker, currentIndex);
|
|
415
538
|
if (selectedIndex !== null) {
|
|
416
539
|
const selected = this.accounts[selectedIndex];
|
|
417
540
|
if (selected) {
|
|
418
541
|
selected.lastUsed = nowMs();
|
|
419
542
|
this.markTouchedForQuota(selected, quotaKey);
|
|
420
|
-
this.
|
|
543
|
+
this.setActiveIndex(family, selected.index, identity);
|
|
421
544
|
return selected;
|
|
422
545
|
}
|
|
423
546
|
}
|
|
@@ -425,50 +548,57 @@ export class AccountManager {
|
|
|
425
548
|
// Fallback: sticky selection (used when hybrid finds no candidates)
|
|
426
549
|
// PID-based offset for multi-session distribution (opt-in)
|
|
427
550
|
// Different sessions (PIDs) will prefer different starting accounts
|
|
428
|
-
|
|
551
|
+
const offsetApplied = identity
|
|
552
|
+
? this.getRequestSessionState(identity).offsetAppliedByFamily
|
|
553
|
+
: this.sessionOffsetApplied;
|
|
554
|
+
if (pidOffsetEnabled && !offsetApplied[family] && this.accounts.length > 1) {
|
|
429
555
|
const pidOffset = process.pid % this.accounts.length;
|
|
430
|
-
const
|
|
556
|
+
const activeIndex = this.getActiveIndex(family, identity);
|
|
557
|
+
const baseIndex = activeIndex >= 0 ? activeIndex : this.getCursor(family, identity);
|
|
431
558
|
const newIndex = (baseIndex + pidOffset) % this.accounts.length;
|
|
432
559
|
debugLogToFile(`[Account] Applying PID offset: pid=${process.pid} offset=${pidOffset} family=${family} index=${baseIndex}->${newIndex}`);
|
|
433
|
-
this.
|
|
434
|
-
|
|
560
|
+
this.setActiveIndex(family, newIndex, identity);
|
|
561
|
+
offsetApplied[family] = true;
|
|
435
562
|
}
|
|
436
|
-
const current = this.getCurrentAccountForFamily(family);
|
|
563
|
+
const current = this.getCurrentAccountForFamily(family, identity);
|
|
437
564
|
if (current) {
|
|
438
565
|
clearExpiredRateLimits(current);
|
|
439
566
|
const isLimitedForRequestedStyle = isRateLimitedForHeaderStyle(current, family, headerStyle, model);
|
|
440
|
-
const isOverThreshold = isOverSoftQuotaThreshold(current, family,
|
|
567
|
+
const isOverThreshold = isOverSoftQuotaThreshold(current, family, effectiveSoftQuotaThreshold, softQuotaCacheTtlMs, model);
|
|
441
568
|
if (!isLimitedForRequestedStyle && !isOverThreshold && !this.isAccountCoolingDown(current)) {
|
|
442
569
|
this.markTouchedForQuota(current, quotaKey);
|
|
443
570
|
return current;
|
|
444
571
|
}
|
|
445
572
|
}
|
|
446
|
-
const next = this.getNextForFamily(family, model, headerStyle,
|
|
573
|
+
const next = this.getNextForFamily(family, model, headerStyle, effectiveSoftQuotaThreshold, softQuotaCacheTtlMs, identity);
|
|
447
574
|
if (next) {
|
|
448
575
|
this.markTouchedForQuota(next, quotaKey);
|
|
449
|
-
this.
|
|
576
|
+
this.setActiveIndex(family, next.index, identity);
|
|
450
577
|
}
|
|
451
578
|
return next;
|
|
452
579
|
}
|
|
453
|
-
getNextForFamily(family, model, headerStyle = "antigravity", softQuotaThresholdPercent = 100, softQuotaCacheTtlMs = 10 * 60 * 1000) {
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
!
|
|
459
|
-
!
|
|
580
|
+
getNextForFamily(family, model, headerStyle = "antigravity", softQuotaThresholdPercent = 100, softQuotaCacheTtlMs = 10 * 60 * 1000, identity) {
|
|
581
|
+
const effectiveSoftQuotaThreshold = this.getEffectiveSoftQuotaThreshold(softQuotaThresholdPercent);
|
|
582
|
+
const allAvailable = this.accounts.filter((account) => {
|
|
583
|
+
clearExpiredRateLimits(account);
|
|
584
|
+
return account.enabled !== false &&
|
|
585
|
+
!isRateLimitedForHeaderStyle(account, family, headerStyle, model) &&
|
|
586
|
+
!isOverSoftQuotaThreshold(account, family, effectiveSoftQuotaThreshold, softQuotaCacheTtlMs, model) &&
|
|
587
|
+
!this.isAccountCoolingDown(account);
|
|
460
588
|
});
|
|
589
|
+
const available = this.preferAccountOutsideParent(allAvailable, family, identity);
|
|
461
590
|
if (available.length === 0) {
|
|
462
591
|
return null;
|
|
463
592
|
}
|
|
464
|
-
const
|
|
593
|
+
const usedAccounts = this.getUsedAccounts(identity);
|
|
594
|
+
const sessionUsed = available.filter(account => usedAccounts.has(account.index));
|
|
465
595
|
const candidates = sessionUsed.length > 0 ? sessionUsed : available;
|
|
466
|
-
const cursor = this.
|
|
596
|
+
const cursor = this.getCursor(family, identity);
|
|
467
597
|
const account = candidates[cursor % candidates.length];
|
|
468
598
|
if (!account) {
|
|
469
599
|
return null;
|
|
470
600
|
}
|
|
471
|
-
this.
|
|
601
|
+
this.advanceCursor(family, identity);
|
|
472
602
|
return account;
|
|
473
603
|
}
|
|
474
604
|
markRateLimited(account, retryAfterMs, family, headerStyle = "antigravity", model) {
|
|
@@ -486,16 +616,16 @@ export class AccountManager {
|
|
|
486
616
|
account.lastUsed = nowMs();
|
|
487
617
|
}
|
|
488
618
|
}
|
|
489
|
-
recordSessionUsage(accountIndex) {
|
|
490
|
-
this.
|
|
619
|
+
recordSessionUsage(accountIndex, identity) {
|
|
620
|
+
this.getUsedAccounts(identity).add(accountIndex);
|
|
491
621
|
}
|
|
492
|
-
wasUsedInSession(accountIndex) {
|
|
493
|
-
return this.
|
|
622
|
+
wasUsedInSession(accountIndex, identity) {
|
|
623
|
+
return this.getUsedAccounts(identity).has(accountIndex);
|
|
494
624
|
}
|
|
495
|
-
shouldProactivelyRotate(family, model, thresholdPercent, cacheTtlMs) {
|
|
625
|
+
shouldProactivelyRotate(family, model, thresholdPercent, cacheTtlMs, identity) {
|
|
496
626
|
if (thresholdPercent <= 0)
|
|
497
627
|
return false;
|
|
498
|
-
const current = this.getCurrentAccountForFamily(family);
|
|
628
|
+
const current = this.getCurrentAccountForFamily(family, identity);
|
|
499
629
|
if (!current || !current.cachedQuota || current.cachedQuotaUpdatedAt == null)
|
|
500
630
|
return false;
|
|
501
631
|
const age = nowMs() - current.cachedQuotaUpdatedAt;
|
|
@@ -508,9 +638,9 @@ export class AccountManager {
|
|
|
508
638
|
const remainingPercent = Math.max(0, Math.min(100, groupData.remainingFraction * 100));
|
|
509
639
|
return remainingPercent < thresholdPercent;
|
|
510
640
|
}
|
|
511
|
-
proactivelyRotateForFamily(family, model, headerStyle, softQuotaThresholdPercent, softQuotaCacheTtlMs) {
|
|
512
|
-
const currentIndex = this.
|
|
513
|
-
const candidates = this.accounts.filter(acc => {
|
|
641
|
+
proactivelyRotateForFamily(family, model, headerStyle, softQuotaThresholdPercent, softQuotaCacheTtlMs, identity) {
|
|
642
|
+
const currentIndex = this.getActiveIndex(family, identity);
|
|
643
|
+
const candidates = this.preferAccountOutsideParent(this.accounts.filter(acc => {
|
|
514
644
|
if (acc.enabled === false)
|
|
515
645
|
return false;
|
|
516
646
|
if (acc.index === currentIndex)
|
|
@@ -523,10 +653,11 @@ export class AccountManager {
|
|
|
523
653
|
if (this.isAccountCoolingDown(acc))
|
|
524
654
|
return false;
|
|
525
655
|
return true;
|
|
526
|
-
});
|
|
656
|
+
}), family, identity);
|
|
527
657
|
if (candidates.length === 0)
|
|
528
658
|
return null;
|
|
529
|
-
const
|
|
659
|
+
const usedAccounts = this.getUsedAccounts(identity);
|
|
660
|
+
const warmCandidates = candidates.filter(account => usedAccounts.has(account.index));
|
|
530
661
|
const pool = warmCandidates.length > 0 ? warmCandidates : candidates;
|
|
531
662
|
const quotaGroup = resolveQuotaGroup(family, model);
|
|
532
663
|
pool.sort((a, b) => {
|
|
@@ -539,7 +670,7 @@ export class AccountManager {
|
|
|
539
670
|
return null;
|
|
540
671
|
const quotaKey = getQuotaKey(family, headerStyle, model);
|
|
541
672
|
this.markTouchedForQuota(selected, quotaKey);
|
|
542
|
-
this.
|
|
673
|
+
this.setActiveIndex(family, selected.index, identity);
|
|
543
674
|
return selected;
|
|
544
675
|
}
|
|
545
676
|
markRateLimitedWithReason(account, family, headerStyle, model, reason, retryAfterMs, failureTtlMs = 3600_000) {
|
|
@@ -679,6 +810,9 @@ export class AccountManager {
|
|
|
679
810
|
if (!account) {
|
|
680
811
|
return false;
|
|
681
812
|
}
|
|
813
|
+
if (enabled && account.accountIneligible) {
|
|
814
|
+
return false;
|
|
815
|
+
}
|
|
682
816
|
account.enabled = enabled;
|
|
683
817
|
if (!enabled) {
|
|
684
818
|
for (const family of Object.keys(this.currentAccountIndexByFamily)) {
|
|
@@ -696,9 +830,18 @@ export class AccountManager {
|
|
|
696
830
|
if (!account) {
|
|
697
831
|
return false;
|
|
698
832
|
}
|
|
833
|
+
const timestamp = nowMs();
|
|
699
834
|
account.verificationRequired = true;
|
|
700
|
-
account.verificationRequiredAt =
|
|
835
|
+
account.verificationRequiredAt = timestamp;
|
|
701
836
|
account.verificationRequiredReason = reason?.trim() || undefined;
|
|
837
|
+
if (account.accountIneligible === true ||
|
|
838
|
+
account.accountIneligibleAt !== undefined ||
|
|
839
|
+
account.accountIneligibleReason !== undefined) {
|
|
840
|
+
account.accountIneligible = false;
|
|
841
|
+
account.accountIneligibleAt = undefined;
|
|
842
|
+
account.accountIneligibleReason = undefined;
|
|
843
|
+
account.eligibilityStateUpdatedAt = timestamp;
|
|
844
|
+
}
|
|
702
845
|
const normalizedVerifyUrl = verifyUrl?.trim();
|
|
703
846
|
if (normalizedVerifyUrl) {
|
|
704
847
|
account.verificationUrl = normalizedVerifyUrl;
|
|
@@ -711,23 +854,56 @@ export class AccountManager {
|
|
|
711
854
|
}
|
|
712
855
|
return true;
|
|
713
856
|
}
|
|
714
|
-
|
|
857
|
+
markAccountIneligible(accountIndex, reason) {
|
|
858
|
+
const account = this.accounts[accountIndex];
|
|
859
|
+
if (!account) {
|
|
860
|
+
return false;
|
|
861
|
+
}
|
|
862
|
+
const timestamp = nowMs();
|
|
863
|
+
account.accountIneligible = true;
|
|
864
|
+
account.accountIneligibleAt = timestamp;
|
|
865
|
+
account.accountIneligibleReason = reason?.trim() || "Google marked this account as ineligible.";
|
|
866
|
+
account.eligibilityStateUpdatedAt = timestamp;
|
|
867
|
+
account.verificationRequired = false;
|
|
868
|
+
account.verificationRequiredAt = undefined;
|
|
869
|
+
account.verificationRequiredReason = undefined;
|
|
870
|
+
account.verificationUrl = undefined;
|
|
871
|
+
if (account.enabled !== false) {
|
|
872
|
+
this.setAccountEnabled(accountIndex, false);
|
|
873
|
+
}
|
|
874
|
+
else {
|
|
875
|
+
this.requestSaveToDisk();
|
|
876
|
+
}
|
|
877
|
+
return true;
|
|
878
|
+
}
|
|
879
|
+
clearAccountAccessBlocks(accountIndex, enableAccount = false) {
|
|
715
880
|
const account = this.accounts[accountIndex];
|
|
716
881
|
if (!account) {
|
|
717
882
|
return false;
|
|
718
883
|
}
|
|
719
884
|
const wasVerificationRequired = account.verificationRequired === true;
|
|
720
|
-
const
|
|
885
|
+
const wasIneligible = account.accountIneligible === true;
|
|
886
|
+
const hadMetadata = wasVerificationRequired || wasIneligible ||
|
|
887
|
+
account.verificationRequiredAt !== undefined ||
|
|
721
888
|
account.verificationRequiredReason !== undefined ||
|
|
722
|
-
account.verificationUrl !== undefined
|
|
889
|
+
account.verificationUrl !== undefined ||
|
|
890
|
+
account.accountIneligibleAt !== undefined ||
|
|
891
|
+
account.accountIneligibleReason !== undefined ||
|
|
892
|
+
account.eligibilityStateUpdatedAt !== undefined;
|
|
723
893
|
account.verificationRequired = false;
|
|
724
894
|
account.verificationRequiredAt = undefined;
|
|
725
895
|
account.verificationRequiredReason = undefined;
|
|
726
896
|
account.verificationUrl = undefined;
|
|
727
|
-
|
|
897
|
+
account.accountIneligible = false;
|
|
898
|
+
account.accountIneligibleAt = undefined;
|
|
899
|
+
account.accountIneligibleReason = undefined;
|
|
900
|
+
if (wasIneligible || account.eligibilityStateUpdatedAt !== undefined) {
|
|
901
|
+
account.eligibilityStateUpdatedAt = nowMs();
|
|
902
|
+
}
|
|
903
|
+
if (enableAccount && (wasVerificationRequired || wasIneligible) && account.enabled === false) {
|
|
728
904
|
this.setAccountEnabled(accountIndex, true);
|
|
729
905
|
}
|
|
730
|
-
else if (
|
|
906
|
+
else if (hadMetadata) {
|
|
731
907
|
this.requestSaveToDisk();
|
|
732
908
|
}
|
|
733
909
|
return true;
|
|
@@ -755,6 +931,7 @@ export class AccountManager {
|
|
|
755
931
|
this.cursorByFamily = { claude: 0, gemini: 0 };
|
|
756
932
|
this.currentAccountIndexByFamily.claude = -1;
|
|
757
933
|
this.currentAccountIndexByFamily.gemini = -1;
|
|
934
|
+
this.requestSessionStates.clear();
|
|
758
935
|
return true;
|
|
759
936
|
}
|
|
760
937
|
for (const family of ["claude", "gemini"]) {
|
|
@@ -768,6 +945,24 @@ export class AccountManager {
|
|
|
768
945
|
if (this.currentAccountIndexByFamily[family] >= this.accounts.length) {
|
|
769
946
|
this.currentAccountIndexByFamily[family] = -1;
|
|
770
947
|
}
|
|
948
|
+
for (const state of this.requestSessionStates.values()) {
|
|
949
|
+
const currentIndex = state.currentAccountIndexByFamily[family];
|
|
950
|
+
if (currentIndex === idx) {
|
|
951
|
+
state.currentAccountIndexByFamily[family] = -1;
|
|
952
|
+
}
|
|
953
|
+
else if (currentIndex > idx) {
|
|
954
|
+
state.currentAccountIndexByFamily[family] -= 1;
|
|
955
|
+
}
|
|
956
|
+
if (state.cursorByFamily[family] > idx) {
|
|
957
|
+
state.cursorByFamily[family] -= 1;
|
|
958
|
+
}
|
|
959
|
+
state.cursorByFamily[family] %= this.accounts.length;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
for (const state of this.requestSessionStates.values()) {
|
|
963
|
+
state.usedAccounts = new Set([...state.usedAccounts]
|
|
964
|
+
.filter((accountIndex) => accountIndex !== idx)
|
|
965
|
+
.map((accountIndex) => accountIndex > idx ? accountIndex - 1 : accountIndex));
|
|
771
966
|
}
|
|
772
967
|
return true;
|
|
773
968
|
}
|
|
@@ -852,6 +1047,10 @@ export class AccountManager {
|
|
|
852
1047
|
verificationRequiredAt: a.verificationRequiredAt,
|
|
853
1048
|
verificationRequiredReason: a.verificationRequiredReason,
|
|
854
1049
|
verificationUrl: a.verificationUrl,
|
|
1050
|
+
accountIneligible: a.accountIneligible,
|
|
1051
|
+
accountIneligibleAt: a.accountIneligibleAt,
|
|
1052
|
+
accountIneligibleReason: a.accountIneligibleReason,
|
|
1053
|
+
eligibilityStateUpdatedAt: a.eligibilityStateUpdatedAt,
|
|
855
1054
|
})),
|
|
856
1055
|
activeIndex: claudeIndex,
|
|
857
1056
|
activeIndexByFamily: {
|
|
@@ -1097,7 +1296,7 @@ export class AccountManager {
|
|
|
1097
1296
|
};
|
|
1098
1297
|
}
|
|
1099
1298
|
isAccountOverSoftQuota(account, family, thresholdPercent, cacheTtlMs, model) {
|
|
1100
|
-
return isOverSoftQuotaThreshold(account, family, thresholdPercent, cacheTtlMs, model);
|
|
1299
|
+
return isOverSoftQuotaThreshold(account, family, this.getEffectiveSoftQuotaThreshold(thresholdPercent), cacheTtlMs, model);
|
|
1101
1300
|
}
|
|
1102
1301
|
getAccountsForQuotaCheck() {
|
|
1103
1302
|
return this.accounts.map((a) => ({
|
|
@@ -1127,7 +1326,7 @@ export class AccountManager {
|
|
|
1127
1326
|
if (thresholdPercent >= 100)
|
|
1128
1327
|
return false;
|
|
1129
1328
|
const enabled = this.accounts.filter(a => a.enabled !== false);
|
|
1130
|
-
if (enabled.length
|
|
1329
|
+
if (enabled.length <= 1)
|
|
1131
1330
|
return false;
|
|
1132
1331
|
return enabled.every(a => isOverSoftQuotaThreshold(a, family, thresholdPercent, cacheTtlMs, model));
|
|
1133
1332
|
}
|
|
@@ -1143,6 +1342,8 @@ export class AccountManager {
|
|
|
1143
1342
|
const enabled = this.accounts.filter(a => a.enabled !== false);
|
|
1144
1343
|
if (enabled.length === 0)
|
|
1145
1344
|
return null;
|
|
1345
|
+
if (enabled.length === 1)
|
|
1346
|
+
return 0;
|
|
1146
1347
|
// If any account is available (not over threshold), no wait needed
|
|
1147
1348
|
const available = enabled.filter(a => !isOverSoftQuotaThreshold(a, family, thresholdPercent, cacheTtlMs, model));
|
|
1148
1349
|
if (available.length > 0)
|