agent-device 0.15.1 → 0.16.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.
Files changed (45) hide show
  1. package/README.md +51 -155
  2. package/android-multitouch-helper/README.md +41 -0
  3. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.0.apk +0 -0
  4. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.0.apk.sha256 +1 -0
  5. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.0.manifest.json +10 -0
  6. package/android-snapshot-helper/README.md +2 -0
  7. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.0.apk +0 -0
  8. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.0.apk.sha256 +1 -0
  9. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.15.1.manifest.json → agent-device-android-snapshot-helper-0.16.0.manifest.json} +6 -6
  10. package/dist/src/1231.js +1 -1
  11. package/dist/src/1769.js +7 -7
  12. package/dist/src/2099.js +1 -0
  13. package/dist/src/221.js +4 -4
  14. package/dist/src/3622.js +3 -0
  15. package/dist/src/7519.js +1 -0
  16. package/dist/src/7556.js +1 -1
  17. package/dist/src/89.js +1 -0
  18. package/dist/src/940.js +1 -1
  19. package/dist/src/9542.js +2 -2
  20. package/dist/src/9639.js +2 -2
  21. package/dist/src/989.js +1 -1
  22. package/dist/src/android-adb.d.ts +26 -0
  23. package/dist/src/android-adb.js +1 -1
  24. package/dist/src/android-snapshot-helper.d.ts +30 -0
  25. package/dist/src/batch.d.ts +9 -9
  26. package/dist/src/cli.js +494 -76
  27. package/dist/src/index.d.ts +48 -5
  28. package/dist/src/internal/daemon.js +69 -44
  29. package/dist/src/server.js +2 -2
  30. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h +1 -0
  31. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +19 -0
  32. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +297 -0
  33. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +144 -5
  34. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +333 -23
  35. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +3 -1
  36. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +8 -0
  37. package/package.json +9 -3
  38. package/server.json +2 -2
  39. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.15.1.apk +0 -0
  40. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.15.1.apk.sha256 +0 -1
  41. package/dist/src/1393.js +0 -1
  42. package/dist/src/2151.js +0 -434
  43. package/dist/src/3572.js +0 -1
  44. package/dist/src/7599.js +0 -3
  45. package/dist/src/9671.js +0 -1
