momentic 0.0.29 → 0.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +18 -18
- package/dist/index.js +20 -20
- package/package.json +1 -1
- package/static/assets/{index-kZODqAuV.js → index-dqYjXZA5.js} +74 -74
- package/static/index.html +1 -1
package/bin/cli.js
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as
|
|
2
|
+
import{Command as Za,Option as ie}from"commander";import el from"dedent";import ha from"body-parser";import fa from"cors";import fo from"express";import{existsSync as ga,statSync as ya}from"fs";import Sa from"http";import wa from"open";import ba,{join as va}from"path";import{Server as Wi}from"socket.io";import{v4 as Zn}from"uuid";var It=class{controllers=new Map;registerSession(e,o=Zn()){return this.controllers.set(o,e),o}async removeSession(e){await this.controllers.get(e)?.browser.cleanup(),this.controllers.delete(e)}async resetSession(e,o){await this.removeSession(o),this.registerSession(e,o)}getController(e){return this.controllers.get(e)}},F=new It;function To(t,e,o){let r=async()=>{let s=F.getController(e);if(!s)return;let i=await s.browser.screenshot();t.emit("screenshot",{viewport:await s.browser.viewport(),buffer:i,url:await s.browser.url()})},n=setInterval(()=>{(async()=>{if(F.getController(e)?.browser.closed){clearInterval(n);return}try{await r()}catch(i){clearInterval(n),o.error(i,"Error taking screenshot")}})()},1e3);return n}var es=({metadata:t,rootController:e,logger:o})=>{let{sessionId:r}=t,n=async()=>{e?await e.resetState({clearCookies:!0,clearStorage:!0}):await F.removeSession(r)};return()=>{n().catch(s=>{o.error({err:s},"Error during disconnect cleanup.")})}},Ro={event:"disconnect",createHandler:es};import ft,{readFileSync as hr,readdirSync as Ks}from"fs";import De from"path";import Ws from"diff-lines";import ht from"semver";import*as U from"zod";var et=U.object({id:U.number().int(),role:U.string().optional(),name:U.string().optional(),numChildren:U.number().optional(),content:U.string().optional(),pathFromRoot:U.string().optional(),serializedForm:U.string().optional(),nodeOnlySerializedForm:U.string().optional()});function tt(t){return t.name||t.role||t.content||t.serializedForm}import{z as Ue}from"zod";var xo=Ue.object({thoughts:Ue.string(),result:Ue.boolean(),relevantElements:Ue.array(Ue.number()).optional()});import Ol from"string-argv";import{z as fe}from"zod";import ts from"dedent";import*as p from"zod";var Y=(R=>(R.AI_ASSERTION="AI_ASSERTION",R.AI_WAIT="AI_WAIT",R.CLICK="CLICK",R.SELECT_OPTION="SELECT_OPTION",R.REQUEST="REQUEST",R.TYPE="TYPE",R.PRESS="PRESS",R.NAVIGATE="NAVIGATE",R.SCROLL_UP="SCROLL_UP",R.SCROLL_DOWN="SCROLL_DOWN",R.SCROLL_LEFT="SCROLL_LEFT",R.SCROLL_RIGHT="SCROLL_RIGHT",R.GO_BACK="GO_BACK",R.GO_FORWARD="GO_FORWARD",R.WAIT="WAIT",R.REFRESH="REFRESH",R.TAB="TAB",R.COOKIE="COOKIE",R.LOCAL_STORAGE="LOCAL_STORAGE",R.HOVER="HOVER",R.CAPTCHA="CAPTCHA",R.JAVASCRIPT="JAVASCRIPT",R.SUCCESS="SUCCESS",R))(Y||{}),ae=p.object({elementDescriptor:p.string(),a11yData:et.optional()}),N=p.object({thoughts:p.string().optional()}),os=N.merge(p.object({type:p.literal("NAVIGATE"),url:p.string()})).describe("NAVIGATE <URL> - Go to the specified URL. Only navigate to URLs relevant to the user goal."),rs=N.merge(p.object({target:ae.optional(),type:p.literal("SCROLL_UP"),useVision:p.boolean().default(!1),deltaY:p.number().optional()})).describe("SCROLL_UP [id] - Scroll up while hovering over the element with the specified id. If no id is provided, scroll the entire page."),ns=N.merge(p.object({target:ae.optional(),type:p.literal("SCROLL_DOWN"),useVision:p.boolean().default(!1),deltaY:p.number().optional()})).describe("SCROLL_DOWN [id] - Scroll down while hovering over the element with the specified id. If no id is provided, scroll a single page height."),ss=N.merge(p.object({target:ae.optional(),type:p.literal("SCROLL_LEFT"),useVision:p.boolean().default(!1),deltaX:p.number().optional()})).describe("SCROLL_LEFT [id] - Scroll leftwards while hovering over the element with the specified id. If no id is provided, scroll a single page width."),is=N.merge(p.object({target:ae.optional(),type:p.literal("SCROLL_RIGHT"),useVision:p.boolean().default(!1),deltaX:p.number().optional()})).describe("SCROLL_RIGHT [id] - Scroll rightwards while hovering over the element with the specified id. If no id is provided, scroll a single page width."),as=N.merge(p.object({type:p.literal("WAIT"),delay:p.number()})),ls=N.merge(p.object({type:p.literal("REFRESH")})),cs=N.merge(p.object({type:p.literal("GO_BACK")})),ds=N.merge(p.object({type:p.literal("GO_FORWARD")})),ms=N.merge(p.object({type:p.literal("CAPTCHA"),useVision:p.boolean().default(!1)})),us=N.merge(p.object({type:p.literal("JAVASCRIPT"),code:p.string(),fragment:p.boolean().optional(),timeout:p.number().int().optional().describe("Max seconds for the code to complete")})),ps=N.merge(p.object({type:p.literal("CLICK"),target:ae,doubleClick:p.boolean().default(!1),rightClick:p.boolean().default(!1),useVision:p.boolean().default(!1),force:p.boolean().optional()})).describe(ts`CLICK <id> - click on the element that has the specified id.
|
|
3
3
|
You are NOT allowed to click on disabled or hidden elements.
|
|
4
4
|
Only click on elements on the Current Page.
|
|
5
5
|
You should try to click on relevant elements with the following tag names: button, input, link, image.
|
|
6
6
|
As a last resort, you may click on relevant generic elements.
|
|
7
7
|
`.replaceAll(`
|
|
8
|
-
`," ")),
|
|
8
|
+
`," ")),hs=N.merge(p.object({type:p.literal("HOVER"),target:ae,useVision:p.boolean().default(!1)})),fs=N.merge(p.object({type:p.literal("SELECT_OPTION"),target:ae,option:p.string()})).describe(`SELECT_OPTION <id> "<option>" - select an option from a dropdown-type element on the page. Provide the "id" of the dropdown element in the <id> argument and the "name" of the option to be selected in the <option> argument enclosed by single quotes. ONLY use this command to interact with combobox, listbox, or menu elements. For other element types, use CLICK. For example, to select Option 2 from <combobox id="24">
|
|
9
9
|
<menuitem name="Option 1" />
|
|
10
10
|
<menuitem id="26" name="Option 2" />
|
|
11
|
-
</combobox>, output SELECT_OPTION 24 'Option 2'`),Io=N.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)})),fs=f.object({clearContent:f.boolean().default(!0),pressKeysSequentially:f.boolean().default(!1)}),gs=N.merge(f.object({type:f.literal("TYPE"),target:Ie,value:f.string(),pressEnter:f.boolean().default(!1),useVision:f.boolean().default(!1),force:f.boolean().optional()})).merge(fs).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.'),ys=N.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. Do not provide key codes; only use key names supported by the Playwright press method.'),Ss=N.merge(f.object({type:f.literal("TAB"),url:f.string()})),ws=N.merge(f.object({type:f.literal("COOKIE"),value:f.string()})),bs=N.merge(f.object({type:f.literal("LOCAL_STORAGE"),key:f.string(),value:f.string()})),vs=N.merge(f.object({type:f.literal("REQUEST"),url:f.string(),method:f.union([f.literal("GET"),f.literal("POST"),f.literal("PUT"),f.literal("DELETE"),f.literal("PATCH")]),headers:f.record(f.string(),f.string()).optional(),params:f.record(f.string(),f.string()).optional(),body:f.string().optional(),timeout:f.number().optional().describe("Max seconds to wait for the request to complete")})),Es=N.merge(f.object({type:f.literal("SUCCESS"),condition:Io.optional()})).describe("SUCCESS - the user goal has been successfully achieved"),Le=f.discriminatedUnion("type",[us,gs,ys,hs,rs,ss,ns,Es]),As=f.discriminatedUnion("type",[ls,cs,as,Io,is,Ss,ws,bs,ps,vs,ds,ms]),Lo=f.discriminatedUnion("type",[...Le.options,...As.options]),Cs=N.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"),Lt=f.discriminatedUnion("type",[...Le.options,Cs]),Ot=t=>{switch(t){case"NAVIGATE":return{type:"NAVIGATE",url:""};case"CAPTCHA":return{type:t,useVision:!1};case"GO_BACK":case"GO_FORWARD":case"REFRESH":return{type:t};case"SCROLL_DOWN":case"SCROLL_UP":return{type:t,useVision:!1};case"WAIT":return{type:t,delay:1};case"CLICK":return{type:t,target:{elementDescriptor:""},doubleClick:!1,rightClick:!1,useVision:!1};case"HOVER":return{type:t,target:{elementDescriptor:""},useVision:!1};case"TYPE":return{type:t,target:{elementDescriptor:""},value:"",clearContent:!0,pressEnter:!1,pressKeysSequentially:!1,useVision:!1};case"PRESS":return{type:t,value:""};case"SELECT_OPTION":return{type:t,target:{elementDescriptor:""},option:""};case"TAB":return{type:t,url:""};case"REQUEST":return{type:t,url:"",method:"GET"};case"COOKIE":return{type:t,value:""};case"LOCAL_STORAGE":return{type:t,key:"",value:""};case"JAVASCRIPT":return{type:t,code:""};case"AI_ASSERTION":return{type:t,assertion:"",disableCache:!1,useVision:!1,cancelOnFailure:!1};case"SUCCESS":return{type:t};default:return(o=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(t)}};var Oe=(a=>(a.AI_PROVIDER="AIProviderError",a.AI_TIMEOUT="AITimeoutError",a.JOB_TIMEOUT="JobTimeoutError",a.ACTION_FAILURE="ActionFailureError",a.ASSERTION_FAILURE="AssertionFailureError",a.WEB_AGENT_PLATFORM="InternalWebAgentError",a.UNKNOWN_PLATFORM="InternalPlatformError",a))(Oe||{});var rt=class extends Error{constructor(e={}){super("Got empty a11y tree",e),this.name="EmptyA11yTreeError"}};var T=class extends Error{reason;emitToUser;constructor(e,o,r={},n=!1){let s=!1;for(let i of Object.values(Oe))if(o.startsWith(i)){s=!0,e=i;break}s?super(o,r):super(`${e}: ${o}`,r),this.name="TestFailureError",this.stack=this.stack?.slice(this.name.length+2),this.reason=e,this.emitToUser=n}toString(){return this.message}toJSON(){return{message:this.message}}};var Pl=pe.object({command:pe.string(),thoughts:pe.string()}),Dl=pe.string().pipe(pe.coerce.number());var Oo=pe.object({phrase:pe.string()});import*as P from"zod";var F="1.0.6",D=(r=>(r.AI_ACTION="AI_ACTION",r.PRESET_ACTION="PRESET_ACTION",r.MODULE="MODULE",r))(D||{}),$e=P.object({type:P.literal("AI_ACTION"),text:P.string(),commands:P.array(Le).optional()}),He=P.object({type:P.literal("PRESET_ACTION"),command:Lo}),Mt=P.object({type:P.literal("MODULE"),moduleId:P.string().uuid()}),he=P.union([$e,He]),Be=P.object({type:P.literal("RESOLVED_MODULE"),moduleId:P.string().uuid(),name:P.string(),steps:he.array()}),fe=P.union([$e,He,Mt]),Ge=P.union([$e,He,Be]);var Hl=new Set(Object.values(j));var Nt={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",LOCAL_STORAGE:"Set local storage",REQUEST:"Request",JAVASCRIPT:"JavaScript",SUCCESS:"Done"},Bl={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",LOCAL_STORAGE:"Set a local storage value that will persist throughout the browser session",CAPTCHA:"Solve captchas on the page. This may take 10-60 seconds.",REQUEST:"Make an API request to a URL.",JAVASCRIPT:"Run JavaScript code in an isolated context.",SUCCESS:"Indicate the entire AI action has succeeded, optionally based on a condition."};import*as A from"zod";var ge=(s=>(s.SUCCESS="SUCCESS",s.FAILED="FAILED",s.RUNNING="RUNNING",s.IDLE="IDLE",s.CANCELLED="CANCELLED",s))(ge||{}),nt=(r=>(r.SUCCESS="SUCCESS",r.FAILED="FAILED",r.CANCELLED="CANCELLED",r))(nt||{}),Ts=A.object({beforeUrl:A.string(),beforeScreenshot:A.string().or(A.any()).optional(),afterUrl:A.string().optional(),afterScreenshot:A.string().or(A.any()).optional(),startedAt:A.coerce.date(),finishedAt:A.coerce.date(),viewport:A.object({height:A.number(),width:A.number()}),status:A.nativeEnum(nt),message:A.string().optional(),elementInteracted:A.string().optional()}),st=A.object({startedAt:A.coerce.date(),finishedAt:A.coerce.date(),status:A.nativeEnum(ge),message:A.string().optional(),data:A.record(A.string(),A.unknown()).optional(),userAgent:A.string().optional()}),Pt=He.merge(st).merge(A.object({results:Ts.array()})),Mo=$e.merge(st).merge(A.object({results:Pt.array()})),Rs=Mt.merge(st).merge(A.object({results:A.union([Mo,Pt]).array()})),it=A.discriminatedUnion("type",[Mo,Pt,Rs]),Z=st.pick({startedAt:!0,finishedAt:!0,status:!0,message:!0,data:!0});function No(t,e){return t.length<e?t:t.slice(0,e-3)+"[...]"}function Me(t){switch(t.type){case"SUCCESS":return t.condition?.assertion?`Check success condition: ${t.condition.assertion}`:"All commands completed";case"NAVIGATE":return`Go to URL: ${No(t.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${t.target?` in the container of: ${t.target.elementDescriptor}`:""}`;case"SCROLL_UP":return`Scroll up one page${t.target?` in the container of: ${t.target.elementDescriptor}`:""}`;case"WAIT":return`Wait for ${t.delay} seconds`;case"REFRESH":return"Refresh the page";case"CLICK":return`Click on '${t.target.elementDescriptor}'`;case"TYPE":{let o="";return t.target.a11yData?.nodeOnlySerializedForm?o=`in element: ${t.target.a11yData.nodeOnlySerializedForm}`:t.target.elementDescriptor.length>0&&(o=`in element: ${t.target.elementDescriptor}`),`Type '${t.value}' ${o}`}case"HOVER":{let o="";return t.target.a11yData?.serializedForm?o=` over element: ${t.target.a11yData.serializedForm}`:t.target.elementDescriptor.length>0&&(o=` over element: ${t.target.elementDescriptor}`),`Hover${o}`}case"PRESS":return`Press ${t.value}`;case"SELECT_OPTION":return`Select option '${t.option}' in '${t.target.elementDescriptor}'`;case"TAB":return`Switch to tab: ${t.url}`;case"REQUEST":return`Send ${t.method} request to ${t.url}`;case"COOKIE":return`Set cookie: ${t.value}`;case"LOCAL_STORAGE":return`Set local storage: ${t.key}: ${t.value}`;case"JAVASCRIPT":return`Run JavaScript: ${No(t.code,30)}`;case"AI_ASSERTION":return`${t.useVision?"Visual assertion":"Assertion"}: '${t.assertion}'`;default:return(o=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(t)}}import*as V from"zod";import*as ye from"zod";var Po=ye.object({type:ye.nativeEnum(D),generatedStep:Le.optional(),serializedCommand:ye.string().optional(),elementInteracted:ye.string().optional()});var ie=V.object({goal:V.string(),url:V.string(),browserState:V.string(),history:V.string(),numPrevious:V.number(),lastCommand:Po.or(V.null())});import{parseString as xs}from"set-cookie-parser";function Do(t){let e=xs(t);if(!e.name)throw new Error("Name missing from cookie");if(!e.value)throw new Error("Value missing from cookie");let o;if(e.sameSite){let n=e.sameSite.trim().toLowerCase();if(n==="strict")o="Strict";else if(n==="lax")o="Lax";else if(n==="none")o="None";else throw new Error(`Invalid sameSite setting in cookie: ${n}`)}return!e.path&&e.domain&&(e.path="/"),{...e,expires:e.expires?e.expires.getTime()/1e3:void 0,sameSite:o}}import{z as Ne}from"zod";var _o=(o=>(o.PROD="production",o.DEV="development",o))(_o||{}),oc=Object.values(_o),at="BASE_URL",rc={[at]:"https://www.google.com"},ko=Ne.string().describe("Name of the fixture (must be available locally in the fixtures directory)."),W=Ne.object({name:Ne.string(),variables:Ne.record(Ne.string().describe("variable name"),Ne.string().describe("variable value"))});import{z as U}from"zod";var Is="1.0.0",Fo=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")}),zo=U.object({type:U.literal("momentic/fixture"),schemaVersion:U.string(),name:U.string(),description:U.string().optional(),setup:U.object({steps:Fo.array(),timeout:U.number().optional().describe("Timeout for all steps in seconds")}).optional(),teardown:U.object({steps:Fo.array(),timeout:U.number().optional().describe("Timeout for all steps in seconds")}).optional()}),lc={type:"momentic/fixture",schemaVersion:Is,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 Ls}from"zod";var mc=Ls.string().array();import{z as I}from"zod";import{z as L}from"zod";import{isValidCron as Os}from"cron-validator";import{z as $}from"zod";var ae=$.object({availableAsModule:$.boolean().default(!1),disableAICaching:$.boolean().default(!1)}),Uo=$.object({cron:$.string().refine(t=>Os(t),{message:"Invalid cron expression."}).default("0 0 */1 * *"),enabled:$.boolean().default(!1),env:$.string().optional(),timeZone:$.string().default("America/Los_Angeles"),jobKey:$.string().optional()}),$o=$.object({onSuccess:$.boolean().default(!1),onFailure:$.boolean().default(!0)});var Ms=L.string().min(1).max(255).superRefine((t,e)=>{try{je(t)}catch(o){return e.addIssue({code:L.ZodIssueCode.custom,message:o.message,fatal:!0}),L.NEVER}}),Ns=L.object({name:L.string(),defaultOnCloud:L.boolean().optional(),defaultOnLocal:L.boolean().optional(),fixtures:ko.array().optional()}),ee=L.object({id:L.string(),name:Ms,baseUrl:L.string().optional(),schemaVersion:L.string(),advanced:ae,retries:L.number(),envs:L.array(Ns).optional(),localOnly:L.boolean().optional()}),bc=ee.pick({name:!0,baseUrl:!0,retries:!0,advanced:!0}),Ps=L.object({createdAt:L.coerce.date(),updatedAt:L.coerce.date(),schedule:Uo,notification:$o,createdBy:L.string(),organizationId:L.string()}),Ho=ee.merge(Ps).merge(L.object({steps:L.array(Ge)})),lt=ee.merge(L.object({steps:L.array(Ge)})),vc=ee.merge(L.object({steps:fe.array()})),Ds=/^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/,_s=["modules","fixtures","environments"];function je(t){if(t=t.toLowerCase().trim(),t.length===0||t.length>255)throw new Error("Name must be between 1 and 255 characters long");if(/[<>:"\/\\|?*\x00]/.test(t))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(t))throw new Error(`"${t}" is a reserved name on Windows and cannot be used as a filename.`);if(/^\.+$/.test(t)||/^\s|\s$/.test(t))throw new Error("Name cannot start or end with a space or dot.");if(t.endsWith(".yaml"))throw new Error('Name cannot end with ".yaml".');if(_s.includes(t))throw new Error("'modules' is a reserved folder name in Momentic. Please choose a different name.");if(t.match(Ds))throw new Error("Name cannot be a UUID. Please choose a different name.")}var le="momentic",te="modules",Bo="fixtures",ct="environments",_t="momentic-frame",dt=`${_t}-0`,xc=I.array(I.object({id:I.string(),name:I.string(),fullFilePath:I.string(),testPath:I.string().describe("path relative to the root test directory, i.e. my-folder/my-test.yaml"),fileName:I.string(),lastModified:I.coerce.date(),createdAt:I.coerce.date()}));var Go=I.object({steps:Ge.array()}),jo=I.object({name:I.string(),baseUrl:I.string().url().optional(),environment:I.string().optional()}),Ic=lt.merge(I.object({testPath:I.string()})),Vo=I.object({name:I.string(),steps:he.array()});var Lc=I.array(Be),Oc=I.array(I.object({name:I.string(),moduleId:I.string().uuid(),numSteps:I.number()})),Mc=I.array(W);import*as oe from"zod";var Wo=oe.object({thoughts:oe.string(),id:oe.number().int(),options:oe.array(oe.string()).optional()});var Ko={DEBUG:0,INFO:1,WARN:2,ERROR:3},ks={0:"DEBUG",1:"INFO",2:"WARN",3:"ERROR"},Fs={0:"\x1B[90m",1:"\x1B[32m",2:"\x1B[33m",3:"\x1B[31m"},kt=class t{minLogLevel;logBindings;constructor(e,o){this.minLogLevel=e,this.logBindings=o}logWithLevel(e,...o){let r=ks[e],n;Array.isArray(o[0])?(n=o[0],o=o.slice(1)):typeof o[0]=="object"&&!(o[0]instanceof Error)&&(n={...o[0],...this.logBindings},o=o.slice(1));let s=Fs[e],i=[`${s}[${new Date().toTimeString().slice(0,8)}][${r}]`];if(e!==0&&i.push("\x1B[39m"),i.push(...o),console.log(...i),n&&!Array.isArray(n))for(let[a,l]of Object.entries(n)){let c=l;l instanceof Error?c=l.message:typeof l=="object"&&(c=JSON.stringify(l,void 0,2),c=c.split(`
|
|
11
|
+
</combobox>, output SELECT_OPTION 24 'Option 2'`),Lt=N.merge(p.object({type:p.literal("AI_ASSERTION"),assertion:p.string(),useVision:p.boolean().default(!1),disableCache:p.boolean().default(!1),cancelOnFailure:p.boolean().default(!1)})),gs=Lt.merge(p.object({type:p.literal("AI_WAIT"),timeout:p.number().int().optional().describe("Max seconds to wait for assertion to be true")})),ys=p.object({clearContent:p.boolean().default(!0),pressKeysSequentially:p.boolean().default(!1)}),Ss=N.merge(p.object({type:p.literal("TYPE"),target:ae.optional(),value:p.string(),pressEnter:p.boolean().default(!1),useVision:p.boolean().default(!1),force:p.boolean().optional()})).merge(ys).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.'),ws=N.merge(p.object({type:p.literal("PRESS"),value:p.string()})).describe('PRESS <key> - press the specified key, such as "ArrowLeft", "Enter", or "a". You must specify at least one key. Do not provide key codes; only use key names supported by the Playwright press method.'),bs=N.merge(p.object({type:p.literal("TAB"),url:p.string()})),vs=N.merge(p.object({type:p.literal("COOKIE"),value:p.string()})),Es=N.merge(p.object({type:p.literal("LOCAL_STORAGE"),key:p.string(),value:p.string()})),As=N.merge(p.object({type:p.literal("REQUEST"),url:p.string(),method:p.union([p.literal("GET"),p.literal("POST"),p.literal("PUT"),p.literal("DELETE"),p.literal("PATCH")]),headers:p.record(p.string(),p.string()).optional(),params:p.record(p.string(),p.string()).optional(),body:p.string().optional(),timeout:p.number().int().optional().describe("Max seconds to wait for the request to complete")})),Cs=N.merge(p.object({type:p.literal("SUCCESS"),condition:Lt.optional()})).describe("SUCCESS - the user goal has been successfully achieved"),Oe=p.discriminatedUnion("type",[ps,Ss,ws,fs,os,ns,rs,Cs]),Ts=p.discriminatedUnion("type",[Lt,gs,ms,vs,cs,ds,hs,us,Es,ls,As,ss,is,bs,as]),Io=p.discriminatedUnion("type",[...Oe.options,...Ts.options]),Rs=N.merge(p.object({type:p.literal("FAILURE")})).describe("FAILURE - there are no commands to suggest that could make progress that have not already been tried before"),Ot=p.discriminatedUnion("type",[...Oe.options,Rs]),ot=t=>{switch(t){case"NAVIGATE":return{type:"NAVIGATE",url:""};case"CAPTCHA":return{type:t,useVision:!1};case"GO_BACK":case"GO_FORWARD":case"REFRESH":return{type:t};case"SCROLL_DOWN":case"SCROLL_UP":case"SCROLL_LEFT":case"SCROLL_RIGHT":return{type:t,useVision:!1};case"WAIT":return{type:t,delay:1};case"CLICK":return{type:t,target:{elementDescriptor:""},doubleClick:!1,rightClick:!1,useVision:!1};case"HOVER":return{type:t,target:{elementDescriptor:""},useVision:!1};case"TYPE":return{type:t,target:void 0,value:"",clearContent:!0,pressEnter:!1,pressKeysSequentially:!1,useVision:!1};case"PRESS":return{type:t,value:""};case"SELECT_OPTION":return{type:t,target:{elementDescriptor:""},option:""};case"TAB":return{type:t,url:""};case"REQUEST":return{type:t,url:"",method:"GET"};case"COOKIE":return{type:t,value:""};case"LOCAL_STORAGE":return{type:t,key:"",value:""};case"JAVASCRIPT":return{type:t,code:""};case"AI_WAIT":case"AI_ASSERTION":return{type:t,assertion:"",disableCache:!1,useVision:!1,cancelOnFailure:!1};case"SUCCESS":return{type:t};default:return(o=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(t)}};var Me=(a=>(a.AI_PROVIDER="AIProviderError",a.AI_TIMEOUT="AITimeoutError",a.JOB_TIMEOUT="JobTimeoutError",a.ACTION_FAILURE="ActionFailureError",a.ASSERTION_FAILURE="AssertionFailureError",a.WEB_AGENT_PLATFORM="InternalWebAgentError",a.UNKNOWN_PLATFORM="InternalPlatformError",a))(Me||{});var rt=class extends Error{constructor(e={}){super("Got empty a11y tree",e),this.name="EmptyA11yTreeError"}};var T=class extends Error{reason;emitToUser;constructor(e,o,r={},n=!1){let s=!1;for(let i of Object.values(Me))if(o.startsWith(i)){s=!0,e=i;break}s?super(o,r):super(`${e}: ${o}`,r),this.name="TestFailureError",this.stack=this.stack?.slice(this.name.length+2),this.reason=e,this.emitToUser=n}toString(){return this.message}toJSON(){return{message:this.message}}};var Dl=fe.object({command:fe.string(),thoughts:fe.string()}),_l=fe.string().pipe(fe.coerce.number());var Lo=fe.object({phrase:fe.string()});import*as D from"zod";var z="1.0.6",_=(r=>(r.AI_ACTION="AI_ACTION",r.PRESET_ACTION="PRESET_ACTION",r.MODULE="MODULE",r))(_||{}),$e=D.object({type:D.literal("AI_ACTION"),text:D.string(),commands:D.array(Oe).optional()}),He=D.object({type:D.literal("PRESET_ACTION"),command:Io}),Mt=D.object({type:D.literal("MODULE"),moduleId:D.string().uuid()}),ge=D.union([$e,He]),Be=D.object({type:D.literal("RESOLVED_MODULE"),moduleId:D.string().uuid(),name:D.string(),steps:ge.array()}),ye=D.union([$e,He,Mt]),Ge=D.union([$e,He,Be]);var Bl=new Set(Object.values(Y));var Nt={AI_ACTION:"AI action",MODULE:"Module",AI_ASSERTION:"AI check",AI_WAIT:"AI wait",CLICK:"Click",HOVER:"Hover",SELECT_OPTION:"Select",TYPE:"Type",PRESS:"Press",NAVIGATE:"Navigate",SCROLL_UP:"Scroll up",SCROLL_DOWN:"Scroll down",SCROLL_LEFT:"Scroll left",SCROLL_RIGHT:"Scroll right",CAPTCHA:"Captcha",GO_BACK:"Go back",GO_FORWARD:"Go forward",WAIT:"Wait",REFRESH:"Refresh",TAB:"Switch tab",COOKIE:"Set cookie",LOCAL_STORAGE:"Set local storage",REQUEST:"Request",JAVASCRIPT:"JavaScript",SUCCESS:"Done"},Gl={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.",AI_WAIT:"Wait until AI considers a condition to be true.",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 by a specified height.",SCROLL_DOWN:"Scroll down by a specified height.",SCROLL_LEFT:"Scroll left by a specified width.",SCROLL_RIGHT:"Scroll right by a specified width.",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",LOCAL_STORAGE:"Set a local storage value that will persist throughout the browser session",CAPTCHA:"Solve captchas on the page. This may take 10-60 seconds.",REQUEST:"Make an API request to a URL.",JAVASCRIPT:"Run JavaScript code in an isolated context.",SUCCESS:"Indicate the entire AI action has succeeded, optionally based on a condition."};import*as A from"zod";var Se=(s=>(s.SUCCESS="SUCCESS",s.FAILED="FAILED",s.RUNNING="RUNNING",s.IDLE="IDLE",s.CANCELLED="CANCELLED",s))(Se||{}),nt=(r=>(r.SUCCESS="SUCCESS",r.FAILED="FAILED",r.CANCELLED="CANCELLED",r))(nt||{}),xs=A.object({beforeUrl:A.string(),beforeScreenshot:A.string().or(A.any()).optional(),afterUrl:A.string().optional(),afterScreenshot:A.string().or(A.any()).optional(),startedAt:A.coerce.date(),finishedAt:A.coerce.date(),viewport:A.object({height:A.number(),width:A.number()}),status:A.nativeEnum(nt),message:A.string().optional(),elementInteracted:A.string().optional()}),st=A.object({startedAt:A.coerce.date(),finishedAt:A.coerce.date(),status:A.nativeEnum(Se),message:A.string().optional(),data:A.record(A.string(),A.unknown()).optional(),userAgent:A.string().optional()}),Pt=He.merge(st).merge(A.object({results:xs.array()})),Oo=$e.merge(st).merge(A.object({results:Pt.array()})),Is=Mt.merge(st).merge(A.object({results:A.union([Oo,Pt]).array()})),it=A.discriminatedUnion("type",[Oo,Pt,Is]),ee=st.pick({startedAt:!0,finishedAt:!0,status:!0,message:!0,data:!0});function Mo(t,e){return t.length<e?t:t.slice(0,e-3)+"[...]"}function Ne(t){switch(t.type){case"SUCCESS":return t.condition?.assertion?`Check success condition: ${t.condition.assertion}`:"All commands completed";case"NAVIGATE":return`Go to URL: ${Mo(t.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 ${t.deltaY?`${t.deltaY}px`:"1 page height"}${t.target?` in the container of: ${t.target.elementDescriptor}`:""}`;case"SCROLL_UP":return`Scroll up ${t.deltaY?`${t.deltaY}px`:"1 page height"}${t.target?` in the container of: ${t.target.elementDescriptor}`:""}`;case"SCROLL_LEFT":return`Scroll left ${t.deltaX?`${t.deltaX}px`:"1 page width"}${t.target?` in the container of: ${t.target.elementDescriptor}`:""}`;case"SCROLL_RIGHT":return`Scroll right ${t.deltaX?`${t.deltaX}px`:"1 page width"}${t.target?` in the container of: ${t.target.elementDescriptor}`:""}`;case"WAIT":return`Wait for ${t.delay} seconds`;case"REFRESH":return"Refresh the page";case"CLICK":return`Click on '${t.target.elementDescriptor}'`;case"TYPE":{let o="";return t.target?.a11yData?.nodeOnlySerializedForm?o=`in element: ${t.target.a11yData.nodeOnlySerializedForm}`:t.target?.elementDescriptor.length&&(o=`in element: ${t.target.elementDescriptor}`),`Type '${t.value}' ${o}`}case"HOVER":{let o="";return t.target.a11yData?.serializedForm?o=` over element: ${t.target.a11yData.serializedForm}`:t.target.elementDescriptor.length>0&&(o=` over element: ${t.target.elementDescriptor}`),`Hover${o}`}case"PRESS":return`Press ${t.value}`;case"SELECT_OPTION":return`Select option '${t.option}' in '${t.target.elementDescriptor}'`;case"TAB":return`Switch to tab: ${t.url}`;case"REQUEST":return`Send ${t.method} request to ${t.url}`;case"COOKIE":return`Set cookie: ${t.value}`;case"LOCAL_STORAGE":return`Set local storage: ${t.key}: ${t.value}`;case"JAVASCRIPT":return`Run JavaScript: ${Mo(t.code,30)}`;case"AI_ASSERTION":return`${t.useVision?"Visual assertion":"Assertion"}: '${t.assertion}'`;case"AI_WAIT":return`Wait until ${t.useVision?"visual assertion":"assertion"} is true: '${t.assertion}'`;default:return(o=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(t)}}import*as W from"zod";import*as we from"zod";var No=we.object({type:we.nativeEnum(_),generatedStep:Oe.optional(),serializedCommand:we.string().optional(),elementInteracted:we.string().optional()});var le=W.object({goal:W.string(),url:W.string(),browserState:W.string(),history:W.string(),numPrevious:W.number(),lastCommand:No.or(W.null())});import{parseString as Ls}from"set-cookie-parser";function Po(t){let e=Ls(t);if(!e.name)throw new Error("Name missing from cookie");if(!e.value)throw new Error("Value missing from cookie");let o;if(e.sameSite){let n=e.sameSite.trim().toLowerCase();if(n==="strict")o="Strict";else if(n==="lax")o="Lax";else if(n==="none")o="None";else throw new Error(`Invalid sameSite setting in cookie: ${n}`)}return!e.path&&e.domain&&(e.path="/"),{...e,expires:e.expires?e.expires.getTime()/1e3:void 0,sameSite:o}}import{z as Pe}from"zod";var Do=(o=>(o.PROD="production",o.DEV="development",o))(Do||{}),rc=Object.values(Do),at="BASE_URL",nc={[at]:"https://www.google.com"},_o=Pe.string().describe("Name of the fixture (must be available locally in the fixtures directory)."),V=Pe.object({name:Pe.string(),variables:Pe.record(Pe.string().describe("variable name"),Pe.string().describe("variable value"))});import{z as $}from"zod";var Os="1.0.0",ko=$.object({run:$.string().describe("Run a single command in the shell. The working directory will be set to where the CLI was invoked from."),waitForCompletion:$.boolean().optional().describe("Defaults to true")}),Fo=$.object({type:$.literal("momentic/fixture"),schemaVersion:$.string(),name:$.string(),description:$.string().optional(),setup:$.object({steps:ko.array(),timeout:$.number().optional().describe("Timeout for all steps in seconds")}).optional(),teardown:$.object({steps:ko.array(),timeout:$.number().optional().describe("Timeout for all steps in seconds")}).optional()}),cc={type:"momentic/fixture",schemaVersion:Os,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 Ms}from"zod";var uc=Ms.string().array();import{z as I}from"zod";import{z as L}from"zod";import{isValidCron as Ns}from"cron-validator";import{z as H}from"zod";var ce=H.object({availableAsModule:H.boolean().default(!1),disableAICaching:H.boolean().default(!1)}),zo=H.object({cron:H.string().refine(t=>Ns(t),{message:"Invalid cron expression."}).default("0 0 */1 * *"),enabled:H.boolean().default(!1),env:H.string().optional(),timeZone:H.string().default("America/Los_Angeles"),jobKey:H.string().optional()}),Uo=H.object({onSuccess:H.boolean().default(!1),onFailure:H.boolean().default(!0)});var Ps=L.string().min(1).max(255).superRefine((t,e)=>{try{je(t)}catch(o){return e.addIssue({code:L.ZodIssueCode.custom,message:o.message,fatal:!0}),L.NEVER}}),Ds=L.object({name:L.string(),defaultOnCloud:L.boolean().optional(),defaultOnLocal:L.boolean().optional(),fixtures:_o.array().optional()}),te=L.object({id:L.string(),name:Ps,baseUrl:L.string().optional(),schemaVersion:L.string(),advanced:ce,retries:L.number(),envs:L.array(Ds).optional(),localOnly:L.boolean().optional()}),vc=te.pick({name:!0,baseUrl:!0,retries:!0,advanced:!0}),_s=L.object({createdAt:L.coerce.date(),updatedAt:L.coerce.date(),schedule:zo,notification:Uo,createdBy:L.string(),organizationId:L.string()}),$o=te.merge(_s).merge(L.object({steps:L.array(Ge)})),lt=te.merge(L.object({steps:L.array(Ge)})),Ec=te.merge(L.object({steps:ye.array()})),ks=/^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/,Fs=["modules","fixtures","environments"];function je(t){if(t=t.toLowerCase().trim(),t.length===0||t.length>255)throw new Error("Name must be between 1 and 255 characters long");if(/[<>:"\/\\|?*\x00]/.test(t))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(t))throw new Error(`"${t}" is a reserved name on Windows and cannot be used as a filename.`);if(/^\.+$/.test(t)||/^\s|\s$/.test(t))throw new Error("Name cannot start or end with a space or dot.");if(t.endsWith(".yaml"))throw new Error('Name cannot end with ".yaml".');if(Fs.includes(t))throw new Error("'modules' is a reserved folder name in Momentic. Please choose a different name.");if(t.match(ks))throw new Error("Name cannot be a UUID. Please choose a different name.")}var de="momentic",oe="modules",Ho="fixtures",ct="environments",_t="momentic-frame",dt=`${_t}-0`,Ic=I.array(I.object({id:I.string(),name:I.string(),fullFilePath:I.string(),testPath:I.string().describe("path relative to the root test directory, i.e. my-folder/my-test.yaml"),fileName:I.string(),lastModified:I.coerce.date(),createdAt:I.coerce.date()}));var Bo=I.object({steps:Ge.array()}),Go=I.object({name:I.string(),baseUrl:I.string().url().optional(),environment:I.string().optional()}),Lc=lt.merge(I.object({testPath:I.string()})),jo=I.object({name:I.string(),steps:ge.array()});var Oc=I.array(Be),Mc=I.array(I.object({name:I.string(),moduleId:I.string().uuid(),numSteps:I.number()})),Nc=I.array(V);import*as re from"zod";var Wo=re.object({thoughts:re.string(),id:re.number().int(),options:re.array(re.string()).optional()});var Vo={DEBUG:0,INFO:1,WARN:2,ERROR:3},zs={0:"DEBUG",1:"INFO",2:"WARN",3:"ERROR"},Us={0:"\x1B[90m",1:"\x1B[32m",2:"\x1B[33m",3:"\x1B[31m"},kt=class t{minLogLevel;logBindings;constructor(e,o){this.minLogLevel=e,this.logBindings=o}logWithLevel(e,...o){let r=zs[e],n;Array.isArray(o[0])?(n=o[0],o=o.slice(1)):typeof o[0]=="object"&&!(o[0]instanceof Error)&&(n={...o[0],...this.logBindings},o=o.slice(1));let s=Us[e],i=[`${s}[${new Date().toTimeString().slice(0,8)}][${r}]`];if(e!==0&&i.push("\x1B[39m"),i.push(...o),console.log(...i),n&&!Array.isArray(n))for(let[a,l]of Object.entries(n)){let c=l;l instanceof Error?c=l.message:typeof l=="object"&&(c=JSON.stringify(l,void 0,2),c=c.split(`
|
|
12
12
|
`).map((d,u)=>u>0?` ${d}`:d).join(`
|
|
13
13
|
`)),console.log(e===0?`${s} ${a}:`:` ${a}:`,c)}else if(n)for(let a of n){let l=a;typeof a=="object"&&(l=JSON.stringify(a,void 0,2),l=l.split(`
|
|
14
14
|
`).map((c,d)=>d>0?` ${c}`:c).join(`
|
|
15
|
-
`)),console.log(e===0?`${s} `:" ",l)}e===0&&process.stdout.write("\x1B[39m")}setMinLevel(e){this.minLogLevel=e}log(...e){this.info(...e)}info(...e){1<this.minLogLevel||this.logWithLevel(1,...e)}debug(...e){0<this.minLogLevel||this.logWithLevel(0,...e)}warn(...e){2<this.minLogLevel||this.logWithLevel(2,...e)}error(...e){3<this.minLogLevel||this.logWithLevel(3,...e)}child(e){return new t(this.minLogLevel,{...this.logBindings,...e})}flush(){}bindings(){return this.logBindings}},g=new kt(1,{});import{z as re}from"zod";var zs=re.object({id:re.string(),createdAt:re.coerce.date(),createdBy:re.string(),organizationId:re.string(),name:re.string(),schemaVersion:re.string(),numSteps:re.number()}),Fc=re.object({steps:he.array()}).merge(zs.omit({numSteps:!0}));var Ve=t=>{let e=t.trim().toLowerCase().replace(/[^a-z0-9]/g,"-");for(;e.includes("--");)e=e.replaceAll("--","-");return e.startsWith("-")&&(e=e.slice(1)),e.endsWith("-")&&(e=e.slice(0,e.length-1)),e};import*as S from"zod";import{z as R}from"zod";var Ft={WEBHOOK:"WEBHOOK",CRON:"CRON",MANUAL:"MANUAL",CLI:"CLI"},zt={PENDING:"PENDING",RUNNING:"RUNNING",PASSED:"PASSED",FAILED:"FAILED",CANCELLED:"CANCELLED"},Us={PASSED:"PASSED",FAILED:"FAILED"},mt=R.string().pipe(R.coerce.date()).or(R.date()),$s=R.object({id:R.string(),createdAt:mt,createdBy:R.string(),organizationId:R.string(),scheduledAt:mt.or(R.null()),startedAt:mt.or(R.null()),finishedAt:mt.or(R.null()),testId:R.string().or(R.null()),status:R.nativeEnum(zt),expectedStatus:R.nativeEnum(Us).or(R.null()),runKey:R.string(),trigger:R.nativeEnum(Ft),attempts:R.number(),test:R.object({name:R.string(),id:R.string()}).or(R.null())}),Ut=$s.merge(R.object({results:it.array(),test:R.object({name:R.string(),id:R.string(),baseUrl:R.string()}).or(R.null())}));var Se=S.object({disableCache:S.boolean()}),Zc=S.object({error:S.boolean(),reason:S.string(),message:S.string()}),ed=ie.merge(Se),qo=Lt,td=S.discriminatedUnion("vision",[ie.merge(Se).merge(S.object({vision:S.literal(!1)})),ie.pick({goal:!0,url:!0}).merge(Se).merge(S.object({screenshot:S.string(),vision:S.literal(!0)}))]),$t=xo,od=ie.pick({browserState:!0,goal:!0}).merge(Se),rd=ie.pick({goal:!0}).merge(Se).merge(S.object({screenshot:S.string().describe("base64 encoded image"),hintActivatedScreenshot:S.string().describe("base64 encoded image")})),Ht=Wo,nd=ie.pick({goal:!0,url:!0}).merge(Se),Yo=S.string().array(),sd=ie.pick({goal:!0,browserState:!0}).merge(Se),Xo=Oo,id=S.object({testPaths:S.string().array().describe("can be either hyphenated, lowercase test names or UUIDs"),env:S.string().optional(),all:S.boolean().optional()}),Jo=S.object({message:S.string(),queuedTests:S.object({name:S.string(),id:S.string()}).array()}),Qo=Ho,Zo=S.string().array(),ad=S.union([S.object({paths:S.string().array().describe("run specific test paths (e.g. todo-test)")}),S.object({path:S.string().describe("deprecated; present for backcompat")}),S.object({all:S.boolean().describe("run all tests")})]),er=S.object({tests:S.record(S.string().describe("Test name"),S.string().describe("Test YAML")),modules:S.record(S.string().describe("Module name"),S.string().describe("Module YAML"))}),Hs=S.object({test:S.string().describe("test YAML"),modules:S.record(S.string().describe("moduleId"),S.string().describe("module YAML"))}),ld=Hs.array();var cd=S.object({testPath:S.string(),testId:S.string()}).partial().merge(S.object({trigger:S.nativeEnum(Ft)})),tr=Ut,or=Ut,dd=S.object({startedAt:S.coerce.date(),finishedAt:S.coerce.date(),results:it.array(),status:S.nativeEnum(zt)}).partial(),md=S.object({screenshot:S.string()}),rr=S.object({key:S.string()}),nr=S.object({orgId:S.string()}),sr=S.array(W),ud=S.array(W);function ut(t){switch(t.type){case"AI_ACTION":return`AI action: ${t.text}`;case"PRESET_ACTION":return Me(t.command);case"RESOLVED_MODULE":return`Module: ${t.moduleId}`}}import{stringify as Bt}from"yaml";import{z as M}from"zod";var Rd=M.object({test:M.string().describe("YAML for the test, including metadata and steps"),modules:M.record(M.string(),M.string()).describe("Map of module name to YAML for the module")}),xd=ee.merge(M.object({steps:fe.array(),fileType:M.literal("momentic/test")})),Gt=Be.omit({type:!0}).merge(M.object({schemaVersion:M.string(),fileType:M.literal("momentic/module")})),Id=ee.merge(M.object({steps:M.array(M.record(M.string(),M.unknown()))})),Ld=M.object({moduleId:M.string().uuid(),name:M.string(),schemaVersion:M.string(),steps:M.array(M.record(M.string(),M.unknown()))});function ir(t,e=new Set){let o={id:t.id,name:t.name,baseUrl:t.baseUrl,schemaVersion:t.schemaVersion,advanced:t.advanced,retries:t.retries,localOnly:t.localOnly,envs:t.envs},r={},n=ar(t.steps,t.schemaVersion,r,e),s={fileType:"momentic/test",...o,steps:n};return{test:Bt(s),modules:r}}function ar(t,e,o,r){let n=[];for(let s of t)switch(s.type){case"PRESET_ACTION":n.push(s);break;case"AI_ACTION":let i=(s.commands??[]).map(l=>({...l,thoughts:void 0}));n.push({...s,commands:i});break;case"RESOLVED_MODULE":r.has(s.name)||(o[s.name]=Bs(s,e),r.add(s.name)),n.push({type:"MODULE",moduleId:s.moduleId});break;default:return(l=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(s)}return n}function Bs(t,e){let o={fileType:"momentic/module",moduleId:t.moduleId,name:t.name,steps:ar(t.steps,e,{},new Set),schemaVersion:e};return Bt(o)}function jt(t){return Bt({fileType:"momentic/environment",...t})}var lr={name:"Migrate to ai step v2",fromVersion:"1.0.4",toVersion:"1.0.5",recursiveKeys:new Set(["results","commands"]),stopOnFailure:!0,execute:async t=>(t=t.filter(e=>!(e.status!==void 0&&e.type==="AI_ACTION")),t=t.map(e=>(e.status===void 0||e.type==="PRESET_ACTION"&&(e.results=e.commands??e.results??[]),e)),t)};var cr={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:async t=>t.map(e=>{if(e.type!=="AI_ACTION"||e.status!==void 0||!e.commands||!e.commands.length)return e;let o=e.commands,r=o[o.length-1];return r&&r.type!=="SUCCESS"&&o.push({type:"SUCCESS"}),e})};var dr={name:"Migrate AI assertions to preset actions",fromVersion:"1.0.0",toVersion:"1.0.1",recursiveKeys:new Set,execute:async t=>t.map(e=>{if(e.type!=="AI_ASSERTION")return e;let r={type:"PRESET_ACTION",command:{type:"AI_ASSERTION",assertion:e.text,useVision:!1,disableCache:!0}},n={...e,...r};return delete n.text,n}),stopOnFailure:!0};var pt=new Set(["CLICK","TYPE","SELECT_OPTION"]),mr={name:"Migrate element descriptor to live in a target object",fromVersion:"1.0.3",toVersion:"1.0.4",recursiveKeys:new Set,execute:async t=>t.map(e=>{let o=e.command,r=o?.type,n=o?.elementDescriptor;return(n!==void 0||pt.has(r))&&(o.target={elementDescriptor:n??""}),e.commands&&Array.isArray(e.commands)&&e.commands.forEach(i=>{let a=i?.elementDescriptor,l=i?.type;(a!==void 0||pt.has(l))&&(i.target={elementDescriptor:a??""})}),e.results&&Array.isArray(e.results)&&e.results.forEach(i=>{let a=i.command,l=a?.elementDescriptor,c=a?.type;(l!==void 0||pt.has(c))&&(a.target={elementDescriptor:l??""}),i.commands&&Array.isArray(i.commands)&&i.commands.forEach(u=>{let p=u?.elementDescriptor,m=u?.type;(p!==void 0||pt.has(m))&&(u.target={elementDescriptor:p??""})})}),e}),stopOnFailure:!0};var ur={name:"Migrate FAILURE status to FAILED",fromVersion:"1.0.1",toVersion:"1.0.2",recursiveKeys:new Set,execute:async t=>t.map(e=>{let o=e;return o.status==="FAILURE"&&(o.status="FAILED"),typeof o.commands=="object"&&Array.isArray(o.commands)&&o.commands.forEach(r=>{if(r&&typeof r=="object"){let n=r;n?.status==="FAILURE"&&(n.status="FAILED")}}),o}),stopOnFailure:!0};var pr={name:"Migrate preset step types to use the same",fromVersion:"1.0.2",toVersion:"1.0.3",recursiveKeys:new Set,execute:async t=>t.map(e=>{let o=e.command,r=o?.type;return r?.startsWith("PRESET_")&&(o.type=r.slice(7)),e.commands&&Array.isArray(e.commands)&&e.commands.forEach(s=>{let i=s.type;i?.startsWith("PRESET_")&&(s.type=i.slice(7))}),e.results&&Array.isArray(e.results)&&e.results.forEach(s=>{let i=s.command,a=i?.type;a?.startsWith("PRESET_")&&(i.type=a.slice(7)),s.commands&&Array.isArray(s.commands)&&s.commands.forEach(c=>{let d=c.type;d?.startsWith("PRESET_")&&(c.type=d.slice(7))})}),e}),stopOnFailure:!0};var we=[dr,ur,pr,mr,lr,cr];if(F!==we[we.length-1].toVersion)throw new Error("Please bump LATEST_VERSION in types package after adding a migration");we.forEach((t,e)=>{if(!ht.valid(t.toVersion)||!ht.valid(t.fromVersion))throw new Error(`Migration '${t.name}' has invalid version`);if(!ht.gt(t.toVersion,t.fromVersion))throw new Error(`Migration '${t.name}' has toVersion <= fromVersion`);if(e===0)return;if(we[e-1].toVersion!==t.fromVersion)throw new Error(`Migration '${t.name}' at index ${e} is not contiguous with previous migration`)});function js(t){return t.every(e=>e&&typeof e=="object"&&!Array.isArray(e))}var Vt=async({metadata:t,steps:e,logger:o})=>{let r=e,{schemaVersion:n,id:s}=t,i=we.findIndex(c=>ht.gt(c.toVersion,n));if(i===-1)return o.debug({id:s},"Step migrations up to date"),{steps:r,newVersion:n};let a=n;for(let c=i;c<we.length;c++){let d=we[c],u={id:s,migration:d.name,toVersion:d.toVersion};o.debug(u,"Starting migration");try{r=await hr(r,d),a=d.toVersion}catch(p){throw o.error({err:p,...u},"Migration failed"),new Error(`Step migration ${d.name} failed: ${p}`)}}let l=Gs(JSON.stringify(e,void 0,2),JSON.stringify(r,void 0,2),{n_surrounding:1});return o.debug({diffs:l,id:s},"Migration diffs"),{newVersion:a,steps:r}};async function hr(t,e){let o=await e.execute(t);for(let r of o)for(let n of Object.keys(r)){if(!e.recursiveKeys.has(n))continue;let s=r[n];!s||!Array.isArray(s)||js(s)&&(r[n]=await hr(s,e))}return o}import{parse as Wt}from"yaml";var gr=["modules","fixtures","environments"],Ws=new Set([...gr,"node_modules","dist","bin",".git","logs",".npm",".next","out",".yarn","__pycache__","build",".env",".venv","venv","env","wheels"]);function We(t,e=t){let o=t.split(Pe.sep).pop()??"";if(Ws.has(o))return gr.includes(o)||g.warn(`Skipping directory '${t}' because it is likely an artifact folder.`),[];let r=ft.readdirSync(t),n=[];return r.forEach(s=>{let i=Pe.join(t,s);if(ft.statSync(i).isDirectory())n=n.concat(We(i,t));else if(s.endsWith(".yaml")){let a=ft.readFileSync(i,"utf-8"),l=ft.statSync(i),c=Wt(a);try{let d=ee.parse(c);n.push({id:d.id,name:d.name,fullFilePath:i,testPath:Pe.relative(Pe.resolve(e),Pe.resolve(i)),localOnly:d.localOnly,fileName:s,lastModified:l.mtime,createdAt:l.ctime})}catch{g.warn(`Skipping file '${i}' because it does not parse as a valid Momentic test.`)}}}),n}function Kt(t,e){let o;try{o=fr(t,"utf8"),o=o.replace(/\r\n|\r/g,`
|
|
16
|
-
`)}catch(a){throw new Error(`Could not read test file ${t}: ${a}`)}let r=new Set,n=/moduleId: (.*)/g,s;for(;(s=n.exec(o))!==null;)r.add(s[1].trim());let i={};return r.size>0&&r.forEach(a=>{i[a]||(i[a]=gt(e,a).content)}),{test:o,modules:i}}function gt(t,e){let o=Vs(t);for(let r of o){let n=Pe.join(t,r),s=fr(n,"utf8");if(s.includes(e))return{path:n,content:s}}throw new Error(`Could not find module file for module ${e} in ${t}`)}async function yt(t,e){let{test:o,modules:r}=Kt(t,e),n=Wt(o);if(!n.steps||!Array.isArray(n.steps))throw new Error(`Test ${t} is missing steps`);if(!n.schemaVersion||!n.id)throw new Error(`Test ${t} is missing an ID or schema version`);let s;if(n.schemaVersion<F){g.warn(`Test ${t} has schema version ${n.schemaVersion}, which is lower than the version used by this SDK, ${F}. Your test will be migrated to the latest version before execution.`);let{steps:l}=await Vt({metadata:n,steps:n.steps,logger:g});s=fe.array().parse(l)}else s=fe.array().parse(n.steps);let i={};for(let[l,c]of Object.entries(r)){let d=Wt(c);if(!d.schemaVersion||!d.moduleId)throw new Error(`Module ${l} is missing an ID or schema version`);if(!d.steps||!Array.isArray(d.steps))throw new Error(`Module ${l} is missing steps`);if(d.schemaVersion<F){g.warn(`Module ${l} has schema version ${d.schemaVersion}, which is lower than the version used by this SDK, ${F}. Your module will be migrated to the latest version before execution.`);let{steps:u}=await Vt({metadata:{id:d.moduleId,schemaVersion:d.schemaVersion},steps:d.steps,logger:g});i[l]={...d,steps:he.array().parse(u)}}else i[l]=d}let a=s.map(l=>{if(l.type!=="MODULE")return l;let c=i[l.moduleId];if(!c)throw new Error(`Could not resolve module ${l.moduleId} required in test ${t}`);return{type:"RESOLVED_MODULE",moduleId:l.moduleId,name:c.name,steps:c.steps}});return lt.parse({...n,steps:a})}import{spawn as Ks}from"child_process";async function yr(t){return new Promise((e,o)=>{let r={stdio:"inherit",env:process.env,detached:!0},n=Ks("bash",["-c",t],r),s=!1;process.on("exit",()=>n.pid!==void 0&&!s&&process.kill(-n.pid)),n.on("close",i=>{if(s=!0,i===0){e();return}o(`command exited with code ${i}`)})})}import{distance as gi}from"fastest-levenshtein";import{homedir as yi}from"os";import{join as Si}from"path";import{chromium as wi,devices as Ur}from"playwright";import{addExtra as bi}from"playwright-extra";import vi from"puppeteer-extra-plugin-recaptcha";import Ei from"puppeteer-extra-plugin-stealth";var qt={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:'.vimiumReset,a.vimiumReset,a:hover.vimiumReset,a:link.vimiumReset,a:visited.vimiumReset,div.vimiumReset,span.vimiumReset,table.vimiumReset,td.vimiumReset,tr.vimiumReset{background:none;border:none;bottom:auto;box-shadow:none;color:#000;cursor:auto;display:inline;float:none;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-size:inherit;font-style:normal;font-variant:normal;font-weight:400;height:auto;left:auto;letter-spacing:0;line-height:100%;margin:0;max-height:none;max-width:none;min-height:0;min-width:0;opacity:1;padding:0;position:static;right:auto;text-align:left;text-decoration:none;text-indent:0;text-shadow:none;text-transform:none;top:auto;vertical-align:baseline;white-space:normal;width:auto;z-index:2140000000}tbody.vimiumReset,thead.vimiumReset{display:table-header-group}tbody.vimiumReset{display:table-row-group}div.internalVimiumHintMarker{background:linear-gradient(180deg,#fff785 0,#ffc542);border:1px solid #c38a22;border-radius:3px;box-shadow:0 3px 7px 0 rgba(0,0,0,.3);display:block;font-size:11px;left:-1px;overflow:hidden;padding:1px 3px 0;position:absolute;top:-1px;white-space:nowrap}div.internalVimiumHintMarker span{color:#302505;font-family:Helvetica,Arial,sans-serif;font-size:11px;font-weight:700;text-shadow:0 1px 0 hsla(0,0%,100%,.6)}div.internalVimiumHintMarker>.matchingCharacter{color:#d4ac3a}div>.vimiumActiveHintMarker span{color:#a07555!important}div.internalVimiumInputHint{background-color:rgba(255,247,133,.3);border:1px solid #c38a22;display:block;pointer-events:none;position:absolute}div.internalVimiumSelectedInputHint{background-color:hsla(0,100%,70%,.3);border:1px solid #933!important}div.internalVimiumSelectedInputHint span{color:#fff!important}div.vimiumHighlightedFrame{border:5px solid #ff0;box-sizing:border-box;margin:0;pointer-events:none}div.vimiumHighlightedFrame,iframe.vimiumHelpDialogFrame{height:100%;left:0;padding:0;position:fixed;top:0;width:100%}iframe.vimiumHelpDialogFrame{background-color:hsla(0,0%,4%,.6);border:none;display:block;z-index:2139999997}div#vimiumHelpDialogContainer{background-color:#fff;border:2px solid #b3b3b3;border-radius:6px;margin:50px auto;max-height:calc(100% - 100px);max-width:calc(100% - 100px);opacity:1;overflow-x:auto;overflow-y:auto;width:840px}div#vimiumHelpDialog{min-width:600px;padding:8px 12px}span#vimiumTitle,span#vimiumTitle *,span#vimiumTitle span{font-size:20px}#vimiumTitle{display:block;line-height:130%;white-space:nowrap}td.vimiumHelpDialogTopButtons{text-align:right;width:100%}#helpDialogOptionsPage,#helpDialogWikiPage{font-size:14px;padding-left:5px;padding-right:5px}div.vimiumColumn{float:left;font-size:11px;line-height:130%;width:50%}div.vimiumColumn tr{display:table-row}div.vimiumColumn td{display:table-cell;font-size:11px;line-height:130%}div.vimiumColumn table,div.vimiumColumn td,div.vimiumColumn tr{margin:0;padding:0}div.vimiumColumn table{table-layout:auto;width:100%}div.vimiumColumn td{padding:1px;vertical-align:top}div#vimiumHelpDialog div.vimiumColumn tr>td:first-of-type{font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-size:14px;text-align:right;white-space:nowrap}span.vimiumHelpDialogKey{background-color:#f3f3f3;border:1px solid;border-color:#ccc #ccc #bbb;border-radius:3px;box-shadow:inset 0 -1px 0 #bbb;color:#212121;font-family:monospace;font-size:11px;margin-left:2px;padding:1px 4px}div#vimiumHelpDialog div.vimiumColumn tr>td:nth-of-type(3){width:100%}div#vimiumHelpDialog div.vimiumDivider{background-color:#9a9a9a;display:block;height:1px;margin:10px auto;width:100%}div#vimiumHelpDialog td.vimiumHelpSectionTitle{font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-size:16px;font-weight:700;padding-top:3px}div#vimiumHelpDialog td.vimiumHelpDescription{font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-size:14px}div#vimiumHelpDialog span.vimiumCopyCommandNameName{cursor:pointer;font-size:12px;font-style:italic}div#vimiumHelpDialog tr.advanced{display:none}div#vimiumHelpDialog.showAdvanced tr.advanced{display:table-row}div#vimiumHelpDialog div.advanced td:nth-of-type(3){color:#555}div#vimiumHelpDialog a.closeButton{color:#555;cursor:pointer;font-family:courier new;font-size:24px;font-weight:700;padding-left:5px;position:relative;text-decoration:none;top:3px}div#vimiumHelpDialog a{text-decoration:underline}div#vimiumHelpDialog a.closeButton:hover{color:#000;-webkit-user-select:none}div#vimiumHelpDialogFooter{display:block;margin-bottom:37px;position:relative}table.helpDialogBottom{width:100%}td.helpDialogBottomRight{float:right;text-align:right;width:100%}td.helpDialogBottomLeft,td.helpDialogBottomRight{padding:0}div#vimiumHelpDialogFooter *{font-size:10px}a#toggleAdvancedCommands,span#help-dialog-tip{font-size:10px;position:relative;top:19px;white-space:nowrap}a#toggleAdvancedCommands,a:active.vimiumHelDialogLink,a:hover.vimiumHelDialogLink,a:link.vimiumHelDialogLink,a:visited.vimiumHelDialogLink{color:#2f508e;cursor:pointer;text-decoration:underline}div.vimiumHUD{background:#f1f1f1;border:1px solid #aaa;border-radius:4px;bottom:8px;box-shadow:0 2px 10px rgba(0,0,0,.8);display:block;left:8px;position:fixed;text-align:left;width:calc(100% - 20px);z-index:2139999999}iframe.vimiumHUDFrame{background-color:transparent;border:none;bottom:-14px;display:block;height:58px;margin:0 0 0 -40%;min-width:300px;opacity:0;overflow:hidden;padding:0;position:fixed;right:20px;width:20%;z-index:2139999998}div.vimiumHUD .vimiumHUDSearchArea{background-color:#f1f1f1;border-radius:4px 4px 0 0;display:block;padding:3px}div.vimiumHUD .vimiumHUDSearchAreaInner{border-radius:3px;box-sizing:border-box;color:#777;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-size:14px;height:30px;line-height:20px;margin-bottom:0;outline:none;padding:2px 4px;width:100%}div.vimiumHUD .hud-find{background:#fff;border:1px solid #ccc}div.vimiumHUD span#hud-find-input,div.vimiumHUD span#hud-match-count{color:#000;display:inline;outline:none;overflow-y:hidden;white-space:nowrap}div.vimiumHUD span#hud-find-input:before{content:"/"}div.vimiumHUD span#hud-match-count{color:#aaa;font-size:12px}div.vimiumHUD span#hud-find-input br{display:none}div.vimiumHUD span#hud-find-input *{display:inline;white-space:nowrap}body.vimiumFindMode ::selection{background:#ff9632}iframe.vomnibarFrame{background-color:transparent;border:none;display:block;font-family:sans-serif;height:calc(100% - 70px);left:50%;margin:0 0 0 -40%;min-width:400px;overflow:hidden;padding:0;position:fixed;top:70px;width:calc(80% + 20px);z-index:2139999998}div.vimiumFlash{background-color:transparent;box-shadow:0 0 4px 2px #4183c4;padding:1px;position:absolute;z-index:2140000000}iframe.vimiumUIComponentHidden{display:none}iframe.vimiumUIComponentVisible{color-scheme:light dark;display:block}iframe.vimiumUIComponentReactivated{border:5px solid #ff0}iframe.vimiumNonClickable{pointer-events:none}@media (prefers-color-scheme:dark){iframe.reverseDarkReaderFilter{-webkit-filter:invert(100%) hue-rotate(180deg)!important;filter:invert(100%) hue-rotate(180deg)!important}body.vimiumBody{background-color:#292a2d;color:#fff}body.vimiumBody a,body.vimiumBody a:visited{color:#8ab4f8}body.vimiumBody input,body.vimiumBody textarea{background-color:#1d1d1f;border-color:#1d1d1f;color:#e8eaed}body.vimiumBody div.example{color:#9aa0a6}body.vimiumBody div#footer,body.vimiumBody div#state,div#vimiumHelpDialogContainer{background-color:#202124;border-color:hsla(0,0%,100%,.1)}div#vimiumHelpDialog{background-color:#292a2d;color:#fff}div#vimiumHelpDialog td.vimiumHelpDescription{color:#c9cccf}div#vimiumHelpDialog td.vimiumHelpSectionTitle,span#vimiumTitle{color:#fff}#vimiumTitle>span:first-child{color:#8ab4f8!important}div#vimiumHelpDialog a{color:#8ab4f8}div#vimiumHelpDialog div.vimiumDivider{background-color:hsla(0,0%,100%,.1)}span.vimiumHelpDialogKey{background-color:#1d1d1f;border:1px solid #000;box-shadow:none;color:#fff}}'};var be=(t,e)=>{let{hostname:o,pathname:r}=new URL(t),{hostname:n,pathname:s}=new URL(e);return o!==n||r!==s},Yt=t=>{try{return new URL(t),!0}catch{return!1}},Xt=(t,e)=>{try{return new URL(t,e),!0}catch{return!1}};import{distance as Qt}from"fastest-levenshtein";var Sr=new Set(["about:blank","chrome-error://chromewebdata/"]),wr=3,De="data-momentic-id";var Ys=["focusable","keyshortcuts","controls"],Xs=["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"],Js=["notRendered","notVisible","ariaHiddenElement","ariaHiddenSubtree"],Qs=80,Zs={paragraph:"p",searchbox:"input",LineBreak:"br"},to=["paragraph","StaticText","ListMarker","RootWebArea","LineBreak"],vr={indentLevel:0,noID:!1,noChildren:!1,noProperties:!1,maxLevel:void 0,neighbors:void 0},Zt=class{id;role;name;content;properties;dataMomenticId;pathFromRoot;parent;children;backendNodeID;ignoredByCDP;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,this.ignoredByCDP=e.ignoredByCDP,e.properties&&e.properties.forEach(o=>{o.name==="keyshortcuts"?this.dataMomenticId=parseInt(o.value.value):this.properties[o.name]=o.value.value})}getLogForm(){return JSON.stringify({id:this.id,name:this.name??"",role:this.role??"",backendNodeId:this.backendNodeID})}isInteresting(){return Xs.includes(this.role)||this.properties.focusable||this.properties.settable||this.children.some(e=>e.role==="StaticText")?!0:!!this.name.trim()||!!this.content}serialize(e=vr){let{indentLevel:o,noChildren:r,noProperties:n,noID:s}=Object.assign({},vr,e),i=" ".repeat(o),a=Zs[this.role]||this.role,l=this.name,c={...this.properties};a==="heading"&&(c.level&&(a=`h${c.level}`,delete c.level),l==="heading"&&(l=""));let d=!to.includes(this.role);if(this.role==="StaticText")return`${i}${l}
|
|
17
|
-
`;let u=`${i}<${a}`;!s&&d&&(u+=` id="${this.id}"`),l&&(u+=` name=${JSON.stringify(l)}`),this.content&&(u+=` content=${JSON.stringify(this.content)}`),Object.keys(this.properties).length>0&&!n&&Object.entries(this.properties).forEach(([m,
|
|
18
|
-
`;else{let m="";for(let w of this.children)m+=w.serialize({...e,indentLevel:o+2});let
|
|
19
|
-
`)?u+=`>${
|
|
15
|
+
`)),console.log(e===0?`${s} `:" ",l)}e===0&&process.stdout.write("\x1B[39m")}setMinLevel(e){this.minLogLevel=e}log(...e){this.info(...e)}info(...e){1<this.minLogLevel||this.logWithLevel(1,...e)}debug(...e){0<this.minLogLevel||this.logWithLevel(0,...e)}warn(...e){2<this.minLogLevel||this.logWithLevel(2,...e)}error(...e){3<this.minLogLevel||this.logWithLevel(3,...e)}child(e){return new t(this.minLogLevel,{...this.logBindings,...e})}flush(){}bindings(){return this.logBindings}},g=new kt(1,{});import{z as ne}from"zod";var $s=ne.object({id:ne.string(),createdAt:ne.coerce.date(),createdBy:ne.string(),organizationId:ne.string(),name:ne.string(),schemaVersion:ne.string(),numSteps:ne.number()}),zc=ne.object({steps:ge.array()}).merge($s.omit({numSteps:!0}));var We=t=>{let e=t.trim().toLowerCase().replace(/[^a-z0-9]/g,"-");for(;e.includes("--");)e=e.replaceAll("--","-");return e.startsWith("-")&&(e=e.slice(1)),e.endsWith("-")&&(e=e.slice(0,e.length-1)),e};import*as S from"zod";import{z as x}from"zod";var Ft={WEBHOOK:"WEBHOOK",CRON:"CRON",MANUAL:"MANUAL",CLI:"CLI"},zt={PENDING:"PENDING",RUNNING:"RUNNING",PASSED:"PASSED",FAILED:"FAILED",CANCELLED:"CANCELLED"},Hs={PASSED:"PASSED",FAILED:"FAILED"},mt=x.string().pipe(x.coerce.date()).or(x.date()),Bs=x.object({id:x.string(),createdAt:mt,createdBy:x.string(),organizationId:x.string(),scheduledAt:mt.or(x.null()),startedAt:mt.or(x.null()),finishedAt:mt.or(x.null()),testId:x.string().or(x.null()),status:x.nativeEnum(zt),expectedStatus:x.nativeEnum(Hs).or(x.null()),runKey:x.string(),trigger:x.nativeEnum(Ft),attempts:x.number(),test:x.object({name:x.string(),id:x.string()}).or(x.null())}),Ut=Bs.merge(x.object({results:it.array(),test:x.object({name:x.string(),id:x.string(),baseUrl:x.string()}).or(x.null())}));var be=S.object({disableCache:S.boolean()}),ed=S.object({error:S.boolean(),reason:S.string(),message:S.string()}),td=le.merge(be),Ko=Ot,od=S.discriminatedUnion("vision",[le.merge(be).merge(S.object({vision:S.literal(!1)})),le.pick({goal:!0,url:!0}).merge(be).merge(S.object({screenshot:S.string(),vision:S.literal(!0)}))]),$t=xo,rd=le.pick({browserState:!0,goal:!0}).merge(be),nd=le.pick({goal:!0}).merge(be).merge(S.object({screenshot:S.string().describe("base64 encoded image"),hintActivatedScreenshot:S.string().describe("base64 encoded image")})),Ht=Wo,sd=le.pick({goal:!0,url:!0}).merge(be),qo=S.string().array(),id=le.pick({goal:!0,browserState:!0}).merge(be),Yo=Lo,ad=S.object({testPaths:S.string().array().describe("can be either hyphenated, lowercase test names or UUIDs"),env:S.string().optional(),all:S.boolean().optional()}),Xo=S.object({message:S.string(),queuedTests:S.object({name:S.string(),id:S.string()}).array()}),Jo=$o,Qo=S.string().array(),ld=S.union([S.object({paths:S.string().array().describe("run specific test paths (e.g. todo-test)")}),S.object({path:S.string().describe("deprecated; present for backcompat")}),S.object({all:S.boolean().describe("run all tests")})]),Zo=S.object({tests:S.record(S.string().describe("Test name"),S.string().describe("Test YAML")),modules:S.record(S.string().describe("Module name"),S.string().describe("Module YAML"))}),Gs=S.object({test:S.string().describe("test YAML"),modules:S.record(S.string().describe("moduleId"),S.string().describe("module YAML"))}),cd=Gs.array();var dd=S.object({testPath:S.string(),testId:S.string()}).partial().merge(S.object({trigger:S.nativeEnum(Ft)})),er=Ut,tr=Ut,md=S.object({startedAt:S.coerce.date(),finishedAt:S.coerce.date(),results:it.array(),status:S.nativeEnum(zt)}).partial(),ud=S.object({screenshot:S.string()}),or=S.object({key:S.string()}),rr=S.object({orgId:S.string()}),nr=S.array(V),pd=S.array(V);function ut(t){switch(t.type){case"AI_ACTION":return`AI action: ${t.text}`;case"PRESET_ACTION":return Ne(t.command);case"RESOLVED_MODULE":return`Module: ${t.moduleId}`}}import{stringify as Bt}from"yaml";import{z as P}from"zod";var xd=P.object({test:P.string().describe("YAML for the test, including metadata and steps"),modules:P.record(P.string(),P.string()).describe("Map of module name to YAML for the module")}),Id=te.merge(P.object({steps:ye.array(),fileType:P.literal("momentic/test")})),Gt=Be.omit({type:!0}).merge(P.object({schemaVersion:P.string(),fileType:P.literal("momentic/module")})),Ld=te.merge(P.object({steps:P.array(P.record(P.string(),P.unknown()))})),Od=P.object({moduleId:P.string().uuid(),name:P.string(),schemaVersion:P.string(),steps:P.array(P.record(P.string(),P.unknown()))});function sr(t,e=new Set){let o={id:t.id,name:t.name,baseUrl:t.baseUrl,schemaVersion:t.schemaVersion,advanced:t.advanced,retries:t.retries,localOnly:t.localOnly,envs:t.envs},r={},n=ir(t.steps,t.schemaVersion,r,e),s={fileType:"momentic/test",...o,steps:n};return{test:Bt(s),modules:r}}function ir(t,e,o,r){let n=[];for(let s of t)switch(s.type){case"PRESET_ACTION":n.push(s);break;case"AI_ACTION":let i=(s.commands??[]).map(l=>({...l,thoughts:void 0}));n.push({...s,commands:i});break;case"RESOLVED_MODULE":r.has(s.name)||(o[s.name]=js(s,e),r.add(s.name)),n.push({type:"MODULE",moduleId:s.moduleId});break;default:return(l=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(s)}return n}function js(t,e){let o={fileType:"momentic/module",moduleId:t.moduleId,name:t.name,steps:ir(t.steps,e,{},new Set),schemaVersion:e};return Bt(o)}function jt(t){return Bt({fileType:"momentic/environment",...t})}var ar={name:"Migrate to ai step v2",fromVersion:"1.0.4",toVersion:"1.0.5",recursiveKeys:new Set(["results","commands"]),stopOnFailure:!0,execute:async t=>(t=t.filter(e=>!(e.status!==void 0&&e.type==="AI_ACTION")),t=t.map(e=>(e.status===void 0||e.type==="PRESET_ACTION"&&(e.results=e.commands??e.results??[]),e)),t)};var lr={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:async t=>t.map(e=>{if(e.type!=="AI_ACTION"||e.status!==void 0||!e.commands||!e.commands.length)return e;let o=e.commands,r=o[o.length-1];return r&&r.type!=="SUCCESS"&&o.push({type:"SUCCESS"}),e})};var cr={name:"Migrate AI assertions to preset actions",fromVersion:"1.0.0",toVersion:"1.0.1",recursiveKeys:new Set,execute:async t=>t.map(e=>{if(e.type!=="AI_ASSERTION")return e;let r={type:"PRESET_ACTION",command:{type:"AI_ASSERTION",assertion:e.text,useVision:!1,disableCache:!0}},n={...e,...r};return delete n.text,n}),stopOnFailure:!0};var pt=new Set(["CLICK","TYPE","SELECT_OPTION"]),dr={name:"Migrate element descriptor to live in a target object",fromVersion:"1.0.3",toVersion:"1.0.4",recursiveKeys:new Set,execute:async t=>t.map(e=>{let o=e.command,r=o?.type,n=o?.elementDescriptor;return(n!==void 0||pt.has(r))&&(o.target={elementDescriptor:n??""}),e.commands&&Array.isArray(e.commands)&&e.commands.forEach(i=>{let a=i?.elementDescriptor,l=i?.type;(a!==void 0||pt.has(l))&&(i.target={elementDescriptor:a??""})}),e.results&&Array.isArray(e.results)&&e.results.forEach(i=>{let a=i.command,l=a?.elementDescriptor,c=a?.type;(l!==void 0||pt.has(c))&&(a.target={elementDescriptor:l??""}),i.commands&&Array.isArray(i.commands)&&i.commands.forEach(u=>{let h=u?.elementDescriptor,m=u?.type;(h!==void 0||pt.has(m))&&(u.target={elementDescriptor:h??""})})}),e}),stopOnFailure:!0};var mr={name:"Migrate FAILURE status to FAILED",fromVersion:"1.0.1",toVersion:"1.0.2",recursiveKeys:new Set,execute:async t=>t.map(e=>{let o=e;return o.status==="FAILURE"&&(o.status="FAILED"),typeof o.commands=="object"&&Array.isArray(o.commands)&&o.commands.forEach(r=>{if(r&&typeof r=="object"){let n=r;n?.status==="FAILURE"&&(n.status="FAILED")}}),o}),stopOnFailure:!0};var ur={name:"Migrate preset step types to use the same",fromVersion:"1.0.2",toVersion:"1.0.3",recursiveKeys:new Set,execute:async t=>t.map(e=>{let o=e.command,r=o?.type;return r?.startsWith("PRESET_")&&(o.type=r.slice(7)),e.commands&&Array.isArray(e.commands)&&e.commands.forEach(s=>{let i=s.type;i?.startsWith("PRESET_")&&(s.type=i.slice(7))}),e.results&&Array.isArray(e.results)&&e.results.forEach(s=>{let i=s.command,a=i?.type;a?.startsWith("PRESET_")&&(i.type=a.slice(7)),s.commands&&Array.isArray(s.commands)&&s.commands.forEach(c=>{let d=c.type;d?.startsWith("PRESET_")&&(c.type=d.slice(7))})}),e}),stopOnFailure:!0};var ve=[cr,mr,ur,dr,ar,lr];if(z!==ve[ve.length-1].toVersion)throw new Error("Please bump LATEST_VERSION in types package after adding a migration");ve.forEach((t,e)=>{if(!ht.valid(t.toVersion)||!ht.valid(t.fromVersion))throw new Error(`Migration '${t.name}' has invalid version`);if(!ht.gt(t.toVersion,t.fromVersion))throw new Error(`Migration '${t.name}' has toVersion <= fromVersion`);if(e===0)return;if(ve[e-1].toVersion!==t.fromVersion)throw new Error(`Migration '${t.name}' at index ${e} is not contiguous with previous migration`)});function Vs(t){return t.every(e=>e&&typeof e=="object"&&!Array.isArray(e))}var Wt=async({metadata:t,steps:e,logger:o})=>{let r=e,{schemaVersion:n,id:s}=t,i=ve.findIndex(c=>ht.gt(c.toVersion,n));if(i===-1)return o.debug({id:s},"Step migrations up to date"),{steps:r,newVersion:n};let a=n;for(let c=i;c<ve.length;c++){let d=ve[c],u={id:s,migration:d.name,toVersion:d.toVersion};o.debug(u,"Starting migration");try{r=await pr(r,d),a=d.toVersion}catch(h){throw o.error({err:h,...u},"Migration failed"),new Error(`Step migration ${d.name} failed: ${h}`)}}let l=Ws(JSON.stringify(e,void 0,2),JSON.stringify(r,void 0,2),{n_surrounding:1});return o.debug({diffs:l,id:s},"Migration diffs"),{newVersion:a,steps:r}};async function pr(t,e){let o=await e.execute(t);for(let r of o)for(let n of Object.keys(r)){if(!e.recursiveKeys.has(n))continue;let s=r[n];!s||!Array.isArray(s)||Vs(s)&&(r[n]=await pr(s,e))}return o}import{parse as Vt}from"yaml";var fr=["modules","fixtures","environments"],qs=new Set([...fr,"node_modules","dist","bin",".git","logs",".npm",".next","out",".yarn","__pycache__","build",".env",".venv","venv","env","wheels"]);function Ve(t,e=t){let o=t.split(De.sep).pop()??"";if(qs.has(o))return fr.includes(o)||g.warn(`Skipping directory '${t}' because it is likely an artifact folder.`),[];let r=ft.readdirSync(t),n=[];return r.forEach(s=>{let i=De.join(t,s);if(ft.statSync(i).isDirectory())n=n.concat(Ve(i,t));else if(s.endsWith(".yaml")){let a=ft.readFileSync(i,"utf-8"),l=ft.statSync(i),c=Vt(a);try{let d=te.parse(c);n.push({id:d.id,name:d.name,fullFilePath:i,testPath:De.relative(De.resolve(e),De.resolve(i)),localOnly:d.localOnly,fileName:s,lastModified:l.mtime,createdAt:l.ctime})}catch{g.warn(`Skipping file '${i}' because it does not parse as a valid Momentic test.`)}}}),n}function Kt(t,e){let o;try{o=hr(t,"utf8"),o=o.replace(/\r\n|\r/g,`
|
|
16
|
+
`)}catch(a){throw new Error(`Could not read test file ${t}: ${a}`)}let r=new Set,n=/moduleId: (.*)/g,s;for(;(s=n.exec(o))!==null;)r.add(s[1].trim());let i={};return r.size>0&&r.forEach(a=>{i[a]||(i[a]=gt(e,a).content)}),{test:o,modules:i}}function gt(t,e){let o=Ks(t);for(let r of o){let n=De.join(t,r),s=hr(n,"utf8");if(s.includes(e))return{path:n,content:s}}throw new Error(`Could not find module file for module ${e} in ${t}`)}async function yt(t,e){let{test:o,modules:r}=Kt(t,e),n=Vt(o);if(!n.steps||!Array.isArray(n.steps))throw new Error(`Test ${t} is missing steps`);if(!n.schemaVersion||!n.id)throw new Error(`Test ${t} is missing an ID or schema version`);let s;if(n.schemaVersion<z){g.warn(`Test ${t} has schema version ${n.schemaVersion}, which is lower than the version used by this SDK, ${z}. Your test will be migrated to the latest version before execution.`);let{steps:l}=await Wt({metadata:n,steps:n.steps,logger:g});s=ye.array().parse(l)}else s=ye.array().parse(n.steps);let i={};for(let[l,c]of Object.entries(r)){let d=Vt(c);if(!d.schemaVersion||!d.moduleId)throw new Error(`Module ${l} is missing an ID or schema version`);if(!d.steps||!Array.isArray(d.steps))throw new Error(`Module ${l} is missing steps`);if(d.schemaVersion<z){g.warn(`Module ${l} has schema version ${d.schemaVersion}, which is lower than the version used by this SDK, ${z}. Your module will be migrated to the latest version before execution.`);let{steps:u}=await Wt({metadata:{id:d.moduleId,schemaVersion:d.schemaVersion},steps:d.steps,logger:g});i[l]={...d,steps:ge.array().parse(u)}}else i[l]=d}let a=s.map(l=>{if(l.type!=="MODULE")return l;let c=i[l.moduleId];if(!c)throw new Error(`Could not resolve module ${l.moduleId} required in test ${t}`);return{type:"RESOLVED_MODULE",moduleId:l.moduleId,name:c.name,steps:c.steps}});return lt.parse({...n,steps:a})}import{spawn as Ys}from"child_process";async function gr(t){return new Promise((e,o)=>{let r={stdio:"inherit",env:process.env,detached:!0},n=Ys("bash",["-c",t],r),s=!1;process.on("exit",()=>n.pid!==void 0&&!s&&process.kill(-n.pid)),n.on("close",i=>{if(s=!0,i===0){e();return}o(`command exited with code ${i}`)})})}import{distance as Si}from"fastest-levenshtein";import{homedir as wi}from"os";import{join as bi}from"path";import{chromium as vi,devices as zr}from"playwright";import{addExtra as Ei}from"playwright-extra";import Ai from"puppeteer-extra-plugin-recaptcha";import Ci from"puppeteer-extra-plugin-stealth";var qt={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:'.vimiumReset,a.vimiumReset,a:hover.vimiumReset,a:link.vimiumReset,a:visited.vimiumReset,div.vimiumReset,span.vimiumReset,table.vimiumReset,td.vimiumReset,tr.vimiumReset{background:none;border:none;bottom:auto;box-shadow:none;color:#000;cursor:auto;display:inline;float:none;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-size:inherit;font-style:normal;font-variant:normal;font-weight:400;height:auto;left:auto;letter-spacing:0;line-height:100%;margin:0;max-height:none;max-width:none;min-height:0;min-width:0;opacity:1;padding:0;position:static;right:auto;text-align:left;text-decoration:none;text-indent:0;text-shadow:none;text-transform:none;top:auto;vertical-align:baseline;white-space:normal;width:auto;z-index:2140000000}tbody.vimiumReset,thead.vimiumReset{display:table-header-group}tbody.vimiumReset{display:table-row-group}div.internalVimiumHintMarker{background:linear-gradient(180deg,#fff785 0,#ffc542);border:1px solid #c38a22;border-radius:3px;box-shadow:0 3px 7px 0 rgba(0,0,0,.3);display:block;font-size:11px;left:-1px;overflow:hidden;padding:1px 3px 0;position:absolute;top:-1px;white-space:nowrap}div.internalVimiumHintMarker span{color:#302505;font-family:Helvetica,Arial,sans-serif;font-size:11px;font-weight:700;text-shadow:0 1px 0 hsla(0,0%,100%,.6)}div.internalVimiumHintMarker>.matchingCharacter{color:#d4ac3a}div>.vimiumActiveHintMarker span{color:#a07555!important}div.internalVimiumInputHint{background-color:rgba(255,247,133,.3);border:1px solid #c38a22;display:block;pointer-events:none;position:absolute}div.internalVimiumSelectedInputHint{background-color:hsla(0,100%,70%,.3);border:1px solid #933!important}div.internalVimiumSelectedInputHint span{color:#fff!important}div.vimiumHighlightedFrame{border:5px solid #ff0;box-sizing:border-box;margin:0;pointer-events:none}div.vimiumHighlightedFrame,iframe.vimiumHelpDialogFrame{height:100%;left:0;padding:0;position:fixed;top:0;width:100%}iframe.vimiumHelpDialogFrame{background-color:hsla(0,0%,4%,.6);border:none;display:block;z-index:2139999997}div#vimiumHelpDialogContainer{background-color:#fff;border:2px solid #b3b3b3;border-radius:6px;margin:50px auto;max-height:calc(100% - 100px);max-width:calc(100% - 100px);opacity:1;overflow-x:auto;overflow-y:auto;width:840px}div#vimiumHelpDialog{min-width:600px;padding:8px 12px}span#vimiumTitle,span#vimiumTitle *,span#vimiumTitle span{font-size:20px}#vimiumTitle{display:block;line-height:130%;white-space:nowrap}td.vimiumHelpDialogTopButtons{text-align:right;width:100%}#helpDialogOptionsPage,#helpDialogWikiPage{font-size:14px;padding-left:5px;padding-right:5px}div.vimiumColumn{float:left;font-size:11px;line-height:130%;width:50%}div.vimiumColumn tr{display:table-row}div.vimiumColumn td{display:table-cell;font-size:11px;line-height:130%}div.vimiumColumn table,div.vimiumColumn td,div.vimiumColumn tr{margin:0;padding:0}div.vimiumColumn table{table-layout:auto;width:100%}div.vimiumColumn td{padding:1px;vertical-align:top}div#vimiumHelpDialog div.vimiumColumn tr>td:first-of-type{font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-size:14px;text-align:right;white-space:nowrap}span.vimiumHelpDialogKey{background-color:#f3f3f3;border:1px solid;border-color:#ccc #ccc #bbb;border-radius:3px;box-shadow:inset 0 -1px 0 #bbb;color:#212121;font-family:monospace;font-size:11px;margin-left:2px;padding:1px 4px}div#vimiumHelpDialog div.vimiumColumn tr>td:nth-of-type(3){width:100%}div#vimiumHelpDialog div.vimiumDivider{background-color:#9a9a9a;display:block;height:1px;margin:10px auto;width:100%}div#vimiumHelpDialog td.vimiumHelpSectionTitle{font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-size:16px;font-weight:700;padding-top:3px}div#vimiumHelpDialog td.vimiumHelpDescription{font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-size:14px}div#vimiumHelpDialog span.vimiumCopyCommandNameName{cursor:pointer;font-size:12px;font-style:italic}div#vimiumHelpDialog tr.advanced{display:none}div#vimiumHelpDialog.showAdvanced tr.advanced{display:table-row}div#vimiumHelpDialog div.advanced td:nth-of-type(3){color:#555}div#vimiumHelpDialog a.closeButton{color:#555;cursor:pointer;font-family:courier new;font-size:24px;font-weight:700;padding-left:5px;position:relative;text-decoration:none;top:3px}div#vimiumHelpDialog a{text-decoration:underline}div#vimiumHelpDialog a.closeButton:hover{color:#000;-webkit-user-select:none}div#vimiumHelpDialogFooter{display:block;margin-bottom:37px;position:relative}table.helpDialogBottom{width:100%}td.helpDialogBottomRight{float:right;text-align:right;width:100%}td.helpDialogBottomLeft,td.helpDialogBottomRight{padding:0}div#vimiumHelpDialogFooter *{font-size:10px}a#toggleAdvancedCommands,span#help-dialog-tip{font-size:10px;position:relative;top:19px;white-space:nowrap}a#toggleAdvancedCommands,a:active.vimiumHelDialogLink,a:hover.vimiumHelDialogLink,a:link.vimiumHelDialogLink,a:visited.vimiumHelDialogLink{color:#2f508e;cursor:pointer;text-decoration:underline}div.vimiumHUD{background:#f1f1f1;border:1px solid #aaa;border-radius:4px;bottom:8px;box-shadow:0 2px 10px rgba(0,0,0,.8);display:block;left:8px;position:fixed;text-align:left;width:calc(100% - 20px);z-index:2139999999}iframe.vimiumHUDFrame{background-color:transparent;border:none;bottom:-14px;display:block;height:58px;margin:0 0 0 -40%;min-width:300px;opacity:0;overflow:hidden;padding:0;position:fixed;right:20px;width:20%;z-index:2139999998}div.vimiumHUD .vimiumHUDSearchArea{background-color:#f1f1f1;border-radius:4px 4px 0 0;display:block;padding:3px}div.vimiumHUD .vimiumHUDSearchAreaInner{border-radius:3px;box-sizing:border-box;color:#777;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-size:14px;height:30px;line-height:20px;margin-bottom:0;outline:none;padding:2px 4px;width:100%}div.vimiumHUD .hud-find{background:#fff;border:1px solid #ccc}div.vimiumHUD span#hud-find-input,div.vimiumHUD span#hud-match-count{color:#000;display:inline;outline:none;overflow-y:hidden;white-space:nowrap}div.vimiumHUD span#hud-find-input:before{content:"/"}div.vimiumHUD span#hud-match-count{color:#aaa;font-size:12px}div.vimiumHUD span#hud-find-input br{display:none}div.vimiumHUD span#hud-find-input *{display:inline;white-space:nowrap}body.vimiumFindMode ::selection{background:#ff9632}iframe.vomnibarFrame{background-color:transparent;border:none;display:block;font-family:sans-serif;height:calc(100% - 70px);left:50%;margin:0 0 0 -40%;min-width:400px;overflow:hidden;padding:0;position:fixed;top:70px;width:calc(80% + 20px);z-index:2139999998}div.vimiumFlash{background-color:transparent;box-shadow:0 0 4px 2px #4183c4;padding:1px;position:absolute;z-index:2140000000}iframe.vimiumUIComponentHidden{display:none}iframe.vimiumUIComponentVisible{color-scheme:light dark;display:block}iframe.vimiumUIComponentReactivated{border:5px solid #ff0}iframe.vimiumNonClickable{pointer-events:none}@media (prefers-color-scheme:dark){iframe.reverseDarkReaderFilter{-webkit-filter:invert(100%) hue-rotate(180deg)!important;filter:invert(100%) hue-rotate(180deg)!important}body.vimiumBody{background-color:#292a2d;color:#fff}body.vimiumBody a,body.vimiumBody a:visited{color:#8ab4f8}body.vimiumBody input,body.vimiumBody textarea{background-color:#1d1d1f;border-color:#1d1d1f;color:#e8eaed}body.vimiumBody div.example{color:#9aa0a6}body.vimiumBody div#footer,body.vimiumBody div#state,div#vimiumHelpDialogContainer{background-color:#202124;border-color:hsla(0,0%,100%,.1)}div#vimiumHelpDialog{background-color:#292a2d;color:#fff}div#vimiumHelpDialog td.vimiumHelpDescription{color:#c9cccf}div#vimiumHelpDialog td.vimiumHelpSectionTitle,span#vimiumTitle{color:#fff}#vimiumTitle>span:first-child{color:#8ab4f8!important}div#vimiumHelpDialog a{color:#8ab4f8}div#vimiumHelpDialog div.vimiumDivider{background-color:hsla(0,0%,100%,.1)}span.vimiumHelpDialogKey{background-color:#1d1d1f;border:1px solid #000;box-shadow:none;color:#fff}}'};var Ee=(t,e)=>{let{hostname:o,pathname:r}=new URL(t),{hostname:n,pathname:s}=new URL(e);return o!==n||r!==s},Yt=t=>{try{return new URL(t),!0}catch{return!1}},Xt=(t,e)=>{try{return new URL(t,e),!0}catch{return!1}};import{distance as Qt}from"fastest-levenshtein";var yr=new Set(["about:blank","chrome-error://chromewebdata/"]),Sr=3,_e="data-momentic-id";var Js=["focusable","keyshortcuts","controls"],Qs=["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"],Zs=["notRendered","notVisible","ariaHiddenElement","ariaHiddenSubtree"],ei=80,ti={paragraph:"p",searchbox:"input",LineBreak:"br"},to=["paragraph","StaticText","ListMarker","RootWebArea","LineBreak"],br={indentLevel:0,noID:!1,noChildren:!1,noProperties:!1,maxLevel:void 0,neighbors:void 0},Zt=class{id;role;name;content;properties;dataMomenticId;pathFromRoot;parent;children;backendNodeID;ignoredByCDP;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,this.ignoredByCDP=e.ignoredByCDP,e.properties&&e.properties.forEach(o=>{o.name==="keyshortcuts"?this.dataMomenticId=parseInt(o.value.value):this.properties[o.name]=o.value.value})}getLogForm(){return JSON.stringify({id:this.id,name:this.name??"",role:this.role??"",backendNodeId:this.backendNodeID})}isInteresting(){return Qs.includes(this.role)||this.properties.focusable||this.properties.settable||this.children.some(e=>e.role==="StaticText")?!0:!!this.name.trim()||!!this.content}serialize(e=br){let{indentLevel:o,noChildren:r,noProperties:n,noID:s}=Object.assign({},br,e),i=" ".repeat(o),a=ti[this.role]||this.role,l=this.name,c={...this.properties};a==="heading"&&(c.level&&(a=`h${c.level}`,delete c.level),l==="heading"&&(l=""));let d=!to.includes(this.role);if(this.role==="StaticText")return`${i}${l}
|
|
17
|
+
`;let u=`${i}<${a}`;!s&&d&&(u+=` id="${this.id}"`),l&&(u+=` name=${JSON.stringify(l)}`),this.content&&(u+=` content=${JSON.stringify(this.content)}`),Object.keys(this.properties).length>0&&!n&&Object.entries(this.properties).forEach(([m,f])=>{Js.includes(m)||(typeof f=="string"?u+=` ${m}="${f}"`:typeof f=="boolean"?f?u+=` ${m}`:u+=` ${m}={false}`:typeof f<"u"&&(u+=` ${m}={${JSON.stringify(f)}}`))});let h=e.maxLevel!==void 0&&o/2>=e.maxLevel;if(this.children.length===0||r||h)u+=` />
|
|
18
|
+
`;else{let m="";for(let w of this.children)m+=w.serialize({...e,indentLevel:o+2});let f=m.trim();f.length<=ei&&!f.includes(`
|
|
19
|
+
`)?u+=`>${f}</${a}>
|
|
20
20
|
`:u+=`>
|
|
21
21
|
${m}${i}</${a}>
|
|
22
|
-
`}if(e.neighbors!==void 0&&e.neighbors>0&&this.parent){let m=this.parent.children.findIndex(y=>y.id===this.id),
|
|
22
|
+
`}if(e.neighbors!==void 0&&e.neighbors>0&&this.parent){let m=this.parent.children.findIndex(y=>y.id===this.id),f=m>0?this.parent.children[m-1]?.serialize({...e,neighbors:0}):"",w=m<this.parent.children.length-1?this.parent.children[m+1]?.serialize({...e,neighbors:0}):"";return`${f||""}
|
|
23
23
|
${u}
|
|
24
|
-
${w||""}`}return u}},eo=class{constructor(e,o,r){this.root=e;this.a11yIdNodeMap=o;this.dataMomenticIdMap=r}serialize(){return this.root?this.root.serialize():""}};function ei(t){return t.name?.value?`"${t.name.value}"`:t.role?.value&&t.role.value!=="none"&&t.role.value!=="generic"?`"${t.role.value}"`:`"${t.nodeId}"`}function Er(t,e,o){if(!e&&t.parentId)throw new Error(`Got no parent for accessibility node ${t.nodeId}: ${JSON.stringify(t)}`);let r=new Zt({id:parseInt(t.nodeId),role:t.role?.value||"",name:t.name?.value||"",content:t.value?.value||"",properties:t.properties,children:[],pathFromRoot:(e?`${e.pathFromRoot} `:"")+ei(t),backendNodeID:t.backendDOMNodeId,ignoredByCDP:t.ignored}),n=t.childIds??[];for(let a of n){if(!a)continue;let l=o.get(parseInt(a));if(!l)continue;let c=Er(l,r,o);c.length&&(r.children=r.children.concat(c))}if(r.role==="StaticText"&&(r.children=[]),r.children.length===1&&r.children[0].role==="StaticText"){let a=r.name,l=r.children[0]?.name;(a===l||!l)&&(r.children=[])}let s=[];for(let a=r.children.length-1;a>=0;a--){let l=r.children[a];if(l.role!=="StaticText"){s.push(l);continue}if(a===0||r.children[a-1].role!=="StaticText"){s.push(l);continue}r.children[a-1].name+=` ${l.name}`}if(r.children=s.reverse(),r.role==="generic"&&r.children.length===1){let a=r.children[0];if(r.name&&!to.includes(a.role)&&r.name===a.name)return r.children}if(!r.isInteresting()&&t.parentId)return r.children;for(let a of r.children)a.parent=r;return[r]}function Ar(t,e,o,r,n=1){t.id=n,n+=1,e.set(t.id,t),t.dataMomenticId?o.set(t.dataMomenticId,t):to.includes(t.role)||r.debug({node:t.serialize({neighbors:1,maxLevel:1})},"Node has no data-momentic-id");for(let s of t.children)n=Ar(s,e,o,r,n);return n}function Cr(t,e){if(!t.root)throw new Error("a11y tree has null root");t.allNodes=t.allNodes.filter(i=>i.ignored?!i.ignoredReasons?.find(l=>Js.includes(l.name)):!0);let o=new Map;for(let i of t.allNodes)o.set(parseInt(i.nodeId),i);let r=Er(t.root,null,o);if(r.length>1)throw new Error(`Something went horribly wrong processing the a11y tree, we got: ${JSON.stringify(r)}`);if(r.length===0)throw new rt;let n=new Map,s=new Map;return Ar(r[0],n,s,e),new eo(r[0],n,s)}var Ke=(t,e)=>{e.id=t.id,e.content=t.content,e.name=t.name,e.role=t.role,e.numChildren=t.children.length,e.serializedForm=t.serialize({noID:!0,maxLevel:1,neighbors:1}),e.nodeOnlySerializedForm=t.serialize({noID:!0,noChildren:!0,noProperties:!0})},oo=(t,e)=>{let o=1;t.role===e.role&&o++;let r=["name","content"];for(let n of r){if(!t[n]?.trim())continue;let s=Qt(t[n],e[n])/Math.min(t[n].length,e[n].length);s===0?o+=2:s<=.1&&o++}if(e.numChildren!==void 0&&(t.children.length===e.numChildren&&e.numChildren>0?o++:(e.numChildren>0&&t.children.length===0||Math.abs(t.children.length-e.numChildren)>2)&&o--),e.nodeOnlySerializedForm){let n=t.serialize({noID:!0,noChildren:!0,noProperties:!0});Qt(n,e.nodeOnlySerializedForm)/Math.min(n.length,e.nodeOnlySerializedForm.length)<=.1&&o++}if(e.serializedForm){let n=t.serialize({noID:!0,maxLevel:1,neighbors:1}),s=Qt(n,e.serializedForm)/Math.min(n.length,e.serializedForm.length);s===0?o+=2:s<=.1&&o++}return o};var ve={r:147,g:196,b:125,a:.55},Tr={showInfo:!1,showRulers:!1,showStyles:!1,showAccessibilityInfo:!1,showExtensionLines:!1,contrastAlgorithm:"aa",contentColor:ve,paddingColor:ve,borderColor:ve,marginColor:ve,eventTargetColor:ve,shapeColor:ve,shapeMarginColor:ve};var H=(t=1e3)=>new Promise(e=>setTimeout(()=>e(),t));function Rr(){window.cursor||(window.cursor=document.createElement("img"),window.cursor.setAttribute("src","data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjMyIiB2aWV3Qm94PSIwIDAgMzIgMzIiIHdpZHRoPSIzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwIDcpIj48cGF0aCBkPSJtNi4xNDggMTguNDczIDEuODYzLTEuMDAzIDEuNjE1LS44MzktMi41NjgtNC44MTZoNC4zMzJsLTExLjM3OS0xMS40MDh2MTYuMDE1bDMuMzE2LTMuMjIxeiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Im02LjQzMSAxNyAxLjc2NS0uOTQxLTIuNzc1LTUuMjAyaDMuNjA0bC04LjAyNS04LjA0M3YxMS4xODhsMi41My0yLjQ0MnoiIGZpbGw9IiMwMDAiLz48L2c+PC9zdmc+"),window.cursor.setAttribute("id","selenium_cursor"),window.cursor.setAttribute("style","position: absolute; z-index: 99999999999; pointer-events: none; left:0; top:0"),window.cursor.style.filter="invert(0%) sepia(6%) saturate(24%) hue-rotate(315deg) brightness(89%) contrast(110%)",document.body.appendChild(cursor),document.onmousemove=function(t){t=t||window.event,document.getElementById("selenium_cursor").style.left=t.pageX+"px",document.getElementById("selenium_cursor").style.top=t.pageY+"px"})}function xr(){window.globalHintManager||(window.globalHintManager=new window.HintManager),window.globalHintManager.capture()}function Ir(){window.globalHintManager&&window.globalHintManager.reset()}function Lr(){let t=document.body.getElementsByTagName("*"),e=1;for(let o=0;o<t.length;o++){let r=e.toString();for(;[6].some(s=>r.includes(s.toString()));)e++,r=e.toString();let n=t[o];n?.setAttribute("data-momentic-id",`${e}`),n?.setAttribute("aria-keyshortcuts",`${e}`),e++}}var ti=new Set(["document","script","XMLHttpRequest","fetch","xhr"]),oi=new Set(["script","document"]),ri=["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"],ni=["api.stripe.com","supabase.co"],si=[];async function Mr(t){for(let e of si)await t.route(e,o=>o.abort())}function ro(t){return`${t.resourceType()} ${t.method()} ${t.url()}`}function Or(t){return t=t.replace(/^www\./,""),t}function Nr(t){return ni.some(e=>t.includes(e))}function Pr(t,e){if(!ti.has(t.resourceType()))return!1;let o=new URL(e),r=new URL(t.url());return ri.some(n=>r.hostname.includes(n))?!1:oi.has(t.resourceType())||t.method()!=="GET"?!0:Or(r.hostname).includes(Or(o.hostname))}var Dr=()=>{window.clickListener&&document.removeEventListener("click",window.clickListener,{capture:!0}),window.clickListener=async t=>{console.log("[Momentic] Window click listener fired",t.target),await window.captureClick(t.target)},document.addEventListener("click",window.clickListener,{capture:!0})};var _r=()=>{window.pressListener&&document.removeEventListener("keydown",window.pressListener,{capture:!0}),window.pressListener=async t=>{console.log("[Momentic] Window press listener fired"),await window.captureKeystroke({key:t.key})},document.addEventListener("keydown",window.pressListener,{capture:!0})};var wt=bi(wi);wt.use(Ei());wt.use(vi({provider:{id:"2captcha",token:process.env.TWO_CAPTCHA_KEY},visualFeedback:!0}));var O=class t{browser;context;page;a11yIdToNodeMap=new Map;dataMomenticIdToNodeMap=new Map;cdpClient;logger;localMode;activeFrame;transformer;mostRecentBrowserState;baseURL;constructor({browser:e,context:o,page:r,baseUrl:n,cdpClient:s,logger:i,localMode:a}){this.browser=e,this.context=o,this.page=r,this.baseURL=n,this.cdpClient=s,this.logger=i,this.localMode=!!a}static USER_AGENT=Ur["Desktop Chrome"].userAgent;static VIEWPORT={width:1920,height:1080};static async init({baseUrl:e,logger:o,browserArgs:r,contextArgs:n,takeScreenshots:s,onScreenshot:i,onClose:a,localMode:l,localAppUrl:c,extensionPath:d,timeout:u=8e3}){let p={headless:!0,handleSIGTERM:!1,chromiumSandbox:!1,...r??{}},m={viewport:t.VIEWPORT,deviceScaleFactor:process.platform==="darwin"?2:1,userAgent:Ur["Desktop Chrome"].userAgent,geolocation:{latitude:37.7749,longitude:-122.4194},locale:"en-US",timezoneId:"America/Los_Angeles",...n??{}},h=null,w,y;if(l){let ze=Si(yi(),"momentic","chromium");w=await wt.launchPersistentContext(ze,{...p,...m,ignoreDefaultArgs:["--enable-automation","--enable-strict-mixed-content-checking"],ignoreHTTPSErrors:!0,bypassCSP:!0,args:["--allow-insecure-localhost","--disable-site-isolation-for-policy","--disable-site-isolation-trials",`--unsafely-treat-insecure-origin-as-secure=${c}`,`--load-extension=${d}`,"--test-type=browser"],baseURL:e}),y=w.pages()[0],a&&y.on("close",()=>{a()})}else h=await wt.launch(p),w=await h.newContext({...m,baseURL:e}),y=await w.newPage();await Mr(w);let E=await w.newCDPSession(y),b=new t({browser:h,context:w,page:y,baseUrl:e,cdpClient:E,logger:o,localMode:l}),x=!1;(async()=>{try{await b.navigate({url:e,wrapPossibleNavigation:!1,initialNavigation:!0})}catch(ze){o.error({err:ze},"Failed to initialize chrome browser")}finally{x=!0}})();let C=async()=>{if(s)try{i?.({viewport:await b.viewport(),buffer:await b.screenshot()})}catch(ze){o.error({err:ze},"Failed to take screenshot")}};C();let G=setInterval(()=>{C()},250),et=Date.now();for(;!x&&Date.now()-et<u;)await H(250);return clearInterval(G),x||o.warn("Timeout elapsed waiting for browser to initialize - are you sure this page is accessible?"),b}async getUserPageOrFrame(){if(this.localMode&&this.activeFrame){let e=null,o=0;for(;!e&&o<5;)e=this.page.frame(this.activeFrame),await H(100),o++;if(!e)throw new T("InternalWebAgentError",`Failed to find frame ${this.activeFrame} on page ${this.page.url()}`);return e}return this.page}async initCDPSession(){let e=!1,o=2,r=async()=>{try{await this.cdpClient.send("Accessibility.enable"),await this.cdpClient.send("DOM.enable"),await this.cdpClient.send("Overlay.enable"),e=!0}catch(n){if(o>0)return this.logger.error({err:n},"Failed to initialize CDP session, re-creating CDP client"),this.cdpClient=await this.context.newCDPSession(this.page),await H(250),o--,r();throw n}};if(await Promise.race([r(),H(3e3)]),!e)throw new T("InternalWebAgentError","Timeout elapsed waiting for CDP session to initialize")}setLogger(e){this.logger=e}ping(){if(this.closed)throw new Error("Page has been closed");if(this.browser&&!this.browser.isConnected())throw new Error("Browser is not connected")}setActiveFrame(e){this.activeFrame=e}async reset(e){this.a11yIdToNodeMap.clear(),this.dataMomenticIdToNodeMap.clear();let o=this.context.pages();this.page=o[0];for(let r=1;r<o.length;r++)await o[r].close();if(e.clearCookies&&await this.context.clearCookies(),!this.page.isClosed()){if(e.clearStorage)try{await(await this.getUserPageOrFrame()).evaluate(async()=>{window.localStorage.clear(),window.sessionStorage.clear(),await indexedDB.databases().then(r=>{r.forEach(n=>{n.name&&indexedDB.deleteDatabase(n.name)})})})}catch(r){this.logger.warn({err:r},"Failed clearing site data, continuing...")}await this.navigate({url:e.url??this.baseURL,wrapPossibleNavigation:!1,initialNavigation:!0})}}async pageSetup(){try{this.localMode||await this.page.evaluate(Rr),await this.initCDPSession()}catch{}}async wait(e){await H(e)}async toggleHints(e){let o=await this.getUserPageOrFrame();e.state==="on"?(await o.addStyleTag({content:qt.css}),await o.addScriptTag({content:qt.js}),await o.evaluate(xr)):await o.evaluate(Ir)}async showHints(){await this.toggleHints({state:"on"});let e=async()=>{try{await this.toggleHints({state:"off"})}catch(o){this.logger.debug({err:o},"Failed to remove vision hints")}};setTimeout(()=>{e()},3e3)}async cleanup(){await this.page.close(),await this.context.close(),this.browser&&await this.browser.close()}get closed(){return this.page.isClosed()||!!this.browser&&!this.browser.isConnected()}async html(){return(await this.getUserPageOrFrame()).content()}async url(){return(await this.getUserPageOrFrame()).url()}async screenshotWithHints(e=100,o="device",r="/tmp/screenshots/test.jpg"){let n=r?.split("."),s=n?.slice(0,-1).join("."),i=n?.slice(-1)[0],a=Buffer.from("");await this.showHints();let l=await this.screenshot(e,o,r?`${s}-after-hint.${i}`:void 0);return{before:a,after:l}}async screenshot(e=100,o="device",r){let n={fullPage:!1,quality:e,scale:o,type:"jpeg",caret:"initial",path:r};return!this.localMode||!this.activeFrame?this.page.screenshot(n):this.page.locator(`iframe[name="${this.activeFrame}"]`).screenshot(n)}async viewport(){if(this.localMode&&this.activeFrame){let o=await this.page.locator(`iframe[name="${this.activeFrame}"]`).boundingBox();if(!o)throw new Error(`Failed to get bounding box for frame: ${this.activeFrame}`);return o}let e=this.page.viewportSize();if(!e)throw new Error("failed to get viewport");return e}async navigate({url:e,wrapPossibleNavigation:o=!0,initialNavigation:r=!1}){this.logger.debug(`Navigating to ${e}`);let n=Date.now(),s=async()=>{try{await(await this.getUserPageOrFrame()).goto(e,{waitUntil:"load",timeout:8e3}),this.logger.debug({url:e},`Got load event in ${Math.floor(Date.now()-n)}ms`)}catch{this.logger.warn({url:e},"Timeout elapsed waiting for page to fire load event, continuing anyways...")}finally{await this.initCDPSession()}};o?await this.wrapPossibleNavigation(s):await s();let i=await this.url();if(Sr.has(i)&&process.env.NODE_ENV==="production")throw new T("ActionFailureError",`${e} took too long to load \u{1F61E}. Please ensure the site and your internet are working.`,{},!0);if(r&&(await this.pageSetup(),await this.attachPageLoadListeners(),this.localMode))try{await this.exposeRecordingBindings()}catch(a){a instanceof Error&&a.message.includes("already registered")||this.logger.error({err:a},"Failed to expose recording bindings during navigation")}this.logger.info({url:e},"Navigation complete")}async type(e,o={}){let{clearContent:r=!0,pressKeysSequentially:n=!1}=o;r&&(process.platform==="darwin"?await this.page.keyboard.press("Meta+A"):await this.page.keyboard.press("Control+A"),await this.page.keyboard.press("Backspace")),n?await this.page.keyboard.type(e):await this.page.keyboard.insertText(e)}async clickByA11yID(e,o={}){let r=this.a11yIdToNodeMap.get(e);if(!r)throw new Error(`Could not find DOM node during click: ${e}`);let n=await this.clickUsingCDP(r,o);return await this.highlightNode(n),r.serialize({noChildren:!0,noProperties:!0,noID:!0})}async selectOptionByA11yID(e,o){let r=this.a11yIdToNodeMap.get(e);if(!r)throw new Error(`Could not find DOM node while selecting option: ${e}`);if(!r.backendNodeID)throw new Error(`Select target missing backend node id: ${r.getLogForm()}`);return await(await this.getLocatorFromBackendID(r.backendNodeID)).selectOption(o,{timeout:8e3}),await this.highlightNode(r),r.serialize({noChildren:!0,noProperties:!0,noID:!0})}async scrollIntoView(e){let o=await this.resolveCachedTargetToID(e),r=this.a11yIdToNodeMap.get(o);if(!r)throw new Error(`Could not find node in DOM with a11y id: ${o}`);if(!r.backendNodeID)throw new Error(`Focus target missing backend node id: ${r.getLogForm()}`);await(await this.getLocatorFromBackendID(r.backendNodeID)).scrollIntoViewIfNeeded({timeout:8e3})}async highlight(e){try{let o=await this.resolveCachedTargetToID(e),r=this.a11yIdToNodeMap.get(o);if(!r)throw new Error(`Could not find DOM node during highlight: ${o}`);if(!r.backendNodeID)throw new Error(`Highlight target missing backend node id: ${r.getLogForm()}`);await this.highlightNode(r)}catch(o){this.logger.warn({err:o,target:e},"Failed to highlight target")}}async highlightNode(e){try{await this.cdpClient.send("Overlay.highlightNode",{highlightConfig:Tr,backendNodeId:e.backendNodeID})}catch{this.logger.warn("Failed to add node highlight, a page navigation likely occurred. This is non-fatal for tests.")}let o=async()=>{try{await this.cdpClient.send("Overlay.hideHighlight",{backendNodeId:e.backendNodeID})}catch(r){this.logger.debug({err:r},"Failed to remove node highlight")}};setTimeout(()=>{o()},3e3)}async wrapPossibleNavigation(e,o=8e3,r=!0){let n=Date.now(),s=await this.url(),i=Date.now(),a=new Map,l=new Map,c=v=>{let C=ro(v.request());l.set(C,(l.get(C)??0)+1);let G=v.status();G>=500&&this.logger.warn({request:C,status:G},"Received 500 level response")},d=v=>{if(!Pr(v,s))return;let C=ro(v);a.set(C,(a.get(C)??0)+1),i=Date.now()};this.page.on("response",c),this.page.on("request",d);let u=[];r&&(u=this.context.pages().map(v=>v.url()));let p=!1,m=e().catch(v=>(p=!0,v instanceof Error?v:new Error(`${v}`)));await H(250);let h=async v=>{let C=await v;if(C instanceof Error)throw C;return C},w=new Set,y=!1,b=await(async()=>{for(;!p&&!(!y&&Date.now()-n>o);){if(await H(250),y=!1,w=new Set,Date.now()-i<=1250)continue;let v=!1;for(let C of a.keys())a.get(C)!==l.get(C)&&(Nr(C)&&(y=!0),v=!0,w.add(C));if(!v)return this.logger.debug({url:await this.url(),requests:JSON.stringify(Array.from(a.entries()))},`Network idle in ${Math.floor(Date.now()-n)}ms`),!0}return!p&&w.size>0&&this.logger.warn({url:await this.url(),unfinishedRequests:JSON.stringify(Array.from(w.entries()))},"Timeout elapsed waiting for network idle, continuing anyways..."),!1})();if(this.page.off("response",c),this.page.off("request",d),!b)return h(m);let x=await this.url();if(!p&&be(x,s)){this.logger.debug({startUrl:s,newUrl:x},"Detected url change in wrapPossibleNavigation, waiting for load state");let v=Math.max(o-(Date.now()-n),0);if(v>0)try{await(await this.getUserPageOrFrame()).waitForLoadState("load",{timeout:v})}catch{this.logger.warn({url:await this.url()},"Timeout elapsed waiting for load state, continuing anyways...")}}if(r){let v=this.context.pages().map(C=>C.url());if(v.length>u.length)for(let C of v)C!==x&&await this.switchToPage(C)}return h(m)}async resolveCachedTargetToID(e){if(!ot(e)){let a=this.a11yIdToNodeMap.get(e.id);if(!a)throw new Error(`Resolving target failed, fresh value did not exist in node map: ${e.id}`);return Ke(a,e),e.id}let o=(await this.getA11yTree()).serialize();this.logger.debug({tree:o},"Refreshed a11y tree before resolving target");let r=this.a11yIdToNodeMap.get(e.id);if(r){let a=oo(r,e);if(a>=5)return this.logger.debug({target:e,proposedNode:r.getLogForm(),comparisonScore:a},"Resolved cached a11y target to node with exact same id"),Ke(r,e),e.id}let n=1/0,s=1/0,i;for(let a of this.a11yIdToNodeMap.values()){let l=oo(a,e);if(l>=5)return this.logger.debug({newNode:a.getLogForm(),target:e,comparisonScore:l},"Resolved cached a11y target to new node with field comparison"),Ke(a,e),a.id;if(!e.serializedForm)continue;let c=a.serialize({noID:!0,maxLevel:1,neighbors:1});if(Math.abs(c.length-e.serializedForm.length)>15)continue;let d=gi(e.serializedForm,c),u=d/Math.min(e.serializedForm.length,c.length);d<n&&u<.2&&(n=d,s=u,i=a)}if(i&&n<15)return this.logger.debug({newNode:i.getLogForm(),target:e,distance:n,ratio:s},"Resolved cached a11y target to new node with pure levenshtein distance"),Ke(i,e),i.id;throw new Error(`Could not find any relevant node given cached target: ${JSON.stringify({target:e,closestLevenshteinDistance:n,closestNode:i})}`)}async click(e,o={}){let r=await this.resolveCachedTargetToID(e);return await this.wrapPossibleNavigation(()=>this.clickByA11yID(r,o))}async hover(e){let o=await this.resolveCachedTargetToID(e),r=this.a11yIdToNodeMap.get(o);if(!r)throw new Error(`Could not find DOM node for hover: ${o}`);if(!r.backendNodeID)throw new Error(`Hover target missing backend node id: ${r.getLogForm()}`);return await(await this.getLocatorFromBackendID(r.backendNodeID)).hover({timeout:8e3}),await this.highlightNode(r),r.serialize({noChildren:!0,noProperties:!0,noID:!0})}async selectOption(e,o){let r=await this.resolveCachedTargetToID(e);return this.selectOptionByA11yID(r,o)}async press(e){await this.wrapPossibleNavigation(()=>this.page.keyboard.press(e))}async refresh(){if(this.localMode&&this.activeFrame){let o=(await this.getUserPageOrFrame()).url();await this.navigate({url:o,wrapPossibleNavigation:!0})}else try{await this.page.reload({timeout:3e3})}catch{this.logger.warn({url:await this.url()},"Timeout elapsed waiting for page on reload, continuing anyways...")}}async getA11yTree(e=!1){let o=null,r=async()=>{let n=0,s=await this.url();for(;!o;)try{let i=await this.getRawA11yTree(e);if(!i.root||i.allNodes.length===0)throw new Error("No a11y tree found on page");o=Cr(i,this.logger)}catch(i){if(this.logger.error({err:i,url:s},"Error fetching a11y tree"),n===0)await H(1e3),n++;else throw new Error(`Max retries exceeded fetching a11y tree: ${i}`)}o.root||this.logger.warn("A11y tree was pruned entirely"),this.a11yIdToNodeMap=o.a11yIdNodeMap,this.dataMomenticIdToNodeMap=o.dataMomenticIdMap};if(await Promise.race([r(),H(8e3)]),o)return o;throw new T("ActionFailureError",`Getting accessibility tree timed out after ${8e3}ms`)}getA11yIdFromDataMomenticId(e){return this.dataMomenticIdToNodeMap.get(e)?.id}async getRawA11yTree(e=!1){let o=await this.url(),r=Date.now(),n=()=>{r=Date.now()};this.cdpClient.addListener("Accessibility.nodesUpdated",n);let s=!1,i=()=>{this.logger.info({url:o},"Load event fired on page"),s=!0,r=Date.now()};this.cdpClient.addListener("Accessibility.loadComplete",i);let a=Date.now(),l=!e;for(;!e&&Date.now()-a<3e3;){if(await H(250),!s&&Date.now()-a<1e3){process.env.NODE_ENV!=="production"&&this.logger.debug({url:o},"A11y tree not loaded yet, waiting...");continue}if(Date.now()-r>=1250){l=!1;break}this.logger.debug({url:o},"A11y tree not stable yet, waiting...")}this.logger.debug({duration:Date.now()-a,eventReceived:s,timeoutTriggered:l,skipWait:e},"A11y wait phase completed"),await(await this.getUserPageOrFrame()).evaluate(Lr);let c;if(this.localMode&&this.activeFrame){let{result:{objectId:u}}=await this.cdpClient.send("Runtime.evaluate",{expression:`document.querySelector("#${this.activeFrame}")`}),p=await this.cdpClient.send("DOM.describeNode",{objectId:u});c=(await this.cdpClient.send("Accessibility.getRootAXNode",{frameId:p.node.frameId})).node.backendDOMNodeId}else{let{node:u}=await this.cdpClient.send("Accessibility.getRootAXNode");c=u.backendDOMNodeId}let{nodes:d}=await this.cdpClient.send("Accessibility.queryAXTree",{backendNodeId:c});return this.cdpClient.removeListener("Accessibility.loadComplete",i),this.cdpClient.removeListener("Accessibility.nodesUpdated",n),{root:d[0],allNodes:d}}async clickUsingVisualCoordinates(e){let o=await this.getElementLocation(e);if(!o)throw new Error(`Could not find element location with backend node id: ${e}`);this.logger.debug({location:o},"Executing mouse click"),await this.page.mouse.click(o.centerX,o.centerY)}async getIDAttributeUsingCDP(e){await this.cdpClient.send("DOM.getDocument",{depth:0});let o=await this.cdpClient.send("DOM.requestNode",{objectId:e}),n=(await this.cdpClient.send("DOM.getAttributes",{nodeId:o.nodeId})).attributes,s=n.findIndex(i=>i===De);if(s===-1||!n[s+1])throw new Error(`Could not find ${De} for object ${e}`);return n[s+1]}async getLocatorFromBackendID(e){let o=await this.cdpClient.send("DOM.resolveNode",{backendNodeId:e});if(!o||!o.object.objectId)throw new Error(`Could not resolve backend node ${e}`);let r;try{r=await this.getIDAttributeUsingCDP(o.object.objectId)}catch(n){throw this.logger.error({err:n,object:JSON.stringify(o.object)},"Failed to get ID attribute"),n}return(await this.getUserPageOrFrame()).locator(`[${De}="${r}"]`)}async clickUsingCDP(e,o={}){let r=0,n=e;for(;r<wr;){if(!n||n.role==="RootWebArea")throw new T("ActionFailureError",`Attempted to click node with no clickable surrounding elements: ${e.getLogForm()}`);if(n.role==="StaticText"){n=n.parent;continue}let s=n.backendNodeID;if(!s){this.logger.warn({node:n.getLogForm()},"Click candidate had no backend node ID"),n=n.parent;continue}try{let i=await this.getLocatorFromBackendID(s);return o.doubleClick?await i.dblclick({timeout:8e3}):await i.click({timeout:8e3,button:o.rightClick?"right":"left",force:o.force}),n.id!==e.id&&this.logger.info({oldNode:e.getLogForm(),newNode:n.getLogForm()},"Redirected click successfully to new element"),n}catch(i){this.logger.error({err:i,node:n.getLogForm()},"Failed click or click timed out"),r++,n=n.parent}}throw new Error(`Max click redirection attempts exhausted on original element: ${e.getLogForm()}`)}async getElementLocation(e){let o=await this.cdpClient.send("DOMSnapshot.captureSnapshot",{computedStyles:[],includeDOMRects:!0,includePaintOrder:!0}),r=await this.page.evaluate(()=>window.devicePixelRatio);process.platform==="darwin"&&r===1&&(r=2);let n=o.documents[0],s=n.layout,i=n.nodes,a=i.nodeName||[],l=i.backendNodeId||[],c=s.nodeIndex,d=s.bounds,u=-1;for(let b=0;b<a.length;b++)if(l[b]===e){u=c.indexOf(b);break}if(u===-1)throw new Error(`Could not find any backend node with ID ${e}`);let[p=0,m=0,h=0,w=0]=d[u];p/=r,m/=r,h/=r,w/=r;let y=p+h/2,E=m+w/2;return{centerX:y,centerY:E}}async scrollUp(e){await this.page.mouse.wheel(0,-(e??t.VIEWPORT.height))}async scrollDown(e){await this.page.mouse.wheel(0,e??t.VIEWPORT.height)}async goForward(){await this.wrapPossibleNavigation(async()=>this.localMode&&this.activeFrame?(await this.getUserPageOrFrame()).evaluate(e=>{let o=e().contentWindow;o?o.history.forward():console.error("Failed to get content window for frame")},()=>document.querySelector(`iframe[name="${this.activeFrame}"]`)):this.page.goForward({timeout:8e3}))}async goBack(){await this.wrapPossibleNavigation(async()=>this.localMode&&this.activeFrame?(await this.getUserPageOrFrame()).evaluate(e=>{let o=e().contentWindow;o?o.history.back():console.error("Failed to get content window for frame")},()=>document.querySelector(`iframe[name="${this.activeFrame}"]`)):this.page.goBack({timeout:8e3}))}async switchToPage(e){let o=this.context.pages();for(let r=0;r<o.length;r++){let n=o[r];if(n.url().includes(e)){this.logger.info(`Switching to tab ${r} with url ${n.url()}`),this.page=n,await this.pageSetup(),await this.attachPageLoadListeners();try{await n.waitForLoadState("load",{timeout:3e3})}catch{this.logger.warn({url:await this.url()},"Timeout elapsed waiting for load state during tab switch, continuing anyways...")}await this.initCDPSession();return}}throw new Error(`Could not find page with url containing ${e}`)}async attachPageLoadListeners(){if(this.localMode)return;let e=async o=>{o.parentFrame()||await this.pageSetup()};this.page.off("framenavigated",e),this.page.on("framenavigated",e)}async setCookie(e){let o=Do(e);await this.context.addCookies([o])}async setLocalStorage(e,o){await(await this.getUserPageOrFrame()).evaluate(([n,s])=>{n&&localStorage.setItem(n,s||"")},[e,o])}async solveCaptcha(){await this.getA11yTree();let e;for(let a of this.a11yIdToNodeMap.values())if(a.role==="image"&&a.name.toLowerCase().includes("captcha")){if(!a.backendNodeID)continue;e=await this.getLocatorFromBackendID(a.backendNodeID);break}if(!e){let a=await(await this.getUserPageOrFrame()).solveRecaptchas();if(!a.captchas||!a.captchas.length)throw new Error("No captchas found on the page");return}let o=await e.screenshot({type:"jpeg",animations:"allow",quality:100}),r=await fetch("https://api.2captcha.com/createTask",{method:"POST",body:JSON.stringify({clientKey:process.env.TWO_CAPTCHA_KEY,task:{type:"ImageToTextTask",body:o.toString("base64"),case:!0},languagePool:"en"})});if(!r.ok){let a=`Captcha solver API returned error response: ${r.statusText}`;throw this.logger.error({text:await r.text()},a),new Error(a)}let{taskId:n}=await r.json(),s=Date.now(),i="";for(;Date.now()-s<6e4;){await H(2500);let a=await fetch("https://api.2captcha.com/getTaskResult",{method:"POST",body:JSON.stringify({clientKey:process.env.TWO_CAPTCHA_KEY,taskId:n})});if(!a.ok){let c=`Captcha solution API returned error response: ${a.statusText}`;throw this.logger.error({text:await a.text()},c),new Error(c)}let l=await a.json();if(l.errorId){let c=`Captcha solution API returned error ID ${l.errorId}`;throw this.logger.error(c),new Error(c)}if(l.status==="ready"){i=l.solution.text;break}}if(!i)throw new Error("Captcha solution timed out");return i}getActiveFrame(){return this.activeFrame}async exposeRecordingBindings(){await this.context.exposeBinding("captureClick",async(e,o)=>{if(!this.transformer){this.logger.warn("Click captured without transformer");return}let r,n;try{r=await o.getAttribute("id"),n=await o.getAttribute(De)}catch(l){throw this.logger.error({err:l},"Failed to get element attributes for click processing"),l}if(this.logger.info({id:r,dataMomenticId:n},"Click captured on element"),!n){this.logger.error({url:await this.url(),element:await o.evaluate(l=>l.outerHTML)},`Could not find ${De} for recorded click`);return}let s=this.getA11yIdFromDataMomenticId(parseInt(n));if(!s){this.logger.warn({url:await this.url(),element:await o.evaluate(l=>l.outerHTML)},"Could not find a11y id for recorded click");return}let i={id:s};await this.resolveCachedTargetToID(i),(async()=>{try{let l=this.mostRecentBrowserState??(await this.getA11yTree(!0)).serialize();await this.transformer?.recordClick(i,l)}catch(l){this.logger.error({err:l},"Failed transforming captured click")}})()},{handle:!0}),await this.context.exposeBinding("captureKeystroke",async({},{key:e})=>{this.transformer&&(this.logger.info({key:e},"Captured keypress"),this.transformer.recordKeystroke(e))})}async startRecording(e,o){this.transformer=o;let r=await this.getA11yTree(!0);this.mostRecentBrowserState=r.serialize();let n=null,s=[async()=>{this.transformer=void 0}],i=async l=>{n&&clearTimeout(n),n=setInterval(()=>{if(!this.transformer||e.aborted)return;(async()=>{let d=0;for(;d<2;)try{let u=await this.getA11yTree(!0);this.mostRecentBrowserState=u.serialize();break}catch(u){this.logger.error({err:u},"Failed to get a11y tree in frame navigation listener"),d++}})()},250),s.push(async()=>{n&&(clearInterval(n),n=null)}),await l.evaluate(Dr),s.push(async()=>{await l.evaluate(()=>{document.removeEventListener("click",window.clickListener,{capture:!0})})}),await l.evaluate(_r),s.push(async()=>{await l.evaluate(()=>{document.removeEventListener("keydown",window.pressListener,{capture:!0})})})};await i(await this.getUserPageOrFrame());let a=async l=>{l.name().startsWith(_t)&&(e.aborted||!this.transformer||(this.logger.info("Re-adding window recording event listeners on navigation"),await i(l)))};this.page.on("framenavigated",a),s.push(async()=>{this.page.off("framenavigated",a)}),e.addEventListener("abort",async()=>{for(let l of s)try{await l()}catch(c){this.logger.warn({err:c},"Recording cleanup function failed, continuing...")}})}async getSelectOptions(e){let o=this.a11yIdToNodeMap.get(e);if(!o)throw new Error(`Could not find node with a11y id ${e}`);if(!o.backendNodeID)throw new Error(`Node with a11y id ${e} has no backend node ID`);return await(await this.getLocatorFromBackendID(o.backendNodeID)).evaluate(s=>Array.from(s.querySelectorAll("option")).map(a=>a.value))}};var ke=async({controller:t,step:e,logger:o,advanced:r,takeScreenshots:n,...s})=>{s.onStarted?.(),t.resetHistory();let i={...e,startedAt:new Date,userAgent:O.USER_AGENT,finishedAt:new Date,results:[],status:"SUCCESS"};try{let a=0;e.commands=e.commands||[];let l=e.commands&&e.commands.length>0;for(;;){if(a>12)throw new Error(`Exceeded max number of commands per step (${12})`);if(t.isClosed())throw new Error("Cancelling remaining steps in AI action because the controller is now closed");let c,d=new Date,u;if(n){let m=await t.browser.screenshot();u=await s.onSaveScreenshot(m)}if(l){if(c=e.commands[a],!c)throw new Error(`Saved command at index ${a} is undefined.`)}else{o.info(`Generating new sub-command ${a} within AI step`);let m;try{m=await t.promptToCommand(e.type,e.text,r.disableAICaching),c=m.command}catch(w){c={type:"FAILURE",thoughts:w instanceof Error?w.message:`${w}`}}finally{if(c.type==="FAILURE")if(a===0){i.finishedAt=new Date,i.status="FAILED",i.message=c.thoughts;break}else{let w="Stopping command generation prematurely since no progress can be made";o.warn({command:c},w),c={type:"SUCCESS",thoughts:w}}}(async()=>{let w=a,y=e,E=c;if("target"in E&&E.target){await new Promise(b=>setTimeout(()=>b(),250));try{let b=await Ai(t,m.context,E.target);E.target.elementDescriptor=b,y.commands[a]=E,s.onCommandExecuted?.({commandIndex:w,command:E})}catch(b){o.error({err:b,currentIndex:w,currentStep:y},"Failed to generate element description, continuing...")}}})()}s.onCommandGenerated?.({commandIndex:a,message:Nt[c.type]||`Unknown command (${c.type})`});let p={beforeScreenshot:u,beforeUrl:await t.browser.url(),startedAt:d,viewport:await t.browser.viewport(),finishedAt:new Date,status:"SUCCESS"};o.info(`Executing sub-command ${a} within AI step: ${Me(c)}`);try{let m=await t.executeCommand(c,r.disableAICaching,l);o.info(`AI sub-command ${a} completed successfully`),p.elementInteracted=m.elementInteracted;let h;if(n){let y=await t.browser.screenshot();h=await s.onSaveScreenshot(y)}p.afterScreenshot=h,p.afterUrl=await t.browser.url(),p.finishedAt=new Date;let w={status:"SUCCESS",startedAt:p.startedAt,finishedAt:p.finishedAt,type:"PRESET_ACTION",data:m.data,command:c,results:[p]};if(i.results.push(w),e.commands[a]=c,s.onCommandExecuted?.({commandIndex:a,output:{...w,message:c.thoughts??m.thoughts??"Successfully executed preset action."},command:c}),c.type==="SUCCESS"){i.finishedAt=new Date,i.status="SUCCESS",i.message=m.thoughts??"All commands completed.";break}if(m.succeedImmediately&&!l){i.finishedAt=new Date,i.status="SUCCESS",i.message=m.succeedImmediatelyReason,c={type:"SUCCESS",thoughts:m.succeedImmediatelyReason},e.commands.push(c),s.onCommandExecuted?.({commandIndex:a+1,output:{...w,message:i.message},command:c}),i.results.push({...w,command:c});break}}catch(m){if(l){l=!1,a=0,i.results=[];continue}let h=m instanceof Error?m.message:`${m}`;p.status="FAILED",p.message=h,p.finishedAt=new Date,p.afterUrl=await t.browser.url();let w;try{w=await t.browser.screenshot()}catch(y){o.warn({err:y},"Failed to take screenshot after error, skipping")}p.afterScreenshot=w,i.results.push({status:"FAILED",startedAt:p.startedAt,finishedAt:p.finishedAt,type:"PRESET_ACTION",command:c,results:[p],message:h}),i.status="FAILED",i.finishedAt=new Date,i.message=h;break}a++}}catch(a){i.message=a instanceof Error?a.message:`${a}`,i.finishedAt=new Date,i.status="FAILED"}return i.status==="SUCCESS"?s.onSuccess?.({message:i.message||"AI step succeeded.",startedAt:i.startedAt.getTime(),durationMs:i.finishedAt.getTime()-i.startedAt.getTime(),output:i}):s.onFailure?.({message:i.message||"AI step errored.",startedAt:i.startedAt.getTime(),durationMs:i.finishedAt.getTime()-i.startedAt.getTime(),output:i}),i};async function Ai(t,e,o){let r=o.a11yData?.id;if(!r)throw new Error("Attempted to get reverse mapping for command with no a11y id target");return t.getReverseMappedTarget(e,r,!0)}var Fe=async({controller:t,step:e,advanced:o,takeScreenshots:r,...n})=>{n.onStarted?.();let s=new Date,i=await t.browser.url(),a;if(r){let l=await t.browser.screenshot();a=await n.onSaveScreenshot(l)}try{let l=await t.executePresetStep(e.command,o.disableAICaching),c;if(r){let h=await t.browser.screenshot();c=await n.onSaveScreenshot(h)}let d=new Date,u={...e,startedAt:s,finishedAt:d,status:"SUCCESS",data:l.data,results:[]},p="Successfully executed preset action.";e.command.type==="AI_ASSERTION"&&(p=l.thoughts||"Assertion passed.");let m={beforeUrl:i,beforeScreenshot:a,afterUrl:await t.browser.url(),afterScreenshot:c,startedAt:s,finishedAt:d,viewport:await t.browser.viewport(),status:"SUCCESS"};return u.status="SUCCESS",u.results=[m],u.message=p,n.onSuccess?.({message:p,startedAt:s.getTime(),durationMs:d.getTime()-s.getTime(),command:e.command,output:u}),u}catch(l){n.logger.error({err:l},`Failed executing preset step ${e.command.type}`);let c=new Date,d=l instanceof Error?l.message:`${l}`,u=await t.browser.screenshot();if(e.command.type==="AI_ASSERTION"&&e.command.cancelOnFailure){let m={...e,startedAt:s,finishedAt:c,status:"CANCELLED",message:d,results:[{beforeUrl:i,beforeScreenshot:a,afterUrl:await t.browser.url(),afterScreenshot:u,startedAt:s,finishedAt:c,viewport:await t.browser.viewport(),status:"CANCELLED",message:d}]};return n.onCancelled?.({message:d,startedAt:s.getTime(),durationMs:c.getTime()-s.getTime(),output:m}),m}let p={...e,startedAt:s,finishedAt:c,status:"FAILED",message:d,results:[{beforeUrl:i,beforeScreenshot:a,afterUrl:await t.browser.url(),afterScreenshot:u,startedAt:s,finishedAt:c,viewport:await t.browser.viewport(),status:"FAILED",message:d}]};return n.onFailure?.({message:d,startedAt:s.getTime(),durationMs:c.getTime()-s.getTime(),output:p}),p}};var bt=async({controller:t,step:e,advanced:o,logger:r,takeScreenshots:n,...s})=>{s.onStarted?.();let i={type:"MODULE",moduleId:e.moduleId,startedAt:new Date,userAgent:O.USER_AGENT,results:[],finishedAt:new Date,status:"SUCCESS"};for(let a=0;a<e.steps.length;a++){let l=e.steps[a];if(t.isClosed())throw i.status="CANCELLED",new Error("Cancelling remaining steps in module because the controller is now closed");r.debug({i:a,moduleStep:l},"Starting module step"),r.info(`Starting module sub-step ${a+1}/${e.steps.length}: ${ut(l)}`);let c;switch(l.type){case"PRESET_ACTION":c=await Fe({controller:t,step:l,advanced:o,logger:r,takeScreenshots:n,onSaveScreenshot:s.onSaveScreenshot,onStarted(){s.onStepStarted?.({index:a})},onSuccess({message:u,startedAt:p,durationMs:m,output:h}){s.onStepSuccess?.({index:a,message:u,startedAt:p,durationMs:m,output:h})},onFailure({message:u,startedAt:p,durationMs:m,output:h}){s.onStepFailure?.({index:a,message:u,startedAt:p,durationMs:m,output:h})},onCancelled({message:u,startedAt:p,durationMs:m,output:h}){s.onStepCancelled?.({index:a,message:u,startedAt:p,durationMs:m,output:h})}});break;case"AI_ACTION":c=await ke({controller:t,step:l,advanced:o,logger:r,takeScreenshots:n,onSaveScreenshot:s.onSaveScreenshot,onStarted(){s.onStepStarted?.({index:a})},onSuccess({message:u,startedAt:p,durationMs:m,output:h}){s.onStepSuccess?.({index:a,message:u,startedAt:p,durationMs:m,output:h})},onFailure({message:u,startedAt:p,durationMs:m,output:h}){s.onStepFailure?.({index:a,message:u,startedAt:p,durationMs:m,output:h})},onCommandGenerated({commandIndex:u,message:p}){s.onCommandGenerated?.({index:a,commandIndex:u,message:p})},onCommandExecuted({commandIndex:u,command:p,output:m}){s.onCommandExecuted?.({index:a,commandIndex:u,command:p,output:m})}});break;default:return(u=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(l)}if(i.results.push(c),c.status==="FAILED"||c.status==="CANCELLED"){i.status=c.status,i.finishedAt=new Date,i.message=c.message;for(let d=a+1;d<e.steps.length;d++){let p={...e.steps[d],status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:O.USER_AGENT,results:[],message:"Cancelled due to previous failure."};i.results.push(p)}break}}return i.status==="SUCCESS"?s.onSuccess?.({message:"Executed module step.",startedAt:i.startedAt.getTime(),durationMs:i.finishedAt.getTime()-i.startedAt.getTime(),output:i}):i.status==="FAILED"?s.onFailure?.({message:"Failed to execute module step.",startedAt:i.startedAt.getTime(),durationMs:i.finishedAt.getTime()-i.startedAt.getTime(),output:i}):i.status==="CANCELLED"&&s.onCancelled?.({message:"Module step cancelled.",startedAt:i.startedAt.getTime(),durationMs:i.finishedAt.getTime()-i.startedAt.getTime(),output:i}),i};import{v4 as yp}from"uuid";var Sp=process.env.AWS_ACCESS_KEY_ID,wp=process.env.AWS_SECRET_ACCESS_KEY,bp=process.env.S3_REGION,vp=process.env.S3_BUCKET;var Hr=async({test:t,runId:e,controller:o,logger:r,takeScreenshots:n,onUpdateRun:s,onSaveScreenshot:i,onTestSuccess:a})=>{try{let l=await Ci({test:t,runId:e,controller:o,logger:r,takeScreenshots:n,onUpdateRun:s,onSaveScreenshot:i});if(l==="PASSED"||l==="CANCELLED")return await a?.(t),l;throw new T("InternalPlatformError",l.message||"An unknown platform error occurred")}catch(l){throw l instanceof T||(l=new T("InternalPlatformError",l instanceof Error?l.message:`${l}`,{cause:l})),l}finally{await o.browser.cleanup()}},Ci=async({test:t,runId:e,controller:o,logger:r,takeScreenshots:n,onUpdateRun:s,onSaveScreenshot:i})=>{let a=ae.parse(t.advanced),l=r.child({runId:e,testId:t.id});l.info("Starting test run"),await s({status:"RUNNING",startedAt:new Date});let c,d="PASSED",u=[];for(let p=0;p<t.steps.length;p++){let m=t.steps[p];l.info(`Starting step ${p+1}/${t.steps.length}: ${ut(m)}`);let h;switch(m.type){case"PRESET_ACTION":h=await Fe({controller:o,step:m,advanced:a,logger:l,takeScreenshots:n,onSaveScreenshot:i});break;case"AI_ACTION":h=await ke({controller:o,step:m,advanced:a,logger:l,takeScreenshots:n,onSaveScreenshot:i});break;case"RESOLVED_MODULE":h=await bt({controller:o,step:m,advanced:a,logger:l,takeScreenshots:n,onSaveScreenshot:i});break;default:return(y=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(m)}if(u.push(h),await s({results:u}),h.status==="SUCCESS"){l.info(`Step ${p+1}/${t.steps.length} succeeded`);continue}if(h.status!=="FAILED"&&h.status!=="CANCELLED")throw new T("InternalPlatformError",`Received unexpected non-terminal status from step: ${h.status}`);l.info({message:u[u.length-1]?.message},`Step ${p+1}/${t.steps.length} ended with status: ${h.status}`),c=h,d=h.status;for(let w=p+1;w<t.steps.length;w++){let y=t.steps[w];if(y.type==="RESOLVED_MODULE"){let E={type:"MODULE",moduleId:y.moduleId,startedAt:new Date,userAgent:O.USER_AGENT,results:y.steps.map(b=>({...b,status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:O.USER_AGENT,results:[]})),finishedAt:new Date,status:"CANCELLED"};u.push(E)}else{let E={...y,status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:O.USER_AGENT,results:[]};u.push(E)}}break}return await s({status:d,finishedAt:new Date,results:u}),d==="FAILED"?c:d};var Br={currentlyExecutingRequests:0},Ti=t=>async e=>{try{Br.currentlyExecutingRequests++,await Ri({...t,...e})}finally{Br.currentlyExecutingRequests--}},Ri=async({socket:t,steps:e,baseUrl:o,testMetadata:r,reInitialize:n,indicesFilter:s,metadata:i,logger:a,rootController:l,localApp:c})=>{let{testId:d,sessionId:u,orgId:p}=i,m={testId:d,orgId:p},h=a.child(m),w=a.child({package:"web-agent",...m}),y=!0;c==="iframe"&&(y=!1);let E=ae.parse(r.advanced??{}),b=k.getController(u);if(!b)throw new Error("No controller found");b.setOpen();let x=O.USER_AGENT;h.info({reInitialize:n,steps:e,indicesFilter:s},"Starting execution");let v={url:o,clearCookies:!0,clearStorage:!0};if(n&&(l?(l.browser.baseURL=o,l.browser.setActiveFrame(dt),await l.resetState(v),b.setLogger(w),b.browser.setLogger(w)):await b.resetState(v),b.setOpen()),h.info(`Session restarted for test ${d} at ${o}`),t.emit("session",{url:o,userAgent:x,viewport:await b.browser.viewport(),localApp:c}),s?.length){let C=Ii(e,s);h.info({step:C},"Starting individual step"),t.emit("started",{indices:s}),await Gr({indices:s,step:C,controller:b,advanced:E,socket:t,logger:h,takeScreenshots:y}),t.emit("finished");return}for(let C=0;C<e.length&&!(b.browser.closed||b.isClosed()||t.disconnected);C++){let G=e[C];if(h.info({index:C,step:G},"Starting step"),!await Gr({indices:[C],step:G,controller:b,advanced:E,socket:t,logger:h,takeScreenshots:y}))break}t.emit("finished")},xi=(t,e)=>{switch(t.type){case"RESOLVED_MODULE":return t.steps[e];case"AI_ACTION":let o=t.commands[e];return{type:"PRESET_ACTION",command:o};case"PRESET_ACTION":default:throw new Error(`Cannot get a child step from step type ${t.type}`)}},Ii=(t,e)=>{let o=t[e[0]];for(let r=1;r<e.length;r++)o=xi(o,e[r]);return o},so=async t=>t,Gr=async t=>{try{return await Li(t)}catch(e){throw e instanceof T&&e.emitToUser&&t.socket.emit("error",{message:e.message}),e}},Li=async({indices:t,step:e,controller:o,advanced:r,socket:n,logger:s,takeScreenshots:i})=>{let a=()=>{n.emit("started",{indices:t})},l=({message:u,startedAt:p,durationMs:m,command:h,output:w})=>{n.emit("success",{indices:t,message:u,startedAt:p,durationMs:m,command:h,output:Z.parse(w)})},c=({message:u,startedAt:p,durationMs:m,output:h})=>{n.emit("failure",{indices:t,message:u,startedAt:p,durationMs:m,output:Z.parse(h)})},d=({message:u,startedAt:p,durationMs:m,output:h})=>{n.emit("cancelled",{indices:t,message:u,startedAt:p,durationMs:m,output:Z.parse(h)})};switch(e.type){case"PRESET_ACTION":return(await Fe({controller:o,step:e,advanced:r,logger:s,takeScreenshots:i,onSaveScreenshot:so,onStarted:a,onSuccess:l,onFailure:c,onCancelled:d})).status==="SUCCESS";case"AI_ACTION":return(await ke({controller:o,step:e,advanced:r,logger:s,takeScreenshots:i,onSaveScreenshot:so,onStarted:a,onSuccess:l,onFailure:c,onCommandGenerated({commandIndex:m,message:h}){n.emit("commandGenerated",{indices:[...t,m],message:h})},onCommandExecuted({commandIndex:m,command:h,output:w}){n.emit("commandExecuted",{indices:[...t,m],command:h,output:Z.parse(w)})}})).status==="SUCCESS";case"RESOLVED_MODULE":return(await bt({controller:o,step:e,advanced:r,logger:s,takeScreenshots:i,onSaveScreenshot:so,onStarted:a,onSuccess:l,onFailure:c,onCancelled:d,onStepStarted({index:m}){n.emit("started",{indices:[...t,m]})},onStepSuccess({index:m,message:h,startedAt:w,durationMs:y,output:E}){n.emit("success",{indices:[...t,m],message:h,startedAt:w,durationMs:y,output:Z.parse(E)})},onStepFailure({index:m,message:h,startedAt:w,durationMs:y,output:E}){n.emit("failure",{indices:[...t,m],message:h,startedAt:w,durationMs:y,output:Z.parse(E)})},onStepCancelled({index:m,message:h,startedAt:w,durationMs:y,output:E}){n.emit("cancelled",{indices:[...t,m],message:h,startedAt:w,durationMs:y,output:Z.parse(E)})},onCommandGenerated({commandIndex:m,index:h,message:w}){n.emit("commandGenerated",{indices:[...t,h,m],message:w})},onCommandExecuted({index:m,commandIndex:h,command:w,output:y}){n.emit("commandExecuted",{indices:[...t,m,h],command:w,output:Z.parse(y)})}})).status==="SUCCESS";default:return(p=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(e)}};var jr={event:"execute",createHandler:Ti};var Oi=({metadata:t,logger:e,socket:o})=>{let{sessionId:r}=t;return async({description:n,useVision:s,testMetadata:i,isSelect:a},l)=>{let c=k.getController(r);if(!c)throw new Error("No controller found");let d=ae.parse(i.advanced??{}),u,p;try{if(u=await c.locateElement(n,s,d.disableAICaching),p={id:u.id},await c.browser.resolveCachedTargetToID(p),a)try{u.options=await c.browser.getSelectOptions(u.id)}catch(m){e.warn({err:m},"Failed getting select options"),o.emit("error",{message:`Failed getting select options: ${m}`})}}catch(m){e.error({err:m},"Error locating element"),o.emit("error",{message:m instanceof Error?m.message:`${m}`}),l({found:!1});return}e.info({locator:u,a11yData:p},"Located element result");try{await c.browser.scrollIntoView({id:u.id}),await c.browser.highlight({id:u.id})}catch(m){e.error({err:m,locator:u},"Error highlighting element"),l({found:!1});return}l({found:!0,locator:u,a11yData:p})}},Vr={event:"locate",createHandler:Oi};var Mi=({socket:t,metadata:e,logger:o})=>{let{sessionId:r}=e;return async({action:n,indices:s},i)=>{try{let a=k.getController(r);if(!a)throw new Error("No controller found");await a.toggleRecordMode({action:n,indices:s,onStepRecord:(l,c)=>{t.emit("stepRecorded",{step:l,indices:c})}}),i()}catch(a){o.error({err:a},"Error in record socket handler"),i(a instanceof Error?a.message:`${a}`)}}},Wr={event:"record",createHandler:Mi};var Ni=({socket:t,metadata:e,rootController:o,logger:r,localApp:n})=>async({baseUrl:s})=>{let{testId:i,sessionId:a}=e;r.info({testId:i,sessionId:a,baseUrl:s},"Reset event received");let l;if(o)await o.resetState({clearCookies:!0,clearStorage:!0,url:s}),l=o.browser.baseURL;else{let d=k.getController(a);if(!d){t.emit("error",{message:"No session to reset"});return}await d.browser.reset({clearCookies:!0,clearStorage:!0,url:s}),l=d.browser.baseURL}let c=O.USER_AGENT;r.info(`Session reset for test ${i} at ${l}`),t.emit("session",{url:l,userAgent:c,viewport:O.VIEWPORT,localApp:n})},Kr={event:"reset",createHandler:Ni};import{v4 as Gi}from"uuid";var Pi={type:"a11y",version:"1.0.0",useHistory:"diff",useGoalSplitter:!0},Ae=Pi;import{existsSync as ki}from"fs";import Di from"p-timeout";async function qr({code,fragment,timeoutMs=3e3}){let evalCode=code;if(fragment){let t=`__momentic${Math.round(Math.random()*1e4)}`;evalCode=`
|
|
24
|
+
${w||""}`}return u}},eo=class{constructor(e,o,r){this.root=e;this.a11yIdNodeMap=o;this.dataMomenticIdMap=r}serialize(){return this.root?this.root.serialize():""}};function oi(t){return t.name?.value?`"${t.name.value}"`:t.role?.value&&t.role.value!=="none"&&t.role.value!=="generic"?`"${t.role.value}"`:`"${t.nodeId}"`}function vr(t,e,o){if(!e&&t.parentId)throw new Error(`Got no parent for accessibility node ${t.nodeId}: ${JSON.stringify(t)}`);let r=new Zt({id:parseInt(t.nodeId),role:t.role?.value||"",name:t.name?.value||"",content:t.value?.value||"",properties:t.properties,children:[],pathFromRoot:(e?`${e.pathFromRoot} `:"")+oi(t),backendNodeID:t.backendDOMNodeId,ignoredByCDP:t.ignored}),n=t.childIds??[];for(let a of n){if(!a)continue;let l=o.get(parseInt(a));if(!l)continue;let c=vr(l,r,o);c.length&&(r.children=r.children.concat(c))}if(r.role==="StaticText"&&(r.children=[]),r.children.length===1&&r.children[0].role==="StaticText"){let a=r.name,l=r.children[0]?.name;(a===l||!l)&&(r.children=[])}let s=[];for(let a=r.children.length-1;a>=0;a--){let l=r.children[a];if(l.role!=="StaticText"){s.push(l);continue}if(a===0||r.children[a-1].role!=="StaticText"){s.push(l);continue}r.children[a-1].name+=` ${l.name}`}if(r.children=s.reverse(),r.role==="generic"&&r.children.length===1){let a=r.children[0];if(r.name&&!to.includes(a.role)&&r.name===a.name)return r.children}if(!r.isInteresting()&&t.parentId)return r.children;for(let a of r.children)a.parent=r;return[r]}function Er(t,e,o,r,n=1){t.id=n,n+=1,e.set(t.id,t),t.dataMomenticId?o.set(t.dataMomenticId,t):to.includes(t.role)||r.debug({node:t.serialize({neighbors:1,maxLevel:1})},"Node has no data-momentic-id");for(let s of t.children)n=Er(s,e,o,r,n);return n}function Ar(t,e){if(!t.root)throw new Error("a11y tree has null root");t.allNodes=t.allNodes.filter(i=>i.ignored?!i.ignoredReasons?.find(l=>Zs.includes(l.name)):!0);let o=new Map;for(let i of t.allNodes)o.set(parseInt(i.nodeId),i);let r=vr(t.root,null,o);if(r.length>1)throw new Error(`Something went horribly wrong processing the a11y tree, we got: ${JSON.stringify(r)}`);if(r.length===0)throw new rt;let n=new Map,s=new Map;return Er(r[0],n,s,e),new eo(r[0],n,s)}var Ke=(t,e)=>{e.id=t.id,e.content=t.content,e.name=t.name,e.role=t.role,e.numChildren=t.children.length,e.serializedForm=t.serialize({noID:!0,maxLevel:1,neighbors:1}),e.nodeOnlySerializedForm=t.serialize({noID:!0,noChildren:!0,noProperties:!0})},oo=(t,e)=>{let o=1;t.role===e.role&&o++;let r=["name","content"];for(let n of r){if(!t[n]?.trim())continue;let s=Qt(t[n],e[n])/Math.min(t[n].length,e[n].length);s===0?o+=2:s<=.1&&o++}if(e.numChildren!==void 0&&(t.children.length===e.numChildren&&e.numChildren>0?o++:(e.numChildren>0&&t.children.length===0||Math.abs(t.children.length-e.numChildren)>2)&&o--),e.nodeOnlySerializedForm){let n=t.serialize({noID:!0,noChildren:!0,noProperties:!0});Qt(n,e.nodeOnlySerializedForm)/Math.min(n.length,e.nodeOnlySerializedForm.length)<=.1&&o++}if(e.serializedForm){let n=t.serialize({noID:!0,maxLevel:1,neighbors:1}),s=Qt(n,e.serializedForm)/Math.min(n.length,e.serializedForm.length);s===0?o+=2:s<=.1&&o++}return o};var Ae={r:147,g:196,b:125,a:.55},Cr={showInfo:!1,showRulers:!1,showStyles:!1,showAccessibilityInfo:!1,showExtensionLines:!1,contrastAlgorithm:"aa",contentColor:Ae,paddingColor:Ae,borderColor:Ae,marginColor:Ae,eventTargetColor:Ae,shapeColor:Ae,shapeMarginColor:Ae};var B=(t=1e3)=>new Promise(e=>setTimeout(()=>e(),t));function Tr(){window.cursor||(window.cursor=document.createElement("img"),window.cursor.setAttribute("src","data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjMyIiB2aWV3Qm94PSIwIDAgMzIgMzIiIHdpZHRoPSIzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwIDcpIj48cGF0aCBkPSJtNi4xNDggMTguNDczIDEuODYzLTEuMDAzIDEuNjE1LS44MzktMi41NjgtNC44MTZoNC4zMzJsLTExLjM3OS0xMS40MDh2MTYuMDE1bDMuMzE2LTMuMjIxeiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Im02LjQzMSAxNyAxLjc2NS0uOTQxLTIuNzc1LTUuMjAyaDMuNjA0bC04LjAyNS04LjA0M3YxMS4xODhsMi41My0yLjQ0MnoiIGZpbGw9IiMwMDAiLz48L2c+PC9zdmc+"),window.cursor.setAttribute("id","momentic_cursor"),window.cursor.setAttribute("style","position: absolute; z-index: 99999999999; pointer-events: none; left:0; top:0; height:4%;"),window.cursor.style.filter="invert(0%) sepia(6%) saturate(24%) hue-rotate(315deg) brightness(89%) contrast(110%)",document.body.appendChild(cursor),document.onmousemove=function(t){t=t||window.event,document.getElementById("momentic_cursor").style.left=t.pageX+"px",document.getElementById("momentic_cursor").style.top=t.pageY+"px"})}function Rr(){window.globalHintManager||(window.globalHintManager=new window.HintManager),window.globalHintManager.capture()}function xr(){window.globalHintManager&&window.globalHintManager.reset()}function Ir(){let t=document.body.getElementsByTagName("*"),e=1;for(let o=0;o<t.length;o++){let r=e.toString();for(;[6].some(s=>r.includes(s.toString()));)e++,r=e.toString();let n=t[o];n?.setAttribute("data-momentic-id",`${e}`),n?.setAttribute("aria-keyshortcuts",`${e}`),e++}}var ri=new Set(["document","script","XMLHttpRequest","fetch","xhr"]),ni=new Set(["script","document"]),si=["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"],ii=["api.stripe.com","supabase.co"],ai=[];async function Or(t){for(let e of ai)await t.route(e,o=>o.abort())}function ro(t){return`${t.resourceType()} ${t.method()} ${t.url()}`}function Lr(t){return t=t.replace(/^www\./,""),t}function Mr(t){return ii.some(e=>t.includes(e))}function Nr(t,e){if(!ri.has(t.resourceType()))return!1;let o=new URL(e),r=new URL(t.url());return si.some(n=>r.hostname.includes(n))?!1:ni.has(t.resourceType())||t.method()!=="GET"?!0:Lr(r.hostname).includes(Lr(o.hostname))}var Pr=()=>{window.clickListener&&document.removeEventListener("click",window.clickListener,{capture:!0}),window.clickListener=async t=>{console.log("[Momentic] Window click listener fired",t.target),await window.captureClick(t.target)},document.addEventListener("click",window.clickListener,{capture:!0})};var Dr=()=>{window.pressListener&&document.removeEventListener("keydown",window.pressListener,{capture:!0}),window.pressListener=async t=>{console.log("[Momentic] Window press listener fired"),await window.captureKeystroke({key:t.key})},document.addEventListener("keydown",window.pressListener,{capture:!0})};var wt=Ei(vi);wt.use(Ci());wt.use(Ai({provider:{id:"2captcha",token:process.env.TWO_CAPTCHA_KEY},visualFeedback:!0}));var O=class t{browser;context;page;a11yIdToNodeMap=new Map;dataMomenticIdToNodeMap=new Map;cdpClient;logger;localMode;activeFrame;transformer;mostRecentBrowserState;baseURL;constructor({browser:e,context:o,page:r,baseUrl:n,logger:s,localMode:i,cdpClient:a}){this.browser=e,this.context=o,this.cdpClient=a,this.page=r,this.baseURL=n,this.logger=s,this.localMode=!!i}static USER_AGENT=zr["Desktop Chrome"].userAgent;static VIEWPORT={width:1920,height:1080};static async init({baseUrl:e,logger:o,browserArgs:r,contextArgs:n,takeScreenshots:s,onScreenshot:i,onClose:a,localMode:l,localAppUrl:c,extensionPath:d,timeout:u=8e3}){let h={headless:!0,handleSIGTERM:!1,chromiumSandbox:!1,...r??{}},m={viewport:t.VIEWPORT,deviceScaleFactor:process.platform==="darwin"?2:1,userAgent:zr["Desktop Chrome"].userAgent,geolocation:{latitude:37.7749,longitude:-122.4194},locale:"en-US",timezoneId:"America/Los_Angeles",...n??{}},f=null,w,y;if(l){let R=bi(wi(),"momentic","chromium");w=await wt.launchPersistentContext(R,{...h,...m,ignoreDefaultArgs:["--enable-automation","--enable-strict-mixed-content-checking"],ignoreHTTPSErrors:!0,bypassCSP:!0,args:["--allow-insecure-localhost","--disable-site-isolation-for-policy","--disable-site-isolation-trials",`--unsafely-treat-insecure-origin-as-secure=${c}`,`--load-extension=${d}`,"--test-type=browser"],baseURL:e}),y=w.pages()[0],a&&y.on("close",()=>{a()})}else f=await wt.launch(h),w=await f.newContext({...m,baseURL:e}),y=await w.newPage();await Or(w);let E=new t({browser:f,context:w,page:y,baseUrl:e,logger:o,localMode:l,cdpClient:await w.newCDPSession(y)}),b=!1;(async()=>{try{await E.navigate({url:e,wrapPossibleNavigation:!1,initialNavigation:!0})}catch(R){o.error({err:R},"Failed to initialize chrome browser")}finally{b=!0}})();let v=async()=>{if(s)try{i?.({viewport:await E.viewport(),buffer:await E.screenshot()})}catch(R){o.error({err:R},"Failed to take screenshot")}};v();let C=setInterval(()=>{v()},250),j=Date.now();for(;!b&&Date.now()-j<u;)await B(250);return clearInterval(C),b||o.warn("Timeout elapsed waiting for browser to initialize - are you sure this page is accessible?"),E}async getUserPageOrFrame(){if(this.localMode&&this.activeFrame){let e=null,o=0;for(;!e&&o<5;)e=this.page.frame(this.activeFrame),await B(100),o++;if(!e)throw new T("InternalWebAgentError",`Failed to find frame ${this.activeFrame} on page ${this.page.url()}`);return e}return this.page}async initCDPSession(){let e=!1,o=2,r=async()=>{try{this.cdpClient=await this.context.newCDPSession(this.page),await this.cdpClient.send("Accessibility.enable"),await this.cdpClient.send("DOM.enable"),await this.cdpClient.send("Overlay.enable"),e=!0}catch(n){if(o>0)return this.logger.error({err:n},"Failed to initialize CDP session, re-creating CDP client"),await B(250),o--,r();throw n}};if(await Promise.race([r(),B(3e3)]),!e)throw new T("InternalWebAgentError","Timeout elapsed waiting for CDP session to initialize")}setLogger(e){this.logger=e}ping(){if(this.closed)throw new Error("Page has been closed");if(this.browser&&!this.browser.isConnected())throw new Error("Browser is not connected")}setActiveFrame(e){this.activeFrame=e}async reset(e){this.a11yIdToNodeMap.clear(),this.dataMomenticIdToNodeMap.clear();let o=this.context.pages();this.page=o[0];for(let r=1;r<o.length;r++)await o[r].close();if(e.clearCookies&&await this.context.clearCookies(),!this.page.isClosed()){if(e.clearStorage)try{await(await this.getUserPageOrFrame()).evaluate(async()=>{window.localStorage.clear(),window.sessionStorage.clear(),await indexedDB.databases().then(r=>{r.forEach(n=>{n.name&&indexedDB.deleteDatabase(n.name)})})})}catch(r){this.logger.warn({err:r},"Failed clearing site data, continuing...")}await this.navigate({url:e.url??this.baseURL,wrapPossibleNavigation:!1,initialNavigation:!0})}}async pageSetup(){try{this.localMode||await this.page.evaluate(Tr)}catch{}}async wait(e){await B(e)}async toggleHints(e){let o=await this.getUserPageOrFrame();e.state==="on"?(await o.addStyleTag({content:qt.css}),await o.addScriptTag({content:qt.js}),await o.evaluate(Rr)):await o.evaluate(xr)}async showHints(){await this.toggleHints({state:"on"});let e=async()=>{try{await this.toggleHints({state:"off"})}catch(o){this.logger.debug({err:o},"Failed to remove vision hints")}};setTimeout(()=>{e()},3e3)}async cleanup(){await this.page.close(),await this.context.close(),this.browser&&await this.browser.close()}get closed(){return this.page.isClosed()||!!this.browser&&!this.browser.isConnected()}async html(){return(await this.getUserPageOrFrame()).content()}async url(){return(await this.getUserPageOrFrame()).url()}async screenshotWithHints(e=100,o="device",r="/tmp/screenshots/test.jpg"){let n=r?.split("."),s=n?.slice(0,-1).join("."),i=n?.slice(-1)[0],a=Buffer.from("");await this.showHints();let l=await this.screenshot(e,o,r?`${s}-after-hint.${i}`:void 0);return{before:a,after:l}}async screenshot(e=100,o="device",r){let n={fullPage:!1,quality:e,scale:o,type:"jpeg",caret:"initial",path:r};return!this.localMode||!this.activeFrame?this.page.screenshot(n):this.page.locator(`iframe[name="${this.activeFrame}"]`).screenshot(n)}async viewport(){if(this.localMode&&this.activeFrame){let o=await this.page.locator(`iframe[name="${this.activeFrame}"]`).boundingBox();if(!o)throw new Error(`Failed to get bounding box for frame: ${this.activeFrame}`);return o}let e=this.page.viewportSize();if(!e)throw new Error("failed to get viewport");return e}async navigate({url:e,wrapPossibleNavigation:o=!0,initialNavigation:r=!1}){this.logger.debug(`Navigating to ${e}`);let n=Date.now(),s=async()=>{try{await(await this.getUserPageOrFrame()).goto(e,{waitUntil:"load",timeout:8e3}),this.logger.debug({url:e},`Got load event in ${Math.floor(Date.now()-n)}ms`)}catch{this.logger.warn({url:e},"Timeout elapsed waiting for page to fire load event, continuing anyways...")}};o?await this.wrapPossibleNavigation(s):await s();let i=await this.url();if(yr.has(i)&&process.env.NODE_ENV==="production")throw new T("ActionFailureError",`${e} took too long to load \u{1F61E}. Please ensure the site and your internet are working.`,{},!0);if(r&&(await this.initCDPSession(),await this.pageSetup(),await this.attachPageLoadListeners(),this.localMode))try{await this.exposeRecordingBindings()}catch(a){a instanceof Error&&a.message.includes("already registered")||this.logger.error({err:a},"Failed to expose recording bindings during navigation")}this.logger.info({url:e},"Navigation complete")}async type(e,o={}){let{clearContent:r=!0,pressKeysSequentially:n=!1}=o;r&&(process.platform==="darwin"?await this.page.keyboard.press("Meta+A"):await this.page.keyboard.press("Control+A"),await this.page.keyboard.press("Backspace")),n?await this.page.keyboard.type(e):await this.page.keyboard.insertText(e)}async clickByA11yID(e,o={}){let r=this.a11yIdToNodeMap.get(e);if(!r)throw new Error(`Could not find DOM node during click: ${e}`);let n=await this.clickUsingCDP(r,o);return await this.highlightNode(n),r.serialize({noChildren:!0,noProperties:!0,noID:!0})}async selectOptionByA11yID(e,o){let r=this.a11yIdToNodeMap.get(e);if(!r)throw new Error(`Could not find DOM node while selecting option: ${e}`);if(!r.backendNodeID)throw new Error(`Select target missing backend node id: ${r.getLogForm()}`);return await(await this.getLocatorFromBackendID(r.backendNodeID)).selectOption(o,{timeout:8e3}),await this.highlightNode(r),r.serialize({noChildren:!0,noProperties:!0,noID:!0})}async scrollIntoView(e){let o=await this.resolveCachedTargetToID(e),r=this.a11yIdToNodeMap.get(o);if(!r)throw new Error(`Could not find node in DOM with a11y id: ${o}`);if(!r.backendNodeID)throw new Error(`Focus target missing backend node id: ${r.getLogForm()}`);await(await this.getLocatorFromBackendID(r.backendNodeID)).scrollIntoViewIfNeeded({timeout:8e3})}async highlight(e){try{let o=await this.resolveCachedTargetToID(e),r=this.a11yIdToNodeMap.get(o);if(!r)throw new Error(`Could not find DOM node during highlight: ${o}`);if(!r.backendNodeID)throw new Error(`Highlight target missing backend node id: ${r.getLogForm()}`);await this.highlightNode(r)}catch(o){this.logger.warn({err:o,target:e},"Failed to highlight target")}}async highlightNode(e){try{await this.cdpClient.send("Overlay.highlightNode",{highlightConfig:Cr,backendNodeId:e.backendNodeID})}catch{this.logger.warn("Failed to add node highlight, a page navigation likely occurred. This is non-fatal for tests.")}let o=async()=>{try{await this.cdpClient.send("Overlay.hideHighlight",{backendNodeId:e.backendNodeID})}catch(r){this.logger.debug({err:r},"Failed to remove node highlight")}};setTimeout(()=>{o()},3e3)}async wrapPossibleNavigation(e,o=8e3,r=!0){let n=Date.now(),s=await this.url(),i=Date.now(),a=new Map,l=new Map,c=v=>{let C=ro(v.request());l.set(C,(l.get(C)??0)+1);let j=v.status();j>=500&&this.logger.warn({request:C,status:j},"Received 500 level response")},d=v=>{if(!Nr(v,s))return;let C=ro(v);a.set(C,(a.get(C)??0)+1),i=Date.now()};this.page.on("response",c),this.page.on("request",d);let u=[];r&&(u=this.context.pages().map(v=>v.url()));let h=!1,m=e().catch(v=>(h=!0,v instanceof Error?v:new Error(`${v}`)));await B(250);let f=async v=>{let C=await v;if(C instanceof Error)throw C;return C},w=new Set,y=!1,b=await(async()=>{for(;!h&&!(!y&&Date.now()-n>o);){if(await B(250),y=!1,w=new Set,Date.now()-i<=1250)continue;let v=!1;for(let C of a.keys())a.get(C)!==l.get(C)&&(Mr(C)&&(y=!0),v=!0,w.add(C));if(!v)return this.logger.debug({url:await this.url(),requests:JSON.stringify(Array.from(a.entries()))},`Network idle in ${Math.floor(Date.now()-n)}ms`),!0}return!h&&w.size>0&&this.logger.warn({url:await this.url(),unfinishedRequests:JSON.stringify(Array.from(w.entries()))},"Timeout elapsed waiting for network idle, continuing anyways..."),!1})();if(this.page.off("response",c),this.page.off("request",d),!b)return f(m);let M=await this.url();if(!h&&Ee(M,s)){this.logger.debug({startUrl:s,newUrl:M},"Detected url change in wrapPossibleNavigation, waiting for load state");let v=Math.max(o-(Date.now()-n),0);if(v>0)try{await(await this.getUserPageOrFrame()).waitForLoadState("load",{timeout:v})}catch{this.logger.warn({url:await this.url()},"Timeout elapsed waiting for load state, continuing anyways...")}}if(r){let v=this.context.pages().map(C=>C.url());if(v.length>u.length)for(let C of v)C!==M&&await this.switchToPage(C)}return f(m)}async resolveCachedTargetToID(e){if(!tt(e)){let a=this.a11yIdToNodeMap.get(e.id);if(!a)throw new Error(`Resolving target failed, fresh value did not exist in node map: ${e.id}`);return Ke(a,e),e.id}let o=(await this.getA11yTree()).serialize();this.logger.debug({tree:o},"Refreshed a11y tree before resolving target");let r=this.a11yIdToNodeMap.get(e.id);if(r){let a=oo(r,e);if(a>=5)return this.logger.debug({target:e,proposedNode:r.getLogForm(),comparisonScore:a},"Resolved cached a11y target to node with exact same id"),Ke(r,e),e.id}let n=1/0,s=1/0,i;for(let a of this.a11yIdToNodeMap.values()){let l=oo(a,e);if(l>=5)return this.logger.debug({newNode:a.getLogForm(),target:e,comparisonScore:l},"Resolved cached a11y target to new node with field comparison"),Ke(a,e),a.id;if(!e.serializedForm)continue;let c=a.serialize({noID:!0,maxLevel:1,neighbors:1});if(Math.abs(c.length-e.serializedForm.length)>15)continue;let d=Si(e.serializedForm,c),u=d/Math.min(e.serializedForm.length,c.length);d<n&&u<.2&&(n=d,s=u,i=a)}if(i&&n<15)return this.logger.debug({newNode:i.getLogForm(),target:e,distance:n,ratio:s},"Resolved cached a11y target to new node with pure levenshtein distance"),Ke(i,e),i.id;throw new Error(`Could not find any relevant node given cached target: ${JSON.stringify({target:e,closestLevenshteinDistance:n,closestNode:i})}`)}async click(e,o={}){let r=await this.resolveCachedTargetToID(e);return await this.wrapPossibleNavigation(()=>this.clickByA11yID(r,o))}async hover(e){let o=await this.resolveCachedTargetToID(e),r=this.a11yIdToNodeMap.get(o);if(!r)throw new Error(`Could not find DOM node for hover: ${o}`);if(!r.backendNodeID)throw new Error(`Hover target missing backend node id: ${r.getLogForm()}`);return await(await this.getLocatorFromBackendID(r.backendNodeID)).hover({timeout:8e3}),await this.highlightNode(r),r.serialize({noChildren:!0,noProperties:!0,noID:!0})}async selectOption(e,o){let r=await this.resolveCachedTargetToID(e);return this.selectOptionByA11yID(r,o)}async press(e){await this.wrapPossibleNavigation(()=>this.page.keyboard.press(e))}async refresh(){if(this.localMode&&this.activeFrame){let o=(await this.getUserPageOrFrame()).url();await this.navigate({url:o,wrapPossibleNavigation:!0})}else try{await this.page.reload({timeout:3e3})}catch{this.logger.warn({url:await this.url()},"Timeout elapsed waiting for page on reload, continuing anyways...")}}async getA11yTree(e=!1){let o=null,r=async()=>{let n=0,s=await this.url();for(;!o&&n<3;)try{let i=await this.getRawA11yTree(e);if(!i.root||i.allNodes.length<=1)throw new Error("No a11y tree found on page");o=Ar(i,this.logger)}catch(i){if(this.logger.error({err:i,url:s},"Error fetching a11y tree"),n===0)await B(1e3),n++;else throw new Error(`Max retries exceeded fetching a11y tree: ${i}`)}if(!o||!o.root)throw new Error("Accessibility tree appears empty");this.a11yIdToNodeMap=o.a11yIdNodeMap,this.dataMomenticIdToNodeMap=o.dataMomenticIdMap};if(await Promise.race([r(),B(8e3)]),o)return o;throw new T("ActionFailureError",`Getting accessibility tree timed out after ${8e3}ms`)}getA11yIdFromDataMomenticId(e){return this.dataMomenticIdToNodeMap.get(e)?.id}async getRawA11yTree(e=!1){let o=await this.url(),r=Date.now(),n=()=>{r=Date.now()};this.cdpClient.addListener("Accessibility.nodesUpdated",n);let s=!1,i=()=>{this.logger.info({url:o},"Load event fired on page"),s=!0,r=Date.now()};this.cdpClient.addListener("Accessibility.loadComplete",i);let a=Date.now(),l=!e;for(;!e&&Date.now()-a<3e3;){if(await B(250),!s&&Date.now()-a<1e3){process.env.NODE_ENV!=="production"&&this.logger.debug({url:o},"A11y tree not loaded yet, waiting...");continue}if(Date.now()-r>=1250){l=!1;break}this.logger.debug({url:o},"A11y tree not stable yet, waiting...")}this.logger.debug({duration:Date.now()-a,eventReceived:s,timeoutTriggered:l,skipWait:e},"A11y wait phase completed"),await(await this.getUserPageOrFrame()).evaluate(Ir);let c;if(this.localMode&&this.activeFrame){let{result:{objectId:u}}=await this.cdpClient.send("Runtime.evaluate",{expression:`document.querySelector("#${this.activeFrame}")`}),h=await this.cdpClient.send("DOM.describeNode",{objectId:u});c=(await this.cdpClient.send("Accessibility.getRootAXNode",{frameId:h.node.frameId})).node.backendDOMNodeId}else{let{node:u}=await this.cdpClient.send("Accessibility.getRootAXNode");c=u.backendDOMNodeId}let{nodes:d}=await this.cdpClient.send("Accessibility.queryAXTree",{backendNodeId:c});return this.cdpClient.removeListener("Accessibility.loadComplete",i),this.cdpClient.removeListener("Accessibility.nodesUpdated",n),{root:d[0],allNodes:d}}async clickUsingVisualCoordinates(e){let o=await this.getElementLocation(e);if(!o)throw new Error(`Could not find element location with backend node id: ${e}`);this.logger.debug({location:o},"Executing mouse click"),await this.page.mouse.click(o.centerX,o.centerY)}async getIDAttributeUsingCDP(e){await this.cdpClient.send("DOM.getDocument",{depth:0});let o=await this.cdpClient.send("DOM.requestNode",{objectId:e}),n=(await this.cdpClient.send("DOM.getAttributes",{nodeId:o.nodeId})).attributes,s=n.findIndex(i=>i===_e);if(s===-1||!n[s+1])throw new Error(`Could not find ${_e} for object ${e}`);return n[s+1]}async getLocatorFromBackendID(e){let o=await this.cdpClient.send("DOM.resolveNode",{backendNodeId:e});if(!o||!o.object.objectId)throw new Error(`Could not resolve backend node ${e}`);let r;try{r=await this.getIDAttributeUsingCDP(o.object.objectId)}catch(n){throw this.logger.error({err:n,object:JSON.stringify(o.object)},"Failed to get ID attribute"),n}return(await this.getUserPageOrFrame()).locator(`[${_e}="${r}"]`)}async clickUsingCDP(e,o={}){let r=0,n=e;for(;r<Sr;){if(!n||n.role==="RootWebArea")throw new T("ActionFailureError",`Attempted to click node with no clickable surrounding elements: ${e.getLogForm()}`);if(n.role==="StaticText"){n=n.parent;continue}let s=n.backendNodeID;if(!s){this.logger.warn({node:n.getLogForm()},"Click candidate had no backend node ID"),n=n.parent;continue}try{let i=await this.getLocatorFromBackendID(s);return o.doubleClick?await i.dblclick({timeout:8e3}):await i.click({timeout:8e3,button:o.rightClick?"right":"left",force:o.force}),n.id!==e.id&&this.logger.info({oldNode:e.getLogForm(),newNode:n.getLogForm()},"Redirected click successfully to new element"),n}catch(i){this.logger.error({err:i,node:n.getLogForm()},"Failed click or click timed out"),r++,n=n.parent}}throw new Error(`Max click redirection attempts exhausted on original element: ${e.getLogForm()}`)}async getElementLocation(e){let o=await this.cdpClient.send("DOMSnapshot.captureSnapshot",{computedStyles:[],includeDOMRects:!0,includePaintOrder:!0}),r=await this.page.evaluate(()=>window.devicePixelRatio);process.platform==="darwin"&&r===1&&(r=2);let n=o.documents[0],s=n.layout,i=n.nodes,a=i.nodeName||[],l=i.backendNodeId||[],c=s.nodeIndex,d=s.bounds,u=-1;for(let b=0;b<a.length;b++)if(l[b]===e){u=c.indexOf(b);break}if(u===-1)throw new Error(`Could not find any backend node with ID ${e}`);let[h=0,m=0,f=0,w=0]=d[u];h/=r,m/=r,f/=r,w/=r;let y=h+f/2,E=m+w/2;return{centerX:y,centerY:E}}async scroll(e,o,r,n){let s=o==="left"?-1:1,i=n==="up"?-1:1;this.activeFrame?await(await this.getUserPageOrFrame()).evaluate(([l,c,d,u])=>window.scrollTo(window.scrollX+(l??window.innerWidth)*d,window.scrollY+(c??window.innerHeight)*u),[e,r,s,i]):await this.page.mouse.wheel((e??t.VIEWPORT.width)*s,(r??t.VIEWPORT.height)*i)}async scrollUp(e){await this.scroll(0,null,e??null,"up")}async scrollDown(e){await this.scroll(0,null,e??null,"down")}async scrollLeft(e){await this.scroll(e??null,"left",0,null)}async scrollRight(e){await this.scroll(e??null,"right",0,null)}async goForward(){await this.wrapPossibleNavigation(async()=>this.localMode&&this.activeFrame?(await this.getUserPageOrFrame()).evaluate(e=>{let o=e().contentWindow;o?o.history.forward():console.error("Failed to get content window for frame")},()=>document.querySelector(`iframe[name="${this.activeFrame}"]`)):this.page.goForward({timeout:8e3}))}async goBack(){await this.wrapPossibleNavigation(async()=>this.localMode&&this.activeFrame?(await this.getUserPageOrFrame()).evaluate(e=>{let o=e().contentWindow;o?o.history.back():console.error("Failed to get content window for frame")},()=>document.querySelector(`iframe[name="${this.activeFrame}"]`)):this.page.goBack({timeout:8e3}))}async switchToPage(e){let o=this.context.pages();for(let r=0;r<o.length;r++){let n=o[r];if(n.url().includes(e)){this.logger.info(`Switching to tab ${r} with url ${n.url()}`),this.page=n,await this.pageSetup(),await this.attachPageLoadListeners();try{await n.waitForLoadState("load",{timeout:3e3})}catch{this.logger.warn({url:await this.url()},"Timeout elapsed waiting for load state during tab switch, continuing anyways...")}await this.initCDPSession();return}}throw new Error(`Could not find page with url containing ${e}`)}async attachPageLoadListeners(){if(this.localMode)return;let e=async o=>{o.parentFrame()||await this.pageSetup()};this.page.off("framenavigated",e),this.page.on("framenavigated",e)}async setCookie(e){let o=Po(e);await this.context.addCookies([o])}async setLocalStorage(e,o){await(await this.getUserPageOrFrame()).evaluate(([n,s])=>{n&&localStorage.setItem(n,s||"")},[e,o])}async solveCaptcha(){await this.getA11yTree();let e;for(let a of this.a11yIdToNodeMap.values())if(a.role==="image"&&a.name.toLowerCase().includes("captcha")){if(!a.backendNodeID)continue;e=await this.getLocatorFromBackendID(a.backendNodeID);break}if(!e){let a=await(await this.getUserPageOrFrame()).solveRecaptchas();if(!a.captchas||!a.captchas.length)throw new Error("No captchas found on the page");return}let o=await e.screenshot({type:"jpeg",animations:"allow",quality:100}),r=await fetch("https://api.2captcha.com/createTask",{method:"POST",body:JSON.stringify({clientKey:process.env.TWO_CAPTCHA_KEY,task:{type:"ImageToTextTask",body:o.toString("base64"),case:!0},languagePool:"en"})});if(!r.ok){let a=`Captcha solver API returned error response: ${r.statusText}`;throw this.logger.error({text:await r.text()},a),new Error(a)}let{taskId:n}=await r.json(),s=Date.now(),i="";for(;Date.now()-s<6e4;){await B(2500);let a=await fetch("https://api.2captcha.com/getTaskResult",{method:"POST",body:JSON.stringify({clientKey:process.env.TWO_CAPTCHA_KEY,taskId:n})});if(!a.ok){let c=`Captcha solution API returned error response: ${a.statusText}`;throw this.logger.error({text:await a.text()},c),new Error(c)}let l=await a.json();if(l.errorId){let c=`Captcha solution API returned error ID ${l.errorId}`;throw this.logger.error(c),new Error(c)}if(l.status==="ready"){i=l.solution.text;break}}if(!i)throw new Error("Captcha solution timed out");return i}getActiveFrame(){return this.activeFrame}async exposeRecordingBindings(){await this.context.exposeBinding("captureClick",async(e,o)=>{if(!this.transformer){this.logger.warn("Click captured without transformer");return}let r,n;try{r=await o.getAttribute("id"),n=await o.getAttribute(_e)}catch(l){throw this.logger.error({err:l},"Failed to get element attributes for click processing"),l}if(this.logger.info({id:r,dataMomenticId:n},"Click captured on element"),!n){this.logger.error({url:await this.url(),element:await o.evaluate(l=>l.outerHTML)},`Could not find ${_e} for recorded click`);return}let s=this.getA11yIdFromDataMomenticId(parseInt(n));if(!s){this.logger.warn({url:await this.url(),element:await o.evaluate(l=>l.outerHTML)},"Could not find a11y id for recorded click");return}let i={id:s};await this.resolveCachedTargetToID(i),(async()=>{try{let l=this.mostRecentBrowserState??(await this.getA11yTree(!0)).serialize();await this.transformer?.recordClick(i,l)}catch(l){this.logger.error({err:l},"Failed transforming captured click")}})()},{handle:!0}),await this.context.exposeBinding("captureKeystroke",async({},{key:e})=>{this.transformer&&(this.logger.info({key:e},"Captured keypress"),this.transformer.recordKeystroke(e))})}async startRecording(e,o){this.transformer=o;let r=await this.getA11yTree(!0);this.mostRecentBrowserState=r.serialize();let n=null,s=[async()=>{this.transformer=void 0}],i=async l=>{n&&clearTimeout(n),n=setInterval(()=>{if(!this.transformer||e.aborted)return;(async()=>{let d=0;for(;d<2;)try{let u=await this.getA11yTree(!0);this.mostRecentBrowserState=u.serialize();break}catch(u){this.logger.error({err:u},"Failed to get a11y tree in frame navigation listener"),d++}})()},250),s.push(async()=>{n&&(clearInterval(n),n=null)}),await l.evaluate(Pr),s.push(async()=>{await l.evaluate(()=>{document.removeEventListener("click",window.clickListener,{capture:!0})})}),await l.evaluate(Dr),s.push(async()=>{await l.evaluate(()=>{document.removeEventListener("keydown",window.pressListener,{capture:!0})})})};await i(await this.getUserPageOrFrame());let a=async l=>{l.name().startsWith(_t)&&(e.aborted||!this.transformer||(this.logger.info("Re-adding window recording event listeners on navigation"),await i(l)))};this.page.on("framenavigated",a),s.push(async()=>{this.page.off("framenavigated",a)}),e.addEventListener("abort",async()=>{for(let l of s)try{await l()}catch(c){this.logger.warn({err:c},"Recording cleanup function failed, continuing...")}})}async getSelectOptions(e){let o=this.a11yIdToNodeMap.get(e);if(!o)throw new Error(`Could not find node with a11y id ${e}`);if(!o.backendNodeID)throw new Error(`Node with a11y id ${e} has no backend node ID`);return await(await this.getLocatorFromBackendID(o.backendNodeID)).evaluate(s=>Array.from(s.querySelectorAll("option")).map(a=>a.value))}};var Fe=async({controller:t,step:e,logger:o,advanced:r,takeScreenshots:n,...s})=>{s.onStarted?.(),t.resetHistory();let i={...e,startedAt:new Date,userAgent:O.USER_AGENT,finishedAt:new Date,results:[],status:"SUCCESS"};try{let a=0;e.commands=e.commands||[];let l=e.commands&&e.commands.length>0;for(;;){if(a>12)throw new Error(`Exceeded max number of commands per step (${12})`);if(t.isClosed())throw new Error("Cancelling remaining steps in AI action because the controller is now closed");let c,d=new Date,u;if(n){let m=await t.browser.screenshot();u=await s.onSaveScreenshot(m)}if(l){if(c=e.commands[a],!c)throw new Error(`Saved command at index ${a} is undefined.`)}else{o.info(`Generating new sub-command ${a} within AI step`);let m;try{m=await t.promptToCommand(e.type,e.text,r.disableAICaching),c=m.command}catch(w){c={type:"FAILURE",thoughts:w instanceof Error?w.message:`${w}`}}finally{if(c.type==="FAILURE")if(a===0){i.finishedAt=new Date,i.status="FAILED",i.message=c.thoughts;break}else{let w="Stopping command generation prematurely since no progress can be made";o.warn({command:c},w),c={type:"SUCCESS",thoughts:w}}}(async()=>{let w=a,y=e,E=c;if("target"in E&&E.target){await new Promise(b=>setTimeout(()=>b(),250));try{let b=await Ti(t,m.context,E.target);E.target.elementDescriptor=b,y.commands[a]=E,s.onCommandExecuted?.({commandIndex:w,command:E})}catch(b){o.error({err:b,currentIndex:w,currentStep:y},"Failed to generate element description, continuing...")}}})()}s.onCommandGenerated?.({commandIndex:a,message:Nt[c.type]||`Unknown command (${c.type})`});let h={beforeScreenshot:u,beforeUrl:await t.browser.url(),startedAt:d,viewport:await t.browser.viewport(),finishedAt:new Date,status:"SUCCESS"};o.info(`Executing sub-command ${a} within AI step: ${Ne(c)}`);try{let m=await t.executeCommand(c,r.disableAICaching,l);o.info(`AI sub-command ${a} completed successfully`),h.elementInteracted=m.elementInteracted;let f;if(n){let y=await t.browser.screenshot();f=await s.onSaveScreenshot(y)}h.afterScreenshot=f,h.afterUrl=await t.browser.url(),h.finishedAt=new Date;let w={status:"SUCCESS",startedAt:h.startedAt,finishedAt:h.finishedAt,type:"PRESET_ACTION",data:m.data,command:c,results:[h]};if(i.results.push(w),e.commands[a]=c,s.onCommandExecuted?.({commandIndex:a,output:{...w,message:c.thoughts??m.thoughts??"Successfully executed preset action."},command:c}),c.type==="SUCCESS"){i.finishedAt=new Date,i.status="SUCCESS",i.message=m.thoughts??"All commands completed.";break}if(m.succeedImmediately&&!l){i.finishedAt=new Date,i.status="SUCCESS",i.message=m.succeedImmediatelyReason,c={type:"SUCCESS",thoughts:m.succeedImmediatelyReason},e.commands.push(c),s.onCommandExecuted?.({commandIndex:a+1,output:{...w,message:i.message},command:c}),i.results.push({...w,command:c});break}}catch(m){if(l){l=!1,a=0,i.results=[];continue}let f=m instanceof Error?m.message:`${m}`;h.status="FAILED",h.message=f,h.finishedAt=new Date,h.afterUrl=await t.browser.url();let w;try{w=await t.browser.screenshot()}catch(y){o.warn({err:y},"Failed to take screenshot after error, skipping")}h.afterScreenshot=w,i.results.push({status:"FAILED",startedAt:h.startedAt,finishedAt:h.finishedAt,type:"PRESET_ACTION",command:c,results:[h],message:f}),i.status="FAILED",i.finishedAt=new Date,i.message=f;break}a++}}catch(a){i.message=a instanceof Error?a.message:`${a}`,i.finishedAt=new Date,i.status="FAILED"}return i.status==="SUCCESS"?s.onSuccess?.({message:i.message||"AI step succeeded.",startedAt:i.startedAt.getTime(),durationMs:i.finishedAt.getTime()-i.startedAt.getTime(),output:i}):s.onFailure?.({message:i.message||"AI step errored.",startedAt:i.startedAt.getTime(),durationMs:i.finishedAt.getTime()-i.startedAt.getTime(),output:i}),i};async function Ti(t,e,o){let r=o.a11yData?.id;if(!r)throw new Error("Attempted to get reverse mapping for command with no a11y id target");return t.getReverseMappedTarget(e,r,!0)}var ze=async({controller:t,step:e,advanced:o,takeScreenshots:r,...n})=>{n.onStarted?.();let s=new Date,i=await t.browser.url(),a;if(r){let l=await t.browser.screenshot();a=await n.onSaveScreenshot(l)}try{let l=await t.executePresetStep(e.command,o.disableAICaching),c;if(r){let f=await t.browser.screenshot();c=await n.onSaveScreenshot(f)}let d=new Date,u={...e,startedAt:s,finishedAt:d,status:"SUCCESS",data:l.data,results:[]},h="Successfully executed preset action.";"assertion"in e.command&&(h=l.thoughts||"Assertion passed.");let m={beforeUrl:i,beforeScreenshot:a,afterUrl:await t.browser.url(),afterScreenshot:c,startedAt:s,finishedAt:d,viewport:await t.browser.viewport(),status:"SUCCESS"};return u.status="SUCCESS",u.results=[m],u.message=h,n.onSuccess?.({message:h,startedAt:s.getTime(),durationMs:d.getTime()-s.getTime(),command:e.command,output:u}),u}catch(l){n.logger.error({err:l},`Failed executing preset step ${e.command.type}`);let c=new Date,d=l instanceof Error?l.message:`${l}`,u=await t.browser.screenshot();if("cancelOnFailure"in e.command&&e.command.cancelOnFailure){let m={...e,startedAt:s,finishedAt:c,status:"CANCELLED",message:d,results:[{beforeUrl:i,beforeScreenshot:a,afterUrl:await t.browser.url(),afterScreenshot:u,startedAt:s,finishedAt:c,viewport:await t.browser.viewport(),status:"CANCELLED",message:d}]};return n.onCancelled?.({message:d,startedAt:s.getTime(),durationMs:c.getTime()-s.getTime(),output:m}),m}let h={...e,startedAt:s,finishedAt:c,status:"FAILED",message:d,results:[{beforeUrl:i,beforeScreenshot:a,afterUrl:await t.browser.url(),afterScreenshot:u,startedAt:s,finishedAt:c,viewport:await t.browser.viewport(),status:"FAILED",message:d}]};return n.onFailure?.({message:d,startedAt:s.getTime(),durationMs:c.getTime()-s.getTime(),output:h}),h}};var bt=async({controller:t,step:e,advanced:o,logger:r,takeScreenshots:n,...s})=>{s.onStarted?.();let i={type:"MODULE",moduleId:e.moduleId,startedAt:new Date,userAgent:O.USER_AGENT,results:[],finishedAt:new Date,status:"SUCCESS"};for(let a=0;a<e.steps.length;a++){let l=e.steps[a];if(t.isClosed())throw i.status="CANCELLED",new Error("Cancelling remaining steps in module because the controller is now closed");r.debug({i:a,moduleStep:l},"Starting module step"),r.info(`Starting module sub-step ${a+1}/${e.steps.length}: ${ut(l)}`);let c;switch(l.type){case"PRESET_ACTION":c=await ze({controller:t,step:l,advanced:o,logger:r,takeScreenshots:n,onSaveScreenshot:s.onSaveScreenshot,onStarted(){s.onStepStarted?.({index:a})},onSuccess({message:u,startedAt:h,durationMs:m,output:f}){s.onStepSuccess?.({index:a,message:u,startedAt:h,durationMs:m,output:f})},onFailure({message:u,startedAt:h,durationMs:m,output:f}){s.onStepFailure?.({index:a,message:u,startedAt:h,durationMs:m,output:f})},onCancelled({message:u,startedAt:h,durationMs:m,output:f}){s.onStepCancelled?.({index:a,message:u,startedAt:h,durationMs:m,output:f})}});break;case"AI_ACTION":c=await Fe({controller:t,step:l,advanced:o,logger:r,takeScreenshots:n,onSaveScreenshot:s.onSaveScreenshot,onStarted(){s.onStepStarted?.({index:a})},onSuccess({message:u,startedAt:h,durationMs:m,output:f}){s.onStepSuccess?.({index:a,message:u,startedAt:h,durationMs:m,output:f})},onFailure({message:u,startedAt:h,durationMs:m,output:f}){s.onStepFailure?.({index:a,message:u,startedAt:h,durationMs:m,output:f})},onCommandGenerated({commandIndex:u,message:h}){s.onCommandGenerated?.({index:a,commandIndex:u,message:h})},onCommandExecuted({commandIndex:u,command:h,output:m}){s.onCommandExecuted?.({index:a,commandIndex:u,command:h,output:m})}});break;default:return(u=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(l)}if(i.results.push(c),c.status==="FAILED"||c.status==="CANCELLED"){i.status=c.status,i.finishedAt=new Date,i.message=c.message;for(let d=a+1;d<e.steps.length;d++){let h={...e.steps[d],status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:O.USER_AGENT,results:[],message:"Cancelled due to previous failure."};i.results.push(h)}break}}return i.status==="SUCCESS"?s.onSuccess?.({message:"Executed module step.",startedAt:i.startedAt.getTime(),durationMs:i.finishedAt.getTime()-i.startedAt.getTime(),output:i}):i.status==="FAILED"?s.onFailure?.({message:"Failed to execute module step.",startedAt:i.startedAt.getTime(),durationMs:i.finishedAt.getTime()-i.startedAt.getTime(),output:i}):i.status==="CANCELLED"&&s.onCancelled?.({message:"Module step cancelled.",startedAt:i.startedAt.getTime(),durationMs:i.finishedAt.getTime()-i.startedAt.getTime(),output:i}),i};import{v4 as wp}from"uuid";var bp=process.env.AWS_ACCESS_KEY_ID,vp=process.env.AWS_SECRET_ACCESS_KEY,Ep=process.env.S3_REGION,Ap=process.env.S3_BUCKET;var $r=async({test:t,runId:e,controller:o,logger:r,takeScreenshots:n,onUpdateRun:s,onSaveScreenshot:i,onTestSuccess:a})=>{try{let l=await Ri({test:t,runId:e,controller:o,logger:r,takeScreenshots:n,onUpdateRun:s,onSaveScreenshot:i});if(l==="PASSED"||l==="CANCELLED")return await a?.(t),l;throw new T("InternalPlatformError",l.message||"An unknown platform error occurred")}catch(l){throw l instanceof T||(l=new T("InternalPlatformError",l instanceof Error?l.message:`${l}`,{cause:l})),l}finally{await o.browser.cleanup()}},Ri=async({test:t,runId:e,controller:o,logger:r,takeScreenshots:n,onUpdateRun:s,onSaveScreenshot:i})=>{let a=ce.parse(t.advanced),l=r.child({runId:e,testId:t.id});l.info("Starting test run"),await s({status:"RUNNING",startedAt:new Date});let c,d="PASSED",u=[];for(let h=0;h<t.steps.length;h++){let m=t.steps[h];l.info(`Starting step ${h+1}/${t.steps.length}: ${ut(m)}`);let f;switch(m.type){case"PRESET_ACTION":f=await ze({controller:o,step:m,advanced:a,logger:l,takeScreenshots:n,onSaveScreenshot:i});break;case"AI_ACTION":f=await Fe({controller:o,step:m,advanced:a,logger:l,takeScreenshots:n,onSaveScreenshot:i});break;case"RESOLVED_MODULE":f=await bt({controller:o,step:m,advanced:a,logger:l,takeScreenshots:n,onSaveScreenshot:i});break;default:return(y=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(m)}if(u.push(f),await s({results:u}),f.status==="SUCCESS"){l.info(`Step ${h+1}/${t.steps.length} succeeded`);continue}if(f.status!=="FAILED"&&f.status!=="CANCELLED")throw new T("InternalPlatformError",`Received unexpected non-terminal status from step: ${f.status}`);l.info({message:u[u.length-1]?.message},`Step ${h+1}/${t.steps.length} ended with status: ${f.status}`),c=f,d=f.status;for(let w=h+1;w<t.steps.length;w++){let y=t.steps[w];if(y.type==="RESOLVED_MODULE"){let E={type:"MODULE",moduleId:y.moduleId,startedAt:new Date,userAgent:O.USER_AGENT,results:y.steps.map(b=>({...b,status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:O.USER_AGENT,results:[]})),finishedAt:new Date,status:"CANCELLED"};u.push(E)}else{let E={...y,status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:O.USER_AGENT,results:[]};u.push(E)}}break}return await s({status:d,finishedAt:new Date,results:u}),d==="FAILED"?c:d};var Hr={currentlyExecutingRequests:0},xi=t=>async e=>{try{Hr.currentlyExecutingRequests++,await Ii({...t,...e})}finally{Hr.currentlyExecutingRequests--}},Ii=async({socket:t,steps:e,baseUrl:o,testMetadata:r,reInitialize:n,indicesFilter:s,metadata:i,logger:a,rootController:l,localApp:c})=>{let{testId:d,sessionId:u,orgId:h}=i,m={testId:d,orgId:h},f=a.child(m),w=a.child({package:"web-agent",...m}),y=!0;c==="iframe"&&(y=!1);let E=ce.parse(r.advanced??{}),b=F.getController(u);if(!b)throw new Error("No controller found");b.setOpen();let M=O.USER_AGENT;f.info({reInitialize:n,steps:e,indicesFilter:s},"Starting execution");let v={url:o,clearCookies:!0,clearStorage:!0};if(n&&(l?(l.browser.baseURL=o,l.browser.setActiveFrame(dt),await l.resetState(v),b.setLogger(w),b.browser.setLogger(w)):await b.resetState(v),b.setOpen()),f.info(`Session restarted for test ${d} at ${o}`),t.emit("session",{url:o,userAgent:M,viewport:await b.browser.viewport(),localApp:c}),s?.length){let C=Oi(e,s);f.info({step:C},"Starting individual step"),t.emit("started",{indices:s}),await Br({indices:s,step:C,controller:b,advanced:E,socket:t,logger:f,takeScreenshots:y}),t.emit("finished");return}for(let C=0;C<e.length&&!(b.browser.closed||b.isClosed()||t.disconnected);C++){let j=e[C];if(f.info({index:C,step:j},"Starting step"),!await Br({indices:[C],step:j,controller:b,advanced:E,socket:t,logger:f,takeScreenshots:y}))break}t.emit("finished")},Li=(t,e)=>{switch(t.type){case"RESOLVED_MODULE":return t.steps[e];case"AI_ACTION":let o=t.commands[e];return{type:"PRESET_ACTION",command:o};case"PRESET_ACTION":default:throw new Error(`Cannot get a child step from step type ${t.type}`)}},Oi=(t,e)=>{let o=t[e[0]];for(let r=1;r<e.length;r++)o=Li(o,e[r]);return o},so=async t=>t,Br=async t=>{try{return await Mi(t)}catch(e){throw e instanceof T&&e.emitToUser&&t.socket.emit("error",{message:e.message}),e}},Mi=async({indices:t,step:e,controller:o,advanced:r,socket:n,logger:s,takeScreenshots:i})=>{let a=()=>{n.emit("started",{indices:t})},l=({message:u,startedAt:h,durationMs:m,command:f,output:w})=>{n.emit("success",{indices:t,message:u,startedAt:h,durationMs:m,command:f,output:ee.parse(w)})},c=({message:u,startedAt:h,durationMs:m,output:f})=>{n.emit("failure",{indices:t,message:u,startedAt:h,durationMs:m,output:ee.parse(f)})},d=({message:u,startedAt:h,durationMs:m,output:f})=>{n.emit("cancelled",{indices:t,message:u,startedAt:h,durationMs:m,output:ee.parse(f)})};switch(e.type){case"PRESET_ACTION":return(await ze({controller:o,step:e,advanced:r,logger:s,takeScreenshots:i,onSaveScreenshot:so,onStarted:a,onSuccess:l,onFailure:c,onCancelled:d})).status==="SUCCESS";case"AI_ACTION":return(await Fe({controller:o,step:e,advanced:r,logger:s,takeScreenshots:i,onSaveScreenshot:so,onStarted:a,onSuccess:l,onFailure:c,onCommandGenerated({commandIndex:m,message:f}){n.emit("commandGenerated",{indices:[...t,m],message:f})},onCommandExecuted({commandIndex:m,command:f,output:w}){n.emit("commandExecuted",{indices:[...t,m],command:f,output:ee.parse(w)})}})).status==="SUCCESS";case"RESOLVED_MODULE":return(await bt({controller:o,step:e,advanced:r,logger:s,takeScreenshots:i,onSaveScreenshot:so,onStarted:a,onSuccess:l,onFailure:c,onCancelled:d,onStepStarted({index:m}){n.emit("started",{indices:[...t,m]})},onStepSuccess({index:m,message:f,startedAt:w,durationMs:y,output:E}){n.emit("success",{indices:[...t,m],message:f,startedAt:w,durationMs:y,output:ee.parse(E)})},onStepFailure({index:m,message:f,startedAt:w,durationMs:y,output:E}){n.emit("failure",{indices:[...t,m],message:f,startedAt:w,durationMs:y,output:ee.parse(E)})},onStepCancelled({index:m,message:f,startedAt:w,durationMs:y,output:E}){n.emit("cancelled",{indices:[...t,m],message:f,startedAt:w,durationMs:y,output:ee.parse(E)})},onCommandGenerated({commandIndex:m,index:f,message:w}){n.emit("commandGenerated",{indices:[...t,f,m],message:w})},onCommandExecuted({index:m,commandIndex:f,command:w,output:y}){n.emit("commandExecuted",{indices:[...t,m,f],command:w,output:ee.parse(y)})}})).status==="SUCCESS";default:return(h=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(e)}};var Gr={event:"execute",createHandler:xi};var Ni=({metadata:t,logger:e,socket:o})=>{let{sessionId:r}=t;return async({description:n,useVision:s,testMetadata:i,isSelect:a},l)=>{let c=F.getController(r);if(!c)throw new Error("No controller found");let d=ce.parse(i.advanced??{}),u,h;try{if(u=await c.locateElement(n,s,d.disableAICaching),h={id:u.id},await c.browser.resolveCachedTargetToID(h),a)try{u.options=await c.browser.getSelectOptions(u.id)}catch(m){e.warn({err:m},"Failed getting select options"),o.emit("error",{message:`Failed getting select options: ${m}`})}}catch(m){e.error({err:m},"Error locating element"),o.emit("error",{message:m instanceof Error?m.message:`${m}`}),l({found:!1});return}e.info({locator:u,a11yData:h},"Located element result");try{await c.browser.scrollIntoView({id:u.id}),await c.browser.highlight({id:u.id})}catch(m){e.error({err:m,locator:u},"Error highlighting element"),l({found:!1});return}l({found:!0,locator:u,a11yData:h})}},jr={event:"locate",createHandler:Ni};var Pi=({socket:t,metadata:e,logger:o})=>{let{sessionId:r}=e;return async({action:n,indices:s},i)=>{try{let a=F.getController(r);if(!a)throw new Error("No controller found");await a.toggleRecordMode({action:n,indices:s,onStepRecord:(l,c)=>{t.emit("stepRecorded",{step:l,indices:c})}}),i()}catch(a){o.error({err:a},"Error in record socket handler"),i(a instanceof Error?a.message:`${a}`)}}},Wr={event:"record",createHandler:Pi};var Di=({socket:t,metadata:e,rootController:o,logger:r,localApp:n})=>async({baseUrl:s})=>{let{testId:i,sessionId:a}=e;r.info({testId:i,sessionId:a,baseUrl:s},"Reset event received");let l;if(o)await o.resetState({clearCookies:!0,clearStorage:!0,url:s}),l=o.browser.baseURL;else{let d=F.getController(a);if(!d){t.emit("error",{message:"No session to reset"});return}await d.browser.reset({clearCookies:!0,clearStorage:!0,url:s}),l=d.browser.baseURL}let c=O.USER_AGENT;r.info(`Session reset for test ${i} at ${l}`),t.emit("session",{url:l,userAgent:c,viewport:O.VIEWPORT,localApp:n})},Vr={event:"reset",createHandler:Di};import{v4 as ji}from"uuid";var _i={type:"a11y",version:"1.0.0",useHistory:"diff",useGoalSplitter:!0},Te=_i;import{existsSync as zi}from"fs";import{faker as Ch}from"@faker-js/faker";import Rh from"axios";import Ih from"moment";import ki from"p-timeout";async function Kr({code,fragment,timeoutMs=1e4}){let evalCode=code;if(fragment){let t=`__momentic${Math.round(Math.random()*1e4)}`;evalCode=`
|
|
25
25
|
const ${t} = async () => {
|
|
26
26
|
${code}
|
|
27
27
|
}
|
|
28
28
|
${t}()
|
|
29
|
-
`}let result,runCode=async()=>{result=await Promise.resolve(eval(evalCode))};return await
|
|
30
|
-
You have already executed the following commands successfully (most recent listed first)`,"-".repeat(10)];return r.reverse().forEach((s,i)=>{if(n.push(`COMMAND ${r.length-i}${i===0?" (command just executed)":""}: ${s.serializedCommand}`),i===0)if(
|
|
29
|
+
`}let result,runCode=async()=>{result=await Promise.resolve(eval(evalCode))};return await ki(runCode(),{milliseconds:timeoutMs,message:`Timeout of ${timeoutMs}ms exceeded for code execution`}),result}import{z as vt}from"zod";var qr=process.env.GCP_JS_EVAL_FUNCTION_ENDPOINT,Fi=vt.object({result:vt.unknown(),error:vt.string().optional(),success:vt.boolean()});async function Yr({code:t,fragment:e,timeoutMs:o=1e4}){let r=new AbortController;if(!qr)throw new Error("GCP_JS_EVAL_FUNCTION_ENDPOINT environment variable not set");let n=setTimeout(()=>{r.abort()},o),s=await fetch(qr,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:t,fragment:e}),signal:r.signal});if(!s.ok)throw new Error(`Code evaluation server returned error code ${s.status}`);clearTimeout(n);let i;try{i=Fi.parse(await s.json())}catch(a){throw new Error(`Failed to parse response: ${a}`)}if(i.error)throw new Error(i.error);return i.result}var Ui=$i();function $i(){if(process.env.NODE_ENV!=="production")return!1;if(process.env.NAMESPACE)return!0;try{if(zi("/var/run/secrets/kubernetes.io/serviceaccount/token"))return!0}catch{}return!1}async function Xr(t){let e;return Ui?(t.logger.info({code:t.code,fragment:t.fragment},"Running code remotely"),e=Yr):(t.logger.info({code:t.code,fragment:t.fragment},"Running code locally"),e=Kr),e(t)}import Hi from"dedent";import Bi from"diff-lines";var Et=class t{static ACTION_DEBOUNCE_DELAY=1e3;onStepRecord;signal;generator;indices;keyBuffer=[];timer=null;keyBufferMode="normal";lastStep=null;constructor({signal:e,onStepRecord:o,generator:r,initialIndices:n}){this.signal=e,this.onStepRecord=(s,i)=>{this.lastStep={step:s,indices:i},o(s,i)},this.generator=r,this.indices=n}async recordClick(e,o){if(this.signal.aborted)return;let r=ot("CLICK"),n={type:"PRESET_ACTION",command:{...r,target:{elementDescriptor:`Generating description for: ${e.nodeOnlySerializedForm?.trim()??`element with id ${e.id}`}`,a11yData:e}}},s=Array.from(this.indices);this.indices[this.indices.length-1]++,this.onStepRecord(n,s);let i=await this.generator.getReverseMappedDescription({goal:`${e.id}`,browserState:o},!1);n.command.target.elementDescriptor=i.phrase,this.onStepRecord(n,s)}flushKeyBufferToCommand(){let e;if(this.keyBufferMode==="normal"){let n=ot("TYPE");e={type:"PRESET_ACTION",command:{...n,target:void 0,value:this.keyBuffer.join("")}}}else{let n=ot("PRESS");e={type:"PRESET_ACTION",command:{...n,value:this.keyBuffer.join("+")}}}let o,r=this.lastStep?.step.command;if(r?.type===e.command.type){let n=r.value,s=e.command.value;e={type:"PRESET_ACTION",command:{...r,value:e.command.type==="PRESS"?`${n}+${s}`:`${n}${s}`}},o=this.lastStep.indices}else r?.type==="CLICK"&&e.command.type==="TYPE"?(this.lastStep.step.command={...e.command,target:r.target},e=this.lastStep.step,o=this.lastStep.indices):(o=Array.from(this.indices),this.indices[this.indices.length-1]++);this.onStepRecord(e,o),this.keyBuffer=[],this.timer=null}recordKeystroke(e){if(e==="Shift"||e==="CapsLock"||this.signal.aborted)return;this.timer!==null&&clearTimeout(this.timer);let o="normal";e.length>1&&(o="special"),this.keyBuffer.length!==0&&o!==this.keyBufferMode&&this.flushKeyBufferToCommand(),this.keyBufferMode=o,this.keyBuffer.push(e),this.timer=setTimeout(()=>{this.flushKeyBufferToCommand()},t.ACTION_DEBOUNCE_DELAY)}};var Gi=1e4,se=class{browser;pendingInstructions;generator;commandHistory;config;closed=!1;logger;recordAbortController=null;constructor({browser:e,config:o,generator:r,logger:n}){this.browser=e,this.generator=r,this.config=o,this.logger=n,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]}setOpen(){this.closed=!1}isClosed(){return this.closed}setLogger(e){this.logger=e}resetHistory(){this.commandHistory=[],this.pendingInstructions=[]}async resetState(e){this.resetHistory(),this.closed=!0,await this.browser.reset(e)}async getBrowserState(){let o=await(await this.browser.getA11yTree()).serialize();return this.logger.debug({tree:o,activeFrame:this.browser.getActiveFrame()},"Got a11y tree"),o}getSerializedHistory(e,o){let r;return this.config.useHistory==="diff"?r=this.getDiffHistory(e,o):r=this.getListHistory(),r}async splitUserGoal(e,o,r){if(e==="AI_ACTION"&&o.match(/[,!;.]|(?:and)|(?:then)/)&&this.config.useGoalSplitter){let n=await this.generator.getGranularGoals({goal:o,url:await this.browser.url()},r);this.pendingInstructions=n.reverse()}else this.pendingInstructions=[o]}async promptToCommand(e,o,r){try{return await this.promptToCommandHelper(e,o,r)}catch(n){throw n instanceof T?n:new T("InternalWebAgentError",n instanceof Error?n.message:`${n}`,{cause:n})}}async promptToCommandHelper(e,o,r){if(this.pendingInstructions.length===0){if(!o.trim())throw new Error("Cannot generate commands for empty goal");await this.splitUserGoal(e,o,r)}let n=this.pendingInstructions[this.pendingInstructions.length-1];this.logger.info({goal:n},"Starting prompt translation");let s=Date.now(),i=await this.browser.url(),a=await this.getBrowserState();this.logger.info({duration:Date.now()-s,url:i},"Got browser state");let l=this.commandHistory.length;this.commandHistory.push({state:"PENDING",browserStateBeforeCommand:a,urlBeforeCommand:i,type:e});let c=this.getSerializedHistory(i,a),d={url:i,numPrevious:l,browserState:a,history:c,goal:n,lastCommand:this.lastExecutedCommand},u=await this.generator.getProposedCommand(d,r);if(this.logger.info({type:u.type,thoughts:u.thoughts},"Got proposed command"),u.type==="SUCCESS"){let h=this.pendingInstructions.pop();if(this.logger.info({finishedInstruction:h,remainingInstructions:this.pendingInstructions},"Removing pending instruction due to SUCCESS"),this.pendingInstructions.length!==0)return this.commandHistory=[],this.promptToCommand(e,"",r)}else u.type==="FAILURE"&&(this.logger.info({remainingInstructions:this.pendingInstructions},"Removing pending instructions due to FAILURE"),this.pendingInstructions=[]);return{context:d,command:u}}async locateElement(e,o,r){if(!e)throw new T("ActionFailureError","Cannot locate element with empty description");let n=await this.getBrowserState(),s;if(o){let{before:i,after:a}=await this.browser.screenshotWithHints();if(s=await this.generator.getElementLocationWithVision({goal:e,screenshot:i,hintActivatedScreenshot:a},r),s.id>0){let l=this.browser.getA11yIdFromDataMomenticId(s.id);if(!l)throw new T("InternalWebAgentError",`Unable to find corresponding DOM node for id ${s.id}`);s.id=l}}else s=await this.generator.getElementLocation({browserState:n,goal:e},r);if(s.id<0)throw new T("ActionFailureError",`Unable to locate element: ${s.thoughts?s.thoughts:"please ensure the element is visible and conforms to Accessibility guidelines"}`);return s}getDiffHistory(e,o){let r=this.history.filter(s=>s.type==="AI_ACTION");if(r.length===0)return"<NONE/>";let n=[`
|
|
30
|
+
You have already executed the following commands successfully (most recent listed first)`,"-".repeat(10)];return r.reverse().forEach((s,i)=>{if(n.push(`COMMAND ${r.length-i}${i===0?" (command just executed)":""}: ${s.serializedCommand}`),i===0)if(Ee(s.urlBeforeCommand,e))n.push(` URL CHANGE: '${s.urlBeforeCommand}' -> '${e}'`);else{let a=Bi(s.browserStateBeforeCommand,o,{n_surrounding:1});a?a.length<Gi?(n.push("PAGE CONTENT CHANGE:"),a.split(`
|
|
31
31
|
`).forEach(l=>n.push(` ${l}`))):n.push("PAGE CONTENT CHANGE: <TOO_LONG_TO_DISPLAY/>"):n.push("PAGE CONTENT CHANGE: <NONE/>")}n.push("-".repeat(10))}),n.push(`STARTING URL: ${this.browser.baseURL}`),n.join(`
|
|
32
|
-
`)}getListHistory(){return
|
|
32
|
+
`)}getListHistory(){return Hi`Here are the commands that you have successfully executed:
|
|
33
33
|
${this.commandHistory.filter(e=>e.type==="AI_ACTION").map(e=>`- ${e.serializedCommand}`).join(`
|
|
34
|
-
`)}`}async executeCommand(e,o,r=!1){let n=this.commandHistory[this.commandHistory.length-1];if(!r&&(!n||n.state!=="PENDING"))throw new T("InternalWebAgentError","Executing command but there is no pending entry in the history");if(this.closed)throw new Error("Attempting to execute command on a closed controller");let s=Date.now(),i=await this.executePresetStep(e,o),a=Date.now()-s;return this.logger.debug({result:i,duration:a},"Got execution result"),i.succeedImmediately&&!r&&(this.pendingInstructions.pop(),this.pendingInstructions.length>0&&(i.succeedImmediately=!1)),i.elementInteracted&&"target"in e&&e.target&&!e.target.elementDescriptor&&(e.target.elementDescriptor=i.elementInteracted.trim()),r||(n.generatedStep=e,n.serializedCommand=Me(e),n.state="DONE"),i}async executeAssertion(e){let o=await this.getBrowserState(),r=await this.browser.url(),n;if(e.useVision)n={goal:e.assertion,url:r,screenshot:await this.browser.screenshot(),browserState:"",history:"",numPrevious:-1,lastCommand:null};else{let i=this.getSerializedHistory(r,o);n={goal:e.assertion,url:r,browserState:o,history:i,lastCommand:this.lastExecutedCommand,numPrevious:this.commandHistory.length}}let s=await this.generator.getAssertionResult(n,e.useVision,e.disableCache);if(s.relevantElements&&Promise.all(s.relevantElements.map(i=>this.browser.highlight({id:i}))),!s.result)throw new T("AssertionFailureError",s.thoughts);return{succeedImmediately:!1,thoughts:s.thoughts,urlAfterCommand:r}}async wrapElementTargetingCommand(e,o,r,n,s=1){if(!e.a11yData&&!e.elementDescriptor)throw new T("ActionFailureError","Cannot target element with no target data or element descriptor");let i=e.a11yData&&ot(e.a11yData);e.a11yData||(this.logger.info("No cached locator data for target, prompting AI for fresh location"),s--,e.a11yData=tt.parse(await this.locateElement(e.elementDescriptor,o,r)));try{let a=await n(e.a11yData);return i?this.logger.info({target:e},"Successfully used cached target to perform action"):this.logger.debug({target:e},"Successfully generated and used new a11y target information"),a}catch(a){if(s>0&&e.elementDescriptor)return this.logger.warn({err:a,target:e},"Failed to execute action with cached target, retrying with AI"),e.a11yData=void 0,this.wrapElementTargetingCommand(e,o,r,n,s);if(a instanceof T)throw a;let l=`Failed to find ${e.elementDescriptor?`${e.elementDescriptor}`:"element"}: ${a instanceof Error?a.message:a}`;throw this.logger.error({err:a,target:e},l),new T("ActionFailureError",l,{cause:a})}}async executePresetStep(e,o){try{return await this.executePresetStepHelper(e,o)}catch(r){throw r instanceof T?r:new T("InternalWebAgentError",r instanceof Error?r.message:`${r}`,{cause:r})}}async executePresetStepHelper(e,o){switch(e.type){case"SUCCESS":return e.condition?.assertion.trim()?this.executeAssertion(e.condition):{succeedImmediately:!1,urlAfterCommand:await this.browser.url()};case"AI_ASSERTION":return this.executeAssertion(e);case"NAVIGATE":if(!Yt(e.url)&&!Xt(e.url,this.browser.baseURL))throw new T("ActionFailureError",`Invalid URL: ${e.url}`);await this.browser.navigate({url:e.url});break;case"CAPTCHA":let r=await this.browser.solveCaptcha();r&&(await this.wrapElementTargetingCommand({elementDescriptor:"the captcha image solution input"},e.useVision,o,l=>this.browser.click(l)),await this.browser.type(r,{clearContent:!0,pressKeysSequentially:!1}));break;case"GO_BACK":await this.browser.goBack();break;case"GO_FORWARD":await this.browser.goForward();break;case"SCROLL_DOWN":case"SCROLL_UP":let n;return e.target&&(e.target.elementDescriptor.trim()||e.target.a11yData)&&(n=await this.wrapElementTargetingCommand(e.target,e.useVision,o,l=>this.browser.hover(l))),e.type==="SCROLL_UP"?await this.browser.scrollUp(e.deltaY):await this.browser.scrollDown(e.deltaY),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:n};case"WAIT":await this.browser.wait(e.delay*1e3);break;case"REFRESH":await this.browser.refresh();break;case"CLICK":{let l=await this.browser.url(),c=await this.wrapElementTargetingCommand(e.target,e.useVision,o,u=>this.browser.click(u,{doubleClick:e.doubleClick,rightClick:e.rightClick,force:e.force})),d={urlAfterCommand:await this.browser.url(),succeedImmediately:!1,elementInteracted:c};return be(l,d.urlAfterCommand)&&(d.succeedImmediately=!0,d.succeedImmediatelyReason="URL changed"),d}case"SELECT_OPTION":{let l=await this.wrapElementTargetingCommand(e.target,!1,o,c=>this.browser.selectOption(c,e.option));return{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:l}}case"TAB":await this.browser.switchToPage(e.url);break;case"COOKIE":if(!e.value)break;await this.browser.setCookie(e.value);break;case"LOCAL_STORAGE":if(!e.value||!e.key)break;await this.browser.setLocalStorage(e.key,e.value);break;case"JAVASCRIPT":{let l;try{l=await Jr({code:e.code,fragment:!!e.fragment,timeoutMs:e.timeout?e.timeout*1e3:void 0,logger:this.logger})}catch(c){throw new T("ActionFailureError",c instanceof Error?c.message:`${c}`,{cause:c})}try{l=JSON.parse(JSON.stringify(l))}catch(c){throw new T("ActionFailureError",`Return value is not serializable: ${c instanceof Error?c.message:`${c}`}`,{cause:c})}return{urlAfterCommand:await this.browser.url(),succeedImmediately:!1,data:{result:l}}}case"TYPE":{let l=await this.browser.url(),c=await this.wrapElementTargetingCommand(e.target,e.useVision,o,u=>this.browser.click(u,{force:e.force}));await this.browser.type(e.value,{clearContent:e.clearContent,pressKeysSequentially:e.pressKeysSequentially}),e.pressEnter&&await this.browser.press("Enter");let d={urlAfterCommand:await this.browser.url(),succeedImmediately:!1,elementInteracted:c};return be(l,d.urlAfterCommand)&&(d.succeedImmediately=!0,d.succeedImmediatelyReason="URL changed"),d}case"HOVER":{let l=await this.wrapElementTargetingCommand(e.target,e.useVision,o,c=>this.browser.hover(c));return{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:l}}case"PRESS":let s=await this.browser.url();await this.browser.press(e.value);let i={urlAfterCommand:await this.browser.url(),succeedImmediately:!1};return be(s,i.urlAfterCommand)&&(i.succeedImmediately=!0,i.succeedImmediatelyReason="URL changed"),i;case"REQUEST":{let l=e.timeout??30,c=null,d=new AbortController,u=Object.fromEntries(Object.entries(e.headers||{}).filter(([x,v])=>x&&v)),p=new URLSearchParams;Object.entries(e.params||{}).filter(([x,v])=>x&&v).forEach(([x,v])=>{p.append(x,v)});let m;if(Yt(e.url)&&(m=e.url),Xt(e.url,this.browser.baseURL)&&(m=new URL(e.url,this.browser.baseURL).toString()),!m)throw new T("ActionFailureError",`Invalid URL: ${e.url}`);let h=async()=>{try{c=await fetch(`${m}?${p.toString()}`,{headers:u,method:e.method,body:e.body,signal:d.signal})}catch(x){this.logger.error({err:x},"Failed to make fetch request")}},w=async()=>{await new Promise(x=>setTimeout(x,l*1e3)),d.abort()};await Promise.race([w(),h()]);let y=c;if(!y)throw new T("ActionFailureError",`Fetch request timed out after ${l} seconds`);if(!y.ok)throw new T("ActionFailureError",`Fetch request failed with status ${y.status}`);let E={};y.headers.forEach((x,v)=>{E[v]=x});let b={status:y.status,headers:E};return y.headers.get("content-type")?.includes("json")?b.json=await y.json():y.headers.get("content-type")?.includes("text")&&(b.text=await y.text()),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),data:b}}default:return(l=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(e)}return{succeedImmediately:!1,urlAfterCommand:await this.browser.url()}}async getReverseMappedTarget(e,o,r){return(await this.generator.getReverseMappedDescription({browserState:e.browserState,goal:`${o}`},r)).phrase}async toggleRecordMode({action:e,indices:o,onStepRecord:r}){if(e==="STOP"){this.recordAbortController?.abort();return}if(!o||!r)throw new Error("Indices or step recording callback not provided to toggleRecordMode");this.recordAbortController=new AbortController;let n=new Et({signal:this.recordAbortController.signal,initialIndices:o,generator:this.generator,onStepRecord:r});await this.browser.startRecording(this.recordAbortController.signal,n)}};async function Qr({socket:t,generator:e,logger:o,rootController:r,localApp:n}){let s=o.child({package:"web-agent"}),i=!0;n==="iframe"&&(i=!1);let a=t.handshake.query.testId,l=t.handshake.query.baseUrl;if(!a)throw new Error("Socket connection is missing testID");let c=t.handshake.query.orgId,d=Gi(),u=h=>{t.emit("screenshot",{...h,url:l})},p=r;if(p)p.browser.setActiveFrame(dt),p.browser.baseURL=l,await p.browser.reset({clearCookies:!0,clearStorage:!0});else{let h=await O.init({baseUrl:l,logger:s,takeScreenshots:i,onScreenshot:u});p=new ne({browser:h,generator:e,config:Ae,logger:s})}i&&To(t,d,o),k.registerSession(p,d);let m=O.USER_AGENT;return t.emit("session",{url:l,userAgent:m,viewport:await p.browser.viewport(),localApp:n}),{sessionId:d,testId:a,orgId:c}}var Zr=[jr,Ro,Kr,Vr,Wr];var en=t=>{let{logger:e}=t,o=new ji(t.baseServer,{cors:{origin:"*",methods:["GET","POST"]}});return o.on("connection",async r=>{let n;try{n=await Qr({...t,socket:r})}catch(s){e.error({event:"connection",type:"websocket",err:s},"Failed to setup connection"),r.emit("error",{message:`${s}`});return}Zr.forEach(s=>Vi(s,{socket:r,metadata:n,...t}))}),o},Vi=(t,e)=>{let o=t.createHandler(e),r=(...n)=>{e.logger.debug({event:t.event,metadata:e.metadata,args:n},"Websocket event");let s=i=>{e.logger.error({event:t.event,type:"websocket",args:n,err:i instanceof Error?i:new Error(`${i}`)},"Unhandled exception in socket handler"),e.socket.emit("error",{message:`${i}`})};try{let i=o.apply(void 0,n);i&&typeof i.catch=="function"&&i.catch(s)}catch(i){s(i)}};e.socket.on(t.event,r)};import Wi from"fetch-retry";var Ki=Wi(global.fetch),Ce="v1",Te=class{baseURL;apiKey;constructor(e){this.baseURL=e.baseURL,this.apiKey=e.apiKey}async getElementLocation(e,o){let r=await this.sendRequest(`/${Ce}/web-agent/locate-element`,{browserState:e.browserState,goal:e.goal,disableCache:o});return Ht.parse(r)}async getElementLocationWithVision(e,o){let r=await this.sendRequest(`/${Ce}/web-agent/visual-locate`,{goal:e.goal,screenshot:e.screenshot?.toString("base64"),hintActivatedScreenshot:e.hintActivatedScreenshot?.toString("base64"),disableCache:o});return Ht.parse(r)}async getAssertionResult(e,o,r){if(o){let s=await this.sendRequest(`/${Ce}/web-agent/assertion`,{url:e.url,goal:e.goal,screenshot:e.screenshot?.toString("base64"),disableCache:r,vision:!0});return $t.parse(s)}let n=await this.sendRequest(`/${Ce}/web-agent/assertion`,{url:e.url,browserState:e.browserState,goal:e.goal,history:e.history,numPrevious:e.numPrevious,lastCommand:e.lastCommand,disableCache:r,vision:!1});return $t.parse(n)}async getProposedCommand(e,o){let r=await this.sendRequest(`/${Ce}/web-agent/next-command`,{url:e.url,browserState:e.browserState,goal:e.goal,history:e.history,numPrevious:e.numPrevious,lastCommand:e.lastCommand,disableCache:o});return qo.parse(r)}async getGranularGoals(e,o){let r=await this.sendRequest(`/${Ce}/web-agent/split-goal`,{url:e.url,goal:e.goal,disableCache:o});return Yo.parse(r)}async getReverseMappedDescription(e,o){let r=await this.sendRequest(`/${Ce}/web-agent/reverse-mapped-description`,{goal:e.goal,browserState:e.browserState,disableCache:o});return Xo.parse(r)}async sendRequest(e,o){let r=await Ki(`${this.baseURL}${e}`,{retries:1,retryDelay:1e3,method:"POST",body:JSON.stringify(o),headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}});if(!r.ok)throw new Error(`Request to ${e} failed with status ${r.status}: ${await r.text()}`);return r.json()}};import{existsSync as qi,readFileSync as Yi,writeFileSync as io}from"fs";import ao from"path";import{parse as Xi,stringify as Ji}from"yaml";var _=process.env.MOMENTIC_DIR??le;function tn(t,e){let o=ir(t);for(let[s,i]of Object.entries(o.modules)){let a=Ve(s);io(ao.join(_,te,`${a}.yaml`),i,"utf-8")}let r=Ve(e),n=ao.join(_,`${r}.yaml`);return io(n,o.test,"utf-8"),`${r}.yaml`}function on(t,e){if(!qi(e))throw new Error(`Test not found at path: ${ao}`);let o=Yi(e,"utf-8"),n={...Xi(o),...t},s=Ji(n);io(e,s,"utf-8")}import{readFileSync as At,readdirSync as rn,writeFileSync as nn}from"fs";import{join as lo}from"path";import{v4 as Qi}from"uuid";import{parse as Ct,stringify as sn}from"yaml";var qe=lo(_,te);function an(t,e){let o=gt(qe,e).path,r=At(o,"utf-8"),s={...Ct(r),...t},i=sn(s);nn(o,i,"utf-8")}function ln(t,e){let o=Ve(t),r=lo(qe,`${o}.yaml`),n={fileType:"momentic/module",schemaVersion:F,moduleId:Qi(),name:t,steps:e},s=sn(n);return nn(r,s,"utf-8"),{type:"RESOLVED_MODULE",moduleId:n.moduleId,name:n.name,steps:n.steps}}function cn(){let t=[];for(let e in rn(qe)){if(!e.endsWith(".yaml"))continue;let o=At(e,"utf-8"),r=Ct(o),n={name:r.name,moduleId:r.moduleId,numSteps:r.steps.length};t.push(n)}return t}function dn(){let t=[];for(let e of rn(qe)){if(!e.endsWith(".yaml"))continue;let o=lo(qe,e),r=At(o,"utf-8"),n=Ct(r);try{let s=Gt.parse(n);t.push(s)}catch(s){g.warn({err:s},"Error parsing module, skipping...")}}return t}function mn(t){let e=gt(te,t).path,o=At(e,"utf-8"),r=Ct(o);return Gt.parse(r)}import{Router as ra}from"express";import{existsSync as Zi,readFileSync as ea,readdirSync as ta}from"fs";import{join as un}from"path";import{parse as oa}from"yaml";var co=un(_,ct);function pn(){let t=[];if(!Zi(co))return[];for(let e of ta(co)){if(!e.endsWith(".yaml"))continue;let o=un(co,e),r=ea(o,"utf-8"),n=oa(r);try{let s=W.parse(n);t.push(s)}catch(s){g.warn({err:s},"Error parsing environment, skipping...")}}return t}function B(t){return function(...e){let o=e[e.length-1],r=t(...e);Promise.resolve(r).catch(o)}}var hn=ra();hn.get("/",B((t,e)=>{let o=pn();e.status(200).json(o)}));var fn=hn;import{Router as na}from"express";var Ye=na();Ye.get("/",B((t,e)=>{let r=dn().map(n=>({...n,type:"RESOLVED_MODULE"}));e.status(200).json(r)}));Ye.get("/metadata",B((t,e)=>{let o=cn();e.status(200).json(o)}));Ye.post("/",B((t,e)=>{let o;try{o=Vo.parse(t.body)}catch(n){e.status(400).json({error:`Invalid request body: ${n}`});return}try{je(o.name)}catch(n){e.status(400).json({error:`Invalid module name: ${n}`});return}let r=ln(o.name,o.steps);e.status(201).json(r)}));Ye.get("/:moduleId",B((t,e)=>{if(!t.params.moduleId){e.status(400).json({error:"Missing moduleId in url path."});return}let o=mn(t.params.moduleId);e.json(o)}));var gn=Ye;import{Router as sa}from"express";import{existsSync as ia}from"fs";import{join as mo}from"path";import{v4 as aa}from"uuid";var Xe=sa();Xe.get("/",B((t,e)=>{let o=We(_).filter(r=>r.localOnly);e.status(200).json(o)}));Xe.post("/",B((t,e)=>{let o;try{o=jo.parse(t.body)}catch(a){e.status(400).json({error:`Invalid request body: ${a}`});return}try{je(o.name)}catch(a){e.status(400).json({error:a.message});return}let n={id:aa(),name:o.name,baseUrl:o.baseUrl,schemaVersion:F,advanced:{disableAICaching:!1,availableAsModule:!0},retries:0,steps:[],localOnly:!0};o.environment&&(n.envs=[{name:o.environment,defaultOnLocal:!0}]);let s=tn(n,o.name),i={...n,testPath:s};e.status(201).json(i)}));Xe.get("/:testPath",B(async(t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let o=mo(_,t.params.testPath);if(!ia(o)){e.status(400).json({error:`Test not found at path: ${o}`});return}let r=await yt(o,mo(_,te));e.status(200).json(r)}));Xe.patch("/:testPath",B((t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let o;try{o=Go.parse(t.body)}catch(s){e.status(400).json({error:`Invalid request body: ${s}`});return}let r=[],n={};for(let s of o.steps){if(s.type!=="RESOLVED_MODULE"){r.push(s);continue}r.push({type:"MODULE",moduleId:s.moduleId}),n[s.moduleId]=s.steps}for(let[s,i]of Object.entries(n))an({steps:i},s);on({steps:r},mo(_,t.params.testPath)),e.status(201).json({message:"ok"})}));var yn=Xe;var uo="https://api.momentic.ai",wn=t=>{uo=t},bn=()=>uo,la,vn=t=>{la=t};var Sn,En=async t=>{if(Sn)return;let e=await fetch(`${uo}/v1/auth/check`,{headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`}});if(!e.ok)throw new Error(`Error checking API key with server (code ${e.status}): ${await e.text()}`);let o=await e.json(),{orgId:r}=nr.parse(o);Sn=r};import An,{multistream as ca}from"pino";import da from"pino-pretty";var po=new Map,ma=process.env.NODE_ENV==="production",ho=class t{consoleLogger;ddClientToken;hostname;bindingAttributes;site="https://http-intake.logs.us5.datadoghq.com/api/v2/logs";constructor({bindings:e,clientToken:o,hostname:r}){this.ddClientToken=o,this.hostname=r,this.bindingAttributes={...e,env:process.env.NODE_ENV};let n={base:this.bindingAttributes,errorKey:"err",level:"debug"};this.consoleLogger=ma?An(n):An(n,ca([{stream:da({colorize:!0})}]))}child(e){return new t({clientToken:this.ddClientToken,bindings:{...this.bindingAttributes,...e,hostname:this.hostname}})}flush(e){this.consoleLogger.flush(e)}log(e,o,r,...n){this.consoleLogger[e](o,r,...n);let s=Object.assign({},this.bindingAttributes,o&&typeof o=="object"?o:{});n.length>0&&(s.args=n),(async()=>{try{let i=await fetch(this.site,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json","DD-API-KEY":this.ddClientToken},body:JSON.stringify([{ddsource:this.bindingAttributes.app,ddtags:Object.keys(s).map(a=>`${a}:${s[a]}`).join(","),hostname:this.hostname??"vercel",message:r||"",service:"momentic"}])});if(!i.ok)throw new Error(`Failed to log to DD: ${i.statusText})}`)}catch(i){this.consoleLogger.warn({obj:o,msg:r,args:n,err:i},"Failed to log to DD")}})()}debug(e,o,...r){this.log("debug",e,o,...r)}info(e,o,...r){this.log("info",e,o,...r)}warn(e,o,...r){this.log("warn",e,o,...r)}error(e,o,...r){this.log("error",e,o,...r)}bindings(){return this.bindingAttributes}},Cn=({app:t,clientToken:e,hostname:o})=>{if(!process.env.DD_CLIENT_TOKEN&&!process.env.NEXT_PUBLIC_DD_CLIENT_TOKEN&&!e)throw new Error("Missing DD_CLIENT_TOKEN");return po.has(t)||po.set(t,new ho({bindings:{app:t},hostname:o,clientToken:e||process.env.NEXT_PUBLIC_DD_CLIENT_TOKEN||process.env.DD_CLIENT_TOKEN})),po.get(t)};import{hostname as ua}from"os";var Y=Cn({app:"desktop-server",clientToken:"pubcfd7516a5c0ba852b42675cd97bee027",hostname:ua()});async function Rn({momenticServerUrl:t,apiKey:e,mode:o,serverPort:r,appPort:n,staticDir:s,assetsDir:i}){if(!fa(_)||!ga(_).isDirectory()){let m=wa.isAbsolute(_);throw new Error(`Root folder ${_} does not exist${m?"":` in the current directory ("${process.cwd()}")`}. Please run \`npx momentic init\` if you wish to setup Momentic here for the first time.`)}t&&wn(t),g.info("Checking API key"),await En(e);let l=`http://localhost:${r}`;n&&(l=`http://localhost:${n}`);let c=va(s);await new Promise(m=>{c.listen(r,()=>{g.info(`Server is running at http://localhost:${r}`),m()})});let u=new Te({baseURL:bn(),apiKey:e});if(o==="web"){await Tn("web",c,u,Y),await Sa(l);return}let p=await Ea({appUrl:l,apiGenerator:u,extensionPath:ba(i,"extension"),onClose:async()=>{g.info("Browser closed, closing app."),await p.browser.cleanup(),c.close(()=>{process.exit(0)})}});await Tn("iframe",c,u,Y,p),process.on("uncaughtException",m=>{Y.error({err:m},"Uncaught exception in desktop-server")}),process.on("unhandledRejection",m=>{Y.error({err:m},"Unhandled rejection in desktop-server")})}function va(t){let e=fo();e.use(ha()),e.use(pa.json({limit:"50mb"}));let o=fo.Router();if(o.use("/tests",yn),o.use("/modules",gn),o.use("/environments",fn),e.use("/api",o),e.use((n,s,i)=>{n.path!=="/healthcheck"&&Y.debug({url:n.url,path:n.path,query:n.query,method:n.method,body:n.body,headers:n.rawHeaders,client:n.ip},"Received desktop-server request"),s.on("close",()=>{s.statusCode>=400&&Y.error({url:n.url,method:n.method,statusCode:s.statusCode},"Request completed in error")}),i()}),e.use((n,s,i,a)=>{Y.error({stack:n.stack,msg:n.message,url:s.url,method:s.method},"Unhandled exception leading to 500 on desktop-server"),i.status(500).send("Internal Server Error"),a(n)}),t){let n=fo.static(t);e.use("/",n),e.use("*",n)}return ya.createServer(e)}async function Ea({appUrl:t,apiGenerator:e,extensionPath:o,onClose:r}){let n=await O.init({baseUrl:t,logger:Y,browserArgs:{headless:!1,handleSIGTERM:!0},contextArgs:{bypassCSP:!0,viewport:null,deviceScaleFactor:void 0},localMode:!0,localAppUrl:t,extensionPath:o,onClose:r}),s=new ne({browser:n,config:Ae,generator:e,logger:Y});return vn(s),s}async function Tn(t,e,o,r,n){en({baseServer:e,generator:o,logger:r,localApp:t,rootController:n})}import{existsSync as el}from"fs";import Qn,{dirname as tl}from"path";import{fileURLToPath as ol}from"url";var xn="0.0.29";var K="v1",X=class{baseURL;apiKey;constructor(e){this.baseURL=e.baseURL,this.apiKey=e.apiKey}async getRun(e){let o=await this.sendRequest(`/${K}/runs/${e}`,{method:"GET"});return or.parse(o)}async createRun(e){let o=await this.sendRequest(`/${K}/runs`,{method:"POST",body:e});return tr.parse(o)}async updateRun(e,o){await this.sendRequest(`/${K}/runs/${e}`,{method:"PATCH",body:o})}async getTest(e){let o=await this.sendRequest(`/${K}/tests/${e}`,{method:"GET"});return Qo.parse(o)}async getAllTestIds(){let e=await this.sendRequest(`/${K}/tests`,{method:"GET"});return Zo.parse(e)}async getTestYAMLExport(e){let o=await this.sendRequest(`/${K}/tests/export`,{method:"POST",body:e});return er.parse(o)}async updateTestWithYAML(e){await this.sendRequest(`/${K}/tests/update`,{method:"POST",body:e})}async queueTests(e){let o=await this.sendRequest(`/${K}/tests/queue`,{method:"POST",body:e});return Jo.parse(o)}async uploadScreenshot(e){let o=await this.sendRequest(`/${K}/screenshots`,{method:"POST",body:e});return rr.parse(o)}async getAllEnvironments(){let e=await this.sendRequest(`/${K}/environments`,{method:"GET"});return sr.parse(e)}async updateEnvironments(e){await this.sendRequest(`/${K}/environments`,{method:"POST",body:e})}async sendRequest(e,o){let r=await fetch(`${this.baseURL}${e}`,{method:o.method,body:o.body?JSON.stringify(o.body):void 0,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}});if(!r.ok)throw new Error(`Request to ${e} failed with status ${r.status}: ${await r.text()}`);return r.status===204?r.text():r.json()}};import{createHash as On}from"crypto";import{existsSync as Mn,mkdirSync as La,readFileSync as wo,writeFileSync as Nn}from"fs";import{join as Pn}from"path";import{parse as Oa}from"yaml";import{existsSync as go,mkdirSync as Je,readdirSync as Ln,statSync as yo}from"fs";import{homedir as Ra}from"os";import{join as Qe,resolve as xa}from"path";import Ca from"chalk";import Ta from"readline/promises";async function J(t,e){if(process.env.CI)return!0;let o=Ta.createInterface({input:process.stdin,output:process.stdout});t=`${t} (y/N) `;let r=e?Ca.bold.yellow(t):t,n=await o.question(r);return o.close(),n.toLowerCase()==="y"}var Tt=le,Re=Qe(le,te),So=Qe(le,Bo),q=Qe(le,ct),Ia=Qe(Ra(),"momentic","chromium");function In(t){return go(t)&&yo(t).isDirectory()}async function ce(t,e=!1){In(Tt)||(!e&&!await J(`A '${le}' folder was not found in the current directory. Setup the required Momentic folder structure?`)&&(g.error("Setup cancelled"),process.exit(1)),Je(Tt),Je(Re),Je(So),Je(q),Je(Ia,{recursive:!0})),(!In(q)||Ln(q).length===0)&&(!e&&!await J("You do not appear to have any environments configured locally. Pull environments from Momentic Cloud?")&&(g.error("Setup cancelled"),process.exit(1)),await Rt({client:t,all:!0,skipPrompts:e})),g.info("Setup complete!")}function Ze(t,e,o=new Set){for(let r of t){let n=xa(r);if(n&&go(n)&&yo(n).isDirectory()){let s=Ln(n).map(i=>Qe(n,i));Ze(s,e,o);continue}if(n.endsWith(".yaml")){if(!go(n)||!yo(n).isFile())throw new Error(`File not found or unreadable: ${n}`);if(!e(n))continue;o.add(n)}}return o}async function Rt({envNames:t,client:e,all:o,skipPrompts:r}){let n=await e.getAllEnvironments();t&&!o&&(n=n.filter(a=>t?.includes(a.name))),Mn(q)||La(q);let s=0,i=0;for(let a of n){let l=Pn(q,`${a.name}.yaml`);if(!Mn(l))Nn(l,jt(a)),s++;else{let c=On("sha256").update(wo(l)).digest("hex"),d=jt(a),u=On("sha256").update(d).digest("hex");if(c!==u){if(!r&&!await J(`Environment ${a.name} already exists on disk but needs to be updated. Overwrite?`)){g.error("Pull cancelled");return}Nn(l,d),i++}}}g.info(`Pulled ${s} new environments and updated ${i} existing environments`)}function Ma(t){return t.endsWith(".yaml")?wo(t,"utf8").includes("momentic/environment")?!0:(g.warn(`Skipping YAML that is not a Momentic environment: ${t}`),!1):!1}async function Dn({client:t,names:e,all:o,yes:r}){let n;o?n=[q]:n=e.map(a=>Pn(q,`${a.toLowerCase()}.yaml`));let s=Ze(n,Ma);g.info(`Found ${s.size} environments(s) to push:`),s.forEach(a=>g.info(` - ${a}`)),g.info("Loading file contents");let i=[];for(let a of s){let l=Oa(wo(a,"utf-8"));try{let c=W.parse(l);i.push(c)}catch(c){g.error({err:c},`${a} failed to parse as a valid environment file`),process.exit(1)}}if(!r&&!await J("Pushing environments can overwrite variables on cloud and instantly affect scheduled runs. Continue?",!0)){g.error("Push cancelled");return}g.info(`Pushing ${s.size} environment(s)`),await t.updateEnvironments(i),g.info("Push successful!")}import{registry as bo}from"playwright-core/lib/server";function Na(t){let e=[],o=[];for(let r of t){let n=bo.findExecutable(r);!n||n.installType==="none"?e.push(r):o.push(n)}return o}async function _n(){let t=Na(["chromium"]);await bo.installDeps(t,!1),await bo.install(t,!1)}import{Argument as xt,Option as xe}from"commander";var de=new xe("--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),me=new xe("--server <server>","Momentic server to use. Leave unchanged unless using Momentic on-premise.").default("https://api.momentic.ai"),ue=new xe("-y, --yes","Skip confirmation prompts.").env("CI").default(!1),kn=new xe("--no-report","Skip reporting test results to Momentic Cloud when running with the --local flag.").default(!0),Fn=new xe("--env <env>","Name of the environment to use when running tests."),vo=new xe("-a --all","Select all tests in your organization from Momentic Cloud. Cannot be used together with <tests> arguments.").default(!1).preset(!0),Eo=new xe("-a --all","Select all environments in your organization from Momentic Cloud. Cannot be used together with <paths> arguments.").default(!1).preset(!0),zn=new xt("<tests...>",`One or more test paths to pull from Momentic Cloud.
|
|
34
|
+
`)}`}async executeCommand(e,o,r=!1){let n=this.commandHistory[this.commandHistory.length-1];if(!r&&(!n||n.state!=="PENDING"))throw new T("InternalWebAgentError","Executing command but there is no pending entry in the history");if(this.closed)throw new Error("Attempting to execute command on a closed controller");let s=Date.now(),i=await this.executePresetStep(e,o),a=Date.now()-s;return this.logger.debug({result:i,duration:a},"Got execution result"),i.succeedImmediately&&!r&&(this.pendingInstructions.pop(),this.pendingInstructions.length>0&&(i.succeedImmediately=!1)),i.elementInteracted&&"target"in e&&e.target&&!e.target.elementDescriptor&&(e.target.elementDescriptor=i.elementInteracted.trim()),r||(n.generatedStep=e,n.serializedCommand=Ne(e),n.state="DONE"),i}async executeAssertion(e){let o=await this.getBrowserState(),r=await this.browser.url(),n;if(e.useVision)n={goal:e.assertion,url:r,screenshot:await this.browser.screenshot(),browserState:"",history:"",numPrevious:-1,lastCommand:null};else{let i=this.getSerializedHistory(r,o);n={goal:e.assertion,url:r,browserState:o,history:i,lastCommand:this.lastExecutedCommand,numPrevious:this.commandHistory.length}}let s=await this.generator.getAssertionResult(n,e.useVision,e.disableCache);if(s.relevantElements&&Promise.all(s.relevantElements.map(i=>this.browser.highlight({id:i}))),!s.result)throw new T("AssertionFailureError",s.thoughts);return{succeedImmediately:!1,thoughts:s.thoughts,urlAfterCommand:r}}async wrapElementTargetingCommand(e,o,r,n,s=1){if(!e.a11yData&&!e.elementDescriptor)throw new T("ActionFailureError","Cannot target element with no target data or element descriptor");let i=e.a11yData&&tt(e.a11yData);e.a11yData||(this.logger.info("No cached locator data for target, prompting AI for fresh location"),s--,e.a11yData=et.parse(await this.locateElement(e.elementDescriptor,o,r)));try{let a=await n(e.a11yData);return i?this.logger.info({target:e},"Successfully used cached target to perform action"):this.logger.debug({target:e},"Successfully generated and used new a11y target information"),a}catch(a){if(s>0&&e.elementDescriptor)return this.logger.warn({err:a,target:e},"Failed to execute action with cached target, retrying with AI"),e.a11yData=void 0,this.wrapElementTargetingCommand(e,o,r,n,s);if(a instanceof T)throw a;let l=`Failed to find ${e.elementDescriptor?`${e.elementDescriptor}`:"element"}: ${a instanceof Error?a.message:a}`;throw this.logger.error({err:a,target:e},l),new T("ActionFailureError",l,{cause:a})}}async executePresetStep(e,o){try{return await this.executePresetStepHelper(e,o)}catch(r){throw r instanceof T?r:new T("InternalWebAgentError",r instanceof Error?r.message:`${r}`,{cause:r})}}async executePresetStepHelper(e,o){switch(e.type){case"SUCCESS":return e.condition?.assertion.trim()?this.executeAssertion(e.condition):{succeedImmediately:!1,urlAfterCommand:await this.browser.url()};case"AI_ASSERTION":return this.executeAssertion(e);case"AI_WAIT":{let l=Date.now(),c=(e.timeout??10)*1e3,d,u,h=0;for(;Date.now()-l<c;)try{d=await this.executeAssertion({...e,type:"AI_ASSERTION"});break}catch(m){u=m instanceof Error?m:new Error(`${m}`),this.logger.warn({err:m},`AI_WAIT assert attempt ${h} failed, retrying...`),h++}if(!d){let m=`AI wait still failing after ${c}ms.`;throw u&&(m+=` Latest result: ${u.message}`),new T("AssertionFailureError",m)}return d}case"NAVIGATE":if(!Yt(e.url)&&!Xt(e.url,this.browser.baseURL))throw new T("ActionFailureError",`Invalid URL: ${e.url}`);await this.browser.navigate({url:e.url});break;case"CAPTCHA":let r=await this.browser.solveCaptcha();r&&(await this.wrapElementTargetingCommand({elementDescriptor:"the captcha image solution input"},e.useVision,o,l=>this.browser.click(l)),await this.browser.type(r,{clearContent:!0,pressKeysSequentially:!1}));break;case"GO_BACK":await this.browser.goBack();break;case"GO_FORWARD":await this.browser.goForward();break;case"SCROLL_LEFT":case"SCROLL_RIGHT":case"SCROLL_DOWN":case"SCROLL_UP":let n;switch(e.target&&(e.target.elementDescriptor.trim()||e.target.a11yData)&&(n=await this.wrapElementTargetingCommand(e.target,e.useVision,o,l=>this.browser.hover(l))),e.type){case"SCROLL_UP":await this.browser.scrollUp(e.deltaY);break;case"SCROLL_DOWN":await this.browser.scrollDown(e.deltaY);break;case"SCROLL_LEFT":await this.browser.scrollLeft(e.deltaX);break;case"SCROLL_RIGHT":await this.browser.scrollRight(e.deltaX);break}return{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:n};case"WAIT":await this.browser.wait(e.delay*1e3);break;case"REFRESH":await this.browser.refresh();break;case"CLICK":{let l=await this.browser.url(),c=await this.wrapElementTargetingCommand(e.target,e.useVision,o,u=>this.browser.click(u,{doubleClick:e.doubleClick,rightClick:e.rightClick,force:e.force})),d={urlAfterCommand:await this.browser.url(),succeedImmediately:!1,elementInteracted:c};return Ee(l,d.urlAfterCommand)&&(d.succeedImmediately=!0,d.succeedImmediatelyReason="URL changed"),d}case"SELECT_OPTION":{let l=await this.wrapElementTargetingCommand(e.target,!1,o,c=>this.browser.selectOption(c,e.option));return{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:l}}case"TAB":await this.browser.switchToPage(e.url);break;case"COOKIE":if(!e.value)break;await this.browser.setCookie(e.value);break;case"LOCAL_STORAGE":if(!e.value||!e.key)break;await this.browser.setLocalStorage(e.key,e.value);break;case"JAVASCRIPT":{let l;try{l=await Xr({code:e.code,fragment:!!e.fragment,timeoutMs:e.timeout?e.timeout*1e3:void 0,logger:this.logger})}catch(c){throw new T("ActionFailureError",c instanceof Error?c.message:`${c}`,{cause:c})}try{l=JSON.parse(JSON.stringify(l))}catch(c){throw new T("ActionFailureError",`Return value is not serializable: ${c instanceof Error?c.message:`${c}`}`,{cause:c})}return{urlAfterCommand:await this.browser.url(),succeedImmediately:!1,data:{result:l}}}case"TYPE":{let l=await this.browser.url(),c;e.target&&(c=await this.wrapElementTargetingCommand(e.target,e.useVision,o,u=>this.browser.click(u,{force:e.force}))),await this.browser.type(e.value,{clearContent:e.clearContent,pressKeysSequentially:e.pressKeysSequentially}),e.pressEnter&&await this.browser.press("Enter");let d={urlAfterCommand:await this.browser.url(),succeedImmediately:!1,elementInteracted:c};return Ee(l,d.urlAfterCommand)&&(d.succeedImmediately=!0,d.succeedImmediatelyReason="URL changed"),d}case"HOVER":{let l=await this.wrapElementTargetingCommand(e.target,e.useVision,o,c=>this.browser.hover(c));return{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:l}}case"PRESS":let s=await this.browser.url();await this.browser.press(e.value);let i={urlAfterCommand:await this.browser.url(),succeedImmediately:!1};return Ee(s,i.urlAfterCommand)&&(i.succeedImmediately=!0,i.succeedImmediatelyReason="URL changed"),i;case"REQUEST":{let l=e.timeout??30,c=null,d=new AbortController,u=Object.fromEntries(Object.entries(e.headers||{}).filter(([M,v])=>M&&v)),h=new URLSearchParams;Object.entries(e.params||{}).filter(([M,v])=>M&&v).forEach(([M,v])=>{h.append(M,v)});let m;if(Yt(e.url)&&(m=e.url),Xt(e.url,this.browser.baseURL)&&(m=new URL(e.url,this.browser.baseURL).toString()),!m)throw new T("ActionFailureError",`Invalid URL: ${e.url}`);let f=async()=>{try{c=await fetch(`${m}?${h.toString()}`,{headers:u,method:e.method,body:e.body,signal:d.signal})}catch(M){this.logger.error({err:M},"Failed to make fetch request")}},w=async()=>{await new Promise(M=>setTimeout(M,l*1e3)),d.abort()};await Promise.race([w(),f()]);let y=c;if(!y)throw new T("ActionFailureError",`Fetch request timed out after ${l} seconds`);if(!y.ok)throw new T("ActionFailureError",`Fetch request failed with status ${y.status}`);let E={};y.headers.forEach((M,v)=>{E[v]=M});let b={status:y.status,headers:E};return y.headers.get("content-type")?.includes("json")?b.json=await y.json():y.headers.get("content-type")?.includes("text")&&(b.text=await y.text()),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),data:b}}default:return(l=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(e)}return{succeedImmediately:!1,urlAfterCommand:await this.browser.url()}}async getReverseMappedTarget(e,o,r){return(await this.generator.getReverseMappedDescription({browserState:e.browserState,goal:`${o}`},r)).phrase}async toggleRecordMode({action:e,indices:o,onStepRecord:r}){if(e==="STOP"){this.recordAbortController?.abort();return}if(!o||!r)throw new Error("Indices or step recording callback not provided to toggleRecordMode");this.recordAbortController=new AbortController;let n=new Et({signal:this.recordAbortController.signal,initialIndices:o,generator:this.generator,onStepRecord:r});await this.browser.startRecording(this.recordAbortController.signal,n)}};async function Jr({socket:t,generator:e,logger:o,rootController:r,localApp:n}){let s=o.child({package:"web-agent"}),i=!0;n==="iframe"&&(i=!1);let a=t.handshake.query.testId,l=t.handshake.query.baseUrl;if(!a)throw new Error("Socket connection is missing testID");let c=t.handshake.query.orgId,d=ji(),u=f=>{t.emit("screenshot",{...f,url:l})},h=r;if(h)h.browser.setActiveFrame(dt),h.browser.baseURL=l,await h.browser.reset({clearCookies:!0,clearStorage:!0});else{let f=await O.init({baseUrl:l,logger:s,takeScreenshots:i,onScreenshot:u});h=new se({browser:f,generator:e,config:Te,logger:s})}i&&To(t,d,o),F.registerSession(h,d);let m=O.USER_AGENT;return t.emit("session",{url:l,userAgent:m,viewport:await h.browser.viewport(),localApp:n}),{sessionId:d,testId:a,orgId:c}}var Qr=[Gr,Ro,Vr,jr,Wr];var Zr=t=>{let{logger:e}=t,o=new Wi(t.baseServer,{cors:{origin:"*",methods:["GET","POST"]}});return o.on("connection",async r=>{let n;try{n=await Jr({...t,socket:r})}catch(s){e.error({event:"connection",type:"websocket",err:s},"Failed to setup connection"),r.emit("error",{message:`${s}`});return}Qr.forEach(s=>Vi(s,{socket:r,metadata:n,...t}))}),o},Vi=(t,e)=>{let o=t.createHandler(e),r=(...n)=>{e.logger.debug({event:t.event,metadata:e.metadata,args:n},"Websocket event");let s=i=>{e.logger.error({event:t.event,type:"websocket",args:n,err:i instanceof Error?i:new Error(`${i}`)},"Unhandled exception in socket handler"),e.socket.emit("error",{message:`${i}`})};try{let i=o.apply(void 0,n);i&&typeof i.catch=="function"&&i.catch(s)}catch(i){s(i)}};e.socket.on(t.event,r)};import Ki from"fetch-retry";var qi=Ki(global.fetch),Re="v1",xe=class{baseURL;apiKey;constructor(e){this.baseURL=e.baseURL,this.apiKey=e.apiKey}async getElementLocation(e,o){let r=await this.sendRequest(`/${Re}/web-agent/locate-element`,{browserState:e.browserState,goal:e.goal,disableCache:o});return Ht.parse(r)}async getElementLocationWithVision(e,o){let r=await this.sendRequest(`/${Re}/web-agent/visual-locate`,{goal:e.goal,screenshot:e.screenshot?.toString("base64"),hintActivatedScreenshot:e.hintActivatedScreenshot?.toString("base64"),disableCache:o});return Ht.parse(r)}async getAssertionResult(e,o,r){if(o){let s=await this.sendRequest(`/${Re}/web-agent/assertion`,{url:e.url,goal:e.goal,screenshot:e.screenshot?.toString("base64"),disableCache:r,vision:!0});return $t.parse(s)}let n=await this.sendRequest(`/${Re}/web-agent/assertion`,{url:e.url,browserState:e.browserState,goal:e.goal,history:e.history,numPrevious:e.numPrevious,lastCommand:e.lastCommand,disableCache:r,vision:!1});return $t.parse(n)}async getProposedCommand(e,o){let r=await this.sendRequest(`/${Re}/web-agent/next-command`,{url:e.url,browserState:e.browserState,goal:e.goal,history:e.history,numPrevious:e.numPrevious,lastCommand:e.lastCommand,disableCache:o});return Ko.parse(r)}async getGranularGoals(e,o){let r=await this.sendRequest(`/${Re}/web-agent/split-goal`,{url:e.url,goal:e.goal,disableCache:o});return qo.parse(r)}async getReverseMappedDescription(e,o){let r=await this.sendRequest(`/${Re}/web-agent/reverse-mapped-description`,{goal:e.goal,browserState:e.browserState,disableCache:o});return Yo.parse(r)}async sendRequest(e,o){let r=await qi(`${this.baseURL}${e}`,{retries:1,retryDelay:1e3,method:"POST",body:JSON.stringify(o),headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}});if(!r.ok)throw new Error(`Request to ${e} failed with status ${r.status}: ${await r.text()}`);return r.json()}};import{existsSync as Yi,readFileSync as Xi,writeFileSync as io}from"fs";import ao from"path";import{parse as Ji,stringify as Qi}from"yaml";var k=process.env.MOMENTIC_DIR??de;function en(t,e){let o=sr(t);for(let[s,i]of Object.entries(o.modules)){let a=We(s);io(ao.join(k,oe,`${a}.yaml`),i,"utf-8")}let r=We(e),n=ao.join(k,`${r}.yaml`);return io(n,o.test,"utf-8"),`${r}.yaml`}function tn(t,e){if(!Yi(e))throw new Error(`Test not found at path: ${ao}`);let o=Xi(e,"utf-8"),n={...Ji(o),...t},s=Qi(n);io(e,s,"utf-8")}import{readFileSync as At,readdirSync as on,writeFileSync as rn}from"fs";import{join as lo}from"path";import{v4 as Zi}from"uuid";import{parse as Ct,stringify as nn}from"yaml";var qe=lo(k,oe);function sn(t,e){let o=gt(qe,e).path,r=At(o,"utf-8"),s={...Ct(r),...t},i=nn(s);rn(o,i,"utf-8")}function an(t,e){let o=We(t),r=lo(qe,`${o}.yaml`),n={fileType:"momentic/module",schemaVersion:z,moduleId:Zi(),name:t,steps:e},s=nn(n);return rn(r,s,"utf-8"),{type:"RESOLVED_MODULE",moduleId:n.moduleId,name:n.name,steps:n.steps}}function ln(){let t=[];for(let e in on(qe)){if(!e.endsWith(".yaml"))continue;let o=At(e,"utf-8"),r=Ct(o),n={name:r.name,moduleId:r.moduleId,numSteps:r.steps.length};t.push(n)}return t}function cn(){let t=[];for(let e of on(qe)){if(!e.endsWith(".yaml"))continue;let o=lo(qe,e),r=At(o,"utf-8"),n=Ct(r);try{let s=Gt.parse(n);t.push(s)}catch(s){g.warn({err:s},"Error parsing module, skipping...")}}return t}function dn(t){let e=gt(oe,t).path,o=At(e,"utf-8"),r=Ct(o);return Gt.parse(r)}import{Router as na}from"express";import{existsSync as ea,readFileSync as ta,readdirSync as oa}from"fs";import{join as mn}from"path";import{parse as ra}from"yaml";var co=mn(k,ct);function un(){let t=[];if(!ea(co))return[];for(let e of oa(co)){if(!e.endsWith(".yaml"))continue;let o=mn(co,e),r=ta(o,"utf-8"),n=ra(r);try{let s=V.parse(n);t.push(s)}catch(s){g.warn({err:s},"Error parsing environment, skipping...")}}return t}function G(t){return function(...e){let o=e[e.length-1],r=t(...e);Promise.resolve(r).catch(o)}}var pn=na();pn.get("/",G((t,e)=>{let o=un();e.status(200).json(o)}));var hn=pn;import{Router as sa}from"express";var Ye=sa();Ye.get("/",G((t,e)=>{let r=cn().map(n=>({...n,type:"RESOLVED_MODULE"}));e.status(200).json(r)}));Ye.get("/metadata",G((t,e)=>{let o=ln();e.status(200).json(o)}));Ye.post("/",G((t,e)=>{let o;try{o=jo.parse(t.body)}catch(n){e.status(400).json({error:`Invalid request body: ${n}`});return}try{je(o.name)}catch(n){e.status(400).json({error:`Invalid module name: ${n}`});return}let r=an(o.name,o.steps);e.status(201).json(r)}));Ye.get("/:moduleId",G((t,e)=>{if(!t.params.moduleId){e.status(400).json({error:"Missing moduleId in url path."});return}let o=dn(t.params.moduleId);e.json(o)}));var fn=Ye;import{Router as ia}from"express";import{existsSync as aa}from"fs";import{join as mo}from"path";import{v4 as la}from"uuid";var Xe=ia();Xe.get("/",G((t,e)=>{let o=Ve(k).filter(r=>r.localOnly);e.status(200).json(o)}));Xe.post("/",G((t,e)=>{let o;try{o=Go.parse(t.body)}catch(a){e.status(400).json({error:`Invalid request body: ${a}`});return}try{je(o.name)}catch(a){e.status(400).json({error:a.message});return}let n={id:la(),name:o.name,baseUrl:o.baseUrl,schemaVersion:z,advanced:{disableAICaching:!1,availableAsModule:!0},retries:0,steps:[],localOnly:!0};o.environment&&(n.envs=[{name:o.environment,defaultOnLocal:!0}]);let s=en(n,o.name),i={...n,testPath:s};e.status(201).json(i)}));Xe.get("/:testPath",G(async(t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let o=mo(k,t.params.testPath);if(!aa(o)){e.status(400).json({error:`Test not found at path: ${o}`});return}let r=await yt(o,mo(k,oe));e.status(200).json(r)}));Xe.patch("/:testPath",G((t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let o;try{o=Bo.parse(t.body)}catch(s){e.status(400).json({error:`Invalid request body: ${s}`});return}let r=[],n={};for(let s of o.steps){if(s.type!=="RESOLVED_MODULE"){r.push(s);continue}r.push({type:"MODULE",moduleId:s.moduleId}),n[s.moduleId]=s.steps}for(let[s,i]of Object.entries(n))sn({steps:i},s);tn({steps:r},mo(k,t.params.testPath)),e.status(201).json({message:"ok"})}));var gn=Xe;var uo="https://api.momentic.ai",Sn=t=>{uo=t},wn=()=>uo,ca,bn=t=>{ca=t};var yn,vn=async t=>{if(yn)return;let e=await fetch(`${uo}/v1/auth/check`,{headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`}});if(!e.ok)throw new Error(`Error checking API key with server (code ${e.status}): ${await e.text()}`);let o=await e.json(),{orgId:r}=rr.parse(o);yn=r};import En,{multistream as da}from"pino";import ma from"pino-pretty";var po=new Map,ua=process.env.NODE_ENV==="production",ho=class t{consoleLogger;ddClientToken;hostname;bindingAttributes;site="https://http-intake.logs.us5.datadoghq.com/api/v2/logs";constructor({bindings:e,clientToken:o,hostname:r}){this.ddClientToken=o,this.hostname=r,this.bindingAttributes={...e,env:process.env.NODE_ENV};let n={base:this.bindingAttributes,errorKey:"err",level:"debug"};this.consoleLogger=ua?En(n):En(n,da([{stream:ma({colorize:!0})}]))}child(e){return new t({clientToken:this.ddClientToken,bindings:{...this.bindingAttributes,...e,hostname:this.hostname}})}flush(e){this.consoleLogger.flush(e)}log(e,o,r,...n){this.consoleLogger[e](o,r,...n);let s=Object.assign({},this.bindingAttributes,o&&typeof o=="object"?o:{});n.length>0&&(s.args=n),(async()=>{try{let i=await fetch(this.site,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json","DD-API-KEY":this.ddClientToken},body:JSON.stringify([{ddsource:this.bindingAttributes.app,ddtags:Object.keys(s).map(a=>`${a}:${s[a]}`).join(","),hostname:this.hostname??"vercel",message:r||"",service:"momentic"}])});if(!i.ok)throw new Error(`Failed to log to DD: ${i.statusText})}`)}catch(i){this.consoleLogger.warn({obj:o,msg:r,args:n,err:i},"Failed to log to DD")}})()}debug(e,o,...r){this.log("debug",e,o,...r)}info(e,o,...r){this.log("info",e,o,...r)}warn(e,o,...r){this.log("warn",e,o,...r)}error(e,o,...r){this.log("error",e,o,...r)}bindings(){return this.bindingAttributes}},An=({app:t,clientToken:e,hostname:o})=>{if(!process.env.DD_CLIENT_TOKEN&&!process.env.NEXT_PUBLIC_DD_CLIENT_TOKEN&&!e)throw new Error("Missing DD_CLIENT_TOKEN");return po.has(t)||po.set(t,new ho({bindings:{app:t},hostname:o,clientToken:e||process.env.NEXT_PUBLIC_DD_CLIENT_TOKEN||process.env.DD_CLIENT_TOKEN})),po.get(t)};import{hostname as pa}from"os";var X=An({app:"desktop-server",clientToken:"pubcfd7516a5c0ba852b42675cd97bee027",hostname:pa()});async function Tn({momenticServerUrl:t,apiKey:e,mode:o,serverPort:r,appPort:n,staticDir:s,assetsDir:i}){if(!ga(k)||!ya(k).isDirectory()){let m=ba.isAbsolute(k);throw new Error(`Root folder ${k} does not exist${m?"":` in the current directory ("${process.cwd()}")`}. Please run \`npx momentic init\` if you wish to setup Momentic here for the first time.`)}t&&Sn(t),g.info("Checking API key"),await vn(e);let l=`http://localhost:${r}`;n&&(l=`http://localhost:${n}`);let c=Ea(s);await new Promise(m=>{c.listen(r,()=>{g.info(`Server is running at http://localhost:${r}`),m()})});let u=new xe({baseURL:wn(),apiKey:e});if(o==="web"){await Cn("web",c,u,X),await wa(l);return}let h=await Aa({appUrl:l,apiGenerator:u,extensionPath:va(i,"extension"),onClose:async()=>{g.info("Browser closed, closing app."),await h.browser.cleanup(),c.close(()=>{process.exit(0)})}});await Cn("iframe",c,u,X,h),process.on("uncaughtException",m=>{X.error({err:m},"Uncaught exception in desktop-server")}),process.on("unhandledRejection",m=>{X.error({err:m},"Unhandled rejection in desktop-server")})}function Ea(t){let e=fo();e.use(fa()),e.use(ha.json({limit:"50mb"}));let o=fo.Router();if(o.use("/tests",gn),o.use("/modules",fn),o.use("/environments",hn),e.use("/api",o),e.use((n,s,i)=>{n.path!=="/healthcheck"&&X.debug({url:n.url,path:n.path,query:n.query,method:n.method,body:n.body,headers:n.rawHeaders,client:n.ip},"Received desktop-server request"),s.on("close",()=>{s.statusCode>=400&&X.error({url:n.url,method:n.method,statusCode:s.statusCode},"Request completed in error")}),i()}),e.use((n,s,i,a)=>{X.error({stack:n.stack,msg:n.message,url:s.url,method:s.method},"Unhandled exception leading to 500 on desktop-server"),i.status(500).send("Internal Server Error"),a(n)}),t){let n=fo.static(t);e.use("/",n),e.use("*",n)}return Sa.createServer(e)}async function Aa({appUrl:t,apiGenerator:e,extensionPath:o,onClose:r}){let n=await O.init({baseUrl:t,logger:X,browserArgs:{headless:!1,handleSIGTERM:!0},contextArgs:{bypassCSP:!0,viewport:null,deviceScaleFactor:void 0},localMode:!0,localAppUrl:t,extensionPath:o,onClose:r}),s=new se({browser:n,config:Te,generator:e,logger:X});return bn(s),s}async function Cn(t,e,o,r,n){Zr({baseServer:e,generator:o,logger:r,localApp:t,rootController:n})}import{existsSync as tl}from"fs";import Jn,{dirname as ol}from"path";import{fileURLToPath as rl}from"url";var Rn="0.0.31";var K="v1",J=class{baseURL;apiKey;constructor(e){this.baseURL=e.baseURL,this.apiKey=e.apiKey}async getRun(e){let o=await this.sendRequest(`/${K}/runs/${e}`,{method:"GET"});return tr.parse(o)}async createRun(e){let o=await this.sendRequest(`/${K}/runs`,{method:"POST",body:e});return er.parse(o)}async updateRun(e,o){await this.sendRequest(`/${K}/runs/${e}`,{method:"PATCH",body:o})}async getTest(e){let o=await this.sendRequest(`/${K}/tests/${e}`,{method:"GET"});return Jo.parse(o)}async getAllTestIds(){let e=await this.sendRequest(`/${K}/tests`,{method:"GET"});return Qo.parse(e)}async getTestYAMLExport(e){let o=await this.sendRequest(`/${K}/tests/export`,{method:"POST",body:e});return Zo.parse(o)}async updateTestWithYAML(e){await this.sendRequest(`/${K}/tests/update`,{method:"POST",body:e})}async queueTests(e){let o=await this.sendRequest(`/${K}/tests/queue`,{method:"POST",body:e});return Xo.parse(o)}async uploadScreenshot(e){let o=await this.sendRequest(`/${K}/screenshots`,{method:"POST",body:e});return or.parse(o)}async getAllEnvironments(){let e=await this.sendRequest(`/${K}/environments`,{method:"GET"});return nr.parse(e)}async updateEnvironments(e){await this.sendRequest(`/${K}/environments`,{method:"POST",body:e})}async sendRequest(e,o){let r=await fetch(`${this.baseURL}${e}`,{method:o.method,body:o.body?JSON.stringify(o.body):void 0,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}});if(!r.ok)throw new Error(`Request to ${e} failed with status ${r.status}: ${await r.text()}`);return r.status===204?r.text():r.json()}};import{createHash as Ln}from"crypto";import{existsSync as On,mkdirSync as Oa,readFileSync as wo,writeFileSync as Mn}from"fs";import{join as Nn}from"path";import{parse as Ma}from"yaml";import{existsSync as go,mkdirSync as Je,readdirSync as In,statSync as yo}from"fs";import{homedir as xa}from"os";import{join as Qe,resolve as Ia}from"path";import Ta from"chalk";import Ra from"readline/promises";async function Q(t,e){if(process.env.CI)return!0;let o=Ra.createInterface({input:process.stdin,output:process.stdout});t=`${t} (y/N) `;let r=e?Ta.bold.yellow(t):t,n=await o.question(r);return o.close(),n.toLowerCase()==="y"}var Tt=de,Ie=Qe(de,oe),So=Qe(de,Ho),q=Qe(de,ct),La=Qe(xa(),"momentic","chromium");function xn(t){return go(t)&&yo(t).isDirectory()}async function me(t,e=!1){xn(Tt)||(!e&&!await Q(`A '${de}' folder was not found in the current directory. Setup the required Momentic folder structure?`)&&(g.error("Setup cancelled"),process.exit(1)),Je(Tt),Je(Ie),Je(So),Je(q),Je(La,{recursive:!0})),(!xn(q)||In(q).length===0)&&(!e&&!await Q("You do not appear to have any environments configured locally. Pull environments from Momentic Cloud?")&&(g.error("Setup cancelled"),process.exit(1)),await Rt({client:t,all:!0,skipPrompts:e})),g.info("Setup complete!")}function Ze(t,e,o=new Set){for(let r of t){let n=Ia(r);if(n&&go(n)&&yo(n).isDirectory()){let s=In(n).map(i=>Qe(n,i));Ze(s,e,o);continue}if(n.endsWith(".yaml")){if(!go(n)||!yo(n).isFile())throw new Error(`File not found or unreadable: ${n}`);if(!e(n))continue;o.add(n)}}return o}async function Rt({envNames:t,client:e,all:o,skipPrompts:r}){let n=await e.getAllEnvironments();t&&!o&&(n=n.filter(a=>t?.includes(a.name))),On(q)||Oa(q);let s=0,i=0;for(let a of n){let l=Nn(q,`${a.name}.yaml`);if(!On(l))Mn(l,jt(a)),s++;else{let c=Ln("sha256").update(wo(l)).digest("hex"),d=jt(a),u=Ln("sha256").update(d).digest("hex");if(c!==u){if(!r&&!await Q(`Environment ${a.name} already exists on disk but needs to be updated. Overwrite?`)){g.error("Pull cancelled");return}Mn(l,d),i++}}}g.info(`Pulled ${s} new environments and updated ${i} existing environments`)}function Na(t){return t.endsWith(".yaml")?wo(t,"utf8").includes("momentic/environment")?!0:(g.warn(`Skipping YAML that is not a Momentic environment: ${t}`),!1):!1}async function Pn({client:t,names:e,all:o,yes:r}){let n;o?n=[q]:n=e.map(a=>Nn(q,`${a.toLowerCase()}.yaml`));let s=Ze(n,Na);g.info(`Found ${s.size} environments(s) to push:`),s.forEach(a=>g.info(` - ${a}`)),g.info("Loading file contents");let i=[];for(let a of s){let l=Ma(wo(a,"utf-8"));try{let c=V.parse(l);i.push(c)}catch(c){g.error({err:c},`${a} failed to parse as a valid environment file`),process.exit(1)}}if(!r&&!await Q("Pushing environments can overwrite variables on cloud and instantly affect scheduled runs. Continue?",!0)){g.error("Push cancelled");return}g.info(`Pushing ${s.size} environment(s)`),await t.updateEnvironments(i),g.info("Push successful!")}import{registry as bo}from"playwright-core/lib/server";function Pa(t){let e=[],o=[];for(let r of t){let n=bo.findExecutable(r);!n||n.installType==="none"?e.push(r):o.push(n)}return o}async function Dn(){let t=Pa(["chromium"]);await bo.installDeps(t,!1),await bo.install(t,!1)}import{Argument as xt,Option as Le}from"commander";var ue=new Le("--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),pe=new Le("--server <server>","Momentic server to use. Leave unchanged unless using Momentic on-premise.").default("https://api.momentic.ai"),he=new Le("-y, --yes","Skip confirmation prompts.").env("CI").default(!1),_n=new Le("--no-report","Skip reporting test results to Momentic Cloud when running with the --local flag.").default(!0),kn=new Le("--env <env>","Name of the environment to use when running tests."),vo=new Le("-a --all","Select all tests in your organization from Momentic Cloud. Cannot be used together with <tests> arguments.").default(!1).preset(!0),Eo=new Le("-a --all","Select all environments in your organization from Momentic Cloud. Cannot be used together with <paths> arguments.").default(!1).preset(!0),Fn=new xt("<tests...>",`One or more test paths to pull from Momentic Cloud.
|
|
35
35
|
|
|
36
|
-
A test path is a lowercased version of your test name where spaces are replaced with underscores: 'npx momentic pull hello-world'.`).argOptional(),
|
|
36
|
+
A test path is a lowercased version of your test name where spaces are replaced with underscores: 'npx momentic pull hello-world'.`).argOptional(),zn=new xt("<tests...>",`One or more test identifiers.
|
|
37
37
|
|
|
38
38
|
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'.
|
|
39
39
|
|
|
40
|
-
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()
|
|
41
|
-
`))}catch(n){throw new Error(`Fixture at path ${e} does not parse as valid YAML: ${n}`)}let r;try{r=
|
|
42
|
-
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));await
|
|
40
|
+
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(),Un=new xt("<paths...>","File paths pointing to one or more Momentic YAML files. Paths to directories are supported as well."),Ao=new xt("<envs...>","One or more environment names to push").argOptional();import{existsSync as _a,writeFileSync as $n}from"fs";import{join as Hn}from"path";async function jn({testsToFetch:t,client:e,all:o}){let{tests:r,modules:n}=await e.getTestYAMLExport({paths:t,all:o}),s=Object.keys(r).length;for(let[a,l]of Object.entries(r)){let c=Hn(Tt,Gn(a));if(!await Bn(c)){g.error("Pull cancelled");return}$n(c,l,"utf-8"),g.info(`Wrote '${c}'`)}let i=Object.keys(n).length;for(let[a,l]of Object.entries(n)){let c=Hn(Ie,Gn(a));if(!await Bn(c)){g.error("Pull cancelled");return}$n(c,l,"utf-8"),g.info(`Wrote '${c}'`)}g.info(`Pulled ${s} test${s>1?"s":""}${i?` and ${i} module${i>1?"s":""}`:""}!`)}async function Bn(t){return _a(t)?Q(`File '${t.replace(/(\s+)/g,"\\$1")}' already exists. Overwrite existing content?`,!0):!0}function Gn(t){return`${t.toLowerCase().replaceAll(" ","-")}.yaml`}import{existsSync as vy,readFileSync as ka}from"fs";async function Wn(t){let e=Ze(t.paths,Fa);g.info(`Found ${e.size} test(s) to push:`),e.forEach(n=>g.info(` - ${n}`)),g.info("Loading file contents and resolving dependent modules");let o=Array.from(e).map(n=>Kt(n,Ie)),r=new Set(o.flatMap(n=>Object.keys(n.modules)));if(g.info(`Resolved ${e.size} test(s) and ${r.size} module(s)`),!t.yes&&!await Q("Pushing tests overwrites tests on production and will instantly affect scheduled runs. Continue?",!0)){g.error("Push cancelled");return}await t.client.updateTestWithYAML(o),g.info("Update successful!")}function Fa(t){if(!t.endsWith(".yaml"))return!1;let e=ka(t,"utf8");return e.includes("momentic/test")?e.match(/localOnly:.*true/)?(g.warn(`Skipping local-only test: ${t}`),!1):!0:(g.warn(`Skipping YAML that is not a Momentic test: ${t}`),!1)}import{existsSync as qn,readFileSync as qa,statSync as Ya}from"fs";import{join as Xa}from"path";import Ja from"wait-on";import{parse as Qa}from"yaml";import za from"@actions/exec";import Ua from"@actions/io";import $a from"quote";import Ha from"string-argv";async function Vn(t,e=!0){let o=Ha(t),r=await Ua.which(o[0],!0),n=o.slice(1),s=za.exec($a(r),n,{delay:100});if(e)return s}import{v4 as Ka}from"uuid";import{existsSync as Ba,readFileSync as Ga}from"fs";import{join as ja}from"path";import{parse as Wa}from"yaml";function Va(t){let e=ja(So,`${t}.yaml`);if(!Ba(e))throw new Error(`Fixture '${t}' does not exist at expected path ${e}`);let o;try{o=Wa(Ga(e,"utf-8").replace(/\r\n|\r/g,`
|
|
41
|
+
`))}catch(n){throw new Error(`Fixture at path ${e} does not parse as valid YAML: ${n}`)}let r;try{r=Fo.parse(o)}catch(n){throw new Error(`Fixture at path ${e} does not conform to expected schema: ${n}`)}return r}async function Co(t,e){let o=Va(t),r=e==="setup"?o.setup:o.teardown;if(!r){g.warn(`Fixture '${t}' does not have any steps in the ${e} phase, skipping...`);return}let n=r.timeout,s=async function(){for(let c=0;c<r.steps.length;c++){let d=r.steps[c],u=d.run,h=async m=>{try{await gr(`set -ex; ${u}`)}catch(f){if(m){let w=`Fixture '${t}' ${e} step ${c+1} errored in the background. The test will continue, but this may affect test execution and results: ${f}`;g.error(w)}else throw new Error(`Fixture '${t}' ${e} step ${c+1} errored: ${f}`)}};d.waitForCompletion===!1?h(!0):await h(!1)}};if(g.info(`Running ${e} phase of fixture '${t}'`),!n){await s();return}let i,a,l=async function(){return new Promise((c,d)=>{a=c,i=setTimeout(()=>{a=void 0,d(`Fixture '${t}' ${e} phase timed out after ${n} seconds`)},n*1e3)})};try{await Promise.race([s(),l()])}catch(c){throw c}finally{a&&a(),clearTimeout(i)}}async function Kn({path:t,apiClient:e,generator:o,useLocalFiles:r,noReport:n,environment:s,newBaseUrl:i}){let a;r?(g.info(`Reading ${t} from local filesystem`),a=await yt(t,Ie)):(g.info(`Fetching ${t} from Momentic Cloud server (${e.baseURL})`),a=await e.getTest(t)),a.schemaVersion>z&&g.warn(`Test ${t} has schema version ${a.schemaVersion}, which is greater than what is currently supported by this SDK. Please update your momentic package version to avoid unexpected behavior.`);let l=[],c={};s&&(l=a.envs?.find(E=>E.name===s.name)?.fixtures??[],c=s.variables);for(let y of l)await Co(y,"setup");let d;if(a.baseUrl){if(d=a.baseUrl,i){let y=new URL(a.baseUrl),E=new URL(i);y.hostname=E.hostname,y.protocol=E.protocol,y.port=E.port,d=y.toString()}}else d=c[at],d||(g.error(`Test ${a.name} has no baseURL and the environment has no ${at} variable either`),process.exit(1));let u=await O.init({baseUrl:d,logger:g}),h=new se({browser:u,generator:o,config:Te,logger:g}),m,f;if(n)f=Ka();else try{m=await e.createRun({testId:a.id,trigger:"CLI"}),f=m.id}catch(y){throw g.error(y),new Error(`Are you sure test ${a.name} exists on the server?`)}let w="FAILED";try{w=await $r({test:a,runId:f,controller:h,logger:g,takeScreenshots:!0,onSaveScreenshot:async y=>{if(n)return"";let{key:E}=await e.uploadScreenshot({screenshot:y.toString("base64")});return E},onUpdateRun:async y=>{n||await e.updateRun(f,y)}})}catch(y){throw n||await e.updateRun(f,{status:"FAILED",finishedAt:new Date}),n||g.error(`Test '${a.name}' failed. You can view a playback of this run at ${e.baseURL}/runs/${f}`),y}finally{try{for(let y of l)await Co(y,"teardown")}catch(y){g.error(`Failed to run teardown fixtures: ${y}`)}}return w}async function Yn({tests:t,start:e,waitOn:o,waitOnTimeout:r,client:n,all:s,noReport:i,env:a,parallelization:l=1}){e&&(g.info(`Running start command: ${e}`),await Vn(e,!1)),o&&(g.info(`Waiting for ${o} to be accessible (timeout: ${r}s)`),await Ja({resources:[o],timeout:r*1e3}));let c;if(a){let b=Xa(q,`${a}.yaml`),M=Qa(qa(b,"utf-8"));try{c=V.parse(M),g.info(`Running in the ${a} environment with the following variables:`),g.info(c.variables)}catch(v){g.error({err:v},`${b} failed to parse as a valid environment file`),process.exit(1)}}let d=new xe({baseURL:n.baseURL,apiKey:n.apiKey}),u=!1;(t.some(b=>b.endsWith(".yaml"))||t.some(b=>qn(b)))&&(u=!0);let h=new Set;if(u)g.info(t,"Reading tests from the following local file paths:"),t.forEach(b=>{if(!qn(b))throw new Error(`Path '${b}' does not exist.`);if(Ya(b).isDirectory())Ve(b).map(v=>v.fullFilePath).forEach(v=>h.add(v));else if(b.endsWith(".yaml"))h.add(b);else throw new Error(`Path '${b}' is not a directory or a .yaml file.`)});else if(g.warn("The paths you specified are not files or directories that exist locally."),g.warn(`Fetching tests from Momentic Cloud (${n.baseURL}) instead...`),s)for(let b of await n.getAllTestIds())h.add(b);else h=new Set(t);let m=Array.from(h);g.info(m,`Identified ${m.length} tests to run locally:`);let f=[];for(let b=0;b<m.length;b+=l){let M=await Promise.all(m.slice(b,b+l).map(async v=>{let C="FAILED";try{C=await Kn({useLocalFiles:u,path:v,apiClient:n,generator:d,newBaseUrl:o,environment:c,noReport:i})}catch(j){let R=j instanceof Error?j.message:`${j}`;g.error(`Test ${v} failed with error: ${R}`)}return{runStatus:C,path:v}}));f=f.concat(M)}let w=f.filter(b=>b.runStatus==="PASSED");w.length>0&&(g.info(`Passed ${w.length} out of ${f.length} tests:`),w.forEach(b=>{g.info(`- ${b.path}`)}));let y=f.filter(b=>b.runStatus==="CANCELLED");y.length>0&&(g.warn(`Cancelled ${y.length} out of ${f.length} tests:`),y.forEach(b=>{g.warn(`- ${b.path}`)}));let E=f.filter(b=>b.runStatus==="FAILED");E.length>0&&(g.error(`Failed ${E.length} out of ${f.length} tests:`),E.forEach(b=>{g.error(`- ${b.path}`)}),process.exit(1)),process.exit(0)}async function Xn({tests:t,client:e,env:o,all:r}){let{queuedTests:n}=await e.queueTests({testPaths:t,env:o,all:r});g.info(`Successfully queued ${n.length} tests!`)}var Z=new Za;Z.name("momentic").description("Momentic CLI").version(Rn);Z.command("install-browsers").action(async()=>{await Dn()});Z.addOption(new ie("--log-level <level>").choices(["debug","info","warn","error"]).default("info")).on("option:log-level",t=>{g.setMinLevel(Vo[t.toUpperCase()])});Z.command("init").addOption(he).addOption(ue).addOption(pe).action(async t=>{let{yes:e,apiKey:o,server:r}=t,n=new J({baseURL:r,apiKey:o});await me(n,e)});Z.command("app").addOption(ue).addOption(pe).addOption(he).addOption(new ie("--port <port>","Port to run the app on. Defaults to 58888.").default(58888)).addOption(new ie("--mode <mode>","Mode to run the app in. Defaults to iframe.").choices(["iframe","web"]).default("iframe")).action(async t=>{let{apiKey:e,port:o,yes:r,server:n,mode:s}=t,i=new J({baseURL:n,apiKey:e});await me(i,r);let a=rl(import.meta.url),l=ol(a),c=Jn.resolve(l,"..","static"),d=Jn.resolve(l,"..","assets");await Tn({momenticServerUrl:n,mode:s,apiKey:e,serverPort:o,appPort:o,staticDir:c,assetsDir:d})});Z.command("run").alias("run-tests").addOption(ue).addOption(pe).addOption(vo).addOption(he).addOption(new ie("-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(_n).addOption(new ie("-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 ie("--start <command>","Arbitrary setup command that will run before Momentic steps begin.")).addOption(new ie("--wait-on <url>","URL to wait to become accessible before Momentic tests begin.")).addOption(kn).addOption(new ie("--wait-on-timeout <timeout>","Max time to wait for the --wait-on URL to become accessible.").default(60,"one minute")).addOption(new ie("-p, --parallel <parallelization>","When running with the --local flag, the number of tests to run in parallel. Defaults to 1.").default(1)).addArgument(zn).action(async(t,e)=>{let{apiKey:o,server:r,remote:n,local:s,all:i,env:a,yes:l}=e,c=new J({baseURL:r,apiKey:o});if(await me(c,l),s){try{await Yn({tests:t,client:c,...e})}catch(d){g.error(d),process.exit(1)}return}if(n){for(let d of t)(d.endsWith(".yaml")||tl(d))&&(g.error(el`'${d}' looks like a local file or directory, but the --local flag was not supplied.
|
|
42
|
+
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));await Xn({tests:t,client:c,all:i,env:a});return}g.error("One of --remote or --local must be specified"),process.exit(1)});Z.command("pull").description("Fetch one or more tests from Momentic Cloud and save it in to local disk in YAML format.").addOption(ue).addOption(pe).addOption(vo).addOption(he).addArgument(Fn).action(async(t,e)=>{let{apiKey:o,server:r,all:n,yes:s}=e,i=new J({baseURL:r,apiKey:o});if(await me(i,s),!n&&!t?.length)throw new Error("At least one test name or --all must be provided");await jn({testsToFetch:t,client:i,all:n})});Z.command("push").description("Save one or more tests in YAML format to Momentic Cloud.").addOption(he).addOption(ue).addOption(pe).addArgument(Un).action(async(t,e)=>{let{apiKey:o,server:r,yes:n}=e;await me(o,n);let s=new J({baseURL:r,apiKey:o});await Wn({paths:t,client:s,yes:n})});var Qn=Z.command("env").description("Perform operations on Momentic environments");Qn.command("push").description("Push one or more environments and associated variables to Momentic cloud.").addOption(he).addOption(ue).addOption(pe).addOption(Eo).addArgument(Ao).action(async(t,e)=>{let{apiKey:o,server:r,yes:n,all:s}=e;!t?.length&&!s&&(g.error("At least one environment name or --all must be provided"),process.exit(1)),await me(o,n);let i=new J({baseURL:r,apiKey:o});await Pn({names:t,client:i,yes:n,all:s})});Qn.command("pull").description("Pull one or more environments and associated variables from Momentic cloud.").addOption(he).addOption(ue).addOption(pe).addOption(Eo).addArgument(Ao).action(async(t,e)=>{let{apiKey:o,server:r,yes:n,all:s}=e;!t?.length&&!s&&(g.error("At least one environment name or --all must be provided"),process.exit(1)),await me(o,n);let i=new J({baseURL:r,apiKey:o});await Rt({envNames:t,client:i,skipPrompts:n,all:s})});async function nl(){await Z.parseAsync(process.argv)}nl();
|