agent-device 0.5.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -7
- package/dist/src/350.js +3 -0
- package/dist/src/bin.js +42 -38
- package/dist/src/daemon.js +16 -16
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +618 -97
- package/package.json +2 -2
- package/skills/agent-device/SKILL.md +15 -2
- package/skills/agent-device/references/snapshot-refs.md +16 -0
- package/dist/src/407.js +0 -3
package/dist/src/daemon.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import{isCancel as e,select as t}from"@clack/prompts";import{node_path as i,
|
|
2
|
-
`)}function $(e){let t=e.error?w(e.error):null,i=e.context?.platform,r=e.context?.phase;if(t?.code==="TOOL_MISSING")return"android"===i?"ADB_TRANSPORT_UNAVAILABLE":"IOS_TOOL_MISSING";let n=t?.details??{},a="string"==typeof n.message?n.message:void 0,o="string"==typeof n.stdout?n.stdout:void 0,s="string"==typeof n.stderr?n.stderr:void 0,l=n.boot&&"object"==typeof n.boot?n.boot:null,c=n.bootstatus&&"object"==typeof n.bootstatus?n.bootstatus:null,d=[e.message,t?.message,e.stdout,e.stderr,a,o,s,"string"==typeof l?.stdout?l.stdout:void 0,"string"==typeof l?.stderr?l.stderr:void 0,"string"==typeof c?.stdout?c.stdout:void 0,"string"==typeof c?.stderr?c.stderr:void 0].filter(Boolean).join("\n").toLowerCase();return"ios"===i&&(d.includes("runner did not accept connection")||"connect"===r&&(d.includes("timed out")||d.includes("timeout")||d.includes("econnrefused")||d.includes("connection refused")||d.includes("fetch failed")||d.includes("socket hang up")))?"IOS_RUNNER_CONNECT_TIMEOUT":"ios"===i&&"boot"===r&&(d.includes("timed out")||d.includes("timeout"))?"IOS_BOOT_TIMEOUT":"android"===i&&"boot"===r&&(d.includes("timed out")||d.includes("timeout"))?"ANDROID_BOOT_TIMEOUT":d.includes("resource temporarily unavailable")||d.includes("killed: 9")||d.includes("cannot allocate memory")||d.includes("system is low on memory")?"CI_RESOURCE_STARVATION_SUSPECTED":"android"===i&&(d.includes("device not found")||d.includes("no devices")||d.includes("device offline")||d.includes("offline")||d.includes("unauthorized")||d.includes("not authorized")||d.includes("unable to locate device")||d.includes("invalid device"))?"ADB_TRANSPORT_UNAVAILABLE":t?.code==="COMMAND_FAILED"||d.length>0?"BOOT_COMMAND_FAILED":"UNKNOWN"}function F(e){switch(e){case"IOS_BOOT_TIMEOUT":return"Retry simulator boot and inspect simctl bootstatus logs; in CI consider increasing AGENT_DEVICE_IOS_BOOT_TIMEOUT_MS.";case"IOS_RUNNER_CONNECT_TIMEOUT":return"Retry runner startup, inspect xcodebuild logs, and verify simulator responsiveness before command execution.";case"ANDROID_BOOT_TIMEOUT":return"Retry emulator startup and verify sys.boot_completed reaches 1; consider increasing startup budget in CI.";case"ADB_TRANSPORT_UNAVAILABLE":return"Check adb server/device transport (adb devices -l), restart adb, and ensure the target device is online and authorized.";case"CI_RESOURCE_STARVATION_SUSPECTED":return"CI machine may be resource constrained; reduce parallel jobs or use a larger runner.";case"IOS_TOOL_MISSING":return"Xcode command-line tools are missing or not in PATH; run xcode-select --install and verify xcrun works.";case"BOOT_COMMAND_FAILED":return"Inspect command stderr/stdout for the failing boot phase and retry after environment validation.";default:return"Retry once and inspect verbose logs for the failing phase."}}function V(e){return e.startsWith("emulator-")}async function U(e,t=L){return u("adb",["-s",e,"shell","getprop","sys.boot_completed"],{allowFailure:!0,timeoutMs:t})}async function G(e,t){let i=t.replace(/_/g," ").trim();if(!V(e))return i||e;let r=await u("adb",["-s",e,"emu","avd","name"],{allowFailure:!0,timeoutMs:L}),n=r.stdout.trim();return 0===r.exitCode&&n?n.replace(/_/g," "):i||e}async function j(){if(!await D("adb"))throw new I("TOOL_MISSING","adb not found in PATH");let e=(await u("adb",["devices","-l"],{timeoutMs:L})).stdout.split("\n").map(e=>e.trim()).filter(e=>e.length>0&&!e.startsWith("List of devices")).map(e=>e.split(/\s+/)).filter(e=>"device"===e[1]).map(e=>({serial:e[0],rawModel:(e.find(e=>e.startsWith("model:"))??"").replace("model:","")}));return await Promise.all(e.map(async({serial:e,rawModel:t})=>{let[i,r]=await Promise.all([G(e,t),B(e)]);return{platform:"android",id:e,name:i,kind:V(e)?"emulator":"device",booted:r}}))}async function B(e){try{let t=await U(e);return"1"===t.stdout.trim()}catch{return!1}}async function q(e,t=6e4){let i,r=C.fromTimeoutMs(t),n=Math.max(1,Math.ceil(t/1e3)),a=!1;try{await R(async({deadline:n})=>{if(n?.isExpired())throw a=!0,new I("COMMAND_FAILED","Android boot deadline exceeded",{serial:e,timeoutMs:t,elapsedMs:r.elapsedMs(),message:"timeout"});let o=Math.max(1e3,n?.remainingMs()??t),s=await U(e,Math.min(o,L));if(i=s,"1"!==s.stdout.trim())throw new I("COMMAND_FAILED","Android device is still booting",{serial:e,stdout:s.stdout,stderr:s.stderr,exitCode:s.exitCode})},{maxAttempts:n,baseDelayMs:1e3,maxDelayMs:1e3,jitter:0,shouldRetry:e=>{let t=$({error:e,stdout:i?.stdout,stderr:i?.stderr,context:{platform:"android",phase:"boot"}});return"ADB_TRANSPORT_UNAVAILABLE"!==t&&"ANDROID_BOOT_TIMEOUT"!==t}},{deadline:r,phase:"boot",classifyReason:e=>$({error:e,stdout:i?.stdout,stderr:i?.stderr,context:{platform:"android",phase:"boot"}})})}catch(u){let n=w(u),o=i?.stdout,s=i?.stderr,l=i?.exitCode,c=$({error:u,stdout:o,stderr:s,context:{platform:"android",phase:"boot"}});"BOOT_COMMAND_FAILED"===c&&"Android device is still booting"===n.message&&(c="ANDROID_BOOT_TIMEOUT");let d={serial:e,timeoutMs:t,elapsedMs:r.elapsedMs(),reason:c,hint:F(c),stdout:o,stderr:s,exitCode:l};if(a||"ANDROID_BOOT_TIMEOUT"===c)throw new I("COMMAND_FAILED","Android device did not finish booting in time",d);if("TOOL_MISSING"===n.code)throw new I("TOOL_MISSING",n.message,{...d,...n.details??{}});if("ADB_TRANSPORT_UNAVAILABLE"===c)throw new I("COMMAND_FAILED",n.message,{...d,...n.details??{}});throw new I(n.code,n.message,{...d,...n.details??{}},n.cause)}}function W(e){let t=e.trim();if(!t||/\s/.test(t))return!1;let i=/^([A-Za-z][A-Za-z0-9+.-]*):(.+)$/.exec(t);if(!i)return!1;let r=i[1]?.toLowerCase(),n=i[2]??"";return"http"!==r&&"https"!==r&&"ws"!==r&&"wss"!==r&&"ftp"!==r&&"ftps"!==r||n.startsWith("//")}function J(e,t){let i,r=e?.trim();return r?r:"http"===(i=t.trim().split(":")[0]?.toLowerCase())||"https"===i?"com.apple.mobilesafari":void 0}function H(e){let t=z(e),i=e=>{let i=X(t,e);if(null!==i)return"true"===i};return{text:X(t,"text"),desc:X(t,"content-desc"),resourceId:X(t,"resource-id"),className:X(t,"class"),bounds:X(t,"bounds"),clickable:i("clickable"),enabled:i("enabled"),focusable:i("focusable"),focused:i("focused")}}function z(e){let t=new Map,i=e.indexOf(" "),r=e.lastIndexOf(">");if(i<0||r<=i)return t;let n=/([^\s=/>]+)\s*=\s*(["'])([\s\S]*?)\2/y,a=i;for(;a<r;){for(;a<r;){let t=e[a];if(" "!==t&&"\n"!==t&&"\r"!==t&&" "!==t)break;a+=1}if(a>=r)break;let i=e[a];if("/"===i||">"===i)break;n.lastIndex=a;let o=n.exec(e);if(!o)break;t.set(o[1],o[3]),a=n.lastIndex}return t}function X(e,t){return e.get(t)??null}function Y(e){if(!e)return;let t=/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/.exec(e);if(!t)return;let i=Number(t[1]),r=Number(t[2]);return{x:i,y:r,width:Math.max(0,Number(t[3])-i),height:Math.max(0,Number(t[4])-r)}}function K(e){return e?e.toLowerCase():""}function Z(e){let t=e.trim();return!!t&&/^[\w.]+:id\/[\w.-]+$/i.test(t)}let Q={settings:{type:"intent",value:"android.settings.SETTINGS"}};function ee(e,t){return["-s",e.id,...t]}async function et(e,t){let i=t.trim();if(i.includes("."))return{type:"package",value:i};let r=Q[i.toLowerCase()];if(r)return r;let n=(await u("adb",ee(e,["shell","pm","list","packages"]))).stdout.split("\n").map(e=>e.replace("package:","").trim()).filter(Boolean).filter(e=>e.toLowerCase().includes(i.toLowerCase()));if(1===n.length)return{type:"package",value:n[0]};if(n.length>1)throw new I("INVALID_ARGS",`Multiple packages matched "${t}"`,{matches:n});throw new I("APP_NOT_INSTALLED",`No package found matching "${t}"`)}async function ei(e,t="all"){let i=await er(e);return("user-installed"===t?(await en(e)).filter(e=>i.has(e)):Array.from(i)).sort((e,t)=>e.localeCompare(t)).map(e=>({package:e,name:function(e){let t=new Set(["com","android","google","app","apps","service","services","mobile","client"]),i=e.split(".").flatMap(e=>e.split(/[_-]+/)).map(e=>e.trim().toLowerCase()).filter(e=>e.length>0),r=i[i.length-1]??e;for(let e=i.length-1;e>=0;e-=1){let n=i[e];if(!t.has(n)){r=n;break}}return r.split(/[^a-z0-9]+/i).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}(e)}))}async function er(e){let t=await u("adb",ee(e,["shell","cmd","package","query-activities","--brief","-a","android.intent.action.MAIN","-c","android.intent.category.LAUNCHER"]),{allowFailure:!0});if(0!==t.exitCode||0===t.stdout.trim().length)return new Set;let i=new Set;for(let e of t.stdout.split("\n")){let t=e.trim();if(!t)continue;let r=t.split(/\s+/)[0],n=r.includes("/")?r.split("/")[0]:r;n&&i.add(n)}return i}async function en(e){return(await u("adb",ee(e,["shell","pm","list","packages","-3"]))).stdout.split("\n").map(e=>e.replace("package:","").trim()).filter(Boolean)}async function ea(e){let t=await eo(e,[["shell","dumpsys","window","windows"],["shell","dumpsys","window"]]);if(t)return t;let i=await eo(e,[["shell","dumpsys","activity","activities"],["shell","dumpsys","activity"]]);return i||{}}async function eo(e,t){for(let i of t){let t=function(e){for(let t of[/mCurrentFocus=Window\{[^}]*\s([\w.]+)\/([\w.$]+)/,/mFocusedApp=AppWindowToken\{[^}]*\s([\w.]+)\/([\w.$]+)/,/mResumedActivity:.*?\s([\w.]+)\/([\w.$]+)/,/ResumedActivity:.*?\s([\w.]+)\/([\w.$]+)/]){let i=t.exec(e);if(i)return{package:i[1],activity:i[2]}}return null}((await u("adb",ee(e,i),{allowFailure:!0})).stdout??"");if(t)return t}return null}async function es(e,t,i){e.booted||await q(e.id);let r=t.trim();if(W(r)){if(i)throw new I("INVALID_ARGS","Activity override is not supported when opening a deep link URL");await u("adb",ee(e,["shell","am","start","-W","-a","android.intent.action.VIEW","-d",r]));return}let n=await et(e,t);if("intent"===n.type){if(i)throw new I("INVALID_ARGS","Activity override requires a package name, not an intent");await u("adb",ee(e,["shell","am","start","-W","-a",n.value]));return}if(i){let t=i.includes("/")?i:`${n.value}/${i.startsWith(".")?i:`.${i}`}`;await u("adb",ee(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-n",t]));return}try{await u("adb",ee(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-p",n.value]));return}catch(i){let t=await el(e,n.value);if(!t)throw i;await u("adb",ee(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-n",t]))}}async function el(e,t){let i=await u("adb",ee(e,["shell","cmd","package","resolve-activity","--brief",t]),{allowFailure:!0});return 0!==i.exitCode?null:function(e){let t=e.split("\n").map(e=>e.trim()).filter(Boolean);for(let e=t.length-1;e>=0;e-=1){let i=t[e];if(i.includes("/"))return i.split(/\s+/)[0]}return null}(i.stdout)}async function ec(e){e.booted||await q(e.id)}async function ed(e,t){if("settings"===t.trim().toLowerCase())return void await u("adb",ee(e,["shell","am","force-stop","com.android.settings"]));let i=await et(e,t);if("intent"===i.type)throw new I("INVALID_ARGS","Close requires a package name, not an intent");await u("adb",ee(e,["shell","am","force-stop",i.value]))}async function eu(e,t){let i=await et(e,t);if("intent"===i.type)throw new I("INVALID_ARGS","reinstall requires a package name, not an intent");let r=await u("adb",ee(e,["uninstall",i.value]),{allowFailure:!0});if(0!==r.exitCode){let e=`${r.stdout}
|
|
3
|
-
${r.stderr}`.toLowerCase();if(!e.includes("unknown package")&&!e.includes("not installed"))throw new
|
|
4
|
-
${r}`,a=/dumped to:\s*(\S+)/i.exec(n),a?.[1]??t),
|
|
5
|
-
${t}`,r=i.indexOf("<?xml"),n=r>=0?r:i.indexOf("<hierarchy");if(n<0)return null;let a=i.lastIndexOf("</hierarchy>");if(a<0||a<n)return null;let o=i.slice(n,a+12).trim();return o.length>0?o:null}function
|
|
6
|
-
${t}`.toLowerCase();return i.includes("device is busy")&&i.includes("connecting")?"iOS device is still connecting. Keep it unlocked and connected by cable until it is fully available in Xcode Devices, then retry.":i.includes("coredeviceservice")&&i.includes("timed out")?"CoreDevice service timed out. Reconnect the device and retry; if it persists restart Xcode and the iOS device.":null}async function
|
|
7
|
-
${t.stderr}`.toLowerCase(),
|
|
8
|
-
${a}`.toLowerCase()))throw new
|
|
9
|
-
${r.stderr}`.toLowerCase()))throw new I("COMMAND_FAILED",`simctl uninstall failed for ${i}`,{stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode});return{bundleId:i}}async function e5(e,t){"simulator"!==e.kind?await eJ(["device","install","app","--device",e.id,t],{action:"install iOS app",deviceId:e.id}):(await eY(e),await u("xcrun",["simctl","install",e.id,t]))}async function e8(e,t,i){let{bundleId:r}=await e4(e,t);return await e5(e,i),{bundleId:r}}async function e6(e,t){if("simulator"===e.kind){await eY(e),await u("xcrun",["simctl","io",e.id,"screenshot",t]);return}await eJ(["device","screenshot","--device",e.id,t],{action:"capture iOS screenshot",deviceId:e.id})}async function e7(e,t,i,r){if("simulator"!==e.kind)throw new I("UNSUPPORTED_OPERATION","settings is only supported on iOS simulators");switch(await eY(e),t.toLowerCase()){case"wifi":{let t=tt(i);await u("xcrun",["simctl","status_bar",e.id,"override","--wifiMode",t?"active":"failed"]);return}case"airplane":return void(tt(i)?await u("xcrun",["simctl","status_bar",e.id,"override","--dataNetwork","hide","--wifiMode","failed","--wifiBars","0","--cellularMode","failed","--cellularBars","0","--operatorName",""]):await u("xcrun",["simctl","status_bar",e.id,"clear"]));case"location":{let t=tt(i);if(!r)throw new I("INVALID_ARGS","location setting requires an active app in session");await u("xcrun",["simctl","privacy",e.id,t?"grant":"revoke","location",r]);return}case"faceid":{let t=function(e){let t=e.trim().toLowerCase();if("match"===t)return"match";if("nonmatch"===t)return"nonmatch";if("enroll"===t)return"enroll";if("unenroll"===t)return"unenroll";throw new I("INVALID_ARGS",`Invalid faceid state: ${e}. Use match|nonmatch|enroll|unenroll.`)}(i);await ti(e.id,t);return}default:throw new I("INVALID_ARGS",`Unsupported setting: ${t}`)}}async function e9(e,t="all"){var i;return"simulator"===e.kind?(i=await te(e),"user-installed"===t?i.filter(e=>!e.bundleId.startsWith("com.apple.")):i):await eH(e,t)}async function te(e){let t=(await u("xcrun",["simctl","listapps",e.id],{allowFailure:!0})).stdout.trim();if(!t)return[];let i=null;if(t.startsWith("{"))try{i=JSON.parse(t)}catch{i=null}if(!i&&t.startsWith("{"))try{let e=await u("plutil",["-convert","json","-o","-","-"],{allowFailure:!0,stdin:t});0===e.exitCode&&e.stdout.trim().startsWith("{")&&(i=JSON.parse(e.stdout))}catch{i=null}return i?Object.entries(i).map(([e,t])=>({bundleId:e,name:t.CFBundleDisplayName??t.CFBundleName??e})):[]}function tt(e){let t=e.toLowerCase();if("on"===t||"true"===t||"1"===t)return!0;if("off"===t||"false"===t||"0"===t)return!1;throw new I("INVALID_ARGS",`Invalid setting state: ${e}`)}async function ti(e,t){let i=function(e,t){switch(t){case"match":return[["simctl","biometric",e,"match","face"],["simctl","biometric","match",e,"face"]];case"nonmatch":return[["simctl","biometric",e,"nonmatch","face"],["simctl","biometric",e,"nomatch","face"],["simctl","biometric","nonmatch",e,"face"],["simctl","biometric","nomatch",e,"face"]];case"enroll":return[["simctl","biometric",e,"enroll","yes"],["simctl","biometric",e,"enroll","1"],["simctl","biometric","enroll",e,"yes"],["simctl","biometric","enroll",e,"1"]];case"unenroll":return[["simctl","biometric",e,"enroll","no"],["simctl","biometric",e,"enroll","0"],["simctl","biometric","enroll",e,"no"],["simctl","biometric","enroll",e,"0"]]}}(e,t),r=[];for(let e of i){let t=await u("xcrun",e,{allowFailure:!0});if(0===t.exitCode)return;r.push({args:e,stderr:t.stderr,stdout:t.stdout,exitCode:t.exitCode})}throw new I("COMMAND_FAILED","simctl biometric command failed. Ensure your Xcode Simulator runtime supports Face ID control.",{deviceId:e,action:t,attempts:r.map(e=>({args:e.args.join(" "),exitCode:e.exitCode,stderr:e.stderr.slice(0,400)}))})}function tr(e){if(!(e instanceof I)||"COMMAND_FAILED"!==e.code)return!1;let t=e.details??{};if(4!==t.exitCode)return!1;let i=String(t.stderr??"").toLowerCase();return i.includes("fbsopenapplicationserviceerrordomain")&&i.includes("the request to open")}async function tn(e,t){await eY(e),await u("open",["-a","Simulator"],{allowFailure:!0});let i=C.fromTimeoutMs(eq);await R(async({deadline:i})=>{if(i?.isExpired())throw new I("COMMAND_FAILED","App launch deadline exceeded",{timeoutMs:eq});let r=await u("xcrun",["simctl","launch",e.id,t],{allowFailure:!0});if(0!==r.exitCode)throw new I("COMMAND_FAILED",`xcrun exited with code ${r.exitCode}`,{cmd:"xcrun",args:["simctl","launch",e.id,t],stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode})},{maxAttempts:30,baseDelayMs:1e3,maxDelayMs:5e3,jitter:.2,shouldRetry:tr},{deadline:i})}async function ta(e,t,i){let r=["device","process","launch","--device",e.id,t];i?.payloadUrl&&r.push("--payload-url",i.payloadUrl),await eJ(r,{action:"launch iOS app",deviceId:e.id})}async function to(e,t,i){let r=(e.get(t)??Promise.resolve()).catch(()=>{}).then(i);return e.set(t,r),r.finally(()=>{e.get(t)===r&&e.delete(t)})}let ts=new Map,tl=new Map,tc=eV(process.env.AGENT_DEVICE_RUNNER_STARTUP_TIMEOUT_MS,45e3,5e3),td=eV(process.env.AGENT_DEVICE_RUNNER_COMMAND_TIMEOUT_MS,15e3,1e3),tu=eV(process.env.AGENT_DEVICE_RUNNER_CONNECT_ATTEMPT_INTERVAL_MS,250,50),tp=eV(process.env.AGENT_DEVICE_RUNNER_CONNECT_RETRY_BASE_DELAY_MS,300,10),tf=eV(process.env.AGENT_DEVICE_RUNNER_CONNECT_RETRY_MAX_DELAY_MS,2e3,10),tm=eV(process.env.AGENT_DEVICE_RUNNER_CONNECT_REQUEST_TIMEOUT_MS,5e3,250),th=eV(process.env.AGENT_DEVICE_IOS_DEVICE_INFO_TIMEOUT_MS,1e4,500),tw=eV(process.env.AGENT_DEVICE_RUNNER_DESTINATION_TIMEOUT_SECONDS,20,5),tg=i.join(S.homedir(),".agent-device","ios-runner");async function tv(e,t,i={}){var r;return(function(e){if("ios"!==e.platform)throw new I("UNSUPPORTED_PLATFORM",`Unsupported platform for iOS runner: ${e.platform}`);if("simulator"!==e.kind&&"device"!==e.kind)throw new I("UNSUPPORTED_OPERATION",`Unsupported iOS device kind for runner: ${e.kind}`)}(e),"snapshot"===(r=t.command)||"findText"===r||"listTappables"===r||"alert"===r)?T(()=>tI(e,t,i),{shouldRetry:tC}):tI(e,t,i)}async function tI(e,t,i={}){let r;try{let n=(r=await tO(e,i)).ready?td:tc;return await tA(e,r,t,i.logPath,n)}catch(a){let n=a instanceof I?a:new I("COMMAND_FAILED",String(a));if("COMMAND_FAILED"===n.code&&"string"==typeof n.message&&n.message.includes("Runner did not accept connection")&&tT(n)&&r?.ready){r?await tb(r):await tN(e.id),r=await tO(e,i);let n=await t$(r.device,r.port,t,i.logPath,tc);return await ty(n,r,i.logPath)}throw a}}async function tA(e,t,i,r,n){let a=await t$(e,t.port,i,r,n,t);return await ty(a,t,r)}async function ty(e,t,i){let r=await e.text(),n={};try{n=JSON.parse(r)}catch{throw new I("COMMAND_FAILED","Invalid runner response",{text:r})}if(!n.ok)throw new I("COMMAND_FAILED",n.error?.message??"Runner error",{runner:n,xcodebuild:{exitCode:1,stdout:"",stderr:""},logPath:i});return t.ready=!0,n.data??{}}async function tN(e){await to(tl,e,async()=>{await t_(e)})}async function tS(){let e=Array.from(ts.keys());await Promise.allSettled(e.map(async e=>{await tN(e)}))}async function tb(e){await to(tl,e.deviceId,async()=>{await t_(e.deviceId,e)})}async function t_(e,t){let i=t??ts.get(e);if(i){try{await t$(i.device,i.port,{command:"shutdown"},void 0,15e3)}catch{await tM(i.child.pid,"SIGTERM")}try{await Promise.race([i.testPromise,new Promise(e=>setTimeout(e,1e4))])}catch{}await tM(i.child.pid,"SIGKILL"),tq(i.xctestrunPath),tq(i.jsonPath),ts.get(e)===i&&ts.delete(e)}}async function tD(e){await u("xcrun",["simctl","bootstatus",e,"-b"],{allowFailure:!0,timeoutMs:tc})}async function tO(e,t){return await to(tl,e.id,async()=>{var i,r;let n=ts.get(e.id);if(n){if((i=n.child.pid)&&v(i))return n;await t_(e.id,n)}await ("simulator"!==(r=e).kind?Promise.resolve():tD(r.id));let a=await tE(e,t),o=await tj(),{xctestrunPath:s,jsonPath:c}=await tB(a,{AGENT_DEVICE_RUNNER_PORT:String(o)},`session-${e.id}-${o}`),{child:d,wait:u}=l("xcodebuild",["test-without-building","-only-testing","AgentDeviceRunnerUITests/RunnerTests/testCommand","-parallel-testing-enabled","NO","-test-timeouts-enabled","NO",tx(e),"1","-destination-timeout",String(tw),"-xctestrun",s,"-destination",function(e){if("ios"!==e.platform)throw new I("UNSUPPORTED_PLATFORM",`Unsupported platform for iOS runner: ${e.platform}`);return"simulator"===e.kind?`platform=iOS Simulator,id=${e.id}`:`platform=iOS,id=${e.id}`}(e)],{allowFailure:!0,env:{...process.env,AGENT_DEVICE_RUNNER_PORT:String(o)}});d.stdout?.on("data",e=>{tL(e,t.logPath,t.traceLogPath,t.verbose)}),d.stderr?.on("data",e=>{tL(e,t.logPath,t.traceLogPath,t.verbose)});let p={device:e,deviceId:e.id,port:o,xctestrunPath:s,jsonPath:c,testPromise:u,child:d,ready:!1};return ts.set(e.id,p),p})}async function tM(e,t){if(!e||e<=0)return;try{process.kill(e,t)}catch{}let i="SIGTERM"===t?"TERM":"KILL";try{await u("pkill",[`-${i}`,"-P",String(e)],{allowFailure:!0})}catch{}}async function tE(e,t){var a,o;let s,l=(a=e.kind,(s=process.env.AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH?.trim())?i.resolve(s):"simulator"===a?i.join(tg,"derived"):i.join(tg,"derived",a));if(E(process.env.AGENT_DEVICE_IOS_CLEAN_DERIVED)){!function(e,t=process.env){if(t.AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH?.trim()&&!function(e=process.env){return E(e.AGENT_DEVICE_IOS_ALLOW_OVERRIDE_DERIVED_CLEAN)}(t))throw new I("COMMAND_FAILED","Refusing to clean AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH automatically",{derivedPath:e,hint:"Unset AGENT_DEVICE_IOS_CLEAN_DERIVED, or set AGENT_DEVICE_IOS_ALLOW_OVERRIDE_DERIVED_CLEAN=1 if you trust this path."})}(l);try{A.rmSync(l,{recursive:!0,force:!0})}catch{}}let c=tk(l);if(c)return c;let d=function(){let e=i.dirname(n(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=i.join(t,"package.json");if(A.existsSync(e))return t;t=i.dirname(t)}return e}(),u=i.join(d,"ios-runner","AgentDeviceRunner","AgentDeviceRunner.xcodeproj");if(!A.existsSync(u))throw new I("COMMAND_FAILED","iOS runner project not found",{projectPath:u});let p=function(e=process.env,t=!1){if(!t)return[];let i=e.AGENT_DEVICE_IOS_TEAM_ID?.trim()||"",r=e.AGENT_DEVICE_IOS_SIGNING_IDENTITY?.trim()||"",n=e.AGENT_DEVICE_IOS_PROVISIONING_PROFILE?.trim()||"",a=["CODE_SIGN_STYLE=Automatic"];return i&&a.push(`DEVELOPMENT_TEAM=${i}`),r&&a.push(`CODE_SIGN_IDENTITY=${r}`),n&&a.push(`PROVISIONING_PROFILE_SPECIFIER=${n}`),a}(process.env,"device"===e.kind),f="device"===e.kind?["-allowProvisioningUpdates"]:[];try{await r("xcodebuild",["build-for-testing","-project",u,"-scheme","AgentDeviceRunner","-parallel-testing-enabled","NO",tx(e),"1","-destination",function(e){if("ios"!==e.platform)throw new I("UNSUPPORTED_PLATFORM",`Unsupported platform for iOS runner: ${e.platform}`);return"simulator"===e.kind?`platform=iOS Simulator,id=${e.id}`:"generic/platform=iOS"}(e),"-derivedDataPath",l,...f,...p],{onStdoutChunk:e=>{tL(e,t.logPath,t.traceLogPath,t.verbose)},onStderrChunk:e=>{tL(e,t.logPath,t.traceLogPath,t.verbose)}})}catch(a){let e,i,r=a instanceof I?a:new I("COMMAND_FAILED",String(a)),n=(e=(o=r).details?JSON.stringify(o.details):"",(i=`${o.message}
|
|
10
|
-
${e}`.toLowerCase()).includes("requires a development team")?"Configure signing in Xcode or set AGENT_DEVICE_IOS_TEAM_ID for physical-device runs.":i.includes("no profiles for")||i.includes("provisioning profile")?"Install/select a valid iOS provisioning profile, or set AGENT_DEVICE_IOS_PROVISIONING_PROFILE.":i.includes("code signing")?"Enable Automatic Signing in Xcode or provide AGENT_DEVICE_IOS_TEAM_ID and optional AGENT_DEVICE_IOS_SIGNING_IDENTITY.":void 0);throw new
|
|
1
|
+
import{isCancel as e,select as t}from"@clack/prompts";import{node_path as i,validateAndNormalizeBatchSteps as r,isAgentDeviceDaemonProcess as n,runCmdBackground as a,node_crypto as o,runCmd as s,displayLabel as l,asAppError as d,AppError as c,whichCmd as u,buildSnapshotDisplayLines as p,fileURLToPath as f,runCmdStreaming as m,formatRole as h,normalizeError as w,readVersion as g,getDiagnosticsMeta as v,withDiagnosticTimer as I,emitDiagnostic as y,promises as A,DEFAULT_BATCH_MAX_STEPS as N,isProcessAlive as S,readProcessStartTime as b,node_fs as _,withDiagnosticsScope as D,flushDiagnosticsToSessionFile as M,node_os as O,node_net as x,formatSnapshotLine as k}from"./350.js";async function E(i,r){let n=i,a=e=>e.toLowerCase().replace(/_/g," ").replace(/\s+/g," ").trim();if(r.platform&&(n=n.filter(e=>e.platform===r.platform)),r.udid){let e=n.find(e=>e.id===r.udid&&"ios"===e.platform);if(!e)throw new c("DEVICE_NOT_FOUND",`No iOS device with UDID ${r.udid}`);return e}if(r.serial){let e=n.find(e=>e.id===r.serial&&"android"===e.platform);if(!e)throw new c("DEVICE_NOT_FOUND",`No Android device with serial ${r.serial}`);return e}if(r.deviceName){let e=a(r.deviceName),t=n.find(t=>a(t.name)===e);if(!t)throw new c("DEVICE_NOT_FOUND",`No device named ${r.deviceName}`);return t}if(1===n.length)return n[0];if(0===n.length)throw new c("DEVICE_NOT_FOUND","No devices found",{selector:r});let o=n.filter(e=>e.booted);if(1===o.length)return o[0];if(!process.env.CI&&process.stdin.isTTY&&process.stdout.isTTY){let i=await t({message:"Multiple devices available. Choose a device to continue:",options:(o.length>0?o:n).map(e=>({label:`${e.name} (${e.platform}${e.kind?`, ${e.kind}`:""}${e.booted?", booted":""})`,value:e.id}))});if(e(i))throw new c("INVALID_ARGS","Device selection cancelled");if(i){let e=n.find(e=>e.id===i);if(e)return e}}return o[0]??n[0]}let C=L(process.env.AGENT_DEVICE_RETRY_LOGS);function L(e){return["1","true","yes","on"].includes((e??"").toLowerCase())}let R=2e4,T=12e4,P=1e4;class ${startedAtMs;expiresAtMs;constructor(e,t){this.startedAtMs=e,this.expiresAtMs=e+Math.max(0,t)}static fromTimeoutMs(e,t=Date.now()){return new $(t,e)}remainingMs(e=Date.now()){return Math.max(0,this.expiresAtMs-e)}elapsedMs(e=Date.now()){return Math.max(0,e-this.startedAtMs)}isExpired(e=Date.now()){return 0>=this.remainingMs(e)}}async function F(e,t={},i={}){let r,n={maxAttempts:t.maxAttempts??3,baseDelayMs:t.baseDelayMs??200,maxDelayMs:t.maxDelayMs??2e3,jitter:t.jitter??.2,shouldRetry:t.shouldRetry};for(let t=1;t<=n.maxAttempts&&(!i.deadline?.isExpired()||!(t>1));t+=1)try{let r=await e({attempt:t,maxAttempts:n.maxAttempts,deadline:i.deadline});return i.onEvent?.({phase:i.phase,event:"succeeded",attempt:t,maxAttempts:n.maxAttempts,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs()}),U({phase:i.phase,event:"succeeded",attempt:t,maxAttempts:n.maxAttempts,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs()}),r}catch(d){r=d;let e=i.classifyReason?.(d),a={phase:i.phase,event:"attempt_failed",attempt:t,maxAttempts:n.maxAttempts,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs(),reason:e};if(i.onEvent?.(a),U(a),t>=n.maxAttempts||n.shouldRetry&&!n.shouldRetry(d,t))break;let o=function(e,t,i,r){let n=Math.min(t,e*2**(r-1));return Math.max(0,n+n*i*(2*Math.random()-1))}(n.baseDelayMs,n.maxDelayMs,n.jitter,t),s=i.deadline?Math.min(o,i.deadline.remainingMs()):o;if(s<=0)break;let l={phase:i.phase,event:"retry_scheduled",attempt:t,maxAttempts:n.maxAttempts,delayMs:s,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs(),reason:e};i.onEvent?.(l),U(l),await function(e){return new Promise(t=>setTimeout(t,e))}(s)}let a={phase:i.phase,event:"exhausted",attempt:n.maxAttempts,maxAttempts:n.maxAttempts,elapsedMs:i.deadline?.elapsedMs(),remainingMs:i.deadline?.remainingMs(),reason:i.classifyReason?.(r)};if(i.onEvent?.(a),U(a),r)throw r;throw new c("COMMAND_FAILED","retry failed")}async function V(e,t={}){return F(()=>e(),{maxAttempts:t.attempts,baseDelayMs:t.baseDelayMs,maxDelayMs:t.maxDelayMs,jitter:t.jitter,shouldRetry:t.shouldRetry})}function U(e){y({level:"attempt_failed"===e.event||"exhausted"===e.event?"warn":"debug",phase:"retry",data:{...e}}),C&&process.stderr.write(`[agent-device][retry] ${JSON.stringify(e)}
|
|
2
|
+
`)}function G(e){let t=e.error?d(e.error):null,i=e.context?.platform,r=e.context?.phase;if(t?.code==="TOOL_MISSING")return"android"===i?"ADB_TRANSPORT_UNAVAILABLE":"IOS_TOOL_MISSING";let n=t?.details??{},a="string"==typeof n.message?n.message:void 0,o="string"==typeof n.stdout?n.stdout:void 0,s="string"==typeof n.stderr?n.stderr:void 0,l=n.boot&&"object"==typeof n.boot?n.boot:null,c=n.bootstatus&&"object"==typeof n.bootstatus?n.bootstatus:null,u=[e.message,t?.message,e.stdout,e.stderr,a,o,s,"string"==typeof l?.stdout?l.stdout:void 0,"string"==typeof l?.stderr?l.stderr:void 0,"string"==typeof c?.stdout?c.stdout:void 0,"string"==typeof c?.stderr?c.stderr:void 0].filter(Boolean).join("\n").toLowerCase();return"ios"===i&&(u.includes("runner did not accept connection")||"connect"===r&&(u.includes("timed out")||u.includes("timeout")||u.includes("econnrefused")||u.includes("connection refused")||u.includes("fetch failed")||u.includes("socket hang up")))?"IOS_RUNNER_CONNECT_TIMEOUT":"ios"===i&&"boot"===r&&(u.includes("timed out")||u.includes("timeout"))?"IOS_BOOT_TIMEOUT":"android"===i&&"boot"===r&&(u.includes("timed out")||u.includes("timeout"))?"ANDROID_BOOT_TIMEOUT":u.includes("resource temporarily unavailable")||u.includes("killed: 9")||u.includes("cannot allocate memory")||u.includes("system is low on memory")?"CI_RESOURCE_STARVATION_SUSPECTED":"android"===i&&(u.includes("device not found")||u.includes("no devices")||u.includes("device offline")||u.includes("offline")||u.includes("unauthorized")||u.includes("not authorized")||u.includes("unable to locate device")||u.includes("invalid device"))?"ADB_TRANSPORT_UNAVAILABLE":t?.code==="COMMAND_FAILED"||u.length>0?"BOOT_COMMAND_FAILED":"UNKNOWN"}function q(e){switch(e){case"IOS_BOOT_TIMEOUT":return"Retry simulator boot and inspect simctl bootstatus logs; in CI consider increasing AGENT_DEVICE_IOS_BOOT_TIMEOUT_MS.";case"IOS_RUNNER_CONNECT_TIMEOUT":return"Retry runner startup, inspect xcodebuild logs, and verify simulator responsiveness before command execution.";case"ANDROID_BOOT_TIMEOUT":return"Retry emulator startup and verify sys.boot_completed reaches 1; consider increasing startup budget in CI.";case"ADB_TRANSPORT_UNAVAILABLE":return"Check adb server/device transport (adb devices -l), restart adb, and ensure the target device is online and authorized.";case"CI_RESOURCE_STARVATION_SUSPECTED":return"CI machine may be resource constrained; reduce parallel jobs or use a larger runner.";case"IOS_TOOL_MISSING":return"Xcode command-line tools are missing or not in PATH; run xcode-select --install and verify xcrun works.";case"BOOT_COMMAND_FAILED":return"Inspect command stderr/stdout for the failing boot phase and retry after environment validation.";default:return"Retry once and inspect verbose logs for the failing phase."}}function B(e){return e.startsWith("emulator-")}async function j(e,t=P){return s("adb",["-s",e,"shell","getprop","sys.boot_completed"],{allowFailure:!0,timeoutMs:t})}async function W(e,t){let i=t.replace(/_/g," ").trim();if(!B(e))return i||e;let r=await s("adb",["-s",e,"emu","avd","name"],{allowFailure:!0,timeoutMs:P}),n=r.stdout.trim();return 0===r.exitCode&&n?n.replace(/_/g," "):i||e}async function J(){if(!await u("adb"))throw new c("TOOL_MISSING","adb not found in PATH");let e=(await s("adb",["devices","-l"],{timeoutMs:P})).stdout.split("\n").map(e=>e.trim()).filter(e=>e.length>0&&!e.startsWith("List of devices")).map(e=>e.split(/\s+/)).filter(e=>"device"===e[1]).map(e=>({serial:e[0],rawModel:(e.find(e=>e.startsWith("model:"))??"").replace("model:","")}));return await Promise.all(e.map(async({serial:e,rawModel:t})=>{let[i,r]=await Promise.all([W(e,t),H(e)]);return{platform:"android",id:e,name:i,kind:B(e)?"emulator":"device",booted:r}}))}async function H(e){try{let t=await j(e);return"1"===t.stdout.trim()}catch{return!1}}async function z(e,t=6e4){let i,r=$.fromTimeoutMs(t),n=Math.max(1,Math.ceil(t/1e3)),a=!1;try{await F(async({deadline:n})=>{if(n?.isExpired())throw a=!0,new c("COMMAND_FAILED","Android boot deadline exceeded",{serial:e,timeoutMs:t,elapsedMs:r.elapsedMs(),message:"timeout"});let o=Math.max(1e3,n?.remainingMs()??t),s=await j(e,Math.min(o,P));if(i=s,"1"!==s.stdout.trim())throw new c("COMMAND_FAILED","Android device is still booting",{serial:e,stdout:s.stdout,stderr:s.stderr,exitCode:s.exitCode})},{maxAttempts:n,baseDelayMs:1e3,maxDelayMs:1e3,jitter:0,shouldRetry:e=>{let t=G({error:e,stdout:i?.stdout,stderr:i?.stderr,context:{platform:"android",phase:"boot"}});return"ADB_TRANSPORT_UNAVAILABLE"!==t&&"ANDROID_BOOT_TIMEOUT"!==t}},{deadline:r,phase:"boot",classifyReason:e=>G({error:e,stdout:i?.stdout,stderr:i?.stderr,context:{platform:"android",phase:"boot"}})})}catch(f){let n=d(f),o=i?.stdout,s=i?.stderr,l=i?.exitCode,u=G({error:f,stdout:o,stderr:s,context:{platform:"android",phase:"boot"}});"BOOT_COMMAND_FAILED"===u&&"Android device is still booting"===n.message&&(u="ANDROID_BOOT_TIMEOUT");let p={serial:e,timeoutMs:t,elapsedMs:r.elapsedMs(),reason:u,hint:q(u),stdout:o,stderr:s,exitCode:l};if(a||"ANDROID_BOOT_TIMEOUT"===u)throw new c("COMMAND_FAILED","Android device did not finish booting in time",p);if("TOOL_MISSING"===n.code)throw new c("TOOL_MISSING",n.message,{...p,...n.details??{}});if("ADB_TRANSPORT_UNAVAILABLE"===u)throw new c("COMMAND_FAILED",n.message,{...p,...n.details??{}});throw new c(n.code,n.message,{...p,...n.details??{}},n.cause)}}function Y(e){let t=e.trim();if(!t||/\s/.test(t))return!1;let i=/^([A-Za-z][A-Za-z0-9+.-]*):(.+)$/.exec(t);if(!i)return!1;let r=i[1]?.toLowerCase(),n=i[2]??"";return"http"!==r&&"https"!==r&&"ws"!==r&&"wss"!==r&&"ftp"!==r&&"ftps"!==r||n.startsWith("//")}function X(e,t){let i,r=e?.trim();return r?r:"http"===(i=t.trim().split(":")[0]?.toLowerCase())||"https"===i?"com.apple.mobilesafari":void 0}function K(e){let t=Z(e),i=e=>{let i=Q(t,e);if(null!==i)return"true"===i};return{text:Q(t,"text"),desc:Q(t,"content-desc"),resourceId:Q(t,"resource-id"),className:Q(t,"class"),bounds:Q(t,"bounds"),clickable:i("clickable"),enabled:i("enabled"),focusable:i("focusable"),focused:i("focused")}}function Z(e){let t=new Map,i=e.indexOf(" "),r=e.lastIndexOf(">");if(i<0||r<=i)return t;let n=/([^\s=/>]+)\s*=\s*(["'])([\s\S]*?)\2/y,a=i;for(;a<r;){for(;a<r;){let t=e[a];if(" "!==t&&"\n"!==t&&"\r"!==t&&" "!==t)break;a+=1}if(a>=r)break;let i=e[a];if("/"===i||">"===i)break;n.lastIndex=a;let o=n.exec(e);if(!o)break;t.set(o[1],o[3]),a=n.lastIndex}return t}function Q(e,t){return e.get(t)??null}function ee(e){if(!e)return;let t=/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/.exec(e);if(!t)return;let i=Number(t[1]),r=Number(t[2]);return{x:i,y:r,width:Math.max(0,Number(t[3])-i),height:Math.max(0,Number(t[4])-r)}}function et(e){return e?e.toLowerCase():""}function ei(e){let t=e.trim();return!!t&&/^[\w.]+:id\/[\w.-]+$/i.test(t)}let er={settings:{type:"intent",value:"android.settings.SETTINGS"}};function en(e,t){return["-s",e.id,...t]}async function ea(e,t){let i=t.trim();if(i.includes("."))return{type:"package",value:i};let r=er[i.toLowerCase()];if(r)return r;let n=(await s("adb",en(e,["shell","pm","list","packages"]))).stdout.split("\n").map(e=>e.replace("package:","").trim()).filter(Boolean).filter(e=>e.toLowerCase().includes(i.toLowerCase()));if(1===n.length)return{type:"package",value:n[0]};if(n.length>1)throw new c("INVALID_ARGS",`Multiple packages matched "${t}"`,{matches:n});throw new c("APP_NOT_INSTALLED",`No package found matching "${t}"`)}async function eo(e,t="all"){let i=await es(e);return("user-installed"===t?(await el(e)).filter(e=>i.has(e)):Array.from(i)).sort((e,t)=>e.localeCompare(t)).map(e=>({package:e,name:function(e){let t=new Set(["com","android","google","app","apps","service","services","mobile","client"]),i=e.split(".").flatMap(e=>e.split(/[_-]+/)).map(e=>e.trim().toLowerCase()).filter(e=>e.length>0),r=i[i.length-1]??e;for(let e=i.length-1;e>=0;e-=1){let n=i[e];if(!t.has(n)){r=n;break}}return r.split(/[^a-z0-9]+/i).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}(e)}))}async function es(e){let t=await s("adb",en(e,["shell","cmd","package","query-activities","--brief","-a","android.intent.action.MAIN","-c","android.intent.category.LAUNCHER"]),{allowFailure:!0});if(0!==t.exitCode||0===t.stdout.trim().length)return new Set;let i=new Set;for(let e of t.stdout.split("\n")){let t=e.trim();if(!t)continue;let r=t.split(/\s+/)[0],n=r.includes("/")?r.split("/")[0]:r;n&&i.add(n)}return i}async function el(e){return(await s("adb",en(e,["shell","pm","list","packages","-3"]))).stdout.split("\n").map(e=>e.replace("package:","").trim()).filter(Boolean)}async function ed(e){let t=await ec(e,[["shell","dumpsys","window","windows"],["shell","dumpsys","window"]]);if(t)return t;let i=await ec(e,[["shell","dumpsys","activity","activities"],["shell","dumpsys","activity"]]);return i||{}}async function ec(e,t){for(let i of t){let t=function(e){for(let t of[/mCurrentFocus=Window\{[^}]*\s([\w.]+)\/([\w.$]+)/,/mFocusedApp=AppWindowToken\{[^}]*\s([\w.]+)\/([\w.$]+)/,/mResumedActivity:.*?\s([\w.]+)\/([\w.$]+)/,/ResumedActivity:.*?\s([\w.]+)\/([\w.$]+)/]){let i=t.exec(e);if(i)return{package:i[1],activity:i[2]}}return null}((await s("adb",en(e,i),{allowFailure:!0})).stdout??"");if(t)return t}return null}async function eu(e,t,i){e.booted||await z(e.id);let r=t.trim();if(Y(r)){if(i)throw new c("INVALID_ARGS","Activity override is not supported when opening a deep link URL");await s("adb",en(e,["shell","am","start","-W","-a","android.intent.action.VIEW","-d",r]));return}let n=await ea(e,t);if("intent"===n.type){if(i)throw new c("INVALID_ARGS","Activity override requires a package name, not an intent");await s("adb",en(e,["shell","am","start","-W","-a",n.value]));return}if(i){let t=i.includes("/")?i:`${n.value}/${i.startsWith(".")?i:`.${i}`}`;await s("adb",en(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-n",t]));return}try{await s("adb",en(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-p",n.value]));return}catch(i){let t=await ep(e,n.value);if(!t)throw i;await s("adb",en(e,["shell","am","start","-W","-a","android.intent.action.MAIN","-c","android.intent.category.DEFAULT","-c","android.intent.category.LAUNCHER","-n",t]))}}async function ep(e,t){let i=await s("adb",en(e,["shell","cmd","package","resolve-activity","--brief",t]),{allowFailure:!0});return 0!==i.exitCode?null:function(e){let t=e.split("\n").map(e=>e.trim()).filter(Boolean);for(let e=t.length-1;e>=0;e-=1){let i=t[e];if(i.includes("/"))return i.split(/\s+/)[0]}return null}(i.stdout)}async function ef(e){e.booted||await z(e.id)}async function em(e,t){if("settings"===t.trim().toLowerCase())return void await s("adb",en(e,["shell","am","force-stop","com.android.settings"]));let i=await ea(e,t);if("intent"===i.type)throw new c("INVALID_ARGS","Close requires a package name, not an intent");await s("adb",en(e,["shell","am","force-stop",i.value]))}async function eh(e,t){let i=await ea(e,t);if("intent"===i.type)throw new c("INVALID_ARGS","reinstall requires a package name, not an intent");let r=await s("adb",en(e,["uninstall",i.value]),{allowFailure:!0});if(0!==r.exitCode){let e=`${r.stdout}
|
|
3
|
+
${r.stderr}`.toLowerCase();if(!e.includes("unknown package")&&!e.includes("not installed"))throw new c("COMMAND_FAILED",`adb uninstall failed for ${i.value}`,{stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode})}return{package:i.value}}async function ew(e,t){await s("adb",en(e,["install",t]))}async function eg(e,t,i){e.booted||await z(e.id);let{package:r}=await eh(e,t);return await ew(e,i),{package:r}}async function ev(e,t,i){await s("adb",en(e,["shell","input","tap",String(t),String(i)]))}async function eI(e,t,i,r,n,a=250){await s("adb",en(e,["shell","input","swipe",String(t),String(i),String(r),String(n),String(a)]))}async function ey(e){await s("adb",en(e,["shell","input","keyevent","4"]))}async function eA(e){await s("adb",en(e,["shell","input","keyevent","3"]))}async function eN(e){await s("adb",en(e,["shell","input","keyevent","187"]))}async function eS(e,t,i,r=800){await s("adb",en(e,["shell","input","swipe",String(t),String(i),String(t),String(i),String(r)]))}async function eb(e,t){let i=t.replace(/ /g,"%s");await s("adb",en(e,["shell","input","text",i]))}async function e_(e,t,i){await ev(e,t,i)}async function eD(e,t,i,r){await e_(e,t,i);let n=null;for(let s of[{clearPadding:12,minClear:8,maxClear:48,chunkSize:4,delayMs:0},{clearPadding:24,minClear:16,maxClear:96,chunkSize:1,delayMs:15}]){var a,o;let l=(a=r.length+s.clearPadding,o=s.minClear,Math.max(o,Math.min(s.maxClear,a)));if(await eV(e,l),await eF(e,r,s.chunkSize,s.delayMs),(n=await eU(e,t,i))===r)return}throw new c("COMMAND_FAILED","Android fill verification failed",{expected:r,actual:n??null})}async function eM(e,t,i=.6){let{width:r,height:n}=await eL(e),a=Math.floor(r*i),o=Math.floor(n*i),l=Math.floor(r/2),d=Math.floor(n/2),u=l,p=d,f=l,m=d;switch(t){case"up":p=d-Math.floor(o/2),m=d+Math.floor(o/2);break;case"down":p=d+Math.floor(o/2),m=d-Math.floor(o/2);break;case"left":u=l-Math.floor(a/2),f=l+Math.floor(a/2);break;case"right":u=l+Math.floor(a/2),f=l-Math.floor(a/2);break;default:throw new c("INVALID_ARGS",`Unknown direction: ${t}`)}await s("adb",en(e,["shell","input","swipe",String(u),String(p),String(f),String(m),"300"]))}async function eO(e,t){for(let i=0;i<8;i+=1){let i="";try{i=await eR(e)}catch(t){let e=t instanceof Error?t.message:String(t);throw new c("UNSUPPORTED_OPERATION",`uiautomator dump failed: ${e}`)}if(function(e,t){let i=t.toLowerCase(),r=/<node[^>]+>/g,n=r.exec(e);for(;n;){let t=Z(n[0]),a=(Q(t,"text")??"").toLowerCase(),o=(Q(t,"content-desc")??"").toLowerCase();if(a.includes(i)||o.includes(i)){let e=ee(Q(t,"bounds"));if(e)return{x:Math.floor(e.x+e.width/2),y:Math.floor(e.y+e.height/2)};return{x:0,y:0}}n=r.exec(e)}return null}(i,t))return;await eM(e,"down",.5)}throw new c("COMMAND_FAILED",`Could not find element containing "${t}" after scrolling`)}async function ex(e,t){let i=await s("adb",en(e,["exec-out","screencap","-p"]),{binaryStdout:!0});if(!i.stdoutBuffer)throw new c("COMMAND_FAILED","Failed to capture screenshot");await A.writeFile(t,i.stdoutBuffer)}async function ek(e,t,i){let r=t.toLowerCase(),n=function(e){let t=e.toLowerCase();if("on"===t||"true"===t||"1"===t)return!0;if("off"===t||"false"===t||"0"===t)return!1;throw new c("INVALID_ARGS",`Invalid setting state: ${e}`)}(i);switch(r){case"wifi":return void await s("adb",en(e,["shell","svc","wifi",n?"enable":"disable"]));case"airplane":await s("adb",en(e,["shell","settings","put","global","airplane_mode_on",n?"1":"0"])),await s("adb",en(e,["shell","am","broadcast","-a","android.intent.action.AIRPLANE_MODE","--ez","state",n?"true":"false"]));return;case"location":return void await s("adb",en(e,["shell","settings","put","secure","location_mode",n?"3":"0"]));default:throw new c("INVALID_ARGS",`Unsupported setting: ${t}`)}}async function eE(e,t={}){return function(e,t,i){let r=function(e){let t={type:null,label:null,value:null,identifier:null,depth:-1,children:[]},i=[t],r=/<node\b[^>]*>|<\/node>/g,n=r.exec(e);for(;n;){let t=n[0];if(t.startsWith("</node")){i.length>1&&i.pop(),n=r.exec(e);continue}let a=K(t),o=ee(a.bounds),s=i[i.length-1],l={type:a.className,label:a.text||a.desc,value:a.text,identifier:a.resourceId,rect:o,enabled:a.enabled,hittable:a.clickable??a.focusable,depth:s.depth+1,parentIndex:void 0,children:[]};s.children.push(l),t.endsWith("/>")||i.push(l),n=r.exec(e)}return t}(e),n=[],a=!1,o=i.depth??1/0,s=i.scope?function(e,t){let i=t.toLowerCase(),r=[...e.children];for(;r.length>0;){let e=r.shift(),t=e.label?.toLowerCase()??"",n=e.value?.toLowerCase()??"",a=e.identifier?.toLowerCase()??"";if(t.includes(i)||n.includes(i)||a.includes(i))return e;r.push(...e.children)}return null}(r,i.scope):null,l=s?[s]:r.children,d=new Map,c=e=>{let t=d.get(e);if(void 0!==t)return t;for(let t of e.children)if(t.hittable||c(t))return d.set(e,!0),!0;return d.set(e,!1),!1},u=(e,t,r,s=!1,l=!1)=>{var d,p,f,m,h,w;let g,v,I,y,A,N,S,b;if(n.length>=800){a=!0;return}if(t>o)return;let _=!!i.raw||(d=e,p=i,f=s,m=c(e),h=l,v=et(d.type),I=!!(d.label&&d.label.trim().length>0),y=!!(d.identifier&&d.identifier.trim().length>0),A=I&&!ei(d.label??""),N=y&&!ei(d.identifier??""),S=(g=(w=v).split(".").pop()??w).includes("layout")||"viewgroup"===g||"view"===g,b="imageview"===v||"imagebutton"===v,p.interactiveOnly?!!d.hittable||!!(A||N)&&!b&&(!S||!!h)&&(f||m||h):p.compact?A||N||!!d.hittable:!S&&!b||!!d.hittable||!!A||!!N&&!!m||m),D=r;_&&(D=n.length,n.push({index:D,type:e.type??void 0,label:e.label??void 0,value:e.value??void 0,identifier:e.identifier??void 0,rect:e.rect,enabled:e.enabled,hittable:e.hittable,depth:t,parentIndex:r}));let M=s||!!e.hittable,O=l||function(e){if(!e)return!1;let t=et(e);return t.includes("recyclerview")||t.includes("listview")||t.includes("gridview")}(e.type);for(let i of e.children)if(u(i,t+1,D,M,O),a)return};for(let e of l)if(u(e,0,void 0,!1,!1),a)break;return a?{nodes:n,truncated:a}:{nodes:n}}(await eR(e),0,t)}async function eC(){if(!await u("adb"))throw new c("TOOL_MISSING","adb not found in PATH")}async function eL(e){let t=(await s("adb",en(e,["shell","wm","size"]))).stdout.match(/Physical size:\s*(\d+)x(\d+)/);if(!t)throw new c("COMMAND_FAILED","Unable to read screen size");return{width:Number(t[1]),height:Number(t[2])}}async function eR(e){return V(()=>eT(e),{shouldRetry:e$})}async function eT(e){var t,i,r;let n,a,o=await s("adb",en(e,["exec-out","uiautomator","dump","/dev/tty"]),{allowFailure:!0});if(0===o.exitCode){let e=eP(o.stdout,o.stderr);if(e)return e}let l="/sdcard/window_dump.xml",d=await s("adb",en(e,["shell","uiautomator","dump",l])),u=(t=l,i=d.stdout,r=d.stderr,n=`${i}
|
|
4
|
+
${r}`,a=/dumped to:\s*(\S+)/i.exec(n),a?.[1]??t),p=await s("adb",en(e,["shell","cat",u])),f=eP(p.stdout,p.stderr);if(!f)throw new c("COMMAND_FAILED","uiautomator dump did not return XML",{stdout:p.stdout,stderr:p.stderr});return f}function eP(e,t){let i=`${e}
|
|
5
|
+
${t}`,r=i.indexOf("<?xml"),n=r>=0?r:i.indexOf("<hierarchy");if(n<0)return null;let a=i.lastIndexOf("</hierarchy>");if(a<0||a<n)return null;let o=i.slice(n,a+12).trim();return o.length>0?o:null}function e$(e){if(!(e instanceof c)||"COMMAND_FAILED"!==e.code)return!1;let t=`${e.details?.stderr??""}`.toLowerCase();return!!(t.includes("device offline")||t.includes("device not found")||t.includes("transport error")||t.includes("connection reset")||t.includes("broken pipe")||t.includes("timed out")||t.includes("no such file or directory"))}async function eF(e,t,i,r){let n=Math.max(1,Math.floor(i));for(let i=0;i<t.length;i+=n){let a=t.slice(i,i+n);await eb(e,a),r>0&&i+n<t.length&&await eq(r)}}async function eV(e,t){let i=Math.max(0,t);await s("adb",en(e,["shell","input","keyevent","KEYCODE_MOVE_END"]),{allowFailure:!0});for(let t=0;t<i;t+=24){let r=Math.min(24,i-t);await s("adb",en(e,["shell","input","keyevent",...Array(r).fill("KEYCODE_DEL")]),{allowFailure:!0})}}async function eU(e,t,i){let r,n=await eR(e),a=/<node\b[^>]*>/g,o=null,s=null,l=null;for(;null!==(r=a.exec(n));){let e=K(r[0]),n=ee(e.bounds);if(!n)continue;let a=e.className??"",d=(e.text??"").replace(/"/g,'"').replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&"),c=e.focused??!1;if(!d)continue;let u=Math.max(1,n.width*n.height),p=t>=n.x&&t<=n.x+n.width&&i>=n.y&&i<=n.y+n.height;if(c&&eG(a)){(!o||u<=o.area)&&(o={text:d,area:u});continue}if(p&&eG(a)){(!s||u<=s.area)&&(s={text:d,area:u});continue}p&&(!l||u<=l.area)&&(l={text:d,area:u})}return o?.text??s?.text??l?.text??null}function eG(e){let t=e.toLowerCase();return t.includes("edittext")||t.includes("textfield")}async function eq(e){await new Promise(t=>setTimeout(t,e))}function eB(e,t,i){if(!e)return t;let r=Number(e);return Number.isFinite(r)?Math.max(i,Math.floor(r)):t}let ej=eB(process.env.AGENT_DEVICE_IOS_DEVICECTL_LIST_TIMEOUT_MS,8e3,500);async function eW(){if("darwin"!==process.platform)throw new c("UNSUPPORTED_PLATFORM","iOS tools are only available on macOS");if(!await u("xcrun"))throw new c("TOOL_MISSING","xcrun not found in PATH");let e=[],t=await s("xcrun",["simctl","list","devices","-j"]);try{let i=JSON.parse(t.stdout);for(let t of Object.values(i.devices))for(let i of t)i.isAvailable&&e.push({platform:"ios",id:i.udid,name:i.name,kind:"simulator",booted:"Booted"===i.state})}catch(e){throw new c("COMMAND_FAILED","Failed to parse simctl devices JSON",void 0,e)}let r=null;try{r=i.join(O.tmpdir(),`agent-device-devicectl-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`);let t=await s("xcrun",["devicectl","list","devices","--json-output",r],{allowFailure:!0,timeoutMs:ej});if(0!==t.exitCode)return e;let n=await A.readFile(r,"utf8"),a=JSON.parse(n);for(let t of a.result?.devices??[])if((t.hardwareProperties?.platform??"").toLowerCase().includes("ios")){let i=t.hardwareProperties?.udid??t.identifier??"",r=t.name??t.deviceProperties?.name??i;if(!i)continue;e.push({platform:"ios",id:i,name:r,kind:"device",booted:!0})}}catch{}finally{r&&await A.rm(r,{force:!0}).catch(()=>{})}return e}let eJ=eB(process.env.AGENT_DEVICE_IOS_BOOT_TIMEOUT_MS,T,5e3),eH=eB(process.env.AGENT_DEVICE_IOS_SIMCTL_LIST_TIMEOUT_MS,R,1e3),ez=eB(process.env.AGENT_DEVICE_IOS_APP_LAUNCH_TIMEOUT_MS,3e4,5e3),eY=eB(process.env.AGENT_DEVICE_IOS_DEVICECTL_TIMEOUT_MS,2e4,1e3);async function eX(e,t){let i=["devicectl",...e],r=await s("xcrun",i,{allowFailure:!0,timeoutMs:eY});if(0===r.exitCode)return;let n=String(r.stdout??""),a=String(r.stderr??"");throw new c("COMMAND_FAILED",`Failed to ${t.action}`,{cmd:"xcrun",args:i,exitCode:r.exitCode,stdout:n,stderr:a,deviceId:t.deviceId,hint:eQ(n,a)??eZ})}async function eK(e,t){let r=i.join(O.tmpdir(),`agent-device-ios-apps-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`),n=["devicectl","device","info","apps","--device",e.id,"--include-all-apps","--json-output",r],a=await s("xcrun",n,{allowFailure:!0,timeoutMs:eY});try{var o,l;if(0!==a.exitCode){let t=String(a.stdout??""),i=String(a.stderr??"");throw new c("COMMAND_FAILED","Failed to list iOS apps",{cmd:"xcrun",args:n,exitCode:a.exitCode,stdout:t,stderr:i,deviceId:e.id,hint:eQ(t,i)??eZ})}let i=await A.readFile(r,"utf8");return o=function(e){let t=e?.result?.apps;if(!Array.isArray(t))return[];let i=[];for(let e of t){if(!e||"object"!=typeof e)continue;let t="string"==typeof e.bundleIdentifier?e.bundleIdentifier.trim():"";if(!t)continue;let r="string"==typeof e.name&&e.name.trim().length>0?e.name.trim():t;i.push({bundleId:t,name:r})}return i}(JSON.parse(i)),l=t,"user-installed"===l?o.filter(e=>!e.bundleId.startsWith("com.apple.")):o}catch(t){if(t instanceof c)throw t;throw new c("COMMAND_FAILED","Failed to parse iOS apps list",{deviceId:e.id,cause:String(t)})}finally{await A.unlink(r).catch(()=>{})}}let eZ="Ensure the iOS device is unlocked, trusted, and available in Xcode > Devices, then retry.";function eQ(e,t){let i=`${e}
|
|
6
|
+
${t}`.toLowerCase();return i.includes("device is busy")&&i.includes("connecting")?"iOS device is still connecting. Keep it unlocked and connected by cable until it is fully available in Xcode Devices, then retry.":i.includes("coredeviceservice")&&i.includes("timed out")?"CoreDevice service timed out. Reconnect the device and retry; if it persists restart Xcode and the iOS device.":null}async function e0(e){let t,i;if("simulator"!==e.kind||"Booted"===await e1(e.id))return;let r=$.fromTimeoutMs(eJ);try{await F(async({deadline:r})=>{if(r?.isExpired())throw new c("COMMAND_FAILED","iOS simulator boot deadline exceeded",{timeoutMs:eJ});let n=Math.max(1e3,r?.remainingMs()??eJ),a=await s("xcrun",["simctl","boot",e.id],{allowFailure:!0,timeoutMs:n});t={stdout:String(a.stdout??""),stderr:String(a.stderr??""),exitCode:a.exitCode};let o=`${t.stdout}
|
|
7
|
+
${t.stderr}`.toLowerCase(),l=o.includes("already booted")||o.includes("current state: booted");if(0!==t.exitCode&&!l)throw new c("COMMAND_FAILED","simctl boot failed",{stdout:t.stdout,stderr:t.stderr,exitCode:t.exitCode});let d=await s("xcrun",["simctl","bootstatus",e.id,"-b"],{allowFailure:!0,timeoutMs:n});if(i={stdout:String(d.stdout??""),stderr:String(d.stderr??""),exitCode:d.exitCode},0!==i.exitCode)throw new c("COMMAND_FAILED","simctl bootstatus failed",{stdout:i.stdout,stderr:i.stderr,exitCode:i.exitCode});let u=await e1(e.id);if("Booted"!==u)throw new c("COMMAND_FAILED","Simulator is still booting",{state:u})},{maxAttempts:3,baseDelayMs:500,maxDelayMs:2e3,jitter:.2,shouldRetry:e=>{let r=G({error:e,stdout:i?.stdout??t?.stdout,stderr:i?.stderr??t?.stderr,context:{platform:"ios",phase:"boot"}});return"IOS_BOOT_TIMEOUT"!==r&&"CI_RESOURCE_STARVATION_SUSPECTED"!==r}},{deadline:r,phase:"boot",classifyReason:e=>G({error:e,stdout:i?.stdout??t?.stdout,stderr:i?.stderr??t?.stderr,context:{platform:"ios",phase:"boot"}})})}catch(a){let n=G({error:a,stdout:i?.stdout??t?.stdout,stderr:i?.stderr??t?.stderr,context:{platform:"ios",phase:"boot"}});throw new c("COMMAND_FAILED","iOS simulator failed to boot",{platform:"ios",deviceId:e.id,timeoutMs:eJ,elapsedMs:r.elapsedMs(),reason:n,hint:q(n),boot:t,bootstatus:i})}}async function e1(e){let t=await s("xcrun",["simctl","list","devices","-j"],{allowFailure:!0,timeoutMs:eH});if(0!==t.exitCode)return null;try{let i=JSON.parse(String(t.stdout??""));for(let t of Object.values(i.devices??{})){let i=t.find(t=>t.udid===e);if(i)return i.state}return null}catch{return null}}let e2={settings:"com.apple.Preferences"};function e3(e){return e.includes("not installed")||e.includes("not found")||e.includes("no such file")}async function e4(e,t){let i=t.trim();if(i.includes("."))return i;let r=e2[i.toLowerCase()];if(r)return r;let n=("simulator"===e.kind?await tn(e):await eK(e,"all")).filter(e=>e.name.toLowerCase()===i.toLowerCase());if(1===n.length)return n[0].bundleId;if(n.length>1)throw new c("INVALID_ARGS",`Multiple apps matched "${t}"`,{matches:n});throw new c("APP_NOT_INSTALLED",`No app found matching "${t}"`)}async function e5(e,t,i){let r=i?.url?.trim();if(r){if(!Y(r))throw new c("INVALID_ARGS","open <app> <url> requires a valid URL target");if("simulator"===e.kind){await e0(e),await s("open",["-a","Simulator"],{allowFailure:!0}),await s("xcrun",["simctl","openurl",e.id,r]);return}let n=X(i?.appBundleId??await e4(e,t),r);if(!n)throw new c("INVALID_ARGS","Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.");await td(e,n,{payloadUrl:r});return}let n=t.trim();if(Y(n)){if("simulator"===e.kind){await e0(e),await s("open",["-a","Simulator"],{allowFailure:!0}),await s("xcrun",["simctl","openurl",e.id,n]);return}let t=X(i?.appBundleId,n);if(!t)throw new c("INVALID_ARGS","Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.");await td(e,t,{payloadUrl:n});return}let a=i?.appBundleId??await e4(e,t);"simulator"===e.kind?await tl(e,a):await td(e,a)}async function e8(e){"simulator"!==e.kind||"Booted"!==await e1(e.id)&&(await e0(e),await s("open",["-a","Simulator"],{allowFailure:!0}))}async function e6(e,t){let i=await e4(e,t);if("simulator"===e.kind){await e0(e);let t=await s("xcrun",["simctl","terminate",e.id,i],{allowFailure:!0});if(0!==t.exitCode){if(t.stderr.toLowerCase().includes("found nothing to terminate"))return;throw new c("COMMAND_FAILED",`xcrun exited with code ${t.exitCode}`,{cmd:"xcrun",args:["simctl","terminate",e.id,i],stdout:t.stdout,stderr:t.stderr,exitCode:t.exitCode})}return}await eX(["device","process","terminate","--device",e.id,i],{action:"terminate iOS app",deviceId:e.id})}async function e9(e,t){let i=await e4(e,t);if("simulator"!==e.kind){let t=["devicectl","device","uninstall","app","--device",e.id,i],r=await s("xcrun",t,{allowFailure:!0,timeoutMs:eY});if(0!==r.exitCode){let n=String(r.stdout??""),a=String(r.stderr??"");if(!e3(`${n}
|
|
8
|
+
${a}`.toLowerCase()))throw new c("COMMAND_FAILED",`Failed to uninstall iOS app ${i}`,{cmd:"xcrun",args:t,exitCode:r.exitCode,stdout:n,stderr:a,deviceId:e.id,hint:eQ(n,a)??eZ})}return{bundleId:i}}await e0(e);let r=await s("xcrun",["simctl","uninstall",e.id,i],{allowFailure:!0});if(0!==r.exitCode&&!e3(`${r.stdout}
|
|
9
|
+
${r.stderr}`.toLowerCase()))throw new c("COMMAND_FAILED",`simctl uninstall failed for ${i}`,{stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode});return{bundleId:i}}async function e7(e,t){"simulator"!==e.kind?await eX(["device","install","app","--device",e.id,t],{action:"install iOS app",deviceId:e.id}):(await e0(e),await s("xcrun",["simctl","install",e.id,t]))}async function te(e,t,i){let{bundleId:r}=await e9(e,t);return await e7(e,i),{bundleId:r}}async function tt(e,t){if("simulator"===e.kind){await e0(e),await s("xcrun",["simctl","io",e.id,"screenshot",t]);return}await eX(["device","screenshot","--device",e.id,t],{action:"capture iOS screenshot",deviceId:e.id})}async function ti(e,t,i,r){if("simulator"!==e.kind)throw new c("UNSUPPORTED_OPERATION","settings is only supported on iOS simulators");switch(await e0(e),t.toLowerCase()){case"wifi":{let t=ta(i);await s("xcrun",["simctl","status_bar",e.id,"override","--wifiMode",t?"active":"failed"]);return}case"airplane":return void(ta(i)?await s("xcrun",["simctl","status_bar",e.id,"override","--dataNetwork","hide","--wifiMode","failed","--wifiBars","0","--cellularMode","failed","--cellularBars","0","--operatorName",""]):await s("xcrun",["simctl","status_bar",e.id,"clear"]));case"location":{let t=ta(i);if(!r)throw new c("INVALID_ARGS","location setting requires an active app in session");await s("xcrun",["simctl","privacy",e.id,t?"grant":"revoke","location",r]);return}case"faceid":{let t=function(e){let t=e.trim().toLowerCase();if("match"===t)return"match";if("nonmatch"===t)return"nonmatch";if("enroll"===t)return"enroll";if("unenroll"===t)return"unenroll";throw new c("INVALID_ARGS",`Invalid faceid state: ${e}. Use match|nonmatch|enroll|unenroll.`)}(i);await to(e.id,t);return}default:throw new c("INVALID_ARGS",`Unsupported setting: ${t}`)}}async function tr(e,t="all"){var i;return"simulator"===e.kind?(i=await tn(e),"user-installed"===t?i.filter(e=>!e.bundleId.startsWith("com.apple.")):i):await eK(e,t)}async function tn(e){let t=(await s("xcrun",["simctl","listapps",e.id],{allowFailure:!0})).stdout.trim();if(!t)return[];let i=null;if(t.startsWith("{"))try{i=JSON.parse(t)}catch{i=null}if(!i&&t.startsWith("{"))try{let e=await s("plutil",["-convert","json","-o","-","-"],{allowFailure:!0,stdin:t});0===e.exitCode&&e.stdout.trim().startsWith("{")&&(i=JSON.parse(e.stdout))}catch{i=null}return i?Object.entries(i).map(([e,t])=>({bundleId:e,name:t.CFBundleDisplayName??t.CFBundleName??e})):[]}function ta(e){let t=e.toLowerCase();if("on"===t||"true"===t||"1"===t)return!0;if("off"===t||"false"===t||"0"===t)return!1;throw new c("INVALID_ARGS",`Invalid setting state: ${e}`)}async function to(e,t){let i=function(e,t){switch(t){case"match":return[["simctl","biometric",e,"match","face"],["simctl","biometric","match",e,"face"]];case"nonmatch":return[["simctl","biometric",e,"nonmatch","face"],["simctl","biometric",e,"nomatch","face"],["simctl","biometric","nonmatch",e,"face"],["simctl","biometric","nomatch",e,"face"]];case"enroll":return[["simctl","biometric",e,"enroll","yes"],["simctl","biometric",e,"enroll","1"],["simctl","biometric","enroll",e,"yes"],["simctl","biometric","enroll",e,"1"]];case"unenroll":return[["simctl","biometric",e,"enroll","no"],["simctl","biometric",e,"enroll","0"],["simctl","biometric","enroll",e,"no"],["simctl","biometric","enroll",e,"0"]]}}(e,t),r=[];for(let e of i){let t=await s("xcrun",e,{allowFailure:!0});if(0===t.exitCode)return;r.push({args:e,stderr:t.stderr,stdout:t.stdout,exitCode:t.exitCode})}throw new c("COMMAND_FAILED","simctl biometric command failed. Ensure your Xcode Simulator runtime supports Face ID control.",{deviceId:e,action:t,attempts:r.map(e=>({args:e.args.join(" "),exitCode:e.exitCode,stderr:e.stderr.slice(0,400)}))})}function ts(e){if(!(e instanceof c)||"COMMAND_FAILED"!==e.code)return!1;let t=e.details??{};if(4!==t.exitCode)return!1;let i=String(t.stderr??"").toLowerCase();return i.includes("fbsopenapplicationserviceerrordomain")&&i.includes("the request to open")}async function tl(e,t){await e0(e),await s("open",["-a","Simulator"],{allowFailure:!0});let i=$.fromTimeoutMs(ez);await F(async({deadline:i})=>{if(i?.isExpired())throw new c("COMMAND_FAILED","App launch deadline exceeded",{timeoutMs:ez});let r=await s("xcrun",["simctl","launch",e.id,t],{allowFailure:!0});if(0!==r.exitCode)throw new c("COMMAND_FAILED",`xcrun exited with code ${r.exitCode}`,{cmd:"xcrun",args:["simctl","launch",e.id,t],stdout:r.stdout,stderr:r.stderr,exitCode:r.exitCode})},{maxAttempts:30,baseDelayMs:1e3,maxDelayMs:5e3,jitter:.2,shouldRetry:ts},{deadline:i})}async function td(e,t,i){let r=["device","process","launch","--device",e.id,t];i?.payloadUrl&&r.push("--payload-url",i.payloadUrl),await eX(r,{action:"launch iOS app",deviceId:e.id})}async function tc(e,t,i){let r=(e.get(t)??Promise.resolve()).catch(()=>{}).then(i);return e.set(t,r),r.finally(()=>{e.get(t)===r&&e.delete(t)})}let tu=new Set;function tp(e){return!!e&&tu.has(e)}let tf=new Map,tm=new Map,th=new Set,tw=eB(process.env.AGENT_DEVICE_RUNNER_STARTUP_TIMEOUT_MS,45e3,5e3),tg=eB(process.env.AGENT_DEVICE_RUNNER_COMMAND_TIMEOUT_MS,45e3,1e3),tv=eB(process.env.AGENT_DEVICE_RUNNER_CONNECT_ATTEMPT_INTERVAL_MS,250,50),tI=eB(process.env.AGENT_DEVICE_RUNNER_CONNECT_RETRY_BASE_DELAY_MS,300,10),ty=eB(process.env.AGENT_DEVICE_RUNNER_CONNECT_RETRY_MAX_DELAY_MS,2e3,10),tA=eB(process.env.AGENT_DEVICE_RUNNER_CONNECT_REQUEST_TIMEOUT_MS,2e4,250),tN=eB(process.env.AGENT_DEVICE_IOS_DEVICE_INFO_TIMEOUT_MS,1e4,500),tS=eB(process.env.AGENT_DEVICE_RUNNER_DESTINATION_TIMEOUT_SECONDS,20,5),tb=i.join(O.homedir(),".agent-device","ios-runner");async function t_(e,t,i={}){var r;return(function(e){if("ios"!==e.platform)throw new c("UNSUPPORTED_PLATFORM",`Unsupported platform for iOS runner: ${e.platform}`);if("simulator"!==e.kind&&"device"!==e.kind)throw new c("UNSUPPORTED_OPERATION",`Unsupported iOS device kind for runner: ${e.kind}`)}(e),tq(i.requestId),"snapshot"===(r=t.command)||"findText"===r||"alert"===r)?V(()=>(tq(i.requestId),tD(e,t,i)),{shouldRetry:e=>(tq(i.requestId),function(e){if(!(e instanceof c)||"COMMAND_FAILED"!==e.code)return!1;let t=`${e.message??""}`.toLowerCase();return!(t.includes("xcodebuild exited early")||t.includes("device is busy")&&t.includes("connecting"))&&!!(t.includes("runner did not accept connection")||t.includes("fetch failed")||t.includes("econnrefused")||t.includes("socket hang up"))}(e))}):tD(e,t,i)}async function tD(e,t,i={}){let r;tq(i.requestId);try{let n=(r=await tT(e,i)).ready?tg:tw;return await tM(e,r,t,i.logPath,n)}catch(a){let n=a instanceof c?a:new c("COMMAND_FAILED",String(a));if("COMMAND_FAILED"===n.code&&"string"==typeof n.message&&n.message.includes("Runner did not accept connection")&&tj(n)&&r?.ready){tq(i.requestId),r?await tC(r):await tx(e.id),r=await tT(e,i);let n=await tJ(r.device,r.port,t,i.logPath,tw);return await tO(n,r,i.logPath)}throw a}}async function tM(e,t,i,r,n){let a=await tJ(e,t.port,i,r,n,t);return await tO(a,t,r)}async function tO(e,t,i){let r=await e.text(),n={};try{n=JSON.parse(r)}catch{throw new c("COMMAND_FAILED","Invalid runner response",{text:r})}if(!n.ok)throw new c("COMMAND_FAILED",n.error?.message??"Runner error",{runner:n,xcodebuild:{exitCode:1,stdout:"",stderr:""},logPath:i});return t.ready=!0,n.data??{}}async function tx(e){await tc(tm,e,async()=>{await tL(e)})}async function tk(){let e=Array.from(tf.values()),t=Array.from(th);await Promise.allSettled(e.map(async e=>{await tP(e.child.pid,"SIGINT")})),await Promise.allSettled(t.map(async e=>{await tP(e.pid,"SIGINT")})),await Promise.allSettled(e.map(async e=>{await tP(e.child.pid,"SIGTERM")})),await Promise.allSettled(t.map(async e=>{await tP(e.pid,"SIGTERM")})),await Promise.allSettled(e.map(async e=>{await tP(e.child.pid,"SIGKILL")})),await Promise.allSettled(t.map(async e=>{await tP(e.pid,"SIGKILL"),th.delete(e)}))}async function tE(){await tk();let e=Array.from(tf.keys());await Promise.allSettled(e.map(async e=>{await tx(e)})),await tF()}async function tC(e){await tc(tm,e.deviceId,async()=>{await tL(e.deviceId,e)})}async function tL(e,t){let i=t??tf.get(e);if(i){try{await tJ(i.device,i.port,{command:"shutdown"},void 0,15e3)}catch{await tP(i.child.pid,"SIGTERM")}try{await Promise.race([i.testPromise,new Promise(e=>setTimeout(e,1e4))])}catch{}await tP(i.child.pid,"SIGKILL"),tQ(i.xctestrunPath),tQ(i.jsonPath),tf.get(e)===i&&tf.delete(e)}}async function tR(e){await s("xcrun",["simctl","bootstatus",e,"-b"],{allowFailure:!0,timeoutMs:tw})}async function tT(e,t){return await tc(tm,e.id,async()=>{var i,r;let n=tf.get(e.id);if(n){if((i=n.child.pid)&&S(i))return n;await tL(e.id,n)}await ("simulator"!==(r=e).kind?Promise.resolve():tR(r.id));let o=await t$(e,t),s=await tK(),{xctestrunPath:l,jsonPath:d}=await tZ(o,{AGENT_DEVICE_RUNNER_PORT:String(s)},`session-${e.id}-${s}`),{child:u,wait:p}=a("xcodebuild",["test-without-building","-only-testing","AgentDeviceRunnerUITests/RunnerTests/testCommand","-parallel-testing-enabled","NO","-test-timeouts-enabled","NO","-collect-test-diagnostics","never",tV(e),"1","-destination-timeout",String(tS),"-xctestrun",l,"-destination",function(e){if("ios"!==e.platform)throw new c("UNSUPPORTED_PLATFORM",`Unsupported platform for iOS runner: ${e.platform}`);return"simulator"===e.kind?`platform=iOS Simulator,id=${e.id}`:`platform=iOS,id=${e.id}`}(e)],{allowFailure:!0,env:{...process.env,AGENT_DEVICE_RUNNER_PORT:String(s)},detached:!0});u.stdout?.on("data",e=>{tG(e,t.logPath,t.traceLogPath,t.verbose)}),u.stderr?.on("data",e=>{tG(e,t.logPath,t.traceLogPath,t.verbose)});let f={device:e,deviceId:e.id,port:s,xctestrunPath:l,jsonPath:d,testPromise:p,child:u,ready:!1};return tf.set(e.id,f),f})}async function tP(e,t){if(!e||e<=0)return;try{process.kill(-e,t)}catch{}try{process.kill(e,t)}catch{}let i="SIGINT"===t?"INT":"SIGTERM"===t?"TERM":"KILL";try{await s("pkill",[`-${i}`,"-P",String(e)],{allowFailure:!0})}catch{}}async function t$(e,t){var r,n;let a,o=(r=e.kind,(a=process.env.AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH?.trim())?i.resolve(a):"simulator"===r?i.join(tb,"derived"):i.join(tb,"derived",r));if(L(process.env.AGENT_DEVICE_IOS_CLEAN_DERIVED)){!function(e,t=process.env){if(t.AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH?.trim()&&!function(e=process.env){return L(e.AGENT_DEVICE_IOS_ALLOW_OVERRIDE_DERIVED_CLEAN)}(t))throw new c("COMMAND_FAILED","Refusing to clean AGENT_DEVICE_IOS_RUNNER_DERIVED_PATH automatically",{derivedPath:e,hint:"Unset AGENT_DEVICE_IOS_CLEAN_DERIVED, or set AGENT_DEVICE_IOS_ALLOW_OVERRIDE_DERIVED_CLEAN=1 if you trust this path."})}(o);try{_.rmSync(o,{recursive:!0,force:!0})}catch{}}let s=tU(o);if(s)return s;let l=function(){let e=i.dirname(f(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=i.join(t,"package.json");if(_.existsSync(e))return t;t=i.dirname(t)}return e}(),d=i.join(l,"ios-runner","AgentDeviceRunner","AgentDeviceRunner.xcodeproj");if(!_.existsSync(d))throw new c("COMMAND_FAILED","iOS runner project not found",{projectPath:d});let u=function(e=process.env,t=!1){if(!t)return[];let i=e.AGENT_DEVICE_IOS_TEAM_ID?.trim()||"",r=e.AGENT_DEVICE_IOS_SIGNING_IDENTITY?.trim()||"",n=e.AGENT_DEVICE_IOS_PROVISIONING_PROFILE?.trim()||"",a=["CODE_SIGN_STYLE=Automatic"];return i&&a.push(`DEVELOPMENT_TEAM=${i}`),r&&a.push(`CODE_SIGN_IDENTITY=${r}`),n&&a.push(`PROVISIONING_PROFILE_SPECIFIER=${n}`),a}(process.env,"device"===e.kind),p="device"===e.kind?["-allowProvisioningUpdates"]:[];try{await m("xcodebuild",["build-for-testing","-project",d,"-scheme","AgentDeviceRunner","-parallel-testing-enabled","NO",tV(e),"1","-destination",function(e){if("ios"!==e.platform)throw new c("UNSUPPORTED_PLATFORM",`Unsupported platform for iOS runner: ${e.platform}`);return"simulator"===e.kind?`platform=iOS Simulator,id=${e.id}`:"generic/platform=iOS"}(e),"-derivedDataPath",o,...p,...u],{detached:!0,onSpawn:e=>{th.add(e),e.on("close",()=>{th.delete(e)})},onStdoutChunk:e=>{tG(e,t.logPath,t.traceLogPath,t.verbose)},onStderrChunk:e=>{tG(e,t.logPath,t.traceLogPath,t.verbose)}})}catch(o){let e,i,r=o instanceof c?o:new c("COMMAND_FAILED",String(o)),a=(e=(n=r).details?JSON.stringify(n.details):"",(i=`${n.message}
|
|
10
|
+
${e}`.toLowerCase()).includes("requires a development team")?"Configure signing in Xcode or set AGENT_DEVICE_IOS_TEAM_ID for physical-device runs.":i.includes("no profiles for")||i.includes("provisioning profile")?"Install/select a valid iOS provisioning profile, or set AGENT_DEVICE_IOS_PROVISIONING_PROFILE.":i.includes("code signing")?"Enable Automatic Signing in Xcode or provide AGENT_DEVICE_IOS_TEAM_ID and optional AGENT_DEVICE_IOS_SIGNING_IDENTITY.":void 0);throw new c("COMMAND_FAILED","xcodebuild build-for-testing failed",{error:r.message,details:r.details,logPath:t.logPath,hint:a})}let h=tU(o);if(!h)throw new c("COMMAND_FAILED","Failed to locate .xctestrun after build");return h}async function tF(){let e=Array.from(th);await Promise.allSettled(e.map(async e=>{try{await tP(e.pid,"SIGTERM"),await tP(e.pid,"SIGKILL")}finally{th.delete(e)}}))}function tV(e){return"device"===e.kind?"-maximum-concurrent-test-device-destinations":"-maximum-concurrent-test-simulator-destinations"}function tU(e){if(!_.existsSync(e))return null;let t=[],r=[e];for(;r.length>0;){let e=r.pop();for(let n of _.readdirSync(e,{withFileTypes:!0})){let a=i.join(e,n.name);if(n.isDirectory()){r.push(a);continue}if(n.isFile()&&n.name.endsWith(".xctestrun"))try{let e=_.statSync(a);t.push({path:a,mtimeMs:e.mtimeMs})}catch{}}}return 0===t.length?null:(t.sort((e,t)=>t.mtimeMs-e.mtimeMs),t[0]?.path??null)}function tG(e,t,i,r){t&&_.appendFileSync(t,e),i&&_.appendFileSync(i,e),r&&process.stderr.write(e)}function tq(e){if(tp(e))throw new c("COMMAND_FAILED","request canceled")}function tB(e){let{port:t,endpoints:i,logPath:r,lastError:n}=e,a="Runner did not accept connection";return new c("COMMAND_FAILED",a,{port:t,endpoints:i,logPath:r,lastError:n?String(n):void 0,reason:G({error:n,message:a,context:{platform:"ios",phase:"connect"}}),hint:q("IOS_RUNNER_CONNECT_TIMEOUT")})}function tj(e){return!(e instanceof c)||"COMMAND_FAILED"!==e.code||!String(e.message??"").toLowerCase().includes("xcodebuild exited early")}async function tW(e){var t,i;let r,{session:n,port:a,logPath:o}=e,s=await n.testPromise,l="Runner did not accept connection (xcodebuild exited early)",d=G({message:l,stdout:s.stdout,stderr:s.stderr,context:{platform:"ios",phase:"connect"}});return new c("COMMAND_FAILED",l,{port:a,logPath:o,xcodebuild:{exitCode:s.exitCode,stdout:s.stdout,stderr:s.stderr},reason:d,hint:(t=s.stdout,i=s.stderr,(r=`${l}
|
|
11
11
|
${t}
|
|
12
|
-
${i}`.toLowerCase()).includes("device is busy")&&r.includes("connecting")?"Target iOS device is still connecting. Keep it unlocked, wait for device trust/connection to settle, then retry.":F("IOS_RUNNER_CONNECT_TIMEOUT"))})}async function t$(e,t,i,r,n=tc,a){let o=C.fromTimeoutMs(n),s=await tF(e,t,o.remainingMs()),l=null,c=Math.max(1,Math.ceil(n/tu));try{return await R(async({deadline:o})=>{if(o?.isExpired())throw new I("COMMAND_FAILED","Runner connection deadline exceeded",{port:t,timeoutMs:n});if(a&&null!==a.child.exitCode&&void 0!==a.child.exitCode)throw await tP({session:a,port:t,logPath:r});for(let r of("device"===e.kind&&(s=await tF(e,t,o?.remainingMs())),s))try{let e=o?.remainingMs()??n;if(e<=0)throw new I("COMMAND_FAILED","Runner connection deadline exceeded",{port:t,timeoutMs:n});return await tV(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)},Math.min(tm,e))}catch(e){l=e}throw new I("COMMAND_FAILED","Runner endpoint probe failed",{port:t,endpoints:s,lastError:l?String(l):void 0})},{maxAttempts:c,baseDelayMs:tp,maxDelayMs:tf,jitter:.2,shouldRetry:tT},{deadline:o,phase:"ios_runner_connect"})}catch(e){l||(l=e)}if("simulator"===e.kind){let n=o.remainingMs();if(n<=0)throw tR({port:t,endpoints:s,logPath:r,lastError:l});let a=await tG(e.id,t,i,n);return new Response(a.body,{status:a.status})}throw tR({port:t,endpoints:s,logPath:r,lastError:l})}async function tF(e,t,i){let r=[`http://127.0.0.1:${t}/command`];if("device"!==e.kind)return r;let n=await tU(e.id,i);return n&&r.unshift(`http://[${n}]:${t}/command`),r}async function tV(e,t,i){let r=new AbortController,n=setTimeout(()=>r.abort(),i);try{return await fetch(e,{...t,signal:r.signal})}finally{clearTimeout(n)}}async function tU(e,t){if("number"==typeof t&&t<=0)return null;let r="number"==typeof t?Math.max(1,Math.min(th,t)):th,n=i.join(S.tmpdir(),`agent-device-devicectl-info-${process.pid}-${Date.now()}.json`);try{let t=Math.max(1,Math.ceil(r/1e3)),i=await u("xcrun",["devicectl","device","info","details","--device",e,"--json-output",n,"--timeout",String(t)],{allowFailure:!0,timeoutMs:r});if(0!==i.exitCode||!A.existsSync(n))return null;let a=JSON.parse(A.readFileSync(n,"utf8"));if(a.info?.outcome&&"success"!==a.info.outcome)return null;let o=(a.result?.connectionProperties?.tunnelIPAddress??a.result?.device?.connectionProperties?.tunnelIPAddress)?.trim();return o&&o.length>0?o:null}catch{return null}finally{tq(n)}}async function tG(e,t,i,r){let n=JSON.stringify(i),a=await u("xcrun",["simctl","spawn",e,"/usr/bin/curl","-s","-X","POST","-H","Content-Type: application/json","--data",n,`http://127.0.0.1:${t}/command`],{allowFailure:!0,timeoutMs:r}),o=a.stdout;if(0!==a.exitCode){let e=$({message:"Runner did not accept connection (simctl spawn)",stdout:a.stdout,stderr:a.stderr,context:{platform:"ios",phase:"connect"}});throw new I("COMMAND_FAILED","Runner did not accept connection (simctl spawn)",{port:t,stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode,reason:e,hint:F(e)})}return{status:200,body:o}}async function tj(){return await new Promise((e,t)=>{let i=_.createServer();i.listen(0,"127.0.0.1",()=>{let r=i.address();i.close(),"object"==typeof r&&r?.port?e(r.port):t(new I("COMMAND_FAILED","Failed to allocate port"))}),i.on("error",t)})}async function tB(e,t,r){let n,a=i.dirname(e),o=r.replace(/[^a-zA-Z0-9._-]/g,"_"),s=i.join(a,`AgentDeviceRunner.env.${o}.json`),l=i.join(a,`AgentDeviceRunner.env.${o}.xctestrun`),c=await u("plutil",["-convert","json","-o","-",e],{allowFailure:!0});if(0!==c.exitCode||!c.stdout.trim())throw new I("COMMAND_FAILED","Failed to read xctestrun plist",{xctestrunPath:e,stderr:c.stderr});try{n=JSON.parse(c.stdout)}catch(t){throw new I("COMMAND_FAILED","Failed to parse xctestrun JSON",{xctestrunPath:e,error:String(t)})}let d=e=>{e.EnvironmentVariables={...e.EnvironmentVariables??{},...t},e.UITestEnvironmentVariables={...e.UITestEnvironmentVariables??{},...t},e.UITargetAppEnvironmentVariables={...e.UITargetAppEnvironmentVariables??{},...t},e.TestingEnvironmentVariables={...e.TestingEnvironmentVariables??{},...t}},p=n.TestConfigurations;if(Array.isArray(p))for(let e of p){if(!e||"object"!=typeof e)continue;let t=e.TestTargets;if(Array.isArray(t))for(let e of t)e&&"object"==typeof e&&d(e)}for(let[e,t]of Object.entries(n))t&&"object"==typeof t&&t.TestBundlePath&&(d(t),n[e]=t);A.writeFileSync(s,JSON.stringify(n,null,2));let f=await u("plutil",["-convert","xml1","-o",l,s],{allowFailure:!0});if(0!==f.exitCode)throw new I("COMMAND_FAILED","Failed to write xctestrun plist",{tmpXctestrunPath:l,stderr:f.stderr});return{xctestrunPath:l,jsonPath:s}}function tq(e){try{A.existsSync(e)&&A.unlinkSync(e)}catch{}}async function tW(e){return await p("resolve_target_device",async()=>{let t={platform:e.platform,deviceName:e.device,udid:e.udid,serial:e.serial};if("android"===t.platform){await eM();let e=await j();return await O(e,t)}if("ios"===t.platform){let e=await eG();return await O(e,t)}let i=[];try{i.push(...await j())}catch{}try{i.push(...await eG())}catch{}return await O(i,t)},{platform:e.platform})}async function tJ(e,t,r,n,a){let o=function(e,t){switch(e.platform){case"android":return{open:(t,i)=>es(e,t,i?.activity),openDevice:()=>ec(e),close:t=>ed(e,t),tap:(t,i)=>em(e,t,i),doubleTap:async(t,i)=>{await em(e,t,i),await em(e,t,i)},swipe:(t,i,r,n,a)=>eh(e,t,i,r,n,a),longPress:(t,i,r)=>eI(e,t,i,r),focus:(t,i)=>ey(e,t,i),type:t=>eA(e,t),fill:(t,i,r)=>eN(e,t,i,r),scroll:(t,i)=>eS(e,t,i),scrollIntoView:t=>eb(e,t),screenshot:t=>e_(e,t)};case"ios":var i,r;let n;return{open:(t,i)=>e1(e,t,{appBundleId:i?.appBundleId,url:i?.url}),openDevice:()=>e2(e),close:t=>e3(e,t),screenshot:t=>e6(e,t),...(i=e,n={verbose:(r=t).verbose,logPath:r.logPath,traceLogPath:r.traceLogPath},{tap:async(e,t)=>{await tv(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n)},doubleTap:async(e,t)=>{await tv(i,{command:"tapSeries",x:e,y:t,count:1,intervalMs:0,doubleTap:!0,appBundleId:r.appBundleId},n)},swipe:async(e,t,a,o,s)=>{await tv(i,{command:"drag",x:e,y:t,x2:a,y2:o,durationMs:s,appBundleId:r.appBundleId},n)},longPress:async(e,t,a)=>{await tv(i,{command:"longPress",x:e,y:t,durationMs:a,appBundleId:r.appBundleId},n)},focus:async(e,t)=>{await tv(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n)},type:async e=>{await tv(i,{command:"type",text:e,appBundleId:r.appBundleId},n)},fill:async(e,t,a)=>{await tv(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n),await tv(i,{command:"type",text:a,clearFirst:!0,appBundleId:r.appBundleId},n)},scroll:async(e,t)=>{if(!["up","down","left","right"].includes(e))throw new I("INVALID_ARGS",`Unknown direction: ${e}`);let a=function(e){switch(e){case"up":return"down";case"down":return"up";case"left":return"right";case"right":return"left"}}(e);await tv(i,{command:"swipe",direction:a,appBundleId:r.appBundleId},n)},scrollIntoView:async e=>{for(let t=0;t<8;t+=1){let a=await tv(i,{command:"findText",text:e,appBundleId:r.appBundleId},n);if(a?.found)return{attempts:t+1};await tv(i,{command:"swipe",direction:"up",appBundleId:r.appBundleId},n),await new Promise(e=>setTimeout(e,300))}throw new I("COMMAND_FAILED",`scrollintoview could not find text: ${e}`)}})};default:throw new I("UNSUPPORTED_PLATFORM",`Unsupported platform: ${e.platform}`)}}(e,{appBundleId:a?.appBundleId,verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath});return m({level:"debug",phase:"platform_command_prepare",data:{command:t,platform:e.platform,kind:e.kind}}),await p("platform_command",async()=>{var s,l,c,d,u,f;switch(t){case"open":{let t=r[0],i=r[1];if(r.length>2)throw new I("INVALID_ARGS","open accepts at most two arguments: <app|url> [url]");if(!t)return await o.openDevice(),{app:null};if(void 0!==i){if("ios"!==e.platform)throw new I("INVALID_ARGS","open <app> <url> is supported only on iOS");if(W(t))throw new I("INVALID_ARGS","open <app> <url> requires an app target as the first argument");if(!W(i))throw new I("INVALID_ARGS","open <app> <url> requires a valid URL target");return await o.open(t,{activity:a?.activity,appBundleId:a?.appBundleId,url:i}),{app:t,url:i}}return await o.open(t,{activity:a?.activity,appBundleId:a?.appBundleId}),{app:t}}case"close":{let e=r[0];if(!e)return{closed:"session"};return await o.close(e),{app:e}}case"press":{let[t,i]=r.map(Number);if(Number.isNaN(t)||Number.isNaN(i))throw new I("INVALID_ARGS","press requires x y");let n=tz(a?.count??1,"count",1,200),u=tz(a?.intervalMs??0,"interval-ms",0,1e4),p=tz(a?.holdMs??0,"hold-ms",0,1e4),f=tz(a?.jitterPx??0,"jitter-px",0,100),m=a?.doubleTap===!0;if(m&&p>0)throw new I("INVALID_ARGS","double-tap cannot be combined with hold-ms");if(m&&f>0)throw new I("INVALID_ARGS","double-tap cannot be combined with jitter-px");if(s=e,l=n,c=p,d=f,"ios"===s.platform&&l>1&&0===c&&0===d)return await tv(e,{command:"tapSeries",x:t,y:i,count:n,intervalMs:u,doubleTap:m,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{x:t,y:i,count:n,intervalMs:u,holdMs:p,jitterPx:f,doubleTap:m,timingMode:"runner-series"};return await tX(n,u,async e=>{let[r,n]=function(e,t){if(t<=0)return[0,0];let[i,r]=tH[e%tH.length];return[i*t,r*t]}(e,f),a=t+r,s=i+n;m?await o.doubleTap(a,s):p>0?await o.longPress(a,s,p):await o.tap(a,s)}),{x:t,y:i,count:n,intervalMs:u,holdMs:p,jitterPx:f,doubleTap:m}}case"swipe":{let t=Number(r[0]),i=Number(r[1]),n=Number(r[2]),s=Number(r[3]);if([t,i,n,s].some(Number.isNaN))throw new I("INVALID_ARGS","swipe requires x1 y1 x2 y2 [durationMs]");let l=tz(r[4]?Number(r[4]):250,"durationMs",16,1e4),c="ios"===e.platform?60:l,d=tz(a?.count??1,"count",1,200),p=tz(a?.pauseMs??0,"pause-ms",0,1e4),m=a?.pattern??"one-way";if("one-way"!==m&&"ping-pong"!==m)throw new I("INVALID_ARGS",`Invalid pattern: ${m}`);if(u=e,f=d,"ios"===u.platform&&f>1)return await tv(e,{command:"dragSeries",x:t,y:i,x2:n,y2:s,durationMs:c,count:d,pauseMs:p,pattern:m,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{x1:t,y1:i,x2:n,y2:s,durationMs:l,effectiveDurationMs:c,timingMode:"runner-series",count:d,pauseMs:p,pattern:m};return await tX(d,p,async e=>{"ping-pong"===m&&e%2==1?await o.swipe(n,s,t,i,c):await o.swipe(t,i,n,s,c)}),{x1:t,y1:i,x2:n,y2:s,durationMs:l,effectiveDurationMs:c,timingMode:"ios"===e.platform?"safe-normalized":"direct",count:d,pauseMs:p,pattern:m}}case"long-press":{let e=Number(r[0]),t=Number(r[1]),i=r[2]?Number(r[2]):void 0;if(Number.isNaN(e)||Number.isNaN(t))throw new I("INVALID_ARGS","long-press requires x y [durationMs]");return await o.longPress(e,t,i),{x:e,y:t,durationMs:i}}case"focus":{let[e,t]=r.map(Number);if(Number.isNaN(e)||Number.isNaN(t))throw new I("INVALID_ARGS","focus requires x y");return await o.focus(e,t),{x:e,y:t}}case"type":{let e=r.join(" ");if(!e)throw new I("INVALID_ARGS","type requires text");return await o.type(e),{text:e}}case"fill":{let e=Number(r[0]),t=Number(r[1]),i=r.slice(2).join(" ");if(Number.isNaN(e)||Number.isNaN(t)||!i)throw new I("INVALID_ARGS","fill requires x y text");return await o.fill(e,t,i),{x:e,y:t,text:i}}case"scroll":{let e=r[0],t=r[1]?Number(r[1]):void 0;if(!e)throw new I("INVALID_ARGS","scroll requires direction");return await o.scroll(e,t),{direction:e,amount:t}}case"scrollintoview":{let e=r.join(" ").trim();if(!e)throw new I("INVALID_ARGS","scrollintoview requires text");let t=await o.scrollIntoView(e);if(t?.attempts)return{text:e,attempts:t.attempts};return{text:e}}case"pinch":{if("android"===e.platform)throw new I("UNSUPPORTED_OPERATION","Android pinch is not supported in current adb backend; requires instrumentation-based backend.");let t=Number(r[0]),i=r[1]?Number(r[1]):void 0,n=r[2]?Number(r[2]):void 0;if(Number.isNaN(t)||t<=0)throw new I("INVALID_ARGS","pinch requires scale > 0");return await tv(e,{command:"pinch",scale:t,x:i,y:n,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{scale:t,x:i,y:n}}case"screenshot":{let e=r[0]??n??`./screenshot-${Date.now()}.png`;return await h.mkdir(i.dirname(e),{recursive:!0}),await o.screenshot(e),{path:e}}case"back":if("ios"===e.platform)return await tv(e,{command:"back",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"back"};return await ew(e),{action:"back"};case"home":if("ios"===e.platform)return await tv(e,{command:"home",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"home"};return await eg(e),{action:"home"};case"app-switcher":if("ios"===e.platform)return await tv(e,{command:"appSwitcher",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{action:"app-switcher"};return await ev(e),{action:"app-switcher"};case"settings":{let[t,i,n]=r;if(m({level:"debug",phase:"settings_apply",data:{setting:t,state:i,platform:e.platform}}),"ios"===e.platform)return await e7(e,t,i,n??a?.appBundleId),{setting:t,state:i};return await eD(e,t,i),{setting:t,state:i}}case"snapshot":{if("ios"===e.platform){let t=await p("snapshot_capture",async()=>await tv(e,{command:"snapshot",appBundleId:a?.appBundleId,interactiveOnly:a?.snapshotInteractiveOnly,compact:a?.snapshotCompact,depth:a?.snapshotDepth,scope:a?.snapshotScope,raw:a?.snapshotRaw},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath}),{backend:"xctest"}),i=t.nodes??[];if(0===i.length&&"simulator"===e.kind)throw new I("COMMAND_FAILED","XCTest snapshot returned 0 nodes on iOS simulator.");return{nodes:i,truncated:t.truncated??!1,backend:"xctest"}}let t=await p("snapshot_capture",async()=>await eO(e,{interactiveOnly:a?.snapshotInteractiveOnly,compact:a?.snapshotCompact,depth:a?.snapshotDepth,scope:a?.snapshotScope,raw:a?.snapshotRaw}),{backend:"android"});return{nodes:t.nodes??[],truncated:t.truncated??!1,backend:"android"}}default:throw new I("INVALID_ARGS",`Unknown command: ${t}`)}},{command:t,platform:e.platform})}let tH=[[0,0],[1,0],[0,1],[-1,0],[0,-1],[1,1],[-1,1],[1,-1],[-1,-1]];function tz(e,t,i,r){if(!Number.isFinite(e)||!Number.isInteger(e)||e<i||e>r)throw new I("INVALID_ARGS",`${t} must be an integer between ${i} and ${r}`);return e}async function tX(e,t,i){for(let r=0;r<e;r+=1)await i(r),r<e-1&&t>0&&await tY(t)}async function tY(e){await new Promise(t=>setTimeout(t,e))}let tK={alert:{ios:{simulator:!0},android:{}},pinch:{ios:{simulator:!0},android:{}},"app-switcher":{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},apps:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},back:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},boot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},click:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},close:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},fill:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},find:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},focus:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},get:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},is:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},home:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},"long-press":{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},open:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},reinstall:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},press:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},record:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},screenshot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},scroll:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},scrollintoview:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},swipe:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},settings:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},snapshot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},type:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},wait:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}}};function tZ(e,t){let i=tK[e];if(!i)return!0;let r=i[t.platform];return!!r&&!0===r[t.kind??"unknown"]}function tQ(e){let t=e.result?.text;if("string"==typeof t&&t.trim().length>0)return t;let i=e.positionals??[];return 0===i.length?"":i[0].startsWith("@")?i.length>=3?i.slice(2).join(" ").trim():i.slice(1).join(" ").trim():!(i.length>=3)||Number.isNaN(Number(i[0]))||Number.isNaN(Number(i[1]))?i.slice(1).join(" ").trim():i.slice(2).join(" ").trim()}function t0(e){let t=new Set,i=[];for(let r of e)t.has(r)||(t.add(r),i.push(r));return i}let t1=/^-?\d+(\.\d+)?$/,t2=new Map([["--count","count"],["--interval-ms","intervalMs"],["--hold-ms","holdMs"],["--jitter-px","jitterPx"]]),t3=new Map([["--count","count"],["--pause-ms","pauseMs"]]);function t4(e){return"click"===e||"press"===e}function t5(e){let t=e.trim();return t.startsWith("@")||t1.test(t)?t:JSON.stringify(t)}function t8(e,t){let i=t.flags??{};if(t4(t.command)){"number"==typeof i.count&&e.push("--count",String(i.count)),"number"==typeof i.intervalMs&&e.push("--interval-ms",String(i.intervalMs)),"number"==typeof i.holdMs&&e.push("--hold-ms",String(i.holdMs)),"number"==typeof i.jitterPx&&e.push("--jitter-px",String(i.jitterPx)),!0===i.doubleTap&&e.push("--double-tap");return}"swipe"===t.command&&("number"==typeof i.count&&e.push("--count",String(i.count)),"number"==typeof i.pauseMs&&e.push("--pause-ms",String(i.pauseMs)),("one-way"===i.pattern||"ping-pong"===i.pattern)&&e.push("--pattern",i.pattern))}function t6(e,t){let i=[],r={},n=t4(e)?t2:"swipe"===e?t3:void 0;for(let a=0;a<t.length;a+=1){let o=t[a];if(t4(e)&&"--double-tap"===o){r.doubleTap=!0;continue}let s=n?.get(o);if(s&&a+1<t.length){let e=function(e){if(!e)return null;let t=Number(e);return!Number.isFinite(t)||t<0?null:Math.floor(t)}(t[a+1]);null!==e&&(r[s]=e),a+=1;continue}if("swipe"===e&&"--pattern"===o&&a+1<t.length){let e=t[a+1];("one-way"===e||"ping-pong"===e)&&(r.pattern=e),a+=1;continue}i.push(o)}return{positionals:i,flags:r}}class t7{sessions=new Map;sessionsDir;constructor(e){this.sessionsDir=e}get(e){return this.sessions.get(e)}has(e){return this.sessions.has(e)}set(e,t){this.sessions.set(e,t)}delete(e){return this.sessions.delete(e)}values(){return this.sessions.values()}toArray(){return Array.from(this.sessions.values())}recordAction(e,t){t.flags?.noRecord||(t.flags?.saveScript&&(e.recordSession=!0,"string"==typeof t.flags.saveScript&&(e.saveScriptPath=t7.expandHome(t.flags.saveScript))),e.actions.push({ts:Date.now(),command:t.command,positionals:t.positionals,flags:function(e){if(!e)return{};let{platform:t,device:i,udid:r,serial:n,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:d,snapshotRaw:u,relaunch:p,saveScript:f,noRecord:m,count:h,intervalMs:w,holdMs:g,jitterPx:v,doubleTap:I,pauseMs:A,pattern:y}=e;return{platform:t,device:i,udid:r,serial:n,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:c,snapshotScope:d,snapshotRaw:u,relaunch:p,saveScript:f,noRecord:m,count:h,intervalMs:w,holdMs:g,jitterPx:v,doubleTap:I,pauseMs:A,pattern:y}}(t.flags),result:t.result}),m({level:"debug",phase:"record_action",data:{command:t.command,session:e.name}}))}writeSessionLog(e){try{if(!e.recordSession)return;let t=this.resolveScriptPath(e),r=i.dirname(t);A.existsSync(r)||A.mkdirSync(r,{recursive:!0});let n=function(e,t){let i=[],r=e.device.name.replace(/"/g,'\\"'),n=e.device.kind?` kind=${e.device.kind}`:"";for(let a of(i.push(`context platform=${e.device.platform} device="${r}"${n} theme=unknown`),t))a.flags?.noRecord||i.push(function(e){let t=[e.command];if(t4(e.command)){let i=e.positionals?.[0];if(i){if(i.startsWith("@")){t.push(t5(i));let r=e.result?.refLabel;return"string"==typeof r&&r.trim().length>0&&t.push(t5(r)),t8(t,e),t.join(" ")}if(1===e.positionals.length)return t.push(t5(i)),t8(t,e),t.join(" ")}}if("fill"===e.command){let i=e.positionals?.[0];if(i&&i.startsWith("@")){t.push(t5(i));let r=e.result?.refLabel,n=e.positionals.slice(1).join(" ");return"string"==typeof r&&r.trim().length>0&&t.push(t5(r)),n&&t.push(t5(n)),t.join(" ")}}if("get"===e.command){let i=e.positionals?.[0],r=e.positionals?.[1];if(i&&r){if(t.push(t5(i)),t.push(t5(r)),r.startsWith("@")){let i=e.result?.refLabel;"string"==typeof i&&i.trim().length>0&&t.push(t5(i))}return t.join(" ")}}if("snapshot"===e.command)return e.flags?.snapshotInteractiveOnly&&t.push("-i"),e.flags?.snapshotCompact&&t.push("-c"),"number"==typeof e.flags?.snapshotDepth&&t.push("-d",String(e.flags.snapshotDepth)),e.flags?.snapshotScope&&t.push("-s",t5(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(t5(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(t5(i));return t8(t,e),t.join(" ")}(a));return`${i.join("\n")}
|
|
13
|
-
`}(e,this.buildOptimizedActions(e));
|
|
14
|
-
${t}`.toLowerCase().includes("timed out waiting for all destinations")?"Xcode destination did not become available in time. Keep device unlocked and retry.":ez)}function io(e){return e.map((e,t)=>({...e,ref:`e${t+1}`}))}function is(e){let t=e.trim();return t.startsWith("@")?t.slice(1)||null:t.startsWith("e")?t:null}function il(e,t){return e.find(e=>e.ref===t)??null}function ic(e){return{x:Math.round(e.x+e.width/2),y:Math.round(e.y+e.height/2)}}function id(e,t){let i=t.toLowerCase();return e.find(e=>{let t=(e.label??"").toLowerCase(),r=(e.value??"").toLowerCase(),n=(e.identifier??"").toLowerCase();return t.includes(i)||r.includes(i)||n.includes(i)})??null}function iu(e,t){let i=[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0);return i&&ip(i)?i:function(e,t){if(!e.rect)return;let i=e.rect.y+e.rect.height/2,r=null;for(let e of t){if(!e.rect)continue;let t=[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0);if(!t||!ip(t))continue;let n=Math.abs(e.rect.y+e.rect.height/2-i);(!r||n<r.distance)&&(r={label:t,distance:n})}return r?.label}(e,t)??(i&&ip(i)?i:void 0)}function ip(e){let t=e.trim();return!(!t||/^(true|false)$/i.test(t)||/^\d+$/.test(t))}function im(e){let t=[],i=[];for(let r of e){let e=r.depth??0;for(;t.length>0&&e<=t[t.length-1];)t.pop();let n=ih(r.type??""),a=[r.label,r.value,r.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0),o=!!a&&ip(a);if(("group"===n||"ioscontentgroup"===n)&&!o){t.push(e);continue}let s=Math.max(0,e-t.length);i.push({...r,depth:s})}return i}function ih(e){let t=e.trim().replace(/XCUIElementType/gi,"").toLowerCase(),i=Math.max(t.lastIndexOf("."),t.lastIndexOf("/"));return -1!==i&&(t=t.slice(i+1)),t}function iw(e,t){let i=ih(e);return!i||("android"===t?i.includes("edittext")||i.includes("autocompletetextview"):i.includes("textfield")||i.includes("securetextfield")||i.includes("searchfield")||i.includes("textview")||i.includes("textarea")||"search"===i)}function ig(e){return[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").filter(e=>e.length>0)[0]??""}let iv=new Set(["id","role","text","label","value"]),iI=new Set(["visible","hidden","editable","selected","enabled","hittable"]),iA=new Set([...iv,...iI]);function iy(e){let t=e.trim();if(!t)throw new I("INVALID_ARGS","Selector expression cannot be empty");let i=function(e){let t=[],i="",r=null;for(let n=0;n<e.length;n+=1){let a=e[n];if(('"'===a||"'"===a)&&!iV(e,n)){r?r===a&&(r=null):r=a,i+=a;continue}if(!r&&"|"===a&&"|"===e[n+1]){let r=i.trim();if(!r)throw new I("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);t.push(r),i="",n+=1;continue}i+=a}let n=i.trim();if(!n)throw new I("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);return t.push(n),t}(t);if(0===i.length)throw new I("INVALID_ARGS","Selector expression cannot be empty");return{raw:t,selectors:i.map(e=>(function(e){let t=e.trim();if(!t)throw new I("INVALID_ARGS","Selector segment cannot be empty");let i=function(e){let t=[],i="",r=null;for(let n=0;n<e.length;n+=1){let a=e[n];if(('"'===a||"'"===a)&&!iV(e,n)){r?r===a&&(r=null):r=a,i+=a;continue}if(!r&&/\s/.test(a)){i.trim().length>0&&t.push(i.trim()),i="";continue}i+=a}if(r)throw new I("INVALID_ARGS",`Unclosed quote in selector: ${e}`);return i.trim().length>0&&t.push(i.trim()),t}(t);if(0===i.length)throw new I("INVALID_ARGS",`Invalid selector segment: ${e}`);return{raw:t,terms:i.map(ik)}})(e))}}function iN(e){try{return iy(e)}catch{return null}}function iS(e,t,i){let r=i.requireRect??!1,n=i.requireUnique??!0,a=i.disambiguateAmbiguous??!1,o=[];for(let s=0;s<t.selectors.length;s+=1){let l=t.selectors[s],c=function(e,t,i){let r=0,n=null,a=null,o=!1;for(let s of e){if(i.requireRect&&!s.rect||!iL(s,t,i.platform))continue;if(r+=1,n||(n=s),!a){a=s;continue}let e=function(e,t){let i=e.depth??0,r=t.depth??0;if(i!==r)return i>r?1:-1;let n=iF(e),a=iF(t);return n!==a?n<a?1:-1:0}(s,a);if(e>0){a=s,o=!1;continue}0===e&&(o=!0)}return{count:r,firstNode:n,disambiguated:o?null:a}}(e,l,{platform:i.platform,requireRect:r});if(o.push({selector:l.raw,matches:c.count}),0!==c.count&&c.firstNode){if(n&&1!==c.count){if(!a)continue;let e=c.disambiguated;if(!e)continue;return{node:e,selector:l,selectorIndex:s,matches:c.count,diagnostics:o}}return{node:c.firstNode,selector:l,selectorIndex:s,matches:c.count,diagnostics:o}}}return null}function ib(e,t,i){let r=i.requireRect??!1,n=[];for(let a=0;a<t.selectors.length;a+=1){let o=t.selectors[a],s=function(e,t,i){let r=0;for(let n of e)(!i.requireRect||n.rect)&&iL(n,t,i.platform)&&(r+=1);return r}(e,o,{platform:i.platform,requireRect:r});if(n.push({selector:o.raw,matches:s}),s>0)return{selectorIndex:a,selector:o,matches:s,diagnostics:n}}return null}function i_(e,t,i){let r=i.unique??!0;if(0===t.length)return`Selector did not match: ${e.raw}`;let n=t.map(e=>`${e.selector} -> ${e.matches}`).join(", ");return r?`Selector did not resolve uniquely (${n})`:`Selector did not match (${n})`}function iD(e,t={}){if(0===e.length)return null;let i=t.preferTrailingValue??!1,r=0,n=[];for(;r<e.length&&function(e){let t=e.trim();if(!t)return!1;if("||"===t)return!0;let i=t.indexOf("=");if(-1!==i){let e=t.slice(0,i).trim().toLowerCase();return iA.has(e)}return iA.has(t.toLowerCase())}(e[r]);){r+=1;let t=e.slice(0,r).join(" ").trim();t&&iN(t)&&n.push(r)}if(0===n.length)return null;let a=n[n.length-1];if(i){for(let t=n.length-1;t>=0;t-=1)if(n[t]<e.length){a=n[t];break}}let o=e.slice(0,a).join(" ").trim();return o?{selectorExpression:o,rest:e.slice(a)}:null}function iO(e){let t=e[0]??"",i=iD(e.slice(1),{preferTrailingValue:"text"===t});return{predicate:t,split:i}}function iM(e){return!0===e.hittable||!!e.rect&&e.rect.width>0&&e.rect.height>0}function iE(e,t){return iw(e.type??"",t)&&!1!==e.enabled}function ix(e,t,i={}){let r=[],n=ih(e.type??""),a=i$(e.identifier),o=i$(e.label),s=i$(e.value),l=i$(ig(e)),c="fill"===i.action;a&&r.push(`id=${iP(a)}`),n&&o&&r.push(c?`role=${iP(n)} label=${iP(o)} editable=true`:`role=${iP(n)} label=${iP(o)}`),o&&r.push(c?`label=${iP(o)} editable=true`:`label=${iP(o)}`),s&&r.push(c?`value=${iP(s)} editable=true`:`value=${iP(s)}`),l&&l!==o&&l!==s&&r.push(c?`text=${iP(l)} editable=true`:`text=${iP(l)}`),n&&c&&!r.some(e=>e.includes("editable=true"))&&r.push(`role=${iP(n)} editable=true`);let d=t0(r);return 0===d.length&&n&&d.push(c?`role=${iP(n)} editable=true`:`role=${iP(n)}`),0===d.length&&iM(e)&&d.push("visible=true"),d}function ik(e){let t=e.trim();if(!t)throw new I("INVALID_ARGS","Empty selector term");let i=t.indexOf("=");if(-1===i){let i=t.toLowerCase();if(!iI.has(i))throw new I("INVALID_ARGS",`Invalid selector term "${e}", expected key=value`);return{key:i,value:!0}}let r=t.slice(0,i).trim().toLowerCase(),n=t.slice(i+1).trim();if(!iA.has(r))throw new I("INVALID_ARGS",`Unknown selector key: ${r}`);if(!n)throw new I("INVALID_ARGS",`Missing selector value for key: ${r}`);if(iI.has(r)){let e,t="true"===(e=iC(n).toLowerCase())||"false"!==e&&null;if(null===t)throw new I("INVALID_ARGS",`Invalid boolean value for ${r}: ${n}`);return{key:r,value:t}}return{key:r,value:iC(n)}}function iL(e,t,i){return t.terms.every(t=>(function(e,t,i){switch(t.key){case"id":return iR(e.identifier,String(t.value));case"role":var r,n;return r=e.type,n=String(t.value),function(e){return ih(e)}(r??"")===function(e){return ih(e)}(n);case"label":return iR(e.label,String(t.value));case"value":return iR(e.value,String(t.value));case"text":{let i=iT(String(t.value));return iT(ig(e))===i}case"visible":return iM(e)===!!t.value;case"hidden":return!iM(e)==!!t.value;case"editable":return iE(e,i)===!!t.value;case"selected":return!0===e.selected==!!t.value;case"enabled":return!1!==e.enabled==!!t.value;case"hittable":return!0===e.hittable==!!t.value;default:return!1}})(e,t,i))}function iC(e){let t=e.trim();return t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1).replace(/\\(["'])/g,"$1"):t}function iR(e,t){return iT(e??"")===iT(t)}function iT(e){return e.trim().toLowerCase().replace(/\s+/g," ")}function iP(e){return JSON.stringify(e)}function i$(e){if(!e)return null;let t=e.trim();return t||null}function iF(e){return e.rect?e.rect.width*e.rect.height:1/0}function iV(e,t){let i=0;for(let r=t-1;r>=0&&"\\"===e[r];r-=1)i+=1;return i%2==1}let iU='iOS appstate requires an active session on the target device. Run open first (for example: open --session sim --platform ios --device "<name>" <app>).',iG=["platform","device","udid","serial","verbose","out"],ij=["platform","device","udid","serial","verbose","out"];function iB(e,t,i){return t||iq(i)?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} requires an active session or an explicit device selector (e.g. --platform ios).`}}}function iq(e){return!!(e?.platform||e?.device||e?.udid||e?.serial)}async function iW(e){let t=iq(e.flags)||!e.session?await e.resolveTargetDeviceFn(e.flags??{}):e.session.device;return!1!==e.ensureReady&&await e.ensureReadyFn(t),t}let iJ={ios:async(e,t,i)=>{let{reinstallIosApp:r}=await Promise.resolve().then(()=>({reinstallIosApp:e8}));return await r(e,t,i)},android:async(e,t,i)=>{let{reinstallAndroidApp:r}=await Promise.resolve().then(()=>({reinstallAndroidApp:ef}));return await r(e,t,i)}};async function iH(e,t,i){if("ios"===e.platform&&t)return W(t)?"device"===e.kind?J(i,t):void 0:await iz(e,t)}async function iz(e,t){try{let{resolveIosApp:i}=await Promise.resolve().then(()=>({resolveIosApp:e0}));return await i(e,t)}catch{return}}async function iX(e){let{req:t,sessionName:i,sessionStore:r,ensureReady:n,resolveDevice:a}=e,o=r.get(i),s=t.flags??{};if(!o&&"string"==typeof s?.session&&s.session.trim().length>0)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"ios"===s.platform?`No active session "${i}". Run open with --session ${i} first.`:`No active session "${i}". Run open with --session ${i} first, or omit --session to query by device selector.`}};let l=iB("appstate",o,s);if(l)return l;let c=o?.device.platform==="ios"&&!!o&&(!iq(s)||!(s?.platform&&s.platform!==o.device.platform||s?.udid&&s.udid!==o.device.id||s?.serial&&s.serial!==o.device.id)&&(!s?.device||s.device.trim().toLowerCase()===o.device.name.trim().toLowerCase()));if("ios"===s.platform&&!c)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:iU}};if(c){let e=o.appName??o.appBundleId;return o.appName||o.appBundleId?{ok:!0,data:{platform:"ios",appName:e??"unknown",appBundleId:o.appBundleId,source:"session"}}:{ok:!1,error:{code:"COMMAND_FAILED",message:"No foreground app is tracked for this iOS session. Open an app in the session, then retry appstate."}}}let d=await iW({session:o,flags:s,ensureReadyFn:n,resolveTargetDeviceFn:a,ensureReady:!0});if("ios"===d.platform)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:iU}};let{getAndroidAppState:u}=await Promise.resolve().then(()=>({getAndroidAppState:ea})),p=await u(d);return{ok:!0,data:{platform:"android",package:p.package,activity:p.activity}}}async function iY(e){let{req:t,sessionName:i,logPath:r,sessionStore:n,invoke:a,dispatch:o,ensureReady:s,resolveTargetDevice:l,reinstallOps:c=iJ}=e,d=o??tJ,u=s??it,p=l??tW,f=t.command;if("session_list"===f)return{ok:!0,data:{sessions:n.toArray().map(e=>({name:e.name,platform:e.device.platform,device:e.device.name,id:e.device.id,createdAt:e.createdAt}))}};if("devices"===f)try{let e=[];if(t.flags?.platform==="android"){let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:j}));e.push(...await t())}else if(t.flags?.platform==="ios"){let{listIosDevices:t}=await Promise.resolve().then(()=>({listIosDevices:eG}));e.push(...await t())}else{let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:j})),{listIosDevices:i}=await Promise.resolve().then(()=>({listIosDevices:eG}));try{e.push(...await t())}catch{}try{e.push(...await i())}catch{}}return{ok:!0,data:{devices:e}}}catch(t){let e=w(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}if("apps"===f){let e=n.get(i),r=t.flags??{},a=iB(f,e,r);if(a)return a;let o=await iW({session:e,flags:r,ensureReadyFn:u,resolveTargetDeviceFn:p,ensureReady:!0});if(!tZ("apps",o))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"apps is not supported on this device"}};let s=t.flags?.appsFilter??"all";if("ios"===o.platform){let{listIosApps:e}=await Promise.resolve().then(()=>({listIosApps:e9}));return{ok:!0,data:{apps:(await e(o,s)).map(e=>e.name&&e.name!==e.bundleId?`${e.name} (${e.bundleId})`:e.bundleId)}}}let{listAndroidApps:l}=await Promise.resolve().then(()=>({listAndroidApps:ei}));return{ok:!0,data:{apps:(await l(o,s)).map(e=>e.name&&e.name!==e.package?`${e.name} (${e.package})`:e.package)}}}if("boot"===f){let e=n.get(i),r=t.flags??{},a=iB(f,e,r);if(a)return a;let o=await iW({session:e,flags:r,ensureReadyFn:u,resolveTargetDeviceFn:p,ensureReady:!0});return tZ("boot",o)?{ok:!0,data:{platform:o.platform,device:o.name,id:o.id,kind:o.kind,booted:!0}}:{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"boot is not supported on this device"}}}if("appstate"===f)return await iX({req:t,sessionName:i,sessionStore:n,ensureReady:u,resolveDevice:p});if("reinstall"===f){let e,r=n.get(i),a=t.flags??{},o=iB(f,r,a);if(o)return o;let s=t.positionals?.[0]?.trim(),l=t.positionals?.[1]?.trim();if(!s||!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"reinstall requires: reinstall <app> <path-to-app-binary>"}};let d=t7.expandHome(l);if(!A.existsSync(d))return{ok:!1,error:{code:"INVALID_ARGS",message:`App binary not found: ${d}`}};let m=await iW({session:r,flags:a,ensureReadyFn:u,resolveTargetDeviceFn:p,ensureReady:!1});if(!tZ("reinstall",m))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"reinstall is not supported on this device"}};if("ios"===m.platform){let t=await c.ios(m,s,d);e={platform:"ios",appId:t.bundleId,bundleId:t.bundleId}}else{let t=await c.android(m,s,d);e={platform:"android",appId:t.package,package:t.package}}let h={app:s,appPath:d,...e};return r&&n.recordAction(r,{command:f,positionals:t.positionals??[],flags:t.flags??{},result:h}),{ok:!0,data:h}}if("open"===f){let e=t.flags?.relaunch===!0;if(n.has(i)){let a=n.get(i),o=t.positionals?.[0],s=o??(e?a?.appName:void 0);if(!a||!s)return e?{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch requires an app name or an active session app."}}:{ok:!1,error:{code:"INVALID_ARGS",message:"Session already active. Close it first or pass a new --session name."}};if(e&&W(s))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};await u(a.device);let l=await iH(a.device,s,a.appBundleId),c=o?t.positionals??[]:[s];if(e){let e=l??s;await d(a.device,"close",[e],t.flags?.out,{...t9(r,t.flags,l??a.appBundleId,a.trace?.outPath)})}await d(a.device,"open",c,t.flags?.out,{...t9(r,t.flags,l)});let p={...a,appBundleId:l,appName:s,recordSession:a.recordSession||!!t.flags?.saveScript,snapshot:void 0};return n.recordAction(p,{command:f,positionals:c,flags:t.flags??{},result:{session:i,appName:s,appBundleId:l}}),n.set(i,p),{ok:!0,data:{session:i,appName:s,appBundleId:l}}}let a=t.positionals?.[0];if(e&&!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch requires an app argument."}};if(e&&a&&W(a))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};let o=await p(t.flags??{}),s=n.toArray().find(e=>e.device.id===o.id);if(s)return{ok:!1,error:{code:"DEVICE_IN_USE",message:`Device is already in use by session "${s.name}".`,details:{session:s.name,deviceId:o.id,deviceName:o.name}}};await u(o);let l=await iH(o,a);if(e&&a){let e=l??a;await d(o,"close",[e],t.flags?.out,{...t9(r,t.flags,l)})}await d(o,"open",t.positionals??[],t.flags?.out,{...t9(r,t.flags,l)});let c={name:i,device:o,createdAt:Date.now(),appBundleId:l,appName:a,recordSession:!!t.flags?.saveScript,actions:[]};return n.recordAction(c,{command:f,positionals:t.positionals??[],flags:t.flags??{},result:{session:i}}),n.set(i,c),{ok:!0,data:{session:i}}}if("replay"===f){let e=t.positionals?.[0];if(!e)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay requires a path"}};try{let o=t7.expandHome(e),s=A.readFileSync(o,"utf8"),l=s.trimStart()[0];if("{"===l||"["===l)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay accepts .ad script files. JSON replay payloads are no longer supported."}};let c=function(e){let t=[];for(let i of e.split(/\r?\n/)){let e=function(e){let t=e.trim();if(0===t.length||t.startsWith("#"))return null;let i=function(e){let t=[],i=0;for(;i<e.length;){for(;i<e.length&&/\s/.test(e[i]);)i+=1;if(i>=e.length)break;if('"'===e[i]){let r=i+1,n=!1;for(;r<e.length;){let t=e[r];if('"'===t&&!n)break;n="\\"===t&&!n,"\\"!==t&&(n=!1),r+=1}if(r>=e.length)throw new I("INVALID_ARGS",`Invalid replay script line: ${e}`);let a=e.slice(i,r+1);t.push(JSON.parse(a)),i=r+1;continue}let r=i;for(;r<e.length&&!/\s/.test(e[r]);)r+=1;t.push(e.slice(i,r)),i=r}return t}(t);if(0===i.length)return null;let[r,...n]=i;if("context"===r)return null;let a={ts:Date.now(),command:r,positionals:[],flags:{}};if("snapshot"===r){a.positionals=[];for(let e=0;e<n.length;e+=1){let t=n[e];if("-i"===t){a.flags.snapshotInteractiveOnly=!0;continue}if("-c"===t){a.flags.snapshotCompact=!0;continue}if("--raw"===t){a.flags.snapshotRaw=!0;continue}if(("-d"===t||"--depth"===t)&&e+1<n.length){let t=Number(n[e+1]);Number.isFinite(t)&&t>=0&&(a.flags.snapshotDepth=Math.floor(t)),e+=1;continue}if(("-s"===t||"--scope"===t)&&e+1<n.length){a.flags.snapshotScope=n[e+1],e+=1;continue}if("--backend"===t&&e+1<n.length){e+=1;continue}}return a}if("open"===r){a.positionals=[];for(let e=0;e<n.length;e+=1){let t=n[e];if("--relaunch"===t){a.flags.relaunch=!0;continue}a.positionals.push(t)}return a}if(t4(r)){let e=t6(r,n);if(Object.assign(a.flags,e.flags),0===e.positionals.length)return a;let t=e.positionals[0];if(t.startsWith("@"))return a.positionals=[t],e.positionals[1]&&(a.result={refLabel:e.positionals[1]}),a;let i=e.positionals[0],o=e.positionals[1];return i4(i)&&i4(o)&&e.positionals.length>=2?a.positionals=[i,o]:a.positionals=[e.positionals.join(" ")],a}if("fill"===r){if(n.length<2)return a.positionals=n,a;let e=n[0];return e.startsWith("@")?(n.length>=3?(a.positionals=[e,n.slice(2).join(" ")],a.result={refLabel:n[1]}):a.positionals=[e,n[1]],a):(a.positionals=[e,n.slice(1).join(" ")],a)}if("get"===r){if(n.length<2)return a.positionals=n,a;let e=n[0],t=n[1];return t.startsWith("@")?(a.positionals=[e,t],n[2]&&(a.result={refLabel:n[2]})):a.positionals=[e,n.slice(1).join(" ")],a}if("swipe"===r){let e=t6(r,n);return Object.assign(a.flags,e.flags),a.positionals=e.positionals,a}return a.positionals=n,a}(i);e&&t.push(e)}return t}(s),u=t.flags?.replayUpdate===!0,p=0;for(let e=0;e<c.length;e+=1){let s=c[e];if(!s||"replay"===s.command)continue;let l=await a({token:t.token,session:i,command:s.command,positionals:s.positionals??[],flags:i0(t.flags,s.flags),meta:t.meta});if(l.ok)continue;if(!u)return iQ(l,s,e,o);let f=await i1({action:s,sessionName:i,logPath:r,sessionStore:n,dispatch:d});if(!f)return iQ(l,s,e,o);if(c[e]=f,!(l=await a({token:t.token,session:i,command:f.command,positionals:f.positionals??[],flags:i0(t.flags,f.flags),meta:t.meta})).ok)return iQ(l,f,e,o);p+=1}if(u&&p>0){let e=n.get(i);!function(e,t,i){let r=[];if(i){let e=i.device.name.replace(/"/g,'\\"'),t=i.device.kind?` kind=${i.device.kind}`:"";r.push(`context platform=${i.device.platform} device="${e}"${t} theme=unknown`)}for(let e of t)r.push(function(e){let t=[e.command];if("snapshot"===e.command)return e.flags?.snapshotInteractiveOnly&&t.push("-i"),e.flags?.snapshotCompact&&t.push("-c"),"number"==typeof e.flags?.snapshotDepth&&t.push("-d",String(e.flags.snapshotDepth)),e.flags?.snapshotScope&&t.push("-s",t5(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(t5(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(t5(i));return t8(t,e),t.join(" ")}(e));let n=`${r.join("\n")}
|
|
15
|
-
`,a=`${e}.tmp-${process.pid}-${Date.now()}`;A.writeFileSync(a,n),A.renameSync(a,e)}(o,c,e)}return{ok:!0,data:{replayed:c.length,healed:p,session:i}}}catch(t){let e=w(t);return{ok:!1,error:{code:e.code,message:e.message}}}}if("batch"===f)return await iK(t,i,a);if("close"===f){let e=n.get(i);return e?(t.positionals&&t.positionals.length>0&&await d(e.device,"close",t.positionals??[],t.flags?.out,{...t9(r,t.flags,e.appBundleId,e.trace?.outPath)}),"ios"===e.device.platform&&await tN(e.device.id),n.recordAction(e,{command:f,positionals:t.positionals??[],flags:t.flags??{},result:{session:i}}),t.flags?.saveScript&&(e.recordSession=!0),n.writeSessionLog(e),n.delete(i),{ok:!0,data:{session:i}}):{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}}}return null}async function iK(e,t,i){let r=e.flags?.batchOnError??"stop";if("stop"!==r)return{ok:!1,error:{code:"INVALID_ARGS",message:`Unsupported batch on-error mode: ${r}.`}};let n=e.flags?.batchMaxSteps??g;if(!Number.isInteger(n)||n<1||n>1e3)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid batch max-steps: ${String(e.flags?.batchMaxSteps)}`}};try{let r=s(e.flags?.batchSteps,n),a=Date.now(),o=[];for(let n=0;n<r.length;n+=1){let a=r[n],s=await iZ(e,t,a,i,n+1);if(!s.ok)return{ok:!1,error:{code:s.error.code,message:`Batch failed at step ${s.step} (${a.command}): ${s.error.message}`,hint:s.error.hint,diagnosticId:s.error.diagnosticId,logPath:s.error.logPath,details:{...s.error.details??{},step:s.step,command:a.command,positionals:a.positionals,executed:n,total:r.length,partialResults:o}}};o.push(s.result)}return{ok:!0,data:{total:r.length,executed:r.length,totalDurationMs:Date.now()-a,results:o}}}catch(t){let e=w(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}}async function iZ(e,t,i,r,n){let a=Date.now(),o=await r({token:e.token,session:t,command:i.command,positionals:i.positionals,flags:function(e,t){let i={...t??{}};delete i.batchSteps,delete i.batchOnError,delete i.batchMaxSteps;let r=e??{};for(let e of iG)void 0===i[e]&&void 0!==r[e]&&(i[e]=r[e]);return i}(e.flags,i.flags),meta:e.meta}),s=Date.now()-a;return o.ok?{ok:!0,step:n,result:{step:n,command:i.command,ok:!0,data:o.data??{},durationMs:s}}:{ok:!1,step:n,error:o.error}}function iQ(e,t,i,r){if(e.ok)return e;let n=i+1,a=function(e){let t;return t=(e.positionals??[]).map(e=>t5(e)),[e.command,...t].join(" ")}(t),o={...e.error.details??{},replayPath:r,step:n,action:t.command,positionals:t.positionals??[]};return{ok:!1,error:{code:e.error.code,message:`Replay failed at step ${n} (${a}): ${e.error.message}`,hint:e.error.hint,diagnosticId:e.error.diagnosticId,logPath:e.error.logPath,details:o}}}function i0(e,t){let i={...t??{}},r=e??{};for(let e of ij)void 0===i[e]&&void 0!==r[e]&&(i[e]=r[e]);return i}async function i1(e){let{action:t,sessionName:i,logPath:r,sessionStore:n,dispatch:a}=e;if(!(t4(t.command)||["fill","get","is","wait"].includes(t.command)))return null;let o=n.get(i);if(!o)return null;let s=t4(t.command)||"fill"===t.command,l=t4(t.command)||"fill"===t.command||"get"===t.command&&t.positionals?.[0]==="text",c=await i2(o,t,r,s,a,n);for(let e of function(e){let t=[],i=Array.isArray(e.result?.selectorChain)&&e.result?.selectorChain.every(e=>"string"==typeof e)?e.result.selectorChain:[];if(t.push(...i),t4(e.command)){let i=e.positionals?.[0]??"";i&&!i.startsWith("@")&&t.push(e.positionals.join(" "))}if("fill"===e.command){let i=e.positionals?.[0]??"";i&&!i.startsWith("@")&&Number.isNaN(Number(i))&&t.push(i)}if("get"===e.command){let i=e.positionals?.[1]??"";i&&!i.startsWith("@")&&t.push(e.positionals.slice(1).join(" "))}if("is"===e.command){let{split:i}=iO(e.positionals);i&&t.push(i.selectorExpression)}if("wait"===e.command){let{selectorExpression:i}=i3(e.positionals??[]);i&&t.push(i)}let r="string"==typeof e.result?.refLabel?e.result.refLabel.trim():"";if(r.length>0){let i=JSON.stringify(r);"fill"===e.command?(t.push(`id=${i} editable=true`),t.push(`label=${i} editable=true`),t.push(`text=${i} editable=true`),t.push(`value=${i} editable=true`)):(t.push(`id=${i}`),t.push(`label=${i}`),t.push(`text=${i}`),t.push(`value=${i}`))}return t0(t).filter(e=>e.trim().length>0)}(t)){let i=iN(e);if(!i)continue;let r=iS(c.nodes,i,{platform:o.device.platform,requireRect:s,requireUnique:!0,disambiguateAmbiguous:l});if(!r)continue;let n=ix(r.node,o.device.platform,{action:t4(t.command)?"click":"fill"===t.command?"fill":"get"}).join(" || ");if(t4(t.command))return{...t,positionals:[n]};if("fill"===t.command){let e=tQ(t);if(!e)continue;return{...t,positionals:[n,e]}}if("get"===t.command){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)continue;return{...t,positionals:[e,n]}}if("is"===t.command){let{predicate:e,split:i}=iO(t.positionals);if(!e)continue;let r=i?.rest.join(" ").trim()??"",a=[e,n];return"text"===e&&r.length>0&&a.push(r),{...t,positionals:a}}if("wait"===t.command){let{selectorTimeout:e}=i3(t.positionals??[]),i=[n];return e&&i.push(e),{...t,positionals:i}}}let d=function(e,t,i){if("get"!==e.command||e.positionals?.[0]!=="text")return null;let r=e.positionals?.[1];if(!r)return null;let n=iN(r);if(!n)return null;let a=new Set,o=!1;for(let e of n.selectors)for(let t of e.terms)"role"===t.key&&"string"==typeof t.value&&a.add(ih(t.value)),("text"===t.key||"label"===t.key||"value"===t.key)&&"string"==typeof t.value&&/^\d+$/.test(t.value.trim())&&(o=!0);if(!o)return null;let s=t.nodes.filter(e=>{let t=ig(e).trim();return!!/^\d+$/.test(t)&&(0===a.size||a.has(ih(e.type??"")))});if(0===s.length||1!==t0(s.map(e=>ig(e).trim())).length)return null;let l=s[0];if(!l)return null;let c=ix(l,i.device.platform,{action:"get"});return 0===c.length?null:{...e,positionals:["text",c.join(" || ")]}}(t,c,o);return d||null}async function i2(e,t,i,r,n,a){let o=await n(e.device,"snapshot",[],t.flags?.out,{...t9(i,{...t.flags??{},snapshotInteractiveOnly:r,snapshotCompact:r},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[],l={nodes:io(t.flags?.snapshotRaw?s:im(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend};return e.snapshot=l,a.set(e.name,e),l}function i3(e){if(0===e.length)return{selectorExpression:null,selectorTimeout:null};let t=e[e.length-1],i=/^\d+$/.test(t??""),r=iD(i?e.slice(0,-1):e.slice());return!r||r.rest.length>0?{selectorExpression:null,selectorTimeout:null}:{selectorExpression:r.selectorExpression,selectorTimeout:i?t:null}}function i4(e){return!!e&&!Number.isNaN(Number(e))}function i5(e){if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}async function i8(e){let{req:t,sessionName:i,logPath:r,sessionStore:n}=e,a=t.command;if("snapshot"===a){let{session:e,device:a}=await i6(n,i,t.flags);if(!tZ("snapshot",a))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"snapshot is not supported on this device"}};let o=e?.appBundleId,s=t.flags?.snapshotScope;if(s&&s.trim().startsWith("@")){if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref scope requires an existing snapshot in session."}};let t=is(s.trim());if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref scope: ${s}`}};let i=il(e.snapshot.nodes,t),r=i?iu(i,e.snapshot.nodes):void 0;if(!r)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s} not found or has no label`}};s=r}return await i7(e,a,async()=>{let l=await tJ(a,"snapshot",[],t.flags?.out,{...t9(r,{...t.flags,snapshotScope:s},o,e?.trace?.outPath)}),c=l?.nodes??[],d=io(t.flags?.snapshotRaw?c:im(c)),u={nodes:d,truncated:l?.truncated,createdAt:Date.now(),backend:l?.backend},p=e?{...e,snapshot:u}:{name:i,device:a,createdAt:Date.now(),appBundleId:o,snapshot:u,actions:[]};return i9(n,p,t,{nodes:d.length,truncated:l?.truncated??!1}),n.set(i,p),{ok:!0,data:{nodes:d,truncated:l?.truncated??!1,appName:p.appBundleId?p.appName??p.appBundleId:void 0,appBundleId:p.appBundleId}}})}if("wait"===a){let{session:e,device:a}=await i6(n,i,t.flags),o=function(e){if(0===e.length)return null;let t=i5(e[0]);if(null!==t)return{kind:"sleep",durationMs:t};if("text"===e[0]){let t=i5(e[e.length-1]);return{kind:"text",text:(null!==t?e.slice(1,-1).join(" "):e.slice(1).join(" ")).trim(),timeoutMs:t}}if(e[0].startsWith("@")){let t=i5(e[e.length-1]);return{kind:"ref",rawRef:e[0],timeoutMs:t}}let i=i5(e[e.length-1]),r=iD(null!==i?e.slice(0,-1):e.slice());if(r&&0===r.rest.length){let e=iN(r.selectorExpression);if(e)return{kind:"selector",selector:e,selectorExpression:r.selectorExpression,timeoutMs:i}}return{kind:"text",text:(null!==i?e.slice(0,-1).join(" "):e.join(" ")).trim(),timeoutMs:i}}(t.positionals??[]);return o?"sleep"===o.kind?(await new Promise(e=>setTimeout(e,o.durationMs)),i9(n,e,t,{waitedMs:o.durationMs}),{ok:!0,data:{waitedMs:o.durationMs}}):tZ("wait",a)?await i7(e,a,async()=>{let s,l;if("selector"===o.kind){let s=o.timeoutMs??1e4,l=Date.now();for(;Date.now()-l<s;){let s=await tJ(a,"snapshot",[],t.flags?.out,{...t9(r,{...t.flags,snapshotInteractiveOnly:!1,snapshotCompact:!1},e?.appBundleId,e?.trace?.outPath)}),c=s?.nodes??[],d=io(t.flags?.snapshotRaw?c:im(c));e&&(e.snapshot={nodes:d,truncated:s?.truncated,createdAt:Date.now(),backend:s?.backend},n.set(i,e));let u=ib(d,o.selector,{platform:a.platform});if(u)return i9(n,e,t,{selector:u.selector.raw,waitedMs:Date.now()-l}),{ok:!0,data:{selector:u.selector.raw,waitedMs:Date.now()-l}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for selector: ${o.selectorExpression}`}}}if("ref"===o.kind){if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref wait requires an existing snapshot in session."}};let t=is(o.rawRef);if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref: ${o.rawRef}`}};let i=il(e.snapshot.nodes,t),r=i?iu(i,e.snapshot.nodes):void 0;if(!r)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${o.rawRef} not found or has no label`}};s=r,l=o.timeoutMs}else s=o.text,l=o.timeoutMs;if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires text"}};let c=l??1e4,d=Date.now();for(;Date.now()-d<c;){if("ios"===a.platform){let i=await tv(a,{command:"findText",text:s,appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});if(i?.found)return i9(n,e,t,{text:s,waitedMs:Date.now()-d}),{ok:!0,data:{text:s,waitedMs:Date.now()-d}}}else if("android"===a.platform&&id(io((await eO(a,{scope:s})).nodes??[]),s))return i9(n,e,t,{text:s,waitedMs:Date.now()-d}),{ok:!0,data:{text:s,waitedMs:Date.now()-d}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for text: ${s}`}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"wait is not supported on this device"}}:{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires a duration or text"}}}if("alert"===a){let{session:e,device:a}=await i6(n,i,t.flags),o=(t.positionals?.[0]??"get").toLowerCase();return tZ("alert",a)?await i7(e,a,async()=>{if("wait"===o){let i=i5(t.positionals?.[1])??1e4,o=Date.now();for(;Date.now()-o<i;){try{let i=await tv(a,{command:"alert",action:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});return i9(n,e,t,i),{ok:!0,data:i}}catch{}await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"alert wait timed out"}}}let i=await tv(a,{command:"alert",action:"accept"===o||"dismiss"===o?o:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath});return i9(n,e,t,i),{ok:!0,data:i}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"alert is only supported on iOS simulators"}}}if("settings"===a){let e=t.positionals?.[0],a=t.positionals?.[1];if(!e||!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"settings requires <wifi|airplane|location> <on|off> or faceid <match|nonmatch|enroll|unenroll>"}};let{session:o,device:s}=await i6(n,i,t.flags);return tZ("settings",s)?await i7(o,s,async()=>{let i=o?.appBundleId,l=await tJ(s,"settings",[e,a,i??""],t.flags?.out,{...t9(r,t.flags,i,o?.trace?.outPath)});return i9(n,o,t,l??{setting:e,state:a}),{ok:!0,data:l??{setting:e,state:a}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"settings is not supported on this device"}}}return null}async function i6(e,t,i){let r=e.get(t),n=r?.device??await tW(i??{});return r||await it(n),{session:r,device:n}}async function i7(e,t,i){let r=!e&&"ios"===t.platform;try{return await i()}finally{r&&await tN(t.id)}}function i9(e,t,i,r){t&&e.recordAction(t,{command:i.command,positionals:i.positionals??[],flags:i.flags??{},result:r})}function re(e,t,i,r={}){let n=ri(i);if(!n)return{matches:[],score:0};let a=0,o=[];for(let i of e){if(r.requireRect&&!i.rect)continue;let e=function(e,t,i){switch(t){case"role":return function(e,t){let i=function(e){let t=e.trim();return t?t=(t.split(".").pop()??t).replace(/XCUIElementType/gi,"").toLowerCase():""}(e??"");return i?i===t?2:+!!i.includes(t):0}(e.type,i);case"label":return rt(e.label,i);case"value":return rt(e.value,i);case"id":return rt(e.identifier,i);default:return Math.max(rt(e.label,i),rt(e.value,i),rt(e.identifier,i))}}(i,t,n);if(!(e<=0)){if(e>a){a=e,o.length=0,o.push(i);continue}e===a&&o.push(i)}}return{matches:o,score:a}}function rt(e,t){let i=ri(e??"");return i?i===t?2:+!!i.includes(t):0}function ri(e){return e.trim().toLowerCase().replace(/\s+/g," ")}async function rr(e){let{req:t,sessionName:i,logPath:r,sessionStore:n,invoke:a}=e,o=t.command;if("find"!==o)return null;let s=t.positionals??[];if(0===s.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a locator or text"}};let{locator:l,query:c,action:d,value:u,timeoutMs:p}=function(e){let t="any",i=0;["text","label","value","role","id"].includes(e[0])&&(t=e[0],i=1);let r=e[i]??"",n=e.slice(i+1);if(0===n.length)return{locator:t,query:r,action:"click"};let a=n[0].toLowerCase();if("get"===a){let e=n[1]?.toLowerCase();if("text"===e)return{locator:t,query:r,action:"get_text"};if("attrs"===e)return{locator:t,query:r,action:"get_attrs"};throw new I("INVALID_ARGS","find get only supports text or attrs")}if("wait"===a)return{locator:t,query:r,action:"wait",timeoutMs:i5(n[1])??void 0};if("exists"===a)return{locator:t,query:r,action:"exists"};if("click"===a)return{locator:t,query:r,action:"click"};if("focus"===a)return{locator:t,query:r,action:"focus"};if("fill"===a)return{locator:t,query:r,action:"fill",value:n.slice(1).join(" ")};if("type"===a)return{locator:t,query:r,action:"type",value:n.slice(1).join(" ")};throw new I("INVALID_ARGS",`Unsupported find action: ${n[0]}`)}(s);if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a value"}};let f=n.get(i);if(!f&&"exists"!==d&&"wait"!==d&&"get_text"!==d&&"get_attrs"!==d)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let m=f?.device??await tW(t.flags??{});f||await it(m);let h=f?.appBundleId,w="role"!==l?c:void 0,g="click"===d||"focus"===d||"fill"===d||"type"===d,v=0,A=null,y=async()=>{let e=Date.now();if(A&&e-v<750)return{nodes:A};let a=await tJ(m,"snapshot",[],t.flags?.out,{...t9(r,{...t.flags,snapshotScope:w,snapshotInteractiveOnly:g,snapshotCompact:g},h,f?.trace?.outPath)}),o=a?.nodes??[],s=io(t.flags?.snapshotRaw?o:im(o));return v=e,A=s,f&&(f.snapshot={nodes:s,truncated:a?.truncated,createdAt:Date.now(),backend:a?.backend},n.set(i,f)),{nodes:s,truncated:a?.truncated,backend:a?.backend}};if("wait"===d){let e=p??1e4,i=Date.now();for(;Date.now()-i<e;){let{nodes:e}=await y();if(re(e,l,c,{requireRect:!1}).matches[0])return f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0,waitedMs:Date.now()-i}}),{ok:!0,data:{found:!0,waitedMs:Date.now()-i}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"find wait timed out"}}}let{nodes:N}=await y(),S=re(N,l,c,{requireRect:g});if(g&&S.matches.length>1){let e=S.matches.slice(0,8).map(e=>{let t=ig(e)||e.label||e.identifier||e.type||"";return`@${e.ref}${t?`(${t})`:""}`});return{ok:!1,error:{code:"AMBIGUOUS_MATCH",message:`find matched ${S.matches.length} elements for ${l} "${c}". Use a more specific locator or selector.`,details:{locator:l,query:c,matches:S.matches.length,candidates:e}}}}let b=S.matches[0]??null;if(!b)return{ok:!1,error:{code:"COMMAND_FAILED",message:"find did not match any element"}};let _="click"===d||"focus"===d||"fill"===d||"type"===d?function(e,t){if(t.hittable)return t;let i=t,r=new Set;for(;void 0!==i.parentIndex&&!r.has(i.ref);){r.add(i.ref);let t=e[i.parentIndex];if(!t)break;if(t.hittable)return t;i=t}return null}(N,b)??b:b,D=`@${_.ref}`,O={...t.flags??{},noRecord:!0};if("exists"===d)return f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0}}),{ok:!0,data:{found:!0}};if("get_text"===d){let e=ig(b);return f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"get text",text:e}}),{ok:!0,data:{ref:D,text:e,node:b}}}if("get_attrs"===d)return f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"get attrs"}}),{ok:!0,data:{ref:D,node:b}};if("click"===d){let e=await a({token:t.token,session:i,command:"click",positionals:[D],flags:O});return e.ok&&f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"click"}}),e}if("fill"===d){if(!u)return{ok:!1,error:{code:"INVALID_ARGS",message:"find fill requires text"}};let e=await a({token:t.token,session:i,command:"fill",positionals:[D,u],flags:O});return e.ok&&f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"fill"}}),e}if("focus"===d){let e=b.rect?ic(b.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};let i=await tJ(m,"focus",[String(e.x),String(e.y)],t.flags?.out,{...t9(r,t.flags,f?.appBundleId,f?.trace?.outPath)});return f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"focus"}}),{ok:!0,data:i??{ref:D}}}if("type"===d){if(!u)return{ok:!1,error:{code:"INVALID_ARGS",message:"find type requires text"}};let e=b.rect?ic(b.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};await tJ(m,"focus",[String(e.x),String(e.y)],t.flags?.out,{...t9(r,t.flags,f?.appBundleId,f?.trace?.outPath)});let i=await tJ(m,"type",[u],t.flags?.out,{...t9(r,t.flags,f?.appBundleId,f?.trace?.outPath)});return f&&n.recordAction(f,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"type"}}),{ok:!0,data:i??{ref:D}}}return null}async function rn(e){let{req:t,sessionName:r,sessionStore:n}=e,a=t.command;if("record"===a){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"record requires start|stop"}};let o=n.get(r),s=o?.device??await tW(t.flags??{});o||await it(s);let c=o??{name:r,device:s,createdAt:Date.now(),actions:[]};if("start"===e){if(c.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"recording already in progress"}};let e=t.positionals?.[1]??`./recording-${Date.now()}.mp4`,o=i.resolve(e),d=i.dirname(o);if(A.existsSync(d)||A.mkdirSync(d,{recursive:!0}),!tZ("record",s))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"record is only supported on iOS simulators"}};if("ios"===s.platform){let{child:e,wait:t}=l("xcrun",["simctl","io",s.id,"recordVideo",o],{allowFailure:!0});c.recording={platform:"ios",outPath:o,child:e,wait:t}}else{let e=`/sdcard/agent-device-recording-${Date.now()}.mp4`,{child:t,wait:i}=l("adb",["-s",s.id,"shell","screenrecord",e],{allowFailure:!0});c.recording={platform:"android",outPath:o,remotePath:e,child:t,wait:i}}return n.set(r,c),n.recordAction(c,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start"}}),{ok:!0,data:{recording:"started",outPath:e}}}if(!c.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active recording"}};let d=c.recording;d.child.kill("SIGINT");try{await d.wait}catch{}if("android"===d.platform&&d.remotePath)try{await u("adb",["-s",s.id,"pull",d.remotePath,d.outPath],{allowFailure:!0}),await u("adb",["-s",s.id,"shell","rm","-f",d.remotePath],{allowFailure:!0})}catch{}return c.recording=void 0,n.recordAction(c,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:d.outPath}}),{ok:!0,data:{recording:"stopped",outPath:d.outPath}}}if("trace"===a){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"trace requires start|stop"}};let o=n.get(r);if(!o)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}};if("start"===e){if(o.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"trace already in progress"}};let e=t.positionals?.[1]??n.defaultTracePath(o),r=t7.expandHome(e);return A.mkdirSync(i.dirname(r),{recursive:!0}),A.appendFileSync(r,""),o.trace={outPath:r,startedAt:Date.now()},n.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start",outPath:r}}),{ok:!0,data:{trace:"started",outPath:r}}}if(!o.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active trace"}};let s=o.trace.outPath;if(t.positionals?.[1]){let e=t7.expandHome(t.positionals[1]);A.mkdirSync(i.dirname(e),{recursive:!0}),A.existsSync(s)?A.renameSync(s,e):A.appendFileSync(e,""),s=e}return o.trace=void 0,n.recordAction(o,{command:a,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:s}}),{ok:!0,data:{trace:"stopped",outPath:s}}}return null}async function ra(e){let{req:t,sessionName:i,sessionStore:r,contextFromFlags:n}=e,a=e.dispatch??tJ,o=t.command;if("press"===o){let e=r.get(i);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=function(e){if(e.length<2)return null;let t=Number(e[0]),i=Number(e[1]);return Number.isFinite(t)&&Number.isFinite(i)?{x:t,y:i}:null}(t.positionals??[]);if(s){let i=await a(e.device,"press",[String(s.x),String(s.y)],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)});return r.recordAction(e,{command:o,positionals:t.positionals??[String(s.x),String(s.y)],flags:t.flags??{},result:i??{x:s.x,y:s.y}}),{ok:!0,data:i??{x:s.x,y:s.y}}}let l="click",c=t.positionals?.[0]??"";if(c.startsWith("@")){let i=rl("press",t.flags);if(i)return i;if(!e.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let s=is(c);if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:`${o} requires a ref like @e2`}};let d=il(e.snapshot.nodes,s);if(!d?.rect&&t.positionals.length>1){let i=t.positionals.slice(1).join(" ").trim();i.length>0&&(d=id(e.snapshot.nodes,i))}if(!d?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${c} not found or has no bounds`}};let u=iu(d,e.snapshot.nodes),p=ix(d,e.device.platform,{action:l}),{x:f,y:m}=ic(d.rect),h=await a(e.device,"press",[String(f),String(m)],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)});return r.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:s,x:f,y:m,refLabel:u,selectorChain:p}}),{ok:!0,data:{...h??{},ref:s,x:f,y:m}}}let d=(t.positionals??[]).join(" ").trim();if(!d)return{ok:!1,error:{code:"INVALID_ARGS",message:`${o} requires @ref, selector expression, or x y coordinates`}};let u=iy(d),f=await ro(e,t.flags,r,n,{interactiveOnly:!0},a),m=await p("selector_resolve",()=>iS(f.nodes,u,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0}),{command:o});if(!m||!m.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:i_(u,m?.diagnostics??[],{unique:!0})}};let{x:h,y:w}=ic(m.node.rect),g=await a(e.device,"press",[String(h),String(w)],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)}),v=ix(m.node,e.device.platform,{action:l}),I=iu(m.node,f.nodes);return r.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{x:h,y:w,selector:m.selector.raw,selectorChain:v,refLabel:I}}),{ok:!0,data:{...g??{},selector:m.selector.raw,x:h,y:w}}}if("fill"===o){let e=r.get(i);if(t.positionals?.[0]?.startsWith("@")){let i=rl("fill",t.flags);if(i)return i;if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let s=is(t.positionals[0]);if(!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires a ref like @e2"}};let l=t.positionals.length>=3?t.positionals[1]:"",c=t.positionals.length>=3?t.positionals.slice(2).join(" "):t.positionals.slice(1).join(" ");if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after ref"}};let d=il(e.snapshot.nodes,s);if(!d?.rect&&l&&(d=id(e.snapshot.nodes,l)),!d?.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${t.positionals[0]} not found or has no bounds`}};let u=d.type??"",p=u&&!iw(u,e.device.platform)?`fill target ${t.positionals[0]} resolved to "${u}", attempting fill anyway.`:void 0,f=iu(d,e.snapshot.nodes),m=ix(d,e.device.platform,{action:"fill"}),{x:h,y:w}=ic(d.rect),g={...await a(e.device,"fill",[String(h),String(w),c],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)})??{ref:s,x:h,y:w}};return p&&(g.warning=p),r.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{...g,refLabel:f,selectorChain:m}}),{ok:!0,data:g}}if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=iD(t.positionals??[],{preferTrailingValue:!0});if(s){if(0===s.rest.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let i=s.rest.join(" ").trim();if(!i)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let l=iy(s.selectorExpression),c=await ro(e,t.flags,r,n,{interactiveOnly:!0},a),d=await p("selector_resolve",()=>iS(c.nodes,l,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0}),{command:o});if(!d||!d.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:i_(l,d?.diagnostics??[],{unique:!0})}};let u=d.node,f=u.type??"",m=f&&!iw(f,e.device.platform)?`fill target ${d.selector.raw} resolved to "${f}", attempting fill anyway.`:void 0,{x:h,y:w}=ic(d.node.rect),g=await a(e.device,"fill",[String(h),String(w),i],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)}),v=ix(u,e.device.platform,{action:"fill"}),I={...g??{x:h,y:w,text:i},selector:d.selector.raw,selectorChain:v,refLabel:iu(u,c.nodes)};return m&&(I.warning=m),r.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:I}),{ok:!0,data:I}}return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires x y text, @ref text, or selector text"}}}if("get"===o){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)return{ok:!1,error:{code:"INVALID_ARGS",message:"get only supports text or attrs"}};let s=r.get(i);if(!s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let l=t.positionals?.[1]??"";if(l.startsWith("@")){let i=rl("get",t.flags);if(i)return i;if(!s.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}};let n=is(l??"");if(!n)return{ok:!1,error:{code:"INVALID_ARGS",message:"get text requires a ref like @e2"}};let a=il(s.snapshot.nodes,n);if(!a&&t.positionals.length>2){let e=t.positionals.slice(2).join(" ").trim();e.length>0&&(a=id(s.snapshot.nodes,e))}if(!a)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${l} not found`}};let c=ix(a,s.device.platform,{action:"get"});if("attrs"===e)return r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:n,selectorChain:c}}),{ok:!0,data:{ref:n,node:a}};let d=ig(a);return r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:n,text:d,refLabel:d||void 0,selectorChain:c}}),{ok:!0,data:{ref:n,text:d,node:a}}}let c=t.positionals.slice(1).join(" ").trim();if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"get requires @ref or selector expression"}};let d=iy(c),u=await ro(s,t.flags,r,n,{interactiveOnly:!1},a),f=await p("selector_resolve",()=>iS(u.nodes,d,{platform:s.device.platform,requireRect:!1,requireUnique:!0,disambiguateAmbiguous:"text"===e}),{command:o});if(!f)return{ok:!1,error:{code:"COMMAND_FAILED",message:i_(d,[],{unique:!0})}};let m=f.node,h=ix(m,s.device.platform,{action:"get"});if("attrs"===e)return r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{selector:f.selector.raw,selectorChain:h}}),{ok:!0,data:{selector:f.selector.raw,node:m}};let w=ig(m);return r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{text:w,refLabel:w||void 0,selector:f.selector.raw,selectorChain:h}}),{ok:!0,data:{selector:f.selector.raw,text:w,node:m}}}if("is"===o){let e=(t.positionals?.[0]??"").toLowerCase();if(!["visible","hidden","exists","editable","selected","text"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires predicate: visible|hidden|exists|editable|selected|text"}};let s=r.get(i);if(!s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!tZ("is",s.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"is is not supported on this device"}};let{split:l}=iO(t.positionals);if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires a selector expression"}};let c=l.rest.join(" ").trim();if("text"===e&&!c)return{ok:!1,error:{code:"INVALID_ARGS",message:"is text requires expected text value"}};if("text"!==e&&l.rest.length>0)return{ok:!1,error:{code:"INVALID_ARGS",message:`is ${e} does not accept trailing values`}};let d=iy(l.selectorExpression),u=await ro(s,t.flags,r,n,{interactiveOnly:!1},a);if("exists"===e){let i=ib(u.nodes,d,{platform:s.device.platform});return i?(r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:i.selector.raw,selectorChain:d.selectors.map(e=>e.raw),pass:!0,matches:i.matches}}),{ok:!0,data:{predicate:e,pass:!0,selector:i.selector.raw,matches:i.matches}}):{ok:!1,error:{code:"COMMAND_FAILED",message:i_(d,[],{unique:!1})}}}let f=await p("selector_resolve",()=>iS(u.nodes,d,{platform:s.device.platform,requireUnique:!0}),{command:"is",predicate:e});if(!f)return{ok:!1,error:{code:"COMMAND_FAILED",message:i_(d,[],{unique:!0})}};let m=function(e){let{predicate:t,node:i,expectedText:r,platform:n}=e,a=ig(i),o=!1;switch(t){case"visible":o=iM(i);break;case"hidden":o=!iM(i);break;case"editable":o=iE(i,n);break;case"selected":o=!0===i.selected;break;case"text":o=a===(r??"")}let s="text"===t?`expected="${r??""}" actual="${a}"`:`actual=${JSON.stringify({visible:iM(i),editable:iE(i,n),selected:!0===i.selected})}`;return{pass:o,actualText:a,details:s}}({predicate:e,node:f.node,expectedText:c,platform:s.device.platform});return m.pass?(r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:f.selector.raw,selectorChain:d.selectors.map(e=>e.raw),pass:!0,text:"text"===e?m.actualText:void 0}}),{ok:!0,data:{predicate:e,pass:!0,selector:f.selector.raw}}):{ok:!1,error:{code:"COMMAND_FAILED",message:`is ${e} failed for selector ${f.selector.raw}: ${m.details}`}}}return null}async function ro(e,t,i,r,n,a=tJ){let o=await a(e.device,"snapshot",[],t?.out,{...r({...t??{},snapshotInteractiveOnly:n.interactiveOnly,snapshotCompact:n.interactiveOnly},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[];return e.snapshot={nodes:io(t?.snapshotRaw?s:im(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend},i.set(e.name,e),e.snapshot}let rs=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"]];function rl(e,t){let i=function(e){if(!e)return[];let t=[];for(let[i,r]of rs)void 0!==e[i]&&t.push(r);return t}(t);return 0===i.length?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} @ref does not support ${i.join(", ")}.`}}}let rc=i.join(S.homedir(),".agent-device"),rd=i.join(rc,"daemon.json"),ru=i.join(rc,"daemon.lock"),rp=i.join(rc,"daemon.log"),rf=new t7(i.join(rc,"sessions")),rm=f(),rh=c.randomBytes(24).toString("hex"),rw=new Set(["session_list","devices"]),rg=b(process.pid)??void 0;function rv(e,t,i){return t9(rp,e,t,i)}async function rI(e){let t=!!(e.meta?.debug||e.flags?.verbose);return await y({session:e.session,requestId:e.meta?.requestId,command:e.command,debug:t,logPath:rp},async()=>{if(e.token!==rh)return{ok:!1,error:a(new I("UNAUTHORIZED","Invalid token"))};m({level:"info",phase:"request_start",data:{session:e.session,command:e.command}});try{var t;let i=(t=e,"click"!==t.command?t:{...t,command:"press"}),r=i.command,n=function(e,t){var i;let r,n=e.session||"default";if(i=e,r=i.flags?.session,"string"==typeof r&&r.trim().length>0||"default"!==n||t.has(n))return n;let a=t.toArray();return 1===a.length?a[0].name:n}(i,rf),a=rf.get(n);a&&!rw.has(r)&&function(e,t){if(!t)return;let i=[],r=e.device;if(t.platform&&t.platform!==r.platform&&i.push(`--platform=${t.platform}`),t.udid&&("ios"!==r.platform||t.udid!==r.id)&&i.push(`--udid=${t.udid}`),t.serial&&("android"!==r.platform||t.serial!==r.id)&&i.push(`--serial=${t.serial}`),t.device&&t.device.trim().toLowerCase()!==r.name.trim().toLowerCase()&&i.push(`--device=${t.device}`),0!==i.length){var n;let t,r,a;throw new I("INVALID_ARGS",`Session "${e.name}" is bound to ${(t=(n=e).device.platform,r=n.device.name.trim(),a=n.device.id,`${t} device "${r}" (${a})`)} and cannot be used with ${i.join(", ")}. Use a different --session name or close this session first.`)}}(a,i.flags);let o=await iY({req:i,sessionName:n,logPath:rp,sessionStore:rf,invoke:rI});if(o)return rA(o);let s=await i8({req:i,sessionName:n,logPath:rp,sessionStore:rf});if(s)return rA(s);let l=await rn({req:e,sessionName:n,sessionStore:rf});if(l)return rA(l);let c=await rr({req:i,sessionName:n,logPath:rp,sessionStore:rf,invoke:rI});if(c)return rA(c);let d=await ra({req:i,sessionName:n,sessionStore:rf,contextFromFlags:rv});if(d)return rA(d);let u=rf.get(n);if(!u)return rA({ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}});if(!tZ(r,u.device))return rA({ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:`${r} is not supported on this device`}});let p=await tJ(u.device,r,i.positionals??[],i.flags?.out,{...rv(i.flags,u.appBundleId,u.trace?.outPath)});return rf.recordAction(u,{command:r,positionals:i.positionals??[],flags:i.flags??{},result:p??{}}),rA({ok:!0,data:p??{}})}catch(i){m({level:"error",phase:"request_failed",data:{error:i instanceof Error?i.message:String(i)}});let e=d(),t=N({force:!0})??void 0;return{ok:!1,error:a(i,{diagnosticId:e.diagnosticId,logPath:t})}}})}function rA(e){let t=d();if(!e.ok){m({level:"error",phase:"request_failed",data:{code:e.error.code,message:e.error.message}});let i=N({force:!0})??void 0;return{ok:!1,error:a(new I(e.error.code,e.error.message,{...e.error.details??{},hint:e.error.hint,diagnosticId:e.error.diagnosticId,logPath:e.error.logPath}),{diagnosticId:t.diagnosticId,logPath:i})}}return m({level:"info",phase:"request_success"}),N(),e}function ry(){if(!A.existsSync(ru))return null;try{let e=JSON.parse(A.readFileSync(ru,"utf8"));if(!Number.isInteger(e.pid)||e.pid<=0)return null;return e}catch{return null}}!function(){if(!function(){A.existsSync(rc)||A.mkdirSync(rc,{recursive:!0});let e=JSON.stringify({pid:process.pid,version:rm,startedAt:Date.now(),processStartTime:rg},null,2),t=()=>{try{return A.writeFileSync(ru,e,{flag:"wx",mode:384}),!0}catch(e){if("EEXIST"===e.code)return!1;throw e}};if(t())return!0;let i=ry();if(i?.pid&&i.pid!==process.pid&&o(i.pid,i.processStartTime))return!1;try{A.unlinkSync(ru)}catch{}return t()}()){process.stderr.write("Daemon lock is held by another process; exiting.\n"),process.exit(0);return}let e=_.createServer(e=>{let t="";e.setEncoding("utf8"),e.on("data",async i=>{let r=(t+=i).indexOf("\n");for(;-1!==r;){let i,n=t.slice(0,r).trim();if(t=t.slice(r+1),0===n.length){r=t.indexOf("\n");continue}try{let e=JSON.parse(n);i=await rI(e)}catch(e){i={ok:!1,error:a(e)}}e.write(`${JSON.stringify(i)}
|
|
16
|
-
`),
|
|
17
|
-
`)}});let t=!1,i=async()=>{await new Promise(t=>{try{e.close(()=>t())}catch{t()}})},r=async()=>{if(t)return;for(let e of(t=!0,await i(),
|
|
12
|
+
${i}`.toLowerCase()).includes("device is busy")&&r.includes("connecting")?"Target iOS device is still connecting. Keep it unlocked, wait for device trust/connection to settle, then retry.":q("IOS_RUNNER_CONNECT_TIMEOUT"))})}async function tJ(e,t,i,r,n=tw,a){let o=$.fromTimeoutMs(n),s=await tH(e,t,o.remainingMs()),l=null,d=Math.max(1,Math.ceil(n/tv));try{return await F(async({deadline:o})=>{if(o?.isExpired())throw new c("COMMAND_FAILED","Runner connection deadline exceeded",{port:t,timeoutMs:n});if(a&&null!==a.child.exitCode&&void 0!==a.child.exitCode)throw await tW({session:a,port:t,logPath:r});for(let r of("device"===e.kind&&(s=await tH(e,t,o?.remainingMs())),s))try{let e=o?.remainingMs()??n;if(e<=0)throw new c("COMMAND_FAILED","Runner connection deadline exceeded",{port:t,timeoutMs:n});return await tz(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)},Math.min(tA,e))}catch(e){l=e}throw new c("COMMAND_FAILED","Runner endpoint probe failed",{port:t,endpoints:s,lastError:l?String(l):void 0})},{maxAttempts:d,baseDelayMs:tI,maxDelayMs:ty,jitter:.2,shouldRetry:tj},{deadline:o,phase:"ios_runner_connect"})}catch(e){l||(l=e)}if("simulator"===e.kind){let n=o.remainingMs();if(n<=0)throw tB({port:t,endpoints:s,logPath:r,lastError:l});let a=await tX(e.id,t,i,n);return new Response(a.body,{status:a.status})}throw tB({port:t,endpoints:s,logPath:r,lastError:l})}async function tH(e,t,i){let r=[`http://127.0.0.1:${t}/command`];if("device"!==e.kind)return r;let n=await tY(e.id,i);return n&&r.unshift(`http://[${n}]:${t}/command`),r}async function tz(e,t,i){let r=new AbortController,n=setTimeout(()=>r.abort(),i);try{return await fetch(e,{...t,signal:r.signal})}finally{clearTimeout(n)}}async function tY(e,t){if("number"==typeof t&&t<=0)return null;let r="number"==typeof t?Math.max(1,Math.min(tN,t)):tN,n=i.join(O.tmpdir(),`agent-device-devicectl-info-${process.pid}-${Date.now()}.json`);try{let t=Math.max(1,Math.ceil(r/1e3)),i=await s("xcrun",["devicectl","device","info","details","--device",e,"--json-output",n,"--timeout",String(t)],{allowFailure:!0,timeoutMs:r});if(0!==i.exitCode||!_.existsSync(n))return null;let a=JSON.parse(_.readFileSync(n,"utf8"));if(a.info?.outcome&&"success"!==a.info.outcome)return null;let o=(a.result?.connectionProperties?.tunnelIPAddress??a.result?.device?.connectionProperties?.tunnelIPAddress)?.trim();return o&&o.length>0?o:null}catch{return null}finally{tQ(n)}}async function tX(e,t,i,r){let n=JSON.stringify(i),a=await s("xcrun",["simctl","spawn",e,"/usr/bin/curl","-s","-X","POST","-H","Content-Type: application/json","--data",n,`http://127.0.0.1:${t}/command`],{allowFailure:!0,timeoutMs:r}),o=a.stdout;if(0!==a.exitCode){let e=G({message:"Runner did not accept connection (simctl spawn)",stdout:a.stdout,stderr:a.stderr,context:{platform:"ios",phase:"connect"}});throw new c("COMMAND_FAILED","Runner did not accept connection (simctl spawn)",{port:t,stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode,reason:e,hint:q(e)})}return{status:200,body:o}}async function tK(){return await new Promise((e,t)=>{let i=x.createServer();i.listen(0,"127.0.0.1",()=>{let r=i.address();i.close(),"object"==typeof r&&r?.port?e(r.port):t(new c("COMMAND_FAILED","Failed to allocate port"))}),i.on("error",t)})}async function tZ(e,t,r){let n,a=i.dirname(e),o=r.replace(/[^a-zA-Z0-9._-]/g,"_"),l=i.join(a,`AgentDeviceRunner.env.${o}.json`),d=i.join(a,`AgentDeviceRunner.env.${o}.xctestrun`),u=await s("plutil",["-convert","json","-o","-",e],{allowFailure:!0});if(0!==u.exitCode||!u.stdout.trim())throw new c("COMMAND_FAILED","Failed to read xctestrun plist",{xctestrunPath:e,stderr:u.stderr});try{n=JSON.parse(u.stdout)}catch(t){throw new c("COMMAND_FAILED","Failed to parse xctestrun JSON",{xctestrunPath:e,error:String(t)})}let p=e=>{e.EnvironmentVariables={...e.EnvironmentVariables??{},...t},e.UITestEnvironmentVariables={...e.UITestEnvironmentVariables??{},...t},e.UITargetAppEnvironmentVariables={...e.UITargetAppEnvironmentVariables??{},...t},e.TestingEnvironmentVariables={...e.TestingEnvironmentVariables??{},...t}},f=n.TestConfigurations;if(Array.isArray(f))for(let e of f){if(!e||"object"!=typeof e)continue;let t=e.TestTargets;if(Array.isArray(t))for(let e of t)e&&"object"==typeof e&&p(e)}for(let[e,t]of Object.entries(n))t&&"object"==typeof t&&t.TestBundlePath&&(p(t),n[e]=t);_.writeFileSync(l,JSON.stringify(n,null,2));let m=await s("plutil",["-convert","xml1","-o",d,l],{allowFailure:!0});if(0!==m.exitCode)throw new c("COMMAND_FAILED","Failed to write xctestrun plist",{tmpXctestrunPath:d,stderr:m.stderr});return{xctestrunPath:d,jsonPath:l}}function tQ(e){try{_.existsSync(e)&&_.unlinkSync(e)}catch{}}async function t0(e){return await I("resolve_target_device",async()=>{let t={platform:e.platform,deviceName:e.device,udid:e.udid,serial:e.serial};if("android"===t.platform){await eC();let e=await J();return await E(e,t)}if("ios"===t.platform){let e=await eW();return await E(e,t)}let i=[];try{i.push(...await J())}catch{}try{i.push(...await eW())}catch{}return await E(i,t)},{platform:e.platform})}async function t1(e,t,r,n,a){let o=function(e,t){switch(e.platform){case"android":return{open:(t,i)=>eu(e,t,i?.activity),openDevice:()=>ef(e),close:t=>em(e,t),tap:(t,i)=>ev(e,t,i),doubleTap:async(t,i)=>{await ev(e,t,i),await ev(e,t,i)},swipe:(t,i,r,n,a)=>eI(e,t,i,r,n,a),longPress:(t,i,r)=>eS(e,t,i,r),focus:(t,i)=>e_(e,t,i),type:t=>eb(e,t),fill:(t,i,r)=>eD(e,t,i,r),scroll:(t,i)=>eM(e,t,i),scrollIntoView:t=>eO(e,t),screenshot:t=>ex(e,t)};case"ios":var i,r;let n,a;return{open:(t,i)=>e5(e,t,{appBundleId:i?.appBundleId,url:i?.url}),openDevice:()=>e8(e),close:t=>e6(e,t),screenshot:t=>tt(e,t),...(i=e,n={verbose:(r=t).verbose,logPath:r.logPath,traceLogPath:r.traceLogPath,requestId:r.requestId},a=()=>{if(tp(r.requestId))throw new c("COMMAND_FAILED","request canceled")},{tap:async(e,t)=>{await t_(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n)},doubleTap:async(e,t)=>{await t_(i,{command:"tapSeries",x:e,y:t,count:1,intervalMs:0,doubleTap:!0,appBundleId:r.appBundleId},n)},swipe:async(e,t,a,o,s)=>{await t_(i,{command:"drag",x:e,y:t,x2:a,y2:o,durationMs:s,appBundleId:r.appBundleId},n)},longPress:async(e,t,a)=>{await t_(i,{command:"longPress",x:e,y:t,durationMs:a,appBundleId:r.appBundleId},n)},focus:async(e,t)=>{await t_(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n)},type:async e=>{await t_(i,{command:"type",text:e,appBundleId:r.appBundleId},n)},fill:async(e,t,a)=>{await t_(i,{command:"tap",x:e,y:t,appBundleId:r.appBundleId},n),await t_(i,{command:"type",text:a,clearFirst:!0,appBundleId:r.appBundleId},n)},scroll:async(e,t)=>{if(!["up","down","left","right"].includes(e))throw new c("INVALID_ARGS",`Unknown direction: ${e}`);let a=function(e){switch(e){case"up":return"down";case"down":return"up";case"left":return"right";case"right":return"left"}}(e);await t_(i,{command:"swipe",direction:a,appBundleId:r.appBundleId},n)},scrollIntoView:async e=>{let t=await t_(i,{command:"findText",text:e,appBundleId:r.appBundleId},n);if(t?.found)return{attempts:1};for(let t=0;t<12;t+=1){for(let e=0;e<4;e+=1)a(),await t_(i,{command:"swipe",direction:"up",appBundleId:r.appBundleId},n),await new Promise(e=>setTimeout(e,80));a();let o=await t_(i,{command:"findText",text:e,appBundleId:r.appBundleId},n);if(o?.found)return{attempts:t+2}}throw new c("COMMAND_FAILED",`scrollintoview could not find text: ${e}`)}})};default:throw new c("UNSUPPORTED_PLATFORM",`Unsupported platform: ${e.platform}`)}}(e,{requestId:a?.requestId,appBundleId:a?.appBundleId,verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath});return y({level:"debug",phase:"platform_command_prepare",data:{command:t,platform:e.platform,kind:e.kind}}),await I("platform_command",async()=>{var s,l,d,u,p,f;switch(t){case"open":{let t=r[0],i=r[1];if(r.length>2)throw new c("INVALID_ARGS","open accepts at most two arguments: <app|url> [url]");if(!t)return await o.openDevice(),{app:null};if(void 0!==i){if("ios"!==e.platform)throw new c("INVALID_ARGS","open <app> <url> is supported only on iOS");if(Y(t))throw new c("INVALID_ARGS","open <app> <url> requires an app target as the first argument");if(!Y(i))throw new c("INVALID_ARGS","open <app> <url> requires a valid URL target");return await o.open(t,{activity:a?.activity,appBundleId:a?.appBundleId,url:i}),{app:t,url:i}}return await o.open(t,{activity:a?.activity,appBundleId:a?.appBundleId}),{app:t}}case"close":{let e=r[0];if(!e)return{closed:"session"};return await o.close(e),{app:e}}case"press":{let[t,i]=r.map(Number);if(Number.isNaN(t)||Number.isNaN(i))throw new c("INVALID_ARGS","press requires x y");let n=t3(a?.count??1,"count",1,200),p=t3(a?.intervalMs??0,"interval-ms",0,1e4),f=t3(a?.holdMs??0,"hold-ms",0,1e4),m=t3(a?.jitterPx??0,"jitter-px",0,100),h=a?.doubleTap===!0;if(h&&f>0)throw new c("INVALID_ARGS","double-tap cannot be combined with hold-ms");if(h&&m>0)throw new c("INVALID_ARGS","double-tap cannot be combined with jitter-px");if(s=e,l=n,d=f,u=m,"ios"===s.platform&&l>1&&0===d&&0===u)return await t_(e,{command:"tapSeries",x:t,y:i,count:n,intervalMs:p,doubleTap:h,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath,requestId:a?.requestId}),{x:t,y:i,count:n,intervalMs:p,holdMs:f,jitterPx:m,doubleTap:h,timingMode:"runner-series"};return await t4(n,p,async e=>{let[r,n]=function(e,t){if(t<=0)return[0,0];let[i,r]=t2[e%t2.length];return[i*t,r*t]}(e,m),a=t+r,s=i+n;h?await o.doubleTap(a,s):f>0?await o.longPress(a,s,f):await o.tap(a,s)}),{x:t,y:i,count:n,intervalMs:p,holdMs:f,jitterPx:m,doubleTap:h}}case"swipe":{let t=Number(r[0]),i=Number(r[1]),n=Number(r[2]),s=Number(r[3]);if([t,i,n,s].some(Number.isNaN))throw new c("INVALID_ARGS","swipe requires x1 y1 x2 y2 [durationMs]");let l=t3(r[4]?Number(r[4]):250,"durationMs",16,1e4),d="ios"===e.platform?Math.min(60,Math.max(16,Math.round(l))):l,u=t3(a?.count??1,"count",1,200),m=t3(a?.pauseMs??0,"pause-ms",0,1e4),h=a?.pattern??"one-way";if("one-way"!==h&&"ping-pong"!==h)throw new c("INVALID_ARGS",`Invalid pattern: ${h}`);if(p=e,f=u,"ios"===p.platform&&f>1)return await t_(e,{command:"dragSeries",x:t,y:i,x2:n,y2:s,durationMs:d,count:u,pauseMs:m,pattern:h,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath,requestId:a?.requestId}),{x1:t,y1:i,x2:n,y2:s,durationMs:l,effectiveDurationMs:d,timingMode:"runner-series",count:u,pauseMs:m,pattern:h};return await t4(u,m,async e=>{"ping-pong"===h&&e%2==1?await o.swipe(n,s,t,i,d):await o.swipe(t,i,n,s,d)}),{x1:t,y1:i,x2:n,y2:s,durationMs:l,effectiveDurationMs:d,timingMode:"ios"===e.platform?"safe-normalized":"direct",count:u,pauseMs:m,pattern:h}}case"longpress":{let e=Number(r[0]),t=Number(r[1]),i=r[2]?Number(r[2]):void 0;if(Number.isNaN(e)||Number.isNaN(t))throw new c("INVALID_ARGS","longpress requires x y [durationMs]");return await o.longPress(e,t,i),{x:e,y:t,durationMs:i}}case"focus":{let[e,t]=r.map(Number);if(Number.isNaN(e)||Number.isNaN(t))throw new c("INVALID_ARGS","focus requires x y");return await o.focus(e,t),{x:e,y:t}}case"type":{let e=r.join(" ");if(!e)throw new c("INVALID_ARGS","type requires text");return await o.type(e),{text:e}}case"fill":{let e=Number(r[0]),t=Number(r[1]),i=r.slice(2).join(" ");if(Number.isNaN(e)||Number.isNaN(t)||!i)throw new c("INVALID_ARGS","fill requires x y text");return await o.fill(e,t,i),{x:e,y:t,text:i}}case"scroll":{let e=r[0],t=r[1]?Number(r[1]):void 0;if(!e)throw new c("INVALID_ARGS","scroll requires direction");return await o.scroll(e,t),{direction:e,amount:t}}case"scrollintoview":{let e=r.join(" ").trim();if(!e)throw new c("INVALID_ARGS","scrollintoview requires text");let t=await o.scrollIntoView(e);if(t?.attempts)return{text:e,attempts:t.attempts};return{text:e}}case"pinch":{if("android"===e.platform)throw new c("UNSUPPORTED_OPERATION","Android pinch is not supported in current adb backend; requires instrumentation-based backend.");let t=Number(r[0]),i=r[1]?Number(r[1]):void 0,n=r[2]?Number(r[2]):void 0;if(Number.isNaN(t)||t<=0)throw new c("INVALID_ARGS","pinch requires scale > 0");return await t_(e,{command:"pinch",scale:t,x:i,y:n,appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath,requestId:a?.requestId}),{scale:t,x:i,y:n}}case"screenshot":{let e=r[0]??n??`./screenshot-${Date.now()}.png`;return await A.mkdir(i.dirname(e),{recursive:!0}),await o.screenshot(e),{path:e}}case"back":if("ios"===e.platform)return await t_(e,{command:"back",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath,requestId:a?.requestId}),{action:"back"};return await ey(e),{action:"back"};case"home":if("ios"===e.platform)return await t_(e,{command:"home",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath,requestId:a?.requestId}),{action:"home"};return await eA(e),{action:"home"};case"app-switcher":if("ios"===e.platform)return await t_(e,{command:"appSwitcher",appBundleId:a?.appBundleId},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath,requestId:a?.requestId}),{action:"app-switcher"};return await eN(e),{action:"app-switcher"};case"settings":{let[t,i,n]=r;if(y({level:"debug",phase:"settings_apply",data:{setting:t,state:i,platform:e.platform}}),"ios"===e.platform)return await ti(e,t,i,n??a?.appBundleId),{setting:t,state:i};return await ek(e,t,i),{setting:t,state:i}}case"snapshot":{if("ios"===e.platform){let t=await I("snapshot_capture",async()=>await t_(e,{command:"snapshot",appBundleId:a?.appBundleId,interactiveOnly:a?.snapshotInteractiveOnly,compact:a?.snapshotCompact,depth:a?.snapshotDepth,scope:a?.snapshotScope,raw:a?.snapshotRaw},{verbose:a?.verbose,logPath:a?.logPath,traceLogPath:a?.traceLogPath,requestId:a?.requestId}),{backend:"xctest"}),i=t.nodes??[];if(0===i.length&&"simulator"===e.kind)throw new c("COMMAND_FAILED","XCTest snapshot returned 0 nodes on iOS simulator.");return{nodes:i,truncated:t.truncated??!1,backend:"xctest"}}let t=await I("snapshot_capture",async()=>await eE(e,{interactiveOnly:a?.snapshotInteractiveOnly,compact:a?.snapshotCompact,depth:a?.snapshotDepth,scope:a?.snapshotScope,raw:a?.snapshotRaw}),{backend:"android"});return{nodes:t.nodes??[],truncated:t.truncated??!1,backend:"android"}}default:throw new c("INVALID_ARGS",`Unknown command: ${t}`)}},{command:t,platform:e.platform})}let t2=[[0,0],[1,0],[0,1],[-1,0],[0,-1],[1,1],[-1,1],[1,-1],[-1,-1]];function t3(e,t,i,r){if(!Number.isFinite(e)||!Number.isInteger(e)||e<i||e>r)throw new c("INVALID_ARGS",`${t} must be an integer between ${i} and ${r}`);return e}async function t4(e,t,i){for(let r=0;r<e;r+=1)await i(r),r<e-1&&t>0&&await t5(t)}async function t5(e){await new Promise(t=>setTimeout(t,e))}let t8={alert:{ios:{simulator:!0},android:{}},pinch:{ios:{simulator:!0},android:{}},"app-switcher":{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},apps:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},back:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},boot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},click:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},close:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},fill:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},diff:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},find:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},focus:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},get:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},is:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},home:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},longpress:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},open:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},reinstall:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},press:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},record:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},screenshot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},scroll:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},scrollintoview:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},swipe:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},settings:{ios:{simulator:!0},android:{emulator:!0,device:!0,unknown:!0}},snapshot:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},type:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}},wait:{ios:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0}}};function t6(e,t){let i=t8[e];if(!i)return!0;let r=i[t.platform];return!!r&&!0===r[t.kind??"unknown"]}function t9(e){let t=e.result?.text;if("string"==typeof t&&t.trim().length>0)return t;let i=e.positionals??[];return 0===i.length?"":i[0].startsWith("@")?i.length>=3?i.slice(2).join(" ").trim():i.slice(1).join(" ").trim():!(i.length>=3)||Number.isNaN(Number(i[0]))||Number.isNaN(Number(i[1]))?i.slice(1).join(" ").trim():i.slice(2).join(" ").trim()}function t7(e){let t=new Set,i=[];for(let r of e)t.has(r)||(t.add(r),i.push(r));return i}let ie=/^-?\d+(\.\d+)?$/,it=new Map([["--count","count"],["--interval-ms","intervalMs"],["--hold-ms","holdMs"],["--jitter-px","jitterPx"]]),ii=new Map([["--count","count"],["--pause-ms","pauseMs"]]);function ir(e){return"click"===e||"press"===e}function ia(e){let t=e.trim();return t.startsWith("@")||ie.test(t)?t:JSON.stringify(t)}function io(e,t){let i=t.flags??{};if(ir(t.command)){"number"==typeof i.count&&e.push("--count",String(i.count)),"number"==typeof i.intervalMs&&e.push("--interval-ms",String(i.intervalMs)),"number"==typeof i.holdMs&&e.push("--hold-ms",String(i.holdMs)),"number"==typeof i.jitterPx&&e.push("--jitter-px",String(i.jitterPx)),!0===i.doubleTap&&e.push("--double-tap");return}"swipe"===t.command&&("number"==typeof i.count&&e.push("--count",String(i.count)),"number"==typeof i.pauseMs&&e.push("--pause-ms",String(i.pauseMs)),("one-way"===i.pattern||"ping-pong"===i.pattern)&&e.push("--pattern",i.pattern))}function is(e,t){let i=[],r={},n=ir(e)?it:"swipe"===e?ii:void 0;for(let a=0;a<t.length;a+=1){let o=t[a];if(ir(e)&&"--double-tap"===o){r.doubleTap=!0;continue}let s=n?.get(o);if(s&&a+1<t.length){let e=function(e){if(!e)return null;let t=Number(e);return!Number.isFinite(t)||t<0?null:Math.floor(t)}(t[a+1]);null!==e&&(r[s]=e),a+=1;continue}if("swipe"===e&&"--pattern"===o&&a+1<t.length){let e=t[a+1];("one-way"===e||"ping-pong"===e)&&(r.pattern=e),a+=1;continue}i.push(o)}return{positionals:i,flags:r}}class il{sessions=new Map;sessionsDir;constructor(e){this.sessionsDir=e}get(e){return this.sessions.get(e)}has(e){return this.sessions.has(e)}set(e,t){this.sessions.set(e,t)}delete(e){return this.sessions.delete(e)}values(){return this.sessions.values()}toArray(){return Array.from(this.sessions.values())}recordAction(e,t){t.flags?.noRecord||(t.flags?.saveScript&&(e.recordSession=!0,"string"==typeof t.flags.saveScript&&(e.saveScriptPath=il.expandHome(t.flags.saveScript))),e.actions.push({ts:Date.now(),command:t.command,positionals:t.positionals,flags:function(e){if(!e)return{};let{platform:t,device:i,udid:r,serial:n,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:d,snapshotScope:c,snapshotRaw:u,relaunch:p,saveScript:f,noRecord:m,count:h,intervalMs:w,holdMs:g,jitterPx:v,doubleTap:I,pauseMs:y,pattern:A}=e;return{platform:t,device:i,udid:r,serial:n,out:a,verbose:o,snapshotInteractiveOnly:s,snapshotCompact:l,snapshotDepth:d,snapshotScope:c,snapshotRaw:u,relaunch:p,saveScript:f,noRecord:m,count:h,intervalMs:w,holdMs:g,jitterPx:v,doubleTap:I,pauseMs:y,pattern:A}}(t.flags),result:t.result}),y({level:"debug",phase:"record_action",data:{command:t.command,session:e.name}}))}writeSessionLog(e){try{if(!e.recordSession)return;let t=this.resolveScriptPath(e),r=i.dirname(t);_.existsSync(r)||_.mkdirSync(r,{recursive:!0});let n=function(e,t){let i=[],r=e.device.name.replace(/"/g,'\\"'),n=e.device.kind?` kind=${e.device.kind}`:"";for(let a of(i.push(`context platform=${e.device.platform} device="${r}"${n} theme=unknown`),t))a.flags?.noRecord||i.push(function(e){let t=[e.command];if(ir(e.command)){let i=e.positionals?.[0];if(i){if(i.startsWith("@")){t.push(ia(i));let r=e.result?.refLabel;return"string"==typeof r&&r.trim().length>0&&t.push(ia(r)),io(t,e),t.join(" ")}if(1===e.positionals.length)return t.push(ia(i)),io(t,e),t.join(" ")}}if("fill"===e.command){let i=e.positionals?.[0];if(i&&i.startsWith("@")){t.push(ia(i));let r=e.result?.refLabel,n=e.positionals.slice(1).join(" ");return"string"==typeof r&&r.trim().length>0&&t.push(ia(r)),n&&t.push(ia(n)),t.join(" ")}}if("get"===e.command){let i=e.positionals?.[0],r=e.positionals?.[1];if(i&&r){if(t.push(ia(i)),t.push(ia(r)),r.startsWith("@")){let i=e.result?.refLabel;"string"==typeof i&&i.trim().length>0&&t.push(ia(i))}return t.join(" ")}}if("snapshot"===e.command)return e.flags?.snapshotInteractiveOnly&&t.push("-i"),e.flags?.snapshotCompact&&t.push("-c"),"number"==typeof e.flags?.snapshotDepth&&t.push("-d",String(e.flags.snapshotDepth)),e.flags?.snapshotScope&&t.push("-s",ia(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(ia(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(ia(i));return io(t,e),t.join(" ")}(a));return`${i.join("\n")}
|
|
13
|
+
`}(e,this.buildOptimizedActions(e));_.writeFileSync(t,n)}catch{}}defaultTracePath(e){let t=e.name.replace(/[^a-zA-Z0-9._-]/g,"_"),r=new Date().toISOString().replace(/[:.]/g,"-");return i.join(this.sessionsDir,`${t}-${r}.trace.log`)}static expandHome(e,t){return e.startsWith("~/")?i.join(O.homedir(),e.slice(2)):t&&!i.isAbsolute(e)?i.resolve(t,e):i.resolve(e)}resolveScriptPath(e){if(e.saveScriptPath)return il.expandHome(e.saveScriptPath);_.existsSync(this.sessionsDir)||_.mkdirSync(this.sessionsDir,{recursive:!0});let t=e.name.replace(/[^a-zA-Z0-9._-]/g,"_"),r=new Date(e.createdAt).toISOString().replace(/[:.]/g,"-");return i.join(this.sessionsDir,`${t}-${r}.ad`)}buildOptimizedActions(e){let t=[];for(let i of e.actions){if("snapshot"===i.command)continue;let r=Array.isArray(i.result?.selectorChain)&&i.result?.selectorChain.every(e=>"string"==typeof e)?i.result.selectorChain:[];if(r.length>0&&(ir(i.command)||"fill"===i.command||"get"===i.command)){let e=r.join(" || ");if(ir(i.command)){t.push({...i,positionals:[e]});continue}if("fill"===i.command){let r=t9(i);if(r.length>0){t.push({...i,positionals:[e,r]});continue}}if("get"===i.command){let r=i.positionals?.[0];if("text"===r||"attrs"===r){t.push({...i,positionals:[r,e]});continue}}}if(ir(i.command)||"fill"===i.command||"get"===i.command){let r=i.result?.refLabel;"string"==typeof r&&r.trim().length>0&&t.push({ts:i.ts,command:"snapshot",positionals:[],flags:{platform:e.device.platform,snapshotInteractiveOnly:!0,snapshotCompact:!0,snapshotScope:r.trim()},result:{scope:r.trim()}})}t.push(i)}return t}}function id(e,t,i,r,n){return{requestId:n??v().requestId,appBundleId:i,activity:t?.activity,verbose:t?.verbose,logPath:e,traceLogPath:r,snapshotInteractiveOnly:t?.snapshotInteractiveOnly,snapshotCompact:t?.snapshotCompact,snapshotDepth:t?.snapshotDepth,snapshotScope:t?.snapshotScope,snapshotRaw:t?.snapshotRaw,count:t?.count,intervalMs:t?.intervalMs,holdMs:t?.holdMs,jitterPx:t?.jitterPx,doubleTap:t?.doubleTap,pauseMs:t?.pauseMs,pattern:t?.pattern}}let ic=eB(process.env.AGENT_DEVICE_IOS_DEVICE_READY_TIMEOUT_MS,15e3,1e3);async function iu(e){if("ios"===e.platform){if("simulator"===e.kind){let{ensureBootedSimulator:t}=await Promise.resolve().then(()=>({ensureBootedSimulator:e0}));await t(e);return}if("device"===e.kind)return void await ip(e.id)}if("android"===e.platform){let{waitForAndroidBoot:t}=await Promise.resolve().then(()=>({waitForAndroidBoot:z}));await t(e.id)}}async function ip(e){let t=i.join(O.tmpdir(),`agent-device-ready-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`),r=Math.max(1,Math.ceil(ic/1e3));try{let i=await s("xcrun",["devicectl","device","info","details","--device",e,"--json-output",t,"--timeout",String(r)],{allowFailure:!0,timeoutMs:ic+3e3}),n=String(i.stdout??""),a=String(i.stderr??""),o=await im(t);if(0===i.exitCode){if(!o.parsed)throw new c("COMMAND_FAILED","iOS device readiness probe failed",{kind:"probe_inconclusive",deviceId:e,stdout:n,stderr:a,hint:"CoreDevice returned success but readiness JSON output was missing or invalid. Retry; if it persists restart Xcode and the iOS device."});let t=o?.tunnelState?.toLowerCase();if("connecting"===t)throw new c("COMMAND_FAILED","iOS device is not ready for automation",{kind:"not_ready",deviceId:e,tunnelState:t,hint:"Device tunnel is still connecting. Keep the device unlocked and connected by cable until it is fully available in Xcode Devices, then retry."});return}throw new c("COMMAND_FAILED","iOS device is not ready for automation",{kind:"not_ready",deviceId:e,stdout:n,stderr:a,exitCode:i.exitCode,tunnelState:o?.tunnelState,hint:ih(n,a)})}catch(t){if(t instanceof c&&"COMMAND_FAILED"===t.code){if("not_ready"===("string"==typeof t.details?.kind?t.details.kind:""))throw t;let i=t.details??{},r=String(i.stdout??""),n=String(i.stderr??""),a=Number(i.timeoutMs??ic),o=`CoreDevice did not respond within ${a}ms. Keep the device unlocked and trusted, then retry; if it persists restart Xcode and the iOS device.`;throw new c("COMMAND_FAILED","iOS device readiness probe failed",{deviceId:e,cause:t.message,timeoutMs:a,stdout:r,stderr:n,hint:r||n?ih(r,n):o},t)}throw new c("COMMAND_FAILED","iOS device readiness probe failed",{deviceId:e,hint:"Reconnect the device, keep it unlocked, and retry."},t instanceof Error?t:void 0)}finally{await A.rm(t,{force:!0}).catch(()=>{})}}async function im(e){try{let t=await A.readFile(e,"utf8"),i=JSON.parse(t),r=function(e){let t=e?.result;if(!t||"object"!=typeof t)return{};let i=t.connectionProperties?.tunnelState,r=t.device?.connectionProperties?.tunnelState,n="string"==typeof i?i:"string"==typeof r?r:void 0;return n?{tunnelState:n}:{}}(i);return{parsed:!0,tunnelState:r.tunnelState}}catch{return{parsed:!1}}}function ih(e,t){let i=eQ(e,t);return i||(`${e}
|
|
14
|
+
${t}`.toLowerCase().includes("timed out waiting for all destinations")?"Xcode destination did not become available in time. Keep device unlocked and retry.":eZ)}function iw(e){return e.map((e,t)=>({...e,ref:`e${t+1}`}))}function ig(e){let t=e.trim();return t.startsWith("@")?t.slice(1)||null:t.startsWith("e")?t:null}function iv(e,t){return e.find(e=>e.ref===t)??null}function iI(e){return{x:Math.round(e.x+e.width/2),y:Math.round(e.y+e.height/2)}}function iy(e,t){let i=t.toLowerCase();return e.find(e=>{let t=(e.label??"").toLowerCase(),r=(e.value??"").toLowerCase(),n=(e.identifier??"").toLowerCase();return t.includes(i)||r.includes(i)||n.includes(i)})??null}function iA(e,t){let i=[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0);return i&&iN(i)?i:function(e,t){if(!e.rect)return;let i=e.rect.y+e.rect.height/2,r=null;for(let e of t){if(!e.rect)continue;let t=[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0);if(!t||!iN(t))continue;let n=Math.abs(e.rect.y+e.rect.height/2-i);(!r||n<r.distance)&&(r={label:t,distance:n})}return r?.label}(e,t)??(i&&iN(i)?i:void 0)}function iN(e){let t=e.trim();return!(!t||/^(true|false)$/i.test(t)||/^\d+$/.test(t))}function iS(e){let t=[],i=[];for(let r of e){let e=r.depth??0;for(;t.length>0&&e<=t[t.length-1];)t.pop();let n=ib(r.type??""),a=[r.label,r.value,r.identifier].map(e=>"string"==typeof e?e.trim():"").find(e=>e&&e.length>0),o=!!a&&iN(a);if(("group"===n||"ioscontentgroup"===n)&&!o){t.push(e);continue}let s=Math.max(0,e-t.length);i.push({...r,depth:s})}return i}function ib(e){let t=e.trim().replace(/XCUIElementType/gi,"").toLowerCase(),i=Math.max(t.lastIndexOf("."),t.lastIndexOf("/"));return -1!==i&&(t=t.slice(i+1)),t}function i_(e,t){let i=ib(e);return!i||("android"===t?i.includes("edittext")||i.includes("autocompletetextview"):i.includes("textfield")||i.includes("securetextfield")||i.includes("searchfield")||i.includes("textview")||i.includes("textarea")||"search"===i)}function iD(e){return[e.label,e.value,e.identifier].map(e=>"string"==typeof e?e.trim():"").filter(e=>e.length>0)[0]??""}let iM=new Set(["id","role","text","label","value"]),iO=new Set(["visible","hidden","editable","selected","enabled","hittable"]),ix=new Set([...iM,...iO]);function ik(e){let t=e.trim();if(!t)throw new c("INVALID_ARGS","Selector expression cannot be empty");let i=function(e){let t=[],i="",r=null;for(let n=0;n<e.length;n+=1){let a=e[n];if(('"'===a||"'"===a)&&!iz(e,n)){r?r===a&&(r=null):r=a,i+=a;continue}if(!r&&"|"===a&&"|"===e[n+1]){let r=i.trim();if(!r)throw new c("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);t.push(r),i="",n+=1;continue}i+=a}let n=i.trim();if(!n)throw new c("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);return t.push(n),t}(t);if(0===i.length)throw new c("INVALID_ARGS","Selector expression cannot be empty");return{raw:t,selectors:i.map(e=>(function(e){let t=e.trim();if(!t)throw new c("INVALID_ARGS","Selector segment cannot be empty");let i=function(e){let t=[],i="",r=null;for(let n=0;n<e.length;n+=1){let a=e[n];if(('"'===a||"'"===a)&&!iz(e,n)){r?r===a&&(r=null):r=a,i+=a;continue}if(!r&&/\s/.test(a)){i.trim().length>0&&t.push(i.trim()),i="";continue}i+=a}if(r)throw new c("INVALID_ARGS",`Unclosed quote in selector: ${e}`);return i.trim().length>0&&t.push(i.trim()),t}(t);if(0===i.length)throw new c("INVALID_ARGS",`Invalid selector segment: ${e}`);return{raw:t,terms:i.map(iU)}})(e))}}function iE(e){try{return ik(e)}catch{return null}}function iC(e,t,i){let r=i.requireRect??!1,n=i.requireUnique??!0,a=i.disambiguateAmbiguous??!1,o=[];for(let s=0;s<t.selectors.length;s+=1){let l=t.selectors[s],d=function(e,t,i){let r=0,n=null,a=null,o=!1;for(let s of e){if(i.requireRect&&!s.rect||!iG(s,t,i.platform))continue;if(r+=1,n||(n=s),!a){a=s;continue}let e=function(e,t){let i=e.depth??0,r=t.depth??0;if(i!==r)return i>r?1:-1;let n=iH(e),a=iH(t);return n!==a?n<a?1:-1:0}(s,a);if(e>0){a=s,o=!1;continue}0===e&&(o=!0)}return{count:r,firstNode:n,disambiguated:o?null:a}}(e,l,{platform:i.platform,requireRect:r});if(o.push({selector:l.raw,matches:d.count}),0!==d.count&&d.firstNode){if(n&&1!==d.count){if(!a)continue;let e=d.disambiguated;if(!e)continue;return{node:e,selector:l,selectorIndex:s,matches:d.count,diagnostics:o}}return{node:d.firstNode,selector:l,selectorIndex:s,matches:d.count,diagnostics:o}}}return null}function iL(e,t,i){let r=i.requireRect??!1,n=[];for(let a=0;a<t.selectors.length;a+=1){let o=t.selectors[a],s=function(e,t,i){let r=0;for(let n of e)(!i.requireRect||n.rect)&&iG(n,t,i.platform)&&(r+=1);return r}(e,o,{platform:i.platform,requireRect:r});if(n.push({selector:o.raw,matches:s}),s>0)return{selectorIndex:a,selector:o,matches:s,diagnostics:n}}return null}function iR(e,t,i){let r=i.unique??!0;if(0===t.length)return`Selector did not match: ${e.raw}`;let n=t.map(e=>`${e.selector} -> ${e.matches}`).join(", ");return r?`Selector did not resolve uniquely (${n})`:`Selector did not match (${n})`}function iT(e,t={}){if(0===e.length)return null;let i=t.preferTrailingValue??!1,r=0,n=[];for(;r<e.length&&function(e){let t=e.trim();if(!t)return!1;if("||"===t)return!0;let i=t.indexOf("=");if(-1!==i){let e=t.slice(0,i).trim().toLowerCase();return ix.has(e)}return ix.has(t.toLowerCase())}(e[r]);){r+=1;let t=e.slice(0,r).join(" ").trim();t&&iE(t)&&n.push(r)}if(0===n.length)return null;let a=n[n.length-1];if(i){for(let t=n.length-1;t>=0;t-=1)if(n[t]<e.length){a=n[t];break}}let o=e.slice(0,a).join(" ").trim();return o?{selectorExpression:o,rest:e.slice(a)}:null}function iP(e){let t=e[0]??"",i=iT(e.slice(1),{preferTrailingValue:"text"===t});return{predicate:t,split:i}}function i$(e){return!0===e.hittable||!!e.rect&&e.rect.width>0&&e.rect.height>0}function iF(e,t){return i_(e.type??"",t)&&!1!==e.enabled}function iV(e,t,i={}){let r=[],n=ib(e.type??""),a=iJ(e.identifier),o=iJ(e.label),s=iJ(e.value),l=iJ(iD(e)),d="fill"===i.action;a&&r.push(`id=${iW(a)}`),n&&o&&r.push(d?`role=${iW(n)} label=${iW(o)} editable=true`:`role=${iW(n)} label=${iW(o)}`),o&&r.push(d?`label=${iW(o)} editable=true`:`label=${iW(o)}`),s&&r.push(d?`value=${iW(s)} editable=true`:`value=${iW(s)}`),l&&l!==o&&l!==s&&r.push(d?`text=${iW(l)} editable=true`:`text=${iW(l)}`),n&&d&&!r.some(e=>e.includes("editable=true"))&&r.push(`role=${iW(n)} editable=true`);let c=t7(r);return 0===c.length&&n&&c.push(d?`role=${iW(n)} editable=true`:`role=${iW(n)}`),0===c.length&&i$(e)&&c.push("visible=true"),c}function iU(e){let t=e.trim();if(!t)throw new c("INVALID_ARGS","Empty selector term");let i=t.indexOf("=");if(-1===i){let i=t.toLowerCase();if(!iO.has(i))throw new c("INVALID_ARGS",`Invalid selector term "${e}", expected key=value`);return{key:i,value:!0}}let r=t.slice(0,i).trim().toLowerCase(),n=t.slice(i+1).trim();if(!ix.has(r))throw new c("INVALID_ARGS",`Unknown selector key: ${r}`);if(!n)throw new c("INVALID_ARGS",`Missing selector value for key: ${r}`);if(iO.has(r)){let e,t="true"===(e=iq(n).toLowerCase())||"false"!==e&&null;if(null===t)throw new c("INVALID_ARGS",`Invalid boolean value for ${r}: ${n}`);return{key:r,value:t}}return{key:r,value:iq(n)}}function iG(e,t,i){return t.terms.every(t=>(function(e,t,i){switch(t.key){case"id":return iB(e.identifier,String(t.value));case"role":var r,n;return r=e.type,n=String(t.value),function(e){return ib(e)}(r??"")===function(e){return ib(e)}(n);case"label":return iB(e.label,String(t.value));case"value":return iB(e.value,String(t.value));case"text":{let i=ij(String(t.value));return ij(iD(e))===i}case"visible":return i$(e)===!!t.value;case"hidden":return!i$(e)==!!t.value;case"editable":return iF(e,i)===!!t.value;case"selected":return!0===e.selected==!!t.value;case"enabled":return!1!==e.enabled==!!t.value;case"hittable":return!0===e.hittable==!!t.value;default:return!1}})(e,t,i))}function iq(e){let t=e.trim();return t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1).replace(/\\(["'])/g,"$1"):t}function iB(e,t){return ij(e??"")===ij(t)}function ij(e){return e.trim().toLowerCase().replace(/\s+/g," ")}function iW(e){return JSON.stringify(e)}function iJ(e){if(!e)return null;let t=e.trim();return t||null}function iH(e){return e.rect?e.rect.width*e.rect.height:1/0}function iz(e,t){let i=0;for(let r=t-1;r>=0&&"\\"===e[r];r-=1)i+=1;return i%2==1}let iY='iOS appstate requires an active session on the target device. Run open first (for example: open --session sim --platform ios --device "<name>" <app>).',iX=["platform","device","udid","serial","verbose","out"],iK=["platform","device","udid","serial","verbose","out"];function iZ(e,t,i){return t||iQ(i)?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} requires an active session or an explicit device selector (e.g. --platform ios).`}}}function iQ(e){return!!(e?.platform||e?.device||e?.udid||e?.serial)}async function i0(e){let t=iQ(e.flags)||!e.session?await e.resolveTargetDeviceFn(e.flags??{}):e.session.device;return!1!==e.ensureReady&&await e.ensureReadyFn(t),t}let i1={ios:async(e,t,i)=>{let{reinstallIosApp:r}=await Promise.resolve().then(()=>({reinstallIosApp:te}));return await r(e,t,i)},android:async(e,t,i)=>{let{reinstallAndroidApp:r}=await Promise.resolve().then(()=>({reinstallAndroidApp:eg}));return await r(e,t,i)}};async function i2(e,t,i){if("ios"===e.platform&&t)return Y(t)?"device"===e.kind?X(i,t):void 0:await i3(e,t)}async function i3(e,t){try{let{resolveIosApp:i}=await Promise.resolve().then(()=>({resolveIosApp:e4}));return await i(e,t)}catch{return}}async function i4(e){let{req:t,sessionName:i,sessionStore:r,ensureReady:n,resolveDevice:a}=e,o=r.get(i),s=t.flags??{};if(!o&&"string"==typeof s?.session&&s.session.trim().length>0)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"ios"===s.platform?`No active session "${i}". Run open with --session ${i} first.`:`No active session "${i}". Run open with --session ${i} first, or omit --session to query by device selector.`}};let l=iZ("appstate",o,s);if(l)return l;let d=o?.device.platform==="ios"&&!!o&&(!iQ(s)||!(s?.platform&&s.platform!==o.device.platform||s?.udid&&s.udid!==o.device.id||s?.serial&&s.serial!==o.device.id)&&(!s?.device||s.device.trim().toLowerCase()===o.device.name.trim().toLowerCase()));if("ios"===s.platform&&!d)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:iY}};if(d){let e=o.appName??o.appBundleId;return o.appName||o.appBundleId?{ok:!0,data:{platform:"ios",appName:e??"unknown",appBundleId:o.appBundleId,source:"session"}}:{ok:!1,error:{code:"COMMAND_FAILED",message:"No foreground app is tracked for this iOS session. Open an app in the session, then retry appstate."}}}let c=await i0({session:o,flags:s,ensureReadyFn:n,resolveTargetDeviceFn:a,ensureReady:!0});if("ios"===c.platform)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:iY}};let{getAndroidAppState:u}=await Promise.resolve().then(()=>({getAndroidAppState:ed})),p=await u(c);return{ok:!0,data:{platform:"android",package:p.package,activity:p.activity}}}async function i5(e){let{req:t,sessionName:i,logPath:r,sessionStore:n,invoke:a,dispatch:o,ensureReady:s,resolveTargetDevice:l,reinstallOps:u=i1,stopIosRunner:p}=e,f=o??t1,m=s??iu,h=l??t0,w=t.command;if("session_list"===w)return{ok:!0,data:{sessions:n.toArray().map(e=>({name:e.name,platform:e.device.platform,device:e.device.name,id:e.device.id,createdAt:e.createdAt}))}};if("devices"===w)try{let e=[];if(t.flags?.platform==="android"){let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:J}));e.push(...await t())}else if(t.flags?.platform==="ios"){let{listIosDevices:t}=await Promise.resolve().then(()=>({listIosDevices:eW}));e.push(...await t())}else{let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:J})),{listIosDevices:i}=await Promise.resolve().then(()=>({listIosDevices:eW}));try{e.push(...await t())}catch{}try{e.push(...await i())}catch{}}return{ok:!0,data:{devices:e}}}catch(t){let e=d(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}if("apps"===w){let e=n.get(i),r=t.flags??{},a=iZ(w,e,r);if(a)return a;let o=await i0({session:e,flags:r,ensureReadyFn:m,resolveTargetDeviceFn:h,ensureReady:!0});if(!t6("apps",o))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"apps is not supported on this device"}};let s=t.flags?.appsFilter??"all";if("ios"===o.platform){let{listIosApps:e}=await Promise.resolve().then(()=>({listIosApps:tr}));return{ok:!0,data:{apps:(await e(o,s)).map(e=>e.name&&e.name!==e.bundleId?`${e.name} (${e.bundleId})`:e.bundleId)}}}let{listAndroidApps:l}=await Promise.resolve().then(()=>({listAndroidApps:eo}));return{ok:!0,data:{apps:(await l(o,s)).map(e=>e.name&&e.name!==e.package?`${e.name} (${e.package})`:e.package)}}}if("boot"===w){let e=n.get(i),r=t.flags??{},a=iZ(w,e,r);if(a)return a;let o=await i0({session:e,flags:r,ensureReadyFn:m,resolveTargetDeviceFn:h,ensureReady:!0});return t6("boot",o)?{ok:!0,data:{platform:o.platform,device:o.name,id:o.id,kind:o.kind,booted:!0}}:{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"boot is not supported on this device"}}}if("appstate"===w)return await i4({req:t,sessionName:i,sessionStore:n,ensureReady:m,resolveDevice:h});if("reinstall"===w){let e,r=n.get(i),a=t.flags??{},o=iZ(w,r,a);if(o)return o;let s=t.positionals?.[0]?.trim(),l=t.positionals?.[1]?.trim();if(!s||!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"reinstall requires: reinstall <app> <path-to-app-binary>"}};let d=il.expandHome(l);if(!_.existsSync(d))return{ok:!1,error:{code:"INVALID_ARGS",message:`App binary not found: ${d}`}};let c=await i0({session:r,flags:a,ensureReadyFn:m,resolveTargetDeviceFn:h,ensureReady:!1});if(!t6("reinstall",c))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"reinstall is not supported on this device"}};if("ios"===c.platform){let t=await u.ios(c,s,d);e={platform:"ios",appId:t.bundleId,bundleId:t.bundleId}}else{let t=await u.android(c,s,d);e={platform:"android",appId:t.package,package:t.package}}let p={app:s,appPath:d,...e};return r&&n.recordAction(r,{command:w,positionals:t.positionals??[],flags:t.flags??{},result:p}),{ok:!0,data:p}}if("open"===w){let e=t.flags?.relaunch===!0;if(n.has(i)){let a=n.get(i),o=t.positionals?.[0],s=o??(e?a?.appName:void 0);if(!a||!s)return e?{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch requires an app name or an active session app."}}:{ok:!1,error:{code:"INVALID_ARGS",message:"Session already active. Close it first or pass a new --session name."}};if(e&&Y(s))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};await m(a.device);let l=await i2(a.device,s,a.appBundleId),d=o?t.positionals??[]:[s];if(e){let e=l??s;await f(a.device,"close",[e],t.flags?.out,{...id(r,t.flags,l??a.appBundleId,a.trace?.outPath)})}await f(a.device,"open",d,t.flags?.out,{...id(r,t.flags,l)});let c={...a,appBundleId:l,appName:s,recordSession:a.recordSession||!!t.flags?.saveScript,snapshot:void 0};return n.recordAction(c,{command:w,positionals:d,flags:t.flags??{},result:{session:i,appName:s,appBundleId:l}}),n.set(i,c),{ok:!0,data:{session:i,appName:s,appBundleId:l}}}let a=t.positionals?.[0];if(e&&!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch requires an app argument."}};if(e&&a&&Y(a))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};let o=await h(t.flags??{}),s=n.toArray().find(e=>e.device.id===o.id);if(s)return{ok:!1,error:{code:"DEVICE_IN_USE",message:`Device is already in use by session "${s.name}".`,details:{session:s.name,deviceId:o.id,deviceName:o.name}}};await m(o);let l=await i2(o,a);if(e&&a){let e=l??a;await f(o,"close",[e],t.flags?.out,{...id(r,t.flags,l)})}await f(o,"open",t.positionals??[],t.flags?.out,{...id(r,t.flags,l)});let d={name:i,device:o,createdAt:Date.now(),appBundleId:l,appName:a,recordSession:!!t.flags?.saveScript,actions:[]};return n.recordAction(d,{command:w,positionals:t.positionals??[],flags:t.flags??{},result:{session:i}}),n.set(i,d),{ok:!0,data:{session:i}}}if("replay"===w){let e=t.positionals?.[0];if(!e)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay requires a path"}};try{let o=il.expandHome(e,t.meta?.cwd),s=_.readFileSync(o,"utf8"),l=s.trimStart()[0];if("{"===l||"["===l)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay accepts .ad script files. JSON replay payloads are no longer supported."}};let d=function(e){let t=[];for(let i of e.split(/\r?\n/)){let e=function(e){let t=e.trim();if(0===t.length||t.startsWith("#"))return null;let i=function(e){let t=[],i=0;for(;i<e.length;){for(;i<e.length&&/\s/.test(e[i]);)i+=1;if(i>=e.length)break;if('"'===e[i]){let r=i+1,n=!1;for(;r<e.length;){let t=e[r];if('"'===t&&!n)break;n="\\"===t&&!n,"\\"!==t&&(n=!1),r+=1}if(r>=e.length)throw new c("INVALID_ARGS",`Invalid replay script line: ${e}`);let a=e.slice(i,r+1);t.push(JSON.parse(a)),i=r+1;continue}let r=i;for(;r<e.length&&!/\s/.test(e[r]);)r+=1;t.push(e.slice(i,r)),i=r}return t}(t);if(0===i.length)return null;let[r,...n]=i;if("context"===r)return null;let a={ts:Date.now(),command:r,positionals:[],flags:{}};if("snapshot"===r){a.positionals=[];for(let e=0;e<n.length;e+=1){let t=n[e];if("-i"===t){a.flags.snapshotInteractiveOnly=!0;continue}if("-c"===t){a.flags.snapshotCompact=!0;continue}if("--raw"===t){a.flags.snapshotRaw=!0;continue}if(("-d"===t||"--depth"===t)&&e+1<n.length){let t=Number(n[e+1]);Number.isFinite(t)&&t>=0&&(a.flags.snapshotDepth=Math.floor(t)),e+=1;continue}if(("-s"===t||"--scope"===t)&&e+1<n.length){a.flags.snapshotScope=n[e+1],e+=1;continue}if("--backend"===t&&e+1<n.length){e+=1;continue}}return a}if("open"===r){a.positionals=[];for(let e=0;e<n.length;e+=1){let t=n[e];if("--relaunch"===t){a.flags.relaunch=!0;continue}a.positionals.push(t)}return a}if(ir(r)){let e=is(r,n);if(Object.assign(a.flags,e.flags),0===e.positionals.length)return a;let t=e.positionals[0];if(t.startsWith("@"))return a.positionals=[t],e.positionals[1]&&(a.result={refLabel:e.positionals[1]}),a;let i=e.positionals[0],o=e.positionals[1];return rr(i)&&rr(o)&&e.positionals.length>=2?a.positionals=[i,o]:a.positionals=[e.positionals.join(" ")],a}if("fill"===r){if(n.length<2)return a.positionals=n,a;let e=n[0];return e.startsWith("@")?(n.length>=3?(a.positionals=[e,n.slice(2).join(" ")],a.result={refLabel:n[1]}):a.positionals=[e,n[1]],a):(a.positionals=[e,n.slice(1).join(" ")],a)}if("get"===r){if(n.length<2)return a.positionals=n,a;let e=n[0],t=n[1];return t.startsWith("@")?(a.positionals=[e,t],n[2]&&(a.result={refLabel:n[2]})):a.positionals=[e,n.slice(1).join(" ")],a}if("swipe"===r){let e=is(r,n);return Object.assign(a.flags,e.flags),a.positionals=e.positionals,a}return a.positionals=n,a}(i);e&&t.push(e)}return t}(s),u=t.flags?.replayUpdate===!0,p=0;for(let e=0;e<d.length;e+=1){let s=d[e];if(!s||"replay"===s.command)continue;let l=await a({token:t.token,session:i,command:s.command,positionals:s.positionals??[],flags:i7(t.flags,s.flags),meta:t.meta});if(l.ok)continue;if(!u)return i9(l,s,e,o);let c=await re({action:s,sessionName:i,logPath:r,sessionStore:n,dispatch:f});if(!c)return i9(l,s,e,o);if(d[e]=c,!(l=await a({token:t.token,session:i,command:c.command,positionals:c.positionals??[],flags:i7(t.flags,c.flags),meta:t.meta})).ok)return i9(l,c,e,o);p+=1}if(u&&p>0){let e=n.get(i);!function(e,t,i){let r=[];if(i){let e=i.device.name.replace(/"/g,'\\"'),t=i.device.kind?` kind=${i.device.kind}`:"";r.push(`context platform=${i.device.platform} device="${e}"${t} theme=unknown`)}for(let e of t)r.push(function(e){let t=[e.command];if("snapshot"===e.command)return e.flags?.snapshotInteractiveOnly&&t.push("-i"),e.flags?.snapshotCompact&&t.push("-c"),"number"==typeof e.flags?.snapshotDepth&&t.push("-d",String(e.flags.snapshotDepth)),e.flags?.snapshotScope&&t.push("-s",ia(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),t.join(" ");if("open"===e.command){for(let i of e.positionals??[])t.push(ia(i));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}for(let i of e.positionals??[])t.push(ia(i));return io(t,e),t.join(" ")}(e));let n=`${r.join("\n")}
|
|
15
|
+
`,a=`${e}.tmp-${process.pid}-${Date.now()}`;_.writeFileSync(a,n),_.renameSync(a,e)}(o,d,e)}return{ok:!0,data:{replayed:d.length,healed:p,session:i}}}catch(t){let e=d(t);return{ok:!1,error:{code:e.code,message:e.message}}}}if("batch"===w)return await i8(t,i,a);if("close"===w){let e=n.get(i);return e?(t.positionals&&t.positionals.length>0&&await f(e.device,"close",t.positionals??[],t.flags?.out,{...id(r,t.flags,e.appBundleId,e.trace?.outPath)}),"ios"===e.device.platform&&await (p??tx)(e.device.id),n.recordAction(e,{command:w,positionals:t.positionals??[],flags:t.flags??{},result:{session:i}}),t.flags?.saveScript&&(e.recordSession=!0),n.writeSessionLog(e),n.delete(i),{ok:!0,data:{session:i}}):{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}}}return null}async function i8(e,t,i){let n=e.flags?.batchOnError??"stop";if("stop"!==n)return{ok:!1,error:{code:"INVALID_ARGS",message:`Unsupported batch on-error mode: ${n}.`}};let a=e.flags?.batchMaxSteps??N;if(!Number.isInteger(a)||a<1||a>1e3)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid batch max-steps: ${String(e.flags?.batchMaxSteps)}`}};try{let n=r(e.flags?.batchSteps,a),o=Date.now(),s=[];for(let r=0;r<n.length;r+=1){let a=n[r],o=await i6(e,t,a,i,r+1);if(!o.ok)return{ok:!1,error:{code:o.error.code,message:`Batch failed at step ${o.step} (${a.command}): ${o.error.message}`,hint:o.error.hint,diagnosticId:o.error.diagnosticId,logPath:o.error.logPath,details:{...o.error.details??{},step:o.step,command:a.command,positionals:a.positionals,executed:r,total:n.length,partialResults:s}}};s.push(o.result)}return{ok:!0,data:{total:n.length,executed:n.length,totalDurationMs:Date.now()-o,results:s}}}catch(t){let e=d(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}}async function i6(e,t,i,r,n){let a=Date.now(),o=await r({token:e.token,session:t,command:i.command,positionals:i.positionals,flags:function(e,t){let i={...t??{}};delete i.batchSteps,delete i.batchOnError,delete i.batchMaxSteps;let r=e??{};for(let e of iX)void 0===i[e]&&void 0!==r[e]&&(i[e]=r[e]);return i}(e.flags,i.flags),meta:e.meta}),s=Date.now()-a;return o.ok?{ok:!0,step:n,result:{step:n,command:i.command,ok:!0,data:o.data??{},durationMs:s}}:{ok:!1,step:n,error:o.error}}function i9(e,t,i,r){if(e.ok)return e;let n=i+1,a=function(e){let t;return t=(e.positionals??[]).map(e=>ia(e)),[e.command,...t].join(" ")}(t),o={...e.error.details??{},replayPath:r,step:n,action:t.command,positionals:t.positionals??[]};return{ok:!1,error:{code:e.error.code,message:`Replay failed at step ${n} (${a}): ${e.error.message}`,hint:e.error.hint,diagnosticId:e.error.diagnosticId,logPath:e.error.logPath,details:o}}}function i7(e,t){let i={...t??{}},r=e??{};for(let e of iK)void 0===i[e]&&void 0!==r[e]&&(i[e]=r[e]);return i}async function re(e){let{action:t,sessionName:i,logPath:r,sessionStore:n,dispatch:a}=e;if(!(ir(t.command)||["fill","get","is","wait"].includes(t.command)))return null;let o=n.get(i);if(!o)return null;let s=ir(t.command)||"fill"===t.command,l=ir(t.command)||"fill"===t.command||"get"===t.command&&t.positionals?.[0]==="text",d=await rt(o,t,r,s,a,n);for(let e of function(e){let t=[],i=Array.isArray(e.result?.selectorChain)&&e.result?.selectorChain.every(e=>"string"==typeof e)?e.result.selectorChain:[];if(t.push(...i),ir(e.command)){let i=e.positionals?.[0]??"";i&&!i.startsWith("@")&&t.push(e.positionals.join(" "))}if("fill"===e.command){let i=e.positionals?.[0]??"";i&&!i.startsWith("@")&&Number.isNaN(Number(i))&&t.push(i)}if("get"===e.command){let i=e.positionals?.[1]??"";i&&!i.startsWith("@")&&t.push(e.positionals.slice(1).join(" "))}if("is"===e.command){let{split:i}=iP(e.positionals);i&&t.push(i.selectorExpression)}if("wait"===e.command){let{selectorExpression:i}=ri(e.positionals??[]);i&&t.push(i)}let r="string"==typeof e.result?.refLabel?e.result.refLabel.trim():"";if(r.length>0){let i=JSON.stringify(r);"fill"===e.command?(t.push(`id=${i} editable=true`),t.push(`label=${i} editable=true`),t.push(`text=${i} editable=true`),t.push(`value=${i} editable=true`)):(t.push(`id=${i}`),t.push(`label=${i}`),t.push(`text=${i}`),t.push(`value=${i}`))}return t7(t).filter(e=>e.trim().length>0)}(t)){let i=iE(e);if(!i)continue;let r=iC(d.nodes,i,{platform:o.device.platform,requireRect:s,requireUnique:!0,disambiguateAmbiguous:l});if(!r)continue;let n=iV(r.node,o.device.platform,{action:ir(t.command)?"click":"fill"===t.command?"fill":"get"}).join(" || ");if(ir(t.command))return{...t,positionals:[n]};if("fill"===t.command){let e=t9(t);if(!e)continue;return{...t,positionals:[n,e]}}if("get"===t.command){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)continue;return{...t,positionals:[e,n]}}if("is"===t.command){let{predicate:e,split:i}=iP(t.positionals);if(!e)continue;let r=i?.rest.join(" ").trim()??"",a=[e,n];return"text"===e&&r.length>0&&a.push(r),{...t,positionals:a}}if("wait"===t.command){let{selectorTimeout:e}=ri(t.positionals??[]),i=[n];return e&&i.push(e),{...t,positionals:i}}}let c=function(e,t,i){if("get"!==e.command||e.positionals?.[0]!=="text")return null;let r=e.positionals?.[1];if(!r)return null;let n=iE(r);if(!n)return null;let a=new Set,o=!1;for(let e of n.selectors)for(let t of e.terms)"role"===t.key&&"string"==typeof t.value&&a.add(ib(t.value)),("text"===t.key||"label"===t.key||"value"===t.key)&&"string"==typeof t.value&&/^\d+$/.test(t.value.trim())&&(o=!0);if(!o)return null;let s=t.nodes.filter(e=>{let t=iD(e).trim();return!!/^\d+$/.test(t)&&(0===a.size||a.has(ib(e.type??"")))});if(0===s.length||1!==t7(s.map(e=>iD(e).trim())).length)return null;let l=s[0];if(!l)return null;let d=iV(l,i.device.platform,{action:"get"});return 0===d.length?null:{...e,positionals:["text",d.join(" || ")]}}(t,d,o);return c||null}async function rt(e,t,i,r,n,a){let o=await n(e.device,"snapshot",[],t.flags?.out,{...id(i,{...t.flags??{},snapshotInteractiveOnly:r,snapshotCompact:r},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[],l={nodes:iw(t.flags?.snapshotRaw?s:iS(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend};return e.snapshot=l,a.set(e.name,e),l}function ri(e){if(0===e.length)return{selectorExpression:null,selectorTimeout:null};let t=e[e.length-1],i=/^\d+$/.test(t??""),r=iT(i?e.slice(0,-1):e.slice());return!r||r.rest.length>0?{selectorExpression:null,selectorTimeout:null}:{selectorExpression:r.selectorExpression,selectorTimeout:i?t:null}}function rr(e){return!!e&&!Number.isNaN(Number(e))}function rn(e){if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}function ra(e,t){let i=h(e.type??"Element"),r=l(e,i),n=!1===e.enabled?"disabled":"enabled",a=!0===e.selected?"selected":"unselected",o=!0===e.hittable?"hittable":"not-hittable";return[String(t??e.depth??0),i,r,n,a,o].join("|")}function ro(e,t){return t.flatten?e.map(e=>({text:k(e,0,!1),comparable:ra(e,0)})):p(e).map(e=>({text:e.text,comparable:ra(e.node,e.depth)}))}function rs(e,t){return e.get(t)??0}async function rl(e){let{req:t,sessionName:i,logPath:r,sessionStore:n}=e,a=e.dispatchSnapshotCommand??t1,o=t.command;if("snapshot"===o){let{session:e,device:o}=await ru(n,i,t.flags);if(!t6("snapshot",o))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"snapshot is not supported on this device"}};let s=rc(t.flags?.snapshotScope,e);return s.ok?await rp(e,o,async()=>{let l=e?.appBundleId,d=await rd({dispatchSnapshotCommand:a,device:o,session:e,req:t,logPath:r,snapshotScope:s.scope}),c=e?{...e,snapshot:d.snapshot}:{name:i,device:o,createdAt:Date.now(),appBundleId:l,snapshot:d.snapshot,actions:[]};return rf(n,c,t,{nodes:d.snapshot.nodes.length,truncated:d.snapshot.truncated??!1}),n.set(i,c),{ok:!0,data:{nodes:d.snapshot.nodes,truncated:d.snapshot.truncated??!1,appName:c.appBundleId?c.appName??c.appBundleId:void 0,appBundleId:c.appBundleId}}}):s.response}if("diff"===o){if(t.positionals?.[0]!=="snapshot")return{ok:!1,error:{code:"INVALID_ARGS",message:"diff currently supports only: diff snapshot"}};let{session:e,device:o}=await ru(n,i,t.flags);if(!t6("diff",o))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"diff is not supported on this device"}};let s=rc(t.flags?.snapshotScope,e);if(!s.ok)return s.response;let l=t.flags?.snapshotInteractiveOnly===!0;return await rp(e,o,async()=>{let d=e?.appBundleId,c=(await rd({dispatchSnapshotCommand:a,device:o,session:e,req:t,logPath:r,snapshotScope:s.scope})).snapshot;if(!e?.snapshot){let r=function(e,t={}){return ro(e,t).length}(c.nodes,{flatten:l}),a=e?{...e,snapshot:c}:{name:i,device:o,createdAt:Date.now(),appBundleId:d,snapshot:c,actions:[]};return rf(n,a,t,{mode:"snapshot",baselineInitialized:!0,summary:{additions:0,removals:0,unchanged:r}}),n.set(i,a),{ok:!0,data:{mode:"snapshot",baselineInitialized:!0,summary:{additions:0,removals:0,unchanged:r},lines:[]}}}let u=function(e,t,i={}){let r=function(e,t){let i=e.length,r=t.length,n=i+r,a=new Map,o=[];a.set(1,0);for(let s=0;s<=n;s+=1){o.push(new Map(a));for(let n=-s;n<=s;n+=2){let l=n===-s||n!==s&&rs(a,n-1)<rs(a,n+1)?rs(a,n+1):rs(a,n-1)+1,d=l-n;for(;l<i&&d<r&&e[l].comparable===t[d].comparable;)l+=1,d+=1;if(a.set(n,l),l>=i&&d>=r)return function(e,t,i,r,n){let a=[],o=r,s=n;for(let r=e.length-1;r>=0;r-=1){let n=e[r],l=o-s,d=l===-r||l!==r&&rs(n,l-1)<rs(n,l+1)?l+1:l-1,c=rs(n,d),u=c-d;for(;o>c&&s>u;)a.push({kind:"unchanged",text:i[s-1].text}),o-=1,s-=1;if(0===r)break;o===c?(a.push({kind:"added",text:i[u].text}),s=u):(a.push({kind:"removed",text:t[c].text}),o=c)}return a.reverse(),a}(o,e,t,i,r)}}return[]}(ro(e,i),ro(t,i)),n={additions:0,removals:0,unchanged:0};for(let e of r)"added"===e.kind&&(n.additions+=1),"removed"===e.kind&&(n.removals+=1),"unchanged"===e.kind&&(n.unchanged+=1);return{summary:n,lines:r}}(e.snapshot.nodes,c.nodes,{flatten:l}),p={...e,snapshot:c};return rf(n,p,t,{mode:"snapshot",baselineInitialized:!1,summary:u.summary}),n.set(i,p),{ok:!0,data:{mode:"snapshot",baselineInitialized:!1,summary:u.summary,lines:u.lines}}})}if("wait"===o){let{session:e,device:o}=await ru(n,i,t.flags),s=function(e){if(0===e.length)return null;let t=rn(e[0]);if(null!==t)return{kind:"sleep",durationMs:t};if("text"===e[0]){let t=rn(e[e.length-1]);return{kind:"text",text:(null!==t?e.slice(1,-1).join(" "):e.slice(1).join(" ")).trim(),timeoutMs:t}}if(e[0].startsWith("@")){let t=rn(e[e.length-1]);return{kind:"ref",rawRef:e[0],timeoutMs:t}}let i=rn(e[e.length-1]),r=iT(null!==i?e.slice(0,-1):e.slice());if(r&&0===r.rest.length){let e=iE(r.selectorExpression);if(e)return{kind:"selector",selector:e,selectorExpression:r.selectorExpression,timeoutMs:i}}return{kind:"text",text:(null!==i?e.slice(0,-1).join(" "):e.join(" ")).trim(),timeoutMs:i}}(t.positionals??[]);return s?"sleep"===s.kind?(await new Promise(e=>setTimeout(e,s.durationMs)),rf(n,e,t,{waitedMs:s.durationMs}),{ok:!0,data:{waitedMs:s.durationMs}}):t6("wait",o)?await rp(e,o,async()=>{let l,d;if("selector"===s.kind){let l=s.timeoutMs??1e4,d=Date.now();for(;Date.now()-d<l;){let l=await a(o,"snapshot",[],t.flags?.out,{...id(r,{...t.flags,snapshotInteractiveOnly:!1,snapshotCompact:!1},e?.appBundleId,e?.trace?.outPath)}),c=l?.nodes??[],u=iw(t.flags?.snapshotRaw?c:iS(c));e&&(e.snapshot={nodes:u,truncated:l?.truncated,createdAt:Date.now(),backend:l?.backend},n.set(i,e));let p=iL(u,s.selector,{platform:o.platform});if(p)return rf(n,e,t,{selector:p.selector.raw,waitedMs:Date.now()-d}),{ok:!0,data:{selector:p.selector.raw,waitedMs:Date.now()-d}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for selector: ${s.selectorExpression}`}}}if("ref"===s.kind){if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref wait requires an existing snapshot in session."}};let t=ig(s.rawRef);if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref: ${s.rawRef}`}};let i=iv(e.snapshot.nodes,t),r=i?iA(i,e.snapshot.nodes):void 0;if(!r)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s.rawRef} not found or has no label`}};l=r,d=s.timeoutMs}else l=s.text,d=s.timeoutMs;if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires text"}};let c=d??1e4,u=Date.now();for(;Date.now()-u<c;){if("ios"===o.platform){let i=await t_(o,{command:"findText",text:l,appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath,requestId:t.meta?.requestId});if(i?.found)return rf(n,e,t,{text:l,waitedMs:Date.now()-u}),{ok:!0,data:{text:l,waitedMs:Date.now()-u}}}else if("android"===o.platform&&iy(iw((await eE(o,{scope:l})).nodes??[]),l))return rf(n,e,t,{text:l,waitedMs:Date.now()-u}),{ok:!0,data:{text:l,waitedMs:Date.now()-u}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for text: ${l}`}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"wait is not supported on this device"}}:{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires a duration or text"}}}if("alert"===o){let{session:e,device:a}=await ru(n,i,t.flags),o=(t.positionals?.[0]??"get").toLowerCase();return t6("alert",a)?await rp(e,a,async()=>{if("wait"===o){let i=rn(t.positionals?.[1])??1e4,o=Date.now();for(;Date.now()-o<i;){try{let i=await t_(a,{command:"alert",action:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath,requestId:t.meta?.requestId});return rf(n,e,t,i),{ok:!0,data:i}}catch{}await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"alert wait timed out"}}}let i=await t_(a,{command:"alert",action:"accept"===o||"dismiss"===o?o:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:r,traceLogPath:e?.trace?.outPath,requestId:t.meta?.requestId});return rf(n,e,t,i),{ok:!0,data:i}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"alert is only supported on iOS simulators"}}}if("settings"===o){let e=t.positionals?.[0],a=t.positionals?.[1];if(!e||!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"settings requires <wifi|airplane|location> <on|off> or faceid <match|nonmatch|enroll|unenroll>"}};let{session:o,device:s}=await ru(n,i,t.flags);return t6("settings",s)?await rp(o,s,async()=>{let i=o?.appBundleId,l=await t1(s,"settings",[e,a,i??""],t.flags?.out,{...id(r,t.flags,i,o?.trace?.outPath)});return rf(n,o,t,l??{setting:e,state:a}),{ok:!0,data:l??{setting:e,state:a}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"settings is not supported on this device"}}}return null}async function rd(e){let{dispatchSnapshotCommand:t,device:i,session:r,req:n,logPath:a,snapshotScope:o}=e,s=await t(i,"snapshot",[],n.flags?.out,{...id(a,{...n.flags,snapshotScope:o},r?.appBundleId,r?.trace?.outPath)}),l=s?.nodes??[];return{snapshot:{nodes:iw(n.flags?.snapshotRaw?l:iS(l)),truncated:s?.truncated,createdAt:Date.now(),backend:s?.backend}}}function rc(e,t){if(!e||!e.trim().startsWith("@"))return{ok:!0,scope:e};if(!t?.snapshot)return{ok:!1,response:{ok:!1,error:{code:"INVALID_ARGS",message:"Ref scope requires an existing snapshot in session."}}};let i=ig(e.trim());if(!i)return{ok:!1,response:{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref scope: ${e}`}}};let r=iv(t.snapshot.nodes,i),n=r?iA(r,t.snapshot.nodes):void 0;return n?{ok:!0,scope:n}:{ok:!1,response:{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${e} not found or has no label`}}}}async function ru(e,t,i){let r=e.get(t),n=r?.device??await t0(i??{});return r||await iu(n),{session:r,device:n}}async function rp(e,t,i){let r=!e&&"ios"===t.platform;try{return await i()}finally{r&&await tx(t.id)}}function rf(e,t,i,r){t&&e.recordAction(t,{command:i.command,positionals:i.positionals??[],flags:i.flags??{},result:r})}function rm(e,t,i,r={}){let n=rw(i);if(!n)return{matches:[],score:0};let a=0,o=[];for(let i of e){if(r.requireRect&&!i.rect)continue;let e=function(e,t,i){switch(t){case"role":return function(e,t){let i=function(e){let t=e.trim();return t?t=(t.split(".").pop()??t).replace(/XCUIElementType/gi,"").toLowerCase():""}(e??"");return i?i===t?2:+!!i.includes(t):0}(e.type,i);case"label":return rh(e.label,i);case"value":return rh(e.value,i);case"id":return rh(e.identifier,i);default:return Math.max(rh(e.label,i),rh(e.value,i),rh(e.identifier,i))}}(i,t,n);if(!(e<=0)){if(e>a){a=e,o.length=0,o.push(i);continue}e===a&&o.push(i)}}return{matches:o,score:a}}function rh(e,t){let i=rw(e??"");return i?i===t?2:+!!i.includes(t):0}function rw(e){return e.trim().toLowerCase().replace(/\s+/g," ")}async function rg(e){let{req:t,sessionName:i,logPath:r,sessionStore:n,invoke:a}=e,o=t.command;if("find"!==o)return null;let s=t.positionals??[];if(0===s.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a locator or text"}};let{locator:l,query:d,action:u,value:p,timeoutMs:f}=function(e){let t="any",i=0;["text","label","value","role","id"].includes(e[0])&&(t=e[0],i=1);let r=e[i]??"",n=e.slice(i+1);if(0===n.length)return{locator:t,query:r,action:"click"};let a=n[0].toLowerCase();if("get"===a){let e=n[1]?.toLowerCase();if("text"===e)return{locator:t,query:r,action:"get_text"};if("attrs"===e)return{locator:t,query:r,action:"get_attrs"};throw new c("INVALID_ARGS","find get only supports text or attrs")}if("wait"===a)return{locator:t,query:r,action:"wait",timeoutMs:rn(n[1])??void 0};if("exists"===a)return{locator:t,query:r,action:"exists"};if("click"===a)return{locator:t,query:r,action:"click"};if("focus"===a)return{locator:t,query:r,action:"focus"};if("fill"===a)return{locator:t,query:r,action:"fill",value:n.slice(1).join(" ")};if("type"===a)return{locator:t,query:r,action:"type",value:n.slice(1).join(" ")};throw new c("INVALID_ARGS",`Unsupported find action: ${n[0]}`)}(s);if(!d)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a value"}};let m=n.get(i);if(!m&&"exists"!==u&&"wait"!==u&&"get_text"!==u&&"get_attrs"!==u)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let h=m?.device??await t0(t.flags??{});m||await iu(h);let w=m?.appBundleId,g="role"!==l?d:void 0,v="click"===u||"focus"===u||"fill"===u||"type"===u,I=0,y=null,A=async()=>{let e=Date.now();if(y&&e-I<750)return{nodes:y};let a=await t1(h,"snapshot",[],t.flags?.out,{...id(r,{...t.flags,snapshotScope:g,snapshotInteractiveOnly:v,snapshotCompact:v},w,m?.trace?.outPath)}),o=a?.nodes??[],s=iw(t.flags?.snapshotRaw?o:iS(o));return I=e,y=s,m&&(m.snapshot={nodes:s,truncated:a?.truncated,createdAt:Date.now(),backend:a?.backend},n.set(i,m)),{nodes:s,truncated:a?.truncated,backend:a?.backend}};if("wait"===u){let e=f??1e4,i=Date.now();for(;Date.now()-i<e;){let{nodes:e}=await A();if(rm(e,l,d,{requireRect:!1}).matches[0])return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0,waitedMs:Date.now()-i}}),{ok:!0,data:{found:!0,waitedMs:Date.now()-i}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"find wait timed out"}}}let{nodes:N}=await A(),S=rm(N,l,d,{requireRect:v});if(v&&S.matches.length>1){let e=S.matches.slice(0,8).map(e=>{let t=iD(e)||e.label||e.identifier||e.type||"";return`@${e.ref}${t?`(${t})`:""}`});return{ok:!1,error:{code:"AMBIGUOUS_MATCH",message:`find matched ${S.matches.length} elements for ${l} "${d}". Use a more specific locator or selector.`,details:{locator:l,query:d,matches:S.matches.length,candidates:e}}}}let b=S.matches[0]??null;if(!b)return{ok:!1,error:{code:"COMMAND_FAILED",message:"find did not match any element"}};let _="click"===u||"focus"===u||"fill"===u||"type"===u?function(e,t){if(t.hittable)return t;let i=t,r=new Set;for(;void 0!==i.parentIndex&&!r.has(i.ref);){r.add(i.ref);let t=e[i.parentIndex];if(!t)break;if(t.hittable)return t;i=t}return null}(N,b)??b:b,D=`@${_.ref}`,M={...t.flags??{},noRecord:!0};if("exists"===u)return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0}}),{ok:!0,data:{found:!0}};if("get_text"===u){let e=iD(b);return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"get text",text:e}}),{ok:!0,data:{ref:D,text:e,node:b}}}if("get_attrs"===u)return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"get attrs"}}),{ok:!0,data:{ref:D,node:b}};if("click"===u){let e=await a({token:t.token,session:i,command:"click",positionals:[D],flags:M});return e.ok&&m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"click"}}),e}if("fill"===u){if(!p)return{ok:!1,error:{code:"INVALID_ARGS",message:"find fill requires text"}};let e=await a({token:t.token,session:i,command:"fill",positionals:[D,p],flags:M});return e.ok&&m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"fill"}}),e}if("focus"===u){let e=b.rect?iI(b.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};let i=await t1(h,"focus",[String(e.x),String(e.y)],t.flags?.out,{...id(r,t.flags,m?.appBundleId,m?.trace?.outPath)});return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"focus"}}),{ok:!0,data:i??{ref:D}}}if("type"===u){if(!p)return{ok:!1,error:{code:"INVALID_ARGS",message:"find type requires text"}};let e=b.rect?iI(b.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};await t1(h,"focus",[String(e.x),String(e.y)],t.flags?.out,{...id(r,t.flags,m?.appBundleId,m?.trace?.outPath)});let i=await t1(h,"type",[p],t.flags?.out,{...id(r,t.flags,m?.appBundleId,m?.trace?.outPath)});return m&&n.recordAction(m,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:D,action:"type"}}),{ok:!0,data:i??{ref:D}}}return null}let rv=function(e){let t=new Set,i=[];for(let r of e){if(!r)continue;let e=r.trim();!e||t.has(e)||(t.add(e),i.push(e))}return i}([process.env.AGENT_DEVICE_IOS_RUNNER_CONTAINER_BUNDLE_ID,process.env.AGENT_DEVICE_IOS_RUNNER_APP_BUNDLE_ID,"com.myapp.AgentDeviceRunnerUITests.xctrunner","com.myapp.AgentDeviceRunner"]);function rI(e){return e instanceof Error?e.message:String(e)}function ry(e){let t=e.appBundleId?.trim();return t&&t.length>0?t:void 0}function rA(e,t,i){return{verbose:e.flags?.verbose,logPath:t,traceLogPath:i.trace?.outPath}}async function rN(e){let{req:t,sessionName:r,sessionStore:n,logPath:o}=e,l=e.deps??{runCmd:s,runCmdBackground:a,runIosRunnerCommand:t_},d=t.command;if("record"===d){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"record requires start|stop"}};let a=n.get(r),s=a?.device??await t0(t.flags??{});a||await iu(s);let p=a??{name:r,device:s,createdAt:Date.now(),actions:[]};if("start"===e){if(p.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"recording already in progress"}};let e=t.flags?.fps;if(void 0!==e&&(!Number.isInteger(e)||e<1||e>120))return{ok:!1,error:{code:"INVALID_ARGS",message:"fps must be an integer between 1 and 120"}};let a=t.positionals?.[1]??`./recording-${Date.now()}.mp4`;if(!t6("record",s))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"record is not supported on this device"}};let f="ios"===s.platform&&"device"===s.kind?ry(p):void 0;if("ios"===s.platform&&"device"===s.kind&&!f)return{ok:!1,error:{code:"INVALID_ARGS",message:"record on physical iOS devices requires an active app session; run open <app> first"}};let m=il.expandHome(a,t.meta?.cwd);_.mkdirSync(i.dirname(m),{recursive:!0});let h=rA(t,o,p);if("ios"===s.platform&&"device"===s.kind){let t=`agent-device-recording-${Date.now()}.mp4`,i=`tmp/${t}`,r=async()=>{await l.runIosRunnerCommand(s,{command:"recordStart",outPath:t,fps:e,appBundleId:f},h)};try{await r()}catch(e){if(!rI(e).toLowerCase().includes("recording already in progress"))return{ok:!1,error:{code:"COMMAND_FAILED",message:`failed to start recording: ${rI(e)}`}};{var c,u;y({level:"warn",phase:"record_start_runner_desynced",data:{platform:s.platform,kind:s.kind,deviceId:s.id,session:p.name,error:rI(e)}});let t=(c=s.id,u=p.name,n.toArray().find(e=>e.name!==u&&"ios"===e.device.platform&&"device"===e.device.kind&&e.device.id===c&&e.recording?.platform==="ios-device-runner"));if(t)return{ok:!1,error:{code:"COMMAND_FAILED",message:`failed to start recording: recording already in progress in session '${t.name}'`}};try{await l.runIosRunnerCommand(s,{command:"recordStop",appBundleId:f},h)}catch{}try{await r()}catch(e){return{ok:!1,error:{code:"COMMAND_FAILED",message:`failed to start recording: ${rI(e)}`}}}}}p.recording={platform:"ios-device-runner",outPath:m,remotePath:i}}else if("ios"===s.platform){let{child:e,wait:t}=l.runCmdBackground("xcrun",["simctl","io",s.id,"recordVideo",m],{allowFailure:!0});p.recording={platform:"ios",outPath:m,child:e,wait:t}}else{let e=`/sdcard/agent-device-recording-${Date.now()}.mp4`,{child:t,wait:i}=l.runCmdBackground("adb",["-s",s.id,"shell","screenrecord",e],{allowFailure:!0});p.recording={platform:"android",outPath:m,remotePath:e,child:t,wait:i}}return n.set(r,p),n.recordAction(p,{command:d,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start"}}),{ok:!0,data:{recording:"started",outPath:a}}}if(!p.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active recording"}};let f=p.recording;if("ios-device-runner"===f.platform){let e=ry(p);try{await l.runIosRunnerCommand(s,{command:"recordStop",appBundleId:e},rA(t,o,p))}catch(e){y({level:"warn",phase:"record_stop_runner_failed",data:{platform:s.platform,kind:s.kind,deviceId:s.id,session:p.name,error:rI(e)}})}let i={stdout:"",stderr:"",exitCode:1};for(let e of rv)if(0===(i=await l.runCmd("xcrun",["devicectl","device","copy","from","--device",s.id,"--source",f.remotePath,"--destination",f.outPath,"--domain-type","appDataContainer","--domain-identifier",e],{allowFailure:!0})).exitCode)break;if(p.recording=void 0,0!==i.exitCode){let e=i.stderr.trim()||i.stdout.trim()||`devicectl exited with code ${i.exitCode}`;return{ok:!1,error:{code:"COMMAND_FAILED",message:`failed to copy recording from device: ${e}`}}}}else{f.child.kill("SIGINT");try{await f.wait}catch{}if("android"===f.platform&&f.remotePath)try{await l.runCmd("adb",["-s",s.id,"pull",f.remotePath,f.outPath],{allowFailure:!0}),await l.runCmd("adb",["-s",s.id,"shell","rm","-f",f.remotePath],{allowFailure:!0})}catch{}p.recording=void 0}return n.recordAction(p,{command:d,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:f.outPath}}),{ok:!0,data:{recording:"stopped",outPath:f.outPath}}}if("trace"===d){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"trace requires start|stop"}};let a=n.get(r);if(!a)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}};if("start"===e){if(a.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"trace already in progress"}};let e=t.positionals?.[1]??n.defaultTracePath(a),r=il.expandHome(e);return _.mkdirSync(i.dirname(r),{recursive:!0}),_.appendFileSync(r,""),a.trace={outPath:r,startedAt:Date.now()},n.recordAction(a,{command:d,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start",outPath:r}}),{ok:!0,data:{trace:"started",outPath:r}}}if(!a.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active trace"}};let o=a.trace.outPath;if(t.positionals?.[1]){let e=il.expandHome(t.positionals[1]);_.mkdirSync(i.dirname(e),{recursive:!0}),_.existsSync(o)?_.renameSync(o,e):_.appendFileSync(e,""),o=e}return a.trace=void 0,n.recordAction(a,{command:d,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:o}}),{ok:!0,data:{trace:"stopped",outPath:o}}}return null}function rS(e,t,i){return t>=e.x&&t<=e.x+e.width&&i>=e.y&&i<=e.y+e.height}function rb(e){let t=null,i=-1;for(let r of e){let e=r.width*r.height;e>i&&(t=r,i=e)}return t}function r_(e,t,i){return Math.min(i,Math.max(t,Math.round(e)))}async function rD(e){let{req:t,sessionName:i,sessionStore:r,contextFromFlags:n}=e,a=e.dispatch??t1,o=t.command;if("press"===o){let e=r.get(i);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=function(e){if(e.length<2)return null;let t=Number(e[0]),i=Number(e[1]);return Number.isFinite(t)&&Number.isFinite(i)?{x:t,y:i}:null}(t.positionals??[]);if(s){let i=await a(e.device,"press",[String(s.x),String(s.y)],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)});return r.recordAction(e,{command:o,positionals:t.positionals??[String(s.x),String(s.y)],flags:t.flags??{},result:i??{x:s.x,y:s.y}}),{ok:!0,data:i??{x:s.x,y:s.y}}}let l="click",d=t.positionals?.[0]??"";if(d.startsWith("@")){let i=rx("press",t.flags);if(i)return i;let s=rk({session:e,refInput:d,fallbackLabel:t.positionals.length>1?t.positionals.slice(1).join(" ").trim():"",requireRect:!0,invalidRefMessage:`${o} requires a ref like @e2`,notFoundMessage:`Ref ${d} not found or has no bounds`});if(!s.ok)return s.response;let{ref:c,node:u,snapshotNodes:p}=s.target;if(!u.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${d} not found or has no bounds`}};let f=iA(u,p),m=iV(u,e.device.platform,{action:l}),{x:h,y:w}=iI(u.rect),g=await a(e.device,"press",[String(h),String(w)],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)});return r.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:c,x:h,y:w,refLabel:f,selectorChain:m}}),{ok:!0,data:{...g??{},ref:c,x:h,y:w}}}let c=(t.positionals??[]).join(" ").trim();if(!c)return{ok:!1,error:{code:"INVALID_ARGS",message:`${o} requires @ref, selector expression, or x y coordinates`}};let u=ik(c),p=await rM(e,t.flags,r,n,{interactiveOnly:!0},a),f=await I("selector_resolve",()=>iC(p.nodes,u,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0}),{command:o});if(!f||!f.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:iR(u,f?.diagnostics??[],{unique:!0})}};let{x:m,y:h}=iI(f.node.rect),w=await a(e.device,"press",[String(m),String(h)],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)}),g=iV(f.node,e.device.platform,{action:l}),v=iA(f.node,p.nodes);return r.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{x:m,y:h,selector:f.selector.raw,selectorChain:g,refLabel:v}}),{ok:!0,data:{...w??{},selector:f.selector.raw,x:m,y:h}}}if("fill"===o){let e=r.get(i);if(t.positionals?.[0]?.startsWith("@")){if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let i=rx("fill",t.flags);if(i)return i;let s=t.positionals.length>=3?t.positionals[1]:"",l=t.positionals.length>=3?t.positionals.slice(2).join(" "):t.positionals.slice(1).join(" ");if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after ref"}};let d=rk({session:e,refInput:t.positionals[0],fallbackLabel:s,requireRect:!0,invalidRefMessage:"fill requires a ref like @e2",notFoundMessage:`Ref ${t.positionals[0]} not found or has no bounds`});if(!d.ok)return d.response;let{ref:c,node:u,snapshotNodes:p}=d.target;if(!u.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${t.positionals[0]} not found or has no bounds`}};let f=u.type??"",m=f&&!i_(f,e.device.platform)?`fill target ${t.positionals[0]} resolved to "${f}", attempting fill anyway.`:void 0,h=iA(u,p),w=iV(u,e.device.platform,{action:"fill"}),{x:g,y:v}=iI(u.rect),I={...await a(e.device,"fill",[String(g),String(v),l],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)})??{ref:c,x:g,y:v}};return m&&(I.warning=m),r.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{...I,refLabel:h,selectorChain:w}}),{ok:!0,data:I}}if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=iT(t.positionals??[],{preferTrailingValue:!0});if(s){if(0===s.rest.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let i=s.rest.join(" ").trim();if(!i)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let l=ik(s.selectorExpression),d=await rM(e,t.flags,r,n,{interactiveOnly:!0},a),c=await I("selector_resolve",()=>iC(d.nodes,l,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0}),{command:o});if(!c||!c.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:iR(l,c?.diagnostics??[],{unique:!0})}};let u=c.node,p=u.type??"",f=p&&!i_(p,e.device.platform)?`fill target ${c.selector.raw} resolved to "${p}", attempting fill anyway.`:void 0,{x:m,y:h}=iI(c.node.rect),w=await a(e.device,"fill",[String(m),String(h),i],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath)}),g=iV(u,e.device.platform,{action:"fill"}),v={...w??{x:m,y:h,text:i},selector:c.selector.raw,selectorChain:g,refLabel:iA(u,d.nodes)};return f&&(v.warning=f),r.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:v}),{ok:!0,data:v}}return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires x y text, @ref text, or selector text"}}}if("get"===o){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)return{ok:!1,error:{code:"INVALID_ARGS",message:"get only supports text or attrs"}};let s=r.get(i);if(!s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let l=t.positionals?.[1]??"";if(l.startsWith("@")){let i=rx("get",t.flags);if(i)return i;let n=rk({session:s,refInput:l,fallbackLabel:t.positionals.length>2?t.positionals.slice(2).join(" ").trim():"",requireRect:!1,invalidRefMessage:"get text requires a ref like @e2",notFoundMessage:`Ref ${l} not found`});if(!n.ok)return n.response;let{ref:a,node:d}=n.target,c=iV(d,s.device.platform,{action:"get"});if("attrs"===e)return r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:a,selectorChain:c}}),{ok:!0,data:{ref:a,node:d}};let u=iD(d);return r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:a,text:u,refLabel:u||void 0,selectorChain:c}}),{ok:!0,data:{ref:a,text:u,node:d}}}let d=t.positionals.slice(1).join(" ").trim();if(!d)return{ok:!1,error:{code:"INVALID_ARGS",message:"get requires @ref or selector expression"}};let c=ik(d),u=await rM(s,t.flags,r,n,{interactiveOnly:!1},a),p=await I("selector_resolve",()=>iC(u.nodes,c,{platform:s.device.platform,requireRect:!1,requireUnique:!0,disambiguateAmbiguous:"text"===e}),{command:o});if(!p)return{ok:!1,error:{code:"COMMAND_FAILED",message:iR(c,[],{unique:!0})}};let f=p.node,m=iV(f,s.device.platform,{action:"get"});if("attrs"===e)return r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{selector:p.selector.raw,selectorChain:m}}),{ok:!0,data:{selector:p.selector.raw,node:f}};let h=iD(f);return r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{text:h,refLabel:h||void 0,selector:p.selector.raw,selectorChain:m}}),{ok:!0,data:{selector:p.selector.raw,text:h,node:f}}}if("is"===o){let e=(t.positionals?.[0]??"").toLowerCase();if(!["visible","hidden","exists","editable","selected","text"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires predicate: visible|hidden|exists|editable|selected|text"}};let s=r.get(i);if(!s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!t6("is",s.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"is is not supported on this device"}};let{split:l}=iP(t.positionals);if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires a selector expression"}};let d=l.rest.join(" ").trim();if("text"===e&&!d)return{ok:!1,error:{code:"INVALID_ARGS",message:"is text requires expected text value"}};if("text"!==e&&l.rest.length>0)return{ok:!1,error:{code:"INVALID_ARGS",message:`is ${e} does not accept trailing values`}};let c=ik(l.selectorExpression),u=await rM(s,t.flags,r,n,{interactiveOnly:!1},a);if("exists"===e){let i=iL(u.nodes,c,{platform:s.device.platform});return i?(r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:i.selector.raw,selectorChain:c.selectors.map(e=>e.raw),pass:!0,matches:i.matches}}),{ok:!0,data:{predicate:e,pass:!0,selector:i.selector.raw,matches:i.matches}}):{ok:!1,error:{code:"COMMAND_FAILED",message:iR(c,[],{unique:!1})}}}let p=await I("selector_resolve",()=>iC(u.nodes,c,{platform:s.device.platform,requireUnique:!0}),{command:"is",predicate:e});if(!p)return{ok:!1,error:{code:"COMMAND_FAILED",message:iR(c,[],{unique:!0})}};let f=function(e){let{predicate:t,node:i,expectedText:r,platform:n}=e,a=iD(i),o=!1;switch(t){case"visible":o=i$(i);break;case"hidden":o=!i$(i);break;case"editable":o=iF(i,n);break;case"selected":o=!0===i.selected;break;case"text":o=a===(r??"")}let s="text"===t?`expected="${r??""}" actual="${a}"`:`actual=${JSON.stringify({visible:i$(i),editable:iF(i,n),selected:!0===i.selected})}`;return{pass:o,actualText:a,details:s}}({predicate:e,node:p.node,expectedText:d,platform:s.device.platform});return f.pass?(r.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:p.selector.raw,selectorChain:c.selectors.map(e=>e.raw),pass:!0,text:"text"===e?f.actualText:void 0}}),{ok:!0,data:{predicate:e,pass:!0,selector:p.selector.raw}}):{ok:!1,error:{code:"COMMAND_FAILED",message:`is ${e} failed for selector ${p.selector.raw}: ${f.details}`}}}if("scrollintoview"===o){let e=r.get(i);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=t.positionals?.[0]??"";if(!s.startsWith("@"))return null;let l=rx("scrollintoview",t.flags);if(l)return l;let d=rk({session:e,refInput:s,fallbackLabel:t.positionals&&t.positionals.length>1?t.positionals.slice(1).join(" ").trim():"",requireRect:!0,invalidRefMessage:"scrollintoview requires a ref like @e2",notFoundMessage:`Ref ${s} not found or has no bounds`});if(!d.ok)return d.response;let{ref:c,node:u,snapshotNodes:p}=d.target;if(!u.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s} not found or has no bounds`}};let f=function(e,t){let i=iI(t),r=e.filter(e=>{var t;return!!(t=e.rect)&&Number.isFinite(t.x)&&Number.isFinite(t.y)&&Number.isFinite(t.width)&&Number.isFinite(t.height)}),n=r.filter(e=>{let t=(e.type??"").toLowerCase();return t.includes("application")||t.includes("window")}),a=rb(n.map(e=>e.rect).filter(e=>rS(e,i.x,i.y)));if(a)return a;let o=rb(n.map(e=>e.rect));if(o)return o;let s=rb(r.map(e=>e.rect).filter(e=>rS(e,i.x,i.y)));return s||null}(p,u.rect);if(!f)return{ok:!1,error:{code:"COMMAND_FAILED",message:`scrollintoview could not infer viewport for ${s}`}};let m=function(e,t){var i,r;let n=Math.max(1,t.height),a=Math.max(1,t.width),o=t.y,s=t.y+n,l=t.x,d=t.x+a,c=o+.25*n,u=s-.25*n,p=Math.max(8,.1*a),f=e.y+e.height/2,m=e.x+e.width/2;if(f>=c&&f<=u)return null;let h=Math.round((i=m,r=l+p,Math.min(d-p,Math.max(r,i)))),w=Math.round(o+.86*n),g=Math.round(o+.14*n),v=Math.max(1,Math.abs(w-g));return f>u?{x:h,startY:w,endY:g,count:r_(Math.ceil((f-u)/v),1,50),direction:"down"}:{x:h,startY:g,endY:w,count:r_(Math.ceil((c-f)/v),1,50),direction:"up"}}(u.rect,f),h=iA(u,p),w=iV(u,e.device.platform,{action:"get"});if(!m)return r.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:c,attempts:0,alreadyVisible:!0,refLabel:h,selectorChain:w}}),{ok:!0,data:{ref:c,attempts:0,alreadyVisible:!0}};let g=await a(e.device,"swipe",[String(m.x),String(m.startY),String(m.x),String(m.endY),"16"],t.flags?.out,{...n(t.flags,e.appBundleId,e.trace?.outPath),count:m.count,pauseMs:0,pattern:"one-way"});return r.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{...g??{},ref:c,attempts:m.count,direction:m.direction,refLabel:h,selectorChain:w}}),{ok:!0,data:{...g??{},ref:c,attempts:m.count,direction:m.direction}}}return null}async function rM(e,t,i,r,n,a=t1){let o=await a(e.device,"snapshot",[],t?.out,{...r({...t??{},snapshotInteractiveOnly:n.interactiveOnly,snapshotCompact:n.interactiveOnly},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[];return e.snapshot={nodes:iw(t?.snapshotRaw?s:iS(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend},i.set(e.name,e),e.snapshot}let rO=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"]];function rx(e,t){let i=function(e){if(!e)return[];let t=[];for(let[i,r]of rO)void 0!==e[i]&&t.push(r);return t}(t);return 0===i.length?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} @ref does not support ${i.join(", ")}.`}}}function rk(e){let{session:t,refInput:i,fallbackLabel:r,requireRect:n,invalidRefMessage:a,notFoundMessage:o}=e;if(!t.snapshot)return{ok:!1,response:{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}}};let s=ig(i);if(!s)return{ok:!1,response:{ok:!1,error:{code:"INVALID_ARGS",message:a}}};let l=iv(t.snapshot.nodes,s);return((!l||n&&!l.rect)&&r.length>0&&(l=iy(t.snapshot.nodes,r)),l&&(!n||l.rect))?{ok:!0,target:{ref:s,node:l,snapshotNodes:t.snapshot.nodes}}:{ok:!1,response:{ok:!1,error:{code:"COMMAND_FAILED",message:o}}}}let rE=i.join(O.homedir(),".agent-device"),rC=i.join(rE,"daemon.json"),rL=i.join(rE,"daemon.lock"),rR=i.join(rE,"daemon.log"),rT=new il(i.join(rE,"sessions")),rP=g(),r$=o.randomBytes(24).toString("hex"),rF=new Set(["session_list","devices"]),rV=b(process.pid)??void 0;function rU(e,t,i){let r=v().requestId;return{...id(rR,e,t,i,r),requestId:r}}async function rG(e){let t=!!(e.meta?.debug||e.flags?.verbose);return await D({session:e.session,requestId:e.meta?.requestId,command:e.command,debug:t,logPath:rR},async()=>{if(e.token!==r$)return{ok:!1,error:w(new c("UNAUTHORIZED","Invalid token"))};y({level:"info",phase:"request_start",data:{session:e.session,command:e.command}});try{var t;let i=(t=e,"click"!==t.command?t:{...t,command:"press"}),r=i.command,n=function(e,t){var i;let r,n=e.session||"default";if(i=e,r=i.flags?.session,"string"==typeof r&&r.trim().length>0||"default"!==n||t.has(n))return n;let a=t.toArray();return 1===a.length?a[0].name:n}(i,rT),a=rT.get(n);a&&!rF.has(r)&&function(e,t){if(!t)return;let i=[],r=e.device;if(t.platform&&t.platform!==r.platform&&i.push(`--platform=${t.platform}`),t.udid&&("ios"!==r.platform||t.udid!==r.id)&&i.push(`--udid=${t.udid}`),t.serial&&("android"!==r.platform||t.serial!==r.id)&&i.push(`--serial=${t.serial}`),t.device&&t.device.trim().toLowerCase()!==r.name.trim().toLowerCase()&&i.push(`--device=${t.device}`),0!==i.length){var n;let t,r,a;throw new c("INVALID_ARGS",`Session "${e.name}" is bound to ${(t=(n=e).device.platform,r=n.device.name.trim(),a=n.device.id,`${t} device "${r}" (${a})`)} and cannot be used with ${i.join(", ")}. Use a different --session name or close this session first.`)}}(a,i.flags);let o=await i5({req:i,sessionName:n,logPath:rR,sessionStore:rT,invoke:rG});if(o)return rq(o);let s=await rl({req:i,sessionName:n,logPath:rR,sessionStore:rT});if(s)return rq(s);let l=await rN({req:e,sessionName:n,sessionStore:rT,logPath:rR});if(l)return rq(l);let d=await rg({req:i,sessionName:n,logPath:rR,sessionStore:rT,invoke:rG});if(d)return rq(d);let u=await rD({req:i,sessionName:n,sessionStore:rT,contextFromFlags:rU});if(u)return rq(u);let p=rT.get(n);if(!p)return rq({ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}});if(!t6(r,p.device))return rq({ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:`${r} is not supported on this device`}});let f=await t1(p.device,r,i.positionals??[],i.flags?.out,{...rU(i.flags,p.appBundleId,p.trace?.outPath)});return rT.recordAction(p,{command:r,positionals:i.positionals??[],flags:i.flags??{},result:f??{}}),rq({ok:!0,data:f??{}})}catch(i){y({level:"error",phase:"request_failed",data:{error:i instanceof Error?i.message:String(i)}});let e=v(),t=M({force:!0})??void 0;return{ok:!1,error:w(i,{diagnosticId:e.diagnosticId,logPath:t})}}})}function rq(e){let t=v();if(!e.ok){y({level:"error",phase:"request_failed",data:{code:e.error.code,message:e.error.message}});let i=M({force:!0})??void 0;return{ok:!1,error:w(new c(e.error.code,e.error.message,{...e.error.details??{},hint:e.error.hint,diagnosticId:e.error.diagnosticId,logPath:e.error.logPath}),{diagnosticId:t.diagnosticId,logPath:i})}}return y({level:"info",phase:"request_success"}),M(),e}function rB(){if(!_.existsSync(rL))return null;try{let e=JSON.parse(_.readFileSync(rL,"utf8"));if(!Number.isInteger(e.pid)||e.pid<=0)return null;return e}catch{return null}}!function(){if(!function(){_.existsSync(rE)||_.mkdirSync(rE,{recursive:!0});let e=JSON.stringify({pid:process.pid,version:rP,startedAt:Date.now(),processStartTime:rV},null,2),t=()=>{try{return _.writeFileSync(rL,e,{flag:"wx",mode:384}),!0}catch(e){if("EEXIST"===e.code)return!1;throw e}};if(t())return!0;let i=rB();if(i?.pid&&i.pid!==process.pid&&n(i.pid,i.processStartTime))return!1;try{_.unlinkSync(rL)}catch{}return t()}()){process.stderr.write("Daemon lock is held by another process; exiting.\n"),process.exit(0);return}let e=x.createServer(e=>{let t="",i=0,r=new Set,n=!1,a=()=>{if(!n&&0!==i){for(let e of(n=!0,r))e&&tu.add(e);y({level:"warn",phase:"request_client_disconnected",data:{inFlightRequests:i}}),(async()=>{let e=Date.now()+15e3;for(;i>0&&Date.now()<e&&(await tk(),!(i<=0));)await new Promise(e=>setTimeout(e,200))})()}};e.setEncoding("utf8"),e.on("close",a),e.on("error",a),e.on("data",async n=>{let a=(t+=n).indexOf("\n");for(;-1!==a;){let n,s,l=t.slice(0,a).trim();if(t=t.slice(a+1),0===l.length){a=t.indexOf("\n");continue}i+=1;try{let e=JSON.parse(l);if((s=e.meta?.requestId)&&(r.add(s),tp(s)))throw new c("COMMAND_FAILED","request canceled");n=await rG(e)}catch(e){n={ok:!1,error:w(e)}}finally{if(i-=1,s){var o;r.delete(s),(o=s)&&tu.delete(o)}}e.destroyed||e.write(`${JSON.stringify(n)}
|
|
16
|
+
`),a=t.indexOf("\n")}})});e.listen(0,"127.0.0.1",()=>{let t=e.address();if("object"==typeof t&&t?.port){var i;i=t.port,_.existsSync(rE)||_.mkdirSync(rE,{recursive:!0}),_.writeFileSync(rR,""),_.writeFileSync(rC,JSON.stringify({port:i,token:r$,pid:process.pid,version:rP,processStartTime:rV},null,2),{mode:384}),process.stdout.write(`AGENT_DEVICE_DAEMON_PORT=${t.port}
|
|
17
|
+
`)}});let t=!1,i=async()=>{await new Promise(t=>{try{e.close(()=>t())}catch{t()}})},r=async()=>{if(t)return;for(let e of(t=!0,await i(),rT.toArray()))rT.writeSessionLog(e);await tE(),_.existsSync(rC)&&_.unlinkSync(rC);let e=rB();if(!e||e.pid===process.pid)try{_.existsSync(rL)&&_.unlinkSync(rL)}catch{}process.exit(0)};process.on("SIGINT",()=>{r()}),process.on("SIGTERM",()=>{r()}),process.on("SIGHUP",()=>{r()}),process.on("uncaughtException",e=>{let t=e instanceof c?e:d(e);process.stderr.write(`Daemon error: ${t.message}
|
|
18
18
|
`),r()})}();
|