agent-device 0.17.0 → 0.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +1 -1
  2. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.17.0.apk → agent-device-android-multitouch-helper-0.17.2.apk} +0 -0
  3. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.17.2.apk.sha256 +1 -0
  4. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.17.0.manifest.json → agent-device-android-multitouch-helper-0.17.2.manifest.json} +4 -4
  5. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.17.0.apk → agent-device-android-snapshot-helper-0.17.2.apk} +0 -0
  6. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.17.2.apk.sha256 +1 -0
  7. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.17.0.manifest.json → agent-device-android-snapshot-helper-0.17.2.manifest.json} +6 -6
  8. package/dist/src/123.js +1 -0
  9. package/dist/src/1352.js +1 -1
  10. package/dist/src/1534.js +1 -0
  11. package/dist/src/1620.js +1 -0
  12. package/dist/src/1644.js +2 -0
  13. package/dist/src/2284.js +1 -0
  14. package/dist/src/2403.js +3 -0
  15. package/dist/src/2415.js +28 -29
  16. package/dist/src/2474.js +1 -0
  17. package/dist/src/2672.js +1 -0
  18. package/dist/src/3393.js +1 -0
  19. package/dist/src/4778.js +1 -1
  20. package/dist/src/5898.js +1 -0
  21. package/dist/src/7556.js +1 -1
  22. package/dist/src/7847.js +1 -1
  23. package/dist/src/8173.js +27 -0
  24. package/dist/src/8407.js +1 -0
  25. package/dist/src/8699.js +1 -1
  26. package/dist/src/8806.js +3 -3
  27. package/dist/src/9010.js +1 -0
  28. package/dist/src/9238.js +3 -3
  29. package/dist/src/9471.js +1 -1
  30. package/dist/src/9542.js +3 -3
  31. package/dist/src/9616.js +1 -0
  32. package/dist/src/9673.js +1 -0
  33. package/dist/src/9974.js +1 -0
  34. package/dist/src/android-adb.d.ts +12 -6
  35. package/dist/src/android-adb.js +1 -1
  36. package/dist/src/android-snapshot-helper.d.ts +15 -7
  37. package/dist/src/android.js +1 -1
  38. package/dist/src/apple.js +1 -1
  39. package/dist/src/apps.js +2 -2
  40. package/dist/src/args.js +2 -2
  41. package/dist/src/batch.d.ts +16 -4
  42. package/dist/src/cli.js +18 -42
  43. package/dist/src/command-surface.js +1 -1
  44. package/dist/src/contracts.d.ts +28 -4
  45. package/dist/src/contracts.js +1 -1
  46. package/dist/src/find.js +1 -1
  47. package/dist/src/finders.d.ts +3 -1
  48. package/dist/src/generic.js +13 -11
  49. package/dist/src/index.d.ts +142 -73
  50. package/dist/src/index.js +1 -1
  51. package/dist/src/input-actions.js +1 -1
  52. package/dist/src/interaction.js +1 -1
  53. package/dist/src/internal/png-worker.d.ts +26 -0
  54. package/dist/src/internal/png-worker.js +1 -0
  55. package/dist/src/metro.d.ts +3 -1
  56. package/dist/src/react-native.js +1 -1
  57. package/dist/src/record-trace-recording.js +26 -0
  58. package/dist/src/record-trace.js +1 -26
  59. package/dist/src/remote-config.d.ts +33 -7
  60. package/dist/src/selector-runtime.js +1 -1
  61. package/dist/src/selectors.d.ts +3 -3
  62. package/dist/src/selectors.js +1 -1
  63. package/dist/src/server.js +2 -2
  64. package/dist/src/session.js +10 -11
  65. package/dist/src/snapshot.js +1 -1
  66. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +6 -2
  67. package/package.json +7 -4
  68. package/server.json +2 -2
  69. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.17.0.apk.sha256 +0 -1
  70. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.17.0.apk.sha256 +0 -1
  71. package/dist/src/1998.js +0 -1
  72. package/dist/src/2805.js +0 -1
  73. package/dist/src/4057.js +0 -1
  74. package/dist/src/5792.js +0 -1
  75. package/dist/src/6085.js +0 -1
  76. package/dist/src/6232.js +0 -1
  77. package/dist/src/8020.js +0 -1
  78. package/dist/src/8502.js +0 -1
  79. package/dist/src/940.js +0 -1
  80. package/dist/src/9404.js +0 -1
  81. package/dist/src/9533.js +0 -1
  82. package/dist/src/command-metadata.js +0 -1
  83. /package/dist/src/{1393.js → 3675.js} +0 -0
  84. /package/dist/src/{5310.js → 695.js} +0 -0
