gm-codex 2.0.951 → 2.0.952

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-codex",
3
- "version": "2.0.951",
3
+ "version": "2.0.952",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": {
6
6
  "name": "AnEntrypoint",
package/bin/bootstrap.js CHANGED
@@ -328,12 +328,14 @@ async function bootstrap(opts) {
328
328
  if (fs.existsSync(finalPath) && fs.existsSync(okSentinel)) {
329
329
  if (!opts.silent) log(`cache hit: ${finalPath}`);
330
330
  pruneOldVersions(root, version, readRtkVersion(wrapperDir));
331
+ proactiveKillForNewInstall(version, finalPath);
331
332
  return finalPath;
332
333
  }
333
334
 
334
335
  if (healIfShaMatches(finalPath, expectedSha, okSentinel, partialPath, 'plugkit')) {
335
336
  if (!opts.silent) log(`cache heal (sha match): ${finalPath}`);
336
337
  pruneOldVersions(root, version, readRtkVersion(wrapperDir));
338
+ proactiveKillForNewInstall(version, finalPath);
337
339
  try { await bootstrapRtk(verDir, version, wrapperDir, opts.silent, root); }
338
340
  catch (err) { log(`rtk fetch skipped: ${err.message}`); }
339
341
  return finalPath;
@@ -344,11 +346,13 @@ async function bootstrap(opts) {
344
346
  try {
345
347
  if (fs.existsSync(finalPath) && fs.existsSync(okSentinel)) {
346
348
  pruneOldVersions(root, version, readRtkVersion(wrapperDir));
349
+ proactiveKillForNewInstall(version, finalPath);
347
350
  return finalPath;
348
351
  }
349
352
  if (healIfShaMatches(finalPath, expectedSha, okSentinel, partialPath, 'plugkit')) {
350
353
  log(`cache heal (sha match) under lock: ${finalPath}`);
351
354
  pruneOldVersions(root, version, readRtkVersion(wrapperDir));
355
+ proactiveKillForNewInstall(version, finalPath);
352
356
  try { await bootstrapRtk(verDir, version, wrapperDir, opts.silent, root); }
353
357
  catch (err) { log(`rtk fetch skipped: ${err.message}`); }
354
358
  return finalPath;
@@ -393,7 +397,7 @@ async function bootstrap(opts) {
393
397
  log(`installed ${finalPath}`);
394
398
  obsEvent('bootstrap', 'install.done', { path: finalPath, version, kind: 'plugkit' });
395
399
  pruneOldVersions(root, version, readRtkVersion(wrapperDir));
396
- proactiveKillForNewInstall(version);
400
+ proactiveKillForNewInstall(version, finalPath);
397
401
  try { await bootstrapRtk(verDir, version, wrapperDir, opts.silent, root); }
398
402
  catch (err) { log(`rtk fetch skipped: ${err.message}`); }
399
403
  return finalPath;
@@ -539,6 +543,60 @@ function killRunningDaemons(reason) {
539
543
  return killedPids;
540
544
  }
541
545
 
546
+ function listRunningPlugkitImagePaths() {
547
+ const out = [];
548
+ try {
549
+ const { spawnSync } = require('child_process');
550
+ if (os.platform() === 'win32') {
551
+ const r = spawnSync('tasklist', ['/FI', 'IMAGENAME eq plugkit*', '/FO', 'CSV', '/NH'], { windowsHide: true, encoding: 'utf8' });
552
+ const text = (r && r.stdout) || '';
553
+ const seen = new Set();
554
+ for (const line of text.split(/\r?\n/)) {
555
+ const m = line.match(/^"([^"]+)","(\d+)"/);
556
+ if (!m) continue;
557
+ const pid = parseInt(m[2], 10);
558
+ if (!Number.isFinite(pid) || seen.has(pid)) continue;
559
+ seen.add(pid);
560
+ let imagePath = '';
561
+ try {
562
+ const p = spawnSync('powershell', ['-NoProfile', '-NonInteractive', '-Command', `(Get-Process -Id ${pid} -ErrorAction SilentlyContinue).Path`], { windowsHide: true, encoding: 'utf8' });
563
+ imagePath = ((p && p.stdout) || '').trim();
564
+ } catch (_) {}
565
+ out.push({ pid, path: imagePath });
566
+ }
567
+ } else if (os.platform() === 'linux') {
568
+ let entries = [];
569
+ try { entries = fs.readdirSync('/proc'); } catch (_) {}
570
+ for (const e of entries) {
571
+ if (!/^\d+$/.test(e)) continue;
572
+ const pid = parseInt(e, 10);
573
+ let comm = '';
574
+ try { comm = fs.readFileSync(`/proc/${pid}/comm`, 'utf8').trim(); } catch (_) { continue; }
575
+ if (!/^plugkit/i.test(comm)) continue;
576
+ let imagePath = '';
577
+ try { imagePath = fs.readlinkSync(`/proc/${pid}/exe`); } catch (_) {}
578
+ out.push({ pid, path: imagePath });
579
+ }
580
+ } else {
581
+ const r = spawnSync('ps', ['-axo', 'pid=,comm='], { encoding: 'utf8' });
582
+ const text = (r && r.stdout) || '';
583
+ for (const line of text.split(/\r?\n/)) {
584
+ const m = line.match(/^\s*(\d+)\s+(.+?)\s*$/);
585
+ if (!m) continue;
586
+ if (!/plugkit/i.test(m[2])) continue;
587
+ const pid = parseInt(m[1], 10);
588
+ let imagePath = '';
589
+ try {
590
+ const p = spawnSync('ps', ['-p', String(pid), '-o', 'command='], { encoding: 'utf8' });
591
+ imagePath = ((p && p.stdout) || '').trim().split(/\s+/)[0] || '';
592
+ } catch (_) {}
593
+ out.push({ pid, path: imagePath });
594
+ }
595
+ }
596
+ } catch (_) {}
597
+ return out;
598
+ }
599
+
542
600
  function killSpoolWatcherInCwd(reason) {
543
601
  try {
544
602
  const pidPath = path.join(process.cwd(), '.gm', 'exec-spool', '.watcher.pid');
@@ -554,19 +612,27 @@ function killSpoolWatcherInCwd(reason) {
554
612
  return null;
555
613
  }
556
614
 
557
- function proactiveKillForNewInstall(installedVersion) {
615
+ function proactiveKillForNewInstall(installedVersion, finalPath) {
558
616
  try {
559
- const recorded = readDaemonVersion();
560
- if (recorded === installedVersion) return;
561
- const reason = `install:${recorded || 'none'}->${installedVersion}`;
562
- const killed = killRunningDaemons(reason);
563
- const watcherPid = killSpoolWatcherInCwd(reason);
564
- if (killed.length || watcherPid) {
565
- const parts = [];
566
- if (killed.length) parts.push(`daemon pid=${killed.join(',')} v${recorded || '?'}`);
567
- if (watcherPid) parts.push(`watcher pid=${watcherPid}`);
568
- try { process.stderr.write(`[bootstrap] killed stale ${parts.join(' + ')}, new binary ${installedVersion} ready\n`); } catch (_) {}
617
+ const reason = `install:v${installedVersion}`;
618
+ const target = finalPath ? path.resolve(finalPath).toLowerCase() : null;
619
+ const cacheRootNorm = (() => {
620
+ try { return path.resolve(cacheRoot()).toLowerCase(); } catch (_) { return null; }
621
+ })();
622
+ const procs = listRunningPlugkitImagePaths();
623
+ for (const { pid, path: imagePath } of procs) {
624
+ if (!Number.isFinite(pid) || pid === process.pid) continue;
625
+ if (!imagePath) continue;
626
+ const norm = path.resolve(imagePath).toLowerCase();
627
+ if (target && norm === target) continue;
628
+ if (!cacheRootNorm || !norm.startsWith(cacheRootNorm + path.sep.toLowerCase())) continue;
629
+ if (killPid(pid)) {
630
+ try { process.stderr.write(`[bootstrap] killed stale daemon pid=${pid} path=${imagePath} (current install: v${installedVersion})\n`); } catch (_) {}
631
+ obsEvent('bootstrap', 'daemon.killed', { pid, oldPath: imagePath, installedVersion, mechanism: 'process-path' });
632
+ }
569
633
  }
634
+ killRunningDaemons(reason);
635
+ killSpoolWatcherInCwd(reason);
570
636
  writeDaemonVersion(installedVersion);
571
637
  } catch (_) {}
572
638
  }
@@ -576,6 +642,11 @@ function proactiveKillForNewInstall(installedVersion) {
576
642
  function killStaleDaemonIfVersionChanged(wrapperDir) {
577
643
  let currentVersion;
578
644
  try { currentVersion = readVersionFile(wrapperDir); } catch (_) { return; }
645
+ const cached = resolveCachedBinary({ wrapperDir, version: currentVersion });
646
+ if (cached) {
647
+ proactiveKillForNewInstall(currentVersion, cached);
648
+ return;
649
+ }
579
650
  const recorded = readDaemonVersion();
580
651
  if (recorded === currentVersion) return;
581
652
  if (recorded) killRunningDaemons(`version_change:${recorded}->${currentVersion}`);
package/gm.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.951",
3
+ "version": "2.0.952",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-codex",
3
- "version": "2.0.951",
3
+ "version": "2.0.952",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": "AnEntrypoint",
6
6
  "license": "MIT",
package/plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm",
3
- "version": "2.0.951",
3
+ "version": "2.0.952",
4
4
  "description": "State machine agent with hooks, skills, and automated git enforcement",
5
5
  "author": {
6
6
  "name": "AnEntrypoint",