gm-plugkit 2.0.1779 → 2.0.1793

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/bootstrap.js CHANGED
@@ -450,6 +450,28 @@ function writeDaemonVersion(v) {
450
450
  try { fs.writeFileSync(daemonVersionSentinel(), String(v)); } catch (_) {}
451
451
  }
452
452
 
453
+ function pidCommandLineForKillGuard(pid) {
454
+ try {
455
+ if (process.platform === 'win32') {
456
+ const r = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', `(Get-CimInstance Win32_Process -Filter "ProcessId=${Number(pid)}").CommandLine`], { encoding: 'utf8', windowsHide: true, timeout: 5000 });
457
+ return String((r && r.stdout) || '');
458
+ }
459
+ const r = spawnSync('ps', ['-p', String(pid), '-o', 'args='], { encoding: 'utf8', timeout: 5000 });
460
+ return String((r && r.stdout) || '');
461
+ } catch (_) { return ''; }
462
+ }
463
+
464
+ function pidIsPlugkitProcess(pid) {
465
+ return /plugkit-wasm-wrapper\.js|plugkit-supervisor\.js|gm-plugkit[\\\/]supervisor\.js/i.test(pidCommandLineForKillGuard(pid));
466
+ }
467
+
468
+ function writeKillAttribution(targetSpoolDir, info) {
469
+ try {
470
+ fs.mkdirSync(targetSpoolDir, { recursive: true });
471
+ fs.writeFileSync(path.join(targetSpoolDir, '.kill-attribution.json'), JSON.stringify({ killer_pid: process.pid, killer_cwd: process.cwd(), killer_script: __filename, ts: Date.now(), ...info }, null, 2));
472
+ } catch (_) {}
473
+ }
474
+
453
475
  function killPid(pid) {
454
476
  if (!Number.isFinite(pid) || pid === process.pid || !pidAlive(pid)) return false;
455
477
  try { process.kill(pid, 'SIGTERM'); }
@@ -465,6 +487,12 @@ function killSpoolWatcherInCwd(reason) {
465
487
  const pidPath = path.join(process.cwd(), '.gm', 'exec-spool', '.watcher.pid');
466
488
  if (!fs.existsSync(pidPath)) return null;
467
489
  const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
490
+ if (pidAlive(pid) && !pidIsPlugkitProcess(pid)) {
491
+ obsEvent('bootstrap', 'watcher.kill-skipped-pid-reused', { pid, reason });
492
+ try { fs.unlinkSync(pidPath); } catch (_) {}
493
+ return null;
494
+ }
495
+ writeKillAttribution(path.join(process.cwd(), '.gm', 'exec-spool'), { reason, target_pid: pid, via: 'killSpoolWatcherInCwd' });
468
496
  if (killPid(pid)) {
469
497
  obsEvent('bootstrap', 'watcher.killed', { pid, reason });
470
498
  try { fs.unlinkSync(pidPath); } catch (_) {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-plugkit",
3
- "version": "2.0.1779",
3
+ "version": "2.0.1793",
4
4
  "description": "Bootstrap and daemon-spawn tool for gm plugkit binary. Downloads the correct platform binary, verifies SHA256, and starts the spool watcher daemon. Includes plugkit-wasm-wrapper for WASM-based spool watching.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -74,6 +74,28 @@ function emitShutdownReason(reason, err) {
74
74
  } catch (_) {}
75
75
  }
76
76
 
77
+ function pidCommandLineForKillGuard(pid) {
78
+ try {
79
+ if (process.platform === 'win32') {
80
+ const r = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', `(Get-CimInstance Win32_Process -Filter "ProcessId=${Number(pid)}").CommandLine`], { encoding: 'utf8', windowsHide: true, timeout: 5000 });
81
+ return String((r && r.stdout) || '');
82
+ }
83
+ const r = spawnSync('ps', ['-p', String(pid), '-o', 'args='], { encoding: 'utf8', timeout: 5000 });
84
+ return String((r && r.stdout) || '');
85
+ } catch (_) { return ''; }
86
+ }
87
+
88
+ function pidIsPlugkitProcess(pid) {
89
+ return /plugkit-wasm-wrapper\.js|plugkit-supervisor\.js|gm-plugkit[\\\/]supervisor\.js/i.test(pidCommandLineForKillGuard(pid));
90
+ }
91
+
92
+ function writeKillAttribution(targetSpoolDir, info) {
93
+ try {
94
+ fs.mkdirSync(targetSpoolDir, { recursive: true });
95
+ fs.writeFileSync(path.join(targetSpoolDir, '.kill-attribution.json'), JSON.stringify({ killer_pid: process.pid, killer_cwd: process.cwd(), killer_script: 'plugkit-wasm-wrapper', ts: Date.now(), ...info }, null, 2));
96
+ } catch (_) {}
97
+ }
98
+
77
99
  function writeVerbActive(verb, task) {
78
100
  __currentVerbContext = { verb, task, started_at_ms: Date.now(), pid: process.pid };
79
101
  try {
@@ -1558,7 +1580,8 @@ function cosineSim(a, b) {
1558
1580
  }
1559
1581
 
1560
1582
  let __wasmAbortFlag = { aborted: false, code: 0 };
1561
- const WASI_FILESYSTEM_ROOT = path.join(GM_TOOLS_ROOT, 'wasi-fs');
1583
+ const WASI_PROJECT_SLUG = crypto.createHash('sha256').update(String(process.env.CLAUDE_PROJECT_DIR || process.cwd()).toLowerCase().replace(/\\/g, '/')).digest('hex').slice(0, 16);
1584
+ const WASI_FILESYSTEM_ROOT = path.join(GM_TOOLS_ROOT, 'wasi-fs', WASI_PROJECT_SLUG);
1562
1585
  const wasiOpenFiles = new Map();
1563
1586
  let wasiNextFd = 100;
1564
1587
 
@@ -2256,6 +2279,19 @@ function makeHostFunctions(instanceRef) {
2256
2279
  }
2257
2280
  },
2258
2281
 
2282
+ host_fs_remove: (pathPtr, pathLen) => {
2283
+ try {
2284
+ const filePath = readWasmStr(instanceRef.value, pathPtr, pathLen);
2285
+ if (!filePath) return 0;
2286
+ const st = fs.statSync(filePath);
2287
+ if (st.isDirectory()) return 0;
2288
+ fs.unlinkSync(filePath);
2289
+ return 1;
2290
+ } catch (e) {
2291
+ return 0;
2292
+ }
2293
+ },
2294
+
2259
2295
  host_fs_readdir: (pathPtr, pathLen) => {
2260
2296
  try {
2261
2297
  const dirPath = readWasmStr(instanceRef.value, pathPtr, pathLen);
@@ -3038,7 +3074,12 @@ async function runSpoolWatcher(instance, spoolDir) {
3038
3074
  holder_sha: holderSha,
3039
3075
  }));
3040
3076
  } catch (_) {}
3041
- try { process.kill(parseInt(pidStr, 10), 'SIGTERM'); } catch (_) {}
3077
+ if (pidIsPlugkitProcess(holderPidNum)) {
3078
+ writeKillAttribution(spoolDir, { reason: 'peer-stale-takeover', target_pid: holderPidNum, via: 'lock-takeover', holder_sha: holderSha });
3079
+ try { process.kill(holderPidNum, 'SIGTERM'); } catch (_) {}
3080
+ } else {
3081
+ try { logEvent('plugkit', 'takeover-kill-skipped-pid-reused', { holder_pid: pidStr }); } catch (_) {}
3082
+ }
3042
3083
  return 'takeover';
3043
3084
  } else {
3044
3085
  const msg = JSON.stringify({ ok: false, reason: 'another-watcher-active', pid: pidStr, age_ms: age });
@@ -3249,6 +3290,11 @@ async function runSpoolWatcher(instance, spoolDir) {
3249
3290
  try {
3250
3291
  process.kill(peerPid, 0);
3251
3292
  } catch (_) { continue; }
3293
+ if (!pidIsPlugkitProcess(peerPid)) {
3294
+ logEvent('plugkit', 'peer.kill-skipped-pid-reused', { peer_cwd: peerCwd, peer_pid: peerPid });
3295
+ continue;
3296
+ }
3297
+ writeKillAttribution(path.join(peerCwd, '.gm', 'exec-spool'), { reason: 'peer-stale-takeover', target_pid: peerPid, via: 'peer-sweep', peer_sha: peerSha, own_sha: _ownWrapperSha12 });
3252
3298
  logEvent('plugkit', 'peer.stale-wrapper-killed', { peer_cwd: peerCwd, peer_pid: peerPid, peer_sha: peerSha, own_sha: _ownWrapperSha12, lock_age_ms: age });
3253
3299
  console.error(`[plugkit-wasm] peer-sweep killing stale-wrapper watcher pid=${peerPid} cwd=${peerCwd} sha=${peerSha} (own=${_ownWrapperSha12})`);
3254
3300
  try {
package/plugkit.version CHANGED
@@ -1 +1 @@
1
- 0.1.807
1
+ 0.1.818
package/supervisor.js CHANGED
@@ -64,6 +64,28 @@ function writeSupervisorStatus(state, extra) {
64
64
  } catch (_) {}
65
65
  }
66
66
 
67
+ function pidCommandLineForKillGuard(pid) {
68
+ try {
69
+ if (process.platform === 'win32') {
70
+ const r = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', `(Get-CimInstance Win32_Process -Filter "ProcessId=${Number(pid)}").CommandLine`], { encoding: 'utf8', windowsHide: true, timeout: 5000 });
71
+ return String((r && r.stdout) || '');
72
+ }
73
+ const r = spawnSync('ps', ['-p', String(pid), '-o', 'args='], { encoding: 'utf8', timeout: 5000 });
74
+ return String((r && r.stdout) || '');
75
+ } catch (_) { return ''; }
76
+ }
77
+
78
+ function pidIsPlugkitProcess(pid) {
79
+ return /plugkit-wasm-wrapper\.js|plugkit-supervisor\.js|gm-plugkit[\\\/]supervisor\.js/i.test(pidCommandLineForKillGuard(pid));
80
+ }
81
+
82
+ function writeKillAttribution(targetSpoolDir, info) {
83
+ try {
84
+ fs.mkdirSync(targetSpoolDir, { recursive: true });
85
+ fs.writeFileSync(path.join(targetSpoolDir, '.kill-attribution.json'), JSON.stringify({ killer_pid: process.pid, killer_cwd: process.cwd(), killer_script: __filename, ts: Date.now(), ...info }, null, 2));
86
+ } catch (_) {}
87
+ }
88
+
67
89
  function pidAlive(pid) {
68
90
  if (!Number.isFinite(pid) || pid <= 0) return false;
69
91
  try { process.kill(pid, 0); return true; } catch (_) { return false; }
@@ -100,9 +122,14 @@ function acquireSingleInstance() {
100
122
  spool_status_age_ms: now - spoolTs,
101
123
  severity: 'critical',
102
124
  });
103
- try { process.kill(other, 'SIGTERM'); } catch (_) {}
104
- if (process.platform === 'win32') {
105
- try { spawnSync('taskkill', ['/F', '/T', '/PID', String(other)], { stdio: 'ignore', windowsHide: true, timeout: 3000 }); } catch (_) {}
125
+ if (pidIsPlugkitProcess(other)) {
126
+ writeKillAttribution(spoolDir, { reason: 'supervisor-takeover-wedged', target_pid: other, via: 'supervisor-duel' });
127
+ try { process.kill(other, 'SIGTERM'); } catch (_) {}
128
+ if (process.platform === 'win32') {
129
+ try { spawnSync('taskkill', ['/F', '/T', '/PID', String(other)], { stdio: 'ignore', windowsHide: true, timeout: 3000 }); } catch (_) {}
130
+ }
131
+ } else {
132
+ logEvent('supervisor.takeover-kill-skipped-pid-reused', { existing_pid: other, severity: 'warn' });
106
133
  }
107
134
  }
108
135
  try { fs.unlinkSync(SUPERVISOR_PID_PATH); } catch (_) {}