gm-plugkit 2.0.1290 → 2.0.1292
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 +3 -9
- package/package.json +1 -1
- package/plugkit-wasm-wrapper.js +46 -28
package/bootstrap.js
CHANGED
|
@@ -756,7 +756,7 @@ function startSpoolDaemon() {
|
|
|
756
756
|
if (!fs.existsSync(wrapper)) {
|
|
757
757
|
return { ok: false, error: `wrapper not at ${wrapper} — ensureReady() must run first` };
|
|
758
758
|
}
|
|
759
|
-
const runtime = process.
|
|
759
|
+
const runtime = process.execPath;
|
|
760
760
|
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
761
761
|
const spoolDir = path.join(projectDir, '.gm', 'exec-spool');
|
|
762
762
|
fs.mkdirSync(spoolDir, { recursive: true });
|
|
@@ -772,14 +772,8 @@ function startSpoolDaemon() {
|
|
|
772
772
|
|
|
773
773
|
const supervisor = path.join(__dirname, 'supervisor.js');
|
|
774
774
|
if (process.env.PLUGKIT_SKIP_SUPERVISOR === '1' || !fs.existsSync(supervisor)) {
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
try {
|
|
778
|
-
require('child_process').execFileSync(runtime, ['--version'], { stdio: 'ignore' });
|
|
779
|
-
} catch (_) {
|
|
780
|
-
cmd = process.execPath;
|
|
781
|
-
args = [wrapper, 'spool'];
|
|
782
|
-
}
|
|
775
|
+
const cmd = runtime;
|
|
776
|
+
const args = [wrapper, 'spool'];
|
|
783
777
|
const logFd = fs.openSync(logPath, 'a');
|
|
784
778
|
try { fs.writeSync(logFd, `\n--- daemon spawn ${new Date().toISOString()} parent=${process.pid} (no supervisor) ---\n`); } catch (_) {}
|
|
785
779
|
const child = require('child_process').spawn(cmd, args, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-plugkit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1292",
|
|
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": {
|
package/plugkit-wasm-wrapper.js
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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, [
|
|
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',
|
|
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',
|
|
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
|
|
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
|
-
|
|
668
|
-
|
|
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,
|
|
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',
|
|
678
|
-
roots.push(path.join(home, 'Library', 'Caches',
|
|
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');
|
|
@@ -707,9 +711,10 @@ function findInstalledChromiumBinary() {
|
|
|
707
711
|
function startManagedBrowser(pw, profileDir) {
|
|
708
712
|
const args = [...pw.baseArgs, 'browser', 'start', '--user-data-dir', profileDir, '--headless'];
|
|
709
713
|
const env = { ...process.env };
|
|
710
|
-
if (!env.PLAYWRITER_BROWSER_PATH) {
|
|
714
|
+
if (!env.GM_BROWSER_RUNNER_PATH && !env.PLAYWRITER_BROWSER_PATH) {
|
|
711
715
|
const browserBin = findInstalledChromiumBinary();
|
|
712
716
|
if (browserBin) {
|
|
717
|
+
env.GM_BROWSER_RUNNER_PATH = browserBin;
|
|
713
718
|
env.PLAYWRITER_BROWSER_PATH = browserBin;
|
|
714
719
|
logEvent('plugkit', 'browser.binary-resolved', { path: browserBin });
|
|
715
720
|
} else {
|
|
@@ -742,24 +747,24 @@ function isColdRunProfile(profileDir) {
|
|
|
742
747
|
}
|
|
743
748
|
|
|
744
749
|
function resolveManagedBrowserKey(pw) {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
if (/^profile:/.test(
|
|
748
|
-
throw new Error(`
|
|
750
|
+
const explicitKey = process.env.GM_BROWSER_RUNNER_KEY || process.env.PLAYWRITER_BROWSER_KEY;
|
|
751
|
+
if (explicitKey) {
|
|
752
|
+
if (/^profile:/.test(explicitKey)) {
|
|
753
|
+
throw new Error(`GM_BROWSER_RUNNER_KEY=${explicitKey} points at a user OS browser profile; refusing — managed sessions must use install:Chromium:*`);
|
|
749
754
|
}
|
|
750
|
-
return
|
|
755
|
+
return explicitKey;
|
|
751
756
|
}
|
|
752
757
|
let text = '';
|
|
753
758
|
try {
|
|
754
|
-
const r =
|
|
759
|
+
const r = runBrowserRunner(pw, ['browser', 'list'], 8000);
|
|
755
760
|
text = (r && (r.stdout || r.stderr)) || '';
|
|
756
761
|
} catch (e) {
|
|
757
|
-
throw new Error(`
|
|
762
|
+
throw new Error(`managed browser session list failed: ${e.message}`);
|
|
758
763
|
}
|
|
759
764
|
const lines = text.split(/\r?\n/);
|
|
760
765
|
const installChromium = lines.map(l => (l.match(/\b(install:Chromium:[A-Za-z0-9_-]+)\b/) || [])[1]).find(Boolean);
|
|
761
766
|
if (installChromium) return installChromium;
|
|
762
|
-
throw new Error(`no managed Chromium install detected;
|
|
767
|
+
throw new Error(`no managed Chromium install detected; browser runner returned: ${scrubBrowserRunnerText(text).trim()}`);
|
|
763
768
|
}
|
|
764
769
|
|
|
765
770
|
function waitForExtensionReady(pw, profileDir, opts) {
|
|
@@ -775,7 +780,7 @@ function waitForExtensionReady(pw, profileDir, opts) {
|
|
|
775
780
|
while (Date.now() < deadline) {
|
|
776
781
|
const remaining = deadline - Date.now();
|
|
777
782
|
const innerTimeout = Math.max(28000, Math.min(remaining, 30000));
|
|
778
|
-
const r =
|
|
783
|
+
const r = runBrowserRunner(pw, ['session', 'new', '--browser', browserKey], innerTimeout);
|
|
779
784
|
if (r && r.status === 0) return r;
|
|
780
785
|
lastErr = scrubBrowserRunnerText((r && (r.stderr || r.stdout)) || '');
|
|
781
786
|
const now = Date.now();
|
|
@@ -1461,11 +1466,11 @@ function makeHostFunctions(instanceRef) {
|
|
|
1461
1466
|
const body = readWasmStr(instanceRef.value, bodyPtr, bodyLen);
|
|
1462
1467
|
const cwd = readWasmStr(instanceRef.value, cwdPtr, cwdLen) || process.cwd();
|
|
1463
1468
|
const sessionId = readWasmStr(instanceRef.value, sidPtr, sidLen) || 'default';
|
|
1464
|
-
const pw =
|
|
1469
|
+
const pw = findBrowserRunner();
|
|
1465
1470
|
if (!pw) return writeWasmJson(instanceRef.value, { ok: false, error: 'managed browser session runner not available' });
|
|
1466
1471
|
if (body.startsWith('session ')) {
|
|
1467
1472
|
const parts = body.slice(8).trim().split(/\s+/);
|
|
1468
|
-
const r =
|
|
1473
|
+
const r = runBrowserRunner(pw, ['session', ...parts], 30000);
|
|
1469
1474
|
return writeWasmJson(instanceRef.value, {
|
|
1470
1475
|
ok: r.status === 0,
|
|
1471
1476
|
stdout: scrubBrowserRunnerText(r.stdout || ''),
|
|
@@ -1474,7 +1479,7 @@ function makeHostFunctions(instanceRef) {
|
|
|
1474
1479
|
});
|
|
1475
1480
|
}
|
|
1476
1481
|
const pwSessionId = getOrCreateBrowserSession(cwd, sessionId, pw);
|
|
1477
|
-
const r =
|
|
1482
|
+
const r = runBrowserRunner(pw, ['-s', pwSessionId, '--timeout', '14000', '-e', body], 60000);
|
|
1478
1483
|
return writeWasmJson(instanceRef.value, {
|
|
1479
1484
|
ok: r.status === 0,
|
|
1480
1485
|
stdout: scrubBrowserRunnerText(r.stdout || ''),
|
|
@@ -1659,7 +1664,13 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
1659
1664
|
const ageMs = Date.now() - priorBoot.ts;
|
|
1660
1665
|
const shutdownIsNewer = priorShutdownForAbort && Number.isFinite(priorShutdownForAbort.ts) && priorShutdownForAbort.ts >= priorBoot.ts;
|
|
1661
1666
|
if (ageMs > 30_000 && !shutdownIsNewer) {
|
|
1662
|
-
|
|
1667
|
+
let priorVerbSnap = null;
|
|
1668
|
+
let priorStatusSnap = null;
|
|
1669
|
+
let priorPidAlive = null;
|
|
1670
|
+
try { priorVerbSnap = JSON.parse(fs.readFileSync(VERB_ACTIVE_PATH, 'utf-8')); } catch (_) {}
|
|
1671
|
+
try { priorStatusSnap = JSON.parse(fs.readFileSync(path.join(path.dirname(BOOT_ACTIVE_PATH), '.status.json'), 'utf-8')); } catch (_) {}
|
|
1672
|
+
try { process.kill(priorBoot.pid, 0); priorPidAlive = true; } catch (e) { priorPidAlive = e.code === 'EPERM'; }
|
|
1673
|
+
const forensics = {
|
|
1663
1674
|
prior_pid: priorBoot.pid,
|
|
1664
1675
|
prior_ts: priorBoot.ts,
|
|
1665
1676
|
prior_sha: priorBoot.wrapper_sha || null,
|
|
@@ -1668,8 +1679,15 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
1668
1679
|
age_ms: ageMs,
|
|
1669
1680
|
shutdown_reason_present: !!priorShutdownForAbort,
|
|
1670
1681
|
shutdown_reason_ts: priorShutdownForAbort ? priorShutdownForAbort.ts : null,
|
|
1671
|
-
|
|
1672
|
-
|
|
1682
|
+
prior_verb_active: priorVerbSnap,
|
|
1683
|
+
prior_status: priorStatusSnap,
|
|
1684
|
+
prior_pid_alive: priorPidAlive,
|
|
1685
|
+
};
|
|
1686
|
+
logEvent('plugkit', 'watcher.silent-abort', forensics);
|
|
1687
|
+
try {
|
|
1688
|
+
fs.writeFileSync(path.join(path.dirname(BOOT_ACTIVE_PATH), '.silent-abort-forensics.json'), JSON.stringify(forensics, null, 2));
|
|
1689
|
+
} catch (_) {}
|
|
1690
|
+
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
1691
|
}
|
|
1674
1692
|
}
|
|
1675
1693
|
} catch (_) {}
|