@hasna/browser 0.3.8 → 0.4.1
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/dist/cli/index.js +259 -65
- package/dist/engines/selector.d.ts.map +1 -1
- package/dist/engines/tui.d.ts +49 -0
- package/dist/engines/tui.d.ts.map +1 -0
- package/dist/engines/tui.test.d.ts +2 -0
- package/dist/engines/tui.test.d.ts.map +1 -0
- package/dist/index.js +139 -42
- package/dist/lib/login-scripts.d.ts +89 -0
- package/dist/lib/login-scripts.d.ts.map +1 -0
- package/dist/lib/session.d.ts.map +1 -1
- package/dist/mcp/capture.d.ts.map +1 -1
- package/dist/mcp/index.js +313 -110
- package/dist/mcp/meta.d.ts.map +1 -1
- package/dist/mcp/network.d.ts.map +1 -1
- package/dist/mcp/recordings.d.ts.map +1 -1
- package/dist/server/index.js +135 -41
- package/dist/types/index.d.ts +3 -2
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/mcp/index.js
CHANGED
|
@@ -6,60 +6,39 @@ var __defProp = Object.defineProperty;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
function __accessProp(key) {
|
|
10
|
-
return this[key];
|
|
11
|
-
}
|
|
12
|
-
var __toESMCache_node;
|
|
13
|
-
var __toESMCache_esm;
|
|
14
9
|
var __toESM = (mod, isNodeMode, target) => {
|
|
15
|
-
var canCache = mod != null && typeof mod === "object";
|
|
16
|
-
if (canCache) {
|
|
17
|
-
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
18
|
-
var cached = cache.get(mod);
|
|
19
|
-
if (cached)
|
|
20
|
-
return cached;
|
|
21
|
-
}
|
|
22
10
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
23
11
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
24
12
|
for (let key of __getOwnPropNames(mod))
|
|
25
13
|
if (!__hasOwnProp.call(to, key))
|
|
26
14
|
__defProp(to, key, {
|
|
27
|
-
get:
|
|
15
|
+
get: () => mod[key],
|
|
28
16
|
enumerable: true
|
|
29
17
|
});
|
|
30
|
-
if (canCache)
|
|
31
|
-
cache.set(mod, to);
|
|
32
18
|
return to;
|
|
33
19
|
};
|
|
20
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
34
21
|
var __toCommonJS = (from) => {
|
|
35
|
-
var entry =
|
|
22
|
+
var entry = __moduleCache.get(from), desc;
|
|
36
23
|
if (entry)
|
|
37
24
|
return entry;
|
|
38
25
|
entry = __defProp({}, "__esModule", { value: true });
|
|
39
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
45
|
-
});
|
|
46
|
-
}
|
|
26
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
27
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
28
|
+
get: () => from[key],
|
|
29
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
30
|
+
}));
|
|
47
31
|
__moduleCache.set(from, entry);
|
|
48
32
|
return entry;
|
|
49
33
|
};
|
|
50
|
-
var __moduleCache;
|
|
51
34
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
52
|
-
var __returnValue = (v) => v;
|
|
53
|
-
function __exportSetter(name, newValue) {
|
|
54
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
55
|
-
}
|
|
56
35
|
var __export = (target, all) => {
|
|
57
36
|
for (var name in all)
|
|
58
37
|
__defProp(target, name, {
|
|
59
38
|
get: all[name],
|
|
60
39
|
enumerable: true,
|
|
61
40
|
configurable: true,
|
|
62
|
-
set:
|
|
41
|
+
set: (newValue) => all[name] = () => newValue
|
|
63
42
|
});
|
|
64
43
|
};
|
|
65
44
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -143,11 +122,11 @@ import { homedir as homedir4 } from "os";
|
|
|
143
122
|
import { join as join4 } from "path";
|
|
144
123
|
import { join as join6, dirname } from "path";
|
|
145
124
|
import { homedir as homedir5, platform } from "os";
|
|
146
|
-
function
|
|
125
|
+
function __accessProp(key) {
|
|
147
126
|
return this[key];
|
|
148
127
|
}
|
|
149
|
-
function
|
|
150
|
-
this[name] =
|
|
128
|
+
function __exportSetter(name, newValue) {
|
|
129
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
151
130
|
}
|
|
152
131
|
function translateSql(sql, dialect) {
|
|
153
132
|
if (dialect === "sqlite")
|
|
@@ -1177,10 +1156,10 @@ class SyncProgressTracker {
|
|
|
1177
1156
|
}
|
|
1178
1157
|
}
|
|
1179
1158
|
}
|
|
1180
|
-
var __create2, __getProtoOf2, __defProp2, __getOwnPropNames2, __hasOwnProp2,
|
|
1159
|
+
var __create2, __getProtoOf2, __defProp2, __getOwnPropNames2, __hasOwnProp2, __toESMCache_node, __toESMCache_esm, __toESM2 = (mod, isNodeMode, target) => {
|
|
1181
1160
|
var canCache = mod != null && typeof mod === "object";
|
|
1182
1161
|
if (canCache) {
|
|
1183
|
-
var cache = isNodeMode ?
|
|
1162
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
1184
1163
|
var cached = cache.get(mod);
|
|
1185
1164
|
if (cached)
|
|
1186
1165
|
return cached;
|
|
@@ -1190,19 +1169,19 @@ var __create2, __getProtoOf2, __defProp2, __getOwnPropNames2, __hasOwnProp2, __t
|
|
|
1190
1169
|
for (let key of __getOwnPropNames2(mod))
|
|
1191
1170
|
if (!__hasOwnProp2.call(to, key))
|
|
1192
1171
|
__defProp2(to, key, {
|
|
1193
|
-
get:
|
|
1172
|
+
get: __accessProp.bind(mod, key),
|
|
1194
1173
|
enumerable: true
|
|
1195
1174
|
});
|
|
1196
1175
|
if (canCache)
|
|
1197
1176
|
cache.set(mod, to);
|
|
1198
1177
|
return to;
|
|
1199
|
-
}, __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports),
|
|
1178
|
+
}, __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports), __returnValue = (v) => v, __export2 = (target, all) => {
|
|
1200
1179
|
for (var name in all)
|
|
1201
1180
|
__defProp2(target, name, {
|
|
1202
1181
|
get: all[name],
|
|
1203
1182
|
enumerable: true,
|
|
1204
1183
|
configurable: true,
|
|
1205
|
-
set:
|
|
1184
|
+
set: __exportSetter.bind(all, name)
|
|
1206
1185
|
});
|
|
1207
1186
|
}, __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res), __require2, require_postgres_array, require_arrayParser, require_postgres_date, require_mutable, require_postgres_interval, require_postgres_bytea, require_textParsers, require_pg_int8, require_binaryParsers, require_builtins, require_pg_types, require_defaults, require_utils, require_utils_legacy, require_utils_webcrypto, require_utils2, require_cert_signatures, require_sasl, require_type_overrides, require_pg_connection_string, require_connection_parameters, require_result, require_query, require_messages, require_buffer_writer, require_serializer, require_buffer_reader, require_parser, require_dist, require_empty, require_stream, require_connection, require_split2, require_helper, require_lib, require_client, require_pg_pool, require_query2, require_client2, require_lib2, import_lib, Client, Pool, Connection, types2, Query, DatabaseError, escapeIdentifier, escapeLiteral, Result, TypeOverrides, defaults, esm_default, init_esm, init_adapter, util3, objectUtil2, ZodParsedType2, getParsedType2 = (data) => {
|
|
1208
1187
|
const t = typeof data;
|
|
@@ -10619,6 +10598,89 @@ var init_bun_webview = __esm(() => {
|
|
|
10619
10598
|
};
|
|
10620
10599
|
});
|
|
10621
10600
|
|
|
10601
|
+
// src/engines/tui.ts
|
|
10602
|
+
import { execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
10603
|
+
function isTuiAvailable() {
|
|
10604
|
+
try {
|
|
10605
|
+
execSync2("which ttyd", { stdio: "ignore" });
|
|
10606
|
+
return true;
|
|
10607
|
+
} catch {
|
|
10608
|
+
return false;
|
|
10609
|
+
}
|
|
10610
|
+
}
|
|
10611
|
+
async function findAvailablePort(startPort) {
|
|
10612
|
+
let port = startPort;
|
|
10613
|
+
for (let i = 0;i < 100; i++) {
|
|
10614
|
+
try {
|
|
10615
|
+
const resp = await fetch(`http://localhost:${port}`);
|
|
10616
|
+
port++;
|
|
10617
|
+
} catch {
|
|
10618
|
+
return port;
|
|
10619
|
+
}
|
|
10620
|
+
}
|
|
10621
|
+
throw new BrowserError("No available port found for ttyd", "TUI_PORT_EXHAUSTED");
|
|
10622
|
+
}
|
|
10623
|
+
async function waitForTtyd(port, timeoutMs = 1e4) {
|
|
10624
|
+
const start = Date.now();
|
|
10625
|
+
while (Date.now() - start < timeoutMs) {
|
|
10626
|
+
try {
|
|
10627
|
+
const resp = await fetch(`http://localhost:${port}`);
|
|
10628
|
+
if (resp.ok || resp.status === 200)
|
|
10629
|
+
return;
|
|
10630
|
+
} catch {}
|
|
10631
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
10632
|
+
}
|
|
10633
|
+
throw new BrowserError(`ttyd did not start within ${timeoutMs}ms`, "TUI_TIMEOUT");
|
|
10634
|
+
}
|
|
10635
|
+
async function launchTui(command, options = {}) {
|
|
10636
|
+
if (!isTuiAvailable()) {
|
|
10637
|
+
throw new BrowserError("ttyd not found \u2014 install with: brew install ttyd", "TUI_NOT_AVAILABLE");
|
|
10638
|
+
}
|
|
10639
|
+
const port = await findAvailablePort(nextPort);
|
|
10640
|
+
nextPort = port + 1;
|
|
10641
|
+
const ttydProcess = spawn2("ttyd", ["--writable", "--port", String(port), "/bin/sh", "-c", command], {
|
|
10642
|
+
stdio: "ignore",
|
|
10643
|
+
detached: false
|
|
10644
|
+
});
|
|
10645
|
+
ttydProcess.on("error", (err) => {
|
|
10646
|
+
console.error(`[tui] ttyd process error: ${err.message}`);
|
|
10647
|
+
});
|
|
10648
|
+
try {
|
|
10649
|
+
await waitForTtyd(port);
|
|
10650
|
+
const viewport = options.viewport ?? { width: 1280, height: 720 };
|
|
10651
|
+
const browser = await launchPlaywright({
|
|
10652
|
+
headless: options.headless ?? true,
|
|
10653
|
+
viewport
|
|
10654
|
+
});
|
|
10655
|
+
const page = await getPage(browser, { viewport });
|
|
10656
|
+
await page.goto(`http://localhost:${port}`, {
|
|
10657
|
+
waitUntil: "domcontentloaded"
|
|
10658
|
+
});
|
|
10659
|
+
await page.waitForSelector(".xterm-screen", { timeout: 1e4 });
|
|
10660
|
+
return { ttydProcess, port, browser, page };
|
|
10661
|
+
} catch (err) {
|
|
10662
|
+
ttydProcess.kill();
|
|
10663
|
+
throw err;
|
|
10664
|
+
}
|
|
10665
|
+
}
|
|
10666
|
+
async function closeTui(session) {
|
|
10667
|
+
try {
|
|
10668
|
+
await session.page.close();
|
|
10669
|
+
} catch {}
|
|
10670
|
+
try {
|
|
10671
|
+
await session.browser.close();
|
|
10672
|
+
} catch {}
|
|
10673
|
+
try {
|
|
10674
|
+
session.ttydProcess.kill("SIGTERM");
|
|
10675
|
+
} catch {}
|
|
10676
|
+
}
|
|
10677
|
+
var DEFAULT_TTYD_PORT_START = 7780, nextPort;
|
|
10678
|
+
var init_tui = __esm(() => {
|
|
10679
|
+
init_types();
|
|
10680
|
+
init_playwright();
|
|
10681
|
+
nextPort = DEFAULT_TTYD_PORT_START;
|
|
10682
|
+
});
|
|
10683
|
+
|
|
10622
10684
|
// src/engines/selector.ts
|
|
10623
10685
|
function selectEngine(useCase, explicit) {
|
|
10624
10686
|
if (explicit && explicit !== "auto")
|
|
@@ -10642,6 +10704,7 @@ var init_selector = __esm(() => {
|
|
|
10642
10704
|
init_types();
|
|
10643
10705
|
init_lightpanda();
|
|
10644
10706
|
init_bun_webview();
|
|
10707
|
+
init_tui();
|
|
10645
10708
|
ENGINE_MAP = {
|
|
10646
10709
|
["scrape" /* SCRAPE */]: "bun",
|
|
10647
10710
|
["extract_links" /* EXTRACT_LINKS */]: "bun",
|
|
@@ -10652,6 +10715,7 @@ var init_selector = __esm(() => {
|
|
|
10652
10715
|
["auth_flow" /* AUTH_FLOW */]: "playwright",
|
|
10653
10716
|
["multi_tab" /* MULTI_TAB */]: "playwright",
|
|
10654
10717
|
["record_replay" /* RECORD_REPLAY */]: "playwright",
|
|
10718
|
+
["terminal_test" /* TERMINAL_TEST */]: "tui",
|
|
10655
10719
|
["network_monitor" /* NETWORK_MONITOR */]: "cdp",
|
|
10656
10720
|
["har_capture" /* HAR_CAPTURE */]: "cdp",
|
|
10657
10721
|
["perf_profile" /* PERF_PROFILE */]: "cdp",
|
|
@@ -12017,7 +12081,7 @@ async function createSession2(opts = {}) {
|
|
|
12017
12081
|
try {
|
|
12018
12082
|
cleanups2.push(setupDialogHandler(page2, session2.id));
|
|
12019
12083
|
} catch {}
|
|
12020
|
-
handles.set(session2.id, { browser: cdpBrowser, bunView: null, page: page2, engine: "cdp", cleanups: cleanups2, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false });
|
|
12084
|
+
handles.set(session2.id, { browser: cdpBrowser, bunView: null, tuiSession: null, page: page2, engine: "cdp", cleanups: cleanups2, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false });
|
|
12021
12085
|
return { session: session2, page: page2 };
|
|
12022
12086
|
}
|
|
12023
12087
|
const engine = opts.engine === "auto" || !opts.engine ? selectEngine(opts.useCase ?? "spa_navigate" /* SPA_NAVIGATE */, opts.engine) : opts.engine;
|
|
@@ -12043,6 +12107,38 @@ async function createSession2(opts = {}) {
|
|
|
12043
12107
|
browser = await connectLightpanda();
|
|
12044
12108
|
const context = await browser.newContext({ viewport: opts.viewport ?? { width: 1280, height: 720 } });
|
|
12045
12109
|
page = await context.newPage();
|
|
12110
|
+
} else if (resolvedEngine === "tui") {
|
|
12111
|
+
const command = opts.startUrl ?? "bash";
|
|
12112
|
+
const tuiSess = await launchTui(command, {
|
|
12113
|
+
headless: opts.headless ?? true,
|
|
12114
|
+
viewport: opts.viewport
|
|
12115
|
+
});
|
|
12116
|
+
browser = tuiSess.browser;
|
|
12117
|
+
page = tuiSess.page;
|
|
12118
|
+
const session2 = createSession({
|
|
12119
|
+
engine: "tui",
|
|
12120
|
+
projectId: opts.projectId,
|
|
12121
|
+
agentId: opts.agentId,
|
|
12122
|
+
startUrl: opts.startUrl,
|
|
12123
|
+
name: opts.name ?? "tui"
|
|
12124
|
+
});
|
|
12125
|
+
const cleanups2 = [];
|
|
12126
|
+
cleanups2.push(() => closeTui(tuiSess));
|
|
12127
|
+
if (opts.captureNetwork !== false) {
|
|
12128
|
+
try {
|
|
12129
|
+
cleanups2.push(enableNetworkLogging(page, session2.id));
|
|
12130
|
+
} catch {}
|
|
12131
|
+
}
|
|
12132
|
+
if (opts.captureConsole !== false) {
|
|
12133
|
+
try {
|
|
12134
|
+
cleanups2.push(enableConsoleCapture(page, session2.id));
|
|
12135
|
+
} catch {}
|
|
12136
|
+
}
|
|
12137
|
+
try {
|
|
12138
|
+
cleanups2.push(setupDialogHandler(page, session2.id));
|
|
12139
|
+
} catch {}
|
|
12140
|
+
handles.set(session2.id, { browser, bunView: null, tuiSession: tuiSess, page, engine: "tui", cleanups: cleanups2, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false });
|
|
12141
|
+
return { session: session2, page };
|
|
12046
12142
|
} else {
|
|
12047
12143
|
browser = await pool.acquire(opts.headless ?? true);
|
|
12048
12144
|
if (opts.storageState) {
|
|
@@ -12113,7 +12209,7 @@ async function createSession2(opts = {}) {
|
|
|
12113
12209
|
} catch {}
|
|
12114
12210
|
}
|
|
12115
12211
|
}
|
|
12116
|
-
handles.set(session.id, { browser, bunView, page, engine: bunView ? "bun" : resolvedEngine, cleanups, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false });
|
|
12212
|
+
handles.set(session.id, { browser, bunView, tuiSession: null, page, engine: bunView ? "bun" : resolvedEngine, cleanups, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false });
|
|
12117
12213
|
if (opts.startUrl) {
|
|
12118
12214
|
try {
|
|
12119
12215
|
if (bunView) {
|
|
@@ -12183,7 +12279,7 @@ async function closeSession2(sessionId) {
|
|
|
12183
12279
|
try {
|
|
12184
12280
|
await handle.bunView.close();
|
|
12185
12281
|
} catch {}
|
|
12186
|
-
} else {
|
|
12282
|
+
} else if (handle.tuiSession) {} else {
|
|
12187
12283
|
try {
|
|
12188
12284
|
await handle.page.context().close();
|
|
12189
12285
|
} catch {}
|
|
@@ -12283,6 +12379,7 @@ var init_session = __esm(() => {
|
|
|
12283
12379
|
init_lightpanda();
|
|
12284
12380
|
init_bun_webview();
|
|
12285
12381
|
init_selector();
|
|
12382
|
+
init_tui();
|
|
12286
12383
|
init_network();
|
|
12287
12384
|
init_console();
|
|
12288
12385
|
init_stealth();
|
|
@@ -19396,6 +19493,63 @@ var init_agents2 = __esm(() => {
|
|
|
19396
19493
|
init_agents();
|
|
19397
19494
|
});
|
|
19398
19495
|
|
|
19496
|
+
// src/lib/gallery-diff.ts
|
|
19497
|
+
var exports_gallery_diff = {};
|
|
19498
|
+
__export(exports_gallery_diff, {
|
|
19499
|
+
diffImages: () => diffImages
|
|
19500
|
+
});
|
|
19501
|
+
import { join as join11 } from "path";
|
|
19502
|
+
import { mkdirSync as mkdirSync9 } from "fs";
|
|
19503
|
+
async function diffImages(path1, path2) {
|
|
19504
|
+
const img1 = import_sharp2.default(path1);
|
|
19505
|
+
const img2 = import_sharp2.default(path2);
|
|
19506
|
+
const [meta1, meta2] = await Promise.all([img1.metadata(), img2.metadata()]);
|
|
19507
|
+
const w = Math.min(meta1.width ?? 1280, meta2.width ?? 1280);
|
|
19508
|
+
const h = Math.min(meta1.height ?? 720, meta2.height ?? 720);
|
|
19509
|
+
const [raw1, raw2] = await Promise.all([
|
|
19510
|
+
import_sharp2.default(path1).resize(w, h, { fit: "fill" }).raw().toBuffer(),
|
|
19511
|
+
import_sharp2.default(path2).resize(w, h, { fit: "fill" }).raw().toBuffer()
|
|
19512
|
+
]);
|
|
19513
|
+
const totalPixels = w * h;
|
|
19514
|
+
const channels = 3;
|
|
19515
|
+
const diffBuffer = Buffer.alloc(raw1.length);
|
|
19516
|
+
let changedPixels = 0;
|
|
19517
|
+
for (let i = 0;i < raw1.length; i += channels) {
|
|
19518
|
+
const dr = Math.abs(raw1[i] - raw2[i]);
|
|
19519
|
+
const dg = Math.abs(raw1[i + 1] - raw2[i + 1]);
|
|
19520
|
+
const db = Math.abs(raw1[i + 2] - raw2[i + 2]);
|
|
19521
|
+
const diff = (dr + dg + db) / 3;
|
|
19522
|
+
if (diff > 10) {
|
|
19523
|
+
changedPixels++;
|
|
19524
|
+
diffBuffer[i] = 255;
|
|
19525
|
+
diffBuffer[i + 1] = 0;
|
|
19526
|
+
diffBuffer[i + 2] = 0;
|
|
19527
|
+
} else {
|
|
19528
|
+
diffBuffer[i] = Math.round(raw1[i] * 0.4);
|
|
19529
|
+
diffBuffer[i + 1] = Math.round(raw1[i + 1] * 0.4);
|
|
19530
|
+
diffBuffer[i + 2] = Math.round(raw1[i + 2] * 0.4);
|
|
19531
|
+
}
|
|
19532
|
+
}
|
|
19533
|
+
const dataDir = getDataDir2();
|
|
19534
|
+
const diffDir = join11(dataDir, "diffs");
|
|
19535
|
+
mkdirSync9(diffDir, { recursive: true });
|
|
19536
|
+
const diffPath = join11(diffDir, `diff-${Date.now()}.webp`);
|
|
19537
|
+
const diffImageBuffer = await import_sharp2.default(diffBuffer, { raw: { width: w, height: h, channels } }).webp({ quality: 85 }).toBuffer();
|
|
19538
|
+
await Bun.write(diffPath, diffImageBuffer);
|
|
19539
|
+
return {
|
|
19540
|
+
diff_path: diffPath,
|
|
19541
|
+
diff_base64: diffImageBuffer.toString("base64"),
|
|
19542
|
+
changed_pixels: changedPixels,
|
|
19543
|
+
total_pixels: totalPixels,
|
|
19544
|
+
changed_percent: changedPixels / totalPixels * 100
|
|
19545
|
+
};
|
|
19546
|
+
}
|
|
19547
|
+
var import_sharp2;
|
|
19548
|
+
var init_gallery_diff = __esm(() => {
|
|
19549
|
+
init_schema();
|
|
19550
|
+
import_sharp2 = __toESM(require_lib3(), 1);
|
|
19551
|
+
});
|
|
19552
|
+
|
|
19399
19553
|
// src/lib/profiles.ts
|
|
19400
19554
|
var exports_profiles = {};
|
|
19401
19555
|
__export(exports_profiles, {
|
|
@@ -24010,8 +24164,8 @@ import { join as join33 } from "path";
|
|
|
24010
24164
|
import { existsSync as existsSync43, mkdirSync as mkdirSync4, readFileSync as readFileSync33, writeFileSync as writeFileSync33 } from "fs";
|
|
24011
24165
|
import { homedir as homedir33 } from "os";
|
|
24012
24166
|
import { join as join43 } from "path";
|
|
24013
|
-
function
|
|
24014
|
-
this[name] =
|
|
24167
|
+
function __exportSetter2(name, newValue) {
|
|
24168
|
+
this[name] = __returnValue2.bind(null, newValue);
|
|
24015
24169
|
}
|
|
24016
24170
|
function isInMemoryDb(path) {
|
|
24017
24171
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
@@ -27125,13 +27279,13 @@ function clearActiveModel() {
|
|
|
27125
27279
|
delete config.activeModel;
|
|
27126
27280
|
writeConfig(config);
|
|
27127
27281
|
}
|
|
27128
|
-
var __defProp3,
|
|
27282
|
+
var __defProp3, __returnValue2 = (v) => v, __export3 = (target, all) => {
|
|
27129
27283
|
for (var name in all)
|
|
27130
27284
|
__defProp3(target, name, {
|
|
27131
27285
|
get: all[name],
|
|
27132
27286
|
enumerable: true,
|
|
27133
27287
|
configurable: true,
|
|
27134
|
-
set:
|
|
27288
|
+
set: __exportSetter2.bind(all, name)
|
|
27135
27289
|
});
|
|
27136
27290
|
}, __esm3 = (fn, res) => () => (fn && (res = fn(fn = 0)), res), exports_database, MIGRATIONS, _db2 = null, init_database, AgentConflictError, EntityNotFoundError, MemoryNotFoundError, DuplicateMemoryError, MemoryExpiredError, InvalidScopeError, VersionConflictError, MemoryConflictError, OPENAI_EMBED_URL = "https://api.openai.com/v1/embeddings", EMBED_MODEL = "text-embedding-3-small", EMBED_DIMENSIONS = 1536, REDACTED = "[REDACTED]", SECRET_PATTERNS, _idCounter = 0, hookRegistry, INSTRUCTION_PATTERNS, PROMOTIONAL_PATTERNS, RECALL_PROMOTE_THRESHOLD = 3, CONFLICT_WINDOW_MS, MEMORY_WRITE_TTL = 30, MemoryLockConflictError, sessionFocus, STOP_WORDS, DEFAULT_CONFIG, VALID_SCOPES, VALID_CATEGORIES, defaultSyncAgents, DEFAULT_AUTO_MEMORY_CONFIG, MEMORY_EXTRACTION_SYSTEM_PROMPT = `You are a precise memory extraction engine for an AI agent.
|
|
27137
27291
|
Given text, extract facts worth remembering as structured JSON.
|
|
@@ -28654,11 +28808,11 @@ import { randomUUID as randomUUID22 } from "crypto";
|
|
|
28654
28808
|
import { readFileSync as readFileSync24, writeFileSync as writeFileSync7, mkdirSync as mkdirSync34 } from "fs";
|
|
28655
28809
|
import { join as join44, dirname as dirname24 } from "path";
|
|
28656
28810
|
import { homedir as homedir42 } from "os";
|
|
28657
|
-
function
|
|
28811
|
+
function __accessProp2(key) {
|
|
28658
28812
|
return this[key];
|
|
28659
28813
|
}
|
|
28660
|
-
function
|
|
28661
|
-
this[name] =
|
|
28814
|
+
function __exportSetter3(name, newValue) {
|
|
28815
|
+
this[name] = __returnValue3.bind(null, newValue);
|
|
28662
28816
|
}
|
|
28663
28817
|
function getDbPath3() {
|
|
28664
28818
|
if (process.env.CONVERSATIONS_DB_PATH)
|
|
@@ -30643,10 +30797,10 @@ function getGraphStats() {
|
|
|
30643
30797
|
map[r.relation] = r.c;
|
|
30644
30798
|
return { total_edges: total, by_relation: map };
|
|
30645
30799
|
}
|
|
30646
|
-
var __create3, __getProtoOf3, __defProp4, __getOwnPropNames3, __getOwnPropDesc2, __hasOwnProp3,
|
|
30800
|
+
var __create3, __getProtoOf3, __defProp4, __getOwnPropNames3, __getOwnPropDesc2, __hasOwnProp3, __toESMCache_node2, __toESMCache_esm2, __toESM3 = (mod, isNodeMode, target) => {
|
|
30647
30801
|
var canCache = mod != null && typeof mod === "object";
|
|
30648
30802
|
if (canCache) {
|
|
30649
|
-
var cache = isNodeMode ?
|
|
30803
|
+
var cache = isNodeMode ? __toESMCache_node2 ??= new WeakMap : __toESMCache_esm2 ??= new WeakMap;
|
|
30650
30804
|
var cached = cache.get(mod);
|
|
30651
30805
|
if (cached)
|
|
30652
30806
|
return cached;
|
|
@@ -30656,7 +30810,7 @@ var __create3, __getProtoOf3, __defProp4, __getOwnPropNames3, __getOwnPropDesc2,
|
|
|
30656
30810
|
for (let key of __getOwnPropNames3(mod))
|
|
30657
30811
|
if (!__hasOwnProp3.call(to, key))
|
|
30658
30812
|
__defProp4(to, key, {
|
|
30659
|
-
get:
|
|
30813
|
+
get: __accessProp2.bind(mod, key),
|
|
30660
30814
|
enumerable: true
|
|
30661
30815
|
});
|
|
30662
30816
|
if (canCache)
|
|
@@ -30671,19 +30825,19 @@ var __create3, __getProtoOf3, __defProp4, __getOwnPropNames3, __getOwnPropDesc2,
|
|
|
30671
30825
|
for (var key of __getOwnPropNames3(from))
|
|
30672
30826
|
if (!__hasOwnProp3.call(entry, key))
|
|
30673
30827
|
__defProp4(entry, key, {
|
|
30674
|
-
get:
|
|
30828
|
+
get: __accessProp2.bind(from, key),
|
|
30675
30829
|
enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable
|
|
30676
30830
|
});
|
|
30677
30831
|
}
|
|
30678
30832
|
__moduleCache2.set(from, entry);
|
|
30679
30833
|
return entry;
|
|
30680
|
-
}, __moduleCache2, __commonJS3 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports),
|
|
30834
|
+
}, __moduleCache2, __commonJS3 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports), __returnValue3 = (v) => v, __export4 = (target, all) => {
|
|
30681
30835
|
for (var name in all)
|
|
30682
30836
|
__defProp4(target, name, {
|
|
30683
30837
|
get: all[name],
|
|
30684
30838
|
enumerable: true,
|
|
30685
30839
|
configurable: true,
|
|
30686
|
-
set:
|
|
30840
|
+
set: __exportSetter3.bind(all, name)
|
|
30687
30841
|
});
|
|
30688
30842
|
}, exports_db, db = null, init_db = () => {}, require_react_development, require_react, cachedConfig = null, configLoadedAt = 0, CONFIG_CACHE_MS = 1e4, import_react, AGENT_NAMES, AGENT_ID_FILE, cachedAutoName = null, ONLINE_THRESHOLD_SECONDS = 60, CONFLICT_THRESHOLD_SECONDS, DEFAULT_LOCK_EXPIRY_MS, STALE_HEARTBEAT_SECONDS, STOPWORDS;
|
|
30689
30843
|
var init_dist4 = __esm(() => {
|
|
@@ -33297,7 +33451,7 @@ import { existsSync as existsSync62 } from "fs";
|
|
|
33297
33451
|
import { join as join62 } from "path";
|
|
33298
33452
|
import { readFileSync as readFileSync43, statSync as statSync22 } from "fs";
|
|
33299
33453
|
import { relative as relative2, resolve as resolve23, join as join72 } from "path";
|
|
33300
|
-
import { execSync as
|
|
33454
|
+
import { execSync as execSync3 } from "child_process";
|
|
33301
33455
|
|
|
33302
33456
|
class TodosClient {
|
|
33303
33457
|
baseUrl;
|
|
@@ -37645,7 +37799,7 @@ function parseGitHubUrl(url) {
|
|
|
37645
37799
|
return { owner: match[1], repo: match[2], number: parseInt(match[3], 10) };
|
|
37646
37800
|
}
|
|
37647
37801
|
function fetchGitHubIssue(owner, repo, number) {
|
|
37648
|
-
const json2 =
|
|
37802
|
+
const json2 = execSync3(`gh api repos/${owner}/${repo}/issues/${number}`, { encoding: "utf-8", timeout: 15000 });
|
|
37649
37803
|
const data = JSON.parse(json2);
|
|
37650
37804
|
return {
|
|
37651
37805
|
number: data.number,
|
|
@@ -43483,57 +43637,9 @@ function detectType(filename) {
|
|
|
43483
43637
|
};
|
|
43484
43638
|
return map[ext] ?? "file";
|
|
43485
43639
|
}
|
|
43486
|
-
// src/lib/gallery-diff.ts
|
|
43487
|
-
init_schema();
|
|
43488
|
-
var import_sharp2 = __toESM(require_lib3(), 1);
|
|
43489
|
-
import { join as join11 } from "path";
|
|
43490
|
-
import { mkdirSync as mkdirSync9 } from "fs";
|
|
43491
|
-
async function diffImages(path1, path2) {
|
|
43492
|
-
const img1 = import_sharp2.default(path1);
|
|
43493
|
-
const img2 = import_sharp2.default(path2);
|
|
43494
|
-
const [meta1, meta2] = await Promise.all([img1.metadata(), img2.metadata()]);
|
|
43495
|
-
const w = Math.min(meta1.width ?? 1280, meta2.width ?? 1280);
|
|
43496
|
-
const h = Math.min(meta1.height ?? 720, meta2.height ?? 720);
|
|
43497
|
-
const [raw1, raw2] = await Promise.all([
|
|
43498
|
-
import_sharp2.default(path1).resize(w, h, { fit: "fill" }).raw().toBuffer(),
|
|
43499
|
-
import_sharp2.default(path2).resize(w, h, { fit: "fill" }).raw().toBuffer()
|
|
43500
|
-
]);
|
|
43501
|
-
const totalPixels = w * h;
|
|
43502
|
-
const channels = 3;
|
|
43503
|
-
const diffBuffer = Buffer.alloc(raw1.length);
|
|
43504
|
-
let changedPixels = 0;
|
|
43505
|
-
for (let i = 0;i < raw1.length; i += channels) {
|
|
43506
|
-
const dr = Math.abs(raw1[i] - raw2[i]);
|
|
43507
|
-
const dg = Math.abs(raw1[i + 1] - raw2[i + 1]);
|
|
43508
|
-
const db = Math.abs(raw1[i + 2] - raw2[i + 2]);
|
|
43509
|
-
const diff = (dr + dg + db) / 3;
|
|
43510
|
-
if (diff > 10) {
|
|
43511
|
-
changedPixels++;
|
|
43512
|
-
diffBuffer[i] = 255;
|
|
43513
|
-
diffBuffer[i + 1] = 0;
|
|
43514
|
-
diffBuffer[i + 2] = 0;
|
|
43515
|
-
} else {
|
|
43516
|
-
diffBuffer[i] = Math.round(raw1[i] * 0.4);
|
|
43517
|
-
diffBuffer[i + 1] = Math.round(raw1[i + 1] * 0.4);
|
|
43518
|
-
diffBuffer[i + 2] = Math.round(raw1[i + 2] * 0.4);
|
|
43519
|
-
}
|
|
43520
|
-
}
|
|
43521
|
-
const dataDir = getDataDir2();
|
|
43522
|
-
const diffDir = join11(dataDir, "diffs");
|
|
43523
|
-
mkdirSync9(diffDir, { recursive: true });
|
|
43524
|
-
const diffPath = join11(diffDir, `diff-${Date.now()}.webp`);
|
|
43525
|
-
const diffImageBuffer = await import_sharp2.default(diffBuffer, { raw: { width: w, height: h, channels } }).webp({ quality: 85 }).toBuffer();
|
|
43526
|
-
await Bun.write(diffPath, diffImageBuffer);
|
|
43527
|
-
return {
|
|
43528
|
-
diff_path: diffPath,
|
|
43529
|
-
diff_base64: diffImageBuffer.toString("base64"),
|
|
43530
|
-
changed_pixels: changedPixels,
|
|
43531
|
-
total_pixels: totalPixels,
|
|
43532
|
-
changed_percent: changedPixels / totalPixels * 100
|
|
43533
|
-
};
|
|
43534
|
-
}
|
|
43535
43640
|
|
|
43536
43641
|
// src/mcp/helpers.ts
|
|
43642
|
+
init_gallery_diff();
|
|
43537
43643
|
init_snapshot();
|
|
43538
43644
|
|
|
43539
43645
|
// src/lib/files-integration.ts
|
|
@@ -43710,7 +43816,7 @@ function resolveSessionId(sessionId) {
|
|
|
43710
43816
|
// src/mcp/sessions.ts
|
|
43711
43817
|
function register(server) {
|
|
43712
43818
|
server.tool("browser_session_create", "Create a new browser session. If agent_id is set and already has an active session, returns the existing one (use force_new to override). If session_id is omitted on other tools, the single active session is auto-selected. Use cdp_url to attach to an already-running Chrome instance.", {
|
|
43713
|
-
engine: exports_external.enum(["playwright", "cdp", "lightpanda", "bun", "auto"]).optional().default("auto"),
|
|
43819
|
+
engine: exports_external.enum(["playwright", "cdp", "lightpanda", "bun", "tui", "auto"]).optional().default("auto"),
|
|
43714
43820
|
use_case: exports_external.string().optional(),
|
|
43715
43821
|
project_id: exports_external.string().optional(),
|
|
43716
43822
|
agent_id: exports_external.string().optional(),
|
|
@@ -44782,6 +44888,40 @@ function register3(server) {
|
|
|
44782
44888
|
return err(e);
|
|
44783
44889
|
}
|
|
44784
44890
|
});
|
|
44891
|
+
server.tool("browser_diff", "Visual diff between two URLs or a URL and a gallery entry. Screenshots both, returns a pixel diff image highlighting changes in red.", {
|
|
44892
|
+
session_id: exports_external.string().optional(),
|
|
44893
|
+
url1: exports_external.string().describe("First URL to screenshot"),
|
|
44894
|
+
url2: exports_external.string().describe("Second URL to screenshot"),
|
|
44895
|
+
threshold: exports_external.number().optional().default(10).describe("Pixel difference threshold (0-255, default 10)"),
|
|
44896
|
+
wait_ms: exports_external.number().optional().default(1000).describe("Wait time after navigation before screenshot (ms)")
|
|
44897
|
+
}, async ({ session_id, url1, url2, threshold, wait_ms }) => {
|
|
44898
|
+
try {
|
|
44899
|
+
const sid = resolveSessionId(session_id);
|
|
44900
|
+
const page = getSessionPage(sid);
|
|
44901
|
+
await page.goto(url1, { waitUntil: "domcontentloaded" });
|
|
44902
|
+
await new Promise((r) => setTimeout(r, wait_ms));
|
|
44903
|
+
const ss1 = await takeScreenshot(page, { maxWidth: 1280, compress: true, track: false });
|
|
44904
|
+
await page.goto(url2, { waitUntil: "domcontentloaded" });
|
|
44905
|
+
await new Promise((r) => setTimeout(r, wait_ms));
|
|
44906
|
+
const ss2 = await takeScreenshot(page, { maxWidth: 1280, compress: true, track: false });
|
|
44907
|
+
const { diffImages: diffImages2 } = await Promise.resolve().then(() => (init_gallery_diff(), exports_gallery_diff));
|
|
44908
|
+
const diff = await diffImages2(ss1.path, ss2.path);
|
|
44909
|
+
logEvent(sid, "diff", { url1, url2, changed_percent: diff.changed_percent });
|
|
44910
|
+
return json({
|
|
44911
|
+
url1,
|
|
44912
|
+
url2,
|
|
44913
|
+
changed_pixels: diff.changed_pixels,
|
|
44914
|
+
total_pixels: diff.total_pixels,
|
|
44915
|
+
changed_percent: Math.round(diff.changed_percent * 100) / 100,
|
|
44916
|
+
diff_path: diff.diff_path,
|
|
44917
|
+
diff_base64: diff.diff_base64.length > 50000 ? undefined : diff.diff_base64,
|
|
44918
|
+
screenshot1_path: ss1.path,
|
|
44919
|
+
screenshot2_path: ss2.path
|
|
44920
|
+
});
|
|
44921
|
+
} catch (e) {
|
|
44922
|
+
return err(e);
|
|
44923
|
+
}
|
|
44924
|
+
});
|
|
44785
44925
|
}
|
|
44786
44926
|
|
|
44787
44927
|
// src/mcp/network.ts
|
|
@@ -45126,6 +45266,53 @@ function register4(server) {
|
|
|
45126
45266
|
return err(e);
|
|
45127
45267
|
}
|
|
45128
45268
|
});
|
|
45269
|
+
server.tool("browser_performance_budget", "Check page performance against a budget. Set thresholds for LCP, FCP, CLS, TTFB, DOM complete, and load event. Returns pass/fail per metric with actual values.", {
|
|
45270
|
+
session_id: exports_external.string().optional(),
|
|
45271
|
+
lcp_ms: exports_external.number().optional().describe("Largest Contentful Paint budget in ms (good: <2500)"),
|
|
45272
|
+
fcp_ms: exports_external.number().optional().describe("First Contentful Paint budget in ms (good: <1800)"),
|
|
45273
|
+
cls: exports_external.number().optional().describe("Cumulative Layout Shift budget (good: <0.1)"),
|
|
45274
|
+
ttfb_ms: exports_external.number().optional().describe("Time to First Byte budget in ms (good: <800)"),
|
|
45275
|
+
dom_complete_ms: exports_external.number().optional().describe("DOM complete budget in ms"),
|
|
45276
|
+
load_event_ms: exports_external.number().optional().describe("Load event budget in ms"),
|
|
45277
|
+
js_heap_mb: exports_external.number().optional().describe("JS heap size budget in MB")
|
|
45278
|
+
}, async ({ session_id, lcp_ms, fcp_ms, cls, ttfb_ms, dom_complete_ms, load_event_ms, js_heap_mb }) => {
|
|
45279
|
+
try {
|
|
45280
|
+
const sid = resolveSessionId(session_id);
|
|
45281
|
+
const page = getSessionPage(sid);
|
|
45282
|
+
const metrics = await getPerformanceMetrics(page);
|
|
45283
|
+
const checks = [];
|
|
45284
|
+
let allPassed = true;
|
|
45285
|
+
const check = (name, budget, actual) => {
|
|
45286
|
+
if (budget === undefined)
|
|
45287
|
+
return;
|
|
45288
|
+
const passed = actual !== undefined && actual <= budget;
|
|
45289
|
+
if (!passed)
|
|
45290
|
+
allPassed = false;
|
|
45291
|
+
checks.push({ metric: name, budget, actual, passed });
|
|
45292
|
+
};
|
|
45293
|
+
check("lcp", lcp_ms, metrics.lcp);
|
|
45294
|
+
check("fcp", fcp_ms, metrics.fcp);
|
|
45295
|
+
check("cls", cls, metrics.cls);
|
|
45296
|
+
check("ttfb", ttfb_ms, metrics.ttfb);
|
|
45297
|
+
check("dom_complete", dom_complete_ms, metrics.dom_complete);
|
|
45298
|
+
check("load_event", load_event_ms, metrics.load_event);
|
|
45299
|
+
if (js_heap_mb !== undefined && metrics.js_heap_size_used !== undefined) {
|
|
45300
|
+
const heapMb = metrics.js_heap_size_used / (1024 * 1024);
|
|
45301
|
+
const passed = heapMb <= js_heap_mb;
|
|
45302
|
+
if (!passed)
|
|
45303
|
+
allPassed = false;
|
|
45304
|
+
checks.push({ metric: "js_heap_mb", budget: js_heap_mb, actual: Math.round(heapMb * 100) / 100, passed });
|
|
45305
|
+
}
|
|
45306
|
+
return json({
|
|
45307
|
+
passed: allPassed,
|
|
45308
|
+
checks,
|
|
45309
|
+
metrics,
|
|
45310
|
+
url: page.url()
|
|
45311
|
+
});
|
|
45312
|
+
} catch (e) {
|
|
45313
|
+
return err(e);
|
|
45314
|
+
}
|
|
45315
|
+
});
|
|
45129
45316
|
}
|
|
45130
45317
|
|
|
45131
45318
|
// src/mcp/recordings.ts
|
|
@@ -45225,7 +45412,7 @@ function register5(server) {
|
|
|
45225
45412
|
max_pages: exports_external.number().optional().default(50),
|
|
45226
45413
|
same_domain: exports_external.boolean().optional().default(true),
|
|
45227
45414
|
project_id: exports_external.string().optional(),
|
|
45228
|
-
engine: exports_external.enum(["playwright", "cdp", "lightpanda", "bun", "auto"]).optional().default("auto")
|
|
45415
|
+
engine: exports_external.enum(["playwright", "cdp", "lightpanda", "bun", "tui", "auto"]).optional().default("auto")
|
|
45229
45416
|
}, async ({ url, max_depth, max_pages, same_domain, project_id, engine }) => {
|
|
45230
45417
|
try {
|
|
45231
45418
|
const result = await crawl(url, {
|
|
@@ -45300,6 +45487,19 @@ function register5(server) {
|
|
|
45300
45487
|
return err(e);
|
|
45301
45488
|
}
|
|
45302
45489
|
});
|
|
45490
|
+
server.tool("browser_record_export", "Export a recording as a Playwright test (.spec.ts), Puppeteer script, or JSON. Returns the generated code as text.", {
|
|
45491
|
+
recording_id: exports_external.string().describe("ID of the recording to export"),
|
|
45492
|
+
format: exports_external.enum(["playwright", "puppeteer", "json"]).optional().default("playwright").describe("Export format")
|
|
45493
|
+
}, async ({ recording_id, format }) => {
|
|
45494
|
+
try {
|
|
45495
|
+
const { exportRecording: exportRecording2 } = await Promise.resolve().then(() => (init_recorder(), exports_recorder));
|
|
45496
|
+
const code = exportRecording2(recording_id, format);
|
|
45497
|
+
const ext = format === "json" ? ".json" : format === "playwright" ? ".spec.ts" : ".js";
|
|
45498
|
+
return json({ format, filename: `recording-${recording_id}${ext}`, code });
|
|
45499
|
+
} catch (e) {
|
|
45500
|
+
return err(e);
|
|
45501
|
+
}
|
|
45502
|
+
});
|
|
45303
45503
|
}
|
|
45304
45504
|
|
|
45305
45505
|
// src/mcp/scripts.ts
|
|
@@ -45307,7 +45507,7 @@ function register6(server) {
|
|
|
45307
45507
|
server.tool("browser_script_run", "Run a saved script asynchronously. Returns run_id immediately \u2014 poll with browser_script_status for step-by-step progress. Scripts combine browser actions + connector calls + AI reasoning. Works with any engine (Bun.WebView, Playwright, CDP).", {
|
|
45308
45508
|
name: exports_external.string().describe("Script name"),
|
|
45309
45509
|
session_id: exports_external.string().optional(),
|
|
45310
|
-
engine: exports_external.enum(["playwright", "cdp", "lightpanda", "bun", "auto"]).optional().default("auto"),
|
|
45510
|
+
engine: exports_external.enum(["playwright", "cdp", "lightpanda", "bun", "tui", "auto"]).optional().default("auto"),
|
|
45311
45511
|
variables: exports_external.record(exports_external.string()).optional().describe("Override script variables")
|
|
45312
45512
|
}, async ({ name, session_id, engine, variables }) => {
|
|
45313
45513
|
try {
|
|
@@ -45712,7 +45912,8 @@ function register7(server) {
|
|
|
45712
45912
|
{ tool: "browser_screenshot", description: "Take a screenshot (PNG/JPEG/WebP, annotate=true for labels)" },
|
|
45713
45913
|
{ tool: "browser_pdf", description: "Generate a PDF of the page" },
|
|
45714
45914
|
{ tool: "browser_scroll_and_screenshot", description: "Scroll then screenshot in one call" },
|
|
45715
|
-
{ tool: "browser_scroll_to_element", description: "Scroll element into view + screenshot" }
|
|
45915
|
+
{ tool: "browser_scroll_to_element", description: "Scroll element into view + screenshot" },
|
|
45916
|
+
{ tool: "browser_diff", description: "Visual diff between two URLs \u2014 highlights changes in red" }
|
|
45716
45917
|
],
|
|
45717
45918
|
Storage: [
|
|
45718
45919
|
{ tool: "browser_cookies_get", description: "Get cookies" },
|
|
@@ -45737,7 +45938,8 @@ function register7(server) {
|
|
|
45737
45938
|
{ tool: "browser_intercept_clear", description: "Remove all response intercepts" }
|
|
45738
45939
|
],
|
|
45739
45940
|
Performance: [
|
|
45740
|
-
{ tool: "browser_performance", description: "Get performance metrics" }
|
|
45941
|
+
{ tool: "browser_performance", description: "Get performance metrics" },
|
|
45942
|
+
{ tool: "browser_performance_budget", description: "Check perf against budget thresholds (LCP, FCP, CLS, TTFB)" }
|
|
45741
45943
|
],
|
|
45742
45944
|
Console: [
|
|
45743
45945
|
{ tool: "browser_console_log", description: "Get console messages" },
|
|
@@ -45750,6 +45952,7 @@ function register7(server) {
|
|
|
45750
45952
|
{ tool: "browser_record_step", description: "Add a step to recording" },
|
|
45751
45953
|
{ tool: "browser_record_stop", description: "Stop and save recording" },
|
|
45752
45954
|
{ tool: "browser_record_replay", description: "Replay a recorded sequence" },
|
|
45955
|
+
{ tool: "browser_record_export", description: "Export recording as Playwright test, Puppeteer script, or JSON" },
|
|
45753
45956
|
{ tool: "browser_recordings_list", description: "List all recordings" }
|
|
45754
45957
|
],
|
|
45755
45958
|
Auth: [
|