@hasna/browser 0.3.7 → 0.4.0

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.
@@ -3,44 +3,41 @@ var __create = Object.create;
3
3
  var __getProtoOf = Object.getPrototypeOf;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- function __accessProp(key) {
8
- return this[key];
9
- }
10
- var __toESMCache_node;
11
- var __toESMCache_esm;
12
8
  var __toESM = (mod, isNodeMode, target) => {
13
- var canCache = mod != null && typeof mod === "object";
14
- if (canCache) {
15
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
- var cached = cache.get(mod);
17
- if (cached)
18
- return cached;
19
- }
20
9
  target = mod != null ? __create(__getProtoOf(mod)) : {};
21
10
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
11
  for (let key of __getOwnPropNames(mod))
23
12
  if (!__hasOwnProp.call(to, key))
24
13
  __defProp(to, key, {
25
- get: __accessProp.bind(mod, key),
14
+ get: () => mod[key],
26
15
  enumerable: true
27
16
  });
28
- if (canCache)
29
- cache.set(mod, to);
30
17
  return to;
31
18
  };
19
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
20
+ var __toCommonJS = (from) => {
21
+ var entry = __moduleCache.get(from), desc;
22
+ if (entry)
23
+ return entry;
24
+ entry = __defProp({}, "__esModule", { value: true });
25
+ if (from && typeof from === "object" || typeof from === "function")
26
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
27
+ get: () => from[key],
28
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
29
+ }));
30
+ __moduleCache.set(from, entry);
31
+ return entry;
32
+ };
32
33
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
- var __returnValue = (v) => v;
34
- function __exportSetter(name, newValue) {
35
- this[name] = __returnValue.bind(null, newValue);
36
- }
37
34
  var __export = (target, all) => {
38
35
  for (var name in all)
39
36
  __defProp(target, name, {
40
37
  get: all[name],
41
38
  enumerable: true,
42
39
  configurable: true,
43
- set: __exportSetter.bind(all, name)
40
+ set: (newValue) => all[name] = () => newValue
44
41
  });
45
42
  };
46
43
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -124,11 +121,11 @@ import { homedir as homedir4 } from "os";
124
121
  import { join as join4 } from "path";
125
122
  import { join as join6, dirname } from "path";
126
123
  import { homedir as homedir5, platform } from "os";
127
- function __accessProp2(key) {
124
+ function __accessProp(key) {
128
125
  return this[key];
129
126
  }
130
- function __exportSetter2(name, newValue) {
131
- this[name] = __returnValue2.bind(null, newValue);
127
+ function __exportSetter(name, newValue) {
128
+ this[name] = __returnValue.bind(null, newValue);
132
129
  }
133
130
  function translateSql(sql, dialect) {
134
131
  if (dialect === "sqlite")
@@ -1158,10 +1155,10 @@ class SyncProgressTracker {
1158
1155
  }
1159
1156
  }
1160
1157
  }
1161
- var __create2, __getProtoOf2, __defProp2, __getOwnPropNames2, __hasOwnProp2, __toESMCache_node2, __toESMCache_esm2, __toESM2 = (mod, isNodeMode, target) => {
1158
+ var __create2, __getProtoOf2, __defProp2, __getOwnPropNames2, __hasOwnProp2, __toESMCache_node, __toESMCache_esm, __toESM2 = (mod, isNodeMode, target) => {
1162
1159
  var canCache = mod != null && typeof mod === "object";
1163
1160
  if (canCache) {
1164
- var cache = isNodeMode ? __toESMCache_node2 ??= new WeakMap : __toESMCache_esm2 ??= new WeakMap;
1161
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
1165
1162
  var cached = cache.get(mod);
1166
1163
  if (cached)
1167
1164
  return cached;
@@ -1171,19 +1168,19 @@ var __create2, __getProtoOf2, __defProp2, __getOwnPropNames2, __hasOwnProp2, __t
1171
1168
  for (let key of __getOwnPropNames2(mod))
1172
1169
  if (!__hasOwnProp2.call(to, key))
1173
1170
  __defProp2(to, key, {
1174
- get: __accessProp2.bind(mod, key),
1171
+ get: __accessProp.bind(mod, key),
1175
1172
  enumerable: true
1176
1173
  });
1177
1174
  if (canCache)
1178
1175
  cache.set(mod, to);
1179
1176
  return to;
1180
- }, __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports), __returnValue2 = (v) => v, __export2 = (target, all) => {
1177
+ }, __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports), __returnValue = (v) => v, __export2 = (target, all) => {
1181
1178
  for (var name in all)
1182
1179
  __defProp2(target, name, {
1183
1180
  get: all[name],
1184
1181
  enumerable: true,
1185
1182
  configurable: true,
1186
- set: __exportSetter2.bind(all, name)
1183
+ set: __exportSetter.bind(all, name)
1187
1184
  });
1188
1185
  }, __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, types, Query, DatabaseError, escapeIdentifier, escapeLiteral, Result, TypeOverrides, defaults, esm_default, init_esm, init_adapter, util, objectUtil, ZodParsedType, getParsedType = (data) => {
1189
1186
  const t = typeof data;
@@ -9487,6 +9484,12 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
9487
9484
  });
9488
9485
 
9489
9486
  // src/db/schema.ts
9487
+ var exports_schema = {};
9488
+ __export(exports_schema, {
9489
+ resetDatabase: () => resetDatabase,
9490
+ getDatabase: () => getDatabase,
9491
+ getDataDir: () => getDataDir2
9492
+ });
9490
9493
  import { join as join5 } from "path";
9491
9494
  import { mkdirSync as mkdirSync3, existsSync as existsSync4, readdirSync as readdirSync3, copyFileSync as copyFileSync2, statSync } from "fs";
9492
9495
  import { homedir as homedir6 } from "os";
@@ -9531,6 +9534,15 @@ function getDatabase(path) {
9531
9534
  runMigrations(_db);
9532
9535
  return _db;
9533
9536
  }
