maxpool 1.5.53 → 1.5.55

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.53",
3
+ "version": "1.5.55",
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",
package/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import net from 'node:net';
3
4
  import { spawn, spawnSync } from 'node:child_process';
4
5
  import { createInterface } from 'node:readline';
5
6
  import { loadOrCreateConfig, loadConfig, saveConfig, atomicConfigUpdate, getConfigPath, loadState, saveState, getStatePath, getLogPath, readGeneration, flushConfigWrites, flushStateWrites } from './config.js';
@@ -7,6 +8,24 @@ import { setEventLogPath, installConsoleMirror, setConsoleStdoutSuppressed } fro
7
8
  import { SleepGuard } from './sleep-guard.js';
8
9
  import { AccountManager } from './account-manager.js';
9
10
  import { createProxyServer, REQUEST_IDLE_MAX_MS } from './server.js';
11
+
12
+ // ── Happy Eyeballs: stop racing a dead IPv6 leg ────────────────────────────────
13
+ // Node 20 resolves both families and, 250ms after starting the IPv4 connect, races an
14
+ // IPv6 one (autoSelectFamily, autoSelectFamilyAttemptTimeout=250). On a machine whose
15
+ // resolver returns an AAAA that BLACKHOLES — a VPN advertising IPv6 it cannot carry is
16
+ // the common case — that second leg never completes and takes the whole connect down
17
+ // with it, surfacing as UND_ERR_CONNECT_TIMEOUT / ETIMEDOUT on a network that is
18
+ // otherwise fine (curl, which falls back cleanly, succeeds in the same second).
19
+ //
20
+ // Measured 2026-08-01 on this fleet: 6,848 connect failures in one day; A/B over 14
21
+ // requests gave 12/14 at the 250ms default vs 14/14 with the timeout raised. Raising it
22
+ // is preferred over disabling the race outright: real IPv6 still wins where it works,
23
+ // and a blackholed leg simply loses instead of poisoning the attempt. `dns
24
+ // setDefaultResultOrder('ipv4first')` was ALSO measured and is NOT reliable (6 of 14 in a
25
+ // degraded window) — it reorders preference but still starts the doomed leg.
26
+ net.setDefaultAutoSelectFamilyAttemptTimeout(
27
+ Math.max(1000, Number(process.env.MAXPOOL_FAMILY_ATTEMPT_TIMEOUT_MS) || 5000),
28
+ );
10
29
  import { Prober } from './prober.js';
11
30
  import { loginOAuth, fetchProfile, refreshAccessToken, isTokenExpiringSoon, tokenFingerprint } from './oauth.js';
12
31
  import { TUI } from './tui.js';
package/src/server.js CHANGED
@@ -1446,11 +1446,18 @@ function unavailableMessage(accountManager, requestInfo = {}, retryAfter, willRe
1446
1446
  }
1447
1447
 
1448
1448
  const claudeCount = accountManager.accounts.filter(a => a.type !== 'provider').length;
1449
- // Only name the providers when this pool actually HAS them (`cc all`). On `cc ma`
1450
- // (Claude-only) there are none, so the old hardcoded "and the GLM/Kimi providers"
1451
- // was a lie. When present they DO serve (not barred), so they're saturated too.
1452
- const providersClause = accountManager.accounts.some(a => a.type === 'provider')
1449
+ // Only name the providers when this pool HAS them AND they are actually allowed to
1450
+ // serve this request. With crossProviderFallbackPolicy 'never' (the default) they are
1451
+ // barred by POLICY, not saturated — saying they are "at their limit" is a lie that
1452
+ // hides the real, one-keypress fix. Name the switch instead.
1453
+ const providerAccounts = accountManager.accounts.filter(a => a.type === 'provider');
1454
+ const providersUsable = providerAccounts.some(a => a.enabled !== false
1455
+ && accountManager._claudeFallbackFor?.(a.provider) !== 'never');
1456
+ const providersClause = providerAccounts.length && providersUsable
1453
1457
  ? ' and the GLM/Kimi providers' : '';
1458
+ const providersBarredHint = providerAccounts.length && !providersUsable
1459
+ ? ' GLM and Kimi are switched off for Claude sessions — turn one on with m then g if you want them to cover this.'
1460
+ : '';
1454
1461
 
1455
1462
  // No route is expected to recover within the queue window — i.e. every Claude
1456
1463
  // account is at its own 5h/weekly limit. A short "retry in Ns" would be a lie;
@@ -1459,10 +1466,14 @@ function unavailableMessage(accountManager, requestInfo = {}, retryAfter, willRe
1459
1466
  const eta = Number.isFinite(retryAfter) && retryAfter > 0
1460
1467
  ? ` Soonest reset in ~${formatRetryDuration(retryAfter)}, beyond the hold window.`
1461
1468
  : '';
1462
- return `No account can take this request — all ${claudeCount} Claude accounts${providersClause} are at their limit.${eta} Add another Claude account or wait for a quota reset.`;
1469
+ return `No account can take this request — all ${claudeCount} Claude accounts${providersClause} are at their limit.${eta} Add another Claude account or wait for a quota reset.${providersBarredHint}`;
1463
1470
  }
1464
1471
 
1465
- return `No account can take this request right now — all ${claudeCount} Claude accounts${providersClause} are momentarily at their limit. Retry in ${retryAfter}s.`;
1472
+ // "momentarily ... Retry in 2282s" was two bugs in one breath: 2282 seconds is 38
1473
+ // MINUTES (not momentary), and raw seconds are unreadable. Scale the wording to the
1474
+ // actual wait and render it in human units, the way every other branch already does.
1475
+ const waitLong = Number.isFinite(retryAfter) && retryAfter >= 120;
1476
+ return `No account can take this request right now — all ${claudeCount} Claude accounts${providersClause} are ${waitLong ? 'at their limit' : 'momentarily at their limit'}. Retry in ~${formatRetryDuration(retryAfter)}.${providersBarredHint}`;
1466
1477
  }
1467
1478
 
1468
1479
  // A provider (GLM/Kimi) rejecting a request whose token count exceeds its context