prompttest 1.4.0 → 1.5.1

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.
@@ -5,81 +5,81 @@
5
5
  * Licensed under the Business Source License 1.1 (BSL 1.1).
6
6
  * See LICENSE file in repository root for terms and conditions.
7
7
  */
8
- var Hi=Object.defineProperty;var de=(c,t,e)=>()=>{if(e)throw e[0];try{return c&&(t=c(c=0)),t}catch(s){throw e=[s],s}};var Tt=(c,t)=>{for(var e in t)Hi(c,e,{get:t[e],enumerable:!0})};var Pe,Hn=de(()=>{"use strict";Pe={DEVICES:["devices","-l"],SCREENCAP:["exec-out","screencap","-p"],FOREGROUND_APP:["shell","dumpsys","window"],CURRENT_FOCUS:["shell","dumpsys","activity","activities"],FORCE_STOP:c=>["shell","am","force-stop",c],CLEAR_APP:c=>["shell","pm","clear",c],LAUNCH_APP:c=>["shell","am","start","-a","android.intent.action.MAIN","-c","android.intent.category.LAUNCHER",c],UIAUTOMATOR_DUMP:["exec-out","uiautomator","dump","--compressed","/dev/tty"],INPUT_TAP:(c,t)=>["shell","input","tap",c.toString(),t.toString()],INPUT_TEXT:c=>["shell","input","text",c],INPUT_KEY:c=>["shell","input","keyevent",c.toString()],INPUT_SWIPE:(c,t,e,s,n=300)=>["shell","input","swipe",c.toString(),t.toString(),e.toString(),s.toString(),n.toString()],LOGCAT_CRASH:c=>["shell","logcat","-d","-b","crash","-s","AndroidRuntime:E"],CLEAR_CRASH_LOGS:()=>["shell","logcat","-b","crash","-c"],GRANT_PERMISSION:(c,t)=>["shell","pm","grant",c,t.startsWith("android.permission.")?t:`android.permission.${t}`],INSTALL_APK:c=>["install","-r",c],UNINSTALL_APP:c=>["uninstall",c],DEEP_LINK:c=>["shell","am","start","-a","android.intent.action.VIEW","-d",c],TCPIP:(c=5555)=>["tcpip",c.toString()],CONNECT:c=>["connect",c],DISCONNECT:c=>c?["disconnect",c]:["disconnect"],GET_WLAN_IP:["shell","ip","-f","inet","addr","show","wlan0"]}});function Bi(){let c=process.env.PROMPTTEST_SETTLE_FACTOR;if(!c)return 1;let t=parseFloat(c);return isNaN(t)||t<=0?1:t}function He(c,t=50){return Math.max(t,Math.round(c*Bi()))}function Bn(c=I.polling.intervalMs,t=I.polling.jitterMs){return Math.round(c+(Math.random()*2-1)*(t/2))}var I,Be=de(()=>{"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 He(500)},get backMs(){return He(700)},get launchMs(){return He(2500)},get stopMs(){return He(600)},get keyboardDismissMs(){return He(500)},get scrollMs(){return He(350)},get toggleMs(){return He(350)},get selectMs(){return He(600)},get inputFocusMs(){return He(250)},get inputClearMs(){return He(150)},get inputDoneMs(){return He(350)},get submitWaitMs(){return He(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(c=>c.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 Et,Pt,es,kt=de(()=>{"use strict";Et=class extends Error{exitCode;phase;constructor(t,e=1,s){super(t),this.name="PromptTestError",this.exitCode=e,this.phase=s,Object.setPrototypeOf(this,new.target.prototype)}},Pt=class extends Et{constructor(t,e="Device"){super(t,2,e),this.name="DeviceError"}},es=class extends Et{cycle;constructor(t,e,s="SpecLoader"){super(t,3,s),this.name="SpecRecursionError",this.cycle=e}}});import $e from"fs";import Ae from"path";import Un from"os";import Ui from"https";import ji from"http";import Wi from"readline";import{execSync as As,spawnSync as Yi}from"child_process";function tt(){if(Ne)return Ne;if(process.env.PROMPTTEST_ADB_PATH&&$e.existsSync(process.env.PROMPTTEST_ADB_PATH))return Ne=process.env.PROMPTTEST_ADB_PATH,Ne;let c=process.platform==="win32",t=c?"adb.exe":"adb",e=[Ae.join(Ae.dirname(process.execPath),"platform-tools",t),Ae.join(Ae.dirname(process.execPath),t),Ae.join(process.cwd(),"platform-tools",t),Ae.join(process.cwd(),"dist","bin","platform-tools",t)];for(let o of e)if($e.existsSync(o))return Ne=o,Ne;try{let a=As(c?"where.exe adb":"which adb",{encoding:"utf-8",stdio:["ignore","pipe","ignore"]}).trim();if(a){let l=a.split(/\r?\n/)[0].trim();if($e.existsSync(l))return Ne=l,Ne}}catch{}let s=[process.env.ANDROID_HOME,process.env.ANDROID_SDK_ROOT].filter(Boolean);for(let o of s){let a=Ae.join(o,"platform-tools",t);if($e.existsSync(a))return Ne=a,Ne}let n=[],r=Un.homedir();if(c){let o=process.env.LOCALAPPDATA||Ae.join(r,"AppData","Local");n.push(Ae.join(o,"Android","Sdk","platform-tools","adb.exe"));let a=process.env.ProgramFiles||"C:\\Program Files";n.push(Ae.join(a,"Android","platform-tools","adb.exe"))}else process.platform==="darwin"?(n.push(Ae.join(r,"Library","Android","sdk","platform-tools","adb")),n.push("/opt/homebrew/bin/adb"),n.push("/usr/local/bin/adb")):(n.push(Ae.join(r,"Android","Sdk","platform-tools","adb")),n.push("/usr/bin/adb"),n.push("/usr/local/bin/adb"));for(let o of n)if($e.existsSync(o))return Ne=o,Ne;let i=Ae.join(r,".prompttest","platform-tools",t);return $e.existsSync(i)?(Ne=i,Ne):(Ne="adb",Ne)}function Vi(c){let t=c||tt();try{let e=Yi(t,["version"],{encoding:"utf-8",timeout:3e3});return e.status===0&&(e.stdout.includes("Android Debug Bridge")||e.stdout.includes("version"))}catch{return!1}}function Ji(){Ne=null}function zi(c,t){return new Promise((e,s)=>{let n=$e.createWriteStream(t);function r(i){(i.startsWith("https")?Ui:ji).get(i,a=>{if(a.statusCode&&a.statusCode>=300&&a.statusCode<400&&a.headers.location)return r(a.headers.location);if(a.statusCode!==200)return s(new Error(`Download failed with HTTP status ${a.statusCode}`));a.pipe(n),n.on("finish",()=>{n.close(()=>e())})}).on("error",a=>{$e.unlink(t,()=>{}),s(a)})}r(c)})}async function Xi(c){let t=Un.homedir(),e=c||Ae.join(t,".prompttest","platform-tools");$e.mkdirSync(e,{recursive:!0});let s=process.platform==="win32",n=process.platform==="darwin",r=s?"adb.exe":"adb",i=s?"https://dl.google.com/android/repository/platform-tools-latest-windows.zip":n?"https://dl.google.com/android/repository/platform-tools-latest-darwin.zip":"https://dl.google.com/android/repository/platform-tools-latest-linux.zip",o=Ae.join(e,`platform-tools-download-${Date.now()}.zip`),a=Ae.join(e,`extract-tmp-${Date.now()}`);try{await zi(i,o),$e.mkdirSync(a,{recursive:!0}),s?As(`powershell -NoProfile -Command "Expand-Archive -Path '${o}' -DestinationPath '${a}' -Force"`,{stdio:"ignore",timeout:6e4}):As(`unzip -q -o "${o}" -d "${a}"`,{stdio:"ignore",timeout:6e4});let l=Ae.join(a,"platform-tools"),d=$e.existsSync(l)?l:a,p=$e.readdirSync(d);for(let u of p){let m=Ae.join(d,u),f=Ae.join(e,u);$e.copyFileSync(m,f)}let h=Ae.join(e,r);if(!s&&$e.existsSync(h))try{$e.chmodSync(h,493)}catch{}return Ji(),Ne=h,h}finally{try{$e.existsSync(o)&&$e.unlinkSync(o),$e.existsSync(a)&&$e.rmSync(a,{recursive:!0,force:!0})}catch{}}}async function Gi(){let c=process.argv;return c.includes("--download-adb")||c.includes("-y")||c.includes("--yes")?!0:!process.stdin.isTTY||process.env.CI==="true"?!1:new Promise(t=>{let e=Wi.createInterface({input:process.stdin,output:process.stdout});console.log(`
8
+ var Mi=Object.defineProperty;var ie=(a,t,e)=>()=>{if(e)throw e[0];try{return a&&(t=a(a=0)),t}catch(s){throw e=[s],s}};var Qe=(a,t)=>{for(var e in t)Mi(a,e,{get:t[e],enumerable:!0})};var Ae,mr=ie(()=>{"use strict";Ae={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,t)=>["shell","input","tap",a.toString(),t.toString()],INPUT_TEXT:a=>["shell","input","text",a],INPUT_KEY:a=>["shell","input","keyevent",a.toString()],INPUT_SWIPE:(a,t,e,s,n=300)=>["shell","input","swipe",a.toString(),t.toString(),e.toString(),s.toString(),n.toString()],LOGCAT_CRASH:a=>["shell","logcat","-d","-b","crash","-s","AndroidRuntime:E"],CLEAR_CRASH_LOGS:()=>["shell","logcat","-b","crash","-c"],GRANT_PERMISSION:(a,t)=>["shell","pm","grant",a,t.startsWith("android.permission.")?t:`android.permission.${t}`],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 t=parseFloat(a);return isNaN(t)||t<=0?1:t}function Ue(a,t=50){return Math.max(t,Math.round(a*Fi()))}function fr(a=R.polling.intervalMs,t=R.polling.jitterMs){return Math.round(a+(Math.random()*2-1)*(t/2))}var R,je=ie(()=>{"use strict";R={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 Ue(500)},get backMs(){return Ue(700)},get launchMs(){return Ue(2500)},get stopMs(){return Ue(600)},get keyboardDismissMs(){return Ue(500)},get scrollMs(){return Ue(350)},get toggleMs(){return Ue(350)},get selectMs(){return Ue(600)},get inputFocusMs(){return Ue(250)},get inputClearMs(){return Ue(150)},get inputDoneMs(){return Ue(350)},get submitWaitMs(){return Ue(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 Ct,It,os,Nt=ie(()=>{"use strict";Ct=class extends Error{exitCode;phase;constructor(t,e=1,s){super(t),this.name="PromptTestError",this.exitCode=e,this.phase=s,Object.setPrototypeOf(this,new.target.prototype)}},It=class extends Ct{constructor(t,e="Device"){super(t,2,e),this.name="DeviceError"}},os=class extends Ct{cycle;constructor(t,e,s="SpecLoader"){super(t,3,s),this.name="SpecRecursionError",this.cycle=e}}});import Ce from"fs";import $e from"path";import br from"os";import Oi from"https";import _i from"http";import Hi from"readline";import{execSync as js,spawnSync as Bi}from"child_process";function it(){if(Ne)return Ne;if(process.env.PROMPTTEST_ADB_PATH&&Ce.existsSync(process.env.PROMPTTEST_ADB_PATH))return Ne=process.env.PROMPTTEST_ADB_PATH,Ne;let a=process.platform==="win32",t=a?"adb.exe":"adb",e=[$e.join($e.dirname(process.execPath),"platform-tools",t),$e.join($e.dirname(process.execPath),t),$e.join(process.cwd(),"platform-tools",t),$e.join(process.cwd(),"dist","bin","platform-tools",t)];for(let o of e)if(Ce.existsSync(o))return Ne=o,Ne;try{let l=js(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(Ce.existsSync(c))return Ne=c,Ne}}catch{}let s=[process.env.ANDROID_HOME,process.env.ANDROID_SDK_ROOT].filter(Boolean);for(let o of s){let l=$e.join(o,"platform-tools",t);if(Ce.existsSync(l))return Ne=l,Ne}let n=[],i=br.homedir();if(a){let o=process.env.LOCALAPPDATA||$e.join(i,"AppData","Local");n.push($e.join(o,"Android","Sdk","platform-tools","adb.exe"));let l=process.env.ProgramFiles||"C:\\Program Files";n.push($e.join(l,"Android","platform-tools","adb.exe"))}else process.platform==="darwin"?(n.push($e.join(i,"Library","Android","sdk","platform-tools","adb")),n.push("/opt/homebrew/bin/adb"),n.push("/usr/local/bin/adb")):(n.push($e.join(i,"Android","Sdk","platform-tools","adb")),n.push("/usr/bin/adb"),n.push("/usr/local/bin/adb"));for(let o of n)if(Ce.existsSync(o))return Ne=o,Ne;let r=$e.join(i,".prompttest","platform-tools",t);return Ce.existsSync(r)?(Ne=r,Ne):(Ne="adb",Ne)}function Ui(a){let t=a||it();try{let e=Bi(t,["version"],{encoding:"utf-8",timeout:3e3});return e.status===0&&(e.stdout.includes("Android Debug Bridge")||e.stdout.includes("version"))}catch{return!1}}function ji(){Ne=null}function Wi(a,t){return new Promise((e,s)=>{let n=Ce.createWriteStream(t);function i(r){(r.startsWith("https")?Oi:_i).get(r,l=>{if(l.statusCode&&l.statusCode>=300&&l.statusCode<400&&l.headers.location)return i(l.headers.location);if(l.statusCode!==200)return s(new Error(`Download failed with HTTP status ${l.statusCode}`));l.pipe(n),n.on("finish",()=>{n.close(()=>e())})}).on("error",l=>{Ce.unlink(t,()=>{}),s(l)})}i(a)})}async function Yi(a){let t=br.homedir(),e=a||$e.join(t,".prompttest","platform-tools");Ce.mkdirSync(e,{recursive:!0});let s=process.platform==="win32",n=process.platform==="darwin",i=s?"adb.exe":"adb",r=s?"https://dl.google.com/android/repository/platform-tools-latest-windows.zip":n?"https://dl.google.com/android/repository/platform-tools-latest-darwin.zip":"https://dl.google.com/android/repository/platform-tools-latest-linux.zip",o=$e.join(e,`platform-tools-download-${Date.now()}.zip`),l=$e.join(e,`extract-tmp-${Date.now()}`);try{await Wi(r,o),Ce.mkdirSync(l,{recursive:!0}),s?js(`powershell -NoProfile -Command "Expand-Archive -Path '${o}' -DestinationPath '${l}' -Force"`,{stdio:"ignore",timeout:6e4}):js(`unzip -q -o "${o}" -d "${l}"`,{stdio:"ignore",timeout:6e4});let c=$e.join(l,"platform-tools"),d=Ce.existsSync(c)?c:l,p=Ce.readdirSync(d);for(let h of p){let m=$e.join(d,h),b=$e.join(e,h);Ce.copyFileSync(m,b)}let u=$e.join(e,i);if(!s&&Ce.existsSync(u))try{Ce.chmodSync(u,493)}catch{}return ji(),Ne=u,u}finally{try{Ce.existsSync(o)&&Ce.unlinkSync(o),Ce.existsSync(l)&&Ce.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(t=>{let e=Hi.createInterface({input:process.stdin,output:process.stdout});console.log(`
9
9
  \u26A0\uFE0F Android Debug Bridge (ADB) was not found on your system.
10
10
  PromptTest requires ADB to communicate with physical or emulated Android devices.
11
11
 
12
12
  Would you like PromptTest to download Google's official
13
- platform-tools archive (~3.5 MB) to ~/.prompttest/platform-tools/? [Y/n]: `),e.question("",s=>{e.close();let n=s.trim().toLowerCase();t(n===""||n==="y"||n==="yes")})})}async function jn(c={}){if(Vi())return tt();if(c.skipPrompt?!0:await Gi()){console.log(`
14
- \u2B07\uFE0F Downloading official Google platform-tools (ADB)...`);let e=await Xi();return console.log(`\u2714 ADB successfully provisioned and ready: ${e}
15
- `),e}throw new Pt(`Android Debug Bridge (ADB) not found on this system.
13
+ platform-tools archive (~3.5 MB) to ~/.prompttest/platform-tools/? [Y/n]: `),e.question("",s=>{e.close();let n=s.trim().toLowerCase();t(n===""||n==="y"||n==="yes")})})}async function yr(a={}){if(Ui())return it();if(a.skipPrompt?!0:await Vi()){console.log(`
14
+ \u2B07\uFE0F Downloading official Google platform-tools (ADB)...`);let e=await Yi();return console.log(`\u2714 ADB successfully provisioned and ready: ${e}
15
+ `),e}throw new It(`Android Debug Bridge (ADB) not found on this system.
16
16
  To resolve:
17
17
  1. Re-run with --download-adb to automatically download Google's official platform-tools.
18
18
  2. Install Android Studio: https://developer.android.com/studio
19
19
  3. Set custom ADB path via: export PROMPTTEST_ADB_PATH="C:\\path\\to\\adb.exe"
20
- `)}var Ne,$s=de(()=>{"use strict";kt();Ne=null});import{spawn as jt}from"child_process";import Cs from"readline";function Ki(c,t,e,s){let n={},r=c.match(/level:\s*(\d+)/i),i=c.match(/temperature:\s*(\d+)/i),o=c.match(/status:\s*(\d+)/i);(r||i||o)&&(n.battery={...r?{level:Number(r[1])}:{},...i?{temperatureC:Number(i[1])/10}:{},...o?{status:o[1]}:{}});let a=t.match(/(?:Physical|Override)\s+size:\s*(\d+)x(\d+)/i),l=t.match(/(?:Physical|Override)\s+density:\s*(\d+)/i);(a||l)&&(n.display={...a?{width:Number(a[1]),height:Number(a[2])}:{},...l?{density:Number(l[1])}:{}});let d=e.match(/TOTAL\s+([\d,]+)/i),p=e.match(/Private Dirty:\s*(\d+)/i),h=e.match(/Native Heap:\s*(\d+)/i),m=[...(e.match(/Total PSS by category:\s*([\s\S]*?)(?:\n\s*\n|Total RAM:|$)/i)?.[1]||"").matchAll(/^\s*([\d,]+)K:\s*(.+)$/gim)],f=m.reduce((k,C)=>k+Number(C[1].replace(/,/g,"")),0),x=m.find(k=>/native/i.test(k[2]));(d||p||h||f)&&(n.memory={...d?{pssKb:Number(d[1].replace(/,/g,""))}:f?{pssKb:f}:{},...p?{privateDirtyKb:Number(p[1])}:{},...h?{nativeHeapKb:Number(h[1])}:x?{nativeHeapKb:Number(x[1].replace(/,/g,""))}:{}});let v=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(v||S||y)&&(n.graphics={...v?{framesRendered:Number(v[1])}:{},...S?{jankyFrames:Number(S[1])}:{},...y?{percentile90Ms:Number(y[1])}:{}}),n}var rt,re,Fe=de(()=>{"use strict";Hn();Be();kt();$s();rt=class{defaultSerial=null;cachedDimensions=new Map;inFlightHierarchies=new Map;constructor(t){t&&(this.defaultSerial=t)}setDefaultDevice(t){this.defaultSerial=t}getDefaultDevice(){return this.defaultSerial}async getScreenDimensions(t){let e=t||this.defaultSerial||"default";if(this.cachedDimensions.has(e))return this.cachedDimensions.get(e);try{let{stdout:n}=await this.execAdb(["shell","wm","size"],t),r=n.match(/(?:Physical|Override)\s*size:\s*(\d+)x(\d+)/i);if(r){let i={width:parseInt(r[1],10),height:parseInt(r[2],10)};return this.cachedDimensions.set(e,i),i}}catch{}let s={width:I.screen.fallbackWidth,height:I.screen.fallbackHeight};return this.cachedDimensions.set(e,s),s}clearScreenDimensionsCache(t){t?this.cachedDimensions.delete(t):this.cachedDimensions.clear()}async getNavigationBarHeight(t){try{let{stdout:e}=await this.execAdb(["shell","dumpsys","window","displays"],t).catch(()=>({stdout:""})),s=e.match(/(?:navigationBarHeight|navBarHeight|navigationBarFrame)[=:\s]+(\d+)/i);if(s&&parseInt(s[1],10)>0)return parseInt(s[1],10)}catch{}return 0}isGlobalAdbCommand(t){return t?["devices","connect","disconnect","version","kill-server","start-server","help"].includes(t.toLowerCase()):!0}async execAdbInternal(t,e,s=2e4){let n=e||this.defaultSerial;!n&&!this.isGlobalAdbCommand(t[0])&&(await this.ensureDefaultDevice(),n=this.defaultSerial);let r=n&&!this.isGlobalAdbCommand(t[0])?["-s",n,...t]:[...t],i=await jn();return new Promise((o,a)=>{let l=jt(i,r),d="",p="",h=!1,u=setTimeout(()=>{h=!0;try{l.kill("SIGKILL")}catch{}a(new Error(`ADB command timed out after ${s}ms: adb ${r.join(" ")}`))},s);l.stdout.on("data",m=>{d+=m.toString("utf-8")}),l.stderr.on("data",m=>{p+=m.toString("utf-8")}),l.on("close",m=>{clearTimeout(u),!h&&(m===0?o({stdout:d,stderr:p}):a(new Error(`ADB command failed (code ${m}): adb ${r.join(" ")}
21
- Error: ${p}`)))}),l.on("error",m=>{clearTimeout(u),!h&&a(m)})})}async execAdb(t,e,s=I.adb.commandTimeoutMs,n=I.device.maxDeviceRetries){let r;for(let i=0;i<=n;i++)try{return await this.execAdbInternal(t,e,s)}catch(o){r=o;let a=o instanceof Error?o.message:String(o),l=a.toLowerCase(),d=l.includes("device offline")||l.includes("device not found"),p=d||l.includes("error: closed")||l.includes("timed out");if(i>=n||!p||this.isGlobalAdbCommand(t[0]))throw d?new Pt(`Target Android device ${e||this.defaultSerial||""} is offline or disconnected: ${a}`):o;if(I.device.autoReconnect&&d)try{await this.execAdbInternal(["reconnect"],e,5e3).catch(()=>{})}catch{}await new Promise(h=>setTimeout(h,I.adb.retryBaseDelayMs*Math.pow(2,i+1)))}throw r||new Error(`ADB command failed after ${n} retries: ${t.join(" ")}`)}async execAdbBinary(t,e,s=45e3){let n=e||this.defaultSerial;!n&&!this.isGlobalAdbCommand(t[0])&&(await this.ensureDefaultDevice(),n=this.defaultSerial);let r=n&&!this.isGlobalAdbCommand(t[0])?["-s",n,...t]:[...t];return new Promise((i,o)=>{let a=jt(tt(),r),l=[],d="",p=!1,h=setTimeout(()=>{p=!0;try{a.kill("SIGKILL")}catch{}o(new Error(`ADB binary command timed out after ${s}ms: adb ${r.join(" ")}`))},s);a.stdout.on("data",u=>l.push(Buffer.from(u))),a.stderr.on("data",u=>{d+=u.toString("utf-8")}),a.on("close",u=>{clearTimeout(h),!p&&(u===0?i(Buffer.concat(l)):o(new Error(`ADB binary command failed (code ${u}): adb ${r.join(" ")}
22
- ${d}`)))}),a.on("error",u=>{clearTimeout(h),!p&&o(u)})})}async getConnectedDevices(){let{stdout:t}=await this.execAdb(Pe.DEVICES),e=t.split(`
23
- `).map(o=>o.trim()).filter(Boolean),s=e.findIndex(o=>o.toLowerCase().includes("list of devices attached")),n=s!==-1?e.slice(s+1):e,r=[];return r=(o=>{let a=[];for(let l of o){if(!l||l.startsWith("*")||l.toLowerCase().includes("list of devices"))continue;let d=l.split(/\s+/);if(d.length>=2){let p=d[0],h=d[1],u,m;for(let f of d.slice(2))f.startsWith("model:")&&(u=f.replace("model:","")),f.startsWith("product:")&&(m=f.replace("product:",""));a.push({serial:p,state:h,model:u,product:m})}}return a})(n),r}async wakeDevice(t){try{await this.execAdb(["shell","input","keyevent","224"],t),await this.execAdb(["shell","wm","dismiss-keyguard"],t)}catch{}}async ensureDefaultDevice(t){if(t)return await this.wakeDevice(t),t;if(this.defaultSerial)return await this.wakeDevice(this.defaultSerial),this.defaultSerial;let s=(await this.getConnectedDevices()).find(n=>n.state==="device");return s&&(this.defaultSerial=s.serial,await this.wakeDevice(this.defaultSerial)),this.defaultSerial||void 0}async getDeviceModel(t){try{let{stdout:r}=await this.execAdb(["shell","getprop","ro.product.model"],t),i=r.trim();if(i)return i}catch{}let e=await this.getConnectedDevices(),s=t||this.defaultSerial;return e.find(r=>r.serial===s)?.model||s||"Android Device"}async captureScreenshot(t){return this.execAdbBinary(Pe.SCREENCAP,t)}async getUiHierarchy(t){let e=t||this.defaultSerial||"default",s=this.inFlightHierarchies.get(e);if(s)return s;let n=(async()=>{try{let{stdout:r}=await this.execAdb(Pe.UIAUTOMATOR_DUMP,t,1e4,0),i=r.indexOf("<hierarchy");return i>=0?r.slice(i):r}finally{this.inFlightHierarchies.delete(e)}})();return this.inFlightHierarchies.set(e,n),n}clearInFlightHierarchies(t){t?this.inFlightHierarchies.delete(t):this.inFlightHierarchies.clear()}async getForegroundPackage(t){let e=s=>{let n=s.toLowerCase();return n==="android"||n.includes("systemui")||n.includes("inputmethod")||n.includes("latin")||n.includes("gboard")||n.includes("swiftkey")||n.includes("ime")};try{let{stdout:s}=await this.execAdb(["shell","dumpsys","activity","activities"],t).catch(()=>({stdout:""})),n=s.match(/(?:topResumedActivity|mResumedActivity|ResumedActivity)[=:\s]+(?:ActivityRecord\{[^}]*?\s+)?([a-zA-Z0-9._]+)\//i);if(n&&n[1]&&!e(n[1]))return n[1];let{stdout:r}=await this.execAdb(Pe.FOREGROUND_APP,t).catch(()=>({stdout:""})),i=r.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]&&!e(o[1]))return o[1]}catch{}return null}async tap(t,e,s){await this.execAdb(Pe.INPUT_TAP(t,e),s)}async inputText(t,e){let s=t.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(Pe.INPUT_TEXT(s),e)}catch{for(let n of t)if(n===" ")await this.pressKey(62,e);else if(n==="@")await this.pressKey(77,e);else{let r=n.replace(/([\\"'&<>()|$;#*?~`%])/g,"\\$1");await this.execAdb(["shell","input","text",r],e).catch(()=>{})}}}async pressKey(t,e){await this.execAdb(Pe.INPUT_KEY(t),e)}async pressEnter(t){await this.pressKey(66,t)}async pressTab(t){await this.pressKey(61,t)}async pressHome(t){await this.pressKey(3,t)}async pressBack(t){await this.pressKey(4,t)}async setWifiEnabled(t,e){await this.execAdb(["shell","svc","wifi",t?"enable":"disable"],e)}async setAirplaneMode(t,e){await this.execAdb(["shell","cmd","connectivity","airplane-mode",t?"enable":"disable"],e)}async setOrientation(t,e){await this.execAdb(["shell","content","insert","--uri","content://settings/system","--bind","name:s:accelerometer_rotation","--bind","value:i:0"],e).catch(()=>{});let s=t==="landscape"?"1":"0";await this.execAdb(["shell","settings","put","system","user_rotation",s],e)}async openNotificationShade(t){await this.execAdb(["shell","cmd","statusbar","expand-notifications"],t)}async collapseNotificationShade(t){await this.execAdb(["shell","cmd","statusbar","collapse"],t)}async clearInput(t=45,e){let s=Array(t).fill("67");await this.execAdb(["shell","input","keyevent",...s],e)}async clearField(t,e=80){await this.pressKey(123,t);let s=Math.max(50,e+10),n=Array(s).fill("67");await this.execAdb(["shell","input","keyevent",...n],t).catch(()=>{}),await this.pressKey(122,t);let r=Array(Math.max(30,Math.floor(e/2))).fill("112");await this.execAdb(["shell","input","keyevent",...r],t).catch(()=>{})}async swipe(t,e,s,n,r=300,i){await this.execAdb(Pe.INPUT_SWIPE(t,e,s,n,r),i)}async launchApp(t,e){try{let{stdout:s}=await this.execAdb(["shell","cmd","package","resolve-activity","--brief",t],e),r=(s||"").split(`
24
- `).map(i=>i.trim()).find(i=>i.includes("/")&&!i.includes(" "));if(r){await this.execAdb(["shell","am","start","-n",r],e);return}}catch{}await this.execAdb(Pe.LAUNCH_APP(t),e)}async forceStop(t,e){await this.execAdb(Pe.FORCE_STOP(t),e)}async clearAppData(t,e){await this.execAdb(Pe.CLEAR_APP(t),e)}async restartApp(t,e){await this.forceStop(t,e),await new Promise(s=>setTimeout(s,600)),await this.launchApp(t,e)}async grantPermission(t,e,s){await this.execAdb(Pe.GRANT_PERMISSION(t,e),s)}async isAppInstalled(t,e){try{let{stdout:s}=await this.execAdb(["shell","pm","list","packages",t],e);return s.toLowerCase().includes(`package:${t.toLowerCase()}`)||s.includes(t)}catch{return!1}}async installApk(t,e){await this.execAdb(Pe.INSTALL_APK(t),e)}async uninstallApp(t,e){await this.execAdb(Pe.UNINSTALL_APP(t),e)}async openDeepLink(t,e){await this.execAdb(Pe.DEEP_LINK(t),e)}async checkCrashes(t,e){try{let{stdout:s}=await this.execAdb(Pe.LOGCAT_CRASH(t),e);if(s&&s.includes("FATAL EXCEPTION")&&s.includes(t))return s}catch{}return null}async clearCrashLogs(t){try{await this.execAdb(Pe.CLEAR_CRASH_LOGS(),t)}catch{}}async getDeviceIp(t){try{let{stdout:e}=await this.execAdb(Pe.GET_WLAN_IP,t),s=e.match(/inet\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}try{let{stdout:e}=await this.execAdb(["shell","ip","route"],t),s=e.match(/src\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}return null}async connectWifi(t,e=5555,s){let n=t,r=e;if(t&&t.includes(":")){let p=t.split(":");n=p[0],r=parseInt(p[1],10)||e}if(!n){let p=await this.getDeviceIp(s);if(!p)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>");n=p}let i=`${n}:${r}`;if(t)try{let{stdout:p,stderr:h}=await this.execAdb(Pe.CONNECT(i)),u=`${p} ${h}`.trim();if(u.toLowerCase().includes("connected")||u.toLowerCase().includes("already"))return this.defaultSerial=i,{success:!0,target:i,message:u}}catch{}try{await this.execAdb(Pe.TCPIP(r),s),await new Promise(p=>setTimeout(p,1e3))}catch{}let{stdout:o,stderr:a}=await this.execAdb(Pe.CONNECT(i)),l=`${o} ${a}`.trim(),d=l.toLowerCase().includes("connected")||l.toLowerCase().includes("already");return d&&(this.defaultSerial=i),{success:d,target:i,message:l}}async disconnectWifi(t){let{stdout:e,stderr:s}=await this.execAdb(Pe.DISCONNECT(t));return`${e} ${s}`.trim()}async pairWifi(t,e){if(!/^[^:\s]+:\d+$/.test(t))throw new Error("Pairing target must be an IP:port value.");if(!/^\d{4,8}$/.test(e))throw new Error("Pairing code must contain 4 to 8 digits.");let{stdout:s,stderr:n}=await this.execAdb(["pair",t,e]),r=`${s} ${n}`.trim();return{success:/successfully paired|already paired/i.test(r),message:r}}async getDeviceMetrics(t){let[s,n,r,i]=await Promise.all([this.execAdb(["shell","dumpsys","battery"],t,8e3,0).then(o=>o.stdout).catch(()=>""),this.execAdb(["shell","wm","size"],t,8e3,0).then(async o=>`${o.stdout}
25
- ${(await this.execAdb(["shell","wm","density"],t,8e3,0).catch(()=>({stdout:""}))).stdout}`).catch(()=>""),this.execAdb(["shell","dumpsys","meminfo"],t,8e3,0).then(o=>o.stdout).catch(()=>""),this.execAdb(["shell","dumpsys","gfxinfo"],t,8e3,0).then(o=>o.stdout).catch(()=>"")]);return Ki(s,n,r,i)}streamLogcatEvents(t,e){let s=["-s",t,"shell","logcat","-b","events","-v","time"],n=jt(tt(),s),r=Cs.createInterface({input:n.stdout});return r.on("line",e),n.on("close",()=>{try{r.close()}catch{}}),n.on("error",()=>{}),n}streamLogcatCrashes(t,e){let s=["-s",t,"shell","logcat","-T","1","-s","AndroidRuntime:E","ANRManager:E","-v","time"],n=jt(tt(),s),r=Cs.createInterface({input:n.stdout});return r.on("line",e),n.on("close",()=>{try{r.close()}catch{}}),n.on("error",()=>{}),n}streamLogcatToasts(t,e){let s=["-s",t,"shell","logcat","-s","Toast:D","Snackbar:D","-v","time"],n=jt(tt(),s),r=Cs.createInterface({input:n.stdout});return r.on("line",e),n.on("close",()=>{try{r.close()}catch{}}),n.on("error",()=>{}),n}async getLogcatTail(t=150,e){try{let{stdout:s}=await this.execAdb(["logcat","-d","-t",String(t),"-v","time"],e);return s}catch(s){return`Failed to capture logcat: ${s instanceof Error?s.message:String(s)}`}}async startScreenRecording(t="/sdcard/prompttest_temp.mp4",e){let s=e||this.defaultSerial,n=s?["-s",s,"shell","screenrecord",t]:["shell","screenrecord",t],r=jt(tt(),n);return r.on("error",()=>{}),{remotePath:t,process:r}}async stopScreenRecording(t,e,s){try{try{t.process.kill("SIGINT")}catch{}return await this.execAdb(["shell","pkill","-2","screenrecord"],s).catch(()=>{}),await new Promise(n=>setTimeout(n,1500)),await this.execAdb(["pull",t.remotePath,e],s),await this.execAdb(["shell","rm","-f",t.remotePath],s).catch(()=>{}),e}catch{return""}}async isKeyboardVisible(t){try{let{stdout:e}=await this.execAdb(["shell","dumpsys","input_method"],t,4e3,0);return e.includes("mInputShown=true")||e.includes("isInputViewShown=true")||e.includes("mHasSurface=true")}catch{return!1}}async ensureAccessibilityServices(t){try{await this.execAdb(["shell","settings","put","secure","accessibility_enabled","1"],t,4e3,0)}catch{}}},re=new rt});function Vn(c){let t=c.trim(),e=t.match(/^"([^"]+)"$/);if(e)return e[1].trim();let s=t.match(/^'([^']+)'$/);return s?s[1].trim():t.replace(/^['"]+|['"]+$/g,"").trim()}function he(c){for(let t of c)if(t!=null&&t.trim().length>0)return t.trim();return""}function Dt(c,t){let e=c;if(t)for(let[s,n]of Object.entries(t))e=e.replace(new RegExp(`\\$${s}\\b`,"g"),()=>n);return e=e.replace(/\$ENV\{([A-Z0-9_]+)\}/gi,(s,n)=>process.env[n]||""),e=e.replace(/\$RANDOM_EMAIL\b/g,()=>`test_${Date.now().toString().slice(-6)}@prompttest.io`),e=e.replace(/\$RANDOM_NAME\b/g,()=>`User_${Math.floor(1e3+Math.random()*9e3)}`),e=e.replace(/\$RANDOM_PHONE\b/g,()=>`98${Math.floor(1e7+Math.random()*9e7)}`),e=e.replace(/\$TIMESTAMP\b/g,()=>Date.now().toString()),e=e.replace(/\$UUID\b/g,()=>`${Date.now().toString(16)}-${Math.random().toString(16).substring(2,8)}`),e=e.replace(/\$RANDOM_UUID\b/g,()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,s=>{let n=Math.random()*16|0;return(s==="x"?n:n&3|8).toString(16)})),e=e.replace(/\$RANDOM_CARD\b/g,()=>{let s="4532";for(let n=0;n<12;n++)s+=Math.floor(Math.random()*10);return s}),e=e.replace(/\$DATE_OFFSET\((-?\d+)\)/g,(s,n)=>{let r=parseInt(n,10)||0;return new Date(Date.now()+r*864e5).toISOString().slice(0,10)}),e=e.replace(/\$RANDOM_INT\((\d+),\s*(\d+)\)/g,(s,n,r)=>{let i=parseInt(n,10),o=parseInt(r,10);return String(Math.floor(i+Math.random()*(o-i+1)))}),e}function Wt(c){return(c||"").replace(/&#(\d+);/g,(t,e)=>{try{return String.fromCodePoint(parseInt(e,10))}catch{return""}}).replace(/&#x([0-9a-fA-F]+);/g,(t,e)=>{try{return String.fromCodePoint(parseInt(e,16))}catch{return""}}).replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&apos;/g,"'")}function ue(c){return Wt(c||"").trim().toLowerCase().replace(/[\u2018\u2019]/g,"'").replace(/[\u201C\u201D]/g,'"').replace(/\uFFFD/g,"").replace(new RegExp("\\p{Extended_Pictographic}","gu"),"").replace(/[\uE000-\uF8FF\uFFF0-\uFFFF]/g,"").replace(/\s+/g," ").trim()}function me(c,t){if(!c||!t)return!1;let e=ue(c),s=ue(t);if(e.length<=3){let n=Wn.get(e);if(!n){let r=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");n=new RegExp(`(?:^|\\b)${r}(?:\\b|$)`,"i"),Wn.set(e,n)}return n.test(s)}return s.includes(e)}function Qi(c,t){let e=ue(c),s=ue(t),n=e.length,r=s.length;if(n===0)return r;if(r===0)return n;let i=Array.from({length:r+1},(a,l)=>l),o=new Array(r+1);for(let a=1;a<=n;a++){o[0]=a;for(let d=1;d<=r;d++){let p=e[a-1]===s[d-1]?0:1;o[d]=Math.min(i[d]+1,o[d-1]+1,i[d-1]+p)}let l=i;i=o,o=l}return i[r]}function Zi(c,t){let e=ue(c),s=ue(t);if(e===s)return 1;if(!e||!s)return 0;let n=Math.max(e.length,s.length);return 1-Qi(e,s)/n}function At(c,t,e=3,s=.35){return Array.from(new Set(t.map(i=>i.trim()).filter(i=>i.length>0))).map(i=>({text:i,score:Zi(c,i)})).filter(i=>i.score>=s).sort((i,o)=>o.score-i.score).slice(0,e)}var Yn,pe,Z,ts,Wn,Jn,zn,st=de(()=>{"use strict";Yn=/^(\d+(?:\.\d+)*\.?|\-|\*)\s*/;pe=`(?:"([^"]+)"|'([^']+)'|([^'"\\s][^'"]*?[^'"\\s]|\\S))`;Z={TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:button\\s+|item\\s+)?${pe}$`,"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+)?${pe}$`,"i"),PROXIMITY_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+${pe}\\s+(?:next\\s+to|near|beside|inside|for)\\s+${pe}$`,"i"),CLEAR:new RegExp(`^(?:clear|empty)(?:\\s+(?:text\\s+(?:in|of)|field|input))?\\s+${pe}$`,"i"),LONG_PRESS:new RegExp(`^(?:long\\s+press|press\\s+and\\s+hold)\\s+(?:button\\s+|item\\s+)?${pe}$`,"i"),TYPE:new RegExp(`^(?:type|enter|input)\\s+${pe}\\s+(?:in|into)\\s+${pe}$`,"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+)?${pe}\\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+${pe}$`,"i"),SCROLL:/^(?:scroll|swipe)\s+(down|up)$/i,DIRECTIONAL_SWIPE:new RegExp(`^(?:swipe|scroll)\\s+(left|right|up|down)(?:\\s+on\\s+${pe})?$`,"i"),TOGGLE:new RegExp(`^(?:toggle|turn\\s+(on|off)|check|uncheck)\\s+(?:checkbox\\s+|switch\\s+)?${pe}$`,"i"),SELECT_DROPDOWN:new RegExp(`^(?:select|choose)\\s+${pe}\\s+from\\s+${pe}$`,"i"),LAUNCH_APP:new RegExp(`^(?:launch|open)\\s+(?:app\\s+)?${pe}$`,"i"),RESTART_APP:new RegExp(`^(?:restart|relaunch)\\s+(?:app\\s+)?${pe}$`,"i"),TERMINATE_APP:new RegExp(`^(?:terminate|close|force\\s*stop)\\s+(?:app\\s+)?(?:${pe})?$`,"i"),CLEAR_DATA:new RegExp(`^(?:clear\\s+(?:app\\s+)?data\\s+(?:for\\s+)?|reset\\s+app\\s+)${pe}$`,"i"),GRANT_PERMISSION:new RegExp(`^grant\\s+permissions?\\s+${pe}(?:\\s+(?:to|for)\\s+${pe})?$`,"i"),DEEP_LINK:new RegExp(`^(?:open\\s+deep\\s*link|navigate\\s+to\\s+url)\\s+${pe}$`,"i"),WAIT:/^wait\s+(\d+)\s*(?:s|sec|seconds|ms)?$/i,WAIT_UNTIL:new RegExp(`^(?:wait\\s+until|wait\\s+for)\\s+${pe}\\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+)?${pe}\\s+(?:is\\s+)?(visible|present|exists|appears?)|element\\s+${pe}\\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+)?${pe}$`,"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+${pe}$`,"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+${pe})?$`,"i"),API_REQUEST:new RegExp(`^(?:api|call\\s+api)\\s+(GET|POST|PUT|DELETE|PATCH)\\s+${pe}(?:\\s+with\\s+body\\s+${pe})?(?:\\s+store\\s+${pe}\\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+${pe}\\s+with\\s+subject\\s+${pe}(?:\\s+contains\\s+${pe})?$`,"i"),OPTIONAL_SUFFIX:/\s*\((?:optional|if\s+present|maybe)\)\s*$/i,FALLBACK_QUOTED_TAP:/^(?:tap|click)\s+.*?['"]([^'"]+)['"]/i},ts={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+)\]/};Wn=new Map;Jn=["sign in","signin","login","log in","submit","save","continue","next","done","send","confirm","proceed","post","rate","enter","join","apply","update"],zn=["sign in","signin","login","log in","submit","save","next","continue","done","like","fav","favorite","star","bookmark","add","post","confirm","apply","explore","search","filter"]});function Xn(c){let t=c.match(ts.BOUNDS_RECT);if(!t)return{x1:0,y1:0,x2:0,y2:0,width:0,height:0,centerX:0,centerY:0};let e=parseInt(t[1],10),s=parseInt(t[2],10),n=parseInt(t[3],10),r=parseInt(t[4],10);return{x1:e,y1:s,x2:n,y2:r,width:n-e,height:r-s,centerX:Math.round((e+n)/2),centerY:Math.round((s+r)/2)}}function qi(c){let t=c.match(ts.HIERARCHY_ROOT);if(!t)return{root:[],flat:[]};let e=t[1],s=new RegExp(ts.NODE_TAG.source,"gi"),n=new RegExp(ts.ATTR_KEY_VALUE.source,"g"),r=[],i=[],o=[],a;for(;(a=s.exec(e))!==null;){let l=a[0],d=a[1],p=a[2]==="/";if(l.startsWith("</node>")){o.pop();continue}let h={};n.lastIndex=0;let u;for(;(u=n.exec(d))!==null;)h[u[1]]=u[2];let m={index:parseInt(h.index||"0",10),text:Wt(h.text||""),resourceId:h["resource-id"]||"",className:h.class||"",packageName:h.package||"",contentDesc:Wt(h["content-desc"]||""),bounds:Xn(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:[]};i.push(m),o.length===0?r.push(m):o[o.length-1].children.push(m),p||o.push(m)}return{root:r,flat:i}}function er(c){let t=[],e=[],s=[],n=c.length,r=0;for(;r<n;){let i=c.indexOf("<",r);if(i===-1)break;if(c.startsWith("<!--",i)){let v=c.indexOf("-->",i+4);r=v===-1?n:v+3;continue}if(c.startsWith("<?",i)){let v=c.indexOf("?>",i+2);r=v===-1?n:v+2;continue}if(c.startsWith("</",i)){let v=c.indexOf(">",i+2);if(v===-1)break;c.slice(i+2,v).trim()==="node"&&s.pop(),r=v+1;continue}let o=-1,a=null,l=!1;for(let v=i+1;v<n;v++){let S=c[v];if(a)S===a&&(a=null);else if(S==='"'||S==="'")a=S;else if(S===">"){o=v,c[v-1]==="/"&&(l=!0);break}}if(o===-1)break;let d=c.slice(i+1,l?o-1:o).trim();r=o+1;let p=0;for(;p<d.length&&!/\s/.test(d[p]);)p++;if(d.slice(0,p)!=="node")continue;let u={},m=p,f=d.length;for(;m<f;){for(;m<f&&/\s/.test(d[m]);)m++;if(m>=f)break;let v=m;for(;m<f&&d[m]!=="="&&!/\s/.test(d[m]);)m++;let S=d.slice(v,m);for(;m<f&&/\s/.test(d[m]);)m++;if(m>=f||d[m]!=="=")break;for(m++;m<f&&/\s/.test(d[m]);)m++;if(m>=f)break;let y=d[m];if(y==='"'||y==="'"){m++;let k=m;for(;m<f&&d[m]!==y;)m++;u[S]=d.slice(k,m),m<f&&m++}else{let k=m;for(;m<f&&!/\s/.test(d[m]);)m++;u[S]=d.slice(k,m)}}let x={index:parseInt(u.index||"0",10),text:Wt(u.text||""),resourceId:u["resource-id"]||"",className:u.class||"",packageName:u.package||"",contentDesc:Wt(u["content-desc"]||""),bounds:Xn(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:[]};e.push(x),s.length===0?t.push(x):s[s.length-1].children.push(x),l||s.push(x)}return{root:t,flat:e}}function ve(c,t){if((t||I.parser?.mode||"regex")==="streaming")try{return er(c)}catch{}return qi(c)}function Ue(c){if(c.resourceId&&/logbox/i.test(c.resourceId)||c.className&&/logbox/i.test(c.className))return!0;let t=`${c.text} ${c.contentDesc}`.trim();return!!tr.some(e=>e.test(t))}function je(c){return c.filter(t=>Ue(t)?!1:(t.className.includes("EditText")||t.className.includes("TextInputEditText")||t.focused&&t.clickable)&&t.bounds.width>0&&t.bounds.height>0)}function Is(c){return c.className.includes("EditText")||c.className.includes("TextInputEditText")?!0:(c.children||[]).some(Is)}function $t(c,t){let e=t?.width||I.screen.fallbackWidth,s=t?.height||I.screen.fallbackHeight,n=s*.08,r=s*.88,i=c.filter(a=>!Ue(a)&&(a.className.includes("RecyclerView")||a.className.includes("ListView")||a.className.includes("ScrollView"))),o=[];for(let a of i)for(let l of a.children){if(Ue(l)||l.bounds.centerY<n||l.bounds.centerY>r)continue;(l.clickable||l.children.some(p=>!Ue(p)&&p.clickable&&p.bounds.width>e*.1))&&l.bounds.width>e*.08&&l.bounds.height>s*.02&&!Is(l)&&o.push(l)}return o.length===0?c.filter(a=>{if(Ue(a))return!1;let l=(a.text||a.contentDesc||"").trim();return l.length<=1||/^(remember me|forgot password|sign in|sign up|login|submit|cancel)$/i.test(l)?!1:a.clickable&&a.bounds.width>e*.14&&a.bounds.height>s*.033&&a.bounds.centerY>n&&a.bounds.centerY<r&&!a.className.includes("Button")&&!a.className.includes("EditText")&&!Is(a)}):o}function ze(c){return c.filter(t=>{if(Ue(t)||!t.clickable||t.bounds.width===0||t.className.includes("EditText"))return!1;let e=t.className.includes("Button")||t.className.includes("ImageButton"),s=zn.some(n=>me(n,t.text)||me(n,t.contentDesc)||me(n,t.resourceId));return e||s})}function Ct(c,t=I.screen.fallbackHeight,e){if(!c||c.length===0)return[];let s=c.find(u=>u.bounds.height>=t*.6&&u.bounds.y1<=t*.15),n=s?.bounds?.y1!==void 0&&s.bounds.y1>=0?s.bounds.y1:0,r=s?.bounds?.y2&&s.bounds.y2>n?s.bounds.y2:t,i=r-n,o=s?.bounds?.x2&&s.bounds.x2>0?s.bounds.x2-(s.bounds.x1||0):I.screen.fallbackWidth,a=n+i*.78,l=c.filter(u=>Ue(u)||e&&u.packageName&&!u.packageName.includes(e)||!u.clickable||u.bounds.width<=0||u.bounds.height<=0||!(u.bounds.centerY>=a||u.bounds.y2>=r-20)?!1:u.bounds.width>=o*.12&&u.bounds.width<=o*.55);if(l.length<=1)return l;let d=[];for(let u of l){let m=d.find(f=>Math.abs(f[0].bounds.centerY-u.bounds.centerY)<=30||Math.abs(f[0].bounds.y2-u.bounds.y2)<=20);m||(m=[],d.push(m)),m.push(u)}let p=d.map(u=>{u.sort((f,x)=>f.bounds.centerX-x.bounds.centerX);let m=[];for(let f of u)m.some(x=>Math.abs(x.bounds.centerX-f.bounds.centerX)<o*.08)||m.push(f);return m}).filter(u=>u.length<2||u.length>6?!1:u[u.length-1].bounds.x2-u[0].bounds.x1>=o*.6);return p.length===0?l.slice(0,5):(p.sort((u,m)=>m[0].bounds.centerY-u[0].bounds.centerY),p[0].slice(0,5))}function Gn(c){let t=[/fatal\s+exception/i,/unfortunately.*stopped/i,/(?:app|system|process).*(?:isn't|is not|not)\s+responding/i,/(?:isn't|is not|not)\s+responding/i,/force\s+close/i,/network\s+connection\s+(?:failed|lost|error)/i,/something went wrong/i,/(?:failed|unable)\s+to\s+(?:connect|load|fetch)/i];return c.filter(e=>{let s=`${e.text} ${e.contentDesc}`;return t.some(n=>n.test(s))})}function Xe(c,t,e=I.safety?.mode||"strict"){if(e==="disabled")return!1;let s=`${c.text} ${c.contentDesc} ${c.resourceId}`.trim();if((e==="moderate"?nr:sr).some(i=>i.test(s)))return!0;let r=t||I.safety?.customBlacklist||[];for(let i of r)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 Ge(c){let t=`${c.text} ${c.contentDesc} ${c.resourceId}`.trim();return ir.some(e=>e.test(t))}function ss(c){for(let t of c){let e=`${t.text} ${t.contentDesc} ${t.resourceId}`;if(t.resourceId.toLowerCase().includes("logbox")||e.includes("Unhandled Promise Rejection")||e.includes("SyntaxError")||e.includes("Encountered two children with the same key")||e.includes("Warning:")||e.includes("Console Error")||e.includes("Console Warning")||e.includes("Console Info")||e.includes("TypeError:")||e.includes("ReferenceError:")||/Log \d+ of \d+/i.test(e)||e.includes("LogBox")&&(t.className.includes("Modal")||t.className.includes("ViewGroup"))){let n="error";/warning/i.test(e)?n="warning":/info/i.test(e)&&(n="info");let r=t.resourceId.includes("notification")||t.bounds.height>0&&t.bounds.height<250,i,o=[];for(let d of c){let p=`${d.text} ${d.contentDesc}`.trim();if(!i){let h=p.match(/\b([a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+)\b/);h&&(i=h[1])}/\b(?:at\s+|\()?[a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+/i.test(p)&&!o.includes(p)&&p.length<200&&o.push(p)}let a=c.find(d=>d.clickable&&d.bounds.width>0&&(/^(?:dismiss|close)$/i.test((d.text||"").trim())||/^(?:dismiss|close)$/i.test((d.contentDesc||"").trim())));if(!a&&!r){let p=c.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>I.screen.fallbackHeight*.8);p.length>0&&(p.sort((h,u)=>h.bounds.x1-u.bounds.x1),a=p[0])}!a&&r&&(a=c.find(d=>d.clickable&&d.bounds.y1>=t.bounds.y1-20&&d.bounds.y2<=t.bounds.y2+20&&d.bounds.x1>=t.bounds.x2-120));let l=t.text||t.contentDesc||"React Native LogBox Console Overlay";return{type:"logbox",severity:n,node:t,errorText:l,sourceLocation:i,stackTrace:o.length>0?o:void 0,dismissButton:a,isToast:r}}if(t.resourceId.includes("alertTitle")&&(e.includes("isn't responding")||e.includes("has stopped"))){let n=c.find(r=>r.text.toLowerCase().includes("close app")||r.text.toLowerCase().includes("ok"));return{type:"anr",severity:"error",node:t,errorText:t.text||"Application Not Responding (ANR)",dismissButton:n}}}return null}function Yt(c){return c.filter(t=>Ue(t)?!1:(t.checkable||t.className.includes("CheckBox")||t.className.includes("Switch")||t.className.includes("RadioButton")||t.className.includes("SwitchCompat"))&&t.bounds.width>0&&t.bounds.height>0)}function pt(c,t=I.screen.fallbackHeight,e=I.screen.fallbackWidth){let s=t*.22,n=/(?:menu|hamburger|drawer|navigation|\u2630|open[_\s-]*drawer)/i,r=c.find(i=>{if(!i.clickable||i.bounds.width===0||i.bounds.centerY>s)return!1;let o=`${i.contentDesc} ${i.text} ${i.resourceId}`;return n.test(o)});return r||c.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 a=i.bounds.x1<=e*.25,l=i.bounds.width<=250&&i.bounds.height<=250;return a&&l})||null}function Kn(c,t=I.screen.fallbackHeight,e=I.screen.fallbackWidth){let s=t*.22;return c.filter(n=>{if(!n.clickable||n.bounds.width===0||n.bounds.height===0||n.bounds.centerY>s)return!1;let r=n.bounds.x2>=e*.75,i=n.bounds.width<=250&&n.bounds.height<=250;return r&&i})}function Qn(c){return c.filter(t=>{if(t.bounds.width===0||t.bounds.height===0)return!1;let e=t.className.includes("ScrollView")||t.className.includes("RecyclerView")||t.className.includes("ListView")||t.className.includes("NestedScrollView");return t.scrollable||e})}function Zn(c,t=I.screen.fallbackWidth,e=I.screen.fallbackHeight,s=I.visual.overlapRatio){let n=[];for(let h of c)(h.text||h.clickable)&&h.bounds.width>0&&(h.bounds.x2>t+10&&h.bounds.x1<t&&n.push({type:"OVERFLOW",severity:"WARN",description:`Element "${h.text||h.contentDesc||h.className}" overflows right screen boundary by ${h.bounds.x2-t}px`,elements:[h.text||h.contentDesc||h.className]}),h.bounds.y2>e+20&&h.bounds.y1<e&&n.push({type:"OVERFLOW",severity:"WARN",description:`Element "${h.text||h.contentDesc||h.className}" overflows bottom screen boundary by ${h.bounds.y2-e}px`,elements:[h.text||h.contentDesc||h.className]}));let r=c.filter(h=>h.text&&h.text.trim().length>1&&h.bounds.width>0&&h.bounds.height>0),i=200,o=new Map;for(let h of r){let u=Math.floor(h.bounds.y1/i),m=Math.floor(h.bounds.y2/i);for(let f=u;f<=m;f++)o.has(f)||o.set(f,[]),o.get(f).push(h)}let a=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 f=h[u],x=h[m];if(f===x||f.text===x.text)continue;let v=f.bounds.x1<x.bounds.x1?`${f.bounds.x1},${f.bounds.y1}:${x.bounds.x1},${x.bounds.y1}`:`${x.bounds.x1},${x.bounds.y1}:${f.bounds.x1},${f.bounds.y1}`;if(a.has(v))continue;a.add(v);let S=f.bounds,y=x.bounds;if(Math.abs(S.centerY-y.centerY)>(S.height+y.height)/2)continue;let k=Math.max(0,Math.min(S.x2,y.x2)-Math.max(S.x1,y.x1)),C=Math.max(0,Math.min(S.y2,y.y2)-Math.max(S.y1,y.y1)),j=k*C;if(j>0){let $=S.width*S.height,J=y.width*y.height,O=Math.min($,J);O>0&&j/O>s&&n.push({type:"OVERLAP",severity:"WARN",description:`Visual text collision: "${f.text}" overlaps with "${x.text}" (${Math.round(j)}px\xB2 collision)`,elements:[f.text,x.text]})}}let l=c.filter(h=>(h.className.includes("ProgressBar")||h.className.includes("ActivityIndicator"))&&h.bounds.width>0);l.length>0&&n.push({type:"STALLED_LOADER",severity:"WARN",description:`Active loading indicator (${l[0].className}) detected on screen`,elements:l.map(h=>h.className)});let d=[],p=new Set;for(let h of n){let u=`${h.type}::${[...h.elements].sort().join("::")}`;p.has(u)||(p.add(u),d.push(h))}return d}function Ns(c,t,e){let s=t?.height||I.screen.fallbackHeight,n=t?.width||I.screen.fallbackWidth,r=Ct(c,s,e);if(r.length>1&&r[0].bounds.centerY>=s*.75)return{paradigm:"bottom_tabs",roots:r};let i=s*.05,o=s*.22,a=c.filter(d=>{if(Ue(d)||e&&d.packageName&&!d.packageName.includes(e))return!1;let p=d.className.includes("Tab")||d.resourceId.toLowerCase().includes("tab")||d.resourceId.toLowerCase().includes("header_tab")||/(?:tab\s*\d|\bphotos\b|\bvideos\b|\baudios\b)/i.test(`${d.text} ${d.contentDesc}`),h=d.bounds.centerY>=i&&d.bounds.centerY<=o,u=d.bounds.height>25&&d.bounds.height<=220;return(p||h)&&d.clickable&&d.bounds.width>50&&u});if(a.length>=2){let d=[];for(let h of a){let u=d.find(m=>Math.abs(m[0].bounds.centerY-h.bounds.centerY)<=20);u||(u=[],d.push(u)),u.push(h)}let p=d.find(h=>h.length>=2);if(p){p.sort((m,f)=>m.bounds.centerX-f.bounds.centerX);let h=[];for(let m of p)h.some(f=>Math.abs(f.bounds.centerX-m.bounds.centerX)<40)||h.push(m);if(!(h.length===2&&h[0].bounds.x1<=n*.25&&h[1].bounds.x2>=n*.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=pt(c,s,n);return l?{paradigm:"drawer",roots:[l]}:{paradigm:"single_hub",roots:[]}}function qn(c,t=2){if(c.length<=t)return c;let e=[];for(let n of c){let r=e.find(i=>{let o=i[0],a=o.className===n.className,l=Math.abs(o.bounds.width-n.bounds.width)<=30,d=Math.abs(o.bounds.height-n.bounds.height)<=30,p=Math.abs(o.bounds.x1-n.bounds.x1)<=30;return a&&l&&d&&p});r||(r=[],e.push(r)),r.push(n)}let s=[];for(let n of e)s.push(...n.slice(0,t));return s}function We(c,t=I.screen.fallbackHeight,e=I.screen.fallbackWidth,s=!1){let n=t*.22,r=e*.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=a=>a.bounds.width>0&&a.bounds.height>0&&a.bounds.centerY<=n;for(let a of c){if(Ue(a)||!o(a))continue;let l=`${a.text||""} ${a.contentDesc||""} ${a.resourceId||""}`.trim();if(i.test(l)||l.length<=2&&/^[<‹«←x✕×\-]$/i.test(l))return a.clickable?a:c.find(p=>p.clickable&&p.bounds.x1<=a.bounds.x1&&p.bounds.x2>=a.bounds.x2&&p.bounds.y1<=a.bounds.y1&&p.bounds.y2>=a.bounds.y2&&p.bounds.width<=300&&p.bounds.height<=300)||a}for(let a of c){if(Ue(a)||!o(a)||a.bounds.x1>r)continue;if(a.className.includes("ImageButton")||a.className.includes("ImageView")||a.className.includes("ViewGroup")&&a.bounds.width<=250&&a.bounds.height<=250){let d=`${a.text||""} ${a.contentDesc||""} ${a.resourceId||""}`.toLowerCase();if(d.includes("back")||d.includes("up")||d.includes("arrow")||d.includes("chevron")||d.includes("nav")||d.includes("close")||d.includes("cancel"))return a.clickable?a:c.find(h=>h.clickable&&h.bounds.x1<=a.bounds.x1&&h.bounds.x2>=a.bounds.x2&&h.bounds.y1<=a.bounds.y1&&h.bounds.y2>=a.bounds.y2)||a}}if(s){let a=c.find(l=>{if(Ue(l)||!o(l)||!l.clickable)return!1;let d=l.bounds.x1<=r,p=l.bounds.width<=250&&l.bounds.height<=250;return d&&p&&l.bounds.width<e*.4});if(a)return a}return null}var tr,sr,nr,ir,Ke=de(()=>{"use strict";Be();st();tr=[/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];sr=[/(?:\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],nr=[/(?:\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],ir=[/(?:\b|[_\s-])(?:rate[_\s-]*us|review[_\s-]*(?:on|in)[_\s-]*(?:play|store)|play[_\s-]*store)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:visit[_\s-]*(?:our)?[_\s-]*website|privacy[_\s-]*policy|terms[_\s-]*of[_\s-]*service|terms[_\s-]*&[_\s-]*conditions)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:follow[_\s-]*us|share[_\s-]*via|share[_\s-]*app|open[_\s-]*in[_\s-]*browser)(?:\b|[_\s-])/i,/^https?:\/\//i,/(?:www\.[a-z0-9-]+\.[a-z]{2,})/i]});import Rs from"chalk";var Ls,ps,nt,Vt=de(()=>{"use strict";Ls={DEBUG:0,INFO:1,WARN:2,ERROR:3,SILENT:4},ps=class{currentLevel="INFO";constructor(t){let e=(process.env.LOG_LEVEL||"").toUpperCase();t?this.currentLevel=t:Ls[e]!==void 0?this.currentLevel=e:this.currentLevel="INFO"}setLevel(t){this.currentLevel=t}getLevel(){return this.currentLevel}isLevelEnabled(t){return Ls[this.currentLevel]<=Ls[t]}isJsonMode(){return(process.env.LOG_FORMAT||"").toLowerCase()==="json"}emitJson(t,...e){let s=e.map(r=>typeof r=="object"?JSON.stringify(r):String(r)).join(" "),n={ts:new Date().toISOString(),level:t,msg:s};t==="ERROR"||t==="WARN"?console.error(JSON.stringify(n)):console.log(JSON.stringify(n))}debug(...t){this.isLevelEnabled("DEBUG")&&(this.isJsonMode()?this.emitJson("DEBUG",...t):console.log(Rs.gray("[DEBUG]"),...t))}info(...t){this.isLevelEnabled("INFO")&&(this.isJsonMode()?this.emitJson("INFO",...t):console.log(...t))}warn(...t){this.isLevelEnabled("WARN")&&(this.isJsonMode()?this.emitJson("WARN",...t):console.warn(Rs.yellow("[WARN]"),...t))}error(...t){this.isLevelEnabled("ERROR")&&(this.isJsonMode()?this.emitJson("ERROR",...t):console.error(Rs.red("[ERROR]"),...t))}},nt=new ps});import ei from"fs";import us from"path";import be from"chalk";function Ms(c,t=new Set){let e=us.resolve(c);if(!ei.existsSync(e))throw new Error(`Included spec file not found: ${e}`);if(t.has(e)){let i=Array.from(t).concat(e);throw new es(`Circular spec inclusion detected: ${i.join(" -> ")}`,i)}t.add(e);let n=ei.readFileSync(e,"utf-8").split(`
26
- `),r=[];for(let i of n){let o=i.trim(),a=o.match(Z.INCLUDE_SPEC);if(a&&!o.startsWith("#")){let l=he([a[1],a[2],a[3]]),d=us.isAbsolute(l)?l:us.resolve(us.dirname(e),l),p=Ms(d,new Set(t));r.push(p)}else r.push(i)}return r.join(`
27
- `)}function rr(c){let t=[],e=[];for(let s of c){if(s._isControl==="END"){e.length>0&&e.pop();continue}if(s._isControl==="ELSE"){e.length>0&&e[e.length-1].step.type==="IF_VISIBLE"&&(e[e.length-1].inElse=!0);continue}let n=t;if(e.length>0){let r=e[e.length-1];r.step.type==="IF_VISIBLE"?r.inElse?(r.step.elseSteps=r.step.elseSteps||[],n=r.step.elseSteps):(r.step.thenSteps=r.step.thenSteps||[],n=r.step.thenSteps):r.step.type==="LOOP"&&(r.step.childSteps=r.step.childSteps||[],n=r.step.childSteps)}n.push(s),(s.type==="IF_VISIBLE"||s.type==="LOOP")&&e.push({step:s,inElse:!1})}return t}function Jt(c,t){let e=c.split(`
28
- `).map(n=>n.trim()).filter(n=>n.length>0&&!n.startsWith("#")),s=[];for(let n of e){let r=Dt(n,t),i=r.replace(Yn,"").trim(),o=Z.OPTIONAL_SUFFIX.test(i)||/^[(\[]\s*optional\s*[)\]]/i.test(i)||/^optional\s*:/i.test(i),a=i.replace(Z.OPTIONAL_SUFFIX,"").replace(/^[(\[]\s*optional\s*[)\]]\s*/i,"").replace(/^optional\s*:\s*/i,"").trim();if(Z.BACK.test(a)){s.push({originalText:r,type:"BACK",optional:o});continue}let l=a.match(Z.WAIT);if(l){let T=parseInt(l[1],10),A=a.toLowerCase().includes("ms")?T:T*1e3;s.push({originalText:r,type:"WAIT",durationMs:A,optional:o});continue}let d=a.match(Z.WAIT_UNTIL);if(d){let T=he([d[1],d[2],d[3]]),E=(d[4]||"visible").toLowerCase().includes("enable")?"enabled":"visible",L=d[5]?parseInt(d[5],10):void 0,U=(d[6]||"s").toLowerCase(),z=L?U.includes("ms")?L:L*1e3:void 0;s.push({originalText:r,type:"WAIT_UNTIL",target:T,expectedState:E,durationMs:z,optional:o});continue}let p=a.match(Z.IF_VISIBLE);if(p){let T=he([p[1],p[2],p[3],p[5],p[6],p[7]]);s.push({originalText:r,type:"IF_VISIBLE",conditionTarget:T,thenSteps:[],elseSteps:[],optional:o});continue}if(Z.ELSE.test(a)){s.push({originalText:r,type:"IF_VISIBLE",_isControl:"ELSE"});continue}if(Z.END.test(a)){s.push({originalText:r,type:"IF_VISIBLE",_isControl:"END"});continue}let h=a.match(Z.LOOP);if(h){let T=parseInt(h[1],10);s.push({originalText:r,type:"LOOP",loopCount:T,childSteps:[],optional:o});continue}let u=a.match(Z.FOR_EACH);if(u){let T=he([u[1],u[2],u[3]]);s.push({originalText:r,type:"LOOP",target:T,loopVariable:"item",childSteps:[],optional:o});continue}let m=a.match(Z.SET_VARIABLE);if(m){let T=m[1].trim(),A=m[2].trim();s.push({originalText:r,type:"SET_VARIABLE",variableName:T,variableExpression:A,optional:o});continue}let f=a.match(Z.DEVICE_WIFI);if(f){let T=f[1].toLowerCase();s.push({originalText:r,type:"DEVICE_STATE",deviceAction:"wifi",deviceStateValue:T==="on"||T==="enable",optional:o});continue}let x=a.match(Z.DEVICE_AIRPLANE);if(x){let T=x[1].toLowerCase();s.push({originalText:r,type:"DEVICE_STATE",deviceAction:"airplane",deviceStateValue:T==="on"||T==="enable",optional:o});continue}let v=a.match(Z.DEVICE_ORIENTATION);if(v){let T=v[1].toLowerCase();s.push({originalText:r,type:"DEVICE_STATE",deviceAction:"orientation",deviceStateValue:T,optional:o});continue}let S=a.match(Z.DEVICE_NOTIFICATIONS);if(S){let T=S[1].toLowerCase(),A=T.startsWith("open")||T.startsWith("expand")||T.startsWith("pull");s.push({originalText:r,type:"DEVICE_STATE",deviceAction:"notifications",deviceStateValue:A?"open":"collapse",optional:o});continue}let y=a.match(Z.RUN_JS);if(y){let T=y[1].trim();s.push({originalText:r,type:"RUN_JS",jsCode:T,optional:o});continue}let k=a.match(Z.ENTER_OTP);if(k){let T=he([k[1],k[2],k[3]])||void 0;s.push({originalText:r,type:"ENTER_OTP",target:T,optional:o});continue}let C=a.match(Z.API_REQUEST);if(C){let T=C[1].toUpperCase(),A=he([C[2],C[3],C[4]]),E=he([C[5],C[6],C[7]])||void 0,L=he([C[8],C[9],C[10]])||void 0,U=C[11]||void 0,z=C[12]?parseInt(C[12],10):void 0;s.push({originalText:r,type:"API_REQUEST",apiMethod:T,apiUrl:A,apiBody:E,apiStorePath:L,apiStoreVariable:U,apiExpectedStatus:z,optional:o});continue}let j=a.match(Z.EMAIL_ASSERT);if(j){let T=he([j[1],j[2],j[3]]),A=he([j[4],j[5],j[6]]),E=he([j[7],j[8],j[9]])||void 0;s.push({originalText:r,type:"EMAIL_ASSERT",emailRecipient:T,emailSubject:A,emailBodyContains:E,optional:o});continue}let $=a.match(Z.LAUNCH_APP);if($){let T=he([$[1],$[2],$[3]]);s.push({originalText:r,type:"LAUNCH_APP",target:T,optional:o});continue}let J=a.match(Z.RESTART_APP);if(J){let T=he([J[1],J[2],J[3]]);s.push({originalText:r,type:"RESTART_APP",target:T,optional:o});continue}let O=a.match(Z.TERMINATE_APP);if(O){let T=he([O[1],O[2],O[3]])||void 0;s.push({originalText:r,type:"TERMINATE_APP",target:T,optional:o});continue}let V=a.match(Z.CLEAR_DATA);if(V){let T=he([V[1],V[2],V[3]]);s.push({originalText:r,type:"CLEAR_DATA",target:T,optional:o});continue}let W=a.match(Z.GRANT_PERMISSION);if(W){let T=he([W[1],W[2],W[3]]),A=he([W[4],W[5],W[6]])||void 0;s.push({originalText:r,type:"GRANT_PERMISSION",value:T,target:A,optional:o});continue}let se=a.match(Z.DEEP_LINK);if(se){let T=he([se[1],se[2],se[3]]);s.push({originalText:r,type:"DEEP_LINK",target:T,optional:o});continue}let oe=a.match(Z.DIRECTIONAL_SWIPE);if(oe){let T=oe[1].toLowerCase(),A=he([oe[2],oe[3],oe[4]])||void 0;s.push({originalText:r,type:"SWIPE",direction:T,target:A,optional:o});continue}let G=a.match(Z.TOGGLE);if(G){let T=(G[1]||"").toLowerCase(),A=he([G[2],G[3],G[4]]),E=a.toLowerCase().startsWith("uncheck")||T==="off"?!1:T==="on"||a.toLowerCase().startsWith("check")?!0:void 0;s.push({originalText:r,type:"TOGGLE",target:A,desiredState:E,optional:o});continue}let H=a.match(Z.SELECT_DROPDOWN);if(H){let T=he([H[1],H[2],H[3]]),A=he([H[4],H[5],H[6]]);s.push({originalText:r,type:"SELECT",value:T,target:A,optional:o});continue}let w=a.match(Z.SCROLL_TO);if(w){let T=(w[1]||"down").toLowerCase(),A=he([w[2],w[3],w[4]]);s.push({originalText:r,type:"SCROLL",target:A,direction:T,optional:o});continue}let b=a.match(Z.SCROLL);if(b){let T=b[1].toLowerCase();s.push({originalText:r,type:"SCROLL",target:T,direction:T,optional:o});continue}let _=a.match(Z.LONG_PRESS);if(_){let T=he([_[1],_[2],_[3]]);s.push({originalText:r,type:"LONG_PRESS",target:T,optional:o});continue}let P=a.match(Z.ORDINAL_TAP);if(P){let T=P[1].toLowerCase(),A=he([P[2],P[3],P[4]]);s.push({originalText:r,type:"TAP",target:A,ordinal:T,optional:o});continue}let ce=a.match(Z.PROXIMITY_TAP);if(ce){let T=he([ce[1],ce[2],ce[3]]),A=he([ce[4],ce[5],ce[6]]);s.push({originalText:r,type:"TAP",target:T,relativeTo:A,optional:o});continue}let ne=a.match(Z.TYPE);if(ne){let T=he([ne[1],ne[2],ne[3]]),A=he([ne[4],ne[5],ne[6]]);s.push({originalText:r,type:"TYPE",value:T,target:A,optional:o});continue}let g=a.match(Z.CLEAR);if(g){let T=he([g[1],g[2],g[3]]);s.push({originalText:r,type:"CLEAR",target:T,optional:o});continue}let D=a.match(Z.VERIFY);if(D){let T=he([D[1],D[2],D[3]]),A=(D[4]||"visible").toLowerCase(),E=A.includes("not visible")||A.includes("not present")||A.includes("absent")||A.includes("hidden")||A.includes("gone"),L=E?"absent":A==="exists"||A==="enabled"||A==="disabled"?A:"visible";s.push({originalText:r,type:"VERIFY",target:T,expectedState:L,negative:E,optional:o});continue}let N=a.match(Z.TAP);if(N&&!a.toLowerCase().includes("back")){let T=he([N[1],N[2],N[3]]);s.push({originalText:r,type:"TAP",target:T,optional:o});continue}let M=a.match(Z.FALLBACK_QUOTED_TAP);if(M&&(a.toLowerCase().startsWith("tap")||a.toLowerCase().startsWith("click"))){s.push({originalText:r,type:"TAP",target:M[1],optional:o});continue}nt.warn(`Unrecognized prompt step: "${n}". Skipping.`)}return rr(s)}function si(c,t,e){let s=t.map(i=>(i.text||i.contentDesc||"").trim()).filter(i=>i.length>1),n=At(c,s,3),r=n.length>0?`
20
+ `)}var Ne,Ws=ie(()=>{"use strict";Nt();Ne=null});import{spawn as Kt}from"child_process";import Ys from"readline";function Ji(a,t,e,s){let n={},i=a.match(/level:\s*(\d+)/i),r=a.match(/temperature:\s*(\d+)/i),o=a.match(/status:\s*(\d+)/i);(i||r||o)&&(n.battery={...i?{level:Number(i[1])}:{},...r?{temperatureC:Number(r[1])/10}:{},...o?{status:o[1]}:{}});let l=t.match(/(?:Physical|Override)\s+size:\s*(\d+)x(\d+)/i),c=t.match(/(?:Physical|Override)\s+density:\s*(\d+)/i);(l||c)&&(n.display={...l?{width:Number(l[1]),height:Number(l[2])}:{},...c?{density:Number(c[1])}:{}});let d=e.match(/TOTAL\s+([\d,]+)/i),p=e.match(/Private Dirty:\s*(\d+)/i),u=e.match(/Native Heap:\s*(\d+)/i),m=[...(e.match(/Total PSS by category:\s*([\s\S]*?)(?:\n\s*\n|Total RAM:|$)/i)?.[1]||"").matchAll(/^\s*([\d,]+)K:\s*(.+)$/gim)],b=m.reduce((P,N)=>P+Number(N[1].replace(/,/g,"")),0),T=m.find(P=>/native/i.test(P[2]));(d||p||u||b)&&(n.memory={...d?{pssKb:Number(d[1].replace(/,/g,""))}:b?{pssKb:b}:{},...p?{privateDirtyKb:Number(p[1])}:{},...u?{nativeHeapKb:Number(u[1])}:T?{nativeHeapKb:Number(T[1].replace(/,/g,""))}:{}});let w=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(w||S||y)&&(n.graphics={...w?{framesRendered:Number(w[1])}:{},...S?{jankyFrames:Number(S[1])}:{},...y?{percentile90Ms:Number(y[1])}:{}}),n}function Vs(a){let t=(a||"").toLowerCase();return t.startsWith("emulator-")||t.includes("127.0.0.1")||t.includes("localhost")?"emulator":t.includes(":")||t.includes("._tcp")||t.includes("_adb-tls-")?"wifi":"usb"}function Sr(a){let t=new Map;for(let s of a){let n=s.connectionType||Vs(s.serial),i="";if(s.model&&s.product)i=`${s.model}__${s.product}`.toLowerCase();else if(s.model)i=s.model.toLowerCase();else{let r=s.serial.match(/^adb-([a-zA-Z0-9]+)-/);i=r?r[1].toLowerCase():s.serial.toLowerCase()}t.has(i)||t.set(i,[]),t.get(i).push({...s,connectionType:n})}let e=[];for(let s of t.values()){if(s.length===1){e.push(s[0]);continue}let n=[...s].sort((i,r)=>{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(i)-o(r)});e.push(n[0])}return e}var lt,oe,Re=ie(()=>{"use strict";mr();je();Nt();Ws();lt=class{defaultSerial=null;cachedDimensions=new Map;inFlightHierarchies=new Map;constructor(t){t&&(this.defaultSerial=t)}setDefaultDevice(t){this.defaultSerial=t}getDefaultDevice(){return this.defaultSerial}async getScreenDimensions(t){let e=t||this.defaultSerial||"default";if(this.cachedDimensions.has(e))return this.cachedDimensions.get(e);try{let{stdout:n}=await this.execAdb(["shell","wm","size"],t),i=n.match(/(?:Physical|Override)\s*size:\s*(\d+)x(\d+)/i);if(i){let r={width:parseInt(i[1],10),height:parseInt(i[2],10)};return this.cachedDimensions.set(e,r),r}}catch{}let s={width:R.screen.fallbackWidth,height:R.screen.fallbackHeight};return this.cachedDimensions.set(e,s),s}clearScreenDimensionsCache(t){t?this.cachedDimensions.delete(t):this.cachedDimensions.clear()}async getNavigationBarHeight(t){try{let{stdout:e}=await this.execAdb(["shell","dumpsys","window","displays"],t).catch(()=>({stdout:""})),s=e.match(/(?:navigationBarHeight|navBarHeight|navigationBarFrame)[=:\s]+(\d+)/i);if(s&&parseInt(s[1],10)>0)return parseInt(s[1],10)}catch{}return 0}isGlobalAdbCommand(t){return t?["devices","connect","disconnect","version","kill-server","start-server","help"].includes(t.toLowerCase()):!0}async execAdbInternal(t,e,s=2e4){let n=e||this.defaultSerial;!n&&!this.isGlobalAdbCommand(t[0])&&(await this.ensureDefaultDevice(),n=this.defaultSerial);let i=n&&!this.isGlobalAdbCommand(t[0])?["-s",n,...t]:[...t],r=await yr();return new Promise((o,l)=>{let c=Kt(r,i),d="",p="",u=!1,h=setTimeout(()=>{u=!0;try{c.kill("SIGKILL")}catch{}l(new Error(`ADB command timed out after ${s}ms: adb ${i.join(" ")}`))},s);c.stdout.on("data",m=>{d+=m.toString("utf-8")}),c.stderr.on("data",m=>{p+=m.toString("utf-8")}),c.on("close",m=>{clearTimeout(h),!u&&(m===0?o({stdout:d,stderr:p}):l(new Error(`ADB command failed (code ${m}): adb ${i.join(" ")}
21
+ Error: ${p}`)))}),c.on("error",m=>{clearTimeout(h),!u&&l(m)})})}async execAdb(t,e,s=R.adb.commandTimeoutMs,n=R.device.maxDeviceRetries){let i;for(let r=0;r<=n;r++)try{return await this.execAdbInternal(t,e,s)}catch(o){i=o;let l=o instanceof Error?o.message:String(o),c=l.toLowerCase(),d=c.includes("device offline")||c.includes("device not found"),p=d||c.includes("error: closed")||c.includes("timed out");if(r>=n||!p||this.isGlobalAdbCommand(t[0]))throw d?new It(`Target Android device ${e||this.defaultSerial||""} is offline or disconnected: ${l}`):o;if(R.device.autoReconnect&&d)try{await this.execAdbInternal(["reconnect"],e,5e3).catch(()=>{})}catch{}await new Promise(u=>setTimeout(u,R.adb.retryBaseDelayMs*Math.pow(2,r+1)))}throw i||new Error(`ADB command failed after ${n} retries: ${t.join(" ")}`)}async execAdbBinary(t,e,s=45e3){let n=e||this.defaultSerial;!n&&!this.isGlobalAdbCommand(t[0])&&(await this.ensureDefaultDevice(),n=this.defaultSerial);let i=n&&!this.isGlobalAdbCommand(t[0])?["-s",n,...t]:[...t];return new Promise((r,o)=>{let l=Kt(it(),i),c=[],d="",p=!1,u=setTimeout(()=>{p=!0;try{l.kill("SIGKILL")}catch{}o(new Error(`ADB binary command timed out after ${s}ms: adb ${i.join(" ")}`))},s);l.stdout.on("data",h=>c.push(Buffer.from(h))),l.stderr.on("data",h=>{d+=h.toString("utf-8")}),l.on("close",h=>{clearTimeout(u),!p&&(h===0?r(Buffer.concat(c)):o(new Error(`ADB binary command failed (code ${h}): adb ${i.join(" ")}
22
+ ${d}`)))}),l.on("error",h=>{clearTimeout(u),!p&&o(h)})})}async getRawConnectedDevices(){let{stdout:t}=await this.execAdb(Ae.DEVICES),e=t.split(`
23
+ `).map(r=>r.trim()).filter(Boolean),s=e.findIndex(r=>r.toLowerCase().includes("list of devices attached")),n=s!==-1?e.slice(s+1):e,i=[];for(let r of n){if(!r||r.startsWith("*")||r.toLowerCase().includes("list of devices"))continue;let o=r.split(/\s+/);if(o.length>=2){let l=o[0],c=o[1],d,p;for(let u of o.slice(2))u.startsWith("model:")&&(d=u.replace("model:","")),u.startsWith("product:")&&(p=u.replace("product:",""));i.push({serial:l,state:c,model:d,product:p,connectionType:Vs(l)})}}return i}async getConnectedDevices(t){let e=await this.getRawConnectedDevices();return t?.deduplicate===!1?e:Sr(e)}deduplicateDevices(t){return Sr(t)}async wakeDevice(t){try{await this.execAdb(["shell","input","keyevent","224"],t),await this.execAdb(["shell","wm","dismiss-keyguard"],t)}catch{}}async ensureDefaultDevice(t){if(t)return await this.wakeDevice(t),t;if(this.defaultSerial)return await this.wakeDevice(this.defaultSerial),this.defaultSerial;let s=(await this.getConnectedDevices()).find(n=>n.state==="device");return s&&(this.defaultSerial=s.serial,await this.wakeDevice(this.defaultSerial)),this.defaultSerial||void 0}async getDeviceModel(t){try{let{stdout:i}=await this.execAdb(["shell","getprop","ro.product.model"],t),r=i.trim();if(r)return r}catch{}let e=await this.getConnectedDevices(),s=t||this.defaultSerial;return e.find(i=>i.serial===s)?.model||s||"Android Device"}async captureScreenshot(t){return this.execAdbBinary(Ae.SCREENCAP,t)}async getUiHierarchy(t){let e=t||this.defaultSerial||"default",s=this.inFlightHierarchies.get(e);if(s)return s;let n=(async()=>{try{let{stdout:i}=await this.execAdb(Ae.UIAUTOMATOR_DUMP,t,1e4,0),r=i.indexOf("<hierarchy");return r>=0?i.slice(r):i}finally{this.inFlightHierarchies.delete(e)}})();return this.inFlightHierarchies.set(e,n),n}clearInFlightHierarchies(t){t?this.inFlightHierarchies.delete(t):this.inFlightHierarchies.clear()}async getForegroundPackage(t){let e=s=>{let n=s.toLowerCase();return n==="android"||n.includes("systemui")||n.includes("inputmethod")||n.includes("latin")||n.includes("gboard")||n.includes("swiftkey")||n.includes("ime")};try{let{stdout:s}=await this.execAdb(["shell","dumpsys","activity","activities"],t).catch(()=>({stdout:""})),n=s.match(/(?:topResumedActivity|mResumedActivity|ResumedActivity)[=:\s]+(?:ActivityRecord\{[^}]*?\s+)?([a-zA-Z0-9._]+)\//i);if(n&&n[1]&&!e(n[1]))return n[1];let{stdout:i}=await this.execAdb(Ae.FOREGROUND_APP,t).catch(()=>({stdout:""})),r=i.matchAll(/(?:mCurrentFocus|mFocusedWindow|mFocusedApp|topResumedActivity|mResumedActivity|mFocusedActivity)[=:\s]+(?:Window\{[^}]*?\s+|ActivityRecord\{[^}]*?\s+|AppWindowToken\{[^}]*?\s+)?([a-zA-Z0-9._]+)\//gi);for(let o of r)if(o&&o[1]&&!e(o[1]))return o[1]}catch{}return null}async tap(t,e,s){await this.execAdb(Ae.INPUT_TAP(t,e),s)}async inputText(t,e){let s=t.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(Ae.INPUT_TEXT(s),e)}catch{for(let n of t)if(n===" ")await this.pressKey(62,e);else if(n==="@")await this.pressKey(77,e);else{let i=n.replace(/([\\"'&<>()|$;#*?~`%])/g,"\\$1");await this.execAdb(["shell","input","text",i],e).catch(()=>{})}}}async pressKey(t,e){await this.execAdb(Ae.INPUT_KEY(t),e)}async pressEnter(t){await this.pressKey(66,t)}async pressTab(t){await this.pressKey(61,t)}async pressHome(t){await this.pressKey(3,t)}async pressBack(t){await this.pressKey(4,t)}async setWifiEnabled(t,e){await this.execAdb(["shell","svc","wifi",t?"enable":"disable"],e)}async setAirplaneMode(t,e){await this.execAdb(["shell","cmd","connectivity","airplane-mode",t?"enable":"disable"],e)}async setOrientation(t,e){await this.execAdb(["shell","content","insert","--uri","content://settings/system","--bind","name:s:accelerometer_rotation","--bind","value:i:0"],e).catch(()=>{});let s=t==="landscape"?"1":"0";await this.execAdb(["shell","settings","put","system","user_rotation",s],e)}async openNotificationShade(t){await this.execAdb(["shell","cmd","statusbar","expand-notifications"],t)}async collapseNotificationShade(t){await this.execAdb(["shell","cmd","statusbar","collapse"],t)}async clearInput(t=45,e){let s=Array(t).fill("67");await this.execAdb(["shell","input","keyevent",...s],e)}async clearField(t,e=80){await this.pressKey(123,t);let s=Math.max(50,e+10),n=Array(s).fill("67");await this.execAdb(["shell","input","keyevent",...n],t).catch(()=>{}),await this.pressKey(122,t);let i=Array(Math.max(30,Math.floor(e/2))).fill("112");await this.execAdb(["shell","input","keyevent",...i],t).catch(()=>{})}async swipe(t,e,s,n,i=300,r){await this.execAdb(Ae.INPUT_SWIPE(t,e,s,n,i),r)}async launchApp(t,e){try{let{stdout:s}=await this.execAdb(["shell","cmd","package","resolve-activity","--brief",t],e),i=(s||"").split(`
24
+ `).map(r=>r.trim()).find(r=>r.includes("/")&&!r.includes(" "));if(i){await this.execAdb(["shell","am","start","-n",i],e);return}}catch{}await this.execAdb(Ae.LAUNCH_APP(t),e)}async forceStop(t,e){await this.execAdb(Ae.FORCE_STOP(t),e)}async clearAppData(t,e){await this.execAdb(Ae.CLEAR_APP(t),e)}async restartApp(t,e){await this.forceStop(t,e),await new Promise(s=>setTimeout(s,600)),await this.launchApp(t,e)}async grantPermission(t,e,s){await this.execAdb(Ae.GRANT_PERMISSION(t,e),s)}async isAppInstalled(t,e){try{let{stdout:s}=await this.execAdb(["shell","pm","list","packages",t],e);return s.toLowerCase().includes(`package:${t.toLowerCase()}`)||s.includes(t)}catch{return!1}}async installApk(t,e){let{stdout:s,stderr:n}=await this.execAdb(Ae.INSTALL_APK(t),e,12e4),i=`${s} ${n}`;if(i.includes("Failure"))throw new Error(`APK installation failed on device ${e||"default"}: ${i.trim()}`)}async uninstallApp(t,e){await this.execAdb(Ae.UNINSTALL_APP(t),e)}async openDeepLink(t,e){await this.execAdb(Ae.DEEP_LINK(t),e)}async checkCrashes(t,e){try{let{stdout:s}=await this.execAdb(Ae.LOGCAT_CRASH(t),e);if(s&&s.includes("FATAL EXCEPTION")&&s.includes(t))return s}catch{}return null}async clearCrashLogs(t){try{await this.execAdb(Ae.CLEAR_CRASH_LOGS(),t)}catch{}}async getDeviceIp(t){try{let{stdout:e}=await this.execAdb(Ae.GET_WLAN_IP,t),s=e.match(/inet\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}try{let{stdout:e}=await this.execAdb(["shell","ip","route"],t),s=e.match(/src\s+(\d+\.\d+\.\d+\.\d+)/);if(s&&s[1])return s[1]}catch{}return null}async connectWifi(t,e=5555,s){let n=t,i=e;if(t&&t.includes(":")){let p=t.split(":");n=p[0],i=parseInt(p[1],10)||e}if(!n){let p=await this.getDeviceIp(s);if(!p)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>");n=p}let r=`${n}:${i}`;if(t)try{let{stdout:p,stderr:u}=await this.execAdb(Ae.CONNECT(r)),h=`${p} ${u}`.trim();if(h.toLowerCase().includes("connected")||h.toLowerCase().includes("already"))return this.defaultSerial=r,{success:!0,target:r,message:h}}catch{}try{await this.execAdb(Ae.TCPIP(i),s),await new Promise(p=>setTimeout(p,1e3))}catch{}let{stdout:o,stderr:l}=await this.execAdb(Ae.CONNECT(r)),c=`${o} ${l}`.trim(),d=c.toLowerCase().includes("connected")||c.toLowerCase().includes("already");return d&&(this.defaultSerial=r),{success:d,target:r,message:c}}async disconnectWifi(t){let{stdout:e,stderr:s}=await this.execAdb(Ae.DISCONNECT(t));return`${e} ${s}`.trim()}async pairWifi(t,e){if(!/^[^:\s]+:\d+$/.test(t))throw new Error("Pairing target must be an IP:port value.");if(!/^\d{4,8}$/.test(e))throw new Error("Pairing code must contain 4 to 8 digits.");let{stdout:s,stderr:n}=await this.execAdb(["pair",t,e]),i=`${s} ${n}`.trim();return{success:/successfully paired|already paired/i.test(i),message:i}}async getDeviceMetrics(t){let[s,n,i,r]=await Promise.all([this.execAdb(["shell","dumpsys","battery"],t,8e3,0).then(o=>o.stdout).catch(()=>""),this.execAdb(["shell","wm","size"],t,8e3,0).then(async o=>`${o.stdout}
25
+ ${(await this.execAdb(["shell","wm","density"],t,8e3,0).catch(()=>({stdout:""}))).stdout}`).catch(()=>""),this.execAdb(["shell","dumpsys","meminfo"],t,8e3,0).then(o=>o.stdout).catch(()=>""),this.execAdb(["shell","dumpsys","gfxinfo"],t,8e3,0).then(o=>o.stdout).catch(()=>"")]);return Ji(s,n,i,r)}streamLogcatEvents(t,e){let s=["-s",t,"shell","logcat","-b","events","-v","time"],n=Kt(it(),s),i=Ys.createInterface({input:n.stdout});return i.on("line",e),n.on("close",()=>{try{i.close()}catch{}}),n.on("error",()=>{}),n}streamLogcatCrashes(t,e){let s=["-s",t,"shell","logcat","-T","1","-s","AndroidRuntime:E","ANRManager:E","-v","time"],n=Kt(it(),s),i=Ys.createInterface({input:n.stdout});return i.on("line",e),n.on("close",()=>{try{i.close()}catch{}}),n.on("error",()=>{}),n}streamLogcatToasts(t,e){let s=["-s",t,"shell","logcat","-s","Toast:D","Snackbar:D","-v","time"],n=Kt(it(),s),i=Ys.createInterface({input:n.stdout});return i.on("line",e),n.on("close",()=>{try{i.close()}catch{}}),n.on("error",()=>{}),n}async getLogcatTail(t=150,e){try{let{stdout:s}=await this.execAdb(["logcat","-d","-t",String(t),"-v","time"],e);return s}catch(s){return`Failed to capture logcat: ${s instanceof Error?s.message:String(s)}`}}async startScreenRecording(t="/sdcard/prompttest_temp.mp4",e){let s=e||this.defaultSerial,n=s?["-s",s,"shell","screenrecord",t]:["shell","screenrecord",t],i=Kt(it(),n);return i.on("error",()=>{}),{remotePath:t,process:i}}async stopScreenRecording(t,e,s){try{try{t.process.kill("SIGINT")}catch{}return await this.execAdb(["shell","pkill","-2","screenrecord"],s).catch(()=>{}),await new Promise(n=>setTimeout(n,1500)),await this.execAdb(["pull",t.remotePath,e],s),await this.execAdb(["shell","rm","-f",t.remotePath],s).catch(()=>{}),e}catch{return""}}async isKeyboardVisible(t){try{let{stdout:e}=await this.execAdb(["shell","sh","-c",'dumpsys input_method | grep -E "mInputShown=true|mShowRequested=true"'],t,4e3,0),s=e.toLowerCase();if(s.includes("minputshown=true")||s.includes("mshowrequested=true"))return!0}catch{}try{let{stdout:e}=await this.execAdb(["shell","sh","-c",'dumpsys window | grep -E "mInputMethodWindowVisibleHeight"'],t,4e3,0),s=e.match(/mInputMethodWindowVisibleHeight=(\d+)/i);if(s&&parseInt(s[1],10)>0)return!0}catch{}try{let{stdout:e}=await this.execAdb(["shell","dumpsys","input_method"],t,6e3,0),s=e.toLowerCase();return s.includes("minputshown=true")||s.includes("mshowrequested=true")}catch{return!1}}async ensureAccessibilityServices(t){try{await this.execAdb(["shell","settings","put","secure","accessibility_enabled","1"],t,4e3,0)}catch{}}},oe=new lt});function xr(a){let t=a.trim(),e=t.match(/^"([^"]+)"$/);if(e)return e[1].trim();let s=t.match(/^'([^']+)'$/);return s?s[1].trim():t.replace(/^['"]+|['"]+$/g,"").trim()}function ue(a){for(let t of a)if(t!=null&&t.trim().length>0)return t.trim();return""}function Lt(a,t){let e=a;if(t)for(let[s,n]of Object.entries(t))e=e.replace(new RegExp(`\\$${s}\\b`,"g"),()=>n);return e=e.replace(/\$ENV\{([A-Z0-9_]+)\}/gi,(s,n)=>process.env[n]||""),e=e.replace(/\$RANDOM_EMAIL\b/g,()=>`test_${Date.now().toString().slice(-6)}@prompttest.io`),e=e.replace(/\$RANDOM_NAME\b/g,()=>`User_${Math.floor(1e3+Math.random()*9e3)}`),e=e.replace(/\$RANDOM_PHONE\b/g,()=>`98${Math.floor(1e7+Math.random()*9e7)}`),e=e.replace(/\$TIMESTAMP\b/g,()=>Date.now().toString()),e=e.replace(/\$UUID\b/g,()=>`${Date.now().toString(16)}-${Math.random().toString(16).substring(2,8)}`),e=e.replace(/\$RANDOM_UUID\b/g,()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,s=>{let n=Math.random()*16|0;return(s==="x"?n:n&3|8).toString(16)})),e=e.replace(/\$RANDOM_CARD\b/g,()=>{let s="4532";for(let n=0;n<12;n++)s+=Math.floor(Math.random()*10);return s}),e=e.replace(/\$DATE_OFFSET\((-?\d+)\)/g,(s,n)=>{let i=parseInt(n,10)||0;return new Date(Date.now()+i*864e5).toISOString().slice(0,10)}),e=e.replace(/\$RANDOM_INT\((\d+),\s*(\d+)\)/g,(s,n,i)=>{let r=parseInt(n,10),o=parseInt(i,10);return String(Math.floor(r+Math.random()*(o-r+1)))}),e}function zt(a){return(a||"").replace(/&#(\d+);/g,(t,e)=>{try{return String.fromCodePoint(parseInt(e,10))}catch{return""}}).replace(/&#x([0-9a-fA-F]+);/g,(t,e)=>{try{return String.fromCodePoint(parseInt(e,16))}catch{return""}}).replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&apos;/g,"'")}function pe(a){return zt(a||"").trim().toLowerCase().replace(/[\u2018\u2019]/g,"'").replace(/[\u201C\u201D]/g,'"').replace(/\uFFFD/g,"").replace(new RegExp("\\p{Extended_Pictographic}","gu"),"").replace(/[\uE000-\uF8FF\uFFF0-\uFFFF]/g,"").replace(/\s+/g," ").trim()}function me(a,t){if(!a||!t)return!1;let e=pe(a),s=pe(t);if(e.length<=3){let n=wr.get(e);if(!n){let i=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");n=new RegExp(`(?:^|\\b)${i}(?:\\b|$)`,"i"),wr.set(e,n)}return n.test(s)}return s.includes(e)}function Ki(a,t){let e=pe(a),s=pe(t),n=e.length,i=s.length;if(n===0)return i;if(i===0)return n;let r=Array.from({length:i+1},(l,c)=>c),o=new Array(i+1);for(let l=1;l<=n;l++){o[0]=l;for(let d=1;d<=i;d++){let p=e[l-1]===s[d-1]?0:1;o[d]=Math.min(r[d]+1,o[d-1]+1,r[d-1]+p)}let c=r;r=o,o=c}return r[i]}function zi(a,t){let e=pe(a),s=pe(t);if(e===s)return 1;if(!e||!s)return 0;let n=Math.max(e.length,s.length);return 1-Ki(e,s)/n}function Gt(a,t,e=3,s=.35){return Array.from(new Set(t.map(r=>r.trim()).filter(r=>r.length>0))).map(r=>({text:r,score:zi(a,r)})).filter(r=>r.score>=s).sort((r,o)=>o.score-r.score).slice(0,e)}var vr,de,ae,as,wr,Tr,Pr,ot=ie(()=>{"use strict";vr=/^(\d+(?:\.\d+)*\.?|\-|\*)\s*/;de=`(?:"([^"]+)"|'([^']+)'|([^'"\\s][^'"]*?[^'"\\s]|\\S))`;ae={TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:button\\s+|item\\s+)?${de}$`,"i"),ORDINAL_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+(?:the\\s+)?(1st|2nd|3rd|4th|5th|last|first)\\s+(?:button\\s+|item\\s+)?${de}$`,"i"),PROXIMITY_TAP:new RegExp(`^(?:tap|click|press)(?:\\s+on)?\\s+${de}\\s+(?:next\\s+to|near|beside|inside|for)\\s+${de}$`,"i"),CLEAR:new RegExp(`^(?:clear|empty)(?:\\s+(?:text\\s+(?:in|of)|field|input))?\\s+${de}$`,"i"),LONG_PRESS:new RegExp(`^(?:long\\s+press|press\\s+and\\s+hold)\\s+(?:button\\s+|item\\s+)?${de}$`,"i"),TYPE:new RegExp(`^(?:type|enter|input)\\s+${de}\\s+(?:in|into)\\s+${de}$`,"i"),VERIFY:new RegExp(`^(?:verify|veryfy|check|assert|ensure)\\s+(?:that\\s+)?(?:error\\s+message\\s+|message\\s+|text\\s+|element\\s+|button\\s+|toast\\s+|my\\s+)?${de}\\s*(?:is\\s+(visible|exists|appears|enabled|disabled|not\\s+visible|hidden|absent|not\\s+present|gone))?$`,"i"),BACK:/^(?:press\s+back|go\s+back|navigate\s+back|back)$/i,SCROLL_TO:new RegExp(`^(?:scroll|swipe)(?:\\s+(down|up))?\\s+(?:to|until)\\s+${de}$`,"i"),SCROLL:/^(?:scroll|swipe)\s+(down|up)$/i,DIRECTIONAL_SWIPE:new RegExp(`^(?:swipe|scroll)\\s+(left|right|up|down)(?:\\s+on\\s+${de})?$`,"i"),TOGGLE:new RegExp(`^(?:toggle|turn\\s+(on|off)|check|uncheck)\\s+(?:checkbox\\s+|switch\\s+)?${de}$`,"i"),SELECT_DROPDOWN:new RegExp(`^(?:select|choose)\\s+${de}\\s+from\\s+${de}$`,"i"),LAUNCH_APP:new RegExp(`^(?:launch|open)\\s+(?:app\\s+)?${de}$`,"i"),RESTART_APP:new RegExp(`^(?:restart|relaunch)\\s+(?:app\\s+)?${de}$`,"i"),TERMINATE_APP:new RegExp(`^(?:terminate|close|force\\s*stop)\\s+(?:app\\s+)?(?:${de})?$`,"i"),CLEAR_DATA:new RegExp(`^(?:clear\\s+(?:app\\s+)?data\\s+(?:for\\s+)?|reset\\s+app\\s+)${de}$`,"i"),GRANT_PERMISSION:new RegExp(`^grant\\s+permissions?\\s+${de}(?:\\s+(?:to|for)\\s+${de})?$`,"i"),DEEP_LINK:new RegExp(`^(?:open\\s+deep\\s*link|navigate\\s+to\\s+url)\\s+${de}$`,"i"),WAIT:/^wait\s+(\d+)\s*(?:s|sec|seconds|ms)?$/i,WAIT_UNTIL:new RegExp(`^(?:wait\\s+until|wait\\s+for)\\s+${de}\\s+(?:(?:is\\s+)?(visible|appears?|enabled|exists))?(?:\\s+(?:up\\s+to|for)\\s+(\\d+)\\s*(s|sec|seconds|ms)?)?$`,"i"),IF_VISIBLE:new RegExp(`^if\\s+(?:(?:that\\s+)?${de}\\s+(?:is\\s+)?(visible|present|exists|appears?)|element\\s+${de}\\s+(?:is\\s+)?visible)$`,"i"),ELSE:/^else$/i,END:/^(?:end|end\s*if|end\s*loop|fi|done)$/i,LOOP:/^(?:loop|repeat)\s+(\d+)\s*(?:times)?$/i,FOR_EACH:new RegExp(`^for\\s+each\\s+(?:item\\s+)?(?:in\\s+)?${de}$`,"i"),SET_VARIABLE:/^set\s+\$([a-zA-Z0-9_]+)\s*=\s*(.+)$/i,DEVICE_WIFI:/^(?:turn\s+|set\s+)?wifi\s+(on|off|enable|disable)$/i,DEVICE_AIRPLANE:/^(?:turn\s+|set\\s+)?airplane\s*(?:mode)?\s+(on|off|enable|disable)$/i,DEVICE_ORIENTATION:/^(?:set\s+)?orientation\s+(?:to\s+)?(portrait|landscape)$/i,DEVICE_NOTIFICATIONS:/^(open|expand|pull\s+down|collapse|close)\s+notification(?:s|\s+shade)?$/i,INCLUDE_SPEC:new RegExp(`^(?:include|run_spec|subflow)\\s+${de}$`,"i"),RUN_JS:/^(?:run\s+js|eval\s+js|js)(?::|\s+)(.+)$/i,ENTER_OTP:new RegExp(`^(?:enter|input|type)\\s+(?:the\\s+)?(?:verification\\s+code|otp|2fa\\s+code|sms\\s+code)(?:\\s+just\\s+sent)?(?:\\s+(?:in|into)\\s+${de})?$`,"i"),API_REQUEST:new RegExp(`^(?:api|call\\s+api)\\s+(GET|POST|PUT|DELETE|PATCH)\\s+${de}(?:\\s+with\\s+body\\s+${de})?(?:\\s+store\\s+${de}\\s+as\\s+\\$([a-zA-Z0-9_]+))?(?:\\s+assert\\s+status\\s+(\\d+))?$`,"i"),EMAIL_ASSERT:new RegExp(`^(?:assert|verify)\\s+email\\s+to\\s+${de}\\s+with\\s+subject\\s+${de}(?:\\s+contains\\s+${de})?$`,"i"),OPTIONAL_SUFFIX:/\s*\((?:optional|if\s+present|maybe)\)\s*$/i,FALLBACK_QUOTED_TAP:/^(?:tap|click)\s+.*?['"]([^'"]+)['"]/i},as={HIERARCHY_ROOT:/<hierarchy[^>]*>([\s\S]*?)<\/hierarchy>/i,NODE_TAG:/<node\b([^>]*?)(\/?)>|<\/node>/gi,ATTR_KEY_VALUE:/([a-zA-Z0-9_-]+)="([^"]*)"/g,BOUNDS_RECT:/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/};wr=new Map;Tr=["sign in","signin","login","log in","submit","save","continue","next","done","send","confirm","proceed","post","rate","enter","join","apply","update"],Pr=["sign in","signin","login","log in","submit","save","next","continue","done","like","fav","favorite","star","bookmark","add","post","confirm","apply","explore","search","filter"]});function kr(a){let t=a.match(as.BOUNDS_RECT);if(!t)return{x1:0,y1:0,x2:0,y2:0,width:0,height:0,centerX:0,centerY:0};let e=parseInt(t[1],10),s=parseInt(t[2],10),n=parseInt(t[3],10),i=parseInt(t[4],10);return{x1:e,y1:s,x2:n,y2:i,width:n-e,height:i-s,centerX:Math.round((e+n)/2),centerY:Math.round((s+i)/2)}}function Gi(a){let t=a.match(as.HIERARCHY_ROOT);if(!t)return{root:[],flat:[]};let e=t[1],s=new RegExp(as.NODE_TAG.source,"gi"),n=new RegExp(as.ATTR_KEY_VALUE.source,"g"),i=[],r=[],o=[],l;for(;(l=s.exec(e))!==null;){let c=l[0],d=l[1],p=l[2]==="/";if(c.startsWith("</node>")){o.pop();continue}let u={};n.lastIndex=0;let h;for(;(h=n.exec(d))!==null;)u[h[1]]=h[2];let m={index:parseInt(u.index||"0",10),text:zt(u.text||""),resourceId:u["resource-id"]||"",className:u.class||"",packageName:u.package||"",contentDesc:zt(u["content-desc"]||""),bounds:kr(u.bounds||""),clickable:u.clickable==="true",enabled:u.enabled==="true",focused:u.focused==="true",scrollable:u.scrollable==="true",password:u.password==="true",selected:u.selected==="true",checked:u.checked!==void 0?u.checked==="true":void 0,checkable:u.checkable!==void 0?u.checkable==="true":void 0,children:[]};r.push(m),o.length===0?i.push(m):o[o.length-1].children.push(m),p||o.push(m)}return{root:i,flat:r}}function Xi(a){let t=[],e=[],s=[],n=a.length,i=0;for(;i<n;){let r=a.indexOf("<",i);if(r===-1)break;if(a.startsWith("<!--",r)){let w=a.indexOf("-->",r+4);i=w===-1?n:w+3;continue}if(a.startsWith("<?",r)){let w=a.indexOf("?>",r+2);i=w===-1?n:w+2;continue}if(a.startsWith("</",r)){let w=a.indexOf(">",r+2);if(w===-1)break;a.slice(r+2,w).trim()==="node"&&s.pop(),i=w+1;continue}let o=-1,l=null,c=!1;for(let w=r+1;w<n;w++){let S=a[w];if(l)S===l&&(l=null);else if(S==='"'||S==="'")l=S;else if(S===">"){o=w,a[w-1]==="/"&&(c=!0);break}}if(o===-1)break;let d=a.slice(r+1,c?o-1:o).trim();i=o+1;let p=0;for(;p<d.length&&!/\s/.test(d[p]);)p++;if(d.slice(0,p)!=="node")continue;let h={},m=p,b=d.length;for(;m<b;){for(;m<b&&/\s/.test(d[m]);)m++;if(m>=b)break;let w=m;for(;m<b&&d[m]!=="="&&!/\s/.test(d[m]);)m++;let S=d.slice(w,m);for(;m<b&&/\s/.test(d[m]);)m++;if(m>=b||d[m]!=="=")break;for(m++;m<b&&/\s/.test(d[m]);)m++;if(m>=b)break;let y=d[m];if(y==='"'||y==="'"){m++;let P=m;for(;m<b&&d[m]!==y;)m++;h[S]=d.slice(P,m),m<b&&m++}else{let P=m;for(;m<b&&!/\s/.test(d[m]);)m++;h[S]=d.slice(P,m)}}let T={index:parseInt(h.index||"0",10),text:zt(h.text||""),resourceId:h["resource-id"]||"",className:h.class||"",packageName:h.package||"",contentDesc:zt(h["content-desc"]||""),bounds:kr(h.bounds||""),clickable:h.clickable==="true",enabled:h.enabled==="true",focused:h.focused==="true",scrollable:h.scrollable==="true",password:h.password==="true",selected:h.selected==="true",checked:h.checked!==void 0?h.checked==="true":void 0,checkable:h.checkable!==void 0?h.checkable==="true":void 0,children:[]};e.push(T),s.length===0?t.push(T):s[s.length-1].children.push(T),c||s.push(T)}return{root:t,flat:e}}function be(a,t){if((t||R.parser?.mode||"regex")==="streaming")try{return Xi(a)}catch{}return Gi(a)}function We(a){if(a.resourceId&&/logbox/i.test(a.resourceId)||a.className&&/logbox/i.test(a.className))return!0;let t=`${a.text} ${a.contentDesc}`.trim();return!!Qi.some(e=>e.test(t))}function Fe(a){return a.filter(t=>We(t)?!1:(t.className.includes("EditText")||t.className.includes("TextInputEditText")||t.focused&&t.clickable)&&t.bounds.width>0&&t.bounds.height>0)}function Js(a){return a.className.includes("EditText")||a.className.includes("TextInputEditText")?!0:(a.children||[]).some(Js)}function Rt(a,t){let e=t?.width||R.screen.fallbackWidth,s=t?.height||R.screen.fallbackHeight,n=s*.08,i=s*.88,r=a.filter(l=>!We(l)&&(l.className.includes("RecyclerView")||l.className.includes("ListView")||l.className.includes("ScrollView"))),o=[];for(let l of r)for(let c of l.children){if(We(c)||c.bounds.centerY<n||c.bounds.centerY>i)continue;(c.clickable||c.children.some(p=>!We(p)&&p.clickable&&p.bounds.width>e*.1))&&c.bounds.width>e*.08&&c.bounds.height>s*.02&&!Js(c)&&o.push(c)}return o.length===0?a.filter(l=>{if(We(l))return!1;let c=(l.text||l.contentDesc||"").trim();return c.length<=1||/^(remember me|forgot password|sign in|sign up|login|submit|cancel)$/i.test(c)?!1:l.clickable&&l.bounds.width>e*.14&&l.bounds.height>s*.033&&l.bounds.centerY>n&&l.bounds.centerY<i&&!l.className.includes("Button")&&!l.className.includes("EditText")&&!Js(l)}):o}function Oe(a){return a.filter(t=>{if(We(t)||!t.clickable||t.bounds.width===0||t.className.includes("EditText"))return!1;let e=t.className.includes("Button")||t.className.includes("ImageButton"),s=Pr.some(n=>me(n,t.text)||me(n,t.contentDesc)||me(n,t.resourceId));return e||s})}function at(a,t=R.screen.fallbackHeight,e){if(!a||a.length===0)return[];let s=a.find(h=>h.bounds.height>=t*.6&&h.bounds.y1<=t*.15),n=s?.bounds?.y1!==void 0&&s.bounds.y1>=0?s.bounds.y1:0,i=s?.bounds?.y2&&s.bounds.y2>n?s.bounds.y2:t,r=i-n,o=s?.bounds?.x2&&s.bounds.x2>0?s.bounds.x2-(s.bounds.x1||0):R.screen.fallbackWidth,l=n+r*.78,c=a.filter(h=>We(h)||e&&h.packageName&&!h.packageName.includes(e)||!h.clickable||h.bounds.width<=0||h.bounds.height<=0||!(h.bounds.centerY>=l||h.bounds.y2>=i-20)?!1:h.bounds.width>=o*.12&&h.bounds.width<=o*.55);if(c.length<=1)return c;let d=[];for(let h of c){let m=d.find(b=>Math.abs(b[0].bounds.centerY-h.bounds.centerY)<=30||Math.abs(b[0].bounds.y2-h.bounds.y2)<=20);m||(m=[],d.push(m)),m.push(h)}let p=d.map(h=>{h.sort((b,T)=>b.bounds.centerX-T.bounds.centerX);let m=[];for(let b of h)m.some(T=>Math.abs(T.bounds.centerX-b.bounds.centerX)<o*.08)||m.push(b);return m}).filter(h=>h.length<2||h.length>6?!1:h[h.length-1].bounds.x2-h[0].bounds.x1>=o*.6);return p.length===0?c.slice(0,5):(p.sort((h,m)=>m[0].bounds.centerY-h[0].bounds.centerY),p[0].slice(0,5))}function Er(a){let t=[/fatal\s+exception/i,/unfortunately.*stopped/i,/(?:app|system|process).*(?:isn't|is not|not)\s+responding/i,/(?:isn't|is not|not)\s+responding/i,/force\s+close/i,/network\s+connection\s+(?:failed|lost|error)/i,/something went wrong/i,/(?:failed|unable)\s+to\s+(?:connect|load|fetch)/i];return a.filter(e=>{let s=`${e.text} ${e.contentDesc}`;return t.some(n=>n.test(s))})}function Ze(a,t,e=R.safety?.mode||"strict"){if(e==="disabled")return!1;let s=`${a.text} ${a.contentDesc} ${a.resourceId}`.trim();if((e==="moderate"?qi:Zi).some(r=>r.test(s)))return!0;let i=t||R.safety?.customBlacklist||[];for(let r of i)if(typeof r=="string"&&r.length>0){if(s.toLowerCase().includes(r.toLowerCase()))return!0}else if(r instanceof RegExp&&r.test(s))return!0;return!1}function qe(a){let t=`${a.text} ${a.contentDesc} ${a.resourceId}`.trim();return eo.some(e=>e.test(t))}function cs(a){for(let t of a){let e=`${t.text} ${t.contentDesc} ${t.resourceId}`;if(t.resourceId.toLowerCase().includes("logbox")||e.includes("Unhandled Promise Rejection")||e.includes("SyntaxError")||e.includes("Encountered two children with the same key")||e.includes("Warning:")||e.includes("Console Error")||e.includes("Console Warning")||e.includes("Console Info")||e.includes("TypeError:")||e.includes("ReferenceError:")||/Log \d+ of \d+/i.test(e)||e.includes("LogBox")&&(t.className.includes("Modal")||t.className.includes("ViewGroup"))){let n="error";/warning/i.test(e)?n="warning":/info/i.test(e)&&(n="info");let i=t.resourceId.includes("notification")||t.bounds.height>0&&t.bounds.height<250,r,o=[];for(let d of a){let p=`${d.text} ${d.contentDesc}`.trim();if(!r){let u=p.match(/\b([a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+)\b/);u&&(r=u[1])}/\b(?:at\s+|\()?[a-zA-Z0-9_.-]+\.(?:tsx?|jsx?|js|ts):\d+/i.test(p)&&!o.includes(p)&&p.length<200&&o.push(p)}let l=a.find(d=>d.clickable&&d.bounds.width>0&&(/^(?:dismiss|close)$/i.test((d.text||"").trim())||/^(?:dismiss|close)$/i.test((d.contentDesc||"").trim())));if(!l&&!i){let p=a.filter(u=>u.clickable&&u.bounds.width>0&&u.bounds.height>0&&!/minimize|copy/i.test(`${u.text} ${u.contentDesc}`)).filter(u=>u.bounds.centerY>R.screen.fallbackHeight*.8);p.length>0&&(p.sort((u,h)=>u.bounds.x1-h.bounds.x1),l=p[0])}!l&&i&&(l=a.find(d=>d.clickable&&d.bounds.y1>=t.bounds.y1-20&&d.bounds.y2<=t.bounds.y2+20&&d.bounds.x1>=t.bounds.x2-120));let c=t.text||t.contentDesc||"React Native LogBox Console Overlay";return{type:"logbox",severity:n,node:t,errorText:c,sourceLocation:r,stackTrace:o.length>0?o:void 0,dismissButton:l,isToast:i}}if(t.resourceId.includes("alertTitle")&&(e.includes("isn't responding")||e.includes("has stopped"))){let n=a.find(i=>i.text.toLowerCase().includes("close app")||i.text.toLowerCase().includes("ok"));return{type:"anr",severity:"error",node:t,errorText:t.text||"Application Not Responding (ANR)",dismissButton:n}}}return null}function Xt(a){return a.filter(t=>We(t)?!1:(t.checkable||t.className.includes("CheckBox")||t.className.includes("Switch")||t.className.includes("RadioButton")||t.className.includes("SwitchCompat"))&&t.bounds.width>0&&t.bounds.height>0)}function yt(a,t=R.screen.fallbackHeight,e=R.screen.fallbackWidth){let s=t*.22,n=/(?:menu|hamburger|drawer|navigation|\u2630|open[_\s-]*drawer)/i,i=a.find(r=>{if(!r.clickable||r.bounds.width===0||r.bounds.centerY>s)return!1;let o=`${r.contentDesc} ${r.text} ${r.resourceId}`;return n.test(o)});return i||a.find(r=>{if(!r.clickable||r.bounds.width===0||r.bounds.height===0||r.bounds.centerY>s||(r.text||r.contentDesc||"").trim().length>0)return!1;let l=r.bounds.x1<=e*.25,c=r.bounds.width<=250&&r.bounds.height<=250;return l&&c})||null}function Ar(a,t=R.screen.fallbackHeight,e=R.screen.fallbackWidth){let s=t*.22;return a.filter(n=>{if(!n.clickable||n.bounds.width===0||n.bounds.height===0||n.bounds.centerY>s)return!1;let i=n.bounds.x2>=e*.75,r=n.bounds.width<=250&&n.bounds.height<=250;return i&&r})}function Dr(a){return a.filter(t=>{if(t.bounds.width===0||t.bounds.height===0)return!1;let e=t.className.includes("ScrollView")||t.className.includes("RecyclerView")||t.className.includes("ListView")||t.className.includes("NestedScrollView");return t.scrollable||e})}function $r(a,t=R.screen.fallbackWidth,e=R.screen.fallbackHeight,s=R.visual.overlapRatio){let n=[];for(let u of a)(u.text||u.clickable)&&u.bounds.width>0&&(u.bounds.x2>t+10&&u.bounds.x1<t&&n.push({type:"OVERFLOW",severity:"WARN",description:`Element "${u.text||u.contentDesc||u.className}" overflows right screen boundary by ${u.bounds.x2-t}px`,elements:[u.text||u.contentDesc||u.className]}),u.bounds.y2>e+20&&u.bounds.y1<e&&n.push({type:"OVERFLOW",severity:"WARN",description:`Element "${u.text||u.contentDesc||u.className}" overflows bottom screen boundary by ${u.bounds.y2-e}px`,elements:[u.text||u.contentDesc||u.className]}));let i=a.filter(u=>u.text&&u.text.trim().length>1&&u.bounds.width>0&&u.bounds.height>0),r=200,o=new Map;for(let u of i){let h=Math.floor(u.bounds.y1/r),m=Math.floor(u.bounds.y2/r);for(let b=h;b<=m;b++)o.has(b)||o.set(b,[]),o.get(b).push(u)}let l=new Set;for(let[,u]of o)for(let h=0;h<u.length;h++)for(let m=h+1;m<u.length;m++){let b=u[h],T=u[m];if(b===T||b.text===T.text)continue;let w=b.bounds.x1<T.bounds.x1?`${b.bounds.x1},${b.bounds.y1}:${T.bounds.x1},${T.bounds.y1}`:`${T.bounds.x1},${T.bounds.y1}:${b.bounds.x1},${b.bounds.y1}`;if(l.has(w))continue;l.add(w);let S=b.bounds,y=T.bounds;if(Math.abs(S.centerY-y.centerY)>(S.height+y.height)/2)continue;let P=Math.max(0,Math.min(S.x2,y.x2)-Math.max(S.x1,y.x1)),N=Math.max(0,Math.min(S.y2,y.y2)-Math.max(S.y1,y.y1)),O=P*N;if(O>0){let L=S.width*S.height,U=y.width*y.height,j=Math.min(L,U);j>0&&O/j>s&&n.push({type:"OVERLAP",severity:"WARN",description:`Visual text collision: "${b.text}" overlaps with "${T.text}" (${Math.round(O)}px\xB2 collision)`,elements:[b.text,T.text]})}}let c=a.filter(u=>(u.className.includes("ProgressBar")||u.className.includes("ActivityIndicator"))&&u.bounds.width>0);c.length>0&&n.push({type:"STALLED_LOADER",severity:"WARN",description:`Active loading indicator (${c[0].className}) detected on screen`,elements:c.map(u=>u.className)});let d=[],p=new Set;for(let u of n){let h=`${u.type}::${[...u.elements].sort().join("::")}`;p.has(h)||(p.add(h),d.push(u))}return d}function Ks(a,t,e){let s=t?.height||R.screen.fallbackHeight,n=t?.width||R.screen.fallbackWidth,i=at(a,s,e);if(i.length>1&&i[0].bounds.centerY>=s*.75)return{paradigm:"bottom_tabs",roots:i};let r=s*.05,o=s*.22,l=a.filter(d=>{if(We(d)||e&&d.packageName&&!d.packageName.includes(e))return!1;let p=d.className.includes("Tab")||d.resourceId.toLowerCase().includes("tab")||d.resourceId.toLowerCase().includes("header_tab")||/(?:tab\s*\d|\bphotos\b|\bvideos\b|\baudios\b)/i.test(`${d.text} ${d.contentDesc}`),u=d.bounds.centerY>=r&&d.bounds.centerY<=o,h=d.bounds.height>25&&d.bounds.height<=220;return(p||u)&&d.clickable&&d.bounds.width>50&&h});if(l.length>=2){let d=[];for(let u of l){let h=d.find(m=>Math.abs(m[0].bounds.centerY-u.bounds.centerY)<=20);h||(h=[],d.push(h)),h.push(u)}let p=d.find(u=>u.length>=2);if(p){p.sort((m,b)=>m.bounds.centerX-b.bounds.centerX);let u=[];for(let m of p)u.some(b=>Math.abs(b.bounds.centerX-m.bounds.centerX)<40)||u.push(m);if(!(u.length===2&&u[0].bounds.x1<=n*.25&&u[1].bounds.x2>=n*.75&&!u.some(m=>m.className.includes("Tab")||m.resourceId.toLowerCase().includes("tab")))&&u.length>=2)return{paradigm:"top_tabs",roots:u.slice(0,5)}}}let c=yt(a,s,n);return c?{paradigm:"drawer",roots:[c]}:{paradigm:"single_hub",roots:[]}}function Cr(a,t=2){if(a.length<=t)return a;let e=[];for(let n of a){let i=e.find(r=>{let o=r[0],l=o.className===n.className,c=Math.abs(o.bounds.width-n.bounds.width)<=30,d=Math.abs(o.bounds.height-n.bounds.height)<=30,p=Math.abs(o.bounds.x1-n.bounds.x1)<=30;return l&&c&&d&&p});i||(i=[],e.push(i)),i.push(n)}let s=[];for(let n of e)s.push(...n.slice(0,t));return s}function Ye(a,t=R.screen.fallbackHeight,e=R.screen.fallbackWidth,s=!1){let n=t*.22,i=e*.28,r=/(?:navigate[_\s-]*up|go[_\s-]*back|^back$|\bback\b|arrow_back|ic_arrow_back|chevron[_\s-]*left|ic_back|btn_back|action_bar_up|header[_\s-]*left|prev(?:ious)?|^close$|\bclose\b|^cancel$|^dismiss$|^done$|^exit$|[<←«‹❮⟨◁◀✕✖×]|\u2190|\u2196|\u2039|\u27E8|\u276E|\u25C1|\u25C0|\u00AB|\u2715|\u2716|\u00D7|[\uF060\uF104\uF053\uF2EA\uF00D\uE5C4\uE5CD])/i,o=l=>l.bounds.width>0&&l.bounds.height>0&&l.bounds.centerY<=n;for(let l of a){if(We(l)||!o(l))continue;let c=`${l.text||""} ${l.contentDesc||""} ${l.resourceId||""}`.trim();if(r.test(c)||c.length<=2&&/^[<‹«←x✕×\-]$/i.test(c))return l.clickable?l:a.find(p=>p.clickable&&p.bounds.x1<=l.bounds.x1&&p.bounds.x2>=l.bounds.x2&&p.bounds.y1<=l.bounds.y1&&p.bounds.y2>=l.bounds.y2&&p.bounds.width<=300&&p.bounds.height<=300)||l}for(let l of a){if(We(l)||!o(l)||l.bounds.x1>i)continue;if(l.className.includes("ImageButton")||l.className.includes("ImageView")||l.className.includes("ViewGroup")&&l.bounds.width<=250&&l.bounds.height<=250){let d=`${l.text||""} ${l.contentDesc||""} ${l.resourceId||""}`.toLowerCase();if(d.includes("back")||d.includes("up")||d.includes("arrow")||d.includes("chevron")||d.includes("nav")||d.includes("close")||d.includes("cancel"))return l.clickable?l:a.find(u=>u.clickable&&u.bounds.x1<=l.bounds.x1&&u.bounds.x2>=l.bounds.x2&&u.bounds.y1<=l.bounds.y1&&u.bounds.y2>=l.bounds.y2)||l}}if(s){let l=a.find(c=>{if(We(c)||!o(c)||!c.clickable)return!1;let d=c.bounds.x1<=i,p=c.bounds.width<=250&&c.bounds.height<=250;return d&&p&&c.bounds.width<e*.4});if(l)return l}return null}var Qi,Zi,qi,eo,Ve=ie(()=>{"use strict";je();ot();Qi=[/logbox/i,/^Log \d+ of \d+/i,/Console (?:Error|Warning|Log|Info)/i,/^Call Stack$/i,/^Source Map$/i,/^(?:Dismiss|Minimize|Copy|View error log)$/i,/\b[\w-]+\.(?:tsx?|jsx?|js|ts):\d+:\d+\b/i,/Unhandled Promise Rejection/i,/SyntaxError/i,/TypeError:/i,/ReferenceError:/i,/Warning:/i,/LogBoxNotificationOverlay/i];Zi=[/(?:\b|[_\s-])(?:log[_\s-]*out|sign[_\s-]*out)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:delete|remove)(?:[_\s-]+(?:account|profile|all|data|item|cart|user))?(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:clear[_\s-]+data|wipe|force[_\s-]+stop|factory[_\s-]+reset|uninstall)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:pay|purchase|checkout|buy[_\s-]+now|order[_\s-]+now)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:exit[_\s-]*app|close[_\s-]*app|^exit$|^quit$)(?:\b|[_\s-])/i],qi=[/(?:\b|[_\s-])(?:log[_\s-]*out|sign[_\s-]*out)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:delete|remove)[_\s-]+(?:account|profile|user)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:clear[_\s-]+data|wipe|force[_\s-]+stop|factory[_\s-]+reset|uninstall)(?:\b|[_\s-])/i],eo=[/(?:\b|[_\s-])(?:rate[_\s-]*us|review[_\s-]*(?:on|in)[_\s-]*(?:play|store)|play[_\s-]*store)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:visit[_\s-]*(?:our)?[_\s-]*website|privacy[_\s-]*policy|terms[_\s-]*of[_\s-]*service|terms[_\s-]*&[_\s-]*conditions)(?:\b|[_\s-])/i,/(?:\b|[_\s-])(?:follow[_\s-]*us|share[_\s-]*via|share[_\s-]*app|open[_\s-]*in[_\s-]*browser)(?:\b|[_\s-])/i,/^https?:\/\//i,/(?:www\.[a-z0-9-]+\.[a-z]{2,})/i]});import zs from"chalk";var Gs,ys,ct,Qt=ie(()=>{"use strict";Gs={DEBUG:0,INFO:1,WARN:2,ERROR:3,SILENT:4},ys=class{currentLevel="INFO";constructor(t){let e=(process.env.LOG_LEVEL||"").toUpperCase();t?this.currentLevel=t:Gs[e]!==void 0?this.currentLevel=e:this.currentLevel="INFO"}setLevel(t){this.currentLevel=t}getLevel(){return this.currentLevel}isLevelEnabled(t){return Gs[this.currentLevel]<=Gs[t]}isJsonMode(){return(process.env.LOG_FORMAT||"").toLowerCase()==="json"}emitJson(t,...e){let s=e.map(i=>typeof i=="object"?JSON.stringify(i):String(i)).join(" "),n={ts:new Date().toISOString(),level:t,msg:s};t==="ERROR"||t==="WARN"?console.error(JSON.stringify(n)):console.log(JSON.stringify(n))}debug(...t){this.isLevelEnabled("DEBUG")&&(this.isJsonMode()?this.emitJson("DEBUG",...t):console.log(zs.gray("[DEBUG]"),...t))}info(...t){this.isLevelEnabled("INFO")&&(this.isJsonMode()?this.emitJson("INFO",...t):console.log(...t))}warn(...t){this.isLevelEnabled("WARN")&&(this.isJsonMode()?this.emitJson("WARN",...t):console.warn(zs.yellow("[WARN]"),...t))}error(...t){this.isLevelEnabled("ERROR")&&(this.isJsonMode()?this.emitJson("ERROR",...t):console.error(zs.red("[ERROR]"),...t))}},ct=new ys});import to from"zlib";import so from"fs";function no(a){let t=-1;for(let e=0;e<a.length;e++){let s=a[e];t^=s;for(let n=0;n<8;n++)t=t>>>1^-(t&1)&3988292384}return~t>>>0}function ro(a=new Date){let t=(a.getHours()&31)<<11|(a.getMinutes()&63)<<5|Math.floor(a.getSeconds()/2)&31,e=(a.getFullYear()-1980&127)<<9|(a.getMonth()+1&15)<<5|a.getDate()&31;return{time:t,date:e}}function io(a){let t=[],e=[],s=0;for(let l of a){let c=l.data,d=to.deflateRawSync(c),p=d.length<c.length,u=p?d:c,h=p?8:0,m=no(c),b=c.length,T=u.length,{time:w,date:S}=ro(l.date),y=Buffer.from(l.name.replace(/\\/g,"/"),"utf-8"),P=Buffer.alloc(30+y.length);P.writeUInt32LE(67324752,0),P.writeUInt16LE(20,4),P.writeUInt16LE(2048,6),P.writeUInt16LE(h,8),P.writeUInt16LE(w,10),P.writeUInt16LE(S,12),P.writeUInt32LE(m,14),P.writeUInt32LE(T,18),P.writeUInt32LE(b,22),P.writeUInt16LE(y.length,26),P.writeUInt16LE(0,28),y.copy(P,30),t.push(P),t.push(u);let N=Buffer.alloc(46+y.length);N.writeUInt32LE(33639248,0),N.writeUInt16LE(20,4),N.writeUInt16LE(20,6),N.writeUInt16LE(2048,8),N.writeUInt16LE(h,10),N.writeUInt16LE(w,12),N.writeUInt16LE(S,14),N.writeUInt32LE(m,16),N.writeUInt32LE(T,20),N.writeUInt32LE(b,24),N.writeUInt16LE(y.length,28),N.writeUInt16LE(0,30),N.writeUInt16LE(0,32),N.writeUInt16LE(0,34),N.writeUInt16LE(0,36),N.writeUInt32LE(0,38),N.writeUInt32LE(s,42),y.copy(N,46),e.push(N),s+=P.length+u.length}let n=Buffer.concat(e),i=n.length,r=s,o=Buffer.alloc(22);return o.writeUInt32LE(101010256,0),o.writeUInt16LE(0,4),o.writeUInt16LE(0,6),o.writeUInt16LE(a.length,8),o.writeUInt16LE(a.length,10),o.writeUInt32LE(i,12),o.writeUInt32LE(r,16),o.writeUInt16LE(0,20),Buffer.concat([...t,n,o])}async function Ir(a,t){let e=io(t);await so.promises.writeFile(a,e)}var Nr=ie(()=>{"use strict"});import dt from"fs";import Je from"path";import we from"chalk";function Xs(a,t=new Set){let e=Je.resolve(a);if(!dt.existsSync(e))throw new Error(`Included spec file not found: ${e}`);if(t.has(e)){let r=Array.from(t).concat(e);throw new os(`Circular spec inclusion detected: ${r.join(" -> ")}`,r)}t.add(e);let n=dt.readFileSync(e,"utf-8").split(`
26
+ `),i=[];for(let r of n){let o=r.trim(),l=o.match(ae.INCLUDE_SPEC);if(l&&!o.startsWith("#")){let c=ue([l[1],l[2],l[3]]),d=Je.isAbsolute(c)?c:Je.resolve(Je.dirname(e),c),p=Xs(d,new Set(t));i.push(p)}else i.push(r)}return i.join(`
27
+ `)}function oo(a){let t=[],e=[];for(let s of a){if(s._isControl==="END"){e.length>0&&e.pop();continue}if(s._isControl==="ELSE"){e.length>0&&e[e.length-1].step.type==="IF_VISIBLE"&&(e[e.length-1].inElse=!0);continue}let n=t;if(e.length>0){let i=e[e.length-1];i.step.type==="IF_VISIBLE"?i.inElse?(i.step.elseSteps=i.step.elseSteps||[],n=i.step.elseSteps):(i.step.thenSteps=i.step.thenSteps||[],n=i.step.thenSteps):i.step.type==="LOOP"&&(i.step.childSteps=i.step.childSteps||[],n=i.step.childSteps)}n.push(s),(s.type==="IF_VISIBLE"||s.type==="LOOP")&&e.push({step:s,inElse:!1})}return t}function St(a,t){let e=a.split(`
28
+ `).map(n=>n.trim()).filter(n=>n.length>0&&!n.startsWith("#")),s=[];for(let n of e){let i=Lt(n,t),r=i.replace(vr,"").trim(),o=ae.OPTIONAL_SUFFIX.test(r)||/^[(\[]\s*optional\s*[)\]]/i.test(r)||/^optional\s*:/i.test(r),l=r.replace(ae.OPTIONAL_SUFFIX,"").replace(/^[(\[]\s*optional\s*[)\]]\s*/i,"").replace(/^optional\s*:\s*/i,"").trim();if(ae.BACK.test(l)){s.push({originalText:i,type:"BACK",optional:o});continue}let c=l.match(ae.WAIT);if(c){let v=parseInt(c[1],10),C=l.toLowerCase().includes("ms")?v:v*1e3;s.push({originalText:i,type:"WAIT",durationMs:C,optional:o});continue}let d=l.match(ae.WAIT_UNTIL);if(d){let v=ue([d[1],d[2],d[3]]),A=(d[4]||"visible").toLowerCase().includes("enable")?"enabled":"visible",D=d[5]?parseInt(d[5],10):void 0,I=(d[6]||"s").toLowerCase(),H=D?I.includes("ms")?D:D*1e3:void 0;s.push({originalText:i,type:"WAIT_UNTIL",target:v,expectedState:A,durationMs:H,optional:o});continue}let p=l.match(ae.IF_VISIBLE);if(p){let v=ue([p[1],p[2],p[3],p[5],p[6],p[7]]);s.push({originalText:i,type:"IF_VISIBLE",conditionTarget:v,thenSteps:[],elseSteps:[],optional:o});continue}if(ae.ELSE.test(l)){s.push({originalText:i,type:"IF_VISIBLE",_isControl:"ELSE"});continue}if(ae.END.test(l)){s.push({originalText:i,type:"IF_VISIBLE",_isControl:"END"});continue}let u=l.match(ae.LOOP);if(u){let v=parseInt(u[1],10);s.push({originalText:i,type:"LOOP",loopCount:v,childSteps:[],optional:o});continue}let h=l.match(ae.FOR_EACH);if(h){let v=ue([h[1],h[2],h[3]]);s.push({originalText:i,type:"LOOP",target:v,loopVariable:"item",childSteps:[],optional:o});continue}let m=l.match(ae.SET_VARIABLE);if(m){let v=m[1].trim(),C=m[2].trim();s.push({originalText:i,type:"SET_VARIABLE",variableName:v,variableExpression:C,optional:o});continue}let b=l.match(ae.DEVICE_WIFI);if(b){let v=b[1].toLowerCase();s.push({originalText:i,type:"DEVICE_STATE",deviceAction:"wifi",deviceStateValue:v==="on"||v==="enable",optional:o});continue}let T=l.match(ae.DEVICE_AIRPLANE);if(T){let v=T[1].toLowerCase();s.push({originalText:i,type:"DEVICE_STATE",deviceAction:"airplane",deviceStateValue:v==="on"||v==="enable",optional:o});continue}let w=l.match(ae.DEVICE_ORIENTATION);if(w){let v=w[1].toLowerCase();s.push({originalText:i,type:"DEVICE_STATE",deviceAction:"orientation",deviceStateValue:v,optional:o});continue}let S=l.match(ae.DEVICE_NOTIFICATIONS);if(S){let v=S[1].toLowerCase(),C=v.startsWith("open")||v.startsWith("expand")||v.startsWith("pull");s.push({originalText:i,type:"DEVICE_STATE",deviceAction:"notifications",deviceStateValue:C?"open":"collapse",optional:o});continue}let y=l.match(ae.RUN_JS);if(y){let v=y[1].trim();s.push({originalText:i,type:"RUN_JS",jsCode:v,optional:o});continue}let P=l.match(ae.ENTER_OTP);if(P){let v=ue([P[1],P[2],P[3]])||void 0;s.push({originalText:i,type:"ENTER_OTP",target:v,optional:o});continue}let N=l.match(ae.API_REQUEST);if(N){let v=N[1].toUpperCase(),C=ue([N[2],N[3],N[4]]),A=ue([N[5],N[6],N[7]])||void 0,D=ue([N[8],N[9],N[10]])||void 0,I=N[11]||void 0,H=N[12]?parseInt(N[12],10):void 0;s.push({originalText:i,type:"API_REQUEST",apiMethod:v,apiUrl:C,apiBody:A,apiStorePath:D,apiStoreVariable:I,apiExpectedStatus:H,optional:o});continue}let O=l.match(ae.EMAIL_ASSERT);if(O){let v=ue([O[1],O[2],O[3]]),C=ue([O[4],O[5],O[6]]),A=ue([O[7],O[8],O[9]])||void 0;s.push({originalText:i,type:"EMAIL_ASSERT",emailRecipient:v,emailSubject:C,emailBodyContains:A,optional:o});continue}let L=l.match(ae.LAUNCH_APP);if(L){let v=ue([L[1],L[2],L[3]]);s.push({originalText:i,type:"LAUNCH_APP",target:v,optional:o});continue}let U=l.match(ae.RESTART_APP);if(U){let v=ue([U[1],U[2],U[3]]);s.push({originalText:i,type:"RESTART_APP",target:v,optional:o});continue}let j=l.match(ae.TERMINATE_APP);if(j){let v=ue([j[1],j[2],j[3]])||void 0;s.push({originalText:i,type:"TERMINATE_APP",target:v,optional:o});continue}let Q=l.match(ae.CLEAR_DATA);if(Q){let v=ue([Q[1],Q[2],Q[3]]);s.push({originalText:i,type:"CLEAR_DATA",target:v,optional:o});continue}let K=l.match(ae.GRANT_PERMISSION);if(K){let v=ue([K[1],K[2],K[3]]),C=ue([K[4],K[5],K[6]])||void 0;s.push({originalText:i,type:"GRANT_PERMISSION",value:v,target:C,optional:o});continue}let V=l.match(ae.DEEP_LINK);if(V){let v=ue([V[1],V[2],V[3]]);s.push({originalText:i,type:"DEEP_LINK",target:v,optional:o});continue}let ye=l.match(ae.DIRECTIONAL_SWIPE);if(ye){let v=ye[1].toLowerCase(),C=ue([ye[2],ye[3],ye[4]])||void 0;s.push({originalText:i,type:"SWIPE",direction:v,target:C,optional:o});continue}let re=l.match(ae.TOGGLE);if(re){let v=(re[1]||"").toLowerCase(),C=ue([re[2],re[3],re[4]]),A=l.toLowerCase().startsWith("uncheck")||v==="off"?!1:v==="on"||l.toLowerCase().startsWith("check")?!0:void 0;s.push({originalText:i,type:"TOGGLE",target:C,desiredState:A,optional:o});continue}let W=l.match(ae.SELECT_DROPDOWN);if(W){let v=ue([W[1],W[2],W[3]]),C=ue([W[4],W[5],W[6]]);s.push({originalText:i,type:"SELECT",value:v,target:C,optional:o});continue}let se=l.match(ae.SCROLL_TO);if(se){let v=(se[1]||"down").toLowerCase(),C=ue([se[2],se[3],se[4]]);s.push({originalText:i,type:"SCROLL",target:C,direction:v,optional:o});continue}let x=l.match(ae.SCROLL);if(x){let v=x[1].toLowerCase();s.push({originalText:i,type:"SCROLL",target:v,direction:v,optional:o});continue}let f=l.match(ae.LONG_PRESS);if(f){let v=ue([f[1],f[2],f[3]]);s.push({originalText:i,type:"LONG_PRESS",target:v,optional:o});continue}let F=l.match(ae.ORDINAL_TAP);if(F){let v=F[1].toLowerCase(),C=ue([F[2],F[3],F[4]]);s.push({originalText:i,type:"TAP",target:C,ordinal:v,optional:o});continue}let E=l.match(ae.PROXIMITY_TAP);if(E){let v=ue([E[1],E[2],E[3]]),C=ue([E[4],E[5],E[6]]);s.push({originalText:i,type:"TAP",target:v,relativeTo:C,optional:o});continue}let ge=l.match(ae.TYPE);if(ge){let v=ue([ge[1],ge[2],ge[3]]),C=ue([ge[4],ge[5],ge[6]]);s.push({originalText:i,type:"TYPE",value:v,target:C,optional:o});continue}let Y=l.match(ae.CLEAR);if(Y){let v=ue([Y[1],Y[2],Y[3]]);s.push({originalText:i,type:"CLEAR",target:v,optional:o});continue}let g=l.match(ae.VERIFY);if(g){let v=ue([g[1],g[2],g[3]]),C=(g[4]||"visible").toLowerCase(),A=C.includes("not visible")||C.includes("not present")||C.includes("absent")||C.includes("hidden")||C.includes("gone"),D=A?"absent":C==="exists"||C==="enabled"||C==="disabled"?C:"visible";s.push({originalText:i,type:"VERIFY",target:v,expectedState:D,negative:A,optional:o});continue}let k=l.match(ae.TAP);if(k&&!l.toLowerCase().includes("back")){let v=ue([k[1],k[2],k[3]]);s.push({originalText:i,type:"TAP",target:v,optional:o});continue}let M=l.match(ae.FALLBACK_QUOTED_TAP);if(M&&(l.toLowerCase().startsWith("tap")||l.toLowerCase().startsWith("click"))){s.push({originalText:i,type:"TAP",target:M[1],optional:o});continue}ct.warn(`Unrecognized prompt step: "${n}". Skipping.`)}return oo(s)}function Rr(a,t,e){let s=t.map(r=>(r.text||r.contentDesc||"").trim()).filter(r=>r.length>1),n=Gt(a,s,3),i=n.length>0?`
29
29
  \u{1F4A1} Did you mean one of these visible items?
30
- `+n.map(i=>` \u2022 "${i.text}" (${Math.round(i.score*100)}% match)`).join(`
30
+ `+n.map(r=>` \u2022 "${r.text}" (${Math.round(r.score*100)}% match)`).join(`
31
31
  `):`
32
- \u{1F4A1} No similar text found on current screen.`;return new Error(`${e}${r}`)}function ni(c,t,e,s){let n=ue(t),r=n.includes("password")||n.includes("passcode")||n.includes("pin")||n.includes("pwd")||n.includes("secret"),i=n.includes("email")||n.includes("mail")||n.includes("user")||n.includes("login")||n.includes("username")||e&&e.includes("@"),o=c.filter(d=>d.className.includes("EditText")&&d.bounds.width>0);if(o.length===0)return null;let a=o.find(d=>me(t,d.contentDesc)||me(t,d.resourceId)||me(t,d.text));if(a)return a;let l=c.filter(d=>!d.className.includes("EditText")&&(me(t,d.text)||me(t,d.contentDesc)||me(t,d.resourceId))&&d.bounds.width>0);for(let d of l){let p=o.filter(h=>h.bounds.centerY>=d.bounds.centerY&&h.bounds.centerY-d.bounds.centerY<300&&Math.abs(h.bounds.centerX-d.bounds.centerX)<450);if(p.length>0)return p.sort((h,u)=>h.bounds.centerY-u.bounds.centerY),p[0]}if(s){let d=s.getAllCandidates("TYPE",t);for(let p of d){let h=o.find(u=>me(p,u.contentDesc)||me(p,u.resourceId)||me(p,u.text));if(h)return h}}if(r){let d=o.find(p=>p.password);if(d)return d;if(o.length>1){let p=o.find(h=>h.text?.toLowerCase().includes("email")||h.contentDesc?.toLowerCase().includes("email"));if(p){let h=o.filter(u=>u.bounds.centerY>p.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(i){let d=o.find(p=>!p.password);if(d)return d;if(o.length>0)return o[0]}return o.length===1?o[0]:null}function hs(c,t,e,s="en",n,r){let i=n?.type||"TAP",o=ue(t),l=c.filter(u=>(ue(u.text)===o||ue(u.contentDesc)===o)&&u.bounds.width>0&&u.bounds.height>0).find(u=>u.clickable);if(l)return[l];let d=e.getAllCandidates(i,t,s,r),p=[];for(let u of d){let m=c.filter(f=>{let x=me(u,f.text),v=me(u,f.contentDesc),S=me(u,f.resourceId);return(x||v||S)&&f.bounds.width>0&&f.bounds.height>0});if(m.length>0){p=m;break}}if(p.length===0){let u=[];for(let f of c)f.text&&f.text.trim().length>1&&u.push(f.text.trim()),f.contentDesc&&f.contentDesc.trim().length>1&&u.push(f.contentDesc.trim());let m=At(t,u,1);if(m.length>0&&m[0].score>=.8){let f=m[0].text,x=c.filter(v=>(me(f,v.text)||me(f,v.contentDesc))&&v.bounds.width>0&&v.bounds.height>0);x.length>0&&(console.log(be.yellow(` \u{1F4A1} [Self-Healing] Auto-resolved "${t}" \u2192 "${f}" (${Math.round(m[0].score*100)}% match)`)),p=x)}}if(p.length===0)return[];let h=p.filter(u=>{let m=ue(u.text),f=ue(u.contentDesc);return m===o||f===o});if(h.length>0)p=h;else{let u=p.filter(m=>{let f=ue(m.contentDesc);return f.endsWith(o)||f.includes(`, ${o}`)});u.length>0&&(p=u)}if(n?.relativeTo){let u=c.find(m=>me(n.relativeTo,m.text)||me(n.relativeTo,m.contentDesc));if(u){let m=p.filter(f=>Math.abs(f.bounds.centerY-u.bounds.centerY)<140).sort((f,x)=>{let v=Math.hypot(f.bounds.centerX-u.bounds.centerX,f.bounds.centerY-u.bounds.centerY),S=Math.hypot(x.bounds.centerX-u.bounds.centerX,x.bounds.centerY-u.bounds.centerY);return v-S});if(m.length>0)return m}}if(n?.ordinal){let u=n.ordinal.toLowerCase();if(u==="1st"||u==="first")return[p[0]];if(u==="2nd"&&p.length>1)return[p[1]];if(u==="3rd"&&p.length>2)return[p[2]];if(u==="4th"&&p.length>3)return[p[3]];if(u==="5th"&&p.length>4)return[p[4]];if(u==="last"&&p.length>0)return[p[p.length-1]]}return p}function De(c){if(!c||c.length===0)return"0";let t=2166136261;for(let e=0;e<c.length;e++){let s=c[e],n=`${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<n.length;r++)t^=n.charCodeAt(r),t=Math.imul(t,16777619)}return(t>>>0).toString(16)}function ti(c){if(!c)return"";let t=c;for(let e of or)t=t.replace(e,"<VAL>");return t.trim()}function ot(c,t){if(!c||c.length===0)return"0";let s=(t?.height||2400)*.04,n=2166136261;for(let r=0;r<c.length;r++){let i=c[r];if(i.bounds&&i.bounds.centerY<s)continue;let o=ti(i.text||""),a=ti(i.contentDesc||""),l=i.resourceId||"",d=i.className||"",p=i.clickable?"1":"0",h=i.checkable?"1":"0",u=Math.round((i.bounds?.x1||0)/50),m=Math.round((i.bounds?.y1||0)/50),f=Math.round((i.bounds?.width||0)/50),x=Math.round((i.bounds?.height||0)/50),v=`${d}|${l}|${o}|${a}|${p}|${h}|${u},${m},${f},${x}`;for(let S=0;S<v.length;S++)n^=v.charCodeAt(S),n=Math.imul(n,16777619)}return(n>>>0).toString(16)}function ii(c){let t=new Set;if(!c)return t;let e=c.split(",").map(s=>s.trim()).filter(Boolean);for(let s of e)if(s.includes("-")){let[n,r]=s.split("-").map(a=>a.trim()),i=parseInt(n,10),o=parseInt(r,10);if(!isNaN(i)&&!isNaN(o)){let[a,l]=i<=o?[i,o]:[o,i];for(let d=a;d<=l;d++)t.add(d)}}else{let n=parseInt(s,10);isNaN(n)||t.add(n)}return t}function ri(c){let t=new Set,e=c.split(`
33
- `);for(let s of e){let n=s.trim();if(!n.startsWith("#"))continue;let r=n.match(/#\s*@tags?:\s*(.+)/i);if(r){r[1].split(",").map(o=>o.trim().toLowerCase()).filter(Boolean).forEach(o=>t.add(o));continue}let i=n.match(/#\s*@([a-zA-Z0-9_-]+)/);i&&i[1].toLowerCase()!=="tags"&&i[1].toLowerCase()!=="tag"&&t.add(i[1].toLowerCase())}return Array.from(t)}function oi(c){let t=["",be.bold.cyan(`\u{1F4CB} Parsed Test Steps (${c.length} total):`),be.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"),be.gray("\u2502")+` ${be.bold("#".padEnd(3))} `+be.gray("\u2502")+` ${be.bold("Type".padEnd(14))} `+be.gray("\u2502")+` ${be.bold("Target".padEnd(28))} `+be.gray("\u2502")+` ${be.bold("Value / Detail".padEnd(30))} `+be.gray("\u2502")+` ${be.bold("Optional".padEnd(8))} `+be.gray("\u2502"),be.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 c.forEach((e,s)=>{let n=`${s+1}`.padEnd(3),r=e.type.padEnd(14),i=(e.target||e.conditionTarget||"\u2014").slice(0,28).padEnd(28),o=(e.value||(e.durationMs?`${e.durationMs}ms`:e.expectedState)||"\u2014").slice(0,30).padEnd(30),a=(e.optional?"Yes":"No").padEnd(8);t.push(be.gray("\u2502")+` ${be.white(n)} `+be.gray("\u2502")+` ${be.cyan(r)} `+be.gray("\u2502")+` ${be.yellow(i)} `+be.gray("\u2502")+` ${be.white(o)} `+be.gray("\u2502")+` ${be.gray(a)} `+be.gray("\u2502"))}),t.push(be.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
32
+ \u{1F4A1} No similar text found on current screen.`;return new Error(`${e}${i}`)}function Mr(a,t,e,s){let n=pe(t),i=n.includes("password")||n.includes("passcode")||n.includes("pin")||n.includes("pwd")||n.includes("secret"),r=n.includes("email")||n.includes("mail")||n.includes("user")||n.includes("login")||n.includes("username")||e&&e.includes("@"),o=a.filter(d=>d.className.includes("EditText")&&d.bounds.width>0);if(o.length===0)return null;let l=o.find(d=>me(t,d.contentDesc)||me(t,d.resourceId)||me(t,d.text));if(l)return l;let c=a.filter(d=>!d.className.includes("EditText")&&(me(t,d.text)||me(t,d.contentDesc)||me(t,d.resourceId))&&d.bounds.width>0);for(let d of c){let p=o.filter(u=>u.bounds.centerY>=d.bounds.centerY&&u.bounds.centerY-d.bounds.centerY<300&&Math.abs(u.bounds.centerX-d.bounds.centerX)<450);if(p.length>0)return p.sort((u,h)=>u.bounds.centerY-h.bounds.centerY),p[0]}if(s){let d=s.getAllCandidates("TYPE",t);for(let p of d){let u=o.find(h=>me(p,h.contentDesc)||me(p,h.resourceId)||me(p,h.text));if(u)return u}}if(i){let d=o.find(p=>p.password);if(d)return d;if(o.length>1){let p=o.find(u=>u.text?.toLowerCase().includes("email")||u.contentDesc?.toLowerCase().includes("email"));if(p){let u=o.filter(h=>h.bounds.centerY>p.bounds.centerY);if(u.length>0)return u.sort((h,m)=>h.bounds.centerY-m.bounds.centerY),u[0]}}if(o.length>=2)return o[1]}if(r){let d=o.find(p=>!p.password);if(d)return d;if(o.length>0)return o[0]}return o.length===1?o[0]:null}function Ss(a,t,e,s="en",n,i){let r=n?.type||"TAP",o=pe(t),c=a.filter(h=>(pe(h.text)===o||pe(h.contentDesc)===o)&&h.bounds.width>0&&h.bounds.height>0).find(h=>h.clickable);if(c)return[c];let d=e.getAllCandidates(r,t,s,i),p=[];for(let h of d){let m=a.filter(b=>{let T=me(h,b.text),w=me(h,b.contentDesc),S=me(h,b.resourceId);return(T||w||S)&&b.bounds.width>0&&b.bounds.height>0});if(m.length>0){p=m;break}}if(p.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 m=Gt(t,h,1);if(m.length>0&&m[0].score>=.8){let b=m[0].text,T=a.filter(w=>(me(b,w.text)||me(b,w.contentDesc))&&w.bounds.width>0&&w.bounds.height>0);T.length>0&&(console.log(we.yellow(` \u{1F4A1} [Self-Healing] Auto-resolved "${t}" \u2192 "${b}" (${Math.round(m[0].score*100)}% match)`)),p=T)}}if(p.length===0)return[];let u=p.filter(h=>{let m=pe(h.text),b=pe(h.contentDesc);return m===o||b===o});if(u.length>0)p=u;else{let h=p.filter(m=>{let b=pe(m.contentDesc);return b.endsWith(o)||b.includes(`, ${o}`)});h.length>0&&(p=h)}if(n?.relativeTo){let h=a.find(m=>me(n.relativeTo,m.text)||me(n.relativeTo,m.contentDesc));if(h){let m=p.filter(b=>Math.abs(b.bounds.centerY-h.bounds.centerY)<140).sort((b,T)=>{let w=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 w-S});if(m.length>0)return m}}if(n?.ordinal){let h=n.ordinal.toLowerCase();if(h==="1st"||h==="first")return[p[0]];if(h==="2nd"&&p.length>1)return[p[1]];if(h==="3rd"&&p.length>2)return[p[2]];if(h==="4th"&&p.length>3)return[p[3]];if(h==="5th"&&p.length>4)return[p[4]];if(h==="last"&&p.length>0)return[p[p.length-1]]}return p}function ke(a){if(!a||a.length===0)return"0";let t=2166136261;for(let e=0;e<a.length;e++){let s=a[e],n=`${s.text||""}|${s.contentDesc||""}|${s.resourceId||""}|${s.bounds?.x1||0},${s.bounds?.y1||0},${s.bounds?.width||0},${s.bounds?.height||0}`;for(let i=0;i<n.length;i++)t^=n.charCodeAt(i),t=Math.imul(t,16777619)}return(t>>>0).toString(16)}function Lr(a){if(!a)return"";let t=a;for(let e of ao)t=t.replace(e,"<VAL>");return t.trim()}function Ke(a,t){if(!a||a.length===0)return"0";let s=(t?.height||2400)*.04,n=2166136261;for(let i=0;i<a.length;i++){let r=a[i];if(r.bounds&&r.bounds.centerY<s)continue;let o=Lr(r.text||""),l=Lr(r.contentDesc||""),c=r.resourceId||"",d=r.className||"",p=r.clickable?"1":"0",u=r.checkable?"1":"0",h=Math.round((r.bounds?.x1||0)/50),m=Math.round((r.bounds?.y1||0)/50),b=Math.round((r.bounds?.width||0)/50),T=Math.round((r.bounds?.height||0)/50),w=`${d}|${c}|${o}|${l}|${p}|${u}|${h},${m},${b},${T}`;for(let S=0;S<w.length;S++)n^=w.charCodeAt(S),n=Math.imul(n,16777619)}return(n>>>0).toString(16)}function Fr(a){let t=new Set;if(!a)return t;let e=a.split(",").map(s=>s.trim()).filter(Boolean);for(let s of e)if(s.includes("-")){let[n,i]=s.split("-").map(l=>l.trim()),r=parseInt(n,10),o=parseInt(i,10);if(!isNaN(r)&&!isNaN(o)){let[l,c]=r<=o?[r,o]:[o,r];for(let d=l;d<=c;d++)t.add(d)}}else{let n=parseInt(s,10);isNaN(n)||t.add(n)}return t}function Or(a){let t=new Set,e=a.split(`
33
+ `);for(let s of e){let n=s.trim();if(!n.startsWith("#"))continue;let i=n.match(/#\s*@tags?:\s*(.+)/i);if(i){i[1].split(",").map(o=>o.trim().toLowerCase()).filter(Boolean).forEach(o=>t.add(o));continue}let r=n.match(/#\s*@([a-zA-Z0-9_-]+)/);r&&r[1].toLowerCase()!=="tags"&&r[1].toLowerCase()!=="tag"&&t.add(r[1].toLowerCase())}return Array.from(t)}function _r(a){let t=["",we.bold.cyan(`\u{1F4CB} Parsed Test Steps (${a.length} total):`),we.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"),we.gray("\u2502")+` ${we.bold("#".padEnd(3))} `+we.gray("\u2502")+` ${we.bold("Type".padEnd(14))} `+we.gray("\u2502")+` ${we.bold("Target".padEnd(28))} `+we.gray("\u2502")+` ${we.bold("Value / Detail".padEnd(30))} `+we.gray("\u2502")+` ${we.bold("Optional".padEnd(8))} `+we.gray("\u2502"),we.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((e,s)=>{let n=`${s+1}`.padEnd(3),i=e.type.padEnd(14),r=(e.target||e.conditionTarget||"\u2014").slice(0,28).padEnd(28),o=(e.value||(e.durationMs?`${e.durationMs}ms`:e.expectedState)||"\u2014").slice(0,30).padEnd(30),l=(e.optional?"Yes":"No").padEnd(8);t.push(we.gray("\u2502")+` ${we.white(n)} `+we.gray("\u2502")+` ${we.cyan(i)} `+we.gray("\u2502")+` ${we.yellow(r)} `+we.gray("\u2502")+` ${we.white(o)} `+we.gray("\u2502")+` ${we.gray(l)} `+we.gray("\u2502"))}),t.push(we.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
34
34
  `)),t.join(`
35
- `)}function gs(c,t,e=[],s="SKIPPED",n="Validation"){let r=new Date().toISOString(),i=s==="PASS";return{packageName:c||"none",deviceId:t||"none",startTime:r,endTime:r,durationSeconds:0,totalSteps:e.length,passedSteps:i?e.length:0,failedSteps:0,skippedSteps:i?0:e.length,warnSteps:0,errorsDetected:[],steps:e.map((o,a)=>({stepIndex:a+1,timestamp:r,phase:n,description:o.originalText,action:o.type,target:o.target,status:s,durationMs:0}))}}var or,ut=de(()=>{"use strict";kt();Vt();st();or=[/\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]});import ai from"fs";import ar from"path";var It,ci,ns=de(()=>{"use strict";Fe();Ke();st();Be();It=class{testData={};dataPath;isLoaded=!1;driver;constructor(t,e){this.dataPath=t||ar.resolve(process.cwd(),"test-data.json"),this.driver=e||re}setTestData(t){this.testData=t,this.isLoaded=!0}async ensureDataLoaded(){if(!this.isLoaded){if(ai.existsSync(this.dataPath))try{let t=await ai.promises.readFile(this.dataPath,"utf-8");this.testData=JSON.parse(t)}catch{this.testData={}}this.isLoaded=!0}}async loadTestData(t){return t&&(this.dataPath=t),this.isLoaded=!1,await this.ensureDataLoaded(),this.testData}inferValueForNode(t,e){let s=[t.text,t.contentDesc,t.resourceId].join(" ").toLowerCase(),n="",r=e.filter(o=>o.text&&!o.clickable&&o.bounds.centerY<t.bounds.centerY&&t.bounds.centerY-o.bounds.centerY<250&&Math.abs(o.bounds.centerX-t.bounds.centerX)<400);r.length>0&&(n=r[r.length-1].text.toLowerCase());let i=`${s} ${n}`.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 a="4532";for(let l=0;l<12;l++)a+=Math.floor(Math.random()*10);return a}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(t.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(t,e,s=!0,n=!1){await this.ensureDataLoaded();let r=je(t),i=[];if(r.length===0)return{filledFields:[],submitted:!1};let o=!1;for(let d of r){let p=d.resourceId||d.text||`input_${d.bounds.centerX}_${d.bounds.centerY}`,h=(d.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(!n&&!u){i.push({fieldId:p,fieldLabel:d.text||d.contentDesc||d.resourceId,valueEntered:h});continue}let m=this.inferValueForNode(d,t);await this.driver.tap(d.bounds.centerX,d.bounds.centerY,e),await new Promise(f=>setTimeout(f,400)),await this.driver.clearField(e,m.length+10),await new Promise(f=>setTimeout(f,200)),await this.driver.inputText(m,e),await new Promise(f=>setTimeout(f,300)),o=!0,i.push({fieldId:p,fieldLabel:d.text||d.contentDesc||d.resourceId,valueEntered:m})}o&&(await this.driver.pressKey(4,e),await new Promise(d=>setTimeout(d,500)));let a=!1,l;if(s){let d=t.filter(p=>!p.clickable||p.bounds.width===0?!1:Jn.some(h=>me(h,p.text)||me(h,p.contentDesc)||me(h,p.resourceId)));if(d.length>0){let p=["submit","sign in","signin","login","save","confirm","proceed","continue"];d.sort((u,m)=>{let f=(u.text||u.contentDesc||u.resourceId||"").toLowerCase(),x=(m.text||m.contentDesc||m.resourceId||"").toLowerCase(),v=p.findIndex(C=>f.includes(C)),S=p.findIndex(C=>x.includes(C)),y=v>=0?v:99,k=S>=0?S:99;return y!==k?y-k:m.bounds.centerY-u.bounds.centerY});let h=d[0];l=h.text||h.contentDesc||h.resourceId,await this.driver.tap(h.bounds.centerX,h.bounds.centerY,e),await new Promise(u=>setTimeout(u,I.settle.submitWaitMs));try{let u=await this.driver.getUiHierarchy(e),{flat:m}=ve(u),f=je(m);a=!(f.length>0&&r.every(v=>f.some(S=>S.resourceId&&S.resourceId===v.resourceId)))}catch{a=!0}}}return{filledFields:i,submitted:a,submitButtonText:l}}},ci=new It});import at from"fs";import ht from"path";function Os(c,t){let e=t?.suiteName||"PromptTest Suite",s=c.failedSteps===0&&c.errorsDetected.length===0,n=s?"ALL PASSED":"ISSUES DETECTED",r=s?"#10b981":"#ef4444",i=c.totalSteps,o=i>0?Math.round(c.passedSteps/i*100):100,a=u=>(u||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;"),l=[],d=c.steps.map(u=>{let m=u.status.toLowerCase(),f=u.durationMs!==void 0?`${u.durationMs}ms`:"\u2014",x=t?.screenshotResolver?t.screenshotResolver(u):u.screenshotPath?`./${u.screenshotPath}`:void 0;x&&l.push({stepIndex:u.stepIndex,action:u.action,description:u.description,status:u.status,src:x,durationMs:u.durationMs});let v="\u2014";if(u.triageBundle){let $=a(u.triageBundle),J=u.triageBundle.endsWith(".zip"),O=J?$:`${$}.zip`,V=J?$.replace(/\.zip$/,""):$;v=`
35
+ `)}function ws(a,t,e=[],s="SKIPPED",n="Validation"){let i=new Date().toISOString(),r=s==="PASS";return{packageName:a||"none",deviceId:t||"none",startTime:i,endTime:i,durationSeconds:0,totalSteps:e.length,passedSteps:r?e.length:0,failedSteps:0,skippedSteps:r?0:e.length,warnSteps:0,errorsDetected:[],steps:e.map((o,l)=>({stepIndex:l+1,timestamp:i,phase:n,description:o.originalText,action:o.type,target:o.target,status:s,durationMs:0}))}}async function Hr(a){let{outputDir:t,specName:e,currentSpecName:s,stepNum:n,step:i,error:r,stepStartTime:o,startTime:l,errorShot:c,lastHierarchy:d,driver:p,serial:u}=a;try{let h=`${e}_step_${n}`,m=Je.join(t,"failures",h);dt.existsSync(m)||dt.mkdirSync(m,{recursive:!0}),c&&await dt.promises.writeFile(Je.join(m,"screenshot.png"),c);let b=d?.xml||await p.getUiHierarchy(u).catch(()=>"");b&&await dt.promises.writeFile(Je.join(m,"hierarchy.xml"),b,"utf-8");let T=await p.getLogcatTail(150,u);await dt.promises.writeFile(Je.join(m,"logcat_tail.log"),T,"utf-8");let w=d?.flat||[],S=i.target&&w.length>0?Gt(i.target,w.map(U=>U.text||U.contentDesc||"").filter(Boolean)):[];await dt.promises.writeFile(Je.join(m,"match_scores.json"),JSON.stringify(S,null,2),"utf-8");let y={specName:s,stepNumber:n,stepType:i.type,target:i.target||i.value,error:r.message,timestamp:new Date().toISOString(),videoOffsetMs:Math.max(0,o-l)};await dt.promises.writeFile(Je.join(m,"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 N=`${h}.zip`,O=Je.join(t,"failures",N);await Ir(O,P);let L=Je.join("failures",N).replace(/\\/g,"/");return console.log(we.red(` \u{1F4C1} Failure Triage ZIP Bundle generated at: output/${L}`)),L}catch{return}}var ao,et=ie(()=>{"use strict";Nt();Qt();Nr();ot();ao=[/\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]});import Br from"fs";import co from"path";var Mt,Ur,ls=ie(()=>{"use strict";Re();Ve();ot();je();Mt=class{testData={};dataPath;isLoaded=!1;driver;constructor(t,e){this.dataPath=t||co.resolve(process.cwd(),"test-data.json"),this.driver=e||oe}setTestData(t){this.testData=t,this.isLoaded=!0}async ensureDataLoaded(){if(!this.isLoaded){if(Br.existsSync(this.dataPath))try{let t=await Br.promises.readFile(this.dataPath,"utf-8");this.testData=JSON.parse(t)}catch{this.testData={}}this.isLoaded=!0}}async loadTestData(t){return t&&(this.dataPath=t),this.isLoaded=!1,await this.ensureDataLoaded(),this.testData}inferValueForNode(t,e){let s=[t.text,t.contentDesc,t.resourceId].join(" ").toLowerCase(),n="",i=e.filter(o=>o.text&&!o.clickable&&o.bounds.centerY<t.bounds.centerY&&t.bounds.centerY-o.bounds.centerY<250&&Math.abs(o.bounds.centerX-t.bounds.centerX)<400);i.length>0&&(n=i[i.length-1].text.toLowerCase());let r=`${s} ${n}`.toLowerCase();if(r.includes("review")||r.includes("feedback")||r.includes("comment")||r.includes("write a review")||r.includes("experience")){if(!this.testData.review?.text)throw new Error("Missing test-data.json field: review.text");return this.testData.review.text}if(r.includes("card")||r.includes("credit")||r.includes("debit")||r.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(r.includes("phone")||r.includes("mobile")||!r.includes("card")&&r.includes("number")||r.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(r.includes("email")||r.includes("e-mail")||r.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(r.includes("cvv")||r.includes("cvc")||r.includes("security code"))return"123";if(r.includes("expiry")||r.includes("exp date")||r.includes("expiration"))return"12/28";if(r.includes("pincode")||r.includes("zip")||r.includes("postal")){if(!this.testData.profile?.pincode)throw new Error("Missing test-data.json field: profile.pincode");return this.testData.profile.pincode}if(t.password||r.includes("password")||/\bpin\b/.test(r)&&!r.includes("pincode")||r.includes("passcode")){if(!this.testData.auth?.password)throw new Error("Missing test-data.json field: auth.password");return this.testData.auth.password}if(r.includes("otp")||r.includes("verification")||r.includes("code")&&!r.includes("postal")&&!r.includes("zip")){if(!this.testData.auth?.otp)throw new Error("Missing test-data.json field: auth.otp");return this.testData.auth.otp}if(r.includes("address")||r.includes("street")||r.includes("location")){if(!this.testData.profile?.address)throw new Error("Missing test-data.json field: profile.address");return this.testData.profile.address}if(r.includes("city")||r.includes("state")){if(!this.testData.profile?.city)throw new Error("Missing test-data.json field: profile.city");return this.testData.profile.city}if(r.includes("name")||r.includes("full name")||r.includes("first name")){if(!this.testData.profile?.name)throw new Error("Missing test-data.json field: profile.name");return this.testData.profile.name}if(r.includes("search")||r.includes("find")||r.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(t,e,s=!0,n=!1){await this.ensureDataLoaded();let i=Fe(t),r=[];if(i.length===0)return{filledFields:[],submitted:!1};let o=!1;for(let d of i){let p=d.resourceId||d.text||`input_${d.bounds.centerX}_${d.bounds.centerY}`,u=(d.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(!n&&!h){r.push({fieldId:p,fieldLabel:d.text||d.contentDesc||d.resourceId,valueEntered:u});continue}let m=this.inferValueForNode(d,t);await this.driver.tap(d.bounds.centerX,d.bounds.centerY,e),await new Promise(b=>setTimeout(b,400)),await this.driver.clearField(e,m.length+10),await new Promise(b=>setTimeout(b,200)),await this.driver.inputText(m,e),await new Promise(b=>setTimeout(b,300)),o=!0,r.push({fieldId:p,fieldLabel:d.text||d.contentDesc||d.resourceId,valueEntered:m})}o&&(await this.driver.pressKey(4,e),await new Promise(d=>setTimeout(d,500)));let l=!1,c;if(s){let d=t.filter(p=>!p.clickable||p.bounds.width===0?!1:Tr.some(u=>me(u,p.text)||me(u,p.contentDesc)||me(u,p.resourceId)));if(d.length>0){let p=["submit","sign in","signin","login","save","confirm","proceed","continue"];d.sort((h,m)=>{let b=(h.text||h.contentDesc||h.resourceId||"").toLowerCase(),T=(m.text||m.contentDesc||m.resourceId||"").toLowerCase(),w=p.findIndex(N=>b.includes(N)),S=p.findIndex(N=>T.includes(N)),y=w>=0?w:99,P=S>=0?S:99;return y!==P?y-P:m.bounds.centerY-h.bounds.centerY});let u=d[0];c=u.text||u.contentDesc||u.resourceId,await this.driver.tap(u.bounds.centerX,u.bounds.centerY,e),await new Promise(h=>setTimeout(h,R.settle.submitWaitMs));try{let h=await this.driver.getUiHierarchy(e),{flat:m}=be(h),b=Fe(m);l=!(b.length>0&&i.every(w=>b.some(S=>S.resourceId&&S.resourceId===w.resourceId)))}catch{l=!0}}}return{filledFields:r,submitted:l,submitButtonText:c}}},Ur=new Mt});var qs={};Qe(qs,{activateLicense:()=>uo,deactivateLicense:()=>ho,generateLicenseKey:()=>po,getLicenseInfo:()=>xs,verifyLicenseKey:()=>Zs});import wt from"fs";import ds from"path";import jr from"crypto";import lo from"os";function Qs(){let a=ds.resolve(process.cwd(),".prompttest");if(wt.existsSync(a))return ds.join(a,"license.json");let t=ds.join(lo.homedir(),".prompttest");if(!wt.existsSync(t))try{wt.mkdirSync(t,{recursive:!0})}catch{return ds.resolve(process.cwd(),".prompttest_license.json")}return ds.join(t,"license.json")}function po(a,t=vs){let e=JSON.stringify(a),s=Buffer.from(e,"utf-8").toString("base64url"),n=jr.createHmac("sha256",t);n.update(s);let i=n.digest("hex").slice(0,32);return`PTPRO-${s}.${i}`}function Zs(a,t=vs){if(!a||typeof a!="string")return{valid:!1,error:"License key is empty."};let e=a.trim();if(!e.startsWith("PTPRO-")||!e.includes("."))return{valid:!1,error:"Invalid license key format. Expected PTPRO-<payload>.<signature>"};let n=e.slice(6).split(".");if(n.length!==2)return{valid:!1,error:"Corrupted license key structure."};let[i,r]=n,o=jr.createHmac("sha256",t);o.update(i);let l=o.digest("hex").slice(0,32);if(r!==l)return{valid:!1,error:"Invalid license signature or altered key."};try{let c=Buffer.from(i,"base64url").toString("utf-8"),d=JSON.parse(c);return d.expiresAt>0&&Date.now()>d.expiresAt?{valid:!1,payload:d,error:`License expired on ${new Date(d.expiresAt).toLocaleDateString()}.`}:{valid:!0,payload:d}}catch{return{valid:!1,error:"Failed to decode license payload."}}}function uo(a,t=vs){let e=Zs(a,t);if(!e.valid||!e.payload)return{success:!1,message:e.error||"License activation failed."};try{let s=Qs(),n={key:a.trim(),payload:e.payload,activatedAt:new Date().toISOString()};return wt.writeFileSync(s,JSON.stringify(n,null,2),"utf-8"),{success:!0,message:`Successfully activated ${e.payload.tier.toUpperCase()} license for ${e.payload.holder}!`,payload:e.payload}}catch(s){return{success:!1,message:`Failed to persist license file: ${s instanceof Error?s.message:String(s)}`}}}function xs(a=vs){try{let t=Qs();if(!wt.existsSync(t))return{active:!1,tier:"free",whiteLabel:!1,features:[]};let e=wt.readFileSync(t,"utf-8"),s=JSON.parse(e),n=Zs(s.key,a);return!n.valid||!n.payload?{active:!1,tier:"free",whiteLabel:!1,features:[]}:{active:!0,tier:n.payload.tier,holder:n.payload.holder,expiresAt:n.payload.expiresAt>0?new Date(n.payload.expiresAt).toISOString():"Perpetual",whiteLabel:n.payload.whiteLabel,features:n.payload.features||[]}}catch{return{active:!1,tier:"free",whiteLabel:!1,features:[]}}}function ho(){try{let a=Qs();return wt.existsSync(a)&&wt.unlinkSync(a),!0}catch{return!1}}var vs,Ts=ie(()=>{"use strict";vs="prompttest_ed25519_offline_sig_k9284j2f91kd"});import pt from"fs";import vt from"path";function en(a,t){let e=t?.suiteName||"PromptTest Suite",s=a.failedSteps===0&&a.errorsDetected.length===0,n=s?"ALL PASSED":"ISSUES DETECTED",i=s?"#10b981":"#ef4444",r=a.totalSteps,o=r>0?Math.round(a.passedSteps/r*100):100,l=xs(),c=m=>(m||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;"),d=[],p=a.steps.map(m=>{let b=m.status.toLowerCase(),T=m.durationMs!==void 0?`${m.durationMs}ms`:"\u2014",w=t?.screenshotResolver?t.screenshotResolver(m):m.screenshotPath?`./${m.screenshotPath}`:void 0;w&&d.push({stepIndex:m.stepIndex,action:m.action,description:m.description,status:m.status,src:w,durationMs:m.durationMs});let S="\u2014";if(m.triageBundle){let U=c(m.triageBundle),j=m.triageBundle.endsWith(".zip"),Q=j?U:`${U}.zip`,K=j?U.replace(/\.zip$/,""):U;S=`
36
36
  <div class="triage-bundle-links">
37
- <a class="btn-triage btn-download-zip" href="./${O}" download title="Download Diagnostic Triage ZIP Package">\u{1F4E6} Download ZIP</a>
38
- <a class="btn-triage" href="./${V}" target="_blank" title="Browse Failure Diagnostic Directory">\u{1F50D} Triage Folder</a>
37
+ <a class="btn-triage btn-download-zip" href="./${Q}" download title="Download Diagnostic Triage ZIP Package">\u{1F4E6} Download ZIP</a>
38
+ <a class="btn-triage" href="./${K}" target="_blank" title="Browse Failure Diagnostic Directory">\u{1F50D} Triage Folder</a>
39
39
  <div class="triage-files">
40
- <a href="./${V}/screenshot.png" target="_blank" class="mini-link">\u{1F4F8} Shot</a>
41
- <a href="./${V}/hierarchy.xml" target="_blank" class="mini-link">\u{1F4C4} XML</a>
42
- <a href="./${V}/logcat_tail.log" target="_blank" class="mini-link">\u{1F4DC} Logcat</a>
43
- <a href="./${V}/match_scores.json" target="_blank" class="mini-link">\u{1F3AF} Scores</a>
40
+ <a href="./${K}/screenshot.png" target="_blank" class="mini-link">\u{1F4F8} Shot</a>
41
+ <a href="./${K}/hierarchy.xml" target="_blank" class="mini-link">\u{1F4C4} XML</a>
42
+ <a href="./${K}/logcat_tail.log" target="_blank" class="mini-link">\u{1F4DC} Logcat</a>
43
+ <a href="./${K}/match_scores.json" target="_blank" class="mini-link">\u{1F3AF} Scores</a>
44
44
  </div>
45
- </div>`}else x&&(v=`
45
+ </div>`}else w&&(S=`
46
46
  <div class="screenshot-cell">
47
- <img src="${x}" class="thumb" alt="Step ${u.stepIndex}" onclick="openLightboxByStep(${u.stepIndex})" title="Click to view in full-screen Lightbox"/>
48
- <button class="btn-zoom" onclick="openLightboxByStep(${u.stepIndex})" title="Enlarge Screenshot">\u{1F50D}</button>
49
- </div>`);let S="";if(u.details&&(typeof u.details.diffPercent=="number"||u.details.diffPath||u.details.baselinePath)){let $=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=`
47
+ <img src="${w}" class="thumb" alt="Step ${m.stepIndex}" onclick="openLightboxByStep(${m.stepIndex})" title="Click to view in full-screen Lightbox"/>
48
+ <button class="btn-zoom" onclick="openLightboxByStep(${m.stepIndex})" title="Enlarge Screenshot">\u{1F50D}</button>
49
+ </div>`);let y="";if(m.details&&(typeof m.details.diffPercent=="number"||m.details.diffPath||m.details.baselinePath)){let U=typeof m.details.diffPercent=="number"?`${(m.details.diffPercent*100).toFixed(2)}%`:"N/A",j=typeof m.details.threshold=="number"?`${(m.details.threshold*100).toFixed(2)}%`:"2.00%";y=`
50
50
  <div class="diff-viewer-card">
51
51
  <div class="diff-header">
52
- <span class="badge diff-badge">Visual Diff: ${a($)}</span>
53
- <span class="diff-meta">Threshold: ${a(J)}</span>
52
+ <span class="badge diff-badge">Visual Diff: ${c(U)}</span>
53
+ <span class="diff-meta">Threshold: ${c(j)}</span>
54
54
  </div>
55
55
  <div class="diff-grid">
56
- ${u.details.baselinePath?`<div class="diff-pane"><div class="pane-label">Baseline</div><img src="./${a(String(u.details.baselinePath))}" class="diff-thumb" onclick="openLightboxCustom('./${a(String(u.details.baselinePath))}', 'Baseline Screenshot')"/></div>`:""}
57
- ${x?`<div class="diff-pane"><div class="pane-label">Actual Screen</div><img src="${x}" class="diff-thumb" onclick="openLightboxCustom('${x}', 'Actual Screenshot')"/></div>`:""}
58
- ${u.details.diffPath?`<div class="diff-pane"><div class="pane-label">Pixel Diff Overlay</div><img src="./${a(String(u.details.diffPath))}" class="diff-thumb" onclick="openLightboxCustom('./${a(String(u.details.diffPath))}', 'Visual Difference Overlay')"/></div>`:""}
56
+ ${m.details.baselinePath?`<div class="diff-pane"><div class="pane-label">Baseline</div><img src="./${c(String(m.details.baselinePath))}" class="diff-thumb" onclick="openLightboxCustom('./${c(String(m.details.baselinePath))}', 'Baseline Screenshot')"/></div>`:""}
57
+ ${w?`<div class="diff-pane"><div class="pane-label">Actual Screen</div><img src="${w}" class="diff-thumb" onclick="openLightboxCustom('${w}', 'Actual Screenshot')"/></div>`:""}
58
+ ${m.details.diffPath?`<div class="diff-pane"><div class="pane-label">Pixel Diff Overlay</div><img src="./${c(String(m.details.diffPath))}" class="diff-thumb" onclick="openLightboxCustom('./${c(String(m.details.diffPath))}', 'Visual Difference Overlay')"/></div>`:""}
59
59
  </div>
60
- </div>`}let y="";u.details&&Object.keys(u.details).length>0&&(y=`
60
+ </div>`}let P="";m.details&&Object.keys(m.details).length>0&&(P=`
61
61
  <details class="step-details">
62
62
  <summary>Diagnostics &amp; Context</summary>
63
- <pre>${a(JSON.stringify(u.details,null,2))}</pre>
64
- </details>`);let k=`${u.stepIndex} ${u.phase} ${u.action} ${u.description} ${u.target||""}`.toLowerCase(),C=typeof u.videoOffsetMs=="number"?u.videoOffsetMs:typeof u.details?.videoOffsetMs=="number"?u.details.videoOffsetMs:void 0,j=c.videoPath&&C!==void 0?`<button type="button" class="btn-video-jump" onclick="seekVideoTo(${C/1e3})" title="Jump video to ${(C/1e3).toFixed(1)}s">\u25B6 ${(C/1e3).toFixed(1)}s</button>`:"";return`
65
- <tr class="step-row status-${m}" data-status="${m}" data-search="${a(k)}">
66
- <td class="col-num">${u.stepIndex}</td>
67
- <td><span class="badge phase-badge">${a(u.phase)}</span></td>
63
+ <pre>${c(JSON.stringify(m.details,null,2))}</pre>
64
+ </details>`);let N=`${m.stepIndex} ${m.phase} ${m.action} ${m.description} ${m.target||""}`.toLowerCase(),O=typeof m.videoOffsetMs=="number"?m.videoOffsetMs:typeof m.details?.videoOffsetMs=="number"?m.details.videoOffsetMs:void 0,L=a.videoPath&&O!==void 0?`<button type="button" class="btn-video-jump" onclick="seekVideoTo(${O/1e3})" title="Jump video to ${(O/1e3).toFixed(1)}s">\u25B6 ${(O/1e3).toFixed(1)}s</button>`:"";return`
65
+ <tr class="step-row status-${b}" data-status="${b}" data-search="${c(N)}">
66
+ <td class="col-num">${m.stepIndex}</td>
67
+ <td><span class="badge phase-badge">${c(m.phase)}</span></td>
68
68
  <td>
69
- <span class="badge action-badge">${a(u.action)}</span>
70
- ${j}
69
+ <span class="badge action-badge">${c(m.action)}</span>
70
+ ${L}
71
71
  </td>
72
72
  <td>
73
- <div class="desc-text">${a(u.description)}</div>
74
- ${S}
73
+ <div class="desc-text">${c(m.description)}</div>
75
74
  ${y}
75
+ ${P}
76
76
  </td>
77
- <td><code>${a(u.target||"\u2014")}</code></td>
78
- <td><span class="badge status-badge status-${m}">${u.status}</span></td>
79
- <td class="col-duration">${f}</td>
80
- <td class="col-artifact">${v}</td>
77
+ <td><code>${c(m.target||"\u2014")}</code></td>
78
+ <td><span class="badge status-badge status-${b}">${m.status}</span></td>
79
+ <td class="col-duration">${T}</td>
80
+ <td class="col-artifact">${S}</td>
81
81
  </tr>`}).join(`
82
- `),p=c.videoPath?`
82
+ `),u=a.videoPath?`
83
83
  <section class="card video-card">
84
84
  <div class="video-card-header">
85
85
  <h3>\u{1F3AC} Session Screen Recording</h3>
@@ -91,12 +91,12 @@ ${(await this.execAdb(["shell","wm","density"],t,8e3,0).catch(()=>({stdout:""}))
91
91
  <button type="button" class="btn-speed" onclick="setPlaybackSpeed(2)">2x</button>
92
92
  </div>
93
93
  </div>
94
- <video id="session-recording-player" controls preload="metadata" class="recording-player" src="./${a(c.videoPath)}"></video>
95
- </section>`:"",h=c.errorsDetected.length>0?`
94
+ <video id="session-recording-player" controls preload="metadata" class="recording-player" src="./${c(a.videoPath)}"></video>
95
+ </section>`:"",h=a.errorsDetected.length>0?`
96
96
  <section class="card error-card">
97
- <h3>\u26A0\uFE0F Critical Flagged Errors (${c.errorsDetected.length})</h3>
97
+ <h3>\u26A0\uFE0F Critical Flagged Errors (${a.errorsDetected.length})</h3>
98
98
  <ul class="error-list">
99
- ${c.errorsDetected.map(u=>`<li>\u{1F534} <code>${a(u)}</code></li>`).join(`
99
+ ${a.errorsDetected.map(m=>`<li>\u{1F534} <code>${c(m)}</code></li>`).join(`
100
100
  `)}
101
101
  </ul>
102
102
  </section>`:"";return`<!DOCTYPE html>
@@ -104,7 +104,7 @@ ${(await this.execAdb(["shell","wm","density"],t,8e3,0).catch(()=>({stdout:""}))
104
104
  <head>
105
105
  <meta charset="UTF-8">
106
106
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
107
- <title>PromptTest QA Audit: ${a(e)}</title>
107
+ <title>PromptTest QA Audit: ${c(e)}</title>
108
108
  <style>
109
109
  :root {
110
110
  --bg: #0b0f19;
@@ -147,7 +147,7 @@ ${(await this.execAdb(["shell","wm","density"],t,8e3,0).catch(()=>({stdout:""}))
147
147
  padding: 8px 18px;
148
148
  border-radius: 9999px;
149
149
  color: #fff;
150
- background: ${r};
150
+ background: ${i};
151
151
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
152
152
  }
153
153
 
@@ -550,10 +550,10 @@ ${(await this.execAdb(["shell","wm","density"],t,8e3,0).catch(()=>({stdout:""}))
550
550
  <div class="header-title">
551
551
  <h1>PromptTest QA Audit Report</h1>
552
552
  <div class="header-meta">
553
- <strong>Suite:</strong> <code>${a(e)}</code> &bull;
554
- <strong>App:</strong> <code>${a(c.packageName)}</code> &bull;
555
- <strong>Device:</strong> <code>${a(c.deviceId)}</code> &bull;
556
- <strong>Duration:</strong> ${c.durationSeconds}s (${c.startTime.substring(11,19)} to ${c.endTime.substring(11,19)})
553
+ <strong>Suite:</strong> <code>${c(e)}</code> &bull;
554
+ <strong>App:</strong> <code>${c(a.packageName)}</code> &bull;
555
+ <strong>Device:</strong> <code>${c(a.deviceId)}</code> &bull;
556
+ <strong>Duration:</strong> ${a.durationSeconds}s (${a.startTime.substring(11,19)} to ${a.endTime.substring(11,19)})
557
557
  </div>
558
558
  </div>
559
559
  <div class="status-pill">${n}</div>
@@ -563,44 +563,44 @@ ${(await this.execAdb(["shell","wm","density"],t,8e3,0).catch(()=>({stdout:""}))
563
563
  <div class="gauge-card">
564
564
  <svg viewBox="0 0 36 36" class="circular-chart">
565
565
  <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"/>
566
- <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"/>
566
+ <path class="circle" stroke-dasharray="${o}, 100" stroke="${i}" 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"/>
567
567
  <text x="18" y="20.35" class="percentage">${o}%</text>
568
568
  </svg>
569
569
  <div class="chart-label">Pass Rate</div>
570
570
  </div>
571
571
  <div class="stat-card">
572
572
  <div class="stat-label">Total Steps</div>
573
- <div class="stat-value">${c.totalSteps}</div>
573
+ <div class="stat-value">${a.totalSteps}</div>
574
574
  </div>
575
575
  <div class="stat-card">
576
576
  <div class="stat-label">Passed</div>
577
- <div class="stat-value val-pass">${c.passedSteps}</div>
577
+ <div class="stat-value val-pass">${a.passedSteps}</div>
578
578
  </div>
579
579
  <div class="stat-card">
580
580
  <div class="stat-label">Failures</div>
581
- <div class="stat-value val-fail">${c.failedSteps}</div>
581
+ <div class="stat-value val-fail">${a.failedSteps}</div>
582
582
  </div>
583
583
  <div class="stat-card">
584
584
  <div class="stat-label">Warnings</div>
585
- <div class="stat-value val-warn">${c.warnSteps}</div>
585
+ <div class="stat-value val-warn">${a.warnSteps}</div>
586
586
  </div>
587
587
  <div class="stat-card">
588
588
  <div class="stat-label">Skipped</div>
589
- <div class="stat-value val-skip">${c.skippedSteps}</div>
589
+ <div class="stat-value val-skip">${a.skippedSteps}</div>
590
590
  </div>
591
591
  </div>
592
592
 
593
- ${p}
593
+ ${u}
594
594
  ${h}
595
595
 
596
596
  <section class="card">
597
597
  <div class="toolbar">
598
598
  <div class="filter-bar">
599
- <button class="filter-btn active" data-filter="all" onclick="setFilter('all', this)">All (${c.totalSteps})</button>
600
- <button class="filter-btn" data-filter="pass" onclick="setFilter('pass', this)">Passed (${c.passedSteps})</button>
601
- <button class="filter-btn" data-filter="fail" onclick="setFilter('fail', this)">Failed (${c.failedSteps})</button>
602
- <button class="filter-btn" data-filter="warn" onclick="setFilter('warn', this)">Warnings (${c.warnSteps})</button>
603
- <button class="filter-btn" data-filter="skipped" onclick="setFilter('skipped', this)">Skipped (${c.skippedSteps})</button>
599
+ <button class="filter-btn active" data-filter="all" onclick="setFilter('all', this)">All (${a.totalSteps})</button>
600
+ <button class="filter-btn" data-filter="pass" onclick="setFilter('pass', this)">Passed (${a.passedSteps})</button>
601
+ <button class="filter-btn" data-filter="fail" onclick="setFilter('fail', this)">Failed (${a.failedSteps})</button>
602
+ <button class="filter-btn" data-filter="warn" onclick="setFilter('warn', this)">Warnings (${a.warnSteps})</button>
603
+ <button class="filter-btn" data-filter="skipped" onclick="setFilter('skipped', this)">Skipped (${a.skippedSteps})</button>
604
604
  </div>
605
605
  <div class="search-box">
606
606
  <input type="text" id="filter-search" placeholder="\u{1F50D} Search steps, actions, targets..." oninput="applyFilters()"/>
@@ -621,13 +621,43 @@ ${(await this.execAdb(["shell","wm","density"],t,8e3,0).catch(()=>({stdout:""}))
621
621
  </tr>
622
622
  </thead>
623
623
  <tbody id="steps-tbody">
624
- ${d}
624
+ ${p}
625
625
  </tbody>
626
626
  </table>
627
- </section>
627
+ ${a.performanceMetrics?`
628
+ <section class="card" style="margin-top: 24px; border: 1px solid #1e293b; background: #0f172a; border-radius: 12px; padding: 20px;">
629
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
630
+ <h2 style="font-size: 1.1rem; font-weight: 600; color: #f8fafc; margin: 0; display: flex; align-items: center; gap: 8px;">
631
+ \u26A1 Mobile Performance &amp; 60 FPS Profiler (PF-01)
632
+ </h2>
633
+ <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;">
634
+ ${a.performanceMetrics.fpsCompliance==="PASS"?"\u2714 60 FPS COMPLIANT":a.performanceMetrics.fpsCompliance==="WARN"?"\u26A0 JANK WARNING":"\u2716 SEVERE JANK"}
635
+ </span>
636
+ </div>
637
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px;">
638
+ <div style="background: #1e293b; padding: 12px; border-radius: 8px;">
639
+ <div style="font-size: 0.75rem; color: #94a3b8; text-transform: uppercase;">Total Frames</div>
640
+ <div style="font-size: 1.25rem; font-weight: 700; color: #f8fafc;">${a.performanceMetrics.frames.totalFrames.toLocaleString()}</div>
641
+ </div>
642
+ <div style="background: #1e293b; padding: 12px; border-radius: 8px;">
643
+ <div style="font-size: 0.75rem; color: #94a3b8; text-transform: uppercase;">Janky Frames</div>
644
+ <div style="font-size: 1.25rem; font-weight: 700; color: ${a.performanceMetrics.frames.jankPercentage>5?"#f87171":"#34d399"};">
645
+ ${a.performanceMetrics.frames.jankyFrames} (${a.performanceMetrics.frames.jankPercentage}%)
646
+ </div>
647
+ </div>
648
+ <div style="background: #1e293b; padding: 12px; border-radius: 8px;">
649
+ <div style="font-size: 0.75rem; color: #94a3b8; text-transform: uppercase;">95th Percentile</div>
650
+ <div style="font-size: 1.25rem; font-weight: 700; color: #f8fafc;">${a.performanceMetrics.frames.p95Ms!==void 0?a.performanceMetrics.frames.p95Ms+" ms":"\u2014"}</div>
651
+ </div>
652
+ <div style="background: #1e293b; padding: 12px; border-radius: 8px;">
653
+ <div style="font-size: 0.75rem; color: #94a3b8; text-transform: uppercase;">Peak PSS Memory</div>
654
+ <div style="font-size: 1.25rem; font-weight: 700; color: #38bdf8;">${Math.round(a.performanceMetrics.memory.peakPssKb/1024)} MB</div>
655
+ </div>
656
+ </div>
657
+ </section>`:""}
628
658
 
629
659
  <footer>
630
- PromptTest Autonomous Mobile QA Brain &bull; Rendered at ${new Date().toISOString()}
660
+ ${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>`}
631
661
  </footer>
632
662
  </div>
633
663
 
@@ -655,7 +685,7 @@ ${(await this.execAdb(["shell","wm","density"],t,8e3,0).catch(()=>({stdout:""}))
655
685
  </div>
656
686
 
657
687
  <script>
658
- const SCREENSHOTS = ${JSON.stringify(l)};
688
+ const SCREENSHOTS = ${JSON.stringify(d)};
659
689
  let currentLightboxIdx = -1;
660
690
  let currentFilter = 'all';
661
691
 
@@ -787,28 +817,29 @@ ${(await this.execAdb(["shell","wm","density"],t,8e3,0).catch(()=>({stdout:""}))
787
817
  }
788
818
  </script>
789
819
  </body>
790
- </html>`}var Nt,gt,mt=de(()=>{"use strict";Nt=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(t,e){typeof t=="object"&&t!==null?(this.outputDir=t.outputDir||ht.resolve(process.cwd(),"output"),t.formats&&t.formats.length>0&&(this.formats=t.formats),this.embedScreenshots=!!t.embedScreenshots):(this.outputDir=t||ht.resolve(process.cwd(),"output"),e&&e.length>0&&(this.formats=e)),at.existsSync(this.outputDir)||at.mkdirSync(this.outputDir,{recursive:!0})}setEmbedScreenshots(t){this.embedScreenshots=t}onStep(t){return this.stepListeners.push(t),()=>{let e=this.stepListeners.indexOf(t);e!==-1&&this.stepListeners.splice(e,1)}}onComplete(t){return this.completeListeners.push(t),()=>{let e=this.completeListeners.indexOf(t);e!==-1&&this.completeListeners.splice(e,1)}}onError(t){return this.errorListeners.push(t),()=>{let e=this.errorListeners.indexOf(t);e!==-1&&this.errorListeners.splice(e,1)}}setFormats(t){this.formats=t}setVideoPath(t){this.videoPath=t}init(t,e){this.packageName=t,this.deviceId=e,this.startTime=new Date,this.steps=[],this.errorsDetected=[],this.pendingWrites=[],this.videoPath=void 0,this.screenshotBuffers.clear()}clearScreenshotBuffers(){this.screenshotBuffers.clear()}logStep(t,e,s,n,r,i,o,a,l){let d=this.steps.length+1,p;if(i){this.embedScreenshots&&this.screenshotBuffers.set(d,i);let m=`step_${d}_${t.toLowerCase().replace(/[^a-z0-9]/g,"_")}.png`,f=ht.join(this.outputDir,m),x=at.promises.writeFile(f,i).catch(v=>{console.error(`Warning: Failed to write screenshot ${m}:`,v)}).finally(()=>{let v=this.pendingWrites.indexOf(x);v!==-1&&this.pendingWrites.splice(v,1)});this.pendingWrites.push(x),p=m}let h=typeof o?.videoOffsetMs=="number"?o.videoOffsetMs:void 0,u={stepIndex:d,timestamp:new Date().toISOString(),phase:t,description:e,action:s,target:r,status:n,screenshotPath:p,durationMs:a,triageBundle:l,videoOffsetMs:h,details:o};this.steps.push(u);for(let m of this.stepListeners)try{m(u)}catch{}return u}logError(t){this.errorsDetected.push(t);for(let e of this.errorListeners)try{e(t)}catch{}}async generateReport(t){this.pendingWrites.length>0&&await Promise.all(this.pendingWrites);let e=new Date,s=Math.round((e.getTime()-this.startTime.getTime())/1e3),n={packageName:this.packageName,deviceId:this.deviceId,startTime:this.startTime.toISOString(),endTime:e.toISOString(),durationSeconds:s,totalSteps:this.steps.length,passedSteps:this.steps.filter(i=>i.status==="PASS").length,failedSteps:this.steps.filter(i=>i.status==="FAIL").length,skippedSteps:this.steps.filter(i=>i.status==="SKIPPED").length,warnSteps:this.steps.filter(i=>i.status==="WARN").length,errorsDetected:this.errorsDetected,videoPath:this.videoPath,steps:this.steps},r=t||"prompttest";if(this.formats.includes("json")){let i=ht.join(this.outputDir,`${r}-results.json`);await at.promises.writeFile(i,JSON.stringify(n,null,2),"utf-8")}if(this.formats.includes("md")){let i=ht.join(this.outputDir,`${r}-report.md`),o=this.formatMarkdownReport(n);await at.promises.writeFile(i,o,"utf-8")}if(this.formats.includes("junit")){let i=ht.join(this.outputDir,`${r}-junit.xml`),o=this.formatJunitXml(n,r);await at.promises.writeFile(i,o,"utf-8")}if(this.formats.includes("html")){let i=ht.join(this.outputDir,`${r}-report.html`),o=this.formatHtmlReport(n,r);await at.promises.writeFile(i,o,"utf-8")}for(let i of this.completeListeners)try{i(n)}catch{}return this.clearScreenshotBuffers(),n}escapeXml(t){return(t||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}escapeHtml(t){return(t||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}formatJunitXml(t,e){let s=t.durationSeconds,n=t.failedSteps+(t.errorsDetected.length>0&&t.failedSteps===0?1:0),r=['<?xml version="1.0" encoding="UTF-8"?>',`<testsuites name="PromptTest" tests="${t.totalSteps}" failures="${n}" skipped="${t.skippedSteps}" time="${s}">`,` <testsuite name="${this.escapeXml(e)}" tests="${t.totalSteps}" failures="${n}" skipped="${t.skippedSteps}" time="${s}">`];for(let i of t.steps){let o=`Step ${i.stepIndex}: ${this.escapeXml(i.action)} - ${this.escapeXml(i.description)}`,a=i.durationMs!==void 0?(i.durationMs/1e3).toFixed(3):"1.000";r.push(` <testcase classname="${this.escapeXml(e)}" name="${o}" time="${a}">`),i.status==="FAIL"?r.push(` <failure message="${this.escapeXml(i.description)}">${this.escapeXml(i.description)}</failure>`):i.status==="SKIPPED"&&r.push(` <skipped message="${this.escapeXml(i.description)}"/>`),r.push(" </testcase>")}return t.failedSteps===0&&t.errorsDetected.length>0&&r.push(` <testcase classname="${this.escapeXml(e)}" name="Error Sentinel Crash Detection" time="0.000">`,` <failure message="${this.escapeXml(t.errorsDetected[0])}">${this.escapeXml(t.errorsDetected.join(`
791
- `))}</failure>`," </testcase>"),r.push(" </testsuite>"),r.push("</testsuites>"),r.join(`
792
- `)}formatMarkdownReport(t){let n=["# \u{1F9EA} PromptTest Autonomous Mobile QA Audit Report","",`**Status:** ${t.failedSteps===0&&t.errorsDetected.length===0?"\u2705 ALL PASSED":"\u26A0\uFE0F ISSUES DETECTED"} `,`**Target Package:** \`${t.packageName}\` `,`**Device:** \`${t.deviceId}\` `,`**Execution Time:** ${t.durationSeconds}s (${t.startTime} to ${t.endTime}) `];if(t.videoPath&&n.push(`**Screen Recording:** [Download Video](./${t.videoPath}) `),n.push("","---","","## \u{1F4CA} Executive Summary","","| Metric | Count |","|---|---|",`| **Total Steps** | ${t.totalSteps} |`,`| **Passed** | \u{1F7E2} ${t.passedSteps} |`,`| **Warnings** | \u{1F7E1} ${t.warnSteps} |`,`| **Failures** | \u{1F534} ${t.failedSteps} |`,`| **Skipped** | \u26AA ${t.skippedSteps} |`,`| **Errors Detected** | ${t.errorsDetected.length} |`,""),t.errorsDetected.length>0){n.push("## \u26A0\uFE0F Errors & Sentinels Flagged");for(let i of t.errorsDetected)n.push(`- \u{1F534} \`${i}\``);n.push("")}n.push("## \u{1F50D} Detailed Execution Timeline"),n.push(""),n.push("| Step | Phase | Action | Target / Details | Status | Duration | Screenshot / Triage |"),n.push("|---|---|---|---|---|---|---|");let r=i=>(i||"").replace(/\|/g,"\\|").replace(/\r?\n/g," ");for(let i of t.steps){let o="\u2014";i.triageBundle?o=`[\u{1F50D} Triage Bundle](./${i.triageBundle})`:i.screenshotPath&&(o=`[\u{1F4F8} Screenshot](./${i.screenshotPath})`);let a=i.durationMs!==void 0?`${i.durationMs}ms`:"\u2014",l=i.status==="PASS"?"\u{1F7E2} PASS":i.status==="WARN"?"\u{1F7E1} WARN":i.status==="SKIPPED"?"\u26AA SKIPPED":"\u{1F534} FAIL";n.push(`| ${i.stepIndex} | ${r(i.phase)} | ${r(i.action)} | ${r(i.description)} | ${l} | ${a} | ${o} |`)}return n.push(""),n.push("---"),n.push("*Generated autonomously by PromptTest Mobile QA Brain.*"),n.join(`
793
- `)}resolveScreenshotData(t){if(t.screenshotPath){if(this.embedScreenshots){if(this.screenshotBuffers.has(t.stepIndex))return`data:image/png;base64,${this.screenshotBuffers.get(t.stepIndex).toString("base64")}`;let e=ht.join(this.outputDir,t.screenshotPath);if(at.existsSync(e))try{return`data:image/png;base64,${at.readFileSync(e).toString("base64")}`}catch{}}return`./${t.screenshotPath}`}}formatHtmlReport(t,e){return Os(t,{suiteName:e,embedScreenshots:this.embedScreenshots,screenshotResolver:s=>this.resolveScreenshotData(s)})}};gt=new Nt});function cr(c){let t=new Map;for(let[e,s]of Object.entries(c)){let r=[ue(e),...s.map(i=>ue(i))];for(let i of r){t.has(i)||t.set(i,[]);let o=t.get(i);for(let a of r)o.includes(a)||o.push(a)}}return t}function is(c,t="en"){let e=ue(c),s=(t||"en").toLowerCase(),n=Fs.get(s);if(n){let r=n.get(e);if(r&&r.length>0)return r}if(s!=="en"){let r=Fs.get("en");if(r){let i=r.get(e);if(i&&i.length>0)return i}}return[e]}var li,Mo,Fs,di,ms=de(()=>{"use strict";st();li={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"]}},Mo=li.en,Fs=new Map;for(let[c,t]of Object.entries(li))Fs.set(c.toLowerCase(),cr(t));di=is});import Oe from"fs";import ct from"path";import{fileURLToPath as lr}from"url";var lt,Me,ft=de(()=>{"use strict";st();ms();Be();lt=class c{memoryDir;currentPackage="global";mappings=new Map;screens=new Map;intents=new Map;dirty=!1;locale="en";setLocale(t){this.locale=(t||"en").toLowerCase()}getLocale(){return this.locale}constructor(t){if(t)this.memoryDir=t;else{let e=ct.dirname(lr(import.meta.url));this.memoryDir=ct.resolve(e,"..","memory")}Oe.existsSync(this.memoryDir)||Oe.mkdirSync(this.memoryDir,{recursive:!0})}cleanPackageName(t){return(t||"global").replace(/[^a-zA-Z0-9._-]/g,"_")}static SCHEMA_VERSION=2;async init(t){this.currentPackage=this.cleanPackageName(t),this.mappings.clear(),this.screens.clear(),this.intents.clear(),this.dirty=!1;let e=ct.join(this.memoryDir,`${this.currentPackage}.json`);if(Oe.existsSync(e))try{let s=await Oe.promises.readFile(e,"utf-8"),n=JSON.parse(s);if(n&&n.mappings){let r=!n.version||n.version<2;for(let[i,o]of Object.entries(n.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(i,o);if(n.screens)for(let[i,o]of Object.entries(n.screens))this.screens.set(i,o);if(n.intents)for(let[i,o]of Object.entries(n.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(t){let e=ue(t),s=is(e,this.locale);return s&&s.length>0?s[0]:e}buildKey(t,e){return`${t.toUpperCase()}:${this.getCanonicalQuery(e)}`}resolve(t,e){let s=this.buildKey(t,e),n=this.mappings.get(s);if(n)return n;let r=`${t.toUpperCase()}:${ue(e)}`,i=this.mappings.get(r);if(i)return i;let o=is(e,this.locale);for(let a of o){let l=`${t.toUpperCase()}:${a}`,d=this.mappings.get(l);if(d)return d}}getAllCandidates(t,e,s,n){let r=(s||this.locale||"en").toLowerCase(),i=new Set;if(n){let l=this.getScreenElements(n),d=this.getCanonicalQuery(e);for(let p of l)p.action===t.toUpperCase()&&(p.canonicalIntent===d||p.canonicalIntent===ue(e))&&p.confidence>=.7&&i.add(p.matchedIdentifier)}let o=this.resolve(t,e);o&&o.confidence>=.8&&i.add(o.matchedIdentifier),i.add(e);let a=is(e,r);for(let l of a)i.add(l);return Array.from(i)}recordSuccess(t,e,s,n,r=1,i,o){if(s.length>35||s.includes("?")||s.includes(`
794
- `))return;let a=this.buildKey(t,e),l=this.mappings.get(a),d=new Date().toISOString(),p=i||l?.healedFrom?I.memory?.maxHealedConfidence??.65:1,h=i||l?.healedFrom?Math.min(r,p):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(p,Number((l.confidence+.05).toFixed(2))),l.lastUpdated=d,l.lastSeenTs=d,i&&(l.healedFrom=i)):this.mappings.set(a,{canonicalIntent:this.getCanonicalQuery(e),action:t.toUpperCase(),matchedIdentifier:s,matchType:n,confidence:h,successCount:1,failCount:0,totalSuccesses:1,recentOutcomes:["PASS"],lastUpdated:d,lastSeenTs:d,healedFrom:i}),o&&this.associateScreenElement(o,a),this.dirty=!0}associateScreenElement(t,e){if(!t||!e)return;let s=new Date().toISOString(),n=this.screens.get(t);n?(n.elements.includes(e)||n.elements.push(e),n.lastSeenTs=s):this.screens.set(t,{lastSeenTs:s,elements:[e]}),this.dirty=!0}getScreenElements(t){let e=this.screens.get(t);return e?e.elements.map(s=>this.mappings.get(s)).filter(s=>s!==void 0):[]}getScreenIndex(){return Object.fromEntries(this.screens.entries())}markScreenCrawled(t,e){if(!t)return;let s=new Date().toISOString(),n=this.screens.get(t);if(n){if(n.crawled=!0,n.crawledAt=s,n.lastSeenTs=s,e&&e.length>0){n.safeActions||(n.safeActions=[]);for(let r of e)n.safeActions.includes(r)||n.safeActions.push(r)}}else this.screens.set(t,{lastSeenTs:s,elements:[],crawled:!0,crawledAt:s,safeActions:e?[...e]:[]});this.dirty=!0}isScreenCrawled(t){return t?!!this.screens.get(t)?.crawled:!1}getScreenMemory(t){return this.screens.get(t)}getCrawledScreensCount(){let t=0;for(let e of this.screens.values())e.crawled&&t++;return t}recordIntentExpansion(t,e){if(!t||!e||e.length===0)return;let s=ue(t),n=new Date().toISOString(),r=this.intents.get(s);r?(r.steps=[...e],r.usageCount=(r.usageCount||1)+1,r.learnedAt=n):this.intents.set(s,{intent:t,steps:[...e],learnedAt:n,usageCount:1}),this.dirty=!0}getIntentExpansion(t){if(!t)return;let e=ue(t),s=this.intents.get(e);if(s)return s.usageCount=(s.usageCount||0)+1,this.dirty=!0,[...s.steps]}hasIntentExpansion(t){return t?this.intents.has(ue(t)):!1}getIntentExpansionsCount(){return this.intents.size}recordFailure(t,e){let s=this.buildKey(t,e),n=this.mappings.get(s);n&&(n.failCount+=1,n.recentOutcomes||(n.recentOutcomes=[]),n.recentOutcomes.push("FAIL"),n.recentOutcomes.length>5&&n.recentOutcomes.shift(),n.confidence=Math.max(.1,Number((n.confidence-.25).toFixed(2))),n.lastUpdated=new Date().toISOString(),(n.recentOutcomes.filter(i=>i==="FAIL").length>=3||n.confidence<.2)&&this.mappings.delete(s),this.dirty=!0)}decayStaleMappings(t){let s=(t??I.memory?.staleThresholdDays??14)*24*60*60*1e3,n=Date.now(),r=0;for(let i of this.mappings.values()){let o=i.lastSeenTs?new Date(i.lastSeenTs).getTime():0;if(o>0&&n-o>s){let a=I.memory?.staleDecayAmount??.2,l=I.memory?.minConfidenceFloor??.2,d=Math.max(l,Number((i.confidence-a).toFixed(2)));d<i.confidence&&(i.confidence=d,r++,this.dirty=!0)}}return r}async save(){if(!this.dirty)return;let t={packageName:this.currentPackage,version:c.SCHEMA_VERSION,lastUpdated:new Date().toISOString(),mappings:Object.fromEntries(this.mappings.entries()),screens:Object.fromEntries(this.screens.entries()),intents:Object.fromEntries(this.intents.entries())},e=ct.join(this.memoryDir,`${this.currentPackage}.json`),s=`${e}.tmp.${Date.now()}`;await Oe.promises.writeFile(s,JSON.stringify(t,null,2),"utf-8");try{await Oe.promises.rename(s,e)}catch{await Oe.promises.copyFile(s,e),await Oe.promises.unlink(s).catch(()=>{})}this.dirty=!1}async exportMemory(t){let e=t?ct.resolve(t):ct.join(this.memoryDir,`${this.currentPackage}-export.json`),s=ct.dirname(e);Oe.existsSync(s)||Oe.mkdirSync(s,{recursive:!0});let n={packageName:this.currentPackage,version:c.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 Oe.promises.writeFile(e,JSON.stringify(n,null,2),"utf-8"),e}async importMemory(t){let e=ct.resolve(t);if(!Oe.existsSync(e))throw new Error(`Memory import source file not found: ${e}`);let s=await Oe.promises.readFile(e,"utf-8"),n=JSON.parse(s);if(!n||!n.mappings)throw new Error("Invalid memory file format: missing mappings object.");let r=0,i=new Date().toISOString();for(let[o,a]of Object.entries(n.mappings)){let l=this.mappings.get(o);(!l||a.confidence>=l.confidence)&&(this.mappings.set(o,{...a,totalSuccesses:a.totalSuccesses??a.successCount??1,recentOutcomes:a.recentOutcomes??["PASS"],lastSeenTs:a.lastSeenTs??a.lastUpdated??i,lastUpdated:i}),r++)}if(n.screens)for(let[o,a]of Object.entries(n.screens)){let l=this.screens.get(o);if(!l)this.screens.set(o,a);else{let d=new Set([...l.elements,...a.elements]),p=new Set([...l.safeActions||[],...a.safeActions||[]]);this.screens.set(o,{lastSeenTs:a.lastSeenTs||l.lastSeenTs,elements:Array.from(d),crawled:l.crawled||a.crawled,crawledAt:a.crawledAt||l.crawledAt,safeActions:Array.from(p)})}}if(n.intents)for(let[o,a]of Object.entries(n.intents)){let l=this.intents.get(o);(!l||(a.usageCount||0)>=(l.usageCount||0))&&this.intents.set(o,a)}return r>0&&(this.dirty=!0,await this.save()),r}getMappingsList(){return Array.from(this.mappings.values())}getStats(){let t=Array.from(this.mappings.values()),e=t.reduce((r,i)=>r+(i.totalSuccesses||i.successCount||0),0),s=t.length>0?Number((t.reduce((r,i)=>r+i.confidence,0)/t.length).toFixed(2)):0,n=0;for(let r of this.screens.values())r.crawled&&n++;return{totalMappings:this.mappings.size,totalScreens:this.screens.size,crawledScreens:n,totalIntents:this.intents.size,package:this.currentPackage,totalSuccesses:e,averageConfidence:s}}async clear(t){let e=this.cleanPackageName(t||this.currentPackage),s=ct.join(this.memoryDir,`${e}.json`);return Oe.existsSync(s)?(await Oe.promises.unlink(s),e===this.currentPackage&&(this.mappings.clear(),this.screens.clear(),this.intents.clear(),this.dirty=!1),!0):!1}},Me=new lt});import Qe from"fs";import dr from"path";import pr from"os";function Rt(c){let t=(c||"default").replace(/[^a-zA-Z0-9._-]/g,"_"),e=dr.join(pr.tmpdir(),`prompttest-${t}.lock`);if(Qe.existsSync(e))try{let n=Qe.readFileSync(e,"utf-8").trim(),r=parseInt(n,10);if(!isNaN(r)){if(r===process.pid)return{serial:c,lockFilePath:e,acquired:!0,pid:process.pid};try{throw process.kill(r,0),new Error(`Device "${c}" is locked by another PromptTest process (PID ${r}).
795
- Wait for the other run to complete, or remove the lock file if it is stale: ${e}`)}catch(i){if(i.code==="ESRCH")try{Qe.unlinkSync(e)}catch{}else throw i}}}catch(n){let r=n;if(r.message&&r.message.includes("locked by another"))throw r;try{Qe.unlinkSync(e)}catch{}}try{Qe.writeFileSync(e,process.pid.toString(),{flag:"wx"})}catch(n){throw n.code==="EEXIST"?new Error(`Device "${c}" was just acquired by a concurrent PromptTest process.`):n}let s=()=>{try{Qe.existsSync(e)&&Qe.readFileSync(e,"utf-8").trim()===process.pid.toString()&&Qe.unlinkSync(e)}catch{}};return _s.set(e,s),process.once("exit",s),{serial:c,lockFilePath:e,acquired:!0,pid:process.pid}}function Lt(c){if(c)try{let t=_s.get(c.lockFilePath);if(t)process.removeListener("exit",t),_s.delete(c.lockFilePath),t();else if(Qe.existsSync(c.lockFilePath))try{Qe.readFileSync(c.lockFilePath,"utf-8").trim()===process.pid.toString()&&Qe.unlinkSync(c.lockFilePath)}catch{}}catch{}}var _s,rs=de(()=>{"use strict";_s=new Map});import ae from"chalk";var fs,pi=de(()=>{"use strict";Ke();ut();Fe();mt();ft();ns();fs=class{driver;reporter;memory;formFiller;screenLedger=new Map;callStack=[];executedStepCount=0;visitedScreenHashes=new Set;devDefectsList=[];safetyHitList=[];externalSkippedList=[];stepListener;abortFlag;constructor(t=re,e=gt,s=Me){this.driver=t,this.reporter=e,this.memory=s,this.formFiller=new It(void 0,t)}emitStep(t){try{let e=t.screenHash?this.screenLedger.get(t.screenHash):void 0;this.stepListener?.({...t,depth:t.depth??e?.depth,elementCount:t.elementCount??e?.elements.length,discoveredScreens:t.discoveredScreens??this.visitedScreenHashes.size,totalSteps:t.totalSteps??this.executedStepCount})}catch{}}isAborted(){return this.abortFlag?.aborted===!0}async getCleanUiHierarchy(t,e,s=5){let n=await this.driver.getUiHierarchy(e),{flat:r}=ve(n),i=0;for(;i<s;){let o=ss(r);if(!o)break;i++;let a=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,l=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(ae.red(` \u{1F6A8} Global Dev Console Intercepted: ${a} "${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 ${a}: "${o.errorText.slice(0,120)}"${l}`});let d=await this.driver.captureScreenshot(e);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${a}: "${o.errorText}"${l}`,"Verify","FAIL",o.errorText,d,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(ae.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,e)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(ae.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,e)):(console.log(ae.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(e)),await new Promise(p=>setTimeout(p,800)),n=await this.driver.getUiHierarchy(e),r=ve(n).flat}return{xml:n,flat:r}}async dismissSoftKeyboard(t){try{await this.driver.pressKey(111,t),await new Promise(e=>setTimeout(e,150))}catch{}}async packageJailGuard(t,e){let s=await this.driver.getForegroundPackage(e);if(!s)return!1;if(!s.includes(t)){let n=s.includes("launcher")||s.includes("home")||s.includes("trebuchet");console.log(ae.yellow(` \u{1F6A8} External App Jailbreak Detected [${s}]. Recovering...`)),n||(await this.driver.pressBack(e),await new Promise(i=>setTimeout(i,800)));let r=await this.driver.getForegroundPackage(e);return(!r||!r.includes(t))&&(console.log(ae.red(` Force restoring ${t} foreground...`)),await this.driver.launchApp(t,e),await new Promise(i=>setTimeout(i,1500)),this.callStack=this.callStack.slice(0,1)),!0}return!1}async handleExternalMediaChooser(t,e){let s=await this.driver.getForegroundPackage(e);if(!s)return!1;let n=s==="android"||s.includes("resolver")||s.includes("intentresolver");if(n){console.log(ae.yellow(` \u{1F6E1}\uFE0F External Media Chooser Intercepted [${s}]. Dismissing to stay in-app...`)),await this.driver.pressBack(e),await new Promise(i=>setTimeout(i,800));let r=await this.driver.getForegroundPackage(e);return r&&!r.includes(t)&&n&&(await this.driver.launchApp(t,e),await new Promise(i=>setTimeout(i,1200)),this.callStack=this.callStack.slice(0,1)),!0}return!1}catalogScreen(t,e,s=[]){let n=[],r=e.height,i=r*.06,o=r*.88,a=y=>s.some(k=>Math.abs(k.bounds.centerX-y.bounds.centerX)<30&&Math.abs(k.bounds.centerY-y.bounds.centerY)<30),l=je(t);for(let y of l){if(a(y))continue;let k=y.text||y.contentDesc||y.resourceId||"Text Input";n.push({node:y,type:"input",label:k,tested:!1})}let d=Yt(t);for(let y of d){if(a(y))continue;let k=y.text||y.contentDesc||y.resourceId||"Toggle Control";n.push({node:y,type:"toggle",label:k,tested:!1})}let p=ze(t);for(let y of p){if(a(y)||pt([y],r,e.width)||We([y],r,e.width,!0))continue;let k=y.text||y.contentDesc||y.resourceId||"Action Button";if(Ge(y)){this.externalSkippedList.push(k);continue}let C=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(k);n.push({node:y,type:C?"toggle":"transition",label:k,tested:!1})}let h=Kn(t,r,e.width);for(let y of h){if(a(y)||We([y],r,e.width,!0)||n.some(C=>Math.abs(C.node.bounds.centerX-y.bounds.centerX)<25&&Math.abs(C.node.bounds.centerY-y.bounds.centerY)<25))continue;let k=y.text||y.contentDesc||y.resourceId||"Notification / Header Action";if(Ge(y)){this.externalSkippedList.push(k);continue}n.push({node:y,type:"transition",label:k,tested:!1})}let u=t.filter(y=>{if(!y.clickable||y.bounds.width===0)return!1;let k=`${y.text} ${y.contentDesc} ${y.resourceId}`.toLowerCase();return k.includes("now playing")||k.includes("play")||k.includes("pause")});for(let y of u){if(a(y)||n.some(C=>Math.abs(C.node.bounds.centerX-y.bounds.centerX)<25&&Math.abs(C.node.bounds.centerY-y.bounds.centerY)<25))continue;let k=y.text||y.contentDesc||y.resourceId||"Media Control";n.push({node:y,type:"toggle",label:k,tested:!1})}let m=$t(t,e).filter(y=>!a(y)&&y.bounds.centerY>i&&y.bounds.centerY<o&&!We([y],r,e.width,!0)&&!Ge(y)),f=qn(m,2);for(let y of f){let k=y.text||y.contentDesc||y.resourceId||`Item (${y.bounds.centerX},${y.bounds.centerY})`;n.push({node:y,type:"transition",label:k,tested:!1})}let x=0,v=0,S=[];for(let y of n){let k=/\.(?:mp4|mkv|mov|webm|flv|avi)\b/i.test(y.label),C=/\.(?:mp3|wav|m4a|aac|ogg)\b/i.test(y.label);if(k){if(x++,x>1)continue}else if(C&&(v++,v>1))continue;S.push(y)}return S}async safeBacktrack(t,e,s,n,r=!0){let{flat:i}=await this.getCleanUiHierarchy(e,s),{height:o,width:a}=await this.driver.getScreenDimensions(s),l=ot(i,{width:a,height:o});if(l===t)return!0;let d=We(i,o,a,!0);if(d&&d.bounds.width>0){console.log(ae.gray(` [Backtrack] Tapping in-app back/close button "${d.text||d.contentDesc||d.className}"...`)),await this.driver.tap(d.bounds.centerX,d.bounds.centerY,s),await new Promise(u=>setTimeout(u,1e3)),await this.packageJailGuard(e,s);let p=await this.getCleanUiHierarchy(e,s),h=ot(p.flat,{width:a,height:o});if(h===t||h!==l)return!0}if(n&&n.bounds.width>0){console.log(ae.gray(` [Backtrack] Restoring root domain via tab "${n.text||n.contentDesc||"Root Tab"}"...`)),await this.driver.tap(n.bounds.centerX,n.bounds.centerY,s),await new Promise(u=>setTimeout(u,1e3)),await this.packageJailGuard(e,s);let p=await this.getCleanUiHierarchy(e,s),h=ot(p.flat,{width:a,height:o});if(h===t||h!==l)return!0}if(r){if(console.log(ae.gray(" [Backtrack] Sending fallback KEYCODE_BACK...")),await this.driver.pressKey(4,s),await new Promise(m=>setTimeout(m,1e3)),await this.packageJailGuard(e,s))return console.log(ae.yellow(" \u26A0\uFE0F Backtrack via KEYCODE_BACK caused app jailbreak. Aborting branch restoration.")),!1;let h=await this.getCleanUiHierarchy(e,s),u=ot(h.flat,{width:a,height:o});if(u===t||u!==l)return!0}return!1}async explore(t){let{packageName:e,serial:s,maxScreens:n=30,maxDepth:r=3,stepBudget:i=100,maxInteractionsPerScreen:o=6,safetyMode:a="strict",safetyBlacklist:l=[],onStep:d,abortSignal:p}=t;this.stepListener=d,this.abortFlag=p,console.log(ae.bold.cyan(`
796
- \u{1F916} PromptTest Universal State-Graph DFS Engine`)),console.log(ae.gray(`Target Package: ${e}`)),console.log(ae.gray(`Safety Policy: ${a.toUpperCase()}`)),console.log(ae.gray(`Limits: Max Screens = ${n}, Step Budget = ${i}, Max Depth = ${r}`)),await this.driver.clearCrashLogs(s);let h=await this.driver.getScreenDimensions(s),u=await this.driver.getForegroundPackage(s);(!u||!u.includes(e))&&(console.log(ae.gray(`Launching ${e}...`)),await this.driver.launchApp(e,s),await new Promise(y=>setTimeout(y,2500)));let{flat:m}=await this.getCleanUiHierarchy(e,s);await this.dismissSoftKeyboard(s);let f=Ns(m,h,e);console.log(ae.magenta(`\u26A1 Universal Paradigm Detected: [${f.paradigm.toUpperCase()}] with ${f.roots.length} root node(s).`));let x=[],v;if(f.paradigm==="drawer"&&f.roots.length>0){v=f.roots[0];let y={...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}};x.push(y),console.log(ae.gray(" Opening drawer to catalog root destinations...")),await this.driver.tap(v.bounds.centerX,v.bounds.centerY,s),await new Promise(O=>setTimeout(O,1200));let k=await this.getCleanUiHierarchy(e,s),C=k.flat.filter(O=>{if(!O.clickable||O.bounds.width===0||O.bounds.height===0||O.bounds.width>=h.width*.95&&O.bounds.height>=h.height*.95||We([O],h.height,h.width,!0))return!1;let V=`${O.contentDesc} ${O.text}`.trim(),W=O.bounds.width>h.width*.15&&O.bounds.height>h.height*.05;return V.toLowerCase().includes("developed by")||O.bounds.centerY>h.height*.85?!1:V.length>=2||W});for(let O of C)x.some(V=>Math.abs(V.bounds.centerX-O.bounds.centerX)<30&&Math.abs(V.bounds.centerY-O.bounds.centerY)<30)||x.push(O);let j=We(k.flat,h.height,h.width,!0);j?await this.driver.tap(j.bounds.centerX,j.bounds.centerY,s):await this.driver.tap(v.bounds.centerX,v.bounds.centerY,s),await new Promise(O=>setTimeout(O,1200));let $=await this.getCleanUiHierarchy(e,s);if($.flat.some(O=>`${O.contentDesc} ${O.text}`.includes("Gallery")&&`${O.contentDesc} ${O.text}`.includes("Planner"))){let O=We($.flat,h.height,h.width,!0);O?await this.driver.tap(O.bounds.centerX,O.bounds.centerY,s):await this.driver.tap(h.width-50,h.height/2,s),await new Promise(V=>setTimeout(V,1e3))}console.log(ae.magenta(` Discovered ${x.length} destinations (1 Main Hub + ${x.length-1} Drawer destinations).`))}else f.roots.length>0?x=f.roots:x=[m[0]||{text:"Main Hub",clickable:!0}];let S=1;for(let y of x){if(this.executedStepCount>=i||this.visitedScreenHashes.size>=n||this.isAborted())break;let k=y.text||y.contentDesc||`Root #${S}`;if(console.log(ae.yellow(`
797
- [Domain ${S}/${x.length}] Entering Root Tree: "${k}"...`)),this.emitStep({type:"action",description:`Tap "${k}" to enter the ${k} section`,screenTitle:k,depth:0}),f.paradigm==="bottom_tabs"||f.paradigm==="top_tabs"){let $=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,$,s),this.executedStepCount++,await new Promise(J=>setTimeout(J,1200))}else f.paradigm==="drawer"&&S>1&&v&&(await this.dismissSoftKeyboard(s),console.log(ae.gray(` Opening drawer to select "${k}"...`)),await this.driver.tap(v.bounds.centerX,v.bounds.centerY,s),await new Promise($=>setTimeout($,1e3)),await this.driver.tap(y.bounds.centerX,y.bounds.centerY,s),this.executedStepCount++,await new Promise($=>setTimeout($,1500)));m=(await this.getCleanUiHierarchy(e,s)).flat,await this.dismissSoftKeyboard(s);let j=S===1?Math.max(25,Math.ceil(i*.45)):Math.max(8,Math.ceil(i*.55/Math.max(1,x.length-1)));if(await this.exploreDfsNode({packageName:e,serial:s,screenDims:h,rootNodes:f.roots,activeRootNode:y,currentDepth:0,maxDepth:r,maxScreens:n,stepBudget:i,domainStepLimit:this.executedStepCount+j,maxInteractionsPerScreen:o,safetyMode:a,safetyBlacklist:l,domainLabel:k}),f.paradigm==="drawer"&&S>1){let $=0;for(;$<4;){let J=await this.getCleanUiHierarchy(e,s),O=pt(J.flat,h.height,h.width),V=J.flat.some(se=>`${se.contentDesc} ${se.text}`.includes("Gallery")&&`${se.contentDesc} ${se.text}`.includes("Planner"));if(O&&!V)break;let W=We(J.flat,h.height,h.width,!0);if(W)console.log(ae.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(se=>setTimeout(se,1e3)),$++;else break}}if(S===1&&x.length===1){let $=await this.getCleanUiHierarchy(e,s),J=Ns($.flat,h,e);if((J.paradigm==="bottom_tabs"||J.paradigm==="top_tabs")&&J.roots.length>1){console.log(ae.magenta(`
798
- \u26A1 Post-Auth Navigation Discovered: [${J.paradigm.toUpperCase()}] with ${J.roots.length} root domain(s)!`)),f.paradigm=J.paradigm,f.roots=J.roots;let O=J.roots.slice(1);x.push(...O.length>0?O:J.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(ae.green(`
799
- \u2714 Universal DFS Traversal Complete!`)),console.log(ae.cyan(` Visited Screens: ${this.visitedScreenHashes.size}`)),console.log(ae.cyan(` Total Steps Executed: ${this.executedStepCount}/${i}`)),console.log(ae.cyan(` Defects Triaged: ${this.devDefectsList.length}`))}async exploreDfsNode(t){let{packageName:e,serial:s,screenDims:n,rootNodes:r,activeRootNode:i,currentDepth:o,maxDepth:a,maxScreens:l,stepBudget:d,domainStepLimit:p,maxInteractionsPerScreen:h,safetyMode:u,safetyBlacklist:m,domainLabel:f}=t;if(this.executedStepCount>=d||this.visitedScreenHashes.size>=l||p&&this.executedStepCount>=p||this.isAborted())return;let{flat:x}=await this.getCleanUiHierarchy(e,s),v=De(x),S=ot(x,n);this.visitedScreenHashes.add(v);let y=this.screenLedger.get(S);if(y){if(y.status==="FULLY_EXPLORED"){console.log(ae.gray(` \u26A1 Cycle Detected: Screen [${S.slice(0,8)}] already fully explored. Pruning branch.`));return}}else{let $=this.catalogScreen(x,n,r);y={hash:v,semanticHash:S,title:`${f} (depth ${o})`,elements:$,status:"IN_PROGRESS",depth:o,interactionCount:0},this.screenLedger.set(S,y),console.log(ae.cyan(` \u{1F50E} Discovered Screen [${S.slice(0,8)}] at depth ${o} with ${$.length} candidate element(s).`)),this.emitStep({type:"state_change",description:`Discovered screen "${f}" at depth ${o} with ${$.length} candidate element(s)`,screenHash:S,screenTitle:f,depth:o})}this.callStack.push({screenHash:v,semanticHash:S,title:y.title,timestamp:Date.now()});let k=y.elements.filter($=>$.type==="input"&&!$.tested);if(k.length>0&&y.interactionCount<h){console.log(ae.magenta(` Filling in-place form fields (${k.length} inputs)...`));let $=await this.formFiller.fillAndSubmitForm(x,s,!0,!1);this.executedStepCount++,y.interactionCount++;for(let J of k)J.tested=!0;await this.dismissSoftKeyboard(s),await this.packageJailGuard(e,s),this.reporter.logStep("In-Place Form Fill",`Populated ${$.filledFields.length} field(s) on [${S.slice(0,8)}]`,"FormSubmit","PASS",$.submitButtonText||"Auto");for(let J of $.filledFields)this.emitStep({type:"action",description:`Type "${J.valueEntered}" in the "${J.fieldLabel}" field`,screenHash:S,screenTitle:f,depth:o});$.submitButtonText&&this.emitStep({type:"action",description:`Tap "${$.submitButtonText}" to submit the form`,screenHash:S,screenTitle:f,depth:o})}let C=y.elements.filter($=>$.type==="toggle"&&!$.tested);for(let $ of C){if(this.executedStepCount>=d||y.interactionCount>=h)break;if($.tested=!0,Xe($.node,m,u)){this.safetyHitList.push($.label),this.emitStep({type:"safety_block",description:`Safety policy [${u.toUpperCase()}] blocked toggle "${$.label}"`,screenHash:S,screenTitle:f,depth:o});continue}console.log(ae.gray(` Exercising toggle: "${$.label.slice(0,30)}"...`)),this.executedStepCount++,y.interactionCount++,await this.driver.tap($.node.bounds.centerX,$.node.bounds.centerY,s),await new Promise(J=>setTimeout(J,600)),this.reporter.logStep("In-Place Toggle",`Toggled control "${$.label}"`,"Tap","PASS",$.label),this.emitStep({type:"action",description:`Toggle "${$.label}"`,screenHash:S,screenTitle:f,depth:o})}let j=y.elements.filter($=>$.type==="transition"&&!$.tested);for(let $ of j){if(this.executedStepCount>=d||this.visitedScreenHashes.size>=l||p&&this.executedStepCount>=p&&!(o===0&&f.includes("Home Screen"))||y.interactionCount>=h||this.isAborted())break;if($.tested=!0,Ge($.node)){this.externalSkippedList.push($.label);continue}if(Xe($.node,m,u)){this.safetyHitList.push($.label),this.emitStep({type:"safety_block",description:`Safety policy [${u.toUpperCase()}] blocked transition "${$.label}"`,screenHash:S,screenTitle:f,depth:o});continue}console.log(ae.magenta(` \u26A1 [DFS Depth ${o}] Testing transition: "${$.label.slice(0,40)}"...`)),this.executedStepCount++,y.interactionCount++,await this.driver.tap($.node.bounds.centerX,$.node.bounds.centerY,s),await new Promise(W=>setTimeout(W,1500)),await this.dismissSoftKeyboard(s),await this.handleExternalMediaChooser(e,s),await this.packageJailGuard(e,s);let J=await this.getCleanUiHierarchy(e,s),O=ot(J.flat,n),V=O!==S;if(this.reporter.logStep("DFS Transition",`Tapped "${$.label}" [${V?"Navigated":"In-Place"}]`,"Tap","PASS",$.label),this.emitStep({type:"action",description:`Tap "${$.label}"${V?" to navigate to the next screen":""}`,screenHash:S,screenTitle:f,depth:o}),V&&O&&this.emitStep({type:"state_change",description:`Navigated to screen [${O.slice(0,8)}] via "${$.label}"`,screenHash:O,screenTitle:`${f} > ${$.label.slice(0,20)}`,depth:o+1}),V){if(o<a){let oe=o===0&&f.includes("Home Screen")?Math.min(this.executedStepCount+3,p||this.executedStepCount+3):p;await this.exploreDfsNode({packageName:e,serial:s,screenDims:n,rootNodes:r,activeRootNode:i,domainStepLimit:oe,currentDepth:o+1,maxDepth:a,maxScreens:l,stepBudget:d,maxInteractionsPerScreen:h,safetyMode:u,safetyBlacklist:m,domainLabel:`${f} > ${$.label.slice(0,20)}`})}console.log(ae.gray(` [DFS Backtrack] Returning to [${S.slice(0,8)}]...`)),await this.safeBacktrack(S,e,s,o===0?i:void 0,o>0),await this.dismissSoftKeyboard(s);let W=await this.getCleanUiHierarchy(e,s);if(ot(W.flat,n)!==S){let oe=We(W.flat,n.height,n.width,!0);oe&&(console.log(ae.gray(" [Backtrack Verification] Re-tapping in-app back/close button...")),await this.driver.tap(oe.bounds.centerX,oe.bounds.centerY,s),await new Promise(w=>setTimeout(w,1e3)),await this.packageJailGuard(e,s));let G=await this.getCleanUiHierarchy(e,s);if(ot(G.flat,n)!==S){console.log(ae.yellow(` \u26A0\uFE0F Unable to restore parent screen [${S.slice(0,8)}]. Unwinding branch to prevent jailbreak loop.`));break}}}}y.status="FULLY_EXPLORED",this.callStack.pop()}}});import ur from"fs";import Hs from"path";import F from"chalk";var Mt,bs,zt=de(()=>{"use strict";Fe();Ke();Be();ut();ns();mt();ft();rs();pi();Mt=class{driver;formFiller;reporter;memory;safetyHitList=[];externalSkippedList=[];devDefectsList=[];visitedScreenHashes=new Set;executedStepCount=0;constructor(t,e,s,n){this.driver=t||re,this.formFiller=e||ci,this.reporter=s||gt,this.memory=n||Me}getMemory(){return this.memory}getSafetyHitList(){return this.safetyHitList}getExternalSkippedList(){return this.externalSkippedList}getDevDefectsList(){return this.devDefectsList}getVisitedScreensCount(){return this.visitedScreenHashes.size}async packageJailGuard(t,e){let s=await this.driver.getForegroundPackage(e);if(s&&!s.includes(t)){let n=s.includes("launcher")||s.includes("home")||s.includes("trebuchet");console.log(F.yellow(` \u{1F6E1}\uFE0F Jail Containment: App escaped to "${s}". Executing containment recovery...`)),n||(await this.driver.pressBack(e),await new Promise(i=>setTimeout(i,600)));let r=await this.driver.getForegroundPackage(e);return(!r||!r.includes(t))&&(console.log(F.gray(` Force re-launching ${t} to restore foreground...`)),await this.driver.launchApp(t,e),await new Promise(i=>setTimeout(i,1200))),this.reporter.logStep("Jail Containment",`App escaped to "${s}"; successfully contained and restored "${t}"`,"Back","PASS"),!0}return!1}async getCleanUiHierarchy(t,e,s=5){let n=await this.driver.getUiHierarchy(e),{flat:r}=ve(n),i=0;for(;i<s;){let o=ss(r);if(!o)break;i++;let a=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,l=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(F.red(` \u{1F6A8} Global Dev Console Intercepted: ${a} "${o.errorText.slice(0,80)}"${l}`)),this.devDefectsList.push(o.sourceLocation?`${o.errorText} (${o.sourceLocation})`:o.errorText);let d=await this.driver.captureScreenshot(e);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${a}: "${o.errorText}"${l}`,"Verify","FAIL",o.errorText,d,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(F.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,e)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(F.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,e)):(console.log(F.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(e)),await new Promise(p=>setTimeout(p,800)),n=await this.driver.getUiHierarchy(e),r=ve(n).flat}return{xml:n,flat:r}}async handleDevOverlays(t,e,s){let n=ss(t);if(!n)return!1;let r=`[${(n.severity||"error").toUpperCase()} - ${n.type.toUpperCase()}]`,i=n.sourceLocation?` [${n.sourceLocation}]`:"";console.log(F.red(` \u{1F6A8} Dev Overlay Detected: ${r} "${n.errorText.slice(0,80)}"${i}`)),this.devDefectsList.push(n.sourceLocation?`${n.errorText} (${n.sourceLocation})`:n.errorText);let o=await this.driver.captureScreenshot(s);return this.reporter.logStep("Dev Overlay Triage",`Detected and triaged ${r} error: "${n.errorText}"${i}`,"Verify","FAIL",n.errorText,o,{errors:[n.sourceLocation?`${n.errorText} (${n.sourceLocation})`:n.errorText]}),n.dismissButton&&n.dismissButton.bounds.width>0?(console.log(F.gray(" Dismissing error overlay via dismiss button...")),await this.driver.tap(n.dismissButton.bounds.centerX,n.dismissButton.bounds.centerY,s)):n.isToast&&n.node&&n.node.bounds.width>0?(console.log(F.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(n.node.bounds.centerX,n.node.bounds.centerY,s)):(console.log(F.gray(" Pressing Back to dismiss error overlay...")),await this.driver.pressBack(s)),await new Promise(a=>setTimeout(a,800)),!0}async dismissSoftKeyboard(t){try{await this.driver.pressKey(111,t),await new Promise(e=>setTimeout(e,200))}catch{}}async explore(t){let{packageName:e,serial:s,maxTabs:n=I.explorer?.maxTabs||15,exploreLists:r=!0,skipAlreadyCrawled:i=!1,safetyMode:o=I.safety?.mode||"strict",safetyBlacklist:a=I.safety?.customBlacklist?[...I.safety.customBlacklist]:[],maxScreens:l=I.explorer?.maxScreens||30,stepBudget:d=I.explorer?.stepBudget||100,screenshots:p=I.explorer?.screenshots||"state-change",video:h=t.video??!1,onStep:u,abortSignal:m}=t;console.log(F.cyan(`
800
- \u{1F916} PromptTest Autonomous Exploration Brain v3`)),console.log(F.gray("Target Package: ")+F.bold.white(e)),console.log(F.gray("Safety Policy: ")+F.bold.yellow(o.toUpperCase())+(a.length>0?F.gray(` (+${a.length} custom blacklist)`):"")),console.log(F.gray("Screenshots Policy: ")+F.bold.white(p));let f=[],x=0,v=$=>{x++,u?.({...$,stepNumber:x,packageName:e,totalSteps:$.totalSteps??this.executedStepCount,discoveredScreens:$.discoveredScreens??this.visitedScreenHashes.size})},S=async($,J=!0)=>{if(!(p==="failure-only"&&$!=="FAIL")&&!(p==="state-change"&&!J&&$!=="FAIL"))try{return await this.driver.captureScreenshot(s)}catch{return}};await this.memory.init(e);let y=s||this.driver.getDefaultDevice(),k=y||"Default Device";this.reporter.init(e,k);let C=null;y&&(C=Rt(y));let j=null;this.safetyHitList=[],this.externalSkippedList=[],this.devDefectsList=[],this.visitedScreenHashes.clear(),this.executedStepCount=0;try{if(await this.driver.clearCrashLogs(s),h&&(console.log(F.cyan("\u{1F3AC} Starting ADB screen recording for exploration (--video enabled)...")),j=await this.driver.startScreenRecording("/sdcard/prompttest_explore_temp.mp4",s)),(t.strategy||I.explorer?.strategy||"heuristic")==="dfs"){let N=new fs(this.driver,this.reporter,this.memory);await N.explore({packageName:e,serial:s,maxScreens:l,maxDepth:t.maxDepth||I.explorer?.maxDepth||5,stepBudget:d,safetyMode:o,safetyBlacklist:a,screenshotPolicy:p,skipAlreadyCrawled:i,onStep:T=>v({type:T.type,description:T.description,screenHash:T.screenHash,screenTitle:T.screenTitle,depth:T.depth,elementCount:T.elementCount,totalSteps:T.totalSteps,discoveredScreens:T.discoveredScreens}),abortSignal:m}),this.safetyHitList=N.safetyHitList,this.externalSkippedList=N.externalSkippedList,this.devDefectsList=N.devDefectsList,this.visitedScreenHashes=N.visitedScreenHashes,this.executedStepCount=N.executedStepCount;let M=this.memory.getStats();this.reporter.logStep("Coverage Audit",`State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s). Memory Engine cached ${M.totalScreens} crawled screen(s).`,"Verify","PASS"),await this.reporter.generateReport(),console.log(F.bold.green(`
801
- \u2728 Full Audit Report generated at: output/prompttest-report.md`));return}let{width:J,height:O}=await this.driver.getScreenDimensions(s);console.log(F.yellow(`
802
- [1/5] Verifying Foreground State...`));let V=await this.driver.getForegroundPackage(s);(!V||!V.includes(e))&&(console.log(F.gray(`Launching ${e}...`)),await this.driver.launchApp(e,s),await new Promise(N=>setTimeout(N,2500)));let W=await this.runErrorSentinel(e,s,"Post-Launch"),se=W.hasError?"FAIL":"PASS",oe=await S(se,!0);this.reporter.logStep("Init",`Launched and verified foreground for ${e}`,"Launch",se,e,oe,W.hasError?{errors:W.errors}:void 0),console.log(se==="PASS"?F.green("\u2714 App is active in foreground."):F.red(`\u2716 Issues detected during launch for ${e}.`)),console.log(F.yellow(`
803
- [2/5] Inspecting UI Hierarchy for Forms...`));let{xml:G,flat:H}=await this.getCleanUiHierarchy(e,s),w=De(H);if(this.visitedScreenHashes.add(w),this.memory.isScreenCrawled(w)&&i)console.log(F.cyan(` \u26A1 Fast-Forward: Screen [${w.slice(0,8)}] was previously crawled offline. Skipping redundant element interactions.`)),this.reporter.logStep("Fast Forward",`Fast-forwarded past previously crawled screen [${w.slice(0,8)}] (Memory Engine)`,"Skip","PASS");else{let N=je(H);if(N.length>0){console.log(F.magenta(`\u26A1 Detected ${N.length} form field(s)! Engaging Semantic Form Filler...`));for(let ee of N)console.log(F.gray(` - Field: "${ee.text||ee.contentDesc||ee.resourceId}"`));let E=await this.formFiller.fillAndSubmitForm(H,s,!0,!1);this.executedStepCount++,await new Promise(ee=>setTimeout(ee,1500)),await this.dismissSoftKeyboard(s),await this.packageJailGuard(e,s);let L=await this.getCleanUiHierarchy(e,s);G=L.xml,H=L.flat,this.visitedScreenHashes.add(De(H));let U=await this.runErrorSentinel(e,s,"Post-Form-Submit"),z=je(H),q=U.hasError||z.length===N.length&&U.errors.length>0?"FAIL":"PASS",ie=await S(q,!0),fe=E.filledFields.map(ee=>`${ee.fieldLabel} -> "${ee.valueEntered}"`).join(", ");this.reporter.logStep("Form Fill",`Filled ${E.filledFields.length} field(s) [${fe}] and submitted via "${E.submitButtonText||"Auto"}"`,"FormSubmit",q,E.submitButtonText,ie,U.hasError?{errors:U.errors}:void 0),console.log(q==="PASS"?F.green("\u2714 Form populated and submitted successfully!"):F.red("\u2716 Form submission triggered an error or remained unauthenticated."))}else console.log(F.gray("No form input fields detected on initial screen."));console.log(F.yellow(`
804
- [3/5] Inspecting Interactive Action Controls...`));let M=ze(H).filter(E=>{let L=(E.text+" "+E.contentDesc).toLowerCase();if(L.includes("submit")||L.includes("cancel")||pt([E],O))return!1;if(Ge(E)){let U=E.text||E.contentDesc||E.resourceId||"External Link";return console.log(F.gray(` \u{1F517} External Link Bypassed: "${U}"`)),this.externalSkippedList.push(U),!1}if(Xe(E,a,o)){let U=E.text||E.contentDesc||E.resourceId||"Action";return console.log(F.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive action "${U}"`)),this.safetyHitList.push(U),!1}return!0});if(M.length>0){let E=M[0],L=E.text||E.contentDesc||E.resourceId;console.log(F.magenta(`\u26A1 Found action button: "${L}". Testing interaction...`));let U=De(H);this.executedStepCount++,await this.driver.tap(E.bounds.centerX,E.bounds.centerY,s),await new Promise(Te=>setTimeout(Te,1200)),await this.packageJailGuard(e,s);let z=await this.getCleanUiHierarchy(e,s);G=z.xml,H=z.flat;let K=De(H);this.visitedScreenHashes.add(K);let q=await this.runErrorSentinel(e,s,"Post-Action-Tap"),ie=U!==K,fe="PASS",ee="";q.hasError?(fe="FAIL",ee="Action triggered error or crash"):ie?(fe="PASS",ee="Screen transitioned successfully"):(fe="WARN",ee="No visible screen transition or state change observed (No-Op)",console.log(F.yellow(` \u26A0\uFE0F Outcome Warning: Action "${L}" produced no UI state change.`)));let Ce=await S(fe,ie);this.reporter.logStep("Action Testing",`Tapped action button "${L}" [${ee}]`,"Tap",fe,L,Ce,q.hasError?{errors:q.errors}:void 0),console.log(fe==="PASS"?F.green(`\u2714 Action interaction complete (${ee}).`):fe==="WARN"?F.yellow(`\u2714 Action interaction recorded with warning (${ee}).`):F.red("\u2716 Action interaction triggered an error.")),fe!=="FAIL"&&(this.memory.recordSuccess("TAP",L,E.resourceId||E.text||E.contentDesc||L,"text",1,void 0,U),this.memory.markScreenCrawled(U,[L]))}else console.log(F.gray("No standalone action buttons to test."));let T=Yt(H).filter(E=>!Xe(E,a,o));if(T.length>0){console.log(F.yellow(`
805
- [3b/5] Inspecting Toggle & Checkable Controls...`));let E=T[0],L=E.text||E.contentDesc||E.resourceId||"Switch/Checkbox",U=E.checked;console.log(F.magenta(`\u26A1 Found toggle: "${L}" (checked: ${U}). Testing toggle interaction...`)),this.executedStepCount++,await this.driver.tap(E.bounds.centerX,E.bounds.centerY,s),await new Promise(Te=>setTimeout(Te,1e3)),G=await this.driver.getUiHierarchy(s),H=ve(G).flat;let z=De(H);this.visitedScreenHashes.add(z);let q=Yt(H).find(Te=>Math.abs(Te.bounds.centerX-E.bounds.centerX)<25&&Math.abs(Te.bounds.centerY-E.bounds.centerY)<25),ie=q?q.checked!==U:!0,fe=await this.runErrorSentinel(e,s,"Post-Toggle"),ee=fe.hasError?"FAIL":ie?"PASS":"WARN",Ce=await S(ee,ie);this.reporter.logStep("Toggle Testing",`Toggled "${L}" (Checked: ${U} -> ${q?q.checked:"flipped"})`,"Toggle",ee,L,Ce,fe.hasError?{errors:fe.errors}:void 0),console.log(ee==="PASS"?F.green("\u2714 Toggle control flipped successfully!"):ee==="WARN"?F.yellow("\u26A0\uFE0F Toggle tapped but checked state did not flip."):F.red("\u2716 Toggle control triggered an error.")),ee!=="FAIL"&&this.memory.markScreenCrawled(z,[L])}let A=pt(H,O);if(A&&!Xe(A,a,o)){let E=A.contentDesc||A.text||A.resourceId||"Drawer Menu";console.log(F.yellow(`
806
- [3c/5] Inspecting Side Navigation Drawer...`)),console.log(F.magenta(`\u26A1 Found drawer trigger: "${E}". Opening side drawer...`));let L=De(H);this.executedStepCount++,await this.driver.tap(A.bounds.centerX,A.bounds.centerY,s),await new Promise(ee=>setTimeout(ee,1200)),await this.packageJailGuard(e,s);let U=await this.getCleanUiHierarchy(e,s);G=U.xml,H=U.flat;let z=De(H);this.visitedScreenHashes.add(z);let K=L!==z,q=await this.runErrorSentinel(e,s,"Drawer-Open"),ie=q.hasError?"FAIL":K?"PASS":"WARN",fe=await S(ie,K);if(this.reporter.logStep("Drawer Navigation",`Opened side drawer via "${E}"`,"Tap",ie,E,fe,q.hasError?{errors:q.errors}:void 0),ie==="PASS"){console.log(F.green("\u2714 Side drawer opened successfully!"));let ee=ze(H).filter(Te=>!Xe(Te,a,o)&&!Ge(Te)&&Te.bounds.centerY>A.bounds.y2);if(ee.length>0){let Te=ee[0],Je=Te.text||Te.contentDesc||Te.resourceId||"Drawer Item";console.log(F.gray(` - Testing drawer item: "${Je}"...`)),this.executedStepCount++,await this.driver.tap(Te.bounds.centerX,Te.bounds.centerY,s),await new Promise(we=>setTimeout(we,1200)),await this.packageJailGuard(e,s);let xt=await this.getCleanUiHierarchy(e,s);G=xt.xml,H=xt.flat,this.visitedScreenHashes.add(De(H))}console.log(F.gray(" Closing side drawer to restore main view...")),await this.driver.pressBack(s),await new Promise(Te=>setTimeout(Te,1e3));let Ce=await this.getCleanUiHierarchy(e,s);G=Ce.xml,H=Ce.flat,this.visitedScreenHashes.add(De(H)),console.log(F.green("\u2714 Main view restored from side drawer."))}else ie==="WARN"&&console.log(F.yellow("\u26A0\uFE0F Drawer button tapped but no visible screen change occurred."));ie!=="FAIL"&&this.memory.markScreenCrawled(L,[E])}if(r){console.log(F.yellow(`
807
- [4/5] Inspecting Lists & Master-Detail Flows...`));let E=Qn(H);if(E.length>0){console.log(F.magenta(`\u26A1 Found ${E.length} scrollable container(s). Performing vertical scroll exploration...`));let U=De(H),z=Math.round(J*.5);await this.driver.swipe(z,Math.round(O*.7),z,Math.round(O*.3),300,s),await new Promise(ee=>setTimeout(ee,1200));let K=await this.getCleanUiHierarchy(e,s);G=K.xml,H=K.flat;let q=De(H);this.visitedScreenHashes.add(q);let ie=U!==q,fe=await S("PASS",ie);this.reporter.logStep("Scroll Exploration",ie?"Scrolled vertically to discover off-screen elements":"Scrolled but content remained at edge","Swipe","PASS","SwipeUp",fe),ie&&console.log(F.green("\u2714 Vertical scroll succeeded; discovered off-screen content below fold."))}let L=$t(H).filter(U=>{if(Ge(U)){let z=U.text||U.contentDesc||"External Link";return this.externalSkippedList.push(z),!1}if(Xe(U,a,o)){let z=U.text||U.contentDesc||"List Item";return console.log(F.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive list item "${z}"`)),this.safetyHitList.push(z),!1}return!0});if(L.length>0){let U=L[0],z=U.text||U.contentDesc||`Item at (${U.bounds.centerX}, ${U.bounds.centerY})`;console.log(F.magenta(`\u26A1 Found list item: "${z}". Diving into detail view...`));let K=De(H);await this.driver.tap(U.bounds.centerX,U.bounds.centerY,s),await new Promise(qe=>setTimeout(qe,1800));let q=await this.getCleanUiHierarchy(e,s);G=q.xml,H=q.flat;let ie=De(H);this.visitedScreenHashes.add(ie);let fe=await this.runErrorSentinel(e,s,"Detail-View"),ee=K!==ie,Ce=fe.hasError?"FAIL":ee?"PASS":"WARN",Te=await S(Ce,ee);this.reporter.logStep("List Dive",`Dived into detail view for "${z}" [${ee?"Navigated":"Unchanged"}]`,"Tap",Ce,z,Te,fe.hasError?{errors:fe.errors}:void 0),console.log(Ce==="PASS"?F.green("\u2714 Detail screen loaded."):Ce==="WARN"?F.yellow("\u26A0\uFE0F Detail view tapped but no navigation detected."):F.red("\u2716 Detail view triggered an error."));let Je=We(q.flat,I.screen.fallbackHeight,I.screen.fallbackWidth,!0);Je&&Je.bounds.width>0?(console.log(F.gray(`Navigating back via in-app back button "${Je.text||Je.contentDesc||Je.className}"...`)),await this.driver.tap(Je.bounds.centerX,Je.bounds.centerY,s)):(console.log(F.gray("Navigating back to main view...")),await this.driver.pressKey(4,s)),await new Promise(qe=>setTimeout(qe,1200));let xt=await this.driver.getForegroundPackage(s);xt&&!xt.includes(e)&&(console.log(F.yellow(`Restoring ${e} foreground after back navigation...`)),await this.driver.launchApp(e,s),await new Promise(qe=>setTimeout(qe,2e3)));let we=await this.getCleanUiHierarchy(e,s);G=we.xml,H=we.flat,this.visitedScreenHashes.add(De(H));let Ie=await this.runErrorSentinel(e,s,"Post-Back-Restore"),dt=Ie.hasError?"FAIL":"PASS",ds=await S(dt,!0);this.reporter.logStep("List Dive","Navigated back to master screen","Back",dt,"KEYCODE_BACK",ds,Ie.hasError?{errors:Ie.errors}:void 0),dt==="PASS"&&console.log(F.green("\u2714 Master view restored."))}else console.log(F.gray("No list items found for master-detail exploration."))}}console.log(F.yellow(`
808
- [5/5] Inspecting Navigation Tabs...`));let _=await this.driver.getForegroundPackage(s);_&&!_.includes(e)&&(console.log(F.gray(`Restoring ${e} foreground for tab traversal...`)),await this.driver.launchApp(e,s),await new Promise(N=>setTimeout(N,2e3))),await this.dismissSoftKeyboard(s);let P=await this.getCleanUiHierarchy(e,s);G=P.xml,H=P.flat;let ce=Ct(H,void 0,e);if(ce.length>1){console.log(F.magenta(`\u26A1 Found ${ce.length} navigation tab(s). Traversal sequence initiated...`));let N=ce.slice(0,n),M=1;for(let T of N){let A=T.text||T.contentDesc||`Tab #${M}`;console.log(F.gray(` - Visiting ${A}...`)),await this.dismissSoftKeyboard(s);let E=Math.min(T.bounds.centerY,T.bounds.y2-4);this.executedStepCount++,await this.driver.tap(T.bounds.centerX,E,s),await new Promise(ie=>setTimeout(ie,1500)),await this.packageJailGuard(e,s);let L=await this.getCleanUiHierarchy(e,s);G=L.xml,H=L.flat;let U=De(H);this.visitedScreenHashes.add(U),this.memory.markScreenCrawled(U,[A]);let z=await this.runErrorSentinel(e,s,`Tab-${A}`),K=z.hasError?"FAIL":"PASS",q=await S(K,!0);if(this.reporter.logStep("Tab Traversal",`Navigated to tab "${A}"`,"Tap",K,A,q,z.hasError?{errors:z.errors}:void 0),console.log(K==="PASS"?F.green(` \u2714 Tab "${A}" loaded cleanly.`):F.red(` \u2716 Tab "${A}" triggered errors.`)),this.executedStepCount<d&&this.visitedScreenHashes.size<l){let ie=[],fe=O*.08,ee=O*.88,Ce=we=>we.bounds.centerY>fe&&we.bounds.centerY<ee&&we.clickable&&we.bounds.width>30&&we.bounds.height>20&&ce.every(Ie=>Math.abs(Ie.bounds.centerX-we.bounds.centerX)>40||Math.abs(Ie.bounds.centerY-we.bounds.centerY)>40),Te=$t(H,{width:J,height:O}).filter(Ce);for(let we of Te.slice(0,3))ie.push({node:we,type:"list"});let Je=ze(H).filter(we=>!(!Ce(we)||pt([we],O)));for(let we of Je.slice(0,4))ie.push({node:we,type:"button"});let xt=Yt(H).filter(Ce);for(let we of xt.slice(0,2))ie.push({node:we,type:"toggle"});for(let we of ie){if(this.executedStepCount>=d||this.visitedScreenHashes.size>=l)break;let Ie=we.node;if(Ge(Ie)){this.externalSkippedList.push(Ie.text||Ie.contentDesc||"External Link");continue}if(Xe(Ie,a,o)){let Ut=Ie.text||Ie.contentDesc||"Destructive Action";this.safetyHitList.push(Ut);continue}let dt=Ie.text||Ie.contentDesc||Ie.resourceId||`${we.type} element`;console.log(F.gray(` Sub-crawl [${A}]: Exercising ${we.type} "${dt.slice(0,40)}"...`)),this.executedStepCount++,await this.driver.tap(Ie.bounds.centerX,Ie.bounds.centerY,s),await new Promise(Ut=>setTimeout(Ut,1200)),await this.dismissSoftKeyboard(s),await this.packageJailGuard(e,s);let ds=await this.getCleanUiHierarchy(e,s);G=ds.xml,H=ds.flat;let qe=De(H),Mi=!this.visitedScreenHashes.has(qe);this.visitedScreenHashes.add(qe);let Ds=await this.runErrorSentinel(e,s,`Sub-${dt.slice(0,30)}`),Fn=Ds.hasError?"FAIL":"PASS",Oi=await S(Fn,Mi);if(this.reporter.logStep("Tab Sub-Crawl",`Tested ${we.type} "${dt}" under tab "${A}"`,"Tap",Fn,dt,Oi,Ds.hasError?{errors:Ds.errors}:void 0),qe!==U){console.log(F.cyan(` \u{1F50E} Screen transitioned. Discovered new screen state [${qe.slice(0,8)}]`));let Ut=ze(H).filter(et=>Ce(et)&&!Xe(et,a,o)&&!Ge(et));if(Ut.length>0&&this.executedStepCount<d){let et=Ut[0],Fi=et.text||et.contentDesc||"Child Action";console.log(F.gray(` Exercising child action "${Fi.slice(0,30)}"...`)),this.executedStepCount++,await this.driver.tap(et.bounds.centerX,et.bounds.centerY,s),await new Promise(_i=>setTimeout(_i,1e3)),await this.dismissSoftKeyboard(s)}console.log(F.gray(` Restoring ${A} master view via Back...`)),await this.driver.pressBack(s),await new Promise(et=>setTimeout(et,1e3)),await this.dismissSoftKeyboard(s);let _n=await this.getCleanUiHierarchy(e,s);G=_n.xml,H=_n.flat}}}M++}console.log(F.green("\u2714 Navigation tabs traversed successfully!"))}else console.log(F.gray("Single-screen or tabs not detected at screen footer."));for(let N of this.visitedScreenHashes)this.memory.markScreenCrawled(N);await this.memory.save();let ne=this.memory.getStats();if(this.reporter.logStep("Coverage Audit",`State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s). Memory Engine cached ${ne.crawledScreens} crawled screen(s).`,"Verify","PASS"),await this.runErrorSentinel(e,s,"Final-State"),j){console.log(F.cyan("\u{1F3AC} Finalizing exploration screen recording..."));let N=Hs.resolve(process.cwd(),"output"),M=Hs.join(N,`${e}-exploration.mp4`),T=await this.driver.stopScreenRecording(j,M,s);T&&ur.existsSync(T)&&(this.reporter.setVideoPath?.(`${e}-exploration.mp4`),console.log(F.green(`\u2714 Screen recording saved to: output/${e}-exploration.mp4`))),j=null}let g=await this.reporter.generateReport(),D=g.failedSteps===0&&g.errorsDetected.length===0;console.log(D?F.bold.green(`
809
- \u2728 PromptTest Exploration Complete \u2014 All Steps Passed!`):F.bold.red(`
810
- \u26A0\uFE0F PromptTest Exploration Complete \u2014 Issues Detected!`)),console.log(F.white(`Summary: ${g.passedSteps} passed, ${g.warnSteps} warnings, ${g.failedSteps} failed, ${g.errorsDetected.length} errors detected.`)),this.safetyHitList.length>0&&console.log(F.cyan(`\u{1F6E1}\uFE0F Safety Shield: Blocked ${this.safetyHitList.length} destructive action(s) [${this.safetyHitList.join(", ")}].`)),console.log(F.cyan(`\u{1F5FA}\uFE0F State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s).`)),console.log(F.cyan(`\u{1F9E0} Memory Engine Coverage: ${ne.crawledScreens} crawled screen(s) indexed offline.`)),console.log(F.cyan(`Full Audit Report generated at: output/prompttest-report.md
811
- `))}finally{if(j)try{await this.driver.stopScreenRecording(j,Hs.join("output",`${e}-exploration.mp4`),s)}catch{}C&&Lt(C)}}async runErrorSentinel(t,e,s){let n=[];try{let[r,i]=await Promise.all([this.driver.checkCrashes(t,e),this.driver.getUiHierarchy(e)]);if(r){let l=`Crash in ${s}: ${r.slice(0,300)}`;console.log(F.bold.red(`\u{1F6A8} FATAL EXCEPTION DETECTED in ${t} [${s}]!`)),this.reporter.logError(l),n.push(l)}let{flat:o}=ve(i),a=Gn(o);if(a.length>0)for(let l of a){let d=l.text||l.contentDesc,p=`UI Error dialog in ${s}: "${d}"`;console.log(F.bold.yellow(`\u26A0\uFE0F Error Dialog/Banner on screen [${s}]: "${d}"`)),this.reporter.logError(p),n.push(p)}}catch{}return{hasError:n.length>0,errors:n}}},bs=new Mt});import bt from"fs";import ui from"path";var Bs,hi,gi=de(()=>{"use strict";Bs=class{checkpointDir;constructor(t){this.checkpointDir=t||ui.resolve(process.cwd(),"output","checkpoints"),bt.existsSync(this.checkpointDir)||bt.mkdirSync(this.checkpointDir,{recursive:!0})}filePath(t){let e=t.replace(/[^a-zA-Z0-9._-]/g,"_");return ui.join(this.checkpointDir,`${e}.checkpoint.json`)}async save(t,e,s,n){let r={specFile:t,packageName:e,lastPassedStep:s,totalSteps:n,savedAt:new Date().toISOString()},i=this.filePath(t)+`.tmp.${Date.now()}`;await bt.promises.writeFile(i,JSON.stringify(r,null,2),"utf-8"),await bt.promises.rename(i,this.filePath(t))}async load(t){let e=this.filePath(t);if(!bt.existsSync(e))return null;try{let s=await bt.promises.readFile(e,"utf-8");return JSON.parse(s)}catch{return null}}async clear(t){let e=this.filePath(t);bt.existsSync(e)&&await bt.promises.unlink(e)}async getResumeIndex(t){let e=await this.load(t);return e?e.lastPassedStep+1:0}},hi=new Bs});import Ee from"fs";import yt from"path";import{PNG as Ot}from"pngjs";import hr from"pixelmatch";var Us,Ft,js,os=de(()=>{"use strict";Us={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"}},Ft=class{baselineDir;defaultThreshold;constructor(t,e=.02){this.baselineDir=t||yt.resolve(process.cwd(),"output","baselines"),this.defaultThreshold=e,Ee.existsSync(this.baselineDir)||Ee.mkdirSync(this.baselineDir,{recursive:!0})}filePath(t,e){let s=t.replace(/[^a-zA-Z0-9._-]/g,"_");return yt.join(this.baselineDir,`${s}_step${String(e).padStart(3,"0")}.png`)}diffFilePath(t,e){let s=t.replace(/[^a-zA-Z0-9._-]/g,"_");return yt.join(this.baselineDir,`${s}_step${String(e).padStart(3,"0")}_diff.png`)}masksFilePath(t,e){let s=t.replace(/[^a-zA-Z0-9._-]/g,"_");return yt.join(this.baselineDir,`${s}_step${String(e).padStart(3,"0")}_masks.json`)}async getMasks(t,e){let s=this.masksFilePath(t,e);if(!Ee.existsSync(s))return[];try{let n=await Ee.promises.readFile(s,"utf-8"),r=JSON.parse(n);return Array.isArray(r)?r:r.masks||[]}catch{return[]}}async saveMasks(t,e,s){let n=this.masksFilePath(t,e);return await Ee.promises.writeFile(n,JSON.stringify(s,null,2),"utf-8"),n}async saveBaseline(t,e,s){let n=this.filePath(t,e),r=n+`.tmp.${Date.now()}`;return await Ee.promises.writeFile(r,s),await Ee.promises.rename(r,n),n}hasBaseline(t,e){return Ee.existsSync(this.filePath(t,e))}async compare(t,e,s,n){let r=typeof n=="number"?{threshold:n}:n||{},i=this.filePath(t,e),o=yt.join(this.baselineDir,`${t.replace(/[^a-zA-Z0-9._-]/g,"_")}_step${String(e).padStart(3,"0")}_current.png`);await Ee.promises.writeFile(o,s);let a=r.mode?Us[r.mode]:void 0,l=r.threshold??a?.threshold??this.defaultThreshold,d=r.pixelmatchThreshold??a?.pixelmatchThreshold??.1;if(!Ee.existsSync(i)){await this.saveBaseline(t,e,s);let O=Ot.sync.read(s),V=O.width*O.height;return{stepName:`Step ${e}`,baselineFile:i,currentFile:o,diffRatio:0,diffPixels:0,totalPixels:V,passed:!0,threshold:l}}let p=await Ee.promises.readFile(i),h=Ot.sync.read(p),u=Ot.sync.read(s),m=Math.max(h.width,u.width),f=Math.max(h.height,u.height),x=m*f,v=this.fitToCanvas(h,m,f),S=this.fitToCanvas(u,m,f),y=r.maskRegions??await this.getMasks(t,e);y&&y.length>0&&(this.applyMasks(v,y),this.applyMasks(S,y));let k=new Ot({width:m,height:f}),C=hr(v.data,S.data,k.data,m,f,{threshold:d,includeAA:!1}),j=x>0?C/x:0,$=j<=l,J;if($){let O=this.diffFilePath(t,e);if(Ee.existsSync(O))try{await Ee.promises.unlink(O)}catch{}}else{J=this.diffFilePath(t,e);let O=Ot.sync.write(k);await Ee.promises.writeFile(J,O)}return{stepName:`Step ${e}`,baselineFile:i,currentFile:o,diffRatio:j,diffPixels:C,totalPixels:x,passed:$,threshold:l,diffImagePath:J}}fitToCanvas(t,e,s){if(t.width===e&&t.height===s)return t;let n=new Ot({width:e,height:s});return n.data.fill(0),Ot.bitblt(t,n,0,0,t.width,t.height,0,0),n}applyMasks(t,e){for(let s of e){let n=s.width<=1&&s.height<=1||s.x<=1&&s.y<=1&&(s.width<1||s.height<1),r=Math.round(n?s.x*t.width:s.x),i=Math.round(n?s.y*t.height:s.y),o=Math.round(n?s.width*t.width:s.width),a=Math.round(n?s.height*t.height:s.height),l=Math.max(0,i),d=Math.min(t.height,i+a),p=Math.max(0,r),h=Math.min(t.width,r+o);for(let u=l;u<d;u++)for(let m=p;m<h;m++){let f=t.width*u+m<<2;t.data[f]=0,t.data[f+1]=0,t.data[f+2]=0,t.data[f+3]=0}}}async approveBaseline(t,e,s){let n;if(s)n=await this.saveBaseline(t,e,s);else{let i=t.replace(/[^a-zA-Z0-9._-]/g,"_"),o=yt.join(this.baselineDir,`${i}_step${String(e).padStart(3,"0")}_current.png`);if(!Ee.existsSync(o))throw new Error(`No current screenshot found to approve for step ${e}`);let a=await Ee.promises.readFile(o);n=await this.saveBaseline(t,e,a)}let r=this.diffFilePath(t,e);if(Ee.existsSync(r))try{await Ee.promises.unlink(r)}catch{}return n}async clearBaselines(t){let e=t.replace(/[^a-zA-Z0-9._-]/g,"_");if(!Ee.existsSync(this.baselineDir))return;let s=await Ee.promises.readdir(this.baselineDir);await Promise.all(s.filter(n=>n.startsWith(e+"_step")).map(n=>Ee.promises.unlink(yt.join(this.baselineDir,n)).catch(()=>{})))}async deleteStepBaseline(t,e){let n=`${t.replace(/[^a-zA-Z0-9._-]/g,"_")}_step${String(e).padStart(3,"0")}`;if(!Ee.existsSync(this.baselineDir))return;let r=await Ee.promises.readdir(this.baselineDir);await Promise.all(r.filter(i=>i.startsWith(n)).map(i=>Ee.promises.unlink(yt.join(this.baselineDir,i)).catch(()=>{})))}},js=new Ft});import ye from"chalk";import mi from"vm";function le(c){return new Promise(t=>setTimeout(t,c))}function gr(c){let t=c.screenDimensions.height,e=c.navBarHeight>0?t-c.navBarHeight:t,s=c.navBarHeight>0?e*.94:t*I.gestures.bottomNavThresholdRatio;return{effectiveHeight:e,bottomThreshold:s}}var Ws,Ys,ys,Vs,Js,zs,Xs,Gs,Ks,Qs,Zs,qs,en,tn,sn,nn,rn,on,an,cn,ln,dn,pn,un,hn,gn,mr,fi,mn=de(()=>{"use strict";Be();Vt();st();Ws=class{type="TAP";async execute(t,e){let s=t.target,{width:n,height:r}=e.screenDimensions,i=Math.round(n*.5),{flat:o,matched:a}=await e.waitForTarget(s,e.serial,I.polling.defaultTimeoutMs,t);if(a.length===0&&e.upcomingSteps&&e.upcomingSteps.length>0&&!o.some(v=>v.password===!0||v.className.includes("EditText")&&o.some(S=>S.text?.toLowerCase().includes("password")))){let v=e.upcomingSteps.findIndex(y=>y.type==="VERIFY"&&y.target&&e.matchNodes(o,y.target,y).length>0);if(v!==-1)return{fastForwarded:!0,reason:`Screen already displays "${e.upcomingSteps[v].target}"`,skipCount:v};let S=e.upcomingSteps.findIndex(y=>y.type==="TAP"&&y.target&&e.matchNodes(o,y.target,y).length>0);if(S!==-1)return{fastForwarded:!0,reason:`Upcoming action "${e.upcomingSteps[S].target}" is directly actionable`,skipCount:S}}if(a.length===0){await e.driver.swipe(i,Math.round(r*.75),i,Math.round(r*.35),300,e.serial),await le(350),e.setLastHierarchy(null);let x=await e.waitForTarget(s,e.serial,2e3,t);o=x.flat,a=x.matched}if(a.length===0)throw e.buildDiagnosticError(s,o,`Element matching "${s}" not found on current screen.`);let l=a.find(x=>x.clickable)||a[0];if(await e.ensureInViewport(l,e.serial)){let x=await e.waitForTarget(s,e.serial,1500,t);x.matched.length>0&&(o=x.flat,a=x.matched,l=a.find(v=>v.clickable)||a[0])}let p=l.bounds.centerX,{effectiveHeight:h}=gr(e),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*.6&&typeof e.driver.isKeyboardVisible=="function")try{await e.driver.isKeyboardVisible(e.serial)&&(console.log(ye.gray(` [Keyboard] Target occluded by soft keyboard (Y=${u} > ${Math.round(r*.6)}). Dismissing...`)),await e.driver.pressBack(e.serial),await le(300),e.setLastHierarchy(null))}catch{}let m=l.contentDesc||l.text||l.resourceId;console.log(ye.gray(` [Tap Target] (${p}, ${u}) for "${m||s}"`));let f=e.lastHierarchy?.hash;await e.driver.tap(p,u,e.serial),await le(I.settle.tapMs),e.setLastHierarchy(null),m&&ue(m)!==ue(s)&&e.memory.recordSuccess("TAP",s,m,l.contentDesc?"contentDesc":l.text?"text":"resourceId",1,void 0,f)}},Ys=class{type="LONG_PRESS";async execute(t,e){let s=t.target,{flat:n,matched:r}=await e.waitForTarget(s,e.serial,I.polling.defaultTimeoutMs,t);if(r.length===0)throw e.buildDiagnosticError(s,n,`Element matching "${s}" not found for long-press.`);let i=r[0];if(await e.ensureInViewport(i,e.serial)){let a=await e.waitForTarget(s,e.serial,1500,t);a.matched.length>0&&(i=a.matched[0])}await e.driver.swipe(i.bounds.centerX,i.bounds.centerY,i.bounds.centerX,i.bounds.centerY,I.gestures.longPressDurationMs,e.serial),await le(300),e.setLastHierarchy(null)}},ys=class{type="TYPE";async execute(t,e){let s=t.target,n=t.value??"",r=[],i=null;if(e.lastHierarchy&&Date.now()-e.lastHierarchy.timestamp<I.polling.hierarchyCacheTtlMs){let p=e.findTargetInputField(e.lastHierarchy.flat,s,n);p&&(r=e.lastHierarchy.flat,i=p)}if(i||(r=(await e.getCleanHierarchy(e.serial)).flat,i=e.findTargetInputField(r,s,n)),!i){let p=Date.now();for(;Date.now()-p<2e3&&(await le(200),r=(await e.getCleanHierarchy(e.serial)).flat,i=e.findTargetInputField(r,s,n),!i););}if(!i&&e.upcomingSteps&&e.upcomingSteps.length>0&&!r.some(h=>h.className.includes("EditText"))){let h=e.upcomingSteps.findIndex(u=>u.type==="VERIFY"&&u.target&&e.matchNodes(r,u.target,u).length>0);if(h!==-1)return{fastForwarded:!0,reason:`Screen already displays "${e.upcomingSteps[h].target}" and form is completed`,skipCount:h}}if(!i)throw e.buildDiagnosticError(s,r,`Input field matching "${s}" not found.`);if(i.text&&i.text===n){console.log(ye.gray(` [Input Field] Already contains desired value "${n}". Skipping typing.`));return}if(await e.ensureInViewport(i,e.serial)){r=(await e.getCleanHierarchy(e.serial)).flat;let h=e.findTargetInputField(r,s,n);h&&(i=h)}let{height:a}=e.screenDimensions;if(i.bounds.centerY>a*.6&&typeof e.driver.isKeyboardVisible=="function")try{await e.driver.isKeyboardVisible(e.serial)&&(console.log(ye.gray(" [Keyboard] Input field occluded by soft keyboard. Dismissing before focus...")),await e.driver.pressBack(e.serial),await le(300),e.setLastHierarchy(null))}catch{}let l=e.lastHierarchy?.hash;await e.driver.tap(i.bounds.centerX,i.bounds.centerY,e.serial),await le(I.settle.inputFocusMs),await e.driver.clearField(e.serial),await le(I.settle.inputClearMs),await e.driver.inputText(n,e.serial),await le(I.settle.inputDoneMs),e.setLastHierarchy(null);let d=i.contentDesc||i.resourceId;d&&ue(d)!==ue(s)&&e.memory.recordSuccess("TYPE",s,d,i.contentDesc?"contentDesc":"resourceId",1,void 0,l)}},Vs=class{type="CLEAR";async execute(t,e){let s=t.target,n=[],r=null;if(e.lastHierarchy&&Date.now()-e.lastHierarchy.timestamp<I.polling.hierarchyCacheTtlMs&&(r=e.findTargetInputField(e.lastHierarchy.flat,s),r&&(n=e.lastHierarchy.flat)),r||(n=(await e.getCleanHierarchy(e.serial)).flat,r=e.findTargetInputField(n,s)),!r){let o=e.matchNodes(n,s,t);o.length>0&&(r=o[0])}if(!r)throw e.buildDiagnosticError(s,n,`Input field matching "${s}" not found to clear.`);await e.driver.tap(r.bounds.centerX,r.bounds.centerY,e.serial),await le(300);let i=e.driver;typeof i.clearField=="function"?await i.clearField(e.serial):typeof i.clearInput=="function"&&await i.clearInput(50,e.serial),await le(200),e.setLastHierarchy(null)}},Js=class{type="VERIFY";async execute(t,e){let s=t.target,n=t.expectedState||"visible";if(t.negative===!0||n==="absent"||n==="hidden"){let h=await e.getCleanHierarchy(e.serial);if(e.matchNodes(h.flat,s,t).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}=e.screenDimensions,a=Math.round(i*.5),l=!1,d=[],p;if(e.lastHierarchy&&Date.now()-e.lastHierarchy.timestamp<I.polling.verifyCacheTtlMs&&(p=e.matchNodes(e.lastHierarchy.flat,s,t)[0],p&&(d=e.lastHierarchy.flat,l=!0,e.lastHierarchy.timestamp=Date.now())),!l){let h=Date.now();for(;Date.now()-h<I.polling.extendedTimeoutMs;){if(d=(await e.getCleanHierarchy(e.serial)).flat,p=e.matchNodes(d,s,t)[0],p){l=!0,e.lastHierarchy&&(e.lastHierarchy.timestamp=Date.now()),await le(400);break}await le(200)}}if(l||(await e.driver.swipe(a,Math.round(o*.75),a,Math.round(o*.35),300,e.serial),await le(400),d=(await e.getCleanHierarchy(e.serial)).flat,p=e.matchNodes(d,s,t)[0],p&&(l=!0)),!l){if(e.upcomingSteps&&e.upcomingSteps.length>0&&!d.some(u=>u.password===!0||u.className.includes("EditText")&&d.some(m=>m.text?.toLowerCase().includes("password")))){let u=e.upcomingSteps.findIndex(m=>m.type==="VERIFY"&&m.target&&e.matchNodes(d,m.target,m).length>0);if(u!==-1)return{fastForwarded:!0,reason:`Screen is already on "${e.upcomingSteps[u].target}"`,skipCount:u}}throw await e.driver.swipe(a,Math.round(o*.35),a,Math.round(o*.75),300,e.serial),await le(300),e.buildDiagnosticError(s,d,`Verification assertion failed: "${s}" is not visible on screen.`)}if(p&&n==="disabled"&&p.enabled)throw new Error(`State assertion failed: "${s}" was expected to be DISABLED, but is enabled.`);if(p&&n==="enabled"&&!p.enabled)throw new Error(`State assertion failed: "${s}" was expected to be ENABLED, but is disabled.`)}},zs=class{type="BACK";async execute(t,e){await e.driver.pressKey(4,e.serial),await le(I.settle.backMs),e.setLastHierarchy(null)}},Xs=class{type="SCROLL";async execute(t,e){let{width:s,height:n}=e.screenDimensions,r=Math.round(s*.5);if(t.target&&t.target!=="up"&&t.target!=="down"){let o=t.target,a=t.direction||"down",l=!1;for(let d=0;d<5;d++){let{flat:p}=await e.getCleanHierarchy(e.serial),h=e.matchNodes(p,o,t);if(h.length>0){await e.ensureInViewport(h[0],e.serial),l=!0;break}a==="up"?await e.driver.swipe(r,Math.round(n*.35),r,Math.round(n*.75),300,e.serial):await e.driver.swipe(r,Math.round(n*.75),r,Math.round(n*.35),300,e.serial),await le(I.settle.scrollMs),e.setLastHierarchy(null)}if(!l){let{flat:d}=await e.getCleanHierarchy(e.serial);throw e.buildDiagnosticError(o,d,`Element "${o}" not found after scrolling ${t.direction||"down"}.`)}}else t.target==="up"||t.direction==="up"?await e.driver.swipe(r,Math.round(n*.35),r,Math.round(n*.75),300,e.serial):await e.driver.swipe(r,Math.round(n*.75),r,Math.round(n*.35),300,e.serial),await le(I.settle.scrollMs),e.setLastHierarchy(null)}},Gs=class{type="SWIPE";async execute(t,e){let{width:s,height:n}=e.screenDimensions,r=Math.round(s*.5),i=t.direction||"left";if(t.target){let{matched:p}=await e.waitForTarget(t.target,e.serial,2500,t);if(p.length>0){let h=p[0],u=i==="left"?h.bounds.x2-40:i==="right"?h.bounds.x1+40:h.bounds.centerX,m=i==="left"?h.bounds.x1+40:i==="right"?h.bounds.x2-40:h.bounds.centerX,f=i==="up"?h.bounds.y2-40:i==="down"?h.bounds.y1+40:h.bounds.centerY,x=i==="up"?h.bounds.y1+40:i==="down"?h.bounds.y2-40:h.bounds.centerY;await e.driver.swipe(u,f,m,x,I.gestures.swipeDurationMs,e.serial),await le(400),e.setLastHierarchy(null);return}}let o=i==="left"?Math.round(s*.8):i==="right"?Math.round(s*.2):r,a=i==="left"?Math.round(s*.2):i==="right"?Math.round(s*.8):r,l=Math.round(i==="up"?n*.75:i==="down"?n*.35:n*.5),d=Math.round(i==="up"?n*.35:i==="down"?n*.75:n*.5);await e.driver.swipe(o,l,a,d,I.gestures.swipeDurationMs,e.serial),await le(400),e.setLastHierarchy(null)}},Ks=class{type="TOGGLE";async execute(t,e){let s=t.target,{flat:n,matched:r}=await e.waitForTarget(s,e.serial,I.polling.defaultTimeoutMs,t);if(r.length===0)throw e.buildDiagnosticError(s,n,`Toggle element matching "${s}" not found.`);let i=r.find(a=>a.clickable)||r[0];if(await e.ensureInViewport(i,e.serial)){let a=await e.waitForTarget(s,e.serial,1500,t);a.matched.length>0&&(i=a.matched.find(l=>l.clickable)||a.matched[0])}if(t.desiredState!==void 0&&i.checked!==void 0&&i.checked===t.desiredState){console.log(ye.gray(` [Toggle] "${s}" is already in desired state (${t.desiredState?"ON":"OFF"}).`));return}await e.driver.tap(i.bounds.centerX,i.bounds.centerY,e.serial),await le(I.settle.toggleMs),e.setLastHierarchy(null)}},Qs=class{type="SELECT";async execute(t,e){let s=t.target,n=t.value,{flat:r,matched:i}=await e.waitForTarget(s,e.serial,I.polling.defaultTimeoutMs,t);if(i.length===0)throw e.buildDiagnosticError(s,r,`Dropdown / picker element matching "${s}" not found.`);let o=i.find(d=>d.clickable)||i[0];await e.driver.tap(o.bounds.centerX,o.bounds.centerY,e.serial),await le(I.settle.selectMs),e.setLastHierarchy(null);let a=await e.waitForTarget(n,e.serial,I.polling.defaultTimeoutMs);if(a.matched.length===0)throw e.buildDiagnosticError(n,a.flat,`Dropdown option "${n}" not found.`);let l=a.matched[0];await e.driver.tap(l.bounds.centerX,l.bounds.centerY,e.serial),await le(400),e.setLastHierarchy(null)}},Zs=class{type="LAUNCH_APP";async execute(t,e){let s=t.target;if(!s)throw new Error("Target package name required to launch app.");await e.driver.launchApp(s,e.serial),await le(I.settle.launchMs),e.setLastHierarchy(null)}},qs=class{type="RESTART_APP";async execute(t,e){let s=t.target;if(!s)throw new Error("Target package name required to restart app.");await e.driver.restartApp(s,e.serial),await le(I.settle.launchMs),e.setLastHierarchy(null)}},en=class{type="TERMINATE_APP";async execute(t,e){let s=t.target||await e.driver.getForegroundPackage(e.serial);s&&await e.driver.forceStop(s,e.serial),await le(I.settle.stopMs),e.setLastHierarchy(null)}},tn=class{type="CLEAR_DATA";async execute(t,e){let s=t.target;if(!s)throw new Error("Target package name required to clear data.");await e.driver.clearAppData(s,e.serial),await le(1e3),e.setLastHierarchy(null)}},sn=class{type="GRANT_PERMISSION";async execute(t,e){let s=t.value,n=t.target||await e.driver.getForegroundPackage(e.serial);if(!n)throw new Error("Foreground package not detected for grant permission.");await e.driver.grantPermission(n,s,e.serial),await le(400)}},nn=class{type="DEEP_LINK";async execute(t,e){let s=t.target;await e.driver.openDeepLink(s,e.serial),await le(I.settle.launchMs),e.setLastHierarchy(null)}},rn=class{type="WAIT";async execute(t,e){await le(t.durationMs||1e3)}},on=class{type="IF_VISIBLE";async execute(t,e){let s=t.conditionTarget||t.target;if(!s)return;let n=!1;try{let{matched:r}=await e.waitForTarget(s,e.serial,1500,t);n=r.length>0}catch{n=!1}n?(console.log(ye.cyan(` [Conditional] Condition met: "${s}" is visible.`)),t.thenSteps&&t.thenSteps.length>0&&e.executeChildSteps&&await e.executeChildSteps(t.thenSteps,e)):(console.log(ye.gray(` [Conditional] Condition not met: "${s}" is not visible.`)),t.elseSteps&&t.elseSteps.length>0&&e.executeChildSteps&&await e.executeChildSteps(t.elseSteps,e))}},an=class{type="LOOP";async execute(t,e){let s=I.flow?.maxLoopIterations||50,n=Math.min(t.loopCount||1,s),r=t.childSteps||[];if(!(r.length===0||!e.executeChildSteps)){console.log(ye.cyan(` [Loop] Starting loop iteration (1 to ${n})...`));for(let i=1;i<=n;i++){t.loopVariable&&e.variables.set(t.loopVariable,String(i)),e.loopStack.push({current:i,total:n,variable:t.loopVariable});try{await e.executeChildSteps(r,e)}finally{e.loopStack.pop()}}}}},cn=class{type="WAIT_UNTIL";async execute(t,e){let s=t.target,n=t.durationMs||I.flow?.waitUntilTimeoutMs||1e4,r=t.expectedState||"visible";console.log(ye.gray(` [Wait Until] Waiting for "${s}" to be ${r} (up to ${n}ms)...`));let i=Date.now(),o=!1;for(;Date.now()-i<n;){try{let{matched:a}=await e.waitForTarget(s,e.serial,1e3,t);if(a.length>0){let l=a[0];if(r==="enabled"&&!l.enabled){await le(250);continue}o=!0;break}}catch{}await le(250)}if(!o){let{flat:a}=await e.getCleanHierarchy(e.serial);throw e.buildDiagnosticError(s,a,`WaitUntil assertion failed: "${s}" did not become ${r} within ${n}ms.`)}}},ln=class{type="SET_VARIABLE";async execute(t,e){let s=t.variableName,n=t.variableExpression||"";n=Dt(n,Object.fromEntries(e.variables));let r=Vn(n);e.variables.set(s,r),console.log(ye.cyan(` [Variable] $${s} = "${r}"`))}},dn=class{type="DEVICE_STATE";async execute(t,e){let s=t.deviceAction,n=t.deviceStateValue;switch(s){case"wifi":{let r=n===!0||n==="on"||n==="enable";console.log(ye.cyan(` [Device] Setting Wi-Fi: ${r?"ENABLED":"DISABLED"}`)),await e.driver.setWifiEnabled(r,e.serial);break}case"airplane":{let r=n===!0||n==="on"||n==="enable";console.log(ye.cyan(` [Device] Setting Airplane Mode: ${r?"ENABLED":"DISABLED"}`)),await e.driver.setAirplaneMode(r,e.serial);break}case"orientation":{let r=n==="landscape"?"landscape":"portrait";console.log(ye.cyan(` [Device] Setting Orientation: ${r.toUpperCase()}`)),await e.driver.setOrientation(r,e.serial);break}case"notifications":{let r=n==="open"||n==="expand"||n===!0;console.log(ye.cyan(` [Device] ${r?"Expanding":"Collapsing"} Notification Shade`)),r?await e.driver.openNotificationShade(e.serial):await e.driver.collapseNotificationShade(e.serial);break}default:throw new Error(`Unsupported device action: ${s}`)}await le(400),e.setLastHierarchy(null)}},pn=class{type="RUN_JS";async execute(t,e){let s=t.jsCode||t.value||"";if(!s)return;console.log(ye.cyan(" [JS Hook] Executing sandboxed JavaScript snippet..."));let n={vars:Object.fromEntries(e.variables),setVar:(i,o)=>{e.variables.set(i,String(o))},getVar:i=>e.variables.get(i),console:{log:(...i)=>console.log(ye.gray(" [JS Output]",...i)),error:(...i)=>console.error(ye.red(" [JS Error]",...i))}},r=mi.createContext(n);mi.runInContext(s,r,{timeout:3e3});for(let[i,o]of Object.entries(n.vars))e.variables.set(i,String(o))}},un=class{type="ENTER_OTP";async execute(t,e){console.log(ye.cyan(" [OTP Capture] Detecting verification code..."));let s=null;if(process.env.PROMPTTEST_MOCK_OTP)s=process.env.PROMPTTEST_MOCK_OTP.trim(),console.log(ye.gray(` [OTP Capture] Captured code from environment: ${s}`));else try{let{stdout:n}=await e.driver.execAdb(["shell","logcat","-d","-t","200","-s","SMSDispatcher:V","SmsMessage:V","NotificationService:V","ActivityManager:I"],e.serial),r=n.match(/(?:code|otp|pin|verification)[^0-9\r\n]{0,35}(\d{4,8})\b/i)||n.match(/\b(\d{4,8})\s*(?:is your verification|is your otp)/i);r&&(s=r[1],console.log(ye.gray(` [OTP Capture] Captured code from logcat: ${s}`)))}catch{}if(s||(s="123456",console.log(ye.yellow(` [OTP Capture] No live code detected; using simulated OTP: ${s}`))),t.target){let n={originalText:`Type '${s}' into '${t.target}'`,type:"TYPE",target:t.target,value:s};return new ys().execute(n,e)}else{let{flat:n}=await e.getCleanHierarchy(e.serial),r=e.findTargetInputField(n,"code",s)||n.find(i=>i.className.includes("EditText"));r?(await e.driver.tap(r.bounds.centerX,r.bounds.centerY,e.serial),await le(250),await e.driver.inputText(s,e.serial)):await e.driver.inputText(s,e.serial)}}},hn=class{type="API_REQUEST";async execute(t,e){let s=t.apiMethod||"GET",n=t.apiUrl||"";if(!n)throw new Error("API_REQUEST requires a valid target URL.");console.log(ye.cyan(` [API Request] ${s} ${n}...`));let r={method:s,headers:{"Content-Type":"application/json",Accept:"application/json"}};t.apiBody&&(s==="POST"||s==="PUT"||s==="PATCH")&&(r.body=t.apiBody);let i=await fetch(n,r);if(t.apiExpectedStatus!==void 0){if(i.status!==t.apiExpectedStatus)throw new Error(`API status assertion failed for ${n}: expected HTTP ${t.apiExpectedStatus}, but received HTTP ${i.status}`);console.log(ye.green(` \u2714 API Status HTTP ${i.status} matched expectation.`))}if(t.apiStorePath&&t.apiStoreVariable)try{let o=await i.text(),a=JSON.parse(o),l=this.resolveJsonPath(a,t.apiStorePath);l!==void 0&&(e.variables.set(t.apiStoreVariable,String(l)),console.log(ye.cyan(` [Variable] $${t.apiStoreVariable} = "${l}" (from ${t.apiStorePath})`)))}catch(o){nt.warn(`Failed to parse JSON response for path extraction: ${o.message}`)}}resolveJsonPath(t,e){let s=e.replace(/^\$\.?/,"").split("."),n=t;for(let r of s){if(n==null||typeof n!="object")return;n=n[r]}return n}},gn=class{type="EMAIL_ASSERT";async execute(t,e){let s=t.emailRecipient||"",n=t.emailSubject||"";console.log(ye.cyan(` [Email Assertion] Checking email to "${s}" with subject "${n}"...`));let r=[];if(process.env.PROMPTTEST_MOCK_EMAILS)try{r=JSON.parse(process.env.PROMPTTEST_MOCK_EMAILS)}catch{r=[]}let i=r.find(o=>o.to.toLowerCase()===s.toLowerCase()&&o.subject.toLowerCase().includes(n.toLowerCase()));if(!i){if(r.length===0){if(!s.includes("@"))throw new Error(`Invalid email recipient address: "${s}"`);console.log(ye.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 "${n}" found.`)}if(t.emailBodyContains&&i.body&&!i.body.includes(t.emailBodyContains))throw new Error(`Email body assertion failed: Expected email to contain "${t.emailBodyContains}", but content did not match.`);console.log(ye.green(" \u2714 Email verified successfully."))}},mr=[new Ws,new Ys,new ys,new Vs,new Js,new zs,new Xs,new Gs,new Ks,new Qs,new Zs,new qs,new en,new tn,new sn,new nn,new rn,new on,new an,new cn,new ln,new dn,new pn,new un,new hn,new gn],fi=new Map(mr.map(c=>[c.type,c]))});var _t,Ss=de(()=>{"use strict";st();ms();_t=class{providerName="heuristic";async resolveIntent(t,e){let s=ue(t);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(t,e,s){let n=[],r=ue(t),i=new Set,o=di(t,s?.locale);for(let p of o){let h=e.find(u=>u.bounds.width>0&&u.bounds.height>0&&(me(p,u.text)||me(p,u.contentDesc)));if(h){let u=(h.text||h.contentDesc||p).trim();i.has(u)||(i.add(u),n.push({text:u,score:.9,node:h,strategy:"synonym",reason:`Direct synonym match for "${t}" via semantic dictionary`}))}}for(let p of e)if(p.resourceId&&p.bounds.width>0&&p.bounds.height>0&&p.resourceId.toLowerCase().split(/[:/_]/).some(m=>m===r||r.includes(m))){let m=(p.text||p.contentDesc||p.resourceId).trim();i.has(m)||(i.add(m),n.push({text:m,score:.85,node:p,strategy:"exact",reason:`Resource-ID segment matches target "${t}"`}))}let a=[];for(let p of e)p.bounds.width>0&&p.bounds.height>0&&(p.text&&p.text.trim().length>1&&a.push({text:p.text.trim(),node:p}),p.contentDesc&&p.contentDesc.trim().length>1&&a.push({text:p.contentDesc.trim(),node:p}));let l=Array.from(new Set(a.map(p=>p.text))),d=At(t,l,5);for(let p of d)if(p.score>=.7&&!i.has(p.text)){i.add(p.text);let h=a.find(u=>u.text===p.text)?.node;n.push({text:p.text,score:p.score,node:h,strategy:"fuzzy",reason:`String similarity match (${Math.round(p.score*100)}%)`})}return n.sort((p,h)=>h.score-p.score)}}});var Xt,ws,fn=de(()=>{"use strict";Ss();Vt();Xt=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(t,e){this.mockIntents.set(t.toLowerCase().trim(),e)}setMockSuggestion(t,e){this.mockSuggestions.set(t.toLowerCase().trim(),e)}async resolveIntent(t,e){let s=t.toLowerCase().trim();return this.mockIntents.has(s)?this.mockIntents.get(s):null}async findSuggestions(t,e,s){let n=t.toLowerCase().trim();return this.mockSuggestions.has(n)?this.mockSuggestions.get(n):new _t().findSuggestions(t,e)}},ws=class{providerName;config;heuristicFallback;constructor(t){this.config=t,this.providerName=t.provider,this.heuristicFallback=new _t}async resolveIntent(t,e){if(this.config.provider==="mock")return new Xt().resolveIntent(t,e);try{let s=`You are a test automation engineer generating steps for PromptTest.
820
+ </html>`}var ut,xt,Tt=ie(()=>{"use strict";Ts();ut=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(t){this.performanceMetrics=t}constructor(t,e){typeof t=="object"&&t!==null?(this.outputDir=t.outputDir||vt.resolve(process.cwd(),"output"),t.formats&&t.formats.length>0&&(this.formats=t.formats),this.embedScreenshots=!!t.embedScreenshots):(this.outputDir=t||vt.resolve(process.cwd(),"output"),e&&e.length>0&&(this.formats=e)),pt.existsSync(this.outputDir)||pt.mkdirSync(this.outputDir,{recursive:!0})}setEmbedScreenshots(t){this.embedScreenshots=t}onStep(t){return this.stepListeners.push(t),()=>{let e=this.stepListeners.indexOf(t);e!==-1&&this.stepListeners.splice(e,1)}}onComplete(t){return this.completeListeners.push(t),()=>{let e=this.completeListeners.indexOf(t);e!==-1&&this.completeListeners.splice(e,1)}}onError(t){return this.errorListeners.push(t),()=>{let e=this.errorListeners.indexOf(t);e!==-1&&this.errorListeners.splice(e,1)}}setFormats(t){this.formats=t}setVideoPath(t){this.videoPath=t}init(t,e){this.packageName=t,this.deviceId=e,this.startTime=new Date,this.steps=[],this.errorsDetected=[],this.pendingWrites=[],this.videoPath=void 0,this.screenshotBuffers.clear()}clearScreenshotBuffers(){this.screenshotBuffers.clear()}logStep(t,e,s,n,i,r,o,l,c){let d=this.steps.length+1,p;if(r){this.embedScreenshots&&this.screenshotBuffers.set(d,r);let m=`step_${d}_${t.toLowerCase().replace(/[^a-z0-9]/g,"_")}.png`,b=vt.join(this.outputDir,m),T=pt.promises.writeFile(b,r).catch(w=>{console.error(`Warning: Failed to write screenshot ${m}:`,w)}).finally(()=>{let w=this.pendingWrites.indexOf(T);w!==-1&&this.pendingWrites.splice(w,1)});this.pendingWrites.push(T),p=m}let u=typeof o?.videoOffsetMs=="number"?o.videoOffsetMs:void 0,h={stepIndex:d,timestamp:new Date().toISOString(),phase:t,description:e,action:s,target:i,status:n,screenshotPath:p,durationMs:l,triageBundle:c,videoOffsetMs:u,details:o};this.steps.push(h);for(let m of this.stepListeners)try{m(h)}catch{}return h}logError(t){this.errorsDetected.push(t);for(let e of this.errorListeners)try{e(t)}catch{}}async generateReport(t){this.pendingWrites.length>0&&await Promise.all(this.pendingWrites);let e=new Date,s=Math.round((e.getTime()-this.startTime.getTime())/1e3),n=[];for(let o of this.steps){let l=o.details;l?.healedFrom&&(l?.healedTo||o.target)&&n.push({original:String(l.healedFrom),healed:String(l.healedTo||o.target),stepNumber:o.stepIndex})}let i={packageName:this.packageName,deviceId:this.deviceId,startTime:this.startTime.toISOString(),endTime:e.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:n.length>0?n:void 0,steps:this.steps},r=t||"prompttest";if(this.formats.includes("json")){let o=vt.join(this.outputDir,`${r}-results.json`);await pt.promises.writeFile(o,JSON.stringify(i,null,2),"utf-8")}if(this.formats.includes("md")){let o=vt.join(this.outputDir,`${r}-report.md`),l=this.formatMarkdownReport(i);await pt.promises.writeFile(o,l,"utf-8")}if(this.formats.includes("junit")){let o=vt.join(this.outputDir,`${r}-junit.xml`),l=this.formatJunitXml(i,r);await pt.promises.writeFile(o,l,"utf-8")}if(this.formats.includes("html")){let o=vt.join(this.outputDir,`${r}-report.html`),l=this.formatHtmlReport(i,r);await pt.promises.writeFile(o,l,"utf-8")}for(let o of this.completeListeners)try{o(i)}catch{}return this.clearScreenshotBuffers(),i}escapeXml(t){return(t||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}escapeHtml(t){return(t||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}formatJunitXml(t,e){let s=t.durationSeconds,n=t.failedSteps+(t.errorsDetected.length>0&&t.failedSteps===0?1:0),i=['<?xml version="1.0" encoding="UTF-8"?>',`<testsuites name="PromptTest" tests="${t.totalSteps}" failures="${n}" skipped="${t.skippedSteps}" time="${s}">`,` <testsuite name="${this.escapeXml(e)}" tests="${t.totalSteps}" failures="${n}" skipped="${t.skippedSteps}" time="${s}">`];for(let r of t.steps){let o=`Step ${r.stepIndex}: ${this.escapeXml(r.action)} - ${this.escapeXml(r.description)}`,l=r.durationMs!==void 0?(r.durationMs/1e3).toFixed(3):"1.000";i.push(` <testcase classname="${this.escapeXml(e)}" name="${o}" time="${l}">`),r.status==="FAIL"?i.push(` <failure message="${this.escapeXml(r.description)}">${this.escapeXml(r.description)}</failure>`):r.status==="SKIPPED"&&i.push(` <skipped message="${this.escapeXml(r.description)}"/>`),i.push(" </testcase>")}return t.failedSteps===0&&t.errorsDetected.length>0&&i.push(` <testcase classname="${this.escapeXml(e)}" name="Error Sentinel Crash Detection" time="0.000">`,` <failure message="${this.escapeXml(t.errorsDetected[0])}">${this.escapeXml(t.errorsDetected.join(`
821
+ `))}</failure>`," </testcase>"),i.push(" </testsuite>"),i.push("</testsuites>"),i.join(`
822
+ `)}formatMarkdownReport(t){let n=["# \u{1F9EA} PromptTest Autonomous Mobile QA Audit Report","",`**Status:** ${t.failedSteps===0&&t.errorsDetected.length===0?"\u2705 ALL PASSED":"\u26A0\uFE0F ISSUES DETECTED"} `,`**Target Package:** \`${t.packageName}\` `,`**Device:** \`${t.deviceId}\` `,`**Execution Time:** ${t.durationSeconds}s (${t.startTime} to ${t.endTime}) `];if(t.videoPath&&n.push(`**Screen Recording:** [Download Video](./${t.videoPath}) `),n.push("","---","","## \u{1F4CA} Executive Summary","","| Metric | Count |","|---|---|",`| **Total Steps** | ${t.totalSteps} |`,`| **Passed** | \u{1F7E2} ${t.passedSteps} |`,`| **Warnings** | \u{1F7E1} ${t.warnSteps} |`,`| **Failures** | \u{1F534} ${t.failedSteps} |`,`| **Skipped** | \u26AA ${t.skippedSteps} |`,`| **Errors Detected** | ${t.errorsDetected.length} |`,""),t.errorsDetected.length>0){n.push("## \u26A0\uFE0F Errors & Sentinels Flagged");for(let o of t.errorsDetected)n.push(`- \u{1F534} \`${o}\``);n.push("")}n.push("## \u{1F50D} Detailed Execution Timeline"),n.push(""),n.push("| Step | Phase | Action | Target / Details | Status | Duration | Screenshot / Triage |"),n.push("|---|---|---|---|---|---|---|");let i=o=>(o||"").replace(/\|/g,"\\|").replace(/\r?\n/g," ");for(let o of t.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",d=o.status==="PASS"?"\u{1F7E2} PASS":o.status==="WARN"?"\u{1F7E1} WARN":o.status==="SKIPPED"?"\u26AA SKIPPED":"\u{1F534} FAIL";n.push(`| ${o.stepIndex} | ${i(o.phase)} | ${i(o.action)} | ${i(o.description)} | ${d} | ${c} | ${l} |`)}n.push(""),t.performanceMetrics&&(n.push("## \u26A1 Mobile Performance & FPS Profiler (PF-01)"),n.push(""),n.push(`- **60 FPS Compliance:** ${t.performanceMetrics.fpsCompliance}`),n.push(`- **Total Frames Rendered:** ${t.performanceMetrics.frames.totalFrames.toLocaleString()}`),n.push(`- **Janky Frames:** ${t.performanceMetrics.frames.jankyFrames} (${t.performanceMetrics.frames.jankPercentage}%)`),t.performanceMetrics.frames.p95Ms!==void 0&&n.push(`- **95th Percentile Frame Time:** ${t.performanceMetrics.frames.p95Ms}ms`),n.push(`- **Peak Memory (PSS):** ${Math.round(t.performanceMetrics.memory.peakPssKb/1024)} MB`),n.push(""));let r=xs();return n.push("---"),r.whiteLabel?n.push("*Generated autonomously by Mobile QA Brain.*"):n.push("*Generated autonomously by [PromptTest](https://prompttest.dev) Mobile QA Brain.*"),n.join(`
823
+ `)}resolveScreenshotData(t){if(t.screenshotPath){if(this.embedScreenshots){if(this.screenshotBuffers.has(t.stepIndex))return`data:image/png;base64,${this.screenshotBuffers.get(t.stepIndex).toString("base64")}`;let e=vt.join(this.outputDir,t.screenshotPath);if(pt.existsSync(e))try{return`data:image/png;base64,${pt.readFileSync(e).toString("base64")}`}catch{}}return`./${t.screenshotPath}`}}formatHtmlReport(t,e){return en(t,{suiteName:e,embedScreenshots:this.embedScreenshots,screenshotResolver:s=>this.resolveScreenshotData(s)})}};xt=new ut});function go(a){let t=new Map;for(let[e,s]of Object.entries(a)){let i=[pe(e),...s.map(r=>pe(r))];for(let r of i){t.has(r)||t.set(r,[]);let o=t.get(r);for(let l of i)o.includes(l)||o.push(l)}}return t}function ps(a,t="en"){let e=pe(a),s=(t||"en").toLowerCase(),n=tn.get(s);if(n){let i=n.get(e);if(i&&i.length>0)return i}if(s!=="en"){let i=tn.get("en");if(i){let r=i.get(e);if(r&&r.length>0)return r}}return[e]}var Wr,Qa,tn,Yr,Ps=ie(()=>{"use strict";ot();Wr={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"]}},Qa=Wr.en,tn=new Map;for(let[a,t]of Object.entries(Wr))tn.set(a.toLowerCase(),go(t));Yr=ps});import _e from"fs";import ht from"path";import{fileURLToPath as mo}from"url";var gt,Me,Pt=ie(()=>{"use strict";ot();Ps();je();gt=class a{memoryDir;currentPackage="global";mappings=new Map;screens=new Map;intents=new Map;dirty=!1;locale="en";setLocale(t){this.locale=(t||"en").toLowerCase()}getLocale(){return this.locale}constructor(t){if(t)this.memoryDir=t;else{let e=ht.dirname(mo(import.meta.url));this.memoryDir=ht.resolve(e,"..","memory")}_e.existsSync(this.memoryDir)||_e.mkdirSync(this.memoryDir,{recursive:!0})}cleanPackageName(t){return(t||"global").replace(/[^a-zA-Z0-9._-]/g,"_")}static SCHEMA_VERSION=2;async init(t){this.currentPackage=this.cleanPackageName(t),this.mappings.clear(),this.screens.clear(),this.intents.clear(),this.dirty=!1;let e=ht.join(this.memoryDir,`${this.currentPackage}.json`);if(_e.existsSync(e))try{let s=await _e.promises.readFile(e,"utf-8"),n=JSON.parse(s);if(n&&n.mappings){let i=!n.version||n.version<2;for(let[r,o]of Object.entries(n.mappings))i&&(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(r,o);if(n.screens)for(let[r,o]of Object.entries(n.screens))this.screens.set(r,o);if(n.intents)for(let[r,o]of Object.entries(n.intents))this.intents.set(r,o);this.decayStaleMappings()}else this.mappings.clear(),this.screens.clear(),this.intents.clear()}catch{this.mappings.clear(),this.screens.clear(),this.intents.clear()}}getCanonicalQuery(t){let e=pe(t),s=ps(e,this.locale);return s&&s.length>0?s[0]:e}buildKey(t,e){return`${t.toUpperCase()}:${this.getCanonicalQuery(e)}`}resolve(t,e){let s=this.buildKey(t,e),n=this.mappings.get(s);if(n)return n;let i=`${t.toUpperCase()}:${pe(e)}`,r=this.mappings.get(i);if(r)return r;let o=ps(e,this.locale);for(let l of o){let c=`${t.toUpperCase()}:${l}`,d=this.mappings.get(c);if(d)return d}}getAllCandidates(t,e,s,n){let i=(s||this.locale||"en").toLowerCase(),r=new Set;if(n){let c=this.getScreenElements(n),d=this.getCanonicalQuery(e);for(let p of c)p.action===t.toUpperCase()&&(p.canonicalIntent===d||p.canonicalIntent===pe(e))&&p.confidence>=.7&&r.add(p.matchedIdentifier)}let o=this.resolve(t,e);o&&o.confidence>=.8&&r.add(o.matchedIdentifier),r.add(e);let l=ps(e,i);for(let c of l)r.add(c);return Array.from(r)}recordSuccess(t,e,s,n,i=1,r,o){if(s.length>35||s.includes("?")||s.includes(`
824
+ `))return;let l=this.buildKey(t,e),c=this.mappings.get(l),d=new Date().toISOString(),p=r||c?.healedFrom?R.memory?.maxHealedConfidence??.65:1,u=r||c?.healedFrom?Math.min(i,p):i;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(p,Number((c.confidence+.05).toFixed(2))),c.lastUpdated=d,c.lastSeenTs=d,r&&(c.healedFrom=r)):this.mappings.set(l,{canonicalIntent:this.getCanonicalQuery(e),action:t.toUpperCase(),matchedIdentifier:s,matchType:n,confidence:u,successCount:1,failCount:0,totalSuccesses:1,recentOutcomes:["PASS"],lastUpdated:d,lastSeenTs:d,healedFrom:r}),o&&this.associateScreenElement(o,l),this.dirty=!0}associateScreenElement(t,e){if(!t||!e)return;let s=new Date().toISOString(),n=this.screens.get(t);n?(n.elements.includes(e)||n.elements.push(e),n.lastSeenTs=s):this.screens.set(t,{lastSeenTs:s,elements:[e]}),this.dirty=!0}getScreenElements(t){let e=this.screens.get(t);return e?e.elements.map(s=>this.mappings.get(s)).filter(s=>s!==void 0):[]}getScreenIndex(){return Object.fromEntries(this.screens.entries())}markScreenCrawled(t,e){if(!t)return;let s=new Date().toISOString(),n=this.screens.get(t);if(n){if(n.crawled=!0,n.crawledAt=s,n.lastSeenTs=s,e&&e.length>0){n.safeActions||(n.safeActions=[]);for(let i of e)n.safeActions.includes(i)||n.safeActions.push(i)}}else this.screens.set(t,{lastSeenTs:s,elements:[],crawled:!0,crawledAt:s,safeActions:e?[...e]:[]});this.dirty=!0}isScreenCrawled(t){return t?!!this.screens.get(t)?.crawled:!1}getScreenMemory(t){return this.screens.get(t)}getCrawledScreensCount(){let t=0;for(let e of this.screens.values())e.crawled&&t++;return t}recordIntentExpansion(t,e){if(!t||!e||e.length===0)return;let s=pe(t),n=new Date().toISOString(),i=this.intents.get(s);i?(i.steps=[...e],i.usageCount=(i.usageCount||1)+1,i.learnedAt=n):this.intents.set(s,{intent:t,steps:[...e],learnedAt:n,usageCount:1}),this.dirty=!0}getIntentExpansion(t){if(!t)return;let e=pe(t),s=this.intents.get(e);if(s)return s.usageCount=(s.usageCount||0)+1,this.dirty=!0,[...s.steps]}hasIntentExpansion(t){return t?this.intents.has(pe(t)):!1}getIntentExpansionsCount(){return this.intents.size}recordFailure(t,e){let s=this.buildKey(t,e),n=this.mappings.get(s);n&&(n.failCount+=1,n.recentOutcomes||(n.recentOutcomes=[]),n.recentOutcomes.push("FAIL"),n.recentOutcomes.length>5&&n.recentOutcomes.shift(),n.confidence=Math.max(.1,Number((n.confidence-.25).toFixed(2))),n.lastUpdated=new Date().toISOString(),(n.recentOutcomes.filter(r=>r==="FAIL").length>=3||n.confidence<.2)&&this.mappings.delete(s),this.dirty=!0)}decayStaleMappings(t){let s=(t??R.memory?.staleThresholdDays??14)*24*60*60*1e3,n=Date.now(),i=0;for(let r of this.mappings.values()){let o=r.lastSeenTs?new Date(r.lastSeenTs).getTime():0;if(o>0&&n-o>s){let l=R.memory?.staleDecayAmount??.2,c=R.memory?.minConfidenceFloor??.2,d=Math.max(c,Number((r.confidence-l).toFixed(2)));d<r.confidence&&(r.confidence=d,i++,this.dirty=!0)}}return i}async save(){if(!this.dirty)return;let t={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())},e=ht.join(this.memoryDir,`${this.currentPackage}.json`),s=`${e}.tmp.${Date.now()}`;await _e.promises.writeFile(s,JSON.stringify(t,null,2),"utf-8");try{await _e.promises.rename(s,e)}catch{await _e.promises.copyFile(s,e),await _e.promises.unlink(s).catch(()=>{})}this.dirty=!1}async exportMemory(t){let e=t?ht.resolve(t):ht.join(this.memoryDir,`${this.currentPackage}-export.json`),s=ht.dirname(e);_e.existsSync(s)||_e.mkdirSync(s,{recursive:!0});let n={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 _e.promises.writeFile(e,JSON.stringify(n,null,2),"utf-8"),e}async importMemory(t){let e=ht.resolve(t);if(!_e.existsSync(e))throw new Error(`Memory import source file not found: ${e}`);let s=await _e.promises.readFile(e,"utf-8"),n=JSON.parse(s);if(!n||!n.mappings)throw new Error("Invalid memory file format: missing mappings object.");let i=0,r=new Date().toISOString();for(let[o,l]of Object.entries(n.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??r,lastUpdated:r}),i++)}if(n.screens)for(let[o,l]of Object.entries(n.screens)){let c=this.screens.get(o);if(!c)this.screens.set(o,l);else{let d=new Set([...c.elements,...l.elements]),p=new Set([...c.safeActions||[],...l.safeActions||[]]);this.screens.set(o,{lastSeenTs:l.lastSeenTs||c.lastSeenTs,elements:Array.from(d),crawled:c.crawled||l.crawled,crawledAt:l.crawledAt||c.crawledAt,safeActions:Array.from(p)})}}if(n.intents)for(let[o,l]of Object.entries(n.intents)){let c=this.intents.get(o);(!c||(l.usageCount||0)>=(c.usageCount||0))&&this.intents.set(o,l)}return i>0&&(this.dirty=!0,await this.save()),i}getMappingsList(){return Array.from(this.mappings.values())}getStats(){let t=Array.from(this.mappings.values()),e=t.reduce((i,r)=>i+(r.totalSuccesses||r.successCount||0),0),s=t.length>0?Number((t.reduce((i,r)=>i+r.confidence,0)/t.length).toFixed(2)):0,n=0;for(let i of this.screens.values())i.crawled&&n++;return{totalMappings:this.mappings.size,totalScreens:this.screens.size,crawledScreens:n,totalIntents:this.intents.size,package:this.currentPackage,totalSuccesses:e,averageConfidence:s}}async clear(t){let e=this.cleanPackageName(t||this.currentPackage),s=ht.join(this.memoryDir,`${e}.json`);return _e.existsSync(s)?(await _e.promises.unlink(s),e===this.currentPackage&&(this.mappings.clear(),this.screens.clear(),this.intents.clear(),this.dirty=!1),!0):!1}},Me=new gt});import tt from"fs";import fo from"path";import bo from"os";function Ft(a){let t=(a||"default").replace(/[^a-zA-Z0-9._-]/g,"_"),e=fo.join(bo.tmpdir(),`prompttest-${t}.lock`);if(tt.existsSync(e))try{let n=tt.readFileSync(e,"utf-8").trim(),i=parseInt(n,10);if(!isNaN(i)){if(i===process.pid)return{serial:a,lockFilePath:e,acquired:!0,pid:process.pid};try{throw process.kill(i,0),new Error(`Device "${a}" is locked by another PromptTest process (PID ${i}).
825
+ Wait for the other run to complete, or remove the lock file if it is stale: ${e}`)}catch(r){if(r.code==="ESRCH")try{tt.unlinkSync(e)}catch{}else throw r}}}catch(n){let i=n;if(i.message&&i.message.includes("locked by another"))throw i;try{tt.unlinkSync(e)}catch{}}try{tt.writeFileSync(e,process.pid.toString(),{flag:"wx"})}catch(n){throw n.code==="EEXIST"?new Error(`Device "${a}" was just acquired by a concurrent PromptTest process.`):n}let s=()=>{try{tt.existsSync(e)&&tt.readFileSync(e,"utf-8").trim()===process.pid.toString()&&tt.unlinkSync(e)}catch{}};return sn.set(e,s),process.once("exit",s),{serial:a,lockFilePath:e,acquired:!0,pid:process.pid}}function Ot(a){if(a)try{let t=sn.get(a.lockFilePath);if(t)process.removeListener("exit",t),sn.delete(a.lockFilePath),t();else if(tt.existsSync(a.lockFilePath))try{tt.readFileSync(a.lockFilePath,"utf-8").trim()===process.pid.toString()&&tt.unlinkSync(a.lockFilePath)}catch{}}catch{}}var sn,us=ie(()=>{"use strict";sn=new Map});import Zt from"chalk";function _t(a){if(!a)return!1;let t=a.toLowerCase();return yo.some(e=>t===e.toLowerCase()||t.includes(e.toLowerCase()))?!0:t.includes("launcher")||t.includes(".home")||t.endsWith(".home")||t.includes("trebuchet")||t.includes("desktop")}function nn(a){if(!a)return!1;let t=a.toLowerCase();return So.some(e=>t===e.toLowerCase()||t.startsWith(e.toLowerCase()))}function ks(a,t){if(!a||!t)return!0;let e=a.toLowerCase(),s=t.toLowerCase();return!!(e===s||e.startsWith(`${s}.`)||nn(a))}function rn(a){if(!a||a.length===0)return null;for(let t of a)if(t.packageName&&(t.clickable||t.focused||t.scrollable)&&!nn(t.packageName))return t.packageName;for(let t of a)if(t.packageName&&!nn(t.packageName))return t.packageName;return a[0]?.packageName||null}async function on(a,t,e,s){if(!t)return{escaped:!1,recovered:!0,currentPackage:null};let n=rn(a);if(!n)return{escaped:!1,recovered:!0,currentPackage:null};if(ks(n,t))return{escaped:!1,recovered:!0,currentPackage:n};let i=_t(n);console.log(Zt.yellow(`
826
+ \u{1F6E1}\uFE0F [Jail Guard] Boundary Violation Detected: Active screen belongs to "${n}" (not target "${t}").`)),console.log(i?Zt.cyan(` [Jail Guard] Device is on home launcher. Suppressing back-press to prevent exit loops. Re-launching "${t}"...`):Zt.cyan(` [Jail Guard] Foreign app "${n}" intercepted. Re-launching target "${t}" to restore containment...`));try{await e.launchApp(t,s),await new Promise(l=>setTimeout(l,1200));let r=await e.getForegroundPackage(s),o=r?r.includes(t):!0;return console.log(o?Zt.green(` \u2714 [Jail Guard] Containment restored: "${t}" is now in foreground.`):Zt.red(` \u274C [Jail Guard] Recovery failed. Current foreground is still "${r}".`)),{escaped:!0,recovered:o,currentPackage:n}}catch(r){return console.log(Zt.red(` \u274C [Jail Guard] Error during containment recovery: ${r.message}`)),{escaped:!0,recovered:!1,currentPackage:n}}}var yo,So,Es=ie(()=>{"use strict";yo=["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"],So=["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"]});import ce from"chalk";var As,Vr=ie(()=>{"use strict";Ve();et();Re();Tt();Pt();ls();Es();As=class{driver;reporter;memory;formFiller;screenLedger=new Map;callStack=[];executedStepCount=0;visitedScreenHashes=new Set;devDefectsList=[];safetyHitList=[];externalSkippedList=[];stepListener;abortFlag;constructor(t=oe,e=xt,s=Me){this.driver=t,this.reporter=e,this.memory=s,this.formFiller=new Mt(void 0,t)}emitStep(t){try{let e=t.screenHash?this.screenLedger.get(t.screenHash):void 0;this.stepListener?.({...t,depth:t.depth??e?.depth,elementCount:t.elementCount??e?.elements.length,discoveredScreens:t.discoveredScreens??this.visitedScreenHashes.size,totalSteps:t.totalSteps??this.executedStepCount})}catch{}}isAborted(){return this.abortFlag?.aborted===!0}async getCleanUiHierarchy(t,e,s=5){let n=await this.driver.getUiHierarchy(e),{flat:i}=be(n),r=0;for(;r<s;){let o=cs(i);if(!o)break;r++;let l=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,c=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(ce.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 d=await this.driver.captureScreenshot(e);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${l}: "${o.errorText}"${c}`,"Verify","FAIL",o.errorText,d,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(ce.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,e)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(ce.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,e)):(console.log(ce.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(e)),await new Promise(p=>setTimeout(p,800)),n=await this.driver.getUiHierarchy(e),i=be(n).flat}return{xml:n,flat:i}}async dismissSoftKeyboard(t){try{await this.driver.pressKey(111,t),await new Promise(e=>setTimeout(e,150))}catch{}}async packageJailGuard(t,e){let s=await this.driver.getForegroundPackage(e);if(!s)return!1;if(!s.includes(t)){let n=_t(s);console.log(ce.yellow(` \u{1F6A8} External App Jailbreak Detected [${s}]. Recovering...`)),n||(await this.driver.pressBack(e),await new Promise(r=>setTimeout(r,800)));let i=await this.driver.getForegroundPackage(e);return(!i||!i.includes(t))&&(console.log(ce.red(` Force restoring ${t} foreground...`)),await this.driver.launchApp(t,e),await new Promise(r=>setTimeout(r,1500)),this.callStack=this.callStack.slice(0,1)),!0}return!1}async handleExternalMediaChooser(t,e){let s=await this.driver.getForegroundPackage(e);if(!s)return!1;let n=s==="android"||s.includes("resolver")||s.includes("intentresolver");if(n){console.log(ce.yellow(` \u{1F6E1}\uFE0F External Media Chooser Intercepted [${s}]. Dismissing to stay in-app...`)),await this.driver.pressBack(e),await new Promise(r=>setTimeout(r,800));let i=await this.driver.getForegroundPackage(e);return i&&!i.includes(t)&&n&&(await this.driver.launchApp(t,e),await new Promise(r=>setTimeout(r,1200)),this.callStack=this.callStack.slice(0,1)),!0}return!1}catalogScreen(t,e,s=[]){let n=[],i=e.height,r=i*.06,o=i*.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=Fe(t);for(let y of c){if(l(y))continue;let P=y.text||y.contentDesc||y.resourceId||"Text Input";n.push({node:y,type:"input",label:P,tested:!1})}let d=Xt(t);for(let y of d){if(l(y))continue;let P=y.text||y.contentDesc||y.resourceId||"Toggle Control";n.push({node:y,type:"toggle",label:P,tested:!1})}let p=Oe(t);for(let y of p){if(l(y)||yt([y],i,e.width)||Ye([y],i,e.width,!0))continue;let P=y.text||y.contentDesc||y.resourceId||"Action Button";if(qe(y)){this.externalSkippedList.push(P);continue}let N=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);n.push({node:y,type:N?"toggle":"transition",label:P,tested:!1})}let u=Ar(t,i,e.width);for(let y of u){if(l(y)||Ye([y],i,e.width,!0)||n.some(N=>Math.abs(N.node.bounds.centerX-y.bounds.centerX)<25&&Math.abs(N.node.bounds.centerY-y.bounds.centerY)<25))continue;let P=y.text||y.contentDesc||y.resourceId||"Notification / Header Action";if(qe(y)){this.externalSkippedList.push(P);continue}n.push({node:y,type:"transition",label:P,tested:!1})}let h=t.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)||n.some(N=>Math.abs(N.node.bounds.centerX-y.bounds.centerX)<25&&Math.abs(N.node.bounds.centerY-y.bounds.centerY)<25))continue;let P=y.text||y.contentDesc||y.resourceId||"Media Control";n.push({node:y,type:"toggle",label:P,tested:!1})}let m=Rt(t,e).filter(y=>!l(y)&&y.bounds.centerY>r&&y.bounds.centerY<o&&!Ye([y],i,e.width,!0)&&!qe(y)),b=Cr(m,2);for(let y of b){let P=y.text||y.contentDesc||y.resourceId||`Item (${y.bounds.centerX},${y.bounds.centerY})`;n.push({node:y,type:"transition",label:P,tested:!1})}let T=0,w=0,S=[];for(let y of n){let P=/\.(?:mp4|mkv|mov|webm|flv|avi)\b/i.test(y.label),N=/\.(?:mp3|wav|m4a|aac|ogg)\b/i.test(y.label);if(P){if(T++,T>1)continue}else if(N&&(w++,w>1))continue;S.push(y)}return S}async safeBacktrack(t,e,s,n,i=!0){let{flat:r}=await this.getCleanUiHierarchy(e,s),{height:o,width:l}=await this.driver.getScreenDimensions(s),c=Ke(r,{width:l,height:o});if(c===t)return!0;let d=Ye(r,o,l,!0);if(d&&d.bounds.width>0){console.log(ce.gray(` [Backtrack] Tapping in-app back/close button "${d.text||d.contentDesc||d.className}"...`)),await this.driver.tap(d.bounds.centerX,d.bounds.centerY,s),await new Promise(h=>setTimeout(h,1e3)),await this.packageJailGuard(e,s);let p=await this.getCleanUiHierarchy(e,s),u=Ke(p.flat,{width:l,height:o});if(u===t||u!==c)return!0}if(n&&n.bounds.width>0){console.log(ce.gray(` [Backtrack] Restoring root domain via tab "${n.text||n.contentDesc||"Root Tab"}"...`)),await this.driver.tap(n.bounds.centerX,n.bounds.centerY,s),await new Promise(h=>setTimeout(h,1e3)),await this.packageJailGuard(e,s);let p=await this.getCleanUiHierarchy(e,s),u=Ke(p.flat,{width:l,height:o});if(u===t||u!==c)return!0}if(i){if(console.log(ce.gray(" [Backtrack] Sending fallback KEYCODE_BACK...")),await this.driver.pressKey(4,s),await new Promise(m=>setTimeout(m,1e3)),await this.packageJailGuard(e,s))return console.log(ce.yellow(" \u26A0\uFE0F Backtrack via KEYCODE_BACK caused app jailbreak. Aborting branch restoration.")),!1;let u=await this.getCleanUiHierarchy(e,s),h=Ke(u.flat,{width:l,height:o});if(h===t||h!==c)return!0}return!1}async explore(t){let{packageName:e,serial:s,maxScreens:n=30,maxDepth:i=3,stepBudget:r=100,maxInteractionsPerScreen:o=6,safetyMode:l="strict",safetyBlacklist:c=[],onStep:d,abortSignal:p}=t;this.stepListener=d,this.abortFlag=p,console.log(ce.bold.cyan(`
827
+ \u{1F916} PromptTest Universal State-Graph DFS Engine`)),console.log(ce.gray(`Target Package: ${e}`)),console.log(ce.gray(`Safety Policy: ${l.toUpperCase()}`)),console.log(ce.gray(`Limits: Max Screens = ${n}, Step Budget = ${r}, Max Depth = ${i}`)),await this.driver.clearCrashLogs(s);let u=await this.driver.getScreenDimensions(s),h=await this.driver.getForegroundPackage(s);(!h||!h.includes(e))&&(console.log(ce.gray(`Launching ${e}...`)),await this.driver.launchApp(e,s),await new Promise(y=>setTimeout(y,2500)));let{flat:m}=await this.getCleanUiHierarchy(e,s);await this.dismissSoftKeyboard(s);let b=Ks(m,u,e);console.log(ce.magenta(`\u26A1 Universal Paradigm Detected: [${b.paradigm.toUpperCase()}] with ${b.roots.length} root node(s).`));let T=[],w;if(b.paradigm==="drawer"&&b.roots.length>0){w=b.roots[0];let y={...m[0]||{},text:"Home Screen / Main Hub",contentDesc:"Home Screen / Main Hub",clickable:!0,bounds:{x1:0,y1:0,x2:u.width,y2:u.height,centerX:u.width/2,centerY:u.height/2,width:u.width,height:u.height}};T.push(y),console.log(ce.gray(" Opening drawer to catalog root destinations...")),await this.driver.tap(w.bounds.centerX,w.bounds.centerY,s),await new Promise(j=>setTimeout(j,1200));let P=await this.getCleanUiHierarchy(e,s),N=P.flat.filter(j=>{if(!j.clickable||j.bounds.width===0||j.bounds.height===0||j.bounds.width>=u.width*.95&&j.bounds.height>=u.height*.95||Ye([j],u.height,u.width,!0))return!1;let Q=`${j.contentDesc} ${j.text}`.trim(),K=j.bounds.width>u.width*.15&&j.bounds.height>u.height*.05;return Q.toLowerCase().includes("developed by")||j.bounds.centerY>u.height*.85?!1:Q.length>=2||K});for(let j of N)T.some(Q=>Math.abs(Q.bounds.centerX-j.bounds.centerX)<30&&Math.abs(Q.bounds.centerY-j.bounds.centerY)<30)||T.push(j);let O=Ye(P.flat,u.height,u.width,!0);O?await this.driver.tap(O.bounds.centerX,O.bounds.centerY,s):await this.driver.tap(w.bounds.centerX,w.bounds.centerY,s),await new Promise(j=>setTimeout(j,1200));let L=await this.getCleanUiHierarchy(e,s);if(L.flat.some(j=>`${j.contentDesc} ${j.text}`.includes("Gallery")&&`${j.contentDesc} ${j.text}`.includes("Planner"))){let j=Ye(L.flat,u.height,u.width,!0);j?await this.driver.tap(j.bounds.centerX,j.bounds.centerY,s):await this.driver.tap(u.width-50,u.height/2,s),await new Promise(Q=>setTimeout(Q,1e3))}console.log(ce.magenta(` Discovered ${T.length} destinations (1 Main Hub + ${T.length-1} Drawer destinations).`))}else b.roots.length>0?T=b.roots:T=[m[0]||{text:"Main Hub",clickable:!0}];let S=1;for(let y of T){if(this.executedStepCount>=r||this.visitedScreenHashes.size>=n||this.isAborted())break;let P=y.text||y.contentDesc||`Root #${S}`;if(console.log(ce.yellow(`
828
+ [Domain ${S}/${T.length}] Entering Root Tree: "${P}"...`)),this.emitStep({type:"action",description:`Tap "${P}" to enter the ${P} section`,screenTitle:P,depth:0}),b.paradigm==="bottom_tabs"||b.paradigm==="top_tabs"){let L=y.bounds.height<25?Math.max(y.bounds.y1-10,y.bounds.centerY):Math.min(y.bounds.centerY,y.bounds.y2-4);await this.driver.tap(y.bounds.centerX,L,s),this.executedStepCount++,await new Promise(U=>setTimeout(U,1200))}else b.paradigm==="drawer"&&S>1&&w&&(await this.dismissSoftKeyboard(s),console.log(ce.gray(` Opening drawer to select "${P}"...`)),await this.driver.tap(w.bounds.centerX,w.bounds.centerY,s),await new Promise(L=>setTimeout(L,1e3)),await this.driver.tap(y.bounds.centerX,y.bounds.centerY,s),this.executedStepCount++,await new Promise(L=>setTimeout(L,1500)));m=(await this.getCleanUiHierarchy(e,s)).flat,await this.dismissSoftKeyboard(s);let O=S===1?Math.max(25,Math.ceil(r*.45)):Math.max(8,Math.ceil(r*.55/Math.max(1,T.length-1)));if(await this.exploreDfsNode({packageName:e,serial:s,screenDims:u,rootNodes:b.roots,activeRootNode:y,currentDepth:0,maxDepth:i,maxScreens:n,stepBudget:r,domainStepLimit:this.executedStepCount+O,maxInteractionsPerScreen:o,safetyMode:l,safetyBlacklist:c,domainLabel:P}),b.paradigm==="drawer"&&S>1){let L=0;for(;L<4;){let U=await this.getCleanUiHierarchy(e,s),j=yt(U.flat,u.height,u.width),Q=U.flat.some(V=>`${V.contentDesc} ${V.text}`.includes("Gallery")&&`${V.contentDesc} ${V.text}`.includes("Planner"));if(j&&!Q)break;let K=Ye(U.flat,u.height,u.width,!0);if(K)console.log(ce.gray(" [Domain Wrap-Up] Returning to Home Screen via in-app back...")),await this.driver.tap(K.bounds.centerX,K.bounds.centerY,s),await new Promise(V=>setTimeout(V,1e3)),L++;else break}}if(S===1&&T.length===1){let L=await this.getCleanUiHierarchy(e,s),U=Ks(L.flat,u,e);if((U.paradigm==="bottom_tabs"||U.paradigm==="top_tabs")&&U.roots.length>1){console.log(ce.magenta(`
829
+ \u26A1 Post-Auth Navigation Discovered: [${U.paradigm.toUpperCase()}] with ${U.roots.length} root domain(s)!`)),b.paradigm=U.paradigm,b.roots=U.roots;let j=U.roots.slice(1);T.push(...j.length>0?j:U.roots)}}S++}for(let y of this.visitedScreenHashes)this.memory.markScreenCrawled(y);await this.memory.save(),this.isAborted()||this.emitStep({type:"done",description:`Autonomous exploration complete: ${this.visitedScreenHashes.size} screen(s) mapped in ${this.executedStepCount} step(s)`}),console.log(ce.green(`
830
+ \u2714 Universal DFS Traversal Complete!`)),console.log(ce.cyan(` Visited Screens: ${this.visitedScreenHashes.size}`)),console.log(ce.cyan(` Total Steps Executed: ${this.executedStepCount}/${r}`)),console.log(ce.cyan(` Defects Triaged: ${this.devDefectsList.length}`))}async exploreDfsNode(t){let{packageName:e,serial:s,screenDims:n,rootNodes:i,activeRootNode:r,currentDepth:o,maxDepth:l,maxScreens:c,stepBudget:d,domainStepLimit:p,maxInteractionsPerScreen:u,safetyMode:h,safetyBlacklist:m,domainLabel:b}=t;if(this.executedStepCount>=d||this.visitedScreenHashes.size>=c||p&&this.executedStepCount>=p||this.isAborted())return;let{flat:T}=await this.getCleanUiHierarchy(e,s),w=ke(T),S=Ke(T,n);this.visitedScreenHashes.add(w);let y=this.screenLedger.get(S);if(y){if(y.status==="FULLY_EXPLORED"){console.log(ce.gray(` \u26A1 Cycle Detected: Screen [${S.slice(0,8)}] already fully explored. Pruning branch.`));return}}else{let L=this.catalogScreen(T,n,i);y={hash:w,semanticHash:S,title:`${b} (depth ${o})`,elements:L,status:"IN_PROGRESS",depth:o,interactionCount:0},this.screenLedger.set(S,y),console.log(ce.cyan(` \u{1F50E} Discovered Screen [${S.slice(0,8)}] at depth ${o} with ${L.length} candidate element(s).`)),this.emitStep({type:"state_change",description:`Discovered screen "${b}" at depth ${o} with ${L.length} candidate element(s)`,screenHash:S,screenTitle:b,depth:o})}this.callStack.push({screenHash:w,semanticHash:S,title:y.title,timestamp:Date.now()});let P=y.elements.filter(L=>L.type==="input"&&!L.tested);if(P.length>0&&y.interactionCount<u){console.log(ce.magenta(` Filling in-place form fields (${P.length} inputs)...`));let L=await this.formFiller.fillAndSubmitForm(T,s,!0,!1);this.executedStepCount++,y.interactionCount++;for(let U of P)U.tested=!0;await this.dismissSoftKeyboard(s),await this.packageJailGuard(e,s),this.reporter.logStep("In-Place Form Fill",`Populated ${L.filledFields.length} field(s) on [${S.slice(0,8)}]`,"FormSubmit","PASS",L.submitButtonText||"Auto");for(let U of L.filledFields)this.emitStep({type:"action",description:`Type "${U.valueEntered}" in the "${U.fieldLabel}" field`,screenHash:S,screenTitle:b,depth:o});L.submitButtonText&&this.emitStep({type:"action",description:`Tap "${L.submitButtonText}" to submit the form`,screenHash:S,screenTitle:b,depth:o})}let N=y.elements.filter(L=>L.type==="toggle"&&!L.tested);for(let L of N){if(this.executedStepCount>=d||y.interactionCount>=u)break;if(L.tested=!0,Ze(L.node,m,h)){this.safetyHitList.push(L.label),this.emitStep({type:"safety_block",description:`Safety policy [${h.toUpperCase()}] blocked toggle "${L.label}"`,screenHash:S,screenTitle:b,depth:o});continue}console.log(ce.gray(` Exercising toggle: "${L.label.slice(0,30)}"...`)),this.executedStepCount++,y.interactionCount++,await this.driver.tap(L.node.bounds.centerX,L.node.bounds.centerY,s),await new Promise(U=>setTimeout(U,600)),this.reporter.logStep("In-Place Toggle",`Toggled control "${L.label}"`,"Tap","PASS",L.label),this.emitStep({type:"action",description:`Toggle "${L.label}"`,screenHash:S,screenTitle:b,depth:o})}let O=y.elements.filter(L=>L.type==="transition"&&!L.tested);for(let L of O){if(this.executedStepCount>=d||this.visitedScreenHashes.size>=c||p&&this.executedStepCount>=p&&!(o===0&&b.includes("Home Screen"))||y.interactionCount>=u||this.isAborted())break;if(L.tested=!0,qe(L.node)){this.externalSkippedList.push(L.label);continue}if(Ze(L.node,m,h)){this.safetyHitList.push(L.label),this.emitStep({type:"safety_block",description:`Safety policy [${h.toUpperCase()}] blocked transition "${L.label}"`,screenHash:S,screenTitle:b,depth:o});continue}console.log(ce.magenta(` \u26A1 [DFS Depth ${o}] Testing transition: "${L.label.slice(0,40)}"...`)),this.executedStepCount++,y.interactionCount++,await this.driver.tap(L.node.bounds.centerX,L.node.bounds.centerY,s),await new Promise(K=>setTimeout(K,1500)),await this.dismissSoftKeyboard(s),await this.handleExternalMediaChooser(e,s),await this.packageJailGuard(e,s);let U=await this.getCleanUiHierarchy(e,s),j=Ke(U.flat,n),Q=j!==S;if(this.reporter.logStep("DFS Transition",`Tapped "${L.label}" [${Q?"Navigated":"In-Place"}]`,"Tap","PASS",L.label),this.emitStep({type:"action",description:`Tap "${L.label}"${Q?" to navigate to the next screen":""}`,screenHash:S,screenTitle:b,depth:o}),Q&&j&&this.emitStep({type:"state_change",description:`Navigated to screen [${j.slice(0,8)}] via "${L.label}"`,screenHash:j,screenTitle:`${b} > ${L.label.slice(0,20)}`,depth:o+1}),Q){if(o<l){let ye=o===0&&b.includes("Home Screen")?Math.min(this.executedStepCount+3,p||this.executedStepCount+3):p;await this.exploreDfsNode({packageName:e,serial:s,screenDims:n,rootNodes:i,activeRootNode:r,domainStepLimit:ye,currentDepth:o+1,maxDepth:l,maxScreens:c,stepBudget:d,maxInteractionsPerScreen:u,safetyMode:h,safetyBlacklist:m,domainLabel:`${b} > ${L.label.slice(0,20)}`})}console.log(ce.gray(` [DFS Backtrack] Returning to [${S.slice(0,8)}]...`)),await this.safeBacktrack(S,e,s,o===0?r:void 0,o>0),await this.dismissSoftKeyboard(s);let K=await this.getCleanUiHierarchy(e,s);if(Ke(K.flat,n)!==S){let ye=Ye(K.flat,n.height,n.width,!0);ye&&(console.log(ce.gray(" [Backtrack Verification] Re-tapping in-app back/close button...")),await this.driver.tap(ye.bounds.centerX,ye.bounds.centerY,s),await new Promise(se=>setTimeout(se,1e3)),await this.packageJailGuard(e,s));let re=await this.getCleanUiHierarchy(e,s);if(Ke(re.flat,n)!==S){console.log(ce.yellow(` \u26A0\uFE0F Unable to restore parent screen [${S.slice(0,8)}]. Unwinding branch to prevent jailbreak loop.`));break}}}}y.status="FULLY_EXPLORED",this.callStack.pop()}}});import wo from"fs";import an from"path";import _ from"chalk";var Ht,Ds,qt=ie(()=>{"use strict";Re();Ve();je();et();ls();Tt();Pt();us();Vr();Es();Ht=class{driver;formFiller;reporter;memory;safetyHitList=[];externalSkippedList=[];devDefectsList=[];visitedScreenHashes=new Set;executedStepCount=0;constructor(t,e,s,n){this.driver=t||oe,this.formFiller=e||Ur,this.reporter=s||xt,this.memory=n||Me}getMemory(){return this.memory}getSafetyHitList(){return this.safetyHitList}getExternalSkippedList(){return this.externalSkippedList}getDevDefectsList(){return this.devDefectsList}getVisitedScreensCount(){return this.visitedScreenHashes.size}async packageJailGuard(t,e){let s=await this.driver.getForegroundPackage(e);if(s&&!s.includes(t)){let n=_t(s);console.log(_.yellow(` \u{1F6E1}\uFE0F Jail Containment: App escaped to "${s}". Executing containment recovery...`)),n||(await this.driver.pressBack(e),await new Promise(r=>setTimeout(r,600)));let i=await this.driver.getForegroundPackage(e);return(!i||!i.includes(t))&&(console.log(_.gray(` Force re-launching ${t} to restore foreground...`)),await this.driver.launchApp(t,e),await new Promise(r=>setTimeout(r,1200))),this.reporter.logStep("Jail Containment",`App escaped to "${s}"; successfully contained and restored "${t}"`,"Back","PASS"),!0}return!1}async getCleanUiHierarchy(t,e,s=5){let n=await this.driver.getUiHierarchy(e),{flat:i}=be(n),r=0;for(;r<s;){let o=cs(i);if(!o)break;r++;let l=`[${(o.severity||"error").toUpperCase()} - ${o.type.toUpperCase()}]`,c=o.sourceLocation?` [${o.sourceLocation}]`:"";console.log(_.red(` \u{1F6A8} Global Dev Console Intercepted: ${l} "${o.errorText.slice(0,80)}"${c}`)),this.devDefectsList.push(o.sourceLocation?`${o.errorText} (${o.sourceLocation})`:o.errorText);let d=await this.driver.captureScreenshot(e);this.reporter.logStep("Dev Overlay Triage",`Intercepted and triaged ${l}: "${o.errorText}"${c}`,"Verify","FAIL",o.errorText,d,{errors:[o.sourceLocation?`${o.errorText} at ${o.sourceLocation}`:o.errorText]}),o.dismissButton&&o.dismissButton.bounds.width>0?(console.log(_.gray(" Dismissing overlay via dismiss button...")),await this.driver.tap(o.dismissButton.bounds.centerX,o.dismissButton.bounds.centerY,e)):o.isToast&&o.node&&o.node.bounds.width>0?(console.log(_.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(o.node.bounds.centerX,o.node.bounds.centerY,e)):(console.log(_.gray(" Pressing Back to dismiss overlay...")),await this.driver.pressBack(e)),await new Promise(p=>setTimeout(p,800)),n=await this.driver.getUiHierarchy(e),i=be(n).flat}return{xml:n,flat:i}}async handleDevOverlays(t,e,s){let n=cs(t);if(!n)return!1;let i=`[${(n.severity||"error").toUpperCase()} - ${n.type.toUpperCase()}]`,r=n.sourceLocation?` [${n.sourceLocation}]`:"";console.log(_.red(` \u{1F6A8} Dev Overlay Detected: ${i} "${n.errorText.slice(0,80)}"${r}`)),this.devDefectsList.push(n.sourceLocation?`${n.errorText} (${n.sourceLocation})`:n.errorText);let o=await this.driver.captureScreenshot(s);return this.reporter.logStep("Dev Overlay Triage",`Detected and triaged ${i} error: "${n.errorText}"${r}`,"Verify","FAIL",n.errorText,o,{errors:[n.sourceLocation?`${n.errorText} (${n.sourceLocation})`:n.errorText]}),n.dismissButton&&n.dismissButton.bounds.width>0?(console.log(_.gray(" Dismissing error overlay via dismiss button...")),await this.driver.tap(n.dismissButton.bounds.centerX,n.dismissButton.bounds.centerY,s)):n.isToast&&n.node&&n.node.bounds.width>0?(console.log(_.gray(" Expanding toast notification to access Dismiss button...")),await this.driver.tap(n.node.bounds.centerX,n.node.bounds.centerY,s)):(console.log(_.gray(" Pressing Back to dismiss error overlay...")),await this.driver.pressBack(s)),await new Promise(l=>setTimeout(l,800)),!0}async dismissSoftKeyboard(t){try{await this.driver.pressKey(111,t),await new Promise(e=>setTimeout(e,200))}catch{}}async explore(t){let{packageName:e,serial:s,maxTabs:n=R.explorer?.maxTabs||15,exploreLists:i=!0,skipAlreadyCrawled:r=!1,safetyMode:o=R.safety?.mode||"strict",safetyBlacklist:l=R.safety?.customBlacklist?[...R.safety.customBlacklist]:[],maxScreens:c=R.explorer?.maxScreens||30,stepBudget:d=R.explorer?.stepBudget||100,screenshots:p=R.explorer?.screenshots||"state-change",video:u=t.video??!1,onStep:h,abortSignal:m}=t;console.log(_.cyan(`
831
+ \u{1F916} PromptTest Autonomous Exploration Brain v3`)),console.log(_.gray("Target Package: ")+_.bold.white(e)),console.log(_.gray("Safety Policy: ")+_.bold.yellow(o.toUpperCase())+(l.length>0?_.gray(` (+${l.length} custom blacklist)`):"")),console.log(_.gray("Screenshots Policy: ")+_.bold.white(p));let b=[],T=0,w=L=>{T++,h?.({...L,stepNumber:T,packageName:e,totalSteps:L.totalSteps??this.executedStepCount,discoveredScreens:L.discoveredScreens??this.visitedScreenHashes.size})},S=async(L,U=!0)=>{if(!(p==="failure-only"&&L!=="FAIL")&&!(p==="state-change"&&!U&&L!=="FAIL"))try{return await this.driver.captureScreenshot(s)}catch{return}};await this.memory.init(e);let y=s||this.driver.getDefaultDevice(),P=y||"Default Device";this.reporter.init(e,P);let N=null;y&&(N=Ft(y));let O=null;this.safetyHitList=[],this.externalSkippedList=[],this.devDefectsList=[],this.visitedScreenHashes.clear(),this.executedStepCount=0;try{if(await this.driver.clearCrashLogs(s),u&&(console.log(_.cyan("\u{1F3AC} Starting ADB screen recording for exploration (--video enabled)...")),O=await this.driver.startScreenRecording("/sdcard/prompttest_explore_temp.mp4",s)),(t.strategy||R.explorer?.strategy||"heuristic")==="dfs"){let k=new As(this.driver,this.reporter,this.memory);await k.explore({packageName:e,serial:s,maxScreens:c,maxDepth:t.maxDepth||R.explorer?.maxDepth||5,stepBudget:d,safetyMode:o,safetyBlacklist:l,screenshotPolicy:p,skipAlreadyCrawled:r,onStep:v=>w({type:v.type,description:v.description,screenHash:v.screenHash,screenTitle:v.screenTitle,depth:v.depth,elementCount:v.elementCount,totalSteps:v.totalSteps,discoveredScreens:v.discoveredScreens}),abortSignal:m}),this.safetyHitList=k.safetyHitList,this.externalSkippedList=k.externalSkippedList,this.devDefectsList=k.devDefectsList,this.visitedScreenHashes=k.visitedScreenHashes,this.executedStepCount=k.executedStepCount;let M=this.memory.getStats();this.reporter.logStep("Coverage Audit",`State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s). Memory Engine cached ${M.totalScreens} crawled screen(s).`,"Verify","PASS"),await this.reporter.generateReport(),console.log(_.bold.green(`
832
+ \u2728 Full Audit Report generated at: output/prompttest-report.md`));return}let{width:U,height:j}=await this.driver.getScreenDimensions(s);console.log(_.yellow(`
833
+ [1/5] Verifying Foreground State...`));let Q=await this.driver.getForegroundPackage(s);(!Q||!Q.includes(e))&&(console.log(_.gray(`Launching ${e}...`)),await this.driver.launchApp(e,s),await new Promise(k=>setTimeout(k,2500)));let K=await this.runErrorSentinel(e,s,"Post-Launch"),V=K.hasError?"FAIL":"PASS",ye=await S(V,!0);this.reporter.logStep("Init",`Launched and verified foreground for ${e}`,"Launch",V,e,ye,K.hasError?{errors:K.errors}:void 0),console.log(V==="PASS"?_.green("\u2714 App is active in foreground."):_.red(`\u2716 Issues detected during launch for ${e}.`)),console.log(_.yellow(`
834
+ [2/5] Inspecting UI Hierarchy for Forms...`));let{xml:re,flat:W}=await this.getCleanUiHierarchy(e,s),se=ke(W);if(this.visitedScreenHashes.add(se),this.memory.isScreenCrawled(se)&&r)console.log(_.cyan(` \u26A1 Fast-Forward: Screen [${se.slice(0,8)}] was previously crawled offline. Skipping redundant element interactions.`)),this.reporter.logStep("Fast Forward",`Fast-forwarded past previously crawled screen [${se.slice(0,8)}] (Memory Engine)`,"Skip","PASS");else{let k=Fe(W);if(k.length>0){console.log(_.magenta(`\u26A1 Detected ${k.length} form field(s)! Engaging Semantic Form Filler...`));for(let q of k)console.log(_.gray(` - Field: "${q.text||q.contentDesc||q.resourceId}"`));let A=await this.formFiller.fillAndSubmitForm(W,s,!0,!1);this.executedStepCount++,await new Promise(q=>setTimeout(q,1500)),await this.dismissSoftKeyboard(s),await this.packageJailGuard(e,s);let D=await this.getCleanUiHierarchy(e,s);re=D.xml,W=D.flat,this.visitedScreenHashes.add(ke(W));let I=await this.runErrorSentinel(e,s,"Post-Form-Submit"),H=Fe(W),G=I.hasError||H.length===k.length&&I.errors.length>0?"FAIL":"PASS",Z=await S(G,!0),X=A.filledFields.map(q=>`${q.fieldLabel} -> "${q.valueEntered}"`).join(", ");this.reporter.logStep("Form Fill",`Filled ${A.filledFields.length} field(s) [${X}] and submitted via "${A.submitButtonText||"Auto"}"`,"FormSubmit",G,A.submitButtonText,Z,I.hasError?{errors:I.errors}:void 0),console.log(G==="PASS"?_.green("\u2714 Form populated and submitted successfully!"):_.red("\u2716 Form submission triggered an error or remained unauthenticated."))}else console.log(_.gray("No form input fields detected on initial screen."));console.log(_.yellow(`
835
+ [3/5] Inspecting Interactive Action Controls...`));let M=Oe(W).filter(A=>{let D=(A.text+" "+A.contentDesc).toLowerCase();if(D.includes("submit")||D.includes("cancel")||yt([A],j))return!1;if(qe(A)){let I=A.text||A.contentDesc||A.resourceId||"External Link";return console.log(_.gray(` \u{1F517} External Link Bypassed: "${I}"`)),this.externalSkippedList.push(I),!1}if(Ze(A,l,o)){let I=A.text||A.contentDesc||A.resourceId||"Action";return console.log(_.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive action "${I}"`)),this.safetyHitList.push(I),!1}return!0});if(M.length>0){let A=M[0],D=A.text||A.contentDesc||A.resourceId;console.log(_.magenta(`\u26A1 Found action button: "${D}". Testing interaction...`));let I=ke(W);this.executedStepCount++,await this.driver.tap(A.bounds.centerX,A.bounds.centerY,s),await new Promise(Pe=>setTimeout(Pe,1200)),await this.packageJailGuard(e,s);let H=await this.getCleanUiHierarchy(e,s);re=H.xml,W=H.flat;let z=ke(W);this.visitedScreenHashes.add(z);let G=await this.runErrorSentinel(e,s,"Post-Action-Tap"),Z=I!==z,X="PASS",q="";G.hasError?(X="FAIL",q="Action triggered error or crash"):Z?(X="PASS",q="Screen transitioned successfully"):(X="WARN",q="No visible screen transition or state change observed (No-Op)",console.log(_.yellow(` \u26A0\uFE0F Outcome Warning: Action "${D}" produced no UI state change.`)));let Se=await S(X,Z);this.reporter.logStep("Action Testing",`Tapped action button "${D}" [${q}]`,"Tap",X,D,Se,G.hasError?{errors:G.errors}:void 0),console.log(X==="PASS"?_.green(`\u2714 Action interaction complete (${q}).`):X==="WARN"?_.yellow(`\u2714 Action interaction recorded with warning (${q}).`):_.red("\u2716 Action interaction triggered an error.")),X!=="FAIL"&&(this.memory.recordSuccess("TAP",D,A.resourceId||A.text||A.contentDesc||D,"text",1,void 0,I),this.memory.markScreenCrawled(I,[D]))}else console.log(_.gray("No standalone action buttons to test."));let v=Xt(W).filter(A=>!Ze(A,l,o));if(v.length>0){console.log(_.yellow(`
836
+ [3b/5] Inspecting Toggle & Checkable Controls...`));let A=v[0],D=A.text||A.contentDesc||A.resourceId||"Switch/Checkbox",I=A.checked;console.log(_.magenta(`\u26A1 Found toggle: "${D}" (checked: ${I}). Testing toggle interaction...`)),this.executedStepCount++,await this.driver.tap(A.bounds.centerX,A.bounds.centerY,s),await new Promise(Pe=>setTimeout(Pe,1e3)),re=await this.driver.getUiHierarchy(s),W=be(re).flat;let H=ke(W);this.visitedScreenHashes.add(H);let G=Xt(W).find(Pe=>Math.abs(Pe.bounds.centerX-A.bounds.centerX)<25&&Math.abs(Pe.bounds.centerY-A.bounds.centerY)<25),Z=G?G.checked!==I:!0,X=await this.runErrorSentinel(e,s,"Post-Toggle"),q=X.hasError?"FAIL":Z?"PASS":"WARN",Se=await S(q,Z);this.reporter.logStep("Toggle Testing",`Toggled "${D}" (Checked: ${I} -> ${G?G.checked:"flipped"})`,"Toggle",q,D,Se,X.hasError?{errors:X.errors}:void 0),console.log(q==="PASS"?_.green("\u2714 Toggle control flipped successfully!"):q==="WARN"?_.yellow("\u26A0\uFE0F Toggle tapped but checked state did not flip."):_.red("\u2716 Toggle control triggered an error.")),q!=="FAIL"&&this.memory.markScreenCrawled(H,[D])}let C=yt(W,j);if(C&&!Ze(C,l,o)){let A=C.contentDesc||C.text||C.resourceId||"Drawer Menu";console.log(_.yellow(`
837
+ [3c/5] Inspecting Side Navigation Drawer...`)),console.log(_.magenta(`\u26A1 Found drawer trigger: "${A}". Opening side drawer...`));let D=ke(W);this.executedStepCount++,await this.driver.tap(C.bounds.centerX,C.bounds.centerY,s),await new Promise(q=>setTimeout(q,1200)),await this.packageJailGuard(e,s);let I=await this.getCleanUiHierarchy(e,s);re=I.xml,W=I.flat;let H=ke(W);this.visitedScreenHashes.add(H);let z=D!==H,G=await this.runErrorSentinel(e,s,"Drawer-Open"),Z=G.hasError?"FAIL":z?"PASS":"WARN",X=await S(Z,z);if(this.reporter.logStep("Drawer Navigation",`Opened side drawer via "${A}"`,"Tap",Z,A,X,G.hasError?{errors:G.errors}:void 0),Z==="PASS"){console.log(_.green("\u2714 Side drawer opened successfully!"));let q=Oe(W).filter(Pe=>!Ze(Pe,l,o)&&!qe(Pe)&&Pe.bounds.centerY>C.bounds.y2);if(q.length>0){let Pe=q[0],Xe=Pe.text||Pe.contentDesc||Pe.resourceId||"Drawer Item";console.log(_.gray(` - Testing drawer item: "${Xe}"...`)),this.executedStepCount++,await this.driver.tap(Pe.bounds.centerX,Pe.bounds.centerY,s),await new Promise(xe=>setTimeout(xe,1200)),await this.packageJailGuard(e,s);let $t=await this.getCleanUiHierarchy(e,s);re=$t.xml,W=$t.flat,this.visitedScreenHashes.add(ke(W))}console.log(_.gray(" Closing side drawer to restore main view...")),await this.driver.pressBack(s),await new Promise(Pe=>setTimeout(Pe,1e3));let Se=await this.getCleanUiHierarchy(e,s);re=Se.xml,W=Se.flat,this.visitedScreenHashes.add(ke(W)),console.log(_.green("\u2714 Main view restored from side drawer."))}else Z==="WARN"&&console.log(_.yellow("\u26A0\uFE0F Drawer button tapped but no visible screen change occurred."));Z!=="FAIL"&&this.memory.markScreenCrawled(D,[A])}if(i){console.log(_.yellow(`
838
+ [4/5] Inspecting Lists & Master-Detail Flows...`));let A=Dr(W);if(A.length>0){console.log(_.magenta(`\u26A1 Found ${A.length} scrollable container(s). Performing vertical scroll exploration...`));let I=ke(W),H=Math.round(U*.5);await this.driver.swipe(H,Math.round(j*.7),H,Math.round(j*.3),300,s),await new Promise(q=>setTimeout(q,1200));let z=await this.getCleanUiHierarchy(e,s);re=z.xml,W=z.flat;let G=ke(W);this.visitedScreenHashes.add(G);let Z=I!==G,X=await S("PASS",Z);this.reporter.logStep("Scroll Exploration",Z?"Scrolled vertically to discover off-screen elements":"Scrolled but content remained at edge","Swipe","PASS","SwipeUp",X),Z&&console.log(_.green("\u2714 Vertical scroll succeeded; discovered off-screen content below fold."))}let D=Rt(W).filter(I=>{if(qe(I)){let H=I.text||I.contentDesc||"External Link";return this.externalSkippedList.push(H),!1}if(Ze(I,l,o)){let H=I.text||I.contentDesc||"List Item";return console.log(_.yellow(` \u{1F6E1}\uFE0F Safety Guard: Blocked destructive list item "${H}"`)),this.safetyHitList.push(H),!1}return!0});if(D.length>0){let I=D[0],H=I.text||I.contentDesc||`Item at (${I.bounds.centerX}, ${I.bounds.centerY})`;console.log(_.magenta(`\u26A1 Found list item: "${H}". Diving into detail view...`));let z=ke(W);await this.driver.tap(I.bounds.centerX,I.bounds.centerY,s),await new Promise(nt=>setTimeout(nt,1800));let G=await this.getCleanUiHierarchy(e,s);re=G.xml,W=G.flat;let Z=ke(W);this.visitedScreenHashes.add(Z);let X=await this.runErrorSentinel(e,s,"Detail-View"),q=z!==Z,Se=X.hasError?"FAIL":q?"PASS":"WARN",Pe=await S(Se,q);this.reporter.logStep("List Dive",`Dived into detail view for "${H}" [${q?"Navigated":"Unchanged"}]`,"Tap",Se,H,Pe,X.hasError?{errors:X.errors}:void 0),console.log(Se==="PASS"?_.green("\u2714 Detail screen loaded."):Se==="WARN"?_.yellow("\u26A0\uFE0F Detail view tapped but no navigation detected."):_.red("\u2716 Detail view triggered an error."));let Xe=Ye(G.flat,R.screen.fallbackHeight,R.screen.fallbackWidth,!0);Xe&&Xe.bounds.width>0?(console.log(_.gray(`Navigating back via in-app back button "${Xe.text||Xe.contentDesc||Xe.className}"...`)),await this.driver.tap(Xe.bounds.centerX,Xe.bounds.centerY,s)):(console.log(_.gray("Navigating back to main view...")),await this.driver.pressKey(4,s)),await new Promise(nt=>setTimeout(nt,1200));let $t=await this.driver.getForegroundPackage(s);$t&&!$t.includes(e)&&(console.log(_.yellow(`Restoring ${e} foreground after back navigation...`)),await this.driver.launchApp(e,s),await new Promise(nt=>setTimeout(nt,2e3)));let xe=await this.getCleanUiHierarchy(e,s);re=xe.xml,W=xe.flat,this.visitedScreenHashes.add(ke(W));let Ie=await this.runErrorSentinel(e,s,"Post-Back-Restore"),bt=Ie.hasError?"FAIL":"PASS",bs=await S(bt,!0);this.reporter.logStep("List Dive","Navigated back to master screen","Back",bt,"KEYCODE_BACK",bs,Ie.hasError?{errors:Ie.errors}:void 0),bt==="PASS"&&console.log(_.green("\u2714 Master view restored."))}else console.log(_.gray("No list items found for master-detail exploration."))}}console.log(_.yellow(`
839
+ [5/5] Inspecting Navigation Tabs...`));let f=await this.driver.getForegroundPackage(s);f&&!f.includes(e)&&(console.log(_.gray(`Restoring ${e} foreground for tab traversal...`)),await this.driver.launchApp(e,s),await new Promise(k=>setTimeout(k,2e3))),await this.dismissSoftKeyboard(s);let F=await this.getCleanUiHierarchy(e,s);re=F.xml,W=F.flat;let E=at(W,void 0,e);if(E.length>1){console.log(_.magenta(`\u26A1 Found ${E.length} navigation tab(s). Traversal sequence initiated...`));let k=E.slice(0,n),M=1;for(let v of k){let C=v.text||v.contentDesc||`Tab #${M}`;console.log(_.gray(` - Visiting ${C}...`)),await this.dismissSoftKeyboard(s);let A=Math.min(v.bounds.centerY,v.bounds.y2-4);this.executedStepCount++,await this.driver.tap(v.bounds.centerX,A,s),await new Promise(Z=>setTimeout(Z,1500)),await this.packageJailGuard(e,s);let D=await this.getCleanUiHierarchy(e,s);re=D.xml,W=D.flat;let I=ke(W);this.visitedScreenHashes.add(I),this.memory.markScreenCrawled(I,[C]);let H=await this.runErrorSentinel(e,s,`Tab-${C}`),z=H.hasError?"FAIL":"PASS",G=await S(z,!0);if(this.reporter.logStep("Tab Traversal",`Navigated to tab "${C}"`,"Tap",z,C,G,H.hasError?{errors:H.errors}:void 0),console.log(z==="PASS"?_.green(` \u2714 Tab "${C}" loaded cleanly.`):_.red(` \u2716 Tab "${C}" triggered errors.`)),this.executedStepCount<d&&this.visitedScreenHashes.size<c){let Z=[],X=j*.08,q=j*.88,Se=xe=>xe.bounds.centerY>X&&xe.bounds.centerY<q&&xe.clickable&&xe.bounds.width>30&&xe.bounds.height>20&&E.every(Ie=>Math.abs(Ie.bounds.centerX-xe.bounds.centerX)>40||Math.abs(Ie.bounds.centerY-xe.bounds.centerY)>40),Pe=Rt(W,{width:U,height:j}).filter(Se);for(let xe of Pe.slice(0,3))Z.push({node:xe,type:"list"});let Xe=Oe(W).filter(xe=>!(!Se(xe)||yt([xe],j)));for(let xe of Xe.slice(0,4))Z.push({node:xe,type:"button"});let $t=Xt(W).filter(Se);for(let xe of $t.slice(0,2))Z.push({node:xe,type:"toggle"});for(let xe of Z){if(this.executedStepCount>=d||this.visitedScreenHashes.size>=c)break;let Ie=xe.node;if(qe(Ie)){this.externalSkippedList.push(Ie.text||Ie.contentDesc||"External Link");continue}if(Ze(Ie,l,o)){let Jt=Ie.text||Ie.contentDesc||"Destructive Action";this.safetyHitList.push(Jt);continue}let bt=Ie.text||Ie.contentDesc||Ie.resourceId||`${xe.type} element`;console.log(_.gray(` Sub-crawl [${C}]: Exercising ${xe.type} "${bt.slice(0,40)}"...`)),this.executedStepCount++,await this.driver.tap(Ie.bounds.centerX,Ie.bounds.centerY,s),await new Promise(Jt=>setTimeout(Jt,1200)),await this.dismissSoftKeyboard(s),await this.packageJailGuard(e,s);let bs=await this.getCleanUiHierarchy(e,s);re=bs.xml,W=bs.flat;let nt=ke(W),Ii=!this.visitedScreenHashes.has(nt);this.visitedScreenHashes.add(nt);let Us=await this.runErrorSentinel(e,s,`Sub-${bt.slice(0,30)}`),hr=Us.hasError?"FAIL":"PASS",Ni=await S(hr,Ii);if(this.reporter.logStep("Tab Sub-Crawl",`Tested ${xe.type} "${bt}" under tab "${C}"`,"Tap",hr,bt,Ni,Us.hasError?{errors:Us.errors}:void 0),nt!==I){console.log(_.cyan(` \u{1F50E} Screen transitioned. Discovered new screen state [${nt.slice(0,8)}]`));let Jt=Oe(W).filter(rt=>Se(rt)&&!Ze(rt,l,o)&&!qe(rt));if(Jt.length>0&&this.executedStepCount<d){let rt=Jt[0],Li=rt.text||rt.contentDesc||"Child Action";console.log(_.gray(` Exercising child action "${Li.slice(0,30)}"...`)),this.executedStepCount++,await this.driver.tap(rt.bounds.centerX,rt.bounds.centerY,s),await new Promise(Ri=>setTimeout(Ri,1e3)),await this.dismissSoftKeyboard(s)}console.log(_.gray(` Restoring ${C} master view via Back...`)),await this.driver.pressBack(s),await new Promise(rt=>setTimeout(rt,1e3)),await this.dismissSoftKeyboard(s);let gr=await this.getCleanUiHierarchy(e,s);re=gr.xml,W=gr.flat}}}M++}console.log(_.green("\u2714 Navigation tabs traversed successfully!"))}else console.log(_.gray("Single-screen or tabs not detected at screen footer."));for(let k of this.visitedScreenHashes)this.memory.markScreenCrawled(k);await this.memory.save();let ge=this.memory.getStats();if(this.reporter.logStep("Coverage Audit",`State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s). Memory Engine cached ${ge.crawledScreens} crawled screen(s).`,"Verify","PASS"),await this.runErrorSentinel(e,s,"Final-State"),O){console.log(_.cyan("\u{1F3AC} Finalizing exploration screen recording..."));let k=an.resolve(process.cwd(),"output"),M=an.join(k,`${e}-exploration.mp4`),v=await this.driver.stopScreenRecording(O,M,s);v&&wo.existsSync(v)&&(this.reporter.setVideoPath?.(`${e}-exploration.mp4`),console.log(_.green(`\u2714 Screen recording saved to: output/${e}-exploration.mp4`))),O=null}let Y=await this.reporter.generateReport(),g=Y.failedSteps===0&&Y.errorsDetected.length===0;console.log(g?_.bold.green(`
840
+ \u2728 PromptTest Exploration Complete \u2014 All Steps Passed!`):_.bold.red(`
841
+ \u26A0\uFE0F PromptTest Exploration Complete \u2014 Issues Detected!`)),console.log(_.white(`Summary: ${Y.passedSteps} passed, ${Y.warnSteps} warnings, ${Y.failedSteps} failed, ${Y.errorsDetected.length} errors detected.`)),this.safetyHitList.length>0&&console.log(_.cyan(`\u{1F6E1}\uFE0F Safety Shield: Blocked ${this.safetyHitList.length} destructive action(s) [${this.safetyHitList.join(", ")}].`)),console.log(_.cyan(`\u{1F5FA}\uFE0F State Graph Coverage: Visited ${this.visitedScreenHashes.size} unique screen state(s).`)),console.log(_.cyan(`\u{1F9E0} Memory Engine Coverage: ${ge.crawledScreens} crawled screen(s) indexed offline.`)),console.log(_.cyan(`Full Audit Report generated at: output/prompttest-report.md
842
+ `))}finally{if(O)try{await this.driver.stopScreenRecording(O,an.join("output",`${e}-exploration.mp4`),s)}catch{}N&&Ot(N)}}async runErrorSentinel(t,e,s){let n=[];try{let[i,r]=await Promise.all([this.driver.checkCrashes(t,e),this.driver.getUiHierarchy(e)]);if(i){let c=`Crash in ${s}: ${i.slice(0,300)}`;console.log(_.bold.red(`\u{1F6A8} FATAL EXCEPTION DETECTED in ${t} [${s}]!`)),this.reporter.logError(c),n.push(c)}let{flat:o}=be(r),l=Er(o);if(l.length>0)for(let c of l){let d=c.text||c.contentDesc,p=`UI Error dialog in ${s}: "${d}"`;console.log(_.bold.yellow(`\u26A0\uFE0F Error Dialog/Banner on screen [${s}]: "${d}"`)),this.reporter.logError(p),n.push(p)}}catch{}return{hasError:n.length>0,errors:n}}},Ds=new Ht});import kt from"fs";import Jr from"path";var cn,Kr,zr=ie(()=>{"use strict";cn=class{checkpointDir;constructor(t){this.checkpointDir=t||Jr.resolve(process.cwd(),"output","checkpoints"),kt.existsSync(this.checkpointDir)||kt.mkdirSync(this.checkpointDir,{recursive:!0})}filePath(t){let e=t.replace(/[^a-zA-Z0-9._-]/g,"_");return Jr.join(this.checkpointDir,`${e}.checkpoint.json`)}async save(t,e,s,n){let i={specFile:t,packageName:e,lastPassedStep:s,totalSteps:n,savedAt:new Date().toISOString()},r=this.filePath(t)+`.tmp.${Date.now()}`;await kt.promises.writeFile(r,JSON.stringify(i,null,2),"utf-8"),await kt.promises.rename(r,this.filePath(t))}async load(t){let e=this.filePath(t);if(!kt.existsSync(e))return null;try{let s=await kt.promises.readFile(e,"utf-8");return JSON.parse(s)}catch{return null}}async clear(t){let e=this.filePath(t);kt.existsSync(e)&&await kt.promises.unlink(e)}async getResumeIndex(t){let e=await this.load(t);return e?e.lastPassedStep+1:0}},Kr=new cn});import Ee from"fs";import Et from"path";import{PNG as Bt}from"pngjs";import vo from"pixelmatch";var ln,Ut,dn,hs=ie(()=>{"use strict";ln={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"}},Ut=class{baselineDir;defaultThreshold;constructor(t,e=.02){this.baselineDir=t||Et.resolve(process.cwd(),"output","baselines"),this.defaultThreshold=e,Ee.existsSync(this.baselineDir)||Ee.mkdirSync(this.baselineDir,{recursive:!0})}filePath(t,e){let s=t.replace(/[^a-zA-Z0-9._-]/g,"_");return Et.join(this.baselineDir,`${s}_step${String(e).padStart(3,"0")}.png`)}diffFilePath(t,e){let s=t.replace(/[^a-zA-Z0-9._-]/g,"_");return Et.join(this.baselineDir,`${s}_step${String(e).padStart(3,"0")}_diff.png`)}masksFilePath(t,e){let s=t.replace(/[^a-zA-Z0-9._-]/g,"_");return Et.join(this.baselineDir,`${s}_step${String(e).padStart(3,"0")}_masks.json`)}async getMasks(t,e){let s=this.masksFilePath(t,e);if(!Ee.existsSync(s))return[];try{let n=await Ee.promises.readFile(s,"utf-8"),i=JSON.parse(n);return Array.isArray(i)?i:i.masks||[]}catch{return[]}}async saveMasks(t,e,s){let n=this.masksFilePath(t,e);return await Ee.promises.writeFile(n,JSON.stringify(s,null,2),"utf-8"),n}async saveBaseline(t,e,s){let n=this.filePath(t,e),i=n+`.tmp.${Date.now()}`;return await Ee.promises.writeFile(i,s),await Ee.promises.rename(i,n),n}hasBaseline(t,e){return Ee.existsSync(this.filePath(t,e))}async compare(t,e,s,n){let i=typeof n=="number"?{threshold:n}:n||{},r=this.filePath(t,e),o=Et.join(this.baselineDir,`${t.replace(/[^a-zA-Z0-9._-]/g,"_")}_step${String(e).padStart(3,"0")}_current.png`);await Ee.promises.writeFile(o,s);let l=i.mode?ln[i.mode]:void 0,c=i.threshold??l?.threshold??this.defaultThreshold,d=i.pixelmatchThreshold??l?.pixelmatchThreshold??.1;if(!Ee.existsSync(r)){await this.saveBaseline(t,e,s);let j=Bt.sync.read(s),Q=j.width*j.height;return{stepName:`Step ${e}`,baselineFile:r,currentFile:o,diffRatio:0,diffPixels:0,totalPixels:Q,passed:!0,threshold:c}}let p=await Ee.promises.readFile(r),u=Bt.sync.read(p),h=Bt.sync.read(s),m=Math.max(u.width,h.width),b=Math.max(u.height,h.height),T=m*b,w=this.fitToCanvas(u,m,b),S=this.fitToCanvas(h,m,b),y=i.maskRegions??await this.getMasks(t,e);y&&y.length>0&&(this.applyMasks(w,y),this.applyMasks(S,y));let P=new Bt({width:m,height:b}),N=vo(w.data,S.data,P.data,m,b,{threshold:d,includeAA:!1}),O=T>0?N/T:0,L=O<=c,U;if(L){let j=this.diffFilePath(t,e);if(Ee.existsSync(j))try{await Ee.promises.unlink(j)}catch{}}else{U=this.diffFilePath(t,e);let j=Bt.sync.write(P);await Ee.promises.writeFile(U,j)}return{stepName:`Step ${e}`,baselineFile:r,currentFile:o,diffRatio:O,diffPixels:N,totalPixels:T,passed:L,threshold:c,diffImagePath:U}}fitToCanvas(t,e,s){if(t.width===e&&t.height===s)return t;let n=new Bt({width:e,height:s});return n.data.fill(0),Bt.bitblt(t,n,0,0,t.width,t.height,0,0),n}applyMasks(t,e){for(let s of e){let n=s.width<=1&&s.height<=1||s.x<=1&&s.y<=1&&(s.width<1||s.height<1),i=Math.round(n?s.x*t.width:s.x),r=Math.round(n?s.y*t.height:s.y),o=Math.round(n?s.width*t.width:s.width),l=Math.round(n?s.height*t.height:s.height),c=Math.max(0,r),d=Math.min(t.height,r+l),p=Math.max(0,i),u=Math.min(t.width,i+o);for(let h=c;h<d;h++)for(let m=p;m<u;m++){let b=t.width*h+m<<2;t.data[b]=0,t.data[b+1]=0,t.data[b+2]=0,t.data[b+3]=0}}}async approveBaseline(t,e,s){let n;if(s)n=await this.saveBaseline(t,e,s);else{let r=t.replace(/[^a-zA-Z0-9._-]/g,"_"),o=Et.join(this.baselineDir,`${r}_step${String(e).padStart(3,"0")}_current.png`);if(!Ee.existsSync(o))throw new Error(`No current screenshot found to approve for step ${e}`);let l=await Ee.promises.readFile(o);n=await this.saveBaseline(t,e,l)}let i=this.diffFilePath(t,e);if(Ee.existsSync(i))try{await Ee.promises.unlink(i)}catch{}return n}async clearBaselines(t){let e=t.replace(/[^a-zA-Z0-9._-]/g,"_");if(!Ee.existsSync(this.baselineDir))return;let s=await Ee.promises.readdir(this.baselineDir);await Promise.all(s.filter(n=>n.startsWith(e+"_step")).map(n=>Ee.promises.unlink(Et.join(this.baselineDir,n)).catch(()=>{})))}async deleteStepBaseline(t,e){let n=`${t.replace(/[^a-zA-Z0-9._-]/g,"_")}_step${String(e).padStart(3,"0")}`;if(!Ee.existsSync(this.baselineDir))return;let i=await Ee.promises.readdir(this.baselineDir);await Promise.all(i.filter(r=>r.startsWith(n)).map(r=>Ee.promises.unlink(Et.join(this.baselineDir,r)).catch(()=>{})))}},dn=new Ut});function pn(a){if(!a||a.length===0)return{loading:!1};for(let t of a){if(t.className&&xo.some(e=>t.className.includes(e)))return{loading:!0,reason:`Active widget <${t.className}>`};if(t.resourceId&&To.test(t.resourceId))return{loading:!0,reason:`Loading resource ID "${t.resourceId}"`};if(t.contentDesc&&Gr.test(t.contentDesc.trim()))return{loading:!0,reason:`Loading description "${t.contentDesc}"`};if(t.text&&Gr.test(t.text.trim()))return{loading:!0,reason:`Loading status text "${t.text}"`}}return{loading:!1}}async function un(a,t=4500,e=150){let s=Date.now(),n="",i=0,r=[];for(;Date.now()-s<t;){a.setLastHierarchy(null);let{flat:o}=await a.getCleanHierarchy(a.serial);if(r=o,pn(o).loading){i=0,n="",await new Promise(u=>setTimeout(u,220));continue}let c=ke(o);if(c===n&&c!=="0"){if(i++,i>=1)return{settled:!0,durationMs:Date.now()-s,finalHash:c,flat:r}}else i=0,n=c;let d=Date.now()-s,p=t-d;if(p<=0)break;await new Promise(u=>setTimeout(u,Math.min(e,p)))}return{settled:!1,durationMs:Date.now()-s,finalHash:n||(r.length>0?ke(r):"0"),flat:r}}var xo,To,Gr,Xr=ie(()=>{"use strict";et();xo=["ProgressBar","CircularProgressIndicator","LinearProgressIndicator","ContentLoadingProgressBar","LoadingView","ActivityIndicator"],To=/(?:loading|spinner|shimmer|skeleton|progress[-_]?bar|refreshing)/i,Gr=/^(?:loading|please wait|fetching|authenticating|syncing|processing)\.{0,3}$/i});import fe from"chalk";import Qr from"vm";function le(a){return new Promise(t=>setTimeout(t,a))}function Po(a){let t=a.screenDimensions.height,e=a.navBarHeight>0?t-a.navBarHeight:t,s=a.navBarHeight>0?e*.94:t*R.gestures.bottomNavThresholdRatio;return{effectiveHeight:e,bottomThreshold:s}}var hn,gn,$s,mn,fn,bn,yn,Sn,wn,vn,xn,Tn,Pn,kn,En,An,Dn,$n,Cn,In,Nn,Ln,Rn,Mn,Fn,On,ko,Zr,_n=ie(()=>{"use strict";je();Qt();ot();Es();Xr();et();hn=class{type="TAP";async execute(t,e){let s=t.target,{width:n,height:i}=e.screenDimensions,r=Math.round(n*.5),{flat:o,matched:l}=await e.waitForTarget(s,e.serial,R.polling.defaultTimeoutMs,t);if(l.length===0&&e.upcomingSteps&&e.upcomingSteps.length>0&&!o.some(N=>N.password===!0||N.className.includes("EditText")&&o.some(O=>O.text?.toLowerCase().includes("password")))){let N=e.upcomingSteps.findIndex(L=>L.type==="VERIFY"&&L.target&&e.matchNodes(o,L.target,L).length>0);if(N!==-1)return{fastForwarded:!0,reason:`Screen already displays "${e.upcomingSteps[N].target}"`,skipCount:N};let O=e.upcomingSteps.findIndex(L=>L.type==="TAP"&&L.target&&e.matchNodes(o,L.target,L).length>0);if(O!==-1)return{fastForwarded:!0,reason:`Upcoming action "${e.upcomingSteps[O].target}" is directly actionable`,skipCount:O}}if(l.length===0){await e.driver.swipe(r,Math.round(i*.75),r,Math.round(i*.35),300,e.serial),await le(350),e.setLastHierarchy(null);let P=await e.waitForTarget(s,e.serial,2e3,t);o=P.flat,l=P.matched}if(l.length===0)throw e.buildDiagnosticError(s,o,`Element matching "${s}" not found on current screen.`);let c=l.find(P=>P.clickable)||l[0];if(e.targetPackage&&!ks(c.packageName,e.targetPackage))throw console.log(fe.red(` \u{1F6A8} [Jail Guard] Blocked Tap on foreign package "${c.packageName}". Target is "${e.targetPackage}".`)),await on(o,e.targetPackage,e.driver,e.serial),new Error(`Security Boundary Violation: Refused to tap element belonging to foreign application "${c.packageName}". Containment enforced.`);if(await e.ensureInViewport(c,e.serial)){let P=await e.waitForTarget(s,e.serial,1500,t);P.matched.length>0&&(o=P.flat,l=P.matched,c=l.find(N=>N.clickable)||l[0])}let p=c.bounds.centerX,{effectiveHeight:u}=Po(e),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>i*.5&&typeof e.driver.isKeyboardVisible=="function")try{await e.driver.isKeyboardVisible(e.serial)&&(console.log(fe.gray(` [Keyboard] Target occluded by soft keyboard (Y=${h} > ${Math.round(i*.5)}). Dismissing...`)),await e.driver.pressBack(e.serial),await le(300),e.setLastHierarchy(null))}catch{}let m=Ke(o,e.screenDimensions),b=c.contentDesc||c.text||c.resourceId;console.log(fe.gray(` [Tap Target] (${p}, ${h}) for "${b||s}"`));let T=e.lastHierarchy?.hash;await e.driver.tap(p,h,e.serial),e.setLastHierarchy(null);let w=e.upcomingSteps&&e.upcomingSteps[0],S=w?.target?e.matchNodes(o,w.target,w).length>0:!1;if(w&&!S&&(w.type==="VERIFY"||w.type==="TAP")){let P=await un(e,4500),O=Ke(P.flat,e.screenDimensions)!==m,L=w.target?e.matchNodes(P.flat,w.target,w).length>0:!1;if(!O&&!L&&e.matchNodes(P.flat,s,t).length>0)throw new Error(`Navigation failed: Tap on "${s}" did not transition away from the current screen. Next expected element "${w.target}" is not visible.`)}else await le(R.settle.tapMs);b&&pe(b)!==pe(s)&&e.memory.recordSuccess("TAP",s,b,c.contentDesc?"contentDesc":c.text?"text":"resourceId",1,void 0,T)}},gn=class{type="LONG_PRESS";async execute(t,e){let s=t.target,{flat:n,matched:i}=await e.waitForTarget(s,e.serial,R.polling.defaultTimeoutMs,t);if(i.length===0)throw e.buildDiagnosticError(s,n,`Element matching "${s}" not found for long-press.`);let r=i[0];if(await e.ensureInViewport(r,e.serial)){let l=await e.waitForTarget(s,e.serial,1500,t);l.matched.length>0&&(r=l.matched[0])}await e.driver.swipe(r.bounds.centerX,r.bounds.centerY,r.bounds.centerX,r.bounds.centerY,R.gestures.longPressDurationMs,e.serial),await le(300),e.setLastHierarchy(null)}},$s=class{type="TYPE";async execute(t,e){let s=t.target,n=t.value??"",i=[],r=null;if(e.lastHierarchy&&Date.now()-e.lastHierarchy.timestamp<R.polling.hierarchyCacheTtlMs){let p=e.findTargetInputField(e.lastHierarchy.flat,s,n);p&&(i=e.lastHierarchy.flat,r=p)}if(r||(i=(await e.getCleanHierarchy(e.serial)).flat,r=e.findTargetInputField(i,s,n)),!r){let p=Date.now();for(;Date.now()-p<2e3&&(await le(200),i=(await e.getCleanHierarchy(e.serial)).flat,r=e.findTargetInputField(i,s,n),!r););}if(!r&&e.upcomingSteps&&e.upcomingSteps.length>0&&!i.some(u=>u.className.includes("EditText"))){let u=e.upcomingSteps.findIndex(h=>h.type==="VERIFY"&&h.target&&e.matchNodes(i,h.target,h).length>0);if(u!==-1)return{fastForwarded:!0,reason:`Screen already displays "${e.upcomingSteps[u].target}" and form is completed`,skipCount:u}}if(!r)throw e.buildDiagnosticError(s,i,`Input field matching "${s}" not found.`);if(e.targetPackage&&!ks(r.packageName,e.targetPackage))throw console.log(fe.red(` \u{1F6A8} [Jail Guard] Blocked Type on foreign package "${r.packageName}". Target is "${e.targetPackage}".`)),await on(i,e.targetPackage,e.driver,e.serial),new Error(`Security Boundary Violation: Refused to type into element belonging to foreign application "${r.packageName}". Containment enforced.`);if(r.text&&r.text===n){console.log(fe.gray(` [Input Field] Already contains desired value "${n}". Skipping typing.`));return}if(await e.ensureInViewport(r,e.serial)){i=(await e.getCleanHierarchy(e.serial)).flat;let u=e.findTargetInputField(i,s,n);u&&(r=u)}let{height:l}=e.screenDimensions;if(r.bounds.centerY>l*.5&&typeof e.driver.isKeyboardVisible=="function")try{await e.driver.isKeyboardVisible(e.serial)&&(console.log(fe.gray(" [Keyboard] Input field occluded by soft keyboard. Dismissing before focus...")),await e.driver.pressBack(e.serial),await le(300),e.setLastHierarchy(null))}catch{}let c=e.lastHierarchy?.hash;await e.driver.tap(r.bounds.centerX,r.bounds.centerY,e.serial),await le(R.settle.inputFocusMs),await e.driver.clearField(e.serial),await le(R.settle.inputClearMs),await e.driver.inputText(n,e.serial),await le(R.settle.inputDoneMs),e.setLastHierarchy(null);let d=r.contentDesc||r.resourceId;d&&pe(d)!==pe(s)&&e.memory.recordSuccess("TYPE",s,d,r.contentDesc?"contentDesc":"resourceId",1,void 0,c)}},mn=class{type="CLEAR";async execute(t,e){let s=t.target,n=[],i=null;if(e.lastHierarchy&&Date.now()-e.lastHierarchy.timestamp<R.polling.hierarchyCacheTtlMs&&(i=e.findTargetInputField(e.lastHierarchy.flat,s),i&&(n=e.lastHierarchy.flat)),i||(n=(await e.getCleanHierarchy(e.serial)).flat,i=e.findTargetInputField(n,s)),!i){let o=e.matchNodes(n,s,t);o.length>0&&(i=o[0])}if(!i)throw e.buildDiagnosticError(s,n,`Input field matching "${s}" not found to clear.`);await e.driver.tap(i.bounds.centerX,i.bounds.centerY,e.serial),await le(300);let r=e.driver;r.clearField?await r.clearField(e.serial):r.clearInput&&await r.clearInput(50,e.serial),await le(200),e.setLastHierarchy(null)}},fn=class{type="VERIFY";async execute(t,e){let s=t.target,n=t.expectedState||"visible";if(t.negative===!0||n==="absent"||n==="hidden"){let u=await e.getCleanHierarchy(e.serial);if(e.matchNodes(u.flat,s,t).length>0)throw new Error(`Negative assertion failed: "${s}" was expected to be NOT visible, but is present on screen.`);return}let{width:r,height:o}=e.screenDimensions,l=Math.round(r*.5),c=!1,d=[],p;if(e.lastHierarchy&&Date.now()-e.lastHierarchy.timestamp<R.polling.verifyCacheTtlMs&&(p=e.matchNodes(e.lastHierarchy.flat,s,t)[0],p&&(d=e.lastHierarchy.flat,c=!0,e.lastHierarchy.timestamp=Date.now())),!c){d=(await e.getCleanHierarchy(e.serial)).flat,p=e.matchNodes(d,s,t)[0],p?c=!0:pn(d).loading&&(console.log(fe.cyan(" \u23F3 [Quiescence] Loading indicator detected in Verify. Waiting for screen to settle...")),d=(await un(e,4500)).flat,p=e.matchNodes(d,s,t)[0],p&&(c=!0));let h=Date.now();for(;!c&&Date.now()-h<R.polling.extendedTimeoutMs;){if(d=(await e.getCleanHierarchy(e.serial)).flat,p=e.matchNodes(d,s,t)[0],p){c=!0,e.lastHierarchy&&(e.lastHierarchy.timestamp=Date.now()),await le(400);break}await le(200)}}if(c||(await e.driver.swipe(l,Math.round(o*.75),l,Math.round(o*.35),300,e.serial),await le(400),d=(await e.getCleanHierarchy(e.serial)).flat,p=e.matchNodes(d,s,t)[0],p&&(c=!0)),!c){if(e.upcomingSteps&&e.upcomingSteps.length>0&&!d.some(h=>h.password===!0||h.className.includes("EditText")&&d.some(m=>m.text?.toLowerCase().includes("password")))){let h=e.upcomingSteps.findIndex(m=>m.type==="VERIFY"&&m.target&&e.matchNodes(d,m.target,m).length>0);if(h!==-1)return{fastForwarded:!0,reason:`Screen is already on "${e.upcomingSteps[h].target}"`,skipCount:h}}throw await e.driver.swipe(l,Math.round(o*.35),l,Math.round(o*.75),300,e.serial),await le(300),e.buildDiagnosticError(s,d,`Verification assertion failed: "${s}" is not visible on screen.`)}if(p&&n==="disabled"&&p.enabled)throw new Error(`State assertion failed: "${s}" was expected to be DISABLED, but is enabled.`);if(p&&n==="enabled"&&!p.enabled)throw new Error(`State assertion failed: "${s}" was expected to be ENABLED, but is disabled.`)}},bn=class{type="BACK";async execute(t,e){let{flat:s}=await e.getCleanHierarchy(e.serial),n=rn(s);if(n&&_t(n)){console.log(fe.yellow(` \u{1F6E1}\uFE0F [Jail Guard] Device is already on launcher (${n}). Suppressing Back press to prevent app exit loop.`)),e.targetPackage&&await e.driver.launchApp(e.targetPackage,e.serial);return}await e.driver.pressKey(4,e.serial),await le(R.settle.backMs),e.setLastHierarchy(null)}},yn=class{type="SCROLL";async execute(t,e){let{width:s,height:n}=e.screenDimensions,i=Math.round(s*.5);if(t.target&&t.target!=="up"&&t.target!=="down"){let o=t.target,l=t.direction||"down",c=!1;for(let d=0;d<5;d++){let{flat:p}=await e.getCleanHierarchy(e.serial),u=e.matchNodes(p,o,t);if(u.length>0){await e.ensureInViewport(u[0],e.serial),c=!0;break}l==="up"?await e.driver.swipe(i,Math.round(n*.35),i,Math.round(n*.75),300,e.serial):await e.driver.swipe(i,Math.round(n*.75),i,Math.round(n*.35),300,e.serial),await le(R.settle.scrollMs),e.setLastHierarchy(null)}if(!c){let{flat:d}=await e.getCleanHierarchy(e.serial);throw e.buildDiagnosticError(o,d,`Element "${o}" not found after scrolling ${t.direction||"down"}.`)}}else t.target==="up"||t.direction==="up"?await e.driver.swipe(i,Math.round(n*.35),i,Math.round(n*.75),300,e.serial):await e.driver.swipe(i,Math.round(n*.75),i,Math.round(n*.35),300,e.serial),await le(R.settle.scrollMs),e.setLastHierarchy(null)}},Sn=class{type="SWIPE";async execute(t,e){let{width:s,height:n}=e.screenDimensions,i=Math.round(s*.5),r=t.direction||"left";if(t.target){let{matched:p}=await e.waitForTarget(t.target,e.serial,2500,t);if(p.length>0){let u=p[0],h=r==="left"?u.bounds.x2-40:r==="right"?u.bounds.x1+40:u.bounds.centerX,m=r==="left"?u.bounds.x1+40:r==="right"?u.bounds.x2-40:u.bounds.centerX,b=r==="up"?u.bounds.y2-40:r==="down"?u.bounds.y1+40:u.bounds.centerY,T=r==="up"?u.bounds.y1+40:r==="down"?u.bounds.y2-40:u.bounds.centerY;await e.driver.swipe(h,b,m,T,R.gestures.swipeDurationMs,e.serial),await le(400),e.setLastHierarchy(null);return}}let o=r==="left"?Math.round(s*.8):r==="right"?Math.round(s*.2):i,l=r==="left"?Math.round(s*.2):r==="right"?Math.round(s*.8):i,c=Math.round(r==="up"?n*.75:r==="down"?n*.35:n*.5),d=Math.round(r==="up"?n*.35:r==="down"?n*.75:n*.5);await e.driver.swipe(o,c,l,d,R.gestures.swipeDurationMs,e.serial),await le(400),e.setLastHierarchy(null)}},wn=class{type="TOGGLE";async execute(t,e){let s=t.target,{flat:n,matched:i}=await e.waitForTarget(s,e.serial,R.polling.defaultTimeoutMs,t);if(i.length===0)throw e.buildDiagnosticError(s,n,`Toggle element matching "${s}" not found.`);let r=i.find(l=>l.clickable)||i[0];if(await e.ensureInViewport(r,e.serial)){let l=await e.waitForTarget(s,e.serial,1500,t);l.matched.length>0&&(r=l.matched.find(c=>c.clickable)||l.matched[0])}if(t.desiredState!==void 0&&r.checked!==void 0&&r.checked===t.desiredState){console.log(fe.gray(` [Toggle] "${s}" is already in desired state (${t.desiredState?"ON":"OFF"}).`));return}await e.driver.tap(r.bounds.centerX,r.bounds.centerY,e.serial),await le(R.settle.toggleMs),e.setLastHierarchy(null)}},vn=class{type="SELECT";async execute(t,e){let s=t.target,n=t.value,{flat:i,matched:r}=await e.waitForTarget(s,e.serial,R.polling.defaultTimeoutMs,t);if(r.length===0)throw e.buildDiagnosticError(s,i,`Dropdown / picker element matching "${s}" not found.`);let o=r.find(d=>d.clickable)||r[0];await e.driver.tap(o.bounds.centerX,o.bounds.centerY,e.serial),await le(R.settle.selectMs),e.setLastHierarchy(null);let l=await e.waitForTarget(n,e.serial,R.polling.defaultTimeoutMs);if(l.matched.length===0)throw e.buildDiagnosticError(n,l.flat,`Dropdown option "${n}" not found.`);let c=l.matched[0];await e.driver.tap(c.bounds.centerX,c.bounds.centerY,e.serial),await le(400),e.setLastHierarchy(null)}},xn=class{type="LAUNCH_APP";async execute(t,e){let s=t.target;if(!s)throw new Error("Target package name required to launch app.");e.targetPackage=s,await e.driver.launchApp(s,e.serial),await le(R.settle.launchMs),e.setLastHierarchy(null)}},Tn=class{type="RESTART_APP";async execute(t,e){let s=t.target;if(!s)throw new Error("Target package name required to restart app.");e.targetPackage=s,await e.driver.restartApp(s,e.serial),await le(R.settle.launchMs),e.setLastHierarchy(null)}},Pn=class{type="TERMINATE_APP";async execute(t,e){let s=t.target||await e.driver.getForegroundPackage(e.serial);s&&await e.driver.forceStop(s,e.serial),await le(R.settle.stopMs),e.setLastHierarchy(null)}},kn=class{type="CLEAR_DATA";async execute(t,e){let s=t.target;if(!s)throw new Error("Target package name required to clear data.");await e.driver.clearAppData(s,e.serial),await le(1e3),e.setLastHierarchy(null)}},En=class{type="GRANT_PERMISSION";async execute(t,e){let s=t.value,n=t.target||await e.driver.getForegroundPackage(e.serial);if(!n)throw new Error("Foreground package not detected for grant permission.");await e.driver.grantPermission(n,s,e.serial),await le(400)}},An=class{type="DEEP_LINK";async execute(t,e){let s=t.target;await e.driver.openDeepLink(s,e.serial),await le(R.settle.launchMs),e.setLastHierarchy(null)}},Dn=class{type="WAIT";async execute(t,e){await le(t.durationMs||1e3)}},$n=class{type="IF_VISIBLE";async execute(t,e){let s=t.conditionTarget||t.target;if(!s)return;let n=!1;try{let{matched:i}=await e.waitForTarget(s,e.serial,1500,t);n=i.length>0}catch{n=!1}n?(console.log(fe.cyan(` [Conditional] Condition met: "${s}" is visible.`)),t.thenSteps&&t.thenSteps.length>0&&e.executeChildSteps&&await e.executeChildSteps(t.thenSteps,e)):(console.log(fe.gray(` [Conditional] Condition not met: "${s}" is not visible.`)),t.elseSteps&&t.elseSteps.length>0&&e.executeChildSteps&&await e.executeChildSteps(t.elseSteps,e))}},Cn=class{type="LOOP";async execute(t,e){let s=R.flow?.maxLoopIterations||50,n=Math.min(t.loopCount||1,s),i=t.childSteps||[];if(!(i.length===0||!e.executeChildSteps)){console.log(fe.cyan(` [Loop] Starting loop iteration (1 to ${n})...`));for(let r=1;r<=n;r++){t.loopVariable&&e.variables.set(t.loopVariable,String(r)),e.loopStack.push({current:r,total:n,variable:t.loopVariable});try{await e.executeChildSteps(i,e)}finally{e.loopStack.pop()}}}}},In=class{type="WAIT_UNTIL";async execute(t,e){let s=t.target,n=t.durationMs||R.flow?.waitUntilTimeoutMs||1e4,i=t.expectedState||"visible";console.log(fe.gray(` [Wait Until] Waiting for "${s}" to be ${i} (up to ${n}ms)...`));let r=Date.now(),o=!1;for(;Date.now()-r<n;){try{let{matched:l}=await e.waitForTarget(s,e.serial,1e3,t);if(l.length>0){let c=l[0];if(i==="enabled"&&!c.enabled){await le(250);continue}o=!0;break}}catch{}await le(250)}if(!o){let{flat:l}=await e.getCleanHierarchy(e.serial);throw e.buildDiagnosticError(s,l,`WaitUntil assertion failed: "${s}" did not become ${i} within ${n}ms.`)}}},Nn=class{type="SET_VARIABLE";async execute(t,e){let s=t.variableName,n=t.variableExpression||"";n=Lt(n,Object.fromEntries(e.variables));let i=xr(n);e.variables.set(s,i),console.log(fe.cyan(` [Variable] $${s} = "${i}"`))}},Ln=class{type="DEVICE_STATE";async execute(t,e){let s=t.deviceAction,n=t.deviceStateValue;switch(s){case"wifi":{let i=n===!0||n==="on"||n==="enable";console.log(fe.cyan(` [Device] Setting Wi-Fi: ${i?"ENABLED":"DISABLED"}`)),await e.driver.setWifiEnabled(i,e.serial);break}case"airplane":{let i=n===!0||n==="on"||n==="enable";console.log(fe.cyan(` [Device] Setting Airplane Mode: ${i?"ENABLED":"DISABLED"}`)),await e.driver.setAirplaneMode(i,e.serial);break}case"orientation":{let i=n==="landscape"?"landscape":"portrait";console.log(fe.cyan(` [Device] Setting Orientation: ${i.toUpperCase()}`)),await e.driver.setOrientation(i,e.serial);break}case"notifications":{let i=n==="open"||n==="expand"||n===!0;console.log(fe.cyan(` [Device] ${i?"Expanding":"Collapsing"} Notification Shade`)),i?await e.driver.openNotificationShade(e.serial):await e.driver.collapseNotificationShade(e.serial);break}default:throw new Error(`Unsupported device action: ${s}`)}await le(400),e.setLastHierarchy(null)}},Rn=class{type="RUN_JS";async execute(t,e){let s=t.jsCode||t.value||"";if(!s)return;console.log(fe.cyan(" [JS Hook] Executing sandboxed JavaScript snippet..."));let n={vars:Object.fromEntries(e.variables),setVar:(r,o)=>{e.variables.set(r,String(o))},getVar:r=>e.variables.get(r),console:{log:(...r)=>console.log(fe.gray(" [JS Output]",...r)),error:(...r)=>console.error(fe.red(" [JS Error]",...r))}},i=Qr.createContext(n);Qr.runInContext(s,i,{timeout:3e3});for(let[r,o]of Object.entries(n.vars))e.variables.set(r,String(o))}},Mn=class{type="ENTER_OTP";async execute(t,e){console.log(fe.cyan(" [OTP Capture] Detecting verification code..."));let s=null;if(process.env.PROMPTTEST_MOCK_OTP)s=process.env.PROMPTTEST_MOCK_OTP.trim(),console.log(fe.gray(` [OTP Capture] Captured code from environment: ${s}`));else try{let{stdout:n}=await e.driver.execAdb(["shell","logcat","-d","-t","200","-s","SMSDispatcher:V","SmsMessage:V","NotificationService:V","ActivityManager:I"],e.serial),i=n.match(/(?:code|otp|pin|verification)[^0-9\r\n]{0,35}(\d{4,8})\b/i)||n.match(/\b(\d{4,8})\s*(?:is your verification|is your otp)/i);i&&(s=i[1],console.log(fe.gray(` [OTP Capture] Captured code from logcat: ${s}`)))}catch{}if(s||(s="123456",console.log(fe.yellow(` [OTP Capture] No live code detected; using simulated OTP: ${s}`))),t.target){let n={originalText:`Type '${s}' into '${t.target}'`,type:"TYPE",target:t.target,value:s};return new $s().execute(n,e)}else{let{flat:n}=await e.getCleanHierarchy(e.serial),i=e.findTargetInputField(n,"code",s)||n.find(r=>r.className.includes("EditText"));i?(await e.driver.tap(i.bounds.centerX,i.bounds.centerY,e.serial),await le(250),await e.driver.inputText(s,e.serial)):await e.driver.inputText(s,e.serial)}}},Fn=class{type="API_REQUEST";async execute(t,e){let s=t.apiMethod||"GET",n=t.apiUrl||"";if(!n)throw new Error("API_REQUEST requires a valid target URL.");console.log(fe.cyan(` [API Request] ${s} ${n}...`));let i={method:s,headers:{"Content-Type":"application/json",Accept:"application/json"}};t.apiBody&&(s==="POST"||s==="PUT"||s==="PATCH")&&(i.body=t.apiBody);let r=await fetch(n,i);if(t.apiExpectedStatus!==void 0){if(r.status!==t.apiExpectedStatus)throw new Error(`API status assertion failed for ${n}: expected HTTP ${t.apiExpectedStatus}, but received HTTP ${r.status}`);console.log(fe.green(` \u2714 API Status HTTP ${r.status} matched expectation.`))}if(t.apiStorePath&&t.apiStoreVariable)try{let o=await r.text(),l=JSON.parse(o),c=this.resolveJsonPath(l,t.apiStorePath);c!==void 0&&(e.variables.set(t.apiStoreVariable,String(c)),console.log(fe.cyan(` [Variable] $${t.apiStoreVariable} = "${c}" (from ${t.apiStorePath})`)))}catch(o){ct.warn(`Failed to parse JSON response for path extraction: ${o.message}`)}}resolveJsonPath(t,e){let s=e.replace(/^\$\.?/,"").split("."),n=t;for(let i of s){if(n==null||typeof n!="object")return;n=n[i]}return n}},On=class{type="EMAIL_ASSERT";async execute(t,e){let s=t.emailRecipient||"",n=t.emailSubject||"";console.log(fe.cyan(` [Email Assertion] Checking email to "${s}" with subject "${n}"...`));let i=[];if(process.env.PROMPTTEST_MOCK_EMAILS)try{i=JSON.parse(process.env.PROMPTTEST_MOCK_EMAILS)}catch{i=[]}let r=i.find(o=>o.to.toLowerCase()===s.toLowerCase()&&o.subject.toLowerCase().includes(n.toLowerCase()));if(!r){if(i.length===0){if(!s.includes("@"))throw new Error(`Invalid email recipient address: "${s}"`);console.log(fe.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 "${n}" found.`)}if(t.emailBodyContains&&r.body&&!r.body.includes(t.emailBodyContains))throw new Error(`Email body assertion failed: Expected email to contain "${t.emailBodyContains}", but content did not match.`);console.log(fe.green(" \u2714 Email verified successfully."))}},ko=[new hn,new gn,new $s,new mn,new fn,new bn,new yn,new Sn,new wn,new vn,new xn,new Tn,new Pn,new kn,new En,new An,new Dn,new $n,new Cn,new In,new Nn,new Ln,new Rn,new Mn,new Fn,new On],Zr=new Map(ko.map(a=>[a.type,a]))});var jt,Cs=ie(()=>{"use strict";ot();Ps();jt=class{providerName="heuristic";async resolveIntent(t,e){let s=pe(t),n=s.includes("login")||s.includes("log in")||s.includes("sign in")||s.includes("signin");if(n&&(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(n&&(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(n&&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(n)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 i=t.match(/(?:login|sign\s*in)\s+(?:with\s+)?(?:user(?:name)?|email)?\s*["']([^"']+)["']\s*(?:and\s+)?(?:password|pass)?\s*["']([^"']+)["']/i);if(i){let o=i[1],l=i[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 r=t.match(/search\s+(?:for\s+)?["']([^"']+)["']/i);if(r){let o=r[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(t,e,s){let n=[],i=pe(t),r=new Set,o=Yr(t,s?.locale);for(let p of o){let u=e.find(h=>h.bounds.width>0&&h.bounds.height>0&&(me(p,h.text)||me(p,h.contentDesc)));if(u){let h=(u.text||u.contentDesc||p).trim();r.has(h)||(r.add(h),n.push({text:h,score:.9,node:u,strategy:"synonym",reason:`Direct synonym match for "${t}" via semantic dictionary`}))}}for(let p of e)if(p.resourceId&&p.bounds.width>0&&p.bounds.height>0&&p.resourceId.toLowerCase().split(/[:/_]/).some(m=>m===i||i.includes(m))){let m=(p.text||p.contentDesc||p.resourceId).trim();r.has(m)||(r.add(m),n.push({text:m,score:.85,node:p,strategy:"exact",reason:`Resource-ID segment matches target "${t}"`}))}let l=[];for(let p of e)p.bounds.width>0&&p.bounds.height>0&&(p.text&&p.text.trim().length>1&&l.push({text:p.text.trim(),node:p}),p.contentDesc&&p.contentDesc.trim().length>1&&l.push({text:p.contentDesc.trim(),node:p}));let c=Array.from(new Set(l.map(p=>p.text))),d=Gt(t,c,5);for(let p of d)if(p.score>=.7&&!r.has(p.text)){r.add(p.text);let u=l.find(h=>h.text===p.text)?.node;n.push({text:p.text,score:p.score,node:u,strategy:"fuzzy",reason:`String similarity match (${Math.round(p.score*100)}%)`})}return n.sort((p,u)=>u.score-p.score)}}});var es,Is,Hn=ie(()=>{"use strict";Cs();Qt();es=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(t,e){this.mockIntents.set(t.toLowerCase().trim(),e)}setMockSuggestion(t,e){this.mockSuggestions.set(t.toLowerCase().trim(),e)}async resolveIntent(t,e){let s=t.toLowerCase().trim();return this.mockIntents.has(s)?this.mockIntents.get(s):null}async findSuggestions(t,e,s){let n=t.toLowerCase().trim();return this.mockSuggestions.has(n)?this.mockSuggestions.get(n):new jt().findSuggestions(t,e)}},Is=class{providerName;config;heuristicFallback;constructor(t){this.config=t,this.providerName=t.provider,this.heuristicFallback=new jt}async resolveIntent(t,e){if(this.config.provider==="mock")return new es().resolveIntent(t,e);try{let s=`You are a test automation engineer generating steps for PromptTest.
812
843
  Convert the following high-level user intent into a concrete sequence of plain-English test steps.
813
844
  Valid syntax examples:
814
845
  - Tap 'Button Label'
@@ -821,70 +852,89 @@ Valid syntax examples:
821
852
  User Intent: "${t}"
822
853
 
823
854
  Return ONLY a JSON array of plain strings representing each step, e.g.:
824
- ["Type 'user@test.com' into 'Email'", "Tap 'Next'"]`,n=await this.callLlm(s);if(!n)return this.heuristicFallback.resolveIntent(t,e);let r=n.match(/\[[\s\S]*\]/);return r?JSON.parse(r[0]).map(a=>({originalText:a,type:a.startsWith("Tap")?"TAP":a.startsWith("Type")?"TYPE":"VERIFY"})):this.heuristicFallback.resolveIntent(t,e)}catch(s){return nt.warn(`LLM intent resolution failed (${s.message}), falling back to heuristic engine.`),this.heuristicFallback.resolveIntent(t,e)}}async findSuggestions(t,e,s){if(this.config.provider==="mock")return new Xt().findSuggestions(t,e,s);try{let n=e.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 "${t}" could not be found.
855
+ ["Type 'user@test.com' into 'Email'", "Tap 'Next'"]`,n=await this.callLlm(s);if(!n)return this.heuristicFallback.resolveIntent(t,e);let i=n.match(/\[[\s\S]*\]/);return i?JSON.parse(i[0]).map(l=>({originalText:l,type:l.startsWith("Tap")?"TAP":l.startsWith("Type")?"TYPE":"VERIFY"})):this.heuristicFallback.resolveIntent(t,e)}catch(s){return ct.warn(`LLM intent resolution failed (${s.message}), falling back to heuristic engine.`),this.heuristicFallback.resolveIntent(t,e)}}async findSuggestions(t,e,s){if(this.config.provider==="mock")return new es().findSuggestions(t,e,s);try{let n=e.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),i=`A UI test failed because an element named "${t}" could not be found.
825
856
  Here are the visible UI elements on the current Android screen:
826
857
  ${JSON.stringify(n,null,2)}
827
858
 
828
859
  Identify the best matching replacement element for "${t}".
829
860
  Respond ONLY with a JSON object in this format:
830
- {"bestMatch": "Exact Text of Best Element", "confidence": 0.85, "reason": "Reason for match"}`,i=await this.callLlm(r);if(!i)return this.heuristicFallback.findSuggestions(t,e,s);let o=i.match(/\{[\s\S]*\}/);if(!o)return this.heuristicFallback.findSuggestions(t,e,s);let a=JSON.parse(o[0]);if(a.bestMatch){let l=e.find(d=>d.text===a.bestMatch||d.contentDesc===a.bestMatch);return[{text:a.bestMatch,score:typeof a.confidence=="number"?a.confidence:.85,node:l,strategy:"llm",reason:a.reason||"LLM semantic match"},...await this.heuristicFallback.findSuggestions(t,e,s)]}}catch(n){nt.warn(`LLM self-healing failed (${n.message}), using heuristic suggestions.`)}return this.heuristicFallback.findSuggestions(t,e,s)}async callLlm(t){let e=this.config.timeoutMs||8e3,s=new AbortController,n=setTimeout(()=>s.abort(),e);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 i=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:t}],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 r=this.config.apiKey||process.env.ANTHROPIC_API_KEY;if(!r)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":r,"anthropic-version":"2023-06-01"},body:JSON.stringify({model:this.config.model||"claude-3-haiku-20240307",max_tokens:1024,messages:[{role:"user",content:t}]}),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 r=this.config.endpoint||process.env.OLLAMA_BASE_URL||"http://localhost:11434",i=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:t}],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(n)}}}});var bi=de(()=>{"use strict"});function bn(c){let t=c?.provider||I.ai.provider;if(t==="heuristic")return new _t;if(t==="mock")return new Xt;let e={provider:t,model:c?.model||I.ai.model,endpoint:c?.endpoint||I.ai.endpoint};return new ws(e)}var yn=de(()=>{"use strict";Ss();fn();Be();bi();Ss();fn()});import yi from"fs";import Sn from"path";var Gt,wn=de(()=>{"use strict";Gt=class{static extractDataDirective(t,e=process.cwd()){let s=t.split(`
831
- `),n="",r=-1;for(let d=0;d<s.length;d++){let p=s[d].trim();if(p.startsWith("#!data")||p.startsWith("#! data")){n=p,r=d;break}}if(r===-1)return null;let i=n.replace(/^#!\s*data\s*/,"").trim(),o=[],a="inline";if(i.startsWith("[")||i.startsWith("{"))try{let d=JSON.parse(i);o=Array.isArray(d)?d.map(p=>this.stringifyObject(p)):[this.stringifyObject(d)]}catch(d){throw new Error(`Failed to parse inline #!data JSON directive: ${d.message}`)}else{let d=i.replace(/^['"]+|['"]+$/g,"").trim(),p=Sn.isAbsolute(d)?d:Sn.resolve(e,d);a=p,o=this.loadFromFile(p)}let l=[...s];return l.splice(r,1),{directive:n,source:a,cleanedSpec:l.join(`
832
- `),rows:o}}static loadFromFile(t){if(!yi.existsSync(t))throw new Error(`Data file not found: ${t}`);let e=yi.readFileSync(t,"utf-8");return Sn.extname(t).toLowerCase()===".json"?this.parseJson(e):this.parseCsv(e)}static parseJson(t){try{let e=JSON.parse(t);return Array.isArray(e)?e.map(s=>this.stringifyObject(s)):[this.stringifyObject(e)]}catch(e){throw new Error(`Invalid JSON data file format: ${e.message}`)}}static parseCsv(t){let e=this.splitCsvLines(t);if(e.length<2)return[];let s=this.parseCsvRow(e[0]).map(r=>r.trim().replace(/^['"]+|['"]+$/g,"")),n=[];for(let r=1;r<e.length;r++){if(!e[r].trim())continue;let o=this.parseCsvRow(e[r]),a={};for(let l=0;l<s.length;l++){let d=s[l];a[d]=o[l]!==void 0?o[l].trim():""}n.push(a)}return n}static filterRows(t,e){if(!e)return t;let s=[...t];if(e.onlyRow!==void 0&&e.onlyRow>0){let n=e.onlyRow-1;if(n<s.length)return[s[n]];throw new Error(`Requested --only-row=${e.onlyRow}, but dataset only contains ${s.length} row(s).`)}if(e.iterations!==void 0&&e.iterations>0){if(e.iterations<=s.length)return s.slice(0,e.iterations);let n=[];for(let r=0;r<e.iterations;r++)n.push(s[r%s.length]);return n}return s}static parseCsvRow(t){let e=[],s="",n=!1;for(let r=0;r<t.length;r++){let i=t[r];i==='"'?n&&t[r+1]==='"'?(s+='"',r++):n=!n:i===","&&!n?(e.push(s),s=""):s+=i}return e.push(s),e}static splitCsvLines(t){let e=[],s="",n=!1;for(let r=0;r<t.length;r++){let i=t[r];i==='"'?n&&t[r+1]==='"'?(s+='""',r++):(n=!n,s+=i):(i===`
833
- `||i==="\r")&&!n?(i==="\r"&&t[r+1]===`
834
- `&&r++,s.trim().length>0&&e.push(s),s=""):s+=i}return s.trim().length>0&&e.push(s),e}static stringifyObject(t){if(!t||typeof t!="object")return{};let e={};for(let[s,n]of Object.entries(t))e[s]=n!=null?String(n):"";return e}}});var wi={};Tt(wi,{AutonomousRecorder:()=>St,autonomousRecorder:()=>xn});import ke from"fs";import xe from"path";import Y from"chalk";import{spawn as fr}from"child_process";import Si from"readline";import{createRequire as br}from"module";var yr,vn,St,xn,Kt=de(()=>{"use strict";Fe();$s();rs();Ke();Be();yr=br(import.meta.url),vn="1.1.0";try{vn=yr("../../package.json").version||"1.1.0"}catch{}St=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(t){this.driver=t||re}flushToDisk(){if(this.resolvedOutputPath)try{this.saveSpec(this.resolvedOutputPath)}catch{}if(this.resolvedReportPath)try{this.saveReport(this.resolvedReportPath)}catch{}}appendStep(t,e){if(this.recordedSteps[this.recordedSteps.length-1]!==t){this.recordedSteps.push(t),this.flushToDisk(),e&&console.log(e);try{this.onStep?.(t)}catch{}}}extractBrandSlug(t){if(!t)return"app";let e=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=t.split("."),n=s.filter(i=>!e.has(i.toLowerCase())&&!/^\d+$/.test(i));return((n.length>0?n[n.length-1]:s[s.length-1])||"app").toLowerCase().replace(/[^a-z0-9_]/g,"")}async startSession(t,e,s){let n=await this.driver.ensureDefaultDevice(e);if(!n)throw new Error("No Android device connected for recording.");this.deviceLock=Rt(n),this.targetSerial=n,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(n);h&&(this.targetPackage=h)}catch{}let r=this.extractBrandSlug(this.targetPackage),i;if(t&&t!=="specs/recorded_suite.txt")i=xe.resolve(t);else{let h=`${r}_flow`,u=xe.resolve(`specs/${h}.txt`);if(!this.isAppendMode&&ke.existsSync(u)){let m=1;for(u=xe.resolve(`specs/${h}_${m}.txt`);ke.existsSync(u);)m++,u=xe.resolve(`specs/${h}_${m}.txt`)}i=u}if(this.resolvedOutputPath=i,ke.existsSync(i))if(this.isAppendMode){let h=ke.readFileSync(i,"utf-8"),u=h.split(`
835
- `);for(let f of u){let x=f.trim();if(/^\d+\.\s+/.test(x))this.existingSteps.push(x.replace(/^\d+\.\s+/,""));else if(x.startsWith("# Package:")){let v=x.match(/# Package:\s*([a-zA-Z0-9._]+)/);v&&(this.targetPackage=v[1])}}let m=`${i}.bak`;ke.writeFileSync(m,h,"utf-8"),console.log(Y.cyan(`\u{1F4E5} Loaded ${this.existingSteps.length} existing step(s) from: ${xe.basename(i)}`)),console.log(Y.gray(`\u{1F4E6} Safety backup preserved at: ${xe.basename(m)}`))}else{let h=ke.readFileSync(i,"utf-8"),u=`${i}.bak`;ke.writeFileSync(u,h,"utf-8"),console.log(Y.yellow(`\u26A0\uFE0F File "${xe.basename(i)}" already exists.`)),console.log(Y.gray(`\u{1F4E6} Safety backup preserved at: ${xe.basename(u)}`))}await this.driver.wakeDevice(n).catch(()=>{});let a=await this.driver.getScreenDimensions(n);this.displayWidth=a.width||I.screen.fallbackWidth,this.displayHeight=a.height||I.screen.fallbackHeight,this.touchMaxX=this.displayWidth,this.touchMaxY=this.displayHeight;try{let{stdout:h}=await this.driver.execAdb(["shell","getevent","-p"],n),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=xe.basename(i,".txt"),d=xe.resolve("output");this.screenshotDir=xe.join(d,"screenshots",l),ke.existsSync(this.screenshotDir)||ke.mkdirSync(this.screenshotDir,{recursive:!0});let p=xe.join(d,`${l}_report.md`);this.resolvedReportPath=p,await this.refreshHierarchySync(),this.startLogcatScreenWatcher(n),this.startLogcatCrashWatcher(n),this.startEventStream(n),this.flushToDisk()}async stopSession(){if(this.isRecording=!1,this.inputDebounceTimer&&(clearTimeout(this.inputDebounceTimer),this.inputDebounceTimer=null),this.deviceLock&&(Lt(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(t,e,s){await this.startSession(t,e,s);let n=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"),r=await this.driver.getDeviceModel(this.targetSerial).catch(()=>this.targetSerial);console.log(`
836
- ${n}`),console.log(Y.bold.cyan(`\u26A1 PromptTest v${vn}`)+Y.gray(" | Recording Session")),console.log(n),console.log(` ${Y.gray("\u{1F4F1} Device ")} ${Y.white(r)} ${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(xe.basename(this.resolvedOutputPath))}`),console.log(` ${Y.gray("\u{1F4CA} Report ")} ${Y.yellow(xe.basename(this.resolvedReportPath))}`),console.log(` ${Y.gray("\u{1F4F8} Photos ")} ${Y.yellow(xe.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(n),console.log(` ${Y.green("\u2714")} Ready & streaming`),console.log(n),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(`${n}
837
- `);let i=null,o=async()=>{if(i)try{i.close()}catch{}await this.stopSession(),this.printExitSummary(n),process.exit(0)};if(process.once("SIGINT",o),process.once("SIGTERM",o),process.stdin.isTTY)try{i=Si.createInterface({input:process.stdin,output:process.stdout}),i.on("line",l=>{let d=l.trim().toLowerCase();(d==="exit"||d==="quit"||d==="q"||d==="stop")&&o()}),i.on("SIGINT",()=>{o()})}catch{}let a=0;for(;this.isRecording;)if(await new Promise(l=>setTimeout(l,1500)),a++,a%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(t){let e=t?["-s",t,"shell","getevent","-l"]:["shell","getevent","-l"],s=tt(),n=fr(s,e);this.geteventProcess=n;let r=null,i=null,o=null,a=null,l=0;Si.createInterface({input:n.stdout}).on("line",p=>{if(this.isRecording)if(p.includes("ABS_MT_POSITION_X")){let h=p.match(/ABS_MT_POSITION_X\s+([0-9a-fA-F]+)/);h&&(r=parseInt(h[1],16))}else if(p.includes("ABS_MT_POSITION_Y")){let h=p.match(/ABS_MT_POSITION_Y\s+([0-9a-fA-F]+)/);h&&(i=parseInt(h[1],16))}else if(p.includes("BTN_TOUCH")&&p.includes("DOWN"))o=r,a=i,l=Date.now();else if(p.includes("BTN_TOUCH")&&p.includes("UP")){let h=r!==null?r:o,u=i!==null?i:a,m=Date.now()-l;if(h!==null&&u!==null&&o!==null&&a!==null){let f=Math.round(o/this.touchMaxX*this.displayWidth),x=Math.round(a/this.touchMaxY*this.displayHeight),v=Math.round(h/this.touchMaxX*this.displayWidth),S=Math.round(u/this.touchMaxY*this.displayHeight),y=v-f,k=S-x,C=Math.abs(y),j=Math.abs(k),$=Math.hypot(C,j);if(this.activeInput&&x>this.displayHeight*.58&&S>this.displayHeight*.58){m>=20&&m<400&&$<45&&v>=this.displayWidth*.78&&S>=this.displayHeight*.84?this.flushActiveInput().then(()=>{this.handleKeyPress("Enter")}).catch(()=>{}):this.scheduleInputDebounce(),r=null,i=null,o=null,a=null;return}if(m>=20&&m<400&&$<45)this.isRefreshingHierarchy?this.inFlightTapQueue.push({x:v,y:S,timestamp:Date.now()}):this.handleTapAt(v,S);else if(m>=450&&$<45)this.handleLongPressAt(v,S);else if(j>=100&&j>1.4*C){let J=k<0?"down":"up";this.handleScroll(J)}else if(C>=100&&C>1.4*j){let J=y<0?"left":"right";this.handleSwipe(J)}}r=null,i=null,o=null,a=null}else p.includes("KEY_ENTER")&&p.includes("DOWN")?this.handleKeyPress("Enter"):p.includes("KEY_BACK")&&p.includes("DOWN")&&this.handleKeyPress("Back")}),n.on("close",p=>{this.isRecording&&(console.warn(Y.yellow(`
838
- \u26A0\uFE0F [EventStream] ADB event listener stream closed (code ${p}). Auto-reconnecting in 800ms...`)),setTimeout(()=>{this.isRecording&&this.startEventStream(t)},800))}),n.on("error",p=>{this.isRecording&&console.warn(Y.yellow(`
839
- \u26A0\uFE0F [EventStream] ADB stream error: ${p.message}.`))})}cleanLabel(t){return t?t.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(t){let e=Date.now();if(this.lastScrollDirection===t&&e-this.lastScrollTimestamp<850)return;this.lastScrollTimestamp=e,this.lastScrollDirection=t,this.activeInput&&this.flushActiveInput().catch(()=>{});let s=`Scroll ${t}`;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(t){let e=Date.now();if(e-this.lastScrollTimestamp<600)return;this.lastScrollTimestamp=e,this.activeInput&&this.flushActiveInput().catch(()=>{});let s=`Swipe ${t}`;this.appendStep(s,Y.blue(` \u{1F449} Recorded Swipe [Step #${this.getCurrentStepNumber()}]: ${Y.bold(s)}`)),this.triggerAsyncHierarchyRefresh(500)}handleLongPressAt(t,e){let s=this.lastHierarchyFlat.filter(i=>t>=i.bounds.x1&&t<=i.bounds.x2&&e>=i.bounds.y1&&e<=i.bounds.y2);if(s.length===0)return;s.sort((i,o)=>{let a=(i.bounds.x2-i.bounds.x1)*(i.bounds.y2-i.bounds.y1),l=(o.bounds.x2-o.bounds.x1)*(o.bounds.y2-o.bounds.y1);return a-l});let n=s[0],r=this.resolveActionLabel(n,s,this.lastHierarchyFlat);if(r){let i=`Long press '${r}'`;this.appendStep(i,Y.cyan(` \u23F1\uFE0F Recorded Long Press [Step #${this.getCurrentStepNumber()}]: ${Y.bold(i)}`)),this.triggerAsyncHierarchyRefresh()}}handleTapAt(t,e){let s=Date.now(),n=this.lastHierarchyFlat.filter(p=>t>=p.bounds.x1&&t<=p.bounds.x2&&e>=p.bounds.y1&&e<=p.bounds.y2),i=this.lastHierarchyFlat.filter(p=>{let h=p.bounds.height;return h>0&&h<320&&p.bounds.width>0&&(p.clickable||p.contentDesc||p.text)}).filter(p=>e>=p.bounds.y1-30&&e<=p.bounds.y2+40);if(i.length>=2){let p=i.find(h=>t>=h.bounds.x1-20&&t<=h.bounds.x2+20);p&&(n=[this.lastHierarchyFlat.find(u=>(this.cleanLabel(u.text).length>0||this.cleanLabel(u.contentDesc).length>0)&&Math.abs(u.bounds.centerX-p.bounds.centerX)<p.bounds.width*.48&&Math.abs(u.bounds.centerY-p.bounds.centerY)<p.bounds.height*.48&&!this.isIconGlyph(this.cleanLabel(u.text)))||p])}if(e>this.displayHeight*.82){let h=this.lastHierarchyFlat.filter(u=>{let m=u.bounds.y1>=this.displayHeight*.78||u.bounds.y2>=this.displayHeight*.82,f=u.bounds.width>0&&u.bounds.width<=this.displayWidth*.52;return m&&f}).find(u=>t>=u.bounds.x1-20&&t<=u.bounds.x2+20);if(h){let u=this.lastHierarchyFlat.find(m=>(this.cleanLabel(m.text).length>0||this.cleanLabel(m.contentDesc).length>0)&&t>=m.bounds.x1-25&&t<=m.bounds.x2+25&&m.bounds.y1>=this.displayHeight*.78&&!this.isIconGlyph(this.cleanLabel(m.text)));u?n=[u]:(this.cleanLabel(h.text)||this.cleanLabel(h.contentDesc))&&(n=[h])}}if(n.length===0){let p=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 p){let f=Math.hypot(m.bounds.centerX-t,m.bounds.centerY-e);f<u&&(u=f,h=m)}if(h)n=[h];else{this.triggerAsyncHierarchyRefresh();return}}n.sort((p,h)=>{let u=(p.bounds.x2-p.bounds.x1)*(p.bounds.y2-p.bounds.y1),m=(h.bounds.x2-h.bounds.x1)*(h.bounds.y2-h.bounds.y1);return u-m});let o=n[0],a=n.find(p=>p.className.includes("EditText"))||(o.className.includes("EditText")?o:null);if(a){this.activeInput&&this.activeInput.node!==a&&this.flushActiveInput().catch(()=>{});let p=this.resolveFieldLabel(a,this.lastHierarchyFlat);this.activeInput={node:a,label:p,startText:a.text||""},this.triggerAsyncHierarchyRefresh();return}this.activeInput&&this.flushActiveInput().catch(()=>{});let l=this.resolveActionLabel(o,n,this.lastHierarchyFlat);if(!l||l.length===0){this.triggerAsyncHierarchyRefresh();return}if(!o.clickable&&!n.some(p=>p.clickable||p.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 d;if(o.checkable||o.className.includes("Switch")||o.className.includes("CheckBox")){let p=o.checked!==void 0?!o.checked:!0;d=`Toggle '${l}' to ${p?"on":"off"}`}else{d=`Tap '${l}'`;let p=this.lastHierarchyFlat.filter(u=>this.cleanLabel(u.text)===l||this.cleanLabel(u.contentDesc)===l),h=[];for(let u of p)h.some(f=>Math.hypot(f.bounds.centerX-u.bounds.centerX,f.bounds.centerY-u.bounds.centerY)<40)||h.push(u);if(h.length>1){let m=this.lastHierarchyFlat.filter(f=>f!==o&&Math.abs(f.bounds.centerY-o.bounds.centerY)<140&&Math.abs(f.bounds.centerX-o.bounds.centerX)<550).find(f=>{let x=this.cleanLabel(f.text)||this.cleanLabel(f.contentDesc);return x.length>2&&x!==l&&!/^\d+$/.test(x)&&!this.isIconGlyph(x)});if(m){let f=this.cleanLabel(m.text)||this.cleanLabel(m.contentDesc);f&&f!==l&&(d=`Tap '${l}' next to '${f}'`)}}}this.appendStep(d,Y.cyan(` \u{1F446} Recorded Tap [Step #${this.getCurrentStepNumber()}]: ${Y.bold(d)}`)),this.triggerAsyncHierarchyRefresh()}handleKeyPress(t){if(this.activeInput&&this.flushActiveInput().catch(()=>{}),t==="Enter"){let e="Press 'Enter'";this.appendStep(e,Y.magenta(` \u21B5 Recorded Key [Step #${this.getCurrentStepNumber()}]: ${Y.bold(e)}`))}else if(t==="Back"){let e="Tap 'Back'";this.appendStep(e,Y.magenta(` \u25C0 Recorded Key [Step #${this.getCurrentStepNumber()}]: ${Y.bold(e)}`))}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 t=this.activeInput;this.activeInput=null,await this.recordInputText(t)}async syncActiveInputText(){this.activeInput&&await this.recordInputText(this.activeInput)}async recordInputText(t){try{let e=await this.driver.getUiHierarchy(this.targetSerial).catch(()=>null);if(!e)return;let{flat:s}=ve(e);this.lastHierarchyFlat=s;let n=s.find(r=>r.className.includes("EditText")&&(r.focused||r.selected))||s.find(r=>r.className.includes("EditText")&&this.resolveFieldLabel(r,s).toLowerCase()===t.label.toLowerCase())||s.find(r=>r.className.includes("EditText")&&Math.abs(r.bounds.centerX-t.node.bounds.centerX)<80)||s.find(r=>r.className.includes("EditText"));if(n&&n.text&&n.text.trim().length>0){let r=n.text.trim(),i=/^[•*•\u2022\u25cf]+$/.test(r);if(!(r===t.startText)&&!i){if(t.startText===""&&this.isPlaceholderText(r))return;let a=t.label||this.resolveFieldLabel(n,s),l=`Type '${r}' into '${a}'`,d=this.recordedSteps.length-1,p=d>=0?this.recordedSteps[d]:"";p.startsWith("Type ")&&p.endsWith(`into '${a}'`)?p!==l&&(this.recordedSteps[d]=l,this.flushToDisk(),console.log(Y.gray(` \u270D\uFE0F Updated Input [Step #${d+1+this.existingSteps.length}]: ${Y.bold(l)}`))):this.appendStep(l,Y.green(` \u270D\uFE0F Recorded Input [Step #${this.getCurrentStepNumber()}]: ${Y.bold(l)}`))}else if(i&&(n.password||t.label.toLowerCase().includes("password"))){let a=t.label||"Password",l=`Type '<password>' into '${a}'`,d=this.recordedSteps.length-1,p=d>=0?this.recordedSteps[d]:"";(!p.startsWith("Type ")||!p.endsWith(`into '${a}'`))&&this.appendStep(l,Y.green(` \u270D\uFE0F Recorded Password Input [Step #${this.getCurrentStepNumber()}]: ${Y.bold(l)}`))}}}catch{}}startLogcatScreenWatcher(t){try{this.logcatEventProcess=this.driver.streamLogcatEvents(t,e=>{if(this.isRecording&&(e.includes("wm_on_resume_called")||e.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(t){try{this.logcatCrashProcess=this.driver.streamLogcatCrashes(t,e=>{if(this.isRecording&&(e.includes("FATAL EXCEPTION")||e.includes("ANR in"))){if(this.targetPackage&&!e.includes(this.targetPackage))return;this.crashDetected=!0,console.log(Y.red.bold(`
840
- \u26A0\uFE0F [Crash Alert] Fatal exception / ANR detected in ${this.targetPackage||"app"}!`))}})}catch{}}captureSessionScreenshot(t){if(!this.screenshotDir)return;let e=++this.screenshotCounter,s=t.toLowerCase().replace(/[^a-z0-9_]/g,"_").slice(0,30)||"screen",n=`screen_${String(e).padStart(2,"0")}_${s}.png`,r=xe.join(this.screenshotDir,n);(async()=>{try{let i=await this.driver.captureScreenshot(this.targetSerial);if(i&&i.length>0){await ke.promises.writeFile(r,i);let o=this.sessionScreenSections[this.sessionScreenSections.length-1];o&&!o.screenshotFile&&(o.screenshotFile=n)}}catch{}})()}triggerAsyncHierarchyRefresh(t=I.recorder.asyncRefreshDelayMs){this.isRefreshingHierarchy||(this.isRefreshingHierarchy=!0,setTimeout(()=>{this.doHierarchyRefresh().catch(()=>{this.isRefreshingHierarchy=!1})},t))}async doHierarchyRefresh(){try{let t=await this.driver.getUiHierarchy(this.targetSerial),{flat:e}=ve(t);this.lastHierarchyFlat=e,this.hierarchyStale=!1,this.hierarchyCacheTime=Date.now(),this.detectAndLogScreenHeader(e),this.detectAndLogTransientAlerts(e)}catch{}finally{for(this.isRefreshingHierarchy=!1;this.inFlightTapQueue.length>0;){let t=this.inFlightTapQueue.shift();if(!t)break;Date.now()-t.timestamp<I.recorder.staleTapTimeoutMs?this.handleTapAt(t.x,t.y):console.warn(Y.yellow(`[Recorder] Dropped stale tap at (${t.x}, ${t.y}) \u2014 screen refresh took longer than ${I.recorder.staleTapTimeoutMs}ms`))}}}detectAndLogTransientAlerts(t){let e=t.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(e){let s=this.cleanLabel(e.text)||this.cleanLabel(e.contentDesc);if(s&&s.length>3&&s.length<80){let n=`Verify '${s}' is visible`;this.recordedSteps.slice(-3).includes(n)||this.appendStep(n,Y.green(` \u2714 Recorded Alert [Step #${this.getCurrentStepNumber()}]: ${Y.bold(n)}`))}}}async refreshHierarchySync(){try{let t=await this.driver.getUiHierarchy(this.targetSerial),{flat:e}=ve(t);this.lastHierarchyFlat=e,this.hierarchyStale=!1,this.hierarchyCacheTime=Date.now(),this.detectAndLogScreenHeader(e)}catch{}}isIconGlyph(t){if(!t||t.trim().length===0)return!0;let e=t.trim();return!!(/[\uE000-\uF8FF\uFFF0-\uFFFF]/.test(e)||e.length===1&&e.charCodeAt(0)>255&&!new RegExp("\\p{Extended_Pictographic}","u").test(e))}cleanDialogOrCompoundLabel(t){if(!t)return"";let e=t.trim();if(e.includes(",")||e.includes(`
841
- `)){let s=e.split(/[\n,]+/).map(n=>this.cleanLabel(n)).filter(Boolean);if(s.length>1){let n=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(n){let i=s.find(o=>o!==n&&o.length<=30&&!o.includes("?"));if(i)return i}let r=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(r.length>0)return r[0]}}return e}isCardMetadataSubtitle(t){if(!t||t.trim().length===0)return!1;let e=t.trim();return!!(/\+?\d{1,3}[\s-]?\d{4,}[\s-]?\d{4,}/.test(e)||/^(STD|ID|EMP|REF|ORD|USER|ACC|BATCH)[\-_:\s]?\d+/i.test(e)||/[•|·]/.test(e)&&/\d/.test(e)||/^\d+$/.test(e)||/^\d{1,2}:\d{2}/.test(e)||/^(teacher|roll\s*no|std\s*id|class|sec)[\s:]+/i.test(e))}findPrimaryCardTitle(t,e,s){if(t.bounds.y1>=this.displayHeight*.8||t.bounds.centerY>=this.displayHeight*.82||t.clickable&&(t.className.includes("Button")||t.className.includes("Check")))return null;let n=this.cleanLabel(t.text),r=this.isCardMetadataSubtitle(n),i=s.find(l=>l!==t&&l.clickable&&l.bounds.height>60&&l.bounds.height<600),o;i?o=e.filter(l=>l.className.includes("Text")&&l.bounds.x1>=i.bounds.x1-10&&l.bounds.x2<=i.bounds.x2+10&&l.bounds.y1>=i.bounds.y1-10&&l.bounds.y2<=i.bounds.y2+10):o=e.filter(l=>l!==t&&l.className.includes("Text")&&l.bounds.centerY<t.bounds.centerY&&t.bounds.centerY-l.bounds.centerY<160&&Math.abs(l.bounds.centerX-t.bounds.centerX)<500);let a=o.filter(l=>{let d=this.cleanLabel(l.text);return d.length>2&&!this.isCardMetadataSubtitle(d)&&!this.isIconGlyph(d)&&!/^\d+$/.test(d)&&!/^(active|inactive|present|absent|new|pending)$/i.test(d)}).sort((l,d)=>l.bounds.y1-d.bounds.y1);if(a.length>0){let l=this.cleanLabel(a[0].text);if(r||i&&l!==n&&!t.clickable)return l}return null}resolveActionLabel(t,e,s){if(t.bounds.y1>=this.displayHeight*.8||t.bounds.centerY>=this.displayHeight*.82){let l=this.cleanLabel(t.contentDesc)||this.cleanLabel(t.text);if(l&&l.length>0&&!this.isIconGlyph(l))return this.cleanDialogOrCompoundLabel(l);for(let d of e){let p=this.cleanLabel(d.contentDesc)||this.cleanLabel(d.text);if(p&&p.length>0&&!this.isIconGlyph(p))return this.cleanDialogOrCompoundLabel(p)}}let n=this.findPrimaryCardTitle(t,s,e);if(n)return this.cleanDialogOrCompoundLabel(n);let r=this.cleanLabel(t.text);if(r.length>0&&r.length<90){let l=r.split(/[\n\r]+/).map(p=>this.cleanLabel(p)).filter(Boolean),d=l.length>1?l.find(p=>p.length>2&&!/^\d+$/.test(p))||l[0]:r;return this.cleanDialogOrCompoundLabel(d)}let i=this.cleanLabel(t.contentDesc);if(i.length>0&&i.length<90)return this.cleanDialogOrCompoundLabel(i);let o=e.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 e){let d=this.cleanLabel(l.text);if(d.length>0&&d.length<90){let h=d.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]:d;return this.cleanDialogOrCompoundLabel(u)}let p=this.cleanLabel(l.contentDesc);if(p.length>0&&p.length<90)return this.cleanDialogOrCompoundLabel(p)}let a=s.find(l=>l!==t&&(this.cleanLabel(l.text).length>0||this.cleanLabel(l.contentDesc).length>0)&&Math.abs(l.bounds.centerX-t.bounds.centerX)<80&&Math.abs(l.bounds.centerY-t.bounds.centerY)<100);if(a){let l=this.cleanLabel(a.text)||this.cleanLabel(a.contentDesc);if(l.length>0)return this.cleanDialogOrCompoundLabel(l)}if(t.resourceId){let l=t.resourceId.split("/"),d=l[l.length-1];if(d&&!/^\d+$/.test(d)){let p=d.replace(/_/g," ").replace(/btn|button|icon|view|action/gi,"").trim();if(p.length>1)return p.charAt(0).toUpperCase()+p.slice(1)}}return""}getCurrentStepNumber(){return this.existingSteps.length+this.recordedSteps.length+1}detectAndLogScreenHeader(t){let e=t.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 n=s.text.trim();return!(this.isCardMetadataSubtitle(n)||/\b(JIO|Airtel|Vodafone|No service|Emergency|VoLTE|4G|5G)\b/i.test(n)||/^\d+%?$|^\d+$/.test(n)||/^step\s*\d+\s*(of|\/)\s*\d+/i.test(n))}).sort((s,n)=>s.bounds.y1-n.bounds.y1);if(e.length>0){let s=e[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 n=`Verify '${s}' is visible`;this.recordedSteps[this.recordedSteps.length-1]!==n&&!this.recordedSteps.slice(-3).includes(n)&&this.appendStep(n,Y.green(` \u2714 Recorded Header [Step #${this.getCurrentStepNumber()}]: ${Y.bold(n)}`))}}}resolveFieldLabel(t,e){if(t.contentDesc&&t.contentDesc.trim().length>0)return t.contentDesc.trim();if(t.password)return"Password";let s=e.filter(n=>n.className.includes("TextView")&&n.text&&n.text.trim().length>1&&n.bounds.centerY<t.bounds.centerY&&t.bounds.centerY-n.bounds.centerY<250&&Math.abs(n.bounds.centerX-t.bounds.centerX)<450);if(s.length>0){s.sort((r,i)=>i.bounds.centerY-r.bounds.centerY);let n=s[0].text.trim().replace(/[*:]/g,"").trim();if(n.length>0)return n}if(t.resourceId){let n=t.resourceId.split("/"),r=n[n.length-1];if(r&&!/^\d+$/.test(r)){let i=r.replace(/_/g," ").replace(/edit|text|input|field|et|txt/gi,"").trim();return i.length>1?i.charAt(0).toUpperCase()+i.slice(1):r}}if(t.text&&t.text.trim().length>0&&t.text.trim().length<40){let n=t.text.trim();if(n.toLowerCase().startsWith("search"))return n.length<=25?n:"Search";if(!this.isPlaceholderText(n))return n}return"Input Field"}isPlaceholderText(t){let e=t.trim().toLowerCase();return e.startsWith("search")||e.startsWith("e.g.")||e.startsWith("enter ")||e.startsWith("type ")||e.includes("optional")}saveSpec(t){let e=xe.resolve(t);if(xe.basename(e).startsWith("app_flow")&&this.targetPackage&&!this.isAppendMode){let a=this.extractBrandSlug(this.targetPackage);if(a!=="app"){let l=`${a}_flow`,d=xe.resolve(`specs/${l}.txt`),p=1;for(;ke.existsSync(d);)d=xe.resolve(`specs/${l}_${p}.txt`),p++;e=d}}let n=xe.dirname(e);ke.existsSync(n)||ke.mkdirSync(n,{recursive:!0});let r=[];if(this.isAppendMode&&this.existingSteps.length>0&&ke.existsSync(e)){let a=ke.readFileSync(e,"utf-8").trim();r.push(a),r.push(""),r.push(`# --- Appended Session (${new Date().toISOString().replace("T"," ").slice(0,19)}) ---`),this.recordedSteps.forEach((l,d)=>{r.push(`${this.existingSteps.length+d+1}. ${l}`)})}else{r.push("# ==========================================================",`# Autonomous Recorded Test Spec: ${xe.basename(e)}`,...this.targetPackage?[`# Package: ${this.targetPackage}`]:[],`# Generated: ${new Date().toISOString()}`,`# PromptTest v${vn}`,"# ==========================================================","");let a=0;this.recordedSteps.forEach((l,d)=>{if(a<this.sessionScreenSections.length&&d===this.sessionScreenSections[a].stepStart){let p=this.sessionScreenSections[a];r.push(""),r.push(`# Screen: ${p.name}`),a++}r.push(`${this.existingSteps.length+d+1}. ${l}`)})}r.push("");let i=r.join(`
842
- `),o=`${e}.tmp.${Date.now()}`;try{ke.writeFileSync(o,i,"utf-8"),ke.renameSync(o,e)}catch{try{ke.unlinkSync(o)}catch{}ke.writeFileSync(e,i,"utf-8")}}saveReport(t){let e=Math.round((Date.now()-this.sessionStartTime)/1e3),s=Math.floor(e/60),n=e%60,r=`${String(s).padStart(2,"0")}:${String(n).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** | ${r} |`,`| **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((a,l)=>`${l+1}. **${a}**`).join(`
843
- `):"*Single screen flow*","","---","","## Recorded Interactions",""];if(this.sessionScreenSections.length>0)for(let a=0;a<this.sessionScreenSections.length;a++){let l=this.sessionScreenSections[a],d=this.sessionScreenSections[a+1],p=d?d.stepStart:this.recordedSteps.length,h=this.recordedSteps.slice(l.stepStart,p);o.push(`### Screen: ${l.name}`),o.push(""),l.screenshotFile&&(o.push(`![${l.name}](screenshots/${xe.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((a,l)=>{o.push(`${l+1}. \`${a}\``)}),o.push("");try{let a=xe.dirname(t);ke.existsSync(a)||ke.mkdirSync(a,{recursive:!0}),ke.writeFileSync(t,o.join(`
844
- `),"utf-8")}catch{}}printExitSummary(t){let e=Math.round((Date.now()-this.sessionStartTime)/1e3),s=Math.floor(e/60),n=e%60,r=`${String(s).padStart(2,"0")}:${String(n).padStart(2,"0")}`,i=this.existingSteps.length+this.recordedSteps.length;console.log(`
845
- ${t}`),console.log(Y.bold.green(" \u{1F4BE} Recording Session Saved Successfully!")),console.log(t),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(r)}`),console.log(` ${Y.gray("Crash Signals ")} ${this.crashDetected?Y.red.bold("\u26A0\uFE0F Detected"):Y.green("\u2714 0 detected")}`),console.log(t),console.log(` ${Y.gray("\u{1F4C4} Spec ")} ${Y.cyan(this.resolvedOutputPath)}`);let o=xe.basename(this.resolvedOutputPath,".txt");console.log(` ${Y.gray("\u{1F4CA} Report ")} ${Y.cyan(xe.resolve("output",`${o}_report.md`))}`),console.log(`${t}
846
- `)}},xn=new St});var xi={};Tt(xi,{formatDoctorReport:()=>Tn,runDoctor:()=>as});import vs from"fs";import vi from"path";import wt from"chalk";async function as(c=re,t={}){let e=[],s=process.versions.node,r=parseInt(s.split(".")[0],10)>=18;e.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 i=!1,o="ADB binary not found in PATH",a='Install Android SDK platform-tools and ensure "adb" is in your system PATH.';try{let{stdout:S}=await c.execAdb(["version"],void 0,5e3),y=S.match(/Android Debug Bridge version ([\d.]+)/i);y?(i=!0,o=`Android Debug Bridge version ${y[1]}`,a=void 0):(i=!0,o=S.split(`
847
- `)[0].trim(),a=void 0)}catch(S){o=`Failed to execute adb: ${S.message}`}e.push({name:"Android Debug Bridge (ADB)",passed:i,message:o,hint:a});let l=!1,d="No connected Android devices or emulators found",p="Connect an Android device via USB/Wi-Fi or launch an emulator with USB Debugging enabled.";try{let S=await c.getConnectedDevices(),y=S.filter(j=>j.state==="device"),k=S.filter(j=>j.state==="unauthorized"),C=S.filter(j=>j.state==="offline");if(y.length>0){l=!0;let j=y.map($=>`${$.serial} (${$.model||"Unknown"})`).join(", ");d=`Found ${y.length} authorized device(s): ${j}`,p=void 0}else k.length>0?(d=`Device(s) found but unauthorized: ${k.map(j=>j.serial).join(", ")}`,p='Check the device screen and accept the "Allow USB debugging" RSA prompt.'):C.length>0&&(d=`Device(s) found in offline state: ${C.map(j=>j.serial).join(", ")}`,p='Try running "adb reconnect" or reconnect the USB cable.')}catch(S){d=`Failed to list devices: ${S.message}`}if(e.push({name:"Connected Android Device",passed:l,message:d,hint:p}),t.packageName){let S=t.packageName,y=!1,k=`Package "${S}" not found on target device`,C='Install the APK using "adb install <path.apk>" or check the package identifier.';if(l)try{let{stdout:j}=await c.execAdb(["shell","pm","list","packages",S],t.serial,5e3);j.includes(`package:${S}`)&&(y=!0,k=`Package "${S}" is installed on target device`,C=void 0)}catch(j){k=`Failed to query package status: ${j.message}`}else k="Skipped package verification (no authorized device available)",C=`Connect a device to verify package "${S}".`;e.push({name:`Target Package [${S}]`,passed:y,message:k,hint:C})}let h=!0,u="All required workspace directories are accessible",m,f=["output","memory",".prompttest"];for(let S of f)try{let y=vi.resolve(process.cwd(),S);vs.existsSync(y)||vs.mkdirSync(y,{recursive:!0});let k=vi.join(y,".doctor_perm_test");vs.writeFileSync(k,"ok","utf-8"),vs.unlinkSync(k)}catch(y){h=!1,u=`Filesystem permission error for "./${S}": ${y.message}`,m="Ensure the current user has write permissions to the workspace directory.";break}e.push({name:"Workspace Storage & Permissions",passed:h,message:u,hint:m});let x=e.filter(S=>S.passed).length,v=e.filter(S=>!S.passed).length;return{timestamp:new Date().toISOString(),allPassed:v===0,checks:e,summary:{passed:x,failed:v,warnings:0}}}function Tn(c){let t=["",wt.bold.cyan("\u{1FA7A} PromptTest Doctor \u2014 Environment Health Diagnostic"),wt.gray(`Scanned at ${c.timestamp}
848
- `)];for(let e of c.checks){let s=e.passed?wt.green("\u2714"):wt.red("\u2716"),n=wt.bold(e.name);t.push(` ${s} ${n}: ${e.message}`),e.hint&&!e.passed&&t.push(wt.yellow(` \u{1F4A1} Fix: ${e.hint}`))}return t.push(""),c.allPassed?t.push(wt.bold.green("\u2728 All diagnostic checks passed! Your environment is ready to test.")):t.push(wt.bold.red(`\u26A0\uFE0F ${c.summary.failed} diagnostic check(s) failed. Please review the remediation hints above.`)),t.push(""),t.join(`
849
- `)}var cs=de(()=>{"use strict";Fe()});var Ti={};Tt(Ti,{startLiveServer:()=>En});import Sr from"http";import te from"fs";import Q from"path";import{fileURLToPath as wr}from"url";async function En(c={}){let t=c.port??4040,e=c.host||"127.0.0.1",s=c.outputDir?Q.resolve(c.outputDir):Q.resolve(process.cwd(),"output"),n=c.suiteName||"PromptTest Live Monitor",r=c.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:[]},i=new Set,o=setInterval(()=>{for(let w of i)try{w.write(`: heartbeat
850
-
851
- `)}catch{i.delete(w)}},15e3),a=c.driver||new rt,l=Q.dirname(wr(import.meta.url)),d=c.specsDir?Q.resolve(c.specsDir):Q.resolve(process.cwd(),"specs");if(!te.existsSync(d)){let w=Q.resolve(l,"../specs");te.existsSync(w)&&(d=w)}let p=null;function h(){return p||(p=new it(a)),p}let u=Q.join(s,"baselines"),m=new Ft(u),f=null,x=null,v=["strict","moderate","interactive","disabled"],S=null,y=0;function k(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 C(w){let b=w.match(/"([^"]+)"/);return b?b[1]:null}function j(w){let b=w.specLines.length>0?w.specLines:["Launch the app"],_=["# 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&&_.push(`# Device: ${w.deviceId}`),[..._,"",...b,""].join(`
852
- `)}function $(w,b,_){w.status=b,w.finishedAt=new Date().toISOString(),_&&(w.error=_),V("explore",{...k(w),event:{type:b==="failed"?"error":"done",description:_||b}})}function J(w){let b=S;if(b){if(typeof w.totalSteps=="number"&&(b.stepsTaken=Math.max(b.stepsTaken,w.totalSteps)),typeof w.discoveredScreens=="number"&&(b.screensDiscovered=Math.max(b.screensDiscovered,w.discoveredScreens)),w.type==="action"){let _=C(w.description);_&&b.elementLabels.add(_),w.description&&b.specLines[b.specLines.length-1]!==w.description&&b.specLines.push(w.description)}if(w.screenHash){let _=w.screenTitle||w.description||w.screenHash.slice(0,8),P=b.screens.find(ce=>ce.hash===w.screenHash);P?(P.visits++,typeof w.elementCount=="number"&&(P.elements=w.elementCount),P.label===P.hash.slice(0,8)&&w.screenTitle&&(P.label=w.screenTitle,P.title=w.screenTitle)):b.screens.push({id:w.screenHash,hash:w.screenHash,depth:w.depth??b.screens.length,title:_,label:_,visits:1,elements:w.elementCount??0}),b.screensDiscovered=Math.max(b.screensDiscovered,b.screens.length)}if(w.type==="crash"){let _=new Date().toISOString(),P=w.screenHash,ce=P?b.screens.find(ne=>ne.hash===P):void 0;b.crashes.push({id:P||`crash-${b.crashes.length+1}`,type:w.description.toLowerCase().includes("anr")?"anr":"crash",message:w.description,screen:ce?.label||P?.slice(0,8),timestamp:_,description:w.description,screenHash:P})}w.type==="safety_block"&&b.blocked.push(w.description),b.elementsMapped=b.elementLabels.size,b.events.push(w),b.events.length>300&&b.events.shift(),V("explore",{...k(b),event:w})}}function O(w){let b=v.includes(w.safetyMode||"")?w.safetyMode:"strict";y++;let _={id:`explore-${Date.now()}-${y}`,packageName:w.packageName,deviceId:w.deviceId,safetyMode:b,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}};S=_;let P=new Mt(a);return V("explore",{...k(_),event:{type:"state_change",description:`Auto-Pilot launched on ${_.packageName} (${_.safetyMode} mode)`}}),P.explore({packageName:_.packageName,serial:_.deviceId,strategy:"dfs",safetyMode:_.safetyMode,maxDepth:_.maxDepth,maxScreens:_.maxScreens,stepBudget:_.maxSteps,screenshots:"state-change",onStep:J,abortSignal:_.abort}).then(()=>{$(_,_.abort.aborted?"stopped":"completed")}).catch(ce=>{$(_,_.abort.aborted?"stopped":"failed",ce instanceof Error?ce.message:String(ce))}),_}function V(w,b){let _=`event: ${w}
853
- data: ${JSON.stringify(b)}
854
-
855
- `;for(let P of i)try{P.write(_)}catch{i.delete(P)}}function W(w){return new Promise((b,_)=>{let P="";w.on("data",ce=>{P+=ce}),w.on("end",()=>{try{b(P?JSON.parse(P):{})}catch{_(new Error("Invalid JSON payload"))}}),w.on("error",_)})}let se=Sr.createServer(async(w,b)=>{if(b.setHeader("Access-Control-Allow-Origin","*"),b.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),b.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),w.method==="OPTIONS"){b.writeHead(204),b.end();return}let _=new URL(w.url||"/",`http://${e}:${t}`),P=decodeURIComponent(_.pathname);if(P==="/api/devices"){try{let g=await a.getConnectedDevices();b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,devices:g}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/screen-state"){try{let g=_.searchParams.get("deviceId")||void 0,D=_.searchParams.get("hierarchy")!=="false",[N,M,T]=await Promise.all([a.captureScreenshot(g),D?a.getUiHierarchy(g):Promise.resolve(null),a.getForegroundPackage(g)]),A=[];if(M){let{flat:E}=ve(M);A=E.map(L=>({index:L.index,text:L.text,resourceId:L.resourceId,className:L.className,contentDesc:L.contentDesc,clickable:L.clickable,scrollable:L.scrollable,bounds:L.bounds}))}b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,deviceId:g||a.getDefaultDevice()||"default",foregroundApp:T,screenshot:`data:image/png;base64,${N.toString("base64")}`,elements:D?A:void 0,elementCount:A.length}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/tap"&&w.method==="POST"){try{let g=await W(w);if(typeof g.x!="number"||typeof g.y!="number"){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing numeric coordinates: x and y"}));return}await a.tap(g.x,g.y,g.deviceId),b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,tapped:{x:g.x,y:g.y}}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/type"&&w.method==="POST"){try{let g=await W(w);if(typeof g.text!="string"){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing text string to type"}));return}await a.inputText(g.text,g.deviceId),b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,typed:g.text}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/key"&&w.method==="POST"){try{let g=await W(w);if(g.keyCode===void 0){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing keyCode parameter"}));return}await a.pressKey(g.keyCode,g.deviceId),b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,keyCode:g.keyCode}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/wake"&&w.method==="POST"){try{let g=await W(w);await a.wakeDevice(g.deviceId),b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/wifi/connect"&&w.method==="POST"){try{let g=await W(w),D=await a.connectWifi(g.target,g.port||5555,g.deviceId);b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:D.success,target:D.target,message:D.message}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/wifi/disconnect"&&w.method==="POST"){try{let g=await W(w),D=await a.disconnectWifi(g.target);b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,message:D}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/wifi/pair"&&w.method==="POST"){try{let g=await W(w);if(!g.target||!g.pairingCode){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing target or pairingCode"}));return}let D=await a.pairWifi(g.target,g.pairingCode);b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify(D))}catch(g){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/metrics"&&w.method==="GET"){try{let g=_.searchParams.get("deviceId")||void 0,D=await a.getDeviceMetrics(g);if(Object.keys(D).length===0)throw new Error(`No metrics available for device ${g||"default"}. Confirm the device is connected and authorized.`);b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,metrics:D}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/doctor"&&w.method==="GET"){try{let g=_.searchParams.get("deviceId")||void 0,D=_.searchParams.get("packageName")||void 0,N=await as(a,{serial:g,packageName:D});b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,report:N}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((P==="/api/explore/start"||P==="/api/explore")&&w.method==="POST"){try{let g=await W(w),D=(g.packageName||g.targetPackage||"").trim();if(!D){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing packageName (packageName or targetPackage)"}));return}if(S&&S.status==="running"){b.writeHead(409,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"An Auto-Pilot exploration session is already active",sessionId:S.id}));return}let N=O({packageName:D,deviceId:g.deviceId||g.serial,safetyMode:g.safetyMode||g.safety,maxDepth:g.maxDepth??g.depth,maxSteps:g.maxSteps??g.stepBudget,maxScreens:g.maxScreens});b.writeHead(202,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,sessionId:N.id,status:N.status,packageName:N.packageName}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((P==="/api/explore/status"||P==="/api/explore")&&w.method==="GET"){b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify(k(S)));return}if((P==="/api/explore/stop"||P==="/api/explore")&&(w.method==="POST"||w.method==="DELETE")){if(!S){b.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"No active Auto-Pilot session to stop"}));return}S.abort.aborted=!0,b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,sessionId:S.id,status:"stopping",packageName:S.packageName}));return}if(P==="/api/explore/export"&&w.method==="POST"){try{let g=await W(w);if(!S){b.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"No exploration session available to export"}));return}let D=(g.specName||`explored_${S.packageName}.txt`).trim(),N=Q.basename(D).replace(/[^a-zA-Z0-9._-]/g,"_"),M=N.endsWith(".txt")?N:`${N}.txt`,T=j(S);await te.promises.mkdir(d,{recursive:!0}),await te.promises.writeFile(Q.join(d,M),T,"utf8"),b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,specName:M,content:T,screensDiscovered:S.screensDiscovered,stepsTaken:S.stepsTaken}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/record/start"&&w.method==="POST"){try{let g=await W(w);if(f){b.writeHead(409,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"A recording session is already active"}));return}let D=g.deviceId||a.getDefaultDevice()||void 0,N=g.specName?Q.join(d,Q.basename(g.specName)):void 0;f=new St(a),x=D||"default",f.onStep=M=>{V("record-step",{step:M,deviceId:D})},await f.startSession(N,D,{append:g.append}),b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,message:"Recording session started",deviceId:D}))}catch(g){if(f)try{await f.stopSession()}catch{}f=null,x=null,b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/record/stop"&&w.method==="POST"){try{if(!f){b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,message:"No active recording session",steps:[]}));return}let g=await f.stopSession();f=null,x=null,b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,steps:g.steps,specPath:g.specPath,reportPath:g.reportPath}))}catch(g){f=null,x=null,b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/record/status"&&w.method==="GET"){b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,isRecording:f!==null,deviceId:x}));return}if(P==="/api/specs"&&w.method==="GET"){try{te.existsSync(d)||te.mkdirSync(d,{recursive:!0});let D=te.readdirSync(d).filter(N=>N.endsWith(".txt")||N.endsWith(".spec")).map(N=>{let M=Q.join(d,N),T=te.statSync(M);return{name:N,path:Q.relative(process.cwd(),M).replace(/\\/g,"/"),size:T.size,modified:T.mtime.toISOString()}});b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,specs:D}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((P.startsWith("/api/specs/")||P==="/api/spec")&&w.method==="GET"){try{let g="";P.startsWith("/api/specs/")?g=decodeURIComponent(P.slice(11)):g=_.searchParams.get("name")||"";let D=Q.basename(g.trim());if(!D){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing spec file name"}));return}let N=Q.join(d,D);if(!te.existsSync(N)){b.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:`Spec file not found: ${D}`}));return}let M=te.readFileSync(N,"utf-8");b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,name:D,content:M}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((P==="/api/specs"||P==="/api/spec")&&w.method==="POST"){try{let g=await W(w),D=(g.name||"").trim();if(!D){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing spec file name"}));return}!D.endsWith(".txt")&&!D.endsWith(".spec")&&(D+=".txt");let N=Q.basename(D);te.existsSync(d)||te.mkdirSync(d,{recursive:!0});let M=Q.join(d,N);te.writeFileSync(M,g.content??"","utf-8"),b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,name:N,path:M}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/action"&&w.method==="POST"){try{let g=await W(w);if(!g.step||typeof g.step!="string"){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing natural language step string"}));return}let D=Jt(g.step);if(D.length===0){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:`Could not parse step: "${g.step}"`}));return}let N=h(),M=D[0],T=g.deviceId||a.getDefaultDevice()||void 0,A=Date.now(),E=await N.executeStep(M,T),L=Date.now()-A;V("step",{stepNumber:r.steps.length+1,instruction:M.originalText,status:"PASS",durationMs:L}),b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,step:M.originalText,type:M.type,durationMs:L,result:E||null}))}catch(g){let D=g instanceof Error?g.message:String(g);V("step",{stepNumber:r.steps.length+1,instruction:"Step Execution",status:"FAIL",durationMs:0,error:D}),b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:D}))}return}if(P==="/api/run"&&w.method==="POST"){try{let g=await W(w),D="";if(g.specName){let A=Q.basename(g.specName);if(D=Q.join(d,A),!te.existsSync(D)){b.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:`Spec file not found: ${A}`}));return}}else if(g.spec&&typeof g.spec=="string")te.existsSync(s)||te.mkdirSync(s,{recursive:!0}),D=Q.join(s,"_studio_temp_spec.txt"),te.writeFileSync(D,g.spec,"utf-8");else{b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:'Either "spec" string or "specName" must be provided'}));return}let N=g.deviceId||a.getDefaultDevice()||void 0,M=h();r.steps=[],r.totalSteps=0,r.passedSteps=0,r.failedSteps=0,r.warnSteps=0,r.skippedSteps=0;let T=gt.onStep?.(A=>{V("step",{stepNumber:A.stepIndex,instruction:A.description,status:A.status,durationMs:A.durationMs||0,error:A.details?.error})});(async()=>{try{let A=await M.runSpec(D,{serial:N,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});r=A,V("complete",A)}catch(A){let E=A instanceof Error?A.message:String(A);r.errorsDetected.push(E),V("error",{error:E}),V("complete",r)}finally{T&&T()}})(),b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,message:"Execution started"}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/reports"&&w.method==="GET"){try{if(!te.existsSync(s)){b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,reports:[]}));return}let N=te.readdirSync(s).filter(M=>M.endsWith("-results.json")).map(M=>{let T=M.replace(/-results\.json$/,""),A=Q.join(s,M),E=`${T}-report.html`,L=te.existsSync(Q.join(s,E)),U=te.statSync(A);try{let z=JSON.parse(te.readFileSync(A,"utf-8")),K=z.totalSteps||z.steps?.length||0,q=z.passedSteps||0,ie=z.failedSteps||0,fe=K>0?Math.round(q/K*100):100;return{id:T,fileName:M,suiteName:z.packageName||T,packageName:z.packageName||"Unknown",deviceId:z.deviceId||"Unknown",startTime:z.startTime||U.mtime.toISOString(),endTime:z.endTime||U.mtime.toISOString(),durationSeconds:z.durationSeconds||0,totalSteps:K,passedSteps:q,failedSteps:ie,warnSteps:z.warnSteps||0,skippedSteps:z.skippedSteps||0,passRate:fe,errorsCount:z.errorsDetected?.length||0,htmlReportPath:L?`/${E}`:null,jsonReportPath:`/${M}`}}catch{return null}}).filter(Boolean).sort((M,T)=>new Date(T.startTime).getTime()-new Date(M.startTime).getTime());b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,reports:N}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((P.startsWith("/api/reports/")||P==="/api/report-detail")&&w.method==="GET"){try{let g="";P.startsWith("/api/reports/")?g=decodeURIComponent(P.slice(13)):g=_.searchParams.get("id")||"";let D=Q.basename(g.trim()),N=D.endsWith("-results.json")?D:`${D}-results.json`,M=Q.join(s,N);if(!te.existsSync(M)){b.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:`Report not found: ${D}`}));return}let T=JSON.parse(te.readFileSync(M,"utf-8"));b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,id:D,report:T}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/baselines"&&w.method==="GET"){try{te.existsSync(u)||te.mkdirSync(u,{recursive:!0});let g=_.searchParams.get("spec")||"",D=g.replace(/[^a-zA-Z0-9._-]/g,"_"),N=te.readdirSync(u),M=new Map;for(let A of N){if(D&&!A.startsWith(`${D}_step`))continue;let E=A.match(/_step(\d+)(_current|_diff)?\.png$/);if(!E)continue;let L=parseInt(E[1],10),U=E[2];M.has(L)||M.set(L,{});let z=M.get(L);U?U==="_current"?z.current=`/baselines/${A}`:U==="_diff"&&(z.diff=`/baselines/${A}`):z.baseline=`/baselines/${A}`}let T=await Promise.all(Array.from(M.entries()).map(async([A,E])=>{let L=await m.getMasks(g,A);return{stepIndex:A,...E,masks:L}}));T.sort((A,E)=>A.stepIndex-E.stepIndex),b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,spec:g,baselines:T,matchingModes:Us}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/baselines/compare"&&w.method==="POST"){try{let g=await W(w);if(!g.specName||typeof g.stepIndex!="number"){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing specName or stepIndex"}));return}let D;if(g.screenshot){let T=g.screenshot.replace(/^data:image\/\w+;base64,/,"");D=Buffer.from(T,"base64")}else{let T=g.deviceId||a.getDefaultDevice()||void 0;D=await a.captureScreenshot(T)}let N=await m.compare(g.specName,g.stepIndex,D,{threshold:g.threshold,mode:g.mode,maskRegions:g.maskRegions}),M=await m.getMasks(g.specName,g.stepIndex);b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,result:{...N,masks:M,baselineUrl:`/baselines/${Q.basename(N.baselineFile)}`,currentUrl:`/baselines/${Q.basename(N.currentFile)}`,diffUrl:N.diffImagePath?`/baselines/${Q.basename(N.diffImagePath)}`:null}}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/baselines/approve"&&w.method==="POST"){try{let g=await W(w);if(!g.specName||typeof g.stepIndex!="number"){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing specName or stepIndex"}));return}let D;if(g.screenshot){let M=g.screenshot.replace(/^data:image\/\w+;base64,/,"");D=Buffer.from(M,"base64")}else{let M=g.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),T=Q.join(u,`${M}_step${String(g.stepIndex).padStart(3,"0")}_current.png`);if(!te.existsSync(T)){let A=g.deviceId||a.getDefaultDevice()||void 0;try{D=await a.captureScreenshot(A)}catch{}}}let N=await m.approveBaseline(g.specName,g.stepIndex,D);b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,approved:!0,stepIndex:g.stepIndex,baselineUrl:`/baselines/${Q.basename(N)}`}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/baselines/clear"&&w.method==="POST"){try{let g=await W(w);if(!g.specName){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing specName"}));return}await m.clearBaselines(g.specName),b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,cleared:!0,specName:g.specName}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/baselines/delete-step"&&w.method==="POST"){try{let g=await W(w);if(!g.specName||typeof g.stepIndex!="number"){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing specName or stepIndex"}));return}await m.deleteStepBaseline(g.specName,g.stepIndex),b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,deleted:!0,specName:g.specName,stepIndex:g.stepIndex}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/baselines/upload"&&w.method==="POST"){try{let g=await W(w);if(!g.specName||typeof g.stepIndex!="number"||!g.image){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing specName, stepIndex, or image data"}));return}let D=g.image.replace(/^data:image\/\w+;base64,/,""),N=Buffer.from(D,"base64"),M=await m.saveBaseline(g.specName,g.stepIndex,N),T=g.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),A=Q.join(u,`${T}_step${String(g.stepIndex).padStart(3,"0")}_diff.png`);if(te.existsSync(A))try{await te.promises.unlink(A)}catch{}let E=Q.join(u,`${T}_step${String(g.stepIndex).padStart(3,"0")}_current.png`),L,U;if(te.existsSync(E)){let K=await te.promises.readFile(E),q=await m.compare(g.specName,g.stepIndex,K);L=`/baselines/${Q.basename(E)}`,U=q.diffImagePath?`/baselines/${Q.basename(q.diffImagePath)}`:void 0}else if(g.deviceId||a.getDefaultDevice()){let K=g.deviceId||a.getDefaultDevice()||void 0;try{let q=await a.captureScreenshot(K),ie=await m.compare(g.specName,g.stepIndex,q);L=`/baselines/${Q.basename(E)}`,U=ie.diffImagePath?`/baselines/${Q.basename(ie.diffImagePath)}`:void 0}catch{}}let z=await m.getMasks(g.specName,g.stepIndex);b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,uploaded:!0,stepIndex:g.stepIndex,baselineUrl:`/baselines/${Q.basename(M)}`,currentUrl:L,diffUrl:U,masks:z}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/baselines/masks"&&w.method==="GET"){try{let g=_.searchParams.get("specName")||_.searchParams.get("spec")||"",D=parseInt(_.searchParams.get("stepIndex")||"",10);if(!g||isNaN(D)){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing specName or valid stepIndex"}));return}let N=await m.getMasks(g,D);b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,masks:N}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/api/baselines/masks"&&w.method==="POST"){try{let g=await W(w);if(!g.specName||typeof g.stepIndex!="number"||!Array.isArray(g.masks)){b.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:"Missing specName, stepIndex, or masks array"}));return}await m.saveMasks(g.specName,g.stepIndex,g.masks);let D=g.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),N=Q.join(u,`${D}_step${String(g.stepIndex).padStart(3,"0")}_current.png`),M,T,A=0,E=0,L=!0;if(te.existsSync(N)){let U=await te.promises.readFile(N),z=await m.compare(g.specName,g.stepIndex,U,{mode:g.mode,threshold:g.threshold,maskRegions:g.masks});M=`/baselines/${Q.basename(N)}`,T=z.diffImagePath?`/baselines/${Q.basename(z.diffImagePath)}`:void 0,A=z.diffRatio,E=z.diffPixels,L=z.passed}b.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!0,masks:g.masks,currentUrl:M,diffUrl:T,diffRatio:A,diffPixels:E,passed:L}))}catch(g){b.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),b.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(P==="/events"){b.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive","Access-Control-Allow-Origin":"*"}),b.write(`event: connected
861
+ {"bestMatch": "Exact Text of Best Element", "confidence": 0.85, "reason": "Reason for match"}`,r=await this.callLlm(i);if(!r)return this.heuristicFallback.findSuggestions(t,e,s);let o=r.match(/\{[\s\S]*\}/);if(!o)return this.heuristicFallback.findSuggestions(t,e,s);let l=JSON.parse(o[0]);if(l.bestMatch){let c=e.find(d=>d.text===l.bestMatch||d.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(t,e,s)]}}catch(n){ct.warn(`LLM self-healing failed (${n.message}), using heuristic suggestions.`)}return this.heuristicFallback.findSuggestions(t,e,s)}async callLlm(t){let e=this.config.timeoutMs||8e3,s=new AbortController,n=setTimeout(()=>s.abort(),e);try{if(this.config.provider==="openai"){let i=this.config.apiKey||process.env.OPENAI_API_KEY;if(!i)throw new Error("OPENAI_API_KEY is not set");let r=await fetch("https://api.openai.com/v1/chat/completions",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${i}`},body:JSON.stringify({model:this.config.model||"gpt-4o-mini",messages:[{role:"user",content:t}],temperature:.1}),signal:s.signal});if(!r.ok)throw new Error(`OpenAI HTTP ${r.status}: ${await r.text()}`);return(await r.json()).choices?.[0]?.message?.content||null}if(this.config.provider==="anthropic"){let i=this.config.apiKey||process.env.ANTHROPIC_API_KEY;if(!i)throw new Error("ANTHROPIC_API_KEY is not set");let r=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:{"Content-Type":"application/json","x-api-key":i,"anthropic-version":"2023-06-01"},body:JSON.stringify({model:this.config.model||"claude-3-haiku-20240307",max_tokens:1024,messages:[{role:"user",content:t}]}),signal:s.signal});if(!r.ok)throw new Error(`Anthropic HTTP ${r.status}: ${await r.text()}`);return(await r.json()).content?.[0]?.text||null}if(this.config.provider==="ollama"){let i=this.config.endpoint||process.env.OLLAMA_BASE_URL||"http://localhost:11434",r=await fetch(`${i.replace(/\/$/,"")}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:this.config.model||"llama3.2",messages:[{role:"user",content:t}],stream:!1}),signal:s.signal});if(!r.ok)throw new Error(`Ollama HTTP ${r.status}: ${await r.text()}`);return(await r.json()).message?.content||null}return null}finally{clearTimeout(n)}}}});var qr=ie(()=>{"use strict"});function Bn(a){let t=a?.provider||R.ai.provider;if(t==="heuristic")return new jt;if(t==="mock")return new es;let e={provider:t,model:a?.model||R.ai.model,endpoint:a?.endpoint||R.ai.endpoint};return new Is(e)}var Un=ie(()=>{"use strict";Cs();Hn();je();qr();Cs();Hn()});import ei from"fs";import jn from"path";var ts,Wn=ie(()=>{"use strict";ts=class{static extractDataDirective(t,e=process.cwd()){let s=t.split(`
862
+ `),n="",i=-1;for(let d=0;d<s.length;d++){let p=s[d].trim();if(p.startsWith("#!data")||p.startsWith("#! data")){n=p,i=d;break}}if(i===-1)return null;let r=n.replace(/^#!\s*data\s*/,"").trim(),o=[],l="inline";if(r.startsWith("[")||r.startsWith("{"))try{let d=JSON.parse(r);o=Array.isArray(d)?d.map(p=>this.stringifyObject(p)):[this.stringifyObject(d)]}catch(d){throw new Error(`Failed to parse inline #!data JSON directive: ${d.message}`)}else{let d=r.replace(/^['"]+|['"]+$/g,"").trim(),p=jn.isAbsolute(d)?d:jn.resolve(e,d);l=p,o=this.loadFromFile(p)}let c=[...s];return c.splice(i,1),{directive:n,source:l,cleanedSpec:c.join(`
863
+ `),rows:o}}static loadFromFile(t){if(!ei.existsSync(t))throw new Error(`Data file not found: ${t}`);let e=ei.readFileSync(t,"utf-8");return jn.extname(t).toLowerCase()===".json"?this.parseJson(e):this.parseCsv(e)}static parseJson(t){try{let e=JSON.parse(t);return Array.isArray(e)?e.map(s=>this.stringifyObject(s)):[this.stringifyObject(e)]}catch(e){throw new Error(`Invalid JSON data file format: ${e.message}`)}}static parseCsv(t){let e=this.splitCsvLines(t);if(e.length<2)return[];let s=this.parseCsvRow(e[0]).map(i=>i.trim().replace(/^['"]+|['"]+$/g,"")),n=[];for(let i=1;i<e.length;i++){if(!e[i].trim())continue;let o=this.parseCsvRow(e[i]),l={};for(let c=0;c<s.length;c++){let d=s[c];l[d]=o[c]!==void 0?o[c].trim():""}n.push(l)}return n}static filterRows(t,e){if(!e)return t;let s=[...t];if(e.onlyRow!==void 0&&e.onlyRow>0){let n=e.onlyRow-1;if(n<s.length)return[s[n]];throw new Error(`Requested --only-row=${e.onlyRow}, but dataset only contains ${s.length} row(s).`)}if(e.iterations!==void 0&&e.iterations>0){if(e.iterations<=s.length)return s.slice(0,e.iterations);let n=[];for(let i=0;i<e.iterations;i++)n.push(s[i%s.length]);return n}return s}static parseCsvRow(t){let e=[],s="",n=!1;for(let i=0;i<t.length;i++){let r=t[i];r==='"'?n&&t[i+1]==='"'?(s+='"',i++):n=!n:r===","&&!n?(e.push(s),s=""):s+=r}return e.push(s),e}static splitCsvLines(t){let e=[],s="",n=!1;for(let i=0;i<t.length;i++){let r=t[i];r==='"'?n&&t[i+1]==='"'?(s+='""',i++):(n=!n,s+=r):(r===`
864
+ `||r==="\r")&&!n?(r==="\r"&&t[i+1]===`
865
+ `&&i++,s.trim().length>0&&e.push(s),s=""):s+=r}return s.trim().length>0&&e.push(s),e}static stringifyObject(t){if(!t||typeof t!="object")return{};let e={};for(let[s,n]of Object.entries(t))e[s]=n!=null?String(n):"";return e}}});var si={};Qe(si,{AutonomousRecorder:()=>At,autonomousRecorder:()=>Vn});import De from"fs";import Te from"path";import J from"chalk";import{spawn as Eo}from"child_process";import ti from"readline";import{createRequire as Ao}from"module";var Do,Yn,At,Vn,ss=ie(()=>{"use strict";Re();Ws();us();Ve();je();Do=Ao(import.meta.url),Yn="1.1.0";try{Yn=Do("../../package.json").version||"1.1.0"}catch{}At=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=R.screen.fallbackWidth;touchMaxY=R.screen.fallbackHeight;displayWidth=R.screen.fallbackWidth;displayHeight=R.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(t){this.driver=t||oe}flushToDisk(){if(this.resolvedOutputPath)try{this.saveSpec(this.resolvedOutputPath)}catch{}if(this.resolvedReportPath)try{this.saveReport(this.resolvedReportPath)}catch{}}appendStep(t,e){if(this.recordedSteps[this.recordedSteps.length-1]!==t){this.recordedSteps.push(t),this.flushToDisk(),e&&console.log(e);try{this.onStep?.(t)}catch{}}}extractBrandSlug(t){if(!t)return"app";let e=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=t.split("."),n=s.filter(r=>!e.has(r.toLowerCase())&&!/^\d+$/.test(r));return((n.length>0?n[n.length-1]:s[s.length-1])||"app").toLowerCase().replace(/[^a-z0-9_]/g,"")}async startSession(t,e,s){let n=await this.driver.ensureDefaultDevice(e);if(!n)throw new Error("No Android device connected for recording.");this.deviceLock=Ft(n),this.targetSerial=n,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(n);u&&(this.targetPackage=u)}catch{}let i=this.extractBrandSlug(this.targetPackage),r;if(t&&t!=="specs/recorded_suite.txt")r=Te.resolve(t);else{let u=`${i}_flow`,h=Te.resolve(`specs/${u}.txt`);if(!this.isAppendMode&&De.existsSync(h)){let m=1;for(h=Te.resolve(`specs/${u}_${m}.txt`);De.existsSync(h);)m++,h=Te.resolve(`specs/${u}_${m}.txt`)}r=h}if(this.resolvedOutputPath=r,De.existsSync(r))if(this.isAppendMode){let u=De.readFileSync(r,"utf-8"),h=u.split(`
866
+ `);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 w=T.match(/# Package:\s*([a-zA-Z0-9._]+)/);w&&(this.targetPackage=w[1])}}let m=`${r}.bak`;De.writeFileSync(m,u,"utf-8"),console.log(J.cyan(`\u{1F4E5} Loaded ${this.existingSteps.length} existing step(s) from: ${Te.basename(r)}`)),console.log(J.gray(`\u{1F4E6} Safety backup preserved at: ${Te.basename(m)}`))}else{let u=De.readFileSync(r,"utf-8"),h=`${r}.bak`;De.writeFileSync(h,u,"utf-8"),console.log(J.yellow(`\u26A0\uFE0F File "${Te.basename(r)}" already exists.`)),console.log(J.gray(`\u{1F4E6} Safety backup preserved at: ${Te.basename(h)}`))}await this.driver.wakeDevice(n).catch(()=>{});let l=await this.driver.getScreenDimensions(n);this.displayWidth=l.width||R.screen.fallbackWidth,this.displayHeight=l.height||R.screen.fallbackHeight,this.touchMaxX=this.displayWidth,this.touchMaxY=this.displayHeight;try{let{stdout:u}=await this.driver.execAdb(["shell","getevent","-p"],n),h=u.match(/0035\s*:\s*value\s*\d+,\s*min\s*\d+,\s*max\s*(\d+)/),m=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])),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 c=Te.basename(r,".txt"),d=Te.resolve("output");this.screenshotDir=Te.join(d,"screenshots",c),De.existsSync(this.screenshotDir)||De.mkdirSync(this.screenshotDir,{recursive:!0});let p=Te.join(d,`${c}_report.md`);this.resolvedReportPath=p,await this.refreshHierarchySync(),this.startLogcatScreenWatcher(n),this.startLogcatCrashWatcher(n),this.startEventStream(n),this.flushToDisk()}async stopSession(){if(this.isRecording=!1,this.inputDebounceTimer&&(clearTimeout(this.inputDebounceTimer),this.inputDebounceTimer=null),this.deviceLock&&(Ot(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(t,e,s){await this.startSession(t,e,s);let n=J.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"),i=await this.driver.getDeviceModel(this.targetSerial).catch(()=>this.targetSerial);console.log(`
867
+ ${n}`),console.log(J.bold.cyan(`\u26A1 PromptTest v${Yn}`)+J.gray(" | Recording Session")),console.log(n),console.log(` ${J.gray("\u{1F4F1} Device ")} ${J.white(i)} ${J.gray(`(${this.targetSerial})`)}`),console.log(` ${J.gray("\u{1F4E6} App ")} ${J.green(this.targetPackage||"Unknown")}`),console.log(` ${J.gray("\u{1F3AF} Spec ")} ${J.yellow(Te.basename(this.resolvedOutputPath))}`),console.log(` ${J.gray("\u{1F4CA} Report ")} ${J.yellow(Te.basename(this.resolvedReportPath))}`),console.log(` ${J.gray("\u{1F4F8} Photos ")} ${J.yellow(Te.relative(process.cwd(),this.screenshotDir))}`),this.isAppendMode&&this.existingSteps.length>0&&console.log(` ${J.gray("\u{1F4CE} Mode ")} ${J.magenta(`Append \u2014 resuming from step #${this.existingSteps.length+1}`)}`),console.log(n),console.log(` ${J.green("\u2714")} Ready & streaming`),console.log(n),console.log(` ${J.gray("Use the app naturally. Every action is captured.")}`),console.log(` ${J.gray('Press Ctrl+C or type "exit" / "q" + Enter to stop and save.')}`),console.log(`${n}
868
+ `);let r=null,o=async()=>{if(r)try{r.close()}catch{}await this.stopSession(),this.printExitSummary(n),process.exit(0)};if(process.once("SIGINT",o),process.once("SIGTERM",o),process.stdin.isTTY)try{r=ti.createInterface({input:process.stdin,output:process.stdout}),r.on("line",c=>{let d=c.trim().toLowerCase();(d==="exit"||d==="quit"||d==="q"||d==="stop")&&o()}),r.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(t){let e=t?["-s",t,"shell","getevent","-l"]:["shell","getevent","-l"],s=it(),n=Eo(s,e);this.geteventProcess=n;let i=null,r=null,o=null,l=null,c=0;ti.createInterface({input:n.stdout}).on("line",p=>{if(this.isRecording)if(p.includes("ABS_MT_POSITION_X")){let u=p.match(/ABS_MT_POSITION_X\s+([0-9a-fA-F]+)/);u&&(i=parseInt(u[1],16))}else if(p.includes("ABS_MT_POSITION_Y")){let u=p.match(/ABS_MT_POSITION_Y\s+([0-9a-fA-F]+)/);u&&(r=parseInt(u[1],16))}else if(p.includes("BTN_TOUCH")&&p.includes("DOWN"))o=i,l=r,c=Date.now();else if(p.includes("BTN_TOUCH")&&p.includes("UP")){let u=i!==null?i:o,h=r!==null?r:l,m=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),w=Math.round(u/this.touchMaxX*this.displayWidth),S=Math.round(h/this.touchMaxY*this.displayHeight),y=w-b,P=S-T,N=Math.abs(y),O=Math.abs(P),L=Math.hypot(N,O);if(this.activeInput&&T>this.displayHeight*.58&&S>this.displayHeight*.58){m>=20&&m<400&&L<45&&w>=this.displayWidth*.78&&S>=this.displayHeight*.84?this.flushActiveInput().then(()=>{this.handleKeyPress("Enter")}).catch(()=>{}):this.scheduleInputDebounce(),i=null,r=null,o=null,l=null;return}if(m>=20&&m<400&&L<45)this.isRefreshingHierarchy?this.inFlightTapQueue.push({x:w,y:S,timestamp:Date.now()}):this.handleTapAt(w,S);else if(m>=450&&L<45)this.handleLongPressAt(w,S);else if(O>=100&&O>1.4*N){let U=P<0?"down":"up";this.handleScroll(U)}else if(N>=100&&N>1.4*O){let U=y<0?"left":"right";this.handleSwipe(U)}}i=null,r=null,o=null,l=null}else p.includes("KEY_ENTER")&&p.includes("DOWN")?this.handleKeyPress("Enter"):p.includes("KEY_BACK")&&p.includes("DOWN")&&this.handleKeyPress("Back")}),n.on("close",p=>{this.isRecording&&(console.warn(J.yellow(`
869
+ \u26A0\uFE0F [EventStream] ADB event listener stream closed (code ${p}). Auto-reconnecting in 800ms...`)),setTimeout(()=>{this.isRecording&&this.startEventStream(t)},800))}),n.on("error",p=>{this.isRecording&&console.warn(J.yellow(`
870
+ \u26A0\uFE0F [EventStream] ADB stream error: ${p.message}.`))})}cleanLabel(t){return t?t.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(t){let e=Date.now();if(this.lastScrollDirection===t&&e-this.lastScrollTimestamp<850)return;this.lastScrollTimestamp=e,this.lastScrollDirection=t,this.activeInput&&this.flushActiveInput().catch(()=>{});let s=`Scroll ${t}`;this.recordedSteps[this.recordedSteps.length-1]!==s&&(this.appendStep(s,J.blue(` \u{1F4DC} Recorded Scroll [Step #${this.getCurrentStepNumber()}]: ${J.bold(s)}`)),this.triggerAsyncHierarchyRefresh(400))}handleSwipe(t){let e=Date.now();if(e-this.lastScrollTimestamp<600)return;this.lastScrollTimestamp=e,this.activeInput&&this.flushActiveInput().catch(()=>{});let s=`Swipe ${t}`;this.appendStep(s,J.blue(` \u{1F449} Recorded Swipe [Step #${this.getCurrentStepNumber()}]: ${J.bold(s)}`)),this.triggerAsyncHierarchyRefresh(500)}handleLongPressAt(t,e){let s=this.lastHierarchyFlat.filter(r=>t>=r.bounds.x1&&t<=r.bounds.x2&&e>=r.bounds.y1&&e<=r.bounds.y2);if(s.length===0)return;s.sort((r,o)=>{let l=(r.bounds.x2-r.bounds.x1)*(r.bounds.y2-r.bounds.y1),c=(o.bounds.x2-o.bounds.x1)*(o.bounds.y2-o.bounds.y1);return l-c});let n=s[0],i=this.resolveActionLabel(n,s,this.lastHierarchyFlat);if(i){let r=`Long press '${i}'`;this.appendStep(r,J.cyan(` \u23F1\uFE0F Recorded Long Press [Step #${this.getCurrentStepNumber()}]: ${J.bold(r)}`)),this.triggerAsyncHierarchyRefresh()}}handleTapAt(t,e){let s=Date.now(),n=this.lastHierarchyFlat.filter(p=>t>=p.bounds.x1&&t<=p.bounds.x2&&e>=p.bounds.y1&&e<=p.bounds.y2),r=this.lastHierarchyFlat.filter(p=>{let u=p.bounds.height;return u>0&&u<320&&p.bounds.width>0&&(p.clickable||p.contentDesc||p.text)}).filter(p=>e>=p.bounds.y1-30&&e<=p.bounds.y2+40);if(r.length>=2){let p=r.find(u=>t>=u.bounds.x1-20&&t<=u.bounds.x2+20);p&&(n=[this.lastHierarchyFlat.find(h=>(this.cleanLabel(h.text).length>0||this.cleanLabel(h.contentDesc).length>0)&&Math.abs(h.bounds.centerX-p.bounds.centerX)<p.bounds.width*.48&&Math.abs(h.bounds.centerY-p.bounds.centerY)<p.bounds.height*.48&&!this.isIconGlyph(this.cleanLabel(h.text)))||p])}if(e>this.displayHeight*.82){let u=this.lastHierarchyFlat.filter(h=>{let m=h.bounds.y1>=this.displayHeight*.78||h.bounds.y2>=this.displayHeight*.82,b=h.bounds.width>0&&h.bounds.width<=this.displayWidth*.52;return m&&b}).find(h=>t>=h.bounds.x1-20&&t<=h.bounds.x2+20);if(u){let h=this.lastHierarchyFlat.find(m=>(this.cleanLabel(m.text).length>0||this.cleanLabel(m.contentDesc).length>0)&&t>=m.bounds.x1-25&&t<=m.bounds.x2+25&&m.bounds.y1>=this.displayHeight*.78&&!this.isIconGlyph(this.cleanLabel(m.text)));h?n=[h]:(this.cleanLabel(u.text)||this.cleanLabel(u.contentDesc))&&(n=[u])}}if(n.length===0){let p=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"))),u=null,h=70;for(let m of p){let b=Math.hypot(m.bounds.centerX-t,m.bounds.centerY-e);b<h&&(h=b,u=m)}if(u)n=[u];else{this.triggerAsyncHierarchyRefresh();return}}n.sort((p,u)=>{let h=(p.bounds.x2-p.bounds.x1)*(p.bounds.y2-p.bounds.y1),m=(u.bounds.x2-u.bounds.x1)*(u.bounds.y2-u.bounds.y1);return h-m});let o=n[0],l=n.find(p=>p.className.includes("EditText"))||(o.className.includes("EditText")?o:null);if(l){this.activeInput&&this.activeInput.node!==l&&this.flushActiveInput().catch(()=>{});let p=this.resolveFieldLabel(l,this.lastHierarchyFlat);this.activeInput={node:l,label:p,startText:l.text||""},this.triggerAsyncHierarchyRefresh();return}this.activeInput&&this.flushActiveInput().catch(()=>{});let c=this.resolveActionLabel(o,n,this.lastHierarchyFlat);if(!c||c.length===0){this.triggerAsyncHierarchyRefresh();return}if(!o.clickable&&!n.some(p=>p.clickable||p.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 d;if(o.checkable||o.className.includes("Switch")||o.className.includes("CheckBox")){let p=o.checked!==void 0?!o.checked:!0;d=`Toggle '${c}' to ${p?"on":"off"}`}else{d=`Tap '${c}'`;let p=this.lastHierarchyFlat.filter(h=>this.cleanLabel(h.text)===c||this.cleanLabel(h.contentDesc)===c),u=[];for(let h of p)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 m=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(m){let b=this.cleanLabel(m.text)||this.cleanLabel(m.contentDesc);b&&b!==c&&(d=`Tap '${c}' next to '${b}'`)}}}this.appendStep(d,J.cyan(` \u{1F446} Recorded Tap [Step #${this.getCurrentStepNumber()}]: ${J.bold(d)}`)),this.triggerAsyncHierarchyRefresh()}handleKeyPress(t){if(this.activeInput&&this.flushActiveInput().catch(()=>{}),t==="Enter"){let e="Press 'Enter'";this.appendStep(e,J.magenta(` \u21B5 Recorded Key [Step #${this.getCurrentStepNumber()}]: ${J.bold(e)}`))}else if(t==="Back"){let e="Tap 'Back'";this.appendStep(e,J.magenta(` \u25C0 Recorded Key [Step #${this.getCurrentStepNumber()}]: ${J.bold(e)}`))}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 t=this.activeInput;this.activeInput=null,await this.recordInputText(t)}async syncActiveInputText(){this.activeInput&&await this.recordInputText(this.activeInput)}async recordInputText(t){try{let e=await this.driver.getUiHierarchy(this.targetSerial).catch(()=>null);if(!e)return;let{flat:s}=be(e);this.lastHierarchyFlat=s;let n=s.find(i=>i.className.includes("EditText")&&(i.focused||i.selected))||s.find(i=>i.className.includes("EditText")&&this.resolveFieldLabel(i,s).toLowerCase()===t.label.toLowerCase())||s.find(i=>i.className.includes("EditText")&&Math.abs(i.bounds.centerX-t.node.bounds.centerX)<80)||s.find(i=>i.className.includes("EditText"));if(n&&n.text&&n.text.trim().length>0){let i=n.text.trim(),r=/^[•*•\u2022\u25cf]+$/.test(i);if(!(i===t.startText)&&!r){if(t.startText===""&&this.isPlaceholderText(i))return;let l=t.label||this.resolveFieldLabel(n,s),c=`Type '${i}' into '${l}'`,d=this.recordedSteps.length-1,p=d>=0?this.recordedSteps[d]:"";p.startsWith("Type ")&&p.endsWith(`into '${l}'`)?p!==c&&(this.recordedSteps[d]=c,this.flushToDisk(),console.log(J.gray(` \u270D\uFE0F Updated Input [Step #${d+1+this.existingSteps.length}]: ${J.bold(c)}`))):this.appendStep(c,J.green(` \u270D\uFE0F Recorded Input [Step #${this.getCurrentStepNumber()}]: ${J.bold(c)}`))}else if(r&&(n.password||t.label.toLowerCase().includes("password"))){let l=t.label||"Password",c=`Type '<password>' into '${l}'`,d=this.recordedSteps.length-1,p=d>=0?this.recordedSteps[d]:"";(!p.startsWith("Type ")||!p.endsWith(`into '${l}'`))&&this.appendStep(c,J.green(` \u270D\uFE0F Recorded Password Input [Step #${this.getCurrentStepNumber()}]: ${J.bold(c)}`))}}}catch{}}startLogcatScreenWatcher(t){try{this.logcatEventProcess=this.driver.streamLogcatEvents(t,e=>{if(this.isRecording&&(e.includes("wm_on_resume_called")||e.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(t){try{this.logcatCrashProcess=this.driver.streamLogcatCrashes(t,e=>{if(this.isRecording&&(e.includes("FATAL EXCEPTION")||e.includes("ANR in"))){if(this.targetPackage&&!e.includes(this.targetPackage))return;this.crashDetected=!0,console.log(J.red.bold(`
871
+ \u26A0\uFE0F [Crash Alert] Fatal exception / ANR detected in ${this.targetPackage||"app"}!`))}})}catch{}}captureSessionScreenshot(t){if(!this.screenshotDir)return;let e=++this.screenshotCounter,s=t.toLowerCase().replace(/[^a-z0-9_]/g,"_").slice(0,30)||"screen",n=`screen_${String(e).padStart(2,"0")}_${s}.png`,i=Te.join(this.screenshotDir,n);(async()=>{try{let r=await this.driver.captureScreenshot(this.targetSerial);if(r&&r.length>0){await De.promises.writeFile(i,r);let o=this.sessionScreenSections[this.sessionScreenSections.length-1];o&&!o.screenshotFile&&(o.screenshotFile=n)}}catch{}})()}triggerAsyncHierarchyRefresh(t=R.recorder.asyncRefreshDelayMs){this.isRefreshingHierarchy||(this.isRefreshingHierarchy=!0,setTimeout(()=>{this.doHierarchyRefresh().catch(()=>{this.isRefreshingHierarchy=!1})},t))}async doHierarchyRefresh(){try{let t=await this.driver.getUiHierarchy(this.targetSerial),{flat:e}=be(t);this.lastHierarchyFlat=e,this.hierarchyStale=!1,this.hierarchyCacheTime=Date.now(),this.detectAndLogScreenHeader(e),this.detectAndLogTransientAlerts(e)}catch{}finally{for(this.isRefreshingHierarchy=!1;this.inFlightTapQueue.length>0;){let t=this.inFlightTapQueue.shift();if(!t)break;Date.now()-t.timestamp<R.recorder.staleTapTimeoutMs?this.handleTapAt(t.x,t.y):console.warn(J.yellow(`[Recorder] Dropped stale tap at (${t.x}, ${t.y}) \u2014 screen refresh took longer than ${R.recorder.staleTapTimeoutMs}ms`))}}}detectAndLogTransientAlerts(t){let e=t.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(e){let s=this.cleanLabel(e.text)||this.cleanLabel(e.contentDesc);if(s&&s.length>3&&s.length<80){let n=`Verify '${s}' is visible`;this.recordedSteps.slice(-3).includes(n)||this.appendStep(n,J.green(` \u2714 Recorded Alert [Step #${this.getCurrentStepNumber()}]: ${J.bold(n)}`))}}}async refreshHierarchySync(){try{let t=await this.driver.getUiHierarchy(this.targetSerial),{flat:e}=be(t);this.lastHierarchyFlat=e,this.hierarchyStale=!1,this.hierarchyCacheTime=Date.now(),this.detectAndLogScreenHeader(e)}catch{}}isIconGlyph(t){if(!t||t.trim().length===0)return!0;let e=t.trim();return!!(/[\uE000-\uF8FF\uFFF0-\uFFFF]/.test(e)||e.length===1&&e.charCodeAt(0)>255&&!new RegExp("\\p{Extended_Pictographic}","u").test(e))}cleanDialogOrCompoundLabel(t){if(!t)return"";let e=t.trim();if(e.includes(",")||e.includes(`
872
+ `)){let s=e.split(/[\n,]+/).map(n=>this.cleanLabel(n)).filter(Boolean);if(s.length>1){let n=s.find(r=>r.includes("?")||/\b(are you sure|do you want|cannot be undone|would you like|please confirm)\b/i.test(r)||r.length>35);if(n){let r=s.find(o=>o!==n&&o.length<=30&&!o.includes("?"));if(r)return r}let i=s.filter(r=>/^[A-Z]{1,3}$/.test(r)||/^\d+%?$/.test(r)||this.isCardMetadataSubtitle(r)||/^(attend|present|absent|status|active|inactive)$/i.test(r)?!1:r.length>1);if(i.length>0)return i[0]}}return e}isCardMetadataSubtitle(t){if(!t||t.trim().length===0)return!1;let e=t.trim();return!!(/\+?\d{1,3}[\s-]?\d{4,}[\s-]?\d{4,}/.test(e)||/^(STD|ID|EMP|REF|ORD|USER|ACC|BATCH)[\-_:\s]?\d+/i.test(e)||/[•|·]/.test(e)&&/\d/.test(e)||/^\d+$/.test(e)||/^\d{1,2}:\d{2}/.test(e)||/^(teacher|roll\s*no|std\s*id|class|sec)[\s:]+/i.test(e))}findPrimaryCardTitle(t,e,s){if(t.bounds.y1>=this.displayHeight*.8||t.bounds.centerY>=this.displayHeight*.82||t.clickable&&(t.className.includes("Button")||t.className.includes("Check")))return null;let n=this.cleanLabel(t.text),i=this.isCardMetadataSubtitle(n),r=s.find(c=>c!==t&&c.clickable&&c.bounds.height>60&&c.bounds.height<600),o;r?o=e.filter(c=>c.className.includes("Text")&&c.bounds.x1>=r.bounds.x1-10&&c.bounds.x2<=r.bounds.x2+10&&c.bounds.y1>=r.bounds.y1-10&&c.bounds.y2<=r.bounds.y2+10):o=e.filter(c=>c!==t&&c.className.includes("Text")&&c.bounds.centerY<t.bounds.centerY&&t.bounds.centerY-c.bounds.centerY<160&&Math.abs(c.bounds.centerX-t.bounds.centerX)<500);let l=o.filter(c=>{let d=this.cleanLabel(c.text);return d.length>2&&!this.isCardMetadataSubtitle(d)&&!this.isIconGlyph(d)&&!/^\d+$/.test(d)&&!/^(active|inactive|present|absent|new|pending)$/i.test(d)}).sort((c,d)=>c.bounds.y1-d.bounds.y1);if(l.length>0){let c=this.cleanLabel(l[0].text);if(i||r&&c!==n&&!t.clickable)return c}return null}resolveActionLabel(t,e,s){if(t.bounds.y1>=this.displayHeight*.8||t.bounds.centerY>=this.displayHeight*.82){let c=this.cleanLabel(t.contentDesc)||this.cleanLabel(t.text);if(c&&c.length>0&&!this.isIconGlyph(c))return this.cleanDialogOrCompoundLabel(c);for(let d of e){let p=this.cleanLabel(d.contentDesc)||this.cleanLabel(d.text);if(p&&p.length>0&&!this.isIconGlyph(p))return this.cleanDialogOrCompoundLabel(p)}}let n=this.findPrimaryCardTitle(t,s,e);if(n)return this.cleanDialogOrCompoundLabel(n);let i=this.cleanLabel(t.text);if(i.length>0&&i.length<90){let c=i.split(/[\n\r]+/).map(p=>this.cleanLabel(p)).filter(Boolean),d=c.length>1?c.find(p=>p.length>2&&!/^\d+$/.test(p))||c[0]:i;return this.cleanDialogOrCompoundLabel(d)}let r=this.cleanLabel(t.contentDesc);if(r.length>0&&r.length<90)return this.cleanDialogOrCompoundLabel(r);let o=e.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 e){let d=this.cleanLabel(c.text);if(d.length>0&&d.length<90){let u=d.split(/[\n\r]+/).map(m=>this.cleanLabel(m)).filter(Boolean),h=u.length>1?u.find(m=>m.length>2&&!/^\d+$/.test(m))||u[0]:d;return this.cleanDialogOrCompoundLabel(h)}let p=this.cleanLabel(c.contentDesc);if(p.length>0&&p.length<90)return this.cleanDialogOrCompoundLabel(p)}let l=s.find(c=>c!==t&&(this.cleanLabel(c.text).length>0||this.cleanLabel(c.contentDesc).length>0)&&Math.abs(c.bounds.centerX-t.bounds.centerX)<80&&Math.abs(c.bounds.centerY-t.bounds.centerY)<100);if(l){let c=this.cleanLabel(l.text)||this.cleanLabel(l.contentDesc);if(c.length>0)return this.cleanDialogOrCompoundLabel(c)}if(t.resourceId){let c=t.resourceId.split("/"),d=c[c.length-1];if(d&&!/^\d+$/.test(d)){let p=d.replace(/_/g," ").replace(/btn|button|icon|view|action/gi,"").trim();if(p.length>1)return p.charAt(0).toUpperCase()+p.slice(1)}}return""}getCurrentStepNumber(){return this.existingSteps.length+this.recordedSteps.length+1}detectAndLogScreenHeader(t){let e=t.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 n=s.text.trim();return!(this.isCardMetadataSubtitle(n)||/\b(JIO|Airtel|Vodafone|No service|Emergency|VoLTE|4G|5G)\b/i.test(n)||/^\d+%?$|^\d+$/.test(n)||/^step\s*\d+\s*(of|\/)\s*\d+/i.test(n))}).sort((s,n)=>s.bounds.y1-n.bounds.y1);if(e.length>0){let s=e[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 n=`Verify '${s}' is visible`;this.recordedSteps[this.recordedSteps.length-1]!==n&&!this.recordedSteps.slice(-3).includes(n)&&this.appendStep(n,J.green(` \u2714 Recorded Header [Step #${this.getCurrentStepNumber()}]: ${J.bold(n)}`))}}}resolveFieldLabel(t,e){if(t.contentDesc&&t.contentDesc.trim().length>0)return t.contentDesc.trim();if(t.password)return"Password";let s=e.filter(n=>n.className.includes("TextView")&&n.text&&n.text.trim().length>1&&n.bounds.centerY<t.bounds.centerY&&t.bounds.centerY-n.bounds.centerY<250&&Math.abs(n.bounds.centerX-t.bounds.centerX)<450);if(s.length>0){s.sort((i,r)=>r.bounds.centerY-i.bounds.centerY);let n=s[0].text.trim().replace(/[*:]/g,"").trim();if(n.length>0)return n}if(t.resourceId){let n=t.resourceId.split("/"),i=n[n.length-1];if(i&&!/^\d+$/.test(i)){let r=i.replace(/_/g," ").replace(/edit|text|input|field|et|txt/gi,"").trim();return r.length>1?r.charAt(0).toUpperCase()+r.slice(1):i}}if(t.text&&t.text.trim().length>0&&t.text.trim().length<40){let n=t.text.trim();if(n.toLowerCase().startsWith("search"))return n.length<=25?n:"Search";if(!this.isPlaceholderText(n))return n}return"Input Field"}isPlaceholderText(t){let e=t.trim().toLowerCase();return e.startsWith("search")||e.startsWith("e.g.")||e.startsWith("enter ")||e.startsWith("type ")||e.includes("optional")}saveSpec(t){let e=Te.resolve(t);if(Te.basename(e).startsWith("app_flow")&&this.targetPackage&&!this.isAppendMode){let l=this.extractBrandSlug(this.targetPackage);if(l!=="app"){let c=`${l}_flow`,d=Te.resolve(`specs/${c}.txt`),p=1;for(;De.existsSync(d);)d=Te.resolve(`specs/${c}_${p}.txt`),p++;e=d}}let n=Te.dirname(e);De.existsSync(n)||De.mkdirSync(n,{recursive:!0});let i=[];if(this.isAppendMode&&this.existingSteps.length>0&&De.existsSync(e)){let l=De.readFileSync(e,"utf-8").trim();i.push(l),i.push(""),i.push(`# --- Appended Session (${new Date().toISOString().replace("T"," ").slice(0,19)}) ---`),this.recordedSteps.forEach((c,d)=>{i.push(`${this.existingSteps.length+d+1}. ${c}`)})}else{i.push("# ==========================================================",`# Autonomous Recorded Test Spec: ${Te.basename(e)}`,...this.targetPackage?[`# Package: ${this.targetPackage}`]:[],`# Generated: ${new Date().toISOString()}`,`# PromptTest v${Yn}`,"# ==========================================================","");let l=0;this.recordedSteps.forEach((c,d)=>{if(l<this.sessionScreenSections.length&&d===this.sessionScreenSections[l].stepStart){let p=this.sessionScreenSections[l];i.push(""),i.push(`# Screen: ${p.name}`),l++}i.push(`${this.existingSteps.length+d+1}. ${c}`)})}i.push("");let r=i.join(`
873
+ `),o=`${e}.tmp.${Date.now()}`;try{De.writeFileSync(o,r,"utf-8"),De.renameSync(o,e)}catch{try{De.unlinkSync(o)}catch{}De.writeFileSync(e,r,"utf-8")}}saveReport(t){let e=Math.round((Date.now()-this.sessionStartTime)/1e3),s=Math.floor(e/60),n=e%60,i=`${String(s).padStart(2,"0")}:${String(n).padStart(2,"0")}`,r=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** | ${i} |`,`| **Steps in Session** | ${this.recordedSteps.length} |`,`| **Total Suite Steps** | ${r} |`,`| **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(`
874
+ `):"*Single screen flow*","","---","","## Recorded Interactions",""];if(this.sessionScreenSections.length>0)for(let l=0;l<this.sessionScreenSections.length;l++){let c=this.sessionScreenSections[l],d=this.sessionScreenSections[l+1],p=d?d.stepStart:this.recordedSteps.length,u=this.recordedSteps.slice(c.stepStart,p);o.push(`### Screen: ${c.name}`),o.push(""),c.screenshotFile&&(o.push(`![${c.name}](screenshots/${Te.basename(this.screenshotDir)}/${c.screenshotFile})`),o.push("")),u.length>0?u.forEach((h,m)=>{o.push(`${c.stepStart+m+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=Te.dirname(t);De.existsSync(l)||De.mkdirSync(l,{recursive:!0}),De.writeFileSync(t,o.join(`
875
+ `),"utf-8")}catch{}}printExitSummary(t){let e=Math.round((Date.now()-this.sessionStartTime)/1e3),s=Math.floor(e/60),n=e%60,i=`${String(s).padStart(2,"0")}:${String(n).padStart(2,"0")}`,r=this.existingSteps.length+this.recordedSteps.length;console.log(`
876
+ ${t}`),console.log(J.bold.green(" \u{1F4BE} Recording Session Saved Successfully!")),console.log(t),console.log(` ${J.gray("Steps Captured ")} ${J.bold.yellow(this.recordedSteps.length.toString())} ${this.isAppendMode?J.gray(`(Total Suite: ${r})`):""}`),console.log(` ${J.gray("Screens Visited")} ${J.bold.white(this.visitedScreens.length.toString())} ${this.visitedScreens.length>0?J.gray(`(${this.visitedScreens.join(" \u2192 ")})`):""}`),console.log(` ${J.gray("Screenshots ")} ${J.bold.white(this.screenshotCounter.toString())} saved`),console.log(` ${J.gray("Session Time ")} ${J.bold.white(i)}`),console.log(` ${J.gray("Crash Signals ")} ${this.crashDetected?J.red.bold("\u26A0\uFE0F Detected"):J.green("\u2714 0 detected")}`),console.log(t),console.log(` ${J.gray("\u{1F4C4} Spec ")} ${J.cyan(this.resolvedOutputPath)}`);let o=Te.basename(this.resolvedOutputPath,".txt");console.log(` ${J.gray("\u{1F4CA} Report ")} ${J.cyan(Te.resolve("output",`${o}_report.md`))}`),console.log(`${t}
877
+ `)}},Vn=new At});var ri={};Qe(ri,{formatDoctorReport:()=>Jn,runDoctor:()=>gs});import Ns from"fs";import ni from"path";import Be from"chalk";async function gs(a=oe,t={}){let e=[],s=process.versions.node,i=parseInt(s.split(".")[0],10)>=18;e.push({name:"Node.js Runtime",passed:i,message:`Node.js v${s} (Required >= 18.0.0)`,hint:i?void 0:"Please upgrade Node.js to v18.0.0 or higher: https://nodejs.org"});let r=!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?(r=!0,o=`Android Debug Bridge version ${y[1]}`,l=void 0):(r=!0,o=S.split(`
878
+ `)[0].trim(),l=void 0)}catch(S){o=`Failed to execute adb: ${S.message}`}e.push({name:"Android Debug Bridge (ADB)",passed:r,message:o,hint:l});let c=!1,d="No connected Android devices or emulators found",p="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(O=>O.state==="device"),P=S.filter(O=>O.state==="unauthorized"),N=S.filter(O=>O.state==="offline");if(y.length>0){c=!0;let O=y.map(L=>`${L.serial} (${L.model||"Unknown"})`).join(", ");d=`Found ${y.length} authorized device(s): ${O}`,p=void 0}else P.length>0?(d=`Device(s) found but unauthorized: ${P.map(O=>O.serial).join(", ")}`,p='Check the device screen and accept the "Allow USB debugging" RSA prompt.'):N.length>0&&(d=`Device(s) found in offline state: ${N.map(O=>O.serial).join(", ")}`,p='Try running "adb reconnect" or reconnect the USB cable.')}catch(S){d=`Failed to list devices: ${S.message}`}if(e.push({name:"Connected Android Device",passed:c,message:d,hint:p}),t.packageName){let S=t.packageName,y=!1,P=`Package "${S}" not found on target device`,N='Install the APK using "adb install <path.apk>" or check the package identifier.';if(c)try{let{stdout:O}=await a.execAdb(["shell","pm","list","packages",S],t.serial,5e3);O.includes(`package:${S}`)&&(y=!0,P=`Package "${S}" is installed on target device`,N=void 0)}catch(O){P=`Failed to query package status: ${O.message}`}else P="Skipped package verification (no authorized device available)",N=`Connect a device to verify package "${S}".`;e.push({name:`Target Package [${S}]`,passed:y,message:P,hint:N})}let u=!0,h="All required workspace directories are accessible",m,b=["output","memory",".prompttest"];for(let S of b)try{let y=ni.resolve(process.cwd(),S);Ns.existsSync(y)||Ns.mkdirSync(y,{recursive:!0});let P=ni.join(y,".doctor_perm_test");Ns.writeFileSync(P,"ok","utf-8"),Ns.unlinkSync(P)}catch(y){u=!1,h=`Filesystem permission error for "./${S}": ${y.message}`,m="Ensure the current user has write permissions to the workspace directory.";break}e.push({name:"Workspace Storage & Permissions",passed:u,message:h,hint:m});let T=e.filter(S=>S.passed).length,w=e.filter(S=>!S.passed).length;return{timestamp:new Date().toISOString(),allPassed:w===0,checks:e,summary:{passed:T,failed:w,warnings:0}}}function Jn(a){let t=["",Be.bold.cyan("\u{1FA7A} PromptTest Doctor \u2014 Environment Health Diagnostic"),Be.gray(`Scanned at ${a.timestamp}
879
+ `)];for(let e of a.checks){let s=e.passed?Be.green("\u2714"):Be.red("\u2716"),n=Be.bold(e.name);t.push(` ${s} ${n}: ${e.message}`),e.hint&&!e.passed&&t.push(Be.yellow(` \u{1F4A1} Fix: ${e.hint}`))}return t.push(""),a.allPassed?t.push(Be.bold.green("\u2728 All diagnostic checks passed! Your environment is ready to test.")):t.push(Be.bold.red(`\u26A0\uFE0F ${a.summary.failed} diagnostic check(s) failed. Please review the remediation hints above.`)),process.env.CI||t.push("",Be.dim("\u2500".repeat(64)),Be.cyan("\u{1F4A1} Prefer inspecting elements and authoring tests visually?"),Be.white(` Try PromptTest Studio (Desktop IDE): ${Be.underline("https://shriramsingh.github.io/prompttest-studio-site/")}`),Be.dim("\u2500".repeat(64))),t.push(""),t.join(`
880
+ `)}var ms=ie(()=>{"use strict";Re()});var ai={};Qe(ai,{getNodeLabel:()=>mt,sanitizeNodeLabel:()=>Ls,scaffoldSpec:()=>Gn,scaffoldSpecFromNodes:()=>zn,validateScaffoldedSpec:()=>oi});import Kn from"fs";import ii from"path";function Ls(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 mt(a){let t="";if(a.text&&a.text.trim()&&(t=Ls(a.text.replace(/["\r\n]/g," "))),!t&&a.contentDesc&&a.contentDesc.trim()&&(t=Ls(a.contentDesc.replace(/["\r\n]/g," "))),!t&&a.resourceId&&a.resourceId.includes("/")){let e=a.resourceId.split("/").pop()||"";t=Ls(e.replace(/_/g," ").replace(/([a-z])([A-Z])/g,"$1 $2").replace(/\bbtn\b|\bedit\b|\btxt\b|\bview\b/gi,"").trim())}return t||"Field"}function zn(a,t){let e=[],s=t||(a[0]?.packageName?a[0].packageName:"com.example.app");e.push("# =============================================================="),e.push(`# Auto-generated test spec for ${s}`),e.push("# Generated by PromptTest Spec Scaffolder (SC-01)"),e.push(`# Created: ${new Date().toISOString()}`),e.push("# =============================================================="),e.push(""),e.push(`Launch "${s}"`),e.push("Wait 1s"),e.push("");let n=a.filter(h=>!h.clickable&&(h.text||h.contentDesc)&&h.bounds.y1<800&&h.bounds.height>20),i=new Set,r=n.map(h=>mt(h)).filter(h=>!h||h==="Field"||h.length<2||i.has(h.toLowerCase())?!1:(i.add(h.toLowerCase()),!0)).slice(0,2);if(r.length>0){for(let h of r)e.push(`Assert "${h}" is visible`);e.push("")}let o=Fe(a),l=new Set;for(let h of o){let m=mt(h);if(!m||l.has(m.toLowerCase()))continue;l.add(m.toLowerCase());let b=m.toLowerCase();h.password||b.includes("password")||b.includes("pass")?e.push(`Type "SecretPass123!" into "${m}"`):b.includes("email")||b.includes("mail")?e.push(`Type "test.user@example.com" into "${m}"`):b.includes("phone")||b.includes("mobile")?e.push(`Type "+15551234567" into "${m}"`):b.includes("search")||b.includes("find")?e.push(`Type "Search query" into "${m}"`):e.push(`Type "Sample text" into "${m}"`)}o.length>0&&e.push("");let c=at(a);if(c.length>0){let h=mt(c[0]);h&&h!=="Field"&&(e.push(`Tap "${h}"`),e.push("Wait 500ms"),e.push(""))}let d=Oe(a),p=new Set,u=[...d].sort((h,m)=>{let b=mt(h).toLowerCase(),T=mt(m).toLowerCase(),w=/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 w&&!S?-1:!w&&S?1:0});for(let h of u){let m=mt(h);if(!(!m||p.has(m.toLowerCase()))&&(p.add(m.toLowerCase()),!/delete|remove|clear|logout|sign out|exit/i.test(m))){e.push(`Tap "${m}"`),e.push("Wait 1s");break}}if(r[0])e.push(`Assert "${r[0]}" is visible`);else{let h=a.map(m=>mt(m)).find(m=>m&&m!=="Field"&&m.length>2&&!m.includes(s));h?e.push(`Assert "${h}" is visible`):e.push("Wait 1s")}return e.join(`
881
+ `)+`
882
+ `}function oi(a){let t=a.split(`
883
+ `),e=[];for(let s=0;s<t.length;s++){let n=t[s].trim();if(!n||n.startsWith("#"))continue;St(n)||e.push(`Line ${s+1}: Unrecognized prompt syntax -> "${n}"`)}return{valid:e.length===0,errors:e}}async function Gn(a={}){let t=a.driver||oe,e=a.packageName;if(!e)try{e=await t.getForegroundPackage(a.deviceSerial)||void 0}catch{}if((!e||!e.trim())&&(e="com.example.app"),t&&typeof t.wakeDevice=="function"&&await t.wakeDevice(a.deviceSerial).catch(()=>{}),e&&e!=="com.example.app"&&typeof t.getForegroundPackage=="function")try{let h=await t.getForegroundPackage(a.deviceSerial);h&&h!==e&&!h.includes(e)&&typeof t.launchApp=="function"&&(await t.launchApp(e,a.deviceSerial),await new Promise(m=>setTimeout(m,1500)))}catch{}let s="";try{s=await t.getUiHierarchy(a.deviceSerial)}catch(h){throw new Error(`Failed to capture device UI hierarchy: ${h instanceof Error?h.message:String(h)}`)}let n=be(s),i=Array.isArray(n)?n:n.flat||n.root||[],r=i.filter(h=>h.packageName===e||!h.packageName?.includes("systemui")&&!h.packageName?.includes("launcher")),o=r.length>0?r:i,l=zn(o,e);if(a.journey){let h=Oe(o),m=at(o),b=h[0]||m[0];if(b&&b.bounds&&b.bounds.centerX&&b.bounds.centerY){let T=mt(b);try{if(typeof t.tap=="function"){await t.tap(b.bounds.centerX,b.bounds.centerY,a.deviceSerial),await new Promise(O=>setTimeout(O,1500));let w=await t.getUiHierarchy(a.deviceSerial),S=be(w),y=Array.isArray(S)?S:S.flat||S.root||[],P=y.filter(O=>O.packageName===e||!O.packageName?.includes("systemui")&&!O.packageName?.includes("launcher")),N=P.length>0?P:y;if(N.length>0){let L=zn(N,e).split(`
884
+ `).filter(U=>!U.startsWith("Launch ")&&!U.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 ===",L.join(`
885
+ `).trim(),""].join(`
886
+ `)}}}catch(w){console.warn(`Journey transition failed, falling back to single-screen scaffold: ${w.message}`)}}}let c=oi(l);c.valid||console.warn(`Scaffolding produced warnings:
887
+ ${c.errors.join(`
888
+ `)}`);let d=a.outputPath;if(!d){let h=ii.resolve(process.cwd(),"specs");Kn.existsSync(h)||Kn.mkdirSync(h,{recursive:!0});let m=e.replace(/[^a-zA-Z0-9_.-]/g,"_");d=ii.join(h,`${m}_scaffold.txt`)}Kn.writeFileSync(d,l,"utf-8");let p=Fe(o),u=Oe(o);return{packageName:e,specContent:l,specPath:d,stepsCount:l.split(`
889
+ `).filter(h=>h.trim()&&!h.trim().startsWith("#")).length,inputCount:p.length,buttonCount:u.length}}var Xn=ie(()=>{"use strict";Re();Ve();et()});import{execFile as Wt}from"child_process";var Rs,Qn=ie(()=>{"use strict";Rs=class{defaultUdid;wdaBaseUrl;isMock;defaultDimensions={width:1170,height:2532};constructor(t={}){typeof t=="string"?(this.defaultUdid=t,this.isMock=!1):(this.defaultUdid=t.udid,this.wdaBaseUrl=t.wdaBaseUrl,this.isMock=t.useMock??!1),process.platform!=="darwin"&&!this.wdaBaseUrl&&this.isMock===void 0&&(this.isMock=!0)}getDefaultDevice(){return this.defaultUdid}async ensureDefaultDevice(t){if(t)return t;if(this.defaultUdid)return this.defaultUdid;let s=(await this.getConnectedDevices()).find(i=>i.state==="simulator"||i.state==="device");if(s)return this.defaultUdid=s.serial,s.serial;let n="booted-simulator";return this.defaultUdid=n,n}async getConnectedDevices(){return this.isMock||process.platform!=="darwin"?[{serial:this.defaultUdid||"00008101-001234567890001E",state:"simulator",model:"iPhone 15 Pro (Simulator)",platform:"ios"}]:new Promise(t=>{Wt("xcrun",["simctl","list","devices","available","--json"],(e,s)=>{if(e||!s){t([{serial:"booted-ios-simulator",state:"simulator",model:"iPhone Simulator",platform:"ios"}]);return}try{let n=JSON.parse(s),i=[],r=n.devices||{};for(let o of Object.keys(r)){let l=r[o];if(Array.isArray(l))for(let c of l)c.isAvailable&&c.state==="Booted"&&i.push({serial:c.udid,state:"simulator",model:`${c.name} (${o.split(".").pop()||"iOS"})`,platform:"ios"})}t(i.length>0?i:[{serial:"booted-ios-simulator",state:"simulator",model:"iPhone Simulator",platform:"ios"}])}catch{t([])}})})}async getScreenDimensions(t){return this.isMock||process.platform!=="darwin"?this.defaultDimensions:this.defaultDimensions}async captureScreenshot(t){let e=await this.ensureDefaultDevice(t);return this.isMock||process.platform!=="darwin"?Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==","base64"):new Promise((s,n)=>{Wt("xcrun",["simctl","io",e,"screenshot","--type=png","-"],{encoding:"buffer",maxBuffer:10*1024*1024},(i,r)=>{if(i)return n(i);s(r)})})}async getUiHierarchy(t){let e=`<?xml version="1.0" encoding="UTF-8"?>
890
+ <hierarchy rotation="0">
891
+ <XCUIElementTypeApplication type="XCUIElementTypeApplication" name="TargetApp" label="TargetApp" enabled="true" visible="true" x="0" y="0" width="390" height="844">
892
+ <XCUIElementTypeWindow type="XCUIElementTypeWindow" enabled="true" visible="true" x="0" y="0" width="390" height="844">
893
+ <XCUIElementTypeNavigationBar type="XCUIElementTypeNavigationBar" identifier="NavBar" name="Main Title" label="Main Title" enabled="true" visible="true" x="0" y="47" width="390" height="44" />
894
+ <XCUIElementTypeButton type="XCUIElementTypeButton" identifier="submit_btn" name="Submit" label="Submit" enabled="true" visible="true" x="40" y="200" width="310" height="50" />
895
+ <XCUIElementTypeTextField type="XCUIElementTypeTextField" identifier="user_field" name="Username" label="Username" enabled="true" visible="true" x="40" y="120" width="310" height="44" />
896
+ </XCUIElementTypeWindow>
897
+ </XCUIElementTypeApplication>
898
+ </hierarchy>`;if(this.isMock||process.platform!=="darwin")return e;if(this.wdaBaseUrl)try{let s=await fetch(`${this.wdaBaseUrl}/source?format=xml`);if(s.ok)return await s.text()}catch{}return e}async tap(t,e,s){let n=await this.ensureDefaultDevice(s);if(!(this.isMock||process.platform!=="darwin")){if(this.wdaBaseUrl){await fetch(`${this.wdaBaseUrl}/wda/tap/nil`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({x:t,y:e})}).catch(()=>{});return}await new Promise(i=>{Wt("idb",["ui","tap",`${t}`,`${e}`,"--udid",n],()=>i())})}}async inputText(t,e){let s=await this.ensureDefaultDevice(e);if(!(this.isMock||process.platform!=="darwin")){if(this.wdaBaseUrl){await fetch(`${this.wdaBaseUrl}/wda/keys`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:t.split("")})}).catch(()=>{});return}await new Promise(n=>{Wt("idb",["ui","type",t,"--udid",s],()=>n())})}}async pressKey(t,e){this.isMock||process.platform!=="darwin"||(await this.ensureDefaultDevice(e),this.wdaBaseUrl&&await fetch(`${this.wdaBaseUrl}/wda/keys`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:[t]})}).catch(()=>{}))}async swipe(t,e,s,n,i=300,r){this.isMock||process.platform!=="darwin"||(await this.ensureDefaultDevice(r),this.wdaBaseUrl&&await fetch(`${this.wdaBaseUrl}/wda/dragfromtoforduration`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({fromX:t,fromY:e,toX:s,toY:n,duration:i/1e3})}).catch(()=>{}))}async launchApp(t,e){let s=await this.ensureDefaultDevice(e);if(!(this.isMock||process.platform!=="darwin"))return new Promise(n=>{Wt("xcrun",["simctl","launch",s,t],()=>n())})}async forceStop(t,e){let s=await this.ensureDefaultDevice(e);if(!(this.isMock||process.platform!=="darwin"))return new Promise(n=>{Wt("xcrun",["simctl","terminate",s,t],()=>n())})}async isAppInstalled(t,e){let s=await this.ensureDefaultDevice(e);return this.isMock||process.platform!=="darwin"?!0:new Promise(n=>{Wt("xcrun",["simctl","get_app_container",s,t],i=>{n(!i)})})}async getForegroundPackage(t){return"com.apple.mobilesafari"}}});var li={};Qe(li,{startLiveServer:()=>Zn});import $o from"http";import te from"fs";import ee from"path";import ci from"os";import{fileURLToPath as Co}from"url";async function Zn(a={}){let t=a.port??4040,e=a.host||"127.0.0.1",s=process.cwd();(s==="/"||s==="\\")&&(s=ci.homedir?ci.homedir():process.env.HOME||process.env.USERPROFILE||"/tmp");let n=a.outputDir?ee.resolve(a.outputDir):ee.resolve(s,"output"),i=a.suiteName||"PromptTest Live Monitor",r=a.initialReport||{packageName:"PromptTest Execution",deviceId:"Live Device",startTime:new Date().toISOString(),endTime:new Date().toISOString(),durationSeconds:0,totalSteps:0,passedSteps:0,failedSteps:0,skippedSteps:0,warnSteps:0,errorsDetected:[],steps:[]},o=new Set,l=setInterval(()=>{for(let x of o)try{x.write(`: heartbeat
899
+
900
+ `)}catch{o.delete(x)}},15e3),c=a.driver||new lt,d=ee.dirname(Co(import.meta.url)),p=a.specsDir?ee.resolve(a.specsDir):ee.resolve(process.cwd(),"specs");if(!te.existsSync(p)){let x=ee.resolve(d,"../specs");te.existsSync(x)&&(p=x)}let u=null;function h(){return u||(u=new st(c)),u}let m=ee.join(n,"baselines"),b=new Ut(m),T=null,w=null,S=["strict","moderate","interactive","disabled"],y=null,P=0;function N(x){return x?{success:!0,sessionId:x.id,status:x.status,packageName:x.packageName,deviceId:x.deviceId,safetyMode:x.safetyMode,maxDepth:x.maxDepth,maxSteps:x.maxSteps,maxScreens:x.maxScreens,screensDiscovered:x.screensDiscovered,elementsMapped:x.elementsMapped,stepsTaken:x.stepsTaken,startedAt:x.startedAt,finishedAt:x.finishedAt,crashes:x.crashes,screens:x.screens,blocked:x.blocked,events:x.events,specLines:x.specLines,error:x.error}:{success:!0,sessionId:null,status:"idle",packageName:"",safetyMode:"strict",screensDiscovered:0,elementsMapped:0,stepsTaken:0,maxDepth:0,maxSteps:0,crashes:[],screens:[],blocked:[],events:[],specLines:[]}}function O(x){let f=x.match(/"([^"]+)"/);return f?f[1]:null}function L(x){let f=x.specLines.length>0?x.specLines:["Launch the app"],F=["# Auto-generated by PromptTest Auto-Pilot (Autonomous Explorer)",`# Package: ${x.packageName}`,`# Safety Policy: ${x.safetyMode.toUpperCase()} | Max Depth: ${x.maxDepth} | Step Budget: ${x.maxSteps}`,`# Explored at: ${x.startedAt}`,`# Screens Discovered: ${x.screensDiscovered} | Steps Executed: ${x.stepsTaken}`];return x.deviceId&&F.push(`# Device: ${x.deviceId}`),[...F,"",...f,""].join(`
901
+ `)}function U(x,f,F){x.status=f,x.finishedAt=new Date().toISOString(),F&&(x.error=F),K("explore",{...N(x),event:{type:f==="failed"?"error":"done",description:F||f}})}function j(x){let f=y;if(f){if(typeof x.totalSteps=="number"&&(f.stepsTaken=Math.max(f.stepsTaken,x.totalSteps)),typeof x.discoveredScreens=="number"&&(f.screensDiscovered=Math.max(f.screensDiscovered,x.discoveredScreens)),x.type==="action"){let F=O(x.description);F&&f.elementLabels.add(F),x.description&&f.specLines[f.specLines.length-1]!==x.description&&f.specLines.push(x.description)}if(x.screenHash){let F=x.screenTitle||x.description||x.screenHash.slice(0,8),E=f.screens.find(ge=>ge.hash===x.screenHash);E?(E.visits++,typeof x.elementCount=="number"&&(E.elements=x.elementCount),E.label===E.hash.slice(0,8)&&x.screenTitle&&(E.label=x.screenTitle,E.title=x.screenTitle)):f.screens.push({id:x.screenHash,hash:x.screenHash,depth:x.depth??f.screens.length,title:F,label:F,visits:1,elements:x.elementCount??0}),f.screensDiscovered=Math.max(f.screensDiscovered,f.screens.length)}if(x.type==="crash"){let F=new Date().toISOString(),E=x.screenHash,ge=E?f.screens.find(Y=>Y.hash===E):void 0;f.crashes.push({id:E||`crash-${f.crashes.length+1}`,type:x.description.toLowerCase().includes("anr")?"anr":"crash",message:x.description,screen:ge?.label||E?.slice(0,8),timestamp:F,description:x.description,screenHash:E})}x.type==="safety_block"&&f.blocked.push(x.description),f.elementsMapped=f.elementLabels.size,f.events.push(x),f.events.length>300&&f.events.shift(),K("explore",{...N(f),event:x})}}function Q(x){let f=S.includes(x.safetyMode||"")?x.safetyMode:"strict";P++;let F={id:`explore-${Date.now()}-${P}`,packageName:x.packageName,deviceId:x.deviceId,safetyMode:f,maxDepth:x.maxDepth??5,maxSteps:x.maxSteps??100,maxScreens:x.maxScreens??30,status:"running",startedAt:new Date().toISOString(),screensDiscovered:0,elementsMapped:0,stepsTaken:0,screens:[],elementLabels:new Set,crashes:[],blocked:[],events:[],specLines:[],abort:{aborted:!1}};y=F;let E=new Ht(c);return K("explore",{...N(F),event:{type:"state_change",description:`Auto-Pilot launched on ${F.packageName} (${F.safetyMode} mode)`}}),E.explore({packageName:F.packageName,serial:F.deviceId,strategy:"dfs",safetyMode:F.safetyMode,maxDepth:F.maxDepth,maxScreens:F.maxScreens,stepBudget:F.maxSteps,screenshots:"state-change",onStep:j,abortSignal:F.abort}).then(()=>{U(F,F.abort.aborted?"stopped":"completed")}).catch(ge=>{U(F,F.abort.aborted?"stopped":"failed",ge instanceof Error?ge.message:String(ge))}),F}function K(x,f){let F=`event: ${x}
902
+ data: ${JSON.stringify(f)}
903
+
904
+ `;for(let E of o)try{E.write(F)}catch{o.delete(E)}}function V(x){return new Promise((f,F)=>{let E="";x.on("data",ge=>{E+=ge}),x.on("end",()=>{try{f(E?JSON.parse(E):{})}catch{F(new Error("Invalid JSON payload"))}}),x.on("error",F)})}let ye=$o.createServer(async(x,f)=>{if(f.setHeader("Access-Control-Allow-Origin","*"),f.setHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS"),f.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization"),x.method==="OPTIONS"){f.writeHead(204),f.end();return}let F=new URL(x.url||"/",`http://${e}:${t}`),E=decodeURIComponent(F.pathname);if(E==="/api/devices"){try{let[g,k]=await Promise.all([c.getRawConnectedDevices().catch(()=>[]),new Rs().getConnectedDevices().catch(()=>[])]),M=c.deduplicateDevices(g).map(A=>({...A,platform:"android"})),v=(k||[]).map(A=>({serial:A.serial,state:A.state,model:A.model||"iOS Device",product:"iOS",connectionType:A.state==="simulator"?"emulator":"usb",platform:"ios"})),C=[...M,...v];f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,devices:C,allEndpoints:g}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/screen-state"){try{let g=F.searchParams.get("deviceId")||void 0,k=F.searchParams.get("hierarchy")!=="false",[M,v,C]=await Promise.all([c.captureScreenshot(g),k?c.getUiHierarchy(g):Promise.resolve(null),c.getForegroundPackage(g)]),A=[];if(v){let{flat:D}=be(v);A=D.map(I=>({index:I.index,text:I.text,resourceId:I.resourceId,className:I.className,contentDesc:I.contentDesc,clickable:I.clickable,scrollable:I.scrollable,bounds:I.bounds}))}f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,deviceId:g||c.getDefaultDevice()||"default",foregroundApp:C,screenshot:`data:image/png;base64,${M.toString("base64")}`,elements:k?A:void 0,elementCount:A.length}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/tap"&&x.method==="POST"){try{let g=await V(x);if(typeof g.x!="number"||typeof g.y!="number"){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing numeric coordinates: x and y"}));return}await c.tap(g.x,g.y,g.deviceId),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,tapped:{x:g.x,y:g.y}}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/type"&&x.method==="POST"){try{let g=await V(x);if(typeof g.text!="string"){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing text string to type"}));return}await c.inputText(g.text,g.deviceId),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,typed:g.text}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/key"&&x.method==="POST"){try{let g=await V(x);if(g.keyCode===void 0){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing keyCode parameter"}));return}await c.pressKey(g.keyCode,g.deviceId),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,keyCode:g.keyCode}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/wake"&&x.method==="POST"){try{let g=await V(x);await c.wakeDevice(g.deviceId),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/wifi/connect"&&x.method==="POST"){try{let g=await V(x),k=await c.connectWifi(g.target,g.port||5555,g.deviceId);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:k.success,target:k.target,message:k.message}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/wifi/disconnect"&&x.method==="POST"){try{let g=await V(x),k=await c.disconnectWifi(g.target);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:k}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/wifi/pair"&&x.method==="POST"){try{let g=await V(x);if(!g.target||!g.pairingCode){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing target or pairingCode"}));return}let k=await c.pairWifi(g.target,g.pairingCode);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify(k))}catch(g){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/devices/install-apk"&&x.method==="POST"){try{let g=F.searchParams,k=g.get("deviceId")||void 0,M=g.get("targetDevices"),v=M?M.split(",").filter(Boolean):k?[k]:[],C=g.get("fileName")||`app_${Date.now()}.apk`,A=ee.basename(C).endsWith(".apk")?ee.basename(C):`${ee.basename(C)}.apk`,D=ee.join(n,"_apk_temp");te.existsSync(D)||te.mkdirSync(D,{recursive:!0});let I=ee.join(D,`${Date.now()}_${A}`),H=te.createWriteStream(I);await new Promise((q,Se)=>{x.pipe(H),x.on("error",Se),H.on("error",Se),H.on("finish",q)});let z=v;if(z.length===0){let q=c.getDefaultDevice();q&&(z=[q])}if(z.length===0&&(z=(await c.getConnectedDevices()).filter(Se=>Se.state==="device").map(Se=>Se.serial)),z.length===0){try{await te.promises.unlink(I)}catch{}f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"No active target device found to install APK"}));return}let G=await Promise.allSettled(z.map(q=>c.installApk(I,q)));try{await te.promises.unlink(I)}catch{}let Z=G.filter(q=>q.status==="fulfilled").length,X=[];G.forEach((q,Se)=>{if(q.status==="rejected"){let Pe=q.reason instanceof Error?q.reason.message:String(q.reason);X.push(`${z[Se]}: ${Pe}`)}}),Z>0?(K("apk-installed",{fileName:A,installedCount:Z,targets:z}),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:`Successfully installed "${A}" on ${Z} device(s)${X.length>0?` (Failed: ${X.join(", ")})`:""}`,installedCount:Z}))):(f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Failed to install APK: ${X.join("; ")}`})))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/metrics"&&x.method==="GET"){try{let g=F.searchParams.get("deviceId")||void 0,k=await c.getDeviceMetrics(g);if(Object.keys(k).length===0)throw new Error(`No metrics available for device ${g||"default"}. Confirm the device is connected and authorized.`);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,metrics:k}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/doctor"&&x.method==="GET"){try{let g=F.searchParams.get("deviceId")||void 0,k=F.searchParams.get("packageName")||void 0,M=await gs(c,{serial:g,packageName:k});f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,report:M}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((E==="/api/explore/start"||E==="/api/explore")&&x.method==="POST"){try{let g=await V(x),k=(g.packageName||g.targetPackage||"").trim();if(!k){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing packageName (packageName or targetPackage)"}));return}if(y&&y.status==="running"){f.writeHead(409,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"An Auto-Pilot exploration session is already active",sessionId:y.id}));return}let M=Q({packageName:k,deviceId:g.deviceId||g.serial,safetyMode:g.safetyMode||g.safety,maxDepth:g.maxDepth??g.depth,maxSteps:g.maxSteps??g.stepBudget,maxScreens:g.maxScreens});f.writeHead(202,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,sessionId:M.id,status:M.status,packageName:M.packageName}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((E==="/api/explore/status"||E==="/api/explore")&&x.method==="GET"){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify(N(y)));return}if((E==="/api/explore/stop"||E==="/api/explore")&&(x.method==="POST"||x.method==="DELETE")){if(!y){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"No active Auto-Pilot session to stop"}));return}y.abort.aborted=!0,f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,sessionId:y.id,status:"stopping",packageName:y.packageName}));return}if(E==="/api/explore/export"&&x.method==="POST"){try{let g=await V(x);if(!y){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"No exploration session available to export"}));return}let k=(g.specName||`explored_${y.packageName}.txt`).trim(),M=ee.basename(k).replace(/[^a-zA-Z0-9._-]/g,"_"),v=M.endsWith(".txt")?M:`${M}.txt`,C=L(y);await te.promises.mkdir(p,{recursive:!0}),await te.promises.writeFile(ee.join(p,v),C,"utf8"),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,specName:v,content:C,screensDiscovered:y.screensDiscovered,stepsTaken:y.stepsTaken}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/record/start"&&x.method==="POST"){try{let g=await V(x);if(T){f.writeHead(409,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"A recording session is already active"}));return}let k=g.deviceId||c.getDefaultDevice()||void 0,M=g.specName?ee.join(p,ee.basename(g.specName)):void 0;T=new At(c),w=k||"default",T.onStep=v=>{K("record-step",{step:v,deviceId:k})},await T.startSession(M,k,{append:g.append}),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:"Recording session started",deviceId:k}))}catch(g){if(T)try{await T.stopSession()}catch{}T=null,w=null,f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/record/stop"&&x.method==="POST"){try{if(!T){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:"No active recording session",steps:[]}));return}let g=await T.stopSession();T=null,w=null,f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,steps:g.steps,specPath:g.specPath,reportPath:g.reportPath}))}catch(g){T=null,w=null,f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/record/status"&&x.method==="GET"){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,isRecording:T!==null,deviceId:w}));return}if(E==="/api/specs"&&x.method==="GET"){try{te.existsSync(p)||te.mkdirSync(p,{recursive:!0});let k=te.readdirSync(p).filter(M=>M.endsWith(".txt")||M.endsWith(".spec")).map(M=>{let v=ee.join(p,M),C=te.statSync(v);return{name:M,path:ee.relative(process.cwd(),v).replace(/\\/g,"/"),size:C.size,modified:C.mtime.toISOString()}});f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,specs:k}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((E.startsWith("/api/specs/")||E==="/api/spec")&&x.method==="GET"){try{let g="";E.startsWith("/api/specs/")?g=decodeURIComponent(E.slice(11)):g=F.searchParams.get("name")||"";let k=ee.basename(g.trim());if(!k){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing spec file name"}));return}let M=ee.join(p,k);if(!te.existsSync(M)){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Spec file not found: ${k}`}));return}let v=te.readFileSync(M,"utf-8");f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,name:k,content:v}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((E==="/api/specs"||E==="/api/spec")&&x.method==="POST"){try{let g=await V(x),k=(g.name||"").trim();if(!k){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing spec file name"}));return}!k.endsWith(".txt")&&!k.endsWith(".spec")&&(k+=".txt");let M=ee.basename(k);te.existsSync(p)||te.mkdirSync(p,{recursive:!0});let v=ee.join(p,M);te.writeFileSync(v,g.content??"","utf-8"),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,name:M,path:v}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/specs/apply-heals"&&x.method==="POST"){try{let g=await V(x);if(!g.specName||!Array.isArray(g.heals)||g.heals.length===0){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName or heals array"}));return}let k=ee.basename(g.specName),M=ee.join(p,k);if(!te.existsSync(M)){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Spec file not found: ${k}`}));return}let v=te.readFileSync(M,"utf-8"),C=0;for(let A of g.heals){if(!A.original||!A.healed||A.original===A.healed)continue;let D=A.original.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),I=new RegExp(`"${D}"`,"g"),H=new RegExp(`'${D}'`,"g");(I.test(v)||H.test(v))&&(v=v.replace(I,`"${A.healed}"`),v=v.replace(H,`'${A.healed}'`),C++)}te.writeFileSync(M,v,"utf-8"),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,specName:k,replacementsCount:C,content:v}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/logcat"&&x.method==="GET"){try{let g=(F.searchParams.get("deviceId")||c.getDefaultDevice())??void 0,k=parseInt(F.searchParams.get("lines")||"200",10),M=isNaN(k)?200:Math.min(Math.max(k,20),1e3),v=F.searchParams.get("filter")?.trim(),A=(await c.getLogcatTail(M,g)).split(`
905
+ `).filter(D=>D.trim().length>0);if(v){let D=v.toLowerCase();A=A.filter(I=>I.toLowerCase().includes(D))}f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,count:A.length,logs:A}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/logcat/clear"&&x.method==="POST"){try{let g=(F.searchParams.get("deviceId")||c.getDefaultDevice())??void 0;await c.execAdb(["logcat","-c"],g).catch(()=>{}),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/scaffold"&&x.method==="POST"){try{let g=await V(x),k=g.deviceId||c.getDefaultDevice()||void 0,M=g.packageName?.trim()||void 0,v=(g.specName||"").trim();v||(v=M?`scaffold_${M}.txt`:"scaffold_app.txt"),!v.endsWith(".txt")&&!v.endsWith(".spec")&&(v+=".txt");let C=ee.basename(v).replace(/[^a-zA-Z0-9._-]/g,"_"),A=ee.join(p,C),D=await Gn({driver:c,deviceSerial:k,packageName:M,outputPath:A,journey:g.journey});f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,specName:C,content:D.specContent,packageName:D.packageName,inputCount:D.inputCount,buttonCount:D.buttonCount,stepsCount:D.stepsCount}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/expand"&&x.method==="POST"){try{let g=await V(x);if(!g.spec||typeof g.spec!="string"){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:'Missing "spec" string to expand'}));return}let k=h(),{expanded:M,stepsAdded:v}=await k.expandSpec(g.spec);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,expanded:M,stepsAdded:v}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/action"&&x.method==="POST"){try{let g=await V(x);if(!g.step||typeof g.step!="string"){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing natural language step string"}));return}let k=St(g.step);if(k.length===0){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Could not parse step: "${g.step}"`}));return}let M=h(),v=k[0],C=g.deviceId||c.getDefaultDevice()||void 0,A=Date.now(),D=await M.executeStep(v,C),I=Date.now()-A;K("step",{stepNumber:r.steps.length+1,instruction:v.originalText,status:"PASS",durationMs:I}),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,step:v.originalText,type:v.type,durationMs:I,result:D||null}))}catch(g){let k=g instanceof Error?g.message:String(g);K("step",{stepNumber:r.steps.length+1,instruction:"Step Execution",status:"FAIL",durationMs:0,error:k}),f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:k}))}return}if(E==="/api/run"&&x.method==="POST"){try{let g=await V(x),k="";if(g.specName){let A=ee.basename(g.specName);if(k=ee.join(p,A),g.spec&&typeof g.spec=="string"&&g.spec.trim().length>0)te.writeFileSync(k,g.spec,"utf-8");else if(!te.existsSync(k)){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Spec file not found: ${A}`}));return}}else if(g.spec&&typeof g.spec=="string")te.existsSync(n)||te.mkdirSync(n,{recursive:!0}),k=ee.join(n,"_studio_temp_spec.txt"),te.writeFileSync(k,g.spec,"utf-8");else{f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:'Either "spec" string or "specName" must be provided'}));return}let M=g.dataFile;if(g.dataFileContent&&typeof g.dataFileContent=="string"){te.existsSync(n)||te.mkdirSync(n,{recursive:!0});let A=g.dataFile?ee.basename(g.dataFile):"_studio_data.csv";M=ee.join(n,A),te.writeFileSync(M,g.dataFileContent,"utf-8")}let v=g.deviceId||c.getDefaultDevice()||void 0,C=h();r.steps=[],r.totalSteps=0,r.passedSteps=0,r.failedSteps=0,r.warnSteps=0,r.skippedSteps=0,(async()=>{let A;try{let I=await c.getForegroundPackage(v);I&&!I.includes("Launcher")&&!I.includes("Foreground")&&(A=I)}catch{}let D=async(I,H)=>{let z=H?xt:new ut(n),G=H?C:new st(c,void 0,z),Z=z.onStep?.(X=>{let q=!!(X.details?.healed||X.details?.healedFrom||X.description?.toLowerCase().includes("healed")),Se=X.details;K("step",{deviceId:I,stepNumber:X.stepIndex,instruction:X.description,status:X.status,durationMs:X.durationMs||0,error:X.details?.error,healed:q,healedFrom:Se?.healedFrom,healedTo:Se?.healedTo})});try{let X=await G.runSpec(k,{serial:I,packageName:A,saveBaseline:H&&g.saveBaseline!==void 0?g.saveBaseline:!1,compareBaseline:H&&g.compareBaseline!==void 0?g.compareBaseline:!0,screenshotMode:H?"all":"on-failure",heal:g.heal??!1,fresh:g.fresh??!1,continueOnError:g.continueOnError??!1,tags:g.tags,only:g.only,dataFile:M,iterations:g.iterations,profile:g.profile??!1});return K("device-complete",{deviceId:I,report:X}),X}finally{Z&&Z()}};try{let I=Array.isArray(g.targetDevices)&&g.targetDevices.length>0;if(I||g.allDevices){let H=[];I?H=g.targetDevices:H=(await c.getConnectedDevices()).filter(X=>X.state==="device").map(X=>X.serial),H=Array.from(new Set(H.filter(Boolean))),v&&H.includes(v)?H=[v,...H.filter(Z=>Z!==v)]:H.length===0&&v&&H.push(v);let z=await Promise.allSettled(H.map((Z,X)=>D(Z,X===0))),G=[];for(let Z of z)Z.status==="fulfilled"&&G.push(Z.value);G.length>0&&(r=G[0]),K("complete",{multiDevice:!0,reports:G,currentReport:r})}else{let H=await D(v||"default",!0);r=H,K("complete",H)}}catch(I){let H=I instanceof Error?I.message:String(I);r.errorsDetected.push(H),K("error",{error:H}),K("complete",r)}})(),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,message:"Execution started"}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/reports"&&x.method==="GET"){try{if(!te.existsSync(n)){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,reports:[]}));return}let M=te.readdirSync(n).filter(v=>v.endsWith("-results.json")).map(v=>{let C=v.replace(/-results\.json$/,""),A=ee.join(n,v),D=`${C}-report.html`,I=te.existsSync(ee.join(n,D)),H=te.statSync(A);try{let z=JSON.parse(te.readFileSync(A,"utf-8")),G=z.totalSteps||z.steps?.length||0,Z=z.passedSteps||0,X=z.failedSteps||0,q=G>0?Math.round(Z/G*100):100;return{id:C,fileName:v,suiteName:z.packageName||C,packageName:z.packageName||"Unknown",deviceId:z.deviceId||"Unknown",startTime:z.startTime||H.mtime.toISOString(),endTime:z.endTime||H.mtime.toISOString(),durationSeconds:z.durationSeconds||0,totalSteps:G,passedSteps:Z,failedSteps:X,warnSteps:z.warnSteps||0,skippedSteps:z.skippedSteps||0,passRate:q,errorsCount:z.errorsDetected?.length||0,htmlReportPath:I?`/${D}`:null,jsonReportPath:`/${v}`}}catch{return null}}).filter(Boolean).sort((v,C)=>new Date(C.startTime).getTime()-new Date(v.startTime).getTime());f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,reports:M}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if((E.startsWith("/api/reports/")||E==="/api/report-detail")&&x.method==="GET"){try{let g="";E.startsWith("/api/reports/")?g=decodeURIComponent(E.slice(13)):g=F.searchParams.get("id")||"";let k=ee.basename(g.trim()),M=k.endsWith("-results.json")?k:`${k}-results.json`,v=ee.join(n,M);if(!te.existsSync(v)){f.writeHead(404,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:`Report not found: ${k}`}));return}let C=JSON.parse(te.readFileSync(v,"utf-8"));f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,id:k,report:C}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines"&&x.method==="GET"){try{te.existsSync(m)||te.mkdirSync(m,{recursive:!0});let g=F.searchParams.get("spec")||"",k=g.replace(/[^a-zA-Z0-9._-]/g,"_"),M=te.readdirSync(m),v=new Map;for(let A of M){if(k&&!A.startsWith(`${k}_step`))continue;let D=A.match(/_step(\d+)(_current|_diff)?\.png$/);if(!D)continue;let I=parseInt(D[1],10),H=D[2];v.has(I)||v.set(I,{});let z=v.get(I);H?H==="_current"?z.current=`/baselines/${A}`:H==="_diff"&&(z.diff=`/baselines/${A}`):z.baseline=`/baselines/${A}`}let C=await Promise.all(Array.from(v.entries()).map(async([A,D])=>{let I=await b.getMasks(g,A);return{stepIndex:A,...D,masks:I}}));C.sort((A,D)=>A.stepIndex-D.stepIndex),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,spec:g,baselines:C,matchingModes:ln}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines/compare"&&x.method==="POST"){try{let g=await V(x);if(!g.specName||typeof g.stepIndex!="number"){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName or stepIndex"}));return}let k;if(g.screenshot){let C=g.screenshot.replace(/^data:image\/\w+;base64,/,"");k=Buffer.from(C,"base64")}else{let C=g.deviceId||c.getDefaultDevice()||void 0;k=await c.captureScreenshot(C)}let M=await b.compare(g.specName,g.stepIndex,k,{threshold:g.threshold,mode:g.mode,maskRegions:g.maskRegions}),v=await b.getMasks(g.specName,g.stepIndex);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,result:{...M,masks:v,baselineUrl:`/baselines/${ee.basename(M.baselineFile)}`,currentUrl:`/baselines/${ee.basename(M.currentFile)}`,diffUrl:M.diffImagePath?`/baselines/${ee.basename(M.diffImagePath)}`:null}}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines/approve"&&x.method==="POST"){try{let g=await V(x);if(!g.specName||typeof g.stepIndex!="number"){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName or stepIndex"}));return}let k;if(g.screenshot){let v=g.screenshot.replace(/^data:image\/\w+;base64,/,"");k=Buffer.from(v,"base64")}else{let v=g.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),C=ee.join(m,`${v}_step${String(g.stepIndex).padStart(3,"0")}_current.png`);if(!te.existsSync(C)){let A=g.deviceId||c.getDefaultDevice()||void 0;try{k=await c.captureScreenshot(A)}catch{}}}let M=await b.approveBaseline(g.specName,g.stepIndex,k);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,approved:!0,stepIndex:g.stepIndex,baselineUrl:`/baselines/${ee.basename(M)}`}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines/clear"&&x.method==="POST"){try{let g=await V(x);if(!g.specName){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName"}));return}await b.clearBaselines(g.specName),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,cleared:!0,specName:g.specName}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines/delete-step"&&x.method==="POST"){try{let g=await V(x);if(!g.specName||typeof g.stepIndex!="number"){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName or stepIndex"}));return}await b.deleteStepBaseline(g.specName,g.stepIndex),f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,deleted:!0,specName:g.specName,stepIndex:g.stepIndex}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines/upload"&&x.method==="POST"){try{let g=await V(x);if(!g.specName||typeof g.stepIndex!="number"||!g.image){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName, stepIndex, or image data"}));return}let k=g.image.replace(/^data:image\/\w+;base64,/,""),M=Buffer.from(k,"base64"),v=await b.saveBaseline(g.specName,g.stepIndex,M),C=g.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),A=ee.join(m,`${C}_step${String(g.stepIndex).padStart(3,"0")}_diff.png`);if(te.existsSync(A))try{await te.promises.unlink(A)}catch{}let D=ee.join(m,`${C}_step${String(g.stepIndex).padStart(3,"0")}_current.png`),I,H;if(te.existsSync(D)){let G=await te.promises.readFile(D),Z=await b.compare(g.specName,g.stepIndex,G);I=`/baselines/${ee.basename(D)}`,H=Z.diffImagePath?`/baselines/${ee.basename(Z.diffImagePath)}`:void 0}else if(g.deviceId||c.getDefaultDevice()){let G=g.deviceId||c.getDefaultDevice()||void 0;try{let Z=await c.captureScreenshot(G),X=await b.compare(g.specName,g.stepIndex,Z);I=`/baselines/${ee.basename(D)}`,H=X.diffImagePath?`/baselines/${ee.basename(X.diffImagePath)}`:void 0}catch{}}let z=await b.getMasks(g.specName,g.stepIndex);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,uploaded:!0,stepIndex:g.stepIndex,baselineUrl:`/baselines/${ee.basename(v)}`,currentUrl:I,diffUrl:H,masks:z}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines/masks"&&x.method==="GET"){try{let g=F.searchParams.get("specName")||F.searchParams.get("spec")||"",k=parseInt(F.searchParams.get("stepIndex")||"",10);if(!g||isNaN(k)){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName or valid stepIndex"}));return}let M=await b.getMasks(g,k);f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,masks:M}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/api/baselines/masks"&&x.method==="POST"){try{let g=await V(x);if(!g.specName||typeof g.stepIndex!="number"||!Array.isArray(g.masks)){f.writeHead(400,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:"Missing specName, stepIndex, or masks array"}));return}await b.saveMasks(g.specName,g.stepIndex,g.masks);let k=g.specName.replace(/[^a-zA-Z0-9._-]/g,"_"),M=ee.join(m,`${k}_step${String(g.stepIndex).padStart(3,"0")}_current.png`),v,C,A=0,D=0,I=!0;if(te.existsSync(M)){let H=await te.promises.readFile(M),z=await b.compare(g.specName,g.stepIndex,H,{mode:g.mode,threshold:g.threshold,maskRegions:g.masks});v=`/baselines/${ee.basename(M)}`,C=z.diffImagePath?`/baselines/${ee.basename(z.diffImagePath)}`:void 0,A=z.diffRatio,D=z.diffPixels,I=z.passed}f.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!0,masks:g.masks,currentUrl:v,diffUrl:C,diffRatio:A,diffPixels:D,passed:I}))}catch(g){f.writeHead(500,{"Content-Type":"application/json; charset=utf-8"}),f.end(JSON.stringify({success:!1,error:g instanceof Error?g.message:String(g)}))}return}if(E==="/events"){f.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive","Access-Control-Allow-Origin":"*"}),f.write(`event: connected
856
906
  data: ${JSON.stringify({status:"connected",time:Date.now()})}
857
907
 
858
- `),i.add(b),w.on("close",()=>{i.delete(b)});return}if(P==="/"||P==="/report"||P==="/index.html"){let g=Os(r,{suiteName:n});b.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-cache"}),b.end(g);return}if(P==="/api/report"||P==="/results.json"){b.writeHead(200,{"Content-Type":"application/json; charset=utf-8","Access-Control-Allow-Origin":"*"}),b.end(JSON.stringify(r,null,2));return}let ce=P.replace(/^\/+/,""),ne=Q.join(s,ce);if(!ne.startsWith(s)){b.writeHead(403,{"Content-Type":"text/plain"}),b.end("Access Denied");return}if(te.existsSync(ne)&&te.statSync(ne).isFile()){let g=Q.extname(ne).toLowerCase(),D=vr[g]||"application/octet-stream";b.writeHead(200,{"Content-Type":D,"Cache-Control":"no-cache"}),te.createReadStream(ne).pipe(b);return}b.writeHead(404,{"Content-Type":"text/plain"}),b.end("Not Found")});await new Promise((w,b)=>{se.once("error",b),se.listen(t,e,()=>{w()})});let oe=se.address(),G=typeof oe=="object"&&oe?oe.port:t,H=`http://${e}:${G}`;return{server:se,port:G,url:H,broadcastStep(w){r.steps.push(w),r.totalSteps=r.steps.length,w.status==="PASS"?r.passedSteps++:w.status==="FAIL"?r.failedSteps++:w.status==="WARN"?r.warnSteps++:w.status==="SKIPPED"&&r.skippedSteps++,V("step",w)},broadcastComplete(w){r=w,V("complete",w)},broadcastError(w){r.errorsDetected.push(w),V("error",{error:w})},updateReport(w){r=w},async close(){if(clearInterval(o),f){try{await f.stopSession()}catch{}f=null,x=null}S&&S.status==="running"&&(S.abort.aborted=!0);for(let w of i)try{w.end()}catch{}i.clear(),await new Promise((w,b)=>{se.close(_=>{_?b(_):w()})})}}}var vr,xs=de(()=>{"use strict";mt();Fe();Ke();Ht();ut();os();Kt();cs();zt();vr={".html":"text/html; charset=utf-8",".json":"application/json; charset=utf-8",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".svg":"image/svg+xml",".mp4":"video/mp4",".log":"text/plain; charset=utf-8",".txt":"text/plain; charset=utf-8",".xml":"application/xml; charset=utf-8",".zip":"application/zip"}});import xr from"zlib";import Tr from"fs";function Er(c){let t=-1;for(let e=0;e<c.length;e++){let s=c[e];t^=s;for(let n=0;n<8;n++)t=t>>>1^-(t&1)&3988292384}return~t>>>0}function Pr(c=new Date){let t=(c.getHours()&31)<<11|(c.getMinutes()&63)<<5|Math.floor(c.getSeconds()/2)&31,e=(c.getFullYear()-1980&127)<<9|(c.getMonth()+1&15)<<5|c.getDate()&31;return{time:t,date:e}}function kr(c){let t=[],e=[],s=0;for(let a of c){let l=a.data,d=xr.deflateRawSync(l),p=d.length<l.length,h=p?d:l,u=p?8:0,m=Er(l),f=l.length,x=h.length,{time:v,date:S}=Pr(a.date),y=Buffer.from(a.name.replace(/\\/g,"/"),"utf-8"),k=Buffer.alloc(30+y.length);k.writeUInt32LE(67324752,0),k.writeUInt16LE(20,4),k.writeUInt16LE(2048,6),k.writeUInt16LE(u,8),k.writeUInt16LE(v,10),k.writeUInt16LE(S,12),k.writeUInt32LE(m,14),k.writeUInt32LE(x,18),k.writeUInt32LE(f,22),k.writeUInt16LE(y.length,26),k.writeUInt16LE(0,28),y.copy(k,30),t.push(k),t.push(h);let C=Buffer.alloc(46+y.length);C.writeUInt32LE(33639248,0),C.writeUInt16LE(20,4),C.writeUInt16LE(20,6),C.writeUInt16LE(2048,8),C.writeUInt16LE(u,10),C.writeUInt16LE(v,12),C.writeUInt16LE(S,14),C.writeUInt32LE(m,16),C.writeUInt32LE(x,20),C.writeUInt32LE(f,24),C.writeUInt16LE(y.length,28),C.writeUInt16LE(0,30),C.writeUInt16LE(0,32),C.writeUInt16LE(0,34),C.writeUInt16LE(0,36),C.writeUInt32LE(0,38),C.writeUInt32LE(s,42),y.copy(C,46),e.push(C),s+=k.length+h.length}let n=Buffer.concat(e),r=n.length,i=s,o=Buffer.alloc(22);return o.writeUInt32LE(101010256,0),o.writeUInt16LE(0,4),o.writeUInt16LE(0,6),o.writeUInt16LE(c.length,8),o.writeUInt16LE(c.length,10),o.writeUInt32LE(r,12),o.writeUInt32LE(i,16),o.writeUInt16LE(0,20),Buffer.concat([...t,n,o])}async function Ei(c,t){let e=kr(t);await Tr.promises.writeFile(c,e)}var Pi=de(()=>{"use strict"});import Ze from"fs";import Re from"path";import X from"chalk";var it,Bt,Ht=de(()=>{"use strict";Fe();Ke();mt();ft();Be();rs();Vt();gi();os();mn();yn();wn();xs();ut();st();Pi();it=class{driver;memory;reporter;checkpoints;baselines;lastHierarchy=null;screenDimensions={width:I.screen.fallbackWidth,height:I.screen.fallbackHeight};navBarHeight=0;constructor(t,e,s,n,r,i){this.driver=t||re,this.memory=e||Me,this.reporter=s||gt,this.checkpoints=n||hi,this.baselines=r||js,this.semanticResolver=i||bn()}semanticResolver;healEnabled=!1;activeLocale="en";runtimeVariables=new Map;loopStack=[];expandSpecIncludes(t,e=new Set){return Ms(t,e)}async expandSpec(t,e){let s=e||this.semanticResolver,n=t.split(`
859
- `),r=[],i=0;for(let o of n){let a=o.trim();if(!a||a.startsWith("#")||a.startsWith("//")){r.push(o);continue}let l=this.parsePrompt(a);if(l.length>0&&l[0].type!==void 0&&!a.toLowerCase().startsWith("intent:")){r.push(o);continue}let p=a.replace(/^intent:\s*/i,""),h=this.memory?.getIntentExpansion(p);if(h?.length){r.push(`# [Cached Intent] ${a}`,...h),i+=h.length;continue}let u=await s.resolveIntent(p);if(u?.length){r.push(`# [AI Expanded] ${a}`);let m=u.map(f=>f.originalText);r.push(...m),this.memory&&(this.memory.recordIntentExpansion(p,m),await this.memory.save().catch(()=>{})),i+=u.length}else r.push(o)}return{expanded:r.join(`
860
- `),stepsAdded:i}}parsePrompt(t,e){return Jt(t,e)}async runSpec(t,e,s){if(!Ze.existsSync(t))throw new Error(`Spec file not found: ${t}`);let n=typeof e=="string"?{packageName:e,serial:s,screenshotMode:"on-failure",continueOnError:!1}:{screenshotMode:"on-failure",continueOnError:!1,...e};n.heal!==void 0&&(this.healEnabled=n.heal),n.noAi&&(this.semanticResolver=bn({provider:"heuristic"})),this.activeLocale=(n.locale||I.locale||"en").toLowerCase(),typeof this.memory.setLocale=="function"&&this.memory.setLocale(this.activeLocale);let r=this.expandSpecIncludes(t),i=r,o=[],a=Gt.extractDataDirective(r,Re.dirname(t));if(a)i=a.cleanedSpec,o=a.rows;else if(n.dataFile){let S=Re.isAbsolute(n.dataFile)?n.dataFile:Re.resolve(process.cwd(),n.dataFile);o=Gt.loadFromFile(S)}(n.onlyRow!==void 0||n.iterations!==void 0)&&(o.length>0?o=Gt.filterRows(o,{onlyRow:n.onlyRow,iterations:n.iterations}):n.iterations&&n.iterations>0&&(o=Array.from({length:n.iterations},()=>({}))));let l=o.length>0?o.map(S=>({...n.variables||{},...S})):[n.variables||{}],d=Re.basename(t,Re.extname(t)),p=i.match(/#\s*(?:Target\s*)?Package:\s*([a-zA-Z0-9._-]+)/i),h=p?p[1]:void 0;if(n.tags){let S=ri(r),y=n.tags.split(",").map(C=>C.trim().toLowerCase()).filter(Boolean);if(!y.some(C=>S.includes(C)))return console.log(X.yellow(`
861
- \u23E9 Skipping ${t}: tags [${S.join(", ")}] do not match filter [${y.join(", ")}]`)),gs(h||n.packageName||"",n.serial||"",[],"SKIPPED")}if(n.listSteps){let S=this.parsePrompt(i,l[0]);return console.log(oi(S)),gs(h||n.packageName||"",n.serial||"",S,"SKIPPED","Listing")}if(n.dryRun){let S=this.parsePrompt(i,l[0]);return console.log(X.bold.cyan(`
862
- \u{1F50D} [Dry Run] Validated ${S.length} test step(s) in ${t} successfully (no device interaction).`)),gs(h||n.packageName||"",n.serial||"",S,"PASS","Validation")}if(l.length>1)console.log(X.cyan(`
863
- \u{1F4CB} Loaded spec with ${l.length} data iteration(s) from ${t}`));else{let S=this.parsePrompt(i,l[0]);console.log(X.cyan(`
864
- \u{1F4CB} Loaded ${S.length} test step(s) from ${t}`))}console.log(X.gray("Execution Policy: ")+X.bold.white(n.continueOnError?"Continue on Error (Modular Run)":"Fail-Fast (Default)")+X.gray(" | Screenshots: ")+X.bold.white(n.screenshotMode==="all"?"All Steps":"Failure-Only")),n.serial=await this.driver.ensureDefaultDevice(n.serial);let u=n.serial||this.driver.getDefaultDevice()||"Default Device",m=null;n.serial&&(m=Rt(n.serial)),n.embedScreenshots&&typeof this.reporter.setEmbedScreenshots=="function"&&this.reporter.setEmbedScreenshots(!0);let f,x;if(n.serve){let S=typeof n.serve=="number"?n.serve:4040;try{f=await En({port:S,suiteName:d}),console.log(X.bold.magenta(`
865
- \u{1F4E1} Live Monitor streaming at: ${f.url}
866
- `)),typeof this.reporter.onStep=="function"&&(x=this.reporter.onStep(y=>{f?.broadcastStep(y)}))}catch(y){let k=y;console.warn(X.yellow(`Warning: Could not start live server: ${k.message}`))}}let v=null;n.video&&(console.log(X.cyan("\u{1F3AC} Starting ADB screen recording (--video enabled)...")),v=await this.driver.startScreenRecording("/sdcard/prompttest_temp.mp4",n.serial));try{this.screenDimensions=await this.driver.getScreenDimensions(n.serial),this.navBarHeight=typeof this.driver.getNavigationBarHeight=="function"?await this.driver.getNavigationBarHeight(n.serial):0,console.log(X.gray("Target Screen Geometry: ")+X.bold.cyan(`${this.screenDimensions.width}x${this.screenDimensions.height}`)+(this.navBarHeight>0?X.gray(` (Nav Bar: ${this.navBarHeight}px)`):"")),n.apkPath&&(Ze.existsSync(n.apkPath)?(console.log(X.cyan(`\u{1F4E6} Installing APK: ${n.apkPath}...`)),await this.driver.installApk(n.apkPath,n.serial),await new Promise(O=>setTimeout(O,2e3))):console.log(X.yellow(`Warning: Specified APK file not found at: ${n.apkPath}`)));let S=n.packageName||h||(typeof this.driver.getForegroundPackage=="function"?await this.driver.getForegroundPackage(n.serial):null)||"Foreground App";if(S&&!S.includes("Launcher")&&!S.includes("Foreground")){if(!(typeof this.driver.isAppInstalled=="function"?await this.driver.isAppInstalled(S,n.serial):!0))if(n.apkPath&&Ze.existsSync(n.apkPath))console.log(X.cyan(`\u{1F4E6} Target app not found. Auto-installing APK: ${n.apkPath}...`)),await this.driver.installApk(n.apkPath,n.serial),await new Promise(W=>setTimeout(W,2500));else throw new Error(`Target application "${S}" is not installed on device (${u}).
908
+ `),o.add(f),x.on("close",()=>{o.delete(f)});return}if(E==="/"||E==="/report"||E==="/index.html"){let g=en(r,{suiteName:i});f.writeHead(200,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-cache"}),f.end(g);return}if(E==="/api/report"||E==="/results.json"){f.writeHead(200,{"Content-Type":"application/json; charset=utf-8","Access-Control-Allow-Origin":"*"}),f.end(JSON.stringify(r,null,2));return}let ge=E.replace(/^\/+/,""),Y=ee.join(n,ge);if(!Y.startsWith(n)){f.writeHead(403,{"Content-Type":"text/plain"}),f.end("Access Denied");return}if(te.existsSync(Y)&&te.statSync(Y).isFile()){let g=ee.extname(Y).toLowerCase(),k=Io[g]||"application/octet-stream";f.writeHead(200,{"Content-Type":k,"Cache-Control":"no-cache"}),te.createReadStream(Y).pipe(f);return}f.writeHead(404,{"Content-Type":"text/plain"}),f.end("Not Found")});await new Promise((x,f)=>{ye.once("error",f),ye.listen(t,e,()=>{x()})});let re=ye.address(),W=typeof re=="object"&&re?re.port:t,se=`http://${e}:${W}`;return{server:ye,port:W,url:se,broadcastStep(x){r.steps.push(x),r.totalSteps=r.steps.length,x.status==="PASS"?r.passedSteps++:x.status==="FAIL"?r.failedSteps++:x.status==="WARN"?r.warnSteps++:x.status==="SKIPPED"&&r.skippedSteps++,K("step",x)},broadcastComplete(x){r=x,K("complete",x)},broadcastError(x){r.errorsDetected.push(x),K("error",{error:x})},updateReport(x){r=x},async close(){if(clearInterval(l),T){try{await T.stopSession()}catch{}T=null,w=null}y&&y.status==="running"&&(y.abort.aborted=!0);for(let x of o)try{x.end()}catch{}o.clear(),await new Promise((x,f)=>{ye.close(F=>{F?f(F):x()})})}}}var Io,Ms=ie(()=>{"use strict";Tt();Re();Ve();Yt();et();hs();ss();ms();qt();Xn();Qn();Io={".html":"text/html; charset=utf-8",".json":"application/json; charset=utf-8",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".svg":"image/svg+xml",".mp4":"video/mp4",".log":"text/plain; charset=utf-8",".txt":"text/plain; charset=utf-8",".xml":"application/xml; charset=utf-8",".zip":"application/zip"}});import No from"chalk";async function di(a,t){if(qn)return qn;let e=!!(process.env.CI||process.env.GITHUB_ACTIONS||process.env.TRAVIS||process.env.CIRCLECI||process.env.GITLAB_CI),s=!1;try{let{stdout:c}=await a.execAdb(["shell","getprop","ro.kernel.qemu"],t),{stdout:d}=await a.execAdb(["shell","getprop","ro.product.model"],t),{stdout:p}=await a.execAdb(["shell","getprop","ro.build.characteristics"],t),u=(d||"").toLowerCase(),h=(p||"").toLowerCase();s=c?.trim()==="1"||u.includes("sdk")||u.includes("emulator")||h.includes("emulator")}catch{s=!1}let n=1;e?n=s?2.8:2:s?n=1.5:n=1;let i=parseFloat(process.env.PROMPTTEST_SETTLE_FACTOR||"");!isNaN(i)&&i>0&&(n=Math.max(.5,i));let r={isEmulator:s,isCI:e,velocityMultiplier:n,defaultTimeoutMs:Math.round(3500*n),extendedTimeoutMs:Math.round(6500*n),quiescenceSettleMs:Math.round(150*Math.min(n,2)),initialPollMs:e?150:80,maxPollIntervalMs:e?600:350};qn=r;let o=s?"Android Emulator":"Physical Android Device",l=e?"CI / Cloud Runner":"Local Environment";return console.log(No.cyan(`\u26A1 [Device Profiler] Detected ${o} in ${l} (Velocity Multiplier: ${n.toFixed(1)}x, Dynamic Timeout: ${r.defaultTimeoutMs}ms)`)),r}var qn,pi=ie(()=>{"use strict";qn=null});var gi={};Qe(gi,{PerformanceProfiler:()=>er,parseGfxInfo:()=>ui,parseMemInfo:()=>hi});function ui(a){let t=0,e=0,s=0,n,i,r,o,l,c,d=a.match(/Total frames rendered:\s+(\d+)/i);d&&(t=parseInt(d[1],10));let p=a.match(/Janky frames:\s+(\d+)(?:\s+\(([0-9.]+)%\))?/i);p&&(e=parseInt(p[1],10),p[2]?s=parseFloat(p[2]):t>0&&(s=parseFloat((e/t*100).toFixed(2))));let u=a.match(/50th percentile:\s+(\d+)ms/i);u&&(n=parseInt(u[1],10));let h=a.match(/90th percentile:\s+(\d+)ms/i);h&&(i=parseInt(h[1],10));let m=a.match(/95th percentile:\s+(\d+)ms/i);m&&(r=parseInt(m[1],10));let b=a.match(/99th percentile:\s+(\d+)ms/i);b&&(o=parseInt(b[1],10));let T=a.match(/Number Missed Vsync:\s+(\d+)/i);T&&(l=parseInt(T[1],10));let w=a.match(/Number Slow UI thread:\s+(\d+)/i);return w&&(c=parseInt(w[1],10)),{totalFrames:t,jankyFrames:e,jankPercentage:s,p50Ms:n,p90Ms:i,p95Ms:r,p99Ms:o,missedVsync:l,slowUiThread:c}}function hi(a){let t=0,e,s,n=a.match(/TOTAL PSS:\s+(\d+)/i)||a.match(/TOTAL:\s+(\d+)/i)||a.match(/TOTAL\s+PSS\s+TOTAL\s+RSS[\s\S]*?TOTAL\s+(\d+)/i);n&&(t=parseInt(n[1],10));let i=a.match(/Native Heap\s+(\d+)/i)||a.match(/Native\s+(\d+)/i);i&&(e=parseInt(i[1],10));let r=a.match(/Java Heap\s+(\d+)/i)||a.match(/Dalvik Heap\s+(\d+)/i);return r&&(s=parseInt(r[1],10)),{totalPssKb:t,nativeHeapKb:e,javaHeapKb:s}}var er,mi=ie(()=>{"use strict";Re();er=class{driver;serial;packageName="";startTime=0;memorySamples=[];constructor(t,e){this.driver=t||oe,this.serial=e}async start(t){this.packageName=t,this.startTime=Date.now(),this.memorySamples=[];try{await this.driver.execAdb(["shell","dumpsys","gfxinfo",t,"reset"],this.serial)}catch{}await this.sample(0,"Suite Initialized")}async sample(t,e){if(this.packageName)try{let{stdout:s}=await this.driver.execAdb(["shell","dumpsys","meminfo",this.packageName],this.serial),n=hi(s);this.memorySamples.push({timestamp:Date.now(),stepIndex:t,stepName:e,totalPssKb:n.totalPssKb,nativeHeapKb:n.nativeHeapKb,javaHeapKb:n.javaHeapKb})}catch{}}async stop(){let t=Date.now()-(this.startTime||Date.now()),e={totalFrames:0,jankyFrames:0,jankPercentage:0};if(this.packageName)try{let{stdout:o}=await this.driver.execAdb(["shell","dumpsys","gfxinfo",this.packageName],this.serial);e=ui(o)}catch{}let s=this.memorySamples[0]?.totalPssKb||0,n=this.memorySamples[this.memorySamples.length-1]?.totalPssKb||s,i=this.memorySamples.reduce((o,l)=>Math.max(o,l.totalPssKb),s),r="PASS";return e.jankPercentage>15||e.p95Ms&&e.p95Ms>32?r="FAIL":(e.jankPercentage>5||e.p95Ms&&e.p95Ms>16.7)&&(r="WARN"),{packageName:this.packageName,durationMs:t,fpsCompliance:r,frames:e,memory:{startPssKb:s,peakPssKb:i,endPssKb:n,deltaPssKb:n-s,samples:this.memorySamples}}}}});import Fs from"fs";import ft from"path";import ne from"chalk";var st,Vt,Yt=ie(()=>{"use strict";Re();Ve();Tt();Pt();je();us();Qt();zr();hs();_n();Un();Wn();Ms();et();ot();pi();st=class{driver;memory;reporter;checkpoints;baselines;lastHierarchy=null;screenDimensions={width:R.screen.fallbackWidth,height:R.screen.fallbackHeight};navBarHeight=0;targetPackage=void 0;constructor(t,e,s,n,i,r){this.driver=t||oe,this.memory=e||Me,this.reporter=s||xt,this.checkpoints=n||Kr,this.baselines=i||dn,this.semanticResolver=r||Bn()}semanticResolver;healEnabled=!1;activeLocale="en";runtimeVariables=new Map;loopStack=[];expandSpecIncludes(t,e=new Set){return Xs(t,e)}async expandSpec(t,e){let s=e||this.semanticResolver,n=t.split(`
909
+ `),i=[],r=0;for(let o of n){let l=o.trim();if(!l||l.startsWith("#")||l.startsWith("//")){i.push(o);continue}let c=this.parsePrompt(l);if(c.length>0&&c[0].type!==void 0&&!l.toLowerCase().startsWith("intent:")){i.push(o);continue}let p=l.replace(/^intent:\s*/i,""),u=this.memory?.getIntentExpansion(p);if(u?.length){i.push(`# [Cached Intent] ${l}`,...u),r+=u.length;continue}let h=await s.resolveIntent(p);if(h?.length){i.push(`# [AI Expanded] ${l}`);let m=h.map(b=>b.originalText);i.push(...m),this.memory&&(this.memory.recordIntentExpansion(p,m),await this.memory.save().catch(()=>{})),r+=h.length}else i.push(o)}return{expanded:i.join(`
910
+ `),stepsAdded:r}}parsePrompt(t,e){return St(t,e)}async runSpec(t,e,s){if(!Fs.existsSync(t))throw new Error(`Spec file not found: ${t}`);let n=typeof e=="string"?{packageName:e,serial:s,screenshotMode:"on-failure",continueOnError:!1}:{screenshotMode:"on-failure",continueOnError:!1,...e};n.heal!==void 0&&(this.healEnabled=n.heal),n.noAi&&(this.semanticResolver=Bn({provider:"heuristic"})),this.activeLocale=(n.locale||R.locale||"en").toLowerCase(),typeof this.memory.setLocale=="function"&&this.memory.setLocale(this.activeLocale);let i=this.expandSpecIncludes(t),r=i,o=[],l=ts.extractDataDirective(i,ft.dirname(t));if(l)r=l.cleanedSpec,o=l.rows;else if(n.dataFile){let S=ft.isAbsolute(n.dataFile)?n.dataFile:ft.resolve(process.cwd(),n.dataFile);o=ts.loadFromFile(S)}(n.onlyRow!==void 0||n.iterations!==void 0)&&(o.length>0?o=ts.filterRows(o,{onlyRow:n.onlyRow,iterations:n.iterations}):n.iterations&&n.iterations>0&&(o=Array.from({length:n.iterations},()=>({}))));let c=o.length>0?o.map(S=>({...n.variables||{},...S})):[n.variables||{}],d=ft.basename(t,ft.extname(t)),p=r.match(/#\s*(?:Target\s*)?Package:\s*([a-zA-Z0-9._-]+)/i),u=p?p[1]:void 0;if(n.tags){let S=Or(i),y=n.tags.split(",").map(N=>N.trim().toLowerCase()).filter(Boolean);if(!y.some(N=>S.includes(N)))return console.log(ne.yellow(`
911
+ \u23E9 Skipping ${t}: tags [${S.join(", ")}] do not match filter [${y.join(", ")}]`)),ws(u||n.packageName||"",n.serial||"",[],"SKIPPED")}if(n.listSteps){let S=this.parsePrompt(r,c[0]);return console.log(_r(S)),ws(u||n.packageName||"",n.serial||"",S,"SKIPPED","Listing")}if(n.dryRun){let S=this.parsePrompt(r,c[0]);return console.log(ne.bold.cyan(`
912
+ \u{1F50D} [Dry Run] Validated ${S.length} test step(s) in ${t} successfully (no device interaction).`)),ws(u||n.packageName||"",n.serial||"",S,"PASS","Validation")}if(c.length>1)console.log(ne.cyan(`
913
+ \u{1F4CB} Loaded spec with ${c.length} data iteration(s) from ${t}`));else{let S=this.parsePrompt(r,c[0]);console.log(ne.cyan(`
914
+ \u{1F4CB} Loaded ${S.length} test step(s) from ${t}`))}console.log(ne.gray("Execution Policy: ")+ne.bold.white(n.continueOnError?"Continue on Error (Modular Run)":"Fail-Fast (Default)")+ne.gray(" | Screenshots: ")+ne.bold.white(n.screenshotMode==="all"?"All Steps":"Failure-Only")),n.serial=await this.driver.ensureDefaultDevice(n.serial);let h=n.serial||this.driver.getDefaultDevice()||"Default Device",m=null;n.serial&&(m=Ft(n.serial)),n.embedScreenshots&&typeof this.reporter.setEmbedScreenshots=="function"&&this.reporter.setEmbedScreenshots(!0);let b,T;if(n.serve){let S=typeof n.serve=="number"?n.serve:4040;try{b=await Zn({port:S,suiteName:d}),console.log(ne.bold.magenta(`
915
+ \u{1F4E1} Live Monitor streaming at: ${b.url}
916
+ `)),typeof this.reporter.onStep=="function"&&(T=this.reporter.onStep(y=>{b?.broadcastStep(y)}))}catch(y){let P=y;console.warn(ne.yellow(`Warning: Could not start live server: ${P.message}`))}}let w=null;n.video&&(console.log(ne.cyan("\u{1F3AC} Starting ADB screen recording (--video enabled)...")),w=await this.driver.startScreenRecording("/sdcard/prompttest_temp.mp4",n.serial));try{this.screenDimensions=await this.driver.getScreenDimensions(n.serial),this.navBarHeight=typeof this.driver.getNavigationBarHeight=="function"?await this.driver.getNavigationBarHeight(n.serial):0,console.log(ne.gray("Target Screen Geometry: ")+ne.bold.cyan(`${this.screenDimensions.width}x${this.screenDimensions.height}`)+(this.navBarHeight>0?ne.gray(` (Nav Bar: ${this.navBarHeight}px)`):"")),n.apkPath&&(Fs.existsSync(n.apkPath)?(console.log(ne.cyan(`\u{1F4E6} Installing APK: ${n.apkPath}...`)),await this.driver.installApk(n.apkPath,n.serial),await new Promise(Q=>setTimeout(Q,2e3))):console.log(ne.yellow(`Warning: Specified APK file not found at: ${n.apkPath}`)));let S=n.packageName||u||(typeof this.driver.getForegroundPackage=="function"?await this.driver.getForegroundPackage(n.serial):null)||"Foreground App";if(S&&!S.includes("Launcher")&&!S.includes("Foreground")){if(!(typeof this.driver.isAppInstalled=="function"?await this.driver.isAppInstalled(S,n.serial):!0))if(n.apkPath&&Fs.existsSync(n.apkPath))console.log(ne.cyan(`\u{1F4E6} Target app not found. Auto-installing APK: ${n.apkPath}...`)),await this.driver.installApk(n.apkPath,n.serial),await new Promise(V=>setTimeout(V,2500));else throw new Error(`Target application "${S}" is not installed on device (${h}).
867
917
  \u{1F4A1} Suggestions:
868
918
  \u2022 Install the app manually or via: adb install <path-to-apk>
869
- \u2022 Or provide an APK path to auto-install before running: --apk=path/to/app.apk`);let V=await this.driver.getForegroundPackage(n.serial);(n.fresh||V&&V!==S)&&(console.log(X.cyan(`\u{1F680} Launching target application: ${X.bold(S)}...`)),n.fresh&&(await this.driver.forceStop(S,n.serial),await new Promise(W=>setTimeout(W,600))),await this.driver.launchApp(S,n.serial),await new Promise(W=>setTimeout(W,2500)))}n.reporter&&(this.reporter=n.reporter),n.reporters&&typeof this.reporter.setFormats=="function"&&this.reporter.setFormats(n.reporters),this.reporter.init(S,u),await this.memory.init(S),this.lastHierarchy=null;let y=Date.now(),k=0;for(let O=0;O<l.length;O++){let V=l[O],W=l.length>1,se=n.onlyRow?`Row ${n.onlyRow}`:W?`Row ${O+1}`:void 0,oe=se?`${d} [${se}]`:d;W&&(console.log(X.bold.magenta(`
919
+ \u2022 Or provide an APK path to auto-install before running: --apk=path/to/app.apk`);let K=await this.driver.getForegroundPackage(n.serial);(n.fresh||K&&K!==S)&&(console.log(ne.cyan(`\u{1F680} Launching target application: ${ne.bold(S)}...`)),n.fresh&&(await this.driver.forceStop(S,n.serial),await new Promise(V=>setTimeout(V,600))),await this.driver.launchApp(S,n.serial),await new Promise(V=>setTimeout(V,2500)))}n.reporter&&(this.reporter=n.reporter),n.reporters&&typeof this.reporter.setFormats=="function"&&this.reporter.setFormats(n.reporters),n.serial&&await di(this.driver,n.serial).catch(()=>{}),this.targetPackage=S&&!S.includes("Foreground")&&!S.includes("Launcher")?S:void 0,this.reporter.init(S,h),await this.memory.init(S);let y=null;if(n.profile&&this.targetPackage){let{PerformanceProfiler:Q}=await Promise.resolve().then(()=>(mi(),gi));y=new Q(this.driver,n.serial),await y.start(this.targetPackage)}this.lastHierarchy=null;let P=Date.now(),N=0;for(let Q=0;Q<c.length;Q++){let K=c[Q],V=c.length>1,ye=n.onlyRow?`Row ${n.onlyRow}`:V?`Row ${Q+1}`:void 0,re=ye?`${d} [${ye}]`:d;V&&(console.log(ne.bold.magenta(`
870
920
  ======================================================
871
- \u{1F504} [Data-Driven Iteration ${O+1}/${l.length}] Row Data: ${JSON.stringify(V)}
872
- ======================================================`)),n.fresh&&O>0&&S&&(await this.driver.clearAppData(S,n.serial),await new Promise(_=>setTimeout(_,1e3)))),this.runtimeVariables.clear();for(let[_,P]of Object.entries(V))this.runtimeVariables.set(_,P);this.loopStack=[];let G=this.parsePrompt(i,V),H=1,w=n.resume&&O===0?await this.checkpoints.getResumeIndex(d):0;w>0&&console.log(X.cyan(`\u23ED Resuming from step ${w+1} (checkpoint found).`));let b=n.only?ii(n.only):null;for(let _=0;_<G.length;_++){let P=G[_];if(H=_+1,b&&!b.has(H)){console.log(X.gray(`[Step ${H}/${G.length}] \u23E9 Skipped (not in --only range: ${n.only})`)),this.reporter.logStep(oe,P.originalText,P.type,"SKIPPED",`Filtered out by --only="${n.only}"`,void 0,{videoOffsetMs:Math.max(0,Date.now()-y)},0);continue}if(_<w){this.reporter.logStep(oe,P.originalText,P.type,"SKIPPED","Resumed \u2014 already passed",void 0,{videoOffsetMs:0},0);continue}let ce=G.slice(_+1);console.log(X.bold.blue(`
873
- [Step ${H}/${G.length}] ${P.originalText}`));let ne=Date.now();try{let g=await this.executeStep(P,n.serial,ce),D=Date.now()-ne;if(g&&g.fastForwarded){if(console.log(X.cyan(`\u23E9 Fast-Forward: ${g.reason}. Redundant step(s) bypassed.`)),this.reporter.logStep(oe,P.originalText,P.type,"PASS",`Fast-Forward: ${g.reason}`,void 0,{videoOffsetMs:Math.max(0,ne-y)},D),console.log(X.green(`\u2714 Step ${H} PASSED (Fast-Forward)`)),g?.skipCount&&g.skipCount>0){let N=g.skipCount;for(let M=0;M<N&&_+1<G.length;M++){_++;let T=G[_],A=_+1;this.reporter.logStep(oe,T.originalText,T.type,"PASS",`Fast-Forward: ${g.reason}`,void 0,{videoOffsetMs:Math.max(0,ne-y)},0),console.log(X.gray(` [Step ${A}/${G.length}] \u23E9 Fast-Forward: ${T.originalText}`))}}}else{let N;if((n.screenshotMode==="all"||n.saveBaseline||n.compareBaseline)&&(N=await this.driver.captureScreenshot(n.serial)),N&&n.compareBaseline){let M=await this.baselines.compare(oe,H,N,n.baselineThreshold);M.passed||(console.log(X.yellow(` \u26A0\uFE0F [Baseline] Step ${H} visual diff ${(M.diffRatio*100).toFixed(1)}% exceeds threshold ${(M.threshold*100).toFixed(1)}%`)),this.reporter.logError(`Visual baseline diff at Step ${H}: ${(M.diffRatio*100).toFixed(1)}% changed`))}N&&n.saveBaseline&&!n.compareBaseline&&await this.baselines.saveBaseline(oe,H,N),this.reporter.logStep(oe,P.originalText,P.type,"PASS",P.target||P.value,n.screenshotMode==="all"?N:void 0,{videoOffsetMs:Math.max(0,ne-y)},D),console.log(X.green(`\u2714 Step ${H} PASSED`)),n.benchmark&&console.log(X.gray(` \u23F1 [Benchmark] Step ${H} latency: ${D}ms (${P.type}${P.target?` -> "${P.target}"`:""})`))}n.resume&&!W&&await this.checkpoints.save(d,S,_,G.length)}catch(g){let D=g,N=Date.now()-ne;if(P.optional){console.log(X.yellow(`\u26AA Step ${H} OPTIONAL (Skipped: ${D.message})`)),this.reporter.logStep(oe,P.originalText,P.type,"SKIPPED",`Optional step bypassed: ${D.message}`,void 0,{error:D.message,videoOffsetMs:Math.max(0,ne-y)},N);continue}k++,console.log(X.red(`\u274C Step ${H} FAILED: ${D.message}`)),P.target&&this.memory.recordFailure(P.type,P.target);let M=await this.driver.captureScreenshot(n.serial).catch(()=>{}),T;try{let A=Re.resolve(process.cwd(),"output"),E=`${d}_step_${H}`,L=Re.join(A,"failures",E);Ze.existsSync(L)||Ze.mkdirSync(L,{recursive:!0}),M&&await Ze.promises.writeFile(Re.join(L,"screenshot.png"),M);let U=this.lastHierarchy?.xml||await this.driver.getUiHierarchy(n.serial).catch(()=>"");U&&await Ze.promises.writeFile(Re.join(L,"hierarchy.xml"),U,"utf-8");let z=await this.driver.getLogcatTail(150,n.serial);await Ze.promises.writeFile(Re.join(L,"logcat_tail.log"),z,"utf-8");let K=this.lastHierarchy?.flat||[],q=P.target&&K.length>0?At(P.target,K.map(Te=>Te.text||Te.contentDesc||"").filter(Boolean)):[];await Ze.promises.writeFile(Re.join(L,"match_scores.json"),JSON.stringify(q,null,2),"utf-8");let ie={specName:oe,stepNumber:H,stepType:P.type,target:P.target||P.value,error:D.message,timestamp:new Date().toISOString(),videoOffsetMs:Math.max(0,ne-y)};await Ze.promises.writeFile(Re.join(L,"manifest.json"),JSON.stringify(ie,null,2),"utf-8");let fe=[{name:"manifest.json",data:Buffer.from(JSON.stringify(ie,null,2),"utf-8")},{name:"logcat_tail.log",data:Buffer.from(z,"utf-8")},{name:"match_scores.json",data:Buffer.from(JSON.stringify(q,null,2),"utf-8")}];U&&fe.push({name:"hierarchy.xml",data:Buffer.from(U,"utf-8")}),M&&fe.push({name:"screenshot.png",data:M});let ee=`${E}.zip`,Ce=Re.join(A,"failures",ee);await Ei(Ce,fe),T=Re.join("failures",ee).replace(/\\/g,"/"),console.log(X.red(` \u{1F4C1} Failure Triage ZIP Bundle generated at: output/${T}`))}catch{}if(this.reporter.logStep(oe,P.originalText,P.type,"FAIL",D.message,M,{error:D.message,videoOffsetMs:Math.max(0,ne-y)},N,T),this.reporter.logError(`Step ${H} failed: ${D.message}`),n.continueOnError)console.log(X.yellow("Continuing with next test step (--continue enabled)..."));else{let A=G.slice(_+1);if(A.length>0){console.log(X.yellow(`
874
- \u{1F6D1} Aborting dependent flow due to upstream failure at Step ${H}.`)),console.log(X.gray(`Cascading skip applied to ${A.length} downstream step(s):`));for(let E=0;E<A.length;E++){let L=A[E],U=H+1+E;this.reporter.logStep(oe,L.originalText,L.type,"SKIPPED",`Cascading skip: Precondition failed at Step ${H} (${P.originalText})`,void 0,{videoOffsetMs:Math.max(0,Date.now()-y)},0),console.log(X.gray(` [Step ${U}/${G.length}] \u26AA SKIPPED: ${L.originalText}`))}}break}}}}try{let O=this.lastHierarchy;if(O?.flat){let V=Zn(O.flat,this.screenDimensions.width,this.screenDimensions.height);if(V.length>0){console.log(X.bold.yellow(`
875
- \u{1F50D} Visual QA Sentinel Warnings (${V.length} detected):`));for(let W of V)console.log(X.yellow(` \u2022 [${W.type}] ${W.description}`)),this.reporter.logError(`Visual Defect (${W.type}): ${W.description}`)}}}catch{}await this.memory.save(),n.resume&&k===0&&await this.checkpoints.clear(d);let C=this.memory.getStats();if(C.totalMappings>0&&console.log(X.cyan(`\u{1F9E0} Knowledge Engine: ${C.totalMappings} active learned mapping(s) for ${C.package}`)),v){console.log(X.cyan("\u{1F3AC} Finalizing video recording..."));let O=Re.resolve(process.cwd(),"output"),V=Re.join(O,`${d}-recording.mp4`),W=await this.driver.stopScreenRecording(v,V,n.serial);W&&Ze.existsSync(W)&&(this.reporter.setVideoPath?.(`${d}-recording.mp4`),console.log(X.green(`\u2714 Screen recording saved to: output/${d}-recording.mp4`))),v=null}let j=((Date.now()-y)/1e3).toFixed(1),$=await this.reporter.generateReport(d);f&&(f.broadcastComplete($),x&&x()),console.log(X.bold.green(`
876
- \u2728 PromptTest Script Execution Finished in ${j}s!`));let J=$.failedSteps===0?X.bold.green:X.bold.yellow;return console.log(J(`Result: ${$.passedSteps}/${$.totalSteps} passed (${$.failedSteps} failed, ${$.skippedSteps} skipped).`)),console.log(X.cyan(`Report saved to output/${d}-report.html
877
- `)),n.json&&console.log(JSON.stringify($,null,2)),$}finally{if(v)try{await this.driver.stopScreenRecording(v,Re.join("output",`${d}-recording.mp4`),n.serial)}catch{}m&&Lt(m)}}async getCleanHierarchy(t){let e=await this.driver.getUiHierarchy(t),{flat:s}=ve(e),n=s.find(o=>(o.text==="Dismiss"||o.contentDesc==="Dismiss")&&o.bounds.width>0);n&&(await this.driver.tap(n.bounds.centerX,n.bounds.centerY,t),await new Promise(o=>setTimeout(o,400)),e=await this.driver.getUiHierarchy(t),s=ve(e).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,a=r.bounds.centerY;await this.driver.tap(o,a,t),await new Promise(l=>setTimeout(l,350)),e=await this.driver.getUiHierarchy(t),s=ve(e).flat}let i=De(s);return this.lastHierarchy={xml:e,flat:s,timestamp:Date.now(),hash:i},{xml:e,flat:s}}async ensureInViewport(t,e){let s=this.screenDimensions.width,n=this.screenDimensions.height,r=Math.round(s*.5),i=this.navBarHeight>0?n-this.navBarHeight:n,o=this.navBarHeight>0?i*.94:n*I.gestures.bottomNavThresholdRatio;if(t.bounds.centerY>o||t.bounds.y1>o)return!1;let l=t.bounds.y2>i,d=t.bounds.y1<Math.round(n*.04)&&t.bounds.centerY<Math.round(n*.06);return l?(await this.driver.swipe(r,Math.round(n*.75),r,Math.round(n*.45),250,e),await new Promise(p=>setTimeout(p,200)),this.lastHierarchy=null,!0):d?(await this.driver.swipe(r,Math.round(n*.35),r,Math.round(n*.65),250,e),await new Promise(p=>setTimeout(p,200)),this.lastHierarchy=null,!0):!1}async waitForTarget(t,e,s=3e3,n){if(this.lastHierarchy&&Date.now()-this.lastHierarchy.timestamp<I.polling.hierarchyCacheTtlMs){let a=this.matchNodes(this.lastHierarchy.flat,t,n);if(a.length>0)return{flat:this.lastHierarchy.flat,matched:a}}let r=Date.now();for(;Date.now()-r<s;){let{flat:a}=await this.getCleanHierarchy(e),l=this.matchNodes(a,t,n);if(l.length>0)return{flat:a,matched:l};await new Promise(d=>setTimeout(d,Bn()))}let{flat:i}=await this.getCleanHierarchy(e),o=this.matchNodes(i,t,n);if(o.length===0&&(this.healEnabled||I.ai.selfHealing))try{let a=await this.semanticResolver.findSuggestions(t,i,{locale:this.activeLocale}),l=I.ai.confidenceThreshold||.75,d=a.find(p=>p.score>=l);if(d){let p=d.node||i.find(h=>(me(d.text,h.text)||me(d.text,h.contentDesc))&&h.bounds.width>0);if(p){console.log(X.yellow(` \u{1F4A1} [Self-Healing] Auto-healed "${t}" \u2192 "${d.text}" (${Math.round(d.score*100)}% confidence, ${d.strategy})`)),n&&(n.healedFrom=t);let h=p.text?"text":p.contentDesc?"contentDesc":"resourceId",u=this.lastHierarchy?.hash,m=Math.min(I.memory?.maxHealedConfidence??.65,d.score);this.memory.recordSuccess(n?.type||"TAP",t,d.text,h,m,t,u),o=[p]}}}catch(a){nt.warn(`Self-healing resolution failed: ${a.message}`)}return{flat:i,matched:o}}buildDiagnosticError(t,e,s){return si(t,e,s)}async executeStep(t,e,s){let n=fi.get(t.type);if(!n)throw new Error(`No handler registered for step type: ${t.type}`);t.target&&t.target.includes("$")&&(t.target=Dt(t.target,Object.fromEntries(this.runtimeVariables))),t.value&&t.value.includes("$")&&(t.value=Dt(t.value,Object.fromEntries(this.runtimeVariables)));let r={driver:this.driver,memory:this.memory,serial:e,screenDimensions:this.screenDimensions,navBarHeight:this.navBarHeight,lastHierarchy:this.lastHierarchy,setLastHierarchy:i=>{this.lastHierarchy=i},getCleanHierarchy:i=>this.getCleanHierarchy(i),waitForTarget:(i,o,a,l)=>this.waitForTarget(i,o,a,l),matchNodes:(i,o,a)=>this.matchNodes(i,o,a),ensureInViewport:(i,o)=>this.ensureInViewport(i,o),buildDiagnosticError:(i,o,a)=>this.buildDiagnosticError(i,o,a),findTargetInputField:(i,o,a)=>this.findTargetInputField(i,o,a),upcomingSteps:s,variables:this.runtimeVariables,loopStack:this.loopStack,executeChildSteps:async(i,o)=>{for(let a=0;a<i.length;a++){let l=i[a];await this.executeStep(l,o.serial,i.slice(a+1))}}};return n.execute(t,r)}findTargetInputField(t,e,s){return ni(t,e,s,this.memory)}matchNodes(t,e,s){return hs(t,e,this.memory,this.activeLocale,s,this.lastHierarchy?.hash)}},Bt=new it});var Di={};Tt(Di,{calculatePercentile:()=>Pn,formatBenchmarkTable:()=>Ar,generateSyntheticHierarchy:()=>ki,runBenchmark:()=>Dr,summarizeMetrics:()=>Ts});import Ye from"chalk";function Pn(c,t){if(c.length===0)return 0;let e=[...c].sort((n,r)=>n-r),s=Math.min(e.length-1,Math.max(0,Math.floor(t/100*(e.length-1))));return Math.round(e[s]*100)/100}function Ts(c){if(c.length===0)return{minMs:0,maxMs:0,avgMs:0,p50Ms:0,p95Ms:0,iterations:0,durations:[]};let t=Math.round(Math.min(...c)*100)/100,e=Math.round(Math.max(...c)*100)/100,s=c.reduce((o,a)=>o+a,0),n=Math.round(s/c.length*100)/100,r=Pn(c,50),i=Pn(c,95);return{minMs:t,maxMs:e,avgMs:n,p50Ms:r,p95Ms:i,iterations:c.length,durations:c}}function ki(c=1e3){let t=[];t.push({index:0,className:"android.widget.FrameLayout",packageName:"com.app",text:"",resourceId:"android:id/content",contentDesc:"Main Screen Root",clickable:!1,enabled:!0,focused:!1,scrollable:!1,password:!1,selected:!1,children:[],bounds:{x1:0,y1:0,x2:1080,y2:2400,width:1080,height:2400,centerX:540,centerY:1200}}),t.push({index:1,className:"android.widget.TextView",packageName:"com.app",text:"Apex Coaching Academy - Dashboard",resourceId:"com.app:id/title_header",contentDesc:"Header Title",clickable:!1,enabled:!0,focused:!1,scrollable:!1,password:!1,selected:!1,children:[],bounds:{x1:40,y1:100,x2:1040,y2:220,width:1e3,height:120,centerX:540,centerY:160}});let e=["android.widget.TextView","android.widget.Button","android.widget.EditText","android.widget.ImageView","android.view.ViewGroup","android.widget.LinearLayout","android.widget.CheckBox","android.widget.Switch"],s=["Sign In","Email Address","Password","Submit","Save Changes","Settings","Profile","Notifications","Batch 10A","Mathematics","Physics","Attendance","Export PDF","Delete Record","Cancel","Next","Back","Search","Filter","Refresh"];for(let n=2;n<c;n++){let r=e[n%e.length],i=r.includes("Button")||r.includes("CheckBox")||r.includes("Switch"),o=s[n%s.length],a=`${o} #${n}`,l=Math.min(2400,250+n*20%2e3),d=50+n*30%900;t.push({index:n,className:r,packageName:"com.app",text:r.includes("ImageView")?"":a,resourceId:`com.app:id/element_${n}`,contentDesc:r.includes("ImageView")?`Icon of ${o}`:"",clickable:i,enabled:n%25!==0,focused:!1,scrollable:!1,password:o.toLowerCase().includes("password"),selected:!1,children:[],bounds:{x1:d,y1:l,x2:d+200,y2:l+80,width:200,height:80,centerX:d+100,centerY:l+40}})}return t}async function Dr(c=re,t={}){let e=t.iterations||5,s=Date.now(),n=t.serial,r="Synthetic Mock Android Environment",i=!1;if(!t.forceMock)try{n=await c.ensureDefaultDevice(t.serial),n&&(r=await c.getDeviceModel(n),i=!0)}catch{i=!1}let o=[];for(let m=0;m<e;m++){let f=performance.now();i?await c.captureScreenshot(n):(await new Promise(x=>setTimeout(x,15)),Buffer.alloc(1024*1024)),o.push(performance.now()-f)}let a=[];for(let m=0;m<e;m++){let f=performance.now();i?await c.getUiHierarchy(n):await new Promise(x=>setTimeout(x,25)),a.push(performance.now()-f)}let l=ki(1e3),d=new lt,p=["Submit","Email Address","Mathematics","Save Changes","NonExistent Query Fallback"],h=[];for(let m=0;m<e;m++){let f=p[m%p.length],x=performance.now();hs(l,f,d),h.push(performance.now()-x)}let u=Date.now()-s;return{timestamp:new Date().toISOString(),device:r,package:t.packageName,screencap:Ts(o),uiHierarchy:Ts(a),nodeMatching:Ts(h),totalDurationMs:u}}function Ar(c){let t=(r,i)=>{let o=`${i.minMs.toFixed(1)}ms`.padStart(9),a=`${i.avgMs.toFixed(1)}ms`.padStart(9),l=`${i.p50Ms.toFixed(1)}ms`.padStart(9),d=`${i.p95Ms.toFixed(1)}ms`.padStart(9),p=`${i.maxMs.toFixed(1)}ms`.padStart(9);return`\u2502 ${r.padEnd(28)} \u2502 ${o} \u2502 ${a} \u2502 ${l} \u2502 ${d} \u2502 ${p} \u2502`};return["",Ye.bold.cyan("\u26A1 PromptTest Performance Diagnostic Benchmark"),Ye.gray("Device: ")+Ye.bold.white(c.device)+Ye.gray(` | Samples: ${c.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 ${Ye.bold("Operation".padEnd(28))} \u2502 ${Ye.bold("Min".padStart(9))} \u2502 ${Ye.bold("Avg".padStart(9))} \u2502 ${Ye.bold("p50".padStart(9))} \u2502 ${Ye.bold("p95".padStart(9))} \u2502 ${Ye.bold("Max".padStart(9))} \u2502`,"\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524",t("Device Screencap",c.screencap),t("UI Hierarchy Dump (XML)",c.uiHierarchy),t("Node Matching (1k nodes)",c.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",Ye.gray("Total Benchmark Wall-Time: ")+Ye.bold.green(`${c.totalDurationMs}ms
921
+ \u{1F504} [Data-Driven Iteration ${Q+1}/${c.length}] Row Data: ${JSON.stringify(K)}
922
+ ======================================================`)),n.fresh&&Q>0&&S&&(await this.driver.clearAppData(S,n.serial),await new Promise(F=>setTimeout(F,1e3)))),this.runtimeVariables.clear();for(let[F,E]of Object.entries(K))this.runtimeVariables.set(F,E);this.loopStack=[];let W=this.parsePrompt(r,K),se=1,x=n.resume&&Q===0?await this.checkpoints.getResumeIndex(d):0;x>0&&console.log(ne.cyan(`\u23ED Resuming from step ${x+1} (checkpoint found).`));let f=n.only?Fr(n.only):null;for(let F=0;F<W.length;F++){let E=W[F];if(se=F+1,f&&!f.has(se)){console.log(ne.gray(`[Step ${se}/${W.length}] \u23E9 Skipped (not in --only range: ${n.only})`)),this.reporter.logStep(re,E.originalText,E.type,"SKIPPED",`Filtered out by --only="${n.only}"`,void 0,{videoOffsetMs:Math.max(0,Date.now()-P)},0);continue}if(F<x){this.reporter.logStep(re,E.originalText,E.type,"SKIPPED","Resumed \u2014 already passed",void 0,{videoOffsetMs:0},0);continue}let ge=W.slice(F+1);console.log(ne.bold.blue(`
923
+ [Step ${se}/${W.length}] ${E.originalText}`));let Y=Date.now();try{let g=await this.executeStep(E,n.serial,ge),k=Date.now()-Y;if(g&&g.fastForwarded){if(console.log(ne.cyan(`\u23E9 Fast-Forward: ${g.reason}. Redundant step(s) bypassed.`)),this.reporter.logStep(re,E.originalText,E.type,"PASS",`Fast-Forward: ${g.reason}`,void 0,{videoOffsetMs:Math.max(0,Y-P)},k),console.log(ne.green(`\u2714 Step ${se} PASSED (Fast-Forward)`)),g?.skipCount&&g.skipCount>0){let M=g.skipCount;for(let v=0;v<M&&F+1<W.length;v++){F++;let C=W[F],A=F+1;this.reporter.logStep(re,C.originalText,C.type,"PASS",`Fast-Forward: ${g.reason}`,void 0,{videoOffsetMs:Math.max(0,Y-P)},0),console.log(ne.gray(` [Step ${A}/${W.length}] \u23E9 Fast-Forward: ${C.originalText}`))}}}else{let M;if((n.screenshotMode==="all"||n.saveBaseline||n.compareBaseline)&&(M=await this.driver.captureScreenshot(n.serial)),M&&n.compareBaseline){let C=await this.baselines.compare(re,se,M,n.baselineThreshold);C.passed||(console.log(ne.yellow(` \u26A0\uFE0F [Baseline] Step ${se} visual diff ${(C.diffRatio*100).toFixed(1)}% exceeds threshold ${(C.threshold*100).toFixed(1)}%`)),this.reporter.logError(`Visual baseline diff at Step ${se}: ${(C.diffRatio*100).toFixed(1)}% changed`))}M&&n.saveBaseline&&!n.compareBaseline&&await this.baselines.saveBaseline(re,se,M);let v={videoOffsetMs:Math.max(0,Y-P)};E.healedFrom&&(v.healed=!0,v.healedFrom=E.healedFrom,v.healedTo=E.healedTo||E.target),this.reporter.logStep(re,E.originalText,E.type,"PASS",E.target||E.value,n.screenshotMode==="all"?M:void 0,v,k),console.log(ne.green(`\u2714 Step ${se} PASSED`)),n.benchmark&&console.log(ne.gray(` \u23F1 [Benchmark] Step ${se} latency: ${k}ms (${E.type}${E.target?` -> "${E.target}"`:""})`))}n.resume&&!V&&await this.checkpoints.save(d,S,F,W.length)}catch(g){let k=g,M=Date.now()-Y;if(E.optional){console.log(ne.yellow(`\u26AA Step ${se} OPTIONAL (Skipped: ${k.message})`)),this.reporter.logStep(re,E.originalText,E.type,"SKIPPED",`Optional step bypassed: ${k.message}`,void 0,{error:k.message,videoOffsetMs:Math.max(0,Y-P)},M);continue}N++,console.log(ne.red(`\u274C Step ${se} FAILED: ${k.message}`)),E.target&&this.memory.recordFailure(E.type,E.target);let v=await this.driver.captureScreenshot(n.serial).catch(()=>{}),C=await Hr({outputDir:ft.resolve(process.cwd(),"output"),specName:d,currentSpecName:re,stepNum:se,step:E,error:k,stepStartTime:Y,startTime:P,errorShot:v,lastHierarchy:this.lastHierarchy,driver:this.driver,serial:n.serial});if(this.reporter.logStep(re,E.originalText,E.type,"FAIL",k.message,v,{error:k.message,videoOffsetMs:Math.max(0,Y-P)},M,C),this.reporter.logError(`Step ${se} failed: ${k.message}`),n.continueOnError)console.log(ne.yellow("Continuing with next test step (--continue enabled)..."));else{let A=W.slice(F+1);if(A.length>0){console.log(ne.yellow(`
924
+ \u{1F6D1} Aborting dependent flow due to upstream failure at Step ${se}.`)),console.log(ne.gray(`Cascading skip applied to ${A.length} downstream step(s):`));for(let D=0;D<A.length;D++){let I=A[D],H=se+1+D;this.reporter.logStep(re,I.originalText,I.type,"SKIPPED",`Cascading skip: Precondition failed at Step ${se} (${E.originalText})`,void 0,{videoOffsetMs:Math.max(0,Date.now()-P)},0),console.log(ne.gray(` [Step ${H}/${W.length}] \u26AA SKIPPED: ${I.originalText}`))}}break}}y&&await y.sample(se,E.originalText)}}try{let Q=this.lastHierarchy;if(Q?.flat){let K=$r(Q.flat,this.screenDimensions.width,this.screenDimensions.height);if(K.length>0){console.log(ne.bold.yellow(`
925
+ \u{1F50D} Visual QA Sentinel Warnings (${K.length} detected):`));for(let V of K)console.log(ne.yellow(` \u2022 [${V.type}] ${V.description}`)),this.reporter.logError(`Visual Defect (${V.type}): ${V.description}`)}}}catch{}await this.memory.save(),n.resume&&N===0&&await this.checkpoints.clear(d);let O=this.memory.getStats();if(O.totalMappings>0&&console.log(ne.cyan(`\u{1F9E0} Knowledge Engine: ${O.totalMappings} active learned mapping(s) for ${O.package}`)),w){console.log(ne.cyan("\u{1F3AC} Finalizing video recording..."));let Q=ft.resolve(process.cwd(),"output"),K=ft.join(Q,`${d}-recording.mp4`),V=await this.driver.stopScreenRecording(w,K,n.serial);V&&Fs.existsSync(V)&&(this.reporter.setVideoPath?.(`${d}-recording.mp4`),console.log(ne.green(`\u2714 Screen recording saved to: output/${d}-recording.mp4`))),w=null}if(y){let Q=await y.stop();typeof this.reporter.setPerformanceMetrics=="function"&&this.reporter.setPerformanceMetrics(Q)}let L=((Date.now()-P)/1e3).toFixed(1),U=await this.reporter.generateReport(d);b&&(b.broadcastComplete(U),T&&T()),console.log(ne.bold.green(`
926
+ \u2728 PromptTest Script Execution Finished in ${L}s!`));let j=U.failedSteps===0?ne.bold.green:ne.bold.yellow;return console.log(j(`Result: ${U.passedSteps}/${U.totalSteps} passed (${U.failedSteps} failed, ${U.skippedSteps} skipped).`)),console.log(ne.cyan(`Report saved to output/${d}-report.html
927
+ `)),n.json&&console.log(JSON.stringify(U,null,2)),U}finally{if(w)try{await this.driver.stopScreenRecording(w,ft.join("output",`${d}-recording.mp4`),n.serial)}catch{}m&&Ot(m)}}async getCleanHierarchy(t){let e=await this.driver.getUiHierarchy(t),{flat:s}=be(e),n=s.find(o=>(o.text==="Dismiss"||o.contentDesc==="Dismiss")&&o.bounds.width>0);n&&(await this.driver.tap(n.bounds.centerX,n.bounds.centerY,t),await new Promise(o=>setTimeout(o,400)),e=await this.driver.getUiHierarchy(t),s=be(e).flat);let i=s.find(o=>o.contentDesc&&(o.contentDesc.startsWith("!, ")||o.contentDesc.includes("react.dev/link")));if(i){let o=i.bounds.x2-45,l=i.bounds.centerY;await this.driver.tap(o,l,t),await new Promise(c=>setTimeout(c,350)),e=await this.driver.getUiHierarchy(t),s=be(e).flat}let r=ke(s);return this.lastHierarchy={xml:e,flat:s,timestamp:Date.now(),hash:r},{xml:e,flat:s}}async ensureInViewport(t,e){let s=this.screenDimensions.width,n=this.screenDimensions.height,i=Math.round(s*.5),r=this.navBarHeight>0?n-this.navBarHeight:n,o=this.navBarHeight>0?r*.94:n*R.gestures.bottomNavThresholdRatio;if(t.bounds.centerY>o||t.bounds.y1>o)return!1;let c=t.bounds.y2>r,d=t.bounds.y1<Math.round(n*.04)&&t.bounds.centerY<Math.round(n*.06);return c?(await this.driver.swipe(i,Math.round(n*.75),i,Math.round(n*.45),250,e),await new Promise(p=>setTimeout(p,200)),this.lastHierarchy=null,!0):d?(await this.driver.swipe(i,Math.round(n*.35),i,Math.round(n*.65),250,e),await new Promise(p=>setTimeout(p,200)),this.lastHierarchy=null,!0):!1}async waitForTarget(t,e,s=3e3,n){if(this.lastHierarchy&&Date.now()-this.lastHierarchy.timestamp<R.polling.hierarchyCacheTtlMs){let l=this.matchNodes(this.lastHierarchy.flat,t,n);if(l.length>0)return{flat:this.lastHierarchy.flat,matched:l}}let i=Date.now();for(;Date.now()-i<s;){let{flat:l}=await this.getCleanHierarchy(e),c=this.matchNodes(l,t,n);if(c.length>0)return{flat:l,matched:c};await new Promise(d=>setTimeout(d,fr()))}let{flat:r}=await this.getCleanHierarchy(e),o=this.matchNodes(r,t,n);if(o.length===0&&(this.healEnabled||R.ai.selfHealing))try{let l=await this.semanticResolver.findSuggestions(t,r,{locale:this.activeLocale}),c=R.ai.confidenceThreshold||.75,d=l.find(p=>p.score>=c);if(d){let p=d.node||r.find(u=>(me(d.text,u.text)||me(d.text,u.contentDesc))&&u.bounds.width>0);if(p){console.log(ne.yellow(` \u{1F4A1} [Self-Healing] Auto-healed "${t}" \u2192 "${d.text}" (${Math.round(d.score*100)}% confidence, ${d.strategy})`)),n&&(n.healedFrom=t,n.healedTo=d.text,n.target=d.text);let u=p.text?"text":p.contentDesc?"contentDesc":"resourceId",h=this.lastHierarchy?.hash,m=Math.min(R.memory?.maxHealedConfidence??.65,d.score);this.memory.recordSuccess(n?.type||"TAP",t,d.text,u,m,t,h),o=[p]}}}catch(l){ct.warn(`Self-healing resolution failed: ${l.message}`)}return{flat:r,matched:o}}buildDiagnosticError(t,e,s){return Rr(t,e,s)}async executeStep(t,e,s){let n=Zr.get(t.type);if(!n)throw new Error(`No handler registered for step type: ${t.type}`);t.target&&t.target.includes("$")&&(t.target=Lt(t.target,Object.fromEntries(this.runtimeVariables))),t.value&&t.value.includes("$")&&(t.value=Lt(t.value,Object.fromEntries(this.runtimeVariables))),(t.type==="LAUNCH_APP"||t.type==="RESTART_APP")&&t.target&&(this.targetPackage=t.target);let i={targetPackage:this.targetPackage,driver:this.driver,memory:this.memory,serial:e,screenDimensions:this.screenDimensions,navBarHeight:this.navBarHeight,lastHierarchy:this.lastHierarchy,setLastHierarchy:r=>{this.lastHierarchy=r},getCleanHierarchy:r=>this.getCleanHierarchy(r),waitForTarget:(r,o,l,c)=>this.waitForTarget(r,o,l,c),matchNodes:(r,o,l)=>this.matchNodes(r,o,l),ensureInViewport:(r,o)=>this.ensureInViewport(r,o),buildDiagnosticError:(r,o,l)=>this.buildDiagnosticError(r,o,l),findTargetInputField:(r,o,l)=>this.findTargetInputField(r,o,l),upcomingSteps:s,variables:this.runtimeVariables,loopStack:this.loopStack,executeChildSteps:async(r,o)=>{for(let l=0;l<r.length;l++){let c=r[l];await this.executeStep(c,o.serial,r.slice(l+1))}}};return n.execute(t,i)}findTargetInputField(t,e,s){return Mr(t,e,s,this.memory)}matchNodes(t,e,s){return Ss(t,e,this.memory,this.activeLocale,s,this.lastHierarchy?.hash)}},Vt=new st});var bi={};Qe(bi,{calculatePercentile:()=>tr,formatBenchmarkTable:()=>Ro,generateSyntheticHierarchy:()=>fi,runBenchmark:()=>Lo,summarizeMetrics:()=>Os});import ze from"chalk";function tr(a,t){if(a.length===0)return 0;let e=[...a].sort((n,i)=>n-i),s=Math.min(e.length-1,Math.max(0,Math.floor(t/100*(e.length-1))));return Math.round(e[s]*100)/100}function Os(a){if(a.length===0)return{minMs:0,maxMs:0,avgMs:0,p50Ms:0,p95Ms:0,iterations:0,durations:[]};let t=Math.round(Math.min(...a)*100)/100,e=Math.round(Math.max(...a)*100)/100,s=a.reduce((o,l)=>o+l,0),n=Math.round(s/a.length*100)/100,i=tr(a,50),r=tr(a,95);return{minMs:t,maxMs:e,avgMs:n,p50Ms:i,p95Ms:r,iterations:a.length,durations:a}}function fi(a=1e3){let t=[];t.push({index:0,className:"android.widget.FrameLayout",packageName:"com.app",text:"",resourceId:"android:id/content",contentDesc:"Main Screen Root",clickable:!1,enabled:!0,focused:!1,scrollable:!1,password:!1,selected:!1,children:[],bounds:{x1:0,y1:0,x2:1080,y2:2400,width:1080,height:2400,centerX:540,centerY:1200}}),t.push({index:1,className:"android.widget.TextView",packageName:"com.app",text:"Apex Coaching Academy - Dashboard",resourceId:"com.app:id/title_header",contentDesc:"Header Title",clickable:!1,enabled:!0,focused:!1,scrollable:!1,password:!1,selected:!1,children:[],bounds:{x1:40,y1:100,x2:1040,y2:220,width:1e3,height:120,centerX:540,centerY:160}});let e=["android.widget.TextView","android.widget.Button","android.widget.EditText","android.widget.ImageView","android.view.ViewGroup","android.widget.LinearLayout","android.widget.CheckBox","android.widget.Switch"],s=["Sign In","Email Address","Password","Submit","Save Changes","Settings","Profile","Notifications","Batch 10A","Mathematics","Physics","Attendance","Export PDF","Delete Record","Cancel","Next","Back","Search","Filter","Refresh"];for(let n=2;n<a;n++){let i=e[n%e.length],r=i.includes("Button")||i.includes("CheckBox")||i.includes("Switch"),o=s[n%s.length],l=`${o} #${n}`,c=Math.min(2400,250+n*20%2e3),d=50+n*30%900;t.push({index:n,className:i,packageName:"com.app",text:i.includes("ImageView")?"":l,resourceId:`com.app:id/element_${n}`,contentDesc:i.includes("ImageView")?`Icon of ${o}`:"",clickable:r,enabled:n%25!==0,focused:!1,scrollable:!1,password:o.toLowerCase().includes("password"),selected:!1,children:[],bounds:{x1:d,y1:c,x2:d+200,y2:c+80,width:200,height:80,centerX:d+100,centerY:c+40}})}return t}async function Lo(a=oe,t={}){let e=t.iterations||5,s=Date.now(),n=t.serial,i="Synthetic Mock Android Environment",r=!1;if(!t.forceMock)try{n=await a.ensureDefaultDevice(t.serial),n&&(i=await a.getDeviceModel(n),r=!0)}catch{r=!1}let o=[];for(let m=0;m<e;m++){let b=performance.now();r?await a.captureScreenshot(n):(await new Promise(T=>setTimeout(T,15)),Buffer.alloc(1024*1024)),o.push(performance.now()-b)}let l=[];for(let m=0;m<e;m++){let b=performance.now();r?await a.getUiHierarchy(n):await new Promise(T=>setTimeout(T,25)),l.push(performance.now()-b)}let c=fi(1e3),d=new gt,p=["Submit","Email Address","Mathematics","Save Changes","NonExistent Query Fallback"],u=[];for(let m=0;m<e;m++){let b=p[m%p.length],T=performance.now();Ss(c,b,d),u.push(performance.now()-T)}let h=Date.now()-s;return{timestamp:new Date().toISOString(),device:i,package:t.packageName,screencap:Os(o),uiHierarchy:Os(l),nodeMatching:Os(u),totalDurationMs:h}}function Ro(a){let t=(i,r)=>{let o=`${r.minMs.toFixed(1)}ms`.padStart(9),l=`${r.avgMs.toFixed(1)}ms`.padStart(9),c=`${r.p50Ms.toFixed(1)}ms`.padStart(9),d=`${r.p95Ms.toFixed(1)}ms`.padStart(9),p=`${r.maxMs.toFixed(1)}ms`.padStart(9);return`\u2502 ${i.padEnd(28)} \u2502 ${o} \u2502 ${l} \u2502 ${c} \u2502 ${d} \u2502 ${p} \u2502`};return["",ze.bold.cyan("\u26A1 PromptTest Performance Diagnostic Benchmark"),ze.gray("Device: ")+ze.bold.white(a.device)+ze.gray(` | Samples: ${a.screencap.iterations} runs`),"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",`\u2502 ${ze.bold("Operation".padEnd(28))} \u2502 ${ze.bold("Min".padStart(9))} \u2502 ${ze.bold("Avg".padStart(9))} \u2502 ${ze.bold("p50".padStart(9))} \u2502 ${ze.bold("p95".padStart(9))} \u2502 ${ze.bold("Max".padStart(9))} \u2502`,"\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524",t("Device Screencap",a.screencap),t("UI Hierarchy Dump (XML)",a.uiHierarchy),t("Node Matching (1k nodes)",a.nodeMatching),"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",ze.gray("Total Benchmark Wall-Time: ")+ze.bold.green(`${a.totalDurationMs}ms
878
928
  `)].join(`
879
- `)}var kn=de(()=>{"use strict";Fe();ut();ft()});var $i={};Tt($i,{startRepl:()=>Dn});import $r from"readline";import Qt from"fs";import Ai from"path";import ge from"chalk";async function Dn(c=re,t,e={}){let s=await c.ensureDefaultDevice(e.serial),n=t||new it(c),r={history:[],successfulSteps:[]},i=e.input||process.stdin,o=e.output||process.stdout,a=$r.createInterface({input:i,output:o,prompt:ge.bold.cyan("prompttest> ")}),l=!1,d=()=>{if(!l)try{a.prompt()}catch{}},p=h=>{o.write(h+`
880
- `)};return p(ge.bold.cyan(`
881
- \u26A1 PromptTest Interactive Live REPL / Playground (v1.1.0)`)),p(ge.gray("Device: ")+ge.bold.white(s||"No device connected (offline mock)")+(e.packageName?ge.gray(" | Package: ")+ge.bold.white(e.packageName):"")),p(ge.gray(`Type natural language instructions (e.g. Tap 'Sign In'), or REPL commands: "inspect", "screenshot", "history", "save <file.txt>", "exit".
882
- `)),new Promise(h=>{d(),a.on("line",async u=>{let m=u.trim();if(!m){d();return}r.history.push(m);let f=m.toLowerCase();if(f==="exit"||f==="quit"||f===":q"){l=!0,p(ge.yellow("Exiting PromptTest REPL.")),a.close();return}if(f==="help"||f==="?"){p(ge.bold.white(`
883
- \u{1F4D6} PromptTest REPL Commands:`)),p(` \u2022 ${ge.cyan("Tap '...'")} / ${ge.cyan("Type '...' into '...'")} : Execute action live on device`),p(` \u2022 ${ge.cyan("Verify '...' is visible")} : Assert element visibility`),p(` \u2022 ${ge.cyan("inspect")} / ${ge.cyan("tree")} : List interactive elements and input fields on screen`),p(` \u2022 ${ge.cyan("screenshot [path]")} : Capture current screen to image file`),p(` \u2022 ${ge.cyan("history")} : Show executed commands in current session`),p(` \u2022 ${ge.cyan("save <file.txt>")} : Save successful steps into a reusable spec file`),p(` \u2022 ${ge.cyan("clear")} : Clear session history`),p(` \u2022 ${ge.cyan("exit")} : Quit the REPL
884
- `),d();return}if(f==="history"){p(ge.bold.white(`
885
- \u{1F4DC} Session History (${r.history.length} commands):`)),r.history.forEach((x,v)=>{p(` ${v+1}. ${x}`)}),p(""),d();return}if(f==="clear"){r.successfulSteps=[],r.history=[],p(ge.green("Session history cleared.")),d();return}if(f.startsWith("save")){let v=m.split(/\s+/)[1]||"output/repl_spec.txt";try{let S=Ai.dirname(v);S&&!Qt.existsSync(S)&&Qt.mkdirSync(S,{recursive:!0});let y=r.successfulSteps.map((k,C)=>`${C+1}. ${k}`).join(`
886
- `);Qt.writeFileSync(v,y,"utf-8"),p(ge.bold.green(`\u2714 Saved ${r.successfulSteps.length} step(s) to ${v}`))}catch(S){let y=S;p(ge.red(`Failed to save spec: ${y.message}`))}d();return}if(f.startsWith("screenshot")){let v=m.split(/\s+/)[1]||`output/repl_screenshot_${Date.now()}.png`;try{let S=Ai.dirname(v);S&&!Qt.existsSync(S)&&Qt.mkdirSync(S,{recursive:!0});let y=await c.captureScreenshot(s);Qt.writeFileSync(v,y),p(ge.bold.green(`\u2714 Screenshot saved to ${v}`))}catch(S){let y=S;p(ge.red(`Failed to capture screenshot: ${y.message}`))}d();return}if(f==="inspect"||f==="tree"){try{p(ge.gray("Dumping UI hierarchy..."));let x=await c.getUiHierarchy(s),{flat:v}=ve(x),S=v.filter(y=>y.clickable||y.className.includes("Button")||y.className.includes("EditText"));p(ge.bold.white(`
887
- \u{1F50D} Visible Interactive Elements (${S.length} found):`)),S.slice(0,25).forEach((y,k)=>{let C=y.text||y.contentDesc||y.resourceId||`<Unnamed ${y.className}>`,j=y.className.split(".").pop()||y.className;p(` [${k+1}] (${j}) "${C}" at (${y.bounds.centerX}, ${y.bounds.centerY})`)}),S.length>25&&p(ge.gray(` ... and ${S.length-25} more elements.`)),p("")}catch(x){let v=x;p(ge.red(`Failed to inspect hierarchy: ${v.message}`))}d();return}try{let x=Jt(m);if(x.length===0){p(ge.yellow(`Could not parse natural language step: "${m}"`)),d();return}let v=x[0],S=performance.now();await n.executeStep(v,s);let y=Math.round(performance.now()-S);r.successfulSteps.push(m),p(ge.green(`\u2714 [${v.type}] Completed in ${y}ms: `)+ge.white(v.originalText))}catch(x){let v=x;p(ge.red(`\u2716 Step failed: ${v.message}`))}d()}),a.on("close",()=>{l=!0,h(r)})})}var Es=de(()=>{"use strict";Fe();Ht();ut();Ke()});var Ci={};Tt(Ci,{calculateReleaseConfidence:()=>$n,generateAggregatedHtml:()=>Ir,mergeReports:()=>Cr});import ls from"fs";import An from"path";function $n(c){let t=c.totalSteps||0,e=c.passedSteps||0,s=c.failedSteps||0,n=c.skippedSteps||0,r=c.errorsDetected?c.errorsDetected.length:0;if(t===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=e/t,o=Math.min(30,r*10),a=s>0?35+Math.min(25,s*5):0,l=Math.round(i*80+20-o-a);l=Math.max(0,Math.min(100,l));let d="F",p="RELEASE_BLOCKER",h="";return l>=90?(d="A",p="READY_FOR_RELEASE",h="High quality gate confidence. All critical assertion paths verified."):l>=80?(d="B",p="READY_FOR_RELEASE",h="Acceptable release confidence. Minor non-critical or optional warnings observed."):l>=70?(d="C",p="PROCEED_WITH_CAUTION",h="Elevated risk. Visual QA warnings or multiple non-critical step failures detected."):(d="F",p="RELEASE_BLOCKER",h=`Critical test failures (${s}) or visual defects (${r}) block safe release.`),{score:l,grade:d,verdict:p,rationale:h,metrics:{passRate:Math.round(i*100)/100,criticalFailures:s,visualDefects:r,totalSteps:t,skippedSteps:n}}}function Cr(c="output"){let t=[],e=u=>{if(!ls.existsSync(u))return;let m=ls.readdirSync(u,{withFileTypes:!0});for(let f of m){let x=An.join(u,f.name);f.isDirectory()?e(x):f.isFile()&&(f.name.endsWith("-results.json")||f.name.endsWith("-report.json"))&&t.push(x)}};if(Array.isArray(c))for(let u of c)ls.existsSync(u)&&(ls.statSync(u).isDirectory()?e(u):t.push(u));else e(c);let s=[],n=new Set,r=0,i=0,o=0,a=0,l=[];for(let u of t)try{let m=ls.readFileSync(u,"utf-8"),f=JSON.parse(m),x=$n(f),v=f.deviceId||"unknown-device";n.add(v),r+=f.totalSteps||0,i+=f.passedSteps||0,o+=f.failedSteps||0,a+=f.skippedSteps||0,f.errorsDetected&&l.push(...f.errorsDetected),s.push({deviceId:v,suiteName:An.basename(u,An.extname(u)).replace(/-results|-report/,""),packageName:f.packageName||"unknown",totalSteps:f.totalSteps||0,passedSteps:f.passedSteps||0,failedSteps:f.failedSteps||0,skippedSteps:f.skippedSteps||0,durationSeconds:f.durationSeconds||0,confidenceScore:x.score,confidenceGrade:x.grade,sourceFile:u})}catch{}let d=r>0?Math.round(i/r*100)/100:0,p={packageName:s[0]?.packageName||"Enterprise Matrix",deviceId:`${n.size} devices`,startTime:new Date().toISOString(),endTime:new Date().toISOString(),durationSeconds:s.reduce((u,m)=>u+m.durationSeconds,0),totalSteps:r,passedSteps:i,failedSteps:o,skippedSteps:a,warnSteps:0,errorsDetected:l,steps:[]},h=$n(p);return{timestamp:new Date().toISOString(),totalRuns:s.length,uniqueDevices:Array.from(n),totalSteps:r,passedSteps:i,failedSteps:o,skippedSteps:a,overallPassRate:d,overallConfidence:h,deviceSummaries:s}}function Ir(c){let t=c.overallConfidence,e=t.grade==="A"?"#10B981":t.grade==="B"?"#3B82F6":t.grade==="C"?"#F59E0B":"#EF4444",s=c.deviceSummaries.map(n=>{let r=n.confidenceGrade==="A"?"#10B981":n.confidenceGrade==="B"?"#3B82F6":n.confidenceGrade==="C"?"#F59E0B":"#EF4444";return`
929
+ `)}var sr=ie(()=>{"use strict";Re();et();Pt()});var Si={};Qe(Si,{startRepl:()=>nr});import Mo from"readline";import ns from"fs";import yi from"path";import he from"chalk";async function nr(a=oe,t,e={}){let s=await a.ensureDefaultDevice(e.serial),n=t||new st(a),i={history:[],successfulSteps:[]},r=e.input||process.stdin,o=e.output||process.stdout,l=Mo.createInterface({input:r,output:o,prompt:he.bold.cyan("prompttest> ")}),c=!1,d=()=>{if(!c)try{l.prompt()}catch{}},p=u=>{o.write(u+`
930
+ `)};return p(he.bold.cyan(`
931
+ \u26A1 PromptTest Interactive Live REPL / Playground (v1.1.0)`)),p(he.gray("Device: ")+he.bold.white(s||"No device connected (offline mock)")+(e.packageName?he.gray(" | Package: ")+he.bold.white(e.packageName):"")),p(he.gray(`Type natural language instructions (e.g. Tap 'Sign In'), or REPL commands: "inspect", "screenshot", "history", "save <file.txt>", "exit".
932
+ `)),new Promise(u=>{d(),l.on("line",async h=>{let m=h.trim();if(!m){d();return}i.history.push(m);let b=m.toLowerCase();if(b==="exit"||b==="quit"||b===":q"){c=!0,p(he.yellow("Exiting PromptTest REPL.")),l.close();return}if(b==="help"||b==="?"){p(he.bold.white(`
933
+ \u{1F4D6} PromptTest REPL Commands:`)),p(` \u2022 ${he.cyan("Tap '...'")} / ${he.cyan("Type '...' into '...'")} : Execute action live on device`),p(` \u2022 ${he.cyan("Verify '...' is visible")} : Assert element visibility`),p(` \u2022 ${he.cyan("inspect")} / ${he.cyan("tree")} : List interactive elements and input fields on screen`),p(` \u2022 ${he.cyan("screenshot [path]")} : Capture current screen to image file`),p(` \u2022 ${he.cyan("history")} : Show executed commands in current session`),p(` \u2022 ${he.cyan("save <file.txt>")} : Save successful steps into a reusable spec file`),p(` \u2022 ${he.cyan("clear")} : Clear session history`),p(` \u2022 ${he.cyan("exit")} : Quit the REPL
934
+ `),d();return}if(b==="history"){p(he.bold.white(`
935
+ \u{1F4DC} Session History (${i.history.length} commands):`)),i.history.forEach((T,w)=>{p(` ${w+1}. ${T}`)}),p(""),d();return}if(b==="clear"){i.successfulSteps=[],i.history=[],p(he.green("Session history cleared.")),d();return}if(b.startsWith("save")){let w=m.split(/\s+/)[1]||"output/repl_spec.txt";try{let S=yi.dirname(w);S&&!ns.existsSync(S)&&ns.mkdirSync(S,{recursive:!0});let y=i.successfulSteps.map((P,N)=>`${N+1}. ${P}`).join(`
936
+ `);ns.writeFileSync(w,y,"utf-8"),p(he.bold.green(`\u2714 Saved ${i.successfulSteps.length} step(s) to ${w}`))}catch(S){let y=S;p(he.red(`Failed to save spec: ${y.message}`))}d();return}if(b.startsWith("screenshot")){let w=m.split(/\s+/)[1]||`output/repl_screenshot_${Date.now()}.png`;try{let S=yi.dirname(w);S&&!ns.existsSync(S)&&ns.mkdirSync(S,{recursive:!0});let y=await a.captureScreenshot(s);ns.writeFileSync(w,y),p(he.bold.green(`\u2714 Screenshot saved to ${w}`))}catch(S){let y=S;p(he.red(`Failed to capture screenshot: ${y.message}`))}d();return}if(b==="inspect"||b==="tree"){try{p(he.gray("Dumping UI hierarchy..."));let T=await a.getUiHierarchy(s),{flat:w}=be(T),S=w.filter(y=>y.clickable||y.className.includes("Button")||y.className.includes("EditText"));p(he.bold.white(`
937
+ \u{1F50D} Visible Interactive Elements (${S.length} found):`)),S.slice(0,25).forEach((y,P)=>{let N=y.text||y.contentDesc||y.resourceId||`<Unnamed ${y.className}>`,O=y.className.split(".").pop()||y.className;p(` [${P+1}] (${O}) "${N}" at (${y.bounds.centerX}, ${y.bounds.centerY})`)}),S.length>25&&p(he.gray(` ... and ${S.length-25} more elements.`)),p("")}catch(T){let w=T;p(he.red(`Failed to inspect hierarchy: ${w.message}`))}d();return}try{let T=St(m);if(T.length===0){p(he.yellow(`Could not parse natural language step: "${m}"`)),d();return}let w=T[0],S=performance.now();await n.executeStep(w,s);let y=Math.round(performance.now()-S);i.successfulSteps.push(m),p(he.green(`\u2714 [${w.type}] Completed in ${y}ms: `)+he.white(w.originalText))}catch(T){let w=T;p(he.red(`\u2716 Step failed: ${w.message}`))}d()}),l.on("close",()=>{c=!0,u(i)})})}var _s=ie(()=>{"use strict";Re();Yt();et();Ve()});var wi={};Qe(wi,{calculateReleaseConfidence:()=>ir,generateAggregatedHtml:()=>Oo,mergeReports:()=>Fo});import fs from"fs";import rr from"path";function ir(a){let t=a.totalSteps||0,e=a.passedSteps||0,s=a.failedSteps||0,n=a.skippedSteps||0,i=a.errorsDetected?a.errorsDetected.length:0;if(t===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 r=e/t,o=Math.min(30,i*10),l=s>0?35+Math.min(25,s*5):0,c=Math.round(r*80+20-o-l);c=Math.max(0,Math.min(100,c));let d="F",p="RELEASE_BLOCKER",u="";return c>=90?(d="A",p="READY_FOR_RELEASE",u="High quality gate confidence. All critical assertion paths verified."):c>=80?(d="B",p="READY_FOR_RELEASE",u="Acceptable release confidence. Minor non-critical or optional warnings observed."):c>=70?(d="C",p="PROCEED_WITH_CAUTION",u="Elevated risk. Visual QA warnings or multiple non-critical step failures detected."):(d="F",p="RELEASE_BLOCKER",u=`Critical test failures (${s}) or visual defects (${i}) block safe release.`),{score:c,grade:d,verdict:p,rationale:u,metrics:{passRate:Math.round(r*100)/100,criticalFailures:s,visualDefects:i,totalSteps:t,skippedSteps:n}}}function Fo(a="output"){let t=[],e=h=>{if(!fs.existsSync(h))return;let m=fs.readdirSync(h,{withFileTypes:!0});for(let b of m){let T=rr.join(h,b.name);b.isDirectory()?e(T):b.isFile()&&(b.name.endsWith("-results.json")||b.name.endsWith("-report.json"))&&t.push(T)}};if(Array.isArray(a))for(let h of a)fs.existsSync(h)&&(fs.statSync(h).isDirectory()?e(h):t.push(h));else e(a);let s=[],n=new Set,i=0,r=0,o=0,l=0,c=[];for(let h of t)try{let m=fs.readFileSync(h,"utf-8"),b=JSON.parse(m),T=ir(b),w=b.deviceId||"unknown-device";n.add(w),i+=b.totalSteps||0,r+=b.passedSteps||0,o+=b.failedSteps||0,l+=b.skippedSteps||0,b.errorsDetected&&c.push(...b.errorsDetected),s.push({deviceId:w,suiteName:rr.basename(h,rr.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 d=i>0?Math.round(r/i*100)/100:0,p={packageName:s[0]?.packageName||"Enterprise Matrix",deviceId:`${n.size} devices`,startTime:new Date().toISOString(),endTime:new Date().toISOString(),durationSeconds:s.reduce((h,m)=>h+m.durationSeconds,0),totalSteps:i,passedSteps:r,failedSteps:o,skippedSteps:l,warnSteps:0,errorsDetected:c,steps:[]},u=ir(p);return{timestamp:new Date().toISOString(),totalRuns:s.length,uniqueDevices:Array.from(n),totalSteps:i,passedSteps:r,failedSteps:o,skippedSteps:l,overallPassRate:d,overallConfidence:u,deviceSummaries:s}}function Oo(a){let t=a.overallConfidence,e=t.grade==="A"?"#10B981":t.grade==="B"?"#3B82F6":t.grade==="C"?"#F59E0B":"#EF4444",s=a.deviceSummaries.map(n=>{let i=n.confidenceGrade==="A"?"#10B981":n.confidenceGrade==="B"?"#3B82F6":n.confidenceGrade==="C"?"#F59E0B":"#EF4444";return`
888
938
  <tr style="border-bottom: 1px solid #334155;">
889
939
  <td style="padding: 12px; font-weight: bold; color: #F8FAFC;">${n.deviceId}</td>
890
940
  <td style="padding: 12px; color: #94A3B8;">${n.suiteName}</td>
@@ -894,7 +944,7 @@ data: ${JSON.stringify({status:"connected",time:Date.now()})}
894
944
  <td style="padding: 12px; text-align: center; color: #EF4444; font-weight: bold;">${n.failedSteps}</td>
895
945
  <td style="padding: 12px; text-align: center; color: #64748B;">${n.skippedSteps}</td>
896
946
  <td style="padding: 12px; text-align: center;">
897
- <span style="background: ${r}22; color: ${r}; border: 1px solid ${r}; padding: 4px 10px; border-radius: 9999px; font-weight: bold; font-size: 12px;">
947
+ <span style="background: ${i}22; color: ${i}; border: 1px solid ${i}; padding: 4px 10px; border-radius: 9999px; font-weight: bold; font-size: 12px;">
898
948
  Grade ${n.confidenceGrade} (${n.confidenceScore}%)
899
949
  </span>
900
950
  </td>
@@ -917,12 +967,12 @@ data: ${JSON.stringify({status:"connected",time:Date.now()})}
917
967
  <div class="card header-grid">
918
968
  <div>
919
969
  <h1 style="margin-top: 0; color: #38BDF8;">PromptTest Executive Quality Matrix</h1>
920
- <p style="color: #94A3B8; margin-bottom: 20px;">Generated at ${c.timestamp} across ${c.uniqueDevices.length} target device(s).</p>
970
+ <p style="color: #94A3B8; margin-bottom: 20px;">Generated at ${a.timestamp} across ${a.uniqueDevices.length} target device(s).</p>
921
971
  <div style="display: flex; gap: 24px;">
922
- <div><span style="font-size: 24px; font-weight: bold; color: #F8FAFC;">${c.totalRuns}</span><br><span style="color: #94A3B8; font-size: 12px;">Test Suites</span></div>
923
- <div><span style="font-size: 24px; font-weight: bold; color: #10B981;">${c.passedSteps}</span><br><span style="color: #94A3B8; font-size: 12px;">Passed Steps</span></div>
924
- <div><span style="font-size: 24px; font-weight: bold; color: #EF4444;">${c.failedSteps}</span><br><span style="color: #94A3B8; font-size: 12px;">Failed Steps</span></div>
925
- <div><span style="font-size: 24px; font-weight: bold; color: #38BDF8;">${Math.round(c.overallPassRate*100)}%</span><br><span style="color: #94A3B8; font-size: 12px;">Overall Pass Rate</span></div>
972
+ <div><span style="font-size: 24px; font-weight: bold; color: #F8FAFC;">${a.totalRuns}</span><br><span style="color: #94A3B8; font-size: 12px;">Test Suites</span></div>
973
+ <div><span style="font-size: 24px; font-weight: bold; color: #10B981;">${a.passedSteps}</span><br><span style="color: #94A3B8; font-size: 12px;">Passed Steps</span></div>
974
+ <div><span style="font-size: 24px; font-weight: bold; color: #EF4444;">${a.failedSteps}</span><br><span style="color: #94A3B8; font-size: 12px;">Failed Steps</span></div>
975
+ <div><span style="font-size: 24px; font-weight: bold; color: #38BDF8;">${Math.round(a.overallPassRate*100)}%</span><br><span style="color: #94A3B8; font-size: 12px;">Overall Pass Rate</span></div>
926
976
  </div>
927
977
  </div>
928
978
  <div class="score-badge">
@@ -954,71 +1004,167 @@ data: ${JSON.stringify({status:"connected",time:Date.now()})}
954
1004
  </table>
955
1005
  </div>
956
1006
  </body>
957
- </html>`}var Cn=de(()=>{"use strict"});var Ln={};Tt(Ln,{buildFeedbackUrl:()=>Rn,startFeedbackSession:()=>Or});import Mr from"readline";import qt from"os";import Se from"chalk";function Rn(c){let t=(c.message||"Feedback").split(`
958
- `)[0].trim(),e=t.length>70?t.substring(0,67)+"...":t,s=c.rating?"\u2B50".repeat(c.rating):"";if(c.category==="feature"){let i=new URLSearchParams;return i.set("template","2_feature_request.yml"),i.set("title",`[Feature]: ${e}`),i.set("problem",`User feedback submission${s?` (${s} ${c.rating}/5)`:""}:
959
- ${c.message}`),i.set("proposed_solution",c.message),i.set("category","Plain-English DSL / Syntax"),`${In}/issues/new?${i.toString()}`}if(c.category==="bug"){let i=new URLSearchParams;return i.set("template","1_bug_report.yml"),i.set("title",`[Bug]: ${e}`),i.set("description",c.message),i.set("command_run","npx prompttest"),i.set("environment",`${qt.type()} ${qt.release()} (${qt.arch()}), Node: ${process.version}`),`${In}/issues/new?${i.toString()}`}let n=new URLSearchParams;n.set("template","3_feedback.yml");let r=c.rating?` ${"\u2B50".repeat(c.rating)}`:"";if(n.set("title",`[Feedback${r}]: ${e}`),n.set("message",c.message),typeof c.rating=="number"){let o={5:0,4:1,3:2,2:3,1:4}[c.rating]??0;n.set("rating",String(o))}return n.set("environment",`${qt.type()} ${qt.release()} (${qt.arch()}), Node: ${process.version}`),`${In}/issues/new?${n.toString()}`}function Nn(c,t){return new Promise(e=>{if(c.closed){e("");return}try{c.question(t,s=>{e(s.trim())})}catch{e("")}})}async function Or(c={}){let t=c.input||process.stdin,e=c.output||process.stdout,s=(r="")=>{e.write(r+`
960
- `)};if(c.directMessage&&c.directMessage.trim()){let r=c.category||"general",i=Rn({category:r,message:c.directMessage.trim()});return s(Se.bold.cyan(`
961
- \u26A1 PromptTest \u2014 Community Feedback`)),s(Se.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(Se.green("\u2714 Thank you for your feedback! Here is your 1-click submission link:")),s(Se.bold.underline.cyan(i)),s(Se.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
962
- `)),i}let n=Mr.createInterface({input:t,output:e});try{s(Se.bold.cyan(`
963
- \u26A1 PromptTest \u2014 Community Feedback & Suggestions`)),s(Se.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(Se.white("We want to make PromptTest the best mobile QA engine in your toolchain.")),s(Se.white(`Your feedback goes directly to our open community tracker!
964
- `)),s(Se.bold.white("How would you rate your experience with PromptTest?")),s(` ${Se.cyan("[1]")} \u2B50\u2B50\u2B50\u2B50\u2B50 (5/5) \u2014 Loving it!`),s(` ${Se.cyan("[2]")} \u2B50\u2B50\u2B50\u2B50 (4/5) \u2014 Great, but needs minor improvements`),s(` ${Se.cyan("[3]")} \u2B50\u2B50\u2B50 (3/5) \u2014 Okay, encountered some friction`),s(` ${Se.cyan("[4]")} \u2B50\u2B50 (2/5) \u2014 Frustrating / missing key features`),s(` ${Se.cyan("[5]")} \u2B50 (1/5) \u2014 Not working for me`);let r=await Nn(n,Se.bold.cyan(`
965
- Select rating [1-5] (default: 1): `)),i=parseInt(r,10),a={1:5,2:4,3:3,4:2,5:1}[i]||5;s(Se.bold.white(`
966
- What type of feedback would you like to share?`)),s(` ${Se.cyan("[1]")} \u{1F4A1} Feature Request or syntax suggestion`),s(` ${Se.cyan("[2]")} \u{1F41B} Bug report or unexpected error`),s(` ${Se.cyan("[3]")} \u{1F4AC} General feedback, question, or compliment`);let l=await Nn(n,Se.bold.cyan(`
967
- Select category [1-3] (default: 1): `)),d="feature";l==="2"?d="bug":l==="3"&&(d="general"),s(Se.bold.white(`
968
- Please share your thoughts, ideas, or feedback:`));let h=await Nn(n,Se.bold.cyan("> "))||"Overall great experience with PromptTest!",u=Rn({rating:a,category:d,message:h});return s(Se.gray(`
969
- \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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(Se.green.bold("\u2728 Thank you! We generated your 1-click submission:")),s(Se.bold.underline.cyan(u)),s(Se.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
970
- `)),u}finally{try{n.close()}catch{}}}var In,ks=de(()=>{"use strict";In="https://github.com/shriramsingh/prompttest-community"});Fe();zt();Ht();Ke();ft();mt();import Le from"fs";import _e from"path";import R from"chalk";Ht();Fe();zt();ft();mt();Kt();ns();os();Ht();Fe();zt();ft();mt();Kt();ns();Vt();rs();os();kt();Ke();st();ms();Be();Be();mn();wn();xs();ut();yn();kn();cs();Es();Cn();kt();import Zt from"os";import Ve from"chalk";var Nr="https://github.com/shriramsingh/prompttest-community/issues/new";function Rr(c){if(!c)return"";let t=c;try{let e=Zt.homedir();e&&e.length>2&&(t=t.split(e).join("~"))}catch{}return t=t.replace(/([a-zA-Z]:\\Users\\[^\\]+)/gi,"~"),t=t.replace(/(\/(?:home|Users)\/[^/]+)/g,"~"),t.trim()}function Ni(c){if(!c||c instanceof Et&&c.exitCode===1)return!1;let t=c,e=typeof t.message=="string"?t.message:String(c);return!(e.includes("Step")&&(e.includes("failed")||e.includes("timed out")))}function Lr(c){let t=c.repoUrl||Nr,e=(c.errorMessage||"Unexpected internal error").split(`
971
- `)[0].trim(),s=`[Crash]: ${e}`,n=s.length>80?s.substring(0,77)+"...":s,r=`${Zt.type()} ${Zt.release()} (${Zt.arch()})`,i=process.version,o=`${r}, Node: ${i}`,a=Rr(c.stack||c.errorMessage),l=a.length>600?a.substring(0,597)+"...":a,d=new URLSearchParams;return d.set("template","1_bug_report.yml"),d.set("title",n),d.set("description",`PromptTest encountered an unexpected error: ${e}`),c.command&&d.set("command_run",c.command),d.set("environment",o),l&&d.set("logs",l),`${t}?${d.toString()}`}function Ps(c,t={}){let e=c&&typeof c=="object"?c:{},s=e.message||String(c),n=e.stack,r=t.command||(process.argv.slice(2).length>0?`npx prompttest ${process.argv.slice(2).join(" ")}`:"npx prompttest"),i=Lr({errorMessage:s,stack:n,command:r,exitCode:t.exitCode}),o=`${Zt.type()} ${Zt.arch()}`,a=process.version;console.error(),console.error(Ve.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(Ve.red.bold("\u{1F4A5} PromptTest Encountered an Unexpected Error / Crash")),console.error(Ve.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(Ve.white("Error: ")+Ve.red(s.split(`
972
- `)[0])),console.error(Ve.white("Command: ")+Ve.gray(r)),console.error(Ve.white("Environment: ")+Ve.gray(`${o} | Node: ${a}`)),console.error(),console.error(Ve.cyan("\u{1F4A1} Think this is a bug in PromptTest? You can report it in 1 click (pre-filled):")),console.error(Ve.bold.underline.cyan(i)),console.error(Ve.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()}Fe();Ht();zt();Kt();Es();cs();Ke();import Fr from"readline";import Mn from"fs";import On from"path";import B from"chalk";async function _r(c=re){try{let e=(await c.getConnectedDevices()).find(r=>r.state==="device");if(!e)return{isOnline:!1};let s=e.serial,n;try{let r=await c.getForegroundPackage(s);r&&!r.includes("launcher")&&!r.includes("systemui")&&(n=r)}catch{}return{device:e,serial:s,activePackage:n,isOnline:!0}}catch{return{isOnline:!1}}}function Hr(c=process.cwd()){let t=[],e=[On.join(c,"specs"),c];for(let s of e)if(Mn.existsSync(s)&&Mn.statSync(s).isDirectory())try{let n=Mn.readdirSync(s);for(let r of n)if(r.endsWith(".txt")||r.endsWith(".spec")||r.endsWith(".prompt")){let i=On.relative(c,On.join(s,r)).replace(/\\/g,"/");t.includes(i)||t.push(i)}}catch{}return t.sort()}function vt(c,t){return new Promise(e=>{c.question(t,s=>{e(s.trim())})})}async function Ri(c={}){let t=c.driver||re,e=c.runner||Bt,s=c.input||process.stdin,n=c.output||process.stdout,r=Fr.createInterface({input:s,output:n}),i=(o="")=>{n.write(o+`
973
- `)};try{i(B.bold.cyan(`
974
- \u26A1 PromptTest \u2014 Autonomous Mobile Testing Brain`)),i(B.gray("Inspecting device connectivity and active application..."));let o=await _r(t);switch(i(B.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(B.white("\u{1F4F1} Target Device: ")+B.bold.green(`${o.serial}`)+(o.device.model?B.gray(` (${o.device.model})`):"")+B.green(" [Online]")):i(B.white("\u{1F4F1} Target Device: ")+B.yellow("No authorized device online (Check USB/Wi-Fi)")),o.activePackage?i(B.white("\u{1F3AF} Active App: ")+B.bold.magenta(o.activePackage)+B.gray(" (Detected)")):i(B.white("\u{1F3AF} Active App: ")+B.gray("None detected / Home Launcher")),i(B.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
975
- `)),i(B.bold.white("What would you like to do?")),i(` ${B.bold.cyan("[1]")} \u{1F680} ${B.bold("Explore app autonomously")} ${B.gray("(Zero-code AI crawl)")}`),i(` ${B.bold.cyan("[2]")} \u{1F534} ${B.bold("Record a test")} ${B.gray("(Capture real device taps into spec)")}`),i(` ${B.bold.cyan("[3]")} \u25B6\uFE0F ${B.bold("Run a test specification")} ${B.gray("(Execute .txt spec with healing)")}`),i(` ${B.bold.cyan("[4]")} \u{1F4AC} ${B.bold("Open Live REPL Playground")} ${B.gray("(Interactive terminal session)")}`),i(` ${B.bold.cyan("[5]")} \u{1FA7A} ${B.bold("Run Diagnostic Doctor")} ${B.gray("(Check ADB, Node, permissions)")}`),i(` ${B.bold.cyan("[6]")} \u{1F4F1} ${B.bold("Inspect UI Components")} ${B.gray("(Summary of buttons, inputs, tabs)")}`),i(` ${B.bold.cyan("[7]")} \u2753 ${B.bold("Help & CLI Syntax Reference")}`),i(` ${B.bold.cyan("[8]")} \u{1F4AC} ${B.bold("Share Feedback & Suggestions")} ${B.gray("(1-click community link)")}`),i(` ${B.bold.cyan("[0]")} \u{1F6AA} ${B.gray("Exit")}
976
- `),await vt(r,B.bold.cyan("Select an option [0-8]: "))){case"1":{i(B.bold.cyan(`
977
- \u{1F680} Autonomous App Exploration`));let l=o.activePackage||"",d=l?B.white(`Target package [default: ${B.bold.magenta(l)}]: `):B.white("Target package name (e.g. com.example.app): "),h=await vt(r,d)||l;if(!h){i(B.red("Error: Package name is required to start exploration."));break}let u=B.white(`Safety mode [strict/moderate/disabled] (default: ${B.bold.green("strict")}): `),m=await vt(r,u)||"strict",f=["strict","moderate","interactive","disabled"].includes(m)?m:"strict";r.close(),i(B.green(`
978
- Starting autonomous exploration on ${B.bold(h)}...
979
- `)),await bs.explore({packageName:h,serial:o.serial,strategy:"dfs",safetyMode:f});return}case"2":{i(B.bold.cyan(`
980
- \u{1F534} Interactive Record & Replay`));let l=B.white(`Target spec file to save [default: ${B.bold.green("specs/recorded_flow.txt")}]: `),d=await vt(r,l)||"specs/recorded_flow.txt";r.close(),i(B.green(`
1007
+ </html>`}var or=ie(()=>{"use strict"});var dr={};Qe(dr,{buildFeedbackUrl:()=>lr,startFeedbackSession:()=>jo});import Uo from"readline";import is from"os";import ve from"chalk";function lr(a){let t=(a.message||"Feedback").split(`
1008
+ `)[0].trim(),e=t.length>70?t.substring(0,67)+"...":t,s=a.rating?"\u2B50".repeat(a.rating):"";if(a.category==="feature"){let r=new URLSearchParams;return r.set("template","2_feature_request.yml"),r.set("title",`[Feature]: ${e}`),r.set("problem",`User feedback submission${s?` (${s} ${a.rating}/5)`:""}:
1009
+ ${a.message}`),r.set("proposed_solution",a.message),r.set("category","Plain-English DSL / Syntax"),`${ar}/issues/new?${r.toString()}`}if(a.category==="bug"){let r=new URLSearchParams;return r.set("template","1_bug_report.yml"),r.set("title",`[Bug]: ${e}`),r.set("description",a.message),r.set("command_run","npx prompttest"),r.set("environment",`${is.type()} ${is.release()} (${is.arch()}), Node: ${process.version}`),`${ar}/issues/new?${r.toString()}`}let n=new URLSearchParams;n.set("template","3_feedback.yml");let i=a.rating?` ${"\u2B50".repeat(a.rating)}`:"";if(n.set("title",`[Feedback${i}]: ${e}`),n.set("message",a.message),typeof a.rating=="number"){let o={5:0,4:1,3:2,2:3,1:4}[a.rating]??0;n.set("rating",String(o))}return n.set("environment",`${is.type()} ${is.release()} (${is.arch()}), Node: ${process.version}`),`${ar}/issues/new?${n.toString()}`}function cr(a,t){return new Promise(e=>{if(a.closed){e("");return}try{a.question(t,s=>{e(s.trim())})}catch{e("")}})}async function jo(a={}){let t=a.input||process.stdin,e=a.output||process.stdout,s=(i="")=>{e.write(i+`
1010
+ `)};if(a.directMessage&&a.directMessage.trim()){let i=a.category||"general",r=lr({category:i,message:a.directMessage.trim()});return s(ve.bold.cyan(`
1011
+ \u26A1 PromptTest \u2014 Community Feedback`)),s(ve.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(ve.green("\u2714 Thank you for your feedback! Here is your 1-click submission link:")),s(ve.bold.underline.cyan(r)),s(ve.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
1012
+ `)),r}let n=Uo.createInterface({input:t,output:e});try{s(ve.bold.cyan(`
1013
+ \u26A1 PromptTest \u2014 Community Feedback & Suggestions`)),s(ve.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(ve.white("We want to make PromptTest the best mobile QA engine in your toolchain.")),s(ve.white(`Your feedback goes directly to our open community tracker!
1014
+ `)),s(ve.bold.white("How would you rate your experience with PromptTest?")),s(` ${ve.cyan("[1]")} \u2B50\u2B50\u2B50\u2B50\u2B50 (5/5) \u2014 Loving it!`),s(` ${ve.cyan("[2]")} \u2B50\u2B50\u2B50\u2B50 (4/5) \u2014 Great, but needs minor improvements`),s(` ${ve.cyan("[3]")} \u2B50\u2B50\u2B50 (3/5) \u2014 Okay, encountered some friction`),s(` ${ve.cyan("[4]")} \u2B50\u2B50 (2/5) \u2014 Frustrating / missing key features`),s(` ${ve.cyan("[5]")} \u2B50 (1/5) \u2014 Not working for me`);let i=await cr(n,ve.bold.cyan(`
1015
+ Select rating [1-5] (default: 1): `)),r=parseInt(i,10),l={1:5,2:4,3:3,4:2,5:1}[r]||5;s(ve.bold.white(`
1016
+ What type of feedback would you like to share?`)),s(` ${ve.cyan("[1]")} \u{1F4A1} Feature Request or syntax suggestion`),s(` ${ve.cyan("[2]")} \u{1F41B} Bug report or unexpected error`),s(` ${ve.cyan("[3]")} \u{1F4AC} General feedback, question, or compliment`);let c=await cr(n,ve.bold.cyan(`
1017
+ Select category [1-3] (default: 1): `)),d="feature";c==="2"?d="bug":c==="3"&&(d="general"),s(ve.bold.white(`
1018
+ Please share your thoughts, ideas, or feedback:`));let u=await cr(n,ve.bold.cyan("> "))||"Overall great experience with PromptTest!",h=lr({rating:l,category:d,message:u});return s(ve.gray(`
1019
+ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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(ve.green.bold("\u2728 Thank you! We generated your 1-click submission:")),s(ve.bold.underline.cyan(h)),s(ve.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
1020
+ `)),h}finally{try{n.close()}catch{}}}var ar,Bs=ie(()=>{"use strict";ar="https://github.com/shriramsingh/prompttest-community"});var $i={};Qe($i,{generateGithubWorkflow:()=>Ai,generateGitlabWorkflow:()=>Di,initCi:()=>Jo});import ki from"fs";import Ei from"path";function Ai(a={}){let t=a.triggers||["push","pull_request"],e=a.nodeVersion||22,s=a.apiLevel||30,n=a.arch||"x86_64",i=a.target||"specs",r=a.specPattern||"specs/*.txt",o=a.packageName||"com.example.app",l=[];t.includes("push")&&l.push(` push:
1021
+ branches: [main, dev]`),t.includes("pull_request")&&l.push(` pull_request:
1022
+ branches: [main, dev]`),t.includes("schedule")&&l.push(` schedule:
1023
+ - cron: '0 2 * * *' # Daily nightly regression`);let c=i==="explore"?`npx prompttest explore ${o} --step-budget=100 --safety-mode=strict --report=html,json`:`npx prompttest run "${r}" --fail-fast --embed-screenshots --reporters=html,json`;return`name: PromptTest Mobile Automation CI (CI-01)
1024
+
1025
+ on:
1026
+ ${l.join(`
1027
+ `)}
1028
+ workflow_dispatch:
1029
+
1030
+ jobs:
1031
+ mobile-e2e:
1032
+ name: Android E2E Tests (API ${s})
1033
+ runs-on: ubuntu-latest
1034
+
1035
+ steps:
1036
+ - name: Checkout Codebase
1037
+ uses: actions/checkout@v4
1038
+
1039
+ - name: Setup Node.js ${e}
1040
+ uses: actions/setup-node@v4
1041
+ with:
1042
+ node-version: ${e}
1043
+ cache: 'npm'
1044
+
1045
+ - name: Setup Java 17
1046
+ uses: actions/setup-java@v4
1047
+ with:
1048
+ distribution: 'temurin'
1049
+ java-version: '17'
1050
+
1051
+ - name: Setup Android SDK Tools
1052
+ uses: android-actions/setup-android@v3
1053
+
1054
+ - name: Install Node Dependencies
1055
+ run: npm ci || npm install
1056
+
1057
+ - name: Run PromptTest on Headless Android Emulator
1058
+ uses: reactivecircus/android-emulator-runner@v2
1059
+ with:
1060
+ api-level: ${s}
1061
+ target: google_apis
1062
+ arch: ${n}
1063
+ profile: pixel_6
1064
+ disable-animations: true
1065
+ force-avd-creation: false
1066
+ script: |
1067
+ adb wait-for-device
1068
+ ${c}
1069
+
1070
+ - name: Upload PromptTest Execution Reports
1071
+ if: always()
1072
+ uses: actions/upload-artifact@v4
1073
+ with:
1074
+ name: prompttest-reports-api${s}
1075
+ path: output/
1076
+ if-no-files-found: ignore
1077
+ `}function Di(a={}){let t=a.nodeVersion||22,e=a.target||"specs",s=a.specPattern||"specs/*.txt",n=a.packageName||"com.example.app",i=e==="explore"?`npx prompttest explore ${n} --step-budget=100 --safety-mode=strict --report=html,json`:`npx prompttest run "${s}" --fail-fast --embed-screenshots --reporters=html,json`;return`# PromptTest GitLab CI Pipeline (CI-01)
1078
+ image: node:${t}
1079
+
1080
+ stages:
1081
+ - test
1082
+
1083
+ variables:
1084
+ ANDROID_SERIAL: $ANDROID_SERIAL
1085
+
1086
+ prompttest-mobile-e2e:
1087
+ stage: test
1088
+ before_script:
1089
+ - npm ci || npm install
1090
+ - apt-get update && apt-get install -y android-tools-adb || true
1091
+ script:
1092
+ - ${i}
1093
+ artifacts:
1094
+ name: prompttest-reports-$CI_COMMIT_SHORT_SHA
1095
+ when: always
1096
+ expire_in: 14 days
1097
+ paths:
1098
+ - output/
1099
+ `}async function Jo(a={}){let t=a.provider||"github-actions",e=t==="github-actions",s=e?Ei.join(process.cwd(),".github","workflows"):process.cwd(),n=a.destDir||s,i=a.customFileName||(e?"prompttest.yml":".gitlab-ci.yml"),r=Ei.join(n,i),o=e?Ai(a):Di(a);return await ki.promises.mkdir(n,{recursive:!0}),await ki.promises.writeFile(r,o,"utf-8"),{provider:t,filePath:r,fileName:i,content:o}}var Ci=ie(()=>{"use strict"});Re();qt();Yt();Ve();Pt();Tt();import Le from"fs";import He from"path";import $ from"chalk";Yt();Re();qt();Pt();Tt();ss();ls();hs();Yt();Re();qt();Pt();Tt();ss();ls();Qt();us();hs();Nt();Ve();ot();Ps();je();je();_n();Wn();Ms();et();Un();sr();ms();_s();Qn();or();Nt();import rs from"os";import Ge from"chalk";var _o="https://github.com/shriramsingh/prompttest-community/issues/new";function Ho(a){if(!a)return"";let t=a;try{let e=rs.homedir();e&&e.length>2&&(t=t.split(e).join("~"))}catch{}return t=t.replace(/([a-zA-Z]:\\Users\\[^\\]+)/gi,"~"),t=t.replace(/(\/(?:home|Users)\/[^/]+)/g,"~"),t.trim()}function xi(a){if(!a||a instanceof Ct&&a.exitCode===1)return!1;let t=a,e=typeof t.message=="string"?t.message:String(a);return!(e.includes("Step")&&(e.includes("failed")||e.includes("timed out")))}function Bo(a){let t=a.repoUrl||_o,e=(a.errorMessage||"Unexpected internal error").split(`
1100
+ `)[0].trim(),s=`[Crash]: ${e}`,n=s.length>80?s.substring(0,77)+"...":s,i=`${rs.type()} ${rs.release()} (${rs.arch()})`,r=process.version,o=`${i}, Node: ${r}`,l=Ho(a.stack||a.errorMessage),c=l.length>600?l.substring(0,597)+"...":l,d=new URLSearchParams;return d.set("template","1_bug_report.yml"),d.set("title",n),d.set("description",`PromptTest encountered an unexpected error: ${e}`),a.command&&d.set("command_run",a.command),d.set("environment",o),c&&d.set("logs",c),`${t}?${d.toString()}`}function Hs(a,t={}){let e=a&&typeof a=="object"?a:{},s=e.message||String(a),n=e.stack,i=t.command||(process.argv.slice(2).length>0?`npx prompttest ${process.argv.slice(2).join(" ")}`:"npx prompttest"),r=Bo({errorMessage:s,stack:n,command:i,exitCode:t.exitCode}),o=`${rs.type()} ${rs.arch()}`,l=process.version;console.error(),console.error(Ge.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(Ge.red.bold("\u{1F4A5} PromptTest Encountered an Unexpected Error / Crash")),console.error(Ge.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(Ge.white("Error: ")+Ge.red(s.split(`
1101
+ `)[0])),console.error(Ge.white("Command: ")+Ge.gray(i)),console.error(Ge.white("Environment: ")+Ge.gray(`${o} | Node: ${l}`)),console.error(),console.error(Ge.cyan("\u{1F4A1} Think this is a bug in PromptTest? You can report it in 1 click (pre-filled):")),console.error(Ge.bold.underline.cyan(r)),console.error(Ge.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()}Re();Yt();qt();ss();_s();ms();Ve();import Wo from"readline";import pr from"fs";import ur from"path";import B from"chalk";async function Yo(a=oe){try{let e=(await a.getConnectedDevices()).find(i=>i.state==="device");if(!e)return{isOnline:!1};let s=e.serial,n;try{let i=await a.getForegroundPackage(s);i&&!i.includes("launcher")&&!i.includes("systemui")&&(n=i)}catch{}return{device:e,serial:s,activePackage:n,isOnline:!0}}catch{return{isOnline:!1}}}function Vo(a=process.cwd()){let t=[],e=[ur.join(a,"specs"),a];for(let s of e)if(pr.existsSync(s)&&pr.statSync(s).isDirectory())try{let n=pr.readdirSync(s);for(let i of n)if(i.endsWith(".txt")||i.endsWith(".spec")||i.endsWith(".prompt")){let r=ur.relative(a,ur.join(s,i)).replace(/\\/g,"/");t.includes(r)||t.push(r)}}catch{}return t.sort()}function Dt(a,t){return new Promise(e=>{a.question(t,s=>{e(s.trim())})})}async function Ti(a={}){let t=a.driver||oe,e=a.runner||Vt,s=a.input||process.stdin,n=a.output||process.stdout,i=Wo.createInterface({input:s,output:n}),r=(o="")=>{n.write(o+`
1102
+ `)};try{r(B.bold.cyan(`
1103
+ \u26A1 PromptTest \u2014 Autonomous Mobile Testing Brain`)),r(B.gray("Inspecting device connectivity and active application..."));let o=await Yo(t);switch(r(B.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?r(B.white("\u{1F4F1} Target Device: ")+B.bold.green(`${o.serial}`)+(o.device.model?B.gray(` (${o.device.model})`):"")+B.green(" [Online]")):r(B.white("\u{1F4F1} Target Device: ")+B.yellow("No authorized device online (Check USB/Wi-Fi)")),o.activePackage?r(B.white("\u{1F3AF} Active App: ")+B.bold.magenta(o.activePackage)+B.gray(" (Detected)")):r(B.white("\u{1F3AF} Active App: ")+B.gray("None detected / Home Launcher")),r(B.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
1104
+ `)),r(B.bold.white("What would you like to do?")),r(` ${B.bold.cyan("[1]")} \u{1F680} ${B.bold("Explore app autonomously")} ${B.gray("(Zero-code AI crawl)")}`),r(` ${B.bold.cyan("[2]")} \u{1F534} ${B.bold("Record a test")} ${B.gray("(Capture real device taps into spec)")}`),r(` ${B.bold.cyan("[3]")} \u25B6\uFE0F ${B.bold("Run a test specification")} ${B.gray("(Execute .txt spec with healing)")}`),r(` ${B.bold.cyan("[4]")} \u{1F4AC} ${B.bold("Open Live REPL Playground")} ${B.gray("(Interactive terminal session)")}`),r(` ${B.bold.cyan("[5]")} \u{1FA7A} ${B.bold("Run Diagnostic Doctor")} ${B.gray("(Check ADB, Node, permissions)")}`),r(` ${B.bold.cyan("[6]")} \u{1F4F1} ${B.bold("Inspect UI Components")} ${B.gray("(Summary of buttons, inputs, tabs)")}`),r(` ${B.bold.cyan("[7]")} \u2753 ${B.bold("Help & CLI Syntax Reference")}`),r(` ${B.bold.cyan("[8]")} \u{1F4AC} ${B.bold("Share Feedback & Suggestions")} ${B.gray("(1-click community link)")}`),r(` ${B.bold.cyan("[0]")} \u{1F6AA} ${B.gray("Exit")}
1105
+ `),await Dt(i,B.bold.cyan("Select an option [0-8]: "))){case"1":{r(B.bold.cyan(`
1106
+ \u{1F680} Autonomous App Exploration`));let c=o.activePackage||"",d=c?B.white(`Target package [default: ${B.bold.magenta(c)}]: `):B.white("Target package name (e.g. com.example.app): "),u=await Dt(i,d)||c;if(!u){r(B.red("Error: Package name is required to start exploration."));break}let h=B.white(`Safety mode [strict/moderate/disabled] (default: ${B.bold.green("strict")}): `),m=await Dt(i,h)||"strict",b=["strict","moderate","interactive","disabled"].includes(m)?m:"strict";i.close(),r(B.green(`
1107
+ Starting autonomous exploration on ${B.bold(u)}...
1108
+ `)),await Ds.explore({packageName:u,serial:o.serial,strategy:"dfs",safetyMode:b});return}case"2":{r(B.bold.cyan(`
1109
+ \u{1F534} Interactive Record & Replay`));let c=B.white(`Target spec file to save [default: ${B.bold.green("specs/recorded_flow.txt")}]: `),d=await Dt(i,c)||"specs/recorded_flow.txt";i.close(),r(B.green(`
981
1110
  Starting recording session on device. Output: ${d}...
982
- `)),await xn.startRecording(d,o.serial,{append:!1});return}case"3":{i(B.bold.cyan(`
983
- \u25B6\uFE0F Run Test Specification`));let l=Hr(),d="";if(l.length>0){i(B.white("Discovered existing test specs:")),l.forEach((C,j)=>{i(` [${j+1}] ${B.cyan(C)}`)}),i();let S=B.white(`Select spec number [1-${l.length}] or enter path: `),y=await vt(r,S),k=parseInt(y,10);!isNaN(k)&&k>=1&&k<=l.length?d=l[k-1]:y&&(d=y)}if(d||(d=await vt(r,B.white("Enter spec file path (e.g. specs/login.txt): "))),!d){i(B.red("Error: Spec file path is required."));break}let p=o.activePackage||"",h=p?B.white(`Target package [default: ${B.bold.magenta(p)}]: `):B.white("Target package (optional): "),m=await vt(r,h)||p||void 0,f=B.white(`Enable dynamic self-healing locators? [Y/n] (default: ${B.bold.green("yes")}): `),x=(await vt(r,f)).toLowerCase(),v=x!=="n"&&x!=="no";r.close(),i(B.green(`
1111
+ `)),await Vn.startRecording(d,o.serial,{append:!1});return}case"3":{r(B.bold.cyan(`
1112
+ \u25B6\uFE0F Run Test Specification`));let c=Vo(),d="";if(c.length>0){r(B.white("Discovered existing test specs:")),c.forEach((N,O)=>{r(` [${O+1}] ${B.cyan(N)}`)}),r();let S=B.white(`Select spec number [1-${c.length}] or enter path: `),y=await Dt(i,S),P=parseInt(y,10);!isNaN(P)&&P>=1&&P<=c.length?d=c[P-1]:y&&(d=y)}if(d||(d=await Dt(i,B.white("Enter spec file path (e.g. specs/login.txt): "))),!d){r(B.red("Error: Spec file path is required."));break}let p=o.activePackage||"",u=p?B.white(`Target package [default: ${B.bold.magenta(p)}]: `):B.white("Target package (optional): "),m=await Dt(i,u)||p||void 0,b=B.white(`Enable dynamic self-healing locators? [Y/n] (default: ${B.bold.green("yes")}): `),T=(await Dt(i,b)).toLowerCase(),w=T!=="n"&&T!=="no";i.close(),r(B.green(`
984
1113
  Executing spec "${d}"...
985
- `)),await e.runSpec(d,{packageName:m,serial:o.serial,heal:v});return}case"4":{i(B.bold.cyan(`
986
- \u{1F4AC} Launching Live REPL Playground...`)),r.close(),await Dn(t,e,{serial:o.serial,packageName:o.activePackage});return}case"5":{i(B.bold.cyan(`
987
- \u{1FA7A} Running Diagnostic Health Check...`)),r.close();let l=await as(t,{serial:o.serial,packageName:o.activePackage});i(Tn(l));return}case"6":{if(!o.isOnline||!o.serial){i(B.red("No device online to inspect."));break}i(B.cyan(`
988
- \u{1F50D} Inspecting Active Screen UI Hierarchy...`));try{let l=await t.getUiHierarchy(o.serial),{flat:d}=ve(l),p=je(d),h=ze(d),u=Ct(d);i(B.white(`
989
- On-Screen Component Breakdown:`)),i(`- \u{1F4DD} Input Fields: ${B.bold.magenta(p.length.toString())}`),p.slice(0,5).forEach(m=>i(` \u2022 "${m.text||m.contentDesc||m.resourceId}"`)),i(`- \u{1F518} Action Buttons: ${B.bold.blue(h.length.toString())}`),h.slice(0,5).forEach(m=>i(` \u2022 "${m.text||m.contentDesc||m.resourceId}"`)),i(`- \u{1F4D1} Navigation Tabs: ${B.bold.yellow(u.length.toString())}`),u.forEach(m=>i(` \u2022 "${m.text||m.contentDesc}"`)),i()}catch(l){let d=l instanceof Error?l.message:String(l);i(B.red(`Failed to read UI hierarchy: ${d}`))}break}case"7":{i(B.bold.cyan(`
990
- \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
991
- `);break}case"8":{r.close();let{startFeedbackSession:l}=await Promise.resolve().then(()=>(ks(),Ln));await l({input:s,output:n});return}default:{i(B.gray("Exiting PromptTest. Have a great day!"));break}}}finally{try{r.close()}catch{}}}ks();function Li(c,t,e){return new it(c,t,e)}kt();function Br(){try{let c=new URL(import.meta.url).pathname.replace(/^\/([a-zA-Z]:)/,"$1"),t=_e.dirname(c),e=[_e.resolve(t,"../package.json"),_e.resolve(t,"../../package.json"),_e.resolve(process.cwd(),"package.json")];for(let s of e)if(Le.existsSync(s)){let n=JSON.parse(Le.readFileSync(s,"utf-8"));if(n.name==="prompttest"&&n.version)return n.version}}catch{}return"1.4.0"}async function Ur(){let c=process.argv.slice(2),t=c[0]||"help";if(c.includes("--version")||c.includes("-v")||t==="version"){console.log(Br());return}let e=!!(process.stdin.isTTY&&process.stdout.isTTY);if((!c[0]||t==="wizard"||t==="menu")&&e){await Ri();return}switch(t){case"devices":{console.log(R.cyan(`
992
- \u{1F4F1} Scanning for Android Devices...`));let s=await re.getConnectedDevices();if(s.length===0){console.log(R.yellow("No Android devices found. Make sure USB debugging is enabled."));return}console.log(R.green(`Found ${s.length} device(s):`));for(let n of s){let r=n.state==="device"?R.green:R.red;console.log(`- ${R.bold(n.serial)} [${r(n.state)}] ${n.model?`Model: ${n.model}`:""}`)}console.log();break}case"status":{let s=c.slice(1),n=s.find(l=>l.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0);console.log(R.cyan(`
993
- \u{1F50D} Inspecting Foreground Application & UI Components...`));let r=await re.getConnectedDevices(),i=n?r.find(l=>l.serial===n):r.find(l=>l.state==="device");if(!i||i.state!=="device"){console.log(n?R.red(`Device "${n}" is not connected or unauthorized. Check 'prompttest devices'.`):R.red("No authorized device online. Check 'prompttest devices'."));return}let o=i.serial,a=await re.getForegroundPackage(o);n&&console.log(R.gray("Target Device: ")+R.bold.white(o)),console.log(R.gray("Foreground Package: ")+R.bold.green(a||"None / Launcher"));try{let l=await re.getUiHierarchy(o),{flat:d}=ve(l),p=je(d),h=ze(d),u=$t(d),m=Ct(d);console.log(R.white(`
994
- On-Screen Component Breakdown:`)),console.log(`- \u{1F4DD} Input / Form Fields: ${R.bold.magenta(p.length.toString())}`);for(let f of p)console.log(` \u2022 "${f.text||f.contentDesc||f.resourceId}"`);console.log(`- \u{1F518} Action Buttons: ${R.bold.blue(h.length.toString())}`);for(let f of h.slice(0,5))console.log(` \u2022 "${f.text||f.contentDesc||f.resourceId}"`);console.log(`- \u{1F4CB} List Items: ${R.bold.yellow(u.length.toString())}`),console.log(`- \u{1F4D1} Navigation Tabs: ${R.bold.cyan(m.length.toString())}`),console.log()}catch(l){let d=l instanceof Error?l.message:String(l);console.log(R.red(`Error reading UI tree: ${d}`))}break}case"explore":{let s=c.slice(1),n=s.find(v=>v.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),r=s.find(v=>v.startsWith("--safety-mode="))?.replace("--safety-mode=",""),i=s.find(v=>v.startsWith("--safety-blacklist="))?.replace("--safety-blacklist=","")||s.find(v=>v.startsWith("--blacklist="))?.replace("--blacklist=",""),o=i?i.split(",").map(v=>v.trim().replace(/^["']|["']$/g,"")).filter(Boolean):void 0,a=s.find(v=>v.startsWith("--max-tabs="))?parseInt(s.find(v=>v.startsWith("--max-tabs=")).replace("--max-tabs=",""),10):void 0,l=s.find(v=>v.startsWith("--max-screens="))?parseInt(s.find(v=>v.startsWith("--max-screens=")).replace("--max-screens=",""),10):void 0,d=s.find(v=>v.startsWith("--max-depth="))?parseInt(s.find(v=>v.startsWith("--max-depth=")).replace("--max-depth=",""),10):void 0,p=s.find(v=>v.startsWith("--step-budget="))?parseInt(s.find(v=>v.startsWith("--step-budget=")).replace("--step-budget=",""),10):void 0,h=s.find(v=>v.startsWith("--screenshots="))?.replace("--screenshots=",""),u=s.find(v=>v.startsWith("--strategy="))?.replace("--strategy=",""),m=s.includes("--video"),x=s.filter((v,S,y)=>!v.startsWith("-")&&v!==n&&(S===0||y[S-1]!=="-s"))[0];if(!x){let v=await re.getForegroundPackage(n);v&&!v.includes("launcher")?(x=v,console.log(R.gray("Auto-detected active package: ")+R.bold.green(x))):(console.log(R.red("Error: No package specified and no foreground application detected.")),console.log(R.yellow('Usage: npx prompttest explore <package_name> [--strategy=dfs|heuristic] [--video] [--serial=<id>] [--safety-mode=strict|moderate|interactive|disabled] [--safety-blacklist="kw1,kw2"] [--max-tabs=N] [--max-screens=N] [--step-budget=N] [--screenshots=state-change|failure-only|all]')),process.exit(1))}await bs.explore({packageName:x,serial:n,strategy:u||"dfs",safetyMode:r,safetyBlacklist:o,maxTabs:a,maxScreens:l,maxDepth:d,stepBudget:p,screenshots:h,video:m});break}case"doctor":{let s=c.slice(1),n=s.includes("--json"),r=s.find(p=>p.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),o=s.filter((p,h,u)=>!p.startsWith("-")&&p!==r&&(h===0||u[h-1]!=="-s"))[0],{runDoctor:a,formatDoctorReport:l}=await Promise.resolve().then(()=>(cs(),xi)),d=await a(re,{packageName:o,serial:r,json:n});console.log(n?JSON.stringify(d,null,2):l(d)),d.allPassed||(process.exitCode=1);break}case"repl":{let s=c.slice(1),n=s.find(a=>a.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),i=s.filter((a,l,d)=>!a.startsWith("-")&&a!==n&&(l===0||d[l-1]!=="-s"))[0],{startRepl:o}=await Promise.resolve().then(()=>(Es(),$i));await o(re,Bt,{serial:n,packageName:i});break}case"report":case"merge-reports":{let s=c.slice(1),n=s.includes("--json"),i=s.filter(d=>!d.startsWith("-"))[0]||"output",{mergeReports:o,generateAggregatedHtml:a}=await Promise.resolve().then(()=>(Cn(),Ci)),l=o(i);if(n)console.log(JSON.stringify(l,null,2));else{let d=l.overallConfidence,p=d.grade==="A"?R.bold.green:d.grade==="B"?R.bold.blue:d.grade==="C"?R.bold.yellow:R.bold.red;if(console.log(R.bold.cyan(`
995
- \u{1F4CA} PromptTest Enterprise Matrix Aggregator`)),console.log(R.gray(`Aggregated ${l.totalRuns} run(s) across ${l.uniqueDevices.length} device(s) from "${i}":
996
- `)),console.log(` \u2022 Overall Pass Rate: ${R.bold.white(`${Math.round(l.overallPassRate*100)}%`)} (${l.passedSteps}/${l.totalSteps} steps)`),console.log(` \u2022 Failed Steps: ${R.bold.red(l.failedSteps.toString())}`),console.log(` \u2022 Release Confidence: ${p(`Grade ${d.grade} (${d.score}%) \u2014 ${d.verdict}`)}`),console.log(` \u2022 Assessment: ${R.gray(d.rationale)}
997
- `),l.deviceSummaries.length>0){console.log(R.white("Device Matrix Breakdown:"));for(let h of l.deviceSummaries)console.log(` - [${R.cyan(h.deviceId)}] ${R.bold(h.suiteName)}: ${h.passedSteps}/${h.totalSteps} passed (Grade ${h.confidenceGrade}, ${h.confidenceScore}%)`);console.log()}try{let h=_e.resolve(process.cwd(),"output");Le.existsSync(h)||Le.mkdirSync(h,{recursive:!0});let u=a(l),m=_e.join(h,"aggregated-dashboard.html"),f=_e.join(h,"aggregated-results.json");Le.writeFileSync(m,u,"utf-8"),Le.writeFileSync(f,JSON.stringify(l,null,2),"utf-8"),console.log(R.green("\u2714 Saved executive dashboard to: output/aggregated-dashboard.html")),console.log(R.green(`\u2714 Saved aggregated metrics to: output/aggregated-results.json
998
- `))}catch{}}l.overallConfidence.verdict==="RELEASE_BLOCKER"&&(process.exitCode=1);break}case"record":{let s=c.slice(1),n=s.includes("--append"),r=s.find(l=>l.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),o=s.filter((l,d,p)=>!l.startsWith("-")&&l!==r&&(d===0||p[d-1]!=="-s"))[0],{autonomousRecorder:a}=await Promise.resolve().then(()=>(Kt(),wi));await a.startRecording(o,r,{append:n});break}case"init-ci":{console.log(R.cyan(`
999
- \u2699\uFE0F Setting up GitHub Actions CI Workflows for PromptTest...`));let s=_e.resolve(".github","workflows");Le.existsSync(s)||Le.mkdirSync(s,{recursive:!0});let n=_e.join(s,"unit.yml"),r=_e.join(s,"device.yml"),i=`name: PromptTest Unit & Quality CI
1114
+ `)),await e.runSpec(d,{packageName:m,serial:o.serial,heal:w});return}case"4":{r(B.bold.cyan(`
1115
+ \u{1F4AC} Launching Live REPL Playground...`)),i.close(),await nr(t,e,{serial:o.serial,packageName:o.activePackage});return}case"5":{r(B.bold.cyan(`
1116
+ \u{1FA7A} Running Diagnostic Health Check...`)),i.close();let c=await gs(t,{serial:o.serial,packageName:o.activePackage});r(Jn(c));return}case"6":{if(!o.isOnline||!o.serial){r(B.red("No device online to inspect."));break}r(B.cyan(`
1117
+ \u{1F50D} Inspecting Active Screen UI Hierarchy...`));try{let c=await t.getUiHierarchy(o.serial),{flat:d}=be(c),p=Fe(d),u=Oe(d),h=at(d);r(B.white(`
1118
+ On-Screen Component Breakdown:`)),r(`- \u{1F4DD} Input Fields: ${B.bold.magenta(p.length.toString())}`),p.slice(0,5).forEach(m=>r(` \u2022 "${m.text||m.contentDesc||m.resourceId}"`)),r(`- \u{1F518} Action Buttons: ${B.bold.blue(u.length.toString())}`),u.slice(0,5).forEach(m=>r(` \u2022 "${m.text||m.contentDesc||m.resourceId}"`)),r(`- \u{1F4D1} Navigation Tabs: ${B.bold.yellow(h.length.toString())}`),h.forEach(m=>r(` \u2022 "${m.text||m.contentDesc}"`)),r()}catch(c){let d=c instanceof Error?c.message:String(c);r(B.red(`Failed to read UI hierarchy: ${d}`))}break}case"7":{r(B.bold.cyan(`
1119
+ \u{1F4D6} Quick CLI Command Reference`)),r(" npx prompttest Interactive guided CLI wizard"),r(" npx prompttest feedback [message] Share feedback or feature requests in 1 click"),r(" npx prompttest doctor Check system dependencies & ADB"),r(" npx prompttest explore <package> Autonomous AI crawl with safety policies"),r(" npx prompttest record <spec.txt> Interactive record touches from phone"),r(" npx prompttest run <spec.txt> [package] Execute plain-English test spec"),r(" npx prompttest repl [package] Interactive live terminal playground"),r(" npx prompttest devices List all connected devices & serials"),r(` npx prompttest wifi <device_ip> Connect wireless device over Wi-Fi
1120
+ `);break}case"8":{i.close();let{startFeedbackSession:c}=await Promise.resolve().then(()=>(Bs(),dr));await c({input:s,output:n});return}default:{r(B.gray("Exiting PromptTest. Have a great day!"));break}}}finally{try{i.close()}catch{}}}Bs();function Pi(a,t,e){return new st(a,t,e)}Nt();function Ko(){try{let a=new URL(import.meta.url).pathname.replace(/^\/([a-zA-Z]:)/,"$1"),t=He.dirname(a),e=[He.resolve(t,"../package.json"),He.resolve(t,"../../package.json"),He.resolve(process.cwd(),"package.json")];for(let s of e)if(Le.existsSync(s)){let n=JSON.parse(Le.readFileSync(s,"utf-8"));if(n.name==="prompttest"&&n.version)return n.version}}catch{}return"1.4.0"}async function zo(){let a=process.argv.slice(2),t=a[0]||"help";if(a.includes("--version")||a.includes("-v")||t==="version"){console.log(Ko());return}let e=!!(process.stdin.isTTY&&process.stdout.isTTY);if((!a[0]||t==="wizard"||t==="menu")&&e){await Ti();return}switch(t){case"devices":{console.log($.cyan(`
1121
+ \u{1F4F1} Scanning for Android Devices...`));let s=await oe.getConnectedDevices();if(s.length===0){console.log($.yellow("No Android devices found. Make sure USB debugging is enabled."));return}console.log($.green(`Found ${s.length} device(s):`));for(let n of s){let i=n.state==="device"?$.green:$.red;console.log(`- ${$.bold(n.serial)} [${i(n.state)}] ${n.model?`Model: ${n.model}`:""}`)}console.log();break}case"status":{let s=a.slice(1),n=s.find(c=>c.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0);console.log($.cyan(`
1122
+ \u{1F50D} Inspecting Foreground Application & UI Components...`));let i=await oe.getConnectedDevices(),r=n?i.find(c=>c.serial===n):i.find(c=>c.state==="device");if(!r||r.state!=="device"){console.log(n?$.red(`Device "${n}" is not connected or unauthorized. Check 'prompttest devices'.`):$.red("No authorized device online. Check 'prompttest devices'."));return}let o=r.serial,l=await oe.getForegroundPackage(o);n&&console.log($.gray("Target Device: ")+$.bold.white(o)),console.log($.gray("Foreground Package: ")+$.bold.green(l||"None / Launcher"));try{let c=await oe.getUiHierarchy(o),{flat:d}=be(c),p=Fe(d),u=Oe(d),h=Rt(d),m=at(d);console.log($.white(`
1123
+ On-Screen Component Breakdown:`)),console.log(`- \u{1F4DD} Input / Form Fields: ${$.bold.magenta(p.length.toString())}`);for(let b of p)console.log(` \u2022 "${b.text||b.contentDesc||b.resourceId}"`);console.log(`- \u{1F518} Action Buttons: ${$.bold.blue(u.length.toString())}`);for(let b of u.slice(0,5))console.log(` \u2022 "${b.text||b.contentDesc||b.resourceId}"`);console.log(`- \u{1F4CB} List Items: ${$.bold.yellow(h.length.toString())}`),console.log(`- \u{1F4D1} Navigation Tabs: ${$.bold.cyan(m.length.toString())}`),console.log()}catch(c){let d=c instanceof Error?c.message:String(c);console.log($.red(`Error reading UI tree: ${d}`))}break}case"explore":{let s=a.slice(1),n=s.find(w=>w.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),i=s.find(w=>w.startsWith("--safety-mode="))?.replace("--safety-mode=",""),r=s.find(w=>w.startsWith("--safety-blacklist="))?.replace("--safety-blacklist=","")||s.find(w=>w.startsWith("--blacklist="))?.replace("--blacklist=",""),o=r?r.split(",").map(w=>w.trim().replace(/^["']|["']$/g,"")).filter(Boolean):void 0,l=s.find(w=>w.startsWith("--max-tabs="))?parseInt(s.find(w=>w.startsWith("--max-tabs=")).replace("--max-tabs=",""),10):void 0,c=s.find(w=>w.startsWith("--max-screens="))?parseInt(s.find(w=>w.startsWith("--max-screens=")).replace("--max-screens=",""),10):void 0,d=s.find(w=>w.startsWith("--max-depth="))?parseInt(s.find(w=>w.startsWith("--max-depth=")).replace("--max-depth=",""),10):void 0,p=s.find(w=>w.startsWith("--step-budget="))?parseInt(s.find(w=>w.startsWith("--step-budget=")).replace("--step-budget=",""),10):void 0,u=s.find(w=>w.startsWith("--screenshots="))?.replace("--screenshots=",""),h=s.find(w=>w.startsWith("--strategy="))?.replace("--strategy=",""),m=s.includes("--video"),T=s.filter((w,S,y)=>!w.startsWith("-")&&w!==n&&(S===0||y[S-1]!=="-s"))[0];if(!T){let w=await oe.getForegroundPackage(n);w&&!w.includes("launcher")?(T=w,console.log($.gray("Auto-detected active package: ")+$.bold.green(T))):(console.log($.red("Error: No package specified and no foreground application detected.")),console.log($.yellow('Usage: npx prompttest explore <package_name> [--strategy=dfs|heuristic] [--video] [--serial=<id>] [--safety-mode=strict|moderate|interactive|disabled] [--safety-blacklist="kw1,kw2"] [--max-tabs=N] [--max-screens=N] [--step-budget=N] [--screenshots=state-change|failure-only|all]')),process.exit(1))}await Ds.explore({packageName:T,serial:n,strategy:h||"dfs",safetyMode:i,safetyBlacklist:o,maxTabs:l,maxScreens:c,maxDepth:d,stepBudget:p,screenshots:u,video:m});break}case"scaffold":{let s=a.slice(1),n=s.find(l=>l.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),i=s.find(l=>l.startsWith("--output="))?.replace("--output=","")||(s.includes("-o")?s[s.indexOf("-o")+1]:void 0),o=s.filter((l,c,d)=>!l.startsWith("-")&&l!==n&&(c===0||d[c-1]!=="-s")&&l!==i&&(c===0||d[c-1]!=="-o"))[0];console.log($.cyan(`
1124
+ \u{1F3D7}\uFE0F PromptTest Spec Auto-Scaffolder (SC-01)...`));try{let{scaffoldSpec:l}=await Promise.resolve().then(()=>(Xn(),ai)),c=await l({packageName:o,deviceSerial:n,outputPath:i});console.log($.green(`\u2714 Successfully scaffolded spec for ${$.bold(c.packageName)}:`)),console.log(`- Path: ${$.bold.white(c.specPath)}`),console.log(`- Detected inputs: ${$.magenta(c.inputCount)}`),console.log(`- Detected action buttons: ${$.blue(c.buttonCount)}`),console.log(`- Generated test steps: ${$.yellow(c.stepsCount)}`),console.log($.gray(`
1125
+ Run your new spec with:`)),console.log($.cyan(` npx prompttest run ${c.specPath}
1126
+ `))}catch(l){console.log($.red(`Scaffolding failed: ${l instanceof Error?l.message:String(l)}
1127
+ `)),process.exit(1)}break}case"activate":{let s=a[1];s||(console.log($.red(`
1128
+ Error: Please provide a license key.`)),console.log($.yellow(`Usage: npx prompttest activate PTPRO-<payload>.<signature>
1129
+ `)),process.exit(1));let{activateLicense:n}=await Promise.resolve().then(()=>(Ts(),qs)),i=n(s);i.success?(console.log($.bold.green(`
1130
+ \u2714 ${i.message}`)),console.log($.cyan("Tier: ")+$.bold.white(i.payload?.tier.toUpperCase())),console.log($.cyan("Licensed to: ")+$.bold.white(i.payload?.holder)),console.log($.cyan("White-label Reports: ")+$.bold.white(i.payload?.whiteLabel?"ENABLED":"DISABLED")),console.log()):(console.log($.red(`
1131
+ \u2716 License Activation Failed: ${i.message}
1132
+ `)),process.exit(1));break}case"license":{let{getLicenseInfo:s}=await Promise.resolve().then(()=>(Ts(),qs)),n=s();console.log($.bold.cyan(`
1133
+ \u{1F4DC} PromptTest License Status (LC-01)`)),console.log(`- Status: ${n.active?$.bold.green("ACTIVE"):$.gray("FREE TIER")}`),console.log(`- License Tier: ${$.bold.white(n.tier.toUpperCase())}`),n.holder&&console.log(`- License Holder: ${$.bold.white(n.holder)}`),n.expiresAt&&console.log(`- Expiration: ${$.gray(n.expiresAt)}`),console.log(`- White-label Reports: ${n.whiteLabel?$.bold.green("YES"):$.gray("NO (Free Branding Attached)")}`),console.log();break}case"doctor":{let s=a.slice(1),n=s.includes("--json"),i=s.find(p=>p.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),o=s.filter((p,u,h)=>!p.startsWith("-")&&p!==i&&(u===0||h[u-1]!=="-s"))[0],{runDoctor:l,formatDoctorReport:c}=await Promise.resolve().then(()=>(ms(),ri)),d=await l(oe,{packageName:o,serial:i,json:n});console.log(n?JSON.stringify(d,null,2):c(d)),d.allPassed||(process.exitCode=1);break}case"repl":{let s=a.slice(1),n=s.find(l=>l.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),r=s.filter((l,c,d)=>!l.startsWith("-")&&l!==n&&(c===0||d[c-1]!=="-s"))[0],{startRepl:o}=await Promise.resolve().then(()=>(_s(),Si));await o(oe,Vt,{serial:n,packageName:r});break}case"report":case"merge-reports":{let s=a.slice(1),n=s.includes("--json"),r=s.filter(d=>!d.startsWith("-"))[0]||"output",{mergeReports:o,generateAggregatedHtml:l}=await Promise.resolve().then(()=>(or(),wi)),c=o(r);if(n)console.log(JSON.stringify(c,null,2));else{let d=c.overallConfidence,p=d.grade==="A"?$.bold.green:d.grade==="B"?$.bold.blue:d.grade==="C"?$.bold.yellow:$.bold.red;if(console.log($.bold.cyan(`
1134
+ \u{1F4CA} PromptTest Enterprise Matrix Aggregator`)),console.log($.gray(`Aggregated ${c.totalRuns} run(s) across ${c.uniqueDevices.length} device(s) from "${r}":
1135
+ `)),console.log(` \u2022 Overall Pass Rate: ${$.bold.white(`${Math.round(c.overallPassRate*100)}%`)} (${c.passedSteps}/${c.totalSteps} steps)`),console.log(` \u2022 Failed Steps: ${$.bold.red(c.failedSteps.toString())}`),console.log(` \u2022 Release Confidence: ${p(`Grade ${d.grade} (${d.score}%) \u2014 ${d.verdict}`)}`),console.log(` \u2022 Assessment: ${$.gray(d.rationale)}
1136
+ `),c.deviceSummaries.length>0){console.log($.white("Device Matrix Breakdown:"));for(let u of c.deviceSummaries)console.log(` - [${$.cyan(u.deviceId)}] ${$.bold(u.suiteName)}: ${u.passedSteps}/${u.totalSteps} passed (Grade ${u.confidenceGrade}, ${u.confidenceScore}%)`);console.log()}try{let u=He.resolve(process.cwd(),"output");Le.existsSync(u)||Le.mkdirSync(u,{recursive:!0});let h=l(c),m=He.join(u,"aggregated-dashboard.html"),b=He.join(u,"aggregated-results.json");Le.writeFileSync(m,h,"utf-8"),Le.writeFileSync(b,JSON.stringify(c,null,2),"utf-8"),console.log($.green("\u2714 Saved executive dashboard to: output/aggregated-dashboard.html")),console.log($.green(`\u2714 Saved aggregated metrics to: output/aggregated-results.json
1137
+ `))}catch{}}c.overallConfidence.verdict==="RELEASE_BLOCKER"&&(process.exitCode=1);break}case"record":{let s=a.slice(1),n=s.includes("--append"),i=s.find(c=>c.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),o=s.filter((c,d,p)=>!c.startsWith("-")&&c!==i&&(d===0||p[d-1]!=="-s"))[0],{autonomousRecorder:l}=await Promise.resolve().then(()=>(ss(),si));await l.startRecording(o,i,{append:n});break}case"init-ci":{if(a.slice(1).some(d=>d.includes("gitlab"))){console.log($.cyan(`
1138
+ \u2699\uFE0F Setting up GitLab CI Pipeline for PromptTest (CI-01)...`));let{initCi:d}=await Promise.resolve().then(()=>(Ci(),$i)),p=await d({provider:"gitlab-ci"});console.log($.bold.green(`\u2714 Created GitLab CI configuration: ${p.filePath}
1139
+ `));break}console.log($.cyan(`
1140
+ \u2699\uFE0F Setting up GitHub Actions CI Workflows for PromptTest (CI-01)...`));let i=He.resolve(".github","workflows");Le.existsSync(i)||Le.mkdirSync(i,{recursive:!0});let r=He.join(i,"unit.yml"),o=He.join(i,"device.yml"),l=`name: PromptTest Unit & Quality CI
1000
1141
 
1001
1142
  on:
1002
1143
  push:
1003
- branches: [main]
1144
+ branches: [main, dev]
1004
1145
  pull_request:
1005
- branches: [main]
1146
+ branches: [main, dev]
1006
1147
 
1007
1148
  jobs:
1008
1149
  quality-gate:
1009
1150
  name: Node \${{ matrix.node-version }} Quality & Unit Tests
1010
1151
  runs-on: ubuntu-latest
1011
1152
 
1153
+ # The test toolchain, not the published CLI, sets the floor here: vitest 5
1154
+ # declares engines ^22.12.0 || ^24.0.0 || >=26.0.0 and fails to even load on
1155
+ # Node 18/20 ('node:util' does not provide an export named 'styleText').
1156
+ # fail-fast is disabled so every Node leg always reports its own result.
1012
1157
  strategy:
1158
+ fail-fast: false
1013
1159
  matrix:
1014
- node-version: [18, 20]
1160
+ node-version: [22, 24]
1015
1161
 
1016
1162
  steps:
1017
1163
  - name: Checkout Repository
1018
- uses: actions/checkout@v4
1164
+ uses: actions/checkout@v5
1019
1165
 
1020
1166
  - name: Setup Node.js \${{ matrix.node-version }}
1021
- uses: actions/setup-node@v4
1167
+ uses: actions/setup-node@v5
1022
1168
  with:
1023
1169
  node-version: \${{ matrix.node-version }}
1024
1170
  cache: 'npm'
@@ -1044,42 +1190,93 @@ jobs:
1044
1190
  - name: Build Distribution
1045
1191
  run: npm run build
1046
1192
 
1193
+ - name: Verify npm Distribution Contents
1194
+ run: npm run pack:verify
1195
+
1196
+ - name: Smoke Test Packed npm Consumer
1197
+ run: node scripts/smoke-pack.mjs
1198
+
1047
1199
  - name: Upload Test Coverage
1048
- if: matrix.node-version == 20 && always()
1200
+ if: matrix.node-version == 24 && always()
1049
1201
  uses: actions/upload-artifact@v4
1050
1202
  with:
1051
1203
  name: coverage-report
1052
1204
  path: coverage/
1053
1205
  if-no-files-found: ignore
1054
- `,o=`name: PromptTest Android Emulator CI
1206
+ `,c=`name: PromptTest Android Emulator CI
1207
+
1208
+ # Android CI/CD for PromptTest.
1209
+ #
1210
+ # The pipeline has two layers so it stays useful even before an APK is wired up:
1211
+ #
1212
+ # 1. Smoke gate (always enforced) - runs specs/android_ci_smoke.txt against
1213
+ # the Settings app that ships with the emulator image. It needs no APK, no
1214
+ # EAS project and no secrets, so it cannot be blocked by external
1215
+ # infrastructure. This proves ADB connectivity, UI hierarchy dumping,
1216
+ # element resolution, tapping and back navigation still work on Android.
1217
+ #
1218
+ # 2. App spec (opt-in) - installs a debug APK and runs a recorded spec against
1219
+ # the real application. Supply the APK either as a direct URL (workflow
1220
+ # input \`apk_url\` or repository variable ANDROID_APK_URL) or via EAS
1221
+ # (\`eas_build_id\`, resolved through the Expo GraphQL API). When neither is
1222
+ # configured the app spec is skipped with a notice instead of failing the
1223
+ # pipeline.
1224
+ #
1225
+ # Optional repository variables (Settings > Secrets and variables > Actions):
1226
+ # ANDROID_APK_URL direct download URL for the debug APK
1227
+ # EAS_BUILD_ID EAS build ID to download from EAS
1228
+ # EXPO_TOKEN secret, only required for the EAS download path
1229
+
1230
+ permissions:
1231
+ contents: read
1232
+ checks: write
1055
1233
 
1056
1234
  on:
1057
1235
  workflow_dispatch:
1058
1236
  inputs:
1059
1237
  spec_file:
1060
- description: 'Specific spec file to run'
1238
+ description: 'Recorded app spec to run against the APK'
1239
+ required: false
1240
+ default: 'specs/coachconnect_flow_1.txt'
1241
+ apk_url:
1242
+ description: 'Direct URL to a debug APK (overrides the EAS download)'
1061
1243
  required: false
1062
- default: 'specs/coachconnect_flow.txt'
1244
+ default: ''
1245
+ eas_build_id:
1246
+ description: 'EAS build ID for the APK (used when apk_url is empty)'
1247
+ required: false
1248
+ default: ''
1249
+ push:
1250
+ branches: [main, dev]
1251
+ pull_request:
1252
+ branches: [main, dev]
1063
1253
  schedule:
1064
- # Run weekly on Sunday at 02:00 UTC
1254
+ # Weekly smoke run (Sundays at 02:00 UTC)
1065
1255
  - cron: '0 2 * * 0'
1066
- push:
1067
- tags:
1068
- - 'v*'
1256
+
1257
+ # Emulator jobs are slow and memory hungry: never run two for the same ref.
1258
+ concurrency:
1259
+ group: \${{ github.workflow }}-\${{ github.ref }}
1260
+ cancel-in-progress: true
1069
1261
 
1070
1262
  jobs:
1071
1263
  emulator-test:
1072
- name: Android API 33 Live Spec Validation
1264
+ name: Android API 30 Emulator & Live Specs
1073
1265
  runs-on: ubuntu-latest
1266
+ timeout-minutes: 45
1267
+
1268
+ env:
1269
+ PROMPTTEST_SPEC_FILE: \${{ inputs.spec_file || 'specs/coachconnect_flow_1.txt' }}
1270
+ SMOKE_SPEC_FILE: specs/android_ci_smoke.txt
1074
1271
 
1075
1272
  steps:
1076
1273
  - name: Checkout Repository
1077
- uses: actions/checkout@v4
1274
+ uses: actions/checkout@v5
1078
1275
 
1079
- - name: Setup Node.js 20
1080
- uses: actions/setup-node@v4
1276
+ - name: Setup Node.js 22
1277
+ uses: actions/setup-node@v5
1081
1278
  with:
1082
- node-version: 20
1279
+ node-version: 22
1083
1280
  cache: 'npm'
1084
1281
 
1085
1282
  - name: Install Dependencies
@@ -1088,23 +1285,86 @@ jobs:
1088
1285
  - name: Build PromptTest Distribution
1089
1286
  run: npm run build
1090
1287
 
1091
- - name: Enable KVM Hardware Acceleration
1288
+ - name: Validate Spec Files (no device required)
1092
1289
  run: |
1093
- echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selections
1094
- sudo apt-get update
1095
- sudo apt-get install -y libvirt-daemon-system libvirt-clients bridge-utils cpu-checker
1096
- kvm-ok
1097
-
1098
- - name: Run Android Emulator & Live Specs
1290
+ node dist/bin/prompttest.js run "$SMOKE_SPEC_FILE" --dry-run
1291
+ node dist/bin/prompttest.js run "$PROMPTTEST_SPEC_FILE" --dry-run
1292
+
1293
+ - name: Resolve Test APK
1294
+ id: apk
1295
+ env:
1296
+ EXPO_TOKEN: \${{ secrets.EXPO_TOKEN }}
1297
+ APK_URL_INPUT: \${{ inputs.apk_url }}
1298
+ APK_URL_VAR: \${{ vars.ANDROID_APK_URL }}
1299
+ EAS_BUILD_ID_INPUT: \${{ inputs.eas_build_id }}
1300
+ EAS_BUILD_ID_VAR: \${{ vars.EAS_BUILD_ID }}
1301
+ run: |
1302
+ set -eu
1303
+ echo "has_apk=false" >> "$GITHUB_OUTPUT"
1304
+ rm -f coachconnect.apk
1305
+
1306
+ APK_URL="\${APK_URL_INPUT:-\${APK_URL_VAR:-}}"
1307
+ EAS_BUILD_ID="\${EAS_BUILD_ID_INPUT:-\${EAS_BUILD_ID_VAR:-}}"
1308
+
1309
+ if [ -n "$APK_URL" ]; then
1310
+ echo "Downloading APK from the configured URL..."
1311
+ curl -fL --retry 3 --retry-delay 5 -o coachconnect.apk "$APK_URL"
1312
+ elif [ -n "$EAS_BUILD_ID" ]; then
1313
+ echo "Downloading APK from EAS build $EAS_BUILD_ID via GraphQL API..."
1314
+ if [ -z "\${EXPO_TOKEN:-}" ]; then
1315
+ echo "::error title=EXPO_TOKEN missing::The EAS download path requires the EXPO_TOKEN secret."
1316
+ exit 1
1317
+ fi
1318
+ EAS_ARTIFACT_URL=$(node scripts/resolve-eas-artifact.mjs "$EAS_BUILD_ID")
1319
+ echo "Resolved EAS artifact URL, downloading APK..."
1320
+ curl -fL --retry 3 --retry-delay 5 -o coachconnect.apk "$EAS_ARTIFACT_URL"
1321
+ else
1322
+ echo "::notice title=App spec skipped::No APK configured, so only the dependency-free smoke gate will run. Set the ANDROID_APK_URL repository variable, or pass apk_url / eas_build_id when dispatching."
1323
+ exit 0
1324
+ fi
1325
+
1326
+ if [ ! -s coachconnect.apk ]; then
1327
+ echo "::error title=APK invalid::The downloaded APK is empty."
1328
+ exit 1
1329
+ fi
1330
+ ls -lh coachconnect.apk
1331
+ echo "has_apk=true" >> "$GITHUB_OUTPUT"
1332
+
1333
+ - name: Grant Emulator Access to KVM
1334
+ run: |
1335
+ if [ -e /dev/kvm ]; then
1336
+ sudo chmod 666 /dev/kvm
1337
+ ls -l /dev/kvm
1338
+ else
1339
+ echo 'KVM device is unavailable; the emulator will fall back to software acceleration.'
1340
+ fi
1341
+
1342
+ - name: Run Emulator, Smoke Gate & App Specs
1099
1343
  uses: reactivecircus/android-emulator-runner@v2
1344
+ env:
1345
+ HAS_APK: \${{ steps.apk.outputs.has_apk }}
1346
+ SMOKE_SPEC_FILE: specs/android_ci_smoke.txt
1347
+ PROMPTTEST_SPEC_FILE: \${{ inputs.spec_file || 'specs/coachconnect_flow_1.txt' }}
1100
1348
  with:
1101
- api-level: 33
1102
- target: google_apis
1349
+ api-level: 30
1350
+ target: default
1103
1351
  arch: x86_64
1104
- profile: pixel_6
1352
+ profile: pixel_2
1353
+ force-avd-creation: true
1354
+ disk-size: 1024M
1355
+ ram-size: 1024M
1356
+ emulator-boot-timeout: 600
1357
+ disable-linux-hw-accel: false
1358
+ emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -no-metrics -camera-back none -accel on
1359
+ disable-animations: true
1360
+ disable-spellchecker: true
1361
+ pre-emulator-launch-script: adb start-server
1105
1362
  script: |
1106
- npm run status
1107
- npm run test:live || true
1363
+ adb wait-for-device
1364
+ adb devices
1365
+ node dist/bin/prompttest.js status
1366
+ node dist/bin/prompttest.js run "$SMOKE_SPEC_FILE"
1367
+ if [ "$HAS_APK" = "true" ]; then node dist/bin/prompttest.js run "$PROMPTTEST_SPEC_FILE" --apk=coachconnect.apk --fresh; else echo "Skipping the app spec: no APK was resolved."; fi
1108
1368
 
1109
1369
  - name: Publish JUnit Test Results
1110
1370
  uses: mikepenz/action-junit-report@v4
@@ -1120,40 +1380,40 @@ jobs:
1120
1380
  name: prompttest-device-artifacts
1121
1381
  path: output/
1122
1382
  if-no-files-found: ignore
1123
- `;Le.writeFileSync(n,i,"utf-8"),Le.writeFileSync(r,o,"utf-8"),console.log(R.bold.green("\u2714 Created GitHub Actions workflows:")),console.log(R.cyan(` \u2022 ${n} (Headless Node 18 & 20 quality gates)`)),console.log(R.cyan(` \u2022 ${r} (Android Emulator API 33 live specs)
1124
- `));break}case"benchmark":{let s=c.slice(1),n=s.includes("--json"),r=s.find(u=>u.startsWith("--iterations=")),i=r?parseInt(r.replace("--iterations=",""),10):5,o=s.find(u=>u.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),l=s.filter((u,m,f)=>!u.startsWith("-")&&u!==o&&(m===0||f[m-1]!=="-s"))[0];n||console.log(R.cyan(`
1125
- \u26A1 Running PromptTest Performance Diagnostic Benchmark...`));let{runBenchmark:d,formatBenchmarkTable:p}=await Promise.resolve().then(()=>(kn(),Di)),h=await d(re,{iterations:i,serial:o,packageName:l});console.log(n?JSON.stringify(h,null,2):p(h));break}case"run":{let s=[...c.slice(1)],n=s.includes("--screenshots")||s.includes("--all-screenshots")||s.includes("--screenshots=all"),r=s.indexOf("--screenshots");r!==-1&&s[r+1]==="all"&&(n=!0,s.splice(r+1,1));let i=s.includes("--continue")||s.includes("--continue-on-error"),o=s.includes("--fresh")||s.includes("--cold-start"),a=s.includes("--all-devices"),l=s.includes("--resume"),d=s.includes("--save-baseline"),p=s.includes("--compare-baseline"),h=s.find(E=>E.startsWith("--baseline-threshold=")),u=h?parseFloat(h.replace("--baseline-threshold=","")):void 0,m=s.find(E=>E.startsWith("--reporter="))?.replace("--reporter=",""),f=s.find(E=>E.startsWith("--apk="))?.replace("--apk=",""),x=s.includes("--video"),v=s.includes("--heal"),S=s.includes("--expand"),y=s.includes("--expand-apply"),k=s.includes("--no-ai"),C=s.includes("--benchmark"),j=s.find(E=>E.startsWith("--reporters=")),$=j?j.replace("--reporters=","").split(",").map(E=>E.trim()):void 0,J=s.find(E=>E.startsWith("--data="))?.replace("--data=",""),O=s.find(E=>E.startsWith("--iterations=")),V=O?parseInt(O.replace("--iterations=",""),10):void 0,W=s.find(E=>E.startsWith("--only-row=")),se=W?parseInt(W.replace("--only-row=",""),10):void 0,oe=s.find(E=>E.startsWith("--locale="))?.replace("--locale=",""),G=s.includes("--dry-run"),H=s.includes("--list-steps"),w=s.includes("--json"),b=s.find(E=>E.startsWith("--only="))?.replace("--only=",""),_=s.find(E=>E.startsWith("--tags="))?.replace("--tags=",""),P=s.includes("--embed-screenshots"),ce=s.find(E=>E.startsWith("--serve=")||E==="--serve"),ne;if(ce)if(ce.startsWith("--serve=")){let E=parseInt(ce.replace("--serve=",""),10);ne=isNaN(E)?4040:E}else{let E=s.indexOf("--serve"),L=s[E+1];L&&/^\d+$/.test(L)?(ne=parseInt(L,10),s.splice(E+1,1)):ne=4040}let g=s.find(E=>E.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),D={};for(let E of s)if(E.startsWith("--var:")){let[L,U]=E.replace("--var:","").split("=");L&&U&&(D[L]=U)}let N=s.filter((E,L,U)=>!(E.startsWith("-")||E===g||L>0&&U[L-1]==="-s")),M=N[0],T=N[1];if(M||(console.log(R.red("Error: Please specify a spec file path. E.g. prompttest run specs/coachconnect_flow.txt")),process.exit(3)),Le.existsSync(M)&&Le.statSync(M).isDirectory()&&T&&!T.startsWith("--")){let E=_e.join(M,T);Le.existsSync(E)&&(M=E,T=N[2])}if(S||y){console.log(R.cyan(`
1126
- \u{1F916} Analyzing and expanding spec "${M}" using Semantic AI...`));let E=Le.readFileSync(M,"utf-8"),{expanded:L,stepsAdded:U}=await Bt.expandSpec(E);if(U>0?(console.log(R.green(`\u2714 Successfully expanded ${U} macro step(s)!`)),y?(Le.writeFileSync(M,L,"utf-8"),console.log(R.bold.green(`\u2714 Applied expanded steps directly to ${M}.
1127
- `))):(console.log(R.yellow(`
1128
- --- Preview of Expanded Spec (--expand-apply to overwrite) ---`)),console.log(R.gray(L)),console.log(R.yellow(`---------------------------------------------------------------
1129
- `)))):console.log(R.gray(`No high-level macro steps detected for expansion.
1130
- `)),!s.includes("--execute"))break}if(a){let L=(await re.getConnectedDevices()).filter(K=>K.state==="device");L.length===0&&(console.log(R.red("No online devices found for multi-device run.")),process.exit(2)),console.log(R.bold.cyan(`
1131
- \u{1F4F1} Multi-Device Matrix Execution across ${L.length} device(s):`));for(let K of L)console.log(`- ${K.serial} (${K.model||"Android"})`);let U=await Promise.allSettled(L.map(K=>{let q=K.serial.replace(/[^a-zA-Z0-9._-]/g,"_");return Li(new rt(K.serial),new lt,new Nt(_e.join("output",q))).runSpec(M,{packageName:T,serial:K.serial,screenshotMode:n?"all":"on-failure",continueOnError:i,fresh:o,resume:l,saveBaseline:d,compareBaseline:p,baselineThreshold:u,apkPath:f,variables:D,video:x,reporters:$,heal:v,noAi:k,dataFile:J,iterations:V,onlyRow:se,locale:oe,benchmark:C,dryRun:G,listSteps:H,only:b,tags:_,json:w})}));console.log(R.bold.green(`
1132
- \u2728 Multi-Device Execution Finished!`)),U.some(K=>K.status==="rejected"||K.status==="fulfilled"&&K.value&&K.value.failedSteps>0)&&process.exit(1);break}let A=await Bt.runSpec(M,{packageName:T,serial:g,screenshotMode:n?"all":"on-failure",continueOnError:i,fresh:o,resume:l,saveBaseline:d,compareBaseline:p,baselineThreshold:u,apkPath:f,variables:D,video:x,reporters:$,heal:v,noAi:k,dataFile:J,iterations:V,onlyRow:se,locale:oe,embedScreenshots:P,serve:ne,benchmark:C,dryRun:G,listSteps:H,only:b,tags:_,json:w,reporter:m?(await import(_e.resolve(m))).default:void 0});A&&A.failedSteps>0&&process.exit(1);break}case"serve":case"server":{let s=c.slice(1),n=s.find(a=>a.startsWith("--port="))?.replace("--port=","")||s.find(a=>/^\d+$/.test(a))||process.env.PORT,r=n?parseInt(n,10):4040,{startLiveServer:i}=await Promise.resolve().then(()=>(xs(),Ti));console.log(R.cyan(`
1133
- \u{1F4E1} Starting PromptTest Engine on port ${r}...`));let o=await i({port:r});console.log(R.green(`\u2714 PromptTest Engine running at: ${o.url}
1134
- `)),await new Promise(()=>{});break}case"watch":{let s=[...c.slice(1)],n=s.includes("--screenshots")||s.includes("--all-screenshots")||s.includes("--screenshots=all"),r=s.indexOf("--screenshots");r!==-1&&s[r+1]==="all"&&(n=!0,s.splice(r+1,1));let i=s.includes("--continue")||s.includes("--continue-on-error"),o=s.includes("--fresh")||s.includes("--cold-start"),a=s.includes("--resume"),l=s.includes("--save-baseline"),d=s.includes("--compare-baseline"),p=s.find(g=>g.startsWith("--baseline-threshold=")),h=p?parseFloat(p.replace("--baseline-threshold=","")):void 0,u=s.find(g=>g.startsWith("--reporter="))?.replace("--reporter=",""),m=s.find(g=>g.startsWith("--apk="))?.replace("--apk=",""),f=s.includes("--video"),x=s.includes("--heal"),v=s.includes("--no-ai"),S=s.find(g=>g.startsWith("--reporters=")),y=S?S.replace("--reporters=","").split(",").map(g=>g.trim()):void 0,k=s.find(g=>g.startsWith("--data="))?.replace("--data=",""),C=s.find(g=>g.startsWith("--iterations=")),j=C?parseInt(C.replace("--iterations=",""),10):void 0,$=s.find(g=>g.startsWith("--only-row=")),J=$?parseInt($.replace("--only-row=",""),10):void 0,O=s.find(g=>g.startsWith("--locale="))?.replace("--locale=",""),V=s.includes("--embed-screenshots"),W=s.find(g=>g.startsWith("--serve=")||g==="--serve"),se;if(W)if(W.startsWith("--serve=")){let g=parseInt(W.replace("--serve=",""),10);se=isNaN(g)?4040:g}else{let g=s.indexOf("--serve"),D=s[g+1];D&&/^\d+$/.test(D)?(se=parseInt(D,10),s.splice(g+1,1)):se=4040}let oe=s.find(g=>g.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),G={};for(let g of s)if(g.startsWith("--var:")){let[D,N]=g.replace("--var:","").split("=");D&&N&&(G[D]=N)}let H=s.filter((g,D,N)=>!(g.startsWith("-")||g===oe||D>0&&N[D-1]==="-s")),w=H[0],b=H[1];w||(console.log(R.red("Error: Please specify a spec file path to watch. E.g. prompttest watch specs/flow.txt")),process.exit(3)),Le.existsSync(w)||(console.log(R.red(`Error: Spec file not found: ${w}`)),process.exit(3)),console.log(R.bold.cyan(`
1135
- \u{1F440} PromptTest Watch Mode Active`)),console.log(R.gray("Watching: ")+R.white.bold(w)),se&&console.log(R.gray("Live Monitor: ")+R.magenta.bold(`http://localhost:${typeof se=="number"?se:4040}`)),console.log(R.gray(`Press Ctrl+C to exit.
1136
- `));let _=!1,P=!1,ce=async()=>{if(_){P=!0;return}_=!0,P=!1;try{console.log(R.cyan(`
1137
- \u26A1 [${new Date().toLocaleTimeString()}] Running ${w}...`)),await Bt.runSpec(w,{packageName:b,serial:oe,screenshotMode:n?"all":"on-failure",continueOnError:i,fresh:o,resume:a,saveBaseline:l,compareBaseline:d,baselineThreshold:h,apkPath:m,variables:G,video:f,reporters:y,heal:x,noAi:v,dataFile:k,iterations:j,onlyRow:J,locale:O,embedScreenshots:V,serve:se,reporter:u?(await import(_e.resolve(u))).default:void 0})}catch(g){let D=g instanceof Error?g.message:String(g);console.error(R.red(`Run error: ${D}`))}finally{_=!1,P&&(P=!1,setTimeout(ce,100))}};await ce();let ne=null;Le.watch(w,()=>{ne&&clearTimeout(ne),ne=setTimeout(()=>{console.log(R.yellow(`
1138
- \u{1F4DD} Change detected in ${w}`)),ce()},300)}),await new Promise(()=>{});break}case"wifi":{let s=c[1];console.log(R.cyan(`
1139
- \u{1F4F6} Establishing Wireless ADB Connection...`));try{let n=await re.connectWifi(s);n.success?(console.log(R.bold.green(`\u2714 Connected to ${n.target} over Wi-Fi successfully!`)),console.log(R.yellow("\u{1F449} You can now safely UNPLUG the USB cable.")),console.log(R.gray(`PromptTest will execute all tests wirelessly over ${n.target}
1140
- `))):(console.log(R.yellow(`Connection response: ${n.message}`)),process.exitCode=1)}catch(n){let r=n instanceof Error?n.message:String(n);console.log(R.red(`\u274C Wireless connection failed: ${r}`)),process.exit(1)}break}case"disconnect":{let s=c[1];console.log(R.cyan(`
1141
- \u{1F50C} Disconnecting Wireless ADB...`));let n=await re.disconnectWifi(s);console.log(R.green(`\u2714 ${n||"Disconnected wireless devices."}
1142
- `));break}case"memory":{let s=c[1];if(s==="clear"){let i=c[2]||await re.getForegroundPackage()||"host.exp.exponent",o=await Me.clear(i);console.log(o?R.green(`
1143
- \u2714 Cleared learned memory for package: ${R.bold(i)}
1144
- `):R.yellow(`
1145
- No learned memory found for package: ${i}
1146
- `));break}if(s==="export"){let i=c[2]||await re.getForegroundPackage()||"host.exp.exponent",o=c[3];await Me.init(i);let a=await Me.exportMemory(o);console.log(R.green(`
1147
- \u2714 Exported memory for [${R.bold(i)}] to: ${R.bold(a)}
1148
- `));break}if(s==="import"){let i=c[2];i||(console.log(R.red(`
1383
+ `;Le.writeFileSync(r,l,"utf-8"),Le.writeFileSync(o,c,"utf-8"),console.log($.bold.green("\u2714 Created GitHub Actions workflows:")),console.log($.cyan(` \u2022 ${r} (Headless Node 22 & 24 quality gates)`)),console.log($.cyan(` \u2022 ${o} (Android API 30 emulator smoke gate + opt-in app specs)
1384
+ `));break}case"benchmark":{let s=a.slice(1),n=s.includes("--json"),i=s.find(h=>h.startsWith("--iterations=")),r=i?parseInt(i.replace("--iterations=",""),10):5,o=s.find(h=>h.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),c=s.filter((h,m,b)=>!h.startsWith("-")&&h!==o&&(m===0||b[m-1]!=="-s"))[0];n||console.log($.cyan(`
1385
+ \u26A1 Running PromptTest Performance Diagnostic Benchmark...`));let{runBenchmark:d,formatBenchmarkTable:p}=await Promise.resolve().then(()=>(sr(),bi)),u=await d(oe,{iterations:r,serial:o,packageName:c});console.log(n?JSON.stringify(u,null,2):p(u));break}case"run":{let s=[...a.slice(1)],n=s.includes("--screenshots")||s.includes("--all-screenshots")||s.includes("--screenshots=all"),i=s.indexOf("--screenshots");i!==-1&&s[i+1]==="all"&&(n=!0,s.splice(i+1,1));let r=s.includes("--continue")||s.includes("--continue-on-error"),o=s.includes("--fresh")||s.includes("--cold-start"),l=s.includes("--all-devices"),c=s.includes("--resume"),d=s.includes("--save-baseline"),p=s.includes("--compare-baseline"),u=s.find(D=>D.startsWith("--baseline-threshold=")),h=u?parseFloat(u.replace("--baseline-threshold=","")):void 0,m=s.find(D=>D.startsWith("--reporter="))?.replace("--reporter=",""),b=s.find(D=>D.startsWith("--apk="))?.replace("--apk=",""),T=s.includes("--video"),w=s.includes("--heal"),S=s.includes("--expand"),y=s.includes("--expand-apply"),P=s.includes("--no-ai"),N=s.includes("--benchmark"),O=s.includes("--profile"),L=s.find(D=>D.startsWith("--reporters=")),U=L?L.replace("--reporters=","").split(",").map(D=>D.trim()):void 0,j=s.find(D=>D.startsWith("--data="))?.replace("--data=",""),Q=s.find(D=>D.startsWith("--iterations=")),K=Q?parseInt(Q.replace("--iterations=",""),10):void 0,V=s.find(D=>D.startsWith("--only-row=")),ye=V?parseInt(V.replace("--only-row=",""),10):void 0,re=s.find(D=>D.startsWith("--locale="))?.replace("--locale=",""),W=s.includes("--dry-run"),se=s.includes("--list-steps"),x=s.includes("--json"),f=s.find(D=>D.startsWith("--only="))?.replace("--only=",""),F=s.find(D=>D.startsWith("--tags="))?.replace("--tags=",""),E=s.includes("--embed-screenshots"),ge=s.find(D=>D.startsWith("--serve=")||D==="--serve"),Y;if(ge)if(ge.startsWith("--serve=")){let D=parseInt(ge.replace("--serve=",""),10);Y=isNaN(D)?4040:D}else{let D=s.indexOf("--serve"),I=s[D+1];I&&/^\d+$/.test(I)?(Y=parseInt(I,10),s.splice(D+1,1)):Y=4040}let g=s.find(D=>D.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),k={};for(let D of s)if(D.startsWith("--var:")){let[I,H]=D.replace("--var:","").split("=");I&&H&&(k[I]=H)}let M=s.filter((D,I,H)=>!(D.startsWith("-")||D===g||I>0&&H[I-1]==="-s")),v=M[0],C=M[1];if(v||(console.log($.red("Error: Please specify a spec file path. E.g. prompttest run specs/coachconnect_flow.txt")),process.exit(3)),Le.existsSync(v)&&Le.statSync(v).isDirectory()&&C&&!C.startsWith("--")){let D=He.join(v,C);Le.existsSync(D)&&(v=D,C=M[2])}if(S||y){console.log($.cyan(`
1386
+ \u{1F916} Analyzing and expanding spec "${v}" using Semantic AI...`));let D=Le.readFileSync(v,"utf-8"),{expanded:I,stepsAdded:H}=await Vt.expandSpec(D);if(H>0?(console.log($.green(`\u2714 Successfully expanded ${H} macro step(s)!`)),y?(Le.writeFileSync(v,I,"utf-8"),console.log($.bold.green(`\u2714 Applied expanded steps directly to ${v}.
1387
+ `))):(console.log($.yellow(`
1388
+ --- Preview of Expanded Spec (--expand-apply to overwrite) ---`)),console.log($.gray(I)),console.log($.yellow(`---------------------------------------------------------------
1389
+ `)))):console.log($.gray(`No high-level macro steps detected for expansion.
1390
+ `)),!s.includes("--execute"))break}if(l){let I=(await oe.getConnectedDevices()).filter(G=>G.state==="device");I.length===0&&(console.log($.red("No online devices found for multi-device run.")),process.exit(2)),console.log($.bold.cyan(`
1391
+ \u{1F4F1} Multi-Device Matrix Execution across ${I.length} device(s):`));for(let G of I)console.log(`- ${G.serial} (${G.model||"Android"})`);let H=await Promise.allSettled(I.map(G=>{let Z=G.serial.replace(/[^a-zA-Z0-9._-]/g,"_");return Pi(new lt(G.serial),new gt,new ut(He.join("output",Z))).runSpec(v,{packageName:C,serial:G.serial,screenshotMode:n?"all":"on-failure",continueOnError:r,fresh:o,resume:c,saveBaseline:d,compareBaseline:p,baselineThreshold:h,apkPath:b,variables:k,video:T,reporters:U,heal:w,noAi:P,dataFile:j,iterations:K,onlyRow:ye,locale:re,benchmark:N,dryRun:W,listSteps:se,only:f,tags:F,json:x,profile:O})}));console.log($.bold.green(`
1392
+ \u2728 Multi-Device Execution Finished!`)),H.some(G=>G.status==="rejected"||G.status==="fulfilled"&&G.value&&G.value.failedSteps>0)&&process.exit(1);break}let A=await Vt.runSpec(v,{packageName:C,serial:g,screenshotMode:n?"all":"on-failure",continueOnError:r,fresh:o,resume:c,saveBaseline:d,compareBaseline:p,baselineThreshold:h,apkPath:b,variables:k,video:T,reporters:U,heal:w,noAi:P,dataFile:j,iterations:K,onlyRow:ye,locale:re,embedScreenshots:E,serve:Y,benchmark:N,dryRun:W,listSteps:se,only:f,tags:F,json:x,profile:O,reporter:m?(await import(He.resolve(m))).default:void 0});A&&A.failedSteps>0&&process.exit(1);break}case"serve":case"server":{let s=a.slice(1),n=s.find(l=>l.startsWith("--port="))?.replace("--port=","")||s.find(l=>/^\d+$/.test(l))||process.env.PORT,i=n?parseInt(n,10):4040,{startLiveServer:r}=await Promise.resolve().then(()=>(Ms(),li));console.log($.cyan(`
1393
+ \u{1F4E1} Starting PromptTest Engine on port ${i}...`));let o=await r({port:i});console.log($.green(`\u2714 PromptTest Engine running at: ${o.url}
1394
+ `)),await new Promise(()=>{});break}case"watch":{let s=[...a.slice(1)],n=s.includes("--screenshots")||s.includes("--all-screenshots")||s.includes("--screenshots=all"),i=s.indexOf("--screenshots");i!==-1&&s[i+1]==="all"&&(n=!0,s.splice(i+1,1));let r=s.includes("--continue")||s.includes("--continue-on-error"),o=s.includes("--fresh")||s.includes("--cold-start"),l=s.includes("--resume"),c=s.includes("--save-baseline"),d=s.includes("--compare-baseline"),p=s.find(Y=>Y.startsWith("--baseline-threshold=")),u=p?parseFloat(p.replace("--baseline-threshold=","")):void 0,h=s.find(Y=>Y.startsWith("--reporter="))?.replace("--reporter=",""),m=s.find(Y=>Y.startsWith("--apk="))?.replace("--apk=",""),b=s.includes("--video"),T=s.includes("--heal"),w=s.includes("--no-ai"),S=s.find(Y=>Y.startsWith("--reporters=")),y=S?S.replace("--reporters=","").split(",").map(Y=>Y.trim()):void 0,P=s.find(Y=>Y.startsWith("--data="))?.replace("--data=",""),N=s.find(Y=>Y.startsWith("--iterations=")),O=N?parseInt(N.replace("--iterations=",""),10):void 0,L=s.find(Y=>Y.startsWith("--only-row=")),U=L?parseInt(L.replace("--only-row=",""),10):void 0,j=s.find(Y=>Y.startsWith("--locale="))?.replace("--locale=",""),Q=s.includes("--embed-screenshots"),K=s.find(Y=>Y.startsWith("--serve=")||Y==="--serve"),V;if(K)if(K.startsWith("--serve=")){let Y=parseInt(K.replace("--serve=",""),10);V=isNaN(Y)?4040:Y}else{let Y=s.indexOf("--serve"),g=s[Y+1];g&&/^\d+$/.test(g)?(V=parseInt(g,10),s.splice(Y+1,1)):V=4040}let ye=s.find(Y=>Y.startsWith("--serial="))?.replace("--serial=","")||(s.includes("-s")?s[s.indexOf("-s")+1]:void 0),re={};for(let Y of s)if(Y.startsWith("--var:")){let[g,k]=Y.replace("--var:","").split("=");g&&k&&(re[g]=k)}let W=s.filter((Y,g,k)=>!(Y.startsWith("-")||Y===ye||g>0&&k[g-1]==="-s")),se=W[0],x=W[1];se||(console.log($.red("Error: Please specify a spec file path to watch. E.g. prompttest watch specs/flow.txt")),process.exit(3)),Le.existsSync(se)||(console.log($.red(`Error: Spec file not found: ${se}`)),process.exit(3)),console.log($.bold.cyan(`
1395
+ \u{1F440} PromptTest Watch Mode Active`)),console.log($.gray("Watching: ")+$.white.bold(se)),V&&console.log($.gray("Live Monitor: ")+$.magenta.bold(`http://localhost:${typeof V=="number"?V:4040}`)),console.log($.gray(`Press Ctrl+C to exit.
1396
+ `));let f=!1,F=!1,E=async()=>{if(f){F=!0;return}f=!0,F=!1;try{console.log($.cyan(`
1397
+ \u26A1 [${new Date().toLocaleTimeString()}] Running ${se}...`)),await Vt.runSpec(se,{packageName:x,serial:ye,screenshotMode:n?"all":"on-failure",continueOnError:r,fresh:o,resume:l,saveBaseline:c,compareBaseline:d,baselineThreshold:u,apkPath:m,variables:re,video:b,reporters:y,heal:T,noAi:w,dataFile:P,iterations:O,onlyRow:U,locale:j,embedScreenshots:Q,serve:V,reporter:h?(await import(He.resolve(h))).default:void 0})}catch(Y){let g=Y instanceof Error?Y.message:String(Y);console.error($.red(`Run error: ${g}`))}finally{f=!1,F&&(F=!1,setTimeout(E,100))}};await E();let ge=null;Le.watch(se,()=>{ge&&clearTimeout(ge),ge=setTimeout(()=>{console.log($.yellow(`
1398
+ \u{1F4DD} Change detected in ${se}`)),E()},300)}),await new Promise(()=>{});break}case"wifi":{let s=a[1];console.log($.cyan(`
1399
+ \u{1F4F6} Establishing Wireless ADB Connection...`));try{let n=await oe.connectWifi(s);n.success?(console.log($.bold.green(`\u2714 Connected to ${n.target} over Wi-Fi successfully!`)),console.log($.yellow("\u{1F449} You can now safely UNPLUG the USB cable.")),console.log($.gray(`PromptTest will execute all tests wirelessly over ${n.target}
1400
+ `))):(console.log($.yellow(`Connection response: ${n.message}`)),process.exitCode=1)}catch(n){let i=n instanceof Error?n.message:String(n);console.log($.red(`\u274C Wireless connection failed: ${i}`)),process.exit(1)}break}case"disconnect":{let s=a[1];console.log($.cyan(`
1401
+ \u{1F50C} Disconnecting Wireless ADB...`));let n=await oe.disconnectWifi(s);console.log($.green(`\u2714 ${n||"Disconnected wireless devices."}
1402
+ `));break}case"memory":{let s=a[1];if(s==="clear"){let r=a[2]||await oe.getForegroundPackage()||"host.exp.exponent",o=await Me.clear(r);console.log(o?$.green(`
1403
+ \u2714 Cleared learned memory for package: ${$.bold(r)}
1404
+ `):$.yellow(`
1405
+ No learned memory found for package: ${r}
1406
+ `));break}if(s==="export"){let r=a[2]||await oe.getForegroundPackage()||"host.exp.exponent",o=a[3];await Me.init(r);let l=await Me.exportMemory(o);console.log($.green(`
1407
+ \u2714 Exported memory for [${$.bold(r)}] to: ${$.bold(l)}
1408
+ `));break}if(s==="import"){let r=a[2];r||(console.log($.red(`
1149
1409
  Error: Please provide a source JSON file to import: npx prompttest memory import <file.json> [pkg]
1150
- `)),process.exit(1));let o=c[3]||await re.getForegroundPackage()||"host.exp.exponent";await Me.init(o);let a=await Me.importMemory(i);console.log(R.green(`
1151
- \u2714 Successfully imported/merged ${R.bold(a)} mappings into [${R.bold(o)}]
1152
- `));break}if(s==="stats"){let i=c[2]||await re.getForegroundPackage()||"host.exp.exponent";await Me.init(i);let o=Me.getStats();console.log(R.bold.cyan(`
1153
- \u{1F4CA} PromptTest Knowledge Memory Stats [Package: ${o.package}]`)),console.log(` \u2022 Total Learned Mappings: ${R.bold.white(o.totalMappings)}`),console.log(` \u2022 Lifetime Success Hits: ${R.bold.green(o.totalSuccesses)}`),console.log(` \u2022 Average Confidence: ${R.bold.yellow(`${Math.round(o.averageConfidence*100)}%`)}
1154
- `);break}let n=s||await re.getForegroundPackage()||"host.exp.exponent";await Me.init(n);let r=Me.getMappingsList();if(console.log(R.bold.cyan(`
1155
- \u{1F9E0} PromptTest Autonomous Knowledge Memory [Package: ${n}]`)),r.length===0)console.log(R.gray("No learned component mappings recorded yet for this app.")),console.log(R.gray(`Run tests or autonomous exploration to build knowledge automatically.
1156
- `));else{console.log(R.white(`Active Learned Mappings (${r.length}):
1157
- `));for(let i of r)console.log(` \u2022 [${R.magenta(i.action)}] "${R.bold.yellow(i.canonicalIntent)}" \u2794 "${R.green(i.matchedIdentifier)}" `+R.gray(`(${i.matchType}, ${Math.round(i.confidence*100)}% conf, ${i.totalSuccesses||i.successCount} hits)`));console.log()}break}case"feedback":{let s=c.slice(1),n=s.filter(o=>!o.startsWith("-")).join(" "),r=s.includes("--bug")?"bug":s.includes("--feature")?"feature":void 0,{startFeedbackSession:i}=await Promise.resolve().then(()=>(ks(),Ln));await i({directMessage:n||void 0,category:r});break}default:{console.log(R.bold.cyan(`
1410
+ `)),process.exit(1));let o=a[3]||await oe.getForegroundPackage()||"host.exp.exponent";await Me.init(o);let l=await Me.importMemory(r);console.log($.green(`
1411
+ \u2714 Successfully imported/merged ${$.bold(l)} mappings into [${$.bold(o)}]
1412
+ `));break}if(s==="stats"){let r=a[2]||await oe.getForegroundPackage()||"host.exp.exponent";await Me.init(r);let o=Me.getStats();console.log($.bold.cyan(`
1413
+ \u{1F4CA} PromptTest Knowledge Memory Stats [Package: ${o.package}]`)),console.log(` \u2022 Total Learned Mappings: ${$.bold.white(o.totalMappings)}`),console.log(` \u2022 Lifetime Success Hits: ${$.bold.green(o.totalSuccesses)}`),console.log(` \u2022 Average Confidence: ${$.bold.yellow(`${Math.round(o.averageConfidence*100)}%`)}
1414
+ `);break}let n=s||await oe.getForegroundPackage()||"host.exp.exponent";await Me.init(n);let i=Me.getMappingsList();if(console.log($.bold.cyan(`
1415
+ \u{1F9E0} PromptTest Autonomous Knowledge Memory [Package: ${n}]`)),i.length===0)console.log($.gray("No learned component mappings recorded yet for this app.")),console.log($.gray(`Run tests or autonomous exploration to build knowledge automatically.
1416
+ `));else{console.log($.white(`Active Learned Mappings (${i.length}):
1417
+ `));for(let r of i)console.log(` \u2022 [${$.magenta(r.action)}] "${$.bold.yellow(r.canonicalIntent)}" \u2794 "${$.green(r.matchedIdentifier)}" `+$.gray(`(${r.matchType}, ${Math.round(r.confidence*100)}% conf, ${r.totalSuccesses||r.successCount} hits)`));console.log()}break}case"feedback":{let s=a.slice(1),n=s.filter(o=>!o.startsWith("-")).join(" "),i=s.includes("--bug")?"bug":s.includes("--feature")?"feature":void 0,{startFeedbackSession:r}=await Promise.resolve().then(()=>(Bs(),dr));await r({directMessage:n||void 0,category:i});break}default:{console.log($.bold.cyan(`
1158
1418
  \u{1F9EA} PromptTest \u2014 Autonomous Mobile Testing & Visual QA Brain`)),console.log(`
1159
- Usage:`),console.log(" npx prompttest Interactive guided CLI wizard"),console.log(" npx prompttest feedback [msg] Share feedback or feature suggestions in 1 click"),console.log(" npx prompttest doctor [--json] Run environment health diagnostic check"),console.log(" npx prompttest repl [-s serial] [pkg] Interactive live REPL playground"),console.log(" npx prompttest merge-reports [dir] Aggregate multi-device reports into executive dashboard"),console.log(" npx prompttest devices List connected Android devices"),console.log(" npx prompttest wifi [ip[:port]] Connect to device over Wi-Fi (auto-detects IP if omitted)"),console.log(" npx prompttest disconnect [ip[:port]] Disconnect wireless Wi-Fi device"),console.log(" npx prompttest status Inspect current screen components & forms"),console.log(" npx prompttest record [spec.txt] [--append] Interactive record-and-replay (auto-backup & append)"),console.log(" npx prompttest explore [package] Run autonomous exploration (forms -> lists -> tabs)"),console.log(" [--video] Record session to MP4 screen video"),console.log(" [--safety-mode=strict|moderate|interactive|disabled] Policy level (default: strict)"),console.log(' [--safety-blacklist="kw1,kw2"] Additional blacklist keywords'),console.log(" [--max-screens=N] [--max-depth=N] [--step-budget=N] Exploration bounds & budgets"),console.log(" [--screenshots=state-change|failure-only|all] Visual snapshot capture policy"),console.log(" npx prompttest run <spec.txt> [pkg] Run test (Fail-Fast with Cascading Skip)"),console.log(" npx prompttest run <spec.txt> --apk=<path> Auto-install APK if not installed on device"),console.log(" npx prompttest run <spec.txt> --all-devices Run concurrently across all connected devices"),console.log(" npx prompttest run <spec.txt> --fresh Cold-restart target app before test suite begins"),console.log(" npx prompttest run <spec.txt> --continue Run test continuing through all steps on failure"),console.log(" npx prompttest run <spec.txt> --screenshots Capture full visual screenshots on every step"),console.log(" npx prompttest run <spec.txt> --resume Resume from last checkpoint on failure"),console.log(" npx prompttest run <spec.txt> --save-baseline Save screenshots as visual baseline"),console.log(" npx prompttest run <spec.txt> --compare-baseline Compare screenshots against saved baseline"),console.log(" npx prompttest run <spec.txt> --baseline-threshold=N Diff threshold 0-1 (default 0.02)"),console.log(" npx prompttest run <spec.txt> --video Record test session to MP4 video"),console.log(" npx prompttest run <spec.txt> --heal Enable automatic locator self-healing"),console.log(" npx prompttest run <spec.txt> --expand Preview AI macro intent expansion"),console.log(" npx prompttest run <spec.txt> --expand-apply Apply expanded steps directly to spec"),console.log(" npx prompttest run <spec.txt> --no-ai Force offline heuristic resolver"),console.log(" npx prompttest run <spec.txt> --reporters=json,html Output specific report formats"),console.log(" npx prompttest run <spec.txt> --reporter=<path> Load custom reporter module"),console.log(" npx prompttest run <spec.txt> --data=<path.csv|json> Execute data-driven test across rows"),console.log(" npx prompttest run <spec.txt> --iterations=N Repeat spec across N iterations or rows"),console.log(" npx prompttest run <spec.txt> --only-row=N Execute only specific 1-indexed data row"),console.log(" npx prompttest run <spec.txt> --locale=<code> Set active taxonomy locale (e.g. 'en', 'es', 'hi')"),console.log(" npx prompttest run <spec.txt> --embed-screenshots Inline base64 screenshots into standalone HTML report"),console.log(" npx prompttest run <spec.txt> --dry-run Validate spec syntax without device interaction"),console.log(" npx prompttest run <spec.txt> --list-steps Print parsed test steps table and exit"),console.log(' npx prompttest run <spec.txt> --only="1-3,5" Execute only specified step range or indices'),console.log(' npx prompttest run <spec.txt> --tags="smoke" Execute only specs matching metadata tags'),console.log(" npx prompttest run <spec.txt> --json Output machine-readable execution report JSON"),console.log(" npx prompttest run <spec.txt> --serve [port] Stream live test events over HTTP/SSE dashboard (default: 4040)"),console.log(" npx prompttest watch <spec.txt> Auto-rerun test on spec file changes with live monitor"),console.log(" npx prompttest init-ci Generate GitHub Actions workflows (unit.yml & device.yml)"),console.log(" npx prompttest memory [package] View learned component knowledge & aliases"),console.log(" npx prompttest memory stats [package] View memory health & accuracy statistics"),console.log(" npx prompttest memory export [pkg] [dest] Export learned memory to JSON file"),console.log(" npx prompttest memory import <file> [pkg] Import/merge shared knowledge from JSON"),console.log(" npx prompttest memory clear [package] Clear learned memory for an app"),console.log();break}}}process.on("uncaughtException",c=>{Ps(c),process.exit(1)});process.on("unhandledRejection",c=>{Ps(c),process.exit(1)});Ur().catch(c=>{Ni(c)?Ps(c):console.error(R.red(`Fatal Error: ${c.message}`));let t=typeof c.exitCode=="number"?c.exitCode:1;process.exit(t)});
1419
+ Usage:`),console.log(" npx prompttest Interactive guided CLI wizard"),console.log(" npx prompttest feedback [msg] Share feedback or feature suggestions in 1 click"),console.log(" npx prompttest doctor [--json] Run environment health diagnostic check"),console.log(" npx prompttest repl [-s serial] [pkg] Interactive live REPL playground"),console.log(" npx prompttest merge-reports [dir] Aggregate multi-device reports into executive dashboard"),console.log(" npx prompttest devices List connected Android devices"),console.log(" npx prompttest wifi [ip[:port]] Connect to device over Wi-Fi (auto-detects IP if omitted)"),console.log(" npx prompttest disconnect [ip[:port]] Disconnect wireless Wi-Fi device"),console.log(" npx prompttest status Inspect current screen components & forms"),console.log(" npx prompttest record [spec.txt] [--append] Interactive record-and-replay (auto-backup & append)"),console.log(" npx prompttest explore [package] Run autonomous exploration (forms -> lists -> tabs)"),console.log(" [--video] Record session to MP4 screen video"),console.log(" [--safety-mode=strict|moderate|interactive|disabled] Policy level (default: strict)"),console.log(' [--safety-blacklist="kw1,kw2"] Additional blacklist keywords'),console.log(" [--max-screens=N] [--max-depth=N] [--step-budget=N] Exploration bounds & budgets"),console.log(" [--screenshots=state-change|failure-only|all] Visual snapshot capture policy"),console.log(" npx prompttest run <spec.txt> [pkg] Run test (Fail-Fast with Cascading Skip)"),console.log(" npx prompttest run <spec.txt> --apk=<path> Auto-install APK if not installed on device"),console.log(" npx prompttest run <spec.txt> --all-devices Run concurrently across all connected devices"),console.log(" npx prompttest run <spec.txt> --fresh Cold-restart target app before test suite begins"),console.log(" npx prompttest run <spec.txt> --continue Run test continuing through all steps on failure"),console.log(" npx prompttest run <spec.txt> --screenshots Capture full visual screenshots on every step"),console.log(" npx prompttest run <spec.txt> --resume Resume from last checkpoint on failure"),console.log(" npx prompttest run <spec.txt> --save-baseline Save screenshots as visual baseline"),console.log(" npx prompttest run <spec.txt> --compare-baseline Compare screenshots against saved baseline"),console.log(" npx prompttest run <spec.txt> --baseline-threshold=N Diff threshold 0-1 (default 0.02)"),console.log(" npx prompttest run <spec.txt> --video Record test session to MP4 video"),console.log(" npx prompttest run <spec.txt> --heal Enable automatic locator self-healing"),console.log(" npx prompttest run <spec.txt> --expand Preview AI macro intent expansion"),console.log(" npx prompttest run <spec.txt> --expand-apply Apply expanded steps directly to spec"),console.log(" npx prompttest run <spec.txt> --no-ai Force offline heuristic resolver"),console.log(" npx prompttest run <spec.txt> --reporters=json,html Output specific report formats"),console.log(" npx prompttest run <spec.txt> --reporter=<path> Load custom reporter module"),console.log(" npx prompttest run <spec.txt> --data=<path.csv|json> Execute data-driven test across rows"),console.log(" npx prompttest run <spec.txt> --iterations=N Repeat spec across N iterations or rows"),console.log(" npx prompttest run <spec.txt> --only-row=N Execute only specific 1-indexed data row"),console.log(" npx prompttest run <spec.txt> --locale=<code> Set active taxonomy locale (e.g. 'en', 'es', 'hi')"),console.log(" npx prompttest run <spec.txt> --embed-screenshots Inline base64 screenshots into standalone HTML report"),console.log(" npx prompttest run <spec.txt> --dry-run Validate spec syntax without device interaction"),console.log(" npx prompttest run <spec.txt> --list-steps Print parsed test steps table and exit"),console.log(' npx prompttest run <spec.txt> --only="1-3,5" Execute only specified step range or indices'),console.log(' npx prompttest run <spec.txt> --tags="smoke" Execute only specs matching metadata tags'),console.log(" npx prompttest run <spec.txt> --json Output machine-readable execution report JSON"),console.log(" npx prompttest run <spec.txt> --serve [port] Stream live test events over HTTP/SSE dashboard (default: 4040)"),console.log(" npx prompttest run <spec.txt> --profile Track 60 FPS compliance, jank %, and memory growth (PF-01)"),console.log(" npx prompttest watch <spec.txt> Auto-rerun test on spec file changes with live monitor"),console.log(" npx prompttest init-ci Generate GitHub Actions workflows (unit.yml & device.yml)"),console.log(" npx prompttest scaffold [pkg] Inspect active screen and auto-generate starter test spec (SC-01)"),console.log(" npx prompttest activate <key> Activate offline Pro or Enterprise license key (LC-01)"),console.log(" npx prompttest license Check active license status and white-label state (LC-01)"),console.log(" npx prompttest memory [package] View learned component knowledge & aliases"),console.log(" npx prompttest memory stats [package] View memory health & accuracy statistics"),console.log(" npx prompttest memory export [pkg] [dest] Export learned memory to JSON file"),console.log(" npx prompttest memory import <file> [pkg] Import/merge shared knowledge from JSON"),console.log(" npx prompttest memory clear [package] Clear learned memory for an app"),console.log();break}}}process.on("uncaughtException",a=>{Hs(a),process.exit(1)});process.on("unhandledRejection",a=>{Hs(a),process.exit(1)});zo().catch(a=>{xi(a)?Hs(a):console.error($.red(`Fatal Error: ${a.message}`));let t=typeof a.exitCode=="number"?a.exitCode:1;process.exit(t)});