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 +1 -1
- package/plugkit-wasm-wrapper.js +49 -29
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-plugkit",
|
|
3
|
-
"version": "2.0.
|
|
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": {
|
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');
|
|
@@ -705,11 +709,14 @@ function findInstalledChromiumBinary() {
|
|
|
705
709
|
}
|
|
706
710
|
|
|
707
711
|
function startManagedBrowser(pw, profileDir) {
|
|
708
|
-
const
|
|
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
|
-
|
|
746
|
-
|
|
747
|
-
if (/^profile:/.test(
|
|
748
|
-
throw new Error(`
|
|
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
|
|
757
|
+
return explicitKey;
|
|
751
758
|
}
|
|
752
759
|
let text = '';
|
|
753
760
|
try {
|
|
754
|
-
const r =
|
|
761
|
+
const r = runBrowserRunner(pw, ['browser', 'list'], 8000);
|
|
755
762
|
text = (r && (r.stdout || r.stderr)) || '';
|
|
756
763
|
} catch (e) {
|
|
757
|
-
throw new Error(`
|
|
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;
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 (_) {}
|