agent-device 0.17.2 → 0.17.3

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 (83) hide show
  1. package/README.md +2 -2
  2. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.17.2.apk → agent-device-android-multitouch-helper-0.17.3.apk} +0 -0
  3. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.17.3.apk.sha256 +1 -0
  4. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.17.2.manifest.json → agent-device-android-multitouch-helper-0.17.3.manifest.json} +4 -4
  5. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.17.2.apk → agent-device-android-snapshot-helper-0.17.3.apk} +0 -0
  6. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.17.3.apk.sha256 +1 -0
  7. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.17.2.manifest.json → agent-device-android-snapshot-helper-0.17.3.manifest.json} +6 -6
  8. package/dist/src/123.js +1 -1
  9. package/dist/src/1231.js +1 -1
  10. package/dist/src/1352.js +1 -1
  11. package/dist/src/221.js +2 -2
  12. package/dist/src/2403.js +3 -3
  13. package/dist/src/2415.js +36 -29
  14. package/dist/src/5611.js +1 -0
  15. package/dist/src/5827.js +1 -0
  16. package/dist/src/6088.js +1 -0
  17. package/dist/src/6133.js +1 -0
  18. package/dist/src/7847.js +1 -1
  19. package/dist/src/9471.js +1 -1
  20. package/dist/src/9542.js +6 -3
  21. package/dist/src/9616.js +2 -1
  22. package/dist/src/9818.js +1 -1
  23. package/dist/src/android-snapshot-helper.d.ts +0 -1
  24. package/dist/src/android.js +5 -5
  25. package/dist/src/apple.js +1 -1
  26. package/dist/src/apps.js +10 -10
  27. package/dist/src/args.js +53 -14
  28. package/dist/src/batch.js +1 -1
  29. package/dist/src/cli-output.js +29 -0
  30. package/dist/src/cli.js +15 -16
  31. package/dist/src/command-surface.js +1 -1
  32. package/dist/src/contracts.d.ts +52 -0
  33. package/dist/src/contracts.js +1 -1
  34. package/dist/src/find.js +1 -1
  35. package/dist/src/generic.js +5 -5
  36. package/dist/src/index.d.ts +111 -8
  37. package/dist/src/index.js +1 -1
  38. package/dist/src/input-actions.js +1 -1
  39. package/dist/src/interaction.js +1 -1
  40. package/dist/src/react-native.js +1 -1
  41. package/dist/src/record-trace-recording.js +3 -3
  42. package/dist/src/selector-runtime.js +1 -1
  43. package/dist/src/selectors.d.ts +14 -0
  44. package/dist/src/selectors.js +1 -1
  45. package/dist/src/server.js +2 -2
  46. package/dist/src/session.js +8 -10
  47. package/dist/src/snapshot.js +2 -2
  48. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h +1 -0
  49. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +14 -0
  50. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +297 -0
  51. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +328 -0
  52. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +121 -172
  53. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +153 -5
  54. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +171 -0
  55. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +0 -10
  56. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +64 -18
  57. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +200 -0
  58. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +406 -0
  59. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +82 -169
  60. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +425 -0
  61. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +77 -6
  62. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +10 -1
  63. package/ios-runner/README.md +5 -5
  64. package/ios-runner/RUNNER_PROTOCOL.md +0 -1
  65. package/package.json +3 -3
  66. package/server.json +2 -2
  67. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.17.2.apk.sha256 +0 -1
  68. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.17.2.apk.sha256 +0 -1
  69. package/dist/src/1534.js +0 -1
  70. package/dist/src/1620.js +0 -1
  71. package/dist/src/1644.js +0 -2
  72. package/dist/src/2284.js +0 -1
  73. package/dist/src/2474.js +0 -1
  74. package/dist/src/2672.js +0 -1
  75. package/dist/src/3393.js +0 -1
  76. package/dist/src/5898.js +0 -1
  77. package/dist/src/695.js +0 -1
  78. package/dist/src/7455.js +0 -1
  79. package/dist/src/8173.js +0 -27
  80. package/dist/src/8407.js +0 -1
  81. package/dist/src/8699.js +0 -1
  82. package/dist/src/9673.js +0 -1
  83. package/dist/src/9974.js +0 -1
@@ -7,6 +7,7 @@ export declare type AgentDeviceClient = {
7
7
  };