9537
+ function resetDatabase() {
9538
+ if (_db) {
9539
+ try {
9540
+ _db.close();
9541
+ } catch {}
9542
+ }
9543
+ _db = null;
9544
+ _dbPath = null;
9545
+ }
9534
9546
  function runMigrations(db) {
9535
9547
  db.exec(`
9536
9548
  CREATE TABLE IF NOT EXISTS schema_migrations (
@@ -9884,6 +9896,94 @@ var init_console_log = __esm(() => {
9884
9896
  init_schema();
9885
9897
  });
9886
9898
 
9899
+ // src/lib/dialogs.ts
9900
+ var exports_dialogs = {};
9901
+ __export(exports_dialogs, {
9902
+ setupDialogHandler: () => setupDialogHandler,
9903
+ handleDialog: () => handleDialog,
9904
+ getDialogs: () => getDialogs,
9905
+ clearDialogs: () => clearDialogs
9906
+ });
9907
+ function setupDialogHandler(page, sessionId) {
9908
+ const onDialog = (dialog) => {
9909
+ const info = {
9910
+ type: dialog.type(),
9911
+ message: dialog.message(),
9912
+ default_value: dialog.defaultValue(),
9913
+ timestamp: new Date().toISOString()
9914
+ };
9915
+ const autoTimer = setTimeout(() => {
9916
+ try {
9917
+ dialog.dismiss().catch(() => {});
9918
+ } catch {}
9919
+ const list = pendingDialogs.get(sessionId);
9920
+ if (list) {
9921
+ const idx = list.findIndex((p) => p.dialog === dialog);
9922
+ if (idx >= 0)
9923
+ list.splice(idx, 1);
9924
+ if (list.length === 0)
9925
+ pendingDialogs.delete(sessionId);
9926
+ }
9927
+ }, AUTO_DISMISS_MS);
9928
+ const pending = { dialog, info, autoTimer };
9929
+ if (!pendingDialogs.has(sessionId)) {
9930
+ pendingDialogs.set(sessionId, []);
9931
+ }
9932
+ pendingDialogs.get(sessionId).push(pending);
9933
+ };
9934
+ page.on("dialog", onDialog);
9935
+ return () => {
9936
+ page.off("dialog", onDialog);
9937
+ const list = pendingDialogs.get(sessionId);
9938
+ if (list) {
9939
+ for (const p of list)
9940
+ clearTimeout(p.autoTimer);
9941
+ pendingDialogs.delete(sessionId);
9942
+ }
9943
+ };
9944
+ }
9945
+ function getDialogs(sessionId) {
9946
+ const list = pendingDialogs.get(sessionId);
9947
+ if (!list)
9948
+ return [];
9949
+ return list.map((p) => p.info);
9950
+ }
9951
+ async function handleDialog(sessionId, action, promptText) {
9952
+ const list = pendingDialogs.get(sessionId);
9953
+ if (!list || list.length === 0) {
9954
+ return { handled: false };
9955
+ }
9956
+ const pending = list.shift();
9957
+ clearTimeout(pending.autoTimer);
9958
+ if (list.length === 0) {
9959
+ pendingDialogs.delete(sessionId);
9960
+ }
9961
+ try {
9962
+ if (action === "accept") {
9963
+ await pending.dialog.accept(promptText);
9964
+ } else {
9965
+ await pending.dialog.dismiss();
9966
+ }
9967
+ } catch {}
9968
+ return { handled: true, dialog: pending.info };
9969
+ }
9970
+ function clearDialogs(sessionId) {
9971
+ const list = pendingDialogs.get(sessionId);
9972
+ if (list) {
9973
+ for (const p of list) {
9974
+ clearTimeout(p.autoTimer);
9975
+ try {
9976
+ p.dialog.dismiss().catch(() => {});
9977
+ } catch {}
9978
+ }
9979
+ pendingDialogs.delete(sessionId);
9980
+ }
9981
+ }
9982
+ var pendingDialogs, AUTO_DISMISS_MS = 5000;
9983
+ var init_dialogs = __esm(() => {
9984
+ pendingDialogs = new Map;
9985
+ });
9986
+
9887
9987
  // src/engines/cdp.ts
9888
9988
  var exports_cdp = {};
9889
9989
  __export(exports_cdp, {
@@ -10066,133 +10166,887 @@ var init_storage_state = __esm(() => {
10066
10166
  STATES_DIR = join8(getDataDir2(), "states");
10067
10167
  });
10068
10168
 
10069
- // node_modules/sharp/lib/is.js
10070
- var require_is = __commonJS((exports, module) => {
10071
- /*!
10072
- Copyright 2013 Lovell Fuller and others.
10073
- SPDX-License-Identifier: Apache-2.0
10074
- */
10075
- var defined = (val) => typeof val !== "undefined" && val !== null;
10076
- var object = (val) => typeof val === "object";
10077
- var plainObject = (val) => Object.prototype.toString.call(val) === "[object Object]";
10078
- var fn = (val) => typeof val === "function";
10079
- var bool = (val) => typeof val === "boolean";
10080
- var buffer = (val) => val instanceof Buffer;
10081
- var typedArray = (val) => {
10082
- if (defined(val)) {
10083
- switch (val.constructor) {
10084
- case Uint8Array:
10085
- case Uint8ClampedArray:
10086
- case Int8Array:
10087
- case Uint16Array:
10088
- case Int16Array:
10089
- case Uint32Array:
10090
- case Int32Array:
10091
- case Float32Array:
10092
- case Float64Array:
10093
- return true;
10094
- }
10095
- }
10096
- return false;
10097
- };
10098
- var arrayBuffer = (val) => val instanceof ArrayBuffer;
10099
- var string = (val) => typeof val === "string" && val.length > 0;
10100
- var number = (val) => typeof val === "number" && !Number.isNaN(val);
10101
- var integer = (val) => Number.isInteger(val);
10102
- var inRange = (val, min, max) => val >= min && val <= max;
10103
- var inArray = (val, list) => list.includes(val);
10104
- var invalidParameterError = (name, expected, actual) => new Error(`Expected ${expected} for ${name} but received ${actual} of type ${typeof actual}`);
10105
- var nativeError = (native, context) => {
10106
- context.message = native.message;
10107
- return context;
10108
- };
10109
- module.exports = {
10110
- defined,
10111
- object,
10112
- plainObject,
10113
- fn,
10114
- bool,
10115
- buffer,
10116
- typedArray,
10117
- arrayBuffer,
10118
- string,
10119
- number,
10120
- integer,
10121
- inRange,
10122
- inArray,
10123
- invalidParameterError,
10124
- nativeError
10125
- };
10169
+ // src/lib/snapshot.ts
10170
+ var exports_snapshot = {};
10171
+ __export(exports_snapshot, {
10172
+ takeSnapshot: () => takeSnapshot,
10173
+ takeBunSnapshot: () => takeBunSnapshot,
10174
+ setLastSnapshot: () => setLastSnapshot,
10175
+ hasRefs: () => hasRefs,
10176
+ getSessionRefs: () => getSessionRefs,
10177
+ getRefLocator: () => getRefLocator,
10178
+ getRefInfo: () => getRefInfo,
10179
+ getLastSnapshot: () => getLastSnapshot,
10180
+ diffSnapshots: () => diffSnapshots,
10181
+ clearSessionRefs: () => clearSessionRefs,
10182
+ clearLastSnapshot: () => clearLastSnapshot
10126
10183
  });
10127
-
10128
- // node_modules/detect-libc/lib/process.js
10129
- var require_process = __commonJS((exports, module) => {
10130
- var isLinux = () => process.platform === "linux";
10131
- var report = null;
10132
- var getReport = () => {
10133
- if (!report) {
10134
- if (isLinux() && process.report) {
10135
- const orig = process.report.excludeNetwork;
10136
- process.report.excludeNetwork = true;
10137
- report = process.report.getReport();
10138
- process.report.excludeNetwork = orig;
10139
- } else {
10140
- report = {};
10184
+ function getLastSnapshot(sessionId) {
10185
+ return lastSnapshots.get(sessionId) ?? null;
10186
+ }
10187
+ function setLastSnapshot(sessionId, snapshot) {
10188
+ lastSnapshots.set(sessionId, snapshot);
10189
+ }
10190
+ function clearLastSnapshot(sessionId) {
10191
+ lastSnapshots.delete(sessionId);
10192
+ }
10193
+ async function takeSnapshot(page, sessionId) {
10194
+ const isBunView = typeof page.getNativeView === "function" || typeof page.bunView !== "undefined";
10195
+ if (isBunView) {
10196
+ return takeBunSnapshot(page, sessionId);
10197
+ }
10198
+ let ariaTree;
10199
+ try {
10200
+ ariaTree = await page.locator("body").ariaSnapshot();
10201
+ } catch {
10202
+ ariaTree = "";
10203
+ }
10204
+ const refs = {};
10205
+ const refMap = new Map;
10206
+ let refCounter = 0;
10207
+ for (const role of INTERACTIVE_ROLES) {
10208
+ const locators = page.getByRole(role);
10209
+ const count = await locators.count();
10210
+ for (let i = 0;i < count; i++) {
10211
+ const el = locators.nth(i);
10212
+ let name = "";
10213
+ let visible = false;
10214
+ let enabled = true;
10215
+ let value;
10216
+ let checked;
10217
+ try {
10218
+ visible = await el.isVisible();
10219
+ if (!visible)
10220
+ continue;
10221
+ } catch {
10222
+ continue;
10223
+ }
10224
+ try {
10225
+ name = await el.evaluate((e) => {
10226
+ const el2 = e;
10227
+ return el2.getAttribute("aria-label") ?? el2.textContent?.trim().slice(0, 80) ?? el2.getAttribute("title") ?? el2.getAttribute("placeholder") ?? "";
10228
+ });
10229
+ } catch {
10230
+ continue;
10141
10231
  }
10232
+ if (!name)
10233
+ continue;
10234
+ try {
10235
+ enabled = await el.isEnabled();
10236
+ } catch {}
10237
+ try {
10238
+ if (role === "checkbox" || role === "radio" || role === "switch") {
10239
+ checked = await el.isChecked();
10240
+ }
10241
+ } catch {}
10242
+ try {
10243
+ if (role === "textbox" || role === "searchbox" || role === "spinbutton" || role === "combobox") {
10244
+ value = await el.inputValue();
10245
+ }
10246
+ } catch {}
10247
+ const ref = `@e${refCounter}`;
10248
+ refCounter++;
10249
+ refs[ref] = { role, name, visible, enabled, value, checked };
10250
+ const escapedName = name.replace(/"/g, "\\\"");
10251
+ refMap.set(ref, { role, name, locatorSelector: `role=${role}[name="${escapedName}"]` });
10252
+ }
10253
+ }
10254
+ let annotatedTree = ariaTree;
10255
+ for (const [ref, info] of Object.entries(refs)) {
10256
+ const escapedName = info.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10257
+ const pattern = new RegExp(`(${info.role}\\s+"${escapedName.slice(0, 40)}[^"]*")`, "m");
10258
+ const match = annotatedTree.match(pattern);
10259
+ if (match) {
10260
+ annotatedTree = annotatedTree.replace(match[0], `${match[0]} [${ref}]`);
10142
10261
  }
10143
- return report;
10144
- };
10145
- module.exports = { isLinux, getReport };
10146
- });
10262
+ }
10263
+ const unmatchedRefs = Object.entries(refs).filter(([ref]) => !annotatedTree.includes(`[${ref}]`));
10264
+ if (unmatchedRefs.length > 0) {
10265
+ annotatedTree += `
10147
10266
 
10148
- // node_modules/detect-libc/lib/filesystem.js
10149
- var require_filesystem = __commonJS((exports, module) => {
10150
- var fs = __require("fs");
10151
- var LDD_PATH = "/usr/bin/ldd";
10152
- var SELF_PATH = "/proc/self/exe";
10153
- var MAX_LENGTH = 2048;
10154
- var readFileSync2 = (path) => {
10155
- const fd = fs.openSync(path, "r");
10156
- const buffer = Buffer.alloc(MAX_LENGTH);
10157
- const bytesRead = fs.readSync(fd, buffer, 0, MAX_LENGTH, 0);
10158
- fs.close(fd, () => {});
10159
- return buffer.subarray(0, bytesRead);
10267
+ --- Interactive elements ---`;
10268
+ for (const [ref, info] of unmatchedRefs) {
10269
+ const extras = [];
10270
+ if (info.checked !== undefined)
10271
+ extras.push(`checked=${info.checked}`);
10272
+ if (!info.enabled)
10273
+ extras.push("disabled");
10274
+ if (info.value)
10275
+ extras.push(`value="${info.value}"`);
10276
+ const extrasStr = extras.length ? ` (${extras.join(", ")})` : "";
10277
+ annotatedTree += `
10278
+ ${info.role} "${info.name}" [${ref}]${extrasStr}`;
10279
+ }
10280
+ }
10281
+ if (sessionId) {
10282
+ sessionRefMaps.set(sessionId, refMap);
10283
+ }
10284
+ return {
10285
+ tree: annotatedTree,
10286
+ refs,
10287
+ interactive_count: refCounter
10160
10288
  };
