gm-plugkit 2.0.1503 → 2.0.1504
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 +71 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gm-plugkit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1504",
|
|
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
|
@@ -563,6 +563,21 @@ function browserStateDir(cwd) {
|
|
|
563
563
|
function browserPortsFile(cwd) { return path.join(browserStateDir(cwd), 'browser-ports.json'); }
|
|
564
564
|
function browserSessionsFile(cwd) { return path.join(browserStateDir(cwd), 'browser-sessions.json'); }
|
|
565
565
|
|
|
566
|
+
const { selectIdleBrowserSessions } = require('./browser-idle.js');
|
|
567
|
+
|
|
568
|
+
function stampBrowserLastUse(cwd, claudeSessionId) {
|
|
569
|
+
try {
|
|
570
|
+
const portsFile = browserPortsFile(cwd);
|
|
571
|
+
const ports = readJsonFile(portsFile, {});
|
|
572
|
+
const entry = ports[claudeSessionId];
|
|
573
|
+
if (entry && typeof entry === 'object') {
|
|
574
|
+
entry.lastUse = Date.now();
|
|
575
|
+
ports[claudeSessionId] = entry;
|
|
576
|
+
writeJsonFile(portsFile, ports);
|
|
577
|
+
}
|
|
578
|
+
} catch (_) {}
|
|
579
|
+
}
|
|
580
|
+
|
|
566
581
|
function atomicWriteJson(filePath, obj) {
|
|
567
582
|
const tmp = filePath + '.tmp.' + process.pid + '.' + Date.now() + '.' + Math.random().toString(36).slice(2, 8);
|
|
568
583
|
fs.writeFileSync(tmp, JSON.stringify(obj, null, 2));
|
|
@@ -1020,6 +1035,7 @@ function getOrCreateBrowserSession(cwd, claudeSessionId, pw) {
|
|
|
1020
1035
|
const sid = parseSessionId(r.stdout || '');
|
|
1021
1036
|
if (sid) {
|
|
1022
1037
|
existing.pwSessionId = sid;
|
|
1038
|
+
existing.lastUse = Date.now();
|
|
1023
1039
|
ports[claudeSessionId] = existing;
|
|
1024
1040
|
sessions[claudeSessionId] = [sid];
|
|
1025
1041
|
writeJsonFile(portsFile, ports);
|
|
@@ -1092,7 +1108,7 @@ function getOrCreateBrowserSession(cwd, claudeSessionId, pw) {
|
|
|
1092
1108
|
logEvent('plugkit', 'browser.launch-failed', { reason: 'session-id-unparseable', stdout: r.stdout });
|
|
1093
1109
|
throw new Error(`could not parse managed browser session id from: ${scrubBrowserRunnerText(r.stdout || '')}`);
|
|
1094
1110
|
}
|
|
1095
|
-
ports[claudeSessionId] = { profileDir, pid: browserPid, port, wsEndpoint, pwSessionId };
|
|
1111
|
+
ports[claudeSessionId] = { profileDir, pid: browserPid, port, wsEndpoint, pwSessionId, lastUse: Date.now() };
|
|
1096
1112
|
sessions[claudeSessionId] = [pwSessionId];
|
|
1097
1113
|
writeJsonFile(portsFile, ports);
|
|
1098
1114
|
writeJsonFile(sessionsFile, sessions);
|
|
@@ -1833,7 +1849,7 @@ function makeHostFunctions(instanceRef) {
|
|
|
1833
1849
|
|
|
1834
1850
|
if (trimmed === 'session new' || trimmed === '') {
|
|
1835
1851
|
const pwSessionId = getOrCreateBrowserSession(cwd, sessionId, pw);
|
|
1836
|
-
|
|
1852
|
+
stampBrowserLastUse(cwd, sessionId);
|
|
1837
1853
|
return writeWasmJson(instanceRef.value, {
|
|
1838
1854
|
ok: true,
|
|
1839
1855
|
stdout: `Session ${pwSessionId} attached to locally-profiled chromium at ${path.join(cwd, '.gm', 'browser-profile')}`,
|
|
@@ -1845,12 +1861,26 @@ function makeHostFunctions(instanceRef) {
|
|
|
1845
1861
|
|
|
1846
1862
|
if (trimmed.startsWith('session ')) {
|
|
1847
1863
|
const parts = trimmed.slice(8).trim().split(/\s+/);
|
|
1848
|
-
// playwriter's actual session subcommands are list|new|delete|reset.
|
|
1849
|
-
// Map the legacy close/kill aliases (recognized here historically) to delete,
|
|
1850
|
-
// and pass list/new/delete/reset through verbatim. Anything else is rejected
|
|
1851
|
-
// by playwriter with its own usage text, which is the correct surface.
|
|
1852
1864
|
if (parts[0] === 'close' || parts[0] === 'kill') parts[0] = 'delete';
|
|
1853
1865
|
const r = runBrowserRunner(pw, ['session', ...parts], 30000, cwd, sessionId);
|
|
1866
|
+
if (r.status === 0 && (parts[0] === 'delete' || parts[0] === 'reset')) {
|
|
1867
|
+
try {
|
|
1868
|
+
const portsFile = browserPortsFile(cwd);
|
|
1869
|
+
const sessionsFile = browserSessionsFile(cwd);
|
|
1870
|
+
const ports = readJsonFile(portsFile, {});
|
|
1871
|
+
const sessions = readJsonFile(sessionsFile, {});
|
|
1872
|
+
const entry = ports[sessionId];
|
|
1873
|
+
if (entry && typeof entry === 'object') {
|
|
1874
|
+
if (Number.isFinite(entry.pid) && isProcessAliveSync(entry.pid)) {
|
|
1875
|
+
gracefulCloseBrowser(entry, `session-${parts[0]}`);
|
|
1876
|
+
}
|
|
1877
|
+
delete ports[sessionId];
|
|
1878
|
+
delete sessions[sessionId];
|
|
1879
|
+
writeJsonFile(portsFile, ports);
|
|
1880
|
+
writeJsonFile(sessionsFile, sessions);
|
|
1881
|
+
}
|
|
1882
|
+
} catch (_) {}
|
|
1883
|
+
}
|
|
1854
1884
|
return writeWasmJson(instanceRef.value, {
|
|
1855
1885
|
ok: r.status === 0,
|
|
1856
1886
|
stdout: scrubBrowserRunnerText(r.stdout || ''),
|
|
@@ -1860,7 +1890,7 @@ function makeHostFunctions(instanceRef) {
|
|
|
1860
1890
|
}
|
|
1861
1891
|
|
|
1862
1892
|
const pwSessionId = getOrCreateBrowserSession(cwd, sessionId, pw);
|
|
1863
|
-
|
|
1893
|
+
stampBrowserLastUse(cwd, sessionId);
|
|
1864
1894
|
let evalBody = body;
|
|
1865
1895
|
let timeoutMs = 14000;
|
|
1866
1896
|
const timeoutMatch = body.match(/^timeout=(\d+)\s*\n([\s\S]*)$/);
|
|
@@ -2538,27 +2568,49 @@ async function runSpoolWatcher(instance, spoolDir) {
|
|
|
2538
2568
|
}
|
|
2539
2569
|
}, 60_000);
|
|
2540
2570
|
|
|
2541
|
-
const BROWSER_IDLE_LIMIT_MS = parseInt(process.env.PLUGKIT_BROWSER_IDLE_LIMIT_MS, 10) ||
|
|
2542
|
-
let lastBrowserActivityMs = Date.now();
|
|
2571
|
+
const BROWSER_IDLE_LIMIT_MS = parseInt(process.env.PLUGKIT_BROWSER_IDLE_LIMIT_MS, 10) || 10 * 60 * 1000;
|
|
2543
2572
|
setInterval(() => {
|
|
2544
2573
|
try {
|
|
2545
|
-
const browserIdleMs = Date.now() - lastBrowserActivityMs;
|
|
2546
|
-
if (browserIdleMs < BROWSER_IDLE_LIMIT_MS) return;
|
|
2547
2574
|
const portsFile = browserPortsFile(process.cwd());
|
|
2548
2575
|
const sessionsFile = browserSessionsFile(process.cwd());
|
|
2549
2576
|
const ports = readJsonFile(portsFile, {});
|
|
2550
|
-
|
|
2577
|
+
const sessions = readJsonFile(sessionsFile, {});
|
|
2578
|
+
const now = Date.now();
|
|
2579
|
+
const idle = selectIdleBrowserSessions(ports, now, BROWSER_IDLE_LIMIT_MS);
|
|
2580
|
+
const idleSids = new Set(idle.map((x) => x.sid));
|
|
2581
|
+
let mutated = false;
|
|
2582
|
+
for (const { sid, entry, idleMs } of idle) {
|
|
2583
|
+
if (Number.isFinite(entry.pid) && isProcessAliveSync(entry.pid)) {
|
|
2584
|
+
try { gracefulCloseBrowser(entry, 'browser-idle'); } catch (_) {}
|
|
2585
|
+
}
|
|
2586
|
+
delete ports[sid];
|
|
2587
|
+
delete sessions[sid];
|
|
2588
|
+
mutated = true;
|
|
2589
|
+
logEvent('plugkit', 'browser.idle-closed', { sid, pid: entry.pid || null, idle_ms: idleMs });
|
|
2590
|
+
}
|
|
2551
2591
|
for (const [sid, entry] of Object.entries(ports)) {
|
|
2552
|
-
if (entry
|
|
2553
|
-
|
|
2592
|
+
if (idleSids.has(sid) || !entry || typeof entry !== 'object') continue;
|
|
2593
|
+
const pidAlive = Number.isFinite(entry.pid) && isProcessAliveSync(entry.pid);
|
|
2594
|
+
if (!pidAlive) {
|
|
2595
|
+
delete ports[sid];
|
|
2596
|
+
delete sessions[sid];
|
|
2597
|
+
mutated = true;
|
|
2598
|
+
logEvent('plugkit', 'browser.stale-reclaimed', { sid, pid: entry.pid || null, reason: 'pid-dead' });
|
|
2599
|
+
continue;
|
|
2600
|
+
}
|
|
2601
|
+
const cdpOk = !!fetchJsonSync(`http://127.0.0.1:${entry.port}/json/version`, 1000);
|
|
2602
|
+
if (!cdpOk) {
|
|
2603
|
+
try { gracefulCloseBrowser(entry, 'orphan-cdp-dead'); } catch (_) {}
|
|
2604
|
+
delete ports[sid];
|
|
2605
|
+
delete sessions[sid];
|
|
2606
|
+
mutated = true;
|
|
2607
|
+
logEvent('plugkit', 'browser.stale-reclaimed', { sid, pid: entry.pid || null, reason: 'cdp-dead' });
|
|
2554
2608
|
}
|
|
2555
2609
|
}
|
|
2556
|
-
if (
|
|
2557
|
-
try {
|
|
2558
|
-
try {
|
|
2559
|
-
logEvent('plugkit', 'browser.idle-closed', { count: closed, idle_ms: browserIdleMs });
|
|
2610
|
+
if (mutated) {
|
|
2611
|
+
try { writeJsonFile(portsFile, ports); } catch (_) {}
|
|
2612
|
+
try { writeJsonFile(sessionsFile, sessions); } catch (_) {}
|
|
2560
2613
|
}
|
|
2561
|
-
lastBrowserActivityMs = Date.now();
|
|
2562
2614
|
} catch (e) {
|
|
2563
2615
|
console.error(`[browser-idle] error: ${e.message}`);
|
|
2564
2616
|
}
|