gm-kilo 2.0.882 → 2.0.884
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/bin/bootstrap.js +71 -4
- package/bin/plugkit.js +28 -16
- package/bin/plugkit.sha256 +6 -6
- package/bin/rtk.sha256 +2 -2
- package/package.json +1 -1
package/bin/bootstrap.js
CHANGED
|
@@ -9,11 +9,16 @@ const crypto = require('crypto');
|
|
|
9
9
|
const { URL } = require('url');
|
|
10
10
|
|
|
11
11
|
const RELEASE_REPO = 'AnEntrypoint/plugkit-bin';
|
|
12
|
-
const ATTEMPT_TIMEOUT_MS = 60 * 1000;
|
|
13
|
-
const STALL_TIMEOUT_MS =
|
|
12
|
+
const ATTEMPT_TIMEOUT_MS = 5 * 60 * 1000;
|
|
13
|
+
const STALL_TIMEOUT_MS = 60 * 1000;
|
|
14
14
|
const MAX_ATTEMPTS = 5;
|
|
15
15
|
const BACKOFF_MS = [2000, 5000, 15000, 30000];
|
|
16
|
-
|
|
16
|
+
// Worst case: a slow link downloading 140MB at 1MB/s = ~140s. Allow 30 minutes
|
|
17
|
+
// before another bootstrap process treats this lock as abandoned. Below this,
|
|
18
|
+
// concurrent bootstrap calls would wipe an in-progress download mid-stream
|
|
19
|
+
// (see the v0.1.294 incident where a race between two wrappers blew away the
|
|
20
|
+
// .partial during a 10-minute fetch).
|
|
21
|
+
const LOCK_STALE_MS = 30 * 60 * 1000;
|
|
17
22
|
|
|
18
23
|
function log(msg) {
|
|
19
24
|
try { process.stderr.write(`[plugkit-bootstrap] ${msg}\n`); } catch (_) {}
|
|
@@ -170,6 +175,10 @@ function fetchToFile(url, destPath, expectedTotal) {
|
|
|
170
175
|
return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
|
|
171
176
|
}
|
|
172
177
|
const append = res.statusCode === 206 && existing > 0;
|
|
178
|
+
// Ensure parent dir exists — a concurrent prune may have removed it
|
|
179
|
+
// between lock-acquire and now. Recreating is cheap and avoids a
|
|
180
|
+
// confusing ENOENT later.
|
|
181
|
+
try { ensureDir(path.dirname(destPath)); } catch (_) {}
|
|
173
182
|
const out = fs.createWriteStream(destPath, { flags: append ? 'a' : 'w' });
|
|
174
183
|
let bytes = append ? existing : 0;
|
|
175
184
|
let lastStderr = Date.now();
|
|
@@ -402,7 +411,65 @@ function resolveCachedBinary(opts) {
|
|
|
402
411
|
return null;
|
|
403
412
|
}
|
|
404
413
|
|
|
405
|
-
|
|
414
|
+
// ---------------------------------------------------------------------------
|
|
415
|
+
// Daemon kill on version change.
|
|
416
|
+
//
|
|
417
|
+
// The plugin tarball pins `plugkit.version`. When that pin advances and we
|
|
418
|
+
// install a newer cached binary, any long-running daemon (the runner) holds
|
|
419
|
+
// stale code and serves stale RPCs until killed. We track which version the
|
|
420
|
+
// daemon was last started under via `.daemon-version`; on every wrapper
|
|
421
|
+
// invocation, if the wrapper-pinned version differs, we kill the daemon so
|
|
422
|
+
// the next exec spawns it fresh under the new binary.
|
|
423
|
+
// ---------------------------------------------------------------------------
|
|
424
|
+
|
|
425
|
+
function daemonVersionSentinel() {
|
|
426
|
+
const root = (() => {
|
|
427
|
+
try { const r = cacheRoot(); ensureDir(r); return r; }
|
|
428
|
+
catch (_) { const r = fallbackCacheRoot(); ensureDir(r); return r; }
|
|
429
|
+
})();
|
|
430
|
+
return path.join(root, '.daemon-version');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function readDaemonVersion() {
|
|
434
|
+
try { return fs.readFileSync(daemonVersionSentinel(), 'utf8').trim(); }
|
|
435
|
+
catch (_) { return null; }
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function writeDaemonVersion(v) {
|
|
439
|
+
try { fs.writeFileSync(daemonVersionSentinel(), String(v)); } catch (_) {}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function killRunningDaemons(reason) {
|
|
443
|
+
const tmp = os.tmpdir();
|
|
444
|
+
let killed = 0;
|
|
445
|
+
for (const pidFile of ['glootie-runner.pid', 'plugkit-runner.pid']) {
|
|
446
|
+
const pidPath = path.join(tmp, pidFile);
|
|
447
|
+
if (!fs.existsSync(pidPath)) continue;
|
|
448
|
+
try {
|
|
449
|
+
const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
|
|
450
|
+
if (Number.isFinite(pid) && pid !== process.pid && pidAlive(pid)) {
|
|
451
|
+
try { process.kill(pid, 'SIGTERM'); killed++; }
|
|
452
|
+
catch (_) { try { process.kill(pid); killed++; } catch (_) {} }
|
|
453
|
+
obsEvent('bootstrap', 'daemon.killed', { pid, pidFile, reason });
|
|
454
|
+
}
|
|
455
|
+
try { fs.unlinkSync(pidPath); } catch (_) {}
|
|
456
|
+
} catch (_) {}
|
|
457
|
+
}
|
|
458
|
+
return killed;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Compare wrapper-pinned version against last-recorded daemon version. If
|
|
462
|
+
// they differ, kill the daemon so it respawns under the new binary.
|
|
463
|
+
function killStaleDaemonIfVersionChanged(wrapperDir) {
|
|
464
|
+
let currentVersion;
|
|
465
|
+
try { currentVersion = readVersionFile(wrapperDir); } catch (_) { return; }
|
|
466
|
+
const recorded = readDaemonVersion();
|
|
467
|
+
if (recorded === currentVersion) return;
|
|
468
|
+
if (recorded) killRunningDaemons(`version_change:${recorded}->${currentVersion}`);
|
|
469
|
+
writeDaemonVersion(currentVersion);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
module.exports = { bootstrap, resolveCachedBinary, resolveCachedRtk, platformKey, binaryName, rtkBinaryName, cacheRoot, obsEvent, killRunningDaemons, killStaleDaemonIfVersionChanged };
|
|
406
473
|
|
|
407
474
|
if (require.main === module) {
|
|
408
475
|
bootstrap({ silent: false })
|
package/bin/plugkit.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { spawn, spawnSync } = require('child_process');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
|
-
const { bootstrap, resolveCachedBinary, resolveCachedRtk, obsEvent } = require('./bootstrap');
|
|
6
|
+
const { bootstrap, resolveCachedBinary, resolveCachedRtk, obsEvent, killStaleDaemonIfVersionChanged } = require('./bootstrap');
|
|
7
7
|
|
|
8
8
|
const dir = __dirname;
|
|
9
9
|
|
|
@@ -26,21 +26,31 @@ async function main() {
|
|
|
26
26
|
const isHook = args[0] === 'hook';
|
|
27
27
|
const startedAt = Date.now();
|
|
28
28
|
obsEvent('plugkit_wrapper', 'invoke', { argv: args.slice(0, 4), is_hook: isHook });
|
|
29
|
+
// If the plugin tarball updated `plugkit.version` since the runner daemon
|
|
30
|
+
// was last started, kill the daemon so the next `runner start` picks up
|
|
31
|
+
// the freshly-installed binary instead of serving stale RPCs.
|
|
32
|
+
try { killStaleDaemonIfVersionChanged(dir); } catch (_) {}
|
|
29
33
|
let bin;
|
|
30
34
|
try {
|
|
31
|
-
|
|
35
|
+
const hookSubcmd = isHook ? (args[1] || '') : '';
|
|
36
|
+
// session-start ALWAYS bootstraps: this is the once-per-session moment
|
|
37
|
+
// where we guarantee the cached binary matches the wrapper-pinned version.
|
|
38
|
+
// If the bootstrap fails (offline) we fall through to whatever the cache
|
|
39
|
+
// currently has — the hook itself isn't blocking, just refreshing.
|
|
40
|
+
if (isHook && hookSubcmd === 'session-start') {
|
|
41
|
+
obsEvent('plugkit_wrapper', 'hook_bootstrap_session_start', { argv: args.slice(0, 4) });
|
|
42
|
+
try {
|
|
43
|
+
bin = await bootstrap({ wrapperDir: dir, silent: true });
|
|
44
|
+
} catch (e) {
|
|
45
|
+
process.stderr.write(`[plugkit] session-start bootstrap failed: ${e.message}\n`);
|
|
46
|
+
bin = resolveCachedBinary({ wrapperDir: dir }) || legacyFallback();
|
|
47
|
+
}
|
|
48
|
+
// session-start hook itself runs in the freshly-bootstrapped binary
|
|
49
|
+
// below — fall through to the spawn path so the actual handler runs.
|
|
50
|
+
if (!bin) process.exit(0);
|
|
51
|
+
} else if (isHook) {
|
|
32
52
|
bin = resolveCachedBinary({ wrapperDir: dir }) || legacyFallback();
|
|
33
53
|
if (!bin) {
|
|
34
|
-
const hookSubcmd = args[1] || '';
|
|
35
|
-
if (hookSubcmd === 'session-start') {
|
|
36
|
-
obsEvent('plugkit_wrapper', 'hook_bootstrap_session_start', { argv: args.slice(0, 4) });
|
|
37
|
-
try {
|
|
38
|
-
await bootstrap({ wrapperDir: dir });
|
|
39
|
-
} catch (e) {
|
|
40
|
-
process.stderr.write(`[plugkit] session-start bootstrap failed: ${e.message}\n`);
|
|
41
|
-
}
|
|
42
|
-
process.exit(0);
|
|
43
|
-
}
|
|
44
54
|
process.stderr.write(`[plugkit] hook ${hookSubcmd} skipped: binary not yet installed. Bootstrap will run on session-start.\n`);
|
|
45
55
|
obsEvent('plugkit_wrapper', 'hook_skip_uncached', { argv: args.slice(0, 4), dur_ms: Date.now() - startedAt });
|
|
46
56
|
process.exit(0);
|
|
@@ -76,16 +86,18 @@ async function main() {
|
|
|
76
86
|
}
|
|
77
87
|
}
|
|
78
88
|
|
|
89
|
+
// legacyFallback only returns a binary that lives next to the wrapper. We
|
|
90
|
+
// never reach across to ~/.claude/gm-tools/plugkit.exe or other ambient
|
|
91
|
+
// install dirs — those have proven to mask bootstrap failures by serving a
|
|
92
|
+
// stale version whose hooks silently mismatch the active wrapper code (see
|
|
93
|
+
// the v0.1.292-vs-v0.1.294 incident).
|
|
79
94
|
function legacyFallback() {
|
|
80
95
|
const os = require('os');
|
|
81
96
|
const p = os.platform();
|
|
82
97
|
const a = os.arch();
|
|
83
98
|
let candidates = [];
|
|
84
99
|
if (p === 'win32') {
|
|
85
|
-
candidates = [
|
|
86
|
-
path.join(dir, a === 'arm64' ? 'plugkit-win32-arm64.exe' : 'plugkit-win32-x64.exe'),
|
|
87
|
-
path.join(dir, 'plugkit.exe'),
|
|
88
|
-
];
|
|
100
|
+
candidates = [path.join(dir, a === 'arm64' ? 'plugkit-win32-arm64.exe' : 'plugkit-win32-x64.exe')];
|
|
89
101
|
} else if (p === 'darwin') {
|
|
90
102
|
candidates = [path.join(dir, a === 'arm64' ? 'plugkit-darwin-arm64' : 'plugkit-darwin-x64')];
|
|
91
103
|
} else {
|
package/bin/plugkit.sha256
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
f0a217d24261d6fee31b3f09347da19e2eda76f9b558adf607240cefcc131d01 plugkit-win32-x64.exe
|
|
2
|
+
24bfc958f0b0682c56f5531a8e8c42f047f228bf9592405ff6802a0371620379 plugkit-win32-arm64.exe
|
|
3
|
+
004d0ea64c0d20e0fc15005be2934995a9f21b5faf2d6f3e46815f1f56a3ea2f plugkit-darwin-x64
|
|
4
|
+
8281cc89e37d683cb798de88ede8214946b4d87d08209a06f5af3d00aff3ab37 plugkit-darwin-arm64
|
|
5
|
+
60c0f7d606bdb7fc38c8d62919204eacc736782223d5775a3329c3a788e50a67 plugkit-linux-x64
|
|
6
|
+
07bfac610e89a4923aa43479ce14e2d060bf95762d2144a32787ee3698aa902d plugkit-linux-arm64
|
package/bin/rtk.sha256
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
45001a4384331c752b20937d2918867717655e1f836a7da626cfd813abd6b828 rtk-win32-x64.exe
|
|
2
|
+
043a0438b9b28e50db56187d0e2e8e1833be674f45db24398cf3892f48f26004 rtk-win32-arm64.exe
|
|
3
3
|
e89fdf402c28796b510587a8b0fe046438b5b24d49533d1a2339a48aecae35e9 rtk-darwin-x64
|
|
4
4
|
2b203fd380f5782b5489eb016e34e3dbf848272a7fadf36b39bce6cfd9a3005c rtk-darwin-arm64
|
|
5
5
|
0da9950b859c7a2693aaf6c169f05f9b8965508ba1f23f1547e63d5fa988749e rtk-linux-x64
|