maxpool 1.5.75 → 1.5.76

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maxpool",
3
- "version": "1.5.75",
3
+ "version": "1.5.76",
4
4
  "description": "Multi-account Claude Code proxy with adaptive, rate-aware load balancing across Claude accounts",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -2370,6 +2370,18 @@ export class AccountManager {
2370
2370
  // worth having; the diagnosis that motivated it was wrong.)
2371
2371
  q.consecutiveProbeFailures = (q.consecutiveProbeFailures || 0) + 1;
2372
2372
  const n = q.consecutiveProbeFailures;
2373
+ // A SUSTAINED 401 is dead credentials, not a blip. Latch refreshDead so (a) the
2374
+ // prober stops re-POSTing a rejected token every 60s forever — measured 2026-08-10:
2375
+ // 8 disabled accounts each past 20 consecutive 401s, hammering Anthropic's OAuth
2376
+ // endpoint for nothing — and (b) the TUI can SHOW that the account needs re-login
2377
+ // instead of leaving the user to guess. Cleared on a successful re-auth
2378
+ // (updateAccountTokens) exactly like a refresh-path invalid_grant.
2379
+ // Three strikes, not one: a single 401 can be a transient edge/token-rotation race.
2380
+ if (status === 401 && n >= 3 && !account.refreshDead) {
2381
+ account.refreshDead = true;
2382
+ console.error(`[Maxpool] "${account.name}" credentials rejected ${n}x (HTTP 401) — marking it needs re-login. `
2383
+ + 'Re-authenticate via the TUI (a → l). Probing stops until then.');
2384
+ }
2373
2385
  // Once, at a threshold that cannot be a blip, then every 100th so it stays visible
2374
2386
  // without walling the log.
2375
2387
  if (n === PROBE_FAILURE_ALERT_AT || (n > PROBE_FAILURE_ALERT_AT && n % 100 === 0)) {
package/src/tui.js CHANGED
@@ -1557,7 +1557,12 @@ export class TUI {
1557
1557
  // A dead refresh token surfaces as "reauth" (re-login needed) rather than a
1558
1558
  // generic "error", so the user knows the fix. Display-only — account.status
1559
1559
  // stays 'error' so routing/eligibility still exclude it.
1560
- if (a.enabled !== false && a.refreshDead) effectiveStatus = 'reauth';
1560
+ // A DISABLED account still shows its auth state. Suppressing it meant disabling an
1561
+ // account hid the fact that it needs re-login: the row read "✕ disabled" whether the
1562
+ // credentials were fine or long dead, so re-enabling it later silently produced a
1563
+ // broken account. Reported 2026-08-10 with all 8 disabled accounts sitting on dead
1564
+ // credentials (HTTP 401) and no way to see it.
1565
+ if (a.refreshDead) effectiveStatus = a.enabled === false ? 'disabled-reauth' : 'reauth';
1561
1566
  switch (effectiveStatus) {
1562
1567
  case 'active': status = isCur ? green('active') : 'active'; break;
1563
1568
  case 'reauth': status = yellow('reauth'); break;
@@ -1566,6 +1571,9 @@ export class TUI {
1566
1571
  case 'waiting': status = yellow('waiting'); break;
1567
1572
  case 'paused': status = yellow('paused'); break;
1568
1573
  case 'disabled': status = red('✕ disabled'); break;
1574
+ // Disabled AND needs re-login — both facts matter: it won't serve because you
1575
+ // switched it off, and it CAN'T serve until you log in again.
1576
+ case 'disabled-reauth': status = red('✕ reauth'); break;
1569
1577
  case 'throttled': {
1570
1578
  // A transient auto-recovering cooldown — show the remaining time (from
1571
1579
  // rateLimitedUntil) so it reads as "recovering in Ns", not stuck.