agent-device 0.17.10 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.17.10.apk → agent-device-android-multitouch-helper-0.18.0.apk} +0 -0
  2. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.18.0.apk.sha256 +1 -0
  3. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.17.10.manifest.json → agent-device-android-multitouch-helper-0.18.0.manifest.json} +4 -4
  4. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.17.10.apk → agent-device-android-snapshot-helper-0.18.0.apk} +0 -0
  5. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.18.0.apk.sha256 +1 -0
  6. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.17.10.manifest.json → agent-device-android-snapshot-helper-0.18.0.manifest.json} +6 -6
  7. package/dist/src/1848.js +4 -0
  8. package/dist/src/2577.js +1 -1
  9. package/dist/src/2948.js +20 -20
  10. package/dist/src/4291.js +1 -1
  11. package/dist/src/4723.js +1 -1
  12. package/dist/src/5341.js +1 -0
  13. package/dist/src/5643.js +1 -0
  14. package/dist/src/6918.js +1 -1
  15. package/dist/src/8275.js +2 -2
  16. package/dist/src/8936.js +1 -0
  17. package/dist/src/9414.js +1 -0
  18. package/dist/src/9581.js +4 -4
  19. package/dist/src/9722.js +32 -37
  20. package/dist/src/9919.js +5 -5
  21. package/dist/src/apple.js +1 -1
  22. package/dist/src/apps.js +10 -10
  23. package/dist/src/batch.d.ts +20 -3
  24. package/dist/src/batch.js +1 -1
  25. package/dist/src/cli-help.js +49 -32
  26. package/dist/src/cli.js +22 -25
  27. package/dist/src/contracts.d.ts +14 -0
  28. package/dist/src/device-ready.js +2 -0
  29. package/dist/src/find.js +1 -1
  30. package/dist/src/index.d.ts +67 -16
  31. package/dist/src/interaction.js +1 -1
  32. package/dist/src/internal/bin.js +1 -1
  33. package/dist/src/lease.js +1 -1
  34. package/dist/src/metro.js +1 -1
  35. package/dist/src/react-native.js +1 -1
  36. package/dist/src/record-trace-recording.js +3 -3
  37. package/dist/src/remote-config.d.ts +17 -11
  38. package/dist/src/selector-runtime.js +1 -1
  39. package/dist/src/session.js +11 -8
  40. package/dist/src/simulator.js +2 -0
  41. package/dist/src/snapshot.js +1 -1
  42. package/package.json +1 -1
  43. package/server.json +2 -2
  44. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.17.10.apk.sha256 +0 -1
  45. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.17.10.apk.sha256 +0 -1
  46. package/dist/src/2133.js +0 -1
  47. package/dist/src/5628.js +0 -1
  48. package/dist/src/8358.js +0 -4
@@ -65,7 +65,7 @@ export declare type AgentDeviceClient = {
65
65
  test: (options: ReplayTestOptions) => Promise<CommandRequestResult>;
66
66
  };
67
67
  batch: {
68
- run: (options: BatchRunOptions) => Promise<CommandRequestResult>;
68
+ run: (options: BatchRunOptions) => Promise<BatchRunResult>;
69
69
  };
70
70
  observability: {
71
71
  perf: (options?: PerfOptions) => Promise<CommandRequestResult>;
@@ -84,21 +84,14 @@ export declare type AgentDeviceClient = {
84
84
  };
85
85
  };
86
86
 
