@broberg/lens-daemon 0.1.34 → 0.1.36
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/src/browser.ts +5 -1
- package/src/capture-session.ts +5 -1
- package/src/profiles.ts +29 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@broberg/lens-daemon",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36",
|
|
4
4
|
"description": "The Lens module of the cardmem daemon (flows, critic, coverage, capture sessions, the /lens/* routes and the cardmem-lens MCP) as a package. The daemon mounts createLensRouter(deps) + createLensMcpRouter(deps). Bun runtime.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/browser.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// auth (F074.4) and concurrent captures never bleed into each other. The
|
|
11
11
|
// context + page are always closed in a finally, even on capture error.
|
|
12
12
|
|
|
13
|
-
import { bindProfileChannel, claimProfile, profileDir, type ProfileChannel } from './profiles.ts';
|
|
13
|
+
import { bindProfileChannel, claimProfile, killOwnBrowsersOn, profileDir, type ProfileChannel } from './profiles.ts';
|
|
14
14
|
import {
|
|
15
15
|
chromium,
|
|
16
16
|
webkit,
|
|
@@ -404,6 +404,9 @@ export function runBrowserLaunchOptions(b: RunBrowser) {
|
|
|
404
404
|
return {
|
|
405
405
|
channel: b.channel,
|
|
406
406
|
headless: b.visible !== true,
|
|
407
|
+
// Playwright launches with --no-sandbox by default. This browser carries a real
|
|
408
|
+
// login (Google) and browses the open web, so Chrome's renderer sandbox stays ON.
|
|
409
|
+
chromiumSandbox: true,
|
|
407
410
|
ignoreDefaultArgs: ['--enable-automation'],
|
|
408
411
|
args: ['--disable-blink-features=AutomationControlled', '--disable-dev-shm-usage'],
|
|
409
412
|
};
|
|
@@ -444,6 +447,7 @@ async function launchRunBrowser(opts: WithPageOptions, b: RunBrowser): Promise<{
|
|
|
444
447
|
});
|
|
445
448
|
return { context, userDataDir };
|
|
446
449
|
} catch (err) {
|
|
450
|
+
killOwnBrowsersOn(userDataDir);
|
|
447
451
|
if (!b.profile) await rm(userDataDir, { recursive: true, force: true }).catch(() => {});
|
|
448
452
|
const msg = err instanceof Error ? err.message.split('\n')[0]! : String(err);
|
|
449
453
|
throw new Error(`browser "${b.channel}" did not start: ${msg}${b.channel === 'chrome' ? ' — is Google Chrome installed on the daemon host?' : ''}`);
|
package/src/capture-session.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// are confined to ~/.lens/state so a bad state_path can't clobber an arbitrary
|
|
19
19
|
// file. The endpoint is loopback-only (127.0.0.1) behind the daemon's auth.
|
|
20
20
|
|
|
21
|
-
import { bindProfileChannel, claimProfile, profileDir, type ProfileChannel } from './profiles.ts';
|
|
21
|
+
import { bindProfileChannel, claimProfile, killOwnBrowsersOn, profileDir, type ProfileChannel } from './profiles.ts';
|
|
22
22
|
import { EXTENSION_CHANNEL } from './browser.ts';
|
|
23
23
|
import { chromium, type Browser, type BrowserContext, type Page } from 'playwright';
|
|
24
24
|
import { z } from 'zod';
|
|
@@ -68,6 +68,9 @@ export function profileLaunchOptions(channel: ProfileChannel = EXTENSION_CHANNEL
|
|
|
68
68
|
// F009.136 — 'chrome' when the runs that will use this profile run in Google
|
|
69
69
|
// Chrome; the default stays the extension runs' build.
|
|
70
70
|
channel,
|
|
71
|
+
// The login window holds the password + 2FA being typed: renderer sandbox ON
|
|
72
|
+
// (Playwright's default is --no-sandbox).
|
|
73
|
+
chromiumSandbox: true,
|
|
71
74
|
ignoreDefaultArgs: ['--enable-automation'],
|
|
72
75
|
args: ['--disable-blink-features=AutomationControlled'],
|
|
73
76
|
};
|
|
@@ -418,6 +421,7 @@ export async function startCaptureSession(input: {
|
|
|
418
421
|
try {
|
|
419
422
|
context = await chromium.launchPersistentContext(dir, profileLaunchOptions(channel));
|
|
420
423
|
} catch (err) {
|
|
424
|
+
killOwnBrowsersOn(dir);
|
|
421
425
|
releaseProfile();
|
|
422
426
|
throw err;
|
|
423
427
|
}
|
package/src/profiles.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// F009.132 — a fixed browser profile per site. Google binds its session to the
|
|
2
2
|
// browser that holds it, so a login only survives if it happens in, and is reused
|
|
3
3
|
// from, ONE profile of ONE browser build. The profile lives on the daemon host.
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
4
5
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
5
6
|
import { homedir } from 'node:os';
|
|
6
7
|
import { join } from 'node:path';
|
|
@@ -52,3 +53,31 @@ export function bindProfileChannel(name: string, channel: ProfileChannel): void
|
|
|
52
53
|
);
|
|
53
54
|
}
|
|
54
55
|
}
|
|
56
|
+
|
|
57
|
+
// F009.136 — when a launch on a profile fails, Playwright can leave the Chrome it
|
|
58
|
+
// started running (measured twice on 25 Oct: a headless Chrome stayed up holding the
|
|
59
|
+
// profile, so every retry hung on the lock it left). Kill only processes this
|
|
60
|
+
// daemon started on this exact user-data dir.
|
|
61
|
+
export function killOwnBrowsersOn(dir: string): number {
|
|
62
|
+
let out = '';
|
|
63
|
+
try {
|
|
64
|
+
out = execFileSync('ps', ['-eo', 'pid=,ppid=,args='], { encoding: 'utf8' });
|
|
65
|
+
} catch {
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
let killed = 0;
|
|
69
|
+
for (const line of out.split('\n')) {
|
|
70
|
+
const m = /^\s*(\d+)\s+(\d+)\s+(.*)$/.exec(line);
|
|
71
|
+
// The EXACT dir: a plain substring test matched profile "a" inside "a-b" (measured).
|
|
72
|
+
const flag = `--user-data-dir=${dir}`;
|
|
73
|
+
const at = m ? (` ${m[3]!} `).indexOf(` ${flag} `) : -1;
|
|
74
|
+
if (!m || Number(m[2]) !== process.pid || at < 0) continue;
|
|
75
|
+
try {
|
|
76
|
+
process.kill(Number(m[1]), 'SIGKILL');
|
|
77
|
+
killed++;
|
|
78
|
+
} catch {
|
|
79
|
+
// already gone
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return killed;
|
|
83
|
+
}
|