momentic 0.0.2 → 0.0.4
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/index.js +1144 -992
- package/package.json +2 -3
package/dist/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import * as __WEBPACK_EXTERNAL_MODULE_diff_lines_24b6f423__ from "diff-lines";
|
|
5
|
-
/******/ var __webpack_modules__ = ({
|
|
1
|
+
/******/ (() => { // webpackBootstrap
|
|
2
|
+
/******/ "use strict";
|
|
3
|
+
/******/ var __webpack_modules__ = ({
|
|
6
4
|
|
|
7
5
|
/***/ 909:
|
|
8
6
|
/***/ (function(__unused_webpack_module, exports) {
|
|
@@ -333,90 +331,16 @@ function ArgumentError(message) {
|
|
|
333
331
|
}
|
|
334
332
|
|
|
335
333
|
|
|
336
|
-
/***/ })
|
|
334
|
+
/***/ }),
|
|
337
335
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
/******/ // The require function
|
|
344
|
-
/******/ function __nccwpck_require__(moduleId) {
|
|
345
|
-
/******/ // Check if module is in cache
|
|
346
|
-
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
|
347
|
-
/******/ if (cachedModule !== undefined) {
|
|
348
|
-
/******/ return cachedModule.exports;
|
|
349
|
-
/******/ }
|
|
350
|
-
/******/ // Create a new module (and put it into the cache)
|
|
351
|
-
/******/ var module = __webpack_module_cache__[moduleId] = {
|
|
352
|
-
/******/ // no module.id needed
|
|
353
|
-
/******/ // no module.loaded needed
|
|
354
|
-
/******/ exports: {}
|
|
355
|
-
/******/ };
|
|
356
|
-
/******/
|
|
357
|
-
/******/ // Execute the module function
|
|
358
|
-
/******/ var threw = true;
|
|
359
|
-
/******/ try {
|
|
360
|
-
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __nccwpck_require__);
|
|
361
|
-
/******/ threw = false;
|
|
362
|
-
/******/ } finally {
|
|
363
|
-
/******/ if(threw) delete __webpack_module_cache__[moduleId];
|
|
364
|
-
/******/ }
|
|
365
|
-
/******/
|
|
366
|
-
/******/ // Return the exports of the module
|
|
367
|
-
/******/ return module.exports;
|
|
368
|
-
/******/ }
|
|
369
|
-
/******/
|
|
370
|
-
/************************************************************************/
|
|
371
|
-
/******/ /* webpack/runtime/compat get default export */
|
|
372
|
-
/******/ (() => {
|
|
373
|
-
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
374
|
-
/******/ __nccwpck_require__.n = (module) => {
|
|
375
|
-
/******/ var getter = module && module.__esModule ?
|
|
376
|
-
/******/ () => (module['default']) :
|
|
377
|
-
/******/ () => (module);
|
|
378
|
-
/******/ __nccwpck_require__.d(getter, { a: getter });
|
|
379
|
-
/******/ return getter;
|
|
380
|
-
/******/ };
|
|
381
|
-
/******/ })();
|
|
382
|
-
/******/
|
|
383
|
-
/******/ /* webpack/runtime/define property getters */
|
|
384
|
-
/******/ (() => {
|
|
385
|
-
/******/ // define getter functions for harmony exports
|
|
386
|
-
/******/ __nccwpck_require__.d = (exports, definition) => {
|
|
387
|
-
/******/ for(var key in definition) {
|
|
388
|
-
/******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) {
|
|
389
|
-
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
390
|
-
/******/ }
|
|
391
|
-
/******/ }
|
|
392
|
-
/******/ };
|
|
393
|
-
/******/ })();
|
|
394
|
-
/******/
|
|
395
|
-
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
|
396
|
-
/******/ (() => {
|
|
397
|
-
/******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
398
|
-
/******/ })();
|
|
399
|
-
/******/
|
|
400
|
-
/******/ /* webpack/runtime/compat */
|
|
401
|
-
/******/
|
|
402
|
-
/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = new URL('.', import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/) ? 1 : 0, -1) + "/";
|
|
403
|
-
/******/
|
|
404
|
-
/************************************************************************/
|
|
405
|
-
var __webpack_exports__ = {};
|
|
406
|
-
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
|
|
407
|
-
(() => {
|
|
408
|
-
|
|
409
|
-
// EXPORTS
|
|
410
|
-
__nccwpck_require__.d(__webpack_exports__, {
|
|
411
|
-
"_w": () => (/* reexport */ APIGenerator),
|
|
412
|
-
"Yt": () => (/* reexport */ AgentController),
|
|
413
|
-
"DE": () => (/* reexport */ ChromeBrowser)
|
|
414
|
-
});
|
|
336
|
+
/***/ 811:
|
|
337
|
+
/***/ ((module, __webpack_exports__, __nccwpck_require__) => {
|
|
338
|
+
|
|
339
|
+
// ESM COMPAT FLAG
|
|
340
|
+
__nccwpck_require__.r(__webpack_exports__);
|
|
415
341
|
|
|
416
342
|
;// CONCATENATED MODULE: external "playwright"
|
|
417
|
-
|
|
418
|
-
var y = x => () => x
|
|
419
|
-
const external_playwright_namespaceObject = x({ ["chromium"]: () => __WEBPACK_EXTERNAL_MODULE_playwright__.chromium, ["devices"]: () => __WEBPACK_EXTERNAL_MODULE_playwright__.devices });
|
|
343
|
+
const external_playwright_namespaceObject = require("playwright");
|
|
420
344
|
;// CONCATENATED MODULE: ../../packages/web-agent/src/utils/url.ts
|
|
421
345
|
// Returns true if 2 urls are different, ignoring differences in query params.
|
|
422
346
|
const urlChanged = (url1, url2) => {
|
|
@@ -433,9 +357,7 @@ var AgentType;
|
|
|
433
357
|
})(AgentType || (AgentType = {}));
|
|
434
358
|
|
|
435
359
|
;// CONCATENATED MODULE: external "zod"
|
|
436
|
-
|
|
437
|
-
var external_zod_y = x => () => x
|
|
438
|
-
const external_zod_namespaceObject = external_zod_x({ ["array"]: () => __WEBPACK_EXTERNAL_MODULE_zod__.array, ["boolean"]: () => __WEBPACK_EXTERNAL_MODULE_zod__.boolean, ["coerce"]: () => __WEBPACK_EXTERNAL_MODULE_zod__.coerce, ["discriminatedUnion"]: () => __WEBPACK_EXTERNAL_MODULE_zod__.discriminatedUnion, ["literal"]: () => __WEBPACK_EXTERNAL_MODULE_zod__.literal, ["nativeEnum"]: () => __WEBPACK_EXTERNAL_MODULE_zod__.nativeEnum, ["null"]: () => __WEBPACK_EXTERNAL_MODULE_zod__["null"], ["number"]: () => __WEBPACK_EXTERNAL_MODULE_zod__.number, ["object"]: () => __WEBPACK_EXTERNAL_MODULE_zod__.object, ["string"]: () => __WEBPACK_EXTERNAL_MODULE_zod__.string, ["union"]: () => __WEBPACK_EXTERNAL_MODULE_zod__.union, ["z"]: () => __WEBPACK_EXTERNAL_MODULE_zod__.z });
|
|
360
|
+
const external_zod_namespaceObject = require("zod");
|
|
439
361
|
;// CONCATENATED MODULE: ../../packages/types/src/a11y-targets.ts
|
|
440
362
|
|
|
441
363
|
const A11yTargetWithCacheSchema = external_zod_namespaceObject.object({
|
|
@@ -481,9 +403,8 @@ class EmptyA11yTreeError extends Error {
|
|
|
481
403
|
}
|
|
482
404
|
|
|
483
405
|
;// CONCATENATED MODULE: external "dedent"
|
|
484
|
-
|
|
485
|
-
var
|
|
486
|
-
const external_dedent_namespaceObject = external_dedent_x({ ["default"]: () => __WEBPACK_EXTERNAL_MODULE_dedent__["default"] });
|
|
406
|
+
const external_dedent_namespaceObject = require("dedent");
|
|
407
|
+
var external_dedent_default = /*#__PURE__*/__nccwpck_require__.n(external_dedent_namespaceObject);
|
|
487
408
|
;// CONCATENATED MODULE: ../../packages/types/src/preset.ts
|
|
488
409
|
|
|
489
410
|
|
|
@@ -541,7 +462,7 @@ const ClickCommandSchema = CommonCommandSchema.merge(external_zod_namespaceObjec
|
|
|
541
462
|
target: ElementDescriptorSchema,
|
|
542
463
|
doubleClick: external_zod_namespaceObject.boolean().default(false),
|
|
543
464
|
rightClick: external_zod_namespaceObject.boolean().default(false),
|
|
544
|
-
})).describe(
|
|
465
|
+
})).describe((external_dedent_default()) `CLICK <id> - click on the element that has the specified id.
|
|
545
466
|
You are NOT allowed to click on disabled, hidden or StaticText elements.
|
|
546
467
|
Only click on elements on the Current Page.
|
|
547
468
|
Only click on elements with the following tag names: button, input, link, image, generic.
|
|
@@ -1073,6 +994,7 @@ function serializeAICommand(cmd) {
|
|
|
1073
994
|
return humanSummary;
|
|
1074
995
|
}
|
|
1075
996
|
function serializePresetCommand(command) {
|
|
997
|
+
var _a;
|
|
1076
998
|
switch (command.type) {
|
|
1077
999
|
case preset_PresetCommandType.NAVIGATE:
|
|
1078
1000
|
return `Go to URL: ${clampText(command.url, 30)}`;
|
|
@@ -1092,7 +1014,7 @@ function serializePresetCommand(command) {
|
|
|
1092
1014
|
return `Click on '${command.target.elementDescriptor}'`;
|
|
1093
1015
|
case preset_PresetCommandType.TYPE:
|
|
1094
1016
|
let serializedTarget = "";
|
|
1095
|
-
if (command.target.a11yData
|
|
1017
|
+
if ((_a = command.target.a11yData) === null || _a === void 0 ? void 0 : _a.serializedForm) {
|
|
1096
1018
|
serializedTarget = ` in element ${command.target.a11yData.serializedForm}`;
|
|
1097
1019
|
}
|
|
1098
1020
|
else if (command.target.elementDescriptor.length > 0) {
|
|
@@ -1277,10 +1199,11 @@ class ProcessedA11yNode {
|
|
|
1277
1199
|
this.backendNodeID = params.backendNodeID;
|
|
1278
1200
|
}
|
|
1279
1201
|
getLogForm() {
|
|
1202
|
+
var _a, _b;
|
|
1280
1203
|
return JSON.stringify({
|
|
1281
1204
|
id: this.id,
|
|
1282
|
-
name: this.name
|
|
1283
|
-
role: this.role
|
|
1205
|
+
name: (_a = this.name) !== null && _a !== void 0 ? _a : "",
|
|
1206
|
+
role: (_b = this.role) !== null && _b !== void 0 ? _b : "",
|
|
1284
1207
|
backendNodeId: this.backendNodeID,
|
|
1285
1208
|
});
|
|
1286
1209
|
}
|
|
@@ -1372,10 +1295,11 @@ class ProcessedA11yTree {
|
|
|
1372
1295
|
* May not be unique in a graph.
|
|
1373
1296
|
*/
|
|
1374
1297
|
function getNodePathIdentifier(node) {
|
|
1375
|
-
|
|
1298
|
+
var _a, _b;
|
|
1299
|
+
if ((_a = node.name) === null || _a === void 0 ? void 0 : _a.value) {
|
|
1376
1300
|
return `"${node.name.value}"`;
|
|
1377
1301
|
}
|
|
1378
|
-
if (node.role
|
|
1302
|
+
if (((_b = node.role) === null || _b === void 0 ? void 0 : _b.value) &&
|
|
1379
1303
|
node.role.value !== "none" &&
|
|
1380
1304
|
node.role.value !== "generic") {
|
|
1381
1305
|
return `"${node.role.value}"`;
|
|
@@ -1383,22 +1307,23 @@ function getNodePathIdentifier(node) {
|
|
|
1383
1307
|
return `"${node.nodeId}"`;
|
|
1384
1308
|
}
|
|
1385
1309
|
function processA11yTreeDFS(node, parent, inputNodeMap, outputNodeMap) {
|
|
1310
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1386
1311
|
if (!parent && node.parentId) {
|
|
1387
1312
|
throw new Error(`Got no parent for accessibility node ${node.nodeId}: ${JSON.stringify(node)}`);
|
|
1388
1313
|
}
|
|
1389
1314
|
const processedNode = new ProcessedA11yNode({
|
|
1390
1315
|
id: node.nodeId,
|
|
1391
|
-
role: node.role
|
|
1392
|
-
name: node.name
|
|
1393
|
-
content: node.value
|
|
1316
|
+
role: ((_a = node.role) === null || _a === void 0 ? void 0 : _a.value) || "",
|
|
1317
|
+
name: ((_b = node.name) === null || _b === void 0 ? void 0 : _b.value) || "",
|
|
1318
|
+
content: ((_c = node.value) === null || _c === void 0 ? void 0 : _c.value) || "",
|
|
1394
1319
|
properties: {},
|
|
1395
1320
|
children: [],
|
|
1396
1321
|
pathFromRoot: (parent ? `${parent.pathFromRoot} ` : "") + getNodePathIdentifier(node),
|
|
1397
1322
|
backendNodeID: node.backendDOMNodeId,
|
|
1398
1323
|
// md5Sum: "",
|
|
1399
1324
|
});
|
|
1400
|
-
if (node.value
|
|
1401
|
-
processedNode.content = `${node.value
|
|
1325
|
+
if ((_d = node.value) === null || _d === void 0 ? void 0 : _d.value) {
|
|
1326
|
+
processedNode.content = `${(_e = node.value) === null || _e === void 0 ? void 0 : _e.value}`;
|
|
1402
1327
|
}
|
|
1403
1328
|
if (node.properties) {
|
|
1404
1329
|
node.properties.forEach((prop) => {
|
|
@@ -1406,7 +1331,7 @@ function processA11yTreeDFS(node, parent, inputNodeMap, outputNodeMap) {
|
|
|
1406
1331
|
});
|
|
1407
1332
|
}
|
|
1408
1333
|
outputNodeMap.set(processedNode.id, processedNode);
|
|
1409
|
-
const children = node.childIds
|
|
1334
|
+
const children = (_f = node.childIds) !== null && _f !== void 0 ? _f : [];
|
|
1410
1335
|
for (const childId of children) {
|
|
1411
1336
|
if (!childId) {
|
|
1412
1337
|
continue;
|
|
@@ -1430,7 +1355,7 @@ function processA11yTreeDFS(node, parent, inputNodeMap, outputNodeMap) {
|
|
|
1430
1355
|
if (processedNode.children.length === 1 &&
|
|
1431
1356
|
processedNode.children[0].role === "StaticText") {
|
|
1432
1357
|
const currentName = processedNode.name;
|
|
1433
|
-
const childName = processedNode.children[0]
|
|
1358
|
+
const childName = (_g = processedNode.children[0]) === null || _g === void 0 ? void 0 : _g.name;
|
|
1434
1359
|
if (currentName === childName || !childName) {
|
|
1435
1360
|
processedNode.children = [];
|
|
1436
1361
|
}
|
|
@@ -1496,12 +1421,16 @@ function processA11yTree(graph) {
|
|
|
1496
1421
|
}
|
|
1497
1422
|
// filter out nodes that are no longer rendered on the page
|
|
1498
1423
|
graph.allNodes = graph.allNodes.filter((node) => {
|
|
1424
|
+
var _a;
|
|
1499
1425
|
if (!node.ignored) {
|
|
1500
1426
|
return true;
|
|
1501
1427
|
}
|
|
1502
1428
|
// CDP types are wrong; notRendered is a possible ignored reason
|
|
1503
|
-
return !node.ignoredReasons
|
|
1504
|
-
|
|
1429
|
+
return !((_a = node.ignoredReasons) === null || _a === void 0 ? void 0 : _a.find((reason) => {
|
|
1430
|
+
var _a;
|
|
1431
|
+
return reason.name === "notRendered" &&
|
|
1432
|
+
((_a = reason.value) === null || _a === void 0 ? void 0 : _a.value);
|
|
1433
|
+
}));
|
|
1505
1434
|
});
|
|
1506
1435
|
const nodeMap = new Map();
|
|
1507
1436
|
for (const node of graph.allNodes) {
|
|
@@ -1609,7 +1538,7 @@ function addIDsScript() {
|
|
|
1609
1538
|
// Loop through all elements and add the property
|
|
1610
1539
|
for (let i = 0; i < allElements.length; i++) {
|
|
1611
1540
|
const element = allElements[i];
|
|
1612
|
-
element
|
|
1541
|
+
element === null || element === void 0 ? void 0 : element.setAttribute("data-momentic-id", currentID);
|
|
1613
1542
|
currentID++;
|
|
1614
1543
|
}
|
|
1615
1544
|
}
|
|
@@ -1678,6 +1607,15 @@ function isRequestRelevantForPageLoad(request, currentURL) {
|
|
|
1678
1607
|
|
|
1679
1608
|
|
|
1680
1609
|
;// CONCATENATED MODULE: ../../packages/web-agent/src/browsers/chrome.ts
|
|
1610
|
+
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
1611
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
1612
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
1613
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
1614
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
1615
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
1616
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
1617
|
+
});
|
|
1618
|
+
};
|
|
1681
1619
|
|
|
1682
1620
|
|
|
1683
1621
|
|
|
@@ -1705,108 +1643,120 @@ class ChromeBrowser {
|
|
|
1705
1643
|
/**
|
|
1706
1644
|
* Creates a new browser and waits for navigation to the given test URL.
|
|
1707
1645
|
*/
|
|
1708
|
-
static
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1646
|
+
static init(baseURL, logger, onScreenshot, timeout = MAX_LOAD_TIMEOUT_MS) {
|
|
1647
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1648
|
+
const browser = yield external_playwright_namespaceObject.chromium.launch({ headless: true });
|
|
1649
|
+
const context = yield browser.newContext({
|
|
1650
|
+
viewport: {
|
|
1651
|
+
width: 1920,
|
|
1652
|
+
height: 1080,
|
|
1653
|
+
},
|
|
1654
|
+
// comment out the below if you are on Mac OS but you're using a monitor
|
|
1655
|
+
deviceScaleFactor: process.platform === "darwin"
|
|
1656
|
+
? RETINA_WINDOW_SCALE_FACTOR
|
|
1657
|
+
: 1,
|
|
1658
|
+
userAgent: external_playwright_namespaceObject.devices["Desktop Chrome"].userAgent,
|
|
1659
|
+
geolocation: { latitude: 37.7749, longitude: -122.4194 },
|
|
1660
|
+
locale: "en-US",
|
|
1661
|
+
timezoneId: "America/Los_Angeles",
|
|
1662
|
+
});
|
|
1663
|
+
const page = yield context.newPage();
|
|
1664
|
+
const cdpClient = yield context.newCDPSession(page);
|
|
1665
|
+
const chrome = new ChromeBrowser({
|
|
1666
|
+
browser,
|
|
1667
|
+
context,
|
|
1668
|
+
page,
|
|
1669
|
+
baseURL,
|
|
1670
|
+
cdpClient,
|
|
1671
|
+
logger,
|
|
1672
|
+
});
|
|
1673
|
+
let completed = false;
|
|
1674
|
+
const navigateAndInitCDP = () => __awaiter(this, void 0, void 0, function* () {
|
|
1675
|
+
try {
|
|
1676
|
+
yield chrome.navigate(baseURL, false);
|
|
1677
|
+
yield cdpClient.send("Accessibility.enable");
|
|
1678
|
+
yield cdpClient.send("DOM.enable");
|
|
1679
|
+
yield cdpClient.send("Overlay.enable");
|
|
1680
|
+
}
|
|
1681
|
+
catch (err) {
|
|
1682
|
+
logger.error({ err }, "Failed to initialize chrome browser");
|
|
1683
|
+
}
|
|
1684
|
+
finally {
|
|
1685
|
+
completed = true;
|
|
1686
|
+
}
|
|
1687
|
+
});
|
|
1688
|
+
void navigateAndInitCDP();
|
|
1689
|
+
const sendScreenshot = () => __awaiter(this, void 0, void 0, function* () {
|
|
1690
|
+
if (!onScreenshot) {
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
try {
|
|
1694
|
+
onScreenshot({
|
|
1695
|
+
viewport: chrome.viewport,
|
|
1696
|
+
buffer: yield chrome.screenshot(),
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
catch (err) {
|
|
1700
|
+
logger.error({ err }, "Failed to take screenshot");
|
|
1701
|
+
}
|
|
1702
|
+
});
|
|
1703
|
+
void sendScreenshot();
|
|
1704
|
+
// NOTE: this is a very quick interval because while chome is navigating
|
|
1705
|
+
// we want to show updates to the user ASAP
|
|
1706
|
+
const screenshotInterval = setInterval(() => {
|
|
1707
|
+
void sendScreenshot();
|
|
1708
|
+
}, 250);
|
|
1709
|
+
const startTime = Date.now();
|
|
1710
|
+
while (!completed && Date.now() - startTime < timeout) {
|
|
1711
|
+
yield sleep(CHECK_INTERVAL_MS);
|
|
1759
1712
|
}
|
|
1760
|
-
|
|
1761
|
-
|
|
1713
|
+
clearInterval(screenshotInterval);
|
|
1714
|
+
if (!completed) {
|
|
1715
|
+
logger.warn("Timeout elapsed waiting for browser to initialize - are you sure this page is accessible?");
|
|
1762
1716
|
}
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
// NOTE: this is a very quick interval because while chome is navigating
|
|
1766
|
-
// we want to show updates to the user ASAP
|
|
1767
|
-
const screenshotInterval = setInterval(() => {
|
|
1768
|
-
void sendScreenshot();
|
|
1769
|
-
}, 250);
|
|
1770
|
-
const startTime = Date.now();
|
|
1771
|
-
while (!completed && Date.now() - startTime < timeout) {
|
|
1772
|
-
await sleep(CHECK_INTERVAL_MS);
|
|
1773
|
-
}
|
|
1774
|
-
clearInterval(screenshotInterval);
|
|
1775
|
-
if (!completed) {
|
|
1776
|
-
logger.warn("Timeout elapsed waiting for browser to initialize - are you sure this page is accessible?");
|
|
1777
|
-
}
|
|
1778
|
-
return chrome;
|
|
1717
|
+
return chrome;
|
|
1718
|
+
});
|
|
1779
1719
|
}
|
|
1780
1720
|
// Things to do on every page load
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1721
|
+
pageSetup() {
|
|
1722
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1723
|
+
yield this.page.evaluate(addCursorScript);
|
|
1724
|
+
yield this.page.evaluate(addIDsScript);
|
|
1725
|
+
});
|
|
1784
1726
|
}
|
|
1785
|
-
|
|
1786
|
-
|
|
1727
|
+
wait(timeoutMs) {
|
|
1728
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1729
|
+
yield this.page.waitForTimeout(timeoutMs);
|
|
1730
|
+
});
|
|
1787
1731
|
}
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1732
|
+
cleanup() {
|
|
1733
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1734
|
+
yield this.page.close();
|
|
1735
|
+
yield this.context.close();
|
|
1736
|
+
yield this.browser.close();
|
|
1737
|
+
});
|
|
1792
1738
|
}
|
|
1793
1739
|
get closed() {
|
|
1794
1740
|
return this.page.isClosed() || !this.browser.isConnected();
|
|
1795
1741
|
}
|
|
1796
|
-
|
|
1797
|
-
return
|
|
1742
|
+
html() {
|
|
1743
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1744
|
+
return yield this.page.content();
|
|
1745
|
+
});
|
|
1798
1746
|
}
|
|
1799
1747
|
get url() {
|
|
1800
1748
|
return this.page.url();
|
|
1801
1749
|
}
|
|
1802
|
-
|
|
1803
|
-
return
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1750
|
+
screenshot(quality = 100, scale = "device") {
|
|
1751
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1752
|
+
return yield this.page.screenshot({
|
|
1753
|
+
fullPage: false,
|
|
1754
|
+
quality,
|
|
1755
|
+
scale,
|
|
1756
|
+
type: "jpeg",
|
|
1757
|
+
// allow the blinking text cursor thing to remain there
|
|
1758
|
+
caret: "initial",
|
|
1759
|
+
});
|
|
1810
1760
|
});
|
|
1811
1761
|
}
|
|
1812
1762
|
get viewport() {
|
|
@@ -1816,502 +1766,560 @@ class ChromeBrowser {
|
|
|
1816
1766
|
}
|
|
1817
1767
|
return viewport;
|
|
1818
1768
|
}
|
|
1819
|
-
|
|
1769
|
+
navigate(url,
|
|
1820
1770
|
// FIXME: this is an escape hatch to make sure some pages load (assembledhq.com)
|
|
1821
1771
|
wrapPossibleNavigation = true) {
|
|
1822
|
-
this
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1772
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1773
|
+
this.logger.debug(`Navigating to ${url}`);
|
|
1774
|
+
const startTime = Date.now();
|
|
1775
|
+
const doNav = () => __awaiter(this, void 0, void 0, function* () {
|
|
1776
|
+
try {
|
|
1777
|
+
yield this.page.goto(url, {
|
|
1778
|
+
timeout: MAX_LOAD_TIMEOUT_MS,
|
|
1779
|
+
});
|
|
1780
|
+
this.logger.debug({ url }, `Got load event in ${Math.floor(Date.now() - startTime)}ms`);
|
|
1781
|
+
}
|
|
1782
|
+
catch (e) {
|
|
1783
|
+
this.logger.warn({ url, type: "navigate", err: e }, "Timeout elapsed waiting for page to load, continuing anyways...");
|
|
1784
|
+
}
|
|
1785
|
+
});
|
|
1786
|
+
if (wrapPossibleNavigation) {
|
|
1787
|
+
yield this.wrapPossibleNavigation(doNav);
|
|
1830
1788
|
}
|
|
1831
|
-
|
|
1832
|
-
|
|
1789
|
+
else {
|
|
1790
|
+
yield doNav();
|
|
1833
1791
|
}
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
if (CHROME_INTERNAL_URLS.has(this.url) &&
|
|
1842
|
-
process.env.NODE_ENV === "production") {
|
|
1843
|
-
// in dev, this is a little annoying
|
|
1844
|
-
throw new Error(`${url} took too long to load 😞. Please ensure the site and your internet are working.`);
|
|
1845
|
-
}
|
|
1846
|
-
await this.pageSetup();
|
|
1847
|
-
this.logger.debug({ url }, "Navigation complete");
|
|
1848
|
-
}
|
|
1849
|
-
async fill(target, text, options = {}) {
|
|
1850
|
-
const element = await this.click(target, {
|
|
1851
|
-
doubleClick: false,
|
|
1852
|
-
rightClick: false,
|
|
1792
|
+
if (CHROME_INTERNAL_URLS.has(this.url) &&
|
|
1793
|
+
process.env.NODE_ENV === "production") {
|
|
1794
|
+
// in dev, this is a little annoying
|
|
1795
|
+
throw new Error(`${url} took too long to load 😞. Please ensure the site and your internet are working.`);
|
|
1796
|
+
}
|
|
1797
|
+
yield this.pageSetup();
|
|
1798
|
+
this.logger.debug({ url }, "Navigation complete");
|
|
1853
1799
|
});
|
|
1854
|
-
await this.type(text, options);
|
|
1855
|
-
return element;
|
|
1856
|
-
}
|
|
1857
|
-
async type(text, options = {}) {
|
|
1858
|
-
const { clearContent = true, pressKeysSequentially = false } = options;
|
|
1859
|
-
if (clearContent) {
|
|
1860
|
-
await this.page.keyboard.press("Meta+A");
|
|
1861
|
-
await this.page.keyboard.press("Backspace");
|
|
1862
|
-
}
|
|
1863
|
-
if (pressKeysSequentially) {
|
|
1864
|
-
await this.page.keyboard.type(text);
|
|
1865
|
-
}
|
|
1866
|
-
else {
|
|
1867
|
-
await this.page.keyboard.insertText(text);
|
|
1868
|
-
}
|
|
1869
1800
|
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
}
|
|
1879
|
-
async selectOptionByA11yID(index, option) {
|
|
1880
|
-
const node = this.nodeMap.get(`${index}`);
|
|
1881
|
-
if (!node) {
|
|
1882
|
-
throw new Error(`Could not find node in DOM with index: ${index}`);
|
|
1883
|
-
}
|
|
1884
|
-
if (!node.backendNodeID) {
|
|
1885
|
-
throw new Error(`Select target missing backend node id: ${node.getLogForm()}`);
|
|
1886
|
-
}
|
|
1887
|
-
const locator = await this.getLocatorFromBackendID(node.backendNodeID);
|
|
1888
|
-
await locator.selectOption(option, {
|
|
1889
|
-
timeout: COMPLICATED_BROWSER_ACTION_TIMEOUT_MS,
|
|
1801
|
+
fill(target, text, options = {}) {
|
|
1802
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1803
|
+
const element = yield this.click(target, {
|
|
1804
|
+
doubleClick: false,
|
|
1805
|
+
rightClick: false,
|
|
1806
|
+
});
|
|
1807
|
+
yield this.type(text, options);
|
|
1808
|
+
return element;
|
|
1890
1809
|
});
|
|
1891
|
-
await this.highlightNode(node);
|
|
1892
|
-
return node.serialize({ noChildren: true, noProperties: true, noID: true });
|
|
1893
1810
|
}
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1811
|
+
type(text, options = {}) {
|
|
1812
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1813
|
+
const { clearContent = true, pressKeysSequentially = false } = options;
|
|
1814
|
+
if (clearContent) {
|
|
1815
|
+
yield this.page.keyboard.press("Meta+A");
|
|
1816
|
+
yield this.page.keyboard.press("Backspace");
|
|
1817
|
+
}
|
|
1818
|
+
if (pressKeysSequentially) {
|
|
1819
|
+
yield this.page.keyboard.type(text);
|
|
1820
|
+
}
|
|
1821
|
+
else {
|
|
1822
|
+
yield this.page.keyboard.insertText(text);
|
|
1823
|
+
}
|
|
1824
|
+
});
|
|
1902
1825
|
}
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1826
|
+
clickByA11yID(index, options = {}) {
|
|
1827
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1828
|
+
const node = this.nodeMap.get(`${index}`);
|
|
1829
|
+
if (!node) {
|
|
1830
|
+
throw new Error(`Could not find node in DOM with index: ${index}`);
|
|
1831
|
+
}
|
|
1832
|
+
const nodeClicked = yield this.clickUsingCDP(node, options);
|
|
1833
|
+
yield this.highlightNode(nodeClicked);
|
|
1834
|
+
return node.serialize({ noChildren: true, noProperties: true, noID: true });
|
|
1835
|
+
});
|
|
1912
1836
|
}
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1837
|
+
selectOptionByA11yID(index, option) {
|
|
1838
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1839
|
+
const node = this.nodeMap.get(`${index}`);
|
|
1840
|
+
if (!node) {
|
|
1841
|
+
throw new Error(`Could not find node in DOM with index: ${index}`);
|
|
1842
|
+
}
|
|
1843
|
+
if (!node.backendNodeID) {
|
|
1844
|
+
throw new Error(`Select target missing backend node id: ${node.getLogForm()}`);
|
|
1845
|
+
}
|
|
1846
|
+
const locator = yield this.getLocatorFromBackendID(node.backendNodeID);
|
|
1847
|
+
yield locator.selectOption(option, {
|
|
1848
|
+
timeout: COMPLICATED_BROWSER_ACTION_TIMEOUT_MS,
|
|
1918
1849
|
});
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1850
|
+
yield this.highlightNode(node);
|
|
1851
|
+
return node.serialize({ noChildren: true, noProperties: true, noID: true });
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1854
|
+
highlight(target) {
|
|
1855
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1924
1856
|
try {
|
|
1925
|
-
|
|
1926
|
-
backendNodeId: node.backendNodeID,
|
|
1927
|
-
});
|
|
1857
|
+
yield this.highlightByA11yID(target.id);
|
|
1928
1858
|
}
|
|
1929
1859
|
catch (err) {
|
|
1930
|
-
//
|
|
1931
|
-
this.logger.
|
|
1860
|
+
// should never be fatal
|
|
1861
|
+
this.logger.warn({ err, target }, "Failed to highlight target");
|
|
1932
1862
|
}
|
|
1933
|
-
};
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
const startURL = this.url;
|
|
1941
|
-
let lastRequestReceived = Date.now();
|
|
1942
|
-
const firedRequests = new Map();
|
|
1943
|
-
const finishedRequests = new Map();
|
|
1944
|
-
const requestFinishedListener = (request) => {
|
|
1945
|
-
const key = serializeRequest(request);
|
|
1946
|
-
finishedRequests.set(key, (finishedRequests.get(key) ?? 0) + 1);
|
|
1947
|
-
};
|
|
1948
|
-
const requestFiredListener = (request) => {
|
|
1949
|
-
if (!isRequestRelevantForPageLoad(request, this.url)) {
|
|
1950
|
-
this.logger.debug({
|
|
1951
|
-
uri: serializeRequest(request),
|
|
1952
|
-
}, "Ignoring request for page load network stability");
|
|
1953
|
-
return;
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
highlightByA11yID(index) {
|
|
1866
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1867
|
+
const node = this.nodeMap.get(`${index}`);
|
|
1868
|
+
if (!node) {
|
|
1869
|
+
throw new Error(`Could not find node in DOM with index: ${index}`);
|
|
1954
1870
|
}
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
firedRequests.set(key, (firedRequests.get(key) ?? 0) + 1);
|
|
1960
|
-
lastRequestReceived = Date.now();
|
|
1961
|
-
};
|
|
1962
|
-
this.page.on("requestfinished", requestFinishedListener);
|
|
1963
|
-
this.page.on("request", requestFiredListener);
|
|
1964
|
-
// fire actual function asynchronously
|
|
1965
|
-
// instead of throwing the error, we return it so we can handle it later
|
|
1966
|
-
let rejected = false;
|
|
1967
|
-
const retPromise = fn().catch((e) => {
|
|
1968
|
-
rejected = true;
|
|
1969
|
-
if (e instanceof Error)
|
|
1970
|
-
return e;
|
|
1971
|
-
// we are returning NOT throwing on purpose
|
|
1972
|
-
return new Error(`${e}`);
|
|
1871
|
+
if (!node.backendNodeID) {
|
|
1872
|
+
throw new Error(`Select target missing backend node id: ${node.getLogForm()}`);
|
|
1873
|
+
}
|
|
1874
|
+
yield this.highlightNode(node);
|
|
1973
1875
|
});
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1876
|
+
}
|
|
1877
|
+
highlightNode(node) {
|
|
1878
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1879
|
+
try {
|
|
1880
|
+
yield this.cdpClient.send("Overlay.highlightNode", {
|
|
1881
|
+
highlightConfig: NODE_HIGHLIGHT_CONFIG,
|
|
1882
|
+
backendNodeId: node.backendNodeID,
|
|
1883
|
+
});
|
|
1979
1884
|
}
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
if (Date.now() - lastRequestReceived <=
|
|
1989
|
-
NETWORK_STABLE_DURATION_MS) {
|
|
1990
|
-
continue;
|
|
1885
|
+
catch (err) {
|
|
1886
|
+
this.logger.warn({ err }, "Failed to add node highlight");
|
|
1887
|
+
}
|
|
1888
|
+
const hideHighlight = () => __awaiter(this, void 0, void 0, function* () {
|
|
1889
|
+
try {
|
|
1890
|
+
yield this.cdpClient.send("Overlay.hideHighlight", {
|
|
1891
|
+
backendNodeId: node.backendNodeID,
|
|
1892
|
+
});
|
|
1991
1893
|
}
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
this.logger.debug({ uri: key }, "Waiting on request to finish");
|
|
1996
|
-
anyDifference = true;
|
|
1997
|
-
unfinishedRequests.add(key);
|
|
1998
|
-
}
|
|
1894
|
+
catch (err) {
|
|
1895
|
+
// this is okay, purely visual and often occurs due to navigation
|
|
1896
|
+
this.logger.debug({ err }, "Failed to remove node highlight");
|
|
1999
1897
|
}
|
|
2000
|
-
|
|
1898
|
+
});
|
|
1899
|
+
setTimeout(() => {
|
|
1900
|
+
void hideHighlight();
|
|
1901
|
+
}, HIGHLIGHT_DURATION_MS);
|
|
1902
|
+
});
|
|
1903
|
+
}
|
|
1904
|
+
wrapPossibleNavigation(fn, timeoutMS = MAX_LOAD_TIMEOUT_MS) {
|
|
1905
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1906
|
+
const startTime = Date.now();
|
|
1907
|
+
const startURL = this.url;
|
|
1908
|
+
let lastRequestReceived = Date.now();
|
|
1909
|
+
const firedRequests = new Map();
|
|
1910
|
+
const finishedRequests = new Map();
|
|
1911
|
+
const requestFinishedListener = (request) => {
|
|
1912
|
+
var _a;
|
|
1913
|
+
const key = serializeRequest(request);
|
|
1914
|
+
finishedRequests.set(key, ((_a = finishedRequests.get(key)) !== null && _a !== void 0 ? _a : 0) + 1);
|
|
1915
|
+
};
|
|
1916
|
+
const requestFiredListener = (request) => {
|
|
1917
|
+
var _a;
|
|
1918
|
+
if (!isRequestRelevantForPageLoad(request, this.url)) {
|
|
2001
1919
|
this.logger.debug({
|
|
1920
|
+
uri: serializeRequest(request),
|
|
1921
|
+
}, "Ignoring request for page load network stability");
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
const key = serializeRequest(request);
|
|
1925
|
+
this.logger.debug({
|
|
1926
|
+
uri: key,
|
|
1927
|
+
}, "Request fired on page load, delaying network stability");
|
|
1928
|
+
firedRequests.set(key, ((_a = firedRequests.get(key)) !== null && _a !== void 0 ? _a : 0) + 1);
|
|
1929
|
+
lastRequestReceived = Date.now();
|
|
1930
|
+
};
|
|
1931
|
+
this.page.on("requestfinished", requestFinishedListener);
|
|
1932
|
+
this.page.on("request", requestFiredListener);
|
|
1933
|
+
// fire actual function asynchronously
|
|
1934
|
+
// instead of throwing the error, we return it so we can handle it later
|
|
1935
|
+
let rejected = false;
|
|
1936
|
+
const retPromise = fn().catch((e) => {
|
|
1937
|
+
rejected = true;
|
|
1938
|
+
if (e instanceof Error)
|
|
1939
|
+
return e;
|
|
1940
|
+
// we are returning NOT throwing on purpose
|
|
1941
|
+
return new Error(`${e}`);
|
|
1942
|
+
});
|
|
1943
|
+
yield sleep(CHECK_INTERVAL_MS);
|
|
1944
|
+
const unwrapAndThrowError = (p) => __awaiter(this, void 0, void 0, function* () {
|
|
1945
|
+
const v = yield p;
|
|
1946
|
+
if (v instanceof Error) {
|
|
1947
|
+
throw v;
|
|
1948
|
+
}
|
|
1949
|
+
return v;
|
|
1950
|
+
});
|
|
1951
|
+
// wait for network idle
|
|
1952
|
+
let unfinishedRequests = new Set();
|
|
1953
|
+
const waitForNetworkIdle = () => __awaiter(this, void 0, void 0, function* () {
|
|
1954
|
+
while (!rejected && Date.now() - startTime < timeoutMS) {
|
|
1955
|
+
unfinishedRequests = new Set();
|
|
1956
|
+
yield sleep(CHECK_INTERVAL_MS);
|
|
1957
|
+
if (Date.now() - lastRequestReceived <=
|
|
1958
|
+
NETWORK_STABLE_DURATION_MS) {
|
|
1959
|
+
continue;
|
|
1960
|
+
}
|
|
1961
|
+
let anyDifference = false;
|
|
1962
|
+
for (const key of firedRequests.keys()) {
|
|
1963
|
+
if (firedRequests.get(key) !== finishedRequests.get(key)) {
|
|
1964
|
+
this.logger.debug({ uri: key }, "Waiting on request to finish");
|
|
1965
|
+
anyDifference = true;
|
|
1966
|
+
unfinishedRequests.add(key);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
if (!anyDifference) {
|
|
1970
|
+
this.logger.debug({
|
|
1971
|
+
url: this.url,
|
|
1972
|
+
requests: JSON.stringify(Array.from(firedRequests.entries())),
|
|
1973
|
+
}, `Network idle in ${Math.floor(Date.now() - startTime)}ms`);
|
|
1974
|
+
return true;
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
if (!rejected) {
|
|
1978
|
+
this.logger.warn({
|
|
2002
1979
|
url: this.url,
|
|
2003
|
-
requests: JSON.stringify(Array.from(
|
|
2004
|
-
},
|
|
2005
|
-
return true;
|
|
1980
|
+
requests: JSON.stringify(Array.from(unfinishedRequests.entries())),
|
|
1981
|
+
}, "Timeout elapsed waiting for network idle, continuing anyways...");
|
|
2006
1982
|
}
|
|
1983
|
+
return false;
|
|
1984
|
+
});
|
|
1985
|
+
const waitResult = yield waitForNetworkIdle();
|
|
1986
|
+
this.page.off("requestfinished", requestFinishedListener);
|
|
1987
|
+
this.page.off("request", requestFiredListener);
|
|
1988
|
+
if (!waitResult) {
|
|
1989
|
+
return unwrapAndThrowError(retPromise);
|
|
2007
1990
|
}
|
|
2008
|
-
if (!rejected) {
|
|
2009
|
-
this.logger.
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
1991
|
+
if (!rejected && urlChanged(this.url, startURL)) {
|
|
1992
|
+
this.logger.debug(`Detected url change in wrapPossibleNavigation, waiting for load state`);
|
|
1993
|
+
try {
|
|
1994
|
+
yield this.page.waitForLoadState("load", {
|
|
1995
|
+
timeout: timeoutMS - (Date.now() - startTime),
|
|
1996
|
+
});
|
|
1997
|
+
}
|
|
1998
|
+
catch (e) {
|
|
1999
|
+
this.logger.warn({ url: this.url }, "Timeout elapsed waiting for load state to fire, continuing anyways...");
|
|
2000
|
+
}
|
|
2013
2001
|
}
|
|
2014
|
-
return false;
|
|
2015
|
-
};
|
|
2016
|
-
const waitResult = await waitForNetworkIdle();
|
|
2017
|
-
this.page.off("requestfinished", requestFinishedListener);
|
|
2018
|
-
this.page.off("request", requestFiredListener);
|
|
2019
|
-
if (!waitResult) {
|
|
2020
2002
|
return unwrapAndThrowError(retPromise);
|
|
2021
|
-
}
|
|
2022
|
-
if (!rejected && urlChanged(this.url, startURL)) {
|
|
2023
|
-
this.logger.debug(`Detected url change in wrapPossibleNavigation, waiting for load state`);
|
|
2024
|
-
try {
|
|
2025
|
-
await this.page.waitForLoadState("load", {
|
|
2026
|
-
timeout: timeoutMS - (Date.now() - startTime),
|
|
2027
|
-
});
|
|
2028
|
-
}
|
|
2029
|
-
catch (e) {
|
|
2030
|
-
this.logger.warn({ url: this.url }, "Timeout elapsed waiting for load state to fire, continuing anyways...");
|
|
2031
|
-
}
|
|
2032
|
-
}
|
|
2033
|
-
return unwrapAndThrowError(retPromise);
|
|
2003
|
+
});
|
|
2034
2004
|
}
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2005
|
+
click(target, options = {}) {
|
|
2006
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2007
|
+
const elementInteracted = yield this.wrapPossibleNavigation(() => this.clickByA11yID(target.id, options));
|
|
2008
|
+
return elementInteracted;
|
|
2009
|
+
});
|
|
2038
2010
|
}
|
|
2039
|
-
|
|
2040
|
-
return this
|
|
2011
|
+
selectOption(target, option) {
|
|
2012
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2013
|
+
return this.selectOptionByA11yID(target.id, option);
|
|
2014
|
+
});
|
|
2041
2015
|
}
|
|
2042
|
-
|
|
2043
|
-
|
|
2016
|
+
press(key) {
|
|
2017
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2018
|
+
yield this.wrapPossibleNavigation(() => this.page.keyboard.press(key));
|
|
2019
|
+
});
|
|
2044
2020
|
}
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2021
|
+
refresh() {
|
|
2022
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2023
|
+
yield this.page.reload();
|
|
2024
|
+
yield this.pageSetup();
|
|
2025
|
+
});
|
|
2048
2026
|
}
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
catch (e) {
|
|
2064
|
-
this.logger.error({ err: e, url }, "Error fetching a11y tree");
|
|
2065
|
-
if (attempt === 0) {
|
|
2066
|
-
await sleep(1000);
|
|
2067
|
-
attempt++;
|
|
2027
|
+
getA11yTree() {
|
|
2028
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2029
|
+
let processedTree = null;
|
|
2030
|
+
let attempt = 0;
|
|
2031
|
+
const url = this.url;
|
|
2032
|
+
while (!processedTree) {
|
|
2033
|
+
try {
|
|
2034
|
+
this.logger.debug(`Getting a11y tree at ${url}`);
|
|
2035
|
+
const graph = yield this.getRawA11yTree();
|
|
2036
|
+
if (!graph.root || graph.allNodes.length === 0) {
|
|
2037
|
+
// throw specific error class
|
|
2038
|
+
throw new Error("No a11y tree found on page");
|
|
2039
|
+
}
|
|
2040
|
+
processedTree = processA11yTree(graph);
|
|
2068
2041
|
}
|
|
2069
|
-
|
|
2070
|
-
|
|
2042
|
+
catch (e) {
|
|
2043
|
+
this.logger.error({ err: e, url }, "Error fetching a11y tree");
|
|
2044
|
+
if (attempt === 0) {
|
|
2045
|
+
yield sleep(1000);
|
|
2046
|
+
attempt++;
|
|
2047
|
+
}
|
|
2048
|
+
else {
|
|
2049
|
+
throw new Error(`Max retries exceeded fetching a11y tree: ${e}`);
|
|
2050
|
+
}
|
|
2071
2051
|
}
|
|
2072
2052
|
}
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
this.logger.warn("A11y tree was pruned entirely");
|
|
2077
|
-
}
|
|
2078
|
-
this.nodeMap = processedTree.nodeMap;
|
|
2079
|
-
return processedTree;
|
|
2080
|
-
}
|
|
2081
|
-
async getRawA11yTree() {
|
|
2082
|
-
const url = this.page.url();
|
|
2083
|
-
let lastTreeUpdateTimestamp = Date.now();
|
|
2084
|
-
const treeUpdateListener = () => {
|
|
2085
|
-
lastTreeUpdateTimestamp = Date.now();
|
|
2086
|
-
};
|
|
2087
|
-
this.cdpClient.addListener("Accessibility.nodesUpdated", treeUpdateListener);
|
|
2088
|
-
let accessibilityTreeLoadFired = false;
|
|
2089
|
-
const accessibilityLoadListener = () => {
|
|
2090
|
-
this.logger.info({ url }, `A11y tree load event fired`);
|
|
2091
|
-
accessibilityTreeLoadFired = true;
|
|
2092
|
-
};
|
|
2093
|
-
this.cdpClient.addListener("Accessibility.loadComplete", accessibilityLoadListener);
|
|
2094
|
-
// make sure the a11y tree hasn't updated in the last 1 second
|
|
2095
|
-
// and the a11y event has fired
|
|
2096
|
-
const a11yLoadStart = Date.now();
|
|
2097
|
-
let timeoutTriggered = true;
|
|
2098
|
-
while (Date.now() - a11yLoadStart < A11Y_STABLE_TIMEOUT_MS) {
|
|
2099
|
-
await sleep(CHECK_INTERVAL_MS);
|
|
2100
|
-
if (!accessibilityTreeLoadFired &&
|
|
2101
|
-
Date.now() - a11yLoadStart < A11Y_LOAD_TIMEOUT_MS) {
|
|
2102
|
-
// some websites never fire the a11y load event
|
|
2103
|
-
// thus, we only allocate 1 second for catching this event
|
|
2104
|
-
this.logger.debug({ url }, `A11y tree not loaded yet, waiting...`);
|
|
2105
|
-
continue;
|
|
2106
|
-
}
|
|
2107
|
-
if (Date.now() - lastTreeUpdateTimestamp >=
|
|
2108
|
-
A11Y_STABLE_DURATION_MS) {
|
|
2109
|
-
this.logger.debug({ url }, `A11y tree not stable yet, waiting...`);
|
|
2110
|
-
continue;
|
|
2053
|
+
if (!processedTree.root) {
|
|
2054
|
+
// could be valid!
|
|
2055
|
+
this.logger.warn("A11y tree was pruned entirely");
|
|
2111
2056
|
}
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
}
|
|
2115
|
-
this.logger.debug({
|
|
2116
|
-
duration: Date.now() - a11yLoadStart,
|
|
2117
|
-
eventReceived: accessibilityTreeLoadFired,
|
|
2118
|
-
timeoutTriggered,
|
|
2119
|
-
}, "A11y wait phase completed");
|
|
2120
|
-
const { node: root } = await this.cdpClient.send("Accessibility.getRootAXNode");
|
|
2121
|
-
const { nodes } = await this.cdpClient.send("Accessibility.queryAXTree", {
|
|
2122
|
-
backendNodeId: root.backendDOMNodeId,
|
|
2057
|
+
this.nodeMap = processedTree.nodeMap;
|
|
2058
|
+
return processedTree;
|
|
2123
2059
|
});
|
|
2124
|
-
this.cdpClient.removeListener("Accessibility.loadComplete", accessibilityLoadListener);
|
|
2125
|
-
this.cdpClient.removeListener("Accessibility.nodesUpdated", treeUpdateListener);
|
|
2126
|
-
return {
|
|
2127
|
-
root,
|
|
2128
|
-
allNodes: nodes,
|
|
2129
|
-
};
|
|
2130
|
-
}
|
|
2131
|
-
async clickUsingVisualCoordinates(backendNodeId) {
|
|
2132
|
-
const location = await this.getElementLocation(backendNodeId);
|
|
2133
|
-
if (!location) {
|
|
2134
|
-
throw new Error(`Could not find element location with backend node id: ${backendNodeId}`);
|
|
2135
|
-
}
|
|
2136
|
-
this.logger.debug({ location }, "Executing mouse click");
|
|
2137
|
-
await this.page.mouse.click(location.centerX, location.centerY);
|
|
2138
2060
|
}
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2061
|
+
getRawA11yTree() {
|
|
2062
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2063
|
+
const url = this.page.url();
|
|
2064
|
+
let lastTreeUpdateTimestamp = Date.now();
|
|
2065
|
+
const treeUpdateListener = () => {
|
|
2066
|
+
lastTreeUpdateTimestamp = Date.now();
|
|
2067
|
+
};
|
|
2068
|
+
this.cdpClient.addListener("Accessibility.nodesUpdated", treeUpdateListener);
|
|
2069
|
+
let accessibilityTreeLoadFired = false;
|
|
2070
|
+
const accessibilityLoadListener = () => {
|
|
2071
|
+
this.logger.info({ url }, `A11y tree load event fired`);
|
|
2072
|
+
accessibilityTreeLoadFired = true;
|
|
2073
|
+
};
|
|
2074
|
+
this.cdpClient.addListener("Accessibility.loadComplete", accessibilityLoadListener);
|
|
2075
|
+
// make sure the a11y tree hasn't updated in the last 1 second
|
|
2076
|
+
// and the a11y event has fired
|
|
2077
|
+
const a11yLoadStart = Date.now();
|
|
2078
|
+
let timeoutTriggered = true;
|
|
2079
|
+
while (Date.now() - a11yLoadStart < A11Y_STABLE_TIMEOUT_MS) {
|
|
2080
|
+
yield sleep(CHECK_INTERVAL_MS);
|
|
2081
|
+
if (!accessibilityTreeLoadFired &&
|
|
2082
|
+
Date.now() - a11yLoadStart < A11Y_LOAD_TIMEOUT_MS) {
|
|
2083
|
+
// some websites never fire the a11y load event
|
|
2084
|
+
// thus, we only allocate 1 second for catching this event
|
|
2085
|
+
this.logger.debug({ url }, `A11y tree not loaded yet, waiting...`);
|
|
2086
|
+
continue;
|
|
2087
|
+
}
|
|
2088
|
+
if (Date.now() - lastTreeUpdateTimestamp >=
|
|
2089
|
+
A11Y_STABLE_DURATION_MS) {
|
|
2090
|
+
this.logger.debug({ url }, `A11y tree not stable yet, waiting...`);
|
|
2091
|
+
continue;
|
|
2092
|
+
}
|
|
2093
|
+
timeoutTriggered = false;
|
|
2094
|
+
break;
|
|
2095
|
+
}
|
|
2096
|
+
this.logger.debug({
|
|
2097
|
+
duration: Date.now() - a11yLoadStart,
|
|
2098
|
+
eventReceived: accessibilityTreeLoadFired,
|
|
2099
|
+
timeoutTriggered,
|
|
2100
|
+
}, "A11y wait phase completed");
|
|
2101
|
+
const { node: root } = yield this.cdpClient.send("Accessibility.getRootAXNode");
|
|
2102
|
+
const { nodes } = yield this.cdpClient.send("Accessibility.queryAXTree", {
|
|
2103
|
+
backendNodeId: root.backendDOMNodeId,
|
|
2104
|
+
});
|
|
2105
|
+
this.cdpClient.removeListener("Accessibility.loadComplete", accessibilityLoadListener);
|
|
2106
|
+
this.cdpClient.removeListener("Accessibility.nodesUpdated", treeUpdateListener);
|
|
2107
|
+
return {
|
|
2108
|
+
root,
|
|
2109
|
+
allNodes: nodes,
|
|
2110
|
+
};
|
|
2148
2111
|
});
|
|
2149
|
-
const attributes = attrResult.attributes;
|
|
2150
|
-
const indexAttr = attributes.findIndex((s) => s === "data-momentic-id");
|
|
2151
|
-
if (indexAttr === -1) {
|
|
2152
|
-
return "";
|
|
2153
|
-
}
|
|
2154
|
-
return attributes[indexAttr + 1] || "";
|
|
2155
2112
|
}
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2113
|
+
clickUsingVisualCoordinates(backendNodeId) {
|
|
2114
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2115
|
+
const location = yield this.getElementLocation(backendNodeId);
|
|
2116
|
+
if (!location) {
|
|
2117
|
+
throw new Error(`Could not find element location with backend node id: ${backendNodeId}`);
|
|
2118
|
+
}
|
|
2119
|
+
this.logger.debug({ location }, "Executing mouse click");
|
|
2120
|
+
yield this.page.mouse.click(location.centerX, location.centerY);
|
|
2161
2121
|
});
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2122
|
+
}
|
|
2123
|
+
// Get the "id" attribute value from an HTML element.
|
|
2124
|
+
getIDAttributeUsingCDP(objectId) {
|
|
2125
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2126
|
+
// https://bugs.chromium.org/p/chromium/issues/detail?id=1374241
|
|
2127
|
+
yield this.cdpClient.send("DOM.getDocument", { depth: 0 });
|
|
2128
|
+
const cdpNodeResult = yield this.cdpClient.send("DOM.requestNode", {
|
|
2129
|
+
objectId,
|
|
2130
|
+
});
|
|
2131
|
+
const attrResult = yield this.cdpClient.send("DOM.getAttributes", {
|
|
2132
|
+
nodeId: cdpNodeResult.nodeId,
|
|
2133
|
+
});
|
|
2134
|
+
const attributes = attrResult.attributes;
|
|
2135
|
+
const indexAttr = attributes.findIndex((s) => s === "data-momentic-id");
|
|
2136
|
+
if (indexAttr === -1) {
|
|
2137
|
+
return "";
|
|
2169
2138
|
}
|
|
2170
|
-
return
|
|
2171
|
-
}
|
|
2172
|
-
catch (err) {
|
|
2173
|
-
this.logger.error({
|
|
2174
|
-
err,
|
|
2175
|
-
}, "Failed to get ID attribute");
|
|
2176
|
-
throw err;
|
|
2177
|
-
}
|
|
2139
|
+
return attributes[indexAttr + 1] || "";
|
|
2140
|
+
});
|
|
2178
2141
|
}
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2142
|
+
getLocatorFromBackendID(backendNodeId) {
|
|
2143
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2144
|
+
yield this.page.evaluate(addIDsScript);
|
|
2145
|
+
// get a remote javascript object from the a11y backend node ID
|
|
2146
|
+
const cdpResolveResult = yield this.cdpClient.send("DOM.resolveNode", {
|
|
2147
|
+
backendNodeId,
|
|
2148
|
+
});
|
|
2149
|
+
if (!cdpResolveResult || !cdpResolveResult.object.objectId) {
|
|
2150
|
+
throw new Error(`Could not resolve backend node ${backendNodeId}`);
|
|
2186
2151
|
}
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2152
|
+
try {
|
|
2153
|
+
const id = yield this.getIDAttributeUsingCDP(cdpResolveResult.object.objectId);
|
|
2154
|
+
if (!id) {
|
|
2155
|
+
throw new Error("Failed getting data-momentic-id attribute using CDP");
|
|
2156
|
+
}
|
|
2157
|
+
return this.page.locator(`[data-momentic-id="${id}"]`);
|
|
2192
2158
|
}
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2159
|
+
catch (err) {
|
|
2160
|
+
this.logger.error({
|
|
2161
|
+
err,
|
|
2162
|
+
}, "Failed to get ID attribute");
|
|
2163
|
+
throw err;
|
|
2198
2164
|
}
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
});
|
|
2165
|
+
});
|
|
2166
|
+
}
|
|
2167
|
+
clickUsingCDP(originalNode, options = {}) {
|
|
2168
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2169
|
+
let clickAttempts = 0;
|
|
2170
|
+
let candidateNode = originalNode;
|
|
2171
|
+
while (clickAttempts < MAX_BROWSER_ACTION_ATTEMPTS) {
|
|
2172
|
+
// Did we reach the root?
|
|
2173
|
+
if (!candidateNode || candidateNode.role === "RootWebArea") {
|
|
2174
|
+
throw new Error(`Attempted to click node with no clickable surrounding elements: ${originalNode.getLogForm()}`);
|
|
2209
2175
|
}
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2176
|
+
// Check disqualifying conditions for clicks - these don't count as "attempts"
|
|
2177
|
+
if (candidateNode.role === "StaticText") {
|
|
2178
|
+
// static text corresponds to html text nodes that are not clickable
|
|
2179
|
+
candidateNode = candidateNode.parent;
|
|
2180
|
+
continue;
|
|
2215
2181
|
}
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2182
|
+
const candidateNodeID = candidateNode.backendNodeID;
|
|
2183
|
+
if (!candidateNodeID) {
|
|
2184
|
+
this.logger.warn({ node: candidateNode.getLogForm() }, "Click candidate had no backend node ID");
|
|
2185
|
+
candidateNode = candidateNode.parent;
|
|
2186
|
+
continue;
|
|
2187
|
+
}
|
|
2188
|
+
// Attempt to click
|
|
2189
|
+
try {
|
|
2190
|
+
const locator = yield this.getLocatorFromBackendID(candidateNodeID);
|
|
2191
|
+
// this timeout is important because playwright checks for clickability/visibility
|
|
2192
|
+
// before clicking, and a timeout indicates obstruction, or disabled state
|
|
2193
|
+
// see: https://playwright.dev/docs/actionability#introduction
|
|
2194
|
+
if (options.doubleClick) {
|
|
2195
|
+
yield locator.dblclick({
|
|
2196
|
+
timeout: BROWSER_ACTION_TIMEOUT_MS,
|
|
2197
|
+
});
|
|
2198
|
+
}
|
|
2199
|
+
else {
|
|
2200
|
+
yield locator.click({
|
|
2201
|
+
timeout: BROWSER_ACTION_TIMEOUT_MS,
|
|
2202
|
+
button: options.rightClick ? "right" : "left",
|
|
2203
|
+
});
|
|
2204
|
+
}
|
|
2205
|
+
if (candidateNode.id !== originalNode.id) {
|
|
2206
|
+
this.logger.info({
|
|
2207
|
+
oldNode: originalNode.getLogForm(),
|
|
2208
|
+
newNode: candidateNode.getLogForm(),
|
|
2209
|
+
}, `Redirected click successfully to new element`);
|
|
2210
|
+
}
|
|
2211
|
+
return candidateNode;
|
|
2212
|
+
}
|
|
2213
|
+
catch (err) {
|
|
2214
|
+
this.logger.error({ err, node: candidateNode.getLogForm() }, "Failed click or click timed out");
|
|
2215
|
+
clickAttempts++;
|
|
2216
|
+
// try to click the parent instead
|
|
2217
|
+
// we could re-prompt the LLM in the future
|
|
2218
|
+
candidateNode = candidateNode.parent;
|
|
2221
2219
|
}
|
|
2222
|
-
return candidateNode;
|
|
2223
|
-
}
|
|
2224
|
-
catch (err) {
|
|
2225
|
-
this.logger.error({ err, node: candidateNode.getLogForm() }, "Failed click or click timed out");
|
|
2226
|
-
clickAttempts++;
|
|
2227
|
-
// try to click the parent instead
|
|
2228
|
-
// we could re-prompt the LLM in the future
|
|
2229
|
-
candidateNode = candidateNode.parent;
|
|
2230
2220
|
}
|
|
2231
|
-
|
|
2232
|
-
|
|
2221
|
+
throw new Error(`Max click redirection attempts exhausted on original element: ${originalNode.getLogForm()}`);
|
|
2222
|
+
});
|
|
2233
2223
|
}
|
|
2234
2224
|
/**
|
|
2235
2225
|
* Currently unused, but could be useful for vision model integration.
|
|
2236
2226
|
* Gets x/y position of an a11y node.
|
|
2237
2227
|
*/
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
const document = tree["documents"][0];
|
|
2253
|
-
const layout = document["layout"];
|
|
2254
|
-
const nodes = document["nodes"];
|
|
2255
|
-
const nodeNames = nodes["nodeName"] || [];
|
|
2256
|
-
const backendNodeIds = nodes["backendNodeId"] || [];
|
|
2257
|
-
const layoutNodeIndex = layout["nodeIndex"];
|
|
2258
|
-
const bounds = layout["bounds"];
|
|
2259
|
-
let cursor = -1;
|
|
2260
|
-
for (let i = 0; i < nodeNames.length; i++) {
|
|
2261
|
-
if (backendNodeIds[i] === backendNodeId) {
|
|
2262
|
-
cursor = layoutNodeIndex.indexOf(i);
|
|
2263
|
-
break;
|
|
2228
|
+
getElementLocation(backendNodeId) {
|
|
2229
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2230
|
+
const tree = yield this.cdpClient.send("DOMSnapshot.captureSnapshot", {
|
|
2231
|
+
computedStyles: [],
|
|
2232
|
+
includeDOMRects: true,
|
|
2233
|
+
includePaintOrder: true,
|
|
2234
|
+
});
|
|
2235
|
+
let devicePixelRatio = yield this.page.evaluate(() => window.devicePixelRatio);
|
|
2236
|
+
// it lies on macos lolol
|
|
2237
|
+
// this apparently isn't working when the browser is dragged onto a monitor either
|
|
2238
|
+
if (process.platform === "darwin" && devicePixelRatio === 1) {
|
|
2239
|
+
// UNCOMMENT THE BELOW IF YOU ARE ON MAC OS AND NOT USING A MONITOR
|
|
2240
|
+
// COMMENT THE BELOW OUT IF YOU ARE USING A MONITOR OR NOT ON MAC OS
|
|
2241
|
+
devicePixelRatio = RETINA_WINDOW_SCALE_FACTOR;
|
|
2264
2242
|
}
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2243
|
+
const document = tree["documents"][0];
|
|
2244
|
+
const layout = document["layout"];
|
|
2245
|
+
const nodes = document["nodes"];
|
|
2246
|
+
const nodeNames = nodes["nodeName"] || [];
|
|
2247
|
+
const backendNodeIds = nodes["backendNodeId"] || [];
|
|
2248
|
+
const layoutNodeIndex = layout["nodeIndex"];
|
|
2249
|
+
const bounds = layout["bounds"];
|
|
2250
|
+
let cursor = -1;
|
|
2251
|
+
for (let i = 0; i < nodeNames.length; i++) {
|
|
2252
|
+
if (backendNodeIds[i] === backendNodeId) {
|
|
2253
|
+
cursor = layoutNodeIndex.indexOf(i);
|
|
2254
|
+
break;
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
if (cursor === -1) {
|
|
2258
|
+
throw new Error(`Could not find any backend node with ID ${backendNodeId}`);
|
|
2259
|
+
}
|
|
2260
|
+
let [x = 0, y = 0, width = 0, height = 0] = bounds[cursor];
|
|
2261
|
+
x /= devicePixelRatio;
|
|
2262
|
+
y /= devicePixelRatio;
|
|
2263
|
+
width /= devicePixelRatio;
|
|
2264
|
+
height /= devicePixelRatio;
|
|
2265
|
+
const centerX = x + width / 2;
|
|
2266
|
+
const centerY = y + height / 2;
|
|
2267
|
+
return { centerX, centerY };
|
|
2284
2268
|
});
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2269
|
+
}
|
|
2270
|
+
scrollUp() {
|
|
2271
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2272
|
+
// TODO: this works pretty well for full page scroll, in the future we'd need to figure out how to scroll nested containers
|
|
2273
|
+
yield this.page.evaluate(() => {
|
|
2274
|
+
(document.scrollingElement || document.body).scrollTop =
|
|
2275
|
+
(document.scrollingElement || document.body).scrollTop -
|
|
2276
|
+
window.innerHeight;
|
|
2277
|
+
});
|
|
2278
|
+
yield this.page.evaluate(() => {
|
|
2279
|
+
(document.scrollingElement || document.body).scrollTop =
|
|
2280
|
+
(document.scrollingElement || document.body).scrollTop +
|
|
2281
|
+
window.innerHeight;
|
|
2282
|
+
});
|
|
2289
2283
|
});
|
|
2290
2284
|
}
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
(document.scrollingElement || document.body).scrollTop
|
|
2295
|
-
|
|
2285
|
+
scrollDown() {
|
|
2286
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2287
|
+
yield this.page.evaluate(() => {
|
|
2288
|
+
(document.scrollingElement || document.body).scrollTop =
|
|
2289
|
+
(document.scrollingElement || document.body).scrollTop +
|
|
2290
|
+
window.innerHeight;
|
|
2291
|
+
});
|
|
2296
2292
|
});
|
|
2297
2293
|
}
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2294
|
+
goForward() {
|
|
2295
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2296
|
+
yield this.wrapPossibleNavigation(() => this.page.goForward({ timeout: MAX_LOAD_TIMEOUT_MS }));
|
|
2297
|
+
yield this.pageSetup();
|
|
2298
|
+
});
|
|
2301
2299
|
}
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2300
|
+
goBack() {
|
|
2301
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2302
|
+
yield this.wrapPossibleNavigation(() => this.page.goBack({ timeout: MAX_LOAD_TIMEOUT_MS }));
|
|
2303
|
+
yield this.pageSetup();
|
|
2304
|
+
});
|
|
2305
2305
|
}
|
|
2306
2306
|
}
|
|
2307
2307
|
ChromeBrowser.USER_AGENT = external_playwright_namespaceObject.devices["Desktop Chrome"].userAgent;
|
|
2308
2308
|
|
|
2309
2309
|
|
|
2310
2310
|
;// CONCATENATED MODULE: external "diff-lines"
|
|
2311
|
-
|
|
2312
|
-
var
|
|
2313
|
-
const external_diff_lines_namespaceObject = external_diff_lines_x({ ["default"]: () => __WEBPACK_EXTERNAL_MODULE_diff_lines_24b6f423__["default"] });
|
|
2311
|
+
const external_diff_lines_namespaceObject = require("diff-lines");
|
|
2312
|
+
var external_diff_lines_default = /*#__PURE__*/__nccwpck_require__.n(external_diff_lines_namespaceObject);
|
|
2314
2313
|
;// CONCATENATED MODULE: ../../packages/web-agent/src/controller.ts
|
|
2314
|
+
var controller_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2315
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
2316
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
2317
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
2318
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
2319
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
2320
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
2321
|
+
});
|
|
2322
|
+
};
|
|
2315
2323
|
|
|
2316
2324
|
// @ts-expect-error: no types from this library
|
|
2317
2325
|
|
|
@@ -2353,16 +2361,20 @@ class AgentController {
|
|
|
2353
2361
|
/**
|
|
2354
2362
|
* Reset controller and browser state.
|
|
2355
2363
|
*/
|
|
2356
|
-
|
|
2357
|
-
this
|
|
2358
|
-
|
|
2364
|
+
resetState() {
|
|
2365
|
+
return controller_awaiter(this, void 0, void 0, function* () {
|
|
2366
|
+
this.resetHistory();
|
|
2367
|
+
yield this.browser.navigate(this.browser.baseURL);
|
|
2368
|
+
});
|
|
2359
2369
|
}
|
|
2360
2370
|
/**
|
|
2361
2371
|
* Get the browser state as a string
|
|
2362
2372
|
*/
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2373
|
+
getBrowserState() {
|
|
2374
|
+
return controller_awaiter(this, void 0, void 0, function* () {
|
|
2375
|
+
const a11yTree = yield this.browser.getA11yTree();
|
|
2376
|
+
return a11yTree.serialize();
|
|
2377
|
+
});
|
|
2366
2378
|
}
|
|
2367
2379
|
getSerializedHistory(url, currentBrowserState) {
|
|
2368
2380
|
let history;
|
|
@@ -2374,84 +2386,90 @@ class AgentController {
|
|
|
2374
2386
|
}
|
|
2375
2387
|
return history;
|
|
2376
2388
|
}
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2389
|
+
splitUserGoal(type, goal, disableCache) {
|
|
2390
|
+
return controller_awaiter(this, void 0, void 0, function* () {
|
|
2391
|
+
if (type === StepType.AI_ACTION &&
|
|
2392
|
+
goal.match(/[,!;.]|(?:and)|(?:then)/) &&
|
|
2393
|
+
this.config.useGoalSplitter) {
|
|
2394
|
+
const granularInstructions = yield this.generator.getGranularGoals({ goal, url: this.browser.url }, disableCache);
|
|
2395
|
+
// convert into a stack (last element is first to be executed)
|
|
2396
|
+
this.pendingInstructions = granularInstructions.reverse();
|
|
2397
|
+
}
|
|
2398
|
+
else {
|
|
2399
|
+
this.pendingInstructions = [goal];
|
|
2400
|
+
}
|
|
2401
|
+
});
|
|
2388
2402
|
}
|
|
2389
2403
|
/**
|
|
2390
2404
|
* Given previously executed commands, generate command for the current prompt.
|
|
2391
2405
|
* Should only be used for AI action.
|
|
2392
2406
|
*/
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
const currInstruction = this.pendingInstructions[this.pendingInstructions.length - 1];
|
|
2400
|
-
this.logger.info({ goal: currInstruction }, "Starting prompt translation");
|
|
2401
|
-
const getBrowserStateStart = Date.now();
|
|
2402
|
-
const url = this.browser.url;
|
|
2403
|
-
const browserState = await this.getBrowserState();
|
|
2404
|
-
this.logger.info({
|
|
2405
|
-
duration: Date.now() - getBrowserStateStart,
|
|
2406
|
-
url,
|
|
2407
|
-
}, "Got browser state");
|
|
2408
|
-
const numPrevious = this.commandHistory.length;
|
|
2409
|
-
this.commandHistory.push({
|
|
2410
|
-
state: "PENDING",
|
|
2411
|
-
browserStateBeforeCommand: browserState,
|
|
2412
|
-
urlBeforeCommand: url,
|
|
2413
|
-
type,
|
|
2414
|
-
});
|
|
2415
|
-
const history = this.getSerializedHistory(url, browserState);
|
|
2416
|
-
const getCommandProposalStart = Date.now();
|
|
2417
|
-
const proposedCommand = await this.generator.getProposedCommand({
|
|
2418
|
-
url,
|
|
2419
|
-
numPrevious,
|
|
2420
|
-
browserState,
|
|
2421
|
-
history,
|
|
2422
|
-
goal: currInstruction,
|
|
2423
|
-
lastCommand: this.lastExecutedCommand,
|
|
2424
|
-
}, disableCache);
|
|
2425
|
-
this.logger.info({ duration: Date.now() - getCommandProposalStart }, "Got proposed command");
|
|
2426
|
-
if (proposedCommand.type === ControlFlowCommandType.SUCCESS) {
|
|
2427
|
-
const finishedInstruction = this.pendingInstructions.pop();
|
|
2428
|
-
this.logger.info({
|
|
2429
|
-
finishedInstruction,
|
|
2430
|
-
remainingInstructions: this.pendingInstructions,
|
|
2431
|
-
}, "Removing pending instruction due to SUCCESS");
|
|
2432
|
-
// promptToCommand will pick the next instruction to execute off the stack
|
|
2433
|
-
if (this.pendingInstructions.length !== 0) {
|
|
2434
|
-
// remove the last command from the history since it was an intermediate command from goalSplitter
|
|
2435
|
-
this.commandHistory.pop();
|
|
2436
|
-
return this.promptToCommand(type, "", disableCache);
|
|
2407
|
+
promptToCommand(type, goal, disableCache) {
|
|
2408
|
+
return controller_awaiter(this, void 0, void 0, function* () {
|
|
2409
|
+
// are we out of granular instructions to execute?
|
|
2410
|
+
if (this.pendingInstructions.length === 0) {
|
|
2411
|
+
// stores granular instructions in this.pendingInstructions, which functions like a stack
|
|
2412
|
+
yield this.splitUserGoal(type, goal, disableCache);
|
|
2437
2413
|
}
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2414
|
+
const currInstruction = this.pendingInstructions[this.pendingInstructions.length - 1];
|
|
2415
|
+
this.logger.info({ goal: currInstruction }, "Starting prompt translation");
|
|
2416
|
+
const getBrowserStateStart = Date.now();
|
|
2417
|
+
const url = this.browser.url;
|
|
2418
|
+
const browserState = yield this.getBrowserState();
|
|
2442
2419
|
this.logger.info({
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2420
|
+
duration: Date.now() - getBrowserStateStart,
|
|
2421
|
+
url,
|
|
2422
|
+
}, "Got browser state");
|
|
2423
|
+
const numPrevious = this.commandHistory.length;
|
|
2424
|
+
this.commandHistory.push({
|
|
2425
|
+
state: "PENDING",
|
|
2426
|
+
browserStateBeforeCommand: browserState,
|
|
2427
|
+
urlBeforeCommand: url,
|
|
2428
|
+
type,
|
|
2429
|
+
});
|
|
2430
|
+
const history = this.getSerializedHistory(url, browserState);
|
|
2431
|
+
const getCommandProposalStart = Date.now();
|
|
2432
|
+
const proposedCommand = yield this.generator.getProposedCommand({
|
|
2433
|
+
url,
|
|
2434
|
+
numPrevious,
|
|
2435
|
+
browserState,
|
|
2436
|
+
history,
|
|
2437
|
+
goal: currInstruction,
|
|
2438
|
+
lastCommand: this.lastExecutedCommand,
|
|
2439
|
+
}, disableCache);
|
|
2440
|
+
this.logger.info({ duration: Date.now() - getCommandProposalStart }, "Got proposed command");
|
|
2441
|
+
if (proposedCommand.type === ControlFlowCommandType.SUCCESS) {
|
|
2442
|
+
const finishedInstruction = this.pendingInstructions.pop();
|
|
2443
|
+
this.logger.info({
|
|
2444
|
+
finishedInstruction,
|
|
2445
|
+
remainingInstructions: this.pendingInstructions,
|
|
2446
|
+
}, "Removing pending instruction due to SUCCESS");
|
|
2447
|
+
// promptToCommand will pick the next instruction to execute off the stack
|
|
2448
|
+
if (this.pendingInstructions.length !== 0) {
|
|
2449
|
+
// remove the last command from the history since it was an intermediate command from goalSplitter
|
|
2450
|
+
this.commandHistory.pop();
|
|
2451
|
+
return this.promptToCommand(type, "", disableCache);
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
else if (
|
|
2455
|
+
// on failure, we don't continue to execute
|
|
2456
|
+
proposedCommand.type === ControlFlowCommandType.FAILURE) {
|
|
2457
|
+
this.logger.info({
|
|
2458
|
+
remainingInstructions: this.pendingInstructions,
|
|
2459
|
+
}, "Removing pending instructions due to FAILURE");
|
|
2460
|
+
this.pendingInstructions = [];
|
|
2461
|
+
}
|
|
2462
|
+
return proposedCommand;
|
|
2463
|
+
});
|
|
2448
2464
|
}
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2465
|
+
locateElement(description, disableCache) {
|
|
2466
|
+
return controller_awaiter(this, void 0, void 0, function* () {
|
|
2467
|
+
const locator = yield this.generator.getElementLocation({ browserState: yield this.getBrowserState(), goal: description }, disableCache);
|
|
2468
|
+
if (locator.id < 0) {
|
|
2469
|
+
throw new Error(`Unable to locate element with description: ${description}`);
|
|
2470
|
+
}
|
|
2471
|
+
return locator;
|
|
2472
|
+
});
|
|
2455
2473
|
}
|
|
2456
2474
|
/**
|
|
2457
2475
|
* Construct a detailed history that can be passed to the LLM.
|
|
@@ -2475,7 +2493,7 @@ class AgentController {
|
|
|
2475
2493
|
historyLines.push(` URL CHANGE: '${log.urlBeforeCommand}' -> '${currentURL}'`);
|
|
2476
2494
|
}
|
|
2477
2495
|
else {
|
|
2478
|
-
const browserStateDiff = (
|
|
2496
|
+
const browserStateDiff = external_diff_lines_default()(log.browserStateBeforeCommand, currentPageState, {
|
|
2479
2497
|
n_surrounding: 1,
|
|
2480
2498
|
});
|
|
2481
2499
|
if (!browserStateDiff) {
|
|
@@ -2498,7 +2516,7 @@ class AgentController {
|
|
|
2498
2516
|
return historyLines.join("\n");
|
|
2499
2517
|
}
|
|
2500
2518
|
getListHistory() {
|
|
2501
|
-
return
|
|
2519
|
+
return (external_dedent_default()) `Here are the commands that you have successfully executed:
|
|
2502
2520
|
${this.commandHistory
|
|
2503
2521
|
.filter((cmd) => cmd.type === StepType.AI_ACTION)
|
|
2504
2522
|
.map((cmd) => `- ${cmd.serializedCommand}`)
|
|
@@ -2509,254 +2527,261 @@ class AgentController {
|
|
|
2509
2527
|
* @param [stateless=false] Execute this command in a stateless fashion, without modifying any controller state such as
|
|
2510
2528
|
* pending instructions. Useful when executing cached instructions.
|
|
2511
2529
|
*/
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2530
|
+
executeCommand(command, disableCache, stateless = false) {
|
|
2531
|
+
return controller_awaiter(this, void 0, void 0, function* () {
|
|
2532
|
+
const pendingHistory = this.commandHistory[this.commandHistory.length - 1];
|
|
2533
|
+
if (!stateless) {
|
|
2534
|
+
// if we're not using cached commands, we must be executing a pending command
|
|
2535
|
+
// generated by promptToCommand
|
|
2536
|
+
if (!pendingHistory || pendingHistory.state !== "PENDING") {
|
|
2537
|
+
throw new Error("Executing command but there is no pending entry in the history");
|
|
2538
|
+
}
|
|
2519
2539
|
}
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2540
|
+
else {
|
|
2541
|
+
// cached commands can rely on things like a11y IDs - we need to populate this state in the chrome browser.
|
|
2542
|
+
// currently, all necessary side effects are accomplished by getting the a11y tree
|
|
2543
|
+
yield this.browser.getA11yTree();
|
|
2544
|
+
}
|
|
2545
|
+
let result;
|
|
2546
|
+
try {
|
|
2547
|
+
const executionStart = Date.now();
|
|
2548
|
+
result = yield this.sendCommandToBrowser(command, disableCache);
|
|
2549
|
+
this.logger.info({ result, duration: Date.now() - executionStart }, "Got execution result");
|
|
2550
|
+
}
|
|
2551
|
+
catch (e) {
|
|
2552
|
+
if (e instanceof Error) {
|
|
2553
|
+
throw new BrowserExecutionError(`Failed to execute command: ${e}`, {
|
|
2554
|
+
cause: e,
|
|
2555
|
+
});
|
|
2556
|
+
}
|
|
2557
|
+
throw new BrowserExecutionError(`Unexpected throw from executing command`, {
|
|
2558
|
+
cause: new Error(`${e}`),
|
|
2536
2559
|
});
|
|
2537
2560
|
}
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
// we still have pending instructions queued up
|
|
2547
|
-
// override the immediate success
|
|
2548
|
-
result.succeedImmediately = false;
|
|
2561
|
+
if (result.succeedImmediately && !stateless) {
|
|
2562
|
+
// pop the last command off the stack since we won't get a real SUCCESS command within promptToCommand
|
|
2563
|
+
this.pendingInstructions.pop();
|
|
2564
|
+
if (this.pendingInstructions.length > 0) {
|
|
2565
|
+
// we still have pending instructions queued up
|
|
2566
|
+
// override the immediate success
|
|
2567
|
+
result.succeedImmediately = false;
|
|
2568
|
+
}
|
|
2549
2569
|
}
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2570
|
+
// TODO(ENG-139): Save other a11y stuff as well.
|
|
2571
|
+
// Update the command with the targeted element
|
|
2572
|
+
// if this is the first time the command was generated
|
|
2573
|
+
if (result.elementInteracted &&
|
|
2574
|
+
"target" in command &&
|
|
2575
|
+
!command.target.elementDescriptor) {
|
|
2576
|
+
// Save the serialized element interacted as the "descriptor" for now
|
|
2577
|
+
// In the future, we can ask the LLM for a more human-readable descriptor
|
|
2578
|
+
command.target.elementDescriptor = result.elementInteracted;
|
|
2579
|
+
}
|
|
2580
|
+
if (!stateless) {
|
|
2581
|
+
// the conditional at the beginning of this function validates that pendingHistory isn't undefined
|
|
2582
|
+
// if stateless is false
|
|
2583
|
+
pendingHistory.generatedStep = command;
|
|
2584
|
+
pendingHistory.serializedCommand = serializeAICommand(command);
|
|
2585
|
+
pendingHistory.state = "DONE";
|
|
2586
|
+
}
|
|
2587
|
+
return result;
|
|
2588
|
+
});
|
|
2569
2589
|
}
|
|
2570
2590
|
/**
|
|
2571
2591
|
* Executes a preset command.
|
|
2572
2592
|
* For most cases, the execution result contains metadata about the command executed.
|
|
2573
2593
|
* For assertions, an AssertionResult with thoughts is returned.
|
|
2574
2594
|
*/
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
params
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2595
|
+
executePresetStep(command, disableCache) {
|
|
2596
|
+
var _a, _b;
|
|
2597
|
+
return controller_awaiter(this, void 0, void 0, function* () {
|
|
2598
|
+
const urlBeforeCommand = this.browser.url;
|
|
2599
|
+
switch (command.type) {
|
|
2600
|
+
case preset_PresetCommandType.AI_ASSERTION: {
|
|
2601
|
+
let params;
|
|
2602
|
+
if (command.useVision) {
|
|
2603
|
+
params = {
|
|
2604
|
+
goal: command.assertion,
|
|
2605
|
+
url: urlBeforeCommand,
|
|
2606
|
+
// used for vision only
|
|
2607
|
+
screenshot: yield this.browser.screenshot(),
|
|
2608
|
+
// unused for visual assertion
|
|
2609
|
+
browserState: "",
|
|
2610
|
+
history: "",
|
|
2611
|
+
numPrevious: -1,
|
|
2612
|
+
lastCommand: null,
|
|
2613
|
+
};
|
|
2614
|
+
}
|
|
2615
|
+
else {
|
|
2616
|
+
const browserState = yield this.getBrowserState();
|
|
2617
|
+
const history = this.getSerializedHistory(urlBeforeCommand, browserState);
|
|
2618
|
+
params = {
|
|
2619
|
+
goal: command.assertion,
|
|
2620
|
+
url: urlBeforeCommand,
|
|
2621
|
+
// used for text only
|
|
2622
|
+
browserState,
|
|
2623
|
+
history,
|
|
2624
|
+
lastCommand: this.lastExecutedCommand,
|
|
2625
|
+
numPrevious: this.commandHistory.length,
|
|
2626
|
+
};
|
|
2627
|
+
}
|
|
2628
|
+
const result = yield this.generator.getAssertionResult(params, command.useVision, command.disableCache);
|
|
2629
|
+
if (result.relevantElements) {
|
|
2630
|
+
// highlight relevant elements
|
|
2631
|
+
void Promise.all(result.relevantElements.map((id) => this.browser.highlight({ id })));
|
|
2632
|
+
}
|
|
2633
|
+
return result;
|
|
2592
2634
|
}
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2635
|
+
case preset_PresetCommandType.NAVIGATE:
|
|
2636
|
+
yield this.browser.navigate(command.url);
|
|
2637
|
+
break;
|
|
2638
|
+
case preset_PresetCommandType.GO_BACK:
|
|
2639
|
+
yield this.browser.goBack();
|
|
2640
|
+
break;
|
|
2641
|
+
case preset_PresetCommandType.GO_FORWARD:
|
|
2642
|
+
yield this.browser.goForward();
|
|
2643
|
+
break;
|
|
2644
|
+
case preset_PresetCommandType.SCROLL_DOWN:
|
|
2645
|
+
yield this.browser.scrollDown();
|
|
2646
|
+
break;
|
|
2647
|
+
case preset_PresetCommandType.SCROLL_UP:
|
|
2648
|
+
yield this.browser.scrollUp();
|
|
2649
|
+
break;
|
|
2650
|
+
case preset_PresetCommandType.WAIT:
|
|
2651
|
+
yield this.browser.wait(command.delay * 1000);
|
|
2652
|
+
break;
|
|
2653
|
+
case preset_PresetCommandType.REFRESH:
|
|
2654
|
+
yield this.browser.refresh();
|
|
2655
|
+
break;
|
|
2656
|
+
case preset_PresetCommandType.CLICK: {
|
|
2657
|
+
let id;
|
|
2658
|
+
if (command.target.a11yData) {
|
|
2659
|
+
id = (_a = command.target.a11yData) === null || _a === void 0 ? void 0 : _a.id;
|
|
2660
|
+
}
|
|
2661
|
+
else {
|
|
2662
|
+
const locator = yield this.locateElement(command.target.elementDescriptor, disableCache);
|
|
2663
|
+
id = locator.id;
|
|
2664
|
+
}
|
|
2665
|
+
const elementInteracted = yield this.browser.click({
|
|
2666
|
+
id,
|
|
2667
|
+
}, {
|
|
2668
|
+
doubleClick: command.doubleClick,
|
|
2669
|
+
rightClick: command.rightClick,
|
|
2670
|
+
});
|
|
2671
|
+
const result = {
|
|
2672
|
+
type: ExecuteResultType.COMMAND,
|
|
2673
|
+
urlAfterCommand: this.browser.url,
|
|
2674
|
+
succeedImmediately: false,
|
|
2675
|
+
elementInteracted,
|
|
2604
2676
|
};
|
|
2677
|
+
if (urlChanged(urlBeforeCommand, result.urlAfterCommand)) {
|
|
2678
|
+
result.succeedImmediately = true;
|
|
2679
|
+
result.succeedImmediatelyReason = "URL changed";
|
|
2680
|
+
}
|
|
2681
|
+
return result;
|
|
2605
2682
|
}
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
break;
|
|
2625
|
-
case preset_PresetCommandType.SCROLL_UP:
|
|
2626
|
-
await this.browser.scrollUp();
|
|
2627
|
-
break;
|
|
2628
|
-
case preset_PresetCommandType.WAIT:
|
|
2629
|
-
await this.browser.wait(command.delay * 1000);
|
|
2630
|
-
break;
|
|
2631
|
-
case preset_PresetCommandType.REFRESH:
|
|
2632
|
-
await this.browser.refresh();
|
|
2633
|
-
break;
|
|
2634
|
-
case preset_PresetCommandType.CLICK: {
|
|
2635
|
-
let id;
|
|
2636
|
-
if (command.target.a11yData) {
|
|
2637
|
-
id = command.target.a11yData?.id;
|
|
2638
|
-
}
|
|
2639
|
-
else {
|
|
2640
|
-
const locator = await this.locateElement(command.target.elementDescriptor, disableCache);
|
|
2641
|
-
id = locator.id;
|
|
2642
|
-
}
|
|
2643
|
-
const elementInteracted = await this.browser.click({
|
|
2644
|
-
id,
|
|
2645
|
-
}, {
|
|
2646
|
-
doubleClick: command.doubleClick,
|
|
2647
|
-
rightClick: command.rightClick,
|
|
2648
|
-
});
|
|
2649
|
-
const result = {
|
|
2650
|
-
type: ExecuteResultType.COMMAND,
|
|
2651
|
-
urlAfterCommand: this.browser.url,
|
|
2652
|
-
succeedImmediately: false,
|
|
2653
|
-
elementInteracted,
|
|
2654
|
-
};
|
|
2655
|
-
if (urlChanged(urlBeforeCommand, result.urlAfterCommand)) {
|
|
2656
|
-
result.succeedImmediately = true;
|
|
2657
|
-
result.succeedImmediatelyReason = "URL changed";
|
|
2658
|
-
}
|
|
2659
|
-
return result;
|
|
2660
|
-
}
|
|
2661
|
-
case preset_PresetCommandType.SELECT_OPTION: {
|
|
2662
|
-
let id;
|
|
2663
|
-
if (command.target.a11yData) {
|
|
2664
|
-
id = command.target.a11yData?.id;
|
|
2665
|
-
}
|
|
2666
|
-
else {
|
|
2667
|
-
const locator = await this.locateElement(command.target.elementDescriptor, disableCache);
|
|
2668
|
-
id = locator.id;
|
|
2669
|
-
}
|
|
2670
|
-
const elementInteracted = await this.browser.selectOption({
|
|
2671
|
-
id,
|
|
2672
|
-
}, command.option);
|
|
2673
|
-
return {
|
|
2674
|
-
type: ExecuteResultType.COMMAND,
|
|
2675
|
-
succeedImmediately: false,
|
|
2676
|
-
urlAfterCommand: this.browser.url,
|
|
2677
|
-
elementInteracted,
|
|
2678
|
-
};
|
|
2679
|
-
}
|
|
2680
|
-
case preset_PresetCommandType.TYPE: {
|
|
2681
|
-
let elementInteracted;
|
|
2682
|
-
const target = command.target;
|
|
2683
|
-
if (target.a11yData) {
|
|
2684
|
-
elementInteracted = await this.browser.click({
|
|
2685
|
-
id: target.a11yData.id,
|
|
2686
|
-
});
|
|
2683
|
+
case preset_PresetCommandType.SELECT_OPTION: {
|
|
2684
|
+
let id;
|
|
2685
|
+
if (command.target.a11yData) {
|
|
2686
|
+
id = (_b = command.target.a11yData) === null || _b === void 0 ? void 0 : _b.id;
|
|
2687
|
+
}
|
|
2688
|
+
else {
|
|
2689
|
+
const locator = yield this.locateElement(command.target.elementDescriptor, disableCache);
|
|
2690
|
+
id = locator.id;
|
|
2691
|
+
}
|
|
2692
|
+
const elementInteracted = yield this.browser.selectOption({
|
|
2693
|
+
id,
|
|
2694
|
+
}, command.option);
|
|
2695
|
+
return {
|
|
2696
|
+
type: ExecuteResultType.COMMAND,
|
|
2697
|
+
succeedImmediately: false,
|
|
2698
|
+
urlAfterCommand: this.browser.url,
|
|
2699
|
+
elementInteracted,
|
|
2700
|
+
};
|
|
2687
2701
|
}
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2702
|
+
case preset_PresetCommandType.TYPE: {
|
|
2703
|
+
let elementInteracted;
|
|
2704
|
+
const target = command.target;
|
|
2705
|
+
if (target.a11yData) {
|
|
2706
|
+
elementInteracted = yield this.browser.click({
|
|
2707
|
+
id: target.a11yData.id,
|
|
2708
|
+
});
|
|
2709
|
+
}
|
|
2710
|
+
else if (target.elementDescriptor.length > 0) {
|
|
2711
|
+
const locator = yield this.locateElement(command.target.elementDescriptor, disableCache);
|
|
2712
|
+
elementInteracted = yield this.browser.click({
|
|
2713
|
+
id: locator.id,
|
|
2714
|
+
});
|
|
2715
|
+
}
|
|
2716
|
+
yield this.browser.type(command.value, {
|
|
2717
|
+
clearContent: command.clearContent,
|
|
2718
|
+
pressKeysSequentially: command.pressKeysSequentially,
|
|
2692
2719
|
});
|
|
2720
|
+
if (command.pressEnter) {
|
|
2721
|
+
yield this.browser.press("Enter");
|
|
2722
|
+
}
|
|
2723
|
+
const result = {
|
|
2724
|
+
type: ExecuteResultType.COMMAND,
|
|
2725
|
+
urlAfterCommand: this.browser.url,
|
|
2726
|
+
succeedImmediately: false,
|
|
2727
|
+
elementInteracted,
|
|
2728
|
+
};
|
|
2729
|
+
if (urlChanged(urlBeforeCommand, result.urlAfterCommand)) {
|
|
2730
|
+
result.succeedImmediately = true;
|
|
2731
|
+
result.succeedImmediatelyReason = "URL changed";
|
|
2732
|
+
}
|
|
2733
|
+
return result;
|
|
2693
2734
|
}
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
return result;
|
|
2735
|
+
case preset_PresetCommandType.PRESS:
|
|
2736
|
+
yield this.browser.press(command.value);
|
|
2737
|
+
const result = {
|
|
2738
|
+
type: ExecuteResultType.COMMAND,
|
|
2739
|
+
urlAfterCommand: this.browser.url,
|
|
2740
|
+
succeedImmediately: false,
|
|
2741
|
+
};
|
|
2742
|
+
if (urlChanged(urlBeforeCommand, result.urlAfterCommand)) {
|
|
2743
|
+
result.succeedImmediately = true;
|
|
2744
|
+
result.succeedImmediatelyReason = "URL changed";
|
|
2745
|
+
}
|
|
2746
|
+
return result;
|
|
2747
|
+
default:
|
|
2748
|
+
const assertUnreachable = (_x) => {
|
|
2749
|
+
throw "If Typescript complains about the line below, you missed a case or break in the switch above";
|
|
2750
|
+
};
|
|
2751
|
+
return assertUnreachable(command);
|
|
2712
2752
|
}
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
type: ExecuteResultType.COMMAND,
|
|
2746
|
-
succeedImmediately: false,
|
|
2747
|
-
urlAfterCommand: this.browser.url,
|
|
2748
|
-
};
|
|
2749
|
-
/**
|
|
2750
|
-
* Preset action
|
|
2751
|
-
*/
|
|
2752
|
-
default:
|
|
2753
|
-
const result = await this.executePresetStep(command, disableCache);
|
|
2754
|
-
if (result.type !== "command") {
|
|
2755
|
-
// AI should never generate 'assertion' results
|
|
2756
|
-
throw new Error(`Unexpected preset result type ${result.type} from executing AI action`);
|
|
2757
|
-
}
|
|
2758
|
-
return result;
|
|
2759
|
-
}
|
|
2753
|
+
return {
|
|
2754
|
+
type: ExecuteResultType.COMMAND,
|
|
2755
|
+
succeedImmediately: false,
|
|
2756
|
+
urlAfterCommand: this.browser.url,
|
|
2757
|
+
};
|
|
2758
|
+
});
|
|
2759
|
+
}
|
|
2760
|
+
sendCommandToBrowser(command, disableCache) {
|
|
2761
|
+
return controller_awaiter(this, void 0, void 0, function* () {
|
|
2762
|
+
switch (command.type) {
|
|
2763
|
+
/**
|
|
2764
|
+
* Control flow
|
|
2765
|
+
*/
|
|
2766
|
+
case ControlFlowCommandType.SUCCESS:
|
|
2767
|
+
case ControlFlowCommandType.FAILURE:
|
|
2768
|
+
return {
|
|
2769
|
+
type: ExecuteResultType.COMMAND,
|
|
2770
|
+
succeedImmediately: false,
|
|
2771
|
+
urlAfterCommand: this.browser.url,
|
|
2772
|
+
};
|
|
2773
|
+
/**
|
|
2774
|
+
* Preset action
|
|
2775
|
+
*/
|
|
2776
|
+
default:
|
|
2777
|
+
const result = yield this.executePresetStep(command, disableCache);
|
|
2778
|
+
if (result.type !== "command") {
|
|
2779
|
+
// AI should never generate 'assertion' results
|
|
2780
|
+
throw new Error(`Unexpected preset result type ${result.type} from executing AI action`);
|
|
2781
|
+
}
|
|
2782
|
+
return result;
|
|
2783
|
+
}
|
|
2784
|
+
});
|
|
2760
2785
|
}
|
|
2761
2786
|
}
|
|
2762
2787
|
|
|
@@ -2764,6 +2789,15 @@ class AgentController {
|
|
|
2764
2789
|
var fetch_retry = __nccwpck_require__(62);
|
|
2765
2790
|
var fetch_retry_default = /*#__PURE__*/__nccwpck_require__.n(fetch_retry);
|
|
2766
2791
|
;// CONCATENATED MODULE: ../../packages/web-agent/src/generators/api-generator.ts
|
|
2792
|
+
var api_generator_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2793
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
2794
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
2795
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
2796
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
2797
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
2798
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
2799
|
+
});
|
|
2800
|
+
};
|
|
2767
2801
|
|
|
2768
2802
|
|
|
2769
2803
|
|
|
@@ -2774,85 +2808,203 @@ class APIGenerator {
|
|
|
2774
2808
|
this.baseURL = params.baseURL;
|
|
2775
2809
|
this.apiKey = params.apiKey;
|
|
2776
2810
|
}
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2811
|
+
getElementLocation(context, disableCache) {
|
|
2812
|
+
return api_generator_awaiter(this, void 0, void 0, function* () {
|
|
2813
|
+
const result = yield this.sendRequest(`/${API_VERSION}/web-agent/locate-element`, {
|
|
2814
|
+
browserState: context.browserState,
|
|
2815
|
+
goal: context.goal,
|
|
2816
|
+
disableCache,
|
|
2817
|
+
});
|
|
2818
|
+
return locator_AILocatorSchema.parse(result);
|
|
2782
2819
|
});
|
|
2783
|
-
return locator_AILocatorSchema.parse(result);
|
|
2784
2820
|
}
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2821
|
+
getAssertionResult(context, useVision, disableCache) {
|
|
2822
|
+
var _a;
|
|
2823
|
+
return api_generator_awaiter(this, void 0, void 0, function* () {
|
|
2824
|
+
if (useVision) {
|
|
2825
|
+
const result = yield this.sendRequest(`/${API_VERSION}/web-agent/assertion`, {
|
|
2826
|
+
url: context.url,
|
|
2827
|
+
goal: context.goal,
|
|
2828
|
+
screenshot: (_a = context.screenshot) === null || _a === void 0 ? void 0 : _a.toString("base64"),
|
|
2829
|
+
disableCache,
|
|
2830
|
+
vision: true,
|
|
2831
|
+
});
|
|
2832
|
+
return execute_results_ExecuteAssertionResultSchema.parse(result);
|
|
2833
|
+
}
|
|
2834
|
+
const result = yield this.sendRequest(`/${API_VERSION}/web-agent/assertion`, {
|
|
2788
2835
|
url: context.url,
|
|
2836
|
+
browserState: context.browserState,
|
|
2789
2837
|
goal: context.goal,
|
|
2790
|
-
|
|
2838
|
+
history: context.history,
|
|
2839
|
+
numPrevious: context.numPrevious,
|
|
2840
|
+
lastCommand: context.lastCommand,
|
|
2791
2841
|
disableCache,
|
|
2792
|
-
vision:
|
|
2842
|
+
vision: false,
|
|
2793
2843
|
});
|
|
2794
2844
|
return execute_results_ExecuteAssertionResultSchema.parse(result);
|
|
2795
|
-
}
|
|
2796
|
-
const result = await this.sendRequest(`/${API_VERSION}/web-agent/assertion`, {
|
|
2797
|
-
url: context.url,
|
|
2798
|
-
browserState: context.browserState,
|
|
2799
|
-
goal: context.goal,
|
|
2800
|
-
history: context.history,
|
|
2801
|
-
numPrevious: context.numPrevious,
|
|
2802
|
-
lastCommand: context.lastCommand,
|
|
2803
|
-
disableCache,
|
|
2804
|
-
vision: false,
|
|
2805
2845
|
});
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2846
|
+
}
|
|
2847
|
+
getProposedCommand(context, disableCache) {
|
|
2848
|
+
return api_generator_awaiter(this, void 0, void 0, function* () {
|
|
2849
|
+
const result = yield this.sendRequest(`/${API_VERSION}/web-agent/next-command`, {
|
|
2850
|
+
url: context.url,
|
|
2851
|
+
browserState: context.browserState,
|
|
2852
|
+
goal: context.goal,
|
|
2853
|
+
history: context.history,
|
|
2854
|
+
numPrevious: context.numPrevious,
|
|
2855
|
+
lastCommand: context.lastCommand,
|
|
2856
|
+
disableCache,
|
|
2857
|
+
});
|
|
2858
|
+
return ai_commands_AICommandSchema.parse(result);
|
|
2817
2859
|
});
|
|
2818
|
-
return ai_commands_AICommandSchema.parse(result);
|
|
2819
2860
|
}
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2861
|
+
getGranularGoals(context, disableCache) {
|
|
2862
|
+
return api_generator_awaiter(this, void 0, void 0, function* () {
|
|
2863
|
+
const result = yield this.sendRequest(`/${API_VERSION}/web-agent/split-goal`, {
|
|
2864
|
+
url: context.url,
|
|
2865
|
+
goal: context.goal,
|
|
2866
|
+
disableCache,
|
|
2867
|
+
});
|
|
2868
|
+
return external_zod_namespaceObject.string().array().parse(result);
|
|
2825
2869
|
});
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2870
|
+
}
|
|
2871
|
+
sendRequest(path, body) {
|
|
2872
|
+
return api_generator_awaiter(this, void 0, void 0, function* () {
|
|
2873
|
+
const response = yield fetch(`${this.baseURL}${path}`, {
|
|
2874
|
+
retries: 3,
|
|
2875
|
+
retryDelay: 1000,
|
|
2876
|
+
method: "POST",
|
|
2877
|
+
body: JSON.stringify(body),
|
|
2878
|
+
headers: {
|
|
2879
|
+
"Content-Type": "application/json",
|
|
2880
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
2881
|
+
},
|
|
2882
|
+
});
|
|
2883
|
+
if (!response.ok) {
|
|
2884
|
+
throw new Error(`Request to ${path} failed with status ${response.status}: ${yield response.text()}`);
|
|
2885
|
+
}
|
|
2886
|
+
return response.json();
|
|
2838
2887
|
});
|
|
2839
|
-
if (!response.ok) {
|
|
2840
|
-
throw new Error(`Request to ${path} failed with status ${response.status}: ${await response.text()}`);
|
|
2841
|
-
}
|
|
2842
|
-
return response.json();
|
|
2843
2888
|
}
|
|
2844
2889
|
}
|
|
2845
2890
|
|
|
2846
2891
|
;// CONCATENATED MODULE: ./src/index.ts
|
|
2892
|
+
/* module decorator */ module = __nccwpck_require__.hmd(module);
|
|
2847
2893
|
// NOTE: these paths are using the direct paths to these files to support treeshaking so we don't bundle unnecessary code
|
|
2848
2894
|
|
|
2849
2895
|
|
|
2850
2896
|
|
|
2897
|
+
module.exports = {
|
|
2898
|
+
APIGenerator: APIGenerator,
|
|
2899
|
+
AgentController: AgentController,
|
|
2900
|
+
ChromeBrowser: ChromeBrowser,
|
|
2901
|
+
};
|
|
2851
2902
|
|
|
2852
2903
|
|
|
2853
|
-
})
|
|
2904
|
+
/***/ })
|
|
2854
2905
|
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2906
|
+
/******/ });
|
|
2907
|
+
/************************************************************************/
|
|
2908
|
+
/******/ // The module cache
|
|
2909
|
+
/******/ var __webpack_module_cache__ = {};
|
|
2910
|
+
/******/
|
|
2911
|
+
/******/ // The require function
|
|
2912
|
+
/******/ function __nccwpck_require__(moduleId) {
|
|
2913
|
+
/******/ // Check if module is in cache
|
|
2914
|
+
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
|
2915
|
+
/******/ if (cachedModule !== undefined) {
|
|
2916
|
+
/******/ return cachedModule.exports;
|
|
2917
|
+
/******/ }
|
|
2918
|
+
/******/ // Create a new module (and put it into the cache)
|
|
2919
|
+
/******/ var module = __webpack_module_cache__[moduleId] = {
|
|
2920
|
+
/******/ id: moduleId,
|
|
2921
|
+
/******/ loaded: false,
|
|
2922
|
+
/******/ exports: {}
|
|
2923
|
+
/******/ };
|
|
2924
|
+
/******/
|
|
2925
|
+
/******/ // Execute the module function
|
|
2926
|
+
/******/ var threw = true;
|
|
2927
|
+
/******/ try {
|
|
2928
|
+
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __nccwpck_require__);
|
|
2929
|
+
/******/ threw = false;
|
|
2930
|
+
/******/ } finally {
|
|
2931
|
+
/******/ if(threw) delete __webpack_module_cache__[moduleId];
|
|
2932
|
+
/******/ }
|
|
2933
|
+
/******/
|
|
2934
|
+
/******/ // Flag the module as loaded
|
|
2935
|
+
/******/ module.loaded = true;
|
|
2936
|
+
/******/
|
|
2937
|
+
/******/ // Return the exports of the module
|
|
2938
|
+
/******/ return module.exports;
|
|
2939
|
+
/******/ }
|
|
2940
|
+
/******/
|
|
2941
|
+
/************************************************************************/
|
|
2942
|
+
/******/ /* webpack/runtime/compat get default export */
|
|
2943
|
+
/******/ (() => {
|
|
2944
|
+
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
2945
|
+
/******/ __nccwpck_require__.n = (module) => {
|
|
2946
|
+
/******/ var getter = module && module.__esModule ?
|
|
2947
|
+
/******/ () => (module['default']) :
|
|
2948
|
+
/******/ () => (module);
|
|
2949
|
+
/******/ __nccwpck_require__.d(getter, { a: getter });
|
|
2950
|
+
/******/ return getter;
|
|
2951
|
+
/******/ };
|
|
2952
|
+
/******/ })();
|
|
2953
|
+
/******/
|
|
2954
|
+
/******/ /* webpack/runtime/define property getters */
|
|
2955
|
+
/******/ (() => {
|
|
2956
|
+
/******/ // define getter functions for harmony exports
|
|
2957
|
+
/******/ __nccwpck_require__.d = (exports, definition) => {
|
|
2958
|
+
/******/ for(var key in definition) {
|
|
2959
|
+
/******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) {
|
|
2960
|
+
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
2961
|
+
/******/ }
|
|
2962
|
+
/******/ }
|
|
2963
|
+
/******/ };
|
|
2964
|
+
/******/ })();
|
|
2965
|
+
/******/
|
|
2966
|
+
/******/ /* webpack/runtime/harmony module decorator */
|
|
2967
|
+
/******/ (() => {
|
|
2968
|
+
/******/ __nccwpck_require__.hmd = (module) => {
|
|
2969
|
+
/******/ module = Object.create(module);
|
|
2970
|
+
/******/ if (!module.children) module.children = [];
|
|
2971
|
+
/******/ Object.defineProperty(module, 'exports', {
|
|
2972
|
+
/******/ enumerable: true,
|
|
2973
|
+
/******/ set: () => {
|
|
2974
|
+
/******/ throw new Error('ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: ' + module.id);
|
|
2975
|
+
/******/ }
|
|
2976
|
+
/******/ });
|
|
2977
|
+
/******/ return module;
|
|
2978
|
+
/******/ };
|
|
2979
|
+
/******/ })();
|
|
2980
|
+
/******/
|
|
2981
|
+
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
|
2982
|
+
/******/ (() => {
|
|
2983
|
+
/******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
2984
|
+
/******/ })();
|
|
2985
|
+
/******/
|
|
2986
|
+
/******/ /* webpack/runtime/make namespace object */
|
|
2987
|
+
/******/ (() => {
|
|
2988
|
+
/******/ // define __esModule on exports
|
|
2989
|
+
/******/ __nccwpck_require__.r = (exports) => {
|
|
2990
|
+
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
2991
|
+
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2992
|
+
/******/ }
|
|
2993
|
+
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
|
2994
|
+
/******/ };
|
|
2995
|
+
/******/ })();
|
|
2996
|
+
/******/
|
|
2997
|
+
/******/ /* webpack/runtime/compat */
|
|
2998
|
+
/******/
|
|
2999
|
+
/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/";
|
|
3000
|
+
/******/
|
|
3001
|
+
/************************************************************************/
|
|
3002
|
+
/******/
|
|
3003
|
+
/******/ // startup
|
|
3004
|
+
/******/ // Load entry module and return exports
|
|
3005
|
+
/******/ // This entry module is referenced by other modules so it can't be inlined
|
|
3006
|
+
/******/ var __webpack_exports__ = __nccwpck_require__(811);
|
|
3007
|
+
/******/ module.exports = __webpack_exports__;
|
|
3008
|
+
/******/
|
|
3009
|
+
/******/ })()
|
|
3010
|
+
;
|