87
- export declare type AgentDeviceClientConfig = {
87
+ export declare type AgentDeviceClientConfig = RemoteConnectionProfileFields & {
88
88
  session?: string;
89
89
  lockPolicy?: DaemonLockPolicy;
90
90
  lockPlatform?: PlatformSelector;
91
91
  requestId?: string;
92
- stateDir?: string;
93
- daemonBaseUrl?: string;
94
- daemonAuthToken?: string;
95
- daemonTransport?: DaemonTransportPreference;
96
- daemonServerMode?: DaemonServerMode;
97
- tenant?: string;
98
92
  sessionIsolation?: SessionIsolationMode;
99
- runId?: string;
100
- leaseId?: string;
101
93
  leaseBackend?: LeaseBackend;
94
+ leaseTtlMs?: number;
102
95
  runtime?: SessionRuntimeHints;
103
96
  cwd?: string;
104
97
  debug?: boolean;
@@ -151,7 +144,7 @@ export declare type AgentDeviceIdentifiers = {
151
144
  package?: string;
152
145
  };
153
146
 
154
- export declare type AgentDeviceRequestOverrides = Pick<AgentDeviceClientConfig, 'session' | 'lockPolicy' | 'lockPlatform' | 'requestId' | 'daemonBaseUrl' | 'daemonAuthToken' | 'daemonTransport' | 'daemonServerMode' | 'tenant' | 'sessionIsolation' | 'runId' | 'leaseId' | 'leaseBackend' | 'cwd' | 'debug' | 'iosXctestrunFile' | 'iosXctestDerivedDataPath' | 'iosXctestEnvDir'>;
147
+ export declare type AgentDeviceRequestOverrides = Pick<AgentDeviceClientConfig, 'session' | 'lockPolicy' | 'lockPlatform' | 'requestId' | 'daemonBaseUrl' | 'daemonAuthToken' | 'daemonTransport' | 'daemonServerMode' | 'tenant' | 'sessionIsolation' | 'runId' | 'leaseId' | 'leaseBackend' | 'leaseProvider' | 'deviceKey' | 'clientId' | 'leaseTtlMs' | 'cwd' | 'debug' | 'iosXctestrunFile' | 'iosXctestDerivedDataPath' | 'iosXctestEnvDir'>;
155
148
 
156
149
  export declare type AgentDeviceSelectionOptions = {
157
150
  platform?: PlatformSelector;
@@ -258,7 +251,7 @@ export declare type AppCloseOptions = AgentDeviceRequestOverrides & {
258
251
  export declare type AppCloseResult = {
259
252
  session: string;
260
253
  closedApp?: string;
261
- shutdown?: Record<string, unknown>;
254
+ shutdown?: TargetShutdownResult;
262
255
  identifiers: AgentDeviceIdentifiers;
263
256
  };
264
257
 
@@ -412,10 +405,25 @@ export declare type BatchRunOptions = AgentDeviceRequestOverrides & {
412
405
  out?: string;
413
406
  };
414
407
 
408
+ export declare type BatchRunResult = Record<string, unknown> & {
409
+ total: number;
410
+ executed: number;
411
+ totalDurationMs: number;
412
+ results: BatchStepResult[];
413
+ };
414
+
415
415
  export declare type BatchStep = {
416
416
  command: string;
417
417
  input: Record<string, unknown>;
418
- runtime?: unknown;
418
+ runtime?: SessionRuntimeHints;
419
+ };
420
+
421
+ declare type BatchStepResult = {
422
+ step: number;
423
+ command: string;
424
+ ok: true;
425
+ data: Record<string, unknown>;
426
+ durationMs: number;
419
427
  };
420
428
 
421
429
  export declare type CaptureDiffOptions = DeviceCommandBaseOptions & Pick<CaptureSnapshotOptions, 'interactiveOnly' | 'depth' | 'scope' | 'raw'> & {
@@ -432,9 +440,8 @@ export declare type CaptureScreenshotOptions = AgentDeviceRequestOverrides & {
432
440
  surface?: SessionSurface;
433
441
  };
434
442
 
435
- export declare type CaptureScreenshotResult = {
443
+ export declare type CaptureScreenshotResult = ScreenshotResultData & {
436
444
  path: string;
437
- overlayRefs?: ScreenshotOverlayRef[];
438
445
  identifiers: AgentDeviceIdentifiers;
439
446
  };
440
447
 
@@ -571,6 +578,9 @@ declare type DaemonRequestMeta = {
571
578
  leaseId?: string;
572
579
  leaseTtlMs?: number;
573
580
  leaseBackend?: LeaseBackend;
581
+ leaseProvider?: string;
582
+ deviceKey?: string;
583
+ clientId?: string;
574
584
  sessionIsolation?: SessionIsolationMode;
575
585
  uploadedArtifactId?: string;
576
586
  clientArtifactPaths?: Record<string, string>;
@@ -784,6 +794,9 @@ declare type Lease = {
784
794
  tenantId: string;
785
795
  runId: string;
786
796
  backend: LeaseBackend;
797
+ leaseProvider?: string;
798
+ deviceKey?: string;
799
+ clientId?: string;
787
800
  createdAt?: number;
788
801
  heartbeatAt?: number;
789
802
  expiresAt?: number;
@@ -795,6 +808,10 @@ declare type LeaseAllocateOptions = LeaseOptions & {
795
808
  tenant: string;
796
809
  runId: string;
797
810
  leaseBackend?: LeaseBackend;
811
+ leaseProvider?: string;
812
+ provider?: string;
813
+ deviceKey?: string;
814
+ clientId?: string;
798
815
  };
799
816
 
800
817
  declare type LeaseBackend = (typeof LEASE_BACKENDS)[number];
@@ -807,6 +824,11 @@ declare type LeaseScopedOptions = LeaseOptions & {
807
824
  tenant?: string;
808
825
  runId?: string;
809
826
  leaseId: string;
827
+ leaseBackend?: LeaseBackend;
828
+ leaseProvider?: string;
829
+ provider?: string;
830
+ deviceKey?: string;
831
+ clientId?: string;
810
832
  };
811
833
 
812
834
  export declare type LocalArtifactAdapterOptions = {
@@ -1081,6 +1103,22 @@ declare type ReloadMetroResult = {
1081
1103
  body: string;
1082
1104
  };
1083
1105
 
1106
+ declare type RemoteConnectionProfileFields = {
1107
+ stateDir?: string;
1108
+ daemonBaseUrl?: string;
1109
+ daemonAuthToken?: string;
1110
+ daemonTransport?: DaemonTransportPreference;
1111
+ daemonServerMode?: DaemonServerMode;
1112
+ tenant?: string;
1113
+ sessionIsolation?: SessionIsolationMode;
1114
+ runId?: string;
1115
+ leaseId?: string;
1116
+ leaseBackend?: LeaseBackend;
1117
+ leaseProvider?: string;
1118
+ deviceKey?: string;
1119
+ clientId?: string;
1120
+ };
1121
+
1084
1122
  declare type RepeatedPressOptions = {
1085
1123
  count?: number;
1086
1124
  intervalMs?: number;
@@ -1165,6 +1203,11 @@ export declare type ScreenshotOverlayRef = {
1165
1203
  center: Point;
1166
1204
  };
1167
1205
 
1206
+ declare type ScreenshotResultData = {
1207
+ path?: string;
1208
+ overlayRefs?: ScreenshotOverlayRef[];
1209
+ };
1210
+
1168
1211
  declare const SCROLL_DIRECTIONS: readonly ['up', 'down', 'left', 'right'];
1169
1212
 
1170
1213
  declare const SCROLL_INPUT_DIRECTIONS: readonly ['up', 'down', 'left', 'right', 'top', 'bottom'];
@@ -1197,7 +1240,7 @@ declare const SESSION_SURFACES: readonly ['app', 'frontmost-app', 'desktop', 'me
1197
1240
 
1198
1241
  export declare type SessionCloseResult = {
1199
1242
  session: string;
1200
- shutdown?: Record<string, unknown>;
1243
+ shutdown?: TargetShutdownResult;
1201
1244
  identifiers: AgentDeviceIdentifiers;
1202
1245
  };
1203
1246
 
@@ -1348,6 +1391,14 @@ declare type SwipePattern = (typeof SWIPE_PATTERNS)[number];
1348
1391
 
1349
1392
  declare type SwipePreset = (typeof SWIPE_PRESETS)[number];
1350
1393
 
1394
+ export declare type TargetShutdownResult = {
1395
+ success: boolean;
1396
+ exitCode: number;
1397
+ stdout: string;
1398
+ stderr: string;
1399
+ error?: NormalizedError;
1400
+ };
1401
+
1351
1402
  export declare type TemporaryFile = {
1352
1403
  path: string;
1353
1404
  visibility: 'internal';
@@ -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"./9722.js";import{buttonTag as c,resolveClickButton as f,readFillTargetFromPositionals as u,getClickButtonValidationError as d,successText as p}from"./2948.js";import{createAgentDevice as g}from"./2577.js";import{asAppError as m,normalizeError as k,AppError as S}from"./733.js";import{dispatchGetViaRuntime as _,isAndroidEscapeError as y,createDaemonRuntimePolicy as w,assertAndroidPressStayedInApp as h,dispatchIsViaRuntime as b,isDirectIosSelectorFallbackError as v,refSnapshotFlagGuardResponse as N,readSimpleIosSelectorTarget as x}from"./selector-runtime.js";import{resolveWebProvider as F}from"./provider.js";import{interaction_snapshot_captureSnapshotForSession as R,readSnapshotNodesReferenceFrame as P,resolveDirectTouchReferenceFrameSafely as M,buildTouchVisualizationResult as O,finalizeTouchInteraction as C}from"./8184.js";import{emitDiagnostic as I}from"./432.js";import{PUBLIC_COMMANDS as A}from"./2133.js";var E={};function q(e,t){return"macos"!==e.device.platform||"desktop"!==e.surface&&"menubar"!==e.surface||"menubar"===e.surface&&("click"===t||"press"===t)?null: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 U(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=U(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 D(e){return"ref"===e.kind?{ref:H(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 H(e){return e?.startsWith("@")?e.slice(1):e}function K(e){let n=e.sessionStore.get(e.sessionName);if(!n)throw new S("SESSION_NOT_FOUND","No active session. Run open first.");return g({backend:function(e){let{req:t,session:r}=e,n=function(e){if("web"!==e.device.platform)return;let t=F();return t.clickRef||t.fillRef?t:void 0}(r);return{platform:r.device.platform,captureSnapshot:async(a,n)=>({snapshot:await e.captureSnapshotForSession(r,t.flags,e.sessionStore,e.contextFromFlags,{interactiveOnly:n?.interactiveOnly===!0,includeRects:n?.includeRects===!0})}),tap:async(n,o)=>L(await a(r.device,"press",[String(o.x),String(o.y)],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath))),tapTarget:n?.clickRef?async(e,t)=>(await n.clickRef?.(t.ref),{ref:H(t.ref)}):void 0,fill:async(n,o,s)=>L(await a(r.device,"fill",[String(o.x),String(o.y),s],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath))),fillTarget:n?.fillRef?async(e,t,r,a)=>(await n.fillRef?.(t.ref,r,a),{ref:H(t.ref),text:r,delayMs:a?.delayMs??0}):void 0,longPress:async(n,o,s)=>L(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)=>L(await a(r.device,"type",[o],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath)))}}({...e,session:n}),...w("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 L(e){return e&&"object"==typeof e?e:void 0}async function j(e){switch(e.req.command){case"press":return await B(e,"press");case"click":return await B(e,"click");case"longpress":return await B(e,"longpress");case"fill":return await X(e);default:return null}}async function B(e,t){let r,{req:a,sessionName:n,sessionStore:i}=e,l=i.get(n);if(!l)return s("SESSION_NOT_FOUND","No active session. Run open first.");let u="click"===t?"click":t,p="longpress"===t?"longpress":"press",g=q(l,u);if(g)return g;if(!o(p,l.device))return s("UNSUPPORTED_OPERATION",`${p} is not supported on this device`);let m=f(a.flags),k=c(m);if("longpress"!==t&&"primary"!==m){let e=d({commandLabel:u,platform:l.device.platform,button:m,count:a.flags?.count,intervalMs:a.flags?.intervalMs,holdMs:a.flags?.holdMs,jitterPx:a.flags?.jitterPx,doubleTap:a.flags?.doubleTap});if(e)return s(e.code,e.message,e.details)}let S="longpress"===t?function(e){var t,r,a;let n,o=U(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=T(s.targetPositionals,"longpress");return i.ok?{ok:!0,target:i.target,...s.duration}:i}(a.positionals??[]):T(a.positionals??[],u);if(!S.ok)return S.response;if("ref"===S.target.kind){let n=e.refSnapshotFlagGuardResponse("longpress"===t?"longpress":"press",a.flags);if(n)return n;r=await J(e,l)}let _=function(e){var t;let{session:r,commandLabel:a,target:n,flags:o}=e;if("click"!==a||"selector"!==n.kind||(t=o,t?.count!==void 0||t?.intervalMs!==void 0||t?.holdMs!==void 0||t?.jitterPx!==void 0||t?.doubleTap!==void 0||t?.clickButton!==void 0&&"primary"!==t.clickButton))return null;let s=x({session:r,selectorExpression:n.selector});return s?{...s,...o?.maestro?.allowNonHittableCoordinateFallback?{allowNonHittableCoordinateFallback:!0}:{}}:null}({session:l,commandLabel:u,target:S.target,flags:a.flags});if(_){let t=await $(e,l,_);if(t)return t}let y="longpress"===t?S.durationMs:void 0;return await z(e,{androidFreshnessBaseline:r,run:async e=>await G({runtime:e,command:t,target:S.target,sessionName:n,requestId:a.meta?.requestId,clickButton:m,flags:a.flags,durationMs:y}),afterRun:async e=>{var t;await h(l,(t=S.target,"point"===t.kind?"coordinate tap":"ref"===e.kind&&e.target?.kind==="ref"?e.target.ref:"selector"===e.kind&&e.target?.kind==="selector"?e.target.selector:"target"))},buildPayloads:async r=>{var a;let n="durationMs"in(a=r)?a.durationMs:void 0,o=await W({params:e,session:l,result:r,extra:"longpress"===t?{...void 0!==n?{durationMs:n}:{},gesture:"longpress"}:k});return{result:o,responseData:o}}})}async function G(e){let{runtime:t,command:r,target:a,sessionName:n,requestId:o,flags:s}=e;if("longpress"===r)return await t.interactions.longPress(a,{session:n,requestId:o,durationMs:e.durationMs});let i={session:n,requestId:o,button:e.clickButton,count:s?.count,intervalMs:s?.intervalMs,holdMs:s?.holdMs,jitterPx:s?.jitterPx,doubleTap:s?.doubleTap};return"click"===r?await t.interactions.click(a,i):await t.interactions.press(a,i)}async function W(e){let{params:t,session:r,result:a,extra:n}=e,o="point"===a.kind?await M({session:r,flags:t.req.flags,sessionStore:t.sessionStore,contextFromFlags:t.contextFromFlags,captureSnapshotForSession:t.captureSnapshotForSession}):P(r.snapshot?.nodes??[]);return O({data:a.backendResult,fallbackX:a.point?.x,fallbackY:a.point?.y,referenceFrame:o,extra:{...D(a),...n}})}async function $(e,t,r){return await V({params:e,session:t,selector:r,command:"press",positionals:[],extra:{selector:r.raw},fallbackPhase:"ios_direct_selector_tap_fallback"})}async function V(e){let{params:t,session:r,selector:n,command:o,positionals:s,extra:i,fallbackPhase:l}=e,c=Date.now();try{var f,u;let e,l,d=await a(r.device,o,s,t.req.flags?.out,{...t.contextFromFlags(t.req.flags,r.appBundleId,r.trace?.outPath),directElementSelector:n,surface:r.surface})??{},p=Date.now(),g=(f=d,e="number"==typeof f.x?f.x:void 0,l="number"==typeof f.y?f.y:void 0,void 0!==e&&void 0!==l?{x:e,y:l}:{x:0,y:0}),m=O({data:d,fallbackX:g.x,fallbackY:g.y,referenceFrame:(u=d,"number"==typeof u.referenceWidth&&"number"==typeof u.referenceHeight?{referenceWidth:u.referenceWidth,referenceHeight:u.referenceHeight}:void 0),extra:{...i,...function(e,t){if(!e.allowNonHittableCoordinateFallback)return{};let r="tapped via non-hittable coordinate fallback"===t.message;return{maestroNonHittableCoordinateFallbackAllowed:!0,maestroNonHittableCoordinateFallbackUsed:r,...r?{maestroFallbackReason:"non-hittable-coordinate"}:{}}}(n,d)}});return C({session:r,sessionStore:t.sessionStore,command:t.req.command,positionals:t.req.positionals??[],retryPositionals:Q(g),flags:t.req.flags,result:m,responseData:m,actionStartedAt:c,actionFinishedAt:p})}catch(e){if(!v(e))return{ok:!1,error:k(e)};return I({level:"debug",phase:l,data:{selector:n.raw,error:e instanceof Error?e.message:String(e)}}),null}}async function X(e){let{req:t,sessionName:r,sessionStore:a}=e,n=a.get(r);if(n){let e=q(n,"fill");if(e)return e}if(n&&!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=u(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=U(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=u(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 J(e,n)}let l=function(e){let{session:t,target:r,flags:a}=e;if("selector"!==r.kind)return null;let n=x({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 Y(e,n,l,i.text);if(t)return t}return await z(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=O({data:e.backendResult,fallbackX:e.point?.x,fallbackY:e.point?.y,referenceFrame:t,extra:{...D(e),text:i.text}});e.warning&&(r.warning=e.warning);let a="ref"===e.kind?{...e.backendResult??{ref:H(e.target?.kind==="ref"?e.target.ref:void 0),...e.point?{x:e.point.x,y:e.point.y}:{}}}:r;return e.warning&&(a.warning=e.warning),{result:r,responseData:a}}})}async function Y(e,t,r,a){return await V({params:e,session:t,selector:r,command:"fill",positionals:[a],extra:{selector:r.raw,text:a},fallbackPhase:"ios_direct_selector_fill_fallback"})}async function z(e,t){let r=e.sessionStore.get(e.sessionName);if(!r)return s("SESSION_NOT_FOUND","No active session. Run open first.");let a=K(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:f}=await t.buildPayloads(i);return"recovered"===s.status&&(c.warning=s.warning,f.warning=s.warning),C({session:r,sessionStore:e.sessionStore,command:e.req.command,positionals:e.req.positionals??[],retryPositionals:function(e,t){if(("ref"!==t.kind||t.node)&&("click"===e||"press"===e)&&t.point)return Q(t.point)}(e.req.command,i),flags:e.req.flags,result:c,responseData:f,actionStartedAt:o,actionFinishedAt:l,androidFreshnessBaseline:t.androidFreshnessBaseline})}catch(t){let e=m(t);if(y(e))throw e;return{ok:!1,error:k(t)}}}async function J(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 Q(e){return[String(e.x),String(e.y)]}async function Z(e){let t=await j({...e,captureSnapshotForSession:R,refSnapshotFlagGuardResponse:N});if(t)return t;switch(e.req.command){case A.type:return await ee({...e,captureSnapshotForSession:R});case"get":return await _(e);case"is":return await b(e);default:return null}}async function ee(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(A.type,a.device))return s("UNSUPPORTED_OPERATION","type is not supported on this device");let n=await et(a);return n||await er(e,a)}async function et(e){return"android"===e.device.platform&&e.recording&&"failed"===(await i({session:e})).status?s("COMMAND_FAILED","Android system dialog blocked the recording session"):null}async function er(e,t){let{req:r,sessionName:a,sessionStore:o}=e,s=(r.positionals??[]).join(" "),i=K(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 f=Date.now(),u={...c.backendResult??{},text:c.text,delayMs:c.delayMs,...p(c.message??`Typed ${Array.from(c.text).length} chars`)};return"recovered"===e.status&&(u.warning=e.warning),C({session:t,sessionStore:o,command:r.command,positionals:r.positionals??[],flags:r.flags,result:u,responseData:u,actionStartedAt:l,actionFinishedAt:f})}catch(e){return{ok:!1,error:k(e)}}}e.r(E),e.d(E,{handleInteractionCommands:()=>Z});export{E as interaction_namespaceObject};
1
+ import{__webpack_require__ as e}from"./rslib-runtime.js";import{createDaemonRuntimeSessionStore as t,setSessionSnapshot as r,dispatchCommand as a,ensureAndroidBlockingSystemDialogReady as n,isCommandSupportedOnDevice as o,errorResponse as s,recoverAndroidBlockingSystemDialog as i,getActiveAndroidSnapshotFreshness as l}from"./9722.js";import{buttonTag as c,resolveClickButton as f,readFillTargetFromPositionals as u,getClickButtonValidationError as d,successText as p}from"./2948.js";import{createAgentDevice as g}from"./2577.js";import{asAppError as m,normalizeError as k,AppError as S}from"./733.js";import{dispatchGetViaRuntime as _,isAndroidEscapeError as y,createDaemonRuntimePolicy as w,assertAndroidPressStayedInApp as h,dispatchIsViaRuntime as b,isDirectIosSelectorFallbackError as v,refSnapshotFlagGuardResponse as N,readSimpleIosSelectorTarget as x}from"./selector-runtime.js";import{resolveWebProvider as F}from"./provider.js";import{interaction_snapshot_captureSnapshotForSession as R,readSnapshotNodesReferenceFrame as P,resolveDirectTouchReferenceFrameSafely as M,buildTouchVisualizationResult as O,finalizeTouchInteraction as C}from"./8184.js";import{emitDiagnostic as I}from"./432.js";import{PUBLIC_COMMANDS as A}from"./5341.js";var E={};function q(e,t){return"macos"!==e.device.platform||"desktop"!==e.surface&&"menubar"!==e.surface||"menubar"===e.surface&&("click"===t||"press"===t)?null: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 U(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=U(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 D(e){return"ref"===e.kind?{ref:H(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 H(e){return e?.startsWith("@")?e.slice(1):e}function K(e){let n=e.sessionStore.get(e.sessionName);if(!n)throw new S("SESSION_NOT_FOUND","No active session. Run open first.");return g({backend:function(e){let{req:t,session:r}=e,n=function(e){if("web"!==e.device.platform)return;let t=F();return t.clickRef||t.fillRef?t:void 0}(r);return{platform:r.device.platform,captureSnapshot:async(a,n)=>({snapshot:await e.captureSnapshotForSession(r,t.flags,e.sessionStore,e.contextFromFlags,{interactiveOnly:n?.interactiveOnly===!0,includeRects:n?.includeRects===!0})}),tap:async(n,o)=>L(await a(r.device,"press",[String(o.x),String(o.y)],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath))),tapTarget:n?.clickRef?async(e,t)=>(await n.clickRef?.(t.ref),{ref:H(t.ref)}):void 0,fill:async(n,o,s)=>L(await a(r.device,"fill",[String(o.x),String(o.y),s],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath))),fillTarget:n?.fillRef?async(e,t,r,a)=>(await n.fillRef?.(t.ref,r,a),{ref:H(t.ref),text:r,delayMs:a?.delayMs??0}):void 0,longPress:async(n,o,s)=>L(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)=>L(await a(r.device,"type",[o],t.flags?.out,e.contextFromFlags(t.flags,r.appBundleId,r.trace?.outPath)))}}({...e,session:n}),...w("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 L(e){return e&&"object"==typeof e?e:void 0}async function j(e){switch(e.req.command){case"press":return await B(e,"press");case"click":return await B(e,"click");case"longpress":return await B(e,"longpress");case"fill":return await X(e);default:return null}}async function B(e,t){let r,{req:a,sessionName:n,sessionStore:i}=e,l=i.get(n);if(!l)return s("SESSION_NOT_FOUND","No active session. Run open first.");let u="click"===t?"click":t,p="longpress"===t?"longpress":"press",g=q(l,u);if(g)return g;if(!o(p,l.device))return s("UNSUPPORTED_OPERATION",`${p} is not supported on this device`);let m=f(a.flags),k=c(m);if("longpress"!==t&&"primary"!==m){let e=d({commandLabel:u,platform:l.device.platform,button:m,count:a.flags?.count,intervalMs:a.flags?.intervalMs,holdMs:a.flags?.holdMs,jitterPx:a.flags?.jitterPx,doubleTap:a.flags?.doubleTap});if(e)return s(e.code,e.message,e.details)}let S="longpress"===t?function(e){var t,r,a;let n,o=U(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=T(s.targetPositionals,"longpress");return i.ok?{ok:!0,target:i.target,...s.duration}:i}(a.positionals??[]):T(a.positionals??[],u);if(!S.ok)return S.response;if("ref"===S.target.kind){let n=e.refSnapshotFlagGuardResponse("longpress"===t?"longpress":"press",a.flags);if(n)return n;r=await J(e,l)}let _=function(e){var t;let{session:r,commandLabel:a,target:n,flags:o}=e;if("click"!==a||"selector"!==n.kind||(t=o,t?.count!==void 0||t?.intervalMs!==void 0||t?.holdMs!==void 0||t?.jitterPx!==void 0||t?.doubleTap!==void 0||t?.clickButton!==void 0&&"primary"!==t.clickButton))return null;let s=x({session:r,selectorExpression:n.selector});return s?{...s,...o?.maestro?.allowNonHittableCoordinateFallback?{allowNonHittableCoordinateFallback:!0}:{}}:null}({session:l,commandLabel:u,target:S.target,flags:a.flags});if(_){let t=await $(e,l,_);if(t)return t}let y="longpress"===t?S.durationMs:void 0;return await z(e,{androidFreshnessBaseline:r,run:async e=>await G({runtime:e,command:t,target:S.target,sessionName:n,requestId:a.meta?.requestId,clickButton:m,flags:a.flags,durationMs:y}),afterRun:async e=>{var t;await h(l,(t=S.target,"point"===t.kind?"coordinate tap":"ref"===e.kind&&e.target?.kind==="ref"?e.target.ref:"selector"===e.kind&&e.target?.kind==="selector"?e.target.selector:"target"))},buildPayloads:async r=>{var a;let n="durationMs"in(a=r)?a.durationMs:void 0,o=await W({params:e,session:l,result:r,extra:"longpress"===t?{...void 0!==n?{durationMs:n}:{},gesture:"longpress"}:k});return{result:o,responseData:o}}})}async function G(e){let{runtime:t,command:r,target:a,sessionName:n,requestId:o,flags:s}=e;if("longpress"===r)return await t.interactions.longPress(a,{session:n,requestId:o,durationMs:e.durationMs});let i={session:n,requestId:o,button:e.clickButton,count:s?.count,intervalMs:s?.intervalMs,holdMs:s?.holdMs,jitterPx:s?.jitterPx,doubleTap:s?.doubleTap};return"click"===r?await t.interactions.click(a,i):await t.interactions.press(a,i)}async function W(e){let{params:t,session:r,result:a,extra:n}=e,o="point"===a.kind?await M({session:r,flags:t.req.flags,sessionStore:t.sessionStore,contextFromFlags:t.contextFromFlags,captureSnapshotForSession:t.captureSnapshotForSession}):P(r.snapshot?.nodes??[]);return O({data:a.backendResult,fallbackX:a.point?.x,fallbackY:a.point?.y,referenceFrame:o,extra:{...D(a),...n}})}async function $(e,t,r){return await V({params:e,session:t,selector:r,command:"press",positionals:[],extra:{selector:r.raw},fallbackPhase:"ios_direct_selector_tap_fallback"})}async function V(e){let{params:t,session:r,selector:n,command:o,positionals:s,extra:i,fallbackPhase:l}=e,c=Date.now();try{var f,u;let e,l,d=await a(r.device,o,s,t.req.flags?.out,{...t.contextFromFlags(t.req.flags,r.appBundleId,r.trace?.outPath),directElementSelector:n,surface:r.surface})??{},p=Date.now(),g=(f=d,e="number"==typeof f.x?f.x:void 0,l="number"==typeof f.y?f.y:void 0,void 0!==e&&void 0!==l?{x:e,y:l}:{x:0,y:0}),m=O({data:d,fallbackX:g.x,fallbackY:g.y,referenceFrame:(u=d,"number"==typeof u.referenceWidth&&"number"==typeof u.referenceHeight?{referenceWidth:u.referenceWidth,referenceHeight:u.referenceHeight}:void 0),extra:{...i,...function(e,t){if(!e.allowNonHittableCoordinateFallback)return{};let r="tapped via non-hittable coordinate fallback"===t.message;return{maestroNonHittableCoordinateFallbackAllowed:!0,maestroNonHittableCoordinateFallbackUsed:r,...r?{maestroFallbackReason:"non-hittable-coordinate"}:{}}}(n,d)}});return C({session:r,sessionStore:t.sessionStore,command:t.req.command,positionals:t.req.positionals??[],retryPositionals:Q(g),flags:t.req.flags,result:m,responseData:m,actionStartedAt:c,actionFinishedAt:p})}catch(e){if(!v(e))return{ok:!1,error:k(e)};return I({level:"debug",phase:l,data:{selector:n.raw,error:e instanceof Error?e.message:String(e)}}),null}}async function X(e){let{req:t,sessionName:r,sessionStore:a}=e,n=a.get(r);if(n){let e=q(n,"fill");if(e)return e}if(n&&!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=u(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=U(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=u(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 J(e,n)}let l=function(e){let{session:t,target:r,flags:a}=e;if("selector"!==r.kind)return null;let n=x({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 Y(e,n,l,i.text);if(t)return t}return await z(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=O({data:e.backendResult,fallbackX:e.point?.x,fallbackY:e.point?.y,referenceFrame:t,extra:{...D(e),text:i.text}});e.warning&&(r.warning=e.warning);let a="ref"===e.kind?{...e.backendResult??{ref:H(e.target?.kind==="ref"?e.target.ref:void 0),...e.point?{x:e.point.x,y:e.point.y}:{}}}:r;return e.warning&&(a.warning=e.warning),{result:r,responseData:a}}})}async function Y(e,t,r,a){return await V({params:e,session:t,selector:r,command:"fill",positionals:[a],extra:{selector:r.raw,text:a},fallbackPhase:"ios_direct_selector_fill_fallback"})}async function z(e,t){let r=e.sessionStore.get(e.sessionName);if(!r)return s("SESSION_NOT_FOUND","No active session. Run open first.");let a=K(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:f}=await t.buildPayloads(i);return"recovered"===s.status&&(c.warning=s.warning,f.warning=s.warning),C({session:r,sessionStore:e.sessionStore,command:e.req.command,positionals:e.req.positionals??[],retryPositionals:function(e,t){if(("ref"!==t.kind||t.node)&&("click"===e||"press"===e)&&t.point)return Q(t.point)}(e.req.command,i),flags:e.req.flags,result:c,responseData:f,actionStartedAt:o,actionFinishedAt:l,androidFreshnessBaseline:t.androidFreshnessBaseline})}catch(t){let e=m(t);if(y(e))throw e;return{ok:!1,error:k(t)}}}async function J(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 Q(e){return[String(e.x),String(e.y)]}async function Z(e){let t=await j({...e,captureSnapshotForSession:R,refSnapshotFlagGuardResponse:N});if(t)return t;switch(e.req.command){case A.type:return await ee({...e,captureSnapshotForSession:R});case"get":return await _(e);case"is":return await b(e);default:return null}}async function ee(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(A.type,a.device))return s("UNSUPPORTED_OPERATION","type is not supported on this device");let n=await et(a);return n||await er(e,a)}async function et(e){return"android"===e.device.platform&&e.recording&&"failed"===(await i({session:e})).status?s("COMMAND_FAILED","Android system dialog blocked the recording session"):null}async function er(e,t){let{req:r,sessionName:a,sessionStore:o}=e,s=(r.positionals??[]).join(" "),i=K(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 f=Date.now(),u={...c.backendResult??{},text:c.text,delayMs:c.delayMs,...p(c.message??`Typed ${Array.from(c.text).length} chars`)};return"recovered"===e.status&&(u.warning=e.warning),C({session:t,sessionStore:o,command:r.command,positionals:r.positionals??[],flags:r.flags,result:u,responseData:u,actionStartedAt:l,actionFinishedAt:f})}catch(e){return{ok:!1,error:k(e)}}}e.r(E),e.d(E,{handleInteractionCommands:()=>Z});export{E as interaction_namespaceObject};
@@ -1,5 +1,5 @@
1
1
  var e,t,r,s;let n,o=process.argv.slice(2);function c(e){return"--help"===e||"-h"===e}function i(e){import("../cli.js").then(({runCli:t})=>t(e)).catch(p)}function p(e){process.stderr.write(`${e instanceof Error?e.message:String(e)}
2
- `),process.exit(1)}1===(t=e=o).length&&("--version"===(r=t[0])||"-V"===r)&&(process.stdout.write(`0.17.10
2
+ `),process.exit(1)}1===(t=e=o).length&&("--version"===(r=t[0])||"-V"===r)&&(process.stdout.write(`0.18.0
3
3
  `),1)||0===e.length&&(import("../cli-help.js").then(({buildUsageText:e})=>{process.stdout.write(`${e()}
4
4
  `),process.exit(1)}).catch(p),1)||void 0!==(n=function(e){var t,r,s,n;switch(e.length){case 1:return"help"===(t=e[0])||c(t)?null:void 0;case 2:return r=e[0],s=e[1],"help"===r?s:(n=r,c(s)?n:void 0);default:return}}(s=e))&&(import("../cli-help.js").then(({buildCommandUsageText:e,buildUsageText:t})=>{var r;if(null===n)return void process.stdout.write(`${t()}
5
5
  `);let o=e("long-press"===(r=n)?"longpress":"metrics"===r?"perf":r);o?process.stdout.write(o):i(s)}).catch(p),1)||("mcp"!==o[0]||o.includes("--help")||o.includes("-h")?i(o):import("../server.js").then(({runAgentDeviceMcpServer:e})=>e()).catch(p));export{};
package/dist/src/lease.js CHANGED
@@ -1 +1 @@
1
- import{resolveLeaseScope as e}from"./9722.js";async function a(a){let{req:t,leaseRegistry:n}=a,s=e(t);switch(t.command){case"lease_allocate":return{ok:!0,data:{lease:n.allocateLease({tenantId:s.tenantId??"",runId:s.runId??"",backend:s.leaseBackend,ttlMs:s.leaseTtlMs})}};case"lease_heartbeat":return{ok:!0,data:{lease:n.heartbeatLease({leaseId:s.leaseId??"",tenantId:s.tenantId,runId:s.runId,ttlMs:s.leaseTtlMs})}};case"lease_release":return{ok:!0,data:n.releaseLease({leaseId:s.leaseId??"",tenantId:s.tenantId,runId:s.runId})};default:return null}}export{a as handleLeaseCommands};
1
+ import{resolveLeaseScope as e}from"./9722.js";import{leaseScopeToAllocateRequest as a,leaseScopeToHeartbeatRequest as t,leaseScopeToReleaseRequest as r}from"./2948.js";async function s(s){let{req:l,leaseRegistry:o}=s,_=e(l);switch(l.command){case"lease_allocate":return{ok:!0,data:{lease:o.allocateLease(a(_))}};case"lease_heartbeat":return{ok:!0,data:{lease:o.heartbeatLease(t(_))}};case"lease_release":return{ok:!0,data:o.releaseLease(r(_))};default:return null}}export{s as handleLeaseCommands};
package/dist/src/metro.js CHANGED
@@ -1 +1 @@
1
- import{buildMetroRuntimeHints as e,ensureMetroCompanion as t,reloadMetro as r,stopMetroCompanion as o,prepareMetroRuntime as n}from"./8275.js";import{resolveRuntimeTransportHints as i}from"./5628.js";function s(e){return i(e)}async function a(e){let t=await n({projectRoot:e.projectRoot,kind:e.kind,publicBaseUrl:e.publicBaseUrl,proxyBaseUrl:e.proxyBaseUrl,proxyBearerToken:e.proxyBearerToken,bridgeScope:e.bridgeScope,launchUrl:e.launchUrl,companionProfileKey:e.profileKey,companionConsumerKey:e.consumerKey,metroPort:e.port,listenHost:e.listenHost,statusHost:e.statusHost,startupTimeoutMs:e.startupTimeoutMs,probeTimeoutMs:e.probeTimeoutMs,reuseExisting:e.reuseExisting,installDependenciesIfNeeded:e.installDependenciesIfNeeded,runtimeFilePath:e.runtimeFilePath,logPath:e.logPath,env:e.env});return{iosRuntime:t.iosRuntime,androidRuntime:t.androidRuntime,bridge:t.bridge,started:t.started,reused:t.reused,logPath:t.logPath}}async function u(e){let r=await t(e);return{pid:r.pid,started:r.spawned,logPath:r.logPath}}async function l(e){await o(e)}async function d(e={}){return await r(e)}function p(t){return e(t,"ios")}function m(t){return e(t,"android")}export{buildBundleUrl,normalizeBaseUrl}from"./4069.js";export{m as buildAndroidRuntimeHints,p as buildIosRuntimeHints,u as ensureMetroTunnel,a as prepareRemoteMetro,d as reloadRemoteMetro,s as resolveRuntimeTransport,l as stopMetroTunnel};
1
+ import{buildMetroRuntimeHints as e,ensureMetroCompanion as t,reloadMetro as r,stopMetroCompanion as o,prepareMetroRuntime as n}from"./8275.js";import{resolveRuntimeTransportHints as i}from"./8936.js";function s(e){return i(e)}async function a(e){let t=await n({projectRoot:e.projectRoot,kind:e.kind,publicBaseUrl:e.publicBaseUrl,proxyBaseUrl:e.proxyBaseUrl,proxyBearerToken:e.proxyBearerToken,bridgeScope:e.bridgeScope,launchUrl:e.launchUrl,companionProfileKey:e.profileKey,companionConsumerKey:e.consumerKey,metroPort:e.port,listenHost:e.listenHost,statusHost:e.statusHost,startupTimeoutMs:e.startupTimeoutMs,probeTimeoutMs:e.probeTimeoutMs,reuseExisting:e.reuseExisting,installDependenciesIfNeeded:e.installDependenciesIfNeeded,runtimeFilePath:e.runtimeFilePath,logPath:e.logPath,env:e.env});return{iosRuntime:t.iosRuntime,androidRuntime:t.androidRuntime,bridge:t.bridge,started:t.started,reused:t.reused,logPath:t.logPath}}async function u(e){let r=await t(e);return{pid:r.pid,started:r.spawned,logPath:r.logPath}}async function l(e){await o(e)}async function d(e={}){return await r(e)}function p(t){return e(t,"ios")}function m(t){return e(t,"android")}export{buildBundleUrl,normalizeBaseUrl}from"./4069.js";export{m as buildAndroidRuntimeHints,p as buildIosRuntimeHints,u as ensureMetroTunnel,a as prepareRemoteMetro,d as reloadRemoteMetro,s as resolveRuntimeTransport,l as stopMetroTunnel};
@@ -1 +1 @@
1
- import{dispatchCommand as e,errorResponse as t,isCommandSupportedOnDevice as i}from"./9722.js";import{PUBLIC_COMMANDS as a}from"./2133.js";import{isSparseSnapshotQualityVerdict as s,analyzeReactNativeOverlay as r,successText as n,parsing_stripUndefined as o}from"./2948.js";import{normalizeError as d}from"./733.js";import{interaction_snapshot_captureSnapshotForSession as l,readSnapshotNodesReferenceFrame as c,finalizeTouchInteraction as v}from"./8184.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
+ import{dispatchCommand as e,errorResponse as t,isCommandSupportedOnDevice as i}from"./9722.js";import{stripUndefined as a,PUBLIC_COMMANDS as s}from"./5341.js";import{isSparseSnapshotQualityVerdict as r,analyzeReactNativeOverlay as n,successText as o}from"./2948.js";import{normalizeError as d}from"./733.js";import{interaction_snapshot_captureSnapshotForSession as l,readSnapshotNodesReferenceFrame as c,finalizeTouchInteraction as v}from"./8184.js";async function f(e){var a,c;let{req:v,sessionName:f,sessionStore:u}=e;if(v.command!==s.reactNative)return null;let y=1===(a=v.positionals??[]).length&&"dismiss-overlay"===a[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(s.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(r(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=n(i.nodes),s=a.primaryAction;if(!s)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,...o("No React Native overlay detected")}};return await m(e,p,i,s)}catch(e){return{ok:!1,error:d(e)}}}async function m(t,i,s,r){let{req:n,sessionStore:d}=t,l=Date.now(),f=await e(i.device,"press",[String(r.point.x),String(r.point.y)],n.flags?.out,t.contextFromFlags(n.flags,i.appBundleId,i.trace?.outPath))??{},m=Date.now(),y=await u(t,i),p=a({...c(s.nodes),...f,action:"dismiss-overlay",overlayAction:r.action,x:r.point.x,y:r.point.y,ref:r.ref,label:r.label,warning:r.warning,dismissed:!0,verified:y.verified,verificationRequired:!y.verified,verificationWarning:y.verificationWarning,nextCommand:y.nextCommand,...o(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:n.command,positionals:n.positionals??[],flags:n.flags,result:p,responseData:p,actionStartedAt:l,actionFinishedAt:m})}async function u(e,t){let{req:i,sessionStore:a}=e,s=await l(t,i.flags,a,e.contextFromFlags,{interactiveOnly:!0});return r(s.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"}:n(s.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,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"./733.js";import{runCmd as l}from"./8875.js";import{acquireProcessLock as u}from"./2577.js";import{readProcessStartTime as m}from"./5628.js";import{sleep as f}from"./5560.js";import{withDiagnosticTimer as h,emitDiagnostic as p}from"./432.js";import{androidDeviceForSerial as w,runAndroidAdb as g}from"./3340.js";import{pullAndroidAdbFile as y}from"./2954.js";import{runXcrun as _,SessionStore as v,runIosRunnerCommand as P,errorResponse as S,isCommandSupportedOnDevice as A,ensureDeviceReady as M,resolvePublicSessionName as C,resolveImplicitSessionScope as b,resolveTargetDevice as x,IOS_RUNNER_CONTAINER_BUNDLE_IDS as I}from"./9722.js";import{resolveRecordingProvider as D}from"./recording-provider.js";import{recordingQualityInputToExportQuality as $,RECORDING_EXPORT_QUALITIES as R}from"./2948.js";function N(e=process.env){let t=F(),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 k(e){let t=a.statSync(e.sourcePath),r=a.readFileSync(e.sourcePath),i=z(e.cacheName??o.basename(e.sourcePath,o.extname(e.sourcePath))),n=U(["2",process.platform,process.arch,o.resolve(e.sourcePath),t.size,r]),s=o.join(F(),"bin",`${i}-${n}`);return await O({sourcePath:e.sourcePath,executablePath:s,timeoutMs:e.timeoutMs}),s}async function E(e){let t=z(e.cacheName),r=U(["2",process.platform,process.arch,e.source]),i=o.join(F(),"sources",`${t}-${r}.swift`),a=o.join(F(),"bin",`${t}-${r}`);return await O({sourcePath:i,executablePath:a,sourceText:e.source,timeoutMs:e.timeoutMs}),a}function F(){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 O(e){if(T(e.executablePath))return;let t=o.dirname(e.executablePath);a.mkdirSync(t,{recursive:!0});let r=`${e.executablePath}.lock`,i=await L(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(T(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:N()}),a.renameSync(s,e.executablePath)}finally{a.rmSync(n,{recursive:!0,force:!0}),await i()}}async function L(e,t,r){if(T(t))return null;try{return await u({lockDirPath:e,owner:{pid:process.pid,startTime:m(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 T(e){try{return a.accessSync(e,a.constants.X_OK),a.statSync(e).isFile()}catch{return!1}}function z(e){return e.replaceAll(/[^A-Za-z0-9._-]/g,"-").replaceAll(/^-+|-+$/g,"")||"swift-helper"}function U(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 K=`
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"./733.js";import{runCmd as u}from"./8875.js";import{acquireProcessLock as l}from"./2577.js";import{readProcessStartTime as m}from"./9414.js";import{sleep as p}from"./5560.js";import{withDiagnosticTimer as h,emitDiagnostic as f}from"./432.js";import{androidDeviceForSerial as w,runAndroidAdb as g}from"./3340.js";import{pullAndroidAdbFile as y}from"./2954.js";import{runXcrun as _,SessionStore as v,runIosRunnerCommand as P,errorResponse as S,resolveImplicitSessionScope as A,resolvePublicSessionName as b,isCommandSupportedOnDevice as M,resolveTargetDevice as C,IOS_RUNNER_CONTAINER_BUNDLE_IDS as x}from"./9722.js";import{defaultRecordingPath as I,appendRecordingExtensionWhenMissing as R}from"./5643.js";import{resolveWebProvider as D}from"./provider.js";import{ensureDeviceReady as N}from"./device-ready.js";import{resolveRecordingProvider as E}from"./recording-provider.js";import{recordingQualityInputToExportQuality as O,RECORDING_EXPORT_QUALITIES as $}from"./2948.js";function k(e=process.env){let t=L(),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 F(e){let t=a.statSync(e.sourcePath),r=a.readFileSync(e.sourcePath),i=G(e.cacheName??o.basename(e.sourcePath,o.extname(e.sourcePath))),n=V(["2",process.platform,process.arch,o.resolve(e.sourcePath),t.size,r]),s=o.join(L(),"bin",`${i}-${n}`);return await U({sourcePath:e.sourcePath,executablePath:s,timeoutMs:e.timeoutMs}),s}async function T(e){let t=G(e.cacheName),r=V(["2",process.platform,process.arch,e.source]),i=o.join(L(),"sources",`${t}-${r}.swift`),a=o.join(L(),"bin",`${t}-${r}`);return await U({sourcePath:i,executablePath:a,sourceText:e.source,timeoutMs:e.timeoutMs}),a}function L(){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 U(e){if(K(e.executablePath))return;let t=o.dirname(e.executablePath);a.mkdirSync(t,{recursive:!0});let r=`${e.executablePath}.lock`,i=await z(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(K(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 u("xcrun",["swiftc",e.sourcePath,"-o",s],{timeoutMs:e.timeoutMs??12e4,env:k()}),a.renameSync(s,e.executablePath)}finally{a.rmSync(n,{recursive:!0,force:!0}),await i()}}async function z(e,t,r){if(K(t))return null;try{return await l({lockDirPath:e,owner:{pid:process.pid,startTime:m(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 K(e){try{return a.accessSync(e,a.constants.X_OK),a.statSync(e).isFile()}catch{return!1}}function G(e){return e.replaceAll(/[^A-Za-z0-9._-]/g,"-").replaceAll(/^-+|-+$/g,"")||"swift-helper"}function V(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 H=`
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 G(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 f(i)}}async function V(e){try{let t=await H(),r=await l(t,[e],{allowFailure:!0,timeoutMs:1e4,env:N()});if(0===r.exitCode)return!0;if(q(r.stderr,r.stdout))return W(e);return!1}catch(r){var t;if((t=r)instanceof d&&("TOOL_MISSING"===t.code||q(String(t.details?.stderr??""),String(t.details?.stdout??""))))return W(e);throw r}}async function H(){e??=E({source:K,cacheName:"video-validator",timeoutMs:3e4});try{return await e}catch(t){throw e=void 0,t}}async function j(e,t={}){let r=t.pollMs??150,i=t.attempts??12;for(let t=0;t<i;t+=1){if(await V(e))return;await f(r)}}function q(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 W(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 B(e){let t=o.parse(e);return o.join(t.dir,`${t.name}.gesture-telemetry.json`)}function X(e){return[...e].sort((e,t)=>e.tMs-t.tMs)}function Q(e){var t,r,i;let o,n,{recording:s,trimStartMs:c}=e,d=(o=B((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?X(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)}]})):X(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 Z(e){var t;let r,i,{videoPath:n,scriptPath:s,scriptArgs:c,commandDescription:u}=e;await G(n),await j(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 k({sourcePath:s});await l(e,["--input",n,"--output",m,...c],{timeoutMs:12e4,env:N()}),await j(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 Y(e){let{videoPath:t,trimStartMs:i}=e;i>0&&await Z({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 ee(e){let{videoPath:r,telemetryPath:i,exportQuality:a="medium",targetLabel:o="recording"}=e;await Z({videoPath:r,scriptPath:t??=J("recording-overlay.swift"),scriptArgs:["--events",i,"--quality",a],commandDescription:`Failed to add touch overlays to the ${o}`})}async function et(e){let{videoPath:t,maxSize:r,exportQuality:a="medium",targetLabel:o="recording"}=e;await Z({videoPath:t,scriptPath:i??=J("recording-resize.swift"),scriptArgs:["--max-size",String(r),"--quality",a],commandDescription:`Failed to resize the ${o}`})}function er(e){return e instanceof Error?e.message:String(e)}function ei(e,t){return e.stderr.trim()||e.stdout.trim()||`${t} exited with code ${e.exitCode}`}function ea(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 eo(e){let{recording:t,deps:r,trimStartMs:i,targetLabel:a}=e,o=Q({recording:t,trimStartMs:i});if(!t.showTouches)return void p({level:"debug",phase:"record_stop_overlay_skipped",data:{reason:"hide_touches"}});if(0===t.gestureEvents.length)return void p({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 h("record_stop_overlay_export",()=>r.overlayRecordingTouches({videoPath:t.outPath,telemetryPath:o,exportQuality:t.exportQuality,targetLabel:a}),{targetLabel:a,gestureEventCount:t.gestureEvents.length})}catch(e){t.overlayWarning??=`failed to overlay recording touches: ${er(e)}`}}function en(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 es(e){return e.chunks??=[{index:1,path:e.outPath,remotePath:e.remotePath}],e.chunks}async function ec(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=es(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:en(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 ed(e){let{recording:t,deps:r}=e;es(t).length<=1?await eo({recording:t,deps:r,targetLabel:"Android recording"}):(Q({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 el(e){for(let t of e.chunks){let r=await eu({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 eu(e){let t,{deps:r,deviceId:i,remotePath:o,outPath:n}=e;for(let e=0;e<2;e+=1){em(n);let s=w(i),c=await y(o,n,{allowFailure:!0,device:s});if(0!==c.exitCode)t=ei(c,"adb pull");else{await r.waitForStableFile(n,{pollMs:250,attempts:20});let t=await r.isPlayableVideo(n);if(p({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;p({level:"warn",phase:"record_stop_android_invalid_video_retry",data:{deviceId:i,remotePath:o,outPath:n,attempt:e+1}})}e<1&&await f(750)}return t?`failed to copy recording from device: ${t}`:(em(n),"failed to copy recording from device: pulled file is not a playable MP4")}function em(e){try{a.rmSync(e,{force:!0})}catch{}}let ef={medium:8e6,high:2e7};async function eh(e,t,r){return await g(w(e),t,r)}async function ep(e,t){let r=await eh(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 ew(e,t){for(let r=0;r<40;r+=1){if(!await ep(e,t))return!0;await f(250)}return!await ep(e,t)}async function eg(e,t){let r,i=0;for(let a=0;a<20;a+=1){let a=await eh(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 f(250)}}async function ey(e,t,r){for(let i=0;i<8;i+=1){let a=await eh(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 ep(e,r))break;if(i+1>=2)return!0;await f(250)}return!1}async function e_(e){let{deviceId:t,maxSize:r}=e;if(void 0===r)return;let i=await eh(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 max-size: ${ei(i,"adb shell wm size")}`);var o={width:Number(a[1]),height:Number(a[2]),maxSize:r};let n=Math.max(o.width,o.height);if(n<=o.maxSize)return{width:o.width,height:o.height};let s=o.maxSize/n;return{width:ev(o.width,s),height:ev(o.height,s)}}function ev(e,t){return Math.max(2,2*Math.round(e*t/2))}async function eP(e,t){await eh(e,["shell","rm","-f",t],{allowFailure:!0})}async function eS(e,t){let r=await eh(e,["shell","kill","-9",t],{allowFailure:!0});return p({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 ep(e,t))&&await ew(e,t)}async function eA(e){var t;let r,i,{device:a,recordingSize:o,quality:n,preferredRemoteDir:s}=e,c="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"],(s&&i.includes(s)?[s,...i.filter(e=>e!==s)]:i).map(e=>`${e}/${r}`))){let t=await eh(a.id,["shell",function(e,t,r){let i=["screenrecord"];return t&&i.push("--size",`${t.width}x${t.height}`),i.push("--bit-rate",String(ef[r])),i.push(e),`${i.join(" ")} >/dev/null 2>&1 & echo $!`}(e,o,n)],{allowFailure:!0});if(0!==t.exitCode){c=`failed to start recording: ${ei(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){c="failed to start recording: adb did not return a valid Android screenrecord pid",await eP(a.id,e);continue}if(p({level:"debug",phase:"record_start_android_started",data:{deviceId:a.id,remotePath:e,remotePid:r}}),await ey(a.id,e,r))return{remotePath:e,remotePid:r,startedAt:Date.now()};c="failed to start recording: Android screenrecord did not begin producing frames",await eS(a.id,r),await eP(a.id,e)}return{error:S("COMMAND_FAILED",c)}}async function eM(e){let t,{device:r,recordingBase:i}=e;try{t=await e_({deviceId:r.id,maxSize:i.maxSize})}catch(e){return S("COMMAND_FAILED",e instanceof Error?e.message:String(e))}let a=i.exportQuality??"medium",o=await eA({device:r,recordingSize:t,quality:a});if("error"in o)return o.error;let n={platform:"android",remotePath:o.remotePath,remotePid:o.remotePid,chunks:[{index:1,path:i.outPath,remotePath:o.remotePath}],...i,startedAt:o.startedAt};return!function e(t){let{recording:r,startNextChunk:i,finishCurrentChunk:a}=t,o=setTimeout(()=>{r.rotationPromise=ec({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:n,finishCurrentChunk:async()=>await eC({device:r,recording:n,waitForRemoteFileStability:!1}),startNextChunk:async e=>{let i=await eA({device:r,recordingSize:t,quality:a,preferredRemoteDir:e});if("error"in i)throw Error(i.error.ok?"failed to start next Android recording chunk":i.error.error.message);return i}}),n}async function eC(e){let{device:t,recording:r,waitForRemoteFileStability:i=!0}=e;await ep(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 eh(t.id,["shell","kill","-2",r.remotePid],{allowFailure:!0});if(p({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 eb(t.id,r.remotePid,a);let o=await ex(t.id,r.remotePid);if(o)return o;i&&await eg(t.id,r.remotePath)}async function eb(e,t,r){if(await ep(e,t)&&!await eS(e,t))return`failed to stop recording: ${ei(r,"adb shell kill")}`}async function ex(e,t){if(!await ew(e,t)&&!await eS(e,t))return`failed to stop recording: Android screenrecord pid ${t} did not exit`}async function eI(e){let t,{deps:r,device:i,recording:a,stopRequestedAt:o}=e;p({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 eC({device:i,recording:a});if(a.rotationFailedReason&&!n&&(a.warning??=`Android recording chunk rotation failed: ${a.rotationFailedReason}`),!n){let e=await el({deps:r,deviceId:i.id,chunks:es(a)});if(e)return await s(),S("COMMAND_FAILED",eD(e,a,o));await ed({recording:a,deps:r})}if(await s(),n)return S("COMMAND_FAILED",eD(n,a,o));if(t)return S("COMMAND_FAILED",t);return null;async function s(){for(let e of es(a)){let r=await eh(i.id,["shell","rm","-f",e.remotePath],{allowFailure:!0});p({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: ${ei(r,"adb shell rm")}`)}}}function eD(e,t,r){return ea(e,t,r).message}function e$(e){let t=e.appBundleId?.trim();return t&&t.length>0?t:void 0}function eR(e,t,r){return{verbose:e.flags?.verbose,logPath:t,traceLogPath:r.trace?.outPath,requestId:e.meta?.requestId,iosXctestrunFile:e.flags?.iosXctestrunFile,iosXctestDerivedDataPath:e.flags?.iosXctestDerivedDataPath,iosXctestEnvDir:e.flags?.iosXctestEnvDir}}async function eN(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o}=e,n=e$(r);try{await o.runIosRunnerCommand(i,{command:"recordStop",appBundleId:n},eR(t,a,r))}catch(e){p({level:"warn",phase:"record_stop_runner_failed",data:{platform:i.platform,kind:i.kind,deviceId:i.id,session:r.name,error:er(e)}})}}async function ek(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o}=e,n=e$(r);if(n)try{let e=await o.runIosRunnerCommand(i,{command:"snapshot",appBundleId:n,interactiveOnly:!0,depth:1},eR(t,a,r)),s=Date.now();if("number"==typeof e.currentUptimeMs&&Number.isFinite(e.currentUptimeMs)&&e.currentUptimeMs>0)return p({level:"debug",phase:"record_start_gesture_clock_anchor",data:{source:"warm_snapshot"}}),{gestureClockOriginAtMs:s,gestureClockOriginUptimeMs:e.currentUptimeMs};return}catch(e){p({level:"warn",phase:"record_start_simulator_runner_warm_failed",data:{deviceId:i.id,session:r.name,appBundleId:n,error:er(e)}});return}}async function eE(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}`,h=eR(i,s,a),w=async()=>c.runIosRunnerCommand(n,{command:"recordStart",outPath:m,fps:d,maxSize:l.maxSize,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(i){var g,y;if(!er(i).toLowerCase().includes("recording already in progress"))return S("COMMAND_FAILED",`failed to start recording: ${er(i)}`);p({level:"warn",phase:"record_start_runner_desynced",data:{platform:n.platform,kind:n.kind,deviceId:n.id,session:a.name,error:er(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 S("COMMAND_FAILED",`failed to start recording: recording already in progress in session '${e.name}'`);try{await c.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 S("COMMAND_FAILED",`failed to start recording: ${er(e)}`)}}return{platform:"ios-device-runner",remotePath:f,runnerStartedAtUptimeMs:t,targetAppReadyUptimeMs:r,...l}}async function eF(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,maxSize:s.maxSize,appBundleId:c},eR(t,a,r))}catch(e){return S("COMMAND_FAILED",`failed to start recording: ${er(e)}`)}return{platform:"macos-runner",...s}}async function eO(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o,recording:n}=e;await eN({req:t,activeSession:r,device:i,logPath:a,deps:o});let s={stdout:"",stderr:"",exitCode:1};for(let e of I)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 S("COMMAND_FAILED",`failed to copy recording from device: ${e}`)}let c="number"!=typeof n.runnerStartedAtUptimeMs||"number"!=typeof n.targetAppReadyUptimeMs?0:Math.max(0,n.targetAppReadyUptimeMs-n.runnerStartedAtUptimeMs);return c>0&&await o.trimRecordingStart({videoPath:n.outPath,trimStartMs:c}),await eo({recording:n,deps:o,trimStartMs:c,targetLabel:"iOS recording"}),null}async function eL(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o,recording:n}=e;return await eN({req:t,activeSession:r,device:i,logPath:a,deps:o}),await eo({recording:n,deps:o,targetLabel:"macOS recording"}),null}async function eT(e){let{deps:t,recording:r}=e;r.child.kill("SIGINT");let i=await ez(r.wait,5e3);return i||(await eU(t,r,"SIGINT"),(i=await ez(r.wait,2e3))||(r.child.kill("SIGTERM"),await eU(t,r,"SIGTERM"),i=await ez(r.wait,2e3)))?i:(r.child.kill("SIGKILL"),await eU(t,r,"SIGKILL"),await ez(r.wait,2e3))}async function ez(e,t){return await Promise.race([e,f(t).then(()=>null)])}async function eU(e,t,r){await eG(e,t,r)||await eK(e,t.outPath,r)}async function eK(e,t,r){let i,a=`simctl.*recordVideo.*${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}`;try{i=await e.runCmd("pgrep",["-f",a],{allowFailure:!0})}catch(e){p({level:"warn",phase:"record_stop_ios_simulator_pgrep_failed",data:{outPath:t,signal:r,error:er(e)}});return}let o=eH(eq(i.stdout)),n=ej(o,r);p({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 eG(e,t,r){let i=t.recorderPid??t.child.pid;if("number"!=typeof i||!Number.isInteger(i)||i<=0)return p({level:"debug",phase:"record_stop_ios_simulator_owned_recorder_unavailable",data:{outPath:t.outPath,signal:r,reason:"missing_recorder_pid"}}),!1;let a=await eV(e,i,t.outPath,r),o=eH([i,...a.pids]),n=ej(o,r);return p({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 eV(e,t,r,i){let a;try{a=await e.runCmd("pgrep",["-P",String(t)],{allowFailure:!0})}catch(e){return p({level:"warn",phase:"record_stop_ios_simulator_owned_pgrep_failed",data:{outPath:r,signal:i,parentPid:t,error:er(e)}}),{pids:[]}}return{pids:eq(a.stdout),exitCode:a.exitCode}}function eH(e){return Array.from(new Set(e)).filter(e=>Number.isInteger(e)&&e>0&&e!==process.pid)}function ej(e,t){let r=0;for(let i of e)try{process.kill(i,t),r+=1}catch{}return r}function eq(e){return e.split(/\s+/).map(e=>Number(e)).filter(e=>Number.isInteger(e)&&e>0)}async function eW(e){"ios"!==e.platform||0!==e.gestureEvents.length&&await f(350)}async function eB(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 f(250)}return Date.now()}async function eX(e){let t,r,{req:i,activeSession:a,device:o,logPath:n,deps:s,recordingBase:c,resolvedOut:d}=e,l=c.showTouches?await ek({req:i,activeSession:a,device:o,logPath:n,deps:s}):void 0,{child:u,wait:m}=s.startIosSimulatorRecording({device:o,outPath:d}),f=await eB(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:e$(a)},eR(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 eQ(e){let t,r,{req:i,sessionName:n,sessionStore:s,activeSession:c,device:d,logPath:l,deps:u}=e;if(c.recording)return S("INVALID_ARGS","recording already in progress");let m=i.flags?.fps,f=i.flags?.quality,h=i.flags?.screenshotMaxSize;if(void 0!==m&&(!Number.isInteger(m)||m<1||m>120))return S("INVALID_ARGS","fps must be an integer between 1 and 120");if(void 0!==f&&void 0===$(f))return S("INVALID_ARGS",`quality must be one of: ${R.join(", ")} (legacy numeric values 5-10 are accepted)`);if(void 0!==h&&(!Number.isInteger(h)||h<1))return S("INVALID_ARGS","max-size must be a positive integer");if(!A("record",d))return S("UNSUPPORTED_OPERATION","record is not supported on this device");let p=i.positionals?.[1]??`./recording-${Date.now()}.mp4`,w=v.expandHome(p,i.meta?.cwd),g=(r=$(i.flags?.quality),{outPath:w,clientOutPath:i.meta?.clientArtifactPaths?.outPath,startedAt:Date.now(),maxSize:i.flags?.screenshotMaxSize,exportQuality:r??"medium",showTouches:i.flags?.hideTouches!==!0,gestureEvents:[]});if(a.mkdirSync(o.dirname(w),{recursive:!0}),a.rmSync(w,{force:!0}),"ios"===d.platform&&"device"===d.kind){let e=e$(c);if(!e)return S("INVALID_ARGS","record on physical iOS devices requires an active app session; run open <app> first");t=await eE({req:i,activeSession:c,sessionStore:s,device:d,logPath:l,deps:u,fpsFlag:m,recordingBase:g,appBundleId:e})}else if("macos"===d.platform){let e=e$(c);if(!e)return S("INVALID_ARGS","record on macOS requires an active app session; run open <app> first");t=await eF({req:i,activeSession:c,device:d,logPath:l,deps:u,fpsFlag:m,recordingBase:g,appBundleId:e})}else t="ios"===d.platform?await eX({req:i,activeSession:c,device:d,logPath:l,deps:u,recordingBase:g,resolvedOut:w}):await eM({device:d,recordingBase:g});if("ok"in t)return t;c.recording=t,s.set(n,c);let y=s.ensureSessionDir(n);return s.recordAction(c,{command:i.command,positionals:i.positionals??[],flags:i.flags??{},result:{action:"start",showTouches:t.showTouches}}),{ok:!0,data:{recording:"started",outPath:t.clientOutPath??p,sessionStateDir:y,showTouches:t.showTouches}}}async function eJ(e){let{deps:t,device:r,recording:i,stopRequestedAt:a}=e;if("android"===i.platform)return await eI({deps:t,device:r,recording:i,stopRequestedAt:a});await h("record_stop_tail_settle",()=>t.waitForRecordingTail(i),{platform:i.platform,gestureEventCount:i.gestureEvents.length});let o=await h("record_stop_ios_simulator_process",()=>eT({deps:t,recording:i}),{outPath:i.outPath});if(!o)return eZ("failed to stop recording: simctl recordVideo did not exit after 5000ms and forced cleanup",i,a);if(0!==o.exitCode)return eZ(`failed to stop recording: ${ei(o,"simctl recordVideo")}`,i,a);if(await h("record_stop_video_stable",()=>t.waitForStableFile(i.outPath,{pollMs:150,attempts:12}),{outPath:i.outPath}),!await h("record_stop_video_playable_check",()=>t.isPlayableVideo(i.outPath),{outPath:i.outPath}))return eZ(`failed to stop recording: ${i.outPath} was not finalized into a playable video`,i,a);if(void 0!==i.maxSize)try{await h("record_stop_resize",()=>t.resizeRecording({videoPath:i.outPath,maxSize:i.maxSize,exportQuality:i.exportQuality,targetLabel:"iOS recording"}),{outPath:i.outPath,maxSize:i.maxSize})}catch(e){i.overlayWarning=`failed to resize recording: ${er(e)}`}return await h("record_stop_finalize_overlay",()=>eo({recording:i,deps:t,targetLabel:"iOS recording"}),{outPath:i.outPath,showTouches:i.showTouches,gestureEventCount:i.gestureEvents.length}),null}function eZ(e,t,r){let i=ea(e,t,r);return function(e){try{a.rmSync(e,{force:!0})}catch{}}(t.outPath),S("COMMAND_FAILED",i.message)}async function eY(e){var t;let r,i,{req:a,activeSession:n,device:s,logPath:c,deps:d}=e;if(!n.recording)return S("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 eO({req:a,activeSession:n,device:s,logPath:c,deps:d,recording:l}):"macos-runner"===l.platform?await eL({req:a,activeSession:n,device:s,logPath:c,deps:d,recording:l}):await eJ({deps:d,device:s,recording:l,stopRequestedAt:u});if(f)return f;if(m&&"ios"===l.platform&&l.showTouches)l.overlayWarning??=`overlay unavailable: ${m}`;else if(m)return S("COMMAND_FAILED",m);return r="android"===(t=l).platform?t.chunks:void 0,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:e0(t,e.index),fileName:o.basename(e0(t,e.index)??e.path)}))),t.telemetryPath&&i.push({field:"telemetryPath",path:t.telemetryPath,localPath:function(e){if(e.clientOutPath)return B(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:e0(t,e.index)??e.path}))}}}function e0(e,t){if("android"===e.platform&&e.clientOutPath)return en(e.clientOutPath,t)}function e1(e,t,r,i={}){r.recordOnlySession&&(i.writeLog&&e.writeSessionLog(r),e.delete(t))}async function e2(e){let{req:t,sessionName:r,sessionStore:i,logPath:a}=e,o={runCmd:async(e,t,r)=>"xcrun"===e?await _(t,r):await l(e,t,r),startIosSimulatorRecording:e=>D().startIosSimulatorRecording(e),runIosRunnerCommand:P,waitForRecordingTail:eW,waitForStableFile:G,isPlayableVideo:V,trimRecordingStart:Y,resizeRecording:et,overlayRecordingTouches:ee},n=i.get(r),s=n?.device??await x(t.flags??{});n||await M(s);let c=n??{name:C(t),sessionScope:b(t),device:s,createdAt:Date.now(),recordOnlySession:!0,actions:[]},d=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(d))return S("INVALID_ARGS","record requires start|stop");if("start"===d)return eQ({req:t,sessionName:r,sessionStore:i,activeSession:c,device:s,logPath:a,deps:o});let u=await eY({req:t,activeSession:c,device:s,logPath:a,deps:o});return u.ok?(i.recordAction(c,{command:t.command,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:u.data?.outPath,showTouches:u.data?.showTouches}}),e1(i,r,c,{writeLog:!0})):e1(i,r,c),u}export{e2 as handleRecordCommand};
25
+ `.trim();async function j(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 p(i)}}async function q(e){try{let t=await W(),r=await u(t,[e],{allowFailure:!0,timeoutMs:1e4,env:k()});if(0===r.exitCode)return!0;if(X(r.stderr,r.stdout))return Q(e);return!1}catch(r){var t;if((t=r)instanceof d&&("TOOL_MISSING"===t.code||X(String(t.details?.stderr??""),String(t.details?.stdout??""))))return Q(e);throw r}}async function W(){e??=T({source:H,cacheName:"video-validator",timeoutMs:3e4});try{return await e}catch(t){throw e=void 0,t}}async function B(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 p(r)}}function X(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 Q(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 Z(e){return[...e].sort((e,t)=>e.tMs-t.tMs)}function Y(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?Z(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)}]})):Z(r))},a.writeFileSync(o,JSON.stringify(n,null,2)),o);return s.telemetryPath=d,d}function ee(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 et(e){var t;let r,i,{videoPath:n,scriptPath:s,scriptArgs:c,commandDescription:l}=e;await j(n),await B(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 F({sourcePath:s});await u(e,["--input",n,"--output",m,...c],{timeoutMs:12e4,env:k()}),await B(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",l,{...e.details,videoPath:n,script:s},e)}finally{a.rmSync(m,{force:!0})}}async function er(e){let{videoPath:t,trimStartMs:i}=e;i>0&&await et({videoPath:t,scriptPath:r??=ee("recording-trim.swift"),scriptArgs:["--trim-start-ms",String(i)],commandDescription:"Failed to trim the start of the iOS recording"})}async function ei(e){let{videoPath:r,telemetryPath:i,exportQuality:a="medium",targetLabel:o="recording"}=e;await et({videoPath:r,scriptPath:t??=ee("recording-overlay.swift"),scriptArgs:["--events",i,"--quality",a],commandDescription:`Failed to add touch overlays to the ${o}`})}async function ea(e){let{videoPath:t,maxSize:r,exportQuality:a="medium",targetLabel:o="recording"}=e;await et({videoPath:t,scriptPath:i??=ee("recording-resize.swift"),scriptArgs:["--max-size",String(r),"--quality",a],commandDescription:`Failed to resize the ${o}`})}function eo(e){return e instanceof Error?e.message:String(e)}function en(e,t){return e.stderr.trim()||e.stdout.trim()||`${t} exited with code ${e.exitCode}`}function es(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 ec(e){let{recording:t,deps:r,trimStartMs:i,targetLabel:a}=e,o=Y({recording:t,trimStartMs:i});if(!t.showTouches)return void f({level:"debug",phase:"record_stop_overlay_skipped",data:{reason:"hide_touches"}});if(0===t.gestureEvents.length)return void f({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 h("record_stop_overlay_export",()=>r.overlayRecordingTouches({videoPath:t.outPath,telemetryPath:o,exportQuality:t.exportQuality,targetLabel:a}),{targetLabel:a,gestureEventCount:t.gestureEvents.length})}catch(e){t.overlayWarning??=`failed to overlay recording touches: ${eo(e)}`}}function ed(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 eu(e){return e.chunks??=[{index:1,path:e.outPath,remotePath:e.remotePath}],e.chunks}async function el(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=eu(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:ed(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 em(e){let{recording:t,deps:r}=e;eu(t).length<=1?await ec({recording:t,deps:r,targetLabel:"Android recording"}):(Y({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 ep(e){for(let t of e.chunks){let r=await eh({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 eh(e){let t,{deps:r,deviceId:i,remotePath:o,outPath:n}=e;for(let e=0;e<2;e+=1){ef(n);let s=w(i),c=await y(o,n,{allowFailure:!0,device:s});if(0!==c.exitCode)t=en(c,"adb pull");else{await r.waitForStableFile(n,{pollMs:250,attempts:20});let t=await r.isPlayableVideo(n);if(f({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;f({level:"warn",phase:"record_stop_android_invalid_video_retry",data:{deviceId:i,remotePath:o,outPath:n,attempt:e+1}})}e<1&&await p(750)}return t?`failed to copy recording from device: ${t}`:(ef(n),"failed to copy recording from device: pulled file is not a playable MP4")}function ef(e){try{a.rmSync(e,{force:!0})}catch{}}let ew={medium:8e6,high:2e7};async function eg(e,t,r){return await g(w(e),t,r)}async function ey(e,t){let r=await eg(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 e_(e,t){for(let r=0;r<40;r+=1){if(!await ey(e,t))return!0;await p(250)}return!await ey(e,t)}async function ev(e,t){let r,i=0;for(let a=0;a<20;a+=1){let a=await eg(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 p(250)}}async function eP(e,t,r){for(let i=0;i<8;i+=1){let a=await eg(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 ey(e,r))break;if(i+1>=2)return!0;await p(250)}return!1}async function eS(e){let{deviceId:t,maxSize:r}=e;if(void 0===r)return;let i=await eg(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 max-size: ${en(i,"adb shell wm size")}`);var o={width:Number(a[1]),height:Number(a[2]),maxSize:r};let n=Math.max(o.width,o.height);if(n<=o.maxSize)return{width:o.width,height:o.height};let s=o.maxSize/n;return{width:eA(o.width,s),height:eA(o.height,s)}}function eA(e,t){return Math.max(2,2*Math.round(e*t/2))}async function eb(e,t){await eg(e,["shell","rm","-f",t],{allowFailure:!0})}async function eM(e,t){let r=await eg(e,["shell","kill","-9",t],{allowFailure:!0});return f({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 ey(e,t))&&await e_(e,t)}async function eC(e){var t;let r,i,{device:a,recordingSize:o,quality:n,preferredRemoteDir:s}=e,c="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"],(s&&i.includes(s)?[s,...i.filter(e=>e!==s)]:i).map(e=>`${e}/${r}`))){let t=await eg(a.id,["shell",function(e,t,r){let i=["screenrecord"];return t&&i.push("--size",`${t.width}x${t.height}`),i.push("--bit-rate",String(ew[r])),i.push(e),`${i.join(" ")} >/dev/null 2>&1 & echo $!`}(e,o,n)],{allowFailure:!0});if(0!==t.exitCode){c=`failed to start recording: ${en(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){c="failed to start recording: adb did not return a valid Android screenrecord pid",await eb(a.id,e);continue}if(f({level:"debug",phase:"record_start_android_started",data:{deviceId:a.id,remotePath:e,remotePid:r}}),await eP(a.id,e,r))return{remotePath:e,remotePid:r,startedAt:Date.now()};c="failed to start recording: Android screenrecord did not begin producing frames",await eM(a.id,r),await eb(a.id,e)}return{error:S("COMMAND_FAILED",c)}}async function ex(e){let t,{device:r,recordingBase:i}=e;try{t=await eS({deviceId:r.id,maxSize:i.maxSize})}catch(e){return S("COMMAND_FAILED",e instanceof Error?e.message:String(e))}let a=i.exportQuality??"medium",o=await eC({device:r,recordingSize:t,quality:a});if("error"in o)return o.error;let n={platform:"android",remotePath:o.remotePath,remotePid:o.remotePid,chunks:[{index:1,path:i.outPath,remotePath:o.remotePath}],...i,startedAt:o.startedAt};return!function e(t){let{recording:r,startNextChunk:i,finishCurrentChunk:a}=t,o=setTimeout(()=>{r.rotationPromise=el({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:n,finishCurrentChunk:async()=>await eI({device:r,recording:n,waitForRemoteFileStability:!1}),startNextChunk:async e=>{let i=await eC({device:r,recordingSize:t,quality:a,preferredRemoteDir:e});if("error"in i)throw Error(i.error.ok?"failed to start next Android recording chunk":i.error.error.message);return i}}),n}async function eI(e){let{device:t,recording:r,waitForRemoteFileStability:i=!0}=e;await ey(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 eg(t.id,["shell","kill","-2",r.remotePid],{allowFailure:!0});if(f({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 eR(t.id,r.remotePid,a);let o=await eD(t.id,r.remotePid);if(o)return o;i&&await ev(t.id,r.remotePath)}async function eR(e,t,r){if(await ey(e,t)&&!await eM(e,t))return`failed to stop recording: ${en(r,"adb shell kill")}`}async function eD(e,t){if(!await e_(e,t)&&!await eM(e,t))return`failed to stop recording: Android screenrecord pid ${t} did not exit`}async function eN(e){let t,{deps:r,device:i,recording:a,stopRequestedAt:o}=e;f({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 eI({device:i,recording:a});if(a.rotationFailedReason&&!n&&(a.warning??=`Android recording chunk rotation failed: ${a.rotationFailedReason}`),!n){let e=await ep({deps:r,deviceId:i.id,chunks:eu(a)});if(e)return await s(),S("COMMAND_FAILED",eE(e,a,o));await em({recording:a,deps:r})}if(await s(),n)return S("COMMAND_FAILED",eE(n,a,o));if(t)return S("COMMAND_FAILED",t);return null;async function s(){for(let e of eu(a)){let r=await eg(i.id,["shell","rm","-f",e.remotePath],{allowFailure:!0});f({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: ${en(r,"adb shell rm")}`)}}}function eE(e,t,r){return es(e,t,r).message}function eO(e){let t=e.appBundleId?.trim();return t&&t.length>0?t:void 0}function e$(e,t,r){return{verbose:e.flags?.verbose,logPath:t,traceLogPath:r.trace?.outPath,requestId:e.meta?.requestId,iosXctestrunFile:e.flags?.iosXctestrunFile,iosXctestDerivedDataPath:e.flags?.iosXctestDerivedDataPath,iosXctestEnvDir:e.flags?.iosXctestEnvDir}}async function ek(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o}=e,n=eO(r);try{await o.runIosRunnerCommand(i,{command:"recordStop",appBundleId:n},e$(t,a,r))}catch(e){f({level:"warn",phase:"record_stop_runner_failed",data:{platform:i.platform,kind:i.kind,deviceId:i.id,session:r.name,error:eo(e)}})}}async function eF(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o}=e,n=eO(r);if(n)try{let e=await o.runIosRunnerCommand(i,{command:"snapshot",appBundleId:n,interactiveOnly:!0,depth:1},e$(t,a,r)),s=Date.now();if("number"==typeof e.currentUptimeMs&&Number.isFinite(e.currentUptimeMs)&&e.currentUptimeMs>0)return f({level:"debug",phase:"record_start_gesture_clock_anchor",data:{source:"warm_snapshot"}}),{gestureClockOriginAtMs:s,gestureClockOriginUptimeMs:e.currentUptimeMs};return}catch(e){f({level:"warn",phase:"record_start_simulator_runner_warm_failed",data:{deviceId:i.id,session:r.name,appBundleId:n,error:eo(e)}});return}}async function eT(e){let t,r,{req:i,activeSession:a,sessionStore:o,device:n,logPath:s,deps:c,fpsFlag:d,recordingBase:u,appBundleId:l}=e,m=`agent-device-recording-${Date.now()}.mp4`,p=`tmp/${m}`,h=e$(i,s,a),w=async()=>c.runIosRunnerCommand(n,{command:"recordStart",outPath:m,fps:d,maxSize:u.maxSize,appBundleId:l},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(i){var g,y;if(!eo(i).toLowerCase().includes("recording already in progress"))return S("COMMAND_FAILED",`failed to start recording: ${eo(i)}`);f({level:"warn",phase:"record_start_runner_desynced",data:{platform:n.platform,kind:n.kind,deviceId:n.id,session:a.name,error:eo(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 S("COMMAND_FAILED",`failed to start recording: recording already in progress in session '${e.name}'`);try{await c.runIosRunnerCommand(n,{command:"recordStop",appBundleId:l},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 S("COMMAND_FAILED",`failed to start recording: ${eo(e)}`)}}return{platform:"ios-device-runner",remotePath:p,runnerStartedAtUptimeMs:t,targetAppReadyUptimeMs:r,...u}}async function eL(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,maxSize:s.maxSize,appBundleId:c},e$(t,a,r))}catch(e){return S("COMMAND_FAILED",`failed to start recording: ${eo(e)}`)}return{platform:"macos-runner",...s}}async function eU(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o,recording:n}=e;await ek({req:t,activeSession:r,device:i,logPath:a,deps:o});let s={stdout:"",stderr:"",exitCode:1};for(let e of x)if(0===(s=await o.runCmd("xcrun",["devicectl","device","copy","from","--device",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 S("COMMAND_FAILED",`failed to copy recording from device: ${e}`)}let c="number"!=typeof n.runnerStartedAtUptimeMs||"number"!=typeof n.targetAppReadyUptimeMs?0:Math.max(0,n.targetAppReadyUptimeMs-n.runnerStartedAtUptimeMs);return c>0&&await o.trimRecordingStart({videoPath:n.outPath,trimStartMs:c}),await ec({recording:n,deps:o,trimStartMs:c,targetLabel:"iOS recording"}),null}async function ez(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o,recording:n}=e;return await ek({req:t,activeSession:r,device:i,logPath:a,deps:o}),await ec({recording:n,deps:o,targetLabel:"macOS recording"}),null}async function eK(e){let{deps:t,recording:r}=e;r.child.kill("SIGINT");let i=await eG(r.wait,5e3);return i||(await eV(t,r,"SIGINT"),(i=await eG(r.wait,2e3))||(r.child.kill("SIGTERM"),await eV(t,r,"SIGTERM"),i=await eG(r.wait,2e3)))?i:(r.child.kill("SIGKILL"),await eV(t,r,"SIGKILL"),await eG(r.wait,2e3))}async function eG(e,t){return await Promise.race([e,p(t).then(()=>null)])}async function eV(e,t,r){await ej(e,t,r)||await eH(e,t.outPath,r)}async function eH(e,t,r){let i,a=`simctl.*recordVideo.*${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}`;try{i=await e.runCmd("pgrep",["-f",a],{allowFailure:!0})}catch(e){f({level:"warn",phase:"record_stop_ios_simulator_pgrep_failed",data:{outPath:t,signal:r,error:eo(e)}});return}let o=eW(eX(i.stdout)),n=eB(o,r);f({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 ej(e,t,r){let i=t.recorderPid??t.child.pid;if("number"!=typeof i||!Number.isInteger(i)||i<=0)return f({level:"debug",phase:"record_stop_ios_simulator_owned_recorder_unavailable",data:{outPath:t.outPath,signal:r,reason:"missing_recorder_pid"}}),!1;let a=await eq(e,i,t.outPath,r),o=eW([i,...a.pids]),n=eB(o,r);return f({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 eq(e,t,r,i){let a;try{a=await e.runCmd("pgrep",["-P",String(t)],{allowFailure:!0})}catch(e){return f({level:"warn",phase:"record_stop_ios_simulator_owned_pgrep_failed",data:{outPath:r,signal:i,parentPid:t,error:eo(e)}}),{pids:[]}}return{pids:eX(a.stdout),exitCode:a.exitCode}}function eW(e){return Array.from(new Set(e)).filter(e=>Number.isInteger(e)&&e>0&&e!==process.pid)}function eB(e,t){let r=0;for(let i of e)try{process.kill(i,t),r+=1}catch{}return r}function eX(e){return e.split(/\s+/).map(e=>Number(e)).filter(e=>Number.isInteger(e)&&e>0)}async function eQ(e){let t,r,{req:i,activeSession:a,device:o,logPath:n,deps:s,recordingBase:c,resolvedOut:d}=e,u=c.showTouches?await eF({req:i,activeSession:a,device:o,logPath:n,deps:s}):void 0,{child:l,wait:m}=s.startIosSimulatorRecording({device:o,outPath:d}),p=await eZ(d);if(u)t=u.gestureClockOriginAtMs,r=u.gestureClockOriginUptimeMs;else if(c.showTouches)try{let e=Date.now(),c=await s.runIosRunnerCommand(o,{command:"uptime",appBundleId:eO(a)},e$(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:l,wait:m,...c,recorderPid:l.pid,startedAt:p,gestureClockOriginAtMs:void 0===r?void 0:t,gestureClockOriginUptimeMs:r}}async function eJ(e){let{deps:t,recording:r,stopRequestedAt:i}=e;await h("record_stop_tail_settle",()=>t.waitForRecordingTail(r),{platform:r.platform,gestureEventCount:r.gestureEvents.length});let a=await h("record_stop_ios_simulator_process",()=>eK({deps:t,recording:r}),{outPath:r.outPath});if(!a)return eY("failed to stop recording: simctl recordVideo did not exit after 5000ms and forced cleanup",r,i);if(0!==a.exitCode)return eY(`failed to stop recording: ${en(a,"simctl recordVideo")}`,r,i);if(await h("record_stop_video_stable",()=>t.waitForStableFile(r.outPath,{pollMs:150,attempts:12}),{outPath:r.outPath}),!await h("record_stop_video_playable_check",()=>t.isPlayableVideo(r.outPath),{outPath:r.outPath}))return eY(`failed to stop recording: ${r.outPath} was not finalized into a playable video`,r,i);if(void 0!==r.maxSize)try{await h("record_stop_resize",()=>t.resizeRecording({videoPath:r.outPath,maxSize:r.maxSize,exportQuality:r.exportQuality,targetLabel:"iOS recording"}),{outPath:r.outPath,maxSize:r.maxSize})}catch(e){r.overlayWarning=`failed to resize recording: ${eo(e)}`}return await h("record_stop_finalize_overlay",()=>ec({recording:r,deps:t,targetLabel:"iOS recording"}),{outPath:r.outPath,showTouches:r.showTouches,gestureEventCount:r.gestureEvents.length}),null}async function eZ(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 p(250)}return Date.now()}function eY(e,t,r){let i=es(e,t,r);return function(e){try{a.rmSync(e,{force:!0})}catch{}}(t.outPath),S("COMMAND_FAILED",i.message)}function e0(e){return"web"===e.platform?e2:"android"===e.platform?e8:"macos"===e.platform?e5:"ios"===e.platform&&"device"===e.kind?e4:"ios"===e.platform?e3:e7}function e1({req:e}){return e.positionals?.[1]??I(void 0)}let e2={validateStart:e=>(function(e){let t,r=(t=[],e.flags?.fps!==void 0&&t.push("--fps"),e.flags?.quality!==void 0&&t.push("--quality"),e.flags?.screenshotMaxSize!==void 0&&t.push("--max-size"),e.flags?.hideTouches!==void 0&&t.push("--hide-touches"),t);return r.length>0?S("INVALID_ARGS",`web recordings do not support ${r.join(", ")}; agent-browser records WebM directly`):null})(e),resolveOutputPath:function({req:e}){let t=e.positionals?.[1];return void 0===t?I("web"):R(t,".webm")},start:async({activeSession:e,recordingBase:t,resolvedOut:r})=>{var i;let a=(i=r,".webm"!==o.extname(i).toLowerCase()?S("INVALID_ARGS","web recordings must use a .webm output path"):null);if(a)return a;if(e.recordOnlySession)return S("INVALID_ARGS","record on web requires an active browser session; run open <url> --platform web first");let n=D();return n.startRecording?(await n.startRecording(r),{...t,outPath:r,startedAt:Date.now(),platform:"web",showTouches:!1}):S("UNSUPPORTED_OPERATION","record is not supported by this web provider")},stop:async({recording:e})=>await e6({recording:e}),cleanupRecordOnlySession:async()=>{try{await D().close()}catch{}}},e4={resolveOutputPath:e1,start:async({req:e,activeSession:t,sessionStore:r,device:i,logPath:a,deps:o,fpsFlag:n,recordingBase:s})=>{let c=eO(t);return c?await eT({req:e,activeSession:t,sessionStore:r,device:i,logPath:a,deps:o,fpsFlag:n,recordingBase:s,appBundleId:c}):S("INVALID_ARGS","record on physical iOS devices requires an active app session; run open <app> first")},stop:async({req:e,activeSession:t,device:r,logPath:i,deps:a,recording:o})=>await eU({req:e,activeSession:t,device:r,logPath:i,deps:a,recording:o})},e5={resolveOutputPath:e1,start:async({req:e,activeSession:t,device:r,logPath:i,deps:a,fpsFlag:o,recordingBase:n})=>{let s=eO(t);return s?await eL({req:e,activeSession:t,device:r,logPath:i,deps:a,fpsFlag:o,recordingBase:n,appBundleId:s}):S("INVALID_ARGS","record on macOS requires an active app session; run open <app> first")},stop:async({req:e,activeSession:t,device:r,logPath:i,deps:a,recording:o})=>await ez({req:e,activeSession:t,device:r,logPath:i,deps:a,recording:o})},e3={resolveOutputPath:e1,start:async({req:e,activeSession:t,device:r,logPath:i,deps:a,recordingBase:o,resolvedOut:n})=>await eQ({req:e,activeSession:t,device:r,logPath:i,deps:a,recordingBase:o,resolvedOut:n}),stop:async({deps:e,recording:t,stopRequestedAt:r})=>await eJ({deps:e,recording:t,stopRequestedAt:r})},e8={resolveOutputPath:e1,start:async({device:e,recordingBase:t})=>await ex({device:e,recordingBase:t}),stop:async({deps:e,device:t,recording:r,stopRequestedAt:i})=>await eN({deps:e,device:t,recording:r,stopRequestedAt:i})},e7={resolveOutputPath:e1,start:async()=>S("UNSUPPORTED_OPERATION","record is not supported on this device"),stop:async()=>S("UNSUPPORTED_OPERATION","record is not supported on this device")};async function e6(e){let{recording:t}=e,r=D();if(!r.stopRecording)return S("UNSUPPORTED_OPERATION","record is not supported by this web provider");if(await r.stopRecording(),!function(e){try{return a.statSync(e).size>0}catch{return!1}}(t.outPath)){var i=t.outPath;try{a.rmSync(i,{force:!0})}catch{}return S("COMMAND_FAILED",`failed to stop recording: ${t.outPath} was not finalized into a WebM video`)}return null}async function e9(e){"ios"!==e.platform||0!==e.gestureEvents.length&&await p(350)}async function te(e){let t,{req:r,sessionName:i,sessionStore:n,activeSession:s,device:c,logPath:d,deps:u}=e;if(s.recording)return S("INVALID_ARGS","recording already in progress");let l=r.flags?.fps,m=r.flags?.quality,p=r.flags?.screenshotMaxSize,h=e0(c),f=h.validateStart?.(r)??null;if(f)return f;if(void 0!==l&&(!Number.isInteger(l)||l<1||l>120))return S("INVALID_ARGS","fps must be an integer between 1 and 120");if(void 0!==m&&void 0===O(m))return S("INVALID_ARGS",`quality must be one of: ${$.join(", ")} (legacy numeric values 5-10 are accepted)`);if(void 0!==p&&(!Number.isInteger(p)||p<1))return S("INVALID_ARGS","max-size must be a positive integer");if(!M("record",c))return S("UNSUPPORTED_OPERATION","record is not supported on this device");let w=h.resolveOutputPath({req:r}),g=v.expandHome(w,r.meta?.cwd),y=(t=O(r.flags?.quality),{outPath:g,clientOutPath:r.meta?.clientArtifactPaths?.outPath,startedAt:Date.now(),maxSize:r.flags?.screenshotMaxSize,exportQuality:t??"medium",showTouches:r.flags?.hideTouches!==!0,gestureEvents:[]});a.mkdirSync(o.dirname(g),{recursive:!0}),a.rmSync(g,{force:!0});let _=await h.start({req:r,activeSession:s,sessionStore:n,device:c,logPath:d,deps:u,fpsFlag:l,recordingBase:y,resolvedOut:g});if("ok"in _)return _;s.recording=_,n.set(i,s);let P=n.ensureSessionDir(i);return n.recordAction(s,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:{action:"start",showTouches:_.showTouches}}),{ok:!0,data:{recording:"started",outPath:_.clientOutPath??w,sessionStateDir:P,showTouches:_.showTouches}}}async function tt(e){var t;let r,i,{req:a,activeSession:n,device:s,logPath:c,deps:d}=e;if(!n.recording)return S("INVALID_ARGS","no active recording");let u=n.recording,l=Date.now(),m=u.invalidatedReason;n.recording=void 0;let p=function(e){switch(e.platform){case"android":return e8;case"ios":return e3;case"ios-device-runner":return e4;case"macos-runner":return e5;case"web":return e2}return e}(u),h=await p.stop({req:a,activeSession:n,device:s,logPath:c,deps:d,recording:u,stopRequestedAt:l});if(h)return h;if(m&&"ios"===u.platform&&u.showTouches)u.overlayWarning??=`overlay unavailable: ${m}`;else if(m)return S("COMMAND_FAILED",m);return r="android"===(t=u).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:tr(t,e.index),fileName:o.basename(tr(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:tr(t,e.index)??e.path}))}}}function tr(e,t){if("android"===e.platform&&e.clientOutPath)return ed(e.clientOutPath,t)}async function ti(e,t,r,i={}){if(!r.recordOnlySession)return;let a=e0(r.device);await a.cleanupRecordOnlySession?.(r),i.writeLog&&e.writeSessionLog(r),e.delete(t)}async function ta(e){let{req:t,sessionName:r,sessionStore:i,logPath:a}=e,o={runCmd:async(e,t,r)=>"xcrun"===e?await _(t,r):await u(e,t,r),startIosSimulatorRecording:e=>E().startIosSimulatorRecording(e),runIosRunnerCommand:P,waitForRecordingTail:e9,waitForStableFile:j,isPlayableVideo:q,trimRecordingStart:er,resizeRecording:ea,overlayRecordingTouches:ei},n=i.get(r),s=n?.device??await C(t.flags??{});n||await N(s);let c=n??{name:b(t),sessionScope:A(t),device:s,createdAt:Date.now(),recordOnlySession:!0,actions:[]},d=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(d))return S("INVALID_ARGS","record requires start|stop");if("start"===d)return te({req:t,sessionName:r,sessionStore:i,activeSession:c,device:s,logPath:a,deps:o});let l=await tt({req:t,activeSession:c,device:s,logPath:a,deps:o});return l.ok?(i.recordAction(c,{command:t.command,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:l.data?.outPath,showTouches:l.data?.showTouches}}),await ti(i,r,c,{writeLog:!0})):await ti(i,r,c),l}export{ta as handleRecordCommand};
@@ -36,17 +36,7 @@ declare type RemoteConfigMetroOptions = {
36
36
  metroNoInstallDeps?: boolean;
37
37
  };
38
38
 
39
- export declare type RemoteConfigProfile = RemoteConfigMetroOptions & {
40
- stateDir?: string;
41
- daemonBaseUrl?: string;
42
- daemonAuthToken?: string;
43
- daemonTransport?: DaemonTransportPreference;
44
- daemonServerMode?: DaemonServerMode;
45
- tenant?: string;
46
- sessionIsolation?: SessionIsolationMode;
47
- runId?: string;
48
- leaseId?: string;
49
- leaseBackend?: LeaseBackend;
39
+ export declare type RemoteConfigProfile = RemoteConfigMetroOptions & RemoteConnectionProfileFields & {
50
40
  platform?: PlatformSelector;
51
41
  target?: DeviceTarget;
52
42
  device?: string;
@@ -63,6 +53,22 @@ export declare type RemoteConfigProfileOptions = {
63
53
  env?: Record<string, string | undefined>;
64
54
  };
65
55
 
56
+ declare type RemoteConnectionProfileFields = {
57
+ stateDir?: string;
58
+ daemonBaseUrl?: string;
59
+ daemonAuthToken?: string;
60
+ daemonTransport?: DaemonTransportPreference;
61
+ daemonServerMode?: DaemonServerMode;
62
+ tenant?: string;
63
+ sessionIsolation?: SessionIsolationMode;
64
+ runId?: string;
65
+ leaseId?: string;
66
+ leaseBackend?: LeaseBackend;
67
+ leaseProvider?: string;
68
+ deviceKey?: string;
69
+ clientId?: string;
70
+ };
71
+
66
72
  export declare type ResolvedRemoteConfigProfile = {
67
73
  resolvedPath: string;
68
74
  profile: RemoteConfigProfile;