8
8
  sessions: {
9
9
  list: (options?: AgentDeviceRequestOverrides) => Promise<AgentDeviceSession[]>;
10
+ stateDir: (options?: AgentDeviceRequestOverrides & Pick<AgentDeviceClientConfig, 'stateDir'>) => Promise<string>;
10
11
  close: (options?: AgentDeviceRequestOverrides & {
11
12
  shutdown?: boolean;
12
13
  }) => Promise<SessionCloseResult>;
@@ -71,6 +72,9 @@ export declare type AgentDeviceClient = {
71
72
  logs: (options?: LogsOptions) => Promise<CommandRequestResult>;
72
73
  network: (options?: NetworkOptions) => Promise<CommandRequestResult>;
73
74
  };
75
+ debug: {
76
+ symbols: (options: DebugSymbolsOptions) => Promise<DebugSymbolsResult>;
77
+ };
74
78
  recording: {
75
79
  record: (options: RecordOptions) => Promise<CommandRequestResult>;
76
80
  trace: (options: TraceOptions) => Promise<CommandRequestResult>;
@@ -316,7 +320,7 @@ export declare type AppOpenOptions = AgentDeviceRequestOverrides & AgentDeviceSe
316
320
  launchArgs?: string[];
317
321
  relaunch?: boolean;
318
322
  saveScript?: boolean | string;
319
- noDeviceHub?: boolean;
323
+ deviceHub?: boolean;
320
324
  noRecord?: boolean;
321
325
  runtime?: SessionRuntimeHints;
322
326
  };
@@ -410,7 +414,7 @@ export declare type BatchStep = {
410
414
  runtime?: unknown;
411
415
  };
412
416
 
413
- export declare type CaptureDiffOptions = DeviceCommandBaseOptions & Pick<CaptureSnapshotOptions, 'interactiveOnly' | 'compact' | 'depth' | 'scope' | 'raw'> & {
417
+ export declare type CaptureDiffOptions = DeviceCommandBaseOptions & Pick<CaptureSnapshotOptions, 'interactiveOnly' | 'depth' | 'scope' | 'raw'> & {
414
418
  kind: 'snapshot';
415
419
  out?: string;
416
420
  };
@@ -432,7 +436,6 @@ export declare type CaptureScreenshotResult = {
432
436
 
433
437
  export declare type CaptureSnapshotOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
434
438
  interactiveOnly?: boolean;
435
- compact?: boolean;
436
439
  depth?: number;
437
440
  scope?: string;
438
441
  raw?: boolean;
@@ -446,11 +449,9 @@ export declare type CaptureSnapshotResult = {
446
449
  appName?: string;
447
450
  appBundleId?: string;
448
451
  visibility?: SnapshotVisibility;
449
- androidSnapshot?: AndroidSnapshotBackendMetadata;
450
- warnings?: string[];
451
452
  unchanged?: SnapshotUnchanged;
452
453
  identifiers: AgentDeviceIdentifiers;
453
- };
454
+ } & PublicSnapshotCaptureAnnotations;
454
455
 
455
456
  export declare function centerOfRect(rect: Rect): Point;
456
457
 
@@ -593,6 +594,58 @@ declare type DaemonServerMode = (typeof DAEMON_SERVER_MODES)[number];
593
594
 
594
595
  declare type DaemonTransportPreference = (typeof DAEMON_TRANSPORT_PREFERENCES)[number];
595
596
 
597
+ declare type DebugSymbolsCrashFrame = {
598
+ index: number;
599
+ image: string;
600
+ address: string;
601
+ symbol?: string;
602
+ };
603
+
604
+ declare type DebugSymbolsCrashSummary = {
605
+ format: 'ips' | 'text';
606
+ appName?: string;
607
+ bundleId?: string;
608
+ version?: string;
609
+ incident?: string;
610
+ timestamp?: string;
611
+ exceptionType?: string;
612
+ exceptionCodes?: string;
613
+ terminationReason?: string;
614
+ crashedThread?: number;
615
+ topFrames: DebugSymbolsCrashFrame[];
616
+ findings: string[];
617
+ };
618
+
619
+ declare type DebugSymbolsImage = {
620
+ name: string;
621
+ uuid: string;
622
+ arch?: string;
623
+ dsymPath: string;
624
+ binaryPath: string;
625
+ };
626
+
627
+ declare type DebugSymbolsOptions = {
628
+ action?: 'symbols';
629
+ artifact: string;
630
+ dsym?: string;
631
+ searchPath?: string;
632
+ out?: string;
633
+ cwd?: string;
634
+ };
635
+
636
+ declare type DebugSymbolsResult = {
637
+ kind: 'debugSymbols';
638
+ platform: 'apple';
639
+ artifactPath: string;
640
+ outPath: string;
641
+ crash: DebugSymbolsCrashSummary;
642
+ matchedImages: DebugSymbolsImage[];
643
+ symbolicatedFrames: number;
644
+ skippedImages: number;
645
+ warnings?: string[];
646
+ message: string;
647
+ };
648
+
596
649
  declare const DEVICE_KINDS: readonly ["simulator", "emulator", "device"];
597
650
 
598
651
  declare const DEVICE_ROTATIONS: readonly ["portrait", "portrait-upside-down", "landscape-left", "landscape-right"];
@@ -875,19 +928,32 @@ declare type PanOptions = DeviceCommandBaseOptions & {
875
928
  durationMs?: number;
876
929
  };
877
930
 
878
- declare const PERF_ACTION_VALUES: readonly ["sample"];
931
+ declare const PERF_ACTION_VALUES: readonly ["sample", "snapshot", "start", "stop", "report"];
932
+
933
+ declare const PERF_AREA_VALUES: readonly ["metrics", "frames", "memory", "cpu", "trace"];
934
+
935
+ declare const PERF_KIND_VALUES: readonly ["xctrace", "simpleperf", "perfetto", "android-hprof", "memgraph"];
879
936
 
880
- declare const PERF_AREA_VALUES: readonly ["metrics", "frames"];
937
+ declare const PERF_SUBJECT_VALUES: readonly ["profile"];
881
938
 
882
939
  declare type PerfAction = (typeof PERF_ACTION_VALUES)[number];
883
940
 
884
941
  declare type PerfArea = (typeof PERF_AREA_VALUES)[number];
885
942
 
943
+ declare type PerfKind = (typeof PERF_KIND_VALUES)[number];
944
+
886
945
  export declare type PerfOptions = DeviceCommandBaseOptions & {
887
946
  area?: PerfArea;
947
+ subject?: PerfSubject;
888
948
  action?: PerfAction;
949
+ kind?: PerfKind;
950
+ template?: string;
951
+ out?: string;
952
+ tracePath?: string;
889
953
  };
890
954
 
955
+ declare type PerfSubject = (typeof PERF_SUBJECT_VALUES)[number];
956
+
891
957
  export declare type PermissionTarget = 'camera' | 'microphone' | 'photos' | 'contacts' | 'contacts-limited' | 'notifications' | 'calendar' | 'location' | 'location-always' | 'media-library' | 'motion' | 'reminders' | 'siri' | 'accessibility' | 'screen-recording' | 'input-monitoring';
892
958
 
893
959
  export declare type PinchOptions = DeviceCommandBaseOptions & {
@@ -940,6 +1006,10 @@ declare type PrepareMetroRuntimeResult = {
940
1006
 
941
1007
  export declare type PressOptions = DeviceCommandBaseOptions & SelectorSnapshotCommandOptions & InteractionTarget & RepeatedPressOptions;
942
1008
 
1009
+ declare type PublicSnapshotCaptureAnnotations = Pick<SnapshotCaptureAnnotations, 'androidSnapshot' | 'warnings'> & {
1010
+ snapshotQuality?: SnapshotQualityVerdict;
1011
+ };
1012
+
943
1013
  declare type RawSnapshotNode = {
944
1014
  index: number;
945
1015
  type?: string;
@@ -1165,10 +1235,43 @@ export declare type SettingsUpdateOptions = (DeviceCommandBaseOptions & {
1165
1235
  mode?: 'full' | 'limited';
1166
1236
  });
1167
1237
 
1238
+ declare type SnapshotCaptureAnalysis = {
1239
+ rawNodeCount: number;
1240
+ maxDepth: number;
1241
+ };
1242
+
1243
+ declare type SnapshotCaptureAnnotations = {
1244
+ analysis?: SnapshotCaptureAnalysis;
1245
+ androidSnapshot?: AndroidSnapshotBackendMetadata;
1246
+ freshness?: SnapshotCaptureFreshness;
1247
+ quality?: SnapshotQualityVerdict;
1248
+ warnings?: string[];
1249
+ };
1250
+
1251
+ declare type SnapshotCaptureFreshness = {
1252
+ action: string;
1253
+ retryCount: number;
1254
+ staleAfterRetries: boolean;
1255
+ reason?: 'empty-interactive' | 'sharp-drop' | 'stuck-route';
1256
+ };
1257
+
1168
1258
  export declare type SnapshotNode = RawSnapshotNode & {
1169
1259
  ref: string;
1170
1260
  };
1171
1261
 
1262
+ /**
1263
+ * Structured quality verdict computed once by the iOS runner's snapshot capture plan.
1264
+ * The daemon renders it; it never re-derives degradation from node shapes.
1265
+ */
1266
+ declare type SnapshotQualityVerdict = {
1267
+ state: 'healthy' | 'recovered' | 'sparse';
1268
+ backend: 'tree' | 'queries' | 'private-ax';
1269
+ reason?: string;
1270
+ reasonCode?: 'ax-rejected' | 'sparse-tree' | 'budget' | 'no-nodes' | 'capture-failed';
1271
+ effectiveDepth?: number;
1272
+ collapsedLeafIndexes?: number[];
1273
+ };
1274
+
1172
1275
  declare type SnapshotUnchanged = {
1173
1276
  ageMs: number;
1174
1277
  nodeCount: number;
package/dist/src/index.js CHANGED
@@ -1 +1 @@
1
- export{AppError,isAgentDeviceError,normalizeAgentDeviceError}from"./9152.js";export{centerOfRect}from"./1644.js";export{createAgentDeviceClient}from"./9542.js";export{createLocalArtifactAdapter}from"./7719.js";
1
+ export{AppError,isAgentDeviceError,normalizeAgentDeviceError}from"./9152.js";export{centerOfRect}from"./9616.js";export{createAgentDeviceClient}from"./9542.js";export{createLocalArtifactAdapter}from"./7719.js";
@@ -1 +1 @@
1
- import{emitDiagnostic as t}from"./7599.js";import{runAndroidAdb as e}from"./8806.js";import{getAndroidKeyboardState as n,isAndroidInputMethodOwnedNode as a}from"./8133.js";import{captureAndroidUiHierarchyXml as r}from"./2415.js";import{androidUiNodes as i}from"./221.js";import{AppError as o}from"./9152.js";import{buildScrollGesturePlan as u}from"./1620.js";import{resolveAndroidTextInjector as l}from"./9639.js";import{sleep as c}from"./4829.js";function s(t){var e;return!!t&&(!!t.password||!!(e=t.text)&&Array.from(e).every(f))}function d(t){if(!t)return null;let e=s(t);return{...t,text:e?null:t.text,...e?{textRedacted:!0}:{}}}function f(t){return"•"===t||"*"===t||"●"===t}async function p(t,e,n,a){let r=null,i=null,o=await y(t);for(let u of[0,150,350]){u>0&&await c(u);let l=await g(t,e,n,a,o);if(r=l,"ime_capture"===l.reason)return l;i=l.ok?l:null}return i??r??{ok:!1,actual:null,reason:"text_mismatch",targetInput:null,actualInput:null}}async function h(t,e,n){var a,i,o;return a=await r(t),i=e,o=n,m(a,i,o).actualInput?.text??null}async function g(t,e,n,a,i){return function(t,e,n,a,r={}){var i,o;let u,l=m(t,e,n,r);return!function(t){let{targetInput:e,actualInput:n}=t;return!!e&&!!n&&n!==e&&n.inputMethodOwned&&!e.inputMethodOwned}(l)?function(t,e){let n=t.actualInput;if(!n||!s(n))return null;let a=n.text??null,r=Array.from(a??"").length,i=Array.from(e).length,o=null!==a&&r>0&&i>0&&r===i;return{ok:o,actual:a,reason:o?void 0:"masked_unverified",masked:!0,targetInput:t.targetInput,actualInput:n}}(l,a)??(i=l,o=a,{ok:function(t,e){if(t===e)return!0;let n=w(t),a=w(e);return!!n&&!!a&&!!(n===a||function(t,e){if(t.length!==e.length||0===t.length||t.slice(1)!==e.slice(1))return!1;let n=t[0],a=e[0];return!!n&&!!a&&a.toLowerCase()===a&&n===a.toUpperCase()}(n,a))}(u=i.actualInput?.text??null,o),actual:u,reason:"text_mismatch",targetInput:i.targetInput,actualInput:i.actualInput}):{ok:!1,actual:l.actualInput?.text??null,reason:"ime_capture",targetInput:l.targetInput,actualInput:l.actualInput}}(await r(t),e,n,a,i)}function m(t,e,n,r={}){var o;let u,l={focusedEdit:null,editAtPoint:null,anyAtPoint:null};for(let o of i(t)){let t=function(t,e){if(!t.rect)return null;let n=t.text??"",r=Math.max(1,t.rect.width*t.rect.height);return{text:n||null,className:t.className,resourceId:t.resourceId,packageName:t.packageName,rect:t.rect,focused:t.focused??!1,password:!0===t.password,inputMethodOwned:a({packageName:t.packageName,resourceId:t.resourceId,activeInputMethodPackage:e.activeInputMethodPackage}),area:r,editText:function(t){let e=t.toLowerCase();return e.includes("edittext")||e.includes("textfield")}(t.className??"")}}(o,r);t&&function(t,e,n,a){var r,i,o;let u=(r=e.rect,i=n,o=a,i>=r.x&&i<=r.x+r.width&&o>=r.y&&o<=r.y+r.height);if(u&&e.editText&&(t.editAtPoint=_(t.editAtPoint,e)),e.focused&&e.editText){t.focusedEdit=_(t.focusedEdit,e);return}u&&e.text&&(t.anyAtPoint=_(t.anyAtPoint,e))}(l,t,e,n)}return{targetInput:u=(o=l).editAtPoint??o.anyAtPoint,actualInput:(o.focusedEdit?.text?o.focusedEdit:null)??u}}function w(t){return(t??"").replace(/\s+/g," ").trim()}async function y(e){try{return{activeInputMethodPackage:(await n(e)).inputMethodPackage}}catch(e){return t({level:"warn",phase:"android_fill_verification_input_method_probe_failed",data:{error:e instanceof Error?e.message:String(e)}}),{}}}function _(t,e){return t&&t.area<e.area?t:e}async function A(t,n,a){await e(t,["shell","input","tap",String(n),String(a)])}async function x(t,n,a,r,i,o=250){await e(t,["shell","input","swipe",String(n),String(a),String(r),String(i),String(o)])}async function I(t){await e(t,["shell","input","keyevent","4"])}async function S(t){await e(t,["shell","input","keyevent","3"])}async function M(t){await e(t,["shell","input","keyevent","ENTER"])}async function k(t,n){let a=function(t){switch(t){case"portrait":return"0";case"landscape-left":return"1";case"portrait-upside-down":return"2";case"landscape-right":return"3";default:throw new o("INVALID_ARGS",`Unsupported Android rotation: ${t}`)}}(n);await e(t,["shell","settings","put","system","accelerometer_rotation","0"]),await e(t,["shell","settings","put","system","user_rotation",a])}async function E(t){await e(t,["shell","input","keyevent","187"])}async function v(t,n,a,r=800){await e(t,["shell","input","swipe",String(n),String(a),String(n),String(a),String(r)])}async function C(t,e,n=0){let a=l(t);if(a){await a({action:"type",text:e,delayMs:n}),T("type","provider-native",e);return}(z(e),await D(t,"type"),n>0&&Array.from(e).length>1)?await L(t,{action:"type",text:e,chunkSize:1,delayMs:n}):await L(t,{action:"type",text:e,chunkSize:O,delayMs:0})}async function P(t,e,n){await A(t,e,n)}async function N(t,e,n,a,r=0){let i=l(t);if(i){await i({action:"fill",target:{x:e,y:n},text:a,delayMs:r}),T("fill","provider-native",a);let o=await p(t,e,n,a);if(o.ok)return;b(a,o)}z(a);let o=Array.from(a).length,u=null;for(let i of[{clearPadding:12,minClear:8,maxClear:48,chunkSize:r>0?1:O,inputDelayMs:r},{clearPadding:24,minClear:16,maxClear:96,chunkSize:r>0?1:4,inputDelayMs:r>0?r:15}]){var c,s;await P(t,e,n),await D(t,"fill");let r=(c=o+i.clearPadding,s=i.minClear,Math.max(s,Math.min(i.maxClear,c)));await F(t,r),await L(t,{action:"fill",text:a,chunkSize:i.chunkSize,delayMs:i.inputDelayMs});let l=await p(t,e,n,a);if(u=l,l.ok)return;"ime_capture"===l.reason&&b(a,l)}b(a,u)}function b(t,e){let n;throw new o("COMMAND_FAILED",e?.reason==="ime_capture"?"Android fill input was captured by the active keyboard instead of the app field":e?.reason==="masked_unverified"?"Android fill verification could not confirm masked text value":"Android fill verification failed",(n=function(t,e){var n;if(!e)return{expected:t,actual:null,failureReason:"text_mismatch",targetInput:null,actualInput:null};let a=!0===(n=e).masked||s(n.targetInput)||s(n.actualInput),r={failureReason:e.reason??"text_mismatch",targetInput:d(e.targetInput),actualInput:d(e.actualInput)};return a?{...r,expectedLength:Array.from(t).length,actual:null,masked:!0,actualLength:Array.from(e.actual??"").length}:{...r,expected:t,actual:e.actual}}(t,e),e?.reason==="ime_capture"&&(n.hint="The focused input belongs to the Android keyboard/IME, not the app field. Disable handwriting/stylus input or switch to a standard IME, then retry fill."),n))}async function K(t,n,a){let r=await R(t),i=u({direction:n,amount:a?.amount,pixels:a?.pixels,referenceWidth:r.width,referenceHeight:r.height});return await e(t,["shell","input","swipe",String(i.x1),String(i.y1),String(i.x2),String(i.y2),"300"]),i}async function D(e,a){let r;try{r=await n(e)}catch(e){t({level:"warn",phase:"android_input_ownership_probe_failed",data:{action:a,error:e instanceof Error?e.message:String(e)}});return}if("ime"===r.inputOwner)throw new o("COMMAND_FAILED","KEYBOARD_OVERLAY_BLOCKING: Android text input is blocked because the focused input belongs to the active keyboard/IME.",{failureReason:"ime_capture",action:a,inputOwner:r.inputOwner,inputType:r.inputType,type:r.type,inputMethodPackage:r.inputMethodPackage,focusedPackage:r.focusedPackage,focusedResourceId:r.focusedResourceId,nextAction:"Focused input appears to be owned by the keyboard/IME; dismiss or change the IME before retrying text entry."})}async function R(t){let n=(await e(t,["shell","wm","size"])).stdout.match(/Physical size:\s*(\d+)x(\d+)/);if(!n)throw new o("COMMAND_FAILED","Unable to read screen size");return{width:Number(n[1]),height:Number(n[2])}}let O=8;async function L(t,n){let a=n.text.split("\n");for(let[r,i]of a.entries()){let o=function(t,e){let n=Math.max(1,Math.floor(e)),a=[],r=Array.from(t);for(let t=0;t<r.length;t+=n)a.push(r.slice(t,t+n).join(""));return a.length>0?a:[""]}(i,n.chunkSize);for(let[e,i]of o.entries())await U(t,i),n.delayMs>0&&(e+1<o.length||r+1<a.length)&&await c(n.delayMs);r+1<a.length&&await e(t,["shell","input","keyevent","ENTER"])}T(n.action,"adb-shell",n.text)}async function U(t,n){if(n)try{await e(t,["shell","input","text",n.replace(/ /g,"%s")])}catch(t){if(function(t){if(!(t instanceof o)||"COMMAND_FAILED"!==t.code)return!1;let e=t.details?.stderr,n=("string"==typeof e?e:"").toLowerCase();return!!(n.includes("exception occurred while executing 'text'")||n.includes("nullpointerexception")&&n.includes("inputshellcommand.sendtext"))}(t))throw H(n,t);throw t}}function z(t){if(!function(t){for(let e of t){let t=e.codePointAt(0);if(void 0!==t&&"\n"!==e&&(t<32||t>126))return!1}return!0}(t))throw H(t)}function H(t,e){return new o("COMMAND_FAILED","Android text input requires provider-native text injection for non-ASCII/control characters; the current adb-shell fallback supports ASCII text only.",{backend:"adb-shell",textLength:Array.from(t).length,textPreview:t.slice(0,32)},e instanceof Error?e:void 0)}function T(e,n,a){t({phase:"android_text_injection",data:{action:e,backend:n,textLength:Array.from(a).length}})}async function F(t,n){let a=Math.max(0,n);await e(t,["shell","input","keyevent","KEYCODE_MOVE_END"],{allowFailure:!0});for(let n=0;n<a;n+=24){let r=Math.min(24,a-n);await e(t,["shell","input","keyevent",...Array(r).fill("KEYCODE_DEL")],{allowFailure:!0})}}export{E as appSwitcherAndroid,I as backAndroid,N as fillAndroid,P as focusAndroid,R as getAndroidScreenSize,S as homeAndroid,v as longPressAndroid,A as pressAndroid,M as pressAndroidEnter,h as readAndroidTextAtPoint,k as rotateAndroid,K as scrollAndroid,x as swipeAndroid,C as typeAndroid};
1
+ import{emitDiagnostic as t}from"./7599.js";import{runAndroidAdb as e}from"./8806.js";import{getAndroidKeyboardState as n,isAndroidInputMethodOwnedNode as a}from"./8133.js";import{captureAndroidUiHierarchyXml as r}from"./2415.js";import{androidUiNodes as i}from"./221.js";import{AppError as o}from"./9152.js";import{buildScrollGesturePlan as u}from"./6133.js";import{resolveAndroidTextInjector as l}from"./9639.js";import{sleep as c}from"./4829.js";function s(t){var e;return!!t&&(!!t.password||!!(e=t.text)&&Array.from(e).every(f))}function d(t){if(!t)return null;let e=s(t);return{...t,text:e?null:t.text,...e?{textRedacted:!0}:{}}}function f(t){return"•"===t||"*"===t||"●"===t}async function p(t,e,n,a){let r=null,i=null,o=await y(t);for(let u of[0,150,350]){u>0&&await c(u);let l=await g(t,e,n,a,o);if(r=l,"ime_capture"===l.reason)return l;i=l.ok?l:null}return i??r??{ok:!1,actual:null,reason:"text_mismatch",targetInput:null,actualInput:null}}async function h(t,e,n){var a,i,o;return a=await r(t),i=e,o=n,m(a,i,o).actualInput?.text??null}async function g(t,e,n,a,i){return function(t,e,n,a,r={}){var i,o;let u,l=m(t,e,n,r);return!function(t){let{targetInput:e,actualInput:n}=t;return!!e&&!!n&&n!==e&&n.inputMethodOwned&&!e.inputMethodOwned}(l)?function(t,e){let n=t.actualInput;if(!n||!s(n))return null;let a=n.text??null,r=Array.from(a??"").length,i=Array.from(e).length,o=null!==a&&r>0&&i>0&&r===i;return{ok:o,actual:a,reason:o?void 0:"masked_unverified",masked:!0,targetInput:t.targetInput,actualInput:n}}(l,a)??(i=l,o=a,{ok:function(t,e){if(t===e)return!0;let n=w(t),a=w(e);return!!n&&!!a&&!!(n===a||function(t,e){if(t.length!==e.length||0===t.length||t.slice(1)!==e.slice(1))return!1;let n=t[0],a=e[0];return!!n&&!!a&&a.toLowerCase()===a&&n===a.toUpperCase()}(n,a))}(u=i.actualInput?.text??null,o),actual:u,reason:"text_mismatch",targetInput:i.targetInput,actualInput:i.actualInput}):{ok:!1,actual:l.actualInput?.text??null,reason:"ime_capture",targetInput:l.targetInput,actualInput:l.actualInput}}(await r(t),e,n,a,i)}function m(t,e,n,r={}){var o;let u,l={focusedEdit:null,editAtPoint:null,anyAtPoint:null};for(let o of i(t)){let t=function(t,e){if(!t.rect)return null;let n=t.text??"",r=Math.max(1,t.rect.width*t.rect.height);return{text:n||null,className:t.className,resourceId:t.resourceId,packageName:t.packageName,rect:t.rect,focused:t.focused??!1,password:!0===t.password,inputMethodOwned:a({packageName:t.packageName,resourceId:t.resourceId,activeInputMethodPackage:e.activeInputMethodPackage}),area:r,editText:function(t){let e=t.toLowerCase();return e.includes("edittext")||e.includes("textfield")}(t.className??"")}}(o,r);t&&function(t,e,n,a){var r,i,o;let u=(r=e.rect,i=n,o=a,i>=r.x&&i<=r.x+r.width&&o>=r.y&&o<=r.y+r.height);if(u&&e.editText&&(t.editAtPoint=_(t.editAtPoint,e)),e.focused&&e.editText){t.focusedEdit=_(t.focusedEdit,e);return}u&&e.text&&(t.anyAtPoint=_(t.anyAtPoint,e))}(l,t,e,n)}return{targetInput:u=(o=l).editAtPoint??o.anyAtPoint,actualInput:(o.focusedEdit?.text?o.focusedEdit:null)??u}}function w(t){return(t??"").replace(/\s+/g," ").trim()}async function y(e){try{return{activeInputMethodPackage:(await n(e)).inputMethodPackage}}catch(e){return t({level:"warn",phase:"android_fill_verification_input_method_probe_failed",data:{error:e instanceof Error?e.message:String(e)}}),{}}}function _(t,e){return t&&t.area<e.area?t:e}async function A(t,n,a){await e(t,["shell","input","tap",String(n),String(a)])}async function x(t,n,a,r,i,o=250){await e(t,["shell","input","swipe",String(n),String(a),String(r),String(i),String(o)])}async function I(t){await e(t,["shell","input","keyevent","4"])}async function S(t){await e(t,["shell","input","keyevent","3"])}async function M(t){await e(t,["shell","input","keyevent","ENTER"])}async function k(t,n){let a=function(t){switch(t){case"portrait":return"0";case"landscape-left":return"1";case"portrait-upside-down":return"2";case"landscape-right":return"3";default:throw new o("INVALID_ARGS",`Unsupported Android rotation: ${t}`)}}(n);await e(t,["shell","settings","put","system","accelerometer_rotation","0"]),await e(t,["shell","settings","put","system","user_rotation",a])}async function E(t){await e(t,["shell","input","keyevent","187"])}async function v(t,n,a,r=800){await e(t,["shell","input","swipe",String(n),String(a),String(n),String(a),String(r)])}async function C(t,e,n=0){let a=l(t);if(a){await a({action:"type",text:e,delayMs:n}),T("type","provider-native",e);return}(z(e),await D(t,"type"),n>0&&Array.from(e).length>1)?await L(t,{action:"type",text:e,chunkSize:1,delayMs:n}):await L(t,{action:"type",text:e,chunkSize:O,delayMs:0})}async function P(t,e,n){await A(t,e,n)}async function N(t,e,n,a,r=0){let i=l(t);if(i){await i({action:"fill",target:{x:e,y:n},text:a,delayMs:r}),T("fill","provider-native",a);let o=await p(t,e,n,a);if(o.ok)return;b(a,o)}z(a);let o=Array.from(a).length,u=null;for(let i of[{clearPadding:12,minClear:8,maxClear:48,chunkSize:r>0?1:O,inputDelayMs:r},{clearPadding:24,minClear:16,maxClear:96,chunkSize:r>0?1:4,inputDelayMs:r>0?r:15}]){var c,s;await P(t,e,n),await D(t,"fill");let r=(c=o+i.clearPadding,s=i.minClear,Math.max(s,Math.min(i.maxClear,c)));await F(t,r),await L(t,{action:"fill",text:a,chunkSize:i.chunkSize,delayMs:i.inputDelayMs});let l=await p(t,e,n,a);if(u=l,l.ok)return;"ime_capture"===l.reason&&b(a,l)}b(a,u)}function b(t,e){let n;throw new o("COMMAND_FAILED",e?.reason==="ime_capture"?"Android fill input was captured by the active keyboard instead of the app field":e?.reason==="masked_unverified"?"Android fill verification could not confirm masked text value":"Android fill verification failed",(n=function(t,e){var n;if(!e)return{expected:t,actual:null,failureReason:"text_mismatch",targetInput:null,actualInput:null};let a=!0===(n=e).masked||s(n.targetInput)||s(n.actualInput),r={failureReason:e.reason??"text_mismatch",targetInput:d(e.targetInput),actualInput:d(e.actualInput)};return a?{...r,expectedLength:Array.from(t).length,actual:null,masked:!0,actualLength:Array.from(e.actual??"").length}:{...r,expected:t,actual:e.actual}}(t,e),e?.reason==="ime_capture"&&(n.hint="The focused input belongs to the Android keyboard/IME, not the app field. Disable handwriting/stylus input or switch to a standard IME, then retry fill."),n))}async function K(t,n,a){let r=await R(t),i=u({direction:n,amount:a?.amount,pixels:a?.pixels,referenceWidth:r.width,referenceHeight:r.height});return await e(t,["shell","input","swipe",String(i.x1),String(i.y1),String(i.x2),String(i.y2),"300"]),i}async function D(e,a){let r;try{r=await n(e)}catch(e){t({level:"warn",phase:"android_input_ownership_probe_failed",data:{action:a,error:e instanceof Error?e.message:String(e)}});return}if("ime"===r.inputOwner)throw new o("COMMAND_FAILED","KEYBOARD_OVERLAY_BLOCKING: Android text input is blocked because the focused input belongs to the active keyboard/IME.",{failureReason:"ime_capture",action:a,inputOwner:r.inputOwner,inputType:r.inputType,type:r.type,inputMethodPackage:r.inputMethodPackage,focusedPackage:r.focusedPackage,focusedResourceId:r.focusedResourceId,nextAction:"Focused input appears to be owned by the keyboard/IME; dismiss or change the IME before retrying text entry."})}async function R(t){let n=(await e(t,["shell","wm","size"])).stdout.match(/Physical size:\s*(\d+)x(\d+)/);if(!n)throw new o("COMMAND_FAILED","Unable to read screen size");return{width:Number(n[1]),height:Number(n[2])}}let O=8;async function L(t,n){let a=n.text.split("\n");for(let[r,i]of a.entries()){let o=function(t,e){let n=Math.max(1,Math.floor(e)),a=[],r=Array.from(t);for(let t=0;t<r.length;t+=n)a.push(r.slice(t,t+n).join(""));return a.length>0?a:[""]}(i,n.chunkSize);for(let[e,i]of o.entries())await U(t,i),n.delayMs>0&&(e+1<o.length||r+1<a.length)&&await c(n.delayMs);r+1<a.length&&await e(t,["shell","input","keyevent","ENTER"])}T(n.action,"adb-shell",n.text)}async function U(t,n){if(n)try{await e(t,["shell","input","text",n.replace(/ /g,"%s")])}catch(t){if(function(t){if(!(t instanceof o)||"COMMAND_FAILED"!==t.code)return!1;let e=t.details?.stderr,n=("string"==typeof e?e:"").toLowerCase();return!!(n.includes("exception occurred while executing 'text'")||n.includes("nullpointerexception")&&n.includes("inputshellcommand.sendtext"))}(t))throw H(n,t);throw t}}function z(t){if(!function(t){for(let e of t){let t=e.codePointAt(0);if(void 0!==t&&"\n"!==e&&(t<32||t>126))return!1}return!0}(t))throw H(t)}function H(t,e){return new o("COMMAND_FAILED","Android text input requires provider-native text injection for non-ASCII/control characters; the current adb-shell fallback supports ASCII text only.",{backend:"adb-shell",textLength:Array.from(t).length,textPreview:t.slice(0,32)},e instanceof Error?e:void 0)}function T(e,n,a){t({phase:"android_text_injection",data:{action:e,backend:n,textLength:Array.from(a).length}})}async function F(t,n){let a=Math.max(0,n);await e(t,["shell","input","keyevent","KEYCODE_MOVE_END"],{allowFailure:!0});for(let n=0;n<a;n+=24){let r=Math.min(24,a-n);await e(t,["shell","input","keyevent",...Array(r).fill("KEYCODE_DEL")],{allowFailure:!0})}}export{E as appSwitcherAndroid,I as backAndroid,N as fillAndroid,P as focusAndroid,R as getAndroidScreenSize,S as homeAndroid,v as longPressAndroid,A as pressAndroid,M as pressAndroidEnter,h as readAndroidTextAtPoint,k as rotateAndroid,K as scrollAndroid,x as swipeAndroid,C as typeAndroid};
@@ -1 +1 @@
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};
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 s,errorResponse as o,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 k,isDirectIosSelectorFallbackError as y,refSnapshotFlagGuardResponse as _,readSimpleIosSelectorTarget as h}from"./selector-runtime.js";import{buttonTag as w,resolveClickButton as b,readFillTargetFromPositionals as v,getClickButtonValidationError as x,successText as N}from"./6133.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 I}from"./7599.js";import{PUBLIC_COMMANDS as C}from"./6088.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:o("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,s)=>U(await a(r.device,"press",[String(s.x),String(s.y)],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath))),fill:async(n,s,o)=>U(await a(r.device,"fill",[String(s.x),String(s.y),o],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath))),longPress:async(n,s,o)=>U(await a(r.device,"longpress",[String(s.x),String(s.y),...o?.durationMs===void 0?[]:[String(o.durationMs)]],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath))),typeText:async(n,s)=>U(await a(r.device,"type",[s],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 T(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:o("INVALID_ARGS",`${t} requires @ref, selector expression, or x y coordinates`)}}function H(e){return"ref"===e.kind?{ref:K(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 K(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 o("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(!s(u,l.device))return o("UNSUPPORTED_OPERATION",`${u} is not supported on this device`);let d=b(a.flags),p=w(d);if("longpress"!==t&&"primary"!==d){let e=x({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 o(e.code,e.message,e.details)}let g="longpress"===t?function(e){var t,r,a;let n,s=D(e);if(s){return{ok:!0,target:{kind:"point",x:s.x,y:s.y},...void 0===(t=e[2])?{}:{durationMs:Number(t)}}}let o=(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=T(o.targetPositionals,"longpress");return i.ok?{ok:!0,target:i.target,...o.duration}:i}(a.positionals??[]):T(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:s}=e;if("click"!==a||"selector"!==n.kind||(t=s,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 o=h({session:r,selectorExpression:n.selector});return o?{...o,...s?.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 k="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:k}),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,s=await G({params:e,session:l,result:r,extra:"longpress"===t?{...void 0!==n?{durationMs:n}:{},gesture:"longpress"}:p});return{result:s,responseData:s}}})}async function B(e){let{runtime:t,command:r,target:a,sessionName:n,requestId:s,flags:o}=e;if("longpress"===r)return await t.interactions.longPress(a,{session:n,requestId:s,durationMs:e.durationMs});let i={session:n,requestId:s,button:e.clickButton,count:o?.count,intervalMs:o?.intervalMs,holdMs:o?.holdMs,jitterPx:o?.jitterPx,doubleTap:o?.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,s="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:s,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:s,positionals:o,extra:i,fallbackPhase:l}=e,c=Date.now();try{var u,d;let e,l,f=await a(r.device,s,o,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=M({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 O({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(!y(e))return{ok:!1,error:f(e)};return I({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&&!s("fill",n.device))return o("UNSUPPORTED_OPERATION","fill is not supported on this device");if(!n)return o("SESSION_NOT_FOUND","No active session. Run open first.");let i=function(e){let t=e[0]??"";if(t.startsWith("@")){var r;let a=v(e).text;return a?{ok:!0,target:{kind:"ref",ref:t,fallbackLabel:(r=e).length>=3&&r[1]?.trim()||""},text:a}:{ok:!1,response:o("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:o("INVALID_ARGS","fill requires text after coordinates")}}let n=v(e);return"selector"!==n.kind?{ok:!1,response:o("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:o("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:P(n.snapshot?.nodes??[]),r=M({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:K(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 o("SESSION_NOT_FOUND","No active session. Run open first.");let a=q(e),s=Date.now();try{let o=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"===o.status&&(c.warning=o.warning,u.warning=o.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 J(t.point)}(e.req.command,i),flags:e.req.flags,result:c,responseData:u,actionStartedAt:s,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){I({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:F,refSnapshotFlagGuardResponse:_});if(t)return t;switch(e.req.command){case C.type:return await Z({...e,captureSnapshotForSession:F});case"get":return await p(e);case"is":return await k(e);default:return null}}async function Z(e){let{sessionName:t,sessionStore:r}=e,a=r.get(t);if(!a)return o("SESSION_NOT_FOUND","No active session. Run open first.");if(!s(C.type,a.device))return o("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?o("COMMAND_FAILED","Android system dialog blocked the recording session"):null}async function et(e,t){let{req:r,sessionName:a,sessionStore:s}=e,o=(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(o,{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,...N(c.message??`Typed ${Array.from(c.text).length} chars`)};return"recovered"===e.status&&(f.warning=e.warning),O({session:t,sessionStore:s,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};
@@ -1 +1 @@
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};
1
+ import{dispatchCommand as e,errorResponse as t,isCommandSupportedOnDevice as i}from"./2415.js";import{PUBLIC_COMMANDS as a}from"./6088.js";import{isSparseSnapshotQualityVerdict as s,analyzeReactNativeOverlay as r,successText as n,parsing_stripUndefined as o}from"./6133.js";import{normalizeError as d}from"./9152.js";import{interaction_snapshot_captureSnapshotForSession as l,readSnapshotNodesReferenceFrame as c,finalizeTouchInteraction as v}from"./9471.js";async function f(e){var o,c;let{req:v,sessionName:f,sessionStore:u}=e;if(v.command!==a.reactNative)return null;let y=1===(o=v.positionals??[]).length&&"dismiss-overlay"===o[0]?{ok:!0}:{ok:!1,response:t("INVALID_ARGS","react-native supports only: dismiss-overlay")};if(!y.ok)return y.response;let p=u.get(f);if(!p)return t("SESSION_NOT_FOUND","No active session. Run open first.");if(!i(a.reactNative,p.device))return t("UNSUPPORTED_OPERATION","react-native dismiss-overlay is not supported on this device");try{let i=await l(p,v.flags,u,e.contextFromFlags,{interactiveOnly:!0});if(s(i.snapshotQuality)){return c=i.snapshotQuality,t("COMMAND_FAILED","React Native overlay state could not be determined because the accessibility tree is unreadable",{reason:c.reason,hint:"The snapshot quality verdict is sparse. Use screenshot as visual truth; if an overlay is visible, report it or navigate with coordinates, then retry snapshot or dismiss-overlay on a readable screen."})}let a=r(i.nodes),o=a.primaryAction;if(!o)return a.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,...n("No React Native overlay detected")}};return await m(e,p,i,o)}catch(e){return{ok:!1,error:d(e)}}}async function m(t,i,a,s){let{req:r,sessionStore:d}=t,l=Date.now(),f=await e(i.device,"press",[String(s.point.x),String(s.point.y)],r.flags?.out,t.contextFromFlags(r.flags,i.appBundleId,i.trace?.outPath))??{},m=Date.now(),y=await u(t,i),p=o({...c(a.nodes),...f,action:"dismiss-overlay",overlayAction:s.action,x:s.point.x,y:s.point.y,ref:s.ref,label:s.label,warning:s.warning,dismissed:!0,verified:y.verified,verificationRequired:!y.verified,verificationWarning:y.verificationWarning,nextCommand:y.nextCommand,...n(y.verified?"React Native overlay dismiss action sent and verified gone":"React Native overlay dismiss action sent, but verification still detects an overlay")});return v({session:i,sessionStore:d,command:r.command,positionals:r.positionals??[],flags:r.flags,result:p,responseData:p,actionStartedAt:l,actionFinishedAt:m})}async function u(e,t){let{req:i,sessionStore:a}=e,n=await l(t,i.flags,a,e.contextFromFlags,{interactiveOnly:!0});return s(n.snapshotQuality)?{verified:!1,verificationWarning:"React Native overlay dismissal could not be verified because the post-dismiss accessibility tree is unreadable. Use screenshot as visual truth.",nextCommand:"agent-device screenshot"}:r(n.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{f as handleReactNativeCommands};
@@ -1,4 +1,4 @@
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=`
1
+ let e,t,r,i;import a from"node:fs";import o from"node:path";import{createHash as n}from"node:crypto";import s from"node:os";import{fileURLToPath as c}from"node:url";import{AppError as d}from"./9152.js";import{runCmd as l}from"./9818.js";import{runXcrun as u,SessionStore as m,runIosRunnerCommand as f,isCommandSupportedOnDevice as p,errorResponse as h,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 M,emitDiagnostic as b}from"./7599.js";import{androidDeviceForSerial as C,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=N(),r=o.join(t,"home"),i=o.join(t,"module-cache");return a.mkdirSync(r,{recursive:!0}),a.mkdirSync(i,{recursive:!0}),{...e,HOME:r,CLANG_MODULE_CACHE_PATH:i}}async function R(e){let t=a.statSync(e.sourcePath),r=a.readFileSync(e.sourcePath),i=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(N(),"bin",`${i}-${n}`);return await E({sourcePath:e.sourcePath,executablePath:s,timeoutMs:e.timeoutMs}),s}async function k(e){let t=L(e.cacheName),r=T(["2",process.platform,process.arch,e.source]),i=o.join(N(),"sources",`${t}-${r}.swift`),a=o.join(N(),"bin",`${t}-${r}`);return await E({sourcePath:i,executablePath:a,sourceText:e.source,timeoutMs:e.timeoutMs}),a}function N(){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 E(e){if(O(e.executablePath))return;let t=o.dirname(e.executablePath);a.mkdirSync(t,{recursive:!0});let r=`${e.executablePath}.lock`,i=await F(r,e.executablePath,e.timeoutMs??12e4);if(!i)return;let n=a.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||a.existsSync(e.sourcePath)||(a.mkdirSync(o.dirname(e.sourcePath),{recursive:!0}),a.writeFileSync(e.sourcePath,e.sourceText)),await l("xcrun",["swiftc",e.sourcePath,"-o",s],{timeoutMs:e.timeoutMs??12e4,env:$()}),a.renameSync(s,e.executablePath)}finally{a.rmSync(n,{recursive:!0,force:!0}),await i()}}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 d&&t.message.startsWith("Timed out waiting for Swift cache lock:"))throw new d("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 a.accessSync(e,a.constants.X_OK),a.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
2
  import Foundation
3
3
  import AVFoundation
4
4
 
@@ -22,5 +22,5 @@ Task {
22
22
 
23
23
  semaphore.wait()
24
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};
25
+ `.trim();async function z(e,t={}){let r,i=t.pollMs??150,o=t.attempts??12,n=0;for(let t=0;t<o;t+=1){let t=0;try{t=a.statSync(e).size}catch{t=0}if(t>0&&t===r){if((n+=1)>=2)return}else n=0;r=t,await A(i)}}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 d&&("TOOL_MISSING"===t.code||V(String(t.details?.stderr??""),String(t.details?.stdout??""))))return H(e);throw r}}async function K(){e??=k({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,i=t.attempts??12;for(let t=0;t<i;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=a.statSync(e);if(!t.isFile()||t.size<=0)return!1}catch{return!1}let t=function(e){try{let t=a.openSync(e,"r");try{let e=a.fstatSync(t).size,r=0,i=[];for(;r+8<=e&&i.length<16;){let e=Buffer.alloc(8);if(8>a.readSync(t,e,0,8,r))break;let o=e.readUInt32BE(0),n=e.toString("latin1",4,8);if(i.push(n),1===o){let e=Buffer.alloc(8);if(8>a.readSync(t,e,0,8,r+8))break;o=Number(e.readBigUInt64BE(0))}if(!Number.isFinite(o)||o<=0)break;r+=o}return i}finally{a.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,i;let o,n,{recording:s,trimStartMs:c}=e,d=(o=j((t={videoPath:s.outPath,events:s.gestureEvents,trimStartMs:c}).videoPath),n={version:1,generatedAt:new Date().toISOString(),events:(r=t.events,(i=t.trimStartMs??0)>0?W(r.flatMap(e=>{let t=e.tMs-i,r="durationMs"in e?e.durationMs:void 0;return("number"==typeof r?t+r:t)<=0?[]:[{...e,tMs:Math.max(0,t)}]})):W(r))},a.writeFileSync(o,JSON.stringify(n,null,2)),o);return s.telemetryPath=d,d}function J(e){let t=o.dirname(c(import.meta.url)),r=[c(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(a.existsSync(e))return e;throw new d("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,i,{videoPath:n,scriptPath:s,scriptArgs:c,commandDescription:u}=e;await z(n),await G(n);let m=(t=n,r=o.parse(t),i=`${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,o.join(r.dir,`.${r.name}.agent-device-${i}${r.ext||".mp4"}`));try{let e=await R({sourcePath:s});await l(e,["--input",n,"--output",m,...c],{timeoutMs:12e4,env:$()}),await G(m),a.renameSync(m,n)}catch(t){let e=t instanceof d?t:new d("COMMAND_FAILED",String(t),void 0,t instanceof Error?t:void 0);throw new d("COMMAND_FAILED",u,{...e.details,videoPath:n,script:s},e)}finally{a.rmSync(m,{force:!0})}}async function Z(e){let{videoPath:t,trimStartMs:i}=e;i>0&&await X({videoPath:t,scriptPath:r??=J("recording-trim.swift"),scriptArgs:["--trim-start-ms",String(i)],commandDescription:"Failed to trim the start of the iOS recording"})}async function Q(e){let{videoPath:r,telemetryPath:i,targetLabel:a="recording"}=e;await X({videoPath:r,scriptPath:t??=J("recording-overlay.swift"),scriptArgs:["--events",i],commandDescription:`Failed to add touch overlays to the ${a}`})}async function Y(e){let{videoPath:t,quality:r,targetLabel:a="recording"}=e;await X({videoPath:t,scriptPath:i??=J("recording-resize.swift"),scriptArgs:["--quality",String(r)],commandDescription:`Failed to resize the ${a}`})}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 i=Math.max(0,r-t.startedAt);return i>=1e3?{message:e,tooShort:!1}:{message:`${e}. Recording stopped after ${Math.round(i)}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:i,targetLabel:a}=e,o=B({recording:t,trimStartMs:i});if(!t.showTouches)return void b({level:"debug",phase:"record_stop_overlay_skipped",data:{reason:"hide_touches"}});if(0===t.gestureEvents.length)return void b({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 M("record_stop_overlay_export",()=>r.overlayRecordingTouches({videoPath:t.outPath,telemetryPath:o,targetLabel:a}),{targetLabel:a,gestureEventCount:t.gestureEvents.length})}catch(e){t.overlayWarning??=`failed to overlay recording touches: ${ee(e)}`}}function ea(e,t){if(1===t)return e;let r=o.parse(e),i=r.ext||".mp4";return o.join(r.dir,`${r.name}.part-${String(t).padStart(3,"0")}${i}`)}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:i}=e;if(t.stopping)return;let a=await i();if(a)throw Error(a);if(t.stopping)return;let n=eo(t),s=n.length+1,c=await r(o.posix.dirname(t.remotePath));t.remotePath=c.remotePath,t.remotePid=c.remotePid,n.push({index:s,path:ea(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 es(e){let{recording:t,deps:r}=e;eo(t).length<=1?await ei({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 ec(e){for(let t of e.chunks){let r=await ed({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 ed(e){let t,{deps:r,deviceId:i,remotePath:o,outPath:n}=e;for(let e=0;e<2;e+=1){el(n);let s=C(i),c=await x(o,n,{allowFailure:!0,device:s});if(0!==c.exitCode)t=et(c,"adb pull");else{await r.waitForStableFile(n,{pollMs:250,attempts:20});let t=await r.isPlayableVideo(n);if(b({level:"debug",phase:"record_stop_android_pull_validation",data:{deviceId:i,remotePath:o,outPath:n,attempt:e+1,fileSize:function(e){try{return a.statSync(e).size}catch{return 0}}(n),playable:t}}),t)return;b({level:"warn",phase:"record_stop_android_invalid_video_retry",data:{deviceId:i,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{a.rmSync(e,{force:!0})}catch{}}async function eu(e,t,r){return await I(C(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 ep(e,t){let r,i=0;for(let a=0;a<20;a+=1){let a=await eu(e,["shell","stat","-c","%s",t],{allowFailure:!0}),o=0===a.exitCode?a.stdout.trim():"";if(o.length>0&&o===r){if((i+=1)>=4)return}else i=0;r=o,await A(250)}}async function eh(e,t,r){for(let i=0;i<8;i+=1){let a=await eu(e,["shell","stat","-c","%s",t],{allowFailure:!0}),o=0===a.exitCode?Number(a.stdout.trim()):NaN;if(Number.isFinite(o)&&o>0)return!0;if(!await em(e,r))break;if(i+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 i=await eu(t,["shell","wm","size"],{allowFailure:!0}),a=i.stdout.match(/Override size:\s*(\d+)x(\d+)/)??i.stdout.match(/Physical size:\s*(\d+)x(\d+)/);if(0!==i.exitCode||!a)throw Error(`failed to resolve Android screen size for recording quality: ${et(i,"adb shell wm size")}`);return{width:eg(Number(a[1]),r),height:eg(Number(a[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 b({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,i,{device:a,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`,i=["/sdcard","/data/local/tmp"],(n&&i.includes(n)?[n,...i.filter(e=>e!==n)]:i).map(e=>`${e}/${r}`))){let t=await eu(a.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(a.id,e);continue}if(b({level:"debug",phase:"record_start_android_started",data:{deviceId:a.id,remotePath:e,remotePid:r}}),await eh(a.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_(a.id,r),await ey(a.id,e)}return{error:h("COMMAND_FAILED",s)}}async function eP(e){let t,{device:r,recordingBase:i}=e;try{t=await ew({deviceId:r.id,quality:i.quality})}catch(e){return h("COMMAND_FAILED",e instanceof Error?e.message:String(e))}let a=await ev({device:r,recordingSize:t});if("error"in a)return a.error;let o={platform:"android",remotePath:a.remotePath,remotePid:a.remotePid,chunks:[{index:1,path:i.outPath,remotePath:a.remotePath}],...i,startedAt:a.startedAt};return!function e(t){let{recording:r,startNextChunk:i,finishCurrentChunk:a}=t,o=setTimeout(()=>{r.rotationPromise=en({recording:r,startNextChunk:i,finishCurrentChunk:a}).catch(e=>{r.rotationFailedReason=e instanceof Error?e.message:String(e)}).finally(()=>{r.rotationPromise=void 0,r.stopping||r.rotationFailedReason||e({recording:r,startNextChunk:i,finishCurrentChunk:a})})},17e4);o.unref?.(),r.rotationTimer=o}({recording:o,finishCurrentChunk:async()=>await eS({device:r,recording:o,waitForRemoteFileStability:!1}),startNextChunk:async e=>{let i=await ev({device:r,recordingSize:t,preferredRemoteDir:e});if("error"in i)throw Error(i.error.ok?"failed to start next Android recording chunk":i.error.error.message);return i}}),o}async function eS(e){let{device:t,recording:r,waitForRemoteFileStability:i=!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 a=await eu(t.id,["shell","kill","-2",r.remotePid],{allowFailure:!0});if(b({level:"debug",phase:"record_stop_android_signal",data:{deviceId:t.id,remotePath:r.remotePath,remotePid:r.remotePid,exitCode:a.exitCode,stdout:a.stdout.trim(),stderr:a.stderr.trim()}}),0!==a.exitCode)return await eA(t.id,r.remotePid,a);let o=await eM(t.id,r.remotePid);if(o)return o;i&&await ep(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 eM(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 eb(e){let t,{deps:r,device:i,recording:a,stopRequestedAt:o}=e;b({level:"debug",phase:"record_stop_android_enter",data:{deviceId:i.id,remotePath:a.remotePath,remotePid:a.remotePid}}),a.stopping=!0,a.rotationTimer&&(clearTimeout(a.rotationTimer),a.rotationTimer=void 0),await a.rotationPromise;let n=await eS({device:i,recording:a});if(a.rotationFailedReason&&!n&&(a.warning??=`Android recording chunk rotation failed: ${a.rotationFailedReason}`),!n){let e=await ec({deps:r,deviceId:i.id,chunks:eo(a)});if(e)return await s(),h("COMMAND_FAILED",eC(e,a,o));await es({recording:a,deps:r})}if(await s(),n)return h("COMMAND_FAILED",eC(n,a,o));if(t)return h("COMMAND_FAILED",t);return null;async function s(){for(let e of eo(a)){let r=await eu(i.id,["shell","rm","-f",e.remotePath],{allowFailure:!0});b({level:"debug",phase:"record_stop_android_cleanup",data:{deviceId:i.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 eC(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:i,logPath:a,deps:o}=e,n=eI(r);try{await o.runIosRunnerCommand(i,{command:"recordStop",appBundleId:n},ex(t,a,r))}catch(e){b({level:"warn",phase:"record_stop_runner_failed",data:{platform:i.platform,kind:i.kind,deviceId:i.id,session:r.name,error:ee(e)}})}}async function e$(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o}=e,n=eI(r);if(n)try{let e=await o.runIosRunnerCommand(i,{command:"snapshot",appBundleId:n,interactiveOnly:!0,depth:1},ex(t,a,r)),s=Date.now();if("number"==typeof e.currentUptimeMs&&Number.isFinite(e.currentUptimeMs)&&e.currentUptimeMs>0)return b({level:"debug",phase:"record_start_gesture_clock_anchor",data:{source:"warm_snapshot"}}),{gestureClockOriginAtMs:s,gestureClockOriginUptimeMs:e.currentUptimeMs};return}catch(e){b({level:"warn",phase:"record_start_simulator_runner_warm_failed",data:{deviceId:i.id,session:r.name,appBundleId:n,error:ee(e)}});return}}async function eR(e){let t,r,{req:i,activeSession:a,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=ex(i,s,a),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(i){var g,y;if(!ee(i).toLowerCase().includes("recording already in progress"))return h("COMMAND_FAILED",`failed to start recording: ${ee(i)}`);b({level:"warn",phase:"record_start_runner_desynced",data:{platform:n.platform,kind:n.kind,deviceId:n.id,session:a.name,error:ee(i)}});let e=(g=n.id,y=a.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 h("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 h("COMMAND_FAILED",`failed to start recording: ${ee(e)}`)}}return{platform:"ios-device-runner",remotePath:f,runnerStartedAtUptimeMs:t,targetAppReadyUptimeMs:r,...l}}async function ek(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o,fpsFlag:n,recordingBase:s,appBundleId:c}=e;try{await o.runIosRunnerCommand(i,{command:"recordStart",outPath:s.outPath,fps:n,quality:s.quality,appBundleId:c},ex(t,a,r))}catch(e){return h("COMMAND_FAILED",`failed to start recording: ${ee(e)}`)}return{platform:"macos-runner",...s}}async function eN(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o,recording:n}=e;await eD({req:t,activeSession:r,device:i,logPath:a,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",i.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 h("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 eE(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o,recording:n}=e;return await eD({req:t,activeSession:r,device:i,logPath:a,deps:o}),await ei({recording:n,deps:o,targetLabel:"macOS recording"}),null}async function eF(e){let{deps:t,recording:r}=e;r.child.kill("SIGINT");let i=await eO(r.wait,5e3);return i||(await eL(t,r,"SIGINT"),(i=await eO(r.wait,2e3))||(r.child.kill("SIGTERM"),await eL(t,r,"SIGTERM"),i=await eO(r.wait,2e3)))?i:(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 i,a=`simctl.*recordVideo.*${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}`;try{i=await e.runCmd("pgrep",["-f",a],{allowFailure:!0})}catch(e){b({level:"warn",phase:"record_stop_ios_simulator_pgrep_failed",data:{outPath:t,signal:r,error:ee(e)}});return}let o=eq(eG(i.stdout)),n=eK(o,r);b({level:n>0?"warn":"debug",phase:"record_stop_ios_simulator_signal_recorders",data:{outPath:t,signal:r,matchedPidCount:o.length,signaled:n,pgrepExitCode:i.exitCode}})}async function eU(e,t,r){let i=t.recorderPid??t.child.pid;if("number"!=typeof i||!Number.isInteger(i)||i<=0)return b({level:"debug",phase:"record_stop_ios_simulator_owned_recorder_unavailable",data:{outPath:t.outPath,signal:r,reason:"missing_recorder_pid"}}),!1;let a=await ez(e,i,t.outPath,r),o=eq([i,...a.pids]),n=eK(o,r);return b({level:n>0?"warn":"debug",phase:"record_stop_ios_simulator_signal_owned_recorder",data:{outPath:t.outPath,signal:r,recorderPid:i,childPidCount:a.pids.length,matchedPidCount:o.length,signaled:n,pgrepExitCode:a.exitCode}}),n>0}async function ez(e,t,r,i){let a;try{a=await e.runCmd("pgrep",["-P",String(t)],{allowFailure:!0})}catch(e){return b({level:"warn",phase:"record_stop_ios_simulator_owned_pgrep_failed",data:{outPath:r,signal:i,parentPid:t,error:ee(e)}}),{pids:[]}}return{pids:eG(a.stdout),exitCode:a.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 i of e)try{process.kill(i,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(a.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:i,activeSession:a,device:o,logPath:n,deps:s,recordingBase:c,resolvedOut:d}=e,l=c.showTouches?await e$({req:i,activeSession:a,device:o,logPath:n,deps:s}):void 0,{child:u,wait:m}=s.startIosSimulatorRecording({device:o,outPath:d}),f=await eH(d);if(l)t=l.gestureClockOriginAtMs,r=l.gestureClockOriginUptimeMs;else if(c.showTouches)try{let e=Date.now(),c=await s.runIosRunnerCommand(o,{command:"uptime",appBundleId:eI(a)},ex(i,n,a)),d=Date.now();t=Math.round((e+d)/2),r="number"==typeof c.currentUptimeMs?c.currentUptimeMs:void 0}catch{}return{platform:"ios",child:u,wait:m,...c,recorderPid:u.pid,startedAt:f,gestureClockOriginAtMs:void 0===r?void 0:t,gestureClockOriginUptimeMs:r}}async function eW(e){let t,{req:r,sessionName:i,sessionStore:n,activeSession:s,device:c,logPath:d,deps:l}=e;if(s.recording)return h("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 h("INVALID_ARGS","fps must be an integer between 1 and 120");if(void 0!==f&&(!Number.isInteger(f)||f<5||f>10))return h("INVALID_ARGS","quality must be an integer between 5 and 10");if(!p("record",c))return h("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(a.mkdirSync(o.dirname(g),{recursive:!0}),a.rmSync(g,{force:!0}),"ios"===c.platform&&"device"===c.kind){let e=eI(s);if(!e)return h("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:c,logPath:d,deps:l,fpsFlag:u,recordingBase:y,appBundleId:e})}else if("macos"===c.platform){let e=eI(s);if(!e)return h("INVALID_ARGS","record on macOS requires an active app session; run open <app> first");t=await ek({req:r,activeSession:s,device:c,logPath:d,deps:l,fpsFlag:u,recordingBase:y,appBundleId:e})}else t="ios"===c.platform?await ej({req:r,activeSession:s,device:c,logPath:d,deps:l,recordingBase:y,resolvedOut:g}):await eP({device:c,recordingBase:y});if("ok"in t)return t;s.recording=t,n.set(i,s);let _=n.ensureSessionDir(i);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:i,stopRequestedAt:a}=e;if("android"===i.platform)return await eb({deps:t,device:r,recording:i,stopRequestedAt:a});await M("record_stop_tail_settle",()=>t.waitForRecordingTail(i),{platform:i.platform,gestureEventCount:i.gestureEvents.length});let o=await M("record_stop_ios_simulator_process",()=>eF({deps:t,recording:i}),{outPath:i.outPath});if(!o)return eJ("failed to stop recording: simctl recordVideo did not exit after 5000ms and forced cleanup",i,a);if(0!==o.exitCode)return eJ(`failed to stop recording: ${et(o,"simctl recordVideo")}`,i,a);if(await M("record_stop_video_stable",()=>t.waitForStableFile(i.outPath,{pollMs:150,attempts:12}),{outPath:i.outPath}),!await M("record_stop_video_playable_check",()=>t.isPlayableVideo(i.outPath),{outPath:i.outPath}))return eJ(`failed to stop recording: ${i.outPath} was not finalized into a playable video`,i,a);if(void 0!==i.quality&&i.quality<10){let e=i.quality;try{await M("record_stop_resize",()=>t.resizeRecording({videoPath:i.outPath,quality:e,targetLabel:"iOS recording"}),{outPath:i.outPath,quality:e})}catch(e){i.overlayWarning=`failed to resize recording: ${ee(e)}`}}return await M("record_stop_finalize_overlay",()=>ei({recording:i,deps:t,targetLabel:"iOS recording"}),{outPath:i.outPath,showTouches:i.showTouches,gestureEventCount:i.gestureEvents.length}),null}function eJ(e,t,r){let i=er(e,t,r);return function(e){try{a.rmSync(e,{force:!0})}catch{}}(t.outPath),h("COMMAND_FAILED",i.message)}async function eX(e){var t;let r,i,{req:a,activeSession:n,device:s,logPath:c,deps:d}=e;if(!n.recording)return h("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 eN({req:a,activeSession:n,device:s,logPath:c,deps:d,recording:l}):"macos-runner"===l.platform?await eE({req:a,activeSession:n,device:s,logPath:c,deps:d,recording:l}):await eB({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 h("COMMAND_FAILED",m);return r="android"===(t=l).platform?t.chunks:void 0,i=[{field:"outPath",path:t.outPath,localPath:t.clientOutPath,fileName:o.basename(t.clientOutPath??t.outPath)}],r&&r.length>1&&i.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&&i.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:i,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 ea(e.clientOutPath,t)}function eQ(e,t,r,i={}){r.recordOnlySession&&(i.writeLog&&e.writeSessionLog(r),e.delete(t))}async function eY(e){let{req:t,sessionName:r,sessionStore:i,logPath:a}=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=i.get(r),s=n?.device??await v(t.flags??{});n||await g(s);let c=n??{name:y(t),sessionScope:_(t),device:s,createdAt:Date.now(),recordOnlySession:!0,actions:[]},d=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(d))return h("INVALID_ARGS","record requires start|stop");if("start"===d)return eW({req:t,sessionName:r,sessionStore:i,activeSession:c,device:s,logPath:a,deps:o});let m=await eX({req:t,activeSession:c,device:s,logPath:a,deps:o});return m.ok?(i.recordAction(c,{command:t.command,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:m.data?.outPath,showTouches:m.data?.showTouches}}),eQ(i,r,c,{writeLog:!0})):eQ(i,r,c),m}export{eY as handleRecordCommand};