momentic 0.0.113 → 0.0.114
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +1 -1
- package/package.json +1 -1
- package/static/assets/index-1XpVtabR.js +623 -0
- package/static/index.html +1 -1
- package/static/assets/index-fkb0MVGt.js +0 -599
package/bin/cli.js
CHANGED
|
@@ -1604,7 +1604,7 @@ You have already executed the following commands successfully (most recent liste
|
|
|
1604
1604
|
`).forEach(l=>o.push(` ${l}`))):o.push("PAGE CONTENT CHANGE: <TOO_LONG_TO_DISPLAY/>"):o.push("PAGE CONTENT CHANGE: <NONE/>")}o.push("-".repeat(10))}),o.push(`STARTING URL: ${this.browser.baseURL}`),o.join(`
|
|
1605
1605
|
`)}getListHistory(){return ip`Here are the commands that you have successfully executed:
|
|
1606
1606
|
${this.commandHistory.filter(e=>e.type==="AI_ACTION").map(e=>`- ${e.serializedCommand}`).join(`
|
|
1607
|
-
`)}`}async executeCommand(e,t,n,o=!1){let s=this.commandHistory[this.commandHistory.length-1];if(!o&&(!s||s.state!=="PENDING"))throw new R("InternalWebAgentError","Executing command but there is no pending entry in the history");if(this.closed)throw new Error("Attempting to execute command on a closed controller");let i=Date.now(),a=await this.executePresetStep(e,t,n),l=Date.now()-i;return this.logger.debug({result:a,duration:l},"Got execution result"),a.succeedImmediately&&!o&&(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()),o||(s.generatedStep=e,s.serializedCommand=ut(e),s.state="DONE"),a}async executeAIAssertion(e){let t=!1,n=async()=>{try{let p=await this.browser.screenshot({clearHighlights:!0,scale:"css"});return Rl(this.uploadScreenshotForDebugging("assertion",p),{milliseconds:5e3,fallback:()=>{}}),p}catch(p){this.logger.warn({err:p},"Failed to take screenshot for assertion, continuing without it")}},o=await this.getBrowserState({}),s=await n(),{serializedTree:i}=o,{tree:a}=o,l=this.browser.url(),c=.2*An["gpt-4o-2024-05-13"],u=$o({serializedTree:i,tokenLimit:c,logger:this.logger});if(u){let h=(await this.generator.getRecommendedChunks({...u,description:e.assertion,tokenLimit:c})).ids;h.length===0?this.logger.debug("RAG returned no important information for assertion"):(i=a.pruneUsingRelevantIds(new Set(h)).serialize(),this.logger.debug({browserState:i},"Pruned a11y tree with RAG"),t=!0)}let d=this.getSerializedHistory(l,i),m={goal:e.assertion,url:l,browserState:i,history:d,lastCommand:this.lastExecutedCommand,screenshot:s?.toString("base64"),numPrevious:this.commandHistory.length},g=await this.generator.getAssertionResult(m,{disableCache:!!e.disableCache,orgId:this.orgId});if(g.relevantElements&&await Promise.all(Array.from(new Set(g.relevantElements)).slice(0,3).map(p=>this.browser.highlight({id:p}))),this.logger.debug({usedRag:t,result:g},"Got assertion result"),!g.result)throw new R("AssertionFailureError",g.thoughts);return{succeedImmediately:!1,thoughts:g.thoughts,urlAfterCommand:l,beforeScreenshotOverride:s}}async wrapMultiElementTargetingCommand(e,t,n,o,s=1){let i=await Promise.all(e.map((a,l)=>this.wrapElementTargetingCommand({target:a,cache:t[l],action:async c=>c,options:o})));try{return{result:await n(...i.map(l=>l.result)),caches:i.map(l=>l.cache),elementInteractedDisplayStrings:i.map(l=>l.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(()=>{}),n,o,s-1);throw new R("ActionFailureError",a.message,{cause:a})}}async wrapFrameUseCommand(e,t){if(!t)return e();let n=this.browser.getActiveFrame();try{return this.logger.debug({frameUrl:t},"Setting parent iframe target"),this.browser.setActiveFrame({type:"url",url:t}),await e()}finally{this.browser.setActiveFrame(n)}}async wrapElementTargetingCommand(e){return this.wrapFrameUseCommand(()=>this.wrapElementTargetingCommandHelper(e),e.options.iframeUrl)}async wrapElementTargetingCommandHelper({target:e,cache:t,action:n,options:o}){let{disableCache:s,useSelector:i}=o,a=o.retriesWithAI??1,l;if((!t||s)&&!Bn(e))throw new R("ActionFailureError","Cannot target element with no cached data or element descriptor");if(i){if(e.type!=="description")throw new R("ActionFailureError","Cannot use selector along with non-description target");let u={id:-1,selector:e.elementDescriptor},d=await this.browser.resolveTarget(u);return{result:await n(d.locator),cache:u,elementInteractedDisplayString:d.displayString}}s&&(this.logger.debug("Cache explicitly disabled for this step"),t=void 0),t?.inputDescription&&e.elementDescriptor!==t.inputDescription&&(this.logger.debug({old:t.inputDescription,new:e.elementDescriptor},"Target cache was generated with a different description, clearing it automatically"),t=void 0);let c=!!t&&_r(t);if(!t){this.logger.debug("Prompting AI for an updated element location"),a--;let u=await this.locateElement({description:e.elementDescriptor,disableCache:s,iframeUrl:o.iframeUrl});t=u.target,l=u.thoughts}try{let u=await this.browser.resolveTarget(t),d=await n(u.locator);return c?this.logger.debug({cache:t},"Successfully used cached target to perform action"):this.logger.debug({cache:t},"Successfully generated and used new target data"),{result:d,cache:t,thoughts:l,elementInteractedDisplayString:u.displayString}}catch(u){if(u instanceof R)throw u;if(a>0&&e)return this.logger.debug({err:u,cache:t},"Failed to execute action with cached target, retrying with AI"),this.wrapElementTargetingCommand({target:e,cache:void 0,action:n,options:{...o,retriesWithAI:a}});throw new R("ActionFailureError",u.message,{cause:u})}}async screenshotWithDimensions(e){return Rr(this.browser,e)}async executePresetStep(e,t,n){let o;try{o=await this.resolveCommandTemplateStrings(e,t)}catch(s){throw new R("ActionFailureError",s.message,{cause:s})}try{let s=await this.browser.getOpenPageUrls(),i=this.browser.url(),a=await this.executePresetActionHelper(e,t,n),l=!0;(e.type==="NAVIGATE"||e.type==="NEW_TAB"||e.type==="TAB"||e.type==="REFRESH")&&(l=!1);let c=await this.browser.getOpenPageUrls(),u=this.browser.url();if(l&&c.length!==s.length)for(let d=c.length-1;d>=0;d--){let m=c[d];if(Ot(m,this.logger)&&m!==i&&m!==u){await this.browser.switchToPage(m,d);break}}return a}catch(s){throw this.logger.error({err:s},"Error thrown in action controller"),s}finally{this.restoreCommandTemplateReplacements(e,o)}}restoreCommandTemplateReplacements(e,t={}){for(let[n,o]of Object.entries(t))Ho(e,n,o)}async resolveCommandTemplateStrings(e,t,n="",o={}){let s=["type","a11yData","thoughts","cache"];for(let i in e){if(s.includes(i))continue;let a=e[i],l=n?`${n}.${i}`:i;if(typeof a=="string"&&a.includes("{{")){let c=await wr({s:a,context:t,logger:this.logger});if(a===c)continue;o[l]=a,e[i]=c}else typeof a=="object"&&a!==null&&!Array.isArray(a)&&await this.resolveCommandTemplateStrings(a,t,l,o)}return o}async executePresetActionHelper(e,t,n){switch(n=n||"disableCache"in e&&!!e.disableCache,e.type){case"SUCCESS":let o=e.condition;return o?.assertion.trim()?this.wrapFrameUseCommand(()=>this.executeAIAssertion(o),o?.iframeUrl):{succeedImmediately:!1,urlAfterCommand:this.browser.url()};case"AI_ASSERTION":{if(!e.assertion.trim())throw new R("ActionFailureError","Missing assertion");return this.wrapFrameUseCommand(()=>this.executeAIAssertion(e),e.iframeUrl)}case"AI_WAIT":{if(!e.assertion.trim())throw new R("ActionFailureError","Missing assertion");let m=Date.now();if(e.timeout&&e.timeout>1800)throw new R("AssertionFailureError",`AI wait timeout of ${e.timeout} exceeds the maximum allowed value of 30 minutes.`);let g=(e.timeout??10)*1e3,p;g>10*60*1e3?p=Math.floor(g/6):g>60*1e3?p=Math.floor(g/5):g>10*1e3?p=Math.floor(g/4):p=0;let h,y,w=0;for(;Date.now()-m<g;)try{h=await this.wrapFrameUseCommand(()=>this.executeAIAssertion({...e,type:"AI_ASSERTION"}),e.iframeUrl);break}catch(S){y=S instanceof Error?S:new Error(`${S}`),this.logger.info({err:S},`AI_WAIT assert attempt ${w} failed, retrying...`),w++,p&&(this.logger.info(`Waiting ${p}ms before retrying AI wait check`),await j(p))}if(!h){let S=`AI wait still failing after ${g}ms.`;throw y&&(S+=` Latest result: ${y.message}`),new R("AssertionFailureError",S)}return h}case"AI_EXTRACT":{if(!e.goal.trim())throw new R("ActionFailureError","Cannot perform AI extraction without goal");let m=await this.browser.getCondensedHtml(),g=await this.generator.getTextExtraction({goal:e.goal,browserState:m,returnSchema:e.schema},{disableCache:n,orgId:this.orgId});if(g.result==="NOT_FOUND")throw new R("ActionFailureError","No relevant data found for extraction goal on this page");return{data:g.result,succeedImmediately:!1,urlAfterCommand:this.browser.url()}}case"NAVIGATE":if(!yn(e.url)&&!Sn(e.url,this.browser.baseURL))throw new R("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:m=>this.browser.click(m,{}),options:{disableCache:n,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()){let{cache:m,thoughts:g,elementInteractedDisplayString:p}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:h=>this.browser.hover(h,!1),options:{disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl}});i=p,m&&(e.cache={target:m}),a=g}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:this.browser.url(),elementInteracted:i,thoughts:a};case"WAIT":if(e.delay>1800)throw new R("UserConfigurationError",`User requested to wait for ${e.delay} seconds, which exceeds the maximum allowed time of 30 minutes`);let l=e.delay*1e3;for(;l>0;){let m=Math.min(l,3e4);l-=m,this.logger.debug(`Sleeping for ${m}ms (with ${l}ms remaining after)`),await j(m)}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 m=gl.parse(e);if(yt(e.target)){await this.browser.clickUsingVisualCoordinates(e.target.percentXYLocation,m);break}let g=this.browser.url(),p={disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl},{elementInteractedDisplayString:h,cache:y,thoughts:w,result:S}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:T=>this.browser.click(T,wl(this.orgId),m),options:p});y&&(e.cache={target:y});let C={urlAfterCommand:this.browser.url(),succeedImmediately:!1,elementInteracted:h,thoughts:w,data:S};return Lt(g,C.urlAfterCommand)&&(C.succeedImmediately=!0,C.succeedImmediatelyReason="URL changed"),C}case"DRAG":{if(yt(e.fromTarget)&&yt(e.toTarget)){await this.browser.dragAndDropUsingVisualCoordinates(e.fromTarget.percentXYLocation,e.toTarget.percentXYLocation,{force:e.force,hoverSeconds:e.hoverSeconds});break}if(yt(e.fromTarget)||yt(e.toTarget))throw new Error("Drag and drop targets must be both coordinates or both descriptions");let{caches:m,elementInteractedDisplayStrings:g}=await this.wrapMultiElementTargetingCommand([e.fromTarget,e.toTarget],[e.cache?.fromTarget,e.cache?.toTarget],(p,h)=>this.browser.dragAndDrop(p,h,{force:e.force,hoverSeconds:e.hoverSeconds}),{useSelector:!!e.useSelector,disableCache:n});return m&&m.every(p=>p)&&(e.cache={fromTarget:m[0],toTarget:m[1]}),{succeedImmediately:!1,urlAfterCommand:this.browser.url(),elementInteracted:g[0]}}case"MOUSE_DRAG":{if(e.target&&!Ge(e.target))throw new Error("Scroll with x/y is not supported yet");let m,g;if(e.target?.elementDescriptor){let y=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:async w=>w,options:{useSelector:!!e.useSelector,iframeUrl:e.iframeUrl,disableCache:n}});m=y.result,g=y.elementInteractedDisplayString}let p=parseInt(e.deltaX),h=parseInt(e.deltaY);if(isNaN(p)||isNaN(h))throw new R("ActionFailureError",`Invalid pixel values passed to mouse drag command: (${e.deltaX}, ${e.deltaY})`);return await this.browser.mouseDrag(p,h,e.steps,m,{force:e.force}),{succeedImmediately:!1,urlAfterCommand:this.browser.url(),elementInteracted:g}}case"SELECT_OPTION":{if(!Ge(e.target))throw new Error("Select with x/y is not supported yet");let m=e.target.elementDescriptor,{cache:g,thoughts:p,elementInteractedDisplayString:h}=await this.wrapElementTargetingCommand({target:{type:"description",elementDescriptor:cr(e,m)},cache:e.cache?.target,action:y=>this.browser.selectOption(y,e.option,e.force),options:{disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl}});return g&&(e.cache={target:g}),{succeedImmediately:!1,urlAfterCommand:this.browser.url(),elementInteracted:h,thoughts:p}}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 m;try{e.environment==="BROWSER"?m=await this.browser.executePageFunction(new Xr(e.fragment?`return ${e.code}`:e.code),void 0):m=await Kt({code:e.code,fragment:!!e.fragment,context:t,timeoutMs:e.timeout?e.timeout*1e3:void 0,logger:this.logger})}catch(g){throw new R("ActionFailureError",g instanceof Error?g.message:`${g}`,{cause:g})}try{JSON.stringify(m)}catch(g){throw new R("ActionFailureError",`Return value is not serializable: ${g instanceof Error?g.message:`${g}`}`,{cause:g})}return{urlAfterCommand:this.browser.url(),succeedImmediately:!1,data:m}}case"TYPE":{let m=this.browser.url(),g,p,h=lp(e.target);if(h){h.elementDescriptor=cr(e,h.elementDescriptor);let{elementInteractedDisplayString:w,cache:S,thoughts:C}=await this.wrapElementTargetingCommand({target:h,cache:e.cache?.target,action:T=>this.browser.typeIntoTarget(e.value,T,{force:e.force,clearContent:e.clearContent,pressKeysSequentially:e.pressKeysSequentially}),options:{disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl}});p=C,g=w,S&&(e.cache={target:S})}else await this.browser.type(e.value,{force:e.force,clearContent:e.clearContent,pressKeysSequentially:e.pressKeysSequentially});e.pressEnter&&await this.browser.press("Enter");let y={urlAfterCommand:this.browser.url(),succeedImmediately:!1,elementInteracted:g,thoughts:p};return Lt(m,y.urlAfterCommand)&&(y.succeedImmediately=!0,y.succeedImmediatelyReason="URL changed"),y}case"HOVER":{if(yt(e.target)){await this.browser.hoverUsingVisualCoordinates(e.target.percentXYLocation);break}let{cache:m,thoughts:g,elementInteractedDisplayString:p}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:h=>this.browser.hover(h,!!e.force),options:{disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl}});return m&&(e.cache={target:m}),{succeedImmediately:!1,urlAfterCommand:this.browser.url(),elementInteracted:p,thoughts:g}}case"FOCUS":{if(!Ge(e.target))throw new Error("Focus with x/y is not supported yet");let{elementInteractedDisplayString:m,cache:g,thoughts:p}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:h=>this.browser.focus(h),options:{disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl}});return g&&(e.cache={target:g}),{succeedImmediately:!1,urlAfterCommand:this.browser.url(),elementInteracted:m,thoughts:p}}case"BLUR":{if(!Ge(e.target))throw new Error("Blur with x/y is not supported yet");let{cache:m,thoughts:g,elementInteractedDisplayString:p}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:h=>this.browser.blur(h),options:{disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl}});return m&&(e.cache={target:m}),{succeedImmediately:!1,urlAfterCommand:this.browser.url(),elementInteracted:p,thoughts:g}}case"PRESS":let c=this.browser.url();await this.browser.press(e.value);let u={urlAfterCommand:this.browser.url(),succeedImmediately:!1};return Lt(c,u.urlAfterCommand)&&(u.succeedImmediately=!0,u.succeedImmediatelyReason="URL changed"),u;case"REQUEST":return El({command:e,browser:this.browser,logger:this.logger});case"VISUAL_DIFF":return vl({command:e,disableCache:n,browser:this.browser,browserStateGetter:m=>this.getBrowserState(m),targetingWrapper:m=>this.wrapElementTargetingCommand(m)});case"FILE_UPLOAD":{let m=e.fileSource.url;if(!m.startsWith("http")&&!m.startsWith(vr))throw new R("UserConfigurationError","The source URI for the file upload step must be a valid URL or a previously downloaded file beginning with 'file://'");let g=await Za({uri:m,logger:this.logger,orgId:this.orgId});await this.browser.setFileChooserHandler(g);break}case"AUTH_SAVE":return{data:await this.browser.saveAuthState(),succeedImmediately:!1,urlAfterCommand:this.browser.url()};case"AUTH_LOAD":{let m=await Kt({code:e.storageState,fragment:!1,context:t,logger:this.logger});if(typeof m!="object")throw new R("ActionFailureError",`Credentials must evaluate to an object (received ${typeof m} instead)`);let g;try{g=hl.parse(m)}catch(p){throw new R("ActionFailureError",`Credentials provided do not follow the required format: ${p}`)}await this.browser.loadAuthState(g);break}case"ELEMENT_CHECK":{if(e.target&&!Ge(e.target))throw new Error("Element assertion with x/y is not supported yet");await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:async m=>bl({assertion:e.assertion,element:m}),options:{disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl}});break}case"PAGE_CHECK":{await Cl({assertion:e.assertion,page:this.browser.getActivePage()});break}default:return(m=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(e)}return{succeedImmediately:!1,urlAfterCommand:this.browser.url()}}async getReverseMappedTarget(e,t,n){return(await this.generator.getReverseMappedDescription({browserState:e.browserState,goal:`${t}`},{disableCache:n,orgId:this.orgId})).phrase}async stopRecordMode(){this.recordAbortController?.abort(),await this.browser.clearAllCdpHighlights()}async startRecordMode(e){this.recordAbortController=new AbortController;let t=new xn({signal:this.recordAbortController.signal,...e});return await this.browser.startRecording(this.recordAbortController.signal,t),t}};var dp=2,up=2250;async function Al({socket:r,logger:e,storage:t,devicePixelRatio:n,generator:o,browserbase:s,getCpuUsage:i}){let a=r.id,l=i?.();if(l&&l>up)throw e.error({cpuUsage:l,event:"connect",args:r.handshake.query,sessionId:a},"Socket connection resource rate limit triggered"),new Error("You have reached a Momentic server under heavy load. Please refresh the page to establish a new connection. Automatic scale-up processes can take up to 3 minutes.");let c=r.handshake.query.testId,u=r.handshake.query.baseUrl,d=r.handshake.query.envName,m=r.handshake.query.viewport?JSON.parse(decodeURIComponent(r.handshake.query.viewport)):void 0,g=r.handshake.query.testMetadata?ae.parse(JSON.parse(decodeURIComponent(r.handshake.query.testMetadata))):void 0;if(!c||!u)throw new Error("Socket connection request is missing testId or baseUrl");let p=await t.getOrgId(c),h=r.handshake.query.environmentVariables?JSON.parse(decodeURIComponent(r.handshake.query.environmentVariables)):{},y=r.handshake.headers["x-forwarded-for"]?.split(",")[0],w=e.child({testId:c,baseUrl:u,orgId:p,sessionId:a});if(w.info({clientIp:y,event:"connect",args:r.handshake.query,sessionId:a},"Websocket event (connect)"),y&&O.getCurrentConnectionsByIp(y)>=dp)throw e.error({clientIp:y,sessions:O.getCurrentSessionsByIp(),...r.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(y);try{await mp({socket:r,baseUrl:u,envName:d,viewport:m,testMetadata:g,orgId:p,sessionId:a,logger:w,environmentVariables:h,clientIp:y,devicePixelRatio:n,storage:t,generator:o,browserbase:s})}catch(S){throw w.debug({err:S},"Error setting up socket session"),O.releaseCapacityByIp(y),S}return{sessionId:a,testId:c,orgId:p}}async function mp({socket:r,baseUrl:e,viewport:t,envName:n,devicePixelRatio:o,testMetadata:s,orgId:i,sessionId:a,logger:l,storage:c,generator:u,environmentVariables:d,clientIp:m}){let g={};t&&(g.viewport=t),o&&(g.deviceScaleFactor=o);let p=sr.parse(s?.advanced??{}),[h,y]=await Promise.all([He.init({baseUrl:e,userBrowserSettings:p,waitForLoad:!0,enricher:new Rn(l),timeout:p.pageLoadTimeoutMs,storage:c,logger:l,contextArgs:g,onTabsChange:(v,E)=>{r.emit("tabs",{tabs:v,activeTab:E})}}),Jt.init(i)]),w=new Qt({browser:h,generator:u,config:Er,logger:l,flagStore:y,orgId:i,storage:c}),S=ra(r,a,l),C=async()=>{clearInterval(S.timer)},T=new Te({baseUrl:e,currentUrl:w.browser.url(),variablesFromEnvironment:d,envName:n});if(!r.connected)throw await h.cleanup(),new Error("Socket not connected anymore, not proceeding with session setup");r.emit("session",{url:e,userAgent:He.USER_AGENT,viewport:await w.browser.getViewport(),sessionId:a}),O.registerSession({controller:w,context:T,sessionId:a,cleanup:C,clientIp:m})}var xl=[oa,Sa,wa,dl,na,ml,ul,Ca,Ea,Ra,Ta,ba,Aa,va];var Il=r=>{let{logger:e}=r,t=new pp(r.baseServer,{cors:{origin:"*",methods:["GET","POST"]},pingTimeout:3e4,pingInterval:6e4,maxHttpBufferSize:1e7});return t.on("connection",async n=>{let o;try{o=await Al({...r,socket:n})}catch(s){e.error({event:"connection",type:"websocket",err:s},"Failed to setup connection"),n.emit("error",{message:s instanceof Error?s.message:`${s}`}),n.disconnect(!0);return}xl.forEach(s=>gp(s,{socket:n,metadata:o,...r}))}),t};var gp=(r,e)=>{let t=r.createHandler(e),n=(...o)=>{["cloudMouseMove","cloudScroll","cloudType"].includes(r.event)||e.logger.debug({...e.metadata,event:r.event,args:o},`Websocket event (${r.event})`);let s=i=>{e.logger.error({event:r.event,type:"websocket",args:o,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=t.apply(void 0,o);i&&typeof i.catch=="function"&&i.catch(s)}catch(i){s(i)}};e.socket.on(r.event,n)};import{z as yp}from"zod";import hp from"fetch-retry";var fp=hp(global.fetch),ce=class{static API_VERSION="v1";baseURL;apiKey;constructor(e){this.baseURL=e.baseURL,this.apiKey=e.apiKey}async sendRequest(e,t){let n=await fp(`${this.baseURL}${e}`,{retries:1,retryDelay:1e3,method:"POST",body:JSON.stringify(t),headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}});if(!n.ok)throw new Error(`Request to ${e} failed with status ${n.status}: ${await n.text()}`);return n.json()}};var kt=class extends ce{constructor(e){super(e)}async getRecommendedChunks(e){let t=await this.sendRequest(`/${ce.API_VERSION}/web-agent/recommend-chunks`,e);return fi.parse(t)}async getScreenshotFromS3(e){let t=await this.sendRequest(`/${ce.API_VERSION}/s3/visual-diff-screenshot`,{url:e});return yp.string().parse(t)}async getElementLocation(e,t){let n=await this.sendRequest(`/${ce.API_VERSION}/web-agent/locate-element`,{...e,disableCache:t.disableCache});return ei.parse(n)}async getAssertionResult(e,t){let n={...e,disableCache:!!t.disableCache},o=await this.sendRequest(`/${ce.API_VERSION}/web-agent/assertion`,n);return Zs.parse(o)}async getProposedCommand(e,t){let n=await this.sendRequest(`/${ce.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:t.disableCache});return Qs.parse(n)}async getGranularGoals(e,t){let n=await this.sendRequest(`/${ce.API_VERSION}/web-agent/split-goal`,{url:e.url,goal:e.goal,disableCache:t.disableCache});return ti.parse(n)}async getReverseMappedDescription(e,t){let n=await this.sendRequest(`/${ce.API_VERSION}/web-agent/reverse-mapped-description`,{goal:e.goal,browserState:e.browserState,disableCache:t.disableCache});return ri.parse(n)}async getTextExtraction(e,t){let n={goal:e.goal,browserState:e.browserState,returnSchema:e.returnSchema,disableCache:!!t.disableCache},o=await this.sendRequest(`/${ce.API_VERSION}/web-agent/text-extraction`,n);return eo.parse(o)}async getTestResultClassification(e){let t=await this.sendRequest(`/${ce.API_VERSION}/web-agent/result-classification`,e);return Zn.parse(t)}};import{Router as Sp}from"express";function Se(r){return function(...e){let t=e[e.length-1],n=r(...e);Promise.resolve(n).catch(t)}}var Ll=Sp();Ll.get("/",Se((r,e)=>{let t=ea();e.status(200).json(t)}));var Ol=Ll;import{Router as vp}from"express";import Pl,{multistream as wp}from"pino";import bp from"pino-pretty";var jo=new Map,Cp=!0,Vo=class r{consoleLogger;ddClientToken;hostname;bindingAttributes;excludeAttributesInConsoleLogs;excludeTimestamps;disableConsoleLogs;site="https://http-intake.logs.us5.datadoghq.com/api/v2/logs";constructor({bindings:e,clientToken:t,hostname:n,excludeAttributesInConsoleLogs:o,excludeTimestamps:s,disableConsoleLogs:i}){this.ddClientToken=t,this.hostname=n,this.excludeAttributesInConsoleLogs=o,this.excludeTimestamps=s,this.disableConsoleLogs=i,this.bindingAttributes={...e,env:"production"};let a={base:o?{}:this.bindingAttributes,errorKey:"err",level:"debug",timestamp:!s};this.consoleLogger=Cp?Pl(a):Pl(a,wp([{stream:bp({colorize:!0})}]))}child(e){return new r({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,t,n,...o){if(t&&n===void 0&&(n=`${t}`,t={}),this.disableConsoleLogs||this.consoleLogger[e](e==="error"?t:{testId:t.testId,runId:t.runId},n,...o),typeof t=="object"&&t&&"err"in t&&t.err instanceof Error){let i={};for(let a of Object.getOwnPropertyNames(t.err))i[a]=t.err[a];i.name=t.err.name,t.err=i}let s=Object.assign({},this.bindingAttributes,t&&typeof t=="object"?t:{});o.length>0&&(s.args=o),(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:n||"",...s,level:e}}])});if(!i.ok)throw new Error(`Failed to log to Datadog: ${i.statusText})}`)}catch(i){this.disableConsoleLogs||this.consoleLogger.warn({obj:t,msg:n,args:o,err:i},"Failed to log to Datadog")}})()}debug(e,t,...n){this.log("debug",e,t,...n)}info(e,t,...n){this.log("info",e,t,...n)}warn(e,t,...n){this.log("warn",e,t,...n)}error(e,t,...n){this.log("error",e,t,...n)}bindings(){return this.bindingAttributes}setMinLevel(e){this.consoleLogger.level=e}enableConsoleLogs(){this.disableConsoleLogs=!1}},On=({app:r,clientToken:e,hostname:t,excludeAttributesInConsoleLogs:n,excludeTimestamps:o,disableConsoleLogs:s})=>{if(!process.env.DD_CLIENT_TOKEN&&!process.env.NEXT_PUBLIC_DD_CLIENT_TOKEN&&!e)throw new Error("Missing DD_CLIENT_TOKEN");return jo.has(r)||jo.set(r,new Vo({bindings:{app:r},hostname:t,clientToken:e||process.env.NEXT_PUBLIC_DD_CLIENT_TOKEN||process.env.DD_CLIENT_TOKEN,excludeAttributesInConsoleLogs:n,excludeTimestamps:o,disableConsoleLogs:s})),jo.get(r)};import{hostname as Tp}from"os";var we=On({app:"desktop-server",clientToken:"pubcfd7516a5c0ba852b42675cd97bee027",hostname:Tp(),excludeAttributesInConsoleLogs:!0,excludeTimestamps:!0,disableConsoleLogs:!0});var Ar=vp();Ar.get("/",Se(async(r,e)=>{let t=await qi(we);e.status(200).json(t)}));Ar.get("/metadata",Se((r,e)=>{let t=Vi();e.status(200).json(t)}));Ar.post("/",Se(async(r,e)=>{let t;try{t=qs.parse(r.body)}catch(o){e.status(400).json({error:`Invalid request body: ${o}`});return}try{ir(t.name)}catch(o){e.status(400).json({error:`Invalid module name: ${o}`});return}let n=await ji(t.name,t.steps);e.status(201).json(n)}));Ar.get("/:moduleId",Se(async(r,e)=>{if(!r.params.moduleId){e.status(400).json({error:"Missing moduleId in url path."});return}let t=await Ki(r.params.moduleId,we);if(!t){e.status(404).json({error:"Module not found."});return}e.json(t)}));var Ml=Ar;import{Router as Ep}from"express";import{existsSync as Rp}from"fs";import{join as Mn}from"path";import{v4 as Ap}from"uuid";var Ko="https://api.momentic.ai",Nl=r=>{Ko=r},xr=()=>Ko,Zt,qo,kl=async r=>{if(Zt)return Zt;let e=new te({baseURL:Ko,apiKey:r});try{return Zt=await e.getOrgId(),qo=r,Zt}catch(t){throw new Error(`Error checking API key against server: ${t}`)}},Pn=()=>{if(!Zt)throw new Error("Your organization ID appears invalid. Please contact Momentic Support.");return Zt},_l=()=>{if(!qo)throw new Error("Your API key appears invalid. Please contact Momentic Support.");return qo};var _t=Ep();_t.get("/",Se((r,e)=>{let t=fr(Y,we);e.status(200).json(t)}));_t.post("/",Se(async(r,e)=>{let t;try{t=Vs.parse(r.body)}catch(a){e.status(400).json({error:`Invalid request body: ${a}`});return}try{ir(t.name)}catch(a){e.status(400).json({error:a.message});return}let o={id:Ap(),name:t.name,baseUrl:t.baseUrl,schemaVersion:ge,advanced:{disableAICaching:!1,viewport:t.viewport??je},retries:0,steps:[]};t.environment&&(o.envs=[{name:t.environment,default:!0}]);let s=await Ji(o,t.name),i={...o,testPath:s};e.status(201).json(i)}));_t.get("/:testPath",Se(async(r,e)=>{if(!r.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let t=Mn(Y,r.params.testPath);if(!Rp(t)){e.status(400).json({error:`Test not found at path: ${t}`});return}let n=await hn(t,mt,we);e.status(200).json(n)}));_t.patch("/:testPath/metadata",Se(async(r,e)=>{if(!r.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let t;try{t=js.parse(r.body)}catch(o){e.status(400).json({error:`Invalid request body: ${o}`});return}let n=Mn(Y,r.params.testPath);gn(t,n),e.status(201).json({message:"ok"})}));_t.patch("/:testPath",Se(async(r,e)=>{if(!r.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let t;try{t=Gs.parse(r.body)}catch(c){e.status(400).json({error:`Invalid request body: ${c}`});return}let n=Mn(Y,r.params.testPath),o;try{o=Qi(n)}catch(c){e.status(400).json({error:`Existing test file on disk is invalid: ${c}`});return}let{stepsToSave:s,moduleUpdates:i,cachesToSave:a}=await Be({steps:t.steps,cacheCreationParams:{testId:o.id,orgId:Pn()}});i.forEach(c=>Gi({...c,schemaVersion:ge})),gn({schemaVersion:ge,steps:s},n),await new te({apiKey:_l(),baseURL:xr()}).updateStepCaches({entries:a,testId:o.id}),e.status(201).json({message:"ok"})}));_t.patch("/:testPath/environments",Se(async(r,e)=>{if(!r.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let t;try{t=Ks.parse(r.body)}catch(o){e.status(400).json({error:`Invalid request body: ${o}`});return}let n=Mn(Y,r.params.testPath);gn({envs:t.defaultEnv?[{name:t.defaultEnv,default:!0}]:[]},n),e.status(201).json({message:"ok"})}));var Dl=_t;async function Fl(r){let{momenticServerUrl:e,apiKey:t,serverPort:n,appPort:o,staticDir:s,devicePixelRatio:i}=r;if(!Lp(Y)||!Op(Y).isDirectory()){let h=Np.isAbsolute(Y);throw new Error(`Root folder ${Y} does not exist${h?"":` in the current directory ("${process.cwd()}")`}. Please run \`npx momentic init\` if you wish to setup Momentic here for the first time.`)}e&&Nl(e),we.debug(r,"Initializing desktop server");let a=await kl(t);we.debug({orgId:a},"Checking API key successful");let c=`http://localhost:${n}`;o&&(c=`http://localhost:${o}`);let u=kp(s);await new Promise(h=>{u.listen(n,()=>{we.info(`Server is running at http://localhost:${n}`),h()})});let m=new kt({baseURL:xr(),apiKey:t}),g=new te({baseURL:xr(),apiKey:t}),p=new qt(g,Pn());Il({baseServer:u,generator:m,storage:p,logger:we,devicePixelRatio:i}),await Mp(c)}function kp(r){let e=Yo();e.use(Ip()),e.use(xp.json({limit:"50mb"}));let t=Yo.Router();if(t.use("/tests",Dl),t.use("/modules",Ml),t.use("/environments",Ol),e.use("/api",t),e.use((o,s,i)=>{o.path!=="/healthcheck"&&we.debug({url:o.url,path:o.path,query:o.query,method:o.method,body:o.body,headers:o.rawHeaders,client:o.ip},"Received desktop-server request"),s.on("close",()=>{s.statusCode>=400&&we.error({url:o.url,method:o.method,statusCode:s.statusCode},"Request completed in error")}),i()}),e.use((o,s,i,a)=>{o instanceof Error&&o.message.includes("BadRequestError: request aborted")||(we.error({stack:o.stack,msg:o.message,url:s.url,method:s.method},"Unhandled exception leading to 500 on desktop-server"),i.status(500).send("Internal Server Error"),a(o))}),r){let o=Yo.static(r);e.use("/",o),e.use("*",o)}return Pp.createServer(e)}import{existsSync as Mg}from"fs";import yc,{dirname as Ng,join as kg}from"path";import{chdir as _g}from"process";import{fileURLToPath as Dg}from"url";var Ul="0.0.113";import Ir from"chalk";var Xe=(...r)=>{q(Ir.blue(...r))};var Je=(...r)=>{q(Ir.dim(...r))};var me=(...r)=>{q(Ir.green(...r))},q=(...r)=>{console.log(...r)},re=(...r)=>{console.error(Ir.yellow(...r))},L=(...r)=>{console.error(Ir.red(...r))};import{hostname as Dp}from"os";var I=On({app:"cli",clientToken:"pub7eb923f18fb3f1d42ac5eba8c5ea13a5",hostname:Dp(),excludeAttributesInConsoleLogs:!0,excludeTimestamps:!0,disableConsoleLogs:!0});import{execSync as Fp}from"child_process";import{platform as Up}from"os";function Xo(){return zl()?(Xe("Setting device pixel ratio to 2 automatically since a Mac OS Retina screen was detected."),Xe("If you are using a low pixel-density monitor, you should manually set --pixel-ratio to 1 to avoid incorrect viewport calculations."),Xe("Confirm your device's pixel-ratio at https://www.mydevice.io."),2):(Xe("Setting device pixel ratio to 1."),Xe("If you are using Momentic on a high-pixel density (HiDPI) monitor, relaunch with the --pixel-ratio option to avoid incorrect viewport calculations"),Xe("Confirm your device's pixel-ratio at https://www.mydevice.io."),1)}function zl(){return Up()==="darwin"&&Fp("system_profiler SPDisplaysDataType").toString().includes("Retina")}function Jo(r){zl()&&r===1&&(re("If you are using Momentic on a Retina screen, relaunch with the --pixel-ratio option to avoid incorrect viewport calculations."),re("Confirm your device's pixel-ratio at https://www.mydevice.io."))}import{createHash as jl}from"crypto";import{existsSync as Vl,readFileSync as ns,writeFileSync as ql}from"fs";import{join as Kl}from"path";import{parse as eg}from"yaml";import{existsSync as es,mkdirSync as $l,readdirSync as Kp,statSync as Hl}from"fs";import{homedir as Wl}from"os";import{join as Qe,resolve as Yp}from"path";import{existsSync as zp,statSync as Bp}from"fs";var Nn=!!process.env.CI||!process.stdout.isTTY;function ht(r){try{return zp(r)&&Bp(r).isDirectory()}catch(e){return L({err:e},`Error reading path ${r} during directory existence check`),!1}}import{registry as Qo}from"playwright-core/lib/server";function $p(r){let e=[],t=[];for(let n of r){let o=Qo.findExecutable(n);!o||o.installType==="none"?e.push(n):t.push(o)}return t}async function kn(){let r=$p(["chromium"]);await Qo.installDeps(r,!1),await Qo.install(r,!1)}import{statSync as Hp}from"fs";import{existsSync as Wp,mkdirSync as Gp}from"fs";import{dirname as jp}from"path";import Vp from"readline/promises";var Zo=!1,qp=(()=>{try{return Hp("/.dockerenv"),!0}catch{return!1}})();async function _e(r,e){if(Nn||Zo||qp)return!0;I.flush(),await new Promise(i=>setTimeout(i,500));let t=Vp.createInterface({input:process.stdin,output:process.stdout}),n=r.split("."),o;if(n.length===1)o=r;else{let i=`${n.slice(0,n.length-1).join(". ").trim()}.`;e?re(i):q(i),o=n[n.length-1].trim()}let s=await t.question(`${o} ('y' for yes / n for no / 'A' to accept all) `);return t.close(),s==="A"?(Zo=!0,setTimeout(()=>{Zo=!1},3e3),!0):s.toLowerCase()==="y"}async function Lr(r){let e=jp(r);return ht(e)?Wp(r)?_e(`File '${Bl(r)}' already exists. Overwrite existing content?`,!0):!0:await _e(`Directory '${Bl(e)}' doesn't exist. Create it now?`,!0)?(Gp(e,{recursive:!0}),!0):!1}function Bl(r){return r.replace(/(\s+)/g,"\\$1")}var Xp=[lt,tn,Wt],Ze=Zp(),Dt=Qe(Ze,lt),rs=Qe(Ze,tn),et=Qe(Ze,Wt),ts=Qe(Wl(),"momentic",uo),Gl=[Dt,rs,et];async function tt(r){let e=process.versions.node,t=parseInt(e.split(".")[0]);(isNaN(t)||t<20)&&(L(`Node.js version 20 or higher is required to run the Momentic CLI. Detected: ${process.versions.node}.`),process.exit(1));let n=await r.client.getOrgId();return await kn(),await Qp(r),n}async function Jp(r){ht(Ze)||!r&&!await _e(`The root '${at}' folder was not found in the current directory. You will need to invoke the Momentic CLI from this directory in the future. Proceed?`)&&(L("Setup cancelled."),process.exit(1)),ht(ts)||(!r&&!await _e("The Chrome cache folder was not found in your home directory. Proceed with creation?")&&(L("Setup cancelled."),process.exit(1)),$l(ts,{recursive:!0}));for(let e of Gl)ht(e)||(!r&&!await _e(`'${e}' folder was not found in the current directory. Create it now?`)&&(L("Setup cancelled."),process.exit(1)),$l(e,{recursive:!0}))}function Or(r,e,t=new Set){for(let n of r){let o=Yp(n),s=!1;try{s=es(o)&&Hl(o).isDirectory()}catch(i){I.error({err:i},`Error reading path ${o} during collect paths`)}if(o&&s){let i=Kp(o).map(a=>Qe(o,a));Or(i,e,t);continue}if(o.endsWith(".yaml")){try{if(!es(o)||!Hl(o).isFile()){I.error(`File not found or unreadable: ${o}`);continue}}catch(i){I.error({err:i},`Error reading file ${o} during collect paths`);continue}if(!e(o))continue;t.add(o)}}return t}async function Qp({client:r,createFolders:e,skipPrompts:t,pullEnvs:n}){if(Wl()||(L("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 Jp(t);else for(let o of[Ze,ts,...Gl])!ht(o)&&!Nn&&(L(`The '${o}' 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.`),L(`The current working directory is: ${process.cwd()}`),process.exit(1));n&&(!t&&!await _e("Pull environments from Momentic Cloud? Environment definitions may be required to run tests locally.")&&(L("Setup cancelled."),process.exit(1)),await _n({client:r,all:!0,skipPrompts:t}))}function Zp(){let r=n=>es(Qe(n,at))&&Xp.some(o=>ht(Qe(n,at,o))),e=process.cwd();for(;e!=="/"&&!r(e);)e=Qe(e,"..");let t;return r(e)?t=e:t=process.cwd(),t!==process.cwd()?re(`Current working directory does not contain a ${at} folder, treating ${t} as the Momentic root folder instead`):I.info(`Identified ${t} as the Momentic root folder`),Qe(t,at)}async function _n({envNames:r,client:e,all:t,skipPrompts:n}){let o=await e.getAllEnvironments();r&&!t&&(o=o.filter(a=>r?.includes(a.name))),Vl(et)||(L(`${et} does not exist in the current directory. Please run 'npx momentic@latest init' first.`),process.exit(1));let s=[],i=[];for(let a of o){let l=Kl(et,`${a.name}.yaml`);if(!n&&!await Lr(l))return;if(!Vl(l))ql(l,wo(a)),s.push(l);else{let c=jl("sha256").update(ns(l)).digest("hex"),u=wo(a),d=jl("sha256").update(u).digest("hex");c!==d&&(ql(l,u),i.push(l))}}me("Pulled environments successfully!"),s.length&&(me("Created:"),s.forEach(a=>me(" ",a))),i.length&&(me("Updated:"),i.forEach(a=>me(" ",a)))}function tg(r){return r.endsWith(".yaml")?ns(r,"utf8").includes("momentic/environment")?!0:(re(`Skipping YAML that is not a Momentic environment: ${r}`),!1):!1}async function Yl({client:r,names:e,all:t,yes:n}){let o;t?o=[et]:o=e.map(a=>Kl(et,`${a.toLowerCase()}.yaml`));let s=Or(o,tg);q(`Found ${s.size} environments to push:`),s.forEach(a=>q(" ",a));let i=[];for(let a of s){let l=eg(ns(a,"utf-8"));try{let c=Ce.parse(l);i.push(c)}catch(c){L(`${a} failed to parse as a valid environment file.`),L(c),process.exit(1)}}!n&&!await _e("Pushing environments can overwrite variables on cloud and instantly affect scheduled runs. Continue?",!0)||(await r.updateEnvironments(i),me("Pushed environments successfully!"))}import{Argument as Pr,Option as Me}from"commander";import{validateHeaderValue as rg}from"http";import{cpus as Xl}from"os";import{z as ng}from"zod";var rt=new Me("--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 Me("--server <server>","Momentic server to use. Leave unchanged unless using Momentic on-premise.").default("https://api.momentic.ai"),ot=new Me("-y, --yes","Skip all confirmation prompts.").env("CI"),os=new Me("-w, --wait","Wait for tests to finish running before exiting. Only applicable when running tests remotely").implies({remote:!0}),ss=new Me("--wait-timeout-seconds <waitTimeoutSeconds>","The maximum number of seconds to wait for tests to complete. Only applicable when the --wait option is specified.").argParser(r=>parseInt(r,10)),is=new Me("--custom-headers <customHeaders...>","Specify custom headers in the form HEADER=VALUE to be sent with each request during the test. Multiple entries can be provided.");function as(r){if(r===void 0)return;let e={};for(let t of r){let n=t.indexOf("=");if(n===-1)throw new Error(`Header value pair does not contain '=': ${t}`);let o=t.slice(0,n),s=t.slice(n+1);rg(o,s),e[o]=s}return e}var ls=new Me("--no-report","Skip reporting test results to Momentic Cloud when running with the --local flag.").implies({local:!0}),cs=new Me("--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(r=>parseInt(r,10)),ds=new Me("--env <env>","Name of the environment to use when running tests."),us=new Me("--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."),ms=new Me("-a, --all","Select all tests in your organization from Momentic Cloud. Cannot be used together with <tests> arguments."),ps=new Me("-a, --all","Select all environments in your organization from Momentic Cloud. Cannot be used together with <paths> arguments."),Jl=new Pr("<tests...>",`One or more test paths to pull from Momentic Cloud.
|
|
1607
|
+
`)}`}async executeCommand(e,t,n,o=!1){let s=this.commandHistory[this.commandHistory.length-1];if(!o&&(!s||s.state!=="PENDING"))throw new R("InternalWebAgentError","Executing command but there is no pending entry in the history");if(this.closed)throw new Error("Attempting to execute command on a closed controller");let i=Date.now(),a=await this.executePresetStep(e,t,n),l=Date.now()-i;return this.logger.debug({result:a,duration:l},"Got execution result"),a.succeedImmediately&&!o&&(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()),o||(s.generatedStep=e,s.serializedCommand=ut(e),s.state="DONE"),a}async executeAIAssertion(e){let t=!1,n=async()=>{try{let p=await this.browser.screenshot({clearHighlights:!0,scale:"css"});return Rl(this.uploadScreenshotForDebugging("assertion",p),{milliseconds:5e3,fallback:()=>{}}),p}catch(p){this.logger.warn({err:p},"Failed to take screenshot for assertion, continuing without it")}},o=await this.getBrowserState({}),s=await n(),{serializedTree:i}=o,{tree:a}=o,l=this.browser.url(),c=.2*An["gpt-4o-2024-05-13"],u=$o({serializedTree:i,tokenLimit:c,logger:this.logger});if(u){let h=(await this.generator.getRecommendedChunks({...u,description:e.assertion,tokenLimit:c})).ids;h.length===0?this.logger.debug("RAG returned no important information for assertion"):(i=a.pruneUsingRelevantIds(new Set(h)).serialize(),this.logger.debug({browserState:i},"Pruned a11y tree with RAG"),t=!0)}let d=this.getSerializedHistory(l,i),m={goal:e.assertion,url:l,browserState:i,history:d,lastCommand:this.lastExecutedCommand,screenshot:s?.toString("base64"),numPrevious:this.commandHistory.length},g=await this.generator.getAssertionResult(m,{disableCache:!!e.disableCache,orgId:this.orgId});if(g.relevantElements&&await Promise.all(Array.from(new Set(g.relevantElements)).slice(0,3).map(p=>this.browser.highlight({id:p}))),this.logger.debug({usedRag:t,result:g},"Got assertion result"),!g.result)throw new R("AssertionFailureError",g.thoughts);return{succeedImmediately:!1,thoughts:g.thoughts,urlAfterCommand:l,beforeScreenshotOverride:s}}async wrapMultiElementTargetingCommand(e,t,n,o,s=1){let i=await Promise.all(e.map((a,l)=>this.wrapElementTargetingCommand({target:a,cache:t[l],action:async c=>c,options:o})));try{return{result:await n(...i.map(l=>l.result)),caches:i.map(l=>l.cache),elementInteractedDisplayStrings:i.map(l=>l.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(()=>{}),n,o,s-1);throw new R("ActionFailureError",a.message,{cause:a})}}async wrapFrameUseCommand(e,t){if(!t)return e();let n=this.browser.getActiveFrame();try{return this.logger.debug({frameUrl:t},"Setting parent iframe target"),this.browser.setActiveFrame({type:"url",url:t}),await e()}finally{this.browser.setActiveFrame(n)}}async wrapElementTargetingCommand(e){return this.wrapFrameUseCommand(()=>this.wrapElementTargetingCommandHelper(e),e.options.iframeUrl)}async wrapElementTargetingCommandHelper({target:e,cache:t,action:n,options:o}){let{disableCache:s,useSelector:i}=o,a=o.retriesWithAI??1,l;if((!t||s)&&!Bn(e))throw new R("ActionFailureError","Cannot target element with no cached data or element descriptor");if(i){if(e.type!=="description")throw new R("ActionFailureError","Cannot use selector along with non-description target");let u={id:-1,selector:e.elementDescriptor},d=await this.browser.resolveTarget(u);return{result:await n(d.locator),cache:u,elementInteractedDisplayString:d.displayString}}s&&(this.logger.debug("Cache explicitly disabled for this step"),t=void 0),t?.inputDescription&&e.elementDescriptor!==t.inputDescription&&(this.logger.debug({old:t.inputDescription,new:e.elementDescriptor},"Target cache was generated with a different description, clearing it automatically"),t=void 0);let c=!!t&&_r(t);if(!t){this.logger.debug("Prompting AI for an updated element location"),a--;let u=await this.locateElement({description:e.elementDescriptor,disableCache:s,iframeUrl:o.iframeUrl});t=u.target,l=u.thoughts}try{let u=await this.browser.resolveTarget(t),d=await n(u.locator);return c?this.logger.debug({cache:t},"Successfully used cached target to perform action"):this.logger.debug({cache:t},"Successfully generated and used new target data"),{result:d,cache:t,thoughts:l,elementInteractedDisplayString:u.displayString}}catch(u){if(u instanceof R)throw u;if(a>0&&e)return this.logger.debug({err:u,cache:t},"Failed to execute action with cached target, retrying with AI"),this.wrapElementTargetingCommand({target:e,cache:void 0,action:n,options:{...o,retriesWithAI:a}});throw new R("ActionFailureError",u.message,{cause:u})}}async screenshotWithDimensions(e){return Rr(this.browser,e)}async executePresetStep(e,t,n){let o;try{o=await this.resolveCommandTemplateStrings(e,t)}catch(s){throw new R("ActionFailureError",s.message,{cause:s})}try{let s=await this.browser.getOpenPageUrls(),i=this.browser.url(),a=await this.executePresetActionHelper(e,t,n),l=!0;(e.type==="NAVIGATE"||e.type==="NEW_TAB"||e.type==="TAB"||e.type==="REFRESH")&&(l=!1);let c=await this.browser.getOpenPageUrls(),u=this.browser.url();if(l&&c.length!==s.length)for(let d=c.length-1;d>=0;d--){let m=c[d];if(Ot(m,this.logger)&&m!==i&&m!==u){await this.browser.switchToPage(m,d);break}}return a}catch(s){throw this.logger.error({err:s},"Error thrown in action controller"),s}finally{this.restoreCommandTemplateReplacements(e,o)}}restoreCommandTemplateReplacements(e,t={}){for(let[n,o]of Object.entries(t))Ho(e,n,o)}async resolveCommandTemplateStrings(e,t,n="",o={}){let s=["type","a11yData","thoughts","cache"];for(let i in e){if(s.includes(i))continue;let a=e[i],l=n?`${n}.${i}`:i;if(typeof a=="string"&&a.includes("{{")){let c=await wr({s:a,context:t,logger:this.logger});if(a===c)continue;o[l]=a,e[i]=c}else typeof a=="object"&&a!==null&&!Array.isArray(a)&&await this.resolveCommandTemplateStrings(a,t,l,o)}return o}async executePresetActionHelper(e,t,n){switch(n=n||"disableCache"in e&&!!e.disableCache,e.type){case"SUCCESS":let o=e.condition;return o?.assertion.trim()?this.wrapFrameUseCommand(()=>this.executeAIAssertion(o),o?.iframeUrl):{succeedImmediately:!1,urlAfterCommand:this.browser.url()};case"AI_ASSERTION":{if(!e.assertion.trim())throw new R("ActionFailureError","Missing assertion");return this.wrapFrameUseCommand(()=>this.executeAIAssertion(e),e.iframeUrl)}case"AI_WAIT":{if(!e.assertion.trim())throw new R("ActionFailureError","Missing assertion");let m=Date.now();if(e.timeout&&e.timeout>1800)throw new R("AssertionFailureError",`AI wait timeout of ${e.timeout} exceeds the maximum allowed value of 30 minutes.`);let g=(e.timeout??10)*1e3,p;g>10*60*1e3?p=Math.floor(g/6):g>60*1e3?p=Math.floor(g/5):g>10*1e3?p=Math.floor(g/4):p=0;let h,y,w=0;for(;Date.now()-m<g;)try{h=await this.wrapFrameUseCommand(()=>this.executeAIAssertion({...e,type:"AI_ASSERTION"}),e.iframeUrl);break}catch(S){y=S instanceof Error?S:new Error(`${S}`),this.logger.info({err:S},`AI_WAIT assert attempt ${w} failed, retrying...`),w++,p&&(this.logger.info(`Waiting ${p}ms before retrying AI wait check`),await j(p))}if(!h){let S=`AI wait still failing after ${g}ms.`;throw y&&(S+=` Latest result: ${y.message}`),new R("AssertionFailureError",S)}return h}case"AI_EXTRACT":{if(!e.goal.trim())throw new R("ActionFailureError","Cannot perform AI extraction without goal");let m=await this.browser.getCondensedHtml(),g=await this.generator.getTextExtraction({goal:e.goal,browserState:m,returnSchema:e.schema},{disableCache:n,orgId:this.orgId});if(g.result==="NOT_FOUND")throw new R("ActionFailureError","No relevant data found for extraction goal on this page");return{data:g.result,succeedImmediately:!1,urlAfterCommand:this.browser.url()}}case"NAVIGATE":if(!yn(e.url)&&!Sn(e.url,this.browser.baseURL))throw new R("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:m=>this.browser.click(m,{}),options:{disableCache:n,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()){let{cache:m,thoughts:g,elementInteractedDisplayString:p}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:h=>this.browser.hover(h,!1),options:{disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl}});i=p,m&&(e.cache={target:m}),a=g}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:this.browser.url(),elementInteracted:i,thoughts:a};case"WAIT":if(e.delay>1800)throw new R("UserConfigurationError",`User requested to wait for ${e.delay} seconds, which exceeds the maximum allowed time of 30 minutes`);let l=e.delay*1e3;for(;l>0;){let m=Math.min(l,3e4);l-=m,this.logger.debug(`Sleeping for ${m}ms (with ${l}ms remaining after)`),await j(m)}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 m=gl.parse(e);if(yt(e.target)){await this.browser.clickUsingVisualCoordinates(e.target.percentXYLocation,m);break}let g=this.browser.url(),p={disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl},{elementInteractedDisplayString:h,cache:y,thoughts:w,result:S}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:T=>this.browser.click(T,wl(this.orgId),m),options:p});y&&(e.cache={target:y});let C={urlAfterCommand:this.browser.url(),succeedImmediately:!1,elementInteracted:h,thoughts:w,data:S};return Lt(g,C.urlAfterCommand)&&(C.succeedImmediately=!0,C.succeedImmediatelyReason="URL changed"),C}case"DRAG":{if(yt(e.fromTarget)&&yt(e.toTarget)){await this.browser.dragAndDropUsingVisualCoordinates(e.fromTarget.percentXYLocation,e.toTarget.percentXYLocation,{force:e.force,hoverSeconds:e.hoverSeconds});break}if(yt(e.fromTarget)||yt(e.toTarget))throw new Error("Drag and drop targets must be both coordinates or both descriptions");let{caches:m,elementInteractedDisplayStrings:g}=await this.wrapMultiElementTargetingCommand([e.fromTarget,e.toTarget],[e.cache?.fromTarget,e.cache?.toTarget],(p,h)=>this.browser.dragAndDrop(p,h,{force:e.force,hoverSeconds:e.hoverSeconds}),{useSelector:!!e.useSelector,disableCache:n});return m&&m.every(p=>p)&&(e.cache={fromTarget:m[0],toTarget:m[1]}),{succeedImmediately:!1,urlAfterCommand:this.browser.url(),elementInteracted:g[0]}}case"MOUSE_DRAG":{if(e.target&&!Ge(e.target))throw new Error("Scroll with x/y is not supported yet");let m,g;if(e.target?.elementDescriptor){let y=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:async w=>w,options:{useSelector:!!e.useSelector,iframeUrl:e.iframeUrl,disableCache:n}});m=y.result,g=y.elementInteractedDisplayString}let p=parseInt(e.deltaX),h=parseInt(e.deltaY);if(isNaN(p)||isNaN(h))throw new R("ActionFailureError",`Invalid pixel values passed to mouse drag command: (${e.deltaX}, ${e.deltaY})`);return await this.browser.mouseDrag(p,h,e.steps,m,{force:e.force}),{succeedImmediately:!1,urlAfterCommand:this.browser.url(),elementInteracted:g}}case"SELECT_OPTION":{if(!Ge(e.target))throw new Error("Select with x/y is not supported yet");let m=e.target.elementDescriptor,{cache:g,thoughts:p,elementInteractedDisplayString:h}=await this.wrapElementTargetingCommand({target:{type:"description",elementDescriptor:cr(e,m)},cache:e.cache?.target,action:y=>this.browser.selectOption(y,e.option,e.force),options:{disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl}});return g&&(e.cache={target:g}),{succeedImmediately:!1,urlAfterCommand:this.browser.url(),elementInteracted:h,thoughts:p}}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 m;try{e.environment==="BROWSER"?m=await this.browser.executePageFunction(new Xr(e.fragment?`return ${e.code}`:e.code),void 0):m=await Kt({code:e.code,fragment:!!e.fragment,context:t,timeoutMs:e.timeout?e.timeout*1e3:void 0,logger:this.logger})}catch(g){throw new R("ActionFailureError",g instanceof Error?g.message:`${g}`,{cause:g})}try{JSON.stringify(m)}catch(g){throw new R("ActionFailureError",`Return value is not serializable: ${g instanceof Error?g.message:`${g}`}`,{cause:g})}return{urlAfterCommand:this.browser.url(),succeedImmediately:!1,data:m}}case"TYPE":{let m=this.browser.url(),g,p,h=lp(e.target);if(h){h.elementDescriptor=cr(e,h.elementDescriptor);let{elementInteractedDisplayString:w,cache:S,thoughts:C}=await this.wrapElementTargetingCommand({target:h,cache:e.cache?.target,action:T=>this.browser.typeIntoTarget(e.value,T,{force:e.force,clearContent:e.clearContent,pressKeysSequentially:e.pressKeysSequentially}),options:{disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl}});p=C,g=w,S&&(e.cache={target:S})}else await this.browser.type(e.value,{force:e.force,clearContent:e.clearContent,pressKeysSequentially:e.pressKeysSequentially});e.pressEnter&&await this.browser.press("Enter");let y={urlAfterCommand:this.browser.url(),succeedImmediately:!1,elementInteracted:g,thoughts:p};return Lt(m,y.urlAfterCommand)&&(y.succeedImmediately=!0,y.succeedImmediatelyReason="URL changed"),y}case"HOVER":{if(yt(e.target)){await this.browser.hoverUsingVisualCoordinates(e.target.percentXYLocation);break}let{cache:m,thoughts:g,elementInteractedDisplayString:p}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:h=>this.browser.hover(h,!!e.force),options:{disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl}});return m&&(e.cache={target:m}),{succeedImmediately:!1,urlAfterCommand:this.browser.url(),elementInteracted:p,thoughts:g}}case"FOCUS":{if(!Ge(e.target))throw new Error("Focus with x/y is not supported yet");let{elementInteractedDisplayString:m,cache:g,thoughts:p}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:h=>this.browser.focus(h),options:{disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl}});return g&&(e.cache={target:g}),{succeedImmediately:!1,urlAfterCommand:this.browser.url(),elementInteracted:m,thoughts:p}}case"BLUR":{if(!Ge(e.target))throw new Error("Blur with x/y is not supported yet");let{cache:m,thoughts:g,elementInteractedDisplayString:p}=await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:h=>this.browser.blur(h),options:{disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl}});return m&&(e.cache={target:m}),{succeedImmediately:!1,urlAfterCommand:this.browser.url(),elementInteracted:p,thoughts:g}}case"PRESS":let c=this.browser.url();await this.browser.press(e.value);let u={urlAfterCommand:this.browser.url(),succeedImmediately:!1};return Lt(c,u.urlAfterCommand)&&(u.succeedImmediately=!0,u.succeedImmediatelyReason="URL changed"),u;case"REQUEST":return El({command:e,browser:this.browser,logger:this.logger});case"VISUAL_DIFF":return vl({command:e,disableCache:n,browser:this.browser,browserStateGetter:m=>this.getBrowserState(m),targetingWrapper:m=>this.wrapElementTargetingCommand(m)});case"FILE_UPLOAD":{let m=e.fileSource.url;if(!m.startsWith("http")&&!m.startsWith(vr))throw new R("UserConfigurationError","The source URI for the file upload step must be a valid URL or a previously downloaded file beginning with 'file://'");let g=await Za({uri:m,logger:this.logger,orgId:this.orgId});await this.browser.setFileChooserHandler(g);break}case"AUTH_SAVE":return{data:await this.browser.saveAuthState(),succeedImmediately:!1,urlAfterCommand:this.browser.url()};case"AUTH_LOAD":{let m=await Kt({code:e.storageState,fragment:!1,context:t,logger:this.logger});if(typeof m!="object")throw new R("ActionFailureError",`Credentials must evaluate to an object (received ${typeof m} instead)`);let g;try{g=hl.parse(m)}catch(p){throw new R("ActionFailureError",`Credentials provided do not follow the required format: ${p}`)}await this.browser.loadAuthState(g);break}case"ELEMENT_CHECK":{if(e.target&&!Ge(e.target))throw new Error("Element assertion with x/y is not supported yet");await this.wrapElementTargetingCommand({target:e.target,cache:e.cache?.target,action:async m=>bl({assertion:e.assertion,element:m}),options:{disableCache:n,useSelector:!!e.useSelector,iframeUrl:e.iframeUrl}});break}case"PAGE_CHECK":{await Cl({assertion:e.assertion,page:this.browser.getActivePage()});break}default:return(m=>{throw"If Typescript complains about the line below, you missed a case or break in the switch above"})(e)}return{succeedImmediately:!1,urlAfterCommand:this.browser.url()}}async getReverseMappedTarget(e,t,n){return(await this.generator.getReverseMappedDescription({browserState:e.browserState,goal:`${t}`},{disableCache:n,orgId:this.orgId})).phrase}async stopRecordMode(){this.recordAbortController?.abort(),await this.browser.clearAllCdpHighlights()}async startRecordMode(e){this.recordAbortController=new AbortController;let t=new xn({signal:this.recordAbortController.signal,...e});return await this.browser.startRecording(this.recordAbortController.signal,t),t}};var dp=2,up=2250;async function Al({socket:r,logger:e,storage:t,devicePixelRatio:n,generator:o,browserbase:s,getCpuUsage:i}){let a=r.id,l=i?.();if(l&&l>up)throw e.error({cpuUsage:l,event:"connect",args:r.handshake.query,sessionId:a},"Socket connection resource rate limit triggered"),new Error("You have reached a Momentic server under heavy load. Please refresh the page to establish a new connection. Automatic scale-up processes can take up to 3 minutes.");let c=r.handshake.query.testId,u=r.handshake.query.baseUrl,d=r.handshake.query.envName,m=r.handshake.query.viewport?JSON.parse(decodeURIComponent(r.handshake.query.viewport)):void 0,g=r.handshake.query.testMetadata?ae.parse(JSON.parse(decodeURIComponent(r.handshake.query.testMetadata))):void 0;if(!c||!u)throw new Error("Socket connection request is missing testId or baseUrl");let p=await t.getOrgId(c),h=r.handshake.query.environmentVariables?JSON.parse(decodeURIComponent(r.handshake.query.environmentVariables)):{},y=r.handshake.headers["x-forwarded-for"]?.split(",")[0],w=e.child({testId:c,baseUrl:u,orgId:p,sessionId:a});if(w.info({clientIp:y,event:"connect",args:r.handshake.query,sessionId:a},"Websocket event (connect)"),y&&O.getCurrentConnectionsByIp(y)>=dp)throw e.error({clientIp:y,sessions:O.getCurrentSessionsByIp(),...r.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(y);try{await mp({socket:r,baseUrl:u,envName:d,viewport:m,testMetadata:g,orgId:p,sessionId:a,logger:w,environmentVariables:h,clientIp:y,devicePixelRatio:n,storage:t,generator:o,browserbase:s})}catch(S){throw w.debug({err:S},"Error setting up socket session"),O.releaseCapacityByIp(y),S}return{sessionId:a,testId:c,orgId:p}}async function mp({socket:r,baseUrl:e,viewport:t,envName:n,devicePixelRatio:o,testMetadata:s,orgId:i,sessionId:a,logger:l,storage:c,generator:u,environmentVariables:d,clientIp:m}){let g={};t&&(g.viewport=t),o&&(g.deviceScaleFactor=o);let p=sr.parse(s?.advanced??{}),[h,y]=await Promise.all([He.init({baseUrl:e,userBrowserSettings:p,waitForLoad:!0,enricher:new Rn(l),timeout:p.pageLoadTimeoutMs,storage:c,logger:l,contextArgs:g,onTabsChange:(v,E)=>{r.emit("tabs",{tabs:v,activeTab:E})}}),Jt.init(i)]),w=new Qt({browser:h,generator:u,config:Er,logger:l,flagStore:y,orgId:i,storage:c}),S=ra(r,a,l),C=async()=>{clearInterval(S.timer)},T=new Te({baseUrl:e,currentUrl:w.browser.url(),variablesFromEnvironment:d,envName:n});if(!r.connected)throw await h.cleanup(),new Error("Socket not connected anymore, not proceeding with session setup");r.emit("session",{url:e,userAgent:He.USER_AGENT,viewport:await w.browser.getViewport(),sessionId:a}),O.registerSession({controller:w,context:T,sessionId:a,cleanup:C,clientIp:m})}var xl=[oa,Sa,wa,dl,na,ml,ul,Ca,Ea,Ra,Ta,ba,Aa,va];var Il=r=>{let{logger:e}=r,t=new pp(r.baseServer,{cors:{origin:"*",methods:["GET","POST"]},pingTimeout:3e4,pingInterval:6e4,maxHttpBufferSize:1e7});return t.on("connection",async n=>{let o;try{o=await Al({...r,socket:n})}catch(s){e.error({event:"connection",type:"websocket",err:s},"Failed to setup connection"),n.emit("error",{message:s instanceof Error?s.message:`${s}`}),n.disconnect(!0);return}xl.forEach(s=>gp(s,{socket:n,metadata:o,...r}))}),t};var gp=(r,e)=>{let t=r.createHandler(e),n=(...o)=>{["cloudMouseMove","cloudScroll","cloudType"].includes(r.event)||e.logger.debug({...e.metadata,event:r.event,args:o},`Websocket event (${r.event})`);let s=i=>{e.logger.error({event:r.event,type:"websocket",args:o,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=t.apply(void 0,o);i&&typeof i.catch=="function"&&i.catch(s)}catch(i){s(i)}};e.socket.on(r.event,n)};import{z as yp}from"zod";import hp from"fetch-retry";var fp=hp(global.fetch),ce=class{static API_VERSION="v1";baseURL;apiKey;constructor(e){this.baseURL=e.baseURL,this.apiKey=e.apiKey}async sendRequest(e,t){let n=await fp(`${this.baseURL}${e}`,{retries:1,retryDelay:1e3,method:"POST",body:JSON.stringify(t),headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}});if(!n.ok)throw new Error(`Request to ${e} failed with status ${n.status}: ${await n.text()}`);return n.json()}};var kt=class extends ce{constructor(e){super(e)}async getRecommendedChunks(e){let t=await this.sendRequest(`/${ce.API_VERSION}/web-agent/recommend-chunks`,e);return fi.parse(t)}async getScreenshotFromS3(e){let t=await this.sendRequest(`/${ce.API_VERSION}/s3/visual-diff-screenshot`,{url:e});return yp.string().parse(t)}async getElementLocation(e,t){let n=await this.sendRequest(`/${ce.API_VERSION}/web-agent/locate-element`,{...e,disableCache:t.disableCache});return ei.parse(n)}async getAssertionResult(e,t){let n={...e,disableCache:!!t.disableCache},o=await this.sendRequest(`/${ce.API_VERSION}/web-agent/assertion`,n);return Zs.parse(o)}async getProposedCommand(e,t){let n=await this.sendRequest(`/${ce.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:t.disableCache});return Qs.parse(n)}async getGranularGoals(e,t){let n=await this.sendRequest(`/${ce.API_VERSION}/web-agent/split-goal`,{url:e.url,goal:e.goal,disableCache:t.disableCache});return ti.parse(n)}async getReverseMappedDescription(e,t){let n=await this.sendRequest(`/${ce.API_VERSION}/web-agent/reverse-mapped-description`,{goal:e.goal,browserState:e.browserState,disableCache:t.disableCache});return ri.parse(n)}async getTextExtraction(e,t){let n={goal:e.goal,browserState:e.browserState,returnSchema:e.returnSchema,disableCache:!!t.disableCache},o=await this.sendRequest(`/${ce.API_VERSION}/web-agent/text-extraction`,n);return eo.parse(o)}async getTestResultClassification(e){let t=await this.sendRequest(`/${ce.API_VERSION}/web-agent/result-classification`,e);return Zn.parse(t)}};import{Router as Sp}from"express";function Se(r){return function(...e){let t=e[e.length-1],n=r(...e);Promise.resolve(n).catch(t)}}var Ll=Sp();Ll.get("/",Se((r,e)=>{let t=ea();e.status(200).json(t)}));var Ol=Ll;import{Router as vp}from"express";import Pl,{multistream as wp}from"pino";import bp from"pino-pretty";var jo=new Map,Cp=!0,Vo=class r{consoleLogger;ddClientToken;hostname;bindingAttributes;excludeAttributesInConsoleLogs;excludeTimestamps;disableConsoleLogs;site="https://http-intake.logs.us5.datadoghq.com/api/v2/logs";constructor({bindings:e,clientToken:t,hostname:n,excludeAttributesInConsoleLogs:o,excludeTimestamps:s,disableConsoleLogs:i}){this.ddClientToken=t,this.hostname=n,this.excludeAttributesInConsoleLogs=o,this.excludeTimestamps=s,this.disableConsoleLogs=i,this.bindingAttributes={...e,env:"production"};let a={base:o?{}:this.bindingAttributes,errorKey:"err",level:"debug",timestamp:!s};this.consoleLogger=Cp?Pl(a):Pl(a,wp([{stream:bp({colorize:!0})}]))}child(e){return new r({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,t,n,...o){if(t&&n===void 0&&(n=`${t}`,t={}),this.disableConsoleLogs||this.consoleLogger[e](e==="error"?t:{testId:t.testId,runId:t.runId},n,...o),typeof t=="object"&&t&&"err"in t&&t.err instanceof Error){let i={};for(let a of Object.getOwnPropertyNames(t.err))i[a]=t.err[a];i.name=t.err.name,t.err=i}let s=Object.assign({},this.bindingAttributes,t&&typeof t=="object"?t:{});o.length>0&&(s.args=o),(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:n||"",...s,level:e}}])});if(!i.ok)throw new Error(`Failed to log to Datadog: ${i.statusText})}`)}catch(i){this.disableConsoleLogs||this.consoleLogger.warn({obj:t,msg:n,args:o,err:i},"Failed to log to Datadog")}})()}debug(e,t,...n){this.log("debug",e,t,...n)}info(e,t,...n){this.log("info",e,t,...n)}warn(e,t,...n){this.log("warn",e,t,...n)}error(e,t,...n){this.log("error",e,t,...n)}bindings(){return this.bindingAttributes}setMinLevel(e){this.consoleLogger.level=e}enableConsoleLogs(){this.disableConsoleLogs=!1}},On=({app:r,clientToken:e,hostname:t,excludeAttributesInConsoleLogs:n,excludeTimestamps:o,disableConsoleLogs:s})=>{if(!process.env.DD_CLIENT_TOKEN&&!process.env.NEXT_PUBLIC_DD_CLIENT_TOKEN&&!e)throw new Error("Missing DD_CLIENT_TOKEN");return jo.has(r)||jo.set(r,new Vo({bindings:{app:r},hostname:t,clientToken:e||process.env.NEXT_PUBLIC_DD_CLIENT_TOKEN||process.env.DD_CLIENT_TOKEN,excludeAttributesInConsoleLogs:n,excludeTimestamps:o,disableConsoleLogs:s})),jo.get(r)};import{hostname as Tp}from"os";var we=On({app:"desktop-server",clientToken:"pubcfd7516a5c0ba852b42675cd97bee027",hostname:Tp(),excludeAttributesInConsoleLogs:!0,excludeTimestamps:!0,disableConsoleLogs:!0});var Ar=vp();Ar.get("/",Se(async(r,e)=>{let t=await qi(we);e.status(200).json(t)}));Ar.get("/metadata",Se((r,e)=>{let t=Vi();e.status(200).json(t)}));Ar.post("/",Se(async(r,e)=>{let t;try{t=qs.parse(r.body)}catch(o){e.status(400).json({error:`Invalid request body: ${o}`});return}try{ir(t.name)}catch(o){e.status(400).json({error:`Invalid module name: ${o}`});return}let n=await ji(t.name,t.steps);e.status(201).json(n)}));Ar.get("/:moduleId",Se(async(r,e)=>{if(!r.params.moduleId){e.status(400).json({error:"Missing moduleId in url path."});return}let t=await Ki(r.params.moduleId,we);if(!t){e.status(404).json({error:"Module not found."});return}e.json(t)}));var Ml=Ar;import{Router as Ep}from"express";import{existsSync as Rp}from"fs";import{join as Mn}from"path";import{v4 as Ap}from"uuid";var Ko="https://api.momentic.ai",Nl=r=>{Ko=r},xr=()=>Ko,Zt,qo,kl=async r=>{if(Zt)return Zt;let e=new te({baseURL:Ko,apiKey:r});try{return Zt=await e.getOrgId(),qo=r,Zt}catch(t){throw new Error(`Error checking API key against server: ${t}`)}},Pn=()=>{if(!Zt)throw new Error("Your organization ID appears invalid. Please contact Momentic Support.");return Zt},_l=()=>{if(!qo)throw new Error("Your API key appears invalid. Please contact Momentic Support.");return qo};var _t=Ep();_t.get("/",Se((r,e)=>{let t=fr(Y,we);e.status(200).json(t)}));_t.post("/",Se(async(r,e)=>{let t;try{t=Vs.parse(r.body)}catch(a){e.status(400).json({error:`Invalid request body: ${a}`});return}try{ir(t.name)}catch(a){e.status(400).json({error:a.message});return}let o={id:Ap(),name:t.name,baseUrl:t.baseUrl,schemaVersion:ge,advanced:{disableAICaching:!1,viewport:t.viewport??je},retries:0,steps:[]};t.environment&&(o.envs=[{name:t.environment,default:!0}]);let s=await Ji(o,t.name),i={...o,testPath:s};e.status(201).json(i)}));_t.get("/:testPath",Se(async(r,e)=>{if(!r.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let t=Mn(Y,r.params.testPath);if(!Rp(t)){e.status(400).json({error:`Test not found at path: ${t}`});return}let n=await hn(t,mt,we);e.status(200).json(n)}));_t.patch("/:testPath/metadata",Se(async(r,e)=>{if(!r.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let t;try{t=js.parse(r.body)}catch(o){e.status(400).json({error:`Invalid request body: ${o}`});return}let n=Mn(Y,r.params.testPath);gn(t,n),e.status(201).json({message:"ok"})}));_t.patch("/:testPath",Se(async(r,e)=>{if(!r.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let t;try{t=Gs.parse(r.body)}catch(c){e.status(400).json({error:`Invalid request body: ${c}`});return}let n=Mn(Y,r.params.testPath),o;try{o=Qi(n)}catch(c){e.status(400).json({error:`Existing test file on disk is invalid: ${c}`});return}let{stepsToSave:s,moduleUpdates:i,cachesToSave:a}=await Be({steps:t.steps,cacheCreationParams:{testId:o.id,orgId:Pn()}});i.forEach(c=>Gi({...c,schemaVersion:ge})),gn({schemaVersion:ge,steps:s},n),await new te({apiKey:_l(),baseURL:xr()}).updateStepCaches({entries:a,testId:o.id}),e.status(201).json({message:"ok"})}));_t.patch("/:testPath/environments",Se(async(r,e)=>{if(!r.params.testPath){e.status(400).json({error:"Missing testPath in url path."});return}let t;try{t=Ks.parse(r.body)}catch(o){e.status(400).json({error:`Invalid request body: ${o}`});return}let n=Mn(Y,r.params.testPath);gn({envs:t.defaultEnv?[{name:t.defaultEnv,default:!0}]:[]},n),e.status(201).json({message:"ok"})}));var Dl=_t;async function Fl(r){let{momenticServerUrl:e,apiKey:t,serverPort:n,appPort:o,staticDir:s,devicePixelRatio:i}=r;if(!Lp(Y)||!Op(Y).isDirectory()){let h=Np.isAbsolute(Y);throw new Error(`Root folder ${Y} does not exist${h?"":` in the current directory ("${process.cwd()}")`}. Please run \`npx momentic init\` if you wish to setup Momentic here for the first time.`)}e&&Nl(e),we.debug(r,"Initializing desktop server");let a=await kl(t);we.debug({orgId:a},"Checking API key successful");let c=`http://localhost:${n}`;o&&(c=`http://localhost:${o}`);let u=kp(s);await new Promise(h=>{u.listen(n,()=>{we.info(`Server is running at http://localhost:${n}`),h()})});let m=new kt({baseURL:xr(),apiKey:t}),g=new te({baseURL:xr(),apiKey:t}),p=new qt(g,Pn());Il({baseServer:u,generator:m,storage:p,logger:we,devicePixelRatio:i}),await Mp(c)}function kp(r){let e=Yo();e.use(Ip()),e.use(xp.json({limit:"50mb"}));let t=Yo.Router();if(t.use("/tests",Dl),t.use("/modules",Ml),t.use("/environments",Ol),e.use("/api",t),e.use((o,s,i)=>{o.path!=="/healthcheck"&&we.debug({url:o.url,path:o.path,query:o.query,method:o.method,body:o.body,headers:o.rawHeaders,client:o.ip},"Received desktop-server request"),s.on("close",()=>{s.statusCode>=400&&we.error({url:o.url,method:o.method,statusCode:s.statusCode},"Request completed in error")}),i()}),e.use((o,s,i,a)=>{o instanceof Error&&o.message.includes("BadRequestError: request aborted")||(we.error({stack:o.stack,msg:o.message,url:s.url,method:s.method},"Unhandled exception leading to 500 on desktop-server"),i.status(500).send("Internal Server Error"),a(o))}),r){let o=Yo.static(r);e.use("/",o),e.use("*",o)}return Pp.createServer(e)}import{existsSync as Mg}from"fs";import yc,{dirname as Ng,join as kg}from"path";import{chdir as _g}from"process";import{fileURLToPath as Dg}from"url";var Ul="0.0.114";import Ir from"chalk";var Xe=(...r)=>{q(Ir.blue(...r))};var Je=(...r)=>{q(Ir.dim(...r))};var me=(...r)=>{q(Ir.green(...r))},q=(...r)=>{console.log(...r)},re=(...r)=>{console.error(Ir.yellow(...r))},L=(...r)=>{console.error(Ir.red(...r))};import{hostname as Dp}from"os";var I=On({app:"cli",clientToken:"pub7eb923f18fb3f1d42ac5eba8c5ea13a5",hostname:Dp(),excludeAttributesInConsoleLogs:!0,excludeTimestamps:!0,disableConsoleLogs:!0});import{execSync as Fp}from"child_process";import{platform as Up}from"os";function Xo(){return zl()?(Xe("Setting device pixel ratio to 2 automatically since a Mac OS Retina screen was detected."),Xe("If you are using a low pixel-density monitor, you should manually set --pixel-ratio to 1 to avoid incorrect viewport calculations."),Xe("Confirm your device's pixel-ratio at https://www.mydevice.io."),2):(Xe("Setting device pixel ratio to 1."),Xe("If you are using Momentic on a high-pixel density (HiDPI) monitor, relaunch with the --pixel-ratio option to avoid incorrect viewport calculations"),Xe("Confirm your device's pixel-ratio at https://www.mydevice.io."),1)}function zl(){return Up()==="darwin"&&Fp("system_profiler SPDisplaysDataType").toString().includes("Retina")}function Jo(r){zl()&&r===1&&(re("If you are using Momentic on a Retina screen, relaunch with the --pixel-ratio option to avoid incorrect viewport calculations."),re("Confirm your device's pixel-ratio at https://www.mydevice.io."))}import{createHash as jl}from"crypto";import{existsSync as Vl,readFileSync as ns,writeFileSync as ql}from"fs";import{join as Kl}from"path";import{parse as eg}from"yaml";import{existsSync as es,mkdirSync as $l,readdirSync as Kp,statSync as Hl}from"fs";import{homedir as Wl}from"os";import{join as Qe,resolve as Yp}from"path";import{existsSync as zp,statSync as Bp}from"fs";var Nn=!!process.env.CI||!process.stdout.isTTY;function ht(r){try{return zp(r)&&Bp(r).isDirectory()}catch(e){return L({err:e},`Error reading path ${r} during directory existence check`),!1}}import{registry as Qo}from"playwright-core/lib/server";function $p(r){let e=[],t=[];for(let n of r){let o=Qo.findExecutable(n);!o||o.installType==="none"?e.push(n):t.push(o)}return t}async function kn(){let r=$p(["chromium"]);await Qo.installDeps(r,!1),await Qo.install(r,!1)}import{statSync as Hp}from"fs";import{existsSync as Wp,mkdirSync as Gp}from"fs";import{dirname as jp}from"path";import Vp from"readline/promises";var Zo=!1,qp=(()=>{try{return Hp("/.dockerenv"),!0}catch{return!1}})();async function _e(r,e){if(Nn||Zo||qp)return!0;I.flush(),await new Promise(i=>setTimeout(i,500));let t=Vp.createInterface({input:process.stdin,output:process.stdout}),n=r.split("."),o;if(n.length===1)o=r;else{let i=`${n.slice(0,n.length-1).join(". ").trim()}.`;e?re(i):q(i),o=n[n.length-1].trim()}let s=await t.question(`${o} ('y' for yes / n for no / 'A' to accept all) `);return t.close(),s==="A"?(Zo=!0,setTimeout(()=>{Zo=!1},3e3),!0):s.toLowerCase()==="y"}async function Lr(r){let e=jp(r);return ht(e)?Wp(r)?_e(`File '${Bl(r)}' already exists. Overwrite existing content?`,!0):!0:await _e(`Directory '${Bl(e)}' doesn't exist. Create it now?`,!0)?(Gp(e,{recursive:!0}),!0):!1}function Bl(r){return r.replace(/(\s+)/g,"\\$1")}var Xp=[lt,tn,Wt],Ze=Zp(),Dt=Qe(Ze,lt),rs=Qe(Ze,tn),et=Qe(Ze,Wt),ts=Qe(Wl(),"momentic",uo),Gl=[Dt,rs,et];async function tt(r){let e=process.versions.node,t=parseInt(e.split(".")[0]);(isNaN(t)||t<20)&&(L(`Node.js version 20 or higher is required to run the Momentic CLI. Detected: ${process.versions.node}.`),process.exit(1));let n=await r.client.getOrgId();return await kn(),await Qp(r),n}async function Jp(r){ht(Ze)||!r&&!await _e(`The root '${at}' folder was not found in the current directory. You will need to invoke the Momentic CLI from this directory in the future. Proceed?`)&&(L("Setup cancelled."),process.exit(1)),ht(ts)||(!r&&!await _e("The Chrome cache folder was not found in your home directory. Proceed with creation?")&&(L("Setup cancelled."),process.exit(1)),$l(ts,{recursive:!0}));for(let e of Gl)ht(e)||(!r&&!await _e(`'${e}' folder was not found in the current directory. Create it now?`)&&(L("Setup cancelled."),process.exit(1)),$l(e,{recursive:!0}))}function Or(r,e,t=new Set){for(let n of r){let o=Yp(n),s=!1;try{s=es(o)&&Hl(o).isDirectory()}catch(i){I.error({err:i},`Error reading path ${o} during collect paths`)}if(o&&s){let i=Kp(o).map(a=>Qe(o,a));Or(i,e,t);continue}if(o.endsWith(".yaml")){try{if(!es(o)||!Hl(o).isFile()){I.error(`File not found or unreadable: ${o}`);continue}}catch(i){I.error({err:i},`Error reading file ${o} during collect paths`);continue}if(!e(o))continue;t.add(o)}}return t}async function Qp({client:r,createFolders:e,skipPrompts:t,pullEnvs:n}){if(Wl()||(L("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 Jp(t);else for(let o of[Ze,ts,...Gl])!ht(o)&&!Nn&&(L(`The '${o}' 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.`),L(`The current working directory is: ${process.cwd()}`),process.exit(1));n&&(!t&&!await _e("Pull environments from Momentic Cloud? Environment definitions may be required to run tests locally.")&&(L("Setup cancelled."),process.exit(1)),await _n({client:r,all:!0,skipPrompts:t}))}function Zp(){let r=n=>es(Qe(n,at))&&Xp.some(o=>ht(Qe(n,at,o))),e=process.cwd();for(;e!=="/"&&!r(e);)e=Qe(e,"..");let t;return r(e)?t=e:t=process.cwd(),t!==process.cwd()?re(`Current working directory does not contain a ${at} folder, treating ${t} as the Momentic root folder instead`):I.info(`Identified ${t} as the Momentic root folder`),Qe(t,at)}async function _n({envNames:r,client:e,all:t,skipPrompts:n}){let o=await e.getAllEnvironments();r&&!t&&(o=o.filter(a=>r?.includes(a.name))),Vl(et)||(L(`${et} does not exist in the current directory. Please run 'npx momentic@latest init' first.`),process.exit(1));let s=[],i=[];for(let a of o){let l=Kl(et,`${a.name}.yaml`);if(!n&&!await Lr(l))return;if(!Vl(l))ql(l,wo(a)),s.push(l);else{let c=jl("sha256").update(ns(l)).digest("hex"),u=wo(a),d=jl("sha256").update(u).digest("hex");c!==d&&(ql(l,u),i.push(l))}}me("Pulled environments successfully!"),s.length&&(me("Created:"),s.forEach(a=>me(" ",a))),i.length&&(me("Updated:"),i.forEach(a=>me(" ",a)))}function tg(r){return r.endsWith(".yaml")?ns(r,"utf8").includes("momentic/environment")?!0:(re(`Skipping YAML that is not a Momentic environment: ${r}`),!1):!1}async function Yl({client:r,names:e,all:t,yes:n}){let o;t?o=[et]:o=e.map(a=>Kl(et,`${a.toLowerCase()}.yaml`));let s=Or(o,tg);q(`Found ${s.size} environments to push:`),s.forEach(a=>q(" ",a));let i=[];for(let a of s){let l=eg(ns(a,"utf-8"));try{let c=Ce.parse(l);i.push(c)}catch(c){L(`${a} failed to parse as a valid environment file.`),L(c),process.exit(1)}}!n&&!await _e("Pushing environments can overwrite variables on cloud and instantly affect scheduled runs. Continue?",!0)||(await r.updateEnvironments(i),me("Pushed environments successfully!"))}import{Argument as Pr,Option as Me}from"commander";import{validateHeaderValue as rg}from"http";import{cpus as Xl}from"os";import{z as ng}from"zod";var rt=new Me("--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 Me("--server <server>","Momentic server to use. Leave unchanged unless using Momentic on-premise.").default("https://api.momentic.ai"),ot=new Me("-y, --yes","Skip all confirmation prompts.").env("CI"),os=new Me("-w, --wait","Wait for tests to finish running before exiting. Only applicable when running tests remotely").implies({remote:!0}),ss=new Me("--wait-timeout-seconds <waitTimeoutSeconds>","The maximum number of seconds to wait for tests to complete. Only applicable when the --wait option is specified.").argParser(r=>parseInt(r,10)),is=new Me("--custom-headers <customHeaders...>","Specify custom headers in the form HEADER=VALUE to be sent with each request during the test. Multiple entries can be provided.");function as(r){if(r===void 0)return;let e={};for(let t of r){let n=t.indexOf("=");if(n===-1)throw new Error(`Header value pair does not contain '=': ${t}`);let o=t.slice(0,n),s=t.slice(n+1);rg(o,s),e[o]=s}return e}var ls=new Me("--no-report","Skip reporting test results to Momentic Cloud when running with the --local flag.").implies({local:!0}),cs=new Me("--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(r=>parseInt(r,10)),ds=new Me("--env <env>","Name of the environment to use when running tests."),us=new Me("--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."),ms=new Me("-a, --all","Select all tests in your organization from Momentic Cloud. Cannot be used together with <tests> arguments."),ps=new Me("-a, --all","Select all environments in your organization from Momentic Cloud. Cannot be used together with <paths> arguments."),Jl=new Pr("<tests...>",`One or more test paths to pull from Momentic Cloud.
|
|
1608
1608
|
|
|
1609
1609
|
A test path is a lowercased version of your test name where spaces are replaced with dashes: 'npx momentic pull hello-world'.`).argOptional(),Ql=new Pr("<tests...>",`One or more test identifiers.
|
|
1610
1610
|
|