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.
- 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
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.18.0.apk.sha256 +1 -0
- 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
- 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
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.18.0.apk.sha256 +1 -0
- 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
- package/dist/src/1848.js +4 -0
- package/dist/src/2577.js +1 -1
- package/dist/src/2948.js +20 -20
- package/dist/src/4291.js +1 -1
- package/dist/src/4723.js +1 -1
- package/dist/src/5341.js +1 -0
- package/dist/src/5643.js +1 -0
- package/dist/src/6918.js +1 -1
- package/dist/src/8275.js +2 -2
- package/dist/src/8936.js +1 -0
- package/dist/src/9414.js +1 -0
- package/dist/src/9581.js +4 -4
- package/dist/src/9722.js +32 -37
- package/dist/src/9919.js +5 -5
- package/dist/src/apple.js +1 -1
- package/dist/src/apps.js +10 -10
- package/dist/src/batch.d.ts +20 -3
- package/dist/src/batch.js +1 -1
- package/dist/src/cli-help.js +49 -32
- package/dist/src/cli.js +22 -25
- package/dist/src/contracts.d.ts +14 -0
- package/dist/src/device-ready.js +2 -0
- package/dist/src/find.js +1 -1
- package/dist/src/index.d.ts +67 -16
- package/dist/src/interaction.js +1 -1
- package/dist/src/internal/bin.js +1 -1
- package/dist/src/lease.js +1 -1
- package/dist/src/metro.js +1 -1
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-trace-recording.js +3 -3
- package/dist/src/remote-config.d.ts +17 -11
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/session.js +11 -8
- package/dist/src/simulator.js +2 -0
- package/dist/src/snapshot.js +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.17.10.apk.sha256 +0 -1
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.17.10.apk.sha256 +0 -1
- package/dist/src/2133.js +0 -1
- package/dist/src/5628.js +0 -1
- package/dist/src/8358.js +0 -4
package/dist/src/index.d.ts
CHANGED
|
@@ -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<
|
|
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?:
|
|
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?:
|
|
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?:
|
|
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';
|
package/dist/src/interaction.js
CHANGED
|
@@ -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};
|
package/dist/src/internal/bin.js
CHANGED
|
@@ -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.
|
|
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
|
|
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"./
|
|
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};
|
package/dist/src/react-native.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dispatchCommand as e,errorResponse as t,isCommandSupportedOnDevice as i}from"./9722.js";import{PUBLIC_COMMANDS as
|
|
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
|
|
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
|
|
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;
|