@devassure/cli 1.1.1 → 1.1.2
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/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -124,7 +124,7 @@ To free the port: lsof -ti:${i} | xargs kill`):u}}throw new Error("Failed to sta
|
|
|
124
124
|
</div>
|
|
125
125
|
</body>
|
|
126
126
|
</html>
|
|
127
|
-
`),B.close(),a({authCode:C.code,redirectUri:u})):(h.writeHead(400),h.end("Invalid callback"));});B.listen(t,async()=>{let l=new URL(g.authorizationUrl);l.searchParams.set("response_type","code"),l.searchParams.set("client_id",g.clientId),l.searchParams.set("redirect_uri",u),l.searchParams.set("state",e),l.searchParams.set("code_challenge",r),l.searchParams.set("code_challenge_method","S256"),l.searchParams.set("scope","offline_access"),l.searchParams.set("utm_source",i||"agent");try{await this.clientPort.openExternal(l.toString());}catch(h){B.close(),c(new Error(`Failed to open browser: ${h instanceof Error?h.message:String(h)}`));}}),B.on("error",l=>{c(l);}),setTimeout(()=>{B.close(),c(new Error("Authentication timeout. Please try again."));},300*1e3);})}async exchangeCodeForTokens(t,e,A){let r=Mn(),s=Buffer.from(`${r.clientId}:${r.clientSecret}`).toString("base64"),n=await fetch(r.tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Authorization:`Basic ${s}`},body:new URLSearchParams({grant_type:"authorization_code",code:t,redirect_uri:A,code_verifier:e})});if(!n.ok){let o=await n.text();throw new Error(`Token exchange failed: ${n.status} ${o}`)}let i=await n.json();if(!i.access_token||!i.refresh_token)throw new Error("Invalid token response");return i}generateRandomString(t){let e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~",A="",r=new Uint8Array(t);Ft.getRandomValues(r);for(let s=0;s<t;s++)A+=e[r[s]%e.length];return A}async generateCodeChallenge(t){let A=new TextEncoder().encode(t),r=await Ft.subtle.digest("SHA-256",A);return btoa(String.fromCharCode(...new Uint8Array(r))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}};async function Xp(t){let e=process.env.DEVASSURE_TOKEN;if(e&&e.trim()!=="")try{return await t.jobPing(e,"api_key_env_var"),{success:!0,message:""}}catch{return {success:false,message:"auth token in Environment variable DEVASSURE_TOKEN is invalid, add valid token or remove the variable and run `devassure login` to login."}}let A=await Te.getAuthToken();if(A)try{return await t.jobPing(A,"token"),{success:!0,message:""}}catch{return await Te.clearAuthToken(),{success:false,message:"auth-token added is invalid. Please add new token using `devassure add-token` command or run `devassure login` to login from browser."}}if(await Te.getAccessToken())try{return await t.ensureAuthenticated(),{success:!0,message:""}}catch(s){return {success:false,message:s?.message||"Not Authenticated! Please login to continue."}}return {success:false,message:"Not Authenticated! Please login to continue."}}async function Dv(t){try{return {Authorization:`Bearer ${await t.ensureAuthenticated()}`}}catch{return null}}function Sv(t){return {Authorization:`Bearer ${t}`}}async function tu(t,e,A={},r,s,n,i,o){let c=pv().baseUrl,g=new URL(e,c);i&&Object.entries(i).forEach(([I,Q])=>{g.searchParams.set(I,Q);});let u=g.toString(),B=n==="no_auth",l=B?void 0:n||await Dv(o);if(!B&&!l)return {exception:"unauthorized",status:401,statusText:"Unauthorized",headers:new Headers};let h={method:t.toUpperCase(),headers:{"Content-Type":"application/json",...l,...r?.headers},...r};(t==="post"||t==="put")&&A&&(h.body=JSON.stringify(A));let d;try{d=await fetch(u,h);}catch{return {exception:"connection_error",status:0,statusText:"Connection Error",headers:new Headers}}if(d.status===401&&s&&!B)try{let I=await o.refreshToken();if(I?.access_token){let Q={...r,headers:{"Content-Type":"application/json",...r?.headers}};return await tu(t,e,A,Q,!1,Sv(I.access_token),i,o)}}catch{return await Te.clearTokens(),{exception:"unauthorized",status:401,statusText:"Unauthorized - Session expired",headers:d.headers}}let C;try{let I=d.headers.get("content-type");I&&I.includes("application/json")?C=await d.json():C=await d.text();}catch{}return {data:C,status:d.status,statusText:d.statusText,headers:d.headers}}var jp=class{constructor(){this.authService=null,this.webAppCallFn=null;}initialize(t,e){this.authService=t,this.webAppCallFn=e;}async request(t,e={}){if(!this.webAppCallFn||!this.authService)throw new Error("ApiService not initialized. Call initialize() first.");let A=e.method?.toLowerCase()||"get",r;if(e.body)if(typeof e.body=="string")try{r=JSON.parse(e.body);}catch{r=e.body;}else r=e.body;let s=await this.webAppCallFn(A,t,r,{...e,refreshTokenOn401:!e.skipAuth,authHeader:e.skipAuth?"no_auth":void 0},!e.skipAuth,e.skipAuth?"no_auth":void 0,void 0);if(s.exception)throw s.exception==="unauthorized"?new Error("Not Authenticated!. Please login to continue!"):s.exception==="connection_error"?new Error("Connection error! Please check your network connection."):new Error(`API request failed: ${s.exception}`);if(!s.data&&s.status!==204)throw new Error(`API request failed: ${s.status} ${s.statusText}`);return s.data}async get(t,e){return this.request(t,{...e,method:"GET"})}async post(t,e,A){return this.request(t,{...A,method:"POST",body:e?JSON.stringify(e):void 0})}async put(t,e,A){return this.request(t,{...A,method:"PUT",body:e?JSON.stringify(e):void 0})}async delete(t,e){return this.request(t,{...e,method:"DELETE"})}},$p="normal",Kg=null;function Rv(t){Kg=t,em();}function em(){Kg&&($p=Kg.getLogLevel());}function Fv(){return em(),$p==="debug"}var x={debug:(...t)=>{Fv()&&console.log("[DEBUG]",...t);}},kv=class{constructor(){this.clientPort=null,this.pollTimer=null,this.pollInterval=500,this.isPolling=false,this.isPaused=false,this.state=null,this.taskFeedItems=new Map,this.scenarioFeedItems=new Map,this.sessionTracking=null,this.completedTasks=new Set,this.completedScenarios=new Set,this.sessionErrorFeedItemCreated=false,this.sessionWaitFeedItemCreated=false,this.sessionWaitFeedItemUpdatedToSuccess=false,this.sessionNotFoundStartTime=null,this.resumeCommand=false,this.resumed=false,this.sessionWaitTimeout=120*1e3,this.maxPollTime=3600*1e3,this.startTime=0,this.resolveCallback=null,this.rejectCallback=null,this.terminationRequested=false,this.activePrompts=new Map,this.pollCallCounter=0;}initialize(t){this.clientPort=t,Rv(t);}async start(t,e){if(!this.clientPort)throw new Error("PollingService not initialized. Call initialize() first.");return x.debug(`PollingService.start() called for session ${t}`),await L.initialize(),this.state={sessionId:t,feedItems:new Map,progress:null,isPolling:false,isCompleted:false},this.startTime=Date.now(),this.isPolling=false,this.isPaused=false,this.terminationRequested=false,this.sessionWaitFeedItemCreated=false,this.sessionWaitFeedItemUpdatedToSuccess=false,this.sessionNotFoundStartTime=null,this.resumeCommand=!!e,this.resumed=false,x.debug(`PollingService: Starting polling for session ${t} with interval ${this.pollInterval}ms`),new Promise((A,r)=>{this.resolveCallback=A,this.rejectCallback=r,this.pollTimer=setInterval(()=>{this.poll().catch(s=>{x.debug(`Error in poll() from interval: ${s instanceof Error?s.message:String(s)}`);});},this.pollInterval),this.poll().catch(s=>{x.debug(`Error in initial poll(): ${s instanceof Error?s.message:String(s)}`);});})}async poll(){if(++this.pollCallCounter,!this.clientPort||!this.state||this.isPaused){x.debug(`poll() - clientPort or state or isPaused is false client port = ${this.clientPort?"true":"false"}, state = ${this.state?"true":"false"}, isPaused = ${this.isPaused?"true":"false"}`);return}if(!this.isPolling){if(this.isPolling=true,this.terminationRequested){this.isPolling=false;return}if(!L){this.isPolling=false;return}try{if(Date.now()-this.startTime>this.maxPollTime){if(x.debug("poll() - max poll time exceeded"),await this.stop(),this.terminationRequested)return;this.rejectCallback?.(new Error("Session execution timed out"));return}let t;try{t=await L.sessionRepository.getById(this.state.sessionId);}catch(o){throw x.debug(`poll() - error getting session from database: ${o instanceof Error?o.message:String(o)}`),o}if(!t||this.resumeCommand&&t.status==="cancelled"&&!this.resumed){if(!this.sessionWaitFeedItemCreated){let o=`session-wait-${this.state.sessionId}`,a={id:o,name:"Booting up DevAssure Agent",status:"running",stateIndicators:[{status:"running",indicator:"blinkingHexagon",color:"cyan"},{status:"success",indicator:"hexagon",color:"green"}]};this.state.feedItems.set(o,a),this.clientPort.onFeedItem(a),this.sessionWaitFeedItemCreated=!0;}if(this.sessionNotFoundStartTime===null&&(this.sessionNotFoundStartTime=Date.now()),!this.resumeCommand&&Date.now()-this.sessionNotFoundStartTime>this.sessionWaitTimeout){if(await this.stop(),this.terminationRequested)return;this.rejectCallback?.(new Error("Session not found in database after waiting 2 minutes"));return}this.isPolling=!1;return}else this.resumeCommand&&(this.resumed=!0);this.sessionNotFoundStartTime=null;let e=`session-wait-${this.state.sessionId}`,A=this.state.feedItems.get(e);if(A&&!this.sessionWaitFeedItemUpdatedToSuccess){this.sessionWaitFeedItemUpdatedToSuccess=!0;let o={...A,status:"success",currentMessages:[k("Agent is ready!",{icon:"blinkingHexagon",iconColor:"grey",textColor:"grey"})]};this.state.feedItems.set(e,o),this.clientPort.onFeedItem(o),this.resumed=!0,await new Promise(a=>setTimeout(a,1e3));}this.sessionTracking||(this.sessionTracking=this.initializeSessionTracking(t.status),x.debug(`Session ${this.state.sessionId} tracking initialized with status ${t.status}`));let r=t.status,s=this.isSessionCompleted(r);if(x.debug(`Session ${this.state.sessionId} status check: ${r}, isCompleted: ${s}, wasActive: ${this.sessionTracking.wasActive}, lastStatus: ${this.sessionTracking.lastStatus}`),s&&!this.sessionTracking.wasActive){if(x.debug(`Session ${this.state.sessionId} is already completed (was never active), handling completion immediately`),await this.handleSessionCompletion(this.state.sessionId),this.sessionTracking.wasActive=!1,this.sessionTracking.lastStatus=t.status,x.debug(`Session ${this.state.sessionId} is completed, all data read and emitted, stopping polling`),await this.stop(),this.terminationRequested)return;this.state.isCompleted=!0,this.resolveCallback?.();return}await this.handleTasks(this.state.sessionId),await this.handleScenarios(this.state.sessionId),await this.updateProgress(this.state.sessionId),this.sessionTracking.lastStatus!==t.status&&this.sessionTracking.lastStatus&&x.debug(`Session ${this.state.sessionId} status changed: ${this.sessionTracking.lastStatus} -> ${t.status}`);let i=this.sessionTracking.wasActive&&s;if(await this.handleSessionErrors(this.state.sessionId),await this.handleSessionMessages(this.state.sessionId,t.title??void 0,t.status),i?(x.debug(`Session ${this.state.sessionId} transitioned from active to completed`),await this.handleSessionCompletion(this.state.sessionId),this.sessionTracking.wasActive=!1):s?(x.debug(`Session ${this.state.sessionId} is completed (was not active before)`),await this.handleSessionCompletion(this.state.sessionId),this.sessionTracking.wasActive=!1):(this.sessionTracking.wasActive||x.debug(`Session ${this.state.sessionId} is now active`),this.sessionTracking.wasActive=!0),this.sessionTracking.lastStatus=t.status,!s){let o={sessionId:this.state.sessionId,status:t.status,feedItems:Array.from(this.state.feedItems.values()),progress:this.state.progress||void 0,timestamp:new Date};this.clientPort.onSnapshot(o);}if(s){if(x.debug(`Session ${this.state.sessionId} is completed, all data read and emitted, stopping polling`),await this.stop(),this.terminationRequested)return;this.state.isCompleted=!0,this.resolveCallback?.();return}this.isPolling=!1;}catch(t){if(x.debug(`poll() error: ${t instanceof Error?t.message:String(t)}`),await this.stop(),this.terminationRequested)return;this.rejectCallback?.(t instanceof Error?t:new Error(String(t)));}}}async handleTasks(t){try{let e=await L.taskRepository.getBySessionId(t),A=new Rt(L.taskRepository);for(let r of e){if(r.name.startsWith("execute_scenario_"))continue;let s=`task-${r.id}`,n=Rt.isTaskActive(r.status),i=Rt.isTaskCompleted(r.status);if(i&&this.completedTasks.has(r.id))continue;let o=this.taskFeedItems.get(r.id);o||(o=this.initializeTaskTracking(r.id),this.taskFeedItems.set(r.id,o),x.debug(`Task ${r.id} (${r.name}) tracking initialized`)),o.wasActive&&i&&x.debug(`Task ${r.id} (${r.name}) transitioned from active to completed`);let{taskStepIds:c,messageIds:g}=await this.collectTaskSubItemIds(r.id),u=await A.getPendingUserInputTaskStep(r.id),B=await L.taskRepository.getValidatingUserInputTaskStep(r.id),l=o.lastStatus!==r.status;l&&o.lastStatus&&x.debug(`Task ${r.id} (${r.name}) status changed: ${o.lastStatus} -> ${r.status}`);let h=this.detectSubItemChanges(c,o.lastTaskStepIds);h&&x.debug(`Task ${r.id} (${r.name}) task steps changed: ${c.size} current, ${o.lastTaskStepIds.size} previous`);let d=this.detectSubItemChanges(g,o.lastMessageIds);d&&x.debug(`Task ${r.id} (${r.name}) messages changed: ${g.size} current, ${o.lastMessageIds.size} previous`);let C=u?.id!==o.lastPendingUserInputStepId;C&&x.debug(`Task ${r.id} (${r.name}) pending user input step changed: ${u?.id||"none"} -> ${o.lastPendingUserInputStepId||"none"}`);let I=B?.id!==o.lastValidatingUserInputStepId;if(I&&x.debug(`Task ${r.id} (${r.name}) validating user input step changed: ${B?.id||"none"} -> ${o.lastValidatingUserInputStepId||"none"}`),n){if(o.wasActive=!0,u){let p=`task-${r.id}-step-${u.id}`;if(!this.activePrompts.has(p)&&(C||!o.lastPendingUserInputStepId)){let y=Rt.getPromptTypeFromTaskStepData(u.data);if(y){this.pause(),x.debug(`Polling paused for user input on task ${r.id} (${r.name}), step ${u.id}`);let b=new AbortController;this.activePrompts.set(p,b);let U=await A.getTaskMessages(r.id),v=Rt.mapTaskStatusToFeedStatus(r.status),G=Rt.createFeedItem(r,U);G.onInput=!0,G.currentPrompt={message:u.label||"Please provide input:",type:y==="text"?"text":"confirm",placeholder:y==="text"?"Enter value...":void 0},this.state.feedItems.set(s,G),this.clientPort.onFeedItem(G);try{let V=await this.clientPort.userPrompt({type:y==="text"?"text":"confirm",message:u.label||"Please provide input:",placeholder:y==="text"?"Enter value...":void 0},b.signal);if(V.ok&&V.value!==void 0){let z=(typeof V.value=="boolean",String(V.value));await L.taskRepository.updateTaskStepUserInput(u.id,z),x.debug(`User input received for task ${r.id} (${r.name}), step ${u.id}, updated DB with status=validating`);}else x.debug(`User input cancelled for task ${r.id} (${r.name}), step ${u.id}`);}catch(V){x.debug(`User input prompt failed for task ${r.id} (${r.name}), step ${u.id}: ${V instanceof Error?V.message:String(V)}`);}finally{this.activePrompts.delete(p);let V=this.state.feedItems.get(s);if(V){let z={...V};delete z.currentPrompt,z.onInput=!1,this.state.feedItems.set(s,z),this.clientPort.onFeedItem(z);}this.resume(),x.debug(`Polling resumed after user input for task ${r.id} (${r.name})`);}}}}let Q=await A.getTaskMessages(r.id),E=Rt.mapTaskStatusToFeedStatus(r.status),f=Rt.createFeedItem(r,Q);u&&!this.activePrompts.has(`task-${r.id}-step-${u.id}`)&&(f.onInput=!0),(!this.state.feedItems.has(s)||l||h||d||C||I)&&(this.state.feedItems.has(s)?x.debug(`Task ${r.id} (${r.name}) feed item updated`):x.debug(`Task ${r.id} (${r.name}) feed item created`),this.state.feedItems.set(s,f),this.clientPort.onFeedItem(f)),o.lastStatus=r.status,o.lastUpdatedAt=new Date().toISOString(),o.lastPendingUserInputStepId=u?.id,o.lastValidatingUserInputStepId=B?.id,o.lastTaskStepIds=c,o.lastMessageIds=g;}else if(i&&!this.completedTasks.has(r.id)){x.debug(`Task ${r.id} (${r.name}) marked as completed`),this.completedTasks.add(r.id),o.wasActive=!1;let Q=await A.getTaskMessages(r.id),E=Rt.createFeedItem(r,Q);this.state.feedItems.set(s,E),this.clientPort.onFeedItem(E),this.state.feedItems.delete(s);}else o.wasActive=!1;}}catch(e){x.debug(`Error handling tasks: ${e}`);}}async handleScenarios(t){try{let e=await L.scenarioRepository.getBySessionId(t),A=new bn(L.scenarioRepository);for(let r of e){let s=`scenario-${r.id}`,n=bn.isScenarioActive(r.status),i=bn.isScenarioCompleted(r.status);if(r.status==="new"||i&&this.completedScenarios.has(r.id))continue;let o=this.scenarioFeedItems.get(r.id);o||(o=this.initializeScenarioTracking(r.id),this.scenarioFeedItems.set(r.id,o),x.debug(`Scenario ${r.id} (${r.title}) tracking initialized`));let a=o.wasActive&&i;a&&x.debug(`Scenario ${r.id} (${r.title}) transitioned from active to completed`),a&&await this.handleScenarioCompletion(r.id);let c=await this.collectScenarioSubItemIds(r.id,r.browser_session_id),g=o.lastStatus!==r.status;g&&o.lastStatus&&x.debug(`Scenario ${r.id} (${r.title}) status changed: ${o.lastStatus} -> ${r.status}`);let u=this.detectSubItemChanges(c.agentStepIds,o.lastAgentStepIds);u&&x.debug(`Scenario ${r.id} (${r.title}) agent steps changed: ${c.agentStepIds.size} current, ${o.lastAgentStepIds.size} previous`);let B=!1;for(let[C,I]of c.agentStepActionIds.entries()){let Q=o.lastAgentStepActionIds.get(C)||new Set;if(this.detectSubItemChanges(I,Q)){B=!0,x.debug(`Scenario ${r.id} (${r.title}) agent step actions changed for step ${C}: ${I.size} current, ${Q.size} previous`);break}}let l=!1;for(let[C,I]of c.agentStepValidationIds.entries()){let Q=o.lastAgentStepValidationIds.get(C)||new Set;if(this.detectSubItemChanges(I,Q)){l=!0,x.debug(`Scenario ${r.id} (${r.title}) agent step validations changed for step ${C}: ${I.size} current, ${Q.size} previous`);break}}let h=this.detectSubItemChanges(c.passedValidationIds,o.lastPassedValidationIds);h&&x.debug(`Scenario ${r.id} (${r.title}) passed validations changed: ${c.passedValidationIds.size} current, ${o.lastPassedValidationIds.size} previous`);let d=this.detectSubItemChanges(c.errorIds,o.lastErrorIds);if(d&&x.debug(`Scenario ${r.id} (${r.title}) errors changed: ${c.errorIds.size} current, ${o.lastErrorIds.size} previous`),n){o.wasActive=!0;let C=await A.getScenarioMessages(r),I=bn.createFeedItem(r,C);(!this.state.feedItems.has(s)||g||u||B||l||h||d)&&(this.state.feedItems.has(s)?x.debug(`Scenario ${r.id} (${r.title}) feed item updated`):x.debug(`Scenario ${r.id} (${r.title}) feed item created`),this.state.feedItems.set(s,I),this.clientPort.onFeedItem(I)),o.lastStatus=r.status,o.lastUpdatedAt=new Date().toISOString(),o.lastAgentStepIds=c.agentStepIds,o.lastAgentStepActionIds=c.agentStepActionIds,o.lastAgentStepValidationIds=c.agentStepValidationIds,o.lastPassedValidationIds=c.passedValidationIds,o.lastErrorIds=c.errorIds;}else i&&!this.completedScenarios.has(r.id)?(x.debug(`Scenario ${r.id} (${r.title}) marked as completed`),this.completedScenarios.add(r.id),o.wasActive=!1,this.state.feedItems.delete(s)):o.wasActive=!1;}}catch(e){x.debug(`Error handling scenarios: ${e}`);}}async handleScenarioCompletion(t){try{let A=(await L.scenarioRepository.getBySessionId(this.state.sessionId)).find(c=>c.id===t);if(!A)return;let r=await L.analysisRepository.getLatestScenarioAnalysis(t);if(!r){x.debug(`Scenario ${t} completion: no analysis found`);return}x.debug(`Scenario ${t} (${A.title}) completion: analysis found, score=${r.score}`);let s=`scenario-${A.id}`,n=this.state.feedItems.get(s);if(!n)return;let i=[];i.push({message:`Score: ${r.score==null?"-":r.score} / 100`,styles:{icon:"\u{1F4CA}",textColor:this.getScoreColor(r.score||-1)}}),r.analysis_summary&&i.push({message:`Analysis: ${r.analysis_summary}`,styles:{icon:"\u{1F9E0}",textColor:"white"}}),i.push({message:`Execution Summary: ${r.execution_summary}`,styles:{icon:"hexagon",iconColor:"blueBright",textColor:"white"}});let o=await L.scenarioRepository.getIssuesByParent(r.id,"scenario_analysis");o.length>0&&x.debug(`Scenario ${t} (${A.title}) completion: ${o.length} issues found`);for(let c of o){let g=[];c.issue_type&&g.push(`${c.issue_type}`),c.description&&g.push(`${c.description}`),g.length>0&&i.push({message:g.join(" | "),styles:{icon:"\u{1F41E}",textColor:c.severity==="critical"||c.severity==="high"?"red":"yellow"}});}let a=await L.scenarioRepository.getPassedValidationsByScenarioId(t);a.length>0&&x.debug(`Scenario ${t} (${A.title}) completion: ${a.length} passed validations found`);for(let c of a){let g=[];c.validation_type&&g.push(`${c.validation_type}`),c.description&&g.push(`${c.description}`),g.length>0&&i.push({message:g.join(" | "),styles:{icon:"check",iconColor:"greenBright",textColor:"greenBright"}});}if(i.length>0){let c={...n,completedLogStyle:{icon:"\u{1F9EA}",textColor:"magenta",bold:!0},status:bn.mapScenarioStatusToFeedStatus(A.status),currentMessages:i};this.state.feedItems.set(s,c),this.clientPort.onFeedItem(c);}}catch{}}async handleSessionErrors(t){try{let e=await L.errorRepository.getAllByParent(t,"test_session");if(e.length===0)return;x.debug(`Session ${t}: Found ${e.length} session-level errors`);let A=`session-error-${t}`;if(this.sessionErrorFeedItemCreated||this.state.feedItems.has(A))return;let r=[];for(let n of e)n.message&&r.push(this.createErrorMessage(n.message));let s={id:A,name:"Session Error",status:"failed",currentMessages:r.length>0?[r[r.length-1]]:[],allMessages:r.length>1?r.slice(0,-1):[],objectType:"Error"};this.state.feedItems.set(A,s),this.clientPort.onFeedItem(s),this.sessionErrorFeedItemCreated=!0,x.debug(`Session ${t}: Created error feed item with ${e.length} errors`);}catch(e){x.debug(`Error handling session errors: ${e instanceof Error?e.message:String(e)}`);}}async handleSessionCompletion(t){try{let e=await L.sessionRepository.getById(t);if(!e)return;await new Promise(c=>setTimeout(c,1e3)),await this.handleTasks(t);let A=[],r=await L.analysisRepository.getLatestTestSessionAnalysis(t),s=await L.scenarioRepository.getBySessionId(t);if(r){A.push({message:`Score: ${r.score==null?"-":r.score} / 100`,styles:{icon:"\u{1F4CA}",textColor:this.getScoreColor(r.score||-1)}}),A.push({message:`Scenarios run: ${s.length}`,styles:{icon:"\u{1F9EA}",textColor:"white"}}),r.analysis_summary&&A.push({message:`Analysis: ${r.analysis_summary}`,styles:{icon:"\u{1F9E0}",textColor:"white"}});let c=await L.analysisRepository.getIssuesByParent(r.id,"test_session_analysis");c.length>0&&x.debug(`Session ${t} completion: ${c.length} issues found`);for(let g of c){let u=[];g.issue_type&&u.push(`${g.issue_type}`),g.description&&u.push(`${g.description}`),u.length>0&&A.push({message:u.join(" | "),styles:{icon:"\u{1F41E}",textColor:g.severity==="critical"||g.severity==="high"?"red":"yellow"}});}}let n=e.status,i;switch(n){case "success":i="success";break;case "error":case "failing":i="failed";break;case "skipped":i="skipped";break;case "cancelled":i="cancelled";break;default:i="failed";}if(n==="error"){let c=await L.errorRepository.getAllByParent(t,"test_session");if(c.length>0){x.debug(`Session ${t} completion: ${c.length} errors found`);for(let g=0;g<c.length;g++){let u=c[g];u.message&&A.push(this.createErrorMessage(u.message));}}}let o={id:`session-${t}`,name:e.title?`Session: ${e.title}`:`Session: ${t}`,status:i,objectType:"TestSession",completedLogStyle:{icon:"\u{1F3AF}",textColor:"cyanBright",bold:!0},currentMessages:A};this.state.feedItems.set(o.id,o),this.clientPort.onFeedItem(o);let a={sessionId:t,status:e.status,feedItems:Array.from(this.state.feedItems.values()),progress:this.state.progress||void 0,timestamp:new Date};this.clientPort.onSnapshot(a),await new Promise(c=>setTimeout(c,500));}catch(e){x.debug(`Error in handleSessionCompletion: ${e instanceof Error?e.message:String(e)}`);}}async updateProgress(t){try{let e=await L.progressRepository.getTotalScenarioCount(t),A=await L.progressRepository.getCompletedScenarioCount(t);if(e>0){let r=Math.round(A/e*100);this.state.progress={total:e,current:A,percentage:r,type:"number"};}}catch{}}isSessionCompleted(t){return ["success","error","skipped","cancelled"].includes(t)}pause(){this.isPaused=true;}resume(){this.isPaused=false,!this.isPolling&&this.state&&!this.state.isCompleted&&this.poll();}async stop(){await new Promise(t=>setTimeout(t,200)),this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.isPolling=false,this.isPaused=false;}requestTermination(){this.terminationRequested=true,this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null);}async stopAndResolve(){await this.stop(),this.state&&(this.state.isCompleted=true),this.resolveCallback?.();}setRate(t){this.pollInterval=t,this.pollTimer&&(this.stop().catch(()=>{}),this.pollTimer=setInterval(()=>this.poll(),this.pollInterval));}createInfoMessage(t){return k(t,{icon:"circle",iconColor:"gray",textColor:"gray"})}createErrorMessage(t){return k(t,{icon:"cross",iconColor:"red",textColor:"red"})}getScoreColor(t){return t<0?"gray":t>=90?"greenBright":t>=80||t>=70?"yellowBright":"redBright"}initializeTaskTracking(t){return {lastStatus:"",wasActive:false,lastUpdatedAt:new Date().toISOString(),lastMessageIds:new Set,lastTaskStepIds:new Set}}initializeScenarioTracking(t){return {lastStatus:"",wasActive:false,lastUpdatedAt:new Date().toISOString(),lastAgentStepIds:new Set,lastAgentStepActionIds:new Map,lastAgentStepValidationIds:new Map,lastPassedValidationIds:new Set,lastErrorIds:new Set}}initializeSessionTracking(t){return {lastStatus:t,wasActive:false,lastErrorIds:new Set,lastMessageIds:new Set}}async handleSessionMessages(t,e,A){if(!this.clientPort||!this.state||!this.sessionTracking)return;let r=await L.taskRepository.getMessagesByParent(t,"session"),s=new Set(r.map(o=>o.id));if(!this.detectSubItemChanges(s,this.sessionTracking.lastMessageIds))return;let i=r.filter(o=>!this.sessionTracking.lastMessageIds.has(o.id));for(let o of i){let a=(o.content??"").toString().trim();if(!a)continue;let c=`session-message-${o.id}`,g=a.length>180?`${a.slice(0,177)}...`:a,u=(o.type??"info").toString().toLowerCase(),l={id:c,name:g,status:u==="error"?"error":u==="info"?"skipped":u==="warning"?"failed":u==="success"||u==="primary"?"success":"skipped",completedLogStyle:{icon:"hexagon",iconColor:u==="success"?"green":u==="error"?"red":u==="warning"?"yellow":"blue",textColor:"white"},objectType:"session_message",sessionId:t};this.state.feedItems.set(c,l),this.clientPort.onFeedItem(l);}this.sessionTracking.lastMessageIds=s;}detectSubItemChanges(t,e){if(t.size!==e.size)return true;for(let A of t)if(!e.has(A))return true;return false}async collectTaskSubItemIds(t){let e=await L.taskRepository.getStepsByTaskId(t),A=new Set(e.map(c=>c.id)),r=await L.taskRepository.getMessagesByParent(t,"task"),s=e.map(c=>L.taskRepository.getMessagesByParent(c.id,"step")),i=(await Promise.all(s)).flat(),o=[...r,...i],a=new Set(o.map(c=>c.id));return {taskStepIds:A,messageIds:a}}async collectScenarioSubItemIds(t,e){let A=new Set,r=new Map,s=new Map;if(e){let c=await L.scenarioRepository.getAgentStepsByBrowserSessionId(e);for(let g of c){A.add(g.id);let u=await L.scenarioRepository.getAgentStepActionsByStepId(g.id),B=new Set(u.map(d=>d.id));r.set(g.id,B);let l=await L.scenarioRepository.getAgentStepValidationsByStepId(g.id),h=new Set(l.map(d=>d.id));s.set(g.id,h);}}let n=await L.scenarioRepository.getPassedValidationsByScenarioId(t),i=new Set(n.map(c=>c.id)),o=await L.scenarioRepository.getErrorsByScenarioId(t),a=new Set(o.map(c=>c.id));return {agentStepIds:A,agentStepActionIds:r,agentStepValidationIds:s,passedValidationIds:i,errorIds:a}}getController(){return {pause:()=>this.pause(),resume:()=>this.resume(),stop:()=>this.stop(),setRate:t=>this.setRate(t)}}};function Au(t,e={}){return new Promise((A,r)=>{let s=Gp.request(t,e,n=>A(n));s.on("error",r),s.end();})}async function tm(t){let e=await Au(t,{method:"HEAD"});if(e.statusCode&&e.statusCode>=300&&e.statusCode<400&&e.headers.location)return e.resume(),tm(e.headers.location);if(!e.statusCode||e.statusCode<200||e.statusCode>=300)throw e.resume(),new Error(`HEAD failed: HTTP ${e.statusCode}`);let A=Number(e.headers["content-length"]??0)||0,r=String(e.headers["accept-ranges"]??"").toLowerCase().includes("bytes");return e.resume(),{total:A,acceptRanges:r}}async function Nv(t,e,A,r,s){let n=await Au(t,{method:"GET",headers:{Range:`bytes=${e}-${A}`}});if(n.statusCode!==206)throw n.resume(),new Error(`Range not supported or ignored (HTTP ${n.statusCode}).`);n.on("data",o=>s?.(o.length));let i=j.createWriteStream(r,{flags:"w"});await pipeline(n,i);}async function bv(t,e){let A=j.createWriteStream(e,{flags:"w"});for(let r of t){let s=j.createReadStream(r);await pipeline(s,A,{end:false});}await new Promise((r,s)=>{A.end(n=>n?s(n):r());});}async function Mv(t,e,A){let r=Math.max(1,A?.parts),s=A?.minPartSizeMB*1024*1024,n=A?.emitEveryMs??250,{total:i,acceptRanges:o}=await tm(t);if(!i)throw new Error("Unknown content-length; cannot parallelize safely.");if(!o||i<s||r===1){let l=await Au(t,{method:"GET"});if(!l.statusCode||l.statusCode<200||l.statusCode>=300)throw l.resume(),new Error(`Download failed: HTTP ${l.statusCode}`);let h=0,d=0;l.on("data",I=>{h+=I.length;let Q=Date.now();A?.onProgress&&Q-d>=n&&(d=Q,A.onProgress({total:i,current:h,percentage:Math.floor(h/i*100)}));});let C=j.createWriteStream(e,{highWaterMark:1024*1024});C.setMaxListeners(50),await pipeline(l,C),A?.onProgress?.({total:i,current:i,percentage:100});return}let a=Math.ceil(i/r),c=[];for(let l=0;l<r;l++){let h=l*a;if(h>=i)break;let d=Math.min(i-1,(l+1)*a-1);c.push({start:h,end:d,partPath:`${e}.part${l}`});}let g=0,u=0,B=l=>{if(g+=l,!A?.onProgress)return;let h=Date.now();h-u>=n&&(u=h,A.onProgress({total:i,current:g,percentage:Math.floor(g/i*100)}));};try{await Promise.all(c.map(l=>Nv(t,l.start,l.end,l.partPath,B))),await bv(c.map(l=>l.partPath),e),A?.onProgress?.({total:i,current:i,percentage:100});}finally{await Promise.allSettled(c.map(l=>se.unlink(l.partPath)));}}var kp=promisify(j.mkdir),Lv=promisify(j.writeFile),Tv=promisify(j.unlink),Np="1.1.1",Uv="1.1.1",vv="https://devassures3bucket.s3.us-east-1.amazonaws.com",bp=/devassure-agent,\s*version\s+([\d.]+)/i;function xv(){return process.platform==="win32"?"windows":process.platform==="darwin"?process.arch==="arm64"?"macos":"macos_intel":"linux"}function Gv(){return process.platform==="win32"?"devassure-agent.exe":"devassure-agent"}function Am(){return Ae.join(xn(),"last-downloaded-version")}function Mp(){try{let t=ps();return j.existsSync(t)&&j.statSync(t).isFile()}catch{return false}}function rm(){let t=YA(),e=Gv(),A=Ae.join(t,e),r=Ae.join(t,".devassure-agent-internal");try{j.existsSync(A)&&j.unlinkSync(A);}catch{}try{j.existsSync(r)&&(j.statSync(r).isDirectory()?j.rmSync(r,{recursive:!0}):j.unlinkSync(r));}catch{}}function Yv(){let t=Am();if(!j.existsSync(t))return null;try{return j.readFileSync(t,"utf8").trim()||null}catch{return null}}function sm(){return new Promise((t,e)=>{let A=ps(),r=spawn(A,["version"],{shell:false,stdio:["ignore","pipe","pipe"]}),s="",n="";r.stdout&&r.stdout.on("data",i=>{s+=i.toString();}),r.stderr&&r.stderr.on("data",i=>{n+=i.toString();}),r.on("error",e),r.on("close",i=>{if(i!==0){e(new Error(`devassure-agent version exited ${i}: ${n||s}`));return}let o=s.match(bp)||n.match(bp);o&&o[1]?t(o[1].trim()):e(new Error(`Could not parse version from: ${s||n}`));});})}function vn(t,e){t?.onFeedItem(e);}function Lp(t,e){if(!t)return;let A=e.progress!==void 0&&e.progress!==null?{...e.progress,type:"progressBar",unit:"MB"}:void 0;t.onSnapshot({sessionId:"",status:"setup",feedItems:e.feedItems,progress:A,timestamp:new Date});}async function Hv(t,e,A,r){let n={id:"setup-download",name:"Downloading devassure-agent",status:"running"};vn(A,n);let i=r?.progressEmitIntervalMs??300;try{await Mv(t,e,{parts:8,minPartSizeMB:10,emitEveryMs:i,onProgress:({total:o,current:a,percentage:c})=>{Lp(A,{feedItems:[{...n}],progress:{total:+(o/(1024*1024)).toFixed(2),current:+(a/(1024*1024)).toFixed(2),percentage:c}});}}),await new Promise(o=>setTimeout(o,100)),Lp(A,{feedItems:[{...n,status:"success",currentMessages:[k("Download complete.")]}],progress:void 0});}catch(o){try{j.unlinkSync(e);}catch{}throw o}}async function Jv(t,e,A){let s={id:"setup-extract",name:"Extracting\u2026",status:"running",currentMessages:[k("Extracting...")]};vn(A,s),new xp.default(t).extractAllTo(e,true),await Tv(t).catch(()=>{}),Tp(ps()),Tp(OU()),await new Promise(i=>setTimeout(i,100)),vn(A,{...s,status:"success",currentMessages:[k("Extracted.")]});}function Tp(t){try{j.chmodSync(t,493);}catch{}}async function _g(t,e){let A=YA(),r=xn(),s=xv(),n=Uv,i=`${vv}/${s}/devassure-agent/${n}/DevAssure-Agent.zip`;await kp(A,{recursive:true}),await kp(r,{recursive:true}),rm();let o=Ae.join(r,`DevAssure-Agent-${n}.zip`);await Hv(i,o,t,e),await Jv(o,A,t);let a={id:"setup-version-check",name:"Setting up DevAssure agent",status:"running"};vn(t,a);let c=await sm();await new Promise(g=>setTimeout(g,100)),vn(t,{...a,status:"success"}),await Lv(Am(),c,"utf8"),await new Promise(g=>setTimeout(g,100));}async function ru(t,e){let A=e?.force===true,r=e?.progressEmitIntervalMs,s=Aa().environment==="dev";if(s){if(!Mp())throw new Error(`Dev mode: devassure-agent not found. Add devassure-agent and .devassure-agent-internal to ${YA()}`);return}let n=r!==void 0?{progressEmitIntervalMs:r}:void 0;if(A){if(rm(),s)throw new Error(`Dev mode: agent cannot be downloaded. Add devassure-agent and .devassure-agent-internal to ${YA()}`);await _g(t,n);return}if(!Mp()){await _g(t,n);return}let i=Yv();if(!(i&&Pn.valid(i)&&Pn.gte(i,Np))){try{let o=await sm();if(Pn.valid(o)&&Pn.gte(o,Np))return}catch{}await _g(t,n);}}var nm=class{constructor(){this.clientPort=null,this.pollingService=null,this.agentProcessBySessionId=new Map,this.activePrompts=new Map;}initialize(t){this.clientPort=t,this.pollingService=new kv,this.pollingService.initialize(t);}async close(){await L.close(),await this.pollingService?.stop();}async executeSession(t,e,A,r,s,n,i,o,a,c,g,u,B,l){if(!this.clientPort||!this.pollingService)throw new Error("SessionService not initialized. Call initialize() first.");await L.initialize(),await ru(this.clientPort??void 0);let h=ps(),d=g===true?["resume",`--test-session-id=${t}`]:(()=>{let f=["start",`--test-session-id=${t}`,`--project-path=${e}`];return r?.trim()?f.push(`--test-case-file=${r}`):(s?.trim()?f.push(`--test-cases-csv=${s}`):A?.trim()&&f.push(`--test-cases-dir=${A}`),n&&f.push(`--filter=${n}`)),l?.sourceBranch?.trim()&&f.push(`--source=${l.sourceBranch.trim()}`),l?.targetBranch?.trim()&&f.push(`--target=${l.targetBranch.trim()}`),l?.commitId?.trim()&&f.push(`--commit-id=${l.commitId.trim()}`),i&&f.push("--archive",i),o?.trim()&&f.push(`--environment=${o.trim()}`),u?.trim()&&f.push("--url",u.trim()),B!==void 0&&f.push("--headless",B?"true":"false"),f})(),C={...process.env};a?.trim()&&(C.HTTP_PROXY=a.trim()),c?.trim()&&(C.HTTPS_PROXY=c.trim());let I=this.clientPort?.getLogLevel()==="debug",Q=spawn(h,d,{shell:false,stdio:["ignore",I?"pipe":"ignore",I?"pipe":"ignore"],env:C});this.agentProcessBySessionId.set(t,Q);let E=async()=>{await this.pollingService?.stop(),Q.kill(),this.agentProcessBySessionId.delete(t);};I&&Q.stdout&&(Q.stdout.on("data",f=>{console.log(">>> [AGENT]: ",f.toString());}),Q.stderr&&Q.stderr.on("data",f=>{console.log(">>> [AGENT]: [ERROR] ",f.toString());}));try{this.handleUserInputPrompts(t).catch(()=>{}),await this.pollingService.start(t,g);}finally{await E();}}async sessionExecutionTermination(t){let e=`session-termination-${t}`;try{if(await L.initialize(),await L.sessionRepository.updateStatus(t,"cancelled"),this.clientPort){let n={id:e,name:"Terminating execution...",status:"failing",stateIndicators:[{status:"failing",indicator:"blinkingCircle",color:"red"}],currentMessages:[],noBorder:!0};this.clientPort.onFeedItem(n);}if(await new Promise(n=>setTimeout(n,2e3)),this.pollingService?.requestTermination(),await this.clientPort?.markActiveFeedItemsCancelled?.([e]),await new Promise(n=>setTimeout(n,6e3)),this.clientPort){let n={id:e,name:"Terminated execution.",status:"failing",stateIndicators:[{status:"failing",indicator:"blinkingCircle",color:"red"}],currentMessages:[],noBorder:!0};this.clientPort.onFeedItem(n);}let A=this.agentProcessBySessionId.get(t);if(A&&(A.kill(),this.agentProcessBySessionId.delete(t)),await new Promise(n=>setTimeout(n,200)),this.clientPort){let n={id:e,name:"Execution terminated!",status:"error",currentMessages:[],noBorder:!0};this.clientPort.onFeedItem(n);}let r=await L.sessionRepository.getById(t);r&&r.status!=="cancelled"&&await L.sessionRepository.updateStatus(t,"cancelled"),(await L.errorRepository.getBySessionId(t)).length===0&&await L.errorRepository.createForSession(t,"Execution cancelled by user!"),await this.pollingService?.stopAndResolve();}catch(A){console.error(`Error during session termination: ${A instanceof Error?A.message:String(A)}`);}}async handleUserInputPrompts(t){if(!this.clientPort||!this.pollingService)return;let e=new Rt(L.taskRepository),A=true,r=setInterval(async()=>{if(!A){clearInterval(r);return}try{let s=await L.taskRepository.getBySessionId(t);for(let n of s){if(!Rt.isTaskActive(n.status))continue;let o=await e.getPendingUserInputTaskStep(n.id);if(o){let a=`task-${n.id}-step-${o.id}`;if(this.activePrompts.has(a))continue;let c=Rt.getPromptTypeFromTaskStepData(o.data);if(!c)continue;let g=new AbortController;this.activePrompts.set(a,g);try{let u=await this.clientPort.userPrompt({type:c==="text"?"text":"confirm",message:o.label||"Please provide input:",placeholder:c==="text"?"Enter value...":void 0},g.signal);if(u.ok&&u.value!==void 0){let B=(typeof u.value=="boolean",String(u.value));await L.taskRepository.updateTaskStepUserInput(o.id,B);}}catch{}finally{this.activePrompts.delete(a);}}}}catch{}},500);return new Promise(s=>{let n=setInterval(()=>{(!this.pollingService||!this.pollingService.state||this.pollingService.state.isCompleted)&&(A=false,clearInterval(r),clearInterval(n),s());},100);})}},ra=class{async getBrowserSessionCount(){return await L.browserSessionRepository.getCount()}async getTestSessionCount(){return await L.sessionRepository.getCount()}async getScenarioCount(){return await L.scenarioRepository.getCount()}async getStorageSize(){let t=AA(),e=Ae.join(t,"data","devassure.db"),A=Ae.join(t,"reports"),r=0;try{r+=await se.stat(e).then(s=>s.size);}catch{}try{r+=await this.calculateDirectorySize(A);}catch{}return r}async calculateDirectorySize(t){let e=0;try{let A=await se.readdir(t,{withFileTypes:!0});for(let r of A){let s=Ae.join(t,r.name);if(r.isDirectory())e+=await this.calculateDirectorySize(s);else if(r.isFile())try{let n=await se.stat(s);e+=n.size;}catch{}}}catch{}return e}formatStorageSize(t){if(t===0)return "0 B";let e=1024,A=["B","KB","MB","GB","TB"],r=Math.floor(Math.log(t)/Math.log(e));return `${(t/Math.pow(e,r)).toFixed(2)} ${A[r]??"B"}`}},im=class{async getAllSessions(){return await L.sessionRepository.getAllOrderedByCreatedAt()}async getSessionsToDelete(t,e){let A=await this.getAllSessions();if(t!==void 0){let r=new Date;r.setDate(r.getDate()-t);let s=r.toISOString();return A.filter(n=>n.created_at?n.created_at<s:true)}return e!==void 0?e>=A.length?[]:A.slice(e):A}async getCleanupStats(t){let A=(await L.browserSessionRepository.getBySessionIds(t)).length,r=await this.calculateStorageSize(t);return {sessionsToDelete:t.length,browserSessionsToDelete:A,storageToFree:r}}async cleanupSessionsByIds(t){if(t.length===0)return;let e=await L.sessionRepository.getExistingSessionIds(t);await this.deleteSessions(e);}async calculateStorageSize(t){let e=AA(),A=0;for(let r of t){let s=Ae.join(e,"data",r),n=Ae.join(e,"reports",r);try{A+=await this.getDirectorySize(s);}catch{}try{A+=await this.getDirectorySize(n);}catch{}}return A}async getDirectorySize(t){let e=0;try{let A=await se.readdir(t,{withFileTypes:!0});for(let r of A){let s=Ae.join(t,r.name);if(r.isDirectory())e+=await this.getDirectorySize(s);else if(r.isFile())try{let n=await se.stat(s);e+=n.size;}catch{}}}catch{}return e}async deleteAllData(){await we(async()=>{await de.run("BEGIN TRANSACTION");try{await de.run("DELETE FROM AgentStepAction"),await de.run("DELETE FROM AgentStepValidation"),await de.run("DELETE FROM AgentStep"),await de.run("DELETE FROM PassedValidation"),await de.run("DELETE FROM TaskStep"),await de.run("DELETE FROM Task"),await de.run("DELETE FROM Error"),await de.run("DELETE FROM Message"),await de.run("DELETE FROM Issue"),await de.run("DELETE FROM BrowserSession"),await de.run("DELETE FROM ScenarioAnalysis"),await de.run("DELETE FROM Scenario"),await de.run("DELETE FROM TestSessionAnalysis"),await de.run("DELETE FROM ReportsRender"),await de.run("DELETE FROM SessionData"),await de.run("DELETE FROM TestSession"),await de.run("COMMIT");}catch(r){throw await de.run("ROLLBACK"),r}});let t=AA(),e=Ae.join(t,"data"),A=Ae.join(t,"reports");try{let r=await se.readdir(e,{withFileTypes:!0});for(let s of r)s.isDirectory()&&await se.rm(Ae.join(e,s.name),{recursive:!0,force:!0});}catch{}try{let r=await se.readdir(A,{withFileTypes:!0});for(let s of r)s.isDirectory()&&await se.rm(Ae.join(A,s.name),{recursive:!0,force:!0});}catch{}}async deleteSessions(t,e){if(t.length===0&&!e)return;if(e){await this.deleteAllData();return}let A=await L.scenarioRepository.getScenarioIdsBySessionIds(t),r=await L.browserSessionRepository.getBrowserSessionIdsBySessionIds(t),s=[];for(let a of t){let c=await L.taskRepository.getBySessionId(a);s.push(...c.map(g=>g.id));}let n=[];for(let a of s){let c=await L.taskRepository.getStepsByTaskId(a);n.push(...c.map(g=>g.id));}let i=[];for(let a of A){let c=await L.analysisRepository.getLatestScenarioAnalysis(a);c&&i.push(c.id);}let o=[];for(let a of t){let c=await L.analysisRepository.getLatestTestSessionAnalysis(a);c&&o.push(c.id);}await we(async()=>{await de.run("BEGIN TRANSACTION");try{let a=L.cleanupRepository;await a.deleteAgentStepActionsByBrowserSessionIds(r),await a.deleteAgentStepValidationsByBrowserSessionIds(r),await a.deleteAgentStepsByBrowserSessionIds(r),await a.deletePassedValidationsByScenarioIds(A),await a.deleteTaskStepsBySessionIds(t),await a.deleteTasksBySessionIds(t),await a.deleteErrorsBySessionIds(t),await a.deleteMessagesBySessionIds(t,A,r,s,n),await a.deleteIssuesByAnalysisIds(i,o),await a.deleteBrowserSessionsBySessionIds(t),await a.deleteScenarioAnalysesByScenarioIds(A),await a.deleteScenariosBySessionIds(t),await a.deleteTestSessionAnalysesBySessionIds(t),await a.deleteReportsRendersBySessionIds(t),await a.deleteSessionDataBySessionIds(t),await a.deleteTestSessionsByIds(t),await de.run("COMMIT");}catch(a){throw await de.run("ROLLBACK"),a}}),await this.deleteSessionFolders(t);}async deleteSessionFolders(t){let e=AA();for(let A of t){let r=Ae.join(e,"data",A),s=Ae.join(e,"reports",A);try{await se.rm(r,{recursive:!0,force:!0});}catch{}try{await se.rm(s,{recursive:!0,force:!0});}catch{}}}};function Vv(){let t=new Date,e=r=>r.toString().padStart(2,"0"),A=t.getMilliseconds().toString().padStart(3,"0").padEnd(6,"0");return `${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())}T${e(t.getHours())}:${e(t.getMinutes())}:${e(t.getSeconds())}.${A}`}var sa=class{constructor(){this.clientPort=null;}initialize(t){this.clientPort=t;}async archiveReport(t,e,A,r){let s=ps(),n=["archive-results","--target-path",t,"--test-session-id",e],i={...process.env};A?.trim()&&(i.HTTP_PROXY=A.trim()),r?.trim()&&(i.HTTPS_PROXY=r.trim());let o=this.clientPort?.getLogLevel()==="debug",a=spawn(s,n,{shell:false,stdio:o?["ignore","pipe","pipe"]:"inherit",env:i});return o&&a.stdout&&a.stdout.on("data",c=>{console.log(">>> [AGENT]: ",c.toString());}),o&&a.stderr&&a.stderr.on("data",c=>{console.log(">>> [AGENT]: [ERROR] ",c.toString());}),new Promise(c=>{a.on("exit",(g,u)=>{c(g??(u?1:0));});})}async openReport(t,e,A,r,s,n){await L.initialize();let i=t;if(!r&&e){let h=await L.sessionRepository.getLastSessionId();if(!h)throw new Error("No test sessions found");i=h;}let o=Vv(),a=ps(),c=["open-report"];r?c.push("--archive",r):i&&c.push(`--test-session-id=${i}`);let g={...process.env};s?.trim()&&(g.HTTP_PROXY=s.trim()),n?.trim()&&(g.HTTPS_PROXY=n.trim());let u=spawn(a,c,{shell:false,env:g});this.clientPort?.getLogLevel()==="debug"&&(u.stdout.on("data",h=>{console.log(">>> [AGENT]: ",h.toString());}),u.stderr.on("data",h=>{console.log(">>> [AGENT]: [ERROR] ",h.toString());}));let B=()=>{u.killed||u.kill();},l=()=>{u.killed||u.kill();};process.on("SIGINT",l),process.on("SIGTERM",l);try{let h=await this.pollForReportsRender(o,i);if(!h)throw new Error("Failed to start report server - no ReportsRender entry found");if(!h.host||h.port===null)throw new Error("Report server started but host/port not available");let d=`http://${h.host}:${h.port}`;if(A){let C="report-server",I={id:C,name:`Running report server at ${d}`,status:"running",currentMessages:[k(`Report server running at ${d}`)]};await A.addFeedItem(I),await new Promise(async Q=>{let E=!1,f=async()=>{if(!E){E=!0,B();try{await A.updateFeedItem(C,{status:"success",currentMessages:[k("Report server closed")],currentPrompt:void 0});}catch(D){x.debug(">>> [REPORT SERVICE]: Feed item already removed",{error:D});}Q();}},p={message:"Stop reporting server.",type:"userKill",callbackFunction:async D=>{x.debug(">>> [REPORT SERVICE]: User kill callback",{proceed:D}),D||await f();}};await A.updateFeedItem(C,{currentPrompt:p}),u.on("exit",async()=>{if(!E){E=!0;try{await A.updateFeedItem(C,{status:"success",currentMessages:[k("Report server closed")],currentPrompt:void 0});}catch(D){x.debug(">>> [REPORT SERVICE]: Feed item already removed on exit",{error:D});}Q();}});});}return {url:d,process:u}}catch(h){throw B(),h}finally{process.removeListener("SIGINT",l),process.removeListener("SIGTERM",l);}}async pollForReportsRender(t,e,A=60,r=500){for(let s=0;s<A;s++)try{let n;if(e)n=await L.reportsRenderRepository.getLatestByTestSessionIdAfter(e,t);else {let i=await L.reportsRenderRepository.getByCreatedAtAfter(t);n=i.length>0?i[0]:void 0;}if(n&&n.host&&n.port!==null)return {host:n.host,port:n.port};await new Promise(i=>setTimeout(i,r));}catch{await new Promise(i=>setTimeout(i,r));}return null}},om="00:00:00";function Pv(t){if(t<0||!Number.isFinite(t))return om;let e=Math.floor(t/1e3),A=Math.floor(e/3600),r=Math.floor(e%3600/60),s=e%60,n=i=>i.toString().padStart(2,"0");return `${n(A)}:${n(r)}:${n(s)}`}function Up(t){if(t==null||t==="")return null;let e=Date.parse(t);return Number.isNaN(e)?null:e}var am=class{async getSessionSummary(t){let e=await L.sessionRepository.getById(t);if(!e)throw new Error("Session not found");let r=(await L.scenarioRepository.getBySessionId(t)).length,s=await L.analysisRepository.getLatestTestSessionAnalysis(t),n=null,i=0;s&&(n=s.score??null,i=(await L.analysisRepository.getIssuesByParent(s.id,"test_session_analysis")).length);let o=await L.scenarioRepository.getPassedValidationCountByTestSessionId(t),a=await L.analysisRepository.getFailedValidationCountByTestSessionId(t),c=null,g=Up(e.created_at),u=Up(e.end_time);g!=null&&u!=null&&(c=u-g);let B=c!=null?Pv(c):om;return {sessionId:t,title:e.title??null,environment:e.test_environment??null,scenarios:r,score:n,passedValidations:o,failedValidations:a,failureGroups:i,durationMs:c,durationString:B}}};function cm(){(process.env.HTTP_PROXY??process.env.http_proxy??process.env.HTTPS_PROXY??process.env.https_proxy)&&(0, ta.setGlobalDispatcher)(new ta.EnvHttpProxyAgent);}var Ov=".devassure";function Ds(t){let e=process.cwd();return Ae.join(e,Ov)}function na(t){let e=Ds();j.existsSync(e)||j.mkdirSync(e,{recursive:true});}function Gn(t,e,A,r){na();let s=Ae.join(Ds(),t),n="";A&&(n=A+`
|
|
127
|
+
`),B.close(),a({authCode:C.code,redirectUri:u})):(h.writeHead(400),h.end("Invalid callback"));});B.listen(t,async()=>{let l=new URL(g.authorizationUrl);l.searchParams.set("response_type","code"),l.searchParams.set("client_id",g.clientId),l.searchParams.set("redirect_uri",u),l.searchParams.set("state",e),l.searchParams.set("code_challenge",r),l.searchParams.set("code_challenge_method","S256"),l.searchParams.set("scope","offline_access"),l.searchParams.set("utm_source",i||"agent");try{await this.clientPort.openExternal(l.toString());}catch(h){B.close(),c(new Error(`Failed to open browser: ${h instanceof Error?h.message:String(h)}`));}}),B.on("error",l=>{c(l);}),setTimeout(()=>{B.close(),c(new Error("Authentication timeout. Please try again."));},300*1e3);})}async exchangeCodeForTokens(t,e,A){let r=Mn(),s=Buffer.from(`${r.clientId}:${r.clientSecret}`).toString("base64"),n=await fetch(r.tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Authorization:`Basic ${s}`},body:new URLSearchParams({grant_type:"authorization_code",code:t,redirect_uri:A,code_verifier:e})});if(!n.ok){let o=await n.text();throw new Error(`Token exchange failed: ${n.status} ${o}`)}let i=await n.json();if(!i.access_token||!i.refresh_token)throw new Error("Invalid token response");return i}generateRandomString(t){let e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~",A="",r=new Uint8Array(t);Ft.getRandomValues(r);for(let s=0;s<t;s++)A+=e[r[s]%e.length];return A}async generateCodeChallenge(t){let A=new TextEncoder().encode(t),r=await Ft.subtle.digest("SHA-256",A);return btoa(String.fromCharCode(...new Uint8Array(r))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}};async function Xp(t){let e=process.env.DEVASSURE_TOKEN;if(e&&e.trim()!=="")try{return await t.jobPing(e,"api_key_env_var"),{success:!0,message:""}}catch{return {success:false,message:"auth token in Environment variable DEVASSURE_TOKEN is invalid, add valid token or remove the variable and run `devassure login` to login."}}let A=await Te.getAuthToken();if(A)try{return await t.jobPing(A,"token"),{success:!0,message:""}}catch{return await Te.clearAuthToken(),{success:false,message:"auth-token added is invalid. Please add new token using `devassure add-token` command or run `devassure login` to login from browser."}}if(await Te.getAccessToken())try{return await t.ensureAuthenticated(),{success:!0,message:""}}catch(s){return {success:false,message:s?.message||"Not Authenticated! Please login to continue."}}return {success:false,message:"Not Authenticated! Please login to continue."}}async function Dv(t){try{return {Authorization:`Bearer ${await t.ensureAuthenticated()}`}}catch{return null}}function Sv(t){return {Authorization:`Bearer ${t}`}}async function tu(t,e,A={},r,s,n,i,o){let c=pv().baseUrl,g=new URL(e,c);i&&Object.entries(i).forEach(([I,Q])=>{g.searchParams.set(I,Q);});let u=g.toString(),B=n==="no_auth",l=B?void 0:n||await Dv(o);if(!B&&!l)return {exception:"unauthorized",status:401,statusText:"Unauthorized",headers:new Headers};let h={method:t.toUpperCase(),headers:{"Content-Type":"application/json",...l,...r?.headers},...r};(t==="post"||t==="put")&&A&&(h.body=JSON.stringify(A));let d;try{d=await fetch(u,h);}catch{return {exception:"connection_error",status:0,statusText:"Connection Error",headers:new Headers}}if(d.status===401&&s&&!B)try{let I=await o.refreshToken();if(I?.access_token){let Q={...r,headers:{"Content-Type":"application/json",...r?.headers}};return await tu(t,e,A,Q,!1,Sv(I.access_token),i,o)}}catch{return await Te.clearTokens(),{exception:"unauthorized",status:401,statusText:"Unauthorized - Session expired",headers:d.headers}}let C;try{let I=d.headers.get("content-type");I&&I.includes("application/json")?C=await d.json():C=await d.text();}catch{}return {data:C,status:d.status,statusText:d.statusText,headers:d.headers}}var jp=class{constructor(){this.authService=null,this.webAppCallFn=null;}initialize(t,e){this.authService=t,this.webAppCallFn=e;}async request(t,e={}){if(!this.webAppCallFn||!this.authService)throw new Error("ApiService not initialized. Call initialize() first.");let A=e.method?.toLowerCase()||"get",r;if(e.body)if(typeof e.body=="string")try{r=JSON.parse(e.body);}catch{r=e.body;}else r=e.body;let s=await this.webAppCallFn(A,t,r,{...e,refreshTokenOn401:!e.skipAuth,authHeader:e.skipAuth?"no_auth":void 0},!e.skipAuth,e.skipAuth?"no_auth":void 0,void 0);if(s.exception)throw s.exception==="unauthorized"?new Error("Not Authenticated!. Please login to continue!"):s.exception==="connection_error"?new Error("Connection error! Please check your network connection."):new Error(`API request failed: ${s.exception}`);if(!s.data&&s.status!==204)throw new Error(`API request failed: ${s.status} ${s.statusText}`);return s.data}async get(t,e){return this.request(t,{...e,method:"GET"})}async post(t,e,A){return this.request(t,{...A,method:"POST",body:e?JSON.stringify(e):void 0})}async put(t,e,A){return this.request(t,{...A,method:"PUT",body:e?JSON.stringify(e):void 0})}async delete(t,e){return this.request(t,{...e,method:"DELETE"})}},$p="normal",Kg=null;function Rv(t){Kg=t,em();}function em(){Kg&&($p=Kg.getLogLevel());}function Fv(){return em(),$p==="debug"}var x={debug:(...t)=>{Fv()&&console.log("[DEBUG]",...t);}},kv=class{constructor(){this.clientPort=null,this.pollTimer=null,this.pollInterval=500,this.isPolling=false,this.isPaused=false,this.state=null,this.taskFeedItems=new Map,this.scenarioFeedItems=new Map,this.sessionTracking=null,this.completedTasks=new Set,this.completedScenarios=new Set,this.sessionErrorFeedItemCreated=false,this.sessionWaitFeedItemCreated=false,this.sessionWaitFeedItemUpdatedToSuccess=false,this.sessionNotFoundStartTime=null,this.resumeCommand=false,this.resumed=false,this.sessionWaitTimeout=120*1e3,this.maxPollTime=3600*1e3,this.startTime=0,this.resolveCallback=null,this.rejectCallback=null,this.terminationRequested=false,this.activePrompts=new Map,this.pollCallCounter=0;}initialize(t){this.clientPort=t,Rv(t);}async start(t,e){if(!this.clientPort)throw new Error("PollingService not initialized. Call initialize() first.");return x.debug(`PollingService.start() called for session ${t}`),await L.initialize(),this.state={sessionId:t,feedItems:new Map,progress:null,isPolling:false,isCompleted:false},this.startTime=Date.now(),this.isPolling=false,this.isPaused=false,this.terminationRequested=false,this.sessionWaitFeedItemCreated=false,this.sessionWaitFeedItemUpdatedToSuccess=false,this.sessionNotFoundStartTime=null,this.resumeCommand=!!e,this.resumed=false,x.debug(`PollingService: Starting polling for session ${t} with interval ${this.pollInterval}ms`),new Promise((A,r)=>{this.resolveCallback=A,this.rejectCallback=r,this.pollTimer=setInterval(()=>{this.poll().catch(s=>{x.debug(`Error in poll() from interval: ${s instanceof Error?s.message:String(s)}`);});},this.pollInterval),this.poll().catch(s=>{x.debug(`Error in initial poll(): ${s instanceof Error?s.message:String(s)}`);});})}async poll(){if(++this.pollCallCounter,!this.clientPort||!this.state||this.isPaused){x.debug(`poll() - clientPort or state or isPaused is false client port = ${this.clientPort?"true":"false"}, state = ${this.state?"true":"false"}, isPaused = ${this.isPaused?"true":"false"}`);return}if(!this.isPolling){if(this.isPolling=true,this.terminationRequested){this.isPolling=false;return}if(!L){this.isPolling=false;return}try{if(Date.now()-this.startTime>this.maxPollTime){if(x.debug("poll() - max poll time exceeded"),await this.stop(),this.terminationRequested)return;this.rejectCallback?.(new Error("Session execution timed out"));return}let t;try{t=await L.sessionRepository.getById(this.state.sessionId);}catch(o){throw x.debug(`poll() - error getting session from database: ${o instanceof Error?o.message:String(o)}`),o}if(!t||this.resumeCommand&&t.status==="cancelled"&&!this.resumed){if(!this.sessionWaitFeedItemCreated){let o=`session-wait-${this.state.sessionId}`,a={id:o,name:"Booting up DevAssure Agent",status:"running",stateIndicators:[{status:"running",indicator:"blinkingHexagon",color:"cyan"},{status:"success",indicator:"hexagon",color:"green"}]};this.state.feedItems.set(o,a),this.clientPort.onFeedItem(a),this.sessionWaitFeedItemCreated=!0;}if(this.sessionNotFoundStartTime===null&&(this.sessionNotFoundStartTime=Date.now()),!this.resumeCommand&&Date.now()-this.sessionNotFoundStartTime>this.sessionWaitTimeout){if(await this.stop(),this.terminationRequested)return;this.rejectCallback?.(new Error("Session not found in database after waiting 2 minutes"));return}this.isPolling=!1;return}else this.resumeCommand&&(this.resumed=!0);this.sessionNotFoundStartTime=null;let e=`session-wait-${this.state.sessionId}`,A=this.state.feedItems.get(e);if(A&&!this.sessionWaitFeedItemUpdatedToSuccess){this.sessionWaitFeedItemUpdatedToSuccess=!0;let o={...A,status:"success",currentMessages:[k("Agent is ready!",{icon:"blinkingHexagon",iconColor:"grey",textColor:"grey"})]};this.state.feedItems.set(e,o),this.clientPort.onFeedItem(o),this.resumed=!0,await new Promise(a=>setTimeout(a,1e3));}this.sessionTracking||(this.sessionTracking=this.initializeSessionTracking(t.status),x.debug(`Session ${this.state.sessionId} tracking initialized with status ${t.status}`));let r=t.status,s=this.isSessionCompleted(r);if(x.debug(`Session ${this.state.sessionId} status check: ${r}, isCompleted: ${s}, wasActive: ${this.sessionTracking.wasActive}, lastStatus: ${this.sessionTracking.lastStatus}`),s&&!this.sessionTracking.wasActive){if(x.debug(`Session ${this.state.sessionId} is already completed (was never active), handling completion immediately`),await this.handleSessionCompletion(this.state.sessionId),this.sessionTracking.wasActive=!1,this.sessionTracking.lastStatus=t.status,x.debug(`Session ${this.state.sessionId} is completed, all data read and emitted, stopping polling`),await this.stop(),this.terminationRequested)return;this.state.isCompleted=!0,this.resolveCallback?.();return}await this.handleTasks(this.state.sessionId),await this.handleScenarios(this.state.sessionId),await this.updateProgress(this.state.sessionId),this.sessionTracking.lastStatus!==t.status&&this.sessionTracking.lastStatus&&x.debug(`Session ${this.state.sessionId} status changed: ${this.sessionTracking.lastStatus} -> ${t.status}`);let i=this.sessionTracking.wasActive&&s;if(await this.handleSessionErrors(this.state.sessionId),await this.handleSessionMessages(this.state.sessionId,t.title??void 0,t.status),i?(x.debug(`Session ${this.state.sessionId} transitioned from active to completed`),await this.handleSessionCompletion(this.state.sessionId),this.sessionTracking.wasActive=!1):s?(x.debug(`Session ${this.state.sessionId} is completed (was not active before)`),await this.handleSessionCompletion(this.state.sessionId),this.sessionTracking.wasActive=!1):(this.sessionTracking.wasActive||x.debug(`Session ${this.state.sessionId} is now active`),this.sessionTracking.wasActive=!0),this.sessionTracking.lastStatus=t.status,!s){let o={sessionId:this.state.sessionId,status:t.status,feedItems:Array.from(this.state.feedItems.values()),progress:this.state.progress||void 0,timestamp:new Date};this.clientPort.onSnapshot(o);}if(s){if(x.debug(`Session ${this.state.sessionId} is completed, all data read and emitted, stopping polling`),await this.stop(),this.terminationRequested)return;this.state.isCompleted=!0,this.resolveCallback?.();return}this.isPolling=!1;}catch(t){if(x.debug(`poll() error: ${t instanceof Error?t.message:String(t)}`),await this.stop(),this.terminationRequested)return;this.rejectCallback?.(t instanceof Error?t:new Error(String(t)));}}}async handleTasks(t){try{let e=await L.taskRepository.getBySessionId(t),A=new Rt(L.taskRepository);for(let r of e){if(r.name.startsWith("execute_scenario_"))continue;let s=`task-${r.id}`,n=Rt.isTaskActive(r.status),i=Rt.isTaskCompleted(r.status);if(i&&this.completedTasks.has(r.id))continue;let o=this.taskFeedItems.get(r.id);o||(o=this.initializeTaskTracking(r.id),this.taskFeedItems.set(r.id,o),x.debug(`Task ${r.id} (${r.name}) tracking initialized`)),o.wasActive&&i&&x.debug(`Task ${r.id} (${r.name}) transitioned from active to completed`);let{taskStepIds:c,messageIds:g}=await this.collectTaskSubItemIds(r.id),u=await A.getPendingUserInputTaskStep(r.id),B=await L.taskRepository.getValidatingUserInputTaskStep(r.id),l=o.lastStatus!==r.status;l&&o.lastStatus&&x.debug(`Task ${r.id} (${r.name}) status changed: ${o.lastStatus} -> ${r.status}`);let h=this.detectSubItemChanges(c,o.lastTaskStepIds);h&&x.debug(`Task ${r.id} (${r.name}) task steps changed: ${c.size} current, ${o.lastTaskStepIds.size} previous`);let d=this.detectSubItemChanges(g,o.lastMessageIds);d&&x.debug(`Task ${r.id} (${r.name}) messages changed: ${g.size} current, ${o.lastMessageIds.size} previous`);let C=u?.id!==o.lastPendingUserInputStepId;C&&x.debug(`Task ${r.id} (${r.name}) pending user input step changed: ${u?.id||"none"} -> ${o.lastPendingUserInputStepId||"none"}`);let I=B?.id!==o.lastValidatingUserInputStepId;if(I&&x.debug(`Task ${r.id} (${r.name}) validating user input step changed: ${B?.id||"none"} -> ${o.lastValidatingUserInputStepId||"none"}`),n){if(o.wasActive=!0,u){let p=`task-${r.id}-step-${u.id}`;if(!this.activePrompts.has(p)&&(C||!o.lastPendingUserInputStepId)){let y=Rt.getPromptTypeFromTaskStepData(u.data);if(y){this.pause(),x.debug(`Polling paused for user input on task ${r.id} (${r.name}), step ${u.id}`);let b=new AbortController;this.activePrompts.set(p,b);let U=await A.getTaskMessages(r.id),v=Rt.mapTaskStatusToFeedStatus(r.status),G=Rt.createFeedItem(r,U);G.onInput=!0,G.currentPrompt={message:u.label||"Please provide input:",type:y==="text"?"text":"confirm",placeholder:y==="text"?"Enter value...":void 0},this.state.feedItems.set(s,G),this.clientPort.onFeedItem(G);try{let V=await this.clientPort.userPrompt({type:y==="text"?"text":"confirm",message:u.label||"Please provide input:",placeholder:y==="text"?"Enter value...":void 0},b.signal);if(V.ok&&V.value!==void 0){let z=(typeof V.value=="boolean",String(V.value));await L.taskRepository.updateTaskStepUserInput(u.id,z),x.debug(`User input received for task ${r.id} (${r.name}), step ${u.id}, updated DB with status=validating`);}else x.debug(`User input cancelled for task ${r.id} (${r.name}), step ${u.id}`);}catch(V){x.debug(`User input prompt failed for task ${r.id} (${r.name}), step ${u.id}: ${V instanceof Error?V.message:String(V)}`);}finally{this.activePrompts.delete(p);let V=this.state.feedItems.get(s);if(V){let z={...V};delete z.currentPrompt,z.onInput=!1,this.state.feedItems.set(s,z),this.clientPort.onFeedItem(z);}this.resume(),x.debug(`Polling resumed after user input for task ${r.id} (${r.name})`);}}}}let Q=await A.getTaskMessages(r.id),E=Rt.mapTaskStatusToFeedStatus(r.status),f=Rt.createFeedItem(r,Q);u&&!this.activePrompts.has(`task-${r.id}-step-${u.id}`)&&(f.onInput=!0),(!this.state.feedItems.has(s)||l||h||d||C||I)&&(this.state.feedItems.has(s)?x.debug(`Task ${r.id} (${r.name}) feed item updated`):x.debug(`Task ${r.id} (${r.name}) feed item created`),this.state.feedItems.set(s,f),this.clientPort.onFeedItem(f)),o.lastStatus=r.status,o.lastUpdatedAt=new Date().toISOString(),o.lastPendingUserInputStepId=u?.id,o.lastValidatingUserInputStepId=B?.id,o.lastTaskStepIds=c,o.lastMessageIds=g;}else if(i&&!this.completedTasks.has(r.id)){x.debug(`Task ${r.id} (${r.name}) marked as completed`),this.completedTasks.add(r.id),o.wasActive=!1;let Q=await A.getTaskMessages(r.id),E=Rt.createFeedItem(r,Q);this.state.feedItems.set(s,E),this.clientPort.onFeedItem(E),this.state.feedItems.delete(s);}else o.wasActive=!1;}}catch(e){x.debug(`Error handling tasks: ${e}`);}}async handleScenarios(t){try{let e=await L.scenarioRepository.getBySessionId(t),A=new bn(L.scenarioRepository);for(let r of e){let s=`scenario-${r.id}`,n=bn.isScenarioActive(r.status),i=bn.isScenarioCompleted(r.status);if(r.status==="new"||i&&this.completedScenarios.has(r.id))continue;let o=this.scenarioFeedItems.get(r.id);o||(o=this.initializeScenarioTracking(r.id),this.scenarioFeedItems.set(r.id,o),x.debug(`Scenario ${r.id} (${r.title}) tracking initialized`));let a=o.wasActive&&i;a&&x.debug(`Scenario ${r.id} (${r.title}) transitioned from active to completed`),a&&await this.handleScenarioCompletion(r.id);let c=await this.collectScenarioSubItemIds(r.id,r.browser_session_id),g=o.lastStatus!==r.status;g&&o.lastStatus&&x.debug(`Scenario ${r.id} (${r.title}) status changed: ${o.lastStatus} -> ${r.status}`);let u=this.detectSubItemChanges(c.agentStepIds,o.lastAgentStepIds);u&&x.debug(`Scenario ${r.id} (${r.title}) agent steps changed: ${c.agentStepIds.size} current, ${o.lastAgentStepIds.size} previous`);let B=!1;for(let[C,I]of c.agentStepActionIds.entries()){let Q=o.lastAgentStepActionIds.get(C)||new Set;if(this.detectSubItemChanges(I,Q)){B=!0,x.debug(`Scenario ${r.id} (${r.title}) agent step actions changed for step ${C}: ${I.size} current, ${Q.size} previous`);break}}let l=!1;for(let[C,I]of c.agentStepValidationIds.entries()){let Q=o.lastAgentStepValidationIds.get(C)||new Set;if(this.detectSubItemChanges(I,Q)){l=!0,x.debug(`Scenario ${r.id} (${r.title}) agent step validations changed for step ${C}: ${I.size} current, ${Q.size} previous`);break}}let h=this.detectSubItemChanges(c.passedValidationIds,o.lastPassedValidationIds);h&&x.debug(`Scenario ${r.id} (${r.title}) passed validations changed: ${c.passedValidationIds.size} current, ${o.lastPassedValidationIds.size} previous`);let d=this.detectSubItemChanges(c.errorIds,o.lastErrorIds);if(d&&x.debug(`Scenario ${r.id} (${r.title}) errors changed: ${c.errorIds.size} current, ${o.lastErrorIds.size} previous`),n){o.wasActive=!0;let C=await A.getScenarioMessages(r),I=bn.createFeedItem(r,C);(!this.state.feedItems.has(s)||g||u||B||l||h||d)&&(this.state.feedItems.has(s)?x.debug(`Scenario ${r.id} (${r.title}) feed item updated`):x.debug(`Scenario ${r.id} (${r.title}) feed item created`),this.state.feedItems.set(s,I),this.clientPort.onFeedItem(I)),o.lastStatus=r.status,o.lastUpdatedAt=new Date().toISOString(),o.lastAgentStepIds=c.agentStepIds,o.lastAgentStepActionIds=c.agentStepActionIds,o.lastAgentStepValidationIds=c.agentStepValidationIds,o.lastPassedValidationIds=c.passedValidationIds,o.lastErrorIds=c.errorIds;}else i&&!this.completedScenarios.has(r.id)?(x.debug(`Scenario ${r.id} (${r.title}) marked as completed`),this.completedScenarios.add(r.id),o.wasActive=!1,this.state.feedItems.delete(s)):o.wasActive=!1;}}catch(e){x.debug(`Error handling scenarios: ${e}`);}}async handleScenarioCompletion(t){try{let A=(await L.scenarioRepository.getBySessionId(this.state.sessionId)).find(c=>c.id===t);if(!A)return;let r=await L.analysisRepository.getLatestScenarioAnalysis(t);if(!r){x.debug(`Scenario ${t} completion: no analysis found`);return}x.debug(`Scenario ${t} (${A.title}) completion: analysis found, score=${r.score}`);let s=`scenario-${A.id}`,n=this.state.feedItems.get(s);if(!n)return;let i=[];i.push({message:`Score: ${r.score==null?"-":r.score} / 100`,styles:{icon:"\u{1F4CA}",textColor:this.getScoreColor(r.score||-1)}}),r.analysis_summary&&i.push({message:`Analysis: ${r.analysis_summary}`,styles:{icon:"\u{1F9E0}",textColor:"white"}}),i.push({message:`Execution Summary: ${r.execution_summary}`,styles:{icon:"hexagon",iconColor:"blueBright",textColor:"white"}});let o=await L.scenarioRepository.getIssuesByParent(r.id,"scenario_analysis");o.length>0&&x.debug(`Scenario ${t} (${A.title}) completion: ${o.length} issues found`);for(let c of o){let g=[];c.issue_type&&g.push(`${c.issue_type}`),c.description&&g.push(`${c.description}`),g.length>0&&i.push({message:g.join(" | "),styles:{icon:"\u{1F41E}",textColor:c.severity==="critical"||c.severity==="high"?"red":"yellow"}});}let a=await L.scenarioRepository.getPassedValidationsByScenarioId(t);a.length>0&&x.debug(`Scenario ${t} (${A.title}) completion: ${a.length} passed validations found`);for(let c of a){let g=[];c.validation_type&&g.push(`${c.validation_type}`),c.description&&g.push(`${c.description}`),g.length>0&&i.push({message:g.join(" | "),styles:{icon:"check",iconColor:"greenBright",textColor:"greenBright"}});}if(i.length>0){let c={...n,completedLogStyle:{icon:"\u{1F9EA}",textColor:"magenta",bold:!0},status:bn.mapScenarioStatusToFeedStatus(A.status),currentMessages:i};this.state.feedItems.set(s,c),this.clientPort.onFeedItem(c);}}catch{}}async handleSessionErrors(t){try{let e=await L.errorRepository.getAllByParent(t,"test_session");if(e.length===0)return;x.debug(`Session ${t}: Found ${e.length} session-level errors`);let A=`session-error-${t}`;if(this.sessionErrorFeedItemCreated||this.state.feedItems.has(A))return;let r=[];for(let n of e)n.message&&r.push(this.createErrorMessage(n.message));let s={id:A,name:"Session Error",status:"failed",currentMessages:r.length>0?[r[r.length-1]]:[],allMessages:r.length>1?r.slice(0,-1):[],objectType:"Error"};this.state.feedItems.set(A,s),this.clientPort.onFeedItem(s),this.sessionErrorFeedItemCreated=!0,x.debug(`Session ${t}: Created error feed item with ${e.length} errors`);}catch(e){x.debug(`Error handling session errors: ${e instanceof Error?e.message:String(e)}`);}}async handleSessionCompletion(t){try{let e=await L.sessionRepository.getById(t);if(!e)return;await new Promise(c=>setTimeout(c,1e3)),await this.handleTasks(t);let A=[],r=await L.analysisRepository.getLatestTestSessionAnalysis(t),s=await L.scenarioRepository.getBySessionId(t);if(r){A.push({message:`Score: ${r.score==null?"-":r.score} / 100`,styles:{icon:"\u{1F4CA}",textColor:this.getScoreColor(r.score||-1)}}),A.push({message:`Scenarios run: ${s.length}`,styles:{icon:"\u{1F9EA}",textColor:"white"}}),r.analysis_summary&&A.push({message:`Analysis: ${r.analysis_summary}`,styles:{icon:"\u{1F9E0}",textColor:"white"}});let c=await L.analysisRepository.getIssuesByParent(r.id,"test_session_analysis");c.length>0&&x.debug(`Session ${t} completion: ${c.length} issues found`);for(let g of c){let u=[];g.issue_type&&u.push(`${g.issue_type}`),g.description&&u.push(`${g.description}`),u.length>0&&A.push({message:u.join(" | "),styles:{icon:"\u{1F41E}",textColor:g.severity==="critical"||g.severity==="high"?"red":"yellow"}});}}let n=e.status,i;switch(n){case "success":i="success";break;case "error":case "failing":i="failed";break;case "skipped":i="skipped";break;case "cancelled":i="cancelled";break;default:i="failed";}if(n==="error"){let c=await L.errorRepository.getAllByParent(t,"test_session");if(c.length>0){x.debug(`Session ${t} completion: ${c.length} errors found`);for(let g=0;g<c.length;g++){let u=c[g];u.message&&A.push(this.createErrorMessage(u.message));}}}let o={id:`session-${t}`,name:e.title?`Session: ${e.title}`:`Session: ${t}`,status:i,objectType:"TestSession",completedLogStyle:{icon:"\u{1F3AF}",textColor:"cyanBright",bold:!0},currentMessages:A};this.state.feedItems.set(o.id,o),this.clientPort.onFeedItem(o);let a={sessionId:t,status:e.status,feedItems:Array.from(this.state.feedItems.values()),progress:this.state.progress||void 0,timestamp:new Date};this.clientPort.onSnapshot(a),await new Promise(c=>setTimeout(c,500));}catch(e){x.debug(`Error in handleSessionCompletion: ${e instanceof Error?e.message:String(e)}`);}}async updateProgress(t){try{let e=await L.progressRepository.getTotalScenarioCount(t),A=await L.progressRepository.getCompletedScenarioCount(t);if(e>0){let r=Math.round(A/e*100);this.state.progress={total:e,current:A,percentage:r,type:"number"};}}catch{}}isSessionCompleted(t){return ["success","error","skipped","cancelled"].includes(t)}pause(){this.isPaused=true;}resume(){this.isPaused=false,!this.isPolling&&this.state&&!this.state.isCompleted&&this.poll();}async stop(){await new Promise(t=>setTimeout(t,200)),this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.isPolling=false,this.isPaused=false;}requestTermination(){this.terminationRequested=true,this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null);}async stopAndResolve(){await this.stop(),this.state&&(this.state.isCompleted=true),this.resolveCallback?.();}setRate(t){this.pollInterval=t,this.pollTimer&&(this.stop().catch(()=>{}),this.pollTimer=setInterval(()=>this.poll(),this.pollInterval));}createInfoMessage(t){return k(t,{icon:"circle",iconColor:"gray",textColor:"gray"})}createErrorMessage(t){return k(t,{icon:"cross",iconColor:"red",textColor:"red"})}getScoreColor(t){return t<0?"gray":t>=90?"greenBright":t>=80||t>=70?"yellowBright":"redBright"}initializeTaskTracking(t){return {lastStatus:"",wasActive:false,lastUpdatedAt:new Date().toISOString(),lastMessageIds:new Set,lastTaskStepIds:new Set}}initializeScenarioTracking(t){return {lastStatus:"",wasActive:false,lastUpdatedAt:new Date().toISOString(),lastAgentStepIds:new Set,lastAgentStepActionIds:new Map,lastAgentStepValidationIds:new Map,lastPassedValidationIds:new Set,lastErrorIds:new Set}}initializeSessionTracking(t){return {lastStatus:t,wasActive:false,lastErrorIds:new Set,lastMessageIds:new Set}}async handleSessionMessages(t,e,A){if(!this.clientPort||!this.state||!this.sessionTracking)return;let r=await L.taskRepository.getMessagesByParent(t,"session"),s=new Set(r.map(o=>o.id));if(!this.detectSubItemChanges(s,this.sessionTracking.lastMessageIds))return;let i=r.filter(o=>!this.sessionTracking.lastMessageIds.has(o.id));for(let o of i){let a=(o.content??"").toString().trim();if(!a)continue;let c=`session-message-${o.id}`,g=a.length>180?`${a.slice(0,177)}...`:a,u=(o.type??"info").toString().toLowerCase(),l={id:c,name:g,status:u==="error"?"error":u==="info"?"skipped":u==="warning"?"failed":u==="success"||u==="primary"?"success":"skipped",completedLogStyle:{icon:"hexagon",iconColor:u==="success"?"green":u==="error"?"red":u==="warning"?"yellow":"blue",textColor:"white"},objectType:"session_message",sessionId:t};this.state.feedItems.set(c,l),this.clientPort.onFeedItem(l);}this.sessionTracking.lastMessageIds=s;}detectSubItemChanges(t,e){if(t.size!==e.size)return true;for(let A of t)if(!e.has(A))return true;return false}async collectTaskSubItemIds(t){let e=await L.taskRepository.getStepsByTaskId(t),A=new Set(e.map(c=>c.id)),r=await L.taskRepository.getMessagesByParent(t,"task"),s=e.map(c=>L.taskRepository.getMessagesByParent(c.id,"step")),i=(await Promise.all(s)).flat(),o=[...r,...i],a=new Set(o.map(c=>c.id));return {taskStepIds:A,messageIds:a}}async collectScenarioSubItemIds(t,e){let A=new Set,r=new Map,s=new Map;if(e){let c=await L.scenarioRepository.getAgentStepsByBrowserSessionId(e);for(let g of c){A.add(g.id);let u=await L.scenarioRepository.getAgentStepActionsByStepId(g.id),B=new Set(u.map(d=>d.id));r.set(g.id,B);let l=await L.scenarioRepository.getAgentStepValidationsByStepId(g.id),h=new Set(l.map(d=>d.id));s.set(g.id,h);}}let n=await L.scenarioRepository.getPassedValidationsByScenarioId(t),i=new Set(n.map(c=>c.id)),o=await L.scenarioRepository.getErrorsByScenarioId(t),a=new Set(o.map(c=>c.id));return {agentStepIds:A,agentStepActionIds:r,agentStepValidationIds:s,passedValidationIds:i,errorIds:a}}getController(){return {pause:()=>this.pause(),resume:()=>this.resume(),stop:()=>this.stop(),setRate:t=>this.setRate(t)}}};function Au(t,e={}){return new Promise((A,r)=>{let s=Gp.request(t,e,n=>A(n));s.on("error",r),s.end();})}async function tm(t){let e=await Au(t,{method:"HEAD"});if(e.statusCode&&e.statusCode>=300&&e.statusCode<400&&e.headers.location)return e.resume(),tm(e.headers.location);if(!e.statusCode||e.statusCode<200||e.statusCode>=300)throw e.resume(),new Error(`HEAD failed: HTTP ${e.statusCode}`);let A=Number(e.headers["content-length"]??0)||0,r=String(e.headers["accept-ranges"]??"").toLowerCase().includes("bytes");return e.resume(),{total:A,acceptRanges:r}}async function Nv(t,e,A,r,s){let n=await Au(t,{method:"GET",headers:{Range:`bytes=${e}-${A}`}});if(n.statusCode!==206)throw n.resume(),new Error(`Range not supported or ignored (HTTP ${n.statusCode}).`);n.on("data",o=>s?.(o.length));let i=j.createWriteStream(r,{flags:"w"});await pipeline(n,i);}async function bv(t,e){let A=j.createWriteStream(e,{flags:"w"});for(let r of t){let s=j.createReadStream(r);await pipeline(s,A,{end:false});}await new Promise((r,s)=>{A.end(n=>n?s(n):r());});}async function Mv(t,e,A){let r=Math.max(1,A?.parts),s=A?.minPartSizeMB*1024*1024,n=A?.emitEveryMs??250,{total:i,acceptRanges:o}=await tm(t);if(!i)throw new Error("Unknown content-length; cannot parallelize safely.");if(!o||i<s||r===1){let l=await Au(t,{method:"GET"});if(!l.statusCode||l.statusCode<200||l.statusCode>=300)throw l.resume(),new Error(`Download failed: HTTP ${l.statusCode}`);let h=0,d=0;l.on("data",I=>{h+=I.length;let Q=Date.now();A?.onProgress&&Q-d>=n&&(d=Q,A.onProgress({total:i,current:h,percentage:Math.floor(h/i*100)}));});let C=j.createWriteStream(e,{highWaterMark:1024*1024});C.setMaxListeners(50),await pipeline(l,C),A?.onProgress?.({total:i,current:i,percentage:100});return}let a=Math.ceil(i/r),c=[];for(let l=0;l<r;l++){let h=l*a;if(h>=i)break;let d=Math.min(i-1,(l+1)*a-1);c.push({start:h,end:d,partPath:`${e}.part${l}`});}let g=0,u=0,B=l=>{if(g+=l,!A?.onProgress)return;let h=Date.now();h-u>=n&&(u=h,A.onProgress({total:i,current:g,percentage:Math.floor(g/i*100)}));};try{await Promise.all(c.map(l=>Nv(t,l.start,l.end,l.partPath,B))),await bv(c.map(l=>l.partPath),e),A?.onProgress?.({total:i,current:i,percentage:100});}finally{await Promise.allSettled(c.map(l=>se.unlink(l.partPath)));}}var kp=promisify(j.mkdir),Lv=promisify(j.writeFile),Tv=promisify(j.unlink),Np="1.1.2",Uv="1.1.2",vv="https://devassures3bucket.s3.us-east-1.amazonaws.com",bp=/devassure-agent,\s*version\s+([\d.]+)/i;function xv(){return process.platform==="win32"?"windows":process.platform==="darwin"?process.arch==="arm64"?"macos":"macos_intel":"linux"}function Gv(){return process.platform==="win32"?"devassure-agent.exe":"devassure-agent"}function Am(){return Ae.join(xn(),"last-downloaded-version")}function Mp(){try{let t=ps();return j.existsSync(t)&&j.statSync(t).isFile()}catch{return false}}function rm(){let t=YA(),e=Gv(),A=Ae.join(t,e),r=Ae.join(t,".devassure-agent-internal");try{j.existsSync(A)&&j.unlinkSync(A);}catch{}try{j.existsSync(r)&&(j.statSync(r).isDirectory()?j.rmSync(r,{recursive:!0}):j.unlinkSync(r));}catch{}}function Yv(){let t=Am();if(!j.existsSync(t))return null;try{return j.readFileSync(t,"utf8").trim()||null}catch{return null}}function sm(){return new Promise((t,e)=>{let A=ps(),r=spawn(A,["version"],{shell:false,stdio:["ignore","pipe","pipe"]}),s="",n="";r.stdout&&r.stdout.on("data",i=>{s+=i.toString();}),r.stderr&&r.stderr.on("data",i=>{n+=i.toString();}),r.on("error",e),r.on("close",i=>{if(i!==0){e(new Error(`devassure-agent version exited ${i}: ${n||s}`));return}let o=s.match(bp)||n.match(bp);o&&o[1]?t(o[1].trim()):e(new Error(`Could not parse version from: ${s||n}`));});})}function vn(t,e){t?.onFeedItem(e);}function Lp(t,e){if(!t)return;let A=e.progress!==void 0&&e.progress!==null?{...e.progress,type:"progressBar",unit:"MB"}:void 0;t.onSnapshot({sessionId:"",status:"setup",feedItems:e.feedItems,progress:A,timestamp:new Date});}async function Hv(t,e,A,r){let n={id:"setup-download",name:"Downloading devassure-agent",status:"running"};vn(A,n);let i=r?.progressEmitIntervalMs??300;try{await Mv(t,e,{parts:8,minPartSizeMB:10,emitEveryMs:i,onProgress:({total:o,current:a,percentage:c})=>{Lp(A,{feedItems:[{...n}],progress:{total:+(o/(1024*1024)).toFixed(2),current:+(a/(1024*1024)).toFixed(2),percentage:c}});}}),await new Promise(o=>setTimeout(o,100)),Lp(A,{feedItems:[{...n,status:"success",currentMessages:[k("Download complete.")]}],progress:void 0});}catch(o){try{j.unlinkSync(e);}catch{}throw o}}async function Jv(t,e,A){let s={id:"setup-extract",name:"Extracting\u2026",status:"running",currentMessages:[k("Extracting...")]};vn(A,s),new xp.default(t).extractAllTo(e,true),await Tv(t).catch(()=>{}),Tp(ps()),Tp(OU()),await new Promise(i=>setTimeout(i,100)),vn(A,{...s,status:"success",currentMessages:[k("Extracted.")]});}function Tp(t){try{j.chmodSync(t,493);}catch{}}async function _g(t,e){let A=YA(),r=xn(),s=xv(),n=Uv,i=`${vv}/${s}/devassure-agent/${n}/DevAssure-Agent.zip`;await kp(A,{recursive:true}),await kp(r,{recursive:true}),rm();let o=Ae.join(r,`DevAssure-Agent-${n}.zip`);await Hv(i,o,t,e),await Jv(o,A,t);let a={id:"setup-version-check",name:"Setting up DevAssure agent",status:"running"};vn(t,a);let c=await sm();await new Promise(g=>setTimeout(g,100)),vn(t,{...a,status:"success"}),await Lv(Am(),c,"utf8"),await new Promise(g=>setTimeout(g,100));}async function ru(t,e){let A=e?.force===true,r=e?.progressEmitIntervalMs,s=Aa().environment==="dev";if(s){if(!Mp())throw new Error(`Dev mode: devassure-agent not found. Add devassure-agent and .devassure-agent-internal to ${YA()}`);return}let n=r!==void 0?{progressEmitIntervalMs:r}:void 0;if(A){if(rm(),s)throw new Error(`Dev mode: agent cannot be downloaded. Add devassure-agent and .devassure-agent-internal to ${YA()}`);await _g(t,n);return}if(!Mp()){await _g(t,n);return}let i=Yv();if(!(i&&Pn.valid(i)&&Pn.gte(i,Np))){try{let o=await sm();if(Pn.valid(o)&&Pn.gte(o,Np))return}catch{}await _g(t,n);}}var nm=class{constructor(){this.clientPort=null,this.pollingService=null,this.agentProcessBySessionId=new Map,this.activePrompts=new Map;}initialize(t){this.clientPort=t,this.pollingService=new kv,this.pollingService.initialize(t);}async close(){await L.close(),await this.pollingService?.stop();}async executeSession(t,e,A,r,s,n,i,o,a,c,g,u,B,l){if(!this.clientPort||!this.pollingService)throw new Error("SessionService not initialized. Call initialize() first.");await L.initialize(),await ru(this.clientPort??void 0);let h=ps(),d=g===true?["resume",`--test-session-id=${t}`]:(()=>{let f=["start",`--test-session-id=${t}`,`--project-path=${e}`];return r?.trim()?f.push(`--test-case-file=${r}`):(s?.trim()?f.push(`--test-cases-csv=${s}`):A?.trim()&&f.push(`--test-cases-dir=${A}`),n&&f.push(`--filter=${n}`)),l?.sourceBranch?.trim()&&f.push(`--source=${l.sourceBranch.trim()}`),l?.targetBranch?.trim()&&f.push(`--target=${l.targetBranch.trim()}`),l?.commitId?.trim()&&f.push(`--commit-id=${l.commitId.trim()}`),i&&f.push("--archive",i),o?.trim()&&f.push(`--environment=${o.trim()}`),u?.trim()&&f.push("--url",u.trim()),B!==void 0&&f.push("--headless",B?"true":"false"),f})(),C={...process.env};a?.trim()&&(C.HTTP_PROXY=a.trim()),c?.trim()&&(C.HTTPS_PROXY=c.trim());let I=this.clientPort?.getLogLevel()==="debug",Q=spawn(h,d,{shell:false,stdio:["ignore",I?"pipe":"ignore",I?"pipe":"ignore"],env:C});this.agentProcessBySessionId.set(t,Q);let E=async()=>{await this.pollingService?.stop(),Q.kill(),this.agentProcessBySessionId.delete(t);};I&&Q.stdout&&(Q.stdout.on("data",f=>{console.log(">>> [AGENT]: ",f.toString());}),Q.stderr&&Q.stderr.on("data",f=>{console.log(">>> [AGENT]: [ERROR] ",f.toString());}));try{this.handleUserInputPrompts(t).catch(()=>{}),await this.pollingService.start(t,g);}finally{await E();}}async sessionExecutionTermination(t){let e=`session-termination-${t}`;try{if(await L.initialize(),await L.sessionRepository.updateStatus(t,"cancelled"),this.clientPort){let n={id:e,name:"Terminating execution...",status:"failing",stateIndicators:[{status:"failing",indicator:"blinkingCircle",color:"red"}],currentMessages:[],noBorder:!0};this.clientPort.onFeedItem(n);}if(await new Promise(n=>setTimeout(n,2e3)),this.pollingService?.requestTermination(),await this.clientPort?.markActiveFeedItemsCancelled?.([e]),await new Promise(n=>setTimeout(n,6e3)),this.clientPort){let n={id:e,name:"Terminated execution.",status:"failing",stateIndicators:[{status:"failing",indicator:"blinkingCircle",color:"red"}],currentMessages:[],noBorder:!0};this.clientPort.onFeedItem(n);}let A=this.agentProcessBySessionId.get(t);if(A&&(A.kill(),this.agentProcessBySessionId.delete(t)),await new Promise(n=>setTimeout(n,200)),this.clientPort){let n={id:e,name:"Execution terminated!",status:"error",currentMessages:[],noBorder:!0};this.clientPort.onFeedItem(n);}let r=await L.sessionRepository.getById(t);r&&r.status!=="cancelled"&&await L.sessionRepository.updateStatus(t,"cancelled"),(await L.errorRepository.getBySessionId(t)).length===0&&await L.errorRepository.createForSession(t,"Execution cancelled by user!"),await this.pollingService?.stopAndResolve();}catch(A){console.error(`Error during session termination: ${A instanceof Error?A.message:String(A)}`);}}async handleUserInputPrompts(t){if(!this.clientPort||!this.pollingService)return;let e=new Rt(L.taskRepository),A=true,r=setInterval(async()=>{if(!A){clearInterval(r);return}try{let s=await L.taskRepository.getBySessionId(t);for(let n of s){if(!Rt.isTaskActive(n.status))continue;let o=await e.getPendingUserInputTaskStep(n.id);if(o){let a=`task-${n.id}-step-${o.id}`;if(this.activePrompts.has(a))continue;let c=Rt.getPromptTypeFromTaskStepData(o.data);if(!c)continue;let g=new AbortController;this.activePrompts.set(a,g);try{let u=await this.clientPort.userPrompt({type:c==="text"?"text":"confirm",message:o.label||"Please provide input:",placeholder:c==="text"?"Enter value...":void 0},g.signal);if(u.ok&&u.value!==void 0){let B=(typeof u.value=="boolean",String(u.value));await L.taskRepository.updateTaskStepUserInput(o.id,B);}}catch{}finally{this.activePrompts.delete(a);}}}}catch{}},500);return new Promise(s=>{let n=setInterval(()=>{(!this.pollingService||!this.pollingService.state||this.pollingService.state.isCompleted)&&(A=false,clearInterval(r),clearInterval(n),s());},100);})}},ra=class{async getBrowserSessionCount(){return await L.browserSessionRepository.getCount()}async getTestSessionCount(){return await L.sessionRepository.getCount()}async getScenarioCount(){return await L.scenarioRepository.getCount()}async getStorageSize(){let t=AA(),e=Ae.join(t,"data","devassure.db"),A=Ae.join(t,"reports"),r=0;try{r+=await se.stat(e).then(s=>s.size);}catch{}try{r+=await this.calculateDirectorySize(A);}catch{}return r}async calculateDirectorySize(t){let e=0;try{let A=await se.readdir(t,{withFileTypes:!0});for(let r of A){let s=Ae.join(t,r.name);if(r.isDirectory())e+=await this.calculateDirectorySize(s);else if(r.isFile())try{let n=await se.stat(s);e+=n.size;}catch{}}}catch{}return e}formatStorageSize(t){if(t===0)return "0 B";let e=1024,A=["B","KB","MB","GB","TB"],r=Math.floor(Math.log(t)/Math.log(e));return `${(t/Math.pow(e,r)).toFixed(2)} ${A[r]??"B"}`}},im=class{async getAllSessions(){return await L.sessionRepository.getAllOrderedByCreatedAt()}async getSessionsToDelete(t,e){let A=await this.getAllSessions();if(t!==void 0){let r=new Date;r.setDate(r.getDate()-t);let s=r.toISOString();return A.filter(n=>n.created_at?n.created_at<s:true)}return e!==void 0?e>=A.length?[]:A.slice(e):A}async getCleanupStats(t){let A=(await L.browserSessionRepository.getBySessionIds(t)).length,r=await this.calculateStorageSize(t);return {sessionsToDelete:t.length,browserSessionsToDelete:A,storageToFree:r}}async cleanupSessionsByIds(t){if(t.length===0)return;let e=await L.sessionRepository.getExistingSessionIds(t);await this.deleteSessions(e);}async calculateStorageSize(t){let e=AA(),A=0;for(let r of t){let s=Ae.join(e,"data",r),n=Ae.join(e,"reports",r);try{A+=await this.getDirectorySize(s);}catch{}try{A+=await this.getDirectorySize(n);}catch{}}return A}async getDirectorySize(t){let e=0;try{let A=await se.readdir(t,{withFileTypes:!0});for(let r of A){let s=Ae.join(t,r.name);if(r.isDirectory())e+=await this.getDirectorySize(s);else if(r.isFile())try{let n=await se.stat(s);e+=n.size;}catch{}}}catch{}return e}async deleteAllData(){await we(async()=>{await de.run("BEGIN TRANSACTION");try{await de.run("DELETE FROM AgentStepAction"),await de.run("DELETE FROM AgentStepValidation"),await de.run("DELETE FROM AgentStep"),await de.run("DELETE FROM PassedValidation"),await de.run("DELETE FROM TaskStep"),await de.run("DELETE FROM Task"),await de.run("DELETE FROM Error"),await de.run("DELETE FROM Message"),await de.run("DELETE FROM Issue"),await de.run("DELETE FROM BrowserSession"),await de.run("DELETE FROM ScenarioAnalysis"),await de.run("DELETE FROM Scenario"),await de.run("DELETE FROM TestSessionAnalysis"),await de.run("DELETE FROM ReportsRender"),await de.run("DELETE FROM SessionData"),await de.run("DELETE FROM TestSession"),await de.run("COMMIT");}catch(r){throw await de.run("ROLLBACK"),r}});let t=AA(),e=Ae.join(t,"data"),A=Ae.join(t,"reports");try{let r=await se.readdir(e,{withFileTypes:!0});for(let s of r)s.isDirectory()&&await se.rm(Ae.join(e,s.name),{recursive:!0,force:!0});}catch{}try{let r=await se.readdir(A,{withFileTypes:!0});for(let s of r)s.isDirectory()&&await se.rm(Ae.join(A,s.name),{recursive:!0,force:!0});}catch{}}async deleteSessions(t,e){if(t.length===0&&!e)return;if(e){await this.deleteAllData();return}let A=await L.scenarioRepository.getScenarioIdsBySessionIds(t),r=await L.browserSessionRepository.getBrowserSessionIdsBySessionIds(t),s=[];for(let a of t){let c=await L.taskRepository.getBySessionId(a);s.push(...c.map(g=>g.id));}let n=[];for(let a of s){let c=await L.taskRepository.getStepsByTaskId(a);n.push(...c.map(g=>g.id));}let i=[];for(let a of A){let c=await L.analysisRepository.getLatestScenarioAnalysis(a);c&&i.push(c.id);}let o=[];for(let a of t){let c=await L.analysisRepository.getLatestTestSessionAnalysis(a);c&&o.push(c.id);}await we(async()=>{await de.run("BEGIN TRANSACTION");try{let a=L.cleanupRepository;await a.deleteAgentStepActionsByBrowserSessionIds(r),await a.deleteAgentStepValidationsByBrowserSessionIds(r),await a.deleteAgentStepsByBrowserSessionIds(r),await a.deletePassedValidationsByScenarioIds(A),await a.deleteTaskStepsBySessionIds(t),await a.deleteTasksBySessionIds(t),await a.deleteErrorsBySessionIds(t),await a.deleteMessagesBySessionIds(t,A,r,s,n),await a.deleteIssuesByAnalysisIds(i,o),await a.deleteBrowserSessionsBySessionIds(t),await a.deleteScenarioAnalysesByScenarioIds(A),await a.deleteScenariosBySessionIds(t),await a.deleteTestSessionAnalysesBySessionIds(t),await a.deleteReportsRendersBySessionIds(t),await a.deleteSessionDataBySessionIds(t),await a.deleteTestSessionsByIds(t),await de.run("COMMIT");}catch(a){throw await de.run("ROLLBACK"),a}}),await this.deleteSessionFolders(t);}async deleteSessionFolders(t){let e=AA();for(let A of t){let r=Ae.join(e,"data",A),s=Ae.join(e,"reports",A);try{await se.rm(r,{recursive:!0,force:!0});}catch{}try{await se.rm(s,{recursive:!0,force:!0});}catch{}}}};function Vv(){let t=new Date,e=r=>r.toString().padStart(2,"0"),A=t.getMilliseconds().toString().padStart(3,"0").padEnd(6,"0");return `${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())}T${e(t.getHours())}:${e(t.getMinutes())}:${e(t.getSeconds())}.${A}`}var sa=class{constructor(){this.clientPort=null;}initialize(t){this.clientPort=t;}async archiveReport(t,e,A,r){let s=ps(),n=["archive-results","--target-path",t,"--test-session-id",e],i={...process.env};A?.trim()&&(i.HTTP_PROXY=A.trim()),r?.trim()&&(i.HTTPS_PROXY=r.trim());let o=this.clientPort?.getLogLevel()==="debug",a=spawn(s,n,{shell:false,stdio:o?["ignore","pipe","pipe"]:"inherit",env:i});return o&&a.stdout&&a.stdout.on("data",c=>{console.log(">>> [AGENT]: ",c.toString());}),o&&a.stderr&&a.stderr.on("data",c=>{console.log(">>> [AGENT]: [ERROR] ",c.toString());}),new Promise(c=>{a.on("exit",(g,u)=>{c(g??(u?1:0));});})}async openReport(t,e,A,r,s,n){await L.initialize();let i=t;if(!r&&e){let h=await L.sessionRepository.getLastSessionId();if(!h)throw new Error("No test sessions found");i=h;}let o=Vv(),a=ps(),c=["open-report"];r?c.push("--archive",r):i&&c.push(`--test-session-id=${i}`);let g={...process.env};s?.trim()&&(g.HTTP_PROXY=s.trim()),n?.trim()&&(g.HTTPS_PROXY=n.trim());let u=spawn(a,c,{shell:false,env:g});this.clientPort?.getLogLevel()==="debug"&&(u.stdout.on("data",h=>{console.log(">>> [AGENT]: ",h.toString());}),u.stderr.on("data",h=>{console.log(">>> [AGENT]: [ERROR] ",h.toString());}));let B=()=>{u.killed||u.kill();},l=()=>{u.killed||u.kill();};process.on("SIGINT",l),process.on("SIGTERM",l);try{let h=await this.pollForReportsRender(o,i);if(!h)throw new Error("Failed to start report server - no ReportsRender entry found");if(!h.host||h.port===null)throw new Error("Report server started but host/port not available");let d=`http://${h.host}:${h.port}`;if(A){let C="report-server",I={id:C,name:`Running report server at ${d}`,status:"running",currentMessages:[k(`Report server running at ${d}`)]};await A.addFeedItem(I),await new Promise(async Q=>{let E=!1,f=async()=>{if(!E){E=!0,B();try{await A.updateFeedItem(C,{status:"success",currentMessages:[k("Report server closed")],currentPrompt:void 0});}catch(D){x.debug(">>> [REPORT SERVICE]: Feed item already removed",{error:D});}Q();}},p={message:"Stop reporting server.",type:"userKill",callbackFunction:async D=>{x.debug(">>> [REPORT SERVICE]: User kill callback",{proceed:D}),D||await f();}};await A.updateFeedItem(C,{currentPrompt:p}),u.on("exit",async()=>{if(!E){E=!0;try{await A.updateFeedItem(C,{status:"success",currentMessages:[k("Report server closed")],currentPrompt:void 0});}catch(D){x.debug(">>> [REPORT SERVICE]: Feed item already removed on exit",{error:D});}Q();}});});}return {url:d,process:u}}catch(h){throw B(),h}finally{process.removeListener("SIGINT",l),process.removeListener("SIGTERM",l);}}async pollForReportsRender(t,e,A=60,r=500){for(let s=0;s<A;s++)try{let n;if(e)n=await L.reportsRenderRepository.getLatestByTestSessionIdAfter(e,t);else {let i=await L.reportsRenderRepository.getByCreatedAtAfter(t);n=i.length>0?i[0]:void 0;}if(n&&n.host&&n.port!==null)return {host:n.host,port:n.port};await new Promise(i=>setTimeout(i,r));}catch{await new Promise(i=>setTimeout(i,r));}return null}},om="00:00:00";function Pv(t){if(t<0||!Number.isFinite(t))return om;let e=Math.floor(t/1e3),A=Math.floor(e/3600),r=Math.floor(e%3600/60),s=e%60,n=i=>i.toString().padStart(2,"0");return `${n(A)}:${n(r)}:${n(s)}`}function Up(t){if(t==null||t==="")return null;let e=Date.parse(t);return Number.isNaN(e)?null:e}var am=class{async getSessionSummary(t){let e=await L.sessionRepository.getById(t);if(!e)throw new Error("Session not found");let r=(await L.scenarioRepository.getBySessionId(t)).length,s=await L.analysisRepository.getLatestTestSessionAnalysis(t),n=null,i=0;s&&(n=s.score??null,i=(await L.analysisRepository.getIssuesByParent(s.id,"test_session_analysis")).length);let o=await L.scenarioRepository.getPassedValidationCountByTestSessionId(t),a=await L.analysisRepository.getFailedValidationCountByTestSessionId(t),c=null,g=Up(e.created_at),u=Up(e.end_time);g!=null&&u!=null&&(c=u-g);let B=c!=null?Pv(c):om;return {sessionId:t,title:e.title??null,environment:e.test_environment??null,scenarios:r,score:n,passedValidations:o,failedValidations:a,failureGroups:i,durationMs:c,durationString:B}}};function cm(){(process.env.HTTP_PROXY??process.env.http_proxy??process.env.HTTPS_PROXY??process.env.https_proxy)&&(0, ta.setGlobalDispatcher)(new ta.EnvHttpProxyAgent);}var Ov=".devassure";function Ds(t){let e=process.cwd();return Ae.join(e,Ov)}function na(t){let e=Ds();j.existsSync(e)||j.mkdirSync(e,{recursive:true});}function Gn(t,e,A,r){na();let s=Ae.join(Ds(),t),n="";A&&(n=A+`
|
|
128
128
|
`),n+=$g.dump(e,{indent:2,lineWidth:-1}),j.writeFileSync(s,n,"utf8");}function lm(t,e){Gn("app.yaml",t,`# App Configuration
|
|
129
129
|
# This file contains the description of the web application
|
|
130
130
|
# Documentation: https://www.npmjs.com/package/@devassure/cli
|