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.
Files changed (3) hide show
  1. package/bin/cli.js +582 -3327
  2. package/dist/index.js +573 -2475
  3. package/package.json +10 -2
package/bin/cli.js CHANGED
@@ -1,3331 +1,586 @@
1
1
  #!/usr/bin/env node
2
- var __defProp = Object.defineProperty;
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
- `.replace("\n", " ")
190
- );
191
- var SelectOptionCommandSchema = CommonCommandSchema.merge(
192
- z2.object({
193
- type: z2.literal("SELECT_OPTION" /* SELECT_OPTION */),
194
- target: ElementDescriptorSchema,
195
- option: z2.string()
196
- })
197
- ).describe(
198
- // TODO: if we move to a non-mutative way of selecting elements (e.g. by selector), we should update this description
199
- `SELECT_OPTION <id> "<option>" - select the specified item from the select with the specified id. The item should exist on the page. Use the name of the item instead of the id. Make sure to include quotes around the option.`
200
- );
201
- var AIAssertionCommandSchema = CommonCommandSchema.merge(
202
- z2.object({
203
- type: z2.literal("AI_ASSERTION" /* AI_ASSERTION */),
204
- assertion: z2.string(),
205
- useVision: z2.boolean().default(false),
206
- disableCache: z2.boolean().default(false)
207
- })
208
- );
209
- var TypeOptionsSchema = z2.object({
210
- clearContent: z2.boolean().default(true),
211
- pressKeysSequentially: z2.boolean().default(false)
212
- });
213
- var TypeCommandSchema = CommonCommandSchema.merge(
214
- z2.object({
215
- type: z2.literal("TYPE" /* TYPE */),
216
- target: ElementDescriptorSchema,
217
- value: z2.string(),
218
- pressEnter: z2.boolean().default(false)
219
- })
220
- ).merge(TypeOptionsSchema).describe(
221
- `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.`
222
- );
223
- var PressCommandSchema = CommonCommandSchema.merge(
224
- z2.object({
225
- type: z2.literal("PRESS" /* PRESS */),
226
- value: z2.string()
227
- })
228
- ).describe(
229
- `PRESS <key> - press the specified key, such as "ArrowLeft", "Enter", or "a". You must specify at least one key.`
230
- );
231
- var TabCommandSchema = CommonCommandSchema.merge(
232
- z2.object({
233
- type: z2.literal("TAB" /* TAB */),
234
- url: z2.string()
235
- })
236
- );
237
- var CookieCommandSchema = CommonCommandSchema.merge(
238
- z2.object({
239
- type: z2.literal("COOKIE" /* COOKIE */),
240
- value: z2.string()
241
- })
242
- );
243
- var SuccessCommandSchema = CommonCommandSchema.merge(
244
- z2.object({
245
- type: z2.literal("SUCCESS" /* SUCCESS */),
246
- condition: AIAssertionCommandSchema.optional()
247
- })
248
- ).describe("SUCCESS - the user goal has been successfully achieved");
249
- var UserEditableAICommandSchema = z2.discriminatedUnion("type", [
250
- ClickCommandSchema,
251
- TypeCommandSchema,
252
- PressCommandSchema,
253
- SelectOptionCommandSchema,
254
- NavigateCommandSchema,
255
- ScrollDownCommandSchema,
256
- ScrollUpCommandSchema,
257
- SuccessCommandSchema
258
- ]);
259
- var UserEditablePresetCommandSchema = z2.discriminatedUnion("type", [
260
- GoBackCommandSchema,
261
- GoForwardCommandSchema,
262
- RefreshCommandSchema,
263
- AIAssertionCommandSchema,
264
- WaitCommandSchema,
265
- TabCommandSchema,
266
- CookieCommandSchema
267
- ]);
268
- var CommandSchema = z2.discriminatedUnion("type", [
269
- // Commands that can be either specified manually or auto-created by AI in an AI step
270
- ...UserEditableAICommandSchema.options,
271
- // Commands that can only be specified manually ("preset commands")
272
- ...UserEditablePresetCommandSchema.options
273
- ]);
274
- var FailureCommandSchema = CommonCommandSchema.merge(
275
- z2.object({
276
- type: z2.literal("FAILURE")
277
- })
278
- ).describe(
279
- "FAILURE - there are no commands to suggest that could make progress that have not already been tried before"
280
- );
281
- var AICommandSchema = z2.discriminatedUnion("type", [
282
- ...UserEditableAICommandSchema.options,
283
- FailureCommandSchema
284
- ]);
285
-
286
- // ../../packages/types/src/steps.ts
287
- import * as z3 from "zod";
288
- var StepType = /* @__PURE__ */ ((StepType2) => {
289
- StepType2["AI_ACTION"] = "AI_ACTION";
290
- StepType2["PRESET_ACTION"] = "PRESET_ACTION";
291
- StepType2["MODULE"] = "MODULE";
292
- return StepType2;
293
- })(StepType || {});
294
- var AIActionSchema = z3.object({
295
- type: z3.literal("AI_ACTION" /* AI_ACTION */),
296
- text: z3.string(),
297
- // Cached commands for this step
298
- commands: z3.array(UserEditableAICommandSchema).optional()
299
- });
300
- var PresetActionSchema = z3.object({
301
- type: z3.literal("PRESET_ACTION" /* PRESET_ACTION */),
302
- command: CommandSchema
303
- });
304
- var ModuleStepSchema = z3.object({
305
- type: z3.literal("MODULE" /* MODULE */),
306
- moduleId: z3.string().uuid()
307
- });
308
- var AllowedModuleStepSchema = z3.union([
309
- AIActionSchema,
310
- PresetActionSchema
311
- ]);
312
- var ResolvedModuleStepSchema = z3.object({
313
- type: z3.literal("RESOLVED_MODULE"),
314
- moduleId: z3.string().uuid(),
315
- name: z3.string(),
316
- steps: AllowedModuleStepSchema.array()
317
- });
318
- var StepSchema = z3.union([
319
- AIActionSchema,
320
- PresetActionSchema,
321
- ModuleStepSchema
322
- ]);
323
- var ResolvedStepSchema = z3.union([
324
- AIActionSchema,
325
- PresetActionSchema,
326
- ResolvedModuleStepSchema
327
- ]);
328
-
329
- // ../../node_modules/.pnpm/playwright@1.40.1/node_modules/playwright/index.mjs
330
- var playwright_exports = {};
331
- __export(playwright_exports, {
332
- default: () => playwright_default
333
- });
334
- __reExport(playwright_exports, playwright_core_star);
335
- import * as playwright_core_star from "playwright-core";
336
- import playwright from "playwright-core";
337
- var playwright_default = playwright;
338
-
339
- // ../../packages/types/src/assertions.ts
340
- import { z as z4 } from "zod";
341
- var AIAssertionResultSchema = z4.object({
342
- thoughts: z4.string(),
343
- result: z4.boolean(),
344
- relevantElements: z4.array(z4.number()).optional()
345
- });
346
-
347
- // ../../packages/types/src/ai-command-generation.ts
348
- import parseArgsStringToArgv from "string-argv";
349
- import { z as z5 } from "zod";
350
-
351
- // ../../packages/types/src/errors.ts
352
- var BrowserExecutionError = class extends Error {
353
- constructor(message, options = {}) {
354
- super(message, options);
355
- this.name = "BrowserExecutionError";
356
- }
357
- };
358
- var EmptyA11yTreeError = class extends Error {
359
- constructor(options = {}) {
360
- super("Got empty a11y tree", options);
361
- this.name = "EmptyA11yTreeError";
362
- }
363
- };
364
-
365
- // ../../packages/types/src/ai-command-generation.ts
366
- var LLMOutputSchema = z5.object({
367
- command: z5.string(),
368
- thoughts: z5.string()
369
- });
370
- var NumericStringSchema = z5.string().pipe(z5.coerce.number());
371
-
372
- // ../../packages/types/src/command-results.ts
373
- import * as z6 from "zod";
374
- var ResultStatus = /* @__PURE__ */ ((ResultStatus2) => {
375
- ResultStatus2["SUCCESS"] = "SUCCESS";
376
- ResultStatus2["FAILED"] = "FAILED";
377
- ResultStatus2["RUNNING"] = "RUNNING";
378
- ResultStatus2["IDLE"] = "IDLE";
379
- ResultStatus2["CANCELLED"] = "CANCELLED";
380
- return ResultStatus2;
381
- })(ResultStatus || {});
382
- var CommandStatus = /* @__PURE__ */ ((CommandStatus2) => {
383
- CommandStatus2["SUCCESS"] = "SUCCESS";
384
- CommandStatus2["FAILED"] = "FAILED";
385
- return CommandStatus2;
386
- })(CommandStatus || {});
387
- var CommandMetadataSchema = z6.object({
388
- beforeUrl: z6.string(),
389
- // FIXME: this should be a discriminated union of string | Buffer
390
- // but to avoid too much schema wranging we leave this for now
391
- // https://github.com/colinhacks/zod/issues/153
392
- beforeScreenshot: z6.string().or(z6.instanceof(Buffer)),
393
- afterUrl: z6.string().optional(),
394
- afterScreenshot: z6.string().or(z6.instanceof(Buffer)).optional(),
395
- startedAt: z6.coerce.date(),
396
- finishedAt: z6.coerce.date(),
397
- viewport: z6.object({
398
- height: z6.number(),
399
- width: z6.number()
400
- }),
401
- status: z6.nativeEnum(CommandStatus),
402
- // used for error message and thoughts
403
- message: z6.string().optional(),
404
- elementInteracted: z6.string().optional()
405
- });
406
- var StepResultMetadataSchema = z6.object({
407
- startedAt: z6.coerce.date(),
408
- finishedAt: z6.coerce.date(),
409
- status: z6.nativeEnum(ResultStatus),
410
- // used for error message and thoughts
411
- message: z6.string().optional(),
412
- // browser info
413
- userAgent: z6.string().optional()
414
- });
415
- var PresetActionResultSchema = PresetActionSchema.merge(
416
- StepResultMetadataSchema
417
- ).merge(
418
- z6.object({
419
- // Array just for consistency with other result types, should only ever be one for preset.
420
- results: CommandMetadataSchema.array()
421
- })
422
- );
423
- var AIActionResultSchema = AIActionSchema.merge(
424
- StepResultMetadataSchema
425
- ).merge(
426
- z6.object({
427
- results: PresetActionResultSchema.array()
428
- })
429
- );
430
- var ModuleResultSchema = ModuleStepSchema.merge(
431
- StepResultMetadataSchema
432
- ).merge(
433
- z6.object({
434
- // nested results
435
- results: z6.union([AIActionResultSchema, PresetActionResultSchema]).array()
436
- })
437
- );
438
- var ResultSchema = z6.discriminatedUnion("type", [
439
- AIActionResultSchema,
440
- PresetActionResultSchema,
441
- ModuleResultSchema
442
- ]);
443
-
444
- // ../../packages/types/src/cookies.ts
445
- import { parseString } from "set-cookie-parser";
446
- function parseCookieString(cookie) {
447
- const parsedCookie = parseString(cookie);
448
- if (!parsedCookie.name) {
449
- throw new Error("Name missing from cookie");
450
- }
451
- if (!parsedCookie.value) {
452
- throw new Error("Value missing from cookie");
453
- }
454
- let sameSite;
455
- if (parsedCookie.sameSite) {
456
- const sameSiteSetting = parsedCookie.sameSite.trim().toLowerCase();
457
- if (sameSiteSetting === "strict") {
458
- sameSite = "Strict";
459
- } else if (sameSiteSetting === "lax") {
460
- sameSite = "Lax";
461
- } else if (sameSiteSetting === "none") {
462
- sameSite = "None";
463
- } else {
464
- throw new Error(`Invalid sameSite setting in cookie: ${sameSiteSetting}`);
465
- }
466
- }
467
- if (!parsedCookie.path && parsedCookie.domain) {
468
- parsedCookie.path = "/";
469
- }
470
- const result = __spreadProps(__spreadValues({}, parsedCookie), {
471
- expires: parsedCookie.expires ? parsedCookie.expires.getTime() / 1e3 : void 0,
472
- sameSite
473
- });
474
- return result;
475
- }
476
-
477
- // ../../packages/types/src/execute-results.ts
478
- import * as z7 from "zod";
479
- var ExecuteCommandHistoryEntrySchema = z7.object({
480
- // type of command executed
481
- type: z7.nativeEnum(StepType),
482
- // if AI step type, what command was executed
483
- generatedStep: UserEditableAICommandSchema.optional(),
484
- // human readable descriptor for action taken, including element interacted with
485
- serializedCommand: z7.string().optional(),
486
- // human readable descriptor for element interacted with
487
- elementInteracted: z7.string().optional()
488
- });
489
-
490
- // ../../packages/types/src/goal-splitter.ts
491
- import { z as z8 } from "zod";
492
- var InstructionsSchema = z8.string().array();
493
-
494
- // ../../packages/types/src/locator.ts
495
- import * as z9 from "zod";
496
- var AILocatorSchema = z9.object({
497
- thoughts: z9.string(),
498
- // a11y id
499
- id: z9.number().int(),
500
- // dropdowns should have options
501
- options: z9.array(z9.string()).optional()
502
- });
503
-
504
- // ../../packages/types/src/modules.ts
505
- import { z as z10 } from "zod";
506
- var ModuleMetadataSchema = z10.object({
507
- id: z10.string(),
508
- createdAt: z10.coerce.date(),
509
- createdBy: z10.string(),
510
- organizationId: z10.string().or(z10.null()),
511
- name: z10.string(),
512
- schemaVersion: z10.string(),
513
- // this is only used in the client and is not stored in the db
514
- numSteps: z10.number()
515
- });
516
- var ModuleSchema = z10.object({
517
- steps: AllowedModuleStepSchema.array()
518
- }).merge(ModuleMetadataSchema.omit({ numSteps: true }));
519
-
520
- // ../../packages/types/src/runs.ts
521
- import { z as z11 } from "zod";
522
- var RunTriggerEnum = {
523
- WEBHOOK: "WEBHOOK",
524
- CRON: "CRON",
525
- MANUAL: "MANUAL",
526
- CLI: "CLI"
527
- };
528
- var RunStatusEnum = {
529
- PENDING: "PENDING",
530
- RUNNING: "RUNNING",
531
- PASSED: "PASSED",
532
- FAILED: "FAILED",
533
- CANCELLED: "CANCELLED"
534
- };
535
- var DateOrStringSchema = z11.string().pipe(z11.coerce.date()).or(z11.date());
536
- var RunMetadataSchema = z11.object({
537
- id: z11.string(),
538
- createdAt: DateOrStringSchema,
539
- createdBy: z11.string(),
540
- organizationId: z11.string().or(z11.null()),
541
- scheduledAt: DateOrStringSchema.or(z11.null()),
542
- startedAt: DateOrStringSchema.or(z11.null()),
543
- finishedAt: DateOrStringSchema.or(z11.null()),
544
- testId: z11.string().or(z11.null()),
545
- status: z11.nativeEnum(RunStatusEnum),
546
- trigger: z11.nativeEnum(RunTriggerEnum),
547
- test: z11.object({
548
- name: z11.string(),
549
- id: z11.string()
550
- }).or(z11.null())
551
- });
552
- var RunWithTestSchema = RunMetadataSchema.merge(
553
- z11.object({
554
- results: ResultSchema.array(),
555
- test: z11.object({
556
- name: z11.string(),
557
- id: z11.string(),
558
- baseUrl: z11.string()
559
- }).or(z11.null())
560
- })
561
- );
562
-
563
- // ../../packages/types/src/serialization.ts
564
- function clampText(text, length) {
565
- if (text.length < length) {
566
- return text;
567
- }
568
- return text.slice(0, length - 3) + "[...]";
569
- }
570
- function serializeCommand(command) {
571
- var _a, _b;
572
- switch (command.type) {
573
- case "SUCCESS" /* SUCCESS */:
574
- if ((_a = command.condition) == null ? void 0 : _a.assertion) {
575
- return `Check success condition: ${command.condition.assertion}`;
576
- }
577
- return `All commands completed`;
578
- case "NAVIGATE" /* NAVIGATE */:
579
- return `Go to URL: ${clampText(command.url, 30)}`;
580
- case "GO_BACK" /* GO_BACK */:
581
- return `Go back to the previous page`;
582
- case "GO_FORWARD" /* GO_FORWARD */:
583
- return `Go forward to the next page`;
584
- case "SCROLL_DOWN" /* SCROLL_DOWN */:
585
- return `Scroll down one page`;
586
- case "SCROLL_UP" /* SCROLL_UP */:
587
- return `Scroll up one page`;
588
- case "WAIT" /* WAIT */:
589
- return `Wait for ${command.delay} seconds`;
590
- case "REFRESH" /* REFRESH */:
591
- return `Refresh the page`;
592
- case "CLICK" /* CLICK */:
593
- return `Click on '${command.target.elementDescriptor}'`;
594
- case "TYPE" /* TYPE */:
595
- let serializedTarget = "";
596
- if ((_b = command.target.a11yData) == null ? void 0 : _b.serializedForm) {
597
- serializedTarget = ` in element ${command.target.a11yData.serializedForm}`;
598
- } else if (command.target.elementDescriptor.length > 0) {
599
- serializedTarget = ` in element ${command.target.elementDescriptor}`;
600
- }
601
- return `Type${serializedTarget}: '${command.value}'`;
602
- case "PRESS" /* PRESS */:
603
- return `Press '${command.value}'`;
604
- case "SELECT_OPTION" /* SELECT_OPTION */:
605
- return `Select option '${command.option}' in '${command.target.elementDescriptor}'`;
606
- case "TAB" /* TAB */:
607
- return `Switch to tab: ${command.url}`;
608
- case "COOKIE" /* COOKIE */:
609
- return `Set cookie: ${command.value}`;
610
- case "AI_ASSERTION" /* AI_ASSERTION */:
611
- return `${command.useVision ? "Visual assertion" : "Assertion"}: '${command.assertion}'`;
612
- default:
613
- const assertUnreachable = (_x) => {
614
- throw "If Typescript complains about the line below, you missed a case or break in the switch above";
615
- };
616
- return assertUnreachable(command);
617
- }
618
- }
619
-
620
- // ../../packages/types/src/card-display.ts
621
- var SELECTABLE_PRESET_COMMAND_OPTIONS_SET = new Set(
622
- Object.values(CommandType)
623
- );
624
- var CARD_DISPLAY_NAMES = {
625
- ["AI_ACTION" /* AI_ACTION */]: "AI action",
626
- ["MODULE" /* MODULE */]: "Module",
627
- ["AI_ASSERTION" /* AI_ASSERTION */]: "AI check",
628
- ["CLICK" /* CLICK */]: "Click",
629
- ["SELECT_OPTION" /* SELECT_OPTION */]: "Select",
630
- ["TYPE" /* TYPE */]: "Type",
631
- ["PRESS" /* PRESS */]: "Press",
632
- ["NAVIGATE" /* NAVIGATE */]: "Navigate",
633
- ["SCROLL_UP" /* SCROLL_UP */]: "Scroll up",
634
- ["SCROLL_DOWN" /* SCROLL_DOWN */]: "Scroll down",
635
- ["GO_BACK" /* GO_BACK */]: "Go back",
636
- ["GO_FORWARD" /* GO_FORWARD */]: "Go forward",
637
- ["WAIT" /* WAIT */]: "Wait",
638
- ["REFRESH" /* REFRESH */]: "Refresh",
639
- ["TAB" /* TAB */]: "Switch tab",
640
- ["COOKIE" /* COOKIE */]: "Set cookie",
641
- ["SUCCESS" /* SUCCESS */]: "Done"
642
- };
643
- var CARD_DESCRIPTIONS = {
644
- ["AI_ACTION" /* AI_ACTION */]: "Ask AI to plan and execute something on the page.",
645
- ["MODULE" /* MODULE */]: "A list of steps that can be reused in multiple tests.",
646
- ["AI_ASSERTION" /* AI_ASSERTION */]: "Ask AI whether something is true on the page.",
647
- ["CLICK" /* CLICK */]: "Click on an element on the page based on a description.",
648
- ["SELECT_OPTION" /* SELECT_OPTION */]: "Select an option from a dropdown based on a description.",
649
- ["TYPE" /* TYPE */]: "Type the specified text into an element.",
650
- ["PRESS" /* PRESS */]: "Press the specified keys using the keyboard. (e.g. Ctrl+A)",
651
- ["NAVIGATE" /* NAVIGATE */]: "Navigate to the specified URL.",
652
- ["SCROLL_UP" /* SCROLL_UP */]: "Scroll up one page.",
653
- ["SCROLL_DOWN" /* SCROLL_DOWN */]: "Scroll down one page.",
654
- ["GO_BACK" /* GO_BACK */]: "Go back in browser history.",
655
- ["GO_FORWARD" /* GO_FORWARD */]: "Go forward in browser history.",
656
- ["WAIT" /* WAIT */]: "Wait for the specified number of seconds.",
657
- ["REFRESH" /* REFRESH */]: "Refresh the page. This will not clear cookies or session data.",
658
- ["TAB" /* TAB */]: "Switch to different tab in the browser.",
659
- ["COOKIE" /* COOKIE */]: "Set a cookie that will persist throughout the browser session",
660
- ["SUCCESS" /* SUCCESS */]: "Indicate the entire AI action has succeeded, optionally based on a condition."
661
- };
662
-
663
- // ../../packages/types/src/test.ts
664
- import { z as z13 } from "zod";
665
-
666
- // ../../packages/types/src/test-settings.ts
667
- import { isValidCron } from "cron-validator";
668
- import { z as z12 } from "zod";
669
- var TestAdvancedSettingsSchema = z12.object({
670
- availableAsModule: z12.boolean().default(false),
671
- disableAICaching: z12.boolean().default(false)
672
- });
673
- var ScheduleSettingsSchema = z12.object({
674
- cron: z12.string().refine(
675
- (v) => {
676
- return isValidCron(v);
677
- },
678
- { message: "Invalid cron expression." }
679
- ).default("0 0 */1 * *"),
680
- enabled: z12.boolean().default(false),
681
- timeZone: z12.string().default("America/Los_Angeles"),
682
- // this is used for removing repeatable jobs (not set by user)
683
- jobKey: z12.string().optional()
684
- });
685
- var WebhookSchema = z12.object({
686
- lastStatus: z12.number().optional(),
687
- url: z12.string().url()
688
- });
689
- var WebhookSettingsSchema = z12.array(WebhookSchema).default([]);
690
- var TestSettingsSchema = z12.object({
691
- name: z12.string().min(1),
692
- baseUrl: z12.string().url(),
693
- advanced: TestAdvancedSettingsSchema
694
- });
695
-
696
- // ../../packages/types/src/test.ts
697
- var ResolvedTestSchema = z13.object({
698
- id: z13.string(),
699
- name: z13.string(),
700
- baseUrl: z13.string(),
701
- steps: z13.array(ResolvedStepSchema),
702
- createdAt: z13.coerce.date(),
703
- updatedAt: z13.coerce.date(),
704
- createdBy: z13.string(),
705
- organizationId: z13.string().or(z13.null()),
706
- schemaVersion: z13.string(),
707
- advanced: TestAdvancedSettingsSchema,
708
- schedule: ScheduleSettingsSchema,
709
- webhooks: WebhookSettingsSchema
710
- });
711
-
712
- // ../../packages/types/src/context.ts
713
- import * as z14 from "zod";
714
- var DynamicContextSchema = z14.object({
715
- // user goal or instruction
716
- goal: z14.string(),
717
- // current url of the browser
718
- url: z14.string(),
719
- // serialized page state
720
- browserState: z14.string(),
721
- // serialized history of previous commands
722
- history: z14.string(),
723
- // number of previously executed commands
724
- numPrevious: z14.number(),
725
- // last executed command, if any
726
- lastCommand: ExecuteCommandHistoryEntrySchema.or(z14.null())
727
- });
728
-
729
- // ../../packages/types/src/public-api.ts
730
- import * as z15 from "zod";
731
- var GeneratorOptionsSchema = z15.object({
732
- disableCache: z15.boolean()
733
- });
734
- var GetNextCommandBodySchema = DynamicContextSchema.merge(
735
- GeneratorOptionsSchema
736
- );
737
- var GetNextCommandResponseSchema = AICommandSchema;
738
- var GetAssertionResultBodySchema = z15.discriminatedUnion("vision", [
739
- DynamicContextSchema.merge(GeneratorOptionsSchema).merge(
740
- z15.object({
741
- vision: z15.literal(false)
742
- })
743
- ),
744
- DynamicContextSchema.pick({
745
- goal: true,
746
- url: true
747
- }).merge(GeneratorOptionsSchema).merge(
748
- z15.object({
749
- // base64 encoded image
750
- screenshot: z15.string(),
751
- vision: z15.literal(true)
752
- })
753
- )
754
- ]);
755
- var GetAssertionResponseSchema = AIAssertionResultSchema;
756
- var LocateBodySchema = DynamicContextSchema.pick({
757
- browserState: true,
758
- goal: true
759
- }).merge(GeneratorOptionsSchema);
760
- var LocateResponseSchema = AILocatorSchema;
761
- var SplitGoalBodySchema = DynamicContextSchema.pick({
762
- goal: true,
763
- url: true
764
- }).merge(GeneratorOptionsSchema);
765
- var SplitGoalResponseSchema = z15.string().array();
766
- var QueueTestsBodySchema = z15.object({
767
- testIds: z15.string().array()
768
- });
769
- var GetTestResponseSchema = ResolvedTestSchema;
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();