prompttest 1.5.0 → 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.
@@ -4,87 +4,87 @@
4
4
  * Licensed under the Business Source License 1.1 (BSL 1.1).
5
5
  * See LICENSE file in repository root for terms and conditions.
6
6
  */
7
- var un=Object.defineProperty;var hn=(a,e,t)=>()=>{if(t)throw t[0];try{return a&&(e=a(a=0)),e}catch(s){throw t=[s],s}};var gn=(a,e)=>{for(var t in e)un(a,t,{get:e[t],enumerable:!0})};var an={};gn(an,{buildFeedbackUrl:()=>oi,startFeedbackSession:()=>mr});import gr from"readline";import Ft from"os";import me from"chalk";function oi(a){let e=(a.message||"Feedback").split(`
8
- `)[0].trim(),t=e.length>70?e.substring(0,67)+"...":e,s=a.rating?"\u2B50".repeat(a.rating):"";if(a.category==="feature"){let n=new URLSearchParams;return n.set("template","2_feature_request.yml"),n.set("title",`[Feature]: ${t}`),n.set("problem",`User feedback submission${s?` (${s} ${a.rating}/5)`:""}:
9
- ${a.message}`),n.set("proposed_solution",a.message),n.set("category","Plain-English DSL / Syntax"),`${ni}/issues/new?${n.toString()}`}if(a.category==="bug"){let n=new URLSearchParams;return n.set("template","1_bug_report.yml"),n.set("title",`[Bug]: ${t}`),n.set("description",a.message),n.set("command_run","npx prompttest"),n.set("environment",`${Ft.type()} ${Ft.release()} (${Ft.arch()}), Node: ${process.version}`),`${ni}/issues/new?${n.toString()}`}let i=new URLSearchParams;i.set("template","3_feedback.yml");let r=a.rating?` ${"\u2B50".repeat(a.rating)}`:"";if(i.set("title",`[Feedback${r}]: ${t}`),i.set("message",a.message),typeof a.rating=="number"){let o={5:0,4:1,3:2,2:3,1:4}[a.rating]??0;i.set("rating",String(o))}return i.set("environment",`${Ft.type()} ${Ft.release()} (${Ft.arch()}), Node: ${process.version}`),`${ni}/issues/new?${i.toString()}`}function ri(a,e){return new Promise(t=>{if(a.closed){t("");return}try{a.question(e,s=>{t(s.trim())})}catch{t("")}})}async function mr(a={}){let e=a.input||process.stdin,t=a.output||process.stdout,s=(r="")=>{t.write(r+`
10
- `)};if(a.directMessage&&a.directMessage.trim()){let r=a.category||"general",n=oi({category:r,message:a.directMessage.trim()});return s(me.bold.cyan(`
11
- \u26A1 PromptTest \u2014 Community Feedback`)),s(me.gray("\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),s(me.green("\u2714 Thank you for your feedback! Here is your 1-click submission link:")),s(me.bold.underline.cyan(n)),s(me.gray(`\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
12
- `)),n}let i=gr.createInterface({input:e,output:t});try{s(me.bold.cyan(`
13
- \u26A1 PromptTest \u2014 Community Feedback & Suggestions`)),s(me.gray("\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),s(me.white("We want to make PromptTest the best mobile QA engine in your toolchain.")),s(me.white(`Your feedback goes directly to our open community tracker!
14
- `)),s(me.bold.white("How would you rate your experience with PromptTest?")),s(` ${me.cyan("[1]")} \u2B50\u2B50\u2B50\u2B50\u2B50 (5/5) \u2014 Loving it!`),s(` ${me.cyan("[2]")} \u2B50\u2B50\u2B50\u2B50 (4/5) \u2014 Great, but needs minor improvements`),s(` ${me.cyan("[3]")} \u2B50\u2B50\u2B50 (3/5) \u2014 Okay, encountered some friction`),s(` ${me.cyan("[4]")} \u2B50\u2B50 (2/5) \u2014 Frustrating / missing key features`),s(` ${me.cyan("[5]")} \u2B50 (1/5) \u2014 Not working for me`);let r=await ri(i,me.bold.cyan(`
15
- Select rating [1-5] (default: 1): `)),n=parseInt(r,10),c={1:5,2:4,3:3,4:2,5:1}[n]||5;s(me.bold.white(`
16
- What type of feedback would you like to share?`)),s(` ${me.cyan("[1]")} \u{1F4A1} Feature Request or syntax suggestion`),s(` ${me.cyan("[2]")} \u{1F41B} Bug report or unexpected error`),s(` ${me.cyan("[3]")} \u{1F4AC} General feedback, question, or compliment`);let l=await ri(i,me.bold.cyan(`
17
- Select category [1-3] (default: 1): `)),p="feature";l==="2"?p="bug":l==="3"&&(p="general"),s(me.bold.white(`
18
- Please share your thoughts, ideas, or feedback:`));let h=await ri(i,me.bold.cyan("> "))||"Overall great experience with PromptTest!",u=oi({rating:c,category:p,message:h});return s(me.gray(`
19
- \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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`)),s(me.green.bold("\u2728 Thank you! We generated your 1-click submission:")),s(me.bold.underline.cyan(u)),s(me.gray(`\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
20
- `)),u}finally{try{i.close()}catch{}}}var ni,ai=hn(()=>{"use strict";ni="https://github.com/shriramsingh/prompttest-community"});import Ye from"fs";import Ie from"path";import V from"chalk";import{spawn as Pt}from"child_process";import ds from"readline";var xe={DEVICES:["devices","-l"],SCREENCAP:["exec-out","screencap","-p"],FOREGROUND_APP:["shell","dumpsys","window"],CURRENT_FOCUS:["shell","dumpsys","activity","activities"],FORCE_STOP:a=>["shell","am","force-stop",a],CLEAR_APP:a=>["shell","pm","clear",a],LAUNCH_APP:a=>["shell","am","start","-a","android.intent.action.MAIN","-c","android.intent.category.LAUNCHER",a],UIAUTOMATOR_DUMP:["exec-out","uiautomator","dump","--compressed","/dev/tty"],INPUT_TAP:(a,e)=>["shell","input","tap",a.toString(),e.toString()],INPUT_TEXT:a=>["shell","input","text",a],INPUT_KEY:a=>["shell","input","keyevent",a.toString()],INPUT_SWIPE:(a,e,t,s,i=300)=>["shell","input","swipe",a.toString(),e.toString(),t.toString(),s.toString(),i.toString()],LOGCAT_CRASH:a=>["shell","logcat","-d","-b","crash","-s","AndroidRuntime:E"],CLEAR_CRASH_LOGS:()=>["shell","logcat","-b","crash","-c"],GRANT_PERMISSION:(a,e)=>["shell","pm","grant",a,e.startsWith("android.permission.")?e:`android.permission.${e}`],INSTALL_APK:a=>["install","-r",a],UNINSTALL_APP:a=>["uninstall",a],DEEP_LINK:a=>["shell","am","start","-a","android.intent.action.VIEW","-d",a],TCPIP:(a=5555)=>["tcpip",a.toString()],CONNECT:a=>["connect",a],DISCONNECT:a=>a?["disconnect",a]:["disconnect"],GET_WLAN_IP:["shell","ip","-f","inet","addr","show","wlan0"]};function mn(){let a=process.env.PROMPTTEST_SETTLE_FACTOR;if(!a)return 1;let e=parseFloat(a);return isNaN(e)||e<=0?1:e}function Le(a,e=50){return Math.max(e,Math.round(a*mn()))}var C={adb:{commandTimeoutMs:2e4,binaryTimeoutMs:45e3,retryCount:2,retryBaseDelayMs:200},polling:{intervalMs:200,jitterMs:80,defaultTimeoutMs:3e3,extendedTimeoutMs:3500,hierarchyCacheTtlMs:3500,verifyCacheTtlMs:2e3},settle:{get tapMs(){return Le(500)},get backMs(){return Le(700)},get launchMs(){return Le(2500)},get stopMs(){return Le(600)},get keyboardDismissMs(){return Le(500)},get scrollMs(){return Le(350)},get toggleMs(){return Le(350)},get selectMs(){return Le(600)},get inputFocusMs(){return Le(250)},get inputClearMs(){return Le(150)},get inputDoneMs(){return Le(350)},get submitWaitMs(){return Le(1800)}},gestures:{swipeDurationMs:300,longPressDurationMs:1e3,bottomNavThresholdRatio:.88,tapTargetYRatio:.96},recorder:{staleTapTimeoutMs:3e3,asyncRefreshDelayMs:80,heartbeatIntervalMs:1500},screen:{fallbackWidth:1080,fallbackHeight:2400},visual:{overlapRatio:.2},device:{maxDeviceRetries:2,autoReconnect:!0},flow:{maxLoopIterations:50,waitUntilTimeoutMs:1e4,defaultRetryCount:3},ai:{provider:"heuristic",model:"gpt-4o-mini",selfHealing:!0,confidenceThreshold:.75,maxHealingAttempts:2,endpoint:""},memory:{staleThresholdDays:parseInt(process.env.PROMPTTEST_STALE_DAYS||"14",10),staleDecayAmount:.2,minConfidenceFloor:.2,maxHealedConfidence:.65},parser:{mode:process.env.PROMPTTEST_XML_PARSER||"regex"},safety:{mode:process.env.PROMPTTEST_SAFETY_MODE||"strict",customBlacklist:process.env.PROMPTTEST_SAFETY_BLACKLIST?process.env.PROMPTTEST_SAFETY_BLACKLIST.split(",").map(a=>a.trim()).filter(Boolean):[]},explorer:{strategy:process.env.PROMPTTEST_STRATEGY||"heuristic",maxTabs:parseInt(process.env.PROMPTTEST_MAX_TABS||"15",10),maxScreens:parseInt(process.env.PROMPTTEST_MAX_SCREENS||"30",10),maxDepth:parseInt(process.env.PROMPTTEST_MAX_DEPTH||"5",10),stepBudget:parseInt(process.env.PROMPTTEST_STEP_BUDGET||"100",10),screenshots:process.env.PROMPTTEST_SCREENSHOTS||"state-change"},locale:"en"};function ui(a=C.polling.intervalMs,e=C.polling.jitterMs){return Math.round(a+(Math.random()*2-1)*(e/2))}var cs=(r=>(r[r.SUCCESS=0]="SUCCESS",r[r.TEST_FAILED=1]="TEST_FAILED",r[r.DEVICE_UNAVAILABLE=2]="DEVICE_UNAVAILABLE",r[r.CONFIG_ERROR=3]="CONFIG_ERROR",r[r.LOCKED=4]="LOCKED",r))(cs||{}),Je=class extends Error{exitCode;phase;constructor(e,t=1,s){super(e),this.name="PromptTestError",this.exitCode=t,this.phase=s,Object.setPrototypeOf(this,new.target.prototype)}},mt=class extends Je{constructor(e,t="Device"){super(e,2,t),this.name="DeviceError"}},rs=class extends Je{target;candidateMatches;constructor(e,t,s,i="Locator"){super(e,1,i),this.name="LocatorError",this.target=t,this.candidateMatches=s}},os=class extends Je{constructor(e,t="Config"){super(e,3,t),this.name="ConfigError"}},as=class extends Je{serial;constructor(e,t,s="Lock"){super(e,4,s),this.name="LockError",this.serial=t}},_t=class extends Je{cycle;constructor(e,t,s="SpecLoader"){super(e,3,s),this.name="SpecRecursionError",this.cycle=t}};import Pe from"fs";import Ee from"path";import hi from"os";import fn from"https";import bn from"http";import yn from"readline";import{execSync as ls,spawnSync as Sn}from"child_process";var Ce=null;function Xe(){if(Ce)return Ce;if(process.env.PROMPTTEST_ADB_PATH&&Pe.existsSync(process.env.PROMPTTEST_ADB_PATH))return Ce=process.env.PROMPTTEST_ADB_PATH,Ce;let a=process.platform==="win32",e=a?"adb.exe":"adb",t=[Ee.join(Ee.dirname(process.execPath),"platform-tools",e),Ee.join(Ee.dirname(process.execPath),e),Ee.join(process.cwd(),"platform-tools",e),Ee.join(process.cwd(),"dist","bin","platform-tools",e)];for(let o of t)if(Pe.existsSync(o))return Ce=o,Ce;try{let c=ls(a?"where.exe adb":"which adb",{encoding:"utf-8",stdio:["ignore","pipe","ignore"]}).trim();if(c){let l=c.split(/\r?\n/)[0].trim();if(Pe.existsSync(l))return Ce=l,Ce}}catch{}let s=[process.env.ANDROID_HOME,process.env.ANDROID_SDK_ROOT].filter(Boolean);for(let o of s){let c=Ee.join(o,"platform-tools",e);if(Pe.existsSync(c))return Ce=c,Ce}let i=[],r=hi.homedir();if(a){let o=process.env.LOCALAPPDATA||Ee.join(r,"AppData","Local");i.push(Ee.join(o,"Android","Sdk","platform-tools","adb.exe"));let c=process.env.ProgramFiles||"C:\\Program Files";i.push(Ee.join(c,"Android","platform-tools","adb.exe"))}else process.platform==="darwin"?(i.push(Ee.join(r,"Library","Android","sdk","platform-tools","adb")),i.push("/opt/homebrew/bin/adb"),i.push("/usr/local/bin/adb")):(i.push(Ee.join(r,"Android","Sdk","platform-tools","adb")),i.push("/usr/bin/adb"),i.push("/usr/local/bin/adb"));for(let o of i)if(Pe.existsSync(o))return Ce=o,Ce;let n=Ee.join(r,".prompttest","platform-tools",e);return Pe.existsSync(n)?(Ce=n,Ce):(Ce="adb",Ce)}function wn(a){let e=a||Xe();try{let t=Sn(e,["version"],{encoding:"utf-8",timeout:3e3});return t.status===0&&(t.stdout.includes("Android Debug Bridge")||t.stdout.includes("version"))}catch{return!1}}function vn(){Ce=null}function xn(a,e){return new Promise((t,s)=>{let i=Pe.createWriteStream(e);function r(n){(n.startsWith("https")?fn:bn).get(n,c=>{if(c.statusCode&&c.statusCode>=300&&c.statusCode<400&&c.headers.location)return r(c.headers.location);if(c.statusCode!==200)return s(new Error(`Download failed with HTTP status ${c.statusCode}`));c.pipe(i),i.on("finish",()=>{i.close(()=>t())})}).on("error",c=>{Pe.unlink(e,()=>{}),s(c)})}r(a)})}async function Tn(a){let e=hi.homedir(),t=a||Ee.join(e,".prompttest","platform-tools");Pe.mkdirSync(t,{recursive:!0});let s=process.platform==="win32",i=process.platform==="darwin",r=s?"adb.exe":"adb",n=s?"https://dl.google.com/android/repository/platform-tools-latest-windows.zip":i?"https://dl.google.com/android/repository/platform-tools-latest-darwin.zip":"https://dl.google.com/android/repository/platform-tools-latest-linux.zip",o=Ee.join(t,`platform-tools-download-${Date.now()}.zip`),c=Ee.join(t,`extract-tmp-${Date.now()}`);try{await xn(n,o),Pe.mkdirSync(c,{recursive:!0}),s?ls(`powershell -NoProfile -Command "Expand-Archive -Path '${o}' -DestinationPath '${c}' -Force"`,{stdio:"ignore",timeout:6e4}):ls(`unzip -q -o "${o}" -d "${c}"`,{stdio:"ignore",timeout:6e4});let l=Ee.join(c,"platform-tools"),p=Pe.existsSync(l)?l:c,d=Pe.readdirSync(p);for(let u of d){let m=Ee.join(p,u),y=Ee.join(t,u);Pe.copyFileSync(m,y)}let h=Ee.join(t,r);if(!s&&Pe.existsSync(h))try{Pe.chmodSync(h,493)}catch{}return vn(),Ce=h,h}finally{try{Pe.existsSync(o)&&Pe.unlinkSync(o),Pe.existsSync(c)&&Pe.rmSync(c,{recursive:!0,force:!0})}catch{}}}async function En(){let a=process.argv;return a.includes("--download-adb")||a.includes("-y")||a.includes("--yes")?!0:!process.stdin.isTTY||process.env.CI==="true"?!1:new Promise(e=>{let t=yn.createInterface({input:process.stdin,output:process.stdout});console.log(`
7
+ var Oi=Object.defineProperty;var xt=(a,e,t)=>()=>{if(t)throw t[0];try{return a&&(e=a(a=0)),e}catch(s){throw t=[s],s}};var Dr=(a,e)=>{for(var t in e)Oi(a,t,{get:e[t],enumerable:!0})};var Te,Ar=xt(()=>{"use strict";Te={DEVICES:["devices","-l"],SCREENCAP:["exec-out","screencap","-p"],FOREGROUND_APP:["shell","dumpsys","window"],CURRENT_FOCUS:["shell","dumpsys","activity","activities"],FORCE_STOP:a=>["shell","am","force-stop",a],CLEAR_APP:a=>["shell","pm","clear",a],LAUNCH_APP:a=>["shell","am","start","-a","android.intent.action.MAIN","-c","android.intent.category.LAUNCHER",a],UIAUTOMATOR_DUMP:["exec-out","uiautomator","dump","--compressed","/dev/tty"],INPUT_TAP:(a,e)=>["shell","input","tap",a.toString(),e.toString()],INPUT_TEXT:a=>["shell","input","text",a],INPUT_KEY:a=>["shell","input","keyevent",a.toString()],INPUT_SWIPE:(a,e,t,s,r=300)=>["shell","input","swipe",a.toString(),e.toString(),t.toString(),s.toString(),r.toString()],LOGCAT_CRASH:a=>["shell","logcat","-d","-b","crash","-s","AndroidRuntime:E"],CLEAR_CRASH_LOGS:()=>["shell","logcat","-b","crash","-c"],GRANT_PERMISSION:(a,e)=>["shell","pm","grant",a,e.startsWith("android.permission.")?e:`android.permission.${e}`],INSTALL_APK:a=>["install","-r","-t","-d",a],UNINSTALL_APP:a=>["uninstall",a],DEEP_LINK:a=>["shell","am","start","-a","android.intent.action.VIEW","-d",a],TCPIP:(a=5555)=>["tcpip",a.toString()],CONNECT:a=>["connect",a],DISCONNECT:a=>a?["disconnect",a]:["disconnect"],GET_WLAN_IP:["shell","ip","-f","inet","addr","show","wlan0"]}});function Fi(){let a=process.env.PROMPTTEST_SETTLE_FACTOR;if(!a)return 1;let e=parseFloat(a);return isNaN(e)||e<=0?1:e}function Oe(a,e=50){return Math.max(e,Math.round(a*Fi()))}function $r(a=I.polling.intervalMs,e=I.polling.jitterMs){return Math.round(a+(Math.random()*2-1)*(e/2))}var I,Fe=xt(()=>{"use strict";I={adb:{commandTimeoutMs:2e4,binaryTimeoutMs:45e3,retryCount:2,retryBaseDelayMs:200},polling:{intervalMs:200,jitterMs:80,defaultTimeoutMs:3e3,extendedTimeoutMs:3500,hierarchyCacheTtlMs:3500,verifyCacheTtlMs:2e3},settle:{get tapMs(){return Oe(500)},get backMs(){return Oe(700)},get launchMs(){return Oe(2500)},get stopMs(){return Oe(600)},get keyboardDismissMs(){return Oe(500)},get scrollMs(){return Oe(350)},get toggleMs(){return Oe(350)},get selectMs(){return Oe(600)},get inputFocusMs(){return Oe(250)},get inputClearMs(){return Oe(150)},get inputDoneMs(){return Oe(350)},get submitWaitMs(){return Oe(1800)}},gestures:{swipeDurationMs:300,longPressDurationMs:1e3,bottomNavThresholdRatio:.88,tapTargetYRatio:.96},recorder:{staleTapTimeoutMs:3e3,asyncRefreshDelayMs:80,heartbeatIntervalMs:1500},screen:{fallbackWidth:1080,fallbackHeight:2400},visual:{overlapRatio:.2},device:{maxDeviceRetries:2,autoReconnect:!0},flow:{maxLoopIterations:50,waitUntilTimeoutMs:1e4,defaultRetryCount:3},ai:{provider:"heuristic",model:"gpt-4o-mini",selfHealing:!0,confidenceThreshold:.75,maxHealingAttempts:2,endpoint:""},memory:{staleThresholdDays:parseInt(process.env.PROMPTTEST_STALE_DAYS||"14",10),staleDecayAmount:.2,minConfidenceFloor:.2,maxHealedConfidence:.65},parser:{mode:process.env.PROMPTTEST_XML_PARSER||"regex"},safety:{mode:process.env.PROMPTTEST_SAFETY_MODE||"strict",customBlacklist:process.env.PROMPTTEST_SAFETY_BLACKLIST?process.env.PROMPTTEST_SAFETY_BLACKLIST.split(",").map(a=>a.trim()).filter(Boolean):[]},explorer:{strategy:process.env.PROMPTTEST_STRATEGY||"heuristic",maxTabs:parseInt(process.env.PROMPTTEST_MAX_TABS||"15",10),maxScreens:parseInt(process.env.PROMPTTEST_MAX_SCREENS||"30",10),maxDepth:parseInt(process.env.PROMPTTEST_MAX_DEPTH||"5",10),stepBudget:parseInt(process.env.PROMPTTEST_STEP_BUDGET||"100",10),screenshots:process.env.PROMPTTEST_SCREENSHOTS||"state-change"},locale:"en"}});var bs,Qe,Tt,gs,ms,fs,Jt,It=xt(()=>{"use strict";bs=(n=>(n[n.SUCCESS=0]="SUCCESS",n[n.TEST_FAILED=1]="TEST_FAILED",n[n.DEVICE_UNAVAILABLE=2]="DEVICE_UNAVAILABLE",n[n.CONFIG_ERROR=3]="CONFIG_ERROR",n[n.LOCKED=4]="LOCKED",n))(bs||{}),Qe=class extends Error{exitCode;phase;constructor(e,t=1,s){super(e),this.name="PromptTestError",this.exitCode=t,this.phase=s,Object.setPrototypeOf(this,new.target.prototype)}},Tt=class extends Qe{constructor(e,t="Device"){super(e,2,t),this.name="DeviceError"}},gs=class extends Qe{target;candidateMatches;constructor(e,t,s,r="Locator"){super(e,1,r),this.name="LocatorError",this.target=t,this.candidateMatches=s}},ms=class extends Qe{constructor(e,t="Config"){super(e,3,t),this.name="ConfigError"}},fs=class extends Qe{serial;constructor(e,t,s="Lock"){super(e,4,s),this.name="LockError",this.serial=t}},Jt=class extends Qe{cycle;constructor(e,t,s="SpecLoader"){super(e,3,s),this.name="SpecRecursionError",this.cycle=t}}});import Ae from"fs";import De from"path";import Cr from"os";import _i from"https";import Hi from"http";import Bi from"readline";import{execSync as ys,spawnSync as Ui}from"child_process";function Ze(){if(Ce)return Ce;if(process.env.PROMPTTEST_ADB_PATH&&Ae.existsSync(process.env.PROMPTTEST_ADB_PATH))return Ce=process.env.PROMPTTEST_ADB_PATH,Ce;let a=process.platform==="win32",e=a?"adb.exe":"adb",t=[De.join(De.dirname(process.execPath),"platform-tools",e),De.join(De.dirname(process.execPath),e),De.join(process.cwd(),"platform-tools",e),De.join(process.cwd(),"dist","bin","platform-tools",e)];for(let o of t)if(Ae.existsSync(o))return Ce=o,Ce;try{let l=ys(a?"where.exe adb":"which adb",{encoding:"utf-8",stdio:["ignore","pipe","ignore"]}).trim();if(l){let c=l.split(/\r?\n/)[0].trim();if(Ae.existsSync(c))return Ce=c,Ce}}catch{}let s=[process.env.ANDROID_HOME,process.env.ANDROID_SDK_ROOT].filter(Boolean);for(let o of s){let l=De.join(o,"platform-tools",e);if(Ae.existsSync(l))return Ce=l,Ce}let r=[],n=Cr.homedir();if(a){let o=process.env.LOCALAPPDATA||De.join(n,"AppData","Local");r.push(De.join(o,"Android","Sdk","platform-tools","adb.exe"));let l=process.env.ProgramFiles||"C:\\Program Files";r.push(De.join(l,"Android","platform-tools","adb.exe"))}else process.platform==="darwin"?(r.push(De.join(n,"Library","Android","sdk","platform-tools","adb")),r.push("/opt/homebrew/bin/adb"),r.push("/usr/local/bin/adb")):(r.push(De.join(n,"Android","Sdk","platform-tools","adb")),r.push("/usr/bin/adb"),r.push("/usr/local/bin/adb"));for(let o of r)if(Ae.existsSync(o))return Ce=o,Ce;let i=De.join(n,".prompttest","platform-tools",e);return Ae.existsSync(i)?(Ce=i,Ce):(Ce="adb",Ce)}function ji(a){let e=a||Ze();try{let t=Ui(e,["version"],{encoding:"utf-8",timeout:3e3});return t.status===0&&(t.stdout.includes("Android Debug Bridge")||t.stdout.includes("version"))}catch{return!1}}function Yi(){Ce=null}function Wi(a,e){return new Promise((t,s)=>{let r=Ae.createWriteStream(e);function n(i){(i.startsWith("https")?_i:Hi).get(i,l=>{if(l.statusCode&&l.statusCode>=300&&l.statusCode<400&&l.headers.location)return n(l.headers.location);if(l.statusCode!==200)return s(new Error(`Download failed with HTTP status ${l.statusCode}`));l.pipe(r),r.on("finish",()=>{r.close(()=>t())})}).on("error",l=>{Ae.unlink(e,()=>{}),s(l)})}n(a)})}async function Ji(a){let e=Cr.homedir(),t=a||De.join(e,".prompttest","platform-tools");Ae.mkdirSync(t,{recursive:!0});let s=process.platform==="win32",r=process.platform==="darwin",n=s?"adb.exe":"adb",i=s?"https://dl.google.com/android/repository/platform-tools-latest-windows.zip":r?"https://dl.google.com/android/repository/platform-tools-latest-darwin.zip":"https://dl.google.com/android/repository/platform-tools-latest-linux.zip",o=De.join(t,`platform-tools-download-${Date.now()}.zip`),l=De.join(t,`extract-tmp-${Date.now()}`);try{await Wi(i,o),Ae.mkdirSync(l,{recursive:!0}),s?ys(`powershell -NoProfile -Command "Expand-Archive -Path '${o}' -DestinationPath '${l}' -Force"`,{stdio:"ignore",timeout:6e4}):ys(`unzip -q -o "${o}" -d "${l}"`,{stdio:"ignore",timeout:6e4});let c=De.join(l,"platform-tools"),p=Ae.existsSync(c)?c:l,d=Ae.readdirSync(p);for(let h of d){let g=De.join(p,h),b=De.join(t,h);Ae.copyFileSync(g,b)}let u=De.join(t,n);if(!s&&Ae.existsSync(u))try{Ae.chmodSync(u,493)}catch{}return Yi(),Ce=u,u}finally{try{Ae.existsSync(o)&&Ae.unlinkSync(o),Ae.existsSync(l)&&Ae.rmSync(l,{recursive:!0,force:!0})}catch{}}}async function Vi(){let a=process.argv;return a.includes("--download-adb")||a.includes("-y")||a.includes("--yes")?!0:!process.stdin.isTTY||process.env.CI==="true"?!1:new Promise(e=>{let t=Bi.createInterface({input:process.stdin,output:process.stdout});console.log(`
21
8
  \u26A0\uFE0F Android Debug Bridge (ADB) was not found on your system.
22
9
  PromptTest requires ADB to communicate with physical or emulated Android devices.
23
10
 
24
11
  Would you like PromptTest to download Google's official
25
- platform-tools archive (~3.5 MB) to ~/.prompttest/platform-tools/? [Y/n]: `),t.question("",s=>{t.close();let i=s.trim().toLowerCase();e(i===""||i==="y"||i==="yes")})})}async function gi(a={}){if(wn())return Xe();if(a.skipPrompt?!0:await En()){console.log(`
26
- \u2B07\uFE0F Downloading official Google platform-tools (ADB)...`);let t=await Tn();return console.log(`\u2714 ADB successfully provisioned and ready: ${t}
27
- `),t}throw new mt(`Android Debug Bridge (ADB) not found on this system.
12
+ platform-tools archive (~3.5 MB) to ~/.prompttest/platform-tools/? [Y/n]: `),t.question("",s=>{t.close();let r=s.trim().toLowerCase();e(r===""||r==="y"||r==="yes")})})}async function Nr(a={}){if(ji())return Ze();if(a.skipPrompt?!0:await Vi()){console.log(`
13
+ \u2B07\uFE0F Downloading official Google platform-tools (ADB)...`);let t=await Ji();return console.log(`\u2714 ADB successfully provisioned and ready: ${t}
14
+ `),t}throw new Tt(`Android Debug Bridge (ADB) not found on this system.
28
15
  To resolve:
29
16
  1. Re-run with --download-adb to automatically download Google's official platform-tools.
30
17
  2. Install Android Studio: https://developer.android.com/studio
31
18
  3. Set custom ADB path via: export PROMPTTEST_ADB_PATH="C:\\path\\to\\adb.exe"
32
- `)}function Pn(a,e,t,s){let i={},r=a.match(/level:\s*(\d+)/i),n=a.match(/temperature:\s*(\d+)/i),o=a.match(/status:\s*(\d+)/i);(r||n||o)&&(i.battery={...r?{level:Number(r[1])}:{},...n?{temperatureC:Number(n[1])/10}:{},...o?{status:o[1]}:{}});let c=e.match(/(?:Physical|Override)\s+size:\s*(\d+)x(\d+)/i),l=e.match(/(?:Physical|Override)\s+density:\s*(\d+)/i);(c||l)&&(i.display={...c?{width:Number(c[1]),height:Number(c[2])}:{},...l?{density:Number(l[1])}:{}});let p=t.match(/TOTAL\s+([\d,]+)/i),d=t.match(/Private Dirty:\s*(\d+)/i),h=t.match(/Native Heap:\s*(\d+)/i),m=[...(t.match(/Total PSS by category:\s*([\s\S]*?)(?:\n\s*\n|Total RAM:|$)/i)?.[1]||"").matchAll(/^\s*([\d,]+)K:\s*(.+)$/gim)],y=m.reduce((E,D)=>E+Number(D[1].replace(/,/g,"")),0),v=m.find(E=>/native/i.test(E[2]));(p||d||h||y)&&(i.memory={...p?{pssKb:Number(p[1].replace(/,/g,""))}:y?{pssKb:y}:{},...d?{privateDirtyKb:Number(d[1])}:{},...h?{nativeHeapKb:Number(h[1])}:v?{nativeHeapKb:Number(v[1].replace(/,/g,""))}:{}});let T=s.match(/Total frames rendered:\s*(\d+)/i),S=s.match(/Janky frames:\s*(\d+)/i),b=s.match(/90th percentile:\s*([\d.]+)\s*ms/i);return(T||S||b)&&(i.graphics={...T?{framesRendered:Number(T[1])}:{},...S?{jankyFrames:Number(S[1])}:{},...b?{percentile90Ms:Number(b[1])}:{}}),i}var tt=class{defaultSerial=null;cachedDimensions=new Map;inFlightHierarchies=new Map;constructor(e){e&&(this.defaultSerial=e)}setDefaultDevice(e){this.defaultSerial=e}getDefaultDevice(){return this.defaultSerial}async getScreenDimensions(e){let t=e||this.defaultSerial||"default";if(this.cachedDimensions.has(t))return this.cachedDimensions.get(t);try{let{stdout:i}=await this.execAdb(["shell","wm","size"],e),r=i.match(/(?:Physical|Override)\s*size:\s*(\d+)x(\d+)/i);if(r){let n={width:parseInt(r[1],10),height:parseInt(r[2],10)};return this.cachedDimensions.set(t,n),n}}catch{}let s={width:C.screen.fallbackWidth,height:C.screen.fallbackHeight};return this.cachedDimensions.set(t,s),s}clearScreenDimensionsCache(e){e?this.cachedDimensions.delete(e):this.cachedDimensions.clear()}async getNavigationBarHeight(e){try{let{stdout:t}=await this.execAdb(["shell","dumpsys","window","displays"],e).catch(()=>({stdout:""})),s=t.match(/(?:navigationBarHeight|navBarHeight|navigationBarFrame)[=:\s]+(\d+)/i);if(s&&parseInt(s[1],10)>0)return parseInt(s[1],10)}catch{}return 0}isGlobalAdbCommand(e){return e?["devices","connect","disconnect","version","kill-server","start-server","help"].includes(e.toLowerCase()):!0}async execAdbInternal(e,t,s=2e4){let i=t||this.defaultSerial;!i&&!this.isGlobalAdbCommand(e[0])&&(await this.ensureDefaultDevice(),i=this.defaultSerial);let r=i&&!this.isGlobalAdbCommand(e[0])?["-s",i,...e]:[...e],n=await gi();return new Promise((o,c)=>{let l=Pt(n,r),p="",d="",h=!1,u=setTimeout(()=>{h=!0;try{l.kill("SIGKILL")}catch{}c(new Error(`ADB command timed out after ${s}ms: adb ${r.join(" ")}`))},s);l.stdout.on("data",m=>{p+=m.toString("utf-8")}),l.stderr.on("data",m=>{d+=m.toString("utf-8")}),l.on("close",m=>{clearTimeout(u),!h&&(m===0?o({stdout:p,stderr:d}):c(new Error(`ADB command failed (code ${m}): adb ${r.join(" ")}
33
- Error: ${d}`)))}),l.on("error",m=>{clearTimeout(u),!h&&c(m)})})}async execAdb(e,t,s=C.adb.commandTimeoutMs,i=C.device.maxDeviceRetries){let r;for(let n=0;n<=i;n++)try{return await this.execAdbInternal(e,t,s)}catch(o){r=o;let c=o instanceof Error?o.message:String(o),l=c.toLowerCase(),p=l.includes("device offline")||l.includes("device not found"),d=p||l.includes("error: closed")||l.includes("timed out");if(n>=i||!d||this.isGlobalAdbCommand(e[0]))throw p?new mt(`Target Android device ${t||this.defaultSerial||""} is offline or disconnected: ${c}`):o;if(C.device.autoReconnect&&p)try{await this.execAdbInternal(["reconnect"],t,5e3).catch(()=>{})}catch{}await new Promise(h=>setTimeout(h,C.adb.retryBaseDelayMs*Math.pow(2,n+1)))}throw r||new Error(`ADB command failed after ${i} retries: ${e.join(" ")}`)}async execAdbBinary(e,t,s=45e3){let i=t||this.defaultSerial;!i&&!this.isGlobalAdbCommand(e[0])&&(await this.ensureDefaultDevice(),i=this.defaultSerial);let r=i&&!this.isGlobalAdbCommand(e[0])?["-s",i,...e]:[...e];return new Promise((n,o)=>{let c=Pt(Xe(),r),l=[],p="",d=!1,h=setTimeout(()=>{d=!0;try{c.kill("SIGKILL")}catch{}o(new Error(`ADB binary command timed out after ${s}ms: adb ${r.join(" ")}`))},s);c.stdout.on("data",u=>l.push(Buffer.from(u))),c.stderr.on("data",u=>{p+=u.toString("utf-8")}),c.on("close",u=>{clearTimeout(h),!d&&(u===0?n(Buffer.concat(l)):o(new Error(`ADB binary command failed (code ${u}): adb ${r.join(" ")}
34
- ${p}`)))}),c.on("error",u=>{clearTimeout(h),!d&&o(u)})})}async getConnectedDevices(){let{stdout:e}=await this.execAdb(xe.DEVICES),t=e.split(`
35
- `).map(o=>o.trim()).filter(Boolean),s=t.findIndex(o=>o.toLowerCase().includes("list of devices attached")),i=s!==-1?t.slice(s+1):t,r=[];return r=(o=>{let c=[];for(let l of o){if(!l||l.startsWith("*")||l.toLowerCase().includes("list of devices"))continue;let p=l.split(/\s+/);if(p.length>=2){let d=p[0],h=p[1],u,m;for(let y of p.slice(2))y.startsWith("model:")&&(u=y.replace("model:","")),y.startsWith("product:")&&(m=y.replace("product:",""));c.push({serial:d,state:h,model:u,product:m})}}return c})(i),r}async wakeDevice(e){try{await this.execAdb(["shell","input","keyevent","224"],e),await this.execAdb(["shell","wm","dismiss-keyguard"],e)}catch{}}async ensureDefaultDevice(e){if(e)return await this.wakeDevice(e),e;if(this.defaultSerial)return await this.wakeDevice(this.defaultSerial),this.defaultSerial;let s=(await this.getConnectedDevices()).find(i=>i.state==="device");return s&&(this.defaultSerial=s.serial,await this.wakeDevice(this.defaultSerial)),this.defaultSerial||void 0}async getDeviceModel(e){try{let{stdout:r}=await this.execAdb(["shell","getprop","ro.product.model"],e),n=r.trim();if(n)return n}catch{}let t=await this.getConnectedDevices(),s=e||this.defaultSerial;return t.find(r=>r.serial===s)?.model||s||"Android Device"}async captureScreenshot(e){return this.execAdbBinary(xe.SCREENCAP,e)}async getUiHierarchy(e){let t=e||this.defaultSerial||"default",s=this.inFlightHierarchies.get(t);if(s)return s;let i=(async()=>{try{let{stdout:r}=await this.execAdb(xe.UIAUTOMATOR_DUMP,e,1e4,0),n=r.indexOf("<hierarchy");return n>=0?r.slice(n):r}finally{this.inFlightHierarchies.delete(t)}})();return this.inFlightHierarchies.set(t,i),i}clearInFlightHierarchies(e){e?this.inFlightHierarchies.delete(e):this.inFlightHierarchies.clear()}async getForegroundPackage(e){let t=s=>{let i=s.toLowerCase();return i==="android"||i.includes("systemui")||i.includes("inputmethod")||i.includes("latin")||i.includes("gboard")||i.includes("swiftkey")||i.includes("ime")};try{let{stdout:s}=await this.execAdb(["shell","dumpsys","activity","activities"],e).catch(()=>({stdout:""})),i=s.match(/(?:topResumedActivity|mResumedActivity|ResumedActivity)[=:\s]+(?:ActivityRecord\{[^}]*?\s+)?([a-zA-Z0-9._]+)\//i);if(i&&i[1]&&!t(i[1]))return i[1];let{stdout:r}=await this.execAdb(xe.FOREGROUND_APP,e).catch(()=>({stdout:""})),n=r.matchAll(/(?:mCurrentFocus|mFocusedWindow|mFocusedApp|topResumedActivity|mResumedActivity|mFocusedActivity)[=:\s]+(?:Window\{[^}]*?\s+|ActivityRecord\{[^}]*?\s+|AppWindowToken\{[^}]*?\s+)?([a-zA-Z0-9._]+)\//gi);for(let o of n)if(o&&o[1]&&!t(o[1]))return o[1]}catch{}return null}async tap(e,t,s){await this.execAdb(xe.INPUT_TAP(e,t),s)}async inputText(e,t){let s=e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/'/g,"\\'").replace(/%/g,"\\%").replace(/ /g,"%s").replace(/&/g,"\\&").replace(/</g,"\\<").replace(/>/g,"\\>").replace(/\(/g,"\\(").replace(/\)/g,"\\)").replace(/;/g,"\\;").replace(/\|/g,"\\|").replace(/\$/g,"\\$").replace(/#/g,"\\#").replace(/\*/g,"\\*").replace(/\?/g,"\\?").replace(/~/g,"\\~").replace(/`/g,"\\`");try{await this.execAdb(xe.INPUT_TEXT(s),t)}catch{for(let i of e)if(i===" ")await this.pressKey(62,t);else if(i==="@")await this.pressKey(77,t);else{let r=i.replace(/([\\"'&<>()|$;#*?~`%])/g,"\\$1");await this.execAdb(["shell","input","text",r],t).catch(()=>{})}}}async pressKey(e,t){await this.execAdb(xe.INPUT_KEY(e),t)}async pressEnter(e){await this.pressKey(66,e)}async pressTab(e){await this.pressKey(61,e)}async pressHome(e){await this.pressKey(3,e)}async pressBack(e){await this.pressKey(4,e)}async setWifiEnabled(e,t){await this.execAdb(["shell","svc","wifi",e?"enable":"disable"],t)}async setAirplaneMode(e,t){await this.execAdb(["shell","cmd","connectivity","airplane-mode",e?"enable":"disable"],t)}async setOrientation(e,t){await this.execAdb(["shell","content","insert","--uri","content://settings/system","--bind","name:s:accelerometer_rotation","--bind","value:i:0"],t).catch(()=>{});let s=e==="landscape"?"1":"0";await this.execAdb(["shell","settings","put","system","user_rotation",s],t)}async openNotificationShade(e){await this.execAdb(["shell","cmd","statusbar","expand-notifications"],e)}async collapseNotificationShade(e){await this.execAdb(["shell","cmd","statusbar","collapse"],e)}async clearInput(e=45,t){let s=Array(e).fill("67");await this.execAdb(["shell","input","keyevent",...s],t)}async clearField(e,t=80){await this.pressKey(123,e);let s=Math.max(50,t+10),i=Array(s).fill("67");await this.execAdb(["shell","input","keyevent",...i],e).catch(()=>{}),await this.pressKey(122,e);let r=Array(Math.max(30,Math.floor(t/2))).fill("112");await this.execAdb(["shell","input","keyevent",...r],e).catch(()=>{})}async swipe(e,t,s,i,r=300,n){await this.execAdb(xe.INPUT_SWIPE(e,t,s,i,r),n)}async launchApp(e,t){try{let{stdout:s}=await this.execAdb(["shell","cmd","package","resolve-activity","--brief",e],t),r=(s||"").split(`
36
- `).map(n=>n.trim()).find(n=>n.includes("/")&&!n.includes(" "));if(r){await this.execAdb(["shell","am","start","-n",r],t);return}}catch{}await this.execAdb(xe.LAUNCH_APP(e),t)}async forceStop(e,t){await this.execAdb(xe.FORCE_STOP(e),t)}async clearAppData(e,t){await this.execAdb(xe.CLEAR_APP(e),t)}async restartApp(e,t){await this.forceStop(e,t),await new Promise(s=>setTimeout(s,600)),await this.launchApp(e,t)}async grantPermission(e,t,s){await this.execAdb(xe.GRANT_PERMISSION(e,t),s)}async isAppInstalled(e,t){try{let{stdout:s}=await this.execAdb(["shell","pm","list","packages",e],t);return s.toLowerCase().includes(`package:${e.toLowerCase()}`)||s.includes(e)}catch{return!1}}async installApk(e,t){await this.execAdb(xe.INSTALL_APK(e),t)}async uninstallApp(e,t){await this.execAdb(xe.UNINSTALL_APP(e),t)}async openDeepLink(e,t){await this.execAdb(xe.DEEP_LINK(e),t)}async checkCrashes(e,t){try{let{stdout:s}=await this.execAdb(xe.LOGCAT_CRASH(e),t);if(s&&s.includes("FATAL EXCEPTION")&&s.includes(e))return s}catch{}return null}async clearCrashLogs(e){try{await this.execAdb(xe.CLEAR_CRASH_LOGS(),e)}catch{}}async getDeviceIp(e){try{let{stdout:t}=await this.execAdb(xe.GET_WLAN_IP,e),s=t.match(/inet\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}try{let{stdout:t}=await this.execAdb(["shell","ip","route"],e),s=t.match(/src\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}return null}async connectWifi(e,t=5555,s){let i=e,r=t;if(e&&e.includes(":")){let d=e.split(":");i=d[0],r=parseInt(d[1],10)||t}if(!i){let d=await this.getDeviceIp(s);if(!d)throw new Error("Could not auto-detect phone Wi-Fi IP. Make sure the phone is on Wi-Fi, or pass IP manually: npx prompttest wifi <ip>");i=d}let n=`${i}:${r}`;if(e)try{let{stdout:d,stderr:h}=await this.execAdb(xe.CONNECT(n)),u=`${d} ${h}`.trim();if(u.toLowerCase().includes("connected")||u.toLowerCase().includes("already"))return this.defaultSerial=n,{success:!0,target:n,message:u}}catch{}try{await this.execAdb(xe.TCPIP(r),s),await new Promise(d=>setTimeout(d,1e3))}catch{}let{stdout:o,stderr:c}=await this.execAdb(xe.CONNECT(n)),l=`${o} ${c}`.trim(),p=l.toLowerCase().includes("connected")||l.toLowerCase().includes("already");return p&&(this.defaultSerial=n),{success:p,target:n,message:l}}async disconnectWifi(e){let{stdout:t,stderr:s}=await this.execAdb(xe.DISCONNECT(e));return`${t} ${s}`.trim()}async pairWifi(e,t){if(!/^[^:\s]+:\d+$/.test(e))throw new Error("Pairing target must be an IP:port value.");if(!/^\d{4,8}$/.test(t))throw new Error("Pairing code must contain 4 to 8 digits.");let{stdout:s,stderr:i}=await this.execAdb(["pair",e,t]),r=`${s} ${i}`.trim();return{success:/successfully paired|already paired/i.test(r),message:r}}async getDeviceMetrics(e){let[s,i,r,n]=await Promise.all([this.execAdb(["shell","dumpsys","battery"],e,8e3,0).then(o=>o.stdout).catch(()=>""),this.execAdb(["shell","wm","size"],e,8e3,0).then(async o=>`${o.stdout}
37
- ${(await this.execAdb(["shell","wm","density"],e,8e3,0).catch(()=>({stdout:""}))).stdout}`).catch(()=>""),this.execAdb(["shell","dumpsys","meminfo"],e,8e3,0).then(o=>o.stdout).catch(()=>""),this.execAdb(["shell","dumpsys","gfxinfo"],e,8e3,0).then(o=>o.stdout).catch(()=>"")]);return Pn(s,i,r,n)}streamLogcatEvents(e,t){let s=["-s",e,"shell","logcat","-b","events","-v","time"],i=Pt(Xe(),s),r=ds.createInterface({input:i.stdout});return r.on("line",t),i.on("close",()=>{try{r.close()}catch{}}),i.on("error",()=>{}),i}streamLogcatCrashes(e,t){let s=["-s",e,"shell","logcat","-T","1","-s","AndroidRuntime:E","ANRManager:E","-v","time"],i=Pt(Xe(),s),r=ds.createInterface({input:i.stdout});return r.on("line",t),i.on("close",()=>{try{r.close()}catch{}}),i.on("error",()=>{}),i}streamLogcatToasts(e,t){let s=["-s",e,"shell","logcat","-s","Toast:D","Snackbar:D","-v","time"],i=Pt(Xe(),s),r=ds.createInterface({input:i.stdout});return r.on("line",t),i.on("close",()=>{try{r.close()}catch{}}),i.on("error",()=>{}),i}async getLogcatTail(e=150,t){try{let{stdout:s}=await this.execAdb(["logcat","-d","-t",String(e),"-v","time"],t);return s}catch(s){return`Failed to capture logcat: ${s instanceof Error?s.message:String(s)}`}}async startScreenRecording(e="/sdcard/prompttest_temp.mp4",t){let s=t||this.defaultSerial,i=s?["-s",s,"shell","screenrecord",e]:["shell","screenrecord",e],r=Pt(Xe(),i);return r.on("error",()=>{}),{remotePath:e,process:r}}async stopScreenRecording(e,t,s){try{try{e.process.kill("SIGINT")}catch{}return await this.execAdb(["shell","pkill","-2","screenrecord"],s).catch(()=>{}),await new Promise(i=>setTimeout(i,1500)),await this.execAdb(["pull",e.remotePath,t],s),await this.execAdb(["shell","rm","-f",e.remotePath],s).catch(()=>{}),t}catch{return""}}async isKeyboardVisible(e){try{let{stdout:t}=await this.execAdb(["shell","sh","-c",'dumpsys input_method | grep -E "mInputShown=true|mShowRequested=true"'],e,4e3,0),s=t.toLowerCase();if(s.includes("minputshown=true")||s.includes("mshowrequested=true"))return!0}catch{}try{let{stdout:t}=await this.execAdb(["shell","sh","-c",'dumpsys window | grep -E "mInputMethodWindowVisibleHeight"'],e,4e3,0),s=t.match(/mInputMethodWindowVisibleHeight=(\d+)/i);if(s&&parseInt(s[1],10)>0)return!0}catch{}try{let{stdout:t}=await this.execAdb(["shell","dumpsys","input_method"],e,6e3,0),s=t.toLowerCase();return s.includes("minputshown=true")||s.includes("mshowrequested=true")}catch{return!1}}async ensureAccessibilityServices(e){try{await this.execAdb(["shell","settings","put","secure","accessibility_enabled","1"],e,4e3,0)}catch{}}},ke=new tt;var fi=/^(\d+(?:\.\d+)*\.?|\-|\*)\s*/;function bi(a){let e=a.trim(),t=e.match(/^"([^"]+)"$/);if(t)return t[1].trim();let s=e.match(/^'([^']+)'$/);return s?s[1].trim():e.replace(/^['"]+|['"]+$/g,"").trim()}var re=`(?:"([^"]+)"|'([^']+)'|([^'"\\s][^'"]*?[^'"\\s]|\\S))`;function ae(a){for(let e of a)if(e!=null&&e.trim().length>0)return e.trim();return""}function ft(a,e){let t=a;if(e)for(let[s,i]of Object.entries(e))t=t.replace(new RegExp(`\\$${s}\\b`,"g"),()=>i);return t=t.replace(/\$ENV\{([A-Z0-9_]+)\}/gi,(s,i)=>process.env[i]||""),t=t.replace(/\$RANDOM_EMAIL\b/g,()=>`test_${Date.now().toString().slice(-6)}@prompttest.io`),t=t.replace(/\$RANDOM_NAME\b/g,()=>`User_${Math.floor(1e3+Math.random()*9e3)}`),t=t.replace(/\$RANDOM_PHONE\b/g,()=>`98${Math.floor(1e7+Math.random()*9e7)}`),t=t.replace(/\$TIMESTAMP\b/g,()=>Date.now().toString()),t=t.replace(/\$UUID\b/g,()=>`${Date.now().toString(16)}-${Math.random().toString(16).substring(2,8)}`),t=t.replace(/\$RANDOM_UUID\b/g,()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,s=>{let i=Math.random()*16|0;return(s==="x"?i:i&3|8).toString(16)})),t=t.replace(/\$RANDOM_CARD\b/g,()=>{let s="4532";for(let i=0;i<12;i++)s+=Math.floor(Math.random()*10);return s}),t=t.replace(/\$DATE_OFFSET\((-?\d+)\)/g,(s,i)=>{let r=parseInt(i,10)||0;return new Date(Date.now()+r*864e5).toISOString().slice(0,10)}),t=t.replace(/\$RANDOM_INT\((\d+),\s*(\d+)\)/g,(s,i,r)=>{let n=parseInt(i,10),o=parseInt(r,10);return String(Math.floor(n+Math.random()*(o-n+1)))}),t}var G={TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:button\\s+|item\\s+)?${re}$`,"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+)?${re}$`,"i"),PROXIMITY_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+${re}\\s+(?:next\\s+to|near|beside|inside|for)\\s+${re}$`,"i"),CLEAR:new RegExp(`^(?:clear|empty)(?:\\s+(?:text\\s+(?:in|of)|field|input))?\\s+${re}$`,"i"),LONG_PRESS:new RegExp(`^(?:long\\s+press|press\\s+and\\s+hold)\\s+(?:button\\s+|item\\s+)?${re}$`,"i"),TYPE:new RegExp(`^(?:type|enter|input)\\s+${re}\\s+(?:in|into)\\s+${re}$`,"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+)?${re}\\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+${re}$`,"i"),SCROLL:/^(?:scroll|swipe)\s+(down|up)$/i,DIRECTIONAL_SWIPE:new RegExp(`^(?:swipe|scroll)\\s+(left|right|up|down)(?:\\s+on\\s+${re})?$`,"i"),TOGGLE:new RegExp(`^(?:toggle|turn\\s+(on|off)|check|uncheck)\\s+(?:checkbox\\s+|switch\\s+)?${re}$`,"i"),SELECT_DROPDOWN:new RegExp(`^(?:select|choose)\\s+${re}\\s+from\\s+${re}$`,"i"),LAUNCH_APP:new RegExp(`^(?:launch|open)\\s+(?:app\\s+)?${re}$`,"i"),RESTART_APP:new RegExp(`^(?:restart|relaunch)\\s+(?:app\\s+)?${re}$`,"i"),TERMINATE_APP:new RegExp(`^(?:terminate|close|force\\s*stop)\\s+(?:app\\s+)?(?:${re})?$`,"i"),CLEAR_DATA:new RegExp(`^(?:clear\\s+(?:app\\s+)?data\\s+(?:for\\s+)?|reset\\s+app\\s+)${re}$`,"i"),GRANT_PERMISSION:new RegExp(`^grant\\s+permissions?\\s+${re}(?:\\s+(?:to|for)\\s+${re})?$`,"i"),DEEP_LINK:new RegExp(`^(?:open\\s+deep\\s*link|navigate\\s+to\\s+url)\\s+${re}$`,"i"),WAIT:/^wait\s+(\d+)\s*(?:s|sec|seconds|ms)?$/i,WAIT_UNTIL:new RegExp(`^(?:wait\\s+until|wait\\s+for)\\s+${re}\\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+)?${re}\\s+(?:is\\s+)?(visible|present|exists|appears?)|element\\s+${re}\\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+)?${re}$`,"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+${re}$`,"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+${re})?$`,"i"),API_REQUEST:new RegExp(`^(?:api|call\\s+api)\\s+(GET|POST|PUT|DELETE|PATCH)\\s+${re}(?:\\s+with\\s+body\\s+${re})?(?:\\s+store\\s+${re}\\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+${re}\\s+with\\s+subject\\s+${re}(?:\\s+contains\\s+${re})?$`,"i"),OPTIONAL_SUFFIX:/\s*\((?:optional|if\s+present|maybe)\)\s*$/i,FALLBACK_QUOTED_TAP:/^(?:tap|click)\s+.*?['"]([^'"]+)['"]/i},Ht={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+)\]/};function kt(a){return(a||"").replace(/&#(\d+);/g,(e,t)=>{try{return String.fromCodePoint(parseInt(t,10))}catch{return""}}).replace(/&#x([0-9a-fA-F]+);/g,(e,t)=>{try{return String.fromCodePoint(parseInt(t,16))}catch{return""}}).replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&apos;/g,"'")}function oe(a){return kt(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()}var mi=new Map;function pe(a,e){if(!a||!e)return!1;let t=oe(a),s=oe(e);if(t.length<=3){let i=mi.get(t);if(!i){let r=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");i=new RegExp(`(?:^|\\b)${r}(?:\\b|$)`,"i"),mi.set(t,i)}return i.test(s)}return s.includes(t)}var yi=["sign in","signin","login","log in","submit","save","continue","next","done","send","confirm","proceed","post","rate","enter","join","apply","update"],Si=["sign in","signin","login","log in","submit","save","next","continue","done","like","fav","favorite","star","bookmark","add","post","confirm","apply","explore","search","filter"];function kn(a,e){let t=oe(a),s=oe(e),i=t.length,r=s.length;if(i===0)return r;if(r===0)return i;let n=Array.from({length:r+1},(c,l)=>l),o=new Array(r+1);for(let c=1;c<=i;c++){o[0]=c;for(let p=1;p<=r;p++){let d=t[c-1]===s[p-1]?0:1;o[p]=Math.min(n[p]+1,o[p-1]+1,n[p-1]+d)}let l=n;n=o,o=l}return n[r]}function Dn(a,e){let t=oe(a),s=oe(e);if(t===s)return 1;if(!t||!s)return 0;let i=Math.max(t.length,s.length);return 1-kn(t,s)/i}function bt(a,e,t=3,s=.35){return Array.from(new Set(e.map(n=>n.trim()).filter(n=>n.length>0))).map(n=>({text:n,score:Dn(a,n)})).filter(n=>n.score>=s).sort((n,o)=>o.score-n.score).slice(0,t)}function wi(a){let e=a.match(Ht.BOUNDS_RECT);if(!e)return{x1:0,y1:0,x2:0,y2:0,width:0,height:0,centerX:0,centerY:0};let t=parseInt(e[1],10),s=parseInt(e[2],10),i=parseInt(e[3],10),r=parseInt(e[4],10);return{x1:t,y1:s,x2:i,y2:r,width:i-t,height:r-s,centerX:Math.round((t+i)/2),centerY:Math.round((s+r)/2)}}function An(a){let e=a.match(Ht.HIERARCHY_ROOT);if(!e)return{root:[],flat:[]};let t=e[1],s=new RegExp(Ht.NODE_TAG.source,"gi"),i=new RegExp(Ht.ATTR_KEY_VALUE.source,"g"),r=[],n=[],o=[],c;for(;(c=s.exec(t))!==null;){let l=c[0],p=c[1],d=c[2]==="/";if(l.startsWith("</node>")){o.pop();continue}let h={};i.lastIndex=0;let u;for(;(u=i.exec(p))!==null;)h[u[1]]=u[2];let m={index:parseInt(h.index||"0",10),text:kt(h.text||""),resourceId:h["resource-id"]||"",className:h.class||"",packageName:h.package||"",contentDesc:kt(h["content-desc"]||""),bounds:wi(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:[]};n.push(m),o.length===0?r.push(m):o[o.length-1].children.push(m),d||o.push(m)}return{root:r,flat:n}}function $n(a){let e=[],t=[],s=[],i=a.length,r=0;for(;r<i;){let n=a.indexOf("<",r);if(n===-1)break;if(a.startsWith("<!--",n)){let T=a.indexOf("-->",n+4);r=T===-1?i:T+3;continue}if(a.startsWith("<?",n)){let T=a.indexOf("?>",n+2);r=T===-1?i:T+2;continue}if(a.startsWith("</",n)){let T=a.indexOf(">",n+2);if(T===-1)break;a.slice(n+2,T).trim()==="node"&&s.pop(),r=T+1;continue}let o=-1,c=null,l=!1;for(let T=n+1;T<i;T++){let S=a[T];if(c)S===c&&(c=null);else if(S==='"'||S==="'")c=S;else if(S===">"){o=T,a[T-1]==="/"&&(l=!0);break}}if(o===-1)break;let p=a.slice(n+1,l?o-1:o).trim();r=o+1;let d=0;for(;d<p.length&&!/\s/.test(p[d]);)d++;if(p.slice(0,d)!=="node")continue;let u={},m=d,y=p.length;for(;m<y;){for(;m<y&&/\s/.test(p[m]);)m++;if(m>=y)break;let T=m;for(;m<y&&p[m]!=="="&&!/\s/.test(p[m]);)m++;let S=p.slice(T,m);for(;m<y&&/\s/.test(p[m]);)m++;if(m>=y||p[m]!=="=")break;for(m++;m<y&&/\s/.test(p[m]);)m++;if(m>=y)break;let b=p[m];if(b==='"'||b==="'"){m++;let E=m;for(;m<y&&p[m]!==b;)m++;u[S]=p.slice(E,m),m<y&&m++}else{let E=m;for(;m<y&&!/\s/.test(p[m]);)m++;u[S]=p.slice(E,m)}}let v={index:parseInt(u.index||"0",10),text:kt(u.text||""),resourceId:u["resource-id"]||"",className:u.class||"",packageName:u.package||"",contentDesc:kt(u["content-desc"]||""),bounds:wi(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:[]};t.push(v),s.length===0?e.push(v):s[s.length-1].children.push(v),l||s.push(v)}return{root:e,flat:t}}function we(a,e){if((e||C.parser?.mode||"regex")==="streaming")try{return $n(a)}catch{}return An(a)}function jr(a,e){return a.filter(t=>{if(e.clickable!==void 0&&t.clickable!==e.clickable||e.className&&!t.className.includes(e.className))return!1;if(e.text){if(typeof e.text=="string"){if(!t.text.toLowerCase().includes(e.text.toLowerCase()))return!1}else if(!e.text.test(t.text))return!1}if(e.contentDesc){if(typeof e.contentDesc=="string"){if(!t.contentDesc.toLowerCase().includes(e.contentDesc.toLowerCase()))return!1}else if(!e.contentDesc.test(t.contentDesc))return!1}if(e.resourceId){if(typeof e.resourceId=="string"){if(!t.resourceId.toLowerCase().includes(e.resourceId.toLowerCase()))return!1}else if(!e.resourceId.test(t.resourceId))return!1}return!0})}var Cn=[/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];function Me(a){if(a.resourceId&&/logbox/i.test(a.resourceId)||a.className&&/logbox/i.test(a.className))return!0;let e=`${a.text} ${a.contentDesc}`.trim();return!!Cn.some(t=>t.test(e))}function ze(a){return a.filter(e=>Me(e)?!1:(e.className.includes("EditText")||e.className.includes("TextInputEditText")||e.focused&&e.clickable)&&e.bounds.width>0&&e.bounds.height>0)}function ps(a){return a.className.includes("EditText")||a.className.includes("TextInputEditText")?!0:(a.children||[]).some(ps)}function Bt(a,e){let t=e?.width||C.screen.fallbackWidth,s=e?.height||C.screen.fallbackHeight,i=s*.08,r=s*.88,n=a.filter(c=>!Me(c)&&(c.className.includes("RecyclerView")||c.className.includes("ListView")||c.className.includes("ScrollView"))),o=[];for(let c of n)for(let l of c.children){if(Me(l)||l.bounds.centerY<i||l.bounds.centerY>r)continue;(l.clickable||l.children.some(d=>!Me(d)&&d.clickable&&d.bounds.width>t*.1))&&l.bounds.width>t*.08&&l.bounds.height>s*.02&&!ps(l)&&o.push(l)}return o.length===0?a.filter(c=>{if(Me(c))return!1;let l=(c.text||c.contentDesc||"").trim();return l.length<=1||/^(remember me|forgot password|sign in|sign up|login|submit|cancel)$/i.test(l)?!1:c.clickable&&c.bounds.width>t*.14&&c.bounds.height>s*.033&&c.bounds.centerY>i&&c.bounds.centerY<r&&!c.className.includes("Button")&&!c.className.includes("EditText")&&!ps(c)}):o}function Qe(a){return a.filter(e=>{if(Me(e)||!e.clickable||e.bounds.width===0||e.className.includes("EditText"))return!1;let t=e.className.includes("Button")||e.className.includes("ImageButton"),s=Si.some(i=>pe(i,e.text)||pe(i,e.contentDesc)||pe(i,e.resourceId));return t||s})}function Ut(a,e=C.screen.fallbackHeight,t){if(!a||a.length===0)return[];let s=a.find(u=>u.bounds.height>=e*.6&&u.bounds.y1<=e*.15),i=s?.bounds?.y1!==void 0&&s.bounds.y1>=0?s.bounds.y1:0,r=s?.bounds?.y2&&s.bounds.y2>i?s.bounds.y2:e,n=r-i,o=s?.bounds?.x2&&s.bounds.x2>0?s.bounds.x2-(s.bounds.x1||0):C.screen.fallbackWidth,c=i+n*.78,l=a.filter(u=>Me(u)||t&&u.packageName&&!u.packageName.includes(t)||!u.clickable||u.bounds.width<=0||u.bounds.height<=0||!(u.bounds.centerY>=c||u.bounds.y2>=r-20)?!1:u.bounds.width>=o*.12&&u.bounds.width<=o*.55);if(l.length<=1)return l;let p=[];for(let u of l){let m=p.find(y=>Math.abs(y[0].bounds.centerY-u.bounds.centerY)<=30||Math.abs(y[0].bounds.y2-u.bounds.y2)<=20);m||(m=[],p.push(m)),m.push(u)}let d=p.map(u=>{u.sort((y,v)=>y.bounds.centerX-v.bounds.centerX);let m=[];for(let y of u)m.some(v=>Math.abs(v.bounds.centerX-y.bounds.centerX)<o*.08)||m.push(y);return m}).filter(u=>u.length<2||u.length>6?!1:u[u.length-1].bounds.x2-u[0].bounds.x1>=o*.6);return d.length===0?l.slice(0,5):(d.sort((u,m)=>m[0].bounds.centerY-u[0].bounds.centerY),d[0].slice(0,5))}function vi(a){let e=[/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(t=>{let s=`${t.text} ${t.contentDesc}`;return e.some(i=>i.test(s))})}var In=[/(?:\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],Nn=[/(?:\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],Ln=[/(?:\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];function Be(a,e,t=C.safety?.mode||"strict"){if(t==="disabled")return!1;let s=`${a.text} ${a.contentDesc} ${a.resourceId}`.trim();if((t==="moderate"?Nn:In).some(n=>n.test(s)))return!0;let r=e||C.safety?.customBlacklist||[];for(let n of r)if(typeof n=="string"&&n.length>0){if(s.toLowerCase().includes(n.toLowerCase()))return!0}else if(n instanceof RegExp&&n.test(s))return!0;return!1}function Ue(a){let e=`${a.text} ${a.contentDesc} ${a.resourceId}`.trim();return Ln.some(t=>t.test(e))}function jt(a){for(let e of a){let t=`${e.text} ${e.contentDesc} ${e.resourceId}`;if(e.resourceId.toLowerCase().includes("logbox")||t.includes("Unhandled Promise Rejection")||t.includes("SyntaxError")||t.includes("Encountered two children with the same key")||t.includes("Warning:")||t.includes("Console Error")||t.includes("Console Warning")||t.includes("Console Info")||t.includes("TypeError:")||t.includes("ReferenceError:")||/Log \d+ of \d+/i.test(t)||t.includes("LogBox")&&(e.className.includes("Modal")||e.className.includes("ViewGroup"))){let i="error";/warning/i.test(t)?i="warning":/info/i.test(t)&&(i="info");let r=e.resourceId.includes("notification")||e.bounds.height>0&&e.bounds.height<250,n,o=[];for(let p of a){let d=`${p.text} ${p.contentDesc}`.trim();if(!n){let h=d.match(/\b([a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+)\b/);h&&(n=h[1])}/\b(?:at\s+|\()?[a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+/i.test(d)&&!o.includes(d)&&d.length<200&&o.push(d)}let c=a.find(p=>p.clickable&&p.bounds.width>0&&(/^(?:dismiss|close)$/i.test((p.text||"").trim())||/^(?:dismiss|close)$/i.test((p.contentDesc||"").trim())));if(!c&&!r){let d=a.filter(h=>h.clickable&&h.bounds.width>0&&h.bounds.height>0&&!/minimize|copy/i.test(`${h.text} ${h.contentDesc}`)).filter(h=>h.bounds.centerY>C.screen.fallbackHeight*.8);d.length>0&&(d.sort((h,u)=>h.bounds.x1-u.bounds.x1),c=d[0])}!c&&r&&(c=a.find(p=>p.clickable&&p.bounds.y1>=e.bounds.y1-20&&p.bounds.y2<=e.bounds.y2+20&&p.bounds.x1>=e.bounds.x2-120));let l=e.text||e.contentDesc||"React Native LogBox Console Overlay";return{type:"logbox",severity:i,node:e,errorText:l,sourceLocation:n,stackTrace:o.length>0?o:void 0,dismissButton:c,isToast:r}}if(e.resourceId.includes("alertTitle")&&(t.includes("isn't responding")||t.includes("has stopped"))){let i=a.find(r=>r.text.toLowerCase().includes("close app")||r.text.toLowerCase().includes("ok"));return{type:"anr",severity:"error",node:e,errorText:e.text||"Application Not Responding (ANR)",dismissButton:i}}}return null}function Dt(a){return a.filter(e=>Me(e)?!1:(e.checkable||e.className.includes("CheckBox")||e.className.includes("Switch")||e.className.includes("RadioButton")||e.className.includes("SwitchCompat"))&&e.bounds.width>0&&e.bounds.height>0)}function st(a,e=C.screen.fallbackHeight,t=C.screen.fallbackWidth){let s=e*.22,i=/(?:menu|hamburger|drawer|navigation|\u2630|open[_\s-]*drawer)/i,r=a.find(n=>{if(!n.clickable||n.bounds.width===0||n.bounds.centerY>s)return!1;let o=`${n.contentDesc} ${n.text} ${n.resourceId}`;return i.test(o)});return r||a.find(n=>{if(!n.clickable||n.bounds.width===0||n.bounds.height===0||n.bounds.centerY>s||(n.text||n.contentDesc||"").trim().length>0)return!1;let c=n.bounds.x1<=t*.25,l=n.bounds.width<=250&&n.bounds.height<=250;return c&&l})||null}function xi(a,e=C.screen.fallbackHeight,t=C.screen.fallbackWidth){let s=e*.22;return a.filter(i=>{if(!i.clickable||i.bounds.width===0||i.bounds.height===0||i.bounds.centerY>s)return!1;let r=i.bounds.x2>=t*.75,n=i.bounds.width<=250&&i.bounds.height<=250;return r&&n})}function Ti(a){return a.filter(e=>{if(e.bounds.width===0||e.bounds.height===0)return!1;let t=e.className.includes("ScrollView")||e.className.includes("RecyclerView")||e.className.includes("ListView")||e.className.includes("NestedScrollView");return e.scrollable||t})}function Ei(a,e=C.screen.fallbackWidth,t=C.screen.fallbackHeight,s=C.visual.overlapRatio){let i=[];for(let h of a)(h.text||h.clickable)&&h.bounds.width>0&&(h.bounds.x2>e+10&&h.bounds.x1<e&&i.push({type:"OVERFLOW",severity:"WARN",description:`Element "${h.text||h.contentDesc||h.className}" overflows right screen boundary by ${h.bounds.x2-e}px`,elements:[h.text||h.contentDesc||h.className]}),h.bounds.y2>t+20&&h.bounds.y1<t&&i.push({type:"OVERFLOW",severity:"WARN",description:`Element "${h.text||h.contentDesc||h.className}" overflows bottom screen boundary by ${h.bounds.y2-t}px`,elements:[h.text||h.contentDesc||h.className]}));let r=a.filter(h=>h.text&&h.text.trim().length>1&&h.bounds.width>0&&h.bounds.height>0),n=200,o=new Map;for(let h of r){let u=Math.floor(h.bounds.y1/n),m=Math.floor(h.bounds.y2/n);for(let y=u;y<=m;y++)o.has(y)||o.set(y,[]),o.get(y).push(h)}let c=new Set;for(let[,h]of o)for(let u=0;u<h.length;u++)for(let m=u+1;m<h.length;m++){let y=h[u],v=h[m];if(y===v||y.text===v.text)continue;let T=y.bounds.x1<v.bounds.x1?`${y.bounds.x1},${y.bounds.y1}:${v.bounds.x1},${v.bounds.y1}`:`${v.bounds.x1},${v.bounds.y1}:${y.bounds.x1},${y.bounds.y1}`;if(c.has(T))continue;c.add(T);let S=y.bounds,b=v.bounds;if(Math.abs(S.centerY-b.centerY)>(S.height+b.height)/2)continue;let E=Math.max(0,Math.min(S.x2,b.x2)-Math.max(S.x1,b.x1)),D=Math.max(0,Math.min(S.y2,b.y2)-Math.max(S.y1,b.y1)),B=E*D;if(B>0){let k=S.width*S.height,j=b.width*b.height,M=Math.min(k,j);M>0&&B/M>s&&i.push({type:"OVERLAP",severity:"WARN",description:`Visual text collision: "${y.text}" overlaps with "${v.text}" (${Math.round(B)}px\xB2 collision)`,elements:[y.text,v.text]})}}let l=a.filter(h=>(h.className.includes("ProgressBar")||h.className.includes("ActivityIndicator"))&&h.bounds.width>0);l.length>0&&i.push({type:"STALLED_LOADER",severity:"WARN",description:`Active loading indicator (${l[0].className}) detected on screen`,elements:l.map(h=>h.className)});let p=[],d=new Set;for(let h of i){let u=`${h.type}::${[...h.elements].sort().join("::")}`;d.has(u)||(d.add(u),p.push(h))}return p}function us(a,e,t){let s=e?.height||C.screen.fallbackHeight,i=e?.width||C.screen.fallbackWidth,r=Ut(a,s,t);if(r.length>1&&r[0].bounds.centerY>=s*.75)return{paradigm:"bottom_tabs",roots:r};let n=s*.05,o=s*.22,c=a.filter(p=>{if(Me(p)||t&&p.packageName&&!p.packageName.includes(t))return!1;let d=p.className.includes("Tab")||p.resourceId.toLowerCase().includes("tab")||p.resourceId.toLowerCase().includes("header_tab")||/(?:tab\s*\d|\bphotos\b|\bvideos\b|\baudios\b)/i.test(`${p.text} ${p.contentDesc}`),h=p.bounds.centerY>=n&&p.bounds.centerY<=o,u=p.bounds.height>25&&p.bounds.height<=220;return(d||h)&&p.clickable&&p.bounds.width>50&&u});if(c.length>=2){let p=[];for(let h of c){let u=p.find(m=>Math.abs(m[0].bounds.centerY-h.bounds.centerY)<=20);u||(u=[],p.push(u)),u.push(h)}let d=p.find(h=>h.length>=2);if(d){d.sort((m,y)=>m.bounds.centerX-y.bounds.centerX);let h=[];for(let m of d)h.some(y=>Math.abs(y.bounds.centerX-m.bounds.centerX)<40)||h.push(m);if(!(h.length===2&&h[0].bounds.x1<=i*.25&&h[1].bounds.x2>=i*.75&&!h.some(m=>m.className.includes("Tab")||m.resourceId.toLowerCase().includes("tab")))&&h.length>=2)return{paradigm:"top_tabs",roots:h.slice(0,5)}}}let l=st(a,s,i);return l?{paradigm:"drawer",roots:[l]}:{paradigm:"single_hub",roots:[]}}function Pi(a,e=2){if(a.length<=e)return a;let t=[];for(let i of a){let r=t.find(n=>{let o=n[0],c=o.className===i.className,l=Math.abs(o.bounds.width-i.bounds.width)<=30,p=Math.abs(o.bounds.height-i.bounds.height)<=30,d=Math.abs(o.bounds.x1-i.bounds.x1)<=30;return c&&l&&p&&d});r||(r=[],t.push(r)),r.push(i)}let s=[];for(let i of t)s.push(...i.slice(0,e));return s}function Re(a,e=C.screen.fallbackHeight,t=C.screen.fallbackWidth,s=!1){let i=e*.22,r=t*.28,n=/(?: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=c=>c.bounds.width>0&&c.bounds.height>0&&c.bounds.centerY<=i;for(let c of a){if(Me(c)||!o(c))continue;let l=`${c.text||""} ${c.contentDesc||""} ${c.resourceId||""}`.trim();if(n.test(l)||l.length<=2&&/^[<‹«←x✕×\-]$/i.test(l))return c.clickable?c:a.find(d=>d.clickable&&d.bounds.x1<=c.bounds.x1&&d.bounds.x2>=c.bounds.x2&&d.bounds.y1<=c.bounds.y1&&d.bounds.y2>=c.bounds.y2&&d.bounds.width<=300&&d.bounds.height<=300)||c}for(let c of a){if(Me(c)||!o(c)||c.bounds.x1>r)continue;if(c.className.includes("ImageButton")||c.className.includes("ImageView")||c.className.includes("ViewGroup")&&c.bounds.width<=250&&c.bounds.height<=250){let p=`${c.text||""} ${c.contentDesc||""} ${c.resourceId||""}`.toLowerCase();if(p.includes("back")||p.includes("up")||p.includes("arrow")||p.includes("chevron")||p.includes("nav")||p.includes("close")||p.includes("cancel"))return c.clickable?c:a.find(h=>h.clickable&&h.bounds.x1<=c.bounds.x1&&h.bounds.x2>=c.bounds.x2&&h.bounds.y1<=c.bounds.y1&&h.bounds.y2>=c.bounds.y2)||c}}if(s){let c=a.find(l=>{if(Me(l)||!o(l)||!l.clickable)return!1;let p=l.bounds.x1<=r,d=l.bounds.width<=250&&l.bounds.height<=250;return p&&d&&l.bounds.width<t*.4});if(c)return c}return null}import Ze from"fs";import it from"path";var At=class{outputDir;steps=[];errorsDetected=[];startTime=new Date;packageName="Unknown";deviceId="Unknown";pendingWrites=[];formats=["json","md","junit","html"];videoPath;embedScreenshots=!1;screenshotBuffers=new Map;stepListeners=[];completeListeners=[];errorListeners=[];constructor(e,t){typeof e=="object"&&e!==null?(this.outputDir=e.outputDir||it.resolve(process.cwd(),"output"),e.formats&&e.formats.length>0&&(this.formats=e.formats),this.embedScreenshots=!!e.embedScreenshots):(this.outputDir=e||it.resolve(process.cwd(),"output"),t&&t.length>0&&(this.formats=t)),Ze.existsSync(this.outputDir)||Ze.mkdirSync(this.outputDir,{recursive:!0})}setEmbedScreenshots(e){this.embedScreenshots=e}onStep(e){return this.stepListeners.push(e),()=>{let t=this.stepListeners.indexOf(e);t!==-1&&this.stepListeners.splice(t,1)}}onComplete(e){return this.completeListeners.push(e),()=>{let t=this.completeListeners.indexOf(e);t!==-1&&this.completeListeners.splice(t,1)}}onError(e){return this.errorListeners.push(e),()=>{let t=this.errorListeners.indexOf(e);t!==-1&&this.errorListeners.splice(t,1)}}setFormats(e){this.formats=e}setVideoPath(e){this.videoPath=e}init(e,t){this.packageName=e,this.deviceId=t,this.startTime=new Date,this.steps=[],this.errorsDetected=[],this.pendingWrites=[],this.videoPath=void 0,this.screenshotBuffers.clear()}clearScreenshotBuffers(){this.screenshotBuffers.clear()}logStep(e,t,s,i,r,n,o,c,l){let p=this.steps.length+1,d;if(n){this.embedScreenshots&&this.screenshotBuffers.set(p,n);let m=`step_${p}_${e.toLowerCase().replace(/[^a-z0-9]/g,"_")}.png`,y=it.join(this.outputDir,m),v=Ze.promises.writeFile(y,n).catch(T=>{console.error(`Warning: Failed to write screenshot ${m}:`,T)}).finally(()=>{let T=this.pendingWrites.indexOf(v);T!==-1&&this.pendingWrites.splice(T,1)});this.pendingWrites.push(v),d=m}let h=typeof o?.videoOffsetMs=="number"?o.videoOffsetMs:void 0,u={stepIndex:p,timestamp:new Date().toISOString(),phase:e,description:t,action:s,target:r,status:i,screenshotPath:d,durationMs:c,triageBundle:l,videoOffsetMs:h,details:o};this.steps.push(u);for(let m of this.stepListeners)try{m(u)}catch{}return u}logError(e){this.errorsDetected.push(e);for(let t of this.errorListeners)try{t(e)}catch{}}async generateReport(e){this.pendingWrites.length>0&&await Promise.all(this.pendingWrites);let t=new Date,s=Math.round((t.getTime()-this.startTime.getTime())/1e3),i={packageName:this.packageName,deviceId:this.deviceId,startTime:this.startTime.toISOString(),endTime:t.toISOString(),durationSeconds:s,totalSteps:this.steps.length,passedSteps:this.steps.filter(n=>n.status==="PASS").length,failedSteps:this.steps.filter(n=>n.status==="FAIL").length,skippedSteps:this.steps.filter(n=>n.status==="SKIPPED").length,warnSteps:this.steps.filter(n=>n.status==="WARN").length,errorsDetected:this.errorsDetected,videoPath:this.videoPath,steps:this.steps},r=e||"prompttest";if(this.formats.includes("json")){let n=it.join(this.outputDir,`${r}-results.json`);await Ze.promises.writeFile(n,JSON.stringify(i,null,2),"utf-8")}if(this.formats.includes("md")){let n=it.join(this.outputDir,`${r}-report.md`),o=this.formatMarkdownReport(i);await Ze.promises.writeFile(n,o,"utf-8")}if(this.formats.includes("junit")){let n=it.join(this.outputDir,`${r}-junit.xml`),o=this.formatJunitXml(i,r);await Ze.promises.writeFile(n,o,"utf-8")}if(this.formats.includes("html")){let n=it.join(this.outputDir,`${r}-report.html`),o=this.formatHtmlReport(i,r);await Ze.promises.writeFile(n,o,"utf-8")}for(let n of this.completeListeners)try{n(i)}catch{}return this.clearScreenshotBuffers(),i}escapeXml(e){return(e||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}escapeHtml(e){return(e||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}formatJunitXml(e,t){let s=e.durationSeconds,i=e.failedSteps+(e.errorsDetected.length>0&&e.failedSteps===0?1:0),r=['<?xml version="1.0" encoding="UTF-8"?>',`<testsuites name="PromptTest" tests="${e.totalSteps}" failures="${i}" skipped="${e.skippedSteps}" time="${s}">`,` <testsuite name="${this.escapeXml(t)}" tests="${e.totalSteps}" failures="${i}" skipped="${e.skippedSteps}" time="${s}">`];for(let n of e.steps){let o=`Step ${n.stepIndex}: ${this.escapeXml(n.action)} - ${this.escapeXml(n.description)}`,c=n.durationMs!==void 0?(n.durationMs/1e3).toFixed(3):"1.000";r.push(` <testcase classname="${this.escapeXml(t)}" name="${o}" time="${c}">`),n.status==="FAIL"?r.push(` <failure message="${this.escapeXml(n.description)}">${this.escapeXml(n.description)}</failure>`):n.status==="SKIPPED"&&r.push(` <skipped message="${this.escapeXml(n.description)}"/>`),r.push(" </testcase>")}return e.failedSteps===0&&e.errorsDetected.length>0&&r.push(` <testcase classname="${this.escapeXml(t)}" name="Error Sentinel Crash Detection" time="0.000">`,` <failure message="${this.escapeXml(e.errorsDetected[0])}">${this.escapeXml(e.errorsDetected.join(`
38
- `))}</failure>`," </testcase>"),r.push(" </testsuite>"),r.push("</testsuites>"),r.join(`
39
- `)}formatMarkdownReport(e){let i=["# \u{1F9EA} PromptTest Autonomous Mobile QA Audit Report","",`**Status:** ${e.failedSteps===0&&e.errorsDetected.length===0?"\u2705 ALL PASSED":"\u26A0\uFE0F ISSUES DETECTED"} `,`**Target Package:** \`${e.packageName}\` `,`**Device:** \`${e.deviceId}\` `,`**Execution Time:** ${e.durationSeconds}s (${e.startTime} to ${e.endTime}) `];if(e.videoPath&&i.push(`**Screen Recording:** [Download Video](./${e.videoPath}) `),i.push("","---","","## \u{1F4CA} Executive Summary","","| Metric | Count |","|---|---|",`| **Total Steps** | ${e.totalSteps} |`,`| **Passed** | \u{1F7E2} ${e.passedSteps} |`,`| **Warnings** | \u{1F7E1} ${e.warnSteps} |`,`| **Failures** | \u{1F534} ${e.failedSteps} |`,`| **Skipped** | \u26AA ${e.skippedSteps} |`,`| **Errors Detected** | ${e.errorsDetected.length} |`,""),e.errorsDetected.length>0){i.push("## \u26A0\uFE0F Errors & Sentinels Flagged");for(let n of e.errorsDetected)i.push(`- \u{1F534} \`${n}\``);i.push("")}i.push("## \u{1F50D} Detailed Execution Timeline"),i.push(""),i.push("| Step | Phase | Action | Target / Details | Status | Duration | Screenshot / Triage |"),i.push("|---|---|---|---|---|---|---|");let r=n=>(n||"").replace(/\|/g,"\\|").replace(/\r?\n/g," ");for(let n of e.steps){let o="\u2014";n.triageBundle?o=`[\u{1F50D} Triage Bundle](./${n.triageBundle})`:n.screenshotPath&&(o=`[\u{1F4F8} Screenshot](./${n.screenshotPath})`);let c=n.durationMs!==void 0?`${n.durationMs}ms`:"\u2014",l=n.status==="PASS"?"\u{1F7E2} PASS":n.status==="WARN"?"\u{1F7E1} WARN":n.status==="SKIPPED"?"\u26AA SKIPPED":"\u{1F534} FAIL";i.push(`| ${n.stepIndex} | ${r(n.phase)} | ${r(n.action)} | ${r(n.description)} | ${l} | ${c} | ${o} |`)}return i.push(""),i.push("---"),i.push("*Generated autonomously by PromptTest Mobile QA Brain.*"),i.join(`
40
- `)}resolveScreenshotData(e){if(e.screenshotPath){if(this.embedScreenshots){if(this.screenshotBuffers.has(e.stepIndex))return`data:image/png;base64,${this.screenshotBuffers.get(e.stepIndex).toString("base64")}`;let t=it.join(this.outputDir,e.screenshotPath);if(Ze.existsSync(t))try{return`data:image/png;base64,${Ze.readFileSync(t).toString("base64")}`}catch{}}return`./${e.screenshotPath}`}}formatHtmlReport(e,t){return hs(e,{suiteName:t,embedScreenshots:this.embedScreenshots,screenshotResolver:s=>this.resolveScreenshotData(s)})}};function hs(a,e){let t=e?.suiteName||"PromptTest Suite",s=a.failedSteps===0&&a.errorsDetected.length===0,i=s?"ALL PASSED":"ISSUES DETECTED",r=s?"#10b981":"#ef4444",n=a.totalSteps,o=n>0?Math.round(a.passedSteps/n*100):100,c=u=>(u||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;"),l=[],p=a.steps.map(u=>{let m=u.status.toLowerCase(),y=u.durationMs!==void 0?`${u.durationMs}ms`:"\u2014",v=e?.screenshotResolver?e.screenshotResolver(u):u.screenshotPath?`./${u.screenshotPath}`:void 0;v&&l.push({stepIndex:u.stepIndex,action:u.action,description:u.description,status:u.status,src:v,durationMs:u.durationMs});let T="\u2014";if(u.triageBundle){let k=c(u.triageBundle),j=u.triageBundle.endsWith(".zip"),M=j?k:`${k}.zip`,J=j?k.replace(/\.zip$/,""):k;T=`
19
+ `)}var Ce,Ss=xt(()=>{"use strict";It();Ce=null});import{spawn as Lt}from"child_process";import ws from"readline";function zi(a,e,t,s){let r={},n=a.match(/level:\s*(\d+)/i),i=a.match(/temperature:\s*(\d+)/i),o=a.match(/status:\s*(\d+)/i);(n||i||o)&&(r.battery={...n?{level:Number(n[1])}:{},...i?{temperatureC:Number(i[1])/10}:{},...o?{status:o[1]}:{}});let l=e.match(/(?:Physical|Override)\s+size:\s*(\d+)x(\d+)/i),c=e.match(/(?:Physical|Override)\s+density:\s*(\d+)/i);(l||c)&&(r.display={...l?{width:Number(l[1]),height:Number(l[2])}:{},...c?{density:Number(c[1])}:{}});let p=t.match(/TOTAL\s+([\d,]+)/i),d=t.match(/Private Dirty:\s*(\d+)/i),u=t.match(/Native Heap:\s*(\d+)/i),g=[...(t.match(/Total PSS by category:\s*([\s\S]*?)(?:\n\s*\n|Total RAM:|$)/i)?.[1]||"").matchAll(/^\s*([\d,]+)K:\s*(.+)$/gim)],b=g.reduce((P,$)=>P+Number($[1].replace(/,/g,"")),0),T=g.find(P=>/native/i.test(P[2]));(p||d||u||b)&&(r.memory={...p?{pssKb:Number(p[1].replace(/,/g,""))}:b?{pssKb:b}:{},...d?{privateDirtyKb:Number(d[1])}:{},...u?{nativeHeapKb:Number(u[1])}:T?{nativeHeapKb:Number(T[1].replace(/,/g,""))}:{}});let x=s.match(/Total frames rendered:\s*(\d+)/i),S=s.match(/Janky frames:\s*(\d+)/i),y=s.match(/90th percentile:\s*([\d.]+)\s*ms/i);return(x||S||y)&&(r.graphics={...x?{framesRendered:Number(x[1])}:{},...S?{jankyFrames:Number(S[1])}:{},...y?{percentile90Ms:Number(y[1])}:{}}),r}function vs(a){let e=(a||"").toLowerCase();return e.startsWith("emulator-")||e.includes("127.0.0.1")||e.includes("localhost")?"emulator":e.includes(":")||e.includes("._tcp")||e.includes("_adb-tls-")?"wifi":"usb"}function Ir(a){let e=new Map;for(let s of a){let r=s.connectionType||vs(s.serial),n="";if(s.model&&s.product)n=`${s.model}__${s.product}`.toLowerCase();else if(s.model)n=s.model.toLowerCase();else{let i=s.serial.match(/^adb-([a-zA-Z0-9]+)-/);n=i?i[1].toLowerCase():s.serial.toLowerCase()}e.has(n)||e.set(n,[]),e.get(n).push({...s,connectionType:r})}let t=[];for(let s of e.values()){if(s.length===1){t.push(s[0]);continue}let r=[...s].sort((n,i)=>{let o=l=>{let c=l.connectionType||vs(l.serial);return c==="usb"?0:c==="wifi"&&!l.serial.includes("._tcp")&&!l.serial.includes("_adb-tls-")?1:c==="wifi"?2:3};return o(n)-o(i)});t.push(r[0])}return t}var nt,Pe,Ne=xt(()=>{"use strict";Ar();Fe();It();Ss();nt=class{defaultSerial=null;cachedDimensions=new Map;inFlightHierarchies=new Map;constructor(e){e&&(this.defaultSerial=e)}setDefaultDevice(e){this.defaultSerial=e}getDefaultDevice(){return this.defaultSerial}async getScreenDimensions(e){let t=e||this.defaultSerial||"default";if(this.cachedDimensions.has(t))return this.cachedDimensions.get(t);try{let{stdout:r}=await this.execAdb(["shell","wm","size"],e),n=r.match(/(?:Physical|Override)\s*size:\s*(\d+)x(\d+)/i);if(n){let i={width:parseInt(n[1],10),height:parseInt(n[2],10)};return this.cachedDimensions.set(t,i),i}}catch{}let s={width:I.screen.fallbackWidth,height:I.screen.fallbackHeight};return this.cachedDimensions.set(t,s),s}clearScreenDimensionsCache(e){e?this.cachedDimensions.delete(e):this.cachedDimensions.clear()}async getNavigationBarHeight(e){try{let{stdout:t}=await this.execAdb(["shell","dumpsys","window","displays"],e).catch(()=>({stdout:""})),s=t.match(/(?:navigationBarHeight|navBarHeight|navigationBarFrame)[=:\s]+(\d+)/i);if(s&&parseInt(s[1],10)>0)return parseInt(s[1],10)}catch{}return 0}isGlobalAdbCommand(e){return e?["devices","connect","disconnect","version","kill-server","start-server","help"].includes(e.toLowerCase()):!0}async execAdbInternal(e,t,s=2e4){let r=t||this.defaultSerial;!r&&!this.isGlobalAdbCommand(e[0])&&(await this.ensureDefaultDevice(),r=this.defaultSerial);let n=r&&!this.isGlobalAdbCommand(e[0])?["-s",r,...e]:[...e],i=await Nr();return new Promise((o,l)=>{let c=Lt(i,n),p="",d="",u=!1,h=setTimeout(()=>{u=!0;try{c.kill("SIGKILL")}catch{}l(new Error(`ADB command timed out after ${s}ms: adb ${n.join(" ")}`))},s);c.stdout.on("data",g=>{p+=g.toString("utf-8")}),c.stderr.on("data",g=>{d+=g.toString("utf-8")}),c.on("close",g=>{clearTimeout(h),!u&&(g===0?o({stdout:p,stderr:d}):l(new Error(`ADB command failed (code ${g}): adb ${n.join(" ")}
20
+ Error: ${d}`)))}),c.on("error",g=>{clearTimeout(h),!u&&l(g)})})}async execAdb(e,t,s=I.adb.commandTimeoutMs,r=I.device.maxDeviceRetries){let n;for(let i=0;i<=r;i++)try{return await this.execAdbInternal(e,t,s)}catch(o){n=o;let l=o instanceof Error?o.message:String(o),c=l.toLowerCase(),p=c.includes("device offline")||c.includes("device not found"),d=p||c.includes("error: closed")||c.includes("timed out");if(i>=r||!d||this.isGlobalAdbCommand(e[0]))throw p?new Tt(`Target Android device ${t||this.defaultSerial||""} is offline or disconnected: ${l}`):o;if(I.device.autoReconnect&&p)try{await this.execAdbInternal(["reconnect"],t,5e3).catch(()=>{})}catch{}await new Promise(u=>setTimeout(u,I.adb.retryBaseDelayMs*Math.pow(2,i+1)))}throw n||new Error(`ADB command failed after ${r} retries: ${e.join(" ")}`)}async execAdbBinary(e,t,s=45e3){let r=t||this.defaultSerial;!r&&!this.isGlobalAdbCommand(e[0])&&(await this.ensureDefaultDevice(),r=this.defaultSerial);let n=r&&!this.isGlobalAdbCommand(e[0])?["-s",r,...e]:[...e];return new Promise((i,o)=>{let l=Lt(Ze(),n),c=[],p="",d=!1,u=setTimeout(()=>{d=!0;try{l.kill("SIGKILL")}catch{}o(new Error(`ADB binary command timed out after ${s}ms: adb ${n.join(" ")}`))},s);l.stdout.on("data",h=>c.push(Buffer.from(h))),l.stderr.on("data",h=>{p+=h.toString("utf-8")}),l.on("close",h=>{clearTimeout(u),!d&&(h===0?i(Buffer.concat(c)):o(new Error(`ADB binary command failed (code ${h}): adb ${n.join(" ")}
21
+ ${p}`)))}),l.on("error",h=>{clearTimeout(u),!d&&o(h)})})}async getRawConnectedDevices(){let{stdout:e}=await this.execAdb(Te.DEVICES),t=e.split(`
22
+ `).map(i=>i.trim()).filter(Boolean),s=t.findIndex(i=>i.toLowerCase().includes("list of devices attached")),r=s!==-1?t.slice(s+1):t,n=[];for(let i of r){if(!i||i.startsWith("*")||i.toLowerCase().includes("list of devices"))continue;let o=i.split(/\s+/);if(o.length>=2){let l=o[0],c=o[1],p,d;for(let u of o.slice(2))u.startsWith("model:")&&(p=u.replace("model:","")),u.startsWith("product:")&&(d=u.replace("product:",""));n.push({serial:l,state:c,model:p,product:d,connectionType:vs(l)})}}return n}async getConnectedDevices(e){let t=await this.getRawConnectedDevices();return e?.deduplicate===!1?t:Ir(t)}deduplicateDevices(e){return Ir(e)}async wakeDevice(e){try{await this.execAdb(["shell","input","keyevent","224"],e),await this.execAdb(["shell","wm","dismiss-keyguard"],e)}catch{}}async ensureDefaultDevice(e){if(e)return await this.wakeDevice(e),e;if(this.defaultSerial)return await this.wakeDevice(this.defaultSerial),this.defaultSerial;let s=(await this.getConnectedDevices()).find(r=>r.state==="device");return s&&(this.defaultSerial=s.serial,await this.wakeDevice(this.defaultSerial)),this.defaultSerial||void 0}async getDeviceModel(e){try{let{stdout:n}=await this.execAdb(["shell","getprop","ro.product.model"],e),i=n.trim();if(i)return i}catch{}let t=await this.getConnectedDevices(),s=e||this.defaultSerial;return t.find(n=>n.serial===s)?.model||s||"Android Device"}async captureScreenshot(e){return this.execAdbBinary(Te.SCREENCAP,e)}async getUiHierarchy(e){let t=e||this.defaultSerial||"default",s=this.inFlightHierarchies.get(t);if(s)return s;let r=(async()=>{try{let{stdout:n}=await this.execAdb(Te.UIAUTOMATOR_DUMP,e,1e4,0),i=n.indexOf("<hierarchy");return i>=0?n.slice(i):n}finally{this.inFlightHierarchies.delete(t)}})();return this.inFlightHierarchies.set(t,r),r}clearInFlightHierarchies(e){e?this.inFlightHierarchies.delete(e):this.inFlightHierarchies.clear()}async getForegroundPackage(e){let t=s=>{let r=s.toLowerCase();return r==="android"||r.includes("systemui")||r.includes("inputmethod")||r.includes("latin")||r.includes("gboard")||r.includes("swiftkey")||r.includes("ime")};try{let{stdout:s}=await this.execAdb(["shell","dumpsys","activity","activities"],e).catch(()=>({stdout:""})),r=s.match(/(?:topResumedActivity|mResumedActivity|ResumedActivity)[=:\s]+(?:ActivityRecord\{[^}]*?\s+)?([a-zA-Z0-9._]+)\//i);if(r&&r[1]&&!t(r[1]))return r[1];let{stdout:n}=await this.execAdb(Te.FOREGROUND_APP,e).catch(()=>({stdout:""})),i=n.matchAll(/(?:mCurrentFocus|mFocusedWindow|mFocusedApp|topResumedActivity|mResumedActivity|mFocusedActivity)[=:\s]+(?:Window\{[^}]*?\s+|ActivityRecord\{[^}]*?\s+|AppWindowToken\{[^}]*?\s+)?([a-zA-Z0-9._]+)\//gi);for(let o of i)if(o&&o[1]&&!t(o[1]))return o[1]}catch{}return null}async tap(e,t,s){await this.execAdb(Te.INPUT_TAP(e,t),s)}async inputText(e,t){let s=e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/'/g,"\\'").replace(/%/g,"\\%").replace(/ /g,"%s").replace(/&/g,"\\&").replace(/</g,"\\<").replace(/>/g,"\\>").replace(/\(/g,"\\(").replace(/\)/g,"\\)").replace(/;/g,"\\;").replace(/\|/g,"\\|").replace(/\$/g,"\\$").replace(/#/g,"\\#").replace(/\*/g,"\\*").replace(/\?/g,"\\?").replace(/~/g,"\\~").replace(/`/g,"\\`");try{await this.execAdb(Te.INPUT_TEXT(s),t)}catch{for(let r of e)if(r===" ")await this.pressKey(62,t);else if(r==="@")await this.pressKey(77,t);else{let n=r.replace(/([\\"'&<>()|$;#*?~`%])/g,"\\$1");await this.execAdb(["shell","input","text",n],t).catch(()=>{})}}}async pressKey(e,t){await this.execAdb(Te.INPUT_KEY(e),t)}async pressEnter(e){await this.pressKey(66,e)}async pressTab(e){await this.pressKey(61,e)}async pressHome(e){await this.pressKey(3,e)}async pressBack(e){await this.pressKey(4,e)}async setWifiEnabled(e,t){await this.execAdb(["shell","svc","wifi",e?"enable":"disable"],t)}async setAirplaneMode(e,t){await this.execAdb(["shell","cmd","connectivity","airplane-mode",e?"enable":"disable"],t)}async setOrientation(e,t){await this.execAdb(["shell","content","insert","--uri","content://settings/system","--bind","name:s:accelerometer_rotation","--bind","value:i:0"],t).catch(()=>{});let s=e==="landscape"?"1":"0";await this.execAdb(["shell","settings","put","system","user_rotation",s],t)}async openNotificationShade(e){await this.execAdb(["shell","cmd","statusbar","expand-notifications"],e)}async collapseNotificationShade(e){await this.execAdb(["shell","cmd","statusbar","collapse"],e)}async clearInput(e=45,t){let s=Array(e).fill("67");await this.execAdb(["shell","input","keyevent",...s],t)}async clearField(e,t=80){await this.pressKey(123,e);let s=Math.max(50,t+10),r=Array(s).fill("67");await this.execAdb(["shell","input","keyevent",...r],e).catch(()=>{}),await this.pressKey(122,e);let n=Array(Math.max(30,Math.floor(t/2))).fill("112");await this.execAdb(["shell","input","keyevent",...n],e).catch(()=>{})}async swipe(e,t,s,r,n=300,i){await this.execAdb(Te.INPUT_SWIPE(e,t,s,r,n),i)}async launchApp(e,t){try{let{stdout:s}=await this.execAdb(["shell","cmd","package","resolve-activity","--brief",e],t),n=(s||"").split(`
23
+ `).map(i=>i.trim()).find(i=>i.includes("/")&&!i.includes(" "));if(n){await this.execAdb(["shell","am","start","-n",n],t);return}}catch{}await this.execAdb(Te.LAUNCH_APP(e),t)}async forceStop(e,t){await this.execAdb(Te.FORCE_STOP(e),t)}async clearAppData(e,t){await this.execAdb(Te.CLEAR_APP(e),t)}async restartApp(e,t){await this.forceStop(e,t),await new Promise(s=>setTimeout(s,600)),await this.launchApp(e,t)}async grantPermission(e,t,s){await this.execAdb(Te.GRANT_PERMISSION(e,t),s)}async isAppInstalled(e,t){try{let{stdout:s}=await this.execAdb(["shell","pm","list","packages",e],t);return s.toLowerCase().includes(`package:${e.toLowerCase()}`)||s.includes(e)}catch{return!1}}async installApk(e,t){let{stdout:s,stderr:r}=await this.execAdb(Te.INSTALL_APK(e),t,12e4),n=`${s} ${r}`;if(n.includes("Failure"))throw new Error(`APK installation failed on device ${t||"default"}: ${n.trim()}`)}async uninstallApp(e,t){await this.execAdb(Te.UNINSTALL_APP(e),t)}async openDeepLink(e,t){await this.execAdb(Te.DEEP_LINK(e),t)}async checkCrashes(e,t){try{let{stdout:s}=await this.execAdb(Te.LOGCAT_CRASH(e),t);if(s&&s.includes("FATAL EXCEPTION")&&s.includes(e))return s}catch{}return null}async clearCrashLogs(e){try{await this.execAdb(Te.CLEAR_CRASH_LOGS(),e)}catch{}}async getDeviceIp(e){try{let{stdout:t}=await this.execAdb(Te.GET_WLAN_IP,e),s=t.match(/inet\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}try{let{stdout:t}=await this.execAdb(["shell","ip","route"],e),s=t.match(/src\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}return null}async connectWifi(e,t=5555,s){let r=e,n=t;if(e&&e.includes(":")){let d=e.split(":");r=d[0],n=parseInt(d[1],10)||t}if(!r){let d=await this.getDeviceIp(s);if(!d)throw new Error("Could not auto-detect phone Wi-Fi IP. Make sure the phone is on Wi-Fi, or pass IP manually: npx prompttest wifi <ip>");r=d}let i=`${r}:${n}`;if(e)try{let{stdout:d,stderr:u}=await this.execAdb(Te.CONNECT(i)),h=`${d} ${u}`.trim();if(h.toLowerCase().includes("connected")||h.toLowerCase().includes("already"))return this.defaultSerial=i,{success:!0,target:i,message:h}}catch{}try{await this.execAdb(Te.TCPIP(n),s),await new Promise(d=>setTimeout(d,1e3))}catch{}let{stdout:o,stderr:l}=await this.execAdb(Te.CONNECT(i)),c=`${o} ${l}`.trim(),p=c.toLowerCase().includes("connected")||c.toLowerCase().includes("already");return p&&(this.defaultSerial=i),{success:p,target:i,message:c}}async disconnectWifi(e){let{stdout:t,stderr:s}=await this.execAdb(Te.DISCONNECT(e));return`${t} ${s}`.trim()}async pairWifi(e,t){if(!/^[^:\s]+:\d+$/.test(e))throw new Error("Pairing target must be an IP:port value.");if(!/^\d{4,8}$/.test(t))throw new Error("Pairing code must contain 4 to 8 digits.");let{stdout:s,stderr:r}=await this.execAdb(["pair",e,t]),n=`${s} ${r}`.trim();return{success:/successfully paired|already paired/i.test(n),message:n}}async getDeviceMetrics(e){let[s,r,n,i]=await Promise.all([this.execAdb(["shell","dumpsys","battery"],e,8e3,0).then(o=>o.stdout).catch(()=>""),this.execAdb(["shell","wm","size"],e,8e3,0).then(async o=>`${o.stdout}
24
+ ${(await this.execAdb(["shell","wm","density"],e,8e3,0).catch(()=>({stdout:""}))).stdout}`).catch(()=>""),this.execAdb(["shell","dumpsys","meminfo"],e,8e3,0).then(o=>o.stdout).catch(()=>""),this.execAdb(["shell","dumpsys","gfxinfo"],e,8e3,0).then(o=>o.stdout).catch(()=>"")]);return zi(s,r,n,i)}streamLogcatEvents(e,t){let s=["-s",e,"shell","logcat","-b","events","-v","time"],r=Lt(Ze(),s),n=ws.createInterface({input:r.stdout});return n.on("line",t),r.on("close",()=>{try{n.close()}catch{}}),r.on("error",()=>{}),r}streamLogcatCrashes(e,t){let s=["-s",e,"shell","logcat","-T","1","-s","AndroidRuntime:E","ANRManager:E","-v","time"],r=Lt(Ze(),s),n=ws.createInterface({input:r.stdout});return n.on("line",t),r.on("close",()=>{try{n.close()}catch{}}),r.on("error",()=>{}),r}streamLogcatToasts(e,t){let s=["-s",e,"shell","logcat","-s","Toast:D","Snackbar:D","-v","time"],r=Lt(Ze(),s),n=ws.createInterface({input:r.stdout});return n.on("line",t),r.on("close",()=>{try{n.close()}catch{}}),r.on("error",()=>{}),r}async getLogcatTail(e=150,t){try{let{stdout:s}=await this.execAdb(["logcat","-d","-t",String(e),"-v","time"],t);return s}catch(s){return`Failed to capture logcat: ${s instanceof Error?s.message:String(s)}`}}async startScreenRecording(e="/sdcard/prompttest_temp.mp4",t){let s=t||this.defaultSerial,r=s?["-s",s,"shell","screenrecord",e]:["shell","screenrecord",e],n=Lt(Ze(),r);return n.on("error",()=>{}),{remotePath:e,process:n}}async stopScreenRecording(e,t,s){try{try{e.process.kill("SIGINT")}catch{}return await this.execAdb(["shell","pkill","-2","screenrecord"],s).catch(()=>{}),await new Promise(r=>setTimeout(r,1500)),await this.execAdb(["pull",e.remotePath,t],s),await this.execAdb(["shell","rm","-f",e.remotePath],s).catch(()=>{}),t}catch{return""}}async isKeyboardVisible(e){try{let{stdout:t}=await this.execAdb(["shell","sh","-c",'dumpsys input_method | grep -E "mInputShown=true|mShowRequested=true"'],e,4e3,0),s=t.toLowerCase();if(s.includes("minputshown=true")||s.includes("mshowrequested=true"))return!0}catch{}try{let{stdout:t}=await this.execAdb(["shell","sh","-c",'dumpsys window | grep -E "mInputMethodWindowVisibleHeight"'],e,4e3,0),s=t.match(/mInputMethodWindowVisibleHeight=(\d+)/i);if(s&&parseInt(s[1],10)>0)return!0}catch{}try{let{stdout:t}=await this.execAdb(["shell","dumpsys","input_method"],e,6e3,0),s=t.toLowerCase();return s.includes("minputshown=true")||s.includes("mshowrequested=true")}catch{return!1}}async ensureAccessibilityServices(e){try{await this.execAdb(["shell","settings","put","secure","accessibility_enabled","1"],e,4e3,0)}catch{}}},Pe=new nt});var xi={};Dr(xi,{PerformanceProfiler:()=>fr,parseGfxInfo:()=>wi,parseMemInfo:()=>vi});function wi(a){let e=0,t=0,s=0,r,n,i,o,l,c,p=a.match(/Total frames rendered:\s+(\d+)/i);p&&(e=parseInt(p[1],10));let d=a.match(/Janky frames:\s+(\d+)(?:\s+\(([0-9.]+)%\))?/i);d&&(t=parseInt(d[1],10),d[2]?s=parseFloat(d[2]):e>0&&(s=parseFloat((t/e*100).toFixed(2))));let u=a.match(/50th percentile:\s+(\d+)ms/i);u&&(r=parseInt(u[1],10));let h=a.match(/90th percentile:\s+(\d+)ms/i);h&&(n=parseInt(h[1],10));let g=a.match(/95th percentile:\s+(\d+)ms/i);g&&(i=parseInt(g[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 x=a.match(/Number Slow UI thread:\s+(\d+)/i);return x&&(c=parseInt(x[1],10)),{totalFrames:e,jankyFrames:t,jankPercentage:s,p50Ms:r,p90Ms:n,p95Ms:i,p99Ms:o,missedVsync:l,slowUiThread:c}}function vi(a){let e=0,t,s,r=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);r&&(e=parseInt(r[1],10));let n=a.match(/Native Heap\s+(\d+)/i)||a.match(/Native\s+(\d+)/i);n&&(t=parseInt(n[1],10));let i=a.match(/Java Heap\s+(\d+)/i)||a.match(/Dalvik Heap\s+(\d+)/i);return i&&(s=parseInt(i[1],10)),{totalPssKb:e,nativeHeapKb:t,javaHeapKb:s}}var fr,Ti=xt(()=>{"use strict";Ne();fr=class{driver;serial;packageName="";startTime=0;memorySamples=[];constructor(e,t){this.driver=e||Pe,this.serial=t}async start(e){this.packageName=e,this.startTime=Date.now(),this.memorySamples=[];try{await this.driver.execAdb(["shell","dumpsys","gfxinfo",e,"reset"],this.serial)}catch{}await this.sample(0,"Suite Initialized")}async sample(e,t){if(this.packageName)try{let{stdout:s}=await this.driver.execAdb(["shell","dumpsys","meminfo",this.packageName],this.serial),r=vi(s);this.memorySamples.push({timestamp:Date.now(),stepIndex:e,stepName:t,totalPssKb:r.totalPssKb,nativeHeapKb:r.nativeHeapKb,javaHeapKb:r.javaHeapKb})}catch{}}async stop(){let e=Date.now()-(this.startTime||Date.now()),t={totalFrames:0,jankyFrames:0,jankPercentage:0};if(this.packageName)try{let{stdout:o}=await this.driver.execAdb(["shell","dumpsys","gfxinfo",this.packageName],this.serial);t=wi(o)}catch{}let s=this.memorySamples[0]?.totalPssKb||0,r=this.memorySamples[this.memorySamples.length-1]?.totalPssKb||s,n=this.memorySamples.reduce((o,l)=>Math.max(o,l.totalPssKb),s),i="PASS";return t.jankPercentage>15||t.p95Ms&&t.p95Ms>32?i="FAIL":(t.jankPercentage>5||t.p95Ms&&t.p95Ms>16.7)&&(i="WARN"),{packageName:this.packageName,durationMs:e,fpsCompliance:i,frames:t,memory:{startPssKb:s,peakPssKb:n,endPssKb:r,deltaPssKb:r-s,samples:this.memorySamples}}}}});var Ni={};Dr(Ni,{buildFeedbackUrl:()=>vr,startFeedbackSession:()=>Yn});import jn from"readline";import Wt from"os";import fe from"chalk";function vr(a){let e=(a.message||"Feedback").split(`
25
+ `)[0].trim(),t=e.length>70?e.substring(0,67)+"...":e,s=a.rating?"\u2B50".repeat(a.rating):"";if(a.category==="feature"){let i=new URLSearchParams;return i.set("template","2_feature_request.yml"),i.set("title",`[Feature]: ${t}`),i.set("problem",`User feedback submission${s?` (${s} ${a.rating}/5)`:""}:
26
+ ${a.message}`),i.set("proposed_solution",a.message),i.set("category","Plain-English DSL / Syntax"),`${Sr}/issues/new?${i.toString()}`}if(a.category==="bug"){let i=new URLSearchParams;return i.set("template","1_bug_report.yml"),i.set("title",`[Bug]: ${t}`),i.set("description",a.message),i.set("command_run","npx prompttest"),i.set("environment",`${Wt.type()} ${Wt.release()} (${Wt.arch()}), Node: ${process.version}`),`${Sr}/issues/new?${i.toString()}`}let r=new URLSearchParams;r.set("template","3_feedback.yml");let n=a.rating?` ${"\u2B50".repeat(a.rating)}`:"";if(r.set("title",`[Feedback${n}]: ${t}`),r.set("message",a.message),typeof a.rating=="number"){let o={5:0,4:1,3:2,2:3,1:4}[a.rating]??0;r.set("rating",String(o))}return r.set("environment",`${Wt.type()} ${Wt.release()} (${Wt.arch()}), Node: ${process.version}`),`${Sr}/issues/new?${r.toString()}`}function wr(a,e){return new Promise(t=>{if(a.closed){t("");return}try{a.question(e,s=>{t(s.trim())})}catch{t("")}})}async function Yn(a={}){let e=a.input||process.stdin,t=a.output||process.stdout,s=(n="")=>{t.write(n+`
27
+ `)};if(a.directMessage&&a.directMessage.trim()){let n=a.category||"general",i=vr({category:n,message:a.directMessage.trim()});return s(fe.bold.cyan(`
28
+ \u26A1 PromptTest \u2014 Community Feedback`)),s(fe.gray("\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),s(fe.green("\u2714 Thank you for your feedback! Here is your 1-click submission link:")),s(fe.bold.underline.cyan(i)),s(fe.gray(`\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
29
+ `)),i}let r=jn.createInterface({input:e,output:t});try{s(fe.bold.cyan(`
30
+ \u26A1 PromptTest \u2014 Community Feedback & Suggestions`)),s(fe.gray("\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),s(fe.white("We want to make PromptTest the best mobile QA engine in your toolchain.")),s(fe.white(`Your feedback goes directly to our open community tracker!
31
+ `)),s(fe.bold.white("How would you rate your experience with PromptTest?")),s(` ${fe.cyan("[1]")} \u2B50\u2B50\u2B50\u2B50\u2B50 (5/5) \u2014 Loving it!`),s(` ${fe.cyan("[2]")} \u2B50\u2B50\u2B50\u2B50 (4/5) \u2014 Great, but needs minor improvements`),s(` ${fe.cyan("[3]")} \u2B50\u2B50\u2B50 (3/5) \u2014 Okay, encountered some friction`),s(` ${fe.cyan("[4]")} \u2B50\u2B50 (2/5) \u2014 Frustrating / missing key features`),s(` ${fe.cyan("[5]")} \u2B50 (1/5) \u2014 Not working for me`);let n=await wr(r,fe.bold.cyan(`
32
+ Select rating [1-5] (default: 1): `)),i=parseInt(n,10),l={1:5,2:4,3:3,4:2,5:1}[i]||5;s(fe.bold.white(`
33
+ What type of feedback would you like to share?`)),s(` ${fe.cyan("[1]")} \u{1F4A1} Feature Request or syntax suggestion`),s(` ${fe.cyan("[2]")} \u{1F41B} Bug report or unexpected error`),s(` ${fe.cyan("[3]")} \u{1F4AC} General feedback, question, or compliment`);let c=await wr(r,fe.bold.cyan(`
34
+ Select category [1-3] (default: 1): `)),p="feature";c==="2"?p="bug":c==="3"&&(p="general"),s(fe.bold.white(`
35
+ Please share your thoughts, ideas, or feedback:`));let u=await wr(r,fe.bold.cyan("> "))||"Overall great experience with PromptTest!",h=vr({rating:l,category:p,message:u});return s(fe.gray(`
36
+ \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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`)),s(fe.green.bold("\u2728 Thank you! We generated your 1-click submission:")),s(fe.bold.underline.cyan(h)),s(fe.gray(`\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
37
+ `)),h}finally{try{r.close()}catch{}}}var Sr,xr=xt(()=>{"use strict";Sr="https://github.com/shriramsingh/prompttest-community"});Ne();import us from"fs";import rt from"path";import q from"chalk";Fe();var Mr=/^(\d+(?:\.\d+)*\.?|\-|\*)\s*/;function Rr(a){let e=a.trim(),t=e.match(/^"([^"]+)"$/);if(t)return t[1].trim();let s=e.match(/^'([^']+)'$/);return s?s[1].trim():e.replace(/^['"]+|['"]+$/g,"").trim()}var oe=`(?:"([^"]+)"|'([^']+)'|([^'"\\s][^'"]*?[^'"\\s]|\\S))`;function ce(a){for(let e of a)if(e!=null&&e.trim().length>0)return e.trim();return""}function Pt(a,e){let t=a;if(e)for(let[s,r]of Object.entries(e))t=t.replace(new RegExp(`\\$${s}\\b`,"g"),()=>r);return t=t.replace(/\$ENV\{([A-Z0-9_]+)\}/gi,(s,r)=>process.env[r]||""),t=t.replace(/\$RANDOM_EMAIL\b/g,()=>`test_${Date.now().toString().slice(-6)}@prompttest.io`),t=t.replace(/\$RANDOM_NAME\b/g,()=>`User_${Math.floor(1e3+Math.random()*9e3)}`),t=t.replace(/\$RANDOM_PHONE\b/g,()=>`98${Math.floor(1e7+Math.random()*9e7)}`),t=t.replace(/\$TIMESTAMP\b/g,()=>Date.now().toString()),t=t.replace(/\$UUID\b/g,()=>`${Date.now().toString(16)}-${Math.random().toString(16).substring(2,8)}`),t=t.replace(/\$RANDOM_UUID\b/g,()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,s=>{let r=Math.random()*16|0;return(s==="x"?r:r&3|8).toString(16)})),t=t.replace(/\$RANDOM_CARD\b/g,()=>{let s="4532";for(let r=0;r<12;r++)s+=Math.floor(Math.random()*10);return s}),t=t.replace(/\$DATE_OFFSET\((-?\d+)\)/g,(s,r)=>{let n=parseInt(r,10)||0;return new Date(Date.now()+n*864e5).toISOString().slice(0,10)}),t=t.replace(/\$RANDOM_INT\((\d+),\s*(\d+)\)/g,(s,r,n)=>{let i=parseInt(r,10),o=parseInt(n,10);return String(Math.floor(i+Math.random()*(o-i+1)))}),t}var te={TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:button\\s+|item\\s+)?${oe}$`,"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+)?${oe}$`,"i"),PROXIMITY_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+${oe}\\s+(?:next\\s+to|near|beside|inside|for)\\s+${oe}$`,"i"),CLEAR:new RegExp(`^(?:clear|empty)(?:\\s+(?:text\\s+(?:in|of)|field|input))?\\s+${oe}$`,"i"),LONG_PRESS:new RegExp(`^(?:long\\s+press|press\\s+and\\s+hold)\\s+(?:button\\s+|item\\s+)?${oe}$`,"i"),TYPE:new RegExp(`^(?:type|enter|input)\\s+${oe}\\s+(?:in|into)\\s+${oe}$`,"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+)?${oe}\\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+${oe}$`,"i"),SCROLL:/^(?:scroll|swipe)\s+(down|up)$/i,DIRECTIONAL_SWIPE:new RegExp(`^(?:swipe|scroll)\\s+(left|right|up|down)(?:\\s+on\\s+${oe})?$`,"i"),TOGGLE:new RegExp(`^(?:toggle|turn\\s+(on|off)|check|uncheck)\\s+(?:checkbox\\s+|switch\\s+)?${oe}$`,"i"),SELECT_DROPDOWN:new RegExp(`^(?:select|choose)\\s+${oe}\\s+from\\s+${oe}$`,"i"),LAUNCH_APP:new RegExp(`^(?:launch|open)\\s+(?:app\\s+)?${oe}$`,"i"),RESTART_APP:new RegExp(`^(?:restart|relaunch)\\s+(?:app\\s+)?${oe}$`,"i"),TERMINATE_APP:new RegExp(`^(?:terminate|close|force\\s*stop)\\s+(?:app\\s+)?(?:${oe})?$`,"i"),CLEAR_DATA:new RegExp(`^(?:clear\\s+(?:app\\s+)?data\\s+(?:for\\s+)?|reset\\s+app\\s+)${oe}$`,"i"),GRANT_PERMISSION:new RegExp(`^grant\\s+permissions?\\s+${oe}(?:\\s+(?:to|for)\\s+${oe})?$`,"i"),DEEP_LINK:new RegExp(`^(?:open\\s+deep\\s*link|navigate\\s+to\\s+url)\\s+${oe}$`,"i"),WAIT:/^wait\s+(\d+)\s*(?:s|sec|seconds|ms)?$/i,WAIT_UNTIL:new RegExp(`^(?:wait\\s+until|wait\\s+for)\\s+${oe}\\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+)?${oe}\\s+(?:is\\s+)?(visible|present|exists|appears?)|element\\s+${oe}\\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+)?${oe}$`,"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+${oe}$`,"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+${oe})?$`,"i"),API_REQUEST:new RegExp(`^(?:api|call\\s+api)\\s+(GET|POST|PUT|DELETE|PATCH)\\s+${oe}(?:\\s+with\\s+body\\s+${oe})?(?:\\s+store\\s+${oe}\\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+${oe}\\s+with\\s+subject\\s+${oe}(?:\\s+contains\\s+${oe})?$`,"i"),OPTIONAL_SUFFIX:/\s*\((?:optional|if\s+present|maybe)\)\s*$/i,FALLBACK_QUOTED_TAP:/^(?:tap|click)\s+.*?['"]([^'"]+)['"]/i},Vt={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+)\]/};function Mt(a){return(a||"").replace(/&#(\d+);/g,(e,t)=>{try{return String.fromCodePoint(parseInt(t,10))}catch{return""}}).replace(/&#x([0-9a-fA-F]+);/g,(e,t)=>{try{return String.fromCodePoint(parseInt(t,16))}catch{return""}}).replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&apos;/g,"'")}function ae(a){return Mt(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()}var Lr=new Map;function de(a,e){if(!a||!e)return!1;let t=ae(a),s=ae(e);if(t.length<=3){let r=Lr.get(t);if(!r){let n=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");r=new RegExp(`(?:^|\\b)${n}(?:\\b|$)`,"i"),Lr.set(t,r)}return r.test(s)}return s.includes(t)}var Or=["sign in","signin","login","log in","submit","save","continue","next","done","send","confirm","proceed","post","rate","enter","join","apply","update"],Fr=["sign in","signin","login","log in","submit","save","next","continue","done","like","fav","favorite","star","bookmark","add","post","confirm","apply","explore","search","filter"];function Ki(a,e){let t=ae(a),s=ae(e),r=t.length,n=s.length;if(r===0)return n;if(n===0)return r;let i=Array.from({length:n+1},(l,c)=>c),o=new Array(n+1);for(let l=1;l<=r;l++){o[0]=l;for(let p=1;p<=n;p++){let d=t[l-1]===s[p-1]?0:1;o[p]=Math.min(i[p]+1,o[p-1]+1,i[p-1]+d)}let c=i;i=o,o=c}return i[n]}function Xi(a,e){let t=ae(a),s=ae(e);if(t===s)return 1;if(!t||!s)return 0;let r=Math.max(t.length,s.length);return 1-Ki(t,s)/r}function Rt(a,e,t=3,s=.35){return Array.from(new Set(e.map(i=>i.trim()).filter(i=>i.length>0))).map(i=>({text:i,score:Xi(a,i)})).filter(i=>i.score>=s).sort((i,o)=>o.score-i.score).slice(0,t)}function _r(a){let e=a.match(Vt.BOUNDS_RECT);if(!e)return{x1:0,y1:0,x2:0,y2:0,width:0,height:0,centerX:0,centerY:0};let t=parseInt(e[1],10),s=parseInt(e[2],10),r=parseInt(e[3],10),n=parseInt(e[4],10);return{x1:t,y1:s,x2:r,y2:n,width:r-t,height:n-s,centerX:Math.round((t+r)/2),centerY:Math.round((s+n)/2)}}function Gi(a){let e=a.match(Vt.HIERARCHY_ROOT);if(!e)return{root:[],flat:[]};let t=e[1],s=new RegExp(Vt.NODE_TAG.source,"gi"),r=new RegExp(Vt.ATTR_KEY_VALUE.source,"g"),n=[],i=[],o=[],l;for(;(l=s.exec(t))!==null;){let c=l[0],p=l[1],d=l[2]==="/";if(c.startsWith("</node>")){o.pop();continue}let u={};r.lastIndex=0;let h;for(;(h=r.exec(p))!==null;)u[h[1]]=h[2];let g={index:parseInt(u.index||"0",10),text:Mt(u.text||""),resourceId:u["resource-id"]||"",className:u.class||"",packageName:u.package||"",contentDesc:Mt(u["content-desc"]||""),bounds:_r(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:[]};i.push(g),o.length===0?n.push(g):o[o.length-1].children.push(g),d||o.push(g)}return{root:n,flat:i}}function Qi(a){let e=[],t=[],s=[],r=a.length,n=0;for(;n<r;){let i=a.indexOf("<",n);if(i===-1)break;if(a.startsWith("<!--",i)){let x=a.indexOf("-->",i+4);n=x===-1?r:x+3;continue}if(a.startsWith("<?",i)){let x=a.indexOf("?>",i+2);n=x===-1?r:x+2;continue}if(a.startsWith("</",i)){let x=a.indexOf(">",i+2);if(x===-1)break;a.slice(i+2,x).trim()==="node"&&s.pop(),n=x+1;continue}let o=-1,l=null,c=!1;for(let x=i+1;x<r;x++){let S=a[x];if(l)S===l&&(l=null);else if(S==='"'||S==="'")l=S;else if(S===">"){o=x,a[x-1]==="/"&&(c=!0);break}}if(o===-1)break;let p=a.slice(i+1,c?o-1:o).trim();n=o+1;let d=0;for(;d<p.length&&!/\s/.test(p[d]);)d++;if(p.slice(0,d)!=="node")continue;let h={},g=d,b=p.length;for(;g<b;){for(;g<b&&/\s/.test(p[g]);)g++;if(g>=b)break;let x=g;for(;g<b&&p[g]!=="="&&!/\s/.test(p[g]);)g++;let S=p.slice(x,g);for(;g<b&&/\s/.test(p[g]);)g++;if(g>=b||p[g]!=="=")break;for(g++;g<b&&/\s/.test(p[g]);)g++;if(g>=b)break;let y=p[g];if(y==='"'||y==="'"){g++;let P=g;for(;g<b&&p[g]!==y;)g++;h[S]=p.slice(P,g),g<b&&g++}else{let P=g;for(;g<b&&!/\s/.test(p[g]);)g++;h[S]=p.slice(P,g)}}let T={index:parseInt(h.index||"0",10),text:Mt(h.text||""),resourceId:h["resource-id"]||"",className:h.class||"",packageName:h.package||"",contentDesc:Mt(h["content-desc"]||""),bounds:_r(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:[]};t.push(T),s.length===0?e.push(T):s[s.length-1].children.push(T),c||s.push(T)}return{root:e,flat:t}}function me(a,e){if((e||I.parser?.mode||"regex")==="streaming")try{return Qi(a)}catch{}return Gi(a)}function bo(a,e){return a.filter(t=>{if(e.clickable!==void 0&&t.clickable!==e.clickable||e.className&&!t.className.includes(e.className))return!1;if(e.text){if(typeof e.text=="string"){if(!t.text.toLowerCase().includes(e.text.toLowerCase()))return!1}else if(!e.text.test(t.text))return!1}if(e.contentDesc){if(typeof e.contentDesc=="string"){if(!t.contentDesc.toLowerCase().includes(e.contentDesc.toLowerCase()))return!1}else if(!e.contentDesc.test(t.contentDesc))return!1}if(e.resourceId){if(typeof e.resourceId=="string"){if(!t.resourceId.toLowerCase().includes(e.resourceId.toLowerCase()))return!1}else if(!e.resourceId.test(t.resourceId))return!1}return!0})}var Zi=[/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];function _e(a){if(a.resourceId&&/logbox/i.test(a.resourceId)||a.className&&/logbox/i.test(a.className))return!0;let e=`${a.text} ${a.contentDesc}`.trim();return!!Zi.some(t=>t.test(e))}function Le(a){return a.filter(e=>_e(e)?!1:(e.className.includes("EditText")||e.className.includes("TextInputEditText")||e.focused&&e.clickable)&&e.bounds.width>0&&e.bounds.height>0)}function xs(a){return a.className.includes("EditText")||a.className.includes("TextInputEditText")?!0:(a.children||[]).some(xs)}function zt(a,e){let t=e?.width||I.screen.fallbackWidth,s=e?.height||I.screen.fallbackHeight,r=s*.08,n=s*.88,i=a.filter(l=>!_e(l)&&(l.className.includes("RecyclerView")||l.className.includes("ListView")||l.className.includes("ScrollView"))),o=[];for(let l of i)for(let c of l.children){if(_e(c)||c.bounds.centerY<r||c.bounds.centerY>n)continue;(c.clickable||c.children.some(d=>!_e(d)&&d.clickable&&d.bounds.width>t*.1))&&c.bounds.width>t*.08&&c.bounds.height>s*.02&&!xs(c)&&o.push(c)}return o.length===0?a.filter(l=>{if(_e(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>t*.14&&l.bounds.height>s*.033&&l.bounds.centerY>r&&l.bounds.centerY<n&&!l.className.includes("Button")&&!l.className.includes("EditText")&&!xs(l)}):o}function Me(a){return a.filter(e=>{if(_e(e)||!e.clickable||e.bounds.width===0||e.className.includes("EditText"))return!1;let t=e.className.includes("Button")||e.className.includes("ImageButton"),s=Fr.some(r=>de(r,e.text)||de(r,e.contentDesc)||de(r,e.resourceId));return t||s})}function ot(a,e=I.screen.fallbackHeight,t){if(!a||a.length===0)return[];let s=a.find(h=>h.bounds.height>=e*.6&&h.bounds.y1<=e*.15),r=s?.bounds?.y1!==void 0&&s.bounds.y1>=0?s.bounds.y1:0,n=s?.bounds?.y2&&s.bounds.y2>r?s.bounds.y2:e,i=n-r,o=s?.bounds?.x2&&s.bounds.x2>0?s.bounds.x2-(s.bounds.x1||0):I.screen.fallbackWidth,l=r+i*.78,c=a.filter(h=>_e(h)||t&&h.packageName&&!h.packageName.includes(t)||!h.clickable||h.bounds.width<=0||h.bounds.height<=0||!(h.bounds.centerY>=l||h.bounds.y2>=n-20)?!1:h.bounds.width>=o*.12&&h.bounds.width<=o*.55);if(c.length<=1)return c;let p=[];for(let h of c){let g=p.find(b=>Math.abs(b[0].bounds.centerY-h.bounds.centerY)<=30||Math.abs(b[0].bounds.y2-h.bounds.y2)<=20);g||(g=[],p.push(g)),g.push(h)}let d=p.map(h=>{h.sort((b,T)=>b.bounds.centerX-T.bounds.centerX);let g=[];for(let b of h)g.some(T=>Math.abs(T.bounds.centerX-b.bounds.centerX)<o*.08)||g.push(b);return g}).filter(h=>h.length<2||h.length>6?!1:h[h.length-1].bounds.x2-h[0].bounds.x1>=o*.6);return d.length===0?c.slice(0,5):(d.sort((h,g)=>g[0].bounds.centerY-h[0].bounds.centerY),d[0].slice(0,5))}function Hr(a){let e=[/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(t=>{let s=`${t.text} ${t.contentDesc}`;return e.some(r=>r.test(s))})}var qi=[/(?:\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],en=[/(?:\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],tn=[/(?:\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];function Je(a,e,t=I.safety?.mode||"strict"){if(t==="disabled")return!1;let s=`${a.text} ${a.contentDesc} ${a.resourceId}`.trim();if((t==="moderate"?en:qi).some(i=>i.test(s)))return!0;let n=e||I.safety?.customBlacklist||[];for(let i of n)if(typeof i=="string"&&i.length>0){if(s.toLowerCase().includes(i.toLowerCase()))return!0}else if(i instanceof RegExp&&i.test(s))return!0;return!1}function Ve(a){let e=`${a.text} ${a.contentDesc} ${a.resourceId}`.trim();return tn.some(t=>t.test(e))}function Kt(a){for(let e of a){let t=`${e.text} ${e.contentDesc} ${e.resourceId}`;if(e.resourceId.toLowerCase().includes("logbox")||t.includes("Unhandled Promise Rejection")||t.includes("SyntaxError")||t.includes("Encountered two children with the same key")||t.includes("Warning:")||t.includes("Console Error")||t.includes("Console Warning")||t.includes("Console Info")||t.includes("TypeError:")||t.includes("ReferenceError:")||/Log \d+ of \d+/i.test(t)||t.includes("LogBox")&&(e.className.includes("Modal")||e.className.includes("ViewGroup"))){let r="error";/warning/i.test(t)?r="warning":/info/i.test(t)&&(r="info");let n=e.resourceId.includes("notification")||e.bounds.height>0&&e.bounds.height<250,i,o=[];for(let p of a){let d=`${p.text} ${p.contentDesc}`.trim();if(!i){let u=d.match(/\b([a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+)\b/);u&&(i=u[1])}/\b(?:at\s+|\()?[a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+/i.test(d)&&!o.includes(d)&&d.length<200&&o.push(d)}let l=a.find(p=>p.clickable&&p.bounds.width>0&&(/^(?:dismiss|close)$/i.test((p.text||"").trim())||/^(?:dismiss|close)$/i.test((p.contentDesc||"").trim())));if(!l&&!n){let d=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>I.screen.fallbackHeight*.8);d.length>0&&(d.sort((u,h)=>u.bounds.x1-h.bounds.x1),l=d[0])}!l&&n&&(l=a.find(p=>p.clickable&&p.bounds.y1>=e.bounds.y1-20&&p.bounds.y2<=e.bounds.y2+20&&p.bounds.x1>=e.bounds.x2-120));let c=e.text||e.contentDesc||"React Native LogBox Console Overlay";return{type:"logbox",severity:r,node:e,errorText:c,sourceLocation:i,stackTrace:o.length>0?o:void 0,dismissButton:l,isToast:n}}if(e.resourceId.includes("alertTitle")&&(t.includes("isn't responding")||t.includes("has stopped"))){let r=a.find(n=>n.text.toLowerCase().includes("close app")||n.text.toLowerCase().includes("ok"));return{type:"anr",severity:"error",node:e,errorText:e.text||"Application Not Responding (ANR)",dismissButton:r}}}return null}function Ot(a){return a.filter(e=>_e(e)?!1:(e.checkable||e.className.includes("CheckBox")||e.className.includes("Switch")||e.className.includes("RadioButton")||e.className.includes("SwitchCompat"))&&e.bounds.width>0&&e.bounds.height>0)}function at(a,e=I.screen.fallbackHeight,t=I.screen.fallbackWidth){let s=e*.22,r=/(?:menu|hamburger|drawer|navigation|\u2630|open[_\s-]*drawer)/i,n=a.find(i=>{if(!i.clickable||i.bounds.width===0||i.bounds.centerY>s)return!1;let o=`${i.contentDesc} ${i.text} ${i.resourceId}`;return r.test(o)});return n||a.find(i=>{if(!i.clickable||i.bounds.width===0||i.bounds.height===0||i.bounds.centerY>s||(i.text||i.contentDesc||"").trim().length>0)return!1;let l=i.bounds.x1<=t*.25,c=i.bounds.width<=250&&i.bounds.height<=250;return l&&c})||null}function Br(a,e=I.screen.fallbackHeight,t=I.screen.fallbackWidth){let s=e*.22;return a.filter(r=>{if(!r.clickable||r.bounds.width===0||r.bounds.height===0||r.bounds.centerY>s)return!1;let n=r.bounds.x2>=t*.75,i=r.bounds.width<=250&&r.bounds.height<=250;return n&&i})}function Ur(a){return a.filter(e=>{if(e.bounds.width===0||e.bounds.height===0)return!1;let t=e.className.includes("ScrollView")||e.className.includes("RecyclerView")||e.className.includes("ListView")||e.className.includes("NestedScrollView");return e.scrollable||t})}function jr(a,e=I.screen.fallbackWidth,t=I.screen.fallbackHeight,s=I.visual.overlapRatio){let r=[];for(let u of a)(u.text||u.clickable)&&u.bounds.width>0&&(u.bounds.x2>e+10&&u.bounds.x1<e&&r.push({type:"OVERFLOW",severity:"WARN",description:`Element "${u.text||u.contentDesc||u.className}" overflows right screen boundary by ${u.bounds.x2-e}px`,elements:[u.text||u.contentDesc||u.className]}),u.bounds.y2>t+20&&u.bounds.y1<t&&r.push({type:"OVERFLOW",severity:"WARN",description:`Element "${u.text||u.contentDesc||u.className}" overflows bottom screen boundary by ${u.bounds.y2-t}px`,elements:[u.text||u.contentDesc||u.className]}));let n=a.filter(u=>u.text&&u.text.trim().length>1&&u.bounds.width>0&&u.bounds.height>0),i=200,o=new Map;for(let u of n){let h=Math.floor(u.bounds.y1/i),g=Math.floor(u.bounds.y2/i);for(let b=h;b<=g;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 g=h+1;g<u.length;g++){let b=u[h],T=u[g];if(b===T||b.text===T.text)continue;let x=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(x))continue;l.add(x);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)),$=Math.max(0,Math.min(S.y2,y.y2)-Math.max(S.y1,y.y1)),R=P*$;if(R>0){let C=S.width*S.height,B=y.width*y.height,U=Math.min(C,B);U>0&&R/U>s&&r.push({type:"OVERLAP",severity:"WARN",description:`Visual text collision: "${b.text}" overlaps with "${T.text}" (${Math.round(R)}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&&r.push({type:"STALLED_LOADER",severity:"WARN",description:`Active loading indicator (${c[0].className}) detected on screen`,elements:c.map(u=>u.className)});let p=[],d=new Set;for(let u of r){let h=`${u.type}::${[...u.elements].sort().join("::")}`;d.has(h)||(d.add(h),p.push(u))}return p}function Ts(a,e,t){let s=e?.height||I.screen.fallbackHeight,r=e?.width||I.screen.fallbackWidth,n=ot(a,s,t);if(n.length>1&&n[0].bounds.centerY>=s*.75)return{paradigm:"bottom_tabs",roots:n};let i=s*.05,o=s*.22,l=a.filter(p=>{if(_e(p)||t&&p.packageName&&!p.packageName.includes(t))return!1;let d=p.className.includes("Tab")||p.resourceId.toLowerCase().includes("tab")||p.resourceId.toLowerCase().includes("header_tab")||/(?:tab\s*\d|\bphotos\b|\bvideos\b|\baudios\b)/i.test(`${p.text} ${p.contentDesc}`),u=p.bounds.centerY>=i&&p.bounds.centerY<=o,h=p.bounds.height>25&&p.bounds.height<=220;return(d||u)&&p.clickable&&p.bounds.width>50&&h});if(l.length>=2){let p=[];for(let u of l){let h=p.find(g=>Math.abs(g[0].bounds.centerY-u.bounds.centerY)<=20);h||(h=[],p.push(h)),h.push(u)}let d=p.find(u=>u.length>=2);if(d){d.sort((g,b)=>g.bounds.centerX-b.bounds.centerX);let u=[];for(let g of d)u.some(b=>Math.abs(b.bounds.centerX-g.bounds.centerX)<40)||u.push(g);if(!(u.length===2&&u[0].bounds.x1<=r*.25&&u[1].bounds.x2>=r*.75&&!u.some(g=>g.className.includes("Tab")||g.resourceId.toLowerCase().includes("tab")))&&u.length>=2)return{paradigm:"top_tabs",roots:u.slice(0,5)}}}let c=at(a,s,r);return c?{paradigm:"drawer",roots:[c]}:{paradigm:"single_hub",roots:[]}}function Yr(a,e=2){if(a.length<=e)return a;let t=[];for(let r of a){let n=t.find(i=>{let o=i[0],l=o.className===r.className,c=Math.abs(o.bounds.width-r.bounds.width)<=30,p=Math.abs(o.bounds.height-r.bounds.height)<=30,d=Math.abs(o.bounds.x1-r.bounds.x1)<=30;return l&&c&&p&&d});n||(n=[],t.push(n)),n.push(r)}let s=[];for(let r of t)s.push(...r.slice(0,e));return s}function He(a,e=I.screen.fallbackHeight,t=I.screen.fallbackWidth,s=!1){let r=e*.22,n=t*.28,i=/(?: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<=r;for(let l of a){if(_e(l)||!o(l))continue;let c=`${l.text||""} ${l.contentDesc||""} ${l.resourceId||""}`.trim();if(i.test(c)||c.length<=2&&/^[<‹«←x✕×\-]$/i.test(c))return l.clickable?l:a.find(d=>d.clickable&&d.bounds.x1<=l.bounds.x1&&d.bounds.x2>=l.bounds.x2&&d.bounds.y1<=l.bounds.y1&&d.bounds.y2>=l.bounds.y2&&d.bounds.width<=300&&d.bounds.height<=300)||l}for(let l of a){if(_e(l)||!o(l)||l.bounds.x1>n)continue;if(l.className.includes("ImageButton")||l.className.includes("ImageView")||l.className.includes("ViewGroup")&&l.bounds.width<=250&&l.bounds.height<=250){let p=`${l.text||""} ${l.contentDesc||""} ${l.resourceId||""}`.toLowerCase();if(p.includes("back")||p.includes("up")||p.includes("arrow")||p.includes("chevron")||p.includes("nav")||p.includes("close")||p.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(_e(c)||!o(c)||!c.clickable)return!1;let p=c.bounds.x1<=n,d=c.bounds.width<=250&&c.bounds.height<=250;return p&&d&&c.bounds.width<t*.4});if(l)return l}return null}import et from"fs";import ct from"path";import Gt from"fs";import Xt from"path";import sn from"crypto";import rn from"os";var Wr="prompttest_ed25519_offline_sig_k9284j2f91kd";function nn(){let a=Xt.resolve(process.cwd(),".prompttest");if(Gt.existsSync(a))return Xt.join(a,"license.json");let e=Xt.join(rn.homedir(),".prompttest");if(!Gt.existsSync(e))try{Gt.mkdirSync(e,{recursive:!0})}catch{return Xt.resolve(process.cwd(),".prompttest_license.json")}return Xt.join(e,"license.json")}function on(a,e=Wr){if(!a||typeof a!="string")return{valid:!1,error:"License key is empty."};let t=a.trim();if(!t.startsWith("PTPRO-")||!t.includes("."))return{valid:!1,error:"Invalid license key format. Expected PTPRO-<payload>.<signature>"};let r=t.slice(6).split(".");if(r.length!==2)return{valid:!1,error:"Corrupted license key structure."};let[n,i]=r,o=sn.createHmac("sha256",e);o.update(n);let l=o.digest("hex").slice(0,32);if(i!==l)return{valid:!1,error:"Invalid license signature or altered key."};try{let c=Buffer.from(n,"base64url").toString("utf-8"),p=JSON.parse(c);return p.expiresAt>0&&Date.now()>p.expiresAt?{valid:!1,payload:p,error:`License expired on ${new Date(p.expiresAt).toLocaleDateString()}.`}:{valid:!0,payload:p}}catch{return{valid:!1,error:"Failed to decode license payload."}}}function Ps(a=Wr){try{let e=nn();if(!Gt.existsSync(e))return{active:!1,tier:"free",whiteLabel:!1,features:[]};let t=Gt.readFileSync(e,"utf-8"),s=JSON.parse(t),r=on(s.key,a);return!r.valid||!r.payload?{active:!1,tier:"free",whiteLabel:!1,features:[]}:{active:!0,tier:r.payload.tier,holder:r.payload.holder,expiresAt:r.payload.expiresAt>0?new Date(r.payload.expiresAt).toISOString():"Perpetual",whiteLabel:r.payload.whiteLabel,features:r.payload.features||[]}}catch{return{active:!1,tier:"free",whiteLabel:!1,features:[]}}}var lt=class{outputDir;steps=[];errorsDetected=[];startTime=new Date;packageName="Unknown";deviceId="Unknown";pendingWrites=[];formats=["json","md","junit","html"];videoPath;embedScreenshots=!1;performanceMetrics;screenshotBuffers=new Map;stepListeners=[];completeListeners=[];errorListeners=[];setPerformanceMetrics(e){this.performanceMetrics=e}constructor(e,t){typeof e=="object"&&e!==null?(this.outputDir=e.outputDir||ct.resolve(process.cwd(),"output"),e.formats&&e.formats.length>0&&(this.formats=e.formats),this.embedScreenshots=!!e.embedScreenshots):(this.outputDir=e||ct.resolve(process.cwd(),"output"),t&&t.length>0&&(this.formats=t)),et.existsSync(this.outputDir)||et.mkdirSync(this.outputDir,{recursive:!0})}setEmbedScreenshots(e){this.embedScreenshots=e}onStep(e){return this.stepListeners.push(e),()=>{let t=this.stepListeners.indexOf(e);t!==-1&&this.stepListeners.splice(t,1)}}onComplete(e){return this.completeListeners.push(e),()=>{let t=this.completeListeners.indexOf(e);t!==-1&&this.completeListeners.splice(t,1)}}onError(e){return this.errorListeners.push(e),()=>{let t=this.errorListeners.indexOf(e);t!==-1&&this.errorListeners.splice(t,1)}}setFormats(e){this.formats=e}setVideoPath(e){this.videoPath=e}init(e,t){this.packageName=e,this.deviceId=t,this.startTime=new Date,this.steps=[],this.errorsDetected=[],this.pendingWrites=[],this.videoPath=void 0,this.screenshotBuffers.clear()}clearScreenshotBuffers(){this.screenshotBuffers.clear()}logStep(e,t,s,r,n,i,o,l,c){let p=this.steps.length+1,d;if(i){this.embedScreenshots&&this.screenshotBuffers.set(p,i);let g=`step_${p}_${e.toLowerCase().replace(/[^a-z0-9]/g,"_")}.png`,b=ct.join(this.outputDir,g),T=et.promises.writeFile(b,i).catch(x=>{console.error(`Warning: Failed to write screenshot ${g}:`,x)}).finally(()=>{let x=this.pendingWrites.indexOf(T);x!==-1&&this.pendingWrites.splice(x,1)});this.pendingWrites.push(T),d=g}let u=typeof o?.videoOffsetMs=="number"?o.videoOffsetMs:void 0,h={stepIndex:p,timestamp:new Date().toISOString(),phase:e,description:t,action:s,target:n,status:r,screenshotPath:d,durationMs:l,triageBundle:c,videoOffsetMs:u,details:o};this.steps.push(h);for(let g of this.stepListeners)try{g(h)}catch{}return h}logError(e){this.errorsDetected.push(e);for(let t of this.errorListeners)try{t(e)}catch{}}async generateReport(e){this.pendingWrites.length>0&&await Promise.all(this.pendingWrites);let t=new Date,s=Math.round((t.getTime()-this.startTime.getTime())/1e3),r=[];for(let o of this.steps){let l=o.details;l?.healedFrom&&(l?.healedTo||o.target)&&r.push({original:String(l.healedFrom),healed:String(l.healedTo||o.target),stepNumber:o.stepIndex})}let n={packageName:this.packageName,deviceId:this.deviceId,startTime:this.startTime.toISOString(),endTime:t.toISOString(),durationSeconds:s,totalSteps:this.steps.length,passedSteps:this.steps.filter(o=>o.status==="PASS").length,failedSteps:this.steps.filter(o=>o.status==="FAIL").length,skippedSteps:this.steps.filter(o=>o.status==="SKIPPED").length,warnSteps:this.steps.filter(o=>o.status==="WARN").length,errorsDetected:this.errorsDetected,videoPath:this.videoPath,performanceMetrics:this.performanceMetrics,healsApplied:r.length>0?r:void 0,steps:this.steps},i=e||"prompttest";if(this.formats.includes("json")){let o=ct.join(this.outputDir,`${i}-results.json`);await et.promises.writeFile(o,JSON.stringify(n,null,2),"utf-8")}if(this.formats.includes("md")){let o=ct.join(this.outputDir,`${i}-report.md`),l=this.formatMarkdownReport(n);await et.promises.writeFile(o,l,"utf-8")}if(this.formats.includes("junit")){let o=ct.join(this.outputDir,`${i}-junit.xml`),l=this.formatJunitXml(n,i);await et.promises.writeFile(o,l,"utf-8")}if(this.formats.includes("html")){let o=ct.join(this.outputDir,`${i}-report.html`),l=this.formatHtmlReport(n,i);await et.promises.writeFile(o,l,"utf-8")}for(let o of this.completeListeners)try{o(n)}catch{}return this.clearScreenshotBuffers(),n}escapeXml(e){return(e||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}escapeHtml(e){return(e||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}formatJunitXml(e,t){let s=e.durationSeconds,r=e.failedSteps+(e.errorsDetected.length>0&&e.failedSteps===0?1:0),n=['<?xml version="1.0" encoding="UTF-8"?>',`<testsuites name="PromptTest" tests="${e.totalSteps}" failures="${r}" skipped="${e.skippedSteps}" time="${s}">`,` <testsuite name="${this.escapeXml(t)}" tests="${e.totalSteps}" failures="${r}" skipped="${e.skippedSteps}" time="${s}">`];for(let i of e.steps){let o=`Step ${i.stepIndex}: ${this.escapeXml(i.action)} - ${this.escapeXml(i.description)}`,l=i.durationMs!==void 0?(i.durationMs/1e3).toFixed(3):"1.000";n.push(` <testcase classname="${this.escapeXml(t)}" name="${o}" time="${l}">`),i.status==="FAIL"?n.push(` <failure message="${this.escapeXml(i.description)}">${this.escapeXml(i.description)}</failure>`):i.status==="SKIPPED"&&n.push(` <skipped message="${this.escapeXml(i.description)}"/>`),n.push(" </testcase>")}return e.failedSteps===0&&e.errorsDetected.length>0&&n.push(` <testcase classname="${this.escapeXml(t)}" name="Error Sentinel Crash Detection" time="0.000">`,` <failure message="${this.escapeXml(e.errorsDetected[0])}">${this.escapeXml(e.errorsDetected.join(`
38
+ `))}</failure>`," </testcase>"),n.push(" </testsuite>"),n.push("</testsuites>"),n.join(`
39
+ `)}formatMarkdownReport(e){let r=["# \u{1F9EA} PromptTest Autonomous Mobile QA Audit Report","",`**Status:** ${e.failedSteps===0&&e.errorsDetected.length===0?"\u2705 ALL PASSED":"\u26A0\uFE0F ISSUES DETECTED"} `,`**Target Package:** \`${e.packageName}\` `,`**Device:** \`${e.deviceId}\` `,`**Execution Time:** ${e.durationSeconds}s (${e.startTime} to ${e.endTime}) `];if(e.videoPath&&r.push(`**Screen Recording:** [Download Video](./${e.videoPath}) `),r.push("","---","","## \u{1F4CA} Executive Summary","","| Metric | Count |","|---|---|",`| **Total Steps** | ${e.totalSteps} |`,`| **Passed** | \u{1F7E2} ${e.passedSteps} |`,`| **Warnings** | \u{1F7E1} ${e.warnSteps} |`,`| **Failures** | \u{1F534} ${e.failedSteps} |`,`| **Skipped** | \u26AA ${e.skippedSteps} |`,`| **Errors Detected** | ${e.errorsDetected.length} |`,""),e.errorsDetected.length>0){r.push("## \u26A0\uFE0F Errors & Sentinels Flagged");for(let o of e.errorsDetected)r.push(`- \u{1F534} \`${o}\``);r.push("")}r.push("## \u{1F50D} Detailed Execution Timeline"),r.push(""),r.push("| Step | Phase | Action | Target / Details | Status | Duration | Screenshot / Triage |"),r.push("|---|---|---|---|---|---|---|");let n=o=>(o||"").replace(/\|/g,"\\|").replace(/\r?\n/g," ");for(let o of e.steps){let l="\u2014";o.triageBundle?l=`[\u{1F50D} Triage Bundle](./${o.triageBundle})`:o.screenshotPath&&(l=`[\u{1F4F8} Screenshot](./${o.screenshotPath})`);let c=o.durationMs!==void 0?`${o.durationMs}ms`:"\u2014",p=o.status==="PASS"?"\u{1F7E2} PASS":o.status==="WARN"?"\u{1F7E1} WARN":o.status==="SKIPPED"?"\u26AA SKIPPED":"\u{1F534} FAIL";r.push(`| ${o.stepIndex} | ${n(o.phase)} | ${n(o.action)} | ${n(o.description)} | ${p} | ${c} | ${l} |`)}r.push(""),e.performanceMetrics&&(r.push("## \u26A1 Mobile Performance & FPS Profiler (PF-01)"),r.push(""),r.push(`- **60 FPS Compliance:** ${e.performanceMetrics.fpsCompliance}`),r.push(`- **Total Frames Rendered:** ${e.performanceMetrics.frames.totalFrames.toLocaleString()}`),r.push(`- **Janky Frames:** ${e.performanceMetrics.frames.jankyFrames} (${e.performanceMetrics.frames.jankPercentage}%)`),e.performanceMetrics.frames.p95Ms!==void 0&&r.push(`- **95th Percentile Frame Time:** ${e.performanceMetrics.frames.p95Ms}ms`),r.push(`- **Peak Memory (PSS):** ${Math.round(e.performanceMetrics.memory.peakPssKb/1024)} MB`),r.push(""));let i=Ps();return r.push("---"),i.whiteLabel?r.push("*Generated autonomously by Mobile QA Brain.*"):r.push("*Generated autonomously by [PromptTest](https://prompttest.dev) Mobile QA Brain.*"),r.join(`
40
+ `)}resolveScreenshotData(e){if(e.screenshotPath){if(this.embedScreenshots){if(this.screenshotBuffers.has(e.stepIndex))return`data:image/png;base64,${this.screenshotBuffers.get(e.stepIndex).toString("base64")}`;let t=ct.join(this.outputDir,e.screenshotPath);if(et.existsSync(t))try{return`data:image/png;base64,${et.readFileSync(t).toString("base64")}`}catch{}}return`./${e.screenshotPath}`}}formatHtmlReport(e,t){return Es(e,{suiteName:t,embedScreenshots:this.embedScreenshots,screenshotResolver:s=>this.resolveScreenshotData(s)})}};function Es(a,e){let t=e?.suiteName||"PromptTest Suite",s=a.failedSteps===0&&a.errorsDetected.length===0,r=s?"ALL PASSED":"ISSUES DETECTED",n=s?"#10b981":"#ef4444",i=a.totalSteps,o=i>0?Math.round(a.passedSteps/i*100):100,l=Ps(),c=g=>(g||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;"),p=[],d=a.steps.map(g=>{let b=g.status.toLowerCase(),T=g.durationMs!==void 0?`${g.durationMs}ms`:"\u2014",x=e?.screenshotResolver?e.screenshotResolver(g):g.screenshotPath?`./${g.screenshotPath}`:void 0;x&&p.push({stepIndex:g.stepIndex,action:g.action,description:g.description,status:g.status,src:x,durationMs:g.durationMs});let S="\u2014";if(g.triageBundle){let B=c(g.triageBundle),U=g.triageBundle.endsWith(".zip"),Z=U?B:`${B}.zip`,W=U?B.replace(/\.zip$/,""):B;S=`
41
41
  <div class="triage-bundle-links">
42
- <a class="btn-triage btn-download-zip" href="./${M}" download title="Download Diagnostic Triage ZIP Package">\u{1F4E6} Download ZIP</a>
43
- <a class="btn-triage" href="./${J}" target="_blank" title="Browse Failure Diagnostic Directory">\u{1F50D} Triage Folder</a>
42
+ <a class="btn-triage btn-download-zip" href="./${Z}" download title="Download Diagnostic Triage ZIP Package">\u{1F4E6} Download ZIP</a>
43
+ <a class="btn-triage" href="./${W}" target="_blank" title="Browse Failure Diagnostic Directory">\u{1F50D} Triage Folder</a>
44
44
  <div class="triage-files">
45
- <a href="./${J}/screenshot.png" target="_blank" class="mini-link">\u{1F4F8} Shot</a>
46
- <a href="./${J}/hierarchy.xml" target="_blank" class="mini-link">\u{1F4C4} XML</a>
47
- <a href="./${J}/logcat_tail.log" target="_blank" class="mini-link">\u{1F4DC} Logcat</a>
48
- <a href="./${J}/match_scores.json" target="_blank" class="mini-link">\u{1F3AF} Scores</a>
45
+ <a href="./${W}/screenshot.png" target="_blank" class="mini-link">\u{1F4F8} Shot</a>
46
+ <a href="./${W}/hierarchy.xml" target="_blank" class="mini-link">\u{1F4C4} XML</a>
47
+ <a href="./${W}/logcat_tail.log" target="_blank" class="mini-link">\u{1F4DC} Logcat</a>
48
+ <a href="./${W}/match_scores.json" target="_blank" class="mini-link">\u{1F3AF} Scores</a>
49
49
  </div>
50
- </div>`}else v&&(T=`
50
+ </div>`}else x&&(S=`
51
51
  <div class="screenshot-cell">
52
- <img src="${v}" class="thumb" alt="Step ${u.stepIndex}" onclick="openLightboxByStep(${u.stepIndex})" title="Click to view in full-screen Lightbox"/>
53
- <button class="btn-zoom" onclick="openLightboxByStep(${u.stepIndex})" title="Enlarge Screenshot">\u{1F50D}</button>
54
- </div>`);let S="";if(u.details&&(typeof u.details.diffPercent=="number"||u.details.diffPath||u.details.baselinePath)){let k=typeof u.details.diffPercent=="number"?`${(u.details.diffPercent*100).toFixed(2)}%`:"N/A",j=typeof u.details.threshold=="number"?`${(u.details.threshold*100).toFixed(2)}%`:"2.00%";S=`
52
+ <img src="${x}" class="thumb" alt="Step ${g.stepIndex}" onclick="openLightboxByStep(${g.stepIndex})" title="Click to view in full-screen Lightbox"/>
53
+ <button class="btn-zoom" onclick="openLightboxByStep(${g.stepIndex})" title="Enlarge Screenshot">\u{1F50D}</button>
54
+ </div>`);let y="";if(g.details&&(typeof g.details.diffPercent=="number"||g.details.diffPath||g.details.baselinePath)){let B=typeof g.details.diffPercent=="number"?`${(g.details.diffPercent*100).toFixed(2)}%`:"N/A",U=typeof g.details.threshold=="number"?`${(g.details.threshold*100).toFixed(2)}%`:"2.00%";y=`
55
55
  <div class="diff-viewer-card">
56
56
  <div class="diff-header">
57
- <span class="badge diff-badge">Visual Diff: ${c(k)}</span>
58
- <span class="diff-meta">Threshold: ${c(j)}</span>
57
+ <span class="badge diff-badge">Visual Diff: ${c(B)}</span>
58
+ <span class="diff-meta">Threshold: ${c(U)}</span>
59
59
  </div>
60
60
  <div class="diff-grid">
61
- ${u.details.baselinePath?`<div class="diff-pane"><div class="pane-label">Baseline</div><img src="./${c(String(u.details.baselinePath))}" class="diff-thumb" onclick="openLightboxCustom('./${c(String(u.details.baselinePath))}', 'Baseline Screenshot')"/></div>`:""}
62
- ${v?`<div class="diff-pane"><div class="pane-label">Actual Screen</div><img src="${v}" class="diff-thumb" onclick="openLightboxCustom('${v}', 'Actual Screenshot')"/></div>`:""}
63
- ${u.details.diffPath?`<div class="diff-pane"><div class="pane-label">Pixel Diff Overlay</div><img src="./${c(String(u.details.diffPath))}" class="diff-thumb" onclick="openLightboxCustom('./${c(String(u.details.diffPath))}', 'Visual Difference Overlay')"/></div>`:""}
61
+ ${g.details.baselinePath?`<div class="diff-pane"><div class="pane-label">Baseline</div><img src="./${c(String(g.details.baselinePath))}" class="diff-thumb" onclick="openLightboxCustom('./${c(String(g.details.baselinePath))}', 'Baseline Screenshot')"/></div>`:""}
62
+ ${x?`<div class="diff-pane"><div class="pane-label">Actual Screen</div><img src="${x}" class="diff-thumb" onclick="openLightboxCustom('${x}', 'Actual Screenshot')"/></div>`:""}
63
+ ${g.details.diffPath?`<div class="diff-pane"><div class="pane-label">Pixel Diff Overlay</div><img src="./${c(String(g.details.diffPath))}" class="diff-thumb" onclick="openLightboxCustom('./${c(String(g.details.diffPath))}', 'Visual Difference Overlay')"/></div>`:""}
64
64
  </div>
65
- </div>`}let b="";u.details&&Object.keys(u.details).length>0&&(b=`
65
+ </div>`}let P="";g.details&&Object.keys(g.details).length>0&&(P=`
66
66
  <details class="step-details">
67
67
  <summary>Diagnostics &amp; Context</summary>
68
- <pre>${c(JSON.stringify(u.details,null,2))}</pre>
69
- </details>`);let E=`${u.stepIndex} ${u.phase} ${u.action} ${u.description} ${u.target||""}`.toLowerCase(),D=typeof u.videoOffsetMs=="number"?u.videoOffsetMs:typeof u.details?.videoOffsetMs=="number"?u.details.videoOffsetMs:void 0,B=a.videoPath&&D!==void 0?`<button type="button" class="btn-video-jump" onclick="seekVideoTo(${D/1e3})" title="Jump video to ${(D/1e3).toFixed(1)}s">\u25B6 ${(D/1e3).toFixed(1)}s</button>`:"";return`
70
- <tr class="step-row status-${m}" data-status="${m}" data-search="${c(E)}">
71
- <td class="col-num">${u.stepIndex}</td>
72
- <td><span class="badge phase-badge">${c(u.phase)}</span></td>
68
+ <pre>${c(JSON.stringify(g.details,null,2))}</pre>
69
+ </details>`);let $=`${g.stepIndex} ${g.phase} ${g.action} ${g.description} ${g.target||""}`.toLowerCase(),R=typeof g.videoOffsetMs=="number"?g.videoOffsetMs:typeof g.details?.videoOffsetMs=="number"?g.details.videoOffsetMs:void 0,C=a.videoPath&&R!==void 0?`<button type="button" class="btn-video-jump" onclick="seekVideoTo(${R/1e3})" title="Jump video to ${(R/1e3).toFixed(1)}s">\u25B6 ${(R/1e3).toFixed(1)}s</button>`:"";return`
70
+ <tr class="step-row status-${b}" data-status="${b}" data-search="${c($)}">
71
+ <td class="col-num">${g.stepIndex}</td>
72
+ <td><span class="badge phase-badge">${c(g.phase)}</span></td>
73
73
  <td>
74
- <span class="badge action-badge">${c(u.action)}</span>
75
- ${B}
74
+ <span class="badge action-badge">${c(g.action)}</span>
75
+ ${C}
76
76
  </td>
77
77
  <td>
78
- <div class="desc-text">${c(u.description)}</div>
79
- ${S}
80
- ${b}
78
+ <div class="desc-text">${c(g.description)}</div>
79
+ ${y}
80
+ ${P}
81
81
  </td>
82
- <td><code>${c(u.target||"\u2014")}</code></td>
83
- <td><span class="badge status-badge status-${m}">${u.status}</span></td>
84
- <td class="col-duration">${y}</td>
85
- <td class="col-artifact">${T}</td>
82
+ <td><code>${c(g.target||"\u2014")}</code></td>
83
+ <td><span class="badge status-badge status-${b}">${g.status}</span></td>
84
+ <td class="col-duration">${T}</td>
85
+ <td class="col-artifact">${S}</td>
86
86
  </tr>`}).join(`
87
- `),d=a.videoPath?`
87
+ `),u=a.videoPath?`
88
88
  <section class="card video-card">
89
89
  <div class="video-card-header">
90
90
  <h3>\u{1F3AC} Session Screen Recording</h3>
@@ -101,7 +101,7 @@ ${(await this.execAdb(["shell","wm","density"],e,8e3,0).catch(()=>({stdout:""}))
101
101
  <section class="card error-card">
102
102
  <h3>\u26A0\uFE0F Critical Flagged Errors (${a.errorsDetected.length})</h3>
103
103
  <ul class="error-list">
104
- ${a.errorsDetected.map(u=>`<li>\u{1F534} <code>${c(u)}</code></li>`).join(`
104
+ ${a.errorsDetected.map(g=>`<li>\u{1F534} <code>${c(g)}</code></li>`).join(`
105
105
  `)}
106
106
  </ul>
107
107
  </section>`:"";return`<!DOCTYPE html>
@@ -152,7 +152,7 @@ ${(await this.execAdb(["shell","wm","density"],e,8e3,0).catch(()=>({stdout:""}))
152
152
  padding: 8px 18px;
153
153
  border-radius: 9999px;
154
154
  color: #fff;
155
- background: ${r};
155
+ background: ${n};
156
156
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
157
157
  }
158
158
 
@@ -561,14 +561,14 @@ ${(await this.execAdb(["shell","wm","density"],e,8e3,0).catch(()=>({stdout:""}))
561
561
  <strong>Duration:</strong> ${a.durationSeconds}s (${a.startTime.substring(11,19)} to ${a.endTime.substring(11,19)})
562
562
  </div>
563
563
  </div>
564
- <div class="status-pill">${i}</div>
564
+ <div class="status-pill">${r}</div>
565
565
  </header>
566
566
 
567
567
  <div class="dashboard-grid">
568
568
  <div class="gauge-card">
569
569
  <svg viewBox="0 0 36 36" class="circular-chart">
570
570
  <path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"/>
571
- <path class="circle" stroke-dasharray="${o}, 100" stroke="${r}" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"/>
571
+ <path class="circle" stroke-dasharray="${o}, 100" stroke="${n}" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831"/>
572
572
  <text x="18" y="20.35" class="percentage">${o}%</text>
573
573
  </svg>
574
574
  <div class="chart-label">Pass Rate</div>
@@ -595,7 +595,7 @@ ${(await this.execAdb(["shell","wm","density"],e,8e3,0).catch(()=>({stdout:""}))
595
595
  </div>
596
596
  </div>
597
597
 
598
- ${d}
598
+ ${u}
599
599
  ${h}
600
600
 
601
601
  <section class="card">
@@ -626,13 +626,43 @@ ${(await this.execAdb(["shell","wm","density"],e,8e3,0).catch(()=>({stdout:""}))
626
626
  </tr>
627
627
  </thead>
628
628
  <tbody id="steps-tbody">
629
- ${p}
629
+ ${d}
630
630
  </tbody>
631
631
  </table>
632
- </section>
632
+ ${a.performanceMetrics?`
633
+ <section class="card" style="margin-top: 24px; border: 1px solid #1e293b; background: #0f172a; border-radius: 12px; padding: 20px;">
634
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
635
+ <h2 style="font-size: 1.1rem; font-weight: 600; color: #f8fafc; margin: 0; display: flex; align-items: center; gap: 8px;">
636
+ \u26A1 Mobile Performance &amp; 60 FPS Profiler (PF-01)
637
+ </h2>
638
+ <span class="badge" style="background: ${a.performanceMetrics.fpsCompliance==="PASS"?"#065f46":a.performanceMetrics.fpsCompliance==="WARN"?"#92400e":"#991b1b"}; color: #fff; padding: 4px 10px; border-radius: 9999px; font-weight: 600; font-size: 0.75rem;">
639
+ ${a.performanceMetrics.fpsCompliance==="PASS"?"\u2714 60 FPS COMPLIANT":a.performanceMetrics.fpsCompliance==="WARN"?"\u26A0 JANK WARNING":"\u2716 SEVERE JANK"}
640
+ </span>
641
+ </div>
642
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px;">
643
+ <div style="background: #1e293b; padding: 12px; border-radius: 8px;">
644
+ <div style="font-size: 0.75rem; color: #94a3b8; text-transform: uppercase;">Total Frames</div>
645
+ <div style="font-size: 1.25rem; font-weight: 700; color: #f8fafc;">${a.performanceMetrics.frames.totalFrames.toLocaleString()}</div>
646
+ </div>
647
+ <div style="background: #1e293b; padding: 12px; border-radius: 8px;">
648
+ <div style="font-size: 0.75rem; color: #94a3b8; text-transform: uppercase;">Janky Frames</div>
649
+ <div style="font-size: 1.25rem; font-weight: 700; color: ${a.performanceMetrics.frames.jankPercentage>5?"#f87171":"#34d399"};">
650
+ ${a.performanceMetrics.frames.jankyFrames} (${a.performanceMetrics.frames.jankPercentage}%)
651
+ </div>
652
+ </div>
653
+ <div style="background: #1e293b; padding: 12px; border-radius: 8px;">
654
+ <div style="font-size: 0.75rem; color: #94a3b8; text-transform: uppercase;">95th Percentile</div>
655
+ <div style="font-size: 1.25rem; font-weight: 700; color: #f8fafc;">${a.performanceMetrics.frames.p95Ms!==void 0?a.performanceMetrics.frames.p95Ms+" ms":"\u2014"}</div>
656
+ </div>
657
+ <div style="background: #1e293b; padding: 12px; border-radius: 8px;">
658
+ <div style="font-size: 0.75rem; color: #94a3b8; text-transform: uppercase;">Peak PSS Memory</div>
659
+ <div style="font-size: 1.25rem; font-weight: 700; color: #38bdf8;">${Math.round(a.performanceMetrics.memory.peakPssKb/1024)} MB</div>
660
+ </div>
661
+ </div>
662
+ </section>`:""}
633
663
 
634
664
  <footer>
635
- PromptTest Autonomous Mobile QA Brain &bull; Rendered at ${new Date().toISOString()}
665
+ ${l.whiteLabel?`<span>Autonomous QA Execution Report &bull; Rendered at ${new Date().toISOString()}</span>`:`<span>PromptTest Autonomous Mobile QA Brain &bull; Rendered at ${new Date().toISOString()} &bull; <a href="https://shriramsingh.github.io/prompttest-studio-site/" target="_blank" style="color: #60a5fa; text-decoration: none; font-weight: 500;">\u26A1 Powered by PromptTest Studio</a></span>`}
636
666
  </footer>
637
667
  </div>
638
668
 
@@ -660,7 +690,7 @@ ${(await this.execAdb(["shell","wm","density"],e,8e3,0).catch(()=>({stdout:""}))
660
690
  </div>
661
691
 
662
692
  <script>
663
- const SCREENSHOTS = ${JSON.stringify(l)};
693
+ const SCREENSHOTS = ${JSON.stringify(p)};
664
694
  let currentLightboxIdx = -1;
665
695
  let currentFilter = 'all';
666
696
 
@@ -792,20 +822,20 @@ ${(await this.execAdb(["shell","wm","density"],e,8e3,0).catch(()=>({stdout:""}))
792
822
  }
793
823
  </script>
794
824
  </body>
795
- </html>`}var nt=new At;import Ne from"fs";import qe from"path";import{fileURLToPath as Mn}from"url";var $t={en:{bell:["bell","notification","notifications","alert","alerts","reminders","notice","badge"],notification:["notification","notifications","bell","alert","alerts","reminders","badge"],close:["close","dismiss","cancel","clear","cross","exit","\u2715","\xD7","x"],like:["like","favorite","heart","thumbs up","thumbs-up","fav","upvote"],dislike:["dislike","thumbs down","thumbs-down","downvote"],search:["search","find","query","magnifier","explore","lookup"],settings:["settings","preferences","gear","options","setup","config"],profile:["profile","account","user","my profile","avatar","me","personal"],trash:["trash","delete","remove","bin","discard"],delete:["delete","trash","remove","bin","discard"],share:["share","export","send to","forward"],back:["back","navigate up","previous","arrow back","chevron left","return"],menu:["menu","hamburger","drawer","sidebar","options","navigation","\u2630"],add:["add","new","create","plus","+","insert"],edit:["edit","pencil","modify","update","rename","change"],calendar:["calendar","schedule","date","events","timetable"],refresh:["refresh","reload","sync","update"],info:["info","information","details","help","about"],filter:["filter","filters","sort","refine","funnel"],check:["check","done","tick","mark","select","\u2713","\u2714"],lock:["lock","security","privacy","padlock"],camera:["camera","photo","picture","capture","snapshot"],cart:["cart","shopping cart","bag","basket","checkout"],email:["email","username","user name","e-mail","mail","login ID","user id"],username:["username","user name","email","user id","handle"],password:["password","passcode","pin","pwd","secret"],phone:["phone","mobile","contact","telephone","cell","phone number"],name:["name","full name","first name","last name"],otp:["otp","verification code","code","pin"],submit:["submit","save","continue","confirm","proceed","done","apply","ok"],save:["save","submit","done","apply","store","update"],cancel:["cancel","abort","discard","close","dismiss"],confirm:["confirm","yes","proceed","ok","agree","accept"],login:["sign in","signin","log in","login","enter"],signin:["sign in","signin","log in","login","enter"],logout:["sign out","signout","log out","logout","exit"],signout:["sign out","signout","log out","logout","exit"]},es:{bell:["campana","notificaci\xF3n","notificaciones","alerta","alertas","aviso","avisos"],notification:["notificaci\xF3n","notificaciones","campana","alerta","alertas","aviso","avisos","notification","notifications"],close:["cerrar","cancelar","quitar","salir","descartar","\u2715","\xD7","x","close"],like:["me gusta","favorito","favoritos","coraz\xF3n","like"],dislike:["no me gusta","desaprobar"],search:["buscar","b\xFAsqueda","lupa","explorar","encontrar","search"],settings:["ajustes","configuraci\xF3n","preferencias","opciones","settings"],profile:["perfil","cuenta","usuario","mi perfil","avatar","profile"],trash:["papelera","eliminar","borrar","descartar","quitar"],delete:["eliminar","borrar","quitar","papelera","descartar","delete"],share:["compartir","enviar","difundir","share"],back:["atr\xE1s","volver","regresar","anterior","flecha atr\xE1s","back"],menu:["men\xFA","opciones","navegaci\xF3n","\u2630","menu"],add:["a\xF1adir","agregar","nuevo","crear","m\xE1s","+","add"],edit:["editar","modificar","cambiar","actualizar","edit"],calendar:["calendario","fecha","eventos","horario","calendar"],refresh:["actualizar","recargar","sincronizar","refresh"],info:["informaci\xF3n","detalles","ayuda","acerca de","info"],filter:["filtrar","filtros","ordenar","filter"],check:["listo","hecho","marcar","seleccionar","\u2713","\u2714","check"],lock:["bloqueo","seguridad","privacidad","candado","lock"],camera:["c\xE1mara","foto","captura","imagen","camera"],cart:["carrito","cesta","bolsa","comprar","cart","checkout"],email:["correo","correo electr\xF3nico","email","usuario","mail"],username:["usuario","nombre de usuario","correo","username"],password:["contrase\xF1a","clave","pin","c\xF3digo","password"],phone:["tel\xE9fono","m\xF3vil","celular","contacto","n\xFAmero","phone"],name:["nombre","nombre completo","apellidos","name"],otp:["c\xF3digo de verificaci\xF3n","c\xF3digo","otp","pin"],submit:["enviar","guardar","confirmar","continuar","proceder","listo","aceptar","submit"],save:["guardar","salvar","almacenar","actualizar","save"],cancel:["cancelar","descartar","anular","abortar","cancel"],confirm:["confirmar","aceptar","s\xED","continuar","de acuerdo","confirm"],login:["iniciar sesi\xF3n","entrar","acceder","identificarse","ingresar","login","sign in"],signin:["iniciar sesi\xF3n","entrar","acceder","identificarse","ingresar","login","sign in"],logout:["cerrar sesi\xF3n","salir","desconectar","logout","sign out"],signout:["cerrar sesi\xF3n","salir","desconectar","logout","sign out"]},hi:{bell:["\u0918\u0902\u091F\u0940","\u0928\u094B\u091F\u093F\u092B\u093F\u0915\u0947\u0936\u0928","\u0938\u0942\u091A\u0928\u093E\u090F\u0902","alert","reminders"],notification:["\u0928\u094B\u091F\u093F\u092B\u093F\u0915\u0947\u0936\u0928","\u0938\u0942\u091A\u0928\u093E\u090F\u0902","alert","reminders","notification"],close:["\u092C\u0902\u0926 \u0915\u0930\u0947\u0902","\u0939\u091F\u093E\u090F\u0902","\u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902","\u2715","\xD7","x","close"],like:["\u092A\u0938\u0902\u0926","\u0932\u093E\u0907\u0915","\u092A\u0938\u0902\u0926\u0940\u0926\u093E"],dislike:["\u0928\u093E\u092A\u0938\u0902\u0926"],search:["\u0916\u094B\u091C\u0947\u0902","\u0922\u0942\u0902\u0922\u0947\u0902","\u0938\u0930\u094D\u091A","explore","search"],settings:["\u0938\u0947\u091F\u093F\u0902\u0917\u094D\u0938","\u0935\u093F\u0915\u0932\u094D\u092A","\u092A\u094D\u0930\u093E\u0925\u092E\u093F\u0915\u0924\u093E\u090F\u0902","settings"],profile:["\u092A\u094D\u0930\u094B\u092B\u093C\u093E\u0907\u0932","\u0916\u093E\u0924\u093E","\u0909\u092A\u092F\u094B\u0917\u0915\u0930\u094D\u0924\u093E","\u0905\u0915\u093E\u0909\u0902\u091F","profile"],trash:["\u0939\u091F\u093E\u090F\u0902","\u0921\u093F\u0932\u0940\u091F","\u0915\u091A\u0930\u093E"],delete:["\u0939\u091F\u093E\u090F\u0902","\u0921\u093F\u0932\u0940\u091F","\u092E\u093F\u091F\u093E\u090F\u0902","delete"],share:["\u0936\u0947\u092F\u0930 \u0915\u0930\u0947\u0902","\u092D\u0947\u091C\u0947\u0902","share"],back:["\u0935\u093E\u092A\u0938","\u092A\u0940\u091B\u0947","\u0935\u093E\u092A\u0938 \u091C\u093E\u090F\u0902","back"],menu:["\u092E\u0947\u0928\u0942","\u0935\u093F\u0915\u0932\u094D\u092A","\u2630","menu"],add:["\u091C\u094B\u0921\u093C\u0947\u0902","\u0928\u092F\u093E","\u092C\u0928\u093E\u090F\u0902","+","add"],edit:["\u0938\u0902\u092A\u093E\u0926\u093F\u0924 \u0915\u0930\u0947\u0902","\u092C\u0926\u0932\u0947\u0902","\u090F\u0921\u093F\u091F","edit"],calendar:["\u0915\u0948\u0932\u0947\u0902\u0921\u0930","\u0924\u093E\u0930\u0940\u0916","\u0938\u092E\u092F \u0938\u093E\u0930\u093F\u0923\u0940"],refresh:["\u0930\u093F\u092B\u094D\u0930\u0947\u0936","\u0924\u093E\u091C\u093C\u093E \u0915\u0930\u0947\u0902","\u0905\u092A\u0921\u0947\u091F \u0915\u0930\u0947\u0902"],info:["\u091C\u093E\u0928\u0915\u093E\u0930\u0940","\u0935\u093F\u0935\u0930\u0923","\u092E\u0926\u0926","info"],filter:["\u092B\u093C\u093F\u0932\u094D\u091F\u0930","\u091B\u093E\u0902\u091F\u0947\u0902","filter"],check:["\u0939\u094B \u0917\u092F\u093E","\u091A\u0941\u0928\u0947\u0902","\u0938\u0939\u0940","\u2713","\u2714"],cart:["\u0915\u093E\u0930\u094D\u091F","\u0925\u0948\u0932\u093E","\u091F\u094B\u0915\u0930\u0940","cart"],email:["\u0908\u092E\u0947\u0932","\u0908-\u092E\u0947\u0932","\u092F\u0942\u091C\u0930\u0928\u0947\u092E","email"],username:["\u0909\u092A\u092F\u094B\u0917\u0915\u0930\u094D\u0924\u093E \u0928\u093E\u092E","\u092F\u0942\u091C\u0930\u0928\u0947\u092E","\u0908\u092E\u0947\u0932","username"],password:["\u092A\u093E\u0938\u0935\u0930\u094D\u0921","\u092A\u093F\u0928","\u092A\u093E\u0938\u0915\u094B\u0921","password"],phone:["\u092B\u093C\u094B\u0928","\u092E\u094B\u092C\u093E\u0907\u0932","\u0928\u0902\u092C\u0930","\u0938\u0902\u092A\u0930\u094D\u0915","phone"],name:["\u0928\u093E\u092E","\u092A\u0942\u0930\u093E \u0928\u093E\u092E","name"],otp:["\u0913\u091F\u0940\u092A\u0940","\u0938\u0924\u094D\u092F\u093E\u092A\u0928 \u0915\u094B\u0921","\u092A\u093F\u0928","otp"],submit:["\u091C\u092E\u093E \u0915\u0930\u0947\u0902","\u0938\u092C\u092E\u093F\u091F","\u092A\u0941\u0937\u094D\u091F\u093F \u0915\u0930\u0947\u0902","\u0906\u0917\u0947 \u092C\u0922\u093C\u0947\u0902","submit"],save:["\u0938\u0939\u0947\u091C\u0947\u0902","\u0938\u0947\u0935 \u0915\u0930\u0947\u0902","\u0938\u0941\u0930\u0915\u094D\u0937\u093F\u0924 \u0915\u0930\u0947\u0902","save"],cancel:["\u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902","\u0915\u0948\u0902\u0938\u093F\u0932","cancel"],confirm:["\u092A\u0941\u0937\u094D\u091F\u093F \u0915\u0930\u0947\u0902","\u0938\u094D\u0935\u0940\u0915\u093E\u0930 \u0915\u0930\u0947\u0902","\u0939\u093E\u0901","\u0920\u0940\u0915 \u0939\u0948","confirm"],login:["\u0932\u0949\u0917 \u0907\u0928","\u0932\u0949\u0917\u093F\u0928","\u092A\u094D\u0930\u0935\u0947\u0936 \u0915\u0930\u0947\u0902","\u0938\u093E\u0907\u0928 \u0907\u0928","login","sign in"],signin:["\u0932\u0949\u0917 \u0907\u0928","\u0932\u0949\u0917\u093F\u0928","\u0938\u093E\u0907\u0928 \u0907\u0928","\u092A\u094D\u0930\u0935\u0947\u0936 \u0915\u0930\u0947\u0902","login","sign in"],logout:["\u0932\u0949\u0917 \u0906\u0909\u091F","\u0938\u093E\u0907\u0928 \u0906\u0909\u091F","\u092C\u093E\u0939\u0930 \u0928\u093F\u0915\u0932\u0947\u0902","logout"],signout:["\u0932\u0949\u0917 \u0906\u0909\u091F","\u0938\u093E\u0907\u0928 \u0906\u0909\u091F","\u092C\u093E\u0939\u0930 \u0928\u093F\u0915\u0932\u0947\u0902","logout"]}},zr=$t.en,Xt=new Map;function ki(a){let e=new Map;for(let[t,s]of Object.entries(a)){let r=[oe(t),...s.map(n=>oe(n))];for(let n of r){e.has(n)||e.set(n,[]);let o=e.get(n);for(let c of r)o.includes(c)||o.push(c)}}return e}for(let[a,e]of Object.entries($t))Xt.set(a.toLowerCase(),ki(e));function Gr(a,e){let t=a.toLowerCase();$t[t]={...$t[t]||{},...e},Xt.set(t,ki($t[t]))}function Kr(){return Object.keys($t)}function Yt(a,e="en"){let t=oe(a),s=(e||"en").toLowerCase(),i=Xt.get(s);if(i){let r=i.get(t);if(r&&r.length>0)return r}if(s!=="en"){let r=Xt.get("en");if(r){let n=r.get(t);if(n&&n.length>0)return n}}return[t]}var Di=Yt;var rt=class a{memoryDir;currentPackage="global";mappings=new Map;screens=new Map;intents=new Map;dirty=!1;locale="en";setLocale(e){this.locale=(e||"en").toLowerCase()}getLocale(){return this.locale}constructor(e){if(e)this.memoryDir=e;else{let t=qe.dirname(Mn(import.meta.url));this.memoryDir=qe.resolve(t,"..","memory")}Ne.existsSync(this.memoryDir)||Ne.mkdirSync(this.memoryDir,{recursive:!0})}cleanPackageName(e){return(e||"global").replace(/[^a-zA-Z0-9._-]/g,"_")}static SCHEMA_VERSION=2;async init(e){this.currentPackage=this.cleanPackageName(e),this.mappings.clear(),this.screens.clear(),this.intents.clear(),this.dirty=!1;let t=qe.join(this.memoryDir,`${this.currentPackage}.json`);if(Ne.existsSync(t))try{let s=await Ne.promises.readFile(t,"utf-8"),i=JSON.parse(s);if(i&&i.mappings){let r=!i.version||i.version<2;for(let[n,o]of Object.entries(i.mappings))r&&(o.totalSuccesses=o.totalSuccesses??o.successCount??1,o.recentOutcomes=o.recentOutcomes??(o.failCount>0?["FAIL"]:["PASS"]),o.lastSeenTs=o.lastSeenTs??o.lastUpdated??new Date().toISOString(),this.dirty=!0),this.mappings.set(n,o);if(i.screens)for(let[n,o]of Object.entries(i.screens))this.screens.set(n,o);if(i.intents)for(let[n,o]of Object.entries(i.intents))this.intents.set(n,o);this.decayStaleMappings()}else this.mappings.clear(),this.screens.clear(),this.intents.clear()}catch{this.mappings.clear(),this.screens.clear(),this.intents.clear()}}getCanonicalQuery(e){let t=oe(e),s=Yt(t,this.locale);return s&&s.length>0?s[0]:t}buildKey(e,t){return`${e.toUpperCase()}:${this.getCanonicalQuery(t)}`}resolve(e,t){let s=this.buildKey(e,t),i=this.mappings.get(s);if(i)return i;let r=`${e.toUpperCase()}:${oe(t)}`,n=this.mappings.get(r);if(n)return n;let o=Yt(t,this.locale);for(let c of o){let l=`${e.toUpperCase()}:${c}`,p=this.mappings.get(l);if(p)return p}}getAllCandidates(e,t,s,i){let r=(s||this.locale||"en").toLowerCase(),n=new Set;if(i){let l=this.getScreenElements(i),p=this.getCanonicalQuery(t);for(let d of l)d.action===e.toUpperCase()&&(d.canonicalIntent===p||d.canonicalIntent===oe(t))&&d.confidence>=.7&&n.add(d.matchedIdentifier)}let o=this.resolve(e,t);o&&o.confidence>=.8&&n.add(o.matchedIdentifier),n.add(t);let c=Yt(t,r);for(let l of c)n.add(l);return Array.from(n)}recordSuccess(e,t,s,i,r=1,n,o){if(s.length>35||s.includes("?")||s.includes(`
796
- `))return;let c=this.buildKey(e,t),l=this.mappings.get(c),p=new Date().toISOString(),d=n||l?.healedFrom?C.memory?.maxHealedConfidence??.65:1,h=n||l?.healedFrom?Math.min(r,d):r;l&&l.matchedIdentifier===s?(l.successCount+=1,l.totalSuccesses=(l.totalSuccesses||0)+1,l.failCount=0,l.recentOutcomes||(l.recentOutcomes=[]),l.recentOutcomes.push("PASS"),l.recentOutcomes.length>5&&l.recentOutcomes.shift(),l.confidence=Math.min(d,Number((l.confidence+.05).toFixed(2))),l.lastUpdated=p,l.lastSeenTs=p,n&&(l.healedFrom=n)):this.mappings.set(c,{canonicalIntent:this.getCanonicalQuery(t),action:e.toUpperCase(),matchedIdentifier:s,matchType:i,confidence:h,successCount:1,failCount:0,totalSuccesses:1,recentOutcomes:["PASS"],lastUpdated:p,lastSeenTs:p,healedFrom:n}),o&&this.associateScreenElement(o,c),this.dirty=!0}associateScreenElement(e,t){if(!e||!t)return;let s=new Date().toISOString(),i=this.screens.get(e);i?(i.elements.includes(t)||i.elements.push(t),i.lastSeenTs=s):this.screens.set(e,{lastSeenTs:s,elements:[t]}),this.dirty=!0}getScreenElements(e){let t=this.screens.get(e);return t?t.elements.map(s=>this.mappings.get(s)).filter(s=>s!==void 0):[]}getScreenIndex(){return Object.fromEntries(this.screens.entries())}markScreenCrawled(e,t){if(!e)return;let s=new Date().toISOString(),i=this.screens.get(e);if(i){if(i.crawled=!0,i.crawledAt=s,i.lastSeenTs=s,t&&t.length>0){i.safeActions||(i.safeActions=[]);for(let r of t)i.safeActions.includes(r)||i.safeActions.push(r)}}else this.screens.set(e,{lastSeenTs:s,elements:[],crawled:!0,crawledAt:s,safeActions:t?[...t]:[]});this.dirty=!0}isScreenCrawled(e){return e?!!this.screens.get(e)?.crawled:!1}getScreenMemory(e){return this.screens.get(e)}getCrawledScreensCount(){let e=0;for(let t of this.screens.values())t.crawled&&e++;return e}recordIntentExpansion(e,t){if(!e||!t||t.length===0)return;let s=oe(e),i=new Date().toISOString(),r=this.intents.get(s);r?(r.steps=[...t],r.usageCount=(r.usageCount||1)+1,r.learnedAt=i):this.intents.set(s,{intent:e,steps:[...t],learnedAt:i,usageCount:1}),this.dirty=!0}getIntentExpansion(e){if(!e)return;let t=oe(e),s=this.intents.get(t);if(s)return s.usageCount=(s.usageCount||0)+1,this.dirty=!0,[...s.steps]}hasIntentExpansion(e){return e?this.intents.has(oe(e)):!1}getIntentExpansionsCount(){return this.intents.size}recordFailure(e,t){let s=this.buildKey(e,t),i=this.mappings.get(s);i&&(i.failCount+=1,i.recentOutcomes||(i.recentOutcomes=[]),i.recentOutcomes.push("FAIL"),i.recentOutcomes.length>5&&i.recentOutcomes.shift(),i.confidence=Math.max(.1,Number((i.confidence-.25).toFixed(2))),i.lastUpdated=new Date().toISOString(),(i.recentOutcomes.filter(n=>n==="FAIL").length>=3||i.confidence<.2)&&this.mappings.delete(s),this.dirty=!0)}decayStaleMappings(e){let s=(e??C.memory?.staleThresholdDays??14)*24*60*60*1e3,i=Date.now(),r=0;for(let n of this.mappings.values()){let o=n.lastSeenTs?new Date(n.lastSeenTs).getTime():0;if(o>0&&i-o>s){let c=C.memory?.staleDecayAmount??.2,l=C.memory?.minConfidenceFloor??.2,p=Math.max(l,Number((n.confidence-c).toFixed(2)));p<n.confidence&&(n.confidence=p,r++,this.dirty=!0)}}return r}async save(){if(!this.dirty)return;let e={packageName:this.currentPackage,version:a.SCHEMA_VERSION,lastUpdated:new Date().toISOString(),mappings:Object.fromEntries(this.mappings.entries()),screens:Object.fromEntries(this.screens.entries()),intents:Object.fromEntries(this.intents.entries())},t=qe.join(this.memoryDir,`${this.currentPackage}.json`),s=`${t}.tmp.${Date.now()}`;await Ne.promises.writeFile(s,JSON.stringify(e,null,2),"utf-8");try{await Ne.promises.rename(s,t)}catch{await Ne.promises.copyFile(s,t),await Ne.promises.unlink(s).catch(()=>{})}this.dirty=!1}async exportMemory(e){let t=e?qe.resolve(e):qe.join(this.memoryDir,`${this.currentPackage}-export.json`),s=qe.dirname(t);Ne.existsSync(s)||Ne.mkdirSync(s,{recursive:!0});let i={packageName:this.currentPackage,version:a.SCHEMA_VERSION,lastUpdated:new Date().toISOString(),mappings:Object.fromEntries(this.mappings.entries()),screens:Object.fromEntries(this.screens.entries()),intents:Object.fromEntries(this.intents.entries())};return await Ne.promises.writeFile(t,JSON.stringify(i,null,2),"utf-8"),t}async importMemory(e){let t=qe.resolve(e);if(!Ne.existsSync(t))throw new Error(`Memory import source file not found: ${t}`);let s=await Ne.promises.readFile(t,"utf-8"),i=JSON.parse(s);if(!i||!i.mappings)throw new Error("Invalid memory file format: missing mappings object.");let r=0,n=new Date().toISOString();for(let[o,c]of Object.entries(i.mappings)){let l=this.mappings.get(o);(!l||c.confidence>=l.confidence)&&(this.mappings.set(o,{...c,totalSuccesses:c.totalSuccesses??c.successCount??1,recentOutcomes:c.recentOutcomes??["PASS"],lastSeenTs:c.lastSeenTs??c.lastUpdated??n,lastUpdated:n}),r++)}if(i.screens)for(let[o,c]of Object.entries(i.screens)){let l=this.screens.get(o);if(!l)this.screens.set(o,c);else{let p=new Set([...l.elements,...c.elements]),d=new Set([...l.safeActions||[],...c.safeActions||[]]);this.screens.set(o,{lastSeenTs:c.lastSeenTs||l.lastSeenTs,elements:Array.from(p),crawled:l.crawled||c.crawled,crawledAt:c.crawledAt||l.crawledAt,safeActions:Array.from(d)})}}if(i.intents)for(let[o,c]of Object.entries(i.intents)){let l=this.intents.get(o);(!l||(c.usageCount||0)>=(l.usageCount||0))&&this.intents.set(o,c)}return r>0&&(this.dirty=!0,await this.save()),r}getMappingsList(){return Array.from(this.mappings.values())}getStats(){let e=Array.from(this.mappings.values()),t=e.reduce((r,n)=>r+(n.totalSuccesses||n.successCount||0),0),s=e.length>0?Number((e.reduce((r,n)=>r+n.confidence,0)/e.length).toFixed(2)):0,i=0;for(let r of this.screens.values())r.crawled&&i++;return{totalMappings:this.mappings.size,totalScreens:this.screens.size,crawledScreens:i,totalIntents:this.intents.size,package:this.currentPackage,totalSuccesses:t,averageConfidence:s}}async clear(e){let t=this.cleanPackageName(e||this.currentPackage),s=qe.join(this.memoryDir,`${t}.json`);return Ne.existsSync(s)?(await Ne.promises.unlink(s),t===this.currentPackage&&(this.mappings.clear(),this.screens.clear(),this.intents.clear(),this.dirty=!1),!0):!1}},Ct=new rt;import je from"fs";import Rn from"path";import On from"os";var gs=new Map;function yt(a){let e=(a||"default").replace(/[^a-zA-Z0-9._-]/g,"_"),t=Rn.join(On.tmpdir(),`prompttest-${e}.lock`);if(je.existsSync(t))try{let i=je.readFileSync(t,"utf-8").trim(),r=parseInt(i,10);if(!isNaN(r)){if(r===process.pid)return{serial:a,lockFilePath:t,acquired:!0,pid:process.pid};try{throw process.kill(r,0),new Error(`Device "${a}" is locked by another PromptTest process (PID ${r}).
797
- Wait for the other run to complete, or remove the lock file if it is stale: ${t}`)}catch(n){if(n.code==="ESRCH")try{je.unlinkSync(t)}catch{}else throw n}}}catch(i){let r=i;if(r.message&&r.message.includes("locked by another"))throw r;try{je.unlinkSync(t)}catch{}}try{je.writeFileSync(t,process.pid.toString(),{flag:"wx"})}catch(i){throw i.code==="EEXIST"?new Error(`Device "${a}" was just acquired by a concurrent PromptTest process.`):i}let s=()=>{try{je.existsSync(t)&&je.readFileSync(t,"utf-8").trim()===process.pid.toString()&&je.unlinkSync(t)}catch{}};return gs.set(t,s),process.once("exit",s),{serial:a,lockFilePath:t,acquired:!0,pid:process.pid}}function St(a){if(a)try{let e=gs.get(a.lockFilePath);if(e)process.removeListener("exit",e),gs.delete(a.lockFilePath),e();else if(je.existsSync(a.lockFilePath))try{je.readFileSync(a.lockFilePath,"utf-8").trim()===process.pid.toString()&&je.unlinkSync(a.lockFilePath)}catch{}}catch{}}import ms from"chalk";var fs={DEBUG:0,INFO:1,WARN:2,ERROR:3,SILENT:4},zt=class{currentLevel="INFO";constructor(e){let t=(process.env.LOG_LEVEL||"").toUpperCase();e?this.currentLevel=e:fs[t]!==void 0?this.currentLevel=t:this.currentLevel="INFO"}setLevel(e){this.currentLevel=e}getLevel(){return this.currentLevel}isLevelEnabled(e){return fs[this.currentLevel]<=fs[e]}isJsonMode(){return(process.env.LOG_FORMAT||"").toLowerCase()==="json"}emitJson(e,...t){let s=t.map(r=>typeof r=="object"?JSON.stringify(r):String(r)).join(" "),i={ts:new Date().toISOString(),level:e,msg:s};e==="ERROR"||e==="WARN"?console.error(JSON.stringify(i)):console.log(JSON.stringify(i))}debug(...e){this.isLevelEnabled("DEBUG")&&(this.isJsonMode()?this.emitJson("DEBUG",...e):console.log(ms.gray("[DEBUG]"),...e))}info(...e){this.isLevelEnabled("INFO")&&(this.isJsonMode()?this.emitJson("INFO",...e):console.log(...e))}warn(...e){this.isLevelEnabled("WARN")&&(this.isJsonMode()?this.emitJson("WARN",...e):console.warn(ms.yellow("[WARN]"),...e))}error(...e){this.isLevelEnabled("ERROR")&&(this.isJsonMode()?this.emitJson("ERROR",...e):console.error(ms.red("[ERROR]"),...e))}},Ge=new zt;import ot from"fs";import Ai from"path";var bs=class{checkpointDir;constructor(e){this.checkpointDir=e||Ai.resolve(process.cwd(),"output","checkpoints"),ot.existsSync(this.checkpointDir)||ot.mkdirSync(this.checkpointDir,{recursive:!0})}filePath(e){let t=e.replace(/[^a-zA-Z0-9._-]/g,"_");return Ai.join(this.checkpointDir,`${t}.checkpoint.json`)}async save(e,t,s,i){let r={specFile:e,packageName:t,lastPassedStep:s,totalSteps:i,savedAt:new Date().toISOString()},n=this.filePath(e)+`.tmp.${Date.now()}`;await ot.promises.writeFile(n,JSON.stringify(r,null,2),"utf-8"),await ot.promises.rename(n,this.filePath(e))}async load(e){let t=this.filePath(e);if(!ot.existsSync(t))return null;try{let s=await ot.promises.readFile(t,"utf-8");return JSON.parse(s)}catch{return null}}async clear(e){let t=this.filePath(e);ot.existsSync(t)&&await ot.promises.unlink(t)}async getResumeIndex(e){let t=await this.load(e);return t?t.lastPassedStep+1:0}},$i=new bs;import ve from"fs";import at from"path";import{PNG as wt}from"pngjs";import Fn from"pixelmatch";var ys={strict:{threshold:.005,pixelmatchThreshold:.05,label:"Strict (0.5%)",description:"Pixel-perfect verification for Figma design-to-code compliance"},moderate:{threshold:.02,pixelmatchThreshold:.1,label:"Moderate (2.0%)",description:"Standard mobile QA matching"},relaxed:{threshold:.05,pixelmatchThreshold:.2,label:"Relaxed (5.0%)",description:"Lenient comparison for cross-device or font variations"}},ct=class{baselineDir;defaultThreshold;constructor(e,t=.02){this.baselineDir=e||at.resolve(process.cwd(),"output","baselines"),this.defaultThreshold=t,ve.existsSync(this.baselineDir)||ve.mkdirSync(this.baselineDir,{recursive:!0})}filePath(e,t){let s=e.replace(/[^a-zA-Z0-9._-]/g,"_");return at.join(this.baselineDir,`${s}_step${String(t).padStart(3,"0")}.png`)}diffFilePath(e,t){let s=e.replace(/[^a-zA-Z0-9._-]/g,"_");return at.join(this.baselineDir,`${s}_step${String(t).padStart(3,"0")}_diff.png`)}masksFilePath(e,t){let s=e.replace(/[^a-zA-Z0-9._-]/g,"_");return at.join(this.baselineDir,`${s}_step${String(t).padStart(3,"0")}_masks.json`)}async getMasks(e,t){let s=this.masksFilePath(e,t);if(!ve.existsSync(s))return[];try{let i=await ve.promises.readFile(s,"utf-8"),r=JSON.parse(i);return Array.isArray(r)?r:r.masks||[]}catch{return[]}}async saveMasks(e,t,s){let i=this.masksFilePath(e,t);return await ve.promises.writeFile(i,JSON.stringify(s,null,2),"utf-8"),i}async saveBaseline(e,t,s){let i=this.filePath(e,t),r=i+`.tmp.${Date.now()}`;return await ve.promises.writeFile(r,s),await ve.promises.rename(r,i),i}hasBaseline(e,t){return ve.existsSync(this.filePath(e,t))}async compare(e,t,s,i){let r=typeof i=="number"?{threshold:i}:i||{},n=this.filePath(e,t),o=at.join(this.baselineDir,`${e.replace(/[^a-zA-Z0-9._-]/g,"_")}_step${String(t).padStart(3,"0")}_current.png`);await ve.promises.writeFile(o,s);let c=r.mode?ys[r.mode]:void 0,l=r.threshold??c?.threshold??this.defaultThreshold,p=r.pixelmatchThreshold??c?.pixelmatchThreshold??.1;if(!ve.existsSync(n)){await this.saveBaseline(e,t,s);let M=wt.sync.read(s),J=M.width*M.height;return{stepName:`Step ${t}`,baselineFile:n,currentFile:o,diffRatio:0,diffPixels:0,totalPixels:J,passed:!0,threshold:l}}let d=await ve.promises.readFile(n),h=wt.sync.read(d),u=wt.sync.read(s),m=Math.max(h.width,u.width),y=Math.max(h.height,u.height),v=m*y,T=this.fitToCanvas(h,m,y),S=this.fitToCanvas(u,m,y),b=r.maskRegions??await this.getMasks(e,t);b&&b.length>0&&(this.applyMasks(T,b),this.applyMasks(S,b));let E=new wt({width:m,height:y}),D=Fn(T.data,S.data,E.data,m,y,{threshold:p,includeAA:!1}),B=v>0?D/v:0,k=B<=l,j;if(k){let M=this.diffFilePath(e,t);if(ve.existsSync(M))try{await ve.promises.unlink(M)}catch{}}else{j=this.diffFilePath(e,t);let M=wt.sync.write(E);await ve.promises.writeFile(j,M)}return{stepName:`Step ${t}`,baselineFile:n,currentFile:o,diffRatio:B,diffPixels:D,totalPixels:v,passed:k,threshold:l,diffImagePath:j}}fitToCanvas(e,t,s){if(e.width===t&&e.height===s)return e;let i=new wt({width:t,height:s});return i.data.fill(0),wt.bitblt(e,i,0,0,e.width,e.height,0,0),i}applyMasks(e,t){for(let s of t){let i=s.width<=1&&s.height<=1||s.x<=1&&s.y<=1&&(s.width<1||s.height<1),r=Math.round(i?s.x*e.width:s.x),n=Math.round(i?s.y*e.height:s.y),o=Math.round(i?s.width*e.width:s.width),c=Math.round(i?s.height*e.height:s.height),l=Math.max(0,n),p=Math.min(e.height,n+c),d=Math.max(0,r),h=Math.min(e.width,r+o);for(let u=l;u<p;u++)for(let m=d;m<h;m++){let y=e.width*u+m<<2;e.data[y]=0,e.data[y+1]=0,e.data[y+2]=0,e.data[y+3]=0}}}async approveBaseline(e,t,s){let i;if(s)i=await this.saveBaseline(e,t,s);else{let n=e.replace(/[^a-zA-Z0-9._-]/g,"_"),o=at.join(this.baselineDir,`${n}_step${String(t).padStart(3,"0")}_current.png`);if(!ve.existsSync(o))throw new Error(`No current screenshot found to approve for step ${t}`);let c=await ve.promises.readFile(o);i=await this.saveBaseline(e,t,c)}let r=this.diffFilePath(e,t);if(ve.existsSync(r))try{await ve.promises.unlink(r)}catch{}return i}async clearBaselines(e){let t=e.replace(/[^a-zA-Z0-9._-]/g,"_");if(!ve.existsSync(this.baselineDir))return;let s=await ve.promises.readdir(this.baselineDir);await Promise.all(s.filter(i=>i.startsWith(t+"_step")).map(i=>ve.promises.unlink(at.join(this.baselineDir,i)).catch(()=>{})))}async deleteStepBaseline(e,t){let i=`${e.replace(/[^a-zA-Z0-9._-]/g,"_")}_step${String(t).padStart(3,"0")}`;if(!ve.existsSync(this.baselineDir))return;let r=await ve.promises.readdir(this.baselineDir);await Promise.all(r.filter(n=>n.startsWith(i)).map(n=>ve.promises.unlink(at.join(this.baselineDir,n)).catch(()=>{})))}},Ss=new ct;import ue from"chalk";import _i from"vm";import It from"chalk";var _n=["com.miui.home","com.mi.android.globallauncher","com.sec.android.app.launcher","com.sec.android.app.twlauncher","com.google.android.apps.nexuslauncher","com.android.launcher3","com.android.launcher","com.oppo.launcher","com.coloros.launcher","com.bbk.launcher2","com.vivo.launcher","com.huawei.android.launcher","net.oneplus.launcher","com.motorola.launcher3","org.lineageos.trebuchet","com.teslacoilsw.launcher","com.actionlauncher.playstore","com.transsion.hilauncher","com.nothing.launcher"],Hn=["android","com.android.systemui","com.google.android.permissioncontroller","com.android.permissioncontroller","com.android.packageinstaller","com.google.android.packageinstaller","com.google.android.inputmethod.latin","com.samsung.android.honeyboard","com.touchtype.swiftkey","com.miui.virtualsim","com.google.android.gms","com.android.settings"];function vt(a){if(!a)return!1;let e=a.toLowerCase();return _n.some(t=>e===t.toLowerCase()||e.includes(t.toLowerCase()))?!0:e.includes("launcher")||e.includes(".home")||e.endsWith(".home")||e.includes("trebuchet")||e.includes("desktop")}function ws(a){if(!a)return!1;let e=a.toLowerCase();return Hn.some(t=>e===t.toLowerCase()||e.startsWith(t.toLowerCase()))}function Gt(a,e){if(!a||!e)return!0;let t=a.toLowerCase(),s=e.toLowerCase();return!!(t===s||t.startsWith(`${s}.`)||ws(a))}function vs(a){if(!a||a.length===0)return null;for(let e of a)if(e.packageName&&(e.clickable||e.focused||e.scrollable)&&!ws(e.packageName))return e.packageName;for(let e of a)if(e.packageName&&!ws(e.packageName))return e.packageName;return a[0]?.packageName||null}async function xs(a,e,t,s){if(!e)return{escaped:!1,recovered:!0,currentPackage:null};let i=vs(a);if(!i)return{escaped:!1,recovered:!0,currentPackage:null};if(Gt(i,e))return{escaped:!1,recovered:!0,currentPackage:i};let r=vt(i);console.log(It.yellow(`
798
- \u{1F6E1}\uFE0F [Jail Guard] Boundary Violation Detected: Active screen belongs to "${i}" (not target "${e}").`)),console.log(r?It.cyan(` [Jail Guard] Device is on home launcher. Suppressing back-press to prevent exit loops. Re-launching "${e}"...`):It.cyan(` [Jail Guard] Foreign app "${i}" intercepted. Re-launching target "${e}" to restore containment...`));try{await t.launchApp(e,s),await new Promise(c=>setTimeout(c,1200));let n=await t.getForegroundPackage(s),o=n?n.includes(e):!0;return console.log(o?It.green(` \u2714 [Jail Guard] Containment restored: "${e}" is now in foreground.`):It.red(` \u274C [Jail Guard] Recovery failed. Current foreground is still "${n}".`)),{escaped:!0,recovered:o,currentPackage:i}}catch(n){return console.log(It.red(` \u274C [Jail Guard] Error during containment recovery: ${n.message}`)),{escaped:!0,recovered:!1,currentPackage:i}}}import Ci from"fs";import Kt from"path";import ge from"chalk";function Ts(a,e=new Set){let t=Kt.resolve(a);if(!Ci.existsSync(t))throw new Error(`Included spec file not found: ${t}`);if(e.has(t)){let n=Array.from(e).concat(t);throw new _t(`Circular spec inclusion detected: ${n.join(" -> ")}`,n)}e.add(t);let i=Ci.readFileSync(t,"utf-8").split(`
799
- `),r=[];for(let n of i){let o=n.trim(),c=o.match(G.INCLUDE_SPEC);if(c&&!o.startsWith("#")){let l=ae([c[1],c[2],c[3]]),p=Kt.isAbsolute(l)?l:Kt.resolve(Kt.dirname(t),l),d=Ts(p,new Set(e));r.push(d)}else r.push(n)}return r.join(`
800
- `)}function Bn(a){let e=[],t=[];for(let s of a){if(s._isControl==="END"){t.length>0&&t.pop();continue}if(s._isControl==="ELSE"){t.length>0&&t[t.length-1].step.type==="IF_VISIBLE"&&(t[t.length-1].inElse=!0);continue}let i=e;if(t.length>0){let r=t[t.length-1];r.step.type==="IF_VISIBLE"?r.inElse?(r.step.elseSteps=r.step.elseSteps||[],i=r.step.elseSteps):(r.step.thenSteps=r.step.thenSteps||[],i=r.step.thenSteps):r.step.type==="LOOP"&&(r.step.childSteps=r.step.childSteps||[],i=r.step.childSteps)}i.push(s),(s.type==="IF_VISIBLE"||s.type==="LOOP")&&t.push({step:s,inElse:!1})}return e}function Nt(a,e){let t=a.split(`
801
- `).map(i=>i.trim()).filter(i=>i.length>0&&!i.startsWith("#")),s=[];for(let i of t){let r=ft(i,e),n=r.replace(fi,"").trim(),o=G.OPTIONAL_SUFFIX.test(n)||/^[(\[]\s*optional\s*[)\]]/i.test(n)||/^optional\s*:/i.test(n),c=n.replace(G.OPTIONAL_SUFFIX,"").replace(/^[(\[]\s*optional\s*[)\]]\s*/i,"").replace(/^optional\s*:\s*/i,"").trim();if(G.BACK.test(c)){s.push({originalText:r,type:"BACK",optional:o});continue}let l=c.match(G.WAIT);if(l){let x=parseInt(l[1],10),$=c.toLowerCase().includes("ms")?x:x*1e3;s.push({originalText:r,type:"WAIT",durationMs:$,optional:o});continue}let p=c.match(G.WAIT_UNTIL);if(p){let x=ae([p[1],p[2],p[3]]),A=(p[4]||"visible").toLowerCase().includes("enable")?"enabled":"visible",H=p[5]?parseInt(p[5],10):void 0,R=(p[6]||"s").toLowerCase(),W=H?R.includes("ms")?H:H*1e3:void 0;s.push({originalText:r,type:"WAIT_UNTIL",target:x,expectedState:A,durationMs:W,optional:o});continue}let d=c.match(G.IF_VISIBLE);if(d){let x=ae([d[1],d[2],d[3],d[5],d[6],d[7]]);s.push({originalText:r,type:"IF_VISIBLE",conditionTarget:x,thenSteps:[],elseSteps:[],optional:o});continue}if(G.ELSE.test(c)){s.push({originalText:r,type:"IF_VISIBLE",_isControl:"ELSE"});continue}if(G.END.test(c)){s.push({originalText:r,type:"IF_VISIBLE",_isControl:"END"});continue}let h=c.match(G.LOOP);if(h){let x=parseInt(h[1],10);s.push({originalText:r,type:"LOOP",loopCount:x,childSteps:[],optional:o});continue}let u=c.match(G.FOR_EACH);if(u){let x=ae([u[1],u[2],u[3]]);s.push({originalText:r,type:"LOOP",target:x,loopVariable:"item",childSteps:[],optional:o});continue}let m=c.match(G.SET_VARIABLE);if(m){let x=m[1].trim(),$=m[2].trim();s.push({originalText:r,type:"SET_VARIABLE",variableName:x,variableExpression:$,optional:o});continue}let y=c.match(G.DEVICE_WIFI);if(y){let x=y[1].toLowerCase();s.push({originalText:r,type:"DEVICE_STATE",deviceAction:"wifi",deviceStateValue:x==="on"||x==="enable",optional:o});continue}let v=c.match(G.DEVICE_AIRPLANE);if(v){let x=v[1].toLowerCase();s.push({originalText:r,type:"DEVICE_STATE",deviceAction:"airplane",deviceStateValue:x==="on"||x==="enable",optional:o});continue}let T=c.match(G.DEVICE_ORIENTATION);if(T){let x=T[1].toLowerCase();s.push({originalText:r,type:"DEVICE_STATE",deviceAction:"orientation",deviceStateValue:x,optional:o});continue}let S=c.match(G.DEVICE_NOTIFICATIONS);if(S){let x=S[1].toLowerCase(),$=x.startsWith("open")||x.startsWith("expand")||x.startsWith("pull");s.push({originalText:r,type:"DEVICE_STATE",deviceAction:"notifications",deviceStateValue:$?"open":"collapse",optional:o});continue}let b=c.match(G.RUN_JS);if(b){let x=b[1].trim();s.push({originalText:r,type:"RUN_JS",jsCode:x,optional:o});continue}let E=c.match(G.ENTER_OTP);if(E){let x=ae([E[1],E[2],E[3]])||void 0;s.push({originalText:r,type:"ENTER_OTP",target:x,optional:o});continue}let D=c.match(G.API_REQUEST);if(D){let x=D[1].toUpperCase(),$=ae([D[2],D[3],D[4]]),A=ae([D[5],D[6],D[7]])||void 0,H=ae([D[8],D[9],D[10]])||void 0,R=D[11]||void 0,W=D[12]?parseInt(D[12],10):void 0;s.push({originalText:r,type:"API_REQUEST",apiMethod:x,apiUrl:$,apiBody:A,apiStorePath:H,apiStoreVariable:R,apiExpectedStatus:W,optional:o});continue}let B=c.match(G.EMAIL_ASSERT);if(B){let x=ae([B[1],B[2],B[3]]),$=ae([B[4],B[5],B[6]]),A=ae([B[7],B[8],B[9]])||void 0;s.push({originalText:r,type:"EMAIL_ASSERT",emailRecipient:x,emailSubject:$,emailBodyContains:A,optional:o});continue}let k=c.match(G.LAUNCH_APP);if(k){let x=ae([k[1],k[2],k[3]]);s.push({originalText:r,type:"LAUNCH_APP",target:x,optional:o});continue}let j=c.match(G.RESTART_APP);if(j){let x=ae([j[1],j[2],j[3]]);s.push({originalText:r,type:"RESTART_APP",target:x,optional:o});continue}let M=c.match(G.TERMINATE_APP);if(M){let x=ae([M[1],M[2],M[3]])||void 0;s.push({originalText:r,type:"TERMINATE_APP",target:x,optional:o});continue}let J=c.match(G.CLEAR_DATA);if(J){let x=ae([J[1],J[2],J[3]]);s.push({originalText:r,type:"CLEAR_DATA",target:x,optional:o});continue}let Y=c.match(G.GRANT_PERMISSION);if(Y){let x=ae([Y[1],Y[2],Y[3]]),$=ae([Y[4],Y[5],Y[6]])||void 0;s.push({originalText:r,type:"GRANT_PERMISSION",value:x,target:$,optional:o});continue}let K=c.match(G.DEEP_LINK);if(K){let x=ae([K[1],K[2],K[3]]);s.push({originalText:r,type:"DEEP_LINK",target:x,optional:o});continue}let le=c.match(G.DIRECTIONAL_SWIPE);if(le){let x=le[1].toLowerCase(),$=ae([le[2],le[3],le[4]])||void 0;s.push({originalText:r,type:"SWIPE",direction:x,target:$,optional:o});continue}let Z=c.match(G.TOGGLE);if(Z){let x=(Z[1]||"").toLowerCase(),$=ae([Z[2],Z[3],Z[4]]),A=c.toLowerCase().startsWith("uncheck")||x==="off"?!1:x==="on"||c.toLowerCase().startsWith("check")?!0:void 0;s.push({originalText:r,type:"TOGGLE",target:$,desiredState:A,optional:o});continue}let _=c.match(G.SELECT_DROPDOWN);if(_){let x=ae([_[1],_[2],_[3]]),$=ae([_[4],_[5],_[6]]);s.push({originalText:r,type:"SELECT",value:x,target:$,optional:o});continue}let De=c.match(G.SCROLL_TO);if(De){let x=(De[1]||"down").toLowerCase(),$=ae([De[2],De[3],De[4]]);s.push({originalText:r,type:"SCROLL",target:$,direction:x,optional:o});continue}let w=c.match(G.SCROLL);if(w){let x=w[1].toLowerCase();s.push({originalText:r,type:"SCROLL",target:x,direction:x,optional:o});continue}let f=c.match(G.LONG_PRESS);if(f){let x=ae([f[1],f[2],f[3]]);s.push({originalText:r,type:"LONG_PRESS",target:x,optional:o});continue}let I=c.match(G.ORDINAL_TAP);if(I){let x=I[1].toLowerCase(),$=ae([I[2],I[3],I[4]]);s.push({originalText:r,type:"TAP",target:$,ordinal:x,optional:o});continue}let N=c.match(G.PROXIMITY_TAP);if(N){let x=ae([N[1],N[2],N[3]]),$=ae([N[4],N[5],N[6]]);s.push({originalText:r,type:"TAP",target:x,relativeTo:$,optional:o});continue}let de=c.match(G.TYPE);if(de){let x=ae([de[1],de[2],de[3]]),$=ae([de[4],de[5],de[6]]);s.push({originalText:r,type:"TYPE",value:x,target:$,optional:o});continue}let he=c.match(G.CLEAR);if(he){let x=ae([he[1],he[2],he[3]]);s.push({originalText:r,type:"CLEAR",target:x,optional:o});continue}let g=c.match(G.VERIFY);if(g){let x=ae([g[1],g[2],g[3]]),$=(g[4]||"visible").toLowerCase(),A=$.includes("not visible")||$.includes("not present")||$.includes("absent")||$.includes("hidden")||$.includes("gone"),H=A?"absent":$==="exists"||$==="enabled"||$==="disabled"?$:"visible";s.push({originalText:r,type:"VERIFY",target:x,expectedState:H,negative:A,optional:o});continue}let P=c.match(G.TAP);if(P&&!c.toLowerCase().includes("back")){let x=ae([P[1],P[2],P[3]]);s.push({originalText:r,type:"TAP",target:x,optional:o});continue}let O=c.match(G.FALLBACK_QUOTED_TAP);if(O&&(c.toLowerCase().startsWith("tap")||c.toLowerCase().startsWith("click"))){s.push({originalText:r,type:"TAP",target:O[1],optional:o});continue}Ge.warn(`Unrecognized prompt step: "${i}". Skipping.`)}return Bn(s)}function Ni(a,e,t){let s=e.map(n=>(n.text||n.contentDesc||"").trim()).filter(n=>n.length>1),i=bt(a,s,3),r=i.length>0?`
825
+ </html>`}var dt=new lt;import Ie from"fs";import tt from"path";import{fileURLToPath as an}from"url";var Ft={en:{bell:["bell","notification","notifications","alert","alerts","reminders","notice","badge"],notification:["notification","notifications","bell","alert","alerts","reminders","badge"],close:["close","dismiss","cancel","clear","cross","exit","\u2715","\xD7","x"],like:["like","favorite","heart","thumbs up","thumbs-up","fav","upvote"],dislike:["dislike","thumbs down","thumbs-down","downvote"],search:["search","find","query","magnifier","explore","lookup"],settings:["settings","preferences","gear","options","setup","config"],profile:["profile","account","user","my profile","avatar","me","personal"],trash:["trash","delete","remove","bin","discard"],delete:["delete","trash","remove","bin","discard"],share:["share","export","send to","forward"],back:["back","navigate up","previous","arrow back","chevron left","return"],menu:["menu","hamburger","drawer","sidebar","options","navigation","\u2630"],add:["add","new","create","plus","+","insert"],edit:["edit","pencil","modify","update","rename","change"],calendar:["calendar","schedule","date","events","timetable"],refresh:["refresh","reload","sync","update"],info:["info","information","details","help","about"],filter:["filter","filters","sort","refine","funnel"],check:["check","done","tick","mark","select","\u2713","\u2714"],lock:["lock","security","privacy","padlock"],camera:["camera","photo","picture","capture","snapshot"],cart:["cart","shopping cart","bag","basket","checkout"],email:["email","username","user name","e-mail","mail","login ID","user id"],username:["username","user name","email","user id","handle"],password:["password","passcode","pin","pwd","secret"],phone:["phone","mobile","contact","telephone","cell","phone number"],name:["name","full name","first name","last name"],otp:["otp","verification code","code","pin"],submit:["submit","save","continue","confirm","proceed","done","apply","ok"],save:["save","submit","done","apply","store","update"],cancel:["cancel","abort","discard","close","dismiss"],confirm:["confirm","yes","proceed","ok","agree","accept"],login:["sign in","signin","log in","login","enter"],signin:["sign in","signin","log in","login","enter"],logout:["sign out","signout","log out","logout","exit"],signout:["sign out","signout","log out","logout","exit"]},es:{bell:["campana","notificaci\xF3n","notificaciones","alerta","alertas","aviso","avisos"],notification:["notificaci\xF3n","notificaciones","campana","alerta","alertas","aviso","avisos","notification","notifications"],close:["cerrar","cancelar","quitar","salir","descartar","\u2715","\xD7","x","close"],like:["me gusta","favorito","favoritos","coraz\xF3n","like"],dislike:["no me gusta","desaprobar"],search:["buscar","b\xFAsqueda","lupa","explorar","encontrar","search"],settings:["ajustes","configuraci\xF3n","preferencias","opciones","settings"],profile:["perfil","cuenta","usuario","mi perfil","avatar","profile"],trash:["papelera","eliminar","borrar","descartar","quitar"],delete:["eliminar","borrar","quitar","papelera","descartar","delete"],share:["compartir","enviar","difundir","share"],back:["atr\xE1s","volver","regresar","anterior","flecha atr\xE1s","back"],menu:["men\xFA","opciones","navegaci\xF3n","\u2630","menu"],add:["a\xF1adir","agregar","nuevo","crear","m\xE1s","+","add"],edit:["editar","modificar","cambiar","actualizar","edit"],calendar:["calendario","fecha","eventos","horario","calendar"],refresh:["actualizar","recargar","sincronizar","refresh"],info:["informaci\xF3n","detalles","ayuda","acerca de","info"],filter:["filtrar","filtros","ordenar","filter"],check:["listo","hecho","marcar","seleccionar","\u2713","\u2714","check"],lock:["bloqueo","seguridad","privacidad","candado","lock"],camera:["c\xE1mara","foto","captura","imagen","camera"],cart:["carrito","cesta","bolsa","comprar","cart","checkout"],email:["correo","correo electr\xF3nico","email","usuario","mail"],username:["usuario","nombre de usuario","correo","username"],password:["contrase\xF1a","clave","pin","c\xF3digo","password"],phone:["tel\xE9fono","m\xF3vil","celular","contacto","n\xFAmero","phone"],name:["nombre","nombre completo","apellidos","name"],otp:["c\xF3digo de verificaci\xF3n","c\xF3digo","otp","pin"],submit:["enviar","guardar","confirmar","continuar","proceder","listo","aceptar","submit"],save:["guardar","salvar","almacenar","actualizar","save"],cancel:["cancelar","descartar","anular","abortar","cancel"],confirm:["confirmar","aceptar","s\xED","continuar","de acuerdo","confirm"],login:["iniciar sesi\xF3n","entrar","acceder","identificarse","ingresar","login","sign in"],signin:["iniciar sesi\xF3n","entrar","acceder","identificarse","ingresar","login","sign in"],logout:["cerrar sesi\xF3n","salir","desconectar","logout","sign out"],signout:["cerrar sesi\xF3n","salir","desconectar","logout","sign out"]},hi:{bell:["\u0918\u0902\u091F\u0940","\u0928\u094B\u091F\u093F\u092B\u093F\u0915\u0947\u0936\u0928","\u0938\u0942\u091A\u0928\u093E\u090F\u0902","alert","reminders"],notification:["\u0928\u094B\u091F\u093F\u092B\u093F\u0915\u0947\u0936\u0928","\u0938\u0942\u091A\u0928\u093E\u090F\u0902","alert","reminders","notification"],close:["\u092C\u0902\u0926 \u0915\u0930\u0947\u0902","\u0939\u091F\u093E\u090F\u0902","\u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902","\u2715","\xD7","x","close"],like:["\u092A\u0938\u0902\u0926","\u0932\u093E\u0907\u0915","\u092A\u0938\u0902\u0926\u0940\u0926\u093E"],dislike:["\u0928\u093E\u092A\u0938\u0902\u0926"],search:["\u0916\u094B\u091C\u0947\u0902","\u0922\u0942\u0902\u0922\u0947\u0902","\u0938\u0930\u094D\u091A","explore","search"],settings:["\u0938\u0947\u091F\u093F\u0902\u0917\u094D\u0938","\u0935\u093F\u0915\u0932\u094D\u092A","\u092A\u094D\u0930\u093E\u0925\u092E\u093F\u0915\u0924\u093E\u090F\u0902","settings"],profile:["\u092A\u094D\u0930\u094B\u092B\u093C\u093E\u0907\u0932","\u0916\u093E\u0924\u093E","\u0909\u092A\u092F\u094B\u0917\u0915\u0930\u094D\u0924\u093E","\u0905\u0915\u093E\u0909\u0902\u091F","profile"],trash:["\u0939\u091F\u093E\u090F\u0902","\u0921\u093F\u0932\u0940\u091F","\u0915\u091A\u0930\u093E"],delete:["\u0939\u091F\u093E\u090F\u0902","\u0921\u093F\u0932\u0940\u091F","\u092E\u093F\u091F\u093E\u090F\u0902","delete"],share:["\u0936\u0947\u092F\u0930 \u0915\u0930\u0947\u0902","\u092D\u0947\u091C\u0947\u0902","share"],back:["\u0935\u093E\u092A\u0938","\u092A\u0940\u091B\u0947","\u0935\u093E\u092A\u0938 \u091C\u093E\u090F\u0902","back"],menu:["\u092E\u0947\u0928\u0942","\u0935\u093F\u0915\u0932\u094D\u092A","\u2630","menu"],add:["\u091C\u094B\u0921\u093C\u0947\u0902","\u0928\u092F\u093E","\u092C\u0928\u093E\u090F\u0902","+","add"],edit:["\u0938\u0902\u092A\u093E\u0926\u093F\u0924 \u0915\u0930\u0947\u0902","\u092C\u0926\u0932\u0947\u0902","\u090F\u0921\u093F\u091F","edit"],calendar:["\u0915\u0948\u0932\u0947\u0902\u0921\u0930","\u0924\u093E\u0930\u0940\u0916","\u0938\u092E\u092F \u0938\u093E\u0930\u093F\u0923\u0940"],refresh:["\u0930\u093F\u092B\u094D\u0930\u0947\u0936","\u0924\u093E\u091C\u093C\u093E \u0915\u0930\u0947\u0902","\u0905\u092A\u0921\u0947\u091F \u0915\u0930\u0947\u0902"],info:["\u091C\u093E\u0928\u0915\u093E\u0930\u0940","\u0935\u093F\u0935\u0930\u0923","\u092E\u0926\u0926","info"],filter:["\u092B\u093C\u093F\u0932\u094D\u091F\u0930","\u091B\u093E\u0902\u091F\u0947\u0902","filter"],check:["\u0939\u094B \u0917\u092F\u093E","\u091A\u0941\u0928\u0947\u0902","\u0938\u0939\u0940","\u2713","\u2714"],cart:["\u0915\u093E\u0930\u094D\u091F","\u0925\u0948\u0932\u093E","\u091F\u094B\u0915\u0930\u0940","cart"],email:["\u0908\u092E\u0947\u0932","\u0908-\u092E\u0947\u0932","\u092F\u0942\u091C\u0930\u0928\u0947\u092E","email"],username:["\u0909\u092A\u092F\u094B\u0917\u0915\u0930\u094D\u0924\u093E \u0928\u093E\u092E","\u092F\u0942\u091C\u0930\u0928\u0947\u092E","\u0908\u092E\u0947\u0932","username"],password:["\u092A\u093E\u0938\u0935\u0930\u094D\u0921","\u092A\u093F\u0928","\u092A\u093E\u0938\u0915\u094B\u0921","password"],phone:["\u092B\u093C\u094B\u0928","\u092E\u094B\u092C\u093E\u0907\u0932","\u0928\u0902\u092C\u0930","\u0938\u0902\u092A\u0930\u094D\u0915","phone"],name:["\u0928\u093E\u092E","\u092A\u0942\u0930\u093E \u0928\u093E\u092E","name"],otp:["\u0913\u091F\u0940\u092A\u0940","\u0938\u0924\u094D\u092F\u093E\u092A\u0928 \u0915\u094B\u0921","\u092A\u093F\u0928","otp"],submit:["\u091C\u092E\u093E \u0915\u0930\u0947\u0902","\u0938\u092C\u092E\u093F\u091F","\u092A\u0941\u0937\u094D\u091F\u093F \u0915\u0930\u0947\u0902","\u0906\u0917\u0947 \u092C\u0922\u093C\u0947\u0902","submit"],save:["\u0938\u0939\u0947\u091C\u0947\u0902","\u0938\u0947\u0935 \u0915\u0930\u0947\u0902","\u0938\u0941\u0930\u0915\u094D\u0937\u093F\u0924 \u0915\u0930\u0947\u0902","save"],cancel:["\u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902","\u0915\u0948\u0902\u0938\u093F\u0932","cancel"],confirm:["\u092A\u0941\u0937\u094D\u091F\u093F \u0915\u0930\u0947\u0902","\u0938\u094D\u0935\u0940\u0915\u093E\u0930 \u0915\u0930\u0947\u0902","\u0939\u093E\u0901","\u0920\u0940\u0915 \u0939\u0948","confirm"],login:["\u0932\u0949\u0917 \u0907\u0928","\u0932\u0949\u0917\u093F\u0928","\u092A\u094D\u0930\u0935\u0947\u0936 \u0915\u0930\u0947\u0902","\u0938\u093E\u0907\u0928 \u0907\u0928","login","sign in"],signin:["\u0932\u0949\u0917 \u0907\u0928","\u0932\u0949\u0917\u093F\u0928","\u0938\u093E\u0907\u0928 \u0907\u0928","\u092A\u094D\u0930\u0935\u0947\u0936 \u0915\u0930\u0947\u0902","login","sign in"],logout:["\u0932\u0949\u0917 \u0906\u0909\u091F","\u0938\u093E\u0907\u0928 \u0906\u0909\u091F","\u092C\u093E\u0939\u0930 \u0928\u093F\u0915\u0932\u0947\u0902","logout"],signout:["\u0932\u0949\u0917 \u0906\u0909\u091F","\u0938\u093E\u0907\u0928 \u0906\u0909\u091F","\u092C\u093E\u0939\u0930 \u0928\u093F\u0915\u0932\u0947\u0902","logout"]}},$o=Ft.en,ts=new Map;function Jr(a){let e=new Map;for(let[t,s]of Object.entries(a)){let n=[ae(t),...s.map(i=>ae(i))];for(let i of n){e.has(i)||e.set(i,[]);let o=e.get(i);for(let l of n)o.includes(l)||o.push(l)}}return e}for(let[a,e]of Object.entries(Ft))ts.set(a.toLowerCase(),Jr(e));function Co(a,e){let t=a.toLowerCase();Ft[t]={...Ft[t]||{},...e},ts.set(t,Jr(Ft[t]))}function No(){return Object.keys(Ft)}function Qt(a,e="en"){let t=ae(a),s=(e||"en").toLowerCase(),r=ts.get(s);if(r){let n=r.get(t);if(n&&n.length>0)return n}if(s!=="en"){let n=ts.get("en");if(n){let i=n.get(t);if(i&&i.length>0)return i}}return[t]}var Vr=Qt;Fe();var pt=class a{memoryDir;currentPackage="global";mappings=new Map;screens=new Map;intents=new Map;dirty=!1;locale="en";setLocale(e){this.locale=(e||"en").toLowerCase()}getLocale(){return this.locale}constructor(e){if(e)this.memoryDir=e;else{let t=tt.dirname(an(import.meta.url));this.memoryDir=tt.resolve(t,"..","memory")}Ie.existsSync(this.memoryDir)||Ie.mkdirSync(this.memoryDir,{recursive:!0})}cleanPackageName(e){return(e||"global").replace(/[^a-zA-Z0-9._-]/g,"_")}static SCHEMA_VERSION=2;async init(e){this.currentPackage=this.cleanPackageName(e),this.mappings.clear(),this.screens.clear(),this.intents.clear(),this.dirty=!1;let t=tt.join(this.memoryDir,`${this.currentPackage}.json`);if(Ie.existsSync(t))try{let s=await Ie.promises.readFile(t,"utf-8"),r=JSON.parse(s);if(r&&r.mappings){let n=!r.version||r.version<2;for(let[i,o]of Object.entries(r.mappings))n&&(o.totalSuccesses=o.totalSuccesses??o.successCount??1,o.recentOutcomes=o.recentOutcomes??(o.failCount>0?["FAIL"]:["PASS"]),o.lastSeenTs=o.lastSeenTs??o.lastUpdated??new Date().toISOString(),this.dirty=!0),this.mappings.set(i,o);if(r.screens)for(let[i,o]of Object.entries(r.screens))this.screens.set(i,o);if(r.intents)for(let[i,o]of Object.entries(r.intents))this.intents.set(i,o);this.decayStaleMappings()}else this.mappings.clear(),this.screens.clear(),this.intents.clear()}catch{this.mappings.clear(),this.screens.clear(),this.intents.clear()}}getCanonicalQuery(e){let t=ae(e),s=Qt(t,this.locale);return s&&s.length>0?s[0]:t}buildKey(e,t){return`${e.toUpperCase()}:${this.getCanonicalQuery(t)}`}resolve(e,t){let s=this.buildKey(e,t),r=this.mappings.get(s);if(r)return r;let n=`${e.toUpperCase()}:${ae(t)}`,i=this.mappings.get(n);if(i)return i;let o=Qt(t,this.locale);for(let l of o){let c=`${e.toUpperCase()}:${l}`,p=this.mappings.get(c);if(p)return p}}getAllCandidates(e,t,s,r){let n=(s||this.locale||"en").toLowerCase(),i=new Set;if(r){let c=this.getScreenElements(r),p=this.getCanonicalQuery(t);for(let d of c)d.action===e.toUpperCase()&&(d.canonicalIntent===p||d.canonicalIntent===ae(t))&&d.confidence>=.7&&i.add(d.matchedIdentifier)}let o=this.resolve(e,t);o&&o.confidence>=.8&&i.add(o.matchedIdentifier),i.add(t);let l=Qt(t,n);for(let c of l)i.add(c);return Array.from(i)}recordSuccess(e,t,s,r,n=1,i,o){if(s.length>35||s.includes("?")||s.includes(`
826
+ `))return;let l=this.buildKey(e,t),c=this.mappings.get(l),p=new Date().toISOString(),d=i||c?.healedFrom?I.memory?.maxHealedConfidence??.65:1,u=i||c?.healedFrom?Math.min(n,d):n;c&&c.matchedIdentifier===s?(c.successCount+=1,c.totalSuccesses=(c.totalSuccesses||0)+1,c.failCount=0,c.recentOutcomes||(c.recentOutcomes=[]),c.recentOutcomes.push("PASS"),c.recentOutcomes.length>5&&c.recentOutcomes.shift(),c.confidence=Math.min(d,Number((c.confidence+.05).toFixed(2))),c.lastUpdated=p,c.lastSeenTs=p,i&&(c.healedFrom=i)):this.mappings.set(l,{canonicalIntent:this.getCanonicalQuery(t),action:e.toUpperCase(),matchedIdentifier:s,matchType:r,confidence:u,successCount:1,failCount:0,totalSuccesses:1,recentOutcomes:["PASS"],lastUpdated:p,lastSeenTs:p,healedFrom:i}),o&&this.associateScreenElement(o,l),this.dirty=!0}associateScreenElement(e,t){if(!e||!t)return;let s=new Date().toISOString(),r=this.screens.get(e);r?(r.elements.includes(t)||r.elements.push(t),r.lastSeenTs=s):this.screens.set(e,{lastSeenTs:s,elements:[t]}),this.dirty=!0}getScreenElements(e){let t=this.screens.get(e);return t?t.elements.map(s=>this.mappings.get(s)).filter(s=>s!==void 0):[]}getScreenIndex(){return Object.fromEntries(this.screens.entries())}markScreenCrawled(e,t){if(!e)return;let s=new Date().toISOString(),r=this.screens.get(e);if(r){if(r.crawled=!0,r.crawledAt=s,r.lastSeenTs=s,t&&t.length>0){r.safeActions||(r.safeActions=[]);for(let n of t)r.safeActions.includes(n)||r.safeActions.push(n)}}else this.screens.set(e,{lastSeenTs:s,elements:[],crawled:!0,crawledAt:s,safeActions:t?[...t]:[]});this.dirty=!0}isScreenCrawled(e){return e?!!this.screens.get(e)?.crawled:!1}getScreenMemory(e){return this.screens.get(e)}getCrawledScreensCount(){let e=0;for(let t of this.screens.values())t.crawled&&e++;return e}recordIntentExpansion(e,t){if(!e||!t||t.length===0)return;let s=ae(e),r=new Date().toISOString(),n=this.intents.get(s);n?(n.steps=[...t],n.usageCount=(n.usageCount||1)+1,n.learnedAt=r):this.intents.set(s,{intent:e,steps:[...t],learnedAt:r,usageCount:1}),this.dirty=!0}getIntentExpansion(e){if(!e)return;let t=ae(e),s=this.intents.get(t);if(s)return s.usageCount=(s.usageCount||0)+1,this.dirty=!0,[...s.steps]}hasIntentExpansion(e){return e?this.intents.has(ae(e)):!1}getIntentExpansionsCount(){return this.intents.size}recordFailure(e,t){let s=this.buildKey(e,t),r=this.mappings.get(s);r&&(r.failCount+=1,r.recentOutcomes||(r.recentOutcomes=[]),r.recentOutcomes.push("FAIL"),r.recentOutcomes.length>5&&r.recentOutcomes.shift(),r.confidence=Math.max(.1,Number((r.confidence-.25).toFixed(2))),r.lastUpdated=new Date().toISOString(),(r.recentOutcomes.filter(i=>i==="FAIL").length>=3||r.confidence<.2)&&this.mappings.delete(s),this.dirty=!0)}decayStaleMappings(e){let s=(e??I.memory?.staleThresholdDays??14)*24*60*60*1e3,r=Date.now(),n=0;for(let i of this.mappings.values()){let o=i.lastSeenTs?new Date(i.lastSeenTs).getTime():0;if(o>0&&r-o>s){let l=I.memory?.staleDecayAmount??.2,c=I.memory?.minConfidenceFloor??.2,p=Math.max(c,Number((i.confidence-l).toFixed(2)));p<i.confidence&&(i.confidence=p,n++,this.dirty=!0)}}return n}async save(){if(!this.dirty)return;let e={packageName:this.currentPackage,version:a.SCHEMA_VERSION,lastUpdated:new Date().toISOString(),mappings:Object.fromEntries(this.mappings.entries()),screens:Object.fromEntries(this.screens.entries()),intents:Object.fromEntries(this.intents.entries())},t=tt.join(this.memoryDir,`${this.currentPackage}.json`),s=`${t}.tmp.${Date.now()}`;await Ie.promises.writeFile(s,JSON.stringify(e,null,2),"utf-8");try{await Ie.promises.rename(s,t)}catch{await Ie.promises.copyFile(s,t),await Ie.promises.unlink(s).catch(()=>{})}this.dirty=!1}async exportMemory(e){let t=e?tt.resolve(e):tt.join(this.memoryDir,`${this.currentPackage}-export.json`),s=tt.dirname(t);Ie.existsSync(s)||Ie.mkdirSync(s,{recursive:!0});let r={packageName:this.currentPackage,version:a.SCHEMA_VERSION,lastUpdated:new Date().toISOString(),mappings:Object.fromEntries(this.mappings.entries()),screens:Object.fromEntries(this.screens.entries()),intents:Object.fromEntries(this.intents.entries())};return await Ie.promises.writeFile(t,JSON.stringify(r,null,2),"utf-8"),t}async importMemory(e){let t=tt.resolve(e);if(!Ie.existsSync(t))throw new Error(`Memory import source file not found: ${t}`);let s=await Ie.promises.readFile(t,"utf-8"),r=JSON.parse(s);if(!r||!r.mappings)throw new Error("Invalid memory file format: missing mappings object.");let n=0,i=new Date().toISOString();for(let[o,l]of Object.entries(r.mappings)){let c=this.mappings.get(o);(!c||l.confidence>=c.confidence)&&(this.mappings.set(o,{...l,totalSuccesses:l.totalSuccesses??l.successCount??1,recentOutcomes:l.recentOutcomes??["PASS"],lastSeenTs:l.lastSeenTs??l.lastUpdated??i,lastUpdated:i}),n++)}if(r.screens)for(let[o,l]of Object.entries(r.screens)){let c=this.screens.get(o);if(!c)this.screens.set(o,l);else{let p=new Set([...c.elements,...l.elements]),d=new Set([...c.safeActions||[],...l.safeActions||[]]);this.screens.set(o,{lastSeenTs:l.lastSeenTs||c.lastSeenTs,elements:Array.from(p),crawled:c.crawled||l.crawled,crawledAt:l.crawledAt||c.crawledAt,safeActions:Array.from(d)})}}if(r.intents)for(let[o,l]of Object.entries(r.intents)){let c=this.intents.get(o);(!c||(l.usageCount||0)>=(c.usageCount||0))&&this.intents.set(o,l)}return n>0&&(this.dirty=!0,await this.save()),n}getMappingsList(){return Array.from(this.mappings.values())}getStats(){let e=Array.from(this.mappings.values()),t=e.reduce((n,i)=>n+(i.totalSuccesses||i.successCount||0),0),s=e.length>0?Number((e.reduce((n,i)=>n+i.confidence,0)/e.length).toFixed(2)):0,r=0;for(let n of this.screens.values())n.crawled&&r++;return{totalMappings:this.mappings.size,totalScreens:this.screens.size,crawledScreens:r,totalIntents:this.intents.size,package:this.currentPackage,totalSuccesses:t,averageConfidence:s}}async clear(e){let t=this.cleanPackageName(e||this.currentPackage),s=tt.join(this.memoryDir,`${t}.json`);return Ie.existsSync(s)?(await Ie.promises.unlink(s),t===this.currentPackage&&(this.mappings.clear(),this.screens.clear(),this.intents.clear(),this.dirty=!1),!0):!1}},_t=new pt;Fe();import ze from"fs";import cn from"path";import ln from"os";var ks=new Map;function Et(a){let e=(a||"default").replace(/[^a-zA-Z0-9._-]/g,"_"),t=cn.join(ln.tmpdir(),`prompttest-${e}.lock`);if(ze.existsSync(t))try{let r=ze.readFileSync(t,"utf-8").trim(),n=parseInt(r,10);if(!isNaN(n)){if(n===process.pid)return{serial:a,lockFilePath:t,acquired:!0,pid:process.pid};try{throw process.kill(n,0),new Error(`Device "${a}" is locked by another PromptTest process (PID ${n}).
827
+ Wait for the other run to complete, or remove the lock file if it is stale: ${t}`)}catch(i){if(i.code==="ESRCH")try{ze.unlinkSync(t)}catch{}else throw i}}}catch(r){let n=r;if(n.message&&n.message.includes("locked by another"))throw n;try{ze.unlinkSync(t)}catch{}}try{ze.writeFileSync(t,process.pid.toString(),{flag:"wx"})}catch(r){throw r.code==="EEXIST"?new Error(`Device "${a}" was just acquired by a concurrent PromptTest process.`):r}let s=()=>{try{ze.existsSync(t)&&ze.readFileSync(t,"utf-8").trim()===process.pid.toString()&&ze.unlinkSync(t)}catch{}};return ks.set(t,s),process.once("exit",s),{serial:a,lockFilePath:t,acquired:!0,pid:process.pid}}function kt(a){if(a)try{let e=ks.get(a.lockFilePath);if(e)process.removeListener("exit",e),ks.delete(a.lockFilePath),e();else if(ze.existsSync(a.lockFilePath))try{ze.readFileSync(a.lockFilePath,"utf-8").trim()===process.pid.toString()&&ze.unlinkSync(a.lockFilePath)}catch{}}catch{}}import Ds from"chalk";var As={DEBUG:0,INFO:1,WARN:2,ERROR:3,SILENT:4},ss=class{currentLevel="INFO";constructor(e){let t=(process.env.LOG_LEVEL||"").toUpperCase();e?this.currentLevel=e:As[t]!==void 0?this.currentLevel=t:this.currentLevel="INFO"}setLevel(e){this.currentLevel=e}getLevel(){return this.currentLevel}isLevelEnabled(e){return As[this.currentLevel]<=As[e]}isJsonMode(){return(process.env.LOG_FORMAT||"").toLowerCase()==="json"}emitJson(e,...t){let s=t.map(n=>typeof n=="object"?JSON.stringify(n):String(n)).join(" "),r={ts:new Date().toISOString(),level:e,msg:s};e==="ERROR"||e==="WARN"?console.error(JSON.stringify(r)):console.log(JSON.stringify(r))}debug(...e){this.isLevelEnabled("DEBUG")&&(this.isJsonMode()?this.emitJson("DEBUG",...e):console.log(Ds.gray("[DEBUG]"),...e))}info(...e){this.isLevelEnabled("INFO")&&(this.isJsonMode()?this.emitJson("INFO",...e):console.log(...e))}warn(...e){this.isLevelEnabled("WARN")&&(this.isJsonMode()?this.emitJson("WARN",...e):console.warn(Ds.yellow("[WARN]"),...e))}error(...e){this.isLevelEnabled("ERROR")&&(this.isJsonMode()?this.emitJson("ERROR",...e):console.error(Ds.red("[ERROR]"),...e))}},qe=new ss;import ut from"fs";import zr from"path";var $s=class{checkpointDir;constructor(e){this.checkpointDir=e||zr.resolve(process.cwd(),"output","checkpoints"),ut.existsSync(this.checkpointDir)||ut.mkdirSync(this.checkpointDir,{recursive:!0})}filePath(e){let t=e.replace(/[^a-zA-Z0-9._-]/g,"_");return zr.join(this.checkpointDir,`${t}.checkpoint.json`)}async save(e,t,s,r){let n={specFile:e,packageName:t,lastPassedStep:s,totalSteps:r,savedAt:new Date().toISOString()},i=this.filePath(e)+`.tmp.${Date.now()}`;await ut.promises.writeFile(i,JSON.stringify(n,null,2),"utf-8"),await ut.promises.rename(i,this.filePath(e))}async load(e){let t=this.filePath(e);if(!ut.existsSync(t))return null;try{let s=await ut.promises.readFile(t,"utf-8");return JSON.parse(s)}catch{return null}}async clear(e){let t=this.filePath(e);ut.existsSync(t)&&await ut.promises.unlink(t)}async getResumeIndex(e){let t=await this.load(e);return t?t.lastPassedStep+1:0}},Kr=new $s;import xe from"fs";import ht from"path";import{PNG as Dt}from"pngjs";import dn from"pixelmatch";var Cs={strict:{threshold:.005,pixelmatchThreshold:.05,label:"Strict (0.5%)",description:"Pixel-perfect verification for Figma design-to-code compliance"},moderate:{threshold:.02,pixelmatchThreshold:.1,label:"Moderate (2.0%)",description:"Standard mobile QA matching"},relaxed:{threshold:.05,pixelmatchThreshold:.2,label:"Relaxed (5.0%)",description:"Lenient comparison for cross-device or font variations"}},gt=class{baselineDir;defaultThreshold;constructor(e,t=.02){this.baselineDir=e||ht.resolve(process.cwd(),"output","baselines"),this.defaultThreshold=t,xe.existsSync(this.baselineDir)||xe.mkdirSync(this.baselineDir,{recursive:!0})}filePath(e,t){let s=e.replace(/[^a-zA-Z0-9._-]/g,"_");return ht.join(this.baselineDir,`${s}_step${String(t).padStart(3,"0")}.png`)}diffFilePath(e,t){let s=e.replace(/[^a-zA-Z0-9._-]/g,"_");return ht.join(this.baselineDir,`${s}_step${String(t).padStart(3,"0")}_diff.png`)}masksFilePath(e,t){let s=e.replace(/[^a-zA-Z0-9._-]/g,"_");return ht.join(this.baselineDir,`${s}_step${String(t).padStart(3,"0")}_masks.json`)}async getMasks(e,t){let s=this.masksFilePath(e,t);if(!xe.existsSync(s))return[];try{let r=await xe.promises.readFile(s,"utf-8"),n=JSON.parse(r);return Array.isArray(n)?n:n.masks||[]}catch{return[]}}async saveMasks(e,t,s){let r=this.masksFilePath(e,t);return await xe.promises.writeFile(r,JSON.stringify(s,null,2),"utf-8"),r}async saveBaseline(e,t,s){let r=this.filePath(e,t),n=r+`.tmp.${Date.now()}`;return await xe.promises.writeFile(n,s),await xe.promises.rename(n,r),r}hasBaseline(e,t){return xe.existsSync(this.filePath(e,t))}async compare(e,t,s,r){let n=typeof r=="number"?{threshold:r}:r||{},i=this.filePath(e,t),o=ht.join(this.baselineDir,`${e.replace(/[^a-zA-Z0-9._-]/g,"_")}_step${String(t).padStart(3,"0")}_current.png`);await xe.promises.writeFile(o,s);let l=n.mode?Cs[n.mode]:void 0,c=n.threshold??l?.threshold??this.defaultThreshold,p=n.pixelmatchThreshold??l?.pixelmatchThreshold??.1;if(!xe.existsSync(i)){await this.saveBaseline(e,t,s);let U=Dt.sync.read(s),Z=U.width*U.height;return{stepName:`Step ${t}`,baselineFile:i,currentFile:o,diffRatio:0,diffPixels:0,totalPixels:Z,passed:!0,threshold:c}}let d=await xe.promises.readFile(i),u=Dt.sync.read(d),h=Dt.sync.read(s),g=Math.max(u.width,h.width),b=Math.max(u.height,h.height),T=g*b,x=this.fitToCanvas(u,g,b),S=this.fitToCanvas(h,g,b),y=n.maskRegions??await this.getMasks(e,t);y&&y.length>0&&(this.applyMasks(x,y),this.applyMasks(S,y));let P=new Dt({width:g,height:b}),$=dn(x.data,S.data,P.data,g,b,{threshold:p,includeAA:!1}),R=T>0?$/T:0,C=R<=c,B;if(C){let U=this.diffFilePath(e,t);if(xe.existsSync(U))try{await xe.promises.unlink(U)}catch{}}else{B=this.diffFilePath(e,t);let U=Dt.sync.write(P);await xe.promises.writeFile(B,U)}return{stepName:`Step ${t}`,baselineFile:i,currentFile:o,diffRatio:R,diffPixels:$,totalPixels:T,passed:C,threshold:c,diffImagePath:B}}fitToCanvas(e,t,s){if(e.width===t&&e.height===s)return e;let r=new Dt({width:t,height:s});return r.data.fill(0),Dt.bitblt(e,r,0,0,e.width,e.height,0,0),r}applyMasks(e,t){for(let s of t){let r=s.width<=1&&s.height<=1||s.x<=1&&s.y<=1&&(s.width<1||s.height<1),n=Math.round(r?s.x*e.width:s.x),i=Math.round(r?s.y*e.height:s.y),o=Math.round(r?s.width*e.width:s.width),l=Math.round(r?s.height*e.height:s.height),c=Math.max(0,i),p=Math.min(e.height,i+l),d=Math.max(0,n),u=Math.min(e.width,n+o);for(let h=c;h<p;h++)for(let g=d;g<u;g++){let b=e.width*h+g<<2;e.data[b]=0,e.data[b+1]=0,e.data[b+2]=0,e.data[b+3]=0}}}async approveBaseline(e,t,s){let r;if(s)r=await this.saveBaseline(e,t,s);else{let i=e.replace(/[^a-zA-Z0-9._-]/g,"_"),o=ht.join(this.baselineDir,`${i}_step${String(t).padStart(3,"0")}_current.png`);if(!xe.existsSync(o))throw new Error(`No current screenshot found to approve for step ${t}`);let l=await xe.promises.readFile(o);r=await this.saveBaseline(e,t,l)}let n=this.diffFilePath(e,t);if(xe.existsSync(n))try{await xe.promises.unlink(n)}catch{}return r}async clearBaselines(e){let t=e.replace(/[^a-zA-Z0-9._-]/g,"_");if(!xe.existsSync(this.baselineDir))return;let s=await xe.promises.readdir(this.baselineDir);await Promise.all(s.filter(r=>r.startsWith(t+"_step")).map(r=>xe.promises.unlink(ht.join(this.baselineDir,r)).catch(()=>{})))}async deleteStepBaseline(e,t){let r=`${e.replace(/[^a-zA-Z0-9._-]/g,"_")}_step${String(t).padStart(3,"0")}`;if(!xe.existsSync(this.baselineDir))return;let n=await xe.promises.readdir(this.baselineDir);await Promise.all(n.filter(i=>i.startsWith(r)).map(i=>xe.promises.unlink(ht.join(this.baselineDir,i)).catch(()=>{})))}},Ns=new gt;Fe();import pe from"chalk";import ii from"vm";import Ht from"chalk";var pn=["com.miui.home","com.mi.android.globallauncher","com.sec.android.app.launcher","com.sec.android.app.twlauncher","com.google.android.apps.nexuslauncher","com.android.launcher3","com.android.launcher","com.oppo.launcher","com.coloros.launcher","com.bbk.launcher2","com.vivo.launcher","com.huawei.android.launcher","net.oneplus.launcher","com.motorola.launcher3","org.lineageos.trebuchet","com.teslacoilsw.launcher","com.actionlauncher.playstore","com.transsion.hilauncher","com.nothing.launcher"],un=["android","com.android.systemui","com.google.android.permissioncontroller","com.android.permissioncontroller","com.android.packageinstaller","com.google.android.packageinstaller","com.google.android.inputmethod.latin","com.samsung.android.honeyboard","com.touchtype.swiftkey","com.miui.virtualsim","com.google.android.gms","com.android.settings"];function At(a){if(!a)return!1;let e=a.toLowerCase();return pn.some(t=>e===t.toLowerCase()||e.includes(t.toLowerCase()))?!0:e.includes("launcher")||e.includes(".home")||e.endsWith(".home")||e.includes("trebuchet")||e.includes("desktop")}function Is(a){if(!a)return!1;let e=a.toLowerCase();return un.some(t=>e===t.toLowerCase()||e.startsWith(t.toLowerCase()))}function rs(a,e){if(!a||!e)return!0;let t=a.toLowerCase(),s=e.toLowerCase();return!!(t===s||t.startsWith(`${s}.`)||Is(a))}function Ls(a){if(!a||a.length===0)return null;for(let e of a)if(e.packageName&&(e.clickable||e.focused||e.scrollable)&&!Is(e.packageName))return e.packageName;for(let e of a)if(e.packageName&&!Is(e.packageName))return e.packageName;return a[0]?.packageName||null}async function Ms(a,e,t,s){if(!e)return{escaped:!1,recovered:!0,currentPackage:null};let r=Ls(a);if(!r)return{escaped:!1,recovered:!0,currentPackage:null};if(rs(r,e))return{escaped:!1,recovered:!0,currentPackage:r};let n=At(r);console.log(Ht.yellow(`
828
+ \u{1F6E1}\uFE0F [Jail Guard] Boundary Violation Detected: Active screen belongs to "${r}" (not target "${e}").`)),console.log(n?Ht.cyan(` [Jail Guard] Device is on home launcher. Suppressing back-press to prevent exit loops. Re-launching "${e}"...`):Ht.cyan(` [Jail Guard] Foreign app "${r}" intercepted. Re-launching target "${e}" to restore containment...`));try{await t.launchApp(e,s),await new Promise(l=>setTimeout(l,1200));let i=await t.getForegroundPackage(s),o=i?i.includes(e):!0;return console.log(o?Ht.green(` \u2714 [Jail Guard] Containment restored: "${e}" is now in foreground.`):Ht.red(` \u274C [Jail Guard] Recovery failed. Current foreground is still "${i}".`)),{escaped:!0,recovered:o,currentPackage:r}}catch(i){return console.log(Ht.red(` \u274C [Jail Guard] Error during containment recovery: ${i.message}`)),{escaped:!0,recovered:!1,currentPackage:r}}}It();import st from"fs";import Be from"path";import ge from"chalk";import hn from"zlib";import gn from"fs";function mn(a){let e=-1;for(let t=0;t<a.length;t++){let s=a[t];e^=s;for(let r=0;r<8;r++)e=e>>>1^-(e&1)&3988292384}return~e>>>0}function fn(a=new Date){let e=(a.getHours()&31)<<11|(a.getMinutes()&63)<<5|Math.floor(a.getSeconds()/2)&31,t=(a.getFullYear()-1980&127)<<9|(a.getMonth()+1&15)<<5|a.getDate()&31;return{time:e,date:t}}function bn(a){let e=[],t=[],s=0;for(let l of a){let c=l.data,p=hn.deflateRawSync(c),d=p.length<c.length,u=d?p:c,h=d?8:0,g=mn(c),b=c.length,T=u.length,{time:x,date:S}=fn(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(x,10),P.writeUInt16LE(S,12),P.writeUInt32LE(g,14),P.writeUInt32LE(T,18),P.writeUInt32LE(b,22),P.writeUInt16LE(y.length,26),P.writeUInt16LE(0,28),y.copy(P,30),e.push(P),e.push(u);let $=Buffer.alloc(46+y.length);$.writeUInt32LE(33639248,0),$.writeUInt16LE(20,4),$.writeUInt16LE(20,6),$.writeUInt16LE(2048,8),$.writeUInt16LE(h,10),$.writeUInt16LE(x,12),$.writeUInt16LE(S,14),$.writeUInt32LE(g,16),$.writeUInt32LE(T,20),$.writeUInt32LE(b,24),$.writeUInt16LE(y.length,28),$.writeUInt16LE(0,30),$.writeUInt16LE(0,32),$.writeUInt16LE(0,34),$.writeUInt16LE(0,36),$.writeUInt32LE(0,38),$.writeUInt32LE(s,42),y.copy($,46),t.push($),s+=P.length+u.length}let r=Buffer.concat(t),n=r.length,i=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(n,12),o.writeUInt32LE(i,16),o.writeUInt16LE(0,20),Buffer.concat([...e,r,o])}async function Xr(a,e){let t=bn(e);await gn.promises.writeFile(a,t)}function Rs(a,e=new Set){let t=Be.resolve(a);if(!st.existsSync(t))throw new Error(`Included spec file not found: ${t}`);if(e.has(t)){let i=Array.from(e).concat(t);throw new Jt(`Circular spec inclusion detected: ${i.join(" -> ")}`,i)}e.add(t);let r=st.readFileSync(t,"utf-8").split(`
829
+ `),n=[];for(let i of r){let o=i.trim(),l=o.match(te.INCLUDE_SPEC);if(l&&!o.startsWith("#")){let c=ce([l[1],l[2],l[3]]),p=Be.isAbsolute(c)?c:Be.resolve(Be.dirname(t),c),d=Rs(p,new Set(e));n.push(d)}else n.push(i)}return n.join(`
830
+ `)}function yn(a){let e=[],t=[];for(let s of a){if(s._isControl==="END"){t.length>0&&t.pop();continue}if(s._isControl==="ELSE"){t.length>0&&t[t.length-1].step.type==="IF_VISIBLE"&&(t[t.length-1].inElse=!0);continue}let r=e;if(t.length>0){let n=t[t.length-1];n.step.type==="IF_VISIBLE"?n.inElse?(n.step.elseSteps=n.step.elseSteps||[],r=n.step.elseSteps):(n.step.thenSteps=n.step.thenSteps||[],r=n.step.thenSteps):n.step.type==="LOOP"&&(n.step.childSteps=n.step.childSteps||[],r=n.step.childSteps)}r.push(s),(s.type==="IF_VISIBLE"||s.type==="LOOP")&&t.push({step:s,inElse:!1})}return e}function mt(a,e){let t=a.split(`
831
+ `).map(r=>r.trim()).filter(r=>r.length>0&&!r.startsWith("#")),s=[];for(let r of t){let n=Pt(r,e),i=n.replace(Mr,"").trim(),o=te.OPTIONAL_SUFFIX.test(i)||/^[(\[]\s*optional\s*[)\]]/i.test(i)||/^optional\s*:/i.test(i),l=i.replace(te.OPTIONAL_SUFFIX,"").replace(/^[(\[]\s*optional\s*[)\]]\s*/i,"").replace(/^optional\s*:\s*/i,"").trim();if(te.BACK.test(l)){s.push({originalText:n,type:"BACK",optional:o});continue}let c=l.match(te.WAIT);if(c){let v=parseInt(c[1],10),A=l.toLowerCase().includes("ms")?v:v*1e3;s.push({originalText:n,type:"WAIT",durationMs:A,optional:o});continue}let p=l.match(te.WAIT_UNTIL);if(p){let v=ce([p[1],p[2],p[3]]),k=(p[4]||"visible").toLowerCase().includes("enable")?"enabled":"visible",F=p[5]?parseInt(p[5],10):void 0,N=(p[6]||"s").toLowerCase(),H=F?N.includes("ms")?F:F*1e3:void 0;s.push({originalText:n,type:"WAIT_UNTIL",target:v,expectedState:k,durationMs:H,optional:o});continue}let d=l.match(te.IF_VISIBLE);if(d){let v=ce([d[1],d[2],d[3],d[5],d[6],d[7]]);s.push({originalText:n,type:"IF_VISIBLE",conditionTarget:v,thenSteps:[],elseSteps:[],optional:o});continue}if(te.ELSE.test(l)){s.push({originalText:n,type:"IF_VISIBLE",_isControl:"ELSE"});continue}if(te.END.test(l)){s.push({originalText:n,type:"IF_VISIBLE",_isControl:"END"});continue}let u=l.match(te.LOOP);if(u){let v=parseInt(u[1],10);s.push({originalText:n,type:"LOOP",loopCount:v,childSteps:[],optional:o});continue}let h=l.match(te.FOR_EACH);if(h){let v=ce([h[1],h[2],h[3]]);s.push({originalText:n,type:"LOOP",target:v,loopVariable:"item",childSteps:[],optional:o});continue}let g=l.match(te.SET_VARIABLE);if(g){let v=g[1].trim(),A=g[2].trim();s.push({originalText:n,type:"SET_VARIABLE",variableName:v,variableExpression:A,optional:o});continue}let b=l.match(te.DEVICE_WIFI);if(b){let v=b[1].toLowerCase();s.push({originalText:n,type:"DEVICE_STATE",deviceAction:"wifi",deviceStateValue:v==="on"||v==="enable",optional:o});continue}let T=l.match(te.DEVICE_AIRPLANE);if(T){let v=T[1].toLowerCase();s.push({originalText:n,type:"DEVICE_STATE",deviceAction:"airplane",deviceStateValue:v==="on"||v==="enable",optional:o});continue}let x=l.match(te.DEVICE_ORIENTATION);if(x){let v=x[1].toLowerCase();s.push({originalText:n,type:"DEVICE_STATE",deviceAction:"orientation",deviceStateValue:v,optional:o});continue}let S=l.match(te.DEVICE_NOTIFICATIONS);if(S){let v=S[1].toLowerCase(),A=v.startsWith("open")||v.startsWith("expand")||v.startsWith("pull");s.push({originalText:n,type:"DEVICE_STATE",deviceAction:"notifications",deviceStateValue:A?"open":"collapse",optional:o});continue}let y=l.match(te.RUN_JS);if(y){let v=y[1].trim();s.push({originalText:n,type:"RUN_JS",jsCode:v,optional:o});continue}let P=l.match(te.ENTER_OTP);if(P){let v=ce([P[1],P[2],P[3]])||void 0;s.push({originalText:n,type:"ENTER_OTP",target:v,optional:o});continue}let $=l.match(te.API_REQUEST);if($){let v=$[1].toUpperCase(),A=ce([$[2],$[3],$[4]]),k=ce([$[5],$[6],$[7]])||void 0,F=ce([$[8],$[9],$[10]])||void 0,N=$[11]||void 0,H=$[12]?parseInt($[12],10):void 0;s.push({originalText:n,type:"API_REQUEST",apiMethod:v,apiUrl:A,apiBody:k,apiStorePath:F,apiStoreVariable:N,apiExpectedStatus:H,optional:o});continue}let R=l.match(te.EMAIL_ASSERT);if(R){let v=ce([R[1],R[2],R[3]]),A=ce([R[4],R[5],R[6]]),k=ce([R[7],R[8],R[9]])||void 0;s.push({originalText:n,type:"EMAIL_ASSERT",emailRecipient:v,emailSubject:A,emailBodyContains:k,optional:o});continue}let C=l.match(te.LAUNCH_APP);if(C){let v=ce([C[1],C[2],C[3]]);s.push({originalText:n,type:"LAUNCH_APP",target:v,optional:o});continue}let B=l.match(te.RESTART_APP);if(B){let v=ce([B[1],B[2],B[3]]);s.push({originalText:n,type:"RESTART_APP",target:v,optional:o});continue}let U=l.match(te.TERMINATE_APP);if(U){let v=ce([U[1],U[2],U[3]])||void 0;s.push({originalText:n,type:"TERMINATE_APP",target:v,optional:o});continue}let Z=l.match(te.CLEAR_DATA);if(Z){let v=ce([Z[1],Z[2],Z[3]]);s.push({originalText:n,type:"CLEAR_DATA",target:v,optional:o});continue}let W=l.match(te.GRANT_PERMISSION);if(W){let v=ce([W[1],W[2],W[3]]),A=ce([W[4],W[5],W[6]])||void 0;s.push({originalText:n,type:"GRANT_PERMISSION",value:v,target:A,optional:o});continue}let J=l.match(te.DEEP_LINK);if(J){let v=ce([J[1],J[2],J[3]]);s.push({originalText:n,type:"DEEP_LINK",target:v,optional:o});continue}let Ee=l.match(te.DIRECTIONAL_SWIPE);if(Ee){let v=Ee[1].toLowerCase(),A=ce([Ee[2],Ee[3],Ee[4]])||void 0;s.push({originalText:n,type:"SWIPE",direction:v,target:A,optional:o});continue}let se=l.match(te.TOGGLE);if(se){let v=(se[1]||"").toLowerCase(),A=ce([se[2],se[3],se[4]]),k=l.toLowerCase().startsWith("uncheck")||v==="off"?!1:v==="on"||l.toLowerCase().startsWith("check")?!0:void 0;s.push({originalText:n,type:"TOGGLE",target:A,desiredState:k,optional:o});continue}let j=l.match(te.SELECT_DROPDOWN);if(j){let v=ce([j[1],j[2],j[3]]),A=ce([j[4],j[5],j[6]]);s.push({originalText:n,type:"SELECT",value:v,target:A,optional:o});continue}let ie=l.match(te.SCROLL_TO);if(ie){let v=(ie[1]||"down").toLowerCase(),A=ce([ie[2],ie[3],ie[4]]);s.push({originalText:n,type:"SCROLL",target:A,direction:v,optional:o});continue}let w=l.match(te.SCROLL);if(w){let v=w[1].toLowerCase();s.push({originalText:n,type:"SCROLL",target:v,direction:v,optional:o});continue}let f=l.match(te.LONG_PRESS);if(f){let v=ce([f[1],f[2],f[3]]);s.push({originalText:n,type:"LONG_PRESS",target:v,optional:o});continue}let O=l.match(te.ORDINAL_TAP);if(O){let v=O[1].toLowerCase(),A=ce([O[2],O[3],O[4]]);s.push({originalText:n,type:"TAP",target:A,ordinal:v,optional:o});continue}let D=l.match(te.PROXIMITY_TAP);if(D){let v=ce([D[1],D[2],D[3]]),A=ce([D[4],D[5],D[6]]);s.push({originalText:n,type:"TAP",target:v,relativeTo:A,optional:o});continue}let we=l.match(te.TYPE);if(we){let v=ce([we[1],we[2],we[3]]),A=ce([we[4],we[5],we[6]]);s.push({originalText:n,type:"TYPE",value:v,target:A,optional:o});continue}let ue=l.match(te.CLEAR);if(ue){let v=ce([ue[1],ue[2],ue[3]]);s.push({originalText:n,type:"CLEAR",target:v,optional:o});continue}let m=l.match(te.VERIFY);if(m){let v=ce([m[1],m[2],m[3]]),A=(m[4]||"visible").toLowerCase(),k=A.includes("not visible")||A.includes("not present")||A.includes("absent")||A.includes("hidden")||A.includes("gone"),F=k?"absent":A==="exists"||A==="enabled"||A==="disabled"?A:"visible";s.push({originalText:n,type:"VERIFY",target:v,expectedState:F,negative:k,optional:o});continue}let E=l.match(te.TAP);if(E&&!l.toLowerCase().includes("back")){let v=ce([E[1],E[2],E[3]]);s.push({originalText:n,type:"TAP",target:v,optional:o});continue}let L=l.match(te.FALLBACK_QUOTED_TAP);if(L&&(l.toLowerCase().startsWith("tap")||l.toLowerCase().startsWith("click"))){s.push({originalText:n,type:"TAP",target:L[1],optional:o});continue}qe.warn(`Unrecognized prompt step: "${r}". Skipping.`)}return yn(s)}function Qr(a,e,t){let s=e.map(i=>(i.text||i.contentDesc||"").trim()).filter(i=>i.length>1),r=Rt(a,s,3),n=r.length>0?`
802
832
  \u{1F4A1} Did you mean one of these visible items?
803
- `+i.map(n=>` \u2022 "${n.text}" (${Math.round(n.score*100)}% match)`).join(`
833
+ `+r.map(i=>` \u2022 "${i.text}" (${Math.round(i.score*100)}% match)`).join(`
804
834
  `):`
805
- \u{1F4A1} No similar text found on current screen.`;return new Error(`${t}${r}`)}function Li(a,e,t,s){let i=oe(e),r=i.includes("password")||i.includes("passcode")||i.includes("pin")||i.includes("pwd")||i.includes("secret"),n=i.includes("email")||i.includes("mail")||i.includes("user")||i.includes("login")||i.includes("username")||t&&t.includes("@"),o=a.filter(p=>p.className.includes("EditText")&&p.bounds.width>0);if(o.length===0)return null;let c=o.find(p=>pe(e,p.contentDesc)||pe(e,p.resourceId)||pe(e,p.text));if(c)return c;let l=a.filter(p=>!p.className.includes("EditText")&&(pe(e,p.text)||pe(e,p.contentDesc)||pe(e,p.resourceId))&&p.bounds.width>0);for(let p of l){let d=o.filter(h=>h.bounds.centerY>=p.bounds.centerY&&h.bounds.centerY-p.bounds.centerY<300&&Math.abs(h.bounds.centerX-p.bounds.centerX)<450);if(d.length>0)return d.sort((h,u)=>h.bounds.centerY-u.bounds.centerY),d[0]}if(s){let p=s.getAllCandidates("TYPE",e);for(let d of p){let h=o.find(u=>pe(d,u.contentDesc)||pe(d,u.resourceId)||pe(d,u.text));if(h)return h}}if(r){let p=o.find(d=>d.password);if(p)return p;if(o.length>1){let d=o.find(h=>h.text?.toLowerCase().includes("email")||h.contentDesc?.toLowerCase().includes("email"));if(d){let h=o.filter(u=>u.bounds.centerY>d.bounds.centerY);if(h.length>0)return h.sort((u,m)=>u.bounds.centerY-m.bounds.centerY),h[0]}}if(o.length>=2)return o[1]}if(n){let p=o.find(d=>!d.password);if(p)return p;if(o.length>0)return o[0]}return o.length===1?o[0]:null}function Qt(a,e,t,s="en",i,r){let n=i?.type||"TAP",o=oe(e),l=a.filter(u=>(oe(u.text)===o||oe(u.contentDesc)===o)&&u.bounds.width>0&&u.bounds.height>0).find(u=>u.clickable);if(l)return[l];let p=t.getAllCandidates(n,e,s,r),d=[];for(let u of p){let m=a.filter(y=>{let v=pe(u,y.text),T=pe(u,y.contentDesc),S=pe(u,y.resourceId);return(v||T||S)&&y.bounds.width>0&&y.bounds.height>0});if(m.length>0){d=m;break}}if(d.length===0){let u=[];for(let y of a)y.text&&y.text.trim().length>1&&u.push(y.text.trim()),y.contentDesc&&y.contentDesc.trim().length>1&&u.push(y.contentDesc.trim());let m=bt(e,u,1);if(m.length>0&&m[0].score>=.8){let y=m[0].text,v=a.filter(T=>(pe(y,T.text)||pe(y,T.contentDesc))&&T.bounds.width>0&&T.bounds.height>0);v.length>0&&(console.log(ge.yellow(` \u{1F4A1} [Self-Healing] Auto-resolved "${e}" \u2192 "${y}" (${Math.round(m[0].score*100)}% match)`)),d=v)}}if(d.length===0)return[];let h=d.filter(u=>{let m=oe(u.text),y=oe(u.contentDesc);return m===o||y===o});if(h.length>0)d=h;else{let u=d.filter(m=>{let y=oe(m.contentDesc);return y.endsWith(o)||y.includes(`, ${o}`)});u.length>0&&(d=u)}if(i?.relativeTo){let u=a.find(m=>pe(i.relativeTo,m.text)||pe(i.relativeTo,m.contentDesc));if(u){let m=d.filter(y=>Math.abs(y.bounds.centerY-u.bounds.centerY)<140).sort((y,v)=>{let T=Math.hypot(y.bounds.centerX-u.bounds.centerX,y.bounds.centerY-u.bounds.centerY),S=Math.hypot(v.bounds.centerX-u.bounds.centerX,v.bounds.centerY-u.bounds.centerY);return T-S});if(m.length>0)return m}}if(i?.ordinal){let u=i.ordinal.toLowerCase();if(u==="1st"||u==="first")return[d[0]];if(u==="2nd"&&d.length>1)return[d[1]];if(u==="3rd"&&d.length>2)return[d[2]];if(u==="4th"&&d.length>3)return[d[3]];if(u==="5th"&&d.length>4)return[d[4]];if(u==="last"&&d.length>0)return[d[d.length-1]]}return d}function be(a){if(!a||a.length===0)return"0";let e=2166136261;for(let t=0;t<a.length;t++){let s=a[t],i=`${s.text||""}|${s.contentDesc||""}|${s.resourceId||""}|${s.bounds?.x1||0},${s.bounds?.y1||0},${s.bounds?.width||0},${s.bounds?.height||0}`;for(let r=0;r<i.length;r++)e^=i.charCodeAt(r),e=Math.imul(e,16777619)}return(e>>>0).toString(16)}var Un=[/\b\d{1,2}:\d{2}(?::\d{2})?\s*(?:AM|PM|am|pm)?\b/g,/\b\d+\s*(?:sec|min|hr|hour|day|week|month|year)s?\s*ago\b/gi,/\b(?:just now|yesterday|today)\b/gi,/[\$€£₹]\s*\d+(?:\.\d{1,2})?/g,/(?:#|(?:\b(?:ID|No)[\s:-]*))[A-Z0-9_-]{3,}\b/gi,/\b\d{1,3}%\b/g];function Ii(a){if(!a)return"";let e=a;for(let t of Un)e=e.replace(t,"<VAL>");return e.trim()}function Oe(a,e){if(!a||a.length===0)return"0";let s=(e?.height||2400)*.04,i=2166136261;for(let r=0;r<a.length;r++){let n=a[r];if(n.bounds&&n.bounds.centerY<s)continue;let o=Ii(n.text||""),c=Ii(n.contentDesc||""),l=n.resourceId||"",p=n.className||"",d=n.clickable?"1":"0",h=n.checkable?"1":"0",u=Math.round((n.bounds?.x1||0)/50),m=Math.round((n.bounds?.y1||0)/50),y=Math.round((n.bounds?.width||0)/50),v=Math.round((n.bounds?.height||0)/50),T=`${p}|${l}|${o}|${c}|${d}|${h}|${u},${m},${y},${v}`;for(let S=0;S<T.length;S++)i^=T.charCodeAt(S),i=Math.imul(i,16777619)}return(i>>>0).toString(16)}function Ao(a,e){let t=be(a),s=be(e);return{hasChanged:t!==s,previousHash:t,currentHash:s,nodeCountDelta:e.length-a.length}}function Mi(a){let e=new Set;if(!a)return e;let t=a.split(",").map(s=>s.trim()).filter(Boolean);for(let s of t)if(s.includes("-")){let[i,r]=s.split("-").map(c=>c.trim()),n=parseInt(i,10),o=parseInt(r,10);if(!isNaN(n)&&!isNaN(o)){let[c,l]=n<=o?[n,o]:[o,n];for(let p=c;p<=l;p++)e.add(p)}}else{let i=parseInt(s,10);isNaN(i)||e.add(i)}return e}function Ri(a){let e=new Set,t=a.split(`
806
- `);for(let s of t){let i=s.trim();if(!i.startsWith("#"))continue;let r=i.match(/#\s*@tags?:\s*(.+)/i);if(r){r[1].split(",").map(o=>o.trim().toLowerCase()).filter(Boolean).forEach(o=>e.add(o));continue}let n=i.match(/#\s*@([a-zA-Z0-9_-]+)/);n&&n[1].toLowerCase()!=="tags"&&n[1].toLowerCase()!=="tag"&&e.add(n[1].toLowerCase())}return Array.from(e)}function Oi(a){let e=["",ge.bold.cyan(`\u{1F4CB} Parsed Test Steps (${a.length} total):`),ge.gray("\u250C\u2500\u2500\u2500\u2500\u2500\u252C\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\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\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\u2510"),ge.gray("\u2502")+` ${ge.bold("#".padEnd(3))} `+ge.gray("\u2502")+` ${ge.bold("Type".padEnd(14))} `+ge.gray("\u2502")+` ${ge.bold("Target".padEnd(28))} `+ge.gray("\u2502")+` ${ge.bold("Value / Detail".padEnd(30))} `+ge.gray("\u2502")+` ${ge.bold("Optional".padEnd(8))} `+ge.gray("\u2502"),ge.gray("\u251C\u2500\u2500\u2500\u2500\u2500\u253C\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\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\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\u2524")];return a.forEach((t,s)=>{let i=`${s+1}`.padEnd(3),r=t.type.padEnd(14),n=(t.target||t.conditionTarget||"\u2014").slice(0,28).padEnd(28),o=(t.value||(t.durationMs?`${t.durationMs}ms`:t.expectedState)||"\u2014").slice(0,30).padEnd(30),c=(t.optional?"Yes":"No").padEnd(8);e.push(ge.gray("\u2502")+` ${ge.white(i)} `+ge.gray("\u2502")+` ${ge.cyan(r)} `+ge.gray("\u2502")+` ${ge.yellow(n)} `+ge.gray("\u2502")+` ${ge.white(o)} `+ge.gray("\u2502")+` ${ge.gray(c)} `+ge.gray("\u2502"))}),e.push(ge.gray(`\u2514\u2500\u2500\u2500\u2500\u2500\u2534\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\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\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\u2518
835
+ \u{1F4A1} No similar text found on current screen.`;return new Error(`${t}${n}`)}function Zr(a,e,t,s){let r=ae(e),n=r.includes("password")||r.includes("passcode")||r.includes("pin")||r.includes("pwd")||r.includes("secret"),i=r.includes("email")||r.includes("mail")||r.includes("user")||r.includes("login")||r.includes("username")||t&&t.includes("@"),o=a.filter(p=>p.className.includes("EditText")&&p.bounds.width>0);if(o.length===0)return null;let l=o.find(p=>de(e,p.contentDesc)||de(e,p.resourceId)||de(e,p.text));if(l)return l;let c=a.filter(p=>!p.className.includes("EditText")&&(de(e,p.text)||de(e,p.contentDesc)||de(e,p.resourceId))&&p.bounds.width>0);for(let p of c){let d=o.filter(u=>u.bounds.centerY>=p.bounds.centerY&&u.bounds.centerY-p.bounds.centerY<300&&Math.abs(u.bounds.centerX-p.bounds.centerX)<450);if(d.length>0)return d.sort((u,h)=>u.bounds.centerY-h.bounds.centerY),d[0]}if(s){let p=s.getAllCandidates("TYPE",e);for(let d of p){let u=o.find(h=>de(d,h.contentDesc)||de(d,h.resourceId)||de(d,h.text));if(u)return u}}if(n){let p=o.find(d=>d.password);if(p)return p;if(o.length>1){let d=o.find(u=>u.text?.toLowerCase().includes("email")||u.contentDesc?.toLowerCase().includes("email"));if(d){let u=o.filter(h=>h.bounds.centerY>d.bounds.centerY);if(u.length>0)return u.sort((h,g)=>h.bounds.centerY-g.bounds.centerY),u[0]}}if(o.length>=2)return o[1]}if(i){let p=o.find(d=>!d.password);if(p)return p;if(o.length>0)return o[0]}return o.length===1?o[0]:null}function is(a,e,t,s="en",r,n){let i=r?.type||"TAP",o=ae(e),c=a.filter(h=>(ae(h.text)===o||ae(h.contentDesc)===o)&&h.bounds.width>0&&h.bounds.height>0).find(h=>h.clickable);if(c)return[c];let p=t.getAllCandidates(i,e,s,n),d=[];for(let h of p){let g=a.filter(b=>{let T=de(h,b.text),x=de(h,b.contentDesc),S=de(h,b.resourceId);return(T||x||S)&&b.bounds.width>0&&b.bounds.height>0});if(g.length>0){d=g;break}}if(d.length===0){let h=[];for(let b of a)b.text&&b.text.trim().length>1&&h.push(b.text.trim()),b.contentDesc&&b.contentDesc.trim().length>1&&h.push(b.contentDesc.trim());let g=Rt(e,h,1);if(g.length>0&&g[0].score>=.8){let b=g[0].text,T=a.filter(x=>(de(b,x.text)||de(b,x.contentDesc))&&x.bounds.width>0&&x.bounds.height>0);T.length>0&&(console.log(ge.yellow(` \u{1F4A1} [Self-Healing] Auto-resolved "${e}" \u2192 "${b}" (${Math.round(g[0].score*100)}% match)`)),d=T)}}if(d.length===0)return[];let u=d.filter(h=>{let g=ae(h.text),b=ae(h.contentDesc);return g===o||b===o});if(u.length>0)d=u;else{let h=d.filter(g=>{let b=ae(g.contentDesc);return b.endsWith(o)||b.includes(`, ${o}`)});h.length>0&&(d=h)}if(r?.relativeTo){let h=a.find(g=>de(r.relativeTo,g.text)||de(r.relativeTo,g.contentDesc));if(h){let g=d.filter(b=>Math.abs(b.bounds.centerY-h.bounds.centerY)<140).sort((b,T)=>{let x=Math.hypot(b.bounds.centerX-h.bounds.centerX,b.bounds.centerY-h.bounds.centerY),S=Math.hypot(T.bounds.centerX-h.bounds.centerX,T.bounds.centerY-h.bounds.centerY);return x-S});if(g.length>0)return g}}if(r?.ordinal){let h=r.ordinal.toLowerCase();if(h==="1st"||h==="first")return[d[0]];if(h==="2nd"&&d.length>1)return[d[1]];if(h==="3rd"&&d.length>2)return[d[2]];if(h==="4th"&&d.length>3)return[d[3]];if(h==="5th"&&d.length>4)return[d[4]];if(h==="last"&&d.length>0)return[d[d.length-1]]}return d}function ye(a){if(!a||a.length===0)return"0";let e=2166136261;for(let t=0;t<a.length;t++){let s=a[t],r=`${s.text||""}|${s.contentDesc||""}|${s.resourceId||""}|${s.bounds?.x1||0},${s.bounds?.y1||0},${s.bounds?.width||0},${s.bounds?.height||0}`;for(let n=0;n<r.length;n++)e^=r.charCodeAt(n),e=Math.imul(e,16777619)}return(e>>>0).toString(16)}var Sn=[/\b\d{1,2}:\d{2}(?::\d{2})?\s*(?:AM|PM|am|pm)?\b/g,/\b\d+\s*(?:sec|min|hr|hour|day|week|month|year)s?\s*ago\b/gi,/\b(?:just now|yesterday|today)\b/gi,/[\$€£₹]\s*\d+(?:\.\d{1,2})?/g,/(?:#|(?:\b(?:ID|No)[\s:-]*))[A-Z0-9_-]{3,}\b/gi,/\b\d{1,3}%\b/g];function Gr(a){if(!a)return"";let e=a;for(let t of Sn)e=e.replace(t,"<VAL>");return e.trim()}function Ue(a,e){if(!a||a.length===0)return"0";let s=(e?.height||2400)*.04,r=2166136261;for(let n=0;n<a.length;n++){let i=a[n];if(i.bounds&&i.bounds.centerY<s)continue;let o=Gr(i.text||""),l=Gr(i.contentDesc||""),c=i.resourceId||"",p=i.className||"",d=i.clickable?"1":"0",u=i.checkable?"1":"0",h=Math.round((i.bounds?.x1||0)/50),g=Math.round((i.bounds?.y1||0)/50),b=Math.round((i.bounds?.width||0)/50),T=Math.round((i.bounds?.height||0)/50),x=`${p}|${c}|${o}|${l}|${d}|${u}|${h},${g},${b},${T}`;for(let S=0;S<x.length;S++)r^=x.charCodeAt(S),r=Math.imul(r,16777619)}return(r>>>0).toString(16)}function ha(a,e){let t=ye(a),s=ye(e);return{hasChanged:t!==s,previousHash:t,currentHash:s,nodeCountDelta:e.length-a.length}}function qr(a){let e=new Set;if(!a)return e;let t=a.split(",").map(s=>s.trim()).filter(Boolean);for(let s of t)if(s.includes("-")){let[r,n]=s.split("-").map(l=>l.trim()),i=parseInt(r,10),o=parseInt(n,10);if(!isNaN(i)&&!isNaN(o)){let[l,c]=i<=o?[i,o]:[o,i];for(let p=l;p<=c;p++)e.add(p)}}else{let r=parseInt(s,10);isNaN(r)||e.add(r)}return e}function ei(a){let e=new Set,t=a.split(`
836
+ `);for(let s of t){let r=s.trim();if(!r.startsWith("#"))continue;let n=r.match(/#\s*@tags?:\s*(.+)/i);if(n){n[1].split(",").map(o=>o.trim().toLowerCase()).filter(Boolean).forEach(o=>e.add(o));continue}let i=r.match(/#\s*@([a-zA-Z0-9_-]+)/);i&&i[1].toLowerCase()!=="tags"&&i[1].toLowerCase()!=="tag"&&e.add(i[1].toLowerCase())}return Array.from(e)}function ti(a){let e=["",ge.bold.cyan(`\u{1F4CB} Parsed Test Steps (${a.length} total):`),ge.gray("\u250C\u2500\u2500\u2500\u2500\u2500\u252C\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\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\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\u2510"),ge.gray("\u2502")+` ${ge.bold("#".padEnd(3))} `+ge.gray("\u2502")+` ${ge.bold("Type".padEnd(14))} `+ge.gray("\u2502")+` ${ge.bold("Target".padEnd(28))} `+ge.gray("\u2502")+` ${ge.bold("Value / Detail".padEnd(30))} `+ge.gray("\u2502")+` ${ge.bold("Optional".padEnd(8))} `+ge.gray("\u2502"),ge.gray("\u251C\u2500\u2500\u2500\u2500\u2500\u253C\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\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\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\u2524")];return a.forEach((t,s)=>{let r=`${s+1}`.padEnd(3),n=t.type.padEnd(14),i=(t.target||t.conditionTarget||"\u2014").slice(0,28).padEnd(28),o=(t.value||(t.durationMs?`${t.durationMs}ms`:t.expectedState)||"\u2014").slice(0,30).padEnd(30),l=(t.optional?"Yes":"No").padEnd(8);e.push(ge.gray("\u2502")+` ${ge.white(r)} `+ge.gray("\u2502")+` ${ge.cyan(n)} `+ge.gray("\u2502")+` ${ge.yellow(i)} `+ge.gray("\u2502")+` ${ge.white(o)} `+ge.gray("\u2502")+` ${ge.gray(l)} `+ge.gray("\u2502"))}),e.push(ge.gray(`\u2514\u2500\u2500\u2500\u2500\u2500\u2534\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\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\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\u2518
807
837
  `)),e.join(`
808
- `)}function Zt(a,e,t=[],s="SKIPPED",i="Validation"){let r=new Date().toISOString(),n=s==="PASS";return{packageName:a||"none",deviceId:e||"none",startTime:r,endTime:r,durationSeconds:0,totalSteps:t.length,passedSteps:n?t.length:0,failedSteps:0,skippedSteps:n?0:t.length,warnSteps:0,errorsDetected:[],steps:t.map((o,c)=>({stepIndex:c+1,timestamp:r,phase:i,description:o.originalText,action:o.type,target:o.target,status:s,durationMs:0}))}}function $o(a,e){return a&&(e&&/password|passwd|pin\b|secret|token|api[_-]?key|auth|credential/i.test(e)?"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022":a)}var jn=["ProgressBar","CircularProgressIndicator","LinearProgressIndicator","ContentLoadingProgressBar","LoadingView","ActivityIndicator"],Yn=/(?:loading|spinner|shimmer|skeleton|progress[-_]?bar|refreshing)/i,Fi=/^(?:loading|please wait|fetching|authenticating|syncing|processing)\.{0,3}$/i;function Es(a){if(!a||a.length===0)return{loading:!1};for(let e of a){if(e.className&&jn.some(t=>e.className.includes(t)))return{loading:!0,reason:`Active widget <${e.className}>`};if(e.resourceId&&Yn.test(e.resourceId))return{loading:!0,reason:`Loading resource ID "${e.resourceId}"`};if(e.contentDesc&&Fi.test(e.contentDesc.trim()))return{loading:!0,reason:`Loading description "${e.contentDesc}"`};if(e.text&&Fi.test(e.text.trim()))return{loading:!0,reason:`Loading status text "${e.text}"`}}return{loading:!1}}async function Ps(a,e=4500,t=150){let s=Date.now(),i="",r=0,n=[];for(;Date.now()-s<e;){a.setLastHierarchy(null);let{flat:o}=await a.getCleanHierarchy(a.serial);if(n=o,Es(o).loading){r=0,i="",await new Promise(h=>setTimeout(h,220));continue}let l=be(o);if(l===i&&l!=="0"){if(r++,r>=1)return{settled:!0,durationMs:Date.now()-s,finalHash:l,flat:n}}else r=0,i=l;let p=Date.now()-s,d=e-p;if(d<=0)break;await new Promise(h=>setTimeout(h,Math.min(t,d)))}return{settled:!1,durationMs:Date.now()-s,finalHash:i||(n.length>0?be(n):"0"),flat:n}}function ie(a){return new Promise(e=>setTimeout(e,a))}function Wn(a){let e=a.screenDimensions.height,t=a.navBarHeight>0?e-a.navBarHeight:e,s=a.navBarHeight>0?t*.94:e*C.gestures.bottomNavThresholdRatio;return{effectiveHeight:t,bottomThreshold:s}}var ks=class{type="TAP";async execute(e,t){let s=e.target,{width:i,height:r}=t.screenDimensions,n=Math.round(i*.5),{flat:o,matched:c}=await t.waitForTarget(s,t.serial,C.polling.defaultTimeoutMs,e);if(c.length===0&&t.upcomingSteps&&t.upcomingSteps.length>0&&!o.some(D=>D.password===!0||D.className.includes("EditText")&&o.some(B=>B.text?.toLowerCase().includes("password")))){let D=t.upcomingSteps.findIndex(k=>k.type==="VERIFY"&&k.target&&t.matchNodes(o,k.target,k).length>0);if(D!==-1)return{fastForwarded:!0,reason:`Screen already displays "${t.upcomingSteps[D].target}"`,skipCount:D};let B=t.upcomingSteps.findIndex(k=>k.type==="TAP"&&k.target&&t.matchNodes(o,k.target,k).length>0);if(B!==-1)return{fastForwarded:!0,reason:`Upcoming action "${t.upcomingSteps[B].target}" is directly actionable`,skipCount:B}}if(c.length===0){await t.driver.swipe(n,Math.round(r*.75),n,Math.round(r*.35),300,t.serial),await ie(350),t.setLastHierarchy(null);let E=await t.waitForTarget(s,t.serial,2e3,e);o=E.flat,c=E.matched}if(c.length===0)throw t.buildDiagnosticError(s,o,`Element matching "${s}" not found on current screen.`);let l=c.find(E=>E.clickable)||c[0];if(t.targetPackage&&!Gt(l.packageName,t.targetPackage))throw console.log(ue.red(` \u{1F6A8} [Jail Guard] Blocked Tap on foreign package "${l.packageName}". Target is "${t.targetPackage}".`)),await xs(o,t.targetPackage,t.driver,t.serial),new Error(`Security Boundary Violation: Refused to tap element belonging to foreign application "${l.packageName}". Containment enforced.`);if(await t.ensureInViewport(l,t.serial)){let E=await t.waitForTarget(s,t.serial,1500,e);E.matched.length>0&&(o=E.flat,c=E.matched,l=c.find(D=>D.clickable)||c[0])}let d=l.bounds.centerX,{effectiveHeight:h}=Wn(t),u=l.bounds.centerY;if(l.bounds.y2>h&&(u=Math.min(Math.round((l.bounds.y1+h)/2),h-15)),u=Math.min(u,l.bounds.y2-2),u=Math.max(u,l.bounds.y1+2),u>r*.5&&typeof t.driver.isKeyboardVisible=="function")try{await t.driver.isKeyboardVisible(t.serial)&&(console.log(ue.gray(` [Keyboard] Target occluded by soft keyboard (Y=${u} > ${Math.round(r*.5)}). Dismissing...`)),await t.driver.pressBack(t.serial),await ie(300),t.setLastHierarchy(null))}catch{}let m=Oe(o,t.screenDimensions),y=l.contentDesc||l.text||l.resourceId;console.log(ue.gray(` [Tap Target] (${d}, ${u}) for "${y||s}"`));let v=t.lastHierarchy?.hash;await t.driver.tap(d,u,t.serial),t.setLastHierarchy(null);let T=t.upcomingSteps&&t.upcomingSteps[0],S=T?.target?t.matchNodes(o,T.target,T).length>0:!1;if(T&&!S&&(T.type==="VERIFY"||T.type==="TAP")){let E=await Ps(t,4500),B=Oe(E.flat,t.screenDimensions)!==m,k=T.target?t.matchNodes(E.flat,T.target,T).length>0:!1;if(!B&&!k&&t.matchNodes(E.flat,s,e).length>0)throw new Error(`Navigation failed: Tap on "${s}" did not transition away from the current screen. Next expected element "${T.target}" is not visible.`)}else await ie(C.settle.tapMs);y&&oe(y)!==oe(s)&&t.memory.recordSuccess("TAP",s,y,l.contentDesc?"contentDesc":l.text?"text":"resourceId",1,void 0,v)}},Ds=class{type="LONG_PRESS";async execute(e,t){let s=e.target,{flat:i,matched:r}=await t.waitForTarget(s,t.serial,C.polling.defaultTimeoutMs,e);if(r.length===0)throw t.buildDiagnosticError(s,i,`Element matching "${s}" not found for long-press.`);let n=r[0];if(await t.ensureInViewport(n,t.serial)){let c=await t.waitForTarget(s,t.serial,1500,e);c.matched.length>0&&(n=c.matched[0])}await t.driver.swipe(n.bounds.centerX,n.bounds.centerY,n.bounds.centerX,n.bounds.centerY,C.gestures.longPressDurationMs,t.serial),await ie(300),t.setLastHierarchy(null)}},qt=class{type="TYPE";async execute(e,t){let s=e.target,i=e.value??"",r=[],n=null;if(t.lastHierarchy&&Date.now()-t.lastHierarchy.timestamp<C.polling.hierarchyCacheTtlMs){let d=t.findTargetInputField(t.lastHierarchy.flat,s,i);d&&(r=t.lastHierarchy.flat,n=d)}if(n||(r=(await t.getCleanHierarchy(t.serial)).flat,n=t.findTargetInputField(r,s,i)),!n){let d=Date.now();for(;Date.now()-d<2e3&&(await ie(200),r=(await t.getCleanHierarchy(t.serial)).flat,n=t.findTargetInputField(r,s,i),!n););}if(!n&&t.upcomingSteps&&t.upcomingSteps.length>0&&!r.some(h=>h.className.includes("EditText"))){let h=t.upcomingSteps.findIndex(u=>u.type==="VERIFY"&&u.target&&t.matchNodes(r,u.target,u).length>0);if(h!==-1)return{fastForwarded:!0,reason:`Screen already displays "${t.upcomingSteps[h].target}" and form is completed`,skipCount:h}}if(!n)throw t.buildDiagnosticError(s,r,`Input field matching "${s}" not found.`);if(t.targetPackage&&!Gt(n.packageName,t.targetPackage))throw console.log(ue.red(` \u{1F6A8} [Jail Guard] Blocked Type on foreign package "${n.packageName}". Target is "${t.targetPackage}".`)),await xs(r,t.targetPackage,t.driver,t.serial),new Error(`Security Boundary Violation: Refused to type into element belonging to foreign application "${n.packageName}". Containment enforced.`);if(n.text&&n.text===i){console.log(ue.gray(` [Input Field] Already contains desired value "${i}". Skipping typing.`));return}if(await t.ensureInViewport(n,t.serial)){r=(await t.getCleanHierarchy(t.serial)).flat;let h=t.findTargetInputField(r,s,i);h&&(n=h)}let{height:c}=t.screenDimensions;if(n.bounds.centerY>c*.5&&typeof t.driver.isKeyboardVisible=="function")try{await t.driver.isKeyboardVisible(t.serial)&&(console.log(ue.gray(" [Keyboard] Input field occluded by soft keyboard. Dismissing before focus...")),await t.driver.pressBack(t.serial),await ie(300),t.setLastHierarchy(null))}catch{}let l=t.lastHierarchy?.hash;await t.driver.tap(n.bounds.centerX,n.bounds.centerY,t.serial),await ie(C.settle.inputFocusMs),await t.driver.clearField(t.serial),await ie(C.settle.inputClearMs),await t.driver.inputText(i,t.serial),await ie(C.settle.inputDoneMs),t.setLastHierarchy(null);let p=n.contentDesc||n.resourceId;p&&oe(p)!==oe(s)&&t.memory.recordSuccess("TYPE",s,p,n.contentDesc?"contentDesc":"resourceId",1,void 0,l)}},As=class{type="CLEAR";async execute(e,t){let s=e.target,i=[],r=null;if(t.lastHierarchy&&Date.now()-t.lastHierarchy.timestamp<C.polling.hierarchyCacheTtlMs&&(r=t.findTargetInputField(t.lastHierarchy.flat,s),r&&(i=t.lastHierarchy.flat)),r||(i=(await t.getCleanHierarchy(t.serial)).flat,r=t.findTargetInputField(i,s)),!r){let o=t.matchNodes(i,s,e);o.length>0&&(r=o[0])}if(!r)throw t.buildDiagnosticError(s,i,`Input field matching "${s}" not found to clear.`);await t.driver.tap(r.bounds.centerX,r.bounds.centerY,t.serial),await ie(300);let n=t.driver;n.clearField?await n.clearField(t.serial):n.clearInput&&await n.clearInput(50,t.serial),await ie(200),t.setLastHierarchy(null)}},$s=class{type="VERIFY";async execute(e,t){let s=e.target,i=e.expectedState||"visible";if(e.negative===!0||i==="absent"||i==="hidden"){let h=await t.getCleanHierarchy(t.serial);if(t.matchNodes(h.flat,s,e).length>0)throw new Error(`Negative assertion failed: "${s}" was expected to be NOT visible, but is present on screen.`);return}let{width:n,height:o}=t.screenDimensions,c=Math.round(n*.5),l=!1,p=[],d;if(t.lastHierarchy&&Date.now()-t.lastHierarchy.timestamp<C.polling.verifyCacheTtlMs&&(d=t.matchNodes(t.lastHierarchy.flat,s,e)[0],d&&(p=t.lastHierarchy.flat,l=!0,t.lastHierarchy.timestamp=Date.now())),!l){p=(await t.getCleanHierarchy(t.serial)).flat,d=t.matchNodes(p,s,e)[0],d?l=!0:Es(p).loading&&(console.log(ue.cyan(" \u23F3 [Quiescence] Loading indicator detected in Verify. Waiting for screen to settle...")),p=(await Ps(t,4500)).flat,d=t.matchNodes(p,s,e)[0],d&&(l=!0));let u=Date.now();for(;!l&&Date.now()-u<C.polling.extendedTimeoutMs;){if(p=(await t.getCleanHierarchy(t.serial)).flat,d=t.matchNodes(p,s,e)[0],d){l=!0,t.lastHierarchy&&(t.lastHierarchy.timestamp=Date.now()),await ie(400);break}await ie(200)}}if(l||(await t.driver.swipe(c,Math.round(o*.75),c,Math.round(o*.35),300,t.serial),await ie(400),p=(await t.getCleanHierarchy(t.serial)).flat,d=t.matchNodes(p,s,e)[0],d&&(l=!0)),!l){if(t.upcomingSteps&&t.upcomingSteps.length>0&&!p.some(u=>u.password===!0||u.className.includes("EditText")&&p.some(m=>m.text?.toLowerCase().includes("password")))){let u=t.upcomingSteps.findIndex(m=>m.type==="VERIFY"&&m.target&&t.matchNodes(p,m.target,m).length>0);if(u!==-1)return{fastForwarded:!0,reason:`Screen is already on "${t.upcomingSteps[u].target}"`,skipCount:u}}throw await t.driver.swipe(c,Math.round(o*.35),c,Math.round(o*.75),300,t.serial),await ie(300),t.buildDiagnosticError(s,p,`Verification assertion failed: "${s}" is not visible on screen.`)}if(d&&i==="disabled"&&d.enabled)throw new Error(`State assertion failed: "${s}" was expected to be DISABLED, but is enabled.`);if(d&&i==="enabled"&&!d.enabled)throw new Error(`State assertion failed: "${s}" was expected to be ENABLED, but is disabled.`)}},Cs=class{type="BACK";async execute(e,t){let{flat:s}=await t.getCleanHierarchy(t.serial),i=vs(s);if(i&&vt(i)){console.log(ue.yellow(` \u{1F6E1}\uFE0F [Jail Guard] Device is already on launcher (${i}). Suppressing Back press to prevent app exit loop.`)),t.targetPackage&&await t.driver.launchApp(t.targetPackage,t.serial);return}await t.driver.pressKey(4,t.serial),await ie(C.settle.backMs),t.setLastHierarchy(null)}},Is=class{type="SCROLL";async execute(e,t){let{width:s,height:i}=t.screenDimensions,r=Math.round(s*.5);if(e.target&&e.target!=="up"&&e.target!=="down"){let o=e.target,c=e.direction||"down",l=!1;for(let p=0;p<5;p++){let{flat:d}=await t.getCleanHierarchy(t.serial),h=t.matchNodes(d,o,e);if(h.length>0){await t.ensureInViewport(h[0],t.serial),l=!0;break}c==="up"?await t.driver.swipe(r,Math.round(i*.35),r,Math.round(i*.75),300,t.serial):await t.driver.swipe(r,Math.round(i*.75),r,Math.round(i*.35),300,t.serial),await ie(C.settle.scrollMs),t.setLastHierarchy(null)}if(!l){let{flat:p}=await t.getCleanHierarchy(t.serial);throw t.buildDiagnosticError(o,p,`Element "${o}" not found after scrolling ${e.direction||"down"}.`)}}else e.target==="up"||e.direction==="up"?await t.driver.swipe(r,Math.round(i*.35),r,Math.round(i*.75),300,t.serial):await t.driver.swipe(r,Math.round(i*.75),r,Math.round(i*.35),300,t.serial),await ie(C.settle.scrollMs),t.setLastHierarchy(null)}},Ns=class{type="SWIPE";async execute(e,t){let{width:s,height:i}=t.screenDimensions,r=Math.round(s*.5),n=e.direction||"left";if(e.target){let{matched:d}=await t.waitForTarget(e.target,t.serial,2500,e);if(d.length>0){let h=d[0],u=n==="left"?h.bounds.x2-40:n==="right"?h.bounds.x1+40:h.bounds.centerX,m=n==="left"?h.bounds.x1+40:n==="right"?h.bounds.x2-40:h.bounds.centerX,y=n==="up"?h.bounds.y2-40:n==="down"?h.bounds.y1+40:h.bounds.centerY,v=n==="up"?h.bounds.y1+40:n==="down"?h.bounds.y2-40:h.bounds.centerY;await t.driver.swipe(u,y,m,v,C.gestures.swipeDurationMs,t.serial),await ie(400),t.setLastHierarchy(null);return}}let o=n==="left"?Math.round(s*.8):n==="right"?Math.round(s*.2):r,c=n==="left"?Math.round(s*.2):n==="right"?Math.round(s*.8):r,l=Math.round(n==="up"?i*.75:n==="down"?i*.35:i*.5),p=Math.round(n==="up"?i*.35:n==="down"?i*.75:i*.5);await t.driver.swipe(o,l,c,p,C.gestures.swipeDurationMs,t.serial),await ie(400),t.setLastHierarchy(null)}},Ls=class{type="TOGGLE";async execute(e,t){let s=e.target,{flat:i,matched:r}=await t.waitForTarget(s,t.serial,C.polling.defaultTimeoutMs,e);if(r.length===0)throw t.buildDiagnosticError(s,i,`Toggle element matching "${s}" not found.`);let n=r.find(c=>c.clickable)||r[0];if(await t.ensureInViewport(n,t.serial)){let c=await t.waitForTarget(s,t.serial,1500,e);c.matched.length>0&&(n=c.matched.find(l=>l.clickable)||c.matched[0])}if(e.desiredState!==void 0&&n.checked!==void 0&&n.checked===e.desiredState){console.log(ue.gray(` [Toggle] "${s}" is already in desired state (${e.desiredState?"ON":"OFF"}).`));return}await t.driver.tap(n.bounds.centerX,n.bounds.centerY,t.serial),await ie(C.settle.toggleMs),t.setLastHierarchy(null)}},Ms=class{type="SELECT";async execute(e,t){let s=e.target,i=e.value,{flat:r,matched:n}=await t.waitForTarget(s,t.serial,C.polling.defaultTimeoutMs,e);if(n.length===0)throw t.buildDiagnosticError(s,r,`Dropdown / picker element matching "${s}" not found.`);let o=n.find(p=>p.clickable)||n[0];await t.driver.tap(o.bounds.centerX,o.bounds.centerY,t.serial),await ie(C.settle.selectMs),t.setLastHierarchy(null);let c=await t.waitForTarget(i,t.serial,C.polling.defaultTimeoutMs);if(c.matched.length===0)throw t.buildDiagnosticError(i,c.flat,`Dropdown option "${i}" not found.`);let l=c.matched[0];await t.driver.tap(l.bounds.centerX,l.bounds.centerY,t.serial),await ie(400),t.setLastHierarchy(null)}},Rs=class{type="LAUNCH_APP";async execute(e,t){let s=e.target;if(!s)throw new Error("Target package name required to launch app.");t.targetPackage=s,await t.driver.launchApp(s,t.serial),await ie(C.settle.launchMs),t.setLastHierarchy(null)}},Os=class{type="RESTART_APP";async execute(e,t){let s=e.target;if(!s)throw new Error("Target package name required to restart app.");t.targetPackage=s,await t.driver.restartApp(s,t.serial),await ie(C.settle.launchMs),t.setLastHierarchy(null)}},Fs=class{type="TERMINATE_APP";async execute(e,t){let s=e.target||await t.driver.getForegroundPackage(t.serial);s&&await t.driver.forceStop(s,t.serial),await ie(C.settle.stopMs),t.setLastHierarchy(null)}},_s=class{type="CLEAR_DATA";async execute(e,t){let s=e.target;if(!s)throw new Error("Target package name required to clear data.");await t.driver.clearAppData(s,t.serial),await ie(1e3),t.setLastHierarchy(null)}},Hs=class{type="GRANT_PERMISSION";async execute(e,t){let s=e.value,i=e.target||await t.driver.getForegroundPackage(t.serial);if(!i)throw new Error("Foreground package not detected for grant permission.");await t.driver.grantPermission(i,s,t.serial),await ie(400)}},Bs=class{type="DEEP_LINK";async execute(e,t){let s=e.target;await t.driver.openDeepLink(s,t.serial),await ie(C.settle.launchMs),t.setLastHierarchy(null)}},Us=class{type="WAIT";async execute(e,t){await ie(e.durationMs||1e3)}},js=class{type="IF_VISIBLE";async execute(e,t){let s=e.conditionTarget||e.target;if(!s)return;let i=!1;try{let{matched:r}=await t.waitForTarget(s,t.serial,1500,e);i=r.length>0}catch{i=!1}i?(console.log(ue.cyan(` [Conditional] Condition met: "${s}" is visible.`)),e.thenSteps&&e.thenSteps.length>0&&t.executeChildSteps&&await t.executeChildSteps(e.thenSteps,t)):(console.log(ue.gray(` [Conditional] Condition not met: "${s}" is not visible.`)),e.elseSteps&&e.elseSteps.length>0&&t.executeChildSteps&&await t.executeChildSteps(e.elseSteps,t))}},Ys=class{type="LOOP";async execute(e,t){let s=C.flow?.maxLoopIterations||50,i=Math.min(e.loopCount||1,s),r=e.childSteps||[];if(!(r.length===0||!t.executeChildSteps)){console.log(ue.cyan(` [Loop] Starting loop iteration (1 to ${i})...`));for(let n=1;n<=i;n++){e.loopVariable&&t.variables.set(e.loopVariable,String(n)),t.loopStack.push({current:n,total:i,variable:e.loopVariable});try{await t.executeChildSteps(r,t)}finally{t.loopStack.pop()}}}}},Ws=class{type="WAIT_UNTIL";async execute(e,t){let s=e.target,i=e.durationMs||C.flow?.waitUntilTimeoutMs||1e4,r=e.expectedState||"visible";console.log(ue.gray(` [Wait Until] Waiting for "${s}" to be ${r} (up to ${i}ms)...`));let n=Date.now(),o=!1;for(;Date.now()-n<i;){try{let{matched:c}=await t.waitForTarget(s,t.serial,1e3,e);if(c.length>0){let l=c[0];if(r==="enabled"&&!l.enabled){await ie(250);continue}o=!0;break}}catch{}await ie(250)}if(!o){let{flat:c}=await t.getCleanHierarchy(t.serial);throw t.buildDiagnosticError(s,c,`WaitUntil assertion failed: "${s}" did not become ${r} within ${i}ms.`)}}},Vs=class{type="SET_VARIABLE";async execute(e,t){let s=e.variableName,i=e.variableExpression||"";i=ft(i,Object.fromEntries(t.variables));let r=bi(i);t.variables.set(s,r),console.log(ue.cyan(` [Variable] $${s} = "${r}"`))}},Js=class{type="DEVICE_STATE";async execute(e,t){let s=e.deviceAction,i=e.deviceStateValue;switch(s){case"wifi":{let r=i===!0||i==="on"||i==="enable";console.log(ue.cyan(` [Device] Setting Wi-Fi: ${r?"ENABLED":"DISABLED"}`)),await t.driver.setWifiEnabled(r,t.serial);break}case"airplane":{let r=i===!0||i==="on"||i==="enable";console.log(ue.cyan(` [Device] Setting Airplane Mode: ${r?"ENABLED":"DISABLED"}`)),await t.driver.setAirplaneMode(r,t.serial);break}case"orientation":{let r=i==="landscape"?"landscape":"portrait";console.log(ue.cyan(` [Device] Setting Orientation: ${r.toUpperCase()}`)),await t.driver.setOrientation(r,t.serial);break}case"notifications":{let r=i==="open"||i==="expand"||i===!0;console.log(ue.cyan(` [Device] ${r?"Expanding":"Collapsing"} Notification Shade`)),r?await t.driver.openNotificationShade(t.serial):await t.driver.collapseNotificationShade(t.serial);break}default:throw new Error(`Unsupported device action: ${s}`)}await ie(400),t.setLastHierarchy(null)}},Xs=class{type="RUN_JS";async execute(e,t){let s=e.jsCode||e.value||"";if(!s)return;console.log(ue.cyan(" [JS Hook] Executing sandboxed JavaScript snippet..."));let i={vars:Object.fromEntries(t.variables),setVar:(n,o)=>{t.variables.set(n,String(o))},getVar:n=>t.variables.get(n),console:{log:(...n)=>console.log(ue.gray(" [JS Output]",...n)),error:(...n)=>console.error(ue.red(" [JS Error]",...n))}},r=_i.createContext(i);_i.runInContext(s,r,{timeout:3e3});for(let[n,o]of Object.entries(i.vars))t.variables.set(n,String(o))}},zs=class{type="ENTER_OTP";async execute(e,t){console.log(ue.cyan(" [OTP Capture] Detecting verification code..."));let s=null;if(process.env.PROMPTTEST_MOCK_OTP)s=process.env.PROMPTTEST_MOCK_OTP.trim(),console.log(ue.gray(` [OTP Capture] Captured code from environment: ${s}`));else try{let{stdout:i}=await t.driver.execAdb(["shell","logcat","-d","-t","200","-s","SMSDispatcher:V","SmsMessage:V","NotificationService:V","ActivityManager:I"],t.serial),r=i.match(/(?:code|otp|pin|verification)[^0-9\r\n]{0,35}(\d{4,8})\b/i)||i.match(/\b(\d{4,8})\s*(?:is your verification|is your otp)/i);r&&(s=r[1],console.log(ue.gray(` [OTP Capture] Captured code from logcat: ${s}`)))}catch{}if(s||(s="123456",console.log(ue.yellow(` [OTP Capture] No live code detected; using simulated OTP: ${s}`))),e.target){let i={originalText:`Type '${s}' into '${e.target}'`,type:"TYPE",target:e.target,value:s};return new qt().execute(i,t)}else{let{flat:i}=await t.getCleanHierarchy(t.serial),r=t.findTargetInputField(i,"code",s)||i.find(n=>n.className.includes("EditText"));r?(await t.driver.tap(r.bounds.centerX,r.bounds.centerY,t.serial),await ie(250),await t.driver.inputText(s,t.serial)):await t.driver.inputText(s,t.serial)}}},Gs=class{type="API_REQUEST";async execute(e,t){let s=e.apiMethod||"GET",i=e.apiUrl||"";if(!i)throw new Error("API_REQUEST requires a valid target URL.");console.log(ue.cyan(` [API Request] ${s} ${i}...`));let r={method:s,headers:{"Content-Type":"application/json",Accept:"application/json"}};e.apiBody&&(s==="POST"||s==="PUT"||s==="PATCH")&&(r.body=e.apiBody);let n=await fetch(i,r);if(e.apiExpectedStatus!==void 0){if(n.status!==e.apiExpectedStatus)throw new Error(`API status assertion failed for ${i}: expected HTTP ${e.apiExpectedStatus}, but received HTTP ${n.status}`);console.log(ue.green(` \u2714 API Status HTTP ${n.status} matched expectation.`))}if(e.apiStorePath&&e.apiStoreVariable)try{let o=await n.text(),c=JSON.parse(o),l=this.resolveJsonPath(c,e.apiStorePath);l!==void 0&&(t.variables.set(e.apiStoreVariable,String(l)),console.log(ue.cyan(` [Variable] $${e.apiStoreVariable} = "${l}" (from ${e.apiStorePath})`)))}catch(o){Ge.warn(`Failed to parse JSON response for path extraction: ${o.message}`)}}resolveJsonPath(e,t){let s=t.replace(/^\$\.?/,"").split("."),i=e;for(let r of s){if(i==null||typeof i!="object")return;i=i[r]}return i}},Ks=class{type="EMAIL_ASSERT";async execute(e,t){let s=e.emailRecipient||"",i=e.emailSubject||"";console.log(ue.cyan(` [Email Assertion] Checking email to "${s}" with subject "${i}"...`));let r=[];if(process.env.PROMPTTEST_MOCK_EMAILS)try{r=JSON.parse(process.env.PROMPTTEST_MOCK_EMAILS)}catch{r=[]}let n=r.find(o=>o.to.toLowerCase()===s.toLowerCase()&&o.subject.toLowerCase().includes(i.toLowerCase()));if(!n){if(r.length===0){if(!s.includes("@"))throw new Error(`Invalid email recipient address: "${s}"`);console.log(ue.yellow(" \u26A0 No mock inbox configured (PROMPTTEST_MOCK_EMAILS); simulated email check passed."));return}throw new Error(`Email assertion failed: No email to "${s}" with subject "${i}" found.`)}if(e.emailBodyContains&&n.body&&!n.body.includes(e.emailBodyContains))throw new Error(`Email body assertion failed: Expected email to contain "${e.emailBodyContains}", but content did not match.`);console.log(ue.green(" \u2714 Email verified successfully."))}},Vn=[new ks,new Ds,new qt,new As,new $s,new Cs,new Is,new Ns,new Ls,new Ms,new Rs,new Os,new Fs,new _s,new Hs,new Bs,new Us,new js,new Ys,new Ws,new Vs,new Js,new Xs,new zs,new Gs,new Ks],Hi=new Map(Vn.map(a=>[a.type,a]));var xt=class{providerName="heuristic";async resolveIntent(e,t){let s=oe(e);return(s.includes("login")||s.includes("log in")||s.includes("sign in")||s.includes("signin"))&&(s.includes("test user")||s.includes("default credentials")||s.includes("credentials"))?[{originalText:"Type 'user@example.com' into 'Email'",type:"TYPE",target:"Email",value:"user@example.com"},{originalText:"Type 'Password123!' into 'Password'",type:"TYPE",target:"Password",value:"Password123!"},{originalText:"Tap 'Sign In'",type:"TAP",target:"Sign In"}]:s.includes("sign up with random user")||s.includes("register new user")?[{originalText:"Type $RANDOM_NAME into 'Name'",type:"TYPE",target:"Name",value:"$RANDOM_NAME"},{originalText:"Type $RANDOM_EMAIL into 'Email'",type:"TYPE",target:"Email",value:"$RANDOM_EMAIL"},{originalText:"Type 'Password123!' into 'Password'",type:"TYPE",target:"Password",value:"Password123!"},{originalText:"Tap 'Create Account'",type:"TAP",target:"Create Account"}]:s.includes("dismiss dialog")||s.includes("close modal")||s.includes("dismiss popup")?[{originalText:"Tap 'Close'",type:"TAP",target:"Close",optional:!0},{originalText:"Tap 'Dismiss'",type:"TAP",target:"Dismiss",optional:!0},{originalText:"Press back",type:"BACK",optional:!0}]:null}async findSuggestions(e,t,s){let i=[],r=oe(e),n=new Set,o=Di(e,s?.locale);for(let d of o){let h=t.find(u=>u.bounds.width>0&&u.bounds.height>0&&(pe(d,u.text)||pe(d,u.contentDesc)));if(h){let u=(h.text||h.contentDesc||d).trim();n.has(u)||(n.add(u),i.push({text:u,score:.9,node:h,strategy:"synonym",reason:`Direct synonym match for "${e}" via semantic dictionary`}))}}for(let d of t)if(d.resourceId&&d.bounds.width>0&&d.bounds.height>0&&d.resourceId.toLowerCase().split(/[:/_]/).some(m=>m===r||r.includes(m))){let m=(d.text||d.contentDesc||d.resourceId).trim();n.has(m)||(n.add(m),i.push({text:m,score:.85,node:d,strategy:"exact",reason:`Resource-ID segment matches target "${e}"`}))}let c=[];for(let d of t)d.bounds.width>0&&d.bounds.height>0&&(d.text&&d.text.trim().length>1&&c.push({text:d.text.trim(),node:d}),d.contentDesc&&d.contentDesc.trim().length>1&&c.push({text:d.contentDesc.trim(),node:d}));let l=Array.from(new Set(c.map(d=>d.text))),p=bt(e,l,5);for(let d of p)if(d.score>=.7&&!n.has(d.text)){n.add(d.text);let h=c.find(u=>u.text===d.text)?.node;i.push({text:d.text,score:d.score,node:h,strategy:"fuzzy",reason:`String similarity match (${Math.round(d.score*100)}%)`})}return i.sort((d,h)=>h.score-d.score)}};var Lt=class{providerName="mock";mockIntents=new Map;mockSuggestions=new Map;constructor(){this.mockIntents.set("log in to account",[{originalText:"Type 'test@example.com' into 'Email'",type:"TYPE",target:"Email",value:"test@example.com"},{originalText:"Type 'Password123' into 'Password'",type:"TYPE",target:"Password",value:"Password123"},{originalText:"Tap 'Sign In'",type:"TAP",target:"Sign In"}])}setMockIntent(e,t){this.mockIntents.set(e.toLowerCase().trim(),t)}setMockSuggestion(e,t){this.mockSuggestions.set(e.toLowerCase().trim(),t)}async resolveIntent(e,t){let s=e.toLowerCase().trim();return this.mockIntents.has(s)?this.mockIntents.get(s):null}async findSuggestions(e,t,s){let i=e.toLowerCase().trim();return this.mockSuggestions.has(i)?this.mockSuggestions.get(i):new xt().findSuggestions(e,t)}},es=class{providerName;config;heuristicFallback;constructor(e){this.config=e,this.providerName=e.provider,this.heuristicFallback=new xt}async resolveIntent(e,t){if(this.config.provider==="mock")return new Lt().resolveIntent(e,t);try{let s=`You are a test automation engineer generating steps for PromptTest.
838
+ `)}function ns(a,e,t=[],s="SKIPPED",r="Validation"){let n=new Date().toISOString(),i=s==="PASS";return{packageName:a||"none",deviceId:e||"none",startTime:n,endTime:n,durationSeconds:0,totalSteps:t.length,passedSteps:i?t.length:0,failedSteps:0,skippedSteps:i?0:t.length,warnSteps:0,errorsDetected:[],steps:t.map((o,l)=>({stepIndex:l+1,timestamp:n,phase:r,description:o.originalText,action:o.type,target:o.target,status:s,durationMs:0}))}}function ga(a,e){return a&&(e&&/password|passwd|pin\b|secret|token|api[_-]?key|auth|credential/i.test(e)?"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022":a)}async function si(a){let{outputDir:e,specName:t,currentSpecName:s,stepNum:r,step:n,error:i,stepStartTime:o,startTime:l,errorShot:c,lastHierarchy:p,driver:d,serial:u}=a;try{let h=`${t}_step_${r}`,g=Be.join(e,"failures",h);st.existsSync(g)||st.mkdirSync(g,{recursive:!0}),c&&await st.promises.writeFile(Be.join(g,"screenshot.png"),c);let b=p?.xml||await d.getUiHierarchy(u).catch(()=>"");b&&await st.promises.writeFile(Be.join(g,"hierarchy.xml"),b,"utf-8");let T=await d.getLogcatTail(150,u);await st.promises.writeFile(Be.join(g,"logcat_tail.log"),T,"utf-8");let x=p?.flat||[],S=n.target&&x.length>0?Rt(n.target,x.map(B=>B.text||B.contentDesc||"").filter(Boolean)):[];await st.promises.writeFile(Be.join(g,"match_scores.json"),JSON.stringify(S,null,2),"utf-8");let y={specName:s,stepNumber:r,stepType:n.type,target:n.target||n.value,error:i.message,timestamp:new Date().toISOString(),videoOffsetMs:Math.max(0,o-l)};await st.promises.writeFile(Be.join(g,"manifest.json"),JSON.stringify(y,null,2),"utf-8");let P=[{name:"manifest.json",data:Buffer.from(JSON.stringify(y,null,2),"utf-8")},{name:"logcat_tail.log",data:Buffer.from(T,"utf-8")},{name:"match_scores.json",data:Buffer.from(JSON.stringify(S,null,2),"utf-8")}];b&&P.push({name:"hierarchy.xml",data:Buffer.from(b,"utf-8")}),c&&P.push({name:"screenshot.png",data:c});let $=`${h}.zip`,R=Be.join(e,"failures",$);await Xr(R,P);let C=Be.join("failures",$).replace(/\\/g,"/");return console.log(ge.red(` \u{1F4C1} Failure Triage ZIP Bundle generated at: output/${C}`)),C}catch{return}}var wn=["ProgressBar","CircularProgressIndicator","LinearProgressIndicator","ContentLoadingProgressBar","LoadingView","ActivityIndicator"],vn=/(?:loading|spinner|shimmer|skeleton|progress[-_]?bar|refreshing)/i,ri=/^(?:loading|please wait|fetching|authenticating|syncing|processing)\.{0,3}$/i;function Os(a){if(!a||a.length===0)return{loading:!1};for(let e of a){if(e.className&&wn.some(t=>e.className.includes(t)))return{loading:!0,reason:`Active widget <${e.className}>`};if(e.resourceId&&vn.test(e.resourceId))return{loading:!0,reason:`Loading resource ID "${e.resourceId}"`};if(e.contentDesc&&ri.test(e.contentDesc.trim()))return{loading:!0,reason:`Loading description "${e.contentDesc}"`};if(e.text&&ri.test(e.text.trim()))return{loading:!0,reason:`Loading status text "${e.text}"`}}return{loading:!1}}async function Fs(a,e=4500,t=150){let s=Date.now(),r="",n=0,i=[];for(;Date.now()-s<e;){a.setLastHierarchy(null);let{flat:o}=await a.getCleanHierarchy(a.serial);if(i=o,Os(o).loading){n=0,r="",await new Promise(u=>setTimeout(u,220));continue}let c=ye(o);if(c===r&&c!=="0"){if(n++,n>=1)return{settled:!0,durationMs:Date.now()-s,finalHash:c,flat:i}}else n=0,r=c;let p=Date.now()-s,d=e-p;if(d<=0)break;await new Promise(u=>setTimeout(u,Math.min(t,d)))}return{settled:!1,durationMs:Date.now()-s,finalHash:r||(i.length>0?ye(i):"0"),flat:i}}function ne(a){return new Promise(e=>setTimeout(e,a))}function xn(a){let e=a.screenDimensions.height,t=a.navBarHeight>0?e-a.navBarHeight:e,s=a.navBarHeight>0?t*.94:e*I.gestures.bottomNavThresholdRatio;return{effectiveHeight:t,bottomThreshold:s}}var _s=class{type="TAP";async execute(e,t){let s=e.target,{width:r,height:n}=t.screenDimensions,i=Math.round(r*.5),{flat:o,matched:l}=await t.waitForTarget(s,t.serial,I.polling.defaultTimeoutMs,e);if(l.length===0&&t.upcomingSteps&&t.upcomingSteps.length>0&&!o.some($=>$.password===!0||$.className.includes("EditText")&&o.some(R=>R.text?.toLowerCase().includes("password")))){let $=t.upcomingSteps.findIndex(C=>C.type==="VERIFY"&&C.target&&t.matchNodes(o,C.target,C).length>0);if($!==-1)return{fastForwarded:!0,reason:`Screen already displays "${t.upcomingSteps[$].target}"`,skipCount:$};let R=t.upcomingSteps.findIndex(C=>C.type==="TAP"&&C.target&&t.matchNodes(o,C.target,C).length>0);if(R!==-1)return{fastForwarded:!0,reason:`Upcoming action "${t.upcomingSteps[R].target}" is directly actionable`,skipCount:R}}if(l.length===0){await t.driver.swipe(i,Math.round(n*.75),i,Math.round(n*.35),300,t.serial),await ne(350),t.setLastHierarchy(null);let P=await t.waitForTarget(s,t.serial,2e3,e);o=P.flat,l=P.matched}if(l.length===0)throw t.buildDiagnosticError(s,o,`Element matching "${s}" not found on current screen.`);let c=l.find(P=>P.clickable)||l[0];if(t.targetPackage&&!rs(c.packageName,t.targetPackage))throw console.log(pe.red(` \u{1F6A8} [Jail Guard] Blocked Tap on foreign package "${c.packageName}". Target is "${t.targetPackage}".`)),await Ms(o,t.targetPackage,t.driver,t.serial),new Error(`Security Boundary Violation: Refused to tap element belonging to foreign application "${c.packageName}". Containment enforced.`);if(await t.ensureInViewport(c,t.serial)){let P=await t.waitForTarget(s,t.serial,1500,e);P.matched.length>0&&(o=P.flat,l=P.matched,c=l.find($=>$.clickable)||l[0])}let d=c.bounds.centerX,{effectiveHeight:u}=xn(t),h=c.bounds.centerY;if(c.bounds.y2>u&&(h=Math.min(Math.round((c.bounds.y1+u)/2),u-15)),h=Math.min(h,c.bounds.y2-2),h=Math.max(h,c.bounds.y1+2),h>n*.5&&typeof t.driver.isKeyboardVisible=="function")try{await t.driver.isKeyboardVisible(t.serial)&&(console.log(pe.gray(` [Keyboard] Target occluded by soft keyboard (Y=${h} > ${Math.round(n*.5)}). Dismissing...`)),await t.driver.pressBack(t.serial),await ne(300),t.setLastHierarchy(null))}catch{}let g=Ue(o,t.screenDimensions),b=c.contentDesc||c.text||c.resourceId;console.log(pe.gray(` [Tap Target] (${d}, ${h}) for "${b||s}"`));let T=t.lastHierarchy?.hash;await t.driver.tap(d,h,t.serial),t.setLastHierarchy(null);let x=t.upcomingSteps&&t.upcomingSteps[0],S=x?.target?t.matchNodes(o,x.target,x).length>0:!1;if(x&&!S&&(x.type==="VERIFY"||x.type==="TAP")){let P=await Fs(t,4500),R=Ue(P.flat,t.screenDimensions)!==g,C=x.target?t.matchNodes(P.flat,x.target,x).length>0:!1;if(!R&&!C&&t.matchNodes(P.flat,s,e).length>0)throw new Error(`Navigation failed: Tap on "${s}" did not transition away from the current screen. Next expected element "${x.target}" is not visible.`)}else await ne(I.settle.tapMs);b&&ae(b)!==ae(s)&&t.memory.recordSuccess("TAP",s,b,c.contentDesc?"contentDesc":c.text?"text":"resourceId",1,void 0,T)}},Hs=class{type="LONG_PRESS";async execute(e,t){let s=e.target,{flat:r,matched:n}=await t.waitForTarget(s,t.serial,I.polling.defaultTimeoutMs,e);if(n.length===0)throw t.buildDiagnosticError(s,r,`Element matching "${s}" not found for long-press.`);let i=n[0];if(await t.ensureInViewport(i,t.serial)){let l=await t.waitForTarget(s,t.serial,1500,e);l.matched.length>0&&(i=l.matched[0])}await t.driver.swipe(i.bounds.centerX,i.bounds.centerY,i.bounds.centerX,i.bounds.centerY,I.gestures.longPressDurationMs,t.serial),await ne(300),t.setLastHierarchy(null)}},os=class{type="TYPE";async execute(e,t){let s=e.target,r=e.value??"",n=[],i=null;if(t.lastHierarchy&&Date.now()-t.lastHierarchy.timestamp<I.polling.hierarchyCacheTtlMs){let d=t.findTargetInputField(t.lastHierarchy.flat,s,r);d&&(n=t.lastHierarchy.flat,i=d)}if(i||(n=(await t.getCleanHierarchy(t.serial)).flat,i=t.findTargetInputField(n,s,r)),!i){let d=Date.now();for(;Date.now()-d<2e3&&(await ne(200),n=(await t.getCleanHierarchy(t.serial)).flat,i=t.findTargetInputField(n,s,r),!i););}if(!i&&t.upcomingSteps&&t.upcomingSteps.length>0&&!n.some(u=>u.className.includes("EditText"))){let u=t.upcomingSteps.findIndex(h=>h.type==="VERIFY"&&h.target&&t.matchNodes(n,h.target,h).length>0);if(u!==-1)return{fastForwarded:!0,reason:`Screen already displays "${t.upcomingSteps[u].target}" and form is completed`,skipCount:u}}if(!i)throw t.buildDiagnosticError(s,n,`Input field matching "${s}" not found.`);if(t.targetPackage&&!rs(i.packageName,t.targetPackage))throw console.log(pe.red(` \u{1F6A8} [Jail Guard] Blocked Type on foreign package "${i.packageName}". Target is "${t.targetPackage}".`)),await Ms(n,t.targetPackage,t.driver,t.serial),new Error(`Security Boundary Violation: Refused to type into element belonging to foreign application "${i.packageName}". Containment enforced.`);if(i.text&&i.text===r){console.log(pe.gray(` [Input Field] Already contains desired value "${r}". Skipping typing.`));return}if(await t.ensureInViewport(i,t.serial)){n=(await t.getCleanHierarchy(t.serial)).flat;let u=t.findTargetInputField(n,s,r);u&&(i=u)}let{height:l}=t.screenDimensions;if(i.bounds.centerY>l*.5&&typeof t.driver.isKeyboardVisible=="function")try{await t.driver.isKeyboardVisible(t.serial)&&(console.log(pe.gray(" [Keyboard] Input field occluded by soft keyboard. Dismissing before focus...")),await t.driver.pressBack(t.serial),await ne(300),t.setLastHierarchy(null))}catch{}let c=t.lastHierarchy?.hash;await t.driver.tap(i.bounds.centerX,i.bounds.centerY,t.serial),await ne(I.settle.inputFocusMs),await t.driver.clearField(t.serial),await ne(I.settle.inputClearMs),await t.driver.inputText(r,t.serial),await ne(I.settle.inputDoneMs),t.setLastHierarchy(null);let p=i.contentDesc||i.resourceId;p&&ae(p)!==ae(s)&&t.memory.recordSuccess("TYPE",s,p,i.contentDesc?"contentDesc":"resourceId",1,void 0,c)}},Bs=class{type="CLEAR";async execute(e,t){let s=e.target,r=[],n=null;if(t.lastHierarchy&&Date.now()-t.lastHierarchy.timestamp<I.polling.hierarchyCacheTtlMs&&(n=t.findTargetInputField(t.lastHierarchy.flat,s),n&&(r=t.lastHierarchy.flat)),n||(r=(await t.getCleanHierarchy(t.serial)).flat,n=t.findTargetInputField(r,s)),!n){let o=t.matchNodes(r,s,e);o.length>0&&(n=o[0])}if(!n)throw t.buildDiagnosticError(s,r,`Input field matching "${s}" not found to clear.`);await t.driver.tap(n.bounds.centerX,n.bounds.centerY,t.serial),await ne(300);let i=t.driver;i.clearField?await i.clearField(t.serial):i.clearInput&&await i.clearInput(50,t.serial),await ne(200),t.setLastHierarchy(null)}},Us=class{type="VERIFY";async execute(e,t){let s=e.target,r=e.expectedState||"visible";if(e.negative===!0||r==="absent"||r==="hidden"){let u=await t.getCleanHierarchy(t.serial);if(t.matchNodes(u.flat,s,e).length>0)throw new Error(`Negative assertion failed: "${s}" was expected to be NOT visible, but is present on screen.`);return}let{width:i,height:o}=t.screenDimensions,l=Math.round(i*.5),c=!1,p=[],d;if(t.lastHierarchy&&Date.now()-t.lastHierarchy.timestamp<I.polling.verifyCacheTtlMs&&(d=t.matchNodes(t.lastHierarchy.flat,s,e)[0],d&&(p=t.lastHierarchy.flat,c=!0,t.lastHierarchy.timestamp=Date.now())),!c){p=(await t.getCleanHierarchy(t.serial)).flat,d=t.matchNodes(p,s,e)[0],d?c=!0:Os(p).loading&&(console.log(pe.cyan(" \u23F3 [Quiescence] Loading indicator detected in Verify. Waiting for screen to settle...")),p=(await Fs(t,4500)).flat,d=t.matchNodes(p,s,e)[0],d&&(c=!0));let h=Date.now();for(;!c&&Date.now()-h<I.polling.extendedTimeoutMs;){if(p=(await t.getCleanHierarchy(t.serial)).flat,d=t.matchNodes(p,s,e)[0],d){c=!0,t.lastHierarchy&&(t.lastHierarchy.timestamp=Date.now()),await ne(400);break}await ne(200)}}if(c||(await t.driver.swipe(l,Math.round(o*.75),l,Math.round(o*.35),300,t.serial),await ne(400),p=(await t.getCleanHierarchy(t.serial)).flat,d=t.matchNodes(p,s,e)[0],d&&(c=!0)),!c){if(t.upcomingSteps&&t.upcomingSteps.length>0&&!p.some(h=>h.password===!0||h.className.includes("EditText")&&p.some(g=>g.text?.toLowerCase().includes("password")))){let h=t.upcomingSteps.findIndex(g=>g.type==="VERIFY"&&g.target&&t.matchNodes(p,g.target,g).length>0);if(h!==-1)return{fastForwarded:!0,reason:`Screen is already on "${t.upcomingSteps[h].target}"`,skipCount:h}}throw await t.driver.swipe(l,Math.round(o*.35),l,Math.round(o*.75),300,t.serial),await ne(300),t.buildDiagnosticError(s,p,`Verification assertion failed: "${s}" is not visible on screen.`)}if(d&&r==="disabled"&&d.enabled)throw new Error(`State assertion failed: "${s}" was expected to be DISABLED, but is enabled.`);if(d&&r==="enabled"&&!d.enabled)throw new Error(`State assertion failed: "${s}" was expected to be ENABLED, but is disabled.`)}},js=class{type="BACK";async execute(e,t){let{flat:s}=await t.getCleanHierarchy(t.serial),r=Ls(s);if(r&&At(r)){console.log(pe.yellow(` \u{1F6E1}\uFE0F [Jail Guard] Device is already on launcher (${r}). Suppressing Back press to prevent app exit loop.`)),t.targetPackage&&await t.driver.launchApp(t.targetPackage,t.serial);return}await t.driver.pressKey(4,t.serial),await ne(I.settle.backMs),t.setLastHierarchy(null)}},Ys=class{type="SCROLL";async execute(e,t){let{width:s,height:r}=t.screenDimensions,n=Math.round(s*.5);if(e.target&&e.target!=="up"&&e.target!=="down"){let o=e.target,l=e.direction||"down",c=!1;for(let p=0;p<5;p++){let{flat:d}=await t.getCleanHierarchy(t.serial),u=t.matchNodes(d,o,e);if(u.length>0){await t.ensureInViewport(u[0],t.serial),c=!0;break}l==="up"?await t.driver.swipe(n,Math.round(r*.35),n,Math.round(r*.75),300,t.serial):await t.driver.swipe(n,Math.round(r*.75),n,Math.round(r*.35),300,t.serial),await ne(I.settle.scrollMs),t.setLastHierarchy(null)}if(!c){let{flat:p}=await t.getCleanHierarchy(t.serial);throw t.buildDiagnosticError(o,p,`Element "${o}" not found after scrolling ${e.direction||"down"}.`)}}else e.target==="up"||e.direction==="up"?await t.driver.swipe(n,Math.round(r*.35),n,Math.round(r*.75),300,t.serial):await t.driver.swipe(n,Math.round(r*.75),n,Math.round(r*.35),300,t.serial),await ne(I.settle.scrollMs),t.setLastHierarchy(null)}},Ws=class{type="SWIPE";async execute(e,t){let{width:s,height:r}=t.screenDimensions,n=Math.round(s*.5),i=e.direction||"left";if(e.target){let{matched:d}=await t.waitForTarget(e.target,t.serial,2500,e);if(d.length>0){let u=d[0],h=i==="left"?u.bounds.x2-40:i==="right"?u.bounds.x1+40:u.bounds.centerX,g=i==="left"?u.bounds.x1+40:i==="right"?u.bounds.x2-40:u.bounds.centerX,b=i==="up"?u.bounds.y2-40:i==="down"?u.bounds.y1+40:u.bounds.centerY,T=i==="up"?u.bounds.y1+40:i==="down"?u.bounds.y2-40:u.bounds.centerY;await t.driver.swipe(h,b,g,T,I.gestures.swipeDurationMs,t.serial),await ne(400),t.setLastHierarchy(null);return}}let o=i==="left"?Math.round(s*.8):i==="right"?Math.round(s*.2):n,l=i==="left"?Math.round(s*.2):i==="right"?Math.round(s*.8):n,c=Math.round(i==="up"?r*.75:i==="down"?r*.35:r*.5),p=Math.round(i==="up"?r*.35:i==="down"?r*.75:r*.5);await t.driver.swipe(o,c,l,p,I.gestures.swipeDurationMs,t.serial),await ne(400),t.setLastHierarchy(null)}},Js=class{type="TOGGLE";async execute(e,t){let s=e.target,{flat:r,matched:n}=await t.waitForTarget(s,t.serial,I.polling.defaultTimeoutMs,e);if(n.length===0)throw t.buildDiagnosticError(s,r,`Toggle element matching "${s}" not found.`);let i=n.find(l=>l.clickable)||n[0];if(await t.ensureInViewport(i,t.serial)){let l=await t.waitForTarget(s,t.serial,1500,e);l.matched.length>0&&(i=l.matched.find(c=>c.clickable)||l.matched[0])}if(e.desiredState!==void 0&&i.checked!==void 0&&i.checked===e.desiredState){console.log(pe.gray(` [Toggle] "${s}" is already in desired state (${e.desiredState?"ON":"OFF"}).`));return}await t.driver.tap(i.bounds.centerX,i.bounds.centerY,t.serial),await ne(I.settle.toggleMs),t.setLastHierarchy(null)}},Vs=class{type="SELECT";async execute(e,t){let s=e.target,r=e.value,{flat:n,matched:i}=await t.waitForTarget(s,t.serial,I.polling.defaultTimeoutMs,e);if(i.length===0)throw t.buildDiagnosticError(s,n,`Dropdown / picker element matching "${s}" not found.`);let o=i.find(p=>p.clickable)||i[0];await t.driver.tap(o.bounds.centerX,o.bounds.centerY,t.serial),await ne(I.settle.selectMs),t.setLastHierarchy(null);let l=await t.waitForTarget(r,t.serial,I.polling.defaultTimeoutMs);if(l.matched.length===0)throw t.buildDiagnosticError(r,l.flat,`Dropdown option "${r}" not found.`);let c=l.matched[0];await t.driver.tap(c.bounds.centerX,c.bounds.centerY,t.serial),await ne(400),t.setLastHierarchy(null)}},zs=class{type="LAUNCH_APP";async execute(e,t){let s=e.target;if(!s)throw new Error("Target package name required to launch app.");t.targetPackage=s,await t.driver.launchApp(s,t.serial),await ne(I.settle.launchMs),t.setLastHierarchy(null)}},Ks=class{type="RESTART_APP";async execute(e,t){let s=e.target;if(!s)throw new Error("Target package name required to restart app.");t.targetPackage=s,await t.driver.restartApp(s,t.serial),await ne(I.settle.launchMs),t.setLastHierarchy(null)}},Xs=class{type="TERMINATE_APP";async execute(e,t){let s=e.target||await t.driver.getForegroundPackage(t.serial);s&&await t.driver.forceStop(s,t.serial),await ne(I.settle.stopMs),t.setLastHierarchy(null)}},Gs=class{type="CLEAR_DATA";async execute(e,t){let s=e.target;if(!s)throw new Error("Target package name required to clear data.");await t.driver.clearAppData(s,t.serial),await ne(1e3),t.setLastHierarchy(null)}},Qs=class{type="GRANT_PERMISSION";async execute(e,t){let s=e.value,r=e.target||await t.driver.getForegroundPackage(t.serial);if(!r)throw new Error("Foreground package not detected for grant permission.");await t.driver.grantPermission(r,s,t.serial),await ne(400)}},Zs=class{type="DEEP_LINK";async execute(e,t){let s=e.target;await t.driver.openDeepLink(s,t.serial),await ne(I.settle.launchMs),t.setLastHierarchy(null)}},qs=class{type="WAIT";async execute(e,t){await ne(e.durationMs||1e3)}},er=class{type="IF_VISIBLE";async execute(e,t){let s=e.conditionTarget||e.target;if(!s)return;let r=!1;try{let{matched:n}=await t.waitForTarget(s,t.serial,1500,e);r=n.length>0}catch{r=!1}r?(console.log(pe.cyan(` [Conditional] Condition met: "${s}" is visible.`)),e.thenSteps&&e.thenSteps.length>0&&t.executeChildSteps&&await t.executeChildSteps(e.thenSteps,t)):(console.log(pe.gray(` [Conditional] Condition not met: "${s}" is not visible.`)),e.elseSteps&&e.elseSteps.length>0&&t.executeChildSteps&&await t.executeChildSteps(e.elseSteps,t))}},tr=class{type="LOOP";async execute(e,t){let s=I.flow?.maxLoopIterations||50,r=Math.min(e.loopCount||1,s),n=e.childSteps||[];if(!(n.length===0||!t.executeChildSteps)){console.log(pe.cyan(` [Loop] Starting loop iteration (1 to ${r})...`));for(let i=1;i<=r;i++){e.loopVariable&&t.variables.set(e.loopVariable,String(i)),t.loopStack.push({current:i,total:r,variable:e.loopVariable});try{await t.executeChildSteps(n,t)}finally{t.loopStack.pop()}}}}},sr=class{type="WAIT_UNTIL";async execute(e,t){let s=e.target,r=e.durationMs||I.flow?.waitUntilTimeoutMs||1e4,n=e.expectedState||"visible";console.log(pe.gray(` [Wait Until] Waiting for "${s}" to be ${n} (up to ${r}ms)...`));let i=Date.now(),o=!1;for(;Date.now()-i<r;){try{let{matched:l}=await t.waitForTarget(s,t.serial,1e3,e);if(l.length>0){let c=l[0];if(n==="enabled"&&!c.enabled){await ne(250);continue}o=!0;break}}catch{}await ne(250)}if(!o){let{flat:l}=await t.getCleanHierarchy(t.serial);throw t.buildDiagnosticError(s,l,`WaitUntil assertion failed: "${s}" did not become ${n} within ${r}ms.`)}}},rr=class{type="SET_VARIABLE";async execute(e,t){let s=e.variableName,r=e.variableExpression||"";r=Pt(r,Object.fromEntries(t.variables));let n=Rr(r);t.variables.set(s,n),console.log(pe.cyan(` [Variable] $${s} = "${n}"`))}},ir=class{type="DEVICE_STATE";async execute(e,t){let s=e.deviceAction,r=e.deviceStateValue;switch(s){case"wifi":{let n=r===!0||r==="on"||r==="enable";console.log(pe.cyan(` [Device] Setting Wi-Fi: ${n?"ENABLED":"DISABLED"}`)),await t.driver.setWifiEnabled(n,t.serial);break}case"airplane":{let n=r===!0||r==="on"||r==="enable";console.log(pe.cyan(` [Device] Setting Airplane Mode: ${n?"ENABLED":"DISABLED"}`)),await t.driver.setAirplaneMode(n,t.serial);break}case"orientation":{let n=r==="landscape"?"landscape":"portrait";console.log(pe.cyan(` [Device] Setting Orientation: ${n.toUpperCase()}`)),await t.driver.setOrientation(n,t.serial);break}case"notifications":{let n=r==="open"||r==="expand"||r===!0;console.log(pe.cyan(` [Device] ${n?"Expanding":"Collapsing"} Notification Shade`)),n?await t.driver.openNotificationShade(t.serial):await t.driver.collapseNotificationShade(t.serial);break}default:throw new Error(`Unsupported device action: ${s}`)}await ne(400),t.setLastHierarchy(null)}},nr=class{type="RUN_JS";async execute(e,t){let s=e.jsCode||e.value||"";if(!s)return;console.log(pe.cyan(" [JS Hook] Executing sandboxed JavaScript snippet..."));let r={vars:Object.fromEntries(t.variables),setVar:(i,o)=>{t.variables.set(i,String(o))},getVar:i=>t.variables.get(i),console:{log:(...i)=>console.log(pe.gray(" [JS Output]",...i)),error:(...i)=>console.error(pe.red(" [JS Error]",...i))}},n=ii.createContext(r);ii.runInContext(s,n,{timeout:3e3});for(let[i,o]of Object.entries(r.vars))t.variables.set(i,String(o))}},or=class{type="ENTER_OTP";async execute(e,t){console.log(pe.cyan(" [OTP Capture] Detecting verification code..."));let s=null;if(process.env.PROMPTTEST_MOCK_OTP)s=process.env.PROMPTTEST_MOCK_OTP.trim(),console.log(pe.gray(` [OTP Capture] Captured code from environment: ${s}`));else try{let{stdout:r}=await t.driver.execAdb(["shell","logcat","-d","-t","200","-s","SMSDispatcher:V","SmsMessage:V","NotificationService:V","ActivityManager:I"],t.serial),n=r.match(/(?:code|otp|pin|verification)[^0-9\r\n]{0,35}(\d{4,8})\b/i)||r.match(/\b(\d{4,8})\s*(?:is your verification|is your otp)/i);n&&(s=n[1],console.log(pe.gray(` [OTP Capture] Captured code from logcat: ${s}`)))}catch{}if(s||(s="123456",console.log(pe.yellow(` [OTP Capture] No live code detected; using simulated OTP: ${s}`))),e.target){let r={originalText:`Type '${s}' into '${e.target}'`,type:"TYPE",target:e.target,value:s};return new os().execute(r,t)}else{let{flat:r}=await t.getCleanHierarchy(t.serial),n=t.findTargetInputField(r,"code",s)||r.find(i=>i.className.includes("EditText"));n?(await t.driver.tap(n.bounds.centerX,n.bounds.centerY,t.serial),await ne(250),await t.driver.inputText(s,t.serial)):await t.driver.inputText(s,t.serial)}}},ar=class{type="API_REQUEST";async execute(e,t){let s=e.apiMethod||"GET",r=e.apiUrl||"";if(!r)throw new Error("API_REQUEST requires a valid target URL.");console.log(pe.cyan(` [API Request] ${s} ${r}...`));let n={method:s,headers:{"Content-Type":"application/json",Accept:"application/json"}};e.apiBody&&(s==="POST"||s==="PUT"||s==="PATCH")&&(n.body=e.apiBody);let i=await fetch(r,n);if(e.apiExpectedStatus!==void 0){if(i.status!==e.apiExpectedStatus)throw new Error(`API status assertion failed for ${r}: expected HTTP ${e.apiExpectedStatus}, but received HTTP ${i.status}`);console.log(pe.green(` \u2714 API Status HTTP ${i.status} matched expectation.`))}if(e.apiStorePath&&e.apiStoreVariable)try{let o=await i.text(),l=JSON.parse(o),c=this.resolveJsonPath(l,e.apiStorePath);c!==void 0&&(t.variables.set(e.apiStoreVariable,String(c)),console.log(pe.cyan(` [Variable] $${e.apiStoreVariable} = "${c}" (from ${e.apiStorePath})`)))}catch(o){qe.warn(`Failed to parse JSON response for path extraction: ${o.message}`)}}resolveJsonPath(e,t){let s=t.replace(/^\$\.?/,"").split("."),r=e;for(let n of s){if(r==null||typeof r!="object")return;r=r[n]}return r}},cr=class{type="EMAIL_ASSERT";async execute(e,t){let s=e.emailRecipient||"",r=e.emailSubject||"";console.log(pe.cyan(` [Email Assertion] Checking email to "${s}" with subject "${r}"...`));let n=[];if(process.env.PROMPTTEST_MOCK_EMAILS)try{n=JSON.parse(process.env.PROMPTTEST_MOCK_EMAILS)}catch{n=[]}let i=n.find(o=>o.to.toLowerCase()===s.toLowerCase()&&o.subject.toLowerCase().includes(r.toLowerCase()));if(!i){if(n.length===0){if(!s.includes("@"))throw new Error(`Invalid email recipient address: "${s}"`);console.log(pe.yellow(" \u26A0 No mock inbox configured (PROMPTTEST_MOCK_EMAILS); simulated email check passed."));return}throw new Error(`Email assertion failed: No email to "${s}" with subject "${r}" found.`)}if(e.emailBodyContains&&i.body&&!i.body.includes(e.emailBodyContains))throw new Error(`Email body assertion failed: Expected email to contain "${e.emailBodyContains}", but content did not match.`);console.log(pe.green(" \u2714 Email verified successfully."))}},Tn=[new _s,new Hs,new os,new Bs,new Us,new js,new Ys,new Ws,new Js,new Vs,new zs,new Ks,new Xs,new Gs,new Qs,new Zs,new qs,new er,new tr,new sr,new rr,new ir,new nr,new or,new ar,new cr],ni=new Map(Tn.map(a=>[a.type,a]));var $t=class{providerName="heuristic";async resolveIntent(e,t){let s=ae(e),r=s.includes("login")||s.includes("log in")||s.includes("sign in")||s.includes("signin");if(r&&(s.includes("student")||s.includes("learner")))return[{originalText:"Type 'student@coachconnect.com' into 'Email'",type:"TYPE",target:"Email",value:"student@coachconnect.com"},{originalText:"Type 'Password123!' into 'Password'",type:"TYPE",target:"Password",value:"Password123!"},{originalText:"Tap 'Sign In'",type:"TAP",target:"Sign In"}];if(r&&(s.includes("teacher")||s.includes("coach")||s.includes("trainer")))return[{originalText:"Type 'teacher@coachconnect.com' into 'Email'",type:"TYPE",target:"Email",value:"teacher@coachconnect.com"},{originalText:"Type 'Password123!' into 'Password'",type:"TYPE",target:"Password",value:"Password123!"},{originalText:"Tap 'Sign In'",type:"TAP",target:"Sign In"}];if(r&&s.includes("admin"))return[{originalText:"Type 'admin@coachconnect.com' into 'Email'",type:"TYPE",target:"Email",value:"admin@coachconnect.com"},{originalText:"Type 'AdminPass123!' into 'Password'",type:"TYPE",target:"Password",value:"AdminPass123!"},{originalText:"Tap 'Sign In'",type:"TAP",target:"Sign In"}];if(r)return[{originalText:"Type 'user@example.com' into 'Email'",type:"TYPE",target:"Email",value:"user@example.com"},{originalText:"Type 'Password123!' into 'Password'",type:"TYPE",target:"Password",value:"Password123!"},{originalText:"Tap 'Sign In'",type:"TAP",target:"Sign In"}];if(s.includes("sign up with random user")||s.includes("register new user"))return[{originalText:"Type $RANDOM_NAME into 'Name'",type:"TYPE",target:"Name",value:"$RANDOM_NAME"},{originalText:"Type $RANDOM_EMAIL into 'Email'",type:"TYPE",target:"Email",value:"$RANDOM_EMAIL"},{originalText:"Type 'Password123!' into 'Password'",type:"TYPE",target:"Password",value:"Password123!"},{originalText:"Tap 'Create Account'",type:"TAP",target:"Create Account"}];if(s.includes("dismiss dialog")||s.includes("close modal")||s.includes("dismiss popup"))return[{originalText:"Tap 'Close'",type:"TAP",target:"Close",optional:!0},{originalText:"Tap 'Dismiss'",type:"TAP",target:"Dismiss",optional:!0},{originalText:"Press back",type:"BACK",optional:!0}];let n=e.match(/(?:login|sign\s*in)\s+(?:with\s+)?(?:user(?:name)?|email)?\s*["']([^"']+)["']\s*(?:and\s+)?(?:password|pass)?\s*["']([^"']+)["']/i);if(n){let o=n[1],l=n[2];return[{originalText:`Type "${o}" into "Email"`,type:"TYPE",target:"Email",value:o},{originalText:`Type "${l}" into "Password"`,type:"TYPE",target:"Password",value:l},{originalText:'Tap "Sign In"',type:"TAP",target:"Sign In"}]}let i=e.match(/search\s+(?:for\s+)?["']([^"']+)["']/i);if(i){let o=i[1];return[{originalText:'Tap "Search"',type:"TAP",target:"Search"},{originalText:`Type "${o}" into "Search"`,type:"TYPE",target:"Search",value:o},{originalText:'Tap "Search"',type:"TAP",target:"Search"}]}return s.includes("checkout")||s.includes("proceed to payment")?[{originalText:'Tap "Checkout"',type:"TAP",target:"Checkout"},{originalText:'Assert "Payment" is visible',type:"VERIFY",target:"Payment"}]:s.includes("logout")||s.includes("log out")||s.includes("sign out")?[{originalText:'Tap "Settings"',type:"TAP",target:"Settings",optional:!0},{originalText:'Tap "Log Out"',type:"TAP",target:"Log Out"},{originalText:'Tap "Confirm"',type:"TAP",target:"Confirm",optional:!0}]:null}async findSuggestions(e,t,s){let r=[],n=ae(e),i=new Set,o=Vr(e,s?.locale);for(let d of o){let u=t.find(h=>h.bounds.width>0&&h.bounds.height>0&&(de(d,h.text)||de(d,h.contentDesc)));if(u){let h=(u.text||u.contentDesc||d).trim();i.has(h)||(i.add(h),r.push({text:h,score:.9,node:u,strategy:"synonym",reason:`Direct synonym match for "${e}" via semantic dictionary`}))}}for(let d of t)if(d.resourceId&&d.bounds.width>0&&d.bounds.height>0&&d.resourceId.toLowerCase().split(/[:/_]/).some(g=>g===n||n.includes(g))){let g=(d.text||d.contentDesc||d.resourceId).trim();i.has(g)||(i.add(g),r.push({text:g,score:.85,node:d,strategy:"exact",reason:`Resource-ID segment matches target "${e}"`}))}let l=[];for(let d of t)d.bounds.width>0&&d.bounds.height>0&&(d.text&&d.text.trim().length>1&&l.push({text:d.text.trim(),node:d}),d.contentDesc&&d.contentDesc.trim().length>1&&l.push({text:d.contentDesc.trim(),node:d}));let c=Array.from(new Set(l.map(d=>d.text))),p=Rt(e,c,5);for(let d of p)if(d.score>=.7&&!i.has(d.text)){i.add(d.text);let u=l.find(h=>h.text===d.text)?.node;r.push({text:d.text,score:d.score,node:u,strategy:"fuzzy",reason:`String similarity match (${Math.round(d.score*100)}%)`})}return r.sort((d,u)=>u.score-d.score)}};var Bt=class{providerName="mock";mockIntents=new Map;mockSuggestions=new Map;constructor(){this.mockIntents.set("log in to account",[{originalText:"Type 'test@example.com' into 'Email'",type:"TYPE",target:"Email",value:"test@example.com"},{originalText:"Type 'Password123' into 'Password'",type:"TYPE",target:"Password",value:"Password123"},{originalText:"Tap 'Sign In'",type:"TAP",target:"Sign In"}])}setMockIntent(e,t){this.mockIntents.set(e.toLowerCase().trim(),t)}setMockSuggestion(e,t){this.mockSuggestions.set(e.toLowerCase().trim(),t)}async resolveIntent(e,t){let s=e.toLowerCase().trim();return this.mockIntents.has(s)?this.mockIntents.get(s):null}async findSuggestions(e,t,s){let r=e.toLowerCase().trim();return this.mockSuggestions.has(r)?this.mockSuggestions.get(r):new $t().findSuggestions(e,t)}},as=class{providerName;config;heuristicFallback;constructor(e){this.config=e,this.providerName=e.provider,this.heuristicFallback=new $t}async resolveIntent(e,t){if(this.config.provider==="mock")return new Bt().resolveIntent(e,t);try{let s=`You are a test automation engineer generating steps for PromptTest.
809
839
  Convert the following high-level user intent into a concrete sequence of plain-English test steps.
810
840
  Valid syntax examples:
811
841
  - Tap 'Button Label'
@@ -818,86 +848,57 @@ Valid syntax examples:
818
848
  User Intent: "${e}"
819
849
 
820
850
  Return ONLY a JSON array of plain strings representing each step, e.g.:
821
- ["Type 'user@test.com' into 'Email'", "Tap 'Next'"]`,i=await this.callLlm(s);if(!i)return this.heuristicFallback.resolveIntent(e,t);let r=i.match(/\[[\s\S]*\]/);return r?JSON.parse(r[0]).map(c=>({originalText:c,type:c.startsWith("Tap")?"TAP":c.startsWith("Type")?"TYPE":"VERIFY"})):this.heuristicFallback.resolveIntent(e,t)}catch(s){return Ge.warn(`LLM intent resolution failed (${s.message}), falling back to heuristic engine.`),this.heuristicFallback.resolveIntent(e,t)}}async findSuggestions(e,t,s){if(this.config.provider==="mock")return new Lt().findSuggestions(e,t,s);try{let i=t.filter(l=>l.bounds.width>0&&l.bounds.height>0&&(l.text||l.contentDesc)).map(l=>({text:l.text||l.contentDesc,id:l.resourceId||"",class:l.className.split(".").pop()})).slice(0,30),r=`A UI test failed because an element named "${e}" could not be found.
851
+ ["Type 'user@test.com' into 'Email'", "Tap 'Next'"]`,r=await this.callLlm(s);if(!r)return this.heuristicFallback.resolveIntent(e,t);let n=r.match(/\[[\s\S]*\]/);return n?JSON.parse(n[0]).map(l=>({originalText:l,type:l.startsWith("Tap")?"TAP":l.startsWith("Type")?"TYPE":"VERIFY"})):this.heuristicFallback.resolveIntent(e,t)}catch(s){return qe.warn(`LLM intent resolution failed (${s.message}), falling back to heuristic engine.`),this.heuristicFallback.resolveIntent(e,t)}}async findSuggestions(e,t,s){if(this.config.provider==="mock")return new Bt().findSuggestions(e,t,s);try{let r=t.filter(c=>c.bounds.width>0&&c.bounds.height>0&&(c.text||c.contentDesc)).map(c=>({text:c.text||c.contentDesc,id:c.resourceId||"",class:c.className.split(".").pop()})).slice(0,30),n=`A UI test failed because an element named "${e}" could not be found.
822
852
  Here are the visible UI elements on the current Android screen:
823
- ${JSON.stringify(i,null,2)}
853
+ ${JSON.stringify(r,null,2)}
824
854
 
825
855
  Identify the best matching replacement element for "${e}".
826
856
  Respond ONLY with a JSON object in this format:
827
- {"bestMatch": "Exact Text of Best Element", "confidence": 0.85, "reason": "Reason for match"}`,n=await this.callLlm(r);if(!n)return this.heuristicFallback.findSuggestions(e,t,s);let o=n.match(/\{[\s\S]*\}/);if(!o)return this.heuristicFallback.findSuggestions(e,t,s);let c=JSON.parse(o[0]);if(c.bestMatch){let l=t.find(p=>p.text===c.bestMatch||p.contentDesc===c.bestMatch);return[{text:c.bestMatch,score:typeof c.confidence=="number"?c.confidence:.85,node:l,strategy:"llm",reason:c.reason||"LLM semantic match"},...await this.heuristicFallback.findSuggestions(e,t,s)]}}catch(i){Ge.warn(`LLM self-healing failed (${i.message}), using heuristic suggestions.`)}return this.heuristicFallback.findSuggestions(e,t,s)}async callLlm(e){let t=this.config.timeoutMs||8e3,s=new AbortController,i=setTimeout(()=>s.abort(),t);try{if(this.config.provider==="openai"){let r=this.config.apiKey||process.env.OPENAI_API_KEY;if(!r)throw new Error("OPENAI_API_KEY is not set");let n=await fetch("https://api.openai.com/v1/chat/completions",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${r}`},body:JSON.stringify({model:this.config.model||"gpt-4o-mini",messages:[{role:"user",content:e}],temperature:.1}),signal:s.signal});if(!n.ok)throw new Error(`OpenAI HTTP ${n.status}: ${await n.text()}`);return(await n.json()).choices?.[0]?.message?.content||null}if(this.config.provider==="anthropic"){let r=this.config.apiKey||process.env.ANTHROPIC_API_KEY;if(!r)throw new Error("ANTHROPIC_API_KEY is not set");let n=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:{"Content-Type":"application/json","x-api-key":r,"anthropic-version":"2023-06-01"},body:JSON.stringify({model:this.config.model||"claude-3-haiku-20240307",max_tokens:1024,messages:[{role:"user",content:e}]}),signal:s.signal});if(!n.ok)throw new Error(`Anthropic HTTP ${n.status}: ${await n.text()}`);return(await n.json()).content?.[0]?.text||null}if(this.config.provider==="ollama"){let r=this.config.endpoint||process.env.OLLAMA_BASE_URL||"http://localhost:11434",n=await fetch(`${r.replace(/\/$/,"")}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:this.config.model||"llama3.2",messages:[{role:"user",content:e}],stream:!1}),signal:s.signal});if(!n.ok)throw new Error(`Ollama HTTP ${n.status}: ${await n.text()}`);return(await n.json()).message?.content||null}return null}finally{clearTimeout(i)}}};function Qs(a){let e=a?.provider||C.ai.provider;if(e==="heuristic")return new xt;if(e==="mock")return new Lt;let t={provider:e,model:a?.model||C.ai.model,endpoint:a?.endpoint||C.ai.endpoint};return new es(t)}import Bi from"fs";import Zs from"path";var Mt=class{static extractDataDirective(e,t=process.cwd()){let s=e.split(`
828
- `),i="",r=-1;for(let p=0;p<s.length;p++){let d=s[p].trim();if(d.startsWith("#!data")||d.startsWith("#! data")){i=d,r=p;break}}if(r===-1)return null;let n=i.replace(/^#!\s*data\s*/,"").trim(),o=[],c="inline";if(n.startsWith("[")||n.startsWith("{"))try{let p=JSON.parse(n);o=Array.isArray(p)?p.map(d=>this.stringifyObject(d)):[this.stringifyObject(p)]}catch(p){throw new Error(`Failed to parse inline #!data JSON directive: ${p.message}`)}else{let p=n.replace(/^['"]+|['"]+$/g,"").trim(),d=Zs.isAbsolute(p)?p:Zs.resolve(t,p);c=d,o=this.loadFromFile(d)}let l=[...s];return l.splice(r,1),{directive:i,source:c,cleanedSpec:l.join(`
829
- `),rows:o}}static loadFromFile(e){if(!Bi.existsSync(e))throw new Error(`Data file not found: ${e}`);let t=Bi.readFileSync(e,"utf-8");return Zs.extname(e).toLowerCase()===".json"?this.parseJson(t):this.parseCsv(t)}static parseJson(e){try{let t=JSON.parse(e);return Array.isArray(t)?t.map(s=>this.stringifyObject(s)):[this.stringifyObject(t)]}catch(t){throw new Error(`Invalid JSON data file format: ${t.message}`)}}static parseCsv(e){let t=this.splitCsvLines(e);if(t.length<2)return[];let s=this.parseCsvRow(t[0]).map(r=>r.trim().replace(/^['"]+|['"]+$/g,"")),i=[];for(let r=1;r<t.length;r++){if(!t[r].trim())continue;let o=this.parseCsvRow(t[r]),c={};for(let l=0;l<s.length;l++){let p=s[l];c[p]=o[l]!==void 0?o[l].trim():""}i.push(c)}return i}static filterRows(e,t){if(!t)return e;let s=[...e];if(t.onlyRow!==void 0&&t.onlyRow>0){let i=t.onlyRow-1;if(i<s.length)return[s[i]];throw new Error(`Requested --only-row=${t.onlyRow}, but dataset only contains ${s.length} row(s).`)}if(t.iterations!==void 0&&t.iterations>0){if(t.iterations<=s.length)return s.slice(0,t.iterations);let i=[];for(let r=0;r<t.iterations;r++)i.push(s[r%s.length]);return i}return s}static parseCsvRow(e){let t=[],s="",i=!1;for(let r=0;r<e.length;r++){let n=e[r];n==='"'?i&&e[r+1]==='"'?(s+='"',r++):i=!i:n===","&&!i?(t.push(s),s=""):s+=n}return t.push(s),t}static splitCsvLines(e){let t=[],s="",i=!1;for(let r=0;r<e.length;r++){let n=e[r];n==='"'?i&&e[r+1]==='"'?(s+='""',r++):(i=!i,s+=n):(n===`
830
- `||n==="\r")&&!i?(n==="\r"&&e[r+1]===`
831
- `&&r++,s.trim().length>0&&t.push(s),s=""):s+=n}return s.trim().length>0&&t.push(s),t}static stringifyObject(e){if(!e||typeof e!="object")return{};let t={};for(let[s,i]of Object.entries(e))t[s]=i!=null?String(i):"";return t}};import Qn from"http";import ee from"fs";import z from"path";import zi from"os";import{fileURLToPath as Zn}from"url";import Te from"fs";import ye from"path";import U from"chalk";import{spawn as Jn}from"child_process";import Ui from"readline";import{createRequire as Xn}from"module";var zn=Xn(import.meta.url),qs="1.1.0";try{qs=zn("../../package.json").version||"1.1.0"}catch{}var lt=class{onStep;driver;deviceLock=null;isRecording=!1;recordedSteps=[];existingSteps=[];existingFileHeader=[];lastHierarchyFlat=[];lastScreenTitle="";visitedScreens=[];targetPackage=null;isAppendMode=!1;resolvedOutputPath="";resolvedReportPath="";activeInput=null;geteventProcess=null;isRefreshingHierarchy=!1;targetSerial="";touchMaxX=C.screen.fallbackWidth;touchMaxY=C.screen.fallbackHeight;displayWidth=C.screen.fallbackWidth;displayHeight=C.screen.fallbackHeight;activeTouchX=-1;activeTouchY=-1;touchDownTime=0;isTouchActive=!1;isSwipeGesture=!1;lastHierarchyTimestamp=0;lastTapTimestamp=0;lastRecordedTapTarget="";inFlightTapQueue=[];inputDebounceTimer=null;logcatEventProcess=null;logcatCrashProcess=null;hierarchyCacheTime=0;hierarchyStale=!0;lastScrollTimestamp=0;lastScrollDirection="";currentScreenName="";sessionScreenSections=[];sessionStartTime=0;screenshotDir="";screenshotCounter=0;crashDetected=!1;lastScreenTransitionTime=0;constructor(e){this.driver=e||ke}flushToDisk(){if(this.resolvedOutputPath)try{this.saveSpec(this.resolvedOutputPath)}catch{}if(this.resolvedReportPath)try{this.saveReport(this.resolvedReportPath)}catch{}}appendStep(e,t){if(this.recordedSteps[this.recordedSteps.length-1]!==e){this.recordedSteps.push(e),this.flushToDisk(),t&&console.log(t);try{this.onStep?.(e)}catch{}}}extractBrandSlug(e){if(!e)return"app";let t=new Set(["com","org","net","io","in","co","uk","de","fr","us","ai","dev","android","mobile","app","apps","client","release","debug","main","browser","ui","view","experience","portal","phone","tablet","service"]),s=e.split("."),i=s.filter(n=>!t.has(n.toLowerCase())&&!/^\d+$/.test(n));return((i.length>0?i[i.length-1]:s[s.length-1])||"app").toLowerCase().replace(/[^a-z0-9_]/g,"")}async startSession(e,t,s){let i=await this.driver.ensureDefaultDevice(t);if(!i)throw new Error("No Android device connected for recording.");this.deviceLock=yt(i),this.targetSerial=i,this.isRecording=!0,this.recordedSteps=[],this.existingSteps=[],this.existingFileHeader=[],this.visitedScreens=[],this.targetPackage=null,this.activeInput=null,this.isAppendMode=!!s?.append;try{let h=await this.driver.getForegroundPackage(i);h&&(this.targetPackage=h)}catch{}let r=this.extractBrandSlug(this.targetPackage),n;if(e&&e!=="specs/recorded_suite.txt")n=ye.resolve(e);else{let h=`${r}_flow`,u=ye.resolve(`specs/${h}.txt`);if(!this.isAppendMode&&Te.existsSync(u)){let m=1;for(u=ye.resolve(`specs/${h}_${m}.txt`);Te.existsSync(u);)m++,u=ye.resolve(`specs/${h}_${m}.txt`)}n=u}if(this.resolvedOutputPath=n,Te.existsSync(n))if(this.isAppendMode){let h=Te.readFileSync(n,"utf-8"),u=h.split(`
832
- `);for(let y of u){let v=y.trim();if(/^\d+\.\s+/.test(v))this.existingSteps.push(v.replace(/^\d+\.\s+/,""));else if(v.startsWith("# Package:")){let T=v.match(/# Package:\s*([a-zA-Z0-9._]+)/);T&&(this.targetPackage=T[1])}}let m=`${n}.bak`;Te.writeFileSync(m,h,"utf-8"),console.log(U.cyan(`\u{1F4E5} Loaded ${this.existingSteps.length} existing step(s) from: ${ye.basename(n)}`)),console.log(U.gray(`\u{1F4E6} Safety backup preserved at: ${ye.basename(m)}`))}else{let h=Te.readFileSync(n,"utf-8"),u=`${n}.bak`;Te.writeFileSync(u,h,"utf-8"),console.log(U.yellow(`\u26A0\uFE0F File "${ye.basename(n)}" already exists.`)),console.log(U.gray(`\u{1F4E6} Safety backup preserved at: ${ye.basename(u)}`))}await this.driver.wakeDevice(i).catch(()=>{});let c=await this.driver.getScreenDimensions(i);this.displayWidth=c.width||C.screen.fallbackWidth,this.displayHeight=c.height||C.screen.fallbackHeight,this.touchMaxX=this.displayWidth,this.touchMaxY=this.displayHeight;try{let{stdout:h}=await this.driver.execAdb(["shell","getevent","-p"],i),u=h.match(/0035\s*:\s*value\s*\d+,\s*min\s*\d+,\s*max\s*(\d+)/),m=h.match(/0036\s*:\s*value\s*\d+,\s*min\s*\d+,\s*max\s*(\d+)/);u&&parseInt(u[1])>0&&(this.touchMaxX=parseInt(u[1])),m&&parseInt(m[1])>0&&(this.touchMaxY=parseInt(m[1]))}catch{}this.sessionStartTime=Date.now(),this.sessionScreenSections=[],this.screenshotCounter=0,this.crashDetected=!1,this.hierarchyStale=!0,this.currentScreenName="";let l=ye.basename(n,".txt"),p=ye.resolve("output");this.screenshotDir=ye.join(p,"screenshots",l),Te.existsSync(this.screenshotDir)||Te.mkdirSync(this.screenshotDir,{recursive:!0});let d=ye.join(p,`${l}_report.md`);this.resolvedReportPath=d,await this.refreshHierarchySync(),this.startLogcatScreenWatcher(i),this.startLogcatCrashWatcher(i),this.startEventStream(i),this.flushToDisk()}async stopSession(){if(this.isRecording=!1,this.inputDebounceTimer&&(clearTimeout(this.inputDebounceTimer),this.inputDebounceTimer=null),this.deviceLock&&(St(this.deviceLock),this.deviceLock=null),this.geteventProcess){try{this.geteventProcess.kill()}catch{}this.geteventProcess=null}if(this.logcatEventProcess){try{this.logcatEventProcess.kill()}catch{}this.logcatEventProcess=null}if(this.logcatCrashProcess){try{this.logcatCrashProcess.kill()}catch{}this.logcatCrashProcess=null}return this.flushToDisk(),{steps:[...this.recordedSteps],specPath:this.resolvedOutputPath,reportPath:this.resolvedReportPath}}async startRecording(e,t,s){await this.startSession(e,t,s);let i=U.gray("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),r=await this.driver.getDeviceModel(this.targetSerial).catch(()=>this.targetSerial);console.log(`
833
- ${i}`),console.log(U.bold.cyan(`\u26A1 PromptTest v${qs}`)+U.gray(" | Recording Session")),console.log(i),console.log(` ${U.gray("\u{1F4F1} Device ")} ${U.white(r)} ${U.gray(`(${this.targetSerial})`)}`),console.log(` ${U.gray("\u{1F4E6} App ")} ${U.green(this.targetPackage||"Unknown")}`),console.log(` ${U.gray("\u{1F3AF} Spec ")} ${U.yellow(ye.basename(this.resolvedOutputPath))}`),console.log(` ${U.gray("\u{1F4CA} Report ")} ${U.yellow(ye.basename(this.resolvedReportPath))}`),console.log(` ${U.gray("\u{1F4F8} Photos ")} ${U.yellow(ye.relative(process.cwd(),this.screenshotDir))}`),this.isAppendMode&&this.existingSteps.length>0&&console.log(` ${U.gray("\u{1F4CE} Mode ")} ${U.magenta(`Append \u2014 resuming from step #${this.existingSteps.length+1}`)}`),console.log(i),console.log(` ${U.green("\u2714")} Ready & streaming`),console.log(i),console.log(` ${U.gray("Use the app naturally. Every action is captured.")}`),console.log(` ${U.gray('Press Ctrl+C or type "exit" / "q" + Enter to stop and save.')}`),console.log(`${i}
834
- `);let n=null,o=async()=>{if(n)try{n.close()}catch{}await this.stopSession(),this.printExitSummary(i),process.exit(0)};if(process.once("SIGINT",o),process.once("SIGTERM",o),process.stdin.isTTY)try{n=Ui.createInterface({input:process.stdin,output:process.stdout}),n.on("line",l=>{let p=l.trim().toLowerCase();(p==="exit"||p==="quit"||p==="q"||p==="stop")&&o()}),n.on("SIGINT",()=>{o()})}catch{}let c=0;for(;this.isRecording;)if(await new Promise(l=>setTimeout(l,1500)),c++,c%20===0&&this.driver.wakeDevice(this.targetSerial).catch(()=>{}),!this.targetPackage){let l=await this.driver.getForegroundPackage(this.targetSerial).catch(()=>null);l&&(this.targetPackage=l)}}startEventStream(e){let t=e?["-s",e,"shell","getevent","-l"]:["shell","getevent","-l"],s=Xe(),i=Jn(s,t);this.geteventProcess=i;let r=null,n=null,o=null,c=null,l=0;Ui.createInterface({input:i.stdout}).on("line",d=>{if(this.isRecording)if(d.includes("ABS_MT_POSITION_X")){let h=d.match(/ABS_MT_POSITION_X\s+([0-9a-fA-F]+)/);h&&(r=parseInt(h[1],16))}else if(d.includes("ABS_MT_POSITION_Y")){let h=d.match(/ABS_MT_POSITION_Y\s+([0-9a-fA-F]+)/);h&&(n=parseInt(h[1],16))}else if(d.includes("BTN_TOUCH")&&d.includes("DOWN"))o=r,c=n,l=Date.now();else if(d.includes("BTN_TOUCH")&&d.includes("UP")){let h=r!==null?r:o,u=n!==null?n:c,m=Date.now()-l;if(h!==null&&u!==null&&o!==null&&c!==null){let y=Math.round(o/this.touchMaxX*this.displayWidth),v=Math.round(c/this.touchMaxY*this.displayHeight),T=Math.round(h/this.touchMaxX*this.displayWidth),S=Math.round(u/this.touchMaxY*this.displayHeight),b=T-y,E=S-v,D=Math.abs(b),B=Math.abs(E),k=Math.hypot(D,B);if(this.activeInput&&v>this.displayHeight*.58&&S>this.displayHeight*.58){m>=20&&m<400&&k<45&&T>=this.displayWidth*.78&&S>=this.displayHeight*.84?this.flushActiveInput().then(()=>{this.handleKeyPress("Enter")}).catch(()=>{}):this.scheduleInputDebounce(),r=null,n=null,o=null,c=null;return}if(m>=20&&m<400&&k<45)this.isRefreshingHierarchy?this.inFlightTapQueue.push({x:T,y:S,timestamp:Date.now()}):this.handleTapAt(T,S);else if(m>=450&&k<45)this.handleLongPressAt(T,S);else if(B>=100&&B>1.4*D){let j=E<0?"down":"up";this.handleScroll(j)}else if(D>=100&&D>1.4*B){let j=b<0?"left":"right";this.handleSwipe(j)}}r=null,n=null,o=null,c=null}else d.includes("KEY_ENTER")&&d.includes("DOWN")?this.handleKeyPress("Enter"):d.includes("KEY_BACK")&&d.includes("DOWN")&&this.handleKeyPress("Back")}),i.on("close",d=>{this.isRecording&&(console.warn(U.yellow(`
835
- \u26A0\uFE0F [EventStream] ADB event listener stream closed (code ${d}). Auto-reconnecting in 800ms...`)),setTimeout(()=>{this.isRecording&&this.startEventStream(e)},800))}),i.on("error",d=>{this.isRecording&&console.warn(U.yellow(`
836
- \u26A0\uFE0F [EventStream] ADB stream error: ${d.message}.`))})}cleanLabel(e){return e?e.replace(/[\uE000-\uF8FF\uFFF0-\uFFFF\uFFFD]/gu,"").replace(/,\s*tab,\s*\d+\s*of\s*\d+/gi,"").replace(/,\s*button$/gi,"").replace(/^[\s,•\-_:;|/\\]+|[\s,•\-_:;|/\\]+$/g,"").trim():""}handleScroll(e){let t=Date.now();if(this.lastScrollDirection===e&&t-this.lastScrollTimestamp<850)return;this.lastScrollTimestamp=t,this.lastScrollDirection=e,this.activeInput&&this.flushActiveInput().catch(()=>{});let s=`Scroll ${e}`;this.recordedSteps[this.recordedSteps.length-1]!==s&&(this.appendStep(s,U.blue(` \u{1F4DC} Recorded Scroll [Step #${this.getCurrentStepNumber()}]: ${U.bold(s)}`)),this.triggerAsyncHierarchyRefresh(400))}handleSwipe(e){let t=Date.now();if(t-this.lastScrollTimestamp<600)return;this.lastScrollTimestamp=t,this.activeInput&&this.flushActiveInput().catch(()=>{});let s=`Swipe ${e}`;this.appendStep(s,U.blue(` \u{1F449} Recorded Swipe [Step #${this.getCurrentStepNumber()}]: ${U.bold(s)}`)),this.triggerAsyncHierarchyRefresh(500)}handleLongPressAt(e,t){let s=this.lastHierarchyFlat.filter(n=>e>=n.bounds.x1&&e<=n.bounds.x2&&t>=n.bounds.y1&&t<=n.bounds.y2);if(s.length===0)return;s.sort((n,o)=>{let c=(n.bounds.x2-n.bounds.x1)*(n.bounds.y2-n.bounds.y1),l=(o.bounds.x2-o.bounds.x1)*(o.bounds.y2-o.bounds.y1);return c-l});let i=s[0],r=this.resolveActionLabel(i,s,this.lastHierarchyFlat);if(r){let n=`Long press '${r}'`;this.appendStep(n,U.cyan(` \u23F1\uFE0F Recorded Long Press [Step #${this.getCurrentStepNumber()}]: ${U.bold(n)}`)),this.triggerAsyncHierarchyRefresh()}}handleTapAt(e,t){let s=Date.now(),i=this.lastHierarchyFlat.filter(d=>e>=d.bounds.x1&&e<=d.bounds.x2&&t>=d.bounds.y1&&t<=d.bounds.y2),n=this.lastHierarchyFlat.filter(d=>{let h=d.bounds.height;return h>0&&h<320&&d.bounds.width>0&&(d.clickable||d.contentDesc||d.text)}).filter(d=>t>=d.bounds.y1-30&&t<=d.bounds.y2+40);if(n.length>=2){let d=n.find(h=>e>=h.bounds.x1-20&&e<=h.bounds.x2+20);d&&(i=[this.lastHierarchyFlat.find(u=>(this.cleanLabel(u.text).length>0||this.cleanLabel(u.contentDesc).length>0)&&Math.abs(u.bounds.centerX-d.bounds.centerX)<d.bounds.width*.48&&Math.abs(u.bounds.centerY-d.bounds.centerY)<d.bounds.height*.48&&!this.isIconGlyph(this.cleanLabel(u.text)))||d])}if(t>this.displayHeight*.82){let h=this.lastHierarchyFlat.filter(u=>{let m=u.bounds.y1>=this.displayHeight*.78||u.bounds.y2>=this.displayHeight*.82,y=u.bounds.width>0&&u.bounds.width<=this.displayWidth*.52;return m&&y}).find(u=>e>=u.bounds.x1-20&&e<=u.bounds.x2+20);if(h){let u=this.lastHierarchyFlat.find(m=>(this.cleanLabel(m.text).length>0||this.cleanLabel(m.contentDesc).length>0)&&e>=m.bounds.x1-25&&e<=m.bounds.x2+25&&m.bounds.y1>=this.displayHeight*.78&&!this.isIconGlyph(this.cleanLabel(m.text)));u?i=[u]:(this.cleanLabel(h.text)||this.cleanLabel(h.contentDesc))&&(i=[h])}}if(i.length===0){let d=this.lastHierarchyFlat.filter(m=>(this.cleanLabel(m.text).length>0||this.cleanLabel(m.contentDesc).length>0)&&(m.clickable||m.className.includes("Button")||m.className.includes("Text"))),h=null,u=70;for(let m of d){let y=Math.hypot(m.bounds.centerX-e,m.bounds.centerY-t);y<u&&(u=y,h=m)}if(h)i=[h];else{this.triggerAsyncHierarchyRefresh();return}}i.sort((d,h)=>{let u=(d.bounds.x2-d.bounds.x1)*(d.bounds.y2-d.bounds.y1),m=(h.bounds.x2-h.bounds.x1)*(h.bounds.y2-h.bounds.y1);return u-m});let o=i[0],c=i.find(d=>d.className.includes("EditText"))||(o.className.includes("EditText")?o:null);if(c){this.activeInput&&this.activeInput.node!==c&&this.flushActiveInput().catch(()=>{});let d=this.resolveFieldLabel(c,this.lastHierarchyFlat);this.activeInput={node:c,label:d,startText:c.text||""},this.triggerAsyncHierarchyRefresh();return}this.activeInput&&this.flushActiveInput().catch(()=>{});let l=this.resolveActionLabel(o,i,this.lastHierarchyFlat);if(!l||l.length===0){this.triggerAsyncHierarchyRefresh();return}if(!o.clickable&&!i.some(d=>d.clickable||d.className.includes("Button"))&&/^\d+%$|^\d+$/.test(l)){this.triggerAsyncHierarchyRefresh();return}if(l===this.lastRecordedTapTarget&&s-this.lastTapTimestamp<350)return;this.lastTapTimestamp=s,this.lastRecordedTapTarget=l;let p;if(o.checkable||o.className.includes("Switch")||o.className.includes("CheckBox")){let d=o.checked!==void 0?!o.checked:!0;p=`Toggle '${l}' to ${d?"on":"off"}`}else{p=`Tap '${l}'`;let d=this.lastHierarchyFlat.filter(u=>this.cleanLabel(u.text)===l||this.cleanLabel(u.contentDesc)===l),h=[];for(let u of d)h.some(y=>Math.hypot(y.bounds.centerX-u.bounds.centerX,y.bounds.centerY-u.bounds.centerY)<40)||h.push(u);if(h.length>1){let m=this.lastHierarchyFlat.filter(y=>y!==o&&Math.abs(y.bounds.centerY-o.bounds.centerY)<140&&Math.abs(y.bounds.centerX-o.bounds.centerX)<550).find(y=>{let v=this.cleanLabel(y.text)||this.cleanLabel(y.contentDesc);return v.length>2&&v!==l&&!/^\d+$/.test(v)&&!this.isIconGlyph(v)});if(m){let y=this.cleanLabel(m.text)||this.cleanLabel(m.contentDesc);y&&y!==l&&(p=`Tap '${l}' next to '${y}'`)}}}this.appendStep(p,U.cyan(` \u{1F446} Recorded Tap [Step #${this.getCurrentStepNumber()}]: ${U.bold(p)}`)),this.triggerAsyncHierarchyRefresh()}handleKeyPress(e){if(this.activeInput&&this.flushActiveInput().catch(()=>{}),e==="Enter"){let t="Press 'Enter'";this.appendStep(t,U.magenta(` \u21B5 Recorded Key [Step #${this.getCurrentStepNumber()}]: ${U.bold(t)}`))}else if(e==="Back"){let t="Tap 'Back'";this.appendStep(t,U.magenta(` \u25C0 Recorded Key [Step #${this.getCurrentStepNumber()}]: ${U.bold(t)}`))}this.triggerAsyncHierarchyRefresh()}scheduleInputDebounce(){this.inputDebounceTimer&&clearTimeout(this.inputDebounceTimer),this.inputDebounceTimer=setTimeout(()=>{this.activeInput&&this.syncActiveInputText().catch(()=>{})},450)}async flushActiveInput(){if(this.inputDebounceTimer&&(clearTimeout(this.inputDebounceTimer),this.inputDebounceTimer=null),!this.activeInput)return;let e=this.activeInput;this.activeInput=null,await this.recordInputText(e)}async syncActiveInputText(){this.activeInput&&await this.recordInputText(this.activeInput)}async recordInputText(e){try{let t=await this.driver.getUiHierarchy(this.targetSerial).catch(()=>null);if(!t)return;let{flat:s}=we(t);this.lastHierarchyFlat=s;let i=s.find(r=>r.className.includes("EditText")&&(r.focused||r.selected))||s.find(r=>r.className.includes("EditText")&&this.resolveFieldLabel(r,s).toLowerCase()===e.label.toLowerCase())||s.find(r=>r.className.includes("EditText")&&Math.abs(r.bounds.centerX-e.node.bounds.centerX)<80)||s.find(r=>r.className.includes("EditText"));if(i&&i.text&&i.text.trim().length>0){let r=i.text.trim(),n=/^[•*•\u2022\u25cf]+$/.test(r);if(!(r===e.startText)&&!n){if(e.startText===""&&this.isPlaceholderText(r))return;let c=e.label||this.resolveFieldLabel(i,s),l=`Type '${r}' into '${c}'`,p=this.recordedSteps.length-1,d=p>=0?this.recordedSteps[p]:"";d.startsWith("Type ")&&d.endsWith(`into '${c}'`)?d!==l&&(this.recordedSteps[p]=l,this.flushToDisk(),console.log(U.gray(` \u270D\uFE0F Updated Input [Step #${p+1+this.existingSteps.length}]: ${U.bold(l)}`))):this.appendStep(l,U.green(` \u270D\uFE0F Recorded Input [Step #${this.getCurrentStepNumber()}]: ${U.bold(l)}`))}else if(n&&(i.password||e.label.toLowerCase().includes("password"))){let c=e.label||"Password",l=`Type '<password>' into '${c}'`,p=this.recordedSteps.length-1,d=p>=0?this.recordedSteps[p]:"";(!d.startsWith("Type ")||!d.endsWith(`into '${c}'`))&&this.appendStep(l,U.green(` \u270D\uFE0F Recorded Password Input [Step #${this.getCurrentStepNumber()}]: ${U.bold(l)}`))}}}catch{}}startLogcatScreenWatcher(e){try{this.logcatEventProcess=this.driver.streamLogcatEvents(e,t=>{if(this.isRecording&&(t.includes("wm_on_resume_called")||t.includes("am_on_resume_called"))){let s=Date.now();if(s-this.lastScreenTransitionTime<500)return;this.lastScreenTransitionTime=s,this.hierarchyStale=!0,this.triggerAsyncHierarchyRefresh(50)}})}catch{}}startLogcatCrashWatcher(e){try{this.logcatCrashProcess=this.driver.streamLogcatCrashes(e,t=>{if(this.isRecording&&(t.includes("FATAL EXCEPTION")||t.includes("ANR in"))){if(this.targetPackage&&!t.includes(this.targetPackage))return;this.crashDetected=!0,console.log(U.red.bold(`
837
- \u26A0\uFE0F [Crash Alert] Fatal exception / ANR detected in ${this.targetPackage||"app"}!`))}})}catch{}}captureSessionScreenshot(e){if(!this.screenshotDir)return;let t=++this.screenshotCounter,s=e.toLowerCase().replace(/[^a-z0-9_]/g,"_").slice(0,30)||"screen",i=`screen_${String(t).padStart(2,"0")}_${s}.png`,r=ye.join(this.screenshotDir,i);(async()=>{try{let n=await this.driver.captureScreenshot(this.targetSerial);if(n&&n.length>0){await Te.promises.writeFile(r,n);let o=this.sessionScreenSections[this.sessionScreenSections.length-1];o&&!o.screenshotFile&&(o.screenshotFile=i)}}catch{}})()}triggerAsyncHierarchyRefresh(e=C.recorder.asyncRefreshDelayMs){this.isRefreshingHierarchy||(this.isRefreshingHierarchy=!0,setTimeout(()=>{this.doHierarchyRefresh().catch(()=>{this.isRefreshingHierarchy=!1})},e))}async doHierarchyRefresh(){try{let e=await this.driver.getUiHierarchy(this.targetSerial),{flat:t}=we(e);this.lastHierarchyFlat=t,this.hierarchyStale=!1,this.hierarchyCacheTime=Date.now(),this.detectAndLogScreenHeader(t),this.detectAndLogTransientAlerts(t)}catch{}finally{for(this.isRefreshingHierarchy=!1;this.inFlightTapQueue.length>0;){let e=this.inFlightTapQueue.shift();if(!e)break;Date.now()-e.timestamp<C.recorder.staleTapTimeoutMs?this.handleTapAt(e.x,e.y):console.warn(U.yellow(`[Recorder] Dropped stale tap at (${e.x}, ${e.y}) \u2014 screen refresh took longer than ${C.recorder.staleTapTimeoutMs}ms`))}}}detectAndLogTransientAlerts(e){let t=e.find(s=>(s.className.includes("Toast")||s.className.includes("Snackbar")||s.resourceId&&s.resourceId.includes("snackbar")||s.contentDesc&&(s.contentDesc.includes("Alert")||s.contentDesc.includes("Success")))&&(s.text||s.contentDesc));if(t){let s=this.cleanLabel(t.text)||this.cleanLabel(t.contentDesc);if(s&&s.length>3&&s.length<80){let i=`Verify '${s}' is visible`;this.recordedSteps.slice(-3).includes(i)||this.appendStep(i,U.green(` \u2714 Recorded Alert [Step #${this.getCurrentStepNumber()}]: ${U.bold(i)}`))}}}async refreshHierarchySync(){try{let e=await this.driver.getUiHierarchy(this.targetSerial),{flat:t}=we(e);this.lastHierarchyFlat=t,this.hierarchyStale=!1,this.hierarchyCacheTime=Date.now(),this.detectAndLogScreenHeader(t)}catch{}}isIconGlyph(e){if(!e||e.trim().length===0)return!0;let t=e.trim();return!!(/[\uE000-\uF8FF\uFFF0-\uFFFF]/.test(t)||t.length===1&&t.charCodeAt(0)>255&&!new RegExp("\\p{Extended_Pictographic}","u").test(t))}cleanDialogOrCompoundLabel(e){if(!e)return"";let t=e.trim();if(t.includes(",")||t.includes(`
838
- `)){let s=t.split(/[\n,]+/).map(i=>this.cleanLabel(i)).filter(Boolean);if(s.length>1){let i=s.find(n=>n.includes("?")||/\b(are you sure|do you want|cannot be undone|would you like|please confirm)\b/i.test(n)||n.length>35);if(i){let n=s.find(o=>o!==i&&o.length<=30&&!o.includes("?"));if(n)return n}let r=s.filter(n=>/^[A-Z]{1,3}$/.test(n)||/^\d+%?$/.test(n)||this.isCardMetadataSubtitle(n)||/^(attend|present|absent|status|active|inactive)$/i.test(n)?!1:n.length>1);if(r.length>0)return r[0]}}return t}isCardMetadataSubtitle(e){if(!e||e.trim().length===0)return!1;let t=e.trim();return!!(/\+?\d{1,3}[\s-]?\d{4,}[\s-]?\d{4,}/.test(t)||/^(STD|ID|EMP|REF|ORD|USER|ACC|BATCH)[\-_:\s]?\d+/i.test(t)||/[•|·]/.test(t)&&/\d/.test(t)||/^\d+$/.test(t)||/^\d{1,2}:\d{2}/.test(t)||/^(teacher|roll\s*no|std\s*id|class|sec)[\s:]+/i.test(t))}findPrimaryCardTitle(e,t,s){if(e.bounds.y1>=this.displayHeight*.8||e.bounds.centerY>=this.displayHeight*.82||e.clickable&&(e.className.includes("Button")||e.className.includes("Check")))return null;let i=this.cleanLabel(e.text),r=this.isCardMetadataSubtitle(i),n=s.find(l=>l!==e&&l.clickable&&l.bounds.height>60&&l.bounds.height<600),o;n?o=t.filter(l=>l.className.includes("Text")&&l.bounds.x1>=n.bounds.x1-10&&l.bounds.x2<=n.bounds.x2+10&&l.bounds.y1>=n.bounds.y1-10&&l.bounds.y2<=n.bounds.y2+10):o=t.filter(l=>l!==e&&l.className.includes("Text")&&l.bounds.centerY<e.bounds.centerY&&e.bounds.centerY-l.bounds.centerY<160&&Math.abs(l.bounds.centerX-e.bounds.centerX)<500);let c=o.filter(l=>{let p=this.cleanLabel(l.text);return p.length>2&&!this.isCardMetadataSubtitle(p)&&!this.isIconGlyph(p)&&!/^\d+$/.test(p)&&!/^(active|inactive|present|absent|new|pending)$/i.test(p)}).sort((l,p)=>l.bounds.y1-p.bounds.y1);if(c.length>0){let l=this.cleanLabel(c[0].text);if(r||n&&l!==i&&!e.clickable)return l}return null}resolveActionLabel(e,t,s){if(e.bounds.y1>=this.displayHeight*.8||e.bounds.centerY>=this.displayHeight*.82){let l=this.cleanLabel(e.contentDesc)||this.cleanLabel(e.text);if(l&&l.length>0&&!this.isIconGlyph(l))return this.cleanDialogOrCompoundLabel(l);for(let p of t){let d=this.cleanLabel(p.contentDesc)||this.cleanLabel(p.text);if(d&&d.length>0&&!this.isIconGlyph(d))return this.cleanDialogOrCompoundLabel(d)}}let i=this.findPrimaryCardTitle(e,s,t);if(i)return this.cleanDialogOrCompoundLabel(i);let r=this.cleanLabel(e.text);if(r.length>0&&r.length<90){let l=r.split(/[\n\r]+/).map(d=>this.cleanLabel(d)).filter(Boolean),p=l.length>1?l.find(d=>d.length>2&&!/^\d+$/.test(d))||l[0]:r;return this.cleanDialogOrCompoundLabel(p)}let n=this.cleanLabel(e.contentDesc);if(n.length>0&&n.length<90)return this.cleanDialogOrCompoundLabel(n);let o=t.find(l=>(l.clickable||l.className.includes("Button"))&&(this.cleanLabel(l.text)||this.cleanLabel(l.contentDesc)));if(o){let l=this.cleanLabel(o.text)||this.cleanLabel(o.contentDesc);if(l.length>0&&l.length<90)return this.cleanDialogOrCompoundLabel(l)}for(let l of t){let p=this.cleanLabel(l.text);if(p.length>0&&p.length<90){let h=p.split(/[\n\r]+/).map(m=>this.cleanLabel(m)).filter(Boolean),u=h.length>1?h.find(m=>m.length>2&&!/^\d+$/.test(m))||h[0]:p;return this.cleanDialogOrCompoundLabel(u)}let d=this.cleanLabel(l.contentDesc);if(d.length>0&&d.length<90)return this.cleanDialogOrCompoundLabel(d)}let c=s.find(l=>l!==e&&(this.cleanLabel(l.text).length>0||this.cleanLabel(l.contentDesc).length>0)&&Math.abs(l.bounds.centerX-e.bounds.centerX)<80&&Math.abs(l.bounds.centerY-e.bounds.centerY)<100);if(c){let l=this.cleanLabel(c.text)||this.cleanLabel(c.contentDesc);if(l.length>0)return this.cleanDialogOrCompoundLabel(l)}if(e.resourceId){let l=e.resourceId.split("/"),p=l[l.length-1];if(p&&!/^\d+$/.test(p)){let d=p.replace(/_/g," ").replace(/btn|button|icon|view|action/gi,"").trim();if(d.length>1)return d.charAt(0).toUpperCase()+d.slice(1)}}return""}getCurrentStepNumber(){return this.existingSteps.length+this.recordedSteps.length+1}detectAndLogScreenHeader(e){let t=e.filter(s=>{if(!s.className.includes("TextView")||s.text.trim().length<2||s.packageName&&(s.packageName.includes("systemui")||s.packageName.includes("launcher"))||s.bounds.y1<60||s.bounds.y1>=450)return!1;let i=s.text.trim();return!(this.isCardMetadataSubtitle(i)||/\b(JIO|Airtel|Vodafone|No service|Emergency|VoLTE|4G|5G)\b/i.test(i)||/^\d+%?$|^\d+$/.test(i)||/^step\s*\d+\s*(of|\/)\s*\d+/i.test(i))}).sort((s,i)=>s.bounds.y1-i.bounds.y1);if(t.length>0){let s=t[0].text.trim();if(s&&s!==this.lastScreenTitle&&!s.includes(":")&&s.length<40){this.lastScreenTitle=s,this.currentScreenName=s,this.visitedScreens.includes(s)||this.visitedScreens.push(s),this.sessionScreenSections.push({name:s,stepStart:this.recordedSteps.length}),this.captureSessionScreenshot(s);let i=`Verify '${s}' is visible`;this.recordedSteps[this.recordedSteps.length-1]!==i&&!this.recordedSteps.slice(-3).includes(i)&&this.appendStep(i,U.green(` \u2714 Recorded Header [Step #${this.getCurrentStepNumber()}]: ${U.bold(i)}`))}}}resolveFieldLabel(e,t){if(e.contentDesc&&e.contentDesc.trim().length>0)return e.contentDesc.trim();if(e.password)return"Password";let s=t.filter(i=>i.className.includes("TextView")&&i.text&&i.text.trim().length>1&&i.bounds.centerY<e.bounds.centerY&&e.bounds.centerY-i.bounds.centerY<250&&Math.abs(i.bounds.centerX-e.bounds.centerX)<450);if(s.length>0){s.sort((r,n)=>n.bounds.centerY-r.bounds.centerY);let i=s[0].text.trim().replace(/[*:]/g,"").trim();if(i.length>0)return i}if(e.resourceId){let i=e.resourceId.split("/"),r=i[i.length-1];if(r&&!/^\d+$/.test(r)){let n=r.replace(/_/g," ").replace(/edit|text|input|field|et|txt/gi,"").trim();return n.length>1?n.charAt(0).toUpperCase()+n.slice(1):r}}if(e.text&&e.text.trim().length>0&&e.text.trim().length<40){let i=e.text.trim();if(i.toLowerCase().startsWith("search"))return i.length<=25?i:"Search";if(!this.isPlaceholderText(i))return i}return"Input Field"}isPlaceholderText(e){let t=e.trim().toLowerCase();return t.startsWith("search")||t.startsWith("e.g.")||t.startsWith("enter ")||t.startsWith("type ")||t.includes("optional")}saveSpec(e){let t=ye.resolve(e);if(ye.basename(t).startsWith("app_flow")&&this.targetPackage&&!this.isAppendMode){let c=this.extractBrandSlug(this.targetPackage);if(c!=="app"){let l=`${c}_flow`,p=ye.resolve(`specs/${l}.txt`),d=1;for(;Te.existsSync(p);)p=ye.resolve(`specs/${l}_${d}.txt`),d++;t=p}}let i=ye.dirname(t);Te.existsSync(i)||Te.mkdirSync(i,{recursive:!0});let r=[];if(this.isAppendMode&&this.existingSteps.length>0&&Te.existsSync(t)){let c=Te.readFileSync(t,"utf-8").trim();r.push(c),r.push(""),r.push(`# --- Appended Session (${new Date().toISOString().replace("T"," ").slice(0,19)}) ---`),this.recordedSteps.forEach((l,p)=>{r.push(`${this.existingSteps.length+p+1}. ${l}`)})}else{r.push("# ==========================================================",`# Autonomous Recorded Test Spec: ${ye.basename(t)}`,...this.targetPackage?[`# Package: ${this.targetPackage}`]:[],`# Generated: ${new Date().toISOString()}`,`# PromptTest v${qs}`,"# ==========================================================","");let c=0;this.recordedSteps.forEach((l,p)=>{if(c<this.sessionScreenSections.length&&p===this.sessionScreenSections[c].stepStart){let d=this.sessionScreenSections[c];r.push(""),r.push(`# Screen: ${d.name}`),c++}r.push(`${this.existingSteps.length+p+1}. ${l}`)})}r.push("");let n=r.join(`
839
- `),o=`${t}.tmp.${Date.now()}`;try{Te.writeFileSync(o,n,"utf-8"),Te.renameSync(o,t)}catch{try{Te.unlinkSync(o)}catch{}Te.writeFileSync(t,n,"utf-8")}}saveReport(e){let t=Math.round((Date.now()-this.sessionStartTime)/1e3),s=Math.floor(t/60),i=t%60,r=`${String(s).padStart(2,"0")}:${String(i).padStart(2,"0")}`,n=this.existingSteps.length+this.recordedSteps.length,o=["# PromptTest Recording Report","","| Property | Value |","|---|---|",`| **Target Package** | \`${this.targetPackage||"Unknown"}\` |`,`| **Device Serial** | \`${this.targetSerial}\` |`,`| **Session Started** | ${new Date(this.sessionStartTime).toISOString()} |`,`| **Session Duration** | ${r} |`,`| **Steps in Session** | ${this.recordedSteps.length} |`,`| **Total Suite Steps** | ${n} |`,`| **Screens Visited** | ${this.visitedScreens.length} |`,`| **Screenshots Saved** | ${this.screenshotCounter} |`,`| **Crash Signals** | ${this.crashDetected?"\u26A0\uFE0F Detected":"\u2705 None"} |`,"","---","","## Screen Journey","",this.visitedScreens.length>0?this.visitedScreens.map((c,l)=>`${l+1}. **${c}**`).join(`
840
- `):"*Single screen flow*","","---","","## Recorded Interactions",""];if(this.sessionScreenSections.length>0)for(let c=0;c<this.sessionScreenSections.length;c++){let l=this.sessionScreenSections[c],p=this.sessionScreenSections[c+1],d=p?p.stepStart:this.recordedSteps.length,h=this.recordedSteps.slice(l.stepStart,d);o.push(`### Screen: ${l.name}`),o.push(""),l.screenshotFile&&(o.push(`![${l.name}](screenshots/${ye.basename(this.screenshotDir)}/${l.screenshotFile})`),o.push("")),h.length>0?h.forEach((u,m)=>{o.push(`${l.stepStart+m+1}. \`${u}\``)}):o.push("*(No interactions on this screen)*"),o.push("")}else o.push("### Steps"),o.push(""),this.recordedSteps.forEach((c,l)=>{o.push(`${l+1}. \`${c}\``)}),o.push("");try{let c=ye.dirname(e);Te.existsSync(c)||Te.mkdirSync(c,{recursive:!0}),Te.writeFileSync(e,o.join(`
841
- `),"utf-8")}catch{}}printExitSummary(e){let t=Math.round((Date.now()-this.sessionStartTime)/1e3),s=Math.floor(t/60),i=t%60,r=`${String(s).padStart(2,"0")}:${String(i).padStart(2,"0")}`,n=this.existingSteps.length+this.recordedSteps.length;console.log(`
842
- ${e}`),console.log(U.bold.green(" \u{1F4BE} Recording Session Saved Successfully!")),console.log(e),console.log(` ${U.gray("Steps Captured ")} ${U.bold.yellow(this.recordedSteps.length.toString())} ${this.isAppendMode?U.gray(`(Total Suite: ${n})`):""}`),console.log(` ${U.gray("Screens Visited")} ${U.bold.white(this.visitedScreens.length.toString())} ${this.visitedScreens.length>0?U.gray(`(${this.visitedScreens.join(" \u2192 ")})`):""}`),console.log(` ${U.gray("Screenshots ")} ${U.bold.white(this.screenshotCounter.toString())} saved`),console.log(` ${U.gray("Session Time ")} ${U.bold.white(r)}`),console.log(` ${U.gray("Crash Signals ")} ${this.crashDetected?U.red.bold("\u26A0\uFE0F Detected"):U.green("\u2714 0 detected")}`),console.log(e),console.log(` ${U.gray("\u{1F4C4} Spec ")} ${U.cyan(this.resolvedOutputPath)}`);let o=ye.basename(this.resolvedOutputPath,".txt");console.log(` ${U.gray("\u{1F4CA} Report ")} ${U.cyan(ye.resolve("output",`${o}_report.md`))}`),console.log(`${e}
843
- `)}},ji=new lt;import ts from"fs";import Yi from"path";import dt from"chalk";async function ss(a=ke,e={}){let t=[],s=process.versions.node,r=parseInt(s.split(".")[0],10)>=18;t.push({name:"Node.js Runtime",passed:r,message:`Node.js v${s} (Required >= 18.0.0)`,hint:r?void 0:"Please upgrade Node.js to v18.0.0 or higher: https://nodejs.org"});let n=!1,o="ADB binary not found in PATH",c='Install Android SDK platform-tools and ensure "adb" is in your system PATH.';try{let{stdout:S}=await a.execAdb(["version"],void 0,5e3),b=S.match(/Android Debug Bridge version ([\d.]+)/i);b?(n=!0,o=`Android Debug Bridge version ${b[1]}`,c=void 0):(n=!0,o=S.split(`
844
- `)[0].trim(),c=void 0)}catch(S){o=`Failed to execute adb: ${S.message}`}t.push({name:"Android Debug Bridge (ADB)",passed:n,message:o,hint:c});let l=!1,p="No connected Android devices or emulators found",d="Connect an Android device via USB/Wi-Fi or launch an emulator with USB Debugging enabled.";try{let S=await a.getConnectedDevices(),b=S.filter(B=>B.state==="device"),E=S.filter(B=>B.state==="unauthorized"),D=S.filter(B=>B.state==="offline");if(b.length>0){l=!0;let B=b.map(k=>`${k.serial} (${k.model||"Unknown"})`).join(", ");p=`Found ${b.length} authorized device(s): ${B}`,d=void 0}else E.length>0?(p=`Device(s) found but unauthorized: ${E.map(B=>B.serial).join(", ")}`,d='Check the device screen and accept the "Allow USB debugging" RSA prompt.'):D.length>0&&(p=`Device(s) found in offline state: ${D.map(B=>B.serial).join(", ")}`,d='Try running "adb reconnect" or reconnect the USB cable.')}catch(S){p=`Failed to list devices: ${S.message}`}if(t.push({name:"Connected Android Device",passed:l,message:p,hint:d}),e.packageName){let S=e.packageName,b=!1,E=`Package "${S}" not found on target device`,D='Install the APK using "adb install <path.apk>" or check the package identifier.';if(l)try{let{stdout:B}=await a.execAdb(["shell","pm","list","packages",S],e.serial,5e3);B.includes(`package:${S}`)&&(b=!0,E=`Package "${S}" is installed on target device`,D=void 0)}catch(B){E=`Failed to query package status: ${B.message}`}else E="Skipped package verification (no authorized device available)",D=`Connect a device to verify package "${S}".`;t.push({name:`Target Package [${S}]`,passed:b,message:E,hint:D})}let h=!0,u="All required workspace directories are accessible",m,y=["output","memory",".prompttest"];for(let S of y)try{let b=Yi.resolve(process.cwd(),S);ts.existsSync(b)||ts.mkdirSync(b,{recursive:!0});let E=Yi.join(b,".doctor_perm_test");ts.writeFileSync(E,"ok","utf-8"),ts.unlinkSync(E)}catch(b){h=!1,u=`Filesystem permission error for "./${S}": ${b.message}`,m="Ensure the current user has write permissions to the workspace directory.";break}t.push({name:"Workspace Storage & Permissions",passed:h,message:u,hint:m});let v=t.filter(S=>S.passed).length,T=t.filter(S=>!S.passed).length;return{timestamp:new Date().toISOString(),allPassed:T===0,checks:t,summary:{passed:v,failed:T,warnings:0}}}function Wi(a){let e=["",dt.bold.cyan("\u{1FA7A} PromptTest Doctor \u2014 Environment Health Diagnostic"),dt.gray(`Scanned at ${a.timestamp}
845
- `)];for(let t of a.checks){let s=t.passed?dt.green("\u2714"):dt.red("\u2716"),i=dt.bold(t.name);e.push(` ${s} ${i}: ${t.message}`),t.hint&&!t.passed&&e.push(dt.yellow(` \u{1F4A1} Fix: ${t.hint}`))}return e.push(""),a.allPassed?e.push(dt.bold.green("\u2728 All diagnostic checks passed! Your environment is ready to test.")):e.push(dt.bold.red(`\u26A0\uFE0F ${a.summary.failed} diagnostic check(s) failed. Please review the remediation hints above.`)),e.push(""),e.join(`
846
- `)}import Kn from"fs";import ei from"path";import L from"chalk";import Vi from"fs";import Gn from"path";var pt=class{testData={};dataPath;isLoaded=!1;driver;constructor(e,t){this.dataPath=e||Gn.resolve(process.cwd(),"test-data.json"),this.driver=t||ke}setTestData(e){this.testData=e,this.isLoaded=!0}async ensureDataLoaded(){if(!this.isLoaded){if(Vi.existsSync(this.dataPath))try{let e=await Vi.promises.readFile(this.dataPath,"utf-8");this.testData=JSON.parse(e)}catch{this.testData={}}this.isLoaded=!0}}async loadTestData(e){return e&&(this.dataPath=e),this.isLoaded=!1,await this.ensureDataLoaded(),this.testData}inferValueForNode(e,t){let s=[e.text,e.contentDesc,e.resourceId].join(" ").toLowerCase(),i="",r=t.filter(o=>o.text&&!o.clickable&&o.bounds.centerY<e.bounds.centerY&&e.bounds.centerY-o.bounds.centerY<250&&Math.abs(o.bounds.centerX-e.bounds.centerX)<400);r.length>0&&(i=r[r.length-1].text.toLowerCase());let n=`${s} ${i}`.toLowerCase();if(n.includes("review")||n.includes("feedback")||n.includes("comment")||n.includes("write a review")||n.includes("experience")){if(!this.testData.review?.text)throw new Error("Missing test-data.json field: review.text");return this.testData.review.text}if(n.includes("card")||n.includes("credit")||n.includes("debit")||n.includes("card number")){let o=this.testData.payment?.card;if(o)return String(o);let c="4532";for(let l=0;l<12;l++)c+=Math.floor(Math.random()*10);return c}if(n.includes("phone")||n.includes("mobile")||!n.includes("card")&&n.includes("number")||n.includes("contact")){let o=this.testData.auth?.phone||this.testData.profile?.phone;if(!o)throw new Error("Missing test-data.json field: auth.phone or profile.phone");return o}if(n.includes("email")||n.includes("e-mail")||n.includes("mail")){let o=this.testData.profile?.email||this.testData.auth?.email;if(!o)throw new Error("Missing test-data.json field: profile.email or auth.email");return o}if(n.includes("cvv")||n.includes("cvc")||n.includes("security code"))return"123";if(n.includes("expiry")||n.includes("exp date")||n.includes("expiration"))return"12/28";if(n.includes("pincode")||n.includes("zip")||n.includes("postal")){if(!this.testData.profile?.pincode)throw new Error("Missing test-data.json field: profile.pincode");return this.testData.profile.pincode}if(e.password||n.includes("password")||/\bpin\b/.test(n)&&!n.includes("pincode")||n.includes("passcode")){if(!this.testData.auth?.password)throw new Error("Missing test-data.json field: auth.password");return this.testData.auth.password}if(n.includes("otp")||n.includes("verification")||n.includes("code")&&!n.includes("postal")&&!n.includes("zip")){if(!this.testData.auth?.otp)throw new Error("Missing test-data.json field: auth.otp");return this.testData.auth.otp}if(n.includes("address")||n.includes("street")||n.includes("location")){if(!this.testData.profile?.address)throw new Error("Missing test-data.json field: profile.address");return this.testData.profile.address}if(n.includes("city")||n.includes("state")){if(!this.testData.profile?.city)throw new Error("Missing test-data.json field: profile.city");return this.testData.profile.city}if(n.includes("name")||n.includes("full name")||n.includes("first name")){if(!this.testData.profile?.name)throw new Error("Missing test-data.json field: profile.name");return this.testData.profile.name}if(n.includes("search")||n.includes("find")||n.includes("query")){if(!this.testData.search?.query)throw new Error("Missing test-data.json field: search.query");return this.testData.search.query}return this.testData.review?.text||"PromptTest Auto QA Test Data"}async fillAndSubmitForm(e,t,s=!0,i=!1){await this.ensureDataLoaded();let r=ze(e),n=[];if(r.length===0)return{filledFields:[],submitted:!1};let o=!1;for(let p of r){let d=p.resourceId||p.text||`input_${p.bounds.centerX}_${p.bounds.centerY}`,h=(p.text||"").trim(),u=!h||/^enter\s+/i.test(h)||/^type\s+/i.test(h)||/^e\.g\./i.test(h)||/^(email|email address|password|phone|name|search)$/i.test(h);if(!i&&!u){n.push({fieldId:d,fieldLabel:p.text||p.contentDesc||p.resourceId,valueEntered:h});continue}let m=this.inferValueForNode(p,e);await this.driver.tap(p.bounds.centerX,p.bounds.centerY,t),await new Promise(y=>setTimeout(y,400)),await this.driver.clearField(t,m.length+10),await new Promise(y=>setTimeout(y,200)),await this.driver.inputText(m,t),await new Promise(y=>setTimeout(y,300)),o=!0,n.push({fieldId:d,fieldLabel:p.text||p.contentDesc||p.resourceId,valueEntered:m})}o&&(await this.driver.pressKey(4,t),await new Promise(p=>setTimeout(p,500)));let c=!1,l;if(s){let p=e.filter(d=>!d.clickable||d.bounds.width===0?!1:yi.some(h=>pe(h,d.text)||pe(h,d.contentDesc)||pe(h,d.resourceId)));if(p.length>0){let d=["submit","sign in","signin","login","save","confirm","proceed","continue"];p.sort((u,m)=>{let y=(u.text||u.contentDesc||u.resourceId||"").toLowerCase(),v=(m.text||m.contentDesc||m.resourceId||"").toLowerCase(),T=d.findIndex(D=>y.includes(D)),S=d.findIndex(D=>v.includes(D)),b=T>=0?T:99,E=S>=0?S:99;return b!==E?b-E:m.bounds.centerY-u.bounds.centerY});let h=p[0];l=h.text||h.contentDesc||h.resourceId,await this.driver.tap(h.bounds.centerX,h.bounds.centerY,t),await new Promise(u=>setTimeout(u,C.settle.submitWaitMs));try{let u=await this.driver.getUiHierarchy(t),{flat:m}=we(u),y=ze(m);c=!(y.length>0&&r.every(T=>y.some(S=>S.resourceId&&S.resourceId===T.resourceId)))}catch{c=!0}}}return{filledFields:n,submitted:c,submitButtonText:l}}},Ji=new pt;import se from"chalk";var is=class{driver;reporter;memory;formFiller;screenLedger=new Map;callStack=[];executedStepCount=0;visitedScreenHashes=new Set;devDefectsList=[];safetyHitList=[];externalSkippedList=[];stepListener;abortFlag;constructor(e=ke,t=nt,s=Ct){this.driver=e,this.reporter=t,this.memory=s,this.formFiller=new pt(void 0,e)}emitStep(e){try{let t=e.screenHash?this.screenLedger.get(e.screenHash):void 0;this.stepListener?.({...e,depth:e.depth??t?.depth,elementCount:e.elementCount??t?.elements.length,discoveredScreens:e.discoveredScreens??this.visitedScreenHashes.size,totalSteps:e.totalSteps??this.executedStepCount})}catch{}}isAborted(){return this.abortFlag?.aborted===!0}async getCleanUiHierarchy(e,t,s=5){let i=await this.driver.getUiHierarchy(t),{flat:r}=we(i),n=0;for(;n<s;){let o=jt(r);if(!o)break;n++;let c=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,l=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(se.red(` \u{1F6A8} Global Dev Console Intercepted: ${c} "${o.errorText.slice(0,80)}"${l}`)),this.devDefectsList.push(o.sourceLocation?`${o.errorText} (${o.sourceLocation})`:o.errorText),this.emitStep({type:"crash",description:`Dev overlay intercepted ${c}: "${o.errorText.slice(0,120)}"${l}`});let p=await this.driver.captureScreenshot(t);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${c}: "${o.errorText}"${l}`,"Verify","FAIL",o.errorText,p,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(se.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,t)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(se.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,t)):(console.log(se.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(t)),await new Promise(d=>setTimeout(d,800)),i=await this.driver.getUiHierarchy(t),r=we(i).flat}return{xml:i,flat:r}}async dismissSoftKeyboard(e){try{await this.driver.pressKey(111,e),await new Promise(t=>setTimeout(t,150))}catch{}}async packageJailGuard(e,t){let s=await this.driver.getForegroundPackage(t);if(!s)return!1;if(!s.includes(e)){let i=vt(s);console.log(se.yellow(` \u{1F6A8} External App Jailbreak Detected [${s}]. Recovering...`)),i||(await this.driver.pressBack(t),await new Promise(n=>setTimeout(n,800)));let r=await this.driver.getForegroundPackage(t);return(!r||!r.includes(e))&&(console.log(se.red(` Force restoring ${e} foreground...`)),await this.driver.launchApp(e,t),await new Promise(n=>setTimeout(n,1500)),this.callStack=this.callStack.slice(0,1)),!0}return!1}async handleExternalMediaChooser(e,t){let s=await this.driver.getForegroundPackage(t);if(!s)return!1;let i=s==="android"||s.includes("resolver")||s.includes("intentresolver");if(i){console.log(se.yellow(` \u{1F6E1}\uFE0F External Media Chooser Intercepted [${s}]. Dismissing to stay in-app...`)),await this.driver.pressBack(t),await new Promise(n=>setTimeout(n,800));let r=await this.driver.getForegroundPackage(t);return r&&!r.includes(e)&&i&&(await this.driver.launchApp(e,t),await new Promise(n=>setTimeout(n,1200)),this.callStack=this.callStack.slice(0,1)),!0}return!1}catalogScreen(e,t,s=[]){let i=[],r=t.height,n=r*.06,o=r*.88,c=b=>s.some(E=>Math.abs(E.bounds.centerX-b.bounds.centerX)<30&&Math.abs(E.bounds.centerY-b.bounds.centerY)<30),l=ze(e);for(let b of l){if(c(b))continue;let E=b.text||b.contentDesc||b.resourceId||"Text Input";i.push({node:b,type:"input",label:E,tested:!1})}let p=Dt(e);for(let b of p){if(c(b))continue;let E=b.text||b.contentDesc||b.resourceId||"Toggle Control";i.push({node:b,type:"toggle",label:E,tested:!1})}let d=Qe(e);for(let b of d){if(c(b)||st([b],r,t.width)||Re([b],r,t.width,!0))continue;let E=b.text||b.contentDesc||b.resourceId||"Action Button";if(Ue(b)){this.externalSkippedList.push(E);continue}let D=b.selected!==void 0||b.checkable||/(?:tab|chip|pill|filter|segment|toggle)/i.test(`${b.resourceId} ${b.className} ${b.contentDesc}`)||/(?:today|yesterday|all|active|archived|pending|completed|present|absent)/i.test(E);i.push({node:b,type:D?"toggle":"transition",label:E,tested:!1})}let h=xi(e,r,t.width);for(let b of h){if(c(b)||Re([b],r,t.width,!0)||i.some(D=>Math.abs(D.node.bounds.centerX-b.bounds.centerX)<25&&Math.abs(D.node.bounds.centerY-b.bounds.centerY)<25))continue;let E=b.text||b.contentDesc||b.resourceId||"Notification / Header Action";if(Ue(b)){this.externalSkippedList.push(E);continue}i.push({node:b,type:"transition",label:E,tested:!1})}let u=e.filter(b=>{if(!b.clickable||b.bounds.width===0)return!1;let E=`${b.text} ${b.contentDesc} ${b.resourceId}`.toLowerCase();return E.includes("now playing")||E.includes("play")||E.includes("pause")});for(let b of u){if(c(b)||i.some(D=>Math.abs(D.node.bounds.centerX-b.bounds.centerX)<25&&Math.abs(D.node.bounds.centerY-b.bounds.centerY)<25))continue;let E=b.text||b.contentDesc||b.resourceId||"Media Control";i.push({node:b,type:"toggle",label:E,tested:!1})}let m=Bt(e,t).filter(b=>!c(b)&&b.bounds.centerY>n&&b.bounds.centerY<o&&!Re([b],r,t.width,!0)&&!Ue(b)),y=Pi(m,2);for(let b of y){let E=b.text||b.contentDesc||b.resourceId||`Item (${b.bounds.centerX},${b.bounds.centerY})`;i.push({node:b,type:"transition",label:E,tested:!1})}let v=0,T=0,S=[];for(let b of i){let E=/\.(?:mp4|mkv|mov|webm|flv|avi)\b/i.test(b.label),D=/\.(?:mp3|wav|m4a|aac|ogg)\b/i.test(b.label);if(E){if(v++,v>1)continue}else if(D&&(T++,T>1))continue;S.push(b)}return S}async safeBacktrack(e,t,s,i,r=!0){let{flat:n}=await this.getCleanUiHierarchy(t,s),{height:o,width:c}=await this.driver.getScreenDimensions(s),l=Oe(n,{width:c,height:o});if(l===e)return!0;let p=Re(n,o,c,!0);if(p&&p.bounds.width>0){console.log(se.gray(` [Backtrack] Tapping in-app back/close button "${p.text||p.contentDesc||p.className}"...`)),await this.driver.tap(p.bounds.centerX,p.bounds.centerY,s),await new Promise(u=>setTimeout(u,1e3)),await this.packageJailGuard(t,s);let d=await this.getCleanUiHierarchy(t,s),h=Oe(d.flat,{width:c,height:o});if(h===e||h!==l)return!0}if(i&&i.bounds.width>0){console.log(se.gray(` [Backtrack] Restoring root domain via tab "${i.text||i.contentDesc||"Root Tab"}"...`)),await this.driver.tap(i.bounds.centerX,i.bounds.centerY,s),await new Promise(u=>setTimeout(u,1e3)),await this.packageJailGuard(t,s);let d=await this.getCleanUiHierarchy(t,s),h=Oe(d.flat,{width:c,height:o});if(h===e||h!==l)return!0}if(r){if(console.log(se.gray(" [Backtrack] Sending fallback KEYCODE_BACK...")),await this.driver.pressKey(4,s),await new Promise(m=>setTimeout(m,1e3)),await this.packageJailGuard(t,s))return console.log(se.yellow(" \u26A0\uFE0F Backtrack via KEYCODE_BACK caused app jailbreak. Aborting branch restoration.")),!1;let h=await this.getCleanUiHierarchy(t,s),u=Oe(h.flat,{width:c,height:o});if(u===e||u!==l)return!0}return!1}async explore(e){let{packageName:t,serial:s,maxScreens:i=30,maxDepth:r=3,stepBudget:n=100,maxInteractionsPerScreen:o=6,safetyMode:c="strict",safetyBlacklist:l=[],onStep:p,abortSignal:d}=e;this.stepListener=p,this.abortFlag=d,console.log(se.bold.cyan(`
847
- \u{1F916} PromptTest Universal State-Graph DFS Engine`)),console.log(se.gray(`Target Package: ${t}`)),console.log(se.gray(`Safety Policy: ${c.toUpperCase()}`)),console.log(se.gray(`Limits: Max Screens = ${i}, Step Budget = ${n}, Max Depth = ${r}`)),await this.driver.clearCrashLogs(s);let h=await this.driver.getScreenDimensions(s),u=await this.driver.getForegroundPackage(s);(!u||!u.includes(t))&&(console.log(se.gray(`Launching ${t}...`)),await this.driver.launchApp(t,s),await new Promise(b=>setTimeout(b,2500)));let{flat:m}=await this.getCleanUiHierarchy(t,s);await this.dismissSoftKeyboard(s);let y=us(m,h,t);console.log(se.magenta(`\u26A1 Universal Paradigm Detected: [${y.paradigm.toUpperCase()}] with ${y.roots.length} root node(s).`));let v=[],T;if(y.paradigm==="drawer"&&y.roots.length>0){T=y.roots[0];let b={...m[0]||{},text:"Home Screen / Main Hub",contentDesc:"Home Screen / Main Hub",clickable:!0,bounds:{x1:0,y1:0,x2:h.width,y2:h.height,centerX:h.width/2,centerY:h.height/2,width:h.width,height:h.height}};v.push(b),console.log(se.gray(" Opening drawer to catalog root destinations...")),await this.driver.tap(T.bounds.centerX,T.bounds.centerY,s),await new Promise(M=>setTimeout(M,1200));let E=await this.getCleanUiHierarchy(t,s),D=E.flat.filter(M=>{if(!M.clickable||M.bounds.width===0||M.bounds.height===0||M.bounds.width>=h.width*.95&&M.bounds.height>=h.height*.95||Re([M],h.height,h.width,!0))return!1;let J=`${M.contentDesc} ${M.text}`.trim(),Y=M.bounds.width>h.width*.15&&M.bounds.height>h.height*.05;return J.toLowerCase().includes("developed by")||M.bounds.centerY>h.height*.85?!1:J.length>=2||Y});for(let M of D)v.some(J=>Math.abs(J.bounds.centerX-M.bounds.centerX)<30&&Math.abs(J.bounds.centerY-M.bounds.centerY)<30)||v.push(M);let B=Re(E.flat,h.height,h.width,!0);B?await this.driver.tap(B.bounds.centerX,B.bounds.centerY,s):await this.driver.tap(T.bounds.centerX,T.bounds.centerY,s),await new Promise(M=>setTimeout(M,1200));let k=await this.getCleanUiHierarchy(t,s);if(k.flat.some(M=>`${M.contentDesc} ${M.text}`.includes("Gallery")&&`${M.contentDesc} ${M.text}`.includes("Planner"))){let M=Re(k.flat,h.height,h.width,!0);M?await this.driver.tap(M.bounds.centerX,M.bounds.centerY,s):await this.driver.tap(h.width-50,h.height/2,s),await new Promise(J=>setTimeout(J,1e3))}console.log(se.magenta(` Discovered ${v.length} destinations (1 Main Hub + ${v.length-1} Drawer destinations).`))}else y.roots.length>0?v=y.roots:v=[m[0]||{text:"Main Hub",clickable:!0}];let S=1;for(let b of v){if(this.executedStepCount>=n||this.visitedScreenHashes.size>=i||this.isAborted())break;let E=b.text||b.contentDesc||`Root #${S}`;if(console.log(se.yellow(`
848
- [Domain ${S}/${v.length}] Entering Root Tree: "${E}"...`)),this.emitStep({type:"action",description:`Tap "${E}" to enter the ${E} section`,screenTitle:E,depth:0}),y.paradigm==="bottom_tabs"||y.paradigm==="top_tabs"){let k=b.bounds.height<25?Math.max(b.bounds.y1-10,b.bounds.centerY):Math.min(b.bounds.centerY,b.bounds.y2-4);await this.driver.tap(b.bounds.centerX,k,s),this.executedStepCount++,await new Promise(j=>setTimeout(j,1200))}else y.paradigm==="drawer"&&S>1&&T&&(await this.dismissSoftKeyboard(s),console.log(se.gray(` Opening drawer to select "${E}"...`)),await this.driver.tap(T.bounds.centerX,T.bounds.centerY,s),await new Promise(k=>setTimeout(k,1e3)),await this.driver.tap(b.bounds.centerX,b.bounds.centerY,s),this.executedStepCount++,await new Promise(k=>setTimeout(k,1500)));m=(await this.getCleanUiHierarchy(t,s)).flat,await this.dismissSoftKeyboard(s);let B=S===1?Math.max(25,Math.ceil(n*.45)):Math.max(8,Math.ceil(n*.55/Math.max(1,v.length-1)));if(await this.exploreDfsNode({packageName:t,serial:s,screenDims:h,rootNodes:y.roots,activeRootNode:b,currentDepth:0,maxDepth:r,maxScreens:i,stepBudget:n,domainStepLimit:this.executedStepCount+B,maxInteractionsPerScreen:o,safetyMode:c,safetyBlacklist:l,domainLabel:E}),y.paradigm==="drawer"&&S>1){let k=0;for(;k<4;){let j=await this.getCleanUiHierarchy(t,s),M=st(j.flat,h.height,h.width),J=j.flat.some(K=>`${K.contentDesc} ${K.text}`.includes("Gallery")&&`${K.contentDesc} ${K.text}`.includes("Planner"));if(M&&!J)break;let Y=Re(j.flat,h.height,h.width,!0);if(Y)console.log(se.gray(" [Domain Wrap-Up] Returning to Home Screen via in-app back...")),await this.driver.tap(Y.bounds.centerX,Y.bounds.centerY,s),await new Promise(K=>setTimeout(K,1e3)),k++;else break}}if(S===1&&v.length===1){let k=await this.getCleanUiHierarchy(t,s),j=us(k.flat,h,t);if((j.paradigm==="bottom_tabs"||j.paradigm==="top_tabs")&&j.roots.length>1){console.log(se.magenta(`
849
- \u26A1 Post-Auth Navigation Discovered: [${j.paradigm.toUpperCase()}] with ${j.roots.length} root domain(s)!`)),y.paradigm=j.paradigm,y.roots=j.roots;let M=j.roots.slice(1);v.push(...M.length>0?M:j.roots)}}S++}for(let b of this.visitedScreenHashes)this.memory.markScreenCrawled(b);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(se.green(`
850
- \u2714 Universal DFS Traversal Complete!`)),console.log(se.cyan(` Visited Screens: ${this.visitedScreenHashes.size}`)),console.log(se.cyan(` Total Steps Executed: ${this.executedStepCount}/${n}`)),console.log(se.cyan(` Defects Triaged: ${this.devDefectsList.length}`))}async exploreDfsNode(e){let{packageName:t,serial:s,screenDims:i,rootNodes:r,activeRootNode:n,currentDepth:o,maxDepth:c,maxScreens:l,stepBudget:p,domainStepLimit:d,maxInteractionsPerScreen:h,safetyMode:u,safetyBlacklist:m,domainLabel:y}=e;if(this.executedStepCount>=p||this.visitedScreenHashes.size>=l||d&&this.executedStepCount>=d||this.isAborted())return;let{flat:v}=await this.getCleanUiHierarchy(t,s),T=be(v),S=Oe(v,i);this.visitedScreenHashes.add(T);let b=this.screenLedger.get(S);if(b){if(b.status==="FULLY_EXPLORED"){console.log(se.gray(` \u26A1 Cycle Detected: Screen [${S.slice(0,8)}] already fully explored. Pruning branch.`));return}}else{let k=this.catalogScreen(v,i,r);b={hash:T,semanticHash:S,title:`${y} (depth ${o})`,elements:k,status:"IN_PROGRESS",depth:o,interactionCount:0},this.screenLedger.set(S,b),console.log(se.cyan(` \u{1F50E} Discovered Screen [${S.slice(0,8)}] at depth ${o} with ${k.length} candidate element(s).`)),this.emitStep({type:"state_change",description:`Discovered screen "${y}" at depth ${o} with ${k.length} candidate element(s)`,screenHash:S,screenTitle:y,depth:o})}this.callStack.push({screenHash:T,semanticHash:S,title:b.title,timestamp:Date.now()});let E=b.elements.filter(k=>k.type==="input"&&!k.tested);if(E.length>0&&b.interactionCount<h){console.log(se.magenta(` Filling in-place form fields (${E.length} inputs)...`));let k=await this.formFiller.fillAndSubmitForm(v,s,!0,!1);this.executedStepCount++,b.interactionCount++;for(let j of E)j.tested=!0;await this.dismissSoftKeyboard(s),await this.packageJailGuard(t,s),this.reporter.logStep("In-Place Form Fill",`Populated ${k.filledFields.length} field(s) on [${S.slice(0,8)}]`,"FormSubmit","PASS",k.submitButtonText||"Auto");for(let j of k.filledFields)this.emitStep({type:"action",description:`Type "${j.valueEntered}" in the "${j.fieldLabel}" field`,screenHash:S,screenTitle:y,depth:o});k.submitButtonText&&this.emitStep({type:"action",description:`Tap "${k.submitButtonText}" to submit the form`,screenHash:S,screenTitle:y,depth:o})}let D=b.elements.filter(k=>k.type==="toggle"&&!k.tested);for(let k of D){if(this.executedStepCount>=p||b.interactionCount>=h)break;if(k.tested=!0,Be(k.node,m,u)){this.safetyHitList.push(k.label),this.emitStep({type:"safety_block",description:`Safety policy [${u.toUpperCase()}] blocked toggle "${k.label}"`,screenHash:S,screenTitle:y,depth:o});continue}console.log(se.gray(` Exercising toggle: "${k.label.slice(0,30)}"...`)),this.executedStepCount++,b.interactionCount++,await this.driver.tap(k.node.bounds.centerX,k.node.bounds.centerY,s),await new Promise(j=>setTimeout(j,600)),this.reporter.logStep("In-Place Toggle",`Toggled control "${k.label}"`,"Tap","PASS",k.label),this.emitStep({type:"action",description:`Toggle "${k.label}"`,screenHash:S,screenTitle:y,depth:o})}let B=b.elements.filter(k=>k.type==="transition"&&!k.tested);for(let k of B){if(this.executedStepCount>=p||this.visitedScreenHashes.size>=l||d&&this.executedStepCount>=d&&!(o===0&&y.includes("Home Screen"))||b.interactionCount>=h||this.isAborted())break;if(k.tested=!0,Ue(k.node)){this.externalSkippedList.push(k.label);continue}if(Be(k.node,m,u)){this.safetyHitList.push(k.label),this.emitStep({type:"safety_block",description:`Safety policy [${u.toUpperCase()}] blocked transition "${k.label}"`,screenHash:S,screenTitle:y,depth:o});continue}console.log(se.magenta(` \u26A1 [DFS Depth ${o}] Testing transition: "${k.label.slice(0,40)}"...`)),this.executedStepCount++,b.interactionCount++,await this.driver.tap(k.node.bounds.centerX,k.node.bounds.centerY,s),await new Promise(Y=>setTimeout(Y,1500)),await this.dismissSoftKeyboard(s),await this.handleExternalMediaChooser(t,s),await this.packageJailGuard(t,s);let j=await this.getCleanUiHierarchy(t,s),M=Oe(j.flat,i),J=M!==S;if(this.reporter.logStep("DFS Transition",`Tapped "${k.label}" [${J?"Navigated":"In-Place"}]`,"Tap","PASS",k.label),this.emitStep({type:"action",description:`Tap "${k.label}"${J?" to navigate to the next screen":""}`,screenHash:S,screenTitle:y,depth:o}),J&&M&&this.emitStep({type:"state_change",description:`Navigated to screen [${M.slice(0,8)}] via "${k.label}"`,screenHash:M,screenTitle:`${y} > ${k.label.slice(0,20)}`,depth:o+1}),J){if(o<c){let le=o===0&&y.includes("Home Screen")?Math.min(this.executedStepCount+3,d||this.executedStepCount+3):d;await this.exploreDfsNode({packageName:t,serial:s,screenDims:i,rootNodes:r,activeRootNode:n,domainStepLimit:le,currentDepth:o+1,maxDepth:c,maxScreens:l,stepBudget:p,maxInteractionsPerScreen:h,safetyMode:u,safetyBlacklist:m,domainLabel:`${y} > ${k.label.slice(0,20)}`})}console.log(se.gray(` [DFS Backtrack] Returning to [${S.slice(0,8)}]...`)),await this.safeBacktrack(S,t,s,o===0?n:void 0,o>0),await this.dismissSoftKeyboard(s);let Y=await this.getCleanUiHierarchy(t,s);if(Oe(Y.flat,i)!==S){let le=Re(Y.flat,i.height,i.width,!0);le&&(console.log(se.gray(" [Backtrack Verification] Re-tapping in-app back/close button...")),await this.driver.tap(le.bounds.centerX,le.bounds.centerY,s),await new Promise(De=>setTimeout(De,1e3)),await this.packageJailGuard(t,s));let Z=await this.getCleanUiHierarchy(t,s);if(Oe(Z.flat,i)!==S){console.log(se.yellow(` \u26A0\uFE0F Unable to restore parent screen [${S.slice(0,8)}]. Unwinding branch to prevent jailbreak loop.`));break}}}}b.status="FULLY_EXPLORED",this.callStack.pop()}};var ut=class{driver;formFiller;reporter;memory;safetyHitList=[];externalSkippedList=[];devDefectsList=[];visitedScreenHashes=new Set;executedStepCount=0;constructor(e,t,s,i){this.driver=e||ke,this.formFiller=t||Ji,this.reporter=s||nt,this.memory=i||Ct}getMemory(){return this.memory}getSafetyHitList(){return this.safetyHitList}getExternalSkippedList(){return this.externalSkippedList}getDevDefectsList(){return this.devDefectsList}getVisitedScreensCount(){return this.visitedScreenHashes.size}async packageJailGuard(e,t){let s=await this.driver.getForegroundPackage(t);if(s&&!s.includes(e)){let i=vt(s);console.log(L.yellow(` \u{1F6E1}\uFE0F Jail Containment: App escaped to "${s}". Executing containment recovery...`)),i||(await this.driver.pressBack(t),await new Promise(n=>setTimeout(n,600)));let r=await this.driver.getForegroundPackage(t);return(!r||!r.includes(e))&&(console.log(L.gray(` Force re-launching ${e} to restore foreground...`)),await this.driver.launchApp(e,t),await new Promise(n=>setTimeout(n,1200))),this.reporter.logStep("Jail Containment",`App escaped to "${s}"; successfully contained and restored "${e}"`,"Back","PASS"),!0}return!1}async getCleanUiHierarchy(e,t,s=5){let i=await this.driver.getUiHierarchy(t),{flat:r}=we(i),n=0;for(;n<s;){let o=jt(r);if(!o)break;n++;let c=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,l=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(L.red(` \u{1F6A8} Global Dev Console Intercepted: ${c} "${o.errorText.slice(0,80)}"${l}`)),this.devDefectsList.push(o.sourceLocation?`${o.errorText} (${o.sourceLocation})`:o.errorText);let p=await this.driver.captureScreenshot(t);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${c}: "${o.errorText}"${l}`,"Verify","FAIL",o.errorText,p,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(L.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,t)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(L.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,t)):(console.log(L.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(t)),await new Promise(d=>setTimeout(d,800)),i=await this.driver.getUiHierarchy(t),r=we(i).flat}return{xml:i,flat:r}}async handleDevOverlays(e,t,s){let i=jt(e);if(!i)return!1;let r=`[${(i.severity||"error").toUpperCase()} - ${i.type.toUpperCase()}]`,n=i.sourceLocation?` [${i.sourceLocation}]`:"";console.log(L.red(` \u{1F6A8} Dev Overlay Detected: ${r} "${i.errorText.slice(0,80)}"${n}`)),this.devDefectsList.push(i.sourceLocation?`${i.errorText} (${i.sourceLocation})`:i.errorText);let o=await this.driver.captureScreenshot(s);return this.reporter.logStep("Dev Overlay Triage",`Detected and triaged ${r} error: "${i.errorText}"${n}`,"Verify","FAIL",i.errorText,o,{errors:[i.sourceLocation?`${i.errorText} (${i.sourceLocation})`:i.errorText]}),i.dismissButton&&i.dismissButton.bounds.width>0?(console.log(L.gray(" Dismissing error overlay via dismiss button...")),await this.driver.tap(i.dismissButton.bounds.centerX,i.dismissButton.bounds.centerY,s)):i.isToast&&i.node&&i.node.bounds.width>0?(console.log(L.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(i.node.bounds.centerX,i.node.bounds.centerY,s)):(console.log(L.gray(" Pressing Back to dismiss error overlay...")),await this.driver.pressBack(s)),await new Promise(c=>setTimeout(c,800)),!0}async dismissSoftKeyboard(e){try{await this.driver.pressKey(111,e),await new Promise(t=>setTimeout(t,200))}catch{}}async explore(e){let{packageName:t,serial:s,maxTabs:i=C.explorer?.maxTabs||15,exploreLists:r=!0,skipAlreadyCrawled:n=!1,safetyMode:o=C.safety?.mode||"strict",safetyBlacklist:c=C.safety?.customBlacklist?[...C.safety.customBlacklist]:[],maxScreens:l=C.explorer?.maxScreens||30,stepBudget:p=C.explorer?.stepBudget||100,screenshots:d=C.explorer?.screenshots||"state-change",video:h=e.video??!1,onStep:u,abortSignal:m}=e;console.log(L.cyan(`
851
- \u{1F916} PromptTest Autonomous Exploration Brain v3`)),console.log(L.gray("Target Package: ")+L.bold.white(t)),console.log(L.gray("Safety Policy: ")+L.bold.yellow(o.toUpperCase())+(c.length>0?L.gray(` (+${c.length} custom blacklist)`):"")),console.log(L.gray("Screenshots Policy: ")+L.bold.white(d));let y=[],v=0,T=k=>{v++,u?.({...k,stepNumber:v,packageName:t,totalSteps:k.totalSteps??this.executedStepCount,discoveredScreens:k.discoveredScreens??this.visitedScreenHashes.size})},S=async(k,j=!0)=>{if(!(d==="failure-only"&&k!=="FAIL")&&!(d==="state-change"&&!j&&k!=="FAIL"))try{return await this.driver.captureScreenshot(s)}catch{return}};await this.memory.init(t);let b=s||this.driver.getDefaultDevice(),E=b||"Default Device";this.reporter.init(t,E);let D=null;b&&(D=yt(b));let B=null;this.safetyHitList=[],this.externalSkippedList=[],this.devDefectsList=[],this.visitedScreenHashes.clear(),this.executedStepCount=0;try{if(await this.driver.clearCrashLogs(s),h&&(console.log(L.cyan("\u{1F3AC} Starting ADB screen recording for exploration (--video enabled)...")),B=await this.driver.startScreenRecording("/sdcard/prompttest_explore_temp.mp4",s)),(e.strategy||C.explorer?.strategy||"heuristic")==="dfs"){let P=new is(this.driver,this.reporter,this.memory);await P.explore({packageName:t,serial:s,maxScreens:l,maxDepth:e.maxDepth||C.explorer?.maxDepth||5,stepBudget:p,safetyMode:o,safetyBlacklist:c,screenshotPolicy:d,skipAlreadyCrawled:n,onStep:x=>T({type:x.type,description:x.description,screenHash:x.screenHash,screenTitle:x.screenTitle,depth:x.depth,elementCount:x.elementCount,totalSteps:x.totalSteps,discoveredScreens:x.discoveredScreens}),abortSignal:m}),this.safetyHitList=P.safetyHitList,this.externalSkippedList=P.externalSkippedList,this.devDefectsList=P.devDefectsList,this.visitedScreenHashes=P.visitedScreenHashes,this.executedStepCount=P.executedStepCount;let O=this.memory.getStats();this.reporter.logStep("Coverage Audit",`State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s). Memory Engine cached ${O.totalScreens} crawled screen(s).`,"Verify","PASS"),await this.reporter.generateReport(),console.log(L.bold.green(`
852
- \u2728 Full Audit Report generated at: output/prompttest-report.md`));return}let{width:j,height:M}=await this.driver.getScreenDimensions(s);console.log(L.yellow(`
853
- [1/5] Verifying Foreground State...`));let J=await this.driver.getForegroundPackage(s);(!J||!J.includes(t))&&(console.log(L.gray(`Launching ${t}...`)),await this.driver.launchApp(t,s),await new Promise(P=>setTimeout(P,2500)));let Y=await this.runErrorSentinel(t,s,"Post-Launch"),K=Y.hasError?"FAIL":"PASS",le=await S(K,!0);this.reporter.logStep("Init",`Launched and verified foreground for ${t}`,"Launch",K,t,le,Y.hasError?{errors:Y.errors}:void 0),console.log(K==="PASS"?L.green("\u2714 App is active in foreground."):L.red(`\u2716 Issues detected during launch for ${t}.`)),console.log(L.yellow(`
854
- [2/5] Inspecting UI Hierarchy for Forms...`));let{xml:Z,flat:_}=await this.getCleanUiHierarchy(t,s),De=be(_);if(this.visitedScreenHashes.add(De),this.memory.isScreenCrawled(De)&&n)console.log(L.cyan(` \u26A1 Fast-Forward: Screen [${De.slice(0,8)}] was previously crawled offline. Skipping redundant element interactions.`)),this.reporter.logStep("Fast Forward",`Fast-forwarded past previously crawled screen [${De.slice(0,8)}] (Memory Engine)`,"Skip","PASS");else{let P=ze(_);if(P.length>0){console.log(L.magenta(`\u26A1 Detected ${P.length} form field(s)! Engaging Semantic Form Filler...`));for(let Q of P)console.log(L.gray(` - Field: "${Q.text||Q.contentDesc||Q.resourceId}"`));let A=await this.formFiller.fillAndSubmitForm(_,s,!0,!1);this.executedStepCount++,await new Promise(Q=>setTimeout(Q,1500)),await this.dismissSoftKeyboard(s),await this.packageJailGuard(t,s);let H=await this.getCleanUiHierarchy(t,s);Z=H.xml,_=H.flat,this.visitedScreenHashes.add(be(_));let R=await this.runErrorSentinel(t,s,"Post-Form-Submit"),W=ze(_),te=R.hasError||W.length===P.length&&R.errors.length>0?"FAIL":"PASS",q=await S(te,!0),ne=A.filledFields.map(Q=>`${Q.fieldLabel} -> "${Q.valueEntered}"`).join(", ");this.reporter.logStep("Form Fill",`Filled ${A.filledFields.length} field(s) [${ne}] and submitted via "${A.submitButtonText||"Auto"}"`,"FormSubmit",te,A.submitButtonText,q,R.hasError?{errors:R.errors}:void 0),console.log(te==="PASS"?L.green("\u2714 Form populated and submitted successfully!"):L.red("\u2716 Form submission triggered an error or remained unauthenticated."))}else console.log(L.gray("No form input fields detected on initial screen."));console.log(L.yellow(`
855
- [3/5] Inspecting Interactive Action Controls...`));let O=Qe(_).filter(A=>{let H=(A.text+" "+A.contentDesc).toLowerCase();if(H.includes("submit")||H.includes("cancel")||st([A],M))return!1;if(Ue(A)){let R=A.text||A.contentDesc||A.resourceId||"External Link";return console.log(L.gray(` \u{1F517} External Link Bypassed: "${R}"`)),this.externalSkippedList.push(R),!1}if(Be(A,c,o)){let R=A.text||A.contentDesc||A.resourceId||"Action";return console.log(L.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive action "${R}"`)),this.safetyHitList.push(R),!1}return!0});if(O.length>0){let A=O[0],H=A.text||A.contentDesc||A.resourceId;console.log(L.magenta(`\u26A1 Found action button: "${H}". Testing interaction...`));let R=be(_);this.executedStepCount++,await this.driver.tap(A.bounds.centerX,A.bounds.centerY,s),await new Promise(Se=>setTimeout(Se,1200)),await this.packageJailGuard(t,s);let W=await this.getCleanUiHierarchy(t,s);Z=W.xml,_=W.flat;let X=be(_);this.visitedScreenHashes.add(X);let te=await this.runErrorSentinel(t,s,"Post-Action-Tap"),q=R!==X,ne="PASS",Q="";te.hasError?(ne="FAIL",Q="Action triggered error or crash"):q?(ne="PASS",Q="Screen transitioned successfully"):(ne="WARN",Q="No visible screen transition or state change observed (No-Op)",console.log(L.yellow(` \u26A0\uFE0F Outcome Warning: Action "${H}" produced no UI state change.`)));let Ae=await S(ne,q);this.reporter.logStep("Action Testing",`Tapped action button "${H}" [${Q}]`,"Tap",ne,H,Ae,te.hasError?{errors:te.errors}:void 0),console.log(ne==="PASS"?L.green(`\u2714 Action interaction complete (${Q}).`):ne==="WARN"?L.yellow(`\u2714 Action interaction recorded with warning (${Q}).`):L.red("\u2716 Action interaction triggered an error.")),ne!=="FAIL"&&(this.memory.recordSuccess("TAP",H,A.resourceId||A.text||A.contentDesc||H,"text",1,void 0,R),this.memory.markScreenCrawled(R,[H]))}else console.log(L.gray("No standalone action buttons to test."));let x=Dt(_).filter(A=>!Be(A,c,o));if(x.length>0){console.log(L.yellow(`
856
- [3b/5] Inspecting Toggle & Checkable Controls...`));let A=x[0],H=A.text||A.contentDesc||A.resourceId||"Switch/Checkbox",R=A.checked;console.log(L.magenta(`\u26A1 Found toggle: "${H}" (checked: ${R}). Testing toggle interaction...`)),this.executedStepCount++,await this.driver.tap(A.bounds.centerX,A.bounds.centerY,s),await new Promise(Se=>setTimeout(Se,1e3)),Z=await this.driver.getUiHierarchy(s),_=we(Z).flat;let W=be(_);this.visitedScreenHashes.add(W);let te=Dt(_).find(Se=>Math.abs(Se.bounds.centerX-A.bounds.centerX)<25&&Math.abs(Se.bounds.centerY-A.bounds.centerY)<25),q=te?te.checked!==R:!0,ne=await this.runErrorSentinel(t,s,"Post-Toggle"),Q=ne.hasError?"FAIL":q?"PASS":"WARN",Ae=await S(Q,q);this.reporter.logStep("Toggle Testing",`Toggled "${H}" (Checked: ${R} -> ${te?te.checked:"flipped"})`,"Toggle",Q,H,Ae,ne.hasError?{errors:ne.errors}:void 0),console.log(Q==="PASS"?L.green("\u2714 Toggle control flipped successfully!"):Q==="WARN"?L.yellow("\u26A0\uFE0F Toggle tapped but checked state did not flip."):L.red("\u2716 Toggle control triggered an error.")),Q!=="FAIL"&&this.memory.markScreenCrawled(W,[H])}let $=st(_,M);if($&&!Be($,c,o)){let A=$.contentDesc||$.text||$.resourceId||"Drawer Menu";console.log(L.yellow(`
857
- [3c/5] Inspecting Side Navigation Drawer...`)),console.log(L.magenta(`\u26A1 Found drawer trigger: "${A}". Opening side drawer...`));let H=be(_);this.executedStepCount++,await this.driver.tap($.bounds.centerX,$.bounds.centerY,s),await new Promise(Q=>setTimeout(Q,1200)),await this.packageJailGuard(t,s);let R=await this.getCleanUiHierarchy(t,s);Z=R.xml,_=R.flat;let W=be(_);this.visitedScreenHashes.add(W);let X=H!==W,te=await this.runErrorSentinel(t,s,"Drawer-Open"),q=te.hasError?"FAIL":X?"PASS":"WARN",ne=await S(q,X);if(this.reporter.logStep("Drawer Navigation",`Opened side drawer via "${A}"`,"Tap",q,A,ne,te.hasError?{errors:te.errors}:void 0),q==="PASS"){console.log(L.green("\u2714 Side drawer opened successfully!"));let Q=Qe(_).filter(Se=>!Be(Se,c,o)&&!Ue(Se)&&Se.bounds.centerY>$.bounds.y2);if(Q.length>0){let Se=Q[0],He=Se.text||Se.contentDesc||Se.resourceId||"Drawer Item";console.log(L.gray(` - Testing drawer item: "${He}"...`)),this.executedStepCount++,await this.driver.tap(Se.bounds.centerX,Se.bounds.centerY,s),await new Promise(fe=>setTimeout(fe,1200)),await this.packageJailGuard(t,s);let gt=await this.getCleanUiHierarchy(t,s);Z=gt.xml,_=gt.flat,this.visitedScreenHashes.add(be(_))}console.log(L.gray(" Closing side drawer to restore main view...")),await this.driver.pressBack(s),await new Promise(Se=>setTimeout(Se,1e3));let Ae=await this.getCleanUiHierarchy(t,s);Z=Ae.xml,_=Ae.flat,this.visitedScreenHashes.add(be(_)),console.log(L.green("\u2714 Main view restored from side drawer."))}else q==="WARN"&&console.log(L.yellow("\u26A0\uFE0F Drawer button tapped but no visible screen change occurred."));q!=="FAIL"&&this.memory.markScreenCrawled(H,[A])}if(r){console.log(L.yellow(`
858
- [4/5] Inspecting Lists & Master-Detail Flows...`));let A=Ti(_);if(A.length>0){console.log(L.magenta(`\u26A1 Found ${A.length} scrollable container(s). Performing vertical scroll exploration...`));let R=be(_),W=Math.round(j*.5);await this.driver.swipe(W,Math.round(M*.7),W,Math.round(M*.3),300,s),await new Promise(Q=>setTimeout(Q,1200));let X=await this.getCleanUiHierarchy(t,s);Z=X.xml,_=X.flat;let te=be(_);this.visitedScreenHashes.add(te);let q=R!==te,ne=await S("PASS",q);this.reporter.logStep("Scroll Exploration",q?"Scrolled vertically to discover off-screen elements":"Scrolled but content remained at edge","Swipe","PASS","SwipeUp",ne),q&&console.log(L.green("\u2714 Vertical scroll succeeded; discovered off-screen content below fold."))}let H=Bt(_).filter(R=>{if(Ue(R)){let W=R.text||R.contentDesc||"External Link";return this.externalSkippedList.push(W),!1}if(Be(R,c,o)){let W=R.text||R.contentDesc||"List Item";return console.log(L.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive list item "${W}"`)),this.safetyHitList.push(W),!1}return!0});if(H.length>0){let R=H[0],W=R.text||R.contentDesc||`Item at (${R.bounds.centerX}, ${R.bounds.centerY})`;console.log(L.magenta(`\u26A1 Found list item: "${W}". Diving into detail view...`));let X=be(_);await this.driver.tap(R.bounds.centerX,R.bounds.centerY,s),await new Promise(We=>setTimeout(We,1800));let te=await this.getCleanUiHierarchy(t,s);Z=te.xml,_=te.flat;let q=be(_);this.visitedScreenHashes.add(q);let ne=await this.runErrorSentinel(t,s,"Detail-View"),Q=X!==q,Ae=ne.hasError?"FAIL":Q?"PASS":"WARN",Se=await S(Ae,Q);this.reporter.logStep("List Dive",`Dived into detail view for "${W}" [${Q?"Navigated":"Unchanged"}]`,"Tap",Ae,W,Se,ne.hasError?{errors:ne.errors}:void 0),console.log(Ae==="PASS"?L.green("\u2714 Detail screen loaded."):Ae==="WARN"?L.yellow("\u26A0\uFE0F Detail view tapped but no navigation detected."):L.red("\u2716 Detail view triggered an error."));let He=Re(te.flat,C.screen.fallbackHeight,C.screen.fallbackWidth,!0);He&&He.bounds.width>0?(console.log(L.gray(`Navigating back via in-app back button "${He.text||He.contentDesc||He.className}"...`)),await this.driver.tap(He.bounds.centerX,He.bounds.centerY,s)):(console.log(L.gray("Navigating back to main view...")),await this.driver.pressKey(4,s)),await new Promise(We=>setTimeout(We,1200));let gt=await this.driver.getForegroundPackage(s);gt&&!gt.includes(t)&&(console.log(L.yellow(`Restoring ${t} foreground after back navigation...`)),await this.driver.launchApp(t,s),await new Promise(We=>setTimeout(We,2e3)));let fe=await this.getCleanUiHierarchy(t,s);Z=fe.xml,_=fe.flat,this.visitedScreenHashes.add(be(_));let $e=await this.runErrorSentinel(t,s,"Post-Back-Restore"),et=$e.hasError?"FAIL":"PASS",Jt=await S(et,!0);this.reporter.logStep("List Dive","Navigated back to master screen","Back",et,"KEYCODE_BACK",Jt,$e.hasError?{errors:$e.errors}:void 0),et==="PASS"&&console.log(L.green("\u2714 Master view restored."))}else console.log(L.gray("No list items found for master-detail exploration."))}}console.log(L.yellow(`
859
- [5/5] Inspecting Navigation Tabs...`));let f=await this.driver.getForegroundPackage(s);f&&!f.includes(t)&&(console.log(L.gray(`Restoring ${t} foreground for tab traversal...`)),await this.driver.launchApp(t,s),await new Promise(P=>setTimeout(P,2e3))),await this.dismissSoftKeyboard(s);let I=await this.getCleanUiHierarchy(t,s);Z=I.xml,_=I.flat;let N=Ut(_,void 0,t);if(N.length>1){console.log(L.magenta(`\u26A1 Found ${N.length} navigation tab(s). Traversal sequence initiated...`));let P=N.slice(0,i),O=1;for(let x of P){let $=x.text||x.contentDesc||`Tab #${O}`;console.log(L.gray(` - Visiting ${$}...`)),await this.dismissSoftKeyboard(s);let A=Math.min(x.bounds.centerY,x.bounds.y2-4);this.executedStepCount++,await this.driver.tap(x.bounds.centerX,A,s),await new Promise(q=>setTimeout(q,1500)),await this.packageJailGuard(t,s);let H=await this.getCleanUiHierarchy(t,s);Z=H.xml,_=H.flat;let R=be(_);this.visitedScreenHashes.add(R),this.memory.markScreenCrawled(R,[$]);let W=await this.runErrorSentinel(t,s,`Tab-${$}`),X=W.hasError?"FAIL":"PASS",te=await S(X,!0);if(this.reporter.logStep("Tab Traversal",`Navigated to tab "${$}"`,"Tap",X,$,te,W.hasError?{errors:W.errors}:void 0),console.log(X==="PASS"?L.green(` \u2714 Tab "${$}" loaded cleanly.`):L.red(` \u2716 Tab "${$}" triggered errors.`)),this.executedStepCount<p&&this.visitedScreenHashes.size<l){let q=[],ne=M*.08,Q=M*.88,Ae=fe=>fe.bounds.centerY>ne&&fe.bounds.centerY<Q&&fe.clickable&&fe.bounds.width>30&&fe.bounds.height>20&&N.every($e=>Math.abs($e.bounds.centerX-fe.bounds.centerX)>40||Math.abs($e.bounds.centerY-fe.bounds.centerY)>40),Se=Bt(_,{width:j,height:M}).filter(Ae);for(let fe of Se.slice(0,3))q.push({node:fe,type:"list"});let He=Qe(_).filter(fe=>!(!Ae(fe)||st([fe],M)));for(let fe of He.slice(0,4))q.push({node:fe,type:"button"});let gt=Dt(_).filter(Ae);for(let fe of gt.slice(0,2))q.push({node:fe,type:"toggle"});for(let fe of q){if(this.executedStepCount>=p||this.visitedScreenHashes.size>=l)break;let $e=fe.node;if(Ue($e)){this.externalSkippedList.push($e.text||$e.contentDesc||"External Link");continue}if(Be($e,c,o)){let Et=$e.text||$e.contentDesc||"Destructive Action";this.safetyHitList.push(Et);continue}let et=$e.text||$e.contentDesc||$e.resourceId||`${fe.type} element`;console.log(L.gray(` Sub-crawl [${$}]: Exercising ${fe.type} "${et.slice(0,40)}"...`)),this.executedStepCount++,await this.driver.tap($e.bounds.centerX,$e.bounds.centerY,s),await new Promise(Et=>setTimeout(Et,1200)),await this.dismissSoftKeyboard(s),await this.packageJailGuard(t,s);let Jt=await this.getCleanUiHierarchy(t,s);Z=Jt.xml,_=Jt.flat;let We=be(_),cn=!this.visitedScreenHashes.has(We);this.visitedScreenHashes.add(We);let ns=await this.runErrorSentinel(t,s,`Sub-${et.slice(0,30)}`),di=ns.hasError?"FAIL":"PASS",ln=await S(di,cn);if(this.reporter.logStep("Tab Sub-Crawl",`Tested ${fe.type} "${et}" under tab "${$}"`,"Tap",di,et,ln,ns.hasError?{errors:ns.errors}:void 0),We!==R){console.log(L.cyan(` \u{1F50E} Screen transitioned. Discovered new screen state [${We.slice(0,8)}]`));let Et=Qe(_).filter(Ve=>Ae(Ve)&&!Be(Ve,c,o)&&!Ue(Ve));if(Et.length>0&&this.executedStepCount<p){let Ve=Et[0],dn=Ve.text||Ve.contentDesc||"Child Action";console.log(L.gray(` Exercising child action "${dn.slice(0,30)}"...`)),this.executedStepCount++,await this.driver.tap(Ve.bounds.centerX,Ve.bounds.centerY,s),await new Promise(pn=>setTimeout(pn,1e3)),await this.dismissSoftKeyboard(s)}console.log(L.gray(` Restoring ${$} master view via Back...`)),await this.driver.pressBack(s),await new Promise(Ve=>setTimeout(Ve,1e3)),await this.dismissSoftKeyboard(s);let pi=await this.getCleanUiHierarchy(t,s);Z=pi.xml,_=pi.flat}}}O++}console.log(L.green("\u2714 Navigation tabs traversed successfully!"))}else console.log(L.gray("Single-screen or tabs not detected at screen footer."));for(let P of this.visitedScreenHashes)this.memory.markScreenCrawled(P);await this.memory.save();let de=this.memory.getStats();if(this.reporter.logStep("Coverage Audit",`State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s). Memory Engine cached ${de.crawledScreens} crawled screen(s).`,"Verify","PASS"),await this.runErrorSentinel(t,s,"Final-State"),B){console.log(L.cyan("\u{1F3AC} Finalizing exploration screen recording..."));let P=ei.resolve(process.cwd(),"output"),O=ei.join(P,`${t}-exploration.mp4`),x=await this.driver.stopScreenRecording(B,O,s);x&&Kn.existsSync(x)&&(this.reporter.setVideoPath?.(`${t}-exploration.mp4`),console.log(L.green(`\u2714 Screen recording saved to: output/${t}-exploration.mp4`))),B=null}let he=await this.reporter.generateReport(),g=he.failedSteps===0&&he.errorsDetected.length===0;console.log(g?L.bold.green(`
860
- \u2728 PromptTest Exploration Complete \u2014 All Steps Passed!`):L.bold.red(`
861
- \u26A0\uFE0F PromptTest Exploration Complete \u2014 Issues Detected!`)),console.log(L.white(`Summary: ${he.passedSteps} passed, ${he.warnSteps} warnings, ${he.failedSteps} failed, ${he.errorsDetected.length} errors detected.`)),this.safetyHitList.length>0&&console.log(L.cyan(`\u{1F6E1}\uFE0F Safety Shield: Blocked ${this.safetyHitList.length} destructive action(s) [${this.safetyHitList.join(", ")}].`)),console.log(L.cyan(`\u{1F5FA}\uFE0F State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s).`)),console.log(L.cyan(`\u{1F9E0} Memory Engine Coverage: ${de.crawledScreens} crawled screen(s) indexed offline.`)),console.log(L.cyan(`Full Audit Report generated at: output/prompttest-report.md
862
- `))}finally{if(B)try{await this.driver.stopScreenRecording(B,ei.join("output",`${t}-exploration.mp4`),s)}catch{}D&&St(D)}}async runErrorSentinel(e,t,s){let i=[];try{let[r,n]=await Promise.all([this.driver.checkCrashes(e,t),this.driver.getUiHierarchy(t)]);if(r){let l=`Crash in ${s}: ${r.slice(0,300)}`;console.log(L.bold.red(`\u{1F6A8} FATAL EXCEPTION DETECTED in ${e} [${s}]!`)),this.reporter.logError(l),i.push(l)}let{flat:o}=we(n),c=vi(o);if(c.length>0)for(let l of c){let p=l.text||l.contentDesc,d=`UI Error dialog in ${s}: "${p}"`;console.log(L.bold.yellow(`\u26A0\uFE0F Error Dialog/Banner on screen [${s}]: "${p}"`)),this.reporter.logError(d),i.push(d)}}catch{}return{hasError:i.length>0,errors:i}}},Xi=new ut;var qn={".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"};async function Gi(a={}){let e=a.port??4040,t=a.host||"127.0.0.1",s=process.cwd();(s==="/"||s==="\\")&&(s=zi.homedir?zi.homedir():process.env.HOME||process.env.USERPROFILE||"/tmp");let i=a.outputDir?z.resolve(a.outputDir):z.resolve(s,"output"),r=a.suiteName||"PromptTest Live Monitor",n=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,c=setInterval(()=>{for(let w of o)try{w.write(`: heartbeat
863
-
864
- `)}catch{o.delete(w)}},15e3),l=a.driver||new tt,p=z.dirname(Zn(import.meta.url)),d=a.specsDir?z.resolve(a.specsDir):z.resolve(process.cwd(),"specs");if(!ee.existsSync(d)){let w=z.resolve(p,"../specs");ee.existsSync(w)&&(d=w)}let h=null;function u(){return h||(h=new Ke(l)),h}let m=z.join(i,"baselines"),y=new ct(m),v=null,T=null,S=["strict","moderate","interactive","disabled"],b=null,E=0;function D(w){return w?{success:!0,sessionId:w.id,status:w.status,packageName:w.packageName,deviceId:w.deviceId,safetyMode:w.safetyMode,maxDepth:w.maxDepth,maxSteps:w.maxSteps,maxScreens:w.maxScreens,screensDiscovered:w.screensDiscovered,elementsMapped:w.elementsMapped,stepsTaken:w.stepsTaken,startedAt:w.startedAt,finishedAt:w.finishedAt,crashes:w.crashes,screens:w.screens,blocked:w.blocked,events:w.events,specLines:w.specLines,error:w.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 B(w){let f=w.match(/"([^"]+)"/);return f?f[1]:null}function k(w){let f=w.specLines.length>0?w.specLines:["Launch the app"],I=["# Auto-generated by PromptTest Auto-Pilot (Autonomous Explorer)",`# Package: ${w.packageName}`,`# Safety Policy: ${w.safetyMode.toUpperCase()} | Max Depth: ${w.maxDepth} | Step Budget: ${w.maxSteps}`,`# Explored at: ${w.startedAt}`,`# Screens Discovered: ${w.screensDiscovered} | Steps Executed: ${w.stepsTaken}`];return w.deviceId&&I.push(`# Device: ${w.deviceId}`),[...I,"",...f,""].join(`
865
- `)}function j(w,f,I){w.status=f,w.finishedAt=new Date().toISOString(),I&&(w.error=I),Y("explore",{...D(w),event:{type:f==="failed"?"error":"done",description:I||f}})}function M(w){let f=b;if(f){if(typeof w.totalSteps=="number"&&(f.stepsTaken=Math.max(f.stepsTaken,w.totalSteps)),typeof w.discoveredScreens=="number"&&(f.screensDiscovered=Math.max(f.screensDiscovered,w.discoveredScreens)),w.type==="action"){let I=B(w.description);I&&f.elementLabels.add(I),w.description&&f.specLines[f.specLines.length-1]!==w.description&&f.specLines.push(w.description)}if(w.screenHash){let I=w.screenTitle||w.description||w.screenHash.slice(0,8),N=f.screens.find(de=>de.hash===w.screenHash);N?(N.visits++,typeof w.elementCount=="number"&&(N.elements=w.elementCount),N.label===N.hash.slice(0,8)&&w.screenTitle&&(N.label=w.screenTitle,N.title=w.screenTitle)):f.screens.push({id:w.screenHash,hash:w.screenHash,depth:w.depth??f.screens.length,title:I,label:I,visits:1,elements:w.elementCount??0}),f.screensDiscovered=Math.max(f.screensDiscovered,f.screens.length)}if(w.type==="crash"){let I=new Date().toISOString(),N=w.screenHash,de=N?f.screens.find(he=>he.hash===N):void 0;f.crashes.push({id:N||`crash-${f.crashes.length+1}`,type:w.description.toLowerCase().includes("anr")?"anr":"crash",message:w.description,screen:de?.label||N?.slice(0,8),timestamp:I,description:w.description,screenHash:N})}w.type==="safety_block"&&f.blocked.push(w.description),f.elementsMapped=f.elementLabels.size,f.events.push(w),f.events.length>300&&f.events.shift(),Y("explore",{...D(f),event:w})}}function J(w){let f=S.includes(w.safetyMode||"")?w.safetyMode:"strict";E++;let I={id:`explore-${Date.now()}-${E}`,packageName:w.packageName,deviceId:w.deviceId,safetyMode:f,maxDepth:w.maxDepth??5,maxSteps:w.maxSteps??100,maxScreens:w.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}};b=I;let N=new ut(l);return Y("explore",{...D(I),event:{type:"state_change",description:`Auto-Pilot launched on ${I.packageName} (${I.safetyMode} mode)`}}),N.explore({packageName:I.packageName,serial:I.deviceId,strategy:"dfs",safetyMode:I.safetyMode,maxDepth:I.maxDepth,maxScreens:I.maxScreens,stepBudget:I.maxSteps,screenshots:"state-change",onStep:M,abortSignal:I.abort}).then(()=>{j(I,I.abort.aborted?"stopped":"completed")}).catch(de=>{j(I,I.abort.aborted?"stopped":"failed",de instanceof Error?de.message:String(de))}),I}function Y(w,f){let I=`event: ${w}
866
- data: ${JSON.stringify(f)}
867
-
868
- `;for(let N of o)try{N.write(I)}catch{o.delete(N)}}function K(w){return new Promise((f,I)=>{let N="";w.on("data",de=>{N+=de}),w.on("end",()=>{try{f(N?JSON.parse(N):{})}catch{I(new Error("Invalid JSON payload"))}}),w.on("error",I)})}let le=Qn.createServer(async(w,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"),w.method==="OPTIONS"){f.writeHead(204),f.end();return}let I=new URL(w.url||"/",`http://${t}:${e}`),N=decodeURIComponent(I.pathname);if(N==="/api/devices"){try{let g=await l.getConnectedDevices();f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,devices: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(N==="/api/screen-state"){try{let g=I.searchParams.get("deviceId")||void 0,P=I.searchParams.get("hierarchy")!=="false",[O,x,$]=await Promise.all([l.captureScreenshot(g),P?l.getUiHierarchy(g):Promise.resolve(null),l.getForegroundPackage(g)]),A=[];if(x){let{flat:H}=we(x);A=H.map(R=>({index:R.index,text:R.text,resourceId:R.resourceId,className:R.className,contentDesc:R.contentDesc,clickable:R.clickable,scrollable:R.scrollable,bounds:R.bounds}))}f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,deviceId:g||l.getDefaultDevice()||"default",foregroundApp:$,screenshot:`data:image/png;base64,${O.toString("base64")}`,elements:P?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(N==="/api/tap"&&w.method==="POST"){try{let g=await K(w);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 l.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(N==="/api/type"&&w.method==="POST"){try{let g=await K(w);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 l.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(N==="/api/key"&&w.method==="POST"){try{let g=await K(w);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 l.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(N==="/api/wake"&&w.method==="POST"){try{let g=await K(w);await l.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(N==="/api/wifi/connect"&&w.method==="POST"){try{let g=await K(w),P=await l.connectWifi(g.target,g.port||5555,g.deviceId);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:P.success,target:P.target,message:P.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(N==="/api/wifi/disconnect"&&w.method==="POST"){try{let g=await K(w),P=await l.disconnectWifi(g.target);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:P}))}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(N==="/api/wifi/pair"&&w.method==="POST"){try{let g=await K(w);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 P=await l.pairWifi(g.target,g.pairingCode);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify(P))}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(N==="/api/metrics"&&w.method==="GET"){try{let g=I.searchParams.get("deviceId")||void 0,P=await l.getDeviceMetrics(g);if(Object.keys(P).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:P}))}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(N==="/api/doctor"&&w.method==="GET"){try{let g=I.searchParams.get("deviceId")||void 0,P=I.searchParams.get("packageName")||void 0,O=await ss(l,{serial:g,packageName:P});f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,report:O}))}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((N==="/api/explore/start"||N==="/api/explore")&&w.method==="POST"){try{let g=await K(w),P=(g.packageName||g.targetPackage||"").trim();if(!P){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing packageName (packageName or targetPackage)"}));return}if(b&&b.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:b.id}));return}let O=J({packageName:P,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:O.id,status:O.status,packageName:O.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((N==="/api/explore/status"||N==="/api/explore")&&w.method==="GET"){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify(D(b)));return}if((N==="/api/explore/stop"||N==="/api/explore")&&(w.method==="POST"||w.method==="DELETE")){if(!b){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}b.abort.aborted=!0,f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,sessionId:b.id,status:"stopping",packageName:b.packageName}));return}if(N==="/api/explore/export"&&w.method==="POST"){try{let g=await K(w);if(!b){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 P=(g.specName||`explored_${b.packageName}.txt`).trim(),O=z.basename(P).replace(/[^a-zA-Z0-9._-]/g,"_"),x=O.endsWith(".txt")?O:`${O}.txt`,$=k(b);await ee.promises.mkdir(d,{recursive:!0}),await ee.promises.writeFile(z.join(d,x),$,"utf8"),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,specName:x,content:$,screensDiscovered:b.screensDiscovered,stepsTaken:b.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(N==="/api/record/start"&&w.method==="POST"){try{let g=await K(w);if(v){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 P=g.deviceId||l.getDefaultDevice()||void 0,O=g.specName?z.join(d,z.basename(g.specName)):void 0;v=new lt(l),T=P||"default",v.onStep=x=>{Y("record-step",{step:x,deviceId:P})},await v.startSession(O,P,{append:g.append}),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:"Recording session started",deviceId:P}))}catch(g){if(v)try{await v.stopSession()}catch{}v=null,T=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(N==="/api/record/stop"&&w.method==="POST"){try{if(!v){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 v.stopSession();v=null,T=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){v=null,T=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(N==="/api/record/status"&&w.method==="GET"){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,isRecording:v!==null,deviceId:T}));return}if(N==="/api/specs"&&w.method==="GET"){try{ee.existsSync(d)||ee.mkdirSync(d,{recursive:!0});let P=ee.readdirSync(d).filter(O=>O.endsWith(".txt")||O.endsWith(".spec")).map(O=>{let x=z.join(d,O),$=ee.statSync(x);return{name:O,path:z.relative(process.cwd(),x).replace(/\\/g,"/"),size:$.size,modified:$.mtime.toISOString()}});f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,specs:P}))}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((N.startsWith("/api/specs/")||N==="/api/spec")&&w.method==="GET"){try{let g="";N.startsWith("/api/specs/")?g=decodeURIComponent(N.slice(11)):g=I.searchParams.get("name")||"";let P=z.basename(g.trim());if(!P){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing spec file name"}));return}let O=z.join(d,P);if(!ee.existsSync(O)){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Spec file not found: ${P}`}));return}let x=ee.readFileSync(O,"utf-8");f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,name:P,content:x}))}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((N==="/api/specs"||N==="/api/spec")&&w.method==="POST"){try{let g=await K(w),P=(g.name||"").trim();if(!P){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing spec file name"}));return}!P.endsWith(".txt")&&!P.endsWith(".spec")&&(P+=".txt");let O=z.basename(P);ee.existsSync(d)||ee.mkdirSync(d,{recursive:!0});let x=z.join(d,O);ee.writeFileSync(x,g.content??"","utf-8"),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,name:O,path:x}))}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(N==="/api/action"&&w.method==="POST"){try{let g=await K(w);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 P=Nt(g.step);if(P.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 O=u(),x=P[0],$=g.deviceId||l.getDefaultDevice()||void 0,A=Date.now(),H=await O.executeStep(x,$),R=Date.now()-A;Y("step",{stepNumber:n.steps.length+1,instruction:x.originalText,status:"PASS",durationMs:R}),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,step:x.originalText,type:x.type,durationMs:R,result:H||null}))}catch(g){let P=g instanceof Error?g.message:String(g);Y("step",{stepNumber:n.steps.length+1,instruction:"Step Execution",status:"FAIL",durationMs:0,error:P}),f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:P}))}return}if(N==="/api/run"&&w.method==="POST"){try{let g=await K(w),P="";if(g.specName){let A=z.basename(g.specName);if(P=z.join(d,A),!ee.existsSync(P)){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")ee.existsSync(i)||ee.mkdirSync(i,{recursive:!0}),P=z.join(i,"_studio_temp_spec.txt"),ee.writeFileSync(P,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 O=g.deviceId||l.getDefaultDevice()||void 0,x=u();n.steps=[],n.totalSteps=0,n.passedSteps=0,n.failedSteps=0,n.warnSteps=0,n.skippedSteps=0;let $=nt.onStep?.(A=>{Y("step",{stepNumber:A.stepIndex,instruction:A.description,status:A.status,durationMs:A.durationMs||0,error:A.details?.error})});(async()=>{try{let A=await x.runSpec(P,{serial:O,saveBaseline:g.saveBaseline!==void 0?g.saveBaseline:!1,compareBaseline:g.compareBaseline!==void 0?g.compareBaseline:!0,screenshotMode:"all",heal:g.heal??!1,fresh:g.fresh??!1,continueOnError:g.continueOnError??!1});n=A,Y("complete",A)}catch(A){let H=A instanceof Error?A.message:String(A);n.errorsDetected.push(H),Y("error",{error:H}),Y("complete",n)}finally{$&&$()}})(),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(N==="/api/reports"&&w.method==="GET"){try{if(!ee.existsSync(i)){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,reports:[]}));return}let O=ee.readdirSync(i).filter(x=>x.endsWith("-results.json")).map(x=>{let $=x.replace(/-results\.json$/,""),A=z.join(i,x),H=`${$}-report.html`,R=ee.existsSync(z.join(i,H)),W=ee.statSync(A);try{let X=JSON.parse(ee.readFileSync(A,"utf-8")),te=X.totalSteps||X.steps?.length||0,q=X.passedSteps||0,ne=X.failedSteps||0,Q=te>0?Math.round(q/te*100):100;return{id:$,fileName:x,suiteName:X.packageName||$,packageName:X.packageName||"Unknown",deviceId:X.deviceId||"Unknown",startTime:X.startTime||W.mtime.toISOString(),endTime:X.endTime||W.mtime.toISOString(),durationSeconds:X.durationSeconds||0,totalSteps:te,passedSteps:q,failedSteps:ne,warnSteps:X.warnSteps||0,skippedSteps:X.skippedSteps||0,passRate:Q,errorsCount:X.errorsDetected?.length||0,htmlReportPath:R?`/${H}`:null,jsonReportPath:`/${x}`}}catch{return null}}).filter(Boolean).sort((x,$)=>new Date($.startTime).getTime()-new Date(x.startTime).getTime());f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,reports:O}))}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((N.startsWith("/api/reports/")||N==="/api/report-detail")&&w.method==="GET"){try{let g="";N.startsWith("/api/reports/")?g=decodeURIComponent(N.slice(13)):g=I.searchParams.get("id")||"";let P=z.basename(g.trim()),O=P.endsWith("-results.json")?P:`${P}-results.json`,x=z.join(i,O);if(!ee.existsSync(x)){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Report not found: ${P}`}));return}let $=JSON.parse(ee.readFileSync(x,"utf-8"));f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,id:P,report:$}))}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(N==="/api/baselines"&&w.method==="GET"){try{ee.existsSync(m)||ee.mkdirSync(m,{recursive:!0});let g=I.searchParams.get("spec")||"",P=g.replace(/[^a-zA-Z0-9._-]/g,"_"),O=ee.readdirSync(m),x=new Map;for(let A of O){if(P&&!A.startsWith(`${P}_step`))continue;let H=A.match(/_step(\d+)(_current|_diff)?\.png$/);if(!H)continue;let R=parseInt(H[1],10),W=H[2];x.has(R)||x.set(R,{});let X=x.get(R);W?W==="_current"?X.current=`/baselines/${A}`:W==="_diff"&&(X.diff=`/baselines/${A}`):X.baseline=`/baselines/${A}`}let $=await Promise.all(Array.from(x.entries()).map(async([A,H])=>{let R=await y.getMasks(g,A);return{stepIndex:A,...H,masks:R}}));$.sort((A,H)=>A.stepIndex-H.stepIndex),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,spec:g,baselines:$,matchingModes:ys}))}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(N==="/api/baselines/compare"&&w.method==="POST"){try{let g=await K(w);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 P;if(g.screenshot){let $=g.screenshot.replace(/^data:image\/\w+;base64,/,"");P=Buffer.from($,"base64")}else{let $=g.deviceId||l.getDefaultDevice()||void 0;P=await l.captureScreenshot($)}let O=await y.compare(g.specName,g.stepIndex,P,{threshold:g.threshold,mode:g.mode,maskRegions:g.maskRegions}),x=await y.getMasks(g.specName,g.stepIndex);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,result:{...O,masks:x,baselineUrl:`/baselines/${z.basename(O.baselineFile)}`,currentUrl:`/baselines/${z.basename(O.currentFile)}`,diffUrl:O.diffImagePath?`/baselines/${z.basename(O.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(N==="/api/baselines/approve"&&w.method==="POST"){try{let g=await K(w);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 P;if(g.screenshot){let x=g.screenshot.replace(/^data:image\/\w+;base64,/,"");P=Buffer.from(x,"base64")}else{let x=g.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),$=z.join(m,`${x}_step${String(g.stepIndex).padStart(3,"0")}_current.png`);if(!ee.existsSync($)){let A=g.deviceId||l.getDefaultDevice()||void 0;try{P=await l.captureScreenshot(A)}catch{}}}let O=await y.approveBaseline(g.specName,g.stepIndex,P);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,approved:!0,stepIndex:g.stepIndex,baselineUrl:`/baselines/${z.basename(O)}`}))}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(N==="/api/baselines/clear"&&w.method==="POST"){try{let g=await K(w);if(!g.specName){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName"}));return}await y.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(N==="/api/baselines/delete-step"&&w.method==="POST"){try{let g=await K(w);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 y.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(N==="/api/baselines/upload"&&w.method==="POST"){try{let g=await K(w);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 P=g.image.replace(/^data:image\/\w+;base64,/,""),O=Buffer.from(P,"base64"),x=await y.saveBaseline(g.specName,g.stepIndex,O),$=g.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),A=z.join(m,`${$}_step${String(g.stepIndex).padStart(3,"0")}_diff.png`);if(ee.existsSync(A))try{await ee.promises.unlink(A)}catch{}let H=z.join(m,`${$}_step${String(g.stepIndex).padStart(3,"0")}_current.png`),R,W;if(ee.existsSync(H)){let te=await ee.promises.readFile(H),q=await y.compare(g.specName,g.stepIndex,te);R=`/baselines/${z.basename(H)}`,W=q.diffImagePath?`/baselines/${z.basename(q.diffImagePath)}`:void 0}else if(g.deviceId||l.getDefaultDevice()){let te=g.deviceId||l.getDefaultDevice()||void 0;try{let q=await l.captureScreenshot(te),ne=await y.compare(g.specName,g.stepIndex,q);R=`/baselines/${z.basename(H)}`,W=ne.diffImagePath?`/baselines/${z.basename(ne.diffImagePath)}`:void 0}catch{}}let X=await y.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/${z.basename(x)}`,currentUrl:R,diffUrl:W,masks:X}))}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(N==="/api/baselines/masks"&&w.method==="GET"){try{let g=I.searchParams.get("specName")||I.searchParams.get("spec")||"",P=parseInt(I.searchParams.get("stepIndex")||"",10);if(!g||isNaN(P)){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName or valid stepIndex"}));return}let O=await y.getMasks(g,P);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,masks:O}))}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(N==="/api/baselines/masks"&&w.method==="POST"){try{let g=await K(w);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 y.saveMasks(g.specName,g.stepIndex,g.masks);let P=g.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),O=z.join(m,`${P}_step${String(g.stepIndex).padStart(3,"0")}_current.png`),x,$,A=0,H=0,R=!0;if(ee.existsSync(O)){let W=await ee.promises.readFile(O),X=await y.compare(g.specName,g.stepIndex,W,{mode:g.mode,threshold:g.threshold,maskRegions:g.masks});x=`/baselines/${z.basename(O)}`,$=X.diffImagePath?`/baselines/${z.basename(X.diffImagePath)}`:void 0,A=X.diffRatio,H=X.diffPixels,R=X.passed}f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,masks:g.masks,currentUrl:x,diffUrl:$,diffRatio:A,diffPixels:H,passed:R}))}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(N==="/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
869
- data: ${JSON.stringify({status:"connected",time:Date.now()})}
870
-
871
- `),o.add(f),w.on("close",()=>{o.delete(f)});return}if(N==="/"||N==="/report"||N==="/index.html"){let g=hs(n,{suiteName:r});f.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-cache"}),f.end(g);return}if(N==="/api/report"||N==="/results.json"){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8","Access-Control-Allow-Origin":"*"}),f.end(JSON.stringify(n,null,2));return}let de=N.replace(/^\/+/,""),he=z.join(i,de);if(!he.startsWith(i)){f.writeHead(403,{"Content-Type":"text/plain"}),f.end("Access Denied");return}if(ee.existsSync(he)&&ee.statSync(he).isFile()){let g=z.extname(he).toLowerCase(),P=qn[g]||"application/octet-stream";f.writeHead(200,{"Content-Type":P,"Cache-Control":"no-cache"}),ee.createReadStream(he).pipe(f);return}f.writeHead(404,{"Content-Type":"text/plain"}),f.end("Not Found")});await new Promise((w,f)=>{le.once("error",f),le.listen(e,t,()=>{w()})});let Z=le.address(),_=typeof Z=="object"&&Z?Z.port:e,De=`http://${t}:${_}`;return{server:le,port:_,url:De,broadcastStep(w){n.steps.push(w),n.totalSteps=n.steps.length,w.status==="PASS"?n.passedSteps++:w.status==="FAIL"?n.failedSteps++:w.status==="WARN"?n.warnSteps++:w.status==="SKIPPED"&&n.skippedSteps++,Y("step",w)},broadcastComplete(w){n=w,Y("complete",w)},broadcastError(w){n.errorsDetected.push(w),Y("error",{error:w})},updateReport(w){n=w},async close(){if(clearInterval(c),v){try{await v.stopSession()}catch{}v=null,T=null}b&&b.status==="running"&&(b.abort.aborted=!0);for(let w of o)try{w.end()}catch{}o.clear(),await new Promise((w,f)=>{le.close(I=>{I?f(I):w()})})}}}import er from"zlib";import tr from"fs";function sr(a){let e=-1;for(let t=0;t<a.length;t++){let s=a[t];e^=s;for(let i=0;i<8;i++)e=e>>>1^-(e&1)&3988292384}return~e>>>0}function ir(a=new Date){let e=(a.getHours()&31)<<11|(a.getMinutes()&63)<<5|Math.floor(a.getSeconds()/2)&31,t=(a.getFullYear()-1980&127)<<9|(a.getMonth()+1&15)<<5|a.getDate()&31;return{time:e,date:t}}function nr(a){let e=[],t=[],s=0;for(let c of a){let l=c.data,p=er.deflateRawSync(l),d=p.length<l.length,h=d?p:l,u=d?8:0,m=sr(l),y=l.length,v=h.length,{time:T,date:S}=ir(c.date),b=Buffer.from(c.name.replace(/\\/g,"/"),"utf-8"),E=Buffer.alloc(30+b.length);E.writeUInt32LE(67324752,0),E.writeUInt16LE(20,4),E.writeUInt16LE(2048,6),E.writeUInt16LE(u,8),E.writeUInt16LE(T,10),E.writeUInt16LE(S,12),E.writeUInt32LE(m,14),E.writeUInt32LE(v,18),E.writeUInt32LE(y,22),E.writeUInt16LE(b.length,26),E.writeUInt16LE(0,28),b.copy(E,30),e.push(E),e.push(h);let D=Buffer.alloc(46+b.length);D.writeUInt32LE(33639248,0),D.writeUInt16LE(20,4),D.writeUInt16LE(20,6),D.writeUInt16LE(2048,8),D.writeUInt16LE(u,10),D.writeUInt16LE(T,12),D.writeUInt16LE(S,14),D.writeUInt32LE(m,16),D.writeUInt32LE(v,20),D.writeUInt32LE(y,24),D.writeUInt16LE(b.length,28),D.writeUInt16LE(0,30),D.writeUInt16LE(0,32),D.writeUInt16LE(0,34),D.writeUInt16LE(0,36),D.writeUInt32LE(0,38),D.writeUInt32LE(s,42),b.copy(D,46),t.push(D),s+=E.length+h.length}let i=Buffer.concat(t),r=i.length,n=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(r,12),o.writeUInt32LE(n,16),o.writeUInt16LE(0,20),Buffer.concat([...e,i,o])}async function Ki(a,e){let t=nr(e);await tr.promises.writeFile(a,t)}import rr from"chalk";var ti=null;async function Qi(a,e){if(ti)return ti;let t=!!(process.env.CI||process.env.GITHUB_ACTIONS||process.env.TRAVIS||process.env.CIRCLECI||process.env.GITLAB_CI),s=!1;try{let{stdout:l}=await a.execAdb(["shell","getprop","ro.kernel.qemu"],e),{stdout:p}=await a.execAdb(["shell","getprop","ro.product.model"],e),{stdout:d}=await a.execAdb(["shell","getprop","ro.build.characteristics"],e),h=(p||"").toLowerCase(),u=(d||"").toLowerCase();s=l?.trim()==="1"||h.includes("sdk")||h.includes("emulator")||u.includes("emulator")}catch{s=!1}let i=1;t?i=s?2.8:2:s?i=1.5:i=1;let r=parseFloat(process.env.PROMPTTEST_SETTLE_FACTOR||"");!isNaN(r)&&r>0&&(i=Math.max(.5,r));let n={isEmulator:s,isCI:t,velocityMultiplier:i,defaultTimeoutMs:Math.round(3500*i),extendedTimeoutMs:Math.round(6500*i),quiescenceSettleMs:Math.round(150*Math.min(i,2)),initialPollMs:t?150:80,maxPollIntervalMs:t?600:350};ti=n;let o=s?"Android Emulator":"Physical Android Device",c=t?"CI / Cloud Runner":"Local Environment";return console.log(rr.cyan(`\u26A1 [Device Profiler] Detected ${o} in ${c} (Velocity Multiplier: ${i.toFixed(1)}x, Dynamic Timeout: ${n.defaultTimeoutMs}ms)`)),n}var Ke=class{driver;memory;reporter;checkpoints;baselines;lastHierarchy=null;screenDimensions={width:C.screen.fallbackWidth,height:C.screen.fallbackHeight};navBarHeight=0;targetPackage=void 0;constructor(e,t,s,i,r,n){this.driver=e||ke,this.memory=t||Ct,this.reporter=s||nt,this.checkpoints=i||$i,this.baselines=r||Ss,this.semanticResolver=n||Qs()}semanticResolver;healEnabled=!1;activeLocale="en";runtimeVariables=new Map;loopStack=[];expandSpecIncludes(e,t=new Set){return Ts(e,t)}async expandSpec(e,t){let s=t||this.semanticResolver,i=e.split(`
872
- `),r=[],n=0;for(let o of i){let c=o.trim();if(!c||c.startsWith("#")||c.startsWith("//")){r.push(o);continue}let l=this.parsePrompt(c);if(l.length>0&&l[0].type!==void 0&&!c.toLowerCase().startsWith("intent:")){r.push(o);continue}let d=c.replace(/^intent:\s*/i,""),h=this.memory?.getIntentExpansion(d);if(h?.length){r.push(`# [Cached Intent] ${c}`,...h),n+=h.length;continue}let u=await s.resolveIntent(d);if(u?.length){r.push(`# [AI Expanded] ${c}`);let m=u.map(y=>y.originalText);r.push(...m),this.memory&&(this.memory.recordIntentExpansion(d,m),await this.memory.save().catch(()=>{})),n+=u.length}else r.push(o)}return{expanded:r.join(`
873
- `),stepsAdded:n}}parsePrompt(e,t){return Nt(e,t)}async runSpec(e,t,s){if(!Ye.existsSync(e))throw new Error(`Spec file not found: ${e}`);let i=typeof t=="string"?{packageName:t,serial:s,screenshotMode:"on-failure",continueOnError:!1}:{screenshotMode:"on-failure",continueOnError:!1,...t};i.heal!==void 0&&(this.healEnabled=i.heal),i.noAi&&(this.semanticResolver=Qs({provider:"heuristic"})),this.activeLocale=(i.locale||C.locale||"en").toLowerCase(),typeof this.memory.setLocale=="function"&&this.memory.setLocale(this.activeLocale);let r=this.expandSpecIncludes(e),n=r,o=[],c=Mt.extractDataDirective(r,Ie.dirname(e));if(c)n=c.cleanedSpec,o=c.rows;else if(i.dataFile){let S=Ie.isAbsolute(i.dataFile)?i.dataFile:Ie.resolve(process.cwd(),i.dataFile);o=Mt.loadFromFile(S)}(i.onlyRow!==void 0||i.iterations!==void 0)&&(o.length>0?o=Mt.filterRows(o,{onlyRow:i.onlyRow,iterations:i.iterations}):i.iterations&&i.iterations>0&&(o=Array.from({length:i.iterations},()=>({}))));let l=o.length>0?o.map(S=>({...i.variables||{},...S})):[i.variables||{}],p=Ie.basename(e,Ie.extname(e)),d=n.match(/#\s*(?:Target\s*)?Package:\s*([a-zA-Z0-9._-]+)/i),h=d?d[1]:void 0;if(i.tags){let S=Ri(r),b=i.tags.split(",").map(D=>D.trim().toLowerCase()).filter(Boolean);if(!b.some(D=>S.includes(D)))return console.log(V.yellow(`
874
- \u23E9 Skipping ${e}: tags [${S.join(", ")}] do not match filter [${b.join(", ")}]`)),Zt(h||i.packageName||"",i.serial||"",[],"SKIPPED")}if(i.listSteps){let S=this.parsePrompt(n,l[0]);return console.log(Oi(S)),Zt(h||i.packageName||"",i.serial||"",S,"SKIPPED","Listing")}if(i.dryRun){let S=this.parsePrompt(n,l[0]);return console.log(V.bold.cyan(`
875
- \u{1F50D} [Dry Run] Validated ${S.length} test step(s) in ${e} successfully (no device interaction).`)),Zt(h||i.packageName||"",i.serial||"",S,"PASS","Validation")}if(l.length>1)console.log(V.cyan(`
876
- \u{1F4CB} Loaded spec with ${l.length} data iteration(s) from ${e}`));else{let S=this.parsePrompt(n,l[0]);console.log(V.cyan(`
877
- \u{1F4CB} Loaded ${S.length} test step(s) from ${e}`))}console.log(V.gray("Execution Policy: ")+V.bold.white(i.continueOnError?"Continue on Error (Modular Run)":"Fail-Fast (Default)")+V.gray(" | Screenshots: ")+V.bold.white(i.screenshotMode==="all"?"All Steps":"Failure-Only")),i.serial=await this.driver.ensureDefaultDevice(i.serial);let u=i.serial||this.driver.getDefaultDevice()||"Default Device",m=null;i.serial&&(m=yt(i.serial)),i.embedScreenshots&&typeof this.reporter.setEmbedScreenshots=="function"&&this.reporter.setEmbedScreenshots(!0);let y,v;if(i.serve){let S=typeof i.serve=="number"?i.serve:4040;try{y=await Gi({port:S,suiteName:p}),console.log(V.bold.magenta(`
878
- \u{1F4E1} Live Monitor streaming at: ${y.url}
879
- `)),typeof this.reporter.onStep=="function"&&(v=this.reporter.onStep(b=>{y?.broadcastStep(b)}))}catch(b){let E=b;console.warn(V.yellow(`Warning: Could not start live server: ${E.message}`))}}let T=null;i.video&&(console.log(V.cyan("\u{1F3AC} Starting ADB screen recording (--video enabled)...")),T=await this.driver.startScreenRecording("/sdcard/prompttest_temp.mp4",i.serial));try{this.screenDimensions=await this.driver.getScreenDimensions(i.serial),this.navBarHeight=typeof this.driver.getNavigationBarHeight=="function"?await this.driver.getNavigationBarHeight(i.serial):0,console.log(V.gray("Target Screen Geometry: ")+V.bold.cyan(`${this.screenDimensions.width}x${this.screenDimensions.height}`)+(this.navBarHeight>0?V.gray(` (Nav Bar: ${this.navBarHeight}px)`):"")),i.apkPath&&(Ye.existsSync(i.apkPath)?(console.log(V.cyan(`\u{1F4E6} Installing APK: ${i.apkPath}...`)),await this.driver.installApk(i.apkPath,i.serial),await new Promise(M=>setTimeout(M,2e3))):console.log(V.yellow(`Warning: Specified APK file not found at: ${i.apkPath}`)));let S=i.packageName||h||(typeof this.driver.getForegroundPackage=="function"?await this.driver.getForegroundPackage(i.serial):null)||"Foreground App";if(S&&!S.includes("Launcher")&&!S.includes("Foreground")){if(!(typeof this.driver.isAppInstalled=="function"?await this.driver.isAppInstalled(S,i.serial):!0))if(i.apkPath&&Ye.existsSync(i.apkPath))console.log(V.cyan(`\u{1F4E6} Target app not found. Auto-installing APK: ${i.apkPath}...`)),await this.driver.installApk(i.apkPath,i.serial),await new Promise(Y=>setTimeout(Y,2500));else throw new Error(`Target application "${S}" is not installed on device (${u}).
880
- \u{1F4A1} Suggestions:
881
- \u2022 Install the app manually or via: adb install <path-to-apk>
882
- \u2022 Or provide an APK path to auto-install before running: --apk=path/to/app.apk`);let J=await this.driver.getForegroundPackage(i.serial);(i.fresh||J&&J!==S)&&(console.log(V.cyan(`\u{1F680} Launching target application: ${V.bold(S)}...`)),i.fresh&&(await this.driver.forceStop(S,i.serial),await new Promise(Y=>setTimeout(Y,600))),await this.driver.launchApp(S,i.serial),await new Promise(Y=>setTimeout(Y,2500)))}i.reporter&&(this.reporter=i.reporter),i.reporters&&typeof this.reporter.setFormats=="function"&&this.reporter.setFormats(i.reporters),i.serial&&await Qi(this.driver,i.serial).catch(()=>{}),this.targetPackage=S&&!S.includes("Foreground")&&!S.includes("Launcher")?S:void 0,this.reporter.init(S,u),await this.memory.init(S),this.lastHierarchy=null;let b=Date.now(),E=0;for(let M=0;M<l.length;M++){let J=l[M],Y=l.length>1,K=i.onlyRow?`Row ${i.onlyRow}`:Y?`Row ${M+1}`:void 0,le=K?`${p} [${K}]`:p;Y&&(console.log(V.bold.magenta(`
883
- ======================================================
884
- \u{1F504} [Data-Driven Iteration ${M+1}/${l.length}] Row Data: ${JSON.stringify(J)}
885
- ======================================================`)),i.fresh&&M>0&&S&&(await this.driver.clearAppData(S,i.serial),await new Promise(f=>setTimeout(f,1e3)))),this.runtimeVariables.clear();for(let[f,I]of Object.entries(J))this.runtimeVariables.set(f,I);this.loopStack=[];let Z=this.parsePrompt(n,J),_=1,De=i.resume&&M===0?await this.checkpoints.getResumeIndex(p):0;De>0&&console.log(V.cyan(`\u23ED Resuming from step ${De+1} (checkpoint found).`));let w=i.only?Mi(i.only):null;for(let f=0;f<Z.length;f++){let I=Z[f];if(_=f+1,w&&!w.has(_)){console.log(V.gray(`[Step ${_}/${Z.length}] \u23E9 Skipped (not in --only range: ${i.only})`)),this.reporter.logStep(le,I.originalText,I.type,"SKIPPED",`Filtered out by --only="${i.only}"`,void 0,{videoOffsetMs:Math.max(0,Date.now()-b)},0);continue}if(f<De){this.reporter.logStep(le,I.originalText,I.type,"SKIPPED","Resumed \u2014 already passed",void 0,{videoOffsetMs:0},0);continue}let N=Z.slice(f+1);console.log(V.bold.blue(`
886
- [Step ${_}/${Z.length}] ${I.originalText}`));let de=Date.now();try{let he=await this.executeStep(I,i.serial,N),g=Date.now()-de;if(he&&he.fastForwarded){if(console.log(V.cyan(`\u23E9 Fast-Forward: ${he.reason}. Redundant step(s) bypassed.`)),this.reporter.logStep(le,I.originalText,I.type,"PASS",`Fast-Forward: ${he.reason}`,void 0,{videoOffsetMs:Math.max(0,de-b)},g),console.log(V.green(`\u2714 Step ${_} PASSED (Fast-Forward)`)),he?.skipCount&&he.skipCount>0){let P=he.skipCount;for(let O=0;O<P&&f+1<Z.length;O++){f++;let x=Z[f],$=f+1;this.reporter.logStep(le,x.originalText,x.type,"PASS",`Fast-Forward: ${he.reason}`,void 0,{videoOffsetMs:Math.max(0,de-b)},0),console.log(V.gray(` [Step ${$}/${Z.length}] \u23E9 Fast-Forward: ${x.originalText}`))}}}else{let P;if((i.screenshotMode==="all"||i.saveBaseline||i.compareBaseline)&&(P=await this.driver.captureScreenshot(i.serial)),P&&i.compareBaseline){let O=await this.baselines.compare(le,_,P,i.baselineThreshold);O.passed||(console.log(V.yellow(` \u26A0\uFE0F [Baseline] Step ${_} visual diff ${(O.diffRatio*100).toFixed(1)}% exceeds threshold ${(O.threshold*100).toFixed(1)}%`)),this.reporter.logError(`Visual baseline diff at Step ${_}: ${(O.diffRatio*100).toFixed(1)}% changed`))}P&&i.saveBaseline&&!i.compareBaseline&&await this.baselines.saveBaseline(le,_,P),this.reporter.logStep(le,I.originalText,I.type,"PASS",I.target||I.value,i.screenshotMode==="all"?P:void 0,{videoOffsetMs:Math.max(0,de-b)},g),console.log(V.green(`\u2714 Step ${_} PASSED`)),i.benchmark&&console.log(V.gray(` \u23F1 [Benchmark] Step ${_} latency: ${g}ms (${I.type}${I.target?` -> "${I.target}"`:""})`))}i.resume&&!Y&&await this.checkpoints.save(p,S,f,Z.length)}catch(he){let g=he,P=Date.now()-de;if(I.optional){console.log(V.yellow(`\u26AA Step ${_} OPTIONAL (Skipped: ${g.message})`)),this.reporter.logStep(le,I.originalText,I.type,"SKIPPED",`Optional step bypassed: ${g.message}`,void 0,{error:g.message,videoOffsetMs:Math.max(0,de-b)},P);continue}E++,console.log(V.red(`\u274C Step ${_} FAILED: ${g.message}`)),I.target&&this.memory.recordFailure(I.type,I.target);let O=await this.driver.captureScreenshot(i.serial).catch(()=>{}),x;try{let $=Ie.resolve(process.cwd(),"output"),A=`${p}_step_${_}`,H=Ie.join($,"failures",A);Ye.existsSync(H)||Ye.mkdirSync(H,{recursive:!0}),O&&await Ye.promises.writeFile(Ie.join(H,"screenshot.png"),O);let R=this.lastHierarchy?.xml||await this.driver.getUiHierarchy(i.serial).catch(()=>"");R&&await Ye.promises.writeFile(Ie.join(H,"hierarchy.xml"),R,"utf-8");let W=await this.driver.getLogcatTail(150,i.serial);await Ye.promises.writeFile(Ie.join(H,"logcat_tail.log"),W,"utf-8");let X=this.lastHierarchy?.flat||[],te=I.target&&X.length>0?bt(I.target,X.map(Se=>Se.text||Se.contentDesc||"").filter(Boolean)):[];await Ye.promises.writeFile(Ie.join(H,"match_scores.json"),JSON.stringify(te,null,2),"utf-8");let q={specName:le,stepNumber:_,stepType:I.type,target:I.target||I.value,error:g.message,timestamp:new Date().toISOString(),videoOffsetMs:Math.max(0,de-b)};await Ye.promises.writeFile(Ie.join(H,"manifest.json"),JSON.stringify(q,null,2),"utf-8");let ne=[{name:"manifest.json",data:Buffer.from(JSON.stringify(q,null,2),"utf-8")},{name:"logcat_tail.log",data:Buffer.from(W,"utf-8")},{name:"match_scores.json",data:Buffer.from(JSON.stringify(te,null,2),"utf-8")}];R&&ne.push({name:"hierarchy.xml",data:Buffer.from(R,"utf-8")}),O&&ne.push({name:"screenshot.png",data:O});let Q=`${A}.zip`,Ae=Ie.join($,"failures",Q);await Ki(Ae,ne),x=Ie.join("failures",Q).replace(/\\/g,"/"),console.log(V.red(` \u{1F4C1} Failure Triage ZIP Bundle generated at: output/${x}`))}catch{}if(this.reporter.logStep(le,I.originalText,I.type,"FAIL",g.message,O,{error:g.message,videoOffsetMs:Math.max(0,de-b)},P,x),this.reporter.logError(`Step ${_} failed: ${g.message}`),i.continueOnError)console.log(V.yellow("Continuing with next test step (--continue enabled)..."));else{let $=Z.slice(f+1);if($.length>0){console.log(V.yellow(`
887
- \u{1F6D1} Aborting dependent flow due to upstream failure at Step ${_}.`)),console.log(V.gray(`Cascading skip applied to ${$.length} downstream step(s):`));for(let A=0;A<$.length;A++){let H=$[A],R=_+1+A;this.reporter.logStep(le,H.originalText,H.type,"SKIPPED",`Cascading skip: Precondition failed at Step ${_} (${I.originalText})`,void 0,{videoOffsetMs:Math.max(0,Date.now()-b)},0),console.log(V.gray(` [Step ${R}/${Z.length}] \u26AA SKIPPED: ${H.originalText}`))}}break}}}}try{let M=this.lastHierarchy;if(M?.flat){let J=Ei(M.flat,this.screenDimensions.width,this.screenDimensions.height);if(J.length>0){console.log(V.bold.yellow(`
888
- \u{1F50D} Visual QA Sentinel Warnings (${J.length} detected):`));for(let Y of J)console.log(V.yellow(` \u2022 [${Y.type}] ${Y.description}`)),this.reporter.logError(`Visual Defect (${Y.type}): ${Y.description}`)}}}catch{}await this.memory.save(),i.resume&&E===0&&await this.checkpoints.clear(p);let D=this.memory.getStats();if(D.totalMappings>0&&console.log(V.cyan(`\u{1F9E0} Knowledge Engine: ${D.totalMappings} active learned mapping(s) for ${D.package}`)),T){console.log(V.cyan("\u{1F3AC} Finalizing video recording..."));let M=Ie.resolve(process.cwd(),"output"),J=Ie.join(M,`${p}-recording.mp4`),Y=await this.driver.stopScreenRecording(T,J,i.serial);Y&&Ye.existsSync(Y)&&(this.reporter.setVideoPath?.(`${p}-recording.mp4`),console.log(V.green(`\u2714 Screen recording saved to: output/${p}-recording.mp4`))),T=null}let B=((Date.now()-b)/1e3).toFixed(1),k=await this.reporter.generateReport(p);y&&(y.broadcastComplete(k),v&&v()),console.log(V.bold.green(`
889
- \u2728 PromptTest Script Execution Finished in ${B}s!`));let j=k.failedSteps===0?V.bold.green:V.bold.yellow;return console.log(j(`Result: ${k.passedSteps}/${k.totalSteps} passed (${k.failedSteps} failed, ${k.skippedSteps} skipped).`)),console.log(V.cyan(`Report saved to output/${p}-report.html
890
- `)),i.json&&console.log(JSON.stringify(k,null,2)),k}finally{if(T)try{await this.driver.stopScreenRecording(T,Ie.join("output",`${p}-recording.mp4`),i.serial)}catch{}m&&St(m)}}async getCleanHierarchy(e){let t=await this.driver.getUiHierarchy(e),{flat:s}=we(t),i=s.find(o=>(o.text==="Dismiss"||o.contentDesc==="Dismiss")&&o.bounds.width>0);i&&(await this.driver.tap(i.bounds.centerX,i.bounds.centerY,e),await new Promise(o=>setTimeout(o,400)),t=await this.driver.getUiHierarchy(e),s=we(t).flat);let r=s.find(o=>o.contentDesc&&(o.contentDesc.startsWith("!, ")||o.contentDesc.includes("react.dev/link")));if(r){let o=r.bounds.x2-45,c=r.bounds.centerY;await this.driver.tap(o,c,e),await new Promise(l=>setTimeout(l,350)),t=await this.driver.getUiHierarchy(e),s=we(t).flat}let n=be(s);return this.lastHierarchy={xml:t,flat:s,timestamp:Date.now(),hash:n},{xml:t,flat:s}}async ensureInViewport(e,t){let s=this.screenDimensions.width,i=this.screenDimensions.height,r=Math.round(s*.5),n=this.navBarHeight>0?i-this.navBarHeight:i,o=this.navBarHeight>0?n*.94:i*C.gestures.bottomNavThresholdRatio;if(e.bounds.centerY>o||e.bounds.y1>o)return!1;let l=e.bounds.y2>n,p=e.bounds.y1<Math.round(i*.04)&&e.bounds.centerY<Math.round(i*.06);return l?(await this.driver.swipe(r,Math.round(i*.75),r,Math.round(i*.45),250,t),await new Promise(d=>setTimeout(d,200)),this.lastHierarchy=null,!0):p?(await this.driver.swipe(r,Math.round(i*.35),r,Math.round(i*.65),250,t),await new Promise(d=>setTimeout(d,200)),this.lastHierarchy=null,!0):!1}async waitForTarget(e,t,s=3e3,i){if(this.lastHierarchy&&Date.now()-this.lastHierarchy.timestamp<C.polling.hierarchyCacheTtlMs){let c=this.matchNodes(this.lastHierarchy.flat,e,i);if(c.length>0)return{flat:this.lastHierarchy.flat,matched:c}}let r=Date.now();for(;Date.now()-r<s;){let{flat:c}=await this.getCleanHierarchy(t),l=this.matchNodes(c,e,i);if(l.length>0)return{flat:c,matched:l};await new Promise(p=>setTimeout(p,ui()))}let{flat:n}=await this.getCleanHierarchy(t),o=this.matchNodes(n,e,i);if(o.length===0&&(this.healEnabled||C.ai.selfHealing))try{let c=await this.semanticResolver.findSuggestions(e,n,{locale:this.activeLocale}),l=C.ai.confidenceThreshold||.75,p=c.find(d=>d.score>=l);if(p){let d=p.node||n.find(h=>(pe(p.text,h.text)||pe(p.text,h.contentDesc))&&h.bounds.width>0);if(d){console.log(V.yellow(` \u{1F4A1} [Self-Healing] Auto-healed "${e}" \u2192 "${p.text}" (${Math.round(p.score*100)}% confidence, ${p.strategy})`)),i&&(i.healedFrom=e);let h=d.text?"text":d.contentDesc?"contentDesc":"resourceId",u=this.lastHierarchy?.hash,m=Math.min(C.memory?.maxHealedConfidence??.65,p.score);this.memory.recordSuccess(i?.type||"TAP",e,p.text,h,m,e,u),o=[d]}}}catch(c){Ge.warn(`Self-healing resolution failed: ${c.message}`)}return{flat:n,matched:o}}buildDiagnosticError(e,t,s){return Ni(e,t,s)}async executeStep(e,t,s){let i=Hi.get(e.type);if(!i)throw new Error(`No handler registered for step type: ${e.type}`);e.target&&e.target.includes("$")&&(e.target=ft(e.target,Object.fromEntries(this.runtimeVariables))),e.value&&e.value.includes("$")&&(e.value=ft(e.value,Object.fromEntries(this.runtimeVariables))),(e.type==="LAUNCH_APP"||e.type==="RESTART_APP")&&e.target&&(this.targetPackage=e.target);let r={targetPackage:this.targetPackage,driver:this.driver,memory:this.memory,serial:t,screenDimensions:this.screenDimensions,navBarHeight:this.navBarHeight,lastHierarchy:this.lastHierarchy,setLastHierarchy:n=>{this.lastHierarchy=n},getCleanHierarchy:n=>this.getCleanHierarchy(n),waitForTarget:(n,o,c,l)=>this.waitForTarget(n,o,c,l),matchNodes:(n,o,c)=>this.matchNodes(n,o,c),ensureInViewport:(n,o)=>this.ensureInViewport(n,o),buildDiagnosticError:(n,o,c)=>this.buildDiagnosticError(n,o,c),findTargetInputField:(n,o,c)=>this.findTargetInputField(n,o,c),upcomingSteps:s,variables:this.runtimeVariables,loopStack:this.loopStack,executeChildSteps:async(n,o)=>{for(let c=0;c<n.length;c++){let l=n[c];await this.executeStep(l,o.serial,n.slice(c+1))}}};return i.execute(e,r)}findTargetInputField(e,t,s){return Li(e,t,s,this.memory)}matchNodes(e,t,s){return Qt(e,t,this.memory,this.activeLocale,s,this.lastHierarchy?.hash)}},Zi=new Ke;import qi from"fs";import or from"path";var Wt=null;function ar(a){return JSON.parse(JSON.stringify(a))}function en(a,e){for(let t of Object.keys(e)){let s=e[t],i=a[t];s&&typeof s=="object"&&!Array.isArray(s)&&i&&typeof i=="object"?en(i,s):s!==void 0&&(a[t]=s)}return a}function cr(a){let e=ar(C),t=a||or.resolve(process.cwd(),"prompttest.config.json");if(qi.existsSync(t))try{let b=qi.readFileSync(t,"utf-8"),E=JSON.parse(b);typeof E=="object"&&E!==null&&en(e,E)}catch{}let s=process.env.PROMPTTEST_ADB_COMMAND_TIMEOUT_MS||process.env.PROMPTTEST_COMMAND_TIMEOUT_MS;if(s){let b=parseInt(s,10);isNaN(b)||(e.adb.commandTimeoutMs=b)}let i=process.env.PROMPTTEST_POLLING_INTERVAL_MS||process.env.PROMPTTEST_POLL_INTERVAL_MS;if(i){let b=parseInt(i,10);isNaN(b)||(e.polling.intervalMs=b)}let r=process.env.PROMPTTEST_POLLING_DEFAULT_TIMEOUT_MS||process.env.PROMPTTEST_DEFAULT_TIMEOUT_MS;if(r){let b=parseInt(r,10);isNaN(b)||(e.polling.defaultTimeoutMs=b)}let n=process.env.PROMPTTEST_SCREEN_FALLBACK_WIDTH||process.env.PROMPTTEST_FALLBACK_WIDTH;if(n){let b=parseInt(n,10);isNaN(b)||(e.screen.fallbackWidth=b)}let o=process.env.PROMPTTEST_SCREEN_FALLBACK_HEIGHT||process.env.PROMPTTEST_FALLBACK_HEIGHT;if(o){let b=parseInt(o,10);isNaN(b)||(e.screen.fallbackHeight=b)}let c=process.env.PROMPTTEST_VISUAL_OVERLAP_RATIO||process.env.PROMPTTEST_OVERLAP_RATIO;if(c){let b=parseFloat(c);isNaN(b)||(e.visual.overlapRatio=b)}let l=process.env.PROMPTTEST_DEVICE_MAX_DEVICE_RETRIES||process.env.PROMPTTEST_MAX_DEVICE_RETRIES;if(l){let b=parseInt(l,10);isNaN(b)||(e.device.maxDeviceRetries=b)}let p=process.env.PROMPTTEST_DEVICE_AUTO_RECONNECT??process.env.PROMPTTEST_AUTO_RECONNECT;p!==void 0&&(e.device.autoReconnect=p.toLowerCase()!=="false");let d=process.env.PROMPTTEST_GESTURES_SWIPE_DURATION_MS||process.env.PROMPTTEST_SWIPE_DURATION_MS;if(d){let b=parseInt(d,10);isNaN(b)||(e.gestures.swipeDurationMs=b)}let h=process.env.PROMPTTEST_FLOW_MAX_LOOP_ITERATIONS||process.env.PROMPTTEST_MAX_LOOP_ITERATIONS;if(h){let b=parseInt(h,10);isNaN(b)||(e.flow.maxLoopIterations=b)}let u=process.env.PROMPTTEST_AI_PROVIDER;u&&["heuristic","openai","anthropic","ollama","mock"].includes(u)&&(e.ai.provider=u);let m=process.env.PROMPTTEST_AI_MODEL;m&&(e.ai.model=m);let y=process.env.PROMPTTEST_SELF_HEALING;y!==void 0&&(e.ai.selfHealing=y.toLowerCase()!=="false");let v=process.env.PROMPTTEST_CONFIDENCE_THRESHOLD;if(v){let b=parseFloat(v);isNaN(b)||(e.ai.confidenceThreshold=b)}let T=process.env.PROMPTTEST_AI_ENDPOINT;T&&(e.ai.endpoint=T);let S=process.env.PROMPTTEST_LOCALE;return S&&(e.locale=S.toLowerCase()),Wt=e,e}function vl(){return Wt||(Wt=cr()),Wt}function xl(){Wt=null}import Fe from"chalk";function tn(a,e){if(a.length===0)return 0;let t=[...a].sort((i,r)=>i-r),s=Math.min(t.length-1,Math.max(0,Math.floor(e/100*(t.length-1))));return Math.round(t[s]*100)/100}function si(a){if(a.length===0)return{minMs:0,maxMs:0,avgMs:0,p50Ms:0,p95Ms:0,iterations:0,durations:[]};let e=Math.round(Math.min(...a)*100)/100,t=Math.round(Math.max(...a)*100)/100,s=a.reduce((o,c)=>o+c,0),i=Math.round(s/a.length*100)/100,r=tn(a,50),n=tn(a,95);return{minMs:e,maxMs:t,avgMs:i,p50Ms:r,p95Ms:n,iterations:a.length,durations:a}}function lr(a=1e3){let e=[];e.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}}),e.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 t=["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 i=2;i<a;i++){let r=t[i%t.length],n=r.includes("Button")||r.includes("CheckBox")||r.includes("Switch"),o=s[i%s.length],c=`${o} #${i}`,l=Math.min(2400,250+i*20%2e3),p=50+i*30%900;e.push({index:i,className:r,packageName:"com.app",text:r.includes("ImageView")?"":c,resourceId:`com.app:id/element_${i}`,contentDesc:r.includes("ImageView")?`Icon of ${o}`:"",clickable:n,enabled:i%25!==0,focused:!1,scrollable:!1,password:o.toLowerCase().includes("password"),selected:!1,children:[],bounds:{x1:p,y1:l,x2:p+200,y2:l+80,width:200,height:80,centerX:p+100,centerY:l+40}})}return e}async function $l(a=ke,e={}){let t=e.iterations||5,s=Date.now(),i=e.serial,r="Synthetic Mock Android Environment",n=!1;if(!e.forceMock)try{i=await a.ensureDefaultDevice(e.serial),i&&(r=await a.getDeviceModel(i),n=!0)}catch{n=!1}let o=[];for(let m=0;m<t;m++){let y=performance.now();n?await a.captureScreenshot(i):(await new Promise(v=>setTimeout(v,15)),Buffer.alloc(1024*1024)),o.push(performance.now()-y)}let c=[];for(let m=0;m<t;m++){let y=performance.now();n?await a.getUiHierarchy(i):await new Promise(v=>setTimeout(v,25)),c.push(performance.now()-y)}let l=lr(1e3),p=new rt,d=["Submit","Email Address","Mathematics","Save Changes","NonExistent Query Fallback"],h=[];for(let m=0;m<t;m++){let y=d[m%d.length],v=performance.now();Qt(l,y,p),h.push(performance.now()-v)}let u=Date.now()-s;return{timestamp:new Date().toISOString(),device:r,package:e.packageName,screencap:si(o),uiHierarchy:si(c),nodeMatching:si(h),totalDurationMs:u}}function Cl(a){let e=(r,n)=>{let o=`${n.minMs.toFixed(1)}ms`.padStart(9),c=`${n.avgMs.toFixed(1)}ms`.padStart(9),l=`${n.p50Ms.toFixed(1)}ms`.padStart(9),p=`${n.p95Ms.toFixed(1)}ms`.padStart(9),d=`${n.maxMs.toFixed(1)}ms`.padStart(9);return`\u2502 ${r.padEnd(28)} \u2502 ${o} \u2502 ${c} \u2502 ${l} \u2502 ${p} \u2502 ${d} \u2502`};return["",Fe.bold.cyan("\u26A1 PromptTest Performance Diagnostic Benchmark"),Fe.gray("Device: ")+Fe.bold.white(a.device)+Fe.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 ${Fe.bold("Operation".padEnd(28))} \u2502 ${Fe.bold("Min".padStart(9))} \u2502 ${Fe.bold("Avg".padStart(9))} \u2502 ${Fe.bold("p50".padStart(9))} \u2502 ${Fe.bold("p95".padStart(9))} \u2502 ${Fe.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",e("Device Screencap",a.screencap),e("UI Hierarchy Dump (XML)",a.uiHierarchy),e("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",Fe.gray("Total Benchmark Wall-Time: ")+Fe.bold.green(`${a.totalDurationMs}ms
891
- `)].join(`
892
- `)}import dr from"readline";import Rt from"fs";import sn from"path";import ce from"chalk";async function nn(a=ke,e,t={}){let s=await a.ensureDefaultDevice(t.serial),i=e||new Ke(a),r={history:[],successfulSteps:[]},n=t.input||process.stdin,o=t.output||process.stdout,c=dr.createInterface({input:n,output:o,prompt:ce.bold.cyan("prompttest> ")}),l=!1,p=()=>{if(!l)try{c.prompt()}catch{}},d=h=>{o.write(h+`
893
- `)};return d(ce.bold.cyan(`
894
- \u26A1 PromptTest Interactive Live REPL / Playground (v1.1.0)`)),d(ce.gray("Device: ")+ce.bold.white(s||"No device connected (offline mock)")+(t.packageName?ce.gray(" | Package: ")+ce.bold.white(t.packageName):"")),d(ce.gray(`Type natural language instructions (e.g. Tap 'Sign In'), or REPL commands: "inspect", "screenshot", "history", "save <file.txt>", "exit".
895
- `)),new Promise(h=>{p(),c.on("line",async u=>{let m=u.trim();if(!m){p();return}r.history.push(m);let y=m.toLowerCase();if(y==="exit"||y==="quit"||y===":q"){l=!0,d(ce.yellow("Exiting PromptTest REPL.")),c.close();return}if(y==="help"||y==="?"){d(ce.bold.white(`
896
- \u{1F4D6} PromptTest REPL Commands:`)),d(` \u2022 ${ce.cyan("Tap '...'")} / ${ce.cyan("Type '...' into '...'")} : Execute action live on device`),d(` \u2022 ${ce.cyan("Verify '...' is visible")} : Assert element visibility`),d(` \u2022 ${ce.cyan("inspect")} / ${ce.cyan("tree")} : List interactive elements and input fields on screen`),d(` \u2022 ${ce.cyan("screenshot [path]")} : Capture current screen to image file`),d(` \u2022 ${ce.cyan("history")} : Show executed commands in current session`),d(` \u2022 ${ce.cyan("save <file.txt>")} : Save successful steps into a reusable spec file`),d(` \u2022 ${ce.cyan("clear")} : Clear session history`),d(` \u2022 ${ce.cyan("exit")} : Quit the REPL
897
- `),p();return}if(y==="history"){d(ce.bold.white(`
898
- \u{1F4DC} Session History (${r.history.length} commands):`)),r.history.forEach((v,T)=>{d(` ${T+1}. ${v}`)}),d(""),p();return}if(y==="clear"){r.successfulSteps=[],r.history=[],d(ce.green("Session history cleared.")),p();return}if(y.startsWith("save")){let T=m.split(/\s+/)[1]||"output/repl_spec.txt";try{let S=sn.dirname(T);S&&!Rt.existsSync(S)&&Rt.mkdirSync(S,{recursive:!0});let b=r.successfulSteps.map((E,D)=>`${D+1}. ${E}`).join(`
899
- `);Rt.writeFileSync(T,b,"utf-8"),d(ce.bold.green(`\u2714 Saved ${r.successfulSteps.length} step(s) to ${T}`))}catch(S){let b=S;d(ce.red(`Failed to save spec: ${b.message}`))}p();return}if(y.startsWith("screenshot")){let T=m.split(/\s+/)[1]||`output/repl_screenshot_${Date.now()}.png`;try{let S=sn.dirname(T);S&&!Rt.existsSync(S)&&Rt.mkdirSync(S,{recursive:!0});let b=await a.captureScreenshot(s);Rt.writeFileSync(T,b),d(ce.bold.green(`\u2714 Screenshot saved to ${T}`))}catch(S){let b=S;d(ce.red(`Failed to capture screenshot: ${b.message}`))}p();return}if(y==="inspect"||y==="tree"){try{d(ce.gray("Dumping UI hierarchy..."));let v=await a.getUiHierarchy(s),{flat:T}=we(v),S=T.filter(b=>b.clickable||b.className.includes("Button")||b.className.includes("EditText"));d(ce.bold.white(`
900
- \u{1F50D} Visible Interactive Elements (${S.length} found):`)),S.slice(0,25).forEach((b,E)=>{let D=b.text||b.contentDesc||b.resourceId||`<Unnamed ${b.className}>`,B=b.className.split(".").pop()||b.className;d(` [${E+1}] (${B}) "${D}" at (${b.bounds.centerX}, ${b.bounds.centerY})`)}),S.length>25&&d(ce.gray(` ... and ${S.length-25} more elements.`)),d("")}catch(v){let T=v;d(ce.red(`Failed to inspect hierarchy: ${T.message}`))}p();return}try{let v=Nt(m);if(v.length===0){d(ce.yellow(`Could not parse natural language step: "${m}"`)),p();return}let T=v[0],S=performance.now();await i.executeStep(T,s);let b=Math.round(performance.now()-S);r.successfulSteps.push(m),d(ce.green(`\u2714 [${T.type}] Completed in ${b}ms: `)+ce.white(T.originalText))}catch(v){let T=v;d(ce.red(`\u2716 Step failed: ${T.message}`))}p()}),c.on("close",()=>{l=!0,h(r)})})}import{execFile as Tt}from"child_process";var rn=class{defaultUdid;wdaBaseUrl;isMock;defaultDimensions={width:1170,height:2532};constructor(e={}){typeof e=="string"?(this.defaultUdid=e,this.isMock=!1):(this.defaultUdid=e.udid,this.wdaBaseUrl=e.wdaBaseUrl,this.isMock=e.useMock??!1),process.platform!=="darwin"&&!this.wdaBaseUrl&&this.isMock===void 0&&(this.isMock=!0)}getDefaultDevice(){return this.defaultUdid}async ensureDefaultDevice(e){if(e)return e;if(this.defaultUdid)return this.defaultUdid;let s=(await this.getConnectedDevices()).find(r=>r.state==="simulator"||r.state==="device");if(s)return this.defaultUdid=s.serial,s.serial;let i="booted-simulator";return this.defaultUdid=i,i}async getConnectedDevices(){return this.isMock||process.platform!=="darwin"?[{serial:this.defaultUdid||"00008101-001234567890001E",state:"simulator",model:"iPhone 15 Pro (Simulator)",platform:"ios"}]:new Promise(e=>{Tt("xcrun",["simctl","list","devices","available","--json"],(t,s)=>{if(t||!s){e([{serial:"booted-ios-simulator",state:"simulator",model:"iPhone Simulator",platform:"ios"}]);return}try{let i=JSON.parse(s),r=[],n=i.devices||{};for(let o of Object.keys(n)){let c=n[o];if(Array.isArray(c))for(let l of c)l.isAvailable&&l.state==="Booted"&&r.push({serial:l.udid,state:"simulator",model:`${l.name} (${o.split(".").pop()||"iOS"})`,platform:"ios"})}e(r.length>0?r:[{serial:"booted-ios-simulator",state:"simulator",model:"iPhone Simulator",platform:"ios"}])}catch{e([])}})})}async getScreenDimensions(e){return this.isMock||process.platform!=="darwin"?this.defaultDimensions:this.defaultDimensions}async captureScreenshot(e){let t=await this.ensureDefaultDevice(e);return this.isMock||process.platform!=="darwin"?Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==","base64"):new Promise((s,i)=>{Tt("xcrun",["simctl","io",t,"screenshot","--type=png","-"],{encoding:"buffer",maxBuffer:10*1024*1024},(r,n)=>{if(r)return i(r);s(n)})})}async getUiHierarchy(e){let t=`<?xml version="1.0" encoding="UTF-8"?>
857
+ {"bestMatch": "Exact Text of Best Element", "confidence": 0.85, "reason": "Reason for match"}`,i=await this.callLlm(n);if(!i)return this.heuristicFallback.findSuggestions(e,t,s);let o=i.match(/\{[\s\S]*\}/);if(!o)return this.heuristicFallback.findSuggestions(e,t,s);let l=JSON.parse(o[0]);if(l.bestMatch){let c=t.find(p=>p.text===l.bestMatch||p.contentDesc===l.bestMatch);return[{text:l.bestMatch,score:typeof l.confidence=="number"?l.confidence:.85,node:c,strategy:"llm",reason:l.reason||"LLM semantic match"},...await this.heuristicFallback.findSuggestions(e,t,s)]}}catch(r){qe.warn(`LLM self-healing failed (${r.message}), using heuristic suggestions.`)}return this.heuristicFallback.findSuggestions(e,t,s)}async callLlm(e){let t=this.config.timeoutMs||8e3,s=new AbortController,r=setTimeout(()=>s.abort(),t);try{if(this.config.provider==="openai"){let n=this.config.apiKey||process.env.OPENAI_API_KEY;if(!n)throw new Error("OPENAI_API_KEY is not set");let i=await fetch("https://api.openai.com/v1/chat/completions",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:JSON.stringify({model:this.config.model||"gpt-4o-mini",messages:[{role:"user",content:e}],temperature:.1}),signal:s.signal});if(!i.ok)throw new Error(`OpenAI HTTP ${i.status}: ${await i.text()}`);return(await i.json()).choices?.[0]?.message?.content||null}if(this.config.provider==="anthropic"){let n=this.config.apiKey||process.env.ANTHROPIC_API_KEY;if(!n)throw new Error("ANTHROPIC_API_KEY is not set");let i=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:{"Content-Type":"application/json","x-api-key":n,"anthropic-version":"2023-06-01"},body:JSON.stringify({model:this.config.model||"claude-3-haiku-20240307",max_tokens:1024,messages:[{role:"user",content:e}]}),signal:s.signal});if(!i.ok)throw new Error(`Anthropic HTTP ${i.status}: ${await i.text()}`);return(await i.json()).content?.[0]?.text||null}if(this.config.provider==="ollama"){let n=this.config.endpoint||process.env.OLLAMA_BASE_URL||"http://localhost:11434",i=await fetch(`${n.replace(/\/$/,"")}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:this.config.model||"llama3.2",messages:[{role:"user",content:e}],stream:!1}),signal:s.signal});if(!i.ok)throw new Error(`Ollama HTTP ${i.status}: ${await i.text()}`);return(await i.json()).message?.content||null}return null}finally{clearTimeout(r)}}};Fe();function lr(a){let e=a?.provider||I.ai.provider;if(e==="heuristic")return new $t;if(e==="mock")return new Bt;let t={provider:e,model:a?.model||I.ai.model,endpoint:a?.endpoint||I.ai.endpoint};return new as(t)}import oi from"fs";import dr from"path";var Ut=class{static extractDataDirective(e,t=process.cwd()){let s=e.split(`
858
+ `),r="",n=-1;for(let p=0;p<s.length;p++){let d=s[p].trim();if(d.startsWith("#!data")||d.startsWith("#! data")){r=d,n=p;break}}if(n===-1)return null;let i=r.replace(/^#!\s*data\s*/,"").trim(),o=[],l="inline";if(i.startsWith("[")||i.startsWith("{"))try{let p=JSON.parse(i);o=Array.isArray(p)?p.map(d=>this.stringifyObject(d)):[this.stringifyObject(p)]}catch(p){throw new Error(`Failed to parse inline #!data JSON directive: ${p.message}`)}else{let p=i.replace(/^['"]+|['"]+$/g,"").trim(),d=dr.isAbsolute(p)?p:dr.resolve(t,p);l=d,o=this.loadFromFile(d)}let c=[...s];return c.splice(n,1),{directive:r,source:l,cleanedSpec:c.join(`
859
+ `),rows:o}}static loadFromFile(e){if(!oi.existsSync(e))throw new Error(`Data file not found: ${e}`);let t=oi.readFileSync(e,"utf-8");return dr.extname(e).toLowerCase()===".json"?this.parseJson(t):this.parseCsv(t)}static parseJson(e){try{let t=JSON.parse(e);return Array.isArray(t)?t.map(s=>this.stringifyObject(s)):[this.stringifyObject(t)]}catch(t){throw new Error(`Invalid JSON data file format: ${t.message}`)}}static parseCsv(e){let t=this.splitCsvLines(e);if(t.length<2)return[];let s=this.parseCsvRow(t[0]).map(n=>n.trim().replace(/^['"]+|['"]+$/g,"")),r=[];for(let n=1;n<t.length;n++){if(!t[n].trim())continue;let o=this.parseCsvRow(t[n]),l={};for(let c=0;c<s.length;c++){let p=s[c];l[p]=o[c]!==void 0?o[c].trim():""}r.push(l)}return r}static filterRows(e,t){if(!t)return e;let s=[...e];if(t.onlyRow!==void 0&&t.onlyRow>0){let r=t.onlyRow-1;if(r<s.length)return[s[r]];throw new Error(`Requested --only-row=${t.onlyRow}, but dataset only contains ${s.length} row(s).`)}if(t.iterations!==void 0&&t.iterations>0){if(t.iterations<=s.length)return s.slice(0,t.iterations);let r=[];for(let n=0;n<t.iterations;n++)r.push(s[n%s.length]);return r}return s}static parseCsvRow(e){let t=[],s="",r=!1;for(let n=0;n<e.length;n++){let i=e[n];i==='"'?r&&e[n+1]==='"'?(s+='"',n++):r=!r:i===","&&!r?(t.push(s),s=""):s+=i}return t.push(s),t}static splitCsvLines(e){let t=[],s="",r=!1;for(let n=0;n<e.length;n++){let i=e[n];i==='"'?r&&e[n+1]==='"'?(s+='""',n++):(r=!r,s+=i):(i===`
860
+ `||i==="\r")&&!r?(i==="\r"&&e[n+1]===`
861
+ `&&n++,s.trim().length>0&&t.push(s),s=""):s+=i}return s.trim().length>0&&t.push(s),t}static stringifyObject(e){if(!e||typeof e!="object")return{};let t={};for(let[s,r]of Object.entries(e))t[s]=r!=null?String(r):"";return t}};import Cn from"http";import Q from"fs";import X from"path";import bi from"os";import{fileURLToPath as Nn}from"url";Ne();Ne();Ss();import ke from"fs";import Se from"path";import Y from"chalk";Fe();import{spawn as Pn}from"child_process";import ai from"readline";import{createRequire as En}from"module";var kn=En(import.meta.url),pr="1.1.0";try{pr=kn("../../package.json").version||"1.1.0"}catch{}var ft=class{onStep;driver;deviceLock=null;isRecording=!1;recordedSteps=[];existingSteps=[];existingFileHeader=[];lastHierarchyFlat=[];lastScreenTitle="";visitedScreens=[];targetPackage=null;isAppendMode=!1;resolvedOutputPath="";resolvedReportPath="";activeInput=null;geteventProcess=null;isRefreshingHierarchy=!1;targetSerial="";touchMaxX=I.screen.fallbackWidth;touchMaxY=I.screen.fallbackHeight;displayWidth=I.screen.fallbackWidth;displayHeight=I.screen.fallbackHeight;activeTouchX=-1;activeTouchY=-1;touchDownTime=0;isTouchActive=!1;isSwipeGesture=!1;lastHierarchyTimestamp=0;lastTapTimestamp=0;lastRecordedTapTarget="";inFlightTapQueue=[];inputDebounceTimer=null;logcatEventProcess=null;logcatCrashProcess=null;hierarchyCacheTime=0;hierarchyStale=!0;lastScrollTimestamp=0;lastScrollDirection="";currentScreenName="";sessionScreenSections=[];sessionStartTime=0;screenshotDir="";screenshotCounter=0;crashDetected=!1;lastScreenTransitionTime=0;constructor(e){this.driver=e||Pe}flushToDisk(){if(this.resolvedOutputPath)try{this.saveSpec(this.resolvedOutputPath)}catch{}if(this.resolvedReportPath)try{this.saveReport(this.resolvedReportPath)}catch{}}appendStep(e,t){if(this.recordedSteps[this.recordedSteps.length-1]!==e){this.recordedSteps.push(e),this.flushToDisk(),t&&console.log(t);try{this.onStep?.(e)}catch{}}}extractBrandSlug(e){if(!e)return"app";let t=new Set(["com","org","net","io","in","co","uk","de","fr","us","ai","dev","android","mobile","app","apps","client","release","debug","main","browser","ui","view","experience","portal","phone","tablet","service"]),s=e.split("."),r=s.filter(i=>!t.has(i.toLowerCase())&&!/^\d+$/.test(i));return((r.length>0?r[r.length-1]:s[s.length-1])||"app").toLowerCase().replace(/[^a-z0-9_]/g,"")}async startSession(e,t,s){let r=await this.driver.ensureDefaultDevice(t);if(!r)throw new Error("No Android device connected for recording.");this.deviceLock=Et(r),this.targetSerial=r,this.isRecording=!0,this.recordedSteps=[],this.existingSteps=[],this.existingFileHeader=[],this.visitedScreens=[],this.targetPackage=null,this.activeInput=null,this.isAppendMode=!!s?.append;try{let u=await this.driver.getForegroundPackage(r);u&&(this.targetPackage=u)}catch{}let n=this.extractBrandSlug(this.targetPackage),i;if(e&&e!=="specs/recorded_suite.txt")i=Se.resolve(e);else{let u=`${n}_flow`,h=Se.resolve(`specs/${u}.txt`);if(!this.isAppendMode&&ke.existsSync(h)){let g=1;for(h=Se.resolve(`specs/${u}_${g}.txt`);ke.existsSync(h);)g++,h=Se.resolve(`specs/${u}_${g}.txt`)}i=h}if(this.resolvedOutputPath=i,ke.existsSync(i))if(this.isAppendMode){let u=ke.readFileSync(i,"utf-8"),h=u.split(`
862
+ `);for(let b of h){let T=b.trim();if(/^\d+\.\s+/.test(T))this.existingSteps.push(T.replace(/^\d+\.\s+/,""));else if(T.startsWith("# Package:")){let x=T.match(/# Package:\s*([a-zA-Z0-9._]+)/);x&&(this.targetPackage=x[1])}}let g=`${i}.bak`;ke.writeFileSync(g,u,"utf-8"),console.log(Y.cyan(`\u{1F4E5} Loaded ${this.existingSteps.length} existing step(s) from: ${Se.basename(i)}`)),console.log(Y.gray(`\u{1F4E6} Safety backup preserved at: ${Se.basename(g)}`))}else{let u=ke.readFileSync(i,"utf-8"),h=`${i}.bak`;ke.writeFileSync(h,u,"utf-8"),console.log(Y.yellow(`\u26A0\uFE0F File "${Se.basename(i)}" already exists.`)),console.log(Y.gray(`\u{1F4E6} Safety backup preserved at: ${Se.basename(h)}`))}await this.driver.wakeDevice(r).catch(()=>{});let l=await this.driver.getScreenDimensions(r);this.displayWidth=l.width||I.screen.fallbackWidth,this.displayHeight=l.height||I.screen.fallbackHeight,this.touchMaxX=this.displayWidth,this.touchMaxY=this.displayHeight;try{let{stdout:u}=await this.driver.execAdb(["shell","getevent","-p"],r),h=u.match(/0035\s*:\s*value\s*\d+,\s*min\s*\d+,\s*max\s*(\d+)/),g=u.match(/0036\s*:\s*value\s*\d+,\s*min\s*\d+,\s*max\s*(\d+)/);h&&parseInt(h[1])>0&&(this.touchMaxX=parseInt(h[1])),g&&parseInt(g[1])>0&&(this.touchMaxY=parseInt(g[1]))}catch{}this.sessionStartTime=Date.now(),this.sessionScreenSections=[],this.screenshotCounter=0,this.crashDetected=!1,this.hierarchyStale=!0,this.currentScreenName="";let c=Se.basename(i,".txt"),p=Se.resolve("output");this.screenshotDir=Se.join(p,"screenshots",c),ke.existsSync(this.screenshotDir)||ke.mkdirSync(this.screenshotDir,{recursive:!0});let d=Se.join(p,`${c}_report.md`);this.resolvedReportPath=d,await this.refreshHierarchySync(),this.startLogcatScreenWatcher(r),this.startLogcatCrashWatcher(r),this.startEventStream(r),this.flushToDisk()}async stopSession(){if(this.isRecording=!1,this.inputDebounceTimer&&(clearTimeout(this.inputDebounceTimer),this.inputDebounceTimer=null),this.deviceLock&&(kt(this.deviceLock),this.deviceLock=null),this.geteventProcess){try{this.geteventProcess.kill()}catch{}this.geteventProcess=null}if(this.logcatEventProcess){try{this.logcatEventProcess.kill()}catch{}this.logcatEventProcess=null}if(this.logcatCrashProcess){try{this.logcatCrashProcess.kill()}catch{}this.logcatCrashProcess=null}return this.flushToDisk(),{steps:[...this.recordedSteps],specPath:this.resolvedOutputPath,reportPath:this.resolvedReportPath}}async startRecording(e,t,s){await this.startSession(e,t,s);let r=Y.gray("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),n=await this.driver.getDeviceModel(this.targetSerial).catch(()=>this.targetSerial);console.log(`
863
+ ${r}`),console.log(Y.bold.cyan(`\u26A1 PromptTest v${pr}`)+Y.gray(" | Recording Session")),console.log(r),console.log(` ${Y.gray("\u{1F4F1} Device ")} ${Y.white(n)} ${Y.gray(`(${this.targetSerial})`)}`),console.log(` ${Y.gray("\u{1F4E6} App ")} ${Y.green(this.targetPackage||"Unknown")}`),console.log(` ${Y.gray("\u{1F3AF} Spec ")} ${Y.yellow(Se.basename(this.resolvedOutputPath))}`),console.log(` ${Y.gray("\u{1F4CA} Report ")} ${Y.yellow(Se.basename(this.resolvedReportPath))}`),console.log(` ${Y.gray("\u{1F4F8} Photos ")} ${Y.yellow(Se.relative(process.cwd(),this.screenshotDir))}`),this.isAppendMode&&this.existingSteps.length>0&&console.log(` ${Y.gray("\u{1F4CE} Mode ")} ${Y.magenta(`Append \u2014 resuming from step #${this.existingSteps.length+1}`)}`),console.log(r),console.log(` ${Y.green("\u2714")} Ready & streaming`),console.log(r),console.log(` ${Y.gray("Use the app naturally. Every action is captured.")}`),console.log(` ${Y.gray('Press Ctrl+C or type "exit" / "q" + Enter to stop and save.')}`),console.log(`${r}
864
+ `);let i=null,o=async()=>{if(i)try{i.close()}catch{}await this.stopSession(),this.printExitSummary(r),process.exit(0)};if(process.once("SIGINT",o),process.once("SIGTERM",o),process.stdin.isTTY)try{i=ai.createInterface({input:process.stdin,output:process.stdout}),i.on("line",c=>{let p=c.trim().toLowerCase();(p==="exit"||p==="quit"||p==="q"||p==="stop")&&o()}),i.on("SIGINT",()=>{o()})}catch{}let l=0;for(;this.isRecording;)if(await new Promise(c=>setTimeout(c,1500)),l++,l%20===0&&this.driver.wakeDevice(this.targetSerial).catch(()=>{}),!this.targetPackage){let c=await this.driver.getForegroundPackage(this.targetSerial).catch(()=>null);c&&(this.targetPackage=c)}}startEventStream(e){let t=e?["-s",e,"shell","getevent","-l"]:["shell","getevent","-l"],s=Ze(),r=Pn(s,t);this.geteventProcess=r;let n=null,i=null,o=null,l=null,c=0;ai.createInterface({input:r.stdout}).on("line",d=>{if(this.isRecording)if(d.includes("ABS_MT_POSITION_X")){let u=d.match(/ABS_MT_POSITION_X\s+([0-9a-fA-F]+)/);u&&(n=parseInt(u[1],16))}else if(d.includes("ABS_MT_POSITION_Y")){let u=d.match(/ABS_MT_POSITION_Y\s+([0-9a-fA-F]+)/);u&&(i=parseInt(u[1],16))}else if(d.includes("BTN_TOUCH")&&d.includes("DOWN"))o=n,l=i,c=Date.now();else if(d.includes("BTN_TOUCH")&&d.includes("UP")){let u=n!==null?n:o,h=i!==null?i:l,g=Date.now()-c;if(u!==null&&h!==null&&o!==null&&l!==null){let b=Math.round(o/this.touchMaxX*this.displayWidth),T=Math.round(l/this.touchMaxY*this.displayHeight),x=Math.round(u/this.touchMaxX*this.displayWidth),S=Math.round(h/this.touchMaxY*this.displayHeight),y=x-b,P=S-T,$=Math.abs(y),R=Math.abs(P),C=Math.hypot($,R);if(this.activeInput&&T>this.displayHeight*.58&&S>this.displayHeight*.58){g>=20&&g<400&&C<45&&x>=this.displayWidth*.78&&S>=this.displayHeight*.84?this.flushActiveInput().then(()=>{this.handleKeyPress("Enter")}).catch(()=>{}):this.scheduleInputDebounce(),n=null,i=null,o=null,l=null;return}if(g>=20&&g<400&&C<45)this.isRefreshingHierarchy?this.inFlightTapQueue.push({x,y:S,timestamp:Date.now()}):this.handleTapAt(x,S);else if(g>=450&&C<45)this.handleLongPressAt(x,S);else if(R>=100&&R>1.4*$){let B=P<0?"down":"up";this.handleScroll(B)}else if($>=100&&$>1.4*R){let B=y<0?"left":"right";this.handleSwipe(B)}}n=null,i=null,o=null,l=null}else d.includes("KEY_ENTER")&&d.includes("DOWN")?this.handleKeyPress("Enter"):d.includes("KEY_BACK")&&d.includes("DOWN")&&this.handleKeyPress("Back")}),r.on("close",d=>{this.isRecording&&(console.warn(Y.yellow(`
865
+ \u26A0\uFE0F [EventStream] ADB event listener stream closed (code ${d}). Auto-reconnecting in 800ms...`)),setTimeout(()=>{this.isRecording&&this.startEventStream(e)},800))}),r.on("error",d=>{this.isRecording&&console.warn(Y.yellow(`
866
+ \u26A0\uFE0F [EventStream] ADB stream error: ${d.message}.`))})}cleanLabel(e){return e?e.replace(/[\uE000-\uF8FF\uFFF0-\uFFFF\uFFFD]/gu,"").replace(/,\s*tab,\s*\d+\s*of\s*\d+/gi,"").replace(/,\s*button$/gi,"").replace(/^[\s,•\-_:;|/\\]+|[\s,•\-_:;|/\\]+$/g,"").trim():""}handleScroll(e){let t=Date.now();if(this.lastScrollDirection===e&&t-this.lastScrollTimestamp<850)return;this.lastScrollTimestamp=t,this.lastScrollDirection=e,this.activeInput&&this.flushActiveInput().catch(()=>{});let s=`Scroll ${e}`;this.recordedSteps[this.recordedSteps.length-1]!==s&&(this.appendStep(s,Y.blue(` \u{1F4DC} Recorded Scroll [Step #${this.getCurrentStepNumber()}]: ${Y.bold(s)}`)),this.triggerAsyncHierarchyRefresh(400))}handleSwipe(e){let t=Date.now();if(t-this.lastScrollTimestamp<600)return;this.lastScrollTimestamp=t,this.activeInput&&this.flushActiveInput().catch(()=>{});let s=`Swipe ${e}`;this.appendStep(s,Y.blue(` \u{1F449} Recorded Swipe [Step #${this.getCurrentStepNumber()}]: ${Y.bold(s)}`)),this.triggerAsyncHierarchyRefresh(500)}handleLongPressAt(e,t){let s=this.lastHierarchyFlat.filter(i=>e>=i.bounds.x1&&e<=i.bounds.x2&&t>=i.bounds.y1&&t<=i.bounds.y2);if(s.length===0)return;s.sort((i,o)=>{let l=(i.bounds.x2-i.bounds.x1)*(i.bounds.y2-i.bounds.y1),c=(o.bounds.x2-o.bounds.x1)*(o.bounds.y2-o.bounds.y1);return l-c});let r=s[0],n=this.resolveActionLabel(r,s,this.lastHierarchyFlat);if(n){let i=`Long press '${n}'`;this.appendStep(i,Y.cyan(` \u23F1\uFE0F Recorded Long Press [Step #${this.getCurrentStepNumber()}]: ${Y.bold(i)}`)),this.triggerAsyncHierarchyRefresh()}}handleTapAt(e,t){let s=Date.now(),r=this.lastHierarchyFlat.filter(d=>e>=d.bounds.x1&&e<=d.bounds.x2&&t>=d.bounds.y1&&t<=d.bounds.y2),i=this.lastHierarchyFlat.filter(d=>{let u=d.bounds.height;return u>0&&u<320&&d.bounds.width>0&&(d.clickable||d.contentDesc||d.text)}).filter(d=>t>=d.bounds.y1-30&&t<=d.bounds.y2+40);if(i.length>=2){let d=i.find(u=>e>=u.bounds.x1-20&&e<=u.bounds.x2+20);d&&(r=[this.lastHierarchyFlat.find(h=>(this.cleanLabel(h.text).length>0||this.cleanLabel(h.contentDesc).length>0)&&Math.abs(h.bounds.centerX-d.bounds.centerX)<d.bounds.width*.48&&Math.abs(h.bounds.centerY-d.bounds.centerY)<d.bounds.height*.48&&!this.isIconGlyph(this.cleanLabel(h.text)))||d])}if(t>this.displayHeight*.82){let u=this.lastHierarchyFlat.filter(h=>{let g=h.bounds.y1>=this.displayHeight*.78||h.bounds.y2>=this.displayHeight*.82,b=h.bounds.width>0&&h.bounds.width<=this.displayWidth*.52;return g&&b}).find(h=>e>=h.bounds.x1-20&&e<=h.bounds.x2+20);if(u){let h=this.lastHierarchyFlat.find(g=>(this.cleanLabel(g.text).length>0||this.cleanLabel(g.contentDesc).length>0)&&e>=g.bounds.x1-25&&e<=g.bounds.x2+25&&g.bounds.y1>=this.displayHeight*.78&&!this.isIconGlyph(this.cleanLabel(g.text)));h?r=[h]:(this.cleanLabel(u.text)||this.cleanLabel(u.contentDesc))&&(r=[u])}}if(r.length===0){let d=this.lastHierarchyFlat.filter(g=>(this.cleanLabel(g.text).length>0||this.cleanLabel(g.contentDesc).length>0)&&(g.clickable||g.className.includes("Button")||g.className.includes("Text"))),u=null,h=70;for(let g of d){let b=Math.hypot(g.bounds.centerX-e,g.bounds.centerY-t);b<h&&(h=b,u=g)}if(u)r=[u];else{this.triggerAsyncHierarchyRefresh();return}}r.sort((d,u)=>{let h=(d.bounds.x2-d.bounds.x1)*(d.bounds.y2-d.bounds.y1),g=(u.bounds.x2-u.bounds.x1)*(u.bounds.y2-u.bounds.y1);return h-g});let o=r[0],l=r.find(d=>d.className.includes("EditText"))||(o.className.includes("EditText")?o:null);if(l){this.activeInput&&this.activeInput.node!==l&&this.flushActiveInput().catch(()=>{});let d=this.resolveFieldLabel(l,this.lastHierarchyFlat);this.activeInput={node:l,label:d,startText:l.text||""},this.triggerAsyncHierarchyRefresh();return}this.activeInput&&this.flushActiveInput().catch(()=>{});let c=this.resolveActionLabel(o,r,this.lastHierarchyFlat);if(!c||c.length===0){this.triggerAsyncHierarchyRefresh();return}if(!o.clickable&&!r.some(d=>d.clickable||d.className.includes("Button"))&&/^\d+%$|^\d+$/.test(c)){this.triggerAsyncHierarchyRefresh();return}if(c===this.lastRecordedTapTarget&&s-this.lastTapTimestamp<350)return;this.lastTapTimestamp=s,this.lastRecordedTapTarget=c;let p;if(o.checkable||o.className.includes("Switch")||o.className.includes("CheckBox")){let d=o.checked!==void 0?!o.checked:!0;p=`Toggle '${c}' to ${d?"on":"off"}`}else{p=`Tap '${c}'`;let d=this.lastHierarchyFlat.filter(h=>this.cleanLabel(h.text)===c||this.cleanLabel(h.contentDesc)===c),u=[];for(let h of d)u.some(b=>Math.hypot(b.bounds.centerX-h.bounds.centerX,b.bounds.centerY-h.bounds.centerY)<40)||u.push(h);if(u.length>1){let g=this.lastHierarchyFlat.filter(b=>b!==o&&Math.abs(b.bounds.centerY-o.bounds.centerY)<140&&Math.abs(b.bounds.centerX-o.bounds.centerX)<550).find(b=>{let T=this.cleanLabel(b.text)||this.cleanLabel(b.contentDesc);return T.length>2&&T!==c&&!/^\d+$/.test(T)&&!this.isIconGlyph(T)});if(g){let b=this.cleanLabel(g.text)||this.cleanLabel(g.contentDesc);b&&b!==c&&(p=`Tap '${c}' next to '${b}'`)}}}this.appendStep(p,Y.cyan(` \u{1F446} Recorded Tap [Step #${this.getCurrentStepNumber()}]: ${Y.bold(p)}`)),this.triggerAsyncHierarchyRefresh()}handleKeyPress(e){if(this.activeInput&&this.flushActiveInput().catch(()=>{}),e==="Enter"){let t="Press 'Enter'";this.appendStep(t,Y.magenta(` \u21B5 Recorded Key [Step #${this.getCurrentStepNumber()}]: ${Y.bold(t)}`))}else if(e==="Back"){let t="Tap 'Back'";this.appendStep(t,Y.magenta(` \u25C0 Recorded Key [Step #${this.getCurrentStepNumber()}]: ${Y.bold(t)}`))}this.triggerAsyncHierarchyRefresh()}scheduleInputDebounce(){this.inputDebounceTimer&&clearTimeout(this.inputDebounceTimer),this.inputDebounceTimer=setTimeout(()=>{this.activeInput&&this.syncActiveInputText().catch(()=>{})},450)}async flushActiveInput(){if(this.inputDebounceTimer&&(clearTimeout(this.inputDebounceTimer),this.inputDebounceTimer=null),!this.activeInput)return;let e=this.activeInput;this.activeInput=null,await this.recordInputText(e)}async syncActiveInputText(){this.activeInput&&await this.recordInputText(this.activeInput)}async recordInputText(e){try{let t=await this.driver.getUiHierarchy(this.targetSerial).catch(()=>null);if(!t)return;let{flat:s}=me(t);this.lastHierarchyFlat=s;let r=s.find(n=>n.className.includes("EditText")&&(n.focused||n.selected))||s.find(n=>n.className.includes("EditText")&&this.resolveFieldLabel(n,s).toLowerCase()===e.label.toLowerCase())||s.find(n=>n.className.includes("EditText")&&Math.abs(n.bounds.centerX-e.node.bounds.centerX)<80)||s.find(n=>n.className.includes("EditText"));if(r&&r.text&&r.text.trim().length>0){let n=r.text.trim(),i=/^[•*•\u2022\u25cf]+$/.test(n);if(!(n===e.startText)&&!i){if(e.startText===""&&this.isPlaceholderText(n))return;let l=e.label||this.resolveFieldLabel(r,s),c=`Type '${n}' into '${l}'`,p=this.recordedSteps.length-1,d=p>=0?this.recordedSteps[p]:"";d.startsWith("Type ")&&d.endsWith(`into '${l}'`)?d!==c&&(this.recordedSteps[p]=c,this.flushToDisk(),console.log(Y.gray(` \u270D\uFE0F Updated Input [Step #${p+1+this.existingSteps.length}]: ${Y.bold(c)}`))):this.appendStep(c,Y.green(` \u270D\uFE0F Recorded Input [Step #${this.getCurrentStepNumber()}]: ${Y.bold(c)}`))}else if(i&&(r.password||e.label.toLowerCase().includes("password"))){let l=e.label||"Password",c=`Type '<password>' into '${l}'`,p=this.recordedSteps.length-1,d=p>=0?this.recordedSteps[p]:"";(!d.startsWith("Type ")||!d.endsWith(`into '${l}'`))&&this.appendStep(c,Y.green(` \u270D\uFE0F Recorded Password Input [Step #${this.getCurrentStepNumber()}]: ${Y.bold(c)}`))}}}catch{}}startLogcatScreenWatcher(e){try{this.logcatEventProcess=this.driver.streamLogcatEvents(e,t=>{if(this.isRecording&&(t.includes("wm_on_resume_called")||t.includes("am_on_resume_called"))){let s=Date.now();if(s-this.lastScreenTransitionTime<500)return;this.lastScreenTransitionTime=s,this.hierarchyStale=!0,this.triggerAsyncHierarchyRefresh(50)}})}catch{}}startLogcatCrashWatcher(e){try{this.logcatCrashProcess=this.driver.streamLogcatCrashes(e,t=>{if(this.isRecording&&(t.includes("FATAL EXCEPTION")||t.includes("ANR in"))){if(this.targetPackage&&!t.includes(this.targetPackage))return;this.crashDetected=!0,console.log(Y.red.bold(`
867
+ \u26A0\uFE0F [Crash Alert] Fatal exception / ANR detected in ${this.targetPackage||"app"}!`))}})}catch{}}captureSessionScreenshot(e){if(!this.screenshotDir)return;let t=++this.screenshotCounter,s=e.toLowerCase().replace(/[^a-z0-9_]/g,"_").slice(0,30)||"screen",r=`screen_${String(t).padStart(2,"0")}_${s}.png`,n=Se.join(this.screenshotDir,r);(async()=>{try{let i=await this.driver.captureScreenshot(this.targetSerial);if(i&&i.length>0){await ke.promises.writeFile(n,i);let o=this.sessionScreenSections[this.sessionScreenSections.length-1];o&&!o.screenshotFile&&(o.screenshotFile=r)}}catch{}})()}triggerAsyncHierarchyRefresh(e=I.recorder.asyncRefreshDelayMs){this.isRefreshingHierarchy||(this.isRefreshingHierarchy=!0,setTimeout(()=>{this.doHierarchyRefresh().catch(()=>{this.isRefreshingHierarchy=!1})},e))}async doHierarchyRefresh(){try{let e=await this.driver.getUiHierarchy(this.targetSerial),{flat:t}=me(e);this.lastHierarchyFlat=t,this.hierarchyStale=!1,this.hierarchyCacheTime=Date.now(),this.detectAndLogScreenHeader(t),this.detectAndLogTransientAlerts(t)}catch{}finally{for(this.isRefreshingHierarchy=!1;this.inFlightTapQueue.length>0;){let e=this.inFlightTapQueue.shift();if(!e)break;Date.now()-e.timestamp<I.recorder.staleTapTimeoutMs?this.handleTapAt(e.x,e.y):console.warn(Y.yellow(`[Recorder] Dropped stale tap at (${e.x}, ${e.y}) \u2014 screen refresh took longer than ${I.recorder.staleTapTimeoutMs}ms`))}}}detectAndLogTransientAlerts(e){let t=e.find(s=>(s.className.includes("Toast")||s.className.includes("Snackbar")||s.resourceId&&s.resourceId.includes("snackbar")||s.contentDesc&&(s.contentDesc.includes("Alert")||s.contentDesc.includes("Success")))&&(s.text||s.contentDesc));if(t){let s=this.cleanLabel(t.text)||this.cleanLabel(t.contentDesc);if(s&&s.length>3&&s.length<80){let r=`Verify '${s}' is visible`;this.recordedSteps.slice(-3).includes(r)||this.appendStep(r,Y.green(` \u2714 Recorded Alert [Step #${this.getCurrentStepNumber()}]: ${Y.bold(r)}`))}}}async refreshHierarchySync(){try{let e=await this.driver.getUiHierarchy(this.targetSerial),{flat:t}=me(e);this.lastHierarchyFlat=t,this.hierarchyStale=!1,this.hierarchyCacheTime=Date.now(),this.detectAndLogScreenHeader(t)}catch{}}isIconGlyph(e){if(!e||e.trim().length===0)return!0;let t=e.trim();return!!(/[\uE000-\uF8FF\uFFF0-\uFFFF]/.test(t)||t.length===1&&t.charCodeAt(0)>255&&!new RegExp("\\p{Extended_Pictographic}","u").test(t))}cleanDialogOrCompoundLabel(e){if(!e)return"";let t=e.trim();if(t.includes(",")||t.includes(`
868
+ `)){let s=t.split(/[\n,]+/).map(r=>this.cleanLabel(r)).filter(Boolean);if(s.length>1){let r=s.find(i=>i.includes("?")||/\b(are you sure|do you want|cannot be undone|would you like|please confirm)\b/i.test(i)||i.length>35);if(r){let i=s.find(o=>o!==r&&o.length<=30&&!o.includes("?"));if(i)return i}let n=s.filter(i=>/^[A-Z]{1,3}$/.test(i)||/^\d+%?$/.test(i)||this.isCardMetadataSubtitle(i)||/^(attend|present|absent|status|active|inactive)$/i.test(i)?!1:i.length>1);if(n.length>0)return n[0]}}return t}isCardMetadataSubtitle(e){if(!e||e.trim().length===0)return!1;let t=e.trim();return!!(/\+?\d{1,3}[\s-]?\d{4,}[\s-]?\d{4,}/.test(t)||/^(STD|ID|EMP|REF|ORD|USER|ACC|BATCH)[\-_:\s]?\d+/i.test(t)||/[•|·]/.test(t)&&/\d/.test(t)||/^\d+$/.test(t)||/^\d{1,2}:\d{2}/.test(t)||/^(teacher|roll\s*no|std\s*id|class|sec)[\s:]+/i.test(t))}findPrimaryCardTitle(e,t,s){if(e.bounds.y1>=this.displayHeight*.8||e.bounds.centerY>=this.displayHeight*.82||e.clickable&&(e.className.includes("Button")||e.className.includes("Check")))return null;let r=this.cleanLabel(e.text),n=this.isCardMetadataSubtitle(r),i=s.find(c=>c!==e&&c.clickable&&c.bounds.height>60&&c.bounds.height<600),o;i?o=t.filter(c=>c.className.includes("Text")&&c.bounds.x1>=i.bounds.x1-10&&c.bounds.x2<=i.bounds.x2+10&&c.bounds.y1>=i.bounds.y1-10&&c.bounds.y2<=i.bounds.y2+10):o=t.filter(c=>c!==e&&c.className.includes("Text")&&c.bounds.centerY<e.bounds.centerY&&e.bounds.centerY-c.bounds.centerY<160&&Math.abs(c.bounds.centerX-e.bounds.centerX)<500);let l=o.filter(c=>{let p=this.cleanLabel(c.text);return p.length>2&&!this.isCardMetadataSubtitle(p)&&!this.isIconGlyph(p)&&!/^\d+$/.test(p)&&!/^(active|inactive|present|absent|new|pending)$/i.test(p)}).sort((c,p)=>c.bounds.y1-p.bounds.y1);if(l.length>0){let c=this.cleanLabel(l[0].text);if(n||i&&c!==r&&!e.clickable)return c}return null}resolveActionLabel(e,t,s){if(e.bounds.y1>=this.displayHeight*.8||e.bounds.centerY>=this.displayHeight*.82){let c=this.cleanLabel(e.contentDesc)||this.cleanLabel(e.text);if(c&&c.length>0&&!this.isIconGlyph(c))return this.cleanDialogOrCompoundLabel(c);for(let p of t){let d=this.cleanLabel(p.contentDesc)||this.cleanLabel(p.text);if(d&&d.length>0&&!this.isIconGlyph(d))return this.cleanDialogOrCompoundLabel(d)}}let r=this.findPrimaryCardTitle(e,s,t);if(r)return this.cleanDialogOrCompoundLabel(r);let n=this.cleanLabel(e.text);if(n.length>0&&n.length<90){let c=n.split(/[\n\r]+/).map(d=>this.cleanLabel(d)).filter(Boolean),p=c.length>1?c.find(d=>d.length>2&&!/^\d+$/.test(d))||c[0]:n;return this.cleanDialogOrCompoundLabel(p)}let i=this.cleanLabel(e.contentDesc);if(i.length>0&&i.length<90)return this.cleanDialogOrCompoundLabel(i);let o=t.find(c=>(c.clickable||c.className.includes("Button"))&&(this.cleanLabel(c.text)||this.cleanLabel(c.contentDesc)));if(o){let c=this.cleanLabel(o.text)||this.cleanLabel(o.contentDesc);if(c.length>0&&c.length<90)return this.cleanDialogOrCompoundLabel(c)}for(let c of t){let p=this.cleanLabel(c.text);if(p.length>0&&p.length<90){let u=p.split(/[\n\r]+/).map(g=>this.cleanLabel(g)).filter(Boolean),h=u.length>1?u.find(g=>g.length>2&&!/^\d+$/.test(g))||u[0]:p;return this.cleanDialogOrCompoundLabel(h)}let d=this.cleanLabel(c.contentDesc);if(d.length>0&&d.length<90)return this.cleanDialogOrCompoundLabel(d)}let l=s.find(c=>c!==e&&(this.cleanLabel(c.text).length>0||this.cleanLabel(c.contentDesc).length>0)&&Math.abs(c.bounds.centerX-e.bounds.centerX)<80&&Math.abs(c.bounds.centerY-e.bounds.centerY)<100);if(l){let c=this.cleanLabel(l.text)||this.cleanLabel(l.contentDesc);if(c.length>0)return this.cleanDialogOrCompoundLabel(c)}if(e.resourceId){let c=e.resourceId.split("/"),p=c[c.length-1];if(p&&!/^\d+$/.test(p)){let d=p.replace(/_/g," ").replace(/btn|button|icon|view|action/gi,"").trim();if(d.length>1)return d.charAt(0).toUpperCase()+d.slice(1)}}return""}getCurrentStepNumber(){return this.existingSteps.length+this.recordedSteps.length+1}detectAndLogScreenHeader(e){let t=e.filter(s=>{if(!s.className.includes("TextView")||s.text.trim().length<2||s.packageName&&(s.packageName.includes("systemui")||s.packageName.includes("launcher"))||s.bounds.y1<60||s.bounds.y1>=450)return!1;let r=s.text.trim();return!(this.isCardMetadataSubtitle(r)||/\b(JIO|Airtel|Vodafone|No service|Emergency|VoLTE|4G|5G)\b/i.test(r)||/^\d+%?$|^\d+$/.test(r)||/^step\s*\d+\s*(of|\/)\s*\d+/i.test(r))}).sort((s,r)=>s.bounds.y1-r.bounds.y1);if(t.length>0){let s=t[0].text.trim();if(s&&s!==this.lastScreenTitle&&!s.includes(":")&&s.length<40){this.lastScreenTitle=s,this.currentScreenName=s,this.visitedScreens.includes(s)||this.visitedScreens.push(s),this.sessionScreenSections.push({name:s,stepStart:this.recordedSteps.length}),this.captureSessionScreenshot(s);let r=`Verify '${s}' is visible`;this.recordedSteps[this.recordedSteps.length-1]!==r&&!this.recordedSteps.slice(-3).includes(r)&&this.appendStep(r,Y.green(` \u2714 Recorded Header [Step #${this.getCurrentStepNumber()}]: ${Y.bold(r)}`))}}}resolveFieldLabel(e,t){if(e.contentDesc&&e.contentDesc.trim().length>0)return e.contentDesc.trim();if(e.password)return"Password";let s=t.filter(r=>r.className.includes("TextView")&&r.text&&r.text.trim().length>1&&r.bounds.centerY<e.bounds.centerY&&e.bounds.centerY-r.bounds.centerY<250&&Math.abs(r.bounds.centerX-e.bounds.centerX)<450);if(s.length>0){s.sort((n,i)=>i.bounds.centerY-n.bounds.centerY);let r=s[0].text.trim().replace(/[*:]/g,"").trim();if(r.length>0)return r}if(e.resourceId){let r=e.resourceId.split("/"),n=r[r.length-1];if(n&&!/^\d+$/.test(n)){let i=n.replace(/_/g," ").replace(/edit|text|input|field|et|txt/gi,"").trim();return i.length>1?i.charAt(0).toUpperCase()+i.slice(1):n}}if(e.text&&e.text.trim().length>0&&e.text.trim().length<40){let r=e.text.trim();if(r.toLowerCase().startsWith("search"))return r.length<=25?r:"Search";if(!this.isPlaceholderText(r))return r}return"Input Field"}isPlaceholderText(e){let t=e.trim().toLowerCase();return t.startsWith("search")||t.startsWith("e.g.")||t.startsWith("enter ")||t.startsWith("type ")||t.includes("optional")}saveSpec(e){let t=Se.resolve(e);if(Se.basename(t).startsWith("app_flow")&&this.targetPackage&&!this.isAppendMode){let l=this.extractBrandSlug(this.targetPackage);if(l!=="app"){let c=`${l}_flow`,p=Se.resolve(`specs/${c}.txt`),d=1;for(;ke.existsSync(p);)p=Se.resolve(`specs/${c}_${d}.txt`),d++;t=p}}let r=Se.dirname(t);ke.existsSync(r)||ke.mkdirSync(r,{recursive:!0});let n=[];if(this.isAppendMode&&this.existingSteps.length>0&&ke.existsSync(t)){let l=ke.readFileSync(t,"utf-8").trim();n.push(l),n.push(""),n.push(`# --- Appended Session (${new Date().toISOString().replace("T"," ").slice(0,19)}) ---`),this.recordedSteps.forEach((c,p)=>{n.push(`${this.existingSteps.length+p+1}. ${c}`)})}else{n.push("# ==========================================================",`# Autonomous Recorded Test Spec: ${Se.basename(t)}`,...this.targetPackage?[`# Package: ${this.targetPackage}`]:[],`# Generated: ${new Date().toISOString()}`,`# PromptTest v${pr}`,"# ==========================================================","");let l=0;this.recordedSteps.forEach((c,p)=>{if(l<this.sessionScreenSections.length&&p===this.sessionScreenSections[l].stepStart){let d=this.sessionScreenSections[l];n.push(""),n.push(`# Screen: ${d.name}`),l++}n.push(`${this.existingSteps.length+p+1}. ${c}`)})}n.push("");let i=n.join(`
869
+ `),o=`${t}.tmp.${Date.now()}`;try{ke.writeFileSync(o,i,"utf-8"),ke.renameSync(o,t)}catch{try{ke.unlinkSync(o)}catch{}ke.writeFileSync(t,i,"utf-8")}}saveReport(e){let t=Math.round((Date.now()-this.sessionStartTime)/1e3),s=Math.floor(t/60),r=t%60,n=`${String(s).padStart(2,"0")}:${String(r).padStart(2,"0")}`,i=this.existingSteps.length+this.recordedSteps.length,o=["# PromptTest Recording Report","","| Property | Value |","|---|---|",`| **Target Package** | \`${this.targetPackage||"Unknown"}\` |`,`| **Device Serial** | \`${this.targetSerial}\` |`,`| **Session Started** | ${new Date(this.sessionStartTime).toISOString()} |`,`| **Session Duration** | ${n} |`,`| **Steps in Session** | ${this.recordedSteps.length} |`,`| **Total Suite Steps** | ${i} |`,`| **Screens Visited** | ${this.visitedScreens.length} |`,`| **Screenshots Saved** | ${this.screenshotCounter} |`,`| **Crash Signals** | ${this.crashDetected?"\u26A0\uFE0F Detected":"\u2705 None"} |`,"","---","","## Screen Journey","",this.visitedScreens.length>0?this.visitedScreens.map((l,c)=>`${c+1}. **${l}**`).join(`
870
+ `):"*Single screen flow*","","---","","## Recorded Interactions",""];if(this.sessionScreenSections.length>0)for(let l=0;l<this.sessionScreenSections.length;l++){let c=this.sessionScreenSections[l],p=this.sessionScreenSections[l+1],d=p?p.stepStart:this.recordedSteps.length,u=this.recordedSteps.slice(c.stepStart,d);o.push(`### Screen: ${c.name}`),o.push(""),c.screenshotFile&&(o.push(`![${c.name}](screenshots/${Se.basename(this.screenshotDir)}/${c.screenshotFile})`),o.push("")),u.length>0?u.forEach((h,g)=>{o.push(`${c.stepStart+g+1}. \`${h}\``)}):o.push("*(No interactions on this screen)*"),o.push("")}else o.push("### Steps"),o.push(""),this.recordedSteps.forEach((l,c)=>{o.push(`${c+1}. \`${l}\``)}),o.push("");try{let l=Se.dirname(e);ke.existsSync(l)||ke.mkdirSync(l,{recursive:!0}),ke.writeFileSync(e,o.join(`
871
+ `),"utf-8")}catch{}}printExitSummary(e){let t=Math.round((Date.now()-this.sessionStartTime)/1e3),s=Math.floor(t/60),r=t%60,n=`${String(s).padStart(2,"0")}:${String(r).padStart(2,"0")}`,i=this.existingSteps.length+this.recordedSteps.length;console.log(`
872
+ ${e}`),console.log(Y.bold.green(" \u{1F4BE} Recording Session Saved Successfully!")),console.log(e),console.log(` ${Y.gray("Steps Captured ")} ${Y.bold.yellow(this.recordedSteps.length.toString())} ${this.isAppendMode?Y.gray(`(Total Suite: ${i})`):""}`),console.log(` ${Y.gray("Screens Visited")} ${Y.bold.white(this.visitedScreens.length.toString())} ${this.visitedScreens.length>0?Y.gray(`(${this.visitedScreens.join(" \u2192 ")})`):""}`),console.log(` ${Y.gray("Screenshots ")} ${Y.bold.white(this.screenshotCounter.toString())} saved`),console.log(` ${Y.gray("Session Time ")} ${Y.bold.white(n)}`),console.log(` ${Y.gray("Crash Signals ")} ${this.crashDetected?Y.red.bold("\u26A0\uFE0F Detected"):Y.green("\u2714 0 detected")}`),console.log(e),console.log(` ${Y.gray("\u{1F4C4} Spec ")} ${Y.cyan(this.resolvedOutputPath)}`);let o=Se.basename(this.resolvedOutputPath,".txt");console.log(` ${Y.gray("\u{1F4CA} Report ")} ${Y.cyan(Se.resolve("output",`${o}_report.md`))}`),console.log(`${e}
873
+ `)}},ci=new ft;Ne();import cs from"fs";import li from"path";import Re from"chalk";async function ls(a=Pe,e={}){let t=[],s=process.versions.node,n=parseInt(s.split(".")[0],10)>=18;t.push({name:"Node.js Runtime",passed:n,message:`Node.js v${s} (Required >= 18.0.0)`,hint:n?void 0:"Please upgrade Node.js to v18.0.0 or higher: https://nodejs.org"});let i=!1,o="ADB binary not found in PATH",l='Install Android SDK platform-tools and ensure "adb" is in your system PATH.';try{let{stdout:S}=await a.execAdb(["version"],void 0,5e3),y=S.match(/Android Debug Bridge version ([\d.]+)/i);y?(i=!0,o=`Android Debug Bridge version ${y[1]}`,l=void 0):(i=!0,o=S.split(`
874
+ `)[0].trim(),l=void 0)}catch(S){o=`Failed to execute adb: ${S.message}`}t.push({name:"Android Debug Bridge (ADB)",passed:i,message:o,hint:l});let c=!1,p="No connected Android devices or emulators found",d="Connect an Android device via USB/Wi-Fi or launch an emulator with USB Debugging enabled.";try{let S=await a.getConnectedDevices(),y=S.filter(R=>R.state==="device"),P=S.filter(R=>R.state==="unauthorized"),$=S.filter(R=>R.state==="offline");if(y.length>0){c=!0;let R=y.map(C=>`${C.serial} (${C.model||"Unknown"})`).join(", ");p=`Found ${y.length} authorized device(s): ${R}`,d=void 0}else P.length>0?(p=`Device(s) found but unauthorized: ${P.map(R=>R.serial).join(", ")}`,d='Check the device screen and accept the "Allow USB debugging" RSA prompt.'):$.length>0&&(p=`Device(s) found in offline state: ${$.map(R=>R.serial).join(", ")}`,d='Try running "adb reconnect" or reconnect the USB cable.')}catch(S){p=`Failed to list devices: ${S.message}`}if(t.push({name:"Connected Android Device",passed:c,message:p,hint:d}),e.packageName){let S=e.packageName,y=!1,P=`Package "${S}" not found on target device`,$='Install the APK using "adb install <path.apk>" or check the package identifier.';if(c)try{let{stdout:R}=await a.execAdb(["shell","pm","list","packages",S],e.serial,5e3);R.includes(`package:${S}`)&&(y=!0,P=`Package "${S}" is installed on target device`,$=void 0)}catch(R){P=`Failed to query package status: ${R.message}`}else P="Skipped package verification (no authorized device available)",$=`Connect a device to verify package "${S}".`;t.push({name:`Target Package [${S}]`,passed:y,message:P,hint:$})}let u=!0,h="All required workspace directories are accessible",g,b=["output","memory",".prompttest"];for(let S of b)try{let y=li.resolve(process.cwd(),S);cs.existsSync(y)||cs.mkdirSync(y,{recursive:!0});let P=li.join(y,".doctor_perm_test");cs.writeFileSync(P,"ok","utf-8"),cs.unlinkSync(P)}catch(y){u=!1,h=`Filesystem permission error for "./${S}": ${y.message}`,g="Ensure the current user has write permissions to the workspace directory.";break}t.push({name:"Workspace Storage & Permissions",passed:u,message:h,hint:g});let T=t.filter(S=>S.passed).length,x=t.filter(S=>!S.passed).length;return{timestamp:new Date().toISOString(),allPassed:x===0,checks:t,summary:{passed:T,failed:x,warnings:0}}}function di(a){let e=["",Re.bold.cyan("\u{1FA7A} PromptTest Doctor \u2014 Environment Health Diagnostic"),Re.gray(`Scanned at ${a.timestamp}
875
+ `)];for(let t of a.checks){let s=t.passed?Re.green("\u2714"):Re.red("\u2716"),r=Re.bold(t.name);e.push(` ${s} ${r}: ${t.message}`),t.hint&&!t.passed&&e.push(Re.yellow(` \u{1F4A1} Fix: ${t.hint}`))}return e.push(""),a.allPassed?e.push(Re.bold.green("\u2728 All diagnostic checks passed! Your environment is ready to test.")):e.push(Re.bold.red(`\u26A0\uFE0F ${a.summary.failed} diagnostic check(s) failed. Please review the remediation hints above.`)),process.env.CI||e.push("",Re.dim("\u2500".repeat(64)),Re.cyan("\u{1F4A1} Prefer inspecting elements and authoring tests visually?"),Re.white(` Try PromptTest Studio (Desktop IDE): ${Re.underline("https://shriramsingh.github.io/prompttest-studio-site/")}`),Re.dim("\u2500".repeat(64))),e.push(""),e.join(`
876
+ `)}Ne();import An from"fs";import ur from"path";import M from"chalk";Fe();Ne();import pi from"fs";import Dn from"path";Fe();var bt=class{testData={};dataPath;isLoaded=!1;driver;constructor(e,t){this.dataPath=e||Dn.resolve(process.cwd(),"test-data.json"),this.driver=t||Pe}setTestData(e){this.testData=e,this.isLoaded=!0}async ensureDataLoaded(){if(!this.isLoaded){if(pi.existsSync(this.dataPath))try{let e=await pi.promises.readFile(this.dataPath,"utf-8");this.testData=JSON.parse(e)}catch{this.testData={}}this.isLoaded=!0}}async loadTestData(e){return e&&(this.dataPath=e),this.isLoaded=!1,await this.ensureDataLoaded(),this.testData}inferValueForNode(e,t){let s=[e.text,e.contentDesc,e.resourceId].join(" ").toLowerCase(),r="",n=t.filter(o=>o.text&&!o.clickable&&o.bounds.centerY<e.bounds.centerY&&e.bounds.centerY-o.bounds.centerY<250&&Math.abs(o.bounds.centerX-e.bounds.centerX)<400);n.length>0&&(r=n[n.length-1].text.toLowerCase());let i=`${s} ${r}`.toLowerCase();if(i.includes("review")||i.includes("feedback")||i.includes("comment")||i.includes("write a review")||i.includes("experience")){if(!this.testData.review?.text)throw new Error("Missing test-data.json field: review.text");return this.testData.review.text}if(i.includes("card")||i.includes("credit")||i.includes("debit")||i.includes("card number")){let o=this.testData.payment?.card;if(o)return String(o);let l="4532";for(let c=0;c<12;c++)l+=Math.floor(Math.random()*10);return l}if(i.includes("phone")||i.includes("mobile")||!i.includes("card")&&i.includes("number")||i.includes("contact")){let o=this.testData.auth?.phone||this.testData.profile?.phone;if(!o)throw new Error("Missing test-data.json field: auth.phone or profile.phone");return o}if(i.includes("email")||i.includes("e-mail")||i.includes("mail")){let o=this.testData.profile?.email||this.testData.auth?.email;if(!o)throw new Error("Missing test-data.json field: profile.email or auth.email");return o}if(i.includes("cvv")||i.includes("cvc")||i.includes("security code"))return"123";if(i.includes("expiry")||i.includes("exp date")||i.includes("expiration"))return"12/28";if(i.includes("pincode")||i.includes("zip")||i.includes("postal")){if(!this.testData.profile?.pincode)throw new Error("Missing test-data.json field: profile.pincode");return this.testData.profile.pincode}if(e.password||i.includes("password")||/\bpin\b/.test(i)&&!i.includes("pincode")||i.includes("passcode")){if(!this.testData.auth?.password)throw new Error("Missing test-data.json field: auth.password");return this.testData.auth.password}if(i.includes("otp")||i.includes("verification")||i.includes("code")&&!i.includes("postal")&&!i.includes("zip")){if(!this.testData.auth?.otp)throw new Error("Missing test-data.json field: auth.otp");return this.testData.auth.otp}if(i.includes("address")||i.includes("street")||i.includes("location")){if(!this.testData.profile?.address)throw new Error("Missing test-data.json field: profile.address");return this.testData.profile.address}if(i.includes("city")||i.includes("state")){if(!this.testData.profile?.city)throw new Error("Missing test-data.json field: profile.city");return this.testData.profile.city}if(i.includes("name")||i.includes("full name")||i.includes("first name")){if(!this.testData.profile?.name)throw new Error("Missing test-data.json field: profile.name");return this.testData.profile.name}if(i.includes("search")||i.includes("find")||i.includes("query")){if(!this.testData.search?.query)throw new Error("Missing test-data.json field: search.query");return this.testData.search.query}return this.testData.review?.text||"PromptTest Auto QA Test Data"}async fillAndSubmitForm(e,t,s=!0,r=!1){await this.ensureDataLoaded();let n=Le(e),i=[];if(n.length===0)return{filledFields:[],submitted:!1};let o=!1;for(let p of n){let d=p.resourceId||p.text||`input_${p.bounds.centerX}_${p.bounds.centerY}`,u=(p.text||"").trim(),h=!u||/^enter\s+/i.test(u)||/^type\s+/i.test(u)||/^e\.g\./i.test(u)||/^(email|email address|password|phone|name|search)$/i.test(u);if(!r&&!h){i.push({fieldId:d,fieldLabel:p.text||p.contentDesc||p.resourceId,valueEntered:u});continue}let g=this.inferValueForNode(p,e);await this.driver.tap(p.bounds.centerX,p.bounds.centerY,t),await new Promise(b=>setTimeout(b,400)),await this.driver.clearField(t,g.length+10),await new Promise(b=>setTimeout(b,200)),await this.driver.inputText(g,t),await new Promise(b=>setTimeout(b,300)),o=!0,i.push({fieldId:d,fieldLabel:p.text||p.contentDesc||p.resourceId,valueEntered:g})}o&&(await this.driver.pressKey(4,t),await new Promise(p=>setTimeout(p,500)));let l=!1,c;if(s){let p=e.filter(d=>!d.clickable||d.bounds.width===0?!1:Or.some(u=>de(u,d.text)||de(u,d.contentDesc)||de(u,d.resourceId)));if(p.length>0){let d=["submit","sign in","signin","login","save","confirm","proceed","continue"];p.sort((h,g)=>{let b=(h.text||h.contentDesc||h.resourceId||"").toLowerCase(),T=(g.text||g.contentDesc||g.resourceId||"").toLowerCase(),x=d.findIndex($=>b.includes($)),S=d.findIndex($=>T.includes($)),y=x>=0?x:99,P=S>=0?S:99;return y!==P?y-P:g.bounds.centerY-h.bounds.centerY});let u=p[0];c=u.text||u.contentDesc||u.resourceId,await this.driver.tap(u.bounds.centerX,u.bounds.centerY,t),await new Promise(h=>setTimeout(h,I.settle.submitWaitMs));try{let h=await this.driver.getUiHierarchy(t),{flat:g}=me(h),b=Le(g);l=!(b.length>0&&n.every(x=>b.some(S=>S.resourceId&&S.resourceId===x.resourceId)))}catch{l=!0}}}return{filledFields:i,submitted:l,submitButtonText:c}}},ui=new bt;import re from"chalk";Ne();var ds=class{driver;reporter;memory;formFiller;screenLedger=new Map;callStack=[];executedStepCount=0;visitedScreenHashes=new Set;devDefectsList=[];safetyHitList=[];externalSkippedList=[];stepListener;abortFlag;constructor(e=Pe,t=dt,s=_t){this.driver=e,this.reporter=t,this.memory=s,this.formFiller=new bt(void 0,e)}emitStep(e){try{let t=e.screenHash?this.screenLedger.get(e.screenHash):void 0;this.stepListener?.({...e,depth:e.depth??t?.depth,elementCount:e.elementCount??t?.elements.length,discoveredScreens:e.discoveredScreens??this.visitedScreenHashes.size,totalSteps:e.totalSteps??this.executedStepCount})}catch{}}isAborted(){return this.abortFlag?.aborted===!0}async getCleanUiHierarchy(e,t,s=5){let r=await this.driver.getUiHierarchy(t),{flat:n}=me(r),i=0;for(;i<s;){let o=Kt(n);if(!o)break;i++;let l=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,c=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(re.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),this.emitStep({type:"crash",description:`Dev overlay intercepted ${l}: "${o.errorText.slice(0,120)}"${c}`});let p=await this.driver.captureScreenshot(t);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${l}: "${o.errorText}"${c}`,"Verify","FAIL",o.errorText,p,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(re.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,t)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(re.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,t)):(console.log(re.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(t)),await new Promise(d=>setTimeout(d,800)),r=await this.driver.getUiHierarchy(t),n=me(r).flat}return{xml:r,flat:n}}async dismissSoftKeyboard(e){try{await this.driver.pressKey(111,e),await new Promise(t=>setTimeout(t,150))}catch{}}async packageJailGuard(e,t){let s=await this.driver.getForegroundPackage(t);if(!s)return!1;if(!s.includes(e)){let r=At(s);console.log(re.yellow(` \u{1F6A8} External App Jailbreak Detected [${s}]. Recovering...`)),r||(await this.driver.pressBack(t),await new Promise(i=>setTimeout(i,800)));let n=await this.driver.getForegroundPackage(t);return(!n||!n.includes(e))&&(console.log(re.red(` Force restoring ${e} foreground...`)),await this.driver.launchApp(e,t),await new Promise(i=>setTimeout(i,1500)),this.callStack=this.callStack.slice(0,1)),!0}return!1}async handleExternalMediaChooser(e,t){let s=await this.driver.getForegroundPackage(t);if(!s)return!1;let r=s==="android"||s.includes("resolver")||s.includes("intentresolver");if(r){console.log(re.yellow(` \u{1F6E1}\uFE0F External Media Chooser Intercepted [${s}]. Dismissing to stay in-app...`)),await this.driver.pressBack(t),await new Promise(i=>setTimeout(i,800));let n=await this.driver.getForegroundPackage(t);return n&&!n.includes(e)&&r&&(await this.driver.launchApp(e,t),await new Promise(i=>setTimeout(i,1200)),this.callStack=this.callStack.slice(0,1)),!0}return!1}catalogScreen(e,t,s=[]){let r=[],n=t.height,i=n*.06,o=n*.88,l=y=>s.some(P=>Math.abs(P.bounds.centerX-y.bounds.centerX)<30&&Math.abs(P.bounds.centerY-y.bounds.centerY)<30),c=Le(e);for(let y of c){if(l(y))continue;let P=y.text||y.contentDesc||y.resourceId||"Text Input";r.push({node:y,type:"input",label:P,tested:!1})}let p=Ot(e);for(let y of p){if(l(y))continue;let P=y.text||y.contentDesc||y.resourceId||"Toggle Control";r.push({node:y,type:"toggle",label:P,tested:!1})}let d=Me(e);for(let y of d){if(l(y)||at([y],n,t.width)||He([y],n,t.width,!0))continue;let P=y.text||y.contentDesc||y.resourceId||"Action Button";if(Ve(y)){this.externalSkippedList.push(P);continue}let $=y.selected!==void 0||y.checkable||/(?:tab|chip|pill|filter|segment|toggle)/i.test(`${y.resourceId} ${y.className} ${y.contentDesc}`)||/(?:today|yesterday|all|active|archived|pending|completed|present|absent)/i.test(P);r.push({node:y,type:$?"toggle":"transition",label:P,tested:!1})}let u=Br(e,n,t.width);for(let y of u){if(l(y)||He([y],n,t.width,!0)||r.some($=>Math.abs($.node.bounds.centerX-y.bounds.centerX)<25&&Math.abs($.node.bounds.centerY-y.bounds.centerY)<25))continue;let P=y.text||y.contentDesc||y.resourceId||"Notification / Header Action";if(Ve(y)){this.externalSkippedList.push(P);continue}r.push({node:y,type:"transition",label:P,tested:!1})}let h=e.filter(y=>{if(!y.clickable||y.bounds.width===0)return!1;let P=`${y.text} ${y.contentDesc} ${y.resourceId}`.toLowerCase();return P.includes("now playing")||P.includes("play")||P.includes("pause")});for(let y of h){if(l(y)||r.some($=>Math.abs($.node.bounds.centerX-y.bounds.centerX)<25&&Math.abs($.node.bounds.centerY-y.bounds.centerY)<25))continue;let P=y.text||y.contentDesc||y.resourceId||"Media Control";r.push({node:y,type:"toggle",label:P,tested:!1})}let g=zt(e,t).filter(y=>!l(y)&&y.bounds.centerY>i&&y.bounds.centerY<o&&!He([y],n,t.width,!0)&&!Ve(y)),b=Yr(g,2);for(let y of b){let P=y.text||y.contentDesc||y.resourceId||`Item (${y.bounds.centerX},${y.bounds.centerY})`;r.push({node:y,type:"transition",label:P,tested:!1})}let T=0,x=0,S=[];for(let y of r){let P=/\.(?:mp4|mkv|mov|webm|flv|avi)\b/i.test(y.label),$=/\.(?:mp3|wav|m4a|aac|ogg)\b/i.test(y.label);if(P){if(T++,T>1)continue}else if($&&(x++,x>1))continue;S.push(y)}return S}async safeBacktrack(e,t,s,r,n=!0){let{flat:i}=await this.getCleanUiHierarchy(t,s),{height:o,width:l}=await this.driver.getScreenDimensions(s),c=Ue(i,{width:l,height:o});if(c===e)return!0;let p=He(i,o,l,!0);if(p&&p.bounds.width>0){console.log(re.gray(` [Backtrack] Tapping in-app back/close button "${p.text||p.contentDesc||p.className}"...`)),await this.driver.tap(p.bounds.centerX,p.bounds.centerY,s),await new Promise(h=>setTimeout(h,1e3)),await this.packageJailGuard(t,s);let d=await this.getCleanUiHierarchy(t,s),u=Ue(d.flat,{width:l,height:o});if(u===e||u!==c)return!0}if(r&&r.bounds.width>0){console.log(re.gray(` [Backtrack] Restoring root domain via tab "${r.text||r.contentDesc||"Root Tab"}"...`)),await this.driver.tap(r.bounds.centerX,r.bounds.centerY,s),await new Promise(h=>setTimeout(h,1e3)),await this.packageJailGuard(t,s);let d=await this.getCleanUiHierarchy(t,s),u=Ue(d.flat,{width:l,height:o});if(u===e||u!==c)return!0}if(n){if(console.log(re.gray(" [Backtrack] Sending fallback KEYCODE_BACK...")),await this.driver.pressKey(4,s),await new Promise(g=>setTimeout(g,1e3)),await this.packageJailGuard(t,s))return console.log(re.yellow(" \u26A0\uFE0F Backtrack via KEYCODE_BACK caused app jailbreak. Aborting branch restoration.")),!1;let u=await this.getCleanUiHierarchy(t,s),h=Ue(u.flat,{width:l,height:o});if(h===e||h!==c)return!0}return!1}async explore(e){let{packageName:t,serial:s,maxScreens:r=30,maxDepth:n=3,stepBudget:i=100,maxInteractionsPerScreen:o=6,safetyMode:l="strict",safetyBlacklist:c=[],onStep:p,abortSignal:d}=e;this.stepListener=p,this.abortFlag=d,console.log(re.bold.cyan(`
877
+ \u{1F916} PromptTest Universal State-Graph DFS Engine`)),console.log(re.gray(`Target Package: ${t}`)),console.log(re.gray(`Safety Policy: ${l.toUpperCase()}`)),console.log(re.gray(`Limits: Max Screens = ${r}, Step Budget = ${i}, Max Depth = ${n}`)),await this.driver.clearCrashLogs(s);let u=await this.driver.getScreenDimensions(s),h=await this.driver.getForegroundPackage(s);(!h||!h.includes(t))&&(console.log(re.gray(`Launching ${t}...`)),await this.driver.launchApp(t,s),await new Promise(y=>setTimeout(y,2500)));let{flat:g}=await this.getCleanUiHierarchy(t,s);await this.dismissSoftKeyboard(s);let b=Ts(g,u,t);console.log(re.magenta(`\u26A1 Universal Paradigm Detected: [${b.paradigm.toUpperCase()}] with ${b.roots.length} root node(s).`));let T=[],x;if(b.paradigm==="drawer"&&b.roots.length>0){x=b.roots[0];let y={...g[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(re.gray(" Opening drawer to catalog root destinations...")),await this.driver.tap(x.bounds.centerX,x.bounds.centerY,s),await new Promise(U=>setTimeout(U,1200));let P=await this.getCleanUiHierarchy(t,s),$=P.flat.filter(U=>{if(!U.clickable||U.bounds.width===0||U.bounds.height===0||U.bounds.width>=u.width*.95&&U.bounds.height>=u.height*.95||He([U],u.height,u.width,!0))return!1;let Z=`${U.contentDesc} ${U.text}`.trim(),W=U.bounds.width>u.width*.15&&U.bounds.height>u.height*.05;return Z.toLowerCase().includes("developed by")||U.bounds.centerY>u.height*.85?!1:Z.length>=2||W});for(let U of $)T.some(Z=>Math.abs(Z.bounds.centerX-U.bounds.centerX)<30&&Math.abs(Z.bounds.centerY-U.bounds.centerY)<30)||T.push(U);let R=He(P.flat,u.height,u.width,!0);R?await this.driver.tap(R.bounds.centerX,R.bounds.centerY,s):await this.driver.tap(x.bounds.centerX,x.bounds.centerY,s),await new Promise(U=>setTimeout(U,1200));let C=await this.getCleanUiHierarchy(t,s);if(C.flat.some(U=>`${U.contentDesc} ${U.text}`.includes("Gallery")&&`${U.contentDesc} ${U.text}`.includes("Planner"))){let U=He(C.flat,u.height,u.width,!0);U?await this.driver.tap(U.bounds.centerX,U.bounds.centerY,s):await this.driver.tap(u.width-50,u.height/2,s),await new Promise(Z=>setTimeout(Z,1e3))}console.log(re.magenta(` Discovered ${T.length} destinations (1 Main Hub + ${T.length-1} Drawer destinations).`))}else b.roots.length>0?T=b.roots:T=[g[0]||{text:"Main Hub",clickable:!0}];let S=1;for(let y of T){if(this.executedStepCount>=i||this.visitedScreenHashes.size>=r||this.isAborted())break;let P=y.text||y.contentDesc||`Root #${S}`;if(console.log(re.yellow(`
878
+ [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 C=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,C,s),this.executedStepCount++,await new Promise(B=>setTimeout(B,1200))}else b.paradigm==="drawer"&&S>1&&x&&(await this.dismissSoftKeyboard(s),console.log(re.gray(` Opening drawer to select "${P}"...`)),await this.driver.tap(x.bounds.centerX,x.bounds.centerY,s),await new Promise(C=>setTimeout(C,1e3)),await this.driver.tap(y.bounds.centerX,y.bounds.centerY,s),this.executedStepCount++,await new Promise(C=>setTimeout(C,1500)));g=(await this.getCleanUiHierarchy(t,s)).flat,await this.dismissSoftKeyboard(s);let R=S===1?Math.max(25,Math.ceil(i*.45)):Math.max(8,Math.ceil(i*.55/Math.max(1,T.length-1)));if(await this.exploreDfsNode({packageName:t,serial:s,screenDims:u,rootNodes:b.roots,activeRootNode:y,currentDepth:0,maxDepth:n,maxScreens:r,stepBudget:i,domainStepLimit:this.executedStepCount+R,maxInteractionsPerScreen:o,safetyMode:l,safetyBlacklist:c,domainLabel:P}),b.paradigm==="drawer"&&S>1){let C=0;for(;C<4;){let B=await this.getCleanUiHierarchy(t,s),U=at(B.flat,u.height,u.width),Z=B.flat.some(J=>`${J.contentDesc} ${J.text}`.includes("Gallery")&&`${J.contentDesc} ${J.text}`.includes("Planner"));if(U&&!Z)break;let W=He(B.flat,u.height,u.width,!0);if(W)console.log(re.gray(" [Domain Wrap-Up] Returning to Home Screen via in-app back...")),await this.driver.tap(W.bounds.centerX,W.bounds.centerY,s),await new Promise(J=>setTimeout(J,1e3)),C++;else break}}if(S===1&&T.length===1){let C=await this.getCleanUiHierarchy(t,s),B=Ts(C.flat,u,t);if((B.paradigm==="bottom_tabs"||B.paradigm==="top_tabs")&&B.roots.length>1){console.log(re.magenta(`
879
+ \u26A1 Post-Auth Navigation Discovered: [${B.paradigm.toUpperCase()}] with ${B.roots.length} root domain(s)!`)),b.paradigm=B.paradigm,b.roots=B.roots;let U=B.roots.slice(1);T.push(...U.length>0?U:B.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(re.green(`
880
+ \u2714 Universal DFS Traversal Complete!`)),console.log(re.cyan(` Visited Screens: ${this.visitedScreenHashes.size}`)),console.log(re.cyan(` Total Steps Executed: ${this.executedStepCount}/${i}`)),console.log(re.cyan(` Defects Triaged: ${this.devDefectsList.length}`))}async exploreDfsNode(e){let{packageName:t,serial:s,screenDims:r,rootNodes:n,activeRootNode:i,currentDepth:o,maxDepth:l,maxScreens:c,stepBudget:p,domainStepLimit:d,maxInteractionsPerScreen:u,safetyMode:h,safetyBlacklist:g,domainLabel:b}=e;if(this.executedStepCount>=p||this.visitedScreenHashes.size>=c||d&&this.executedStepCount>=d||this.isAborted())return;let{flat:T}=await this.getCleanUiHierarchy(t,s),x=ye(T),S=Ue(T,r);this.visitedScreenHashes.add(x);let y=this.screenLedger.get(S);if(y){if(y.status==="FULLY_EXPLORED"){console.log(re.gray(` \u26A1 Cycle Detected: Screen [${S.slice(0,8)}] already fully explored. Pruning branch.`));return}}else{let C=this.catalogScreen(T,r,n);y={hash:x,semanticHash:S,title:`${b} (depth ${o})`,elements:C,status:"IN_PROGRESS",depth:o,interactionCount:0},this.screenLedger.set(S,y),console.log(re.cyan(` \u{1F50E} Discovered Screen [${S.slice(0,8)}] at depth ${o} with ${C.length} candidate element(s).`)),this.emitStep({type:"state_change",description:`Discovered screen "${b}" at depth ${o} with ${C.length} candidate element(s)`,screenHash:S,screenTitle:b,depth:o})}this.callStack.push({screenHash:x,semanticHash:S,title:y.title,timestamp:Date.now()});let P=y.elements.filter(C=>C.type==="input"&&!C.tested);if(P.length>0&&y.interactionCount<u){console.log(re.magenta(` Filling in-place form fields (${P.length} inputs)...`));let C=await this.formFiller.fillAndSubmitForm(T,s,!0,!1);this.executedStepCount++,y.interactionCount++;for(let B of P)B.tested=!0;await this.dismissSoftKeyboard(s),await this.packageJailGuard(t,s),this.reporter.logStep("In-Place Form Fill",`Populated ${C.filledFields.length} field(s) on [${S.slice(0,8)}]`,"FormSubmit","PASS",C.submitButtonText||"Auto");for(let B of C.filledFields)this.emitStep({type:"action",description:`Type "${B.valueEntered}" in the "${B.fieldLabel}" field`,screenHash:S,screenTitle:b,depth:o});C.submitButtonText&&this.emitStep({type:"action",description:`Tap "${C.submitButtonText}" to submit the form`,screenHash:S,screenTitle:b,depth:o})}let $=y.elements.filter(C=>C.type==="toggle"&&!C.tested);for(let C of $){if(this.executedStepCount>=p||y.interactionCount>=u)break;if(C.tested=!0,Je(C.node,g,h)){this.safetyHitList.push(C.label),this.emitStep({type:"safety_block",description:`Safety policy [${h.toUpperCase()}] blocked toggle "${C.label}"`,screenHash:S,screenTitle:b,depth:o});continue}console.log(re.gray(` Exercising toggle: "${C.label.slice(0,30)}"...`)),this.executedStepCount++,y.interactionCount++,await this.driver.tap(C.node.bounds.centerX,C.node.bounds.centerY,s),await new Promise(B=>setTimeout(B,600)),this.reporter.logStep("In-Place Toggle",`Toggled control "${C.label}"`,"Tap","PASS",C.label),this.emitStep({type:"action",description:`Toggle "${C.label}"`,screenHash:S,screenTitle:b,depth:o})}let R=y.elements.filter(C=>C.type==="transition"&&!C.tested);for(let C of R){if(this.executedStepCount>=p||this.visitedScreenHashes.size>=c||d&&this.executedStepCount>=d&&!(o===0&&b.includes("Home Screen"))||y.interactionCount>=u||this.isAborted())break;if(C.tested=!0,Ve(C.node)){this.externalSkippedList.push(C.label);continue}if(Je(C.node,g,h)){this.safetyHitList.push(C.label),this.emitStep({type:"safety_block",description:`Safety policy [${h.toUpperCase()}] blocked transition "${C.label}"`,screenHash:S,screenTitle:b,depth:o});continue}console.log(re.magenta(` \u26A1 [DFS Depth ${o}] Testing transition: "${C.label.slice(0,40)}"...`)),this.executedStepCount++,y.interactionCount++,await this.driver.tap(C.node.bounds.centerX,C.node.bounds.centerY,s),await new Promise(W=>setTimeout(W,1500)),await this.dismissSoftKeyboard(s),await this.handleExternalMediaChooser(t,s),await this.packageJailGuard(t,s);let B=await this.getCleanUiHierarchy(t,s),U=Ue(B.flat,r),Z=U!==S;if(this.reporter.logStep("DFS Transition",`Tapped "${C.label}" [${Z?"Navigated":"In-Place"}]`,"Tap","PASS",C.label),this.emitStep({type:"action",description:`Tap "${C.label}"${Z?" to navigate to the next screen":""}`,screenHash:S,screenTitle:b,depth:o}),Z&&U&&this.emitStep({type:"state_change",description:`Navigated to screen [${U.slice(0,8)}] via "${C.label}"`,screenHash:U,screenTitle:`${b} > ${C.label.slice(0,20)}`,depth:o+1}),Z){if(o<l){let Ee=o===0&&b.includes("Home Screen")?Math.min(this.executedStepCount+3,d||this.executedStepCount+3):d;await this.exploreDfsNode({packageName:t,serial:s,screenDims:r,rootNodes:n,activeRootNode:i,domainStepLimit:Ee,currentDepth:o+1,maxDepth:l,maxScreens:c,stepBudget:p,maxInteractionsPerScreen:u,safetyMode:h,safetyBlacklist:g,domainLabel:`${b} > ${C.label.slice(0,20)}`})}console.log(re.gray(` [DFS Backtrack] Returning to [${S.slice(0,8)}]...`)),await this.safeBacktrack(S,t,s,o===0?i:void 0,o>0),await this.dismissSoftKeyboard(s);let W=await this.getCleanUiHierarchy(t,s);if(Ue(W.flat,r)!==S){let Ee=He(W.flat,r.height,r.width,!0);Ee&&(console.log(re.gray(" [Backtrack Verification] Re-tapping in-app back/close button...")),await this.driver.tap(Ee.bounds.centerX,Ee.bounds.centerY,s),await new Promise(ie=>setTimeout(ie,1e3)),await this.packageJailGuard(t,s));let se=await this.getCleanUiHierarchy(t,s);if(Ue(se.flat,r)!==S){console.log(re.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()}};var yt=class{driver;formFiller;reporter;memory;safetyHitList=[];externalSkippedList=[];devDefectsList=[];visitedScreenHashes=new Set;executedStepCount=0;constructor(e,t,s,r){this.driver=e||Pe,this.formFiller=t||ui,this.reporter=s||dt,this.memory=r||_t}getMemory(){return this.memory}getSafetyHitList(){return this.safetyHitList}getExternalSkippedList(){return this.externalSkippedList}getDevDefectsList(){return this.devDefectsList}getVisitedScreensCount(){return this.visitedScreenHashes.size}async packageJailGuard(e,t){let s=await this.driver.getForegroundPackage(t);if(s&&!s.includes(e)){let r=At(s);console.log(M.yellow(` \u{1F6E1}\uFE0F Jail Containment: App escaped to "${s}". Executing containment recovery...`)),r||(await this.driver.pressBack(t),await new Promise(i=>setTimeout(i,600)));let n=await this.driver.getForegroundPackage(t);return(!n||!n.includes(e))&&(console.log(M.gray(` Force re-launching ${e} to restore foreground...`)),await this.driver.launchApp(e,t),await new Promise(i=>setTimeout(i,1200))),this.reporter.logStep("Jail Containment",`App escaped to "${s}"; successfully contained and restored "${e}"`,"Back","PASS"),!0}return!1}async getCleanUiHierarchy(e,t,s=5){let r=await this.driver.getUiHierarchy(t),{flat:n}=me(r),i=0;for(;i<s;){let o=Kt(n);if(!o)break;i++;let l=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,c=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(M.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 p=await this.driver.captureScreenshot(t);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${l}: "${o.errorText}"${c}`,"Verify","FAIL",o.errorText,p,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(M.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,t)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(M.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,t)):(console.log(M.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(t)),await new Promise(d=>setTimeout(d,800)),r=await this.driver.getUiHierarchy(t),n=me(r).flat}return{xml:r,flat:n}}async handleDevOverlays(e,t,s){let r=Kt(e);if(!r)return!1;let n=`[${(r.severity||"error").toUpperCase()} - ${r.type.toUpperCase()}]`,i=r.sourceLocation?` [${r.sourceLocation}]`:"";console.log(M.red(` \u{1F6A8} Dev Overlay Detected: ${n} "${r.errorText.slice(0,80)}"${i}`)),this.devDefectsList.push(r.sourceLocation?`${r.errorText} (${r.sourceLocation})`:r.errorText);let o=await this.driver.captureScreenshot(s);return this.reporter.logStep("Dev Overlay Triage",`Detected and triaged ${n} error: "${r.errorText}"${i}`,"Verify","FAIL",r.errorText,o,{errors:[r.sourceLocation?`${r.errorText} (${r.sourceLocation})`:r.errorText]}),r.dismissButton&&r.dismissButton.bounds.width>0?(console.log(M.gray(" Dismissing error overlay via dismiss button...")),await this.driver.tap(r.dismissButton.bounds.centerX,r.dismissButton.bounds.centerY,s)):r.isToast&&r.node&&r.node.bounds.width>0?(console.log(M.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(r.node.bounds.centerX,r.node.bounds.centerY,s)):(console.log(M.gray(" Pressing Back to dismiss error overlay...")),await this.driver.pressBack(s)),await new Promise(l=>setTimeout(l,800)),!0}async dismissSoftKeyboard(e){try{await this.driver.pressKey(111,e),await new Promise(t=>setTimeout(t,200))}catch{}}async explore(e){let{packageName:t,serial:s,maxTabs:r=I.explorer?.maxTabs||15,exploreLists:n=!0,skipAlreadyCrawled:i=!1,safetyMode:o=I.safety?.mode||"strict",safetyBlacklist:l=I.safety?.customBlacklist?[...I.safety.customBlacklist]:[],maxScreens:c=I.explorer?.maxScreens||30,stepBudget:p=I.explorer?.stepBudget||100,screenshots:d=I.explorer?.screenshots||"state-change",video:u=e.video??!1,onStep:h,abortSignal:g}=e;console.log(M.cyan(`
881
+ \u{1F916} PromptTest Autonomous Exploration Brain v3`)),console.log(M.gray("Target Package: ")+M.bold.white(t)),console.log(M.gray("Safety Policy: ")+M.bold.yellow(o.toUpperCase())+(l.length>0?M.gray(` (+${l.length} custom blacklist)`):"")),console.log(M.gray("Screenshots Policy: ")+M.bold.white(d));let b=[],T=0,x=C=>{T++,h?.({...C,stepNumber:T,packageName:t,totalSteps:C.totalSteps??this.executedStepCount,discoveredScreens:C.discoveredScreens??this.visitedScreenHashes.size})},S=async(C,B=!0)=>{if(!(d==="failure-only"&&C!=="FAIL")&&!(d==="state-change"&&!B&&C!=="FAIL"))try{return await this.driver.captureScreenshot(s)}catch{return}};await this.memory.init(t);let y=s||this.driver.getDefaultDevice(),P=y||"Default Device";this.reporter.init(t,P);let $=null;y&&($=Et(y));let R=null;this.safetyHitList=[],this.externalSkippedList=[],this.devDefectsList=[],this.visitedScreenHashes.clear(),this.executedStepCount=0;try{if(await this.driver.clearCrashLogs(s),u&&(console.log(M.cyan("\u{1F3AC} Starting ADB screen recording for exploration (--video enabled)...")),R=await this.driver.startScreenRecording("/sdcard/prompttest_explore_temp.mp4",s)),(e.strategy||I.explorer?.strategy||"heuristic")==="dfs"){let E=new ds(this.driver,this.reporter,this.memory);await E.explore({packageName:t,serial:s,maxScreens:c,maxDepth:e.maxDepth||I.explorer?.maxDepth||5,stepBudget:p,safetyMode:o,safetyBlacklist:l,screenshotPolicy:d,skipAlreadyCrawled:i,onStep:v=>x({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:g}),this.safetyHitList=E.safetyHitList,this.externalSkippedList=E.externalSkippedList,this.devDefectsList=E.devDefectsList,this.visitedScreenHashes=E.visitedScreenHashes,this.executedStepCount=E.executedStepCount;let L=this.memory.getStats();this.reporter.logStep("Coverage Audit",`State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s). Memory Engine cached ${L.totalScreens} crawled screen(s).`,"Verify","PASS"),await this.reporter.generateReport(),console.log(M.bold.green(`
882
+ \u2728 Full Audit Report generated at: output/prompttest-report.md`));return}let{width:B,height:U}=await this.driver.getScreenDimensions(s);console.log(M.yellow(`
883
+ [1/5] Verifying Foreground State...`));let Z=await this.driver.getForegroundPackage(s);(!Z||!Z.includes(t))&&(console.log(M.gray(`Launching ${t}...`)),await this.driver.launchApp(t,s),await new Promise(E=>setTimeout(E,2500)));let W=await this.runErrorSentinel(t,s,"Post-Launch"),J=W.hasError?"FAIL":"PASS",Ee=await S(J,!0);this.reporter.logStep("Init",`Launched and verified foreground for ${t}`,"Launch",J,t,Ee,W.hasError?{errors:W.errors}:void 0),console.log(J==="PASS"?M.green("\u2714 App is active in foreground."):M.red(`\u2716 Issues detected during launch for ${t}.`)),console.log(M.yellow(`
884
+ [2/5] Inspecting UI Hierarchy for Forms...`));let{xml:se,flat:j}=await this.getCleanUiHierarchy(t,s),ie=ye(j);if(this.visitedScreenHashes.add(ie),this.memory.isScreenCrawled(ie)&&i)console.log(M.cyan(` \u26A1 Fast-Forward: Screen [${ie.slice(0,8)}] was previously crawled offline. Skipping redundant element interactions.`)),this.reporter.logStep("Fast Forward",`Fast-forwarded past previously crawled screen [${ie.slice(0,8)}] (Memory Engine)`,"Skip","PASS");else{let E=Le(j);if(E.length>0){console.log(M.magenta(`\u26A1 Detected ${E.length} form field(s)! Engaging Semantic Form Filler...`));for(let K of E)console.log(M.gray(` - Field: "${K.text||K.contentDesc||K.resourceId}"`));let k=await this.formFiller.fillAndSubmitForm(j,s,!0,!1);this.executedStepCount++,await new Promise(K=>setTimeout(K,1500)),await this.dismissSoftKeyboard(s),await this.packageJailGuard(t,s);let F=await this.getCleanUiHierarchy(t,s);se=F.xml,j=F.flat,this.visitedScreenHashes.add(ye(j));let N=await this.runErrorSentinel(t,s,"Post-Form-Submit"),H=Le(j),ee=N.hasError||H.length===E.length&&N.errors.length>0?"FAIL":"PASS",G=await S(ee,!0),z=k.filledFields.map(K=>`${K.fieldLabel} -> "${K.valueEntered}"`).join(", ");this.reporter.logStep("Form Fill",`Filled ${k.filledFields.length} field(s) [${z}] and submitted via "${k.submitButtonText||"Auto"}"`,"FormSubmit",ee,k.submitButtonText,G,N.hasError?{errors:N.errors}:void 0),console.log(ee==="PASS"?M.green("\u2714 Form populated and submitted successfully!"):M.red("\u2716 Form submission triggered an error or remained unauthenticated."))}else console.log(M.gray("No form input fields detected on initial screen."));console.log(M.yellow(`
885
+ [3/5] Inspecting Interactive Action Controls...`));let L=Me(j).filter(k=>{let F=(k.text+" "+k.contentDesc).toLowerCase();if(F.includes("submit")||F.includes("cancel")||at([k],U))return!1;if(Ve(k)){let N=k.text||k.contentDesc||k.resourceId||"External Link";return console.log(M.gray(` \u{1F517} External Link Bypassed: "${N}"`)),this.externalSkippedList.push(N),!1}if(Je(k,l,o)){let N=k.text||k.contentDesc||k.resourceId||"Action";return console.log(M.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive action "${N}"`)),this.safetyHitList.push(N),!1}return!0});if(L.length>0){let k=L[0],F=k.text||k.contentDesc||k.resourceId;console.log(M.magenta(`\u26A1 Found action button: "${F}". Testing interaction...`));let N=ye(j);this.executedStepCount++,await this.driver.tap(k.bounds.centerX,k.bounds.centerY,s),await new Promise(ve=>setTimeout(ve,1200)),await this.packageJailGuard(t,s);let H=await this.getCleanUiHierarchy(t,s);se=H.xml,j=H.flat;let V=ye(j);this.visitedScreenHashes.add(V);let ee=await this.runErrorSentinel(t,s,"Post-Action-Tap"),G=N!==V,z="PASS",K="";ee.hasError?(z="FAIL",K="Action triggered error or crash"):G?(z="PASS",K="Screen transitioned successfully"):(z="WARN",K="No visible screen transition or state change observed (No-Op)",console.log(M.yellow(` \u26A0\uFE0F Outcome Warning: Action "${F}" produced no UI state change.`)));let he=await S(z,G);this.reporter.logStep("Action Testing",`Tapped action button "${F}" [${K}]`,"Tap",z,F,he,ee.hasError?{errors:ee.errors}:void 0),console.log(z==="PASS"?M.green(`\u2714 Action interaction complete (${K}).`):z==="WARN"?M.yellow(`\u2714 Action interaction recorded with warning (${K}).`):M.red("\u2716 Action interaction triggered an error.")),z!=="FAIL"&&(this.memory.recordSuccess("TAP",F,k.resourceId||k.text||k.contentDesc||F,"text",1,void 0,N),this.memory.markScreenCrawled(N,[F]))}else console.log(M.gray("No standalone action buttons to test."));let v=Ot(j).filter(k=>!Je(k,l,o));if(v.length>0){console.log(M.yellow(`
886
+ [3b/5] Inspecting Toggle & Checkable Controls...`));let k=v[0],F=k.text||k.contentDesc||k.resourceId||"Switch/Checkbox",N=k.checked;console.log(M.magenta(`\u26A1 Found toggle: "${F}" (checked: ${N}). Testing toggle interaction...`)),this.executedStepCount++,await this.driver.tap(k.bounds.centerX,k.bounds.centerY,s),await new Promise(ve=>setTimeout(ve,1e3)),se=await this.driver.getUiHierarchy(s),j=me(se).flat;let H=ye(j);this.visitedScreenHashes.add(H);let ee=Ot(j).find(ve=>Math.abs(ve.bounds.centerX-k.bounds.centerX)<25&&Math.abs(ve.bounds.centerY-k.bounds.centerY)<25),G=ee?ee.checked!==N:!0,z=await this.runErrorSentinel(t,s,"Post-Toggle"),K=z.hasError?"FAIL":G?"PASS":"WARN",he=await S(K,G);this.reporter.logStep("Toggle Testing",`Toggled "${F}" (Checked: ${N} -> ${ee?ee.checked:"flipped"})`,"Toggle",K,F,he,z.hasError?{errors:z.errors}:void 0),console.log(K==="PASS"?M.green("\u2714 Toggle control flipped successfully!"):K==="WARN"?M.yellow("\u26A0\uFE0F Toggle tapped but checked state did not flip."):M.red("\u2716 Toggle control triggered an error.")),K!=="FAIL"&&this.memory.markScreenCrawled(H,[F])}let A=at(j,U);if(A&&!Je(A,l,o)){let k=A.contentDesc||A.text||A.resourceId||"Drawer Menu";console.log(M.yellow(`
887
+ [3c/5] Inspecting Side Navigation Drawer...`)),console.log(M.magenta(`\u26A1 Found drawer trigger: "${k}". Opening side drawer...`));let F=ye(j);this.executedStepCount++,await this.driver.tap(A.bounds.centerX,A.bounds.centerY,s),await new Promise(K=>setTimeout(K,1200)),await this.packageJailGuard(t,s);let N=await this.getCleanUiHierarchy(t,s);se=N.xml,j=N.flat;let H=ye(j);this.visitedScreenHashes.add(H);let V=F!==H,ee=await this.runErrorSentinel(t,s,"Drawer-Open"),G=ee.hasError?"FAIL":V?"PASS":"WARN",z=await S(G,V);if(this.reporter.logStep("Drawer Navigation",`Opened side drawer via "${k}"`,"Tap",G,k,z,ee.hasError?{errors:ee.errors}:void 0),G==="PASS"){console.log(M.green("\u2714 Side drawer opened successfully!"));let K=Me(j).filter(ve=>!Je(ve,l,o)&&!Ve(ve)&&ve.bounds.centerY>A.bounds.y2);if(K.length>0){let ve=K[0],We=ve.text||ve.contentDesc||ve.resourceId||"Drawer Item";console.log(M.gray(` - Testing drawer item: "${We}"...`)),this.executedStepCount++,await this.driver.tap(ve.bounds.centerX,ve.bounds.centerY,s),await new Promise(be=>setTimeout(be,1200)),await this.packageJailGuard(t,s);let vt=await this.getCleanUiHierarchy(t,s);se=vt.xml,j=vt.flat,this.visitedScreenHashes.add(ye(j))}console.log(M.gray(" Closing side drawer to restore main view...")),await this.driver.pressBack(s),await new Promise(ve=>setTimeout(ve,1e3));let he=await this.getCleanUiHierarchy(t,s);se=he.xml,j=he.flat,this.visitedScreenHashes.add(ye(j)),console.log(M.green("\u2714 Main view restored from side drawer."))}else G==="WARN"&&console.log(M.yellow("\u26A0\uFE0F Drawer button tapped but no visible screen change occurred."));G!=="FAIL"&&this.memory.markScreenCrawled(F,[k])}if(n){console.log(M.yellow(`
888
+ [4/5] Inspecting Lists & Master-Detail Flows...`));let k=Ur(j);if(k.length>0){console.log(M.magenta(`\u26A1 Found ${k.length} scrollable container(s). Performing vertical scroll exploration...`));let N=ye(j),H=Math.round(B*.5);await this.driver.swipe(H,Math.round(U*.7),H,Math.round(U*.3),300,s),await new Promise(K=>setTimeout(K,1200));let V=await this.getCleanUiHierarchy(t,s);se=V.xml,j=V.flat;let ee=ye(j);this.visitedScreenHashes.add(ee);let G=N!==ee,z=await S("PASS",G);this.reporter.logStep("Scroll Exploration",G?"Scrolled vertically to discover off-screen elements":"Scrolled but content remained at edge","Swipe","PASS","SwipeUp",z),G&&console.log(M.green("\u2714 Vertical scroll succeeded; discovered off-screen content below fold."))}let F=zt(j).filter(N=>{if(Ve(N)){let H=N.text||N.contentDesc||"External Link";return this.externalSkippedList.push(H),!1}if(Je(N,l,o)){let H=N.text||N.contentDesc||"List Item";return console.log(M.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive list item "${H}"`)),this.safetyHitList.push(H),!1}return!0});if(F.length>0){let N=F[0],H=N.text||N.contentDesc||`Item at (${N.bounds.centerX}, ${N.bounds.centerY})`;console.log(M.magenta(`\u26A1 Found list item: "${H}". Diving into detail view...`));let V=ye(j);await this.driver.tap(N.bounds.centerX,N.bounds.centerY,s),await new Promise(Xe=>setTimeout(Xe,1800));let ee=await this.getCleanUiHierarchy(t,s);se=ee.xml,j=ee.flat;let G=ye(j);this.visitedScreenHashes.add(G);let z=await this.runErrorSentinel(t,s,"Detail-View"),K=V!==G,he=z.hasError?"FAIL":K?"PASS":"WARN",ve=await S(he,K);this.reporter.logStep("List Dive",`Dived into detail view for "${H}" [${K?"Navigated":"Unchanged"}]`,"Tap",he,H,ve,z.hasError?{errors:z.errors}:void 0),console.log(he==="PASS"?M.green("\u2714 Detail screen loaded."):he==="WARN"?M.yellow("\u26A0\uFE0F Detail view tapped but no navigation detected."):M.red("\u2716 Detail view triggered an error."));let We=He(ee.flat,I.screen.fallbackHeight,I.screen.fallbackWidth,!0);We&&We.bounds.width>0?(console.log(M.gray(`Navigating back via in-app back button "${We.text||We.contentDesc||We.className}"...`)),await this.driver.tap(We.bounds.centerX,We.bounds.centerY,s)):(console.log(M.gray("Navigating back to main view...")),await this.driver.pressKey(4,s)),await new Promise(Xe=>setTimeout(Xe,1200));let vt=await this.driver.getForegroundPackage(s);vt&&!vt.includes(t)&&(console.log(M.yellow(`Restoring ${t} foreground after back navigation...`)),await this.driver.launchApp(t,s),await new Promise(Xe=>setTimeout(Xe,2e3)));let be=await this.getCleanUiHierarchy(t,s);se=be.xml,j=be.flat,this.visitedScreenHashes.add(ye(j));let $e=await this.runErrorSentinel(t,s,"Post-Back-Restore"),it=$e.hasError?"FAIL":"PASS",es=await S(it,!0);this.reporter.logStep("List Dive","Navigated back to master screen","Back",it,"KEYCODE_BACK",es,$e.hasError?{errors:$e.errors}:void 0),it==="PASS"&&console.log(M.green("\u2714 Master view restored."))}else console.log(M.gray("No list items found for master-detail exploration."))}}console.log(M.yellow(`
889
+ [5/5] Inspecting Navigation Tabs...`));let f=await this.driver.getForegroundPackage(s);f&&!f.includes(t)&&(console.log(M.gray(`Restoring ${t} foreground for tab traversal...`)),await this.driver.launchApp(t,s),await new Promise(E=>setTimeout(E,2e3))),await this.dismissSoftKeyboard(s);let O=await this.getCleanUiHierarchy(t,s);se=O.xml,j=O.flat;let D=ot(j,void 0,t);if(D.length>1){console.log(M.magenta(`\u26A1 Found ${D.length} navigation tab(s). Traversal sequence initiated...`));let E=D.slice(0,r),L=1;for(let v of E){let A=v.text||v.contentDesc||`Tab #${L}`;console.log(M.gray(` - Visiting ${A}...`)),await this.dismissSoftKeyboard(s);let k=Math.min(v.bounds.centerY,v.bounds.y2-4);this.executedStepCount++,await this.driver.tap(v.bounds.centerX,k,s),await new Promise(G=>setTimeout(G,1500)),await this.packageJailGuard(t,s);let F=await this.getCleanUiHierarchy(t,s);se=F.xml,j=F.flat;let N=ye(j);this.visitedScreenHashes.add(N),this.memory.markScreenCrawled(N,[A]);let H=await this.runErrorSentinel(t,s,`Tab-${A}`),V=H.hasError?"FAIL":"PASS",ee=await S(V,!0);if(this.reporter.logStep("Tab Traversal",`Navigated to tab "${A}"`,"Tap",V,A,ee,H.hasError?{errors:H.errors}:void 0),console.log(V==="PASS"?M.green(` \u2714 Tab "${A}" loaded cleanly.`):M.red(` \u2716 Tab "${A}" triggered errors.`)),this.executedStepCount<p&&this.visitedScreenHashes.size<c){let G=[],z=U*.08,K=U*.88,he=be=>be.bounds.centerY>z&&be.bounds.centerY<K&&be.clickable&&be.bounds.width>30&&be.bounds.height>20&&D.every($e=>Math.abs($e.bounds.centerX-be.bounds.centerX)>40||Math.abs($e.bounds.centerY-be.bounds.centerY)>40),ve=zt(j,{width:B,height:U}).filter(he);for(let be of ve.slice(0,3))G.push({node:be,type:"list"});let We=Me(j).filter(be=>!(!he(be)||at([be],U)));for(let be of We.slice(0,4))G.push({node:be,type:"button"});let vt=Ot(j).filter(he);for(let be of vt.slice(0,2))G.push({node:be,type:"toggle"});for(let be of G){if(this.executedStepCount>=p||this.visitedScreenHashes.size>=c)break;let $e=be.node;if(Ve($e)){this.externalSkippedList.push($e.text||$e.contentDesc||"External Link");continue}if(Je($e,l,o)){let Nt=$e.text||$e.contentDesc||"Destructive Action";this.safetyHitList.push(Nt);continue}let it=$e.text||$e.contentDesc||$e.resourceId||`${be.type} element`;console.log(M.gray(` Sub-crawl [${A}]: Exercising ${be.type} "${it.slice(0,40)}"...`)),this.executedStepCount++,await this.driver.tap($e.bounds.centerX,$e.bounds.centerY,s),await new Promise(Nt=>setTimeout(Nt,1200)),await this.dismissSoftKeyboard(s),await this.packageJailGuard(t,s);let es=await this.getCleanUiHierarchy(t,s);se=es.xml,j=es.flat;let Xe=ye(j),Ii=!this.visitedScreenHashes.has(Xe);this.visitedScreenHashes.add(Xe);let hs=await this.runErrorSentinel(t,s,`Sub-${it.slice(0,30)}`),Er=hs.hasError?"FAIL":"PASS",Li=await S(Er,Ii);if(this.reporter.logStep("Tab Sub-Crawl",`Tested ${be.type} "${it}" under tab "${A}"`,"Tap",Er,it,Li,hs.hasError?{errors:hs.errors}:void 0),Xe!==N){console.log(M.cyan(` \u{1F50E} Screen transitioned. Discovered new screen state [${Xe.slice(0,8)}]`));let Nt=Me(j).filter(Ge=>he(Ge)&&!Je(Ge,l,o)&&!Ve(Ge));if(Nt.length>0&&this.executedStepCount<p){let Ge=Nt[0],Mi=Ge.text||Ge.contentDesc||"Child Action";console.log(M.gray(` Exercising child action "${Mi.slice(0,30)}"...`)),this.executedStepCount++,await this.driver.tap(Ge.bounds.centerX,Ge.bounds.centerY,s),await new Promise(Ri=>setTimeout(Ri,1e3)),await this.dismissSoftKeyboard(s)}console.log(M.gray(` Restoring ${A} master view via Back...`)),await this.driver.pressBack(s),await new Promise(Ge=>setTimeout(Ge,1e3)),await this.dismissSoftKeyboard(s);let kr=await this.getCleanUiHierarchy(t,s);se=kr.xml,j=kr.flat}}}L++}console.log(M.green("\u2714 Navigation tabs traversed successfully!"))}else console.log(M.gray("Single-screen or tabs not detected at screen footer."));for(let E of this.visitedScreenHashes)this.memory.markScreenCrawled(E);await this.memory.save();let we=this.memory.getStats();if(this.reporter.logStep("Coverage Audit",`State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s). Memory Engine cached ${we.crawledScreens} crawled screen(s).`,"Verify","PASS"),await this.runErrorSentinel(t,s,"Final-State"),R){console.log(M.cyan("\u{1F3AC} Finalizing exploration screen recording..."));let E=ur.resolve(process.cwd(),"output"),L=ur.join(E,`${t}-exploration.mp4`),v=await this.driver.stopScreenRecording(R,L,s);v&&An.existsSync(v)&&(this.reporter.setVideoPath?.(`${t}-exploration.mp4`),console.log(M.green(`\u2714 Screen recording saved to: output/${t}-exploration.mp4`))),R=null}let ue=await this.reporter.generateReport(),m=ue.failedSteps===0&&ue.errorsDetected.length===0;console.log(m?M.bold.green(`
890
+ \u2728 PromptTest Exploration Complete \u2014 All Steps Passed!`):M.bold.red(`
891
+ \u26A0\uFE0F PromptTest Exploration Complete \u2014 Issues Detected!`)),console.log(M.white(`Summary: ${ue.passedSteps} passed, ${ue.warnSteps} warnings, ${ue.failedSteps} failed, ${ue.errorsDetected.length} errors detected.`)),this.safetyHitList.length>0&&console.log(M.cyan(`\u{1F6E1}\uFE0F Safety Shield: Blocked ${this.safetyHitList.length} destructive action(s) [${this.safetyHitList.join(", ")}].`)),console.log(M.cyan(`\u{1F5FA}\uFE0F State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s).`)),console.log(M.cyan(`\u{1F9E0} Memory Engine Coverage: ${we.crawledScreens} crawled screen(s) indexed offline.`)),console.log(M.cyan(`Full Audit Report generated at: output/prompttest-report.md
892
+ `))}finally{if(R)try{await this.driver.stopScreenRecording(R,ur.join("output",`${t}-exploration.mp4`),s)}catch{}$&&kt($)}}async runErrorSentinel(e,t,s){let r=[];try{let[n,i]=await Promise.all([this.driver.checkCrashes(e,t),this.driver.getUiHierarchy(t)]);if(n){let c=`Crash in ${s}: ${n.slice(0,300)}`;console.log(M.bold.red(`\u{1F6A8} FATAL EXCEPTION DETECTED in ${e} [${s}]!`)),this.reporter.logError(c),r.push(c)}let{flat:o}=me(i),l=Hr(o);if(l.length>0)for(let c of l){let p=c.text||c.contentDesc,d=`UI Error dialog in ${s}: "${p}"`;console.log(M.bold.yellow(`\u26A0\uFE0F Error Dialog/Banner on screen [${s}]: "${p}"`)),this.reporter.logError(d),r.push(d)}}catch{}return{hasError:r.length>0,errors:r}}},hi=new yt;Ne();import hr from"fs";import gi from"path";function gr(a){return a?a.replace(/[\uE000-\uF8FF\uFFF0-\uFFFF]/gu,"").replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/gu,"").replace(/^[\s,.\-_/:;|]+|[\s,.\-_/:;|]+$/g,"").replace(/\s+/g," ").trim():""}function St(a){let e="";if(a.text&&a.text.trim()&&(e=gr(a.text.replace(/["\r\n]/g," "))),!e&&a.contentDesc&&a.contentDesc.trim()&&(e=gr(a.contentDesc.replace(/["\r\n]/g," "))),!e&&a.resourceId&&a.resourceId.includes("/")){let t=a.resourceId.split("/").pop()||"";e=gr(t.replace(/_/g," ").replace(/([a-z])([A-Z])/g,"$1 $2").replace(/\bbtn\b|\bedit\b|\btxt\b|\bview\b/gi,"").trim())}return e||"Field"}function mi(a,e){let t=[],s=e||(a[0]?.packageName?a[0].packageName:"com.example.app");t.push("# =============================================================="),t.push(`# Auto-generated test spec for ${s}`),t.push("# Generated by PromptTest Spec Scaffolder (SC-01)"),t.push(`# Created: ${new Date().toISOString()}`),t.push("# =============================================================="),t.push(""),t.push(`Launch "${s}"`),t.push("Wait 1s"),t.push("");let r=a.filter(h=>!h.clickable&&(h.text||h.contentDesc)&&h.bounds.y1<800&&h.bounds.height>20),n=new Set,i=r.map(h=>St(h)).filter(h=>!h||h==="Field"||h.length<2||n.has(h.toLowerCase())?!1:(n.add(h.toLowerCase()),!0)).slice(0,2);if(i.length>0){for(let h of i)t.push(`Assert "${h}" is visible`);t.push("")}let o=Le(a),l=new Set;for(let h of o){let g=St(h);if(!g||l.has(g.toLowerCase()))continue;l.add(g.toLowerCase());let b=g.toLowerCase();h.password||b.includes("password")||b.includes("pass")?t.push(`Type "SecretPass123!" into "${g}"`):b.includes("email")||b.includes("mail")?t.push(`Type "test.user@example.com" into "${g}"`):b.includes("phone")||b.includes("mobile")?t.push(`Type "+15551234567" into "${g}"`):b.includes("search")||b.includes("find")?t.push(`Type "Search query" into "${g}"`):t.push(`Type "Sample text" into "${g}"`)}o.length>0&&t.push("");let c=ot(a);if(c.length>0){let h=St(c[0]);h&&h!=="Field"&&(t.push(`Tap "${h}"`),t.push("Wait 500ms"),t.push(""))}let p=Me(a),d=new Set,u=[...p].sort((h,g)=>{let b=St(h).toLowerCase(),T=St(g).toLowerCase(),x=/login|sign in|submit|continue|next|search|save|confirm|done/i.test(b),S=/login|sign in|submit|continue|next|search|save|confirm|done/i.test(T);return x&&!S?-1:!x&&S?1:0});for(let h of u){let g=St(h);if(!(!g||d.has(g.toLowerCase()))&&(d.add(g.toLowerCase()),!/delete|remove|clear|logout|sign out|exit/i.test(g))){t.push(`Tap "${g}"`),t.push("Wait 1s");break}}if(i[0])t.push(`Assert "${i[0]}" is visible`);else{let h=a.map(g=>St(g)).find(g=>g&&g!=="Field"&&g.length>2&&!g.includes(s));h?t.push(`Assert "${h}" is visible`):t.push("Wait 1s")}return t.join(`
893
+ `)+`
894
+ `}function $n(a){let e=a.split(`
895
+ `),t=[];for(let s=0;s<e.length;s++){let r=e[s].trim();if(!r||r.startsWith("#"))continue;mt(r)||t.push(`Line ${s+1}: Unrecognized prompt syntax -> "${r}"`)}return{valid:t.length===0,errors:t}}async function fi(a={}){let e=a.driver||Pe,t=a.packageName;if(!t)try{t=await e.getForegroundPackage(a.deviceSerial)||void 0}catch{}if((!t||!t.trim())&&(t="com.example.app"),e&&typeof e.wakeDevice=="function"&&await e.wakeDevice(a.deviceSerial).catch(()=>{}),t&&t!=="com.example.app"&&typeof e.getForegroundPackage=="function")try{let h=await e.getForegroundPackage(a.deviceSerial);h&&h!==t&&!h.includes(t)&&typeof e.launchApp=="function"&&(await e.launchApp(t,a.deviceSerial),await new Promise(g=>setTimeout(g,1500)))}catch{}let s="";try{s=await e.getUiHierarchy(a.deviceSerial)}catch(h){throw new Error(`Failed to capture device UI hierarchy: ${h instanceof Error?h.message:String(h)}`)}let r=me(s),n=Array.isArray(r)?r:r.flat||r.root||[],i=n.filter(h=>h.packageName===t||!h.packageName?.includes("systemui")&&!h.packageName?.includes("launcher")),o=i.length>0?i:n,l=mi(o,t);if(a.journey){let h=Me(o),g=ot(o),b=h[0]||g[0];if(b&&b.bounds&&b.bounds.centerX&&b.bounds.centerY){let T=St(b);try{if(typeof e.tap=="function"){await e.tap(b.bounds.centerX,b.bounds.centerY,a.deviceSerial),await new Promise(R=>setTimeout(R,1500));let x=await e.getUiHierarchy(a.deviceSerial),S=me(x),y=Array.isArray(S)?S:S.flat||S.root||[],P=y.filter(R=>R.packageName===t||!R.packageName?.includes("systemui")&&!R.packageName?.includes("launcher")),$=P.length>0?P:y;if($.length>0){let C=mi($,t).split(`
896
+ `).filter(B=>!B.startsWith("Launch ")&&!B.startsWith("Restart "));l=["# === Screen 1: Initial Screen ===",l.trim(),"",`# === Journey Transition: Tap "${T||"Action"}" ===`,T?`Tap "${T}"`:`Tap ${b.bounds.centerX}, ${b.bounds.centerY}`,"Wait 1s","","# === Screen 2: Next Screen ===",C.join(`
897
+ `).trim(),""].join(`
898
+ `)}}}catch(x){console.warn(`Journey transition failed, falling back to single-screen scaffold: ${x.message}`)}}}let c=$n(l);c.valid||console.warn(`Scaffolding produced warnings:
899
+ ${c.errors.join(`
900
+ `)}`);let p=a.outputPath;if(!p){let h=gi.resolve(process.cwd(),"specs");hr.existsSync(h)||hr.mkdirSync(h,{recursive:!0});let g=t.replace(/[^a-zA-Z0-9_.-]/g,"_");p=gi.join(h,`${g}_scaffold.txt`)}hr.writeFileSync(p,l,"utf-8");let d=Le(o),u=Me(o);return{packageName:t,specContent:l,specPath:p,stepsCount:l.split(`
901
+ `).filter(h=>h.trim()&&!h.trim().startsWith("#")).length,inputCount:d.length,buttonCount:u.length}}import{execFile as Ct}from"child_process";var ps=class{defaultUdid;wdaBaseUrl;isMock;defaultDimensions={width:1170,height:2532};constructor(e={}){typeof e=="string"?(this.defaultUdid=e,this.isMock=!1):(this.defaultUdid=e.udid,this.wdaBaseUrl=e.wdaBaseUrl,this.isMock=e.useMock??!1),process.platform!=="darwin"&&!this.wdaBaseUrl&&this.isMock===void 0&&(this.isMock=!0)}getDefaultDevice(){return this.defaultUdid}async ensureDefaultDevice(e){if(e)return e;if(this.defaultUdid)return this.defaultUdid;let s=(await this.getConnectedDevices()).find(n=>n.state==="simulator"||n.state==="device");if(s)return this.defaultUdid=s.serial,s.serial;let r="booted-simulator";return this.defaultUdid=r,r}async getConnectedDevices(){return this.isMock||process.platform!=="darwin"?[{serial:this.defaultUdid||"00008101-001234567890001E",state:"simulator",model:"iPhone 15 Pro (Simulator)",platform:"ios"}]:new Promise(e=>{Ct("xcrun",["simctl","list","devices","available","--json"],(t,s)=>{if(t||!s){e([{serial:"booted-ios-simulator",state:"simulator",model:"iPhone Simulator",platform:"ios"}]);return}try{let r=JSON.parse(s),n=[],i=r.devices||{};for(let o of Object.keys(i)){let l=i[o];if(Array.isArray(l))for(let c of l)c.isAvailable&&c.state==="Booted"&&n.push({serial:c.udid,state:"simulator",model:`${c.name} (${o.split(".").pop()||"iOS"})`,platform:"ios"})}e(n.length>0?n:[{serial:"booted-ios-simulator",state:"simulator",model:"iPhone Simulator",platform:"ios"}])}catch{e([])}})})}async getScreenDimensions(e){return this.isMock||process.platform!=="darwin"?this.defaultDimensions:this.defaultDimensions}async captureScreenshot(e){let t=await this.ensureDefaultDevice(e);return this.isMock||process.platform!=="darwin"?Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==","base64"):new Promise((s,r)=>{Ct("xcrun",["simctl","io",t,"screenshot","--type=png","-"],{encoding:"buffer",maxBuffer:10*1024*1024},(n,i)=>{if(n)return r(n);s(i)})})}async getUiHierarchy(e){let t=`<?xml version="1.0" encoding="UTF-8"?>
901
902
  <hierarchy rotation="0">
902
903
  <XCUIElementTypeApplication type="XCUIElementTypeApplication" name="TargetApp" label="TargetApp" enabled="true" visible="true" x="0" y="0" width="390" height="844">
903
904
  <XCUIElementTypeWindow type="XCUIElementTypeWindow" enabled="true" visible="true" x="0" y="0" width="390" height="844">
@@ -906,18 +907,56 @@ data: ${JSON.stringify({status:"connected",time:Date.now()})}
906
907
  <XCUIElementTypeTextField type="XCUIElementTypeTextField" identifier="user_field" name="Username" label="Username" enabled="true" visible="true" x="40" y="120" width="310" height="44" />
907
908
  </XCUIElementTypeWindow>
908
909
  </XCUIElementTypeApplication>
909
- </hierarchy>`;if(this.isMock||process.platform!=="darwin")return t;if(this.wdaBaseUrl)try{let s=await fetch(`${this.wdaBaseUrl}/source?format=xml`);if(s.ok)return await s.text()}catch{}return t}async tap(e,t,s){let i=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:e,y:t})}).catch(()=>{});return}await new Promise(r=>{Tt("idb",["ui","tap",`${e}`,`${t}`,"--udid",i],()=>r())})}}async inputText(e,t){let s=await this.ensureDefaultDevice(t);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:e.split("")})}).catch(()=>{});return}await new Promise(i=>{Tt("idb",["ui","type",e,"--udid",s],()=>i())})}}async pressKey(e,t){this.isMock||process.platform!=="darwin"||(await this.ensureDefaultDevice(t),this.wdaBaseUrl&&await fetch(`${this.wdaBaseUrl}/wda/keys`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:[e]})}).catch(()=>{}))}async swipe(e,t,s,i,r=300,n){this.isMock||process.platform!=="darwin"||(await this.ensureDefaultDevice(n),this.wdaBaseUrl&&await fetch(`${this.wdaBaseUrl}/wda/dragfromtoforduration`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({fromX:e,fromY:t,toX:s,toY:i,duration:r/1e3})}).catch(()=>{}))}async launchApp(e,t){let s=await this.ensureDefaultDevice(t);if(!(this.isMock||process.platform!=="darwin"))return new Promise(i=>{Tt("xcrun",["simctl","launch",s,e],()=>i())})}async forceStop(e,t){let s=await this.ensureDefaultDevice(t);if(!(this.isMock||process.platform!=="darwin"))return new Promise(i=>{Tt("xcrun",["simctl","terminate",s,e],()=>i())})}async isAppInstalled(e,t){let s=await this.ensureDefaultDevice(t);return this.isMock||process.platform!=="darwin"?!0:new Promise(i=>{Tt("xcrun",["simctl","get_app_container",s,e],r=>{i(!r)})})}async getForegroundPackage(e){return"com.apple.mobilesafari"}};import Vt from"fs";import ii from"path";function on(a){let e=a.totalSteps||0,t=a.passedSteps||0,s=a.failedSteps||0,i=a.skippedSteps||0,r=a.errorsDetected?a.errorsDetected.length:0;if(e===0)return{score:0,grade:"F",verdict:"RELEASE_BLOCKER",rationale:"No test steps were executed.",metrics:{passRate:0,criticalFailures:0,visualDefects:0,totalSteps:0,skippedSteps:0}};let n=t/e,o=Math.min(30,r*10),c=s>0?35+Math.min(25,s*5):0,l=Math.round(n*80+20-o-c);l=Math.max(0,Math.min(100,l));let p="F",d="RELEASE_BLOCKER",h="";return l>=90?(p="A",d="READY_FOR_RELEASE",h="High quality gate confidence. All critical assertion paths verified."):l>=80?(p="B",d="READY_FOR_RELEASE",h="Acceptable release confidence. Minor non-critical or optional warnings observed."):l>=70?(p="C",d="PROCEED_WITH_CAUTION",h="Elevated risk. Visual QA warnings or multiple non-critical step failures detected."):(p="F",d="RELEASE_BLOCKER",h=`Critical test failures (${s}) or visual defects (${r}) block safe release.`),{score:l,grade:p,verdict:d,rationale:h,metrics:{passRate:Math.round(n*100)/100,criticalFailures:s,visualDefects:r,totalSteps:e,skippedSteps:i}}}function Jl(a="output"){let e=[],t=u=>{if(!Vt.existsSync(u))return;let m=Vt.readdirSync(u,{withFileTypes:!0});for(let y of m){let v=ii.join(u,y.name);y.isDirectory()?t(v):y.isFile()&&(y.name.endsWith("-results.json")||y.name.endsWith("-report.json"))&&e.push(v)}};if(Array.isArray(a))for(let u of a)Vt.existsSync(u)&&(Vt.statSync(u).isDirectory()?t(u):e.push(u));else t(a);let s=[],i=new Set,r=0,n=0,o=0,c=0,l=[];for(let u of e)try{let m=Vt.readFileSync(u,"utf-8"),y=JSON.parse(m),v=on(y),T=y.deviceId||"unknown-device";i.add(T),r+=y.totalSteps||0,n+=y.passedSteps||0,o+=y.failedSteps||0,c+=y.skippedSteps||0,y.errorsDetected&&l.push(...y.errorsDetected),s.push({deviceId:T,suiteName:ii.basename(u,ii.extname(u)).replace(/-results|-report/,""),packageName:y.packageName||"unknown",totalSteps:y.totalSteps||0,passedSteps:y.passedSteps||0,failedSteps:y.failedSteps||0,skippedSteps:y.skippedSteps||0,durationSeconds:y.durationSeconds||0,confidenceScore:v.score,confidenceGrade:v.grade,sourceFile:u})}catch{}let p=r>0?Math.round(n/r*100)/100:0,d={packageName:s[0]?.packageName||"Enterprise Matrix",deviceId:`${i.size} devices`,startTime:new Date().toISOString(),endTime:new Date().toISOString(),durationSeconds:s.reduce((u,m)=>u+m.durationSeconds,0),totalSteps:r,passedSteps:n,failedSteps:o,skippedSteps:c,warnSteps:0,errorsDetected:l,steps:[]},h=on(d);return{timestamp:new Date().toISOString(),totalRuns:s.length,uniqueDevices:Array.from(i),totalSteps:r,passedSteps:n,failedSteps:o,skippedSteps:c,overallPassRate:p,overallConfidence:h,deviceSummaries:s}}function Xl(a){let e=a.overallConfidence,t=e.grade==="A"?"#10B981":e.grade==="B"?"#3B82F6":e.grade==="C"?"#F59E0B":"#EF4444",s=a.deviceSummaries.map(i=>{let r=i.confidenceGrade==="A"?"#10B981":i.confidenceGrade==="B"?"#3B82F6":i.confidenceGrade==="C"?"#F59E0B":"#EF4444";return`
910
+ </hierarchy>`;if(this.isMock||process.platform!=="darwin")return t;if(this.wdaBaseUrl)try{let s=await fetch(`${this.wdaBaseUrl}/source?format=xml`);if(s.ok)return await s.text()}catch{}return t}async tap(e,t,s){let r=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:e,y:t})}).catch(()=>{});return}await new Promise(n=>{Ct("idb",["ui","tap",`${e}`,`${t}`,"--udid",r],()=>n())})}}async inputText(e,t){let s=await this.ensureDefaultDevice(t);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:e.split("")})}).catch(()=>{});return}await new Promise(r=>{Ct("idb",["ui","type",e,"--udid",s],()=>r())})}}async pressKey(e,t){this.isMock||process.platform!=="darwin"||(await this.ensureDefaultDevice(t),this.wdaBaseUrl&&await fetch(`${this.wdaBaseUrl}/wda/keys`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:[e]})}).catch(()=>{}))}async swipe(e,t,s,r,n=300,i){this.isMock||process.platform!=="darwin"||(await this.ensureDefaultDevice(i),this.wdaBaseUrl&&await fetch(`${this.wdaBaseUrl}/wda/dragfromtoforduration`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({fromX:e,fromY:t,toX:s,toY:r,duration:n/1e3})}).catch(()=>{}))}async launchApp(e,t){let s=await this.ensureDefaultDevice(t);if(!(this.isMock||process.platform!=="darwin"))return new Promise(r=>{Ct("xcrun",["simctl","launch",s,e],()=>r())})}async forceStop(e,t){let s=await this.ensureDefaultDevice(t);if(!(this.isMock||process.platform!=="darwin"))return new Promise(r=>{Ct("xcrun",["simctl","terminate",s,e],()=>r())})}async isAppInstalled(e,t){let s=await this.ensureDefaultDevice(t);return this.isMock||process.platform!=="darwin"?!0:new Promise(r=>{Ct("xcrun",["simctl","get_app_container",s,e],n=>{r(!n)})})}async getForegroundPackage(e){return"com.apple.mobilesafari"}};var In={".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"};async function yi(a={}){let e=a.port??4040,t=a.host||"127.0.0.1",s=process.cwd();(s==="/"||s==="\\")&&(s=bi.homedir?bi.homedir():process.env.HOME||process.env.USERPROFILE||"/tmp");let r=a.outputDir?X.resolve(a.outputDir):X.resolve(s,"output"),n=a.suiteName||"PromptTest Live Monitor",i=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 w of o)try{w.write(`: heartbeat
911
+
912
+ `)}catch{o.delete(w)}},15e3),c=a.driver||new nt,p=X.dirname(Nn(import.meta.url)),d=a.specsDir?X.resolve(a.specsDir):X.resolve(process.cwd(),"specs");if(!Q.existsSync(d)){let w=X.resolve(p,"../specs");Q.existsSync(w)&&(d=w)}let u=null;function h(){return u||(u=new Ke(c)),u}let g=X.join(r,"baselines"),b=new gt(g),T=null,x=null,S=["strict","moderate","interactive","disabled"],y=null,P=0;function $(w){return w?{success:!0,sessionId:w.id,status:w.status,packageName:w.packageName,deviceId:w.deviceId,safetyMode:w.safetyMode,maxDepth:w.maxDepth,maxSteps:w.maxSteps,maxScreens:w.maxScreens,screensDiscovered:w.screensDiscovered,elementsMapped:w.elementsMapped,stepsTaken:w.stepsTaken,startedAt:w.startedAt,finishedAt:w.finishedAt,crashes:w.crashes,screens:w.screens,blocked:w.blocked,events:w.events,specLines:w.specLines,error:w.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 R(w){let f=w.match(/"([^"]+)"/);return f?f[1]:null}function C(w){let f=w.specLines.length>0?w.specLines:["Launch the app"],O=["# Auto-generated by PromptTest Auto-Pilot (Autonomous Explorer)",`# Package: ${w.packageName}`,`# Safety Policy: ${w.safetyMode.toUpperCase()} | Max Depth: ${w.maxDepth} | Step Budget: ${w.maxSteps}`,`# Explored at: ${w.startedAt}`,`# Screens Discovered: ${w.screensDiscovered} | Steps Executed: ${w.stepsTaken}`];return w.deviceId&&O.push(`# Device: ${w.deviceId}`),[...O,"",...f,""].join(`
913
+ `)}function B(w,f,O){w.status=f,w.finishedAt=new Date().toISOString(),O&&(w.error=O),W("explore",{...$(w),event:{type:f==="failed"?"error":"done",description:O||f}})}function U(w){let f=y;if(f){if(typeof w.totalSteps=="number"&&(f.stepsTaken=Math.max(f.stepsTaken,w.totalSteps)),typeof w.discoveredScreens=="number"&&(f.screensDiscovered=Math.max(f.screensDiscovered,w.discoveredScreens)),w.type==="action"){let O=R(w.description);O&&f.elementLabels.add(O),w.description&&f.specLines[f.specLines.length-1]!==w.description&&f.specLines.push(w.description)}if(w.screenHash){let O=w.screenTitle||w.description||w.screenHash.slice(0,8),D=f.screens.find(we=>we.hash===w.screenHash);D?(D.visits++,typeof w.elementCount=="number"&&(D.elements=w.elementCount),D.label===D.hash.slice(0,8)&&w.screenTitle&&(D.label=w.screenTitle,D.title=w.screenTitle)):f.screens.push({id:w.screenHash,hash:w.screenHash,depth:w.depth??f.screens.length,title:O,label:O,visits:1,elements:w.elementCount??0}),f.screensDiscovered=Math.max(f.screensDiscovered,f.screens.length)}if(w.type==="crash"){let O=new Date().toISOString(),D=w.screenHash,we=D?f.screens.find(ue=>ue.hash===D):void 0;f.crashes.push({id:D||`crash-${f.crashes.length+1}`,type:w.description.toLowerCase().includes("anr")?"anr":"crash",message:w.description,screen:we?.label||D?.slice(0,8),timestamp:O,description:w.description,screenHash:D})}w.type==="safety_block"&&f.blocked.push(w.description),f.elementsMapped=f.elementLabels.size,f.events.push(w),f.events.length>300&&f.events.shift(),W("explore",{...$(f),event:w})}}function Z(w){let f=S.includes(w.safetyMode||"")?w.safetyMode:"strict";P++;let O={id:`explore-${Date.now()}-${P}`,packageName:w.packageName,deviceId:w.deviceId,safetyMode:f,maxDepth:w.maxDepth??5,maxSteps:w.maxSteps??100,maxScreens:w.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=O;let D=a.explorerFactory?a.explorerFactory(c):new yt(c);return W("explore",{...$(O),event:{type:"state_change",description:`Auto-Pilot launched on ${O.packageName} (${O.safetyMode} mode)`}}),D.explore({packageName:O.packageName,serial:O.deviceId,strategy:"dfs",safetyMode:O.safetyMode,maxDepth:O.maxDepth,maxScreens:O.maxScreens,stepBudget:O.maxSteps,screenshots:"state-change",onStep:U,abortSignal:O.abort}).then(()=>{B(O,O.abort.aborted?"stopped":"completed")}).catch(we=>{B(O,O.abort.aborted?"stopped":"failed",we instanceof Error?we.message:String(we))}),O}function W(w,f){let O=`event: ${w}
914
+ data: ${JSON.stringify(f)}
915
+
916
+ `;for(let D of o)try{D.write(O)}catch{o.delete(D)}}function J(w){return new Promise((f,O)=>{let D="";w.on("data",we=>{D+=we}),w.on("end",()=>{try{f(D?JSON.parse(D):{})}catch{O(new Error("Invalid JSON payload"))}}),w.on("error",O)})}let Ee=Cn.createServer(async(w,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"),w.method==="OPTIONS"){f.writeHead(204),f.end();return}let O=new URL(w.url||"/",`http://${t}:${e}`),D=decodeURIComponent(O.pathname);if(D==="/api/devices"){try{let[m,E]=await Promise.all([c.getRawConnectedDevices().catch(()=>[]),new ps().getConnectedDevices().catch(()=>[])]),L=c.deduplicateDevices(m).map(k=>({...k,platform:"android"})),v=(E||[]).map(k=>({serial:k.serial,state:k.state,model:k.model||"iOS Device",product:"iOS",connectionType:k.state==="simulator"?"emulator":"usb",platform:"ios"})),A=[...L,...v];f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,devices:A,allEndpoints:m}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/screen-state"){try{let m=O.searchParams.get("deviceId")||void 0,E=O.searchParams.get("hierarchy")!=="false",[L,v,A]=await Promise.all([c.captureScreenshot(m),E?c.getUiHierarchy(m):Promise.resolve(null),c.getForegroundPackage(m)]),k=[];if(v){let{flat:F}=me(v);k=F.map(N=>({index:N.index,text:N.text,resourceId:N.resourceId,className:N.className,contentDesc:N.contentDesc,clickable:N.clickable,scrollable:N.scrollable,bounds:N.bounds}))}f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,deviceId:m||c.getDefaultDevice()||"default",foregroundApp:A,screenshot:`data:image/png;base64,${L.toString("base64")}`,elements:E?k:void 0,elementCount:k.length}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/tap"&&w.method==="POST"){try{let m=await J(w);if(typeof m.x!="number"||typeof m.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(m.x,m.y,m.deviceId),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,tapped:{x:m.x,y:m.y}}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/type"&&w.method==="POST"){try{let m=await J(w);if(typeof m.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(m.text,m.deviceId),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,typed:m.text}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/key"&&w.method==="POST"){try{let m=await J(w);if(m.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(m.keyCode,m.deviceId),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,keyCode:m.keyCode}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/wake"&&w.method==="POST"){try{let m=await J(w);await c.wakeDevice(m.deviceId),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/wifi/connect"&&w.method==="POST"){try{let m=await J(w),E=await c.connectWifi(m.target,m.port||5555,m.deviceId);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:E.success,target:E.target,message:E.message}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/wifi/disconnect"&&w.method==="POST"){try{let m=await J(w),E=await c.disconnectWifi(m.target);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:E}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/wifi/pair"&&w.method==="POST"){try{let m=await J(w);if(!m.target||!m.pairingCode){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing target or pairingCode"}));return}let E=await c.pairWifi(m.target,m.pairingCode);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify(E))}catch(m){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/devices/install-apk"&&w.method==="POST"){try{let m=O.searchParams,E=m.get("deviceId")||void 0,L=m.get("targetDevices"),v=L?L.split(",").filter(Boolean):E?[E]:[],A=m.get("fileName")||`app_${Date.now()}.apk`,k=X.basename(A).endsWith(".apk")?X.basename(A):`${X.basename(A)}.apk`,F=X.join(r,"_apk_temp");Q.existsSync(F)||Q.mkdirSync(F,{recursive:!0});let N=X.join(F,`${Date.now()}_${k}`),H=Q.createWriteStream(N);await new Promise((K,he)=>{w.pipe(H),w.on("error",he),H.on("error",he),H.on("finish",K)});let V=v;if(V.length===0){let K=c.getDefaultDevice();K&&(V=[K])}if(V.length===0&&(V=(await c.getConnectedDevices()).filter(he=>he.state==="device").map(he=>he.serial)),V.length===0){try{await Q.promises.unlink(N)}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 ee=await Promise.allSettled(V.map(K=>c.installApk(N,K)));try{await Q.promises.unlink(N)}catch{}let G=ee.filter(K=>K.status==="fulfilled").length,z=[];ee.forEach((K,he)=>{if(K.status==="rejected"){let ve=K.reason instanceof Error?K.reason.message:String(K.reason);z.push(`${V[he]}: ${ve}`)}}),G>0?(W("apk-installed",{fileName:k,installedCount:G,targets:V}),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:`Successfully installed "${k}" on ${G} device(s)${z.length>0?` (Failed: ${z.join(", ")})`:""}`,installedCount:G}))):(f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Failed to install APK: ${z.join("; ")}`})))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/metrics"&&w.method==="GET"){try{let m=O.searchParams.get("deviceId")||void 0,E=await c.getDeviceMetrics(m);if(Object.keys(E).length===0)throw new Error(`No metrics available for device ${m||"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:E}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/doctor"&&w.method==="GET"){try{let m=O.searchParams.get("deviceId")||void 0,E=O.searchParams.get("packageName")||void 0,L=await ls(c,{serial:m,packageName:E});f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,report:L}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if((D==="/api/explore/start"||D==="/api/explore")&&w.method==="POST"){try{let m=await J(w),E=(m.packageName||m.targetPackage||"").trim();if(!E){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 L=Z({packageName:E,deviceId:m.deviceId||m.serial,safetyMode:m.safetyMode||m.safety,maxDepth:m.maxDepth??m.depth,maxSteps:m.maxSteps??m.stepBudget,maxScreens:m.maxScreens});f.writeHead(202,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,sessionId:L.id,status:L.status,packageName:L.packageName}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if((D==="/api/explore/status"||D==="/api/explore")&&w.method==="GET"){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify($(y)));return}if((D==="/api/explore/stop"||D==="/api/explore")&&(w.method==="POST"||w.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(D==="/api/explore/export"&&w.method==="POST"){try{let m=await J(w);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 E=(m.specName||`explored_${y.packageName}.txt`).trim(),L=X.basename(E).replace(/[^a-zA-Z0-9._-]/g,"_"),v=L.endsWith(".txt")?L:`${L}.txt`,A=C(y);await Q.promises.mkdir(d,{recursive:!0}),await Q.promises.writeFile(X.join(d,v),A,"utf8"),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,specName:v,content:A,screensDiscovered:y.screensDiscovered,stepsTaken:y.stepsTaken}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/record/start"&&w.method==="POST"){try{let m=await J(w);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 E=m.deviceId||c.getDefaultDevice()||void 0,L=m.specName?X.join(d,X.basename(m.specName)):void 0;T=new ft(c),x=E||"default",T.onStep=v=>{W("record-step",{step:v,deviceId:E})},await T.startSession(L,E,{append:m.append}),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:"Recording session started",deviceId:E}))}catch(m){if(T)try{await T.stopSession()}catch{}T=null,x=null,f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/record/stop"&&w.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 m=await T.stopSession();T=null,x=null,f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,steps:m.steps,specPath:m.specPath,reportPath:m.reportPath}))}catch(m){T=null,x=null,f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/record/status"&&w.method==="GET"){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,isRecording:T!==null,deviceId:x}));return}if(D==="/api/specs"&&w.method==="GET"){try{Q.existsSync(d)||Q.mkdirSync(d,{recursive:!0});let E=Q.readdirSync(d).filter(L=>L.endsWith(".txt")||L.endsWith(".spec")).map(L=>{let v=X.join(d,L),A=Q.statSync(v);return{name:L,path:X.relative(process.cwd(),v).replace(/\\/g,"/"),size:A.size,modified:A.mtime.toISOString()}});f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,specs:E}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if((D.startsWith("/api/specs/")||D==="/api/spec")&&w.method==="GET"){try{let m="";D.startsWith("/api/specs/")?m=decodeURIComponent(D.slice(11)):m=O.searchParams.get("name")||"";let E=X.basename(m.trim());if(!E){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing spec file name"}));return}let L=X.join(d,E);if(!Q.existsSync(L)){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Spec file not found: ${E}`}));return}let v=Q.readFileSync(L,"utf-8");f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,name:E,content:v}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if((D==="/api/specs"||D==="/api/spec")&&w.method==="POST"){try{let m=await J(w),E=(m.name||"").trim();if(!E){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing spec file name"}));return}!E.endsWith(".txt")&&!E.endsWith(".spec")&&(E+=".txt");let L=X.basename(E);Q.existsSync(d)||Q.mkdirSync(d,{recursive:!0});let v=X.join(d,L);Q.writeFileSync(v,m.content??"","utf-8"),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,name:L,path:v}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/specs/apply-heals"&&w.method==="POST"){try{let m=await J(w);if(!m.specName||!Array.isArray(m.heals)||m.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 E=X.basename(m.specName),L=X.join(d,E);if(!Q.existsSync(L)){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Spec file not found: ${E}`}));return}let v=Q.readFileSync(L,"utf-8"),A=0;for(let k of m.heals){if(!k.original||!k.healed||k.original===k.healed)continue;let F=k.original.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),N=new RegExp(`"${F}"`,"g"),H=new RegExp(`'${F}'`,"g");(N.test(v)||H.test(v))&&(v=v.replace(N,`"${k.healed}"`),v=v.replace(H,`'${k.healed}'`),A++)}Q.writeFileSync(L,v,"utf-8"),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,specName:E,replacementsCount:A,content:v}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/logcat"&&w.method==="GET"){try{let m=(O.searchParams.get("deviceId")||c.getDefaultDevice())??void 0,E=parseInt(O.searchParams.get("lines")||"200",10),L=isNaN(E)?200:Math.min(Math.max(E,20),1e3),v=O.searchParams.get("filter")?.trim(),k=(await c.getLogcatTail(L,m)).split(`
917
+ `).filter(F=>F.trim().length>0);if(v){let F=v.toLowerCase();k=k.filter(N=>N.toLowerCase().includes(F))}f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,count:k.length,logs:k}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/logcat/clear"&&w.method==="POST"){try{let m=(O.searchParams.get("deviceId")||c.getDefaultDevice())??void 0;await c.execAdb(["logcat","-c"],m).catch(()=>{}),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/scaffold"&&w.method==="POST"){try{let m=await J(w),E=m.deviceId||c.getDefaultDevice()||void 0,L=m.packageName?.trim()||void 0,v=(m.specName||"").trim();v||(v=L?`scaffold_${L}.txt`:"scaffold_app.txt"),!v.endsWith(".txt")&&!v.endsWith(".spec")&&(v+=".txt");let A=X.basename(v).replace(/[^a-zA-Z0-9._-]/g,"_"),k=X.join(d,A),F=await fi({driver:c,deviceSerial:E,packageName:L,outputPath:k,journey:m.journey});f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,specName:A,content:F.specContent,packageName:F.packageName,inputCount:F.inputCount,buttonCount:F.buttonCount,stepsCount:F.stepsCount}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/expand"&&w.method==="POST"){try{let m=await J(w);if(!m.spec||typeof m.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 E=h(),{expanded:L,stepsAdded:v}=await E.expandSpec(m.spec);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,expanded:L,stepsAdded:v}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/action"&&w.method==="POST"){try{let m=await J(w);if(!m.step||typeof m.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 E=mt(m.step);if(E.length===0){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Could not parse step: "${m.step}"`}));return}let L=h(),v=E[0],A=m.deviceId||c.getDefaultDevice()||void 0,k=Date.now(),F=await L.executeStep(v,A),N=Date.now()-k;W("step",{stepNumber:i.steps.length+1,instruction:v.originalText,status:"PASS",durationMs:N}),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,step:v.originalText,type:v.type,durationMs:N,result:F||null}))}catch(m){let E=m instanceof Error?m.message:String(m);W("step",{stepNumber:i.steps.length+1,instruction:"Step Execution",status:"FAIL",durationMs:0,error:E}),f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:E}))}return}if(D==="/api/run"&&w.method==="POST"){try{let m=await J(w),E="";if(m.specName){let k=X.basename(m.specName);if(E=X.join(d,k),m.spec&&typeof m.spec=="string"&&m.spec.trim().length>0)Q.writeFileSync(E,m.spec,"utf-8");else if(!Q.existsSync(E)){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Spec file not found: ${k}`}));return}}else if(m.spec&&typeof m.spec=="string")Q.existsSync(r)||Q.mkdirSync(r,{recursive:!0}),E=X.join(r,"_studio_temp_spec.txt"),Q.writeFileSync(E,m.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 L=m.dataFile;if(m.dataFileContent&&typeof m.dataFileContent=="string"){Q.existsSync(r)||Q.mkdirSync(r,{recursive:!0});let k=m.dataFile?X.basename(m.dataFile):"_studio_data.csv";L=X.join(r,k),Q.writeFileSync(L,m.dataFileContent,"utf-8")}let v=m.deviceId||c.getDefaultDevice()||void 0,A=h();i.steps=[],i.totalSteps=0,i.passedSteps=0,i.failedSteps=0,i.warnSteps=0,i.skippedSteps=0,(async()=>{let k;try{let N=await c.getForegroundPackage(v);N&&!N.includes("Launcher")&&!N.includes("Foreground")&&(k=N)}catch{}let F=async(N,H)=>{let V=H?dt:new lt(r),ee=H?A:new Ke(c,void 0,V),G=V.onStep?.(z=>{let K=!!(z.details?.healed||z.details?.healedFrom||z.description?.toLowerCase().includes("healed")),he=z.details;W("step",{deviceId:N,stepNumber:z.stepIndex,instruction:z.description,status:z.status,durationMs:z.durationMs||0,error:z.details?.error,healed:K,healedFrom:he?.healedFrom,healedTo:he?.healedTo})});try{let z=await ee.runSpec(E,{serial:N,packageName:k,saveBaseline:H&&m.saveBaseline!==void 0?m.saveBaseline:!1,compareBaseline:H&&m.compareBaseline!==void 0?m.compareBaseline:!0,screenshotMode:H?"all":"on-failure",heal:m.heal??!1,fresh:m.fresh??!1,continueOnError:m.continueOnError??!1,tags:m.tags,only:m.only,dataFile:L,iterations:m.iterations,profile:m.profile??!1});return W("device-complete",{deviceId:N,report:z}),z}finally{G&&G()}};try{let N=Array.isArray(m.targetDevices)&&m.targetDevices.length>0;if(N||m.allDevices){let H=[];N?H=m.targetDevices:H=(await c.getConnectedDevices()).filter(z=>z.state==="device").map(z=>z.serial),H=Array.from(new Set(H.filter(Boolean))),v&&H.includes(v)?H=[v,...H.filter(G=>G!==v)]:H.length===0&&v&&H.push(v);let V=await Promise.allSettled(H.map((G,z)=>F(G,z===0))),ee=[];for(let G of V)G.status==="fulfilled"&&ee.push(G.value);ee.length>0&&(i=ee[0]),W("complete",{multiDevice:!0,reports:ee,currentReport:i})}else{let H=await F(v||"default",!0);i=H,W("complete",H)}}catch(N){let H=N instanceof Error?N.message:String(N);i.errorsDetected.push(H),W("error",{error:H}),W("complete",i)}})(),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:"Execution started"}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/reports"&&w.method==="GET"){try{if(!Q.existsSync(r)){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,reports:[]}));return}let L=Q.readdirSync(r).filter(v=>v.endsWith("-results.json")).map(v=>{let A=v.replace(/-results\.json$/,""),k=X.join(r,v),F=`${A}-report.html`,N=Q.existsSync(X.join(r,F)),H=Q.statSync(k);try{let V=JSON.parse(Q.readFileSync(k,"utf-8")),ee=V.totalSteps||V.steps?.length||0,G=V.passedSteps||0,z=V.failedSteps||0,K=ee>0?Math.round(G/ee*100):100;return{id:A,fileName:v,suiteName:V.packageName||A,packageName:V.packageName||"Unknown",deviceId:V.deviceId||"Unknown",startTime:V.startTime||H.mtime.toISOString(),endTime:V.endTime||H.mtime.toISOString(),durationSeconds:V.durationSeconds||0,totalSteps:ee,passedSteps:G,failedSteps:z,warnSteps:V.warnSteps||0,skippedSteps:V.skippedSteps||0,passRate:K,errorsCount:V.errorsDetected?.length||0,htmlReportPath:N?`/${F}`:null,jsonReportPath:`/${v}`}}catch{return null}}).filter(Boolean).sort((v,A)=>new Date(A.startTime).getTime()-new Date(v.startTime).getTime());f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,reports:L}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if((D.startsWith("/api/reports/")||D==="/api/report-detail")&&w.method==="GET"){try{let m="";D.startsWith("/api/reports/")?m=decodeURIComponent(D.slice(13)):m=O.searchParams.get("id")||"";let E=X.basename(m.trim()),L=E.endsWith("-results.json")?E:`${E}-results.json`,v=X.join(r,L);if(!Q.existsSync(v)){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Report not found: ${E}`}));return}let A=JSON.parse(Q.readFileSync(v,"utf-8"));f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,id:E,report:A}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/baselines"&&w.method==="GET"){try{Q.existsSync(g)||Q.mkdirSync(g,{recursive:!0});let m=O.searchParams.get("spec")||"",E=m.replace(/[^a-zA-Z0-9._-]/g,"_"),L=Q.readdirSync(g),v=new Map;for(let k of L){if(E&&!k.startsWith(`${E}_step`))continue;let F=k.match(/_step(\d+)(_current|_diff)?\.png$/);if(!F)continue;let N=parseInt(F[1],10),H=F[2];v.has(N)||v.set(N,{});let V=v.get(N);H?H==="_current"?V.current=`/baselines/${k}`:H==="_diff"&&(V.diff=`/baselines/${k}`):V.baseline=`/baselines/${k}`}let A=await Promise.all(Array.from(v.entries()).map(async([k,F])=>{let N=await b.getMasks(m,k);return{stepIndex:k,...F,masks:N}}));A.sort((k,F)=>k.stepIndex-F.stepIndex),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,spec:m,baselines:A,matchingModes:Cs}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/baselines/compare"&&w.method==="POST"){try{let m=await J(w);if(!m.specName||typeof m.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 E;if(m.screenshot){let A=m.screenshot.replace(/^data:image\/\w+;base64,/,"");E=Buffer.from(A,"base64")}else{let A=m.deviceId||c.getDefaultDevice()||void 0;E=await c.captureScreenshot(A)}let L=await b.compare(m.specName,m.stepIndex,E,{threshold:m.threshold,mode:m.mode,maskRegions:m.maskRegions}),v=await b.getMasks(m.specName,m.stepIndex);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,result:{...L,masks:v,baselineUrl:`/baselines/${X.basename(L.baselineFile)}`,currentUrl:`/baselines/${X.basename(L.currentFile)}`,diffUrl:L.diffImagePath?`/baselines/${X.basename(L.diffImagePath)}`:null}}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/baselines/approve"&&w.method==="POST"){try{let m=await J(w);if(!m.specName||typeof m.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 E;if(m.screenshot){let v=m.screenshot.replace(/^data:image\/\w+;base64,/,"");E=Buffer.from(v,"base64")}else{let v=m.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),A=X.join(g,`${v}_step${String(m.stepIndex).padStart(3,"0")}_current.png`);if(!Q.existsSync(A)){let k=m.deviceId||c.getDefaultDevice()||void 0;try{E=await c.captureScreenshot(k)}catch{}}}let L=await b.approveBaseline(m.specName,m.stepIndex,E);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,approved:!0,stepIndex:m.stepIndex,baselineUrl:`/baselines/${X.basename(L)}`}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/baselines/clear"&&w.method==="POST"){try{let m=await J(w);if(!m.specName){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName"}));return}await b.clearBaselines(m.specName),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,cleared:!0,specName:m.specName}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/baselines/delete-step"&&w.method==="POST"){try{let m=await J(w);if(!m.specName||typeof m.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(m.specName,m.stepIndex),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,deleted:!0,specName:m.specName,stepIndex:m.stepIndex}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/baselines/upload"&&w.method==="POST"){try{let m=await J(w);if(!m.specName||typeof m.stepIndex!="number"||!m.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 E=m.image.replace(/^data:image\/\w+;base64,/,""),L=Buffer.from(E,"base64"),v=await b.saveBaseline(m.specName,m.stepIndex,L),A=m.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),k=X.join(g,`${A}_step${String(m.stepIndex).padStart(3,"0")}_diff.png`);if(Q.existsSync(k))try{await Q.promises.unlink(k)}catch{}let F=X.join(g,`${A}_step${String(m.stepIndex).padStart(3,"0")}_current.png`),N,H;if(Q.existsSync(F)){let ee=await Q.promises.readFile(F),G=await b.compare(m.specName,m.stepIndex,ee);N=`/baselines/${X.basename(F)}`,H=G.diffImagePath?`/baselines/${X.basename(G.diffImagePath)}`:void 0}else if(m.deviceId||c.getDefaultDevice()){let ee=m.deviceId||c.getDefaultDevice()||void 0;try{let G=await c.captureScreenshot(ee),z=await b.compare(m.specName,m.stepIndex,G);N=`/baselines/${X.basename(F)}`,H=z.diffImagePath?`/baselines/${X.basename(z.diffImagePath)}`:void 0}catch{}}let V=await b.getMasks(m.specName,m.stepIndex);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,uploaded:!0,stepIndex:m.stepIndex,baselineUrl:`/baselines/${X.basename(v)}`,currentUrl:N,diffUrl:H,masks:V}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/baselines/masks"&&w.method==="GET"){try{let m=O.searchParams.get("specName")||O.searchParams.get("spec")||"",E=parseInt(O.searchParams.get("stepIndex")||"",10);if(!m||isNaN(E)){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName or valid stepIndex"}));return}let L=await b.getMasks(m,E);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,masks:L}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/api/baselines/masks"&&w.method==="POST"){try{let m=await J(w);if(!m.specName||typeof m.stepIndex!="number"||!Array.isArray(m.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(m.specName,m.stepIndex,m.masks);let E=m.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),L=X.join(g,`${E}_step${String(m.stepIndex).padStart(3,"0")}_current.png`),v,A,k=0,F=0,N=!0;if(Q.existsSync(L)){let H=await Q.promises.readFile(L),V=await b.compare(m.specName,m.stepIndex,H,{mode:m.mode,threshold:m.threshold,maskRegions:m.masks});v=`/baselines/${X.basename(L)}`,A=V.diffImagePath?`/baselines/${X.basename(V.diffImagePath)}`:void 0,k=V.diffRatio,F=V.diffPixels,N=V.passed}f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,masks:m.masks,currentUrl:v,diffUrl:A,diffRatio:k,diffPixels:F,passed:N}))}catch(m){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:m instanceof Error?m.message:String(m)}))}return}if(D==="/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
918
+ data: ${JSON.stringify({status:"connected",time:Date.now()})}
919
+
920
+ `),o.add(f),w.on("close",()=>{o.delete(f)});return}if(D==="/"||D==="/report"||D==="/index.html"){let m=Es(i,{suiteName:n});f.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-cache"}),f.end(m);return}if(D==="/api/report"||D==="/results.json"){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8","Access-Control-Allow-Origin":"*"}),f.end(JSON.stringify(i,null,2));return}let we=D.replace(/^\/+/,""),ue=X.join(r,we);if(!ue.startsWith(r)){f.writeHead(403,{"Content-Type":"text/plain"}),f.end("Access Denied");return}if(Q.existsSync(ue)&&Q.statSync(ue).isFile()){let m=X.extname(ue).toLowerCase(),E=In[m]||"application/octet-stream";f.writeHead(200,{"Content-Type":E,"Cache-Control":"no-cache"}),Q.createReadStream(ue).pipe(f);return}f.writeHead(404,{"Content-Type":"text/plain"}),f.end("Not Found")});await new Promise((w,f)=>{Ee.once("error",f),Ee.listen(e,t,()=>{w()})});let se=Ee.address(),j=typeof se=="object"&&se?se.port:e,ie=`http://${t}:${j}`;return{server:Ee,port:j,url:ie,broadcastStep(w){i.steps.push(w),i.totalSteps=i.steps.length,w.status==="PASS"?i.passedSteps++:w.status==="FAIL"?i.failedSteps++:w.status==="WARN"?i.warnSteps++:w.status==="SKIPPED"&&i.skippedSteps++,W("step",w)},broadcastComplete(w){i=w,W("complete",w)},broadcastError(w){i.errorsDetected.push(w),W("error",{error:w})},updateReport(w){i=w},async close(){if(clearInterval(l),T){try{await T.stopSession()}catch{}T=null,x=null}y&&y.status==="running"&&(y.abort.aborted=!0);for(let w of o)try{w.end()}catch{}o.clear(),await new Promise((w,f)=>{Ee.close(O=>{O?f(O):w()})})}}}import Ln from"chalk";var mr=null;async function Si(a,e){if(mr)return mr;let t=!!(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"],e),{stdout:p}=await a.execAdb(["shell","getprop","ro.product.model"],e),{stdout:d}=await a.execAdb(["shell","getprop","ro.build.characteristics"],e),u=(p||"").toLowerCase(),h=(d||"").toLowerCase();s=c?.trim()==="1"||u.includes("sdk")||u.includes("emulator")||h.includes("emulator")}catch{s=!1}let r=1;t?r=s?2.8:2:s?r=1.5:r=1;let n=parseFloat(process.env.PROMPTTEST_SETTLE_FACTOR||"");!isNaN(n)&&n>0&&(r=Math.max(.5,n));let i={isEmulator:s,isCI:t,velocityMultiplier:r,defaultTimeoutMs:Math.round(3500*r),extendedTimeoutMs:Math.round(6500*r),quiescenceSettleMs:Math.round(150*Math.min(r,2)),initialPollMs:t?150:80,maxPollIntervalMs:t?600:350};mr=i;let o=s?"Android Emulator":"Physical Android Device",l=t?"CI / Cloud Runner":"Local Environment";return console.log(Ln.cyan(`\u26A1 [Device Profiler] Detected ${o} in ${l} (Velocity Multiplier: ${r.toFixed(1)}x, Dynamic Timeout: ${i.defaultTimeoutMs}ms)`)),i}var Ke=class{driver;memory;reporter;checkpoints;baselines;lastHierarchy=null;screenDimensions={width:I.screen.fallbackWidth,height:I.screen.fallbackHeight};navBarHeight=0;targetPackage=void 0;constructor(e,t,s,r,n,i){this.driver=e||Pe,this.memory=t||_t,this.reporter=s||dt,this.checkpoints=r||Kr,this.baselines=n||Ns,this.semanticResolver=i||lr()}semanticResolver;healEnabled=!1;activeLocale="en";runtimeVariables=new Map;loopStack=[];expandSpecIncludes(e,t=new Set){return Rs(e,t)}async expandSpec(e,t){let s=t||this.semanticResolver,r=e.split(`
921
+ `),n=[],i=0;for(let o of r){let l=o.trim();if(!l||l.startsWith("#")||l.startsWith("//")){n.push(o);continue}let c=this.parsePrompt(l);if(c.length>0&&c[0].type!==void 0&&!l.toLowerCase().startsWith("intent:")){n.push(o);continue}let d=l.replace(/^intent:\s*/i,""),u=this.memory?.getIntentExpansion(d);if(u?.length){n.push(`# [Cached Intent] ${l}`,...u),i+=u.length;continue}let h=await s.resolveIntent(d);if(h?.length){n.push(`# [AI Expanded] ${l}`);let g=h.map(b=>b.originalText);n.push(...g),this.memory&&(this.memory.recordIntentExpansion(d,g),await this.memory.save().catch(()=>{})),i+=h.length}else n.push(o)}return{expanded:n.join(`
922
+ `),stepsAdded:i}}parsePrompt(e,t){return mt(e,t)}async runSpec(e,t,s){if(!us.existsSync(e))throw new Error(`Spec file not found: ${e}`);let r=typeof t=="string"?{packageName:t,serial:s,screenshotMode:"on-failure",continueOnError:!1}:{screenshotMode:"on-failure",continueOnError:!1,...t};r.heal!==void 0&&(this.healEnabled=r.heal),r.noAi&&(this.semanticResolver=lr({provider:"heuristic"})),this.activeLocale=(r.locale||I.locale||"en").toLowerCase(),typeof this.memory.setLocale=="function"&&this.memory.setLocale(this.activeLocale);let n=this.expandSpecIncludes(e),i=n,o=[],l=Ut.extractDataDirective(n,rt.dirname(e));if(l)i=l.cleanedSpec,o=l.rows;else if(r.dataFile){let S=rt.isAbsolute(r.dataFile)?r.dataFile:rt.resolve(process.cwd(),r.dataFile);o=Ut.loadFromFile(S)}(r.onlyRow!==void 0||r.iterations!==void 0)&&(o.length>0?o=Ut.filterRows(o,{onlyRow:r.onlyRow,iterations:r.iterations}):r.iterations&&r.iterations>0&&(o=Array.from({length:r.iterations},()=>({}))));let c=o.length>0?o.map(S=>({...r.variables||{},...S})):[r.variables||{}],p=rt.basename(e,rt.extname(e)),d=i.match(/#\s*(?:Target\s*)?Package:\s*([a-zA-Z0-9._-]+)/i),u=d?d[1]:void 0;if(r.tags){let S=ei(n),y=r.tags.split(",").map($=>$.trim().toLowerCase()).filter(Boolean);if(!y.some($=>S.includes($)))return console.log(q.yellow(`
923
+ \u23E9 Skipping ${e}: tags [${S.join(", ")}] do not match filter [${y.join(", ")}]`)),ns(u||r.packageName||"",r.serial||"",[],"SKIPPED")}if(r.listSteps){let S=this.parsePrompt(i,c[0]);return console.log(ti(S)),ns(u||r.packageName||"",r.serial||"",S,"SKIPPED","Listing")}if(r.dryRun){let S=this.parsePrompt(i,c[0]);return console.log(q.bold.cyan(`
924
+ \u{1F50D} [Dry Run] Validated ${S.length} test step(s) in ${e} successfully (no device interaction).`)),ns(u||r.packageName||"",r.serial||"",S,"PASS","Validation")}if(c.length>1)console.log(q.cyan(`
925
+ \u{1F4CB} Loaded spec with ${c.length} data iteration(s) from ${e}`));else{let S=this.parsePrompt(i,c[0]);console.log(q.cyan(`
926
+ \u{1F4CB} Loaded ${S.length} test step(s) from ${e}`))}console.log(q.gray("Execution Policy: ")+q.bold.white(r.continueOnError?"Continue on Error (Modular Run)":"Fail-Fast (Default)")+q.gray(" | Screenshots: ")+q.bold.white(r.screenshotMode==="all"?"All Steps":"Failure-Only")),r.serial=await this.driver.ensureDefaultDevice(r.serial);let h=r.serial||this.driver.getDefaultDevice()||"Default Device",g=null;r.serial&&(g=Et(r.serial)),r.embedScreenshots&&typeof this.reporter.setEmbedScreenshots=="function"&&this.reporter.setEmbedScreenshots(!0);let b,T;if(r.serve){let S=typeof r.serve=="number"?r.serve:4040;try{b=await yi({port:S,suiteName:p}),console.log(q.bold.magenta(`
927
+ \u{1F4E1} Live Monitor streaming at: ${b.url}
928
+ `)),typeof this.reporter.onStep=="function"&&(T=this.reporter.onStep(y=>{b?.broadcastStep(y)}))}catch(y){let P=y;console.warn(q.yellow(`Warning: Could not start live server: ${P.message}`))}}let x=null;r.video&&(console.log(q.cyan("\u{1F3AC} Starting ADB screen recording (--video enabled)...")),x=await this.driver.startScreenRecording("/sdcard/prompttest_temp.mp4",r.serial));try{this.screenDimensions=await this.driver.getScreenDimensions(r.serial),this.navBarHeight=typeof this.driver.getNavigationBarHeight=="function"?await this.driver.getNavigationBarHeight(r.serial):0,console.log(q.gray("Target Screen Geometry: ")+q.bold.cyan(`${this.screenDimensions.width}x${this.screenDimensions.height}`)+(this.navBarHeight>0?q.gray(` (Nav Bar: ${this.navBarHeight}px)`):"")),r.apkPath&&(us.existsSync(r.apkPath)?(console.log(q.cyan(`\u{1F4E6} Installing APK: ${r.apkPath}...`)),await this.driver.installApk(r.apkPath,r.serial),await new Promise(Z=>setTimeout(Z,2e3))):console.log(q.yellow(`Warning: Specified APK file not found at: ${r.apkPath}`)));let S=r.packageName||u||(typeof this.driver.getForegroundPackage=="function"?await this.driver.getForegroundPackage(r.serial):null)||"Foreground App";if(S&&!S.includes("Launcher")&&!S.includes("Foreground")){if(!(typeof this.driver.isAppInstalled=="function"?await this.driver.isAppInstalled(S,r.serial):!0))if(r.apkPath&&us.existsSync(r.apkPath))console.log(q.cyan(`\u{1F4E6} Target app not found. Auto-installing APK: ${r.apkPath}...`)),await this.driver.installApk(r.apkPath,r.serial),await new Promise(J=>setTimeout(J,2500));else throw new Error(`Target application "${S}" is not installed on device (${h}).
929
+ \u{1F4A1} Suggestions:
930
+ \u2022 Install the app manually or via: adb install <path-to-apk>
931
+ \u2022 Or provide an APK path to auto-install before running: --apk=path/to/app.apk`);let W=await this.driver.getForegroundPackage(r.serial);(r.fresh||W&&W!==S)&&(console.log(q.cyan(`\u{1F680} Launching target application: ${q.bold(S)}...`)),r.fresh&&(await this.driver.forceStop(S,r.serial),await new Promise(J=>setTimeout(J,600))),await this.driver.launchApp(S,r.serial),await new Promise(J=>setTimeout(J,2500)))}r.reporter&&(this.reporter=r.reporter),r.reporters&&typeof this.reporter.setFormats=="function"&&this.reporter.setFormats(r.reporters),r.serial&&await Si(this.driver,r.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(r.profile&&this.targetPackage){let{PerformanceProfiler:Z}=await Promise.resolve().then(()=>(Ti(),xi));y=new Z(this.driver,r.serial),await y.start(this.targetPackage)}this.lastHierarchy=null;let P=Date.now(),$=0;for(let Z=0;Z<c.length;Z++){let W=c[Z],J=c.length>1,Ee=r.onlyRow?`Row ${r.onlyRow}`:J?`Row ${Z+1}`:void 0,se=Ee?`${p} [${Ee}]`:p;J&&(console.log(q.bold.magenta(`
932
+ ======================================================
933
+ \u{1F504} [Data-Driven Iteration ${Z+1}/${c.length}] Row Data: ${JSON.stringify(W)}
934
+ ======================================================`)),r.fresh&&Z>0&&S&&(await this.driver.clearAppData(S,r.serial),await new Promise(O=>setTimeout(O,1e3)))),this.runtimeVariables.clear();for(let[O,D]of Object.entries(W))this.runtimeVariables.set(O,D);this.loopStack=[];let j=this.parsePrompt(i,W),ie=1,w=r.resume&&Z===0?await this.checkpoints.getResumeIndex(p):0;w>0&&console.log(q.cyan(`\u23ED Resuming from step ${w+1} (checkpoint found).`));let f=r.only?qr(r.only):null;for(let O=0;O<j.length;O++){let D=j[O];if(ie=O+1,f&&!f.has(ie)){console.log(q.gray(`[Step ${ie}/${j.length}] \u23E9 Skipped (not in --only range: ${r.only})`)),this.reporter.logStep(se,D.originalText,D.type,"SKIPPED",`Filtered out by --only="${r.only}"`,void 0,{videoOffsetMs:Math.max(0,Date.now()-P)},0);continue}if(O<w){this.reporter.logStep(se,D.originalText,D.type,"SKIPPED","Resumed \u2014 already passed",void 0,{videoOffsetMs:0},0);continue}let we=j.slice(O+1);console.log(q.bold.blue(`
935
+ [Step ${ie}/${j.length}] ${D.originalText}`));let ue=Date.now();try{let m=await this.executeStep(D,r.serial,we),E=Date.now()-ue;if(m&&m.fastForwarded){if(console.log(q.cyan(`\u23E9 Fast-Forward: ${m.reason}. Redundant step(s) bypassed.`)),this.reporter.logStep(se,D.originalText,D.type,"PASS",`Fast-Forward: ${m.reason}`,void 0,{videoOffsetMs:Math.max(0,ue-P)},E),console.log(q.green(`\u2714 Step ${ie} PASSED (Fast-Forward)`)),m?.skipCount&&m.skipCount>0){let L=m.skipCount;for(let v=0;v<L&&O+1<j.length;v++){O++;let A=j[O],k=O+1;this.reporter.logStep(se,A.originalText,A.type,"PASS",`Fast-Forward: ${m.reason}`,void 0,{videoOffsetMs:Math.max(0,ue-P)},0),console.log(q.gray(` [Step ${k}/${j.length}] \u23E9 Fast-Forward: ${A.originalText}`))}}}else{let L;if((r.screenshotMode==="all"||r.saveBaseline||r.compareBaseline)&&(L=await this.driver.captureScreenshot(r.serial)),L&&r.compareBaseline){let A=await this.baselines.compare(se,ie,L,r.baselineThreshold);A.passed||(console.log(q.yellow(` \u26A0\uFE0F [Baseline] Step ${ie} visual diff ${(A.diffRatio*100).toFixed(1)}% exceeds threshold ${(A.threshold*100).toFixed(1)}%`)),this.reporter.logError(`Visual baseline diff at Step ${ie}: ${(A.diffRatio*100).toFixed(1)}% changed`))}L&&r.saveBaseline&&!r.compareBaseline&&await this.baselines.saveBaseline(se,ie,L);let v={videoOffsetMs:Math.max(0,ue-P)};D.healedFrom&&(v.healed=!0,v.healedFrom=D.healedFrom,v.healedTo=D.healedTo||D.target),this.reporter.logStep(se,D.originalText,D.type,"PASS",D.target||D.value,r.screenshotMode==="all"?L:void 0,v,E),console.log(q.green(`\u2714 Step ${ie} PASSED`)),r.benchmark&&console.log(q.gray(` \u23F1 [Benchmark] Step ${ie} latency: ${E}ms (${D.type}${D.target?` -> "${D.target}"`:""})`))}r.resume&&!J&&await this.checkpoints.save(p,S,O,j.length)}catch(m){let E=m,L=Date.now()-ue;if(D.optional){console.log(q.yellow(`\u26AA Step ${ie} OPTIONAL (Skipped: ${E.message})`)),this.reporter.logStep(se,D.originalText,D.type,"SKIPPED",`Optional step bypassed: ${E.message}`,void 0,{error:E.message,videoOffsetMs:Math.max(0,ue-P)},L);continue}$++,console.log(q.red(`\u274C Step ${ie} FAILED: ${E.message}`)),D.target&&this.memory.recordFailure(D.type,D.target);let v=await this.driver.captureScreenshot(r.serial).catch(()=>{}),A=await si({outputDir:rt.resolve(process.cwd(),"output"),specName:p,currentSpecName:se,stepNum:ie,step:D,error:E,stepStartTime:ue,startTime:P,errorShot:v,lastHierarchy:this.lastHierarchy,driver:this.driver,serial:r.serial});if(this.reporter.logStep(se,D.originalText,D.type,"FAIL",E.message,v,{error:E.message,videoOffsetMs:Math.max(0,ue-P)},L,A),this.reporter.logError(`Step ${ie} failed: ${E.message}`),r.continueOnError)console.log(q.yellow("Continuing with next test step (--continue enabled)..."));else{let k=j.slice(O+1);if(k.length>0){console.log(q.yellow(`
936
+ \u{1F6D1} Aborting dependent flow due to upstream failure at Step ${ie}.`)),console.log(q.gray(`Cascading skip applied to ${k.length} downstream step(s):`));for(let F=0;F<k.length;F++){let N=k[F],H=ie+1+F;this.reporter.logStep(se,N.originalText,N.type,"SKIPPED",`Cascading skip: Precondition failed at Step ${ie} (${D.originalText})`,void 0,{videoOffsetMs:Math.max(0,Date.now()-P)},0),console.log(q.gray(` [Step ${H}/${j.length}] \u26AA SKIPPED: ${N.originalText}`))}}break}}y&&await y.sample(ie,D.originalText)}}try{let Z=this.lastHierarchy;if(Z?.flat){let W=jr(Z.flat,this.screenDimensions.width,this.screenDimensions.height);if(W.length>0){console.log(q.bold.yellow(`
937
+ \u{1F50D} Visual QA Sentinel Warnings (${W.length} detected):`));for(let J of W)console.log(q.yellow(` \u2022 [${J.type}] ${J.description}`)),this.reporter.logError(`Visual Defect (${J.type}): ${J.description}`)}}}catch{}await this.memory.save(),r.resume&&$===0&&await this.checkpoints.clear(p);let R=this.memory.getStats();if(R.totalMappings>0&&console.log(q.cyan(`\u{1F9E0} Knowledge Engine: ${R.totalMappings} active learned mapping(s) for ${R.package}`)),x){console.log(q.cyan("\u{1F3AC} Finalizing video recording..."));let Z=rt.resolve(process.cwd(),"output"),W=rt.join(Z,`${p}-recording.mp4`),J=await this.driver.stopScreenRecording(x,W,r.serial);J&&us.existsSync(J)&&(this.reporter.setVideoPath?.(`${p}-recording.mp4`),console.log(q.green(`\u2714 Screen recording saved to: output/${p}-recording.mp4`))),x=null}if(y){let Z=await y.stop();typeof this.reporter.setPerformanceMetrics=="function"&&this.reporter.setPerformanceMetrics(Z)}let C=((Date.now()-P)/1e3).toFixed(1),B=await this.reporter.generateReport(p);b&&(b.broadcastComplete(B),T&&T()),console.log(q.bold.green(`
938
+ \u2728 PromptTest Script Execution Finished in ${C}s!`));let U=B.failedSteps===0?q.bold.green:q.bold.yellow;return console.log(U(`Result: ${B.passedSteps}/${B.totalSteps} passed (${B.failedSteps} failed, ${B.skippedSteps} skipped).`)),console.log(q.cyan(`Report saved to output/${p}-report.html`)),process.env.CI||console.log(q.dim("\u2B50 Star PromptTest on GitHub: ")+q.underline.cyan("https://github.com/shriramsingh/prompttest-community")),console.log(""),r.json&&console.log(JSON.stringify(B,null,2)),B}finally{if(x)try{await this.driver.stopScreenRecording(x,rt.join("output",`${p}-recording.mp4`),r.serial)}catch{}g&&kt(g)}}async getCleanHierarchy(e){let t=await this.driver.getUiHierarchy(e),{flat:s}=me(t),r=s.find(o=>(o.text==="Dismiss"||o.contentDesc==="Dismiss")&&o.bounds.width>0);r&&(await this.driver.tap(r.bounds.centerX,r.bounds.centerY,e),await new Promise(o=>setTimeout(o,400)),t=await this.driver.getUiHierarchy(e),s=me(t).flat);let n=s.find(o=>o.contentDesc&&(o.contentDesc.startsWith("!, ")||o.contentDesc.includes("react.dev/link")));if(n){let o=n.bounds.x2-45,l=n.bounds.centerY;await this.driver.tap(o,l,e),await new Promise(c=>setTimeout(c,350)),t=await this.driver.getUiHierarchy(e),s=me(t).flat}let i=ye(s);return this.lastHierarchy={xml:t,flat:s,timestamp:Date.now(),hash:i},{xml:t,flat:s}}async ensureInViewport(e,t){let s=this.screenDimensions.width,r=this.screenDimensions.height,n=Math.round(s*.5),i=this.navBarHeight>0?r-this.navBarHeight:r,o=this.navBarHeight>0?i*.94:r*I.gestures.bottomNavThresholdRatio;if(e.bounds.centerY>o||e.bounds.y1>o)return!1;let c=e.bounds.y2>i,p=e.bounds.y1<Math.round(r*.04)&&e.bounds.centerY<Math.round(r*.06);return c?(await this.driver.swipe(n,Math.round(r*.75),n,Math.round(r*.45),250,t),await new Promise(d=>setTimeout(d,200)),this.lastHierarchy=null,!0):p?(await this.driver.swipe(n,Math.round(r*.35),n,Math.round(r*.65),250,t),await new Promise(d=>setTimeout(d,200)),this.lastHierarchy=null,!0):!1}async waitForTarget(e,t,s=3e3,r){if(this.lastHierarchy&&Date.now()-this.lastHierarchy.timestamp<I.polling.hierarchyCacheTtlMs){let l=this.matchNodes(this.lastHierarchy.flat,e,r);if(l.length>0)return{flat:this.lastHierarchy.flat,matched:l}}let n=Date.now();for(;Date.now()-n<s;){let{flat:l}=await this.getCleanHierarchy(t),c=this.matchNodes(l,e,r);if(c.length>0)return{flat:l,matched:c};await new Promise(p=>setTimeout(p,$r()))}let{flat:i}=await this.getCleanHierarchy(t),o=this.matchNodes(i,e,r);if(o.length===0&&(this.healEnabled||I.ai.selfHealing))try{let l=await this.semanticResolver.findSuggestions(e,i,{locale:this.activeLocale}),c=I.ai.confidenceThreshold||.75,p=l.find(d=>d.score>=c);if(p){let d=p.node||i.find(u=>(de(p.text,u.text)||de(p.text,u.contentDesc))&&u.bounds.width>0);if(d){console.log(q.yellow(` \u{1F4A1} [Self-Healing] Auto-healed "${e}" \u2192 "${p.text}" (${Math.round(p.score*100)}% confidence, ${p.strategy})`)),r&&(r.healedFrom=e,r.healedTo=p.text,r.target=p.text);let u=d.text?"text":d.contentDesc?"contentDesc":"resourceId",h=this.lastHierarchy?.hash,g=Math.min(I.memory?.maxHealedConfidence??.65,p.score);this.memory.recordSuccess(r?.type||"TAP",e,p.text,u,g,e,h),o=[d]}}}catch(l){qe.warn(`Self-healing resolution failed: ${l.message}`)}return{flat:i,matched:o}}buildDiagnosticError(e,t,s){return Qr(e,t,s)}async executeStep(e,t,s){let r=ni.get(e.type);if(!r)throw new Error(`No handler registered for step type: ${e.type}`);e.target&&e.target.includes("$")&&(e.target=Pt(e.target,Object.fromEntries(this.runtimeVariables))),e.value&&e.value.includes("$")&&(e.value=Pt(e.value,Object.fromEntries(this.runtimeVariables))),(e.type==="LAUNCH_APP"||e.type==="RESTART_APP")&&e.target&&(this.targetPackage=e.target);let n={targetPackage:this.targetPackage,driver:this.driver,memory:this.memory,serial:t,screenDimensions:this.screenDimensions,navBarHeight:this.navBarHeight,lastHierarchy:this.lastHierarchy,setLastHierarchy:i=>{this.lastHierarchy=i},getCleanHierarchy:i=>this.getCleanHierarchy(i),waitForTarget:(i,o,l,c)=>this.waitForTarget(i,o,l,c),matchNodes:(i,o,l)=>this.matchNodes(i,o,l),ensureInViewport:(i,o)=>this.ensureInViewport(i,o),buildDiagnosticError:(i,o,l)=>this.buildDiagnosticError(i,o,l),findTargetInputField:(i,o,l)=>this.findTargetInputField(i,o,l),upcomingSteps:s,variables:this.runtimeVariables,loopStack:this.loopStack,executeChildSteps:async(i,o)=>{for(let l=0;l<i.length;l++){let c=i[l];await this.executeStep(c,o.serial,i.slice(l+1))}}};return r.execute(e,n)}findTargetInputField(e,t,s){return Zr(e,t,s,this.memory)}matchNodes(e,t,s){return is(e,t,this.memory,this.activeLocale,s,this.lastHierarchy?.hash)}},Pi=new Ke;Ne();Ne();It();Fe();Fe();import Ei from"fs";import Mn from"path";var Zt=null;function Rn(a){return JSON.parse(JSON.stringify(a))}function ki(a,e){for(let t of Object.keys(e)){let s=e[t],r=a[t];s&&typeof s=="object"&&!Array.isArray(s)&&r&&typeof r=="object"?ki(r,s):s!==void 0&&(a[t]=s)}return a}function On(a){let e=Rn(I),t=a||Mn.resolve(process.cwd(),"prompttest.config.json");if(Ei.existsSync(t))try{let y=Ei.readFileSync(t,"utf-8"),P=JSON.parse(y);typeof P=="object"&&P!==null&&ki(e,P)}catch{}let s=process.env.PROMPTTEST_ADB_COMMAND_TIMEOUT_MS||process.env.PROMPTTEST_COMMAND_TIMEOUT_MS;if(s){let y=parseInt(s,10);isNaN(y)||(e.adb.commandTimeoutMs=y)}let r=process.env.PROMPTTEST_POLLING_INTERVAL_MS||process.env.PROMPTTEST_POLL_INTERVAL_MS;if(r){let y=parseInt(r,10);isNaN(y)||(e.polling.intervalMs=y)}let n=process.env.PROMPTTEST_POLLING_DEFAULT_TIMEOUT_MS||process.env.PROMPTTEST_DEFAULT_TIMEOUT_MS;if(n){let y=parseInt(n,10);isNaN(y)||(e.polling.defaultTimeoutMs=y)}let i=process.env.PROMPTTEST_SCREEN_FALLBACK_WIDTH||process.env.PROMPTTEST_FALLBACK_WIDTH;if(i){let y=parseInt(i,10);isNaN(y)||(e.screen.fallbackWidth=y)}let o=process.env.PROMPTTEST_SCREEN_FALLBACK_HEIGHT||process.env.PROMPTTEST_FALLBACK_HEIGHT;if(o){let y=parseInt(o,10);isNaN(y)||(e.screen.fallbackHeight=y)}let l=process.env.PROMPTTEST_VISUAL_OVERLAP_RATIO||process.env.PROMPTTEST_OVERLAP_RATIO;if(l){let y=parseFloat(l);isNaN(y)||(e.visual.overlapRatio=y)}let c=process.env.PROMPTTEST_DEVICE_MAX_DEVICE_RETRIES||process.env.PROMPTTEST_MAX_DEVICE_RETRIES;if(c){let y=parseInt(c,10);isNaN(y)||(e.device.maxDeviceRetries=y)}let p=process.env.PROMPTTEST_DEVICE_AUTO_RECONNECT??process.env.PROMPTTEST_AUTO_RECONNECT;p!==void 0&&(e.device.autoReconnect=p.toLowerCase()!=="false");let d=process.env.PROMPTTEST_GESTURES_SWIPE_DURATION_MS||process.env.PROMPTTEST_SWIPE_DURATION_MS;if(d){let y=parseInt(d,10);isNaN(y)||(e.gestures.swipeDurationMs=y)}let u=process.env.PROMPTTEST_FLOW_MAX_LOOP_ITERATIONS||process.env.PROMPTTEST_MAX_LOOP_ITERATIONS;if(u){let y=parseInt(u,10);isNaN(y)||(e.flow.maxLoopIterations=y)}let h=process.env.PROMPTTEST_AI_PROVIDER;h&&["heuristic","openai","anthropic","ollama","mock"].includes(h)&&(e.ai.provider=h);let g=process.env.PROMPTTEST_AI_MODEL;g&&(e.ai.model=g);let b=process.env.PROMPTTEST_SELF_HEALING;b!==void 0&&(e.ai.selfHealing=b.toLowerCase()!=="false");let T=process.env.PROMPTTEST_CONFIDENCE_THRESHOLD;if(T){let y=parseFloat(T);isNaN(y)||(e.ai.confidenceThreshold=y)}let x=process.env.PROMPTTEST_AI_ENDPOINT;x&&(e.ai.endpoint=x);let S=process.env.PROMPTTEST_LOCALE;return S&&(e.locale=S.toLowerCase()),Zt=e,e}function md(){return Zt||(Zt=On()),Zt}function fd(){Zt=null}Ne();import je from"chalk";function Di(a,e){if(a.length===0)return 0;let t=[...a].sort((r,n)=>r-n),s=Math.min(t.length-1,Math.max(0,Math.floor(e/100*(t.length-1))));return Math.round(t[s]*100)/100}function br(a){if(a.length===0)return{minMs:0,maxMs:0,avgMs:0,p50Ms:0,p95Ms:0,iterations:0,durations:[]};let e=Math.round(Math.min(...a)*100)/100,t=Math.round(Math.max(...a)*100)/100,s=a.reduce((o,l)=>o+l,0),r=Math.round(s/a.length*100)/100,n=Di(a,50),i=Di(a,95);return{minMs:e,maxMs:t,avgMs:r,p50Ms:n,p95Ms:i,iterations:a.length,durations:a}}function Fn(a=1e3){let e=[];e.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}}),e.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 t=["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 r=2;r<a;r++){let n=t[r%t.length],i=n.includes("Button")||n.includes("CheckBox")||n.includes("Switch"),o=s[r%s.length],l=`${o} #${r}`,c=Math.min(2400,250+r*20%2e3),p=50+r*30%900;e.push({index:r,className:n,packageName:"com.app",text:n.includes("ImageView")?"":l,resourceId:`com.app:id/element_${r}`,contentDesc:n.includes("ImageView")?`Icon of ${o}`:"",clickable:i,enabled:r%25!==0,focused:!1,scrollable:!1,password:o.toLowerCase().includes("password"),selected:!1,children:[],bounds:{x1:p,y1:c,x2:p+200,y2:c+80,width:200,height:80,centerX:p+100,centerY:c+40}})}return e}async function Td(a=Pe,e={}){let t=e.iterations||5,s=Date.now(),r=e.serial,n="Synthetic Mock Android Environment",i=!1;if(!e.forceMock)try{r=await a.ensureDefaultDevice(e.serial),r&&(n=await a.getDeviceModel(r),i=!0)}catch{i=!1}let o=[];for(let g=0;g<t;g++){let b=performance.now();i?await a.captureScreenshot(r):(await new Promise(T=>setTimeout(T,15)),Buffer.alloc(1024*1024)),o.push(performance.now()-b)}let l=[];for(let g=0;g<t;g++){let b=performance.now();i?await a.getUiHierarchy(r):await new Promise(T=>setTimeout(T,25)),l.push(performance.now()-b)}let c=Fn(1e3),p=new pt,d=["Submit","Email Address","Mathematics","Save Changes","NonExistent Query Fallback"],u=[];for(let g=0;g<t;g++){let b=d[g%d.length],T=performance.now();is(c,b,p),u.push(performance.now()-T)}let h=Date.now()-s;return{timestamp:new Date().toISOString(),device:n,package:e.packageName,screencap:br(o),uiHierarchy:br(l),nodeMatching:br(u),totalDurationMs:h}}function Pd(a){let e=(n,i)=>{let o=`${i.minMs.toFixed(1)}ms`.padStart(9),l=`${i.avgMs.toFixed(1)}ms`.padStart(9),c=`${i.p50Ms.toFixed(1)}ms`.padStart(9),p=`${i.p95Ms.toFixed(1)}ms`.padStart(9),d=`${i.maxMs.toFixed(1)}ms`.padStart(9);return`\u2502 ${n.padEnd(28)} \u2502 ${o} \u2502 ${l} \u2502 ${c} \u2502 ${p} \u2502 ${d} \u2502`};return["",je.bold.cyan("\u26A1 PromptTest Performance Diagnostic Benchmark"),je.gray("Device: ")+je.bold.white(a.device)+je.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 ${je.bold("Operation".padEnd(28))} \u2502 ${je.bold("Min".padStart(9))} \u2502 ${je.bold("Avg".padStart(9))} \u2502 ${je.bold("p50".padStart(9))} \u2502 ${je.bold("p95".padStart(9))} \u2502 ${je.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",e("Device Screencap",a.screencap),e("UI Hierarchy Dump (XML)",a.uiHierarchy),e("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",je.gray("Total Benchmark Wall-Time: ")+je.bold.green(`${a.totalDurationMs}ms
939
+ `)].join(`
940
+ `)}Ne();import _n from"readline";import jt from"fs";import Ai from"path";import le from"chalk";async function $i(a=Pe,e,t={}){let s=await a.ensureDefaultDevice(t.serial),r=e||new Ke(a),n={history:[],successfulSteps:[]},i=t.input||process.stdin,o=t.output||process.stdout,l=_n.createInterface({input:i,output:o,prompt:le.bold.cyan("prompttest> ")}),c=!1,p=()=>{if(!c)try{l.prompt()}catch{}},d=u=>{o.write(u+`
941
+ `)};return d(le.bold.cyan(`
942
+ \u26A1 PromptTest Interactive Live REPL / Playground (v1.1.0)`)),d(le.gray("Device: ")+le.bold.white(s||"No device connected (offline mock)")+(t.packageName?le.gray(" | Package: ")+le.bold.white(t.packageName):"")),d(le.gray(`Type natural language instructions (e.g. Tap 'Sign In'), or REPL commands: "inspect", "screenshot", "history", "save <file.txt>", "exit".
943
+ `)),new Promise(u=>{p(),l.on("line",async h=>{let g=h.trim();if(!g){p();return}n.history.push(g);let b=g.toLowerCase();if(b==="exit"||b==="quit"||b===":q"){c=!0,d(le.yellow("Exiting PromptTest REPL.")),l.close();return}if(b==="help"||b==="?"){d(le.bold.white(`
944
+ \u{1F4D6} PromptTest REPL Commands:`)),d(` \u2022 ${le.cyan("Tap '...'")} / ${le.cyan("Type '...' into '...'")} : Execute action live on device`),d(` \u2022 ${le.cyan("Verify '...' is visible")} : Assert element visibility`),d(` \u2022 ${le.cyan("inspect")} / ${le.cyan("tree")} : List interactive elements and input fields on screen`),d(` \u2022 ${le.cyan("screenshot [path]")} : Capture current screen to image file`),d(` \u2022 ${le.cyan("history")} : Show executed commands in current session`),d(` \u2022 ${le.cyan("save <file.txt>")} : Save successful steps into a reusable spec file`),d(` \u2022 ${le.cyan("clear")} : Clear session history`),d(` \u2022 ${le.cyan("exit")} : Quit the REPL
945
+ `),p();return}if(b==="history"){d(le.bold.white(`
946
+ \u{1F4DC} Session History (${n.history.length} commands):`)),n.history.forEach((T,x)=>{d(` ${x+1}. ${T}`)}),d(""),p();return}if(b==="clear"){n.successfulSteps=[],n.history=[],d(le.green("Session history cleared.")),p();return}if(b.startsWith("save")){let x=g.split(/\s+/)[1]||"output/repl_spec.txt";try{let S=Ai.dirname(x);S&&!jt.existsSync(S)&&jt.mkdirSync(S,{recursive:!0});let y=n.successfulSteps.map((P,$)=>`${$+1}. ${P}`).join(`
947
+ `);jt.writeFileSync(x,y,"utf-8"),d(le.bold.green(`\u2714 Saved ${n.successfulSteps.length} step(s) to ${x}`))}catch(S){let y=S;d(le.red(`Failed to save spec: ${y.message}`))}p();return}if(b.startsWith("screenshot")){let x=g.split(/\s+/)[1]||`output/repl_screenshot_${Date.now()}.png`;try{let S=Ai.dirname(x);S&&!jt.existsSync(S)&&jt.mkdirSync(S,{recursive:!0});let y=await a.captureScreenshot(s);jt.writeFileSync(x,y),d(le.bold.green(`\u2714 Screenshot saved to ${x}`))}catch(S){let y=S;d(le.red(`Failed to capture screenshot: ${y.message}`))}p();return}if(b==="inspect"||b==="tree"){try{d(le.gray("Dumping UI hierarchy..."));let T=await a.getUiHierarchy(s),{flat:x}=me(T),S=x.filter(y=>y.clickable||y.className.includes("Button")||y.className.includes("EditText"));d(le.bold.white(`
948
+ \u{1F50D} Visible Interactive Elements (${S.length} found):`)),S.slice(0,25).forEach((y,P)=>{let $=y.text||y.contentDesc||y.resourceId||`<Unnamed ${y.className}>`,R=y.className.split(".").pop()||y.className;d(` [${P+1}] (${R}) "${$}" at (${y.bounds.centerX}, ${y.bounds.centerY})`)}),S.length>25&&d(le.gray(` ... and ${S.length-25} more elements.`)),d("")}catch(T){let x=T;d(le.red(`Failed to inspect hierarchy: ${x.message}`))}p();return}try{let T=mt(g);if(T.length===0){d(le.yellow(`Could not parse natural language step: "${g}"`)),p();return}let x=T[0],S=performance.now();await r.executeStep(x,s);let y=Math.round(performance.now()-S);n.successfulSteps.push(g),d(le.green(`\u2714 [${x.type}] Completed in ${y}ms: `)+le.white(x.originalText))}catch(T){let x=T;d(le.red(`\u2716 Step failed: ${x.message}`))}p()}),l.on("close",()=>{c=!0,u(n)})})}import qt from"fs";import yr from"path";function Ci(a){let e=a.totalSteps||0,t=a.passedSteps||0,s=a.failedSteps||0,r=a.skippedSteps||0,n=a.errorsDetected?a.errorsDetected.length:0;if(e===0)return{score:0,grade:"F",verdict:"RELEASE_BLOCKER",rationale:"No test steps were executed.",metrics:{passRate:0,criticalFailures:0,visualDefects:0,totalSteps:0,skippedSteps:0}};let i=t/e,o=Math.min(30,n*10),l=s>0?35+Math.min(25,s*5):0,c=Math.round(i*80+20-o-l);c=Math.max(0,Math.min(100,c));let p="F",d="RELEASE_BLOCKER",u="";return c>=90?(p="A",d="READY_FOR_RELEASE",u="High quality gate confidence. All critical assertion paths verified."):c>=80?(p="B",d="READY_FOR_RELEASE",u="Acceptable release confidence. Minor non-critical or optional warnings observed."):c>=70?(p="C",d="PROCEED_WITH_CAUTION",u="Elevated risk. Visual QA warnings or multiple non-critical step failures detected."):(p="F",d="RELEASE_BLOCKER",u=`Critical test failures (${s}) or visual defects (${n}) block safe release.`),{score:c,grade:p,verdict:d,rationale:u,metrics:{passRate:Math.round(i*100)/100,criticalFailures:s,visualDefects:n,totalSteps:e,skippedSteps:r}}}function _d(a="output"){let e=[],t=h=>{if(!qt.existsSync(h))return;let g=qt.readdirSync(h,{withFileTypes:!0});for(let b of g){let T=yr.join(h,b.name);b.isDirectory()?t(T):b.isFile()&&(b.name.endsWith("-results.json")||b.name.endsWith("-report.json"))&&e.push(T)}};if(Array.isArray(a))for(let h of a)qt.existsSync(h)&&(qt.statSync(h).isDirectory()?t(h):e.push(h));else t(a);let s=[],r=new Set,n=0,i=0,o=0,l=0,c=[];for(let h of e)try{let g=qt.readFileSync(h,"utf-8"),b=JSON.parse(g),T=Ci(b),x=b.deviceId||"unknown-device";r.add(x),n+=b.totalSteps||0,i+=b.passedSteps||0,o+=b.failedSteps||0,l+=b.skippedSteps||0,b.errorsDetected&&c.push(...b.errorsDetected),s.push({deviceId:x,suiteName:yr.basename(h,yr.extname(h)).replace(/-results|-report/,""),packageName:b.packageName||"unknown",totalSteps:b.totalSteps||0,passedSteps:b.passedSteps||0,failedSteps:b.failedSteps||0,skippedSteps:b.skippedSteps||0,durationSeconds:b.durationSeconds||0,confidenceScore:T.score,confidenceGrade:T.grade,sourceFile:h})}catch{}let p=n>0?Math.round(i/n*100)/100:0,d={packageName:s[0]?.packageName||"Enterprise Matrix",deviceId:`${r.size} devices`,startTime:new Date().toISOString(),endTime:new Date().toISOString(),durationSeconds:s.reduce((h,g)=>h+g.durationSeconds,0),totalSteps:n,passedSteps:i,failedSteps:o,skippedSteps:l,warnSteps:0,errorsDetected:c,steps:[]},u=Ci(d);return{timestamp:new Date().toISOString(),totalRuns:s.length,uniqueDevices:Array.from(r),totalSteps:n,passedSteps:i,failedSteps:o,skippedSteps:l,overallPassRate:p,overallConfidence:u,deviceSummaries:s}}function Hd(a){let e=a.overallConfidence,t=e.grade==="A"?"#10B981":e.grade==="B"?"#3B82F6":e.grade==="C"?"#F59E0B":"#EF4444",s=a.deviceSummaries.map(r=>{let n=r.confidenceGrade==="A"?"#10B981":r.confidenceGrade==="B"?"#3B82F6":r.confidenceGrade==="C"?"#F59E0B":"#EF4444";return`
910
949
  <tr style="border-bottom: 1px solid #334155;">
911
- <td style="padding: 12px; font-weight: bold; color: #F8FAFC;">${i.deviceId}</td>
912
- <td style="padding: 12px; color: #94A3B8;">${i.suiteName}</td>
913
- <td style="padding: 12px; color: #94A3B8;">${i.packageName}</td>
914
- <td style="padding: 12px; text-align: center; color: #F8FAFC;">${i.totalSteps}</td>
915
- <td style="padding: 12px; text-align: center; color: #10B981; font-weight: bold;">${i.passedSteps}</td>
916
- <td style="padding: 12px; text-align: center; color: #EF4444; font-weight: bold;">${i.failedSteps}</td>
917
- <td style="padding: 12px; text-align: center; color: #64748B;">${i.skippedSteps}</td>
950
+ <td style="padding: 12px; font-weight: bold; color: #F8FAFC;">${r.deviceId}</td>
951
+ <td style="padding: 12px; color: #94A3B8;">${r.suiteName}</td>
952
+ <td style="padding: 12px; color: #94A3B8;">${r.packageName}</td>
953
+ <td style="padding: 12px; text-align: center; color: #F8FAFC;">${r.totalSteps}</td>
954
+ <td style="padding: 12px; text-align: center; color: #10B981; font-weight: bold;">${r.passedSteps}</td>
955
+ <td style="padding: 12px; text-align: center; color: #EF4444; font-weight: bold;">${r.failedSteps}</td>
956
+ <td style="padding: 12px; text-align: center; color: #64748B;">${r.skippedSteps}</td>
918
957
  <td style="padding: 12px; text-align: center;">
919
- <span style="background: ${r}22; color: ${r}; border: 1px solid ${r}; padding: 4px 10px; border-radius: 9999px; font-weight: bold; font-size: 12px;">
920
- Grade ${i.confidenceGrade} (${i.confidenceScore}%)
958
+ <span style="background: ${n}22; color: ${n}; border: 1px solid ${n}; padding: 4px 10px; border-radius: 9999px; font-weight: bold; font-size: 12px;">
959
+ Grade ${r.confidenceGrade} (${r.confidenceScore}%)
921
960
  </span>
922
961
  </td>
923
962
  </tr>`}).join("");return`<!DOCTYPE html>
@@ -976,25 +1015,25 @@ data: ${JSON.stringify({status:"connected",time:Date.now()})}
976
1015
  </table>
977
1016
  </div>
978
1017
  </body>
979
- </html>`}import Ot from"os";import _e from"chalk";var pr="https://github.com/shriramsingh/prompttest-community/issues/new";function ur(a){if(!a)return"";let e=a;try{let t=Ot.homedir();t&&t.length>2&&(e=e.split(t).join("~"))}catch{}return e=e.replace(/([a-zA-Z]:\\Users\\[^\\]+)/gi,"~"),e=e.replace(/(\/(?:home|Users)\/[^/]+)/g,"~"),e.trim()}function Zl(a){if(!a||a instanceof Je&&a.exitCode===1)return!1;let e=a,t=typeof e.message=="string"?e.message:String(a);return!(t.includes("Step")&&(t.includes("failed")||t.includes("timed out")))}function hr(a){let e=a.repoUrl||pr,t=(a.errorMessage||"Unexpected internal error").split(`
980
- `)[0].trim(),s=`[Crash]: ${t}`,i=s.length>80?s.substring(0,77)+"...":s,r=`${Ot.type()} ${Ot.release()} (${Ot.arch()})`,n=process.version,o=`${r}, Node: ${n}`,c=ur(a.stack||a.errorMessage),l=c.length>600?c.substring(0,597)+"...":c,p=new URLSearchParams;return p.set("template","1_bug_report.yml"),p.set("title",i),p.set("description",`PromptTest encountered an unexpected error: ${t}`),a.command&&p.set("command_run",a.command),p.set("environment",o),l&&p.set("logs",l),`${e}?${p.toString()}`}function ql(a,e={}){let t=a&&typeof a=="object"?a:{},s=t.message||String(a),i=t.stack,r=e.command||(process.argv.slice(2).length>0?`npx prompttest ${process.argv.slice(2).join(" ")}`:"npx prompttest"),n=hr({errorMessage:s,stack:i,command:r,exitCode:e.exitCode}),o=`${Ot.type()} ${Ot.arch()}`,c=process.version;console.error(),console.error(_e.red.bold("\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.error(_e.red.bold("\u{1F4A5} PromptTest Encountered an Unexpected Error / Crash")),console.error(_e.red.bold("\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.error(_e.white("Error: ")+_e.red(s.split(`
981
- `)[0])),console.error(_e.white("Command: ")+_e.gray(r)),console.error(_e.white("Environment: ")+_e.gray(`${o} | Node: ${c}`)),console.error(),console.error(_e.cyan("\u{1F4A1} Think this is a bug in PromptTest? You can report it in 1 click (pre-filled):")),console.error(_e.bold.underline.cyan(n)),console.error(_e.red.bold("\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.error()}import fr from"readline";import ci from"fs";import li from"path";import F from"chalk";async function br(a=ke){try{let t=(await a.getConnectedDevices()).find(r=>r.state==="device");if(!t)return{isOnline:!1};let s=t.serial,i;try{let r=await a.getForegroundPackage(s);r&&!r.includes("launcher")&&!r.includes("systemui")&&(i=r)}catch{}return{device:t,serial:s,activePackage:i,isOnline:!0}}catch{return{isOnline:!1}}}function yr(a=process.cwd()){let e=[],t=[li.join(a,"specs"),a];for(let s of t)if(ci.existsSync(s)&&ci.statSync(s).isDirectory())try{let i=ci.readdirSync(s);for(let r of i)if(r.endsWith(".txt")||r.endsWith(".spec")||r.endsWith(".prompt")){let n=li.relative(a,li.join(s,r)).replace(/\\/g,"/");e.includes(n)||e.push(n)}}catch{}return e.sort()}function ht(a,e){return new Promise(t=>{a.question(e,s=>{t(s.trim())})})}async function yd(a={}){let e=a.driver||ke,t=a.runner||Zi,s=a.input||process.stdin,i=a.output||process.stdout,r=fr.createInterface({input:s,output:i}),n=(o="")=>{i.write(o+`
982
- `)};try{n(F.bold.cyan(`
983
- \u26A1 PromptTest \u2014 Autonomous Mobile Testing Brain`)),n(F.gray("Inspecting device connectivity and active application..."));let o=await br(e);switch(n(F.gray("\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),o.isOnline&&o.device?n(F.white("\u{1F4F1} Target Device: ")+F.bold.green(`${o.serial}`)+(o.device.model?F.gray(` (${o.device.model})`):"")+F.green(" [Online]")):n(F.white("\u{1F4F1} Target Device: ")+F.yellow("No authorized device online (Check USB/Wi-Fi)")),o.activePackage?n(F.white("\u{1F3AF} Active App: ")+F.bold.magenta(o.activePackage)+F.gray(" (Detected)")):n(F.white("\u{1F3AF} Active App: ")+F.gray("None detected / Home Launcher")),n(F.gray(`\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
984
- `)),n(F.bold.white("What would you like to do?")),n(` ${F.bold.cyan("[1]")} \u{1F680} ${F.bold("Explore app autonomously")} ${F.gray("(Zero-code AI crawl)")}`),n(` ${F.bold.cyan("[2]")} \u{1F534} ${F.bold("Record a test")} ${F.gray("(Capture real device taps into spec)")}`),n(` ${F.bold.cyan("[3]")} \u25B6\uFE0F ${F.bold("Run a test specification")} ${F.gray("(Execute .txt spec with healing)")}`),n(` ${F.bold.cyan("[4]")} \u{1F4AC} ${F.bold("Open Live REPL Playground")} ${F.gray("(Interactive terminal session)")}`),n(` ${F.bold.cyan("[5]")} \u{1FA7A} ${F.bold("Run Diagnostic Doctor")} ${F.gray("(Check ADB, Node, permissions)")}`),n(` ${F.bold.cyan("[6]")} \u{1F4F1} ${F.bold("Inspect UI Components")} ${F.gray("(Summary of buttons, inputs, tabs)")}`),n(` ${F.bold.cyan("[7]")} \u2753 ${F.bold("Help & CLI Syntax Reference")}`),n(` ${F.bold.cyan("[8]")} \u{1F4AC} ${F.bold("Share Feedback & Suggestions")} ${F.gray("(1-click community link)")}`),n(` ${F.bold.cyan("[0]")} \u{1F6AA} ${F.gray("Exit")}
985
- `),await ht(r,F.bold.cyan("Select an option [0-8]: "))){case"1":{n(F.bold.cyan(`
986
- \u{1F680} Autonomous App Exploration`));let l=o.activePackage||"",p=l?F.white(`Target package [default: ${F.bold.magenta(l)}]: `):F.white("Target package name (e.g. com.example.app): "),h=await ht(r,p)||l;if(!h){n(F.red("Error: Package name is required to start exploration."));break}let u=F.white(`Safety mode [strict/moderate/disabled] (default: ${F.bold.green("strict")}): `),m=await ht(r,u)||"strict",y=["strict","moderate","interactive","disabled"].includes(m)?m:"strict";r.close(),n(F.green(`
987
- Starting autonomous exploration on ${F.bold(h)}...
988
- `)),await Xi.explore({packageName:h,serial:o.serial,strategy:"dfs",safetyMode:y});return}case"2":{n(F.bold.cyan(`
989
- \u{1F534} Interactive Record & Replay`));let l=F.white(`Target spec file to save [default: ${F.bold.green("specs/recorded_flow.txt")}]: `),p=await ht(r,l)||"specs/recorded_flow.txt";r.close(),n(F.green(`
1018
+ </html>`}It();import Yt from"os";import Ye from"chalk";var Hn="https://github.com/shriramsingh/prompttest-community/issues/new";function Bn(a){if(!a)return"";let e=a;try{let t=Yt.homedir();t&&t.length>2&&(e=e.split(t).join("~"))}catch{}return e=e.replace(/([a-zA-Z]:\\Users\\[^\\]+)/gi,"~"),e=e.replace(/(\/(?:home|Users)\/[^/]+)/g,"~"),e.trim()}function Wd(a){if(!a||a instanceof Qe&&a.exitCode===1)return!1;let e=a,t=typeof e.message=="string"?e.message:String(a);return!(t.includes("Step")&&(t.includes("failed")||t.includes("timed out")))}function Un(a){let e=a.repoUrl||Hn,t=(a.errorMessage||"Unexpected internal error").split(`
1019
+ `)[0].trim(),s=`[Crash]: ${t}`,r=s.length>80?s.substring(0,77)+"...":s,n=`${Yt.type()} ${Yt.release()} (${Yt.arch()})`,i=process.version,o=`${n}, Node: ${i}`,l=Bn(a.stack||a.errorMessage),c=l.length>600?l.substring(0,597)+"...":l,p=new URLSearchParams;return p.set("template","1_bug_report.yml"),p.set("title",r),p.set("description",`PromptTest encountered an unexpected error: ${t}`),a.command&&p.set("command_run",a.command),p.set("environment",o),c&&p.set("logs",c),`${e}?${p.toString()}`}function Jd(a,e={}){let t=a&&typeof a=="object"?a:{},s=t.message||String(a),r=t.stack,n=e.command||(process.argv.slice(2).length>0?`npx prompttest ${process.argv.slice(2).join(" ")}`:"npx prompttest"),i=Un({errorMessage:s,stack:r,command:n,exitCode:e.exitCode}),o=`${Yt.type()} ${Yt.arch()}`,l=process.version;console.error(),console.error(Ye.red.bold("\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.error(Ye.red.bold("\u{1F4A5} PromptTest Encountered an Unexpected Error / Crash")),console.error(Ye.red.bold("\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.error(Ye.white("Error: ")+Ye.red(s.split(`
1020
+ `)[0])),console.error(Ye.white("Command: ")+Ye.gray(n)),console.error(Ye.white("Environment: ")+Ye.gray(`${o} | Node: ${l}`)),console.error(),console.error(Ye.cyan("\u{1F4A1} Think this is a bug in PromptTest? You can report it in 1 click (pre-filled):")),console.error(Ye.bold.underline.cyan(i)),console.error(Ye.red.bold("\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.error()}Ne();import Wn from"readline";import Tr from"fs";import Pr from"path";import _ from"chalk";async function Jn(a=Pe){try{let t=(await a.getConnectedDevices()).find(n=>n.state==="device");if(!t)return{isOnline:!1};let s=t.serial,r;try{let n=await a.getForegroundPackage(s);n&&!n.includes("launcher")&&!n.includes("systemui")&&(r=n)}catch{}return{device:t,serial:s,activePackage:r,isOnline:!0}}catch{return{isOnline:!1}}}function Vn(a=process.cwd()){let e=[],t=[Pr.join(a,"specs"),a];for(let s of t)if(Tr.existsSync(s)&&Tr.statSync(s).isDirectory())try{let r=Tr.readdirSync(s);for(let n of r)if(n.endsWith(".txt")||n.endsWith(".spec")||n.endsWith(".prompt")){let i=Pr.relative(a,Pr.join(s,n)).replace(/\\/g,"/");e.includes(i)||e.push(i)}}catch{}return e.sort()}function wt(a,e){return new Promise(t=>{a.question(e,s=>{t(s.trim())})})}async function dp(a={}){let e=a.driver||Pe,t=a.runner||Pi,s=a.input||process.stdin,r=a.output||process.stdout,n=Wn.createInterface({input:s,output:r}),i=(o="")=>{r.write(o+`
1021
+ `)};try{i(_.bold.cyan(`
1022
+ \u26A1 PromptTest \u2014 Autonomous Mobile Testing Brain`)),i(_.gray("Inspecting device connectivity and active application..."));let o=await Jn(e);switch(i(_.gray("\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),o.isOnline&&o.device?i(_.white("\u{1F4F1} Target Device: ")+_.bold.green(`${o.serial}`)+(o.device.model?_.gray(` (${o.device.model})`):"")+_.green(" [Online]")):i(_.white("\u{1F4F1} Target Device: ")+_.yellow("No authorized device online (Check USB/Wi-Fi)")),o.activePackage?i(_.white("\u{1F3AF} Active App: ")+_.bold.magenta(o.activePackage)+_.gray(" (Detected)")):i(_.white("\u{1F3AF} Active App: ")+_.gray("None detected / Home Launcher")),i(_.gray(`\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\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1023
+ `)),i(_.bold.white("What would you like to do?")),i(` ${_.bold.cyan("[1]")} \u{1F680} ${_.bold("Explore app autonomously")} ${_.gray("(Zero-code AI crawl)")}`),i(` ${_.bold.cyan("[2]")} \u{1F534} ${_.bold("Record a test")} ${_.gray("(Capture real device taps into spec)")}`),i(` ${_.bold.cyan("[3]")} \u25B6\uFE0F ${_.bold("Run a test specification")} ${_.gray("(Execute .txt spec with healing)")}`),i(` ${_.bold.cyan("[4]")} \u{1F4AC} ${_.bold("Open Live REPL Playground")} ${_.gray("(Interactive terminal session)")}`),i(` ${_.bold.cyan("[5]")} \u{1FA7A} ${_.bold("Run Diagnostic Doctor")} ${_.gray("(Check ADB, Node, permissions)")}`),i(` ${_.bold.cyan("[6]")} \u{1F4F1} ${_.bold("Inspect UI Components")} ${_.gray("(Summary of buttons, inputs, tabs)")}`),i(` ${_.bold.cyan("[7]")} \u2753 ${_.bold("Help & CLI Syntax Reference")}`),i(` ${_.bold.cyan("[8]")} \u{1F4AC} ${_.bold("Share Feedback & Suggestions")} ${_.gray("(1-click community link)")}`),i(` ${_.bold.cyan("[0]")} \u{1F6AA} ${_.gray("Exit")}
1024
+ `),await wt(n,_.bold.cyan("Select an option [0-8]: "))){case"1":{i(_.bold.cyan(`
1025
+ \u{1F680} Autonomous App Exploration`));let c=o.activePackage||"",p=c?_.white(`Target package [default: ${_.bold.magenta(c)}]: `):_.white("Target package name (e.g. com.example.app): "),u=await wt(n,p)||c;if(!u){i(_.red("Error: Package name is required to start exploration."));break}let h=_.white(`Safety mode [strict/moderate/disabled] (default: ${_.bold.green("strict")}): `),g=await wt(n,h)||"strict",b=["strict","moderate","interactive","disabled"].includes(g)?g:"strict";n.close(),i(_.green(`
1026
+ Starting autonomous exploration on ${_.bold(u)}...
1027
+ `)),await hi.explore({packageName:u,serial:o.serial,strategy:"dfs",safetyMode:b});return}case"2":{i(_.bold.cyan(`
1028
+ \u{1F534} Interactive Record & Replay`));let c=_.white(`Target spec file to save [default: ${_.bold.green("specs/recorded_flow.txt")}]: `),p=await wt(n,c)||"specs/recorded_flow.txt";n.close(),i(_.green(`
990
1029
  Starting recording session on device. Output: ${p}...
991
- `)),await ji.startRecording(p,o.serial,{append:!1});return}case"3":{n(F.bold.cyan(`
992
- \u25B6\uFE0F Run Test Specification`));let l=yr(),p="";if(l.length>0){n(F.white("Discovered existing test specs:")),l.forEach((D,B)=>{n(` [${B+1}] ${F.cyan(D)}`)}),n();let S=F.white(`Select spec number [1-${l.length}] or enter path: `),b=await ht(r,S),E=parseInt(b,10);!isNaN(E)&&E>=1&&E<=l.length?p=l[E-1]:b&&(p=b)}if(p||(p=await ht(r,F.white("Enter spec file path (e.g. specs/login.txt): "))),!p){n(F.red("Error: Spec file path is required."));break}let d=o.activePackage||"",h=d?F.white(`Target package [default: ${F.bold.magenta(d)}]: `):F.white("Target package (optional): "),m=await ht(r,h)||d||void 0,y=F.white(`Enable dynamic self-healing locators? [Y/n] (default: ${F.bold.green("yes")}): `),v=(await ht(r,y)).toLowerCase(),T=v!=="n"&&v!=="no";r.close(),n(F.green(`
1030
+ `)),await ci.startRecording(p,o.serial,{append:!1});return}case"3":{i(_.bold.cyan(`
1031
+ \u25B6\uFE0F Run Test Specification`));let c=Vn(),p="";if(c.length>0){i(_.white("Discovered existing test specs:")),c.forEach(($,R)=>{i(` [${R+1}] ${_.cyan($)}`)}),i();let S=_.white(`Select spec number [1-${c.length}] or enter path: `),y=await wt(n,S),P=parseInt(y,10);!isNaN(P)&&P>=1&&P<=c.length?p=c[P-1]:y&&(p=y)}if(p||(p=await wt(n,_.white("Enter spec file path (e.g. specs/login.txt): "))),!p){i(_.red("Error: Spec file path is required."));break}let d=o.activePackage||"",u=d?_.white(`Target package [default: ${_.bold.magenta(d)}]: `):_.white("Target package (optional): "),g=await wt(n,u)||d||void 0,b=_.white(`Enable dynamic self-healing locators? [Y/n] (default: ${_.bold.green("yes")}): `),T=(await wt(n,b)).toLowerCase(),x=T!=="n"&&T!=="no";n.close(),i(_.green(`
993
1032
  Executing spec "${p}"...
994
- `)),await t.runSpec(p,{packageName:m,serial:o.serial,heal:T});return}case"4":{n(F.bold.cyan(`
995
- \u{1F4AC} Launching Live REPL Playground...`)),r.close(),await nn(e,t,{serial:o.serial,packageName:o.activePackage});return}case"5":{n(F.bold.cyan(`
996
- \u{1FA7A} Running Diagnostic Health Check...`)),r.close();let l=await ss(e,{serial:o.serial,packageName:o.activePackage});n(Wi(l));return}case"6":{if(!o.isOnline||!o.serial){n(F.red("No device online to inspect."));break}n(F.cyan(`
997
- \u{1F50D} Inspecting Active Screen UI Hierarchy...`));try{let l=await e.getUiHierarchy(o.serial),{flat:p}=we(l),d=ze(p),h=Qe(p),u=Ut(p);n(F.white(`
998
- On-Screen Component Breakdown:`)),n(`- \u{1F4DD} Input Fields: ${F.bold.magenta(d.length.toString())}`),d.slice(0,5).forEach(m=>n(` \u2022 "${m.text||m.contentDesc||m.resourceId}"`)),n(`- \u{1F518} Action Buttons: ${F.bold.blue(h.length.toString())}`),h.slice(0,5).forEach(m=>n(` \u2022 "${m.text||m.contentDesc||m.resourceId}"`)),n(`- \u{1F4D1} Navigation Tabs: ${F.bold.yellow(u.length.toString())}`),u.forEach(m=>n(` \u2022 "${m.text||m.contentDesc}"`)),n()}catch(l){let p=l instanceof Error?l.message:String(l);n(F.red(`Failed to read UI hierarchy: ${p}`))}break}case"7":{n(F.bold.cyan(`
999
- \u{1F4D6} Quick CLI Command Reference`)),n(" npx prompttest Interactive guided CLI wizard"),n(" npx prompttest feedback [message] Share feedback or feature requests in 1 click"),n(" npx prompttest doctor Check system dependencies & ADB"),n(" npx prompttest explore <package> Autonomous AI crawl with safety policies"),n(" npx prompttest record <spec.txt> Interactive record touches from phone"),n(" npx prompttest run <spec.txt> [package] Execute plain-English test spec"),n(" npx prompttest repl [package] Interactive live terminal playground"),n(" npx prompttest devices List all connected devices & serials"),n(` npx prompttest wifi <device_ip> Connect wireless device over Wi-Fi
1000
- `);break}case"8":{r.close();let{startFeedbackSession:l}=await Promise.resolve().then(()=>(ai(),an));await l({input:s,output:i});return}default:{n(F.gray("Exiting PromptTest. Have a great day!"));break}}}finally{try{r.close()}catch{}}}ai();function Ad(a,e,t){return new Ke(a,e,t)}function $d(a,e,t){return new ut(a,e,t)}function Cd(a){return new tt(a)}function Id(){return new rt}function Nd(a){return new At(a)}function Ld(a){return new lt(a)}function Md(a,e){return new pt(a,e)}function Rd(a,e){return new ct(a,e)}export{Si as ACTION_KEYWORDS,tt as AndroidDriver,Gs as ApiHandler,ut as AutonomousExplorer,lt as AutonomousRecorder,Cs as BackHandler,ct as BaselineManager,C as CONFIG,_s as ClearDataHandler,As as ClearHandler,js as ConditionalHandler,os as ConfigError,In as DESTRUCTIVE_ACTION_PATTERNS,Mt as DataSpecLoader,Bs as DeepLinkHandler,mt as DeviceError,Js as DeviceStateHandler,Ln as EXTERNAL_INTENT_PATTERNS,Ks as EmailHandler,cs as ExitCode,zr as GLOBAL_SEED_DICTIONARY,Hs as GrantPermissionHandler,xt as HeuristicResolver,rn as IosDriver,es as LLMResolver,$t as LOCALE_PACKS,Cn as LOGBOX_PATTERNS,Rs as LaunchAppHandler,rs as LocatorError,as as LockError,zt as Logger,Ds as LongPressHandler,Ys as LoopHandler,Nn as MODERATE_DESTRUCTIVE_PATTERNS,rt as MemoryEngine,Lt as MockAIProvider,zs as OtpHandler,G as PROMPT_ACTION_PATTERNS,Ke as PromptRunner,Je as PromptTestError,At as QaReporter,Os as RestartAppHandler,Xs as RunJsHandler,Hi as STEP_HANDLER_REGISTRY,fi as STEP_PREFIX_REGEX,yi as SUBMIT_KEYWORDS,Is as ScrollHandler,Ms as SelectHandler,pt as SemanticFormFiller,Vs as SetVariableHandler,_t as SpecRecursionError,Ns as SwipeHandler,ks as TapHandler,Fs as TerminateAppHandler,Ls as ToggleHandler,qt as TypeHandler,Un as VOLATILE_DATA_PATTERNS,$s as VerifyHandler,Us as WaitHandler,Ws as WaitUntilHandler,Ht as XML_HIERARCHY_PATTERNS,yt as acquireDeviceLock,Bn as assembleAstBlocks,Ss as baselineManager,Ni as buildDiagnosticError,oi as buildFeedbackUrl,hr as buildIssueUrl,tn as calculatePercentile,on as calculateReleaseConfidence,Pi as clusterSiblingElements,Ao as compareHierarchyNodes,Oe as computeCanonicalSemanticHash,be as computeHierarchyHash,Cd as createAndroidDriver,$d as createAutonomousExplorer,Ld as createAutonomousRecorder,Rd as createBaselineManager,Md as createFormFiller,Id as createMemoryEngine,Ad as createPromptRunner,Nd as createQaReporter,Qs as createSemanticResolver,Zt as createSyntheticReport,br as detectActiveEnvironment,us as detectNavigationParadigm,Ei as detectVisualDefects,ql as displayCrashTriage,Ts as expandSpecIncludes,bi as extractQuotedText,Ri as extractSpecTags,Qe as findActionButtons,Dt as findCheckableControls,bt as findClosestMatches,jt as findDevOverlay,st as findDrawerButton,vi as findErrorDialogs,yr as findExistingSpecs,xi as findHeaderActionButtons,Re as findInAppBackButton,ze as findInputFields,Bt as findListItems,Ut as findNavigationTabs,jr as findNodes,Ti as findScrollableContainers,Li as findTargetInputField,Cl as formatBenchmarkTable,Wi as formatDoctorReport,Oi as formatStepTable,Xl as generateAggregatedHtml,lr as generateSyntheticHierarchy,vl as getActiveConfig,ui as getJitteredPollDelay,Yt as getSeedSynonyms,mn as getSettleFactor,Kr as getSupportedLocales,Di as getSynonyms,ft as interpolateVariables,Be as isDestructiveAction,Ue as isExternalIntentAction,Zl as isFrameworkCrash,Me as isLogBoxNode,kn as levenshteinDistance,cr as loadConfig,Ge as logger,$o as maskSensitiveValue,Qt as matchNodes,pe as matchesText,Jl as mergeReports,Ii as normalizeSemanticText,oe as normalizeText,wi as parseBounds,we as parseHierarchyXml,An as parseHierarchyXmlRegex,$n as parseHierarchyXmlStreaming,Nt as parsePrompt,Mi as parseStepRange,Gr as registerLocalePack,St as releaseDeviceLock,xl as resetActiveConfig,ae as resolveTokenMatch,$l as runBenchmark,ss as runDoctor,ur as sanitizeStackTrace,Le as scaleSettle,mr as startFeedbackSession,yd as startInteractiveWizard,Gi as startLiveServer,nn as startRepl,Dn as stringSimilarity,si as summarizeMetrics,kt as unescapeXml};
1033
+ `)),await t.runSpec(p,{packageName:g,serial:o.serial,heal:x});return}case"4":{i(_.bold.cyan(`
1034
+ \u{1F4AC} Launching Live REPL Playground...`)),n.close(),await $i(e,t,{serial:o.serial,packageName:o.activePackage});return}case"5":{i(_.bold.cyan(`
1035
+ \u{1FA7A} Running Diagnostic Health Check...`)),n.close();let c=await ls(e,{serial:o.serial,packageName:o.activePackage});i(di(c));return}case"6":{if(!o.isOnline||!o.serial){i(_.red("No device online to inspect."));break}i(_.cyan(`
1036
+ \u{1F50D} Inspecting Active Screen UI Hierarchy...`));try{let c=await e.getUiHierarchy(o.serial),{flat:p}=me(c),d=Le(p),u=Me(p),h=ot(p);i(_.white(`
1037
+ On-Screen Component Breakdown:`)),i(`- \u{1F4DD} Input Fields: ${_.bold.magenta(d.length.toString())}`),d.slice(0,5).forEach(g=>i(` \u2022 "${g.text||g.contentDesc||g.resourceId}"`)),i(`- \u{1F518} Action Buttons: ${_.bold.blue(u.length.toString())}`),u.slice(0,5).forEach(g=>i(` \u2022 "${g.text||g.contentDesc||g.resourceId}"`)),i(`- \u{1F4D1} Navigation Tabs: ${_.bold.yellow(h.length.toString())}`),h.forEach(g=>i(` \u2022 "${g.text||g.contentDesc}"`)),i()}catch(c){let p=c instanceof Error?c.message:String(c);i(_.red(`Failed to read UI hierarchy: ${p}`))}break}case"7":{i(_.bold.cyan(`
1038
+ \u{1F4D6} Quick CLI Command Reference`)),i(" npx prompttest Interactive guided CLI wizard"),i(" npx prompttest feedback [message] Share feedback or feature requests in 1 click"),i(" npx prompttest doctor Check system dependencies & ADB"),i(" npx prompttest explore <package> Autonomous AI crawl with safety policies"),i(" npx prompttest record <spec.txt> Interactive record touches from phone"),i(" npx prompttest run <spec.txt> [package] Execute plain-English test spec"),i(" npx prompttest repl [package] Interactive live terminal playground"),i(" npx prompttest devices List all connected devices & serials"),i(` npx prompttest wifi <device_ip> Connect wireless device over Wi-Fi
1039
+ `);break}case"8":{n.close();let{startFeedbackSession:c}=await Promise.resolve().then(()=>(xr(),Ni));await c({input:s,output:r});return}default:{i(_.gray("Exiting PromptTest. Have a great day!"));break}}}finally{try{n.close()}catch{}}}xr();function wp(a,e,t){return new Ke(a,e,t)}function vp(a,e,t){return new yt(a,e,t)}function xp(a){return new nt(a)}function Tp(){return new pt}function Pp(a){return new lt(a)}function Ep(a){return new ft(a)}function kp(a,e){return new bt(a,e)}function Dp(a,e){return new gt(a,e)}export{Fr as ACTION_KEYWORDS,nt as AndroidDriver,ar as ApiHandler,yt as AutonomousExplorer,ft as AutonomousRecorder,js as BackHandler,gt as BaselineManager,I as CONFIG,Gs as ClearDataHandler,Bs as ClearHandler,er as ConditionalHandler,ms as ConfigError,qi as DESTRUCTIVE_ACTION_PATTERNS,Ut as DataSpecLoader,Zs as DeepLinkHandler,Tt as DeviceError,ir as DeviceStateHandler,tn as EXTERNAL_INTENT_PATTERNS,cr as EmailHandler,bs as ExitCode,$o as GLOBAL_SEED_DICTIONARY,Qs as GrantPermissionHandler,$t as HeuristicResolver,ps as IosDriver,as as LLMResolver,Ft as LOCALE_PACKS,Zi as LOGBOX_PATTERNS,zs as LaunchAppHandler,gs as LocatorError,fs as LockError,ss as Logger,Hs as LongPressHandler,tr as LoopHandler,en as MODERATE_DESTRUCTIVE_PATTERNS,pt as MemoryEngine,Bt as MockAIProvider,or as OtpHandler,te as PROMPT_ACTION_PATTERNS,Ke as PromptRunner,Qe as PromptTestError,lt as QaReporter,Ks as RestartAppHandler,nr as RunJsHandler,ni as STEP_HANDLER_REGISTRY,Mr as STEP_PREFIX_REGEX,Or as SUBMIT_KEYWORDS,Ys as ScrollHandler,Vs as SelectHandler,bt as SemanticFormFiller,rr as SetVariableHandler,Jt as SpecRecursionError,Ws as SwipeHandler,_s as TapHandler,Xs as TerminateAppHandler,Js as ToggleHandler,os as TypeHandler,Sn as VOLATILE_DATA_PATTERNS,Us as VerifyHandler,qs as WaitHandler,sr as WaitUntilHandler,Vt as XML_HIERARCHY_PATTERNS,Et as acquireDeviceLock,yn as assembleAstBlocks,Ns as baselineManager,Qr as buildDiagnosticError,vr as buildFeedbackUrl,Un as buildIssueUrl,Di as calculatePercentile,Ci as calculateReleaseConfidence,Yr as clusterSiblingElements,ha as compareHierarchyNodes,Ue as computeCanonicalSemanticHash,ye as computeHierarchyHash,xp as createAndroidDriver,vp as createAutonomousExplorer,Ep as createAutonomousRecorder,Dp as createBaselineManager,si as createFailureTriageBundle,kp as createFormFiller,Tp as createMemoryEngine,wp as createPromptRunner,Pp as createQaReporter,lr as createSemanticResolver,ns as createSyntheticReport,Jn as detectActiveEnvironment,Ts as detectNavigationParadigm,jr as detectVisualDefects,Jd as displayCrashTriage,Rs as expandSpecIncludes,Rr as extractQuotedText,ei as extractSpecTags,Me as findActionButtons,Ot as findCheckableControls,Rt as findClosestMatches,Kt as findDevOverlay,at as findDrawerButton,Hr as findErrorDialogs,Vn as findExistingSpecs,Br as findHeaderActionButtons,He as findInAppBackButton,Le as findInputFields,zt as findListItems,ot as findNavigationTabs,bo as findNodes,Ur as findScrollableContainers,Zr as findTargetInputField,Pd as formatBenchmarkTable,di as formatDoctorReport,ti as formatStepTable,Hd as generateAggregatedHtml,Fn as generateSyntheticHierarchy,md as getActiveConfig,$r as getJitteredPollDelay,Qt as getSeedSynonyms,Fi as getSettleFactor,No as getSupportedLocales,Vr as getSynonyms,Pt as interpolateVariables,Je as isDestructiveAction,Ve as isExternalIntentAction,Wd as isFrameworkCrash,_e as isLogBoxNode,Ki as levenshteinDistance,On as loadConfig,qe as logger,ga as maskSensitiveValue,is as matchNodes,de as matchesText,_d as mergeReports,Gr as normalizeSemanticText,ae as normalizeText,_r as parseBounds,me as parseHierarchyXml,Gi as parseHierarchyXmlRegex,Qi as parseHierarchyXmlStreaming,mt as parsePrompt,qr as parseStepRange,Co as registerLocalePack,kt as releaseDeviceLock,fd as resetActiveConfig,ce as resolveTokenMatch,Td as runBenchmark,ls as runDoctor,Bn as sanitizeStackTrace,Oe as scaleSettle,Yn as startFeedbackSession,dp as startInteractiveWizard,yi as startLiveServer,$i as startRepl,Xi as stringSimilarity,br as summarizeMetrics,Mt as unescapeXml};