momentic 0.0.91 → 0.0.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/cli.js +2 -2
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -1343,7 +1343,7 @@ You have already executed the following commands successfully (most recent liste
1343
1343
  `).forEach(c=>n.push(` ${c}`))):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(`
1344
1344
  `)}getListHistory(){return Eh`Here are the commands that you have successfully executed:
1345
1345
  ${this.commandHistory.filter(e=>e.type==="AI_ACTION").map(e=>`- ${e.serializedCommand}`).join(`
1346
- `)}`}async executeCommand(e,r,o,n=!1){let s=this.commandHistory[this.commandHistory.length-1];if(!n&&(!s||s.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 i=Date.now(),a=await this.executePresetStep(e,r,o),c=Date.now()-i;return this.logger.debug({result:a,duration:c},"Got execution result"),a.succeedImmediately&&!n&&(this.pendingInstructions.pop(),this.pendingInstructions.length>0&&(a.succeedImmediately=!1)),a.elementInteracted&&"target"in e&&e.target&&e.target.type==="description"&&!e.target.elementDescriptor&&(e.target.elementDescriptor=a.elementInteracted.trim()),n||(s.generatedStep=e,s.serializedCommand=Ye(e),s.state="DONE"),a}async executeAssertion(e){let r=!1,o,n=async()=>{try{o=await this.browser.screenshot({clearHighlights:!0,scale:"css"}),Kl(this.uploadScreenshotForDebugging("assertion",o),{milliseconds:5e3,fallback:()=>{}})}catch(g){this.logger.warn({err:g},"Failed to take screenshot for assertion, continuing without it")}},s=await this.browser.url(),[i]=await Promise.all([this.getBrowserState({filterByViewport:e.filterByViewport}),n()]),{serializedTree:a}=i,{tree:c}=i,l=.2*Eo["gpt-4o-2024-05-13"],d=os({serializedTree:a,tokenLimit:l});if(d){let w=(await this.generator.getRecommendedChunks({...d,description:e.assertion,tokenLimit:l})).ids;w.length===0?this.logger.debug("RAG returned no important information for assertion"):(a=c.pruneUsingRelevantIds(new Set(w)).serialize(),this.logger.debug({browserState:a},"Pruned a11y tree with RAG"),r=!0)}let u=this.getSerializedHistory(s,a),p={goal:e.assertion,url:s,browserState:a,history:u,lastCommand:this.lastExecutedCommand,screenshot:o?.toString("base64"),numPrevious:this.commandHistory.length},m=await this.generator.getAssertionResult(p,e.disableCache);if(m.relevantElements&&Promise.all(Array.from(new Set(m.relevantElements)).slice(0,5).map(g=>this.browser.highlight({id:g}))),this.logger.debug({usedRag:r,result:m},"Got assertion result"),!m.result)throw new x("AssertionFailureError",m.thoughts);return{succeedImmediately:!1,thoughts:m.thoughts,urlAfterCommand:s,beforeScreenshotOverride:o}}async wrapMultiElementTargetingCommand(e,r,o,n,s=1){let i=await Promise.all(e.map((a,c)=>this.wrapElementTargetingCommand({target:a,cache:r[c],action:async l=>l,options:n})));try{return{result:await o(...i.map(c=>c.result)),caches:i.map(c=>c.cache),elementInteractedDisplayStrings:i.map(c=>c.elementInteractedDisplayString)}}catch(a){if(s>0)return this.logger.debug({err:a},"Failed to execute action with multiple cached targets, retrying with AI"),this.wrapMultiElementTargetingCommand(e,e.map(()=>{}),o,n,s-1);throw new x("ActionFailureError",a.message,{cause:a})}}async wrapFrameUseCommand(e,r){if(!r)return e();let o=this.browser.getActiveFrame();try{return this.logger.debug({frameUrl:r},"Setting parent iframe target"),this.browser.setActiveFrame({type:"url",url:r}),await e()}finally{this.browser.setActiveFrame(o)}}async wrapElementTargetingCommand(e){return this.wrapFrameUseCommand(()=>this.wrapElementTargetingCommandHelper(e),e.options.iframeUrl)}async wrapElementTargetingCommandHelper({target:e,cache:r,action:o,options:n}){let{disableCache:s,filterByViewport:i,useSelector:a}=n,c=n.retriesWithAI??1,l;if((!r||s)&&!Fo(e))throw new x("ActionFailureError","Cannot target element with no cached data or element descriptor");if(a){if(e.type!=="description")throw new x("ActionFailureError","Cannot use selector along with non-description target");let u={id:-1,selector:e.elementDescriptor},p=await this.browser.resolveTarget(u);return{result:await o(p.locator),cache:u,elementInteractedDisplayString:p.displayString}}s&&(this.logger.debug("Cache explicitly disabled for this step"),r=void 0);let d=!!r&&Dr(r);if(!r){this.logger.debug("No cached locator data for target, prompting AI for fresh location"),c--;let u=await this.locateElement({description:e.elementDescriptor,disableCache:s,filterByViewport:i,iframeUrl:n.iframeUrl});r=u.target,l=u.thoughts}try{let u=await this.browser.resolveTarget(r),p=await o(u.locator);return d?this.logger.debug({cache:r},"Successfully used cached target to perform action"):this.logger.debug({cache:r},"Successfully generated and used new target data"),{result:p,cache:r,thoughts:l,elementInteractedDisplayString:u.displayString}}catch(u){if(u instanceof x)throw u;if(c>0&&e)return this.logger.debug({err:u,cache:r},"Failed to execute action with cached target, retrying with AI"),this.wrapElementTargetingCommand({target:e,cache:void 0,action:o,options:{...n,retriesWithAI:c}});throw new x("ActionFailureError",u.message,{cause:u})}}async screenshotWithDimensions(e){let r=await this.browser.screenshot(e),o=await is(r).metadata();return{buffer:r,width:Math.ceil(o.width??0),height:Math.ceil(o.height??0)}}async executePresetStep(e,r,o){let n;try{n=await this.resolveCommandTemplateStrings(e,r)}catch(s){throw new x("ActionFailureError",s.message,{cause:s})}try{let s=this.browser.getOpenPages(),i=await this.browser.url(),a=await this.executePresetActionHelper(e,r,o),c=!0;(e.type==="NAVIGATE"||e.type==="NEW_TAB"||e.type==="TAB"||e.type==="REFRESH")&&(c=!1);let l=this.browser.getOpenPages(),d=await this.browser.url();if(c&&l.length!==s.length)for(let u=l.length-1;u>=0;u--){let p=l[u];if(p!==i&&p!==d){await this.browser.switchToPage(p,u);break}}return a}catch(s){throw this.logger.error({err:s},"Error thrown in action controller"),s}finally{this.restoreCommandTemplateReplacements(e,n)}}restoreCommandTemplateReplacements(e,r={}){for(let[o,n]of Object.entries(r))ns(e,o,n)}async resolveCommandTemplateStrings(e,r,o="",n={}){let s=["type","a11yData","thoughts","cache"];for(let i in e){if(s.includes(i))continue;let a=e[i],c=o?`${o}.${i}`:i;if(typeof a=="string"&&a.includes("{{")){let l=await ho({s:a,context:r,logger:this.logger});if(a===l)continue;n[c]=a,e[i]=l}else typeof a=="object"&&a!==null&&!Array.isArray(a)&&await this.resolveCommandTemplateStrings(a,r,c,n)}return n}async executePresetActionHelper(e,r,o){switch(o=o||"disableCache"in e&&e.disableCache,e.type){case"SUCCESS":let n=e.condition;return n?.assertion.trim()?this.wrapFrameUseCommand(()=>this.executeAssertion(n),n?.iframeUrl):{succeedImmediately:!1,urlAfterCommand:await this.browser.url()};case"AI_ASSERTION":{if(!e.assertion.trim())throw new x("ActionFailureError","Missing assertion");return this.wrapFrameUseCommand(()=>this.executeAssertion(e),e.iframeUrl)}case"AI_WAIT":{if(!e.assertion.trim())throw new x("ActionFailureError","Missing assertion");let u=Date.now();if(e.timeout&&e.timeout>60)throw new x("AssertionFailureError",`AI wait timeout of ${e.timeout} exceeds the maximum allowed timeout of 60s`);let p=(e.timeout??10)*1e3,m,g,w=0;for(;Date.now()-u<p&&w>1;)try{m=await this.executeAssertion({...e,type:"AI_ASSERTION"});break}catch(f){g=f instanceof Error?f:new Error(`${f}`),this.logger.info({err:f},`AI_WAIT assert attempt ${w} failed, retrying...`),w++,await W(p/10)}if(!m){let f=`AI wait still failing after ${p}ms.`;throw g&&(f+=` Latest result: ${g.message}`),new x("AssertionFailureError",f)}return m}case"AI_EXTRACT":{if(!e.goal.trim())throw new x("ActionFailureError","Cannot perform AI extraction without goal");let u=await this.browser.getCondensedHtml(),p=await this.generator.getTextExtraction({goal:e.goal,browserState:u,returnSchema:e.schema},o);if(p.result==="NOT_FOUND")throw new x("ActionFailureError","No relevant data found for extraction goal on this page");return{data:p.result,succeedImmediately:!1,urlAfterCommand:await this.browser.url()}}case"NAVIGATE":if(!Vn(e.url)&&!qn(e.url,this.browser.baseURL))throw new x("ActionFailureError",`Invalid URL provided to navigate command: ${e.url}`);await this.browser.navigate({url:e.url,loadTimeoutMs:e.loadTimeout?e.loadTimeout*1e3:void 0,networkIdleTimeoutMs:e.networkTimeout?e.networkTimeout*1e3:5e3});break;case"DIALOG":this.browser.registerDialogHandler(e.action);break;case"CAPTCHA":if(!this.browser.canSolveCaptchas())break;let s=await this.browser.solveCaptcha();s&&(await this.wrapElementTargetingCommand({target:{type:"description",elementDescriptor:"the captcha image solution input"},cache:void 0,action:u=>this.browser.click(u),options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}}),await this.browser.type(s,{clearContent:!0,pressKeysSequentially:!1}));break;case"GO_BACK":await this.browser.goBack();break;case"GO_FORWARD":await this.browser.goForward();break;case"SCROLL_LEFT":case"SCROLL_RIGHT":case"SCROLL_DOWN":case"SCROLL_UP":if(e.target&&!Ge(e.target))throw new Error("Scroll with x/y is not supported yet");let i,a;if(e.target&&(e.target.elementDescriptor.trim()||e.target.a11yData)){let{cache:u,thoughts:p,elementInteractedDisplayString:m}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target??e.target.a11yData,action:g=>this.browser.hover(g,!1),options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}});i=m,u&&(e.cache={target:u}),a=p}switch(e.type){case"SCROLL_UP":await this.browser.scrollUp(e.deltaY);break;case"SCROLL_DOWN":await this.browser.scrollDown(e.deltaY);break;case"SCROLL_LEFT":await this.browser.scrollLeft(e.deltaX);break;case"SCROLL_RIGHT":await this.browser.scrollRight(e.deltaX);break}return{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:i,thoughts:a};case"WAIT":await this.browser.wait(e.delay*1e3);break;case"REFRESH":await this.browser.refresh({networkIdleTimeoutMs:e.networkTimeout?e.networkTimeout*1e3:void 0,loadTimeoutMs:e.loadTimeout?e.loadTimeout*1e3:void 0});break;case"CLICK":{let u={doubleClick:e.doubleClick,rightClick:e.rightClick,force:e.force,waitForUrl:e.waitForUrl};if(at(e.target)){await this.browser.clickUsingVisualCoordinates(e.target.percentXYLocation,u);break}let p=await this.browser.url(),m={disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl},{elementInteractedDisplayString:g,cache:w,thoughts:f}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target??e.target.a11yData,action:b=>this.browser.click(b,u),options:m});w&&(e.cache={target:w});let h={urlAfterCommand:await this.browser.url(),succeedImmediately:!1,elementInteracted:g,thoughts:f};return Ct(p,h.urlAfterCommand)&&(h.succeedImmediately=!0,h.succeedImmediatelyReason="URL changed"),h}case"DRAG":{if(at(e.fromTarget)&&at(e.toTarget)){await this.browser.dragAndDropUsingVisualCoordinates(e.fromTarget.percentXYLocation,e.toTarget.percentXYLocation,{force:e.force,hoverSeconds:e.hoverSeconds});break}if(at(e.fromTarget)||at(e.toTarget))throw new Error("Drag and drop targets must be both coordinates or both descriptions");let{caches:u,elementInteractedDisplayStrings:p}=await this.wrapMultiElementTargetingCommand([e.fromTarget,e.toTarget],[e.cache?.fromTarget,e.cache?.toTarget],(m,g)=>this.browser.dragAndDrop(m,g,{force:e.force,hoverSeconds:e.hoverSeconds}),{filterByViewport:e.filterByViewport,useSelector:e.useSelector,disableCache:o});return u&&u.every(m=>m)&&(e.cache={fromTarget:u[0],toTarget:u[1]}),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:p[0]}}case"MOUSE_DRAG":{if(e.target&&!Ge(e.target))throw new Error("Scroll with x/y is not supported yet");let u,p;if(e.target?.elementDescriptor){let w=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:async f=>f,options:{filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl,disableCache:o}});u=w.result,p=w.elementInteractedDisplayString}let m=parseInt(e.deltaX),g=parseInt(e.deltaY);if(isNaN(m)||isNaN(g))throw new x("ActionFailureError",`Invalid pixel values passed to mouse drag command: (${e.deltaX}, ${e.deltaY})`);return await this.browser.mouseDrag(m,g,e.steps,u,{force:e.force}),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:p}}case"SELECT_OPTION":{if(!Ge(e.target))throw new Error("Select with x/y is not supported yet");let u=e.target.elementDescriptor,{cache:p,thoughts:m,elementInteractedDisplayString:g}=await this.wrapElementTargetingCommand({target:{type:"description",elementDescriptor:cr(e,u)},cache:e.cache?.target??e.target.a11yData,action:w=>this.browser.selectOption(w,e.option),options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}});return p&&(e.cache={target:p}),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:g,thoughts:m}}case"TAB":await this.browser.switchToPage(e.url,void 0,{networkIdleTimeoutMs:e.networkTimeout?e.networkTimeout*1e3:void 0,loadTimeoutMs:e.loadTimeout?e.loadTimeout*1e3:void 0});break;case"NEW_TAB":await this.browser.createNewTab(e.url,{networkIdleTimeoutMs:e.networkTimeout?e.networkTimeout*1e3:void 0,loadTimeoutMs:e.loadTimeout?e.loadTimeout*1e3:void 0});break;case"COOKIE":if(!e.value)break;await this.browser.setCookie(e.value);break;case"LOCAL_STORAGE":if(!e.value||!e.key)break;await this.browser.setLocalStorage(e.key,e.value);break;case"JAVASCRIPT":{let u;try{e.environment==="BROWSER"?u=await this.browser.executePageFunction(new Hr(e.fragment?`return ${e.code}`:e.code),void 0):u=await vt({code:e.code,fragment:!!e.fragment,context:r,timeoutMs:e.timeout?e.timeout*1e3:void 0,logger:this.logger})}catch(p){throw new x("ActionFailureError",p instanceof Error?p.message:`${p}`,{cause:p})}try{JSON.stringify(u)}catch(p){throw new x("ActionFailureError",`Return value is not serializable: ${p instanceof Error?p.message:`${p}`}`,{cause:p})}return{urlAfterCommand:await this.browser.url(),succeedImmediately:!1,data:u}}case"TYPE":{let u=await this.browser.url(),p,m,g=Ch(e.target);if(g){g.elementDescriptor=cr(e,g.elementDescriptor);let{elementInteractedDisplayString:f,cache:h,thoughts:b}=await this.wrapElementTargetingCommand({target:g,cache:e.cache?.target??g.a11yData,action:T=>this.browser.typeIntoTarget(e.value,T,{force:e.force,clearContent:e.clearContent,pressKeysSequentially:e.pressKeysSequentially}),options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}});m=b,p=f,h&&(e.cache={target:h})}else await this.browser.type(e.value,{force:e.force,clearContent:e.clearContent,pressKeysSequentially:e.pressKeysSequentially});e.pressEnter&&await this.browser.press("Enter");let w={urlAfterCommand:await this.browser.url(),succeedImmediately:!1,elementInteracted:p,thoughts:m};return Ct(u,w.urlAfterCommand)&&(w.succeedImmediately=!0,w.succeedImmediatelyReason="URL changed"),w}case"HOVER":{if(at(e.target)){await this.browser.hoverUsingVisualCoordinates(e.target.percentXYLocation);break}let{cache:u,thoughts:p,elementInteractedDisplayString:m}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target??e.target.a11yData,action:g=>this.browser.hover(g,e.force),options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}});return u&&(e.cache={target:u}),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:m,thoughts:p}}case"FOCUS":{if(!Ge(e.target))throw new Error("Focus with x/y is not supported yet");let{elementInteractedDisplayString:u,cache:p,thoughts:m}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target??e.target.a11yData,action:g=>this.browser.focus(g),options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}});return p&&(e.cache={target:p}),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:u,thoughts:m}}case"BLUR":{if(!Ge(e.target))throw new Error("Blur with x/y is not supported yet");let{cache:u,thoughts:p,elementInteractedDisplayString:m}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target??e.target.a11yData,action:g=>this.browser.blur(g),options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}});return u&&(e.cache={target:u}),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:m,thoughts:p}}case"PRESS":let c=await this.browser.url();await this.browser.press(e.value);let l={urlAfterCommand:await this.browser.url(),succeedImmediately:!1};return Ct(c,l.urlAfterCommand)&&(l.succeedImmediately=!0,l.succeedImmediatelyReason="URL changed"),l;case"REQUEST":{let u=e.timeout??30,p=null,m=new AbortController,g=Object.fromEntries(Object.entries(e.headers||{}).filter(([v,_])=>v&&_)),w=new URLSearchParams;Object.entries(e.params||{}).filter(([v,_])=>v&&_).forEach(([v,_])=>{w.append(v,_)});let f;if(Vn(e.url)&&(f=e.url),qn(e.url,this.browser.baseURL)&&(f=new URL(e.url,this.browser.baseURL).toString()),!f)throw new x("ActionFailureError",`Invalid URL: ${e.url}`);let h=async()=>{try{p=await fetch(`${f}?${w.toString()}`,{headers:g,method:e.method,body:e.body,signal:m.signal})}catch(v){this.logger.error({err:v},"Failed to make HTTP request")}},b=async()=>{await new Promise(v=>setTimeout(v,u*1e3)),m.abort()};await Promise.race([b(),h()]);let T=p;if(!T)throw new x("ActionFailureError",`Fetch request timed out after ${u} seconds`);if(!T.ok)throw new x("ActionFailureError",`Fetch request failed with status ${T.status}`);let S={};T.headers.forEach((v,_)=>{S[_]=v});let C={status:T.status,headers:S};return T.headers.get("content-type")?.includes("json")?C.json=await T.json():T.headers.get("content-type")?.includes("text")&&(C.text=await T.text()),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),data:C}}case"VISUAL_DIFF":{if(e.target&&!Ge(e.target))throw new Error("Visual Diff with x/y is not supported yet");if(!e.screenshot)throw new x("ActionFailureError","Cannot execute visual diff without saved screenshot");await this.getBrowserState({skipWait:!1});let u={clearHighlights:!0,scale:"css",hideCaret:!0},p;if(!e.target)p=await this.screenshotWithDimensions(u);else{let K=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target??e.target?.a11yData,action:async G=>{},options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}});if(!K.cache)throw new x("ActionFailureError",`Could not find target given description: ${e.target.elementDescriptor}`);p=await this.screenshotWithDimensions({target:K.cache,...u})}let m;if(e.screenshot.data.startsWith("https://")){let K=await fetch(e.screenshot.data);m=Buffer.from(await K.arrayBuffer())}else m=Buffer.from(e.screenshot.data,"base64");if(Math.abs(p.height-e.screenshot.height)>10||Math.abs(p.width-e.screenshot.width)>10){let K=`${p.width}x${p.height}`,G=`${e.screenshot.width}x${e.screenshot.height}`;return{fail:!0,thoughts:`Current screenshot (${K}) does not match saved screenshot dimensions (${G}) - did you change the size of the target or the viewport?`,beforeScreenshotOverride:m,afterScreenshotOverride:p.buffer,succeedImmediately:!1,urlAfterCommand:await this.browser.url()}}let g=is(p.buffer),w={width:p.width,height:p.height},f=is(m),h={width:e.screenshot.width,height:e.screenshot.height},b,T=w.width*w.height,S=h.width*h.height,C=Math.abs(w.height-h.height),v=Math.abs(w.width-h.width);T>S?(p.buffer=await g.resize(h.width,h.height,{fit:"cover"}).toBuffer(),b="current",p.width=h.width,p.height=h.height):S>T&&(m=await f.resize(w.width,w.height,{fit:"cover"}).toBuffer(),b="saved");let _={data:Buffer.alloc(p.width*p.height*4),width:p.width,height:p.height},L=Ah(ss.decode(m).data,ss.decode(p.buffer).data,_.data,p.width,p.height,{threshold:e.threshold,diffColorAlt:[0,255,0]})/(p.width*p.height)*100,A=L>e.threshold*100,P=`Visual diff of ${L.toFixed(2)}% detected, which is ${A?"over":"under"} the threshold of ${e.threshold*100}%.`;return b&&(P+=` The ${b} screenshot was cropped since it was taller by ${C} pixels and wider by ${v} pixels.`),{fail:A,thoughts:P,beforeScreenshotOverride:p.buffer,afterScreenshotOverride:ss.encode(_,75).data,succeedImmediately:!1,urlAfterCommand:await this.browser.url()}}case"FILE_UPLOAD":{let u=await jl({url:e.fileSource.url,logger:this.logger});await this.browser.setFileChooserHandler(u);break}default:return(u=>{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,r,o){return(await this.generator.getReverseMappedDescription({browserState:e.browserState,goal:`${r}`},o)).phrase}stopRecordMode(){this.recordAbortController?.abort()}async startRecordMode(e){this.recordAbortController=new AbortController;let r=new zt({signal:this.recordAbortController.signal,...e});await this.browser.startRecording(this.recordAbortController.signal,r)}};var _h=2;async function Yl({socket:t,logger:e,storage:r,devicePixelRatio:o,generator:n}){let s=t.id,i=t.handshake.query.testId,a=t.handshake.query.baseUrl,c=t.handshake.query.viewport?JSON.parse(decodeURIComponent(t.handshake.query.viewport)):void 0,l=t.handshake.query.testMetadata?J.parse(JSON.parse(decodeURIComponent(t.handshake.query.testMetadata))):void 0;if(!i||!a)throw new Error("Socket connection request is missing testId or baseUrl");let d=await r.getOrgId(i),u=t.handshake.query.environmentVariables?JSON.parse(decodeURIComponent(t.handshake.query.environmentVariables)):{},p=t.handshake.headers["x-forwarded-for"]?.split(",")[0],m=e.child({testId:i,baseUrl:a,orgId:d,sessionId:s});if(m.info({clientIp:p,event:"connect",args:t.handshake.query,sessionId:s},"Websocket event (connect)"),p&&O.getCurrentConnectionsByIp(p)>=_h)throw e.error({clientIp:p,sessions:O.getCurrentSessionsByIp(),...t.handshake.query},"Socket connection browser creation rate limit triggered"),new Error("You have exceeded the maximum number of connections allowed per client. Momentic limits the number of simultaneously open tabs to uphold browser reliability. Please close another tab and retry or contact Momentic Support if you believe this is in error.");O.reserveCapacityByIp(p);try{await Oh({socket:t,baseUrl:a,viewport:c,testMetadata:l,orgId:d,sessionId:s,logger:m,environmentVariables:u,clientIp:p,devicePixelRatio:o,storage:r,generator:n})}catch(g){throw m.debug({err:g},"Error setting up socket session"),O.releaseCapacityByIp(p),g}return{sessionId:s,testId:i,orgId:d}}async function Oh({socket:t,baseUrl:e,viewport:r,devicePixelRatio:o,testMetadata:n,orgId:s,sessionId:i,logger:a,storage:c,generator:l,environmentVariables:d,clientIp:u}){let p=C=>{t.emit("browserState",{...C,url:e})},m={};r&&(m.viewport=r),o&&(m.deviceScaleFactor=o);let g=ir.parse(n?.advanced??{}),[w,f]=await Promise.all([j.init({baseUrl:e,networkSettings:g,waitForLoad:!1,enricher:new So(a),timeout:g.pageLoadTimeoutMs,storage:c,logger:a,onBrowserUpdateDuringLoad:p,contextArgs:m}),Jt.init(s)]),h=new Qt({browser:w,generator:l,config:_r,logger:a,flagStore:f,orgId:s,storage:c}),b=Zi(t,i,a),T=async()=>{clearInterval(b.timer)},S=new Se({baseUrl:e,currentUrl:await h.browser.url(),variablesFromEnvironment:d});if(!t.connected)throw await w.cleanup(),new Error("Socket not connected anymore, not proceeding with session setup");t.emit("session",{url:e,userAgent:j.USER_AGENT,viewport:await h.browser.getViewport(),sessionId:i}),O.registerSession({controller:h,context:S,sessionId:i,cleanup:T,clientIp:u})}var Xl=[Ta,Nl,zl,Gl,Ul,$l,ea,Bl,Fl,pa,ha,ma,fa,ga,ya,wa,ra,ta,ba,oa];var Jl=t=>{let{logger:e}=t,r=new Ih(t.baseServer,{cors:{origin:"*",methods:["GET","POST"]},pingTimeout:3e4,pingInterval:6e4,maxHttpBufferSize:1e7});return r.on("connection",async o=>{let n;try{n=await Yl({...t,socket:o})}catch(s){e.error({event:"connection",type:"websocket",err:s},"Failed to setup connection"),o.emit("error",{message:s instanceof Error?s.message:`${s}`}),o.disconnect(!0);return}Xl.forEach(s=>xh(s,{socket:o,metadata:n,...t}))}),r};var xh=(t,e)=>{let r=t.createHandler(e),o=(...n)=>{["cloudMouseMove","cloudScroll"].includes(t.event)||e.logger.debug({...e.metadata,event:t.event,args:n},`Websocket event (${t.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 instanceof Error?i.message:`${i}`})};try{let i=r.apply(void 0,n);i&&typeof i.catch=="function"&&i.catch(s)}catch(i){s(i)}};e.socket.on(t.event,o)};import{z as Lh}from"zod";import Ph from"fetch-retry";var kh=Ph(global.fetch),ne=class{static API_VERSION="v1";baseURL;apiKey;constructor(e){this.baseURL=e.baseURL,this.apiKey=e.apiKey}async sendRequest(e,r){let o=await kh(`${this.baseURL}${e}`,{retries:1,retryDelay:1e3,method:"POST",body:JSON.stringify(r),headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}});if(!o.ok)throw new Error(`Request to ${e} failed with status ${o.status}: ${await o.text()}`);return o.json()}};var _t=class extends ne{constructor(e){super(e)}async getRecommendedChunks(e){let r=await this.sendRequest(`/${ne.API_VERSION}/web-agent/recommend-chunks`,e);return Ai.parse(r)}async getScreenshotFromS3(e){let r=await this.sendRequest(`/${ne.API_VERSION}/s3/visual-diff-screenshot`,{url:e});return Lh.string().parse(r)}async getElementLocation(e,r){let o=await this.sendRequest(`/${ne.API_VERSION}/web-agent/locate-element`,{...e,disableCache:r});return ui.parse(o)}async getAssertionResult(e,r){let o={...e,disableCache:r},n=await this.sendRequest(`/${ne.API_VERSION}/web-agent/assertion`,o);return li.parse(n)}async getProposedCommand(e,r){let o=await this.sendRequest(`/${ne.API_VERSION}/web-agent/next-command`,{url:e.url,browserState:e.browserState,goal:e.goal,history:e.history,numPrevious:e.numPrevious,lastCommand:e.lastCommand,screenshot:e.screenshot,disableCache:r});return ci.parse(o)}async getGranularGoals(e,r){let o=await this.sendRequest(`/${ne.API_VERSION}/web-agent/split-goal`,{url:e.url,goal:e.goal,disableCache:r});return di.parse(o)}async getReverseMappedDescription(e,r){let o=await this.sendRequest(`/${ne.API_VERSION}/web-agent/reverse-mapped-description`,{goal:e.goal,browserState:e.browserState,disableCache:r});return pi.parse(o)}async getTextExtraction(e,r){let o={goal:e.goal,browserState:e.browserState,returnSchema:e.returnSchema,disableCache:r},n=await this.sendRequest(`/${ne.API_VERSION}/web-agent/text-extraction`,o);return Vo.parse(n)}async getTestResultClassification(e){let r=await this.sendRequest(`/${ne.API_VERSION}/web-agent/result-classification`,e);return jo.parse(r)}};import{Router as Mh}from"express";function ge(t){return function(...e){let r=e[e.length-1],o=t(...e);Promise.resolve(o).catch(r)}}var Ql=Mh();Ql.get("/",ge((t,e)=>{let r=Ji();e.status(200).json(r)}));var Zl=Ql;import{Router as Uh}from"express";import eu,{multistream as Dh}from"pino";import Nh from"pino-pretty";var as=new Map,Fh=process.env.NODE_ENV==="production",cs=class t{consoleLogger;ddClientToken;hostname;bindingAttributes;excludeAttributesInConsoleLogs;excludeTimestamps;disableConsoleLogs;site="https://http-intake.logs.us5.datadoghq.com/api/v2/logs";constructor({bindings:e,clientToken:r,hostname:o,excludeAttributesInConsoleLogs:n,excludeTimestamps:s,disableConsoleLogs:i}){this.ddClientToken=r,this.hostname=o,this.excludeAttributesInConsoleLogs=n,this.excludeTimestamps=s,this.disableConsoleLogs=i,this.bindingAttributes={...e,env:process.env.NODE_ENV};let a={base:n?{}:this.bindingAttributes,errorKey:"err",level:"debug",timestamp:!s};this.consoleLogger=Fh?eu(a):eu(a,Dh([{stream:Nh({colorize:!0})}]))}child(e){return new t({clientToken:this.ddClientToken,bindings:{...this.bindingAttributes,...e},hostname:this.hostname,excludeAttributesInConsoleLogs:this.excludeAttributesInConsoleLogs,disableConsoleLogs:this.disableConsoleLogs})}flush(e){this.disableConsoleLogs||this.consoleLogger.flush(e)}log(e,r,o,...n){if(r&&o===void 0&&(o=`${r}`,r={}),this.disableConsoleLogs||this.consoleLogger[e](e==="error"?r:{testId:r.testId,runId:r.runId},o,...n),typeof r=="object"&&r&&"err"in r&&r.err instanceof Error){let i={};for(let a of Object.getOwnPropertyNames(r.err))i[a]=r.err[a];i.name=r.err.name,r.err=i}let s=Object.assign({},this.bindingAttributes,r&&typeof r=="object"?r:{});n.length>0&&(s.args=n),(async()=>{try{let i=await fetch(this.site,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json","DD-API-KEY":this.ddClientToken},body:JSON.stringify([{ddsource:this.bindingAttributes.app,hostname:this.hostname??"vercel",service:"momentic",message:{message:o||"",...s,level:e}}])});if(!i.ok)throw new Error(`Failed to log to Datadog: ${i.statusText})}`)}catch(i){this.disableConsoleLogs||this.consoleLogger.warn({obj:r,msg:o,args:n,err:i},"Failed to log to Datadog")}})()}debug(e,r,...o){this.log("debug",e,r,...o)}info(e,r,...o){this.log("info",e,r,...o)}warn(e,r,...o){this.log("warn",e,r,...o)}error(e,r,...o){this.log("error",e,r,...o)}bindings(){return this.bindingAttributes}setMinLevel(e){this.consoleLogger.level=e}enableConsoleLogs(){this.disableConsoleLogs=!1}},Ao=({app:t,clientToken:e,hostname:r,excludeAttributesInConsoleLogs:o,excludeTimestamps:n,disableConsoleLogs:s})=>{if(!process.env.DD_CLIENT_TOKEN&&!process.env.NEXT_PUBLIC_DD_CLIENT_TOKEN&&!e)throw new Error("Missing DD_CLIENT_TOKEN");return as.has(t)||as.set(t,new cs({bindings:{app:t},hostname:r,clientToken:e||process.env.NEXT_PUBLIC_DD_CLIENT_TOKEN||process.env.DD_CLIENT_TOKEN,excludeAttributesInConsoleLogs:o,excludeTimestamps:n,disableConsoleLogs:s})),as.get(t)};import{hostname as Gh}from"os";var Te=Ao({app:"desktop-server",clientToken:"pubcfd7516a5c0ba852b42675cd97bee027",hostname:Gh(),excludeAttributesInConsoleLogs:!0,excludeTimestamps:!0,disableConsoleLogs:!0});var Or=Uh();Or.get("/",ge(async(t,e)=>{let r=await Hi(Te);e.status(200).json(r)}));Or.get("/metadata",ge((t,e)=>{let r=Bi();e.status(200).json(r)}));Or.post("/",ge(async(t,e)=>{let r;try{r=si.parse(t.body)}catch(n){e.status(400).json({error:`Invalid request body: ${n}`});return}try{ar(r.name)}catch(n){e.status(400).json({error:`Invalid module name: ${n}`});return}let o=$i(r.name,r.steps);e.status(201).json(o)}));Or.get("/:moduleId",ge(async(t,e)=>{if(!t.params.moduleId){e.status(400).json({error:"Missing moduleId in url path."});return}let r=await Wi(t.params.moduleId,Te);e.json(r)}));var tu=Or;import{Router as zh}from"express";import{existsSync as $h}from"fs";import{join as Oo}from"path";import{v4 as Bh}from"uuid";var us="https://api.momentic.ai",ru=t=>{us=t},Ir=()=>us,Ro,ls,ou=async t=>{if(Ro)return;let e=new oe({baseURL:us,apiKey:t});try{Ro=await e.getOrgId(),ls=t}catch(r){throw new Error(`Error checking API key against server: ${r}`)}},_o=()=>{if(!Ro)throw new Error("Your organization ID appears invalid. Please contact Momentic Support.");return Ro},nu=()=>{if(!ls)throw new Error("Your API key appears invalid. Please contact Momentic Support.");return ls};var Ot=zh();Ot.get("/",ge((t,e)=>{let r=gr(Z,Te);e.status(200).json(r)}));Ot.post("/",ge((t,e)=>{let r;try{r=ni.parse(t.body)}catch(a){e.status(400).json({error:`Invalid request body: ${a}`});return}try{ar(r.name)}catch(a){e.status(400).json({error:a.message});return}let n={id:Bh(),name:r.name,baseUrl:r.baseUrl,schemaVersion:X,advanced:{disableAICaching:!1,viewport:r.viewport??ut},retries:0,steps:[]};r.environment&&(n.envs=[{name:r.environment,default:!0}]);let s=Ki(n,r.name),i={...n,testPath:s};e.status(201).json(i)}));Ot.get("/:testPath",ge(async(t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let r=Oo(Z,t.params.testPath);if(!$h(r)){e.status(400).json({error:`Test not found at path: ${r}`});return}let o=await no(r,Tt,Te);e.status(200).json(o)}));Ot.patch("/:testPath/metadata",ge(async(t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let r;try{r=oi.parse(t.body)}catch(n){e.status(400).json({error:`Invalid request body: ${n}`});return}let o=Oo(Z,t.params.testPath);mr(r,o),e.status(201).json({message:"ok"})}));Ot.patch("/:testPath",ge(async(t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let r;try{r=ri.parse(t.body)}catch(l){e.status(400).json({error:`Invalid request body: ${l}`});return}let o=Oo(Z,t.params.testPath),n;try{n=Yi(o)}catch(l){e.status(400).json({error:`Existing test file on disk is invalid: ${l}`});return}let{stepsToSave:s,moduleUpdates:i,cachesToSave:a}=de({steps:r.steps,testId:n.id,orgId:_o()});i.forEach(l=>zi(l)),mr({steps:s},o),await new oe({apiKey:nu(),baseURL:Ir()}).updateStepCaches({entries:a,testId:n.id}),e.status(201).json({message:"ok"})}));Ot.patch("/:testPath/environments",ge(async(t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let r;try{r=ii.parse(t.body)}catch(n){e.status(400).json({error:`Invalid request body: ${n}`});return}let o=Oo(Z,t.params.testPath);mr({envs:r.defaultEnv?[{name:r.defaultEnv,default:!0}]:[]},o),e.status(201).json({message:"ok"})}));var su=Ot;async function iu({momenticServerUrl:t,apiKey:e,serverPort:r,appPort:o,staticDir:n,devicePixelRatio:s}){if(!jh(Z)||!Vh(Z).isDirectory()){let m=Yh.isAbsolute(Z);throw new Error(`Root folder ${Z} does not exist${m?"":` in the current directory ("${process.cwd()}")`}. Please run \`npx momentic init\` if you wish to setup Momentic here for the first time.`)}t&&ru(t),Te.info("Checking API key"),await ou(e);let a=`http://localhost:${r}`;o&&(a=`http://localhost:${o}`);let c=Xh(n);await new Promise(m=>{c.listen(r,()=>{Te.info(`Server is running at http://localhost:${r}`),m()})});let d=new _t({baseURL:Ir(),apiKey:e}),u=new oe({baseURL:Ir(),apiKey:e}),p=new Ut(u,_o());Jl({baseServer:c,generator:d,storage:p,logger:Te,devicePixelRatio:s}),await Kh(a)}function Xh(t){let e=ds();e.use(Wh()),e.use(Hh.json({limit:"50mb"}));let r=ds.Router();if(r.use("/tests",su),r.use("/modules",tu),r.use("/environments",Zl),e.use("/api",r),e.use((n,s,i)=>{n.path!=="/healthcheck"&&Te.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&&Te.error({url:n.url,method:n.method,statusCode:s.statusCode},"Request completed in error")}),i()}),e.use((n,s,i,a)=>{n instanceof Error&&n.message.includes("BadRequestError: request aborted")||(Te.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=ds.static(t);e.use("/",n),e.use("*",n)}return qh.createServer(e)}import{existsSync as qf}from"fs";import Fu,{dirname as Kf,join as Yf}from"path";import{chdir as Xf}from"process";import{fileURLToPath as Jf}from"url";var au="0.0.91";import xr from"chalk";var It=(...t)=>{Ie(xr.blue(...t))};var Pr=(...t)=>{Ie(xr.dim(...t))};var se=(...t)=>{Ie(xr.green(...t))},Ie=(...t)=>{console.log(...t)},le=(...t)=>{console.error(xr.yellow(...t))},F=(...t)=>{console.error(xr.red(...t))};import{hostname as Qh}from"os";var I=Ao({app:"cli",clientToken:"pub7eb923f18fb3f1d42ac5eba8c5ea13a5",hostname:Qh(),excludeAttributesInConsoleLogs:!0,excludeTimestamps:!0,disableConsoleLogs:!0});import{execSync as Zh}from"child_process";import{platform as ef}from"os";function ps(){return cu()?(It("Setting device pixel ratio to 2 automatically since a Mac OS Retina screen was detected."),It("If you are using a low pixel-density monitor, you should manually set --pixel-ratio to 1 to avoid incorrect viewport calculations."),It("Confirm your device's pixel-ratio at https://www.mydevice.io."),2):(It("Setting device pixel ratio to 1."),It("If you are using Momentic on a high-pixel density (HiDPI) monitor, relaunch with the --pixel-ratio option to avoid incorrect viewport calculations"),It("Confirm your device's pixel-ratio at https://www.mydevice.io."),1)}function cu(){return ef()==="darwin"&&Zh("system_profiler SPDisplaysDataType").toString().includes("Retina")}function ms(t){cu()&&t===1&&(le("If you are using Momentic on a Retina screen, relaunch with the --pixel-ratio option to avoid incorrect viewport calculations."),le("Confirm your device's pixel-ratio at https://www.mydevice.io."))}import{createHash as pu}from"crypto";import{existsSync as mu,readFileSync as bs,writeFileSync as gu}from"fs";import{join as hu}from"path";import{parse as mf}from"yaml";import{existsSync as xo,mkdirSync as lu,readdirSync as sf,statSync as fs}from"fs";import{homedir as uu}from"os";import{join as We,resolve as af}from"path";import{registry as gs}from"playwright-core/lib/server";function tf(t){let e=[],r=[];for(let o of t){let n=gs.findExecutable(o);!n||n.installType==="none"?e.push(o):r.push(n)}return r}async function Io(){let t=tf(["chromium"]);await gs.installDeps(t,!1),await gs.install(t,!1)}import{statSync as rf}from"fs";import of from"readline/promises";var hs=!1,nf=(()=>{try{return rf("/.dockerenv"),!0}catch{return!1}})();async function Ae(t,e){if(process.env.CI||hs||!process.stdout.isTTY||nf)return!0;I.flush(),await new Promise(i=>setTimeout(i,500));let r=of.createInterface({input:process.stdin,output:process.stdout}),o=t.split("."),n;if(o.length===1)n=t;else{let i=`${o.slice(0,o.length-1).join(". ").trim()}.`;e?le(i):Ie(i),n=o[o.length-1].trim()}let s=await r.question(`${n} ('y' for yes / n for no / 'A' to accept all) `);return r.close(),s==="A"?(hs=!0,setTimeout(()=>{hs=!1},3e3),!0):s.toLowerCase()==="y"}var cf=[Be,Kr,Ft],je=pf(),xt=We(je,Be),ws=We(je,Kr),Ve=We(je,Ft),lf=!!process.env.CI||!process.stdout.isTTY,ys=We(uu(),"momentic",rn),du=[xt,ws,Ve];function kr(t){try{return xo(t)&&fs(t).isDirectory()}catch(e){return I.error({err:e},`Error reading path ${t} during directory exists check`),!1}}async function rt(t){let e=process.versions.node,r=parseInt(e.split(".")[0]);(isNaN(r)||r<20)&&(F(`Node.js version 20 or higher is required to run the Momentic CLI. Detected: ${process.versions.node}.`),process.exit(1));let o=await t.client.getOrgId();return await Io(),await df(t),o}async function uf(t){kr(je)||!t&&!await Ae(`The root '${Xe}' folder was not found in the current directory. You will need to invoke the Momentic CLI from this directory in the future. Proceed?`)&&(F("Setup cancelled."),process.exit(1)),kr(ys)||(!t&&!await Ae("The Chrome cache folder was not found in your home directory. Proceed with creation?")&&(F("Setup cancelled."),process.exit(1)),lu(ys,{recursive:!0}));for(let e of du)kr(e)||(!t&&!await Ae(`'${e}' folder was not found in the current directory. Create it now?`)&&(F("Setup cancelled."),process.exit(1)),lu(e,{recursive:!0}))}function Lr(t,e,r=new Set){for(let o of t){let n=af(o),s=!1;try{s=xo(n)&&fs(n).isDirectory()}catch(i){I.error({err:i},`Error reading path ${n} during collect paths`)}if(n&&s){let i=sf(n).map(a=>We(n,a));Lr(i,e,r);continue}if(n.endsWith(".yaml")){try{if(!xo(n)||!fs(n).isFile()){I.error(`File not found or unreadable: ${n}`);continue}}catch(i){I.error({err:i},`Error reading file ${n} during collect paths`);continue}if(!e(n))continue;r.add(n)}}return r}async function df({client:t,createFolders:e,skipPrompts:r,pullEnvs:o}){if(uu()||(F("Your home directory could not be found. Please ensure the HOME environment variable is set for POSIX systems, and the USERPROFILE environment variable is set for Windows systems."),process.exit(1)),e)await uf(r);else for(let n of[je,ys,...du])!kr(n)&&!lf&&(F(`The '${n}' folder is required to run the Momentic CLI but was not found in the current directory. Please run 'npx momentic@latest init' to create the required folders.`),F(`The current working directory is: ${process.cwd()}`),process.exit(1));o&&(!r&&!await Ae("Pull environments from Momentic Cloud? Environment definitions may be required to run tests locally.")&&(F("Setup cancelled."),process.exit(1)),await Po({client:t,all:!0,skipPrompts:r}))}function pf(){let t=o=>xo(We(o,Xe))&&cf.some(n=>kr(We(o,Xe,n))),e=process.cwd();for(;e!=="/"&&!t(e);)e=We(e,"..");let r;return t(e)?r=e:r=process.cwd(),r!==process.cwd()?le(`Current working directory does not contain a ${Xe} folder, treating ${r} as the Momentic root folder instead`):I.info(`Identified ${r} as the Momentic root folder`),We(r,Xe)}async function Po({envNames:t,client:e,all:r,skipPrompts:o}){let n=await e.getAllEnvironments();t&&!r&&(n=n.filter(a=>t?.includes(a.name))),mu(Ve)||(F(`${Ve} does not exist in the current directory. Please run 'npx momentic@latest init' first.`),process.exit(1));let s=[],i=[];for(let a of n){let c=hu(Ve,`${a.name}.yaml`);if(!mu(c))gu(c,ln(a)),s.push(c);else{let l=pu("sha256").update(bs(c)).digest("hex"),d=ln(a),u=pu("sha256").update(d).digest("hex");if(l!==u){if(!o&&!await Ae(`Environment ${a.name} already exists on disk but needs to be updated. Overwrite?`))return;gu(c,d),i.push(c)}}}se("Pulled environments successfully!"),s.length&&(se("Created:"),s.forEach(a=>se(" ",a))),i.length&&(se("Updated:"),i.forEach(a=>se(" ",a)))}function gf(t){return t.endsWith(".yaml")?bs(t,"utf8").includes("momentic/environment")?!0:(le(`Skipping YAML that is not a Momentic environment: ${t}`),!1):!1}async function fu({client:t,names:e,all:r,yes:o}){let n;r?n=[Ve]:n=e.map(a=>hu(Ve,`${a.toLowerCase()}.yaml`));let s=Lr(n,gf);Ie(`Found ${s.size} environments to push:`),s.forEach(a=>Ie(" ",a));let i=[];for(let a of s){let c=mf(bs(a,"utf-8"));try{let l=we.parse(c);i.push(l)}catch(l){F(`${a} failed to parse as a valid environment file.`),F(l),process.exit(1)}}!o&&!await Ae("Pushing environments can overwrite variables on cloud and instantly affect scheduled runs. Continue?",!0)||(await t.updateEnvironments(i),se("Pushed environments successfully!"))}import{Argument as ko,Option as Ne}from"commander";import{cpus as yu}from"os";import{z as hf}from"zod";var ot=new Ne("--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),nt=new Ne("--server <server>","Momentic server to use. Leave unchanged unless using Momentic on-premise.").default("https://api.momentic.ai"),st=new Ne("-y, --yes","Skip all confirmation prompts.").env("CI"),wu=new Ne("-w, --wait","Wait for tests to finish running before exiting. Only applicable when running tests remotely").implies({remote:!0}),Ts=new Ne("--no-report","Skip reporting test results to Momentic Cloud when running with the --local flag.").implies({local:!0}),Ss=new Ne("--pixel-ratio <number>","Device pixel ratio of your screen or monitor. Mac OS Retina displays and machines marketed as 'HiDPI' should set this to 2. Visit https://www.mydevice.io/ to find your device's pixel ratio.").argParser(t=>parseInt(t,10)),bu=new Ne("--env <env>","Name of the environment to use when running tests."),Tu=new Ne("--url-override <urlOverride>","Fully qualified url (e.g. https://www.google.com) to start all tests from. Overrides any default starting url set from the test or environment."),Es=new Ne("-a, --all","Select all tests in your organization from Momentic Cloud. Cannot be used together with <tests> arguments."),vs=new Ne("-a, --all","Select all environments in your organization from Momentic Cloud. Cannot be used together with <paths> arguments."),Su=new ko("<tests...>",`One or more test paths to pull from Momentic Cloud.
1346
+ `)}`}async executeCommand(e,r,o,n=!1){let s=this.commandHistory[this.commandHistory.length-1];if(!n&&(!s||s.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 i=Date.now(),a=await this.executePresetStep(e,r,o),c=Date.now()-i;return this.logger.debug({result:a,duration:c},"Got execution result"),a.succeedImmediately&&!n&&(this.pendingInstructions.pop(),this.pendingInstructions.length>0&&(a.succeedImmediately=!1)),a.elementInteracted&&"target"in e&&e.target&&e.target.type==="description"&&!e.target.elementDescriptor&&(e.target.elementDescriptor=a.elementInteracted.trim()),n||(s.generatedStep=e,s.serializedCommand=Ye(e),s.state="DONE"),a}async executeAssertion(e){let r=!1,o,n=async()=>{try{o=await this.browser.screenshot({clearHighlights:!0,scale:"css"}),Kl(this.uploadScreenshotForDebugging("assertion",o),{milliseconds:5e3,fallback:()=>{}})}catch(g){this.logger.warn({err:g},"Failed to take screenshot for assertion, continuing without it")}},s=await this.browser.url(),[i]=await Promise.all([this.getBrowserState({filterByViewport:e.filterByViewport}),n()]),{serializedTree:a}=i,{tree:c}=i,l=.2*Eo["gpt-4o-2024-05-13"],d=os({serializedTree:a,tokenLimit:l});if(d){let w=(await this.generator.getRecommendedChunks({...d,description:e.assertion,tokenLimit:l})).ids;w.length===0?this.logger.debug("RAG returned no important information for assertion"):(a=c.pruneUsingRelevantIds(new Set(w)).serialize(),this.logger.debug({browserState:a},"Pruned a11y tree with RAG"),r=!0)}let u=this.getSerializedHistory(s,a),p={goal:e.assertion,url:s,browserState:a,history:u,lastCommand:this.lastExecutedCommand,screenshot:o?.toString("base64"),numPrevious:this.commandHistory.length},m=await this.generator.getAssertionResult(p,e.disableCache);if(m.relevantElements&&Promise.all(Array.from(new Set(m.relevantElements)).slice(0,5).map(g=>this.browser.highlight({id:g}))),this.logger.debug({usedRag:r,result:m},"Got assertion result"),!m.result)throw new x("AssertionFailureError",m.thoughts);return{succeedImmediately:!1,thoughts:m.thoughts,urlAfterCommand:s,beforeScreenshotOverride:o}}async wrapMultiElementTargetingCommand(e,r,o,n,s=1){let i=await Promise.all(e.map((a,c)=>this.wrapElementTargetingCommand({target:a,cache:r[c],action:async l=>l,options:n})));try{return{result:await o(...i.map(c=>c.result)),caches:i.map(c=>c.cache),elementInteractedDisplayStrings:i.map(c=>c.elementInteractedDisplayString)}}catch(a){if(s>0)return this.logger.debug({err:a},"Failed to execute action with multiple cached targets, retrying with AI"),this.wrapMultiElementTargetingCommand(e,e.map(()=>{}),o,n,s-1);throw new x("ActionFailureError",a.message,{cause:a})}}async wrapFrameUseCommand(e,r){if(!r)return e();let o=this.browser.getActiveFrame();try{return this.logger.debug({frameUrl:r},"Setting parent iframe target"),this.browser.setActiveFrame({type:"url",url:r}),await e()}finally{this.browser.setActiveFrame(o)}}async wrapElementTargetingCommand(e){return this.wrapFrameUseCommand(()=>this.wrapElementTargetingCommandHelper(e),e.options.iframeUrl)}async wrapElementTargetingCommandHelper({target:e,cache:r,action:o,options:n}){let{disableCache:s,filterByViewport:i,useSelector:a}=n,c=n.retriesWithAI??1,l;if((!r||s)&&!Fo(e))throw new x("ActionFailureError","Cannot target element with no cached data or element descriptor");if(a){if(e.type!=="description")throw new x("ActionFailureError","Cannot use selector along with non-description target");let u={id:-1,selector:e.elementDescriptor},p=await this.browser.resolveTarget(u);return{result:await o(p.locator),cache:u,elementInteractedDisplayString:p.displayString}}s&&(this.logger.debug("Cache explicitly disabled for this step"),r=void 0);let d=!!r&&Dr(r);if(!r){this.logger.debug("No cached locator data for target, prompting AI for fresh location"),c--;let u=await this.locateElement({description:e.elementDescriptor,disableCache:s,filterByViewport:i,iframeUrl:n.iframeUrl});r=u.target,l=u.thoughts}try{let u=await this.browser.resolveTarget(r),p=await o(u.locator);return d?this.logger.debug({cache:r},"Successfully used cached target to perform action"):this.logger.debug({cache:r},"Successfully generated and used new target data"),{result:p,cache:r,thoughts:l,elementInteractedDisplayString:u.displayString}}catch(u){if(u instanceof x)throw u;if(c>0&&e)return this.logger.debug({err:u,cache:r},"Failed to execute action with cached target, retrying with AI"),this.wrapElementTargetingCommand({target:e,cache:void 0,action:o,options:{...n,retriesWithAI:c}});throw new x("ActionFailureError",u.message,{cause:u})}}async screenshotWithDimensions(e){let r=await this.browser.screenshot(e),o=await is(r).metadata();return{buffer:r,width:Math.ceil(o.width??0),height:Math.ceil(o.height??0)}}async executePresetStep(e,r,o){let n;try{n=await this.resolveCommandTemplateStrings(e,r)}catch(s){throw new x("ActionFailureError",s.message,{cause:s})}try{let s=this.browser.getOpenPages(),i=await this.browser.url(),a=await this.executePresetActionHelper(e,r,o),c=!0;(e.type==="NAVIGATE"||e.type==="NEW_TAB"||e.type==="TAB"||e.type==="REFRESH")&&(c=!1);let l=this.browser.getOpenPages(),d=await this.browser.url();if(c&&l.length!==s.length)for(let u=l.length-1;u>=0;u--){let p=l[u];if(p!==i&&p!==d){await this.browser.switchToPage(p,u);break}}return a}catch(s){throw this.logger.error({err:s},"Error thrown in action controller"),s}finally{this.restoreCommandTemplateReplacements(e,n)}}restoreCommandTemplateReplacements(e,r={}){for(let[o,n]of Object.entries(r))ns(e,o,n)}async resolveCommandTemplateStrings(e,r,o="",n={}){let s=["type","a11yData","thoughts","cache"];for(let i in e){if(s.includes(i))continue;let a=e[i],c=o?`${o}.${i}`:i;if(typeof a=="string"&&a.includes("{{")){let l=await ho({s:a,context:r,logger:this.logger});if(a===l)continue;n[c]=a,e[i]=l}else typeof a=="object"&&a!==null&&!Array.isArray(a)&&await this.resolveCommandTemplateStrings(a,r,c,n)}return n}async executePresetActionHelper(e,r,o){switch(o=o||"disableCache"in e&&e.disableCache,e.type){case"SUCCESS":let n=e.condition;return n?.assertion.trim()?this.wrapFrameUseCommand(()=>this.executeAssertion(n),n?.iframeUrl):{succeedImmediately:!1,urlAfterCommand:await this.browser.url()};case"AI_ASSERTION":{if(!e.assertion.trim())throw new x("ActionFailureError","Missing assertion");return this.wrapFrameUseCommand(()=>this.executeAssertion(e),e.iframeUrl)}case"AI_WAIT":{if(!e.assertion.trim())throw new x("ActionFailureError","Missing assertion");let u=Date.now();if(e.timeout&&e.timeout>60)throw new x("AssertionFailureError",`AI wait timeout of ${e.timeout} exceeds the maximum allowed timeout of 60s`);let p=(e.timeout??10)*1e3,m,g,w=0;for(;Date.now()-u<p;)try{m=await this.executeAssertion({...e,type:"AI_ASSERTION"});break}catch(f){g=f instanceof Error?f:new Error(`${f}`),this.logger.info({err:f},`AI_WAIT assert attempt ${w} failed, retrying...`),w++,await W(p/10)}if(!m){let f=`AI wait still failing after ${p}ms.`;throw g&&(f+=` Latest result: ${g.message}`),new x("AssertionFailureError",f)}return m}case"AI_EXTRACT":{if(!e.goal.trim())throw new x("ActionFailureError","Cannot perform AI extraction without goal");let u=await this.browser.getCondensedHtml(),p=await this.generator.getTextExtraction({goal:e.goal,browserState:u,returnSchema:e.schema},o);if(p.result==="NOT_FOUND")throw new x("ActionFailureError","No relevant data found for extraction goal on this page");return{data:p.result,succeedImmediately:!1,urlAfterCommand:await this.browser.url()}}case"NAVIGATE":if(!Vn(e.url)&&!qn(e.url,this.browser.baseURL))throw new x("ActionFailureError",`Invalid URL provided to navigate command: ${e.url}`);await this.browser.navigate({url:e.url,loadTimeoutMs:e.loadTimeout?e.loadTimeout*1e3:void 0,networkIdleTimeoutMs:e.networkTimeout?e.networkTimeout*1e3:5e3});break;case"DIALOG":this.browser.registerDialogHandler(e.action);break;case"CAPTCHA":if(!this.browser.canSolveCaptchas())break;let s=await this.browser.solveCaptcha();s&&(await this.wrapElementTargetingCommand({target:{type:"description",elementDescriptor:"the captcha image solution input"},cache:void 0,action:u=>this.browser.click(u),options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}}),await this.browser.type(s,{clearContent:!0,pressKeysSequentially:!1}));break;case"GO_BACK":await this.browser.goBack();break;case"GO_FORWARD":await this.browser.goForward();break;case"SCROLL_LEFT":case"SCROLL_RIGHT":case"SCROLL_DOWN":case"SCROLL_UP":if(e.target&&!Ge(e.target))throw new Error("Scroll with x/y is not supported yet");let i,a;if(e.target&&(e.target.elementDescriptor.trim()||e.target.a11yData)){let{cache:u,thoughts:p,elementInteractedDisplayString:m}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target??e.target.a11yData,action:g=>this.browser.hover(g,!1),options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}});i=m,u&&(e.cache={target:u}),a=p}switch(e.type){case"SCROLL_UP":await this.browser.scrollUp(e.deltaY);break;case"SCROLL_DOWN":await this.browser.scrollDown(e.deltaY);break;case"SCROLL_LEFT":await this.browser.scrollLeft(e.deltaX);break;case"SCROLL_RIGHT":await this.browser.scrollRight(e.deltaX);break}return{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:i,thoughts:a};case"WAIT":await this.browser.wait(e.delay*1e3);break;case"REFRESH":await this.browser.refresh({networkIdleTimeoutMs:e.networkTimeout?e.networkTimeout*1e3:void 0,loadTimeoutMs:e.loadTimeout?e.loadTimeout*1e3:void 0});break;case"CLICK":{let u={doubleClick:e.doubleClick,rightClick:e.rightClick,force:e.force,waitForUrl:e.waitForUrl};if(at(e.target)){await this.browser.clickUsingVisualCoordinates(e.target.percentXYLocation,u);break}let p=await this.browser.url(),m={disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl},{elementInteractedDisplayString:g,cache:w,thoughts:f}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target??e.target.a11yData,action:b=>this.browser.click(b,u),options:m});w&&(e.cache={target:w});let h={urlAfterCommand:await this.browser.url(),succeedImmediately:!1,elementInteracted:g,thoughts:f};return Ct(p,h.urlAfterCommand)&&(h.succeedImmediately=!0,h.succeedImmediatelyReason="URL changed"),h}case"DRAG":{if(at(e.fromTarget)&&at(e.toTarget)){await this.browser.dragAndDropUsingVisualCoordinates(e.fromTarget.percentXYLocation,e.toTarget.percentXYLocation,{force:e.force,hoverSeconds:e.hoverSeconds});break}if(at(e.fromTarget)||at(e.toTarget))throw new Error("Drag and drop targets must be both coordinates or both descriptions");let{caches:u,elementInteractedDisplayStrings:p}=await this.wrapMultiElementTargetingCommand([e.fromTarget,e.toTarget],[e.cache?.fromTarget,e.cache?.toTarget],(m,g)=>this.browser.dragAndDrop(m,g,{force:e.force,hoverSeconds:e.hoverSeconds}),{filterByViewport:e.filterByViewport,useSelector:e.useSelector,disableCache:o});return u&&u.every(m=>m)&&(e.cache={fromTarget:u[0],toTarget:u[1]}),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:p[0]}}case"MOUSE_DRAG":{if(e.target&&!Ge(e.target))throw new Error("Scroll with x/y is not supported yet");let u,p;if(e.target?.elementDescriptor){let w=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:async f=>f,options:{filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl,disableCache:o}});u=w.result,p=w.elementInteractedDisplayString}let m=parseInt(e.deltaX),g=parseInt(e.deltaY);if(isNaN(m)||isNaN(g))throw new x("ActionFailureError",`Invalid pixel values passed to mouse drag command: (${e.deltaX}, ${e.deltaY})`);return await this.browser.mouseDrag(m,g,e.steps,u,{force:e.force}),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:p}}case"SELECT_OPTION":{if(!Ge(e.target))throw new Error("Select with x/y is not supported yet");let u=e.target.elementDescriptor,{cache:p,thoughts:m,elementInteractedDisplayString:g}=await this.wrapElementTargetingCommand({target:{type:"description",elementDescriptor:cr(e,u)},cache:e.cache?.target??e.target.a11yData,action:w=>this.browser.selectOption(w,e.option),options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}});return p&&(e.cache={target:p}),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:g,thoughts:m}}case"TAB":await this.browser.switchToPage(e.url,void 0,{networkIdleTimeoutMs:e.networkTimeout?e.networkTimeout*1e3:void 0,loadTimeoutMs:e.loadTimeout?e.loadTimeout*1e3:void 0});break;case"NEW_TAB":await this.browser.createNewTab(e.url,{networkIdleTimeoutMs:e.networkTimeout?e.networkTimeout*1e3:void 0,loadTimeoutMs:e.loadTimeout?e.loadTimeout*1e3:void 0});break;case"COOKIE":if(!e.value)break;await this.browser.setCookie(e.value);break;case"LOCAL_STORAGE":if(!e.value||!e.key)break;await this.browser.setLocalStorage(e.key,e.value);break;case"JAVASCRIPT":{let u;try{e.environment==="BROWSER"?u=await this.browser.executePageFunction(new Hr(e.fragment?`return ${e.code}`:e.code),void 0):u=await vt({code:e.code,fragment:!!e.fragment,context:r,timeoutMs:e.timeout?e.timeout*1e3:void 0,logger:this.logger})}catch(p){throw new x("ActionFailureError",p instanceof Error?p.message:`${p}`,{cause:p})}try{JSON.stringify(u)}catch(p){throw new x("ActionFailureError",`Return value is not serializable: ${p instanceof Error?p.message:`${p}`}`,{cause:p})}return{urlAfterCommand:await this.browser.url(),succeedImmediately:!1,data:u}}case"TYPE":{let u=await this.browser.url(),p,m,g=Ch(e.target);if(g){g.elementDescriptor=cr(e,g.elementDescriptor);let{elementInteractedDisplayString:f,cache:h,thoughts:b}=await this.wrapElementTargetingCommand({target:g,cache:e.cache?.target??g.a11yData,action:T=>this.browser.typeIntoTarget(e.value,T,{force:e.force,clearContent:e.clearContent,pressKeysSequentially:e.pressKeysSequentially}),options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}});m=b,p=f,h&&(e.cache={target:h})}else await this.browser.type(e.value,{force:e.force,clearContent:e.clearContent,pressKeysSequentially:e.pressKeysSequentially});e.pressEnter&&await this.browser.press("Enter");let w={urlAfterCommand:await this.browser.url(),succeedImmediately:!1,elementInteracted:p,thoughts:m};return Ct(u,w.urlAfterCommand)&&(w.succeedImmediately=!0,w.succeedImmediatelyReason="URL changed"),w}case"HOVER":{if(at(e.target)){await this.browser.hoverUsingVisualCoordinates(e.target.percentXYLocation);break}let{cache:u,thoughts:p,elementInteractedDisplayString:m}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target??e.target.a11yData,action:g=>this.browser.hover(g,e.force),options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}});return u&&(e.cache={target:u}),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:m,thoughts:p}}case"FOCUS":{if(!Ge(e.target))throw new Error("Focus with x/y is not supported yet");let{elementInteractedDisplayString:u,cache:p,thoughts:m}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target??e.target.a11yData,action:g=>this.browser.focus(g),options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}});return p&&(e.cache={target:p}),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:u,thoughts:m}}case"BLUR":{if(!Ge(e.target))throw new Error("Blur with x/y is not supported yet");let{cache:u,thoughts:p,elementInteractedDisplayString:m}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target??e.target.a11yData,action:g=>this.browser.blur(g),options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}});return u&&(e.cache={target:u}),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),elementInteracted:m,thoughts:p}}case"PRESS":let c=await this.browser.url();await this.browser.press(e.value);let l={urlAfterCommand:await this.browser.url(),succeedImmediately:!1};return Ct(c,l.urlAfterCommand)&&(l.succeedImmediately=!0,l.succeedImmediatelyReason="URL changed"),l;case"REQUEST":{let u=e.timeout??30,p=null,m=new AbortController,g=Object.fromEntries(Object.entries(e.headers||{}).filter(([v,_])=>v&&_)),w=new URLSearchParams;Object.entries(e.params||{}).filter(([v,_])=>v&&_).forEach(([v,_])=>{w.append(v,_)});let f;if(Vn(e.url)&&(f=e.url),qn(e.url,this.browser.baseURL)&&(f=new URL(e.url,this.browser.baseURL).toString()),!f)throw new x("ActionFailureError",`Invalid URL: ${e.url}`);let h=async()=>{try{p=await fetch(`${f}?${w.toString()}`,{headers:g,method:e.method,body:e.body,signal:m.signal})}catch(v){this.logger.error({err:v},"Failed to make HTTP request")}},b=async()=>{await new Promise(v=>setTimeout(v,u*1e3)),m.abort()};await Promise.race([b(),h()]);let T=p;if(!T)throw new x("ActionFailureError",`Fetch request timed out after ${u} seconds`);if(!T.ok)throw new x("ActionFailureError",`Fetch request failed with status ${T.status}`);let S={};T.headers.forEach((v,_)=>{S[_]=v});let C={status:T.status,headers:S};return T.headers.get("content-type")?.includes("json")?C.json=await T.json():T.headers.get("content-type")?.includes("text")&&(C.text=await T.text()),{succeedImmediately:!1,urlAfterCommand:await this.browser.url(),data:C}}case"VISUAL_DIFF":{if(e.target&&!Ge(e.target))throw new Error("Visual Diff with x/y is not supported yet");if(!e.screenshot)throw new x("ActionFailureError","Cannot execute visual diff without saved screenshot");await this.getBrowserState({skipWait:!1});let u={clearHighlights:!0,scale:"css",hideCaret:!0},p;if(!e.target)p=await this.screenshotWithDimensions(u);else{let K=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target??e.target?.a11yData,action:async G=>{},options:{disableCache:o,filterByViewport:e.filterByViewport,useSelector:e.useSelector,iframeUrl:e.iframeUrl}});if(!K.cache)throw new x("ActionFailureError",`Could not find target given description: ${e.target.elementDescriptor}`);p=await this.screenshotWithDimensions({target:K.cache,...u})}let m;if(e.screenshot.data.startsWith("https://")){let K=await fetch(e.screenshot.data);m=Buffer.from(await K.arrayBuffer())}else m=Buffer.from(e.screenshot.data,"base64");if(Math.abs(p.height-e.screenshot.height)>10||Math.abs(p.width-e.screenshot.width)>10){let K=`${p.width}x${p.height}`,G=`${e.screenshot.width}x${e.screenshot.height}`;return{fail:!0,thoughts:`Current screenshot (${K}) does not match saved screenshot dimensions (${G}) - did you change the size of the target or the viewport?`,beforeScreenshotOverride:m,afterScreenshotOverride:p.buffer,succeedImmediately:!1,urlAfterCommand:await this.browser.url()}}let g=is(p.buffer),w={width:p.width,height:p.height},f=is(m),h={width:e.screenshot.width,height:e.screenshot.height},b,T=w.width*w.height,S=h.width*h.height,C=Math.abs(w.height-h.height),v=Math.abs(w.width-h.width);T>S?(p.buffer=await g.resize(h.width,h.height,{fit:"cover"}).toBuffer(),b="current",p.width=h.width,p.height=h.height):S>T&&(m=await f.resize(w.width,w.height,{fit:"cover"}).toBuffer(),b="saved");let _={data:Buffer.alloc(p.width*p.height*4),width:p.width,height:p.height},L=Ah(ss.decode(m).data,ss.decode(p.buffer).data,_.data,p.width,p.height,{threshold:e.threshold,diffColorAlt:[0,255,0]})/(p.width*p.height)*100,A=L>e.threshold*100,P=`Visual diff of ${L.toFixed(2)}% detected, which is ${A?"over":"under"} the threshold of ${e.threshold*100}%.`;return b&&(P+=` The ${b} screenshot was cropped since it was taller by ${C} pixels and wider by ${v} pixels.`),{fail:A,thoughts:P,beforeScreenshotOverride:p.buffer,afterScreenshotOverride:ss.encode(_,75).data,succeedImmediately:!1,urlAfterCommand:await this.browser.url()}}case"FILE_UPLOAD":{let u=await jl({url:e.fileSource.url,logger:this.logger});await this.browser.setFileChooserHandler(u);break}default:return(u=>{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,r,o){return(await this.generator.getReverseMappedDescription({browserState:e.browserState,goal:`${r}`},o)).phrase}stopRecordMode(){this.recordAbortController?.abort()}async startRecordMode(e){this.recordAbortController=new AbortController;let r=new zt({signal:this.recordAbortController.signal,...e});await this.browser.startRecording(this.recordAbortController.signal,r)}};var _h=2;async function Yl({socket:t,logger:e,storage:r,devicePixelRatio:o,generator:n}){let s=t.id,i=t.handshake.query.testId,a=t.handshake.query.baseUrl,c=t.handshake.query.viewport?JSON.parse(decodeURIComponent(t.handshake.query.viewport)):void 0,l=t.handshake.query.testMetadata?J.parse(JSON.parse(decodeURIComponent(t.handshake.query.testMetadata))):void 0;if(!i||!a)throw new Error("Socket connection request is missing testId or baseUrl");let d=await r.getOrgId(i),u=t.handshake.query.environmentVariables?JSON.parse(decodeURIComponent(t.handshake.query.environmentVariables)):{},p=t.handshake.headers["x-forwarded-for"]?.split(",")[0],m=e.child({testId:i,baseUrl:a,orgId:d,sessionId:s});if(m.info({clientIp:p,event:"connect",args:t.handshake.query,sessionId:s},"Websocket event (connect)"),p&&O.getCurrentConnectionsByIp(p)>=_h)throw e.error({clientIp:p,sessions:O.getCurrentSessionsByIp(),...t.handshake.query},"Socket connection browser creation rate limit triggered"),new Error("You have exceeded the maximum number of connections allowed per client. Momentic limits the number of simultaneously open tabs to uphold browser reliability. Please close another tab and retry or contact Momentic Support if you believe this is in error.");O.reserveCapacityByIp(p);try{await Oh({socket:t,baseUrl:a,viewport:c,testMetadata:l,orgId:d,sessionId:s,logger:m,environmentVariables:u,clientIp:p,devicePixelRatio:o,storage:r,generator:n})}catch(g){throw m.debug({err:g},"Error setting up socket session"),O.releaseCapacityByIp(p),g}return{sessionId:s,testId:i,orgId:d}}async function Oh({socket:t,baseUrl:e,viewport:r,devicePixelRatio:o,testMetadata:n,orgId:s,sessionId:i,logger:a,storage:c,generator:l,environmentVariables:d,clientIp:u}){let p=C=>{t.emit("browserState",{...C,url:e})},m={};r&&(m.viewport=r),o&&(m.deviceScaleFactor=o);let g=ir.parse(n?.advanced??{}),[w,f]=await Promise.all([j.init({baseUrl:e,networkSettings:g,waitForLoad:!1,enricher:new So(a),timeout:g.pageLoadTimeoutMs,storage:c,logger:a,onBrowserUpdateDuringLoad:p,contextArgs:m}),Jt.init(s)]),h=new Qt({browser:w,generator:l,config:_r,logger:a,flagStore:f,orgId:s,storage:c}),b=Zi(t,i,a),T=async()=>{clearInterval(b.timer)},S=new Se({baseUrl:e,currentUrl:await h.browser.url(),variablesFromEnvironment:d});if(!t.connected)throw await w.cleanup(),new Error("Socket not connected anymore, not proceeding with session setup");t.emit("session",{url:e,userAgent:j.USER_AGENT,viewport:await h.browser.getViewport(),sessionId:i}),O.registerSession({controller:h,context:S,sessionId:i,cleanup:T,clientIp:u})}var Xl=[Ta,Nl,zl,Gl,Ul,$l,ea,Bl,Fl,pa,ha,ma,fa,ga,ya,wa,ra,ta,ba,oa];var Jl=t=>{let{logger:e}=t,r=new Ih(t.baseServer,{cors:{origin:"*",methods:["GET","POST"]},pingTimeout:3e4,pingInterval:6e4,maxHttpBufferSize:1e7});return r.on("connection",async o=>{let n;try{n=await Yl({...t,socket:o})}catch(s){e.error({event:"connection",type:"websocket",err:s},"Failed to setup connection"),o.emit("error",{message:s instanceof Error?s.message:`${s}`}),o.disconnect(!0);return}Xl.forEach(s=>xh(s,{socket:o,metadata:n,...t}))}),r};var xh=(t,e)=>{let r=t.createHandler(e),o=(...n)=>{["cloudMouseMove","cloudScroll"].includes(t.event)||e.logger.debug({...e.metadata,event:t.event,args:n},`Websocket event (${t.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 instanceof Error?i.message:`${i}`})};try{let i=r.apply(void 0,n);i&&typeof i.catch=="function"&&i.catch(s)}catch(i){s(i)}};e.socket.on(t.event,o)};import{z as Lh}from"zod";import Ph from"fetch-retry";var kh=Ph(global.fetch),ne=class{static API_VERSION="v1";baseURL;apiKey;constructor(e){this.baseURL=e.baseURL,this.apiKey=e.apiKey}async sendRequest(e,r){let o=await kh(`${this.baseURL}${e}`,{retries:1,retryDelay:1e3,method:"POST",body:JSON.stringify(r),headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}});if(!o.ok)throw new Error(`Request to ${e} failed with status ${o.status}: ${await o.text()}`);return o.json()}};var _t=class extends ne{constructor(e){super(e)}async getRecommendedChunks(e){let r=await this.sendRequest(`/${ne.API_VERSION}/web-agent/recommend-chunks`,e);return Ai.parse(r)}async getScreenshotFromS3(e){let r=await this.sendRequest(`/${ne.API_VERSION}/s3/visual-diff-screenshot`,{url:e});return Lh.string().parse(r)}async getElementLocation(e,r){let o=await this.sendRequest(`/${ne.API_VERSION}/web-agent/locate-element`,{...e,disableCache:r});return ui.parse(o)}async getAssertionResult(e,r){let o={...e,disableCache:r},n=await this.sendRequest(`/${ne.API_VERSION}/web-agent/assertion`,o);return li.parse(n)}async getProposedCommand(e,r){let o=await this.sendRequest(`/${ne.API_VERSION}/web-agent/next-command`,{url:e.url,browserState:e.browserState,goal:e.goal,history:e.history,numPrevious:e.numPrevious,lastCommand:e.lastCommand,screenshot:e.screenshot,disableCache:r});return ci.parse(o)}async getGranularGoals(e,r){let o=await this.sendRequest(`/${ne.API_VERSION}/web-agent/split-goal`,{url:e.url,goal:e.goal,disableCache:r});return di.parse(o)}async getReverseMappedDescription(e,r){let o=await this.sendRequest(`/${ne.API_VERSION}/web-agent/reverse-mapped-description`,{goal:e.goal,browserState:e.browserState,disableCache:r});return pi.parse(o)}async getTextExtraction(e,r){let o={goal:e.goal,browserState:e.browserState,returnSchema:e.returnSchema,disableCache:r},n=await this.sendRequest(`/${ne.API_VERSION}/web-agent/text-extraction`,o);return Vo.parse(n)}async getTestResultClassification(e){let r=await this.sendRequest(`/${ne.API_VERSION}/web-agent/result-classification`,e);return jo.parse(r)}};import{Router as Mh}from"express";function ge(t){return function(...e){let r=e[e.length-1],o=t(...e);Promise.resolve(o).catch(r)}}var Ql=Mh();Ql.get("/",ge((t,e)=>{let r=Ji();e.status(200).json(r)}));var Zl=Ql;import{Router as Uh}from"express";import eu,{multistream as Dh}from"pino";import Nh from"pino-pretty";var as=new Map,Fh=process.env.NODE_ENV==="production",cs=class t{consoleLogger;ddClientToken;hostname;bindingAttributes;excludeAttributesInConsoleLogs;excludeTimestamps;disableConsoleLogs;site="https://http-intake.logs.us5.datadoghq.com/api/v2/logs";constructor({bindings:e,clientToken:r,hostname:o,excludeAttributesInConsoleLogs:n,excludeTimestamps:s,disableConsoleLogs:i}){this.ddClientToken=r,this.hostname=o,this.excludeAttributesInConsoleLogs=n,this.excludeTimestamps=s,this.disableConsoleLogs=i,this.bindingAttributes={...e,env:process.env.NODE_ENV};let a={base:n?{}:this.bindingAttributes,errorKey:"err",level:"debug",timestamp:!s};this.consoleLogger=Fh?eu(a):eu(a,Dh([{stream:Nh({colorize:!0})}]))}child(e){return new t({clientToken:this.ddClientToken,bindings:{...this.bindingAttributes,...e},hostname:this.hostname,excludeAttributesInConsoleLogs:this.excludeAttributesInConsoleLogs,disableConsoleLogs:this.disableConsoleLogs})}flush(e){this.disableConsoleLogs||this.consoleLogger.flush(e)}log(e,r,o,...n){if(r&&o===void 0&&(o=`${r}`,r={}),this.disableConsoleLogs||this.consoleLogger[e](e==="error"?r:{testId:r.testId,runId:r.runId},o,...n),typeof r=="object"&&r&&"err"in r&&r.err instanceof Error){let i={};for(let a of Object.getOwnPropertyNames(r.err))i[a]=r.err[a];i.name=r.err.name,r.err=i}let s=Object.assign({},this.bindingAttributes,r&&typeof r=="object"?r:{});n.length>0&&(s.args=n),(async()=>{try{let i=await fetch(this.site,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json","DD-API-KEY":this.ddClientToken},body:JSON.stringify([{ddsource:this.bindingAttributes.app,hostname:this.hostname??"vercel",service:"momentic",message:{message:o||"",...s,level:e}}])});if(!i.ok)throw new Error(`Failed to log to Datadog: ${i.statusText})}`)}catch(i){this.disableConsoleLogs||this.consoleLogger.warn({obj:r,msg:o,args:n,err:i},"Failed to log to Datadog")}})()}debug(e,r,...o){this.log("debug",e,r,...o)}info(e,r,...o){this.log("info",e,r,...o)}warn(e,r,...o){this.log("warn",e,r,...o)}error(e,r,...o){this.log("error",e,r,...o)}bindings(){return this.bindingAttributes}setMinLevel(e){this.consoleLogger.level=e}enableConsoleLogs(){this.disableConsoleLogs=!1}},Ao=({app:t,clientToken:e,hostname:r,excludeAttributesInConsoleLogs:o,excludeTimestamps:n,disableConsoleLogs:s})=>{if(!process.env.DD_CLIENT_TOKEN&&!process.env.NEXT_PUBLIC_DD_CLIENT_TOKEN&&!e)throw new Error("Missing DD_CLIENT_TOKEN");return as.has(t)||as.set(t,new cs({bindings:{app:t},hostname:r,clientToken:e||process.env.NEXT_PUBLIC_DD_CLIENT_TOKEN||process.env.DD_CLIENT_TOKEN,excludeAttributesInConsoleLogs:o,excludeTimestamps:n,disableConsoleLogs:s})),as.get(t)};import{hostname as Gh}from"os";var Te=Ao({app:"desktop-server",clientToken:"pubcfd7516a5c0ba852b42675cd97bee027",hostname:Gh(),excludeAttributesInConsoleLogs:!0,excludeTimestamps:!0,disableConsoleLogs:!0});var Or=Uh();Or.get("/",ge(async(t,e)=>{let r=await Hi(Te);e.status(200).json(r)}));Or.get("/metadata",ge((t,e)=>{let r=Bi();e.status(200).json(r)}));Or.post("/",ge(async(t,e)=>{let r;try{r=si.parse(t.body)}catch(n){e.status(400).json({error:`Invalid request body: ${n}`});return}try{ar(r.name)}catch(n){e.status(400).json({error:`Invalid module name: ${n}`});return}let o=$i(r.name,r.steps);e.status(201).json(o)}));Or.get("/:moduleId",ge(async(t,e)=>{if(!t.params.moduleId){e.status(400).json({error:"Missing moduleId in url path."});return}let r=await Wi(t.params.moduleId,Te);e.json(r)}));var tu=Or;import{Router as zh}from"express";import{existsSync as $h}from"fs";import{join as Oo}from"path";import{v4 as Bh}from"uuid";var us="https://api.momentic.ai",ru=t=>{us=t},Ir=()=>us,Ro,ls,ou=async t=>{if(Ro)return;let e=new oe({baseURL:us,apiKey:t});try{Ro=await e.getOrgId(),ls=t}catch(r){throw new Error(`Error checking API key against server: ${r}`)}},_o=()=>{if(!Ro)throw new Error("Your organization ID appears invalid. Please contact Momentic Support.");return Ro},nu=()=>{if(!ls)throw new Error("Your API key appears invalid. Please contact Momentic Support.");return ls};var Ot=zh();Ot.get("/",ge((t,e)=>{let r=gr(Z,Te);e.status(200).json(r)}));Ot.post("/",ge((t,e)=>{let r;try{r=ni.parse(t.body)}catch(a){e.status(400).json({error:`Invalid request body: ${a}`});return}try{ar(r.name)}catch(a){e.status(400).json({error:a.message});return}let n={id:Bh(),name:r.name,baseUrl:r.baseUrl,schemaVersion:X,advanced:{disableAICaching:!1,viewport:r.viewport??ut},retries:0,steps:[]};r.environment&&(n.envs=[{name:r.environment,default:!0}]);let s=Ki(n,r.name),i={...n,testPath:s};e.status(201).json(i)}));Ot.get("/:testPath",ge(async(t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let r=Oo(Z,t.params.testPath);if(!$h(r)){e.status(400).json({error:`Test not found at path: ${r}`});return}let o=await no(r,Tt,Te);e.status(200).json(o)}));Ot.patch("/:testPath/metadata",ge(async(t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let r;try{r=oi.parse(t.body)}catch(n){e.status(400).json({error:`Invalid request body: ${n}`});return}let o=Oo(Z,t.params.testPath);mr(r,o),e.status(201).json({message:"ok"})}));Ot.patch("/:testPath",ge(async(t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let r;try{r=ri.parse(t.body)}catch(l){e.status(400).json({error:`Invalid request body: ${l}`});return}let o=Oo(Z,t.params.testPath),n;try{n=Yi(o)}catch(l){e.status(400).json({error:`Existing test file on disk is invalid: ${l}`});return}let{stepsToSave:s,moduleUpdates:i,cachesToSave:a}=de({steps:r.steps,testId:n.id,orgId:_o()});i.forEach(l=>zi(l)),mr({steps:s},o),await new oe({apiKey:nu(),baseURL:Ir()}).updateStepCaches({entries:a,testId:n.id}),e.status(201).json({message:"ok"})}));Ot.patch("/:testPath/environments",ge(async(t,e)=>{if(!t.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let r;try{r=ii.parse(t.body)}catch(n){e.status(400).json({error:`Invalid request body: ${n}`});return}let o=Oo(Z,t.params.testPath);mr({envs:r.defaultEnv?[{name:r.defaultEnv,default:!0}]:[]},o),e.status(201).json({message:"ok"})}));var su=Ot;async function iu({momenticServerUrl:t,apiKey:e,serverPort:r,appPort:o,staticDir:n,devicePixelRatio:s}){if(!jh(Z)||!Vh(Z).isDirectory()){let m=Yh.isAbsolute(Z);throw new Error(`Root folder ${Z} does not exist${m?"":` in the current directory ("${process.cwd()}")`}. Please run \`npx momentic init\` if you wish to setup Momentic here for the first time.`)}t&&ru(t),Te.info("Checking API key"),await ou(e);let a=`http://localhost:${r}`;o&&(a=`http://localhost:${o}`);let c=Xh(n);await new Promise(m=>{c.listen(r,()=>{Te.info(`Server is running at http://localhost:${r}`),m()})});let d=new _t({baseURL:Ir(),apiKey:e}),u=new oe({baseURL:Ir(),apiKey:e}),p=new Ut(u,_o());Jl({baseServer:c,generator:d,storage:p,logger:Te,devicePixelRatio:s}),await Kh(a)}function Xh(t){let e=ds();e.use(Wh()),e.use(Hh.json({limit:"50mb"}));let r=ds.Router();if(r.use("/tests",su),r.use("/modules",tu),r.use("/environments",Zl),e.use("/api",r),e.use((n,s,i)=>{n.path!=="/healthcheck"&&Te.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&&Te.error({url:n.url,method:n.method,statusCode:s.statusCode},"Request completed in error")}),i()}),e.use((n,s,i,a)=>{n instanceof Error&&n.message.includes("BadRequestError: request aborted")||(Te.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=ds.static(t);e.use("/",n),e.use("*",n)}return qh.createServer(e)}import{existsSync as qf}from"fs";import Fu,{dirname as Kf,join as Yf}from"path";import{chdir as Xf}from"process";import{fileURLToPath as Jf}from"url";var au="0.0.92";import xr from"chalk";var It=(...t)=>{Ie(xr.blue(...t))};var Pr=(...t)=>{Ie(xr.dim(...t))};var se=(...t)=>{Ie(xr.green(...t))},Ie=(...t)=>{console.log(...t)},le=(...t)=>{console.error(xr.yellow(...t))},F=(...t)=>{console.error(xr.red(...t))};import{hostname as Qh}from"os";var I=Ao({app:"cli",clientToken:"pub7eb923f18fb3f1d42ac5eba8c5ea13a5",hostname:Qh(),excludeAttributesInConsoleLogs:!0,excludeTimestamps:!0,disableConsoleLogs:!0});import{execSync as Zh}from"child_process";import{platform as ef}from"os";function ps(){return cu()?(It("Setting device pixel ratio to 2 automatically since a Mac OS Retina screen was detected."),It("If you are using a low pixel-density monitor, you should manually set --pixel-ratio to 1 to avoid incorrect viewport calculations."),It("Confirm your device's pixel-ratio at https://www.mydevice.io."),2):(It("Setting device pixel ratio to 1."),It("If you are using Momentic on a high-pixel density (HiDPI) monitor, relaunch with the --pixel-ratio option to avoid incorrect viewport calculations"),It("Confirm your device's pixel-ratio at https://www.mydevice.io."),1)}function cu(){return ef()==="darwin"&&Zh("system_profiler SPDisplaysDataType").toString().includes("Retina")}function ms(t){cu()&&t===1&&(le("If you are using Momentic on a Retina screen, relaunch with the --pixel-ratio option to avoid incorrect viewport calculations."),le("Confirm your device's pixel-ratio at https://www.mydevice.io."))}import{createHash as pu}from"crypto";import{existsSync as mu,readFileSync as bs,writeFileSync as gu}from"fs";import{join as hu}from"path";import{parse as mf}from"yaml";import{existsSync as xo,mkdirSync as lu,readdirSync as sf,statSync as fs}from"fs";import{homedir as uu}from"os";import{join as We,resolve as af}from"path";import{registry as gs}from"playwright-core/lib/server";function tf(t){let e=[],r=[];for(let o of t){let n=gs.findExecutable(o);!n||n.installType==="none"?e.push(o):r.push(n)}return r}async function Io(){let t=tf(["chromium"]);await gs.installDeps(t,!1),await gs.install(t,!1)}import{statSync as rf}from"fs";import of from"readline/promises";var hs=!1,nf=(()=>{try{return rf("/.dockerenv"),!0}catch{return!1}})();async function Ae(t,e){if(process.env.CI||hs||!process.stdout.isTTY||nf)return!0;I.flush(),await new Promise(i=>setTimeout(i,500));let r=of.createInterface({input:process.stdin,output:process.stdout}),o=t.split("."),n;if(o.length===1)n=t;else{let i=`${o.slice(0,o.length-1).join(". ").trim()}.`;e?le(i):Ie(i),n=o[o.length-1].trim()}let s=await r.question(`${n} ('y' for yes / n for no / 'A' to accept all) `);return r.close(),s==="A"?(hs=!0,setTimeout(()=>{hs=!1},3e3),!0):s.toLowerCase()==="y"}var cf=[Be,Kr,Ft],je=pf(),xt=We(je,Be),ws=We(je,Kr),Ve=We(je,Ft),lf=!!process.env.CI||!process.stdout.isTTY,ys=We(uu(),"momentic",rn),du=[xt,ws,Ve];function kr(t){try{return xo(t)&&fs(t).isDirectory()}catch(e){return I.error({err:e},`Error reading path ${t} during directory exists check`),!1}}async function rt(t){let e=process.versions.node,r=parseInt(e.split(".")[0]);(isNaN(r)||r<20)&&(F(`Node.js version 20 or higher is required to run the Momentic CLI. Detected: ${process.versions.node}.`),process.exit(1));let o=await t.client.getOrgId();return await Io(),await df(t),o}async function uf(t){kr(je)||!t&&!await Ae(`The root '${Xe}' folder was not found in the current directory. You will need to invoke the Momentic CLI from this directory in the future. Proceed?`)&&(F("Setup cancelled."),process.exit(1)),kr(ys)||(!t&&!await Ae("The Chrome cache folder was not found in your home directory. Proceed with creation?")&&(F("Setup cancelled."),process.exit(1)),lu(ys,{recursive:!0}));for(let e of du)kr(e)||(!t&&!await Ae(`'${e}' folder was not found in the current directory. Create it now?`)&&(F("Setup cancelled."),process.exit(1)),lu(e,{recursive:!0}))}function Lr(t,e,r=new Set){for(let o of t){let n=af(o),s=!1;try{s=xo(n)&&fs(n).isDirectory()}catch(i){I.error({err:i},`Error reading path ${n} during collect paths`)}if(n&&s){let i=sf(n).map(a=>We(n,a));Lr(i,e,r);continue}if(n.endsWith(".yaml")){try{if(!xo(n)||!fs(n).isFile()){I.error(`File not found or unreadable: ${n}`);continue}}catch(i){I.error({err:i},`Error reading file ${n} during collect paths`);continue}if(!e(n))continue;r.add(n)}}return r}async function df({client:t,createFolders:e,skipPrompts:r,pullEnvs:o}){if(uu()||(F("Your home directory could not be found. Please ensure the HOME environment variable is set for POSIX systems, and the USERPROFILE environment variable is set for Windows systems."),process.exit(1)),e)await uf(r);else for(let n of[je,ys,...du])!kr(n)&&!lf&&(F(`The '${n}' folder is required to run the Momentic CLI but was not found in the current directory. Please run 'npx momentic@latest init' to create the required folders.`),F(`The current working directory is: ${process.cwd()}`),process.exit(1));o&&(!r&&!await Ae("Pull environments from Momentic Cloud? Environment definitions may be required to run tests locally.")&&(F("Setup cancelled."),process.exit(1)),await Po({client:t,all:!0,skipPrompts:r}))}function pf(){let t=o=>xo(We(o,Xe))&&cf.some(n=>kr(We(o,Xe,n))),e=process.cwd();for(;e!=="/"&&!t(e);)e=We(e,"..");let r;return t(e)?r=e:r=process.cwd(),r!==process.cwd()?le(`Current working directory does not contain a ${Xe} folder, treating ${r} as the Momentic root folder instead`):I.info(`Identified ${r} as the Momentic root folder`),We(r,Xe)}async function Po({envNames:t,client:e,all:r,skipPrompts:o}){let n=await e.getAllEnvironments();t&&!r&&(n=n.filter(a=>t?.includes(a.name))),mu(Ve)||(F(`${Ve} does not exist in the current directory. Please run 'npx momentic@latest init' first.`),process.exit(1));let s=[],i=[];for(let a of n){let c=hu(Ve,`${a.name}.yaml`);if(!mu(c))gu(c,ln(a)),s.push(c);else{let l=pu("sha256").update(bs(c)).digest("hex"),d=ln(a),u=pu("sha256").update(d).digest("hex");if(l!==u){if(!o&&!await Ae(`Environment ${a.name} already exists on disk but needs to be updated. Overwrite?`))return;gu(c,d),i.push(c)}}}se("Pulled environments successfully!"),s.length&&(se("Created:"),s.forEach(a=>se(" ",a))),i.length&&(se("Updated:"),i.forEach(a=>se(" ",a)))}function gf(t){return t.endsWith(".yaml")?bs(t,"utf8").includes("momentic/environment")?!0:(le(`Skipping YAML that is not a Momentic environment: ${t}`),!1):!1}async function fu({client:t,names:e,all:r,yes:o}){let n;r?n=[Ve]:n=e.map(a=>hu(Ve,`${a.toLowerCase()}.yaml`));let s=Lr(n,gf);Ie(`Found ${s.size} environments to push:`),s.forEach(a=>Ie(" ",a));let i=[];for(let a of s){let c=mf(bs(a,"utf-8"));try{let l=we.parse(c);i.push(l)}catch(l){F(`${a} failed to parse as a valid environment file.`),F(l),process.exit(1)}}!o&&!await Ae("Pushing environments can overwrite variables on cloud and instantly affect scheduled runs. Continue?",!0)||(await t.updateEnvironments(i),se("Pushed environments successfully!"))}import{Argument as ko,Option as Ne}from"commander";import{cpus as yu}from"os";import{z as hf}from"zod";var ot=new Ne("--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),nt=new Ne("--server <server>","Momentic server to use. Leave unchanged unless using Momentic on-premise.").default("https://api.momentic.ai"),st=new Ne("-y, --yes","Skip all confirmation prompts.").env("CI"),wu=new Ne("-w, --wait","Wait for tests to finish running before exiting. Only applicable when running tests remotely").implies({remote:!0}),Ts=new Ne("--no-report","Skip reporting test results to Momentic Cloud when running with the --local flag.").implies({local:!0}),Ss=new Ne("--pixel-ratio <number>","Device pixel ratio of your screen or monitor. Mac OS Retina displays and machines marketed as 'HiDPI' should set this to 2. Visit https://www.mydevice.io/ to find your device's pixel ratio.").argParser(t=>parseInt(t,10)),bu=new Ne("--env <env>","Name of the environment to use when running tests."),Tu=new Ne("--url-override <urlOverride>","Fully qualified url (e.g. https://www.google.com) to start all tests from. Overrides any default starting url set from the test or environment."),Es=new Ne("-a, --all","Select all tests in your organization from Momentic Cloud. Cannot be used together with <tests> arguments."),vs=new Ne("-a, --all","Select all environments in your organization from Momentic Cloud. Cannot be used together with <paths> arguments."),Su=new ko("<tests...>",`One or more test paths to pull from Momentic Cloud.
1347
1347
 
1348
1348
  A test path is a lowercased version of your test name where spaces are replaced with underscores: 'npx momentic pull hello-world'.`).argOptional(),Eu=new ko("<tests...>",`One or more test identifiers.
1349
1349
 
@@ -1355,4 +1355,4 @@ To use tests stored remotely on Momentic Cloud, pass '--remote' and specify one
1355
1355
  ${t.map(R=>` - ${R}`).join(`
1356
1356
  `)}`),t.forEach(R=>{if(!Mu(R))throw new Error(`Path '${R}' does not exist.`);let L,A;try{L=zf(R),A=L.isDirectory()}catch(P){I.error({err:P},`Skipping path ${R} because it cannot be read`);return}if(A)gr(R,I).map(P=>P.fullFilePath).forEach(P=>w.add(P));else if(R.endsWith(".yaml"))w.add(R);else throw new Error(`Path '${R}' is not a directory or a .yaml file.`)});else if(I.warn("The paths you specified are not files or directories that exist locally."),I.warn(`Fetching tests from Momentic Cloud (${n.baseURL}) instead...`),s)for(let R of await n.getAllTestIds())w.add(R);else w=new Set(t);let f=Array.from(w);I.info({testsToRun:f},`Running ${f.length} tests using local machine as worker:
1357
1357
  ${f.map(R=>` - ${R}`).join(`
1358
- `)}`),Pr(`Running ${f.length} tests with ${p} workers.`);let h=new Uf.SingleBar({format:"{testPath} | {value}/{total}",noTTYOutput:!0});h.start(f.length,0);let b=[],T=Date.now();for(let R=0;R<f.length;R+=p){let L=await Promise.all(f.slice(R,R+p).map(async A=>{h.update(b.length,{testPath:A});try{let P=await Lu({path:A,orgId:d,useLocalFiles:g,devicePixelRatio:u,apiClient:n,generator:m,retriesOverride:a,urlOverride:c,envName:l,noReport:!i});return{identifier:A,...P}}catch(P){return{identifier:A,status:"FAILED",failureDetails:{errorMessage:`A fatal error occurred while setting up the test '${A}': ${P.message}`,errorStack:P.stack}}}}));b.push(...L)}h.stop();let S=n.baseURL.replace(/\/\/api/,"//app"),C=b.filter(R=>R.status==="PASSED"),v=b.filter(R=>R.status==="FAILED"),_=b.filter(R=>R.status==="CANCELLED");v.length&&(F(`${v.length} failed:`),v.forEach(R=>{let L=` ${R.identifier}`;i&&(L+=`: ${S}/runs/${R.runId}`),F(L)})),_.length&&(le(`${v.length} cancelled:`),v.forEach(R=>{let L=` ${R.identifier}`;i&&(L+=`: ${S}/runs/${R.runId}`),le(L)})),se(`${C.length} passed. (${Math.floor(Date.now()-T)/1e3}s)`),process.exit(0)}import Bf from"cli-progress";var Hf=30*6e4;async function Nu({tests:t,client:e,...r}){let{queuedTests:o,runIds:n}=await e.queueTests({testPaths:t,...r});if(Pr(`Queued ${o.length} tests.`),r.wait){Pr(`Waiting for ${o.length} tests to complete.`);let s=e.baseURL.replace(/\/\/api/,"//app"),i=Date.now(),a=await Wf(e,n),c=a.filter(u=>u.status==="PASSED"),l=a.filter(u=>u.status==="FAILED"),d=a.filter(u=>u.status==="CANCELLED");l.length&&(F(`${l.length} failed:`),l.forEach(u=>{F(` ${u.testName||u.test?.name||"Unknown test"}: ${s}/runs/${u.id}`)})),d.length&&(le(`${d.length} cancelled:`),d.forEach(u=>{le(` ${u.testName||u.test?.name||"Unknown test"}: ${s}/runs/${u.id}`)})),se(`${c.length} passed. (${Math.floor(Date.now()-i)/1e3}s)`)}}async function Wf(t,e){if(!e.length)return[];let r=Date.now(),o=new Bf.SingleBar({format:"{testName} | {value}/{total}",noTTYOutput:!0});o.start(e.length,0);let n=0,s="";for(;Date.now()-r<Hf;){let i;n>3&&(F("Failed to fetch run status too many times."),process.exit(1));try{i=await t.bulkGetRunStatus(e),n=0}catch{n++,await new Promise(d=>setTimeout(d,5e3));continue}let a=i.filter(l=>l.status==="RUNNING");if(a.length&&(s=a[Math.floor(Math.random()*a.length)].testName||"Unknown test"),o.update(i.filter(l=>l.status!=="PENDING"&&l.status!=="RUNNING").length,{testName:s}),i.every(l=>l.status!=="PENDING"&&l.status!=="RUNNING"))return o.stop(),i;await new Promise(l=>setTimeout(l,5e3))}F("Timeout elapsed waiting for runs to complete."),process.exit(1)}var xe=new Vf;xe.name("momentic").description("Momentic CLI").version(au);xe.command("install-browsers").action(async()=>{await Io()});xe.addOption(new Fe("--log-level <level>").choices(["debug","info","warn","error"]).default("info")).on("option:log-level",t=>{I.setMinLevel(t.toLowerCase())});xe.addOption(new Fe("--verbose","Enable verbose logging.")).on("option:verbose",()=>{I.enableConsoleLogs()});xe.command("init").addOption(st).addOption(ot).addOption(nt).action(async t=>{let{yes:e,apiKey:r,server:o}=t,n=new oe({baseURL:o,apiKey:r,logger:I});await rt({client:n,skipPrompts:e,pullEnvs:!0,createFolders:!0})});xe.command("app").addOption(ot).addOption(nt).addOption(st).addOption(Ss).addOption(new Fe("--port <port>","Port to run the app on. Defaults to 58888.").default(58888)).action(async t=>{let{apiKey:e,port:r,yes:o,server:n,pixelRatio:s}=t,i=new oe({baseURL:n,apiKey:e,logger:I});await rt({client:i,skipPrompts:o,pullEnvs:!1,createFolders:!1}),Xf(Yf(je,".."));let a=Jf(import.meta.url),c=Kf(a),l=Fu.resolve(c,"..","static"),d=Fu.resolve(c,"..","assets"),u=s??ps();ms(u),await iu({momenticServerUrl:n,apiKey:e,serverPort:r,appPort:r,staticDir:l,assetsDir:d,devicePixelRatio:u})});xe.command("run").alias("run-tests").addOption(ot).addOption(nt).addOption(Es).addOption(st).addOption(bu).addOption(Tu).addOption(new Fe("-r, --remote","Run tests remotely. The production version of this test will be queued for execution.").default(!0).conflicts(["start","waitOn","waitOnTimeout","retries","parallel","no-report","pixel-ratio"])).addOption(wu).addOption(Ts).addOption(Ss).addOption(new Fe("-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).").conflicts(["wait"])).addOption(new Fe("--start <start>","Arbitrary setup command that will run before Momentic steps begin.")).addOption(new Fe("--wait-on <waitOn>","URL to wait to become accessible before Momentic tests begin.")).addOption(new Fe("--wait-on-timeout <waitOnTimeout>","Max time to wait for the --wait-on URL to become accessible.").default(60,"one minute")).addOption(new Fe("--retries <retries>","Number of retries to attempt when running tests locally. Defaults to each test's own retry configuration.")).addOption(new Fe("-p, --parallel <parallel>","When running with the --local flag, the number of tests to run in parallel. Defaults to 1.").default(1).argParser(t=>parseInt(t,10))).addOption(Ts).addArgument(Eu).action(async(t,e)=>{Cu(e);let{apiKey:r,server:o,remote:n,local:s,all:i,env:a,urlOverride:c,yes:l,parallel:d,report:u,start:p,waitOn:m,waitOnTimeout:g,retries:w,wait:f,pixelRatio:h}=e;!i&&(!t||!t.length)&&(F("You must pass at least one test path or use the --all flag."),process.exit(1));let b=new oe({baseURL:o,apiKey:r,logger:I});if(s){let T=await rt({client:b,skipPrompts:l,pullEnvs:!1,createFolders:!1}),S=h??ps();ms(S);try{await Du({tests:t,client:b,all:i,start:p,waitOn:m,waitOnTimeout:g,retriesOverride:w,parallel:d,report:u,devicePixelRatio:S,envName:a,orgId:T,urlOverride:c})}catch(C){F("Failed to run tests locally. Please check the error message below or run with the --verbose flag."),F(C),process.exit(1)}return}if(n){for(let T of t)(T.endsWith(".yaml")||qf(T))&&(F(`Looks like you tried to run a local test remotely: ${T}`),F("Use the --local flag or specify the test path without its file extension to queue tests remotely (e.g. 'hello-world')."),process.exit(1));await Nu({tests:t,client:b,all:i,env:a,urlOverride:c,wait:f});return}F("One of --remote or --local flag must be specified."),process.exit(1)});xe.command("pull").description("Fetch one or more tests from Momentic Cloud and save it in to local disk in YAML format.").addOption(ot).addOption(nt).addOption(Es).addOption(st).addArgument(Su).action(async(t,e)=>{let{apiKey:r,server:o,all:n,yes:s}=e,i=new oe({baseURL:o,apiKey:r,logger:I});await rt({client:i,skipPrompts:s,pullEnvs:!1,createFolders:!1}),!n&&!t?.length&&(F("At least one test name or the --all flag must be provided."),process.exit(1)),await xu({testsToFetch:t??[],client:i,all:n,yes:s})});xe.command("push").description("Save one or more tests in YAML format to Momentic Cloud.").addOption(st).addOption(ot).addOption(nt).addArgument(vu).action(async(t,e)=>{let{apiKey:r,server:o,yes:n}=e,s=new oe({baseURL:o,apiKey:r,logger:I});await rt({client:s,skipPrompts:n,pullEnvs:!1,createFolders:!1}),await Pu({paths:t,client:s,yes:n}),process.exit(0)});var Gu=xe.command("env").description("Perform operations on Momentic environments");Gu.command("push").description("Push one or more environments and associated variables to Momentic cloud.").addOption(st).addOption(ot).addOption(nt).addOption(vs).addArgument(Cs).action(async(t,e)=>{let{apiKey:r,server:o,yes:n,all:s}=e;!t?.length&&!s&&(F("At least one environment name or the --all flag must be provided."),process.exit(1));let i=new oe({baseURL:o,apiKey:r,logger:I});await rt({client:i,skipPrompts:n,pullEnvs:!1,createFolders:!1}),await fu({names:t,client:i,yes:n,all:s})});Gu.command("pull").description("Pull one or more environments and associated variables from Momentic cloud.").addOption(st).addOption(ot).addOption(nt).addOption(vs).addArgument(Cs).action(async(t,e)=>{let{apiKey:r,server:o,yes:n,all:s}=e;!t?.length&&!s&&(F("At least one environment name or the --all flag must be provided."),process.exit(1));let i=new oe({baseURL:o,apiKey:r,logger:I});await rt({client:i,skipPrompts:n,pullEnvs:!1,createFolders:!1}),await Po({envNames:t,client:i,skipPrompts:n,all:s})});async function Qf(){try{await xe.parseAsync(process.argv)}catch(t){let e={};try{e.playwrightVersion=jf("npx playwright --version").toString()}catch(r){I.error({err:r},"Error fetching debug information")}I.error({err:t,debugInfo:e},"Uncaught error in CLI"),F("Failed to start Momentic CLI."),F(t),process.exit(1)}}Qf();
1358
+ `)}`),Pr(`Running ${f.length} tests with ${p} workers.`);let h=new Uf.SingleBar({format:"{testPath} | {value}/{total}",noTTYOutput:!0});h.start(f.length,0);let b=[],T=Date.now();for(let R=0;R<f.length;R+=p){let L=await Promise.all(f.slice(R,R+p).map(async A=>{h.update(b.length,{testPath:A});try{let P=await Lu({path:A,orgId:d,useLocalFiles:g,devicePixelRatio:u,apiClient:n,generator:m,retriesOverride:a,urlOverride:c,envName:l,noReport:!i});return{identifier:A,...P}}catch(P){return{identifier:A,status:"FAILED",failureDetails:{errorMessage:`A fatal error occurred while setting up the test '${A}': ${P.message}`,errorStack:P.stack}}}}));b.push(...L)}h.stop();let S=n.baseURL.replace(/\/\/api/,"//app"),C=b.filter(R=>R.status==="PASSED"),v=b.filter(R=>R.status==="FAILED"),_=b.filter(R=>R.status==="CANCELLED");v.length&&(F(`${v.length} failed:`),v.forEach(R=>{let L=` ${R.identifier}`;i&&(L+=`: ${S}/runs/${R.runId}`),F(L)})),_.length&&(le(`${v.length} cancelled:`),v.forEach(R=>{let L=` ${R.identifier}`;i&&(L+=`: ${S}/runs/${R.runId}`),le(L)})),se(`${C.length} passed. (${Math.floor(Date.now()-T)/1e3}s)`),v.length>0?process.exit(1):process.exit(0)}import Bf from"cli-progress";var Hf=30*6e4;async function Nu({tests:t,client:e,...r}){let{queuedTests:o,runIds:n}=await e.queueTests({testPaths:t,...r});if(Pr(`Queued ${o.length} tests.`),r.wait){Pr(`Waiting for ${o.length} tests to complete.`);let s=e.baseURL.replace(/\/\/api/,"//app"),i=Date.now(),a=await Wf(e,n),c=a.filter(u=>u.status==="PASSED"),l=a.filter(u=>u.status==="FAILED"),d=a.filter(u=>u.status==="CANCELLED");l.length&&(F(`${l.length} failed:`),l.forEach(u=>{F(` ${u.testName||u.test?.name||"Unknown test"}: ${s}/runs/${u.id}`)})),d.length&&(le(`${d.length} cancelled:`),d.forEach(u=>{le(` ${u.testName||u.test?.name||"Unknown test"}: ${s}/runs/${u.id}`)})),se(`${c.length} passed. (${Math.floor(Date.now()-i)/1e3}s)`)}}async function Wf(t,e){if(!e.length)return[];let r=Date.now(),o=new Bf.SingleBar({format:"{testName} | {value}/{total}",noTTYOutput:!0});o.start(e.length,0);let n=0,s="";for(;Date.now()-r<Hf;){let i;n>3&&(F("Failed to fetch run status too many times."),process.exit(1));try{i=await t.bulkGetRunStatus(e),n=0}catch{n++,await new Promise(d=>setTimeout(d,5e3));continue}let a=i.filter(l=>l.status==="RUNNING");if(a.length&&(s=a[Math.floor(Math.random()*a.length)].testName||"Unknown test"),o.update(i.filter(l=>l.status!=="PENDING"&&l.status!=="RUNNING").length,{testName:s}),i.every(l=>l.status!=="PENDING"&&l.status!=="RUNNING"))return o.stop(),i;await new Promise(l=>setTimeout(l,5e3))}F("Timeout elapsed waiting for runs to complete."),process.exit(1)}var xe=new Vf;xe.name("momentic").description("Momentic CLI").version(au);xe.command("install-browsers").action(async()=>{await Io()});xe.addOption(new Fe("--log-level <level>").choices(["debug","info","warn","error"]).default("info")).on("option:log-level",t=>{I.setMinLevel(t.toLowerCase())});xe.addOption(new Fe("--verbose","Enable verbose logging.")).on("option:verbose",()=>{I.enableConsoleLogs()});xe.command("init").addOption(st).addOption(ot).addOption(nt).action(async t=>{let{yes:e,apiKey:r,server:o}=t,n=new oe({baseURL:o,apiKey:r,logger:I});await rt({client:n,skipPrompts:e,pullEnvs:!0,createFolders:!0})});xe.command("app").addOption(ot).addOption(nt).addOption(st).addOption(Ss).addOption(new Fe("--port <port>","Port to run the app on. Defaults to 58888.").default(58888)).action(async t=>{let{apiKey:e,port:r,yes:o,server:n,pixelRatio:s}=t,i=new oe({baseURL:n,apiKey:e,logger:I});await rt({client:i,skipPrompts:o,pullEnvs:!1,createFolders:!1}),Xf(Yf(je,".."));let a=Jf(import.meta.url),c=Kf(a),l=Fu.resolve(c,"..","static"),d=Fu.resolve(c,"..","assets"),u=s??ps();ms(u),await iu({momenticServerUrl:n,apiKey:e,serverPort:r,appPort:r,staticDir:l,assetsDir:d,devicePixelRatio:u})});xe.command("run").alias("run-tests").addOption(ot).addOption(nt).addOption(Es).addOption(st).addOption(bu).addOption(Tu).addOption(new Fe("-r, --remote","Run tests remotely. The production version of this test will be queued for execution.").default(!0).conflicts(["start","waitOn","waitOnTimeout","retries","parallel","no-report","pixel-ratio"])).addOption(wu).addOption(Ts).addOption(Ss).addOption(new Fe("-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).").conflicts(["wait"])).addOption(new Fe("--start <start>","Arbitrary setup command that will run before Momentic steps begin.")).addOption(new Fe("--wait-on <waitOn>","URL to wait to become accessible before Momentic tests begin.")).addOption(new Fe("--wait-on-timeout <waitOnTimeout>","Max time to wait for the --wait-on URL to become accessible.").default(60,"one minute")).addOption(new Fe("--retries <retries>","Number of retries to attempt when running tests locally. Defaults to each test's own retry configuration.")).addOption(new Fe("-p, --parallel <parallel>","When running with the --local flag, the number of tests to run in parallel. Defaults to 1.").default(1).argParser(t=>parseInt(t,10))).addOption(Ts).addArgument(Eu).action(async(t,e)=>{Cu(e);let{apiKey:r,server:o,remote:n,local:s,all:i,env:a,urlOverride:c,yes:l,parallel:d,report:u,start:p,waitOn:m,waitOnTimeout:g,retries:w,wait:f,pixelRatio:h}=e;!i&&(!t||!t.length)&&(F("You must pass at least one test path or use the --all flag."),process.exit(1));let b=new oe({baseURL:o,apiKey:r,logger:I});if(s){let T=await rt({client:b,skipPrompts:l,pullEnvs:!1,createFolders:!1}),S=h??ps();ms(S);try{await Du({tests:t,client:b,all:i,start:p,waitOn:m,waitOnTimeout:g,retriesOverride:w,parallel:d,report:u,devicePixelRatio:S,envName:a,orgId:T,urlOverride:c})}catch(C){F("Failed to run tests locally. Please check the error message below or run with the --verbose flag."),F(C),process.exit(1)}return}if(n){for(let T of t)(T.endsWith(".yaml")||qf(T))&&(F(`Looks like you tried to run a local test remotely: ${T}`),F("Use the --local flag or specify the test path without its file extension to queue tests remotely (e.g. 'hello-world')."),process.exit(1));await Nu({tests:t,client:b,all:i,env:a,urlOverride:c,wait:f});return}F("One of --remote or --local flag must be specified."),process.exit(1)});xe.command("pull").description("Fetch one or more tests from Momentic Cloud and save it in to local disk in YAML format.").addOption(ot).addOption(nt).addOption(Es).addOption(st).addArgument(Su).action(async(t,e)=>{let{apiKey:r,server:o,all:n,yes:s}=e,i=new oe({baseURL:o,apiKey:r,logger:I});await rt({client:i,skipPrompts:s,pullEnvs:!1,createFolders:!1}),!n&&!t?.length&&(F("At least one test name or the --all flag must be provided."),process.exit(1)),await xu({testsToFetch:t??[],client:i,all:n,yes:s})});xe.command("push").description("Save one or more tests in YAML format to Momentic Cloud.").addOption(st).addOption(ot).addOption(nt).addArgument(vu).action(async(t,e)=>{let{apiKey:r,server:o,yes:n}=e,s=new oe({baseURL:o,apiKey:r,logger:I});await rt({client:s,skipPrompts:n,pullEnvs:!1,createFolders:!1}),await Pu({paths:t,client:s,yes:n}),process.exit(0)});var Gu=xe.command("env").description("Perform operations on Momentic environments");Gu.command("push").description("Push one or more environments and associated variables to Momentic cloud.").addOption(st).addOption(ot).addOption(nt).addOption(vs).addArgument(Cs).action(async(t,e)=>{let{apiKey:r,server:o,yes:n,all:s}=e;!t?.length&&!s&&(F("At least one environment name or the --all flag must be provided."),process.exit(1));let i=new oe({baseURL:o,apiKey:r,logger:I});await rt({client:i,skipPrompts:n,pullEnvs:!1,createFolders:!1}),await fu({names:t,client:i,yes:n,all:s})});Gu.command("pull").description("Pull one or more environments and associated variables from Momentic cloud.").addOption(st).addOption(ot).addOption(nt).addOption(vs).addArgument(Cs).action(async(t,e)=>{let{apiKey:r,server:o,yes:n,all:s}=e;!t?.length&&!s&&(F("At least one environment name or the --all flag must be provided."),process.exit(1));let i=new oe({baseURL:o,apiKey:r,logger:I});await rt({client:i,skipPrompts:n,pullEnvs:!1,createFolders:!1}),await Po({envNames:t,client:i,skipPrompts:n,all:s})});async function Qf(){try{await xe.parseAsync(process.argv)}catch(t){let e={};try{e.playwrightVersion=jf("npx playwright --version").toString()}catch(r){I.error({err:r},"Error fetching debug information")}I.error({err:t,debugInfo:e},"Uncaught error in CLI"),F("Failed to start Momentic CLI."),F(t),process.exit(1)}}Qf();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "momentic",
3
- "version": "0.0.91",
3
+ "version": "0.0.92",
4
4
  "description": "The Momentic SDK for Node.js",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",