package/dist/src/2151.js DELETED
@@ -1,434 +0,0 @@
1
- let e,t,a,n,r,i,s,o;import{promises as l}from"node:fs";import{PNG as c}from"pngjs";import d from"node:path";import{normalizeRef as p,centerOfRect as u,buildSnapshotPresentationKey as h,findNodeByRef as m}from"./4057.js";import{isScrollableNodeLike as f}from"./2842.js";import{asAppError as g,AppError as b}from"./9152.js";import{SCREENSHOT_SPECIFIC_FLAG_DEFINITIONS as w,SCREENSHOT_COMMAND_FLAG_KEYS as y,PUBLIC_COMMANDS as v,ALL_DEVICE_COMMAND_CAPABILITY as k,defineCommand as x,INTERACTION_COMMAND_SCHEMAS as A,successText as D,commandCapabilityMap as I,commandSchemaMap as R}from"./3572.js";import{resolveAppsFilter as S,DEFAULT_APPS_FILTER as M,assertResolvedAppsFilter as N}from"./1393.js";import{whichCmd as O,runCmd as L}from"./9818.js";import{trimText as P,extractNodeText as E,parseSelectorChain as C,isNodeVisible as T,isNodeEditable as U,buildTextPreview as _,findNearestHittableAncestor as $,buildSelectorChainForNode as F,findSelectorChainMatch as B,resolveSelectorChain as q,findNodeByLabel as G,resolveRefLabel as j,describeTextSurface as V,extractReadableText as X,formatSelectorFailure as Y,normalizeType as K,isFillableType as z}from"./940.js";import{findBestMatchesByLocator as H}from"./7556.js";import"./7847.js";function J(e,t,a){return t>=e.x&&t<=e.x+e.width&&a>=e.y&&a<=e.y+e.height}function W(e){let t=null,a=-1;for(let n of e){let e=n.width*n.height;e>a&&(t=n,a=e)}return t}function Q(e,t,a,n){return Math.max(e,a)<=Math.min(t,n)}function Z(e){let t=new Map;for(let[a,n]of e.entries())t.set(n.index,a);let a=[],n=[];for(let[r,i]of e.entries()){let e=Math.max(0,i.depth??0);for(;n.length>0&&e<=n[n.length-1].depth;)n.pop();let s="number"==typeof i.parentIndex?t.get(i.parentIndex):void 0,o="number"==typeof s&&s<r?s:n[n.length-1]?.index;a.push({...i,index:r,depth:e,parentIndex:o}),n.push({depth:e,index:r})}return a}function ee(e){return new Map(e.map(e=>[e.index,e]))}function et(e){return e.label?.trim()||e.value?.trim()||e.identifier?.trim()||""}function ea(e){if(0===e.length)return{nodes:e,hiddenCount:0,summaryLines:[]};let{byIndex:t,visibleNodeIndexes:a,offscreenNodes:n,hintedContainers:r}=er(e),i=0===a.size?e:e.filter(e=>a.has(e.index));return{nodes:i.map(e=>(function(e,t){let a=t.get(e.index);if(!a||0===a.size)return e;let n=!!(!0===e.hiddenContentAbove||a.has("above"))||void 0,r=!!(!0===e.hiddenContentBelow||a.has("below"))||void 0;return{...e,hiddenContentAbove:n,hiddenContentBelow:r}})(e,r.directionsByContainer)),hiddenCount:0===a.size?0:e.length-i.length,summaryLines:function(e,t,a){let n=new Map;for(let r of e){let e=function(e,t,a){if(!e.rect)return null;let n=es(e,t,a);return n?eo(e.rect,n):null}(r,t,a);if(!e)continue;let i=n.get(e)??[];i.push(r),n.set(e,i)}return["above","below"].flatMap(e=>{let t=n.get(e);if(!t||0===t.length)return[];let a=(function(e){let t=new Set,a=[];for(let n of e){let e=et(n);!e||t.has(e)||(t.add(e),a.push(e))}return a})(t).slice(0,3).map(e=>`"${e}"`),r=1===t.length?"interactive item":"interactive items",i=a.length>0?`: ${a.join(", ")}`:"";return[`[off-screen ${e}] ${t.length} ${r}${i}`]})}(n.filter(e=>!r.coveredNodeIndexes.has(e.index)&&function(e){if(!0===e.hittable)return!0;let t=(e.type??"").toLowerCase();return t.includes("button")||t.includes("link")||t.includes("textfield")||t.includes("edittext")||t.includes("searchfield")||t.includes("checkbox")||t.includes("radio")||t.includes("switch")||t.includes("menuitem")||!!et(e)}(e)),e,t)}}function en(e){if(0===e.length)return new Map;let{hintedContainers:t}=er(e);var a=t.directionsByContainer;let n=new Map;for(let[e,t]of a){let a={};t.has("above")&&(a.hiddenContentAbove=!0),t.has("below")&&(a.hiddenContentBelow=!0),(a.hiddenContentAbove||a.hiddenContentBelow)&&n.set(e,a)}return n}function er(e){let t=ee(e),a=new Set,n=[];for(let r of e){if(ei(r,e,t)){!function(e,t,a){let n=e,r=new Set;for(;n&&!r.has(n.index);)r.add(n.index),t.add(n.index),n="number"==typeof n.parentIndex?a.get(n.parentIndex):void 0}(r,a,t);continue}n.push(r)}let r=function(e,t,a,n){let r=new Map,i=new Map,s=new Set;for(let e of t){if(!e.rect)continue;let t=el(e,a,n);if(!t?.rect)continue;let o=eo(e.rect,t.rect);if(!o)continue;let l=r.get(t.index)??new Set;l.add(o),r.set(t.index,l);let c=i.get(t.index)??new Set;c.add(o),i.set(t.index,c),s.add(e.index)}return function(e,t,a,n,r){for(let i of e){let e=function(e){let t=function(e,t){if(!(e?.trim().toLowerCase()??"").includes("vertical scroll bar"))return null;let a=function(e){if(!e)return null;let t=/^(\d{1,3})%$/.exec(e.trim());if(!t)return null;let a=Number(t[1]);return Number.isFinite(a)?Math.max(0,Math.min(100,a)):null}(t);return null===a?null:a<=1?{above:!1,below:!0}:a>=99?{above:!0,below:!1}:{above:!0,below:!0}}(e.label,e.value);if(!t)return null;let a=new Set;return t.above&&a.add("above"),t.below&&a.add("below"),a.size>0?a:null}(i);if(!e||0===e.size)continue;let s=el(i,t,a);if(!s)continue;let o=n.get(s.index)??new Set,l=r.get(s.index);for(let t of e)(!l||!(l.size>0)||l.has(t))&&o.add(t);n.set(s.index,o)}}(e,a,n,r,i),{directionsByContainer:r,coveredNodeIndexes:s}}(e,n,a,t);return{byIndex:t,visibleNodeIndexes:a,offscreenNodes:n,hintedContainers:r}}function ei(e,t,a=ee(t)){var n;if(!e.rect)return!0;let r=es(e,t,a);return!r||(n=e.rect,Q(n.x,n.x+n.width,r.x,r.x+r.width)&&Q(n.y,n.y+n.height,r.y,r.y+r.height))}function es(e,t,a=ee(t)){var n,r;let i=(n=e,r=a,ec(n,r,e=>!!e.rect)?.rect??null);return i||function(e,t){let a=u(t),n=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)}),r=n.filter(e=>{let t=(e.type??"").toLowerCase();return t.includes("application")||t.includes("window")}),i=W(r.map(e=>e.rect).filter(e=>J(e,a.x,a.y)));if(i)return i;let s=W(r.map(e=>e.rect));if(s)return s;let o=W(n.map(e=>e.rect).filter(e=>J(e,a.x,a.y)));return o||null}(t,e.rect??{x:0,y:0,width:0,height:0})}function eo(e,t){return e.y+e.height<=t.y?"above":e.y>=t.y+t.height?"below":null}function el(e,t,a){return ec(e,a,e=>t.has(e.index))}function ec(e,t,a){let n="number"==typeof e.parentIndex?t.get(e.parentIndex):void 0,r=new Set;for(;n&&!r.has(n.index);){if(r.add(n.index),a(n)&&f(n))return n;n="number"==typeof n.parentIndex?t.get(n.parentIndex):void 0}return null}let ed="<wifi|airplane|location> <on|off>",ep="location set <lat> <lon>",eu="animations <on|off>",eh="appearance <light|dark|toggle>",em="faceid <match|nonmatch|enroll|unenroll>",ef="touchid <match|nonmatch|enroll|unenroll>",eg="fingerprint <match|nonmatch>",eb="permission <grant|deny|reset> <camera|microphone|photos|contacts|contacts-limited|notifications|calendar|location|location-always|media-library|motion|reminders|siri> [full|limited]",ew="permission <grant|reset> <accessibility|screen-recording|input-monitoring>",ey=`macOS supports only settings ${eh} and settings ${ew}. wifi|airplane|location|animations remain unsupported on macOS.`,ev=`settings ${ed} | settings ${ep} | settings ${eu} | settings ${eh} | settings ${em} | settings ${ef} | settings ${eg} | settings ${eb} | settings ${ew}`,ek=`settings requires ${ed}, ${ep}, ${eu}, ${eh}, ${em}, ${ef}, ${eg}, ${eb}, or ${ew}`;function ex(e){let t=e.trim().toLowerCase();return"appearance"===t||"permission"===t}function eA(e){return`Unsupported macOS setting: ${e}. ${ey}`}function eD(e){let t=e?.trim().split(/\s+/,1)[0];if(!t||!t.startsWith("@")||t.length<3)return null;let a=t.slice(1);return/^[A-Za-z_-]*\d[\w-]*$/i.test(a)||/^(?:ref|node|element|el)[\w-]*$/i.test(a)?t:null}async function eI(e){let{edge:t,target:a={},scope:n,captureNodes:r}=e;try{let e=await r(n),i=function(e,t,a={}){var n,r,i;let s,o=(e??[]).map((e,t)=>({...e,ref:"ref"in e&&e.ref?e.ref:`e${t+1}`}));if(0===o.length)return{canScroll:!1,emptySnapshot:!0,signature:""};let l=en(o),c=function(e,t,a,n){let r=new Map(e.map(e=>[e.index,e])),i=e.filter(e=>f(e)&&eE(e.rect));if(0===i.length)return null;let s=function(e,t){if(void 0===e)return null;let a=t.get(e);for(;a;){if(f(a)&&eE(a.rect))return a;a=void 0===a.parentIndex?void 0:t.get(a.parentIndex)}return null}(n.nodeIndex,r);if(s)return s;let o=n.point;if(o){let e=i.filter(e=>{var t,a;return e.rect&&(t=e.rect,(a=o).x>=t.x&&a.x<=t.x+t.width&&a.y>=t.y&&a.y<=t.y+t.height)}).sort(eO);if(e.length>0)return e.find(e=>eM(e,t.get(e.index),a))??e[0]??null}let l=i.filter(e=>eM(e,t.get(e.index),a)).sort(eL);return l.length>0?l[0]??null:i.filter(t=>ei(t,e)).sort(eL)[0]??i.sort(eL)[0]??null}(o,l,t,a),d=(c?(n=o,r=c.index,s=new Map(n.map(e=>[e.index,e])),n.filter(e=>e.index===r||function(e,t,a){let n=void 0===e.parentIndex?void 0:a.get(e.parentIndex);for(;n;){if(n.index===t)return!0;n=void 0===n.parentIndex?void 0:a.get(n.parentIndex)}return!1}(e,r,s))):o).map(e=>{let t=e.rect?["x","y","width","height"].map(t=>{var a;return a=e.rect?.[t],"number"==typeof a&&Number.isFinite(a)?a.toFixed(1):""}).join(","):"";return[String(e.index??""),String(e.parentIndex??""),String(e.type??""),String(e.label??""),String(e.value??""),t].join("|")}).join("\n");return c?{canScroll:eM(c,l.get(c.index),t),emptySnapshot:!1,signature:d,scope:[(i=c).identifier,i.label,i.value].map(e=>"string"==typeof e?e.trim():"").find(eN)}:{canScroll:!1,emptySnapshot:!1,signature:d}}(e,t,a);if(n&&i.emptySnapshot)return await eI({edge:t,target:a,captureNodes:r});return i}catch(e){var i,s,o;throw i=t,s=n,o=e,s?new b("COMMAND_FAILED",`Failed to verify scroll ${i} state for scoped container`,{scope:s,hint:`scroll ${i} could not verify the scoped scroll container. Run snapshot -i -c for the current screen and retry with a visible scroll target.`},o):new b("COMMAND_FAILED",`Failed to verify scroll ${i} state`,{hint:`scroll ${i} needs a snapshot showing hidden content ${"bottom"===i?"below":"above"} before it will move.`},o)}}async function eR(e){let t,{edge:a,captureState:n,scroll:r}=e,i=await n();i.scope&&(i=await n(i.scope));let s=0;for(;i.canScroll;){if(s>=40)throw new b("COMMAND_FAILED",`scroll ${a} reached the safety limit before the snapshot showed the edge`,{hint:"The scoped scroll container still reports hidden content. Use a smaller manual scroll + snapshot loop to inspect the current state."});t=await r(),s+=1,i=await n(i.scope)}return{passes:s,result:t}}function eS(e,t,a,n,r){return t&&0===a?`Already at ${t}; no hidden content ${"bottom"===t?"below":"above"} detected`:t?`Scrolled to ${t} with ${a} ${e} passes`:void 0!==r?`Scrolled ${e} by ${r}px`:void 0!==n?`Scrolled ${e} by ${n}`:`Scrolled ${e}`}function eM(e,t,a){return"bottom"===a?!0===e.hiddenContentBelow||t?.hiddenContentBelow===!0:!0===e.hiddenContentAbove||t?.hiddenContentAbove===!0}function eN(e){return e.length>0&&e.length<=80&&!/^(true|false)$/i.test(e)&&!/^\d+$/.test(e)&&!/^\d+%$/.test(e)}function eO(e,t){return eP(e.rect)-eP(t.rect)}function eL(e,t){return eP(t.rect)-eP(e.rect)}function eP(e){return e?e.width*e.height:0}function eE(e){return!!(e&&e.width>0&&e.height>0)}function eC(e,t,a,n){if(!Number.isFinite(e)||!Number.isInteger(e)||e<a||e>n)throw new b("INVALID_ARGS",`${t} must be an integer between ${a} and ${n}`);return e}function eT(e){if(void 0===e)throw new b("INVALID_ARGS","rotate requires an orientation argument. Use portrait|portrait-upside-down|landscape-left|landscape-right.");switch(e?.trim().toLowerCase()){case"portrait":return"portrait";case"portrait-upside-down":case"upside-down":return"portrait-upside-down";case"landscape-left":case"left":return"landscape-left";case"landscape-right":case"right":return"landscape-right";default:throw new b("INVALID_ARGS",`Invalid rotation: ${e}. Use portrait|portrait-upside-down|landscape-left|landscape-right.`)}}let eU=["snapshotInteractiveOnly","snapshotCompact","snapshotDepth","snapshotScope","snapshotRaw"],e_=[x({name:v.snapshot,schema:{usageOverride:"snapshot [--diff] [-i] [-c] [-d <depth>] [-s <scope>] [--raw] [--force-full]",helpDescription:"Capture accessibility tree or diff against the previous session baseline",positionalArgs:[],allowedFlags:["snapshotDiff",...eU,"snapshotForceFull"]},capability:k}),x({name:v.diff,schema:{usageOverride:"diff snapshot | diff screenshot --baseline <path> [current.png] [--out <diff.png>] [--threshold <0-1>] [--overlay-refs]",helpDescription:"Diff accessibility snapshot or compare screenshots pixel-by-pixel",summary:"Diff snapshot or screenshot",positionalArgs:["kind","current?"],allowedFlags:[...eU,"baseline","threshold","out","overlayRefs"]},capability:k}),x({name:v.screenshot,schema:{helpDescription:"Capture screenshot (macOS app sessions default to the app window; use --fullscreen for full desktop, --max-size to downscale, --overlay-refs to annotate current refs, or --no-stabilize for low-latency Android capture loops)",positionalArgs:["path?"],allowedFlags:y},capability:k})],e$=R(e_),eF=I(e_),eB=[x({name:v.reactNative,schema:{usageOverride:"react-native dismiss-overlay",listUsageOverride:"react-native dismiss-overlay",helpDescription:"Dismiss React Native LogBox/RedBox overlays safely",summary:"Dismiss React Native overlays",positionalArgs:["dismiss-overlay"],allowedFlags:[]},capability:{apple:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0},linux:{}}})],eq=R(eB),eG=I(eB),ej=["snapshotDepth","snapshotScope","snapshotRaw"],eV=[x({name:v.wait,schema:{usageOverride:"wait <ms>|text <text>|@ref|<selector> [timeoutMs]",helpDescription:"Wait for duration, text, ref, or selector to appear",summary:"Wait for time, text, ref, or selector",positionalArgs:["durationOrSelector","timeoutMs?"],allowsExtraPositionals:!0,allowedFlags:[...ej]},capability:k}),x({name:v.get,schema:{usageOverride:"get text|attrs <@ref|selector>",helpDescription:"Return exposed element text/attributes by ref or selector; use snapshot -s @ref for truncated previews",summary:"Get exposed text or attrs by ref or selector",positionalArgs:["subcommand","target"],allowedFlags:[...ej]},capability:k}),x({name:v.find,schema:{usageOverride:"find <locator|text> <action> [value] [--first|--last]",helpDescription:"Find by text/label/value/role/id and run action",summary:"Find an element and act",positionalArgs:["query","action","value?"],allowsExtraPositionals:!0,allowedFlags:["snapshotDepth","snapshotRaw","findFirst","findLast"]},capability:k}),x({name:v.is,schema:{helpDescription:"Assert UI state (visible|hidden|exists|editable|selected|text)",summary:"Assert UI state",positionalArgs:["predicate","selector","value?"],allowsExtraPositionals:!0,allowedFlags:[...ej]},capability:k})],eX=R(eV),eY=I(eV),eK={apple:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0},linux:{device:!0}},ez={apple:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0},linux:{},supports:e=>"macos"!==e.platform},eH=[x({name:v.open,schema:{helpDescription:"Boot device/simulator; optionally launch app or deep link URL (macOS also supports --surface app|frontmost-app|desktop|menubar)",summary:"Open an app, deep link or URL, save replays",positionalArgs:["appOrUrl?","url?"],allowedFlags:["activity","saveScript","relaunch","surface"]},capability:eK}),x({name:v.close,schema:{helpDescription:"Close app or just end session",summary:"Close app or end session",positionalArgs:["app?"],allowedFlags:["saveScript","shutdown"]},capability:eK}),x({name:v.reinstall,schema:{helpDescription:"Uninstall + install app from binary path",summary:"Reinstall app from binary path",positionalArgs:["app","path"],allowedFlags:[]},capability:ez}),x({name:v.install,schema:{helpDescription:"Install app from binary path without uninstalling first",summary:"Install app from binary path",positionalArgs:["app","path"],allowedFlags:[]},capability:ez}),x({name:v.installFromSource,schema:{usageOverride:"install-from-source <url> | install-from-source --github-actions-artifact <owner/repo:artifact>",listUsageOverride:"install-from-source <url> | install-from-source --github-actions-artifact",helpDescription:"Install app from a URL or remote-resolved source",summary:"Install app from a source",positionalArgs:["url?"],allowedFlags:["header","githubActionsArtifact","installSource","retainPaths","retentionMs"]},capability:ez}),x({name:v.apps,schema:{helpDescription:"List user-installed apps; use --all to include system/OEM apps",summary:"List installed apps",positionalArgs:[],allowedFlags:["appsFilter"],defaults:{appsFilter:M}},capability:{apple:{simulator:!0,device:!0},android:{emulator:!0,device:!0,unknown:!0},linux:{}}})],eJ=R(eH),eW=I(eH);function eQ(e,t){try{return c.sync.read(e)}catch(e){throw new b("COMMAND_FAILED",`Failed to decode ${t} as PNG`,{label:t,reason:e instanceof Error?e.message:String(e)})}}async function eZ(e,t){if(!Number.isInteger(t)||t<1)throw new b("INVALID_ARGS","Screenshot max size must be a positive integer");let a=eQ(await l.readFile(e),"screenshot"),n=Math.max(a.width,a.height);if(n<=t)return;let r=t/n,i=Math.max(1,Math.round(a.width*r)),s=Math.max(1,Math.round(a.height*r)),o=function(e,t,a){let n=new c({width:t,height:a});for(let r=0;r<a;r+=1){let i=r*e.height/a,s=(r+1)*e.height/a;for(let a=0;a<t;a+=1){let o=a*e.width/t,l=(a+1)*e.width/t,c=0,d=0,p=0,u=0,h=0;for(let t=Math.floor(i);t<Math.ceil(s);t+=1){let a=Math.min(t+1,s)-Math.max(t,i);for(let n=Math.floor(o);n<Math.ceil(l);n+=1){let r=a*(Math.min(n+1,l)-Math.max(n,o)),i=(t*e.width+n)*4;c+=(e.data[i]??0)*r,d+=(e.data[i+1]??0)*r,p+=(e.data[i+2]??0)*r,u+=(e.data[i+3]??0)*r,h+=r}}let m=(r*n.width+a)*4;n.data[m]=Math.round(c/h),n.data[m+1]=Math.round(d/h),n.data[m+2]=Math.round(p/h),n.data[m+3]=Math.round(u/h)}}return n}(a,i,s);await l.writeFile(e,c.sync.write(o))}async function e0(e,t,a){if("path"===t.kind&&!e.policy.allowLocalInputPaths)throw new b("INVALID_ARGS",`Local ${a.field??"input"} paths are not allowed by command policy`);try{return await e.artifacts.resolveInput(t,a)}catch(e){throw g(e)}}async function e1(e,t,a){if(t?.kind==="path"&&!e.policy.allowLocalOutputPaths)throw new b("INVALID_ARGS","Local output paths are not allowed by command policy");try{return await e.artifacts.reserveOutput(t,{...a,visibility:a.visibility??"client-visible",requestedClientPath:t?.kind==="downloadableArtifact"?t.clientPath??a.requestedClientPath:a.requestedClientPath})}catch(e){throw g(e)}}async function e2(e,t){try{return await e.artifacts.createTempFile(t)}catch(e){throw g(e)}}let e5=async(e,t)=>{let a;if(!e.backend.captureScreenshot)throw new b("UNSUPPORTED_OPERATION","screenshot is not supported by this backend");let n=await e1(e,t.out,{field:"path",ext:".png"});try{await e.backend.captureScreenshot({session:t.session,requestId:t.requestId,appId:t.appId,appBundleId:t.appBundleId,signal:t.signal??e.signal,metadata:t.metadata},n.path,{fullscreen:t.fullscreen,overlayRefs:t.overlayRefs,stabilize:t.stabilize,surface:t.surface}),void 0!==t.maxSize&&await eZ(n.path,t.maxSize),a=await n.publish()}catch(e){throw await n.cleanup?.(),e}return{path:n.path,...a?{artifacts:[a]}:{},...D(`Saved screenshot: ${n.path}`)}},e3=[0,187,255,255];function e4(e,t,a,n){if(t<0||t>=e.width||a<0||a>=e.height)return;let r=(a*e.width+t)*4;e.data[r]=n[0],e.data[r+1]=n[1],e.data[r+2]=n[2],e.data[r+3]=n[3]}function e8(e,t,a){return Math.min(Math.max(e,t),a)}let e6={icon:90,toggle:90,chevron:75,separator:45,visual:35,background:10},e7={leading:20,trailing:20,separator:10,unknown:0,background:-30};function e9(e){return"background"!==e.likelyKind}function te(e,t){return e.width>=.25*t.width||e.height>=.06*t.height}function tt(e,t){let a,n=0;for(let r of t){let t=ts(e,r.rect);t<=n||(n=t,a=r)}return a}function ta(e){let t=[];for(let a of[...e].sort((e,t)=>e.rect.y-t.rect.y)){let e=t.find(e=>{var t,n;return t=e.rect,ts(t,n=a.rect)>0||Math.abs(to(t).y-to(n).y)<=.5*Math.max(t.height,n.height)});if(!e){t.push({rect:a.rect,blocks:[a]});continue}e.blocks.push(a),e.blocks.sort((e,t)=>e.rect.x-t.rect.x),e.rect=function(e){let t=1/0,a=1/0,n=-1/0,r=-1/0;for(let i of e)t=Math.min(t,i.x),a=Math.min(a,i.y),n=Math.max(n,i.x+i.width),r=Math.max(r,i.y+i.height);return{x:t,y:a,width:n-t,height:r-a}}([e.rect,a.rect])}return t}function tn(e,t){let a,n=to(e);for(let e of t){var r,i;let t=Math.sqrt((r=n,i=to(e.rect),(r.x-i.x)**2+(r.y-i.y)**2));a&&t>=a.distance||(a={block:e,distance:t})}return a}function tr(e){let t=ti(e);return e.differentPixels>=24&&t.width>=3&&t.height>=3}function ti(e){return{x:e.minX,y:e.minY,width:e.maxX-e.minX+1,height:e.maxY-e.minY+1}}function ts(e,t){return Math.max(0,Math.min(e.y+e.height,t.y+t.height)-Math.max(e.y,t.y))}function to(e){return{x:e.x+e.width/2,y:e.y+e.height/2}}function tl(e,t,a){return Math.min(Math.max(e,t),a)}async function tc(e){if(await O("tesseract"))try{let[t,a]=await Promise.all([tp(e.baselinePath),tp(e.currentPath)]);if(0!==t.exitCode||0!==a.exitCode)return;let n=td(t.stdout,e.width,e.height),r=td(a.stdout,e.width,e.height),i=function(e,t){let a=new Set,n=[];for(let i of e){var r;let e=tw(i.text),s=function(e,t,a,n){let r=null,i=1/0;for(let l=0;l<a.length;l+=1){var s,o;if(n.has(l))continue;let c=a[l];if(tw(c.text)!==t)continue;let d=(s=tf(e.normalizedRect),o=tf(c.normalizedRect),(s.x-o.x)**2+(s.y-o.y)**2);d>=i||(r=l,i=d)}return r}(i,e,t,a);if(null===s)continue;a.add(s);let o=function(e,t){let a={x:t.rect.x-e.rect.x,y:t.rect.y-e.rect.y,width:t.rect.width-e.rect.width,height:t.rect.height-e.rect.height},n=tk(t.rect.width/e.rect.width),r=tk(t.rect.height/e.rect.height),i=Math.abs(n-1)>=.08||Math.abs(r-1)>=.12;return{text:e.text,baselineRect:e.rect,currentRect:t.rect,delta:a,confidence:Math.round(100*Math.min(e.confidence,t.confidence))/100,possibleTextMetricMismatch:i}}(i,t[s]);r=o,(Math.abs(r.delta.x)>=2||Math.abs(r.delta.y)>=2||Math.abs(r.delta.width)>=2||Math.abs(r.delta.height)>=2||r.possibleTextMetricMismatch)&&n.push(o)}return n.sort((e,t)=>tu(t)-tu(e)).slice(0,12)}(n,r),s=function(e){let t=[];for(let a of[...e].sort((e,t)=>e.currentRect.y-t.currentRect.y)){let e=t.find(e=>32>=Math.abs(a.delta.x-ty(e.map(e=>e.delta.x))));e?e.push(a):t.push([a])}return t.filter(e=>e.length>=2).map(th).filter(e=>e.yRange.max-e.yRange.min<=60).sort((e,t)=>tm(t)-tm(e)).slice(0,4)}(i);if(0===n.length&&0===r.length)return;return{provider:"tesseract",baselineBlocks:n.length,currentBlocks:r.length,baselineBlocksRaw:n,currentBlocksRaw:r,matches:i,...s.length>0?{movementClusters:s}:{}}}catch{return}}function td(e,t,a){let[n,...r]=e.split(/\r?\n/);if(!n)return[];let i=new Map(n.split(" ").map((e,t)=>[e,t])),s=[];for(let e of r){var o;if(!e.trim())continue;let t=e.split(" "),a=tb(t,i,"level"),n=tg(t,i,"text").trim(),r=tb(t,i,"conf");if(5!==a||(o=n,!/[\p{L}\p{N}]/u.test(o))||r<0)continue;let l=tb(t,i,"left"),c=tb(t,i,"top"),d=tb(t,i,"width"),p=tb(t,i,"height");d<=0||p<=0||s.push({key:[tg(t,i,"page_num"),tg(t,i,"block_num"),tg(t,i,"par_num"),tg(t,i,"line_num")].join(":"),text:n,confidence:r,rect:{x:l,y:c,width:d,height:p}})}let l=new Map;for(let e of s){let t=l.get(e.key);t?t.push(e):l.set(e.key,[e])}return Array.from(l.values()).flatMap(e=>(function(e){let t=[...e].sort((e,t)=>e.rect.x-t.rect.x),a=[],n=[];for(let e of t){let t=n.at(-1);if(!t){n.push(e);continue}if(e.rect.x-(t.rect.x+t.rect.width)>Math.max(48,2.5*Math.max(t.rect.height,e.rect.height))){a.push(n),n=[e];continue}n.push(e)}return n.length>0&&a.push(n),a})(e)).map(e=>(function(e,t,a){if(0===e.length)return null;let n=[...e].sort((e,t)=>e.rect.x-t.rect.x),r=function(e){let t=1/0,a=1/0,n=-1/0,r=-1/0;for(let i of e)t=Math.min(t,i.x),a=Math.min(a,i.y),n=Math.max(n,i.x+i.width),r=Math.max(r,i.y+i.height);return{x:t,y:a,width:n-t,height:r-a}}(n.map(e=>e.rect)),i=Math.round(100*ty(n.map(e=>e.confidence)))/100;return{text:n.map(e=>e.text).join(" "),confidence:i,rect:r,normalizedRect:{x:tv(r.x/t),y:tv(r.y/a),width:tv(r.width/t),height:tv(r.height/a)}}})(e,t,a)).filter(e=>null!==e)}function tp(e){return L("tesseract",[e,"stdout","-l","eng","tsv"],{allowFailure:!0,timeoutMs:1e4})}function tu(e){return Math.abs(e.delta.x)+Math.abs(e.delta.y)+Math.abs(e.delta.width)+Math.abs(e.delta.height)+25*!!e.possibleTextMetricMismatch}function th(e){let t=e.map(e=>e.delta.x),a=e.map(e=>e.delta.y);return{texts:e.map(e=>e.text),xRange:{min:Math.min(...t),max:Math.max(...t)},yRange:{min:Math.min(...a),max:Math.max(...a)}}}function tm(e){return 2*Math.abs((e.xRange.min+e.xRange.max)/2)+Math.abs((e.yRange.min+e.yRange.max)/2)}function tf(e){return{x:e.x+e.width/2,y:e.y+e.height/2}}function tg(e,t,a){let n=t.get(a);return void 0===n?"":e[n]??""}function tb(e,t,a){let n=Number(tg(e,t,a));return Number.isFinite(n)?n:0}function tw(e){return e.trim().replace(/\s+/g," ").toLowerCase()}function ty(e){return e.reduce((e,t)=>e+t,0)/e.length}function tv(e){return Math.round(100*e*100)/100}function tk(e){return Math.round(1e3*e)/1e3}function tx(e,t,a,n){return{r:Math.round(e/n),g:Math.round(t/n),b:Math.round(a/n)}}function tA(e){return .2126*e.r+.7152*e.g+.0722*e.b}function tD(e){return`#${tI(e.r)}${tI(e.g)}${tI(e.b)}`}function tI(e){return e.toString(16).padStart(2,"0")}function tR(e){return Math.round(100*e*100)/100}let tS=255*Math.sqrt(3);async function tM(e,t,a={}){let n,r,i,s;await tN(e,"Baseline image not found"),await tN(t,"Current screenshot not found");let o=a.outputPath,[p,u]=await Promise.all([l.readFile(e),l.readFile(t)]),h=eQ(p,"baseline screenshot"),m=eQ(u,"current screenshot");tO(h.width,h.height,"baseline screenshot",a.maxPixels),tO(m.width,m.height,"current screenshot",a.maxPixels);let f=a.threshold??.1;if(h.width!==m.width||h.height!==m.height){let e=h.width*h.height;return await tL(a.outputPath),{match:!1,mismatchPercentage:100,totalPixels:e,differentPixels:e,dimensionMismatch:{expected:{width:h.width,height:h.height},actual:{width:m.width,height:m.height}}}}let g=h.width*h.height,b=f*tS,w=new c({width:h.width,height:h.height}),y=new Uint8Array(g),v=0;for(let e=0,t=0;e<h.data.length;e+=4,t+=1){if(Math.sqrt((h.data[e]-m.data[e])**2+(h.data[e+1]-m.data[e+1])**2+(h.data[e+2]-m.data[e+2])**2)>b){v+=1,y[t]=1;let a=tP(m,e);w.data[e]=tE(a,220,.78),w.data[e+1]=tE(a,0,.78),w.data[e+2]=tE(a,0,.78),w.data[e+3]=255;continue}let a=tP(m,e);w.data[e]=a,w.data[e+1]=a,w.data[e+2]=a,w.data[e+3]=255}let k=v>0?(A=(n=function(e){let{diffMask:t,baseline:a,current:n}=e,{width:r,height:i}=a,s=new Uint8Array(t.length),o=new Int32Array(t.length),l=[];for(let e=0;e<t.length;e+=1){if(1!==t[e]||1===s[e])continue;let c=0,d=0;o[0]=e,d+=1,s[e]=1;let p=e%r,u=Math.floor(e/r),h={minX:p,minY:u,maxX:p,maxY:u,differentPixels:0,baselineRed:0,baselineGreen:0,baselineBlue:0,currentRed:0,currentGreen:0,currentBlue:0};for(;c<d;){let e=o[c];c+=1,function(e,t,a,n,r){let i=t%a,s=Math.floor(t/a),o=4*t;e.minX=Math.min(e.minX,i),e.minY=Math.min(e.minY,s),e.maxX=Math.max(e.maxX,i),e.maxY=Math.max(e.maxY,s),e.differentPixels+=1,e.baselineRed+=n.data[o],e.baselineGreen+=n.data[o+1],e.baselineBlue+=n.data[o+2],e.currentRed+=r.data[o],e.currentGreen+=r.data[o+1],e.currentBlue+=r.data[o+2]}(h,e,r,a,n);let l=e%r,p=Math.floor(e/r);for(let e=-1;e<=1;e+=1){let a=p+e;if(!(a<0)&&!(a>=i))for(let n=-1;n<=1;n+=1){if(0===n&&0===e)continue;let i=l+n;if(i<0||i>=r)continue;let c=a*r+i;1===t[c]&&1!==s[c]&&(s[c]=1,o[d]=c,d+=1)}}}l.push(h)}return l}(x={diffMask:y,baseline:h,current:m,totalPixels:g,differentPixels:v,maxRegions:a.maxRegions})).length<=2e3?function(e){let t=[];for(let r of e.sort((e,t)=>{let a=e.minY-t.minY;return 0!==a?a:e.minX-t.minX})){var a,n;let e=t.find(e=>{var t,a,n;return t=e,a=r,n=12,t.minX-n<=a.maxX&&a.minX-n<=t.maxX&&t.minY-n<=a.maxY&&a.minY-n<=t.maxY});if(!e){t.push({...r});continue}a=e,n=r,a.minX=Math.min(a.minX,n.minX),a.minY=Math.min(a.minY,n.minY),a.maxX=Math.max(a.maxX,n.maxX),a.maxY=Math.max(a.maxY,n.maxY),a.differentPixels+=n.differentPixels,a.baselineRed+=n.baselineRed,a.baselineGreen+=n.baselineGreen,a.baselineBlue+=n.baselineBlue,a.currentRed+=n.currentRed,a.currentGreen+=n.currentGreen,a.currentBlue+=n.currentBlue}return t}(n):n,A.flatMap(e=>{var t,a,n;let r;return(t=e,a=x.baseline.width,n=x.baseline.height,r=t.maxX-t.minX+1,t.maxY-t.minY+1>=Math.max(48,Math.round(.07*n))&&r>=.35*a)?function(e,t,a){var n;let r=function(e,t){let a=[],n=null;for(let r=0;r<e.length;r+=1){if(e[r]<=t){n??=r;continue}null!==n&&(r-n>=6&&a.push([n,r-1]),n=null)}return null!==n&&e.length-n>=6&&a.push([n,e.length-1]),a}((n=function(e,t,a){let n=[];for(let r=e.minY;r<=e.maxY;r+=1){let i=0;for(let n=e.minX;n<=e.maxX;n+=1)1===t[r*a+n]&&(i+=1);n.push(i)}return n}(e,t.diffMask,t.baseline.width)).map((e,t)=>{let a=0,r=0,i=Math.max(0,t-3),s=Math.min(n.length-1,t+3);for(let e=i;e<=s;e+=1)a+=n[e],r+=1;return Math.round(a/r)}),Math.max(1,Math.round((e.maxX-e.minX+1)*.08))),i=function(e,t,a){let n=[],r=e.minY;for(let[i,s]of t){let t=e.minY+Math.round((i+s)/2);t-r+1<a||e.maxY-t<a||(n.push([r,t]),r=t+1)}return n.push([r,e.maxY]),n}(e,r,a);if(i.length<=1)return[e];let s=i.map(([a,n])=>(function(e,t,a,n){let r=null;for(let o=t;o<=a;o+=1)for(let t=e.minX;t<=e.maxX;t+=1){var i,s;let e=o*n.baseline.width+t;1===n.diffMask[e]&&function(e,t,a,n,r,i){let s=4*t;e.minX=Math.min(e.minX,a),e.minY=Math.min(e.minY,n),e.maxX=Math.max(e.maxX,a),e.maxY=Math.max(e.maxY,n),e.differentPixels+=1,e.baselineRed+=r.data[s],e.baselineGreen+=r.data[s+1],e.baselineBlue+=r.data[s+2],e.currentRed+=i.data[s],e.currentGreen+=i.data[s+1],e.currentBlue+=i.data[s+2]}(r??={minX:i=t,minY:s=o,maxX:i,maxY:s,differentPixels:0,baselineRed:0,baselineGreen:0,baselineBlue:0,currentRed:0,currentGreen:0,currentBlue:0},e,t,o,n.baseline,n.current)}return r})(e,a,n,t)).filter(e=>null!==e);return s.length>1?s:[e]}(e,x,Math.max(24,Math.round(.03*x.baseline.height))):[e]})).sort((e,t)=>{let a=t.differentPixels-e.differentPixels;if(0!==a)return a;let n=e.minY-t.minY;return 0!==n?n:e.minX-t.minX}).slice(0,Math.max(0,x.maxRegions??8)).map((e,t)=>{var a,n,r,i,s,o,l,c,d,p,u;let h,m,f,g,b,w,y,v,k,A,D,I,R,S,M,N,O;return a=e,n=t+1,r={width:x.baseline.width,height:x.baseline.height,totalPixels:x.totalPixels,differentPixels:x.differentPixels},w={x:a.minX,y:a.minY,width:a.maxX-a.minX+1,height:a.maxY-a.minY+1},y={x:Math.round(a.minX+w.width/2),y:Math.round(a.minY+w.height/2)},v=tx(a.baselineRed,a.baselineGreen,a.baselineBlue,a.differentPixels),k=tx(a.currentRed,a.currentGreen,a.currentBlue,a.differentPixels),A=w.width*w.height,D=tR(a.differentPixels/A),I=Math.round(tA(v)),R=Math.round(tA(k)),S=(i=w,s=r.width,o=r.height,i.width>=.55*s&&i.height>=.12*o?"large-area":i.width>=2.5*i.height?"horizontal-band":i.height>=2.5*i.width?"vertical-band":"compact"),M=(h=A/r.totalPixels)>=.04?"large":h>=.01?"medium":"small",N=(l=v,c=k,m=tA(l),Math.abs(f=tA(c)-m)>=12?f>0?"brighter":"darker":Math.max(Math.abs(c.r-l.r),Math.abs(c.g-l.g),Math.abs(c.b-l.b))>=12?"color-shift":"mixed"),O=(d=y,p=r.width,u=r.height,g=d.x<p/3?"left":d.x>2*p/3?"right":"center",b=d.y<u/3?"top":d.y>2*u/3?"bottom":"middle","center"===g&&"middle"===b?"center":`${b}-${g}`),{index:n,rect:w,normalizedRect:{x:tR(w.x/r.width),y:tR(w.y/r.height),width:tR(w.width/r.width),height:tR(w.height/r.height)},differentPixels:a.differentPixels,shareOfDiffPercentage:tR(a.differentPixels/r.differentPixels),densityPercentage:D,shape:S,size:M,location:O,averageBaselineColorHex:tD(v),averageCurrentColorHex:tD(k),baselineLuminance:I,currentLuminance:R,dominantChange:N}}):[];if(v>0&&o){var x,A,D;for(let e of k)e.rect.width<4||e.rect.height<4||function(e,t){let a=e8(t.x,0,e.width-1),n=e8(t.y,0,e.height-1),r=e8(t.x+t.width-1,0,e.width-1),i=e8(t.y+t.height-1,0,e.height-1);for(let t=0;t<2;t+=1){for(let s=a;s<=r;s+=1)e4(e,s,n+t,e3),e4(e,s,i-t,e3);for(let s=n;s<=i;s+=1)e4(e,a+t,s,e3),e4(e,r-t,s,e3)}}(w,e.rect);await l.mkdir(d.dirname(o),{recursive:!0}),await l.writeFile(o,c.sync.write(w))}else await tL(a.outputPath);let I=v>0?await tc({baselinePath:e,currentPath:t,width:h.width,height:h.height}):void 0,R=I&&(I.matches.length>0||(I.movementClusters?.length??0)>0)?{provider:I.provider,baselineBlocks:I.baselineBlocks,currentBlocks:I.currentBlocks,matches:I.matches,...I.movementClusters?{movementClusters:I.movementClusters}:{}}:void 0,S=v>0&&I?(r=function(e){let t=[];for(let a of e.sort((e,t)=>e.minY-t.minY||e.minX-t.minX)){let e=t.find(e=>{var t,n,r;return t=e,n=a,r=10,t.minX-r<=n.maxX&&n.minX-r<=t.maxX&&t.minY-r<=n.maxY&&n.minY-r<=t.maxY});if(!e){t.push({...a});continue}e.minX=Math.min(e.minX,a.minX),e.minY=Math.min(e.minY,a.minY),e.maxX=Math.max(e.maxX,a.maxX),e.maxY=Math.max(e.maxY,a.maxY),e.differentPixels+=a.differentPixels}return t}(function(e,t,a){let n=new Uint8Array(e.length),r=new Int32Array(e.length),i=[];for(let s=0;s<e.length;s+=1){if(1!==e[s]||1===n[s])continue;let o=0,l=0;r[0]=s,l+=1,n[s]=1;let c=s%t,d=Math.floor(s/t),p={minX:c,minY:d,maxX:c,maxY:d,differentPixels:0};for(;o<l;){let i=r[o];o+=1;let s=i%t,c=Math.floor(i/t);p.minX=Math.min(p.minX,s),p.minY=Math.min(p.minY,c),p.maxX=Math.max(p.maxX,s),p.maxY=Math.max(p.maxY,c),p.differentPixels+=1;for(let i=-1;i<=1;i+=1){let o=c+i;if(!(o<0)&&!(o>=a))for(let a=-1;a<=1;a+=1){if(0===a&&0===i)continue;let c=s+a;if(c<0||c>=t)continue;let d=o*t+c;1===e[d]&&1!==n[d]&&(n[d]=1,r[l]=d,l+=1)}}}i.push(p)}return i}(function(e,t,a,n){let r=new Uint8Array(e);if(!n)return r;for(let e of[...n.baselineBlocksRaw,...n.currentBlocksRaw]){var i;!function(e,t,a,n){let r=tl(Math.floor(n.x),0,t-1),i=tl(Math.floor(n.y),0,a-1),s=tl(Math.ceil(n.x+n.width),0,t),o=tl(Math.ceil(n.y+n.height),0,a);for(let a=i;a<o;a+=1)for(let n=r;n<s;n+=1)e[a*t+n]=0}(r,t,a,{x:(i=e.rect).x-8,y:i.y-8,width:i.width+16,height:i.height+16})}return r}((D={diffMask:y,width:h.width,height:h.height,regions:k,ocr:I}).diffMask,D.width,D.height,D.ocr),D.width,D.height)),i=ta(D.ocr?.currentBlocksRaw??[]),s=ta(D.ocr?.baselineBlocksRaw??[]),r.filter(tr).map(e=>{var t,a,n,r,o,l,c,d,p,u,h;let m,f,g,b,w,y,v,k,x,A;return t=e,a=D,n=i,r=s,b=function(e,t){let a,n=0;for(let r of t){let t=function(e,t){let a=Math.max(e.x,t.x),n=Math.max(e.y,t.y),r=Math.min(e.x+e.width,t.x+t.width),i=Math.min(e.y+e.height,t.y+t.height);return r<=a||i<=n?0:(r-a)*(i-n)}(e,r.rect);t<=n||(n=t,a=r)}return a?.index}(g=ti(t),a.regions),w=function(e,t,a){let n=tt(e,t);if(n)return tn(e,n.blocks);let r=tt(e,a);return r?tn(e,r.blocks):void 0}(g,n,r),y=function(e,t,a){if(e.height<=3&&e.width>=.12*a)return"separator";if(!t)return e.width>=.4*a?"background":"unknown";if(e.width>=.4*a)return"background";let n=e.x+e.width/2,r=t.x+t.width/2;return n<r-t.width/2?"leading":n>r+t.width/2?"trailing":e.width>=.35*a?"background":"unknown"}(g,w?.block.rect,a.width),v=(o=g,l=y,c=t.differentPixels,d=a,m=o.width/o.height,f=c/(o.width*o.height),"separator"===l?"separator":"background"===l?"background":"trailing"===l&&m>=1.5&&m<=3.8&&f>=.35?"toggle":"trailing"===l&&o.width<=.06*d.width&&o.height<=.04*d.height?"chevron":"leading"===l&&m>=.55&&m<=1.8?"icon":te(o,d)?"background":"visual"),k={...b?{regionIndex:b}:{},slot:y,likelyKind:v,rect:g},{...b?{regionIndex:b}:{},slot:y,likelyKind:v,rect:g,...w?{nearestText:w.block.text.trim().replace(/^[^\p{L}\p{N}]+/u,"").replace(/^\p{L}\s+/u,"")}:{},score:(p=k,u=t.differentPixels,h=a,x=te(p.rect,h)?-35:0,A=20*!!p.regionIndex,e6[p.likelyKind]+e7[p.slot]+A+x+Math.min(20,u/200))}}).filter(e=>e.rect.y>=.08*D.height).filter(e9).sort((e,t)=>t.score-e.score).slice(0,Math.max(0,D.maxDeltas??12)).map((e,t)=>{var a;return a=e,{index:t+1,...a.regionIndex?{regionIndex:a.regionIndex}:{},slot:a.slot,likelyKind:a.likelyKind,rect:a.rect,...a.nearestText?{nearestText:a.nearestText}:{}}})):[],M=g>0?Math.round(v/g*1e4)/100:0;return{...v>0&&o?{diffPath:o}:{},...k.length>0?{regions:k}:{},...R?{ocr:R}:{},...S.length>0?{nonTextDeltas:S}:{},totalPixels:g,differentPixels:v,mismatchPercentage:M,match:0===v}}async function tN(e,t){try{await l.access(e)}catch{throw new b("INVALID_ARGS",`${t}: ${e}`)}}function tO(e,t,a,n){if(null==n||n<=0)return;let r=e*t;if(!(r<=n))throw new b("INVALID_ARGS",`${a} is ${r} pixels, which exceeds the configured maxImagePixels limit of ${n}`)}async function tL(e){if(e)try{await l.unlink(e)}catch(e){var t;if(!("object"==typeof(t=e)&&null!==t&&"code"in t&&"ENOENT"===t.code))throw e}}function tP(e,t){return tE(Math.round(.299*e.data[t]+.587*e.data[t+1]+.114*e.data[t+2]),255,.72)}function tE(e,t,a){return Math.round(e*(1-a)+t*a)}function tC(e){return e.width*e.height}function tT(e){return Math.round(100*e*100)/100}let tU=async(e,t)=>{let a,n,r;if(!t.baseline)throw new b("INVALID_ARGS","diff screenshot requires a baseline image");let i=function(e){if(null==e||""===e)return .1;let t=Number(e);if(Number.isNaN(t)||t<0||t>1)throw new b("INVALID_ARGS","--threshold must be a number between 0 and 1");return t}(t.threshold),s=t.current??{kind:"live"};if(t.overlayRefs&&!tj(s))throw new b("INVALID_ARGS","diff screenshot <current.png> cannot use --overlay-refs because saved-image comparisons have no live accessibility refs");let o=await e0(e,t.baseline,{usage:"diff screenshot baseline",field:"baseline"}),l=[];try{let c;c=tj(s)?(n=await t_(e,t)).path:(a=await e0(e,s,{usage:"diff screenshot current",field:"current"})).path,r=t.out?await e1(e,t.out,{field:"diffPath",ext:".png"}):void 0;let d=await tM(o.path,c,{threshold:i,outputPath:r?.path,maxPixels:e.policy.maxImagePixels});tj(s)&&(d=await t$(e,t,r?.path,d,l));let p=d.diffPath?await r?.publish():void 0;return p&&l.push(p),d.diffPath||await r?.cleanup?.(),{...d,...l.length>0?{artifacts:l}:{}}}catch(e){throw await r?.cleanup?.(),e}finally{await o.cleanup?.(),await a?.cleanup?.(),await n?.cleanup?.()}};async function t_(e,t){let a=await e2(e,{prefix:"agent-device-diff-current",ext:".png"});try{await tF(e,t,a.path,tB(t))}catch(e){throw await a.cleanup(),e}return a}async function t$(e,t,a,n,r){var i,s,o,l;if(!t.overlayRefs)return n;if(n.match||n.dimensionMismatch)return a&&await tG(a),n;let c=(i=t,s=a,i.currentOverlayOut?i.currentOverlayOut:i.out?.kind==="path"?{kind:"path",path:tq(s??i.out.path)}:i.out?.kind==="downloadableArtifact"?{kind:"downloadableArtifact",...i.out.clientPath?{clientPath:tq(i.out.clientPath)}:{},...i.out.fileName?{fileName:tq(i.out.fileName)}:{}}:void 0),d=await e1(e,c,{field:"currentOverlayPath",ext:".png"});try{let a=await tF(e,t,d.path,{overlayRefs:!0,...tB(t)}),i=await d.publish();return i&&r.push(i),{...n,currentOverlayPath:a.path??d.path,...a.overlayRefs?{currentOverlayRefCount:a.overlayRefs.length}:{},...n.regions&&a.overlayRefs?{regions:(o=n.regions,l=a.overlayRefs,o.map(e=>{var t,a;let n,r=(t=e,a=l,n=tC(t.rect),a.map(e=>{var a,r;let i,s,o,l,c=e.overlayRect,d=(a=t.rect,r=c,i=Math.max(a.x,r.x),s=Math.max(a.y,r.y),o=Math.min(a.x+a.width,r.x+r.width),l=Math.min(a.y+a.height,r.y+r.height),o<=i||l<=s?0:(o-i)*(l-s));return d<=0?null:{ref:e.ref,...e.label?{label:e.label}:{},rect:c,overlayCoveragePercentage:tT(d/tC(c)),regionCoveragePercentage:tT(d/n)}}).filter(e=>null!==e).sort((e,t)=>{let a=t.regionCoveragePercentage-e.regionCoveragePercentage;return 0!==a?a:t.overlayCoveragePercentage-e.overlayCoveragePercentage}).slice(0,3).map(e=>({ref:e.ref,...e.label?{label:e.label}:{},rect:e.rect,regionCoveragePercentage:e.regionCoveragePercentage})));return r.length>0?{...e,currentOverlayMatches:r}:e}))}:{}}}catch(e){throw await d.cleanup?.(),e}}async function tF(e,t,a,n={}){if(!e.backend.captureScreenshot)throw new b("UNSUPPORTED_OPERATION","screenshot is not supported by this backend");return await e.backend.captureScreenshot({session:t.session,requestId:t.requestId,signal:t.signal??e.signal,metadata:t.metadata},a,n)??{}}function tB(e){return e.surface?{surface:e.surface}:{}}function tq(e){let t=d.extname(e),a=t?e.slice(0,-t.length):e;return`${a}.current-overlay${t||".png"}`}async function tG(e){try{await l.unlink(tq(e))}catch(e){var t;if(!("object"==typeof(t=e)&&null!==t&&"code"in t&&"ENOENT"===t.code))throw e}}function tj(e){return"live"===e.kind}let tV={application:"application",navigationbar:"navigation-bar",tabbar:"tab-bar",button:"button",imagebutton:"button",link:"link",cell:"cell",statictext:"text",checkedtextview:"text",textfield:"text-field",edittext:"text-field",textarea:"text-view",switch:"switch",slider:"slider",image:"image",imageview:"image",webview:"webview",framelayout:"group",linearlayout:"group",relativelayout:"group",constraintlayout:"group",viewgroup:"group",view:"group",listview:"list",recyclerview:"list",collectionview:"collection",searchfield:"search",segmentedcontrol:"segmented-control",group:"group",window:"window",checkbox:"checkbox",radio:"radio",menuitem:"menu-item",toolbar:"toolbar",scrollarea:"scroll-area",scrollview:"scroll-area",nestedscrollview:"scroll-area",table:"table"};function tX(e,t={}){let a=[],n=[];for(let r of e){let e=r.depth??0,i=r.label?.trim()||r.value?.trim()||r.identifier?.trim()||"",s=tz(r.type??"Element");if("group"===s&&!i)continue;for(;a.length>0&&e<=a[a.length-1];)a.pop();let o=a.length;a.push(e),n.push({node:r,depth:o,type:s,text:tY(r,o,!1,s,t)})}return n}function tY(e,t,a,n,r={}){var i,s,o,l,c,d,p,u;let h,m=n??tz(e.type??"Element"),f=V(e,m),g=(i=e,s=m,o=r,l=f,o.summarizeTextSurfaces&&l.shouldSummarize&&function(e,t,a){let n=P(e.label);if(n&&n!==a)return n;let r=P(e.identifier);if(r&&!tJ(r)&&r!==a)return r;switch(t){case"text":case"text-view":return"Text view";case"text-field":return"Text field";case"search":return"Search field";default:return""}}(i,s,l.text)||tK(i,s)),b=" ".repeat(t),w=e.ref?`@${e.ref}`:"",y=(c=e,d=m,p=r,u=f,h=[],(!1===c.enabled&&h.push("disabled"),p.summarizeTextSurfaces)?(!0===c.selected&&h.push("selected"),!0===c.focused&&h.push("focused"),tH(d)&&h.push("editable"),function(e,t){if("scroll-area"===t)return!0;let a=(e.type??"").toLowerCase(),n=`${e.role??""} ${e.subrole??""}`.toLowerCase();return a.includes("scroll")||n.includes("scroll")}(c,d)&&h.push("scrollable"),h.push(...c.presentationHints??[]),u.shouldSummarize&&(h.push(`preview:"${_(u.text).replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`),h.push("truncated")),tW(h)):h).map(e=>` [${e}]`).join(""),v=g?` "${g}"`:"";return a?`${b}${w} [${m}]${y}`.trimEnd():`${b}${w} [${m}]${v}${y}`.trimEnd()}function tK(e,t){var a,n;let r,i=e.label?.trim();if(i&&(a=t,n=i,("scroll-area"===a||"list"===a||"collection"===a||"table"===a)&&(r=n.trim().toLowerCase())&&/^(vertical|horizontal)\s+scroll\s+bar(?:,?\s*\d+\s+pages?)?$/.test(r)))return"";let s=e.value?.trim();if(tH(t)){if(s)return s;if(i)return i}else if(i)return i;if(s)return s;let o=e.identifier?.trim();return!o||tJ(o)&&("group"===t||"image"===t||"list"===t||"collection"===t)?"":o}function tz(e){var t;let a=e.replace(/XCUIElementType/gi,"").toLowerCase(),n=e.includes(".")&&(e.startsWith("android.")||e.startsWith("androidx.")||e.startsWith("com."));return(a.includes(".")&&(a=a.replace(/^android\.widget\./,"").replace(/^android\.view\./,"").replace(/^android\.webkit\./,"").replace(/^androidx\./,"").replace(/^com\.google\.android\./,"").replace(/^com\.android\./,""),n&&a.includes(".")&&(a=a.slice(a.lastIndexOf(".")+1))),"textview"===a)?n?"text":"text-view":(t=a,(Object.prototype.hasOwnProperty.call(tV,t)?tV[t]:void 0)||a||"element")}function tH(e){return"text-field"===e||"text-view"===e||"search"===e}function tJ(e){return/^[\w.]+:id\/[\w.-]+$/i.test(e)}function tW(e){return[...new Set(e)]}function tQ(e,t){let a=tz(e.type??"Element"),n=tK(e,a),r=!1===e.enabled?"disabled":"enabled",i=!0===e.selected?"selected":"unselected",s=!0===e.hittable?"hittable":"not-hittable";return[String(t??e.depth??0),a,n,r,i,s].join("|")}function tZ(e,t){return t.flatten?e.map(e=>({text:tY(e,0,!1),comparable:tQ(e,0)})):tX(e).map(e=>({text:e.text,comparable:tQ(e.node,e.depth)}))}function t0(e,t){return e.get(t)??0}function t1(e){var t,a;let n=e.map(e=>[e.label,e.value,e.identifier,e.type,e.role].filter(Boolean).join(" ")).join("\n").toLowerCase(),r=t6(e,t5),i=t6(e,t3),s=t6(e,t4,t8),o=t7(r),l=t7(i),c=t7(s),d=(t=n,/\b[\w.$<>/-]+\.(?:tsx?|jsx?):\d+(?::\d+)?\b/.test(t)||/\b[\w.$<>/-]+\.(?:tsx?|jsx?)\s+\(\d+:\d+\)/.test(t)),p=o.length>0||l.length>0||/\b(reload js|copy stack)\b/.test(n),u=/\b(redbox|runtime error|reload js|copy stack|component stack|call stack)\b/.test(n)||d&&p;return{detected:c.length>0||p&&(a=n,/\b(logbox|redbox|reload js|copy stack|component stack|call stack|runtime error|open debugger to view warnings)\b/.test(a)||d),redBox:u,dismissRefs:o,minimizeRefs:l,collapsedRefs:c,dismissNodes:r,minimizeNodes:i,collapsedNodes:s}}function t2(e){let t,a=t1(e);if(!a.detected)return null;if(a.redBox){let e=t9(a.minimizeNodes);if(e)return ae(e,"minimize");let t=t9(a.dismissNodes);return t?{...ae(t,at(t)),warning:"RedBox Minimize control was not exposed; used Dismiss fallback"}:null}let n=t9(a.dismissNodes);if(n)return ae(n,at(n));let r=0===(t=a.collapsedNodes.filter(e=>e.rect)).length?null:t.sort((e,t)=>{let a=+(!0===e.hittable),n=+(!0===t.hittable);if(a!==n)return n-a;let r=e.rect?.width??0,i=t.rect?.width??0;return r!==i?i-r:(t.rect?.y??0)-(e.rect?.y??0)})[0];return r?.rect?{action:"close-collapsed-banner",point:function(e){var t,a;if(!e.rect)throw Error("Collapsed React Native warning node must have rect");let n=Math.min(36,Math.max(18,.45*e.rect.height));return{x:Math.round((t=e.rect.x+e.rect.width-n,a=e.rect.x+1,Math.min(e.rect.x+e.rect.width-1,Math.max(a,t)))),y:Math.round(e.rect.y+e.rect.height/2)}}(r),ref:r.ref,label:aa(r)}:null}function t5(e){return"dismiss"===e||"close"===e||["x","\xd7","✕","✖","⨯"].includes(e)}function t3(e){return/^minimi[sz]e$/.test(e)}function t4(e){return e.includes("open debugger to view warnings")||/^!,\s+/.test(e)||/^(warn|warning|error):\s+/.test(e)||/\b(?:possible\s+)?unhandled (?:promise )?rejection\b/.test(e)||e.includes("getsnapshot should be cached to avoid an infinite loop")||e.includes('unique "key" prop')||e.includes("unique 'key' prop")||e.includes("virtualizedlists should never be nested")||e.includes("failed prop type")}function t8(e){return!e.rect||e.rect.height<=180}function t6(e,t,a=()=>!0){let n=[];for(let r of e)r.ref&&a(r)&&[r.label,r.value,r.identifier].map(e=>e?.trim().toLowerCase()).filter(e=>!!e).some(t)&&n.push(r);return n}function t7(e){return e.map(e=>e.ref)}function t9(e){return e.find(e=>e.rect)??null}function ae(e,t){if(!e.rect)throw Error("React Native overlay target node must have rect");return{action:t,point:u(e.rect),ref:e.ref,label:aa(e)}}function at(e){return"dismiss"===aa(e)?.trim().toLowerCase()?"dismiss":"close"}function aa(e){return e.label??e.value??e.identifier}function an(e){return e.map(e=>({index:e.index,depth:e.depth,parentIndex:e.parentIndex,type:e.type,role:e.role,subrole:e.subrole,label:e.label,value:e.value,identifier:e.identifier,enabled:e.enabled,selected:e.selected,focused:e.focused,hittable:e.hittable,rect:e.rect,bundleId:e.bundleId,appName:e.appName,windowTitle:e.windowTitle,surface:e.surface,hiddenContentAbove:e.hiddenContentAbove,hiddenContentBelow:e.hiddenContentBelow}))}function ar(e){return"text"===e||"label"===e||"any"===e}function ai(e,t){return{session:t.session,requestId:t.requestId,signal:t.signal??e.signal,metadata:t.metadata}}function as(e){return e.clock?.now()??Date.now()}async function ao(e,t){e.clock?await e.clock.sleep(t):await new Promise(e=>setTimeout(e,t))}let al=async(e,t)=>{var a;let n,r,i=await ad(e,t),s=function(e){var t,a,n,r,i;let{previous:s,current:o,options:l,identity:c}=e;if(!0===l.forceFull||!0===l.raw||!s||!1===s.comparisonSafe||!1===o.comparisonSafe||(t=s,a=o,n=c,t.backend&&a.backend&&t.backend!==a.backend||n?.previousAppBundleId&&n.currentAppBundleId&&n.previousAppBundleId!==n.currentAppBundleId)||!s.presentationKey||s.presentationKey!==o.presentationKey||(r=s,i=o,r.truncated!==i.truncated||JSON.stringify(an(r.nodes))!==JSON.stringify(an(i.nodes))))return;let d=l.scope?.trim();return{ageMs:Math.max(0,o.createdAt-s.createdAt),nodeCount:o.nodes.length,...!0===l.interactiveOnly?{interactiveOnly:!0}:{},...d?{scope:d}:{}}}({previous:i.session?.snapshot,current:i.snapshot,options:t,identity:{previousAppBundleId:i.session?.appBundleId,currentAppBundleId:i.result.appBundleId??i.session?.appBundleId}});return await e.sessions.set(ap(t.session,i)),{nodes:i.snapshot.nodes,truncated:i.snapshot.truncated??!1,visibility:function(e){var t;let{nodes:a,backend:n,snapshotRaw:r}=e;if(r||"macos-helper"===(t=n)||"linux-atspi"===t)return{partial:!1,visibleNodeCount:a.length,totalNodeCount:a.length,reasons:[]};let i=ea(a),s=new Set;return i.hiddenCount>0&&s.add("offscreen-nodes"),i.nodes.some(e=>e.hiddenContentAbove)&&s.add("scroll-hidden-above"),i.nodes.some(e=>e.hiddenContentBelow)&&s.add("scroll-hidden-below"),{partial:s.size>0,visibleNodeCount:i.nodes.length,totalNodeCount:a.length,reasons:[...s]}}({nodes:i.snapshot.nodes,backend:i.snapshot.backend,snapshotRaw:t.raw}),...i.result.androidSnapshot?{androidSnapshot:i.result.androidSnapshot}:{},...i.warnings.length>0?{warnings:i.warnings}:{},...s?{unchanged:s}:{},...(n=(a=i).result.appName??a.session?.appName,r=a.result.appBundleId??a.session?.appBundleId,{...n||r?{appName:n??r}:{},...r?{appBundleId:r}:{}})}},ac=async(e,t)=>{let a=await ad(e,t),n=!0===t.interactiveOnly,r=a.session?.snapshot,i=ap(t.session,a);if(!r){let t=function(e,t={}){return tZ(e,t).length}(a.snapshot.nodes,{flatten:n});return await e.sessions.set(i),{mode:"snapshot",baselineInitialized:!0,summary:{additions:0,removals:0,unchanged:t},lines:[],...a.warnings.length>0?{warnings:a.warnings}:{}}}let s=function(e,t,a={}){let n=function(e,t){let a=e.length,n=t.length,r=a+n,i=new Map,s=[];i.set(1,0);for(let o=0;o<=r;o+=1){s.push(new Map(i));for(let r=-o;r<=o;r+=2){let l=r===-o||r!==o&&t0(i,r-1)<t0(i,r+1)?t0(i,r+1):t0(i,r-1)+1,c=l-r;for(;l<a&&c<n&&e[l].comparable===t[c].comparable;)l+=1,c+=1;if(i.set(r,l),l>=a&&c>=n)return function(e,t,a,n,r){let i=[],s=n,o=r;for(let n=e.length-1;n>=0;n-=1){let r=e[n],l=s-o,c=l===-n||l!==n&&t0(r,l-1)<t0(r,l+1)?l+1:l-1,d=t0(r,c),p=d-c;for(;s>d&&o>p;)i.push({kind:"unchanged",text:a[o-1].text}),s-=1,o-=1;if(0===n)break;s===d?(i.push({kind:"added",text:a[p].text}),o=p):(i.push({kind:"removed",text:t[d].text}),s=d)}return i.reverse(),i}(s,e,t,a,n)}}return[]}(tZ(e,a),tZ(t,a)),r={additions:0,removals:0,unchanged:0};for(let e of n)"added"===e.kind&&(r.additions+=1),"removed"===e.kind&&(r.removals+=1),"unchanged"===e.kind&&(r.unchanged+=1);return{summary:r,lines:n}}(r.nodes,a.snapshot.nodes,{flatten:n});return await e.sessions.set(i),{mode:"snapshot",baselineInitialized:!1,summary:s.summary,lines:s.lines,...a.warnings.length>0?{warnings:a.warnings}:{}}};async function ad(e,t){var a,n,r,i,s,o;let l,c,d,p;if(!e.backend.captureSnapshot)throw new b("UNSUPPORTED_OPERATION","snapshot is not supported by this backend");let u=t.session??"default",m=await e.sessions.get(u),f=await e.backend.captureSnapshot({session:u,requestId:t.requestId,appId:m?.appId,appBundleId:m?.appBundleId,signal:t.signal??e.signal,metadata:t.metadata},{interactiveOnly:t.interactiveOnly,compact:t.compact,depth:t.depth,scope:t.scope,raw:t.raw}),g=(n=f,r=e,a=n.snapshot?n.snapshot:{nodes:n.nodes??[],truncated:n.truncated,backend:n.backend,createdAt:as(r)},a.presentationKey?a:{...a,presentationKey:h(t)}),w=as(e);return{snapshot:g,result:f,session:m,warnings:((l=[...(i={result:f,snapshot:g,options:t,session:m,capturedAt:g.createdAt??w,runtimeNow:w}).result.warnings??[]]).push(...function(e){let t=e.result.analysis;if("android"!==e.snapshot.backend||!0!==e.options.interactiveOnly||e.snapshot.nodes.length>0||!t||(t.rawNodeCount??0)<12)return[];let a=[`Interactive snapshot is empty after filtering ${t.rawNodeCount} raw Android nodes. Likely causes: the app content is not accessibility-visible yet, a transient route change, or depth/filter options hid the target.`];return"number"==typeof e.options.depth&&"number"==typeof t.maxDepth&&t.maxDepth>=e.options.depth+2&&a.push(`Interactive output is empty at depth ${e.options.depth}; retry without -d.`),a}(i)),(c=function(e){if(e?.backend!=="uiautomator-dump")return;let t=e.fallbackReason?` Reason: ${e.fallbackReason}`:"";return`Android snapshot helper unavailable; using stock UIAutomator dump, which can time out on busy React Native UIs.${t}`}(i.result.androidSnapshot))&&l.push(c),(d=function(e){if(t1(e).detected)return"Hint: React Native warning/error overlay detected. It overlays part of the app and should be handled before interacting.\nRun: agent-device react-native dismiss-overlay\nThen run: agent-device snapshot -i -c\nUse refs from the new snapshot."}(i.snapshot.nodes))&&l.push(d),(p=function(e){var t,a;let n=e.session?.snapshot,r=!!n&&[e.capturedAt,e.runtimeNow].some(e=>{let t=e-n.createdAt;return t>=0&&t<=2e3});if(!e.result.freshness&&n&&r&&(t=n.nodes.length,a=e.snapshot.nodes.length,!(t<12)&&a<=Math.floor(.2*t)))return au}(i))&&l.push(p),l.push(...(s=i.result.freshness,o=i.snapshot.backend,s?.staleAfterRetries&&"android"===o?"stuck-route"===s.reason?[`Recent ${s.action} was followed by a nearly identical snapshot after ${s.retryCount} automatic retr${1===s.retryCount?"y":"ies"}. If you expected navigation or submit, the tree may still be stale. Use screenshot as visual truth, wait briefly, then re-snapshot once.`]:"sharp-drop"===s.reason?[au]:[]:[])),Array.from(new Set(l)))}}function ap(e,t){let a=t.session?.name??e??"default";return{...t.session??{name:a},name:a,snapshot:t.snapshot,appName:t.result.appName??t.session?.appName,appBundleId:t.result.appBundleId??t.session?.appBundleId}}let au="Recent snapshots dropped sharply in node count, which suggests stale or mid-transition UI. Use screenshot as visual truth, wait briefly, then re-snapshot once.";function ah(e){return["visible","hidden","exists","editable","selected","text"].includes(e)}function am(e){let{predicate:t,node:a,nodes:n,expectedText:r,platform:i}=e,s=E(a),o=U(a,i),l=!0===a.selected,c="text"===t?T(a):function(e,t){if(!0===e.hittable)return!0;if(af(e.rect))return function(e,t){return ei(e,t)}(e,t);if(e.rect)return!1;let a=function(e,t){let a=new Map(t.map(e=>[e.index,e])),n=e,r=new Set;for(;"number"==typeof n.parentIndex&&!r.has(n.index);){r.add(n.index);let e=a.get(n.parentIndex);if(!e)break;if(function(e){let t=K(e.type??"");return!(t.includes("application")||t.includes("window")||t.includes("scrollview")||t.includes("tableview")||t.includes("collectionview"))&&"table"!==t&&"list"!==t&&"listview"!==t&&(!0===e.hittable||af(e.rect))}(e))return e;n=e}return null}(e,t);return!!a&&(!0===a.hittable||!!af(a.rect)&&function(e,t){return ei(e,t)}(a,t))}(a,n),d=!1;switch(t){case"visible":d=c;break;case"hidden":d=!c;break;case"editable":d=o;break;case"selected":d=l;break;case"text":d=s===(r??"")}let p="text"===t?`expected="${r??""}" actual="${s}"`:`actual=${JSON.stringify({visible:c,editable:o,selected:l})}`;return{pass:d,actualText:s,details:p}}function af(e){return!!(e&&Number.isFinite(e.x)&&Number.isFinite(e.y)&&Number.isFinite(e.width)&&Number.isFinite(e.height)&&e.width>0&&e.height>0)}async function ag(e,t){let a=t??"default",n=await e.sessions.get(a);if(!n)throw new b("SESSION_NOT_FOUND","No active session. Run open first.");if(!n.snapshot)throw new b("INVALID_ARGS","No snapshot in session. Run snapshot first.");return{sessionName:a,session:n,snapshot:n.snapshot}}async function ab(e,t,a={updateSession:!0}){let n=e.backend.captureSnapshot;if(!n)throw new b("UNSUPPORTED_OPERATION","snapshot is not supported by this backend");let r=t.session??"default",i=await e.sessions.get(r),s=await n(ai(e,t),{interactiveOnly:!1,compact:!1,depth:t.depth,scope:a.scope??t.scope,raw:t.raw}),o=s.snapshot??{nodes:s.nodes??[],truncated:s.truncated,backend:s.backend,createdAt:as(e)};return a.updateSession&&i&&await e.sessions.set({...i,snapshot:o}),{sessionName:r,session:i,snapshot:o}}async function aw(e,t,a){if(e.backend.readText){let n=await e.backend.readText(ai(e,{session:t.sessionName}),a);if(n.text.trim())return n.text}return X(a)}let ay=async(e,t)=>{let a=t.locator??"any";if(!t.query)throw new b("INVALID_ARGS","find requires a value");if("wait"===t.action)return await aM(e,t,a);let n=await ab(e,t,{updateSession:!0,scope:ar(a)?t.query:void 0}),r=H(n.snapshot.nodes,a,t.query,{requireRect:!1}).matches[0];if(!r)throw new b("COMMAND_FAILED","find did not match any element");if("exists"===t.action)return{kind:"found",found:!0};let i=`@${r.ref}`;return"get_attrs"===t.action?{kind:"attrs",ref:i,node:r}:{kind:"text",ref:i,text:await aw(e,n,r),node:r}},av=async(e,t)=>{if("ref"===t.target.kind){let a=await ag(e,t.session),n=function(e,t,a){let n=p(t);if(!n)throw new b("INVALID_ARGS",a.invalidRefMessage);let r=m(e,n)??(a.fallbackLabel.length>0?G(e,a.fallbackLabel):null);if(!r)throw new b("COMMAND_FAILED",a.notFoundMessage);return{ref:n,node:r}}(a.snapshot.nodes,t.target.ref,{fallbackLabel:t.target.fallbackLabel??"",invalidRefMessage:"get text requires a ref like @e2",notFoundMessage:`Ref ${t.target.ref} not found`}),r=F(n.node,e.backend.platform,{action:"get"}),i={kind:"ref",ref:`@${n.ref}`};return"attrs"===t.property?{kind:"attrs",target:i,node:n.node,selectorChain:r}:{kind:"text",target:i,text:await aw(e,a,n.node),node:n.node,selectorChain:r}}let a=await aP(e,t,t.session??"default",{selector:t.target.selector,disambiguateAmbiguous:"text"===t.property}),n=F(a.node,e.backend.platform,{action:"get"});if("attrs"===t.property)return{kind:"attrs",target:{kind:"selector",selector:a.selector},node:a.node,selectorChain:n};let r=await aw(e,a.capture,a.node);return{kind:"text",target:{kind:"selector",selector:a.selector},text:r,node:a.node,selectorChain:n}},ak=async(e,t)=>{let a=await av(e,{...t,property:"text",target:t.target});if("text"!==a.kind)throw new b("COMMAND_FAILED","getText returned non-text result");return a},ax=async(e,t)=>{let a=await av(e,{...t,property:"attrs",target:t.target});if("attrs"!==a.kind)throw new b("COMMAND_FAILED","getAttrs returned non-attrs result");return a},aA=async(e,t)=>{if(!ah(t.predicate))throw new b("INVALID_ARGS","is requires predicate: visible|hidden|exists|editable|selected|text");if("text"===t.predicate&&!t.expectedText)throw new b("INVALID_ARGS","is text requires expected text value");let a=await ab(e,t,{updateSession:!0}),n=C(t.selector);if("exists"===t.predicate){let r=B(a.snapshot.nodes,n,{platform:e.backend.platform});if(!r)throw new b("COMMAND_FAILED",Y(n,[],{unique:!1}));return{predicate:t.predicate,pass:!0,selector:r.selector.raw,matches:r.matches,selectorChain:n.selectors.map(e=>e.raw)}}let r=q(a.snapshot.nodes,n,{platform:e.backend.platform,requireRect:!1,requireUnique:!0,disambiguateAmbiguous:!1});if(!r)throw new b("COMMAND_FAILED",Y(n,[],{unique:!0}));let i=am({predicate:t.predicate,node:r.node,nodes:a.snapshot.nodes,expectedText:t.expectedText,platform:e.backend.platform});if(!i.pass)throw new b("COMMAND_FAILED",`is ${t.predicate} failed for selector ${r.selector.raw}: ${i.details}`);return{predicate:t.predicate,pass:!0,selector:r.selector.raw,..."text"===t.predicate?{text:i.actualText}:{},selectorChain:n.selectors.map(e=>e.raw)}},aD=async(e,t)=>await aA(e,{...t,predicate:"visible",selector:t.target.selector}),aI=async(e,t)=>await aA(e,{...t,predicate:"hidden",selector:t.target.selector}),aR=async(e,t)=>{if("sleep"===t.target.kind)return await ao(e,t.target.durationMs),{kind:"sleep",waitedMs:t.target.durationMs};if("ref"===t.target.kind){let a=await ag(e,t.session),n=p(t.target.ref);if(!n)throw new b("INVALID_ARGS",`Invalid ref: ${t.target.ref}`);let r=m(a.snapshot.nodes,n),i=r?j(r,a.snapshot.nodes):void 0;if(!i)throw new b("COMMAND_FAILED",`Ref ${t.target.ref} not found or has no label`);return await aO(e,t,i,t.target.timeoutMs)}if("selector"===t.target.kind)return await aN(e,t,t.target.selector,t.target.timeoutMs);if(!t.target.text)throw new b("INVALID_ARGS","wait requires text");return await aO(e,t,t.target.text,t.target.timeoutMs)},aS=async(e,t)=>{let a=await aR(e,{...t,target:{kind:"text",text:t.text,timeoutMs:t.timeoutMs}});if("text"!==a.kind)throw new b("COMMAND_FAILED","waitForText returned non-text result");return a};async function aM(e,t,a){let n=t.timeoutMs??1e4,r=as(e);for(;as(e)-r<n;){if(H((await ab(e,t,{updateSession:!0,scope:ar(a)?t.query:void 0})).snapshot.nodes,a,t.query,{requireRect:!1}).matches[0])return{kind:"found",found:!0,waitedMs:as(e)-r};await ao(e,300)}throw new b("COMMAND_FAILED","find wait timed out")}async function aN(e,t,a,n){let r=n??1e4,i=as(e),s=C(a);for(;as(e)-i<r;){let a=B((await ab(e,t,{updateSession:!0})).snapshot.nodes,s,{platform:e.backend.platform});if(a)return{kind:"selector",selector:a.selector.raw,waitedMs:as(e)-i};await ao(e,300)}throw new b("COMMAND_FAILED",`wait timed out for selector: ${a}`)}async function aO(e,t,a,n){let r=n??1e4,i=as(e);for(;as(e)-i<r;){if(e.backend.findText?(await e.backend.findText(ai(e,t),a)).found:await aL(e,t,a))return{kind:"text",text:a,waitedMs:as(e)-i};await ao(e,300)}throw new b("COMMAND_FAILED",`wait timed out for text: ${a}`)}async function aL(e,t,a){return!!G((await ab(e,t,{updateSession:!0})).snapshot.nodes,a)}async function aP(e,t,a,n){let r=await ab(e,{...t,session:a},{updateSession:!0}),i=C(n.selector),s=q(r.snapshot.nodes,i,{platform:e.backend.platform,requireRect:!1,requireUnique:!0,disambiguateAmbiguous:n.disambiguateAmbiguous});if(!s)throw new b("COMMAND_FAILED",Y(i,[],{unique:!0}));return{capture:r,node:s.node,selector:s.selector.raw,ref:`@${s.node.ref}`}}function aE(e){let t=aC(e);if(!t)return null;let a=u(t);return Number.isFinite(a.x)&&Number.isFinite(a.y)?a:null}function aC(e){if(!e)return null;let t=Number(e.x),a=Number(e.y),n=Number(e.width),r=Number(e.height);return Number.isFinite(t)&&Number.isFinite(a)&&Number.isFinite(n)&&Number.isFinite(r)&&!(n<0)&&!(r<0)?{x:t,y:a,width:n,height:r}:null}let aT=["button","link","menuitem","tabitem","textfield","searchfield","securetextfield","checkbox","radio","switch","cell"];function aU(e,t){var a;let n=function(e,t){let a=aC(t.rect);if(!a)return null;let n=t,r=new Set;for(;!r.has(n.ref);){r.add(n.ref);let t=e.filter(e=>{if(e.parentIndex!==n.index||!e.hittable)return!1;let t=aC(e.rect);return!!t&&a$(t,a)});if(1!==t.length)break;n=t[0]}return n===t?null:n}(e,t);if(n?.rect&&aE(n.rect))return{node:n,reason:"same-rect-descendant"};if([(a=t).type,a.role,a.subrole].map(e=>K(e??"")).some(a_)&&t.rect&&aE(t.rect))return{node:t,reason:"semantic-target"};let r=$(e,t);return r?.rect&&aE(r.rect)?!function(e,t,a){var n,r,i,s;let o,l,c,d=aC(e.rect),p=aC(t.rect);if(!d||!p)return!1;let h=function(e,t){let a=u(t),n=e.filter(e=>{let t=(e.type??"").toLowerCase();return t.includes("application")||t.includes("window")}).map(e=>aC(e.rect)).filter(e=>null!==e);if(0===n.length)return null;let r=n.filter(e=>J(e,a.x,a.y));return W(r.length>0?r:n)}(a,d);return!!h&&(n=p,r=h,o=(i=n,s=r,Math.max(0,Math.min(i.x+i.width,s.x+s.width)-Math.max(i.x,s.x))*Math.max(0,Math.min(i.y+i.height,s.y+s.height)-Math.max(i.y,s.y))),l=n.width*n.height,c=r.width*r.height,!(o<=0)&&!(l<=0)&&!(c<=0)&&!!(o/c>=.9)&&!!(o/l>=.8))&&!a$(d,p)}(t,r,e)?{node:r,reason:"hittable-ancestor"}:{node:t,reason:"overly-broad-ancestor"}:{node:t,reason:"original"}}function a_(e){return"tab"===e||aT.some(t=>e.includes(t))}function a$(e,t){return .5>=Math.abs(e.x-t.x)&&.5>=Math.abs(e.y-t.y)&&.5>=Math.abs(e.width-t.width)&&.5>=Math.abs(e.height-t.height)}async function aF(e,t,a){if(await aq(e,t,a.action),"point"===t.target.kind)return{kind:"point",point:{x:t.target.x,y:t.target.y}};if("ref"===t.target.kind){let n=await aj(e,t,t.target),r=n.resolved,i=a.promoteToHittableAncestor?aU(n.snapshot.nodes,r.node).node:r.node;return function(e,t,a,n){let r=e.rect?es(e,t):null;if(!(!e.rect||!r||ei(e,t)))throw new b("COMMAND_FAILED",`Ref ${a} is off-screen and not safe to ${n}`,{reason:"offscreen_ref",ref:p(a),rect:e.rect,viewport:r,hint:`Use scroll with the direction from the off-screen summary, take a fresh snapshot, then retry ${n} with the new ref or a selector.`})}(i,n.snapshot.nodes,t.target.ref,a.action),{kind:"ref",point:aX(i,`Ref ${t.target.ref} not found or has invalid bounds`),target:{kind:"ref",ref:`@${r.ref}`},node:i,selectorChain:F(i,e.backend.platform,{action:"fill"===a.action?"fill":"click"}),refLabel:j(i,n.snapshot.nodes)}}let n=await aB(e,t,a.requireInteractive),r=C(t.target.selector),i=q(n.snapshot.nodes,r,{platform:e.backend.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0});if(!i||!i.node.rect)throw new b("COMMAND_FAILED",Y(r,i?.diagnostics??[],{unique:!0}));let s=a.promoteToHittableAncestor?aU(n.snapshot.nodes,i.node).node:i.node;return{kind:"selector",point:aX(s,`Selector ${i.selector.raw} resolved to invalid bounds`),target:{kind:"selector",selector:i.selector.raw},node:s,selectorChain:F(s,e.backend.platform,{action:"fill"===a.action?"fill":"click"}),refLabel:j(s,n.snapshot.nodes)}}async function aB(e,t,a){if(!e.backend.captureSnapshot)throw new b("UNSUPPORTED_OPERATION","snapshot is not supported by this backend");let n=t.session??"default",r=await e.sessions.get(n);if(!r)throw new b("SESSION_NOT_FOUND","No active session. Run open first.");let i=await e.backend.captureSnapshot(ai(e,t),{interactiveOnly:a,compact:a}),s=i.snapshot??{nodes:i.nodes??[],truncated:i.truncated,backend:i.backend,createdAt:as(e)};return await e.sessions.set({...r,snapshot:s}),{snapshot:s}}async function aq(e,t,a){if("macos"!==e.backend.platform)return;let n=await aG(e,t);if(("desktop"===n||"menubar"===n)&&("menubar"!==n||"click"!==a&&"press"!==a))throw new b("UNSUPPORTED_OPERATION",`${a} is not supported on macOS ${n} sessions yet. Open an app session to act, or use the ${n} surface to inspect.`)}async function aG(e,t){let a=await e.sessions.get(t.session??"default");return a?.metadata?.surface}async function aj(e,t,a){let n=t.session??"default",r=await e.sessions.get(n);if(!r)throw new b("SESSION_NOT_FOUND","No active session. Run open first.");if(!r.snapshot)throw new b("INVALID_ARGS","No snapshot in session. Run snapshot first.");let i=a.fallbackLabel??"",s=aV(r.snapshot.nodes,a.ref,{fallbackLabel:i,requireRect:!0});if(s)return{snapshot:r.snapshot,resolved:s};let o=await aB(e,t,!0),l=aV(o.snapshot.nodes,a.ref,{fallbackLabel:i,requireRect:!0});if(!l)throw new b("COMMAND_FAILED",`Ref ${a.ref} not found or has no bounds`);return{...o,resolved:l}}function aV(e,t,a){let n=p(t);if(!n)throw new b("INVALID_ARGS",`Invalid ref: ${t}`);let r=m(e,n);if(aY(r,a.requireRect))return{ref:n,node:r};let i=a.fallbackLabel.length>0?G(e,a.fallbackLabel):null;return aY(i,a.requireRect)?{ref:n,node:i}:null}function aX(e,t){if(!e.rect)throw new b("COMMAND_FAILED",t);let a=u(e.rect);if(!Number.isFinite(a.x)||!Number.isFinite(a.y))throw new b("COMMAND_FAILED",t);return a}function aY(e,t){if(!e)return!1;if(!t)return!0;if(!e.rect)return!1;let{x:a,y:n,width:r,height:i}=e.rect;if(!Number.isFinite(Number(a))||!Number.isFinite(Number(n))||!Number.isFinite(Number(r))||!Number.isFinite(Number(i))||0>Number(r)||0>Number(i))return!1;let s=u(e.rect);return Number.isFinite(s.x)&&Number.isFinite(s.y)}async function aK(e,t){let a=t.target??{kind:"viewport"};return"viewport"===a.kind?(await aq(e,t,"scroll"),{kind:"viewport"}):await aF(e,{...t,target:a},{action:"scroll",requireInteractive:!1,promoteToHittableAncestor:!1})}async function az(e,t){if(t.from){var a;if("x"in(a=t.from)&&"y"in a)return await aq(e,t,"swipe"),{point:aW(t.from,"from")};let n=await aF(e,{...t,target:t.from},{action:"swipe",requireInteractive:!1,promoteToHittableAncestor:!1});return{point:n.point,target:n}}if(!t.direction)throw new b("INVALID_ARGS","swipe requires from+to or a direction");return await aq(e,t,"swipe"),{point:u(function(e){let t=e.filter(t=>ei(t,e)).map(e=>e.rect).filter(aZ),a=t.length>0?t:e.map(e=>e.rect).filter(aZ);if(0===a.length)throw new b("COMMAND_FAILED","Cannot infer viewport for directional swipe");let n=Math.min(...a.map(e=>e.x)),r=Math.min(...a.map(e=>e.y));return{x:n,y:r,width:Math.max(...a.map(e=>e.x+e.width))-n,height:Math.max(...a.map(e=>e.y+e.height))-r}}((await aB(e,t,!1)).snapshot.nodes)),target:{kind:"viewport"}}}async function aH(e,t,a,n,r){if(!e.backend.captureSnapshot)throw new b("UNSUPPORTED_OPERATION",`scroll ${a} requires snapshot support to verify hidden content before scrolling`);let{captureSnapshot:i}=e.backend;return await eI({edge:a,target:n,scope:r,captureNodes:async a=>{let n=await i(ai(e,t),{compact:!0,scope:a});return n.snapshot?.nodes??n.nodes??[]}})}function aJ(e,t){switch(e){case"up":case"down":case"left":case"right":return e;default:throw new b("INVALID_ARGS",`${t} must be up, down, left, or right`)}}function aW(e,t){let a=Number(e.x),n=Number(e.y);if(!Number.isFinite(a)||!Number.isFinite(n))throw new b("INVALID_ARGS",`${t} point requires finite x and y`);return{x:a,y:n}}function aQ(e,t){if(!Number.isFinite(e)||e<=0)throw new b("INVALID_ARGS",`${t} must be a positive number`);return e}function aZ(e){return!!(e&&e.width>0&&e.height>0)}function a0(e){return e&&"object"==typeof e?e:void 0}async function a1(e,t,a){let n=await aF(e,t,{action:a,requireInteractive:!0,promoteToHittableAncestor:!0});if(!e.backend.tap)throw new b("UNSUPPORTED_OPERATION","tap is not supported by this backend");let r=a2(await e.backend.tap(ai(e,t),n.point,{button:t.button,count:t.count,intervalMs:t.intervalMs,holdMs:t.holdMs,jitterPx:t.jitterPx,doubleTap:t.doubleTap}));return{...n,...r?{backendResult:r}:{}}}function a2(e){return e&&"object"==typeof e?e:void 0}function a5(e,t){if(void 0!==e)return a3(e,t)}function a3(e,t){let a=e?.trim();if(!a)throw new b("INVALID_ARGS",`${t} must be a non-empty string`);return a}function a4(e){return!!(e&&"object"==typeof e)}function a8(e){return e&&"object"==typeof e?e:void 0}let a6=/^[A-Za-z0-9_.:-]{1,64}$/;async function a7(e,t){if(!t||"object"!=typeof t)throw new b("INVALID_ARGS","apps.push requires an input");if("json"===t.kind)return a9(t.payload,"apps.push JSON payload",8192),{backendInput:{kind:"json",payload:t.payload},inputKind:"json"};let a=await e0(e,t,{usage:"apps.push",field:"input"});return{backendInput:{kind:"file",path:a.path},inputKind:"file",...a.cleanup?{cleanup:a.cleanup}:{}}}function a9(e,t,a){if(function(e,t){if(!e||"object"!=typeof e||Array.isArray(e))throw new b("INVALID_ARGS",`${t} must be a JSON object`)}(e,t),Buffer.byteLength(function(e,t){try{let a=JSON.stringify(e);if("string"!=typeof a)throw new b("INVALID_ARGS",`${t} must be JSON-serializable`);return a}catch{throw new b("INVALID_ARGS",`${t} must be JSON-serializable`)}}(e,t),"utf8")>a)throw new b("INVALID_ARGS",`${t} exceeds ${a} bytes`)}function ne(e,t){return{session:t.session,requestId:t.requestId,signal:t.signal??e.signal,metadata:t.metadata}}function nt(e){return e&&"object"==typeof e?e:void 0}async function na(e,t,a){let n="reinstall"===a?"reinstallApp":"installApp",r=e.backend[n];if(!r)throw new b("UNSUPPORTED_OPERATION",`admin.${a} is not supported by this backend`);let i="app"in t&&void 0!==t.app?a3(t.app,"app"):void 0;if("installFromSource"!==a&&!i)throw new b("INVALID_ARGS",`admin.${a} requires app`);let s=ai(e,t),o=await nn(e,s,t.source);try{var l,c,d,p,u;let t,n,h,m,f,g,b,w,y=await r.call(e.backend,s,{...i?{app:i}:{},source:o.source});return l=a,c=i,d=o.source,p=y,t=no(p),n=ns(p,"appName"),h=ns(p,"appId"),m=ns(p,"bundleId"),f=ns(p,"packageName"),g=ns(p,"launchTarget"),b=ns(p,"installablePath"),w=ns(p,"archivePath"),{kind:"reinstall"===l?"appReinstalled":"installFromSource"===l?"appInstalledFromSource":"appInstalled",...c?{app:c}:{},source:d,...h?{appId:h}:{},...n?{appName:n}:{},...m?{bundleId:m}:{},...f?{packageName:f}:{},...g?{launchTarget:g}:{},...b?{installablePath:b}:{},...w?{archivePath:w}:{},...t?{backendResult:t}:{},...D(`${"reinstall"===l?"Reinstalled":"Installed"}: ${n??g??c??(u=d,"path"===u.kind?u.path:"uploadedArtifact"===u.kind?u.id:u.url)}`)}}finally{await o.cleanup?.()}}async function nn(e,t,a){let n=ni(a),r=await nr(e,n);try{let a=e.backend.resolveInstallSource?await e.backend.resolveInstallSource(t,r.source):r.source;return{source:ni(a),...r.cleanup?{cleanup:r.cleanup}:{}}}catch(e){if(r.cleanup)try{await r.cleanup()}catch{}throw e}}async function nr(e,t){if("url"===t.kind)return{source:t};let a=await e0(e,t,{usage:"admin.install",field:"source"});return{source:{kind:"path",path:a.path},...a.cleanup?{cleanup:a.cleanup}:{}}}function ni(e){if(!e||"object"!=typeof e)throw new b("INVALID_ARGS","install source is required");if("path"===e.kind)return{kind:"path",path:a3(e.path,"source.path")};if("uploadedArtifact"===e.kind)return{kind:"uploadedArtifact",id:a3(e.id,"source.id")};if("url"===e.kind){let t=a3(e.url,"source.url");return function(e){let t;try{t=new URL(e)}catch{throw new b("INVALID_ARGS",`Invalid install source URL: ${e}`)}if("http:"!==t.protocol&&"https:"!==t.protocol)throw new b("INVALID_ARGS","Install source URL must use http or https")}(t),{kind:"url",url:t}}throw new b("INVALID_ARGS","install source kind must be path, uploadedArtifact, or url")}function ns(e,t){let a=e[t];return"string"==typeof a&&a.length>0?a:void 0}function no(e){return e&&"object"==typeof e?e:void 0}function nl(e,t){if("start"===e||"stop"===e)return e;throw new b("INVALID_ARGS",`${t} action must be start or stop`)}function nc(e,t,a,n){return{kind:"start"===e?n.startKind:n.stopKind,action:e,...a?{artifact:a}:{},backendResult:t,...D("start"===e?n.startMessage:n.stopMessage)}}let nd=/(?:authorization|cookie|token|secret|password|passwd|api[-_]?key)/i,np=/\b[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\b/g;function nu(e){var t,a;let n=!1,r=e;return{value:(t=r=(r=(r=(r=(r=r.replaceAll(/(authorization|token|secret|password|passwd|api[-_]?key)=([^&\s]+)/gi,(e,t)=>(n=!0,`${String(t)}=[REDACTED]`))).replaceAll(/("(?:authorization|cookie|token|secret|password|passwd|api[-_]?key)"\s*:\s*")([^"]*)(")/gi,(e,t,a,r)=>(n=!0,`${String(t)}[REDACTED]${String(r)}`))).replaceAll(/\b(Bearer\s+)([^\s",;]+)/gi,(e,t)=>(n=!0,`${String(t)}[REDACTED]`))).replaceAll(/((?:authorization|cookie|token|secret|password|passwd|api[-_]?key)\s*[:=]\s*)([^\s,;&]+)/gi,(e,t)=>(n=!0,`${String(t)}[REDACTED]`))).replaceAll(np,()=>(n=!0,"[REDACTED]")),a=()=>{n=!0},r=/(https?:\/\/|token|secret|password|authorization|cookie|api[-_]?key)/i.test(t)?t.replaceAll(/https?:\/\/[^\s"'<>)]+/gi,e=>{let t=nh(e);return t?(t.redacted&&a(),t.value):e}):t),redacted:n}}function nh(e){try{let t=new URL(e),a=function(e){let t=!1;for(let a of Array.from(e.searchParams.keys()))nd.test(a)&&(e.searchParams.set(a,"[REDACTED]"),t=!0);return t}(t);return(t.username||t.password)&&(t.username="REDACTED",t.password="REDACTED",a=!0),{value:t.toString(),redacted:a}}catch{return}}let nm=/(?:authorization|cookie|token|secret|password|passwd|api[-_]?key)/i;function nf(e){if(!e)return{redacted:!1};let t=!1,a={};for(let[n,r]of Object.entries(e))if(nm.test(n))a[n]="[REDACTED]",t=!0;else{let e=ny(r,2048);a[n]=e.value??"",t||=e.redacted}return{value:a,redacted:t}}function ng(e){return void 0===e?{redacted:!1}:function(e){try{let t=JSON.parse(e),a=nw(t,nu);return nv(JSON.stringify(a.value),2048,a.redacted)}catch{return}}(e)??ny(e,2048)}function nb(e){return nw(e,e=>ny(e,2048))}function nw(e,t){if(void 0===e)return{redacted:!1};if("string"==typeof e)return t(e);if(!e||"object"!=typeof e)return{value:e,redacted:!1};if(Array.isArray(e)){let a=!1;return{value:e.map(e=>{let n=nw(e,t);return a||=n.redacted,n.value}),redacted:a}}let a=!1,n={};for(let[r,i]of Object.entries(e)){if(nm.test(r)){n[r]="[REDACTED]",a=!0;continue}let e=nw(i,t);n[r]=e.value,a||=e.redacted}return{value:n,redacted:a}}function ny(e,t){if(void 0===e)return{redacted:!1};let a=nu(e);return nv(a.value,t,a.redacted)}function nv(e,t,a){if(void 0===e)return{redacted:a};let n=e;return n.length>t&&(n=`${n.slice(0,t)}...[truncated]`,a=!0),{value:n,redacted:a}}async function nk(e,t){let a=ai(e,t),n=t.session?await e.sessions.get(t.session):void 0;return{...a,...t.appId??n?.appId?{appId:t.appId??n?.appId}:{},...t.appBundleId??n?.appBundleId?{appBundleId:t.appBundleId??n?.appBundleId}:{}}}function nx(e,t,a,n){return{...nA(e),...void 0!==e.cursor?{cursor:a3(e.cursor,"cursor")}:{},limit:void 0===e.limit?t:eC(e.limit,n,1,a)}}function nA(e){return{...void 0!==e.since?{since:a3(e.since,"since")}:{},...void 0!==e.until?{until:a3(e.until,"until")}:{}}}function nD(e,t,a=50){if(!Array.isArray(e))throw new b("INVALID_ARGS",`${t} must be an array of strings`);if(e.length>a)throw new b("INVALID_ARGS",`${t} must contain at most ${a} entries`);return e.map((e,a)=>a3(e,`${t}[${a}]`))}let nI=e5,nR=tU,nS=al,nM=ac,nN=ay,nO=av,nL=ak,nP=ax,nE=aA,nC=aD,nT=aI,nU=aR,n_=aS,n$=async(e,t)=>await a1(e,t,"click"),nF=async(e,t)=>await a1(e,t,"press"),nB=async(e,t)=>{var a;if(!t.text)throw new b("INVALID_ARGS","fill requires text");let n=await aF(e,t,{action:"fill",requireInteractive:!0,promoteToHittableAncestor:!1});if(!e.backend.fill)throw new b("UNSUPPORTED_OPERATION","fill is not supported by this backend");let r=a2(await e.backend.fill(ai(e,t),n.point,t.text,{delayMs:t.delayMs})),i="node"in n?n.node.type??"":"",s=i&&!z(i,e.backend.platform)?`fill target ${a=n,a.target?.kind==="ref"?a.target.ref:a.target?.kind==="selector"?a.target.selector:"point"} resolved to "${i}", attempting fill anyway.`:void 0;return{...n,text:t.text,...s?{warning:s}:{},...r?{backendResult:r}:{}}},nq=async(e,t)=>{let a=t.text;if(!a)throw new b("INVALID_ARGS","type requires text");let n=eD(a);if(n)throw new b("INVALID_ARGS",`type does not accept a target ref like "${n}"`,{hint:`Use fill ${n} "text" to target that field, or press ${n} then type "text" to append.`});if(!e.backend.typeText)throw new b("UNSUPPORTED_OPERATION","type is not supported by this backend");let r=eC(t.delayMs??0,"delay-ms",0,1e4),i=a2(await e.backend.typeText(ai(e,t),a,{delayMs:r}));return{kind:"text",text:a,delayMs:r,...i?{backendResult:i}:{},...D(`Typed ${Array.from(a).length} chars`)}},nG=async(e,t)=>{let a=await aF(e,t,{action:"focus",requireInteractive:!0,promoteToHittableAncestor:!1});if(!e.backend.focus)throw new b("UNSUPPORTED_OPERATION","focus is not supported by this backend");let n=a0(await e.backend.focus(ai(e,t),a.point));return{...a,...n?{backendResult:n}:{},...D(`Focused (${a.point.x}, ${a.point.y})`)}},nj=async(e,t)=>{let a=await aF(e,t,{action:"longPress",requireInteractive:!0,promoteToHittableAncestor:!0});if(!e.backend.longPress)throw new b("UNSUPPORTED_OPERATION","longPress is not supported by this backend");let n=void 0===t.durationMs?void 0:eC(t.durationMs,"durationMs",0,12e4),r=a0(await e.backend.longPress(ai(e,t),a.point,{durationMs:n}));return{...a,...void 0!==n?{durationMs:n}:{},...r?{backendResult:r}:{},...D(`Long pressed (${a.point.x}, ${a.point.y})`)}},nV=async(e,t)=>{if(!e.backend.swipe)throw new b("UNSUPPORTED_OPERATION","swipe is not supported by this backend");let a=await az(e,t),n=function(e,t){if(t.to)return{point:aW(t.to,"to")};let a=aJ(t.direction,"swipe direction"),n=aQ(t.distance??200,"swipe distance");switch(a){case"up":return{point:{x:e.x,y:e.y-n},direction:a,distance:n};case"down":return{point:{x:e.x,y:e.y+n},direction:a,distance:n};case"left":return{point:{x:e.x-n,y:e.y},direction:a,distance:n};case"right":return{point:{x:e.x+n,y:e.y},direction:a,distance:n}}}(a.point,t),r=void 0===t.durationMs?void 0:eC(t.durationMs,"durationMs",16,1e4),i=a0(await e.backend.swipe(ai(e,t),a.point,n.point,{durationMs:r}));return{kind:"swipe",from:a.point,to:n.point,...n.direction?{direction:n.direction}:{},...void 0!==n.distance?{distance:n.distance}:{},...void 0!==r?{durationMs:r}:{},...a.target?{fromTarget:a.target}:{},...i?{backendResult:i}:{},...D("Swiped")}},nX=async(e,t)=>{var a,n,r,i;let s;if(!e.backend.scroll)throw new b("UNSUPPORTED_OPERATION","scroll is not supported by this backend");let o="bottom"===(a=t.direction)?{direction:"down",edge:"bottom"}:"top"===a?{direction:"up",edge:"top"}:{direction:aJ(a,"scroll direction")},l=(n=t.amount,r="scroll amount",void 0===n?void 0:aQ(n,r)),c=function(e,t){if(void 0!==e){if(!Number.isFinite(e)||!Number.isInteger(e)||e<=0)throw new b("INVALID_ARGS",`${t} must be a positive integer`);return e}}(t.pixels,"scroll pixels");if(void 0!==l&&void 0!==c)throw new b("INVALID_ARGS","scroll accepts either amount or pixels, not both");let d=await aK(e,t),p="viewport"===d.kind?{kind:"viewport"}:{kind:"point",point:d.point},u=e.backend.scroll,h=async()=>await u(ai(e,t),p,{direction:o.direction,...void 0!==l?{amount:l}:{},...void 0!==c?{pixels:c}:{}}),m=0;if(o.edge){let a=o.edge,n="viewport"===(i=d).kind?{}:{point:i.point,nodeIndex:"node"in i?i.node.index:void 0},r=await eR({edge:a,captureState:async r=>await aH(e,t,a,n,r),scroll:h});s=r.result,m=r.passes}else s=await h(),m=1;let f=a0(s);return{...d,direction:o.direction,...o.edge?{edge:o.edge,passes:m}:{},...void 0!==l?{amount:l}:{},...void 0!==c?{pixels:c}:{},...f?{backendResult:f}:{},...D(eS(o.direction,o.edge,m,l,c))}},nY=async(e,t)=>{if(!e.backend.pinch)throw new b("UNSUPPORTED_OPERATION","pinch is not supported by this backend");await aq(e,t,"pinch");let a=aQ(t.scale,"pinch scale"),n=t.center?await aF(e,{...t,target:t.center},{action:"pinch",requireInteractive:!1,promoteToHittableAncestor:!1}):void 0,r=a0(await e.backend.pinch(ai(e,t),{scale:a,...n?{center:n.point}:{}}));return{kind:"pinch",scale:a,...n?{center:n.point,centerTarget:n}:{},...r?{backendResult:r}:{},...D(`Pinched to scale ${a}`)}},nK=async(e,t={})=>{if(!e.backend.pressBack)throw new b("UNSUPPORTED_OPERATION","system.back is not supported by this backend");let a=t.mode??"in-app";if("in-app"!==a&&"system"!==a)throw new b("INVALID_ARGS","system.back mode must be in-app or system");let n=a8(await e.backend.pressBack(ai(e,t),{mode:a}));return{kind:"systemBack",mode:a,...n?{backendResult:n}:{},...D("Back")}},nz=async(e,t={})=>{if(!e.backend.pressHome)throw new b("UNSUPPORTED_OPERATION","system.home is not supported by this backend");let a=a8(await e.backend.pressHome(ai(e,t)));return{kind:"systemHome",...a?{backendResult:a}:{},...D("Home")}},nH=async(e,t)=>{if(!e.backend.rotate)throw new b("UNSUPPORTED_OPERATION","system.rotate is not supported by this backend");let a=function(e){switch(e){case"portrait":case"portrait-upside-down":case"landscape-left":case"landscape-right":return e;default:throw new b("INVALID_ARGS","system.rotate orientation must be portrait, portrait-upside-down, landscape-left, or landscape-right")}}(t.orientation),n=a8(await e.backend.rotate(ai(e,t),a));return{kind:"systemRotated",orientation:a,...n?{backendResult:n}:{},...D(`Rotated to ${a}`)}},nJ=async(e,t={})=>{if(!e.backend.setKeyboard)throw new b("UNSUPPORTED_OPERATION","system.keyboard is not supported by this backend");let a=t.action??"status";if("status"!==a&&"get"!==a&&"dismiss"!==a)throw new b("INVALID_ARGS","system.keyboard action must be status, get, or dismiss");let n=await e.backend.setKeyboard(ai(e,t),{action:a}),r=a8(n);if("dismiss"===a){let e=a4(n)?n.dismissed:void 0;return{kind:"keyboardDismissed",action:a,state:a4(n)?n:{},...r?{backendResult:r}:{},...D(!1===e?"Keyboard already hidden":"Keyboard dismissed")}}return{kind:"keyboardState",action:a,state:a4(n)?n:{},...r?{backendResult:r}:{}}},nW=async(e,t)=>{if("read"===t.action){if(!e.backend.getClipboard)throw new b("UNSUPPORTED_OPERATION","system.clipboard read is not supported by this backend");let a=await e.backend.getClipboard(ai(e,t));return{kind:"clipboardText",action:"read",text:"string"==typeof a?a:a.text}}if("write"!==t.action)throw new b("INVALID_ARGS","system.clipboard action must be read or write");if(!e.backend.setClipboard)throw new b("UNSUPPORTED_OPERATION","system.clipboard write is not supported by this backend");if("string"!=typeof t.text)throw new b("INVALID_ARGS","system.clipboard write requires text");let a=a8(await e.backend.setClipboard(ai(e,t),t.text));return{kind:"clipboardUpdated",action:"write",textLength:Array.from(t.text).length,...a?{backendResult:a}:{},...D("Clipboard updated")}},nQ=async(e,t={})=>{if(!e.backend.openSettings)throw new b("UNSUPPORTED_OPERATION","system.settings is not supported by this backend");let a=a5(t.target,"target"),n=a8(await e.backend.openSettings(ai(e,t),a));return{kind:"settingsOpened",...a?{target:a}:{},...n?{backendResult:n}:{},...D(a?`Opened settings: ${a}`:"Opened settings")}},nZ=async(e,t={})=>{if(!e.backend.handleAlert)throw new b("UNSUPPORTED_OPERATION","system.alert is not supported by this backend");let a=t.action??"get";if("get"!==a&&"accept"!==a&&"dismiss"!==a&&"wait"!==a)throw new b("INVALID_ARGS","system.alert action must be get, accept, dismiss, or wait");let n=void 0===t.timeoutMs?void 0:eC(t.timeoutMs,"timeoutMs",0,12e4),r=await e.backend.handleAlert(ai(e,t),a,{timeoutMs:n});var i=a,s=r;if("get"===i){if("alertStatus"!==s.kind)throw new b("COMMAND_FAILED","system.alert get returned an invalid backend result");return{kind:"alertStatus",action:i,alert:s.alert}}if("wait"===i){if("alertWait"!==s.kind)throw new b("COMMAND_FAILED","system.alert wait returned an invalid backend result");return{kind:"alertWait",action:i,alert:s.alert,...void 0!==s.waitedMs?{waitedMs:s.waitedMs}:{},...void 0!==s.timedOut?{timedOut:s.timedOut}:{},...D(s.alert?"Alert visible":"Alert wait timed out")}}if("alertHandled"!==s.kind)throw new b("COMMAND_FAILED",`system.alert ${i} returned an invalid backend result`);return{kind:"alertHandled",action:i,handled:s.handled,...s.alert?{alert:s.alert}:{},...s.button?{button:s.button}:{},...D(s.handled?`Alert ${i}ed`:"No alert handled")}},n0=async(e,t={})=>{if(!e.backend.openAppSwitcher)throw new b("UNSUPPORTED_OPERATION","system.appSwitcher is not supported by this backend");let a=a8(await e.backend.openAppSwitcher(ai(e,t)));return{kind:"appSwitcherOpened",...a?{backendResult:a}:{},...D("Opened app switcher")}},n1=async(e,t)=>{var a;if(!e.backend.openApp)throw new b("UNSUPPORTED_OPERATION","apps.open is not supported by this backend");let n=function(e){var t;let a=a5(e.app,"app"),n=a5(e.appId,"appId"),r=a5(e.bundleId,"bundleId"),i=a5(e.packageName,"packageName"),s=a5(e.url,"url"),o=a5(e.activity,"activity"),l={...a?{app:a}:{},...n?{appId:n}:{},...r?{bundleId:r}:{},...i?{packageName:i}:{},...s?{url:s}:{},...o?{activity:o}:{}};if(!((t=l).app??t.appId??t.bundleId??t.packageName??t.url??t.activity))throw new b("INVALID_ARGS","apps.open requires app, appId, bundleId, packageName, url, or activity");return l}(t),r=nt(await e.backend.openApp(ne(e,t),n,{relaunch:t.relaunch}));return{kind:"appOpened",target:n,relaunch:!0===t.relaunch,...r?{backendResult:r}:{},...D(`Opened: ${(a=n).app??a.appId??a.bundleId??a.packageName??a.url??a.activity??"app"}`)}},n2=async(e,t={})=>{if(!e.backend.closeApp)throw new b("UNSUPPORTED_OPERATION","apps.close is not supported by this backend");let a=a5(t.app,"app"),n=nt(await e.backend.closeApp(ne(e,t),a));return{kind:"appClosed",...a?{app:a}:{},...n?{backendResult:n}:{},...D(a?`Closed: ${a}`:"Closed app")}},n5=async(e,t={})=>{if(!e.backend.listApps)throw new b("UNSUPPORTED_OPERATION","apps.list is not supported by this backend");return{kind:"appsList",apps:await e.backend.listApps(ne(e,t),N(t.filter))}},n3=async(e,t)=>{if(!e.backend.getAppState)throw new b("UNSUPPORTED_OPERATION","apps.state is not supported by this backend");let a=a3(t.app,"app"),n=await e.backend.getAppState(ne(e,t),a);return{kind:"appState",app:a,state:n}},n4=async(e,t)=>{if(!e.backend.pushFile)throw new b("UNSUPPORTED_OPERATION","apps.push is not supported by this backend");let a=a3(t.app,"app"),n=await a7(e,t.input);try{let r=await e.backend.pushFile(ne(e,t),n.backendInput,a),i=nt(r);return{kind:"appPushed",app:a,inputKind:n.inputKind,...i?{backendResult:i}:{},...D(`Pushed to ${a}`)}}finally{await n.cleanup?.()}},n8=async(e,t)=>{var a,n;if(!e.backend.triggerAppEvent)throw new b("UNSUPPORTED_OPERATION","apps.triggerEvent is not supported by this backend");let r=function(e){let t=a3(e,"name");if(!a6.test(t))throw new b("INVALID_ARGS",`Invalid apps.triggerEvent name: ${t}`,{hint:"Use 1-64 chars: letters, numbers, underscore, dot, colon, or dash."});return t}(t.name);a=t.payload,n=`apps.triggerEvent payload for "${r}"`,void 0!==a&&a9(a,n,8192);let i=nt(await e.backend.triggerAppEvent(ne(e,t),{name:r,...t.payload?{payload:t.payload}:{}}));return{kind:"appEventTriggered",name:r,...t.payload?{payload:t.payload}:{},...i?{backendResult:i}:{},...D(`Triggered app event: ${r}`)}},n6=async(e,t={})=>{if(!e.backend.listDevices)throw new b("UNSUPPORTED_OPERATION","admin.devices is not supported by this backend");return{kind:"adminDevices",devices:await e.backend.listDevices(ai(e,t),t.filter)}},n7=async(e,t={})=>{if(!e.backend.bootDevice)throw new b("UNSUPPORTED_OPERATION","admin.boot is not supported by this backend");let a=function(e){if(!e)return;let t=a5(e.id,"target.id"),a=a5(e.name,"target.name"),n={...t?{id:t}:{},...a?{name:a}:{},...e.platform?{platform:e.platform}:{},...e.target?{target:e.target}:{},...void 0!==e.headless?{headless:e.headless}:{}};return Object.keys(n).length>0?n:void 0}(t.target),n=no(await e.backend.bootDevice(ai(e,t),a));return{kind:"deviceBooted",...a?{target:a}:{},...n?{backendResult:n}:{},...D("Booted device")}},n9=async(e,t)=>await na(e,t,"install"),re=async(e,t)=>await na(e,t,"reinstall"),rt=async(e,t)=>await na(e,t,"installFromSource"),ra=async(e,t)=>{let a=nl(t.action,"record"),n="start"===a?e.backend.startRecording:e.backend.stopRecording;if(!n)throw new b("UNSUPPORTED_OPERATION",`record ${a} is not supported by this backend`);let r=t.out?await e1(e,t.out,{field:"path",ext:".mp4"}):void 0;try{var i,s,o,l,c;let d,p,u=(i=t,s=r?.path,d=void 0===i.fps?void 0:eC(i.fps,"fps",1,60),p=void 0===i.quality?void 0:eC(i.quality,"quality",5,10),{...s?{outPath:s}:{},...void 0!==d?{fps:d}:{},...void 0!==p?{quality:p}:{},...void 0!==i.hideTouches?{showTouches:!0!==i.hideTouches}:{}}),h=await n.call(e.backend,ai(e,t),u),m=await r?.publish();return o=a,l=h,c=m,{..."string"==typeof l.path?{path:l.path}:{},..."string"==typeof l.telemetryPath?{telemetryPath:l.telemetryPath}:{},..."string"==typeof l.warning?{warning:l.warning}:{},...nc(o,l,c,{startKind:"recordingStarted",stopKind:"recordingStopped",startMessage:"Recording started",stopMessage:"Recording stopped"})}}catch(e){throw await r?.cleanup?.(),e}},rn=async(e,t)=>{let a=nl(t.action,"trace"),n="start"===a?e.backend.startTrace:e.backend.stopTrace;if(!n)throw new b("UNSUPPORTED_OPERATION",`trace ${a} is not supported by this backend`);let r=t.out?await e1(e,t.out,{field:"outPath",ext:".trace"}):void 0;try{var i,s,o;let l={...r?.path?{outPath:r.path}:{}},c=await n.call(e.backend,ai(e,t),l),d=await r?.publish();return i=a,s=c,o=d,{..."string"==typeof s.outPath?{outPath:s.outPath}:{},...nc(i,s,o,{startKind:"traceStarted",stopKind:"traceStopped",startMessage:"Trace started",stopMessage:"Trace stopped"})}}catch(e){throw await r?.cleanup?.(),e}},rr=async(e,t={})=>{var a,n;let r;if(!e.backend.readLogs)throw new b("UNSUPPORTED_OPERATION","diagnostics.logs is not supported by this backend");return r=!0===(a=await e.backend.readLogs(await nk(e,t),{...nx(n=t,100,500,"logs limit"),...void 0!==n.levels?{levels:nD(n.levels,"levels")}:{},...void 0!==n.search?{search:a3(n.search,"search")}:{},...void 0!==n.source?{source:a3(n.source,"source")}:{}})).redacted,{kind:"diagnosticsLogs",entries:a.entries.map(e=>{let t=ny(e.message,4096),a=nb(e.metadata);return r||=t.redacted||a.redacted,{...e.timestamp?{timestamp:e.timestamp}:{},...e.level?{level:e.level}:{},message:t.value??"",...e.source?{source:e.source}:{},...a.value?{metadata:a.value}:{}}}),...a.nextCursor?{nextCursor:a.nextCursor}:{},...a.timeWindow?{timeWindow:a.timeWindow}:{},...a.backend?{backend:a.backend}:{},redacted:r,...a.notes?{notes:a.notes}:{}}},ri=async(e,t={})=>{var a,n,r;let i;if(!e.backend.dumpNetwork)throw new b("UNSUPPORTED_OPERATION","diagnostics.network is not supported by this backend");let s={...nx(r=t,25,200,"network limit"),include:function(e){if(void 0===e)return"summary";if("summary"===e||"headers"===e||"body"===e||"all"===e)return e;throw new b("INVALID_ARGS","network include must be summary, headers, body, or all")}(r.include)};return a=await e.backend.dumpNetwork(await nk(e,t),s),n=s.include??"summary",i=!0===a.redacted,{kind:"diagnosticsNetwork",entries:a.entries.map(e=>{var t;let a=e.url?nh(t=e.url)??ny(t,2048):void 0,r="headers"===n||"all"===n?nf(e.requestHeaders):void 0,s="headers"===n||"all"===n?nf(e.responseHeaders):void 0,o="body"===n||"all"===n?ng(e.requestBody):void 0,l="body"===n||"all"===n?ng(e.responseBody):void 0,c=nb(e.metadata);return i||=(a?.redacted??!1)||(r?.redacted??!1)||(s?.redacted??!1)||(o?.redacted??!1)||(l?.redacted??!1)||c.redacted,{...e.timestamp?{timestamp:e.timestamp}:{},...e.method?{method:e.method}:{},...a?{url:a.value}:{},...void 0!==e.status?{status:e.status}:{},...void 0!==e.durationMs?{durationMs:e.durationMs}:{},...r?.value?{requestHeaders:r.value}:{},...s?.value?{responseHeaders:s.value}:{},...o?.value!==void 0?{requestBody:o.value}:{},...l?.value!==void 0?{responseBody:l.value}:{},...c.value?{metadata:c.value}:{}}}),...a.nextCursor?{nextCursor:a.nextCursor}:{},...a.timeWindow?{timeWindow:a.timeWindow}:{},...a.backend?{backend:a.backend}:{},redacted:i,...a.notes?{notes:a.notes}:{}}},rs=async(e,t={})=>{var a,n;let r;if(!e.backend.measurePerf)throw new b("UNSUPPORTED_OPERATION","diagnostics.perf is not supported by this backend");return r=!0===(a=await e.backend.measurePerf(await nk(e,t),{...nA(n=t),...void 0!==n.sampleMs?{sampleMs:eC(n.sampleMs,"sampleMs",100,6e4)}:{},...void 0!==n.metrics?{metrics:nD(n.metrics,"metrics",20)}:{}})).redacted,{kind:"diagnosticsPerf",metrics:a.metrics.map(e=>{let t=ny(e.message,4096),a=nb(e.metadata);return r||=t.redacted||a.redacted,{name:e.name,...void 0!==e.value?{value:e.value}:{},...e.unit?{unit:e.unit}:{},...e.status?{status:e.status}:{},...void 0!==t.value?{message:t.value}:{},...a.value?{metadata:a.value}:{}}}),...a.startedAt?{startedAt:a.startedAt}:{},...a.endedAt?{endedAt:a.endedAt}:{},...a.backend?{backend:a.backend}:{},redacted:r,...a.notes?{notes:a.notes}:{}}};function ro(e){let t={backend:e.backend,artifacts:e.artifacts,sessions:e.sessions??function(e=[]){let t=new Map(e.map(e=>[e.name,rl(e)]));return{get:e=>rl(t.get(e)),set:e=>{t.set(e.name,rl(e))},delete:e=>{t.delete(e)},list:()=>Array.from(t.values(),e=>rl(e))}}(),policy:e.policy??function(e={}){return{allowLocalInputPaths:!1,allowLocalOutputPaths:!1,maxImagePixels:2e7,allowNamedBackendCapabilities:[],...e}}(),diagnostics:e.diagnostics,clock:e.clock,signal:e.signal};return{...t,capture:{screenshot:e=>nI(t,e),diffScreenshot:e=>nR(t,e),snapshot:e=>nS(t,e),diffSnapshot:e=>nM(t,e)},selectors:{find:e=>nN(t,e),get:e=>nO(t,e),getText:(e,a={})=>nL(t,{...a,target:e}),getAttrs:(e,a={})=>nP(t,{...a,target:e}),is:e=>nE(t,e),isVisible:(e,a={})=>nC(t,{...a,target:e}),isHidden:(e,a={})=>nT(t,{...a,target:e}),wait:e=>nU(t,e),waitForText:(e,a={})=>n_(t,{...a,text:e})},interactions:{click:(e,a={})=>n$(t,{...a,target:e}),press:(e,a={})=>nF(t,{...a,target:e}),fill:(e,a,n={})=>nB(t,{...n,target:e,text:a}),typeText:(e,a={})=>nq(t,{...a,text:e}),focus:(e,a={})=>nG(t,{...a,target:e}),longPress:(e,a={})=>nj(t,{...a,target:e}),swipe:e=>nV(t,e),scroll:e=>nX(t,e),pinch:e=>nY(t,e)},system:{back:e=>nK(t,e),home:e=>nz(t,e),rotate:e=>nH(t,e),keyboard:e=>nJ(t,e),clipboard:e=>nW(t,e),settings:e=>nQ(t,e),alert:e=>nZ(t,e),appSwitcher:e=>n0(t,e)},apps:{open:e=>n1(t,e),close:e=>n2(t,e),list:(e={})=>n5(t,{...e,filter:S(e.filter)}),state:e=>n3(t,e),push:e=>n4(t,e),triggerEvent:e=>n8(t,e)},admin:{devices:e=>n6(t,e),boot:e=>n7(t,e),install:e=>n9(t,e),reinstall:e=>re(t,e),installFromSource:e=>rt(t,e)},recording:{record:e=>ra(t,e),trace:e=>rn(t,e)},observability:{logs:e=>rr(t,e),network:e=>ri(t,e),perf:e=>rs(t,e)}}}function rl(e){if(e)return{...e,...e.snapshot?{snapshot:structuredClone(e.snapshot)}:{},...e.metadata?{metadata:function(e){try{return structuredClone(e)}catch{return{...e}}}(e.metadata)}:{}}}function rc(e={}){return{allowLocalInputPaths:!0,allowLocalOutputPaths:!0,maxImagePixels:2e7,allowNamedBackendCapabilities:[],...e}}let rd=["app","frontmost-app","desktop","menubar"];function rp(e){let t=e?.trim().toLowerCase();if("app"===t||"frontmost-app"===t||"desktop"===t||"menubar"===t)return t;throw new b("INVALID_ARGS",`Invalid surface: ${e}. Use ${rd.join("|")}.`)}let ru={workflow:{summary:"Normal agent-device bootstrap, exploration, and validation loop",body:`agent-device help workflow
2
-
3
- Version-matched operating guide for normal agent-device work.
4
-
5
- Core loop:
6
- devices/apps -> open -> snapshot or snapshot -i -> get/is/find/wait or press/fill/scroll/back -> verify -> close
7
-
8
- Command shape:
9
- Plans should use agent-device commands, not raw platform tools, pseudo commands, package-manager aliases, or helper prose.
10
- Put subcommand first, then positionals, then flags:
11
- agent-device open com.example.app --session checkout --platform android --relaunch
12
- agent-device record start ./checkout.mp4 --session checkout
13
- Snapshot refs look like @e12. After snapshot -i, use the exact @eN ref from that output.
14
- If the exact ref is not known yet, first output snapshot -i, then use a concrete example shape like press @e12 in the next command; do not write @<ref>, @ref, @Label_Name, or @eN placeholders.
15
- Close means agent-device close. App-owned back means back; system back means back --system.
16
- Taps are press or click. Gestures are direct commands: swipe, longpress, pinch.
17
-
18
- Bootstrap:
19
- agent-device devices --platform ios
20
- agent-device apps --platform android
21
- agent-device open MyApp --platform ios --device "iPhone 17 Pro"
22
- agent-device open <discovered-app-id> --session checkout --platform android
23
- agent-device install com.example.app ./dist/app.apk --platform android
24
- agent-device reinstall com.example.app ./build/MyApp.app --platform ios
25
- agent-device install-from-source --github-actions-artifact org/repo:app-debug --platform android
26
- agent-device open com.example.app --platform android --relaunch
27
- If app id is unknown, plan devices, apps, then open <discovered-app-id>. Discovery is not enough when the task asks to open/start the app.
28
- Install arguments are app/package id then artifact path. If the task says install, use install; use reinstall only when explicitly requested. Fresh runtime state is open --relaunch after install.
29
- Do not open artifact paths or invent package ids. If apps lookup misses the target and no URL/artifact is provided, ask or stop.
30
-
31
- Snapshots and refs:
32
- snapshot reads visible state. snapshot -i gets current interactive refs only; it is the fast path when the next step is an interaction.
33
- Default snapshot text is an agent-facing, token-efficient view for planning and targeting actions; use --raw or --json only when you need the full provider tree.
34
- Snapshot legend:
35
- @e12 [button] label="Add to cart" id="add-cart" enabled hittable -> press @e12 or press 'id="add-cart"'.
36
- @e13 [textinput] label="Notes" preview="Leave at side..." truncated -> snapshot -s @e13 before reading.
37
- @e14 [cell] label="Profiles" focused -> tvOS focus is currently on this row.
38
- [off-screen below] 4 items: "Privacy", "About" -> scroll down, then snapshot -i; those are hints, not refs.
39
- Re-snapshot after navigation, submit, typing/fill, modal/list/reload/dynamic changes when you need new refs.
40
- Anti-pattern: snapshot -i followed by snapshot -i | grep ...
41
- Refs from the first snapshot remain valid until you press, fill, type, scroll, go back, wait for async UI, or otherwise change app state.
42
- After a mutation, prefer a known selector/label directly (for example press 'label="Send"') because interaction commands refresh interactive state internally. If you need to discover the new control, use snapshot -i, or snapshot -i -s "Composer" when a stable container label/id can scope the refresh.
43
- For a targeted query, use find/get/is. If you truly need the full tree again, pass --force-full.
44
- Off-screen summaries are scroll hints; use scroll, not swipe, then snapshot -i.
45
- Missing target in a long list: use a short manual scroll + snapshot loop with a max attempt count. If a named target is summarized as off-screen below/above, use scroll down/up, then snapshot -i; do not use scroll bottom/top because the target may appear before the absolute list edge. Use scroll bottom/top only when the task explicitly asks for the list edge. Edge scrolls verify hidden content with snapshots and stop when no matching hidden content remains.
46
- Truncated text/input previews: do not use get text first; expand with snapshot -s @ref (for example snapshot -s @e7), then read the scoped output.
47
- Rare iOS accessibility gaps: if a row ref is shown disabled/hittable:false and press @ref reports success but no UI change, or a horizontal tab/filter bar is collapsed into one composite/seekbar with no child refs, run agent-device snapshot -i -c --json to read rects, compute the target center, press x y, then diff snapshot -i. Coordinates are fallback-only; document why you used them.
48
-
49
- Selectors:
50
- Use selectors as positional targets: id="field-email" or label="Allow".
51
- Do not use CSS selectors, pseudo refs, --selector, --text, or raw x/y when refs/selectors exist.
52
- agent-device fill 'id="catalog-search"' "tart" --delay-ms 80
53
- agent-device press 'id="submit-order"'
54
- agent-device is visible 'label="Online"'
55
- agent-device get text 'id="quantity-value"'
56
-
57
- Text entry:
58
- fill replaces; type appends to focused field.
59
- agent-device fill @e5 "qa@example.com"
60
- agent-device fill 'id="field-email"' "qa@example.com"
61
- agent-device press 'id="product-note"'
62
- agent-device type "Handle with care" --delay-ms 80
63
- Empty replacement is not a supported clear-field command: do not plan fill <target> "" or fill <target> ''. Prefer a visible clear/reset control; if the app exposes none, report the tool gap instead of inventing a clear command.
64
- Debounced field with no result selector: agent-device wait 1000. Keyboard read-only: keyboard status/get. Blocked control: try keyboard dismiss when supported.
65
- On iOS, prefer keyboard dismiss before manually pressing visible Done; the runner can use safe native keyboard controls and still reports unsupported layouts explicitly. If it returns UNSUPPORTED_OPERATION, prefer a visible app dismiss control, or use back --system only when system navigation is an acceptable side effect.
66
- Search-as-you-type fields on iOS can drop characters when driven too fast; use --delay-ms on fill/type before trying clipboard paste.
67
- iOS Allow Paste prompt cannot be exercised under XCUITest. To test paste-driven app behavior, prefill first with agent-device clipboard write "some text"; test the system prompt manually.
68
- Android Gboard handwriting/stylus UI can capture text in an IME-owned input instead of the app field. If fill reports that input was captured by the keyboard/IME, use the diagnostic targetInput/actualInput details, inspect keyboard status/get if needed, and switch or disable handwriting outside the command plan before retrying. Do not keep retrying fill/type against the same field while the IME owns focus.
69
- Android text entry is owned by agent-device: provider-native text injection when available, then chunk-safe ASCII shell input. Do not switch to raw adb, clipboard, or paste as an agent fallback. If non-ASCII is unsupported in the current backend, report the tool/device gap.
70
-
71
- Session ordering:
72
- Stateful commands against one --session must run serially. Do not run open/press/fill/type/scroll/back/alert/replay/batch/close commands in parallel against the same session.
73
- It is fine to parallelize independent read-only collection or commands that use different sessions/devices.
74
-
75
- Read-only and waits:
76
- Read-only visible/state question: use snapshot/get/is/find.
77
- agent-device snapshot
78
- agent-device get text 'id="product-title"'
79
- agent-device get attrs @e4
80
- agent-device is visible 'label="Online"'
81
- agent-device wait text "Refreshing metrics..." 3000
82
- agent-device wait 'label="Ready"' 3000
83
- agent-device find "Increment" press --json
84
- For async/list text presence, prefer wait text over is visible when no interaction is needed.
85
- Use snapshot -i only when refs are needed for an action or targeted query.
86
- Ambiguous find: add --first or --last. If info is not visible/exposed, report that gap instead of typing/searching/navigating to reveal it.
87
-
88
- Navigation and gestures:
89
- Use scroll for lists; swipe for coordinate gestures/carousels.
90
- If app-owned back is ambiguous or has just misrouted, prefer a visible nav/back button ref, tab-bar ref, or deep link over repeated back/system back.
91
- App-owned action sheets, menus, and camera/scan screens are normal UI. After opening one, run snapshot -i or wait for the option, press by label/ref, handle visible permission sheets through UI or platform-supported native alerts, then wait for a concrete result before returning to chat/form state.
92
- Keep count/pause/pattern on one swipe; flags are --count, --pause-ms, --pattern ping-pong.
93
- longpress accepts coordinates, @refs, or selectors. Prefer @ref/selector from snapshot -i; use coordinates only as a fallback when accessibility refs miss the exact target. Duration and pinch scale/center are positional:
94
- agent-device longpress 300 500 800
95
- agent-device longpress @e12 800
96
- agent-device swipe 320 500 40 500 --count 8 --pause-ms 30 --pattern ping-pong
97
- agent-device pinch 0.5 200 400
98
-
99
- Validation and evidence:
100
- Nearby mutation diff: agent-device diff snapshot -i.
101
- Expected text/selector verification must include the exact text or selector via wait, is, get, or find; bare screenshots/snapshots are insufficient for named expectations.
102
- Prefer provided testIDs/ids/selectors for verification; use visible text when no durable selector is provided.
103
- If task says snapshot, use snapshot. If it asks visual evidence, use screenshot.
104
- Icon/tappable visual proof: screenshot --overlay-refs. Flag is --overlay-refs.
105
- Startup/frame health/CPU/memory: perf --json or metrics. Replay maintenance: replay -u ./flow.ad.
106
- Recording: record start/stop. By default, stop burns touch overlays into the video; use record start --hide-touches for the fastest raw recording. Tracing: trace start ./trace.log, trace stop ./trace.log. Paths are positional.
107
- Stable known flow: batch ./steps.json, not workflow batch.
108
- Inline batch JSON example:
109
- agent-device batch --steps '[{"command":"open","positionals":["settings"],"flags":{}},{"command":"wait","positionals":["100"],"flags":{}}]'
110
- Batch step keys are command, positionals, flags, and optional runtime. Never use args, step, text, or target as batch step fields.
111
- Android animations: settings animations off/on, not animations disable/restore.
112
- Debug logs: logs clear --restart, logs mark, reproduce, then logs path; do not split clear/restart into separate stop/start commands.
113
- Network headers: network dump --include headers; do not write network log headers.
114
- Remote/cloud: connect to discover a cloud profile, or connect --remote-config ./remote-config.json for a local profile; then open, snapshot, disconnect.
115
- macOS menu bar: open ... --platform macos --surface menubar; snapshot -i --platform macos --surface menubar.
116
-
117
- React Native dev loop:
118
- JS-only change with Metro connected:
119
- agent-device metro reload
120
- agent-device find "Home"
121
- Do not use agent-device reload. Use open --relaunch for native startup reset.
122
- React Native apps: use help react-native for Metro/Fast Refresh, DevTools routing, and RN-specific blockers; use react-native dismiss-overlay for LogBox/RedBox overlays.
123
- Android RN/Expo Metro: run adb reverse tcp:<port> tcp:<port> before opening the app or URL; it is harmless even if already configured.
124
- Expo Go is a host shell. Use a provided project URL instead of inventing a bundle id; if no URL is provided but a target/app name is provided, open that target and do not inspect project files to find one. On iOS, prefer host + URL when the host shell is known because direct URL open can report success while leaving the runner/shell focused; verify with snapshot -i after opening:
125
- agent-device open "Expo Go" exp://127.0.0.1:8081 --platform ios
126
- agent-device snapshot -i --platform ios
127
- There is no open-url command; use open with the URL target or host + URL form.
128
- Direct iOS URL open remains valid when no host shell is known, but verify that the app UI loaded:
129
- agent-device open exp://127.0.0.1:8081 --platform ios
130
- Android uses the URL target directly; do not write open <app> <url> there:
131
- agent-device open exp://127.0.0.1:8081 --platform android
132
- Android URL/deep-link opens infer the foreground package after launch when possible, so logs/perf can remain package-bound. If perf still says no package is associated, open the host package/app id first, then open the URL in the same session.
133
- If apps lookup misses the project but shows Expo Go/dev-client and a project URL is available, open the URL/host shell; if no URL is available, ask instead of inventing an app id.
134
- Expo Dev Client/development builds: open the installed dev-client app id/name; if a dev-client URL is provided, open that URL next. For Metro setup use metro prepare --kind expo.
135
-
136
- Escalate:
137
- help debugging logs, network, alerts, traces, flaky runtime failures
138
- help react-devtools React Native performance, profiling, props/state/hooks, slow renders, rerenders
139
- help react-native React Native app automation hazards, overlays, Metro, and routing
140
- help remote remote/cloud config, tenant, lease, local service tunnels
141
- help macos desktop, frontmost-app, menu bar surfaces
142
- help dogfood exploratory QA report workflow`},debugging:{summary:"Targeted failure evidence without dumping stale context",body:`agent-device help debugging
143
-
144
- Use this when behavior fails, hangs, times out, throws alerts, or needs runtime evidence.
145
-
146
- Logs:
147
- Keep log windows small. Prefer clear, mark, reproduce, then path.
148
- agent-device logs clear --restart
149
- agent-device logs mark "before diagnostics retry"
150
- agent-device press 'id="load-diagnostics"'
151
- agent-device logs path
152
- Do not cat a full stale log into agent context. Open or grep only the relevant window when needed.
153
- logs clear --restart is the compact command to clear old logs and start a fresh capture; do not split it into logs stop, logs clear, logs start.
154
-
155
- Network:
156
- Use network dump for recent session HTTP traffic parsed from app logs.
157
- agent-device network dump --include headers
158
- agent-device network dump 20 --include all
159
- Use this instead of logs path when the question is request/response metadata.
160
- network log is a supported alias, but network dump --include headers is the clearest plan form. Do not write network log headers.
161
-
162
- Alerts:
163
- Native and platform dialogs:
164
- agent-device alert wait 3000
165
- agent-device alert accept
166
- agent-device alert dismiss
167
- Android support is snapshot-derived for runtime permission prompts and native app dialogs. iOS support is runner-derived for XCTest alerts, app-owned modal popups with native blocking markers, and blocking system dialogs. Use cheap alert get for an immediate check; use alert wait <short-ms> only when a prompt may appear after async work.
168
- If alert says no alert but a sheet is visibly on screen, treat it as app-owned UI:
169
- agent-device snapshot -i
170
- agent-device press 'label="Allow"'
171
- Do not use settings permission to answer a dialog already on screen. Reserve settings permission for setup/resetting permission state before a flow.
172
-
173
- Diagnostics and traces:
174
- Use --debug for CLI/daemon diagnostic ids and log paths.
175
- Use trace for low-level session diagnostics around one repro:
176
- agent-device trace start ./traces/diagnostics.trace
177
- agent-device press 'id="load-diagnostics"'
178
- agent-device trace stop ./traces/diagnostics.trace
179
- The trace path is positional. Do not use --path for trace start or trace stop.
180
-
181
- Stabilizers:
182
- Android animation-sensitive flows:
183
- agent-device settings animations off
184
- agent-device snapshot
185
- agent-device settings animations on
186
- Re-enable settings you changed before finishing.
187
-
188
- React Native internals:
189
- If the question is about React Native performance, profiling, props, state, hooks, render causes, slow components, or rerenders, use help react-devtools instead of inferring from screenshots or logs.`},"react-devtools":{summary:"React Native performance, profiling, and component internals",body:`agent-device help react-devtools
190
-
191
- Use this for React Native performance/profiling and internals that the accessibility tree cannot expose: components, props, state, hooks, ownership, slow renders, and rerenders.
192
-
193
- Core commands:
194
- agent-device react-devtools start
195
- agent-device react-devtools stop
196
- agent-device react-devtools status
197
- agent-device react-devtools wait --connected
198
- agent-device react-devtools wait --component <ComponentName>
199
- agent-device react-devtools count
200
- agent-device react-devtools get tree --depth 3
201
- agent-device react-devtools find <ComponentName>
202
- agent-device react-devtools find <ComponentName> --exact
203
- agent-device react-devtools get component @c5
204
- agent-device react-devtools errors
205
- agent-device react-devtools profile start
206
- agent-device react-devtools profile stop
207
- agent-device react-devtools profile slow --limit 5
208
- agent-device react-devtools profile rerenders --limit 5
209
- agent-device react-devtools profile report @c5
210
- agent-device react-devtools profile timeline --limit 20
211
- agent-device react-devtools profile export profile.json
212
- agent-device react-devtools profile diff before.json after.json --limit 10
213
-
214
- Profiling loop:
215
- 1. Verify the app is connected: react-devtools status, then wait --connected if needed.
216
- 2. If correlating with logs or network, run logs clear --restart before the first logs mark.
217
- 3. Start profiling immediately before the interaction.
218
- 4. Drive the interaction with normal agent-device commands and mark before/after the repro when timing matters.
219
- 5. Stop profiling.
220
- 6. Make one bounded first-pass survey: profile stop for the summary, profile slow --limit 5 once, profile rerenders --limit 5 once, and profile timeline --limit 20 only when commit timing matters.
221
- 7. Use profile report @cN for targeted render causes and changed props/state/hooks; use get component @cN for current props/state/hooks.
222
-
223
- Rules:
224
- Every React DevTools command is an agent-device subcommand: agent-device react-devtools ...
225
- Do not write agent-devtools, agent-react-devtools, or bare react-devtools commands in final command plans.
226
- Start with get tree --depth 3 or find <name>; use find --exact when fuzzy results are noisy.
227
- @c refs reset after reload/remount. After reload, wait --connected and inspect again.
228
- Keep the profile window narrow; unrelated navigation makes render data noisy.
229
- Do not repeatedly raise broad profile slow limits such as --limit 50, --limit 200, or --limit 500. Drill into a specific @c ref with profile report unless you have a specific target that needs more rows.
230
- For network evidence, use agent-device network dump --include headers; headers is not a positional argument.
231
- For cross-platform validation with explicit device selectors, prefer isolated --state-dir and restart react-devtools between platforms.
232
- Remote Android and iOS bridge runs normally through agent-device react-devtools; the CLI keeps the needed local service tunnel alive until agent-device react-devtools stop or disconnect. Expo support depends on the SDK's bundled React Native runtime.
233
- Remote iOS apps attempt the legacy React DevTools websocket during JavaScript startup. If the app was already open before react-devtools start, run open <bundle-id> --platform ios --relaunch, then wait --connected.
234
-
235
- Example:
236
- agent-device react-devtools status
237
- agent-device react-devtools wait --connected
238
- agent-device logs clear --restart
239
- agent-device logs mark "before catalog search"
240
- agent-device react-devtools profile start
241
- agent-device fill 'id="catalog-search"' "tart" --delay-ms 80
242
- agent-device logs mark "after catalog search"
243
- agent-device react-devtools profile stop
244
- agent-device react-devtools profile slow --limit 5
245
- agent-device react-devtools profile rerenders --limit 5
246
- agent-device react-devtools profile timeline --limit 20
247
- agent-device react-devtools profile report @c5
248
- agent-device network dump --include headers
249
-
250
- Use snapshot, screenshot, logs, network, and perf for device/app runtime evidence. Use react-devtools only when component internals or React rendering behavior matters.`},"react-native":{summary:"React Native app automation hazards and routing",body:`agent-device help react-native
251
-
252
- Use this when the target app is React Native, Expo, or a React Native dev client.
253
- This topic covers React Native-specific automation hazards and routes deeper
254
- questions to the owning help topic.
255
-
256
- Choose the next help topic:
257
- Generic navigation, selectors, refs, verification, serial commands: help workflow.
258
- Logs, network, diagnostics, traces, permission dialogs, or runtime failures: help debugging.
259
- Component tree, props/state/hooks, slow renders, rerenders, or render causes: help react-devtools.
260
- Remote/cloud config, leases, and local service tunnels: help remote.
261
-
262
- React Native dev loop:
263
- For "start from screen X" flows, prefer open --relaunch before the first snapshot so the app does not reuse a prior in-progress navigation state.
264
- JS-only change with Metro connected:
265
- agent-device metro reload
266
- agent-device find "Home"
267
- Do not use agent-device reload. Use open --relaunch for native startup reset.
268
- Android RN/Expo Metro: run adb reverse tcp:<port> tcp:<port> before opening the app or URL; it is harmless even if already configured.
269
- Expo Go/dev clients are host shells. Use provided project URLs, verify with snapshot -i after opening, and ask instead of inventing app ids or URLs. Help workflow owns the full Expo URL command shapes.
270
-
271
- Overlays and busy RN UIs:
272
- If snapshot reports a React Native warning/error overlay, handle it before interacting with the app: run agent-device react-native dismiss-overlay, then agent-device snapshot -i -c. Use refs from the new snapshot.
273
- Do not manually press warning/error text bodies, collapsed banner bodies, full-screen warning parents, or broad LogBox/RedBox refs. The dismiss-overlay command owns the narrow LogBox/RedBox targeting policy.
274
- Report the overlay in the final summary. Use screenshot --overlay-refs before dismissing only if visual evidence is required.
275
- If snapshot times out because the UI never becomes idle, Android accessibility may be blocked by busy or continuously changing app UI. After that timeout, use screenshot as visual truth instead of repeatedly retrying snapshots.
276
- Android runtime permission dialogs and native alerts are handled by alert wait/accept/dismiss. If alert reports no alert, treat the visible surface as app-owned UI and use snapshot -i plus press by label/ref.
277
-
278
- React DevTools routing:
279
- Keep the agent-device react-devtools prefix on every React DevTools command.
280
- Use help react-devtools for status/wait, component trees, props/state/hooks, profile windows, slow renders, rerenders, and remote bridge rules.
281
- If React DevTools cannot connect, report status and continue with logs, network, perf, screenshot, and trace evidence instead of blocking the whole flow.
282
-
283
- Slow-flow investigation:
284
- Keep one named session, start with session list, open, and snapshot -i.
285
- Use help react-devtools for the narrow React profile window.
286
- Use help debugging for logs clear --restart, logs mark, network dump --include headers, perf --json, traces, and runtime failure evidence.
287
- For 15-20s async work, use wait with the exact expected text or selector instead of repeated snapshots.
288
- Report React render offenders separately from network/backend waits and device frame/CPU/memory findings.`},remote:{summary:"Remote config, tenant, lease, and remote host flow",body:`agent-device help remote
289
-
290
- Use remote config or the cloud connection profile when a profile owns daemon URL, auth, tenant, run, lease, device scope, and Metro hints. Do not restate those as individual flags unless overriding intentionally.
291
-
292
- Cloud profile flow:
293
- agent-device connect
294
- agent-device open com.example.app
295
- agent-device snapshot
296
- agent-device disconnect
297
-
298
- Local profile flow:
299
- agent-device connect --remote-config ./remote-config.json
300
- agent-device open com.example.app
301
- agent-device snapshot
302
- agent-device disconnect
303
-
304
- Script flow, per-command config:
305
- agent-device open com.example.app --remote-config ./remote-config.json
306
- agent-device snapshot --remote-config ./remote-config.json
307
- agent-device disconnect --remote-config ./remote-config.json
308
-
309
- Rules:
310
- connect and disconnect are top-level commands. Do not write agent-device remote connect or agent-device remote disconnect.
311
- Use connect without --remote-config when the cloud control plane owns the connection profile.
312
- Prefer --remote-config over --daemon-base-url, --tenant, --run-id, and --lease-id when using a local profile.
313
- For self-contained scripts, pass the same --remote-config to every operational command, including disconnect; a preceding connect is optional but not required.
314
- For remote artifact installs, use install-from-source <url> or install-from-source --github-actions-artifact org/repo:artifact; do not download CI artifacts locally first.
315
- After connect, let the active remote connection supply runtime hints.
316
- For remote Android and iOS bridge React DevTools, run agent-device react-devtools normally. The CLI opens the needed local service tunnel for the DevTools daemon and keeps it alive until agent-device react-devtools stop or disconnect.
317
- Use --debug when remote connection or transport errors need diagnostic ids and remote log hints.`},macos:{summary:"macOS desktop, frontmost-app, and menu bar surfaces",body:`agent-device help macos
318
-
319
- Use macOS only when the task targets desktop apps, desktop surfaces, or menu bar extras.
320
-
321
- Open and inspect:
322
- agent-device open TextEdit --platform macos
323
- agent-device snapshot -i --platform macos
324
-
325
- Surfaces:
326
- --surface app normal app session
327
- --surface frontmost-app inspect whichever app is frontmost
328
- --surface desktop desktop-wide surface
329
- --surface menubar menu bar extras and menu bar-only apps
330
-
331
- Menu bar app example:
332
- agent-device open "Agent Device Tester Menu" --platform macos --surface menubar
333
- agent-device snapshot -i --platform macos --surface menubar
334
-
335
- Context menu example:
336
- agent-device click @e66 --button secondary --platform macos
337
- agent-device snapshot -i --platform macos
338
-
339
- Rules:
340
- Use open and snapshot -i for menu bar inspection. Do not output inspect as a command.
341
- Context menus are not ambient UI: secondary-click a visible target, then re-snapshot and use the new menu-item refs.
342
- Do not let iOS simulator-set scoping hide macOS desktop targets.
343
- Prefer refs/selectors over raw coordinates.
344
- macOS snapshot rects are window-space; use current refs or overlay refs instead of guessing coordinates.`},dogfood:{summary:"Exploratory QA workflow with reproducible evidence",body:`agent-device help dogfood
345
-
346
- Use this when asked to dogfood, exploratory test, bug hunt, QA, or find issues in an app.
347
-
348
- Goal:
349
- Find user-visible issues from runtime behavior. Do not read app source or invent findings from code.
350
- Produce a concise report with severity, repro commands, expected/actual behavior, and evidence paths.
351
-
352
- Loop:
353
- 1. Identify target app/platform; ask only if missing.
354
- 2. Create output dirs and open a named session. If auth or OTP is required, sign in or ask the user for the code.
355
- 3. Capture baseline snapshot -i and screenshot.
356
- 4. Map top-level navigation, then exercise primary flows and edge states.
357
- 5. For each issue, capture evidence and write the finding immediately, then continue.
358
- 6. Close the session and reconcile the report summary.
359
- Keep stateful commands serial within the same session. Parallel runs can pollute text fields, focus, alerts, and navigation state.
360
-
361
- Coverage:
362
- Navigation, forms, empty/error/loading states, offline or retry behavior, permissions, settings, accessibility labels, orientation/keyboard, and obvious performance stalls.
363
- React Native warning/error overlays can be real findings or test blockers. Capture them, use react-native dismiss-overlay if unrelated, re-snapshot, and report them.
364
- Expo Go/dev-client shells: use the provided exp:// or dev-client URL and record whether the shell, project load, or app UI is being tested. On iOS dogfood, prefer agent-device open "Expo Go" <url> when Expo Go is the known shell, then snapshot -i to confirm the project UI rather than the runner splash.
365
- Android RN/Expo Metro: run adb reverse tcp:<port> tcp:<port> before opening the app or URL; it is harmless even if already configured.
366
- Categories: visual, functional, UX, content, performance, diagnostics, permissions, accessibility.
367
- Severity: critical blocks a core flow/data/crashes; high breaks a major feature; medium has friction or workaround; low is polish.
368
-
369
- Evidence commands:
370
- mkdir -p ./dogfood-output/screenshots ./dogfood-output/videos ./dogfood-output/traces
371
- agent-device --session qa open <app> --platform ios
372
- agent-device --session qa snapshot -i
373
- agent-device --session qa screenshot ./dogfood-output/screenshots/initial.png
374
- agent-device --session qa screenshot ./dogfood-output/screenshots/issue-001.png --overlay-refs
375
- agent-device --session qa logs clear --restart
376
- agent-device --session qa logs mark "issue-001 repro"
377
- agent-device --session qa logs path
378
- agent-device --session qa record start ./dogfood-output/videos/issue-001.mp4
379
- agent-device --session qa record start ./dogfood-output/videos/benchmark.mp4 --hide-touches
380
- agent-device --session qa record stop
381
- agent-device --session qa close
382
-
383
- Evidence rules:
384
- Interactive/behavioral issues need step screenshots and usually a repro video.
385
- Static/on-load issues can use one screenshot; set repro video to N/A.
386
- Use screenshot --overlay-refs when showing the tappable target or broken state helps repro.
387
-
388
- Report shape:
389
- ./dogfood-output/report.md
390
- Include date, platform, target app, session, scope, severity counts, and issues.
391
- For each finding: ID, severity, category, title, affected flow/screen, repro commands, expected, actual, evidence files, notes.
392
- Target 5-10 well-evidenced issues when available. If no issues are found, report coverage completed and residual risk instead of claiming the app is bug-free.
393
-
394
- Rules:
395
- Findings must come from observed runtime behavior, not source reads.
396
- Re-snapshot after each mutation.
397
- Keep commands in the report reproducible; use selectors or refs from fresh snapshots, not guessed coordinates.
398
- Prefer refs for exploration and selectors for deterministic replay.
399
- Use logs, network, screenshot --overlay-refs, trace, perf, or react-devtools only when they add evidence to a specific issue.
400
- Never delete screenshots, videos, traces, or report artifacts during a session.
401
- Escalate to help debugging or help react-devtools when runtime symptoms require those tools.`}},rh=[{key:"config",names:["--config"],type:"string",usageLabel:"--config <path>",usageDescription:"Load CLI defaults from a specific config file"},{key:"remoteConfig",names:["--remote-config"],type:"string",usageLabel:"--remote-config <path>",usageDescription:"Load remote host + Metro workflow settings from a specific profile file"},{key:"stateDir",names:["--state-dir"],type:"string",usageLabel:"--state-dir <path>",usageDescription:"Daemon state directory (defaults to ~/.agent-device)"},{key:"daemonBaseUrl",names:["--daemon-base-url"],type:"string",usageLabel:"--daemon-base-url <url>",usageDescription:"Explicit remote HTTP daemon base URL (skip local daemon discovery/startup)"},{key:"daemonAuthToken",names:["--daemon-auth-token"],type:"string",usageLabel:"--daemon-auth-token <token>",usageDescription:"Remote HTTP daemon auth token (sent as request token and bearer header)"},{key:"daemonTransport",names:["--daemon-transport"],type:"enum",enumValues:["auto","socket","http"],usageLabel:"--daemon-transport auto|socket|http",usageDescription:"Daemon client transport preference"},{key:"daemonServerMode",names:["--daemon-server-mode"],type:"enum",enumValues:["socket","http","dual"],usageLabel:"--daemon-server-mode socket|http|dual",usageDescription:"Daemon server mode used when spawning daemon"},{key:"tenant",names:["--tenant"],type:"string",usageLabel:"--tenant <id>",usageDescription:"Tenant scope identifier for isolated daemon sessions"},{key:"sessionIsolation",names:["--session-isolation"],type:"enum",enumValues:["none","tenant"],usageLabel:"--session-isolation none|tenant",usageDescription:"Session isolation strategy (tenant prefixes session namespace)"},{key:"runId",names:["--run-id"],type:"string",usageLabel:"--run-id <id>",usageDescription:"Run identifier used for tenant lease admission checks"},{key:"leaseId",names:["--lease-id"],type:"string",usageLabel:"--lease-id <id>",usageDescription:"Lease identifier bound to tenant/run admission scope"},{key:"leaseBackend",names:["--lease-backend"],type:"enum",enumValues:["ios-simulator","ios-instance","android-instance"],usageLabel:"--lease-backend ios-simulator|ios-instance|android-instance",usageDescription:"Lease backend for remote tenant connection admission"},{key:"force",names:["--force"],type:"boolean",usageLabel:"--force",usageDescription:"Force connection state replacement when reconnecting"},{key:"noLogin",names:["--no-login"],type:"boolean",usageLabel:"--no-login",usageDescription:"Connect: fail instead of starting implicit cloud login"},{key:"sessionLock",names:["--session-lock"],type:"enum",enumValues:["reject","strip"],usageLabel:"--session-lock reject|strip",usageDescription:"Lock bound-session device routing for this CLI invocation and nested batch steps"},{key:"sessionLocked",names:["--session-locked"],type:"boolean",usageLabel:"--session-locked",usageDescription:"Deprecated alias for --session-lock reject"},{key:"sessionLockConflicts",names:["--session-lock-conflicts"],type:"enum",enumValues:["reject","strip"],usageLabel:"--session-lock-conflicts reject|strip",usageDescription:"Deprecated alias for --session-lock"},{key:"platform",names:["--platform"],type:"enum",enumValues:["ios","macos","android","linux","apple"],usageLabel:"--platform ios|macos|android|linux|apple",usageDescription:"Platform to target (`apple` aliases the Apple automation backend)"},{key:"target",names:["--target"],type:"enum",enumValues:["mobile","tv","desktop"],usageLabel:"--target mobile|tv|desktop",usageDescription:"Device target class to match"},{key:"device",names:["--device"],type:"string",usageLabel:"--device <name>",usageDescription:"Device name to target"},{key:"udid",names:["--udid"],type:"string",usageLabel:"--udid <udid>",usageDescription:"iOS device UDID"},{key:"serial",names:["--serial"],type:"string",usageLabel:"--serial <serial>",usageDescription:"Android device serial"},{key:"surface",names:["--surface"],type:"enum",enumValues:rd,usageLabel:"--surface app|frontmost-app|desktop|menubar",usageDescription:"macOS session surface for open (defaults to app)"},{key:"headless",names:["--headless"],type:"boolean",usageLabel:"--headless",usageDescription:"Boot: launch Android emulator without a GUI window"},{key:"metroHost",names:["--metro-host"],type:"string",usageLabel:"--metro-host <host>",usageDescription:"Session-scoped Metro/debug host hint"},{key:"metroPort",names:["--metro-port"],type:"int",min:1,max:65535,usageLabel:"--metro-port <port>",usageDescription:"Session-scoped Metro/debug port hint"},{key:"metroProjectRoot",names:["--project-root"],type:"string",usageLabel:"--project-root <path>",usageDescription:"metro prepare: React Native project root (default: cwd)"},{key:"metroKind",names:["--kind"],type:"enum",enumValues:["auto","react-native","expo"],usageLabel:"--kind auto|react-native|expo",usageDescription:"metro prepare: detect or force the Metro launcher kind"},{key:"metroPublicBaseUrl",names:["--public-base-url"],type:"string",usageLabel:"--public-base-url <url>",usageDescription:"metro prepare: public base URL used for direct bundle hints"},{key:"metroProxyBaseUrl",names:["--proxy-base-url"],type:"string",usageLabel:"--proxy-base-url <url>",usageDescription:"metro prepare: optional bridge origin for remote Metro access"},{key:"metroBearerToken",names:["--bearer-token"],type:"string",usageLabel:"--bearer-token <token>",usageDescription:"metro prepare: host bridge bearer token (or AGENT_DEVICE_METRO_BEARER_TOKEN; falls back to AGENT_DEVICE_DAEMON_AUTH_TOKEN)"},{key:"metroPreparePort",names:["--port"],type:"int",min:1,max:65535,usageLabel:"--port <port>",usageDescription:"metro prepare: local Metro port (default: 8081)"},{key:"metroListenHost",names:["--listen-host"],type:"string",usageLabel:"--listen-host <host>",usageDescription:"metro prepare: host Metro listens on (default: 0.0.0.0)"},{key:"metroStatusHost",names:["--status-host"],type:"string",usageLabel:"--status-host <host>",usageDescription:"metro prepare: host used for local /status polling (default: 127.0.0.1)"},{key:"metroStartupTimeoutMs",names:["--startup-timeout-ms"],type:"int",min:1,usageLabel:"--startup-timeout-ms <ms>",usageDescription:"metro prepare: timeout while waiting for Metro to become ready"},{key:"metroProbeTimeoutMs",names:["--probe-timeout-ms"],type:"int",min:1,usageLabel:"--probe-timeout-ms <ms>",usageDescription:"metro prepare: timeout for /status and proxy bridge calls"},{key:"metroRuntimeFile",names:["--runtime-file"],type:"string",usageLabel:"--runtime-file <path>",usageDescription:"metro prepare: optional file path to persist the JSON result"},{key:"metroNoReuseExisting",names:["--no-reuse-existing"],type:"boolean",usageLabel:"--no-reuse-existing",usageDescription:"metro prepare: always start a fresh Metro process"},{key:"metroNoInstallDeps",names:["--no-install-deps"],type:"boolean",usageLabel:"--no-install-deps",usageDescription:"metro prepare: skip package-manager install when node_modules is missing"},{key:"bundleUrl",names:["--bundle-url"],type:"string",usageLabel:"--bundle-url <url>",usageDescription:"Session-scoped bundle URL hint"},{key:"launchUrl",names:["--launch-url"],type:"string",usageLabel:"--launch-url <url>",usageDescription:"Session-scoped deep link / launch URL hint"},{key:"iosSimulatorDeviceSet",names:["--ios-simulator-device-set"],type:"string",usageLabel:"--ios-simulator-device-set <path>",usageDescription:"Scope iOS simulator discovery/commands to this simulator device set"},{key:"androidDeviceAllowlist",names:["--android-device-allowlist"],type:"string",usageLabel:"--android-device-allowlist <serials>",usageDescription:"Comma/space separated Android serial allowlist for discovery/selection"},{key:"activity",names:["--activity"],type:"string",usageLabel:"--activity <component>",usageDescription:"Android app launch activity (package/Activity); not for URL opens"},{key:"header",names:["--header"],type:"string",multiple:!0,usageLabel:"--header <name:value>",usageDescription:"install-from-source: repeatable HTTP header for URL downloads"},{key:"githubActionsArtifact",names:["--github-actions-artifact"],type:"string",usageLabel:"--github-actions-artifact <owner/repo:artifact>",usageDescription:"install-from-source: GitHub Actions artifact resolved by a remote daemon"},{key:"installSource",names:[],type:"string"},{key:"session",names:["--session"],type:"string",usageLabel:"--session <name>",usageDescription:"Named session"},{key:"count",names:["--count"],type:"int",min:1,max:200,usageLabel:"--count <n>",usageDescription:"Repeat count for press/swipe series"},{key:"fps",names:["--fps"],type:"int",min:1,max:120,usageLabel:"--fps <n>",usageDescription:"Record: target frames per second (iOS physical device runner)"},{key:"quality",names:["--quality"],type:"int",min:5,max:10,usageLabel:"--quality <5-10>",usageDescription:"Record: scale recording resolution from 5 (50%) through 10 (native resolution)"},{key:"hideTouches",names:["--hide-touches"],type:"boolean",usageLabel:"--hide-touches",usageDescription:"Record: skip touch-overlay post-processing for faster raw benchmark videos"},{key:"intervalMs",names:["--interval-ms"],type:"int",min:0,max:1e4,usageLabel:"--interval-ms <ms>",usageDescription:"Delay between press iterations"},{key:"delayMs",names:["--delay-ms"],type:"int",min:0,max:1e4,usageLabel:"--delay-ms <ms>",usageDescription:"Delay between typed characters"},{key:"holdMs",names:["--hold-ms"],type:"int",min:0,max:1e4,usageLabel:"--hold-ms <ms>",usageDescription:"Press hold duration for each iteration"},{key:"jitterPx",names:["--jitter-px"],type:"int",min:0,max:100,usageLabel:"--jitter-px <n>",usageDescription:"Deterministic coordinate jitter radius for press"},{key:"pixels",names:["--pixels"],type:"int",min:1,max:1e5,usageLabel:"--pixels <n>",usageDescription:"Scroll: explicit gesture distance in pixels"},{key:"doubleTap",names:["--double-tap"],type:"boolean",usageLabel:"--double-tap",usageDescription:"Use double-tap gesture per press iteration"},{key:"clickButton",names:["--button"],type:"enum",enumValues:["primary","secondary","middle"],usageLabel:"--button primary|secondary|middle",usageDescription:"Click: choose mouse button (middle reserved for future macOS support)"},{key:"backMode",names:["--in-app"],type:"enum",enumValues:["in-app","system"],setValue:"in-app",usageLabel:"--in-app",usageDescription:"Back: use app-provided back UI when available"},{key:"backMode",names:["--system"],type:"enum",enumValues:["in-app","system"],setValue:"system",usageLabel:"--system",usageDescription:"Back: use system back input or gesture when available"},{key:"pauseMs",names:["--pause-ms"],type:"int",min:0,max:1e4,usageLabel:"--pause-ms <ms>",usageDescription:"Delay between swipe iterations"},{key:"pattern",names:["--pattern"],type:"enum",enumValues:["one-way","ping-pong"],usageLabel:"--pattern one-way|ping-pong",usageDescription:"Swipe repeat pattern"},{key:"verbose",names:["--debug","--verbose","-v"],type:"boolean",usageLabel:"--debug, --verbose, -v",usageDescription:"Enable debug diagnostics and stream daemon/runner logs"},{key:"json",names:["--json"],type:"boolean",usageLabel:"--json",usageDescription:"JSON output"},{key:"help",names:["--help","-h"],type:"boolean",usageLabel:"--help, -h",usageDescription:"Print help and exit"},{key:"version",names:["--version","-V"],type:"boolean",usageLabel:"--version, -V",usageDescription:"Print version and exit"},{key:"snapshotDiff",names:["--diff"],type:"boolean",usageLabel:"--diff",usageDescription:"Snapshot: show structural diff against the previous session baseline"},{key:"saveScript",names:["--save-script"],type:"booleanOrString",usageLabel:"--save-script [path]",usageDescription:"Save session script (.ad) on close; optional custom output path"},{key:"networkInclude",names:["--include"],type:"enum",enumValues:["summary","headers","body","all"],usageLabel:"--include summary|headers|body|all",usageDescription:"Network: include headers, bodies, or both in output"},{key:"shutdown",names:["--shutdown"],type:"boolean",usageLabel:"--shutdown",usageDescription:"close: shutdown associated simulator/emulator after ending session"},{key:"relaunch",names:["--relaunch"],type:"boolean",usageLabel:"--relaunch",usageDescription:"open: terminate app process before launching it"},{key:"restart",names:["--restart"],type:"boolean",usageLabel:"--restart",usageDescription:"logs clear: stop active stream, clear logs, then start streaming again"},{key:"retainPaths",names:["--retain-paths"],type:"boolean",usageLabel:"--retain-paths",usageDescription:"install-from-source: keep materialized artifact paths after install"},{key:"retentionMs",names:["--retention-ms"],type:"int",min:1,usageLabel:"--retention-ms <ms>",usageDescription:"install-from-source: retention TTL for materialized artifact paths"},{key:"noRecord",names:["--no-record"],type:"boolean",usageLabel:"--no-record",usageDescription:"Do not record this action"},{key:"replayUpdate",names:["--update","-u"],type:"boolean",usageLabel:"--update, -u",usageDescription:"Replay: update selectors and rewrite replay file in place"},{key:"replayMaestro",names:["--maestro"],type:"boolean",usageLabel:"--maestro",usageDescription:"Replay: treat input as a Maestro YAML compatibility flow. Supported subset: launchApp without state-reset side effects, runFlow file/inline with when.platform, onFlowStart/onFlowComplete, deterministic repeat.times, tapOn, doubleTapOn, longPressOn, inputText, pasteText, openLink, assertVisible, assertNotVisible, assertTrue literal true/false, extendedWaitUntil, scroll, absolute/percentage swipe, takeScreenshot, hideKeyboard, pressKey back/enter/home, back, waitForAnimationToEnd, stopApp/killApp, setAirplaneMode, setLocation, setOrientation, supported setPermissions targets, and startRecording/stopRecording. Unsupported syntax fails loudly with a link to https://github.com/callstackincubator/agent-device/issues/558"},{key:"replayEnv",names:["-e","--env"],type:"string",multiple:!0,usageLabel:"-e KEY=VALUE, --env KEY=VALUE",usageDescription:"Replay/Test: inject or override a ${KEY} variable for the script (repeatable)"},{key:"failFast",names:["--fail-fast"],type:"boolean",usageLabel:"--fail-fast",usageDescription:"Test: stop the suite after the first failing script"},{key:"timeoutMs",names:["--timeout"],type:"int",min:1,usageLabel:"--timeout <ms>",usageDescription:"Test: maximum wall-clock time per script attempt"},{key:"retries",names:["--retries"],type:"int",min:0,max:3,usageLabel:"--retries <n>",usageDescription:"Test: retry each failed script up to n additional times"},{key:"artifactsDir",names:["--artifacts-dir"],type:"string",usageLabel:"--artifacts-dir <path>",usageDescription:"Test: root directory for suite artifacts"},{key:"reportJunit",names:["--report-junit"],type:"string",usageLabel:"--report-junit <path>",usageDescription:"Test: write a JUnit XML report for the replay suite"},{key:"steps",names:["--steps"],type:"string",usageLabel:"--steps <json>",usageDescription:"Batch: JSON array of steps"},{key:"stepsFile",names:["--steps-file"],type:"string",usageLabel:"--steps-file <path>",usageDescription:"Batch: read steps JSON from file"},{key:"batchOnError",names:["--on-error"],type:"enum",enumValues:["stop"],usageLabel:"--on-error stop",usageDescription:"Batch: stop when a step fails"},{key:"batchMaxSteps",names:["--max-steps"],type:"int",min:1,max:1e3,usageLabel:"--max-steps <n>",usageDescription:"Batch: maximum number of allowed steps"},{key:"appsFilter",names:["--all"],type:"enum",enumValues:["user-installed","all"],setValue:"all",usageLabel:"--all",usageDescription:"Apps: include system/OEM apps"},{key:"snapshotInteractiveOnly",names:["-i"],type:"boolean",usageLabel:"-i",usageDescription:"Snapshot: interactive elements only"},{key:"snapshotCompact",names:["-c"],type:"boolean",usageLabel:"-c",usageDescription:"Snapshot: compact output (drop empty structure)"},{key:"snapshotDepth",names:["--depth","-d"],type:"int",min:0,usageLabel:"--depth, -d <depth>",usageDescription:"Snapshot: limit snapshot depth"},{key:"snapshotScope",names:["--scope","-s"],type:"string",usageLabel:"--scope, -s <scope>",usageDescription:"Snapshot: scope snapshot to label/identifier"},{key:"snapshotRaw",names:["--raw"],type:"boolean",usageLabel:"--raw",usageDescription:"Snapshot: raw node output"},{key:"snapshotForceFull",names:["--force-full"],type:"boolean",usageLabel:"--force-full",usageDescription:"Snapshot: re-emit the full tree even when unchanged"},{key:"findFirst",names:["--first"],type:"boolean",usageLabel:"--first",usageDescription:"Find: pick the first match when ambiguous"},{key:"findLast",names:["--last"],type:"boolean",usageLabel:"--last",usageDescription:"Find: pick the last match when ambiguous"},{key:"out",names:["--out"],type:"string",usageLabel:"--out <path>",usageDescription:"Output path"},{key:"overlayRefs",names:["--overlay-refs"],type:"boolean",usageLabel:"--overlay-refs",usageDescription:"Screenshot: draw current snapshot refs and target rectangles onto the saved PNG; diff screenshot: also write a separate current-screen overlay guide"},...w,{key:"baseline",names:["--baseline","-b"],type:"string",usageLabel:"--baseline, -b <path>",usageDescription:"Diff screenshot: path to baseline image file"},{key:"threshold",names:["--threshold"],type:"string",usageLabel:"--threshold <0-1>",usageDescription:"Diff screenshot: color distance threshold (default 0.1)"}],rm=new Set(["json","config","remoteConfig","stateDir","daemonBaseUrl","daemonAuthToken","daemonTransport","daemonServerMode","tenant","sessionIsolation","runId","leaseId","leaseBackend","sessionLock","sessionLocked","sessionLockConflicts","help","version","verbose","platform","target","device","udid","serial","iosSimulatorDeviceSet","androidDeviceAllowlist","session","noRecord"]),rf={boot:{helpDescription:"Ensure target device/simulator is booted and ready",summary:"Boot target device/simulator",positionalArgs:[],allowedFlags:["headless"]},...eJ,connect:{usageOverride:"connect [--remote-config <path>] [--tenant <id>] [--run-id <id>] [--lease-backend <backend>] [--force] [--no-login]",helpDescription:"Connect to a remote daemon, authenticate when needed, and save remote session state. AGENT_DEVICE_CLOUD_BASE_URL is the bridge/control-plane API origin; use AGENT_DEVICE_DAEMON_AUTH_TOKEN=adc_live_... for CI/service-token automation.",summary:"Connect to remote daemon",positionalArgs:[],allowedFlags:["force","noLogin","metroProjectRoot","metroKind","metroPublicBaseUrl","metroProxyBaseUrl","metroBearerToken","metroPreparePort","metroListenHost","metroStatusHost","metroStartupTimeoutMs","metroProbeTimeoutMs","metroRuntimeFile","metroNoReuseExisting","metroNoInstallDeps","launchUrl"],skipCapabilityCheck:!0},mcp:{helpDescription:"Start the official stdio MCP discovery router. It exposes only a status tool with CLI install, verify, and starting-help guidance; device automation still runs through terminal CLI commands.",summary:"Start MCP discovery router",positionalArgs:[],allowedFlags:[],skipCapabilityCheck:!0},disconnect:{helpDescription:"Disconnect remote daemon state, stop owned Metro companion, and release lease",summary:"Disconnect remote daemon",positionalArgs:[],allowedFlags:["shutdown"],skipCapabilityCheck:!0},connection:{usageOverride:"connection status",listUsageOverride:"connection status",helpDescription:"Inspect active remote connection state",summary:"Inspect remote connection",positionalArgs:["status"],allowedFlags:[],skipCapabilityCheck:!0},auth:{usageOverride:"auth status|login|logout",listUsageOverride:"auth status|login|logout",helpDescription:"Manage cloud CLI authentication",summary:"Manage cloud authentication",positionalArgs:["status|login|logout"],allowedFlags:[],skipCapabilityCheck:!0},push:{helpDescription:"Simulate push notification payload delivery",summary:"Deliver push payload",positionalArgs:["bundleOrPackage","payloadOrJson"],allowedFlags:[]},...e$,devices:{helpDescription:"List available devices",positionalArgs:[],allowedFlags:[],skipCapabilityCheck:!0},appstate:{helpDescription:"Show foreground app/activity",positionalArgs:[],allowedFlags:[],skipCapabilityCheck:!0},metro:{usageOverride:"metro prepare (--public-base-url <url> | --proxy-base-url <url>) [--project-root <path>] [--port <port>] [--kind auto|react-native|expo]\n agent-device metro reload [--metro-host <host>] [--metro-port <port>] [--bundle-url <url>]",listUsageOverride:"metro prepare --public-base-url <url> | --proxy-base-url <url>; metro reload",helpDescription:"Prepare a local Metro runtime or ask Metro to reload connected React Native apps",summary:"Prepare Metro or reload apps",positionalArgs:["prepare|reload"],allowedFlags:["metroHost","metroPort","metroProjectRoot","metroKind","metroPublicBaseUrl","metroProxyBaseUrl","metroBearerToken","metroPreparePort","metroListenHost","metroStatusHost","metroStartupTimeoutMs","metroProbeTimeoutMs","metroRuntimeFile","metroNoReuseExisting","metroNoInstallDeps","bundleUrl"],skipCapabilityCheck:!0},clipboard:{usageOverride:"clipboard read | clipboard write <text>",listUsageOverride:"clipboard read | clipboard write <text>",helpDescription:"Read or write device clipboard text",positionalArgs:["read|write","text?"],allowsExtraPositionals:!0,allowedFlags:[]},keyboard:{usageOverride:"keyboard [status|get|dismiss]",helpDescription:"Inspect Android keyboard visibility/type or dismiss the device keyboard",summary:"Inspect or dismiss the device keyboard",positionalArgs:["action?"],allowedFlags:[]},perf:{helpDescription:"Show session performance metrics, including frame health on Android and iOS devices",summary:"Show performance metrics",positionalArgs:[],allowedFlags:[]},"react-devtools":{usageOverride:"react-devtools [...args]",listUsageOverride:"react-devtools [...args]",helpDescription:"Run pinned agent-react-devtools commands for React Native performance profiling, component trees, props/state/hooks, and render analysis",summary:"Profile React Native performance and component renders",positionalArgs:["args?"],allowsExtraPositionals:!0,allowedFlags:[],skipCapabilityCheck:!0},back:{usageOverride:"back [--in-app|--system]",helpDescription:"Navigate back with explicit app or system semantics",summary:"Go back",positionalArgs:[],allowedFlags:["backMode"]},home:{helpDescription:"Go to home screen (where supported)",summary:"Go home",positionalArgs:[],allowedFlags:[]},rotate:{usageOverride:"rotate <portrait|portrait-upside-down|landscape-left|landscape-right>",helpDescription:"Rotate device orientation on iOS and Android",summary:"Rotate device orientation",positionalArgs:["orientation"],allowedFlags:[]},"app-switcher":{helpDescription:"Open app switcher (where supported)",summary:"Open app switcher",positionalArgs:[],allowedFlags:[]},...eX,alert:{usageOverride:"alert [get|accept|dismiss|wait] [timeout]",helpDescription:"Inspect or handle platform alerts/dialogs",summary:"Inspect or handle platform alerts",positionalArgs:["action?","timeout?"],allowedFlags:[]},click:{usageOverride:"click <x y|@ref|selector>",helpDescription:"Tap/click by coordinates, snapshot ref, or selector",summary:"Tap by coordinates, ref, or selector",positionalArgs:["target"],allowsExtraPositionals:!0,allowedFlags:["count","intervalMs","holdMs","jitterPx","doubleTap","clickButton",...ej]},replay:{helpDescription:"Replay a recorded session",positionalArgs:["path"],allowedFlags:["replayUpdate","replayMaestro","replayEnv"],skipCapabilityCheck:!0},test:{usageOverride:"test <path-or-glob>...",listUsageOverride:"test <path-or-glob>...",helpDescription:"Run one or more .ad scripts as a serial test suite",summary:"Run .ad test suites",positionalArgs:["pathOrGlob"],allowsExtraPositionals:!0,allowedFlags:["replayUpdate","replayEnv","failFast","timeoutMs","retries","artifactsDir","reportJunit"],skipCapabilityCheck:!0},batch:{usageOverride:"batch [--steps <json> | --steps-file <path>]",listUsageOverride:"batch --steps <json> | --steps-file <path>",helpDescription:"Execute multiple commands in one daemon request",summary:"Run multiple commands",positionalArgs:[],allowedFlags:["steps","stepsFile","batchOnError","batchMaxSteps","out"],skipCapabilityCheck:!0},press:{usageOverride:"press <x y|@ref|selector>",helpDescription:"Tap/press by coordinates, snapshot ref, or selector (supports repeated series)",summary:"Press by coordinates, ref, or selector",positionalArgs:["targetOrX","y?"],allowsExtraPositionals:!0,allowedFlags:["count","intervalMs","holdMs","jitterPx","doubleTap",...ej]},longpress:{usageOverride:"longpress <x y|@ref|selector> [durationMs]",helpDescription:"Long press a coordinate, ref, or selector (iOS and Android)",summary:"Long press a target",positionalArgs:["targetOrX","yOrDurationMs?","durationMs?"],allowsExtraPositionals:!0,allowedFlags:[...ej]},swipe:{helpDescription:"Swipe coordinates with optional repeat pattern",summary:"Swipe coordinates",positionalArgs:["x1","y1","x2","y2","durationMs?"],allowedFlags:["count","pauseMs","pattern"]},focus:{helpDescription:"Focus input at coordinates",positionalArgs:["x","y"],allowedFlags:[]},...A,fill:{usageOverride:"fill <x> <y> <text> | fill <@ref|selector> <text>",helpDescription:"Tap then type",positionalArgs:["targetOrX","yOrText","text?"],allowsExtraPositionals:!0,allowedFlags:[...ej,"delayMs"]},scroll:{usageOverride:"scroll <direction|top|bottom> [amount] [--pixels <n>]",helpDescription:"Scroll in direction, or verify hidden content and scroll toward top/bottom",summary:"Scroll in a direction or to an edge",positionalArgs:["directionOrEdge","amount?"],allowedFlags:["pixels"]},pinch:{helpDescription:"Pinch/zoom gesture (Apple simulator or macOS app session)",positionalArgs:["scale","x?","y?"],allowedFlags:[]},"trigger-app-event":{usageOverride:"trigger-app-event <event> [payloadJson]",helpDescription:"Trigger app-defined event hook via deep link template",summary:"Trigger app event hook",positionalArgs:["event","payloadJson?"],allowedFlags:[]},record:{usageOverride:"record start [path] [--fps <n>] [--quality <5-10>] [--hide-touches] | record stop",listUsageOverride:"record start [path] | record stop",helpDescription:"Start/stop screen recording",summary:"Start or stop screen recording",positionalArgs:["start|stop","path?"],allowedFlags:["fps","quality","hideTouches"]},...eq,trace:{usageOverride:"trace start <path> | trace stop <path>",listUsageOverride:"trace start <path> | trace stop <path>",helpDescription:"Start/stop trace log capture; when an artifact path is requested, pass the same positional path to start and stop",summary:"Start or stop trace capture",positionalArgs:["start|stop","path?"],allowedFlags:[],skipCapabilityCheck:!0},logs:{usageOverride:"logs path | logs start | logs stop | logs clear [--restart] | logs doctor | logs mark [message...]",helpDescription:"Session app log info, start/stop streaming, diagnostics, and markers",summary:"Manage session app logs",positionalArgs:["path|start|stop|clear|doctor|mark","message?"],allowsExtraPositionals:!0,allowedFlags:["restart"]},network:{usageOverride:"network dump [limit] [summary|headers|body|all] [--include summary|headers|body|all] | network log [limit] [summary|headers|body|all] [--include summary|headers|body|all]",helpDescription:"Dump recent HTTP(s) traffic parsed from the session app log",summary:"Show recent HTTP traffic",positionalArgs:["dump|log","limit?","include?"],allowedFlags:["networkInclude"]},settings:{usageOverride:ev,listUsageOverride:"settings [area] [options]",helpDescription:"Toggle OS settings, animation scales, appearance, and app permissions (macOS supports only settings appearance <light|dark|toggle> and settings permission <grant|reset> <accessibility|screen-recording|input-monitoring>; wifi|airplane|location|animations remain unsupported on macOS; mobile permission actions use the active session app)",summary:"Change OS settings and app permissions",positionalArgs:["setting","state","target?","mode?"],allowedFlags:[]},session:{usageOverride:"session list",helpDescription:"List active sessions",positionalArgs:["list?"],allowedFlags:[],skipCapabilityCheck:!0}},rg=new Map,rb=new Map;for(let e of rh){for(let t of e.names)rg.set(t,e);let t=rb.get(e.key);t?t.push(e):rb.set(e.key,[e])}function rw(e){return rg.get(e)}function ry(){return rh}function rv(e){if(e)return rf[e]}function rk(e,t){let a=rv(e);if(!a?.defaults)return!1;let n=!1;for(let[e,r]of Object.entries(a.defaults))void 0===t[e]&&(t[e]=r,n=!0);return n}function rx(){return Object.keys(rf)}function rA(e){let t=e.endsWith("?"),a=t?e.slice(0,-1):e;return t?`[${a}]`:`<${a}>`}let rD=(e=`agent-device <command> [args] [--json]
402
-
403
- CLI to control iOS and Android devices for AI agents.
404
- `,t=rM("Commands:",rx().map(e=>{let t=rf[e];if(!t)throw Error(`Missing command schema for ${e}`);return{name:e,schema:t,usage:function(e,t){if(t.listUsageOverride)return t.listUsageOverride;let a=t.positionalArgs.map(a=>{var n,r,i;let s,o,l,c;return n=e,r=t,o=(s=(i=a).endsWith("?"))?i.slice(0,-1):i,c=(l=/^[a-z-]+(?:\|[a-z-]+)+$/i.test(o))||void 0!==r.usageOverride&&r.usageOverride.startsWith(`${n} ${o}`),s?l?`[${o}]`:c?o:`[${o}]`:c?o:`<${o}>`});return[e,...a].join(" ")}(e,t)}}).map(e=>({label:e.usage,description:e.schema.summary??e.schema.helpDescription}))),a=rS("Flags:",rR(rm)),n=rN("Agent Quickstart:",["Default loop: devices/apps -> open -> snapshot -i -> press/fill/get/is/wait/find -> verify -> close.",'Use selectors or refs as positional targets: id="submit", label="Allow", or @e12 from snapshot -i.',"Plain snapshot reads state; snapshot -i refreshes current interactive refs only.","Default snapshot text is an agent-facing, token-efficient view for planning and targeting actions.","Read-only visible/state question: use snapshot/get/is/find; use snapshot -i only when refs are needed.","Anti-pattern: snapshot -i followed by snapshot -i | grep ...; prior refs stay valid until app state changes, and --force-full is the explicit full re-read.","Truncated text/input preview: expand first with snapshot -s @e12, not get text.","React Native apps: read help react-native for Metro, DevTools routing, and RN-specific blockers; use react-native dismiss-overlay for LogBox/RedBox overlays.","Android RN/Expo Metro: adb reverse tcp:<port> tcp:<port> is harmless and helps the device reach any local Metro port.",'Expo Go/dev clients: use the provided URL when given; on iOS prefer open "Expo Go" <url>; Android URL opens infer the foreground package for logs/perf when possible.',"Install flows: install/install-from-source first, then open the installed id with --relaunch.",'Text: fill \'id="field-email"\' "qa@example.com" replaces; type appends after press.','Clearing text: do not use fill <target> ""; use a visible clear/reset control or report that clearing is unsupported.',"Android IME capture: if fill says input was captured by the keyboard/IME, inspect keyboard state and switch/disable handwriting before retrying; do not loop fill/type.","Run mutating commands serially against one session; parallelize only read-only commands or separate sessions.","Before taking over a shared device, run session list and reuse the active session name when one already owns the device.","Clipboard limits: iOS Allow Paste cannot be automated through XCUITest; prefill with clipboard write. Android non-ASCII should use fill/type, not raw adb input.","After mutation: refs are stale. If the next target is known, use its selector directly; otherwise refresh with snapshot -i, scoped with -s when a stable container is known.","Raw coordinates are fallback-only: use snapshot -i -c --json rects when iOS refs no-op or child refs are missing.",'Batch JSON steps use "command", "positionals", "flags"; never "args" or "step".',"Navigation: app-owned back uses back; system back uses back --system.","Verification commands must name the expected text/selector; bare screenshots/snapshots are not enough.","Debug evidence: logs clear --restart/mark/path; trace start ./path; trace stop ./path; network dump --include headers.","Use agent-device commands in final plans; raw platform tools, pseudo commands, and helper prose are wrong.","Full operating guide: agent-device help workflow. Exploratory QA: agent-device help dogfood."]),r=rM("Agent Workflows:",[{label:"help workflow",description:"Normal bootstrap, exploration, and validation loop"},{label:"help debugging",description:"Logs, network, alerts, diagnostics, and traces"},{label:"help react-native",description:"React Native app automation hazards, overlays, Metro, and routing"},{label:"help react-devtools",description:"React Native performance, profiling, component tree, and renders"},{label:"help remote",description:"Remote/cloud config, tenants, leases, and local service tunnels"},{label:"help macos",description:"Desktop, frontmost-app, and menu bar surfaces"},{label:"help dogfood",description:"Exploratory QA report workflow"}]),i=rN("Configuration:",["Default config files: ~/.agent-device/config.json, ./agent-device.json","Use --config <path> or AGENT_DEVICE_CONFIG to load one explicit config file."]),s=rM("Environment:",[{label:"AGENT_DEVICE_SESSION",description:"Default session name"},{label:"AGENT_DEVICE_PLATFORM",description:"Default platform binding"},{label:"AGENT_DEVICE_SESSION_LOCK",description:"Bound-session conflict mode"},{label:"AGENT_DEVICE_DAEMON_BASE_URL",description:"Connect to remote daemon"},{label:"AGENT_DEVICE_DAEMON_AUTH_TOKEN",description:"Remote daemon service/API token"},{label:"AGENT_DEVICE_CLOUD_BASE_URL",description:"Bridge/control-plane API origin for cloud auth and /api-keys"}]),o=rN("Examples:",["agent-device open Settings --platform ios","agent-device open TextEdit --platform macos","agent-device snapshot -i","agent-device react-devtools get tree --depth 3",'agent-device fill @e3 "test@example.com"',"agent-device replay ./session.ad","agent-device test ./suite --platform android"]),`${e}
405
- ${t}
406
-
407
- ${a}
408
-
409
- ${n}
410
-
411
- ${r}
412
-
413
- ${i}
414
-
415
- ${s}
416
-
417
- ${o}
418
- `);function rI(){return rD}function rR(e){return rh.filter(t=>e.has(t.key)&&void 0!==t.usageLabel&&void 0!==t.usageDescription)}function rS(e,t){return rM(e,t.map(e=>({label:e.usageLabel??"",description:e.usageDescription??""})))}function rM(e,t){if(0===t.length)return`${e}
419
- (none)`;let a=Math.max(...t.map(e=>e.label.length))+2,n=[e];for(let e of t)n.push(` ${e.label.padEnd(a)}${e.description}`);return n.join("\n")}function rN(e,t){return 0===t.length?`${e}
420
- (none)`:[e,...t.map(e=>` ${e}`)].join("\n")}function rO(e){var t,a;let n,r=(n=ru[e])?`${n.body}
421
-
422
- Related:
423
- agent-device help command list and global flags
424
- agent-device help <command> command-specific flags
425
- agent-device help workflow normal app automation loop
426
- `:null;if(r)return r;let i=rv(e);if(!i)return null;let s=(t=e,(a=i).usageOverride?a.usageOverride:[t,...a.positionalArgs.map(rA),...a.allowedFlags.flatMap(e=>(rb.get(e)??[]).map(e=>e.usageLabel??e.names[0])).map(e=>`[${e}]`)].join(" ")),o=rR(new Set(i.allowedFlags)),l=rR(rm),c=[];return o.length>0&&c.push(rS("Command flags:",o)),c.push(rS("Global flags:",l)),`agent-device ${s}
427
-
428
- ${i.helpDescription}
429
-
430
- Usage:
431
- agent-device ${s}
432
-
433
- ${c.join("\n\n")}
434
- `}export{eF as CAPTURE_COMMAND_CAPABILITIES,rm as GLOBAL_FLAG_KEYS,eG as REACT_NATIVE_COMMAND_CAPABILITIES,eY as SELECTOR_COMMAND_CAPABILITIES,eW as SESSION_LIFECYCLE_COMMAND_CAPABILITIES,ek as SETTINGS_INVALID_ARGS_MESSAGE,rk as applyCommandDefaults,rO as buildCommandUsageText,ea as buildMobileSnapshotPresentation,tX as buildSnapshotDisplayLines,rI as buildUsageText,eI as captureScrollEdgeState,ro as createAgentDevice,eQ as decodePng,en as deriveMobileSnapshotHiddenContentHints,t1 as detectReactNativeOverlay,et as displayNodeLabel,am as evaluateIsPredicate,eD as findMistargetedTypeRefToken,eS as formatScrollEdgeMessage,tY as formatSnapshotLine,rx as getCliCommandNames,rv as getCommandSchema,rw as getFlagDefinition,ry as getFlagDefinitions,eA as getUnsupportedMacOsSettingMessage,ex as isMacOsSettingSupported,ah as isSupportedPredicate,rc as localCommandPolicy,Z as normalizeSnapshotTree,eT as parseDeviceRotation,rp as parseSessionSurface,eC as requireIntInRange,t2 as resolveReactNativeOverlayDismissTarget,aE as resolveRectCenter,eR as runScrollEdgePasses};