maxpool 1.24.9 → 1.24.11

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.24.9",
3
+ "version": "1.24.11",
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",
@@ -9,6 +9,7 @@
9
9
  * broken provider degrades to "disabled" instead of crashing the proxy.
10
10
  */
11
11
  import { execFile } from 'node:child_process';
12
+ import https from 'node:https';
12
13
  import { promisify } from 'node:util';
13
14
  import { readFileSync } from 'node:fs';
14
15
  import { homedir } from 'node:os';
@@ -57,12 +58,90 @@ function fromCache(secretName) {
57
58
  return (typeof v === 'string' && v.trim()) ? v.trim() : null;
58
59
  }
59
60
 
61
+ // REST PATH FIRST (2026-10-08). `gcloud secrets versions access` hung on this machine
62
+ // (>60s, 21 piled-up gcloud processes) while a plain Secret Manager REST call with the
63
+ // same user's token answered in 0.75s. Every startup resolve AND every 5-minute
64
+ // re-resolve went through the hung CLI, so all z.ai providers stayed
65
+ // "secret-unresolved" until a manual restart happened to land on a good moment.
66
+ // The token itself comes from `gcloud auth print-access-token` (fast — it reads the
67
+ // stored credential, no Secret Manager round-trip) and is reused for ~45 min.
68
+ let tokenMemo = { value: null, at: 0 };
69
+ const TOKEN_TTL_MS = 45 * 60_000;
70
+
71
+ async function accessToken(timeoutMs) {
72
+ if (tokenMemo.value && Date.now() - tokenMemo.at < TOKEN_TTL_MS) return tokenMemo.value;
73
+ const { stdout } = await execFileAsync('gcloud', ['auth', 'print-access-token'],
74
+ { timeout: Math.min(timeoutMs, 20_000), maxBuffer: 64 * 1024 });
75
+ const t = stdout.trim();
76
+ if (!t) throw new Error('empty token');
77
+ tokenMemo = { value: t, at: Date.now() };
78
+ return t;
79
+ }
80
+
81
+ // IPv4-PINNED GET (2026-10-08). On this network IPv6 to Google is black-holed:
82
+ // secretmanager.googleapis.com advertises AAAA records, an IPv6 connect hangs until
83
+ // timeout, and IPv4 answers fine (measured: `curl -6` 8s timeout, `curl -4` OK). gcloud's
84
+ // own HTTP stack tries IPv6 first and HANGS — that is why every CLI resolve timed out.
85
+ // `family: 4` makes this path immune; the CLI fallback below stays as a last resort.
86
+ function ipv4Get(url, { headers, signal }) {
87
+ return new Promise((resolve, reject) => {
88
+ const req = https.get(url, { headers, family: 4, signal }, res => {
89
+ const chunks = [];
90
+ res.on('data', c => chunks.push(c));
91
+ res.on('end', () => {
92
+ const body = Buffer.concat(chunks).toString('utf8');
93
+ resolve({ status: res.statusCode, ok: res.statusCode >= 200 && res.statusCode < 300, json: async () => JSON.parse(body) });
94
+ });
95
+ res.on('error', reject);
96
+ });
97
+ req.on('error', reject);
98
+ });
99
+ }
100
+
101
+ /** Test seam: inject a fetch + token source. */
102
+ let restDeps = { fetch: ipv4Get, token: accessToken };
103
+ export function __setRestDeps(d) { restDeps = { ...restDeps, ...d }; }
104
+ export function __resetRestDeps() { restDeps = { fetch: ipv4Get, token: accessToken }; tokenMemo = { value: null, at: 0 }; }
105
+
106
+ async function viaRest(secretName, project, timeoutMs) {
107
+ const tok = await restDeps.token(timeoutMs);
108
+ const url = `https://secretmanager.googleapis.com/v1/projects/${encodeURIComponent(project)}`
109
+ + `/secrets/${encodeURIComponent(secretName)}/versions/latest:access`;
110
+ const ctl = new AbortController();
111
+ const t = setTimeout(() => ctl.abort(), Math.min(timeoutMs, 15_000));
112
+ try {
113
+ const res = await restDeps.fetch(url, { headers: { Authorization: `Bearer ${tok}` }, signal: ctl.signal });
114
+ if (res.status === 401) { tokenMemo = { value: null, at: 0 }; return null; } // stale token → CLI fallback
115
+ // 403/404: the server answered definitively — the CLI would get the same answer.
116
+ if (res.status === 403 || res.status === 404) return { value: null, definitive: true };
117
+ if (!res.ok) return null;
118
+ const j = await res.json();
119
+ const b64 = j?.payload?.data;
120
+ if (!b64) return { value: null, definitive: true };
121
+ const v = Buffer.from(b64, 'base64').toString('utf8').trim();
122
+ return { value: v || null, definitive: true };
123
+ } finally { clearTimeout(t); }
124
+ }
125
+
60
126
  export async function resolveSecret(secretName, { project = DEFAULT_PROJECT, timeoutMs = 45_000, useCache = true } = {}) {
61
127
  if (!secretName || typeof secretName !== 'string') return null;
62
128
  if (useCache) {
63
129
  const cached = fromCache(secretName);
64
130
  if (cached) return cached;
65
131
  }
132
+ // ONE time budget for the whole lookup: REST first, then the CLI with whatever is
133
+ // left. A definitive REST answer (the secret does not exist / no access) skips the
134
+ // CLI entirely — the CLI would only re-ask the same server, and on an IPv6-broken
135
+ // network it hangs for the full timeout anyway.
136
+ const deadline = Date.now() + timeoutMs;
137
+ try {
138
+ const r = await viaRest(secretName, project, timeoutMs);
139
+ if (r?.value) return r.value;
140
+ if (r?.definitive) return null;
141
+ } catch { /* transport failure — fall through to the CLI path */ }
142
+ const remaining = deadline - Date.now();
143
+ if (remaining < 1000) return null;
144
+ timeoutMs = remaining;
66
145
  try {
67
146
  const { stdout } = await execFileAsync(
68
147
  'gcloud',