@@ -1 +1 @@
1
- import{__webpack_require__ as e}from"./rslib-runtime.js";import{createDaemonRuntimeSessionStore as t,setSessionSnapshot as r,dispatchCommand as a,getClickButtonValidationError as n,ensureAndroidBlockingSystemDialogReady as o,isCommandSupportedOnDevice as s,buttonTag as i,resolveClickButton as l,errorResponse as c,recoverAndroidBlockingSystemDialog as u,getActiveAndroidSnapshotFreshness as f}from"./2415.js";import{createAgentDevice as d}from"./9533.js";import{asAppError as p,normalizeError as g,AppError as m}from"./9152.js";import{dispatchGetViaRuntime as S,isAndroidEscapeError as k,createDaemonRuntimePolicy as _,assertAndroidPressStayedInApp as y,dispatchIsViaRuntime as h,isDirectIosSelectorFallbackError as w,refSnapshotFlagGuardResponse as b,readSimpleIosSelectorTarget as N}from"./selector-runtime.js";import{readInteractionTargetFromPositionals as v,readFillTargetFromPositionals as x}from"./8502.js";import{interaction_snapshot_captureSnapshotForSession as F,readSnapshotNodesReferenceFrame as P,resolveDirectTouchReferenceFrameSafely as R,buildTouchVisualizationResult as M,finalizeTouchInteraction as O}from"./9471.js";import{emitDiagnostic as C}from"./7599.js";import{PUBLIC_COMMANDS as I}from"./5792.js";import{successText as A}from"./1998.js";var E={};function q(e,t){return"macos"!==e.device.platform||"desktop"!==e.surface&&"menubar"!==e.surface||"menubar"===e.surface&&("click"===t||"press"===t)?null:c("UNSUPPORTED_OPERATION",`${t} is not supported on macOS ${e.surface} sessions yet. Open an app session to act, or use the ${e.surface} surface to inspect.`)}function U(e){let n=e.sessionStore.get(e.sessionName);if(!n)throw new m("SESSION_NOT_FOUND","No active session. Run open first.");return d({backend:function(e){let{req:t,session:r}=e;return{platform:r.device.platform,captureSnapshot:async(a,n)=>({snapshot:await e.captureSnapshotForSession(r,t.flags,e.sessionStore,e.contextFromFlags,{interactiveOnly:n?.interactiveOnly===!0})}),tap:async(n,o)=>D(await a(r.device,"press",[String(o.x),String(o.y)],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath))),fill:async(n,o,s)=>D(await a(r.device,"fill",[String(o.x),String(o.y),s],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath))),longPress:async(n,o,s)=>D(await a(r.device,"longpress",[String(o.x),String(o.y),...s?.durationMs===void 0?[]:[String(s.durationMs)]],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath))),typeText:async(n,o)=>D(await a(r.device,"type",[o],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath)))}}({...e,session:n}),..._("interaction commands",{plural:!0}),sessions:t({sessionName:e.sessionName,getSession:()=>n,recordOptions:{includeSnapshot:!0},setRecord:t=>{t.snapshot&&(r(n,t.snapshot),e.sessionStore.set(e.sessionName,n))}})})}function D(e){return e&&"object"==typeof e?e:void 0}function T(e){if(e.length<2)return null;let t=Number(e[0]),r=Number(e[1]);return Number.isFinite(t)&&Number.isFinite(r)?{x:t,y:r}:null}function H(e,t){let r=T(e);if(r)return{ok:!0,target:{kind:"point",x:r.x,y:r.y}};let a=e[0]??"";if(a.startsWith("@"))return{ok:!0,target:{kind:"ref",ref:a,fallbackLabel:v(e).label??""}};let n=e.join(" ").trim();return n?{ok:!0,target:{kind:"selector",selector:n}}:{ok:!1,response:c("INVALID_ARGS",`${t} requires @ref, selector expression, or x y coordinates`)}}function K(e){return"ref"===e.kind?{ref:L(e.target?.kind==="ref"?e.target.ref:void 0),refLabel:e.refLabel,selectorChain:e.selectorChain}:"selector"===e.kind?{selector:e.target?.kind==="selector"?e.target.selector:void 0,selectorChain:e.selectorChain,refLabel:e.refLabel}:{}}function L(e){return e?.startsWith("@")?e.slice(1):e}async function j(e){switch(e.req.command){case"press":return await B(e,"press");case"click":return await B(e,"click");case"longpress":return await B(e,"longpress");case"fill":return await X(e);default:return null}}async function B(e,t){let r,{req:a,sessionName:o,sessionStore:u}=e,f=u.get(o);if(!f)return c("SESSION_NOT_FOUND","No active session. Run open first.");let d="click"===t?"click":t,p="longpress"===t?"longpress":"press",g=q(f,d);if(g)return g;if(!s(p,f.device))return c("UNSUPPORTED_OPERATION",`${p} is not supported on this device`);let m=l(a.flags),S=i(m);if("longpress"!==t&&"primary"!==m){let e=n({commandLabel:d,platform:f.device.platform,button:m,count:a.flags?.count,intervalMs:a.flags?.intervalMs,holdMs:a.flags?.holdMs,jitterPx:a.flags?.jitterPx,doubleTap:a.flags?.doubleTap});if(e)return c(e.code,e.message,e.details)}let k="longpress"===t?function(e){var t,r,a;let n,o=T(e);if(o){return{ok:!0,target:{kind:"point",x:o.x,y:o.y},...void 0===(t=e[2])?{}:{durationMs:Number(t)}}}let s=(n=(r=e).at(-1),r.length>1&&void 0!==(a=n)&&""!==a.trim()&&Number.isFinite(Number(a))?{targetPositionals:r.slice(0,-1),duration:{durationMs:Number(n)}}:{targetPositionals:r,duration:{}}),i=H(s.targetPositionals,"longpress");return i.ok?{ok:!0,target:i.target,...s.duration}:i}(a.positionals??[]):H(a.positionals??[],d);if(!k.ok)return k.response;if("ref"===k.target.kind){let n=e.refSnapshotFlagGuardResponse("longpress"===t?"longpress":"press",a.flags);if(n)return n;r=await J(e,f)}let _=function(e){var t;let{session:r,commandLabel:a,target:n,flags:o}=e;if("click"!==a||"selector"!==n.kind||(t=o,t?.count!==void 0||t?.intervalMs!==void 0||t?.holdMs!==void 0||t?.jitterPx!==void 0||t?.doubleTap!==void 0||t?.clickButton!==void 0&&"primary"!==t.clickButton))return null;let s=N({session:r,selectorExpression:n.selector});return s?{...s,...o?.maestro?.allowNonHittableCoordinateFallback?{allowNonHittableCoordinateFallback:!0}:{}}:null}({session:f,commandLabel:d,target:k.target,flags:a.flags});if(_){let t=await $(e,f,_);if(t)return t}let h="longpress"===t?k.durationMs:void 0;return await z(e,{androidFreshnessBaseline:r,run:async e=>await G({runtime:e,command:t,target:k.target,sessionName:o,requestId:a.meta?.requestId,clickButton:m,flags:a.flags,durationMs:h}),afterRun:async e=>{var t;await y(f,(t=k.target,"point"===t.kind?"coordinate tap":"ref"===e.kind&&e.target?.kind==="ref"?e.target.ref:"selector"===e.kind&&e.target?.kind==="selector"?e.target.selector:"target"))},buildPayloads:async r=>{var a;let n="durationMs"in(a=r)?a.durationMs:void 0,o=await W({params:e,session:f,result:r,extra:"longpress"===t?{...void 0!==n?{durationMs:n}:{},gesture:"longpress"}:S});return{result:o,responseData:o}}})}async function G(e){let{runtime:t,command:r,target:a,sessionName:n,requestId:o,flags:s}=e;if("longpress"===r)return await t.interactions.longPress(a,{session:n,requestId:o,durationMs:e.durationMs});let i={session:n,requestId:o,button:e.clickButton,count:s?.count,intervalMs:s?.intervalMs,holdMs:s?.holdMs,jitterPx:s?.jitterPx,doubleTap:s?.doubleTap};return"click"===r?await t.interactions.click(a,i):await t.interactions.press(a,i)}async function W(e){let{params:t,session:r,result:a,extra:n}=e,o="point"===a.kind?await R({session:r,flags:t.req.flags,sessionStore:t.sessionStore,contextFromFlags:t.contextFromFlags,captureSnapshotForSession:t.captureSnapshotForSession}):P(r.snapshot?.nodes??[]);return M({data:a.backendResult,fallbackX:a.point.x,fallbackY:a.point.y,referenceFrame:o,extra:{...K(a),...n}})}async function $(e,t,r){return await V({params:e,session:t,selector:r,command:"press",positionals:[],extra:{selector:r.raw},fallbackPhase:"ios_direct_selector_tap_fallback"})}async function V(e){let{params:t,session:r,selector:n,command:o,positionals:s,extra:i,fallbackPhase:l}=e,c=Date.now();try{var u,f;let e,l,d=await a(r.device,o,s,t.req.flags?.out,{...t.contextFromFlags(t.req.flags,r.appBundleId,r.trace?.outPath),directElementSelector:n,surface:r.surface})??{},p=Date.now(),g=(u=d,e="number"==typeof u.x?u.x:void 0,l="number"==typeof u.y?u.y:void 0,void 0!==e&&void 0!==l?{x:e,y:l}:{x:0,y:0}),m=M({data:d,fallbackX:g.x,fallbackY:g.y,referenceFrame:(f=d,"number"==typeof f.referenceWidth&&"number"==typeof f.referenceHeight?{referenceWidth:f.referenceWidth,referenceHeight:f.referenceHeight}:void 0),extra:{...i,...function(e,t){if(!e.allowNonHittableCoordinateFallback)return{};let r="tapped via non-hittable coordinate fallback"===t.message;return{maestroNonHittableCoordinateFallbackAllowed:!0,maestroNonHittableCoordinateFallbackUsed:r,...r?{maestroFallbackReason:"non-hittable-coordinate"}:{}}}(n,d)}});return O({session:r,sessionStore:t.sessionStore,command:t.req.command,positionals:t.req.positionals??[],retryPositionals:Q(g),flags:t.req.flags,result:m,responseData:m,actionStartedAt:c,actionFinishedAt:p})}catch(e){if(!w(e))return{ok:!1,error:g(e)};return C({level:"debug",phase:l,data:{selector:n.raw,error:e instanceof Error?e.message:String(e)}}),null}}async function X(e){let{req:t,sessionName:r,sessionStore:a}=e,n=a.get(r);if(n){let e=q(n,"fill");if(e)return e}if(n&&!s("fill",n.device))return c("UNSUPPORTED_OPERATION","fill is not supported on this device");if(!n)return c("SESSION_NOT_FOUND","No active session. Run open first.");let o=function(e){let t=e[0]??"";if(t.startsWith("@")){var r;let a=x(e).text;return a?{ok:!0,target:{kind:"ref",ref:t,fallbackLabel:(r=e).length>=3&&r[1]?.trim()||""},text:a}:{ok:!1,response:c("INVALID_ARGS","fill requires text after ref")}}let a=T(e);if(a){let t=e.slice(2).join(" ");return t?{ok:!0,target:{kind:"point",x:a.x,y:a.y},text:t}:{ok:!1,response:c("INVALID_ARGS","fill requires text after coordinates")}}let n=x(e);return"selector"!==n.kind?{ok:!1,response:c("INVALID_ARGS","fill requires x y text, @ref text, or selector text")}:n.text.trim()?{ok:!0,target:{kind:"selector",selector:n.target.selector},text:n.text}:{ok:!1,response:c("INVALID_ARGS","fill requires text after selector")}}(t.positionals??[]);if(!o.ok)return o.response;if("ref"===o.target.kind){let r=e.refSnapshotFlagGuardResponse("fill",t.flags);if(r)return r;await J(e,n)}let i=function(e){let{session:t,target:r,flags:a}=e;if("selector"!==r.kind)return null;let n=N({session:t,selectorExpression:r.selector});return n?{...n,...a?.maestro?.allowNonHittableCoordinateFallback?{allowNonHittableCoordinateFallback:!0}:{}}:null}({session:n,target:o.target,flags:t.flags});if(i){let t=await Y(e,n,i,o.text);if(t)return t}return await z(e,{run:async e=>await e.interactions.fill(o.target,o.text,{session:r,requestId:t.meta?.requestId,delayMs:t.flags?.delayMs}),buildPayloads:e=>{let t="point"===e.kind?void 0:P(n.snapshot?.nodes??[]),r=M({data:e.backendResult,fallbackX:e.point.x,fallbackY:e.point.y,referenceFrame:t,extra:{...K(e),text:o.text}});e.warning&&(r.warning=e.warning);let a="ref"===e.kind?{...e.backendResult??{ref:L(e.target?.kind==="ref"?e.target.ref:void 0),x:e.point.x,y:e.point.y}}:r;return e.warning&&(a.warning=e.warning),{result:r,responseData:a}}})}async function Y(e,t,r,a){return await V({params:e,session:t,selector:r,command:"fill",positionals:[a],extra:{selector:r.raw,text:a},fallbackPhase:"ios_direct_selector_fill_fallback"})}async function z(e,t){let r=e.sessionStore.get(e.sessionName);if(!r)return c("SESSION_NOT_FOUND","No active session. Run open first.");let a=U(e),n=Date.now();try{let s=await o({session:r,command:e.req.command,phase:"before-command"}),i=await t.run(a);await t.afterRun?.(i),await o({session:r,command:e.req.command,phase:"after-command"});let l=Date.now(),{result:c,responseData:u}=await t.buildPayloads(i);return"recovered"===s.status&&(c.warning=s.warning,u.warning=s.warning),O({session:r,sessionStore:e.sessionStore,command:e.req.command,positionals:e.req.positionals??[],retryPositionals:function(e,t){if("click"===e||"press"===e)return Q(t.point)}(e.req.command,i),flags:e.req.flags,result:c,responseData:u,actionStartedAt:n,actionFinishedAt:l,androidFreshnessBaseline:t.androidFreshnessBaseline})}catch(t){let e=p(t);if(k(e))throw e;return{ok:!1,error:g(t)}}}async function J(e,t){if(!f(t))return;let r=t.snapshot?.comparisonSafe===!0?t.snapshot:void 0;try{await e.captureSnapshotForSession(t,e.req.flags,e.sessionStore,e.contextFromFlags,{interactiveOnly:!0,androidFreshnessMode:"ref-refresh"})}catch(t){C({level:"warn",phase:"android_ref_snapshot_refresh_failed",data:{command:e.req.command,error:t instanceof Error?t.message:String(t)}})}return r}function Q(e){return[String(e.x),String(e.y)]}async function Z(e){let t=await j({...e,captureSnapshotForSession:F,refSnapshotFlagGuardResponse:b});if(t)return t;switch(e.req.command){case I.type:return await ee({...e,captureSnapshotForSession:F});case"get":return await S(e);case"is":return await h(e);default:return null}}async function ee(e){let{sessionName:t,sessionStore:r}=e,a=r.get(t);if(!a)return c("SESSION_NOT_FOUND","No active session. Run open first.");if(!s(I.type,a.device))return c("UNSUPPORTED_OPERATION","type is not supported on this device");let n=await et(a);return n||await er(e,a)}async function et(e){return"android"===e.device.platform&&e.recording&&"failed"===await u({session:e})?c("COMMAND_FAILED","Android system dialog blocked the recording session"):null}async function er(e,t){let{req:r,sessionName:a,sessionStore:n}=e,s=(r.positionals??[]).join(" "),i=U(e),l=Date.now();try{let e=await o({session:t,command:r.command,phase:"before-command"}),c=await i.interactions.typeText(s,{session:a,requestId:r.meta?.requestId,delayMs:r.flags?.delayMs});await o({session:t,command:r.command,phase:"after-command"});let u=Date.now(),f={...c.backendResult??{},text:c.text,delayMs:c.delayMs,...A(c.message??`Typed ${Array.from(c.text).length} chars`)};return"recovered"===e.status&&(f.warning=e.warning),O({session:t,sessionStore:n,command:r.command,positionals:r.positionals??[],flags:r.flags,result:f,responseData:f,actionStartedAt:l,actionFinishedAt:u})}catch(e){return{ok:!1,error:g(e)}}}e.r(E),e.d(E,{handleInteractionCommands:()=>Z});export{E as interaction_namespaceObject};
1
+ import{__webpack_require__ as e}from"./rslib-runtime.js";import{createDaemonRuntimeSessionStore as t,setSessionSnapshot as r,dispatchCommand as a,ensureAndroidBlockingSystemDialogReady as n,isCommandSupportedOnDevice as o,errorResponse as s,recoverAndroidBlockingSystemDialog as i,getActiveAndroidSnapshotFreshness as l}from"./2415.js";import{createAgentDevice as c}from"./123.js";import{asAppError as u,normalizeError as f,AppError as d}from"./9152.js";import{dispatchGetViaRuntime as p,isAndroidEscapeError as g,createDaemonRuntimePolicy as m,assertAndroidPressStayedInApp as S,dispatchIsViaRuntime as _,isDirectIosSelectorFallbackError as k,refSnapshotFlagGuardResponse as y,readSimpleIosSelectorTarget as h}from"./selector-runtime.js";import{readFillTargetFromPositionals as w}from"./3393.js";import{buttonTag as b,resolveClickButton as N,getClickButtonValidationError as v}from"./1620.js";import{interaction_snapshot_captureSnapshotForSession as x,readSnapshotNodesReferenceFrame as F,resolveDirectTouchReferenceFrameSafely as P,buildTouchVisualizationResult as R,finalizeTouchInteraction as M}from"./9471.js";import{emitDiagnostic as C}from"./7599.js";import{PUBLIC_COMMANDS as O}from"./5898.js";import{successText as I}from"./1534.js";var A={};function E(e,t){return"macos"!==e.device.platform||"desktop"!==e.surface&&"menubar"!==e.surface||"menubar"===e.surface&&("click"===t||"press"===t)?null:s("UNSUPPORTED_OPERATION",`${t} is not supported on macOS ${e.surface} sessions yet. Open an app session to act, or use the ${e.surface} surface to inspect.`)}function q(e){let n=e.sessionStore.get(e.sessionName);if(!n)throw new d("SESSION_NOT_FOUND","No active session. Run open first.");return c({backend:function(e){let{req:t,session:r}=e;return{platform:r.device.platform,captureSnapshot:async(a,n)=>({snapshot:await e.captureSnapshotForSession(r,t.flags,e.sessionStore,e.contextFromFlags,{interactiveOnly:n?.interactiveOnly===!0})}),tap:async(n,o)=>U(await a(r.device,"press",[String(o.x),String(o.y)],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath))),fill:async(n,o,s)=>U(await a(r.device,"fill",[String(o.x),String(o.y),s],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath))),longPress:async(n,o,s)=>U(await a(r.device,"longpress",[String(o.x),String(o.y),...s?.durationMs===void 0?[]:[String(s.durationMs)]],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath))),typeText:async(n,o)=>U(await a(r.device,"type",[o],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath)))}}({...e,session:n}),...m("interaction commands",{plural:!0}),sessions:t({sessionName:e.sessionName,getSession:()=>n,recordOptions:{includeSnapshot:!0},setRecord:t=>{t.snapshot&&(r(n,t.snapshot),e.sessionStore.set(e.sessionName,n))}})})}function U(e){return e&&"object"==typeof e?e:void 0}function D(e){if(e.length<2)return null;let t=Number(e[0]),r=Number(e[1]);return Number.isFinite(t)&&Number.isFinite(r)?{x:t,y:r}:null}function K(e,t){let r=D(e);if(r)return{ok:!0,target:{kind:"point",x:r.x,y:r.y}};let a=e[0]??"";if(a.startsWith("@"))return{ok:!0,target:{kind:"ref",ref:a,fallbackLabel:e.slice(1).join(" ").trim()}};let n=e.join(" ").trim();return n?{ok:!0,target:{kind:"selector",selector:n}}:{ok:!1,response:s("INVALID_ARGS",`${t} requires @ref, selector expression, or x y coordinates`)}}function H(e){return"ref"===e.kind?{ref:T(e.target?.kind==="ref"?e.target.ref:void 0),refLabel:e.refLabel,selectorChain:e.selectorChain}:"selector"===e.kind?{selector:e.target?.kind==="selector"?e.target.selector:void 0,selectorChain:e.selectorChain,refLabel:e.refLabel}:{}}function T(e){return e?.startsWith("@")?e.slice(1):e}async function L(e){switch(e.req.command){case"press":return await j(e,"press");case"click":return await j(e,"click");case"longpress":return await j(e,"longpress");case"fill":return await V(e);default:return null}}async function j(e,t){let r,{req:a,sessionName:n,sessionStore:i}=e,l=i.get(n);if(!l)return s("SESSION_NOT_FOUND","No active session. Run open first.");let c="click"===t?"click":t,u="longpress"===t?"longpress":"press",f=E(l,c);if(f)return f;if(!o(u,l.device))return s("UNSUPPORTED_OPERATION",`${u} is not supported on this device`);let d=N(a.flags),p=b(d);if("longpress"!==t&&"primary"!==d){let e=v({commandLabel:c,platform:l.device.platform,button:d,count:a.flags?.count,intervalMs:a.flags?.intervalMs,holdMs:a.flags?.holdMs,jitterPx:a.flags?.jitterPx,doubleTap:a.flags?.doubleTap});if(e)return s(e.code,e.message,e.details)}let g="longpress"===t?function(e){var t,r,a;let n,o=D(e);if(o){return{ok:!0,target:{kind:"point",x:o.x,y:o.y},...void 0===(t=e[2])?{}:{durationMs:Number(t)}}}let s=(n=(r=e).at(-1),r.length>1&&void 0!==(a=n)&&""!==a.trim()&&Number.isFinite(Number(a))?{targetPositionals:r.slice(0,-1),duration:{durationMs:Number(n)}}:{targetPositionals:r,duration:{}}),i=K(s.targetPositionals,"longpress");return i.ok?{ok:!0,target:i.target,...s.duration}:i}(a.positionals??[]):K(a.positionals??[],c);if(!g.ok)return g.response;if("ref"===g.target.kind){let n=e.refSnapshotFlagGuardResponse("longpress"===t?"longpress":"press",a.flags);if(n)return n;r=await z(e,l)}let m=function(e){var t;let{session:r,commandLabel:a,target:n,flags:o}=e;if("click"!==a||"selector"!==n.kind||(t=o,t?.count!==void 0||t?.intervalMs!==void 0||t?.holdMs!==void 0||t?.jitterPx!==void 0||t?.doubleTap!==void 0||t?.clickButton!==void 0&&"primary"!==t.clickButton))return null;let s=h({session:r,selectorExpression:n.selector});return s?{...s,...o?.maestro?.allowNonHittableCoordinateFallback?{allowNonHittableCoordinateFallback:!0}:{}}:null}({session:l,commandLabel:c,target:g.target,flags:a.flags});if(m){let t=await W(e,l,m);if(t)return t}let _="longpress"===t?g.durationMs:void 0;return await Y(e,{androidFreshnessBaseline:r,run:async e=>await B({runtime:e,command:t,target:g.target,sessionName:n,requestId:a.meta?.requestId,clickButton:d,flags:a.flags,durationMs:_}),afterRun:async e=>{var t;await S(l,(t=g.target,"point"===t.kind?"coordinate tap":"ref"===e.kind&&e.target?.kind==="ref"?e.target.ref:"selector"===e.kind&&e.target?.kind==="selector"?e.target.selector:"target"))},buildPayloads:async r=>{var a;let n="durationMs"in(a=r)?a.durationMs:void 0,o=await G({params:e,session:l,result:r,extra:"longpress"===t?{...void 0!==n?{durationMs:n}:{},gesture:"longpress"}:p});return{result:o,responseData:o}}})}async function B(e){let{runtime:t,command:r,target:a,sessionName:n,requestId:o,flags:s}=e;if("longpress"===r)return await t.interactions.longPress(a,{session:n,requestId:o,durationMs:e.durationMs});let i={session:n,requestId:o,button:e.clickButton,count:s?.count,intervalMs:s?.intervalMs,holdMs:s?.holdMs,jitterPx:s?.jitterPx,doubleTap:s?.doubleTap};return"click"===r?await t.interactions.click(a,i):await t.interactions.press(a,i)}async function G(e){let{params:t,session:r,result:a,extra:n}=e,o="point"===a.kind?await P({session:r,flags:t.req.flags,sessionStore:t.sessionStore,contextFromFlags:t.contextFromFlags,captureSnapshotForSession:t.captureSnapshotForSession}):F(r.snapshot?.nodes??[]);return R({data:a.backendResult,fallbackX:a.point.x,fallbackY:a.point.y,referenceFrame:o,extra:{...H(a),...n}})}async function W(e,t,r){return await $({params:e,session:t,selector:r,command:"press",positionals:[],extra:{selector:r.raw},fallbackPhase:"ios_direct_selector_tap_fallback"})}async function $(e){let{params:t,session:r,selector:n,command:o,positionals:s,extra:i,fallbackPhase:l}=e,c=Date.now();try{var u,d;let e,l,f=await a(r.device,o,s,t.req.flags?.out,{...t.contextFromFlags(t.req.flags,r.appBundleId,r.trace?.outPath),directElementSelector:n,surface:r.surface})??{},p=Date.now(),g=(u=f,e="number"==typeof u.x?u.x:void 0,l="number"==typeof u.y?u.y:void 0,void 0!==e&&void 0!==l?{x:e,y:l}:{x:0,y:0}),m=R({data:f,fallbackX:g.x,fallbackY:g.y,referenceFrame:(d=f,"number"==typeof d.referenceWidth&&"number"==typeof d.referenceHeight?{referenceWidth:d.referenceWidth,referenceHeight:d.referenceHeight}:void 0),extra:{...i,...function(e,t){if(!e.allowNonHittableCoordinateFallback)return{};let r="tapped via non-hittable coordinate fallback"===t.message;return{maestroNonHittableCoordinateFallbackAllowed:!0,maestroNonHittableCoordinateFallbackUsed:r,...r?{maestroFallbackReason:"non-hittable-coordinate"}:{}}}(n,f)}});return M({session:r,sessionStore:t.sessionStore,command:t.req.command,positionals:t.req.positionals??[],retryPositionals:J(g),flags:t.req.flags,result:m,responseData:m,actionStartedAt:c,actionFinishedAt:p})}catch(e){if(!k(e))return{ok:!1,error:f(e)};return C({level:"debug",phase:l,data:{selector:n.raw,error:e instanceof Error?e.message:String(e)}}),null}}async function V(e){let{req:t,sessionName:r,sessionStore:a}=e,n=a.get(r);if(n){let e=E(n,"fill");if(e)return e}if(n&&!o("fill",n.device))return s("UNSUPPORTED_OPERATION","fill is not supported on this device");if(!n)return s("SESSION_NOT_FOUND","No active session. Run open first.");let i=function(e){let t=e[0]??"";if(t.startsWith("@")){var r;let a=w(e).text;return a?{ok:!0,target:{kind:"ref",ref:t,fallbackLabel:(r=e).length>=3&&r[1]?.trim()||""},text:a}:{ok:!1,response:s("INVALID_ARGS","fill requires text after ref")}}let a=D(e);if(a){let t=e.slice(2).join(" ");return t?{ok:!0,target:{kind:"point",x:a.x,y:a.y},text:t}:{ok:!1,response:s("INVALID_ARGS","fill requires text after coordinates")}}let n=w(e);return"selector"!==n.kind?{ok:!1,response:s("INVALID_ARGS","fill requires x y text, @ref text, or selector text")}:n.text.trim()?{ok:!0,target:{kind:"selector",selector:n.target.selector},text:n.text}:{ok:!1,response:s("INVALID_ARGS","fill requires text after selector")}}(t.positionals??[]);if(!i.ok)return i.response;if("ref"===i.target.kind){let r=e.refSnapshotFlagGuardResponse("fill",t.flags);if(r)return r;await z(e,n)}let l=function(e){let{session:t,target:r,flags:a}=e;if("selector"!==r.kind)return null;let n=h({session:t,selectorExpression:r.selector});return n?{...n,...a?.maestro?.allowNonHittableCoordinateFallback?{allowNonHittableCoordinateFallback:!0}:{}}:null}({session:n,target:i.target,flags:t.flags});if(l){let t=await X(e,n,l,i.text);if(t)return t}return await Y(e,{run:async e=>await e.interactions.fill(i.target,i.text,{session:r,requestId:t.meta?.requestId,delayMs:t.flags?.delayMs}),buildPayloads:e=>{let t="point"===e.kind?void 0:F(n.snapshot?.nodes??[]),r=R({data:e.backendResult,fallbackX:e.point.x,fallbackY:e.point.y,referenceFrame:t,extra:{...H(e),text:i.text}});e.warning&&(r.warning=e.warning);let a="ref"===e.kind?{...e.backendResult??{ref:T(e.target?.kind==="ref"?e.target.ref:void 0),x:e.point.x,y:e.point.y}}:r;return e.warning&&(a.warning=e.warning),{result:r,responseData:a}}})}async function X(e,t,r,a){return await $({params:e,session:t,selector:r,command:"fill",positionals:[a],extra:{selector:r.raw,text:a},fallbackPhase:"ios_direct_selector_fill_fallback"})}async function Y(e,t){let r=e.sessionStore.get(e.sessionName);if(!r)return s("SESSION_NOT_FOUND","No active session. Run open first.");let a=q(e),o=Date.now();try{let s=await n({session:r,command:e.req.command,phase:"before-command"}),i=await t.run(a);await t.afterRun?.(i),await n({session:r,command:e.req.command,phase:"after-command"});let l=Date.now(),{result:c,responseData:u}=await t.buildPayloads(i);return"recovered"===s.status&&(c.warning=s.warning,u.warning=s.warning),M({session:r,sessionStore:e.sessionStore,command:e.req.command,positionals:e.req.positionals??[],retryPositionals:function(e,t){if("click"===e||"press"===e)return J(t.point)}(e.req.command,i),flags:e.req.flags,result:c,responseData:u,actionStartedAt:o,actionFinishedAt:l,androidFreshnessBaseline:t.androidFreshnessBaseline})}catch(t){let e=u(t);if(g(e))throw e;return{ok:!1,error:f(t)}}}async function z(e,t){if(!l(t))return;let r=t.snapshot?.comparisonSafe===!0?t.snapshot:void 0;try{await e.captureSnapshotForSession(t,e.req.flags,e.sessionStore,e.contextFromFlags,{interactiveOnly:!0,androidFreshnessMode:"ref-refresh"})}catch(t){C({level:"warn",phase:"android_ref_snapshot_refresh_failed",data:{command:e.req.command,error:t instanceof Error?t.message:String(t)}})}return r}function J(e){return[String(e.x),String(e.y)]}async function Q(e){let t=await L({...e,captureSnapshotForSession:x,refSnapshotFlagGuardResponse:y});if(t)return t;switch(e.req.command){case O.type:return await Z({...e,captureSnapshotForSession:x});case"get":return await p(e);case"is":return await _(e);default:return null}}async function Z(e){let{sessionName:t,sessionStore:r}=e,a=r.get(t);if(!a)return s("SESSION_NOT_FOUND","No active session. Run open first.");if(!o(O.type,a.device))return s("UNSUPPORTED_OPERATION","type is not supported on this device");let n=await ee(a);return n||await et(e,a)}async function ee(e){return"android"===e.device.platform&&e.recording&&"failed"===(await i({session:e})).status?s("COMMAND_FAILED","Android system dialog blocked the recording session"):null}async function et(e,t){let{req:r,sessionName:a,sessionStore:o}=e,s=(r.positionals??[]).join(" "),i=q(e),l=Date.now();try{let e=await n({session:t,command:r.command,phase:"before-command"}),c=await i.interactions.typeText(s,{session:a,requestId:r.meta?.requestId,delayMs:r.flags?.delayMs});await n({session:t,command:r.command,phase:"after-command"});let u=Date.now(),f={...c.backendResult??{},text:c.text,delayMs:c.delayMs,...I(c.message??`Typed ${Array.from(c.text).length} chars`)};return"recovered"===e.status&&(f.warning=e.warning),M({session:t,sessionStore:o,command:r.command,positionals:r.positionals??[],flags:r.flags,result:f,responseData:f,actionStartedAt:l,actionFinishedAt:u})}catch(e){return{ok:!1,error:f(e)}}}e.r(A),e.d(A,{handleInteractionCommands:()=>Q});export{A as interaction_namespaceObject};
@@ -0,0 +1,26 @@
1
+ declare type PngWorkerJobResult = {
2
+ kind: 'decode';
3
+ width: number;
4
+ height: number;
5
+ data: Uint8Array;
6
+ } | {
7
+ kind: 'encode';
8
+ png: Uint8Array;
9
+ } | ({
10
+ kind: 'diff-pixels';
11
+ } & ScreenshotDiffPixelsResult);
12
+
13
+ /**
14
+ * Transfers result buffers instead of structured-cloning them, but only when a
15
+ * view fully owns its ArrayBuffer. Exported for direct unit coverage; the
16
+ * worker itself is the only runtime caller.
17
+ */
18
+ export declare function resultTransferList(result: PngWorkerJobResult): ArrayBuffer[];
19
+
20
+ declare type ScreenshotDiffPixelsResult = {
21
+ diffData: Buffer;
22
+ diffMask: Uint8Array;
23
+ differentPixels: number;
24
+ };
25
+
26
+ export { }
@@ -0,0 +1 @@
1
+ import{parentPort as e}from"node:worker_threads";import{normalizeError as t}from"../9152.js";import{computeScreenshotDiffPixels as r,PNG as i,toBuffer as n,decodePng as d}from"../9010.js";function f(e){return e.buffer instanceof ArrayBuffer&&0===e.byteOffset&&e.byteLength===e.buffer.byteLength}function a(e){return(function(e){switch(e.kind){case"decode":return[e.data];case"encode":return[e.png];case"diff-pixels":return[e.diffData,e.diffMask]}})(e).filter(f).map(e=>e.buffer)}e&&e.on("message",f=>{let o;try{o={id:f.id,ok:!0,result:function(e){switch(e.kind){case"decode":{let t=d(n(e.png),e.label);return{kind:"decode",width:t.width,height:t.height,data:t.data}}case"encode":{let t=new i({width:e.width,height:e.height,data:n(e.data)});return{kind:"encode",png:i.sync.write(t)}}case"diff-pixels":return{kind:"diff-pixels",...r(e)}}}(f)}}catch(e){o={id:f.id,ok:!1,error:t(e)}}e.postMessage(o,o.ok?a(o.result):[])});export{a as resultTransferList};
@@ -83,6 +83,8 @@ export declare type MetroBridgeRuntimePayload = {
83
83
 
84
84
  export declare type MetroBridgeScope = CompanionTunnelScope;
85
85
 
86
+ declare type MetroPrepareKind = 'auto' | 'react-native' | 'expo';
87
+
86
88
  /** Re-export of {@link SessionRuntimeHints} under the Metro-specific alias used by public API consumers. */
87
89
  export declare type MetroRuntimeHints = SessionRuntimeHints;
88
90
 
@@ -160,7 +162,7 @@ export declare function prepareRemoteMetro(options: PrepareRemoteMetroOptions):
160
162
 
161
163
  export declare type PrepareRemoteMetroOptions = {
162
164
  projectRoot: string;
163
- kind: 'auto' | 'react-native' | 'expo';
165
+ kind: MetroPrepareKind;
164
166
  publicBaseUrl?: string;
165
167
  proxyBaseUrl?: string;
166
168
  proxyBearerToken?: string;
@@ -1 +1 @@
1
- import{dispatchCommand as e,errorResponse as t,isCommandSupportedOnDevice as i}from"./2415.js";import{PUBLIC_COMMANDS as r}from"./5792.js";import{analyzeReactNativeOverlay as a}from"./9533.js";import{normalizeError as n}from"./9152.js";import{stripUndefined as o}from"./7455.js";import{interaction_snapshot_captureSnapshotForSession as s,readSnapshotNodesReferenceFrame as d,finalizeTouchInteraction as c}from"./9471.js";import{successText as l}from"./1998.js";async function v(e){var o;let{req:d,sessionName:c,sessionStore:v}=e;if(d.command!==r.reactNative)return null;let m=1===(o=d.positionals??[]).length&&"dismiss-overlay"===o[0]?{ok:!0}:{ok:!1,response:t("INVALID_ARGS","react-native supports only: dismiss-overlay")};if(!m.ok)return m.response;let _=v.get(c);if(!_)return t("SESSION_NOT_FOUND","No active session. Run open first.");if(!i(r.reactNative,_.device))return t("UNSUPPORTED_OPERATION","react-native dismiss-overlay is not supported on this device");try{let i=await s(_,d.flags,v,e.contextFromFlags,{interactiveOnly:!0}),r=a(i.nodes),n=r.primaryAction;if(!n)return r.detected?t("COMMAND_FAILED","React Native overlay detected, but no safe dismiss target was found",{hint:"Use screenshot --overlay-refs for visual evidence and report the overlay instead of pressing the warning body."}):{ok:!0,data:{action:"dismiss-overlay",detected:!1,dismissed:!1,...l("No React Native overlay detected")}};return await f(e,_,i,n)}catch(e){return{ok:!1,error:n(e)}}}async function f(t,i,r,a){let{req:n,sessionStore:s}=t,v=Date.now(),f=await e(i.device,"press",[String(a.point.x),String(a.point.y)],n.flags?.out,t.contextFromFlags(n.flags,i.appBundleId,i.trace?.outPath))??{},_=Date.now(),p=await m(t,i),y=o({...d(r.nodes),...f,action:"dismiss-overlay",overlayAction:a.action,x:a.point.x,y:a.point.y,ref:a.ref,label:a.label,warning:a.warning,dismissed:!0,verified:p.verified,verificationRequired:!p.verified,verificationWarning:p.verificationWarning,nextCommand:p.nextCommand,...l(p.verified?"React Native overlay dismiss action sent and verified gone":"React Native overlay dismiss action sent, but verification still detects an overlay")});return c({session:i,sessionStore:s,command:n.command,positionals:n.positionals??[],flags:n.flags,result:y,responseData:y,actionStartedAt:v,actionFinishedAt:_})}async function m(e,t){let{req:i,sessionStore:r}=e;return a((await s(t,i.flags,r,e.contextFromFlags,{interactiveOnly:!0})).nodes).detected?{verified:!1,verificationWarning:"React Native overlay is still detected after dismissal. Use screenshot --overlay-refs for visual evidence and report the overlay instead of pressing the warning body.",nextCommand:"agent-device screenshot --overlay-refs"}:{verified:!0}}export{v as handleReactNativeCommands};
1
+ import{dispatchCommand as e,errorResponse as t,isCommandSupportedOnDevice as i}from"./2415.js";import{PUBLIC_COMMANDS as r}from"./5898.js";import{analyzeReactNativeOverlay as a}from"./123.js";import{normalizeError as n}from"./9152.js";import{stripUndefined as o}from"./7455.js";import{interaction_snapshot_captureSnapshotForSession as s,readSnapshotNodesReferenceFrame as d,finalizeTouchInteraction as c}from"./9471.js";import{successText as l}from"./1534.js";async function v(e){var o;let{req:d,sessionName:c,sessionStore:v}=e;if(d.command!==r.reactNative)return null;let m=1===(o=d.positionals??[]).length&&"dismiss-overlay"===o[0]?{ok:!0}:{ok:!1,response:t("INVALID_ARGS","react-native supports only: dismiss-overlay")};if(!m.ok)return m.response;let _=v.get(c);if(!_)return t("SESSION_NOT_FOUND","No active session. Run open first.");if(!i(r.reactNative,_.device))return t("UNSUPPORTED_OPERATION","react-native dismiss-overlay is not supported on this device");try{let i=await s(_,d.flags,v,e.contextFromFlags,{interactiveOnly:!0}),r=a(i.nodes),n=r.primaryAction;if(!n)return r.detected?t("COMMAND_FAILED","React Native overlay detected, but no safe dismiss target was found",{hint:"Use screenshot --overlay-refs for visual evidence and report the overlay instead of pressing the warning body."}):{ok:!0,data:{action:"dismiss-overlay",detected:!1,dismissed:!1,...l("No React Native overlay detected")}};return await f(e,_,i,n)}catch(e){return{ok:!1,error:n(e)}}}async function f(t,i,r,a){let{req:n,sessionStore:s}=t,v=Date.now(),f=await e(i.device,"press",[String(a.point.x),String(a.point.y)],n.flags?.out,t.contextFromFlags(n.flags,i.appBundleId,i.trace?.outPath))??{},_=Date.now(),p=await m(t,i),y=o({...d(r.nodes),...f,action:"dismiss-overlay",overlayAction:a.action,x:a.point.x,y:a.point.y,ref:a.ref,label:a.label,warning:a.warning,dismissed:!0,verified:p.verified,verificationRequired:!p.verified,verificationWarning:p.verificationWarning,nextCommand:p.nextCommand,...l(p.verified?"React Native overlay dismiss action sent and verified gone":"React Native overlay dismiss action sent, but verification still detects an overlay")});return c({session:i,sessionStore:s,command:n.command,positionals:n.positionals??[],flags:n.flags,result:y,responseData:y,actionStartedAt:v,actionFinishedAt:_})}async function m(e,t){let{req:i,sessionStore:r}=e;return a((await s(t,i.flags,r,e.contextFromFlags,{interactiveOnly:!0})).nodes).detected?{verified:!1,verificationWarning:"React Native overlay is still detected after dismissal. Use screenshot --overlay-refs for visual evidence and report the overlay instead of pressing the warning body.",nextCommand:"agent-device screenshot --overlay-refs"}:{verified:!0}}export{v as handleReactNativeCommands};
@@ -0,0 +1,26 @@
1
+ let e,t,r,a;import i from"node:fs";import o from"node:path";import{createHash as n}from"node:crypto";import s from"node:os";import{fileURLToPath as d}from"node:url";import{AppError as c}from"./9152.js";import{runCmd as l}from"./9818.js";import{runXcrun as u,SessionStore as m,runIosRunnerCommand as f,isCommandSupportedOnDevice as h,errorResponse as p,acquireProcessLock as w,ensureDeviceReady as g,resolvePublicSessionName as y,resolveImplicitSessionScope as _,resolveTargetDevice as v,IOS_RUNNER_CONTAINER_BUNDLE_IDS as P}from"./2415.js";import{readProcessStartTime as S}from"./8656.js";import{sleep as A}from"./4829.js";import{withDiagnosticTimer as b,emitDiagnostic as C}from"./7599.js";import{androidDeviceForSerial as M,runAndroidAdb as I}from"./8806.js";import{pullAndroidAdbFile as x}from"./9639.js";import{resolveRecordingProvider as D}from"./recording-provider.js";function $(e=process.env){let t=E(),r=o.join(t,"home"),a=o.join(t,"module-cache");return i.mkdirSync(r,{recursive:!0}),i.mkdirSync(a,{recursive:!0}),{...e,HOME:r,CLANG_MODULE_CACHE_PATH:a}}async function R(e){let t=i.statSync(e.sourcePath),r=i.readFileSync(e.sourcePath),a=L(e.cacheName??o.basename(e.sourcePath,o.extname(e.sourcePath))),n=T(["2",process.platform,process.arch,o.resolve(e.sourcePath),t.size,r]),s=o.join(E(),"bin",`${a}-${n}`);return await k({sourcePath:e.sourcePath,executablePath:s,timeoutMs:e.timeoutMs}),s}async function N(e){let t=L(e.cacheName),r=T(["2",process.platform,process.arch,e.source]),a=o.join(E(),"sources",`${t}-${r}.swift`),i=o.join(E(),"bin",`${t}-${r}`);return await k({sourcePath:a,executablePath:i,sourceText:e.source,timeoutMs:e.timeoutMs}),i}function E(){let e=process.env.AGENT_DEVICE_SWIFT_CACHE_DIR?.trim();return e?o.resolve(e):o.join(s.tmpdir(),"agent-device-swift-cache")}async function k(e){if(O(e.executablePath))return;let t=o.dirname(e.executablePath);i.mkdirSync(t,{recursive:!0});let r=`${e.executablePath}.lock`,a=await F(r,e.executablePath,e.timeoutMs??12e4);if(!a)return;let n=i.mkdtempSync(o.join(t,`.${o.basename(e.executablePath)}.${process.pid}.`)),s=o.join(n,o.basename(e.executablePath));try{if(O(e.executablePath))return;void 0===e.sourceText||i.existsSync(e.sourcePath)||(i.mkdirSync(o.dirname(e.sourcePath),{recursive:!0}),i.writeFileSync(e.sourcePath,e.sourceText)),await l("xcrun",["swiftc",e.sourcePath,"-o",s],{timeoutMs:e.timeoutMs??12e4,env:$()}),i.renameSync(s,e.executablePath)}finally{i.rmSync(n,{recursive:!0,force:!0}),await a()}}async function F(e,t,r){if(O(t))return null;try{return await w({lockDirPath:e,owner:{pid:process.pid,startTime:S(process.pid),acquiredAtMs:Date.now()},timeoutMs:r,pollMs:25,ownerGraceMs:r,description:`Swift cache lock: ${e} (${r}ms)`})}catch(t){if(t instanceof c&&t.message.startsWith("Timed out waiting for Swift cache lock:"))throw new c("COMMAND_FAILED",t.message,{...t.details,lockDir:e,timeoutMs:r,hint:`Another agent-device process may still be compiling this Swift helper. Retry shortly; if no agent-device process is active, remove "${e}" and retry.`});throw t}}function O(e){try{return i.accessSync(e,i.constants.X_OK),i.statSync(e).isFile()}catch{return!1}}function L(e){return e.replaceAll(/[^A-Za-z0-9._-]/g,"-").replaceAll(/^-+|-+$/g,"")||"swift-helper"}function T(e){let t=n("sha256");for(let r of e)t.update(Buffer.isBuffer(r)?r:String(r)),t.update("\0");return t.digest("hex").slice(0,16)}let U=`
2
+ import Foundation
3
+ import AVFoundation
4
+
5
+ let url = URL(fileURLWithPath: CommandLine.arguments[1])
6
+ let asset = AVURLAsset(url: url)
7
+ let semaphore = DispatchSemaphore(value: 0)
8
+ var exitCode: Int32 = 1
9
+
10
+ Task {
11
+ defer { semaphore.signal() }
12
+ do {
13
+ let playable = try await asset.load(.isPlayable)
14
+ let duration = try await asset.load(.duration)
15
+ if playable && duration.isValid && !duration.isIndefinite && CMTimeGetSeconds(duration) > 0 {
16
+ exitCode = 0
17
+ }
18
+ } catch {
19
+ exitCode = 1
20
+ }
21
+ }
22
+
23
+ semaphore.wait()
24
+ exit(exitCode)
25
+ `.trim();async function z(e,t={}){let r,a=t.pollMs??150,o=t.attempts??12,n=0;for(let t=0;t<o;t+=1){let t=0;try{t=i.statSync(e).size}catch{t=0}if(t>0&&t===r){if((n+=1)>=2)return}else n=0;r=t,await A(a)}}async function q(e){try{let t=await K(),r=await l(t,[e],{allowFailure:!0,timeoutMs:1e4,env:$()});if(0===r.exitCode)return!0;if(V(r.stderr,r.stdout))return H(e);return!1}catch(r){var t;if((t=r)instanceof c&&("TOOL_MISSING"===t.code||V(String(t.details?.stderr??""),String(t.details?.stdout??""))))return H(e);throw r}}async function K(){e??=N({source:U,cacheName:"video-validator",timeoutMs:3e4});try{return await e}catch(t){throw e=void 0,t}}async function G(e,t={}){let r=t.pollMs??150,a=t.attempts??12;for(let t=0;t<a;t+=1){if(await q(e))return;await A(r)}}function V(e,t){let r=`${e}
26
+ ${t}`;return/\b(no such module ['"]AVFoundation['"]|unable to find utility ["']swiftc?["']|xcrun: error: unable to find utility ["']swiftc?["'])\b/i.test(r)}function H(e){try{let t=i.statSync(e);if(!t.isFile()||t.size<=0)return!1}catch{return!1}let t=function(e){try{let t=i.openSync(e,"r");try{let e=i.fstatSync(t).size,r=0,a=[];for(;r+8<=e&&a.length<16;){let e=Buffer.alloc(8);if(8>i.readSync(t,e,0,8,r))break;let o=e.readUInt32BE(0),n=e.toString("latin1",4,8);if(a.push(n),1===o){let e=Buffer.alloc(8);if(8>i.readSync(t,e,0,8,r+8))break;o=Number(e.readBigUInt64BE(0))}if(!Number.isFinite(o)||o<=0)break;r+=o}return a}finally{i.closeSync(t)}}catch{return[]}}(e);return t.includes("ftyp")&&t.includes("moov")}function j(e){let t=o.parse(e);return o.join(t.dir,`${t.name}.gesture-telemetry.json`)}function W(e){return[...e].sort((e,t)=>e.tMs-t.tMs)}function B(e){var t,r,a;let o,n,{recording:s,trimStartMs:d}=e,c=(o=j((t={videoPath:s.outPath,events:s.gestureEvents,trimStartMs:d}).videoPath),n={version:1,generatedAt:new Date().toISOString(),events:(r=t.events,(a=t.trimStartMs??0)>0?W(r.flatMap(e=>{let t=e.tMs-a,r="durationMs"in e?e.durationMs:void 0;return("number"==typeof r?t+r:t)<=0?[]:[{...e,tMs:Math.max(0,t)}]})):W(r))},i.writeFileSync(o,JSON.stringify(n,null,2)),o);return s.telemetryPath=c,c}function J(e){let t=o.dirname(d(import.meta.url)),r=[d(new URL(`./${e}`,import.meta.url)),o.resolve(t,`../../ios-runner/AgentDeviceRunner/RecordingScripts/${e}`),o.resolve(t,`../../../ios-runner/AgentDeviceRunner/RecordingScripts/${e}`),o.resolve(process.cwd(),`ios-runner/AgentDeviceRunner/RecordingScripts/${e}`)];for(let e of r)if(i.existsSync(e))return e;throw new c("COMMAND_FAILED",`Missing recording helper script: ${e}`,{hint:"Ensure ios-runner/AgentDeviceRunner/RecordingScripts is present in this checkout or bundled with the package.",scriptName:e,searchedPaths:r})}async function X(e){var t;let r,a,{videoPath:n,scriptPath:s,scriptArgs:d,commandDescription:u}=e;await z(n),await G(n);let m=(t=n,r=o.parse(t),a=`${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,o.join(r.dir,`.${r.name}.agent-device-${a}${r.ext||".mp4"}`));try{let e=await R({sourcePath:s});await l(e,["--input",n,"--output",m,...d],{timeoutMs:12e4,env:$()}),await G(m),i.renameSync(m,n)}catch(t){let e=t instanceof c?t:new c("COMMAND_FAILED",String(t),void 0,t instanceof Error?t:void 0);throw new c("COMMAND_FAILED",u,{...e.details,videoPath:n,script:s},e)}finally{i.rmSync(m,{force:!0})}}async function Z(e){let{videoPath:t,trimStartMs:a}=e;a>0&&await X({videoPath:t,scriptPath:r??=J("recording-trim.swift"),scriptArgs:["--trim-start-ms",String(a)],commandDescription:"Failed to trim the start of the iOS recording"})}async function Q(e){let{videoPath:r,telemetryPath:a,targetLabel:i="recording"}=e;await X({videoPath:r,scriptPath:t??=J("recording-overlay.swift"),scriptArgs:["--events",a],commandDescription:`Failed to add touch overlays to the ${i}`})}async function Y(e){let{videoPath:t,quality:r,targetLabel:i="recording"}=e;await X({videoPath:t,scriptPath:a??=J("recording-resize.swift"),scriptArgs:["--quality",String(r)],commandDescription:`Failed to resize the ${i}`})}function ee(e){return e instanceof Error?e.message:String(e)}function et(e,t){return e.stderr.trim()||e.stdout.trim()||`${t} exited with code ${e.exitCode}`}function er(e,t,r=Date.now()){let a=Math.max(0,r-t.startedAt);return a>=1e3?{message:e,tooShort:!1}:{message:`${e}. Recording stopped after ${Math.round(a)}ms; wait at least 1000ms between record start and record stop so the recorder can finalize a playable MP4`,tooShort:!0}}async function ea(e){let{recording:t,deps:r,trimStartMs:a,targetLabel:i}=e,o=B({recording:t,trimStartMs:a});if(!t.showTouches)return void C({level:"debug",phase:"record_stop_overlay_skipped",data:{reason:"hide_touches"}});if(0===t.gestureEvents.length)return void C({level:"debug",phase:"record_stop_overlay_skipped",data:{reason:"no_gesture_events"}});let n=function(e=process.platform){if("darwin"!==e)return"touch overlay burn-in is only available on macOS hosts; returning raw video plus gesture telemetry"}();if(n){t.overlayWarning??=n;return}try{await b("record_stop_overlay_export",()=>r.overlayRecordingTouches({videoPath:t.outPath,telemetryPath:o,targetLabel:i}),{targetLabel:i,gestureEventCount:t.gestureEvents.length})}catch(e){t.overlayWarning??=`failed to overlay recording touches: ${ee(e)}`}}function ei(e,t){if(1===t)return e;let r=o.parse(e),a=r.ext||".mp4";return o.join(r.dir,`${r.name}.part-${String(t).padStart(3,"0")}${a}`)}function eo(e){return e.chunks??=[{index:1,path:e.outPath,remotePath:e.remotePath}],e.chunks}async function en(e){let{recording:t,startNextChunk:r,finishCurrentChunk:a}=e;if(t.stopping)return;let i=await a();if(i)throw Error(i);if(t.stopping)return;let n=eo(t),s=n.length+1,d=await r(o.posix.dirname(t.remotePath));t.remotePath=d.remotePath,t.remotePid=d.remotePid,n.push({index:s,path:ei(t.outPath,s),remotePath:d.remotePath}),t.warning??="Android adb screenrecord is capped at 180s, so this recording was split into multiple MP4 chunks."}async function es(e){let{recording:t,deps:r}=e;eo(t).length<=1?await ea({recording:t,deps:r,targetLabel:"Android recording"}):(B({recording:t}),t.showTouches&&t.gestureEvents.length>0&&(t.overlayWarning??="touch overlay burn-in is skipped for chunked Android recordings; returning raw chunks plus gesture telemetry"))}async function ed(e){for(let t of e.chunks){let r=await ec({deps:e.deps,deviceId:e.deviceId,remotePath:t.remotePath,outPath:t.path});if(r)return`failed to copy recording chunk ${t.index}: ${r}`}}async function ec(e){let t,{deps:r,deviceId:a,remotePath:o,outPath:n}=e;for(let e=0;e<2;e+=1){el(n);let s=M(a),d=await x(o,n,{allowFailure:!0,device:s});if(0!==d.exitCode)t=et(d,"adb pull");else{await r.waitForStableFile(n,{pollMs:250,attempts:20});let t=await r.isPlayableVideo(n);if(C({level:"debug",phase:"record_stop_android_pull_validation",data:{deviceId:a,remotePath:o,outPath:n,attempt:e+1,fileSize:function(e){try{return i.statSync(e).size}catch{return 0}}(n),playable:t}}),t)return;C({level:"warn",phase:"record_stop_android_invalid_video_retry",data:{deviceId:a,remotePath:o,outPath:n,attempt:e+1}})}e<1&&await A(750)}return t?`failed to copy recording from device: ${t}`:(el(n),"failed to copy recording from device: pulled file is not a playable MP4")}function el(e){try{i.rmSync(e,{force:!0})}catch{}}async function eu(e,t,r){return await I(M(e),t,r)}async function em(e,t){let r=await eu(e,["shell","ps","-o","pid=","-p",t],{allowFailure:!0});return 0===r.exitCode&&r.stdout.split(/\s+/).map(e=>e.trim()).includes(t)}async function ef(e,t){for(let r=0;r<40;r+=1){if(!await em(e,t))return!0;await A(250)}return!await em(e,t)}async function eh(e,t){let r,a=0;for(let i=0;i<20;i+=1){let i=await eu(e,["shell","stat","-c","%s",t],{allowFailure:!0}),o=0===i.exitCode?i.stdout.trim():"";if(o.length>0&&o===r){if((a+=1)>=4)return}else a=0;r=o,await A(250)}}async function ep(e,t,r){for(let a=0;a<8;a+=1){let i=await eu(e,["shell","stat","-c","%s",t],{allowFailure:!0}),o=0===i.exitCode?Number(i.stdout.trim()):NaN;if(Number.isFinite(o)&&o>0)return!0;if(!await em(e,r))break;if(a+1>=2)return!0;await A(250)}return!1}async function ew(e){let{deviceId:t,quality:r}=e;if(void 0===r||r>=10)return;let a=await eu(t,["shell","wm","size"],{allowFailure:!0}),i=a.stdout.match(/Override size:\s*(\d+)x(\d+)/)??a.stdout.match(/Physical size:\s*(\d+)x(\d+)/);if(0!==a.exitCode||!i)throw Error(`failed to resolve Android screen size for recording quality: ${et(a,"adb shell wm size")}`);return{width:eg(Number(i[1]),r),height:eg(Number(i[2]),r)}}function eg(e,t){return Math.max(2,2*Math.round(e*t/10/2))}async function ey(e,t){await eu(e,["shell","rm","-f",t],{allowFailure:!0})}async function e_(e,t){let r=await eu(e,["shell","kill","-9",t],{allowFailure:!0});return C({level:"warn",phase:"record_stop_android_force_signal",data:{deviceId:e,remotePid:t,exitCode:r.exitCode,stdout:r.stdout.trim(),stderr:r.stderr.trim()}}),!(0!==r.exitCode&&await em(e,t))&&await ef(e,t)}async function ev(e){var t;let r,a,{device:i,recordingSize:o,preferredRemoteDir:n}=e,s="failed to start recording: Android screenrecord did not begin producing frames";for(let e of(t=Date.now(),r=`agent-device-recording-${t}.mp4`,a=["/sdcard","/data/local/tmp"],(n&&a.includes(n)?[n,...a.filter(e=>e!==n)]:a).map(e=>`${e}/${r}`))){let t=await eu(i.id,["shell",function(e,t){let r=["screenrecord"];return t&&r.push("--size",`${t.width}x${t.height}`),r.push(e),`${r.join(" ")} >/dev/null 2>&1 & echo $!`}(e,o)],{allowFailure:!0});if(0!==t.exitCode){s=`failed to start recording: ${et(t,"adb shell screenrecord")}`;continue}let r=t.stdout.split(/\r?\n/).map(e=>e.trim()).filter(e=>/^\d+$/.test(e)).at(-1);if(!r){s="failed to start recording: adb did not return a valid Android screenrecord pid",await ey(i.id,e);continue}if(C({level:"debug",phase:"record_start_android_started",data:{deviceId:i.id,remotePath:e,remotePid:r}}),await ep(i.id,e,r))return{remotePath:e,remotePid:r,startedAt:Date.now()};s="failed to start recording: Android screenrecord did not begin producing frames",await e_(i.id,r),await ey(i.id,e)}return{error:p("COMMAND_FAILED",s)}}async function eP(e){let t,{device:r,recordingBase:a}=e;try{t=await ew({deviceId:r.id,quality:a.quality})}catch(e){return p("COMMAND_FAILED",e instanceof Error?e.message:String(e))}let i=await ev({device:r,recordingSize:t});if("error"in i)return i.error;let o={platform:"android",remotePath:i.remotePath,remotePid:i.remotePid,chunks:[{index:1,path:a.outPath,remotePath:i.remotePath}],...a,startedAt:i.startedAt};return!function e(t){let{recording:r,startNextChunk:a,finishCurrentChunk:i}=t,o=setTimeout(()=>{r.rotationPromise=en({recording:r,startNextChunk:a,finishCurrentChunk:i}).catch(e=>{r.rotationFailedReason=e instanceof Error?e.message:String(e)}).finally(()=>{r.rotationPromise=void 0,r.stopping||r.rotationFailedReason||e({recording:r,startNextChunk:a,finishCurrentChunk:i})})},17e4);o.unref?.(),r.rotationTimer=o}({recording:o,finishCurrentChunk:async()=>await eS({device:r,recording:o,waitForRemoteFileStability:!1}),startNextChunk:async e=>{let a=await ev({device:r,recordingSize:t,preferredRemoteDir:e});if("error"in a)throw Error(a.error.ok?"failed to start next Android recording chunk":a.error.error.message);return a}}),o}async function eS(e){let{device:t,recording:r,waitForRemoteFileStability:a=!0}=e;await em(t.id,r.remotePid)||(r.warning??=function(e){if(!(Date.now()-e.startedAt<178e3))return"Android adb screenrecord stopped before record stop, likely after reaching the 180s platform limit. The MP4 may be truncated; final interactions after the limit are not in the video."}(r));let i=await eu(t.id,["shell","kill","-2",r.remotePid],{allowFailure:!0});if(C({level:"debug",phase:"record_stop_android_signal",data:{deviceId:t.id,remotePath:r.remotePath,remotePid:r.remotePid,exitCode:i.exitCode,stdout:i.stdout.trim(),stderr:i.stderr.trim()}}),0!==i.exitCode)return await eA(t.id,r.remotePid,i);let o=await eb(t.id,r.remotePid);if(o)return o;a&&await eh(t.id,r.remotePath)}async function eA(e,t,r){if(await em(e,t)&&!await e_(e,t))return`failed to stop recording: ${et(r,"adb shell kill")}`}async function eb(e,t){if(!await ef(e,t)&&!await e_(e,t))return`failed to stop recording: Android screenrecord pid ${t} did not exit`}async function eC(e){let t,{deps:r,device:a,recording:i,stopRequestedAt:o}=e;C({level:"debug",phase:"record_stop_android_enter",data:{deviceId:a.id,remotePath:i.remotePath,remotePid:i.remotePid}}),i.stopping=!0,i.rotationTimer&&(clearTimeout(i.rotationTimer),i.rotationTimer=void 0),await i.rotationPromise;let n=await eS({device:a,recording:i});if(i.rotationFailedReason&&!n&&(i.warning??=`Android recording chunk rotation failed: ${i.rotationFailedReason}`),!n){let e=await ed({deps:r,deviceId:a.id,chunks:eo(i)});if(e)return await s(),p("COMMAND_FAILED",eM(e,i,o));await es({recording:i,deps:r})}if(await s(),n)return p("COMMAND_FAILED",eM(n,i,o));if(t)return p("COMMAND_FAILED",t);return null;async function s(){for(let e of eo(i)){let r=await eu(a.id,["shell","rm","-f",e.remotePath],{allowFailure:!0});C({level:"debug",phase:"record_stop_android_cleanup",data:{deviceId:a.id,remotePath:e.remotePath,exitCode:r.exitCode,stdout:r.stdout.trim(),stderr:r.stderr.trim()}}),0===r.exitCode||n||(t=`failed to clean up remote recording: ${et(r,"adb shell rm")}`)}}}function eM(e,t,r){return er(e,t,r).message}function eI(e){let t=e.appBundleId?.trim();return t&&t.length>0?t:void 0}function ex(e,t,r){return{verbose:e.flags?.verbose,logPath:t,traceLogPath:r.trace?.outPath,requestId:e.meta?.requestId}}async function eD(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o}=e,n=eI(r);try{await o.runIosRunnerCommand(a,{command:"recordStop",appBundleId:n},ex(t,i,r))}catch(e){C({level:"warn",phase:"record_stop_runner_failed",data:{platform:a.platform,kind:a.kind,deviceId:a.id,session:r.name,error:ee(e)}})}}async function e$(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o}=e,n=eI(r);if(n)try{await o.runIosRunnerCommand(a,{command:"snapshot",appBundleId:n,interactiveOnly:!0,compact:!0,depth:1},ex(t,i,r))}catch(e){C({level:"warn",phase:"record_start_simulator_runner_warm_failed",data:{deviceId:a.id,session:r.name,appBundleId:n,error:ee(e)}})}}async function eR(e){let t,r,{req:a,activeSession:i,sessionStore:o,device:n,logPath:s,deps:d,fpsFlag:c,recordingBase:l,appBundleId:u}=e,m=`agent-device-recording-${Date.now()}.mp4`,f=`tmp/${m}`,h=ex(a,s,i),w=async()=>d.runIosRunnerCommand(n,{command:"recordStart",outPath:m,fps:c,quality:l.quality,appBundleId:u},h);try{let e=await w();t="number"==typeof e.recorderStartUptimeMs?e.recorderStartUptimeMs:void 0,r="number"==typeof e.targetAppReadyUptimeMs?e.targetAppReadyUptimeMs:void 0}catch(a){var g,y;if(!ee(a).toLowerCase().includes("recording already in progress"))return p("COMMAND_FAILED",`failed to start recording: ${ee(a)}`);C({level:"warn",phase:"record_start_runner_desynced",data:{platform:n.platform,kind:n.kind,deviceId:n.id,session:i.name,error:ee(a)}});let e=(g=n.id,y=i.name,o.toArray().find(e=>e.name!==y&&"ios"===e.device.platform&&"device"===e.device.kind&&e.device.id===g&&e.recording?.platform==="ios-device-runner"));if(e)return p("COMMAND_FAILED",`failed to start recording: recording already in progress in session '${e.name}'`);try{await d.runIosRunnerCommand(n,{command:"recordStop",appBundleId:u},h)}catch{}try{let e=await w();t="number"==typeof e.recorderStartUptimeMs?e.recorderStartUptimeMs:void 0,r="number"==typeof e.targetAppReadyUptimeMs?e.targetAppReadyUptimeMs:void 0}catch(e){return p("COMMAND_FAILED",`failed to start recording: ${ee(e)}`)}}return{platform:"ios-device-runner",remotePath:f,runnerStartedAtUptimeMs:t,targetAppReadyUptimeMs:r,...l}}async function eN(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,fpsFlag:n,recordingBase:s,appBundleId:d}=e;try{await o.runIosRunnerCommand(a,{command:"recordStart",outPath:s.outPath,fps:n,quality:s.quality,appBundleId:d},ex(t,i,r))}catch(e){return p("COMMAND_FAILED",`failed to start recording: ${ee(e)}`)}return{platform:"macos-runner",...s}}async function eE(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,recording:n}=e;await eD({req:t,activeSession:r,device:a,logPath:i,deps:o});let s={stdout:"",stderr:"",exitCode:1};for(let e of P)if(0===(s=await o.runCmd("xcrun",["devicectl","device","copy","from","--device",a.id,"--source",n.remotePath,"--destination",n.outPath,"--domain-type","appDataContainer","--domain-identifier",e],{allowFailure:!0})).exitCode)break;if(0!==s.exitCode){let e=s.stderr.trim()||s.stdout.trim()||`devicectl exited with code ${s.exitCode}`;return p("COMMAND_FAILED",`failed to copy recording from device: ${e}`)}let d="number"!=typeof n.runnerStartedAtUptimeMs||"number"!=typeof n.targetAppReadyUptimeMs?0:Math.max(0,n.targetAppReadyUptimeMs-n.runnerStartedAtUptimeMs);return d>0&&await o.trimRecordingStart({videoPath:n.outPath,trimStartMs:d}),await ea({recording:n,deps:o,trimStartMs:d,targetLabel:"iOS recording"}),null}async function ek(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,recording:n}=e;return await eD({req:t,activeSession:r,device:a,logPath:i,deps:o}),await ea({recording:n,deps:o,targetLabel:"macOS recording"}),null}async function eF(e){let{deps:t,recording:r}=e;r.child.kill("SIGINT");let a=await eO(r.wait,5e3);return a||(await eL(t,r,"SIGINT"),(a=await eO(r.wait,2e3))||(r.child.kill("SIGTERM"),await eL(t,r,"SIGTERM"),a=await eO(r.wait,2e3)))?a:(r.child.kill("SIGKILL"),await eL(t,r,"SIGKILL"),await eO(r.wait,2e3))}async function eO(e,t){return await Promise.race([e,A(t).then(()=>null)])}async function eL(e,t,r){await eU(e,t,r)||await eT(e,t.outPath,r)}async function eT(e,t,r){let a,i=`simctl.*recordVideo.*${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}`;try{a=await e.runCmd("pgrep",["-f",i],{allowFailure:!0})}catch(e){C({level:"warn",phase:"record_stop_ios_simulator_pgrep_failed",data:{outPath:t,signal:r,error:ee(e)}});return}let o=eq(eG(a.stdout)),n=eK(o,r);C({level:n>0?"warn":"debug",phase:"record_stop_ios_simulator_signal_recorders",data:{outPath:t,signal:r,matchedPidCount:o.length,signaled:n,pgrepExitCode:a.exitCode}})}async function eU(e,t,r){let a=t.recorderPid??t.child.pid;if("number"!=typeof a||!Number.isInteger(a)||a<=0)return C({level:"debug",phase:"record_stop_ios_simulator_owned_recorder_unavailable",data:{outPath:t.outPath,signal:r,reason:"missing_recorder_pid"}}),!1;let i=await ez(e,a,t.outPath,r),o=eq([a,...i.pids]),n=eK(o,r);return C({level:n>0?"warn":"debug",phase:"record_stop_ios_simulator_signal_owned_recorder",data:{outPath:t.outPath,signal:r,recorderPid:a,childPidCount:i.pids.length,matchedPidCount:o.length,signaled:n,pgrepExitCode:i.exitCode}}),n>0}async function ez(e,t,r,a){let i;try{i=await e.runCmd("pgrep",["-P",String(t)],{allowFailure:!0})}catch(e){return C({level:"warn",phase:"record_stop_ios_simulator_owned_pgrep_failed",data:{outPath:r,signal:a,parentPid:t,error:ee(e)}}),{pids:[]}}return{pids:eG(i.stdout),exitCode:i.exitCode}}function eq(e){return Array.from(new Set(e)).filter(e=>Number.isInteger(e)&&e>0&&e!==process.pid)}function eK(e,t){let r=0;for(let a of e)try{process.kill(a,t),r+=1}catch{}return r}function eG(e){return e.split(/\s+/).map(e=>Number(e)).filter(e=>Number.isInteger(e)&&e>0)}async function eV(e){"ios"!==e.platform||0!==e.gestureEvents.length&&await A(350)}async function eH(e){for(let t=0;t<2;t+=1){try{if(i.statSync(e).size>0)return Date.now()}catch{}if(t+1>=2)break;await A(250)}return Date.now()}async function ej(e){let t,r,{req:a,activeSession:i,device:o,logPath:n,deps:s,recordingBase:d,resolvedOut:c}=e;d.showTouches&&await e$({req:a,activeSession:i,device:o,logPath:n,deps:s});let{child:l,wait:u}=s.startIosSimulatorRecording({device:o,outPath:c}),m=await eH(c);if(d.showTouches)try{let e=Date.now(),d=await s.runIosRunnerCommand(o,{command:"uptime",appBundleId:eI(i)},ex(a,n,i)),c=Date.now();t=Math.round((e+c)/2),r="number"==typeof d.currentUptimeMs?d.currentUptimeMs:void 0}catch{}return{platform:"ios",child:l,wait:u,...d,recorderPid:l.pid,startedAt:m,gestureClockOriginAtMs:void 0===r?void 0:t,gestureClockOriginUptimeMs:r}}async function eW(e){let t,{req:r,sessionName:a,sessionStore:n,activeSession:s,device:d,logPath:c,deps:l}=e;if(s.recording)return p("INVALID_ARGS","recording already in progress");let u=r.flags?.fps,f=r.flags?.quality;if(void 0!==u&&(!Number.isInteger(u)||u<1||u>120))return p("INVALID_ARGS","fps must be an integer between 1 and 120");if(void 0!==f&&(!Number.isInteger(f)||f<5||f>10))return p("INVALID_ARGS","quality must be an integer between 5 and 10");if(!h("record",d))return p("UNSUPPORTED_OPERATION","record is not supported on this device");let w=r.positionals?.[1]??`./recording-${Date.now()}.mp4`,g=m.expandHome(w,r.meta?.cwd),y={outPath:g,clientOutPath:r.meta?.clientArtifactPaths?.outPath,startedAt:Date.now(),quality:r.flags?.quality,showTouches:r.flags?.hideTouches!==!0,gestureEvents:[]};if(i.mkdirSync(o.dirname(g),{recursive:!0}),i.rmSync(g,{force:!0}),"ios"===d.platform&&"device"===d.kind){let e=eI(s);if(!e)return p("INVALID_ARGS","record on physical iOS devices requires an active app session; run open <app> first");t=await eR({req:r,activeSession:s,sessionStore:n,device:d,logPath:c,deps:l,fpsFlag:u,recordingBase:y,appBundleId:e})}else if("macos"===d.platform){let e=eI(s);if(!e)return p("INVALID_ARGS","record on macOS requires an active app session; run open <app> first");t=await eN({req:r,activeSession:s,device:d,logPath:c,deps:l,fpsFlag:u,recordingBase:y,appBundleId:e})}else t="ios"===d.platform?await ej({req:r,activeSession:s,device:d,logPath:c,deps:l,recordingBase:y,resolvedOut:g}):await eP({device:d,recordingBase:y});if("ok"in t)return t;s.recording=t,n.set(a,s);let _=n.ensureSessionDir(a);return n.recordAction(s,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:{action:"start",showTouches:t.showTouches}}),{ok:!0,data:{recording:"started",outPath:t.clientOutPath??w,sessionStateDir:_,showTouches:t.showTouches}}}async function eB(e){let{deps:t,device:r,recording:a,stopRequestedAt:i}=e;if("android"===a.platform)return await eC({deps:t,device:r,recording:a,stopRequestedAt:i});await b("record_stop_tail_settle",()=>t.waitForRecordingTail(a),{platform:a.platform,gestureEventCount:a.gestureEvents.length});let o=await b("record_stop_ios_simulator_process",()=>eF({deps:t,recording:a}),{outPath:a.outPath});if(!o)return eJ("failed to stop recording: simctl recordVideo did not exit after 5000ms and forced cleanup",a,i);if(0!==o.exitCode)return eJ(`failed to stop recording: ${et(o,"simctl recordVideo")}`,a,i);if(await b("record_stop_video_stable",()=>t.waitForStableFile(a.outPath,{pollMs:150,attempts:12}),{outPath:a.outPath}),!await b("record_stop_video_playable_check",()=>t.isPlayableVideo(a.outPath),{outPath:a.outPath}))return eJ(`failed to stop recording: ${a.outPath} was not finalized into a playable video`,a,i);if(void 0!==a.quality&&a.quality<10){let e=a.quality;try{await b("record_stop_resize",()=>t.resizeRecording({videoPath:a.outPath,quality:e,targetLabel:"iOS recording"}),{outPath:a.outPath,quality:e})}catch(e){a.overlayWarning=`failed to resize recording: ${ee(e)}`}}return await b("record_stop_finalize_overlay",()=>ea({recording:a,deps:t,targetLabel:"iOS recording"}),{outPath:a.outPath,showTouches:a.showTouches,gestureEventCount:a.gestureEvents.length}),null}function eJ(e,t,r){let a=er(e,t,r);return function(e){try{i.rmSync(e,{force:!0})}catch{}}(t.outPath),p("COMMAND_FAILED",a.message)}async function eX(e){var t;let r,a,{req:i,activeSession:n,device:s,logPath:d,deps:c}=e;if(!n.recording)return p("INVALID_ARGS","no active recording");let l=n.recording,u=Date.now(),m=l.invalidatedReason;n.recording=void 0;let f="ios-device-runner"===l.platform?await eE({req:i,activeSession:n,device:s,logPath:d,deps:c,recording:l}):"macos-runner"===l.platform?await ek({req:i,activeSession:n,device:s,logPath:d,deps:c,recording:l}):await eB({deps:c,device:s,recording:l,stopRequestedAt:u});if(f)return f;if(m&&"ios"===l.platform&&l.showTouches)l.overlayWarning??=`overlay unavailable: ${m}`;else if(m)return p("COMMAND_FAILED",m);return r="android"===(t=l).platform?t.chunks:void 0,a=[{field:"outPath",path:t.outPath,localPath:t.clientOutPath,fileName:o.basename(t.clientOutPath??t.outPath)}],r&&r.length>1&&a.push(...r.slice(1).map(e=>({field:"chunkPath",path:e.path,localPath:eZ(t,e.index),fileName:o.basename(eZ(t,e.index)??e.path)}))),t.telemetryPath&&a.push({field:"telemetryPath",path:t.telemetryPath,localPath:function(e){if(e.clientOutPath)return j(e.clientOutPath)}(t),fileName:o.basename(t.telemetryPath)}),{ok:!0,data:{recording:"stopped",outPath:t.outPath,telemetryPath:t.telemetryPath,artifacts:a,showTouches:t.showTouches,warning:t.warning,overlayWarning:t.overlayWarning,chunks:r?.map(e=>({index:e.index,path:eZ(t,e.index)??e.path}))}}}function eZ(e,t){if("android"===e.platform&&e.clientOutPath)return ei(e.clientOutPath,t)}function eQ(e,t,r,a={}){r.recordOnlySession&&(a.writeLog&&e.writeSessionLog(r),e.delete(t))}async function eY(e){let{req:t,sessionName:r,sessionStore:a,logPath:i}=e,o={runCmd:async(e,t,r)=>"xcrun"===e?await u(t,r):await l(e,t,r),startIosSimulatorRecording:e=>D().startIosSimulatorRecording(e),runIosRunnerCommand:f,waitForRecordingTail:eV,waitForStableFile:z,isPlayableVideo:q,trimRecordingStart:Z,resizeRecording:Y,overlayRecordingTouches:Q},n=a.get(r),s=n?.device??await v(t.flags??{});n||await g(s);let d=n??{name:y(t),sessionScope:_(t),device:s,createdAt:Date.now(),recordOnlySession:!0,actions:[]},c=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(c))return p("INVALID_ARGS","record requires start|stop");if("start"===c)return eW({req:t,sessionName:r,sessionStore:a,activeSession:d,device:s,logPath:i,deps:o});let m=await eX({req:t,activeSession:d,device:s,logPath:i,deps:o});return m.ok?(a.recordAction(d,{command:t.command,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:m.data?.outPath,showTouches:m.data?.showTouches}}),eQ(a,r,d,{writeLog:!0})):eQ(a,r,d),m}export{eY as handleRecordCommand};
@@ -1,26 +1 @@
1
- let e,t,r,a;import{__webpack_require__ as i}from"./rslib-runtime.js";import o from"node:fs";import n from"node:path";import{createHash as s}from"node:crypto";import c from"node:os";import{setTimeout as d}from"node:timers/promises";import{fileURLToPath as l}from"node:url";import{AppError as u}from"./9152.js";import{runCmd as m}from"./9818.js";import{sleep as f}from"./4829.js";import{withDiagnosticTimer as p,emitDiagnostic as h}from"./7599.js";import{androidDeviceForSerial as w,runAndroidAdb as g}from"./8806.js";import{pullAndroidAdbFile as y}from"./9639.js";import{runXcrun as _,SessionStore as v,runIosRunnerCommand as P,errorResponse as S,isCommandSupportedOnDevice as A,ensureDeviceReady as b,resolvePublicSessionName as C,resolveImplicitSessionScope as M,resolveTargetDevice as I,IOS_RUNNER_CONTAINER_BUNDLE_IDS as x}from"./2415.js";import{resolveRecordingProvider as D}from"./recording-provider.js";var R={};function $(e=process.env){let t=k(),r=n.join(t,"home"),a=n.join(t,"module-cache");return o.mkdirSync(r,{recursive:!0}),o.mkdirSync(a,{recursive:!0}),{...e,HOME:r,CLANG_MODULE_CACHE_PATH:a}}async function N(e){let t=o.statSync(e.sourcePath),r=o.readFileSync(e.sourcePath),a=T(e.cacheName??n.basename(e.sourcePath,n.extname(e.sourcePath))),i=U(["2",process.platform,process.arch,n.resolve(e.sourcePath),t.size,r]),s=n.join(k(),"bin",`${a}-${i}`);return await F({sourcePath:e.sourcePath,executablePath:s,timeoutMs:e.timeoutMs}),s}async function E(e){let t=T(e.cacheName),r=U(["2",process.platform,process.arch,e.source]),a=n.join(k(),"sources",`${t}-${r}.swift`),i=n.join(k(),"bin",`${t}-${r}`);return await F({sourcePath:a,executablePath:i,sourceText:e.source,timeoutMs:e.timeoutMs}),i}function k(){let e=process.env.AGENT_DEVICE_SWIFT_CACHE_DIR?.trim();return e?n.resolve(e):n.join(c.tmpdir(),"agent-device-swift-cache")}async function F(e){if(L(e.executablePath))return;let t=n.dirname(e.executablePath);o.mkdirSync(t,{recursive:!0});let r=`${e.executablePath}.lock`;if(!await O(r,e.executablePath,e.timeoutMs??12e4))return;let a=o.mkdtempSync(n.join(t,`.${n.basename(e.executablePath)}.${process.pid}.`)),i=n.join(a,n.basename(e.executablePath));try{if(L(e.executablePath))return;void 0===e.sourceText||o.existsSync(e.sourcePath)||(o.mkdirSync(n.dirname(e.sourcePath),{recursive:!0}),o.writeFileSync(e.sourcePath,e.sourceText)),await m("xcrun",["swiftc",e.sourcePath,"-o",i],{timeoutMs:e.timeoutMs??12e4,env:$()}),o.renameSync(i,e.executablePath)}finally{o.rmSync(a,{recursive:!0,force:!0}),o.rmSync(r,{recursive:!0,force:!0})}}async function O(e,t,r){let a=Date.now()+r;for(;;){if(L(t))return!1;try{return o.mkdirSync(e),!0}catch(t){if("EEXIST"!==t.code)throw t;if(function(e,t){try{return Date.now()-o.statSync(e).mtimeMs>=t}catch{return!1}}(e,r)){o.rmSync(e,{recursive:!0,force:!0});continue}if(Date.now()>=a)throw new u("COMMAND_FAILED",`Timed out waiting for Swift cache lock: ${e} (${r}ms)`,{lockDir:e,timeoutMs:r,hint:`Another agent-device process may still be compiling this Swift helper. Retry shortly; if no agent-device process is active, remove "${e}" and retry.`});await d(25)}}}function L(e){try{return o.accessSync(e,o.constants.X_OK),o.statSync(e).isFile()}catch{return!1}}function T(e){return e.replaceAll(/[^A-Za-z0-9._-]/g,"-").replaceAll(/^-+|-+$/g,"")||"swift-helper"}function U(e){let t=s("sha256");for(let r of e)t.update(Buffer.isBuffer(r)?r:String(r)),t.update("\0");return t.digest("hex").slice(0,16)}i.r(R),i.d(R,{handleRecordTraceCommands:()=>e1});let z=`
2
- import Foundation
3
- import AVFoundation
4
-
5
- let url = URL(fileURLWithPath: CommandLine.arguments[1])
6
- let asset = AVURLAsset(url: url)
7
- let semaphore = DispatchSemaphore(value: 0)
8
- var exitCode: Int32 = 1
9
-
10
- Task {
11
- defer { semaphore.signal() }
12
- do {
13
- let playable = try await asset.load(.isPlayable)
14
- let duration = try await asset.load(.duration)
15
- if playable && duration.isValid && !duration.isIndefinite && CMTimeGetSeconds(duration) > 0 {
16
- exitCode = 0
17
- }
18
- } catch {
19
- exitCode = 1
20
- }
21
- }
22
-
23
- semaphore.wait()
24
- exit(exitCode)
25
- `.trim();async function q(e,t={}){let r,a=t.pollMs??150,i=t.attempts??12,n=0;for(let t=0;t<i;t+=1){let t=0;try{t=o.statSync(e).size}catch{t=0}if(t>0&&t===r){if((n+=1)>=2)return}else n=0;r=t,await f(a)}}async function K(e){try{let t=await V(),r=await m(t,[e],{allowFailure:!0,timeoutMs:1e4,env:$()});if(0===r.exitCode)return!0;if(H(r.stderr,r.stdout))return j(e);return!1}catch(r){var t;if((t=r)instanceof u&&("TOOL_MISSING"===t.code||H(String(t.details?.stderr??""),String(t.details?.stdout??""))))return j(e);throw r}}async function V(){e??=E({source:z,cacheName:"video-validator",timeoutMs:3e4});try{return await e}catch(t){throw e=void 0,t}}async function G(e,t={}){let r=t.pollMs??150,a=t.attempts??12;for(let t=0;t<a;t+=1){if(await K(e))return;await f(r)}}function H(e,t){let r=`${e}
26
- ${t}`;return/\b(no such module ['"]AVFoundation['"]|unable to find utility ["']swiftc?["']|xcrun: error: unable to find utility ["']swiftc?["'])\b/i.test(r)}function j(e){try{let t=o.statSync(e);if(!t.isFile()||t.size<=0)return!1}catch{return!1}let t=function(e){try{let t=o.openSync(e,"r");try{let e=o.fstatSync(t).size,r=0,a=[];for(;r+8<=e&&a.length<16;){let e=Buffer.alloc(8);if(8>o.readSync(t,e,0,8,r))break;let i=e.readUInt32BE(0),n=e.toString("latin1",4,8);if(a.push(n),1===i){let e=Buffer.alloc(8);if(8>o.readSync(t,e,0,8,r+8))break;i=Number(e.readBigUInt64BE(0))}if(!Number.isFinite(i)||i<=0)break;r+=i}return a}finally{o.closeSync(t)}}catch{return[]}}(e);return t.includes("ftyp")&&t.includes("moov")}function B(e){let t=n.parse(e);return n.join(t.dir,`${t.name}.gesture-telemetry.json`)}function W(e){return[...e].sort((e,t)=>e.tMs-t.tMs)}function X(e){var t,r,a;let i,n,{recording:s,trimStartMs:c}=e,d=(i=B((t={videoPath:s.outPath,events:s.gestureEvents,trimStartMs:c}).videoPath),n={version:1,generatedAt:new Date().toISOString(),events:(r=t.events,(a=t.trimStartMs??0)>0?W(r.flatMap(e=>{let t=e.tMs-a,r="durationMs"in e?e.durationMs:void 0;return("number"==typeof r?t+r:t)<=0?[]:[{...e,tMs:Math.max(0,t)}]})):W(r))},o.writeFileSync(i,JSON.stringify(n,null,2)),i);return s.telemetryPath=d,d}function J(e){let t=n.dirname(l(import.meta.url)),r=[l(new URL(`./${e}`,import.meta.url)),n.resolve(t,`../../ios-runner/AgentDeviceRunner/RecordingScripts/${e}`),n.resolve(t,`../../../ios-runner/AgentDeviceRunner/RecordingScripts/${e}`),n.resolve(process.cwd(),`ios-runner/AgentDeviceRunner/RecordingScripts/${e}`)];for(let e of r)if(o.existsSync(e))return e;throw new u("COMMAND_FAILED",`Missing recording helper script: ${e}`,{hint:"Ensure ios-runner/AgentDeviceRunner/RecordingScripts is present in this checkout or bundled with the package.",scriptName:e,searchedPaths:r})}async function Z(e){var t;let r,a,{videoPath:i,scriptPath:s,scriptArgs:c,commandDescription:d}=e;await q(i),await G(i);let l=(t=i,r=n.parse(t),a=`${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,n.join(r.dir,`.${r.name}.agent-device-${a}${r.ext||".mp4"}`));try{let e=await N({sourcePath:s});await m(e,["--input",i,"--output",l,...c],{timeoutMs:12e4,env:$()}),await G(l),o.renameSync(l,i)}catch(t){let e=t instanceof u?t:new u("COMMAND_FAILED",String(t),void 0,t instanceof Error?t:void 0);throw new u("COMMAND_FAILED",d,{...e.details,videoPath:i,script:s},e)}finally{o.rmSync(l,{force:!0})}}async function Q(e){let{videoPath:t,trimStartMs:a}=e;a>0&&await Z({videoPath:t,scriptPath:r??=J("recording-trim.swift"),scriptArgs:["--trim-start-ms",String(a)],commandDescription:"Failed to trim the start of the iOS recording"})}async function Y(e){let{videoPath:r,telemetryPath:a,targetLabel:i="recording"}=e;await Z({videoPath:r,scriptPath:t??=J("recording-overlay.swift"),scriptArgs:["--events",a],commandDescription:`Failed to add touch overlays to the ${i}`})}async function ee(e){let{videoPath:t,quality:r,targetLabel:i="recording"}=e;await Z({videoPath:t,scriptPath:a??=J("recording-resize.swift"),scriptArgs:["--quality",String(r)],commandDescription:`Failed to resize the ${i}`})}function et(e){return e instanceof Error?e.message:String(e)}function er(e,t){return e.stderr.trim()||e.stdout.trim()||`${t} exited with code ${e.exitCode}`}function ea(e,t,r=Date.now()){let a=Math.max(0,r-t.startedAt);return a>=1e3?{message:e,tooShort:!1}:{message:`${e}. Recording stopped after ${Math.round(a)}ms; wait at least 1000ms between record start and record stop so the recorder can finalize a playable MP4`,tooShort:!0}}async function ei(e){let{recording:t,deps:r,trimStartMs:a,targetLabel:i}=e,o=X({recording:t,trimStartMs:a});if(!t.showTouches)return void h({level:"debug",phase:"record_stop_overlay_skipped",data:{reason:"hide_touches"}});if(0===t.gestureEvents.length)return void h({level:"debug",phase:"record_stop_overlay_skipped",data:{reason:"no_gesture_events"}});let n=function(e=process.platform){if("darwin"!==e)return"touch overlay burn-in is only available on macOS hosts; returning raw video plus gesture telemetry"}();if(n){t.overlayWarning??=n;return}try{await p("record_stop_overlay_export",()=>r.overlayRecordingTouches({videoPath:t.outPath,telemetryPath:o,targetLabel:i}),{targetLabel:i,gestureEventCount:t.gestureEvents.length})}catch(e){t.overlayWarning??=`failed to overlay recording touches: ${et(e)}`}}function eo(e,t){if(1===t)return e;let r=n.parse(e),a=r.ext||".mp4";return n.join(r.dir,`${r.name}.part-${String(t).padStart(3,"0")}${a}`)}function en(e){return e.chunks??=[{index:1,path:e.outPath,remotePath:e.remotePath}],e.chunks}async function es(e){let{recording:t,startNextChunk:r,finishCurrentChunk:a}=e;if(t.stopping)return;let i=await a();if(i)throw Error(i);if(t.stopping)return;let o=en(t),s=o.length+1,c=await r(n.posix.dirname(t.remotePath));t.remotePath=c.remotePath,t.remotePid=c.remotePid,o.push({index:s,path:eo(t.outPath,s),remotePath:c.remotePath}),t.warning??="Android adb screenrecord is capped at 180s, so this recording was split into multiple MP4 chunks."}async function ec(e){let{recording:t,deps:r}=e;en(t).length<=1?await ei({recording:t,deps:r,targetLabel:"Android recording"}):(X({recording:t}),t.showTouches&&t.gestureEvents.length>0&&(t.overlayWarning??="touch overlay burn-in is skipped for chunked Android recordings; returning raw chunks plus gesture telemetry"))}async function ed(e){for(let t of e.chunks){let r=await el({deps:e.deps,deviceId:e.deviceId,remotePath:t.remotePath,outPath:t.path});if(r)return`failed to copy recording chunk ${t.index}: ${r}`}}async function el(e){let t,{deps:r,deviceId:a,remotePath:i,outPath:n}=e;for(let e=0;e<2;e+=1){eu(n);let s=w(a),c=await y(i,n,{allowFailure:!0,device:s});if(0!==c.exitCode)t=er(c,"adb pull");else{await r.waitForStableFile(n,{pollMs:250,attempts:20});let t=await r.isPlayableVideo(n);if(h({level:"debug",phase:"record_stop_android_pull_validation",data:{deviceId:a,remotePath:i,outPath:n,attempt:e+1,fileSize:function(e){try{return o.statSync(e).size}catch{return 0}}(n),playable:t}}),t)return;h({level:"warn",phase:"record_stop_android_invalid_video_retry",data:{deviceId:a,remotePath:i,outPath:n,attempt:e+1}})}e<1&&await f(750)}return t?`failed to copy recording from device: ${t}`:(eu(n),"failed to copy recording from device: pulled file is not a playable MP4")}function eu(e){try{o.rmSync(e,{force:!0})}catch{}}async function em(e,t,r){return await g(w(e),t,r)}async function ef(e,t){let r=await em(e,["shell","ps","-o","pid=","-p",t],{allowFailure:!0});return 0===r.exitCode&&r.stdout.split(/\s+/).map(e=>e.trim()).includes(t)}async function ep(e,t){for(let r=0;r<40;r+=1){if(!await ef(e,t))return!0;await f(250)}return!await ef(e,t)}async function eh(e,t){let r,a=0;for(let i=0;i<20;i+=1){let i=await em(e,["shell","stat","-c","%s",t],{allowFailure:!0}),o=0===i.exitCode?i.stdout.trim():"";if(o.length>0&&o===r){if((a+=1)>=4)return}else a=0;r=o,await f(250)}}async function ew(e,t,r){for(let a=0;a<8;a+=1){let i=await em(e,["shell","stat","-c","%s",t],{allowFailure:!0}),o=0===i.exitCode?Number(i.stdout.trim()):NaN;if(Number.isFinite(o)&&o>0)return!0;if(!await ef(e,r))break;if(a+1>=2)return!0;await f(250)}return!1}async function eg(e){let{deviceId:t,quality:r}=e;if(void 0===r||r>=10)return;let a=await em(t,["shell","wm","size"],{allowFailure:!0}),i=a.stdout.match(/Override size:\s*(\d+)x(\d+)/)??a.stdout.match(/Physical size:\s*(\d+)x(\d+)/);if(0!==a.exitCode||!i)throw Error(`failed to resolve Android screen size for recording quality: ${er(a,"adb shell wm size")}`);return{width:ey(Number(i[1]),r),height:ey(Number(i[2]),r)}}function ey(e,t){return Math.max(2,2*Math.round(e*t/10/2))}async function e_(e,t){await em(e,["shell","rm","-f",t],{allowFailure:!0})}async function ev(e,t){let r=await em(e,["shell","kill","-9",t],{allowFailure:!0});return h({level:"warn",phase:"record_stop_android_force_signal",data:{deviceId:e,remotePid:t,exitCode:r.exitCode,stdout:r.stdout.trim(),stderr:r.stderr.trim()}}),!(0!==r.exitCode&&await ef(e,t))&&await ep(e,t)}async function eP(e){var t;let r,a,{device:i,recordingSize:o,preferredRemoteDir:n}=e,s="failed to start recording: Android screenrecord did not begin producing frames";for(let e of(t=Date.now(),r=`agent-device-recording-${t}.mp4`,a=["/sdcard","/data/local/tmp"],(n&&a.includes(n)?[n,...a.filter(e=>e!==n)]:a).map(e=>`${e}/${r}`))){let t=await em(i.id,["shell",function(e,t){let r=["screenrecord"];return t&&r.push("--size",`${t.width}x${t.height}`),r.push(e),`${r.join(" ")} >/dev/null 2>&1 & echo $!`}(e,o)],{allowFailure:!0});if(0!==t.exitCode){s=`failed to start recording: ${er(t,"adb shell screenrecord")}`;continue}let r=t.stdout.split(/\r?\n/).map(e=>e.trim()).filter(e=>/^\d+$/.test(e)).at(-1);if(!r){s="failed to start recording: adb did not return a valid Android screenrecord pid",await e_(i.id,e);continue}if(h({level:"debug",phase:"record_start_android_started",data:{deviceId:i.id,remotePath:e,remotePid:r}}),await ew(i.id,e,r))return{remotePath:e,remotePid:r,startedAt:Date.now()};s="failed to start recording: Android screenrecord did not begin producing frames",await ev(i.id,r),await e_(i.id,e)}return{error:S("COMMAND_FAILED",s)}}async function eS(e){let t,{device:r,recordingBase:a}=e;try{t=await eg({deviceId:r.id,quality:a.quality})}catch(e){return S("COMMAND_FAILED",e instanceof Error?e.message:String(e))}let i=await eP({device:r,recordingSize:t});if("error"in i)return i.error;let o={platform:"android",remotePath:i.remotePath,remotePid:i.remotePid,chunks:[{index:1,path:a.outPath,remotePath:i.remotePath}],...a,startedAt:i.startedAt};return!function e(t){let{recording:r,startNextChunk:a,finishCurrentChunk:i}=t,o=setTimeout(()=>{r.rotationPromise=es({recording:r,startNextChunk:a,finishCurrentChunk:i}).catch(e=>{r.rotationFailedReason=e instanceof Error?e.message:String(e)}).finally(()=>{r.rotationPromise=void 0,r.stopping||r.rotationFailedReason||e({recording:r,startNextChunk:a,finishCurrentChunk:i})})},17e4);o.unref?.(),r.rotationTimer=o}({recording:o,finishCurrentChunk:async()=>await eA({device:r,recording:o,waitForRemoteFileStability:!1}),startNextChunk:async e=>{let a=await eP({device:r,recordingSize:t,preferredRemoteDir:e});if("error"in a)throw Error(a.error.ok?"failed to start next Android recording chunk":a.error.error.message);return a}}),o}async function eA(e){let{device:t,recording:r,waitForRemoteFileStability:a=!0}=e;await ef(t.id,r.remotePid)||(r.warning??=function(e){if(!(Date.now()-e.startedAt<178e3))return"Android adb screenrecord stopped before record stop, likely after reaching the 180s platform limit. The MP4 may be truncated; final interactions after the limit are not in the video."}(r));let i=await em(t.id,["shell","kill","-2",r.remotePid],{allowFailure:!0});if(h({level:"debug",phase:"record_stop_android_signal",data:{deviceId:t.id,remotePath:r.remotePath,remotePid:r.remotePid,exitCode:i.exitCode,stdout:i.stdout.trim(),stderr:i.stderr.trim()}}),0!==i.exitCode)return await eb(t.id,r.remotePid,i);let o=await eC(t.id,r.remotePid);if(o)return o;a&&await eh(t.id,r.remotePath)}async function eb(e,t,r){if(await ef(e,t)&&!await ev(e,t))return`failed to stop recording: ${er(r,"adb shell kill")}`}async function eC(e,t){if(!await ep(e,t)&&!await ev(e,t))return`failed to stop recording: Android screenrecord pid ${t} did not exit`}async function eM(e){let t,{deps:r,device:a,recording:i,stopRequestedAt:o}=e;h({level:"debug",phase:"record_stop_android_enter",data:{deviceId:a.id,remotePath:i.remotePath,remotePid:i.remotePid}}),i.stopping=!0,i.rotationTimer&&(clearTimeout(i.rotationTimer),i.rotationTimer=void 0),await i.rotationPromise;let n=await eA({device:a,recording:i});if(i.rotationFailedReason&&!n&&(i.warning??=`Android recording chunk rotation failed: ${i.rotationFailedReason}`),!n){let e=await ed({deps:r,deviceId:a.id,chunks:en(i)});if(e)return await s(),S("COMMAND_FAILED",eI(e,i,o));await ec({recording:i,deps:r})}if(await s(),n)return S("COMMAND_FAILED",eI(n,i,o));if(t)return S("COMMAND_FAILED",t);return null;async function s(){for(let e of en(i)){let r=await em(a.id,["shell","rm","-f",e.remotePath],{allowFailure:!0});h({level:"debug",phase:"record_stop_android_cleanup",data:{deviceId:a.id,remotePath:e.remotePath,exitCode:r.exitCode,stdout:r.stdout.trim(),stderr:r.stderr.trim()}}),0===r.exitCode||n||(t=`failed to clean up remote recording: ${er(r,"adb shell rm")}`)}}}function eI(e,t,r){return ea(e,t,r).message}function ex(e){let t=e.appBundleId?.trim();return t&&t.length>0?t:void 0}function eD(e,t,r){return{verbose:e.flags?.verbose,logPath:t,traceLogPath:r.trace?.outPath,requestId:e.meta?.requestId}}async function eR(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o}=e,n=ex(r);try{await o.runIosRunnerCommand(a,{command:"recordStop",appBundleId:n},eD(t,i,r))}catch(e){h({level:"warn",phase:"record_stop_runner_failed",data:{platform:a.platform,kind:a.kind,deviceId:a.id,session:r.name,error:et(e)}})}}async function e$(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o}=e,n=ex(r);if(n)try{await o.runIosRunnerCommand(a,{command:"snapshot",appBundleId:n,interactiveOnly:!0,compact:!0,depth:1},eD(t,i,r))}catch(e){h({level:"warn",phase:"record_start_simulator_runner_warm_failed",data:{deviceId:a.id,session:r.name,appBundleId:n,error:et(e)}})}}async function eN(e){let t,r,{req:a,activeSession:i,sessionStore:o,device:n,logPath:s,deps:c,fpsFlag:d,recordingBase:l,appBundleId:u}=e,m=`agent-device-recording-${Date.now()}.mp4`,f=`tmp/${m}`,p=eD(a,s,i),w=async()=>c.runIosRunnerCommand(n,{command:"recordStart",outPath:m,fps:d,quality:l.quality,appBundleId:u},p);try{let e=await w();t="number"==typeof e.recorderStartUptimeMs?e.recorderStartUptimeMs:void 0,r="number"==typeof e.targetAppReadyUptimeMs?e.targetAppReadyUptimeMs:void 0}catch(a){var g,y;if(!et(a).toLowerCase().includes("recording already in progress"))return S("COMMAND_FAILED",`failed to start recording: ${et(a)}`);h({level:"warn",phase:"record_start_runner_desynced",data:{platform:n.platform,kind:n.kind,deviceId:n.id,session:i.name,error:et(a)}});let e=(g=n.id,y=i.name,o.toArray().find(e=>e.name!==y&&"ios"===e.device.platform&&"device"===e.device.kind&&e.device.id===g&&e.recording?.platform==="ios-device-runner"));if(e)return S("COMMAND_FAILED",`failed to start recording: recording already in progress in session '${e.name}'`);try{await c.runIosRunnerCommand(n,{command:"recordStop",appBundleId:u},p)}catch{}try{let e=await w();t="number"==typeof e.recorderStartUptimeMs?e.recorderStartUptimeMs:void 0,r="number"==typeof e.targetAppReadyUptimeMs?e.targetAppReadyUptimeMs:void 0}catch(e){return S("COMMAND_FAILED",`failed to start recording: ${et(e)}`)}}return{platform:"ios-device-runner",remotePath:f,runnerStartedAtUptimeMs:t,targetAppReadyUptimeMs:r,...l}}async function eE(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,fpsFlag:n,recordingBase:s,appBundleId:c}=e;try{await o.runIosRunnerCommand(a,{command:"recordStart",outPath:s.outPath,fps:n,quality:s.quality,appBundleId:c},eD(t,i,r))}catch(e){return S("COMMAND_FAILED",`failed to start recording: ${et(e)}`)}return{platform:"macos-runner",...s}}async function ek(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,recording:n}=e;await eR({req:t,activeSession:r,device:a,logPath:i,deps:o});let s={stdout:"",stderr:"",exitCode:1};for(let e of x)if(0===(s=await o.runCmd("xcrun",["devicectl","device","copy","from","--device",a.id,"--source",n.remotePath,"--destination",n.outPath,"--domain-type","appDataContainer","--domain-identifier",e],{allowFailure:!0})).exitCode)break;if(0!==s.exitCode){let e=s.stderr.trim()||s.stdout.trim()||`devicectl exited with code ${s.exitCode}`;return S("COMMAND_FAILED",`failed to copy recording from device: ${e}`)}let c="number"!=typeof n.runnerStartedAtUptimeMs||"number"!=typeof n.targetAppReadyUptimeMs?0:Math.max(0,n.targetAppReadyUptimeMs-n.runnerStartedAtUptimeMs);return c>0&&await o.trimRecordingStart({videoPath:n.outPath,trimStartMs:c}),await ei({recording:n,deps:o,trimStartMs:c,targetLabel:"iOS recording"}),null}async function eF(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,recording:n}=e;return await eR({req:t,activeSession:r,device:a,logPath:i,deps:o}),await ei({recording:n,deps:o,targetLabel:"macOS recording"}),null}async function eO(e){let{deps:t,recording:r}=e;r.child.kill("SIGINT");let a=await eL(r.wait,5e3);return a||(await eT(t,r,"SIGINT"),(a=await eL(r.wait,2e3))||(r.child.kill("SIGTERM"),await eT(t,r,"SIGTERM"),a=await eL(r.wait,2e3)))?a:(r.child.kill("SIGKILL"),await eT(t,r,"SIGKILL"),await eL(r.wait,2e3))}async function eL(e,t){return await Promise.race([e,f(t).then(()=>null)])}async function eT(e,t,r){await ez(e,t,r)||await eU(e,t.outPath,r)}async function eU(e,t,r){let a,i=`simctl.*recordVideo.*${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}`;try{a=await e.runCmd("pgrep",["-f",i],{allowFailure:!0})}catch(e){h({level:"warn",phase:"record_stop_ios_simulator_pgrep_failed",data:{outPath:t,signal:r,error:et(e)}});return}let o=eK(eG(a.stdout)),n=eV(o,r);h({level:n>0?"warn":"debug",phase:"record_stop_ios_simulator_signal_recorders",data:{outPath:t,signal:r,matchedPidCount:o.length,signaled:n,pgrepExitCode:a.exitCode}})}async function ez(e,t,r){let a=t.recorderPid??t.child.pid;if("number"!=typeof a||!Number.isInteger(a)||a<=0)return h({level:"debug",phase:"record_stop_ios_simulator_owned_recorder_unavailable",data:{outPath:t.outPath,signal:r,reason:"missing_recorder_pid"}}),!1;let i=await eq(e,a,t.outPath,r),o=eK([a,...i.pids]),n=eV(o,r);return h({level:n>0?"warn":"debug",phase:"record_stop_ios_simulator_signal_owned_recorder",data:{outPath:t.outPath,signal:r,recorderPid:a,childPidCount:i.pids.length,matchedPidCount:o.length,signaled:n,pgrepExitCode:i.exitCode}}),n>0}async function eq(e,t,r,a){let i;try{i=await e.runCmd("pgrep",["-P",String(t)],{allowFailure:!0})}catch(e){return h({level:"warn",phase:"record_stop_ios_simulator_owned_pgrep_failed",data:{outPath:r,signal:a,parentPid:t,error:et(e)}}),{pids:[]}}return{pids:eG(i.stdout),exitCode:i.exitCode}}function eK(e){return Array.from(new Set(e)).filter(e=>Number.isInteger(e)&&e>0&&e!==process.pid)}function eV(e,t){let r=0;for(let a of e)try{process.kill(a,t),r+=1}catch{}return r}function eG(e){return e.split(/\s+/).map(e=>Number(e)).filter(e=>Number.isInteger(e)&&e>0)}async function eH(e){"ios"!==e.platform||0!==e.gestureEvents.length&&await f(350)}async function ej(e){for(let t=0;t<2;t+=1){try{if(o.statSync(e).size>0)return Date.now()}catch{}if(t+1>=2)break;await f(250)}return Date.now()}async function eB(e){let t,r,{req:a,activeSession:i,device:o,logPath:n,deps:s,recordingBase:c,resolvedOut:d}=e;c.showTouches&&await e$({req:a,activeSession:i,device:o,logPath:n,deps:s});let{child:l,wait:u}=s.startIosSimulatorRecording({device:o,outPath:d}),m=await ej(d);if(c.showTouches)try{let e=Date.now(),c=await s.runIosRunnerCommand(o,{command:"uptime",appBundleId:ex(i)},eD(a,n,i)),d=Date.now();t=Math.round((e+d)/2),r="number"==typeof c.currentUptimeMs?c.currentUptimeMs:void 0}catch{}return{platform:"ios",child:l,wait:u,...c,recorderPid:l.pid,startedAt:m,gestureClockOriginAtMs:void 0===r?void 0:t,gestureClockOriginUptimeMs:r}}async function eW(e){let t,{req:r,sessionName:a,sessionStore:i,activeSession:s,device:c,logPath:d,deps:l}=e;if(s.recording)return S("INVALID_ARGS","recording already in progress");let u=r.flags?.fps,m=r.flags?.quality;if(void 0!==u&&(!Number.isInteger(u)||u<1||u>120))return S("INVALID_ARGS","fps must be an integer between 1 and 120");if(void 0!==m&&(!Number.isInteger(m)||m<5||m>10))return S("INVALID_ARGS","quality must be an integer between 5 and 10");if(!A("record",c))return S("UNSUPPORTED_OPERATION","record is not supported on this device");let f=r.positionals?.[1]??`./recording-${Date.now()}.mp4`,p=v.expandHome(f,r.meta?.cwd),h={outPath:p,clientOutPath:r.meta?.clientArtifactPaths?.outPath,startedAt:Date.now(),quality:r.flags?.quality,showTouches:r.flags?.hideTouches!==!0,gestureEvents:[]};if(o.mkdirSync(n.dirname(p),{recursive:!0}),o.rmSync(p,{force:!0}),"ios"===c.platform&&"device"===c.kind){let e=ex(s);if(!e)return S("INVALID_ARGS","record on physical iOS devices requires an active app session; run open <app> first");t=await eN({req:r,activeSession:s,sessionStore:i,device:c,logPath:d,deps:l,fpsFlag:u,recordingBase:h,appBundleId:e})}else if("macos"===c.platform){let e=ex(s);if(!e)return S("INVALID_ARGS","record on macOS requires an active app session; run open <app> first");t=await eE({req:r,activeSession:s,device:c,logPath:d,deps:l,fpsFlag:u,recordingBase:h,appBundleId:e})}else t="ios"===c.platform?await eB({req:r,activeSession:s,device:c,logPath:d,deps:l,recordingBase:h,resolvedOut:p}):await eS({device:c,recordingBase:h});if("ok"in t)return t;s.recording=t,i.set(a,s);let w=i.ensureSessionDir(a);return i.recordAction(s,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:{action:"start",showTouches:t.showTouches}}),{ok:!0,data:{recording:"started",outPath:t.clientOutPath??f,sessionStateDir:w,showTouches:t.showTouches}}}async function eX(e){let{deps:t,device:r,recording:a,stopRequestedAt:i}=e;if("android"===a.platform)return await eM({deps:t,device:r,recording:a,stopRequestedAt:i});await p("record_stop_tail_settle",()=>t.waitForRecordingTail(a),{platform:a.platform,gestureEventCount:a.gestureEvents.length});let o=await p("record_stop_ios_simulator_process",()=>eO({deps:t,recording:a}),{outPath:a.outPath});if(!o)return eJ("failed to stop recording: simctl recordVideo did not exit after 5000ms and forced cleanup",a,i);if(0!==o.exitCode)return eJ(`failed to stop recording: ${er(o,"simctl recordVideo")}`,a,i);if(await p("record_stop_video_stable",()=>t.waitForStableFile(a.outPath,{pollMs:150,attempts:12}),{outPath:a.outPath}),!await p("record_stop_video_playable_check",()=>t.isPlayableVideo(a.outPath),{outPath:a.outPath}))return eJ(`failed to stop recording: ${a.outPath} was not finalized into a playable video`,a,i);if(void 0!==a.quality&&a.quality<10){let e=a.quality;try{await p("record_stop_resize",()=>t.resizeRecording({videoPath:a.outPath,quality:e,targetLabel:"iOS recording"}),{outPath:a.outPath,quality:e})}catch(e){a.overlayWarning=`failed to resize recording: ${et(e)}`}}return await p("record_stop_finalize_overlay",()=>ei({recording:a,deps:t,targetLabel:"iOS recording"}),{outPath:a.outPath,showTouches:a.showTouches,gestureEventCount:a.gestureEvents.length}),null}function eJ(e,t,r){let a=ea(e,t,r);return function(e){try{o.rmSync(e,{force:!0})}catch{}}(t.outPath),S("COMMAND_FAILED",a.message)}async function eZ(e){var t;let r,a,{req:i,activeSession:o,device:s,logPath:c,deps:d}=e;if(!o.recording)return S("INVALID_ARGS","no active recording");let l=o.recording,u=Date.now(),m=l.invalidatedReason;o.recording=void 0;let f="ios-device-runner"===l.platform?await ek({req:i,activeSession:o,device:s,logPath:c,deps:d,recording:l}):"macos-runner"===l.platform?await eF({req:i,activeSession:o,device:s,logPath:c,deps:d,recording:l}):await eX({deps:d,device:s,recording:l,stopRequestedAt:u});if(f)return f;if(m&&"ios"===l.platform&&l.showTouches)l.overlayWarning??=`overlay unavailable: ${m}`;else if(m)return S("COMMAND_FAILED",m);return r="android"===(t=l).platform?t.chunks:void 0,a=[{field:"outPath",path:t.outPath,localPath:t.clientOutPath,fileName:n.basename(t.clientOutPath??t.outPath)}],r&&r.length>1&&a.push(...r.slice(1).map(e=>({field:"chunkPath",path:e.path,localPath:eQ(t,e.index),fileName:n.basename(eQ(t,e.index)??e.path)}))),t.telemetryPath&&a.push({field:"telemetryPath",path:t.telemetryPath,localPath:function(e){if(e.clientOutPath)return B(e.clientOutPath)}(t),fileName:n.basename(t.telemetryPath)}),{ok:!0,data:{recording:"stopped",outPath:t.outPath,telemetryPath:t.telemetryPath,artifacts:a,showTouches:t.showTouches,warning:t.warning,overlayWarning:t.overlayWarning,chunks:r?.map(e=>({index:e.index,path:eQ(t,e.index)??e.path}))}}}function eQ(e,t){if("android"===e.platform&&e.clientOutPath)return eo(e.clientOutPath,t)}function eY(e,t,r,a={}){r.recordOnlySession&&(a.writeLog&&e.writeSessionLog(r),e.delete(t))}async function e0(e){let{req:t,sessionName:r,sessionStore:a,logPath:i}=e,o={runCmd:async(e,t,r)=>"xcrun"===e?await _(t,r):await m(e,t,r),startIosSimulatorRecording:e=>D().startIosSimulatorRecording(e),runIosRunnerCommand:P,waitForRecordingTail:eH,waitForStableFile:q,isPlayableVideo:K,trimRecordingStart:Q,resizeRecording:ee,overlayRecordingTouches:Y},n=a.get(r),s=n?.device??await I(t.flags??{});n||await b(s);let c=n??{name:C(t),sessionScope:M(t),device:s,createdAt:Date.now(),recordOnlySession:!0,actions:[]},d=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(d))return S("INVALID_ARGS","record requires start|stop");if("start"===d)return eW({req:t,sessionName:r,sessionStore:a,activeSession:c,device:s,logPath:i,deps:o});let l=await eZ({req:t,activeSession:c,device:s,logPath:i,deps:o});return l.ok?(a.recordAction(c,{command:t.command,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:l.data?.outPath,showTouches:l.data?.showTouches}}),eY(a,r,c,{writeLog:!0})):eY(a,r,c),l}async function e1(e){let{req:t,sessionName:r,sessionStore:a,logPath:i}=e,s=t.command;if("record"===s)return e0({req:t,sessionName:r,sessionStore:a,logPath:i});if("trace"===s){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return S("INVALID_ARGS","trace requires start|stop");let i=a.get(r);if(!i)return S("SESSION_NOT_FOUND","No active session");if("start"===e){if(i.trace)return S("INVALID_ARGS","trace already in progress");let e=t.positionals?.[1]??a.defaultTracePath(i),r=v.expandHome(e);return o.mkdirSync(n.dirname(r),{recursive:!0}),o.appendFileSync(r,""),i.trace={outPath:r,startedAt:Date.now()},a.recordAction(i,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start",outPath:r}}),{ok:!0,data:{trace:"started",outPath:r}}}if(!i.trace)return S("INVALID_ARGS","no active trace");let c=i.trace.outPath;if(t.positionals?.[1]){let e=v.expandHome(t.positionals[1]);o.mkdirSync(n.dirname(e),{recursive:!0}),o.existsSync(c)?o.renameSync(c,e):o.appendFileSync(e,""),c=e}return i.trace=void 0,a.recordAction(i,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:c}}),{ok:!0,data:{trace:"stopped",outPath:c}}}return null}export{R as record_trace_namespaceObject};
1
+ import t from"node:fs";import e from"node:path";import{SessionStore as r,errorResponse as a}from"./2415.js";import{handleRecordCommand as o}from"./record-trace-recording.js";async function i(i){let{req:n,sessionName:s,sessionStore:c,logPath:l}=i,d=n.command;if("record"===d)return o({req:n,sessionName:s,sessionStore:c,logPath:l});if("trace"===d){let o=(n.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(o))return a("INVALID_ARGS","trace requires start|stop");let i=c.get(s);if(!i)return a("SESSION_NOT_FOUND","No active session");if("start"===o){if(i.trace)return a("INVALID_ARGS","trace already in progress");let o=n.positionals?.[1]??c.defaultTracePath(i),s=r.expandHome(o);return t.mkdirSync(e.dirname(s),{recursive:!0}),t.appendFileSync(s,""),i.trace={outPath:s,startedAt:Date.now()},c.recordAction(i,{command:d,positionals:n.positionals??[],flags:n.flags??{},result:{action:"start",outPath:s}}),{ok:!0,data:{trace:"started",outPath:s}}}if(!i.trace)return a("INVALID_ARGS","no active trace");let l=i.trace.outPath;if(n.positionals?.[1]){let a=r.expandHome(n.positionals[1]);t.mkdirSync(e.dirname(a),{recursive:!0}),t.existsSync(l)?t.renameSync(l,a):t.appendFileSync(a,""),l=a}return i.trace=void 0,c.recordAction(i,{command:d,positionals:n.positionals??[],flags:n.flags??{},result:{action:"stop",outPath:l}}),{ok:!0,data:{trace:"stopped",outPath:l}}}return null}export{i as handleRecordTraceCommands};
@@ -1,6 +1,28 @@
1
+ declare const DAEMON_SERVER_MODES: readonly ["socket", "http", "dual"];
2
+
3
+ declare const DAEMON_TRANSPORT_PREFERENCES: readonly ["auto", "socket", "http"];
4
+
5
+ declare type DaemonServerMode = (typeof DAEMON_SERVER_MODES)[number];
6
+
7
+ declare type DaemonTransportPreference = (typeof DAEMON_TRANSPORT_PREFERENCES)[number];
8
+
9
+ declare const DEVICE_TARGETS: readonly ["mobile", "tv", "desktop"];
10
+
11
+ declare type DeviceTarget = (typeof DEVICE_TARGETS)[number];
12
+
13
+ declare const LEASE_BACKENDS: readonly ["ios-simulator", "ios-instance", "android-instance"];
14
+
15
+ declare type LeaseBackend = (typeof LEASE_BACKENDS)[number];
16
+
17
+ declare type MetroPrepareKind = 'auto' | 'react-native' | 'expo';
18
+
19
+ declare const PLATFORM_SELECTORS: readonly ["ios", "macos", "android", "linux", "apple"];
20
+
21
+ declare type PlatformSelector = (typeof PLATFORM_SELECTORS)[number];
22
+
1
23
  declare type RemoteConfigMetroOptions = {
2
24
  metroProjectRoot?: string;
3
- metroKind?: 'auto' | 'react-native' | 'expo';
25
+ metroKind?: MetroPrepareKind;
4
26
  metroPublicBaseUrl?: string;
5
27
  metroProxyBaseUrl?: string;
6
28
  metroBearerToken?: string;
@@ -18,15 +40,15 @@ export declare type RemoteConfigProfile = RemoteConfigMetroOptions & {
18
40
  stateDir?: string;
19
41
  daemonBaseUrl?: string;
20
42
  daemonAuthToken?: string;
21
- daemonTransport?: 'auto' | 'socket' | 'http';
22
- daemonServerMode?: 'socket' | 'http' | 'dual';
43
+ daemonTransport?: DaemonTransportPreference;
44
+ daemonServerMode?: DaemonServerMode;
23
45
  tenant?: string;
24
- sessionIsolation?: 'none' | 'tenant';
46
+ sessionIsolation?: SessionIsolationMode;
25
47
  runId?: string;
26
48
  leaseId?: string;
27
- leaseBackend?: 'ios-simulator' | 'ios-instance' | 'android-instance';
28
- platform?: 'ios' | 'macos' | 'android' | 'linux' | 'apple';
29
- target?: 'mobile' | 'tv' | 'desktop';
49
+ leaseBackend?: LeaseBackend;
50
+ platform?: PlatformSelector;
51
+ target?: DeviceTarget;
30
52
  device?: string;
31
53
  udid?: string;
32
54
  serial?: string;
@@ -50,4 +72,8 @@ export declare function resolveRemoteConfigPath(options: RemoteConfigProfileOpti
50
72
 
51
73
  export declare function resolveRemoteConfigProfile(options: RemoteConfigProfileOptions): ResolvedRemoteConfigProfile;
52
74
 
75
+ declare const SESSION_ISOLATION_MODES: readonly ["none", "tenant"];
76
+
77
+ declare type SessionIsolationMode = (typeof SESSION_ISOLATION_MODES)[number];
78
+
53
79
  export { }
@@ -1 +1 @@
1
- import{createDaemonRuntimeSessionStore as e,setSessionSnapshot as t,captureSnapshot as r,dispatchCommand as n,runIosRunnerCommand as s,isCommandSupportedOnDevice as o,errorResponse as i,ensureDeviceReady as a,isApplePlatform as l,stopIosRunnerSession as u,getActiveAndroidSnapshotFreshness as c,resolveRunnerAppBundleId as d,resolveTargetDevice as f,context_contextFromFlags as p}from"./2415.js";import{closeIosApp as m}from"./apps.js";import{emitDiagnostic as g}from"./7599.js";import{asAppError as h,normalizeError as y,AppError as w}from"./9152.js";import{evaluateIsPredicate as _,isSupportedPredicate as S,localCommandPolicy as x,resolveRectCenter as k,createAgentDevice as I}from"./9533.js";import{tryParseSelectorChain as v,extractNodeReadText as N,prefersValueForReadableText as A,findNodeByLabel as P,normalizeType as R,splitIsSelectorArgs as b}from"./940.js";import{getAndroidAppState as C}from"./8806.js";import{parseWaitPositionals as E}from"./6085.js";import{parseFindArgs as q}from"./7556.js";import{buildSnapshotPresentationKey as M,snapshotPresentationOptionsFromFlags as D}from"./4057.js";async function O(e,t,r){let n=e.get(t),s=n?.device??await f(r??{});return n||await a(s),{session:n,device:s}}async function $(e,t,r){let n=!e&&"ios"===t.platform;try{return await r()}finally{n&&(await u(t.id),await L(t))}}async function L(e){let t=d();await m(e,t).catch(r=>{g({level:"debug",phase:"ios_sessionless_runner_host_close_failed",data:{deviceId:e.id,bundleId:t,error:r instanceof Error?r.message:String(r)}})})}function U(e,t,r,n){t&&e.recordAction(t,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:n})}function K(e){let{session:t,sessionName:r,device:n,snapshot:s,appBundleId:o}=e;return t?{...t,snapshot:s,lastComparisonSafeSnapshot:s?.comparisonSafe===!0?s:t.lastComparisonSafeSnapshot}:{name:r,device:n,createdAt:Date.now(),appBundleId:o,snapshot:s,...s?.comparisonSafe===!0?{lastComparisonSafeSnapshot:s}:{},actions:[]}}function T(e,t={}){return{artifacts:function(e,t={}){let r=!0===t.plural?"do":"does";return{resolveInput:async()=>{throw new w("UNSUPPORTED_OPERATION",`${e} ${r} not resolve input artifacts`)},reserveOutput:async()=>{throw new w("UNSUPPORTED_OPERATION",`${e} ${r} not reserve output artifacts`)},createTempFile:async()=>{throw new w("UNSUPPORTED_OPERATION",`${e} ${r} not create temporary files`)}}}(e,t),policy:x()}}async function G(e){let{device:t,node:r,flags:s,appBundleId:o,traceOutPath:i,surface:a,contextFromFlags:l}=e,u=N(r),c=k(r.rect);if(!c||"ios"===t.platform&&u&&!A(r.type??""))return u;try{let e=await n(t,"read",[String(c.x),String(c.y)],void 0,{...l(s,o,i),surface:a}),d=e&&"object"==typeof e?e:void 0,f="string"==typeof d?.text?d.text:"";if(f.trim())return f;return g({level:"warn",phase:"interaction_read_fallback",data:{reason:"empty_backend_text",nodeRef:r.ref,surface:a,platform:t.platform}}),u}catch(e){return g({level:"warn",phase:"interaction_read_fallback",data:{reason:"backend_read_failed",nodeRef:r.ref,surface:a,platform:t.platform,error:e instanceof Error?e.message:String(e)}}),u}}let V=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"]];function F(e,t){let r=function(e){if(!e)return[];let t=[];for(let[r,n]of V)void 0!==e[r]&&t.push(n);return t}(t);return 0===r.length?null:i("INVALID_ARGS",`${e} @ref does not support ${r.join(", ")}.`)}async function B(e,t){let r=await j(e);if(r)throw new w("COMMAND_FAILED",`press ${t} left ${e.appBundleId} and foregrounded ${r.foregroundPackage}. The tap likely escaped the app.`,r)}async function j(e){var t;if("android"!==e.device.platform||!e.appBundleId)return null;let r=await C(e.device),n=r.package?.trim();return n&&n!==e.appBundleId&&("com.android.settings"===(t=n)||"com.android.systemui"===t||"com.google.android.permissioncontroller"===t||t.includes("launcher"))?{expectedPackage:e.appBundleId,foregroundPackage:n,activity:r.activity,hint:"com.google.android.permissioncontroller"===n?"Dismiss or allow the permission prompt, then retry the smoke assertion.":"Use screenshot as visual truth, then take a fresh snapshot -i before retrying."}:null}function H(e){return"COMMAND_FAILED"===e.code&&"string"==typeof e.details?.expectedPackage&&"string"==typeof e.details?.foregroundPackage}function W(e,t){let r=Array.isArray(e.selectorChain)?e.selectorChain:void 0,n=X(e),s=n?.kind==="ref"?Y(n.ref):void 0,o=n?.kind==="selector"?n.selector:void 0,i={...s?{ref:s}:{},...o?{selector:o}:{},...r?{selectorChain:r}:{}};if("attrs"===t)return i;let a="string"==typeof e.text?e.text:"";return{...i,text:a,refLabel:function(e){let t=e.trim();if(!(!t||t.length>80||/[\r\n]/.test(t)))return t}(a)}}function z(e){let t=X(e);return{...t?.kind==="ref"?{ref:Y(t.ref)}:{},...t?.kind==="selector"?{selector:t.selector}:{},..."string"==typeof e.text?{text:e.text}:{},...e.node&&"object"==typeof e.node?{node:e.node}:{}}}function J(e){let{selectorChain:t,...r}=e;return r}function Q(e,t,r,n){let s=e.get(t);s&&e.recordAction(s,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:n})}function X(e){let t=e.target;return t&&"object"==typeof t?"ref"===t.kind&&"string"==typeof t.ref?{kind:"ref",ref:t.ref}:"selector"===t.kind&&"string"==typeof t.selector?{kind:"selector",selector:t.selector}:void 0:void 0}function Y(e){return e.startsWith("@")?e.slice(1):e}let Z=["application","window","tabbar","scrollbar","image"],ee=new Set(["tab bar"]);async function et(e,t){var r;if(t.ok||(r=t.error.message,!/^wait timed out for (?:selector|text): /i.test(r)))return t;let n=await er(e).catch(()=>null);return n?i(t.error.code,`${t.error.message}. Current surface: ${n.summary}.`,{...t.error.details??{},currentSurface:n.details}):t}async function er(e){let t=[...(await r({device:e.device,session:e.session,flags:{...e.req.flags,snapshotInteractiveOnly:!0,snapshotCompact:!0},logPath:e.logPath??""})).snapshot.nodes].sort(es),n=en(t,6,{includeIdentifiers:!0});if(0===n.length)return null;let s=en(t.filter(e=>!ea(e)),4,{includeIdentifiers:!1}),o=en(t.filter(el),4,{includeIdentifiers:!0});return{summary:(s.length>0?s:n.slice(0,4)).join(", "),details:{labels:n,...o.length>0?{buttons:o}:{}}}}function en(e,t,r){let n=new Set,s=[];for(let o of e){let e=ei(o,r);if(!(!e||n.has(e))&&(n.add(e),s.push(e),s.length>=t))break}return s}function es(e,t){var r,n;return eo(e)-eo(t)||(r=e,n=t,r.rect&&n.rect?r.rect.y-n.rect.y||r.rect.x-n.rect.x:r.rect?-1:n.rect?1:(r.depth??0)-(n.depth??0)||r.index-n.index)}function eo(e){let t=!!ei(e,{includeIdentifiers:!1});return 2*!!ea(e)+ +!t}function ei(e,t){let r=(t.includeIdentifiers?[e.label,e.value,e.identifier]:[e.label,e.value]).map(e=>"string"==typeof e?e.trim():"").find(e=>e.length>0);return r?r.replace(/\s+/g," ").slice(0,80):""}function ea(e){let t=R(`${e.type??""} ${e.role??""} ${e.subrole??""}`),r=`${e.label??""} ${e.value??""}`.trim().toLowerCase();return Z.some(e=>t.includes(e))||ee.has(r)||r.endsWith(".fill")}function el(e){return R(`${e.type??""} ${e.role??""} ${e.subrole??""}`).includes("button")}function eu(e){var t;let{session:r,selectorExpression:n}=e;if(!r||"ios"!==r.device.platform||r.postGestureStabilization)return null;let s=v(n);if(!s||1!==s.selectors.length)return null;let o=s.selectors[0];if(!o||1!==o.terms.length)return null;let i=o.terms[0];return i&&"string"==typeof i.value&&("id"===(t=i.key)||"label"===t||"text"===t||"value"===t)?{key:i.key,value:i.value,raw:o.raw}:null}function ec(e,t={}){let r=h(e);if("ELEMENT_NOT_FOUND"===r.code)return!0===t.allowElementNotFound;if("COMMAND_FAILED"!==r.code)return!1;let n=r.message.toLowerCase();return n.includes("fetch failed")||n.includes("timed out")||n.includes("timeout")||n.includes("runner did not accept connection")||n.includes("invalid runner response")}async function ed(e){var t;let{req:r}=e;if("find"!==r.command)return null;let n=r.positionals??[];if(0===n.length)return i("INVALID_ARGS","find requires a locator or text");let s=q(n);if(!s.query)return i("INVALID_ARGS","find requires a value");if(r.flags?.findFirst&&r.flags?.findLast)return i("INVALID_ARGS","find accepts only one of --first or --last");let o=s.action;if("exists"!==(t=o)&&"wait"!==t&&"get_text"!==t&&"get_attrs"!==t)return null;let a=await eI(e,{requireSession:!1,capability:"find"});return a.ok?await eA(async()=>{let t=await a.runtime.selectors.find({session:e.sessionName,requestId:r.meta?.requestId,locator:s.locator,query:s.query,action:o,timeoutMs:s.timeoutMs});return Q(e.sessionStore,e.sessionName,r,function(e,t){if("exists"===t)return{found:!0};if("wait"===t)return{found:!0,waitedMs:e.waitedMs};let r="string"==typeof e.ref?e.ref:void 0;return"get_attrs"===t?{ref:r,action:"get attrs"}:{ref:r,action:"get text",text:"string"==typeof e.text?e.text:""}}(t,o)),"found"===t.kind?{found:!0,..."number"==typeof t.waitedMs?{waitedMs:t.waitedMs}:{}}:{..."string"==typeof t.ref?{ref:t.ref}:{},..."string"==typeof t.text?{text:t.text}:{},...t.node&&"object"==typeof t.node?{node:t.node}:{}}}):a.response}async function ef(e){let{req:t}=e;if("get"!==t.command)return null;let r=t.positionals?.[0];if("text"!==r&&"attrs"!==r)return i("INVALID_ARGS","get only supports text or attrs");let n=function(e){let t=e.positionals?.[1]??"";if(t.startsWith("@"))return{ok:!0,target:{kind:"ref",ref:t,fallbackLabel:e.positionals.length>2?e.positionals.slice(2).join(" ").trim():""}};let r=e.positionals?.slice(1).join(" ").trim()??"";return r?{ok:!0,target:{kind:"selector",selector:r}}:{ok:!1,response:i("INVALID_ARGS","get requires @ref or selector expression")}}(t);if(!n.ok)return n.response;if("ref"===n.target.kind){let e=F("get",t.flags);if(e)return e}if("selector"===n.target.kind){let t=await eg(e,r,n.target.selector);if(t)return t}let s=await eI(e,{requireSession:!0,capability:"get"});return s.ok?await eA(async()=>{let o=await s.runtime.selectors.get({session:e.sessionName,requestId:t.meta?.requestId,property:r,target:n.target});return Q(e.sessionStore,e.sessionName,t,W(o,r)),z(o)}):s.response}async function ep(e){let{req:t}=e;if("is"!==t.command)return null;let r=(t.positionals?.[0]??"").toLowerCase();if(!S(r))return i("INVALID_ARGS","is requires predicate: visible|hidden|exists|editable|selected|text");let{split:n}=b(t.positionals??[]);if(!n)return i("INVALID_ARGS","is requires a selector expression");let s=n.rest.join(" ").trim();if("text"===r&&!s)return i("INVALID_ARGS","is text requires expected text value");if("text"!==r&&n.rest.length>0)return i("INVALID_ARGS",`is ${r} does not accept trailing values`);let o=await eh(e,r,n.selectorExpression,s);if(o)return o;let a=await eI(e,{requireSession:!0,capability:"is"});if(!a.ok)return a.response;let l=await eA(async()=>{let o=await a.runtime.selectors.is({session:e.sessionName,requestId:t.meta?.requestId,predicate:r,selector:n.selectorExpression,expectedText:s});return Q(e.sessionStore,e.sessionName,t,o),J(o)});return await eP(e,l,`is ${r}`)}async function em(e){let{req:t,sessionName:r,sessionStore:n}=e,s=E(t.positionals??[]);if(!s)return i("INVALID_ARGS","wait requires a duration or text");let{session:a,device:l}=await O(n,r,t.flags);if("sleep"!==s.kind&&!o("wait",l))return i("UNSUPPORTED_OPERATION","wait is not supported on this device");if("selector"===s.kind){let t=await ey({...e,session:a,device:l,selectorExpression:s.selectorExpression,timeoutMs:s.timeoutMs});if(t)return t}let u=async()=>{let o=ek({...e,session:a,device:l}),i=await eA(async()=>{let e=await o.selectors.wait({session:r,requestId:t.meta?.requestId,target:function(e,t){if("sleep"===e.kind)return{kind:"sleep",durationMs:e.durationMs};if("selector"===e.kind)return{kind:"selector",selector:e.selectorExpression,timeoutMs:e.timeoutMs};if("ref"===e.kind){if(!t?.snapshot)throw new w("INVALID_ARGS","Ref wait requires an existing snapshot in session.");return{kind:"ref",ref:e.rawRef,timeoutMs:e.timeoutMs}}if(!e.text)throw new w("INVALID_ARGS","wait requires text");return{kind:"text",text:e.text,timeoutMs:e.timeoutMs}}(s,a)});return Q(n,r,t,e),{waitedMs:e.waitedMs,..."string"==typeof e.text?{text:e.text}:{},..."string"==typeof e.selector?{selector:e.selector}:{}}}),u=await et({req:t,logPath:e.logPath,session:a,device:l},i);return await eP(e,u,"wait")};return"sleep"===s.kind?await u():await $(a,l,u)}async function eg(e,t,r){let n=e.sessionStore.get(e.sessionName),s=eu({session:n,selectorExpression:r});if(!n||!s||"text"===t&&"id"!==s.key)return null;let o=await eS(e,n,s);if(ex(o))return o.response;if(!o)return null;let i=function(e,t,r){if(!r.found||!r.node)return null;let n={target:{kind:"selector",selector:t},node:r.node,selectorChain:[t]};return"attrs"===e?{kind:"attrs",...n}:"string"!=typeof r.text?null:{kind:"text",...n,text:r.text}}(t,s.raw,o);return i?(Q(e.sessionStore,e.sessionName,e.req,W(i,t)),{ok:!0,data:z(i)}):null}async function eh(e,t,r,n){var s,o,i,a,l;let u;if("hidden"===t)return null;let c=await ew(e,r);if(ex(c))return c.response;if(!c?.result.found||!c.result.node)return null;let d="exists"===t?{predicate:t,pass:!0,selector:c.selector.raw,matches:1,selectorChain:[c.selector.raw]}:(s=t,o=n,i=c.selector.raw,a=c.session,u=_({predicate:s,node:l=c.result.node,nodes:[l],expectedText:o,platform:a.device.platform}),{predicate:s,pass:u.pass,selector:i,..."text"===s?{text:u.actualText}:{},selectorChain:[i]});return d?(Q(e.sessionStore,e.sessionName,e.req,d),{ok:!0,data:J(d)}):null}async function ey(e){let t=eu({session:e.session,selectorExpression:e.selectorExpression});if(!e.session||!t)return null;let r=Date.now(),n=await eS(e,e.session,t);if(ex(n))return n.response;if(!n?.found)return null;let s={kind:"selector",selector:t.raw,waitedMs:Date.now()-r,selectorChain:[t.raw]};return Q(e.sessionStore,e.sessionName,e.req,s),await et({req:e.req,logPath:e.logPath,session:e.session,device:e.device},{ok:!0,data:s})}async function ew(e,t){let r=e.sessionStore.get(e.sessionName),n=eu({session:r,selectorExpression:t});if(!r||!n)return null;let s=await eS(e,r,n);return ex(s)?s:s?{session:r,selector:n,result:s}:null}async function e_(e,t,r){let n=await s(t.device,{command:"querySelector",selectorKey:r.key,selectorValue:r.value,appBundleId:t.appBundleId},{verbose:!!e.req.flags?.verbose,logPath:e.logPath,traceLogPath:t.trace?.outPath,requestId:e.req.meta?.requestId}),o=!0===n.found,i=function(e){let t=e.nodes;if(!Array.isArray(t))return;let r=t[0];if(r&&"object"==typeof r)return r}(n);return{found:o,..."string"==typeof n.text?{text:n.text}:{},...i?{node:i}:{}}}async function eS(e,t,r){try{return await e_(e,t,r)}catch(e){if(ec(e,{allowElementNotFound:!0}))return null;return{kind:"error",response:{ok:!1,error:y(e)}}}}function ex(e){return null!==e&&"kind"in e&&"error"===e.kind}function ek(n){return I({backend:function(e){let n,{req:s,session:o,device:i,logPath:a,sessionName:l,sessionStore:u}=e,d=0;return{platform:i.platform,captureSnapshot:async(e,f)=>{var p;let m,g={...s.flags,...(p=f,m={},p?.interactiveOnly!==void 0&&(m.snapshotInteractiveOnly=p.interactiveOnly),p?.compact!==void 0&&(m.snapshotCompact=p.compact),p?.scope!==void 0&&(m.snapshotScope=p.scope),p?.depth!==void 0&&(m.snapshotDepth=p.depth),p?.raw!==void 0&&(m.snapshotRaw=p.raw),m)},h=f?.scope??s.flags?.snapshotScope,y=Date.now(),w=M(D(g)),_="wait"===s.command||"find"===s.command;if(!_&&n&&y-d<750&&!c(o)&&!o?.postGestureStabilization)return n;if(!_&&o?.snapshot&&y-o.snapshot.createdAt<750&&o.snapshot.presentationKey===w&&!c(o)&&!o.postGestureStabilization)return d=o.snapshot.createdAt,n={snapshot:o.snapshot};let S=await r({device:i,session:o,flags:g,outPath:s.flags?.out,logPath:a??"",snapshotScope:h});return o&&(t(o,S.snapshot),u.set(l,o)),d=y,n={snapshot:S.snapshot}},readText:async(t,r)=>({text:await G({device:i,node:r,flags:s.flags,appBundleId:o?.appBundleId,traceOutPath:o?.trace?.outPath,surface:o?.surface,contextFromFlags:e.contextFromFlags??((e,t,r)=>p(a??"",e,t,r))})}),findText:async(t,r)=>({found:await ev(e,r)})}}(n),...T("selector commands",{plural:!0}),sessions:e({sessionName:n.sessionName,getSession:()=>n.session,recordOptions:{includeSnapshot:!0},setRecord:e=>{n.session&&e.snapshot&&(t(n.session,e.snapshot),n.sessionStore.set(n.sessionName,n.session))}})})}async function eI(e,t){let r=e.sessionStore.get(e.sessionName);if(!r&&t.requireSession)return{ok:!1,response:i("SESSION_NOT_FOUND","No active session. Run open first.")};let n=r?.device??await f(e.req.flags??{});return(r||await a(n),o(t.capability,n))?{ok:!0,runtime:ek({...e,session:r,device:n})}:{ok:!1,response:i("UNSUPPORTED_OPERATION",`${t.capability} is not supported on this device`)}}async function ev(e,t){let{device:r,session:n,req:o,logPath:i}=e;if("macos"===r.platform&&n?.surface&&"app"!==n.surface)return!!P((await eN(e)).nodes,t);if(l(r.platform)&&n?.appBundleId){let e=await s(r,{command:"findText",text:t,appBundleId:n?.appBundleId},{verbose:o.flags?.verbose,logPath:i,traceLogPath:n?.trace?.outPath,requestId:o.meta?.requestId});return e?.found===!0}return!!P((await eN(e)).nodes,t)}async function eN(e){let n=await r({device:e.device,session:e.session,flags:{...e.req.flags,snapshotInteractiveOnly:!1,snapshotCompact:!1},outPath:e.req.flags?.out,logPath:e.logPath??""});return e.session&&(t(e.session,n.snapshot),e.sessionStore.set(e.sessionName,e.session)),n.snapshot}async function eA(e){try{return{ok:!0,data:await e()}}catch(t){let e=h(t);return i(e.code,e.message,e.details)}}async function eP(e,t,r){var n;let s;if(t.ok)return t;let o=e.sessionStore.get(e.sessionName);if(!o)return t;try{s=await j(o)}catch{return t}return s?i(t.error.code,`${r} failed because ${"com.google.android.permissioncontroller"===(n=s).foregroundPackage?`Android permission dialog is blocking ${n.expectedPackage}`:`${n.foregroundPackage} is foreground instead of ${n.expectedPackage}`}.`,{...t.error.details??{},...s,blockedBy:"android_foreground_surface",originalMessage:t.error.message}):t}export{B as assertAndroidPressStayedInApp,K as buildSnapshotSession,T as createDaemonRuntimePolicy,ed as dispatchFindReadOnlyViaRuntime,ef as dispatchGetViaRuntime,ep as dispatchIsViaRuntime,em as dispatchWaitViaRuntime,H as isAndroidEscapeError,ec as isDirectIosSelectorFallbackError,eu as readSimpleIosSelectorTarget,G as readTextForNode,U as recordIfSession,F as refSnapshotFlagGuardResponse,O as resolveSessionDevice,$ as withSessionlessRunnerCleanup};
1
+ import{createDaemonRuntimeSessionStore as e,setSessionSnapshot as t,captureSnapshot as r,dispatchCommand as n,runIosRunnerCommand as s,isCommandSupportedOnDevice as o,errorResponse as i,ensureDeviceReady as a,stopIosRunnerSession as l,resolveRunnerAppBundleId as u,getActiveAndroidSnapshotFreshness as c,resolveTargetDevice as d,context_contextFromFlags as f}from"./2415.js";import{closeIosApp as p}from"./apps.js";import{emitDiagnostic as m}from"./7599.js";import{asAppError as g,normalizeError as h,AppError as y}from"./9152.js";import{evaluateIsPredicate as w,isSupportedPredicate as _,localCommandPolicy as S,createAgentDevice as x}from"./123.js";import{splitIsSelectorArgs as k,extractNodeReadText as I,prefersValueForReadableText as v,findNodeByLabel as N,normalizeType as A}from"./7847.js";import{resolveRectCenter as P}from"./1620.js";import{getAndroidAppState as R}from"./8806.js";import{parseWaitPositionals as b}from"./2284.js";import{buildSnapshotPresentationKey as C,isApplePlatform as E,snapshotPresentationOptionsFromFlags as q}from"./1644.js";import{parseFindArgs as M}from"./7556.js";import{tryParseSelectorChain as D}from"./8407.js";async function O(e,t,r){let n=e.get(t),s=n?.device??await d(r??{});return n||await a(s),{session:n,device:s}}async function $(e,t,r){let n=!e&&"ios"===t.platform;try{return await r()}finally{n&&(await l(t.id),await K(t))}}async function K(e){let t=u();await p(e,t).catch(r=>{m({level:"debug",phase:"ios_sessionless_runner_host_close_failed",data:{deviceId:e.id,bundleId:t,error:r instanceof Error?r.message:String(r)}})})}function L(e,t,r,n){t&&e.recordAction(t,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:n})}function U(e){let{session:t,sessionName:r,device:n,snapshot:s,appBundleId:o}=e;return t?{...t,snapshot:s,lastComparisonSafeSnapshot:s?.comparisonSafe===!0?s:t.lastComparisonSafeSnapshot}:{name:r,device:n,createdAt:Date.now(),appBundleId:o,snapshot:s,...s?.comparisonSafe===!0?{lastComparisonSafeSnapshot:s}:{},actions:[]}}function T(e,t={}){return{artifacts:function(e,t={}){let r=!0===t.plural?"do":"does";return{resolveInput:async()=>{throw new y("UNSUPPORTED_OPERATION",`${e} ${r} not resolve input artifacts`)},reserveOutput:async()=>{throw new y("UNSUPPORTED_OPERATION",`${e} ${r} not reserve output artifacts`)},createTempFile:async()=>{throw new y("UNSUPPORTED_OPERATION",`${e} ${r} not create temporary files`)}}}(e,t),policy:S()}}async function G(e){let{device:t,node:r,flags:s,appBundleId:o,traceOutPath:i,surface:a,contextFromFlags:l}=e,u=I(r),c=P(r.rect);if(!c||"ios"===t.platform&&u&&!v(r.type??""))return u;try{let e=await n(t,"read",[String(c.x),String(c.y)],void 0,{...l(s,o,i),surface:a}),d=e&&"object"==typeof e?e:void 0,f="string"==typeof d?.text?d.text:"";if(f.trim())return f;return m({level:"warn",phase:"interaction_read_fallback",data:{reason:"empty_backend_text",nodeRef:r.ref,surface:a,platform:t.platform}}),u}catch(e){return m({level:"warn",phase:"interaction_read_fallback",data:{reason:"backend_read_failed",nodeRef:r.ref,surface:a,platform:t.platform,error:e instanceof Error?e.message:String(e)}}),u}}let V=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"]];function F(e,t){let r=function(e){if(!e)return[];let t=[];for(let[r,n]of V)void 0!==e[r]&&t.push(n);return t}(t);return 0===r.length?null:i("INVALID_ARGS",`${e} @ref does not support ${r.join(", ")}.`)}async function B(e,t){let r=await H(e);if(r)throw new y("COMMAND_FAILED",`press ${t} left ${e.appBundleId} and foregrounded ${r.foregroundPackage}. The tap likely escaped the app.`,r)}async function H(e){var t;if("android"!==e.device.platform||!e.appBundleId)return null;let r=await R(e.device),n=r.package?.trim();return n&&n!==e.appBundleId&&("com.android.settings"===(t=n)||"com.android.systemui"===t||"com.google.android.permissioncontroller"===t||t.includes("launcher"))?{expectedPackage:e.appBundleId,foregroundPackage:n,activity:r.activity,hint:"com.google.android.permissioncontroller"===n?"Dismiss or allow the permission prompt, then retry the smoke assertion.":"Use screenshot as visual truth, then take a fresh snapshot -i before retrying."}:null}function j(e){return"COMMAND_FAILED"===e.code&&"string"==typeof e.details?.expectedPackage&&"string"==typeof e.details?.foregroundPackage}function W(e,t){let r=Array.isArray(e.selectorChain)?e.selectorChain:void 0,n=X(e),s=n?.kind==="ref"?Y(n.ref):void 0,o=n?.kind==="selector"?n.selector:void 0,i={...s?{ref:s}:{},...o?{selector:o}:{},...r?{selectorChain:r}:{}};if("attrs"===t)return i;let a="string"==typeof e.text?e.text:"";return{...i,text:a,refLabel:function(e){let t=e.trim();if(!(!t||t.length>80||/[\r\n]/.test(t)))return t}(a)}}function z(e){let t=X(e);return{...t?.kind==="ref"?{ref:Y(t.ref)}:{},...t?.kind==="selector"?{selector:t.selector}:{},..."string"==typeof e.text?{text:e.text}:{},...e.node&&"object"==typeof e.node?{node:e.node}:{}}}function J(e){let{selectorChain:t,...r}=e;return r}function Q(e,t,r,n){let s=e.get(t);s&&e.recordAction(s,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:n})}function X(e){let t=e.target;return t&&"object"==typeof t?"ref"===t.kind&&"string"==typeof t.ref?{kind:"ref",ref:t.ref}:"selector"===t.kind&&"string"==typeof t.selector?{kind:"selector",selector:t.selector}:void 0:void 0}function Y(e){return e.startsWith("@")?e.slice(1):e}let Z=["application","window","tabbar","scrollbar","image"],ee=new Set(["tab bar"]);async function et(e,t){var r;if(t.ok||(r=t.error.message,!/^wait timed out for (?:selector|text): /i.test(r)))return t;let n=await er(e).catch(()=>null);return n?i(t.error.code,`${t.error.message}. Current surface: ${n.summary}.`,{...t.error.details??{},currentSurface:n.details}):t}async function er(e){let t=[...(await r({device:e.device,session:e.session,flags:{...e.req.flags,snapshotInteractiveOnly:!0,snapshotCompact:!0},logPath:e.logPath??""})).snapshot.nodes].sort(es),n=en(t,6,{includeIdentifiers:!0});if(0===n.length)return null;let s=en(t.filter(e=>!ea(e)),4,{includeIdentifiers:!1}),o=en(t.filter(el),4,{includeIdentifiers:!0});return{summary:(s.length>0?s:n.slice(0,4)).join(", "),details:{labels:n,...o.length>0?{buttons:o}:{}}}}function en(e,t,r){let n=new Set,s=[];for(let o of e){let e=ei(o,r);if(!(!e||n.has(e))&&(n.add(e),s.push(e),s.length>=t))break}return s}function es(e,t){var r,n;return eo(e)-eo(t)||(r=e,n=t,r.rect&&n.rect?r.rect.y-n.rect.y||r.rect.x-n.rect.x:r.rect?-1:n.rect?1:(r.depth??0)-(n.depth??0)||r.index-n.index)}function eo(e){let t=!!ei(e,{includeIdentifiers:!1});return 2*!!ea(e)+ +!t}function ei(e,t){let r=(t.includeIdentifiers?[e.label,e.value,e.identifier]:[e.label,e.value]).map(e=>"string"==typeof e?e.trim():"").find(e=>e.length>0);return r?r.replace(/\s+/g," ").slice(0,80):""}function ea(e){let t=A(`${e.type??""} ${e.role??""} ${e.subrole??""}`),r=`${e.label??""} ${e.value??""}`.trim().toLowerCase();return Z.some(e=>t.includes(e))||ee.has(r)||r.endsWith(".fill")}function el(e){return A(`${e.type??""} ${e.role??""} ${e.subrole??""}`).includes("button")}function eu(e){var t;let{session:r,selectorExpression:n}=e;if(!r||"ios"!==r.device.platform||r.postGestureStabilization)return null;let s=D(n);if(!s||1!==s.selectors.length)return null;let o=s.selectors[0];if(!o||1!==o.terms.length)return null;let i=o.terms[0];return i&&"string"==typeof i.value&&("id"===(t=i.key)||"label"===t||"text"===t||"value"===t)?{key:i.key,value:i.value,raw:o.raw}:null}function ec(e,t={}){let r=g(e);if("ELEMENT_NOT_FOUND"===r.code)return!0===t.allowElementNotFound;if("COMMAND_FAILED"!==r.code)return!1;let n=r.message.toLowerCase();return n.includes("fetch failed")||n.includes("timed out")||n.includes("timeout")||n.includes("runner did not accept connection")||n.includes("invalid runner response")}async function ed(e){var t;let{req:r}=e;if("find"!==r.command)return null;let n=r.positionals??[];if(0===n.length)return i("INVALID_ARGS","find requires a locator or text");let s=M(n);if(!s.query)return i("INVALID_ARGS","find requires a value");if(r.flags?.findFirst&&r.flags?.findLast)return i("INVALID_ARGS","find accepts only one of --first or --last");let o=s.action;if("exists"!==(t=o)&&"wait"!==t&&"get_text"!==t&&"get_attrs"!==t)return null;let a=await eI(e,{requireSession:!1,capability:"find"});return a.ok?await eA(async()=>{let t=await a.runtime.selectors.find({session:e.sessionName,requestId:r.meta?.requestId,locator:s.locator,query:s.query,action:o,timeoutMs:s.timeoutMs});return Q(e.sessionStore,e.sessionName,r,function(e,t){if("exists"===t)return{found:!0};if("wait"===t)return{found:!0,waitedMs:e.waitedMs};let r="string"==typeof e.ref?e.ref:void 0;return"get_attrs"===t?{ref:r,action:"get attrs"}:{ref:r,action:"get text",text:"string"==typeof e.text?e.text:""}}(t,o)),"found"===t.kind?{found:!0,..."number"==typeof t.waitedMs?{waitedMs:t.waitedMs}:{}}:{..."string"==typeof t.ref?{ref:t.ref}:{},..."string"==typeof t.text?{text:t.text}:{},...t.node&&"object"==typeof t.node?{node:t.node}:{}}}):a.response}async function ef(e){let{req:t}=e;if("get"!==t.command)return null;let r=t.positionals?.[0];if("text"!==r&&"attrs"!==r)return i("INVALID_ARGS","get only supports text or attrs");let n=function(e){let t=e.positionals?.[1]??"";if(t.startsWith("@"))return{ok:!0,target:{kind:"ref",ref:t,fallbackLabel:e.positionals.length>2?e.positionals.slice(2).join(" ").trim():""}};let r=e.positionals?.slice(1).join(" ").trim()??"";return r?{ok:!0,target:{kind:"selector",selector:r}}:{ok:!1,response:i("INVALID_ARGS","get requires @ref or selector expression")}}(t);if(!n.ok)return n.response;if("ref"===n.target.kind){let e=F("get",t.flags);if(e)return e}if("selector"===n.target.kind){let t=await eg(e,r,n.target.selector);if(t)return t}let s=await eI(e,{requireSession:!0,capability:"get"});return s.ok?await eA(async()=>{let o=await s.runtime.selectors.get({session:e.sessionName,requestId:t.meta?.requestId,property:r,target:n.target});return Q(e.sessionStore,e.sessionName,t,W(o,r)),z(o)}):s.response}async function ep(e){let{req:t}=e;if("is"!==t.command)return null;let r=(t.positionals?.[0]??"").toLowerCase();if(!_(r))return i("INVALID_ARGS","is requires predicate: visible|hidden|exists|editable|selected|text");let{split:n}=k(t.positionals??[]);if(!n)return i("INVALID_ARGS","is requires a selector expression");let s=n.rest.join(" ").trim();if("text"===r&&!s)return i("INVALID_ARGS","is text requires expected text value");if("text"!==r&&n.rest.length>0)return i("INVALID_ARGS",`is ${r} does not accept trailing values`);let o=await eh(e,r,n.selectorExpression,s);if(o)return o;let a=await eI(e,{requireSession:!0,capability:"is"});if(!a.ok)return a.response;let l=await eA(async()=>{let o=await a.runtime.selectors.is({session:e.sessionName,requestId:t.meta?.requestId,predicate:r,selector:n.selectorExpression,expectedText:s});return Q(e.sessionStore,e.sessionName,t,o),J(o)});return await eP(e,l,`is ${r}`)}async function em(e){let{req:t,sessionName:r,sessionStore:n}=e,s=b(t.positionals??[]);if(!s)return i("INVALID_ARGS","wait requires a duration or text");let{session:a,device:l}=await O(n,r,t.flags);if("sleep"!==s.kind&&!o("wait",l))return i("UNSUPPORTED_OPERATION","wait is not supported on this device");if("selector"===s.kind){let t=await ey({...e,session:a,device:l,selectorExpression:s.selectorExpression,timeoutMs:s.timeoutMs});if(t)return t}let u=async()=>{let o=ek({...e,session:a,device:l}),i=await eA(async()=>{let e=await o.selectors.wait({session:r,requestId:t.meta?.requestId,target:function(e,t){if("sleep"===e.kind)return{kind:"sleep",durationMs:e.durationMs};if("selector"===e.kind)return{kind:"selector",selector:e.selectorExpression,timeoutMs:e.timeoutMs};if("ref"===e.kind){if(!t?.snapshot)throw new y("INVALID_ARGS","Ref wait requires an existing snapshot in session.");return{kind:"ref",ref:e.rawRef,timeoutMs:e.timeoutMs}}if(!e.text)throw new y("INVALID_ARGS","wait requires text");return{kind:"text",text:e.text,timeoutMs:e.timeoutMs}}(s,a)});return Q(n,r,t,e),{waitedMs:e.waitedMs,..."string"==typeof e.text?{text:e.text}:{},..."string"==typeof e.selector?{selector:e.selector}:{}}}),u=await et({req:t,logPath:e.logPath,session:a,device:l},i);return await eP(e,u,"wait")};return"sleep"===s.kind?await u():await $(a,l,u)}async function eg(e,t,r){let n=e.sessionStore.get(e.sessionName),s=eu({session:n,selectorExpression:r});if(!n||!s||"text"===t&&"id"!==s.key)return null;let o=await eS(e,n,s);if(ex(o))return o.response;if(!o)return null;let i=function(e,t,r){if(!r.found||!r.node)return null;let n={target:{kind:"selector",selector:t},node:r.node,selectorChain:[t]};return"attrs"===e?{kind:"attrs",...n}:"string"!=typeof r.text?null:{kind:"text",...n,text:r.text}}(t,s.raw,o);return i?(Q(e.sessionStore,e.sessionName,e.req,W(i,t)),{ok:!0,data:z(i)}):null}async function eh(e,t,r,n){var s,o,i,a,l;let u;if("hidden"===t)return null;let c=await ew(e,r);if(ex(c))return c.response;if(!c?.result.found||!c.result.node)return null;let d="exists"===t?{predicate:t,pass:!0,selector:c.selector.raw,matches:1,selectorChain:[c.selector.raw]}:(s=t,o=n,i=c.selector.raw,a=c.session,u=w({predicate:s,node:l=c.result.node,nodes:[l],expectedText:o,platform:a.device.platform}),{predicate:s,pass:u.pass,selector:i,..."text"===s?{text:u.actualText}:{},selectorChain:[i]});return d?(Q(e.sessionStore,e.sessionName,e.req,d),{ok:!0,data:J(d)}):null}async function ey(e){let t=eu({session:e.session,selectorExpression:e.selectorExpression});if(!e.session||!t)return null;let r=Date.now(),n=await eS(e,e.session,t);if(ex(n))return n.response;if(!n?.found)return null;let s={kind:"selector",selector:t.raw,waitedMs:Date.now()-r,selectorChain:[t.raw]};return Q(e.sessionStore,e.sessionName,e.req,s),await et({req:e.req,logPath:e.logPath,session:e.session,device:e.device},{ok:!0,data:s})}async function ew(e,t){let r=e.sessionStore.get(e.sessionName),n=eu({session:r,selectorExpression:t});if(!r||!n)return null;let s=await eS(e,r,n);return ex(s)?s:s?{session:r,selector:n,result:s}:null}async function e_(e,t,r){let n=await s(t.device,{command:"querySelector",selectorKey:r.key,selectorValue:r.value,appBundleId:t.appBundleId},{verbose:!!e.req.flags?.verbose,logPath:e.logPath,traceLogPath:t.trace?.outPath,requestId:e.req.meta?.requestId}),o=!0===n.found,i=function(e){let t=e.nodes;if(!Array.isArray(t))return;let r=t[0];if(r&&"object"==typeof r)return r}(n);return{found:o,..."string"==typeof n.text?{text:n.text}:{},...i?{node:i}:{}}}async function eS(e,t,r){try{return await e_(e,t,r)}catch(e){if(ec(e,{allowElementNotFound:!0}))return null;return{kind:"error",response:{ok:!1,error:h(e)}}}}function ex(e){return null!==e&&"kind"in e&&"error"===e.kind}function ek(n){return x({backend:function(e){let n,{req:s,session:o,device:i,logPath:a,sessionName:l,sessionStore:u}=e,d=0;return{platform:i.platform,captureSnapshot:async(e,f)=>{var p;let m,g={...s.flags,...(p=f,m={},p?.interactiveOnly!==void 0&&(m.snapshotInteractiveOnly=p.interactiveOnly),p?.compact!==void 0&&(m.snapshotCompact=p.compact),p?.scope!==void 0&&(m.snapshotScope=p.scope),p?.depth!==void 0&&(m.snapshotDepth=p.depth),p?.raw!==void 0&&(m.snapshotRaw=p.raw),m)},h=f?.scope??s.flags?.snapshotScope,y=Date.now(),w=C(q(g)),_="wait"===s.command||"find"===s.command;if(!_&&n&&y-d<750&&!c(o)&&!o?.postGestureStabilization)return n;if(!_&&o?.snapshot&&y-o.snapshot.createdAt<750&&o.snapshot.presentationKey===w&&!c(o)&&!o.postGestureStabilization)return d=o.snapshot.createdAt,n={snapshot:o.snapshot};let S=await r({device:i,session:o,flags:g,outPath:s.flags?.out,logPath:a??"",snapshotScope:h});return o&&(t(o,S.snapshot),u.set(l,o)),d=y,n={snapshot:S.snapshot}},readText:async(t,r)=>({text:await G({device:i,node:r,flags:s.flags,appBundleId:o?.appBundleId,traceOutPath:o?.trace?.outPath,surface:o?.surface,contextFromFlags:e.contextFromFlags??((e,t,r)=>f(a??"",e,t,r))})}),findText:async(t,r)=>({found:await ev(e,r)})}}(n),...T("selector commands",{plural:!0}),sessions:e({sessionName:n.sessionName,getSession:()=>n.session,recordOptions:{includeSnapshot:!0},setRecord:e=>{n.session&&e.snapshot&&(t(n.session,e.snapshot),n.sessionStore.set(n.sessionName,n.session))}})})}async function eI(e,t){let r=e.sessionStore.get(e.sessionName);if(!r&&t.requireSession)return{ok:!1,response:i("SESSION_NOT_FOUND","No active session. Run open first.")};let n=r?.device??await d(e.req.flags??{});return(r||await a(n),o(t.capability,n))?{ok:!0,runtime:ek({...e,session:r,device:n})}:{ok:!1,response:i("UNSUPPORTED_OPERATION",`${t.capability} is not supported on this device`)}}async function ev(e,t){let{device:r,session:n,req:o,logPath:i}=e;if("macos"===r.platform&&n?.surface&&"app"!==n.surface)return!!N((await eN(e)).nodes,t);if(E(r.platform)&&n?.appBundleId){let e=await s(r,{command:"findText",text:t,appBundleId:n?.appBundleId},{verbose:o.flags?.verbose,logPath:i,traceLogPath:n?.trace?.outPath,requestId:o.meta?.requestId});return e?.found===!0}return!!N((await eN(e)).nodes,t)}async function eN(e){let n=await r({device:e.device,session:e.session,flags:{...e.req.flags,snapshotInteractiveOnly:!1,snapshotCompact:!1},outPath:e.req.flags?.out,logPath:e.logPath??""});return e.session&&(t(e.session,n.snapshot),e.sessionStore.set(e.sessionName,e.session)),n.snapshot}async function eA(e){try{return{ok:!0,data:await e()}}catch(t){let e=g(t);return i(e.code,e.message,e.details)}}async function eP(e,t,r){var n;let s;if(t.ok)return t;let o=e.sessionStore.get(e.sessionName);if(!o)return t;try{s=await H(o)}catch{return t}return s?i(t.error.code,`${r} failed because ${"com.google.android.permissioncontroller"===(n=s).foregroundPackage?`Android permission dialog is blocking ${n.expectedPackage}`:`${n.foregroundPackage} is foreground instead of ${n.expectedPackage}`}.`,{...t.error.details??{},...s,blockedBy:"android_foreground_surface",originalMessage:t.error.message}):t}export{B as assertAndroidPressStayedInApp,U as buildSnapshotSession,T as createDaemonRuntimePolicy,ed as dispatchFindReadOnlyViaRuntime,ef as dispatchGetViaRuntime,ep as dispatchIsViaRuntime,em as dispatchWaitViaRuntime,j as isAndroidEscapeError,ec as isDirectIosSelectorFallbackError,eu as readSimpleIosSelectorTarget,G as readTextForNode,L as recordIfSession,F as refSnapshotFlagGuardResponse,O as resolveSessionDevice,$ as withSessionlessRunnerCleanup};
@@ -1,5 +1,3 @@
1
- declare type ApplePlatform = 'ios' | 'macos';
2
-
3
1
  export declare function findSelectorChainMatch(nodes: SnapshotState['nodes'], chain: SelectorChain, options: {
4
2
  platform: Platform;
5
3
  requireRect?: boolean;
@@ -22,7 +20,9 @@ export declare function isSelectorToken(token: string): boolean;
22
20
 
23
21
  export declare function parseSelectorChain(expression: string): SelectorChain;
24
22
 
25
- declare type Platform = ApplePlatform | 'android' | 'linux';
23
+ declare type Platform = (typeof PLATFORMS)[number];
24
+
25
+ declare const PLATFORMS: readonly ["ios", "macos", "android", "linux"];
26
26
 
27
27
  declare type RawSnapshotNode = {
28
28
  index: number;
@@ -1 +1 @@
1
- import"./7847.js";export{findSelectorChainMatch,formatSelectorFailure,isNodeEditable,isNodeVisible,isSelectorToken,parseSelectorChain,resolveSelectorChain,tryParseSelectorChain}from"./940.js";
1
+ export{findSelectorChainMatch,formatSelectorFailure,isNodeEditable,isNodeVisible,resolveSelectorChain}from"./7847.js";export{isSelectorToken,parseSelectorChain,tryParseSelectorChain}from"./8407.js";
@@ -1,2 +1,2 @@
1
- import{listMcpCommandMetadata as r,isCommandName as t}from"./command-metadata.js";import{readVersion as e}from"./9671.js";let n=function(r={}){return{execute:async(e,n)=>{var o;if(!t(e))throw Error(`Unknown command tool: ${e}`);let a=await i(r,function(r){if(!r||"object"!=typeof r||Array.isArray(r))return{};let t=r.stateDir;if(void 0===t)return{};if("string"!=typeof t||0===t.length)throw Error("Expected stateDir to be a non-empty string.");return{stateDir:t}}(n)),c=await (r.runCommand??s)(a,e,function(r){if(!r||"object"!=typeof r||Array.isArray(r))return r;let{stateDir:t,...e}=r;return e}(n));return{isError:!1,structuredContent:c,content:[{type:"text",text:"string"==typeof(o=c)?o:JSON.stringify(o,null,2)}]}}}}();async function i(r,t){if(r.createClient)return await r.createClient(t);let{createAgentDeviceClient:e}=await import("./9542.js");return e(t)}async function s(r,t,e){let n=await import("./command-surface.js");return await n.runCommand(r,t,e)}async function o(r){if("2.0"!==r.jsonrpc||"string"!=typeof r.method)return u(r.id??null,-32600,"Invalid JSON-RPC request.");if(void 0===r.id)return null;try{var t,e;return t=r.id,e=await a(r.method,r.params),{jsonrpc:"2.0",id:t,result:e}}catch(t){if(t instanceof f)return u(r.id,-32601,t.message);return u(r.id,-32602,t instanceof Error?t.message:String(t))}}async function a(t,n){switch(t){case"initialize":return{protocolVersion:"2025-11-25",capabilities:{tools:{}},serverInfo:{name:"agent-device",version:e()}};case"ping":return{};case"tools/list":return{tools:r().map(r=>{var t;return{name:r.name,description:r.description,inputSchema:{...t=r.inputSchema,properties:{...t.properties,stateDir:{type:"string",description:"Agent-device state directory."}}}}})};case"tools/call":return await c(n);default:throw new f(`Unsupported MCP method: ${t}`)}}async function c(r){let t=function(r){if(!r||"object"!=typeof r||Array.isArray(r))throw Error("Expected object parameters.");return r}(r),e=function(r,t){let e=r[t];if("string"!=typeof e||0===e.length)throw Error(`Expected ${t} to be a non-empty string.`);return e}(t,"name");try{return await n.execute(e,t.arguments)}catch(r){return function(r,t=!1){return{isError:t,content:[{type:"text",text:r}]}}(r instanceof Error?r.message:String(r),!0)}}function u(r,t,e){return{jsonrpc:"2.0",id:r,error:{code:t,message:e}}}class f extends Error{}async function l(){let r=function(r={}){let t=r.handlePayload??p,e=r.write??m,n=Promise.resolve();return{push:r=>{var i;let s=Array.isArray(i=r)?1===i.length?i[0]?.id??null:null:i.id??null;n=n.then(async()=>{let n=await t(r);n&&e(n)}).catch(r=>{e({jsonrpc:"2.0",id:s,error:{code:-32603,message:r instanceof Error?r.message:String(r)}})})},idle:async()=>{await n}}}(),t=new y(t=>{r.push(t)});process.stdin.setEncoding("utf8"),process.stdin.on("data",r=>{try{t.push(r)}catch(r){m({jsonrpc:"2.0",id:null,error:{code:-32700,message:r instanceof Error?r.message:String(r)}})}}),await new Promise(r=>{process.stdin.on("end",r),process.stdin.on("close",r),process.stdin.resume()}),await r.idle()}function p(r){return Array.isArray(r)?d(r):o(r)}async function d(r){let t=[];for(let n of r){var e;t.push(...(e=await o(n))?[e]:[])}return t.length>0?t:null}class y{buffer="";sink;constructor(r){this.sink=r}push(r){for(this.buffer+=r;;){let r=this.tryReadLineMessage();if(void 0!==r){this.emit(r);continue}break}}tryReadLineMessage(){let r=this.buffer.indexOf("\n");if(-1===r)return;let t=this.buffer.slice(0,r).trim();return this.buffer=this.buffer.slice(r+1),t.length>0?t:void 0}emit(r){let t=JSON.parse(r);Array.isArray(t),this.sink(t)}}function m(r){process.stdout.write(`${JSON.stringify(r)}
2
- `)}export{l as runAgentDeviceMcpServer};
1
+ import{isCommandName as t,listMcpCommandMetadata as r,formatCliOutput as e}from"./8173.js";import{readVersion as n}from"./9671.js";let i=function(r={}){return{execute:async(n,i)=>{var u;if(!t(n))throw Error(`Unknown command tool: ${n}`);let c=!(u=i)||"object"!=typeof u||Array.isArray(u)?{client:{},outputFormat:"optimized"}:{client:function(t){let r=t.stateDir,e={};if(void 0!==r&&("string"!=typeof r||0===r.length))throw Error("Expected stateDir to be a non-empty string.");return"string"==typeof r&&(e.stateDir=r),e}(u),outputFormat:function(t){if(void 0===t)return"optimized";if("optimized"!==t&&"json"!==t)throw Error('Expected mcpOutputFormat to be "optimized" or "json".');return t}(u.mcpOutputFormat)},p=function(t){if(!t||"object"!=typeof t||Array.isArray(t))return t;let{stateDir:r,mcpOutputFormat:e,...n}=t;return n}(i),l=await o(r,c.client),f=await (r.runCommand??s)(l,n,p);return{isError:!1,structuredContent:f,content:[{type:"text",text:function(t){if("json"===t.outputFormat)return a(t.result);let r=e({name:t.name,input:t.input,result:t.result});return"string"==typeof r?.text?r.text:a(r?.data??t.result)}({name:n,input:p,result:f,outputFormat:c.outputFormat})}]}}}}();async function o(t,r){if(t.createClient)return await t.createClient(r);let{createAgentDeviceClient:e}=await import("./9542.js");return e(r)}async function s(t,r,e){let n=await import("./command-surface.js");return await n.runCommand(t,r,e)}function a(t){return"string"==typeof t?t:JSON.stringify(t,null,2)}async function u(t){if("2.0"!==t.jsonrpc||"string"!=typeof t.method)return l(t.id??null,-32600,"Invalid JSON-RPC request.");if(void 0===t.id)return null;try{var r,e;return r=t.id,e=await c(t.method,t.params),{jsonrpc:"2.0",id:r,result:e}}catch(r){if(r instanceof f)return l(t.id,-32601,r.message);return l(t.id,-32602,r instanceof Error?r.message:String(r))}}async function c(t,e){switch(t){case"initialize":return{protocolVersion:"2025-11-25",capabilities:{tools:{}},serverInfo:{name:"agent-device",version:n()}};case"ping":return{};case"tools/list":return{tools:r().map(t=>{var r;return{name:t.name,description:t.description,inputSchema:{...r=t.inputSchema,properties:{...r.properties,stateDir:{type:"string",description:"Agent-device state directory."},mcpOutputFormat:{type:"string",enum:["optimized","json"],description:"MCP text content format. Defaults to optimized agent-friendly text; use json for JSON text. Structured content is always returned separately."}}}}})};case"tools/call":return await p(e);default:throw new f(`Unsupported MCP method: ${t}`)}}async function p(t){let r=function(t){if(!t||"object"!=typeof t||Array.isArray(t))throw Error("Expected object parameters.");return t}(t),e=function(t,r){let e=t[r];if("string"!=typeof e||0===e.length)throw Error(`Expected ${r} to be a non-empty string.`);return e}(r,"name");try{return await i.execute(e,r.arguments)}catch(t){return function(t,r=!1){return{isError:r,content:[{type:"text",text:t}]}}(t instanceof Error?t.message:String(t),!0)}}function l(t,r,e){return{jsonrpc:"2.0",id:t,error:{code:r,message:e}}}class f extends Error{}async function d(){let t=function(t={}){let r=t.handlePayload??m,e=t.write??h,n=Promise.resolve();return{push:t=>{var i;let o=Array.isArray(i=t)?1===i.length?i[0]?.id??null:null:i.id??null;n=n.then(async()=>{let n=await r(t);n&&e(n)}).catch(t=>{e({jsonrpc:"2.0",id:o,error:{code:-32603,message:t instanceof Error?t.message:String(t)}})})},idle:async()=>{await n}}}(),r=new g(r=>{t.push(r)});process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{try{r.push(t)}catch(t){h({jsonrpc:"2.0",id:null,error:{code:-32700,message:t instanceof Error?t.message:String(t)}})}}),await new Promise(t=>{process.stdin.on("end",t),process.stdin.on("close",t),process.stdin.resume()}),await t.idle()}function m(t){return Array.isArray(t)?y(t):u(t)}async function y(t){let r=[];for(let n of t){var e;r.push(...(e=await u(n))?[e]:[])}return r.length>0?r:null}class g{buffer="";sink;constructor(t){this.sink=t}push(t){for(this.buffer+=t;;){let t=this.tryReadLineMessage();if(void 0!==t){this.emit(t);continue}break}}tryReadLineMessage(){let t=this.buffer.indexOf("\n");if(-1===t)return;let r=this.buffer.slice(0,t).trim();return this.buffer=this.buffer.slice(t+1),r.length>0?r:void 0}emit(t){let r=JSON.parse(t);Array.isArray(r),this.sink(r)}}function h(t){process.stdout.write(`${JSON.stringify(t)}
2
+ `)}export{d as runAgentDeviceMcpServer};