momentic 0.0.22 → 0.0.24

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 CHANGED
@@ -1,33 +1,33 @@
1
1
  #!/usr/bin/env node
2
- import{Command as Mi,Option as ee}from"commander";import Di from"dedent";import Qs from"body-parser";import Zs from"cors";import Bt from"express";import{existsSync as ei,statSync as ti}from"fs";import oi from"http";import ri from"path";import{Server as Us}from"socket.io";import{v4 as rn}from"uuid";var ht=class{controllers=new Map;registerSession(e,o=rn()){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)}},k=new ht;function Yt(t,e,o){let r=async()=>{let s=k.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(k.getController(e)?.browser.closed){clearInterval(n);return}try{await r()}catch(i){clearInterval(n),o.error(i,"Error taking screenshot")}})()},1e3);return n}var nn=({metadata:t,rootController:e,logger:o})=>{let{sessionId:r}=t,n=async()=>{e?await e.resetState({clearCookies:!0,clearStorage:!0}):await k.removeSession(r)};return()=>{n().catch(s=>{o.error({err:s},"Error during disconnect cleanup.")})}},Xt={event:"disconnect",createHandler:nn};import ot,{readFileSync as Fo,readdirSync as Yn}from"fs";import we from"path";import Kn from"diff-lines";import tt from"semver";import*as F from"zod";var Ge=F.object({id:F.number().int(),role:F.string().optional(),name:F.string().optional(),numChildren:F.number().optional(),content:F.string().optional(),pathFromRoot:F.string().optional(),serializedForm:F.string().optional()});function je(t){return t.name||t.role||t.content||t.serializedForm}import{z as Ce}from"zod";var Jt=Ce.object({thoughts:Ce.string(),result:Ce.boolean(),relevantElements:Ce.array(Ce.number()).optional()});import ca from"string-argv";import{z as te}from"zod";import sn from"dedent";import*as h from"zod";var B=(b=>(b.AI_ASSERTION="AI_ASSERTION",b.CLICK="CLICK",b.SELECT_OPTION="SELECT_OPTION",b.REQUEST="REQUEST",b.TYPE="TYPE",b.PRESS="PRESS",b.NAVIGATE="NAVIGATE",b.SCROLL_UP="SCROLL_UP",b.SCROLL_DOWN="SCROLL_DOWN",b.GO_BACK="GO_BACK",b.GO_FORWARD="GO_FORWARD",b.WAIT="WAIT",b.REFRESH="REFRESH",b.TAB="TAB",b.COOKIE="COOKIE",b.LOCAL_STORAGE="LOCAL_STORAGE",b.HOVER="HOVER",b.CAPTCHA="CAPTCHA",b.SUCCESS="SUCCESS",b))(B||{}),ge=h.object({elementDescriptor:h.string(),a11yData:Ge.optional()}),M=h.object({thoughts:h.string().optional()}),an=M.merge(h.object({type:h.literal("NAVIGATE"),url:h.string()})).describe("NAVIGATE <URL> - Go to the specified URL. Only navigate to URLs relevant to the user goal."),ln=M.merge(h.object({target:ge.optional(),type:h.literal("SCROLL_UP"),useVision:h.boolean().default(!1),deltaY:h.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."),cn=M.merge(h.object({target:ge.optional(),type:h.literal("SCROLL_DOWN"),useVision:h.boolean().default(!1),deltaY:h.number().optional()})).describe("SCROLL_DOWN [id] - Scroll down while hovering over the element with the specified id. If no id is provided, scroll the entire page."),dn=M.merge(h.object({type:h.literal("WAIT"),delay:h.number()})),mn=M.merge(h.object({type:h.literal("REFRESH")})),un=M.merge(h.object({type:h.literal("GO_BACK")})),pn=M.merge(h.object({type:h.literal("GO_FORWARD")})),hn=M.merge(h.object({type:h.literal("CAPTCHA"),useVision:h.boolean().default(!1)})),fn=M.merge(h.object({type:h.literal("CLICK"),target:ge,doubleClick:h.boolean().default(!1),rightClick:h.boolean().default(!1),useVision:h.boolean().default(!1),force:h.boolean().optional()})).describe(sn`CLICK <id> - click on the element that has the specified id.
2
+ import{Command as zi,Option as J}from"commander";import Ui from"dedent";import oi from"body-parser";import ri from"cors";import Gt from"express";import{existsSync as ni,statSync as si}from"fs";import ii from"http";import ai from"open";import li from"path";import{Server as Bs}from"socket.io";import{v4 as ln}from"uuid";var gt=class{controllers=new Map;registerSession(e,o=ln()){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)}},k=new gt;function Qt(t,e,o){let r=async()=>{let s=k.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(k.getController(e)?.browser.closed){clearInterval(n);return}try{await r()}catch(i){clearInterval(n),o.error(i,"Error taking screenshot")}})()},1e3);return n}var cn=({metadata:t,rootController:e,logger:o})=>{let{sessionId:r}=t,n=async()=>{e?await e.resetState({clearCookies:!0,clearStorage:!0}):await k.removeSession(r)};return()=>{n().catch(s=>{o.error({err:s},"Error during disconnect cleanup.")})}},Zt={event:"disconnect",createHandler:cn};import ot,{readFileSync as Bo,readdirSync as Zn}from"fs";import we from"path";import Jn from"diff-lines";import tt from"semver";import*as F from"zod";var je=F.object({id:F.number().int(),role:F.string().optional(),name:F.string().optional(),numChildren:F.number().optional(),content:F.string().optional(),pathFromRoot:F.string().optional(),serializedForm:F.string().optional()});function Ge(t){return t.name||t.role||t.content||t.serializedForm}import{z as Ce}from"zod";var eo=Ce.object({thoughts:Ce.string(),result:Ce.boolean(),relevantElements:Ce.array(Ce.number()).optional()});import ha from"string-argv";import{z as oe}from"zod";import dn from"dedent";import*as f from"zod";var B=(b=>(b.AI_ASSERTION="AI_ASSERTION",b.CLICK="CLICK",b.SELECT_OPTION="SELECT_OPTION",b.REQUEST="REQUEST",b.TYPE="TYPE",b.PRESS="PRESS",b.NAVIGATE="NAVIGATE",b.SCROLL_UP="SCROLL_UP",b.SCROLL_DOWN="SCROLL_DOWN",b.GO_BACK="GO_BACK",b.GO_FORWARD="GO_FORWARD",b.WAIT="WAIT",b.REFRESH="REFRESH",b.TAB="TAB",b.COOKIE="COOKIE",b.LOCAL_STORAGE="LOCAL_STORAGE",b.HOVER="HOVER",b.CAPTCHA="CAPTCHA",b.SUCCESS="SUCCESS",b))(B||{}),ge=f.object({elementDescriptor:f.string(),a11yData:je.optional()}),D=f.object({thoughts:f.string().optional()}),mn=D.merge(f.object({type:f.literal("NAVIGATE"),url:f.string()})).describe("NAVIGATE <URL> - Go to the specified URL. Only navigate to URLs relevant to the user goal."),un=D.merge(f.object({target:ge.optional(),type:f.literal("SCROLL_UP"),useVision:f.boolean().default(!1),deltaY:f.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."),pn=D.merge(f.object({target:ge.optional(),type:f.literal("SCROLL_DOWN"),useVision:f.boolean().default(!1),deltaY:f.number().optional()})).describe("SCROLL_DOWN [id] - Scroll down while hovering over the element with the specified id. If no id is provided, scroll the entire page."),hn=D.merge(f.object({type:f.literal("WAIT"),delay:f.number()})),fn=D.merge(f.object({type:f.literal("REFRESH")})),gn=D.merge(f.object({type:f.literal("GO_BACK")})),yn=D.merge(f.object({type:f.literal("GO_FORWARD")})),Sn=D.merge(f.object({type:f.literal("CAPTCHA"),useVision:f.boolean().default(!1)})),wn=D.merge(f.object({type:f.literal("CLICK"),target:ge,doubleClick:f.boolean().default(!1),rightClick:f.boolean().default(!1),useVision:f.boolean().default(!1),force:f.boolean().optional()})).describe(dn`CLICK <id> - click on the element that has the specified id.
3
3
  You are NOT allowed to click on disabled, hidden or StaticText elements.
4
4
  Only click on elements on the Current Page.
5
5
  Only click on elements with the following tag names: button, input, link, image, generic.
6
6
  `.replaceAll(`
7
- `," ")),gn=M.merge(h.object({type:h.literal("HOVER"),target:ge,useVision:h.boolean().default(!1)})),yn=M.merge(h.object({type:h.literal("SELECT_OPTION"),target:ge,option:h.string()})).describe('SELECT_OPTION <id> "<option>" - select an option from a combobox, listbox, or menu element on the page. Provide the id of the parent combobox, listbox, or menu element in <id>. Do NOT provide the id of the option: instead, provide the name of the option in <option> enclosed by single quotes.'),Qt=M.merge(h.object({type:h.literal("AI_ASSERTION"),assertion:h.string(),useVision:h.boolean().default(!1),disableCache:h.boolean().default(!1),cancelOnFailure:h.boolean().default(!1)})),Sn=h.object({clearContent:h.boolean().default(!0),pressKeysSequentially:h.boolean().default(!1)}),wn=M.merge(h.object({type:h.literal("TYPE"),target:ge,value:h.string(),pressEnter:h.boolean().default(!1),useVision:h.boolean().default(!1),force:h.boolean().optional()})).merge(Sn).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.'),bn=M.merge(h.object({type:h.literal("PRESS"),value:h.string()})).describe('PRESS <key> - press the specified key, such as "ArrowLeft", "Enter", or "a". You must specify at least one key.'),An=M.merge(h.object({type:h.literal("TAB"),url:h.string()})),En=M.merge(h.object({type:h.literal("COOKIE"),value:h.string()})),vn=M.merge(h.object({type:h.literal("LOCAL_STORAGE"),key:h.string(),value:h.string()})),Cn=M.merge(h.object({type:h.literal("REQUEST"),url:h.string(),method:h.union([h.literal("GET"),h.literal("POST"),h.literal("PUT"),h.literal("DELETE"),h.literal("PATCH")]),headers:h.record(h.string(),h.string()).optional(),params:h.record(h.string(),h.string()).optional(),body:h.string().optional(),timeout:h.number().optional().describe("Max seconds to wait for the request to complete")})),Tn=M.merge(h.object({type:h.literal("SUCCESS"),condition:Qt.optional()})).describe("SUCCESS - the user goal has been successfully achieved"),ye=h.discriminatedUnion("type",[fn,wn,bn,yn,an,cn,ln,Tn]),Rn=h.discriminatedUnion("type",[un,pn,mn,Qt,dn,An,En,vn,gn,Cn,hn]),Zt=h.discriminatedUnion("type",[...ye.options,...Rn.options]),xn=M.merge(h.object({type:h.literal("FAILURE")})).describe("FAILURE - there are no commands to suggest that could make progress that have not already been tried before"),ft=h.discriminatedUnion("type",[...ye.options,xn]);var Te=(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))(Te||{});var Re=class extends Error{constructor(e,o={}){super(e,o),this.name="BrowserExecutionError"}};var Ve=class extends Error{constructor(e={}){super("Got empty a11y tree",e),this.name="EmptyA11yTreeError"}};var R=class extends Error{reason;constructor(e,o,r={}){let n=!1;for(let s of Object.values(Te))if(o.startsWith(s)){n=!0,e=s;break}n?super(o,r):super(`${e}: ${o}`,r),this.name="TestFailureError",this.stack=this.stack?.slice(this.name.length+2),this.reason=e}toString(){return this.message}toJSON(){return{message:this.message}}};var pa=te.object({command:te.string(),thoughts:te.string()}),ha=te.string().pipe(te.coerce.number());var eo=te.object({phrase:te.string()});import*as O from"zod";var z="1.0.6",P=(r=>(r.AI_ACTION="AI_ACTION",r.PRESET_ACTION="PRESET_ACTION",r.MODULE="MODULE",r))(P||{}),xe=O.object({type:O.literal("AI_ACTION"),text:O.string(),commands:O.array(ye).optional()}),Ie=O.object({type:O.literal("PRESET_ACTION"),command:Zt}),gt=O.object({type:O.literal("MODULE"),moduleId:O.string().uuid()}),oe=O.union([xe,Ie]),Le=O.object({type:O.literal("RESOLVED_MODULE"),moduleId:O.string().uuid(),name:O.string(),steps:oe.array()}),re=O.union([xe,Ie,gt]),Oe=O.union([xe,Ie,Le]);var Aa=new Set(Object.values(B));var yt={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",SUCCESS:"Done"},Ea={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.",SUCCESS:"Indicate the entire AI action has succeeded, optionally based on a condition."};import*as A from"zod";var ne=(s=>(s.SUCCESS="SUCCESS",s.FAILED="FAILED",s.RUNNING="RUNNING",s.IDLE="IDLE",s.CANCELLED="CANCELLED",s))(ne||{}),We=(r=>(r.SUCCESS="SUCCESS",r.FAILED="FAILED",r.CANCELLED="CANCELLED",r))(We||{}),In=A.object({beforeUrl:A.string(),beforeScreenshot:A.string().or(A.any()),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(We),message:A.string().optional(),elementInteracted:A.string().optional()}),Ke=A.object({startedAt:A.coerce.date(),finishedAt:A.coerce.date(),status:A.nativeEnum(ne),message:A.string().optional(),data:A.record(A.string(),A.unknown()).optional(),userAgent:A.string().optional()}),St=Ie.merge(Ke).merge(A.object({results:In.array()})),to=xe.merge(Ke).merge(A.object({results:St.array()})),Ln=gt.merge(Ke).merge(A.object({results:A.union([to,St]).array()})),qe=A.discriminatedUnion("type",[to,St,Ln]),Ta=Ke.pick({startedAt:!0,finishedAt:!0,status:!0,message:!0,data:!0});function On(t,e){return t.length<e?t:t.slice(0,e-3)+"[...]"}function Se(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: ${On(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?.serializedForm?o=`in element: ${t.target.a11yData.serializedForm}`: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"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 $ from"zod";import*as se from"zod";var oo=se.object({type:se.nativeEnum(P),generatedStep:ye.optional(),serializedCommand:se.string().optional(),elementInteracted:se.string().optional()});var Q=$.object({goal:$.string(),url:$.string(),browserState:$.string(),history:$.string(),numPrevious:$.number(),lastCommand:oo.or($.null())});import{parseString as Mn}from"set-cookie-parser";function ro(t){let e=Mn(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 U}from"zod";var Dn="1.0.0",no=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")}),io=U.object({type:U.literal("momentic/fixture"),schemaVersion:U.string(),name:U.string(),description:U.string().optional(),setup:U.object({steps:no.array(),timeout:U.number().optional().describe("Timeout for all steps in seconds")}).optional(),teardown:U.object({steps:no.array(),timeout:U.number().optional().describe("Timeout for all steps in seconds")}).optional()}),Fa={type:"momentic/fixture",schemaVersion:Dn,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 Nn}from"zod";var Ba=Nn.string().array();import{z as I}from"zod";import{z as D}from"zod";import{z as Ye}from"zod";var ao=Ye.object({name:Ye.string(),fixtures:Ye.array(Ye.string().describe("Name of the fixture (must be available locally in the fixtures directory).")).optional()});import{isValidCron as Pn}from"cron-validator";import{z as H}from"zod";var Z=H.object({availableAsModule:H.boolean().default(!1),disableAICaching:H.boolean().default(!1)}),lo=H.object({cron:H.string().refine(t=>Pn(t),{message:"Invalid cron expression."}).default("0 0 */1 * *"),enabled:H.boolean().default(!1),timeZone:H.string().default("America/Los_Angeles"),jobKey:H.string().optional()}),co=H.object({onSuccess:H.boolean().default(!1),onFailure:H.boolean().default(!0)});var _n=D.string().min(1).max(255).superRefine((t,e)=>{try{De(t)}catch(o){return e.addIssue({code:D.ZodIssueCode.custom,message:o.message,fatal:!0}),D.NEVER}}),j=D.object({id:D.string(),name:_n,baseUrl:D.string(),schemaVersion:D.string(),advanced:Z,retries:D.number(),envSettings:ao.array().optional(),localOnly:D.boolean().optional()}),Za=j.pick({name:!0,baseUrl:!0,retries:!0,advanced:!0}),kn=D.object({createdAt:D.coerce.date(),updatedAt:D.coerce.date(),schedule:lo,notification:co,createdBy:D.string(),organizationId:D.string()}),mo=j.merge(kn).merge(D.object({steps:D.array(Oe)})),Xe=j.merge(D.object({steps:D.array(Oe)})),el=j.merge(D.object({steps:re.array()})),Me=t=>t.toLowerCase().replace(/ /g,"-");var zn=/^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/,Un=["modules","fixtures"];function De(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(Un.includes(t))throw new Error("'modules' is a reserved folder name in Momentic. Please choose a different name.");if(t.match(zn))throw new Error("Name cannot be a UUID. Please choose a different name.")}var ie="momentic",V="modules",uo="fixtures",Fn="momentic-frame",Je=`${Fn}-0`,sl=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 po=I.object({steps:Oe.array()}),ho=I.object({name:I.string(),baseUrl:I.string().url()}),il=Xe.merge(I.object({testPath:I.string()})),fo=I.object({name:I.string(),steps:oe.array()});var al=I.array(Le),ll=I.array(I.object({name:I.string(),moduleId:I.string().uuid(),numSteps:I.number()}));import*as W from"zod";var go=W.object({thoughts:W.string(),id:W.number().int(),options:W.array(W.string()).optional()});var yo={DEBUG:0,INFO:1,WARN:2,ERROR:3},$n={0:"DEBUG",1:"INFO",2:"WARN",3:"ERROR"},Hn={0:"\x1B[90m",1:"\x1B[32m",2:"\x1B[33m",3:"\x1B[31m"},wt=class t{minLogLevel;logBindings;constructor(e,o){this.minLogLevel=e,this.logBindings=o}log(e,...o){let r=$n[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=Hn[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 d=l;l instanceof Error?d=l.message:typeof l=="object"&&(d=JSON.stringify(l,void 0,2),d=d.split(`
8
- `).map((c,u)=>u>0?` ${c}`:c).join(`
9
- `)),console.log(e===0?`${s} ${a}:`:` ${a}:`,d)}else if(n)for(let a of n){let l=a;typeof a=="object"&&(l=JSON.stringify(a,void 0,2),l=l.split(`
10
- `).map((d,c)=>c>0?` ${d}`:d).join(`
11
- `)),console.log(e===0?`${s} `:" ",l)}e===0&&process.stdout.write("\x1B[39m")}setMinLevel(e){this.minLogLevel=e}info(...e){1<this.minLogLevel||this.log(1,...e)}debug(...e){0<this.minLogLevel||this.log(0,...e)}warn(...e){2<this.minLogLevel||this.log(2,...e)}error(...e){3<this.minLogLevel||this.log(3,...e)}child(e){return new t(this.minLogLevel,{...this.logBindings,...e})}flush(){}bindings(){return this.logBindings}},y=new wt(1,{});import{z as K}from"zod";var Bn=K.object({id:K.string(),createdAt:K.coerce.date(),createdBy:K.string(),organizationId:K.string(),name:K.string(),schemaVersion:K.string(),numSteps:K.number()}),hl=K.object({steps:oe.array()}).merge(Bn.omit({numSteps:!0}));import*as S from"zod";import{z as C}from"zod";var bt={WEBHOOK:"WEBHOOK",CRON:"CRON",MANUAL:"MANUAL",CLI:"CLI"},At={PENDING:"PENDING",RUNNING:"RUNNING",PASSED:"PASSED",FAILED:"FAILED",CANCELLED:"CANCELLED"},Gn={PASSED:"PASSED",FAILED:"FAILED"},Qe=C.string().pipe(C.coerce.date()).or(C.date()),jn=C.object({id:C.string(),createdAt:Qe,createdBy:C.string(),organizationId:C.string(),scheduledAt:Qe.or(C.null()),startedAt:Qe.or(C.null()),finishedAt:Qe.or(C.null()),testId:C.string().or(C.null()),status:C.nativeEnum(At),expectedStatus:C.nativeEnum(Gn).or(C.null()),runKey:C.string(),trigger:C.nativeEnum(bt),attempts:C.number(),test:C.object({name:C.string(),id:C.string()}).or(C.null())}),Et=jn.merge(C.object({results:qe.array(),test:C.object({name:C.string(),id:C.string(),baseUrl:C.string()}).or(C.null())}));var ae=S.object({disableCache:S.boolean()}),xl=S.object({error:S.boolean(),reason:S.string(),message:S.string()}),Il=Q.merge(ae),So=ft,Ll=S.discriminatedUnion("vision",[Q.merge(ae).merge(S.object({vision:S.literal(!1)})),Q.pick({goal:!0,url:!0}).merge(ae).merge(S.object({screenshot:S.string(),vision:S.literal(!0)}))]),vt=Jt,Ol=Q.pick({browserState:!0,goal:!0}).merge(ae),Ml=Q.pick({goal:!0}).merge(ae).merge(S.object({screenshot:S.string().describe("base64 encoded image"),hintActivatedScreenshot:S.string().describe("base64 encoded image")})),Ct=go,Dl=Q.pick({goal:!0,url:!0}).merge(ae),wo=S.string().array(),Nl=Q.pick({goal:!0,browserState:!0}).merge(ae),bo=eo,Pl=S.object({testPaths:S.string().array().describe("can be either hyphenated, lowercase test names or UUIDs"),all:S.boolean().optional()}),Ao=S.object({message:S.string(),queuedTests:S.object({name:S.string(),id:S.string()}).array()}),Eo=mo,vo=S.string().array(),_l=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")})]),Co=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"))}),Vn=S.object({test:S.string().describe("test YAML"),modules:S.record(S.string().describe("moduleId"),S.string().describe("module YAML"))}),kl=Vn.array();var zl=S.object({testPath:S.string(),testId:S.string()}).partial().merge(S.object({trigger:S.nativeEnum(bt)})),To=Et,Ro=Et,Ul=S.object({startedAt:S.coerce.date(),finishedAt:S.coerce.date(),results:qe.array(),status:S.nativeEnum(At)}).partial(),Fl=S.object({screenshot:S.string()}),xo=S.object({key:S.string()}),Io=S.object({orgId:S.string()});function Ze(t){switch(t.type){case"AI_ACTION":return`AI action: ${t.text}`;case"PRESET_ACTION":return Se(t.command);case"RESOLVED_MODULE":return`Module: ${t.moduleId}`}}import{stringify as Lo}from"yaml";import{z as L}from"zod";var Zl=L.object({test:L.string().describe("YAML for the test, including metadata and steps"),modules:L.record(L.string(),L.string()).describe("Map of module name to YAML for the module")}),ec=j.merge(L.object({steps:re.array(),fileType:L.literal("momentic/test")})),Tt=Le.omit({type:!0}).merge(L.object({schemaVersion:L.string(),fileType:L.literal("momentic/module")})),tc=j.merge(L.object({steps:L.array(L.record(L.string(),L.unknown()))})),oc=L.object({moduleId:L.string().uuid(),name:L.string(),schemaVersion:L.string(),steps:L.array(L.record(L.string(),L.unknown()))});function Oo(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},r={},n=Mo(t.steps,t.schemaVersion,r,e),s={fileType:"momentic/test",...o,steps:n};return{test:Lo(s),modules:r}}function Mo(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]=Wn(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 Wn(t,e){let o={fileType:"momentic/module",moduleId:t.moduleId,name:t.name,steps:Mo(t.steps,e,{},new Set),schemaVersion:e};return Lo(o)}var Do={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 No={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 Po={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 et=new Set(["CLICK","TYPE","SELECT_OPTION"]),_o={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||et.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||et.has(l))&&(i.target={elementDescriptor:a??""})}),e.results&&Array.isArray(e.results)&&e.results.forEach(i=>{let a=i.command,l=a?.elementDescriptor,d=a?.type;(l!==void 0||et.has(d))&&(a.target={elementDescriptor:l??""}),i.commands&&Array.isArray(i.commands)&&i.commands.forEach(u=>{let m=u?.elementDescriptor,p=u?.type;(m!==void 0||et.has(p))&&(u.target={elementDescriptor:m??""})})}),e}),stopOnFailure:!0};var ko={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 zo={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(d=>{let c=d.type;c?.startsWith("PRESET_")&&(d.type=c.slice(7))})}),e}),stopOnFailure:!0};var le=[Po,ko,zo,_o,Do,No];if(z!==le[le.length-1].toVersion)throw new Error("Please bump LATEST_VERSION in types package after adding a migration");le.forEach((t,e)=>{if(!tt.valid(t.toVersion)||!tt.valid(t.fromVersion))throw new Error(`Migration '${t.name}' has invalid version`);if(!tt.gt(t.toVersion,t.fromVersion))throw new Error(`Migration '${t.name}' has toVersion <= fromVersion`);if(e===0)return;if(le[e-1].toVersion!==t.fromVersion)throw new Error(`Migration '${t.name}' at index ${e} is not contiguous with previous migration`)});function qn(t){return t.every(e=>e&&typeof e=="object"&&!Array.isArray(e))}var Rt=async({metadata:t,steps:e,logger:o})=>{let r=e,{schemaVersion:n,id:s}=t,i=le.findIndex(d=>tt.gt(d.toVersion,n));if(i===-1)return o.debug({id:s},"Step migrations up to date"),{steps:r,newVersion:n};let a=n;for(let d=i;d<le.length;d++){let c=le[d],u={id:s,migration:c.name,toVersion:c.toVersion};o.debug(u,"Starting migration");try{r=await Uo(r,c),a=c.toVersion}catch(m){throw o.error({err:m,...u},"Migration failed"),new Error(`Step migration ${c.name} failed: ${m}`)}}let l=Kn(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 Uo(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)||qn(s)&&(r[n]=await Uo(s,e))}return o}import{parse as xt}from"yaml";var Xn=new Set(["modules","fixtures","node_modules","dist","bin",".git","logs",".npm",".next","out",".yarn","__pycache__","build",".env",".venv","venv","env","wheels"]);function Ne(t,e=t){let o=t.split(we.sep).pop()??"";if(Xn.has(o))return o!=="modules"&&o!=="fixtures"&&y.warn(`Skipping directory '${t}' because it is likely an artifact folder.`),[];let r=ot.readdirSync(t),n=[];return r.forEach(s=>{let i=we.join(t,s);if(ot.statSync(i).isDirectory())n=n.concat(Ne(i,t));else if(s.endsWith(".yaml")){let a=ot.readFileSync(i,"utf-8"),l=ot.statSync(i),d=xt(a);try{let c=j.parse(d);n.push({id:c.id,name:c.name,fullFilePath:i,testPath:we.relative(we.resolve(e),we.resolve(i)),localOnly:c.localOnly,fileName:s,lastModified:l.mtime,createdAt:l.ctime})}catch{y.warn(`Skipping file '${i}' because it does not parse as a valid Momentic test.`)}}}),n}function It(t,e){let o;try{o=Fo(t,"utf8"),o=o.replace(/\r\n|\r/g,`
12
- `)}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]=rt(e,a).content)}),{test:o,modules:i}}function rt(t,e){let o=Yn(t);for(let r of o){let n=we.join(t,r),s=Fo(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 nt(t,e){let{test:o,modules:r}=It(t,e),n=xt(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){y.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 Rt({metadata:n,steps:n.steps,logger:y});s=re.array().parse(l)}else s=re.array().parse(n.steps);let i={};for(let[l,d]of Object.entries(r)){let c=xt(d);if(!c.schemaVersion||!c.moduleId)throw new Error(`Module ${l} is missing an ID or schema version`);if(!c.steps||!Array.isArray(c.steps))throw new Error(`Module ${l} is missing steps`);if(c.schemaVersion<z){y.warn(`Module ${l} has schema version ${c.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 Rt({metadata:{id:c.moduleId,schemaVersion:c.schemaVersion},steps:c.steps,logger:y});i[l]={...c,steps:oe.array().parse(u)}}else i[l]=c}let a=s.map(l=>{if(l.type!=="MODULE")return l;let d=i[l.moduleId];if(!d)throw new Error(`Could not resolve module ${l.moduleId} required in test ${t}`);return{type:"RESOLVED_MODULE",moduleId:l.moduleId,name:d.name,steps:d.steps}});return Xe.parse({...n,steps:a})}import{spawn as Jn}from"child_process";async function $o(t){return new Promise((e,o)=>{let r={stdio:"inherit",env:process.env,detached:!0},n=Jn("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 Ss}from"fastest-levenshtein";import{homedir as ws}from"os";import{join as bs}from"path";import{chromium as As,devices as lr}from"playwright";import{addExtra as Es}from"playwright-extra";import vs from"puppeteer-extra-plugin-recaptcha";import Cs from"puppeteer-extra-plugin-stealth";var Lt={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 ce=(t,e)=>{let{hostname:o,pathname:r}=new URL(t),{hostname:n,pathname:s}=new URL(e);return o!==n||r!==s},Ot=t=>{try{return new URL(t),!0}catch{return!1}},Mt=(t,e)=>{try{return new URL(t,e),!0}catch{return!1}};import{distance as Vo}from"fastest-levenshtein";var Ho=new Set(["about:blank","chrome-error://chromewebdata/"]),Bo=2;var Zn=["focusable","keyshortcuts","controls"],es=["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"],ts=["notRendered","notVisible","ariaHiddenElement","ariaHiddenSubtree"],os=80,rs={paragraph:"p",searchbox:"input"},Ko=["paragraph","StaticText"],Wo={indentLevel:0,noID:!1,noChildren:!1,noProperties:!1,maxLevel:void 0,neighbors:void 0},Dt=class{id;role;name;content;properties;dataMomenticId;pathFromRoot;parent;children;backendNodeID;constructor(e){this.id=e.id,this.role=e.role,this.name=e.name,this.content=e.content,this.properties={},this.pathFromRoot=e.pathFromRoot,this.children=e.children,this.backendNodeID=e.backendNodeID,e.properties&&e.properties.forEach(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 es.includes(this.role)||this.children.some(e=>e.role==="StaticText")?!0:!!this.name.trim()||!!this.content}serialize(e=Wo){let{indentLevel:o,noChildren:r,noProperties:n,noID:s}=Object.assign({},Wo,e),i=" ".repeat(o),a=rs[this.role]||this.role,l=this.name,d={...this.properties};a==="heading"&&(d.level&&(a=`h${d.level}`,delete d.level),l==="heading"&&(l=""));let c=!Ko.includes(this.role);if(this.role==="StaticText")return`${i}${l}
13
- `;let u=`${i}<${a}`;!s&&c&&(u+=` id="${this.id}"`),l&&(u+=` name="${l}"`),this.content&&(u+=` content="${this.content}"`),Object.keys(this.properties).length>0&&!n&&Object.entries(this.properties).forEach(([p,f])=>{Zn.includes(p)||(typeof f=="string"?u+=` ${p}="${f}"`:typeof f=="boolean"?f?u+=` ${p}`:u+=` ${p}={false}`:typeof f<"u"&&(u+=` ${p}={${JSON.stringify(f)}}`))});let m=e.maxLevel!==void 0&&o/2>=e.maxLevel;if(this.children.length===0||r||m)u+=` />
14
- `;else{let p="";for(let w of this.children)p+=w.serialize({...e,indentLevel:o+2});let f=p.trim();f.length<=os&&!f.includes(`
15
- `)?u+=`>${f}</${a}>
7
+ `," ")),bn=D.merge(f.object({type:f.literal("HOVER"),target:ge,useVision:f.boolean().default(!1)})),An=D.merge(f.object({type:f.literal("SELECT_OPTION"),target:ge,option:f.string()})).describe('SELECT_OPTION <id> "<option>" - select an option from a combobox, listbox, or menu element on the page. Provide the id of the parent combobox, listbox, or menu element in <id>. Do NOT provide the id of the option: instead, provide the name of the option in <option> enclosed by single quotes.'),to=D.merge(f.object({type:f.literal("AI_ASSERTION"),assertion:f.string(),useVision:f.boolean().default(!1),disableCache:f.boolean().default(!1),cancelOnFailure:f.boolean().default(!1)})),En=f.object({clearContent:f.boolean().default(!0),pressKeysSequentially:f.boolean().default(!1)}),vn=D.merge(f.object({type:f.literal("TYPE"),target:ge,value:f.string(),pressEnter:f.boolean().default(!1),useVision:f.boolean().default(!1),force:f.boolean().optional()})).merge(En).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.'),Cn=D.merge(f.object({type:f.literal("PRESS"),value:f.string()})).describe('PRESS <key> - press the specified key, such as "ArrowLeft", "Enter", or "a". You must specify at least one key.'),Tn=D.merge(f.object({type:f.literal("TAB"),url:f.string()})),Rn=D.merge(f.object({type:f.literal("COOKIE"),value:f.string()})),xn=D.merge(f.object({type:f.literal("LOCAL_STORAGE"),key:f.string(),value:f.string()})),In=D.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")})),Ln=D.merge(f.object({type:f.literal("SUCCESS"),condition:to.optional()})).describe("SUCCESS - the user goal has been successfully achieved"),ye=f.discriminatedUnion("type",[wn,vn,Cn,An,mn,pn,un,Ln]),On=f.discriminatedUnion("type",[gn,yn,fn,to,hn,Tn,Rn,xn,bn,In,Sn]),oo=f.discriminatedUnion("type",[...ye.options,...On.options]),Mn=D.merge(f.object({type:f.literal("FAILURE")})).describe("FAILURE - there are no commands to suggest that could make progress that have not already been tried before"),yt=f.discriminatedUnion("type",[...ye.options,Mn]);var Te=(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))(Te||{});var Re=class extends Error{constructor(e,o={}){super(e,o),this.name="BrowserExecutionError"}};var Ve=class extends Error{constructor(e={}){super("Got empty a11y tree",e),this.name="EmptyA11yTreeError"}};var x=class extends Error{reason;constructor(e,o,r={}){let n=!1;for(let s of Object.values(Te))if(o.startsWith(s)){n=!0,e=s;break}n?super(o,r):super(`${e}: ${o}`,r),this.name="TestFailureError",this.stack=this.stack?.slice(this.name.length+2),this.reason=e}toString(){return this.message}toJSON(){return{message:this.message}}};var Sa=oe.object({command:oe.string(),thoughts:oe.string()}),wa=oe.string().pipe(oe.coerce.number());var ro=oe.object({phrase:oe.string()});import*as M from"zod";var z="1.0.6",P=(r=>(r.AI_ACTION="AI_ACTION",r.PRESET_ACTION="PRESET_ACTION",r.MODULE="MODULE",r))(P||{}),xe=M.object({type:M.literal("AI_ACTION"),text:M.string(),commands:M.array(ye).optional()}),Ie=M.object({type:M.literal("PRESET_ACTION"),command:oo}),St=M.object({type:M.literal("MODULE"),moduleId:M.string().uuid()}),re=M.union([xe,Ie]),Le=M.object({type:M.literal("RESOLVED_MODULE"),moduleId:M.string().uuid(),name:M.string(),steps:re.array()}),ne=M.union([xe,Ie,St]),Oe=M.union([xe,Ie,Le]);var Ra=new Set(Object.values(B));var wt={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",SUCCESS:"Done"},xa={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.",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||{}),We=(r=>(r.SUCCESS="SUCCESS",r.FAILED="FAILED",r.CANCELLED="CANCELLED",r))(We||{}),Dn=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(We),message:A.string().optional(),elementInteracted:A.string().optional()}),Ke=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()}),bt=Ie.merge(Ke).merge(A.object({results:Dn.array()})),no=xe.merge(Ke).merge(A.object({results:bt.array()})),Nn=St.merge(Ke).merge(A.object({results:A.union([no,bt]).array()})),qe=A.discriminatedUnion("type",[no,bt,Nn]),Oa=Ke.pick({startedAt:!0,finishedAt:!0,status:!0,message:!0,data:!0});function Pn(t,e){return t.length<e?t:t.slice(0,e-3)+"[...]"}function Se(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: ${Pn(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?.serializedForm?o=`in element: ${t.target.a11yData.serializedForm}`: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"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 $ from"zod";import*as ie from"zod";var so=ie.object({type:ie.nativeEnum(P),generatedStep:ye.optional(),serializedCommand:ie.string().optional(),elementInteracted:ie.string().optional()});var Z=$.object({goal:$.string(),url:$.string(),browserState:$.string(),history:$.string(),numPrevious:$.number(),lastCommand:so.or($.null())});import{parseString as _n}from"set-cookie-parser";function io(t){let e=_n(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 U}from"zod";var kn="1.0.0",ao=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")}),co=U.object({type:U.literal("momentic/fixture"),schemaVersion:U.string(),name:U.string(),description:U.string().optional(),setup:U.object({steps:ao.array(),timeout:U.number().optional().describe("Timeout for all steps in seconds")}).optional(),teardown:U.object({steps:ao.array(),timeout:U.number().optional().describe("Timeout for all steps in seconds")}).optional()}),Ga={type:"momentic/fixture",schemaVersion:kn,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 zn}from"zod";var Ka=zn.string().array();import{z as L}from"zod";import{z as N}from"zod";import{z as Ye}from"zod";var mo=Ye.object({name:Ye.string(),fixtures:Ye.array(Ye.string().describe("Name of the fixture (must be available locally in the fixtures directory).")).optional()});import{isValidCron as Un}from"cron-validator";import{z as H}from"zod";var ee=H.object({availableAsModule:H.boolean().default(!1),disableAICaching:H.boolean().default(!1)}),uo=H.object({cron:H.string().refine(t=>Un(t),{message:"Invalid cron expression."}).default("0 0 */1 * *"),enabled:H.boolean().default(!1),timeZone:H.string().default("America/Los_Angeles"),jobKey:H.string().optional()}),po=H.object({onSuccess:H.boolean().default(!1),onFailure:H.boolean().default(!0)});var Fn=N.string().min(1).max(255).superRefine((t,e)=>{try{De(t)}catch(o){return e.addIssue({code:N.ZodIssueCode.custom,message:o.message,fatal:!0}),N.NEVER}}),G=N.object({id:N.string(),name:Fn,baseUrl:N.string(),schemaVersion:N.string(),advanced:ee,retries:N.number(),envSettings:mo.array().optional(),localOnly:N.boolean().optional()}),nl=G.pick({name:!0,baseUrl:!0,retries:!0,advanced:!0}),$n=N.object({createdAt:N.coerce.date(),updatedAt:N.coerce.date(),schedule:uo,notification:po,createdBy:N.string(),organizationId:N.string()}),ho=G.merge($n).merge(N.object({steps:N.array(Oe)})),Xe=G.merge(N.object({steps:N.array(Oe)})),sl=G.merge(N.object({steps:ne.array()})),Me=t=>t.toLowerCase().replace(/ /g,"-");var Hn=/^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/,Bn=["modules","fixtures"];function De(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(Bn.includes(t))throw new Error("'modules' is a reserved folder name in Momentic. Please choose a different name.");if(t.match(Hn))throw new Error("Name cannot be a UUID. Please choose a different name.")}var ae="momentic",V="modules",fo="fixtures",jn="momentic-frame",Je=`${jn}-0`,dl=L.array(L.object({id:L.string(),name:L.string(),fullFilePath:L.string(),testPath:L.string().describe("path relative to the root test directory, i.e. my-folder/my-test.yaml"),fileName:L.string(),lastModified:L.coerce.date(),createdAt:L.coerce.date()}));var go=L.object({steps:Oe.array()}),yo=L.object({name:L.string(),baseUrl:L.string().url()}),ml=Xe.merge(L.object({testPath:L.string()})),So=L.object({name:L.string(),steps:re.array()});var ul=L.array(Le),pl=L.array(L.object({name:L.string(),moduleId:L.string().uuid(),numSteps:L.number()}));import*as W from"zod";var wo=W.object({thoughts:W.string(),id:W.number().int(),options:W.array(W.string()).optional()});var bo={DEBUG:0,INFO:1,WARN:2,ERROR:3},Gn={0:"DEBUG",1:"INFO",2:"WARN",3:"ERROR"},Vn={0:"\x1B[90m",1:"\x1B[32m",2:"\x1B[33m",3:"\x1B[31m"},At=class t{minLogLevel;logBindings;constructor(e,o){this.minLogLevel=e,this.logBindings=o}log(e,...o){let r=Gn[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=Vn[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(`
8
+ `).map((d,u)=>u>0?` ${d}`:d).join(`
9
+ `)),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(`
10
+ `).map((c,d)=>d>0?` ${c}`:c).join(`
11
+ `)),console.log(e===0?`${s} `:" ",l)}e===0&&process.stdout.write("\x1B[39m")}setMinLevel(e){this.minLogLevel=e}info(...e){1<this.minLogLevel||this.log(1,...e)}debug(...e){0<this.minLogLevel||this.log(0,...e)}warn(...e){2<this.minLogLevel||this.log(2,...e)}error(...e){3<this.minLogLevel||this.log(3,...e)}child(e){return new t(this.minLogLevel,{...this.logBindings,...e})}flush(){}bindings(){return this.logBindings}},y=new At(1,{});import{z as K}from"zod";var Wn=K.object({id:K.string(),createdAt:K.coerce.date(),createdBy:K.string(),organizationId:K.string(),name:K.string(),schemaVersion:K.string(),numSteps:K.number()}),wl=K.object({steps:re.array()}).merge(Wn.omit({numSteps:!0}));import*as S from"zod";import{z as R}from"zod";var Et={WEBHOOK:"WEBHOOK",CRON:"CRON",MANUAL:"MANUAL",CLI:"CLI"},vt={PENDING:"PENDING",RUNNING:"RUNNING",PASSED:"PASSED",FAILED:"FAILED",CANCELLED:"CANCELLED"},Kn={PASSED:"PASSED",FAILED:"FAILED"},Qe=R.string().pipe(R.coerce.date()).or(R.date()),qn=R.object({id:R.string(),createdAt:Qe,createdBy:R.string(),organizationId:R.string(),scheduledAt:Qe.or(R.null()),startedAt:Qe.or(R.null()),finishedAt:Qe.or(R.null()),testId:R.string().or(R.null()),status:R.nativeEnum(vt),expectedStatus:R.nativeEnum(Kn).or(R.null()),runKey:R.string(),trigger:R.nativeEnum(Et),attempts:R.number(),test:R.object({name:R.string(),id:R.string()}).or(R.null())}),Ct=qn.merge(R.object({results:qe.array(),test:R.object({name:R.string(),id:R.string(),baseUrl:R.string()}).or(R.null())}));var le=S.object({disableCache:S.boolean()}),Dl=S.object({error:S.boolean(),reason:S.string(),message:S.string()}),Nl=Z.merge(le),Ao=yt,Pl=S.discriminatedUnion("vision",[Z.merge(le).merge(S.object({vision:S.literal(!1)})),Z.pick({goal:!0,url:!0}).merge(le).merge(S.object({screenshot:S.string(),vision:S.literal(!0)}))]),Tt=eo,_l=Z.pick({browserState:!0,goal:!0}).merge(le),kl=Z.pick({goal:!0}).merge(le).merge(S.object({screenshot:S.string().describe("base64 encoded image"),hintActivatedScreenshot:S.string().describe("base64 encoded image")})),Rt=wo,zl=Z.pick({goal:!0,url:!0}).merge(le),Eo=S.string().array(),Ul=Z.pick({goal:!0,browserState:!0}).merge(le),vo=ro,Fl=S.object({testPaths:S.string().array().describe("can be either hyphenated, lowercase test names or UUIDs"),all:S.boolean().optional()}),Co=S.object({message:S.string(),queuedTests:S.object({name:S.string(),id:S.string()}).array()}),To=ho,Ro=S.string().array(),$l=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")})]),xo=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"))}),Yn=S.object({test:S.string().describe("test YAML"),modules:S.record(S.string().describe("moduleId"),S.string().describe("module YAML"))}),Hl=Yn.array();var Bl=S.object({testPath:S.string(),testId:S.string()}).partial().merge(S.object({trigger:S.nativeEnum(Et)})),Io=Ct,Lo=Ct,jl=S.object({startedAt:S.coerce.date(),finishedAt:S.coerce.date(),results:qe.array(),status:S.nativeEnum(vt)}).partial(),Gl=S.object({screenshot:S.string()}),Oo=S.object({key:S.string()}),Mo=S.object({orgId:S.string()});function Ze(t){switch(t.type){case"AI_ACTION":return`AI action: ${t.text}`;case"PRESET_ACTION":return Se(t.command);case"RESOLVED_MODULE":return`Module: ${t.moduleId}`}}import{stringify as Do}from"yaml";import{z as O}from"zod";var nc=O.object({test:O.string().describe("YAML for the test, including metadata and steps"),modules:O.record(O.string(),O.string()).describe("Map of module name to YAML for the module")}),sc=G.merge(O.object({steps:ne.array(),fileType:O.literal("momentic/test")})),xt=Le.omit({type:!0}).merge(O.object({schemaVersion:O.string(),fileType:O.literal("momentic/module")})),ic=G.merge(O.object({steps:O.array(O.record(O.string(),O.unknown()))})),ac=O.object({moduleId:O.string().uuid(),name:O.string(),schemaVersion:O.string(),steps:O.array(O.record(O.string(),O.unknown()))});function No(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},r={},n=Po(t.steps,t.schemaVersion,r,e),s={fileType:"momentic/test",...o,steps:n};return{test:Do(s),modules:r}}function Po(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]=Xn(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 Xn(t,e){let o={fileType:"momentic/module",moduleId:t.moduleId,name:t.name,steps:Po(t.steps,e,{},new Set),schemaVersion:e};return Do(o)}var _o={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 ko={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 zo={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 et=new Set(["CLICK","TYPE","SELECT_OPTION"]),Uo={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||et.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||et.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||et.has(c))&&(a.target={elementDescriptor:l??""}),i.commands&&Array.isArray(i.commands)&&i.commands.forEach(u=>{let m=u?.elementDescriptor,h=u?.type;(m!==void 0||et.has(h))&&(u.target={elementDescriptor:m??""})})}),e}),stopOnFailure:!0};var Fo={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 $o={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 ce=[zo,Fo,$o,Uo,_o,ko];if(z!==ce[ce.length-1].toVersion)throw new Error("Please bump LATEST_VERSION in types package after adding a migration");ce.forEach((t,e)=>{if(!tt.valid(t.toVersion)||!tt.valid(t.fromVersion))throw new Error(`Migration '${t.name}' has invalid version`);if(!tt.gt(t.toVersion,t.fromVersion))throw new Error(`Migration '${t.name}' has toVersion <= fromVersion`);if(e===0)return;if(ce[e-1].toVersion!==t.fromVersion)throw new Error(`Migration '${t.name}' at index ${e} is not contiguous with previous migration`)});function Qn(t){return t.every(e=>e&&typeof e=="object"&&!Array.isArray(e))}var It=async({metadata:t,steps:e,logger:o})=>{let r=e,{schemaVersion:n,id:s}=t,i=ce.findIndex(c=>tt.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<ce.length;c++){let d=ce[c],u={id:s,migration:d.name,toVersion:d.toVersion};o.debug(u,"Starting migration");try{r=await Ho(r,d),a=d.toVersion}catch(m){throw o.error({err:m,...u},"Migration failed"),new Error(`Step migration ${d.name} failed: ${m}`)}}let l=Jn(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 Ho(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)||Qn(s)&&(r[n]=await Ho(s,e))}return o}import{parse as Lt}from"yaml";var es=new Set(["modules","fixtures","node_modules","dist","bin",".git","logs",".npm",".next","out",".yarn","__pycache__","build",".env",".venv","venv","env","wheels"]);function Ne(t,e=t){let o=t.split(we.sep).pop()??"";if(es.has(o))return o!=="modules"&&o!=="fixtures"&&y.warn(`Skipping directory '${t}' because it is likely an artifact folder.`),[];let r=ot.readdirSync(t),n=[];return r.forEach(s=>{let i=we.join(t,s);if(ot.statSync(i).isDirectory())n=n.concat(Ne(i,t));else if(s.endsWith(".yaml")){let a=ot.readFileSync(i,"utf-8"),l=ot.statSync(i),c=Lt(a);try{let d=G.parse(c);n.push({id:d.id,name:d.name,fullFilePath:i,testPath:we.relative(we.resolve(e),we.resolve(i)),localOnly:d.localOnly,fileName:s,lastModified:l.mtime,createdAt:l.ctime})}catch{y.warn(`Skipping file '${i}' because it does not parse as a valid Momentic test.`)}}}),n}function Ot(t,e){let o;try{o=Bo(t,"utf8"),o=o.replace(/\r\n|\r/g,`
12
+ `)}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]=rt(e,a).content)}),{test:o,modules:i}}function rt(t,e){let o=Zn(t);for(let r of o){let n=we.join(t,r),s=Bo(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 nt(t,e){let{test:o,modules:r}=Ot(t,e),n=Lt(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){y.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 It({metadata:n,steps:n.steps,logger:y});s=ne.array().parse(l)}else s=ne.array().parse(n.steps);let i={};for(let[l,c]of Object.entries(r)){let d=Lt(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){y.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 It({metadata:{id:d.moduleId,schemaVersion:d.schemaVersion},steps:d.steps,logger:y});i[l]={...d,steps:re.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 Xe.parse({...n,steps:a})}import{spawn as ts}from"child_process";async function jo(t){return new Promise((e,o)=>{let r={stdio:"inherit",env:process.env,detached:!0},n=ts("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 Es}from"fastest-levenshtein";import{homedir as vs}from"os";import{join as Cs}from"path";import{chromium as Ts,devices as mr}from"playwright";import{addExtra as Rs}from"playwright-extra";import xs from"puppeteer-extra-plugin-recaptcha";import Is from"puppeteer-extra-plugin-stealth";var Mt={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 de=(t,e)=>{let{hostname:o,pathname:r}=new URL(t),{hostname:n,pathname:s}=new URL(e);return o!==n||r!==s},Dt=t=>{try{return new URL(t),!0}catch{return!1}},Nt=(t,e)=>{try{return new URL(t,e),!0}catch{return!1}};import{distance as qo}from"fastest-levenshtein";var Go=new Set(["about:blank","chrome-error://chromewebdata/"]),Vo=2;var rs=["focusable","keyshortcuts","controls"],ns=["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"],ss=["notRendered","notVisible","ariaHiddenElement","ariaHiddenSubtree"],is=80,as={paragraph:"p",searchbox:"input"},Xo=["paragraph","StaticText"],Yo={indentLevel:0,noID:!1,noChildren:!1,noProperties:!1,maxLevel:void 0,neighbors:void 0},Pt=class{id;role;name;content;properties;dataMomenticId;pathFromRoot;parent;children;backendNodeID;constructor(e){this.id=e.id,this.role=e.role,this.name=e.name,this.content=e.content,this.properties={},this.pathFromRoot=e.pathFromRoot,this.children=e.children,this.backendNodeID=e.backendNodeID,e.properties&&e.properties.forEach(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 ns.includes(this.role)||this.children.some(e=>e.role==="StaticText")?!0:!!this.name.trim()||!!this.content}serialize(e=Yo){let{indentLevel:o,noChildren:r,noProperties:n,noID:s}=Object.assign({},Yo,e),i=" ".repeat(o),a=as[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=!Xo.includes(this.role);if(this.role==="StaticText")return`${i}${l}
13
+ `;let u=`${i}<${a}`;!s&&d&&(u+=` id="${this.id}"`),l&&(u+=` name="${l}"`),this.content&&(u+=` content="${this.content}"`),Object.keys(this.properties).length>0&&!n&&Object.entries(this.properties).forEach(([h,p])=>{rs.includes(h)||(typeof p=="string"?u+=` ${h}="${p}"`:typeof p=="boolean"?p?u+=` ${h}`:u+=` ${h}={false}`:typeof p<"u"&&(u+=` ${h}={${JSON.stringify(p)}}`))});let m=e.maxLevel!==void 0&&o/2>=e.maxLevel;if(this.children.length===0||r||m)u+=` />
14
+ `;else{let h="";for(let w of this.children)h+=w.serialize({...e,indentLevel:o+2});let p=h.trim();p.length<=is&&!p.includes(`
15
+ `)?u+=`>${p}</${a}>
16
16
  `:u+=`>
17
- ${p}${i}</${a}>
18
- `}if(e.neighbors!==void 0&&e.neighbors>0&&this.parent){let p=this.parent.children.findIndex(g=>g.id===this.id),f=p>0?this.parent.children[p-1]?.serialize({...e,neighbors:0}):"",w=p<this.parent.children.length-1?this.parent.children[p+1]?.serialize({...e,neighbors:0}):"";return`${f||""}
17
+ ${h}${i}</${a}>
18
+ `}if(e.neighbors!==void 0&&e.neighbors>0&&this.parent){let h=this.parent.children.findIndex(g=>g.id===this.id),p=h>0?this.parent.children[h-1]?.serialize({...e,neighbors:0}):"",w=h<this.parent.children.length-1?this.parent.children[h+1]?.serialize({...e,neighbors:0}):"";return`${p||""}
19
19
  ${u}
20
- ${w||""}`}return u}},Nt=class{constructor(e,o,r){this.root=e;this.a11yIdNodeMap=o;this.dataMomenticIdMap=r}serialize(){return this.root?this.root.serialize():""}};function ns(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 qo(t,e,o){if(!e&&t.parentId)throw new Error(`Got no parent for accessibility node ${t.nodeId}: ${JSON.stringify(t)}`);let r=new Dt({id:parseInt(t.nodeId),role:t.role?.value||"",name:t.name?.value||"",content:t.value?.value||"",properties:t.properties,children:[],pathFromRoot:(e?`${e.pathFromRoot} `:"")+ns(t),backendNodeID:t.backendDOMNodeId});t.value?.value&&(r.content=`${t.value?.value}`);let n=t.childIds??[];for(let a of n){if(!a)continue;let l=o.get(parseInt(a));if(!l)continue;let d=qo(l,r,o);d.length&&(r.children=r.children.concat(d))}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(!Ko.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 Yo(t,e,o,r,n=1){t.id=n,n+=1,e.set(t.id,t),t.dataMomenticId?o.set(t.dataMomenticId,t):t.role!=="StaticText"&&t.role!=="RootWebArea"&&t.role!=="paragraph"&&r.debug({node:t.serialize({neighbors:1,maxLevel:1})},"Node has no data-momentic-id");for(let s of t.children)n=Yo(s,e,o,r,n);return n}function Xo(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=>ts.includes(l.name)):!0);let o=new Map;for(let i of t.allNodes)o.set(parseInt(i.nodeId),i);let r=qo(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 Ve;let n=new Map,s=new Map;return Yo(r[0],n,s,e),new Nt(r[0],n,s)}var Pe=(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})},Pt=(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=Vo(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.serializedForm){let n=t.serialize({noID:!0,maxLevel:1,neighbors:1}),s=Vo(n,e.serializedForm)/Math.min(n.length,e.serializedForm.length);s===0?o+=2:s<=.1&&o++}return o};var de={r:147,g:196,b:125,a:.55},Jo={showInfo:!1,showRulers:!1,showStyles:!1,showAccessibilityInfo:!1,showExtensionLines:!1,contrastAlgorithm:"aa",contentColor:de,paddingColor:de,borderColor:de,marginColor:de,eventTargetColor:de,shapeColor:de,shapeMarginColor:de};var q=(t=1e3)=>new Promise(e=>setTimeout(()=>e(),t));function Qo(){cursor=document.createElement("img"),cursor.setAttribute("src","data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjMyIiB2aWV3Qm94PSIwIDAgMzIgMzIiIHdpZHRoPSIzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwIDcpIj48cGF0aCBkPSJtNi4xNDggMTguNDczIDEuODYzLTEuMDAzIDEuNjE1LS44MzktMi41NjgtNC44MTZoNC4zMzJsLTExLjM3OS0xMS40MDh2MTYuMDE1bDMuMzE2LTMuMjIxeiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Im02LjQzMSAxNyAxLjc2NS0uOTQxLTIuNzc1LTUuMjAyaDMuNjA0bC04LjAyNS04LjA0M3YxMS4xODhsMi41My0yLjQ0MnoiIGZpbGw9IiMwMDAiLz48L2c+PC9zdmc+"),cursor.setAttribute("id","selenium_cursor"),cursor.setAttribute("style","position: absolute; z-index: 99999999999; pointer-events: none; left:0; top:0"),cursor.style.filter="invert(0%) sepia(6%) saturate(24%) hue-rotate(315deg) brightness(89%) contrast(110%)",document.body.appendChild(cursor),document.onmousemove=function(t){t=t||window.event,document.getElementById("selenium_cursor").style.left=t.pageX+"px",document.getElementById("selenium_cursor").style.top=t.pageY+"px"}}function Zo(){window.globalHintManager||(window.globalHintManager=new window.HintManager),window.globalHintManager.capture()}function er(){window.globalHintManager&&window.globalHintManager.reset()}function tr(){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 ss=new Set(["document","script","XMLHttpRequest","fetch","xhr"]),is=new Set(["script","document"]),as=["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"],ls=["api.stripe.com","supabase.co"];function _t(t){return`${t.resourceType()} ${t.method()} ${t.url()}`}function or(t){return t=t.replace(/^www\./,""),t}function rr(t){return ls.some(e=>t.includes(e))}function nr(t,e){if(!ss.has(t.resourceType()))return!1;let o=new URL(e),r=new URL(t.url());return as.some(n=>r.hostname.includes(n))?!1:is.has(t.resourceType())||t.method()!=="GET"?!0:or(r.hostname).includes(or(o.hostname))}var at=Es(As);at.use(Cs());at.use(vs({provider:{id:"2captcha",token:process.env.TWO_CAPTCHA_KEY},visualFeedback:!0}));var x=class t{browser;context;page;a11yIdToNodeMap=new Map;dataMomenticIdToNodeMap=new Map;cdpClient;logger;localMode;activeFrame;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=lr["Desktop Chrome"].userAgent;static VIEWPORT={width:1920,height:1080};static async init({baseUrl:e,logger:o,browserArgs:r,contextArgs:n,sendScreenshotsDuringLoad:s,onClose:i,localMode:a,localAppUrl:l,timeout:d=8e3}){let c={headless:!0,handleSIGTERM:!1,chromiumSandbox:!1,serviceWorkers:"block",...r??{}},u={viewport:t.VIEWPORT,deviceScaleFactor:process.platform==="darwin"?2:1,userAgent:lr["Desktop Chrome"].userAgent,geolocation:{latitude:37.7749,longitude:-122.4194},locale:"en-US",timezoneId:"America/Los_Angeles",...n??{}},m=null,p,f;a?(p=await at.launchPersistentContext(bs(ws(),"momentic","chromium"),{...c,...u,ignoreDefaultArgs:["--enable-automation"],ignoreHTTPSErrors:!0,bypassCSP:!0,args:["--allow-insecure-localhost","--disable-web-security","--disable-site-isolation-for-policy","--disable-site-isolation-trials",`--unsafely-treat-insecure-origin-as-secure=${l}`],baseURL:e}),f=p.pages()[0],f.on("close",()=>{i?.()})):(m=await at.launch(c),p=await m.newContext({...u,baseURL:e}),f=await p.newPage());let w=await p.newCDPSession(f),g=new t({browser:m,context:p,page:f,baseUrl:e,cdpClient:w,logger:o,localMode:a}),N=!1;(async()=>{try{await g.navigate({url:e,wrapPossibleNavigation:!1,initialNavigation:!0})}catch(fe){o.error({err:fe},"Failed to initialize chrome browser")}finally{N=!0}})();let b=async()=>{if(s)try{s({viewport:await g.viewport(),buffer:await g.screenshot()})}catch(fe){o.error({err:fe},"Failed to take screenshot")}};b();let E=setInterval(()=>{b()},250),T=Date.now();for(;!N&&Date.now()-T<d;)await q(250);return clearInterval(E),N||o.warn("Timeout elapsed waiting for browser to initialize - are you sure this page is accessible?"),g}async getUserPageOrFrame(){if(this.localMode&&this.activeFrame){let e=this.page.frame(this.activeFrame);if(!e)throw new Error(`Failed to get non-existent frame: ${this.activeFrame}`);return e}return this.page}async initCDPSession(e=2){try{await this.cdpClient.send("Accessibility.enable"),await this.cdpClient.send("DOM.enable"),await this.cdpClient.send("Overlay.enable")}catch(o){if(e>0)return this.logger.error({err:o},"Failed to initialize CDP session, re-creating CDP client"),this.cdpClient=await this.context.newCDPSession(this.page),await q(250),this.initCDPSession(e-1)}}setLogger(e){this.logger=e}async registerLocalRequestInterceptors(e){await e.route("**/*",async o=>{let r;try{r=await o.fetch()}catch(s){this.closed||this.logger.warn({err:s},"Network request failed");return}let n=r.headers();delete n["content-security-policy"],delete n["x-frame-options"],delete n["x-xss-protection"],n["Access-Control-Allow-Origin"]="*",await o.fulfill({headers:n,response:r})})}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();e.clearCookies&&await this.context.clearCookies(),!this.page.isClosed()&&(e.clearStorage&&await(await this.getUserPageOrFrame()).evaluate(()=>{localStorage.clear()}),await this.navigate({url:e.url??this.baseURL,wrapPossibleNavigation:!1,initialNavigation:!0}))}async pageSetup(){try{this.localMode||await this.page.evaluate(Qo)}catch{}}async wait(e){await q(e)}async toggleHints(e){let o=await this.getUserPageOrFrame();e.state==="on"?(await o.addStyleTag({content:Lt.css}),await o.addScriptTag({content:Lt.js}),await o.evaluate(Zo)):await o.evaluate(er)}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}`),r&&this.localMode&&await this.registerLocalRequestInterceptors(this.context);let n=Date.now(),s=async()=>{try{await(await this.getUserPageOrFrame()).goto(e,{waitUntil:"load",timeout:3e3}),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(Ho.has(i)&&process.env.NODE_ENV==="production")throw new Error(`${e} took too long to load \u{1F61E}. Please ensure the site and your internet are working.`);await this.pageSetup(),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(`Select 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:Jo,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,d=E=>{let T=_t(E.request());l.set(T,(l.get(T)??0)+1);let fe=E.status();fe>=500&&this.logger.warn({request:T,status:fe},"Received 500 level response")},c=E=>{if(!nr(E,s))return;let T=_t(E);a.set(T,(a.get(T)??0)+1),i=Date.now()};this.page.on("response",d),this.page.on("request",c);let u=[];r&&(u=this.context.pages().map(E=>E.url()));let m=!1,p=e().catch(E=>(m=!0,E instanceof Error?E:new Error(`${E}`)));await q(250);let f=async E=>{let T=await E;if(T instanceof Error)throw T;return T},w=new Set,g=!1,v=await(async()=>{for(;!m&&!(!g&&Date.now()-n>o);){if(await q(250),g=!1,w=new Set,Date.now()-i<=1250)continue;let E=!1;for(let T of a.keys())a.get(T)!==l.get(T)&&(rr(T)&&(g=!0),E=!0,w.add(T));if(!E)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!m&&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",d),this.page.off("request",c),!v)return f(p);let b=await this.url();if(!m&&ce(b,s)){this.logger.debug({startUrl:s,newUrl:b},"Detected url change in wrapPossibleNavigation, waiting for load state");let E=Math.max(o-(Date.now()-n),0);if(E>0)try{await(await this.getUserPageOrFrame()).waitForLoadState("load",{timeout:E})}catch{this.logger.warn({url:await this.url()},"Timeout elapsed waiting for load state, continuing anyways...")}}if(r){let E=this.context.pages().map(T=>T.url());if(E.length>u.length)for(let T of E)T!==b&&await this.switchToPage(T)}return f(p)}async resolveCachedTargetToID(e){if(!je(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 Pe(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=Pt(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"),Pe(r,e),e.id}let n=1/0,s=1/0,i;for(let a of this.a11yIdToNodeMap.values()){let l=Pt(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"),Pe(a,e),a.id;if(!e.serializedForm)continue;let d=a.serialize({noID:!0,maxLevel:1,neighbors:1});if(Math.abs(d.length-e.serializedForm.length)>15)continue;let c=Ss(e.serializedForm,d),u=c/Math.min(e.serializedForm.length,d.length);c<n&&u<.2&&(n=c,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"),Pe(i,e),i.id;throw new Error(`Could not find any relevant node given cached target: ${JSON.stringify(e)}`)}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 await this.page.reload({timeout:3e3}),await this.pageSetup()}async getA11yTree(){await this.initCDPSession();let e=null,o=0,r=await this.url();for(;!e;)try{let n=await this.getRawA11yTree();if(!n.root||n.allNodes.length===0)throw new Error("No a11y tree found on page");e=Xo(n,this.logger)}catch(n){if(this.logger.error({err:n,url:r},"Error fetching a11y tree"),o===0)await q(1e3),o++;else throw new Error(`Max retries exceeded fetching a11y tree: ${n}`)}return e.root||this.logger.warn("A11y tree was pruned entirely"),this.a11yIdToNodeMap=e.a11yIdNodeMap,this.dataMomenticIdToNodeMap=e.dataMomenticIdMap,e}getA11yIdFromDataMomenticId(e){return this.dataMomenticIdToNodeMap.get(e)?.id}async getRawA11yTree(){let e=await this.url(),o=Date.now(),r=()=>{o=Date.now()};this.cdpClient.addListener("Accessibility.nodesUpdated",r);let n=!1,s=()=>{this.logger.info({url:e},"Load event fired on page"),n=!0,o=Date.now()};this.cdpClient.addListener("Accessibility.loadComplete",s);let i=Date.now(),a=!0;for(;Date.now()-i<3e3;){if(await q(250),!n&&Date.now()-i<1e3){process.env.NODE_ENV!=="production"&&this.logger.debug({url:e},"A11y tree not loaded yet, waiting...");continue}if(Date.now()-o>=1250){a=!1;break}this.logger.debug({url:e},"A11y tree not stable yet, waiting...")}this.logger.debug({duration:Date.now()-i,eventReceived:n,timeoutTriggered:a},"A11y wait phase completed"),await(await this.getUserPageOrFrame()).evaluate(tr);let l;if(this.localMode&&this.activeFrame){let{result:{objectId:c}}=await this.cdpClient.send("Runtime.evaluate",{expression:`document.querySelector("#${this.activeFrame}")`}),u=await this.cdpClient.send("DOM.describeNode",{objectId:c});l=(await this.cdpClient.send("Accessibility.getRootAXNode",{frameId:u.node.frameId})).node.backendDOMNodeId}else{let{node:c}=await this.cdpClient.send("Accessibility.getRootAXNode");l=c.backendDOMNodeId}let{nodes:d}=await this.cdpClient.send("Accessibility.queryAXTree",{backendNodeId:l});return this.cdpClient.removeListener("Accessibility.loadComplete",s),this.cdpClient.removeListener("Accessibility.nodesUpdated",r),{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==="data-momentic-id");return s===-1?"":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}`);try{let r=await this.getIDAttributeUsingCDP(o.object.objectId);if(!r)throw new Error("Failed getting data-momentic-id attribute using CDP");return(await this.getUserPageOrFrame()).locator(`[data-momentic-id="${r}"]`)}catch(r){throw this.logger.error({err:r},"Failed to get ID attribute"),r}}async clickUsingCDP(e,o={}){let r=0,n=e;for(;r<Bo;){if(!n||n.role==="RootWebArea")throw new Error(`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||[],d=s.nodeIndex,c=s.bounds,u=-1;for(let v=0;v<a.length;v++)if(l[v]===e){u=d.indexOf(v);break}if(u===-1)throw new Error(`Could not find any backend node with ID ${e}`);let[m=0,p=0,f=0,w=0]=c[u];m/=r,p/=r,f/=r,w/=r;let g=m+f/2,N=p+w/2;return{centerX:g,centerY:N}}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})),await this.pageSetup()}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})),await this.pageSetup()}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;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.pageSetup(),await this.initCDPSession();return}}throw new Error(`Could not find page with url containing ${e}`)}async setCookie(e){let o=ro(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 q(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 d=`Captcha solution API returned error response: ${a.statusText}`;throw this.logger.error({text:await a.text()},d),new Error(d)}let l=await a.json();if(l.errorId){let d=`Captcha solution API returned error ID ${l.errorId}`;throw this.logger.error(d),new Error(d)}if(l.status==="ready"){i=l.solution.text;break}}if(!i)throw new Error("Captcha solution timed out");return i}getActiveFrame(){return this.activeFrame}};var be=async({controller:t,step:e,logger:o,advanced:r,...n})=>{n.onStarted?.(),t.resetHistory();let s={...e,startedAt:new Date,userAgent:x.USER_AGENT,finishedAt:new Date,results:[],status:"SUCCESS"};try{let i=0,a=e.commands&&e.commands.length>0;for(;;){if(i>20)throw new Error(`Exceeded max number of commands per step (${20})`);if(t.isClosed())throw new Error("Cancelling remaining steps in AI action because the controller is now closed");let l,d=new Date,c=await t.browser.screenshot(),u=await n.onSaveScreenshot(c);if(a){if(l=e.commands[i],!l)throw new Error(`Saved command at index ${i} is undefined.`)}else{o.info(`Executing sub-command ${i} within AI step`);let p=await t.promptToCommand(e.type,e.text,r.disableAICaching);if(l=p.command,l.type==="FAILURE"){s.finishedAt=new Date,s.status="FAILED",s.message=l.thoughts;break}let f=i;(async()=>{if("target"in l&&l.target){await new Promise(g=>setTimeout(()=>g(),500));try{let g=await Ts(t,p.context,l.target);l.target.elementDescriptor=g,n.onCommandExecuted?.({commandIndex:f,command:l})}catch(g){o.error({err:g},"Failed to generate element description, continuing...")}}})()}n.onCommandGenerated?.({commandIndex:i,message:yt[l.type]||`Unknown command (${l.type})`});let m={beforeScreenshot:u,beforeUrl:await t.browser.url(),startedAt:d,viewport:await t.browser.viewport(),finishedAt:new Date,status:"SUCCESS"};o.info(`Executing sub-command ${i} within AI step: ${Se(l)}`);try{let p=await t.executeCommand(l,r.disableAICaching,a);o.info(`AI sub-command ${i} completed successfully`),m.elementInteracted=p.elementInteracted;let f=await t.browser.screenshot(),w=await n.onSaveScreenshot(f);m.afterScreenshot=w,m.afterUrl=await t.browser.url(),m.finishedAt=new Date;let g={status:"SUCCESS",startedAt:m.startedAt,finishedAt:m.finishedAt,type:"PRESET_ACTION",data:p.data,command:l,results:[m]};if(s.results.push(g),n.onCommandExecuted?.({commandIndex:i,output:g,command:l}),l.type==="SUCCESS"){s.finishedAt=new Date,s.status="SUCCESS",s.message=p.thoughts??"All commands completed.";break}if(p.succeedImmediately&&!a){s.finishedAt=new Date,s.status="SUCCESS",s.message=p.succeedImmediatelyReason,l={type:"SUCCESS"},n.onCommandExecuted?.({commandIndex:i+1,output:g,command:l}),s.results.push({...g,command:l});break}}catch(p){if(a){a=!1,i=0,s.results=[];continue}let f=p instanceof Error?p.message:`${p}`;m.status="FAILED",m.message=f,m.finishedAt=new Date,m.afterUrl=await t.browser.url();let w;try{w=await t.browser.screenshot()}catch(g){o.warn({err:g},"Failed to take screenshot after error, skipping")}m.afterScreenshot=w,s.results.push({status:"FAILED",startedAt:m.startedAt,finishedAt:m.finishedAt,type:"PRESET_ACTION",command:l,results:[m],message:f}),s.status="FAILED",s.finishedAt=new Date,s.message=f;break}i++}}catch(i){s.message=i instanceof Error?i.message:`${i}`,s.finishedAt=new Date,s.status="FAILED"}return s.status==="SUCCESS"?n.onSuccess?.({message:s.message||"AI step succeeded.",startedAt:s.startedAt.getTime(),durationMs:s.finishedAt.getTime()-s.startedAt.getTime(),output:s}):n.onFailure?.({message:s.message||"AI step errored.",startedAt:s.startedAt.getTime(),durationMs:s.finishedAt.getTime()-s.startedAt.getTime(),output:s}),s};async function Ts(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 Ae=async({controller:t,step:e,advanced:o,...r})=>{r.onStarted?.();let n=new Date,s=await t.browser.url(),i=await t.browser.screenshot(),a=await r.onSaveScreenshot(i);try{let l=await t.executePresetStep(e.command,o.disableAICaching),d=await t.browser.screenshot(),c=await r.onSaveScreenshot(d),u=new Date,m={...e,startedAt:n,finishedAt:u,status:"SUCCESS",data:l.data,results:[]},p="Successfully executed preset action.";e.command.type==="AI_ASSERTION"&&(p=l.thoughts||"Assertion passed.");let f={beforeUrl:s,beforeScreenshot:a,afterUrl:await t.browser.url(),afterScreenshot:c,startedAt:n,finishedAt:u,viewport:await t.browser.viewport(),status:"SUCCESS"};return m.status="SUCCESS",m.results=[f],m.message=p,r.onSuccess?.({message:p,startedAt:n.getTime(),durationMs:u.getTime()-n.getTime(),command:e.command,output:m}),m}catch(l){r.logger.error({err:l},`Failed executing preset step ${e.command.type}`);let d=new Date,c=l instanceof Error?l.message:`${l}`,u=await t.browser.screenshot();if(e.command.type==="AI_ASSERTION"&&e.command.cancelOnFailure){let p={...e,startedAt:n,finishedAt:d,status:"CANCELLED",message:c,results:[{beforeUrl:s,beforeScreenshot:a,afterUrl:await t.browser.url(),afterScreenshot:u,startedAt:n,finishedAt:d,viewport:await t.browser.viewport(),status:"CANCELLED",message:c}]};return r.onCancelled?.({message:c,startedAt:n.getTime(),durationMs:d.getTime()-n.getTime(),output:p}),p}let m={...e,startedAt:n,finishedAt:d,status:"FAILED",message:c,results:[{beforeUrl:s,beforeScreenshot:a,afterUrl:await t.browser.url(),afterScreenshot:u,startedAt:n,finishedAt:d,viewport:await t.browser.viewport(),status:"FAILED",message:c}]};return r.onFailure?.({message:c,startedAt:n.getTime(),durationMs:d.getTime()-n.getTime(),output:m}),m}};var lt=async({controller:t,step:e,advanced:o,logger:r,...n})=>{n.onStarted?.();let s={type:"MODULE",moduleId:e.moduleId,startedAt:new Date,userAgent:x.USER_AGENT,results:[],finishedAt:new Date,status:"SUCCESS"};for(let i=0;i<e.steps.length;i++){let a=e.steps[i];if(t.isClosed())throw s.status="CANCELLED",new Error("Cancelling remaining steps in module because the controller is now closed");r.debug({i,moduleStep:a},"Starting module step"),r.info(`Starting module sub-step ${i+1}/${e.steps.length}: ${Ze(a)}`);let l;switch(a.type){case"PRESET_ACTION":l=await Ae({controller:t,step:a,advanced:o,logger:r,onSaveScreenshot:n.onSaveScreenshot,onStarted(){n.onStepStarted?.({index:i})},onSuccess({message:c,startedAt:u,durationMs:m,output:p}){n.onStepSuccess?.({index:i,message:c,startedAt:u,durationMs:m,output:p})},onFailure({message:c,startedAt:u,durationMs:m,output:p}){n.onStepFailure?.({index:i,message:c,startedAt:u,durationMs:m,output:p})},onCancelled({message:c,startedAt:u,durationMs:m,output:p}){n.onStepCancelled?.({index:i,message:c,startedAt:u,durationMs:m,output:p})}});break;case"AI_ACTION":l=await be({controller:t,step:a,advanced:o,logger:r,onSaveScreenshot:n.onSaveScreenshot,onStarted(){n.onStepStarted?.({index:i})},onSuccess({message:c,startedAt:u,durationMs:m,output:p}){n.onStepSuccess?.({index:i,message:c,startedAt:u,durationMs:m,output:p})},onFailure({message:c,startedAt:u,durationMs:m,output:p}){n.onStepFailure?.({index:i,message:c,startedAt:u,durationMs:m,output:p})},onCommandGenerated({commandIndex:c,message:u}){n.onCommandGenerated?.({index:i,commandIndex:c,message:u})},onCommandExecuted({commandIndex:c,command:u,output:m}){n.onCommandExecuted?.({index:i,commandIndex:c,command:u,output:m})}});break;default:return(c=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(a)}if(s.results.push(l),l.status==="FAILED"||l.status==="CANCELLED"){s.status=l.status,s.finishedAt=new Date,s.message=l.message;for(let d=i+1;d<e.steps.length;d++){let u={...e.steps[d],status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:x.USER_AGENT,results:[],message:"Cancelled due to previous failure."};s.results.push(u)}break}}return s.status==="SUCCESS"?n.onSuccess?.({message:"Executed module step.",startedAt:s.startedAt.getTime(),durationMs:s.finishedAt.getTime()-s.startedAt.getTime(),output:s}):s.status==="FAILED"?n.onFailure?.({message:"Failed to execute module step.",startedAt:s.startedAt.getTime(),durationMs:s.finishedAt.getTime()-s.startedAt.getTime(),output:s}):s.status==="CANCELLED"&&n.onCancelled?.({message:"Module step cancelled.",startedAt:s.startedAt.getTime(),durationMs:s.finishedAt.getTime()-s.startedAt.getTime(),output:s}),s};import{v4 as zm}from"uuid";var Um=process.env.AWS_ACCESS_KEY_ID,Fm=process.env.AWS_SECRET_ACCESS_KEY,$m=process.env.S3_REGION,Hm=process.env.S3_BUCKET;var dr=async({test:t,runId:e,controller:o,logger:r,onUpdateRun:n,onSaveScreenshot:s})=>{try{let i=await Rs({test:t,runId:e,controller:o,logger:r,onUpdateRun:n,onSaveScreenshot:s});if(i==="PASSED"||i==="CANCELLED")return i;throw new R("InternalPlatformError",i.message||"An unknown platform error occurred")}catch(i){throw i instanceof R||(i=new R("InternalPlatformError",i instanceof Error?i.message:`${i}`,{cause:i})),i}finally{await o.browser.cleanup()}},Rs=async({test:t,runId:e,controller:o,logger:r,onUpdateRun:n,onSaveScreenshot:s})=>{let i=Z.parse(t.advanced),a=r.child({runId:e,testId:t.id});a.info("Starting test run"),await n({status:"RUNNING",startedAt:new Date});let l,d="PASSED",c=[];for(let u=0;u<t.steps.length;u++){let m=t.steps[u];a.info(`Starting step ${u+1}/${t.steps.length}: ${Ze(m)}`);let p;switch(m.type){case"PRESET_ACTION":p=await Ae({controller:o,step:m,advanced:i,logger:a,onSaveScreenshot:s});break;case"AI_ACTION":p=await be({controller:o,step:m,advanced:i,logger:a,onSaveScreenshot:s});break;case"RESOLVED_MODULE":p=await lt({controller:o,step:m,advanced:i,logger:a,onSaveScreenshot:s});break;default:return(w=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(m)}if(c.push(p),await n({results:c}),p.status==="SUCCESS"){a.info(`Step ${u+1}/${t.steps.length} succeeded`);continue}if(p.status!=="FAILED"&&p.status!=="CANCELLED")throw new R("InternalPlatformError",`Received unexpected non-terminal status from step: ${p.status}`);a.info({message:c[c.length-1]?.message},`Step ${u+1}/${t.steps.length} ended with status: ${p.status}`),l=p,d=p.status;for(let f=u+1;f<t.steps.length;f++){let w=t.steps[f];if(w.type==="RESOLVED_MODULE"){let g={type:"MODULE",moduleId:w.moduleId,startedAt:new Date,userAgent:x.USER_AGENT,results:w.steps.map(N=>({...N,status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:x.USER_AGENT,results:[]})),finishedAt:new Date,status:"CANCELLED"};c.push(g)}else{let g={...w,status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:x.USER_AGENT,results:[]};c.push(g)}}break}return await n({status:d,finishedAt:new Date,results:c}),d==="FAILED"?l:d};var mr={currentlyExecutingRequests:0},xs=t=>async e=>{try{mr.currentlyExecutingRequests++,await Is({...t,...e})}finally{mr.currentlyExecutingRequests--}},Is=async({socket:t,steps:e,testMetadata:o,reInitialize:r,indicesFilter:n,metadata:s,logger:i,rootController:a})=>{let{testId:l,sessionId:d,orgId:c}=s,u={testId:l,orgId:c},m=i.child(u),p=i.child({package:"web-agent",...u}),f=Z.parse(o.advanced??{}),w=k.getController(d);if(!w)throw new Error("No controller found");w.setOpen();let g=o.baseUrl,N=x.USER_AGENT;m.info({reInitialize:r,steps:e,indicesFilter:n},"Starting execution");let v={url:g,clearCookies:!0,clearStorage:!0};if(r&&(a?(a.browser.baseURL=g,a.browser.setActiveFrame(Je),await a.resetState(v),w.setLogger(p),w.browser.setLogger(p)):await w.resetState(v),w.setOpen()),m.info(`Session restarted for test ${l} at ${g}`),t.emit("session",{url:g,userAgent:N,viewport:await w.browser.viewport()}),n?.length){let b=Os(e,n);m.info({step:b},"Starting individual step"),t.emit("started",{indices:n}),await ur({indices:n,step:b,controller:w,advanced:f,socket:t,logger:m}),t.emit("finished");return}for(let b=0;b<e.length&&!(w.browser.closed||w.isClosed()||t.disconnected);b++){let E=e[b];if(m.info({index:b,step:E},"Starting step"),!await ur({indices:[b],step:E,controller:w,advanced:f,socket:t,logger:m}))break}t.emit("finished")},Ls=(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}`)}},Os=(t,e)=>{let o=t[e[0]];for(let r=1;r<e.length;r++)o=Ls(o,e[r]);return o},zt=async t=>t,ur=async({indices:t,step:e,controller:o,advanced:r,socket:n,logger:s})=>{let i=()=>{n.emit("started",{indices:t})},a=({message:c,startedAt:u,durationMs:m,command:p,output:f})=>{n.emit("success",{indices:t,message:c,startedAt:u,durationMs:m,command:p,output:f})},l=({message:c,startedAt:u,durationMs:m,output:p})=>{n.emit("failure",{indices:t,message:c,startedAt:u,durationMs:m,output:p})},d=({message:c,startedAt:u,durationMs:m,output:p})=>{n.emit("cancelled",{indices:t,message:c,startedAt:u,durationMs:m,output:p})};switch(e.type){case"PRESET_ACTION":return(await Ae({controller:o,step:e,advanced:r,logger:s,onSaveScreenshot:zt,onStarted:i,onSuccess:a,onFailure:l,onCancelled:d})).status==="SUCCESS";case"AI_ACTION":return(await be({controller:o,step:e,advanced:r,logger:s,onSaveScreenshot:zt,onStarted:i,onSuccess:a,onFailure:l,onCommandGenerated({commandIndex:m,message:p}){n.emit("commandGenerated",{indices:[...t,m],message:p})},onCommandExecuted({commandIndex:m,command:p,output:f}){n.emit("commandExecuted",{indices:[...t,m],command:p,output:f})}})).status==="SUCCESS";case"RESOLVED_MODULE":return(await lt({controller:o,step:e,advanced:r,logger:s,onSaveScreenshot:zt,onStarted:i,onSuccess:a,onFailure:l,onCancelled:d,onStepStarted({index:m}){n.emit("started",{indices:[...t,m]})},onStepSuccess({index:m,message:p,startedAt:f,durationMs:w,output:g}){n.emit("success",{indices:[...t,m],message:p,startedAt:f,durationMs:w,output:g})},onStepFailure({index:m,message:p,startedAt:f,durationMs:w,output:g}){n.emit("failure",{indices:[...t,m],message:p,startedAt:f,durationMs:w,output:g})},onStepCancelled({index:m,message:p,startedAt:f,durationMs:w,output:g}){n.emit("cancelled",{indices:[...t,m],message:p,startedAt:f,durationMs:w,output:g})},onCommandGenerated({commandIndex:m,index:p,message:f}){n.emit("commandGenerated",{indices:[...t,p,m],message:f})},onCommandExecuted({index:m,commandIndex:p,command:f,output:w}){n.emit("commandExecuted",{indices:[...t,m,p],command:f,output:w})}})).status==="SUCCESS";default:return(u=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(e)}};var pr={event:"execute",createHandler:xs};var Ms=({metadata:t,logger:e})=>{let{sessionId:o}=t;return async({description:r,useVision:n,testMetadata:s},i)=>{let a=k.getController(o);if(!a)throw new Error("No controller found");let l=Z.parse(s.advanced??{}),d,c;try{d=await a.locateElement(r,n,l.disableAICaching),c={id:d.id},await a.browser.resolveCachedTargetToID(c)}catch(u){e.error({err:u},"Error locating element"),i({found:!1});return}e.info({locator:d,a11yData:c},"Located element result");try{await a.browser.scrollIntoView({id:d.id}),await a.browser.highlight({id:d.id})}catch(u){e.error({err:u,locator:d},"Error highlighting element"),i({found:!1});return}i({found:!0,locator:d,a11yData:c})}},hr={event:"locate",createHandler:Ms};var Ds=({socket:t,metadata:e,rootController:o,logger:r})=>async()=>{let{testId:n,sessionId:s}=e,i;if(o)await o.resetState({clearCookies:!0,clearStorage:!0}),i=o.browser.baseURL;else{let l=k.getController(s);if(!l){t.emit("error",{message:"No session to reset"});return}await l.browser.reset({clearCookies:!0,clearStorage:!0}),i=l.browser.baseURL}let a=x.USER_AGENT;r.info(`Session restarted for test ${n} at ${i}`),t.emit("session",{url:i,userAgent:a,viewport:x.VIEWPORT})},fr={event:"reset",createHandler:Ds};import{v4 as zs}from"uuid";var Ns={type:"a11y",version:"1.0.0",useHistory:"diff",useGoalSplitter:!0},me=Ns;import Ps from"dedent";import _s from"diff-lines";var ks=1e4,Y=class{browser;pendingInstructions;generator;commandHistory;config;closed=!1;logger;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 R?n:new R("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 d=this.getSerializedHistory(i,a),c={url:i,numPrevious:l,browserState:a,history:d,goal:n,lastCommand:this.lastExecutedCommand},u=await this.generator.getProposedCommand(c,r);if(this.logger.info({type:u.type,thoughts:u.thoughts},"Got proposed command"),u.type==="SUCCESS"){let m=this.pendingInstructions.pop();if(this.logger.info({finishedInstruction:m,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:c,command:u}}async locateElement(e,o,r){if(!e)throw new R("InternalWebAgentError","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 R("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 R("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=[`
21
- 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(ce(s.urlBeforeCommand,e))n.push(` URL CHANGE: '${s.urlBeforeCommand}' -> '${e}'`);else{let a=_s(s.browserStateBeforeCommand,o,{n_surrounding:1});a?a.length<ks?(n.push("PAGE CONTENT CHANGE:"),a.split(`
20
+ ${w||""}`}return u}},_t=class{constructor(e,o,r){this.root=e;this.a11yIdNodeMap=o;this.dataMomenticIdMap=r}serialize(){return this.root?this.root.serialize():""}};function ls(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 Jo(t,e,o){if(!e&&t.parentId)throw new Error(`Got no parent for accessibility node ${t.nodeId}: ${JSON.stringify(t)}`);let r=new Pt({id:parseInt(t.nodeId),role:t.role?.value||"",name:t.name?.value||"",content:t.value?.value||"",properties:t.properties,children:[],pathFromRoot:(e?`${e.pathFromRoot} `:"")+ls(t),backendNodeID:t.backendDOMNodeId});t.value?.value&&(r.content=`${t.value?.value}`);let n=t.childIds??[];for(let a of n){if(!a)continue;let l=o.get(parseInt(a));if(!l)continue;let c=Jo(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(!Xo.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 Qo(t,e,o,r,n=1){t.id=n,n+=1,e.set(t.id,t),t.dataMomenticId?o.set(t.dataMomenticId,t):t.role!=="StaticText"&&t.role!=="RootWebArea"&&t.role!=="paragraph"&&r.debug({node:t.serialize({neighbors:1,maxLevel:1})},"Node has no data-momentic-id");for(let s of t.children)n=Qo(s,e,o,r,n);return n}function Zo(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=>ss.includes(l.name)):!0);let o=new Map;for(let i of t.allNodes)o.set(parseInt(i.nodeId),i);let r=Jo(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 Ve;let n=new Map,s=new Map;return Qo(r[0],n,s,e),new _t(r[0],n,s)}var Pe=(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})},kt=(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=qo(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.serializedForm){let n=t.serialize({noID:!0,maxLevel:1,neighbors:1}),s=qo(n,e.serializedForm)/Math.min(n.length,e.serializedForm.length);s===0?o+=2:s<=.1&&o++}return o};var me={r:147,g:196,b:125,a:.55},er={showInfo:!1,showRulers:!1,showStyles:!1,showAccessibilityInfo:!1,showExtensionLines:!1,contrastAlgorithm:"aa",contentColor:me,paddingColor:me,borderColor:me,marginColor:me,eventTargetColor:me,shapeColor:me,shapeMarginColor:me};var q=(t=1e3)=>new Promise(e=>setTimeout(()=>e(),t));function tr(){cursor=document.createElement("img"),cursor.setAttribute("src","data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjMyIiB2aWV3Qm94PSIwIDAgMzIgMzIiIHdpZHRoPSIzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwIDcpIj48cGF0aCBkPSJtNi4xNDggMTguNDczIDEuODYzLTEuMDAzIDEuNjE1LS44MzktMi41NjgtNC44MTZoNC4zMzJsLTExLjM3OS0xMS40MDh2MTYuMDE1bDMuMzE2LTMuMjIxeiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Im02LjQzMSAxNyAxLjc2NS0uOTQxLTIuNzc1LTUuMjAyaDMuNjA0bC04LjAyNS04LjA0M3YxMS4xODhsMi41My0yLjQ0MnoiIGZpbGw9IiMwMDAiLz48L2c+PC9zdmc+"),cursor.setAttribute("id","selenium_cursor"),cursor.setAttribute("style","position: absolute; z-index: 99999999999; pointer-events: none; left:0; top:0"),cursor.style.filter="invert(0%) sepia(6%) saturate(24%) hue-rotate(315deg) brightness(89%) contrast(110%)",document.body.appendChild(cursor),document.onmousemove=function(t){t=t||window.event,document.getElementById("selenium_cursor").style.left=t.pageX+"px",document.getElementById("selenium_cursor").style.top=t.pageY+"px"}}function or(){window.globalHintManager||(window.globalHintManager=new window.HintManager),window.globalHintManager.capture()}function rr(){window.globalHintManager&&window.globalHintManager.reset()}function nr(){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 cs=new Set(["document","script","XMLHttpRequest","fetch","xhr"]),ds=new Set(["script","document"]),ms=["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"],us=["api.stripe.com","supabase.co"];function zt(t){return`${t.resourceType()} ${t.method()} ${t.url()}`}function sr(t){return t=t.replace(/^www\./,""),t}function ir(t){return us.some(e=>t.includes(e))}function ar(t,e){if(!cs.has(t.resourceType()))return!1;let o=new URL(e),r=new URL(t.url());return ms.some(n=>r.hostname.includes(n))?!1:ds.has(t.resourceType())||t.method()!=="GET"?!0:sr(r.hostname).includes(sr(o.hostname))}var at=Rs(Ts);at.use(Is());at.use(xs({provider:{id:"2captcha",token:process.env.TWO_CAPTCHA_KEY},visualFeedback:!0}));var I=class t{browser;context;page;a11yIdToNodeMap=new Map;dataMomenticIdToNodeMap=new Map;cdpClient;logger;localMode;activeFrame;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=mr["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 m={headless:!0,handleSIGTERM:!1,chromiumSandbox:!1,...r??{}},h={viewport:t.VIEWPORT,deviceScaleFactor:process.platform==="darwin"?2:1,userAgent:mr["Desktop Chrome"].userAgent,geolocation:{latitude:37.7749,longitude:-122.4194},locale:"en-US",timezoneId:"America/Los_Angeles",...n??{}},p=null,w,g;l?(w=await at.launchPersistentContext(Cs(vs(),"momentic","chromium"),{...m,...h,ignoreDefaultArgs:["--enable-automation"],ignoreHTTPSErrors:!0,bypassCSP:!0,args:["--allow-insecure-localhost","--disable-web-security","--disable-site-isolation-for-policy","--disable-site-isolation-trials",`--unsafely-treat-insecure-origin-as-secure=${c}`,`--load-extension=${d}`],baseURL:e}),g=w.pages()[0],g.on("close",()=>{a?.()})):(p=await at.launch(m),w=await p.newContext({...h,baseURL:e}),g=await w.newPage());let C=await w.newCDPSession(g),T=new t({browser:p,context:w,page:g,baseUrl:e,cdpClient:C,logger:o,localMode:l}),b=!1;(async()=>{try{await T.navigate({url:e,wrapPossibleNavigation:!1,initialNavigation:!0})}catch(ft){o.error({err:ft},"Failed to initialize chrome browser")}finally{b=!0}})();let v=async()=>{if(s)try{i?.({viewport:await T.viewport(),buffer:await T.screenshot()})}catch(ft){o.error({err:ft},"Failed to take screenshot")}};v();let te=setInterval(()=>{v()},250),Jt=Date.now();for(;!b&&Date.now()-Jt<u;)await q(250);return clearInterval(te),b||o.warn("Timeout elapsed waiting for browser to initialize - are you sure this page is accessible?"),T}async getUserPageOrFrame(){if(this.localMode&&this.activeFrame){let e=this.page.frame(this.activeFrame);if(!e)throw new Error(`Failed to get non-existent frame: ${this.activeFrame}`);return e}return this.page}async initCDPSession(e=2){try{await this.cdpClient.send("Accessibility.enable"),await this.cdpClient.send("DOM.enable"),await this.cdpClient.send("Overlay.enable")}catch(o){if(e>0)return this.logger.error({err:o},"Failed to initialize CDP session, re-creating CDP client"),this.cdpClient=await this.context.newCDPSession(this.page),await q(250),this.initCDPSession(e-1)}}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();e.clearCookies&&await this.context.clearCookies(),!this.page.isClosed()&&(e.clearStorage&&await(await this.getUserPageOrFrame()).evaluate(()=>{localStorage.clear()}),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 q(e)}async toggleHints(e){let o=await this.getUserPageOrFrame();e.state==="on"?(await o.addStyleTag({content:Mt.css}),await o.addScriptTag({content:Mt.js}),await o.evaluate(or)):await o.evaluate(rr)}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:3e3}),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(Go.has(i)&&process.env.NODE_ENV==="production")throw new Error(`${e} took too long to load \u{1F61E}. Please ensure the site and your internet are working.`);await this.pageSetup(),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(`Select 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:er,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=E=>{let v=zt(E.request());l.set(v,(l.get(v)??0)+1);let te=E.status();te>=500&&this.logger.warn({request:v,status:te},"Received 500 level response")},d=E=>{if(!ar(E,s))return;let v=zt(E);a.set(v,(a.get(v)??0)+1),i=Date.now()};this.page.on("response",c),this.page.on("request",d);let u=[];r&&(u=this.context.pages().map(E=>E.url()));let m=!1,h=e().catch(E=>(m=!0,E instanceof Error?E:new Error(`${E}`)));await q(250);let p=async E=>{let v=await E;if(v instanceof Error)throw v;return v},w=new Set,g=!1,T=await(async()=>{for(;!m&&!(!g&&Date.now()-n>o);){if(await q(250),g=!1,w=new Set,Date.now()-i<=1250)continue;let E=!1;for(let v of a.keys())a.get(v)!==l.get(v)&&(ir(v)&&(g=!0),E=!0,w.add(v));if(!E)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!m&&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),!T)return p(h);let b=await this.url();if(!m&&de(b,s)){this.logger.debug({startUrl:s,newUrl:b},"Detected url change in wrapPossibleNavigation, waiting for load state");let E=Math.max(o-(Date.now()-n),0);if(E>0)try{await(await this.getUserPageOrFrame()).waitForLoadState("load",{timeout:E})}catch{this.logger.warn({url:await this.url()},"Timeout elapsed waiting for load state, continuing anyways...")}}if(r){let E=this.context.pages().map(v=>v.url());if(E.length>u.length)for(let v of E)v!==b&&await this.switchToPage(v)}return p(h)}async resolveCachedTargetToID(e){if(!Ge(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 Pe(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=kt(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"),Pe(r,e),e.id}let n=1/0,s=1/0,i;for(let a of this.a11yIdToNodeMap.values()){let l=kt(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"),Pe(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=Es(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"),Pe(i,e),i.id;throw new Error(`Could not find any relevant node given cached target: ${JSON.stringify(e)}`)}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 await this.page.reload({timeout:3e3}),await this.pageSetup()}async getA11yTree(){await this.initCDPSession();let e=null,o=0,r=await this.url();for(;!e;)try{let n=await this.getRawA11yTree();if(!n.root||n.allNodes.length===0)throw new Error("No a11y tree found on page");e=Zo(n,this.logger)}catch(n){if(this.logger.error({err:n,url:r},"Error fetching a11y tree"),o===0)await q(1e3),o++;else throw new Error(`Max retries exceeded fetching a11y tree: ${n}`)}return e.root||this.logger.warn("A11y tree was pruned entirely"),this.a11yIdToNodeMap=e.a11yIdNodeMap,this.dataMomenticIdToNodeMap=e.dataMomenticIdMap,e}getA11yIdFromDataMomenticId(e){return this.dataMomenticIdToNodeMap.get(e)?.id}async getRawA11yTree(){let e=await this.url(),o=Date.now(),r=()=>{o=Date.now()};this.cdpClient.addListener("Accessibility.nodesUpdated",r);let n=!1,s=()=>{this.logger.info({url:e},"Load event fired on page"),n=!0,o=Date.now()};this.cdpClient.addListener("Accessibility.loadComplete",s);let i=Date.now(),a=!0;for(;Date.now()-i<3e3;){if(await q(250),!n&&Date.now()-i<1e3){process.env.NODE_ENV!=="production"&&this.logger.debug({url:e},"A11y tree not loaded yet, waiting...");continue}if(Date.now()-o>=1250){a=!1;break}this.logger.debug({url:e},"A11y tree not stable yet, waiting...")}this.logger.debug({duration:Date.now()-i,eventReceived:n,timeoutTriggered:a},"A11y wait phase completed"),await(await this.getUserPageOrFrame()).evaluate(nr);let l;if(this.localMode&&this.activeFrame){let{result:{objectId:d}}=await this.cdpClient.send("Runtime.evaluate",{expression:`document.querySelector("#${this.activeFrame}")`}),u=await this.cdpClient.send("DOM.describeNode",{objectId:d});l=(await this.cdpClient.send("Accessibility.getRootAXNode",{frameId:u.node.frameId})).node.backendDOMNodeId}else{let{node:d}=await this.cdpClient.send("Accessibility.getRootAXNode");l=d.backendDOMNodeId}let{nodes:c}=await this.cdpClient.send("Accessibility.queryAXTree",{backendNodeId:l});return this.cdpClient.removeListener("Accessibility.loadComplete",s),this.cdpClient.removeListener("Accessibility.nodesUpdated",r),{root:c[0],allNodes:c}}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==="data-momentic-id");return s===-1?"":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}`);try{let r=await this.getIDAttributeUsingCDP(o.object.objectId);if(!r)throw new Error("Failed getting data-momentic-id attribute using CDP");return(await this.getUserPageOrFrame()).locator(`[data-momentic-id="${r}"]`)}catch(r){throw this.logger.error({err:r},"Failed to get ID attribute"),r}}async clickUsingCDP(e,o={}){let r=0,n=e;for(;r<Vo;){if(!n||n.role==="RootWebArea")throw new Error(`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 T=0;T<a.length;T++)if(l[T]===e){u=c.indexOf(T);break}if(u===-1)throw new Error(`Could not find any backend node with ID ${e}`);let[m=0,h=0,p=0,w=0]=d[u];m/=r,h/=r,p/=r,w/=r;let g=m+p/2,C=h+w/2;return{centerX:g,centerY:C}}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})),await this.pageSetup()}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})),await this.pageSetup()}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;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.pageSetup(),await this.initCDPSession();return}}throw new Error(`Could not find page with url containing ${e}`)}async setCookie(e){let o=io(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 q(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}};var be=async({controller:t,step:e,logger:o,advanced:r,takeScreenshots:n,...s})=>{s.onStarted?.(),t.resetHistory();let i={...e,startedAt:new Date,userAgent:I.USER_AGENT,finishedAt:new Date,results:[],status:"SUCCESS"};try{let a=0,l=e.commands&&e.commands.length>0;for(;;){if(a>20)throw new Error(`Exceeded max number of commands per step (${20})`);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 h=await t.browser.screenshot();u=await s.onSaveScreenshot(h)}if(l){if(c=e.commands[a],!c)throw new Error(`Saved command at index ${a} is undefined.`)}else{o.info(`Executing sub-command ${a} within AI step`);let h=await t.promptToCommand(e.type,e.text,r.disableAICaching);if(c=h.command,c.type==="FAILURE"){i.finishedAt=new Date,i.status="FAILED",i.message=c.thoughts;break}let p=a;(async()=>{if("target"in c&&c.target){await new Promise(g=>setTimeout(()=>g(),500));try{let g=await Ls(t,h.context,c.target);c.target.elementDescriptor=g,s.onCommandExecuted?.({commandIndex:p,command:c})}catch(g){o.error({err:g},"Failed to generate element description, continuing...")}}})()}s.onCommandGenerated?.({commandIndex:a,message:wt[c.type]||`Unknown command (${c.type})`});let m={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: ${Se(c)}`);try{let h=await t.executeCommand(c,r.disableAICaching,l);o.info(`AI sub-command ${a} completed successfully`),m.elementInteracted=h.elementInteracted;let p;if(n){let g=await t.browser.screenshot();p=await s.onSaveScreenshot(g)}m.afterScreenshot=p,m.afterUrl=await t.browser.url(),m.finishedAt=new Date;let w={status:"SUCCESS",startedAt:m.startedAt,finishedAt:m.finishedAt,type:"PRESET_ACTION",data:h.data,command:c,results:[m]};if(i.results.push(w),s.onCommandExecuted?.({commandIndex:a,output:w,command:c}),c.type==="SUCCESS"){i.finishedAt=new Date,i.status="SUCCESS",i.message=h.thoughts??"All commands completed.";break}if(h.succeedImmediately&&!l){i.finishedAt=new Date,i.status="SUCCESS",i.message=h.succeedImmediatelyReason,c={type:"SUCCESS"},s.onCommandExecuted?.({commandIndex:a+1,output:w,command:c}),i.results.push({...w,command:c});break}}catch(h){if(l){l=!1,a=0,i.results=[];continue}let p=h instanceof Error?h.message:`${h}`;m.status="FAILED",m.message=p,m.finishedAt=new Date,m.afterUrl=await t.browser.url();let w;try{w=await t.browser.screenshot()}catch(g){o.warn({err:g},"Failed to take screenshot after error, skipping")}m.afterScreenshot=w,i.results.push({status:"FAILED",startedAt:m.startedAt,finishedAt:m.finishedAt,type:"PRESET_ACTION",command:c,results:[m],message:p}),i.status="FAILED",i.finishedAt=new Date,i.message=p;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 Ls(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 Ae=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 p=await t.browser.screenshot();c=await n.onSaveScreenshot(p)}let d=new Date,u={...e,startedAt:s,finishedAt:d,status:"SUCCESS",data:l.data,results:[]},m="Successfully executed preset action.";e.command.type==="AI_ASSERTION"&&(m=l.thoughts||"Assertion passed.");let h={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=[h],u.message=m,n.onSuccess?.({message:m,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 h={...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:h}),h}let m={...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:m}),m}};var lt=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:I.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}: ${Ze(l)}`);let c;switch(l.type){case"PRESET_ACTION":c=await Ae({controller:t,step:l,advanced:o,logger:r,takeScreenshots:n,onSaveScreenshot:s.onSaveScreenshot,onStarted(){s.onStepStarted?.({index:a})},onSuccess({message:u,startedAt:m,durationMs:h,output:p}){s.onStepSuccess?.({index:a,message:u,startedAt:m,durationMs:h,output:p})},onFailure({message:u,startedAt:m,durationMs:h,output:p}){s.onStepFailure?.({index:a,message:u,startedAt:m,durationMs:h,output:p})},onCancelled({message:u,startedAt:m,durationMs:h,output:p}){s.onStepCancelled?.({index:a,message:u,startedAt:m,durationMs:h,output:p})}});break;case"AI_ACTION":c=await be({controller:t,step:l,advanced:o,logger:r,takeScreenshots:n,onSaveScreenshot:s.onSaveScreenshot,onStarted(){s.onStepStarted?.({index:a})},onSuccess({message:u,startedAt:m,durationMs:h,output:p}){s.onStepSuccess?.({index:a,message:u,startedAt:m,durationMs:h,output:p})},onFailure({message:u,startedAt:m,durationMs:h,output:p}){s.onStepFailure?.({index:a,message:u,startedAt:m,durationMs:h,output:p})},onCommandGenerated({commandIndex:u,message:m}){s.onCommandGenerated?.({index:a,commandIndex:u,message:m})},onCommandExecuted({commandIndex:u,command:m,output:h}){s.onCommandExecuted?.({index:a,commandIndex:u,command:m,output:h})}});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 m={...e.steps[d],status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:I.USER_AGENT,results:[],message:"Cancelled due to previous failure."};i.results.push(m)}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 Bm}from"uuid";var jm=process.env.AWS_ACCESS_KEY_ID,Gm=process.env.AWS_SECRET_ACCESS_KEY,Vm=process.env.S3_REGION,Wm=process.env.S3_BUCKET;var pr=async({test:t,runId:e,controller:o,logger:r,takeScreenshots:n,onUpdateRun:s,onSaveScreenshot:i})=>{try{let a=await Os({test:t,runId:e,controller:o,logger:r,takeScreenshots:n,onUpdateRun:s,onSaveScreenshot:i});if(a==="PASSED"||a==="CANCELLED")return a;throw new x("InternalPlatformError",a.message||"An unknown platform error occurred")}catch(a){throw a instanceof x||(a=new x("InternalPlatformError",a instanceof Error?a.message:`${a}`,{cause:a})),a}finally{await o.browser.cleanup()}},Os=async({test:t,runId:e,controller:o,logger:r,takeScreenshots:n,onUpdateRun:s,onSaveScreenshot:i})=>{let a=ee.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 m=0;m<t.steps.length;m++){let h=t.steps[m];l.info(`Starting step ${m+1}/${t.steps.length}: ${Ze(h)}`);let p;switch(h.type){case"PRESET_ACTION":p=await Ae({controller:o,step:h,advanced:a,logger:l,takeScreenshots:n,onSaveScreenshot:i});break;case"AI_ACTION":p=await be({controller:o,step:h,advanced:a,logger:l,takeScreenshots:n,onSaveScreenshot:i});break;case"RESOLVED_MODULE":p=await lt({controller:o,step:h,advanced:a,logger:l,takeScreenshots:n,onSaveScreenshot:i});break;default:return(g=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(h)}if(u.push(p),await s({results:u}),p.status==="SUCCESS"){l.info(`Step ${m+1}/${t.steps.length} succeeded`);continue}if(p.status!=="FAILED"&&p.status!=="CANCELLED")throw new x("InternalPlatformError",`Received unexpected non-terminal status from step: ${p.status}`);l.info({message:u[u.length-1]?.message},`Step ${m+1}/${t.steps.length} ended with status: ${p.status}`),c=p,d=p.status;for(let w=m+1;w<t.steps.length;w++){let g=t.steps[w];if(g.type==="RESOLVED_MODULE"){let C={type:"MODULE",moduleId:g.moduleId,startedAt:new Date,userAgent:I.USER_AGENT,results:g.steps.map(T=>({...T,status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:I.USER_AGENT,results:[]})),finishedAt:new Date,status:"CANCELLED"};u.push(C)}else{let C={...g,status:"CANCELLED",startedAt:new Date,finishedAt:new Date,userAgent:I.USER_AGENT,results:[]};u.push(C)}}break}return await s({status:d,finishedAt:new Date,results:u}),d==="FAILED"?c:d};var hr={currentlyExecutingRequests:0},Ms=t=>async e=>{try{hr.currentlyExecutingRequests++,await Ds({...t,...e})}finally{hr.currentlyExecutingRequests--}},Ds=async({socket:t,steps:e,testMetadata:o,reInitialize:r,indicesFilter:n,metadata:s,logger:i,rootController:a,localApp:l})=>{let{testId:c,sessionId:d,orgId:u}=s,m={testId:c,orgId:u},h=i.child(m),p=i.child({package:"web-agent",...m}),w=!0;l==="iframe"&&(w=!1);let g=ee.parse(o.advanced??{}),C=k.getController(d);if(!C)throw new Error("No controller found");C.setOpen();let T=o.baseUrl,b=I.USER_AGENT;h.info({reInitialize:r,steps:e,indicesFilter:n},"Starting execution");let E={url:T,clearCookies:!0,clearStorage:!0};if(r&&(a?(a.browser.baseURL=T,a.browser.setActiveFrame(Je),await a.resetState(E),C.setLogger(p),C.browser.setLogger(p)):await C.resetState(E),C.setOpen()),h.info(`Session restarted for test ${c} at ${T}`),t.emit("session",{url:T,userAgent:b,viewport:await C.browser.viewport(),localApp:l}),n?.length){let v=Ps(e,n);h.info({step:v},"Starting individual step"),t.emit("started",{indices:n}),await fr({indices:n,step:v,controller:C,advanced:g,socket:t,logger:h,takeScreenshots:w}),t.emit("finished");return}for(let v=0;v<e.length&&!(C.browser.closed||C.isClosed()||t.disconnected);v++){let te=e[v];if(h.info({index:v,step:te},"Starting step"),!await fr({indices:[v],step:te,controller:C,advanced:g,socket:t,logger:h,takeScreenshots:w}))break}t.emit("finished")},Ns=(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}`)}},Ps=(t,e)=>{let o=t[e[0]];for(let r=1;r<e.length;r++)o=Ns(o,e[r]);return o},Ft=async t=>t,fr=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:m,durationMs:h,command:p,output:w})=>{n.emit("success",{indices:t,message:u,startedAt:m,durationMs:h,command:p,output:w})},c=({message:u,startedAt:m,durationMs:h,output:p})=>{n.emit("failure",{indices:t,message:u,startedAt:m,durationMs:h,output:p})},d=({message:u,startedAt:m,durationMs:h,output:p})=>{n.emit("cancelled",{indices:t,message:u,startedAt:m,durationMs:h,output:p})};switch(e.type){case"PRESET_ACTION":return(await Ae({controller:o,step:e,advanced:r,logger:s,takeScreenshots:i,onSaveScreenshot:Ft,onStarted:a,onSuccess:l,onFailure:c,onCancelled:d})).status==="SUCCESS";case"AI_ACTION":return(await be({controller:o,step:e,advanced:r,logger:s,takeScreenshots:i,onSaveScreenshot:Ft,onStarted:a,onSuccess:l,onFailure:c,onCommandGenerated({commandIndex:h,message:p}){n.emit("commandGenerated",{indices:[...t,h],message:p})},onCommandExecuted({commandIndex:h,command:p,output:w}){n.emit("commandExecuted",{indices:[...t,h],command:p,output:w})}})).status==="SUCCESS";case"RESOLVED_MODULE":return(await lt({controller:o,step:e,advanced:r,logger:s,takeScreenshots:i,onSaveScreenshot:Ft,onStarted:a,onSuccess:l,onFailure:c,onCancelled:d,onStepStarted({index:h}){n.emit("started",{indices:[...t,h]})},onStepSuccess({index:h,message:p,startedAt:w,durationMs:g,output:C}){n.emit("success",{indices:[...t,h],message:p,startedAt:w,durationMs:g,output:C})},onStepFailure({index:h,message:p,startedAt:w,durationMs:g,output:C}){n.emit("failure",{indices:[...t,h],message:p,startedAt:w,durationMs:g,output:C})},onStepCancelled({index:h,message:p,startedAt:w,durationMs:g,output:C}){n.emit("cancelled",{indices:[...t,h],message:p,startedAt:w,durationMs:g,output:C})},onCommandGenerated({commandIndex:h,index:p,message:w}){n.emit("commandGenerated",{indices:[...t,p,h],message:w})},onCommandExecuted({index:h,commandIndex:p,command:w,output:g}){n.emit("commandExecuted",{indices:[...t,h,p],command:w,output:g})}})).status==="SUCCESS";default:return(m=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(e)}};var gr={event:"execute",createHandler:Ms};var _s=({metadata:t,logger:e})=>{let{sessionId:o}=t;return async({description:r,useVision:n,testMetadata:s},i)=>{let a=k.getController(o);if(!a)throw new Error("No controller found");let l=ee.parse(s.advanced??{}),c,d;try{c=await a.locateElement(r,n,l.disableAICaching),d={id:c.id},await a.browser.resolveCachedTargetToID(d)}catch(u){e.error({err:u},"Error locating element"),i({found:!1});return}e.info({locator:c,a11yData:d},"Located element result");try{await a.browser.scrollIntoView({id:c.id}),await a.browser.highlight({id:c.id})}catch(u){e.error({err:u,locator:c},"Error highlighting element"),i({found:!1});return}i({found:!0,locator:c,a11yData:d})}},yr={event:"locate",createHandler:_s};var ks=({socket:t,metadata:e,rootController:o,logger:r,localApp:n})=>async()=>{let{testId:s,sessionId:i}=e,a;if(o)await o.resetState({clearCookies:!0,clearStorage:!0}),a=o.browser.baseURL;else{let c=k.getController(i);if(!c){t.emit("error",{message:"No session to reset"});return}await c.browser.reset({clearCookies:!0,clearStorage:!0}),a=c.browser.baseURL}let l=I.USER_AGENT;r.info(`Session restarted for test ${s} at ${a}`),t.emit("session",{url:a,userAgent:l,viewport:I.VIEWPORT,localApp:n})},Sr={event:"reset",createHandler:ks};import{v4 as Hs}from"uuid";var zs={type:"a11y",version:"1.0.0",useHistory:"diff",useGoalSplitter:!0},ue=zs;import Us from"dedent";import Fs from"diff-lines";var $s=1e4,Y=class{browser;pendingInstructions;generator;commandHistory;config;closed=!1;logger;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 x?n:new x("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 m=this.pendingInstructions.pop();if(this.logger.info({finishedInstruction:m,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 x("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 x("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 x("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=[`
21
+ 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(de(s.urlBeforeCommand,e))n.push(` URL CHANGE: '${s.urlBeforeCommand}' -> '${e}'`);else{let a=Fs(s.browserStateBeforeCommand,o,{n_surrounding:1});a?a.length<$s?(n.push("PAGE CONTENT CHANGE:"),a.split(`
22
22
  `).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(`
23
- `)}getListHistory(){return Ps`Here are the commands that you have successfully executed:
23
+ `)}getListHistory(){return Us`Here are the commands that you have successfully executed:
24
24
  ${this.commandHistory.filter(e=>e.type==="AI_ACTION").map(e=>`- ${e.serializedCommand}`).join(`
25
- `)}`}async executeCommand(e,o,r=!1){let n=this.commandHistory[this.commandHistory.length-1];if(!r&&(!n||n.state!=="PENDING"))throw new R("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;try{let i=Date.now();s=await this.executePresetStep(e,o);let a=Date.now()-i;this.logger.debug({result:s,duration:a},"Got execution result")}catch(i){throw i instanceof Error?new Re(`Failed to execute command: ${i}`,{cause:i}):new Re("Unexpected throw from executing command",{cause:new Error(`${i}`)})}return s.succeedImmediately&&!r&&(this.pendingInstructions.pop(),this.pendingInstructions.length>0&&(s.succeedImmediately=!1)),s.elementInteracted&&"target"in e&&e.target&&!e.target.elementDescriptor&&(e.target.elementDescriptor=s.elementInteracted.trim()),r||(n.generatedStep=e,n.serializedCommand=Se(e),n.state="DONE"),s}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 R("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 R("InternalWebAgentError","Cannot target element with no target data or element descriptor");let i=e.a11yData&&je(e.a11yData);e.a11yData||(this.logger.debug("No cached locator data for target, prompting AI for fresh location"),s--,e.a11yData=Ge.parse(await this.locateElement(e.elementDescriptor,o,r)));try{let a=await n(e.a11yData);return i?this.logger.debug({target:e},"Successfully used cached target to perform action"):this.logger.debug({target:e},"Successfully generated and used new a11y target information"),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 R)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 R("ActionFailureError",l,{cause:a})}}async executePresetStep(e,o){try{return await this.executePresetStepHelper(e,o)}catch(r){throw r instanceof R?r:new R("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(!Ot(e.url)&&!Mt(e.url,this.browser.baseURL))throw new R("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(),d=await this.wrapElementTargetingCommand(e.target,e.useVision,o,u=>this.browser.click(u,{doubleClick:e.doubleClick,rightClick:e.rightClick,force:e.force})),c={urlAfterCommand:await this.browser.url(),succeedImmediately:!1,elementInteracted:d};return ce(l,c.urlAfterCommand)&&(c.succeedImmediately=!0,c.succeedImmediatelyReason="URL changed"),c}case"SELECT_OPTION":{let l=await this.wrapElementTargetingCommand(e.target,!1,o,d=>this.browser.selectOption(d,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"TYPE":{let l=await this.browser.url(),d=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 c={urlAfterCommand:await this.browser.url(),succeedImmediately:!1,elementInteracted:d};return ce(l,c.urlAfterCommand)&&(c.succeedImmediately=!0,c.succeedImmediatelyReason="URL changed"),c}case"HOVER":{let l=await this.wrapElementTargetingCommand(e.target,e.useVision,o,d=>this.browser.hover(d));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 ce(s,i.urlAfterCommand)&&(i.succeedImmediately=!0,i.succeedImmediatelyReason="URL changed"),i;case"REQUEST":{let l=e.timeout??30,d=null,c=new AbortController,u=Object.fromEntries(Object.entries(e.headers||{}).filter(([v,b])=>v&&b)),m=new URLSearchParams;Object.entries(e.params||{}).filter(([v,b])=>v&&b).forEach(([v,b])=>{m.append(v,b)});let p;if(Ot(e.url)&&(p=e.url),Mt(e.url,this.browser.baseURL)&&(p=new URL(e.url,this.browser.baseURL).toString()),!p)throw new R("ActionFailureError",`Invalid URL: ${e.url}`);let f=async()=>{try{d=await fetch(`${p}?${m.toString()}`,{headers:u,method:e.method,body:e.body,signal:c.signal})}catch(v){this.logger.error({err:v},"Failed to make fetch request")}},w=async()=>{await new Promise(v=>setTimeout(v,l*1e3)),c.abort()};await Promise.race([w(),f()]);let g=d;if(!g)throw new R("ActionFailureError",`Fetch request timed out after ${l} seconds`);if(!g.ok)throw new R("ActionFailureError",`Fetch request failed with status ${g.status}`);let N={status:g.status,headers:g.headers};return g.headers.get("content-type")?.includes("json")?N.json=await g.json():N.text=await g.text(),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),data:N}}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 function gr({socket:t,generator:e,logger:o,emitScreenshots:r,rootController:n}){let s=o.child({package:"web-agent"}),i=t.handshake.query.testId,a=t.handshake.query.baseUrl;if(!i)throw new Error("Socket connection is missing testID");let l=t.handshake.query.orgId,d=zs(),c=p=>{t.emit("screenshot",{...p,url:a})},u=n;if(u)u.browser.setActiveFrame(Je);else{let p=await x.init({baseUrl:a,logger:s,sendScreenshotsDuringLoad:r?c:void 0});u=new Y({browser:p,generator:e,config:me,logger:s})}r&&Yt(t,d,o),k.registerSession(u,d);let m=x.USER_AGENT;return t.emit("session",{url:a,userAgent:m,viewport:await u.browser.viewport()}),{sessionId:d,testId:i,orgId:l}}var yr=[pr,Xt,fr,hr];var Sr=t=>{let{logger:e}=t,o=new Us(t.baseServer,{cors:{origin:"*",methods:["GET","POST"]}});return o.on("connection",async r=>{let n;try{n=await gr({...t,socket:r})}catch(s){e.error({event:"connection",type:"websocket",err:s},"Failed to setup connection"),r.emit("error",{message:`${s}`});return}yr.forEach(s=>Fs(s,{socket:r,metadata:n,...t}))}),o},Fs=(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 $s from"fetch-retry";var Hs=$s(global.fetch),ue="v1",pe=class{baseURL;apiKey;constructor(e){this.baseURL=e.baseURL,this.apiKey=e.apiKey}async getElementLocation(e,o){let r=await this.sendRequest(`/${ue}/web-agent/locate-element`,{browserState:e.browserState,goal:e.goal,disableCache:o});return Ct.parse(r)}async getElementLocationWithVision(e,o){let r=await this.sendRequest(`/${ue}/web-agent/visual-locate`,{goal:e.goal,screenshot:e.screenshot?.toString("base64"),hintActivatedScreenshot:e.hintActivatedScreenshot?.toString("base64"),disableCache:o});return Ct.parse(r)}async getAssertionResult(e,o,r){if(o){let s=await this.sendRequest(`/${ue}/web-agent/assertion`,{url:e.url,goal:e.goal,screenshot:e.screenshot?.toString("base64"),disableCache:r,vision:!0});return vt.parse(s)}let n=await this.sendRequest(`/${ue}/web-agent/assertion`,{url:e.url,browserState:e.browserState,goal:e.goal,history:e.history,numPrevious:e.numPrevious,lastCommand:e.lastCommand,disableCache:r,vision:!1});return vt.parse(n)}async getProposedCommand(e,o){let r=await this.sendRequest(`/${ue}/web-agent/next-command`,{url:e.url,browserState:e.browserState,goal:e.goal,history:e.history,numPrevious:e.numPrevious,lastCommand:e.lastCommand,disableCache:o});return So.parse(r)}async getGranularGoals(e,o){let r=await this.sendRequest(`/${ue}/web-agent/split-goal`,{url:e.url,goal:e.goal,disableCache:o});return wo.parse(r)}async getReverseMappedDescription(e,o){let r=await this.sendRequest(`/${ue}/web-agent/reverse-mapped-description`,{goal:e.goal,browserState:e.browserState,disableCache:o});return bo.parse(r)}async sendRequest(e,o){let r=await Hs(`${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 Bs,readFileSync as Gs,writeFileSync as Ut}from"fs";import Ft from"path";import{parse as js,stringify as Vs}from"yaml";var _=process.env.MOMENTIC_DIR??ie;function wr(t,e){let o=Oo(t);for(let[s,i]of Object.entries(o.modules)){let a=Me(s);Ut(Ft.join(_,V,`${a}.yaml`),i,"utf-8")}let r=Me(e),n=Ft.join(_,`${r}.yaml`);return Ut(n,o.test,"utf-8"),`${r}.yaml`}function br(t,e){if(!Bs(e))throw new Error(`Test not found at path: ${Ft}`);let o=Gs(e,"utf-8"),n={...js(o),...t},s=Vs(n);Ut(e,s,"utf-8")}import{readFileSync as ct,readdirSync as Ar,writeFileSync as Er}from"fs";import{join as $t}from"path";import{v4 as Ws}from"uuid";import{parse as dt,stringify as vr}from"yaml";var ke=$t(_,V);function Cr(t,e){let o=rt(ke,e).path,r=ct(o,"utf-8"),s={...dt(r),...t},i=vr(s);Er(o,i,"utf-8")}function Tr(t,e){let o=Me(t),r=$t(ke,`${o}.yaml`),n={fileType:"momentic/module",schemaVersion:z,moduleId:Ws(),name:t,steps:e},s=vr(n);return Er(r,s,"utf-8"),{type:"RESOLVED_MODULE",moduleId:n.moduleId,name:n.name,steps:n.steps}}function Rr(){let t=[];for(let e in Ar(ke)){if(!e.endsWith(".yaml"))continue;let o=ct(e,"utf-8"),r=dt(o),n={name:r.name,moduleId:r.moduleId,numSteps:r.steps.length};t.push(n)}return t}function xr(){let t=[];for(let e of Ar(ke)){if(!e.endsWith(".yaml"))continue;let o=$t(ke,e),r=ct(o,"utf-8"),n=dt(r);try{let s=Tt.parse(n);t.push(s)}catch(s){y.warn({err:s},"Error parsing module, skipping...")}}return t}function Ir(t){let e=rt(V,t).path,o=ct(e,"utf-8"),r=dt(o);return Tt.parse(r)}import{Router as Ks}from"express";function G(t){return function(...e){let o=e[e.length-1],r=t(...e);Promise.resolve(r).catch(o)}}var ze=Ks();ze.get("/",G((t,e)=>{let r=xr().map(n=>({...n,type:"RESOLVED_MODULE"}));e.status(200).json(r)}));ze.get("/metadata",G((t,e)=>{let o=Rr();e.status(200).json(o)}));ze.post("/",G((t,e)=>{let o;try{o=fo.parse(t.body)}catch(n){e.status(400).json({error:`Invalid request body: ${n}`});return}try{De(o.name)}catch(n){e.status(400).json({error:`Invalid module name: ${n}`});return}let r=Tr(o.name,o.steps);e.status(201).json(r)}));ze.get("/:moduleId",G((t,e)=>{if(!t.params.moduleId){e.status(400).json({error:"Missing moduleId in url path."});return}let o=Ir(t.params.moduleId);e.json(o)}));var Lr=ze;import{Router as qs}from"express";import{existsSync as Ys}from"fs";import{join as Ht}from"path";import{v4 as Xs}from"uuid";var Ue=qs();Ue.get("/",G((t,e)=>{let o=Ne(_).filter(r=>r.localOnly);e.status(200).json(o)}));Ue.post("/",G((t,e)=>{let o;try{o=ho.parse(t.body)}catch(a){e.status(400).json({error:`Invalid request body: ${a}`});return}try{De(o.name)}catch(a){e.status(400).json({error:a.message});return}let n={id:Xs(),name:o.name,baseUrl:o.baseUrl,schemaVersion:z,advanced:{disableAICaching:!1,availableAsModule:!0},retries:0,steps:[],localOnly:!0},s=wr(n,o.name),i={...n,testPath:s};e.status(201).json(i)}));Ue.get("/:testPath",G(async(t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let o=Ht(_,t.params.testPath);if(!Ys(o)){e.status(400).json({error:`Test not found at path: ${o}`});return}let r=await nt(o,Ht(_,V));e.status(200).json(r)}));Ue.patch("/:testPath",G((t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let o;try{o=po.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))Cr({steps:i},s);br({steps:r},Ht(_,t.params.testPath)),e.status(201).json({message:"ok"})}));var Or=Ue;var mt=process.env.MOMENTIC_SERVER??"https://api.momentic.ai";var Js,Dr=t=>{Js=t};var Mr,Nr=async t=>{if(Mr)return;let e=await fetch(`${mt}/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}=Io.parse(o);Mr=r};async function Pr(t,e,o,r){if(!ei(_)||!ti(_).isDirectory()){let c=ri.isAbsolute(_);throw new Error(`Root folder ${_} does not exist${c?"":` in the current directory ("${process.cwd()}")`}. Please run \`npx momentic init\` if you wish to setup Momentic here for the first time.`)}y.info("Checking API key"),await Nr(t);let s=`http://localhost:${e}`;o&&(s=`http://localhost:${o}`);let i=ni(r);await new Promise(c=>{i.listen(e,()=>{y.info(`Server is running at http://localhost:${e}`),c()})});let[l,d]=await si(s,t,()=>{y.info("Browser closed, closing app."),i.close(),process.exit(0)});await ii(i,l,d)}function ni(t){let e=Bt();e.use(Zs()),e.use(Qs.json({limit:"50mb"}));let o=Bt.Router();if(o.use("/tests",Or),o.use("/modules",Lr),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=Bt.static(t);e.use("/",n),e.use("*",n)}return oi.createServer(e)}async function si(t,e,o){let r=await x.init({baseUrl:t,logger:y,browserArgs:{headless:!1,handleSIGTERM:!0},contextArgs:{bypassCSP:!0,viewport:null,deviceScaleFactor:void 0},localMode:!0,localAppUrl:t,onClose:o}),n=new pe({baseURL:mt,apiKey:e}),s=new Y({browser:r,config:me,generator:n,logger:y});return Dr(s),[s,n]}async function ii(t,e,o){Sr({baseServer:t,generator:o,logger:y,emitScreenshots:!1,rootController:e})}import{existsSync as Ni}from"fs";import Pi,{dirname as _i}from"path";import{fileURLToPath as ki}from"url";var _r="0.0.22";var X="v1",Ee=class{baseURL;apiKey;constructor(e){this.baseURL=e.baseURL,this.apiKey=e.apiKey}async getRun(e){let o=await this.sendRequest(`/${X}/runs/${e}`,{method:"GET"});return Ro.parse(o)}async createRun(e){let o=await this.sendRequest(`/${X}/runs`,{method:"POST",body:e});return To.parse(o)}async updateRun(e,o){await this.sendRequest(`/${X}/runs/${e}`,{method:"PATCH",body:o})}async getTest(e){let o=await this.sendRequest(`/${X}/tests/${e}`,{method:"GET"});return Eo.parse(o)}async getAllTestIds(){let e=await this.sendRequest(`/${X}/tests`,{method:"GET"});return vo.parse(e)}async getTestYAMLExport(e){let o=await this.sendRequest(`/${X}/tests/export`,{method:"POST",body:e});return Co.parse(o)}async updateTestWithYAML(e){await this.sendRequest(`/${X}/tests/update`,{method:"POST",body:e})}async queueTests(e){let o=await this.sendRequest(`/${X}/tests/queue`,{method:"POST",body:e});return Ao.parse(o)}async uploadScreenshot(e){let o=await this.sendRequest(`/${X}/screenshots`,{method:"POST",body:e});return xo.parse(o)}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{existsSync as di,mkdirSync as Gt,statSync as mi}from"fs";import{join as kr}from"path";import li from"chalk";import ci from"readline/promises";async function ve(t,e){if(process.env.CI)return!0;let o=ci.createInterface({input:process.stdin,output:process.stdout});t=`${t} (y/N) `;let r=e?li.bold.yellow(t):t,n=await o.question(r);return o.close(),n.toLowerCase()==="y"}var ut=ie,he=kr(ie,V),jt=kr(ie,uo);function ui(t){return di(t)&&mi(t).isDirectory()}async function Fe(t=!1){ui(ut)||(!t&&!await ve(`A '${ie}' folder was not found in the current directory. Setup the required Momentic folder structure?`)&&(y.error("Setup cancelled"),process.exit(1)),Gt(ut),Gt(he),Gt(jt),y.info("Setup complete!"))}import{registry as Vt}from"playwright-core/lib/server";function pi(t){let e=[],o=[];for(let r of t){let n=Vt.findExecutable(r);!n||n.installType==="none"?e.push(r):o.push(n)}return o}async function zr(){let t=pi(["chromium"]);await Vt.installDeps(t,!1),await Vt.install(t,!1)}import{Argument as Wt,Option as $e}from"commander";var He=new $e("--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),pt=new $e("--server <server>","Momentic server to use. Leave unchanged unless using Momentic on-premise.").default("https://api.momentic.ai"),Be=new $e("-y, --yes","Skip confirmation prompts.").env("CI").default(!1),Ur=new $e("--no-report","Skip reporting test results to Momentic Cloud when running with the --local flag.").default(!0),Kt=new $e("-a --all","Select all tests in your organization from Momentic Cloud. Cannot be used together with <tests> arguments.").default(!1).preset(!0),Fr=new Wt("<tests...>",`One or more test paths to pull from Momentic Cloud.
25
+ `)}`}async executeCommand(e,o,r=!1){let n=this.commandHistory[this.commandHistory.length-1];if(!r&&(!n||n.state!=="PENDING"))throw new x("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;try{let i=Date.now();s=await this.executePresetStep(e,o);let a=Date.now()-i;this.logger.debug({result:s,duration:a},"Got execution result")}catch(i){throw i instanceof Error?new Re(`Failed to execute command: ${i}`,{cause:i}):new Re("Unexpected throw from executing command",{cause:new Error(`${i}`)})}return s.succeedImmediately&&!r&&(this.pendingInstructions.pop(),this.pendingInstructions.length>0&&(s.succeedImmediately=!1)),s.elementInteracted&&"target"in e&&e.target&&!e.target.elementDescriptor&&(e.target.elementDescriptor=s.elementInteracted.trim()),r||(n.generatedStep=e,n.serializedCommand=Se(e),n.state="DONE"),s}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 x("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 x("ActionFailureError","Cannot target element with no target data or element descriptor");let i=e.a11yData&&Ge(e.a11yData);e.a11yData||(this.logger.debug("No cached locator data for target, prompting AI for fresh location"),s--,e.a11yData=je.parse(await this.locateElement(e.elementDescriptor,o,r)));try{let a=await n(e.a11yData);return i?this.logger.debug({target:e},"Successfully used cached target to perform action"):this.logger.debug({target:e},"Successfully generated and used new a11y target information"),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 x)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 x("ActionFailureError",l,{cause:a})}}async executePresetStep(e,o){try{return await this.executePresetStepHelper(e,o)}catch(r){throw r instanceof x?r:new x("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(!Dt(e.url)&&!Nt(e.url,this.browser.baseURL))throw new x("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 de(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"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 de(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 de(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(([b,E])=>b&&E)),m=new URLSearchParams;Object.entries(e.params||{}).filter(([b,E])=>b&&E).forEach(([b,E])=>{m.append(b,E)});let h;if(Dt(e.url)&&(h=e.url),Nt(e.url,this.browser.baseURL)&&(h=new URL(e.url,this.browser.baseURL).toString()),!h)throw new x("ActionFailureError",`Invalid URL: ${e.url}`);let p=async()=>{try{c=await fetch(`${h}?${m.toString()}`,{headers:u,method:e.method,body:e.body,signal:d.signal})}catch(b){this.logger.error({err:b},"Failed to make fetch request")}},w=async()=>{await new Promise(b=>setTimeout(b,l*1e3)),d.abort()};await Promise.race([w(),p()]);let g=c;if(!g)throw new x("ActionFailureError",`Fetch request timed out after ${l} seconds`);if(!g.ok)throw new x("ActionFailureError",`Fetch request failed with status ${g.status}`);let C={};g.headers.forEach((b,E)=>{C[E]=b});let T={status:g.status,headers:C};return g.headers.get("content-type")?.includes("json")?T.json=await g.json():g.headers.get("content-type")?.includes("text")&&(T.text=await g.text()),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),data:T}}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 function wr({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=Hs(),u=p=>{t.emit("screenshot",{...p,url:l})},m=r;if(m)m.browser.setActiveFrame(Je);else{let p=await I.init({baseUrl:l,logger:s,takeScreenshots:i,onScreenshot:u});m=new Y({browser:p,generator:e,config:ue,logger:s})}i&&Qt(t,d,o),k.registerSession(m,d);let h=I.USER_AGENT;return t.emit("session",{url:l,userAgent:h,viewport:await m.browser.viewport(),localApp:n}),{sessionId:d,testId:a,orgId:c}}var br=[gr,Zt,Sr,yr];var Ar=t=>{let{logger:e}=t,o=new Bs(t.baseServer,{cors:{origin:"*",methods:["GET","POST"]}});return o.on("connection",async r=>{let n;try{n=await wr({...t,socket:r})}catch(s){e.error({event:"connection",type:"websocket",err:s},"Failed to setup connection"),r.emit("error",{message:`${s}`});return}br.forEach(s=>js(s,{socket:r,metadata:n,...t}))}),o},js=(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 Gs from"fetch-retry";var Vs=Gs(global.fetch),pe="v1",he=class{baseURL;apiKey;constructor(e){this.baseURL=e.baseURL,this.apiKey=e.apiKey}async getElementLocation(e,o){let r=await this.sendRequest(`/${pe}/web-agent/locate-element`,{browserState:e.browserState,goal:e.goal,disableCache:o});return Rt.parse(r)}async getElementLocationWithVision(e,o){let r=await this.sendRequest(`/${pe}/web-agent/visual-locate`,{goal:e.goal,screenshot:e.screenshot?.toString("base64"),hintActivatedScreenshot:e.hintActivatedScreenshot?.toString("base64"),disableCache:o});return Rt.parse(r)}async getAssertionResult(e,o,r){if(o){let s=await this.sendRequest(`/${pe}/web-agent/assertion`,{url:e.url,goal:e.goal,screenshot:e.screenshot?.toString("base64"),disableCache:r,vision:!0});return Tt.parse(s)}let n=await this.sendRequest(`/${pe}/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 Tt.parse(n)}async getProposedCommand(e,o){let r=await this.sendRequest(`/${pe}/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 Ao.parse(r)}async getGranularGoals(e,o){let r=await this.sendRequest(`/${pe}/web-agent/split-goal`,{url:e.url,goal:e.goal,disableCache:o});return Eo.parse(r)}async getReverseMappedDescription(e,o){let r=await this.sendRequest(`/${pe}/web-agent/reverse-mapped-description`,{goal:e.goal,browserState:e.browserState,disableCache:o});return vo.parse(r)}async sendRequest(e,o){let r=await Vs(`${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 Ws,readFileSync as Ks,writeFileSync as $t}from"fs";import Ht from"path";import{parse as qs,stringify as Ys}from"yaml";var _=process.env.MOMENTIC_DIR??ae;function Er(t,e){let o=No(t);for(let[s,i]of Object.entries(o.modules)){let a=Me(s);$t(Ht.join(_,V,`${a}.yaml`),i,"utf-8")}let r=Me(e),n=Ht.join(_,`${r}.yaml`);return $t(n,o.test,"utf-8"),`${r}.yaml`}function vr(t,e){if(!Ws(e))throw new Error(`Test not found at path: ${Ht}`);let o=Ks(e,"utf-8"),n={...qs(o),...t},s=Ys(n);$t(e,s,"utf-8")}import{readFileSync as ct,readdirSync as Cr,writeFileSync as Tr}from"fs";import{join as Bt}from"path";import{v4 as Xs}from"uuid";import{parse as dt,stringify as Rr}from"yaml";var ke=Bt(_,V);function xr(t,e){let o=rt(ke,e).path,r=ct(o,"utf-8"),s={...dt(r),...t},i=Rr(s);Tr(o,i,"utf-8")}function Ir(t,e){let o=Me(t),r=Bt(ke,`${o}.yaml`),n={fileType:"momentic/module",schemaVersion:z,moduleId:Xs(),name:t,steps:e},s=Rr(n);return Tr(r,s,"utf-8"),{type:"RESOLVED_MODULE",moduleId:n.moduleId,name:n.name,steps:n.steps}}function Lr(){let t=[];for(let e in Cr(ke)){if(!e.endsWith(".yaml"))continue;let o=ct(e,"utf-8"),r=dt(o),n={name:r.name,moduleId:r.moduleId,numSteps:r.steps.length};t.push(n)}return t}function Or(){let t=[];for(let e of Cr(ke)){if(!e.endsWith(".yaml"))continue;let o=Bt(ke,e),r=ct(o,"utf-8"),n=dt(r);try{let s=xt.parse(n);t.push(s)}catch(s){y.warn({err:s},"Error parsing module, skipping...")}}return t}function Mr(t){let e=rt(V,t).path,o=ct(e,"utf-8"),r=dt(o);return xt.parse(r)}import{Router as Js}from"express";function j(t){return function(...e){let o=e[e.length-1],r=t(...e);Promise.resolve(r).catch(o)}}var ze=Js();ze.get("/",j((t,e)=>{let r=Or().map(n=>({...n,type:"RESOLVED_MODULE"}));e.status(200).json(r)}));ze.get("/metadata",j((t,e)=>{let o=Lr();e.status(200).json(o)}));ze.post("/",j((t,e)=>{let o;try{o=So.parse(t.body)}catch(n){e.status(400).json({error:`Invalid request body: ${n}`});return}try{De(o.name)}catch(n){e.status(400).json({error:`Invalid module name: ${n}`});return}let r=Ir(o.name,o.steps);e.status(201).json(r)}));ze.get("/:moduleId",j((t,e)=>{if(!t.params.moduleId){e.status(400).json({error:"Missing moduleId in url path."});return}let o=Mr(t.params.moduleId);e.json(o)}));var Dr=ze;import{Router as Qs}from"express";import{existsSync as Zs}from"fs";import{join as jt}from"path";import{v4 as ei}from"uuid";var Ue=Qs();Ue.get("/",j((t,e)=>{let o=Ne(_).filter(r=>r.localOnly);e.status(200).json(o)}));Ue.post("/",j((t,e)=>{let o;try{o=yo.parse(t.body)}catch(a){e.status(400).json({error:`Invalid request body: ${a}`});return}try{De(o.name)}catch(a){e.status(400).json({error:a.message});return}let n={id:ei(),name:o.name,baseUrl:o.baseUrl,schemaVersion:z,advanced:{disableAICaching:!1,availableAsModule:!0},retries:0,steps:[],localOnly:!0},s=Er(n,o.name),i={...n,testPath:s};e.status(201).json(i)}));Ue.get("/:testPath",j(async(t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let o=jt(_,t.params.testPath);if(!Zs(o)){e.status(400).json({error:`Test not found at path: ${o}`});return}let r=await nt(o,jt(_,V));e.status(200).json(r)}));Ue.patch("/:testPath",j((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))xr({steps:i},s);vr({steps:r},jt(_,t.params.testPath)),e.status(201).json({message:"ok"})}));var Nr=Ue;var mt=process.env.MOMENTIC_SERVER??"https://api.momentic.ai";var ti,_r=t=>{ti=t};var Pr,kr=async t=>{if(Pr)return;let e=await fetch(`${mt}/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}=Mo.parse(o);Pr=r};async function Ur(t,e,o,r,n,s){if(!ni(_)||!si(_).isDirectory()){let m=li.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.`)}y.info("Checking API key"),await kr(e);let a=`http://localhost:${o}`;r&&(a=`http://localhost:${r}`);let l=ci(n);await new Promise(m=>{l.listen(o,()=>{y.info(`Server is running at http://localhost:${o}`),m()})});let d=new he({baseURL:mt,apiKey:e});if(t==="web"){await zr("web",l,d),await ai(a);return}let u=await di(a,d,s,()=>{y.info("Browser closed, closing app."),l.close(),process.exit(0)});await zr("iframe",l,d,u)}function ci(t){let e=Gt();e.use(ri()),e.use(oi.json({limit:"50mb"}));let o=Gt.Router();if(o.use("/tests",Nr),o.use("/modules",Dr),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=Gt.static(t);e.use("/",n),e.use("*",n)}return ii.createServer(e)}async function di(t,e,o,r){let n=await I.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 Y({browser:n,config:ue,generator:e,logger:y});return _r(s),s}async function zr(t,e,o,r){Ar({baseServer:e,generator:o,logger:y,localApp:t,rootController:r})}import{existsSync as Fi}from"fs";import an,{dirname as $i}from"path";import{fileURLToPath as Hi}from"url";var Fr="0.0.24";var X="v1",Ee=class{baseURL;apiKey;constructor(e){this.baseURL=e.baseURL,this.apiKey=e.apiKey}async getRun(e){let o=await this.sendRequest(`/${X}/runs/${e}`,{method:"GET"});return Lo.parse(o)}async createRun(e){let o=await this.sendRequest(`/${X}/runs`,{method:"POST",body:e});return Io.parse(o)}async updateRun(e,o){await this.sendRequest(`/${X}/runs/${e}`,{method:"PATCH",body:o})}async getTest(e){let o=await this.sendRequest(`/${X}/tests/${e}`,{method:"GET"});return To.parse(o)}async getAllTestIds(){let e=await this.sendRequest(`/${X}/tests`,{method:"GET"});return Ro.parse(e)}async getTestYAMLExport(e){let o=await this.sendRequest(`/${X}/tests/export`,{method:"POST",body:e});return xo.parse(o)}async updateTestWithYAML(e){await this.sendRequest(`/${X}/tests/update`,{method:"POST",body:e})}async queueTests(e){let o=await this.sendRequest(`/${X}/tests/queue`,{method:"POST",body:e});return Co.parse(o)}async uploadScreenshot(e){let o=await this.sendRequest(`/${X}/screenshots`,{method:"POST",body:e});return Oo.parse(o)}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{existsSync as hi,mkdirSync as ut,statSync as fi}from"fs";import{homedir as gi}from"os";import{join as Vt}from"path";import ui from"chalk";import pi from"readline/promises";async function ve(t,e){if(process.env.CI)return!0;let o=pi.createInterface({input:process.stdin,output:process.stdout});t=`${t} (y/N) `;let r=e?ui.bold.yellow(t):t,n=await o.question(r);return o.close(),n.toLowerCase()==="y"}var pt=ae,fe=Vt(ae,V),Wt=Vt(ae,fo),yi=Vt(gi(),"momentic","chromium");function Si(t){return hi(t)&&fi(t).isDirectory()}async function Fe(t=!1){Si(pt)||(!t&&!await ve(`A '${ae}' folder was not found in the current directory. Setup the required Momentic folder structure?`)&&(y.error("Setup cancelled"),process.exit(1)),ut(pt),ut(fe),ut(Wt),ut(yi,{recursive:!0}),y.info("Setup complete!"))}import{registry as Kt}from"playwright-core/lib/server";function wi(t){let e=[],o=[];for(let r of t){let n=Kt.findExecutable(r);!n||n.installType==="none"?e.push(r):o.push(n)}return o}async function $r(){let t=wi(["chromium"]);await Kt.installDeps(t,!1),await Kt.install(t,!1)}import{Argument as qt,Option as $e}from"commander";var He=new $e("--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),ht=new $e("--server <server>","Momentic server to use. Leave unchanged unless using Momentic on-premise.").default("https://api.momentic.ai"),Be=new $e("-y, --yes","Skip confirmation prompts.").env("CI").default(!1),Hr=new $e("--no-report","Skip reporting test results to Momentic Cloud when running with the --local flag.").default(!0),Yt=new $e("-a --all","Select all tests in your organization from Momentic Cloud. Cannot be used together with <tests> arguments.").default(!1).preset(!0),Br=new qt("<tests...>",`One or more test paths to pull from Momentic Cloud.
26
26
 
27
- A test path is a lowercased version of your test name where spaces are replaced with underscores: 'npx momentic pull hello-world'.`).argOptional(),$r=new Wt("<tests...>",`One or more test identifiers.
27
+ A test path is a lowercased version of your test name where spaces are replaced with underscores: 'npx momentic pull hello-world'.`).argOptional(),jr=new qt("<tests...>",`One or more test identifiers.
28
28
 
29
29
  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'.
30
30
 
31
- 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(),Hr=new Wt("<paths...>","File paths pointing to one or more YAML files containing Momentic tests, or a directory of Momentic YAML files.");import{existsSync as fi,writeFileSync as Br}from"fs";import{join as Gr}from"path";async function Wr({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 d=Gr(ut,Vr(a));if(!await jr(d)){y.error("Pull cancelled");return}Br(d,l,"utf-8"),y.info(`Wrote '${d}'`)}let i=Object.keys(n).length;for(let[a,l]of Object.entries(n)){let d=Gr(he,Vr(a));if(!await jr(d)){y.error("Pull cancelled");return}Br(d,l,"utf-8"),y.info(`Wrote '${d}'`)}y.info(`Pulled ${s} test${s>1?"s":""}${i?` and ${i} module${i>1?"s":""}`:""}!`)}async function jr(t){return fi(t)?ve(`File '${t.replace(/(\s+)/g,"\\$1")}' already exists. Overwrite existing content?`,!0):!0}function Vr(t){return`${t.toLowerCase().replaceAll(" ","-")}.yaml`}import{existsSync as Kr,readFileSync as gi,readdirSync as yi,statSync as qr}from"fs";import Yr from"path";async function Xr(t){let e=Jr(t.paths);y.info(`Found ${e.size} test(s) to push:`),e.forEach(n=>y.info(` - ${n}`)),y.info("Loading file contents and resolving dependent modules");let o=Array.from(e).map(n=>It(n,he)),r=new Set(o.flatMap(n=>Object.keys(n.modules)));if(y.info(`Resolved ${e.size} test(s) and ${r.size} module(s)`),!t.yes&&!await ve("Pushing tests overwrites tests on production and will instantly affect scheduled runs. Continue?",!0)){y.error("Push cancelled");return}await t.client.updateTestWithYAML(o),y.info("Update successful!")}function Si(t){if(!t.endsWith(".yaml"))return!1;let e=gi(t,"utf8");return e.includes("momentic/test")?e.match(/localOnly:.*true/)?(y.warn(`Skipping local-only test: ${t}`),!1):!0:(y.warn(`Skipping YAML that is not a Momentic test: ${t}`),!1)}function Jr(t,e=new Set){for(let o of t){let r=Yr.resolve(o);if(r&&Kr(r)&&qr(r).isDirectory()){let n=yi(r).map(s=>Yr.join(r,s));Jr(n,e);continue}if(r.endsWith(".yaml")){if(!Kr(r)||!qr(r).isFile())throw new Error(`File not found or unreadable: ${r}`);if(!Si(r))continue;e.add(r)}}return e}import{existsSync as en,statSync as Li}from"fs";import Oi from"wait-on";import wi from"@actions/exec";import bi from"@actions/io";import Ai from"quote";import Ei from"string-argv";async function Qr(t,e=!0){let o=Ei(t),r=await bi.which(o[0],!0),n=o.slice(1),s=wi.exec(Ai(r),n,{delay:100});if(e)return s}import{v4 as Ii}from"uuid";import{existsSync as vi,readFileSync as Ci}from"fs";import{join as Ti}from"path";import{parse as Ri}from"yaml";function xi(t){let e=Ti(jt,`${t}.yaml`);if(!vi(e))throw new Error(`Fixture '${t}' does not exist at expected path ${e}`);let o;try{o=Ri(Ci(e,"utf-8").replace(/\r\n|\r/g,`
32
- `))}catch(n){throw new Error(`Fixture at path ${e} does not parse as valid YAML: ${n}`)}let r;try{r=io.parse(o)}catch(n){throw new Error(`Fixture at path ${e} does not conform to expected schema: ${n}`)}return r}async function qt(t,e){let o=xi(t),r=e==="setup"?o.setup:o.teardown;if(!r){y.warn(`Fixture '${t}' does not have any steps in the ${e} phase, skipping...`);return}let n=r.timeout,s=async function(){for(let d=0;d<r.steps.length;d++){let c=r.steps[d],u=c.run,m=async p=>{try{await $o(`set -ex; ${u}`)}catch(f){if(p){let w=`Fixture '${t}' ${e} step ${d+1} errored in the background. The test will continue, but this may affect test execution and results: ${f}`;y.error(w)}else throw new Error(`Fixture '${t}' ${e} step ${d+1} errored: ${f}`)}};c.waitForCompletion===!1?m(!0):await m(!1)}};if(y.info(`Running ${e} phase of fixture '${t}'`),!n){await s();return}let i,a,l=async function(){return new Promise((d,c)=>{a=d,i=setTimeout(()=>{a=void 0,c(`Fixture '${t}' ${e} phase timed out after ${n} seconds`)},n*1e3)})};try{await Promise.race([s(),l()])}catch(d){throw d}finally{a&&a(),clearTimeout(i)}}async function Zr({path:t,apiClient:e,generator:o,newBaseURL:r,useLocalFiles:n,noReport:s}){let i;n?(y.info(`Reading ${t} from local filesystem`),i=await nt(t,he)):(y.info(`Fetching ${t} from Momentic Cloud server (${e.baseURL})`),i=await e.getTest(t)),i.schemaVersion>z&&y.warn(`Test ${t} has schema version ${i.schemaVersion}, which is greater than what is currently supported by this SDK. Please update your momentic package version to avoid unexpected behavior.`);let a=i.envSettings?.find(f=>f.name==="development");for(let f of a?.fixtures||[])await qt(f,"setup");let l=new URL(i.baseUrl);if(r){let f=new URL(r);l.hostname=f.hostname,l.protocol=f.protocol,l.port=f.port}let d=await x.init({baseUrl:l.toString(),logger:y}),c=new Y({browser:d,generator:o,config:me,logger:y}),u,m;if(s)m=Ii();else try{u=await e.createRun({testId:i.id,trigger:"CLI"}),m=u.id}catch(f){throw y.error(f),new Error(`Are you sure test ${i.name} exists on the server?`)}let p="FAILED";try{p=await dr({test:i,runId:m,controller:c,logger:y,onSaveScreenshot:async f=>{if(s)return"";let{key:w}=await e.uploadScreenshot({screenshot:f.toString("base64")});return w},onUpdateRun:async f=>{s||await e.updateRun(m,f)}})}catch(f){throw s||await e.updateRun(m,{status:"FAILED",finishedAt:new Date}),s||y.error(`Test '${i.name}' failed. You can view a playback of this run at ${e.baseURL}/runs/${m}`),f}finally{try{for(let f of a?.fixtures||[])await qt(f,"teardown")}catch(f){y.error(`Failed to run teardown fixtures: ${f}`)}}return p}async function tn({tests:t,start:e,waitOn:o,waitOnTimeout:r,client:n,all:s,noReport:i,parallelization:a=1}){e&&(y.info(`Running start command: ${e}`),await Qr(e,!1)),o&&(y.info(`Waiting for ${o} to be accessible (timeout: ${r}s)`),await Oi({resources:[o],timeout:r*1e3}));let l=new pe({baseURL:n.baseURL,apiKey:n.apiKey}),d=!1;(t.some(g=>g.endsWith(".yaml"))||t.some(g=>en(g)))&&(d=!0);let c=new Set;if(d)y.info(t,"Reading tests from the following local file paths:"),t.forEach(g=>{if(!en(g))throw new Error(`Path '${g}' does not exist.`);if(Li(g).isDirectory())Ne(g).map(v=>v.fullFilePath).forEach(v=>c.add(v));else if(g.endsWith(".yaml"))c.add(g);else throw new Error(`Path '${g}' is not a directory or a .yaml file.`)});else if(y.warn("The paths you specified are not files or directories that exist locally."),y.warn(`Fetching tests from Momentic Cloud (${n.baseURL}) instead...`),s)for(let g of await n.getAllTestIds())c.add(g);else c=new Set(t);let u=Array.from(c);y.info(u,`Identified ${u.length} tests to run locally:`);let m=[];for(let g=0;g<u.length;g+=a){let N=await Promise.all(u.slice(g,g+a).map(async v=>{let b="FAILED";try{b=await Zr({useLocalFiles:d,path:v,apiClient:n,generator:l,newBaseURL:o,noReport:i})}catch(E){let T=E instanceof Error?E.message:`${E}`;y.error(`Test ${v} failed with error: ${T}`)}return{runStatus:b,path:v}}));m=m.concat(N)}let p=m.filter(g=>g.runStatus==="PASSED");p.length>0&&(y.info(`Passed ${p.length} out of ${m.length} tests:`),p.forEach(g=>{y.info(`- ${g.path}`)}));let f=m.filter(g=>g.runStatus==="CANCELLED");f.length>0&&(y.warn(`Cancelled ${f.length} out of ${m.length} tests:`),f.forEach(g=>{y.warn(`- ${g.path}`)}));let w=m.filter(g=>g.runStatus==="FAILED");w.length>0&&(y.error(`Failed ${w.length} out of ${m.length} tests:`),w.forEach(g=>{y.error(`- ${g.path}`)}),process.exit(1)),process.exit(0)}async function on({tests:t,client:e,all:o}){let{queuedTests:r}=await e.queueTests({testPaths:t,all:o});y.info(`Successfully queued ${r.length} tests!`)}var J=new Mi;J.name("momentic").description("Momentic CLI").version(_r);J.command("install-browsers").action(async()=>{await zr()});J.addOption(new ee("--log-level <level>").choices(["debug","info","warn","error"]).default("info")).on("option:log-level",t=>{y.setMinLevel(yo[t.toUpperCase()])});J.command("init").addOption(Be).action(async t=>{let{yes:e}=t;await Fe(e)});J.command("app").addOption(He).addOption(Be).addOption(new ee("--port <port>","Port to run the app on. Defaults to 58888.").default(58888)).action(async t=>{let{apiKey:e,port:o,yes:r}=t;await Fe(r);let n=ki(import.meta.url),s=_i(n),i=Pi.resolve(s,"..","static");await Pr(e,o,o,i)});J.command("run").alias("run-tests").addOption(He).addOption(pt).addOption(Kt).addOption(new ee("-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(Ur).addOption(new ee("-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 ee("--start <command>","Arbitrary setup command that will run before Momentic steps begin.")).addOption(new ee("--wait-on <url>","URL to wait to become accessible before Momentic tests begin.")).addOption(new ee("--wait-on-timeout <timeout>","Max time to wait for the --wait-on URL to become accessible.").default(60,"one minute")).addOption(new ee("-p, --parallel <parallelization>","When running with the --local flag, the number of tests to run in parallel. Defaults to 1.").default(1)).addArgument($r).action(async(t,e)=>{let{apiKey:o,server:r,remote:n,local:s,all:i}=e,a=new Ee({baseURL:r,apiKey:o});if(s){try{await tn({tests:t,client:a,...e})}catch(l){y.error(l),process.exit(1)}return}if(n){for(let l of t)(l.endsWith(".yaml")||Ni(l))&&(y.error(Di`'${l}' looks like a local file or directory, but the --local flag was not supplied.
33
- 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 on({tests:t,client:a,all:i});return}y.error("One of --remote or --local must be specified"),process.exit(1)});J.command("pull").description("Fetch one or more tests from Momentic Cloud and save it in to local disk in YAML format.").addOption(He).addOption(pt).addOption(Kt).addOption(Be).addArgument(Fr).action(async(t,e)=>{let{apiKey:o,server:r,all:n,yes:s}=e;if(await Fe(s),!n&&!t?.length)throw new Error("At least one test name or --all must be provided");let i=new Ee({baseURL:r,apiKey:o});await Wr({testsToFetch:t,client:i,all:n})});J.command("push").description("Save one or more tests in YAML format to Momentic Cloud.").addOption(Be).addOption(He).addOption(pt).addArgument(Hr).action(async(t,e)=>{let{apiKey:o,server:r,yes:n}=e;await Fe(n);let s=new Ee({baseURL:r,apiKey:o});await Xr({paths:t,client:s,yes:n})});async function zi(){await J.parseAsync(process.argv)}zi();
31
+ 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(),Gr=new qt("<paths...>","File paths pointing to one or more YAML files containing Momentic tests, or a directory of Momentic YAML files.");import{existsSync as Ai,writeFileSync as Vr}from"fs";import{join as Wr}from"path";async function Yr({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=Wr(pt,qr(a));if(!await Kr(c)){y.error("Pull cancelled");return}Vr(c,l,"utf-8"),y.info(`Wrote '${c}'`)}let i=Object.keys(n).length;for(let[a,l]of Object.entries(n)){let c=Wr(fe,qr(a));if(!await Kr(c)){y.error("Pull cancelled");return}Vr(c,l,"utf-8"),y.info(`Wrote '${c}'`)}y.info(`Pulled ${s} test${s>1?"s":""}${i?` and ${i} module${i>1?"s":""}`:""}!`)}async function Kr(t){return Ai(t)?ve(`File '${t.replace(/(\s+)/g,"\\$1")}' already exists. Overwrite existing content?`,!0):!0}function qr(t){return`${t.toLowerCase().replaceAll(" ","-")}.yaml`}import{existsSync as Xr,readFileSync as Ei,readdirSync as vi,statSync as Jr}from"fs";import Qr from"path";async function Zr(t){let e=en(t.paths);y.info(`Found ${e.size} test(s) to push:`),e.forEach(n=>y.info(` - ${n}`)),y.info("Loading file contents and resolving dependent modules");let o=Array.from(e).map(n=>Ot(n,fe)),r=new Set(o.flatMap(n=>Object.keys(n.modules)));if(y.info(`Resolved ${e.size} test(s) and ${r.size} module(s)`),!t.yes&&!await ve("Pushing tests overwrites tests on production and will instantly affect scheduled runs. Continue?",!0)){y.error("Push cancelled");return}await t.client.updateTestWithYAML(o),y.info("Update successful!")}function Ci(t){if(!t.endsWith(".yaml"))return!1;let e=Ei(t,"utf8");return e.includes("momentic/test")?e.match(/localOnly:.*true/)?(y.warn(`Skipping local-only test: ${t}`),!1):!0:(y.warn(`Skipping YAML that is not a Momentic test: ${t}`),!1)}function en(t,e=new Set){for(let o of t){let r=Qr.resolve(o);if(r&&Xr(r)&&Jr(r).isDirectory()){let n=vi(r).map(s=>Qr.join(r,s));en(n,e);continue}if(r.endsWith(".yaml")){if(!Xr(r)||!Jr(r).isFile())throw new Error(`File not found or unreadable: ${r}`);if(!Ci(r))continue;e.add(r)}}return e}import{existsSync as rn,statSync as _i}from"fs";import ki from"wait-on";import Ti from"@actions/exec";import Ri from"@actions/io";import xi from"quote";import Ii from"string-argv";async function tn(t,e=!0){let o=Ii(t),r=await Ri.which(o[0],!0),n=o.slice(1),s=Ti.exec(xi(r),n,{delay:100});if(e)return s}import{v4 as Pi}from"uuid";import{existsSync as Li,readFileSync as Oi}from"fs";import{join as Mi}from"path";import{parse as Di}from"yaml";function Ni(t){let e=Mi(Wt,`${t}.yaml`);if(!Li(e))throw new Error(`Fixture '${t}' does not exist at expected path ${e}`);let o;try{o=Di(Oi(e,"utf-8").replace(/\r\n|\r/g,`
32
+ `))}catch(n){throw new Error(`Fixture at path ${e} does not parse as valid YAML: ${n}`)}let r;try{r=co.parse(o)}catch(n){throw new Error(`Fixture at path ${e} does not conform to expected schema: ${n}`)}return r}async function Xt(t,e){let o=Ni(t),r=e==="setup"?o.setup:o.teardown;if(!r){y.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,m=async h=>{try{await jo(`set -ex; ${u}`)}catch(p){if(h){let w=`Fixture '${t}' ${e} step ${c+1} errored in the background. The test will continue, but this may affect test execution and results: ${p}`;y.error(w)}else throw new Error(`Fixture '${t}' ${e} step ${c+1} errored: ${p}`)}};d.waitForCompletion===!1?m(!0):await m(!1)}};if(y.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 on({path:t,apiClient:e,generator:o,newBaseURL:r,useLocalFiles:n,noReport:s}){let i;n?(y.info(`Reading ${t} from local filesystem`),i=await nt(t,fe)):(y.info(`Fetching ${t} from Momentic Cloud server (${e.baseURL})`),i=await e.getTest(t)),i.schemaVersion>z&&y.warn(`Test ${t} has schema version ${i.schemaVersion}, which is greater than what is currently supported by this SDK. Please update your momentic package version to avoid unexpected behavior.`);let a=i.envSettings?.find(p=>p.name==="development");for(let p of a?.fixtures||[])await Xt(p,"setup");let l=new URL(i.baseUrl);if(r){let p=new URL(r);l.hostname=p.hostname,l.protocol=p.protocol,l.port=p.port}let c=await I.init({baseUrl:l.toString(),logger:y}),d=new Y({browser:c,generator:o,config:ue,logger:y}),u,m;if(s)m=Pi();else try{u=await e.createRun({testId:i.id,trigger:"CLI"}),m=u.id}catch(p){throw y.error(p),new Error(`Are you sure test ${i.name} exists on the server?`)}let h="FAILED";try{h=await pr({test:i,runId:m,controller:d,logger:y,takeScreenshots:!0,onSaveScreenshot:async p=>{if(s)return"";let{key:w}=await e.uploadScreenshot({screenshot:p.toString("base64")});return w},onUpdateRun:async p=>{s||await e.updateRun(m,p)}})}catch(p){throw s||await e.updateRun(m,{status:"FAILED",finishedAt:new Date}),s||y.error(`Test '${i.name}' failed. You can view a playback of this run at ${e.baseURL}/runs/${m}`),p}finally{try{for(let p of a?.fixtures||[])await Xt(p,"teardown")}catch(p){y.error(`Failed to run teardown fixtures: ${p}`)}}return h}async function nn({tests:t,start:e,waitOn:o,waitOnTimeout:r,client:n,all:s,noReport:i,parallelization:a=1}){e&&(y.info(`Running start command: ${e}`),await tn(e,!1)),o&&(y.info(`Waiting for ${o} to be accessible (timeout: ${r}s)`),await ki({resources:[o],timeout:r*1e3}));let l=new he({baseURL:n.baseURL,apiKey:n.apiKey}),c=!1;(t.some(g=>g.endsWith(".yaml"))||t.some(g=>rn(g)))&&(c=!0);let d=new Set;if(c)y.info(t,"Reading tests from the following local file paths:"),t.forEach(g=>{if(!rn(g))throw new Error(`Path '${g}' does not exist.`);if(_i(g).isDirectory())Ne(g).map(T=>T.fullFilePath).forEach(T=>d.add(T));else if(g.endsWith(".yaml"))d.add(g);else throw new Error(`Path '${g}' is not a directory or a .yaml file.`)});else if(y.warn("The paths you specified are not files or directories that exist locally."),y.warn(`Fetching tests from Momentic Cloud (${n.baseURL}) instead...`),s)for(let g of await n.getAllTestIds())d.add(g);else d=new Set(t);let u=Array.from(d);y.info(u,`Identified ${u.length} tests to run locally:`);let m=[];for(let g=0;g<u.length;g+=a){let C=await Promise.all(u.slice(g,g+a).map(async T=>{let b="FAILED";try{b=await on({useLocalFiles:c,path:T,apiClient:n,generator:l,newBaseURL:o,noReport:i})}catch(E){let v=E instanceof Error?E.message:`${E}`;y.error(`Test ${T} failed with error: ${v}`)}return{runStatus:b,path:T}}));m=m.concat(C)}let h=m.filter(g=>g.runStatus==="PASSED");h.length>0&&(y.info(`Passed ${h.length} out of ${m.length} tests:`),h.forEach(g=>{y.info(`- ${g.path}`)}));let p=m.filter(g=>g.runStatus==="CANCELLED");p.length>0&&(y.warn(`Cancelled ${p.length} out of ${m.length} tests:`),p.forEach(g=>{y.warn(`- ${g.path}`)}));let w=m.filter(g=>g.runStatus==="FAILED");w.length>0&&(y.error(`Failed ${w.length} out of ${m.length} tests:`),w.forEach(g=>{y.error(`- ${g.path}`)}),process.exit(1)),process.exit(0)}async function sn({tests:t,client:e,all:o}){let{queuedTests:r}=await e.queueTests({testPaths:t,all:o});y.info(`Successfully queued ${r.length} tests!`)}var Q=new zi;Q.name("momentic").description("Momentic CLI").version(Fr);Q.command("install-browsers").action(async()=>{await $r()});Q.addOption(new J("--log-level <level>").choices(["debug","info","warn","error"]).default("info")).on("option:log-level",t=>{y.setMinLevel(bo[t.toUpperCase()])});Q.command("init").addOption(Be).action(async t=>{let{yes:e}=t;await Fe(e)});Q.command("app").addOption(He).addOption(Be).addOption(new J("--port <port>","Port to run the app on. Defaults to 58888.").default(58888)).addOption(new J("--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,mode:n}=t;await Fe(r);let s=Hi(import.meta.url),i=$i(s),a=an.resolve(i,"..","static"),l=an.resolve(i,"..","extension");await Ur(n,e,o,o,a,l)});Q.command("run").alias("run-tests").addOption(He).addOption(ht).addOption(Yt).addOption(new J("-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(Hr).addOption(new J("-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 J("--start <command>","Arbitrary setup command that will run before Momentic steps begin.")).addOption(new J("--wait-on <url>","URL to wait to become accessible before Momentic tests begin.")).addOption(new J("--wait-on-timeout <timeout>","Max time to wait for the --wait-on URL to become accessible.").default(60,"one minute")).addOption(new J("-p, --parallel <parallelization>","When running with the --local flag, the number of tests to run in parallel. Defaults to 1.").default(1)).addArgument(jr).action(async(t,e)=>{let{apiKey:o,server:r,remote:n,local:s,all:i}=e,a=new Ee({baseURL:r,apiKey:o});if(s){try{await nn({tests:t,client:a,...e})}catch(l){y.error(l),process.exit(1)}return}if(n){for(let l of t)(l.endsWith(".yaml")||Fi(l))&&(y.error(Ui`'${l}' looks like a local file or directory, but the --local flag was not supplied.
33
+ 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 sn({tests:t,client:a,all:i});return}y.error("One of --remote or --local must be specified"),process.exit(1)});Q.command("pull").description("Fetch one or more tests from Momentic Cloud and save it in to local disk in YAML format.").addOption(He).addOption(ht).addOption(Yt).addOption(Be).addArgument(Br).action(async(t,e)=>{let{apiKey:o,server:r,all:n,yes:s}=e;if(await Fe(s),!n&&!t?.length)throw new Error("At least one test name or --all must be provided");let i=new Ee({baseURL:r,apiKey:o});await Yr({testsToFetch:t,client:i,all:n})});Q.command("push").description("Save one or more tests in YAML format to Momentic Cloud.").addOption(Be).addOption(He).addOption(ht).addArgument(Gr).action(async(t,e)=>{let{apiKey:o,server:r,yes:n}=e;await Fe(n);let s=new Ee({baseURL:r,apiKey:o});await Zr({paths:t,client:s,yes:n})});async function Bi(){await Q.parseAsync(process.argv)}Bi();