gm-plugkit 2.0.1291 → 2.0.1293

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": "gm-plugkit",
3
- "version": "2.0.1291",
3
+ "version": "2.0.1293",
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": {
@@ -461,15 +461,17 @@ function writeJsonFile(fp, value) {
461
461
  try { fs.writeFileSync(fp, JSON.stringify(value, null, 2)); } catch (_) {}
462
462
  }
463
463
 
464
- function findPlaywriter() {
464
+ const BROWSER_RUNNER_BIN = process.env.GM_BROWSER_RUNNER_BIN || 'playwriter';
465
+
466
+ function findBrowserRunner() {
465
467
  const npmR = spawnSync('npm', ['root', '-g'], { encoding: 'utf-8', shell: true });
466
468
  if (npmR.status === 0 && npmR.stdout.trim()) {
467
469
  const root = npmR.stdout.trim().split(/\r?\n/).pop();
468
- const binJs = path.join(root, 'playwriter', 'bin.js');
470
+ const binJs = path.join(root, BROWSER_RUNNER_BIN, 'bin.js');
469
471
  if (fs.existsSync(binJs)) return { cmd: process.execPath, baseArgs: [binJs], shell: false };
470
472
  }
471
473
  const whichCmd = process.platform === 'win32' ? 'where' : 'which';
472
- const r = spawnSync(whichCmd, ['playwriter'], { encoding: 'utf-8', shell: true });
474
+ const r = spawnSync(whichCmd, [BROWSER_RUNNER_BIN], { encoding: 'utf-8', shell: true });
473
475
  if (r.status === 0 && r.stdout.trim()) {
474
476
  const candidates = r.stdout.split(/\r?\n/).map(l => l.trim()).filter(Boolean);
475
477
  const cmd = candidates.find(c => c.toLowerCase().endsWith('.cmd')) || candidates.find(c => !c.toLowerCase().endsWith('.ps1')) || candidates[0];
@@ -477,11 +479,11 @@ function findPlaywriter() {
477
479
  }
478
480
  const bunR = spawnSync(whichCmd, ['bun'], { encoding: 'utf-8', shell: true });
479
481
  if (bunR.status === 0 && bunR.stdout.trim()) {
480
- return { cmd: 'bun', baseArgs: ['x', 'playwriter@latest'], shell: true };
482
+ return { cmd: 'bun', baseArgs: ['x', `${BROWSER_RUNNER_BIN}@latest`], shell: true };
481
483
  }
482
484
  const npxR = spawnSync(whichCmd, ['npx'], { encoding: 'utf-8', shell: true });
483
485
  if (npxR.status === 0 && npxR.stdout.trim()) {
484
- return { cmd: 'npx', baseArgs: ['-y', 'playwriter'], shell: true };
486
+ return { cmd: 'npx', baseArgs: ['-y', BROWSER_RUNNER_BIN], shell: true };
485
487
  }
486
488
  return null;
487
489
  }
@@ -637,7 +639,7 @@ function sleepSync(ms) {
637
639
  spawnSync(process.execPath, ['-e', `setTimeout(()=>{}, ${ms})`], { timeout: ms + 2000 });
638
640
  }
639
641
 
640
- function runPlaywriter(pw, args, timeoutMs) {
642
+ function runBrowserRunner(pw, args, timeoutMs) {
641
643
  const allArgs = [...pw.baseArgs, ...args];
642
644
  const useShell = !!pw.shell;
643
645
  const spawnCmd = useShell && /\s/.test(pw.cmd) ? `"${pw.cmd}"` : pw.cmd;
@@ -654,7 +656,7 @@ function runPlaywriter(pw, args, timeoutMs) {
654
656
  function scrubBrowserRunnerText(s) {
655
657
  if (!s || typeof s !== 'string') return s;
656
658
  let t = s;
657
- t = t.replace(/(^|[^A-Za-z0-9_\\/.-])playwriter(?![A-Za-z0-9_\\/.-])/gi, (m, pre) => `${pre}managed browser session`);
659
+ t = t.replace(/(^|[^A-Za-z0-9_\\/.-])(playwriter|playwright|puppeteer)(?![A-Za-z0-9_\\/.-])/gi, (m, pre) => `${pre}managed browser session`);
658
660
  t = t.replace(/Click the[^.\n]*?extension[^.\n]*?icon[^.\n]*?\.?/gi, '');
659
661
  t = t.replace(/(connected\s+)?browser\s+extension(\s+is)?\s+not\s+connected\b[^.\n]*\.?/gi, '');
660
662
  t = t.replace(/no\s+connected\s+browsers?\b[^.\n]*\.?/gi, '');
@@ -664,18 +666,20 @@ function scrubBrowserRunnerText(s) {
664
666
 
665
667
  function findInstalledChromiumBinary() {
666
668
  try {
667
- if (process.env.PLAYWRITER_BROWSER_PATH && fs.existsSync(process.env.PLAYWRITER_BROWSER_PATH)) {
668
- return process.env.PLAYWRITER_BROWSER_PATH;
669
+ const explicit = process.env.GM_BROWSER_RUNNER_PATH || process.env.PLAYWRITER_BROWSER_PATH;
670
+ if (explicit && fs.existsSync(explicit)) {
671
+ return explicit;
669
672
  }
670
673
  const roots = [];
674
+ const cacheDir = process.env.GM_BROWSER_RUNNER_CACHE_DIR || 'ms-playwright';
671
675
  if (process.platform === 'win32') {
672
676
  const lad = process.env.LOCALAPPDATA;
673
- if (lad) roots.push(path.join(lad, 'ms-playwright'));
677
+ if (lad) roots.push(path.join(lad, cacheDir));
674
678
  } else {
675
679
  const home = process.env.HOME || '';
676
680
  if (home) {
677
- roots.push(path.join(home, '.cache', 'ms-playwright'));
678
- roots.push(path.join(home, 'Library', 'Caches', 'ms-playwright'));
681
+ roots.push(path.join(home, '.cache', cacheDir));
682
+ roots.push(path.join(home, 'Library', 'Caches', cacheDir));
679
683
  }
680
684
  }
681
685
  const exeName = process.platform === 'win32' ? 'chrome.exe' : (process.platform === 'darwin' ? 'Chromium.app/Contents/MacOS/Chromium' : 'chrome');
@@ -705,11 +709,14 @@ function findInstalledChromiumBinary() {
705
709
  }
706
710
 
707
711
  function startManagedBrowser(pw, profileDir) {
708
- const args = [...pw.baseArgs, 'browser', 'start', '--user-data-dir', profileDir, '--headless'];
712
+ const headless = process.env.GM_BROWSER_HEADLESS === '1';
713
+ const args = [...pw.baseArgs, 'browser', 'start', '--user-data-dir', profileDir];
714
+ if (headless) args.push('--headless');
709
715
  const env = { ...process.env };
710
- if (!env.PLAYWRITER_BROWSER_PATH) {
716
+ if (!env.GM_BROWSER_RUNNER_PATH && !env.PLAYWRITER_BROWSER_PATH) {
711
717
  const browserBin = findInstalledChromiumBinary();
712
718
  if (browserBin) {
719
+ env.GM_BROWSER_RUNNER_PATH = browserBin;
713
720
  env.PLAYWRITER_BROWSER_PATH = browserBin;
714
721
  logEvent('plugkit', 'browser.binary-resolved', { path: browserBin });
715
722
  } else {
@@ -742,24 +749,24 @@ function isColdRunProfile(profileDir) {
742
749
  }
743
750
 
744
751
  function resolveManagedBrowserKey(pw) {
745
- if (process.env.PLAYWRITER_BROWSER_KEY) {
746
- const k = process.env.PLAYWRITER_BROWSER_KEY;
747
- if (/^profile:/.test(k)) {
748
- throw new Error(`PLAYWRITER_BROWSER_KEY=${k} points at a user OS browser profile; refusing — managed sessions must use install:Chromium:*`);
752
+ const explicitKey = process.env.GM_BROWSER_RUNNER_KEY || process.env.PLAYWRITER_BROWSER_KEY;
753
+ if (explicitKey) {
754
+ if (/^profile:/.test(explicitKey)) {
755
+ throw new Error(`GM_BROWSER_RUNNER_KEY=${explicitKey} points at a user OS browser profile; refusing — managed sessions must use install:Chromium:*`);
749
756
  }
750
- return k;
757
+ return explicitKey;
751
758
  }
752
759
  let text = '';
753
760
  try {
754
- const r = runPlaywriter(pw, ['browser', 'list'], 8000);
761
+ const r = runBrowserRunner(pw, ['browser', 'list'], 8000);
755
762
  text = (r && (r.stdout || r.stderr)) || '';
756
763
  } catch (e) {
757
- throw new Error(`playwriter browser list failed: ${e.message}`);
764
+ throw new Error(`managed browser session list failed: ${e.message}`);
758
765
  }
759
766
  const lines = text.split(/\r?\n/);
760
767
  const installChromium = lines.map(l => (l.match(/\b(install:Chromium:[A-Za-z0-9_-]+)\b/) || [])[1]).find(Boolean);
761
768
  if (installChromium) return installChromium;
762
- throw new Error(`no managed Chromium install detected; playwriter browser list returned: ${scrubBrowserRunnerText(text).trim()}`);
769
+ throw new Error(`no managed Chromium install detected; browser runner returned: ${scrubBrowserRunnerText(text).trim()}`);
763
770
  }
764
771
 
765
772
  function waitForExtensionReady(pw, profileDir, opts) {
@@ -775,7 +782,7 @@ function waitForExtensionReady(pw, profileDir, opts) {
775
782
  while (Date.now() < deadline) {
776
783
  const remaining = deadline - Date.now();
777
784
  const innerTimeout = Math.max(28000, Math.min(remaining, 30000));
778
- const r = runPlaywriter(pw, ['session', 'new', '--browser', browserKey], innerTimeout);
785
+ const r = runBrowserRunner(pw, ['session', 'new', '--browser', browserKey], innerTimeout);
779
786
  if (r && r.status === 0) return r;
780
787
  lastErr = scrubBrowserRunnerText((r && (r.stderr || r.stdout)) || '');
781
788
  const now = Date.now();
@@ -1461,11 +1468,11 @@ function makeHostFunctions(instanceRef) {
1461
1468
  const body = readWasmStr(instanceRef.value, bodyPtr, bodyLen);
1462
1469
  const cwd = readWasmStr(instanceRef.value, cwdPtr, cwdLen) || process.cwd();
1463
1470
  const sessionId = readWasmStr(instanceRef.value, sidPtr, sidLen) || 'default';
1464
- const pw = findPlaywriter();
1471
+ const pw = findBrowserRunner();
1465
1472
  if (!pw) return writeWasmJson(instanceRef.value, { ok: false, error: 'managed browser session runner not available' });
1466
1473
  if (body.startsWith('session ')) {
1467
1474
  const parts = body.slice(8).trim().split(/\s+/);
1468
- const r = runPlaywriter(pw, ['session', ...parts], 30000);
1475
+ const r = runBrowserRunner(pw, ['session', ...parts], 30000);
1469
1476
  return writeWasmJson(instanceRef.value, {
1470
1477
  ok: r.status === 0,
1471
1478
  stdout: scrubBrowserRunnerText(r.stdout || ''),
@@ -1474,7 +1481,7 @@ function makeHostFunctions(instanceRef) {
1474
1481
  });
1475
1482
  }
1476
1483
  const pwSessionId = getOrCreateBrowserSession(cwd, sessionId, pw);
1477
- const r = runPlaywriter(pw, ['-s', pwSessionId, '--timeout', '14000', '-e', body], 60000);
1484
+ const r = runBrowserRunner(pw, ['-s', pwSessionId, '--timeout', '14000', '-e', body], 60000);
1478
1485
  return writeWasmJson(instanceRef.value, {
1479
1486
  ok: r.status === 0,
1480
1487
  stdout: scrubBrowserRunnerText(r.stdout || ''),
@@ -1659,7 +1666,13 @@ async function runSpoolWatcher(instance, spoolDir) {
1659
1666
  const ageMs = Date.now() - priorBoot.ts;
1660
1667
  const shutdownIsNewer = priorShutdownForAbort && Number.isFinite(priorShutdownForAbort.ts) && priorShutdownForAbort.ts >= priorBoot.ts;
1661
1668
  if (ageMs > 30_000 && !shutdownIsNewer) {
1662
- logEvent('plugkit', 'watcher.silent-abort', {
1669
+ let priorVerbSnap = null;
1670
+ let priorStatusSnap = null;
1671
+ let priorPidAlive = null;
1672
+ try { priorVerbSnap = JSON.parse(fs.readFileSync(VERB_ACTIVE_PATH, 'utf-8')); } catch (_) {}
1673
+ try { priorStatusSnap = JSON.parse(fs.readFileSync(path.join(path.dirname(BOOT_ACTIVE_PATH), '.status.json'), 'utf-8')); } catch (_) {}
1674
+ try { process.kill(priorBoot.pid, 0); priorPidAlive = true; } catch (e) { priorPidAlive = e.code === 'EPERM'; }
1675
+ const forensics = {
1663
1676
  prior_pid: priorBoot.pid,
1664
1677
  prior_ts: priorBoot.ts,
1665
1678
  prior_sha: priorBoot.wrapper_sha || null,
@@ -1668,8 +1681,15 @@ async function runSpoolWatcher(instance, spoolDir) {
1668
1681
  age_ms: ageMs,
1669
1682
  shutdown_reason_present: !!priorShutdownForAbort,
1670
1683
  shutdown_reason_ts: priorShutdownForAbort ? priorShutdownForAbort.ts : null,
1671
- });
1672
- try { console.error(`[plugkit-wasm] SILENT ABORT detected: prior watcher pid=${priorBoot.pid} sha=${priorBoot.wrapper_sha} died without writing .shutdown-reason.json (age=${ageMs}ms)`); } catch (_) {}
1684
+ prior_verb_active: priorVerbSnap,
1685
+ prior_status: priorStatusSnap,
1686
+ prior_pid_alive: priorPidAlive,
1687
+ };
1688
+ logEvent('plugkit', 'watcher.silent-abort', forensics);
1689
+ try {
1690
+ fs.writeFileSync(path.join(path.dirname(BOOT_ACTIVE_PATH), '.silent-abort-forensics.json'), JSON.stringify(forensics, null, 2));
1691
+ } catch (_) {}
1692
+ try { console.error(`[plugkit-wasm] SILENT ABORT detected: prior watcher pid=${priorBoot.pid} sha=${priorBoot.wrapper_sha} died without writing .shutdown-reason.json (age=${ageMs}ms, prior_pid_alive=${priorPidAlive})`); } catch (_) {}
1673
1693
  }
1674
1694
  }
1675
1695
  } catch (_) {}