prompttest 1.5.1 → 1.5.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/README.md +8 -4
- package/dist/bin/prompttest.js +24 -25
- package/dist/engine.bundle.js +142 -143
- package/dist/index.js +142 -143
- package/dist/lib/live-server.d.ts +4 -0
- package/package.json +16 -5
package/dist/bin/prompttest.js
CHANGED
|
@@ -25,7 +25,7 @@ ${d}`)))}),l.on("error",h=>{clearTimeout(u),!p&&o(h)})})}async getRawConnectedDe
|
|
|
25
25
|
${(await this.execAdb(["shell","wm","density"],t,8e3,0).catch(()=>({stdout:""}))).stdout}`).catch(()=>""),this.execAdb(["shell","dumpsys","meminfo"],t,8e3,0).then(o=>o.stdout).catch(()=>""),this.execAdb(["shell","dumpsys","gfxinfo"],t,8e3,0).then(o=>o.stdout).catch(()=>"")]);return Ji(s,n,i,r)}streamLogcatEvents(t,e){let s=["-s",t,"shell","logcat","-b","events","-v","time"],n=Kt(it(),s),i=Ys.createInterface({input:n.stdout});return i.on("line",e),n.on("close",()=>{try{i.close()}catch{}}),n.on("error",()=>{}),n}streamLogcatCrashes(t,e){let s=["-s",t,"shell","logcat","-T","1","-s","AndroidRuntime:E","ANRManager:E","-v","time"],n=Kt(it(),s),i=Ys.createInterface({input:n.stdout});return i.on("line",e),n.on("close",()=>{try{i.close()}catch{}}),n.on("error",()=>{}),n}streamLogcatToasts(t,e){let s=["-s",t,"shell","logcat","-s","Toast:D","Snackbar:D","-v","time"],n=Kt(it(),s),i=Ys.createInterface({input:n.stdout});return i.on("line",e),n.on("close",()=>{try{i.close()}catch{}}),n.on("error",()=>{}),n}async getLogcatTail(t=150,e){try{let{stdout:s}=await this.execAdb(["logcat","-d","-t",String(t),"-v","time"],e);return s}catch(s){return`Failed to capture logcat: ${s instanceof Error?s.message:String(s)}`}}async startScreenRecording(t="/sdcard/prompttest_temp.mp4",e){let s=e||this.defaultSerial,n=s?["-s",s,"shell","screenrecord",t]:["shell","screenrecord",t],i=Kt(it(),n);return i.on("error",()=>{}),{remotePath:t,process:i}}async stopScreenRecording(t,e,s){try{try{t.process.kill("SIGINT")}catch{}return await this.execAdb(["shell","pkill","-2","screenrecord"],s).catch(()=>{}),await new Promise(n=>setTimeout(n,1500)),await this.execAdb(["pull",t.remotePath,e],s),await this.execAdb(["shell","rm","-f",t.remotePath],s).catch(()=>{}),e}catch{return""}}async isKeyboardVisible(t){try{let{stdout:e}=await this.execAdb(["shell","sh","-c",'dumpsys input_method | grep -E "mInputShown=true|mShowRequested=true"'],t,4e3,0),s=e.toLowerCase();if(s.includes("minputshown=true")||s.includes("mshowrequested=true"))return!0}catch{}try{let{stdout:e}=await this.execAdb(["shell","sh","-c",'dumpsys window | grep -E "mInputMethodWindowVisibleHeight"'],t,4e3,0),s=e.match(/mInputMethodWindowVisibleHeight=(\d+)/i);if(s&&parseInt(s[1],10)>0)return!0}catch{}try{let{stdout:e}=await this.execAdb(["shell","dumpsys","input_method"],t,6e3,0),s=e.toLowerCase();return s.includes("minputshown=true")||s.includes("mshowrequested=true")}catch{return!1}}async ensureAccessibilityServices(t){try{await this.execAdb(["shell","settings","put","secure","accessibility_enabled","1"],t,4e3,0)}catch{}}},oe=new lt});function xr(a){let t=a.trim(),e=t.match(/^"([^"]+)"$/);if(e)return e[1].trim();let s=t.match(/^'([^']+)'$/);return s?s[1].trim():t.replace(/^['"]+|['"]+$/g,"").trim()}function ue(a){for(let t of a)if(t!=null&&t.trim().length>0)return t.trim();return""}function Lt(a,t){let e=a;if(t)for(let[s,n]of Object.entries(t))e=e.replace(new RegExp(`\\$${s}\\b`,"g"),()=>n);return e=e.replace(/\$ENV\{([A-Z0-9_]+)\}/gi,(s,n)=>process.env[n]||""),e=e.replace(/\$RANDOM_EMAIL\b/g,()=>`test_${Date.now().toString().slice(-6)}@prompttest.io`),e=e.replace(/\$RANDOM_NAME\b/g,()=>`User_${Math.floor(1e3+Math.random()*9e3)}`),e=e.replace(/\$RANDOM_PHONE\b/g,()=>`98${Math.floor(1e7+Math.random()*9e7)}`),e=e.replace(/\$TIMESTAMP\b/g,()=>Date.now().toString()),e=e.replace(/\$UUID\b/g,()=>`${Date.now().toString(16)}-${Math.random().toString(16).substring(2,8)}`),e=e.replace(/\$RANDOM_UUID\b/g,()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,s=>{let n=Math.random()*16|0;return(s==="x"?n:n&3|8).toString(16)})),e=e.replace(/\$RANDOM_CARD\b/g,()=>{let s="4532";for(let n=0;n<12;n++)s+=Math.floor(Math.random()*10);return s}),e=e.replace(/\$DATE_OFFSET\((-?\d+)\)/g,(s,n)=>{let i=parseInt(n,10)||0;return new Date(Date.now()+i*864e5).toISOString().slice(0,10)}),e=e.replace(/\$RANDOM_INT\((\d+),\s*(\d+)\)/g,(s,n,i)=>{let r=parseInt(n,10),o=parseInt(i,10);return String(Math.floor(r+Math.random()*(o-r+1)))}),e}function zt(a){return(a||"").replace(/&#(\d+);/g,(t,e)=>{try{return String.fromCodePoint(parseInt(e,10))}catch{return""}}).replace(/&#x([0-9a-fA-F]+);/g,(t,e)=>{try{return String.fromCodePoint(parseInt(e,16))}catch{return""}}).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'")}function pe(a){return zt(a||"").trim().toLowerCase().replace(/[\u2018\u2019]/g,"'").replace(/[\u201C\u201D]/g,'"').replace(/\uFFFD/g,"").replace(new RegExp("\\p{Extended_Pictographic}","gu"),"").replace(/[\uE000-\uF8FF\uFFF0-\uFFFF]/g,"").replace(/\s+/g," ").trim()}function me(a,t){if(!a||!t)return!1;let e=pe(a),s=pe(t);if(e.length<=3){let n=wr.get(e);if(!n){let i=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");n=new RegExp(`(?:^|\\b)${i}(?:\\b|$)`,"i"),wr.set(e,n)}return n.test(s)}return s.includes(e)}function Ki(a,t){let e=pe(a),s=pe(t),n=e.length,i=s.length;if(n===0)return i;if(i===0)return n;let r=Array.from({length:i+1},(l,c)=>c),o=new Array(i+1);for(let l=1;l<=n;l++){o[0]=l;for(let d=1;d<=i;d++){let p=e[l-1]===s[d-1]?0:1;o[d]=Math.min(r[d]+1,o[d-1]+1,r[d-1]+p)}let c=r;r=o,o=c}return r[i]}function zi(a,t){let e=pe(a),s=pe(t);if(e===s)return 1;if(!e||!s)return 0;let n=Math.max(e.length,s.length);return 1-Ki(e,s)/n}function Gt(a,t,e=3,s=.35){return Array.from(new Set(t.map(r=>r.trim()).filter(r=>r.length>0))).map(r=>({text:r,score:zi(a,r)})).filter(r=>r.score>=s).sort((r,o)=>o.score-r.score).slice(0,e)}var vr,de,ae,as,wr,Tr,Pr,ot=ie(()=>{"use strict";vr=/^(\d+(?:\.\d+)*\.?|\-|\*)\s*/;de=`(?:"([^"]+)"|'([^']+)'|([^'"\\s][^'"]*?[^'"\\s]|\\S))`;ae={TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:button\\s+|item\\s+)?${de}$`,"i"),ORDINAL_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:the\\s+)?(1st|2nd|3rd|4th|5th|last|first)\\s+(?:button\\s+|item\\s+)?${de}$`,"i"),PROXIMITY_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+${de}\\s+(?:next\\s+to|near|beside|inside|for)\\s+${de}$`,"i"),CLEAR:new RegExp(`^(?:clear|empty)(?:\\s+(?:text\\s+(?:in|of)|field|input))?\\s+${de}$`,"i"),LONG_PRESS:new RegExp(`^(?:long\\s+press|press\\s+and\\s+hold)\\s+(?:button\\s+|item\\s+)?${de}$`,"i"),TYPE:new RegExp(`^(?:type|enter|input)\\s+${de}\\s+(?:in|into)\\s+${de}$`,"i"),VERIFY:new RegExp(`^(?:verify|veryfy|check|assert|ensure)\\s+(?:that\\s+)?(?:error\\s+message\\s+|message\\s+|text\\s+|element\\s+|button\\s+|toast\\s+|my\\s+)?${de}\\s*(?:is\\s+(visible|exists|appears|enabled|disabled|not\\s+visible|hidden|absent|not\\s+present|gone))?$`,"i"),BACK:/^(?:press\s+back|go\s+back|navigate\s+back|back)$/i,SCROLL_TO:new RegExp(`^(?:scroll|swipe)(?:\\s+(down|up))?\\s+(?:to|until)\\s+${de}$`,"i"),SCROLL:/^(?:scroll|swipe)\s+(down|up)$/i,DIRECTIONAL_SWIPE:new RegExp(`^(?:swipe|scroll)\\s+(left|right|up|down)(?:\\s+on\\s+${de})?$`,"i"),TOGGLE:new RegExp(`^(?:toggle|turn\\s+(on|off)|check|uncheck)\\s+(?:checkbox\\s+|switch\\s+)?${de}$`,"i"),SELECT_DROPDOWN:new RegExp(`^(?:select|choose)\\s+${de}\\s+from\\s+${de}$`,"i"),LAUNCH_APP:new RegExp(`^(?:launch|open)\\s+(?:app\\s+)?${de}$`,"i"),RESTART_APP:new RegExp(`^(?:restart|relaunch)\\s+(?:app\\s+)?${de}$`,"i"),TERMINATE_APP:new RegExp(`^(?:terminate|close|force\\s*stop)\\s+(?:app\\s+)?(?:${de})?$`,"i"),CLEAR_DATA:new RegExp(`^(?:clear\\s+(?:app\\s+)?data\\s+(?:for\\s+)?|reset\\s+app\\s+)${de}$`,"i"),GRANT_PERMISSION:new RegExp(`^grant\\s+permissions?\\s+${de}(?:\\s+(?:to|for)\\s+${de})?$`,"i"),DEEP_LINK:new RegExp(`^(?:open\\s+deep\\s*link|navigate\\s+to\\s+url)\\s+${de}$`,"i"),WAIT:/^wait\s+(\d+)\s*(?:s|sec|seconds|ms)?$/i,WAIT_UNTIL:new RegExp(`^(?:wait\\s+until|wait\\s+for)\\s+${de}\\s+(?:(?:is\\s+)?(visible|appears?|enabled|exists))?(?:\\s+(?:up\\s+to|for)\\s+(\\d+)\\s*(s|sec|seconds|ms)?)?$`,"i"),IF_VISIBLE:new RegExp(`^if\\s+(?:(?:that\\s+)?${de}\\s+(?:is\\s+)?(visible|present|exists|appears?)|element\\s+${de}\\s+(?:is\\s+)?visible)$`,"i"),ELSE:/^else$/i,END:/^(?:end|end\s*if|end\s*loop|fi|done)$/i,LOOP:/^(?:loop|repeat)\s+(\d+)\s*(?:times)?$/i,FOR_EACH:new RegExp(`^for\\s+each\\s+(?:item\\s+)?(?:in\\s+)?${de}$`,"i"),SET_VARIABLE:/^set\s+\$([a-zA-Z0-9_]+)\s*=\s*(.+)$/i,DEVICE_WIFI:/^(?:turn\s+|set\s+)?wifi\s+(on|off|enable|disable)$/i,DEVICE_AIRPLANE:/^(?:turn\s+|set\\s+)?airplane\s*(?:mode)?\s+(on|off|enable|disable)$/i,DEVICE_ORIENTATION:/^(?:set\s+)?orientation\s+(?:to\s+)?(portrait|landscape)$/i,DEVICE_NOTIFICATIONS:/^(open|expand|pull\s+down|collapse|close)\s+notification(?:s|\s+shade)?$/i,INCLUDE_SPEC:new RegExp(`^(?:include|run_spec|subflow)\\s+${de}$`,"i"),RUN_JS:/^(?:run\s+js|eval\s+js|js)(?::|\s+)(.+)$/i,ENTER_OTP:new RegExp(`^(?:enter|input|type)\\s+(?:the\\s+)?(?:verification\\s+code|otp|2fa\\s+code|sms\\s+code)(?:\\s+just\\s+sent)?(?:\\s+(?:in|into)\\s+${de})?$`,"i"),API_REQUEST:new RegExp(`^(?:api|call\\s+api)\\s+(GET|POST|PUT|DELETE|PATCH)\\s+${de}(?:\\s+with\\s+body\\s+${de})?(?:\\s+store\\s+${de}\\s+as\\s+\\$([a-zA-Z0-9_]+))?(?:\\s+assert\\s+status\\s+(\\d+))?$`,"i"),EMAIL_ASSERT:new RegExp(`^(?:assert|verify)\\s+email\\s+to\\s+${de}\\s+with\\s+subject\\s+${de}(?:\\s+contains\\s+${de})?$`,"i"),OPTIONAL_SUFFIX:/\s*\((?:optional|if\s+present|maybe)\)\s*$/i,FALLBACK_QUOTED_TAP:/^(?:tap|click)\s+.*?['"]([^'"]+)['"]/i},as={HIERARCHY_ROOT:/<hierarchy[^>]*>([\s\S]*?)<\/hierarchy>/i,NODE_TAG:/<node\b([^>]*?)(\/?)>|<\/node>/gi,ATTR_KEY_VALUE:/([a-zA-Z0-9_-]+)="([^"]*)"/g,BOUNDS_RECT:/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/};wr=new Map;Tr=["sign in","signin","login","log in","submit","save","continue","next","done","send","confirm","proceed","post","rate","enter","join","apply","update"],Pr=["sign in","signin","login","log in","submit","save","next","continue","done","like","fav","favorite","star","bookmark","add","post","confirm","apply","explore","search","filter"]});function kr(a){let t=a.match(as.BOUNDS_RECT);if(!t)return{x1:0,y1:0,x2:0,y2:0,width:0,height:0,centerX:0,centerY:0};let e=parseInt(t[1],10),s=parseInt(t[2],10),n=parseInt(t[3],10),i=parseInt(t[4],10);return{x1:e,y1:s,x2:n,y2:i,width:n-e,height:i-s,centerX:Math.round((e+n)/2),centerY:Math.round((s+i)/2)}}function Gi(a){let t=a.match(as.HIERARCHY_ROOT);if(!t)return{root:[],flat:[]};let e=t[1],s=new RegExp(as.NODE_TAG.source,"gi"),n=new RegExp(as.ATTR_KEY_VALUE.source,"g"),i=[],r=[],o=[],l;for(;(l=s.exec(e))!==null;){let c=l[0],d=l[1],p=l[2]==="/";if(c.startsWith("</node>")){o.pop();continue}let u={};n.lastIndex=0;let h;for(;(h=n.exec(d))!==null;)u[h[1]]=h[2];let m={index:parseInt(u.index||"0",10),text:zt(u.text||""),resourceId:u["resource-id"]||"",className:u.class||"",packageName:u.package||"",contentDesc:zt(u["content-desc"]||""),bounds:kr(u.bounds||""),clickable:u.clickable==="true",enabled:u.enabled==="true",focused:u.focused==="true",scrollable:u.scrollable==="true",password:u.password==="true",selected:u.selected==="true",checked:u.checked!==void 0?u.checked==="true":void 0,checkable:u.checkable!==void 0?u.checkable==="true":void 0,children:[]};r.push(m),o.length===0?i.push(m):o[o.length-1].children.push(m),p||o.push(m)}return{root:i,flat:r}}function Xi(a){let t=[],e=[],s=[],n=a.length,i=0;for(;i<n;){let r=a.indexOf("<",i);if(r===-1)break;if(a.startsWith("<!--",r)){let w=a.indexOf("-->",r+4);i=w===-1?n:w+3;continue}if(a.startsWith("<?",r)){let w=a.indexOf("?>",r+2);i=w===-1?n:w+2;continue}if(a.startsWith("</",r)){let w=a.indexOf(">",r+2);if(w===-1)break;a.slice(r+2,w).trim()==="node"&&s.pop(),i=w+1;continue}let o=-1,l=null,c=!1;for(let w=r+1;w<n;w++){let S=a[w];if(l)S===l&&(l=null);else if(S==='"'||S==="'")l=S;else if(S===">"){o=w,a[w-1]==="/"&&(c=!0);break}}if(o===-1)break;let d=a.slice(r+1,c?o-1:o).trim();i=o+1;let p=0;for(;p<d.length&&!/\s/.test(d[p]);)p++;if(d.slice(0,p)!=="node")continue;let h={},m=p,b=d.length;for(;m<b;){for(;m<b&&/\s/.test(d[m]);)m++;if(m>=b)break;let w=m;for(;m<b&&d[m]!=="="&&!/\s/.test(d[m]);)m++;let S=d.slice(w,m);for(;m<b&&/\s/.test(d[m]);)m++;if(m>=b||d[m]!=="=")break;for(m++;m<b&&/\s/.test(d[m]);)m++;if(m>=b)break;let y=d[m];if(y==='"'||y==="'"){m++;let P=m;for(;m<b&&d[m]!==y;)m++;h[S]=d.slice(P,m),m<b&&m++}else{let P=m;for(;m<b&&!/\s/.test(d[m]);)m++;h[S]=d.slice(P,m)}}let T={index:parseInt(h.index||"0",10),text:zt(h.text||""),resourceId:h["resource-id"]||"",className:h.class||"",packageName:h.package||"",contentDesc:zt(h["content-desc"]||""),bounds:kr(h.bounds||""),clickable:h.clickable==="true",enabled:h.enabled==="true",focused:h.focused==="true",scrollable:h.scrollable==="true",password:h.password==="true",selected:h.selected==="true",checked:h.checked!==void 0?h.checked==="true":void 0,checkable:h.checkable!==void 0?h.checkable==="true":void 0,children:[]};e.push(T),s.length===0?t.push(T):s[s.length-1].children.push(T),c||s.push(T)}return{root:t,flat:e}}function be(a,t){if((t||R.parser?.mode||"regex")==="streaming")try{return Xi(a)}catch{}return Gi(a)}function We(a){if(a.resourceId&&/logbox/i.test(a.resourceId)||a.className&&/logbox/i.test(a.className))return!0;let t=`${a.text} ${a.contentDesc}`.trim();return!!Qi.some(e=>e.test(t))}function Fe(a){return a.filter(t=>We(t)?!1:(t.className.includes("EditText")||t.className.includes("TextInputEditText")||t.focused&&t.clickable)&&t.bounds.width>0&&t.bounds.height>0)}function Js(a){return a.className.includes("EditText")||a.className.includes("TextInputEditText")?!0:(a.children||[]).some(Js)}function Rt(a,t){let e=t?.width||R.screen.fallbackWidth,s=t?.height||R.screen.fallbackHeight,n=s*.08,i=s*.88,r=a.filter(l=>!We(l)&&(l.className.includes("RecyclerView")||l.className.includes("ListView")||l.className.includes("ScrollView"))),o=[];for(let l of r)for(let c of l.children){if(We(c)||c.bounds.centerY<n||c.bounds.centerY>i)continue;(c.clickable||c.children.some(p=>!We(p)&&p.clickable&&p.bounds.width>e*.1))&&c.bounds.width>e*.08&&c.bounds.height>s*.02&&!Js(c)&&o.push(c)}return o.length===0?a.filter(l=>{if(We(l))return!1;let c=(l.text||l.contentDesc||"").trim();return c.length<=1||/^(remember me|forgot password|sign in|sign up|login|submit|cancel)$/i.test(c)?!1:l.clickable&&l.bounds.width>e*.14&&l.bounds.height>s*.033&&l.bounds.centerY>n&&l.bounds.centerY<i&&!l.className.includes("Button")&&!l.className.includes("EditText")&&!Js(l)}):o}function Oe(a){return a.filter(t=>{if(We(t)||!t.clickable||t.bounds.width===0||t.className.includes("EditText"))return!1;let e=t.className.includes("Button")||t.className.includes("ImageButton"),s=Pr.some(n=>me(n,t.text)||me(n,t.contentDesc)||me(n,t.resourceId));return e||s})}function at(a,t=R.screen.fallbackHeight,e){if(!a||a.length===0)return[];let s=a.find(h=>h.bounds.height>=t*.6&&h.bounds.y1<=t*.15),n=s?.bounds?.y1!==void 0&&s.bounds.y1>=0?s.bounds.y1:0,i=s?.bounds?.y2&&s.bounds.y2>n?s.bounds.y2:t,r=i-n,o=s?.bounds?.x2&&s.bounds.x2>0?s.bounds.x2-(s.bounds.x1||0):R.screen.fallbackWidth,l=n+r*.78,c=a.filter(h=>We(h)||e&&h.packageName&&!h.packageName.includes(e)||!h.clickable||h.bounds.width<=0||h.bounds.height<=0||!(h.bounds.centerY>=l||h.bounds.y2>=i-20)?!1:h.bounds.width>=o*.12&&h.bounds.width<=o*.55);if(c.length<=1)return c;let d=[];for(let h of c){let m=d.find(b=>Math.abs(b[0].bounds.centerY-h.bounds.centerY)<=30||Math.abs(b[0].bounds.y2-h.bounds.y2)<=20);m||(m=[],d.push(m)),m.push(h)}let p=d.map(h=>{h.sort((b,T)=>b.bounds.centerX-T.bounds.centerX);let m=[];for(let b of h)m.some(T=>Math.abs(T.bounds.centerX-b.bounds.centerX)<o*.08)||m.push(b);return m}).filter(h=>h.length<2||h.length>6?!1:h[h.length-1].bounds.x2-h[0].bounds.x1>=o*.6);return p.length===0?c.slice(0,5):(p.sort((h,m)=>m[0].bounds.centerY-h[0].bounds.centerY),p[0].slice(0,5))}function Er(a){let t=[/fatal\s+exception/i,/unfortunately.*stopped/i,/(?:app|system|process).*(?:isn't|is not|not)\s+responding/i,/(?:isn't|is not|not)\s+responding/i,/force\s+close/i,/network\s+connection\s+(?:failed|lost|error)/i,/something went wrong/i,/(?:failed|unable)\s+to\s+(?:connect|load|fetch)/i];return a.filter(e=>{let s=`${e.text} ${e.contentDesc}`;return t.some(n=>n.test(s))})}function Ze(a,t,e=R.safety?.mode||"strict"){if(e==="disabled")return!1;let s=`${a.text} ${a.contentDesc} ${a.resourceId}`.trim();if((e==="moderate"?qi:Zi).some(r=>r.test(s)))return!0;let i=t||R.safety?.customBlacklist||[];for(let r of i)if(typeof r=="string"&&r.length>0){if(s.toLowerCase().includes(r.toLowerCase()))return!0}else if(r instanceof RegExp&&r.test(s))return!0;return!1}function qe(a){let t=`${a.text} ${a.contentDesc} ${a.resourceId}`.trim();return eo.some(e=>e.test(t))}function cs(a){for(let t of a){let e=`${t.text} ${t.contentDesc} ${t.resourceId}`;if(t.resourceId.toLowerCase().includes("logbox")||e.includes("Unhandled Promise Rejection")||e.includes("SyntaxError")||e.includes("Encountered two children with the same key")||e.includes("Warning:")||e.includes("Console Error")||e.includes("Console Warning")||e.includes("Console Info")||e.includes("TypeError:")||e.includes("ReferenceError:")||/Log \d+ of \d+/i.test(e)||e.includes("LogBox")&&(t.className.includes("Modal")||t.className.includes("ViewGroup"))){let n="error";/warning/i.test(e)?n="warning":/info/i.test(e)&&(n="info");let i=t.resourceId.includes("notification")||t.bounds.height>0&&t.bounds.height<250,r,o=[];for(let d of a){let p=`${d.text} ${d.contentDesc}`.trim();if(!r){let u=p.match(/\b([a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+)\b/);u&&(r=u[1])}/\b(?:at\s+|\()?[a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+/i.test(p)&&!o.includes(p)&&p.length<200&&o.push(p)}let l=a.find(d=>d.clickable&&d.bounds.width>0&&(/^(?:dismiss|close)$/i.test((d.text||"").trim())||/^(?:dismiss|close)$/i.test((d.contentDesc||"").trim())));if(!l&&!i){let p=a.filter(u=>u.clickable&&u.bounds.width>0&&u.bounds.height>0&&!/minimize|copy/i.test(`${u.text} ${u.contentDesc}`)).filter(u=>u.bounds.centerY>R.screen.fallbackHeight*.8);p.length>0&&(p.sort((u,h)=>u.bounds.x1-h.bounds.x1),l=p[0])}!l&&i&&(l=a.find(d=>d.clickable&&d.bounds.y1>=t.bounds.y1-20&&d.bounds.y2<=t.bounds.y2+20&&d.bounds.x1>=t.bounds.x2-120));let c=t.text||t.contentDesc||"React Native LogBox Console Overlay";return{type:"logbox",severity:n,node:t,errorText:c,sourceLocation:r,stackTrace:o.length>0?o:void 0,dismissButton:l,isToast:i}}if(t.resourceId.includes("alertTitle")&&(e.includes("isn't responding")||e.includes("has stopped"))){let n=a.find(i=>i.text.toLowerCase().includes("close app")||i.text.toLowerCase().includes("ok"));return{type:"anr",severity:"error",node:t,errorText:t.text||"Application Not Responding (ANR)",dismissButton:n}}}return null}function Xt(a){return a.filter(t=>We(t)?!1:(t.checkable||t.className.includes("CheckBox")||t.className.includes("Switch")||t.className.includes("RadioButton")||t.className.includes("SwitchCompat"))&&t.bounds.width>0&&t.bounds.height>0)}function yt(a,t=R.screen.fallbackHeight,e=R.screen.fallbackWidth){let s=t*.22,n=/(?:menu|hamburger|drawer|navigation|\u2630|open[_\s-]*drawer)/i,i=a.find(r=>{if(!r.clickable||r.bounds.width===0||r.bounds.centerY>s)return!1;let o=`${r.contentDesc} ${r.text} ${r.resourceId}`;return n.test(o)});return i||a.find(r=>{if(!r.clickable||r.bounds.width===0||r.bounds.height===0||r.bounds.centerY>s||(r.text||r.contentDesc||"").trim().length>0)return!1;let l=r.bounds.x1<=e*.25,c=r.bounds.width<=250&&r.bounds.height<=250;return l&&c})||null}function Ar(a,t=R.screen.fallbackHeight,e=R.screen.fallbackWidth){let s=t*.22;return a.filter(n=>{if(!n.clickable||n.bounds.width===0||n.bounds.height===0||n.bounds.centerY>s)return!1;let i=n.bounds.x2>=e*.75,r=n.bounds.width<=250&&n.bounds.height<=250;return i&&r})}function Dr(a){return a.filter(t=>{if(t.bounds.width===0||t.bounds.height===0)return!1;let e=t.className.includes("ScrollView")||t.className.includes("RecyclerView")||t.className.includes("ListView")||t.className.includes("NestedScrollView");return t.scrollable||e})}function $r(a,t=R.screen.fallbackWidth,e=R.screen.fallbackHeight,s=R.visual.overlapRatio){let n=[];for(let u of a)(u.text||u.clickable)&&u.bounds.width>0&&(u.bounds.x2>t+10&&u.bounds.x1<t&&n.push({type:"OVERFLOW",severity:"WARN",description:`Element "${u.text||u.contentDesc||u.className}" overflows right screen boundary by ${u.bounds.x2-t}px`,elements:[u.text||u.contentDesc||u.className]}),u.bounds.y2>e+20&&u.bounds.y1<e&&n.push({type:"OVERFLOW",severity:"WARN",description:`Element "${u.text||u.contentDesc||u.className}" overflows bottom screen boundary by ${u.bounds.y2-e}px`,elements:[u.text||u.contentDesc||u.className]}));let i=a.filter(u=>u.text&&u.text.trim().length>1&&u.bounds.width>0&&u.bounds.height>0),r=200,o=new Map;for(let u of i){let h=Math.floor(u.bounds.y1/r),m=Math.floor(u.bounds.y2/r);for(let b=h;b<=m;b++)o.has(b)||o.set(b,[]),o.get(b).push(u)}let l=new Set;for(let[,u]of o)for(let h=0;h<u.length;h++)for(let m=h+1;m<u.length;m++){let b=u[h],T=u[m];if(b===T||b.text===T.text)continue;let w=b.bounds.x1<T.bounds.x1?`${b.bounds.x1},${b.bounds.y1}:${T.bounds.x1},${T.bounds.y1}`:`${T.bounds.x1},${T.bounds.y1}:${b.bounds.x1},${b.bounds.y1}`;if(l.has(w))continue;l.add(w);let S=b.bounds,y=T.bounds;if(Math.abs(S.centerY-y.centerY)>(S.height+y.height)/2)continue;let P=Math.max(0,Math.min(S.x2,y.x2)-Math.max(S.x1,y.x1)),N=Math.max(0,Math.min(S.y2,y.y2)-Math.max(S.y1,y.y1)),O=P*N;if(O>0){let L=S.width*S.height,U=y.width*y.height,j=Math.min(L,U);j>0&&O/j>s&&n.push({type:"OVERLAP",severity:"WARN",description:`Visual text collision: "${b.text}" overlaps with "${T.text}" (${Math.round(O)}px\xB2 collision)`,elements:[b.text,T.text]})}}let c=a.filter(u=>(u.className.includes("ProgressBar")||u.className.includes("ActivityIndicator"))&&u.bounds.width>0);c.length>0&&n.push({type:"STALLED_LOADER",severity:"WARN",description:`Active loading indicator (${c[0].className}) detected on screen`,elements:c.map(u=>u.className)});let d=[],p=new Set;for(let u of n){let h=`${u.type}::${[...u.elements].sort().join("::")}`;p.has(h)||(p.add(h),d.push(u))}return d}function Ks(a,t,e){let s=t?.height||R.screen.fallbackHeight,n=t?.width||R.screen.fallbackWidth,i=at(a,s,e);if(i.length>1&&i[0].bounds.centerY>=s*.75)return{paradigm:"bottom_tabs",roots:i};let r=s*.05,o=s*.22,l=a.filter(d=>{if(We(d)||e&&d.packageName&&!d.packageName.includes(e))return!1;let p=d.className.includes("Tab")||d.resourceId.toLowerCase().includes("tab")||d.resourceId.toLowerCase().includes("header_tab")||/(?:tab\s*\d|\bphotos\b|\bvideos\b|\baudios\b)/i.test(`${d.text} ${d.contentDesc}`),u=d.bounds.centerY>=r&&d.bounds.centerY<=o,h=d.bounds.height>25&&d.bounds.height<=220;return(p||u)&&d.clickable&&d.bounds.width>50&&h});if(l.length>=2){let d=[];for(let u of l){let h=d.find(m=>Math.abs(m[0].bounds.centerY-u.bounds.centerY)<=20);h||(h=[],d.push(h)),h.push(u)}let p=d.find(u=>u.length>=2);if(p){p.sort((m,b)=>m.bounds.centerX-b.bounds.centerX);let u=[];for(let m of p)u.some(b=>Math.abs(b.bounds.centerX-m.bounds.centerX)<40)||u.push(m);if(!(u.length===2&&u[0].bounds.x1<=n*.25&&u[1].bounds.x2>=n*.75&&!u.some(m=>m.className.includes("Tab")||m.resourceId.toLowerCase().includes("tab")))&&u.length>=2)return{paradigm:"top_tabs",roots:u.slice(0,5)}}}let c=yt(a,s,n);return c?{paradigm:"drawer",roots:[c]}:{paradigm:"single_hub",roots:[]}}function Cr(a,t=2){if(a.length<=t)return a;let e=[];for(let n of a){let i=e.find(r=>{let o=r[0],l=o.className===n.className,c=Math.abs(o.bounds.width-n.bounds.width)<=30,d=Math.abs(o.bounds.height-n.bounds.height)<=30,p=Math.abs(o.bounds.x1-n.bounds.x1)<=30;return l&&c&&d&&p});i||(i=[],e.push(i)),i.push(n)}let s=[];for(let n of e)s.push(...n.slice(0,t));return s}function Ye(a,t=R.screen.fallbackHeight,e=R.screen.fallbackWidth,s=!1){let n=t*.22,i=e*.28,r=/(?:navigate[_\s-]*up|go[_\s-]*back|^back$|\bback\b|arrow_back|ic_arrow_back|chevron[_\s-]*left|ic_back|btn_back|action_bar_up|header[_\s-]*left|prev(?:ious)?|^close$|\bclose\b|^cancel$|^dismiss$|^done$|^exit$|[<←«‹❮⟨◁◀✕✖×]|\u2190|\u2196|\u2039|\u27E8|\u276E|\u25C1|\u25C0|\u00AB|\u2715|\u2716|\u00D7|[\uF060\uF104\uF053\uF2EA\uF00D\uE5C4\uE5CD])/i,o=l=>l.bounds.width>0&&l.bounds.height>0&&l.bounds.centerY<=n;for(let l of a){if(We(l)||!o(l))continue;let c=`${l.text||""} ${l.contentDesc||""} ${l.resourceId||""}`.trim();if(r.test(c)||c.length<=2&&/^[<‹«←x✕×\-]$/i.test(c))return l.clickable?l:a.find(p=>p.clickable&&p.bounds.x1<=l.bounds.x1&&p.bounds.x2>=l.bounds.x2&&p.bounds.y1<=l.bounds.y1&&p.bounds.y2>=l.bounds.y2&&p.bounds.width<=300&&p.bounds.height<=300)||l}for(let l of a){if(We(l)||!o(l)||l.bounds.x1>i)continue;if(l.className.includes("ImageButton")||l.className.includes("ImageView")||l.className.includes("ViewGroup")&&l.bounds.width<=250&&l.bounds.height<=250){let d=`${l.text||""} ${l.contentDesc||""} ${l.resourceId||""}`.toLowerCase();if(d.includes("back")||d.includes("up")||d.includes("arrow")||d.includes("chevron")||d.includes("nav")||d.includes("close")||d.includes("cancel"))return l.clickable?l:a.find(u=>u.clickable&&u.bounds.x1<=l.bounds.x1&&u.bounds.x2>=l.bounds.x2&&u.bounds.y1<=l.bounds.y1&&u.bounds.y2>=l.bounds.y2)||l}}if(s){let l=a.find(c=>{if(We(c)||!o(c)||!c.clickable)return!1;let d=c.bounds.x1<=i,p=c.bounds.width<=250&&c.bounds.height<=250;return d&&p&&c.bounds.width<e*.4});if(l)return l}return null}var Qi,Zi,qi,eo,Ve=ie(()=>{"use strict";je();ot();Qi=[/logbox/i,/^Log \d+ of \d+/i,/Console (?:Error|Warning|Log|Info)/i,/^Call Stack$/i,/^Source Map$/i,/^(?:Dismiss|Minimize|Copy|View error log)$/i,/\b[\w-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+\b/i,/Unhandled Promise Rejection/i,/SyntaxError/i,/TypeError:/i,/ReferenceError:/i,/Warning:/i,/LogBoxNotificationOverlay/i];Zi=[/(?:\b|[_\s-])(?:log[_\s-]*out|sign[_\s-]*out)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:delete|remove)(?:[_\s-]+(?:account|profile|all|data|item|cart|user))?(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:clear[_\s-]+data|wipe|force[_\s-]+stop|factory[_\s-]+reset|uninstall)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:pay|purchase|checkout|buy[_\s-]+now|order[_\s-]+now)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:exit[_\s-]*app|close[_\s-]*app|^exit$|^quit$)(?:\b|[_\s-])/i],qi=[/(?:\b|[_\s-])(?:log[_\s-]*out|sign[_\s-]*out)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:delete|remove)[_\s-]+(?:account|profile|user)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:clear[_\s-]+data|wipe|force[_\s-]+stop|factory[_\s-]+reset|uninstall)(?:\b|[_\s-])/i],eo=[/(?:\b|[_\s-])(?:rate[_\s-]*us|review[_\s-]*(?:on|in)[_\s-]*(?:play|store)|play[_\s-]*store)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:visit[_\s-]*(?:our)?[_\s-]*website|privacy[_\s-]*policy|terms[_\s-]*of[_\s-]*service|terms[_\s-]*&[_\s-]*conditions)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:follow[_\s-]*us|share[_\s-]*via|share[_\s-]*app|open[_\s-]*in[_\s-]*browser)(?:\b|[_\s-])/i,/^https?:\/\//i,/(?:www\.[a-z0-9-]+\.[a-z]{2,})/i]});import zs from"chalk";var Gs,ys,ct,Qt=ie(()=>{"use strict";Gs={DEBUG:0,INFO:1,WARN:2,ERROR:3,SILENT:4},ys=class{currentLevel="INFO";constructor(t){let e=(process.env.LOG_LEVEL||"").toUpperCase();t?this.currentLevel=t:Gs[e]!==void 0?this.currentLevel=e:this.currentLevel="INFO"}setLevel(t){this.currentLevel=t}getLevel(){return this.currentLevel}isLevelEnabled(t){return Gs[this.currentLevel]<=Gs[t]}isJsonMode(){return(process.env.LOG_FORMAT||"").toLowerCase()==="json"}emitJson(t,...e){let s=e.map(i=>typeof i=="object"?JSON.stringify(i):String(i)).join(" "),n={ts:new Date().toISOString(),level:t,msg:s};t==="ERROR"||t==="WARN"?console.error(JSON.stringify(n)):console.log(JSON.stringify(n))}debug(...t){this.isLevelEnabled("DEBUG")&&(this.isJsonMode()?this.emitJson("DEBUG",...t):console.log(zs.gray("[DEBUG]"),...t))}info(...t){this.isLevelEnabled("INFO")&&(this.isJsonMode()?this.emitJson("INFO",...t):console.log(...t))}warn(...t){this.isLevelEnabled("WARN")&&(this.isJsonMode()?this.emitJson("WARN",...t):console.warn(zs.yellow("[WARN]"),...t))}error(...t){this.isLevelEnabled("ERROR")&&(this.isJsonMode()?this.emitJson("ERROR",...t):console.error(zs.red("[ERROR]"),...t))}},ct=new ys});import to from"zlib";import so from"fs";function no(a){let t=-1;for(let e=0;e<a.length;e++){let s=a[e];t^=s;for(let n=0;n<8;n++)t=t>>>1^-(t&1)&3988292384}return~t>>>0}function ro(a=new Date){let t=(a.getHours()&31)<<11|(a.getMinutes()&63)<<5|Math.floor(a.getSeconds()/2)&31,e=(a.getFullYear()-1980&127)<<9|(a.getMonth()+1&15)<<5|a.getDate()&31;return{time:t,date:e}}function io(a){let t=[],e=[],s=0;for(let l of a){let c=l.data,d=to.deflateRawSync(c),p=d.length<c.length,u=p?d:c,h=p?8:0,m=no(c),b=c.length,T=u.length,{time:w,date:S}=ro(l.date),y=Buffer.from(l.name.replace(/\\/g,"/"),"utf-8"),P=Buffer.alloc(30+y.length);P.writeUInt32LE(67324752,0),P.writeUInt16LE(20,4),P.writeUInt16LE(2048,6),P.writeUInt16LE(h,8),P.writeUInt16LE(w,10),P.writeUInt16LE(S,12),P.writeUInt32LE(m,14),P.writeUInt32LE(T,18),P.writeUInt32LE(b,22),P.writeUInt16LE(y.length,26),P.writeUInt16LE(0,28),y.copy(P,30),t.push(P),t.push(u);let N=Buffer.alloc(46+y.length);N.writeUInt32LE(33639248,0),N.writeUInt16LE(20,4),N.writeUInt16LE(20,6),N.writeUInt16LE(2048,8),N.writeUInt16LE(h,10),N.writeUInt16LE(w,12),N.writeUInt16LE(S,14),N.writeUInt32LE(m,16),N.writeUInt32LE(T,20),N.writeUInt32LE(b,24),N.writeUInt16LE(y.length,28),N.writeUInt16LE(0,30),N.writeUInt16LE(0,32),N.writeUInt16LE(0,34),N.writeUInt16LE(0,36),N.writeUInt32LE(0,38),N.writeUInt32LE(s,42),y.copy(N,46),e.push(N),s+=P.length+u.length}let n=Buffer.concat(e),i=n.length,r=s,o=Buffer.alloc(22);return o.writeUInt32LE(101010256,0),o.writeUInt16LE(0,4),o.writeUInt16LE(0,6),o.writeUInt16LE(a.length,8),o.writeUInt16LE(a.length,10),o.writeUInt32LE(i,12),o.writeUInt32LE(r,16),o.writeUInt16LE(0,20),Buffer.concat([...t,n,o])}async function Ir(a,t){let e=io(t);await so.promises.writeFile(a,e)}var Nr=ie(()=>{"use strict"});import dt from"fs";import Je from"path";import we from"chalk";function Xs(a,t=new Set){let e=Je.resolve(a);if(!dt.existsSync(e))throw new Error(`Included spec file not found: ${e}`);if(t.has(e)){let r=Array.from(t).concat(e);throw new os(`Circular spec inclusion detected: ${r.join(" -> ")}`,r)}t.add(e);let n=dt.readFileSync(e,"utf-8").split(`
|
|
26
26
|
`),i=[];for(let r of n){let o=r.trim(),l=o.match(ae.INCLUDE_SPEC);if(l&&!o.startsWith("#")){let c=ue([l[1],l[2],l[3]]),d=Je.isAbsolute(c)?c:Je.resolve(Je.dirname(e),c),p=Xs(d,new Set(t));i.push(p)}else i.push(r)}return i.join(`
|
|
27
27
|
`)}function oo(a){let t=[],e=[];for(let s of a){if(s._isControl==="END"){e.length>0&&e.pop();continue}if(s._isControl==="ELSE"){e.length>0&&e[e.length-1].step.type==="IF_VISIBLE"&&(e[e.length-1].inElse=!0);continue}let n=t;if(e.length>0){let i=e[e.length-1];i.step.type==="IF_VISIBLE"?i.inElse?(i.step.elseSteps=i.step.elseSteps||[],n=i.step.elseSteps):(i.step.thenSteps=i.step.thenSteps||[],n=i.step.thenSteps):i.step.type==="LOOP"&&(i.step.childSteps=i.step.childSteps||[],n=i.step.childSteps)}n.push(s),(s.type==="IF_VISIBLE"||s.type==="LOOP")&&e.push({step:s,inElse:!1})}return t}function St(a,t){let e=a.split(`
|
|
28
|
-
`).map(n=>n.trim()).filter(n=>n.length>0&&!n.startsWith("#")),s=[];for(let n of e){let i=Lt(n,t),r=i.replace(vr,"").trim(),o=ae.OPTIONAL_SUFFIX.test(r)||/^[(\[]\s*optional\s*[)\]]/i.test(r)||/^optional\s*:/i.test(r),l=r.replace(ae.OPTIONAL_SUFFIX,"").replace(/^[(\[]\s*optional\s*[)\]]\s*/i,"").replace(/^optional\s*:\s*/i,"").trim();if(ae.BACK.test(l)){s.push({originalText:i,type:"BACK",optional:o});continue}let c=l.match(ae.WAIT);if(c){let v=parseInt(c[1],10),C=l.toLowerCase().includes("ms")?v:v*1e3;s.push({originalText:i,type:"WAIT",durationMs:C,optional:o});continue}let d=l.match(ae.WAIT_UNTIL);if(d){let v=ue([d[1],d[2],d[3]]),A=(d[4]||"visible").toLowerCase().includes("enable")?"enabled":"visible",D=d[5]?parseInt(d[5],10):void 0,I=(d[6]||"s").toLowerCase(),H=D?I.includes("ms")?D:D*1e3:void 0;s.push({originalText:i,type:"WAIT_UNTIL",target:v,expectedState:A,durationMs:H,optional:o});continue}let p=l.match(ae.IF_VISIBLE);if(p){let v=ue([p[1],p[2],p[3],p[5],p[6],p[7]]);s.push({originalText:i,type:"IF_VISIBLE",conditionTarget:v,thenSteps:[],elseSteps:[],optional:o});continue}if(ae.ELSE.test(l)){s.push({originalText:i,type:"IF_VISIBLE",_isControl:"ELSE"});continue}if(ae.END.test(l)){s.push({originalText:i,type:"IF_VISIBLE",_isControl:"END"});continue}let u=l.match(ae.LOOP);if(u){let v=parseInt(u[1],10);s.push({originalText:i,type:"LOOP",loopCount:v,childSteps:[],optional:o});continue}let h=l.match(ae.FOR_EACH);if(h){let v=ue([h[1],h[2],h[3]]);s.push({originalText:i,type:"LOOP",target:v,loopVariable:"item",childSteps:[],optional:o});continue}let m=l.match(ae.SET_VARIABLE);if(m){let v=m[1].trim(),C=m[2].trim();s.push({originalText:i,type:"SET_VARIABLE",variableName:v,variableExpression:C,optional:o});continue}let b=l.match(ae.DEVICE_WIFI);if(b){let v=b[1].toLowerCase();s.push({originalText:i,type:"DEVICE_STATE",deviceAction:"wifi",deviceStateValue:v==="on"||v==="enable",optional:o});continue}let T=l.match(ae.DEVICE_AIRPLANE);if(T){let v=T[1].toLowerCase();s.push({originalText:i,type:"DEVICE_STATE",deviceAction:"airplane",deviceStateValue:v==="on"||v==="enable",optional:o});continue}let w=l.match(ae.DEVICE_ORIENTATION);if(w){let v=w[1].toLowerCase();s.push({originalText:i,type:"DEVICE_STATE",deviceAction:"orientation",deviceStateValue:v,optional:o});continue}let S=l.match(ae.DEVICE_NOTIFICATIONS);if(S){let v=S[1].toLowerCase(),C=v.startsWith("open")||v.startsWith("expand")||v.startsWith("pull");s.push({originalText:i,type:"DEVICE_STATE",deviceAction:"notifications",deviceStateValue:C?"open":"collapse",optional:o});continue}let y=l.match(ae.RUN_JS);if(y){let v=y[1].trim();s.push({originalText:i,type:"RUN_JS",jsCode:v,optional:o});continue}let P=l.match(ae.ENTER_OTP);if(P){let v=ue([P[1],P[2],P[3]])||void 0;s.push({originalText:i,type:"ENTER_OTP",target:v,optional:o});continue}let N=l.match(ae.API_REQUEST);if(N){let v=N[1].toUpperCase(),C=ue([N[2],N[3],N[4]]),A=ue([N[5],N[6],N[7]])||void 0,D=ue([N[8],N[9],N[10]])||void 0,I=N[11]||void 0,H=N[12]?parseInt(N[12],10):void 0;s.push({originalText:i,type:"API_REQUEST",apiMethod:v,apiUrl:C,apiBody:A,apiStorePath:D,apiStoreVariable:I,apiExpectedStatus:H,optional:o});continue}let O=l.match(ae.EMAIL_ASSERT);if(O){let v=ue([O[1],O[2],O[3]]),C=ue([O[4],O[5],O[6]]),A=ue([O[7],O[8],O[9]])||void 0;s.push({originalText:i,type:"EMAIL_ASSERT",emailRecipient:v,emailSubject:C,emailBodyContains:A,optional:o});continue}let L=l.match(ae.LAUNCH_APP);if(L){let v=ue([L[1],L[2],L[3]]);s.push({originalText:i,type:"LAUNCH_APP",target:v,optional:o});continue}let U=l.match(ae.RESTART_APP);if(U){let v=ue([U[1],U[2],U[3]]);s.push({originalText:i,type:"RESTART_APP",target:v,optional:o});continue}let j=l.match(ae.TERMINATE_APP);if(j){let v=ue([j[1],j[2],j[3]])||void 0;s.push({originalText:i,type:"TERMINATE_APP",target:v,optional:o});continue}let Q=l.match(ae.CLEAR_DATA);if(Q){let v=ue([Q[1],Q[2],Q[3]]);s.push({originalText:i,type:"CLEAR_DATA",target:v,optional:o});continue}let K=l.match(ae.GRANT_PERMISSION);if(K){let v=ue([K[1],K[2],K[3]]),C=ue([K[4],K[5],K[6]])||void 0;s.push({originalText:i,type:"GRANT_PERMISSION",value:v,target:C,optional:o});continue}let V=l.match(ae.DEEP_LINK);if(V){let v=ue([V[1],V[2],V[3]]);s.push({originalText:i,type:"DEEP_LINK",target:v,optional:o});continue}let ye=l.match(ae.DIRECTIONAL_SWIPE);if(ye){let v=ye[1].toLowerCase(),C=ue([ye[2],ye[3],ye[4]])||void 0;s.push({originalText:i,type:"SWIPE",direction:v,target:C,optional:o});continue}let re=l.match(ae.TOGGLE);if(re){let v=(re[1]||"").toLowerCase(),C=ue([re[2],re[3],re[4]]),A=l.toLowerCase().startsWith("uncheck")||v==="off"?!1:v==="on"||l.toLowerCase().startsWith("check")?!0:void 0;s.push({originalText:i,type:"TOGGLE",target:C,desiredState:A,optional:o});continue}let W=l.match(ae.SELECT_DROPDOWN);if(W){let v=ue([W[1],W[2],W[3]]),C=ue([W[4],W[5],W[6]]);s.push({originalText:i,type:"SELECT",value:v,target:C,optional:o});continue}let
|
|
28
|
+
`).map(n=>n.trim()).filter(n=>n.length>0&&!n.startsWith("#")),s=[];for(let n of e){let i=Lt(n,t),r=i.replace(vr,"").trim(),o=ae.OPTIONAL_SUFFIX.test(r)||/^[(\[]\s*optional\s*[)\]]/i.test(r)||/^optional\s*:/i.test(r),l=r.replace(ae.OPTIONAL_SUFFIX,"").replace(/^[(\[]\s*optional\s*[)\]]\s*/i,"").replace(/^optional\s*:\s*/i,"").trim();if(ae.BACK.test(l)){s.push({originalText:i,type:"BACK",optional:o});continue}let c=l.match(ae.WAIT);if(c){let v=parseInt(c[1],10),C=l.toLowerCase().includes("ms")?v:v*1e3;s.push({originalText:i,type:"WAIT",durationMs:C,optional:o});continue}let d=l.match(ae.WAIT_UNTIL);if(d){let v=ue([d[1],d[2],d[3]]),A=(d[4]||"visible").toLowerCase().includes("enable")?"enabled":"visible",D=d[5]?parseInt(d[5],10):void 0,I=(d[6]||"s").toLowerCase(),H=D?I.includes("ms")?D:D*1e3:void 0;s.push({originalText:i,type:"WAIT_UNTIL",target:v,expectedState:A,durationMs:H,optional:o});continue}let p=l.match(ae.IF_VISIBLE);if(p){let v=ue([p[1],p[2],p[3],p[5],p[6],p[7]]);s.push({originalText:i,type:"IF_VISIBLE",conditionTarget:v,thenSteps:[],elseSteps:[],optional:o});continue}if(ae.ELSE.test(l)){s.push({originalText:i,type:"IF_VISIBLE",_isControl:"ELSE"});continue}if(ae.END.test(l)){s.push({originalText:i,type:"IF_VISIBLE",_isControl:"END"});continue}let u=l.match(ae.LOOP);if(u){let v=parseInt(u[1],10);s.push({originalText:i,type:"LOOP",loopCount:v,childSteps:[],optional:o});continue}let h=l.match(ae.FOR_EACH);if(h){let v=ue([h[1],h[2],h[3]]);s.push({originalText:i,type:"LOOP",target:v,loopVariable:"item",childSteps:[],optional:o});continue}let m=l.match(ae.SET_VARIABLE);if(m){let v=m[1].trim(),C=m[2].trim();s.push({originalText:i,type:"SET_VARIABLE",variableName:v,variableExpression:C,optional:o});continue}let b=l.match(ae.DEVICE_WIFI);if(b){let v=b[1].toLowerCase();s.push({originalText:i,type:"DEVICE_STATE",deviceAction:"wifi",deviceStateValue:v==="on"||v==="enable",optional:o});continue}let T=l.match(ae.DEVICE_AIRPLANE);if(T){let v=T[1].toLowerCase();s.push({originalText:i,type:"DEVICE_STATE",deviceAction:"airplane",deviceStateValue:v==="on"||v==="enable",optional:o});continue}let w=l.match(ae.DEVICE_ORIENTATION);if(w){let v=w[1].toLowerCase();s.push({originalText:i,type:"DEVICE_STATE",deviceAction:"orientation",deviceStateValue:v,optional:o});continue}let S=l.match(ae.DEVICE_NOTIFICATIONS);if(S){let v=S[1].toLowerCase(),C=v.startsWith("open")||v.startsWith("expand")||v.startsWith("pull");s.push({originalText:i,type:"DEVICE_STATE",deviceAction:"notifications",deviceStateValue:C?"open":"collapse",optional:o});continue}let y=l.match(ae.RUN_JS);if(y){let v=y[1].trim();s.push({originalText:i,type:"RUN_JS",jsCode:v,optional:o});continue}let P=l.match(ae.ENTER_OTP);if(P){let v=ue([P[1],P[2],P[3]])||void 0;s.push({originalText:i,type:"ENTER_OTP",target:v,optional:o});continue}let N=l.match(ae.API_REQUEST);if(N){let v=N[1].toUpperCase(),C=ue([N[2],N[3],N[4]]),A=ue([N[5],N[6],N[7]])||void 0,D=ue([N[8],N[9],N[10]])||void 0,I=N[11]||void 0,H=N[12]?parseInt(N[12],10):void 0;s.push({originalText:i,type:"API_REQUEST",apiMethod:v,apiUrl:C,apiBody:A,apiStorePath:D,apiStoreVariable:I,apiExpectedStatus:H,optional:o});continue}let O=l.match(ae.EMAIL_ASSERT);if(O){let v=ue([O[1],O[2],O[3]]),C=ue([O[4],O[5],O[6]]),A=ue([O[7],O[8],O[9]])||void 0;s.push({originalText:i,type:"EMAIL_ASSERT",emailRecipient:v,emailSubject:C,emailBodyContains:A,optional:o});continue}let L=l.match(ae.LAUNCH_APP);if(L){let v=ue([L[1],L[2],L[3]]);s.push({originalText:i,type:"LAUNCH_APP",target:v,optional:o});continue}let U=l.match(ae.RESTART_APP);if(U){let v=ue([U[1],U[2],U[3]]);s.push({originalText:i,type:"RESTART_APP",target:v,optional:o});continue}let j=l.match(ae.TERMINATE_APP);if(j){let v=ue([j[1],j[2],j[3]])||void 0;s.push({originalText:i,type:"TERMINATE_APP",target:v,optional:o});continue}let Q=l.match(ae.CLEAR_DATA);if(Q){let v=ue([Q[1],Q[2],Q[3]]);s.push({originalText:i,type:"CLEAR_DATA",target:v,optional:o});continue}let K=l.match(ae.GRANT_PERMISSION);if(K){let v=ue([K[1],K[2],K[3]]),C=ue([K[4],K[5],K[6]])||void 0;s.push({originalText:i,type:"GRANT_PERMISSION",value:v,target:C,optional:o});continue}let V=l.match(ae.DEEP_LINK);if(V){let v=ue([V[1],V[2],V[3]]);s.push({originalText:i,type:"DEEP_LINK",target:v,optional:o});continue}let ye=l.match(ae.DIRECTIONAL_SWIPE);if(ye){let v=ye[1].toLowerCase(),C=ue([ye[2],ye[3],ye[4]])||void 0;s.push({originalText:i,type:"SWIPE",direction:v,target:C,optional:o});continue}let re=l.match(ae.TOGGLE);if(re){let v=(re[1]||"").toLowerCase(),C=ue([re[2],re[3],re[4]]),A=l.toLowerCase().startsWith("uncheck")||v==="off"?!1:v==="on"||l.toLowerCase().startsWith("check")?!0:void 0;s.push({originalText:i,type:"TOGGLE",target:C,desiredState:A,optional:o});continue}let W=l.match(ae.SELECT_DROPDOWN);if(W){let v=ue([W[1],W[2],W[3]]),C=ue([W[4],W[5],W[6]]);s.push({originalText:i,type:"SELECT",value:v,target:C,optional:o});continue}let ne=l.match(ae.SCROLL_TO);if(ne){let v=(ne[1]||"down").toLowerCase(),C=ue([ne[2],ne[3],ne[4]]);s.push({originalText:i,type:"SCROLL",target:C,direction:v,optional:o});continue}let x=l.match(ae.SCROLL);if(x){let v=x[1].toLowerCase();s.push({originalText:i,type:"SCROLL",target:v,direction:v,optional:o});continue}let f=l.match(ae.LONG_PRESS);if(f){let v=ue([f[1],f[2],f[3]]);s.push({originalText:i,type:"LONG_PRESS",target:v,optional:o});continue}let F=l.match(ae.ORDINAL_TAP);if(F){let v=F[1].toLowerCase(),C=ue([F[2],F[3],F[4]]);s.push({originalText:i,type:"TAP",target:C,ordinal:v,optional:o});continue}let E=l.match(ae.PROXIMITY_TAP);if(E){let v=ue([E[1],E[2],E[3]]),C=ue([E[4],E[5],E[6]]);s.push({originalText:i,type:"TAP",target:v,relativeTo:C,optional:o});continue}let ge=l.match(ae.TYPE);if(ge){let v=ue([ge[1],ge[2],ge[3]]),C=ue([ge[4],ge[5],ge[6]]);s.push({originalText:i,type:"TYPE",value:v,target:C,optional:o});continue}let Y=l.match(ae.CLEAR);if(Y){let v=ue([Y[1],Y[2],Y[3]]);s.push({originalText:i,type:"CLEAR",target:v,optional:o});continue}let g=l.match(ae.VERIFY);if(g){let v=ue([g[1],g[2],g[3]]),C=(g[4]||"visible").toLowerCase(),A=C.includes("not visible")||C.includes("not present")||C.includes("absent")||C.includes("hidden")||C.includes("gone"),D=A?"absent":C==="exists"||C==="enabled"||C==="disabled"?C:"visible";s.push({originalText:i,type:"VERIFY",target:v,expectedState:D,negative:A,optional:o});continue}let k=l.match(ae.TAP);if(k&&!l.toLowerCase().includes("back")){let v=ue([k[1],k[2],k[3]]);s.push({originalText:i,type:"TAP",target:v,optional:o});continue}let M=l.match(ae.FALLBACK_QUOTED_TAP);if(M&&(l.toLowerCase().startsWith("tap")||l.toLowerCase().startsWith("click"))){s.push({originalText:i,type:"TAP",target:M[1],optional:o});continue}ct.warn(`Unrecognized prompt step: "${n}". Skipping.`)}return oo(s)}function Rr(a,t,e){let s=t.map(r=>(r.text||r.contentDesc||"").trim()).filter(r=>r.length>1),n=Gt(a,s,3),i=n.length>0?`
|
|
29
29
|
\u{1F4A1} Did you mean one of these visible items?
|
|
30
30
|
`+n.map(r=>` \u2022 "${r.text}" (${Math.round(r.score*100)}% match)`).join(`
|
|
31
31
|
`):`
|
|
@@ -827,11 +827,11 @@ Wait for the other run to complete, or remove the lock file if it is stale: ${e}
|
|
|
827
827
|
\u{1F916} PromptTest Universal State-Graph DFS Engine`)),console.log(ce.gray(`Target Package: ${e}`)),console.log(ce.gray(`Safety Policy: ${l.toUpperCase()}`)),console.log(ce.gray(`Limits: Max Screens = ${n}, Step Budget = ${r}, Max Depth = ${i}`)),await this.driver.clearCrashLogs(s);let u=await this.driver.getScreenDimensions(s),h=await this.driver.getForegroundPackage(s);(!h||!h.includes(e))&&(console.log(ce.gray(`Launching ${e}...`)),await this.driver.launchApp(e,s),await new Promise(y=>setTimeout(y,2500)));let{flat:m}=await this.getCleanUiHierarchy(e,s);await this.dismissSoftKeyboard(s);let b=Ks(m,u,e);console.log(ce.magenta(`\u26A1 Universal Paradigm Detected: [${b.paradigm.toUpperCase()}] with ${b.roots.length} root node(s).`));let T=[],w;if(b.paradigm==="drawer"&&b.roots.length>0){w=b.roots[0];let y={...m[0]||{},text:"Home Screen / Main Hub",contentDesc:"Home Screen / Main Hub",clickable:!0,bounds:{x1:0,y1:0,x2:u.width,y2:u.height,centerX:u.width/2,centerY:u.height/2,width:u.width,height:u.height}};T.push(y),console.log(ce.gray(" Opening drawer to catalog root destinations...")),await this.driver.tap(w.bounds.centerX,w.bounds.centerY,s),await new Promise(j=>setTimeout(j,1200));let P=await this.getCleanUiHierarchy(e,s),N=P.flat.filter(j=>{if(!j.clickable||j.bounds.width===0||j.bounds.height===0||j.bounds.width>=u.width*.95&&j.bounds.height>=u.height*.95||Ye([j],u.height,u.width,!0))return!1;let Q=`${j.contentDesc} ${j.text}`.trim(),K=j.bounds.width>u.width*.15&&j.bounds.height>u.height*.05;return Q.toLowerCase().includes("developed by")||j.bounds.centerY>u.height*.85?!1:Q.length>=2||K});for(let j of N)T.some(Q=>Math.abs(Q.bounds.centerX-j.bounds.centerX)<30&&Math.abs(Q.bounds.centerY-j.bounds.centerY)<30)||T.push(j);let O=Ye(P.flat,u.height,u.width,!0);O?await this.driver.tap(O.bounds.centerX,O.bounds.centerY,s):await this.driver.tap(w.bounds.centerX,w.bounds.centerY,s),await new Promise(j=>setTimeout(j,1200));let L=await this.getCleanUiHierarchy(e,s);if(L.flat.some(j=>`${j.contentDesc} ${j.text}`.includes("Gallery")&&`${j.contentDesc} ${j.text}`.includes("Planner"))){let j=Ye(L.flat,u.height,u.width,!0);j?await this.driver.tap(j.bounds.centerX,j.bounds.centerY,s):await this.driver.tap(u.width-50,u.height/2,s),await new Promise(Q=>setTimeout(Q,1e3))}console.log(ce.magenta(` Discovered ${T.length} destinations (1 Main Hub + ${T.length-1} Drawer destinations).`))}else b.roots.length>0?T=b.roots:T=[m[0]||{text:"Main Hub",clickable:!0}];let S=1;for(let y of T){if(this.executedStepCount>=r||this.visitedScreenHashes.size>=n||this.isAborted())break;let P=y.text||y.contentDesc||`Root #${S}`;if(console.log(ce.yellow(`
|
|
828
828
|
[Domain ${S}/${T.length}] Entering Root Tree: "${P}"...`)),this.emitStep({type:"action",description:`Tap "${P}" to enter the ${P} section`,screenTitle:P,depth:0}),b.paradigm==="bottom_tabs"||b.paradigm==="top_tabs"){let L=y.bounds.height<25?Math.max(y.bounds.y1-10,y.bounds.centerY):Math.min(y.bounds.centerY,y.bounds.y2-4);await this.driver.tap(y.bounds.centerX,L,s),this.executedStepCount++,await new Promise(U=>setTimeout(U,1200))}else b.paradigm==="drawer"&&S>1&&w&&(await this.dismissSoftKeyboard(s),console.log(ce.gray(` Opening drawer to select "${P}"...`)),await this.driver.tap(w.bounds.centerX,w.bounds.centerY,s),await new Promise(L=>setTimeout(L,1e3)),await this.driver.tap(y.bounds.centerX,y.bounds.centerY,s),this.executedStepCount++,await new Promise(L=>setTimeout(L,1500)));m=(await this.getCleanUiHierarchy(e,s)).flat,await this.dismissSoftKeyboard(s);let O=S===1?Math.max(25,Math.ceil(r*.45)):Math.max(8,Math.ceil(r*.55/Math.max(1,T.length-1)));if(await this.exploreDfsNode({packageName:e,serial:s,screenDims:u,rootNodes:b.roots,activeRootNode:y,currentDepth:0,maxDepth:i,maxScreens:n,stepBudget:r,domainStepLimit:this.executedStepCount+O,maxInteractionsPerScreen:o,safetyMode:l,safetyBlacklist:c,domainLabel:P}),b.paradigm==="drawer"&&S>1){let L=0;for(;L<4;){let U=await this.getCleanUiHierarchy(e,s),j=yt(U.flat,u.height,u.width),Q=U.flat.some(V=>`${V.contentDesc} ${V.text}`.includes("Gallery")&&`${V.contentDesc} ${V.text}`.includes("Planner"));if(j&&!Q)break;let K=Ye(U.flat,u.height,u.width,!0);if(K)console.log(ce.gray(" [Domain Wrap-Up] Returning to Home Screen via in-app back...")),await this.driver.tap(K.bounds.centerX,K.bounds.centerY,s),await new Promise(V=>setTimeout(V,1e3)),L++;else break}}if(S===1&&T.length===1){let L=await this.getCleanUiHierarchy(e,s),U=Ks(L.flat,u,e);if((U.paradigm==="bottom_tabs"||U.paradigm==="top_tabs")&&U.roots.length>1){console.log(ce.magenta(`
|
|
829
829
|
\u26A1 Post-Auth Navigation Discovered: [${U.paradigm.toUpperCase()}] with ${U.roots.length} root domain(s)!`)),b.paradigm=U.paradigm,b.roots=U.roots;let j=U.roots.slice(1);T.push(...j.length>0?j:U.roots)}}S++}for(let y of this.visitedScreenHashes)this.memory.markScreenCrawled(y);await this.memory.save(),this.isAborted()||this.emitStep({type:"done",description:`Autonomous exploration complete: ${this.visitedScreenHashes.size} screen(s) mapped in ${this.executedStepCount} step(s)`}),console.log(ce.green(`
|
|
830
|
-
\u2714 Universal DFS Traversal Complete!`)),console.log(ce.cyan(` Visited Screens: ${this.visitedScreenHashes.size}`)),console.log(ce.cyan(` Total Steps Executed: ${this.executedStepCount}/${r}`)),console.log(ce.cyan(` Defects Triaged: ${this.devDefectsList.length}`))}async exploreDfsNode(t){let{packageName:e,serial:s,screenDims:n,rootNodes:i,activeRootNode:r,currentDepth:o,maxDepth:l,maxScreens:c,stepBudget:d,domainStepLimit:p,maxInteractionsPerScreen:u,safetyMode:h,safetyBlacklist:m,domainLabel:b}=t;if(this.executedStepCount>=d||this.visitedScreenHashes.size>=c||p&&this.executedStepCount>=p||this.isAborted())return;let{flat:T}=await this.getCleanUiHierarchy(e,s),w=ke(T),S=Ke(T,n);this.visitedScreenHashes.add(w);let y=this.screenLedger.get(S);if(y){if(y.status==="FULLY_EXPLORED"){console.log(ce.gray(` \u26A1 Cycle Detected: Screen [${S.slice(0,8)}] already fully explored. Pruning branch.`));return}}else{let L=this.catalogScreen(T,n,i);y={hash:w,semanticHash:S,title:`${b} (depth ${o})`,elements:L,status:"IN_PROGRESS",depth:o,interactionCount:0},this.screenLedger.set(S,y),console.log(ce.cyan(` \u{1F50E} Discovered Screen [${S.slice(0,8)}] at depth ${o} with ${L.length} candidate element(s).`)),this.emitStep({type:"state_change",description:`Discovered screen "${b}" at depth ${o} with ${L.length} candidate element(s)`,screenHash:S,screenTitle:b,depth:o})}this.callStack.push({screenHash:w,semanticHash:S,title:y.title,timestamp:Date.now()});let P=y.elements.filter(L=>L.type==="input"&&!L.tested);if(P.length>0&&y.interactionCount<u){console.log(ce.magenta(` Filling in-place form fields (${P.length} inputs)...`));let L=await this.formFiller.fillAndSubmitForm(T,s,!0,!1);this.executedStepCount++,y.interactionCount++;for(let U of P)U.tested=!0;await this.dismissSoftKeyboard(s),await this.packageJailGuard(e,s),this.reporter.logStep("In-Place Form Fill",`Populated ${L.filledFields.length} field(s) on [${S.slice(0,8)}]`,"FormSubmit","PASS",L.submitButtonText||"Auto");for(let U of L.filledFields)this.emitStep({type:"action",description:`Type "${U.valueEntered}" in the "${U.fieldLabel}" field`,screenHash:S,screenTitle:b,depth:o});L.submitButtonText&&this.emitStep({type:"action",description:`Tap "${L.submitButtonText}" to submit the form`,screenHash:S,screenTitle:b,depth:o})}let N=y.elements.filter(L=>L.type==="toggle"&&!L.tested);for(let L of N){if(this.executedStepCount>=d||y.interactionCount>=u)break;if(L.tested=!0,Ze(L.node,m,h)){this.safetyHitList.push(L.label),this.emitStep({type:"safety_block",description:`Safety policy [${h.toUpperCase()}] blocked toggle "${L.label}"`,screenHash:S,screenTitle:b,depth:o});continue}console.log(ce.gray(` Exercising toggle: "${L.label.slice(0,30)}"...`)),this.executedStepCount++,y.interactionCount++,await this.driver.tap(L.node.bounds.centerX,L.node.bounds.centerY,s),await new Promise(U=>setTimeout(U,600)),this.reporter.logStep("In-Place Toggle",`Toggled control "${L.label}"`,"Tap","PASS",L.label),this.emitStep({type:"action",description:`Toggle "${L.label}"`,screenHash:S,screenTitle:b,depth:o})}let O=y.elements.filter(L=>L.type==="transition"&&!L.tested);for(let L of O){if(this.executedStepCount>=d||this.visitedScreenHashes.size>=c||p&&this.executedStepCount>=p&&!(o===0&&b.includes("Home Screen"))||y.interactionCount>=u||this.isAborted())break;if(L.tested=!0,qe(L.node)){this.externalSkippedList.push(L.label);continue}if(Ze(L.node,m,h)){this.safetyHitList.push(L.label),this.emitStep({type:"safety_block",description:`Safety policy [${h.toUpperCase()}] blocked transition "${L.label}"`,screenHash:S,screenTitle:b,depth:o});continue}console.log(ce.magenta(` \u26A1 [DFS Depth ${o}] Testing transition: "${L.label.slice(0,40)}"...`)),this.executedStepCount++,y.interactionCount++,await this.driver.tap(L.node.bounds.centerX,L.node.bounds.centerY,s),await new Promise(K=>setTimeout(K,1500)),await this.dismissSoftKeyboard(s),await this.handleExternalMediaChooser(e,s),await this.packageJailGuard(e,s);let U=await this.getCleanUiHierarchy(e,s),j=Ke(U.flat,n),Q=j!==S;if(this.reporter.logStep("DFS Transition",`Tapped "${L.label}" [${Q?"Navigated":"In-Place"}]`,"Tap","PASS",L.label),this.emitStep({type:"action",description:`Tap "${L.label}"${Q?" to navigate to the next screen":""}`,screenHash:S,screenTitle:b,depth:o}),Q&&j&&this.emitStep({type:"state_change",description:`Navigated to screen [${j.slice(0,8)}] via "${L.label}"`,screenHash:j,screenTitle:`${b} > ${L.label.slice(0,20)}`,depth:o+1}),Q){if(o<l){let ye=o===0&&b.includes("Home Screen")?Math.min(this.executedStepCount+3,p||this.executedStepCount+3):p;await this.exploreDfsNode({packageName:e,serial:s,screenDims:n,rootNodes:i,activeRootNode:r,domainStepLimit:ye,currentDepth:o+1,maxDepth:l,maxScreens:c,stepBudget:d,maxInteractionsPerScreen:u,safetyMode:h,safetyBlacklist:m,domainLabel:`${b} > ${L.label.slice(0,20)}`})}console.log(ce.gray(` [DFS Backtrack] Returning to [${S.slice(0,8)}]...`)),await this.safeBacktrack(S,e,s,o===0?r:void 0,o>0),await this.dismissSoftKeyboard(s);let K=await this.getCleanUiHierarchy(e,s);if(Ke(K.flat,n)!==S){let ye=Ye(K.flat,n.height,n.width,!0);ye&&(console.log(ce.gray(" [Backtrack Verification] Re-tapping in-app back/close button...")),await this.driver.tap(ye.bounds.centerX,ye.bounds.centerY,s),await new Promise(
|
|
830
|
+
\u2714 Universal DFS Traversal Complete!`)),console.log(ce.cyan(` Visited Screens: ${this.visitedScreenHashes.size}`)),console.log(ce.cyan(` Total Steps Executed: ${this.executedStepCount}/${r}`)),console.log(ce.cyan(` Defects Triaged: ${this.devDefectsList.length}`))}async exploreDfsNode(t){let{packageName:e,serial:s,screenDims:n,rootNodes:i,activeRootNode:r,currentDepth:o,maxDepth:l,maxScreens:c,stepBudget:d,domainStepLimit:p,maxInteractionsPerScreen:u,safetyMode:h,safetyBlacklist:m,domainLabel:b}=t;if(this.executedStepCount>=d||this.visitedScreenHashes.size>=c||p&&this.executedStepCount>=p||this.isAborted())return;let{flat:T}=await this.getCleanUiHierarchy(e,s),w=ke(T),S=Ke(T,n);this.visitedScreenHashes.add(w);let y=this.screenLedger.get(S);if(y){if(y.status==="FULLY_EXPLORED"){console.log(ce.gray(` \u26A1 Cycle Detected: Screen [${S.slice(0,8)}] already fully explored. Pruning branch.`));return}}else{let L=this.catalogScreen(T,n,i);y={hash:w,semanticHash:S,title:`${b} (depth ${o})`,elements:L,status:"IN_PROGRESS",depth:o,interactionCount:0},this.screenLedger.set(S,y),console.log(ce.cyan(` \u{1F50E} Discovered Screen [${S.slice(0,8)}] at depth ${o} with ${L.length} candidate element(s).`)),this.emitStep({type:"state_change",description:`Discovered screen "${b}" at depth ${o} with ${L.length} candidate element(s)`,screenHash:S,screenTitle:b,depth:o})}this.callStack.push({screenHash:w,semanticHash:S,title:y.title,timestamp:Date.now()});let P=y.elements.filter(L=>L.type==="input"&&!L.tested);if(P.length>0&&y.interactionCount<u){console.log(ce.magenta(` Filling in-place form fields (${P.length} inputs)...`));let L=await this.formFiller.fillAndSubmitForm(T,s,!0,!1);this.executedStepCount++,y.interactionCount++;for(let U of P)U.tested=!0;await this.dismissSoftKeyboard(s),await this.packageJailGuard(e,s),this.reporter.logStep("In-Place Form Fill",`Populated ${L.filledFields.length} field(s) on [${S.slice(0,8)}]`,"FormSubmit","PASS",L.submitButtonText||"Auto");for(let U of L.filledFields)this.emitStep({type:"action",description:`Type "${U.valueEntered}" in the "${U.fieldLabel}" field`,screenHash:S,screenTitle:b,depth:o});L.submitButtonText&&this.emitStep({type:"action",description:`Tap "${L.submitButtonText}" to submit the form`,screenHash:S,screenTitle:b,depth:o})}let N=y.elements.filter(L=>L.type==="toggle"&&!L.tested);for(let L of N){if(this.executedStepCount>=d||y.interactionCount>=u)break;if(L.tested=!0,Ze(L.node,m,h)){this.safetyHitList.push(L.label),this.emitStep({type:"safety_block",description:`Safety policy [${h.toUpperCase()}] blocked toggle "${L.label}"`,screenHash:S,screenTitle:b,depth:o});continue}console.log(ce.gray(` Exercising toggle: "${L.label.slice(0,30)}"...`)),this.executedStepCount++,y.interactionCount++,await this.driver.tap(L.node.bounds.centerX,L.node.bounds.centerY,s),await new Promise(U=>setTimeout(U,600)),this.reporter.logStep("In-Place Toggle",`Toggled control "${L.label}"`,"Tap","PASS",L.label),this.emitStep({type:"action",description:`Toggle "${L.label}"`,screenHash:S,screenTitle:b,depth:o})}let O=y.elements.filter(L=>L.type==="transition"&&!L.tested);for(let L of O){if(this.executedStepCount>=d||this.visitedScreenHashes.size>=c||p&&this.executedStepCount>=p&&!(o===0&&b.includes("Home Screen"))||y.interactionCount>=u||this.isAborted())break;if(L.tested=!0,qe(L.node)){this.externalSkippedList.push(L.label);continue}if(Ze(L.node,m,h)){this.safetyHitList.push(L.label),this.emitStep({type:"safety_block",description:`Safety policy [${h.toUpperCase()}] blocked transition "${L.label}"`,screenHash:S,screenTitle:b,depth:o});continue}console.log(ce.magenta(` \u26A1 [DFS Depth ${o}] Testing transition: "${L.label.slice(0,40)}"...`)),this.executedStepCount++,y.interactionCount++,await this.driver.tap(L.node.bounds.centerX,L.node.bounds.centerY,s),await new Promise(K=>setTimeout(K,1500)),await this.dismissSoftKeyboard(s),await this.handleExternalMediaChooser(e,s),await this.packageJailGuard(e,s);let U=await this.getCleanUiHierarchy(e,s),j=Ke(U.flat,n),Q=j!==S;if(this.reporter.logStep("DFS Transition",`Tapped "${L.label}" [${Q?"Navigated":"In-Place"}]`,"Tap","PASS",L.label),this.emitStep({type:"action",description:`Tap "${L.label}"${Q?" to navigate to the next screen":""}`,screenHash:S,screenTitle:b,depth:o}),Q&&j&&this.emitStep({type:"state_change",description:`Navigated to screen [${j.slice(0,8)}] via "${L.label}"`,screenHash:j,screenTitle:`${b} > ${L.label.slice(0,20)}`,depth:o+1}),Q){if(o<l){let ye=o===0&&b.includes("Home Screen")?Math.min(this.executedStepCount+3,p||this.executedStepCount+3):p;await this.exploreDfsNode({packageName:e,serial:s,screenDims:n,rootNodes:i,activeRootNode:r,domainStepLimit:ye,currentDepth:o+1,maxDepth:l,maxScreens:c,stepBudget:d,maxInteractionsPerScreen:u,safetyMode:h,safetyBlacklist:m,domainLabel:`${b} > ${L.label.slice(0,20)}`})}console.log(ce.gray(` [DFS Backtrack] Returning to [${S.slice(0,8)}]...`)),await this.safeBacktrack(S,e,s,o===0?r:void 0,o>0),await this.dismissSoftKeyboard(s);let K=await this.getCleanUiHierarchy(e,s);if(Ke(K.flat,n)!==S){let ye=Ye(K.flat,n.height,n.width,!0);ye&&(console.log(ce.gray(" [Backtrack Verification] Re-tapping in-app back/close button...")),await this.driver.tap(ye.bounds.centerX,ye.bounds.centerY,s),await new Promise(ne=>setTimeout(ne,1e3)),await this.packageJailGuard(e,s));let re=await this.getCleanUiHierarchy(e,s);if(Ke(re.flat,n)!==S){console.log(ce.yellow(` \u26A0\uFE0F Unable to restore parent screen [${S.slice(0,8)}]. Unwinding branch to prevent jailbreak loop.`));break}}}}y.status="FULLY_EXPLORED",this.callStack.pop()}}});import wo from"fs";import an from"path";import _ from"chalk";var Ht,Ds,qt=ie(()=>{"use strict";Re();Ve();je();et();ls();Tt();Pt();us();Vr();Es();Ht=class{driver;formFiller;reporter;memory;safetyHitList=[];externalSkippedList=[];devDefectsList=[];visitedScreenHashes=new Set;executedStepCount=0;constructor(t,e,s,n){this.driver=t||oe,this.formFiller=e||Ur,this.reporter=s||xt,this.memory=n||Me}getMemory(){return this.memory}getSafetyHitList(){return this.safetyHitList}getExternalSkippedList(){return this.externalSkippedList}getDevDefectsList(){return this.devDefectsList}getVisitedScreensCount(){return this.visitedScreenHashes.size}async packageJailGuard(t,e){let s=await this.driver.getForegroundPackage(e);if(s&&!s.includes(t)){let n=_t(s);console.log(_.yellow(` \u{1F6E1}\uFE0F Jail Containment: App escaped to "${s}". Executing containment recovery...`)),n||(await this.driver.pressBack(e),await new Promise(r=>setTimeout(r,600)));let i=await this.driver.getForegroundPackage(e);return(!i||!i.includes(t))&&(console.log(_.gray(` Force re-launching ${t} to restore foreground...`)),await this.driver.launchApp(t,e),await new Promise(r=>setTimeout(r,1200))),this.reporter.logStep("Jail Containment",`App escaped to "${s}"; successfully contained and restored "${t}"`,"Back","PASS"),!0}return!1}async getCleanUiHierarchy(t,e,s=5){let n=await this.driver.getUiHierarchy(e),{flat:i}=be(n),r=0;for(;r<s;){let o=cs(i);if(!o)break;r++;let l=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,c=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(_.red(` \u{1F6A8} Global Dev Console Intercepted: ${l} "${o.errorText.slice(0,80)}"${c}`)),this.devDefectsList.push(o.sourceLocation?`${o.errorText} (${o.sourceLocation})`:o.errorText);let d=await this.driver.captureScreenshot(e);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${l}: "${o.errorText}"${c}`,"Verify","FAIL",o.errorText,d,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(_.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,e)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(_.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,e)):(console.log(_.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(e)),await new Promise(p=>setTimeout(p,800)),n=await this.driver.getUiHierarchy(e),i=be(n).flat}return{xml:n,flat:i}}async handleDevOverlays(t,e,s){let n=cs(t);if(!n)return!1;let i=`[${(n.severity||"error").toUpperCase()} - ${n.type.toUpperCase()}]`,r=n.sourceLocation?` [${n.sourceLocation}]`:"";console.log(_.red(` \u{1F6A8} Dev Overlay Detected: ${i} "${n.errorText.slice(0,80)}"${r}`)),this.devDefectsList.push(n.sourceLocation?`${n.errorText} (${n.sourceLocation})`:n.errorText);let o=await this.driver.captureScreenshot(s);return this.reporter.logStep("Dev Overlay Triage",`Detected and triaged ${i} error: "${n.errorText}"${r}`,"Verify","FAIL",n.errorText,o,{errors:[n.sourceLocation?`${n.errorText} (${n.sourceLocation})`:n.errorText]}),n.dismissButton&&n.dismissButton.bounds.width>0?(console.log(_.gray(" Dismissing error overlay via dismiss button...")),await this.driver.tap(n.dismissButton.bounds.centerX,n.dismissButton.bounds.centerY,s)):n.isToast&&n.node&&n.node.bounds.width>0?(console.log(_.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(n.node.bounds.centerX,n.node.bounds.centerY,s)):(console.log(_.gray(" Pressing Back to dismiss error overlay...")),await this.driver.pressBack(s)),await new Promise(l=>setTimeout(l,800)),!0}async dismissSoftKeyboard(t){try{await this.driver.pressKey(111,t),await new Promise(e=>setTimeout(e,200))}catch{}}async explore(t){let{packageName:e,serial:s,maxTabs:n=R.explorer?.maxTabs||15,exploreLists:i=!0,skipAlreadyCrawled:r=!1,safetyMode:o=R.safety?.mode||"strict",safetyBlacklist:l=R.safety?.customBlacklist?[...R.safety.customBlacklist]:[],maxScreens:c=R.explorer?.maxScreens||30,stepBudget:d=R.explorer?.stepBudget||100,screenshots:p=R.explorer?.screenshots||"state-change",video:u=t.video??!1,onStep:h,abortSignal:m}=t;console.log(_.cyan(`
|
|
831
831
|
\u{1F916} PromptTest Autonomous Exploration Brain v3`)),console.log(_.gray("Target Package: ")+_.bold.white(e)),console.log(_.gray("Safety Policy: ")+_.bold.yellow(o.toUpperCase())+(l.length>0?_.gray(` (+${l.length} custom blacklist)`):"")),console.log(_.gray("Screenshots Policy: ")+_.bold.white(p));let b=[],T=0,w=L=>{T++,h?.({...L,stepNumber:T,packageName:e,totalSteps:L.totalSteps??this.executedStepCount,discoveredScreens:L.discoveredScreens??this.visitedScreenHashes.size})},S=async(L,U=!0)=>{if(!(p==="failure-only"&&L!=="FAIL")&&!(p==="state-change"&&!U&&L!=="FAIL"))try{return await this.driver.captureScreenshot(s)}catch{return}};await this.memory.init(e);let y=s||this.driver.getDefaultDevice(),P=y||"Default Device";this.reporter.init(e,P);let N=null;y&&(N=Ft(y));let O=null;this.safetyHitList=[],this.externalSkippedList=[],this.devDefectsList=[],this.visitedScreenHashes.clear(),this.executedStepCount=0;try{if(await this.driver.clearCrashLogs(s),u&&(console.log(_.cyan("\u{1F3AC} Starting ADB screen recording for exploration (--video enabled)...")),O=await this.driver.startScreenRecording("/sdcard/prompttest_explore_temp.mp4",s)),(t.strategy||R.explorer?.strategy||"heuristic")==="dfs"){let k=new As(this.driver,this.reporter,this.memory);await k.explore({packageName:e,serial:s,maxScreens:c,maxDepth:t.maxDepth||R.explorer?.maxDepth||5,stepBudget:d,safetyMode:o,safetyBlacklist:l,screenshotPolicy:p,skipAlreadyCrawled:r,onStep:v=>w({type:v.type,description:v.description,screenHash:v.screenHash,screenTitle:v.screenTitle,depth:v.depth,elementCount:v.elementCount,totalSteps:v.totalSteps,discoveredScreens:v.discoveredScreens}),abortSignal:m}),this.safetyHitList=k.safetyHitList,this.externalSkippedList=k.externalSkippedList,this.devDefectsList=k.devDefectsList,this.visitedScreenHashes=k.visitedScreenHashes,this.executedStepCount=k.executedStepCount;let M=this.memory.getStats();this.reporter.logStep("Coverage Audit",`State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s). Memory Engine cached ${M.totalScreens} crawled screen(s).`,"Verify","PASS"),await this.reporter.generateReport(),console.log(_.bold.green(`
|
|
832
832
|
\u2728 Full Audit Report generated at: output/prompttest-report.md`));return}let{width:U,height:j}=await this.driver.getScreenDimensions(s);console.log(_.yellow(`
|
|
833
833
|
[1/5] Verifying Foreground State...`));let Q=await this.driver.getForegroundPackage(s);(!Q||!Q.includes(e))&&(console.log(_.gray(`Launching ${e}...`)),await this.driver.launchApp(e,s),await new Promise(k=>setTimeout(k,2500)));let K=await this.runErrorSentinel(e,s,"Post-Launch"),V=K.hasError?"FAIL":"PASS",ye=await S(V,!0);this.reporter.logStep("Init",`Launched and verified foreground for ${e}`,"Launch",V,e,ye,K.hasError?{errors:K.errors}:void 0),console.log(V==="PASS"?_.green("\u2714 App is active in foreground."):_.red(`\u2716 Issues detected during launch for ${e}.`)),console.log(_.yellow(`
|
|
834
|
-
[2/5] Inspecting UI Hierarchy for Forms...`));let{xml:re,flat:W}=await this.getCleanUiHierarchy(e,s),
|
|
834
|
+
[2/5] Inspecting UI Hierarchy for Forms...`));let{xml:re,flat:W}=await this.getCleanUiHierarchy(e,s),ne=ke(W);if(this.visitedScreenHashes.add(ne),this.memory.isScreenCrawled(ne)&&r)console.log(_.cyan(` \u26A1 Fast-Forward: Screen [${ne.slice(0,8)}] was previously crawled offline. Skipping redundant element interactions.`)),this.reporter.logStep("Fast Forward",`Fast-forwarded past previously crawled screen [${ne.slice(0,8)}] (Memory Engine)`,"Skip","PASS");else{let k=Fe(W);if(k.length>0){console.log(_.magenta(`\u26A1 Detected ${k.length} form field(s)! Engaging Semantic Form Filler...`));for(let q of k)console.log(_.gray(` - Field: "${q.text||q.contentDesc||q.resourceId}"`));let A=await this.formFiller.fillAndSubmitForm(W,s,!0,!1);this.executedStepCount++,await new Promise(q=>setTimeout(q,1500)),await this.dismissSoftKeyboard(s),await this.packageJailGuard(e,s);let D=await this.getCleanUiHierarchy(e,s);re=D.xml,W=D.flat,this.visitedScreenHashes.add(ke(W));let I=await this.runErrorSentinel(e,s,"Post-Form-Submit"),H=Fe(W),G=I.hasError||H.length===k.length&&I.errors.length>0?"FAIL":"PASS",Z=await S(G,!0),X=A.filledFields.map(q=>`${q.fieldLabel} -> "${q.valueEntered}"`).join(", ");this.reporter.logStep("Form Fill",`Filled ${A.filledFields.length} field(s) [${X}] and submitted via "${A.submitButtonText||"Auto"}"`,"FormSubmit",G,A.submitButtonText,Z,I.hasError?{errors:I.errors}:void 0),console.log(G==="PASS"?_.green("\u2714 Form populated and submitted successfully!"):_.red("\u2716 Form submission triggered an error or remained unauthenticated."))}else console.log(_.gray("No form input fields detected on initial screen."));console.log(_.yellow(`
|
|
835
835
|
[3/5] Inspecting Interactive Action Controls...`));let M=Oe(W).filter(A=>{let D=(A.text+" "+A.contentDesc).toLowerCase();if(D.includes("submit")||D.includes("cancel")||yt([A],j))return!1;if(qe(A)){let I=A.text||A.contentDesc||A.resourceId||"External Link";return console.log(_.gray(` \u{1F517} External Link Bypassed: "${I}"`)),this.externalSkippedList.push(I),!1}if(Ze(A,l,o)){let I=A.text||A.contentDesc||A.resourceId||"Action";return console.log(_.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive action "${I}"`)),this.safetyHitList.push(I),!1}return!0});if(M.length>0){let A=M[0],D=A.text||A.contentDesc||A.resourceId;console.log(_.magenta(`\u26A1 Found action button: "${D}". Testing interaction...`));let I=ke(W);this.executedStepCount++,await this.driver.tap(A.bounds.centerX,A.bounds.centerY,s),await new Promise(Pe=>setTimeout(Pe,1200)),await this.packageJailGuard(e,s);let H=await this.getCleanUiHierarchy(e,s);re=H.xml,W=H.flat;let z=ke(W);this.visitedScreenHashes.add(z);let G=await this.runErrorSentinel(e,s,"Post-Action-Tap"),Z=I!==z,X="PASS",q="";G.hasError?(X="FAIL",q="Action triggered error or crash"):Z?(X="PASS",q="Screen transitioned successfully"):(X="WARN",q="No visible screen transition or state change observed (No-Op)",console.log(_.yellow(` \u26A0\uFE0F Outcome Warning: Action "${D}" produced no UI state change.`)));let Se=await S(X,Z);this.reporter.logStep("Action Testing",`Tapped action button "${D}" [${q}]`,"Tap",X,D,Se,G.hasError?{errors:G.errors}:void 0),console.log(X==="PASS"?_.green(`\u2714 Action interaction complete (${q}).`):X==="WARN"?_.yellow(`\u2714 Action interaction recorded with warning (${q}).`):_.red("\u2716 Action interaction triggered an error.")),X!=="FAIL"&&(this.memory.recordSuccess("TAP",D,A.resourceId||A.text||A.contentDesc||D,"text",1,void 0,I),this.memory.markScreenCrawled(I,[D]))}else console.log(_.gray("No standalone action buttons to test."));let v=Xt(W).filter(A=>!Ze(A,l,o));if(v.length>0){console.log(_.yellow(`
|
|
836
836
|
[3b/5] Inspecting Toggle & Checkable Controls...`));let A=v[0],D=A.text||A.contentDesc||A.resourceId||"Switch/Checkbox",I=A.checked;console.log(_.magenta(`\u26A1 Found toggle: "${D}" (checked: ${I}). Testing toggle interaction...`)),this.executedStepCount++,await this.driver.tap(A.bounds.centerX,A.bounds.centerY,s),await new Promise(Pe=>setTimeout(Pe,1e3)),re=await this.driver.getUiHierarchy(s),W=be(re).flat;let H=ke(W);this.visitedScreenHashes.add(H);let G=Xt(W).find(Pe=>Math.abs(Pe.bounds.centerX-A.bounds.centerX)<25&&Math.abs(Pe.bounds.centerY-A.bounds.centerY)<25),Z=G?G.checked!==I:!0,X=await this.runErrorSentinel(e,s,"Post-Toggle"),q=X.hasError?"FAIL":Z?"PASS":"WARN",Se=await S(q,Z);this.reporter.logStep("Toggle Testing",`Toggled "${D}" (Checked: ${I} -> ${G?G.checked:"flipped"})`,"Toggle",q,D,Se,X.hasError?{errors:X.errors}:void 0),console.log(q==="PASS"?_.green("\u2714 Toggle control flipped successfully!"):q==="WARN"?_.yellow("\u26A0\uFE0F Toggle tapped but checked state did not flip."):_.red("\u2716 Toggle control triggered an error.")),q!=="FAIL"&&this.memory.markScreenCrawled(H,[D])}let C=yt(W,j);if(C&&!Ze(C,l,o)){let A=C.contentDesc||C.text||C.resourceId||"Drawer Menu";console.log(_.yellow(`
|
|
837
837
|
[3c/5] Inspecting Side Navigation Drawer...`)),console.log(_.magenta(`\u26A1 Found drawer trigger: "${A}". Opening side drawer...`));let D=ke(W);this.executedStepCount++,await this.driver.tap(C.bounds.centerX,C.bounds.centerY,s),await new Promise(q=>setTimeout(q,1200)),await this.packageJailGuard(e,s);let I=await this.getCleanUiHierarchy(e,s);re=I.xml,W=I.flat;let H=ke(W);this.visitedScreenHashes.add(H);let z=D!==H,G=await this.runErrorSentinel(e,s,"Drawer-Open"),Z=G.hasError?"FAIL":z?"PASS":"WARN",X=await S(Z,z);if(this.reporter.logStep("Drawer Navigation",`Opened side drawer via "${A}"`,"Tap",Z,A,X,G.hasError?{errors:G.errors}:void 0),Z==="PASS"){console.log(_.green("\u2714 Side drawer opened successfully!"));let q=Oe(W).filter(Pe=>!Ze(Pe,l,o)&&!qe(Pe)&&Pe.bounds.centerY>C.bounds.y2);if(q.length>0){let Pe=q[0],Xe=Pe.text||Pe.contentDesc||Pe.resourceId||"Drawer Item";console.log(_.gray(` - Testing drawer item: "${Xe}"...`)),this.executedStepCount++,await this.driver.tap(Pe.bounds.centerX,Pe.bounds.centerY,s),await new Promise(xe=>setTimeout(xe,1200)),await this.packageJailGuard(e,s);let $t=await this.getCleanUiHierarchy(e,s);re=$t.xml,W=$t.flat,this.visitedScreenHashes.add(ke(W))}console.log(_.gray(" Closing side drawer to restore main view...")),await this.driver.pressBack(s),await new Promise(Pe=>setTimeout(Pe,1e3));let Se=await this.getCleanUiHierarchy(e,s);re=Se.xml,W=Se.flat,this.visitedScreenHashes.add(ke(W)),console.log(_.green("\u2714 Main view restored from side drawer."))}else Z==="WARN"&&console.log(_.yellow("\u26A0\uFE0F Drawer button tapped but no visible screen change occurred."));Z!=="FAIL"&&this.memory.markScreenCrawled(D,[A])}if(i){console.log(_.yellow(`
|
|
@@ -898,33 +898,32 @@ ${c.errors.join(`
|
|
|
898
898
|
</hierarchy>`;if(this.isMock||process.platform!=="darwin")return e;if(this.wdaBaseUrl)try{let s=await fetch(`${this.wdaBaseUrl}/source?format=xml`);if(s.ok)return await s.text()}catch{}return e}async tap(t,e,s){let n=await this.ensureDefaultDevice(s);if(!(this.isMock||process.platform!=="darwin")){if(this.wdaBaseUrl){await fetch(`${this.wdaBaseUrl}/wda/tap/nil`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({x:t,y:e})}).catch(()=>{});return}await new Promise(i=>{Wt("idb",["ui","tap",`${t}`,`${e}`,"--udid",n],()=>i())})}}async inputText(t,e){let s=await this.ensureDefaultDevice(e);if(!(this.isMock||process.platform!=="darwin")){if(this.wdaBaseUrl){await fetch(`${this.wdaBaseUrl}/wda/keys`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:t.split("")})}).catch(()=>{});return}await new Promise(n=>{Wt("idb",["ui","type",t,"--udid",s],()=>n())})}}async pressKey(t,e){this.isMock||process.platform!=="darwin"||(await this.ensureDefaultDevice(e),this.wdaBaseUrl&&await fetch(`${this.wdaBaseUrl}/wda/keys`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:[t]})}).catch(()=>{}))}async swipe(t,e,s,n,i=300,r){this.isMock||process.platform!=="darwin"||(await this.ensureDefaultDevice(r),this.wdaBaseUrl&&await fetch(`${this.wdaBaseUrl}/wda/dragfromtoforduration`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({fromX:t,fromY:e,toX:s,toY:n,duration:i/1e3})}).catch(()=>{}))}async launchApp(t,e){let s=await this.ensureDefaultDevice(e);if(!(this.isMock||process.platform!=="darwin"))return new Promise(n=>{Wt("xcrun",["simctl","launch",s,t],()=>n())})}async forceStop(t,e){let s=await this.ensureDefaultDevice(e);if(!(this.isMock||process.platform!=="darwin"))return new Promise(n=>{Wt("xcrun",["simctl","terminate",s,t],()=>n())})}async isAppInstalled(t,e){let s=await this.ensureDefaultDevice(e);return this.isMock||process.platform!=="darwin"?!0:new Promise(n=>{Wt("xcrun",["simctl","get_app_container",s,t],i=>{n(!i)})})}async getForegroundPackage(t){return"com.apple.mobilesafari"}}});var li={};Qe(li,{startLiveServer:()=>Zn});import $o from"http";import te from"fs";import ee from"path";import ci from"os";import{fileURLToPath as Co}from"url";async function Zn(a={}){let t=a.port??4040,e=a.host||"127.0.0.1",s=process.cwd();(s==="/"||s==="\\")&&(s=ci.homedir?ci.homedir():process.env.HOME||process.env.USERPROFILE||"/tmp");let n=a.outputDir?ee.resolve(a.outputDir):ee.resolve(s,"output"),i=a.suiteName||"PromptTest Live Monitor",r=a.initialReport||{packageName:"PromptTest Execution",deviceId:"Live Device",startTime:new Date().toISOString(),endTime:new Date().toISOString(),durationSeconds:0,totalSteps:0,passedSteps:0,failedSteps:0,skippedSteps:0,warnSteps:0,errorsDetected:[],steps:[]},o=new Set,l=setInterval(()=>{for(let x of o)try{x.write(`: heartbeat
|
|
899
899
|
|
|
900
900
|
`)}catch{o.delete(x)}},15e3),c=a.driver||new lt,d=ee.dirname(Co(import.meta.url)),p=a.specsDir?ee.resolve(a.specsDir):ee.resolve(process.cwd(),"specs");if(!te.existsSync(p)){let x=ee.resolve(d,"../specs");te.existsSync(x)&&(p=x)}let u=null;function h(){return u||(u=new st(c)),u}let m=ee.join(n,"baselines"),b=new Ut(m),T=null,w=null,S=["strict","moderate","interactive","disabled"],y=null,P=0;function N(x){return x?{success:!0,sessionId:x.id,status:x.status,packageName:x.packageName,deviceId:x.deviceId,safetyMode:x.safetyMode,maxDepth:x.maxDepth,maxSteps:x.maxSteps,maxScreens:x.maxScreens,screensDiscovered:x.screensDiscovered,elementsMapped:x.elementsMapped,stepsTaken:x.stepsTaken,startedAt:x.startedAt,finishedAt:x.finishedAt,crashes:x.crashes,screens:x.screens,blocked:x.blocked,events:x.events,specLines:x.specLines,error:x.error}:{success:!0,sessionId:null,status:"idle",packageName:"",safetyMode:"strict",screensDiscovered:0,elementsMapped:0,stepsTaken:0,maxDepth:0,maxSteps:0,crashes:[],screens:[],blocked:[],events:[],specLines:[]}}function O(x){let f=x.match(/"([^"]+)"/);return f?f[1]:null}function L(x){let f=x.specLines.length>0?x.specLines:["Launch the app"],F=["# Auto-generated by PromptTest Auto-Pilot (Autonomous Explorer)",`# Package: ${x.packageName}`,`# Safety Policy: ${x.safetyMode.toUpperCase()} | Max Depth: ${x.maxDepth} | Step Budget: ${x.maxSteps}`,`# Explored at: ${x.startedAt}`,`# Screens Discovered: ${x.screensDiscovered} | Steps Executed: ${x.stepsTaken}`];return x.deviceId&&F.push(`# Device: ${x.deviceId}`),[...F,"",...f,""].join(`
|
|
901
|
-
`)}function U(x,f,F){x.status=f,x.finishedAt=new Date().toISOString(),F&&(x.error=F),K("explore",{...N(x),event:{type:f==="failed"?"error":"done",description:F||f}})}function j(x){let f=y;if(f){if(typeof x.totalSteps=="number"&&(f.stepsTaken=Math.max(f.stepsTaken,x.totalSteps)),typeof x.discoveredScreens=="number"&&(f.screensDiscovered=Math.max(f.screensDiscovered,x.discoveredScreens)),x.type==="action"){let F=O(x.description);F&&f.elementLabels.add(F),x.description&&f.specLines[f.specLines.length-1]!==x.description&&f.specLines.push(x.description)}if(x.screenHash){let F=x.screenTitle||x.description||x.screenHash.slice(0,8),E=f.screens.find(ge=>ge.hash===x.screenHash);E?(E.visits++,typeof x.elementCount=="number"&&(E.elements=x.elementCount),E.label===E.hash.slice(0,8)&&x.screenTitle&&(E.label=x.screenTitle,E.title=x.screenTitle)):f.screens.push({id:x.screenHash,hash:x.screenHash,depth:x.depth??f.screens.length,title:F,label:F,visits:1,elements:x.elementCount??0}),f.screensDiscovered=Math.max(f.screensDiscovered,f.screens.length)}if(x.type==="crash"){let F=new Date().toISOString(),E=x.screenHash,ge=E?f.screens.find(Y=>Y.hash===E):void 0;f.crashes.push({id:E||`crash-${f.crashes.length+1}`,type:x.description.toLowerCase().includes("anr")?"anr":"crash",message:x.description,screen:ge?.label||E?.slice(0,8),timestamp:F,description:x.description,screenHash:E})}x.type==="safety_block"&&f.blocked.push(x.description),f.elementsMapped=f.elementLabels.size,f.events.push(x),f.events.length>300&&f.events.shift(),K("explore",{...N(f),event:x})}}function Q(x){let f=S.includes(x.safetyMode||"")?x.safetyMode:"strict";P++;let F={id:`explore-${Date.now()}-${P}`,packageName:x.packageName,deviceId:x.deviceId,safetyMode:f,maxDepth:x.maxDepth??5,maxSteps:x.maxSteps??100,maxScreens:x.maxScreens??30,status:"running",startedAt:new Date().toISOString(),screensDiscovered:0,elementsMapped:0,stepsTaken:0,screens:[],elementLabels:new Set,crashes:[],blocked:[],events:[],specLines:[],abort:{aborted:!1}};y=F;let E=new Ht(c);return K("explore",{...N(F),event:{type:"state_change",description:`Auto-Pilot launched on ${F.packageName} (${F.safetyMode} mode)`}}),E.explore({packageName:F.packageName,serial:F.deviceId,strategy:"dfs",safetyMode:F.safetyMode,maxDepth:F.maxDepth,maxScreens:F.maxScreens,stepBudget:F.maxSteps,screenshots:"state-change",onStep:j,abortSignal:F.abort}).then(()=>{U(F,F.abort.aborted?"stopped":"completed")}).catch(ge=>{U(F,F.abort.aborted?"stopped":"failed",ge instanceof Error?ge.message:String(ge))}),F}function K(x,f){let F=`event: ${x}
|
|
901
|
+
`)}function U(x,f,F){x.status=f,x.finishedAt=new Date().toISOString(),F&&(x.error=F),K("explore",{...N(x),event:{type:f==="failed"?"error":"done",description:F||f}})}function j(x){let f=y;if(f){if(typeof x.totalSteps=="number"&&(f.stepsTaken=Math.max(f.stepsTaken,x.totalSteps)),typeof x.discoveredScreens=="number"&&(f.screensDiscovered=Math.max(f.screensDiscovered,x.discoveredScreens)),x.type==="action"){let F=O(x.description);F&&f.elementLabels.add(F),x.description&&f.specLines[f.specLines.length-1]!==x.description&&f.specLines.push(x.description)}if(x.screenHash){let F=x.screenTitle||x.description||x.screenHash.slice(0,8),E=f.screens.find(ge=>ge.hash===x.screenHash);E?(E.visits++,typeof x.elementCount=="number"&&(E.elements=x.elementCount),E.label===E.hash.slice(0,8)&&x.screenTitle&&(E.label=x.screenTitle,E.title=x.screenTitle)):f.screens.push({id:x.screenHash,hash:x.screenHash,depth:x.depth??f.screens.length,title:F,label:F,visits:1,elements:x.elementCount??0}),f.screensDiscovered=Math.max(f.screensDiscovered,f.screens.length)}if(x.type==="crash"){let F=new Date().toISOString(),E=x.screenHash,ge=E?f.screens.find(Y=>Y.hash===E):void 0;f.crashes.push({id:E||`crash-${f.crashes.length+1}`,type:x.description.toLowerCase().includes("anr")?"anr":"crash",message:x.description,screen:ge?.label||E?.slice(0,8),timestamp:F,description:x.description,screenHash:E})}x.type==="safety_block"&&f.blocked.push(x.description),f.elementsMapped=f.elementLabels.size,f.events.push(x),f.events.length>300&&f.events.shift(),K("explore",{...N(f),event:x})}}function Q(x){let f=S.includes(x.safetyMode||"")?x.safetyMode:"strict";P++;let F={id:`explore-${Date.now()}-${P}`,packageName:x.packageName,deviceId:x.deviceId,safetyMode:f,maxDepth:x.maxDepth??5,maxSteps:x.maxSteps??100,maxScreens:x.maxScreens??30,status:"running",startedAt:new Date().toISOString(),screensDiscovered:0,elementsMapped:0,stepsTaken:0,screens:[],elementLabels:new Set,crashes:[],blocked:[],events:[],specLines:[],abort:{aborted:!1}};y=F;let E=a.explorerFactory?a.explorerFactory(c):new Ht(c);return K("explore",{...N(F),event:{type:"state_change",description:`Auto-Pilot launched on ${F.packageName} (${F.safetyMode} mode)`}}),E.explore({packageName:F.packageName,serial:F.deviceId,strategy:"dfs",safetyMode:F.safetyMode,maxDepth:F.maxDepth,maxScreens:F.maxScreens,stepBudget:F.maxSteps,screenshots:"state-change",onStep:j,abortSignal:F.abort}).then(()=>{U(F,F.abort.aborted?"stopped":"completed")}).catch(ge=>{U(F,F.abort.aborted?"stopped":"failed",ge instanceof Error?ge.message:String(ge))}),F}function K(x,f){let F=`event: ${x}
|
|
902
902
|
data: ${JSON.stringify(f)}
|
|
903
903
|
|
|
904
904
|
`;for(let E of o)try{E.write(F)}catch{o.delete(E)}}function V(x){return new Promise((f,F)=>{let E="";x.on("data",ge=>{E+=ge}),x.on("end",()=>{try{f(E?JSON.parse(E):{})}catch{F(new Error("Invalid JSON payload"))}}),x.on("error",F)})}let ye=$o.createServer(async(x,f)=>{if(f.setHeader("Access-Control-Allow-Origin","*"),f.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),f.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),x.method==="OPTIONS"){f.writeHead(204),f.end();return}let F=new URL(x.url||"/",`http://${e}:${t}`),E=decodeURIComponent(F.pathname);if(E==="/api/devices"){try{let[g,k]=await Promise.all([c.getRawConnectedDevices().catch(()=>[]),new Rs().getConnectedDevices().catch(()=>[])]),M=c.deduplicateDevices(g).map(A=>({...A,platform:"android"})),v=(k||[]).map(A=>({serial:A.serial,state:A.state,model:A.model||"iOS Device",product:"iOS",connectionType:A.state==="simulator"?"emulator":"usb",platform:"ios"})),C=[...M,...v];f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,devices:C,allEndpoints:g}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/screen-state"){try{let g=F.searchParams.get("deviceId")||void 0,k=F.searchParams.get("hierarchy")!=="false",[M,v,C]=await Promise.all([c.captureScreenshot(g),k?c.getUiHierarchy(g):Promise.resolve(null),c.getForegroundPackage(g)]),A=[];if(v){let{flat:D}=be(v);A=D.map(I=>({index:I.index,text:I.text,resourceId:I.resourceId,className:I.className,contentDesc:I.contentDesc,clickable:I.clickable,scrollable:I.scrollable,bounds:I.bounds}))}f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,deviceId:g||c.getDefaultDevice()||"default",foregroundApp:C,screenshot:`data:image/png;base64,${M.toString("base64")}`,elements:k?A:void 0,elementCount:A.length}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/tap"&&x.method==="POST"){try{let g=await V(x);if(typeof g.x!="number"||typeof g.y!="number"){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing numeric coordinates: x and y"}));return}await c.tap(g.x,g.y,g.deviceId),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,tapped:{x:g.x,y:g.y}}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/type"&&x.method==="POST"){try{let g=await V(x);if(typeof g.text!="string"){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing text string to type"}));return}await c.inputText(g.text,g.deviceId),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,typed:g.text}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/key"&&x.method==="POST"){try{let g=await V(x);if(g.keyCode===void 0){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing keyCode parameter"}));return}await c.pressKey(g.keyCode,g.deviceId),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,keyCode:g.keyCode}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/wake"&&x.method==="POST"){try{let g=await V(x);await c.wakeDevice(g.deviceId),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/wifi/connect"&&x.method==="POST"){try{let g=await V(x),k=await c.connectWifi(g.target,g.port||5555,g.deviceId);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:k.success,target:k.target,message:k.message}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/wifi/disconnect"&&x.method==="POST"){try{let g=await V(x),k=await c.disconnectWifi(g.target);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:k}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/wifi/pair"&&x.method==="POST"){try{let g=await V(x);if(!g.target||!g.pairingCode){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing target or pairingCode"}));return}let k=await c.pairWifi(g.target,g.pairingCode);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify(k))}catch(g){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/devices/install-apk"&&x.method==="POST"){try{let g=F.searchParams,k=g.get("deviceId")||void 0,M=g.get("targetDevices"),v=M?M.split(",").filter(Boolean):k?[k]:[],C=g.get("fileName")||`app_${Date.now()}.apk`,A=ee.basename(C).endsWith(".apk")?ee.basename(C):`${ee.basename(C)}.apk`,D=ee.join(n,"_apk_temp");te.existsSync(D)||te.mkdirSync(D,{recursive:!0});let I=ee.join(D,`${Date.now()}_${A}`),H=te.createWriteStream(I);await new Promise((q,Se)=>{x.pipe(H),x.on("error",Se),H.on("error",Se),H.on("finish",q)});let z=v;if(z.length===0){let q=c.getDefaultDevice();q&&(z=[q])}if(z.length===0&&(z=(await c.getConnectedDevices()).filter(Se=>Se.state==="device").map(Se=>Se.serial)),z.length===0){try{await te.promises.unlink(I)}catch{}f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"No active target device found to install APK"}));return}let G=await Promise.allSettled(z.map(q=>c.installApk(I,q)));try{await te.promises.unlink(I)}catch{}let Z=G.filter(q=>q.status==="fulfilled").length,X=[];G.forEach((q,Se)=>{if(q.status==="rejected"){let Pe=q.reason instanceof Error?q.reason.message:String(q.reason);X.push(`${z[Se]}: ${Pe}`)}}),Z>0?(K("apk-installed",{fileName:A,installedCount:Z,targets:z}),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:`Successfully installed "${A}" on ${Z} device(s)${X.length>0?` (Failed: ${X.join(", ")})`:""}`,installedCount:Z}))):(f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Failed to install APK: ${X.join("; ")}`})))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/metrics"&&x.method==="GET"){try{let g=F.searchParams.get("deviceId")||void 0,k=await c.getDeviceMetrics(g);if(Object.keys(k).length===0)throw new Error(`No metrics available for device ${g||"default"}. Confirm the device is connected and authorized.`);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,metrics:k}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/doctor"&&x.method==="GET"){try{let g=F.searchParams.get("deviceId")||void 0,k=F.searchParams.get("packageName")||void 0,M=await gs(c,{serial:g,packageName:k});f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,report:M}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((E==="/api/explore/start"||E==="/api/explore")&&x.method==="POST"){try{let g=await V(x),k=(g.packageName||g.targetPackage||"").trim();if(!k){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing packageName (packageName or targetPackage)"}));return}if(y&&y.status==="running"){f.writeHead(409,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"An Auto-Pilot exploration session is already active",sessionId:y.id}));return}let M=Q({packageName:k,deviceId:g.deviceId||g.serial,safetyMode:g.safetyMode||g.safety,maxDepth:g.maxDepth??g.depth,maxSteps:g.maxSteps??g.stepBudget,maxScreens:g.maxScreens});f.writeHead(202,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,sessionId:M.id,status:M.status,packageName:M.packageName}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((E==="/api/explore/status"||E==="/api/explore")&&x.method==="GET"){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify(N(y)));return}if((E==="/api/explore/stop"||E==="/api/explore")&&(x.method==="POST"||x.method==="DELETE")){if(!y){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"No active Auto-Pilot session to stop"}));return}y.abort.aborted=!0,f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,sessionId:y.id,status:"stopping",packageName:y.packageName}));return}if(E==="/api/explore/export"&&x.method==="POST"){try{let g=await V(x);if(!y){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"No exploration session available to export"}));return}let k=(g.specName||`explored_${y.packageName}.txt`).trim(),M=ee.basename(k).replace(/[^a-zA-Z0-9._-]/g,"_"),v=M.endsWith(".txt")?M:`${M}.txt`,C=L(y);await te.promises.mkdir(p,{recursive:!0}),await te.promises.writeFile(ee.join(p,v),C,"utf8"),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,specName:v,content:C,screensDiscovered:y.screensDiscovered,stepsTaken:y.stepsTaken}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/record/start"&&x.method==="POST"){try{let g=await V(x);if(T){f.writeHead(409,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"A recording session is already active"}));return}let k=g.deviceId||c.getDefaultDevice()||void 0,M=g.specName?ee.join(p,ee.basename(g.specName)):void 0;T=new At(c),w=k||"default",T.onStep=v=>{K("record-step",{step:v,deviceId:k})},await T.startSession(M,k,{append:g.append}),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:"Recording session started",deviceId:k}))}catch(g){if(T)try{await T.stopSession()}catch{}T=null,w=null,f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/record/stop"&&x.method==="POST"){try{if(!T){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:"No active recording session",steps:[]}));return}let g=await T.stopSession();T=null,w=null,f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,steps:g.steps,specPath:g.specPath,reportPath:g.reportPath}))}catch(g){T=null,w=null,f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/record/status"&&x.method==="GET"){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,isRecording:T!==null,deviceId:w}));return}if(E==="/api/specs"&&x.method==="GET"){try{te.existsSync(p)||te.mkdirSync(p,{recursive:!0});let k=te.readdirSync(p).filter(M=>M.endsWith(".txt")||M.endsWith(".spec")).map(M=>{let v=ee.join(p,M),C=te.statSync(v);return{name:M,path:ee.relative(process.cwd(),v).replace(/\\/g,"/"),size:C.size,modified:C.mtime.toISOString()}});f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,specs:k}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((E.startsWith("/api/specs/")||E==="/api/spec")&&x.method==="GET"){try{let g="";E.startsWith("/api/specs/")?g=decodeURIComponent(E.slice(11)):g=F.searchParams.get("name")||"";let k=ee.basename(g.trim());if(!k){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing spec file name"}));return}let M=ee.join(p,k);if(!te.existsSync(M)){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Spec file not found: ${k}`}));return}let v=te.readFileSync(M,"utf-8");f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,name:k,content:v}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((E==="/api/specs"||E==="/api/spec")&&x.method==="POST"){try{let g=await V(x),k=(g.name||"").trim();if(!k){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing spec file name"}));return}!k.endsWith(".txt")&&!k.endsWith(".spec")&&(k+=".txt");let M=ee.basename(k);te.existsSync(p)||te.mkdirSync(p,{recursive:!0});let v=ee.join(p,M);te.writeFileSync(v,g.content??"","utf-8"),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,name:M,path:v}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/specs/apply-heals"&&x.method==="POST"){try{let g=await V(x);if(!g.specName||!Array.isArray(g.heals)||g.heals.length===0){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName or heals array"}));return}let k=ee.basename(g.specName),M=ee.join(p,k);if(!te.existsSync(M)){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Spec file not found: ${k}`}));return}let v=te.readFileSync(M,"utf-8"),C=0;for(let A of g.heals){if(!A.original||!A.healed||A.original===A.healed)continue;let D=A.original.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),I=new RegExp(`"${D}"`,"g"),H=new RegExp(`'${D}'`,"g");(I.test(v)||H.test(v))&&(v=v.replace(I,`"${A.healed}"`),v=v.replace(H,`'${A.healed}'`),C++)}te.writeFileSync(M,v,"utf-8"),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,specName:k,replacementsCount:C,content:v}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/logcat"&&x.method==="GET"){try{let g=(F.searchParams.get("deviceId")||c.getDefaultDevice())??void 0,k=parseInt(F.searchParams.get("lines")||"200",10),M=isNaN(k)?200:Math.min(Math.max(k,20),1e3),v=F.searchParams.get("filter")?.trim(),A=(await c.getLogcatTail(M,g)).split(`
|
|
905
905
|
`).filter(D=>D.trim().length>0);if(v){let D=v.toLowerCase();A=A.filter(I=>I.toLowerCase().includes(D))}f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,count:A.length,logs:A}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/logcat/clear"&&x.method==="POST"){try{let g=(F.searchParams.get("deviceId")||c.getDefaultDevice())??void 0;await c.execAdb(["logcat","-c"],g).catch(()=>{}),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/scaffold"&&x.method==="POST"){try{let g=await V(x),k=g.deviceId||c.getDefaultDevice()||void 0,M=g.packageName?.trim()||void 0,v=(g.specName||"").trim();v||(v=M?`scaffold_${M}.txt`:"scaffold_app.txt"),!v.endsWith(".txt")&&!v.endsWith(".spec")&&(v+=".txt");let C=ee.basename(v).replace(/[^a-zA-Z0-9._-]/g,"_"),A=ee.join(p,C),D=await Gn({driver:c,deviceSerial:k,packageName:M,outputPath:A,journey:g.journey});f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,specName:C,content:D.specContent,packageName:D.packageName,inputCount:D.inputCount,buttonCount:D.buttonCount,stepsCount:D.stepsCount}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/expand"&&x.method==="POST"){try{let g=await V(x);if(!g.spec||typeof g.spec!="string"){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:'Missing "spec" string to expand'}));return}let k=h(),{expanded:M,stepsAdded:v}=await k.expandSpec(g.spec);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,expanded:M,stepsAdded:v}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/action"&&x.method==="POST"){try{let g=await V(x);if(!g.step||typeof g.step!="string"){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing natural language step string"}));return}let k=St(g.step);if(k.length===0){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Could not parse step: "${g.step}"`}));return}let M=h(),v=k[0],C=g.deviceId||c.getDefaultDevice()||void 0,A=Date.now(),D=await M.executeStep(v,C),I=Date.now()-A;K("step",{stepNumber:r.steps.length+1,instruction:v.originalText,status:"PASS",durationMs:I}),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,step:v.originalText,type:v.type,durationMs:I,result:D||null}))}catch(g){let k=g instanceof Error?g.message:String(g);K("step",{stepNumber:r.steps.length+1,instruction:"Step Execution",status:"FAIL",durationMs:0,error:k}),f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:k}))}return}if(E==="/api/run"&&x.method==="POST"){try{let g=await V(x),k="";if(g.specName){let A=ee.basename(g.specName);if(k=ee.join(p,A),g.spec&&typeof g.spec=="string"&&g.spec.trim().length>0)te.writeFileSync(k,g.spec,"utf-8");else if(!te.existsSync(k)){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Spec file not found: ${A}`}));return}}else if(g.spec&&typeof g.spec=="string")te.existsSync(n)||te.mkdirSync(n,{recursive:!0}),k=ee.join(n,"_studio_temp_spec.txt"),te.writeFileSync(k,g.spec,"utf-8");else{f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:'Either "spec" string or "specName" must be provided'}));return}let M=g.dataFile;if(g.dataFileContent&&typeof g.dataFileContent=="string"){te.existsSync(n)||te.mkdirSync(n,{recursive:!0});let A=g.dataFile?ee.basename(g.dataFile):"_studio_data.csv";M=ee.join(n,A),te.writeFileSync(M,g.dataFileContent,"utf-8")}let v=g.deviceId||c.getDefaultDevice()||void 0,C=h();r.steps=[],r.totalSteps=0,r.passedSteps=0,r.failedSteps=0,r.warnSteps=0,r.skippedSteps=0,(async()=>{let A;try{let I=await c.getForegroundPackage(v);I&&!I.includes("Launcher")&&!I.includes("Foreground")&&(A=I)}catch{}let D=async(I,H)=>{let z=H?xt:new ut(n),G=H?C:new st(c,void 0,z),Z=z.onStep?.(X=>{let q=!!(X.details?.healed||X.details?.healedFrom||X.description?.toLowerCase().includes("healed")),Se=X.details;K("step",{deviceId:I,stepNumber:X.stepIndex,instruction:X.description,status:X.status,durationMs:X.durationMs||0,error:X.details?.error,healed:q,healedFrom:Se?.healedFrom,healedTo:Se?.healedTo})});try{let X=await G.runSpec(k,{serial:I,packageName:A,saveBaseline:H&&g.saveBaseline!==void 0?g.saveBaseline:!1,compareBaseline:H&&g.compareBaseline!==void 0?g.compareBaseline:!0,screenshotMode:H?"all":"on-failure",heal:g.heal??!1,fresh:g.fresh??!1,continueOnError:g.continueOnError??!1,tags:g.tags,only:g.only,dataFile:M,iterations:g.iterations,profile:g.profile??!1});return K("device-complete",{deviceId:I,report:X}),X}finally{Z&&Z()}};try{let I=Array.isArray(g.targetDevices)&&g.targetDevices.length>0;if(I||g.allDevices){let H=[];I?H=g.targetDevices:H=(await c.getConnectedDevices()).filter(X=>X.state==="device").map(X=>X.serial),H=Array.from(new Set(H.filter(Boolean))),v&&H.includes(v)?H=[v,...H.filter(Z=>Z!==v)]:H.length===0&&v&&H.push(v);let z=await Promise.allSettled(H.map((Z,X)=>D(Z,X===0))),G=[];for(let Z of z)Z.status==="fulfilled"&&G.push(Z.value);G.length>0&&(r=G[0]),K("complete",{multiDevice:!0,reports:G,currentReport:r})}else{let H=await D(v||"default",!0);r=H,K("complete",H)}}catch(I){let H=I instanceof Error?I.message:String(I);r.errorsDetected.push(H),K("error",{error:H}),K("complete",r)}})(),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:"Execution started"}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/reports"&&x.method==="GET"){try{if(!te.existsSync(n)){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,reports:[]}));return}let M=te.readdirSync(n).filter(v=>v.endsWith("-results.json")).map(v=>{let C=v.replace(/-results\.json$/,""),A=ee.join(n,v),D=`${C}-report.html`,I=te.existsSync(ee.join(n,D)),H=te.statSync(A);try{let z=JSON.parse(te.readFileSync(A,"utf-8")),G=z.totalSteps||z.steps?.length||0,Z=z.passedSteps||0,X=z.failedSteps||0,q=G>0?Math.round(Z/G*100):100;return{id:C,fileName:v,suiteName:z.packageName||C,packageName:z.packageName||"Unknown",deviceId:z.deviceId||"Unknown",startTime:z.startTime||H.mtime.toISOString(),endTime:z.endTime||H.mtime.toISOString(),durationSeconds:z.durationSeconds||0,totalSteps:G,passedSteps:Z,failedSteps:X,warnSteps:z.warnSteps||0,skippedSteps:z.skippedSteps||0,passRate:q,errorsCount:z.errorsDetected?.length||0,htmlReportPath:I?`/${D}`:null,jsonReportPath:`/${v}`}}catch{return null}}).filter(Boolean).sort((v,C)=>new Date(C.startTime).getTime()-new Date(v.startTime).getTime());f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,reports:M}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((E.startsWith("/api/reports/")||E==="/api/report-detail")&&x.method==="GET"){try{let g="";E.startsWith("/api/reports/")?g=decodeURIComponent(E.slice(13)):g=F.searchParams.get("id")||"";let k=ee.basename(g.trim()),M=k.endsWith("-results.json")?k:`${k}-results.json`,v=ee.join(n,M);if(!te.existsSync(v)){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Report not found: ${k}`}));return}let C=JSON.parse(te.readFileSync(v,"utf-8"));f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,id:k,report:C}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines"&&x.method==="GET"){try{te.existsSync(m)||te.mkdirSync(m,{recursive:!0});let g=F.searchParams.get("spec")||"",k=g.replace(/[^a-zA-Z0-9._-]/g,"_"),M=te.readdirSync(m),v=new Map;for(let A of M){if(k&&!A.startsWith(`${k}_step`))continue;let D=A.match(/_step(\d+)(_current|_diff)?\.png$/);if(!D)continue;let I=parseInt(D[1],10),H=D[2];v.has(I)||v.set(I,{});let z=v.get(I);H?H==="_current"?z.current=`/baselines/${A}`:H==="_diff"&&(z.diff=`/baselines/${A}`):z.baseline=`/baselines/${A}`}let C=await Promise.all(Array.from(v.entries()).map(async([A,D])=>{let I=await b.getMasks(g,A);return{stepIndex:A,...D,masks:I}}));C.sort((A,D)=>A.stepIndex-D.stepIndex),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,spec:g,baselines:C,matchingModes:ln}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines/compare"&&x.method==="POST"){try{let g=await V(x);if(!g.specName||typeof g.stepIndex!="number"){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName or stepIndex"}));return}let k;if(g.screenshot){let C=g.screenshot.replace(/^data:image\/\w+;base64,/,"");k=Buffer.from(C,"base64")}else{let C=g.deviceId||c.getDefaultDevice()||void 0;k=await c.captureScreenshot(C)}let M=await b.compare(g.specName,g.stepIndex,k,{threshold:g.threshold,mode:g.mode,maskRegions:g.maskRegions}),v=await b.getMasks(g.specName,g.stepIndex);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,result:{...M,masks:v,baselineUrl:`/baselines/${ee.basename(M.baselineFile)}`,currentUrl:`/baselines/${ee.basename(M.currentFile)}`,diffUrl:M.diffImagePath?`/baselines/${ee.basename(M.diffImagePath)}`:null}}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines/approve"&&x.method==="POST"){try{let g=await V(x);if(!g.specName||typeof g.stepIndex!="number"){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName or stepIndex"}));return}let k;if(g.screenshot){let v=g.screenshot.replace(/^data:image\/\w+;base64,/,"");k=Buffer.from(v,"base64")}else{let v=g.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),C=ee.join(m,`${v}_step${String(g.stepIndex).padStart(3,"0")}_current.png`);if(!te.existsSync(C)){let A=g.deviceId||c.getDefaultDevice()||void 0;try{k=await c.captureScreenshot(A)}catch{}}}let M=await b.approveBaseline(g.specName,g.stepIndex,k);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,approved:!0,stepIndex:g.stepIndex,baselineUrl:`/baselines/${ee.basename(M)}`}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines/clear"&&x.method==="POST"){try{let g=await V(x);if(!g.specName){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName"}));return}await b.clearBaselines(g.specName),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,cleared:!0,specName:g.specName}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines/delete-step"&&x.method==="POST"){try{let g=await V(x);if(!g.specName||typeof g.stepIndex!="number"){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName or stepIndex"}));return}await b.deleteStepBaseline(g.specName,g.stepIndex),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,deleted:!0,specName:g.specName,stepIndex:g.stepIndex}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines/upload"&&x.method==="POST"){try{let g=await V(x);if(!g.specName||typeof g.stepIndex!="number"||!g.image){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName, stepIndex, or image data"}));return}let k=g.image.replace(/^data:image\/\w+;base64,/,""),M=Buffer.from(k,"base64"),v=await b.saveBaseline(g.specName,g.stepIndex,M),C=g.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),A=ee.join(m,`${C}_step${String(g.stepIndex).padStart(3,"0")}_diff.png`);if(te.existsSync(A))try{await te.promises.unlink(A)}catch{}let D=ee.join(m,`${C}_step${String(g.stepIndex).padStart(3,"0")}_current.png`),I,H;if(te.existsSync(D)){let G=await te.promises.readFile(D),Z=await b.compare(g.specName,g.stepIndex,G);I=`/baselines/${ee.basename(D)}`,H=Z.diffImagePath?`/baselines/${ee.basename(Z.diffImagePath)}`:void 0}else if(g.deviceId||c.getDefaultDevice()){let G=g.deviceId||c.getDefaultDevice()||void 0;try{let Z=await c.captureScreenshot(G),X=await b.compare(g.specName,g.stepIndex,Z);I=`/baselines/${ee.basename(D)}`,H=X.diffImagePath?`/baselines/${ee.basename(X.diffImagePath)}`:void 0}catch{}}let z=await b.getMasks(g.specName,g.stepIndex);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,uploaded:!0,stepIndex:g.stepIndex,baselineUrl:`/baselines/${ee.basename(v)}`,currentUrl:I,diffUrl:H,masks:z}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines/masks"&&x.method==="GET"){try{let g=F.searchParams.get("specName")||F.searchParams.get("spec")||"",k=parseInt(F.searchParams.get("stepIndex")||"",10);if(!g||isNaN(k)){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName or valid stepIndex"}));return}let M=await b.getMasks(g,k);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,masks:M}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines/masks"&&x.method==="POST"){try{let g=await V(x);if(!g.specName||typeof g.stepIndex!="number"||!Array.isArray(g.masks)){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName, stepIndex, or masks array"}));return}await b.saveMasks(g.specName,g.stepIndex,g.masks);let k=g.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),M=ee.join(m,`${k}_step${String(g.stepIndex).padStart(3,"0")}_current.png`),v,C,A=0,D=0,I=!0;if(te.existsSync(M)){let H=await te.promises.readFile(M),z=await b.compare(g.specName,g.stepIndex,H,{mode:g.mode,threshold:g.threshold,maskRegions:g.masks});v=`/baselines/${ee.basename(M)}`,C=z.diffImagePath?`/baselines/${ee.basename(z.diffImagePath)}`:void 0,A=z.diffRatio,D=z.diffPixels,I=z.passed}f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,masks:g.masks,currentUrl:v,diffUrl:C,diffRatio:A,diffPixels:D,passed:I}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/events"){f.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive","Access-Control-Allow-Origin":"*"}),f.write(`event: connected
|
|
906
906
|
data: ${JSON.stringify({status:"connected",time:Date.now()})}
|
|
907
907
|
|
|
908
|
-
`),o.add(f),x.on("close",()=>{o.delete(f)});return}if(E==="/"||E==="/report"||E==="/index.html"){let g=en(r,{suiteName:i});f.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-cache"}),f.end(g);return}if(E==="/api/report"||E==="/results.json"){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8","Access-Control-Allow-Origin":"*"}),f.end(JSON.stringify(r,null,2));return}let ge=E.replace(/^\/+/,""),Y=ee.join(n,ge);if(!Y.startsWith(n)){f.writeHead(403,{"Content-Type":"text/plain"}),f.end("Access Denied");return}if(te.existsSync(Y)&&te.statSync(Y).isFile()){let g=ee.extname(Y).toLowerCase(),k=Io[g]||"application/octet-stream";f.writeHead(200,{"Content-Type":k,"Cache-Control":"no-cache"}),te.createReadStream(Y).pipe(f);return}f.writeHead(404,{"Content-Type":"text/plain"}),f.end("Not Found")});await new Promise((x,f)=>{ye.once("error",f),ye.listen(t,e,()=>{x()})});let re=ye.address(),W=typeof re=="object"&&re?re.port:t,
|
|
908
|
+
`),o.add(f),x.on("close",()=>{o.delete(f)});return}if(E==="/"||E==="/report"||E==="/index.html"){let g=en(r,{suiteName:i});f.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-cache"}),f.end(g);return}if(E==="/api/report"||E==="/results.json"){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8","Access-Control-Allow-Origin":"*"}),f.end(JSON.stringify(r,null,2));return}let ge=E.replace(/^\/+/,""),Y=ee.join(n,ge);if(!Y.startsWith(n)){f.writeHead(403,{"Content-Type":"text/plain"}),f.end("Access Denied");return}if(te.existsSync(Y)&&te.statSync(Y).isFile()){let g=ee.extname(Y).toLowerCase(),k=Io[g]||"application/octet-stream";f.writeHead(200,{"Content-Type":k,"Cache-Control":"no-cache"}),te.createReadStream(Y).pipe(f);return}f.writeHead(404,{"Content-Type":"text/plain"}),f.end("Not Found")});await new Promise((x,f)=>{ye.once("error",f),ye.listen(t,e,()=>{x()})});let re=ye.address(),W=typeof re=="object"&&re?re.port:t,ne=`http://${e}:${W}`;return{server:ye,port:W,url:ne,broadcastStep(x){r.steps.push(x),r.totalSteps=r.steps.length,x.status==="PASS"?r.passedSteps++:x.status==="FAIL"?r.failedSteps++:x.status==="WARN"?r.warnSteps++:x.status==="SKIPPED"&&r.skippedSteps++,K("step",x)},broadcastComplete(x){r=x,K("complete",x)},broadcastError(x){r.errorsDetected.push(x),K("error",{error:x})},updateReport(x){r=x},async close(){if(clearInterval(l),T){try{await T.stopSession()}catch{}T=null,w=null}y&&y.status==="running"&&(y.abort.aborted=!0);for(let x of o)try{x.end()}catch{}o.clear(),await new Promise((x,f)=>{ye.close(F=>{F?f(F):x()})})}}}var Io,Ms=ie(()=>{"use strict";Tt();Re();Ve();Yt();et();hs();ss();ms();qt();Xn();Qn();Io={".html":"text/html; charset=utf-8",".json":"application/json; charset=utf-8",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".svg":"image/svg+xml",".mp4":"video/mp4",".log":"text/plain; charset=utf-8",".txt":"text/plain; charset=utf-8",".xml":"application/xml; charset=utf-8",".zip":"application/zip"}});import No from"chalk";async function di(a,t){if(qn)return qn;let e=!!(process.env.CI||process.env.GITHUB_ACTIONS||process.env.TRAVIS||process.env.CIRCLECI||process.env.GITLAB_CI),s=!1;try{let{stdout:c}=await a.execAdb(["shell","getprop","ro.kernel.qemu"],t),{stdout:d}=await a.execAdb(["shell","getprop","ro.product.model"],t),{stdout:p}=await a.execAdb(["shell","getprop","ro.build.characteristics"],t),u=(d||"").toLowerCase(),h=(p||"").toLowerCase();s=c?.trim()==="1"||u.includes("sdk")||u.includes("emulator")||h.includes("emulator")}catch{s=!1}let n=1;e?n=s?2.8:2:s?n=1.5:n=1;let i=parseFloat(process.env.PROMPTTEST_SETTLE_FACTOR||"");!isNaN(i)&&i>0&&(n=Math.max(.5,i));let r={isEmulator:s,isCI:e,velocityMultiplier:n,defaultTimeoutMs:Math.round(3500*n),extendedTimeoutMs:Math.round(6500*n),quiescenceSettleMs:Math.round(150*Math.min(n,2)),initialPollMs:e?150:80,maxPollIntervalMs:e?600:350};qn=r;let o=s?"Android Emulator":"Physical Android Device",l=e?"CI / Cloud Runner":"Local Environment";return console.log(No.cyan(`\u26A1 [Device Profiler] Detected ${o} in ${l} (Velocity Multiplier: ${n.toFixed(1)}x, Dynamic Timeout: ${r.defaultTimeoutMs}ms)`)),r}var qn,pi=ie(()=>{"use strict";qn=null});var gi={};Qe(gi,{PerformanceProfiler:()=>er,parseGfxInfo:()=>ui,parseMemInfo:()=>hi});function ui(a){let t=0,e=0,s=0,n,i,r,o,l,c,d=a.match(/Total frames rendered:\s+(\d+)/i);d&&(t=parseInt(d[1],10));let p=a.match(/Janky frames:\s+(\d+)(?:\s+\(([0-9.]+)%\))?/i);p&&(e=parseInt(p[1],10),p[2]?s=parseFloat(p[2]):t>0&&(s=parseFloat((e/t*100).toFixed(2))));let u=a.match(/50th percentile:\s+(\d+)ms/i);u&&(n=parseInt(u[1],10));let h=a.match(/90th percentile:\s+(\d+)ms/i);h&&(i=parseInt(h[1],10));let m=a.match(/95th percentile:\s+(\d+)ms/i);m&&(r=parseInt(m[1],10));let b=a.match(/99th percentile:\s+(\d+)ms/i);b&&(o=parseInt(b[1],10));let T=a.match(/Number Missed Vsync:\s+(\d+)/i);T&&(l=parseInt(T[1],10));let w=a.match(/Number Slow UI thread:\s+(\d+)/i);return w&&(c=parseInt(w[1],10)),{totalFrames:t,jankyFrames:e,jankPercentage:s,p50Ms:n,p90Ms:i,p95Ms:r,p99Ms:o,missedVsync:l,slowUiThread:c}}function hi(a){let t=0,e,s,n=a.match(/TOTAL PSS:\s+(\d+)/i)||a.match(/TOTAL:\s+(\d+)/i)||a.match(/TOTAL\s+PSS\s+TOTAL\s+RSS[\s\S]*?TOTAL\s+(\d+)/i);n&&(t=parseInt(n[1],10));let i=a.match(/Native Heap\s+(\d+)/i)||a.match(/Native\s+(\d+)/i);i&&(e=parseInt(i[1],10));let r=a.match(/Java Heap\s+(\d+)/i)||a.match(/Dalvik Heap\s+(\d+)/i);return r&&(s=parseInt(r[1],10)),{totalPssKb:t,nativeHeapKb:e,javaHeapKb:s}}var er,mi=ie(()=>{"use strict";Re();er=class{driver;serial;packageName="";startTime=0;memorySamples=[];constructor(t,e){this.driver=t||oe,this.serial=e}async start(t){this.packageName=t,this.startTime=Date.now(),this.memorySamples=[];try{await this.driver.execAdb(["shell","dumpsys","gfxinfo",t,"reset"],this.serial)}catch{}await this.sample(0,"Suite Initialized")}async sample(t,e){if(this.packageName)try{let{stdout:s}=await this.driver.execAdb(["shell","dumpsys","meminfo",this.packageName],this.serial),n=hi(s);this.memorySamples.push({timestamp:Date.now(),stepIndex:t,stepName:e,totalPssKb:n.totalPssKb,nativeHeapKb:n.nativeHeapKb,javaHeapKb:n.javaHeapKb})}catch{}}async stop(){let t=Date.now()-(this.startTime||Date.now()),e={totalFrames:0,jankyFrames:0,jankPercentage:0};if(this.packageName)try{let{stdout:o}=await this.driver.execAdb(["shell","dumpsys","gfxinfo",this.packageName],this.serial);e=ui(o)}catch{}let s=this.memorySamples[0]?.totalPssKb||0,n=this.memorySamples[this.memorySamples.length-1]?.totalPssKb||s,i=this.memorySamples.reduce((o,l)=>Math.max(o,l.totalPssKb),s),r="PASS";return e.jankPercentage>15||e.p95Ms&&e.p95Ms>32?r="FAIL":(e.jankPercentage>5||e.p95Ms&&e.p95Ms>16.7)&&(r="WARN"),{packageName:this.packageName,durationMs:t,fpsCompliance:r,frames:e,memory:{startPssKb:s,peakPssKb:i,endPssKb:n,deltaPssKb:n-s,samples:this.memorySamples}}}}});import Fs from"fs";import ft from"path";import se from"chalk";var st,Vt,Yt=ie(()=>{"use strict";Re();Ve();Tt();Pt();je();us();Qt();zr();hs();_n();Un();Wn();Ms();et();ot();pi();st=class{driver;memory;reporter;checkpoints;baselines;lastHierarchy=null;screenDimensions={width:R.screen.fallbackWidth,height:R.screen.fallbackHeight};navBarHeight=0;targetPackage=void 0;constructor(t,e,s,n,i,r){this.driver=t||oe,this.memory=e||Me,this.reporter=s||xt,this.checkpoints=n||Kr,this.baselines=i||dn,this.semanticResolver=r||Bn()}semanticResolver;healEnabled=!1;activeLocale="en";runtimeVariables=new Map;loopStack=[];expandSpecIncludes(t,e=new Set){return Xs(t,e)}async expandSpec(t,e){let s=e||this.semanticResolver,n=t.split(`
|
|
909
909
|
`),i=[],r=0;for(let o of n){let l=o.trim();if(!l||l.startsWith("#")||l.startsWith("//")){i.push(o);continue}let c=this.parsePrompt(l);if(c.length>0&&c[0].type!==void 0&&!l.toLowerCase().startsWith("intent:")){i.push(o);continue}let p=l.replace(/^intent:\s*/i,""),u=this.memory?.getIntentExpansion(p);if(u?.length){i.push(`# [Cached Intent] ${l}`,...u),r+=u.length;continue}let h=await s.resolveIntent(p);if(h?.length){i.push(`# [AI Expanded] ${l}`);let m=h.map(b=>b.originalText);i.push(...m),this.memory&&(this.memory.recordIntentExpansion(p,m),await this.memory.save().catch(()=>{})),r+=h.length}else i.push(o)}return{expanded:i.join(`
|
|
910
|
-
`),stepsAdded:r}}parsePrompt(t,e){return St(t,e)}async runSpec(t,e,s){if(!Fs.existsSync(t))throw new Error(`Spec file not found: ${t}`);let n=typeof e=="string"?{packageName:e,serial:s,screenshotMode:"on-failure",continueOnError:!1}:{screenshotMode:"on-failure",continueOnError:!1,...e};n.heal!==void 0&&(this.healEnabled=n.heal),n.noAi&&(this.semanticResolver=Bn({provider:"heuristic"})),this.activeLocale=(n.locale||R.locale||"en").toLowerCase(),typeof this.memory.setLocale=="function"&&this.memory.setLocale(this.activeLocale);let i=this.expandSpecIncludes(t),r=i,o=[],l=ts.extractDataDirective(i,ft.dirname(t));if(l)r=l.cleanedSpec,o=l.rows;else if(n.dataFile){let S=ft.isAbsolute(n.dataFile)?n.dataFile:ft.resolve(process.cwd(),n.dataFile);o=ts.loadFromFile(S)}(n.onlyRow!==void 0||n.iterations!==void 0)&&(o.length>0?o=ts.filterRows(o,{onlyRow:n.onlyRow,iterations:n.iterations}):n.iterations&&n.iterations>0&&(o=Array.from({length:n.iterations},()=>({}))));let c=o.length>0?o.map(S=>({...n.variables||{},...S})):[n.variables||{}],d=ft.basename(t,ft.extname(t)),p=r.match(/#\s*(?:Target\s*)?Package:\s*([a-zA-Z0-9._-]+)/i),u=p?p[1]:void 0;if(n.tags){let S=Or(i),y=n.tags.split(",").map(N=>N.trim().toLowerCase()).filter(Boolean);if(!y.some(N=>S.includes(N)))return console.log(
|
|
911
|
-
\u23E9 Skipping ${t}: tags [${S.join(", ")}] do not match filter [${y.join(", ")}]`)),ws(u||n.packageName||"",n.serial||"",[],"SKIPPED")}if(n.listSteps){let S=this.parsePrompt(r,c[0]);return console.log(_r(S)),ws(u||n.packageName||"",n.serial||"",S,"SKIPPED","Listing")}if(n.dryRun){let S=this.parsePrompt(r,c[0]);return console.log(
|
|
912
|
-
\u{1F50D} [Dry Run] Validated ${S.length} test step(s) in ${t} successfully (no device interaction).`)),ws(u||n.packageName||"",n.serial||"",S,"PASS","Validation")}if(c.length>1)console.log(
|
|
913
|
-
\u{1F4CB} Loaded spec with ${c.length} data iteration(s) from ${t}`));else{let S=this.parsePrompt(r,c[0]);console.log(
|
|
914
|
-
\u{1F4CB} Loaded ${S.length} test step(s) from ${t}`))}console.log(
|
|
910
|
+
`),stepsAdded:r}}parsePrompt(t,e){return St(t,e)}async runSpec(t,e,s){if(!Fs.existsSync(t))throw new Error(`Spec file not found: ${t}`);let n=typeof e=="string"?{packageName:e,serial:s,screenshotMode:"on-failure",continueOnError:!1}:{screenshotMode:"on-failure",continueOnError:!1,...e};n.heal!==void 0&&(this.healEnabled=n.heal),n.noAi&&(this.semanticResolver=Bn({provider:"heuristic"})),this.activeLocale=(n.locale||R.locale||"en").toLowerCase(),typeof this.memory.setLocale=="function"&&this.memory.setLocale(this.activeLocale);let i=this.expandSpecIncludes(t),r=i,o=[],l=ts.extractDataDirective(i,ft.dirname(t));if(l)r=l.cleanedSpec,o=l.rows;else if(n.dataFile){let S=ft.isAbsolute(n.dataFile)?n.dataFile:ft.resolve(process.cwd(),n.dataFile);o=ts.loadFromFile(S)}(n.onlyRow!==void 0||n.iterations!==void 0)&&(o.length>0?o=ts.filterRows(o,{onlyRow:n.onlyRow,iterations:n.iterations}):n.iterations&&n.iterations>0&&(o=Array.from({length:n.iterations},()=>({}))));let c=o.length>0?o.map(S=>({...n.variables||{},...S})):[n.variables||{}],d=ft.basename(t,ft.extname(t)),p=r.match(/#\s*(?:Target\s*)?Package:\s*([a-zA-Z0-9._-]+)/i),u=p?p[1]:void 0;if(n.tags){let S=Or(i),y=n.tags.split(",").map(N=>N.trim().toLowerCase()).filter(Boolean);if(!y.some(N=>S.includes(N)))return console.log(se.yellow(`
|
|
911
|
+
\u23E9 Skipping ${t}: tags [${S.join(", ")}] do not match filter [${y.join(", ")}]`)),ws(u||n.packageName||"",n.serial||"",[],"SKIPPED")}if(n.listSteps){let S=this.parsePrompt(r,c[0]);return console.log(_r(S)),ws(u||n.packageName||"",n.serial||"",S,"SKIPPED","Listing")}if(n.dryRun){let S=this.parsePrompt(r,c[0]);return console.log(se.bold.cyan(`
|
|
912
|
+
\u{1F50D} [Dry Run] Validated ${S.length} test step(s) in ${t} successfully (no device interaction).`)),ws(u||n.packageName||"",n.serial||"",S,"PASS","Validation")}if(c.length>1)console.log(se.cyan(`
|
|
913
|
+
\u{1F4CB} Loaded spec with ${c.length} data iteration(s) from ${t}`));else{let S=this.parsePrompt(r,c[0]);console.log(se.cyan(`
|
|
914
|
+
\u{1F4CB} Loaded ${S.length} test step(s) from ${t}`))}console.log(se.gray("Execution Policy: ")+se.bold.white(n.continueOnError?"Continue on Error (Modular Run)":"Fail-Fast (Default)")+se.gray(" | Screenshots: ")+se.bold.white(n.screenshotMode==="all"?"All Steps":"Failure-Only")),n.serial=await this.driver.ensureDefaultDevice(n.serial);let h=n.serial||this.driver.getDefaultDevice()||"Default Device",m=null;n.serial&&(m=Ft(n.serial)),n.embedScreenshots&&typeof this.reporter.setEmbedScreenshots=="function"&&this.reporter.setEmbedScreenshots(!0);let b,T;if(n.serve){let S=typeof n.serve=="number"?n.serve:4040;try{b=await Zn({port:S,suiteName:d}),console.log(se.bold.magenta(`
|
|
915
915
|
\u{1F4E1} Live Monitor streaming at: ${b.url}
|
|
916
|
-
`)),typeof this.reporter.onStep=="function"&&(T=this.reporter.onStep(y=>{b?.broadcastStep(y)}))}catch(y){let P=y;console.warn(
|
|
916
|
+
`)),typeof this.reporter.onStep=="function"&&(T=this.reporter.onStep(y=>{b?.broadcastStep(y)}))}catch(y){let P=y;console.warn(se.yellow(`Warning: Could not start live server: ${P.message}`))}}let w=null;n.video&&(console.log(se.cyan("\u{1F3AC} Starting ADB screen recording (--video enabled)...")),w=await this.driver.startScreenRecording("/sdcard/prompttest_temp.mp4",n.serial));try{this.screenDimensions=await this.driver.getScreenDimensions(n.serial),this.navBarHeight=typeof this.driver.getNavigationBarHeight=="function"?await this.driver.getNavigationBarHeight(n.serial):0,console.log(se.gray("Target Screen Geometry: ")+se.bold.cyan(`${this.screenDimensions.width}x${this.screenDimensions.height}`)+(this.navBarHeight>0?se.gray(` (Nav Bar: ${this.navBarHeight}px)`):"")),n.apkPath&&(Fs.existsSync(n.apkPath)?(console.log(se.cyan(`\u{1F4E6} Installing APK: ${n.apkPath}...`)),await this.driver.installApk(n.apkPath,n.serial),await new Promise(Q=>setTimeout(Q,2e3))):console.log(se.yellow(`Warning: Specified APK file not found at: ${n.apkPath}`)));let S=n.packageName||u||(typeof this.driver.getForegroundPackage=="function"?await this.driver.getForegroundPackage(n.serial):null)||"Foreground App";if(S&&!S.includes("Launcher")&&!S.includes("Foreground")){if(!(typeof this.driver.isAppInstalled=="function"?await this.driver.isAppInstalled(S,n.serial):!0))if(n.apkPath&&Fs.existsSync(n.apkPath))console.log(se.cyan(`\u{1F4E6} Target app not found. Auto-installing APK: ${n.apkPath}...`)),await this.driver.installApk(n.apkPath,n.serial),await new Promise(V=>setTimeout(V,2500));else throw new Error(`Target application "${S}" is not installed on device (${h}).
|
|
917
917
|
\u{1F4A1} Suggestions:
|
|
918
918
|
\u2022 Install the app manually or via: adb install <path-to-apk>
|
|
919
|
-
\u2022 Or provide an APK path to auto-install before running: --apk=path/to/app.apk`);let K=await this.driver.getForegroundPackage(n.serial);(n.fresh||K&&K!==S)&&(console.log(
|
|
919
|
+
\u2022 Or provide an APK path to auto-install before running: --apk=path/to/app.apk`);let K=await this.driver.getForegroundPackage(n.serial);(n.fresh||K&&K!==S)&&(console.log(se.cyan(`\u{1F680} Launching target application: ${se.bold(S)}...`)),n.fresh&&(await this.driver.forceStop(S,n.serial),await new Promise(V=>setTimeout(V,600))),await this.driver.launchApp(S,n.serial),await new Promise(V=>setTimeout(V,2500)))}n.reporter&&(this.reporter=n.reporter),n.reporters&&typeof this.reporter.setFormats=="function"&&this.reporter.setFormats(n.reporters),n.serial&&await di(this.driver,n.serial).catch(()=>{}),this.targetPackage=S&&!S.includes("Foreground")&&!S.includes("Launcher")?S:void 0,this.reporter.init(S,h),await this.memory.init(S);let y=null;if(n.profile&&this.targetPackage){let{PerformanceProfiler:Q}=await Promise.resolve().then(()=>(mi(),gi));y=new Q(this.driver,n.serial),await y.start(this.targetPackage)}this.lastHierarchy=null;let P=Date.now(),N=0;for(let Q=0;Q<c.length;Q++){let K=c[Q],V=c.length>1,ye=n.onlyRow?`Row ${n.onlyRow}`:V?`Row ${Q+1}`:void 0,re=ye?`${d} [${ye}]`:d;V&&(console.log(se.bold.magenta(`
|
|
920
920
|
======================================================
|
|
921
921
|
\u{1F504} [Data-Driven Iteration ${Q+1}/${c.length}] Row Data: ${JSON.stringify(K)}
|
|
922
|
-
======================================================`)),n.fresh&&Q>0&&S&&(await this.driver.clearAppData(S,n.serial),await new Promise(F=>setTimeout(F,1e3)))),this.runtimeVariables.clear();for(let[F,E]of Object.entries(K))this.runtimeVariables.set(F,E);this.loopStack=[];let W=this.parsePrompt(r,K),
|
|
923
|
-
[Step ${
|
|
924
|
-
\u{1F6D1} Aborting dependent flow due to upstream failure at Step ${
|
|
925
|
-
\u{1F50D} Visual QA Sentinel Warnings (${K.length} detected):`));for(let V of K)console.log(
|
|
926
|
-
\u2728 PromptTest Script Execution Finished in ${L}s!`));let j=U.failedSteps===0?ne.bold.green:ne.bold.yellow;return console.log(j(`Result: ${U.passedSteps}/${U.totalSteps} passed (${U.failedSteps} failed, ${U.skippedSteps} skipped).`)),console.log(ne.cyan(`Report saved to output/${d}-report.html
|
|
927
|
-
`)),n.json&&console.log(JSON.stringify(U,null,2)),U}finally{if(w)try{await this.driver.stopScreenRecording(w,ft.join("output",`${d}-recording.mp4`),n.serial)}catch{}m&&Ot(m)}}async getCleanHierarchy(t){let e=await this.driver.getUiHierarchy(t),{flat:s}=be(e),n=s.find(o=>(o.text==="Dismiss"||o.contentDesc==="Dismiss")&&o.bounds.width>0);n&&(await this.driver.tap(n.bounds.centerX,n.bounds.centerY,t),await new Promise(o=>setTimeout(o,400)),e=await this.driver.getUiHierarchy(t),s=be(e).flat);let i=s.find(o=>o.contentDesc&&(o.contentDesc.startsWith("!, ")||o.contentDesc.includes("react.dev/link")));if(i){let o=i.bounds.x2-45,l=i.bounds.centerY;await this.driver.tap(o,l,t),await new Promise(c=>setTimeout(c,350)),e=await this.driver.getUiHierarchy(t),s=be(e).flat}let r=ke(s);return this.lastHierarchy={xml:e,flat:s,timestamp:Date.now(),hash:r},{xml:e,flat:s}}async ensureInViewport(t,e){let s=this.screenDimensions.width,n=this.screenDimensions.height,i=Math.round(s*.5),r=this.navBarHeight>0?n-this.navBarHeight:n,o=this.navBarHeight>0?r*.94:n*R.gestures.bottomNavThresholdRatio;if(t.bounds.centerY>o||t.bounds.y1>o)return!1;let c=t.bounds.y2>r,d=t.bounds.y1<Math.round(n*.04)&&t.bounds.centerY<Math.round(n*.06);return c?(await this.driver.swipe(i,Math.round(n*.75),i,Math.round(n*.45),250,e),await new Promise(p=>setTimeout(p,200)),this.lastHierarchy=null,!0):d?(await this.driver.swipe(i,Math.round(n*.35),i,Math.round(n*.65),250,e),await new Promise(p=>setTimeout(p,200)),this.lastHierarchy=null,!0):!1}async waitForTarget(t,e,s=3e3,n){if(this.lastHierarchy&&Date.now()-this.lastHierarchy.timestamp<R.polling.hierarchyCacheTtlMs){let l=this.matchNodes(this.lastHierarchy.flat,t,n);if(l.length>0)return{flat:this.lastHierarchy.flat,matched:l}}let i=Date.now();for(;Date.now()-i<s;){let{flat:l}=await this.getCleanHierarchy(e),c=this.matchNodes(l,t,n);if(c.length>0)return{flat:l,matched:c};await new Promise(d=>setTimeout(d,fr()))}let{flat:r}=await this.getCleanHierarchy(e),o=this.matchNodes(r,t,n);if(o.length===0&&(this.healEnabled||R.ai.selfHealing))try{let l=await this.semanticResolver.findSuggestions(t,r,{locale:this.activeLocale}),c=R.ai.confidenceThreshold||.75,d=l.find(p=>p.score>=c);if(d){let p=d.node||r.find(u=>(me(d.text,u.text)||me(d.text,u.contentDesc))&&u.bounds.width>0);if(p){console.log(ne.yellow(` \u{1F4A1} [Self-Healing] Auto-healed "${t}" \u2192 "${d.text}" (${Math.round(d.score*100)}% confidence, ${d.strategy})`)),n&&(n.healedFrom=t,n.healedTo=d.text,n.target=d.text);let u=p.text?"text":p.contentDesc?"contentDesc":"resourceId",h=this.lastHierarchy?.hash,m=Math.min(R.memory?.maxHealedConfidence??.65,d.score);this.memory.recordSuccess(n?.type||"TAP",t,d.text,u,m,t,h),o=[p]}}}catch(l){ct.warn(`Self-healing resolution failed: ${l.message}`)}return{flat:r,matched:o}}buildDiagnosticError(t,e,s){return Rr(t,e,s)}async executeStep(t,e,s){let n=Zr.get(t.type);if(!n)throw new Error(`No handler registered for step type: ${t.type}`);t.target&&t.target.includes("$")&&(t.target=Lt(t.target,Object.fromEntries(this.runtimeVariables))),t.value&&t.value.includes("$")&&(t.value=Lt(t.value,Object.fromEntries(this.runtimeVariables))),(t.type==="LAUNCH_APP"||t.type==="RESTART_APP")&&t.target&&(this.targetPackage=t.target);let i={targetPackage:this.targetPackage,driver:this.driver,memory:this.memory,serial:e,screenDimensions:this.screenDimensions,navBarHeight:this.navBarHeight,lastHierarchy:this.lastHierarchy,setLastHierarchy:r=>{this.lastHierarchy=r},getCleanHierarchy:r=>this.getCleanHierarchy(r),waitForTarget:(r,o,l,c)=>this.waitForTarget(r,o,l,c),matchNodes:(r,o,l)=>this.matchNodes(r,o,l),ensureInViewport:(r,o)=>this.ensureInViewport(r,o),buildDiagnosticError:(r,o,l)=>this.buildDiagnosticError(r,o,l),findTargetInputField:(r,o,l)=>this.findTargetInputField(r,o,l),upcomingSteps:s,variables:this.runtimeVariables,loopStack:this.loopStack,executeChildSteps:async(r,o)=>{for(let l=0;l<r.length;l++){let c=r[l];await this.executeStep(c,o.serial,r.slice(l+1))}}};return n.execute(t,i)}findTargetInputField(t,e,s){return Mr(t,e,s,this.memory)}matchNodes(t,e,s){return Ss(t,e,this.memory,this.activeLocale,s,this.lastHierarchy?.hash)}},Vt=new st});var bi={};Qe(bi,{calculatePercentile:()=>tr,formatBenchmarkTable:()=>Ro,generateSyntheticHierarchy:()=>fi,runBenchmark:()=>Lo,summarizeMetrics:()=>Os});import ze from"chalk";function tr(a,t){if(a.length===0)return 0;let e=[...a].sort((n,i)=>n-i),s=Math.min(e.length-1,Math.max(0,Math.floor(t/100*(e.length-1))));return Math.round(e[s]*100)/100}function Os(a){if(a.length===0)return{minMs:0,maxMs:0,avgMs:0,p50Ms:0,p95Ms:0,iterations:0,durations:[]};let t=Math.round(Math.min(...a)*100)/100,e=Math.round(Math.max(...a)*100)/100,s=a.reduce((o,l)=>o+l,0),n=Math.round(s/a.length*100)/100,i=tr(a,50),r=tr(a,95);return{minMs:t,maxMs:e,avgMs:n,p50Ms:i,p95Ms:r,iterations:a.length,durations:a}}function fi(a=1e3){let t=[];t.push({index:0,className:"android.widget.FrameLayout",packageName:"com.app",text:"",resourceId:"android:id/content",contentDesc:"Main Screen Root",clickable:!1,enabled:!0,focused:!1,scrollable:!1,password:!1,selected:!1,children:[],bounds:{x1:0,y1:0,x2:1080,y2:2400,width:1080,height:2400,centerX:540,centerY:1200}}),t.push({index:1,className:"android.widget.TextView",packageName:"com.app",text:"Apex Coaching Academy - Dashboard",resourceId:"com.app:id/title_header",contentDesc:"Header Title",clickable:!1,enabled:!0,focused:!1,scrollable:!1,password:!1,selected:!1,children:[],bounds:{x1:40,y1:100,x2:1040,y2:220,width:1e3,height:120,centerX:540,centerY:160}});let e=["android.widget.TextView","android.widget.Button","android.widget.EditText","android.widget.ImageView","android.view.ViewGroup","android.widget.LinearLayout","android.widget.CheckBox","android.widget.Switch"],s=["Sign In","Email Address","Password","Submit","Save Changes","Settings","Profile","Notifications","Batch 10A","Mathematics","Physics","Attendance","Export PDF","Delete Record","Cancel","Next","Back","Search","Filter","Refresh"];for(let n=2;n<a;n++){let i=e[n%e.length],r=i.includes("Button")||i.includes("CheckBox")||i.includes("Switch"),o=s[n%s.length],l=`${o} #${n}`,c=Math.min(2400,250+n*20%2e3),d=50+n*30%900;t.push({index:n,className:i,packageName:"com.app",text:i.includes("ImageView")?"":l,resourceId:`com.app:id/element_${n}`,contentDesc:i.includes("ImageView")?`Icon of ${o}`:"",clickable:r,enabled:n%25!==0,focused:!1,scrollable:!1,password:o.toLowerCase().includes("password"),selected:!1,children:[],bounds:{x1:d,y1:c,x2:d+200,y2:c+80,width:200,height:80,centerX:d+100,centerY:c+40}})}return t}async function Lo(a=oe,t={}){let e=t.iterations||5,s=Date.now(),n=t.serial,i="Synthetic Mock Android Environment",r=!1;if(!t.forceMock)try{n=await a.ensureDefaultDevice(t.serial),n&&(i=await a.getDeviceModel(n),r=!0)}catch{r=!1}let o=[];for(let m=0;m<e;m++){let b=performance.now();r?await a.captureScreenshot(n):(await new Promise(T=>setTimeout(T,15)),Buffer.alloc(1024*1024)),o.push(performance.now()-b)}let l=[];for(let m=0;m<e;m++){let b=performance.now();r?await a.getUiHierarchy(n):await new Promise(T=>setTimeout(T,25)),l.push(performance.now()-b)}let c=fi(1e3),d=new gt,p=["Submit","Email Address","Mathematics","Save Changes","NonExistent Query Fallback"],u=[];for(let m=0;m<e;m++){let b=p[m%p.length],T=performance.now();Ss(c,b,d),u.push(performance.now()-T)}let h=Date.now()-s;return{timestamp:new Date().toISOString(),device:i,package:t.packageName,screencap:Os(o),uiHierarchy:Os(l),nodeMatching:Os(u),totalDurationMs:h}}function Ro(a){let t=(i,r)=>{let o=`${r.minMs.toFixed(1)}ms`.padStart(9),l=`${r.avgMs.toFixed(1)}ms`.padStart(9),c=`${r.p50Ms.toFixed(1)}ms`.padStart(9),d=`${r.p95Ms.toFixed(1)}ms`.padStart(9),p=`${r.maxMs.toFixed(1)}ms`.padStart(9);return`\u2502 ${i.padEnd(28)} \u2502 ${o} \u2502 ${l} \u2502 ${c} \u2502 ${d} \u2502 ${p} \u2502`};return["",ze.bold.cyan("\u26A1 PromptTest Performance Diagnostic Benchmark"),ze.gray("Device: ")+ze.bold.white(a.device)+ze.gray(` | Samples: ${a.screencap.iterations} runs`),"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",`\u2502 ${ze.bold("Operation".padEnd(28))} \u2502 ${ze.bold("Min".padStart(9))} \u2502 ${ze.bold("Avg".padStart(9))} \u2502 ${ze.bold("p50".padStart(9))} \u2502 ${ze.bold("p95".padStart(9))} \u2502 ${ze.bold("Max".padStart(9))} \u2502`,"\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524",t("Device Screencap",a.screencap),t("UI Hierarchy Dump (XML)",a.uiHierarchy),t("Node Matching (1k nodes)",a.nodeMatching),"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",ze.gray("Total Benchmark Wall-Time: ")+ze.bold.green(`${a.totalDurationMs}ms
|
|
922
|
+
======================================================`)),n.fresh&&Q>0&&S&&(await this.driver.clearAppData(S,n.serial),await new Promise(F=>setTimeout(F,1e3)))),this.runtimeVariables.clear();for(let[F,E]of Object.entries(K))this.runtimeVariables.set(F,E);this.loopStack=[];let W=this.parsePrompt(r,K),ne=1,x=n.resume&&Q===0?await this.checkpoints.getResumeIndex(d):0;x>0&&console.log(se.cyan(`\u23ED Resuming from step ${x+1} (checkpoint found).`));let f=n.only?Fr(n.only):null;for(let F=0;F<W.length;F++){let E=W[F];if(ne=F+1,f&&!f.has(ne)){console.log(se.gray(`[Step ${ne}/${W.length}] \u23E9 Skipped (not in --only range: ${n.only})`)),this.reporter.logStep(re,E.originalText,E.type,"SKIPPED",`Filtered out by --only="${n.only}"`,void 0,{videoOffsetMs:Math.max(0,Date.now()-P)},0);continue}if(F<x){this.reporter.logStep(re,E.originalText,E.type,"SKIPPED","Resumed \u2014 already passed",void 0,{videoOffsetMs:0},0);continue}let ge=W.slice(F+1);console.log(se.bold.blue(`
|
|
923
|
+
[Step ${ne}/${W.length}] ${E.originalText}`));let Y=Date.now();try{let g=await this.executeStep(E,n.serial,ge),k=Date.now()-Y;if(g&&g.fastForwarded){if(console.log(se.cyan(`\u23E9 Fast-Forward: ${g.reason}. Redundant step(s) bypassed.`)),this.reporter.logStep(re,E.originalText,E.type,"PASS",`Fast-Forward: ${g.reason}`,void 0,{videoOffsetMs:Math.max(0,Y-P)},k),console.log(se.green(`\u2714 Step ${ne} PASSED (Fast-Forward)`)),g?.skipCount&&g.skipCount>0){let M=g.skipCount;for(let v=0;v<M&&F+1<W.length;v++){F++;let C=W[F],A=F+1;this.reporter.logStep(re,C.originalText,C.type,"PASS",`Fast-Forward: ${g.reason}`,void 0,{videoOffsetMs:Math.max(0,Y-P)},0),console.log(se.gray(` [Step ${A}/${W.length}] \u23E9 Fast-Forward: ${C.originalText}`))}}}else{let M;if((n.screenshotMode==="all"||n.saveBaseline||n.compareBaseline)&&(M=await this.driver.captureScreenshot(n.serial)),M&&n.compareBaseline){let C=await this.baselines.compare(re,ne,M,n.baselineThreshold);C.passed||(console.log(se.yellow(` \u26A0\uFE0F [Baseline] Step ${ne} visual diff ${(C.diffRatio*100).toFixed(1)}% exceeds threshold ${(C.threshold*100).toFixed(1)}%`)),this.reporter.logError(`Visual baseline diff at Step ${ne}: ${(C.diffRatio*100).toFixed(1)}% changed`))}M&&n.saveBaseline&&!n.compareBaseline&&await this.baselines.saveBaseline(re,ne,M);let v={videoOffsetMs:Math.max(0,Y-P)};E.healedFrom&&(v.healed=!0,v.healedFrom=E.healedFrom,v.healedTo=E.healedTo||E.target),this.reporter.logStep(re,E.originalText,E.type,"PASS",E.target||E.value,n.screenshotMode==="all"?M:void 0,v,k),console.log(se.green(`\u2714 Step ${ne} PASSED`)),n.benchmark&&console.log(se.gray(` \u23F1 [Benchmark] Step ${ne} latency: ${k}ms (${E.type}${E.target?` -> "${E.target}"`:""})`))}n.resume&&!V&&await this.checkpoints.save(d,S,F,W.length)}catch(g){let k=g,M=Date.now()-Y;if(E.optional){console.log(se.yellow(`\u26AA Step ${ne} OPTIONAL (Skipped: ${k.message})`)),this.reporter.logStep(re,E.originalText,E.type,"SKIPPED",`Optional step bypassed: ${k.message}`,void 0,{error:k.message,videoOffsetMs:Math.max(0,Y-P)},M);continue}N++,console.log(se.red(`\u274C Step ${ne} FAILED: ${k.message}`)),E.target&&this.memory.recordFailure(E.type,E.target);let v=await this.driver.captureScreenshot(n.serial).catch(()=>{}),C=await Hr({outputDir:ft.resolve(process.cwd(),"output"),specName:d,currentSpecName:re,stepNum:ne,step:E,error:k,stepStartTime:Y,startTime:P,errorShot:v,lastHierarchy:this.lastHierarchy,driver:this.driver,serial:n.serial});if(this.reporter.logStep(re,E.originalText,E.type,"FAIL",k.message,v,{error:k.message,videoOffsetMs:Math.max(0,Y-P)},M,C),this.reporter.logError(`Step ${ne} failed: ${k.message}`),n.continueOnError)console.log(se.yellow("Continuing with next test step (--continue enabled)..."));else{let A=W.slice(F+1);if(A.length>0){console.log(se.yellow(`
|
|
924
|
+
\u{1F6D1} Aborting dependent flow due to upstream failure at Step ${ne}.`)),console.log(se.gray(`Cascading skip applied to ${A.length} downstream step(s):`));for(let D=0;D<A.length;D++){let I=A[D],H=ne+1+D;this.reporter.logStep(re,I.originalText,I.type,"SKIPPED",`Cascading skip: Precondition failed at Step ${ne} (${E.originalText})`,void 0,{videoOffsetMs:Math.max(0,Date.now()-P)},0),console.log(se.gray(` [Step ${H}/${W.length}] \u26AA SKIPPED: ${I.originalText}`))}}break}}y&&await y.sample(ne,E.originalText)}}try{let Q=this.lastHierarchy;if(Q?.flat){let K=$r(Q.flat,this.screenDimensions.width,this.screenDimensions.height);if(K.length>0){console.log(se.bold.yellow(`
|
|
925
|
+
\u{1F50D} Visual QA Sentinel Warnings (${K.length} detected):`));for(let V of K)console.log(se.yellow(` \u2022 [${V.type}] ${V.description}`)),this.reporter.logError(`Visual Defect (${V.type}): ${V.description}`)}}}catch{}await this.memory.save(),n.resume&&N===0&&await this.checkpoints.clear(d);let O=this.memory.getStats();if(O.totalMappings>0&&console.log(se.cyan(`\u{1F9E0} Knowledge Engine: ${O.totalMappings} active learned mapping(s) for ${O.package}`)),w){console.log(se.cyan("\u{1F3AC} Finalizing video recording..."));let Q=ft.resolve(process.cwd(),"output"),K=ft.join(Q,`${d}-recording.mp4`),V=await this.driver.stopScreenRecording(w,K,n.serial);V&&Fs.existsSync(V)&&(this.reporter.setVideoPath?.(`${d}-recording.mp4`),console.log(se.green(`\u2714 Screen recording saved to: output/${d}-recording.mp4`))),w=null}if(y){let Q=await y.stop();typeof this.reporter.setPerformanceMetrics=="function"&&this.reporter.setPerformanceMetrics(Q)}let L=((Date.now()-P)/1e3).toFixed(1),U=await this.reporter.generateReport(d);b&&(b.broadcastComplete(U),T&&T()),console.log(se.bold.green(`
|
|
926
|
+
\u2728 PromptTest Script Execution Finished in ${L}s!`));let j=U.failedSteps===0?se.bold.green:se.bold.yellow;return console.log(j(`Result: ${U.passedSteps}/${U.totalSteps} passed (${U.failedSteps} failed, ${U.skippedSteps} skipped).`)),console.log(se.cyan(`Report saved to output/${d}-report.html`)),process.env.CI||console.log(se.dim("\u2B50 Star PromptTest on GitHub: ")+se.underline.cyan("https://github.com/shriramsingh/prompttest-community")),console.log(""),n.json&&console.log(JSON.stringify(U,null,2)),U}finally{if(w)try{await this.driver.stopScreenRecording(w,ft.join("output",`${d}-recording.mp4`),n.serial)}catch{}m&&Ot(m)}}async getCleanHierarchy(t){let e=await this.driver.getUiHierarchy(t),{flat:s}=be(e),n=s.find(o=>(o.text==="Dismiss"||o.contentDesc==="Dismiss")&&o.bounds.width>0);n&&(await this.driver.tap(n.bounds.centerX,n.bounds.centerY,t),await new Promise(o=>setTimeout(o,400)),e=await this.driver.getUiHierarchy(t),s=be(e).flat);let i=s.find(o=>o.contentDesc&&(o.contentDesc.startsWith("!, ")||o.contentDesc.includes("react.dev/link")));if(i){let o=i.bounds.x2-45,l=i.bounds.centerY;await this.driver.tap(o,l,t),await new Promise(c=>setTimeout(c,350)),e=await this.driver.getUiHierarchy(t),s=be(e).flat}let r=ke(s);return this.lastHierarchy={xml:e,flat:s,timestamp:Date.now(),hash:r},{xml:e,flat:s}}async ensureInViewport(t,e){let s=this.screenDimensions.width,n=this.screenDimensions.height,i=Math.round(s*.5),r=this.navBarHeight>0?n-this.navBarHeight:n,o=this.navBarHeight>0?r*.94:n*R.gestures.bottomNavThresholdRatio;if(t.bounds.centerY>o||t.bounds.y1>o)return!1;let c=t.bounds.y2>r,d=t.bounds.y1<Math.round(n*.04)&&t.bounds.centerY<Math.round(n*.06);return c?(await this.driver.swipe(i,Math.round(n*.75),i,Math.round(n*.45),250,e),await new Promise(p=>setTimeout(p,200)),this.lastHierarchy=null,!0):d?(await this.driver.swipe(i,Math.round(n*.35),i,Math.round(n*.65),250,e),await new Promise(p=>setTimeout(p,200)),this.lastHierarchy=null,!0):!1}async waitForTarget(t,e,s=3e3,n){if(this.lastHierarchy&&Date.now()-this.lastHierarchy.timestamp<R.polling.hierarchyCacheTtlMs){let l=this.matchNodes(this.lastHierarchy.flat,t,n);if(l.length>0)return{flat:this.lastHierarchy.flat,matched:l}}let i=Date.now();for(;Date.now()-i<s;){let{flat:l}=await this.getCleanHierarchy(e),c=this.matchNodes(l,t,n);if(c.length>0)return{flat:l,matched:c};await new Promise(d=>setTimeout(d,fr()))}let{flat:r}=await this.getCleanHierarchy(e),o=this.matchNodes(r,t,n);if(o.length===0&&(this.healEnabled||R.ai.selfHealing))try{let l=await this.semanticResolver.findSuggestions(t,r,{locale:this.activeLocale}),c=R.ai.confidenceThreshold||.75,d=l.find(p=>p.score>=c);if(d){let p=d.node||r.find(u=>(me(d.text,u.text)||me(d.text,u.contentDesc))&&u.bounds.width>0);if(p){console.log(se.yellow(` \u{1F4A1} [Self-Healing] Auto-healed "${t}" \u2192 "${d.text}" (${Math.round(d.score*100)}% confidence, ${d.strategy})`)),n&&(n.healedFrom=t,n.healedTo=d.text,n.target=d.text);let u=p.text?"text":p.contentDesc?"contentDesc":"resourceId",h=this.lastHierarchy?.hash,m=Math.min(R.memory?.maxHealedConfidence??.65,d.score);this.memory.recordSuccess(n?.type||"TAP",t,d.text,u,m,t,h),o=[p]}}}catch(l){ct.warn(`Self-healing resolution failed: ${l.message}`)}return{flat:r,matched:o}}buildDiagnosticError(t,e,s){return Rr(t,e,s)}async executeStep(t,e,s){let n=Zr.get(t.type);if(!n)throw new Error(`No handler registered for step type: ${t.type}`);t.target&&t.target.includes("$")&&(t.target=Lt(t.target,Object.fromEntries(this.runtimeVariables))),t.value&&t.value.includes("$")&&(t.value=Lt(t.value,Object.fromEntries(this.runtimeVariables))),(t.type==="LAUNCH_APP"||t.type==="RESTART_APP")&&t.target&&(this.targetPackage=t.target);let i={targetPackage:this.targetPackage,driver:this.driver,memory:this.memory,serial:e,screenDimensions:this.screenDimensions,navBarHeight:this.navBarHeight,lastHierarchy:this.lastHierarchy,setLastHierarchy:r=>{this.lastHierarchy=r},getCleanHierarchy:r=>this.getCleanHierarchy(r),waitForTarget:(r,o,l,c)=>this.waitForTarget(r,o,l,c),matchNodes:(r,o,l)=>this.matchNodes(r,o,l),ensureInViewport:(r,o)=>this.ensureInViewport(r,o),buildDiagnosticError:(r,o,l)=>this.buildDiagnosticError(r,o,l),findTargetInputField:(r,o,l)=>this.findTargetInputField(r,o,l),upcomingSteps:s,variables:this.runtimeVariables,loopStack:this.loopStack,executeChildSteps:async(r,o)=>{for(let l=0;l<r.length;l++){let c=r[l];await this.executeStep(c,o.serial,r.slice(l+1))}}};return n.execute(t,i)}findTargetInputField(t,e,s){return Mr(t,e,s,this.memory)}matchNodes(t,e,s){return Ss(t,e,this.memory,this.activeLocale,s,this.lastHierarchy?.hash)}},Vt=new st});var bi={};Qe(bi,{calculatePercentile:()=>tr,formatBenchmarkTable:()=>Ro,generateSyntheticHierarchy:()=>fi,runBenchmark:()=>Lo,summarizeMetrics:()=>Os});import ze from"chalk";function tr(a,t){if(a.length===0)return 0;let e=[...a].sort((n,i)=>n-i),s=Math.min(e.length-1,Math.max(0,Math.floor(t/100*(e.length-1))));return Math.round(e[s]*100)/100}function Os(a){if(a.length===0)return{minMs:0,maxMs:0,avgMs:0,p50Ms:0,p95Ms:0,iterations:0,durations:[]};let t=Math.round(Math.min(...a)*100)/100,e=Math.round(Math.max(...a)*100)/100,s=a.reduce((o,l)=>o+l,0),n=Math.round(s/a.length*100)/100,i=tr(a,50),r=tr(a,95);return{minMs:t,maxMs:e,avgMs:n,p50Ms:i,p95Ms:r,iterations:a.length,durations:a}}function fi(a=1e3){let t=[];t.push({index:0,className:"android.widget.FrameLayout",packageName:"com.app",text:"",resourceId:"android:id/content",contentDesc:"Main Screen Root",clickable:!1,enabled:!0,focused:!1,scrollable:!1,password:!1,selected:!1,children:[],bounds:{x1:0,y1:0,x2:1080,y2:2400,width:1080,height:2400,centerX:540,centerY:1200}}),t.push({index:1,className:"android.widget.TextView",packageName:"com.app",text:"Apex Coaching Academy - Dashboard",resourceId:"com.app:id/title_header",contentDesc:"Header Title",clickable:!1,enabled:!0,focused:!1,scrollable:!1,password:!1,selected:!1,children:[],bounds:{x1:40,y1:100,x2:1040,y2:220,width:1e3,height:120,centerX:540,centerY:160}});let e=["android.widget.TextView","android.widget.Button","android.widget.EditText","android.widget.ImageView","android.view.ViewGroup","android.widget.LinearLayout","android.widget.CheckBox","android.widget.Switch"],s=["Sign In","Email Address","Password","Submit","Save Changes","Settings","Profile","Notifications","Batch 10A","Mathematics","Physics","Attendance","Export PDF","Delete Record","Cancel","Next","Back","Search","Filter","Refresh"];for(let n=2;n<a;n++){let i=e[n%e.length],r=i.includes("Button")||i.includes("CheckBox")||i.includes("Switch"),o=s[n%s.length],l=`${o} #${n}`,c=Math.min(2400,250+n*20%2e3),d=50+n*30%900;t.push({index:n,className:i,packageName:"com.app",text:i.includes("ImageView")?"":l,resourceId:`com.app:id/element_${n}`,contentDesc:i.includes("ImageView")?`Icon of ${o}`:"",clickable:r,enabled:n%25!==0,focused:!1,scrollable:!1,password:o.toLowerCase().includes("password"),selected:!1,children:[],bounds:{x1:d,y1:c,x2:d+200,y2:c+80,width:200,height:80,centerX:d+100,centerY:c+40}})}return t}async function Lo(a=oe,t={}){let e=t.iterations||5,s=Date.now(),n=t.serial,i="Synthetic Mock Android Environment",r=!1;if(!t.forceMock)try{n=await a.ensureDefaultDevice(t.serial),n&&(i=await a.getDeviceModel(n),r=!0)}catch{r=!1}let o=[];for(let m=0;m<e;m++){let b=performance.now();r?await a.captureScreenshot(n):(await new Promise(T=>setTimeout(T,15)),Buffer.alloc(1024*1024)),o.push(performance.now()-b)}let l=[];for(let m=0;m<e;m++){let b=performance.now();r?await a.getUiHierarchy(n):await new Promise(T=>setTimeout(T,25)),l.push(performance.now()-b)}let c=fi(1e3),d=new gt,p=["Submit","Email Address","Mathematics","Save Changes","NonExistent Query Fallback"],u=[];for(let m=0;m<e;m++){let b=p[m%p.length],T=performance.now();Ss(c,b,d),u.push(performance.now()-T)}let h=Date.now()-s;return{timestamp:new Date().toISOString(),device:i,package:t.packageName,screencap:Os(o),uiHierarchy:Os(l),nodeMatching:Os(u),totalDurationMs:h}}function Ro(a){let t=(i,r)=>{let o=`${r.minMs.toFixed(1)}ms`.padStart(9),l=`${r.avgMs.toFixed(1)}ms`.padStart(9),c=`${r.p50Ms.toFixed(1)}ms`.padStart(9),d=`${r.p95Ms.toFixed(1)}ms`.padStart(9),p=`${r.maxMs.toFixed(1)}ms`.padStart(9);return`\u2502 ${i.padEnd(28)} \u2502 ${o} \u2502 ${l} \u2502 ${c} \u2502 ${d} \u2502 ${p} \u2502`};return["",ze.bold.cyan("\u26A1 PromptTest Performance Diagnostic Benchmark"),ze.gray("Device: ")+ze.bold.white(a.device)+ze.gray(` | Samples: ${a.screencap.iterations} runs`),"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",`\u2502 ${ze.bold("Operation".padEnd(28))} \u2502 ${ze.bold("Min".padStart(9))} \u2502 ${ze.bold("Avg".padStart(9))} \u2502 ${ze.bold("p50".padStart(9))} \u2502 ${ze.bold("p95".padStart(9))} \u2502 ${ze.bold("Max".padStart(9))} \u2502`,"\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524",t("Device Screencap",a.screencap),t("UI Hierarchy Dump (XML)",a.uiHierarchy),t("Node Matching (1k nodes)",a.nodeMatching),"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",ze.gray("Total Benchmark Wall-Time: ")+ze.bold.green(`${a.totalDurationMs}ms
|
|
928
927
|
`)].join(`
|
|
929
928
|
`)}var sr=ie(()=>{"use strict";Re();et();Pt()});var Si={};Qe(Si,{startRepl:()=>nr});import Mo from"readline";import ns from"fs";import yi from"path";import he from"chalk";async function nr(a=oe,t,e={}){let s=await a.ensureDefaultDevice(e.serial),n=t||new st(a),i={history:[],successfulSteps:[]},r=e.input||process.stdin,o=e.output||process.stdout,l=Mo.createInterface({input:r,output:o,prompt:he.bold.cyan("prompttest> ")}),c=!1,d=()=>{if(!c)try{l.prompt()}catch{}},p=u=>{o.write(u+`
|
|
930
929
|
`)};return p(he.bold.cyan(`
|
|
@@ -1382,20 +1381,20 @@ jobs:
|
|
|
1382
1381
|
if-no-files-found: ignore
|
|
1383
1382
|
`;Le.writeFileSync(r,l,"utf-8"),Le.writeFileSync(o,c,"utf-8"),console.log($.bold.green("\u2714 Created GitHub Actions workflows:")),console.log($.cyan(` \u2022 ${r} (Headless Node 22 & 24 quality gates)`)),console.log($.cyan(` \u2022 ${o} (Android API 30 emulator smoke gate + opt-in app specs)
|
|
1384
1383
|
`));break}case"benchmark":{let s=a.slice(1),n=s.includes("--json"),i=s.find(h=>h.startsWith("--iterations=")),r=i?parseInt(i.replace("--iterations=",""),10):5,o=s.find(h=>h.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),c=s.filter((h,m,b)=>!h.startsWith("-")&&h!==o&&(m===0||b[m-1]!=="-s"))[0];n||console.log($.cyan(`
|
|
1385
|
-
\u26A1 Running PromptTest Performance Diagnostic Benchmark...`));let{runBenchmark:d,formatBenchmarkTable:p}=await Promise.resolve().then(()=>(sr(),bi)),u=await d(oe,{iterations:r,serial:o,packageName:c});console.log(n?JSON.stringify(u,null,2):p(u));break}case"run":{let s=[...a.slice(1)],n=s.includes("--screenshots")||s.includes("--all-screenshots")||s.includes("--screenshots=all"),i=s.indexOf("--screenshots");i!==-1&&s[i+1]==="all"&&(n=!0,s.splice(i+1,1));let r=s.includes("--continue")||s.includes("--continue-on-error"),o=s.includes("--fresh")||s.includes("--cold-start"),l=s.includes("--all-devices"),c=s.includes("--resume"),d=s.includes("--save-baseline"),p=s.includes("--compare-baseline"),u=s.find(D=>D.startsWith("--baseline-threshold=")),h=u?parseFloat(u.replace("--baseline-threshold=","")):void 0,m=s.find(D=>D.startsWith("--reporter="))?.replace("--reporter=",""),b=s.find(D=>D.startsWith("--apk="))?.replace("--apk=",""),T=s.includes("--video"),w=s.includes("--heal"),S=s.includes("--expand"),y=s.includes("--expand-apply"),P=s.includes("--no-ai"),N=s.includes("--benchmark"),O=s.includes("--profile"),L=s.find(D=>D.startsWith("--reporters=")),U=L?L.replace("--reporters=","").split(",").map(D=>D.trim()):void 0,j=s.find(D=>D.startsWith("--data="))?.replace("--data=",""),Q=s.find(D=>D.startsWith("--iterations=")),K=Q?parseInt(Q.replace("--iterations=",""),10):void 0,V=s.find(D=>D.startsWith("--only-row=")),ye=V?parseInt(V.replace("--only-row=",""),10):void 0,re=s.find(D=>D.startsWith("--locale="))?.replace("--locale=",""),W=s.includes("--dry-run"),
|
|
1384
|
+
\u26A1 Running PromptTest Performance Diagnostic Benchmark...`));let{runBenchmark:d,formatBenchmarkTable:p}=await Promise.resolve().then(()=>(sr(),bi)),u=await d(oe,{iterations:r,serial:o,packageName:c});console.log(n?JSON.stringify(u,null,2):p(u));break}case"run":{let s=[...a.slice(1)],n=s.includes("--screenshots")||s.includes("--all-screenshots")||s.includes("--screenshots=all"),i=s.indexOf("--screenshots");i!==-1&&s[i+1]==="all"&&(n=!0,s.splice(i+1,1));let r=s.includes("--continue")||s.includes("--continue-on-error"),o=s.includes("--fresh")||s.includes("--cold-start"),l=s.includes("--all-devices"),c=s.includes("--resume"),d=s.includes("--save-baseline"),p=s.includes("--compare-baseline"),u=s.find(D=>D.startsWith("--baseline-threshold=")),h=u?parseFloat(u.replace("--baseline-threshold=","")):void 0,m=s.find(D=>D.startsWith("--reporter="))?.replace("--reporter=",""),b=s.find(D=>D.startsWith("--apk="))?.replace("--apk=",""),T=s.includes("--video"),w=s.includes("--heal"),S=s.includes("--expand"),y=s.includes("--expand-apply"),P=s.includes("--no-ai"),N=s.includes("--benchmark"),O=s.includes("--profile"),L=s.find(D=>D.startsWith("--reporters=")),U=L?L.replace("--reporters=","").split(",").map(D=>D.trim()):void 0,j=s.find(D=>D.startsWith("--data="))?.replace("--data=",""),Q=s.find(D=>D.startsWith("--iterations=")),K=Q?parseInt(Q.replace("--iterations=",""),10):void 0,V=s.find(D=>D.startsWith("--only-row=")),ye=V?parseInt(V.replace("--only-row=",""),10):void 0,re=s.find(D=>D.startsWith("--locale="))?.replace("--locale=",""),W=s.includes("--dry-run"),ne=s.includes("--list-steps"),x=s.includes("--json"),f=s.find(D=>D.startsWith("--only="))?.replace("--only=",""),F=s.find(D=>D.startsWith("--tags="))?.replace("--tags=",""),E=s.includes("--embed-screenshots"),ge=s.find(D=>D.startsWith("--serve=")||D==="--serve"),Y;if(ge)if(ge.startsWith("--serve=")){let D=parseInt(ge.replace("--serve=",""),10);Y=isNaN(D)?4040:D}else{let D=s.indexOf("--serve"),I=s[D+1];I&&/^\d+$/.test(I)?(Y=parseInt(I,10),s.splice(D+1,1)):Y=4040}let g=s.find(D=>D.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),k={};for(let D of s)if(D.startsWith("--var:")){let[I,H]=D.replace("--var:","").split("=");I&&H&&(k[I]=H)}let M=s.filter((D,I,H)=>!(D.startsWith("-")||D===g||I>0&&H[I-1]==="-s")),v=M[0],C=M[1];if(v||(console.log($.red("Error: Please specify a spec file path. E.g. prompttest run specs/coachconnect_flow.txt")),process.exit(3)),Le.existsSync(v)&&Le.statSync(v).isDirectory()&&C&&!C.startsWith("--")){let D=He.join(v,C);Le.existsSync(D)&&(v=D,C=M[2])}if(S||y){console.log($.cyan(`
|
|
1386
1385
|
\u{1F916} Analyzing and expanding spec "${v}" using Semantic AI...`));let D=Le.readFileSync(v,"utf-8"),{expanded:I,stepsAdded:H}=await Vt.expandSpec(D);if(H>0?(console.log($.green(`\u2714 Successfully expanded ${H} macro step(s)!`)),y?(Le.writeFileSync(v,I,"utf-8"),console.log($.bold.green(`\u2714 Applied expanded steps directly to ${v}.
|
|
1387
1386
|
`))):(console.log($.yellow(`
|
|
1388
1387
|
--- Preview of Expanded Spec (--expand-apply to overwrite) ---`)),console.log($.gray(I)),console.log($.yellow(`---------------------------------------------------------------
|
|
1389
1388
|
`)))):console.log($.gray(`No high-level macro steps detected for expansion.
|
|
1390
1389
|
`)),!s.includes("--execute"))break}if(l){let I=(await oe.getConnectedDevices()).filter(G=>G.state==="device");I.length===0&&(console.log($.red("No online devices found for multi-device run.")),process.exit(2)),console.log($.bold.cyan(`
|
|
1391
|
-
\u{1F4F1} Multi-Device Matrix Execution across ${I.length} device(s):`));for(let G of I)console.log(`- ${G.serial} (${G.model||"Android"})`);let H=await Promise.allSettled(I.map(G=>{let Z=G.serial.replace(/[^a-zA-Z0-9._-]/g,"_");return Pi(new lt(G.serial),new gt,new ut(He.join("output",Z))).runSpec(v,{packageName:C,serial:G.serial,screenshotMode:n?"all":"on-failure",continueOnError:r,fresh:o,resume:c,saveBaseline:d,compareBaseline:p,baselineThreshold:h,apkPath:b,variables:k,video:T,reporters:U,heal:w,noAi:P,dataFile:j,iterations:K,onlyRow:ye,locale:re,benchmark:N,dryRun:W,listSteps:
|
|
1392
|
-
\u2728 Multi-Device Execution Finished!`)),H.some(G=>G.status==="rejected"||G.status==="fulfilled"&&G.value&&G.value.failedSteps>0)&&process.exit(1);break}let A=await Vt.runSpec(v,{packageName:C,serial:g,screenshotMode:n?"all":"on-failure",continueOnError:r,fresh:o,resume:c,saveBaseline:d,compareBaseline:p,baselineThreshold:h,apkPath:b,variables:k,video:T,reporters:U,heal:w,noAi:P,dataFile:j,iterations:K,onlyRow:ye,locale:re,embedScreenshots:E,serve:Y,benchmark:N,dryRun:W,listSteps:
|
|
1390
|
+
\u{1F4F1} Multi-Device Matrix Execution across ${I.length} device(s):`));for(let G of I)console.log(`- ${G.serial} (${G.model||"Android"})`);let H=await Promise.allSettled(I.map(G=>{let Z=G.serial.replace(/[^a-zA-Z0-9._-]/g,"_");return Pi(new lt(G.serial),new gt,new ut(He.join("output",Z))).runSpec(v,{packageName:C,serial:G.serial,screenshotMode:n?"all":"on-failure",continueOnError:r,fresh:o,resume:c,saveBaseline:d,compareBaseline:p,baselineThreshold:h,apkPath:b,variables:k,video:T,reporters:U,heal:w,noAi:P,dataFile:j,iterations:K,onlyRow:ye,locale:re,benchmark:N,dryRun:W,listSteps:ne,only:f,tags:F,json:x,profile:O})}));console.log($.bold.green(`
|
|
1391
|
+
\u2728 Multi-Device Execution Finished!`)),H.some(G=>G.status==="rejected"||G.status==="fulfilled"&&G.value&&G.value.failedSteps>0)&&process.exit(1);break}let A=await Vt.runSpec(v,{packageName:C,serial:g,screenshotMode:n?"all":"on-failure",continueOnError:r,fresh:o,resume:c,saveBaseline:d,compareBaseline:p,baselineThreshold:h,apkPath:b,variables:k,video:T,reporters:U,heal:w,noAi:P,dataFile:j,iterations:K,onlyRow:ye,locale:re,embedScreenshots:E,serve:Y,benchmark:N,dryRun:W,listSteps:ne,only:f,tags:F,json:x,profile:O,reporter:m?(await import(He.resolve(m))).default:void 0});A&&A.failedSteps>0&&process.exit(1);break}case"serve":case"server":{let s=a.slice(1),n=s.find(l=>l.startsWith("--port="))?.replace("--port=","")||s.find(l=>/^\d+$/.test(l))||process.env.PORT,i=n?parseInt(n,10):4040,{startLiveServer:r}=await Promise.resolve().then(()=>(Ms(),li));console.log($.cyan(`
|
|
1393
1392
|
\u{1F4E1} Starting PromptTest Engine on port ${i}...`));let o=await r({port:i});console.log($.green(`\u2714 PromptTest Engine running at: ${o.url}
|
|
1394
|
-
`)),await new Promise(()=>{});break}case"watch":{let s=[...a.slice(1)],n=s.includes("--screenshots")||s.includes("--all-screenshots")||s.includes("--screenshots=all"),i=s.indexOf("--screenshots");i!==-1&&s[i+1]==="all"&&(n=!0,s.splice(i+1,1));let r=s.includes("--continue")||s.includes("--continue-on-error"),o=s.includes("--fresh")||s.includes("--cold-start"),l=s.includes("--resume"),c=s.includes("--save-baseline"),d=s.includes("--compare-baseline"),p=s.find(Y=>Y.startsWith("--baseline-threshold=")),u=p?parseFloat(p.replace("--baseline-threshold=","")):void 0,h=s.find(Y=>Y.startsWith("--reporter="))?.replace("--reporter=",""),m=s.find(Y=>Y.startsWith("--apk="))?.replace("--apk=",""),b=s.includes("--video"),T=s.includes("--heal"),w=s.includes("--no-ai"),S=s.find(Y=>Y.startsWith("--reporters=")),y=S?S.replace("--reporters=","").split(",").map(Y=>Y.trim()):void 0,P=s.find(Y=>Y.startsWith("--data="))?.replace("--data=",""),N=s.find(Y=>Y.startsWith("--iterations=")),O=N?parseInt(N.replace("--iterations=",""),10):void 0,L=s.find(Y=>Y.startsWith("--only-row=")),U=L?parseInt(L.replace("--only-row=",""),10):void 0,j=s.find(Y=>Y.startsWith("--locale="))?.replace("--locale=",""),Q=s.includes("--embed-screenshots"),K=s.find(Y=>Y.startsWith("--serve=")||Y==="--serve"),V;if(K)if(K.startsWith("--serve=")){let Y=parseInt(K.replace("--serve=",""),10);V=isNaN(Y)?4040:Y}else{let Y=s.indexOf("--serve"),g=s[Y+1];g&&/^\d+$/.test(g)?(V=parseInt(g,10),s.splice(Y+1,1)):V=4040}let ye=s.find(Y=>Y.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),re={};for(let Y of s)if(Y.startsWith("--var:")){let[g,k]=Y.replace("--var:","").split("=");g&&k&&(re[g]=k)}let W=s.filter((Y,g,k)=>!(Y.startsWith("-")||Y===ye||g>0&&k[g-1]==="-s")),
|
|
1395
|
-
\u{1F440} PromptTest Watch Mode Active`)),console.log($.gray("Watching: ")+$.white.bold(
|
|
1393
|
+
`)),await new Promise(()=>{});break}case"watch":{let s=[...a.slice(1)],n=s.includes("--screenshots")||s.includes("--all-screenshots")||s.includes("--screenshots=all"),i=s.indexOf("--screenshots");i!==-1&&s[i+1]==="all"&&(n=!0,s.splice(i+1,1));let r=s.includes("--continue")||s.includes("--continue-on-error"),o=s.includes("--fresh")||s.includes("--cold-start"),l=s.includes("--resume"),c=s.includes("--save-baseline"),d=s.includes("--compare-baseline"),p=s.find(Y=>Y.startsWith("--baseline-threshold=")),u=p?parseFloat(p.replace("--baseline-threshold=","")):void 0,h=s.find(Y=>Y.startsWith("--reporter="))?.replace("--reporter=",""),m=s.find(Y=>Y.startsWith("--apk="))?.replace("--apk=",""),b=s.includes("--video"),T=s.includes("--heal"),w=s.includes("--no-ai"),S=s.find(Y=>Y.startsWith("--reporters=")),y=S?S.replace("--reporters=","").split(",").map(Y=>Y.trim()):void 0,P=s.find(Y=>Y.startsWith("--data="))?.replace("--data=",""),N=s.find(Y=>Y.startsWith("--iterations=")),O=N?parseInt(N.replace("--iterations=",""),10):void 0,L=s.find(Y=>Y.startsWith("--only-row=")),U=L?parseInt(L.replace("--only-row=",""),10):void 0,j=s.find(Y=>Y.startsWith("--locale="))?.replace("--locale=",""),Q=s.includes("--embed-screenshots"),K=s.find(Y=>Y.startsWith("--serve=")||Y==="--serve"),V;if(K)if(K.startsWith("--serve=")){let Y=parseInt(K.replace("--serve=",""),10);V=isNaN(Y)?4040:Y}else{let Y=s.indexOf("--serve"),g=s[Y+1];g&&/^\d+$/.test(g)?(V=parseInt(g,10),s.splice(Y+1,1)):V=4040}let ye=s.find(Y=>Y.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),re={};for(let Y of s)if(Y.startsWith("--var:")){let[g,k]=Y.replace("--var:","").split("=");g&&k&&(re[g]=k)}let W=s.filter((Y,g,k)=>!(Y.startsWith("-")||Y===ye||g>0&&k[g-1]==="-s")),ne=W[0],x=W[1];ne||(console.log($.red("Error: Please specify a spec file path to watch. E.g. prompttest watch specs/flow.txt")),process.exit(3)),Le.existsSync(ne)||(console.log($.red(`Error: Spec file not found: ${ne}`)),process.exit(3)),console.log($.bold.cyan(`
|
|
1394
|
+
\u{1F440} PromptTest Watch Mode Active`)),console.log($.gray("Watching: ")+$.white.bold(ne)),V&&console.log($.gray("Live Monitor: ")+$.magenta.bold(`http://localhost:${typeof V=="number"?V:4040}`)),console.log($.gray(`Press Ctrl+C to exit.
|
|
1396
1395
|
`));let f=!1,F=!1,E=async()=>{if(f){F=!0;return}f=!0,F=!1;try{console.log($.cyan(`
|
|
1397
|
-
\u26A1 [${new Date().toLocaleTimeString()}] Running ${
|
|
1398
|
-
\u{1F4DD} Change detected in ${
|
|
1396
|
+
\u26A1 [${new Date().toLocaleTimeString()}] Running ${ne}...`)),await Vt.runSpec(ne,{packageName:x,serial:ye,screenshotMode:n?"all":"on-failure",continueOnError:r,fresh:o,resume:l,saveBaseline:c,compareBaseline:d,baselineThreshold:u,apkPath:m,variables:re,video:b,reporters:y,heal:T,noAi:w,dataFile:P,iterations:O,onlyRow:U,locale:j,embedScreenshots:Q,serve:V,reporter:h?(await import(He.resolve(h))).default:void 0})}catch(Y){let g=Y instanceof Error?Y.message:String(Y);console.error($.red(`Run error: ${g}`))}finally{f=!1,F&&(F=!1,setTimeout(E,100))}};await E();let ge=null;Le.watch(ne,()=>{ge&&clearTimeout(ge),ge=setTimeout(()=>{console.log($.yellow(`
|
|
1397
|
+
\u{1F4DD} Change detected in ${ne}`)),E()},300)}),await new Promise(()=>{});break}case"wifi":{let s=a[1];console.log($.cyan(`
|
|
1399
1398
|
\u{1F4F6} Establishing Wireless ADB Connection...`));try{let n=await oe.connectWifi(s);n.success?(console.log($.bold.green(`\u2714 Connected to ${n.target} over Wi-Fi successfully!`)),console.log($.yellow("\u{1F449} You can now safely UNPLUG the USB cable.")),console.log($.gray(`PromptTest will execute all tests wirelessly over ${n.target}
|
|
1400
1399
|
`))):(console.log($.yellow(`Connection response: ${n.message}`)),process.exitCode=1)}catch(n){let i=n instanceof Error?n.message:String(n);console.log($.red(`\u274C Wireless connection failed: ${i}`)),process.exit(1)}break}case"disconnect":{let s=a[1];console.log($.cyan(`
|
|
1401
1400
|
\u{1F50C} Disconnecting Wireless ADB...`));let n=await oe.disconnectWifi(s);console.log($.green(`\u2714 ${n||"Disconnected wireless devices."}
|