momentic 0.0.10 → 0.0.12
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/bin/cli.js +1325 -1170
- package/dist/index.js +1085 -982
- package/package.json +3 -1
package/bin/cli.js
CHANGED
|
@@ -1,10 +1,62 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
4
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
5
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
+
var __spreadValues = (a, b) => {
|
|
10
|
+
for (var prop in b || (b = {}))
|
|
11
|
+
if (__hasOwnProp.call(b, prop))
|
|
12
|
+
__defNormalProp(a, prop, b[prop]);
|
|
13
|
+
if (__getOwnPropSymbols)
|
|
14
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
15
|
+
if (__propIsEnum.call(b, prop))
|
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
|
17
|
+
}
|
|
18
|
+
return a;
|
|
19
|
+
};
|
|
20
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
21
|
+
var __objRest = (source, exclude) => {
|
|
22
|
+
var target = {};
|
|
23
|
+
for (var prop in source)
|
|
24
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
25
|
+
target[prop] = source[prop];
|
|
26
|
+
if (source != null && __getOwnPropSymbols)
|
|
27
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
28
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
29
|
+
target[prop] = source[prop];
|
|
30
|
+
}
|
|
31
|
+
return target;
|
|
32
|
+
};
|
|
33
|
+
var __async = (__this, __arguments, generator) => {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
var fulfilled = (value) => {
|
|
36
|
+
try {
|
|
37
|
+
step(generator.next(value));
|
|
38
|
+
} catch (e) {
|
|
39
|
+
reject(e);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var rejected = (value) => {
|
|
43
|
+
try {
|
|
44
|
+
step(generator.throw(value));
|
|
45
|
+
} catch (e) {
|
|
46
|
+
reject(e);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
50
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
51
|
+
});
|
|
52
|
+
};
|
|
2
53
|
|
|
3
54
|
// src/cli.ts
|
|
4
55
|
import exec from "@actions/exec";
|
|
5
56
|
import io from "@actions/io";
|
|
6
57
|
import chalk from "chalk";
|
|
7
58
|
import { Command as Command4, Option } from "commander";
|
|
59
|
+
import { registry } from "playwright-core/lib/server";
|
|
8
60
|
import quote from "quote";
|
|
9
61
|
import parseArgsStringToArgv2 from "string-argv";
|
|
10
62
|
import waitOnFn from "wait-on";
|
|
@@ -383,11 +435,10 @@ function parseCookieString(cookie) {
|
|
|
383
435
|
if (!parsedCookie.path && parsedCookie.domain) {
|
|
384
436
|
parsedCookie.path = "/";
|
|
385
437
|
}
|
|
386
|
-
const result = {
|
|
387
|
-
...parsedCookie,
|
|
438
|
+
const result = __spreadProps(__spreadValues({}, parsedCookie), {
|
|
388
439
|
expires: parsedCookie.expires ? parsedCookie.expires.getTime() / 1e3 : void 0,
|
|
389
440
|
sameSite
|
|
390
|
-
};
|
|
441
|
+
});
|
|
391
442
|
return result;
|
|
392
443
|
}
|
|
393
444
|
|
|
@@ -724,18 +775,6 @@ var defaultA11yNodeSerializeParams = {
|
|
|
724
775
|
noProperties: false
|
|
725
776
|
};
|
|
726
777
|
var ProcessedA11yNode = class {
|
|
727
|
-
id;
|
|
728
|
-
role;
|
|
729
|
-
name;
|
|
730
|
-
content;
|
|
731
|
-
properties;
|
|
732
|
-
// css-like selector from the root of the tree to the current node
|
|
733
|
-
pathFromRoot;
|
|
734
|
-
parent;
|
|
735
|
-
// md5 hash - set lazily in most cases (not used at the moment)
|
|
736
|
-
// md5Sum: string;
|
|
737
|
-
children;
|
|
738
|
-
backendNodeID;
|
|
739
778
|
constructor(params) {
|
|
740
779
|
this.id = params.id;
|
|
741
780
|
this.role = params.role;
|
|
@@ -747,10 +786,11 @@ var ProcessedA11yNode = class {
|
|
|
747
786
|
this.backendNodeID = params.backendNodeID;
|
|
748
787
|
}
|
|
749
788
|
getLogForm() {
|
|
789
|
+
var _a, _b;
|
|
750
790
|
return JSON.stringify({
|
|
751
791
|
id: this.id,
|
|
752
|
-
name: this.name
|
|
753
|
-
role: this.role
|
|
792
|
+
name: (_a = this.name) != null ? _a : "",
|
|
793
|
+
role: (_b = this.role) != null ? _b : "",
|
|
754
794
|
backendNodeId: this.backendNodeID
|
|
755
795
|
});
|
|
756
796
|
}
|
|
@@ -843,7 +883,7 @@ function getNodePathIdentifier(node) {
|
|
|
843
883
|
return `"${node.nodeId}"`;
|
|
844
884
|
}
|
|
845
885
|
function processA11yTreeDFS(node, parent, inputNodeMap, outputNodeMap) {
|
|
846
|
-
var _a, _b, _c, _d, _e, _f;
|
|
886
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
847
887
|
if (!parent && node.parentId) {
|
|
848
888
|
throw new Error(
|
|
849
889
|
`Got no parent for accessibility node ${node.nodeId}: ${JSON.stringify(
|
|
@@ -871,7 +911,7 @@ function processA11yTreeDFS(node, parent, inputNodeMap, outputNodeMap) {
|
|
|
871
911
|
});
|
|
872
912
|
}
|
|
873
913
|
outputNodeMap.set(processedNode.id, processedNode);
|
|
874
|
-
const children = node.childIds
|
|
914
|
+
const children = (_f = node.childIds) != null ? _f : [];
|
|
875
915
|
for (const childId of children) {
|
|
876
916
|
if (!childId) {
|
|
877
917
|
continue;
|
|
@@ -896,7 +936,7 @@ function processA11yTreeDFS(node, parent, inputNodeMap, outputNodeMap) {
|
|
|
896
936
|
}
|
|
897
937
|
if (processedNode.children.length === 1 && processedNode.children[0].role === "StaticText") {
|
|
898
938
|
const currentName = processedNode.name;
|
|
899
|
-
const childName = (
|
|
939
|
+
const childName = (_g = processedNode.children[0]) == null ? void 0 : _g.name;
|
|
900
940
|
if (currentName === childName || !childName) {
|
|
901
941
|
processedNode.children = [];
|
|
902
942
|
}
|
|
@@ -1099,20 +1139,14 @@ function isRequestRelevantForPageLoad(request, currentURL) {
|
|
|
1099
1139
|
}
|
|
1100
1140
|
|
|
1101
1141
|
// ../../packages/web-agent/src/browsers/chrome.ts
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1142
|
+
function initCDPSession(cdpClient) {
|
|
1143
|
+
return __async(this, null, function* () {
|
|
1144
|
+
yield cdpClient.send("Accessibility.enable");
|
|
1145
|
+
yield cdpClient.send("DOM.enable");
|
|
1146
|
+
yield cdpClient.send("Overlay.enable");
|
|
1147
|
+
});
|
|
1106
1148
|
}
|
|
1107
|
-
var
|
|
1108
|
-
browser;
|
|
1109
|
-
context;
|
|
1110
|
-
page;
|
|
1111
|
-
// key is nodeId, according to the a11y tree
|
|
1112
|
-
nodeMap = /* @__PURE__ */ new Map();
|
|
1113
|
-
cdpClient;
|
|
1114
|
-
logger;
|
|
1115
|
-
baseURL;
|
|
1149
|
+
var _ChromeBrowser = class _ChromeBrowser {
|
|
1116
1150
|
constructor({
|
|
1117
1151
|
browser,
|
|
1118
1152
|
context,
|
|
@@ -1121,6 +1155,8 @@ var ChromeBrowser = class _ChromeBrowser {
|
|
|
1121
1155
|
cdpClient,
|
|
1122
1156
|
logger
|
|
1123
1157
|
}) {
|
|
1158
|
+
// key is nodeId, according to the a11y tree
|
|
1159
|
+
this.nodeMap = /* @__PURE__ */ new Map();
|
|
1124
1160
|
this.browser = browser;
|
|
1125
1161
|
this.context = context;
|
|
1126
1162
|
this.page = page;
|
|
@@ -1128,106 +1164,117 @@ var ChromeBrowser = class _ChromeBrowser {
|
|
|
1128
1164
|
this.cdpClient = cdpClient;
|
|
1129
1165
|
this.logger = logger;
|
|
1130
1166
|
}
|
|
1131
|
-
static USER_AGENT = devices["Desktop Chrome"].userAgent;
|
|
1132
1167
|
/**
|
|
1133
1168
|
* Creates a new browser and waits for navigation to the given test URL.
|
|
1134
1169
|
*/
|
|
1135
|
-
static
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1170
|
+
static init(_0, _1, _2) {
|
|
1171
|
+
return __async(this, arguments, function* (baseURL, logger, onScreenshot, timeout = MAX_LOAD_TIMEOUT_MS) {
|
|
1172
|
+
const browser = yield chromium.launch({ headless: true });
|
|
1173
|
+
const context = yield browser.newContext({
|
|
1174
|
+
viewport: {
|
|
1175
|
+
width: 1920,
|
|
1176
|
+
height: 1080
|
|
1177
|
+
},
|
|
1178
|
+
// comment out the below if you are on Mac OS but you're using a monitor
|
|
1179
|
+
deviceScaleFactor: process.platform === "darwin" ? RETINA_WINDOW_SCALE_FACTOR : 1,
|
|
1180
|
+
userAgent: devices["Desktop Chrome"].userAgent,
|
|
1181
|
+
geolocation: { latitude: 37.7749, longitude: -122.4194 },
|
|
1182
|
+
// san francisco
|
|
1183
|
+
locale: "en-US",
|
|
1184
|
+
timezoneId: "America/Los_Angeles"
|
|
1185
|
+
});
|
|
1186
|
+
const page = yield context.newPage();
|
|
1187
|
+
const cdpClient = yield context.newCDPSession(page);
|
|
1188
|
+
const chrome = new _ChromeBrowser({
|
|
1189
|
+
browser,
|
|
1190
|
+
context,
|
|
1191
|
+
page,
|
|
1192
|
+
baseURL,
|
|
1193
|
+
cdpClient,
|
|
1194
|
+
logger
|
|
1195
|
+
});
|
|
1196
|
+
let completed = false;
|
|
1197
|
+
const navigateAndInitCDP = () => __async(this, null, function* () {
|
|
1198
|
+
try {
|
|
1199
|
+
yield chrome.navigate(baseURL, false);
|
|
1200
|
+
yield initCDPSession(cdpClient);
|
|
1201
|
+
} catch (err) {
|
|
1202
|
+
logger.error({ err }, "Failed to initialize chrome browser");
|
|
1203
|
+
} finally {
|
|
1204
|
+
completed = true;
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
void navigateAndInitCDP();
|
|
1208
|
+
const sendScreenshot = () => __async(this, null, function* () {
|
|
1209
|
+
if (!onScreenshot) {
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
try {
|
|
1213
|
+
onScreenshot({
|
|
1214
|
+
viewport: chrome.viewport,
|
|
1215
|
+
buffer: yield chrome.screenshot()
|
|
1216
|
+
});
|
|
1217
|
+
} catch (err) {
|
|
1218
|
+
logger.error({ err }, "Failed to take screenshot");
|
|
1219
|
+
}
|
|
1220
|
+
});
|
|
1221
|
+
void sendScreenshot();
|
|
1222
|
+
const screenshotInterval = setInterval(() => {
|
|
1223
|
+
void sendScreenshot();
|
|
1224
|
+
}, 250);
|
|
1225
|
+
const startTime = Date.now();
|
|
1226
|
+
while (!completed && Date.now() - startTime < timeout) {
|
|
1227
|
+
yield sleep(CHECK_INTERVAL_MS);
|
|
1175
1228
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
} catch (err) {
|
|
1182
|
-
logger.error({ err }, "Failed to take screenshot");
|
|
1229
|
+
clearInterval(screenshotInterval);
|
|
1230
|
+
if (!completed) {
|
|
1231
|
+
logger.warn(
|
|
1232
|
+
"Timeout elapsed waiting for browser to initialize - are you sure this page is accessible?"
|
|
1233
|
+
);
|
|
1183
1234
|
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
const screenshotInterval = setInterval(() => {
|
|
1187
|
-
void sendScreenshot();
|
|
1188
|
-
}, 250);
|
|
1189
|
-
const startTime = Date.now();
|
|
1190
|
-
while (!completed && Date.now() - startTime < timeout) {
|
|
1191
|
-
await sleep(CHECK_INTERVAL_MS);
|
|
1192
|
-
}
|
|
1193
|
-
clearInterval(screenshotInterval);
|
|
1194
|
-
if (!completed) {
|
|
1195
|
-
logger.warn(
|
|
1196
|
-
"Timeout elapsed waiting for browser to initialize - are you sure this page is accessible?"
|
|
1197
|
-
);
|
|
1198
|
-
}
|
|
1199
|
-
return chrome;
|
|
1235
|
+
return chrome;
|
|
1236
|
+
});
|
|
1200
1237
|
}
|
|
1201
1238
|
// Things to do on every page load
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1239
|
+
pageSetup() {
|
|
1240
|
+
return __async(this, null, function* () {
|
|
1241
|
+
yield this.page.evaluate(addCursorScript);
|
|
1242
|
+
yield this.page.evaluate(addIDsScript);
|
|
1243
|
+
});
|
|
1205
1244
|
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1245
|
+
wait(timeoutMs) {
|
|
1246
|
+
return __async(this, null, function* () {
|
|
1247
|
+
yield this.page.waitForTimeout(timeoutMs);
|
|
1248
|
+
});
|
|
1208
1249
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1250
|
+
cleanup() {
|
|
1251
|
+
return __async(this, null, function* () {
|
|
1252
|
+
yield this.page.close();
|
|
1253
|
+
yield this.context.close();
|
|
1254
|
+
yield this.browser.close();
|
|
1255
|
+
});
|
|
1213
1256
|
}
|
|
1214
1257
|
get closed() {
|
|
1215
1258
|
return this.page.isClosed() || !this.browser.isConnected();
|
|
1216
1259
|
}
|
|
1217
|
-
|
|
1218
|
-
return
|
|
1260
|
+
html() {
|
|
1261
|
+
return __async(this, null, function* () {
|
|
1262
|
+
return yield this.page.content();
|
|
1263
|
+
});
|
|
1219
1264
|
}
|
|
1220
1265
|
get url() {
|
|
1221
1266
|
return this.page.url();
|
|
1222
1267
|
}
|
|
1223
|
-
|
|
1224
|
-
return
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1268
|
+
screenshot(quality = 100, scale = "device") {
|
|
1269
|
+
return __async(this, null, function* () {
|
|
1270
|
+
return yield this.page.screenshot({
|
|
1271
|
+
fullPage: false,
|
|
1272
|
+
quality,
|
|
1273
|
+
scale,
|
|
1274
|
+
type: "jpeg",
|
|
1275
|
+
// allow the blinking text cursor thing to remain there
|
|
1276
|
+
caret: "initial"
|
|
1277
|
+
});
|
|
1231
1278
|
});
|
|
1232
1279
|
}
|
|
1233
1280
|
get viewport() {
|
|
@@ -1237,539 +1284,595 @@ var ChromeBrowser = class _ChromeBrowser {
|
|
|
1237
1284
|
}
|
|
1238
1285
|
return viewport;
|
|
1239
1286
|
}
|
|
1240
|
-
|
|
1241
|
-
this
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1287
|
+
navigate(url, wrapPossibleNavigation = true) {
|
|
1288
|
+
return __async(this, null, function* () {
|
|
1289
|
+
this.logger.debug(`Navigating to ${url}`);
|
|
1290
|
+
const startTime = Date.now();
|
|
1291
|
+
const doNav = () => __async(this, null, function* () {
|
|
1292
|
+
try {
|
|
1293
|
+
yield this.page.goto(url, {
|
|
1294
|
+
timeout: MAX_LOAD_TIMEOUT_MS
|
|
1295
|
+
});
|
|
1296
|
+
this.logger.debug(
|
|
1297
|
+
{ url },
|
|
1298
|
+
`Got load event in ${Math.floor(Date.now() - startTime)}ms`
|
|
1299
|
+
);
|
|
1300
|
+
} catch (e) {
|
|
1301
|
+
this.logger.warn(
|
|
1302
|
+
{ url, type: "navigate", err: e },
|
|
1303
|
+
"Timeout elapsed waiting for page to load, continuing anyways..."
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
if (wrapPossibleNavigation) {
|
|
1308
|
+
yield this.wrapPossibleNavigation(doNav);
|
|
1309
|
+
} else {
|
|
1310
|
+
yield doNav();
|
|
1311
|
+
}
|
|
1312
|
+
if (CHROME_INTERNAL_URLS.has(this.url) && process.env.NODE_ENV === "production") {
|
|
1313
|
+
throw new Error(
|
|
1314
|
+
`${url} took too long to load \u{1F61E}. Please ensure the site and your internet are working.`
|
|
1256
1315
|
);
|
|
1257
1316
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
} else {
|
|
1262
|
-
await doNav();
|
|
1263
|
-
}
|
|
1264
|
-
if (CHROME_INTERNAL_URLS.has(this.url) && process.env.NODE_ENV === "production") {
|
|
1265
|
-
throw new Error(
|
|
1266
|
-
`${url} took too long to load \u{1F61E}. Please ensure the site and your internet are working.`
|
|
1267
|
-
);
|
|
1268
|
-
}
|
|
1269
|
-
await this.pageSetup();
|
|
1270
|
-
this.logger.debug({ url }, "Navigation complete");
|
|
1271
|
-
}
|
|
1272
|
-
async fill(target, text, options = {}) {
|
|
1273
|
-
const element = await this.click(target, {
|
|
1274
|
-
doubleClick: false,
|
|
1275
|
-
rightClick: false
|
|
1276
|
-
});
|
|
1277
|
-
await this.type(text, options);
|
|
1278
|
-
return element;
|
|
1279
|
-
}
|
|
1280
|
-
async type(text, options = {}) {
|
|
1281
|
-
const { clearContent = true, pressKeysSequentially = false } = options;
|
|
1282
|
-
if (clearContent) {
|
|
1283
|
-
await this.page.keyboard.press("Meta+A");
|
|
1284
|
-
await this.page.keyboard.press("Backspace");
|
|
1285
|
-
}
|
|
1286
|
-
if (pressKeysSequentially) {
|
|
1287
|
-
await this.page.keyboard.type(text);
|
|
1288
|
-
} else {
|
|
1289
|
-
await this.page.keyboard.insertText(text);
|
|
1290
|
-
}
|
|
1317
|
+
yield this.pageSetup();
|
|
1318
|
+
this.logger.debug({ url }, "Navigation complete");
|
|
1319
|
+
});
|
|
1291
1320
|
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
}
|
|
1301
|
-
async selectOptionByA11yID(index, option) {
|
|
1302
|
-
const node = this.nodeMap.get(`${index}`);
|
|
1303
|
-
if (!node) {
|
|
1304
|
-
throw new Error(`Could not find node in DOM with index: ${index}`);
|
|
1305
|
-
}
|
|
1306
|
-
if (!node.backendNodeID) {
|
|
1307
|
-
throw new Error(
|
|
1308
|
-
`Select target missing backend node id: ${node.getLogForm()}`
|
|
1309
|
-
);
|
|
1310
|
-
}
|
|
1311
|
-
const locator = await this.getLocatorFromBackendID(node.backendNodeID);
|
|
1312
|
-
await locator.selectOption(option, {
|
|
1313
|
-
timeout: COMPLICATED_BROWSER_ACTION_TIMEOUT_MS
|
|
1321
|
+
fill(_0, _1) {
|
|
1322
|
+
return __async(this, arguments, function* (target, text, options = {}) {
|
|
1323
|
+
const element = yield this.click(target, {
|
|
1324
|
+
doubleClick: false,
|
|
1325
|
+
rightClick: false
|
|
1326
|
+
});
|
|
1327
|
+
yield this.type(text, options);
|
|
1328
|
+
return element;
|
|
1314
1329
|
});
|
|
1315
|
-
await this.highlightNode(node);
|
|
1316
|
-
return node.serialize({ noChildren: true, noProperties: true, noID: true });
|
|
1317
1330
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1331
|
+
type(_0) {
|
|
1332
|
+
return __async(this, arguments, function* (text, options = {}) {
|
|
1333
|
+
const { clearContent = true, pressKeysSequentially = false } = options;
|
|
1334
|
+
if (clearContent) {
|
|
1335
|
+
yield this.page.keyboard.press("Meta+A");
|
|
1336
|
+
yield this.page.keyboard.press("Backspace");
|
|
1337
|
+
}
|
|
1338
|
+
if (pressKeysSequentially) {
|
|
1339
|
+
yield this.page.keyboard.type(text);
|
|
1340
|
+
} else {
|
|
1341
|
+
yield this.page.keyboard.insertText(text);
|
|
1342
|
+
}
|
|
1343
|
+
});
|
|
1324
1344
|
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
);
|
|
1334
|
-
}
|
|
1335
|
-
await this.highlightNode(node);
|
|
1345
|
+
clickByA11yID(_0) {
|
|
1346
|
+
return __async(this, arguments, function* (index, options = {}) {
|
|
1347
|
+
const node = this.nodeMap.get(`${index}`);
|
|
1348
|
+
if (!node) {
|
|
1349
|
+
throw new Error(`Could not find node in DOM with index: ${index}`);
|
|
1350
|
+
}
|
|
1351
|
+
const nodeClicked = yield this.clickUsingCDP(node, options);
|
|
1352
|
+
yield this.highlightNode(nodeClicked);
|
|
1353
|
+
return node.serialize({ noChildren: true, noProperties: true, noID: true });
|
|
1354
|
+
});
|
|
1336
1355
|
}
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1356
|
+
selectOptionByA11yID(index, option) {
|
|
1357
|
+
return __async(this, null, function* () {
|
|
1358
|
+
const node = this.nodeMap.get(`${index}`);
|
|
1359
|
+
if (!node) {
|
|
1360
|
+
throw new Error(`Could not find node in DOM with index: ${index}`);
|
|
1361
|
+
}
|
|
1362
|
+
if (!node.backendNodeID) {
|
|
1363
|
+
throw new Error(
|
|
1364
|
+
`Select target missing backend node id: ${node.getLogForm()}`
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
const locator = yield this.getLocatorFromBackendID(node.backendNodeID);
|
|
1368
|
+
yield locator.selectOption(option, {
|
|
1369
|
+
timeout: COMPLICATED_BROWSER_ACTION_TIMEOUT_MS
|
|
1342
1370
|
});
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1371
|
+
yield this.highlightNode(node);
|
|
1372
|
+
return node.serialize({ noChildren: true, noProperties: true, noID: true });
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
highlight(target) {
|
|
1376
|
+
return __async(this, null, function* () {
|
|
1377
|
+
try {
|
|
1378
|
+
yield this.highlightByA11yID(target.id);
|
|
1379
|
+
} catch (err) {
|
|
1380
|
+
this.logger.warn({ err, target }, "Failed to highlight target");
|
|
1381
|
+
}
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
highlightByA11yID(index) {
|
|
1385
|
+
return __async(this, null, function* () {
|
|
1386
|
+
const node = this.nodeMap.get(`${index}`);
|
|
1387
|
+
if (!node) {
|
|
1388
|
+
throw new Error(`Could not find node in DOM with index: ${index}`);
|
|
1389
|
+
}
|
|
1390
|
+
if (!node.backendNodeID) {
|
|
1391
|
+
throw new Error(
|
|
1392
|
+
`Select target missing backend node id: ${node.getLogForm()}`
|
|
1393
|
+
);
|
|
1394
|
+
}
|
|
1395
|
+
yield this.highlightNode(node);
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
highlightNode(node) {
|
|
1399
|
+
return __async(this, null, function* () {
|
|
1347
1400
|
try {
|
|
1348
|
-
|
|
1401
|
+
yield this.cdpClient.send("Overlay.highlightNode", {
|
|
1402
|
+
highlightConfig: NODE_HIGHLIGHT_CONFIG,
|
|
1349
1403
|
backendNodeId: node.backendNodeID
|
|
1350
1404
|
});
|
|
1351
1405
|
} catch (err) {
|
|
1352
|
-
this.logger.
|
|
1406
|
+
this.logger.warn({ err }, "Failed to add node highlight");
|
|
1353
1407
|
}
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1408
|
+
const hideHighlight = () => __async(this, null, function* () {
|
|
1409
|
+
try {
|
|
1410
|
+
yield this.cdpClient.send("Overlay.hideHighlight", {
|
|
1411
|
+
backendNodeId: node.backendNodeID
|
|
1412
|
+
});
|
|
1413
|
+
} catch (err) {
|
|
1414
|
+
this.logger.debug({ err }, "Failed to remove node highlight");
|
|
1415
|
+
}
|
|
1416
|
+
});
|
|
1417
|
+
setTimeout(() => {
|
|
1418
|
+
void hideHighlight();
|
|
1419
|
+
}, HIGHLIGHT_DURATION_MS);
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
wrapPossibleNavigation(_0) {
|
|
1423
|
+
return __async(this, arguments, function* (fn, timeoutMS = MAX_LOAD_TIMEOUT_MS) {
|
|
1424
|
+
const startTime = Date.now();
|
|
1425
|
+
const startURL = this.url;
|
|
1426
|
+
let lastRequestReceived = Date.now();
|
|
1427
|
+
const firedRequests = /* @__PURE__ */ new Map();
|
|
1428
|
+
const finishedRequests = /* @__PURE__ */ new Map();
|
|
1429
|
+
const requestFinishedListener = (request) => {
|
|
1430
|
+
var _a;
|
|
1431
|
+
const key = serializeRequest(request);
|
|
1432
|
+
finishedRequests.set(key, ((_a = finishedRequests.get(key)) != null ? _a : 0) + 1);
|
|
1433
|
+
};
|
|
1434
|
+
const requestFiredListener = (request) => {
|
|
1435
|
+
var _a;
|
|
1436
|
+
if (!isRequestRelevantForPageLoad(request, this.url)) {
|
|
1437
|
+
this.logger.debug(
|
|
1438
|
+
{
|
|
1439
|
+
uri: serializeRequest(request)
|
|
1440
|
+
},
|
|
1441
|
+
"Ignoring request for page load network stability"
|
|
1442
|
+
);
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
const key = serializeRequest(request);
|
|
1371
1446
|
this.logger.debug(
|
|
1372
1447
|
{
|
|
1373
|
-
uri:
|
|
1448
|
+
uri: key
|
|
1374
1449
|
},
|
|
1375
|
-
"
|
|
1450
|
+
"Request fired on page load, delaying network stability"
|
|
1376
1451
|
);
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
this.
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
if (e instanceof Error)
|
|
1395
|
-
return e;
|
|
1396
|
-
return new Error(`${e}`);
|
|
1397
|
-
});
|
|
1398
|
-
await sleep(CHECK_INTERVAL_MS);
|
|
1399
|
-
const unwrapAndThrowError = async (p) => {
|
|
1400
|
-
const v = await p;
|
|
1401
|
-
if (v instanceof Error) {
|
|
1402
|
-
throw v;
|
|
1403
|
-
}
|
|
1404
|
-
return v;
|
|
1405
|
-
};
|
|
1406
|
-
let unfinishedRequests = /* @__PURE__ */ new Set();
|
|
1407
|
-
const waitForNetworkIdle = async () => {
|
|
1408
|
-
while (!rejected && Date.now() - startTime < timeoutMS) {
|
|
1409
|
-
unfinishedRequests = /* @__PURE__ */ new Set();
|
|
1410
|
-
await sleep(CHECK_INTERVAL_MS);
|
|
1411
|
-
if (Date.now() - lastRequestReceived <= NETWORK_STABLE_DURATION_MS) {
|
|
1412
|
-
continue;
|
|
1452
|
+
firedRequests.set(key, ((_a = firedRequests.get(key)) != null ? _a : 0) + 1);
|
|
1453
|
+
lastRequestReceived = Date.now();
|
|
1454
|
+
};
|
|
1455
|
+
this.page.on("requestfinished", requestFinishedListener);
|
|
1456
|
+
this.page.on("request", requestFiredListener);
|
|
1457
|
+
let rejected = false;
|
|
1458
|
+
const retPromise = fn().catch((e) => {
|
|
1459
|
+
rejected = true;
|
|
1460
|
+
if (e instanceof Error)
|
|
1461
|
+
return e;
|
|
1462
|
+
return new Error(`${e}`);
|
|
1463
|
+
});
|
|
1464
|
+
yield sleep(CHECK_INTERVAL_MS);
|
|
1465
|
+
const unwrapAndThrowError = (p) => __async(this, null, function* () {
|
|
1466
|
+
const v = yield p;
|
|
1467
|
+
if (v instanceof Error) {
|
|
1468
|
+
throw v;
|
|
1413
1469
|
}
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1470
|
+
return v;
|
|
1471
|
+
});
|
|
1472
|
+
let unfinishedRequests = /* @__PURE__ */ new Set();
|
|
1473
|
+
const waitForNetworkIdle = () => __async(this, null, function* () {
|
|
1474
|
+
while (!rejected && Date.now() - startTime < timeoutMS) {
|
|
1475
|
+
unfinishedRequests = /* @__PURE__ */ new Set();
|
|
1476
|
+
yield sleep(CHECK_INTERVAL_MS);
|
|
1477
|
+
if (Date.now() - lastRequestReceived <= NETWORK_STABLE_DURATION_MS) {
|
|
1478
|
+
continue;
|
|
1479
|
+
}
|
|
1480
|
+
let anyDifference = false;
|
|
1481
|
+
for (const key of firedRequests.keys()) {
|
|
1482
|
+
if (firedRequests.get(key) !== finishedRequests.get(key)) {
|
|
1483
|
+
this.logger.debug({ uri: key }, "Waiting on request to finish");
|
|
1484
|
+
anyDifference = true;
|
|
1485
|
+
unfinishedRequests.add(key);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
if (!anyDifference) {
|
|
1489
|
+
this.logger.debug(
|
|
1490
|
+
{
|
|
1491
|
+
url: this.url,
|
|
1492
|
+
requests: JSON.stringify(Array.from(firedRequests.entries()))
|
|
1493
|
+
},
|
|
1494
|
+
`Network idle in ${Math.floor(Date.now() - startTime)}ms`
|
|
1495
|
+
);
|
|
1496
|
+
return true;
|
|
1420
1497
|
}
|
|
1421
1498
|
}
|
|
1422
|
-
if (!
|
|
1423
|
-
this.logger.
|
|
1499
|
+
if (!rejected) {
|
|
1500
|
+
this.logger.warn(
|
|
1424
1501
|
{
|
|
1425
1502
|
url: this.url,
|
|
1426
|
-
requests: JSON.stringify(Array.from(
|
|
1503
|
+
requests: JSON.stringify(Array.from(unfinishedRequests.entries()))
|
|
1427
1504
|
},
|
|
1428
|
-
|
|
1505
|
+
"Timeout elapsed waiting for network idle, continuing anyways..."
|
|
1429
1506
|
);
|
|
1430
|
-
return true;
|
|
1431
1507
|
}
|
|
1508
|
+
return false;
|
|
1509
|
+
});
|
|
1510
|
+
const waitResult = yield waitForNetworkIdle();
|
|
1511
|
+
this.page.off("requestfinished", requestFinishedListener);
|
|
1512
|
+
this.page.off("request", requestFiredListener);
|
|
1513
|
+
if (!waitResult) {
|
|
1514
|
+
return unwrapAndThrowError(retPromise);
|
|
1432
1515
|
}
|
|
1433
|
-
if (!rejected) {
|
|
1434
|
-
this.logger.
|
|
1435
|
-
|
|
1436
|
-
url: this.url,
|
|
1437
|
-
requests: JSON.stringify(Array.from(unfinishedRequests.entries()))
|
|
1438
|
-
},
|
|
1439
|
-
"Timeout elapsed waiting for network idle, continuing anyways..."
|
|
1516
|
+
if (!rejected && urlChanged(this.url, startURL)) {
|
|
1517
|
+
this.logger.debug(
|
|
1518
|
+
`Detected url change in wrapPossibleNavigation, waiting for load state`
|
|
1440
1519
|
);
|
|
1520
|
+
try {
|
|
1521
|
+
yield this.page.waitForLoadState("load", {
|
|
1522
|
+
timeout: timeoutMS - (Date.now() - startTime)
|
|
1523
|
+
});
|
|
1524
|
+
} catch (e) {
|
|
1525
|
+
this.logger.warn(
|
|
1526
|
+
{ url: this.url },
|
|
1527
|
+
"Timeout elapsed waiting for load state to fire, continuing anyways..."
|
|
1528
|
+
);
|
|
1529
|
+
}
|
|
1441
1530
|
}
|
|
1442
|
-
return false;
|
|
1443
|
-
};
|
|
1444
|
-
const waitResult = await waitForNetworkIdle();
|
|
1445
|
-
this.page.off("requestfinished", requestFinishedListener);
|
|
1446
|
-
this.page.off("request", requestFiredListener);
|
|
1447
|
-
if (!waitResult) {
|
|
1448
1531
|
return unwrapAndThrowError(retPromise);
|
|
1449
|
-
}
|
|
1450
|
-
if (!rejected && urlChanged(this.url, startURL)) {
|
|
1451
|
-
this.logger.debug(
|
|
1452
|
-
`Detected url change in wrapPossibleNavigation, waiting for load state`
|
|
1453
|
-
);
|
|
1454
|
-
try {
|
|
1455
|
-
await this.page.waitForLoadState("load", {
|
|
1456
|
-
timeout: timeoutMS - (Date.now() - startTime)
|
|
1457
|
-
});
|
|
1458
|
-
} catch (e) {
|
|
1459
|
-
this.logger.warn(
|
|
1460
|
-
{ url: this.url },
|
|
1461
|
-
"Timeout elapsed waiting for load state to fire, continuing anyways..."
|
|
1462
|
-
);
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
return unwrapAndThrowError(retPromise);
|
|
1532
|
+
});
|
|
1466
1533
|
}
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1534
|
+
click(_0) {
|
|
1535
|
+
return __async(this, arguments, function* (target, options = {}) {
|
|
1536
|
+
const elementInteracted = yield this.wrapPossibleNavigation(
|
|
1537
|
+
() => this.clickByA11yID(target.id, options)
|
|
1538
|
+
);
|
|
1539
|
+
return elementInteracted;
|
|
1540
|
+
});
|
|
1472
1541
|
}
|
|
1473
|
-
|
|
1474
|
-
return this
|
|
1542
|
+
selectOption(target, option) {
|
|
1543
|
+
return __async(this, null, function* () {
|
|
1544
|
+
return this.selectOptionByA11yID(target.id, option);
|
|
1545
|
+
});
|
|
1475
1546
|
}
|
|
1476
|
-
|
|
1477
|
-
|
|
1547
|
+
press(key) {
|
|
1548
|
+
return __async(this, null, function* () {
|
|
1549
|
+
yield this.wrapPossibleNavigation(() => this.page.keyboard.press(key));
|
|
1550
|
+
});
|
|
1478
1551
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1552
|
+
refresh() {
|
|
1553
|
+
return __async(this, null, function* () {
|
|
1554
|
+
yield this.page.reload();
|
|
1555
|
+
yield this.pageSetup();
|
|
1556
|
+
});
|
|
1482
1557
|
}
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1558
|
+
getA11yTree() {
|
|
1559
|
+
return __async(this, null, function* () {
|
|
1560
|
+
let processedTree = null;
|
|
1561
|
+
let attempt = 0;
|
|
1562
|
+
const url = this.url;
|
|
1563
|
+
while (!processedTree) {
|
|
1564
|
+
try {
|
|
1565
|
+
this.logger.debug(`Getting a11y tree at ${url}`);
|
|
1566
|
+
const graph = yield this.getRawA11yTree();
|
|
1567
|
+
if (!graph.root || graph.allNodes.length === 0) {
|
|
1568
|
+
throw new Error("No a11y tree found on page");
|
|
1569
|
+
}
|
|
1570
|
+
processedTree = processA11yTree(graph);
|
|
1571
|
+
} catch (e) {
|
|
1572
|
+
this.logger.error({ err: e, url }, "Error fetching a11y tree");
|
|
1573
|
+
if (attempt === 0) {
|
|
1574
|
+
yield sleep(1e3);
|
|
1575
|
+
attempt++;
|
|
1576
|
+
} else {
|
|
1577
|
+
throw new Error(`Max retries exceeded fetching a11y tree: ${e}`);
|
|
1578
|
+
}
|
|
1502
1579
|
}
|
|
1503
1580
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
this.logger.warn("A11y tree was pruned entirely");
|
|
1507
|
-
}
|
|
1508
|
-
this.nodeMap = processedTree.nodeMap;
|
|
1509
|
-
return processedTree;
|
|
1510
|
-
}
|
|
1511
|
-
async getRawA11yTree() {
|
|
1512
|
-
const url = this.page.url();
|
|
1513
|
-
let lastTreeUpdateTimestamp = Date.now();
|
|
1514
|
-
const treeUpdateListener = () => {
|
|
1515
|
-
lastTreeUpdateTimestamp = Date.now();
|
|
1516
|
-
};
|
|
1517
|
-
this.cdpClient.addListener(
|
|
1518
|
-
"Accessibility.nodesUpdated",
|
|
1519
|
-
treeUpdateListener
|
|
1520
|
-
);
|
|
1521
|
-
let accessibilityTreeLoadFired = false;
|
|
1522
|
-
const accessibilityLoadListener = () => {
|
|
1523
|
-
this.logger.info({ url }, `A11y tree load event fired`);
|
|
1524
|
-
accessibilityTreeLoadFired = true;
|
|
1525
|
-
};
|
|
1526
|
-
this.cdpClient.addListener(
|
|
1527
|
-
"Accessibility.loadComplete",
|
|
1528
|
-
accessibilityLoadListener
|
|
1529
|
-
);
|
|
1530
|
-
const a11yLoadStart = Date.now();
|
|
1531
|
-
let timeoutTriggered = true;
|
|
1532
|
-
while (Date.now() - a11yLoadStart < A11Y_STABLE_TIMEOUT_MS) {
|
|
1533
|
-
await sleep(CHECK_INTERVAL_MS);
|
|
1534
|
-
if (!accessibilityTreeLoadFired && Date.now() - a11yLoadStart < A11Y_LOAD_TIMEOUT_MS) {
|
|
1535
|
-
this.logger.debug({ url }, `A11y tree not loaded yet, waiting...`);
|
|
1536
|
-
continue;
|
|
1537
|
-
}
|
|
1538
|
-
if (Date.now() - lastTreeUpdateTimestamp >= A11Y_STABLE_DURATION_MS) {
|
|
1539
|
-
this.logger.debug({ url }, `A11y tree not stable yet, waiting...`);
|
|
1540
|
-
continue;
|
|
1581
|
+
if (!processedTree.root) {
|
|
1582
|
+
this.logger.warn("A11y tree was pruned entirely");
|
|
1541
1583
|
}
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
}
|
|
1545
|
-
this.logger.debug(
|
|
1546
|
-
{
|
|
1547
|
-
duration: Date.now() - a11yLoadStart,
|
|
1548
|
-
eventReceived: accessibilityTreeLoadFired,
|
|
1549
|
-
timeoutTriggered
|
|
1550
|
-
},
|
|
1551
|
-
"A11y wait phase completed"
|
|
1552
|
-
);
|
|
1553
|
-
const { node: root } = await this.cdpClient.send(
|
|
1554
|
-
"Accessibility.getRootAXNode"
|
|
1555
|
-
);
|
|
1556
|
-
const { nodes } = await this.cdpClient.send("Accessibility.queryAXTree", {
|
|
1557
|
-
backendNodeId: root.backendDOMNodeId
|
|
1584
|
+
this.nodeMap = processedTree.nodeMap;
|
|
1585
|
+
return processedTree;
|
|
1558
1586
|
});
|
|
1559
|
-
this.cdpClient.removeListener(
|
|
1560
|
-
"Accessibility.loadComplete",
|
|
1561
|
-
accessibilityLoadListener
|
|
1562
|
-
);
|
|
1563
|
-
this.cdpClient.removeListener(
|
|
1564
|
-
"Accessibility.nodesUpdated",
|
|
1565
|
-
treeUpdateListener
|
|
1566
|
-
);
|
|
1567
|
-
return {
|
|
1568
|
-
root,
|
|
1569
|
-
allNodes: nodes
|
|
1570
|
-
};
|
|
1571
1587
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1588
|
+
getRawA11yTree() {
|
|
1589
|
+
return __async(this, null, function* () {
|
|
1590
|
+
const url = this.page.url();
|
|
1591
|
+
let lastTreeUpdateTimestamp = Date.now();
|
|
1592
|
+
const treeUpdateListener = () => {
|
|
1593
|
+
lastTreeUpdateTimestamp = Date.now();
|
|
1594
|
+
};
|
|
1595
|
+
this.cdpClient.addListener(
|
|
1596
|
+
"Accessibility.nodesUpdated",
|
|
1597
|
+
treeUpdateListener
|
|
1577
1598
|
);
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
objectId
|
|
1587
|
-
});
|
|
1588
|
-
const attrResult = await this.cdpClient.send("DOM.getAttributes", {
|
|
1589
|
-
nodeId: cdpNodeResult.nodeId
|
|
1590
|
-
});
|
|
1591
|
-
const attributes = attrResult.attributes;
|
|
1592
|
-
const indexAttr = attributes.findIndex((s) => s === "data-momentic-id");
|
|
1593
|
-
if (indexAttr === -1) {
|
|
1594
|
-
return "";
|
|
1595
|
-
}
|
|
1596
|
-
return attributes[indexAttr + 1] || "";
|
|
1597
|
-
}
|
|
1598
|
-
async getLocatorFromBackendID(backendNodeId) {
|
|
1599
|
-
await this.page.evaluate(addIDsScript);
|
|
1600
|
-
const cdpResolveResult = await this.cdpClient.send("DOM.resolveNode", {
|
|
1601
|
-
backendNodeId
|
|
1602
|
-
});
|
|
1603
|
-
if (!cdpResolveResult || !cdpResolveResult.object.objectId) {
|
|
1604
|
-
throw new Error(`Could not resolve backend node ${backendNodeId}`);
|
|
1605
|
-
}
|
|
1606
|
-
try {
|
|
1607
|
-
const id = await this.getIDAttributeUsingCDP(
|
|
1608
|
-
cdpResolveResult.object.objectId
|
|
1599
|
+
let accessibilityTreeLoadFired = false;
|
|
1600
|
+
const accessibilityLoadListener = () => {
|
|
1601
|
+
this.logger.info({ url }, `A11y tree load event fired`);
|
|
1602
|
+
accessibilityTreeLoadFired = true;
|
|
1603
|
+
};
|
|
1604
|
+
this.cdpClient.addListener(
|
|
1605
|
+
"Accessibility.loadComplete",
|
|
1606
|
+
accessibilityLoadListener
|
|
1609
1607
|
);
|
|
1610
|
-
|
|
1611
|
-
|
|
1608
|
+
const a11yLoadStart = Date.now();
|
|
1609
|
+
let timeoutTriggered = true;
|
|
1610
|
+
while (Date.now() - a11yLoadStart < A11Y_STABLE_TIMEOUT_MS) {
|
|
1611
|
+
yield sleep(CHECK_INTERVAL_MS);
|
|
1612
|
+
if (!accessibilityTreeLoadFired && Date.now() - a11yLoadStart < A11Y_LOAD_TIMEOUT_MS) {
|
|
1613
|
+
this.logger.debug({ url }, `A11y tree not loaded yet, waiting...`);
|
|
1614
|
+
continue;
|
|
1615
|
+
}
|
|
1616
|
+
if (Date.now() - lastTreeUpdateTimestamp >= A11Y_STABLE_DURATION_MS) {
|
|
1617
|
+
this.logger.debug({ url }, `A11y tree not stable yet, waiting...`);
|
|
1618
|
+
continue;
|
|
1619
|
+
}
|
|
1620
|
+
timeoutTriggered = false;
|
|
1621
|
+
break;
|
|
1612
1622
|
}
|
|
1613
|
-
|
|
1614
|
-
} catch (err) {
|
|
1615
|
-
this.logger.error(
|
|
1623
|
+
this.logger.debug(
|
|
1616
1624
|
{
|
|
1617
|
-
|
|
1625
|
+
duration: Date.now() - a11yLoadStart,
|
|
1626
|
+
eventReceived: accessibilityTreeLoadFired,
|
|
1627
|
+
timeoutTriggered
|
|
1618
1628
|
},
|
|
1619
|
-
"
|
|
1629
|
+
"A11y wait phase completed"
|
|
1620
1630
|
);
|
|
1621
|
-
|
|
1622
|
-
|
|
1631
|
+
const { node: root } = yield this.cdpClient.send(
|
|
1632
|
+
"Accessibility.getRootAXNode"
|
|
1633
|
+
);
|
|
1634
|
+
const { nodes } = yield this.cdpClient.send("Accessibility.queryAXTree", {
|
|
1635
|
+
backendNodeId: root.backendDOMNodeId
|
|
1636
|
+
});
|
|
1637
|
+
this.cdpClient.removeListener(
|
|
1638
|
+
"Accessibility.loadComplete",
|
|
1639
|
+
accessibilityLoadListener
|
|
1640
|
+
);
|
|
1641
|
+
this.cdpClient.removeListener(
|
|
1642
|
+
"Accessibility.nodesUpdated",
|
|
1643
|
+
treeUpdateListener
|
|
1644
|
+
);
|
|
1645
|
+
return {
|
|
1646
|
+
root,
|
|
1647
|
+
allNodes: nodes
|
|
1648
|
+
};
|
|
1649
|
+
});
|
|
1623
1650
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
if (!candidateNode || candidateNode.role === "RootWebArea") {
|
|
1651
|
+
clickUsingVisualCoordinates(backendNodeId) {
|
|
1652
|
+
return __async(this, null, function* () {
|
|
1653
|
+
const location = yield this.getElementLocation(backendNodeId);
|
|
1654
|
+
if (!location) {
|
|
1629
1655
|
throw new Error(
|
|
1630
|
-
`
|
|
1656
|
+
`Could not find element location with backend node id: ${backendNodeId}`
|
|
1631
1657
|
);
|
|
1632
1658
|
}
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1659
|
+
this.logger.debug({ location }, "Executing mouse click");
|
|
1660
|
+
yield this.page.mouse.click(location.centerX, location.centerY);
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
// Get the "id" attribute value from an HTML element.
|
|
1664
|
+
getIDAttributeUsingCDP(objectId) {
|
|
1665
|
+
return __async(this, null, function* () {
|
|
1666
|
+
yield this.cdpClient.send("DOM.getDocument", { depth: 0 });
|
|
1667
|
+
const cdpNodeResult = yield this.cdpClient.send("DOM.requestNode", {
|
|
1668
|
+
objectId
|
|
1669
|
+
});
|
|
1670
|
+
const attrResult = yield this.cdpClient.send("DOM.getAttributes", {
|
|
1671
|
+
nodeId: cdpNodeResult.nodeId
|
|
1672
|
+
});
|
|
1673
|
+
const attributes = attrResult.attributes;
|
|
1674
|
+
const indexAttr = attributes.findIndex((s) => s === "data-momentic-id");
|
|
1675
|
+
if (indexAttr === -1) {
|
|
1676
|
+
return "";
|
|
1636
1677
|
}
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1678
|
+
return attributes[indexAttr + 1] || "";
|
|
1679
|
+
});
|
|
1680
|
+
}
|
|
1681
|
+
getLocatorFromBackendID(backendNodeId) {
|
|
1682
|
+
return __async(this, null, function* () {
|
|
1683
|
+
yield this.page.evaluate(addIDsScript);
|
|
1684
|
+
const cdpResolveResult = yield this.cdpClient.send("DOM.resolveNode", {
|
|
1685
|
+
backendNodeId
|
|
1686
|
+
});
|
|
1687
|
+
if (!cdpResolveResult || !cdpResolveResult.object.objectId) {
|
|
1688
|
+
throw new Error(`Could not resolve backend node ${backendNodeId}`);
|
|
1645
1689
|
}
|
|
1646
1690
|
try {
|
|
1647
|
-
const
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
} else {
|
|
1653
|
-
await locator.click({
|
|
1654
|
-
timeout: BROWSER_ACTION_TIMEOUT_MS,
|
|
1655
|
-
button: options.rightClick ? "right" : "left"
|
|
1656
|
-
});
|
|
1657
|
-
}
|
|
1658
|
-
if (candidateNode.id !== originalNode.id) {
|
|
1659
|
-
this.logger.info(
|
|
1660
|
-
{
|
|
1661
|
-
oldNode: originalNode.getLogForm(),
|
|
1662
|
-
newNode: candidateNode.getLogForm()
|
|
1663
|
-
},
|
|
1664
|
-
`Redirected click successfully to new element`
|
|
1665
|
-
);
|
|
1691
|
+
const id = yield this.getIDAttributeUsingCDP(
|
|
1692
|
+
cdpResolveResult.object.objectId
|
|
1693
|
+
);
|
|
1694
|
+
if (!id) {
|
|
1695
|
+
throw new Error("Failed getting data-momentic-id attribute using CDP");
|
|
1666
1696
|
}
|
|
1667
|
-
return
|
|
1697
|
+
return this.page.locator(`[data-momentic-id="${id}"]`);
|
|
1668
1698
|
} catch (err) {
|
|
1669
1699
|
this.logger.error(
|
|
1670
|
-
{
|
|
1671
|
-
|
|
1700
|
+
{
|
|
1701
|
+
err
|
|
1702
|
+
},
|
|
1703
|
+
"Failed to get ID attribute"
|
|
1672
1704
|
);
|
|
1673
|
-
|
|
1674
|
-
candidateNode = candidateNode.parent;
|
|
1705
|
+
throw err;
|
|
1675
1706
|
}
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
)
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
clickUsingCDP(_0) {
|
|
1710
|
+
return __async(this, arguments, function* (originalNode, options = {}) {
|
|
1711
|
+
let clickAttempts = 0;
|
|
1712
|
+
let candidateNode = originalNode;
|
|
1713
|
+
while (clickAttempts < MAX_BROWSER_ACTION_ATTEMPTS) {
|
|
1714
|
+
if (!candidateNode || candidateNode.role === "RootWebArea") {
|
|
1715
|
+
throw new Error(
|
|
1716
|
+
`Attempted to click node with no clickable surrounding elements: ${originalNode.getLogForm()}`
|
|
1717
|
+
);
|
|
1718
|
+
}
|
|
1719
|
+
if (candidateNode.role === "StaticText") {
|
|
1720
|
+
candidateNode = candidateNode.parent;
|
|
1721
|
+
continue;
|
|
1722
|
+
}
|
|
1723
|
+
const candidateNodeID = candidateNode.backendNodeID;
|
|
1724
|
+
if (!candidateNodeID) {
|
|
1725
|
+
this.logger.warn(
|
|
1726
|
+
{ node: candidateNode.getLogForm() },
|
|
1727
|
+
"Click candidate had no backend node ID"
|
|
1728
|
+
);
|
|
1729
|
+
candidateNode = candidateNode.parent;
|
|
1730
|
+
continue;
|
|
1731
|
+
}
|
|
1732
|
+
try {
|
|
1733
|
+
const locator = yield this.getLocatorFromBackendID(candidateNodeID);
|
|
1734
|
+
if (options.doubleClick) {
|
|
1735
|
+
yield locator.dblclick({
|
|
1736
|
+
timeout: BROWSER_ACTION_TIMEOUT_MS
|
|
1737
|
+
});
|
|
1738
|
+
} else {
|
|
1739
|
+
yield locator.click({
|
|
1740
|
+
timeout: BROWSER_ACTION_TIMEOUT_MS,
|
|
1741
|
+
button: options.rightClick ? "right" : "left"
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
if (candidateNode.id !== originalNode.id) {
|
|
1745
|
+
this.logger.info(
|
|
1746
|
+
{
|
|
1747
|
+
oldNode: originalNode.getLogForm(),
|
|
1748
|
+
newNode: candidateNode.getLogForm()
|
|
1749
|
+
},
|
|
1750
|
+
`Redirected click successfully to new element`
|
|
1751
|
+
);
|
|
1752
|
+
}
|
|
1753
|
+
return candidateNode;
|
|
1754
|
+
} catch (err) {
|
|
1755
|
+
this.logger.error(
|
|
1756
|
+
{ err, node: candidateNode.getLogForm() },
|
|
1757
|
+
"Failed click or click timed out"
|
|
1758
|
+
);
|
|
1759
|
+
clickAttempts++;
|
|
1760
|
+
candidateNode = candidateNode.parent;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
throw new Error(
|
|
1764
|
+
`Max click redirection attempts exhausted on original element: ${originalNode.getLogForm()}`
|
|
1765
|
+
);
|
|
1766
|
+
});
|
|
1680
1767
|
}
|
|
1681
1768
|
/**
|
|
1682
1769
|
* Currently unused, but could be useful for vision model integration.
|
|
1683
1770
|
* Gets x/y position of an a11y node.
|
|
1684
1771
|
*/
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
if (process.platform === "darwin" && devicePixelRatio === 1) {
|
|
1695
|
-
devicePixelRatio = RETINA_WINDOW_SCALE_FACTOR;
|
|
1696
|
-
}
|
|
1697
|
-
const document2 = tree["documents"][0];
|
|
1698
|
-
const layout = document2["layout"];
|
|
1699
|
-
const nodes = document2["nodes"];
|
|
1700
|
-
const nodeNames = nodes["nodeName"] || [];
|
|
1701
|
-
const backendNodeIds = nodes["backendNodeId"] || [];
|
|
1702
|
-
const layoutNodeIndex = layout["nodeIndex"];
|
|
1703
|
-
const bounds = layout["bounds"];
|
|
1704
|
-
let cursor2 = -1;
|
|
1705
|
-
for (let i = 0; i < nodeNames.length; i++) {
|
|
1706
|
-
if (backendNodeIds[i] === backendNodeId) {
|
|
1707
|
-
cursor2 = layoutNodeIndex.indexOf(i);
|
|
1708
|
-
break;
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
if (cursor2 === -1) {
|
|
1712
|
-
throw new Error(
|
|
1713
|
-
`Could not find any backend node with ID ${backendNodeId}`
|
|
1772
|
+
getElementLocation(backendNodeId) {
|
|
1773
|
+
return __async(this, null, function* () {
|
|
1774
|
+
const tree = yield this.cdpClient.send("DOMSnapshot.captureSnapshot", {
|
|
1775
|
+
computedStyles: [],
|
|
1776
|
+
includeDOMRects: true,
|
|
1777
|
+
includePaintOrder: true
|
|
1778
|
+
});
|
|
1779
|
+
let devicePixelRatio = yield this.page.evaluate(
|
|
1780
|
+
() => window.devicePixelRatio
|
|
1714
1781
|
);
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1782
|
+
if (process.platform === "darwin" && devicePixelRatio === 1) {
|
|
1783
|
+
devicePixelRatio = RETINA_WINDOW_SCALE_FACTOR;
|
|
1784
|
+
}
|
|
1785
|
+
const document2 = tree["documents"][0];
|
|
1786
|
+
const layout = document2["layout"];
|
|
1787
|
+
const nodes = document2["nodes"];
|
|
1788
|
+
const nodeNames = nodes["nodeName"] || [];
|
|
1789
|
+
const backendNodeIds = nodes["backendNodeId"] || [];
|
|
1790
|
+
const layoutNodeIndex = layout["nodeIndex"];
|
|
1791
|
+
const bounds = layout["bounds"];
|
|
1792
|
+
let cursor2 = -1;
|
|
1793
|
+
for (let i = 0; i < nodeNames.length; i++) {
|
|
1794
|
+
if (backendNodeIds[i] === backendNodeId) {
|
|
1795
|
+
cursor2 = layoutNodeIndex.indexOf(i);
|
|
1796
|
+
break;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
if (cursor2 === -1) {
|
|
1800
|
+
throw new Error(
|
|
1801
|
+
`Could not find any backend node with ID ${backendNodeId}`
|
|
1802
|
+
);
|
|
1803
|
+
}
|
|
1804
|
+
let [x = 0, y = 0, width = 0, height = 0] = bounds[cursor2];
|
|
1805
|
+
x /= devicePixelRatio;
|
|
1806
|
+
y /= devicePixelRatio;
|
|
1807
|
+
width /= devicePixelRatio;
|
|
1808
|
+
height /= devicePixelRatio;
|
|
1809
|
+
const centerX = x + width / 2;
|
|
1810
|
+
const centerY = y + height / 2;
|
|
1811
|
+
return { centerX, centerY };
|
|
1812
|
+
});
|
|
1724
1813
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1814
|
+
scrollUp() {
|
|
1815
|
+
return __async(this, null, function* () {
|
|
1816
|
+
yield this.page.evaluate(() => {
|
|
1817
|
+
(document.scrollingElement || document.body).scrollTop = (document.scrollingElement || document.body).scrollTop - window.innerHeight;
|
|
1818
|
+
});
|
|
1819
|
+
yield this.page.evaluate(() => {
|
|
1820
|
+
(document.scrollingElement || document.body).scrollTop = (document.scrollingElement || document.body).scrollTop + window.innerHeight;
|
|
1821
|
+
});
|
|
1728
1822
|
});
|
|
1729
|
-
|
|
1730
|
-
|
|
1823
|
+
}
|
|
1824
|
+
scrollDown() {
|
|
1825
|
+
return __async(this, null, function* () {
|
|
1826
|
+
yield this.page.evaluate(() => {
|
|
1827
|
+
(document.scrollingElement || document.body).scrollTop = (document.scrollingElement || document.body).scrollTop + window.innerHeight;
|
|
1828
|
+
});
|
|
1731
1829
|
});
|
|
1732
1830
|
}
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1831
|
+
goForward() {
|
|
1832
|
+
return __async(this, null, function* () {
|
|
1833
|
+
yield this.wrapPossibleNavigation(
|
|
1834
|
+
() => this.page.goForward({ timeout: MAX_LOAD_TIMEOUT_MS })
|
|
1835
|
+
);
|
|
1836
|
+
yield this.pageSetup();
|
|
1736
1837
|
});
|
|
1737
1838
|
}
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1839
|
+
goBack() {
|
|
1840
|
+
return __async(this, null, function* () {
|
|
1841
|
+
yield this.wrapPossibleNavigation(
|
|
1842
|
+
() => this.page.goBack({ timeout: MAX_LOAD_TIMEOUT_MS })
|
|
1843
|
+
);
|
|
1844
|
+
yield this.pageSetup();
|
|
1845
|
+
});
|
|
1743
1846
|
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
this.cdpClient = await this.context.newCDPSession(page);
|
|
1761
|
-
await initCDPSession(this.cdpClient);
|
|
1762
|
-
this.logger.info(`Switching to tab ${i} with url ${page.url()}`);
|
|
1763
|
-
return;
|
|
1847
|
+
switchToPage(urlSubstring) {
|
|
1848
|
+
return __async(this, null, function* () {
|
|
1849
|
+
const allPages = yield this.context.pages();
|
|
1850
|
+
for (let i = 0; i < allPages.length; i++) {
|
|
1851
|
+
const page = allPages[i];
|
|
1852
|
+
if (page.url().includes(urlSubstring)) {
|
|
1853
|
+
this.page = page;
|
|
1854
|
+
yield page.waitForLoadState("load", {
|
|
1855
|
+
timeout: MAX_LOAD_TIMEOUT_MS
|
|
1856
|
+
});
|
|
1857
|
+
yield this.pageSetup();
|
|
1858
|
+
this.cdpClient = yield this.context.newCDPSession(page);
|
|
1859
|
+
yield initCDPSession(this.cdpClient);
|
|
1860
|
+
this.logger.info(`Switching to tab ${i} with url ${page.url()}`);
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1764
1863
|
}
|
|
1765
|
-
|
|
1766
|
-
|
|
1864
|
+
throw new Error(`Could not find page with url containing ${urlSubstring}`);
|
|
1865
|
+
});
|
|
1767
1866
|
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1867
|
+
setCookie(cookie) {
|
|
1868
|
+
return __async(this, null, function* () {
|
|
1869
|
+
const cookieSettings = parseCookieString(cookie);
|
|
1870
|
+
yield this.context.addCookies([cookieSettings]);
|
|
1871
|
+
});
|
|
1771
1872
|
}
|
|
1772
1873
|
};
|
|
1874
|
+
_ChromeBrowser.USER_AGENT = devices["Desktop Chrome"].userAgent;
|
|
1875
|
+
var ChromeBrowser = _ChromeBrowser;
|
|
1773
1876
|
|
|
1774
1877
|
// ../../packages/web-agent/src/configs/controller.ts
|
|
1775
1878
|
var A11Y_CONTROLLER_CONFIG = {
|
|
@@ -1785,18 +1888,6 @@ import dedent2 from "dedent";
|
|
|
1785
1888
|
import diffLines from "diff-lines";
|
|
1786
1889
|
var MAX_HISTORY_CHAR_LENGTH = 1e4;
|
|
1787
1890
|
var AgentController = class {
|
|
1788
|
-
// Instance of browser to interact with
|
|
1789
|
-
browser;
|
|
1790
|
-
// Stack of queued-up instructions
|
|
1791
|
-
pendingInstructions;
|
|
1792
|
-
// manager for all AI generation
|
|
1793
|
-
generator;
|
|
1794
|
-
// Stack of commands previously executed.
|
|
1795
|
-
// Top of stack can be a pending command that hasn't been executed yet.
|
|
1796
|
-
// Should not contain intermediate successes due to granular commands.
|
|
1797
|
-
commandHistory;
|
|
1798
|
-
config;
|
|
1799
|
-
logger;
|
|
1800
1891
|
constructor({ browser, config, generator, logger }) {
|
|
1801
1892
|
this.browser = browser;
|
|
1802
1893
|
this.generator = generator;
|
|
@@ -1831,16 +1922,20 @@ var AgentController = class {
|
|
|
1831
1922
|
/**
|
|
1832
1923
|
* Reset controller and browser state.
|
|
1833
1924
|
*/
|
|
1834
|
-
|
|
1835
|
-
this
|
|
1836
|
-
|
|
1925
|
+
resetState() {
|
|
1926
|
+
return __async(this, null, function* () {
|
|
1927
|
+
this.resetHistory();
|
|
1928
|
+
yield this.browser.navigate(this.browser.baseURL);
|
|
1929
|
+
});
|
|
1837
1930
|
}
|
|
1838
1931
|
/**
|
|
1839
1932
|
* Get the browser state as a string
|
|
1840
1933
|
*/
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1934
|
+
getBrowserState() {
|
|
1935
|
+
return __async(this, null, function* () {
|
|
1936
|
+
const a11yTree = yield this.browser.getA11yTree();
|
|
1937
|
+
return a11yTree.serialize();
|
|
1938
|
+
});
|
|
1844
1939
|
}
|
|
1845
1940
|
getSerializedHistory(url, currentBrowserState) {
|
|
1846
1941
|
let history;
|
|
@@ -1851,99 +1946,105 @@ var AgentController = class {
|
|
|
1851
1946
|
}
|
|
1852
1947
|
return history;
|
|
1853
1948
|
}
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1949
|
+
splitUserGoal(type, goal, disableCache) {
|
|
1950
|
+
return __async(this, null, function* () {
|
|
1951
|
+
if (type === "AI_ACTION" /* AI_ACTION */ && goal.match(/[,!;.]|(?:and)|(?:then)/) && this.config.useGoalSplitter) {
|
|
1952
|
+
const granularInstructions = yield this.generator.getGranularGoals(
|
|
1953
|
+
{ goal, url: this.browser.url },
|
|
1954
|
+
disableCache
|
|
1955
|
+
);
|
|
1956
|
+
this.pendingInstructions = granularInstructions.reverse();
|
|
1957
|
+
} else {
|
|
1958
|
+
this.pendingInstructions = [goal];
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1864
1961
|
}
|
|
1865
1962
|
/**
|
|
1866
1963
|
* Given previously executed commands, generate command for the current prompt.
|
|
1867
1964
|
* Should only be used for AI action.
|
|
1868
1965
|
*/
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
{
|
|
1880
|
-
duration: Date.now() - getBrowserStateStart,
|
|
1881
|
-
url
|
|
1882
|
-
},
|
|
1883
|
-
"Got browser state"
|
|
1884
|
-
);
|
|
1885
|
-
const numPrevious = this.commandHistory.length;
|
|
1886
|
-
this.commandHistory.push({
|
|
1887
|
-
state: "PENDING",
|
|
1888
|
-
browserStateBeforeCommand: browserState,
|
|
1889
|
-
urlBeforeCommand: url,
|
|
1890
|
-
type
|
|
1891
|
-
});
|
|
1892
|
-
const history = this.getSerializedHistory(url, browserState);
|
|
1893
|
-
const getCommandProposalStart = Date.now();
|
|
1894
|
-
const proposedCommand = await this.generator.getProposedCommand(
|
|
1895
|
-
{
|
|
1896
|
-
url,
|
|
1897
|
-
numPrevious,
|
|
1898
|
-
browserState,
|
|
1899
|
-
history,
|
|
1900
|
-
goal: currInstruction,
|
|
1901
|
-
lastCommand: this.lastExecutedCommand
|
|
1902
|
-
},
|
|
1903
|
-
disableCache
|
|
1904
|
-
);
|
|
1905
|
-
this.logger.info(
|
|
1906
|
-
{ duration: Date.now() - getCommandProposalStart },
|
|
1907
|
-
"Got proposed command"
|
|
1908
|
-
);
|
|
1909
|
-
if (proposedCommand.type === "SUCCESS" /* SUCCESS */) {
|
|
1910
|
-
const finishedInstruction = this.pendingInstructions.pop();
|
|
1966
|
+
promptToCommand(type, goal, disableCache) {
|
|
1967
|
+
return __async(this, null, function* () {
|
|
1968
|
+
if (this.pendingInstructions.length === 0) {
|
|
1969
|
+
yield this.splitUserGoal(type, goal, disableCache);
|
|
1970
|
+
}
|
|
1971
|
+
const currInstruction = this.pendingInstructions[this.pendingInstructions.length - 1];
|
|
1972
|
+
this.logger.info({ goal: currInstruction }, "Starting prompt translation");
|
|
1973
|
+
const getBrowserStateStart = Date.now();
|
|
1974
|
+
const url = this.browser.url;
|
|
1975
|
+
const browserState = yield this.getBrowserState();
|
|
1911
1976
|
this.logger.info(
|
|
1912
1977
|
{
|
|
1913
|
-
|
|
1914
|
-
|
|
1978
|
+
duration: Date.now() - getBrowserStateStart,
|
|
1979
|
+
url
|
|
1915
1980
|
},
|
|
1916
|
-
"
|
|
1981
|
+
"Got browser state"
|
|
1917
1982
|
);
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1983
|
+
const numPrevious = this.commandHistory.length;
|
|
1984
|
+
this.commandHistory.push({
|
|
1985
|
+
state: "PENDING",
|
|
1986
|
+
browserStateBeforeCommand: browserState,
|
|
1987
|
+
urlBeforeCommand: url,
|
|
1988
|
+
type
|
|
1989
|
+
});
|
|
1990
|
+
const history = this.getSerializedHistory(url, browserState);
|
|
1991
|
+
const getCommandProposalStart = Date.now();
|
|
1992
|
+
const proposedCommand = yield this.generator.getProposedCommand(
|
|
1927
1993
|
{
|
|
1928
|
-
|
|
1994
|
+
url,
|
|
1995
|
+
numPrevious,
|
|
1996
|
+
browserState,
|
|
1997
|
+
history,
|
|
1998
|
+
goal: currInstruction,
|
|
1999
|
+
lastCommand: this.lastExecutedCommand
|
|
1929
2000
|
},
|
|
1930
|
-
|
|
2001
|
+
disableCache
|
|
1931
2002
|
);
|
|
1932
|
-
this.
|
|
1933
|
-
|
|
1934
|
-
|
|
2003
|
+
this.logger.info(
|
|
2004
|
+
{ duration: Date.now() - getCommandProposalStart },
|
|
2005
|
+
"Got proposed command"
|
|
2006
|
+
);
|
|
2007
|
+
if (proposedCommand.type === "SUCCESS" /* SUCCESS */) {
|
|
2008
|
+
const finishedInstruction = this.pendingInstructions.pop();
|
|
2009
|
+
this.logger.info(
|
|
2010
|
+
{
|
|
2011
|
+
finishedInstruction,
|
|
2012
|
+
remainingInstructions: this.pendingInstructions
|
|
2013
|
+
},
|
|
2014
|
+
"Removing pending instruction due to SUCCESS"
|
|
2015
|
+
);
|
|
2016
|
+
if (this.pendingInstructions.length !== 0) {
|
|
2017
|
+
this.commandHistory.pop();
|
|
2018
|
+
return this.promptToCommand(type, "", disableCache);
|
|
2019
|
+
}
|
|
2020
|
+
} else if (
|
|
2021
|
+
// on failure, we don't continue to execute
|
|
2022
|
+
proposedCommand.type === "FAILURE"
|
|
2023
|
+
) {
|
|
2024
|
+
this.logger.info(
|
|
2025
|
+
{
|
|
2026
|
+
remainingInstructions: this.pendingInstructions
|
|
2027
|
+
},
|
|
2028
|
+
"Removing pending instructions due to FAILURE"
|
|
2029
|
+
);
|
|
2030
|
+
this.pendingInstructions = [];
|
|
2031
|
+
}
|
|
2032
|
+
return proposedCommand;
|
|
2033
|
+
});
|
|
1935
2034
|
}
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
if (locator.id < 0) {
|
|
1942
|
-
throw new Error(
|
|
1943
|
-
`Unable to locate element with description: ${description}`
|
|
2035
|
+
locateElement(description, disableCache) {
|
|
2036
|
+
return __async(this, null, function* () {
|
|
2037
|
+
const locator = yield this.generator.getElementLocation(
|
|
2038
|
+
{ browserState: yield this.getBrowserState(), goal: description },
|
|
2039
|
+
disableCache
|
|
1944
2040
|
);
|
|
1945
|
-
|
|
1946
|
-
|
|
2041
|
+
if (locator.id < 0) {
|
|
2042
|
+
throw new Error(
|
|
2043
|
+
`Unable to locate element with description: ${description}`
|
|
2044
|
+
);
|
|
2045
|
+
}
|
|
2046
|
+
return locator;
|
|
2047
|
+
});
|
|
1947
2048
|
}
|
|
1948
2049
|
/**
|
|
1949
2050
|
* Construct a detailed history that can be passed to the LLM.
|
|
@@ -2001,104 +2102,108 @@ var AgentController = class {
|
|
|
2001
2102
|
* @param [stateless=false] Execute this command in a stateless fashion, without modifying any controller state such as
|
|
2002
2103
|
* pending instructions. Useful when executing cached instructions.
|
|
2003
2104
|
*/
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
if (!
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2105
|
+
executeCommand(command, disableCache, stateless = false) {
|
|
2106
|
+
return __async(this, null, function* () {
|
|
2107
|
+
const pendingHistory = this.commandHistory[this.commandHistory.length - 1];
|
|
2108
|
+
if (!stateless) {
|
|
2109
|
+
if (!pendingHistory || pendingHistory.state !== "PENDING") {
|
|
2110
|
+
throw new Error(
|
|
2111
|
+
"Executing command but there is no pending entry in the history"
|
|
2112
|
+
);
|
|
2113
|
+
}
|
|
2114
|
+
} else {
|
|
2115
|
+
yield this.browser.getA11yTree();
|
|
2011
2116
|
}
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2117
|
+
let result;
|
|
2118
|
+
try {
|
|
2119
|
+
const executionStart = Date.now();
|
|
2120
|
+
result = yield this.executePresetStep(
|
|
2121
|
+
command,
|
|
2122
|
+
disableCache
|
|
2123
|
+
);
|
|
2124
|
+
this.logger.info(
|
|
2125
|
+
{ result, duration: Date.now() - executionStart },
|
|
2126
|
+
"Got execution result"
|
|
2127
|
+
);
|
|
2128
|
+
} catch (e) {
|
|
2129
|
+
if (e instanceof Error) {
|
|
2130
|
+
throw new BrowserExecutionError(`Failed to execute command: ${e}`, {
|
|
2131
|
+
cause: e
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
throw new BrowserExecutionError(
|
|
2135
|
+
`Unexpected throw from executing command`,
|
|
2136
|
+
{
|
|
2137
|
+
cause: new Error(`${e}`)
|
|
2138
|
+
}
|
|
2139
|
+
);
|
|
2031
2140
|
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
{
|
|
2035
|
-
|
|
2141
|
+
if (result.succeedImmediately && !stateless) {
|
|
2142
|
+
this.pendingInstructions.pop();
|
|
2143
|
+
if (this.pendingInstructions.length > 0) {
|
|
2144
|
+
result.succeedImmediately = false;
|
|
2036
2145
|
}
|
|
2037
|
-
);
|
|
2038
|
-
}
|
|
2039
|
-
if (result.succeedImmediately && !stateless) {
|
|
2040
|
-
this.pendingInstructions.pop();
|
|
2041
|
-
if (this.pendingInstructions.length > 0) {
|
|
2042
|
-
result.succeedImmediately = false;
|
|
2043
2146
|
}
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2147
|
+
if (result.elementInteracted && "target" in command && !command.target.elementDescriptor) {
|
|
2148
|
+
command.target.elementDescriptor = result.elementInteracted.trim();
|
|
2149
|
+
}
|
|
2150
|
+
if (!stateless) {
|
|
2151
|
+
pendingHistory.generatedStep = command;
|
|
2152
|
+
pendingHistory.serializedCommand = serializeCommand(command);
|
|
2153
|
+
pendingHistory.state = "DONE";
|
|
2154
|
+
}
|
|
2155
|
+
return result;
|
|
2156
|
+
});
|
|
2054
2157
|
}
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
if (assertionEval.relevantElements) {
|
|
2088
|
-
void Promise.all(
|
|
2089
|
-
assertionEval.relevantElements.map(
|
|
2090
|
-
(id) => this.browser.highlight({ id })
|
|
2091
|
-
)
|
|
2158
|
+
executeAssertion(urlBeforeCommand, command) {
|
|
2159
|
+
return __async(this, null, function* () {
|
|
2160
|
+
let params;
|
|
2161
|
+
if (command.useVision) {
|
|
2162
|
+
params = {
|
|
2163
|
+
goal: command.assertion,
|
|
2164
|
+
url: urlBeforeCommand,
|
|
2165
|
+
// used for vision only
|
|
2166
|
+
screenshot: yield this.browser.screenshot(),
|
|
2167
|
+
// unused for visual assertion
|
|
2168
|
+
browserState: "",
|
|
2169
|
+
history: "",
|
|
2170
|
+
numPrevious: -1,
|
|
2171
|
+
lastCommand: null
|
|
2172
|
+
};
|
|
2173
|
+
} else {
|
|
2174
|
+
const browserState = yield this.getBrowserState();
|
|
2175
|
+
const history = this.getSerializedHistory(urlBeforeCommand, browserState);
|
|
2176
|
+
params = {
|
|
2177
|
+
goal: command.assertion,
|
|
2178
|
+
url: urlBeforeCommand,
|
|
2179
|
+
// used for text only
|
|
2180
|
+
browserState,
|
|
2181
|
+
history,
|
|
2182
|
+
lastCommand: this.lastExecutedCommand,
|
|
2183
|
+
numPrevious: this.commandHistory.length
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
const assertionEval = yield this.generator.getAssertionResult(
|
|
2187
|
+
params,
|
|
2188
|
+
command.useVision,
|
|
2189
|
+
command.disableCache
|
|
2092
2190
|
);
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2191
|
+
if (assertionEval.relevantElements) {
|
|
2192
|
+
void Promise.all(
|
|
2193
|
+
assertionEval.relevantElements.map(
|
|
2194
|
+
(id) => this.browser.highlight({ id })
|
|
2195
|
+
)
|
|
2196
|
+
);
|
|
2197
|
+
}
|
|
2198
|
+
if (!assertionEval.result) {
|
|
2199
|
+
throw new Error(assertionEval.thoughts);
|
|
2200
|
+
}
|
|
2201
|
+
return {
|
|
2202
|
+
succeedImmediately: false,
|
|
2203
|
+
thoughts: assertionEval.thoughts,
|
|
2204
|
+
urlAfterCommand: urlBeforeCommand
|
|
2205
|
+
};
|
|
2206
|
+
});
|
|
2102
2207
|
}
|
|
2103
2208
|
/**
|
|
2104
2209
|
* Executes a preset command.
|
|
@@ -2106,157 +2211,159 @@ var AgentController = class {
|
|
|
2106
2211
|
* For assertions, an AssertionResult with thoughts is returned.
|
|
2107
2212
|
* Throws on failure.
|
|
2108
2213
|
*/
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2214
|
+
executePresetStep(command, disableCache) {
|
|
2215
|
+
return __async(this, null, function* () {
|
|
2216
|
+
var _a, _b, _c;
|
|
2217
|
+
const urlBeforeCommand = this.browser.url;
|
|
2218
|
+
switch (command.type) {
|
|
2219
|
+
case "SUCCESS" /* SUCCESS */:
|
|
2220
|
+
if ((_a = command.condition) == null ? void 0 : _a.assertion.trim()) {
|
|
2221
|
+
return this.executeAssertion(urlBeforeCommand, command.condition);
|
|
2222
|
+
}
|
|
2223
|
+
return {
|
|
2224
|
+
succeedImmediately: false,
|
|
2225
|
+
urlAfterCommand: this.browser.url
|
|
2226
|
+
};
|
|
2227
|
+
case "AI_ASSERTION" /* AI_ASSERTION */: {
|
|
2228
|
+
return this.executeAssertion(urlBeforeCommand, command);
|
|
2116
2229
|
}
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2230
|
+
case "NAVIGATE" /* NAVIGATE */:
|
|
2231
|
+
yield this.browser.navigate(command.url);
|
|
2232
|
+
break;
|
|
2233
|
+
case "GO_BACK" /* GO_BACK */:
|
|
2234
|
+
yield this.browser.goBack();
|
|
2235
|
+
break;
|
|
2236
|
+
case "GO_FORWARD" /* GO_FORWARD */:
|
|
2237
|
+
yield this.browser.goForward();
|
|
2238
|
+
break;
|
|
2239
|
+
case "SCROLL_DOWN" /* SCROLL_DOWN */:
|
|
2240
|
+
yield this.browser.scrollDown();
|
|
2241
|
+
break;
|
|
2242
|
+
case "SCROLL_UP" /* SCROLL_UP */:
|
|
2243
|
+
yield this.browser.scrollUp();
|
|
2244
|
+
break;
|
|
2245
|
+
case "WAIT" /* WAIT */:
|
|
2246
|
+
yield this.browser.wait(command.delay * 1e3);
|
|
2247
|
+
break;
|
|
2248
|
+
case "REFRESH" /* REFRESH */:
|
|
2249
|
+
yield this.browser.refresh();
|
|
2250
|
+
break;
|
|
2251
|
+
case "CLICK" /* CLICK */: {
|
|
2252
|
+
let id;
|
|
2253
|
+
if (command.target.a11yData) {
|
|
2254
|
+
id = (_b = command.target.a11yData) == null ? void 0 : _b.id;
|
|
2255
|
+
} else {
|
|
2256
|
+
const locator = yield this.locateElement(
|
|
2257
|
+
command.target.elementDescriptor,
|
|
2258
|
+
disableCache
|
|
2259
|
+
);
|
|
2260
|
+
id = locator.id;
|
|
2261
|
+
}
|
|
2262
|
+
const elementInteracted = yield this.browser.click(
|
|
2263
|
+
{
|
|
2264
|
+
id
|
|
2265
|
+
},
|
|
2266
|
+
{
|
|
2267
|
+
doubleClick: command.doubleClick,
|
|
2268
|
+
rightClick: command.rightClick
|
|
2269
|
+
}
|
|
2153
2270
|
);
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
rightClick: command.rightClick
|
|
2271
|
+
const result2 = {
|
|
2272
|
+
urlAfterCommand: this.browser.url,
|
|
2273
|
+
succeedImmediately: false,
|
|
2274
|
+
elementInteracted
|
|
2275
|
+
};
|
|
2276
|
+
if (urlChanged(urlBeforeCommand, result2.urlAfterCommand)) {
|
|
2277
|
+
result2.succeedImmediately = true;
|
|
2278
|
+
result2.succeedImmediatelyReason = "URL changed";
|
|
2163
2279
|
}
|
|
2164
|
-
|
|
2165
|
-
const result2 = {
|
|
2166
|
-
urlAfterCommand: this.browser.url,
|
|
2167
|
-
succeedImmediately: false,
|
|
2168
|
-
elementInteracted
|
|
2169
|
-
};
|
|
2170
|
-
if (urlChanged(urlBeforeCommand, result2.urlAfterCommand)) {
|
|
2171
|
-
result2.succeedImmediately = true;
|
|
2172
|
-
result2.succeedImmediatelyReason = "URL changed";
|
|
2280
|
+
return result2;
|
|
2173
2281
|
}
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2282
|
+
case "SELECT_OPTION" /* SELECT_OPTION */: {
|
|
2283
|
+
let id;
|
|
2284
|
+
if (command.target.a11yData) {
|
|
2285
|
+
id = (_c = command.target.a11yData) == null ? void 0 : _c.id;
|
|
2286
|
+
} else {
|
|
2287
|
+
const locator = yield this.locateElement(
|
|
2288
|
+
command.target.elementDescriptor,
|
|
2289
|
+
disableCache
|
|
2290
|
+
);
|
|
2291
|
+
id = locator.id;
|
|
2292
|
+
}
|
|
2293
|
+
const elementInteracted = yield this.browser.selectOption(
|
|
2294
|
+
{
|
|
2295
|
+
id
|
|
2296
|
+
},
|
|
2297
|
+
command.option
|
|
2184
2298
|
);
|
|
2185
|
-
|
|
2299
|
+
return {
|
|
2300
|
+
succeedImmediately: false,
|
|
2301
|
+
urlAfterCommand: this.browser.url,
|
|
2302
|
+
elementInteracted
|
|
2303
|
+
};
|
|
2186
2304
|
}
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
command.
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
} else if (target.elementDescriptor.length > 0) {
|
|
2213
|
-
const locator = await this.locateElement(
|
|
2214
|
-
command.target.elementDescriptor,
|
|
2215
|
-
disableCache
|
|
2216
|
-
);
|
|
2217
|
-
elementInteracted = await this.browser.click({
|
|
2218
|
-
id: locator.id
|
|
2305
|
+
case "TAB" /* TAB */:
|
|
2306
|
+
yield this.browser.switchToPage(command.url);
|
|
2307
|
+
break;
|
|
2308
|
+
case "COOKIE" /* COOKIE */:
|
|
2309
|
+
yield this.browser.setCookie(command.value);
|
|
2310
|
+
break;
|
|
2311
|
+
case "TYPE" /* TYPE */: {
|
|
2312
|
+
let elementInteracted;
|
|
2313
|
+
const target = command.target;
|
|
2314
|
+
if (target.a11yData) {
|
|
2315
|
+
elementInteracted = yield this.browser.click({
|
|
2316
|
+
id: target.a11yData.id
|
|
2317
|
+
});
|
|
2318
|
+
} else if (target.elementDescriptor.length > 0) {
|
|
2319
|
+
const locator = yield this.locateElement(
|
|
2320
|
+
command.target.elementDescriptor,
|
|
2321
|
+
disableCache
|
|
2322
|
+
);
|
|
2323
|
+
elementInteracted = yield this.browser.click({
|
|
2324
|
+
id: locator.id
|
|
2325
|
+
});
|
|
2326
|
+
}
|
|
2327
|
+
yield this.browser.type(command.value, {
|
|
2328
|
+
clearContent: command.clearContent,
|
|
2329
|
+
pressKeysSequentially: command.pressKeysSequentially
|
|
2219
2330
|
});
|
|
2331
|
+
if (command.pressEnter) {
|
|
2332
|
+
yield this.browser.press("Enter");
|
|
2333
|
+
}
|
|
2334
|
+
const result2 = {
|
|
2335
|
+
urlAfterCommand: this.browser.url,
|
|
2336
|
+
succeedImmediately: false,
|
|
2337
|
+
elementInteracted
|
|
2338
|
+
};
|
|
2339
|
+
if (urlChanged(urlBeforeCommand, result2.urlAfterCommand)) {
|
|
2340
|
+
result2.succeedImmediately = true;
|
|
2341
|
+
result2.succeedImmediatelyReason = "URL changed";
|
|
2342
|
+
}
|
|
2343
|
+
return result2;
|
|
2220
2344
|
}
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
return result2;
|
|
2345
|
+
case "PRESS" /* PRESS */:
|
|
2346
|
+
yield this.browser.press(command.value);
|
|
2347
|
+
const result = {
|
|
2348
|
+
urlAfterCommand: this.browser.url,
|
|
2349
|
+
succeedImmediately: false
|
|
2350
|
+
};
|
|
2351
|
+
if (urlChanged(urlBeforeCommand, result.urlAfterCommand)) {
|
|
2352
|
+
result.succeedImmediately = true;
|
|
2353
|
+
result.succeedImmediatelyReason = "URL changed";
|
|
2354
|
+
}
|
|
2355
|
+
return result;
|
|
2356
|
+
default:
|
|
2357
|
+
const assertUnreachable = (_x) => {
|
|
2358
|
+
throw "If Typescript complains about the line below, you missed a case or break in the switch above";
|
|
2359
|
+
};
|
|
2360
|
+
return assertUnreachable(command);
|
|
2238
2361
|
}
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
};
|
|
2245
|
-
if (urlChanged(urlBeforeCommand, result.urlAfterCommand)) {
|
|
2246
|
-
result.succeedImmediately = true;
|
|
2247
|
-
result.succeedImmediatelyReason = "URL changed";
|
|
2248
|
-
}
|
|
2249
|
-
return result;
|
|
2250
|
-
default:
|
|
2251
|
-
const assertUnreachable = (_x) => {
|
|
2252
|
-
throw "If Typescript complains about the line below, you missed a case or break in the switch above";
|
|
2253
|
-
};
|
|
2254
|
-
return assertUnreachable(command);
|
|
2255
|
-
}
|
|
2256
|
-
return {
|
|
2257
|
-
succeedImmediately: false,
|
|
2258
|
-
urlAfterCommand: this.browser.url
|
|
2259
|
-
};
|
|
2362
|
+
return {
|
|
2363
|
+
succeedImmediately: false,
|
|
2364
|
+
urlAfterCommand: this.browser.url
|
|
2365
|
+
};
|
|
2366
|
+
});
|
|
2260
2367
|
}
|
|
2261
2368
|
};
|
|
2262
2369
|
|
|
@@ -2265,96 +2372,104 @@ import fetchRetry from "fetch-retry";
|
|
|
2265
2372
|
var fetch2 = fetchRetry(global.fetch);
|
|
2266
2373
|
var API_VERSION = "v1";
|
|
2267
2374
|
var APIGenerator = class {
|
|
2268
|
-
baseURL;
|
|
2269
|
-
apiKey;
|
|
2270
2375
|
constructor(params) {
|
|
2271
2376
|
this.baseURL = params.baseURL;
|
|
2272
2377
|
this.apiKey = params.apiKey;
|
|
2273
2378
|
}
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2379
|
+
getElementLocation(context, disableCache) {
|
|
2380
|
+
return __async(this, null, function* () {
|
|
2381
|
+
const result = yield this.sendRequest(
|
|
2382
|
+
`/${API_VERSION}/web-agent/locate-element`,
|
|
2383
|
+
{
|
|
2384
|
+
browserState: context.browserState,
|
|
2385
|
+
goal: context.goal,
|
|
2386
|
+
disableCache
|
|
2387
|
+
}
|
|
2388
|
+
);
|
|
2389
|
+
return LocateResponseSchema.parse(result);
|
|
2390
|
+
});
|
|
2284
2391
|
}
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2392
|
+
getAssertionResult(context, useVision, disableCache) {
|
|
2393
|
+
return __async(this, null, function* () {
|
|
2394
|
+
var _a;
|
|
2395
|
+
if (useVision) {
|
|
2396
|
+
const result2 = yield this.sendRequest(
|
|
2397
|
+
`/${API_VERSION}/web-agent/assertion`,
|
|
2398
|
+
{
|
|
2399
|
+
url: context.url,
|
|
2400
|
+
goal: context.goal,
|
|
2401
|
+
screenshot: (_a = context.screenshot) == null ? void 0 : _a.toString("base64"),
|
|
2402
|
+
disableCache,
|
|
2403
|
+
vision: true
|
|
2404
|
+
}
|
|
2405
|
+
);
|
|
2406
|
+
return GetAssertionResponseSchema.parse(result2);
|
|
2407
|
+
}
|
|
2408
|
+
const result = yield this.sendRequest(
|
|
2289
2409
|
`/${API_VERSION}/web-agent/assertion`,
|
|
2290
2410
|
{
|
|
2291
2411
|
url: context.url,
|
|
2412
|
+
browserState: context.browserState,
|
|
2292
2413
|
goal: context.goal,
|
|
2293
|
-
|
|
2414
|
+
history: context.history,
|
|
2415
|
+
numPrevious: context.numPrevious,
|
|
2416
|
+
lastCommand: context.lastCommand,
|
|
2294
2417
|
disableCache,
|
|
2295
|
-
vision:
|
|
2418
|
+
vision: false
|
|
2296
2419
|
}
|
|
2297
2420
|
);
|
|
2298
|
-
return GetAssertionResponseSchema.parse(
|
|
2299
|
-
}
|
|
2300
|
-
const result = await this.sendRequest(
|
|
2301
|
-
`/${API_VERSION}/web-agent/assertion`,
|
|
2302
|
-
{
|
|
2303
|
-
url: context.url,
|
|
2304
|
-
browserState: context.browserState,
|
|
2305
|
-
goal: context.goal,
|
|
2306
|
-
history: context.history,
|
|
2307
|
-
numPrevious: context.numPrevious,
|
|
2308
|
-
lastCommand: context.lastCommand,
|
|
2309
|
-
disableCache,
|
|
2310
|
-
vision: false
|
|
2311
|
-
}
|
|
2312
|
-
);
|
|
2313
|
-
return GetAssertionResponseSchema.parse(result);
|
|
2314
|
-
}
|
|
2315
|
-
async getProposedCommand(context, disableCache) {
|
|
2316
|
-
const result = await this.sendRequest(
|
|
2317
|
-
`/${API_VERSION}/web-agent/next-command`,
|
|
2318
|
-
{
|
|
2319
|
-
url: context.url,
|
|
2320
|
-
browserState: context.browserState,
|
|
2321
|
-
goal: context.goal,
|
|
2322
|
-
history: context.history,
|
|
2323
|
-
numPrevious: context.numPrevious,
|
|
2324
|
-
lastCommand: context.lastCommand,
|
|
2325
|
-
disableCache
|
|
2326
|
-
}
|
|
2327
|
-
);
|
|
2328
|
-
return GetNextCommandResponseSchema.parse(result);
|
|
2329
|
-
}
|
|
2330
|
-
async getGranularGoals(context, disableCache) {
|
|
2331
|
-
const result = await this.sendRequest(
|
|
2332
|
-
`/${API_VERSION}/web-agent/split-goal`,
|
|
2333
|
-
{
|
|
2334
|
-
url: context.url,
|
|
2335
|
-
goal: context.goal,
|
|
2336
|
-
disableCache
|
|
2337
|
-
}
|
|
2338
|
-
);
|
|
2339
|
-
return SplitGoalResponseSchema.parse(result);
|
|
2340
|
-
}
|
|
2341
|
-
async sendRequest(path, body) {
|
|
2342
|
-
const response = await fetch2(`${this.baseURL}${path}`, {
|
|
2343
|
-
retries: 3,
|
|
2344
|
-
retryDelay: 1e3,
|
|
2345
|
-
method: "POST",
|
|
2346
|
-
body: JSON.stringify(body),
|
|
2347
|
-
headers: {
|
|
2348
|
-
"Content-Type": "application/json",
|
|
2349
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
2350
|
-
}
|
|
2421
|
+
return GetAssertionResponseSchema.parse(result);
|
|
2351
2422
|
});
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2423
|
+
}
|
|
2424
|
+
getProposedCommand(context, disableCache) {
|
|
2425
|
+
return __async(this, null, function* () {
|
|
2426
|
+
const result = yield this.sendRequest(
|
|
2427
|
+
`/${API_VERSION}/web-agent/next-command`,
|
|
2428
|
+
{
|
|
2429
|
+
url: context.url,
|
|
2430
|
+
browserState: context.browserState,
|
|
2431
|
+
goal: context.goal,
|
|
2432
|
+
history: context.history,
|
|
2433
|
+
numPrevious: context.numPrevious,
|
|
2434
|
+
lastCommand: context.lastCommand,
|
|
2435
|
+
disableCache
|
|
2436
|
+
}
|
|
2355
2437
|
);
|
|
2356
|
-
|
|
2357
|
-
|
|
2438
|
+
return GetNextCommandResponseSchema.parse(result);
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
getGranularGoals(context, disableCache) {
|
|
2442
|
+
return __async(this, null, function* () {
|
|
2443
|
+
const result = yield this.sendRequest(
|
|
2444
|
+
`/${API_VERSION}/web-agent/split-goal`,
|
|
2445
|
+
{
|
|
2446
|
+
url: context.url,
|
|
2447
|
+
goal: context.goal,
|
|
2448
|
+
disableCache
|
|
2449
|
+
}
|
|
2450
|
+
);
|
|
2451
|
+
return SplitGoalResponseSchema.parse(result);
|
|
2452
|
+
});
|
|
2453
|
+
}
|
|
2454
|
+
sendRequest(path, body) {
|
|
2455
|
+
return __async(this, null, function* () {
|
|
2456
|
+
const response = yield fetch2(`${this.baseURL}${path}`, {
|
|
2457
|
+
retries: 3,
|
|
2458
|
+
retryDelay: 1e3,
|
|
2459
|
+
method: "POST",
|
|
2460
|
+
body: JSON.stringify(body),
|
|
2461
|
+
headers: {
|
|
2462
|
+
"Content-Type": "application/json",
|
|
2463
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
2464
|
+
}
|
|
2465
|
+
});
|
|
2466
|
+
if (!response.ok) {
|
|
2467
|
+
throw new Error(
|
|
2468
|
+
`Request to ${path} failed with status ${response.status}: ${yield response.text()}`
|
|
2469
|
+
);
|
|
2470
|
+
}
|
|
2471
|
+
return response.json();
|
|
2472
|
+
});
|
|
2358
2473
|
}
|
|
2359
2474
|
};
|
|
2360
2475
|
|
|
@@ -2364,62 +2479,72 @@ var version = "1.0.0";
|
|
|
2364
2479
|
// src/api-client.ts
|
|
2365
2480
|
var API_VERSION2 = "v1";
|
|
2366
2481
|
var APIClient = class {
|
|
2367
|
-
baseURL;
|
|
2368
|
-
apiKey;
|
|
2369
2482
|
constructor(params) {
|
|
2370
2483
|
this.baseURL = params.baseURL;
|
|
2371
2484
|
this.apiKey = params.apiKey;
|
|
2372
2485
|
}
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2486
|
+
getRun(runId) {
|
|
2487
|
+
return __async(this, null, function* () {
|
|
2488
|
+
const result = yield this.sendRequest(`/${API_VERSION2}/runs/${runId}`, {
|
|
2489
|
+
method: "GET"
|
|
2490
|
+
});
|
|
2491
|
+
return GetRunResponseSchema.parse(result);
|
|
2376
2492
|
});
|
|
2377
|
-
return GetRunResponseSchema.parse(result);
|
|
2378
2493
|
}
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2494
|
+
createRun(body) {
|
|
2495
|
+
return __async(this, null, function* () {
|
|
2496
|
+
const result = yield this.sendRequest(`/${API_VERSION2}/runs`, {
|
|
2497
|
+
method: "POST",
|
|
2498
|
+
body
|
|
2499
|
+
});
|
|
2500
|
+
return CreateRunResponseSchema.parse(result);
|
|
2383
2501
|
});
|
|
2384
|
-
return CreateRunResponseSchema.parse(result);
|
|
2385
2502
|
}
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2503
|
+
updateRun(runId, body) {
|
|
2504
|
+
return __async(this, null, function* () {
|
|
2505
|
+
yield this.sendRequest(`/${API_VERSION2}/runs/${runId}`, {
|
|
2506
|
+
method: "PATCH",
|
|
2507
|
+
body
|
|
2508
|
+
});
|
|
2390
2509
|
});
|
|
2391
2510
|
}
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2511
|
+
getTest(testId) {
|
|
2512
|
+
return __async(this, null, function* () {
|
|
2513
|
+
const result = yield this.sendRequest(`/${API_VERSION2}/tests/${testId}`, {
|
|
2514
|
+
method: "GET"
|
|
2515
|
+
});
|
|
2516
|
+
return GetTestResponseSchema.parse(result);
|
|
2395
2517
|
});
|
|
2396
|
-
return GetTestResponseSchema.parse(result);
|
|
2397
2518
|
}
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2519
|
+
uploadScreenshot(body) {
|
|
2520
|
+
return __async(this, null, function* () {
|
|
2521
|
+
const result = yield this.sendRequest(`/${API_VERSION2}/screenshots`, {
|
|
2522
|
+
method: "POST",
|
|
2523
|
+
body
|
|
2524
|
+
});
|
|
2525
|
+
return CreateScreenshotResponseSchema.parse(result);
|
|
2402
2526
|
});
|
|
2403
|
-
return CreateScreenshotResponseSchema.parse(result);
|
|
2404
2527
|
}
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2528
|
+
sendRequest(path, options) {
|
|
2529
|
+
return __async(this, null, function* () {
|
|
2530
|
+
const response = yield fetch(`${this.baseURL}${path}`, {
|
|
2531
|
+
method: options.method,
|
|
2532
|
+
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2533
|
+
headers: {
|
|
2534
|
+
"Content-Type": "application/json",
|
|
2535
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
2536
|
+
}
|
|
2537
|
+
});
|
|
2538
|
+
if (!response.ok) {
|
|
2539
|
+
throw new Error(
|
|
2540
|
+
`Request to ${path} failed with status ${response.status}: ${yield response.text()}`
|
|
2541
|
+
);
|
|
2542
|
+
}
|
|
2543
|
+
if (response.status === 204) {
|
|
2544
|
+
return response.text();
|
|
2412
2545
|
}
|
|
2546
|
+
return response.json();
|
|
2413
2547
|
});
|
|
2414
|
-
if (!response.ok) {
|
|
2415
|
-
throw new Error(
|
|
2416
|
-
`Request to ${path} failed with status ${response.status}: ${await response.text()}`
|
|
2417
|
-
);
|
|
2418
|
-
}
|
|
2419
|
-
if (response.status === 204) {
|
|
2420
|
-
return response.text();
|
|
2421
|
-
}
|
|
2422
|
-
return response.json();
|
|
2423
2548
|
}
|
|
2424
2549
|
};
|
|
2425
2550
|
|
|
@@ -2427,25 +2552,29 @@ var APIClient = class {
|
|
|
2427
2552
|
var MAX_COMMANDS_PER_STEP = 20;
|
|
2428
2553
|
|
|
2429
2554
|
// ../../packages/execute/src/steps/ai.ts
|
|
2430
|
-
var executeAIStep =
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
}
|
|
2437
|
-
|
|
2438
|
-
|
|
2555
|
+
var executeAIStep = (_a) => __async(void 0, null, function* () {
|
|
2556
|
+
var _b = _a, {
|
|
2557
|
+
controller,
|
|
2558
|
+
step,
|
|
2559
|
+
logger,
|
|
2560
|
+
advanced
|
|
2561
|
+
} = _b, callbacks = __objRest(_b, [
|
|
2562
|
+
"controller",
|
|
2563
|
+
"step",
|
|
2564
|
+
"logger",
|
|
2565
|
+
"advanced"
|
|
2566
|
+
]);
|
|
2567
|
+
var _a2, _b2, _c, _d, _e, _f, _g;
|
|
2568
|
+
(_a2 = callbacks.onStarted) == null ? void 0 : _a2.call(callbacks);
|
|
2439
2569
|
controller.resetHistory();
|
|
2440
|
-
const result = {
|
|
2441
|
-
...step,
|
|
2570
|
+
const result = __spreadProps(__spreadValues({}, step), {
|
|
2442
2571
|
startedAt: /* @__PURE__ */ new Date(),
|
|
2443
2572
|
userAgent: ChromeBrowser.USER_AGENT,
|
|
2444
2573
|
// placeholder values
|
|
2445
2574
|
finishedAt: /* @__PURE__ */ new Date(),
|
|
2446
2575
|
results: [],
|
|
2447
2576
|
status: "SUCCESS" /* SUCCESS */
|
|
2448
|
-
};
|
|
2577
|
+
});
|
|
2449
2578
|
try {
|
|
2450
2579
|
let commandIndex = 0;
|
|
2451
2580
|
let useSavedCommands = step.commands && step.commands.length > 0;
|
|
@@ -2457,8 +2586,8 @@ var executeAIStep = async ({
|
|
|
2457
2586
|
}
|
|
2458
2587
|
let command;
|
|
2459
2588
|
const startedAt = /* @__PURE__ */ new Date();
|
|
2460
|
-
const beforeScreenshotBuffer =
|
|
2461
|
-
const beforeScreenshot =
|
|
2589
|
+
const beforeScreenshotBuffer = yield controller.browser.screenshot();
|
|
2590
|
+
const beforeScreenshot = yield callbacks.onSaveScreenshot(
|
|
2462
2591
|
beforeScreenshotBuffer
|
|
2463
2592
|
);
|
|
2464
2593
|
if (useSavedCommands) {
|
|
@@ -2469,7 +2598,7 @@ var executeAIStep = async ({
|
|
|
2469
2598
|
);
|
|
2470
2599
|
}
|
|
2471
2600
|
} else {
|
|
2472
|
-
command =
|
|
2601
|
+
command = yield controller.promptToCommand(
|
|
2473
2602
|
step.type,
|
|
2474
2603
|
step.text,
|
|
2475
2604
|
advanced.disableAICaching
|
|
@@ -2481,7 +2610,7 @@ var executeAIStep = async ({
|
|
|
2481
2610
|
result.message = command.thoughts;
|
|
2482
2611
|
break;
|
|
2483
2612
|
}
|
|
2484
|
-
(
|
|
2613
|
+
(_b2 = callbacks.onCommandGenerated) == null ? void 0 : _b2.call(callbacks, {
|
|
2485
2614
|
commandIndex,
|
|
2486
2615
|
message: CARD_DISPLAY_NAMES[command.type] || `Unknown command (${command.type})`
|
|
2487
2616
|
});
|
|
@@ -2498,7 +2627,7 @@ var executeAIStep = async ({
|
|
|
2498
2627
|
`Executing command ${commandIndex}: ${serializeCommand(command)}`
|
|
2499
2628
|
);
|
|
2500
2629
|
try {
|
|
2501
|
-
const executionResult =
|
|
2630
|
+
const executionResult = yield controller.executeCommand(
|
|
2502
2631
|
command,
|
|
2503
2632
|
advanced.disableAICaching,
|
|
2504
2633
|
useSavedCommands
|
|
@@ -2509,8 +2638,8 @@ var executeAIStep = async ({
|
|
|
2509
2638
|
message: serializeCommand(command),
|
|
2510
2639
|
command
|
|
2511
2640
|
});
|
|
2512
|
-
const afterScreenshotBuffer =
|
|
2513
|
-
const afterScreenshot =
|
|
2641
|
+
const afterScreenshotBuffer = yield controller.browser.screenshot();
|
|
2642
|
+
const afterScreenshot = yield callbacks.onSaveScreenshot(
|
|
2514
2643
|
afterScreenshotBuffer
|
|
2515
2644
|
);
|
|
2516
2645
|
cmdResult.afterScreenshot = afterScreenshot;
|
|
@@ -2528,7 +2657,7 @@ var executeAIStep = async ({
|
|
|
2528
2657
|
if (command.type === "SUCCESS" /* SUCCESS */) {
|
|
2529
2658
|
result.finishedAt = /* @__PURE__ */ new Date();
|
|
2530
2659
|
result.status = "SUCCESS" /* SUCCESS */;
|
|
2531
|
-
result.message = executionResult.thoughts
|
|
2660
|
+
result.message = (_d = executionResult.thoughts) != null ? _d : "All commands completed.";
|
|
2532
2661
|
break;
|
|
2533
2662
|
}
|
|
2534
2663
|
if (executionResult.succeedImmediately && !useSavedCommands) {
|
|
@@ -2538,15 +2667,14 @@ var executeAIStep = async ({
|
|
|
2538
2667
|
command = {
|
|
2539
2668
|
type: "SUCCESS" /* SUCCESS */
|
|
2540
2669
|
};
|
|
2541
|
-
(
|
|
2670
|
+
(_e = callbacks.onCommandExecuted) == null ? void 0 : _e.call(callbacks, {
|
|
2542
2671
|
commandIndex: commandIndex + 1,
|
|
2543
2672
|
message: serializeCommand(command),
|
|
2544
2673
|
command
|
|
2545
2674
|
});
|
|
2546
|
-
result.results.push({
|
|
2547
|
-
...presetActionResult,
|
|
2675
|
+
result.results.push(__spreadProps(__spreadValues({}, presetActionResult), {
|
|
2548
2676
|
command
|
|
2549
|
-
});
|
|
2677
|
+
}));
|
|
2550
2678
|
break;
|
|
2551
2679
|
}
|
|
2552
2680
|
} catch (err) {
|
|
@@ -2583,54 +2711,57 @@ var executeAIStep = async ({
|
|
|
2583
2711
|
result.status = "FAILED" /* FAILED */;
|
|
2584
2712
|
}
|
|
2585
2713
|
if (result.status === "SUCCESS" /* SUCCESS */) {
|
|
2586
|
-
(
|
|
2714
|
+
(_f = callbacks.onSuccess) == null ? void 0 : _f.call(callbacks, {
|
|
2587
2715
|
message: result.message || "AI step succeeded.",
|
|
2588
2716
|
startedAt: result.startedAt.getTime(),
|
|
2589
2717
|
durationMs: result.finishedAt.getTime() - result.startedAt.getTime()
|
|
2590
2718
|
});
|
|
2591
2719
|
} else {
|
|
2592
|
-
(
|
|
2720
|
+
(_g = callbacks.onFailure) == null ? void 0 : _g.call(callbacks, {
|
|
2593
2721
|
message: result.message || "AI step errored.",
|
|
2594
2722
|
startedAt: result.startedAt.getTime(),
|
|
2595
2723
|
durationMs: result.finishedAt.getTime() - result.startedAt.getTime()
|
|
2596
2724
|
});
|
|
2597
2725
|
}
|
|
2598
2726
|
return result;
|
|
2599
|
-
};
|
|
2727
|
+
});
|
|
2600
2728
|
|
|
2601
2729
|
// ../../packages/execute/src/steps/preset.ts
|
|
2602
|
-
var executePresetStep =
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
}
|
|
2608
|
-
|
|
2609
|
-
|
|
2730
|
+
var executePresetStep = (_a) => __async(void 0, null, function* () {
|
|
2731
|
+
var _b = _a, {
|
|
2732
|
+
controller,
|
|
2733
|
+
step,
|
|
2734
|
+
advanced
|
|
2735
|
+
} = _b, callbacks = __objRest(_b, [
|
|
2736
|
+
"controller",
|
|
2737
|
+
"step",
|
|
2738
|
+
"advanced"
|
|
2739
|
+
]);
|
|
2740
|
+
var _a2, _b2, _c;
|
|
2741
|
+
(_a2 = callbacks.onStarted) == null ? void 0 : _a2.call(callbacks);
|
|
2610
2742
|
const startedAt = /* @__PURE__ */ new Date();
|
|
2611
2743
|
const beforeUrl = controller.browser.url;
|
|
2612
|
-
const beforeScreenshotBuffer =
|
|
2613
|
-
const beforeScreenshot =
|
|
2744
|
+
const beforeScreenshotBuffer = yield controller.browser.screenshot();
|
|
2745
|
+
const beforeScreenshot = yield callbacks.onSaveScreenshot(
|
|
2614
2746
|
beforeScreenshotBuffer
|
|
2615
2747
|
);
|
|
2616
2748
|
try {
|
|
2617
|
-
const execResult =
|
|
2749
|
+
const execResult = yield controller.executePresetStep(
|
|
2618
2750
|
step.command,
|
|
2619
2751
|
advanced.disableAICaching
|
|
2620
2752
|
);
|
|
2621
|
-
const afterScreenshotBuffer =
|
|
2622
|
-
const afterScreenshot =
|
|
2753
|
+
const afterScreenshotBuffer = yield controller.browser.screenshot();
|
|
2754
|
+
const afterScreenshot = yield callbacks.onSaveScreenshot(
|
|
2623
2755
|
afterScreenshotBuffer
|
|
2624
2756
|
);
|
|
2625
2757
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
2626
|
-
const result = {
|
|
2627
|
-
...step,
|
|
2758
|
+
const result = __spreadProps(__spreadValues({}, step), {
|
|
2628
2759
|
startedAt,
|
|
2629
2760
|
finishedAt,
|
|
2630
2761
|
// placeholder values
|
|
2631
2762
|
status: "SUCCESS" /* SUCCESS */,
|
|
2632
2763
|
results: []
|
|
2633
|
-
};
|
|
2764
|
+
});
|
|
2634
2765
|
let message = "Successfully executed preset action.";
|
|
2635
2766
|
if (step.command.type === "AI_ASSERTION" /* AI_ASSERTION */) {
|
|
2636
2767
|
message = execResult.thoughts || "Assertion passed.";
|
|
@@ -2648,7 +2779,7 @@ var executePresetStep = async ({
|
|
|
2648
2779
|
result.status = "SUCCESS" /* SUCCESS */;
|
|
2649
2780
|
result.results = [cmdMetadata];
|
|
2650
2781
|
result.message = message;
|
|
2651
|
-
(
|
|
2782
|
+
(_b2 = callbacks.onSuccess) == null ? void 0 : _b2.call(callbacks, {
|
|
2652
2783
|
message,
|
|
2653
2784
|
startedAt: startedAt.getTime(),
|
|
2654
2785
|
durationMs: finishedAt.getTime() - startedAt.getTime()
|
|
@@ -2656,8 +2787,7 @@ var executePresetStep = async ({
|
|
|
2656
2787
|
return result;
|
|
2657
2788
|
} catch (err) {
|
|
2658
2789
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
2659
|
-
const result = {
|
|
2660
|
-
...step,
|
|
2790
|
+
const result = __spreadProps(__spreadValues({}, step), {
|
|
2661
2791
|
startedAt,
|
|
2662
2792
|
finishedAt,
|
|
2663
2793
|
status: "FAILED" /* FAILED */,
|
|
@@ -2675,7 +2805,7 @@ var executePresetStep = async ({
|
|
|
2675
2805
|
message: `${err}`
|
|
2676
2806
|
}
|
|
2677
2807
|
]
|
|
2678
|
-
};
|
|
2808
|
+
});
|
|
2679
2809
|
(_c = callbacks.onFailure) == null ? void 0 : _c.call(callbacks, {
|
|
2680
2810
|
message: `${err}`,
|
|
2681
2811
|
startedAt: startedAt.getTime(),
|
|
@@ -2683,18 +2813,23 @@ var executePresetStep = async ({
|
|
|
2683
2813
|
});
|
|
2684
2814
|
return result;
|
|
2685
2815
|
}
|
|
2686
|
-
};
|
|
2816
|
+
});
|
|
2687
2817
|
|
|
2688
2818
|
// ../../packages/execute/src/steps/module.ts
|
|
2689
|
-
var executeModuleStep =
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
}
|
|
2696
|
-
|
|
2697
|
-
|
|
2819
|
+
var executeModuleStep = (_a) => __async(void 0, null, function* () {
|
|
2820
|
+
var _b = _a, {
|
|
2821
|
+
controller,
|
|
2822
|
+
step,
|
|
2823
|
+
advanced,
|
|
2824
|
+
logger
|
|
2825
|
+
} = _b, callbacks = __objRest(_b, [
|
|
2826
|
+
"controller",
|
|
2827
|
+
"step",
|
|
2828
|
+
"advanced",
|
|
2829
|
+
"logger"
|
|
2830
|
+
]);
|
|
2831
|
+
var _a2, _b2, _c;
|
|
2832
|
+
(_a2 = callbacks.onStarted) == null ? void 0 : _a2.call(callbacks);
|
|
2698
2833
|
const result = {
|
|
2699
2834
|
type: "MODULE" /* MODULE */,
|
|
2700
2835
|
moduleId: step.moduleId,
|
|
@@ -2711,19 +2846,19 @@ var executeModuleStep = async ({
|
|
|
2711
2846
|
let moduleStepResult;
|
|
2712
2847
|
switch (moduleStep.type) {
|
|
2713
2848
|
case "PRESET_ACTION" /* PRESET_ACTION */:
|
|
2714
|
-
moduleStepResult =
|
|
2849
|
+
moduleStepResult = yield executePresetStep({
|
|
2715
2850
|
controller,
|
|
2716
2851
|
step: moduleStep,
|
|
2717
2852
|
advanced,
|
|
2718
2853
|
logger,
|
|
2719
2854
|
onSaveScreenshot: callbacks.onSaveScreenshot,
|
|
2720
2855
|
onStarted() {
|
|
2721
|
-
var
|
|
2722
|
-
(
|
|
2856
|
+
var _a3;
|
|
2857
|
+
(_a3 = callbacks.onStepStarted) == null ? void 0 : _a3.call(callbacks, { index: i });
|
|
2723
2858
|
},
|
|
2724
2859
|
onSuccess({ message, startedAt, durationMs }) {
|
|
2725
|
-
var
|
|
2726
|
-
(
|
|
2860
|
+
var _a3;
|
|
2861
|
+
(_a3 = callbacks.onStepSuccess) == null ? void 0 : _a3.call(callbacks, {
|
|
2727
2862
|
index: i,
|
|
2728
2863
|
message,
|
|
2729
2864
|
startedAt,
|
|
@@ -2731,8 +2866,8 @@ var executeModuleStep = async ({
|
|
|
2731
2866
|
});
|
|
2732
2867
|
},
|
|
2733
2868
|
onFailure({ message, startedAt, durationMs }) {
|
|
2734
|
-
var
|
|
2735
|
-
(
|
|
2869
|
+
var _a3;
|
|
2870
|
+
(_a3 = callbacks.onStepFailure) == null ? void 0 : _a3.call(callbacks, {
|
|
2736
2871
|
index: i,
|
|
2737
2872
|
message,
|
|
2738
2873
|
startedAt,
|
|
@@ -2742,19 +2877,19 @@ var executeModuleStep = async ({
|
|
|
2742
2877
|
});
|
|
2743
2878
|
break;
|
|
2744
2879
|
case "AI_ACTION" /* AI_ACTION */:
|
|
2745
|
-
moduleStepResult =
|
|
2880
|
+
moduleStepResult = yield executeAIStep({
|
|
2746
2881
|
controller,
|
|
2747
2882
|
step: moduleStep,
|
|
2748
2883
|
advanced,
|
|
2749
2884
|
logger,
|
|
2750
2885
|
onSaveScreenshot: callbacks.onSaveScreenshot,
|
|
2751
2886
|
onStarted() {
|
|
2752
|
-
var
|
|
2753
|
-
(
|
|
2887
|
+
var _a3;
|
|
2888
|
+
(_a3 = callbacks.onStepStarted) == null ? void 0 : _a3.call(callbacks, { index: i });
|
|
2754
2889
|
},
|
|
2755
2890
|
onSuccess({ message, startedAt, durationMs }) {
|
|
2756
|
-
var
|
|
2757
|
-
(
|
|
2891
|
+
var _a3;
|
|
2892
|
+
(_a3 = callbacks.onStepSuccess) == null ? void 0 : _a3.call(callbacks, {
|
|
2758
2893
|
index: i,
|
|
2759
2894
|
message,
|
|
2760
2895
|
startedAt,
|
|
@@ -2762,8 +2897,8 @@ var executeModuleStep = async ({
|
|
|
2762
2897
|
});
|
|
2763
2898
|
},
|
|
2764
2899
|
onFailure({ message, startedAt, durationMs }) {
|
|
2765
|
-
var
|
|
2766
|
-
(
|
|
2900
|
+
var _a3;
|
|
2901
|
+
(_a3 = callbacks.onStepFailure) == null ? void 0 : _a3.call(callbacks, {
|
|
2767
2902
|
index: i,
|
|
2768
2903
|
message,
|
|
2769
2904
|
startedAt,
|
|
@@ -2771,12 +2906,12 @@ var executeModuleStep = async ({
|
|
|
2771
2906
|
});
|
|
2772
2907
|
},
|
|
2773
2908
|
onCommandGenerated({ commandIndex, message }) {
|
|
2774
|
-
var
|
|
2775
|
-
(
|
|
2909
|
+
var _a3;
|
|
2910
|
+
(_a3 = callbacks.onCommandGenerated) == null ? void 0 : _a3.call(callbacks, { index: i, commandIndex, message });
|
|
2776
2911
|
},
|
|
2777
2912
|
onCommandExecuted({ commandIndex, message, command }) {
|
|
2778
|
-
var
|
|
2779
|
-
(
|
|
2913
|
+
var _a3;
|
|
2914
|
+
(_a3 = callbacks.onCommandExecuted) == null ? void 0 : _a3.call(callbacks, {
|
|
2780
2915
|
index: i,
|
|
2781
2916
|
commandIndex,
|
|
2782
2917
|
message,
|
|
@@ -2797,22 +2932,21 @@ var executeModuleStep = async ({
|
|
|
2797
2932
|
result.finishedAt = /* @__PURE__ */ new Date();
|
|
2798
2933
|
for (let j = i + 1; j < step.steps.length; j++) {
|
|
2799
2934
|
const skippedStep = step.steps[j];
|
|
2800
|
-
const skippedResult = {
|
|
2801
|
-
...skippedStep,
|
|
2935
|
+
const skippedResult = __spreadProps(__spreadValues({}, skippedStep), {
|
|
2802
2936
|
status: "CANCELLED" /* CANCELLED */,
|
|
2803
2937
|
startedAt: /* @__PURE__ */ new Date(),
|
|
2804
2938
|
finishedAt: /* @__PURE__ */ new Date(),
|
|
2805
2939
|
userAgent: ChromeBrowser.USER_AGENT,
|
|
2806
2940
|
results: [],
|
|
2807
2941
|
message: "Cancelled due to previous failure."
|
|
2808
|
-
};
|
|
2942
|
+
});
|
|
2809
2943
|
result.results.push(skippedResult);
|
|
2810
2944
|
}
|
|
2811
2945
|
break;
|
|
2812
2946
|
}
|
|
2813
2947
|
}
|
|
2814
2948
|
if (result.status === "SUCCESS" /* SUCCESS */) {
|
|
2815
|
-
(
|
|
2949
|
+
(_b2 = callbacks.onSuccess) == null ? void 0 : _b2.call(callbacks, {
|
|
2816
2950
|
message: "Executed module step.",
|
|
2817
2951
|
startedAt: result.startedAt.getTime(),
|
|
2818
2952
|
durationMs: result.finishedAt.getTime() - result.startedAt.getTime()
|
|
@@ -2825,20 +2959,20 @@ var executeModuleStep = async ({
|
|
|
2825
2959
|
});
|
|
2826
2960
|
}
|
|
2827
2961
|
return result;
|
|
2828
|
-
};
|
|
2962
|
+
});
|
|
2829
2963
|
|
|
2830
2964
|
// ../../packages/execute/src/test.ts
|
|
2831
|
-
var executeTest =
|
|
2965
|
+
var executeTest = (_0) => __async(void 0, [_0], function* ({
|
|
2832
2966
|
test,
|
|
2833
2967
|
runId,
|
|
2834
2968
|
controller,
|
|
2835
2969
|
logger,
|
|
2836
2970
|
onUpdateRun,
|
|
2837
2971
|
onSaveScreenshot
|
|
2838
|
-
})
|
|
2972
|
+
}) {
|
|
2839
2973
|
const advanced = TestAdvancedSettingsSchema.parse(test.advanced);
|
|
2840
2974
|
logger.info(`Starting run ${runId} for test ${test.id}`);
|
|
2841
|
-
|
|
2975
|
+
yield onUpdateRun({
|
|
2842
2976
|
status: "RUNNING",
|
|
2843
2977
|
startedAt: /* @__PURE__ */ new Date()
|
|
2844
2978
|
});
|
|
@@ -2849,7 +2983,7 @@ var executeTest = async ({
|
|
|
2849
2983
|
let result;
|
|
2850
2984
|
switch (step.type) {
|
|
2851
2985
|
case "PRESET_ACTION" /* PRESET_ACTION */:
|
|
2852
|
-
result =
|
|
2986
|
+
result = yield executePresetStep({
|
|
2853
2987
|
controller,
|
|
2854
2988
|
step,
|
|
2855
2989
|
advanced,
|
|
@@ -2858,7 +2992,7 @@ var executeTest = async ({
|
|
|
2858
2992
|
});
|
|
2859
2993
|
break;
|
|
2860
2994
|
case "AI_ACTION" /* AI_ACTION */:
|
|
2861
|
-
result =
|
|
2995
|
+
result = yield executeAIStep({
|
|
2862
2996
|
controller,
|
|
2863
2997
|
step,
|
|
2864
2998
|
advanced,
|
|
@@ -2867,7 +3001,7 @@ var executeTest = async ({
|
|
|
2867
3001
|
});
|
|
2868
3002
|
break;
|
|
2869
3003
|
case "RESOLVED_MODULE":
|
|
2870
|
-
result =
|
|
3004
|
+
result = yield executeModuleStep({
|
|
2871
3005
|
controller,
|
|
2872
3006
|
step,
|
|
2873
3007
|
advanced,
|
|
@@ -2882,7 +3016,7 @@ var executeTest = async ({
|
|
|
2882
3016
|
return assertUnreachable(step);
|
|
2883
3017
|
}
|
|
2884
3018
|
results.push(result);
|
|
2885
|
-
|
|
3019
|
+
yield onUpdateRun({
|
|
2886
3020
|
results
|
|
2887
3021
|
});
|
|
2888
3022
|
if (result.status === "FAILED" /* FAILED */) {
|
|
@@ -2896,28 +3030,26 @@ var executeTest = async ({
|
|
|
2896
3030
|
startedAt: /* @__PURE__ */ new Date(),
|
|
2897
3031
|
userAgent: ChromeBrowser.USER_AGENT,
|
|
2898
3032
|
results: skippedStep.steps.map((s) => {
|
|
2899
|
-
return {
|
|
2900
|
-
...s,
|
|
3033
|
+
return __spreadProps(__spreadValues({}, s), {
|
|
2901
3034
|
status: "CANCELLED" /* CANCELLED */,
|
|
2902
3035
|
startedAt: /* @__PURE__ */ new Date(),
|
|
2903
3036
|
finishedAt: /* @__PURE__ */ new Date(),
|
|
2904
3037
|
userAgent: ChromeBrowser.USER_AGENT,
|
|
2905
3038
|
results: []
|
|
2906
|
-
};
|
|
3039
|
+
});
|
|
2907
3040
|
}),
|
|
2908
3041
|
finishedAt: /* @__PURE__ */ new Date(),
|
|
2909
3042
|
status: "CANCELLED" /* CANCELLED */
|
|
2910
3043
|
};
|
|
2911
3044
|
results.push(skippedResult);
|
|
2912
3045
|
} else {
|
|
2913
|
-
const skippedResult = {
|
|
2914
|
-
...skippedStep,
|
|
3046
|
+
const skippedResult = __spreadProps(__spreadValues({}, skippedStep), {
|
|
2915
3047
|
status: "CANCELLED" /* CANCELLED */,
|
|
2916
3048
|
startedAt: /* @__PURE__ */ new Date(),
|
|
2917
3049
|
finishedAt: /* @__PURE__ */ new Date(),
|
|
2918
3050
|
userAgent: ChromeBrowser.USER_AGENT,
|
|
2919
3051
|
results: []
|
|
2920
|
-
};
|
|
3052
|
+
});
|
|
2921
3053
|
results.push(skippedResult);
|
|
2922
3054
|
}
|
|
2923
3055
|
}
|
|
@@ -2926,14 +3058,14 @@ var executeTest = async ({
|
|
|
2926
3058
|
break;
|
|
2927
3059
|
}
|
|
2928
3060
|
}
|
|
2929
|
-
|
|
3061
|
+
yield onUpdateRun({
|
|
2930
3062
|
status: failed ? "FAILED" : "PASSED",
|
|
2931
3063
|
finishedAt: /* @__PURE__ */ new Date(),
|
|
2932
3064
|
results
|
|
2933
3065
|
});
|
|
2934
|
-
|
|
3066
|
+
yield controller.browser.cleanup();
|
|
2935
3067
|
return failed;
|
|
2936
|
-
};
|
|
3068
|
+
});
|
|
2937
3069
|
|
|
2938
3070
|
// src/logger.ts
|
|
2939
3071
|
var consoleLogger = {
|
|
@@ -2947,52 +3079,66 @@ var consoleLogger = {
|
|
|
2947
3079
|
};
|
|
2948
3080
|
|
|
2949
3081
|
// src/run-test.ts
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
}) {
|
|
2955
|
-
const test = await apiClient.getTest(testId);
|
|
2956
|
-
const browser = await ChromeBrowser.init(test.baseUrl, consoleLogger);
|
|
2957
|
-
const controller = new AgentController({
|
|
2958
|
-
browser,
|
|
3082
|
+
function runTest(_0) {
|
|
3083
|
+
return __async(this, arguments, function* ({
|
|
3084
|
+
testId,
|
|
3085
|
+
apiClient,
|
|
2959
3086
|
generator,
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
return key;
|
|
2978
|
-
},
|
|
2979
|
-
onUpdateRun: async (data) => {
|
|
2980
|
-
await apiClient.updateRun(run.id, data);
|
|
2981
|
-
}
|
|
3087
|
+
newBaseURL
|
|
3088
|
+
}) {
|
|
3089
|
+
const test = yield apiClient.getTest(testId);
|
|
3090
|
+
const originalURL = new URL(test.baseUrl);
|
|
3091
|
+
const newURL = new URL(newBaseURL);
|
|
3092
|
+
originalURL.hostname = newURL.hostname;
|
|
3093
|
+
originalURL.protocol = newURL.protocol;
|
|
3094
|
+
originalURL.port = newURL.port;
|
|
3095
|
+
const browser = yield ChromeBrowser.init(
|
|
3096
|
+
originalURL.toString(),
|
|
3097
|
+
consoleLogger
|
|
3098
|
+
);
|
|
3099
|
+
const controller = new AgentController({
|
|
3100
|
+
browser,
|
|
3101
|
+
generator,
|
|
3102
|
+
config: DEFAULT_CONTROLLER_CONFIG,
|
|
3103
|
+
logger: consoleLogger
|
|
2982
3104
|
});
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
await apiClient.updateRun(run.id, {
|
|
2986
|
-
status: "FAILED",
|
|
2987
|
-
finishedAt: /* @__PURE__ */ new Date()
|
|
3105
|
+
const run = yield apiClient.createRun({
|
|
3106
|
+
testId
|
|
2988
3107
|
});
|
|
2989
|
-
|
|
2990
|
-
|
|
3108
|
+
let failed = true;
|
|
3109
|
+
try {
|
|
3110
|
+
failed = yield executeTest({
|
|
3111
|
+
test,
|
|
3112
|
+
runId: run.id,
|
|
3113
|
+
controller,
|
|
3114
|
+
logger: consoleLogger,
|
|
3115
|
+
onSaveScreenshot: (buffer) => __async(this, null, function* () {
|
|
3116
|
+
const { key } = yield apiClient.uploadScreenshot({
|
|
3117
|
+
screenshot: buffer.toString("base64")
|
|
3118
|
+
});
|
|
3119
|
+
return key;
|
|
3120
|
+
}),
|
|
3121
|
+
onUpdateRun: (data) => __async(this, null, function* () {
|
|
3122
|
+
yield apiClient.updateRun(run.id, data);
|
|
3123
|
+
})
|
|
3124
|
+
});
|
|
3125
|
+
} catch (err) {
|
|
3126
|
+
consoleLogger.error(err);
|
|
3127
|
+
yield apiClient.updateRun(run.id, {
|
|
3128
|
+
status: "FAILED",
|
|
3129
|
+
finishedAt: /* @__PURE__ */ new Date()
|
|
3130
|
+
});
|
|
3131
|
+
}
|
|
3132
|
+
return failed;
|
|
3133
|
+
});
|
|
2991
3134
|
}
|
|
2992
3135
|
|
|
2993
3136
|
// src/cli.ts
|
|
2994
3137
|
var program = new Command4();
|
|
2995
3138
|
program.name("momentic").description("Momentic CLI").version(version);
|
|
3139
|
+
program.command("install-browsers").action(() => __async(void 0, null, function* () {
|
|
3140
|
+
yield installBrowsers();
|
|
3141
|
+
}));
|
|
2996
3142
|
program.command("run-tests").addOption(
|
|
2997
3143
|
new Option(
|
|
2998
3144
|
"--tests <tests...>",
|
|
@@ -3014,10 +3160,10 @@ program.command("run-tests").addOption(
|
|
|
3014
3160
|
).default(60, "one minute")
|
|
3015
3161
|
).addOption(
|
|
3016
3162
|
new Option("--api-key <key>", "API key for authenticating").env("MOMENTIC_API_KEY").makeOptionMandatory(true)
|
|
3017
|
-
).action(
|
|
3163
|
+
).action((options) => __async(void 0, null, function* () {
|
|
3018
3164
|
const { tests, start, waitOn, waitOnTimeout, apiKey } = options;
|
|
3019
|
-
|
|
3020
|
-
|
|
3165
|
+
yield execCommand(start, false);
|
|
3166
|
+
yield waitOnFn({
|
|
3021
3167
|
resources: [waitOn],
|
|
3022
3168
|
timeout: waitOnTimeout * 1e3
|
|
3023
3169
|
});
|
|
@@ -3029,15 +3175,16 @@ program.command("run-tests").addOption(
|
|
|
3029
3175
|
baseURL: "https://api.momentic.ai",
|
|
3030
3176
|
apiKey
|
|
3031
3177
|
});
|
|
3032
|
-
const promises = tests.map(
|
|
3033
|
-
const failed =
|
|
3178
|
+
const promises = tests.map((testId) => __async(void 0, null, function* () {
|
|
3179
|
+
const failed = yield runTest({
|
|
3034
3180
|
testId,
|
|
3035
3181
|
apiClient,
|
|
3036
|
-
generator: apiGenerator
|
|
3182
|
+
generator: apiGenerator,
|
|
3183
|
+
newBaseURL: waitOn
|
|
3037
3184
|
});
|
|
3038
3185
|
return { failed, testId };
|
|
3039
|
-
});
|
|
3040
|
-
const results =
|
|
3186
|
+
}));
|
|
3187
|
+
const results = yield Promise.all(promises);
|
|
3041
3188
|
const failedResults = results.filter((result) => result.failed);
|
|
3042
3189
|
if (failedResults.length > 0) {
|
|
3043
3190
|
console.log(
|
|
@@ -3051,17 +3198,25 @@ program.command("run-tests").addOption(
|
|
|
3051
3198
|
process.exit(1);
|
|
3052
3199
|
}
|
|
3053
3200
|
console.log(chalk.green(`All ${results.length} tests passed!`));
|
|
3054
|
-
});
|
|
3055
|
-
var execCommand =
|
|
3201
|
+
}));
|
|
3202
|
+
var execCommand = (fullCommand, waitToFinish = true) => __async(void 0, null, function* () {
|
|
3056
3203
|
const args = parseArgsStringToArgv2(fullCommand);
|
|
3057
|
-
const toolPath =
|
|
3204
|
+
const toolPath = yield io.which(args[0], true);
|
|
3058
3205
|
const toolArguments = args.slice(1);
|
|
3059
3206
|
const promise = exec.exec(quote(toolPath), toolArguments);
|
|
3060
3207
|
if (waitToFinish) {
|
|
3061
3208
|
return promise;
|
|
3062
3209
|
}
|
|
3063
|
-
};
|
|
3064
|
-
|
|
3065
|
-
|
|
3210
|
+
});
|
|
3211
|
+
function installBrowsers() {
|
|
3212
|
+
return __async(this, null, function* () {
|
|
3213
|
+
const executables = registry.defaultExecutables();
|
|
3214
|
+
yield registry.install(executables, false);
|
|
3215
|
+
});
|
|
3216
|
+
}
|
|
3217
|
+
function main() {
|
|
3218
|
+
return __async(this, null, function* () {
|
|
3219
|
+
yield program.parseAsync(process.argv);
|
|
3220
|
+
});
|
|
3066
3221
|
}
|
|
3067
3222
|
void main();
|