maxpool 1.5.58 → 1.5.60
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/server.js +34 -2
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -33,6 +33,10 @@ const NETWORK_ERROR_CODES = new Set([
|
|
|
33
33
|
'ENOTFOUND', 'EAI_AGAIN', 'EPIPE', 'ECONNABORTED', 'UND_ERR_SOCKET', 'UND_ERR_HEADERS_TIMEOUT',
|
|
34
34
|
]);
|
|
35
35
|
const isNetworkCode = c => Boolean(c) && NETWORK_ERROR_CODES.has(c);
|
|
36
|
+
// How long a NON-STREAMING request may soak through a network blip. Short by construction:
|
|
37
|
+
// with no keepalive the client's hard 300s floor applies, so we must error first.
|
|
38
|
+
const NETWORK_SOAK_NONSTREAM_MS = Math.max(5_000,
|
|
39
|
+
Number(process.env.MAXPOOL_NETWORK_SOAK_NONSTREAM_MS) || 45_000);
|
|
36
40
|
const QUEUE_KEEPALIVE = 'event: ping\ndata: {}\n\n';
|
|
37
41
|
// 240s, strictly BELOW Claude Code's hard 300s stall floor. At the old 300_000 the two
|
|
38
42
|
// timers were a dead heat and the client always won — maxpool's clock starts when a chunk
|
|
@@ -1332,11 +1336,20 @@ async function forwardRequest(
|
|
|
1332
1336
|
// Record the user-facing failure explicitly so it's greppable in the event
|
|
1333
1337
|
// log (the in-memory TUI feed scrolls away). `err` is the last network error.
|
|
1334
1338
|
console.error(`[Maxpool] Returned connection_unavailable (503) after network errors on all routes (last: "${account.name}" — ${err.code || err.message})`);
|
|
1339
|
+
// Name what actually happened. "Check your internet connection" sent the user
|
|
1340
|
+
// hunting a fault that wasn't there: measured 2026-08-04, 10 requests failed this
|
|
1341
|
+
// way within one hour while every other session kept working — the connection to
|
|
1342
|
+
// Anthropic dropped mid-flight (`terminated`) on each account tried, which is not
|
|
1343
|
+
// the same as the machine being offline. Carrying the real code makes it greppable
|
|
1344
|
+
// and stops the misdiagnosis.
|
|
1345
|
+
const lastCode = err.code || err.cause?.code || (err.message || '').slice(0, 40);
|
|
1335
1346
|
sendErrorResponse(res, requestInfo, 503, {
|
|
1336
1347
|
type: 'error',
|
|
1337
1348
|
error: {
|
|
1338
1349
|
type: 'connection_unavailable',
|
|
1339
|
-
message:
|
|
1350
|
+
message: `The connection to Claude dropped on every account maxpool tried (${lastCode}). `
|
|
1351
|
+
+ 'This is not a quota problem, and it is usually brief — sending the message again normally works. '
|
|
1352
|
+
+ 'If it keeps happening, the link between this machine and Anthropic is unstable.',
|
|
1340
1353
|
},
|
|
1341
1354
|
});
|
|
1342
1355
|
return;
|
|
@@ -1949,7 +1962,26 @@ async function queueAndRetry(
|
|
|
1949
1962
|
// a real outage doesn't spin. A non-streaming network failure (no keepalive) still
|
|
1950
1963
|
// fails fast — it would die on the client's own timeout anyway.
|
|
1951
1964
|
if (cause === 'proxy') return false;
|
|
1952
|
-
if (cause === 'network' &&
|
|
1965
|
+
if (cause === 'network' && !canQueueBufferedBody) return false;
|
|
1966
|
+
// NETWORK SOAK, hard-bounded. A brief connectivity blip should not throw away a turn —
|
|
1967
|
+
// but an unbounded soak is worse than a fast error (it presents as a hang, which is the
|
|
1968
|
+
// failure mode the user explicitly fears). So a network-caused hold carries its OWN
|
|
1969
|
+
// absolute deadline, stamped once and NEVER reset by a re-queue, independent of the
|
|
1970
|
+
// per-attempt queue window. Past it, we stop soaking and return the honest error.
|
|
1971
|
+
//
|
|
1972
|
+
// Non-streaming gets a short budget: it has no keepalive, so the client's hard 300s
|
|
1973
|
+
// floor applies with nothing resetting it — we must give up well before that so the user
|
|
1974
|
+
// sees a real message instead of a client-side timeout.
|
|
1975
|
+
if (cause === 'network') {
|
|
1976
|
+
const budgetMs = requestInfo.stream
|
|
1977
|
+
? Math.max(60_000, Number(queueConfig.networkMaxWaitMs) || 120_000)
|
|
1978
|
+
: NETWORK_SOAK_NONSTREAM_MS;
|
|
1979
|
+
requestInfo.networkSoakDeadline ||= Date.now() + budgetMs;
|
|
1980
|
+
if (Date.now() >= requestInfo.networkSoakDeadline) {
|
|
1981
|
+
console.log(`[Maxpool] Network soak budget spent (${Math.round(budgetMs / 1000)}s) — returning the connection error instead of holding longer`);
|
|
1982
|
+
return false;
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1953
1985
|
|
|
1954
1986
|
const maxWaitMs = Math.max(0, Number(queueConfig.maxWaitMs) || 0);
|
|
1955
1987
|
const autoMaxWaitMs = queueConfig.autoMaxWaitMs == null
|