agent-device 0.17.8 → 0.17.10

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 (55) hide show
  1. package/README.md +18 -0
  2. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.17.8.apk → agent-device-android-multitouch-helper-0.17.10.apk} +0 -0
  3. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.17.10.apk.sha256 +1 -0
  4. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.17.8.manifest.json → agent-device-android-multitouch-helper-0.17.10.manifest.json} +4 -4
  5. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.17.8.apk → agent-device-android-snapshot-helper-0.17.10.apk} +0 -0
  6. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.17.10.apk.sha256 +1 -0
  7. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.17.8.manifest.json → agent-device-android-snapshot-helper-0.17.10.manifest.json} +6 -6
  8. package/dist/src/1404.js +1 -0
  9. package/dist/src/2133.js +1 -1
  10. package/dist/src/2577.js +1 -1
  11. package/dist/src/2948.js +30 -1
  12. package/dist/src/4428.js +1 -1
  13. package/dist/src/587.js +1 -1
  14. package/dist/src/7394.js +5 -5
  15. package/dist/src/8184.js +1 -1
  16. package/dist/src/8358.js +1 -1
  17. package/dist/src/9581.js +2 -2
  18. package/dist/src/9722.js +36 -36
  19. package/dist/src/9836.js +1 -1
  20. package/dist/src/9919.js +5 -5
  21. package/dist/src/android.js +3 -3
  22. package/dist/src/apple.js +1 -1
  23. package/dist/src/apps.js +1 -1
  24. package/dist/src/cli-help.js +93 -27
  25. package/dist/src/cli-output.js +1 -4
  26. package/dist/src/cli.js +18 -18
  27. package/dist/src/command-surface.js +1 -1
  28. package/dist/src/find.js +1 -1
  29. package/dist/src/generic.js +5 -5
  30. package/dist/src/index.d.ts +12 -0
  31. package/dist/src/input-actions.js +1 -1
  32. package/dist/src/input-actions~1.js +1 -1
  33. package/dist/src/interaction.js +1 -1
  34. package/dist/src/internal/bin.js +1 -1
  35. package/dist/src/react-native.js +1 -1
  36. package/dist/src/record-trace-recording.js +1 -1
  37. package/dist/src/selector-runtime.js +1 -1
  38. package/dist/src/selectors.js +1 -1
  39. package/dist/src/server.js +1 -1
  40. package/dist/src/session.js +7 -7
  41. package/dist/src/snapshot.js +1 -1
  42. package/dist/src/web.js +1 -1
  43. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +92 -4
  44. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +1 -1
  45. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +130 -0
  46. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +4 -2
  47. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +18 -0
  48. package/package.json +3 -3
  49. package/server.json +2 -2
  50. package/skills/agent-device/SKILL.md +1 -0
  51. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.17.8.apk.sha256 +0 -1
  52. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.17.8.apk.sha256 +0 -1
  53. package/dist/src/6311.js +0 -27
  54. package/dist/src/7244.js +0 -1
  55. package/dist/src/9873.js +0 -1
