gm-plugkit 2.0.1303 → 2.0.1305
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 +108 -108
- package/plugkit.sha256 +1 -1
- package/plugkit.version +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-plugkit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1305",
|
|
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
|
@@ -722,105 +722,84 @@ function findInstalledChromiumBinary() {
|
|
|
722
722
|
}
|
|
723
723
|
}
|
|
724
724
|
|
|
725
|
+
function fetchJsonSync(url, timeoutMs) {
|
|
726
|
+
const r = spawnSync(process.execPath, ['-e', `
|
|
727
|
+
const http = require('http');
|
|
728
|
+
const req = http.get(${JSON.stringify(url)}, (res) => {
|
|
729
|
+
let buf = '';
|
|
730
|
+
res.on('data', d => buf += d);
|
|
731
|
+
res.on('end', () => {
|
|
732
|
+
if (res.statusCode !== 200) { process.stderr.write('status ' + res.statusCode); process.exit(2); }
|
|
733
|
+
process.stdout.write(buf);
|
|
734
|
+
});
|
|
735
|
+
});
|
|
736
|
+
req.on('error', e => { process.stderr.write(e.message); process.exit(1); });
|
|
737
|
+
req.setTimeout(${timeoutMs || 1500}, () => { req.destroy(new Error('timeout')); });
|
|
738
|
+
`], { encoding: 'utf-8', timeout: (timeoutMs || 1500) + 1500, windowsHide: true });
|
|
739
|
+
if (r.status !== 0) return null;
|
|
740
|
+
try { return JSON.parse(r.stdout); } catch (_) { return null; }
|
|
741
|
+
}
|
|
742
|
+
|
|
725
743
|
function startManagedBrowser(pw, profileDir) {
|
|
726
744
|
const headless = process.env.GM_BROWSER_HEADLESS === '1';
|
|
727
|
-
|
|
728
|
-
if (
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
}
|
|
745
|
+
let browserBin = findInstalledChromiumBinary();
|
|
746
|
+
if (!browserBin) {
|
|
747
|
+
logEvent('plugkit', 'browser.chromium-installing', {});
|
|
748
|
+
spawnSync(process.platform === 'win32' ? 'npx.cmd' : 'npx', ['--yes', 'playwright', 'install', 'chromium'], {
|
|
749
|
+
encoding: 'utf-8',
|
|
750
|
+
timeout: 300000,
|
|
751
|
+
windowsHide: true,
|
|
752
|
+
shell: process.platform === 'win32',
|
|
753
|
+
stdio: 'ignore',
|
|
754
|
+
});
|
|
755
|
+
browserBin = findInstalledChromiumBinary();
|
|
739
756
|
}
|
|
740
|
-
|
|
757
|
+
if (!browserBin) {
|
|
758
|
+
const err = new Error('chromium binary not found after install attempt');
|
|
759
|
+
logEvent('plugkit', 'browser.launch-failed', { reason: 'chromium-missing' });
|
|
760
|
+
throw err;
|
|
761
|
+
}
|
|
762
|
+
const port = findFreePortSync();
|
|
763
|
+
const args = [
|
|
764
|
+
'--user-data-dir=' + profileDir,
|
|
765
|
+
'--remote-debugging-port=' + port,
|
|
766
|
+
'--remote-debugging-address=127.0.0.1',
|
|
767
|
+
'--no-first-run',
|
|
768
|
+
'--no-default-browser-check',
|
|
769
|
+
'--disable-default-apps',
|
|
770
|
+
];
|
|
771
|
+
if (headless) args.push('--headless=new');
|
|
772
|
+
const chromeLogPath = path.join(profileDir, '.chrome-launch.log');
|
|
773
|
+
let logFd;
|
|
774
|
+
try { logFd = fs.openSync(chromeLogPath, 'a'); } catch (_) { logFd = null; }
|
|
775
|
+
const child = spawn(browserBin, args, {
|
|
741
776
|
detached: true,
|
|
742
|
-
stdio: 'ignore',
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
env,
|
|
746
|
-
...(process.platform === 'win32' ? { creationFlags: 0x08000000 | 0x00000008 } : {}),
|
|
777
|
+
stdio: ['ignore', logFd != null ? logFd : 'ignore', logFd != null ? logFd : 'ignore'],
|
|
778
|
+
windowsHide: false,
|
|
779
|
+
env: process.env,
|
|
747
780
|
});
|
|
781
|
+
try { if (typeof logFd === 'number') fs.closeSync(logFd); } catch (_) {}
|
|
748
782
|
const pid = child.pid;
|
|
749
783
|
child.unref();
|
|
750
|
-
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
function isColdRunProfile(profileDir) {
|
|
754
|
-
try {
|
|
755
|
-
if (!fs.existsSync(profileDir)) return true;
|
|
756
|
-
const entries = fs.readdirSync(profileDir);
|
|
757
|
-
if (entries.length === 0) return true;
|
|
758
|
-
if (!entries.some(n => n === 'Default' || n === 'Local State')) return true;
|
|
759
|
-
return false;
|
|
760
|
-
} catch (_) {
|
|
761
|
-
return true;
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
function resolveManagedBrowserKey(pw) {
|
|
766
|
-
const explicitKey = process.env.GM_BROWSER_RUNNER_KEY || process.env.PLAYWRITER_BROWSER_KEY;
|
|
767
|
-
if (explicitKey) {
|
|
768
|
-
if (/^profile:/.test(explicitKey)) {
|
|
769
|
-
throw new Error(`GM_BROWSER_RUNNER_KEY=${explicitKey} points at a user OS browser profile; refusing — managed sessions must use install:Chromium:*`);
|
|
770
|
-
}
|
|
771
|
-
return explicitKey;
|
|
772
|
-
}
|
|
773
|
-
let text = '';
|
|
774
|
-
try {
|
|
775
|
-
const r = runBrowserRunner(pw, ['browser', 'list'], 8000);
|
|
776
|
-
text = (r && (r.stdout || r.stderr)) || '';
|
|
777
|
-
} catch (e) {
|
|
778
|
-
throw new Error(`managed browser session list failed: ${e.message}`);
|
|
779
|
-
}
|
|
780
|
-
const lines = text.split(/\r?\n/);
|
|
781
|
-
const installChromium = lines.map(l => (l.match(/\b(install:Chromium:[A-Za-z0-9_-]+)\b/) || [])[1]).find(Boolean);
|
|
782
|
-
if (installChromium) return installChromium;
|
|
783
|
-
throw new Error(`no managed Chromium install detected; browser runner returned: ${scrubBrowserRunnerText(text).trim()}`);
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
function waitForExtensionReady(pw, profileDir, opts) {
|
|
787
|
-
const cold = (opts && typeof opts.cold === 'boolean') ? opts.cold : isColdRunProfile(profileDir);
|
|
788
|
-
const timeoutMs = (opts && opts.timeoutMs) || (cold ? 180000 : 30000);
|
|
784
|
+
logEvent('plugkit', 'browser.chromium-launched', { pid, port, profileDir, headless, binary: browserBin, chromeLogPath });
|
|
789
785
|
const start = Date.now();
|
|
790
|
-
const deadline = start +
|
|
791
|
-
|
|
792
|
-
let
|
|
793
|
-
let lastErr = '';
|
|
794
|
-
let lastProgressAt = start;
|
|
795
|
-
const browserKey = resolveManagedBrowserKey(pw);
|
|
786
|
+
const deadline = start + 30000;
|
|
787
|
+
let wsEndpoint = null;
|
|
788
|
+
let lastErr = null;
|
|
796
789
|
while (Date.now() < deadline) {
|
|
797
|
-
const
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
lastErr = scrubBrowserRunnerText((r && (r.stderr || r.stdout)) || '');
|
|
802
|
-
const now = Date.now();
|
|
803
|
-
if (now - lastProgressAt >= 10000) {
|
|
804
|
-
logEvent('plugkit', 'browser.extension-wait', {
|
|
805
|
-
elapsed_ms: now - start,
|
|
806
|
-
cold_run: cold,
|
|
807
|
-
profileDir,
|
|
808
|
-
attempt,
|
|
809
|
-
});
|
|
810
|
-
lastProgressAt = now;
|
|
790
|
+
const info = fetchJsonSync(`http://127.0.0.1:${port}/json/version`, 1500);
|
|
791
|
+
if (info && info.webSocketDebuggerUrl) {
|
|
792
|
+
wsEndpoint = info.webSocketDebuggerUrl;
|
|
793
|
+
break;
|
|
811
794
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
795
|
+
sleepSync(500);
|
|
796
|
+
}
|
|
797
|
+
if (!wsEndpoint) {
|
|
798
|
+
logEvent('plugkit', 'browser.launch-failed', { reason: 'cdp-not-ready', pid, port, elapsed_ms: Date.now() - start });
|
|
799
|
+
throw new Error(`chromium launched (pid=${pid}) but CDP at 127.0.0.1:${port} did not become ready within 30s${lastErr ? ' :: ' + lastErr : ''}`);
|
|
816
800
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
: `warm-run timeout after ${Math.round((Date.now() - start) / 1000)}s waiting for managed browser extension to reconnect (profile exists but extension is not registering; relay server may be wedged)`;
|
|
820
|
-
const err = new Error(`managed browser session start failed: ${flavor}${lastErr ? ` :: ${lastErr}` : ''}`);
|
|
821
|
-
err._lastErr = lastErr;
|
|
822
|
-
err._coldRun = cold;
|
|
823
|
-
throw err;
|
|
801
|
+
logEvent('plugkit', 'browser.cdp-ready', { pid, port, ms: Date.now() - start, wsEndpoint });
|
|
802
|
+
return { pid, port, wsEndpoint };
|
|
824
803
|
}
|
|
825
804
|
|
|
826
805
|
function getOrCreateBrowserSession(cwd, claudeSessionId, pw) {
|
|
@@ -830,15 +809,29 @@ function getOrCreateBrowserSession(cwd, claudeSessionId, pw) {
|
|
|
830
809
|
const ports = readJsonFile(portsFile, {});
|
|
831
810
|
const sessions = readJsonFile(sessionsFile, {});
|
|
832
811
|
const existing = ports[claudeSessionId];
|
|
833
|
-
if (existing && existing.pid) {
|
|
812
|
+
if (existing && existing.pid && existing.wsEndpoint) {
|
|
834
813
|
const wantProfile = path.join(cwd, '.gm', 'browser-profile');
|
|
835
814
|
const pidOk = isProcessAliveSync(existing.pid);
|
|
836
815
|
const profileOk = !existing.profileDir || existing.profileDir === wantProfile || existing.profileDir.startsWith(path.join(cwd, '.gm', 'browser-profile'));
|
|
837
|
-
|
|
816
|
+
const cdpOk = pidOk && !!fetchJsonSync(`http://127.0.0.1:${existing.port}/json/version`, 1000);
|
|
817
|
+
if (pidOk && profileOk && cdpOk) {
|
|
838
818
|
const pwIds = sessions[claudeSessionId] || [];
|
|
839
|
-
if (pwIds.length > 0) return
|
|
819
|
+
if (pwIds.length > 0 && existing.pwSessionId) return existing.pwSessionId;
|
|
820
|
+
const r = runBrowserRunner(pw, ['session', 'new', '--direct', existing.wsEndpoint], 30000);
|
|
821
|
+
if (r && r.status === 0) {
|
|
822
|
+
const sid = parseSessionId(r.stdout || '');
|
|
823
|
+
if (sid) {
|
|
824
|
+
existing.pwSessionId = sid;
|
|
825
|
+
ports[claudeSessionId] = existing;
|
|
826
|
+
sessions[claudeSessionId] = [sid];
|
|
827
|
+
writeJsonFile(portsFile, ports);
|
|
828
|
+
writeJsonFile(sessionsFile, sessions);
|
|
829
|
+
logEvent('plugkit', 'browser.attached', { pwSessionId: sid, reused: true });
|
|
830
|
+
return sid;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
840
833
|
} else {
|
|
841
|
-
const reason = !pidOk ? 'pid-dead' : 'profile-drift';
|
|
834
|
+
const reason = !pidOk ? 'pid-dead' : (!cdpOk ? 'cdp-dead' : 'profile-drift');
|
|
842
835
|
logEvent('hook', 'deviation.browser-profile-collision', {
|
|
843
836
|
sid: claudeSessionId,
|
|
844
837
|
stale_pid: existing.pid || null,
|
|
@@ -853,32 +846,39 @@ function getOrCreateBrowserSession(cwd, claudeSessionId, pw) {
|
|
|
853
846
|
}
|
|
854
847
|
}
|
|
855
848
|
cleanDeadProfileFragments(cwd);
|
|
856
|
-
const probedProfile = path.join(cwd, '.gm', 'browser-profile');
|
|
857
|
-
const coldRun = isColdRunProfile(probedProfile);
|
|
858
849
|
const profileDir = acquireProfileDir(cwd);
|
|
859
|
-
logEvent('plugkit', 'browser.start', { profileDir
|
|
860
|
-
const browserPid = startManagedBrowser(pw, profileDir);
|
|
861
|
-
const
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
if (created) pwSessionId = created[1];
|
|
867
|
-
if (!pwSessionId) {
|
|
868
|
-
const hex = out.match(/\b([a-f0-9-]{8,})\b/i);
|
|
869
|
-
if (hex) pwSessionId = hex[1];
|
|
850
|
+
logEvent('plugkit', 'browser.start', { profileDir });
|
|
851
|
+
const { pid: browserPid, port, wsEndpoint } = startManagedBrowser(pw, profileDir);
|
|
852
|
+
const r = runBrowserRunner(pw, ['session', 'new', '--direct', wsEndpoint], 30000);
|
|
853
|
+
if (!r || r.status !== 0) {
|
|
854
|
+
const errTxt = scrubBrowserRunnerText((r && (r.stderr || r.stdout)) || 'unknown');
|
|
855
|
+
logEvent('plugkit', 'browser.launch-failed', { reason: 'session-attach-failed', pid: browserPid, port, error: errTxt });
|
|
856
|
+
throw new Error(`playwriter session new --direct failed: ${errTxt}`);
|
|
870
857
|
}
|
|
858
|
+
const pwSessionId = parseSessionId(r.stdout || '');
|
|
871
859
|
if (!pwSessionId) {
|
|
872
|
-
|
|
860
|
+
logEvent('plugkit', 'browser.launch-failed', { reason: 'session-id-unparseable', stdout: r.stdout });
|
|
861
|
+
throw new Error(`could not parse managed browser session id from: ${scrubBrowserRunnerText(r.stdout || '')}`);
|
|
873
862
|
}
|
|
874
|
-
|
|
875
|
-
ports[claudeSessionId] = { profileDir, pid: browserPid };
|
|
863
|
+
ports[claudeSessionId] = { profileDir, pid: browserPid, port, wsEndpoint, pwSessionId };
|
|
876
864
|
sessions[claudeSessionId] = [pwSessionId];
|
|
877
865
|
writeJsonFile(portsFile, ports);
|
|
878
866
|
writeJsonFile(sessionsFile, sessions);
|
|
867
|
+
logEvent('plugkit', 'browser.attached', { pwSessionId, pid: browserPid, port });
|
|
879
868
|
return pwSessionId;
|
|
880
869
|
}
|
|
881
870
|
|
|
871
|
+
function parseSessionId(rawOut) {
|
|
872
|
+
const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
873
|
+
const out = stripAnsi(rawOut || '').trim();
|
|
874
|
+
const created = out.match(/Session\s+(\S+)\s+created/i);
|
|
875
|
+
if (created) return created[1];
|
|
876
|
+
const hex = out.match(/\b([a-f0-9-]{8,})\b/i);
|
|
877
|
+
if (hex) return hex[1];
|
|
878
|
+
try { const j = JSON.parse(out); return j.id || j.session_id || j.session || null; } catch (_) {}
|
|
879
|
+
return null;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
882
|
const VEC_K_DEFAULT = 10;
|
|
883
883
|
const EMBED_MODEL_DEFAULT = process.env.EMBED_MODEL || 'mistral/mistral-embed';
|
|
884
884
|
const INFERENCE_MODEL_DEFAULT = process.env.INFERENCE_MODEL || 'groq/llama-3.3-70b-versatile';
|
package/plugkit.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"plugkit.wasm":"
|
|
1
|
+
{"plugkit.wasm":"7f24800faa40a43e615a98669dedfc7be0a2650dcb37e63dafb68e1f39c500a3"}
|
package/plugkit.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.485
|