momentic 0.0.17 → 0.0.19
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 +582 -3327
- package/dist/index.js +573 -2475
- package/package.json +10 -2
package/bin/cli.js
CHANGED
|
@@ -1,3331 +1,586 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
var __defProps = Object.defineProperties;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
10
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
11
|
-
var __spreadValues = (a, b) => {
|
|
12
|
-
for (var prop in b || (b = {}))
|
|
13
|
-
if (__hasOwnProp.call(b, prop))
|
|
14
|
-
__defNormalProp(a, prop, b[prop]);
|
|
15
|
-
if (__getOwnPropSymbols)
|
|
16
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
17
|
-
if (__propIsEnum.call(b, prop))
|
|
18
|
-
__defNormalProp(a, prop, b[prop]);
|
|
19
|
-
}
|
|
20
|
-
return a;
|
|
21
|
-
};
|
|
22
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
23
|
-
var __objRest = (source, exclude) => {
|
|
24
|
-
var target = {};
|
|
25
|
-
for (var prop in source)
|
|
26
|
-
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
27
|
-
target[prop] = source[prop];
|
|
28
|
-
if (source != null && __getOwnPropSymbols)
|
|
29
|
-
for (var prop of __getOwnPropSymbols(source)) {
|
|
30
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
31
|
-
target[prop] = source[prop];
|
|
32
|
-
}
|
|
33
|
-
return target;
|
|
34
|
-
};
|
|
35
|
-
var __export = (target, all) => {
|
|
36
|
-
for (var name in all)
|
|
37
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
38
|
-
};
|
|
39
|
-
var __copyProps = (to, from, except, desc) => {
|
|
40
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
41
|
-
for (let key of __getOwnPropNames(from))
|
|
42
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
43
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
44
|
-
}
|
|
45
|
-
return to;
|
|
46
|
-
};
|
|
47
|
-
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
48
|
-
var __async = (__this, __arguments, generator) => {
|
|
49
|
-
return new Promise((resolve, reject) => {
|
|
50
|
-
var fulfilled = (value) => {
|
|
51
|
-
try {
|
|
52
|
-
step(generator.next(value));
|
|
53
|
-
} catch (e) {
|
|
54
|
-
reject(e);
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
var rejected = (value) => {
|
|
58
|
-
try {
|
|
59
|
-
step(generator.throw(value));
|
|
60
|
-
} catch (e) {
|
|
61
|
-
reject(e);
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
65
|
-
step((generator = generator.apply(__this, __arguments)).next());
|
|
66
|
-
});
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// src/cli.ts
|
|
70
|
-
import { Command as Command4, Option } from "commander";
|
|
71
|
-
|
|
72
|
-
// package.json
|
|
73
|
-
var version = "1.0.0";
|
|
74
|
-
|
|
75
|
-
// src/install-browsers.ts
|
|
76
|
-
import { registry } from "playwright-core/lib/server";
|
|
77
|
-
function installBrowsers() {
|
|
78
|
-
return __async(this, null, function* () {
|
|
79
|
-
const executables = registry.defaultExecutables();
|
|
80
|
-
yield registry.install(executables, false);
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// src/run-tests-locally.ts
|
|
85
|
-
import exec from "@actions/exec";
|
|
86
|
-
import io from "@actions/io";
|
|
87
|
-
import chalk from "chalk";
|
|
88
|
-
import quote from "quote";
|
|
89
|
-
import parseArgsStringToArgv2 from "string-argv";
|
|
90
|
-
import waitOnFn from "wait-on";
|
|
91
|
-
|
|
92
|
-
// ../../packages/types/src/commands.ts
|
|
93
|
-
import dedent from "dedent";
|
|
94
|
-
import * as z2 from "zod";
|
|
95
|
-
|
|
96
|
-
// ../../packages/types/src/a11y-targets.ts
|
|
97
|
-
import * as z from "zod";
|
|
98
|
-
var A11yTargetWithCacheSchema = z.object({
|
|
99
|
-
// a11y ID
|
|
100
|
-
id: z.number().int(),
|
|
101
|
-
// additional metadata stored after the action is executed
|
|
102
|
-
// to assist in re-execution
|
|
103
|
-
role: z.string().optional(),
|
|
104
|
-
name: z.string().optional(),
|
|
105
|
-
content: z.string().optional(),
|
|
106
|
-
pathFromRoot: z.string().optional(),
|
|
107
|
-
serializedForm: z.string().optional()
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// ../../packages/types/src/commands.ts
|
|
111
|
-
var CommandType = /* @__PURE__ */ ((CommandType2) => {
|
|
112
|
-
CommandType2["AI_ASSERTION"] = "AI_ASSERTION";
|
|
113
|
-
CommandType2["CLICK"] = "CLICK";
|
|
114
|
-
CommandType2["SELECT_OPTION"] = "SELECT_OPTION";
|
|
115
|
-
CommandType2["TYPE"] = "TYPE";
|
|
116
|
-
CommandType2["PRESS"] = "PRESS";
|
|
117
|
-
CommandType2["NAVIGATE"] = "NAVIGATE";
|
|
118
|
-
CommandType2["SCROLL_UP"] = "SCROLL_UP";
|
|
119
|
-
CommandType2["SCROLL_DOWN"] = "SCROLL_DOWN";
|
|
120
|
-
CommandType2["GO_BACK"] = "GO_BACK";
|
|
121
|
-
CommandType2["GO_FORWARD"] = "GO_FORWARD";
|
|
122
|
-
CommandType2["WAIT"] = "WAIT";
|
|
123
|
-
CommandType2["REFRESH"] = "REFRESH";
|
|
124
|
-
CommandType2["TAB"] = "TAB";
|
|
125
|
-
CommandType2["COOKIE"] = "COOKIE";
|
|
126
|
-
CommandType2["SUCCESS"] = "SUCCESS";
|
|
127
|
-
return CommandType2;
|
|
128
|
-
})(CommandType || {});
|
|
129
|
-
var ElementDescriptorSchema = z2.object({
|
|
130
|
-
// natural language passed to LLM
|
|
131
|
-
elementDescriptor: z2.string(),
|
|
132
|
-
// Cached A11y target - when a user creates a preset action, this will not exist
|
|
133
|
-
a11yData: A11yTargetWithCacheSchema.optional()
|
|
134
|
-
});
|
|
135
|
-
var CommonCommandSchema = z2.object({
|
|
136
|
-
// If the command is suggested by AI, why it did so
|
|
137
|
-
thoughts: z2.string().optional()
|
|
138
|
-
});
|
|
139
|
-
var NavigateCommandSchema = CommonCommandSchema.merge(
|
|
140
|
-
z2.object({
|
|
141
|
-
type: z2.literal("NAVIGATE" /* NAVIGATE */),
|
|
142
|
-
url: z2.string()
|
|
143
|
-
})
|
|
144
|
-
).describe("NAVIGATE <url> - Go to the specified url");
|
|
145
|
-
var ScrollUpCommandSchema = CommonCommandSchema.merge(
|
|
146
|
-
z2.object({
|
|
147
|
-
type: z2.literal("SCROLL_UP" /* SCROLL_UP */)
|
|
148
|
-
})
|
|
149
|
-
).describe("SCROLL_UP - Scroll up one page");
|
|
150
|
-
var ScrollDownCommandSchema = CommonCommandSchema.merge(
|
|
151
|
-
z2.object({
|
|
152
|
-
type: z2.literal("SCROLL_DOWN" /* SCROLL_DOWN */)
|
|
153
|
-
})
|
|
154
|
-
).describe("SCROLL_DOWN - Scroll down one page");
|
|
155
|
-
var WaitCommandSchema = CommonCommandSchema.merge(
|
|
156
|
-
z2.object({
|
|
157
|
-
type: z2.literal("WAIT" /* WAIT */),
|
|
158
|
-
delay: z2.number()
|
|
159
|
-
// seconds
|
|
160
|
-
})
|
|
161
|
-
);
|
|
162
|
-
var RefreshCommandSchema = CommonCommandSchema.merge(
|
|
163
|
-
z2.object({
|
|
164
|
-
type: z2.literal("REFRESH" /* REFRESH */)
|
|
165
|
-
})
|
|
166
|
-
);
|
|
167
|
-
var GoBackCommandSchema = CommonCommandSchema.merge(
|
|
168
|
-
z2.object({
|
|
169
|
-
type: z2.literal("GO_BACK" /* GO_BACK */)
|
|
170
|
-
})
|
|
171
|
-
);
|
|
172
|
-
var GoForwardCommandSchema = CommonCommandSchema.merge(
|
|
173
|
-
z2.object({
|
|
174
|
-
type: z2.literal("GO_FORWARD" /* GO_FORWARD */)
|
|
175
|
-
})
|
|
176
|
-
);
|
|
177
|
-
var ClickCommandSchema = CommonCommandSchema.merge(
|
|
178
|
-
z2.object({
|
|
179
|
-
type: z2.literal("CLICK" /* CLICK */),
|
|
180
|
-
target: ElementDescriptorSchema,
|
|
181
|
-
doubleClick: z2.boolean().default(false),
|
|
182
|
-
rightClick: z2.boolean().default(false)
|
|
183
|
-
})
|
|
184
|
-
).describe(
|
|
185
|
-
dedent`CLICK <id> - click on the element that has the specified id.
|
|
2
|
+
var un=Object.defineProperty,mn=Object.defineProperties;var pn=Object.getOwnPropertyDescriptors;var Re=Object.getOwnPropertySymbols;var _t=Object.prototype.hasOwnProperty,zt=Object.prototype.propertyIsEnumerable;var Pt=(n,e,t)=>e in n?un(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,C=(n,e)=>{for(var t in e||(e={}))_t.call(e,t)&&Pt(n,t,e[t]);if(Re)for(var t of Re(e))zt.call(e,t)&&Pt(n,t,e[t]);return n},O=(n,e)=>mn(n,pn(e));var ne=(n,e)=>{var t={};for(var o in n)_t.call(n,o)&&e.indexOf(o)<0&&(t[o]=n[o]);if(n!=null&&Re)for(var o of Re(n))e.indexOf(o)<0&&zt.call(n,o)&&(t[o]=n[o]);return t};var l=(n,e,t)=>new Promise((o,r)=>{var s=c=>{try{i(t.next(c))}catch(d){r(d)}},a=c=>{try{i(t.throw(c))}catch(d){r(d)}},i=c=>c.done?o(c.value):Promise.resolve(c.value).then(s,a);i((t=t.apply(n,e)).next())});import{Command as si,Option as te}from"commander";import ai from"dedent";import{existsSync as li}from"fs";import*as k from"zod";var Ie=k.object({id:k.number().int(),role:k.string().optional(),name:k.string().optional(),numChildren:k.number().optional(),content:k.string().optional(),pathFromRoot:k.string().optional(),serializedForm:k.string().optional()});function Le(n){return n.name||n.role||n.content||n.serializedForm}import{z as me}from"zod";var Ut=me.object({thoughts:me.string(),result:me.boolean(),relevantElements:me.array(me.number()).optional()});import Ti from"string-argv";import{z as ge}from"zod";import hn from"dedent";import*as f from"zod";var B=(u=>(u.AI_ASSERTION="AI_ASSERTION",u.CLICK="CLICK",u.SELECT_OPTION="SELECT_OPTION",u.TYPE="TYPE",u.PRESS="PRESS",u.NAVIGATE="NAVIGATE",u.SCROLL_UP="SCROLL_UP",u.SCROLL_DOWN="SCROLL_DOWN",u.GO_BACK="GO_BACK",u.GO_FORWARD="GO_FORWARD",u.WAIT="WAIT",u.REFRESH="REFRESH",u.TAB="TAB",u.COOKIE="COOKIE",u.HOVER="HOVER",u.CAPTCHA="CAPTCHA",u.SUCCESS="SUCCESS",u))(B||{}),re=f.object({elementDescriptor:f.string(),a11yData:Ie.optional()}),D=f.object({thoughts:f.string().optional()}),gn=D.merge(f.object({type:f.literal("NAVIGATE"),url:f.string()})).describe("NAVIGATE <URL> - Go to the specified URL. Only navigate to URLs relevant to the user goal."),fn=D.merge(f.object({target:re.optional(),type:f.literal("SCROLL_UP"),useVision:f.boolean().default(!1)})).describe("SCROLL_UP [id] - Scroll up while hovering over the element with the specified id. If no id is provided, scroll the entire page."),yn=D.merge(f.object({target:re.optional(),type:f.literal("SCROLL_DOWN"),useVision:f.boolean().default(!1)})).describe("SCROLL_DOWN [id] - Scroll down while hovering over the element with the specified id. If no id is provided, scroll the entire page."),Sn=D.merge(f.object({type:f.literal("WAIT"),delay:f.number()})),wn=D.merge(f.object({type:f.literal("REFRESH")})),bn=D.merge(f.object({type:f.literal("GO_BACK")})),An=D.merge(f.object({type:f.literal("GO_FORWARD")})),En=D.merge(f.object({type:f.literal("CAPTCHA"),useVision:f.boolean().default(!1)})),Tn=D.merge(f.object({type:f.literal("CLICK"),target:re,doubleClick:f.boolean().default(!1),rightClick:f.boolean().default(!1),useVision:f.boolean().default(!1)})).describe(hn`CLICK <id> - click on the element that has the specified id.
|
|
186
3
|
You are NOT allowed to click on disabled, hidden or StaticText elements.
|
|
187
4
|
Only click on elements on the Current Page.
|
|
188
5
|
Only click on elements with the following tag names: button, input, link, image, generic.
|
|
189
|
-
`.
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
);
|
|
201
|
-
var AIAssertionCommandSchema = CommonCommandSchema.merge(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
]);
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
goal: true,
|
|
763
|
-
|
|
764
|
-
}).
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
testIds: z15.string().array()
|
|
768
|
-
});
|
|
769
|
-
|
|
770
|
-
var CreateRunBodySchema = z15.object({
|
|
771
|
-
testId: z15.string(),
|
|
772
|
-
trigger: z15.nativeEnum(RunTriggerEnum)
|
|
773
|
-
});
|
|
774
|
-
var CreateRunResponseSchema = RunWithTestSchema;
|
|
775
|
-
var GetRunResponseSchema = RunWithTestSchema;
|
|
776
|
-
var UpdateRunBodySchema = z15.object({
|
|
777
|
-
startedAt: z15.coerce.date(),
|
|
778
|
-
finishedAt: z15.coerce.date(),
|
|
779
|
-
results: ResultSchema.array(),
|
|
780
|
-
status: z15.nativeEnum(RunStatusEnum)
|
|
781
|
-
}).partial();
|
|
782
|
-
var CreateScreenshotBodySchema = z15.object({
|
|
783
|
-
// base64 string
|
|
784
|
-
screenshot: z15.string()
|
|
785
|
-
});
|
|
786
|
-
var CreateScreenshotResponseSchema = z15.object({
|
|
787
|
-
key: z15.string()
|
|
788
|
-
});
|
|
789
|
-
|
|
790
|
-
// ../../packages/web-agent/src/utils/url.ts
|
|
791
|
-
var urlChanged = (url1, url2) => {
|
|
792
|
-
const { hostname, pathname } = new URL(url1);
|
|
793
|
-
const { hostname: hostname2, pathname: pathname2 } = new URL(url2);
|
|
794
|
-
return hostname !== hostname2 || pathname !== pathname2;
|
|
795
|
-
};
|
|
796
|
-
|
|
797
|
-
// ../../packages/web-agent/src/browsers/a11y.ts
|
|
798
|
-
var bannedProperties = /* @__PURE__ */ new Set(["focusable"]);
|
|
799
|
-
var alwaysInterestingRoles = /* @__PURE__ */ new Set([
|
|
800
|
-
"textbox",
|
|
801
|
-
"checkbox",
|
|
802
|
-
"button",
|
|
803
|
-
"link"
|
|
804
|
-
]);
|
|
805
|
-
var rolesToOmitID = /* @__PURE__ */ new Set(["paragraph", "menuitem", "option"]);
|
|
806
|
-
var defaultA11yNodeSerializeParams = {
|
|
807
|
-
indentLevel: 0,
|
|
808
|
-
noID: false,
|
|
809
|
-
noChildren: false,
|
|
810
|
-
noProperties: false
|
|
811
|
-
};
|
|
812
|
-
var ProcessedA11yNode = class {
|
|
813
|
-
constructor(params) {
|
|
814
|
-
this.id = params.id;
|
|
815
|
-
this.role = params.role;
|
|
816
|
-
this.name = params.name;
|
|
817
|
-
this.content = params.content;
|
|
818
|
-
this.properties = params.properties;
|
|
819
|
-
this.pathFromRoot = params.pathFromRoot;
|
|
820
|
-
this.children = params.children;
|
|
821
|
-
this.backendNodeID = params.backendNodeID;
|
|
822
|
-
}
|
|
823
|
-
getLogForm() {
|
|
824
|
-
var _a, _b;
|
|
825
|
-
return JSON.stringify({
|
|
826
|
-
id: this.id,
|
|
827
|
-
name: (_a = this.name) != null ? _a : "",
|
|
828
|
-
role: (_b = this.role) != null ? _b : "",
|
|
829
|
-
backendNodeId: this.backendNodeID
|
|
830
|
-
});
|
|
831
|
-
}
|
|
832
|
-
/**
|
|
833
|
-
* Returns true if the current node contains interesting properties.
|
|
834
|
-
* Does not go through children.
|
|
835
|
-
*/
|
|
836
|
-
isInteresting() {
|
|
837
|
-
if (alwaysInterestingRoles.has(this.role))
|
|
838
|
-
return true;
|
|
839
|
-
if (this.children.some((child) => child.role === "StaticText"))
|
|
840
|
-
return true;
|
|
841
|
-
return !!this.name.trim() || !!this.content;
|
|
842
|
-
}
|
|
843
|
-
serialize(opts = defaultA11yNodeSerializeParams) {
|
|
844
|
-
const { indentLevel, noChildren, noProperties, noID } = Object.assign(
|
|
845
|
-
{},
|
|
846
|
-
defaultA11yNodeSerializeParams,
|
|
847
|
-
opts
|
|
848
|
-
);
|
|
849
|
-
const indent = " ".repeat(indentLevel);
|
|
850
|
-
if (this.role === "StaticText") {
|
|
851
|
-
return `${indent}${this.name}
|
|
852
|
-
`;
|
|
853
|
-
}
|
|
854
|
-
let s = `${indent}<${this.role}`;
|
|
855
|
-
if (!noID && !rolesToOmitID.has(this.role)) {
|
|
856
|
-
s += ` id="${this.id}"`;
|
|
857
|
-
}
|
|
858
|
-
if (this.name) {
|
|
859
|
-
s += ` name="${this.name}"`;
|
|
860
|
-
}
|
|
861
|
-
if (this.content) {
|
|
862
|
-
s += ` content="${this.content}"`;
|
|
863
|
-
}
|
|
864
|
-
if (Object.keys(this.properties).length > 0 && !noProperties) {
|
|
865
|
-
Object.entries(this.properties).forEach(([k, v]) => {
|
|
866
|
-
if (bannedProperties.has(k)) {
|
|
867
|
-
return;
|
|
868
|
-
} else if (typeof v === "string") {
|
|
869
|
-
s += ` ${k}="${v}"`;
|
|
870
|
-
} else if (typeof v === "boolean") {
|
|
871
|
-
if (v) {
|
|
872
|
-
s += ` ${k}`;
|
|
873
|
-
} else {
|
|
874
|
-
s += ` ${k}={false}`;
|
|
875
|
-
}
|
|
876
|
-
} else if (typeof v !== "undefined") {
|
|
877
|
-
s += ` ${k}={${JSON.stringify(v)}}`;
|
|
878
|
-
}
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
|
-
if (this.children.length === 0 || noChildren) {
|
|
882
|
-
s += " />\n";
|
|
883
|
-
return s;
|
|
884
|
-
} else {
|
|
885
|
-
s += ">\n";
|
|
886
|
-
}
|
|
887
|
-
for (const child of this.children) {
|
|
888
|
-
s += child.serialize({ indentLevel: indentLevel + 2 });
|
|
889
|
-
}
|
|
890
|
-
s += `${indent}</${this.role}>
|
|
891
|
-
`;
|
|
892
|
-
return s;
|
|
893
|
-
}
|
|
894
|
-
};
|
|
895
|
-
var ProcessedA11yTree = class {
|
|
896
|
-
constructor(root, nodeMap) {
|
|
897
|
-
this.root = root;
|
|
898
|
-
this.nodeMap = nodeMap;
|
|
899
|
-
}
|
|
900
|
-
serialize() {
|
|
901
|
-
if (!this.root) {
|
|
902
|
-
return "";
|
|
903
|
-
}
|
|
904
|
-
return this.root.serialize();
|
|
905
|
-
}
|
|
906
|
-
// public diff(other: ProcessedA11yTree): string[] {
|
|
907
|
-
// const results: string[] = [];
|
|
908
|
-
// }
|
|
909
|
-
};
|
|
910
|
-
function getNodePathIdentifier(node) {
|
|
911
|
-
var _a, _b;
|
|
912
|
-
if ((_a = node.name) == null ? void 0 : _a.value) {
|
|
913
|
-
return `"${node.name.value}"`;
|
|
914
|
-
}
|
|
915
|
-
if (((_b = node.role) == null ? void 0 : _b.value) && node.role.value !== "none" && node.role.value !== "generic") {
|
|
916
|
-
return `"${node.role.value}"`;
|
|
917
|
-
}
|
|
918
|
-
return `"${node.nodeId}"`;
|
|
919
|
-
}
|
|
920
|
-
function processA11yTreeDFS(node, parent, inputNodeMap, outputNodeMap) {
|
|
921
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
922
|
-
if (!parent && node.parentId) {
|
|
923
|
-
throw new Error(
|
|
924
|
-
`Got no parent for accessibility node ${node.nodeId}: ${JSON.stringify(
|
|
925
|
-
node
|
|
926
|
-
)}`
|
|
927
|
-
);
|
|
928
|
-
}
|
|
929
|
-
const processedNode = new ProcessedA11yNode({
|
|
930
|
-
id: node.nodeId,
|
|
931
|
-
role: ((_a = node.role) == null ? void 0 : _a.value) || "",
|
|
932
|
-
name: ((_b = node.name) == null ? void 0 : _b.value) || "",
|
|
933
|
-
content: ((_c = node.value) == null ? void 0 : _c.value) || "",
|
|
934
|
-
properties: {},
|
|
935
|
-
children: [],
|
|
936
|
-
pathFromRoot: (parent ? `${parent.pathFromRoot} ` : "") + getNodePathIdentifier(node),
|
|
937
|
-
backendNodeID: node.backendDOMNodeId
|
|
938
|
-
// md5Sum: "",
|
|
939
|
-
});
|
|
940
|
-
if ((_d = node.value) == null ? void 0 : _d.value) {
|
|
941
|
-
processedNode.content = `${(_e = node.value) == null ? void 0 : _e.value}`;
|
|
942
|
-
}
|
|
943
|
-
if (node.properties) {
|
|
944
|
-
node.properties.forEach((prop) => {
|
|
945
|
-
processedNode.properties[prop.name] = prop.value.value;
|
|
946
|
-
});
|
|
947
|
-
}
|
|
948
|
-
outputNodeMap.set(processedNode.id, processedNode);
|
|
949
|
-
const children = (_f = node.childIds) != null ? _f : [];
|
|
950
|
-
for (const childId of children) {
|
|
951
|
-
if (!childId) {
|
|
952
|
-
continue;
|
|
953
|
-
}
|
|
954
|
-
const child = inputNodeMap.get(childId);
|
|
955
|
-
if (!child) {
|
|
956
|
-
continue;
|
|
957
|
-
}
|
|
958
|
-
const processedChildren = processA11yTreeDFS(
|
|
959
|
-
child,
|
|
960
|
-
processedNode,
|
|
961
|
-
inputNodeMap,
|
|
962
|
-
outputNodeMap
|
|
963
|
-
);
|
|
964
|
-
if (!processedChildren.length) {
|
|
965
|
-
continue;
|
|
966
|
-
}
|
|
967
|
-
processedNode.children = processedNode.children.concat(processedChildren);
|
|
968
|
-
}
|
|
969
|
-
if (processedNode.role === "StaticText") {
|
|
970
|
-
processedNode.children = [];
|
|
971
|
-
}
|
|
972
|
-
if (processedNode.children.length === 1 && processedNode.children[0].role === "StaticText") {
|
|
973
|
-
const currentName = processedNode.name;
|
|
974
|
-
const childName = (_g = processedNode.children[0]) == null ? void 0 : _g.name;
|
|
975
|
-
if (currentName === childName || !childName) {
|
|
976
|
-
processedNode.children = [];
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
const staticTextGroupedChildren = [];
|
|
980
|
-
for (let i = processedNode.children.length - 1; i >= 0; i--) {
|
|
981
|
-
const node2 = processedNode.children[i];
|
|
982
|
-
if (node2.role !== "StaticText") {
|
|
983
|
-
staticTextGroupedChildren.push(node2);
|
|
984
|
-
continue;
|
|
985
|
-
}
|
|
986
|
-
if (i === 0 || processedNode.children[i - 1].role !== "StaticText") {
|
|
987
|
-
staticTextGroupedChildren.push(node2);
|
|
988
|
-
continue;
|
|
989
|
-
}
|
|
990
|
-
processedNode.children[i - 1].name += ` ${node2.name}`;
|
|
991
|
-
}
|
|
992
|
-
processedNode.children = staticTextGroupedChildren.reverse();
|
|
993
|
-
for (const child of processedNode.children) {
|
|
994
|
-
child.parent = processedNode;
|
|
995
|
-
}
|
|
996
|
-
const interesting = processedNode.isInteresting();
|
|
997
|
-
if (!interesting) {
|
|
998
|
-
if (processedNode.children.length === 0) {
|
|
999
|
-
return [];
|
|
1000
|
-
} else if (processedNode.children.length === 1) {
|
|
1001
|
-
return [processedNode.children[0]];
|
|
1002
|
-
} else if (node.parentId) {
|
|
1003
|
-
return processedNode.children;
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
return [processedNode];
|
|
1007
|
-
}
|
|
1008
|
-
function processA11yTree(graph) {
|
|
1009
|
-
if (!graph.root) {
|
|
1010
|
-
throw new Error("a11y tree has null root");
|
|
1011
|
-
}
|
|
1012
|
-
graph.allNodes = graph.allNodes.filter((node) => {
|
|
1013
|
-
var _a;
|
|
1014
|
-
if (!node.ignored) {
|
|
1015
|
-
return true;
|
|
1016
|
-
}
|
|
1017
|
-
return !((_a = node.ignoredReasons) == null ? void 0 : _a.find(
|
|
1018
|
-
(reason) => {
|
|
1019
|
-
var _a2;
|
|
1020
|
-
return reason.name === "notRendered" && ((_a2 = reason.value) == null ? void 0 : _a2.value);
|
|
1021
|
-
}
|
|
1022
|
-
));
|
|
1023
|
-
});
|
|
1024
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
1025
|
-
for (const node of graph.allNodes) {
|
|
1026
|
-
nodeMap.set(node.nodeId, node);
|
|
1027
|
-
}
|
|
1028
|
-
const outputNodeMap = /* @__PURE__ */ new Map();
|
|
1029
|
-
const processedRoot = processA11yTreeDFS(
|
|
1030
|
-
graph.root,
|
|
1031
|
-
null,
|
|
1032
|
-
nodeMap,
|
|
1033
|
-
outputNodeMap
|
|
1034
|
-
);
|
|
1035
|
-
if (processedRoot.length > 1) {
|
|
1036
|
-
throw new Error(
|
|
1037
|
-
`Something went horribly wrong processing the a11y tree, we got: ${JSON.stringify(
|
|
1038
|
-
processedRoot
|
|
1039
|
-
)}`
|
|
1040
|
-
);
|
|
1041
|
-
} else if (processedRoot.length === 0) {
|
|
1042
|
-
throw new EmptyA11yTreeError();
|
|
1043
|
-
}
|
|
1044
|
-
return new ProcessedA11yTree(processedRoot[0], outputNodeMap);
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
// ../../packages/web-agent/src/browsers/cdp.ts
|
|
1048
|
-
var GREEN = { r: 147, g: 196, b: 125, a: 0.55 };
|
|
1049
|
-
var NODE_HIGHLIGHT_CONFIG = {
|
|
1050
|
-
showInfo: false,
|
|
1051
|
-
showRulers: false,
|
|
1052
|
-
showStyles: false,
|
|
1053
|
-
showAccessibilityInfo: false,
|
|
1054
|
-
showExtensionLines: false,
|
|
1055
|
-
contrastAlgorithm: "aa",
|
|
1056
|
-
contentColor: GREEN,
|
|
1057
|
-
paddingColor: GREEN,
|
|
1058
|
-
borderColor: GREEN,
|
|
1059
|
-
marginColor: GREEN,
|
|
1060
|
-
eventTargetColor: GREEN,
|
|
1061
|
-
shapeColor: GREEN,
|
|
1062
|
-
shapeMarginColor: GREEN
|
|
1063
|
-
};
|
|
1064
|
-
|
|
1065
|
-
// ../../packages/web-agent/src/browsers/constants.ts
|
|
1066
|
-
var RETINA_WINDOW_SCALE_FACTOR = 2;
|
|
1067
|
-
var MAX_LOAD_TIMEOUT_MS = 8e3;
|
|
1068
|
-
var NETWORK_STABLE_DURATION_MS = 1250;
|
|
1069
|
-
var NETWORK_IDLE_TIMEOUT_MS = 3e3;
|
|
1070
|
-
var CHECK_INTERVAL_MS = 250;
|
|
1071
|
-
var A11Y_LOAD_TIMEOUT_MS = 1e3;
|
|
1072
|
-
var A11Y_STABLE_TIMEOUT_MS = NETWORK_IDLE_TIMEOUT_MS;
|
|
1073
|
-
var A11Y_STABLE_DURATION_MS = NETWORK_STABLE_DURATION_MS;
|
|
1074
|
-
var BROWSER_ACTION_TIMEOUT_MS = MAX_LOAD_TIMEOUT_MS;
|
|
1075
|
-
var COMPLICATED_BROWSER_ACTION_TIMEOUT_MS = MAX_LOAD_TIMEOUT_MS;
|
|
1076
|
-
var HIGHLIGHT_DURATION_MS = 3e3;
|
|
1077
|
-
var CHROME_INTERNAL_URLS = /* @__PURE__ */ new Set([
|
|
1078
|
-
"about:blank",
|
|
1079
|
-
"chrome-error://chromewebdata/"
|
|
1080
|
-
]);
|
|
1081
|
-
var MAX_BROWSER_ACTION_ATTEMPTS = 2;
|
|
1082
|
-
|
|
1083
|
-
// ../../packages/web-agent/src/browsers/utils/time.ts
|
|
1084
|
-
var sleep = (ms = 1e3) => {
|
|
1085
|
-
return new Promise((resolve) => setTimeout(() => resolve(), ms));
|
|
1086
|
-
};
|
|
1087
|
-
|
|
1088
|
-
// ../../packages/web-agent/src/browsers/utils/scripts/cursor.ts
|
|
1089
|
-
function addCursorScript() {
|
|
1090
|
-
cursor = document.createElement("img");
|
|
1091
|
-
cursor.setAttribute(
|
|
1092
|
-
"src",
|
|
1093
|
-
"data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjMyIiB2aWV3Qm94PSIwIDAgMzIgMzIiIHdpZHRoPSIzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwIDcpIj48cGF0aCBkPSJtNi4xNDggMTguNDczIDEuODYzLTEuMDAzIDEuNjE1LS44MzktMi41NjgtNC44MTZoNC4zMzJsLTExLjM3OS0xMS40MDh2MTYuMDE1bDMuMzE2LTMuMjIxeiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Im02LjQzMSAxNyAxLjc2NS0uOTQxLTIuNzc1LTUuMjAyaDMuNjA0bC04LjAyNS04LjA0M3YxMS4xODhsMi41My0yLjQ0MnoiIGZpbGw9IiMwMDAiLz48L2c+PC9zdmc+"
|
|
1094
|
-
);
|
|
1095
|
-
cursor.setAttribute("id", "selenium_cursor");
|
|
1096
|
-
cursor.setAttribute(
|
|
1097
|
-
"style",
|
|
1098
|
-
"position: absolute; z-index: 99999999999; pointer-events: none; left:0; top:0"
|
|
1099
|
-
);
|
|
1100
|
-
cursor.style.filter = "invert(0%) sepia(6%) saturate(24%) hue-rotate(315deg) brightness(89%) contrast(110%)";
|
|
1101
|
-
document.body.appendChild(cursor);
|
|
1102
|
-
document.onmousemove = function(e) {
|
|
1103
|
-
e = e || window.event;
|
|
1104
|
-
document.getElementById("selenium_cursor").style.left = e.pageX + "px";
|
|
1105
|
-
document.getElementById("selenium_cursor").style.top = e.pageY + "px";
|
|
1106
|
-
};
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
// ../../packages/web-agent/src/browsers/utils/scripts/addIDs.ts
|
|
1110
|
-
function addIDsScript() {
|
|
1111
|
-
const allElements = document.getElementsByTagName("*");
|
|
1112
|
-
let currentID = 1;
|
|
1113
|
-
for (let i = 0; i < allElements.length; i++) {
|
|
1114
|
-
const element = allElements[i];
|
|
1115
|
-
element == null ? void 0 : element.setAttribute("data-momentic-id", currentID);
|
|
1116
|
-
currentID++;
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
// ../../packages/web-agent/src/browsers/utils/playwright.ts
|
|
1121
|
-
var sometimesRelevantResourceTypes = /* @__PURE__ */ new Set([
|
|
1122
|
-
"document",
|
|
1123
|
-
"script",
|
|
1124
|
-
"XMLHttpRequest",
|
|
1125
|
-
"fetch",
|
|
1126
|
-
"xhr"
|
|
1127
|
-
]);
|
|
1128
|
-
var alwaysRelevantResourceTypes = /* @__PURE__ */ new Set(["script", "document"]);
|
|
1129
|
-
var bannedDomains = [
|
|
1130
|
-
"intercom.io",
|
|
1131
|
-
"googletagmanager.com",
|
|
1132
|
-
"google-analytics.com",
|
|
1133
|
-
"www.gstatic.com",
|
|
1134
|
-
"apis.google.com",
|
|
1135
|
-
"sentry.io",
|
|
1136
|
-
"newrelic.com",
|
|
1137
|
-
"p.retool.com",
|
|
1138
|
-
"m.stripe.com",
|
|
1139
|
-
"m.stripe.network",
|
|
1140
|
-
"js.stripe.com",
|
|
1141
|
-
"assets.trybento.co",
|
|
1142
|
-
"udon.trybento.co",
|
|
1143
|
-
"cdn.lr-in-prod.com",
|
|
1144
|
-
"r.lr-in-prod.com",
|
|
1145
|
-
"content.product-usage.assembledhq.com",
|
|
1146
|
-
"data.product-usage.assembledhq.com",
|
|
1147
|
-
"static.zdassets.com"
|
|
1148
|
-
];
|
|
1149
|
-
function serializeRequest(request) {
|
|
1150
|
-
return `${request.resourceType()} ${request.method()} ${request.url()}`;
|
|
1151
|
-
}
|
|
1152
|
-
function stripWWWPrefix(url) {
|
|
1153
|
-
url = url.replace(/^www\./, "");
|
|
1154
|
-
return url;
|
|
1155
|
-
}
|
|
1156
|
-
function isRequestRelevantForPageLoad(request, currentURL) {
|
|
1157
|
-
if (!sometimesRelevantResourceTypes.has(request.resourceType())) {
|
|
1158
|
-
return false;
|
|
1159
|
-
}
|
|
1160
|
-
const parsedCurrentURL = new URL(currentURL);
|
|
1161
|
-
const parsedRequestURL = new URL(request.url());
|
|
1162
|
-
if (bannedDomains.some((domain) => parsedRequestURL.hostname.includes(domain))) {
|
|
1163
|
-
return false;
|
|
1164
|
-
}
|
|
1165
|
-
if (alwaysRelevantResourceTypes.has(request.resourceType())) {
|
|
1166
|
-
return true;
|
|
1167
|
-
}
|
|
1168
|
-
if (request.method() !== "GET") {
|
|
1169
|
-
return true;
|
|
1170
|
-
}
|
|
1171
|
-
return stripWWWPrefix(parsedRequestURL.hostname).includes(
|
|
1172
|
-
stripWWWPrefix(parsedCurrentURL.hostname)
|
|
1173
|
-
);
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
// ../../packages/web-agent/src/browsers/chrome.ts
|
|
1177
|
-
function initCDPSession(cdpClient) {
|
|
1178
|
-
return __async(this, null, function* () {
|
|
1179
|
-
yield cdpClient.send("Accessibility.enable");
|
|
1180
|
-
yield cdpClient.send("DOM.enable");
|
|
1181
|
-
yield cdpClient.send("Overlay.enable");
|
|
1182
|
-
});
|
|
1183
|
-
}
|
|
1184
|
-
var _ChromeBrowser = class _ChromeBrowser {
|
|
1185
|
-
constructor({
|
|
1186
|
-
browser,
|
|
1187
|
-
context,
|
|
1188
|
-
page,
|
|
1189
|
-
baseURL,
|
|
1190
|
-
cdpClient,
|
|
1191
|
-
logger
|
|
1192
|
-
}) {
|
|
1193
|
-
// key is nodeId, according to the a11y tree
|
|
1194
|
-
this.nodeMap = /* @__PURE__ */ new Map();
|
|
1195
|
-
this.browser = browser;
|
|
1196
|
-
this.context = context;
|
|
1197
|
-
this.page = page;
|
|
1198
|
-
this.baseURL = baseURL;
|
|
1199
|
-
this.cdpClient = cdpClient;
|
|
1200
|
-
this.logger = logger;
|
|
1201
|
-
}
|
|
1202
|
-
/**
|
|
1203
|
-
* Creates a new browser and waits for navigation to the given test URL.
|
|
1204
|
-
*/
|
|
1205
|
-
static init(_0, _1, _2) {
|
|
1206
|
-
return __async(this, arguments, function* (baseURL, logger, onScreenshot, timeout = MAX_LOAD_TIMEOUT_MS) {
|
|
1207
|
-
const browser = yield playwright_exports.chromium.launch({ headless: true });
|
|
1208
|
-
const context = yield browser.newContext({
|
|
1209
|
-
viewport: {
|
|
1210
|
-
width: 1920,
|
|
1211
|
-
height: 1080
|
|
1212
|
-
},
|
|
1213
|
-
// comment out the below if you are on Mac OS but you're using a monitor
|
|
1214
|
-
deviceScaleFactor: process.platform === "darwin" ? RETINA_WINDOW_SCALE_FACTOR : 1,
|
|
1215
|
-
userAgent: playwright_exports.devices["Desktop Chrome"].userAgent,
|
|
1216
|
-
geolocation: { latitude: 37.7749, longitude: -122.4194 },
|
|
1217
|
-
// san francisco
|
|
1218
|
-
locale: "en-US",
|
|
1219
|
-
timezoneId: "America/Los_Angeles"
|
|
1220
|
-
});
|
|
1221
|
-
const page = yield context.newPage();
|
|
1222
|
-
const cdpClient = yield context.newCDPSession(page);
|
|
1223
|
-
const chrome = new _ChromeBrowser({
|
|
1224
|
-
browser,
|
|
1225
|
-
context,
|
|
1226
|
-
page,
|
|
1227
|
-
baseURL,
|
|
1228
|
-
cdpClient,
|
|
1229
|
-
logger
|
|
1230
|
-
});
|
|
1231
|
-
let completed = false;
|
|
1232
|
-
const navigateAndInitCDP = () => __async(this, null, function* () {
|
|
1233
|
-
try {
|
|
1234
|
-
yield chrome.navigate(baseURL, false);
|
|
1235
|
-
yield initCDPSession(cdpClient);
|
|
1236
|
-
} catch (err) {
|
|
1237
|
-
logger.error({ err }, "Failed to initialize chrome browser");
|
|
1238
|
-
} finally {
|
|
1239
|
-
completed = true;
|
|
1240
|
-
}
|
|
1241
|
-
});
|
|
1242
|
-
void navigateAndInitCDP();
|
|
1243
|
-
const sendScreenshot = () => __async(this, null, function* () {
|
|
1244
|
-
if (!onScreenshot) {
|
|
1245
|
-
return;
|
|
1246
|
-
}
|
|
1247
|
-
try {
|
|
1248
|
-
onScreenshot({
|
|
1249
|
-
viewport: chrome.viewport,
|
|
1250
|
-
buffer: yield chrome.screenshot()
|
|
1251
|
-
});
|
|
1252
|
-
} catch (err) {
|
|
1253
|
-
logger.error({ err }, "Failed to take screenshot");
|
|
1254
|
-
}
|
|
1255
|
-
});
|
|
1256
|
-
void sendScreenshot();
|
|
1257
|
-
const screenshotInterval = setInterval(() => {
|
|
1258
|
-
void sendScreenshot();
|
|
1259
|
-
}, 250);
|
|
1260
|
-
const startTime = Date.now();
|
|
1261
|
-
while (!completed && Date.now() - startTime < timeout) {
|
|
1262
|
-
yield sleep(CHECK_INTERVAL_MS);
|
|
1263
|
-
}
|
|
1264
|
-
clearInterval(screenshotInterval);
|
|
1265
|
-
if (!completed) {
|
|
1266
|
-
logger.warn(
|
|
1267
|
-
"Timeout elapsed waiting for browser to initialize - are you sure this page is accessible?"
|
|
1268
|
-
);
|
|
1269
|
-
}
|
|
1270
|
-
return chrome;
|
|
1271
|
-
});
|
|
1272
|
-
}
|
|
1273
|
-
// Things to do on every page load
|
|
1274
|
-
pageSetup() {
|
|
1275
|
-
return __async(this, null, function* () {
|
|
1276
|
-
yield this.page.evaluate(addCursorScript);
|
|
1277
|
-
yield this.page.evaluate(addIDsScript);
|
|
1278
|
-
});
|
|
1279
|
-
}
|
|
1280
|
-
wait(timeoutMs) {
|
|
1281
|
-
return __async(this, null, function* () {
|
|
1282
|
-
yield this.page.waitForTimeout(timeoutMs);
|
|
1283
|
-
});
|
|
1284
|
-
}
|
|
1285
|
-
cleanup() {
|
|
1286
|
-
return __async(this, null, function* () {
|
|
1287
|
-
yield this.page.close();
|
|
1288
|
-
yield this.context.close();
|
|
1289
|
-
yield this.browser.close();
|
|
1290
|
-
});
|
|
1291
|
-
}
|
|
1292
|
-
get closed() {
|
|
1293
|
-
return this.page.isClosed() || !this.browser.isConnected();
|
|
1294
|
-
}
|
|
1295
|
-
html() {
|
|
1296
|
-
return __async(this, null, function* () {
|
|
1297
|
-
return yield this.page.content();
|
|
1298
|
-
});
|
|
1299
|
-
}
|
|
1300
|
-
get url() {
|
|
1301
|
-
return this.page.url();
|
|
1302
|
-
}
|
|
1303
|
-
screenshot(quality = 100, scale = "device") {
|
|
1304
|
-
return __async(this, null, function* () {
|
|
1305
|
-
return yield this.page.screenshot({
|
|
1306
|
-
fullPage: false,
|
|
1307
|
-
quality,
|
|
1308
|
-
scale,
|
|
1309
|
-
type: "jpeg",
|
|
1310
|
-
// allow the blinking text cursor thing to remain there
|
|
1311
|
-
caret: "initial"
|
|
1312
|
-
});
|
|
1313
|
-
});
|
|
1314
|
-
}
|
|
1315
|
-
get viewport() {
|
|
1316
|
-
const viewport = this.page.viewportSize();
|
|
1317
|
-
if (!viewport) {
|
|
1318
|
-
throw new Error("failed to get viewport");
|
|
1319
|
-
}
|
|
1320
|
-
return viewport;
|
|
1321
|
-
}
|
|
1322
|
-
navigate(url, wrapPossibleNavigation = true) {
|
|
1323
|
-
return __async(this, null, function* () {
|
|
1324
|
-
this.logger.debug(`Navigating to ${url}`);
|
|
1325
|
-
const startTime = Date.now();
|
|
1326
|
-
const doNav = () => __async(this, null, function* () {
|
|
1327
|
-
try {
|
|
1328
|
-
yield this.page.goto(url, {
|
|
1329
|
-
timeout: MAX_LOAD_TIMEOUT_MS
|
|
1330
|
-
});
|
|
1331
|
-
this.logger.debug(
|
|
1332
|
-
{ url },
|
|
1333
|
-
`Got load event in ${Math.floor(Date.now() - startTime)}ms`
|
|
1334
|
-
);
|
|
1335
|
-
} catch (e) {
|
|
1336
|
-
this.logger.warn(
|
|
1337
|
-
{ url, type: "navigate", err: e },
|
|
1338
|
-
"Timeout elapsed waiting for page to load, continuing anyways..."
|
|
1339
|
-
);
|
|
1340
|
-
}
|
|
1341
|
-
});
|
|
1342
|
-
if (wrapPossibleNavigation) {
|
|
1343
|
-
yield this.wrapPossibleNavigation(doNav);
|
|
1344
|
-
} else {
|
|
1345
|
-
yield doNav();
|
|
1346
|
-
}
|
|
1347
|
-
if (CHROME_INTERNAL_URLS.has(this.url) && process.env.NODE_ENV === "production") {
|
|
1348
|
-
throw new Error(
|
|
1349
|
-
`${url} took too long to load \u{1F61E}. Please ensure the site and your internet are working.`
|
|
1350
|
-
);
|
|
1351
|
-
}
|
|
1352
|
-
yield this.pageSetup();
|
|
1353
|
-
this.logger.debug({ url }, "Navigation complete");
|
|
1354
|
-
});
|
|
1355
|
-
}
|
|
1356
|
-
fill(_0, _1) {
|
|
1357
|
-
return __async(this, arguments, function* (target, text, options = {}) {
|
|
1358
|
-
const element = yield this.click(target, {
|
|
1359
|
-
doubleClick: false,
|
|
1360
|
-
rightClick: false
|
|
1361
|
-
});
|
|
1362
|
-
yield this.type(text, options);
|
|
1363
|
-
return element;
|
|
1364
|
-
});
|
|
1365
|
-
}
|
|
1366
|
-
type(_0) {
|
|
1367
|
-
return __async(this, arguments, function* (text, options = {}) {
|
|
1368
|
-
const { clearContent = true, pressKeysSequentially = false } = options;
|
|
1369
|
-
if (clearContent) {
|
|
1370
|
-
yield this.page.keyboard.press("Meta+A");
|
|
1371
|
-
yield this.page.keyboard.press("Backspace");
|
|
1372
|
-
}
|
|
1373
|
-
if (pressKeysSequentially) {
|
|
1374
|
-
yield this.page.keyboard.type(text);
|
|
1375
|
-
} else {
|
|
1376
|
-
yield this.page.keyboard.insertText(text);
|
|
1377
|
-
}
|
|
1378
|
-
});
|
|
1379
|
-
}
|
|
1380
|
-
clickByA11yID(_0) {
|
|
1381
|
-
return __async(this, arguments, function* (index, options = {}) {
|
|
1382
|
-
const node = this.nodeMap.get(`${index}`);
|
|
1383
|
-
if (!node) {
|
|
1384
|
-
throw new Error(`Could not find node in DOM with index: ${index}`);
|
|
1385
|
-
}
|
|
1386
|
-
const nodeClicked = yield this.clickUsingCDP(node, options);
|
|
1387
|
-
yield this.highlightNode(nodeClicked);
|
|
1388
|
-
return node.serialize({ noChildren: true, noProperties: true, noID: true });
|
|
1389
|
-
});
|
|
1390
|
-
}
|
|
1391
|
-
selectOptionByA11yID(index, option) {
|
|
1392
|
-
return __async(this, null, function* () {
|
|
1393
|
-
const node = this.nodeMap.get(`${index}`);
|
|
1394
|
-
if (!node) {
|
|
1395
|
-
throw new Error(`Could not find node in DOM with index: ${index}`);
|
|
1396
|
-
}
|
|
1397
|
-
if (!node.backendNodeID) {
|
|
1398
|
-
throw new Error(
|
|
1399
|
-
`Select target missing backend node id: ${node.getLogForm()}`
|
|
1400
|
-
);
|
|
1401
|
-
}
|
|
1402
|
-
const locator = yield this.getLocatorFromBackendID(node.backendNodeID);
|
|
1403
|
-
yield locator.selectOption(option, {
|
|
1404
|
-
timeout: COMPLICATED_BROWSER_ACTION_TIMEOUT_MS
|
|
1405
|
-
});
|
|
1406
|
-
yield this.highlightNode(node);
|
|
1407
|
-
return node.serialize({ noChildren: true, noProperties: true, noID: true });
|
|
1408
|
-
});
|
|
1409
|
-
}
|
|
1410
|
-
highlight(target) {
|
|
1411
|
-
return __async(this, null, function* () {
|
|
1412
|
-
try {
|
|
1413
|
-
yield this.highlightByA11yID(target.id);
|
|
1414
|
-
} catch (err) {
|
|
1415
|
-
this.logger.warn({ err, target }, "Failed to highlight target");
|
|
1416
|
-
}
|
|
1417
|
-
});
|
|
1418
|
-
}
|
|
1419
|
-
highlightByA11yID(index) {
|
|
1420
|
-
return __async(this, null, function* () {
|
|
1421
|
-
const node = this.nodeMap.get(`${index}`);
|
|
1422
|
-
if (!node) {
|
|
1423
|
-
throw new Error(`Could not find node in DOM with index: ${index}`);
|
|
1424
|
-
}
|
|
1425
|
-
if (!node.backendNodeID) {
|
|
1426
|
-
throw new Error(
|
|
1427
|
-
`Select target missing backend node id: ${node.getLogForm()}`
|
|
1428
|
-
);
|
|
1429
|
-
}
|
|
1430
|
-
yield this.highlightNode(node);
|
|
1431
|
-
});
|
|
1432
|
-
}
|
|
1433
|
-
highlightNode(node) {
|
|
1434
|
-
return __async(this, null, function* () {
|
|
1435
|
-
try {
|
|
1436
|
-
yield this.cdpClient.send("Overlay.highlightNode", {
|
|
1437
|
-
highlightConfig: NODE_HIGHLIGHT_CONFIG,
|
|
1438
|
-
backendNodeId: node.backendNodeID
|
|
1439
|
-
});
|
|
1440
|
-
} catch (err) {
|
|
1441
|
-
this.logger.warn({ err }, "Failed to add node highlight");
|
|
1442
|
-
}
|
|
1443
|
-
const hideHighlight = () => __async(this, null, function* () {
|
|
1444
|
-
try {
|
|
1445
|
-
yield this.cdpClient.send("Overlay.hideHighlight", {
|
|
1446
|
-
backendNodeId: node.backendNodeID
|
|
1447
|
-
});
|
|
1448
|
-
} catch (err) {
|
|
1449
|
-
this.logger.debug({ err }, "Failed to remove node highlight");
|
|
1450
|
-
}
|
|
1451
|
-
});
|
|
1452
|
-
setTimeout(() => {
|
|
1453
|
-
void hideHighlight();
|
|
1454
|
-
}, HIGHLIGHT_DURATION_MS);
|
|
1455
|
-
});
|
|
1456
|
-
}
|
|
1457
|
-
wrapPossibleNavigation(_0) {
|
|
1458
|
-
return __async(this, arguments, function* (fn, timeoutMS = MAX_LOAD_TIMEOUT_MS) {
|
|
1459
|
-
const startTime = Date.now();
|
|
1460
|
-
const startURL = this.url;
|
|
1461
|
-
let lastRequestReceived = Date.now();
|
|
1462
|
-
const firedRequests = /* @__PURE__ */ new Map();
|
|
1463
|
-
const finishedRequests = /* @__PURE__ */ new Map();
|
|
1464
|
-
const requestFinishedListener = (request) => {
|
|
1465
|
-
var _a;
|
|
1466
|
-
const key = serializeRequest(request);
|
|
1467
|
-
finishedRequests.set(key, ((_a = finishedRequests.get(key)) != null ? _a : 0) + 1);
|
|
1468
|
-
};
|
|
1469
|
-
const requestFiredListener = (request) => {
|
|
1470
|
-
var _a;
|
|
1471
|
-
if (!isRequestRelevantForPageLoad(request, this.url)) {
|
|
1472
|
-
this.logger.debug(
|
|
1473
|
-
{
|
|
1474
|
-
uri: serializeRequest(request)
|
|
1475
|
-
},
|
|
1476
|
-
"Ignoring request for page load network stability"
|
|
1477
|
-
);
|
|
1478
|
-
return;
|
|
1479
|
-
}
|
|
1480
|
-
const key = serializeRequest(request);
|
|
1481
|
-
this.logger.debug(
|
|
1482
|
-
{
|
|
1483
|
-
uri: key
|
|
1484
|
-
},
|
|
1485
|
-
"Request fired on page load, delaying network stability"
|
|
1486
|
-
);
|
|
1487
|
-
firedRequests.set(key, ((_a = firedRequests.get(key)) != null ? _a : 0) + 1);
|
|
1488
|
-
lastRequestReceived = Date.now();
|
|
1489
|
-
};
|
|
1490
|
-
this.page.on("requestfinished", requestFinishedListener);
|
|
1491
|
-
this.page.on("request", requestFiredListener);
|
|
1492
|
-
let rejected = false;
|
|
1493
|
-
const retPromise = fn().catch((e) => {
|
|
1494
|
-
rejected = true;
|
|
1495
|
-
if (e instanceof Error)
|
|
1496
|
-
return e;
|
|
1497
|
-
return new Error(`${e}`);
|
|
1498
|
-
});
|
|
1499
|
-
yield sleep(CHECK_INTERVAL_MS);
|
|
1500
|
-
const unwrapAndThrowError = (p) => __async(this, null, function* () {
|
|
1501
|
-
const v = yield p;
|
|
1502
|
-
if (v instanceof Error) {
|
|
1503
|
-
throw v;
|
|
1504
|
-
}
|
|
1505
|
-
return v;
|
|
1506
|
-
});
|
|
1507
|
-
let unfinishedRequests = /* @__PURE__ */ new Set();
|
|
1508
|
-
const waitForNetworkIdle = () => __async(this, null, function* () {
|
|
1509
|
-
while (!rejected && Date.now() - startTime < timeoutMS) {
|
|
1510
|
-
unfinishedRequests = /* @__PURE__ */ new Set();
|
|
1511
|
-
yield sleep(CHECK_INTERVAL_MS);
|
|
1512
|
-
if (Date.now() - lastRequestReceived <= NETWORK_STABLE_DURATION_MS) {
|
|
1513
|
-
continue;
|
|
1514
|
-
}
|
|
1515
|
-
let anyDifference = false;
|
|
1516
|
-
for (const key of firedRequests.keys()) {
|
|
1517
|
-
if (firedRequests.get(key) !== finishedRequests.get(key)) {
|
|
1518
|
-
this.logger.debug({ uri: key }, "Waiting on request to finish");
|
|
1519
|
-
anyDifference = true;
|
|
1520
|
-
unfinishedRequests.add(key);
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
if (!anyDifference) {
|
|
1524
|
-
this.logger.debug(
|
|
1525
|
-
{
|
|
1526
|
-
url: this.url,
|
|
1527
|
-
requests: JSON.stringify(Array.from(firedRequests.entries()))
|
|
1528
|
-
},
|
|
1529
|
-
`Network idle in ${Math.floor(Date.now() - startTime)}ms`
|
|
1530
|
-
);
|
|
1531
|
-
return true;
|
|
1532
|
-
}
|
|
1533
|
-
}
|
|
1534
|
-
if (!rejected) {
|
|
1535
|
-
this.logger.warn(
|
|
1536
|
-
{
|
|
1537
|
-
url: this.url,
|
|
1538
|
-
requests: JSON.stringify(Array.from(unfinishedRequests.entries()))
|
|
1539
|
-
},
|
|
1540
|
-
"Timeout elapsed waiting for network idle, continuing anyways..."
|
|
1541
|
-
);
|
|
1542
|
-
}
|
|
1543
|
-
return false;
|
|
1544
|
-
});
|
|
1545
|
-
const waitResult = yield waitForNetworkIdle();
|
|
1546
|
-
this.page.off("requestfinished", requestFinishedListener);
|
|
1547
|
-
this.page.off("request", requestFiredListener);
|
|
1548
|
-
if (!waitResult) {
|
|
1549
|
-
return unwrapAndThrowError(retPromise);
|
|
1550
|
-
}
|
|
1551
|
-
if (!rejected && urlChanged(this.url, startURL)) {
|
|
1552
|
-
this.logger.debug(
|
|
1553
|
-
`Detected url change in wrapPossibleNavigation, waiting for load state`
|
|
1554
|
-
);
|
|
1555
|
-
try {
|
|
1556
|
-
yield this.page.waitForLoadState("load", {
|
|
1557
|
-
timeout: timeoutMS - (Date.now() - startTime)
|
|
1558
|
-
});
|
|
1559
|
-
} catch (e) {
|
|
1560
|
-
this.logger.warn(
|
|
1561
|
-
{ url: this.url },
|
|
1562
|
-
"Timeout elapsed waiting for load state to fire, continuing anyways..."
|
|
1563
|
-
);
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
|
-
return unwrapAndThrowError(retPromise);
|
|
1567
|
-
});
|
|
1568
|
-
}
|
|
1569
|
-
click(_0) {
|
|
1570
|
-
return __async(this, arguments, function* (target, options = {}) {
|
|
1571
|
-
const elementInteracted = yield this.wrapPossibleNavigation(
|
|
1572
|
-
() => this.clickByA11yID(target.id, options)
|
|
1573
|
-
);
|
|
1574
|
-
return elementInteracted;
|
|
1575
|
-
});
|
|
1576
|
-
}
|
|
1577
|
-
selectOption(target, option) {
|
|
1578
|
-
return __async(this, null, function* () {
|
|
1579
|
-
return this.selectOptionByA11yID(target.id, option);
|
|
1580
|
-
});
|
|
1581
|
-
}
|
|
1582
|
-
press(key) {
|
|
1583
|
-
return __async(this, null, function* () {
|
|
1584
|
-
yield this.wrapPossibleNavigation(() => this.page.keyboard.press(key));
|
|
1585
|
-
});
|
|
1586
|
-
}
|
|
1587
|
-
refresh() {
|
|
1588
|
-
return __async(this, null, function* () {
|
|
1589
|
-
yield this.page.reload();
|
|
1590
|
-
yield this.pageSetup();
|
|
1591
|
-
});
|
|
1592
|
-
}
|
|
1593
|
-
getA11yTree() {
|
|
1594
|
-
return __async(this, null, function* () {
|
|
1595
|
-
let processedTree = null;
|
|
1596
|
-
let attempt = 0;
|
|
1597
|
-
const url = this.url;
|
|
1598
|
-
while (!processedTree) {
|
|
1599
|
-
try {
|
|
1600
|
-
this.logger.debug(`Getting a11y tree at ${url}`);
|
|
1601
|
-
const graph = yield this.getRawA11yTree();
|
|
1602
|
-
if (!graph.root || graph.allNodes.length === 0) {
|
|
1603
|
-
throw new Error("No a11y tree found on page");
|
|
1604
|
-
}
|
|
1605
|
-
processedTree = processA11yTree(graph);
|
|
1606
|
-
} catch (e) {
|
|
1607
|
-
this.logger.error({ err: e, url }, "Error fetching a11y tree");
|
|
1608
|
-
if (attempt === 0) {
|
|
1609
|
-
yield sleep(1e3);
|
|
1610
|
-
attempt++;
|
|
1611
|
-
} else {
|
|
1612
|
-
throw new Error(`Max retries exceeded fetching a11y tree: ${e}`);
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
if (!processedTree.root) {
|
|
1617
|
-
this.logger.warn("A11y tree was pruned entirely");
|
|
1618
|
-
}
|
|
1619
|
-
this.nodeMap = processedTree.nodeMap;
|
|
1620
|
-
return processedTree;
|
|
1621
|
-
});
|
|
1622
|
-
}
|
|
1623
|
-
getRawA11yTree() {
|
|
1624
|
-
return __async(this, null, function* () {
|
|
1625
|
-
const url = this.page.url();
|
|
1626
|
-
let lastTreeUpdateTimestamp = Date.now();
|
|
1627
|
-
const treeUpdateListener = () => {
|
|
1628
|
-
lastTreeUpdateTimestamp = Date.now();
|
|
1629
|
-
};
|
|
1630
|
-
this.cdpClient.addListener(
|
|
1631
|
-
"Accessibility.nodesUpdated",
|
|
1632
|
-
treeUpdateListener
|
|
1633
|
-
);
|
|
1634
|
-
let accessibilityTreeLoadFired = false;
|
|
1635
|
-
const accessibilityLoadListener = () => {
|
|
1636
|
-
this.logger.info({ url }, `A11y tree load event fired`);
|
|
1637
|
-
accessibilityTreeLoadFired = true;
|
|
1638
|
-
};
|
|
1639
|
-
this.cdpClient.addListener(
|
|
1640
|
-
"Accessibility.loadComplete",
|
|
1641
|
-
accessibilityLoadListener
|
|
1642
|
-
);
|
|
1643
|
-
const a11yLoadStart = Date.now();
|
|
1644
|
-
let timeoutTriggered = true;
|
|
1645
|
-
while (Date.now() - a11yLoadStart < A11Y_STABLE_TIMEOUT_MS) {
|
|
1646
|
-
yield sleep(CHECK_INTERVAL_MS);
|
|
1647
|
-
if (!accessibilityTreeLoadFired && Date.now() - a11yLoadStart < A11Y_LOAD_TIMEOUT_MS) {
|
|
1648
|
-
this.logger.debug({ url }, `A11y tree not loaded yet, waiting...`);
|
|
1649
|
-
continue;
|
|
1650
|
-
}
|
|
1651
|
-
if (Date.now() - lastTreeUpdateTimestamp >= A11Y_STABLE_DURATION_MS) {
|
|
1652
|
-
this.logger.debug({ url }, `A11y tree not stable yet, waiting...`);
|
|
1653
|
-
continue;
|
|
1654
|
-
}
|
|
1655
|
-
timeoutTriggered = false;
|
|
1656
|
-
break;
|
|
1657
|
-
}
|
|
1658
|
-
this.logger.debug(
|
|
1659
|
-
{
|
|
1660
|
-
duration: Date.now() - a11yLoadStart,
|
|
1661
|
-
eventReceived: accessibilityTreeLoadFired,
|
|
1662
|
-
timeoutTriggered
|
|
1663
|
-
},
|
|
1664
|
-
"A11y wait phase completed"
|
|
1665
|
-
);
|
|
1666
|
-
const { node: root } = yield this.cdpClient.send(
|
|
1667
|
-
"Accessibility.getRootAXNode"
|
|
1668
|
-
);
|
|
1669
|
-
const { nodes } = yield this.cdpClient.send("Accessibility.queryAXTree", {
|
|
1670
|
-
backendNodeId: root.backendDOMNodeId
|
|
1671
|
-
});
|
|
1672
|
-
this.cdpClient.removeListener(
|
|
1673
|
-
"Accessibility.loadComplete",
|
|
1674
|
-
accessibilityLoadListener
|
|
1675
|
-
);
|
|
1676
|
-
this.cdpClient.removeListener(
|
|
1677
|
-
"Accessibility.nodesUpdated",
|
|
1678
|
-
treeUpdateListener
|
|
1679
|
-
);
|
|
1680
|
-
return {
|
|
1681
|
-
root,
|
|
1682
|
-
allNodes: nodes
|
|
1683
|
-
};
|
|
1684
|
-
});
|
|
1685
|
-
}
|
|
1686
|
-
clickUsingVisualCoordinates(backendNodeId) {
|
|
1687
|
-
return __async(this, null, function* () {
|
|
1688
|
-
const location = yield this.getElementLocation(backendNodeId);
|
|
1689
|
-
if (!location) {
|
|
1690
|
-
throw new Error(
|
|
1691
|
-
`Could not find element location with backend node id: ${backendNodeId}`
|
|
1692
|
-
);
|
|
1693
|
-
}
|
|
1694
|
-
this.logger.debug({ location }, "Executing mouse click");
|
|
1695
|
-
yield this.page.mouse.click(location.centerX, location.centerY);
|
|
1696
|
-
});
|
|
1697
|
-
}
|
|
1698
|
-
// Get the "id" attribute value from an HTML element.
|
|
1699
|
-
getIDAttributeUsingCDP(objectId) {
|
|
1700
|
-
return __async(this, null, function* () {
|
|
1701
|
-
yield this.cdpClient.send("DOM.getDocument", { depth: 0 });
|
|
1702
|
-
const cdpNodeResult = yield this.cdpClient.send("DOM.requestNode", {
|
|
1703
|
-
objectId
|
|
1704
|
-
});
|
|
1705
|
-
const attrResult = yield this.cdpClient.send("DOM.getAttributes", {
|
|
1706
|
-
nodeId: cdpNodeResult.nodeId
|
|
1707
|
-
});
|
|
1708
|
-
const attributes = attrResult.attributes;
|
|
1709
|
-
const indexAttr = attributes.findIndex((s) => s === "data-momentic-id");
|
|
1710
|
-
if (indexAttr === -1) {
|
|
1711
|
-
return "";
|
|
1712
|
-
}
|
|
1713
|
-
return attributes[indexAttr + 1] || "";
|
|
1714
|
-
});
|
|
1715
|
-
}
|
|
1716
|
-
getLocatorFromBackendID(backendNodeId) {
|
|
1717
|
-
return __async(this, null, function* () {
|
|
1718
|
-
yield this.page.evaluate(addIDsScript);
|
|
1719
|
-
const cdpResolveResult = yield this.cdpClient.send("DOM.resolveNode", {
|
|
1720
|
-
backendNodeId
|
|
1721
|
-
});
|
|
1722
|
-
if (!cdpResolveResult || !cdpResolveResult.object.objectId) {
|
|
1723
|
-
throw new Error(`Could not resolve backend node ${backendNodeId}`);
|
|
1724
|
-
}
|
|
1725
|
-
try {
|
|
1726
|
-
const id = yield this.getIDAttributeUsingCDP(
|
|
1727
|
-
cdpResolveResult.object.objectId
|
|
1728
|
-
);
|
|
1729
|
-
if (!id) {
|
|
1730
|
-
throw new Error("Failed getting data-momentic-id attribute using CDP");
|
|
1731
|
-
}
|
|
1732
|
-
return this.page.locator(`[data-momentic-id="${id}"]`);
|
|
1733
|
-
} catch (err) {
|
|
1734
|
-
this.logger.error(
|
|
1735
|
-
{
|
|
1736
|
-
err
|
|
1737
|
-
},
|
|
1738
|
-
"Failed to get ID attribute"
|
|
1739
|
-
);
|
|
1740
|
-
throw err;
|
|
1741
|
-
}
|
|
1742
|
-
});
|
|
1743
|
-
}
|
|
1744
|
-
clickUsingCDP(_0) {
|
|
1745
|
-
return __async(this, arguments, function* (originalNode, options = {}) {
|
|
1746
|
-
let clickAttempts = 0;
|
|
1747
|
-
let candidateNode = originalNode;
|
|
1748
|
-
while (clickAttempts < MAX_BROWSER_ACTION_ATTEMPTS) {
|
|
1749
|
-
if (!candidateNode || candidateNode.role === "RootWebArea") {
|
|
1750
|
-
throw new Error(
|
|
1751
|
-
`Attempted to click node with no clickable surrounding elements: ${originalNode.getLogForm()}`
|
|
1752
|
-
);
|
|
1753
|
-
}
|
|
1754
|
-
if (candidateNode.role === "StaticText") {
|
|
1755
|
-
candidateNode = candidateNode.parent;
|
|
1756
|
-
continue;
|
|
1757
|
-
}
|
|
1758
|
-
const candidateNodeID = candidateNode.backendNodeID;
|
|
1759
|
-
if (!candidateNodeID) {
|
|
1760
|
-
this.logger.warn(
|
|
1761
|
-
{ node: candidateNode.getLogForm() },
|
|
1762
|
-
"Click candidate had no backend node ID"
|
|
1763
|
-
);
|
|
1764
|
-
candidateNode = candidateNode.parent;
|
|
1765
|
-
continue;
|
|
1766
|
-
}
|
|
1767
|
-
try {
|
|
1768
|
-
const locator = yield this.getLocatorFromBackendID(candidateNodeID);
|
|
1769
|
-
if (options.doubleClick) {
|
|
1770
|
-
yield locator.dblclick({
|
|
1771
|
-
timeout: BROWSER_ACTION_TIMEOUT_MS
|
|
1772
|
-
});
|
|
1773
|
-
} else {
|
|
1774
|
-
yield locator.click({
|
|
1775
|
-
timeout: BROWSER_ACTION_TIMEOUT_MS,
|
|
1776
|
-
button: options.rightClick ? "right" : "left"
|
|
1777
|
-
});
|
|
1778
|
-
}
|
|
1779
|
-
if (candidateNode.id !== originalNode.id) {
|
|
1780
|
-
this.logger.info(
|
|
1781
|
-
{
|
|
1782
|
-
oldNode: originalNode.getLogForm(),
|
|
1783
|
-
newNode: candidateNode.getLogForm()
|
|
1784
|
-
},
|
|
1785
|
-
`Redirected click successfully to new element`
|
|
1786
|
-
);
|
|
1787
|
-
}
|
|
1788
|
-
return candidateNode;
|
|
1789
|
-
} catch (err) {
|
|
1790
|
-
this.logger.error(
|
|
1791
|
-
{ err, node: candidateNode.getLogForm() },
|
|
1792
|
-
"Failed click or click timed out"
|
|
1793
|
-
);
|
|
1794
|
-
clickAttempts++;
|
|
1795
|
-
candidateNode = candidateNode.parent;
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
throw new Error(
|
|
1799
|
-
`Max click redirection attempts exhausted on original element: ${originalNode.getLogForm()}`
|
|
1800
|
-
);
|
|
1801
|
-
});
|
|
1802
|
-
}
|
|
1803
|
-
/**
|
|
1804
|
-
* Currently unused, but could be useful for vision model integration.
|
|
1805
|
-
* Gets x/y position of an a11y node.
|
|
1806
|
-
*/
|
|
1807
|
-
getElementLocation(backendNodeId) {
|
|
1808
|
-
return __async(this, null, function* () {
|
|
1809
|
-
const tree = yield this.cdpClient.send("DOMSnapshot.captureSnapshot", {
|
|
1810
|
-
computedStyles: [],
|
|
1811
|
-
includeDOMRects: true,
|
|
1812
|
-
includePaintOrder: true
|
|
1813
|
-
});
|
|
1814
|
-
let devicePixelRatio = yield this.page.evaluate(
|
|
1815
|
-
() => window.devicePixelRatio
|
|
1816
|
-
);
|
|
1817
|
-
if (process.platform === "darwin" && devicePixelRatio === 1) {
|
|
1818
|
-
devicePixelRatio = RETINA_WINDOW_SCALE_FACTOR;
|
|
1819
|
-
}
|
|
1820
|
-
const document2 = tree["documents"][0];
|
|
1821
|
-
const layout = document2["layout"];
|
|
1822
|
-
const nodes = document2["nodes"];
|
|
1823
|
-
const nodeNames = nodes["nodeName"] || [];
|
|
1824
|
-
const backendNodeIds = nodes["backendNodeId"] || [];
|
|
1825
|
-
const layoutNodeIndex = layout["nodeIndex"];
|
|
1826
|
-
const bounds = layout["bounds"];
|
|
1827
|
-
let cursor2 = -1;
|
|
1828
|
-
for (let i = 0; i < nodeNames.length; i++) {
|
|
1829
|
-
if (backendNodeIds[i] === backendNodeId) {
|
|
1830
|
-
cursor2 = layoutNodeIndex.indexOf(i);
|
|
1831
|
-
break;
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
if (cursor2 === -1) {
|
|
1835
|
-
throw new Error(
|
|
1836
|
-
`Could not find any backend node with ID ${backendNodeId}`
|
|
1837
|
-
);
|
|
1838
|
-
}
|
|
1839
|
-
let [x = 0, y = 0, width = 0, height = 0] = bounds[cursor2];
|
|
1840
|
-
x /= devicePixelRatio;
|
|
1841
|
-
y /= devicePixelRatio;
|
|
1842
|
-
width /= devicePixelRatio;
|
|
1843
|
-
height /= devicePixelRatio;
|
|
1844
|
-
const centerX = x + width / 2;
|
|
1845
|
-
const centerY = y + height / 2;
|
|
1846
|
-
return { centerX, centerY };
|
|
1847
|
-
});
|
|
1848
|
-
}
|
|
1849
|
-
scrollUp() {
|
|
1850
|
-
return __async(this, null, function* () {
|
|
1851
|
-
yield this.page.evaluate(() => {
|
|
1852
|
-
(document.scrollingElement || document.body).scrollTop = (document.scrollingElement || document.body).scrollTop - window.innerHeight;
|
|
1853
|
-
});
|
|
1854
|
-
yield this.page.evaluate(() => {
|
|
1855
|
-
(document.scrollingElement || document.body).scrollTop = (document.scrollingElement || document.body).scrollTop + window.innerHeight;
|
|
1856
|
-
});
|
|
1857
|
-
});
|
|
1858
|
-
}
|
|
1859
|
-
scrollDown() {
|
|
1860
|
-
return __async(this, null, function* () {
|
|
1861
|
-
yield this.page.evaluate(() => {
|
|
1862
|
-
(document.scrollingElement || document.body).scrollTop = (document.scrollingElement || document.body).scrollTop + window.innerHeight;
|
|
1863
|
-
});
|
|
1864
|
-
});
|
|
1865
|
-
}
|
|
1866
|
-
goForward() {
|
|
1867
|
-
return __async(this, null, function* () {
|
|
1868
|
-
yield this.wrapPossibleNavigation(
|
|
1869
|
-
() => this.page.goForward({ timeout: MAX_LOAD_TIMEOUT_MS })
|
|
1870
|
-
);
|
|
1871
|
-
yield this.pageSetup();
|
|
1872
|
-
});
|
|
1873
|
-
}
|
|
1874
|
-
goBack() {
|
|
1875
|
-
return __async(this, null, function* () {
|
|
1876
|
-
yield this.wrapPossibleNavigation(
|
|
1877
|
-
() => this.page.goBack({ timeout: MAX_LOAD_TIMEOUT_MS })
|
|
1878
|
-
);
|
|
1879
|
-
yield this.pageSetup();
|
|
1880
|
-
});
|
|
1881
|
-
}
|
|
1882
|
-
switchToPage(urlSubstring) {
|
|
1883
|
-
return __async(this, null, function* () {
|
|
1884
|
-
const allPages = yield this.context.pages();
|
|
1885
|
-
for (let i = 0; i < allPages.length; i++) {
|
|
1886
|
-
const page = allPages[i];
|
|
1887
|
-
if (page.url().includes(urlSubstring)) {
|
|
1888
|
-
this.page = page;
|
|
1889
|
-
yield page.waitForLoadState("load", {
|
|
1890
|
-
timeout: MAX_LOAD_TIMEOUT_MS
|
|
1891
|
-
});
|
|
1892
|
-
yield this.pageSetup();
|
|
1893
|
-
this.cdpClient = yield this.context.newCDPSession(page);
|
|
1894
|
-
yield initCDPSession(this.cdpClient);
|
|
1895
|
-
this.logger.info(`Switching to tab ${i} with url ${page.url()}`);
|
|
1896
|
-
return;
|
|
1897
|
-
}
|
|
1898
|
-
}
|
|
1899
|
-
throw new Error(`Could not find page with url containing ${urlSubstring}`);
|
|
1900
|
-
});
|
|
1901
|
-
}
|
|
1902
|
-
setCookie(cookie) {
|
|
1903
|
-
return __async(this, null, function* () {
|
|
1904
|
-
const cookieSettings = parseCookieString(cookie);
|
|
1905
|
-
yield this.context.addCookies([cookieSettings]);
|
|
1906
|
-
});
|
|
1907
|
-
}
|
|
1908
|
-
};
|
|
1909
|
-
_ChromeBrowser.USER_AGENT = playwright_exports.devices["Desktop Chrome"].userAgent;
|
|
1910
|
-
var ChromeBrowser = _ChromeBrowser;
|
|
1911
|
-
|
|
1912
|
-
// ../../packages/web-agent/src/configs/controller.ts
|
|
1913
|
-
var A11Y_CONTROLLER_CONFIG = {
|
|
1914
|
-
type: "a11y",
|
|
1915
|
-
version: "1.0.0",
|
|
1916
|
-
useHistory: "diff",
|
|
1917
|
-
useGoalSplitter: true
|
|
1918
|
-
};
|
|
1919
|
-
var DEFAULT_CONTROLLER_CONFIG = A11Y_CONTROLLER_CONFIG;
|
|
1920
|
-
|
|
1921
|
-
// ../../packages/web-agent/src/controller.ts
|
|
1922
|
-
import dedent2 from "dedent";
|
|
1923
|
-
import diffLines from "diff-lines";
|
|
1924
|
-
var MAX_HISTORY_CHAR_LENGTH = 1e4;
|
|
1925
|
-
var AgentController = class {
|
|
1926
|
-
constructor({ browser, config, generator, logger }) {
|
|
1927
|
-
this.browser = browser;
|
|
1928
|
-
this.generator = generator;
|
|
1929
|
-
this.config = config;
|
|
1930
|
-
this.logger = logger;
|
|
1931
|
-
this.pendingInstructions = [];
|
|
1932
|
-
this.commandHistory = [];
|
|
1933
|
-
}
|
|
1934
|
-
/**
|
|
1935
|
-
* Get copy of executed commands in human readable form. Most recent is last.
|
|
1936
|
-
* Only commands that have completed execution are returned.
|
|
1937
|
-
*/
|
|
1938
|
-
get history() {
|
|
1939
|
-
return this.commandHistory.filter((cmd) => cmd.state === "DONE");
|
|
1940
|
-
}
|
|
1941
|
-
get lastExecutedCommand() {
|
|
1942
|
-
const history = this.history;
|
|
1943
|
-
if (history.length === 0)
|
|
1944
|
-
return null;
|
|
1945
|
-
const lastEntry = history[history.length - 1];
|
|
1946
|
-
return lastEntry;
|
|
1947
|
-
}
|
|
1948
|
-
/**
|
|
1949
|
-
* Reset the command history provided to agents.
|
|
1950
|
-
* Should be called due to a logical break between commands
|
|
1951
|
-
* such as a SUCCESS being issued.
|
|
1952
|
-
*/
|
|
1953
|
-
resetHistory() {
|
|
1954
|
-
this.commandHistory = [];
|
|
1955
|
-
this.pendingInstructions = [];
|
|
1956
|
-
}
|
|
1957
|
-
/**
|
|
1958
|
-
* Reset controller and browser state.
|
|
1959
|
-
*/
|
|
1960
|
-
resetState() {
|
|
1961
|
-
return __async(this, null, function* () {
|
|
1962
|
-
this.resetHistory();
|
|
1963
|
-
yield this.browser.navigate(this.browser.baseURL);
|
|
1964
|
-
});
|
|
1965
|
-
}
|
|
1966
|
-
/**
|
|
1967
|
-
* Get the browser state as a string
|
|
1968
|
-
*/
|
|
1969
|
-
getBrowserState() {
|
|
1970
|
-
return __async(this, null, function* () {
|
|
1971
|
-
const a11yTree = yield this.browser.getA11yTree();
|
|
1972
|
-
return a11yTree.serialize();
|
|
1973
|
-
});
|
|
1974
|
-
}
|
|
1975
|
-
getSerializedHistory(url, currentBrowserState) {
|
|
1976
|
-
let history;
|
|
1977
|
-
if (this.config.useHistory === "diff") {
|
|
1978
|
-
history = this.getDiffHistory(url, currentBrowserState);
|
|
1979
|
-
} else {
|
|
1980
|
-
history = this.getListHistory();
|
|
1981
|
-
}
|
|
1982
|
-
return history;
|
|
1983
|
-
}
|
|
1984
|
-
splitUserGoal(type, goal, disableCache) {
|
|
1985
|
-
return __async(this, null, function* () {
|
|
1986
|
-
if (type === "AI_ACTION" /* AI_ACTION */ && goal.match(/[,!;.]|(?:and)|(?:then)/) && this.config.useGoalSplitter) {
|
|
1987
|
-
const granularInstructions = yield this.generator.getGranularGoals(
|
|
1988
|
-
{ goal, url: this.browser.url },
|
|
1989
|
-
disableCache
|
|
1990
|
-
);
|
|
1991
|
-
this.pendingInstructions = granularInstructions.reverse();
|
|
1992
|
-
} else {
|
|
1993
|
-
this.pendingInstructions = [goal];
|
|
1994
|
-
}
|
|
1995
|
-
});
|
|
1996
|
-
}
|
|
1997
|
-
/**
|
|
1998
|
-
* Given previously executed commands, generate command for the current prompt.
|
|
1999
|
-
* Should only be used for AI action.
|
|
2000
|
-
*/
|
|
2001
|
-
promptToCommand(type, goal, disableCache) {
|
|
2002
|
-
return __async(this, null, function* () {
|
|
2003
|
-
if (this.pendingInstructions.length === 0) {
|
|
2004
|
-
yield this.splitUserGoal(type, goal, disableCache);
|
|
2005
|
-
}
|
|
2006
|
-
const currInstruction = this.pendingInstructions[this.pendingInstructions.length - 1];
|
|
2007
|
-
this.logger.info({ goal: currInstruction }, "Starting prompt translation");
|
|
2008
|
-
const getBrowserStateStart = Date.now();
|
|
2009
|
-
const url = this.browser.url;
|
|
2010
|
-
const browserState = yield this.getBrowserState();
|
|
2011
|
-
this.logger.info(
|
|
2012
|
-
{
|
|
2013
|
-
duration: Date.now() - getBrowserStateStart,
|
|
2014
|
-
url
|
|
2015
|
-
},
|
|
2016
|
-
"Got browser state"
|
|
2017
|
-
);
|
|
2018
|
-
const numPrevious = this.commandHistory.length;
|
|
2019
|
-
this.commandHistory.push({
|
|
2020
|
-
state: "PENDING",
|
|
2021
|
-
browserStateBeforeCommand: browserState,
|
|
2022
|
-
urlBeforeCommand: url,
|
|
2023
|
-
type
|
|
2024
|
-
});
|
|
2025
|
-
const history = this.getSerializedHistory(url, browserState);
|
|
2026
|
-
const getCommandProposalStart = Date.now();
|
|
2027
|
-
const proposedCommand = yield this.generator.getProposedCommand(
|
|
2028
|
-
{
|
|
2029
|
-
url,
|
|
2030
|
-
numPrevious,
|
|
2031
|
-
browserState,
|
|
2032
|
-
history,
|
|
2033
|
-
goal: currInstruction,
|
|
2034
|
-
lastCommand: this.lastExecutedCommand
|
|
2035
|
-
},
|
|
2036
|
-
disableCache
|
|
2037
|
-
);
|
|
2038
|
-
this.logger.info(
|
|
2039
|
-
{ duration: Date.now() - getCommandProposalStart },
|
|
2040
|
-
"Got proposed command"
|
|
2041
|
-
);
|
|
2042
|
-
if (proposedCommand.type === "SUCCESS" /* SUCCESS */) {
|
|
2043
|
-
const finishedInstruction = this.pendingInstructions.pop();
|
|
2044
|
-
this.logger.info(
|
|
2045
|
-
{
|
|
2046
|
-
finishedInstruction,
|
|
2047
|
-
remainingInstructions: this.pendingInstructions
|
|
2048
|
-
},
|
|
2049
|
-
"Removing pending instruction due to SUCCESS"
|
|
2050
|
-
);
|
|
2051
|
-
if (this.pendingInstructions.length !== 0) {
|
|
2052
|
-
this.commandHistory.pop();
|
|
2053
|
-
return this.promptToCommand(type, "", disableCache);
|
|
2054
|
-
}
|
|
2055
|
-
} else if (
|
|
2056
|
-
// on failure, we don't continue to execute
|
|
2057
|
-
proposedCommand.type === "FAILURE"
|
|
2058
|
-
) {
|
|
2059
|
-
this.logger.info(
|
|
2060
|
-
{
|
|
2061
|
-
remainingInstructions: this.pendingInstructions
|
|
2062
|
-
},
|
|
2063
|
-
"Removing pending instructions due to FAILURE"
|
|
2064
|
-
);
|
|
2065
|
-
this.pendingInstructions = [];
|
|
2066
|
-
}
|
|
2067
|
-
return proposedCommand;
|
|
2068
|
-
});
|
|
2069
|
-
}
|
|
2070
|
-
locateElement(description, disableCache) {
|
|
2071
|
-
return __async(this, null, function* () {
|
|
2072
|
-
const locator = yield this.generator.getElementLocation(
|
|
2073
|
-
{ browserState: yield this.getBrowserState(), goal: description },
|
|
2074
|
-
disableCache
|
|
2075
|
-
);
|
|
2076
|
-
if (locator.id < 0) {
|
|
2077
|
-
throw new Error(
|
|
2078
|
-
`Unable to locate element with description: ${description}`
|
|
2079
|
-
);
|
|
2080
|
-
}
|
|
2081
|
-
return locator;
|
|
2082
|
-
});
|
|
2083
|
-
}
|
|
2084
|
-
/**
|
|
2085
|
-
* Construct a detailed history that can be passed to the LLM.
|
|
2086
|
-
* History includes commands executed as well as browser state changes that occurred
|
|
2087
|
-
* at each step.
|
|
2088
|
-
*/
|
|
2089
|
-
getDiffHistory(currentURL, currentPageState) {
|
|
2090
|
-
const doneCommands = this.history.filter(
|
|
2091
|
-
(h) => h.type === "AI_ACTION" /* AI_ACTION */
|
|
2092
|
-
);
|
|
2093
|
-
if (doneCommands.length === 0)
|
|
2094
|
-
return "<NONE/>";
|
|
2095
|
-
const historyLines = [
|
|
2096
|
-
"\nYou have already executed the following commands successfully (most recent listed first)",
|
|
2097
|
-
"-".repeat(10)
|
|
2098
|
-
];
|
|
2099
|
-
doneCommands.reverse().forEach((log, i) => {
|
|
2100
|
-
historyLines.push(
|
|
2101
|
-
`COMMAND ${doneCommands.length - i}${i === 0 ? " (command just executed)" : ""}: ${log.serializedCommand}`
|
|
2102
|
-
);
|
|
2103
|
-
if (i === 0) {
|
|
2104
|
-
if (urlChanged(log.urlBeforeCommand, currentURL)) {
|
|
2105
|
-
historyLines.push(
|
|
2106
|
-
` URL CHANGE: '${log.urlBeforeCommand}' -> '${currentURL}'`
|
|
2107
|
-
);
|
|
2108
|
-
} else {
|
|
2109
|
-
const browserStateDiff = diffLines(
|
|
2110
|
-
log.browserStateBeforeCommand,
|
|
2111
|
-
currentPageState,
|
|
2112
|
-
{
|
|
2113
|
-
n_surrounding: 1
|
|
2114
|
-
}
|
|
2115
|
-
);
|
|
2116
|
-
if (!browserStateDiff) {
|
|
2117
|
-
historyLines.push("PAGE CONTENT CHANGE: <NONE/>");
|
|
2118
|
-
} else if (browserStateDiff.length < MAX_HISTORY_CHAR_LENGTH) {
|
|
2119
|
-
historyLines.push("PAGE CONTENT CHANGE:");
|
|
2120
|
-
browserStateDiff.split("\n").forEach((l) => historyLines.push(` ${l}`));
|
|
2121
|
-
} else {
|
|
2122
|
-
historyLines.push("PAGE CONTENT CHANGE: <TOO_LONG_TO_DISPLAY/>");
|
|
2123
|
-
}
|
|
2124
|
-
}
|
|
2125
|
-
}
|
|
2126
|
-
historyLines.push("-".repeat(10));
|
|
2127
|
-
});
|
|
2128
|
-
historyLines.push(`STARTING URL: ${this.browser.baseURL}`);
|
|
2129
|
-
return historyLines.join("\n");
|
|
2130
|
-
}
|
|
2131
|
-
getListHistory() {
|
|
2132
|
-
return dedent2`Here are the commands that you have successfully executed:
|
|
2133
|
-
${this.commandHistory.filter((cmd) => cmd.type === "AI_ACTION" /* AI_ACTION */).map((cmd) => `- ${cmd.serializedCommand}`).join("\n")}`;
|
|
2134
|
-
}
|
|
2135
|
-
/**
|
|
2136
|
-
* Given a command, interact with the chromium browser to actually execute the actions
|
|
2137
|
-
* @param [stateless=false] Execute this command in a stateless fashion, without modifying any controller state such as
|
|
2138
|
-
* pending instructions. Useful when executing cached instructions.
|
|
2139
|
-
*/
|
|
2140
|
-
executeCommand(command, disableCache, stateless = false) {
|
|
2141
|
-
return __async(this, null, function* () {
|
|
2142
|
-
const pendingHistory = this.commandHistory[this.commandHistory.length - 1];
|
|
2143
|
-
if (!stateless) {
|
|
2144
|
-
if (!pendingHistory || pendingHistory.state !== "PENDING") {
|
|
2145
|
-
throw new Error(
|
|
2146
|
-
"Executing command but there is no pending entry in the history"
|
|
2147
|
-
);
|
|
2148
|
-
}
|
|
2149
|
-
} else {
|
|
2150
|
-
yield this.browser.getA11yTree();
|
|
2151
|
-
}
|
|
2152
|
-
let result;
|
|
2153
|
-
try {
|
|
2154
|
-
const executionStart = Date.now();
|
|
2155
|
-
result = yield this.executePresetStep(
|
|
2156
|
-
command,
|
|
2157
|
-
disableCache
|
|
2158
|
-
);
|
|
2159
|
-
this.logger.info(
|
|
2160
|
-
{ result, duration: Date.now() - executionStart },
|
|
2161
|
-
"Got execution result"
|
|
2162
|
-
);
|
|
2163
|
-
} catch (e) {
|
|
2164
|
-
if (e instanceof Error) {
|
|
2165
|
-
throw new BrowserExecutionError(`Failed to execute command: ${e}`, {
|
|
2166
|
-
cause: e
|
|
2167
|
-
});
|
|
2168
|
-
}
|
|
2169
|
-
throw new BrowserExecutionError(
|
|
2170
|
-
`Unexpected throw from executing command`,
|
|
2171
|
-
{
|
|
2172
|
-
cause: new Error(`${e}`)
|
|
2173
|
-
}
|
|
2174
|
-
);
|
|
2175
|
-
}
|
|
2176
|
-
if (result.succeedImmediately && !stateless) {
|
|
2177
|
-
this.pendingInstructions.pop();
|
|
2178
|
-
if (this.pendingInstructions.length > 0) {
|
|
2179
|
-
result.succeedImmediately = false;
|
|
2180
|
-
}
|
|
2181
|
-
}
|
|
2182
|
-
if (result.elementInteracted && "target" in command && !command.target.elementDescriptor) {
|
|
2183
|
-
command.target.elementDescriptor = result.elementInteracted.trim();
|
|
2184
|
-
}
|
|
2185
|
-
if (!stateless) {
|
|
2186
|
-
pendingHistory.generatedStep = command;
|
|
2187
|
-
pendingHistory.serializedCommand = serializeCommand(command);
|
|
2188
|
-
pendingHistory.state = "DONE";
|
|
2189
|
-
}
|
|
2190
|
-
return result;
|
|
2191
|
-
});
|
|
2192
|
-
}
|
|
2193
|
-
executeAssertion(urlBeforeCommand, command) {
|
|
2194
|
-
return __async(this, null, function* () {
|
|
2195
|
-
let params;
|
|
2196
|
-
if (command.useVision) {
|
|
2197
|
-
params = {
|
|
2198
|
-
goal: command.assertion,
|
|
2199
|
-
url: urlBeforeCommand,
|
|
2200
|
-
// used for vision only
|
|
2201
|
-
screenshot: yield this.browser.screenshot(),
|
|
2202
|
-
// unused for visual assertion
|
|
2203
|
-
browserState: "",
|
|
2204
|
-
history: "",
|
|
2205
|
-
numPrevious: -1,
|
|
2206
|
-
lastCommand: null
|
|
2207
|
-
};
|
|
2208
|
-
} else {
|
|
2209
|
-
const browserState = yield this.getBrowserState();
|
|
2210
|
-
const history = this.getSerializedHistory(urlBeforeCommand, browserState);
|
|
2211
|
-
params = {
|
|
2212
|
-
goal: command.assertion,
|
|
2213
|
-
url: urlBeforeCommand,
|
|
2214
|
-
// used for text only
|
|
2215
|
-
browserState,
|
|
2216
|
-
history,
|
|
2217
|
-
lastCommand: this.lastExecutedCommand,
|
|
2218
|
-
numPrevious: this.commandHistory.length
|
|
2219
|
-
};
|
|
2220
|
-
}
|
|
2221
|
-
const assertionEval = yield this.generator.getAssertionResult(
|
|
2222
|
-
params,
|
|
2223
|
-
command.useVision,
|
|
2224
|
-
command.disableCache
|
|
2225
|
-
);
|
|
2226
|
-
if (assertionEval.relevantElements) {
|
|
2227
|
-
void Promise.all(
|
|
2228
|
-
assertionEval.relevantElements.map(
|
|
2229
|
-
(id) => this.browser.highlight({ id })
|
|
2230
|
-
)
|
|
2231
|
-
);
|
|
2232
|
-
}
|
|
2233
|
-
if (!assertionEval.result) {
|
|
2234
|
-
throw new Error(assertionEval.thoughts);
|
|
2235
|
-
}
|
|
2236
|
-
return {
|
|
2237
|
-
succeedImmediately: false,
|
|
2238
|
-
thoughts: assertionEval.thoughts,
|
|
2239
|
-
urlAfterCommand: urlBeforeCommand
|
|
2240
|
-
};
|
|
2241
|
-
});
|
|
2242
|
-
}
|
|
2243
|
-
/**
|
|
2244
|
-
* Executes a preset command.
|
|
2245
|
-
* For most cases, the execution result contains metadata about the command executed.
|
|
2246
|
-
* For assertions, an AssertionResult with thoughts is returned.
|
|
2247
|
-
* Throws on failure.
|
|
2248
|
-
*/
|
|
2249
|
-
executePresetStep(command, disableCache) {
|
|
2250
|
-
return __async(this, null, function* () {
|
|
2251
|
-
var _a, _b, _c;
|
|
2252
|
-
const urlBeforeCommand = this.browser.url;
|
|
2253
|
-
switch (command.type) {
|
|
2254
|
-
case "SUCCESS" /* SUCCESS */:
|
|
2255
|
-
if ((_a = command.condition) == null ? void 0 : _a.assertion.trim()) {
|
|
2256
|
-
return this.executeAssertion(urlBeforeCommand, command.condition);
|
|
2257
|
-
}
|
|
2258
|
-
return {
|
|
2259
|
-
succeedImmediately: false,
|
|
2260
|
-
urlAfterCommand: this.browser.url
|
|
2261
|
-
};
|
|
2262
|
-
case "AI_ASSERTION" /* AI_ASSERTION */: {
|
|
2263
|
-
return this.executeAssertion(urlBeforeCommand, command);
|
|
2264
|
-
}
|
|
2265
|
-
case "NAVIGATE" /* NAVIGATE */:
|
|
2266
|
-
yield this.browser.navigate(command.url);
|
|
2267
|
-
break;
|
|
2268
|
-
case "GO_BACK" /* GO_BACK */:
|
|
2269
|
-
yield this.browser.goBack();
|
|
2270
|
-
break;
|
|
2271
|
-
case "GO_FORWARD" /* GO_FORWARD */:
|
|
2272
|
-
yield this.browser.goForward();
|
|
2273
|
-
break;
|
|
2274
|
-
case "SCROLL_DOWN" /* SCROLL_DOWN */:
|
|
2275
|
-
yield this.browser.scrollDown();
|
|
2276
|
-
break;
|
|
2277
|
-
case "SCROLL_UP" /* SCROLL_UP */:
|
|
2278
|
-
yield this.browser.scrollUp();
|
|
2279
|
-
break;
|
|
2280
|
-
case "WAIT" /* WAIT */:
|
|
2281
|
-
yield this.browser.wait(command.delay * 1e3);
|
|
2282
|
-
break;
|
|
2283
|
-
case "REFRESH" /* REFRESH */:
|
|
2284
|
-
yield this.browser.refresh();
|
|
2285
|
-
break;
|
|
2286
|
-
case "CLICK" /* CLICK */: {
|
|
2287
|
-
let id;
|
|
2288
|
-
if (command.target.a11yData) {
|
|
2289
|
-
id = (_b = command.target.a11yData) == null ? void 0 : _b.id;
|
|
2290
|
-
} else {
|
|
2291
|
-
const locator = yield this.locateElement(
|
|
2292
|
-
command.target.elementDescriptor,
|
|
2293
|
-
disableCache
|
|
2294
|
-
);
|
|
2295
|
-
id = locator.id;
|
|
2296
|
-
}
|
|
2297
|
-
const elementInteracted = yield this.browser.click(
|
|
2298
|
-
{
|
|
2299
|
-
id
|
|
2300
|
-
},
|
|
2301
|
-
{
|
|
2302
|
-
doubleClick: command.doubleClick,
|
|
2303
|
-
rightClick: command.rightClick
|
|
2304
|
-
}
|
|
2305
|
-
);
|
|
2306
|
-
const result2 = {
|
|
2307
|
-
urlAfterCommand: this.browser.url,
|
|
2308
|
-
succeedImmediately: false,
|
|
2309
|
-
elementInteracted
|
|
2310
|
-
};
|
|
2311
|
-
if (urlChanged(urlBeforeCommand, result2.urlAfterCommand)) {
|
|
2312
|
-
result2.succeedImmediately = true;
|
|
2313
|
-
result2.succeedImmediatelyReason = "URL changed";
|
|
2314
|
-
}
|
|
2315
|
-
return result2;
|
|
2316
|
-
}
|
|
2317
|
-
case "SELECT_OPTION" /* SELECT_OPTION */: {
|
|
2318
|
-
let id;
|
|
2319
|
-
if (command.target.a11yData) {
|
|
2320
|
-
id = (_c = command.target.a11yData) == null ? void 0 : _c.id;
|
|
2321
|
-
} else {
|
|
2322
|
-
const locator = yield this.locateElement(
|
|
2323
|
-
command.target.elementDescriptor,
|
|
2324
|
-
disableCache
|
|
2325
|
-
);
|
|
2326
|
-
id = locator.id;
|
|
2327
|
-
}
|
|
2328
|
-
const elementInteracted = yield this.browser.selectOption(
|
|
2329
|
-
{
|
|
2330
|
-
id
|
|
2331
|
-
},
|
|
2332
|
-
command.option
|
|
2333
|
-
);
|
|
2334
|
-
return {
|
|
2335
|
-
succeedImmediately: false,
|
|
2336
|
-
urlAfterCommand: this.browser.url,
|
|
2337
|
-
elementInteracted
|
|
2338
|
-
};
|
|
2339
|
-
}
|
|
2340
|
-
case "TAB" /* TAB */:
|
|
2341
|
-
yield this.browser.switchToPage(command.url);
|
|
2342
|
-
break;
|
|
2343
|
-
case "COOKIE" /* COOKIE */:
|
|
2344
|
-
yield this.browser.setCookie(command.value);
|
|
2345
|
-
break;
|
|
2346
|
-
case "TYPE" /* TYPE */: {
|
|
2347
|
-
let elementInteracted;
|
|
2348
|
-
const target = command.target;
|
|
2349
|
-
if (target.a11yData) {
|
|
2350
|
-
elementInteracted = yield this.browser.click({
|
|
2351
|
-
id: target.a11yData.id
|
|
2352
|
-
});
|
|
2353
|
-
} else if (target.elementDescriptor.length > 0) {
|
|
2354
|
-
const locator = yield this.locateElement(
|
|
2355
|
-
command.target.elementDescriptor,
|
|
2356
|
-
disableCache
|
|
2357
|
-
);
|
|
2358
|
-
elementInteracted = yield this.browser.click({
|
|
2359
|
-
id: locator.id
|
|
2360
|
-
});
|
|
2361
|
-
}
|
|
2362
|
-
yield this.browser.type(command.value, {
|
|
2363
|
-
clearContent: command.clearContent,
|
|
2364
|
-
pressKeysSequentially: command.pressKeysSequentially
|
|
2365
|
-
});
|
|
2366
|
-
if (command.pressEnter) {
|
|
2367
|
-
yield this.browser.press("Enter");
|
|
2368
|
-
}
|
|
2369
|
-
const result2 = {
|
|
2370
|
-
urlAfterCommand: this.browser.url,
|
|
2371
|
-
succeedImmediately: false,
|
|
2372
|
-
elementInteracted
|
|
2373
|
-
};
|
|
2374
|
-
if (urlChanged(urlBeforeCommand, result2.urlAfterCommand)) {
|
|
2375
|
-
result2.succeedImmediately = true;
|
|
2376
|
-
result2.succeedImmediatelyReason = "URL changed";
|
|
2377
|
-
}
|
|
2378
|
-
return result2;
|
|
2379
|
-
}
|
|
2380
|
-
case "PRESS" /* PRESS */:
|
|
2381
|
-
yield this.browser.press(command.value);
|
|
2382
|
-
const result = {
|
|
2383
|
-
urlAfterCommand: this.browser.url,
|
|
2384
|
-
succeedImmediately: false
|
|
2385
|
-
};
|
|
2386
|
-
if (urlChanged(urlBeforeCommand, result.urlAfterCommand)) {
|
|
2387
|
-
result.succeedImmediately = true;
|
|
2388
|
-
result.succeedImmediatelyReason = "URL changed";
|
|
2389
|
-
}
|
|
2390
|
-
return result;
|
|
2391
|
-
default:
|
|
2392
|
-
const assertUnreachable = (_x) => {
|
|
2393
|
-
throw "If Typescript complains about the line below, you missed a case or break in the switch above";
|
|
2394
|
-
};
|
|
2395
|
-
return assertUnreachable(command);
|
|
2396
|
-
}
|
|
2397
|
-
return {
|
|
2398
|
-
succeedImmediately: false,
|
|
2399
|
-
urlAfterCommand: this.browser.url
|
|
2400
|
-
};
|
|
2401
|
-
});
|
|
2402
|
-
}
|
|
2403
|
-
};
|
|
2404
|
-
|
|
2405
|
-
// ../../packages/web-agent/src/generators/api-generator.ts
|
|
2406
|
-
import fetchRetry from "fetch-retry";
|
|
2407
|
-
var fetch2 = fetchRetry(global.fetch);
|
|
2408
|
-
var API_VERSION = "v1";
|
|
2409
|
-
var APIGenerator = class {
|
|
2410
|
-
constructor(params) {
|
|
2411
|
-
this.baseURL = params.baseURL;
|
|
2412
|
-
this.apiKey = params.apiKey;
|
|
2413
|
-
}
|
|
2414
|
-
getElementLocation(context, disableCache) {
|
|
2415
|
-
return __async(this, null, function* () {
|
|
2416
|
-
const result = yield this.sendRequest(
|
|
2417
|
-
`/${API_VERSION}/web-agent/locate-element`,
|
|
2418
|
-
{
|
|
2419
|
-
browserState: context.browserState,
|
|
2420
|
-
goal: context.goal,
|
|
2421
|
-
disableCache
|
|
2422
|
-
}
|
|
2423
|
-
);
|
|
2424
|
-
return LocateResponseSchema.parse(result);
|
|
2425
|
-
});
|
|
2426
|
-
}
|
|
2427
|
-
getAssertionResult(context, useVision, disableCache) {
|
|
2428
|
-
return __async(this, null, function* () {
|
|
2429
|
-
var _a;
|
|
2430
|
-
if (useVision) {
|
|
2431
|
-
const result2 = yield this.sendRequest(
|
|
2432
|
-
`/${API_VERSION}/web-agent/assertion`,
|
|
2433
|
-
{
|
|
2434
|
-
url: context.url,
|
|
2435
|
-
goal: context.goal,
|
|
2436
|
-
screenshot: (_a = context.screenshot) == null ? void 0 : _a.toString("base64"),
|
|
2437
|
-
disableCache,
|
|
2438
|
-
vision: true
|
|
2439
|
-
}
|
|
2440
|
-
);
|
|
2441
|
-
return GetAssertionResponseSchema.parse(result2);
|
|
2442
|
-
}
|
|
2443
|
-
const result = yield this.sendRequest(
|
|
2444
|
-
`/${API_VERSION}/web-agent/assertion`,
|
|
2445
|
-
{
|
|
2446
|
-
url: context.url,
|
|
2447
|
-
browserState: context.browserState,
|
|
2448
|
-
goal: context.goal,
|
|
2449
|
-
history: context.history,
|
|
2450
|
-
numPrevious: context.numPrevious,
|
|
2451
|
-
lastCommand: context.lastCommand,
|
|
2452
|
-
disableCache,
|
|
2453
|
-
vision: false
|
|
2454
|
-
}
|
|
2455
|
-
);
|
|
2456
|
-
return GetAssertionResponseSchema.parse(result);
|
|
2457
|
-
});
|
|
2458
|
-
}
|
|
2459
|
-
getProposedCommand(context, disableCache) {
|
|
2460
|
-
return __async(this, null, function* () {
|
|
2461
|
-
const result = yield this.sendRequest(
|
|
2462
|
-
`/${API_VERSION}/web-agent/next-command`,
|
|
2463
|
-
{
|
|
2464
|
-
url: context.url,
|
|
2465
|
-
browserState: context.browserState,
|
|
2466
|
-
goal: context.goal,
|
|
2467
|
-
history: context.history,
|
|
2468
|
-
numPrevious: context.numPrevious,
|
|
2469
|
-
lastCommand: context.lastCommand,
|
|
2470
|
-
disableCache
|
|
2471
|
-
}
|
|
2472
|
-
);
|
|
2473
|
-
return GetNextCommandResponseSchema.parse(result);
|
|
2474
|
-
});
|
|
2475
|
-
}
|
|
2476
|
-
getGranularGoals(context, disableCache) {
|
|
2477
|
-
return __async(this, null, function* () {
|
|
2478
|
-
const result = yield this.sendRequest(
|
|
2479
|
-
`/${API_VERSION}/web-agent/split-goal`,
|
|
2480
|
-
{
|
|
2481
|
-
url: context.url,
|
|
2482
|
-
goal: context.goal,
|
|
2483
|
-
disableCache
|
|
2484
|
-
}
|
|
2485
|
-
);
|
|
2486
|
-
return SplitGoalResponseSchema.parse(result);
|
|
2487
|
-
});
|
|
2488
|
-
}
|
|
2489
|
-
sendRequest(path, body) {
|
|
2490
|
-
return __async(this, null, function* () {
|
|
2491
|
-
const response = yield fetch2(`${this.baseURL}${path}`, {
|
|
2492
|
-
retries: 3,
|
|
2493
|
-
retryDelay: 1e3,
|
|
2494
|
-
method: "POST",
|
|
2495
|
-
body: JSON.stringify(body),
|
|
2496
|
-
headers: {
|
|
2497
|
-
"Content-Type": "application/json",
|
|
2498
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
2499
|
-
}
|
|
2500
|
-
});
|
|
2501
|
-
if (!response.ok) {
|
|
2502
|
-
throw new Error(
|
|
2503
|
-
`Request to ${path} failed with status ${response.status}: ${yield response.text()}`
|
|
2504
|
-
);
|
|
2505
|
-
}
|
|
2506
|
-
return response.json();
|
|
2507
|
-
});
|
|
2508
|
-
}
|
|
2509
|
-
};
|
|
2510
|
-
|
|
2511
|
-
// src/api-client.ts
|
|
2512
|
-
var API_VERSION2 = "v1";
|
|
2513
|
-
var APIClient = class {
|
|
2514
|
-
constructor(params) {
|
|
2515
|
-
this.baseURL = params.baseURL;
|
|
2516
|
-
this.apiKey = params.apiKey;
|
|
2517
|
-
}
|
|
2518
|
-
getRun(runId) {
|
|
2519
|
-
return __async(this, null, function* () {
|
|
2520
|
-
const result = yield this.sendRequest(`/${API_VERSION2}/runs/${runId}`, {
|
|
2521
|
-
method: "GET"
|
|
2522
|
-
});
|
|
2523
|
-
return GetRunResponseSchema.parse(result);
|
|
2524
|
-
});
|
|
2525
|
-
}
|
|
2526
|
-
createRun(body) {
|
|
2527
|
-
return __async(this, null, function* () {
|
|
2528
|
-
const result = yield this.sendRequest(`/${API_VERSION2}/runs`, {
|
|
2529
|
-
method: "POST",
|
|
2530
|
-
body
|
|
2531
|
-
});
|
|
2532
|
-
return CreateRunResponseSchema.parse(result);
|
|
2533
|
-
});
|
|
2534
|
-
}
|
|
2535
|
-
updateRun(runId, body) {
|
|
2536
|
-
return __async(this, null, function* () {
|
|
2537
|
-
yield this.sendRequest(`/${API_VERSION2}/runs/${runId}`, {
|
|
2538
|
-
method: "PATCH",
|
|
2539
|
-
body
|
|
2540
|
-
});
|
|
2541
|
-
});
|
|
2542
|
-
}
|
|
2543
|
-
getTest(testId) {
|
|
2544
|
-
return __async(this, null, function* () {
|
|
2545
|
-
const result = yield this.sendRequest(`/${API_VERSION2}/tests/${testId}`, {
|
|
2546
|
-
method: "GET"
|
|
2547
|
-
});
|
|
2548
|
-
return GetTestResponseSchema.parse(result);
|
|
2549
|
-
});
|
|
2550
|
-
}
|
|
2551
|
-
queueTests(body) {
|
|
2552
|
-
return __async(this, null, function* () {
|
|
2553
|
-
yield this.sendRequest(`/${API_VERSION2}/tests/queue`, {
|
|
2554
|
-
method: "POST",
|
|
2555
|
-
body
|
|
2556
|
-
});
|
|
2557
|
-
});
|
|
2558
|
-
}
|
|
2559
|
-
uploadScreenshot(body) {
|
|
2560
|
-
return __async(this, null, function* () {
|
|
2561
|
-
const result = yield this.sendRequest(`/${API_VERSION2}/screenshots`, {
|
|
2562
|
-
method: "POST",
|
|
2563
|
-
body
|
|
2564
|
-
});
|
|
2565
|
-
return CreateScreenshotResponseSchema.parse(result);
|
|
2566
|
-
});
|
|
2567
|
-
}
|
|
2568
|
-
sendRequest(path, options) {
|
|
2569
|
-
return __async(this, null, function* () {
|
|
2570
|
-
const response = yield fetch(`${this.baseURL}${path}`, {
|
|
2571
|
-
method: options.method,
|
|
2572
|
-
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
2573
|
-
headers: {
|
|
2574
|
-
"Content-Type": "application/json",
|
|
2575
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
2576
|
-
}
|
|
2577
|
-
});
|
|
2578
|
-
if (!response.ok) {
|
|
2579
|
-
throw new Error(
|
|
2580
|
-
`Request to ${path} failed with status ${response.status}: ${yield response.text()}`
|
|
2581
|
-
);
|
|
2582
|
-
}
|
|
2583
|
-
if (response.status === 204) {
|
|
2584
|
-
return response.text();
|
|
2585
|
-
}
|
|
2586
|
-
return response.json();
|
|
2587
|
-
});
|
|
2588
|
-
}
|
|
2589
|
-
};
|
|
2590
|
-
|
|
2591
|
-
// ../../packages/execute/src/constants.ts
|
|
2592
|
-
var MAX_COMMANDS_PER_STEP = 20;
|
|
2593
|
-
|
|
2594
|
-
// ../../packages/execute/src/steps/ai.ts
|
|
2595
|
-
var executeAIStep = (_a) => __async(void 0, null, function* () {
|
|
2596
|
-
var _b = _a, {
|
|
2597
|
-
controller,
|
|
2598
|
-
step,
|
|
2599
|
-
logger,
|
|
2600
|
-
advanced
|
|
2601
|
-
} = _b, callbacks = __objRest(_b, [
|
|
2602
|
-
"controller",
|
|
2603
|
-
"step",
|
|
2604
|
-
"logger",
|
|
2605
|
-
"advanced"
|
|
2606
|
-
]);
|
|
2607
|
-
var _a2, _b2, _c, _d, _e, _f, _g;
|
|
2608
|
-
(_a2 = callbacks.onStarted) == null ? void 0 : _a2.call(callbacks);
|
|
2609
|
-
controller.resetHistory();
|
|
2610
|
-
const result = __spreadProps(__spreadValues({}, step), {
|
|
2611
|
-
startedAt: /* @__PURE__ */ new Date(),
|
|
2612
|
-
userAgent: ChromeBrowser.USER_AGENT,
|
|
2613
|
-
// placeholder values
|
|
2614
|
-
finishedAt: /* @__PURE__ */ new Date(),
|
|
2615
|
-
results: [],
|
|
2616
|
-
status: "SUCCESS" /* SUCCESS */
|
|
2617
|
-
});
|
|
2618
|
-
try {
|
|
2619
|
-
let commandIndex = 0;
|
|
2620
|
-
let useSavedCommands = step.commands && step.commands.length > 0;
|
|
2621
|
-
while (true) {
|
|
2622
|
-
if (commandIndex > MAX_COMMANDS_PER_STEP) {
|
|
2623
|
-
throw new Error(
|
|
2624
|
-
`Exceeded max number of commands per step (${MAX_COMMANDS_PER_STEP})`
|
|
2625
|
-
);
|
|
2626
|
-
}
|
|
2627
|
-
let command;
|
|
2628
|
-
const startedAt = /* @__PURE__ */ new Date();
|
|
2629
|
-
const beforeScreenshotBuffer = yield controller.browser.screenshot();
|
|
2630
|
-
const beforeScreenshot = yield callbacks.onSaveScreenshot(
|
|
2631
|
-
beforeScreenshotBuffer
|
|
2632
|
-
);
|
|
2633
|
-
if (useSavedCommands) {
|
|
2634
|
-
command = step.commands[commandIndex];
|
|
2635
|
-
if (!command) {
|
|
2636
|
-
throw new Error(
|
|
2637
|
-
`Saved command at index ${commandIndex} is undefined.`
|
|
2638
|
-
);
|
|
2639
|
-
}
|
|
2640
|
-
} else {
|
|
2641
|
-
command = yield controller.promptToCommand(
|
|
2642
|
-
step.type,
|
|
2643
|
-
step.text,
|
|
2644
|
-
advanced.disableAICaching
|
|
2645
|
-
);
|
|
2646
|
-
}
|
|
2647
|
-
if (command.type === "FAILURE") {
|
|
2648
|
-
result.finishedAt = /* @__PURE__ */ new Date();
|
|
2649
|
-
result.status = "FAILED" /* FAILED */;
|
|
2650
|
-
result.message = command.thoughts;
|
|
2651
|
-
break;
|
|
2652
|
-
}
|
|
2653
|
-
(_b2 = callbacks.onCommandGenerated) == null ? void 0 : _b2.call(callbacks, {
|
|
2654
|
-
commandIndex,
|
|
2655
|
-
message: CARD_DISPLAY_NAMES[command.type] || `Unknown command (${command.type})`
|
|
2656
|
-
});
|
|
2657
|
-
const cmdResult = {
|
|
2658
|
-
beforeScreenshot,
|
|
2659
|
-
beforeUrl: controller.browser.url,
|
|
2660
|
-
startedAt,
|
|
2661
|
-
viewport: controller.browser.viewport,
|
|
2662
|
-
// placeholder values
|
|
2663
|
-
finishedAt: /* @__PURE__ */ new Date(),
|
|
2664
|
-
status: "SUCCESS" /* SUCCESS */
|
|
2665
|
-
};
|
|
2666
|
-
logger.info(
|
|
2667
|
-
`Executing command ${commandIndex}: ${serializeCommand(command)}`
|
|
2668
|
-
);
|
|
2669
|
-
try {
|
|
2670
|
-
const executionResult = yield controller.executeCommand(
|
|
2671
|
-
command,
|
|
2672
|
-
advanced.disableAICaching,
|
|
2673
|
-
useSavedCommands
|
|
2674
|
-
);
|
|
2675
|
-
cmdResult.elementInteracted = executionResult.elementInteracted;
|
|
2676
|
-
(_c = callbacks.onCommandExecuted) == null ? void 0 : _c.call(callbacks, {
|
|
2677
|
-
commandIndex,
|
|
2678
|
-
message: serializeCommand(command),
|
|
2679
|
-
command
|
|
2680
|
-
});
|
|
2681
|
-
const afterScreenshotBuffer = yield controller.browser.screenshot();
|
|
2682
|
-
const afterScreenshot = yield callbacks.onSaveScreenshot(
|
|
2683
|
-
afterScreenshotBuffer
|
|
2684
|
-
);
|
|
2685
|
-
cmdResult.afterScreenshot = afterScreenshot;
|
|
2686
|
-
cmdResult.afterUrl = controller.browser.url;
|
|
2687
|
-
cmdResult.finishedAt = /* @__PURE__ */ new Date();
|
|
2688
|
-
const presetActionResult = {
|
|
2689
|
-
status: "SUCCESS" /* SUCCESS */,
|
|
2690
|
-
startedAt: cmdResult.startedAt,
|
|
2691
|
-
finishedAt: cmdResult.finishedAt,
|
|
2692
|
-
type: "PRESET_ACTION" /* PRESET_ACTION */,
|
|
2693
|
-
command,
|
|
2694
|
-
results: [cmdResult]
|
|
2695
|
-
};
|
|
2696
|
-
result.results.push(presetActionResult);
|
|
2697
|
-
if (command.type === "SUCCESS" /* SUCCESS */) {
|
|
2698
|
-
result.finishedAt = /* @__PURE__ */ new Date();
|
|
2699
|
-
result.status = "SUCCESS" /* SUCCESS */;
|
|
2700
|
-
result.message = (_d = executionResult.thoughts) != null ? _d : "All commands completed.";
|
|
2701
|
-
break;
|
|
2702
|
-
}
|
|
2703
|
-
if (executionResult.succeedImmediately && !useSavedCommands) {
|
|
2704
|
-
result.finishedAt = /* @__PURE__ */ new Date();
|
|
2705
|
-
result.status = "SUCCESS" /* SUCCESS */;
|
|
2706
|
-
result.message = executionResult.succeedImmediatelyReason;
|
|
2707
|
-
command = {
|
|
2708
|
-
type: "SUCCESS" /* SUCCESS */
|
|
2709
|
-
};
|
|
2710
|
-
(_e = callbacks.onCommandExecuted) == null ? void 0 : _e.call(callbacks, {
|
|
2711
|
-
commandIndex: commandIndex + 1,
|
|
2712
|
-
message: serializeCommand(command),
|
|
2713
|
-
command
|
|
2714
|
-
});
|
|
2715
|
-
result.results.push(__spreadProps(__spreadValues({}, presetActionResult), {
|
|
2716
|
-
command
|
|
2717
|
-
}));
|
|
2718
|
-
break;
|
|
2719
|
-
}
|
|
2720
|
-
} catch (err) {
|
|
2721
|
-
if (useSavedCommands) {
|
|
2722
|
-
useSavedCommands = false;
|
|
2723
|
-
commandIndex = 0;
|
|
2724
|
-
result.results = [];
|
|
2725
|
-
continue;
|
|
2726
|
-
}
|
|
2727
|
-
cmdResult.status = "FAILED" /* FAILED */;
|
|
2728
|
-
cmdResult.message = `${err}`;
|
|
2729
|
-
cmdResult.finishedAt = /* @__PURE__ */ new Date();
|
|
2730
|
-
cmdResult.afterScreenshot = void 0;
|
|
2731
|
-
cmdResult.afterUrl = controller.browser.url;
|
|
2732
|
-
result.results.push({
|
|
2733
|
-
status: "FAILED" /* FAILED */,
|
|
2734
|
-
startedAt: cmdResult.startedAt,
|
|
2735
|
-
finishedAt: cmdResult.finishedAt,
|
|
2736
|
-
type: "PRESET_ACTION" /* PRESET_ACTION */,
|
|
2737
|
-
command,
|
|
2738
|
-
results: [cmdResult],
|
|
2739
|
-
message: `${err}`
|
|
2740
|
-
});
|
|
2741
|
-
result.status = "FAILED" /* FAILED */;
|
|
2742
|
-
result.finishedAt = /* @__PURE__ */ new Date();
|
|
2743
|
-
result.message = `${err}`;
|
|
2744
|
-
break;
|
|
2745
|
-
}
|
|
2746
|
-
commandIndex++;
|
|
2747
|
-
}
|
|
2748
|
-
} catch (err) {
|
|
2749
|
-
result.message = `${err}`;
|
|
2750
|
-
result.finishedAt = /* @__PURE__ */ new Date();
|
|
2751
|
-
result.status = "FAILED" /* FAILED */;
|
|
2752
|
-
}
|
|
2753
|
-
if (result.status === "SUCCESS" /* SUCCESS */) {
|
|
2754
|
-
(_f = callbacks.onSuccess) == null ? void 0 : _f.call(callbacks, {
|
|
2755
|
-
message: result.message || "AI step succeeded.",
|
|
2756
|
-
startedAt: result.startedAt.getTime(),
|
|
2757
|
-
durationMs: result.finishedAt.getTime() - result.startedAt.getTime()
|
|
2758
|
-
});
|
|
2759
|
-
} else {
|
|
2760
|
-
(_g = callbacks.onFailure) == null ? void 0 : _g.call(callbacks, {
|
|
2761
|
-
message: result.message || "AI step errored.",
|
|
2762
|
-
startedAt: result.startedAt.getTime(),
|
|
2763
|
-
durationMs: result.finishedAt.getTime() - result.startedAt.getTime()
|
|
2764
|
-
});
|
|
2765
|
-
}
|
|
2766
|
-
return result;
|
|
2767
|
-
});
|
|
2768
|
-
|
|
2769
|
-
// ../../packages/execute/src/steps/preset.ts
|
|
2770
|
-
var executePresetStep = (_a) => __async(void 0, null, function* () {
|
|
2771
|
-
var _b = _a, {
|
|
2772
|
-
controller,
|
|
2773
|
-
step,
|
|
2774
|
-
advanced
|
|
2775
|
-
} = _b, callbacks = __objRest(_b, [
|
|
2776
|
-
"controller",
|
|
2777
|
-
"step",
|
|
2778
|
-
"advanced"
|
|
2779
|
-
]);
|
|
2780
|
-
var _a2, _b2, _c;
|
|
2781
|
-
(_a2 = callbacks.onStarted) == null ? void 0 : _a2.call(callbacks);
|
|
2782
|
-
const startedAt = /* @__PURE__ */ new Date();
|
|
2783
|
-
const beforeUrl = controller.browser.url;
|
|
2784
|
-
const beforeScreenshotBuffer = yield controller.browser.screenshot();
|
|
2785
|
-
const beforeScreenshot = yield callbacks.onSaveScreenshot(
|
|
2786
|
-
beforeScreenshotBuffer
|
|
2787
|
-
);
|
|
2788
|
-
try {
|
|
2789
|
-
const execResult = yield controller.executePresetStep(
|
|
2790
|
-
step.command,
|
|
2791
|
-
advanced.disableAICaching
|
|
2792
|
-
);
|
|
2793
|
-
const afterScreenshotBuffer = yield controller.browser.screenshot();
|
|
2794
|
-
const afterScreenshot = yield callbacks.onSaveScreenshot(
|
|
2795
|
-
afterScreenshotBuffer
|
|
2796
|
-
);
|
|
2797
|
-
const finishedAt = /* @__PURE__ */ new Date();
|
|
2798
|
-
const result = __spreadProps(__spreadValues({}, step), {
|
|
2799
|
-
startedAt,
|
|
2800
|
-
finishedAt,
|
|
2801
|
-
// placeholder values
|
|
2802
|
-
status: "SUCCESS" /* SUCCESS */,
|
|
2803
|
-
results: []
|
|
2804
|
-
});
|
|
2805
|
-
let message = "Successfully executed preset action.";
|
|
2806
|
-
if (step.command.type === "AI_ASSERTION" /* AI_ASSERTION */) {
|
|
2807
|
-
message = execResult.thoughts || "Assertion passed.";
|
|
2808
|
-
}
|
|
2809
|
-
const cmdMetadata = {
|
|
2810
|
-
beforeUrl,
|
|
2811
|
-
beforeScreenshot,
|
|
2812
|
-
afterUrl: controller.browser.url,
|
|
2813
|
-
afterScreenshot,
|
|
2814
|
-
startedAt,
|
|
2815
|
-
finishedAt,
|
|
2816
|
-
viewport: controller.browser.viewport,
|
|
2817
|
-
status: "SUCCESS" /* SUCCESS */
|
|
2818
|
-
};
|
|
2819
|
-
result.status = "SUCCESS" /* SUCCESS */;
|
|
2820
|
-
result.results = [cmdMetadata];
|
|
2821
|
-
result.message = message;
|
|
2822
|
-
(_b2 = callbacks.onSuccess) == null ? void 0 : _b2.call(callbacks, {
|
|
2823
|
-
message,
|
|
2824
|
-
startedAt: startedAt.getTime(),
|
|
2825
|
-
durationMs: finishedAt.getTime() - startedAt.getTime()
|
|
2826
|
-
});
|
|
2827
|
-
return result;
|
|
2828
|
-
} catch (err) {
|
|
2829
|
-
const finishedAt = /* @__PURE__ */ new Date();
|
|
2830
|
-
const result = __spreadProps(__spreadValues({}, step), {
|
|
2831
|
-
startedAt,
|
|
2832
|
-
finishedAt,
|
|
2833
|
-
status: "FAILED" /* FAILED */,
|
|
2834
|
-
message: `${err}`,
|
|
2835
|
-
results: [
|
|
2836
|
-
{
|
|
2837
|
-
beforeUrl,
|
|
2838
|
-
beforeScreenshot,
|
|
2839
|
-
afterUrl: controller.browser.url,
|
|
2840
|
-
afterScreenshot: void 0,
|
|
2841
|
-
startedAt,
|
|
2842
|
-
finishedAt,
|
|
2843
|
-
viewport: controller.browser.viewport,
|
|
2844
|
-
status: "FAILED" /* FAILED */,
|
|
2845
|
-
message: `${err}`
|
|
2846
|
-
}
|
|
2847
|
-
]
|
|
2848
|
-
});
|
|
2849
|
-
(_c = callbacks.onFailure) == null ? void 0 : _c.call(callbacks, {
|
|
2850
|
-
message: `${err}`,
|
|
2851
|
-
startedAt: startedAt.getTime(),
|
|
2852
|
-
durationMs: finishedAt.getTime() - startedAt.getTime()
|
|
2853
|
-
});
|
|
2854
|
-
return result;
|
|
2855
|
-
}
|
|
2856
|
-
});
|
|
2857
|
-
|
|
2858
|
-
// ../../packages/execute/src/steps/module.ts
|
|
2859
|
-
var executeModuleStep = (_a) => __async(void 0, null, function* () {
|
|
2860
|
-
var _b = _a, {
|
|
2861
|
-
controller,
|
|
2862
|
-
step,
|
|
2863
|
-
advanced,
|
|
2864
|
-
logger
|
|
2865
|
-
} = _b, callbacks = __objRest(_b, [
|
|
2866
|
-
"controller",
|
|
2867
|
-
"step",
|
|
2868
|
-
"advanced",
|
|
2869
|
-
"logger"
|
|
2870
|
-
]);
|
|
2871
|
-
var _a2, _b2, _c;
|
|
2872
|
-
(_a2 = callbacks.onStarted) == null ? void 0 : _a2.call(callbacks);
|
|
2873
|
-
const result = {
|
|
2874
|
-
type: "MODULE" /* MODULE */,
|
|
2875
|
-
moduleId: step.moduleId,
|
|
2876
|
-
startedAt: /* @__PURE__ */ new Date(),
|
|
2877
|
-
userAgent: ChromeBrowser.USER_AGENT,
|
|
2878
|
-
// placeholder values
|
|
2879
|
-
results: [],
|
|
2880
|
-
finishedAt: /* @__PURE__ */ new Date(),
|
|
2881
|
-
status: "SUCCESS" /* SUCCESS */
|
|
2882
|
-
};
|
|
2883
|
-
for (let i = 0; i < step.steps.length; i++) {
|
|
2884
|
-
const moduleStep = step.steps[i];
|
|
2885
|
-
logger.info({ i, moduleStep }, `Starting module step`);
|
|
2886
|
-
let moduleStepResult;
|
|
2887
|
-
switch (moduleStep.type) {
|
|
2888
|
-
case "PRESET_ACTION" /* PRESET_ACTION */:
|
|
2889
|
-
moduleStepResult = yield executePresetStep({
|
|
2890
|
-
controller,
|
|
2891
|
-
step: moduleStep,
|
|
2892
|
-
advanced,
|
|
2893
|
-
logger,
|
|
2894
|
-
onSaveScreenshot: callbacks.onSaveScreenshot,
|
|
2895
|
-
onStarted() {
|
|
2896
|
-
var _a3;
|
|
2897
|
-
(_a3 = callbacks.onStepStarted) == null ? void 0 : _a3.call(callbacks, { index: i });
|
|
2898
|
-
},
|
|
2899
|
-
onSuccess({ message, startedAt, durationMs }) {
|
|
2900
|
-
var _a3;
|
|
2901
|
-
(_a3 = callbacks.onStepSuccess) == null ? void 0 : _a3.call(callbacks, {
|
|
2902
|
-
index: i,
|
|
2903
|
-
message,
|
|
2904
|
-
startedAt,
|
|
2905
|
-
durationMs
|
|
2906
|
-
});
|
|
2907
|
-
},
|
|
2908
|
-
onFailure({ message, startedAt, durationMs }) {
|
|
2909
|
-
var _a3;
|
|
2910
|
-
(_a3 = callbacks.onStepFailure) == null ? void 0 : _a3.call(callbacks, {
|
|
2911
|
-
index: i,
|
|
2912
|
-
message,
|
|
2913
|
-
startedAt,
|
|
2914
|
-
durationMs
|
|
2915
|
-
});
|
|
2916
|
-
}
|
|
2917
|
-
});
|
|
2918
|
-
break;
|
|
2919
|
-
case "AI_ACTION" /* AI_ACTION */:
|
|
2920
|
-
moduleStepResult = yield executeAIStep({
|
|
2921
|
-
controller,
|
|
2922
|
-
step: moduleStep,
|
|
2923
|
-
advanced,
|
|
2924
|
-
logger,
|
|
2925
|
-
onSaveScreenshot: callbacks.onSaveScreenshot,
|
|
2926
|
-
onStarted() {
|
|
2927
|
-
var _a3;
|
|
2928
|
-
(_a3 = callbacks.onStepStarted) == null ? void 0 : _a3.call(callbacks, { index: i });
|
|
2929
|
-
},
|
|
2930
|
-
onSuccess({ message, startedAt, durationMs }) {
|
|
2931
|
-
var _a3;
|
|
2932
|
-
(_a3 = callbacks.onStepSuccess) == null ? void 0 : _a3.call(callbacks, {
|
|
2933
|
-
index: i,
|
|
2934
|
-
message,
|
|
2935
|
-
startedAt,
|
|
2936
|
-
durationMs
|
|
2937
|
-
});
|
|
2938
|
-
},
|
|
2939
|
-
onFailure({ message, startedAt, durationMs }) {
|
|
2940
|
-
var _a3;
|
|
2941
|
-
(_a3 = callbacks.onStepFailure) == null ? void 0 : _a3.call(callbacks, {
|
|
2942
|
-
index: i,
|
|
2943
|
-
message,
|
|
2944
|
-
startedAt,
|
|
2945
|
-
durationMs
|
|
2946
|
-
});
|
|
2947
|
-
},
|
|
2948
|
-
onCommandGenerated({ commandIndex, message }) {
|
|
2949
|
-
var _a3;
|
|
2950
|
-
(_a3 = callbacks.onCommandGenerated) == null ? void 0 : _a3.call(callbacks, { index: i, commandIndex, message });
|
|
2951
|
-
},
|
|
2952
|
-
onCommandExecuted({ commandIndex, message, command }) {
|
|
2953
|
-
var _a3;
|
|
2954
|
-
(_a3 = callbacks.onCommandExecuted) == null ? void 0 : _a3.call(callbacks, {
|
|
2955
|
-
index: i,
|
|
2956
|
-
commandIndex,
|
|
2957
|
-
message,
|
|
2958
|
-
command
|
|
2959
|
-
});
|
|
2960
|
-
}
|
|
2961
|
-
});
|
|
2962
|
-
break;
|
|
2963
|
-
default:
|
|
2964
|
-
const assertUnreachable = (_x) => {
|
|
2965
|
-
throw "If Typescript complains about the line below, you missed a case or break in the switch above";
|
|
2966
|
-
};
|
|
2967
|
-
return assertUnreachable(moduleStep);
|
|
2968
|
-
}
|
|
2969
|
-
result.results.push(moduleStepResult);
|
|
2970
|
-
if (moduleStepResult.status === "FAILED" /* FAILED */) {
|
|
2971
|
-
result.status = "FAILED" /* FAILED */;
|
|
2972
|
-
result.finishedAt = /* @__PURE__ */ new Date();
|
|
2973
|
-
for (let j = i + 1; j < step.steps.length; j++) {
|
|
2974
|
-
const skippedStep = step.steps[j];
|
|
2975
|
-
const skippedResult = __spreadProps(__spreadValues({}, skippedStep), {
|
|
2976
|
-
status: "CANCELLED" /* CANCELLED */,
|
|
2977
|
-
startedAt: /* @__PURE__ */ new Date(),
|
|
2978
|
-
finishedAt: /* @__PURE__ */ new Date(),
|
|
2979
|
-
userAgent: ChromeBrowser.USER_AGENT,
|
|
2980
|
-
results: [],
|
|
2981
|
-
message: "Cancelled due to previous failure."
|
|
2982
|
-
});
|
|
2983
|
-
result.results.push(skippedResult);
|
|
2984
|
-
}
|
|
2985
|
-
break;
|
|
2986
|
-
}
|
|
2987
|
-
}
|
|
2988
|
-
if (result.status === "SUCCESS" /* SUCCESS */) {
|
|
2989
|
-
(_b2 = callbacks.onSuccess) == null ? void 0 : _b2.call(callbacks, {
|
|
2990
|
-
message: "Executed module step.",
|
|
2991
|
-
startedAt: result.startedAt.getTime(),
|
|
2992
|
-
durationMs: result.finishedAt.getTime() - result.startedAt.getTime()
|
|
2993
|
-
});
|
|
2994
|
-
} else {
|
|
2995
|
-
(_c = callbacks.onFailure) == null ? void 0 : _c.call(callbacks, {
|
|
2996
|
-
message: "Failed to execute module step.",
|
|
2997
|
-
startedAt: result.startedAt.getTime(),
|
|
2998
|
-
durationMs: result.finishedAt.getTime() - result.startedAt.getTime()
|
|
2999
|
-
});
|
|
3000
|
-
}
|
|
3001
|
-
return result;
|
|
3002
|
-
});
|
|
3003
|
-
|
|
3004
|
-
// ../../packages/execute/src/test.ts
|
|
3005
|
-
var executeTest = (_0) => __async(void 0, [_0], function* ({
|
|
3006
|
-
test,
|
|
3007
|
-
runId,
|
|
3008
|
-
controller,
|
|
3009
|
-
logger,
|
|
3010
|
-
onUpdateRun,
|
|
3011
|
-
onSaveScreenshot
|
|
3012
|
-
}) {
|
|
3013
|
-
const advanced = TestAdvancedSettingsSchema.parse(test.advanced);
|
|
3014
|
-
logger.info(`Starting run ${runId} for test ${test.id}`);
|
|
3015
|
-
yield onUpdateRun({
|
|
3016
|
-
status: "RUNNING",
|
|
3017
|
-
startedAt: /* @__PURE__ */ new Date()
|
|
3018
|
-
});
|
|
3019
|
-
let failed = false;
|
|
3020
|
-
const results = [];
|
|
3021
|
-
for (let i = 0; i < test.steps.length; i++) {
|
|
3022
|
-
const step = test.steps[i];
|
|
3023
|
-
let result;
|
|
3024
|
-
switch (step.type) {
|
|
3025
|
-
case "PRESET_ACTION" /* PRESET_ACTION */:
|
|
3026
|
-
result = yield executePresetStep({
|
|
3027
|
-
controller,
|
|
3028
|
-
step,
|
|
3029
|
-
advanced,
|
|
3030
|
-
logger,
|
|
3031
|
-
onSaveScreenshot
|
|
3032
|
-
});
|
|
3033
|
-
break;
|
|
3034
|
-
case "AI_ACTION" /* AI_ACTION */:
|
|
3035
|
-
result = yield executeAIStep({
|
|
3036
|
-
controller,
|
|
3037
|
-
step,
|
|
3038
|
-
advanced,
|
|
3039
|
-
logger,
|
|
3040
|
-
onSaveScreenshot
|
|
3041
|
-
});
|
|
3042
|
-
break;
|
|
3043
|
-
case "RESOLVED_MODULE":
|
|
3044
|
-
result = yield executeModuleStep({
|
|
3045
|
-
controller,
|
|
3046
|
-
step,
|
|
3047
|
-
advanced,
|
|
3048
|
-
logger,
|
|
3049
|
-
onSaveScreenshot
|
|
3050
|
-
});
|
|
3051
|
-
break;
|
|
3052
|
-
default:
|
|
3053
|
-
const assertUnreachable = (_x) => {
|
|
3054
|
-
throw "If Typescript complains about the line below, you missed a case or break in the switch above";
|
|
3055
|
-
};
|
|
3056
|
-
return assertUnreachable(step);
|
|
3057
|
-
}
|
|
3058
|
-
results.push(result);
|
|
3059
|
-
yield onUpdateRun({
|
|
3060
|
-
results
|
|
3061
|
-
});
|
|
3062
|
-
if (result.status === "FAILED" /* FAILED */) {
|
|
3063
|
-
failed = true;
|
|
3064
|
-
for (let j = i + 1; j < test.steps.length; j++) {
|
|
3065
|
-
const skippedStep = test.steps[j];
|
|
3066
|
-
if (skippedStep.type === "RESOLVED_MODULE") {
|
|
3067
|
-
const skippedResult = {
|
|
3068
|
-
type: "MODULE" /* MODULE */,
|
|
3069
|
-
moduleId: skippedStep.moduleId,
|
|
3070
|
-
startedAt: /* @__PURE__ */ new Date(),
|
|
3071
|
-
userAgent: ChromeBrowser.USER_AGENT,
|
|
3072
|
-
results: skippedStep.steps.map((s) => {
|
|
3073
|
-
return __spreadProps(__spreadValues({}, s), {
|
|
3074
|
-
status: "CANCELLED" /* CANCELLED */,
|
|
3075
|
-
startedAt: /* @__PURE__ */ new Date(),
|
|
3076
|
-
finishedAt: /* @__PURE__ */ new Date(),
|
|
3077
|
-
userAgent: ChromeBrowser.USER_AGENT,
|
|
3078
|
-
results: []
|
|
3079
|
-
});
|
|
3080
|
-
}),
|
|
3081
|
-
finishedAt: /* @__PURE__ */ new Date(),
|
|
3082
|
-
status: "CANCELLED" /* CANCELLED */
|
|
3083
|
-
};
|
|
3084
|
-
results.push(skippedResult);
|
|
3085
|
-
} else {
|
|
3086
|
-
const skippedResult = __spreadProps(__spreadValues({}, skippedStep), {
|
|
3087
|
-
status: "CANCELLED" /* CANCELLED */,
|
|
3088
|
-
startedAt: /* @__PURE__ */ new Date(),
|
|
3089
|
-
finishedAt: /* @__PURE__ */ new Date(),
|
|
3090
|
-
userAgent: ChromeBrowser.USER_AGENT,
|
|
3091
|
-
results: []
|
|
3092
|
-
});
|
|
3093
|
-
results.push(skippedResult);
|
|
3094
|
-
}
|
|
3095
|
-
}
|
|
3096
|
-
}
|
|
3097
|
-
if (failed) {
|
|
3098
|
-
break;
|
|
3099
|
-
}
|
|
3100
|
-
}
|
|
3101
|
-
yield onUpdateRun({
|
|
3102
|
-
status: failed ? "FAILED" : "PASSED",
|
|
3103
|
-
finishedAt: /* @__PURE__ */ new Date(),
|
|
3104
|
-
results
|
|
3105
|
-
});
|
|
3106
|
-
yield controller.browser.cleanup();
|
|
3107
|
-
return failed;
|
|
3108
|
-
});
|
|
3109
|
-
|
|
3110
|
-
// src/logger.ts
|
|
3111
|
-
var consoleLogger = {
|
|
3112
|
-
info: console.log,
|
|
3113
|
-
error: console.error,
|
|
3114
|
-
debug: console.debug,
|
|
3115
|
-
warn: console.warn,
|
|
3116
|
-
child: () => consoleLogger,
|
|
3117
|
-
flush: () => {
|
|
3118
|
-
}
|
|
3119
|
-
};
|
|
3120
|
-
|
|
3121
|
-
// src/run-test.ts
|
|
3122
|
-
function runTest(_0) {
|
|
3123
|
-
return __async(this, arguments, function* ({
|
|
3124
|
-
testId,
|
|
3125
|
-
apiClient,
|
|
3126
|
-
generator,
|
|
3127
|
-
newBaseURL
|
|
3128
|
-
}) {
|
|
3129
|
-
const test = yield apiClient.getTest(testId);
|
|
3130
|
-
const originalURL = new URL(test.baseUrl);
|
|
3131
|
-
const newURL = new URL(newBaseURL);
|
|
3132
|
-
originalURL.hostname = newURL.hostname;
|
|
3133
|
-
originalURL.protocol = newURL.protocol;
|
|
3134
|
-
originalURL.port = newURL.port;
|
|
3135
|
-
const browser = yield ChromeBrowser.init(
|
|
3136
|
-
originalURL.toString(),
|
|
3137
|
-
consoleLogger
|
|
3138
|
-
);
|
|
3139
|
-
const controller = new AgentController({
|
|
3140
|
-
browser,
|
|
3141
|
-
generator,
|
|
3142
|
-
config: DEFAULT_CONTROLLER_CONFIG,
|
|
3143
|
-
logger: consoleLogger
|
|
3144
|
-
});
|
|
3145
|
-
const run = yield apiClient.createRun({
|
|
3146
|
-
testId,
|
|
3147
|
-
trigger: "CLI"
|
|
3148
|
-
});
|
|
3149
|
-
let failed = true;
|
|
3150
|
-
try {
|
|
3151
|
-
failed = yield executeTest({
|
|
3152
|
-
test,
|
|
3153
|
-
runId: run.id,
|
|
3154
|
-
controller,
|
|
3155
|
-
logger: consoleLogger,
|
|
3156
|
-
onSaveScreenshot: (buffer) => __async(this, null, function* () {
|
|
3157
|
-
const { key } = yield apiClient.uploadScreenshot({
|
|
3158
|
-
screenshot: buffer.toString("base64")
|
|
3159
|
-
});
|
|
3160
|
-
return key;
|
|
3161
|
-
}),
|
|
3162
|
-
onUpdateRun: (data) => __async(this, null, function* () {
|
|
3163
|
-
yield apiClient.updateRun(run.id, data);
|
|
3164
|
-
})
|
|
3165
|
-
});
|
|
3166
|
-
} catch (err) {
|
|
3167
|
-
consoleLogger.error(err);
|
|
3168
|
-
yield apiClient.updateRun(run.id, {
|
|
3169
|
-
status: "FAILED",
|
|
3170
|
-
finishedAt: /* @__PURE__ */ new Date()
|
|
3171
|
-
});
|
|
3172
|
-
}
|
|
3173
|
-
return failed;
|
|
3174
|
-
});
|
|
3175
|
-
}
|
|
3176
|
-
|
|
3177
|
-
// src/run-tests-locally.ts
|
|
3178
|
-
function runTestsLocally(_0) {
|
|
3179
|
-
return __async(this, arguments, function* ({
|
|
3180
|
-
tests,
|
|
3181
|
-
start,
|
|
3182
|
-
waitOn,
|
|
3183
|
-
waitOnTimeout,
|
|
3184
|
-
server,
|
|
3185
|
-
apiKey
|
|
3186
|
-
}) {
|
|
3187
|
-
yield execCommand(start, false);
|
|
3188
|
-
yield waitOnFn({
|
|
3189
|
-
resources: [waitOn],
|
|
3190
|
-
timeout: waitOnTimeout * 1e3
|
|
3191
|
-
});
|
|
3192
|
-
const apiClient = new APIClient({
|
|
3193
|
-
baseURL: server,
|
|
3194
|
-
apiKey
|
|
3195
|
-
});
|
|
3196
|
-
const apiGenerator = new APIGenerator({
|
|
3197
|
-
baseURL: server,
|
|
3198
|
-
apiKey
|
|
3199
|
-
});
|
|
3200
|
-
const promises = tests.map((testId) => __async(this, null, function* () {
|
|
3201
|
-
let failed = true;
|
|
3202
|
-
try {
|
|
3203
|
-
failed = yield runTest({
|
|
3204
|
-
testId,
|
|
3205
|
-
apiClient,
|
|
3206
|
-
generator: apiGenerator,
|
|
3207
|
-
newBaseURL: waitOn
|
|
3208
|
-
});
|
|
3209
|
-
} catch (e) {
|
|
3210
|
-
console.error(e);
|
|
3211
|
-
}
|
|
3212
|
-
return { failed, testId };
|
|
3213
|
-
}));
|
|
3214
|
-
const results = yield Promise.all(promises);
|
|
3215
|
-
const failedResults = results.filter((result) => result.failed);
|
|
3216
|
-
if (failedResults.length > 0) {
|
|
3217
|
-
console.log(
|
|
3218
|
-
chalk.red(
|
|
3219
|
-
`Failed ${failedResults.length} out of ${results.length} tests:`
|
|
3220
|
-
)
|
|
3221
|
-
);
|
|
3222
|
-
failedResults.forEach((result) => {
|
|
3223
|
-
console.log(chalk.red(`- ${result.testId}`));
|
|
3224
|
-
});
|
|
3225
|
-
process.exit(1);
|
|
3226
|
-
}
|
|
3227
|
-
console.log(chalk.green(`All ${results.length} tests passed!`));
|
|
3228
|
-
process.exit(0);
|
|
3229
|
-
});
|
|
3230
|
-
}
|
|
3231
|
-
function execCommand(fullCommand, waitToFinish = true) {
|
|
3232
|
-
return __async(this, null, function* () {
|
|
3233
|
-
const args = parseArgsStringToArgv2(fullCommand);
|
|
3234
|
-
const toolPath = yield io.which(args[0], true);
|
|
3235
|
-
const toolArguments = args.slice(1);
|
|
3236
|
-
const promise = exec.exec(quote(toolPath), toolArguments);
|
|
3237
|
-
if (waitToFinish) {
|
|
3238
|
-
return promise;
|
|
3239
|
-
}
|
|
3240
|
-
});
|
|
3241
|
-
}
|
|
3242
|
-
|
|
3243
|
-
// src/run-tests-remotely.ts
|
|
3244
|
-
import chalk2 from "chalk";
|
|
3245
|
-
function runTestsRemotely(_0) {
|
|
3246
|
-
return __async(this, arguments, function* ({
|
|
3247
|
-
tests,
|
|
3248
|
-
apiKey,
|
|
3249
|
-
server
|
|
3250
|
-
}) {
|
|
3251
|
-
const apiClient = new APIClient({
|
|
3252
|
-
baseURL: server,
|
|
3253
|
-
apiKey
|
|
3254
|
-
});
|
|
3255
|
-
yield apiClient.queueTests({
|
|
3256
|
-
testIds: tests
|
|
3257
|
-
});
|
|
3258
|
-
console.log(chalk2.green(`Successfully queued ${tests.length} tests!`));
|
|
3259
|
-
});
|
|
3260
|
-
}
|
|
3261
|
-
|
|
3262
|
-
// src/cli.ts
|
|
3263
|
-
var program = new Command4();
|
|
3264
|
-
program.name("momentic").description("Momentic CLI").version(version);
|
|
3265
|
-
program.command("install-browsers").action(() => __async(void 0, null, function* () {
|
|
3266
|
-
yield installBrowsers();
|
|
3267
|
-
}));
|
|
3268
|
-
program.command("run-tests").addOption(
|
|
3269
|
-
new Option(
|
|
3270
|
-
"--tests <tests...>",
|
|
3271
|
-
"specify tests to run"
|
|
3272
|
-
).makeOptionMandatory(true)
|
|
3273
|
-
).addOption(
|
|
3274
|
-
new Option("--api-key <key>", "API key for authenticating").env("MOMENTIC_API_KEY").makeOptionMandatory(true)
|
|
3275
|
-
).addOption(
|
|
3276
|
-
new Option("--server <server>", "Momentic server to use").default(
|
|
3277
|
-
"https://api.momentic.ai"
|
|
3278
|
-
)
|
|
3279
|
-
).addOption(
|
|
3280
|
-
new Option("--remote", "run tests remotely").default(true).conflicts(["start, waitOn, waitOnTimeout"]).implies({
|
|
3281
|
-
local: false
|
|
3282
|
-
})
|
|
3283
|
-
).addOption(
|
|
3284
|
-
new Option("--local", "run tests locally").implies({
|
|
3285
|
-
start: "npm run start",
|
|
3286
|
-
waitOn: "http://localhost:3000",
|
|
3287
|
-
remote: false
|
|
3288
|
-
})
|
|
3289
|
-
).addOption(new Option("--start <command>", "specify start command")).addOption(new Option("--wait-on <url>", "specify URL to wait on")).addOption(
|
|
3290
|
-
new Option(
|
|
3291
|
-
"--wait-on-timeout <timeout>",
|
|
3292
|
-
"specify how long to wait on url"
|
|
3293
|
-
).default(60, "one minute")
|
|
3294
|
-
).action((options) => __async(void 0, null, function* () {
|
|
3295
|
-
const {
|
|
3296
|
-
tests,
|
|
3297
|
-
apiKey,
|
|
3298
|
-
server,
|
|
3299
|
-
remote,
|
|
3300
|
-
local,
|
|
3301
|
-
start,
|
|
3302
|
-
waitOn,
|
|
3303
|
-
waitOnTimeout
|
|
3304
|
-
} = options;
|
|
3305
|
-
if (remote) {
|
|
3306
|
-
yield runTestsRemotely({ tests, apiKey, server });
|
|
3307
|
-
return;
|
|
3308
|
-
}
|
|
3309
|
-
if (local) {
|
|
3310
|
-
try {
|
|
3311
|
-
yield runTestsLocally({
|
|
3312
|
-
tests,
|
|
3313
|
-
start,
|
|
3314
|
-
waitOn,
|
|
3315
|
-
waitOnTimeout,
|
|
3316
|
-
server,
|
|
3317
|
-
apiKey
|
|
3318
|
-
});
|
|
3319
|
-
} catch (e) {
|
|
3320
|
-
console.error(e);
|
|
3321
|
-
process.exit(1);
|
|
3322
|
-
}
|
|
3323
|
-
return;
|
|
3324
|
-
}
|
|
3325
|
-
}));
|
|
3326
|
-
function main() {
|
|
3327
|
-
return __async(this, null, function* () {
|
|
3328
|
-
yield program.parseAsync(process.argv);
|
|
3329
|
-
});
|
|
3330
|
-
}
|
|
3331
|
-
void main();
|
|
6
|
+
`.replaceAll(`
|
|
7
|
+
`," ")),Cn=D.merge(f.object({type:f.literal("HOVER"),target:re,useVision:f.boolean().default(!1)})),vn=D.merge(f.object({type:f.literal("SELECT_OPTION"),target:re,option:f.string()})).describe('SELECT_OPTION <id> "<option>" - select an option from a combobox, listbox, or menu element on the page. Provide the id of the parent combobox, listbox, or menu element in <id>. Provide the name of the option in <option> enclosed by single quotes.'),Ft=D.merge(f.object({type:f.literal("AI_ASSERTION"),assertion:f.string(),useVision:f.boolean().default(!1),disableCache:f.boolean().default(!1),cancelOnFailure:f.boolean().default(!1)})),xn=f.object({clearContent:f.boolean().default(!0),pressKeysSequentially:f.boolean().default(!1)}),Rn=D.merge(f.object({type:f.literal("TYPE"),target:re,value:f.string(),pressEnter:f.boolean().default(!1),useVision:f.boolean().default(!1)})).merge(xn).describe('TYPE <id> "<text>" - type the specified text into the input with the specified id. The text should be specified by the user - do not use text from the EXAMPLES or generate text yourself. Make sure to include quotes around the text.'),In=D.merge(f.object({type:f.literal("PRESS"),value:f.string()})).describe('PRESS <key> - press the specified key, such as "ArrowLeft", "Enter", or "a". You must specify at least one key.'),Ln=D.merge(f.object({type:f.literal("TAB"),url:f.string()})),On=D.merge(f.object({type:f.literal("COOKIE"),value:f.string()})),Nn=D.merge(f.object({type:f.literal("SUCCESS"),condition:Ft.optional()})).describe("SUCCESS - the user goal has been successfully achieved"),ie=f.discriminatedUnion("type",[Tn,Rn,In,vn,gn,yn,fn,Nn]),Mn=f.discriminatedUnion("type",[bn,An,wn,Ft,Sn,Ln,On,Cn,En]),kt=f.discriminatedUnion("type",[...ie.options,...Mn.options]),Dn=D.merge(f.object({type:f.literal("FAILURE")})).describe("FAILURE - there are no commands to suggest that could make progress that have not already been tried before"),qe=f.discriminatedUnion("type",[...ie.options,Dn]);var pe=(i=>(i.AI_PROVIDER="AIProviderError",i.AI_TIMEOUT="AITimeoutError",i.JOB_TIMEOUT="JobTimeoutError",i.ACTION_FAILURE="ActionFailureError",i.ASSERTION_FAILURE="AssertionFailureError",i.WEB_AGENT_PLATFORM="InternalWebAgentError",i.UNKNOWN_PLATFORM="InternalPlatformError",i))(pe||{});var he=class extends Error{constructor(e,t={}){super(e,t),this.name="BrowserExecutionError"}};var Oe=class extends Error{constructor(e={}){super("Got empty a11y tree",e),this.name="EmptyA11yTreeError"}};var M=class extends Error{constructor(e,t,o={}){var s;let r=!1;for(let a of Object.values(pe))if(t.startsWith(a)){r=!0,e=a;break}r?super(t,o):super(`${e}: ${t}`,o),this.name="TestFailureError",this.stack=(s=this.stack)==null?void 0:s.slice(this.name.length+2),this.reason=e}toString(){return this.message}toJSON(){return{message:this.message}}};var Ri=ge.object({command:ge.string(),thoughts:ge.string()}),Ii=ge.string().pipe(ge.coerce.number());import*as N from"zod";var K="1.0.6",z=(o=>(o.AI_ACTION="AI_ACTION",o.PRESET_ACTION="PRESET_ACTION",o.MODULE="MODULE",o))(z||{}),fe=N.object({type:N.literal("AI_ACTION"),text:N.string(),commands:N.array(ie).optional()}),ye=N.object({type:N.literal("PRESET_ACTION"),command:kt}),Xe=N.object({type:N.literal("MODULE"),moduleId:N.string().uuid()}),Se=N.union([fe,ye]),Je=N.object({type:N.literal("RESOLVED_MODULE"),moduleId:N.string().uuid(),name:N.string(),steps:Se.array()}),we=N.union([fe,ye,Xe]),Qe=N.union([fe,ye,Je]);var _i=new Set(Object.values(B));var Ze={AI_ACTION:"AI action",MODULE:"Module",AI_ASSERTION:"AI check",CLICK:"Click",HOVER:"Hover",SELECT_OPTION:"Select",TYPE:"Type",PRESS:"Press",NAVIGATE:"Navigate",SCROLL_UP:"Scroll up",SCROLL_DOWN:"Scroll down",CAPTCHA:"Captcha",GO_BACK:"Go back",GO_FORWARD:"Go forward",WAIT:"Wait",REFRESH:"Refresh",TAB:"Switch tab",COOKIE:"Set cookie",SUCCESS:"Done"},zi={AI_ACTION:"Ask AI to plan and execute something on the page.",MODULE:"A list of steps that can be reused in multiple tests.",AI_ASSERTION:"Ask AI whether something is true on the page.",CLICK:"Click on an element on the page based on a description.",HOVER:"Hover over an element on the page based on a description.",SELECT_OPTION:"Select an option from a dropdown based on a description.",TYPE:"Type the specified text into an element.",PRESS:"Press the specified keys using the keyboard. (e.g. Ctrl+A)",NAVIGATE:"Navigate to the specified URL.",SCROLL_UP:"Scroll up one page.",SCROLL_DOWN:"Scroll down one page.",GO_BACK:"Go back in browser history.",GO_FORWARD:"Go forward in browser history.",WAIT:"Wait for the specified number of seconds.",REFRESH:"Refresh the page. This will not clear cookies or session data.",TAB:"Switch to different tab in the browser.",COOKIE:"Set a cookie that will persist throughout the browser session",CAPTCHA:"Solve captchas on the page. This may take 10-60 seconds.",SUCCESS:"Indicate the entire AI action has succeeded, optionally based on a condition."};import*as v from"zod";var se=(s=>(s.SUCCESS="SUCCESS",s.FAILED="FAILED",s.RUNNING="RUNNING",s.IDLE="IDLE",s.CANCELLED="CANCELLED",s))(se||{}),Ne=(o=>(o.SUCCESS="SUCCESS",o.FAILED="FAILED",o.CANCELLED="CANCELLED",o))(Ne||{}),Pn=v.object({beforeUrl:v.string(),beforeScreenshot:v.string().or(v.instanceof(Buffer)),afterUrl:v.string().optional(),afterScreenshot:v.string().or(v.instanceof(Buffer)).optional(),startedAt:v.coerce.date(),finishedAt:v.coerce.date(),viewport:v.object({height:v.number(),width:v.number()}),status:v.nativeEnum(Ne),message:v.string().optional(),elementInteracted:v.string().optional()}),et=v.object({startedAt:v.coerce.date(),finishedAt:v.coerce.date(),status:v.nativeEnum(se),message:v.string().optional(),userAgent:v.string().optional()}),tt=ye.merge(et).merge(v.object({results:Pn.array()})),$t=fe.merge(et).merge(v.object({results:tt.array()})),_n=Xe.merge(et).merge(v.object({results:v.union([$t,tt]).array()})),Me=v.discriminatedUnion("type",[$t,tt,_n]);function zn(n,e){return n.length<e?n:n.slice(0,e-3)+"[...]"}function Y(n){var e,t,o;switch(n.type){case"SUCCESS":return(e=n.condition)!=null&&e.assertion?`Check success condition: ${n.condition.assertion}`:"All commands completed";case"NAVIGATE":return`Go to URL: ${zn(n.url,30)}`;case"CAPTCHA":return"Solve captchas on the page";case"GO_BACK":return"Go back to the previous page";case"GO_FORWARD":return"Go forward to the next page";case"SCROLL_DOWN":return`Scroll down one page${n.target?` in the container of: ${n.target.elementDescriptor}`:""}`;case"SCROLL_UP":return`Scroll up one page${n.target?` in the container of: ${n.target.elementDescriptor}`:""}`;case"WAIT":return`Wait for ${n.delay} seconds`;case"REFRESH":return"Refresh the page";case"CLICK":return`Click on '${n.target.elementDescriptor}'`;case"TYPE":{let s="";return(t=n.target.a11yData)!=null&&t.serializedForm?s=`in element: ${n.target.a11yData.serializedForm}`:n.target.elementDescriptor.length>0&&(s=`in element: ${n.target.elementDescriptor}`),`Type '${n.value}' ${s}`}case"HOVER":{let s="";return(o=n.target.a11yData)!=null&&o.serializedForm?s=` over element: ${n.target.a11yData.serializedForm}`:n.target.elementDescriptor.length>0&&(s=` over element: ${n.target.elementDescriptor}`),`Hover${s}`}case"PRESS":return`Press '${n.value}'`;case"SELECT_OPTION":return`Select option '${n.option}' in '${n.target.elementDescriptor}'`;case"TAB":return`Switch to tab: ${n.url}`;case"COOKIE":return`Set cookie: ${n.value}`;case"AI_ASSERTION":return`${n.useVision?"Visual assertion":"Assertion"}: '${n.assertion}'`;default:return(s=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(n)}}import*as $ from"zod";import*as X from"zod";var Ht=X.object({type:X.nativeEnum(z),generatedStep:ie.optional(),serializedCommand:X.string().optional(),elementInteracted:X.string().optional()});var ae=$.object({goal:$.string(),url:$.string(),browserState:$.string(),history:$.string(),numPrevious:$.number(),lastCommand:Ht.or($.null())});import{parseString as Un}from"set-cookie-parser";function Bt(n){let e=Un(n);if(!e.name)throw new Error("Name missing from cookie");if(!e.value)throw new Error("Value missing from cookie");let t;if(e.sameSite){let r=e.sameSite.trim().toLowerCase();if(r==="strict")t="Strict";else if(r==="lax")t="Lax";else if(r==="none")t="None";else throw new Error(`Invalid sameSite setting in cookie: ${r}`)}return!e.path&&e.domain&&(e.path="/"),O(C({},e),{expires:e.expires?e.expires.getTime()/1e3:void 0,sameSite:t})}import{z as U}from"zod";var Fn="1.0.0",jt=U.object({run:U.string().describe("Run a single command in the shell. The working directory will be set to where the CLI was invoked from."),waitForCompletion:U.boolean().optional().describe("Defaults to true")}),Gt=U.object({type:U.literal("momentic/fixture"),schemaVersion:U.string(),name:U.string(),description:U.string().optional(),setup:U.object({steps:jt.array(),timeout:U.number().optional().describe("Timeout for all steps in seconds")}).optional(),teardown:U.object({steps:jt.array(),timeout:U.number().optional().describe("Timeout for all steps in seconds")}).optional()}),Zi={type:"momentic/fixture",schemaVersion:Fn,name:"example",description:"An example fixture",setup:{steps:[{run:"./scripts/seed_db.sh",waitForCompletion:!0},{run:"npm run start",waitForCompletion:!1}],timeout:30},teardown:{steps:[{run:"./scripts/shutdown_db.sh"}]}};import{z as kn}from"zod";var os=kn.string().array();import*as G from"zod";var Vt=G.object({thoughts:G.string(),id:G.number().int(),options:G.array(G.string()).optional()});var Wt={DEBUG:0,INFO:1,WARN:2,ERROR:3},$n={0:"DEBUG",1:"INFO",2:"WARN",3:"ERROR"},Hn={0:"\x1B[90m",1:"\x1B[32m",2:"\x1B[33m",3:"\x1B[31m"},nt=class n{constructor(e,t){this.minLogLevel=e,this.logBindings=t}log(e,...t){let o=$n[e],r;Array.isArray(t[0])?(r=t[0],t=t.slice(1)):typeof t[0]=="object"&&!(t[0]instanceof Error)&&(r=C(C({},t[0]),this.logBindings),t=t.slice(1));let s=Hn[e],a=[`${s}[${new Date().toTimeString().slice(0,8)}][${o}]`];if(e!==0&&a.push("\x1B[39m"),a.push(...t),console.log(...a),r&&!Array.isArray(r))for(let[i,c]of Object.entries(r)){let d=c;typeof c=="object"&&(d=JSON.stringify(c,void 0,2),d=d.split(`
|
|
8
|
+
`).map((h,p)=>p>0?` ${h}`:h).join(`
|
|
9
|
+
`)),console.log(e===0?`${s} ${i}:`:` ${i}:`,d)}else if(r)for(let i of r){let c=i;typeof i=="object"&&(c=JSON.stringify(i,void 0,2),c=c.split(`
|
|
10
|
+
`).map((d,h)=>h>0?` ${d}`:d).join(`
|
|
11
|
+
`)),console.log(e===0?`${s} `:" ",c)}e===0&&process.stdout.write("\x1B[39m")}setMinLevel(e){this.minLogLevel=e}info(...e){1<this.minLogLevel||this.log(1,...e)}debug(...e){0<this.minLogLevel||this.log(0,...e)}warn(...e){2<this.minLogLevel||this.log(2,...e)}error(...e){3<this.minLogLevel||this.log(3,...e)}child(e){return new n(this.minLogLevel,C(C({},this.logBindings),e))}flush(){}bindings(){return this.logBindings}},S=new nt(1,{});import{z as V}from"zod";var Bn=V.object({id:V.string(),createdAt:V.coerce.date(),createdBy:V.string(),organizationId:V.string(),name:V.string(),schemaVersion:V.string(),numSteps:V.number()}),cs=V.object({steps:Se.array()}).merge(Bn.omit({numSteps:!0}));import*as y from"zod";import{z as I}from"zod";var rt={WEBHOOK:"WEBHOOK",CRON:"CRON",MANUAL:"MANUAL",CLI:"CLI"},it={PENDING:"PENDING",RUNNING:"RUNNING",PASSED:"PASSED",FAILED:"FAILED",CANCELLED:"CANCELLED"},jn={PASSED:"PASSED",FAILED:"FAILED"},De=I.string().pipe(I.coerce.date()).or(I.date()),Gn=I.object({id:I.string(),createdAt:De,createdBy:I.string(),organizationId:I.string(),scheduledAt:De.or(I.null()),startedAt:De.or(I.null()),finishedAt:De.or(I.null()),testId:I.string().or(I.null()),status:I.nativeEnum(it),expectedStatus:I.nativeEnum(jn).or(I.null()),runKey:I.string(),trigger:I.nativeEnum(rt),attempts:I.number(),test:I.object({name:I.string(),id:I.string()}).or(I.null())}),st=Gn.merge(I.object({results:Me.array(),test:I.object({name:I.string(),id:I.string(),baseUrl:I.string()}).or(I.null())}));import{z as P}from"zod";import{z as Pe}from"zod";var Kt=Pe.object({name:Pe.string(),fixtures:Pe.array(Pe.string().describe("Name of the fixture (must be available locally in the fixtures directory).")).optional()});import{isValidCron as Vn}from"cron-validator";import{z as H}from"zod";var _e=H.object({availableAsModule:H.boolean().default(!1),disableAICaching:H.boolean().default(!1)}),Yt=H.object({cron:H.string().refine(n=>Vn(n),{message:"Invalid cron expression."}).default("0 0 */1 * *"),enabled:H.boolean().default(!1),timeZone:H.string().default("America/Los_Angeles"),jobKey:H.string().optional()}),qt=H.object({onSuccess:H.boolean().default(!1),onFailure:H.boolean().default(!0)});var Wn=P.string().min(1).max(255).superRefine((n,e)=>{try{Xn(n)}catch(t){return e.addIssue({code:P.ZodIssueCode.custom,message:t.message,fatal:!0}),P.NEVER}}),le=P.object({id:P.string(),name:Wn,baseUrl:P.string(),schemaVersion:P.string(),advanced:_e,retries:P.number(),envSettings:Kt.array().optional()}),Ts=le.pick({name:!0,baseUrl:!0,retries:!0,advanced:!0}),Kn=P.object({createdAt:P.coerce.date(),updatedAt:P.coerce.date(),schedule:Yt,notification:qt,createdBy:P.string(),organizationId:P.string()}),Xt=le.merge(Kn).merge(P.object({steps:P.array(Qe)})),Jt=le.merge(P.object({steps:P.array(Qe)}));var Yn=/^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/,qn=["modules","fixtures"];function Xn(n){if(n=n.toLowerCase().trim(),n.length===0||n.length>255)throw new Error("Name must be between 1 and 255 characters long");if(/[<>:"\/\\|?*\x00]/.test(n))throw new Error("Name can only contain alphanumeric characters, dashes, and underscores.");if(/^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i.test(n))throw new Error(`"${n}" is a reserved name on Windows and cannot be used as a filename.`);if(/^\.+$/.test(n)||/^\s|\s$/.test(n))throw new Error("Name cannot start or end with a space or dot.");if(n.endsWith(".yaml"))throw new Error('Name cannot end with ".yaml".');if(qn.includes(n))throw new Error("'modules' is a reserved folder name in Momentic. Please choose a different name.");if(n.match(Yn))throw new Error("Name cannot be a UUID. Please choose a different name.")}var be=y.object({disableCache:y.boolean()}),Ms=y.object({error:y.boolean(),reason:y.string(),message:y.string()}),Ds=ae.merge(be),Qt=qe,Ps=y.discriminatedUnion("vision",[ae.merge(be).merge(y.object({vision:y.literal(!1)})),ae.pick({goal:!0,url:!0}).merge(be).merge(y.object({screenshot:y.string(),vision:y.literal(!0)}))]),at=Ut,_s=ae.pick({browserState:!0,goal:!0}).merge(be),lt=Vt,zs=ae.pick({goal:!0,url:!0}).merge(be),Zt=y.string().array(),Us=y.object({testPaths:y.string().array().describe("can be either hyphenated, lowercase test names or UUIDs"),all:y.boolean().optional()}),eo=y.object({message:y.string(),queuedTests:y.object({name:y.string(),id:y.string()}).array()}),to=Xt,oo=y.string().array(),Fs=y.union([y.object({paths:y.string().array().describe("run specific test paths (e.g. todo-test)")}),y.object({path:y.string().describe("deprecated; present for backcompat")}),y.object({all:y.boolean().describe("run all tests")})]),no=y.object({tests:y.record(y.string().describe("Test name"),y.string().describe("Test YAML")),modules:y.record(y.string().describe("Module name"),y.string().describe("Module YAML"))}),Jn=y.object({test:y.string().describe("test YAML"),modules:y.record(y.string().describe("moduleId"),y.string().describe("module YAML"))}),ks=Jn.array();var $s=y.object({testPath:y.string(),testId:y.string()}).partial().merge(y.object({trigger:y.nativeEnum(rt)})),ro=st,io=st,Hs=y.object({startedAt:y.coerce.date(),finishedAt:y.coerce.date(),results:Me.array(),status:y.nativeEnum(it)}).partial(),Bs=y.object({screenshot:y.string()}),so=y.object({key:y.string()});function ze(n){switch(n.type){case"AI_ACTION":return`AI action: ${n.text}`;case"PRESET_ACTION":return Y(n.command);case"RESOLVED_MODULE":return`Module: ${n.moduleId}`}}import{stringify as Xs}from"yaml";import{z as L}from"zod";var na=L.object({test:L.string().describe("YAML for the test, including metadata and steps"),modules:L.record(L.string(),L.string()).describe("Map of module name to YAML for the module")}),ra=le.merge(L.object({steps:we.array(),fileType:L.literal("momentic/test")})),ia=Je.omit({type:!0}).merge(L.object({schemaVersion:L.string(),fileType:L.literal("momentic/module")})),sa=le.merge(L.object({steps:L.array(L.record(L.string(),L.unknown()))})),aa=L.object({moduleId:L.string().uuid(),name:L.string(),schemaVersion:L.string(),steps:L.array(L.record(L.string(),L.unknown()))});var ao="0.0.19";var W="v1",ce=class{constructor(e){this.baseURL=e.baseURL,this.apiKey=e.apiKey}getRun(e){return l(this,null,function*(){let t=yield this.sendRequest(`/${W}/runs/${e}`,{method:"GET"});return io.parse(t)})}createRun(e){return l(this,null,function*(){let t=yield this.sendRequest(`/${W}/runs`,{method:"POST",body:e});return ro.parse(t)})}updateRun(e,t){return l(this,null,function*(){yield this.sendRequest(`/${W}/runs/${e}`,{method:"PATCH",body:t})})}getTest(e){return l(this,null,function*(){let t=yield this.sendRequest(`/${W}/tests/${e}`,{method:"GET"});return to.parse(t)})}getAllTestIds(){return l(this,null,function*(){let e=yield this.sendRequest(`/${W}/tests`,{method:"GET"});return oo.parse(e)})}getTestYAMLExport(e){return l(this,null,function*(){let t=yield this.sendRequest(`/${W}/tests/export`,{method:"POST",body:e});return no.parse(t)})}updateTestWithYAML(e){return l(this,null,function*(){yield this.sendRequest(`/${W}/tests/update`,{method:"POST",body:e})})}queueTests(e){return l(this,null,function*(){let t=yield this.sendRequest(`/${W}/tests/queue`,{method:"POST",body:e});return eo.parse(t)})}uploadScreenshot(e){return l(this,null,function*(){let t=yield this.sendRequest(`/${W}/screenshots`,{method:"POST",body:e});return so.parse(t)})}sendRequest(e,t){return l(this,null,function*(){let o=yield fetch(`${this.baseURL}${e}`,{method:t.method,body:t.body?JSON.stringify(t.body):void 0,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}});if(!o.ok)throw new Error(`Request to ${e} failed with status ${o.status}: ${yield o.text()}`);return o.status===204?o.text():o.json()})}};import{existsSync as tr,mkdirSync as ct,statSync as or}from"fs";import{join as lo}from"path";import Zn from"chalk";import er from"readline/promises";function de(n,e){return l(this,null,function*(){if(process.env.CI)return!0;let t=er.createInterface({input:process.stdin,output:process.stdout});n=`${n} (y/N) `;let o=e?Zn.bold.yellow(n):n,r=yield t.question(o);return t.close(),r.toLowerCase()==="y"})}var Fe="momentic",ke="modules",nr="fixtures",Ue=Fe,dt=lo(Fe,ke),ut=lo(Fe,nr);function rr(n){return tr(n)&&or(n).isDirectory()}function mt(n=!1){return l(this,null,function*(){rr(Ue)||(!n&&!(yield de(`A '${Fe}' folder was not found in the current directory. Setup the required Momentic folder structure?`))&&(S.error("Setup cancelled"),process.exit(1)),ct(Ue),ct(dt),ct(ut),S.info("Setup complete!"))})}import{registry as pt}from"playwright-core/lib/server";function ir(n){let e=[],t=[];for(let o of n){let r=pt.findExecutable(o);!r||r.installType==="none"?e.push(o):t.push(r)}return t}function co(){return l(this,null,function*(){let n=ir(["chromium"]);yield pt.installDeps(n,!1),yield pt.install(n,!1)})}import{Argument as ht,Option as Ae}from"commander";var $e=new Ae("--api-key <key>","API key for authentication. If not supplied, attempts to read the MOMENTIC_API_KEY env var.").env("MOMENTIC_API_KEY").makeOptionMandatory(!0),He=new Ae("--server <server>","Momentic server to use. Leave unchanged unless using Momentic on-premise.").default("https://api.momentic.ai"),gt=new Ae("-y, --yes","Skip confirmation prompts.").env("CI").default(!1),uo=new Ae("--no-report","Skip reporting test results to Momentic Cloud when running with the --local flag.").default(!0),ft=new Ae("-a --all","Select all tests in your organization from Momentic Cloud. Cannot be used together with <tests> arguments.").default(!1).preset(!0),mo=new ht("<tests...>",`One or more test paths to pull from Momentic Cloud.
|
|
12
|
+
|
|
13
|
+
A test path is a lowercased version of your test name where spaces are replaced with underscores: 'npx momentic pull hello-world'.`).argOptional(),po=new ht("<tests...>",`One or more test identifiers.
|
|
14
|
+
|
|
15
|
+
To use tests stored on your local file system, pass '--local' and specify file paths to Momentic YAML files or folders that exist locally: 'npx momentic run --local momentic/hello-world.yaml'.
|
|
16
|
+
|
|
17
|
+
To use tests stored remotely on Momentic Cloud, pass '--remote' and specify one or more test paths. A test path is a lowercased version of your test name where spaces are replaced with underscores: 'npx momentic run --remote hello-world'.`).argRequired(),ho=new ht("<paths...>","File paths pointing to one or more YAML files containing Momentic tests, or a directory of Momentic YAML files.");import{existsSync as ar,writeFileSync as go}from"fs";import{join as fo}from"path";function wo(o){return l(this,arguments,function*({testsToFetch:n,client:e,all:t}){let{tests:r,modules:s}=yield e.getTestYAMLExport({paths:n,all:t}),a=Object.keys(r).length;for(let[c,d]of Object.entries(r)){let h=fo(Ue,So(c));if(!(yield yo(h))){S.error("Pull cancelled");return}go(h,d,"utf-8"),S.info(`Wrote '${h}'`)}let i=Object.keys(s).length;for(let[c,d]of Object.entries(s)){let h=fo(dt,So(c));if(!(yield yo(h))){S.error("Pull cancelled");return}go(h,d,"utf-8"),S.info(`Wrote '${h}'`)}S.info(`Pulled ${a} test${a>1?"s":""}${i?` and ${i} module${i>1?"s":""}`:""}!`)})}function yo(n){return l(this,null,function*(){return ar(n)?de(`File '${n.replace(/(\s+)/g,"\\$1")}' already exists. Overwrite existing content?`,!0):!0})}function So(n){return`${n.toLowerCase().replaceAll(" ","-")}.yaml`}import{existsSync as yt,readFileSync as St,readdirSync as Ao,statSync as bo}from"fs";import Ee from"path";function Eo(n){return l(this,null,function*(){let e=To(n.paths);S.info(`Found ${e.size} test(s) to push:`),e.forEach(r=>S.info(` - ${r}`)),S.info("Loading file contents and resolving dependent modules");let t=Array.from(e).map(wt),o=new Set(t.flatMap(r=>Object.keys(r.modules)));if(S.info(`Resolved ${e.size} test(s) and ${o.size} module(s)`),!n.yes&&!(yield de("Pushing tests overwrites tests on production and will instantly affect scheduled runs. Continue?",!0))){S.error("Push cancelled");return}yield n.client.updateTestWithYAML(t),S.info("Update successful!")})}function wt(n){let e;try{e=St(n,"utf8"),e=e.replace(/\r\n|\r/g,`
|
|
18
|
+
`)}catch(a){throw new Error(`Could not read test file ${n}: ${a}`)}let t=new Set,o=/moduleId: (.*)/g,r;for(;(r=o.exec(e))!==null;)t.add(r[1].trim());let s={};if(t.size>0){let a=lr(n);t.forEach(i=>{s[i]||(s[i]=cr(a,i))})}return{test:e,modules:s}}function lr(n){let e=n;for(;e!=="/";){let t=Ee.join(e,ke);if(yt(t))return t;e=Ee.dirname(e)}throw new Error(`No '${ke}' directory found in the path ${n} or any of its parents`)}function cr(n,e){let t=Ao(n);for(let o of t){let r=Ee.join(n,o),s=St(r,"utf8");if(s.includes(e))return s}throw new Error(`Could not find module file for module ${e} in ${n}`)}function dr(n){if(!n.endsWith(".yaml"))return!1;let e=St(n,"utf8");return e.includes("momentic/test")?e.match(/localOnly:.*true/)?(S.warn(`Skipping local-only test: ${n}`),!1):!0:(S.warn(`Skipping YAML that is not a Momentic test: ${n}`),!1)}function To(n,e=new Set){for(let t of n){let o=Ee.resolve(t);if(o&&yt(o)&&bo(o).isDirectory()){let r=Ao(o).map(s=>Ee.join(o,s));To(r,e);continue}if(o.endsWith(".yaml")){if(!yt(o)||!bo(o).isFile())throw new Error(`File not found or unreadable: ${o}`);if(!dr(o))continue;e.add(o)}}return e}import{existsSync as ln,statSync as ri}from"fs";import ii from"wait-on";import{distance as Nr}from"fastest-levenshtein";import{chromium as Mr,devices as Vo}from"playwright";import{addExtra as Dr}from"playwright-extra";import Pr from"puppeteer-extra-plugin-recaptcha";import _r from"puppeteer-extra-plugin-stealth";var bt={js:'var K=Object.defineProperty;var P=Object.getOwnPropertySymbols;var z=Object.prototype.hasOwnProperty,B=Object.prototype.propertyIsEnumerable;var H=(t,e,n)=>e in t?K(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,D=(t,e)=>{for(var n in e||(e={}))z.call(e,n)&&H(t,n,e[n]);if(P)for(var n of P(e))B.call(e,n)&&H(t,n,e[n]);return t};var g=(t,e,n)=>(H(t,typeof e!="symbol"?e+"":e,n),n);var _=(t,e,n)=>new Promise((o,r)=>{var i=s=>{try{d(n.next(s))}catch(l){r(l)}},a=s=>{try{d(n.throw(s))}catch(l){r(l)}},d=s=>s.done?o(s.value):Promise.resolve(s.value).then(i,a);d((n=n.apply(t,e)).next())});var E=t=>function(e){return e&&e.isTrusted?t.apply(this,arguments):!0};globalThis.forTrusted==null&&(globalThis.forTrusted=E);var k={create(t,e,n,o){return{bottom:o,top:e,left:t,right:n,width:n-t,height:o-e}},copy(t){return{bottom:t.bottom,top:t.top,left:t.left,right:t.right,width:t.width,height:t.height}},translate(t,e,n){return e==null&&(e=0),n==null&&(n=0),{bottom:t.bottom+n,top:t.top+n,left:t.left+e,right:t.right+e,width:t.width,height:t.height}},subtract(t,e){return e=this.create(Math.max(t.left,e.left),Math.max(t.top,e.top),Math.min(t.right,e.right),Math.min(t.bottom,e.bottom)),e.width<0||e.height<0?[k.copy(t)]:[this.create(t.left,t.top,e.left,e.top),this.create(e.left,t.top,e.right,e.top),this.create(e.right,t.top,t.right,e.top),this.create(t.left,e.top,e.left,e.bottom),this.create(e.right,e.top,t.right,e.bottom),this.create(t.left,e.bottom,e.left,t.bottom),this.create(e.left,e.bottom,e.right,t.bottom),this.create(e.right,e.bottom,t.right,t.bottom)].filter(o=>o.height>0&&o.width>0)},intersects(t,e){return t.right>e.left&&t.left<e.right&&t.bottom>e.top&&t.top<e.bottom},intersectsStrict(t,e){return t.right>=e.left&&t.left<=e.right&&t.bottom>=e.top&&t.top<=e.bottom},equals(t,e){for(let n of["top","bottom","left","right","width","height"])if(t[n]!==e[n])return!1;return!0},intersect(t,e){return this.create(Math.max(t.left,e.left),Math.max(t.top,e.top),Math.min(t.right,e.right),Math.min(t.bottom,e.bottom))}};var N={_browserInfoLoaded:!0,_firefoxVersion:null,_isFirefox:!1,isFirefox(){if(!this._browserInfoLoaded)throw Error("browserInfo has not yet loaded.");return this._isFirefox},firefoxVersion(){if(!this._browserInfoLoaded)throw Error("browserInfo has not yet loaded.");return this._firefoxVersion},isString(t){return typeof t=="string"||t instanceof String}};var f={isReady(){return document.readyState!=="loading"},documentReady:function(){let t=document.readyState!=="loading",e=[];if(!t){let n;globalThis.addEventListener("DOMContentLoaded",n=E(function(){globalThis.removeEventListener("DOMContentLoaded",n,!0),t=!0;for(let o of e)o();e=null}),!0)}return function(n){if(t)return n();e.push(n)}}(),documentComplete:function(){let t=document.readyState==="complete",e=[];if(!t){let n;globalThis.addEventListener("load",n=E(function(o){if(o.target===document){globalThis.removeEventListener("load",n,!0),t=!0;for(let r of e)r();e=null}}),!0)}return function(n){t?n():e.push(n)}}(),createElement(t){let e=document.createElement(t);return e instanceof HTMLElement?(this.createElement=n=>document.createElement(n),e):(this.createElement=n=>document.createElementNS("http://www.w3.org/1999/xhtml",n),this.createElement(t))},addElementsToPage(t,e){let n=this.createElement("div");e.id!=null&&(n.id=e.id),e.className!=null&&(n.className=e.className);for(let o of t)n.appendChild(o);return document.body.appendChild(n),n},removeElement(t){return t.parentNode.removeChild(t)},isTopFrame(){return globalThis.top===globalThis.self},makeXPath(t){let e=[];for(let n of t)e.push(".//"+n,".//xhtml:"+n);return e.join(" | ")},evaluateXPath(t,e){let n=document.webkitIsFullScreen?document.webkitFullscreenElement:document.documentElement,o=function(r){return r==="xhtml"?"http://www.w3.org/1999/xhtml":null};return document.evaluate(t,n,o,e,null)},getVisibleClientRect(t,e){let n;e==null&&(e=!1);let o=(()=>{let i=[];for(n of t.getClientRects())i.push(k.copy(n));return i})(),r=function(){let i=window.getComputedStyle(t,null),a=i.getPropertyValue("display").indexOf("inline")===0&&i.getPropertyValue("font-size")==="0px";return r=()=>a,a};for(n of o){let i;if((n.width===0||n.height===0)&&e)for(let a of Array.from(t.children)){i=window.getComputedStyle(a,null);let d=i.getPropertyValue("position");if(i.getPropertyValue("float")==="none"&&!["absolute","fixed"].includes(d)&&!(n.height===0&&r()&&i.getPropertyValue("display").indexOf("inline")===0))continue;let s=this.getVisibleClientRect(a,!0);if(!(s===null||s.width<3||s.height<3))return s}else{if(n=this.cropRectToVisible(n),n===null||n.width<3||n.height<3||(i=window.getComputedStyle(t,null),i.getPropertyValue("visibility")!=="visible"))continue;return n}}return null},cropRectToVisible(t){let e=k.create(Math.max(t.left,0),Math.max(t.top,0),t.right,t.bottom);return e.top>=window.innerHeight-4||e.left>=window.innerWidth-4?null:e},getClientRectsForAreas(t,e){let n=[];for(let o of e){let r,i,a,d,s=o.coords.split(",").map(p=>parseInt(p,10)),l=o.shape.toLowerCase();if(["rect","rectangle"].includes(l))s.length==4&&([r,a,i,d]=s);else if(["circle","circ"].includes(l)){if(s.length==3){let[p,w,v]=s,u=v/Math.sqrt(2);r=p-u,i=p+u,a=w-u,d=w+u}}else l==="default"?s.length==2&&([r,a,i,d]=[0,0,t.width,t.height]):s.length>=4&&([r,a,i,d]=s);let c=k.translate(k.create(r,a,i,d),t.left,t.top);c=this.cropRectToVisible(c),c&&!isNaN(c.top)&&!isNaN(c.left)&&!isNaN(c.width)&&!isNaN(c.height)&&n.push({element:o,rect:c})}return n},isSelectable(t){if(!(t instanceof Element))return!1;let e=["button","checkbox","color","file","hidden","image","radio","reset","submit"];return t.nodeName.toLowerCase()==="input"&&e.indexOf(t.type)===-1||t.nodeName.toLowerCase()==="textarea"||t.isContentEditable},isEditable(t){return this.isSelectable(t)||(t.nodeName!=null?t.nodeName.toLowerCase():void 0)==="select"},isEmbed(t){let e=t.nodeName!=null?t.nodeName.toLowerCase():null;return["embed","object"].includes(e)},isFocusable(t){return t&&(this.isEditable(t)||this.isEmbed(t))},isDOMDescendant(t,e){let n=e;for(;n!==null;){if(n===t)return!0;n=n.parentNode}return!1},isSelected(t){let e=document.getSelection();if(t.isContentEditable){let n=e.anchorNode;return n&&this.isDOMDescendant(t,n)}else if(f.getSelectionType(e)==="Range"&&e.isCollapsed){let n=e.anchorNode.childNodes[e.anchorOffset];return t===n}else return!1},simulateSelect(t){if(t===document.activeElement&&f.isEditable(document.activeElement))return handlerStack.bubbleEvent("click",{target:t});if(t.focus(),t.tagName.toLowerCase()!=="textarea"||t.value.indexOf(`\n`)<0)try{if(t.selectionStart===0&&t.selectionEnd===0)return t.setSelectionRange(t.value.length,t.value.length)}catch(e){}},simulateClick(t,e){e==null&&(e={});let n=["mouseover","mousedown","mouseup","click"],o=[];for(let r of n){let i=this.simulateMouseEvent(r,t,e);o.push(i)}return o},simulateMouseEvent(t,e,n){if(n==null&&(n={}),t==="mouseout"){if(e==null&&(e=this.lastHoveredElement),this.lastHoveredElement=void 0,e==null)return}else t==="mouseover"&&(this.simulateMouseEvent("mouseout",void 0,n),this.lastHoveredElement=e);let o=new MouseEvent(t,{bubbles:!0,cancelable:!0,composed:!0,view:window,detail:1,ctrlKey:n.ctrlKey,altKey:n.altKey,shiftKey:n.shiftKey,metaKey:n.metaKey});return e.dispatchEvent(o)},simulateClickDefaultAction(t,e){let n;if(e==null&&(e={}),(t.tagName!=null?t.tagName.toLowerCase():void 0)!=="a"||!t.href)return;let{ctrlKey:o,shiftKey:r,metaKey:i,altKey:a}=e;KeyboardUtils.platform==="Mac"?n=i===!0&&o===!1:n=i===!1&&o===!0,n?chrome.runtime.sendMessage({handler:"openUrlInNewTab",url:t.href,active:r===!0}):r===!0&&i===!1&&o===!1&&a===!1?chrome.runtime.sendMessage({handler:"openUrlInNewWindow",url:t.href}):t.target==="_blank"&&chrome.runtime.sendMessage({handler:"openUrlInNewTab",url:t.href,active:!0})},simulateHover(t,e){return e==null&&(e={}),this.simulateMouseEvent("mouseover",t,e)},simulateUnhover(t,e){return e==null&&(e={}),this.simulateMouseEvent("mouseout",t,e)},addFlashRect(t){let e=this.createElement("div");return e.classList.add("vimiumReset"),e.classList.add("vimiumFlash"),e.style.left=t.left+"px",e.style.top=t.top+"px",e.style.width=t.width+"px",e.style.height=t.height+"px",document.documentElement.appendChild(e),e},getViewportTopLeft(){let t=document.documentElement,e=getComputedStyle(t),n=t.getBoundingClientRect();if(e.position==="static"&&!/content|paint|strict/.test(e.contain||"")){let o=parseInt(e.marginTop),r=parseInt(e.marginLeft);return{top:-n.top+o,left:-n.left+r}}else{let o,r;return N.isFirefox()?(r=parseInt(e.borderTopWidth),o=parseInt(e.borderLeftWidth)):{clientTop:r,clientLeft:o}=t,{top:-n.top-r,left:-n.left-o}}},suppressPropagation(t){t.stopImmediatePropagation()},suppressEvent(t){t.preventDefault(),this.suppressPropagation(t)},consumeKeyup:function(){let t=null;return function(e,n=null,o){if(!e.repeat){t!=null&&handlerStack.remove(t);let{code:r}=e;t=handlerStack.push({_name:"dom_utils/consumeKeyup",keyup(i){return i.code!==r||(this.remove(),o?f.suppressPropagation(i):f.suppressEvent(i)),handlerStack.continueBubbling},blur(i){return i.target===window&&this.remove(),handlerStack.continueBubbling}})}return typeof n=="function"&&n(),o?(f.suppressPropagation(e),handlerStack.suppressPropagation):(f.suppressEvent(e),handlerStack.suppressEvent)}}(),getSelectionType(t){return t==null&&(t=document.getSelection()),t.type?t.type:t.rangeCount===0?"None":t.isCollapsed?"Caret":"Range"},getElementWithFocus(t,e){let n,o=n=t.getRangeAt(0);f.getSelectionType(t)==="Range"&&(o=n.cloneRange(),o.collapse(e)),n=o.startContainer,n.nodeType===1&&(n=n.childNodes[o.startOffset]);let r=n;for(;r&&r.nodeType!==1;)r=r.previousSibling;return n=r||(n!=null?n.parentNode:void 0),n},getSelectionFocusElement(){let t=window.getSelection(),e=t.focusNode;return e==null?null:(e===t.anchorNode&&t.focusOffset===t.anchorOffset&&(e=e.childNodes[t.focusOffset]||e),e.nodeType!==Node.ELEMENT_NODE?e.parentElement:e)},getContainingElement(t){return(typeof t.getDestinationInsertionPoints=="function"?t.getDestinationInsertionPoints()[0]:void 0)||t.parentElement},windowIsTooSmall(){return window.innerWidth<3||window.innerHeight<3},injectUserCss(){let t=document.createElement("style");t.type="text/css",t.textContent=Settings.get("userDefinedLinkHintCss"),document.head.appendChild(t)}};var O={MAX_CONTENT_LENGTH:1e3,MAX_ATTRIBUTE_LENGTH:500,MAX_NUM_DATA_ATTRIBUTES:10,commonAttributes:["id","className","title","aria-label","aria-labelledby"],attributeNamesMapping:new Map([["a",["href","title","rel","target"]],["label",["for"]],["input",["type","name","placeholder","checked","maximumLength"]],["textarea",["placeholder","maximumLength"]],["button",["type"]],["select",["name","multiple"]],["div",["role"]],["iframe",["src"]],["img",["src","alt"]]]),describe(t){var r,i;let e={};this.addAttributes(t,this.commonAttributes,e);let n=((i=(r=t.tagName).toLowerCase)==null?void 0:i.call(r))||"";this.attributeNamesMapping.has(n)&&this.addAttributes(t,this.attributeNamesMapping.get(n),e),this.addDataAttrs(t,e);let o=this.getContent(t);return this.additionalHandling(t,D({tag:n,attributes:e},o&&{content:o}))},getContent(t){var n,o;let e=((o=(n=t.tagName).toLowerCase)==null?void 0:o.call(n))||"";return["input","textarea"].includes(e)?t.value:["div","iframe","img","body"].includes(e)?null:(["a","button","select","label"].includes(e),t.innerText)},additionalHandling(t,e){var o,r;if((((r=(o=t.tagName).toLowerCase)==null?void 0:r.call(o))||"")=="label"&&t.hasAttribute("for")){let i=t.getAttribute("for"),a=document.getElementById(i);a&&(e.target=this.describe(a))}return e},addAttributes(t,e,n){n||(n={});for(let o of e)t.hasAttribute(o)&&(n[o]=t.getAttribute(o).substring(0,this.MAX_ATTRIBUTE_LENGTH));return n},addDataAttrs(t,e){let n=0;for(let o in t.dataset)if(e[`data-${o}`]=t.dataset[o].substring(0,this.MAX_ATTRIBUTE_LENGTH),n++,n>this.MAX_NUM_DATA_ATTRIBUTES)return e;return e}};var x=null,C=()=>G()||document.scrollingElement||document.body,W=function(t){return t?t<0?-1:1:0},U={x:{axisName:"scrollLeft",max:"scrollWidth",viewSize:"clientWidth"},y:{axisName:"scrollTop",max:"scrollHeight",viewSize:"clientHeight"}},X=function(t,e,n){if(N.isString(n)){let o=n;return o==="viewSize"&&t===C()?e==="x"?window.innerWidth:window.innerHeight:t[U[e][o]]}else return n},V=function(t,e,n){let o=U[e].axisName,r=t[o];if(t.scrollBy){let i={behavior:"instant"};i[e==="x"?"left":"top"]=n,t.scrollBy(i)}else t[o]+=n;return t[o]!==r},q=function(t,e){let n=window.getComputedStyle(t);return!(n.getPropertyValue(`overflow-${e}`)==="hidden"||["hidden","collapse"].includes(n.getPropertyValue("visibility"))||n.getPropertyValue("display")==="none")},T=function(t,e,n,o){let r=o*X(t,e,n)||-1;return r=W(r),V(t,e,r)&&V(t,e,-r)},$=function(t,e,n,o){return e==null&&(e="y"),n==null&&(n=1),o==null&&(o=1),T(t,e,n,o)&&q(t,e)},j=function(t=null){let e;if(!t){let n=C();if(T(n,"y",1,1)||T(n,"y",-1,1))return n;t=document.body||C()}if(T(t,"y",1,1)||T(t,"y",-1,1))return t;{let n=Array.from(t.children).map(o=>({element:o,rect:f.getVisibleClientRect(o)})).filter(o=>o.rect);n.map(o=>o.area=o.rect.width*o.rect.height);for(e of n.sort((o,r)=>r.area-o.area)){let o=j(e.element);if(o)return o}return null}},L={init(){x=null},isScrollableElement(t){return x||(x=C()&&j()||C()),t!==x&&$(t)}},G=function(){let t=J[window.location.host];if(t)return document.querySelector(t)},J={"twitter.com":"div.permalink-container div.permalink[role=main]","reddit.com":"#overlayScrollContainer","new.reddit.com":"#overlayScrollContainer","www.reddit.com":"#overlayScrollContainer","web.telegram.org":".MessageList"};window.Scroller=L;var A=function(){let t=null;return f.documentReady(()=>t=document.hasFocus()),globalThis.addEventListener("focus",E(function(e){return e.target===window&&(t=!0),!0}),!0),globalThis.addEventListener("blur",E(function(e){return e.target===window&&(t=!1),!0}),!0),()=>t}();Object.assign(globalThis,{windowIsFocused:A});var R=class{constructor(e){g(this,"element");g(this,"image");g(this,"rect");g(this,"linkText");g(this,"showLinkText");g(this,"reason");g(this,"secondClassCitizen");g(this,"possibleFalsePositive");Object.seal(this),e&&Object.assign(this,e)}},M={getLocalHintsForElement(t){var p,w,v;let e=((w=(p=t.tagName).toLowerCase)==null?void 0:w.call(p))||"",n=!1,o=!1,r=!1,i=[],a=[],d=null;if(e==="img"){let u=t.getAttribute("usemap");if(u){let h=t.getClientRects();u=u.replace(/^#/,"").replace(\'"\',\'\\\\"\');let m=document.querySelector(`map[name="${u}"]`);if(m&&h.length>0){n=!0;let y=m.getElementsByTagName("area"),S=f.getClientRectsForAreas(h[0],y);S=S.map(F=>Object.assign(F,{image:t})),a.push(...S)}}}let s=t.getAttribute("aria-disabled");if(s&&["","true"].includes(s.toLowerCase()))return[];if(this.checkForAngularJs||(this.checkForAngularJs=function(){if(document.getElementsByClassName("ng-scope").length===0)return()=>!1;{let h=[];for(let m of["","data-","x-"])for(let y of["-",":","_"])h.push(`${m}ng${y}click`);return function(m){for(let y of h)if(m.hasAttribute(y))return!0;return!1}}}()),n||(n=this.checkForAngularJs(t)),t.hasAttribute("onclick"))n=!0;else{let u=t.getAttribute("role"),h=["button","tab","link","checkbox","menuitem","menuitemcheckbox","menuitemradio","radio"];if(u!=null&&h.includes(u.toLowerCase()))n=!0;else{let m=t.getAttribute("contentEditable");m!=null&&["","contenteditable","true","plaintext-only"].includes(m.toLowerCase())&&(n=!0)}}if(!n&&t.hasAttribute("jsaction")){let u=t.getAttribute("jsaction").split(";");for(let h of u){let m=h.trim().split(":");if(m.length>=1&&m.length<=2){let[y,S,F]=m.length===1?["click",...m[0].trim().split("."),"_"]:[m[0],...m[1].trim().split("."),"_"];n||(n=y==="click"&&S!=="none"&&F!=="_")}}}switch(e){case"a":n=!0;break;case"textarea":n||(n=!t.disabled&&!t.readOnly);break;case"input":n||(n=!(((v=t.getAttribute("type"))==null?void 0:v.toLowerCase())=="hidden"||t.disabled||t.readOnly&&f.isSelectable(t)));break;case"button":case"select":n||(n=!t.disabled);break;case"object":case"embed":n=!0;break;case"label":n||(n=t.control!=null&&!t.control.disabled&&this.getLocalHintsForElement(t.control).length===0);break;case"body":n||(n=t===document.body&&!A()&&window.innerWidth>3&&window.innerHeight>3&&(document.body!=null?document.body.tagName.toLowerCase():void 0)!=="frameset"?d="Frame.":void 0),n||(n=t===document.body&&A()&&L.isScrollableElement(t)?d="Scroll.":void 0);break;case"img":n||(n=["zoom-in","zoom-out"].includes(t.style.cursor));break;case"div":case"ol":case"ul":n||(n=t.clientHeight<t.scrollHeight&&L.isScrollableElement(t)?d="Scroll.":void 0);break;case"details":n=!0,d="Open.";break}let l=t.getAttribute("class");!n&&(l!=null&&l.toLowerCase().includes("button"))&&(n=!0,r=!0);let c=t.getAttribute("tabindex"),b=c?parseInt(c):-1;if(!n&&!(b<0)&&!isNaN(b)&&(n=!0,o=!0),n)if(a.length>0){let u=a.map(h=>new R({element:h.element,image:t,rect:h.rect,secondClassCitizen:o,possibleFalsePositive:r,reason:d}));i.push(...u)}else{let u=f.getVisibleClientRect(t,!0);if(u!==null){let h=new R({element:t,rect:u,secondClassCitizen:o,possibleFalsePositive:r,reason:d});i.push(h)}}return i},getElementFromPoint(t,e,n,o){n==null&&(n=document),o==null&&(o=[]);let r=n.elementsFromPoint?n.elementsFromPoint(t,e)[0]:n.elementFromPoint(t,e);return o.includes(r)?r:(o.push(r),r&&r.shadowRoot?M.getElementFromPoint(t,e,r.shadowRoot,o):r)},getLocalHints(t){if(!document.body)return[];let e=(s,l)=>{l==null&&(l=[]);for(let c of Array.from(s.querySelectorAll("*")))l.push(c),c.shadowRoot&&e(c.shadowRoot,l);return l},n=e(document.body),o=[];for(let s of Array.from(n))if(!t||s.href){let l=this.getLocalHintsForElement(s);o.push(...l)}o=o.reverse();let r=[1,2,3];o=o.filter((s,l)=>{if(!s.possibleFalsePositive)return!0;let b=Math.max(0,l-6);for(;b<l;){let p=o[b].element;for(let w of r)if(p=p==null?void 0:p.parentElement,p===s.element)return!1;b+=1}return!0});let i=o.filter(s=>{if(s.secondClassCitizen)return!1;let l=s.rect,c=M.getElementFromPoint(l.left+l.width*.5,l.top+l.height*.5);if(c&&(s.element.contains(c)||c.contains(s.element))||s.element.localName=="area"&&c==s.image)return!0;let p=[l.top+.1,l.bottom-.1],w=[l.left+.1,l.right-.1];for(let v of p)for(let u of w){let h=M.getElementFromPoint(u,v);if(h&&(s.element.contains(h)||h.contains(s.element)))return!0}});i.reverse();let{top:a,left:d}=f.getViewportTopLeft();for(let s of i)s.rect.top+=a,s.rect.left+=d;return i}};var I=class{constructor(){this.hints=null;this.hintMarkers=null;this.markersDiv=null;this.enrichedMarkers=null}reset(){this.removeMarkers(),this.hints=null,this.hintMarkers=null,this.markersDiv=null}capture(){return _(this,null,function*(){this.reset(),this.createMarkers(),this.displayMarkers()})}createMarkers(){this.hints=M.getLocalHints(),this.hintMarkers=new Map,this.hints.forEach((e,n)=>{var i,a;let o=f.createElement("div"),r=(a=(i=e.element.attributes["data-momentic-id"])==null?void 0:i.value)!=null?a:void 0;if(!r){console.warn(`[Momentic] No data-momentic-id found for interactive element ${e.element.outerHTML}`);return}o.style.left=e.rect.left+"px",o.style.top=e.rect.top+"px",o.style.zIndex=214e7+n,o.className="vimiumReset internalVimiumHintMarker vimiumHintMarker",Z(o,r),this.hintMarkers.set(r,{hint:e,marker:o})})}enrichMarkers(){if(this.hintMarkers){this.enrichedMarkers=[];for(let[e,n]of this.hintMarkers)this.enrichedMarkers.push(Object.assign(O.describe(n.hint.element),{hintString:e}))}}displayMarkers(){this.hintMarkers&&(this.markersDiv||(this.markersDiv=f.addElementsToPage(Array.from(this.hintMarkers.values()).map(e=>e.marker),{id:"vimiumHintMarkerContainer",className:"vimiumReset"})))}removeMarkers(){this.markersDiv&&(f.removeElement(this.markersDiv),this.markersDiv=null)}toggleMarkers(){this.markersDiv?this.removeMarkers():this.displayMarkers()}},Z=(t,e)=>{for(let n of e){let o=document.createElement("span");o.className="vimiumReset",o.textContent=n,t.appendChild(o)}};window.HintManager=I;\n',css:`/* Reproduced from https://github.com/philc/vimium/blob/master/content_scripts/vimium.css */
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* Many CSS class names in this file use the verbose "vimiumXYZ" as the class name. This is so we
|
|
22
|
+
* don't use the same CSS class names that the page is using, so the page's CSS doesn't mess with
|
|
23
|
+
* the style of our Vimium dialogs.
|
|
24
|
+
*
|
|
25
|
+
* The z-indexes of Vimium elements are very large, because we always want them to show on top. We
|
|
26
|
+
* know that Chrome supports z-index values up to about 2^31. The values we use are large numbers
|
|
27
|
+
* approaching that bound. However, we must leave headroom for link hints. Hint marker z-indexes
|
|
28
|
+
* start at 2140000001.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
* This vimiumReset class can be added to any of our UI elements to give it a clean slate. This is
|
|
33
|
+
* useful in case the page has declared a broad rule like "a { padding: 50px; }" which will mess up
|
|
34
|
+
* our UI. These declarations contain more specifiers than necessary to increase their specificity
|
|
35
|
+
* (precedence).
|
|
36
|
+
*/
|
|
37
|
+
.vimiumReset,
|
|
38
|
+
div.vimiumReset,
|
|
39
|
+
span.vimiumReset,
|
|
40
|
+
table.vimiumReset,
|
|
41
|
+
a.vimiumReset,
|
|
42
|
+
a:visited.vimiumReset,
|
|
43
|
+
a:link.vimiumReset,
|
|
44
|
+
a:hover.vimiumReset,
|
|
45
|
+
td.vimiumReset,
|
|
46
|
+
tr.vimiumReset {
|
|
47
|
+
background: none;
|
|
48
|
+
border: none;
|
|
49
|
+
bottom: auto;
|
|
50
|
+
box-shadow: none;
|
|
51
|
+
color: black;
|
|
52
|
+
cursor: auto;
|
|
53
|
+
display: inline;
|
|
54
|
+
float: none;
|
|
55
|
+
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
|
|
56
|
+
font-size: inherit;
|
|
57
|
+
font-style: normal;
|
|
58
|
+
font-variant: normal;
|
|
59
|
+
font-weight: normal;
|
|
60
|
+
height: auto;
|
|
61
|
+
left: auto;
|
|
62
|
+
letter-spacing: 0;
|
|
63
|
+
line-height: 100%;
|
|
64
|
+
margin: 0;
|
|
65
|
+
max-height: none;
|
|
66
|
+
max-width: none;
|
|
67
|
+
min-height: 0;
|
|
68
|
+
min-width: 0;
|
|
69
|
+
opacity: 1;
|
|
70
|
+
padding: 0;
|
|
71
|
+
position: static;
|
|
72
|
+
right: auto;
|
|
73
|
+
text-align: left;
|
|
74
|
+
text-decoration: none;
|
|
75
|
+
text-indent: 0;
|
|
76
|
+
text-shadow: none;
|
|
77
|
+
text-transform: none;
|
|
78
|
+
top: auto;
|
|
79
|
+
vertical-align: baseline;
|
|
80
|
+
white-space: normal;
|
|
81
|
+
width: auto;
|
|
82
|
+
z-index: 2140000000; /* Vimium's reference z-index value. */
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
thead.vimiumReset,
|
|
86
|
+
tbody.vimiumReset {
|
|
87
|
+
display: table-header-group;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
tbody.vimiumReset {
|
|
91
|
+
display: table-row-group;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Linkhints CSS */
|
|
95
|
+
|
|
96
|
+
div.internalVimiumHintMarker {
|
|
97
|
+
position: absolute;
|
|
98
|
+
display: block;
|
|
99
|
+
top: -1px;
|
|
100
|
+
left: -1px;
|
|
101
|
+
white-space: nowrap;
|
|
102
|
+
overflow: hidden;
|
|
103
|
+
font-size: 11px;
|
|
104
|
+
padding: 1px 3px 0px 3px;
|
|
105
|
+
background: linear-gradient(to bottom, #fff785 0%, #ffc542 100%);
|
|
106
|
+
border: solid 1px #c38a22;
|
|
107
|
+
border-radius: 3px;
|
|
108
|
+
box-shadow: 0px 3px 7px 0px rgba(0, 0, 0, 0.3);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
div.internalVimiumHintMarker span {
|
|
112
|
+
color: #302505;
|
|
113
|
+
font-family: Helvetica, Arial, sans-serif;
|
|
114
|
+
font-weight: bold;
|
|
115
|
+
font-size: 11px;
|
|
116
|
+
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
div.internalVimiumHintMarker > .matchingCharacter {
|
|
120
|
+
color: #d4ac3a;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
div > .vimiumActiveHintMarker span {
|
|
124
|
+
color: #a07555 !important;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* Input hints CSS */
|
|
128
|
+
|
|
129
|
+
div.internalVimiumInputHint {
|
|
130
|
+
position: absolute;
|
|
131
|
+
display: block;
|
|
132
|
+
background-color: rgba(255, 247, 133, 0.3);
|
|
133
|
+
border: solid 1px #c38a22;
|
|
134
|
+
pointer-events: none;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
div.internalVimiumSelectedInputHint {
|
|
138
|
+
background-color: rgba(255, 102, 102, 0.3);
|
|
139
|
+
border: solid 1px #993333 !important;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
div.internalVimiumSelectedInputHint span {
|
|
143
|
+
color: white !important;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Frame Highlight Marker CSS*/
|
|
147
|
+
div.vimiumHighlightedFrame {
|
|
148
|
+
position: fixed;
|
|
149
|
+
top: 0px;
|
|
150
|
+
left: 0px;
|
|
151
|
+
width: 100%;
|
|
152
|
+
height: 100%;
|
|
153
|
+
padding: 0px;
|
|
154
|
+
margin: 0px;
|
|
155
|
+
border: 5px solid yellow;
|
|
156
|
+
box-sizing: border-box;
|
|
157
|
+
pointer-events: none;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* Help Dialog CSS */
|
|
161
|
+
|
|
162
|
+
iframe.vimiumHelpDialogFrame {
|
|
163
|
+
background-color: rgba(10, 10, 10, 0.6);
|
|
164
|
+
padding: 0px;
|
|
165
|
+
top: 0px;
|
|
166
|
+
left: 0px;
|
|
167
|
+
width: 100%;
|
|
168
|
+
height: 100%;
|
|
169
|
+
display: block;
|
|
170
|
+
position: fixed;
|
|
171
|
+
border: none;
|
|
172
|
+
z-index: 2139999997; /* Three less than the reference value. */
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
div#vimiumHelpDialogContainer {
|
|
176
|
+
opacity: 1;
|
|
177
|
+
background-color: white;
|
|
178
|
+
border: 2px solid #b3b3b3;
|
|
179
|
+
border-radius: 6px;
|
|
180
|
+
width: 840px;
|
|
181
|
+
max-width: calc(100% - 100px);
|
|
182
|
+
max-height: calc(100% - 100px);
|
|
183
|
+
margin: 50px auto;
|
|
184
|
+
overflow-y: auto;
|
|
185
|
+
overflow-x: auto;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
div#vimiumHelpDialog {
|
|
189
|
+
min-width: 600px;
|
|
190
|
+
padding: 8px 12px;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
span#vimiumTitle,
|
|
194
|
+
span#vimiumTitle span,
|
|
195
|
+
span#vimiumTitle * {
|
|
196
|
+
font-size: 20px;
|
|
197
|
+
}
|
|
198
|
+
#vimiumTitle {
|
|
199
|
+
display: block;
|
|
200
|
+
line-height: 130%;
|
|
201
|
+
white-space: nowrap;
|
|
202
|
+
}
|
|
203
|
+
td.vimiumHelpDialogTopButtons {
|
|
204
|
+
width: 100%;
|
|
205
|
+
text-align: right;
|
|
206
|
+
}
|
|
207
|
+
#helpDialogOptionsPage,
|
|
208
|
+
#helpDialogWikiPage {
|
|
209
|
+
font-size: 14px;
|
|
210
|
+
padding-left: 5px;
|
|
211
|
+
padding-right: 5px;
|
|
212
|
+
}
|
|
213
|
+
div.vimiumColumn {
|
|
214
|
+
width: 50%;
|
|
215
|
+
float: left;
|
|
216
|
+
font-size: 11px;
|
|
217
|
+
line-height: 130%;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
div.vimiumColumn tr {
|
|
221
|
+
display: table-row;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
div.vimiumColumn td {
|
|
225
|
+
display: table-cell;
|
|
226
|
+
font-size: 11px;
|
|
227
|
+
line-height: 130%;
|
|
228
|
+
}
|
|
229
|
+
div.vimiumColumn table,
|
|
230
|
+
div.vimiumColumn td,
|
|
231
|
+
div.vimiumColumn tr {
|
|
232
|
+
padding: 0;
|
|
233
|
+
margin: 0;
|
|
234
|
+
}
|
|
235
|
+
div.vimiumColumn table {
|
|
236
|
+
width: 100%;
|
|
237
|
+
table-layout: auto;
|
|
238
|
+
}
|
|
239
|
+
div.vimiumColumn td {
|
|
240
|
+
vertical-align: top;
|
|
241
|
+
padding: 1px;
|
|
242
|
+
}
|
|
243
|
+
div#vimiumHelpDialog div.vimiumColumn tr > td:first-of-type {
|
|
244
|
+
/* This is the "key" column, e.g. "j", "gg". */
|
|
245
|
+
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
246
|
+
font-size: 14px;
|
|
247
|
+
text-align: right;
|
|
248
|
+
white-space: nowrap;
|
|
249
|
+
}
|
|
250
|
+
span.vimiumHelpDialogKey {
|
|
251
|
+
background-color: rgb(243, 243, 243);
|
|
252
|
+
color: rgb(33, 33, 33);
|
|
253
|
+
margin-left: 2px;
|
|
254
|
+
padding-top: 1px;
|
|
255
|
+
padding-bottom: 1px;
|
|
256
|
+
padding-left: 4px;
|
|
257
|
+
padding-right: 4px;
|
|
258
|
+
border-radius: 3px;
|
|
259
|
+
border: solid 1px #ccc;
|
|
260
|
+
border-bottom-color: #bbb;
|
|
261
|
+
box-shadow: inset 0 -1px 0 #bbb;
|
|
262
|
+
font-family: monospace;
|
|
263
|
+
font-size: 11px;
|
|
264
|
+
}
|
|
265
|
+
/* Make the description column as wide as it can be. */
|
|
266
|
+
div#vimiumHelpDialog div.vimiumColumn tr > td:nth-of-type(3) {
|
|
267
|
+
width: 100%;
|
|
268
|
+
}
|
|
269
|
+
div#vimiumHelpDialog div.vimiumDivider {
|
|
270
|
+
display: block;
|
|
271
|
+
height: 1px;
|
|
272
|
+
width: 100%;
|
|
273
|
+
margin: 10px auto;
|
|
274
|
+
background-color: #9a9a9a;
|
|
275
|
+
}
|
|
276
|
+
div#vimiumHelpDialog td.vimiumHelpSectionTitle {
|
|
277
|
+
padding-top: 3px;
|
|
278
|
+
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
279
|
+
font-size: 16px;
|
|
280
|
+
font-weight: bold;
|
|
281
|
+
}
|
|
282
|
+
div#vimiumHelpDialog td.vimiumHelpDescription {
|
|
283
|
+
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
284
|
+
font-size: 14px;
|
|
285
|
+
}
|
|
286
|
+
div#vimiumHelpDialog span.vimiumCopyCommandNameName {
|
|
287
|
+
font-style: italic;
|
|
288
|
+
cursor: pointer;
|
|
289
|
+
font-size: 12px;
|
|
290
|
+
}
|
|
291
|
+
/* Advanced commands are hidden by default until you show them. */
|
|
292
|
+
div#vimiumHelpDialog tr.advanced {
|
|
293
|
+
display: none;
|
|
294
|
+
}
|
|
295
|
+
div#vimiumHelpDialog.showAdvanced tr.advanced {
|
|
296
|
+
display: table-row;
|
|
297
|
+
}
|
|
298
|
+
div#vimiumHelpDialog div.advanced td:nth-of-type(3) {
|
|
299
|
+
color: #555;
|
|
300
|
+
}
|
|
301
|
+
div#vimiumHelpDialog a.closeButton {
|
|
302
|
+
font-family: 'courier new';
|
|
303
|
+
font-weight: bold;
|
|
304
|
+
color: #555;
|
|
305
|
+
text-decoration: none;
|
|
306
|
+
font-size: 24px;
|
|
307
|
+
position: relative;
|
|
308
|
+
top: 3px;
|
|
309
|
+
padding-left: 5px;
|
|
310
|
+
cursor: pointer;
|
|
311
|
+
}
|
|
312
|
+
div#vimiumHelpDialog a {
|
|
313
|
+
text-decoration: underline;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
div#vimiumHelpDialog a.closeButton:hover {
|
|
317
|
+
color: black;
|
|
318
|
+
-webkit-user-select: none;
|
|
319
|
+
}
|
|
320
|
+
div#vimiumHelpDialogFooter {
|
|
321
|
+
display: block;
|
|
322
|
+
position: relative;
|
|
323
|
+
margin-bottom: 37px;
|
|
324
|
+
}
|
|
325
|
+
table.helpDialogBottom {
|
|
326
|
+
width: 100%;
|
|
327
|
+
}
|
|
328
|
+
td.helpDialogBottomRight {
|
|
329
|
+
width: 100%;
|
|
330
|
+
float: right;
|
|
331
|
+
text-align: right;
|
|
332
|
+
}
|
|
333
|
+
td.helpDialogBottomRight,
|
|
334
|
+
td.helpDialogBottomLeft {
|
|
335
|
+
padding: 0px;
|
|
336
|
+
}
|
|
337
|
+
div#vimiumHelpDialogFooter * {
|
|
338
|
+
font-size: 10px;
|
|
339
|
+
}
|
|
340
|
+
a#toggleAdvancedCommands,
|
|
341
|
+
span#help-dialog-tip {
|
|
342
|
+
position: relative;
|
|
343
|
+
top: 19px;
|
|
344
|
+
white-space: nowrap;
|
|
345
|
+
font-size: 10px;
|
|
346
|
+
}
|
|
347
|
+
a:link.vimiumHelDialogLink,
|
|
348
|
+
a:visited.vimiumHelDialogLink,
|
|
349
|
+
a:hover.vimiumHelDialogLink,
|
|
350
|
+
a:active.vimiumHelDialogLink,
|
|
351
|
+
a#toggleAdvancedCommands {
|
|
352
|
+
color: #2f508e;
|
|
353
|
+
text-decoration: underline;
|
|
354
|
+
cursor: pointer;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/* Vimium HUD CSS */
|
|
358
|
+
|
|
359
|
+
div.vimiumHUD {
|
|
360
|
+
display: block;
|
|
361
|
+
position: fixed;
|
|
362
|
+
width: calc(100% - 20px);
|
|
363
|
+
bottom: 8px;
|
|
364
|
+
left: 8px;
|
|
365
|
+
background: #f1f1f1;
|
|
366
|
+
text-align: left;
|
|
367
|
+
border-radius: 4px;
|
|
368
|
+
box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.8);
|
|
369
|
+
border: 1px solid #aaa;
|
|
370
|
+
z-index: 2139999999;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
iframe.vimiumHUDFrame {
|
|
374
|
+
background-color: transparent;
|
|
375
|
+
padding: 0px;
|
|
376
|
+
overflow: hidden;
|
|
377
|
+
display: block;
|
|
378
|
+
position: fixed;
|
|
379
|
+
width: 20%;
|
|
380
|
+
min-width: 300px;
|
|
381
|
+
height: 58px;
|
|
382
|
+
bottom: -14px;
|
|
383
|
+
right: 20px;
|
|
384
|
+
margin: 0 0 0 -40%;
|
|
385
|
+
border: none;
|
|
386
|
+
z-index: 2139999998; /* Two less than the reference value. */
|
|
387
|
+
opacity: 0;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
div.vimiumHUD .vimiumHUDSearchArea {
|
|
391
|
+
display: block;
|
|
392
|
+
padding: 3px;
|
|
393
|
+
background-color: #f1f1f1;
|
|
394
|
+
border-radius: 4px 4px 0 0;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
div.vimiumHUD .vimiumHUDSearchAreaInner {
|
|
398
|
+
color: #777;
|
|
399
|
+
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
400
|
+
font-size: 14px;
|
|
401
|
+
height: 30px;
|
|
402
|
+
margin-bottom: 0;
|
|
403
|
+
padding: 2px 4px;
|
|
404
|
+
border-radius: 3px;
|
|
405
|
+
width: 100%;
|
|
406
|
+
outline: none;
|
|
407
|
+
box-sizing: border-box;
|
|
408
|
+
line-height: 20px;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
div.vimiumHUD .hud-find {
|
|
412
|
+
background: #fff;
|
|
413
|
+
border: 1px solid #ccc;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
div.vimiumHUD span#hud-find-input,
|
|
417
|
+
div.vimiumHUD span#hud-match-count {
|
|
418
|
+
color: #000;
|
|
419
|
+
display: inline;
|
|
420
|
+
outline: none;
|
|
421
|
+
white-space: nowrap;
|
|
422
|
+
overflow-y: hidden;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
div.vimiumHUD span#hud-find-input:before {
|
|
426
|
+
content: '/';
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
div.vimiumHUD span#hud-match-count {
|
|
430
|
+
color: #aaa;
|
|
431
|
+
font-size: 12px;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
div.vimiumHUD span#hud-find-input br {
|
|
435
|
+
display: none;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
div.vimiumHUD span#hud-find-input * {
|
|
439
|
+
display: inline;
|
|
440
|
+
white-space: nowrap;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
body.vimiumFindMode ::selection {
|
|
444
|
+
background: #ff9632;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/* Vomnibar Frame CSS */
|
|
448
|
+
|
|
449
|
+
iframe.vomnibarFrame {
|
|
450
|
+
background-color: transparent;
|
|
451
|
+
padding: 0px;
|
|
452
|
+
overflow: hidden;
|
|
453
|
+
|
|
454
|
+
display: block;
|
|
455
|
+
position: fixed;
|
|
456
|
+
width: calc(80% + 20px); /* same adjustment as in pages/vomnibar.js */
|
|
457
|
+
min-width: 400px;
|
|
458
|
+
height: calc(100% - 70px);
|
|
459
|
+
top: 70px;
|
|
460
|
+
left: 50%;
|
|
461
|
+
margin: 0 0 0 -40%;
|
|
462
|
+
border: none;
|
|
463
|
+
font-family: sans-serif;
|
|
464
|
+
z-index: 2139999998; /* Two less than the reference value. */
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
div.vimiumFlash {
|
|
468
|
+
box-shadow: 0px 0px 4px 2px #4183c4;
|
|
469
|
+
padding: 1px;
|
|
470
|
+
background-color: transparent;
|
|
471
|
+
position: absolute;
|
|
472
|
+
z-index: 2140000000;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/* UIComponent CSS */
|
|
476
|
+
iframe.vimiumUIComponentHidden {
|
|
477
|
+
display: none;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
iframe.vimiumUIComponentVisible {
|
|
481
|
+
display: block;
|
|
482
|
+
color-scheme: light dark;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
iframe.vimiumUIComponentReactivated {
|
|
486
|
+
border: 5px solid yellow;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
iframe.vimiumNonClickable {
|
|
490
|
+
pointer-events: none;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
@media (prefers-color-scheme: dark) {
|
|
494
|
+
/* DarkReader is a popular dark mode browser extension. It can apply an invert filter to the whole
|
|
495
|
+
* page to make the page dark, when used in Filter and Filter+ modes. We want to reverse/invert
|
|
496
|
+
* that filter again for Vimium's UI elements, because Vimium is already dark mode aware. */
|
|
497
|
+
iframe.reverseDarkReaderFilter {
|
|
498
|
+
-webkit-filter: invert(100%) hue-rotate(180deg) !important;
|
|
499
|
+
filter: invert(100%) hue-rotate(180deg) !important;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/* Dark mode CSS for options page and exclusions */
|
|
503
|
+
|
|
504
|
+
body.vimiumBody {
|
|
505
|
+
background-color: #292a2d;
|
|
506
|
+
color: white;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
body.vimiumBody a,
|
|
510
|
+
body.vimiumBody a:visited {
|
|
511
|
+
color: #8ab4f8;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
body.vimiumBody textarea,
|
|
515
|
+
body.vimiumBody input {
|
|
516
|
+
background-color: #1d1d1f;
|
|
517
|
+
border-color: #1d1d1f;
|
|
518
|
+
color: #e8eaed;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
body.vimiumBody div.example {
|
|
522
|
+
color: #9aa0a6;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
body.vimiumBody div#state,
|
|
526
|
+
body.vimiumBody div#footer {
|
|
527
|
+
background-color: #202124;
|
|
528
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/* Dark Mode CSS for Help Dialog */
|
|
532
|
+
|
|
533
|
+
div#vimiumHelpDialogContainer {
|
|
534
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
535
|
+
background-color: #202124;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
div#vimiumHelpDialog {
|
|
539
|
+
background-color: #292a2d;
|
|
540
|
+
color: white;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
div#vimiumHelpDialog td.vimiumHelpDescription {
|
|
544
|
+
color: #c9cccf;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
span#vimiumTitle,
|
|
548
|
+
div#vimiumHelpDialog td.vimiumHelpSectionTitle {
|
|
549
|
+
color: white;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
#vimiumTitle > span:first-child {
|
|
553
|
+
color: #8ab4f8 !important;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
div#vimiumHelpDialog a {
|
|
557
|
+
color: #8ab4f8;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
div#vimiumHelpDialog div.vimiumDivider {
|
|
561
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
span.vimiumHelpDialogKey {
|
|
565
|
+
background-color: #1d1d1f;
|
|
566
|
+
border: solid 1px black;
|
|
567
|
+
box-shadow: none;
|
|
568
|
+
color: white;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
`};var J=(n,e)=>{let{hostname:t,pathname:o}=new URL(n),{hostname:r,pathname:s}=new URL(e);return t!==r||o!==s};import{distance as Io}from"fastest-levenshtein";var Co=new Set(["about:blank","chrome-error://chromewebdata/"]),vo=2;var mr=["focusable","keyshortcuts","controls"],pr=["textbox","checkbox","combobox","button","link","list","listitem","tablist","tabpanel","tab","searchbox","menu","menubar","form","dialog","alertdialog","banner","navigation","main","menuitem","menuitemcheckbox","menuitemradio","option","radio","progressbar","switch"],hr=["notRendered","notVisible","ariaHiddenSubtree","ariaHiddenElement"],gr=80,fr={paragraph:"p",searchbox:"input"},Oo=["paragraph","option","StaticText"],Lo={indentLevel:0,noID:!1,noChildren:!1,noProperties:!1,maxLevel:void 0,neighbors:void 0},At=class{constructor(e){this.id=e.id,this.role=e.role,this.name=e.name,this.content=e.content,this.properties={},this.pathFromRoot=e.pathFromRoot,this.children=e.children,this.backendNodeID=e.backendNodeID,e.properties&&e.properties.forEach(t=>{t.name==="keyshortcuts"?this.dataMomenticId=parseInt(t.value.value):this.properties[t.name]=t.value.value})}getLogForm(){var e,t;return JSON.stringify({id:this.id,name:(e=this.name)!=null?e:"",role:(t=this.role)!=null?t:"",backendNodeId:this.backendNodeID})}isInteresting(){return pr.includes(this.role)||this.children.some(e=>e.role==="StaticText")?!0:!!this.name.trim()||!!this.content}serialize(e=Lo){var w,b;let{indentLevel:t,noChildren:o,noProperties:r,noID:s}=Object.assign({},Lo,e),a=" ".repeat(t),i=fr[this.role]||this.role,c=this.name,d=C({},this.properties);i==="heading"&&(d.level&&(i=`h${d.level}`,delete d.level),c==="heading"&&(c=""));let h=!Oo.includes(this.role);if(this.role==="StaticText")return`${a}${c}
|
|
572
|
+
`;let p=`${a}<${i}`;!s&&h&&(p+=` id="${this.id}"`),c&&(p+=` name="${c}"`),this.content&&(p+=` content="${this.content}"`),Object.keys(this.properties).length>0&&!r&&Object.entries(this.properties).forEach(([m,g])=>{mr.includes(m)||(typeof g=="string"?p+=` ${m}="${g}"`:typeof g=="boolean"?g?p+=` ${m}`:p+=` ${m}={false}`:typeof g!="undefined"&&(p+=` ${m}={${JSON.stringify(g)}}`))});let A=e.maxLevel!==void 0&&t/2>=e.maxLevel;if(this.children.length===0||o||A)return p+=` />
|
|
573
|
+
`,p;{let m="";for(let u of this.children)m+=u.serialize(O(C({},e),{indentLevel:t+2}));let g=m.trim();g.length<=gr&&!g.includes(`
|
|
574
|
+
`)?p+=`>${g}</${i}>
|
|
575
|
+
`:p+=`>
|
|
576
|
+
${m}${a}</${i}>
|
|
577
|
+
`}if(e.neighbors!==void 0&&e.neighbors>0&&this.parent){let m=this.parent.children.findIndex(E=>E.id===this.id),g=m>0?(w=this.parent.children[m-1])==null?void 0:w.serialize(O(C({},e),{neighbors:0})):"",u=m<this.parent.children.length-1?(b=this.parent.children[m+1])==null?void 0:b.serialize(O(C({},e),{neighbors:0})):"";return`${g||""}
|
|
578
|
+
${p}
|
|
579
|
+
${u||""}`}return p}},Et=class{constructor(e,t,o){this.root=e;this.a11yIdNodeMap=t;this.dataMomenticIdMap=o}serialize(){return this.root?this.root.serialize():""}};function yr(n){var e,t;return(e=n.name)!=null&&e.value?`"${n.name.value}"`:(t=n.role)!=null&&t.value&&n.role.value!=="none"&&n.role.value!=="generic"?`"${n.role.value}"`:`"${n.nodeId}"`}function No(n,e,t){var i,c,d,h,p,A,w;if(!e&&n.parentId)throw new Error(`Got no parent for accessibility node ${n.nodeId}: ${JSON.stringify(n)}`);let o=new At({id:parseInt(n.nodeId),role:((i=n.role)==null?void 0:i.value)||"",name:((c=n.name)==null?void 0:c.value)||"",content:((d=n.value)==null?void 0:d.value)||"",properties:n.properties,children:[],pathFromRoot:(e?`${e.pathFromRoot} `:"")+yr(n),backendNodeID:n.backendDOMNodeId});(h=n.value)!=null&&h.value&&(o.content=`${(p=n.value)==null?void 0:p.value}`);let r=(A=n.childIds)!=null?A:[];for(let b of r){if(!b)continue;let m=t.get(parseInt(b));if(!m)continue;let g=No(m,o,t);g.length&&(o.children=o.children.concat(g))}if(o.role==="StaticText"&&(o.children=[]),o.children.length===1&&o.children[0].role==="StaticText"){let b=o.name,m=(w=o.children[0])==null?void 0:w.name;(b===m||!m)&&(o.children=[])}let s=[];for(let b=o.children.length-1;b>=0;b--){let m=o.children[b];if(m.role!=="StaticText"){s.push(m);continue}if(b===0||o.children[b-1].role!=="StaticText"){s.push(m);continue}o.children[b-1].name+=` ${m.name}`}if(o.children=s.reverse(),o.role==="generic"&&o.children.length===1){let b=o.children[0];if(!Oo.includes(b.role)&&o.name===b.name)return o.children}if(!o.isInteresting()&&n.parentId)return o.children;for(let b of o.children)b.parent=o;return[o]}function Mo(n,e,t,o,r=1){n.id=r,r+=1,e.set(n.id,n),n.dataMomenticId?t.set(n.dataMomenticId,n):n.role!=="StaticText"&&n.role!=="RootWebArea"&&n.role!=="paragraph"&&o.debug({node:n.serialize({neighbors:1,maxLevel:1})},"Node has no data-momentic-id");for(let s of n.children)r=Mo(s,e,t,o,r);return r}function Do(n,e){if(!n.root)throw new Error("a11y tree has null root");n.allNodes=n.allNodes.filter(a=>{var c;return a.ignored?!((c=a.ignoredReasons)==null?void 0:c.find(d=>hr.includes(d.name))):!0});let t=new Map;for(let a of n.allNodes)t.set(parseInt(a.nodeId),a);let o=No(n.root,null,t);if(o.length>1)throw new Error(`Something went horribly wrong processing the a11y tree, we got: ${JSON.stringify(o)}`);if(o.length===0)throw new Oe;let r=new Map,s=new Map;return Mo(o[0],r,s,e),new Et(o[0],r,s)}var Te=(n,e)=>{e.id=n.id,e.content=n.content,e.name=n.name,e.role=n.role,e.numChildren=n.children.length,e.serializedForm=n.serialize({noID:!0,maxLevel:1,neighbors:1})},Tt=(n,e)=>{var r;let t=1;n.role===e.role&&t++;let o=["name","content"];for(let s of o){if(!((r=n[s])!=null&&r.trim()))continue;let a=Io(n[s],e[s])/Math.min(n[s].length,e[s].length);a===0?t+=2:a<=.1&&t++}if(e.numChildren!==void 0&&(n.children.length===e.numChildren&&e.numChildren>0?t++:(e.numChildren>0&&n.children.length===0||Math.abs(n.children.length-e.numChildren)>2)&&t--),e.serializedForm){let s=n.serialize({noID:!0,maxLevel:1,neighbors:1}),a=Io(s,e.serializedForm)/Math.min(s.length,e.serializedForm.length);a===0?t+=2:a<=.1&&t++}return t};var Q={r:147,g:196,b:125,a:.55},Po={showInfo:!1,showRulers:!1,showStyles:!1,showAccessibilityInfo:!1,showExtensionLines:!1,contrastAlgorithm:"aa",contentColor:Q,paddingColor:Q,borderColor:Q,marginColor:Q,eventTargetColor:Q,shapeColor:Q,shapeMarginColor:Q};var Z=(n=1e3)=>new Promise(e=>setTimeout(()=>e(),n));function _o(){cursor=document.createElement("img"),cursor.setAttribute("src","data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjMyIiB2aWV3Qm94PSIwIDAgMzIgMzIiIHdpZHRoPSIzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwIDcpIj48cGF0aCBkPSJtNi4xNDggMTguNDczIDEuODYzLTEuMDAzIDEuNjE1LS44MzktMi41NjgtNC44MTZoNC4zMzJsLTExLjM3OS0xMS40MDh2MTYuMDE1bDMuMzE2LTMuMjIxeiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Im02LjQzMSAxNyAxLjc2NS0uOTQxLTIuNzc1LTUuMjAyaDMuNjA0bC04LjAyNS04LjA0M3YxMS4xODhsMi41My0yLjQ0MnoiIGZpbGw9IiMwMDAiLz48L2c+PC9zdmc+"),cursor.setAttribute("id","selenium_cursor"),cursor.setAttribute("style","position: absolute; z-index: 99999999999; pointer-events: none; left:0; top:0"),cursor.style.filter="invert(0%) sepia(6%) saturate(24%) hue-rotate(315deg) brightness(89%) contrast(110%)",document.body.appendChild(cursor),document.onmousemove=function(n){n=n||window.event,document.getElementById("selenium_cursor").style.left=n.pageX+"px",document.getElementById("selenium_cursor").style.top=n.pageY+"px"}}function zo(){window.globalHintManager||(window.globalHintManager=new window.HintManager),window.globalHintManager.capture()}function Uo(){window.globalHintManager&&window.globalHintManager.reset()}function Fo(){let n=document.body.getElementsByTagName("*"),e=1;for(let t=0;t<n.length;t++){let o=e.toString();for(;[6].some(s=>o.includes(s.toString()));)e++,o=e.toString();let r=n[t];r==null||r.setAttribute("data-momentic-id",`${e}`),r==null||r.setAttribute("aria-keyshortcuts",`${e}`),e++}}var Sr=new Set(["document","script","XMLHttpRequest","fetch","xhr"]),wr=new Set(["script","document"]),br=["intercom.io","googletagmanager.com","google-analytics.com","www.gstatic.com","gstatic.com","apis.google.com","sentry.io","newrelic.com","p.retool.com","m.stripe.com","m.stripe.network","js.stripe.com","assets.trybento.co","udon.trybento.co","cdn.lr-in-prod.com","r.lr-in-prod.com","content.product-usage.assembledhq.com","data.product-usage.assembledhq.com","static.zdassets.com","o.clarity.ms/collect"],Ar=["api.stripe.com","supabase.co"];function Ct(n){return`${n.resourceType()} ${n.method()} ${n.url()}`}function ko(n){return n=n.replace(/^www\./,""),n}function $o(n){return Ar.some(e=>n.includes(e))}function Ho(n,e){if(!Sr.has(n.resourceType()))return!1;let t=new URL(e),o=new URL(n.url());return br.some(r=>o.hostname.includes(r))?!1:wr.has(n.resourceType())||n.method()!=="GET"?!0:ko(o.hostname).includes(ko(t.hostname))}var Rt=Dr(Mr);Rt.use(_r());Rt.use(Pr({provider:{id:"2captcha",token:process.env.TWO_CAPTCHA_KEY},visualFeedback:!0}));function xt(n){return l(this,null,function*(){yield n.send("Accessibility.enable"),yield n.send("DOM.enable"),yield n.send("Overlay.enable")})}var q=class q{constructor({browser:e,context:t,page:o,baseURL:r,cdpClient:s,logger:a}){this.a11yIdToNodeMap=new Map;this.dataMomenticIdToNodeMap=new Map;this.browser=e,this.context=t,this.page=o,this.baseURL=r,this.cdpClient=s,this.logger=a}static init(s,a,i){return l(this,arguments,function*(e,t,o,r=8e3){let c=yield Rt.launch({headless:!0,handleSIGTERM:!1}),d=yield c.newContext({viewport:q.VIEWPORT,deviceScaleFactor:process.platform==="darwin"?2:1,userAgent:Vo["Desktop Chrome"].userAgent,geolocation:{latitude:37.7749,longitude:-122.4194},locale:"en-US",timezoneId:"America/Los_Angeles"}),h=yield d.newPage(),p=yield d.newCDPSession(h),A=new q({browser:c,context:d,page:h,baseURL:e,cdpClient:p,logger:t}),w=!1;l(this,null,function*(){try{yield A.navigate(e,!1),yield xt(p)}catch(E){t.error({err:E},"Failed to initialize chrome browser")}finally{w=!0}});let m=()=>l(this,null,function*(){if(o)try{o({viewport:A.viewport,buffer:yield A.screenshot()})}catch(E){t.error({err:E},"Failed to take screenshot")}});m();let g=setInterval(()=>{m()},250),u=Date.now();for(;!w&&Date.now()-u<r;)yield Z(250);return clearInterval(g),w||t.warn("Timeout elapsed waiting for browser to initialize - are you sure this page is accessible?"),A})}reset(e){return l(this,null,function*(){this.a11yIdToNodeMap.clear(),this.dataMomenticIdToNodeMap.clear();let t=yield this.context.pages();this.page=t[0];for(let o=1;o<t.length;o++)yield t[o].close();e.clearCookies&&(yield this.context.clearCookies()),e.clearStorage&&(yield this.page.evaluate(()=>{localStorage.clear()})),yield this.page.goto(this.baseURL,{waitUntil:"load",timeout:3e3})})}pageSetup(){return l(this,null,function*(){try{yield this.page.evaluate(_o)}catch(e){}})}wait(e){return l(this,null,function*(){yield this.page.waitForTimeout(e)})}toggleHints(e){return l(this,null,function*(){e.state==="on"?(yield this.page.addStyleTag({content:bt.css}),yield this.page.addScriptTag({content:bt.js}),yield this.page.evaluate(zo)):yield this.page.evaluate(Uo)})}showHints(){return l(this,null,function*(){yield this.toggleHints({state:"on"});let e=()=>l(this,null,function*(){try{yield this.toggleHints({state:"off"})}catch(t){this.logger.debug({err:t},"Failed to remove vision hints")}});setTimeout(()=>{e()},3e3)})}cleanup(){return l(this,null,function*(){yield this.page.close(),yield this.context.close(),yield this.browser.close()})}get closed(){return this.page.isClosed()||!this.browser.isConnected()}html(){return l(this,null,function*(){return yield this.page.content()})}get url(){return this.page.url()}screenshotWithHints(e=100,t="device",o="/tmp/screenshots/test.jpg"){return l(this,null,function*(){let r=o==null?void 0:o.split("."),s=r==null?void 0:r.slice(0,-1).join("."),a=r==null?void 0:r.slice(-1)[0],i=yield this.screenshot(e,t,o?`${s}-before-hint.${a}`:void 0);yield this.showHints();let c=yield this.screenshot(e,t,o?`${s}-after-hint.${a}`:void 0);return{before:i,after:c}})}screenshot(e=100,t="device",o){return l(this,null,function*(){return this.page.screenshot({fullPage:!1,quality:e,scale:t,type:"jpeg",caret:"initial",path:o})})}get viewport(){let e=this.page.viewportSize();if(!e)throw new Error("failed to get viewport");return e}navigate(e,t=!0){return l(this,null,function*(){this.logger.debug(`Navigating to ${e}`);let o=Date.now(),r=()=>l(this,null,function*(){try{yield this.page.goto(e,{waitUntil:"load",timeout:3e3}),this.logger.debug({url:e},`Got load event in ${Math.floor(Date.now()-o)}ms`)}catch(s){this.logger.warn({url:e},"Timeout elapsed waiting for page to fire load event, continuing anyways...")}});if(t?yield this.wrapPossibleNavigation(r):yield r(),Co.has(this.url)&&process.env.NODE_ENV==="production")throw new Error(`${e} took too long to load \u{1F61E}. Please ensure the site and your internet are working.`);this.logger.debug({url:e},"Navigation complete")})}fill(r,s){return l(this,arguments,function*(e,t,o={}){let a=yield this.click(e,{doubleClick:!1,rightClick:!1});return yield this.type(t,o),a})}type(o){return l(this,arguments,function*(e,t={}){let{clearContent:r=!0,pressKeysSequentially:s=!1}=t;r&&(process.platform==="darwin"?yield this.page.keyboard.press("Meta+A"):yield this.page.keyboard.press("Control+A"),yield this.page.keyboard.press("Backspace")),s?yield this.page.keyboard.type(e):yield this.page.keyboard.insertText(e)})}clickByA11yID(o){return l(this,arguments,function*(e,t={}){let r=this.a11yIdToNodeMap.get(e);if(!r)throw new Error(`Could not find DOM node during click: ${e}`);let s=yield this.clickUsingCDP(r,t);return yield this.highlightNode(s),r.serialize({noChildren:!0,noProperties:!0,noID:!0})})}selectOptionByA11yID(e,t){return l(this,null,function*(){let o=this.a11yIdToNodeMap.get(e);if(!o)throw new Error(`Could not find DOM node while selecting option: ${e}`);if(!o.backendNodeID)throw new Error(`Select target missing backend node id: ${o.getLogForm()}`);return yield(yield this.getLocatorFromBackendID(o.backendNodeID)).selectOption(t,{timeout:8e3}),yield this.highlightNode(o),o.serialize({noChildren:!0,noProperties:!0,noID:!0})})}scrollIntoView(e){return l(this,null,function*(){let t=yield this.resolveCachedTargetToID(e),o=this.a11yIdToNodeMap.get(t);if(!o)throw new Error(`Could not find node in DOM with a11y id: ${t}`);if(!o.backendNodeID)throw new Error(`Focus target missing backend node id: ${o.getLogForm()}`);yield(yield this.getLocatorFromBackendID(o.backendNodeID)).scrollIntoViewIfNeeded({timeout:8e3})})}highlight(e){return l(this,null,function*(){try{let t=yield this.resolveCachedTargetToID(e),o=this.a11yIdToNodeMap.get(t);if(!o)throw new Error(`Could not find DOM node during highlight: ${t}`);if(!o.backendNodeID)throw new Error(`Select target missing backend node id: ${o.getLogForm()}`);yield this.highlightNode(o)}catch(t){this.logger.warn({err:t,target:e},"Failed to highlight target")}})}highlightNode(e){return l(this,null,function*(){try{yield this.cdpClient.send("Overlay.highlightNode",{highlightConfig:Po,backendNodeId:e.backendNodeID})}catch(o){this.logger.warn("Failed to add node highlight, a page navigation likely occurred. This is non-fatal for tests.")}let t=()=>l(this,null,function*(){try{yield this.cdpClient.send("Overlay.hideHighlight",{backendNodeId:e.backendNodeID})}catch(o){this.logger.debug({err:o},"Failed to remove node highlight")}});setTimeout(()=>{t()},3e3)})}wrapPossibleNavigation(r){return l(this,arguments,function*(e,t=8e3,o=!0){let s=Date.now(),a=this.url,i=Date.now(),c=new Map,d=new Map,h=T=>{var xe;let x=Ct(T.request());d.set(x,((xe=d.get(x))!=null?xe:0)+1);let F=T.status();F>=500&&this.logger.warn({request:x,status:F},"Received 500 level response")},p=T=>{var F;if(!Ho(T,this.url))return;let x=Ct(T);c.set(x,((F=c.get(x))!=null?F:0)+1),i=Date.now()};this.page.on("response",h),this.page.on("request",p);let A=[];o&&(A=(yield this.context.pages()).map(T=>T.url()));let w=!1,b=e().catch(T=>(w=!0,T instanceof Error?T:new Error(`${T}`)));yield Z(250);let m=T=>l(this,null,function*(){let x=yield T;if(x instanceof Error)throw x;return x}),g=new Set,u=!1,R=yield l(this,null,function*(){for(;!w&&!(!u&&Date.now()-s>t);){if(yield Z(250),u=!1,g=new Set,Date.now()-i<=1250)continue;let T=!1;for(let x of c.keys())c.get(x)!==d.get(x)&&($o(x)&&(u=!0),T=!0,g.add(x));if(!T)return this.logger.debug({url:this.url,requests:JSON.stringify(Array.from(c.entries()))},`Network idle in ${Math.floor(Date.now()-s)}ms`),!0}return!w&&g.size>0&&this.logger.warn({url:this.url,unfinishedRequests:JSON.stringify(Array.from(g.entries()))},"Timeout elapsed waiting for network idle, continuing anyways..."),!1});if(this.page.off("response",h),this.page.off("request",p),!R)return m(b);let j=this.url;if(!w&&J(j,a)){this.logger.debug({startURL:a,newURL:this.url},"Detected url change in wrapPossibleNavigation, waiting for load state");let T=Math.max(t-(Date.now()-s),0);if(T>0)try{yield this.page.waitForLoadState("load",{timeout:T})}catch(x){this.logger.warn({url:this.url},"Timeout elapsed waiting for load state to fire, continuing anyways...")}}if(o){let T=(yield this.context.pages()).map(x=>x.url());if(T.length>A.length)for(let x of T)x!==j&&(yield this.switchToPage(x))}return m(b)})}resolveCachedTargetToID(e){return l(this,null,function*(){if(!Le(e)){let i=this.a11yIdToNodeMap.get(e.id);if(!i)throw new Error(`Resolving target failed, fresh value did not exist in node map: ${e.id}`);return Te(i,e),e.id}let t=(yield this.getA11yTree()).serialize();this.logger.debug({tree:t},"Refreshed a11y tree before resolving target");let o=this.a11yIdToNodeMap.get(e.id);if(o){let i=Tt(o,e);if(i>=5)return this.logger.debug({target:e,proposedNode:o.getLogForm(),comparisonScore:i},"Resolved cached a11y target to node with exact same id"),Te(o,e),e.id}let r=1/0,s=1/0,a;for(let i of this.a11yIdToNodeMap.values()){let c=Tt(i,e);if(c>=5)return this.logger.debug({newNode:i.getLogForm(),target:e,comparisonScore:c},"Resolved cached a11y target to new node with field comparison"),Te(i,e),i.id;if(!e.serializedForm)continue;let d=i.serialize({noID:!0,maxLevel:1,neighbors:1});if(Math.abs(d.length-e.serializedForm.length)>15)continue;let h=Nr(e.serializedForm,d),p=h/Math.min(e.serializedForm.length,d.length);h<r&&p<.2&&(r=h,s=p,a=i)}if(a&&r<15)return this.logger.debug({newNode:a.getLogForm(),target:e,distance:r,ratio:s},"Resolved cached a11y target to new node with pure levenshtein distance"),Te(a,e),a.id;throw new Error(`Could not find any relevant node given cached target: ${JSON.stringify(e)}`)})}click(o){return l(this,arguments,function*(e,t={}){let r=yield this.resolveCachedTargetToID(e);return yield this.wrapPossibleNavigation(()=>this.clickByA11yID(r,t))})}hover(e){return l(this,null,function*(){let t=yield this.resolveCachedTargetToID(e),o=this.a11yIdToNodeMap.get(t);if(!o)throw new Error(`Could not find DOM node for hover: ${t}`);if(!o.backendNodeID)throw new Error(`Hover target missing backend node id: ${o.getLogForm()}`);return yield(yield this.getLocatorFromBackendID(o.backendNodeID)).hover({timeout:8e3}),yield this.highlightNode(o),o.serialize({noChildren:!0,noProperties:!0,noID:!0})})}selectOption(e,t){return l(this,null,function*(){let o=yield this.resolveCachedTargetToID(e);return this.selectOptionByA11yID(o,t)})}press(e){return l(this,null,function*(){yield this.wrapPossibleNavigation(()=>this.page.keyboard.press(e))})}refresh(){return l(this,null,function*(){yield this.page.reload(),yield this.pageSetup()})}getA11yTree(){return l(this,null,function*(){yield xt(this.cdpClient),yield this.page.evaluate(Fo);let e=null,t=0,o=this.url;for(;!e;)try{let r=yield this.getRawA11yTree();if(!r.root||r.allNodes.length===0)throw new Error("No a11y tree found on page");e=Do(r,this.logger)}catch(r){if(this.logger.error({err:r,url:o},"Error fetching a11y tree"),t===0)yield Z(1e3),t++;else throw new Error(`Max retries exceeded fetching a11y tree: ${r}`)}return e.root||this.logger.warn("A11y tree was pruned entirely"),this.a11yIdToNodeMap=e.a11yIdNodeMap,this.dataMomenticIdToNodeMap=e.dataMomenticIdMap,e})}getA11yIdFromDataMomenticId(e){var t;return(t=this.dataMomenticIdToNodeMap.get(e))==null?void 0:t.id}getRawA11yTree(){return l(this,null,function*(){let e=this.page.url(),t=Date.now(),o=()=>{t=Date.now()};this.cdpClient.addListener("Accessibility.nodesUpdated",o);let r=!1,s=()=>{this.logger.info({url:e},"Load event fired on page"),r=!0,t=Date.now()};this.cdpClient.addListener("Accessibility.loadComplete",s);let a=Date.now(),i=!0;for(;Date.now()-a<3e3;){if(yield Z(250),!r&&Date.now()-a<1e3){process.env.NODE_ENV!=="production"&&this.logger.debug({url:e},"A11y tree not loaded yet, waiting...");continue}if(Date.now()-t>=1250){i=!1;break}this.logger.debug({url:e},"A11y tree not stable yet, waiting...")}this.logger.debug({duration:Date.now()-a,eventReceived:r,timeoutTriggered:i},"A11y wait phase completed");let{node:c}=yield this.cdpClient.send("Accessibility.getRootAXNode"),{nodes:d}=yield this.cdpClient.send("Accessibility.queryAXTree",{backendNodeId:c.backendDOMNodeId});return this.cdpClient.removeListener("Accessibility.loadComplete",s),this.cdpClient.removeListener("Accessibility.nodesUpdated",o),{root:c,allNodes:d}})}clickUsingVisualCoordinates(e){return l(this,null,function*(){let t=yield this.getElementLocation(e);if(!t)throw new Error(`Could not find element location with backend node id: ${e}`);this.logger.debug({location:t},"Executing mouse click"),yield this.page.mouse.click(t.centerX,t.centerY)})}getIDAttributeUsingCDP(e){return l(this,null,function*(){yield this.cdpClient.send("DOM.getDocument",{depth:0});let t=yield this.cdpClient.send("DOM.requestNode",{objectId:e}),r=(yield this.cdpClient.send("DOM.getAttributes",{nodeId:t.nodeId})).attributes,s=r.findIndex(a=>a==="data-momentic-id");return s===-1?"":r[s+1]||""})}getLocatorFromBackendID(e){return l(this,null,function*(){let t=yield this.cdpClient.send("DOM.resolveNode",{backendNodeId:e});if(!t||!t.object.objectId)throw new Error(`Could not resolve backend node ${e}`);try{let o=yield this.getIDAttributeUsingCDP(t.object.objectId);if(!o)throw new Error("Failed getting data-momentic-id attribute using CDP");return this.page.locator(`[data-momentic-id="${o}"]`)}catch(o){throw this.logger.error({err:o},"Failed to get ID attribute"),o}})}clickUsingCDP(o){return l(this,arguments,function*(e,t={}){let r=0,s=e;for(;r<vo;){if(!s||s.role==="RootWebArea")throw new Error(`Attempted to click node with no clickable surrounding elements: ${e.getLogForm()}`);if(s.role==="StaticText"){s=s.parent;continue}let a=s.backendNodeID;if(!a){this.logger.warn({node:s.getLogForm()},"Click candidate had no backend node ID"),s=s.parent;continue}try{let i=yield this.getLocatorFromBackendID(a);return t.doubleClick?yield i.dblclick({timeout:8e3}):yield i.click({timeout:8e3,button:t.rightClick?"right":"left"}),s.id!==e.id&&this.logger.info({oldNode:e.getLogForm(),newNode:s.getLogForm()},"Redirected click successfully to new element"),s}catch(i){this.logger.error({err:i,node:s.getLogForm()},"Failed click or click timed out"),r++,s=s.parent}}throw new Error(`Max click redirection attempts exhausted on original element: ${e.getLogForm()}`)})}getElementLocation(e){return l(this,null,function*(){let t=yield this.cdpClient.send("DOMSnapshot.captureSnapshot",{computedStyles:[],includeDOMRects:!0,includePaintOrder:!0}),o=yield this.page.evaluate(()=>window.devicePixelRatio);process.platform==="darwin"&&o===1&&(o=2);let r=t.documents[0],s=r.layout,a=r.nodes,i=a.nodeName||[],c=a.backendNodeId||[],d=s.nodeIndex,h=s.bounds,p=-1;for(let E=0;E<i.length;E++)if(c[E]===e){p=d.indexOf(E);break}if(p===-1)throw new Error(`Could not find any backend node with ID ${e}`);let[A=0,w=0,b=0,m=0]=h[p];A/=o,w/=o,b/=o,m/=o;let g=A+b/2,u=w+m/2;return{centerX:g,centerY:u}})}scrollUp(){return l(this,null,function*(){yield this.page.mouse.wheel(0,-q.VIEWPORT.height)})}scrollDown(){return l(this,null,function*(){yield this.page.mouse.wheel(0,q.VIEWPORT.height)})}goForward(){return l(this,null,function*(){yield this.wrapPossibleNavigation(()=>this.page.goForward({timeout:8e3})),yield this.pageSetup()})}goBack(){return l(this,null,function*(){yield this.wrapPossibleNavigation(()=>this.page.goBack({timeout:8e3})),yield this.pageSetup()})}switchToPage(e){return l(this,null,function*(){let t=yield this.context.pages();for(let o=0;o<t.length;o++){let r=t[o];if(r.url().includes(e)){this.logger.info(`Switching to tab ${o} with url ${r.url()}`),this.page=r,yield r.waitForLoadState("load",{timeout:3e3}),yield this.pageSetup(),this.cdpClient=yield this.context.newCDPSession(r),yield xt(this.cdpClient);return}}throw new Error(`Could not find page with url containing ${e}`)})}setCookie(e){return l(this,null,function*(){let t=Bt(e);yield this.context.addCookies([t])})}solveCaptcha(){return l(this,null,function*(){yield this.getA11yTree();let e;for(let i of this.a11yIdToNodeMap.values())if(i.role==="image"&&i.name.toLowerCase().includes("captcha")){if(!i.backendNodeID)continue;e=yield this.getLocatorFromBackendID(i.backendNodeID);break}if(!e){let i=yield this.page.solveRecaptchas();if(!i.captchas||!i.captchas.length)throw new Error("No captchas found on the page");return}let t=yield e.screenshot({type:"jpeg",animations:"allow",quality:100}),o=yield fetch("https://api.2captcha.com/createTask",{method:"POST",body:JSON.stringify({clientKey:process.env.TWO_CAPTCHA_KEY,task:{type:"ImageToTextTask",body:t.toString("base64"),case:!0},languagePool:"en"})});if(!o.ok){let i=`Captcha solver API returned error response: ${o.statusText}`;throw this.logger.error({text:yield o.text()},i),new Error(i)}let{taskId:r}=yield o.json(),s=Date.now(),a="";for(;Date.now()-s<6e4;){yield Z(2500);let i=yield fetch("https://api.2captcha.com/getTaskResult",{method:"POST",body:JSON.stringify({clientKey:process.env.TWO_CAPTCHA_KEY,taskId:r})});if(!i.ok){let d=`Captcha solution API returned error response: ${i.statusText}`;throw this.logger.error({text:yield i.text()},d),new Error(d)}let c=yield i.json();if(c.errorId){let d=`Captcha solution API returned error ID ${c.errorId}`;throw this.logger.error(d),new Error(d)}if(c.status==="ready"){a=c.solution.text;break}}if(!a)throw new Error("Captcha solution timed out");return a})}};q.USER_AGENT=Vo["Desktop Chrome"].userAgent,q.VIEWPORT={width:1920,height:1080};var _=q;var zr={type:"a11y",version:"1.0.0",useHistory:"diff",useGoalSplitter:!0},It=zr;import Ur from"dedent";import Fr from"diff-lines";var kr=1e4,Ce=class{constructor({browser:e,config:t,generator:o,logger:r}){this.browser=e,this.generator=o,this.config=t,this.logger=r,this.pendingInstructions=[],this.commandHistory=[]}get history(){return this.commandHistory.filter(e=>e.state==="DONE")}get lastExecutedCommand(){let e=this.history;return e.length===0?null:e[e.length-1]}resetHistory(){this.commandHistory=[],this.pendingInstructions=[]}resetState(){return l(this,null,function*(){this.resetHistory(),yield this.browser.navigate(this.browser.baseURL)})}getBrowserState(){return l(this,null,function*(){let t=yield(yield this.browser.getA11yTree()).serialize();return this.logger.debug({tree:t},"Got a11y tree"),t})}getSerializedHistory(e,t){let o;return this.config.useHistory==="diff"?o=this.getDiffHistory(e,t):o=this.getListHistory(),o}splitUserGoal(e,t,o){return l(this,null,function*(){if(e==="AI_ACTION"&&t.match(/[,!;.]|(?:and)|(?:then)/)&&this.config.useGoalSplitter){let r=yield this.generator.getGranularGoals({goal:t,url:this.browser.url},o);this.pendingInstructions=r.reverse()}else this.pendingInstructions=[t]})}promptToCommand(e,t,o){return l(this,null,function*(){try{return yield this.promptToCommandHelper(e,t,o)}catch(r){throw r instanceof M?r:new M("InternalWebAgentError",r instanceof Error?r.message:`${r}`,{cause:r})}})}promptToCommandHelper(e,t,o){return l(this,null,function*(){if(this.pendingInstructions.length===0){if(!t.trim())throw new Error("Cannot generate commands for empty goal");yield this.splitUserGoal(e,t,o)}let r=this.pendingInstructions[this.pendingInstructions.length-1];this.logger.info({goal:r},"Starting prompt translation");let s=Date.now(),a=this.browser.url,i=yield this.getBrowserState();this.logger.info({duration:Date.now()-s,url:a},"Got browser state");let c=this.commandHistory.length;this.commandHistory.push({state:"PENDING",browserStateBeforeCommand:i,urlBeforeCommand:a,type:e});let d=this.getSerializedHistory(a,i),h=yield this.generator.getProposedCommand({url:a,numPrevious:c,browserState:i,history:d,goal:r,lastCommand:this.lastExecutedCommand},o);if(this.logger.info({type:h.type,thoughts:h.thoughts},"Got proposed command"),h.type==="SUCCESS"){let p=this.pendingInstructions.pop();if(this.logger.info({finishedInstruction:p,remainingInstructions:this.pendingInstructions},"Removing pending instruction due to SUCCESS"),this.pendingInstructions.length!==0)return this.commandHistory=[],this.promptToCommand(e,"",o)}else h.type==="FAILURE"&&(this.logger.info({remainingInstructions:this.pendingInstructions},"Removing pending instructions due to FAILURE"),this.pendingInstructions=[]);return h})}locateElement(e,t,o){return l(this,null,function*(){if(!e)throw new M("InternalWebAgentError","Cannot locate element with empty description");let r=yield this.getBrowserState(),s;if(t){let{before:a,after:i}=yield this.browser.screenshotWithHints();if(s=yield this.generator.getElementLocationWithVision({goal:e,screenshot:a,hintActivatedScreenshot:i},o),s.id>0){let c=this.browser.getA11yIdFromDataMomenticId(s.id);if(!c)throw new M("InternalWebAgentError",`Unable to find corresponding DOM node for id ${s.id}`);s.id=c}}else s=yield this.generator.getElementLocation({browserState:r,goal:e},o);if(s.id<0)throw new M("ActionFailureError",`Unable to locate element: ${s.thoughts?s.thoughts:"please ensure the element is visible and conforms to Accessibility guidelines"}`);return s})}getDiffHistory(e,t){let o=this.history.filter(s=>s.type==="AI_ACTION");if(o.length===0)return"<NONE/>";let r=[`
|
|
580
|
+
You have already executed the following commands successfully (most recent listed first)`,"-".repeat(10)];return o.reverse().forEach((s,a)=>{if(r.push(`COMMAND ${o.length-a}${a===0?" (command just executed)":""}: ${s.serializedCommand}`),a===0)if(J(s.urlBeforeCommand,e))r.push(` URL CHANGE: '${s.urlBeforeCommand}' -> '${e}'`);else{let i=Fr(s.browserStateBeforeCommand,t,{n_surrounding:1});i?i.length<kr?(r.push("PAGE CONTENT CHANGE:"),i.split(`
|
|
581
|
+
`).forEach(c=>r.push(` ${c}`))):r.push("PAGE CONTENT CHANGE: <TOO_LONG_TO_DISPLAY/>"):r.push("PAGE CONTENT CHANGE: <NONE/>")}r.push("-".repeat(10))}),r.push(`STARTING URL: ${this.browser.baseURL}`),r.join(`
|
|
582
|
+
`)}getListHistory(){return Ur`Here are the commands that you have successfully executed:
|
|
583
|
+
${this.commandHistory.filter(e=>e.type==="AI_ACTION").map(e=>`- ${e.serializedCommand}`).join(`
|
|
584
|
+
`)}`}executeCommand(e,t,o=!1){return l(this,null,function*(){let r=this.commandHistory[this.commandHistory.length-1];if(!o&&(!r||r.state!=="PENDING"))throw new M("InternalWebAgentError","Executing command but there is no pending entry in the history");let s;try{let a=Date.now();s=yield this.executePresetStep(e,t);let i=Date.now()-a;this.logger.debug({result:s,duration:i},"Got execution result")}catch(a){throw a instanceof Error?new he(`Failed to execute command: ${a}`,{cause:a}):new he("Unexpected throw from executing command",{cause:new Error(`${a}`)})}return s.succeedImmediately&&!o&&(this.pendingInstructions.pop(),this.pendingInstructions.length>0&&(s.succeedImmediately=!1)),s.elementInteracted&&"target"in e&&e.target&&!e.target.elementDescriptor&&(e.target.elementDescriptor=s.elementInteracted.trim()),o||(r.generatedStep=e,r.serializedCommand=Y(e),r.state="DONE"),s})}executeAssertion(e,t){return l(this,null,function*(){let o;if(t.useVision)o={goal:t.assertion,url:e,screenshot:yield this.browser.screenshot(),browserState:"",history:"",numPrevious:-1,lastCommand:null};else{let s=yield this.getBrowserState(),a=this.getSerializedHistory(e,s);o={goal:t.assertion,url:e,browserState:s,history:a,lastCommand:this.lastExecutedCommand,numPrevious:this.commandHistory.length}}let r=yield this.generator.getAssertionResult(o,t.useVision,t.disableCache);if(r.relevantElements&&Promise.all(r.relevantElements.map(s=>this.browser.highlight({id:s}))),!r.result)throw new M("AssertionFailureError",r.thoughts);return{succeedImmediately:!1,thoughts:r.thoughts,urlAfterCommand:e}})}wrapElementTargetingCommand(e,t,o,r,s=!0){return l(this,null,function*(){if(!e.a11yData&&!e.elementDescriptor)throw new M("InternalWebAgentError","Cannot target element with no target data or element descriptor");let a=e.a11yData&&Le(e.a11yData);e.a11yData||(e.a11yData=Ie.parse(yield this.locateElement(e.elementDescriptor,t,o)),s=!1);try{let i=yield r(e.a11yData);return a?this.logger.debug({target:e},"Successfully used cached target to perform action"):this.logger.debug({target:e},"Successfully generated and used new a11y target information"),i}catch(i){if(s&&e.elementDescriptor)return this.logger.warn({err:i,target:e},"Failed to execute action with cached target, retrying with AI"),e.a11yData=void 0,this.wrapElementTargetingCommand(e,t,o,r,!0);if(i instanceof M)throw i;let c=`Failed to find '${e.elementDescriptor}': ${i instanceof Error?i.message:i}`;throw this.logger.error({err:i,target:e},c),new M("ActionFailureError",c,{cause:i})}})}executePresetStep(e,t){return l(this,null,function*(){try{return yield this.executePresetStepHelper(e,t)}catch(o){throw o instanceof M?o:new M("InternalWebAgentError",o instanceof Error?o.message:`${o}`,{cause:o})}})}executePresetStepHelper(e,t){return l(this,null,function*(){var r;let o=this.browser.url;switch(e.type){case"SUCCESS":return(r=e.condition)!=null&&r.assertion.trim()?this.executeAssertion(o,e.condition):{succeedImmediately:!1,urlAfterCommand:this.browser.url};case"AI_ASSERTION":return this.executeAssertion(o,e);case"NAVIGATE":yield this.browser.navigate(e.url);break;case"CAPTCHA":let s=yield this.browser.solveCaptcha();s&&(yield this.wrapElementTargetingCommand({elementDescriptor:"the captcha image solution input"},e.useVision,t,d=>this.browser.click(d)),yield this.browser.type(s,{clearContent:!0,pressKeysSequentially:!1}));break;case"GO_BACK":yield this.browser.goBack();break;case"GO_FORWARD":yield this.browser.goForward();break;case"SCROLL_DOWN":case"SCROLL_UP":let a;return e.target&&(e.target.elementDescriptor.trim()||e.target.a11yData)&&(a=yield this.wrapElementTargetingCommand(e.target,e.useVision,t,d=>this.browser.hover(d))),e.type==="SCROLL_UP"?yield this.browser.scrollUp():yield this.browser.scrollDown(),{succeedImmediately:!1,urlAfterCommand:o,elementInteracted:a};case"WAIT":yield this.browser.wait(e.delay*1e3);break;case"REFRESH":yield this.browser.refresh();break;case"CLICK":{let d=yield this.wrapElementTargetingCommand(e.target,e.useVision,t,p=>this.browser.click(p,{doubleClick:e.doubleClick,rightClick:e.rightClick})),h={urlAfterCommand:this.browser.url,succeedImmediately:!1,elementInteracted:d};return J(o,h.urlAfterCommand)&&(h.succeedImmediately=!0,h.succeedImmediatelyReason="URL changed"),h}case"SELECT_OPTION":{let d=yield this.wrapElementTargetingCommand(e.target,!1,t,h=>this.browser.selectOption(h,e.option));return{succeedImmediately:!1,urlAfterCommand:this.browser.url,elementInteracted:d}}case"TAB":yield this.browser.switchToPage(e.url);break;case"COOKIE":if(!e.value)break;yield this.browser.setCookie(e.value);break;case"TYPE":{let d=yield this.wrapElementTargetingCommand(e.target,e.useVision,t,p=>this.browser.click(p));yield this.browser.type(e.value,{clearContent:e.clearContent,pressKeysSequentially:e.pressKeysSequentially}),e.pressEnter&&(yield this.browser.press("Enter"));let h={urlAfterCommand:this.browser.url,succeedImmediately:!1,elementInteracted:d};return J(o,h.urlAfterCommand)&&(h.succeedImmediately=!0,h.succeedImmediatelyReason="URL changed"),h}case"HOVER":{let d=yield this.wrapElementTargetingCommand(e.target,e.useVision,t,h=>this.browser.hover(h));return{succeedImmediately:!1,urlAfterCommand:this.browser.url,elementInteracted:d}}case"PRESS":yield this.browser.press(e.value);let i={urlAfterCommand:this.browser.url,succeedImmediately:!1};return J(o,i.urlAfterCommand)&&(i.succeedImmediately=!0,i.succeedImmediatelyReason="URL changed"),i;default:return(d=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(e)}return{succeedImmediately:!1,urlAfterCommand:this.browser.url}})}};import $r from"fetch-retry";var Hr=$r(global.fetch),ue="v1",ve=class{constructor(e){this.baseURL=e.baseURL,this.apiKey=e.apiKey}getElementLocation(e,t){return l(this,null,function*(){let o=yield this.sendRequest(`/${ue}/web-agent/locate-element`,{browserState:e.browserState,goal:e.goal,disableCache:t});return lt.parse(o)})}getElementLocationWithVision(e,t){return l(this,null,function*(){var r,s;let o=yield this.sendRequest(`/${ue}/web-agent/visual-locate`,{goal:e.goal,screenshot:(r=e.screenshot)==null?void 0:r.toString("base64"),hintActivatedScreenshot:(s=e.hintActivatedScreenshot)==null?void 0:s.toString("base64"),disableCache:t});return lt.parse(o)})}getAssertionResult(e,t,o){return l(this,null,function*(){var s;if(t){let a=yield this.sendRequest(`/${ue}/web-agent/assertion`,{url:e.url,goal:e.goal,screenshot:(s=e.screenshot)==null?void 0:s.toString("base64"),disableCache:o,vision:!0});return at.parse(a)}let r=yield this.sendRequest(`/${ue}/web-agent/assertion`,{url:e.url,browserState:e.browserState,goal:e.goal,history:e.history,numPrevious:e.numPrevious,lastCommand:e.lastCommand,disableCache:o,vision:!1});return at.parse(r)})}getProposedCommand(e,t){return l(this,null,function*(){let o=yield this.sendRequest(`/${ue}/web-agent/next-command`,{url:e.url,browserState:e.browserState,goal:e.goal,history:e.history,numPrevious:e.numPrevious,lastCommand:e.lastCommand,disableCache:t});return Qt.parse(o)})}getGranularGoals(e,t){return l(this,null,function*(){let o=yield this.sendRequest(`/${ue}/web-agent/split-goal`,{url:e.url,goal:e.goal,disableCache:t});return Zt.parse(o)})}sendRequest(e,t){return l(this,null,function*(){let o=yield Hr(`${this.baseURL}${e}`,{retries:1,retryDelay:1e3,method:"POST",body:JSON.stringify(t),headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}});if(!o.ok)throw new Error(`Request to ${e} failed with status ${o.status}: ${yield o.text()}`);return o.json()})}};import Br from"@actions/exec";import jr from"@actions/io";import{spawn as Gr}from"child_process";import Vr from"quote";import Wr from"string-argv";function Wo(n,e=!0){return l(this,null,function*(){let t=Wr(n),o=yield jr.which(t[0],!0),r=t.slice(1),s=Br.exec(Vr(o),r,{delay:100});if(e)return s})}function Ko(n){return l(this,null,function*(){return new Promise((e,t)=>{let o={stdio:"inherit",env:process.env,detached:!0},r=Gr("bash",["-c",n],o),s=!1;process.on("exit",()=>r.pid!==void 0&&!s&&process.kill(-r.pid)),r.on("close",a=>{if(s=!0,a===0){e();return}t(`command exited with code ${a}`)})})})}import Lt from"fs";import Yo from"path";var Kr=new Set(["modules","node_modules","dist","bin",".git","logs",".npm",".next","out",".yarn","__pycache__","build",".env",".venv","venv","env","wheels"]);function Ot(n){var r;let e=(r=n.split(Yo.sep).pop())!=null?r:"";if(Kr.has(e))return e!=="modules"&&S.warn(`Skipping directory '${n}' because it is likely an artifact folder.`),[];let t=Lt.readdirSync(n),o=[];return t.forEach(s=>{let a=Yo.join(n,s);Lt.statSync(a).isDirectory()?o=o.concat(Ot(a)):s.endsWith(".yaml")&&(Lt.readFileSync(a,"utf-8").includes("momentic/test")?o.push(a):S.warn(`Skipping file '${a}' because it does not appear to be a valid Momentic test.`))}),o}var Ve=s=>l(void 0,null,function*(){var a=s,{controller:n,step:e,logger:t,advanced:o}=a,r=ne(a,["controller","step","logger","advanced"]);var c,d,h,p,A,w,b;(c=r.onStarted)==null||c.call(r),n.resetHistory();let i=O(C({},e),{startedAt:new Date,userAgent:_.USER_AGENT,finishedAt:new Date,results:[],status:"SUCCESS"});try{let m=0,g=e.commands&&e.commands.length>0;for(;;){if(m>20)throw new Error(`Exceeded max number of commands per step (${20})`);let u,E=new Date,R=yield n.browser.screenshot(),j=yield r.onSaveScreenshot(R);if(g){if(u=e.commands[m],!u)throw new Error(`Saved command at index ${m} is undefined.`)}else u=yield n.promptToCommand(e.type,e.text,o.disableAICaching);if(u.type==="FAILURE"){i.finishedAt=new Date,i.status="FAILED",i.message=u.thoughts;break}(d=r.onCommandGenerated)==null||d.call(r,{commandIndex:m,message:Ze[u.type]||`Unknown command (${u.type})`});let T={beforeScreenshot:j,beforeUrl:n.browser.url,startedAt:E,viewport:n.browser.viewport,finishedAt:new Date,status:"SUCCESS"};t.info(`Starting sub-command ${m} within AI step: ${Y(u)}`);try{let x=yield n.executeCommand(u,o.disableAICaching,g);t.info(`AI sub-command ${m} completed successfully`),T.elementInteracted=x.elementInteracted,(h=r.onCommandExecuted)==null||h.call(r,{commandIndex:m,message:Y(u),command:u});let F=yield n.browser.screenshot(),xe=yield r.onSaveScreenshot(F);T.afterScreenshot=xe,T.afterUrl=n.browser.url,T.finishedAt=new Date;let Dt={status:"SUCCESS",startedAt:T.startedAt,finishedAt:T.finishedAt,type:"PRESET_ACTION",command:u,results:[T]};if(i.results.push(Dt),u.type==="SUCCESS"){i.finishedAt=new Date,i.status="SUCCESS",i.message=(p=x.thoughts)!=null?p:"All commands completed.";break}if(x.succeedImmediately&&!g){i.finishedAt=new Date,i.status="SUCCESS",i.message=x.succeedImmediatelyReason,u={type:"SUCCESS"},(A=r.onCommandExecuted)==null||A.call(r,{commandIndex:m+1,message:Y(u),command:u}),i.results.push(O(C({},Dt),{command:u}));break}}catch(x){if(g){g=!1,m=0,i.results=[];continue}let F=x instanceof Error?x.message:`${x}`;T.status="FAILED",T.message=F,T.finishedAt=new Date,T.afterScreenshot=void 0,T.afterUrl=n.browser.url,i.results.push({status:"FAILED",startedAt:T.startedAt,finishedAt:T.finishedAt,type:"PRESET_ACTION",command:u,results:[T],message:F}),i.status="FAILED",i.finishedAt=new Date,i.message=F;break}m++}}catch(m){i.message=m instanceof Error?m.message:`${m}`,i.finishedAt=new Date,i.status="FAILED"}return i.status==="SUCCESS"?(w=r.onSuccess)==null||w.call(r,{message:i.message||"AI step succeeded.",startedAt:i.startedAt.getTime(),durationMs:i.finishedAt.getTime()-i.startedAt.getTime(),output:i}):(b=r.onFailure)==null||b.call(r,{message:i.message||"AI step errored.",startedAt:i.startedAt.getTime(),durationMs:i.finishedAt.getTime()-i.startedAt.getTime(),output:i}),i});var We=r=>l(void 0,null,function*(){var s=r,{controller:n,step:e,advanced:t}=s,o=ne(s,["controller","step","advanced"]);var h,p,A,w;(h=o.onStarted)==null||h.call(o);let a=new Date,i=n.browser.url,c=yield n.browser.screenshot(),d=yield o.onSaveScreenshot(c);try{let b=yield n.executePresetStep(e.command,t.disableAICaching),m=yield n.browser.screenshot(),g=yield o.onSaveScreenshot(m),u=new Date,E=O(C({},e),{startedAt:a,finishedAt:u,status:"SUCCESS",results:[]}),R="Successfully executed preset action.";e.command.type==="AI_ASSERTION"&&(R=b.thoughts||"Assertion passed.");let j={beforeUrl:i,beforeScreenshot:d,afterUrl:n.browser.url,afterScreenshot:g,startedAt:a,finishedAt:u,viewport:n.browser.viewport,status:"SUCCESS"};return E.status="SUCCESS",E.results=[j],E.message=R,(p=o.onSuccess)==null||p.call(o,{message:R,startedAt:a.getTime(),durationMs:u.getTime()-a.getTime(),command:e.command,output:E}),E}catch(b){let m=new Date,g=b instanceof Error?b.message:`${b}`;if(e.command.type==="AI_ASSERTION"&&e.command.cancelOnFailure){let E=O(C({},e),{startedAt:a,finishedAt:m,status:"CANCELLED",message:g,results:[{beforeUrl:i,beforeScreenshot:d,afterUrl:n.browser.url,afterScreenshot:void 0,startedAt:a,finishedAt:m,viewport:n.browser.viewport,status:"CANCELLED",message:g}]});return(A=o.onCancelled)==null||A.call(o,{message:g,startedAt:a.getTime(),durationMs:m.getTime()-a.getTime(),output:E}),E}let u=O(C({},e),{startedAt:a,finishedAt:m,status:"FAILED",message:g,results:[{beforeUrl:i,beforeScreenshot:d,afterUrl:n.browser.url,afterScreenshot:void 0,startedAt:a,finishedAt:m,viewport:n.browser.viewport,status:"FAILED",message:g}]});return(w=o.onFailure)==null||w.call(o,{message:g,startedAt:a.getTime(),durationMs:m.getTime()-a.getTime(),output:u}),u}});var Xo=s=>l(void 0,null,function*(){var a=s,{controller:n,step:e,advanced:t,logger:o}=a,r=ne(a,["controller","step","advanced","logger"]);var c,d,h;(c=r.onStarted)==null||c.call(r);let i={type:"MODULE",moduleId:e.moduleId,startedAt:new Date,userAgent:_.USER_AGENT,results:[],finishedAt:new Date,status:"SUCCESS"};for(let p=0;p<e.steps.length;p++){let A=e.steps[p];o.debug({i:p,moduleStep:A},"Starting module step"),o.info(`Starting module sub-step ${p+1}/${e.steps.length}: ${ze(A)}`);let w;switch(A.type){case"PRESET_ACTION":w=yield We({controller:n,step:A,advanced:t,logger:o,onSaveScreenshot:r.onSaveScreenshot,onStarted(){var m;(m=r.onStepStarted)==null||m.call(r,{index:p})},onSuccess({message:m,startedAt:g,durationMs:u,output:E}){var R;(R=r.onStepSuccess)==null||R.call(r,{index:p,message:m,startedAt:g,durationMs:u,output:E})},onFailure({message:m,startedAt:g,durationMs:u,output:E}){var R;(R=r.onStepFailure)==null||R.call(r,{index:p,message:m,startedAt:g,durationMs:u,output:E})},onCancelled({message:m,startedAt:g,durationMs:u,output:E}){var R;(R=r.onStepCancelled)==null||R.call(r,{index:p,message:m,startedAt:g,durationMs:u,output:E})}});break;case"AI_ACTION":w=yield Ve({controller:n,step:A,advanced:t,logger:o,onSaveScreenshot:r.onSaveScreenshot,onStarted(){var m;(m=r.onStepStarted)==null||m.call(r,{index:p})},onSuccess({message:m,startedAt:g,durationMs:u,output:E}){var R;(R=r.onStepSuccess)==null||R.call(r,{index:p,message:m,startedAt:g,durationMs:u,output:E})},onFailure({message:m,startedAt:g,durationMs:u,output:E}){var R;(R=r.onStepFailure)==null||R.call(r,{index:p,message:m,startedAt:g,durationMs:u,output:E})},onCommandGenerated({commandIndex:m,message:g}){var u;(u=r.onCommandGenerated)==null||u.call(r,{index:p,commandIndex:m,message:g})},onCommandExecuted({commandIndex:m,message:g,command:u}){var E;(E=r.onCommandExecuted)==null||E.call(r,{index:p,commandIndex:m,message:g,command:u})}});break;default:return(m=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(A)}if(i.results.push(w),w.status==="FAILED"){i.status="FAILED",i.finishedAt=new Date;for(let b=p+1;b<e.steps.length;b++){let m=e.steps[b],g=O(C({},m),{status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:_.USER_AGENT,results:[],message:"Cancelled due to previous failure."});i.results.push(g)}break}}return i.status==="SUCCESS"?(d=r.onSuccess)==null||d.call(r,{message:"Executed module step.",startedAt:i.startedAt.getTime(),durationMs:i.finishedAt.getTime()-i.startedAt.getTime(),output:i}):(h=r.onFailure)==null||h.call(r,{message:"Failed to execute module step.",startedAt:i.startedAt.getTime(),durationMs:i.finishedAt.getTime()-i.startedAt.getTime(),output:i}),i});var Jo=a=>l(void 0,[a],function*({test:n,runId:e,controller:t,logger:o,onUpdateRun:r,onSaveScreenshot:s}){try{let i=yield Yr({test:n,runId:e,controller:t,logger:o,onUpdateRun:r,onSaveScreenshot:s});if(i==="PASSED"||i==="CANCELLED")return i;throw new M("InternalPlatformError",i.message||"")}catch(i){throw i instanceof M||(i=new M("InternalPlatformError",i instanceof Error?i.message:`${i}`,{cause:i})),i}finally{yield t.browser.cleanup()}}),Yr=a=>l(void 0,[a],function*({test:n,runId:e,controller:t,logger:o,onUpdateRun:r,onSaveScreenshot:s}){var A;let i=_e.parse(n.advanced),c=o.child({runId:e,testId:n.id});c.info("Starting test run"),yield r({status:"RUNNING",startedAt:new Date});let d,h="PASSED",p=[];for(let w=0;w<n.steps.length;w++){let b=n.steps[w];c.info(`Starting step ${w+1}/${n.steps.length}: ${ze(b)}`);let m;switch(b.type){case"PRESET_ACTION":m=yield We({controller:t,step:b,advanced:i,logger:c,onSaveScreenshot:s});break;case"AI_ACTION":m=yield Ve({controller:t,step:b,advanced:i,logger:c,onSaveScreenshot:s});break;case"RESOLVED_MODULE":m=yield Xo({controller:t,step:b,advanced:i,logger:c,onSaveScreenshot:s});break;default:return(u=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(b)}if(p.push(m),yield r({results:p}),m.status==="SUCCESS"){c.info(`Step ${w+1}/${n.steps.length} succeeded`);continue}if(m.status!=="FAILED"&&m.status!=="CANCELLED")throw new M("InternalPlatformError",`Received unexpected non-terminal status from step: ${m.status}`);c.info({message:(A=p[p.length-1])==null?void 0:A.message},`Step ${w+1}/${n.steps.length} ended with status: ${m.status}`),d=m,h=m.status;for(let g=w+1;g<n.steps.length;g++){let u=n.steps[g];if(u.type==="RESOLVED_MODULE"){let E={type:"MODULE",moduleId:u.moduleId,startedAt:new Date,userAgent:_.USER_AGENT,results:u.steps.map(R=>O(C({},R),{status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:_.USER_AGENT,results:[]})),finishedAt:new Date,status:"CANCELLED"};p.push(E)}else{let E=O(C({},u),{status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:_.USER_AGENT,results:[]});p.push(E)}}break}return yield r({status:h,finishedAt:new Date,results:p}),h==="FAILED"?d:h});import qr from"diff-lines";import Ye from"semver";var Qo={name:"Migrate to ai step v2",fromVersion:"1.0.4",toVersion:"1.0.5",recursiveKeys:new Set(["results","commands"]),stopOnFailure:!0,execute:n=>l(void 0,null,function*(){return n=n.filter(e=>!(e.status!==void 0&&e.type==="AI_ACTION")),n=n.map(e=>{var t,o;return e.status===void 0||e.type==="PRESET_ACTION"&&(e.results=(o=(t=e.commands)!=null?t:e.results)!=null?o:[]),e}),n})};var Zo={name:"Make sure ai step v2 has done command",fromVersion:"1.0.5",toVersion:"1.0.6",recursiveKeys:new Set(["results","commands"]),stopOnFailure:!0,execute:n=>l(void 0,null,function*(){return n.map(e=>{if(e.type!=="AI_ACTION"||e.status!==void 0||!e.commands||!e.commands.length)return e;let t=e.commands,o=t[t.length-1];return o&&o.type!=="SUCCESS"&&t.push({type:"SUCCESS"}),e})})};var en={name:"Migrate AI assertions to preset actions",fromVersion:"1.0.0",toVersion:"1.0.1",recursiveKeys:new Set,execute:n=>l(void 0,null,function*(){return n.map(e=>{if(e.type!=="AI_ASSERTION")return e;let o={type:"PRESET_ACTION",command:{type:"AI_ASSERTION",assertion:e.text,useVision:!1,disableCache:!0}},r=C(C({},e),o);return delete r.text,r})}),stopOnFailure:!0};var Ke=new Set(["CLICK","TYPE","SELECT_OPTION"]),tn={name:"Migrate element descriptor to live in a target object",fromVersion:"1.0.3",toVersion:"1.0.4",recursiveKeys:new Set,execute:n=>l(void 0,null,function*(){return n.map(e=>{let t=e.command,o=t==null?void 0:t.type,r=t==null?void 0:t.elementDescriptor;return(r!==void 0||Ke.has(o))&&(t.target={elementDescriptor:r!=null?r:""}),e.commands&&Array.isArray(e.commands)&&e.commands.forEach(a=>{let i=a==null?void 0:a.elementDescriptor,c=a==null?void 0:a.type;(i!==void 0||Ke.has(c))&&(a.target={elementDescriptor:i!=null?i:""})}),e.results&&Array.isArray(e.results)&&e.results.forEach(a=>{let i=a.command,c=i==null?void 0:i.elementDescriptor,d=i==null?void 0:i.type;(c!==void 0||Ke.has(d))&&(i.target={elementDescriptor:c!=null?c:""}),a.commands&&Array.isArray(a.commands)&&a.commands.forEach(p=>{let A=p==null?void 0:p.elementDescriptor,w=p==null?void 0:p.type;(A!==void 0||Ke.has(w))&&(p.target={elementDescriptor:A!=null?A:""})})}),e})}),stopOnFailure:!0};var on={name:"Migrate FAILURE status to FAILED",fromVersion:"1.0.1",toVersion:"1.0.2",recursiveKeys:new Set,execute:n=>l(void 0,null,function*(){return n.map(e=>{let t=e;return t.status==="FAILURE"&&(t.status="FAILED"),typeof t.commands=="object"&&Array.isArray(t.commands)&&t.commands.forEach(o=>{if(o&&typeof o=="object"){let r=o;(r==null?void 0:r.status)==="FAILURE"&&(r.status="FAILED")}}),t})}),stopOnFailure:!0};var nn={name:"Migrate preset step types to use the same",fromVersion:"1.0.2",toVersion:"1.0.3",recursiveKeys:new Set,execute:n=>l(void 0,null,function*(){return n.map(e=>{let t=e.command,o=t==null?void 0:t.type;return o!=null&&o.startsWith("PRESET_")&&(t.type=o.slice(7)),e.commands&&Array.isArray(e.commands)&&e.commands.forEach(s=>{let a=s.type;a!=null&&a.startsWith("PRESET_")&&(s.type=a.slice(7))}),e.results&&Array.isArray(e.results)&&e.results.forEach(s=>{let a=s.command,i=a==null?void 0:a.type;i!=null&&i.startsWith("PRESET_")&&(a.type=i.slice(7)),s.commands&&Array.isArray(s.commands)&&s.commands.forEach(d=>{let h=d.type;h!=null&&h.startsWith("PRESET_")&&(d.type=h.slice(7))})}),e})}),stopOnFailure:!0};var ee=[en,on,nn,tn,Qo,Zo];if(K!==ee[ee.length-1].toVersion)throw new Error("Please bump LATEST_VERSION in types package after adding a migration");ee.forEach((n,e)=>{if(!Ye.valid(n.toVersion)||!Ye.valid(n.fromVersion))throw new Error(`Migration '${n.name}' has invalid version`);if(!Ye.gt(n.toVersion,n.fromVersion))throw new Error(`Migration '${n.name}' has toVersion <= fromVersion`);if(e===0)return;if(ee[e-1].toVersion!==n.fromVersion)throw new Error(`Migration '${n.name}' at index ${e} is not contiguous with previous migration`)});function Xr(n){return n.every(e=>e&&typeof e=="object"&&!Array.isArray(e))}var Nt=o=>l(void 0,[o],function*({metadata:n,steps:e,logger:t}){let r=e,{schemaVersion:s,id:a}=n,i=ee.findIndex(h=>Ye.gt(h.toVersion,s));if(i===-1)return t.debug({id:a},"Step migrations up to date"),{steps:r,newVersion:s};let c=s;for(let h=i;h<ee.length;h++){let p=ee[h],A={id:a,migration:p.name,toVersion:p.toVersion};t.debug(A,"Starting migration");try{r=yield rn(r,p),c=p.toVersion}catch(w){throw t.error(C({err:w},A),"Migration failed"),new Error(`Step migration ${p.name} failed: ${w}`)}}let d=qr(JSON.stringify(e,void 0,2),JSON.stringify(r,void 0,2),{n_surrounding:1});return t.debug({diffs:d,id:a},"Migration diffs"),{newVersion:c,steps:r}});function rn(n,e){return l(this,null,function*(){let t=yield e.execute(n);for(let o of t)for(let r of Object.keys(o)){if(!e.recursiveKeys.has(r))continue;let s=o[r];!s||!Array.isArray(s)||Xr(s)&&(o[r]=yield rn(s,e))}return t})}import{v4 as oi}from"uuid";import{parse as sn}from"yaml";import{existsSync as Jr,readFileSync as Qr}from"fs";import{join as Zr}from"path";import{parse as ei}from"yaml";function ti(n){let e=Zr(ut,`${n}.yaml`);if(!Jr(e))throw new Error(`Fixture '${n}' does not exist at expected path ${e}`);let t;try{t=ei(Qr(e,"utf-8").replace(/\r\n|\r/g,`
|
|
585
|
+
`))}catch(r){throw new Error(`Fixture at path ${e} does not parse as valid YAML: ${r}`)}let o;try{o=Gt.parse(t)}catch(r){throw new Error(`Fixture at path ${e} does not conform to expected schema: ${r}`)}return o}function Mt(n,e){return l(this,null,function*(){let t=ti(n),o=e==="setup"?t.setup:t.teardown;if(!o){S.warn(`Fixture '${n}' does not have any steps in the ${e} phase, skipping...`);return}let r=o.timeout,s=function(){return l(this,null,function*(){for(let d=0;d<o.steps.length;d++){let h=o.steps[d],p=h.run,A=w=>l(this,null,function*(){try{yield Ko(`set -ex; ${p}`)}catch(b){if(w){let m=`Fixture '${n}' ${e} step ${d+1} errored in the background. The test will continue, but this may affect test execution and results: ${b}`;S.error(m)}else throw new Error(`Fixture '${n}' ${e} step ${d+1} errored: ${b}`)}});h.waitForCompletion===!1?A(!0):yield A(!1)}})};if(S.info(`Running ${e} phase of fixture '${n}'`),!r){yield s();return}let a,i,c=function(){return l(this,null,function*(){return new Promise((d,h)=>{i=d,a=setTimeout(()=>{i=void 0,h(`Fixture '${n}' ${e} phase timed out after ${r} seconds`)},r*1e3)})})};try{yield Promise.race([s(),c()])}catch(d){throw d}finally{i&&i(),clearTimeout(a)}})}function an(a){return l(this,arguments,function*({path:n,apiClient:e,generator:t,newBaseURL:o,useLocalFiles:r,noReport:s}){var m;let i;r?(S.info(`Reading ${n} from local filesystem`),i=yield ni(n)):(S.info(`Fetching ${n} from Momentic Cloud server (${e.baseURL})`),i=yield e.getTest(n)),i.schemaVersion>K&&S.warn(`Test ${n} has schema version ${i.schemaVersion}, which is greater than what is currently supported by this SDK. Please update your momentic package version to avoid unexpected behavior.`);let c=(m=i.envSettings)==null?void 0:m.find(g=>g.name==="development");for(let g of(c==null?void 0:c.fixtures)||[])yield Mt(g,"setup");let d=new URL(i.baseUrl);if(o){let g=new URL(o);d.hostname=g.hostname,d.protocol=g.protocol,d.port=g.port}let h=yield _.init(d.toString(),S),p=new Ce({browser:h,generator:t,config:It,logger:S}),A,w;if(s)w=oi();else try{A=yield e.createRun({testId:i.id,trigger:"CLI"}),w=A.id}catch(g){throw S.error(g),new Error(`Are you sure test ${i.name} exists on the server?`)}let b="FAILED";try{b=yield Jo({test:i,runId:w,controller:p,logger:S,onSaveScreenshot:g=>l(this,null,function*(){if(s)return"";let{key:u}=yield e.uploadScreenshot({screenshot:g.toString("base64")});return u}),onUpdateRun:g=>l(this,null,function*(){s||(yield e.updateRun(w,g))})})}catch(g){throw s||(yield e.updateRun(w,{status:"FAILED",finishedAt:new Date})),g}finally{try{for(let g of(c==null?void 0:c.fixtures)||[])yield Mt(g,"teardown")}catch(g){S.error(`Failed to run teardown fixtures: ${g}`)}}return b})}function ni(n){return l(this,null,function*(){let{test:e,modules:t}=wt(n),o=sn(e);if(!o.steps||!Array.isArray(o.steps))throw new Error(`Test ${n} is missing steps`);if(!o.schemaVersion||!o.id)throw new Error(`Test ${n} is missing an ID or schema version`);let r;if(o.schemaVersion<K){S.warn(`Test ${n} has schema version ${o.schemaVersion}, which is lower than the version used by this SDK, ${K}. Your test will be migrated to the latest version before execution.`);let{steps:i}=yield Nt({metadata:o,steps:o.steps,logger:S});r=we.array().parse(i)}else r=we.array().parse(o.steps);let s={};for(let[i,c]of Object.entries(t)){let d=sn(c);if(!d.schemaVersion||!d.moduleId)throw new Error(`Module ${i} is missing an ID or schema version`);if(!d.steps||!Array.isArray(d.steps))throw new Error(`Module ${i} is missing steps`);if(d.schemaVersion<K){S.warn(`Module ${i} has schema version ${d.schemaVersion}, which is lower than the version used by this SDK, ${K}. Your module will be migrated to the latest version before execution.`);let{steps:h}=yield Nt({metadata:{id:d.moduleId,schemaVersion:d.schemaVersion},steps:d.steps,logger:S});s[i]=O(C({},d),{steps:Se.array().parse(h)})}else s[i]=d}let a=r.map(i=>{if(i.type!=="MODULE")return i;let c=s[i.moduleId];if(!c)throw new Error(`Could not resolve module ${i.moduleId} required in test ${n}`);return{type:"RESOLVED_MODULE",moduleId:i.moduleId,name:c.name,steps:c.steps}});return Jt.parse(O(C({},o),{steps:a}))})}function cn(c){return l(this,arguments,function*({tests:n,start:e,waitOn:t,waitOnTimeout:o,client:r,all:s,noReport:a,parallelization:i=1}){e&&(S.info(`Running start command: ${e}`),yield Wo(e,!1)),t&&(S.info(`Waiting for ${t} to be accessible (timeout: ${o}s)`),yield ii({resources:[t],timeout:o*1e3}));let d=new ve({baseURL:r.baseURL,apiKey:r.apiKey}),h=!1;(n.some(u=>u.endsWith(".yaml"))||n.some(u=>ln(u)))&&(h=!0);let p=new Set;if(h)S.info(n,"Reading tests from the following local file paths:"),n.forEach(u=>{if(!ln(u))throw new Error(`Path '${u}' does not exist.`);if(ri(u).isDirectory())Ot(u).forEach(R=>p.add(R));else if(u.endsWith(".yaml"))p.add(u);else throw new Error(`Path '${u}' is not a directory or a .yaml file.`)});else if(S.warn("The paths you specified are not files or directories that exist locally."),S.warn(`Fetching tests from Momentic Cloud (${r.baseURL}) instead...`),s)for(let u of yield r.getAllTestIds())p.add(u);else p=new Set(n);let A=Array.from(p);S.info(A,`Identified ${A.length} tests to run locally:`);let w=[];for(let u=0;u<A.length;u+=i){let E=yield Promise.all(A.slice(u,u+i).map(R=>l(this,null,function*(){let j="FAILED";try{j=yield an({useLocalFiles:h,path:R,apiClient:r,generator:d,newBaseURL:t,noReport:a})}catch(T){let x=T instanceof Error?T.message:`${T}`;S.error(`Test ${R} failed with error: ${x}`)}return{runStatus:j,path:R}})));w=w.concat(E)}let b=w.filter(u=>u.runStatus==="PASSED");b.length>0&&(S.info(`Passed ${b.length} out of ${w.length} tests:`),b.forEach(u=>{S.info(`- ${u.path}`)}));let m=w.filter(u=>u.runStatus==="CANCELLED");m.length>0&&(S.warn(`Cancelled ${m.length} out of ${w.length} tests:`),m.forEach(u=>{S.warn(`- ${u.path}`)}));let g=w.filter(u=>u.runStatus==="FAILED");g.length>0&&(S.error(`Failed ${g.length} out of ${w.length} tests:`),g.forEach(u=>{S.error(`- ${u.path}`)}),process.exit(1)),process.exit(0)})}function dn(o){return l(this,arguments,function*({tests:n,client:e,all:t}){let{queuedTests:r}=yield e.queueTests({testPaths:n,all:t});S.info(`Successfully queued ${r.length} tests!`)})}var oe=new si;oe.name("momentic").description("Momentic CLI").version(ao);oe.command("install-browsers").action(()=>l(void 0,null,function*(){yield co()}));oe.addOption(new te("--log-level <level>").choices(["debug","info","warn","error"]).default("info")).on("option:log-level",n=>{S.setMinLevel(Wt[n.toUpperCase()])});oe.command("run").alias("run-tests").addOption($e).addOption(He).addOption(ft).addOption(new te("-r, --remote","Run tests remotely. The production version of this test will be queued for execution.").default(!0).conflicts(["start, waitOn, waitOnTimeout, local"]).implies({local:!1})).addOption(uo).addOption(new te("-l, --local","Run tests locally. Useful for accessing apps on localhost. This option does not control where tests are read from (see <tests> argument documentation).").implies({remote:!1,noReport:!0})).addOption(new te("--start <command>","Arbitrary setup command that will run before Momentic steps begin.")).addOption(new te("--wait-on <url>","URL to wait to become accessible before Momentic tests begin.")).addOption(new te("--wait-on-timeout <timeout>","Max time to wait for the --wait-on URL to become accessible.").default(60,"one minute")).addOption(new te("-p, --parallel <parallelization>","When running with the --local flag, the number of tests to run in parallel. Defaults to 1.").default(1)).addArgument(po).action((n,e)=>l(void 0,null,function*(){let{apiKey:t,server:o,remote:r,local:s,all:a}=e,i=new ce({baseURL:o,apiKey:t});if(s){try{yield cn(C({tests:n,client:i},e))}catch(c){S.error(c),process.exit(1)}return}if(r){for(let c of n)(c.endsWith(".yaml")||li(c))&&(S.error(ai`'${c}' looks like a local file or directory, but the --local flag was not supplied.
|
|
586
|
+
Please supply the --local flag to run tests locally, or specify the test path without its file extension to queue tests remotely (e.g. 'hello-world').`),process.exit(1));yield dn({tests:n,client:i,all:a});return}S.error("One of --remote or --local must be specified"),process.exit(1)}));oe.command("pull").description("Fetch one or more tests from Momentic Cloud and save it in to local disk in YAML format.").addOption($e).addOption(He).addOption(ft).addOption(gt).addArgument(mo).action((n,e)=>l(void 0,null,function*(){let{apiKey:t,server:o,all:r,yes:s}=e;if(yield mt(s),!r&&!(n!=null&&n.length))throw new Error("At least one test name or --all must be provided");let a=new ce({baseURL:o,apiKey:t});yield wo({testsToFetch:n,client:a,all:r})}));oe.command("push").description("Save one or more tests in YAML format to Momentic Cloud.").addOption(gt).addOption($e).addOption(He).addArgument(ho).action((n,e)=>l(void 0,null,function*(){let{apiKey:t,server:o,yes:r}=e;yield mt(r);let s=new ce({baseURL:o,apiKey:t});yield Eo({paths:n,client:s,yes:r})}));function ci(){return l(this,null,function*(){yield oe.parseAsync(process.argv)})}ci();
|