package/dist/src/apps.js CHANGED
@@ -1,4 +1,4 @@
1
- let e;import{promises as t}from"node:fs";import i from"node:os";import a from"node:path";import{fileURLToPath as r}from"node:url";import{AppError as n}from"./733.js";import{runXcrun as o,IOS_RUNNER_CONTAINER_BUNDLE_IDS as l,resolveIosDevicectlHint as s,LAUNCH_CONSOLE_DIRECT_APP_ONLY_MESSAGE as d,runIosRunnerCommand as u,resolveAppleToolProvider as c,runSimctlForDevice as p,IOS_DEVICECTL_DEFAULT_HINT as f,readInfoPlistString as w,requireSimulatorDevice as m,buildSimctlArgsForDevice as h,runMacOsPermissionAction as y,runIosDevicectl as A,filterAppleAppsByBundlePrefix as I,runAppleToolCommand as v,listIosDeviceApps as g,quitMacOsApp as _,ensureBootedSimulator as C,LAUNCH_CONSOLE_IOS_SIMULATOR_ONLY_MESSAGE as b,getSimulatorState as N}from"./9722.js";import{runCmd as S}from"./8875.js";import{materializeInstallablePath as L,isTrustedInstallSourceUrl as D}from"./5484.js";import{parseAppearanceAction as M,summarizeCommandAttemptFailures as O,parseSettingState as P,parsePermissionTarget as F,parsePermissionAction as x}from"./8225.js";import{isWebUrl as R,createAppResolutionCache as $,isDeepLinkTarget as E,resolveIosSimulatorDeviceSetPath as k,resolveIosDeviceDeepLinkBundleId as T,retryWithPolicy as U,Deadline as B}from"./3340.js";import{emitDiagnostic as G}from"./432.js";import{getUnsupportedMacOsSettingMessage as V,requireLocationCoordinates as j}from"./9873.js";function W(e){if(!(e instanceof n)||"COMMAND_FAILED"!==e.code)return!1;let t=e.details??{};if(4!==t.exitCode)return!1;let i=String(t.stderr??"").toLowerCase();return i.includes("fbsopenapplicationserviceerrordomain")&&i.includes("the request to open")}async function H(e,t){let i=await o(h(e,["get_app_container",e.id,t]),{allowFailure:!0});if(0!==i.exitCode)return{installed:!1};let a=i.stdout.trim();if(!a)return{installed:!1};let r=await v("plutil",["-extract","CFBundleExecutable","raw","-o","-",`${a}/Info.plist`],{allowFailure:!0});if(0!==r.exitCode||!r.stdout.trim())return{installed:!0};let n=r.stdout.trim(),l=`${a}/${n}`,s=await v("otool",["-l",l],{allowFailure:!0});if(0!==s.exitCode)return{installed:!0};let d=s.stdout.toLowerCase();return{installed:!0,simulatorCompatible:d.includes("iossimulator")||d.includes("platform 7")}}async function K(e,t){let i;if("url"===e.kind&&!D(e.url))throw new n("INVALID_ARGS","iOS install_from_source URL sources are only supported for trusted artifact services such as GitHub Actions and EAS. Use a path source for other hosts.");let a=await L({source:e,isInstallablePath:(e,t)=>t.isDirectory()&&e.toLowerCase().endsWith(".app")||t.isFile()&&e.toLowerCase().endsWith(".ipa"),installableLabel:"iOS installable (.app or .ipa)",allowArchiveExtraction:"url"!==e.kind||D(e.url),signal:t?.signal});try{let e=i=await q(a.installablePath,t),r=await z(e.installPath);return{archivePath:a.archivePath??(a.installablePath.toLowerCase().endsWith(".ipa")?a.installablePath:void 0),installablePath:e.installPath,bundleId:r.bundleId,appName:r.appName,cleanup:async()=>{await e.cleanup(),await a.cleanup()}}}catch(e){try{await i?.cleanup()}finally{await a.cleanup()}throw e}}async function z(e){let t=a.join(e,"Info.plist"),[i,r,n]=await Promise.all([w(t,"CFBundleIdentifier"),w(t,"CFBundleDisplayName"),w(t,"CFBundleName")]);return{bundleId:i,appName:r??n}}async function q(e,r){if(!e.toLowerCase().endsWith(".ipa"))return{installPath:e,cleanup:async()=>{}};let o=await t.mkdtemp(a.join(i.tmpdir(),"agent-device-ios-ipa-")),l=async()=>{await t.rm(o,{recursive:!0,force:!0})};try{await S("unzip",["-q",e,"-d",o]);let i=a.join(o,"Payload"),s=(await t.readdir(i,{withFileTypes:!0}).catch(()=>{throw new n("INVALID_ARGS","Invalid IPA: missing Payload directory")})).filter(e=>e.isDirectory()&&e.name.toLowerCase().endsWith(".app")).map(e=>({installPath:a.join(i,e.name),bundleName:e.name.replace(/\.app$/i,"")})),d=s[0];if(void 0!==d&&1===s.length)return{installPath:d.installPath,cleanup:l};if(0===s.length)throw new n("INVALID_ARGS","Invalid IPA: expected at least one .app under Payload, found 0");await J(s);let u=r?.appIdentifierHint?.trim();if(u){let e=function(e,t){let i=t.toLowerCase(),a=e.filter(e=>e.bundleName.toLowerCase()===i);if(1===a.length)return a[0];if(a.length>1)throw new n("INVALID_ARGS",`Invalid IPA: multiple app bundles matched "${t}" by name. Use a bundle id hint instead.`);if(t.includes(".")){let t=e.filter(e=>e.bundleId?.toLowerCase()===i);if(1===t.length)return t[0]}}(s,u);if(e)return{installPath:e.installPath,cleanup:l};throw new n("INVALID_ARGS",`Invalid IPA: found ${s.length} .app bundles under Payload and none matched "${u}". Available bundles: ${s.map(Z).join(", ")}`)}throw new n("INVALID_ARGS",`Invalid IPA: found ${s.length} .app bundles under Payload. Pass an app identifier or bundle name matching one of: ${s.map(Z).join(", ")}`)}catch(e){throw await l(),e}}async function J(e){await Promise.all(e.map(async e=>{if(e.bundleId&&e.appName)return;let t=await z(e.installPath);e.bundleId=e.bundleId??t.bundleId,e.appName=e.appName??t.appName}))}function Z(e){let t=e.bundleId??e.appName;return t?`${e.bundleName}.app (${t})`:`${e.bundleName}.app`}let Q={settings:"com.apple.systempreferences"},X=/^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/,Y={platform:"macos",deviceId:"host",variant:"all"},ee=$();async function et(e){let t=e.trim(),i=Q[t.toLowerCase()];if(i)return i;if(X.test(t))return t;let a=ee.get(Y,t);if(a)return a;let r=(await es("all")).filter(e=>e.name.toLowerCase()===t.toLowerCase()),o=r[0];if(void 0!==o&&1===r.length)return ee.set(Y,t,o.bundleId);if(r.length>1)throw new n("INVALID_ARGS",`Multiple apps matched "${e}"`,{matches:r});throw new n("APP_NOT_INSTALLED",`No app found matching "${e}"`)}async function ei(e,t,i){let a=i?.url?.trim();if(a){if(!E(a))throw new n("INVALID_ARGS","open <app> <url> requires a valid URL target");let e=i?.appBundleId??await et(t);await ed().openBundle(e,a);return}let r=t.trim();if(E(r))return void await ed().openTarget(r);let o=i?.appBundleId??await et(r);await ed().openBundle(o)}async function ea(e,t){let i=await et(t),a=await _(i);if(a.running&&!a.terminated&&!a.forceTerminated)throw new n("COMMAND_FAILED",`Failed to close macOS app ${t}`,{bundleId:i,running:a.running,terminated:a.terminated,forceTerminated:a.forceTerminated})}async function er(){return await ed().readClipboard()}async function en(e){await ed().writeClipboard(e)}async function eo(){return await ed().readDarkMode()}async function el(e){let t=M(e),i="toggle"===t?!await eo():"dark"===t;await ed().setDarkMode(i)}async function es(e){return await ed().listApps(e)}function ed(){let e=c().macosHost;if(!e)throw new n("UNSUPPORTED_OPERATION","macOS host provider is not available");return e}function eu(e){if(!(e instanceof n))return{reason:e instanceof Error?e.message:String(e)};let t=e.details??{},i=Array.isArray(t.args)?t.args.filter(e=>"string"==typeof e).join(" "):void 0;return{errorCode:e.code,reason:e.message,timeoutMs:"number"==typeof t.timeoutMs?t.timeoutMs:void 0,exitCode:"number"==typeof t.exitCode?t.exitCode:void 0,stderr:"string"==typeof t.stderr&&t.stderr.trim()?t.stderr:void 0,stdout:"string"==typeof t.stdout&&t.stdout.trim()?t.stdout:void 0,commandArgs:i}}let ec=["--time","9:41","--dataNetwork","wifi","--wifiMode","active","--wifiBars","3","--batteryState","charged","--batteryLevel","100"],ep={0:"hide",1:"wifi",6:"3g",7:"4g",8:"lte",9:"lte-a",10:"lte+",11:"5g",12:"5g+",13:"5g-uwb",14:"5g-uc"},ef={1:"searching",2:"failed",3:"active"},ew={0:"notSupported",1:"searching",2:"failed",3:"active"};async function em(e){let t=null,i=!1;try{t=await ey(e),i=!0}catch(t){ev(e,"snapshot_failed",t)}try{await eA(e),await eI(e,ec)}catch(t){ev(e,"prepare_failed",t)}return async()=>{await eh(e,i?t:null)}}async function eh(e,t){var i,a;let r;await eA(e),t&&await eI(e,(r=[],(i=t).dataNetwork&&r.push("--dataNetwork",i.dataNetwork),i.wifiMode&&r.push("--wifiMode",i.wifiMode),void 0!==i.wifiBars&&("wifi"===i.dataNetwork||i.wifiMode)&&r.push("--wifiBars",i.wifiBars),i.cellularMode&&r.push("--cellularMode",i.cellularMode),void 0!==i.cellularBars&&(i.cellularMode||(a=i.dataNetwork)&&"hide"!==a&&"wifi"!==a||void 0!==i.operatorName)&&r.push("--cellularBars",i.cellularBars),void 0!==i.operatorName&&r.push("--operatorName",i.operatorName),r))}async function ey(e){var t;let i=await (t=["status_bar",e.id,"list"],p(e,t,{allowFailure:!0}));if(0!==i.exitCode)throw new n("COMMAND_FAILED","Failed to read simulator status bar overrides",{exitCode:i.exitCode,stdout:i.stdout,stderr:i.stderr});return function(e){let t={};for(let i of e.split("\n").map(e=>e.trim()).filter(e=>e.length>0&&"Current Status Bar Overrides:"!==e&&!/^=+$/.test(e))){let e=/^DataNetworkType:\s+(\d+)$/.exec(i);if(e){let i=Number(e[1]),a=ep[i];if(!a)throw new n("COMMAND_FAILED",`Unsupported simulator data network type: ${i}`);t.dataNetwork=a;continue}let a=/^WiFi Mode:\s+(\d+),\s+WiFi Bars:\s+(\d+)$/.exec(i);if(a){let e=ef[Number(a[1])];e&&(t.wifiMode=e),t.wifiBars=a[2];continue}let r=/^Cell Mode:\s+(\d+),\s+Cell Bars:\s+(\d+)$/.exec(i);if(r){let e=Number(r[1]),i=ew[e];if(!i)throw new n("COMMAND_FAILED",`Unsupported simulator cellular mode: ${e}`);t.cellularMode=i,t.cellularBars=r[2];continue}let o=/^Operator Name:\s*(.*)$/.exec(i);if(o){t.operatorName=o[1]??"";continue}}return 0===Object.keys(t).length?null:t}(i.stdout)}async function eA(e){await p(e,["status_bar",e.id,"clear"],void 0)}async function eI(e,t){0!==t.length&&await p(e,["status_bar",e.id,"override",...t],void 0)}function ev(e,t,i){G({level:"warn",phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...eu(i)}})}let eg={ensureBooted:C,prepareStatusBarForScreenshot:em,captureWithRetry:eb,captureWithRunner:eN,shouldFallbackToRunner:eP};async function e_(e,t,i,a,r){if("macos"===e.platform)return void await eN(e,t,i,a,r);if("simulator"===e.kind)return void await eC(e,t,i,a,void 0,r);try{await A(["device","screenshot","--device",e.id,t],{action:"capture iOS screenshot",deviceId:e.id});return}catch(t){if(!function(e){if(!(e instanceof n)||"COMMAND_FAILED"!==e.code)return!1;let t=e.details??{},i="string"==typeof t.stdout?t.stdout:"",a="string"==typeof t.stderr?t.stderr:"",r=`${e.message}
1
+ let e;import{promises as t}from"node:fs";import i from"node:os";import a from"node:path";import{fileURLToPath as r}from"node:url";import{AppError as n}from"./733.js";import{runXcrun as o,IOS_RUNNER_CONTAINER_BUNDLE_IDS as l,resolveIosDevicectlHint as s,LAUNCH_CONSOLE_DIRECT_APP_ONLY_MESSAGE as d,runIosRunnerCommand as u,resolveAppleToolProvider as c,runSimctlForDevice as p,IOS_DEVICECTL_DEFAULT_HINT as f,readInfoPlistString as w,requireSimulatorDevice as m,buildSimctlArgsForDevice as h,runMacOsPermissionAction as y,runIosDevicectl as A,filterAppleAppsByBundlePrefix as I,runAppleToolCommand as v,listIosDeviceApps as g,quitMacOsApp as _,ensureBootedSimulator as C,LAUNCH_CONSOLE_IOS_SIMULATOR_ONLY_MESSAGE as b,getSimulatorState as N}from"./9722.js";import{runCmd as S}from"./8875.js";import{materializeInstallablePath as L,isTrustedInstallSourceUrl as D}from"./5484.js";import{parseAppearanceAction as M,summarizeCommandAttemptFailures as O,parseSettingState as P,parsePermissionTarget as F,parsePermissionAction as x}from"./8225.js";import{isWebUrl as R,createAppResolutionCache as $,isDeepLinkTarget as E,resolveIosSimulatorDeviceSetPath as k,resolveIosDeviceDeepLinkBundleId as T,retryWithPolicy as U,Deadline as B}from"./3340.js";import{emitDiagnostic as G}from"./432.js";import{getUnsupportedMacOsSettingMessage as V,requireLocationCoordinates as j}from"./2948.js";function W(e){if(!(e instanceof n)||"COMMAND_FAILED"!==e.code)return!1;let t=e.details??{};if(4!==t.exitCode)return!1;let i=String(t.stderr??"").toLowerCase();return i.includes("fbsopenapplicationserviceerrordomain")&&i.includes("the request to open")}async function H(e,t){let i=await o(h(e,["get_app_container",e.id,t]),{allowFailure:!0});if(0!==i.exitCode)return{installed:!1};let a=i.stdout.trim();if(!a)return{installed:!1};let r=await v("plutil",["-extract","CFBundleExecutable","raw","-o","-",`${a}/Info.plist`],{allowFailure:!0});if(0!==r.exitCode||!r.stdout.trim())return{installed:!0};let n=r.stdout.trim(),l=`${a}/${n}`,s=await v("otool",["-l",l],{allowFailure:!0});if(0!==s.exitCode)return{installed:!0};let d=s.stdout.toLowerCase();return{installed:!0,simulatorCompatible:d.includes("iossimulator")||d.includes("platform 7")}}async function K(e,t){let i;if("url"===e.kind&&!D(e.url))throw new n("INVALID_ARGS","iOS install_from_source URL sources are only supported for trusted artifact services such as GitHub Actions and EAS. Use a path source for other hosts.");let a=await L({source:e,isInstallablePath:(e,t)=>t.isDirectory()&&e.toLowerCase().endsWith(".app")||t.isFile()&&e.toLowerCase().endsWith(".ipa"),installableLabel:"iOS installable (.app or .ipa)",allowArchiveExtraction:"url"!==e.kind||D(e.url),signal:t?.signal});try{let e=i=await q(a.installablePath,t),r=await z(e.installPath);return{archivePath:a.archivePath??(a.installablePath.toLowerCase().endsWith(".ipa")?a.installablePath:void 0),installablePath:e.installPath,bundleId:r.bundleId,appName:r.appName,cleanup:async()=>{await e.cleanup(),await a.cleanup()}}}catch(e){try{await i?.cleanup()}finally{await a.cleanup()}throw e}}async function z(e){let t=a.join(e,"Info.plist"),[i,r,n]=await Promise.all([w(t,"CFBundleIdentifier"),w(t,"CFBundleDisplayName"),w(t,"CFBundleName")]);return{bundleId:i,appName:r??n}}async function q(e,r){if(!e.toLowerCase().endsWith(".ipa"))return{installPath:e,cleanup:async()=>{}};let o=await t.mkdtemp(a.join(i.tmpdir(),"agent-device-ios-ipa-")),l=async()=>{await t.rm(o,{recursive:!0,force:!0})};try{await S("unzip",["-q",e,"-d",o]);let i=a.join(o,"Payload"),s=(await t.readdir(i,{withFileTypes:!0}).catch(()=>{throw new n("INVALID_ARGS","Invalid IPA: missing Payload directory")})).filter(e=>e.isDirectory()&&e.name.toLowerCase().endsWith(".app")).map(e=>({installPath:a.join(i,e.name),bundleName:e.name.replace(/\.app$/i,"")})),d=s[0];if(void 0!==d&&1===s.length)return{installPath:d.installPath,cleanup:l};if(0===s.length)throw new n("INVALID_ARGS","Invalid IPA: expected at least one .app under Payload, found 0");await J(s);let u=r?.appIdentifierHint?.trim();if(u){let e=function(e,t){let i=t.toLowerCase(),a=e.filter(e=>e.bundleName.toLowerCase()===i);if(1===a.length)return a[0];if(a.length>1)throw new n("INVALID_ARGS",`Invalid IPA: multiple app bundles matched "${t}" by name. Use a bundle id hint instead.`);if(t.includes(".")){let t=e.filter(e=>e.bundleId?.toLowerCase()===i);if(1===t.length)return t[0]}}(s,u);if(e)return{installPath:e.installPath,cleanup:l};throw new n("INVALID_ARGS",`Invalid IPA: found ${s.length} .app bundles under Payload and none matched "${u}". Available bundles: ${s.map(Z).join(", ")}`)}throw new n("INVALID_ARGS",`Invalid IPA: found ${s.length} .app bundles under Payload. Pass an app identifier or bundle name matching one of: ${s.map(Z).join(", ")}`)}catch(e){throw await l(),e}}async function J(e){await Promise.all(e.map(async e=>{if(e.bundleId&&e.appName)return;let t=await z(e.installPath);e.bundleId=e.bundleId??t.bundleId,e.appName=e.appName??t.appName}))}function Z(e){let t=e.bundleId??e.appName;return t?`${e.bundleName}.app (${t})`:`${e.bundleName}.app`}let Q={settings:"com.apple.systempreferences"},X=/^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/,Y={platform:"macos",deviceId:"host",variant:"all"},ee=$();async function et(e){let t=e.trim(),i=Q[t.toLowerCase()];if(i)return i;if(X.test(t))return t;let a=ee.get(Y,t);if(a)return a;let r=(await es("all")).filter(e=>e.name.toLowerCase()===t.toLowerCase()),o=r[0];if(void 0!==o&&1===r.length)return ee.set(Y,t,o.bundleId);if(r.length>1)throw new n("INVALID_ARGS",`Multiple apps matched "${e}"`,{matches:r});throw new n("APP_NOT_INSTALLED",`No app found matching "${e}"`)}async function ei(e,t,i){let a=i?.url?.trim();if(a){if(!E(a))throw new n("INVALID_ARGS","open <app> <url> requires a valid URL target");let e=i?.appBundleId??await et(t);await ed().openBundle(e,a);return}let r=t.trim();if(E(r))return void await ed().openTarget(r);let o=i?.appBundleId??await et(r);await ed().openBundle(o)}async function ea(e,t){let i=await et(t),a=await _(i);if(a.running&&!a.terminated&&!a.forceTerminated)throw new n("COMMAND_FAILED",`Failed to close macOS app ${t}`,{bundleId:i,running:a.running,terminated:a.terminated,forceTerminated:a.forceTerminated})}async function er(){return await ed().readClipboard()}async function en(e){await ed().writeClipboard(e)}async function eo(){return await ed().readDarkMode()}async function el(e){let t=M(e),i="toggle"===t?!await eo():"dark"===t;await ed().setDarkMode(i)}async function es(e){return await ed().listApps(e)}function ed(){let e=c().macosHost;if(!e)throw new n("UNSUPPORTED_OPERATION","macOS host provider is not available");return e}function eu(e){if(!(e instanceof n))return{reason:e instanceof Error?e.message:String(e)};let t=e.details??{},i=Array.isArray(t.args)?t.args.filter(e=>"string"==typeof e).join(" "):void 0;return{errorCode:e.code,reason:e.message,timeoutMs:"number"==typeof t.timeoutMs?t.timeoutMs:void 0,exitCode:"number"==typeof t.exitCode?t.exitCode:void 0,stderr:"string"==typeof t.stderr&&t.stderr.trim()?t.stderr:void 0,stdout:"string"==typeof t.stdout&&t.stdout.trim()?t.stdout:void 0,commandArgs:i}}let ec=["--time","9:41","--dataNetwork","wifi","--wifiMode","active","--wifiBars","3","--batteryState","charged","--batteryLevel","100"],ep={0:"hide",1:"wifi",6:"3g",7:"4g",8:"lte",9:"lte-a",10:"lte+",11:"5g",12:"5g+",13:"5g-uwb",14:"5g-uc"},ef={1:"searching",2:"failed",3:"active"},ew={0:"notSupported",1:"searching",2:"failed",3:"active"};async function em(e){let t=null,i=!1;try{t=await ey(e),i=!0}catch(t){ev(e,"snapshot_failed",t)}try{await eA(e),await eI(e,ec)}catch(t){ev(e,"prepare_failed",t)}return async()=>{await eh(e,i?t:null)}}async function eh(e,t){var i,a;let r;await eA(e),t&&await eI(e,(r=[],(i=t).dataNetwork&&r.push("--dataNetwork",i.dataNetwork),i.wifiMode&&r.push("--wifiMode",i.wifiMode),void 0!==i.wifiBars&&("wifi"===i.dataNetwork||i.wifiMode)&&r.push("--wifiBars",i.wifiBars),i.cellularMode&&r.push("--cellularMode",i.cellularMode),void 0!==i.cellularBars&&(i.cellularMode||(a=i.dataNetwork)&&"hide"!==a&&"wifi"!==a||void 0!==i.operatorName)&&r.push("--cellularBars",i.cellularBars),void 0!==i.operatorName&&r.push("--operatorName",i.operatorName),r))}async function ey(e){var t;let i=await (t=["status_bar",e.id,"list"],p(e,t,{allowFailure:!0}));if(0!==i.exitCode)throw new n("COMMAND_FAILED","Failed to read simulator status bar overrides",{exitCode:i.exitCode,stdout:i.stdout,stderr:i.stderr});return function(e){let t={};for(let i of e.split("\n").map(e=>e.trim()).filter(e=>e.length>0&&"Current Status Bar Overrides:"!==e&&!/^=+$/.test(e))){let e=/^DataNetworkType:\s+(\d+)$/.exec(i);if(e){let i=Number(e[1]),a=ep[i];if(!a)throw new n("COMMAND_FAILED",`Unsupported simulator data network type: ${i}`);t.dataNetwork=a;continue}let a=/^WiFi Mode:\s+(\d+),\s+WiFi Bars:\s+(\d+)$/.exec(i);if(a){let e=ef[Number(a[1])];e&&(t.wifiMode=e),t.wifiBars=a[2];continue}let r=/^Cell Mode:\s+(\d+),\s+Cell Bars:\s+(\d+)$/.exec(i);if(r){let e=Number(r[1]),i=ew[e];if(!i)throw new n("COMMAND_FAILED",`Unsupported simulator cellular mode: ${e}`);t.cellularMode=i,t.cellularBars=r[2];continue}let o=/^Operator Name:\s*(.*)$/.exec(i);if(o){t.operatorName=o[1]??"";continue}}return 0===Object.keys(t).length?null:t}(i.stdout)}async function eA(e){await p(e,["status_bar",e.id,"clear"],void 0)}async function eI(e,t){0!==t.length&&await p(e,["status_bar",e.id,"override",...t],void 0)}function ev(e,t,i){G({level:"warn",phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...eu(i)}})}let eg={ensureBooted:C,prepareStatusBarForScreenshot:em,captureWithRetry:eb,captureWithRunner:eN,shouldFallbackToRunner:eP};async function e_(e,t,i,a,r){if("macos"===e.platform)return void await eN(e,t,i,a,r);if("simulator"===e.kind)return void await eC(e,t,i,a,void 0,r);try{await A(["device","screenshot","--device",e.id,t],{action:"capture iOS screenshot",deviceId:e.id});return}catch(t){if(!function(e){if(!(e instanceof n)||"COMMAND_FAILED"!==e.code)return!1;let t=e.details??{},i="string"==typeof t.stdout?t.stdout:"",a="string"==typeof t.stderr?t.stderr:"",r=`${e.message}
2
2
  ${i}
3
3
  ${a}`.toLowerCase();return r.includes("unknown option '--device'")||r.includes("unknown subcommand")&&r.includes("screenshot")||r.includes("unrecognized subcommand")&&r.includes("screenshot")}(t))throw t;eM(e,"devicectl_screenshot",t)}await eN(e,t,i,a,r)}async function eC(e,t,i,a,r=eg,o){if("simulator"!==e.kind)throw new n("UNSUPPORTED_OPERATION","Simulator screenshot fallback flow supports only iOS simulators");let l="object"==typeof a&&null!==a?a:r;await l.ensureBooted(e);let s=async()=>{};try{s=await l.prepareStatusBarForScreenshot(e)}catch(t){eO(e,"prepare_failed",t)}try{try{await l.captureWithRetry(e,t);return}catch(t){if(!l.shouldFallbackToRunner(t))throw t;eM(e,"simctl_screenshot",t)}await l.captureWithRunner(e,t,i,"boolean"==typeof a?a:void 0,o)}finally{await s().catch(t=>eO(e,"restore_failed",t))}}async function eb(e,t){let i=B.fromTimeoutMs(2e4);await U(async({deadline:i})=>{var a;await (a=["io",e.id,"screenshot",t],p(e,a,{timeoutMs:Math.max(1e3,i?.remainingMs()??2e4)}))},{maxAttempts:5,baseDelayMs:1e3,maxDelayMs:5e3,jitter:.2,shouldRetry:e=>eP(e)},{deadline:i,phase:"ios_simulator_screenshot"})}async function eN(e,i,a,r,o){let l=(await u(e,{command:"screenshot",appBundleId:a,fullscreen:r},o)).message;if(!l)throw new n("COMMAND_FAILED","Failed to capture iOS screenshot: runner returned no file path");"macos"===e.platform?await t.copyFile(l,i):"simulator"===e.kind?await eL(e,l,i):await eS(e,l,i)}async function eS(e,t,i){let a=B.fromTimeoutMs(2e4),r={exitCode:1,stdout:"",stderr:""};for(let n of l)if(0===(r=await o(["devicectl","device","copy","from","--device",e.id,"--source",t,"--destination",i,"--domain-type","appDataContainer","--domain-identifier",n],{allowFailure:!0,timeoutMs:eD(a,2e4,"runner screenshot copy")})).exitCode)return;let s=r.stderr.trim()||r.stdout.trim()||`devicectl exited with code ${r.exitCode}`;throw new n("COMMAND_FAILED",`Failed to capture iOS screenshot: ${s}`)}async function eL(e,i,r){let o=B.fromTimeoutMs(2e4),s="Unable to locate runner container for simulator screenshot";for(let n of l){var d;let l=await (d=["get_app_container",e.id,n,"data"],p(e,d,{allowFailure:!0,timeoutMs:eD(o,2e4,"runner screenshot container lookup")}));if(0!==l.exitCode){let e=l.stderr.trim();e&&(s=e);continue}let u=l.stdout.trim();if(!u){s="simctl get_app_container returned empty output";continue}for(let e of function(e,t){let i=a.resolve(e),r=t.trim();if(!r)return[];let n=[],o=new Set,l=e=>{let t=a.normalize(e);o.has(t)||(o.add(t),n.push(t))},s=r.replace(/^\/+/,""),d=s.replace(/\\/g,"/");if(s&&l(a.join(i,s)),a.isAbsolute(r)&&l(a.normalize(r)),d.startsWith("tmp/"))l(a.join(i,d));else{let e=d.lastIndexOf("/tmp/");if(e>=0){let t=d.slice(e+1);l(a.join(i,t))}}let u=a.basename(r);return u&&l(a.join(i,"tmp",u)),n}(u,i))try{await t.copyFile(e,r);return}catch(e){s=e instanceof Error?e.message:String(e)}}throw new n("COMMAND_FAILED",`Failed to capture iOS screenshot: ${s}`)}function eD(e,t,i){let a=e.remainingMs();if(a>0)return a;throw new n("COMMAND_FAILED",`iOS ${i} timed out after ${t}ms`,{timeoutMs:t,step:i})}function eM(e,t,i){let a=eu(i);G({level:"warn",phase:"ios_screenshot_fallback",data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,from:t,to:"runner",...a}})}function eO(e,t,i){G({level:"warn",phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...eu(i)}})}function eP(e){if(!(e instanceof n)||"COMMAND_FAILED"!==e.code)return!1;let t=e.details??{},i="string"==typeof t.stdout?t.stdout:"",a="string"==typeof t.stderr?t.stderr:"",r=Array.isArray(t.args)?t.args.filter(e=>"string"==typeof e).join(" "):"",o=`${e.message}
4
4
  ${i}
@@ -1,4 +1,4 @@
1
- import{listCliCommandNames as e}from"./2133.js";import{getCliCommandSchema as t,getCommandSchema as o}from"./9581.js";import{getFlagDefinitions as a,GLOBAL_FLAG_KEYS as s}from"./9873.js";let n=[{label:"help workflow",description:"Normal bootstrap, exploration, and validation loop"},{label:"help debugging",description:"Logs, network, perf memory, 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 physical-device",description:"Connected phone/tablet setup and iOS signing prerequisites"},{label:"help remote",description:"Remote/cloud config, tenants, leases, and local service tunnels"},{label:"help web",description:"Minimal browser sessions through agent-browser"},{label:"help macos",description:"Desktop, frontmost-app, and menu bar surfaces"},{label:"help dogfood",description:"Exploratory QA report workflow"}],r=["Planning output contract: when asked to plan commands, output command lines only: no prose, numbering, Markdown fences, pipes, or shell helpers.","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: direct Android localhost URL opens with a port auto-configure host reachability.",'Expo Go/dev clients: use the provided URL when given; on iOS use open "Expo Go" <url> --platform ios, then snapshot -i --platform ios to verify project UI. Do not use plain snapshot or snapshot --diff for this recovery check. 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.","Implicit default sessions are scoped to the current worktree; if a prompt names a Session, include --session <name> on every command in that flow.","Run mutating commands serially within one session; parallelize only read-only commands or separate sessions/devices.","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. Do not use tap; use press or click.","Raw coordinates are fallback-only: use snapshot -i --json rects when iOS refs no-op or child refs are missing, then verify the action with diff snapshot -i or snapshot --diff.","Sparse or AX-unavailable snapshot: use screenshot for visual truth, press the visible coordinate to leave the bad screen, then retry AX with snapshot -i.","macOS context menus use click <ref> --button secondary, then snapshot -i. Longpress is for mobile hold gestures, not macOS secondary-click menus.","Remote workflow profiles use --remote-config on operational commands. Do not substitute --config; --config only loads CLI defaults.",'Batch JSON steps use "command" and structured "input"; legacy "positionals"/"flags" steps still run in CLI but are deprecated until the next major version.',"Navigation: app-owned back uses back; system back uses back --system.","Web browser sessions: read help web; first slice is web setup if needed -> web doctor -> open <url> --platform web -> snapshot -i -> click/fill/get/is/find/wait/screenshot -> close.","Verification commands must name the expected text/selector; bare screenshots/snapshots are not enough.","Debug evidence: Session state contains request diagnostics and runner.log; use logs clear --restart/mark/path, trace, and network dump --include headers for app evidence.","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."],i=["Default config files: ~/.agent-device/config.json, ./agent-device.json","Use --config <path> or AGENT_DEVICE_CONFIG to load one explicit config file."],c=[{label:"AGENT_DEVICE_SESSION",description:"Explicit 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"}],l=["agent-device open Settings --platform ios","agent-device open https://example.com --platform web","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"],d={workflow:{summary:"Normal agent-device bootstrap, exploration, and validation loop",body:`agent-device help workflow
1
+ import{listCliCommandNames as e}from"./2133.js";import{getCliCommandSchema as t,getCommandSchema as a}from"./9581.js";import{getFlagDefinitions as o,GLOBAL_FLAG_KEYS as s}from"./2948.js";let n=[{label:"help workflow",description:"Normal bootstrap, exploration, and validation loop"},{label:"help debugging",description:"Logs, network, perf memory, 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 cdp",description:"React Native CDP targets, JS heap snapshots, and leak triage"},{label:"help physical-device",description:"Connected phone/tablet setup and iOS signing prerequisites"},{label:"help remote",description:"Remote/cloud config, tenants, leases, and local service tunnels"},{label:"help web",description:"Minimal browser sessions through agent-browser"},{label:"help macos",description:"Desktop, frontmost-app, and menu bar surfaces"},{label:"help dogfood",description:"Exploratory QA report workflow"}],r=["Planning output contract: when asked to plan commands, output command lines only: no prose, numbering, Markdown fences, pipes, or shell helpers.","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.","React Native JS memory leaks: read help cdp; use heap usage samples for a quick signal, then snapshot diff/leak-triplet for retained object proof.","Android RN/Expo Metro: direct Android localhost URL opens with a port auto-configure host reachability.",'Expo Go/dev clients: use the provided URL when given; on iOS use open "Expo Go" <url> --platform ios, then snapshot -i --platform ios to verify project UI. Do not use plain snapshot or snapshot --diff for this recovery check. 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.","Implicit default sessions are scoped to the current worktree; if a prompt names a Session, include --session <name> on every command in that flow.","Run mutating commands serially within one session; parallelize only read-only commands or separate sessions/devices.","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. Do not use tap; use press or click.","Raw coordinates are fallback-only: use snapshot -i --json rects when iOS refs no-op or child refs are missing, then verify the action with diff snapshot -i or snapshot --diff.","Sparse or AX-unavailable snapshot: use screenshot for visual truth, press the visible coordinate to leave the bad screen, then retry AX with snapshot -i.","macOS context menus use click <ref> --button secondary, then snapshot -i. Longpress is for mobile hold gestures, not macOS secondary-click menus.","Direct proxy: Cloud/Linux clients can use iOS simulators through a Mac running agent-device proxy. A proxy URL/token means direct proxy mode: use --daemon-base-url plus --daemon-auth-token, or saved daemonBaseUrl/daemonAuthToken config.","Direct proxy sessions: choose one explicit --session and reuse it for open/snapshot/interactions/close; do not use connect, --remote-config, tenant, run, or lease flags.","Cloud/remote-config profiles are separate from direct proxy: use connect or --remote-config on operational commands. Do not substitute --config; --config only loads CLI defaults.",'Batch JSON steps use "command" and structured "input"; legacy "positionals"/"flags" steps still run in CLI but are deprecated until the next major version.',"Navigation: app-owned back uses back; system back uses back --system.","Web browser sessions: read help web; first slice is web setup if needed -> web doctor -> open <url> --platform web -> snapshot -i -> click/fill/get/is/find/wait/screenshot -> close.","Verification commands must name the expected text/selector; bare screenshots/snapshots are not enough.","Debug evidence: Session state contains request diagnostics and runner.log; use logs clear --restart/mark/path, trace, and network dump --include headers for app evidence.","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."],i=["Default config files: ~/.agent-device/config.json, ./agent-device.json","Use --config <path> or AGENT_DEVICE_CONFIG to load one explicit config file."],c=[{label:"AGENT_DEVICE_SESSION",description:"Explicit 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"}],l=["agent-device open Settings --platform ios","agent-device open https://example.com --platform web","agent-device open TextEdit --platform macos","agent-device snapshot -i","agent-device react-devtools get tree --depth 3","agent-device cdp memory usage sample --gc --label baseline",'agent-device fill @e3 "test@example.com"',"agent-device replay ./session.ad","agent-device test ./suite --platform android"],d={workflow:{summary:"Normal agent-device bootstrap, exploration, and validation loop",body:`agent-device help workflow
2
2
 
3
3
  Version-matched operating guide for normal agent-device work.
4
4
 
@@ -28,7 +28,7 @@ Bootstrap:
28
28
  agent-device prepare ios-runner --platform ios --timeout 240000
29
29
  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.
30
30
  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.
31
- In Apple CI, run prepare ios-runner after boot/install and before replay/test. prepare ios-runner builds/reuses the XCTest runner, health-checks it with a lightweight command, and retries one stuck/non-connecting runner launch before the first snapshot pays that setup cost. If the replay/test step starts a separate daemon, run clean:daemon after prepare so the prepared runner does not keep a live lease owned by the prepare daemon.
31
+ In Apple CI, run prepare ios-runner after boot/install and before replay/test. prepare ios-runner builds/reuses the XCTest runner, health-checks it with a lightweight command, and retries one stuck/non-connecting runner launch before the first snapshot pays that setup cost. It is not a recovery step for "runner already owned by another agent-device daemon"; stop or clean the owning daemon on the Mac with simulator access instead. If the replay/test step starts a separate daemon, run clean:daemon after prepare so the prepared runner does not keep a live lease owned by the prepare daemon.
32
32
  CI may cache ~/.agent-device/ios-runner/derived with an exact key that includes the agent-device package and Xcode version. Avoid broad restore-key fallbacks; prepare ios-runner already recovers bad restored runner artifacts and one retryable non-connecting runner launch. Runner build/start output is written to the session's runner.log; daemon.log is for daemon lifecycle/startup issues.
33
33
  Do not open artifact paths or invent package ids. If apps lookup misses the target and no URL/artifact is provided, ask or stop.
34
34
 
@@ -92,6 +92,9 @@ Read-only and waits:
92
92
 
93
93
  Navigation and gestures:
94
94
  Use scroll for lists; swipe for coordinate gestures/carousels; gesture pan for deliberate drags; gesture fling for fast directional throws.
95
+ For fast macOS desktop list traversal, prefer fixed pixel wheel steps and batch them when no snapshot is needed between passes:
96
+ agent-device scroll down --pixels 200 --duration-ms 50 --platform macos
97
+ agent-device batch --steps '[{"command":"scroll","input":{"direction":"down","pixels":200,"durationMs":50}},{"command":"scroll","input":{"direction":"down","pixels":200,"durationMs":50}}]' --platform macos
95
98
  For raw coordinate gestures, run snapshot -i first and choose a point near the center of the intended app-owned target. Avoid screen edges, tab bars, navigation bars, and home indicators because those areas can trigger system or app navigation instead of the gesture under test.
96
99
  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.
97
100
  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.
@@ -137,7 +140,8 @@ Validation and evidence:
137
140
  Android animations: settings animations off/on, not animations disable/restore.
138
141
  Debug logs: logs clear --restart, logs mark, reproduce, then logs path; do not split clear/restart into separate stop/start commands.
139
142
  Network headers: network dump --include headers; do not write network log headers.
140
- Remote/cloud: connect to discover a cloud profile, or connect --remote-config ./remote-config.json for a local profile; then open, snapshot, disconnect.
143
+ Direct proxy to a Mac you control: cloud/Linux clients can still use iOS simulators through the proxied Mac. Use the printed /agent-device daemon base URL and auth token, or store them as daemonBaseUrl and daemonAuthToken in agent-device.json. Use one explicit --session across open, snapshot, interactions, and close. Do not use connect, --remote-config, tenant, run, or lease flags for direct proxy simulators.
144
+ Cloud/remote-config profiles: use connect to discover a cloud profile, or connect --remote-config ./remote-config.json for a local profile; then open, snapshot, disconnect.
141
145
  Web: agent-device uses a managed, pinned agent-browser backend as an implementation detail. Use --platform web when a browser step belongs inside an agent-device session, replay, batch, MCP, or typed-client flow; use agent-browser directly for standalone web automation. Run agent-device web setup before first use, then agent-device web doctor for backend health checks. Web automation requires Node 24+.
142
146
  agent-device web setup
143
147
  agent-device web doctor
@@ -151,6 +155,8 @@ Validation and evidence:
151
155
  agent-device wait text "Welcome" 3000 --platform web
152
156
  agent-device network dump 25 --include headers --platform web
153
157
  agent-device screenshot ./artifacts/web-home.png --platform web
158
+ agent-device screenshot ./artifacts/web-full.png --platform web --fullscreen
159
+ agent-device viewport 1280 900 --platform web
154
160
  agent-device close --platform web
155
161
  Minimal web support is for browser sessions with open, snapshot, find, get, is, click/press, fill/type, wait, network dump, screenshot, close, and replay over those commands. Use agent-browser directly for browser-specific features that agent-device does not surface, such as tab/devtools management, advanced page scripting, network routing/HAR, or raw browser debugging.
156
162
  macOS menu bar: open ... --platform macos --surface menubar; snapshot -i --platform macos --surface menubar.
@@ -338,7 +344,54 @@ Example:
338
344
  agent-device react-devtools profile report @c5
339
345
  agent-device network dump --include headers
340
346
 
341
- Use snapshot, screenshot, logs, network, and perf metrics 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
347
+ Use snapshot, screenshot, logs, network, and perf metrics for device/app runtime evidence. Use react-devtools only when component internals or React rendering behavior matters.`},cdp:{summary:"React Native CDP targets, JS heap snapshots, and leak triage",body:`agent-device help cdp
348
+
349
+ Use this when a React Native or Expo app exposes a CDP target through Metro and
350
+ the task needs JavaScript heap growth checks, heap snapshot diffs, allocation
351
+ hotspots, retained-object leak evidence, or a small runtime eval to confirm JS
352
+ state. Do not use this as the default React Native profiler.
353
+
354
+ Setup:
355
+ Start Metro and open the app first. For Android devices/emulators, make sure Metro is reachable from the app, typically with adb reverse tcp:8081 tcp:8081.
356
+ agent-device cdp target list --url http://127.0.0.1:8081
357
+ agent-device cdp target select <target-id>
358
+
359
+ Quick JS heap signal:
360
+ agent-device cdp memory usage sample --label baseline --gc
361
+ # perform the suspected leaking action with agent-device commands
362
+ agent-device cdp memory usage sample --label after-action --gc
363
+ agent-device cdp memory usage diff --base jm_1 --compare jm_2
364
+ agent-device cdp memory usage leak-signal --since jm_1
365
+
366
+ Retained-object proof:
367
+ agent-device cdp memory snapshot capture --name baseline --gc
368
+ # perform the suspected leaking action
369
+ agent-device cdp memory snapshot capture --name after-action --gc
370
+ # perform cleanup/navigation that should release the objects
371
+ agent-device cdp memory snapshot capture --name cleanup --gc
372
+ agent-device cdp memory snapshot diff --base ms_1 --compare ms_2 --limit 10
373
+ agent-device cdp memory snapshot leak-triplet --baseline ms_1 --action ms_2 --cleanup ms_3 --limit 10
374
+ agent-device cdp memory snapshot retainers --snapshot ms_3 --id <node-id> --depth 8 --limit 10
375
+
376
+ Allocation pressure:
377
+ Use allocation sampling to find where allocations were created, not to prove a leak:
378
+ agent-device cdp memory allocation start --name suspected-flow --interval 32768 --stack-depth 32
379
+ # perform the flow once
380
+ agent-device cdp memory allocation stop
381
+ agent-device cdp memory allocation hotspots --limit 10
382
+ agent-device cdp memory allocation source-maps
383
+
384
+ Recommended subset:
385
+ cdp dynamically runs a pinned CDP helper through npm; the first run may download the pinned package, and later runs can reuse the npm cache.
386
+ Every argument after cdp is passed to the CDP helper. Put agent-device global flags before cdp when you need the outer CLI to consume them.
387
+ Use cdp memory usage, memory snapshot, memory allocation, and targeted runtime eval.
388
+ Avoid cdp profile cpu, trace, network, and console by default because agent-device already has perf cpu, trace, network, logs, and react-devtools guidance for those areas.
389
+
390
+ Output contract:
391
+ Until cdp has a compact leak report command, synthesize one from memory usage diff, snapshot diff, leak-triplet, and retainers. Report heap deltas, top retained classes/shapes, leak-triplet rows that stayed high after cleanup, and the shortest useful retaining paths. Do not paste raw heap snapshots or large allocation profiles into the response; use exported artifacts only when the user asks for raw data.
392
+
393
+ Target caveats:
394
+ React Native/Hermes implements a subset of browser CDP. If a command reports an unsupported method, keep the target selected and switch to heap usage samples plus heap snapshots. Prefer react-devtools for component tree/render causes; prefer perf memory sample or perf memory snapshot for native/process memory.`},"react-native":{summary:"React Native app automation hazards and routing",body:`agent-device help react-native
342
395
 
343
396
  Use this when the target app is React Native, Expo, or a React Native dev client.
344
397
  This topic covers React Native-specific automation hazards and routes deeper
@@ -348,6 +401,7 @@ Choose the next help topic:
348
401
  Generic navigation, selectors, refs, verification, serial commands: help workflow.
349
402
  Logs, network, diagnostics, traces, permission dialogs, or runtime failures: help debugging.
350
403
  Component tree, props/state/hooks, slow renders, rerenders, or render causes: help react-devtools.
404
+ JS heap growth, heap snapshots, allocation hotspots, or retained-object leaks: help cdp.
351
405
  Remote/cloud config, leases, and local service tunnels: help remote.
352
406
 
353
407
  React Native dev loop:
@@ -395,6 +449,11 @@ React DevTools routing:
395
449
  Use help react-devtools for status/wait, component trees, props/state/hooks, profile windows, slow renders, rerenders, and remote bridge rules.
396
450
  If React DevTools cannot connect, report status and continue with logs, network, perf metrics, screenshot, and trace evidence instead of blocking the whole flow.
397
451
 
452
+ CDP memory routing:
453
+ Keep the agent-device cdp prefix on every CDP command.
454
+ Use help cdp for JS heap usage samples, heap snapshots, snapshot diffs, leak-triplet analysis, allocation hotspots, and retained-object paths.
455
+ Use perf memory sample or perf memory snapshot for native/process memory; use cdp only for JavaScript heap evidence.
456
+
398
457
  Slow-flow investigation:
399
458
  Keep one session, open the app first, and snapshot -i before interacting.
400
459
  Start React Native slow-flow plans with this ordered scaffold:
@@ -433,9 +492,21 @@ iOS physical-device prerequisites:
433
492
 
434
493
  Android physical-device prerequisites:
435
494
  Enable USB debugging and confirm the device appears in agent-device devices --platform android.
436
- Android does not need the iOS runner signing setup. For React Native/Expo Metro reachability, read help react-native.`},remote:{summary:"Remote config, tenant, lease, and remote host flow",body:`agent-device help remote
495
+ Android does not need the iOS runner signing setup. For React Native/Expo Metro reachability, read help react-native.`},remote:{summary:"Direct proxy, cloud profiles, and remote config",body:`agent-device help remote
496
+
497
+ There are two different remote modes:
498
+ 1. Direct proxy: agent-device proxy exposes a Mac you control. A cloud/Linux client can use iOS simulators through that proxied Mac. Use --daemon-base-url plus --daemon-auth-token, or store daemonBaseUrl and daemonAuthToken in agent-device.json. Use one explicit --session across open, snapshot, interactions, and close so implicit cwd-scoped default sessions do not diverge. Do not use connect, --remote-config, tenant, run, or lease flags for this mode.
499
+ 2. Cloud/profile: the cloud connection profile or a local --remote-config owns daemon URL, auth, tenant, run, lease, device scope, and Metro hints. Do not restate those as individual flags unless overriding intentionally.
437
500
 
438
- 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.
501
+ Direct proxy flow for a remote Mac/simulator:
502
+ On the Mac with simulator/device access:
503
+ agent-device proxy --port 4310
504
+ cloudflared tunnel --url http://127.0.0.1:4310
505
+ On the remote client:
506
+ agent-device devices --daemon-base-url https://example.trycloudflare.com/agent-device --daemon-auth-token <token>
507
+ agent-device open Maps --session maps --platform ios --device "iPhone 17 Pro" --daemon-base-url https://example.trycloudflare.com/agent-device --daemon-auth-token <token>
508
+ agent-device snapshot -i --session maps --platform ios --device "iPhone 17 Pro" --daemon-base-url https://example.trycloudflare.com/agent-device --daemon-auth-token <token>
509
+ agent-device close --session maps --daemon-base-url https://example.trycloudflare.com/agent-device --daemon-auth-token <token>
439
510
 
440
511
  Cloud profile flow:
441
512
  agent-device connect
@@ -454,21 +525,14 @@ Script flow, per-command config:
454
525
  agent-device snapshot --remote-config ./remote-config.json
455
526
  agent-device disconnect --remote-config ./remote-config.json
456
527
 
457
- Direct proxy flow for a remote Mac:
458
- On the Mac with simulator/device access:
459
- agent-device proxy --port 4310
460
- cloudflared tunnel --url http://127.0.0.1:4310
461
- On the remote client:
462
- agent-device devices --daemon-base-url https://example.trycloudflare.com/agent-device --daemon-auth-token <token>
463
- agent-device devices
464
-
465
528
  Rules:
466
529
  connect and disconnect are top-level commands. Do not write agent-device remote connect or agent-device remote disconnect.
467
530
  Use connect without --remote-config when the cloud control plane owns the connection profile.
468
531
  Prefer --remote-config over --daemon-base-url, --tenant, --run-id, and --lease-id when using a local profile.
469
- Use agent-device proxy for direct tunnel access to a Mac you control. Copy the printed daemon base URL and daemon auth token; do not use agent-device auth for this direct proxy flow.
470
- For repeated direct proxy commands, store daemonBaseUrl and daemonAuthToken in normal agent-device.json CLI config. Keep platform selection on each command or workflow. Do not use --remote-config unless you are using the tenant/run/lease remote connection flow.
532
+ Use agent-device proxy for direct tunnel access to a Mac you control. Copy the printed daemon base URL and daemon auth token; do not use agent-device auth, connect, disconnect, --remote-config, tenant, run, or lease flags for this direct proxy flow.
533
+ For repeated direct proxy commands, store daemonBaseUrl and daemonAuthToken in normal agent-device.json CLI config. Keep platform selection on each command or workflow, and keep the same explicit --session until close.
471
534
  Keep the proxy token secret. Anyone with the token can control the proxied daemon.
535
+ If iOS snapshot/interaction reports that the runner is already owned by another agent-device daemon, do not run prepare ios-runner from the remote client. Retry the original snapshot or interaction; same-proxy-state stale runner leases are reclaimed by the proxy daemon. If the conflict repeats, the Mac operator should close the owning session or clean the conflicting local daemon.
472
536
  Do not use --config as a remote profile flag. --config loads CLI defaults; --remote-config selects remote daemon/profile settings.
473
537
  For self-contained scripts, pass the same --remote-config to every operational command, including disconnect; a preceding connect is optional but not required.
474
538
  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.
@@ -530,6 +594,8 @@ First-slice loop:
530
594
  agent-device wait text "Welcome" 3000 --platform web
531
595
  agent-device network dump 25 --include headers --platform web
532
596
  agent-device screenshot ./artifacts/web-home.png --platform web
597
+ agent-device screenshot ./artifacts/web-full.png --platform web --fullscreen
598
+ agent-device viewport 1280 900 --platform web
533
599
  agent-device close --platform web
534
600
 
535
601
  Supported in agent-device web sessions:
@@ -598,37 +664,37 @@ Rules:
598
664
  Prefer refs for exploration and selectors for deterministic replay.
599
665
  Use logs, network, screenshot --overlay-refs, trace, perf metrics, perf frames, or react-devtools only when they add evidence to a specific issue.
600
666
  Never delete screenshots, videos, traces, or report artifacts during a session.
601
- Escalate to help debugging or help react-devtools when runtime symptoms require those tools.`}};function p(e){let t=e.endsWith("?"),o=t?e.slice(0,-1):e;return t?`[${o}]`:`<${o}>`}function u(){let o,a,d,p,u,v,b,w;return o=`agent-device <command> [args] [--json]
667
+ Escalate to help debugging or help react-devtools when runtime symptoms require those tools.`}};function p(e){let t=e.endsWith("?"),a=t?e.slice(0,-1):e;return t?`[${a}]`:`<${a}>`}function h(){let a,o,d,p,h,v,b,w;return a=`agent-device <command> [args] [--json]
602
668
 
603
669
  CLI to control iOS and Android devices for AI agents.
604
- `,a=m("Commands:",e().map(e=>{let o=t(e);return{name:e,schema:o,usage:function(e,t){if(t.listUsageOverride)return t.listUsageOverride;let o=(t.positionalArgs??[]).map(o=>{var a,s,n;let r,i,c,l;return a=e,s=t,i=(r=(n=o).endsWith("?"))?n.slice(0,-1):n,l=(c=/^[a-z-]+(?:\|[a-z-]+)+$/i.test(i))||void 0!==s.usageOverride&&s.usageOverride.startsWith(`${a} ${i}`),r?c?`[${i}]`:l?i:`[${i}]`:l?i:`<${i}>`});return[e,...o].join(" ")}(e,o)}}).map(e=>({label:e.usage,description:e.schema.summary??e.schema.helpDescription}))),d=f("Global Flags:",h(s)),p=g("Agent Quickstart:",r),u=m("Agent Workflows:",n),v=g("Configuration:",i),b=m("Environment:",c),w=g("Examples:",l),`${o}
605
- ${a}
670
+ `,o=f("Commands:",e().map(e=>{let a=t(e);return{name:e,schema:a,usage:function(e,t){if(t.listUsageOverride)return t.listUsageOverride;let a=(t.positionalArgs??[]).map(a=>{var o,s,n;let r,i,c,l;return o=e,s=t,i=(r=(n=a).endsWith("?"))?n.slice(0,-1):n,l=(c=/^[a-z-]+(?:\|[a-z-]+)+$/i.test(i))||void 0!==s.usageOverride&&s.usageOverride.startsWith(`${o} ${i}`),r?c?`[${i}]`:l?i:`[${i}]`:l?i:`<${i}>`});return[e,...a].join(" ")}(e,a)}}).map(e=>({label:e.usage,description:e.schema.summary??e.schema.helpDescription}))),d=m("Global Flags:",u(s)),p=g("Agent Quickstart:",r),h=f("Agent Workflows:",n),v=g("Configuration:",i),b=f("Environment:",c),w=g("Examples:",l),`${a}
671
+ ${h}
672
+
673
+ ${o}
606
674
 
607
675
  ${d}
608
676
 
609
677
  ${p}
610
678
 
611
- ${u}
612
-
613
679
  ${v}
614
680
 
615
681
  ${b}
616
682
 
617
683
  ${w}
618
- `}function h(e){return a().filter(t=>e.has(t.key)&&void 0!==t.usageLabel&&void 0!==t.usageDescription)}function f(e,t){return m(e,t.map(e=>({label:e.usageLabel??"",description:e.usageDescription??""})))}function m(e,t){if(0===t.length)return`${e}
619
- (none)`;let o=Math.max(...t.map(e=>e.label.length))+2,a=[e];for(let e of t)a.push(` ${e.label.padEnd(o)}${e.description}`);return a.join("\n")}function g(e,t){return 0===t.length?`${e}
684
+ `}function u(e){return o().filter(t=>e.has(t.key)&&void 0!==t.usageLabel&&void 0!==t.usageDescription)}function m(e,t){return f(e,t.map(e=>({label:e.usageLabel??"",description:e.usageDescription??""})))}function f(e,t){if(0===t.length)return`${e}
685
+ (none)`;let a=Math.max(...t.map(e=>e.label.length))+2,o=[e];for(let e of t)o.push(` ${e.label.padEnd(a)}${e.description}`);return o.join("\n")}function g(e,t){return 0===t.length?`${e}
620
686
  (none)`:[e,...t.map(e=>` ${e}`)].join("\n")}function v(e){var t,s;let n,r=(n=d[e])?`${n.body}
621
687
 
622
688
  Related:
623
689
  agent-device help command list and global flags
624
690
  agent-device help <command> command-specific flags
625
691
  agent-device help workflow normal app automation loop
626
- `:null;if(r)return r;let i=o(e);if(!i)return null;let c=(t=e,(s=i).usageOverride?s.usageOverride:[t,...(s.positionalArgs??[]).map(p),...(s.allowedFlags??[]).flatMap(e=>{var t;return(t=e,a().filter(e=>e.key===t)).map(e=>e.usageLabel??e.names[0])}).map(e=>`[${e}]`)].join(" ")),l=h(new Set(i.allowedFlags??[])),u=[];l.length>0&&u.push(f("Command flags:",l));let m=u.length>0?`
692
+ `:null;if(r)return r;let i=a(e);if(!i)return null;let c=(t=e,(s=i).usageOverride?s.usageOverride:[t,...(s.positionalArgs??[]).map(p),...(s.allowedFlags??[]).flatMap(e=>{var t;return(t=e,o().filter(e=>e.key===t)).map(e=>e.usageLabel??e.names[0])}).map(e=>`[${e}]`)].join(" ")),l=u(new Set(i.allowedFlags??[])),h=[];l.length>0&&h.push(m("Command flags:",l));let f=h.length>0?`
627
693
 
628
- ${u.join("\n\n")}`:"";return`agent-device ${c}
694
+ ${h.join("\n\n")}`:"";return`agent-device ${c}
629
695
 
630
696
  ${i.helpDescription}
631
697
 
632
698
  Usage:
633
- agent-device ${c}${m}
634
- `}export{v as buildCommandUsageText,u as buildUsageText};
699
+ agent-device ${c}${f}
700
+ `}export{v as buildCommandUsageText,h as buildUsageText};
@@ -1,4 +1 @@
1
- import{serializeOpenResult as t,serializeSnapshotResult as e,serializeSessionListEntry as n,readCommandMessage as r,serializeDeployResult as o,serializeCloseResult as a,serializeInstallFromSourceResult as i,serializeDevice as s}from"./9873.js";import{formatSnapshotText as u}from"./6311.js";function d(t){return({result:e})=>t(e)}let p=d(l);function l(t){return{data:t,text:r(t)}}function f(t){return t&&"object"==typeof t&&!Array.isArray(t)?t:void 0}function c(t){return!!t&&"object"==typeof t&&!Array.isArray(t)}let h={batch:d(function(t){let e="number"==typeof t.total?t.total:0,n="number"==typeof t.executed?t.executed:0,o="number"==typeof t.totalDurationMs?t.totalDurationMs:void 0,a=[`Batch completed: ${n}/${e} steps${void 0!==o?` in ${o}ms`:""}`];for(let e of Array.isArray(t.results)?t.results:[]){let t=function(t){let e=f(t);if(!e)return;let n="number"==typeof e.step?e.step:void 0,o="string"==typeof e.command?e.command:"step",a=!1!==e.ok,i=function(t,e,n){var o;return e?r(f(t.data))??n:(o=f(t.error),("string"==typeof o?.message&&o.message.length>0?o.message:null)??n)}(e,a,o),s=void 0!==n?`${n}. `:"- ",u="number"==typeof e.durationMs?e.durationMs:void 0,d=void 0!==u?` (${u}ms)`:"";return`${s}${a?"OK":"FAILED"} ${i}${d}`}(e);t&&a.push(t)}return{data:t,text:a.join("\n")}})},$={debug:d(function(t){let e=[t.outPath,t.message];for(let n of(e.push(...function(t){let e=t.crash,n=[`Crash: ${e.appName??"unknown app"}${void 0===e.crashedThread?"":` thread ${e.crashedThread}`}`];for(let t of(e.bundleId&&n.push(`Bundle: ${e.bundleId}`),e.exceptionType&&n.push(`Exception: ${e.exceptionType}`),e.terminationReason&&n.push(`Termination: ${e.terminationReason}`),e.topFrames))n.push(`Frame ${t.index}: ${t.image} ${t.symbol??t.address}`);for(let t of e.findings)n.push(`Finding: ${t}`);return n}(t)),t.matchedImages))e.push(`Matched: ${n.name} ${n.uuid}${n.arch?` ${n.arch}`:""}`);for(let n of t.warnings??[])e.push(`Warning: ${n}`);return{data:t,text:e.join("\n")}})};function m(t){let e=t.ref??"",n=t.x,r=t.y;return e&&"number"==typeof n&&"number"==typeof r?{data:t,text:`Tapped @${e} (${n}, ${r})`}:l(t)}let y={click:d(m),press:d(m),get:({input:t,result:e})=>{var n;let r;return r=(n={result:e,format:t.format}).result,"text"===n.format?{data:r,text:"string"==typeof r.text?r.text:""}:"attrs"===n.format?{data:r,text:JSON.stringify(r.node??{},null,2)}:l(r)},is:d(function(t){return{data:t,text:`Passed: is ${t.predicate??"assertion"}`}}),find:d(function(t){return"string"==typeof t.text?{data:t,text:t.text}:"boolean"==typeof t.found?{data:t,text:`Found: ${t.found}`}:t.node?{data:t,text:JSON.stringify(t.node,null,2)}:l(t)})};function g(t){return l(o(t))}let v={boot:d(function(t){let e=t.platform??"unknown",n=t.device??t.id??"unknown";return{data:t,text:`Boot ready: ${n} (${e})`}}),shutdown:d(function(t){let e=t.platform??"unknown",n=t.device??t.id??"unknown",r=t.shutdown,o=!!r&&"object"==typeof r&&"success"in r&&!0===r.success;return{data:t,text:`${o?"Shutdown":"Shutdown failed"}: ${n} (${e})`}}),devices:d(function(t){return{data:{devices:t.map(s)},text:t.map(b).join("\n")}}),apps:({input:t,result:e})=>{var n;return{data:{apps:(n={result:e,appsFilter:t.appsFilter}).result},stderr:"all"===n.appsFilter?"Showing all apps, including system apps.\n":"Showing user-installed apps. Use --all to include system apps.\n",text:n.result.length>0?n.result.join("\n"):"all"===n.appsFilter?"No apps found.":"No user-installed apps found."}},session:d(function(t){if("stateDir"in t)return{data:t,text:t.stateDir};let e={sessions:t.sessions.map(n)};return{data:e,text:JSON.stringify(e,null,2)}}),open:d(function(e){let n=t(e),o=[r(n)].filter(t=>!!t);return"string"==typeof n.sessionStateDir&&o.push(`Session state: ${n.sessionStateDir}`),{data:n,text:o.join("\n")||null}}),close:d(function(t){return l(a(t))}),install:d(g),reinstall:d(g),"install-from-source":d(function(t){return l(i(t))}),prepare:p};function b(t){let e=t.kind?` ${t.kind}`:"",n=t.target?` target=${t.target}`:"",r="boolean"==typeof t.booted?` booted=${t.booted}`:"";return`${t.name} (${t.platform}${e}${n})${r}`}let x={logs:d(function(t){var e;let n="string"==typeof t.path?t.path:"";return{data:t,text:n,stderr:P([k(t,["active","state","backend","sizeBytes"]),(e=t,["started","stopped","marked","cleared","restarted","removedRotatedFiles"].map(t=>{var n,r;return n=t,!0===(r=e[t])?`${n}=true`:"number"==typeof r?`${n}=${r}`:""}).filter(Boolean).join(" ")||void 0),"string"==typeof t.hint?t.hint:void 0,F(t.notes)])}}),network:d(function(t){let e=[],n="string"==typeof t.path?t.path:"";n&&e.push(n);let r=Array.isArray(t.entries)?t.entries:[];if(0===r.length)e.push("No recent HTTP(s) entries found.");else for(let t of r)e.push(...function(t){let e=f(t)??{},n="string"==typeof e.method?e.method:"HTTP",r="string"==typeof e.url?e.url:"<unknown-url>",o="number"==typeof e.status?` status=${e.status}`:"",a="string"==typeof e.timestamp?`${e.timestamp} `:"",i="number"==typeof e.durationMs?` durationMs=${e.durationMs}`:"",s=[`${a}${n} ${r}${o}${i}`],u="string"==typeof e.headers;return M(s,"headers",e.headers),u||(w(s,"request headers",e.requestHeaders),w(s,"response headers",e.responseHeaders)),M(s,"request",e.requestBody),M(s,"response",e.responseBody),s}(t));return{data:t,text:e.join("\n"),stderr:P([k(t,["active","state","backend","include","scannedLines","matchedLines"]),F(t.notes)])}})};function w(t,e,n){let r=f(n);r&&0!==Object.keys(r).length&&t.push(` ${e}: ${JSON.stringify(r)}`)}function M(t,e,n){"string"==typeof n&&t.push(` ${e}: ${n}`)}function k(t,e){return e.map(e=>void 0!==t[e]&&null!==t[e]?`${e}=${t[e]}`:"").filter(Boolean).join(" ")||void 0}function F(t){if(!Array.isArray(t))return;let e=t.filter(t=>"string"==typeof t&&t.length>0);return e.length>0?e.join("\n"):void 0}function P(t){return t.filter(t=>!!t).join("\n")||void 0}let j={perf:d(function(t){return{data:t,text:function(t){var e,n,r,o;let a,i="xctrace"===(e=t).kind?function(t){var e,n,r,o,a;let i="string"==typeof t.perf?t.perf:void 0,s="string"==typeof(e=t).outPath?e.outPath:"string"==typeof e.reportPath?e.reportPath:void 0;if(i&&s&&"xctrace"===t.kind){let e;return n=s,r="string"==typeof t.mode?t.mode:"capture",o=i,a=t.template,e=[n,`Perf ${r}: ${o}`],"string"==typeof a&&e.push(`Template: ${a}`),e.join("\n")}}(e):function(t){var e;let n,r,o,a=(n=S((e=t).action),r=S(e.kind),o=S(e.type),n&&r&&o?{action:n,kind:r,type:o}:void 0);if(a){let e;return`Perf ${a.action}: ${a.kind} ${a.type}${(e=S(t.state))?` state=${e}`:""}${function(t){let e=S(t.outPath);if(!e)return"";let n=A(t.sizeBytes);return`
2
- ${e}${void 0!==n?` (${I(n)})`:""}`}(t)}${function(t){let e=f(t.summary),n=f(e?.frameHealth);if(!n||!0!==n.available)return"";let r=A(n.droppedFramePercent),o=A(n.droppedFrameCount),a=A(n.totalFrameCount);return void 0===r||void 0===o||void 0===a?"":`
3
- Trace frame health: dropped ${O(r)} (${Math.round(o)}/${Math.round(a)} frames)`}(t)}`}}(e);if(i)return i;let s=f(t.artifact);if(s)return function(t){let e="string"==typeof t.kind?t.kind:"memory";if(!1===t.available){let n="string"==typeof t.reason&&t.reason.length>0?t.reason:"not available";return`Memory artifact (${e}): unavailable - ${n}`}let n="string"==typeof t.path?t.path:void 0,r=A(t.sizeBytes),o=void 0===r?"":` (${I(r)})`;return n?`Memory artifact (${e}): ${n}${o}`:`Memory artifact (${e}): captured${o}`}(s);let u=f(t.metrics),d=f(u?.fps),p=(n=u,(a=[function(t){if(t?.available!==!0)return;let e=A(t.usagePercent);return void 0!==e?`CPU ${O(e)}`:void 0}(f(n?.cpu)),function(t){let e;if(t?.available!==!0)return;let n=A(t.residentMemoryKb)??A(t.totalPssKb)??A(t.totalRssKb);return void 0!==n?`memory ${e=n/1024,`${e>=10?Math.round(e):e.toFixed(1)}MB`}`:void 0}(f(n?.memory))].filter(t=>!!t)).length>0?a.join(", "):void 0);if(!d)return B(p,"missing frame metric");if(!1===d.available){return B(p,"string"==typeof(r=d).reason&&r.reason.length>0?r.reason:"not available")}let l=function(t){let e=A(t.droppedFramePercent),n=A(t.droppedFrameCount);if(void 0!==e&&void 0!==n){var r,o,a;return[`dropped ${O(e)}`,(r=n,void 0!==(o=A(t.totalFrameCount))?`(${Math.round(r)}/${Math.round(o)} frames)`:`(${Math.round(r)} dropped frames)`),void 0!==(a=A(t.sampleWindowMs))?`window ${N(a)}`:""].filter(Boolean).join(" ")}}(d);if(!l)return B(p,"missing dropped-frame summary");let h=[`Frame health: ${l}`],$=(Array.isArray(o=d.worstWindows)?o.filter(c):[]).flatMap(t=>{let e=function(t){let e=A(t.startOffsetMs),n=A(t.endOffsetMs),r=A(t.missedDeadlineFrameCount);if(void 0===e||void 0===n||void 0===r)return;let o=A(t.worstFrameMs),a=void 0===o?"":`, worst ${N(o)}`;return`- +${N(e)}-+${N(n)}: ${Math.round(r)} missed-deadline frames${a}`}(t);return e?[e]:[]});return $.length>0&&h.push("Worst windows:",...$),h.join("\n")}(t)}})};function S(t){return"string"==typeof t?t:void 0}function B(t,e){return t?`Performance: ${t}`:`Frame health: unavailable - ${e}`}function A(t){return"number"==typeof t&&Number.isFinite(t)?t:void 0}function O(t){return`${Number.isInteger(t)?t:t.toFixed(1)}%`}function N(t){let e=Math.max(0,Math.round(t));if(e<1e3)return`${e}ms`;let n=Math.round(e/1e3);if(n<60)return`${n}s`;let r=Math.floor(n/60),o=n%60;return o>0?`${r}m ${o}s`:`${r}m`}function I(t){if(t<1024)return`${Math.round(t)}B`;let e=t/1024;if(e<1024)return`${e>=10?Math.round(e):e.toFixed(1)}KB`;let n=e/1024;return`${n>=10?Math.round(n):n.toFixed(1)}MB`}let T={record:d(function(t){let e,n="string"==typeof t.outPath?t.outPath:"",r=Array.isArray(e=t.chunks)?e.flatMap(t=>t&&"object"==typeof t?"number"!=typeof t.index||"string"!=typeof t.path?[]:[{index:t.index,path:t.path}]:[]):[];if(r.length<=1){var o,a;let e;return{data:t,text:(o=t,e=[],(a=n)&&e.push(a),"string"==typeof o.sessionStateDir&&e.push(`Session state: ${o.sessionStateDir}`),"string"==typeof o.warning&&e.push(`Warning: ${o.warning}`),"string"==typeof o.overlayWarning&&e.push(`Overlay warning: ${o.overlayWarning}`),e.join("\n"))}}let i=["Recording chunks:"];for(let t of r)i.push(` ${t.index}: ${t.path}`);return"string"==typeof t.telemetryPath&&i.push(`Telemetry: ${t.telemetryPath}`),"string"==typeof t.warning&&i.push(`Warning: ${t.warning}`),"string"==typeof t.overlayWarning&&i.push(`Overlay warning: ${t.overlayWarning}`),{data:t,text:i.join("\n")}})},D={appstate:d(function(t){return{data:t,text:function(t){if("ios"===t.platform){let e=[`Foreground app: ${t.appName??t.appBundleId??"unknown"}`];return t.appBundleId&&e.push(`Bundle: ${t.appBundleId}`),t.source&&e.push(`Source: ${t.source}`),e.join("\n")}if("android"===t.platform){let e=[`Foreground app: ${t.package??"unknown"}`];return t.activity&&e.push(`Activity: ${t.activity}`),e.join("\n")}return null}(t)}}),back:p,home:p,rotate:p,"app-switcher":p,keyboard:d(function(t){if("android"===t.platform&&"status"===t.action){var e;let n=[`Keyboard visible: ${!0===t.visible?"yes":"no"}`,`Input type: ${t.type??t.inputType??"unknown"}`,`Input owner: ${t.inputOwner??"unknown"}`];return t.inputMethodPackage&&n.push(`Input method: ${t.inputMethodPackage}`),t.focusedPackage&&n.push(`Focused package: ${t.focusedPackage}`),t.focusedResourceId&&n.push(`Focused resource: ${t.focusedResourceId}`),n.push(`Next action: ${(e=t.visible,"ime"===t.inputOwner?"Focused input appears to be owned by the keyboard/IME; dismiss or change the IME before retrying text entry.":!0===e?"Keyboard is visible and focused input appears app-owned; fill/type can proceed.":"Keyboard is hidden; focus an app field before type, or use fill with a concrete target.")}`),{data:t,text:n.join("\n")}}return l(t)}),clipboard:d(function(t){return"read"===t.action?{data:t,text:t.text}:l(t)})},C={...v,snapshot:({input:t,result:n})=>{var r;let o;return{data:o=e((r={result:n,raw:t.raw,interactiveOnly:t.interactiveOnly,scope:t.scope,depth:t.depth}).result),jsonData:function(t){let{unchanged:e,...n}=t;return n}(o),stderr:r.result.snapshotDiagnostics?.warning?`${r.result.snapshotDiagnostics.warning}
4
- `:void 0,text:u(o,{raw:r.raw,flatten:r.interactiveOnly,scoped:"string"==typeof r.scope&&r.scope.trim().length>0,depthLimited:"number"==typeof r.depth})}},wait:p,alert:p,...D,...y,...x,...j,...$,...h,...T,metro:({input:t,result:e})=>{var n;return{data:(n={result:e,action:t.action}).result,text:"reload"===n.action?`Reloaded React Native apps via ${n.result.reloadUrl}`:JSON.stringify(n.result,null,2)}}};function K(t){return C[t.name]?.({input:t.input??{},result:t.result})}export{K as formatCliOutput};
1
+ import{listCommandFamilyCliOutputFormatters as t}from"./2948.js";let r=t();function u(t){return r[t.name]?.({input:t.input??{},result:t.result})}export{u as formatCliOutput};