gm-plugkit 2.0.1687 → 2.0.1689
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 +94 -40
- 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.1689",
|
|
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
|
@@ -1168,11 +1168,14 @@ function closeExtraBlankTabs(port, keepWsEndpoint) {
|
|
|
1168
1168
|
try {
|
|
1169
1169
|
const targets = fetchJsonSync(`http://127.0.0.1:${port}/json/list`, 1500);
|
|
1170
1170
|
if (!Array.isArray(targets)) return { closed: 0 };
|
|
1171
|
+
const pages = targets.filter(t => t && t.type === 'page');
|
|
1172
|
+
const blank = pages.filter(t => t.url === 'about:blank' || t.url === '');
|
|
1173
|
+
const nonBlankCount = pages.length - blank.length;
|
|
1174
|
+
const keepCount = nonBlankCount > 0 ? 0 : 1;
|
|
1175
|
+
const toClose = blank.slice(0, Math.max(0, blank.length - keepCount));
|
|
1171
1176
|
let closed = 0;
|
|
1172
|
-
for (const t of
|
|
1173
|
-
if (!t || t.type !== 'page') continue;
|
|
1177
|
+
for (const t of toClose) {
|
|
1174
1178
|
if (t.webSocketDebuggerUrl === keepWsEndpoint) continue;
|
|
1175
|
-
if (t.url !== 'about:blank' && t.url !== '') continue;
|
|
1176
1179
|
const r = spawnSync(process.execPath, ['-e', `
|
|
1177
1180
|
const http = require('http');
|
|
1178
1181
|
const req = http.get(${JSON.stringify(`http://127.0.0.1:${port}/json/close/${t.id}`)}, res => { res.resume(); res.on('end', () => process.exit(0)); });
|
|
@@ -1187,26 +1190,15 @@ function closeExtraBlankTabs(port, keepWsEndpoint) {
|
|
|
1187
1190
|
}
|
|
1188
1191
|
}
|
|
1189
1192
|
|
|
1190
|
-
function
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
spawnSync(process.platform === 'win32' ? 'npx.cmd' : 'npx', ['--yes', 'playwright', 'install', 'chromium'], {
|
|
1196
|
-
encoding: 'utf-8',
|
|
1197
|
-
timeout: 300000,
|
|
1198
|
-
windowsHide: true,
|
|
1199
|
-
shell: process.platform === 'win32',
|
|
1200
|
-
stdio: 'ignore',
|
|
1201
|
-
});
|
|
1202
|
-
browserBin = findInstalledChromiumBinary();
|
|
1203
|
-
}
|
|
1204
|
-
if (!browserBin) {
|
|
1205
|
-
const err = new Error('chromium binary not found after install attempt');
|
|
1206
|
-
logEvent('plugkit', 'browser.launch-failed', { reason: 'chromium-missing' });
|
|
1207
|
-
throw err;
|
|
1193
|
+
function chromeLogHasSandboxDenied(chromeLogPath) {
|
|
1194
|
+
try {
|
|
1195
|
+
return /Sandbox cannot access executable/.test(fs.readFileSync(chromeLogPath, 'utf-8'));
|
|
1196
|
+
} catch (_) {
|
|
1197
|
+
return false;
|
|
1208
1198
|
}
|
|
1209
|
-
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
function spawnChromiumOnce(browserBin, profileDir, port, headless, noSandbox) {
|
|
1210
1202
|
const args = [
|
|
1211
1203
|
'--user-data-dir=' + profileDir,
|
|
1212
1204
|
'--remote-debugging-port=' + port,
|
|
@@ -1216,7 +1208,7 @@ function startManagedBrowser(pw, profileDir) {
|
|
|
1216
1208
|
'--disable-default-apps',
|
|
1217
1209
|
'--disable-gpu-process-crash-limit',
|
|
1218
1210
|
];
|
|
1219
|
-
if (
|
|
1211
|
+
if (noSandbox) {
|
|
1220
1212
|
args.push('--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage');
|
|
1221
1213
|
}
|
|
1222
1214
|
if (headless) {
|
|
@@ -1234,27 +1226,70 @@ function startManagedBrowser(pw, profileDir) {
|
|
|
1234
1226
|
env: process.env,
|
|
1235
1227
|
});
|
|
1236
1228
|
try { if (typeof logFd === 'number') fs.closeSync(logFd); } catch (_) {}
|
|
1237
|
-
const pid = child.pid;
|
|
1238
1229
|
child.unref();
|
|
1239
|
-
|
|
1230
|
+
return { pid: child.pid, chromeLogPath };
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
function waitForCdpReady(port, deadlineMs) {
|
|
1240
1234
|
const start = Date.now();
|
|
1241
|
-
const deadline = start +
|
|
1242
|
-
let wsEndpoint = null;
|
|
1243
|
-
let lastErr = null;
|
|
1235
|
+
const deadline = start + deadlineMs;
|
|
1244
1236
|
while (Date.now() < deadline) {
|
|
1245
1237
|
const info = fetchJsonSync(`http://127.0.0.1:${port}/json/version`, 1500);
|
|
1246
|
-
if (info && info.webSocketDebuggerUrl) {
|
|
1247
|
-
wsEndpoint = info.webSocketDebuggerUrl;
|
|
1248
|
-
break;
|
|
1249
|
-
}
|
|
1238
|
+
if (info && info.webSocketDebuggerUrl) return { wsEndpoint: info.webSocketDebuggerUrl, ms: Date.now() - start };
|
|
1250
1239
|
sleepSync(500);
|
|
1251
1240
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1241
|
+
return null;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
function startManagedBrowser(pw, profileDir) {
|
|
1245
|
+
const headless = process.env.GM_BROWSER_HEADLESS === '1';
|
|
1246
|
+
logEvent('plugkit', 'browser.headless-mode-resolved', { headless, source: headless ? 'GM_BROWSER_HEADLESS=1' : 'default-headful' });
|
|
1247
|
+
let browserBin = findInstalledChromiumBinary();
|
|
1248
|
+
if (!browserBin) {
|
|
1249
|
+
logEvent('plugkit', 'browser.chromium-installing', {});
|
|
1250
|
+
spawnSync(process.platform === 'win32' ? 'npx.cmd' : 'npx', ['--yes', 'playwright', 'install', 'chromium'], {
|
|
1251
|
+
encoding: 'utf-8',
|
|
1252
|
+
timeout: 300000,
|
|
1253
|
+
windowsHide: true,
|
|
1254
|
+
shell: process.platform === 'win32',
|
|
1255
|
+
stdio: 'ignore',
|
|
1256
|
+
});
|
|
1257
|
+
browserBin = findInstalledChromiumBinary();
|
|
1258
|
+
}
|
|
1259
|
+
if (!browserBin) {
|
|
1260
|
+
const err = new Error('chromium binary not found after install attempt');
|
|
1261
|
+
logEvent('plugkit', 'browser.launch-failed', { reason: 'chromium-missing' });
|
|
1262
|
+
throw err;
|
|
1263
|
+
}
|
|
1264
|
+
const port = findFreePortSync();
|
|
1265
|
+
let noSandbox = process.env.GM_BROWSER_NO_SANDBOX === '1';
|
|
1266
|
+
let { pid, chromeLogPath } = spawnChromiumOnce(browserBin, profileDir, port, headless, noSandbox);
|
|
1267
|
+
logEvent('plugkit', 'browser.chromium-launched', { pid, port, profileDir, headless, noSandbox, binary: browserBin, chromeLogPath });
|
|
1268
|
+
let ready = waitForCdpReady(port, 30000);
|
|
1269
|
+
if (!ready) {
|
|
1270
|
+
logEvent('plugkit', 'browser.launch-failed', { reason: 'cdp-not-ready', pid, port });
|
|
1271
|
+
throw new Error(`chromium launched (pid=${pid}) but CDP at 127.0.0.1:${port} did not become ready within 30s`);
|
|
1272
|
+
}
|
|
1273
|
+
if (!noSandbox && chromeLogHasSandboxDenied(chromeLogPath)) {
|
|
1274
|
+
logEvent('plugkit', 'browser.sandbox-fallback-engaged', { pid, port, profileDir, reason: 'sandbox-access-denied-detected-in-chrome-launch-log' });
|
|
1275
|
+
try { process.kill(pid, 'SIGTERM'); } catch (_) {}
|
|
1276
|
+
sleepSyncMs(500);
|
|
1277
|
+
try { killPidQuiet(pid); } catch (_) {}
|
|
1278
|
+
purgeProfileLockFiles(profileDir);
|
|
1279
|
+
noSandbox = true;
|
|
1280
|
+
const port2 = findFreePortSync();
|
|
1281
|
+
({ pid, chromeLogPath } = spawnChromiumOnce(browserBin, profileDir, port2, headless, noSandbox));
|
|
1282
|
+
logEvent('plugkit', 'browser.chromium-launched', { pid, port: port2, profileDir, headless, noSandbox, binary: browserBin, chromeLogPath, retry: true });
|
|
1283
|
+
ready = waitForCdpReady(port2, 30000);
|
|
1284
|
+
if (!ready) {
|
|
1285
|
+
logEvent('plugkit', 'browser.launch-failed', { reason: 'cdp-not-ready-after-sandbox-fallback', pid, port: port2 });
|
|
1286
|
+
throw new Error(`chromium sandbox-fallback relaunch (pid=${pid}) but CDP at 127.0.0.1:${port2} did not become ready within 30s`);
|
|
1287
|
+
}
|
|
1288
|
+
logEvent('plugkit', 'browser.cdp-ready', { pid, port: port2, ms: ready.ms, wsEndpoint: ready.wsEndpoint, noSandbox: true });
|
|
1289
|
+
return { pid, port: port2, wsEndpoint: ready.wsEndpoint };
|
|
1255
1290
|
}
|
|
1256
|
-
logEvent('plugkit', 'browser.cdp-ready', { pid, port, ms:
|
|
1257
|
-
return { pid, port, wsEndpoint };
|
|
1291
|
+
logEvent('plugkit', 'browser.cdp-ready', { pid, port, ms: ready.ms, wsEndpoint: ready.wsEndpoint });
|
|
1292
|
+
return { pid, port, wsEndpoint: ready.wsEndpoint };
|
|
1258
1293
|
}
|
|
1259
1294
|
|
|
1260
1295
|
function killPidQuiet(pid) {
|
|
@@ -1306,6 +1341,18 @@ function gracefulCloseBrowser(entry, reason) {
|
|
|
1306
1341
|
try { logEvent('plugkit', 'browser.closed', { reason: reason || 'closed', pid, port, profileDir }); } catch (_) {}
|
|
1307
1342
|
}
|
|
1308
1343
|
|
|
1344
|
+
function checkSessionNavigatedAway(port, claudeSessionId) {
|
|
1345
|
+
try {
|
|
1346
|
+
const list = fetchJsonSync(`http://127.0.0.1:${port}/json/list`, 1000);
|
|
1347
|
+
if (!Array.isArray(list)) return;
|
|
1348
|
+
const pages = list.filter(t => t && t.type === 'page');
|
|
1349
|
+
const stray = pages.filter(t => /^(chrome:\/\/new-tab-page|about:blank|chrome:\/\/newtab)/i.test(String(t.url || '')));
|
|
1350
|
+
if (pages.length > 0 && stray.length === pages.length) {
|
|
1351
|
+
logEvent('plugkit', 'browser.session-navigated-away', { sid: claudeSessionId, port, urls: pages.map(p => p.url) });
|
|
1352
|
+
}
|
|
1353
|
+
} catch (_) {}
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1309
1356
|
function resolveExistingBrowserEntry(cwd, claudeSessionId, pw, portsFile, sessionsFile, ports, sessions) {
|
|
1310
1357
|
const existing = ports[claudeSessionId];
|
|
1311
1358
|
if (!(existing && existing.pid && existing.wsEndpoint)) return null;
|
|
@@ -1315,7 +1362,10 @@ function resolveExistingBrowserEntry(cwd, claudeSessionId, pw, portsFile, sessio
|
|
|
1315
1362
|
const cdpOk = pidOk && !!fetchJsonSync(`http://127.0.0.1:${existing.port}/json/version`, 1000);
|
|
1316
1363
|
if (pidOk && profileOk && cdpOk) {
|
|
1317
1364
|
const pwIds = sessions[claudeSessionId] || [];
|
|
1318
|
-
if (pwIds.length > 0 && existing.pwSessionId)
|
|
1365
|
+
if (pwIds.length > 0 && existing.pwSessionId) {
|
|
1366
|
+
checkSessionNavigatedAway(existing.port, claudeSessionId);
|
|
1367
|
+
return existing.pwSessionId;
|
|
1368
|
+
}
|
|
1319
1369
|
const r = runBrowserRunner(pw, ['session', 'new', '--direct', existing.wsEndpoint], 30000, cwd, claudeSessionId);
|
|
1320
1370
|
if (r && r.status === 0) {
|
|
1321
1371
|
const sid = parseSessionId(r.stdout || '');
|
|
@@ -1385,7 +1435,11 @@ function getOrCreateBrowserSession(cwd, claudeSessionId, pw) {
|
|
|
1385
1435
|
&& fetchJsonSync(`http://127.0.0.1:${winner.port}/json/version`, 1000)) {
|
|
1386
1436
|
const a = runBrowserRunner(pw, ['session', 'new', '--direct', winner.wsEndpoint], 30000, cwd, claudeSessionId);
|
|
1387
1437
|
const sid = a && a.status === 0 ? parseSessionId(a.stdout || '') : null;
|
|
1388
|
-
if (sid) {
|
|
1438
|
+
if (sid) {
|
|
1439
|
+
checkSessionNavigatedAway(winner.port, claudeSessionId);
|
|
1440
|
+
logEvent('plugkit', 'browser.attached', { pwSessionId: sid, reused: true, via: 'spawn-lock-wait' });
|
|
1441
|
+
return sid;
|
|
1442
|
+
}
|
|
1389
1443
|
}
|
|
1390
1444
|
if (Date.now() > spawnDeadline) break;
|
|
1391
1445
|
sleepSyncMs(300);
|
|
@@ -2328,7 +2382,7 @@ function makeHostFunctions(instanceRef) {
|
|
|
2328
2382
|
stampBrowserLastUse(cwd, sessionId);
|
|
2329
2383
|
return writeWasmJson(instanceRef.value, {
|
|
2330
2384
|
ok: true,
|
|
2331
|
-
stdout: `Session ${pwSessionId} attached to locally-profiled chromium at ${
|
|
2385
|
+
stdout: `Session ${pwSessionId} attached to locally-profiled chromium at ${sessionProfileDir(cwd, sessionId)}`,
|
|
2332
2386
|
stderr: '',
|
|
2333
2387
|
exit_code: 0,
|
|
2334
2388
|
session_id: pwSessionId,
|
package/plugkit.version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.736
|