maxpool 1.5.23 → 1.5.24
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/package.json +1 -1
- package/src/account-manager.js +13 -0
- package/src/prober.js +3 -1
- package/src/tui.js +9 -0
package/package.json
CHANGED
package/src/account-manager.js
CHANGED
|
@@ -2276,6 +2276,11 @@ export class AccountManager {
|
|
|
2276
2276
|
// for the bounded drain; the lease holder owns all rotation.
|
|
2277
2277
|
if (!this.writerLease) return true;
|
|
2278
2278
|
|
|
2279
|
+
// A dead refresh token (invalid_grant) is PERMANENT until browser re-auth —
|
|
2280
|
+
// never auto-retry it. Without this the prober re-POSTs the rejected token every
|
|
2281
|
+
// ~60s forever (hammering Anthropic's OAuth endpoint). Cleared on re-login.
|
|
2282
|
+
if (account.refreshDead) return false;
|
|
2283
|
+
|
|
2279
2284
|
if (!force && !isTokenExpiringSoon(account.expiresAt)) return true;
|
|
2280
2285
|
|
|
2281
2286
|
// Coalesce concurrent refreshes
|
|
@@ -2309,6 +2314,11 @@ export class AccountManager {
|
|
|
2309
2314
|
this.markTransientFailure(accountIndex, `token_refresh_${err.status || 'network'}`, { network: !err.status });
|
|
2310
2315
|
} else {
|
|
2311
2316
|
this.markAuthFailed(accountIndex, err.status || 401, 'token_refresh_failed');
|
|
2317
|
+
// The refresh TOKEN itself was rejected (invalid_grant) — permanent until
|
|
2318
|
+
// browser re-auth. Latch so ensureTokenFresh + the prober stop retrying a
|
|
2319
|
+
// dead token. Set ONLY here (a rejected refresh), never in markAuthFailed
|
|
2320
|
+
// (shared with provider auth failures).
|
|
2321
|
+
account.refreshDead = true;
|
|
2312
2322
|
}
|
|
2313
2323
|
return false;
|
|
2314
2324
|
}
|
|
@@ -2350,6 +2360,7 @@ export class AccountManager {
|
|
|
2350
2360
|
account.credential = accessToken;
|
|
2351
2361
|
if (refreshToken) account.refreshToken = refreshToken;
|
|
2352
2362
|
account.expiresAt = expiresAt;
|
|
2363
|
+
account.refreshDead = false; // fresh tokens from re-auth revive a dead-refresh account
|
|
2353
2364
|
if (account.status === 'error') account.status = 'active';
|
|
2354
2365
|
console.log(`[Maxpool] Updated tokens for account "${account.name}"`);
|
|
2355
2366
|
this._onTokenRefresh?.(accountIndex, {
|
|
@@ -2434,6 +2445,7 @@ export class AccountManager {
|
|
|
2434
2445
|
account.lastError = null;
|
|
2435
2446
|
account.lastErrorAt = null;
|
|
2436
2447
|
account.consecutiveFailures = 0;
|
|
2448
|
+
account.refreshDead = false;
|
|
2437
2449
|
}
|
|
2438
2450
|
return idx;
|
|
2439
2451
|
}
|
|
@@ -2575,6 +2587,7 @@ export class AccountManager {
|
|
|
2575
2587
|
priority: a.priority,
|
|
2576
2588
|
runtime: a.runtime,
|
|
2577
2589
|
status: a.status,
|
|
2590
|
+
refreshDead: Boolean(a.refreshDead),
|
|
2578
2591
|
inFlight: a.inFlight,
|
|
2579
2592
|
activeWeight: a.activeWeight,
|
|
2580
2593
|
completedRequests: a.completedRequests,
|
package/src/prober.js
CHANGED
|
@@ -59,7 +59,9 @@ export class Prober {
|
|
|
59
59
|
this._running = true;
|
|
60
60
|
this._inflight = (async () => {
|
|
61
61
|
try {
|
|
62
|
-
|
|
62
|
+
// Skip auth-dead accounts (dead refresh token): probing them just re-POSTs
|
|
63
|
+
// the rejected token every cycle — a 400 storm. They recover only on re-auth.
|
|
64
|
+
const oauth = this.am.accounts.filter(a => a.type === 'oauth' && a.credential && !a.refreshDead);
|
|
63
65
|
const providers = this.am.accounts.filter(a => a.type === 'provider' && a.credential);
|
|
64
66
|
await Promise.all([
|
|
65
67
|
...oauth.map(a => this.probeOne(a)),
|
package/src/tui.js
CHANGED
|
@@ -664,6 +664,10 @@ export class TUI {
|
|
|
664
664
|
amAcct.expiresAt = creds.expiresAt;
|
|
665
665
|
amAcct.accountUuid = entry.accountUuid;
|
|
666
666
|
amAcct.name = name;
|
|
667
|
+
// Fresh tokens from re-auth revive a dead-refresh account. This in-place
|
|
668
|
+
// update is the actual re-login path (the index.js reconcile sees no change
|
|
669
|
+
// and never calls updateAccountTokens), so refreshDead MUST be cleared here.
|
|
670
|
+
amAcct.refreshDead = false;
|
|
667
671
|
if (amAcct.status === 'error') amAcct.status = 'active';
|
|
668
672
|
}
|
|
669
673
|
this._addLog(`Updated account "${name}"`);
|
|
@@ -975,8 +979,13 @@ export class TUI {
|
|
|
975
979
|
if (a.enabled !== false && a.status === 'active' && this.am._isAccountWideRejected?.(a)) {
|
|
976
980
|
effectiveStatus = 'blocked';
|
|
977
981
|
}
|
|
982
|
+
// A dead refresh token surfaces as "reauth" (re-login needed) rather than a
|
|
983
|
+
// generic "error", so the user knows the fix. Display-only — account.status
|
|
984
|
+
// stays 'error' so routing/eligibility still exclude it.
|
|
985
|
+
if (a.enabled !== false && a.refreshDead) effectiveStatus = 'reauth';
|
|
978
986
|
switch (effectiveStatus) {
|
|
979
987
|
case 'active': status = isCur ? green('active') : 'active'; break;
|
|
988
|
+
case 'reauth': status = yellow('reauth'); break;
|
|
980
989
|
case 'blocked': status = red('blocked'); break;
|
|
981
990
|
case 'probing': status = green('probing'); break;
|
|
982
991
|
case 'waiting': status = yellow('waiting'); break;
|