maxpool 1.0.3 → 1.0.4
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 +3 -2
- package/src/config.js +5 -0
- package/src/index.js +6 -0
- package/src/updater.js +93 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maxpool",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
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",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"scripts": {
|
|
15
15
|
"start": "node src/index.js",
|
|
16
16
|
"test": "node --test",
|
|
17
|
-
"lint": "eslint src/ test/"
|
|
17
|
+
"lint": "eslint src/ test/",
|
|
18
|
+
"release": "bash scripts/release.sh"
|
|
18
19
|
},
|
|
19
20
|
"keywords": [
|
|
20
21
|
"claude",
|
package/src/config.js
CHANGED
|
@@ -36,6 +36,11 @@ export function createDefaultConfig() {
|
|
|
36
36
|
apiKey: 'mp-' + randomBytes(24).toString('base64url'),
|
|
37
37
|
},
|
|
38
38
|
upstream: 'https://api.anthropic.com',
|
|
39
|
+
// On startup, check npm for a newer maxpool and notify. Set false to disable.
|
|
40
|
+
updateCheck: true,
|
|
41
|
+
// When true, a newer version is installed automatically (npm i -g maxpool@latest)
|
|
42
|
+
// and applied on the NEXT restart — running sessions are never interrupted.
|
|
43
|
+
autoUpdate: false,
|
|
39
44
|
// Per-account "stop using this account" gate, applied to BOTH the 5h
|
|
40
45
|
// session window and the 7d weekly window (whichever utilization is
|
|
41
46
|
// higher). 0.90 = stop routing to an account once it crosses 90% of a
|
package/src/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { importCredentials, loginOAuth, fetchProfile, refreshAccessToken, isToke
|
|
|
10
10
|
import { TUI } from './tui.js';
|
|
11
11
|
import { RestartController } from './restart-controller.js';
|
|
12
12
|
import { resolveAccounts } from './account-config.js';
|
|
13
|
+
import { maybeCheckForUpdate } from './updater.js';
|
|
13
14
|
|
|
14
15
|
const args = process.argv.slice(2);
|
|
15
16
|
const command = args[0];
|
|
@@ -361,6 +362,11 @@ async function serverWorkerCommand() {
|
|
|
361
362
|
} else {
|
|
362
363
|
logPlainServerStart({ host, port, accounts, threshold, config });
|
|
363
364
|
}
|
|
365
|
+
|
|
366
|
+
// Non-blocking update check. Notifies (or self-updates if config.autoUpdate);
|
|
367
|
+
// never interrupts the running proxy. Failures are swallowed.
|
|
368
|
+
const notify = msg => (tui?._addLog ? tui._addLog(msg) : console.log(`[Maxpool] ${msg}`));
|
|
369
|
+
maybeCheckForUpdate(config, notify).catch(() => {});
|
|
364
370
|
});
|
|
365
371
|
|
|
366
372
|
process.on('SIGINT', () => shutdownGracefully('SIGINT'));
|
package/src/updater.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
const PACKAGE = 'maxpool';
|
|
9
|
+
const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
|
|
10
|
+
|
|
11
|
+
/** Read the running maxpool version from its own package.json. Null on failure. */
|
|
12
|
+
export async function getCurrentVersion() {
|
|
13
|
+
try {
|
|
14
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const pkg = JSON.parse(await readFile(join(here, '..', 'package.json'), 'utf-8'));
|
|
16
|
+
return pkg.version || null;
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Compare two dotted versions. Returns 1 if a>b, -1 if a<b, 0 if equal. */
|
|
23
|
+
export function compareVersions(a, b) {
|
|
24
|
+
const pa = String(a).split('.').map(n => Number(n) || 0);
|
|
25
|
+
const pb = String(b).split('.').map(n => Number(n) || 0);
|
|
26
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
27
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return 1;
|
|
28
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return -1;
|
|
29
|
+
}
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Check npm for a newer published version. Network-failure-safe: returns null
|
|
35
|
+
* on any error (offline, timeout, bad response) so a check never breaks startup.
|
|
36
|
+
*/
|
|
37
|
+
export async function checkForUpdate(currentVersion, { timeoutMs = 4000, registry = DEFAULT_REGISTRY } = {}) {
|
|
38
|
+
try {
|
|
39
|
+
const ctrl = new AbortController();
|
|
40
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
41
|
+
let res;
|
|
42
|
+
try {
|
|
43
|
+
res = await fetch(`${registry}/${PACKAGE}/latest`, { signal: ctrl.signal });
|
|
44
|
+
} finally {
|
|
45
|
+
clearTimeout(timer);
|
|
46
|
+
}
|
|
47
|
+
if (!res.ok) return null;
|
|
48
|
+
const data = await res.json();
|
|
49
|
+
const latest = data.version;
|
|
50
|
+
if (!latest) return null;
|
|
51
|
+
return {
|
|
52
|
+
latest,
|
|
53
|
+
current: currentVersion,
|
|
54
|
+
hasUpdate: currentVersion ? compareVersions(latest, currentVersion) > 0 : false,
|
|
55
|
+
};
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Run `npm install -g maxpool@latest`. Returns {ok, output|error}. */
|
|
62
|
+
export async function selfUpdate({ timeoutMs = 120_000 } = {}) {
|
|
63
|
+
try {
|
|
64
|
+
const { stdout, stderr } = await execFileAsync('npm', ['install', '-g', `${PACKAGE}@latest`], { timeout: timeoutMs });
|
|
65
|
+
return { ok: true, output: (stdout || stderr || '').trim() };
|
|
66
|
+
} catch (err) {
|
|
67
|
+
return { ok: false, error: err.message };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Startup hook: check for an update and either notify (default) or self-install
|
|
73
|
+
* (config.autoUpdate). Never auto-restarts a running proxy — the new version
|
|
74
|
+
* applies on the next restart, so in-flight sessions are never interrupted.
|
|
75
|
+
* Fire-and-forget; all failures are swallowed.
|
|
76
|
+
*/
|
|
77
|
+
export async function maybeCheckForUpdate(config, notify) {
|
|
78
|
+
if (config?.updateCheck === false) return;
|
|
79
|
+
const current = await getCurrentVersion();
|
|
80
|
+
const result = await checkForUpdate(current);
|
|
81
|
+
if (!result || !result.hasUpdate) return;
|
|
82
|
+
|
|
83
|
+
notify(`Update available: ${result.current} → ${result.latest}`);
|
|
84
|
+
if (config?.autoUpdate) {
|
|
85
|
+
notify(`Auto-updating to ${result.latest}…`);
|
|
86
|
+
const r = await selfUpdate();
|
|
87
|
+
notify(r.ok
|
|
88
|
+
? `Updated to ${result.latest}. Restart maxpool to apply (running sessions are not interrupted).`
|
|
89
|
+
: `Auto-update failed: ${r.error}. Run: npm i -g ${PACKAGE}`);
|
|
90
|
+
} else {
|
|
91
|
+
notify(`Run 'npm i -g ${PACKAGE}' to update, or set "autoUpdate": true in your config.`);
|
|
92
|
+
}
|
|
93
|
+
}
|