10161
- var readFile = (path) => new Promise((resolve, reject) => {
10162
- fs.open(path, "r", (err, fd) => {
10163
- if (err) {
10164
- reject(err);
10165
- } else {
10166
- const buffer = Buffer.alloc(MAX_LENGTH);
10167
- fs.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
10168
- resolve(buffer.subarray(0, bytesRead));
10169
- fs.close(fd, () => {});
10170
- });
10289
+ }
10290
+ function getRefLocator(page, sessionId, ref) {
10291
+ const refMap = sessionRefMaps.get(sessionId);
10292
+ if (!refMap)
10293
+ throw new Error(`No snapshot taken for session ${sessionId}. Call browser_snapshot first.`);
10294
+ const entry = refMap.get(ref);
10295
+ if (!entry)
10296
+ throw new Error(`Ref ${ref} not found. Available refs: ${[...refMap.keys()].slice(0, 20).join(", ")}`);
10297
+ return page.getByRole(entry.role, { name: entry.name }).first();
10298
+ }
10299
+ function getRefInfo(sessionId, ref) {
10300
+ const refMap = sessionRefMaps.get(sessionId);
10301
+ if (!refMap)
10302
+ return null;
10303
+ return refMap.get(ref) ?? null;
10304
+ }
10305
+ function getSessionRefs(sessionId) {
10306
+ return sessionRefMaps.get(sessionId) ?? null;
10307
+ }
10308
+ function clearSessionRefs(sessionId) {
10309
+ sessionRefMaps.delete(sessionId);
10310
+ }
10311
+ function hasRefs(sessionId) {
10312
+ return sessionRefMaps.has(sessionId) && (sessionRefMaps.get(sessionId)?.size ?? 0) > 0;
10313
+ }
10314
+ function refKey(info) {
10315
+ return `${info.role}::${info.name}`;
10316
+ }
10317
+ function diffSnapshots(before, after) {
10318
+ const beforeMap = new Map;
10319
+ for (const [ref, info] of Object.entries(before.refs)) {
10320
+ beforeMap.set(refKey(info), { ref, info });
10321
+ }
10322
+ const afterMap = new Map;
10323
+ for (const [ref, info] of Object.entries(after.refs)) {
10324
+ afterMap.set(refKey(info), { ref, info });
10325
+ }
10326
+ const added = [];
10327
+ const removed = [];
10328
+ const modified = [];
10329
+ for (const [key, afterEntry] of afterMap) {
10330
+ const beforeEntry = beforeMap.get(key);
10331
+ if (!beforeEntry) {
10332
+ added.push({ ref: afterEntry.ref, info: afterEntry.info });
10333
+ } else {
10334
+ const b = beforeEntry.info;
10335
+ const a = afterEntry.info;
10336
+ if (b.visible !== a.visible || b.enabled !== a.enabled || b.value !== a.value || b.checked !== a.checked || b.description !== a.description) {
10337
+ modified.push({ ref: afterEntry.ref, before: b, after: a });
10171
10338
  }
10172
- });
10173
- });
10174
- module.exports = {
10175
- LDD_PATH,
10176
- SELF_PATH,
10177
- readFileSync: readFileSync2,
10178
- readFile
10179
- };
10180
- });
10181
-
10182
- // node_modules/detect-libc/lib/elf.js
10183
- var require_elf = __commonJS((exports, module) => {
10184
- var interpreterPath = (elf) => {
10185
- if (elf.length < 64) {
10186
- return null;
10187
10339
  }
10188
- if (elf.readUInt32BE(0) !== 2135247942) {
10189
- return null;
10190
- }
10191
- if (elf.readUInt8(4) !== 2) {
10192
- return null;
10340
+ }
10341
+ for (const [key, beforeEntry] of beforeMap) {
10342
+ if (!afterMap.has(key)) {
10343
+ removed.push({ ref: beforeEntry.ref, info: beforeEntry.info });
10193
10344
  }
10194
- if (elf.readUInt8(5) !== 1) {
10195
- return null;
10345
+ }
10346
+ const url_changed = before.tree.split(`
10347
+ `)[0] !== after.tree.split(`
10348
+ `)[0];
10349
+ const title_changed = before.tree !== after.tree && (added.length > 0 || removed.length > 0 || modified.length > 0);
10350
+ return { added, removed, modified, url_changed, title_changed };
10351
+ }
10352
+ async function takeBunSnapshot(page, sessionId) {
10353
+ const refs = {};
10354
+ const refMap = new Map;
10355
+ let refCounter = 0;
10356
+ const lines = [];
10357
+ try {
10358
+ const elements = await page.evaluate(`
10359
+ (() => {
10360
+ const SELECTOR = 'a[href], button, input:not([type=hidden]), select, textarea, [role=button], [role=link], [role=checkbox], [role=combobox], [role=menuitem], [role=tab], [role=option]';
10361
+ const els = Array.from(document.querySelectorAll(SELECTOR));
10362
+ return els.slice(0, 100).map(el => {
10363
+ const tag = el.tagName.toLowerCase();
10364
+ const inputType = el.getAttribute('type') ?? '';
10365
+ let role = el.getAttribute('role') || (['a'].includes(tag) ? 'link' : ['button'].includes(tag) ? 'button' : ['input'].includes(tag) ? (inputType === 'checkbox' ? 'checkbox' : inputType === 'radio' ? 'radio' : 'textbox') : ['select'].includes(tag) ? 'combobox' : ['textarea'].includes(tag) ? 'textbox' : tag);
10366
+ const name = (el.getAttribute('aria-label') || el.textContent?.trim() || el.getAttribute('placeholder') || el.getAttribute('title') || el.getAttribute('value') || el.id || '').slice(0, 80);
10367
+ const enabled = !el.disabled && !el.getAttribute('disabled');
10368
+ const style = window.getComputedStyle(el);
10369
+ const visible = style.display !== 'none' && style.visibility !== 'hidden' && el.offsetWidth > 0;
10370
+ const checked = el.type === 'checkbox' || el.type === 'radio' ? el.checked : undefined;
10371
+ const value = ['input', 'select', 'textarea'].includes(tag) && el.type !== 'checkbox' && el.type !== 'radio' ? el.value : undefined;
10372
+ const selector = el.id ? '#' + el.id : (el.getAttribute('aria-label') ? '[aria-label="' + el.getAttribute('aria-label') + '"]' : tag);
10373
+ return { role, name, enabled, visible, checked, value, selector };
10374
+ }).filter(e => e.visible && e.name);
10375
+ })()
10376
+ `);
10377
+ const pageTitle = await page.evaluate("document.title");
10378
+ const pageUrl = typeof page.url === "function" ? page.url() : "";
10379
+ lines.push(`# ${pageTitle || "Page"} (${pageUrl})`);
10380
+ for (const el of elements) {
10381
+ if (!el.name)
10382
+ continue;
10383
+ const ref = `@e${refCounter}`;
10384
+ refCounter++;
10385
+ refs[ref] = {
10386
+ role: el.role,
10387
+ name: el.name,
10388
+ visible: el.visible,
10389
+ enabled: el.enabled,
10390
+ value: el.value,
10391
+ checked: el.checked
10392
+ };
10393
+ refMap.set(ref, { role: el.role, name: el.name, locatorSelector: el.selector });
10394
+ const extras = [];
10395
+ if (el.checked !== undefined)
10396
+ extras.push(`checked=${el.checked}`);
10397
+ if (!el.enabled)
10398
+ extras.push("disabled");
10399
+ if (el.value && el.value !== el.name)
10400
+ extras.push(`value="${el.value.slice(0, 30)}"`);
10401
+ const extrasStr = extras.length ? ` (${extras.join(", ")})` : "";
10402
+ lines.push(`${el.role} "${el.name}" [${ref}]${extrasStr}`);
10403
+ }
10404
+ } catch (err) {
10405
+ lines.push(`# (snapshot error: ${err instanceof Error ? err.message : String(err)})`);
10406
+ }
10407
+ if (sessionId) {
10408
+ sessionRefMaps.set(sessionId, refMap);
10409
+ }
10410
+ return {
10411
+ tree: lines.join(`
10412
+ `),
10413
+ refs,
10414
+ interactive_count: refCounter
10415
+ };
10416
+ }
10417
+ var lastSnapshots, sessionRefMaps, INTERACTIVE_ROLES;
10418
+ var init_snapshot = __esm(() => {
10419
+ lastSnapshots = new Map;
10420
+ sessionRefMaps = new Map;
10421
+ INTERACTIVE_ROLES = [
10422
+ "button",
10423
+ "link",
10424
+ "textbox",
10425
+ "checkbox",
10426
+ "radio",
10427
+ "combobox",
10428
+ "menuitem",
10429
+ "menuitemcheckbox",
10430
+ "menuitemradio",
10431
+ "option",
10432
+ "searchbox",
10433
+ "slider",
10434
+ "spinbutton",
10435
+ "switch",
10436
+ "tab",
10437
+ "treeitem",
10438
+ "listbox",
10439
+ "menu"
10440
+ ];
10441
+ });
10442
+
10443
+ // src/lib/self-heal.ts
10444
+ async function healSelector(page, selector, sessionId) {
10445
+ const attempts = [];
10446
+ attempts.push(`selector: ${selector}`);
10447
+ try {
10448
+ const loc = page.locator(selector).first();
10449
+ if (await loc.count() > 0) {
10450
+ return { found: true, locator: loc, method: "original", healed: false, attempts };
10451
+ }
10452
+ } catch {}
10453
+ if (!selector.startsWith("#") && !selector.startsWith(".") && !selector.startsWith("[") && !selector.includes(">") && !selector.includes(" ")) {
10454
+ attempts.push(`text: "${selector}"`);
10455
+ try {
10456
+ const loc = page.getByText(selector, { exact: false }).first();
10457
+ if (await loc.count() > 0) {
10458
+ return { found: true, locator: loc, method: "text", healed: true, attempts };
10459
+ }
10460
+ } catch {}
10461
+ }
10462
+ const roleMap = {
10463
+ button: ["button", "submit", "reset"],
10464
+ link: ["a"],
10465
+ input: ["input", "textarea"],
10466
+ heading: ["h1", "h2", "h3", "h4", "h5", "h6"]
10467
+ };
10468
+ const nameHint = selector.replace(/^[#.]/, "").replace(/[-_]/g, " ").toLowerCase();
10469
+ for (const [role, tags] of Object.entries(roleMap)) {
10470
+ attempts.push(`role: ${role} name~="${nameHint}"`);
10471
+ try {
10472
+ const loc = page.getByRole(role, { name: new RegExp(nameHint.split(" ")[0], "i") }).first();
10473
+ if (await loc.count() > 0) {
10474
+ return { found: true, locator: loc, method: "role", healed: true, attempts };
10475
+ }
10476
+ } catch {}
10477
+ }
10478
+ if (selector.startsWith("#")) {
10479
+ const idPart = selector.slice(1).split("-").pop() ?? selector.slice(1);
10480
+ const partialSel = `[id*="${idPart}"]`;
10481
+ attempts.push(`partial_id: ${partialSel}`);
10482
+ try {
10483
+ const loc = page.locator(partialSel).first();
10484
+ if (await loc.count() > 0) {
10485
+ return { found: true, locator: loc, method: "partial_id", healed: true, attempts };
10486
+ }
10487
+ } catch {}
10488
+ }
10489
+ if (selector.startsWith(".")) {
10490
+ const classPart = selector.slice(1).split("-").pop() ?? selector.slice(1);
10491
+ const partialSel = `[class*="${classPart}"]`;
10492
+ attempts.push(`partial_class: ${partialSel}`);
10493
+ try {
10494
+ const loc = page.locator(partialSel).first();
10495
+ if (await loc.count() > 0) {
10496
+ return { found: true, locator: loc, method: "partial_class", healed: true, attempts };
10497
+ }
10498
+ } catch {}
10499
+ }
10500
+ return { found: false, locator: null, method: "none", healed: false, attempts };
10501
+ }
10502
+
10503
+ // src/lib/actions.ts
10504
+ var exports_actions = {};
10505
+ __export(exports_actions, {
10506
+ withRetry: () => withRetry,
10507
+ watchPage: () => watchPage,
10508
+ waitForText: () => waitForText,
10509
+ waitForSelector: () => waitForSelector,
10510
+ waitForNavigation: () => waitForNavigation,
10511
+ uploadFile: () => uploadFile,
10512
+ typeRef: () => typeRef,
10513
+ type: () => type,
10514
+ stopWatch: () => stopWatch,
10515
+ stopAllWatchesForSession: () => stopAllWatchesForSession,
10516
+ selectRef: () => selectRef,
10517
+ selectOption: () => selectOption,
10518
+ scrollTo: () => scrollTo,
10519
+ scroll: () => scroll,
10520
+ reload: () => reload,
10521
+ pressKey: () => pressKey,
10522
+ navigate: () => navigate,
10523
+ hoverRef: () => hoverRef,
10524
+ hover: () => hover,
10525
+ goForward: () => goForward,
10526
+ goBack: () => goBack,
10527
+ getWatchChanges: () => getWatchChanges,
10528
+ fillRef: () => fillRef,
10529
+ fillForm: () => fillForm,
10530
+ fill: () => fill,
10531
+ clickText: () => clickText,
10532
+ clickRef: () => clickRef,
10533
+ click: () => click,
10534
+ checkRef: () => checkRef,
10535
+ checkBox: () => checkBox
10536
+ });
10537
+ async function click(page, selector, opts) {
10538
+ try {
10539
+ await page.click(selector, {
10540
+ button: opts?.button ?? "left",
10541
+ clickCount: opts?.clickCount ?? 1,
10542
+ delay: opts?.delay,
10543
+ timeout: opts?.timeout ?? 1e4
10544
+ });
10545
+ return {};
10546
+ } catch (originalError) {
10547
+ if (opts?.selfHeal !== false) {
10548
+ const result = await healSelector(page, selector);
10549
+ if (result.found && result.locator) {
10550
+ await result.locator.click({
10551
+ button: opts?.button ?? "left",
10552
+ timeout: opts?.timeout ?? 1e4
10553
+ });
10554
+ return { healed: true, method: result.method, attempts: result.attempts };
10555
+ }
10556
+ }
10557
+ if (originalError instanceof Error && originalError.message.includes("not found")) {
10558
+ throw new ElementNotFoundError(selector);
10559
+ }
10560
+ throw new BrowserError(`Click failed on '${selector}': ${originalError instanceof Error ? originalError.message : String(originalError)}`, "CLICK_FAILED");
10561
+ }
10562
+ }
10563
+ async function type(page, selector, text, opts) {
10564
+ try {
10565
+ if (opts?.clear) {
10566
+ await page.fill(selector, "", { timeout: opts?.timeout ?? 1e4 });
10567
+ }
10568
+ await page.type(selector, text, { delay: opts?.delay, timeout: opts?.timeout ?? 1e4 });
10569
+ return {};
10570
+ } catch (originalError) {
10571
+ if (opts?.selfHeal !== false) {
10572
+ const result = await healSelector(page, selector);
10573
+ if (result.found && result.locator) {
10574
+ if (opts?.clear)
10575
+ await result.locator.fill("", { timeout: opts?.timeout ?? 1e4 });
10576
+ await result.locator.pressSequentially(text, { delay: opts?.delay, timeout: opts?.timeout ?? 1e4 });
10577
+ return { healed: true, method: result.method, attempts: result.attempts };
10578
+ }
10579
+ }
10580
+ if (originalError instanceof Error && originalError.message.includes("not found")) {
10581
+ throw new ElementNotFoundError(selector);
10582
+ }
10583
+ throw new BrowserError(`Type failed on '${selector}': ${originalError instanceof Error ? originalError.message : String(originalError)}`, "TYPE_FAILED");
10584
+ }
10585
+ }
10586
+ async function fill(page, selector, value, timeout = 1e4, selfHeal = true) {
10587
+ try {
10588
+ await page.fill(selector, value, { timeout });
10589
+ return {};
10590
+ } catch (originalError) {
10591
+ if (selfHeal) {
10592
+ const result = await healSelector(page, selector);
10593
+ if (result.found && result.locator) {
10594
+ await result.locator.fill(value, { timeout });
10595
+ return { healed: true, method: result.method, attempts: result.attempts };
10596
+ }
10597
+ }
10598
+ throw new ElementNotFoundError(selector);
10599
+ }
10600
+ }
10601
+ async function scroll(page, direction = "down", amount = 300) {
10602
+ const x = direction === "left" ? -amount : direction === "right" ? amount : 0;
10603
+ const y = direction === "up" ? -amount : direction === "down" ? amount : 0;
10604
+ await page.evaluate(({ x: x2, y: y2 }) => window.scrollBy(x2, y2), { x, y });
10605
+ }
10606
+ async function scrollTo(page, selector) {
10607
+ try {
10608
+ await page.locator(selector).scrollIntoViewIfNeeded();
10609
+ } catch (err) {
10610
+ throw new ElementNotFoundError(selector);
10611
+ }
10612
+ }
10613
+ async function hover(page, selector, timeout = 1e4) {
10614
+ try {
10615
+ await page.hover(selector, { timeout });
10616
+ } catch (err) {
10617
+ throw new ElementNotFoundError(selector);
10618
+ }
10619
+ }
10620
+ async function selectOption(page, selector, value, timeout = 1e4) {
10621
+ try {
10622
+ return await page.selectOption(selector, value, { timeout });
10623
+ } catch (err) {
10624
+ throw new ElementNotFoundError(selector);
10625
+ }
10626
+ }
10627
+ async function checkBox(page, selector, checked, timeout = 1e4) {
10628
+ try {
10629
+ if (checked) {
10630
+ await page.check(selector, { timeout });
10631
+ } else {
10632
+ await page.uncheck(selector, { timeout });
10633
+ }
10634
+ } catch (err) {
10635
+ throw new ElementNotFoundError(selector);
10636
+ }
10637
+ }
10638
+ async function uploadFile(page, selector, filePaths, timeout = 1e4) {
10639
+ try {
10640
+ await page.setInputFiles(selector, filePaths, { timeout });
10641
+ } catch (err) {
10642
+ throw new ElementNotFoundError(selector);
10643
+ }
10644
+ }
10645
+ async function goBack(page, timeout = 1e4) {
10646
+ try {
10647
+ await page.goBack({ timeout, waitUntil: "domcontentloaded" });
10648
+ } catch (err) {
10649
+ throw new NavigationError("back", err instanceof Error ? err.message : String(err));
10650
+ }
10651
+ }
10652
+ async function goForward(page, timeout = 1e4) {
10653
+ try {
10654
+ await page.goForward({ timeout, waitUntil: "domcontentloaded" });
10655
+ } catch (err) {
10656
+ throw new NavigationError("forward", err instanceof Error ? err.message : String(err));
10657
+ }
10658
+ }
10659
+ async function reload(page, timeout = 1e4) {
10660
+ try {
10661
+ await page.reload({ timeout, waitUntil: "domcontentloaded" });
10662
+ } catch (err) {
10663
+ throw new NavigationError("reload", err instanceof Error ? err.message : String(err));
10664
+ }
10665
+ }
10666
+ async function navigate(page, url, timeout = 30000) {
10667
+ try {
10668
+ await page.goto(url, { waitUntil: "domcontentloaded", timeout });
10669
+ } catch (err) {
10670
+ throw new NavigationError(url, err instanceof Error ? err.message : String(err));
10671
+ }
10672
+ }
10673
+ async function waitForSelector(page, selector, opts) {
10674
+ try {
10675
+ await page.waitForSelector(selector, {
10676
+ state: opts?.state ?? "visible",
10677
+ timeout: opts?.timeout ?? 1e4
10678
+ });
10679
+ } catch (err) {
10680
+ throw new ElementNotFoundError(selector);
10681
+ }
10682
+ }
10683
+ async function waitForNavigation(page, timeout = 30000) {
10684
+ try {
10685
+ await page.waitForLoadState("domcontentloaded", { timeout });
10686
+ } catch (err) {
10687
+ throw new NavigationError("navigation", err instanceof Error ? err.message : String(err));
10688
+ }
10689
+ }
10690
+ async function pressKey(page, key) {
10691
+ await page.keyboard.press(key);
10692
+ }
10693
+ async function withRetry(fn, opts) {
10694
+ const retries = opts?.retries ?? 2;
10695
+ const delay = opts?.delay ?? 300;
10696
+ const retryOn = opts?.retryOn ?? RETRYABLE_ERRORS;
10697
+ let lastErr;
10698
+ for (let attempt = 0;attempt <= retries; attempt++) {
10699
+ try {
10700
+ return await fn();
10701
+ } catch (err) {
10702
+ lastErr = err;
10703
+ const msg = err instanceof Error ? err.message : String(err);
10704
+ const shouldRetry = retryOn.some((pattern) => msg.includes(pattern));
10705
+ if (!shouldRetry || err instanceof ElementNotFoundError)
10706
+ throw err;
10707
+ if (attempt < retries)
10708
+ await new Promise((r) => setTimeout(r, delay));
10709
+ }
10710
+ }
10711
+ throw lastErr;
10712
+ }
10713
+ async function clickText(page, text, opts) {
10714
+ await withRetry(async () => {
10715
+ try {
10716
+ await page.getByText(text, { exact: opts?.exact ?? false }).first().click({ timeout: opts?.timeout ?? 1e4 });
10717
+ } catch (err) {
10718
+ throw new BrowserError(`clickText: could not find or click text "${text}": ${err instanceof Error ? err.message : String(err)}`, "CLICK_TEXT_FAILED");
10719
+ }
10720
+ }, { retries: opts?.retries ?? 1 });
10721
+ }
10722
+ async function fillForm(page, fields, submitSelector, selfHeal = true) {
10723
+ let filled = 0;
10724
+ const errors = [];
10725
+ const healedFields = [];
10726
+ for (const [selector, value] of Object.entries(fields)) {
10727
+ try {
10728
+ let el = await page.$(selector);
10729
+ if (!el && selfHeal) {
10730
+ const result = await healSelector(page, selector);
10731
+ if (result.found && result.locator) {
10732
+ const handle = await result.locator.elementHandle();
10733
+ if (handle) {
10734
+ el = handle;
10735
+ healedFields.push(selector);
10736
+ const tagName2 = await result.locator.evaluate((e) => e.tagName.toLowerCase());
10737
+ const inputType2 = await result.locator.evaluate((e) => e.type?.toLowerCase() ?? "text");
10738
+ if (tagName2 === "select") {
10739
+ await result.locator.selectOption(String(value));
10740
+ } else if (tagName2 === "input" && (inputType2 === "checkbox" || inputType2 === "radio")) {
10741
+ if (Boolean(value))
10742
+ await result.locator.check();
10743
+ else
10744
+ await result.locator.uncheck();
10745
+ } else {
10746
+ await result.locator.fill(String(value));
10747
+ }
10748
+ filled++;
10749
+ continue;
10750
+ }
10751
+ }
10752
+ errors.push(`${selector}: element not found`);
10753
+ continue;
10754
+ }
10755
+ if (!el) {
10756
+ errors.push(`${selector}: element not found`);
10757
+ continue;
10758
+ }
10759
+ const tagName = await el.evaluate((e) => e.tagName.toLowerCase());
10760
+ const inputType = await el.evaluate((e) => e.type?.toLowerCase() ?? "text");
10761
+ if (tagName === "select") {
10762
+ await page.selectOption(selector, String(value));
10763
+ } else if (tagName === "input" && (inputType === "checkbox" || inputType === "radio")) {
10764
+ const checked = Boolean(value);
10765
+ if (checked) {
10766
+ await page.check(selector);
10767
+ } else {
10768
+ await page.uncheck(selector);
10769
+ }
10770
+ } else {
10771
+ await page.fill(selector, String(value));
10772
+ }
10773
+ filled++;
10774
+ } catch (err) {
10775
+ errors.push(`${selector}: ${err instanceof Error ? err.message : String(err)}`);
10776
+ }
10777
+ }
10778
+ if (submitSelector) {
10779
+ try {
10780
+ await page.click(submitSelector);
10781
+ } catch (submitErr) {
10782
+ if (selfHeal) {
10783
+ const result = await healSelector(page, submitSelector);
10784
+ if (result.found && result.locator) {
10785
+ await result.locator.click();
10786
+ healedFields.push(submitSelector);
10787
+ } else {
10788
+ errors.push(`submit(${submitSelector}): ${submitErr instanceof Error ? submitErr.message : String(submitErr)}`);
10789
+ }
10790
+ } else {
10791
+ errors.push(`submit(${submitSelector}): ${submitErr instanceof Error ? submitErr.message : String(submitErr)}`);
10792
+ }
10793
+ }
10794
+ }
10795
+ return { filled, errors, fields_attempted: Object.keys(fields).length, ...healedFields.length > 0 ? { healed_fields: healedFields } : {} };
10796
+ }
10797
+ async function waitForText(page, text, opts) {
10798
+ const timeout = opts?.timeout ?? 1e4;
10799
+ try {
10800
+ await page.getByText(text, { exact: opts?.exact ?? false }).first().waitFor({ state: "visible", timeout });
10801
+ } catch (err) {
10802
+ throw new ElementNotFoundError(`text:"${text}"`);
10803
+ }
10804
+ }
10805
+ function watchPage(page, opts) {
10806
+ const id = `watch-${Date.now()}`;
10807
+ const changes = [];
10808
+ const intervalMs = opts?.intervalMs ?? 500;
10809
+ const maxChanges = opts?.maxChanges ?? 50;
10810
+ const interval = setInterval(async () => {
10811
+ if (changes.length >= maxChanges)
10812
+ return;
10813
+ try {
10814
+ const change = await page.evaluate((sel) => {
10815
+ const el = sel ? document.querySelector(sel) : document.body;
10816
+ return el ? `${new Date().toISOString()}:${el.textContent?.slice(0, 100)}` : null;
10817
+ }, opts?.selector ?? null);
10818
+ if (change && (changes.length === 0 || changes[changes.length - 1] !== change)) {
10819
+ changes.push(change);
10820
+ }
10821
+ } catch {}
10822
+ }, intervalMs);
10823
+ activeWatches.set(id, { interval, changes });
10824
+ return {
10825
+ id,
10826
+ stop: () => {
10827
+ clearInterval(interval);
10828
+ activeWatches.delete(id);
10829
+ }
10830
+ };
10831
+ }
10832
+ function getWatchChanges(watchId) {
10833
+ return activeWatches.get(watchId)?.changes ?? [];
10834
+ }
10835
+ function stopWatch(watchId) {
10836
+ const w = activeWatches.get(watchId);
10837
+ if (w) {
10838
+ clearInterval(w.interval);
10839
+ activeWatches.delete(watchId);
10840
+ }
10841
+ }
10842
+ function stopAllWatchesForSession(_sessionId) {
10843
+ for (const [id, w] of activeWatches) {
10844
+ clearInterval(w.interval);
10845
+ activeWatches.delete(id);
10846
+ }
10847
+ }
10848
+ async function clickRef(page, sessionId, ref, opts) {
10849
+ try {
10850
+ const locator = getRefLocator(page, sessionId, ref);
10851
+ await locator.click({ timeout: opts?.timeout ?? 1e4 });
10852
+ } catch (err) {
10853
+ if (err instanceof Error && err.message.includes("Ref "))
10854
+ throw new ElementNotFoundError(ref);
10855
+ if (err instanceof Error && err.message.includes("No snapshot"))
10856
+ throw new BrowserError(err.message, "NO_SNAPSHOT");
10857
+ throw new BrowserError(`clickRef ${ref} failed: ${err instanceof Error ? err.message : String(err)}`, "CLICK_REF_FAILED");
10858
+ }
10859
+ }
10860
+ async function typeRef(page, sessionId, ref, text, opts) {
10861
+ try {
10862
+ const locator = getRefLocator(page, sessionId, ref);
10863
+ if (opts?.clear)
10864
+ await locator.fill("", { timeout: opts.timeout ?? 1e4 });
10865
+ await locator.pressSequentially(text, { delay: opts?.delay, timeout: opts?.timeout ?? 1e4 });
10866
+ } catch (err) {
10867
+ if (err instanceof Error && err.message.includes("Ref "))
10868
+ throw new ElementNotFoundError(ref);
10869
+ throw new BrowserError(`typeRef ${ref} failed: ${err instanceof Error ? err.message : String(err)}`, "TYPE_REF_FAILED");
10870
+ }
10871
+ }
10872
+ async function fillRef(page, sessionId, ref, value, timeout = 1e4) {
10873
+ try {
10874
+ const locator = getRefLocator(page, sessionId, ref);
10875
+ await locator.fill(value, { timeout });
10876
+ } catch (err) {
10877
+ if (err instanceof Error && err.message.includes("Ref "))
10878
+ throw new ElementNotFoundError(ref);
10879
+ throw new BrowserError(`fillRef ${ref} failed: ${err instanceof Error ? err.message : String(err)}`, "FILL_REF_FAILED");
10880
+ }
10881
+ }
10882
+ async function selectRef(page, sessionId, ref, value, timeout = 1e4) {
10883
+ try {
10884
+ const locator = getRefLocator(page, sessionId, ref);
10885
+ return await locator.selectOption(value, { timeout });
10886
+ } catch (err) {
10887
+ if (err instanceof Error && err.message.includes("Ref "))
10888
+ throw new ElementNotFoundError(ref);
10889
+ throw new BrowserError(`selectRef ${ref} failed: ${err instanceof Error ? err.message : String(err)}`, "SELECT_REF_FAILED");
10890
+ }
10891
+ }
10892
+ async function checkRef(page, sessionId, ref, checked, timeout = 1e4) {
10893
+ try {
10894
+ const locator = getRefLocator(page, sessionId, ref);
10895
+ if (checked)
10896
+ await locator.check({ timeout });
10897
+ else
10898
+ await locator.uncheck({ timeout });
10899
+ } catch (err) {
10900
+ if (err instanceof Error && err.message.includes("Ref "))
10901
+ throw new ElementNotFoundError(ref);
10902
+ throw new BrowserError(`checkRef ${ref} failed: ${err instanceof Error ? err.message : String(err)}`, "CHECK_REF_FAILED");
10903
+ }
10904
+ }
10905
+ async function hoverRef(page, sessionId, ref, timeout = 1e4) {
10906
+ try {
10907
+ const locator = getRefLocator(page, sessionId, ref);
10908
+ await locator.hover({ timeout });
10909
+ } catch (err) {
10910
+ if (err instanceof Error && err.message.includes("Ref "))
10911
+ throw new ElementNotFoundError(ref);
10912
+ throw new BrowserError(`hoverRef ${ref} failed: ${err instanceof Error ? err.message : String(err)}`, "HOVER_REF_FAILED");
10913
+ }
10914
+ }
10915
+ var RETRYABLE_ERRORS, activeWatches;
10916
+ var init_actions = __esm(() => {
10917
+ init_types();
10918
+ init_snapshot();
10919
+ RETRYABLE_ERRORS = ["Timeout", "timeout", "navigation", "net::ERR", "Target closed"];
10920
+ activeWatches = new Map;
10921
+ });
10922
+
10923
+ // node_modules/sharp/lib/is.js
10924
+ var require_is = __commonJS((exports, module) => {
10925
+ /*!
10926
+ Copyright 2013 Lovell Fuller and others.
10927
+ SPDX-License-Identifier: Apache-2.0
10928
+ */
10929
+ var defined = (val) => typeof val !== "undefined" && val !== null;
10930
+ var object = (val) => typeof val === "object";
10931
+ var plainObject = (val) => Object.prototype.toString.call(val) === "[object Object]";
10932
+ var fn = (val) => typeof val === "function";
10933
+ var bool = (val) => typeof val === "boolean";
10934
+ var buffer = (val) => val instanceof Buffer;
10935
+ var typedArray = (val) => {
10936
+ if (defined(val)) {
10937
+ switch (val.constructor) {
10938
+ case Uint8Array:
10939
+ case Uint8ClampedArray:
10940
+ case Int8Array:
10941
+ case Uint16Array:
10942
+ case Int16Array:
10943
+ case Uint32Array:
10944
+ case Int32Array:
10945
+ case Float32Array:
10946
+ case Float64Array:
10947
+ return true;
10948
+ }
10949
+ }
10950
+ return false;
10951
+ };
10952
+ var arrayBuffer = (val) => val instanceof ArrayBuffer;
10953
+ var string = (val) => typeof val === "string" && val.length > 0;
10954
+ var number = (val) => typeof val === "number" && !Number.isNaN(val);
10955
+ var integer = (val) => Number.isInteger(val);
10956
+ var inRange = (val, min, max) => val >= min && val <= max;
10957
+ var inArray = (val, list) => list.includes(val);
10958
+ var invalidParameterError = (name, expected, actual) => new Error(`Expected ${expected} for ${name} but received ${actual} of type ${typeof actual}`);
10959
+ var nativeError = (native, context) => {
10960
+ context.message = native.message;
10961
+ return context;
10962
+ };
10963
+ module.exports = {
10964
+ defined,
10965
+ object,
10966
+ plainObject,
10967
+ fn,
10968
+ bool,
10969
+ buffer,
10970
+ typedArray,
10971
+ arrayBuffer,
10972
+ string,
10973
+ number,
10974
+ integer,
10975
+ inRange,
10976
+ inArray,
10977
+ invalidParameterError,
10978
+ nativeError
10979
+ };
10980
+ });
10981
+
10982
+ // node_modules/detect-libc/lib/process.js
10983
+ var require_process = __commonJS((exports, module) => {
10984
+ var isLinux = () => process.platform === "linux";
10985
+ var report = null;
10986
+ var getReport = () => {
10987
+ if (!report) {
10988
+ if (isLinux() && process.report) {
10989
+ const orig = process.report.excludeNetwork;
10990
+ process.report.excludeNetwork = true;
10991
+ report = process.report.getReport();
10992
+ process.report.excludeNetwork = orig;
10993
+ } else {
10994
+ report = {};
10995
+ }
10996
+ }
10997
+ return report;
10998
+ };
10999
+ module.exports = { isLinux, getReport };
11000
+ });
11001
+
11002
+ // node_modules/detect-libc/lib/filesystem.js
11003
+ var require_filesystem = __commonJS((exports, module) => {
11004
+ var fs = __require("fs");
11005
+ var LDD_PATH = "/usr/bin/ldd";
11006
+ var SELF_PATH = "/proc/self/exe";
11007
+ var MAX_LENGTH = 2048;
11008
+ var readFileSync2 = (path) => {
11009
+ const fd = fs.openSync(path, "r");
11010
+ const buffer = Buffer.alloc(MAX_LENGTH);
11011
+ const bytesRead = fs.readSync(fd, buffer, 0, MAX_LENGTH, 0);
11012
+ fs.close(fd, () => {});
11013
+ return buffer.subarray(0, bytesRead);
11014
+ };
11015
+ var readFile = (path) => new Promise((resolve, reject) => {
11016
+ fs.open(path, "r", (err, fd) => {
11017
+ if (err) {
11018
+ reject(err);
11019
+ } else {
11020
+ const buffer = Buffer.alloc(MAX_LENGTH);
11021
+ fs.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
11022
+ resolve(buffer.subarray(0, bytesRead));
11023
+ fs.close(fd, () => {});
11024
+ });
11025
+ }
11026
+ });
11027
+ });
11028
+ module.exports = {
11029
+ LDD_PATH,
11030
+ SELF_PATH,
11031
+ readFileSync: readFileSync2,
11032
+ readFile
11033
+ };
11034
+ });
11035
+
11036
+ // node_modules/detect-libc/lib/elf.js
11037
+ var require_elf = __commonJS((exports, module) => {
11038
+ var interpreterPath = (elf) => {
11039
+ if (elf.length < 64) {
11040
+ return null;
11041
+ }
11042
+ if (elf.readUInt32BE(0) !== 2135247942) {
11043
+ return null;
11044
+ }
11045
+ if (elf.readUInt8(4) !== 2) {
11046
+ return null;
11047
+ }
11048
+ if (elf.readUInt8(5) !== 1) {
11049
+ return null;
10196
11050
  }
10197
11051
  const offset = elf.readUInt32LE(32);
10198
11052
  const size = elf.readUInt16LE(54);
@@ -13725,7 +14579,7 @@ var require_operation = __commonJS((exports, module) => {
13725
14579
  // node_modules/@img/colour/color.cjs
13726
14580
  var require_color = __commonJS((exports, module) => {
13727
14581
  var __defProp3 = Object.defineProperty;
13728
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
14582
+ var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
13729
14583
  var __getOwnPropNames3 = Object.getOwnPropertyNames;
13730
14584
  var __hasOwnProp3 = Object.prototype.hasOwnProperty;
13731
14585
  var __export3 = (target, all) => {
@@ -13736,16 +14590,16 @@ var require_color = __commonJS((exports, module) => {
13736
14590
  if (from && typeof from === "object" || typeof from === "function") {
13737
14591
  for (let key of __getOwnPropNames3(from))
13738
14592
  if (!__hasOwnProp3.call(to, key) && key !== except)
13739
- __defProp3(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14593
+ __defProp3(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
13740
14594
  }
13741
14595
  return to;
13742
14596
  };
13743
- var __toCommonJS = (mod) => __copyProps(__defProp3({}, "__esModule", { value: true }), mod);
14597
+ var __toCommonJS2 = (mod) => __copyProps(__defProp3({}, "__esModule", { value: true }), mod);
13744
14598
  var index_exports = {};
13745
14599
  __export3(index_exports, {
13746
14600
  default: () => index_default
13747
14601
  });
13748
- module.exports = __toCommonJS(index_exports);
14602
+ module.exports = __toCommonJS2(index_exports);
13749
14603
  var colors = {
13750
14604
  aliceblue: [240, 248, 255],
13751
14605
  antiquewhite: [250, 235, 215],
@@ -17293,6 +18147,88 @@ class BunWebViewSession {
17293
18147
 
17294
18148
  // src/engines/selector.ts
17295
18149
  init_types();
18150
+
18151
+ // src/engines/tui.ts
18152
+ init_types();
18153
+ import { execSync as execSync2, spawn as spawn2 } from "child_process";
18154
+ var DEFAULT_TTYD_PORT_START = 7780;
18155
+ var nextPort = DEFAULT_TTYD_PORT_START;
18156
+ function isTuiAvailable() {
18157
+ try {
18158
+ execSync2("which ttyd", { stdio: "ignore" });
18159
+ return true;
18160
+ } catch {
18161
+ return false;
18162
+ }
18163
+ }
18164
+ async function findAvailablePort(startPort) {
18165
+ let port = startPort;
18166
+ for (let i = 0;i < 100; i++) {
18167
+ try {
18168
+ const resp = await fetch(`http://localhost:${port}`);
18169
+ port++;
18170
+ } catch {
18171
+ return port;
18172
+ }
18173
+ }
18174
+ throw new BrowserError("No available port found for ttyd", "TUI_PORT_EXHAUSTED");
18175
+ }
18176
+ async function waitForTtyd(port, timeoutMs = 1e4) {
18177
+ const start = Date.now();
18178
+ while (Date.now() - start < timeoutMs) {
18179
+ try {
18180
+ const resp = await fetch(`http://localhost:${port}`);
18181
+ if (resp.ok || resp.status === 200)
18182
+ return;
18183
+ } catch {}
18184
+ await new Promise((r) => setTimeout(r, 150));
18185
+ }
18186
+ throw new BrowserError(`ttyd did not start within ${timeoutMs}ms`, "TUI_TIMEOUT");
18187
+ }
18188
+ async function launchTui(command, options = {}) {
18189
+ if (!isTuiAvailable()) {
18190
+ throw new BrowserError("ttyd not found \u2014 install with: brew install ttyd", "TUI_NOT_AVAILABLE");
18191
+ }
18192
+ const port = await findAvailablePort(nextPort);
18193
+ nextPort = port + 1;
18194
+ const ttydProcess = spawn2("ttyd", ["--writable", "--port", String(port), "/bin/sh", "-c", command], {
18195
+ stdio: "ignore",
18196
+ detached: false
18197
+ });
18198
+ ttydProcess.on("error", (err) => {
18199
+ console.error(`[tui] ttyd process error: ${err.message}`);
18200
+ });
18201
+ try {
18202
+ await waitForTtyd(port);
18203
+ const viewport = options.viewport ?? { width: 1280, height: 720 };
18204
+ const browser = await launchPlaywright({
18205
+ headless: options.headless ?? true,
18206
+ viewport
18207
+ });
18208
+ const page = await getPage(browser, { viewport });
18209
+ await page.goto(`http://localhost:${port}`, {
18210
+ waitUntil: "domcontentloaded"
18211
+ });
18212
+ await page.waitForSelector(".xterm-screen", { timeout: 1e4 });
18213
+ return { ttydProcess, port, browser, page };
18214
+ } catch (err) {
18215
+ ttydProcess.kill();
18216
+ throw err;
18217
+ }
18218
+ }
18219
+ async function closeTui(session) {
18220
+ try {
18221
+ await session.page.close();
18222
+ } catch {}
18223
+ try {
18224
+ await session.browser.close();
18225
+ } catch {}
18226
+ try {
18227
+ session.ttydProcess.kill("SIGTERM");
18228
+ } catch {}
18229
+ }
18230
+
18231
+ // src/engines/selector.ts
17296
18232
  var ENGINE_MAP = {
17297
18233
  ["scrape" /* SCRAPE */]: "bun",
17298
18234
  ["extract_links" /* EXTRACT_LINKS */]: "bun",
@@ -17303,6 +18239,7 @@ var ENGINE_MAP = {
17303
18239
  ["auth_flow" /* AUTH_FLOW */]: "playwright",
17304
18240
  ["multi_tab" /* MULTI_TAB */]: "playwright",
17305
18241
  ["record_replay" /* RECORD_REPLAY */]: "playwright",
18242
+ ["terminal_test" /* TERMINAL_TEST */]: "tui",
17306
18243
  ["network_monitor" /* NETWORK_MONITOR */]: "cdp",
17307
18244
  ["har_capture" /* HAR_CAPTURE */]: "cdp",
17308
18245
  ["perf_profile" /* PERF_PROFILE */]: "cdp",
@@ -17389,6 +18326,7 @@ function enableNetworkLogging(page, sessionId) {
17389
18326
  requestStart.clear();
17390
18327
  };
17391
18328
  }
18329
+ var HAR_MAX_ENTRIES = 5000;
17392
18330
  function startHAR(page) {
17393
18331
  const entries = [];
17394
18332
  const requestStart = new Map;
@@ -17427,7 +18365,8 @@ function startHAR(page) {
17427
18365
  },
17428
18366
  timings: { send: 0, wait: duration, receive: 0 }
17429
18367
  };
17430
- entries.push(entry);
18368
+ if (entries.length < HAR_MAX_ENTRIES)
18369
+ entries.push(entry);
17431
18370
  };
17432
18371
  const onRequestFailed = (req) => {
17433
18372
  requestStart.delete(req.url() + req.method());
@@ -17545,49 +18484,8 @@ async function applyStealthPatches(page) {
17545
18484
  });
17546
18485
  }
17547
18486
 
17548
- // src/lib/dialogs.ts
17549
- var pendingDialogs = new Map;
17550
- var AUTO_DISMISS_MS = 5000;
17551
- function setupDialogHandler(page, sessionId) {
17552
- const onDialog = (dialog) => {
17553
- const info = {
17554
- type: dialog.type(),
17555
- message: dialog.message(),
17556
- default_value: dialog.defaultValue(),
17557
- timestamp: new Date().toISOString()
17558
- };
17559
- const autoTimer = setTimeout(() => {
17560
- try {
17561
- dialog.dismiss().catch(() => {});
17562
- } catch {}
17563
- const list = pendingDialogs.get(sessionId);
17564
- if (list) {
17565
- const idx = list.findIndex((p) => p.dialog === dialog);
17566
- if (idx >= 0)
17567
- list.splice(idx, 1);
17568
- if (list.length === 0)
17569
- pendingDialogs.delete(sessionId);
17570
- }
17571
- }, AUTO_DISMISS_MS);
17572
- const pending = { dialog, info, autoTimer };
17573
- if (!pendingDialogs.has(sessionId)) {
17574
- pendingDialogs.set(sessionId, []);
17575
- }
17576
- pendingDialogs.get(sessionId).push(pending);
17577
- };
17578
- page.on("dialog", onDialog);
17579
- return () => {
17580
- page.off("dialog", onDialog);
17581
- const list = pendingDialogs.get(sessionId);
17582
- if (list) {
17583
- for (const p of list)
17584
- clearTimeout(p.autoTimer);
17585
- pendingDialogs.delete(sessionId);
17586
- }
17587
- };
17588
- }
17589
-
17590
18487
  // src/lib/session.ts
18488
+ init_dialogs();
17591
18489
  var handles = new Map;
17592
18490
  var pool = new BrowserPool(5);
17593
18491
  var SESSION_TTL_MS = parseInt(process.env["SESSION_TTL_MINUTES"] ?? "10", 10) * 60000;
@@ -17603,6 +18501,20 @@ var ttlInterval = setInterval(async () => {
17603
18501
  }, 60000);
17604
18502
  if (ttlInterval.unref)
17605
18503
  ttlInterval.unref();
18504
+ var DB_PRUNE_INTERVAL_MS = 30 * 60000;
18505
+ var DB_RETENTION_HOURS = 24;
18506
+ var dbPruneInterval = setInterval(() => {
18507
+ try {
18508
+ const { getDatabase: getDatabase2 } = (init_schema(), __toCommonJS(exports_schema));
18509
+ const db = getDatabase2();
18510
+ const cutoff = new Date(Date.now() - DB_RETENTION_HOURS * 3600000).toISOString();
18511
+ db.prepare("DELETE FROM network_log WHERE session_id IN (SELECT id FROM sessions WHERE status != 'active') AND timestamp < ?").run(cutoff);
18512
+ db.prepare("DELETE FROM console_log WHERE session_id IN (SELECT id FROM sessions WHERE status != 'active') AND timestamp < ?").run(cutoff);
18513
+ db.prepare("DELETE FROM snapshots WHERE session_id IN (SELECT id FROM sessions WHERE status != 'active') AND timestamp < ?").run(cutoff);
18514
+ } catch {}
18515
+ }, DB_PRUNE_INTERVAL_MS);
18516
+ if (dbPruneInterval.unref)
18517
+ dbPruneInterval.unref();
17606
18518
  function createBunProxy(view) {
17607
18519
  return view;
17608
18520
  }
@@ -17635,7 +18547,7 @@ async function createSession2(opts = {}) {
17635
18547
  try {
17636
18548
  cleanups2.push(setupDialogHandler(page2, session2.id));
17637
18549
  } catch {}
17638
- 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 });
18550
+ 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 });
17639
18551
  return { session: session2, page: page2 };
17640
18552
  }
17641
18553
  const engine = opts.engine === "auto" || !opts.engine ? selectEngine(opts.useCase ?? "spa_navigate" /* SPA_NAVIGATE */, opts.engine) : opts.engine;
@@ -17661,6 +18573,38 @@ async function createSession2(opts = {}) {
17661
18573
  browser = await connectLightpanda();
17662
18574
  const context = await browser.newContext({ viewport: opts.viewport ?? { width: 1280, height: 720 } });
17663
18575
  page = await context.newPage();
18576
+ } else if (resolvedEngine === "tui") {
18577
+ const command = opts.startUrl ?? "bash";
18578
+ const tuiSess = await launchTui(command, {
18579
+ headless: opts.headless ?? true,
18580
+ viewport: opts.viewport
18581
+ });
18582
+ browser = tuiSess.browser;
18583
+ page = tuiSess.page;
18584
+ const session2 = createSession({
18585
+ engine: "tui",
18586
+ projectId: opts.projectId,
18587
+ agentId: opts.agentId,
18588
+ startUrl: opts.startUrl,
18589
+ name: opts.name ?? "tui"
18590
+ });
18591
+ const cleanups2 = [];
18592
+ cleanups2.push(() => closeTui(tuiSess));
18593
+ if (opts.captureNetwork !== false) {
18594
+ try {
18595
+ cleanups2.push(enableNetworkLogging(page, session2.id));
18596
+ } catch {}
18597
+ }
18598
+ if (opts.captureConsole !== false) {
18599
+ try {
18600
+ cleanups2.push(enableConsoleCapture(page, session2.id));
18601
+ } catch {}
18602
+ }
18603
+ try {
18604
+ cleanups2.push(setupDialogHandler(page, session2.id));
18605
+ } catch {}
18606
+ 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 });
18607
+ return { session: session2, page };
17664
18608
  } else {
17665
18609
  browser = await pool.acquire(opts.headless ?? true);
17666
18610
  if (opts.storageState) {
@@ -17731,7 +18675,7 @@ async function createSession2(opts = {}) {
17731
18675
  } catch {}
17732
18676
  }
17733
18677
  }
17734
- handles.set(session.id, { browser, bunView, page, engine: bunView ? "bun" : resolvedEngine, cleanups, tokenBudget: { total: 0, used: 0 }, lastActivity: Date.now(), autoGallery: opts.autoGallery ?? false });
18678
+ 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 });
17735
18679
  if (opts.startUrl) {
17736
18680
  try {
17737
18681
  if (bunView) {
@@ -17772,7 +18716,7 @@ async function closeSession2(sessionId) {
17772
18716
  try {
17773
18717
  await handle.bunView.close();
17774
18718
  } catch {}
17775
- } else {
18719
+ } else if (handle.tuiSession) {} else {
17776
18720
  try {
17777
18721
  await handle.page.context().close();
17778
18722
  } catch {}
@@ -17781,142 +18725,27 @@ async function closeSession2(sessionId) {
17781
18725
  }
17782
18726
  handles.delete(sessionId);
17783
18727
  }
17784
- return closeSession(sessionId);
17785
- }
17786
- function listSessions2(filter) {
17787
- return listSessions(filter);
17788
- }
17789
-
17790
- // src/lib/actions.ts
17791
- init_types();
17792
-
17793
- // src/lib/snapshot.ts
17794
- var lastSnapshots = new Map;
17795
- var sessionRefMaps = new Map;
17796
-
17797
- // src/lib/self-heal.ts
17798
- async function healSelector(page, selector, sessionId) {
17799
- const attempts = [];
17800
- attempts.push(`selector: ${selector}`);
17801
18728
  try {
17802
- const loc = page.locator(selector).first();
17803
- if (await loc.count() > 0) {
17804
- return { found: true, locator: loc, method: "original", healed: false, attempts };
17805
- }
18729
+ const { clearLastSnapshot: clearLastSnapshot2, clearSessionRefs: clearSessionRefs2 } = await Promise.resolve().then(() => (init_snapshot(), exports_snapshot));
18730
+ clearLastSnapshot2(sessionId);
18731
+ clearSessionRefs2(sessionId);
17806
18732
  } catch {}
17807
- if (!selector.startsWith("#") && !selector.startsWith(".") && !selector.startsWith("[") && !selector.includes(">") && !selector.includes(" ")) {
17808
- attempts.push(`text: "${selector}"`);
17809
- try {
17810
- const loc = page.getByText(selector, { exact: false }).first();
17811
- if (await loc.count() > 0) {
17812
- return { found: true, locator: loc, method: "text", healed: true, attempts };
17813
- }
17814
- } catch {}
17815
- }
17816
- const roleMap = {
17817
- button: ["button", "submit", "reset"],
17818
- link: ["a"],
17819
- input: ["input", "textarea"],
17820
- heading: ["h1", "h2", "h3", "h4", "h5", "h6"]
17821
- };
17822
- const nameHint = selector.replace(/^[#.]/, "").replace(/[-_]/g, " ").toLowerCase();
17823
- for (const [role, tags] of Object.entries(roleMap)) {
17824
- attempts.push(`role: ${role} name~="${nameHint}"`);
17825
- try {
17826
- const loc = page.getByRole(role, { name: new RegExp(nameHint.split(" ")[0], "i") }).first();
17827
- if (await loc.count() > 0) {
17828
- return { found: true, locator: loc, method: "role", healed: true, attempts };
17829
- }
17830
- } catch {}
17831
- }
17832
- if (selector.startsWith("#")) {
17833
- const idPart = selector.slice(1).split("-").pop() ?? selector.slice(1);
17834
- const partialSel = `[id*="${idPart}"]`;
17835
- attempts.push(`partial_id: ${partialSel}`);
17836
- try {
17837
- const loc = page.locator(partialSel).first();
17838
- if (await loc.count() > 0) {
17839
- return { found: true, locator: loc, method: "partial_id", healed: true, attempts };
17840
- }
17841
- } catch {}
17842
- }
17843
- if (selector.startsWith(".")) {
17844
- const classPart = selector.slice(1).split("-").pop() ?? selector.slice(1);
17845
- const partialSel = `[class*="${classPart}"]`;
17846
- attempts.push(`partial_class: ${partialSel}`);
17847
- try {
17848
- const loc = page.locator(partialSel).first();
17849
- if (await loc.count() > 0) {
17850
- return { found: true, locator: loc, method: "partial_class", healed: true, attempts };
17851
- }
17852
- } catch {}
17853
- }
17854
- return { found: false, locator: null, method: "none", healed: false, attempts };
17855
- }
17856
-
17857
- // src/lib/actions.ts
17858
- async function click(page, selector, opts) {
17859
18733
  try {
17860
- await page.click(selector, {
17861
- button: opts?.button ?? "left",
17862
- clickCount: opts?.clickCount ?? 1,
17863
- delay: opts?.delay,
17864
- timeout: opts?.timeout ?? 1e4
17865
- });
17866
- return {};
17867
- } catch (originalError) {
17868
- if (opts?.selfHeal !== false) {
17869
- const result = await healSelector(page, selector);
17870
- if (result.found && result.locator) {
17871
- await result.locator.click({
17872
- button: opts?.button ?? "left",
17873
- timeout: opts?.timeout ?? 1e4
17874
- });
17875
- return { healed: true, method: result.method, attempts: result.attempts };
17876
- }
17877
- }
17878
- if (originalError instanceof Error && originalError.message.includes("not found")) {
17879
- throw new ElementNotFoundError(selector);
17880
- }
17881
- throw new BrowserError(`Click failed on '${selector}': ${originalError instanceof Error ? originalError.message : String(originalError)}`, "CLICK_FAILED");
17882
- }
17883
- }
17884
- async function type(page, selector, text, opts) {
18734
+ const { stopAllWatchesForSession: stopAllWatchesForSession2 } = await Promise.resolve().then(() => (init_actions(), exports_actions));
18735
+ stopAllWatchesForSession2(sessionId);
18736
+ } catch {}
17885
18737
  try {
17886
- if (opts?.clear) {
17887
- await page.fill(selector, "", { timeout: opts?.timeout ?? 1e4 });
17888
- }
17889
- await page.type(selector, text, { delay: opts?.delay, timeout: opts?.timeout ?? 1e4 });
17890
- return {};
17891
- } catch (originalError) {
17892
- if (opts?.selfHeal !== false) {
17893
- const result = await healSelector(page, selector);
17894
- if (result.found && result.locator) {
17895
- if (opts?.clear)
17896
- await result.locator.fill("", { timeout: opts?.timeout ?? 1e4 });
17897
- await result.locator.pressSequentially(text, { delay: opts?.delay, timeout: opts?.timeout ?? 1e4 });
17898
- return { healed: true, method: result.method, attempts: result.attempts };
17899
- }
17900
- }
17901
- if (originalError instanceof Error && originalError.message.includes("not found")) {
17902
- throw new ElementNotFoundError(selector);
17903
- }
17904
- throw new BrowserError(`Type failed on '${selector}': ${originalError instanceof Error ? originalError.message : String(originalError)}`, "TYPE_FAILED");
17905
- }
17906
- }
17907
- async function scroll(page, direction = "down", amount = 300) {
17908
- const x = direction === "left" ? -amount : direction === "right" ? amount : 0;
17909
- const y = direction === "up" ? -amount : direction === "down" ? amount : 0;
17910
- await page.evaluate(({ x: x2, y: y2 }) => window.scrollBy(x2, y2), { x, y });
18738
+ const { clearDialogs: clearDialogs2 } = await Promise.resolve().then(() => (init_dialogs(), exports_dialogs));
18739
+ clearDialogs2(sessionId);
18740
+ } catch {}
18741
+ return closeSession(sessionId);
17911
18742
  }
17912
- async function navigate(page, url, timeout = 30000) {
17913
- try {
17914
- await page.goto(url, { waitUntil: "domcontentloaded", timeout });
17915
- } catch (err) {
17916
- throw new NavigationError(url, err instanceof Error ? err.message : String(err));
17917
- }
18743
+ function listSessions2(filter) {
18744
+ return listSessions(filter);
17918
18745
  }
17919
- var activeWatches = new Map;
18746
+
18747
+ // src/server/index.ts
18748
+ init_actions();
17920
18749
 
17921
18750
  // src/lib/extractor.ts
17922
18751
  async function getText(page, selector) {
@@ -18398,6 +19227,7 @@ async function crawl(startUrl, opts = {}) {
18398
19227
 
18399
19228
  // src/lib/recorder.ts
18400
19229
  init_recordings();
19230
+ init_actions();
18401
19231
  init_types();
18402
19232
  var activeRecordings = new Map;
18403
19233
  async function replayRecording(recordingId, page) {