agent-device 0.11.15 → 0.12.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.
@@ -43,7 +43,6 @@ export declare type AgentDeviceClient = {
43
43
  type: (options: TypeTextOptions) => Promise<CommandRequestResult>;
44
44
  fill: (options: FillOptions) => Promise<CommandRequestResult>;
45
45
  scroll: (options: ScrollOptions_2) => Promise<CommandRequestResult>;
46
- scrollIntoView: (options: ScrollIntoViewOptions_2) => Promise<CommandRequestResult>;
47
46
  pinch: (options: PinchOptions) => Promise<CommandRequestResult>;
48
47
  get: (options: GetOptions) => Promise<CommandRequestResult>;
49
48
  is: (options: IsOptions) => Promise<CommandRequestResult>;
@@ -199,12 +198,14 @@ export declare type AppDeployResult = {
199
198
  };
200
199
 
201
200
  export declare class AppError extends Error {
202
- code: ErrorCode;
201
+ code: AppErrorCode;
203
202
  details?: AppErrorDetails;
204
203
  cause?: unknown;
205
- constructor(code: ErrorCode, message: string, details?: AppErrorDetails, cause?: unknown);
204
+ constructor(code: AppErrorCode, message: string, details?: AppErrorDetails, cause?: unknown);
206
205
  }
207
206
 
207
+ declare type AppErrorCode = 'INVALID_ARGS' | 'DEVICE_NOT_FOUND' | 'TOOL_MISSING' | 'APP_NOT_INSTALLED' | 'UNSUPPORTED_PLATFORM' | 'UNSUPPORTED_OPERATION' | 'COMMAND_FAILED' | 'SESSION_NOT_FOUND' | 'UNAUTHORIZED' | 'UNKNOWN';
208
+
208
209
  declare type AppErrorDetails = Record<string, unknown> & {
209
210
  hint?: string;
210
211
  diagnosticId?: string;
@@ -305,13 +306,6 @@ export declare type BatchStep = {
305
306
  flags?: Record<string, unknown>;
306
307
  };
307
308
 
308
- declare type BatchStep_2 = {
309
- command: string;
310
- positionals?: string[];
311
- flags?: Partial<CommandFlags>;
312
- runtime?: unknown;
313
- };
314
-
315
309
  export declare type CaptureDiffOptions = ClientCommandBaseOptions & Pick<CaptureSnapshotOptions, 'interactiveOnly' | 'compact' | 'depth' | 'scope' | 'raw'> & {
316
310
  kind: 'snapshot';
317
311
  out?: string;
@@ -347,116 +341,14 @@ export declare type CaptureSnapshotResult = {
347
341
  identifiers: AgentDeviceIdentifiers;
348
342
  };
349
343
 
344
+ export declare function centerOfRect(rect: Rect): Point;
345
+
350
346
  export declare type ClickOptions = ClientCommandBaseOptions & SelectorSnapshotCommandOptions & InteractionTarget & RepeatedPressOptions & {
351
347
  button?: 'primary' | 'secondary' | 'middle';
352
348
  };
353
349
 
354
350
  declare type ClientCommandBaseOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions;
355
351
 
356
- declare type CliFlags = {
357
- json: boolean;
358
- config?: string;
359
- remoteConfig?: string;
360
- stateDir?: string;
361
- daemonBaseUrl?: string;
362
- daemonAuthToken?: string;
363
- daemonTransport?: 'auto' | 'socket' | 'http';
364
- daemonServerMode?: 'socket' | 'http' | 'dual';
365
- tenant?: string;
366
- sessionIsolation?: 'none' | 'tenant';
367
- runId?: string;
368
- leaseId?: string;
369
- sessionLock?: 'reject' | 'strip';
370
- sessionLocked?: boolean;
371
- sessionLockConflicts?: 'reject' | 'strip';
372
- platform?: 'ios' | 'macos' | 'android' | 'linux' | 'apple';
373
- target?: 'mobile' | 'tv' | 'desktop';
374
- device?: string;
375
- udid?: string;
376
- serial?: string;
377
- iosSimulatorDeviceSet?: string;
378
- androidDeviceAllowlist?: string;
379
- out?: string;
380
- session?: string;
381
- runtime?: string;
382
- metroHost?: string;
383
- metroPort?: number;
384
- metroProjectRoot?: string;
385
- metroKind?: 'auto' | 'react-native' | 'expo';
386
- metroPublicBaseUrl?: string;
387
- metroProxyBaseUrl?: string;
388
- metroBearerToken?: string;
389
- metroPreparePort?: number;
390
- metroListenHost?: string;
391
- metroStatusHost?: string;
392
- metroStartupTimeoutMs?: number;
393
- metroProbeTimeoutMs?: number;
394
- metroRuntimeFile?: string;
395
- metroNoReuseExisting?: boolean;
396
- metroNoInstallDeps?: boolean;
397
- bundleUrl?: string;
398
- launchUrl?: string;
399
- boot?: boolean;
400
- reuseExisting?: boolean;
401
- verbose?: boolean;
402
- snapshotInteractiveOnly?: boolean;
403
- snapshotDiff?: boolean;
404
- snapshotCompact?: boolean;
405
- snapshotDepth?: number;
406
- snapshotScope?: string;
407
- snapshotRaw?: boolean;
408
- networkInclude?: 'summary' | 'headers' | 'body' | 'all';
409
- overlayRefs?: boolean;
410
- screenshotFullscreen?: boolean;
411
- baseline?: string;
412
- threshold?: string;
413
- appsFilter?: 'user-installed' | 'all';
414
- count?: number;
415
- fps?: number;
416
- hideTouches?: boolean;
417
- intervalMs?: number;
418
- delayMs?: number;
419
- holdMs?: number;
420
- jitterPx?: number;
421
- pixels?: number;
422
- doubleTap?: boolean;
423
- clickButton?: 'primary' | 'secondary' | 'middle';
424
- backMode?: 'in-app' | 'system';
425
- pauseMs?: number;
426
- pattern?: 'one-way' | 'ping-pong';
427
- maxScrolls?: number;
428
- activity?: string;
429
- header?: string[];
430
- saveScript?: boolean | string;
431
- shutdown?: boolean;
432
- relaunch?: boolean;
433
- surface?: 'app' | 'frontmost-app' | 'desktop' | 'menubar';
434
- headless?: boolean;
435
- restart?: boolean;
436
- noRecord?: boolean;
437
- retainPaths?: boolean;
438
- retentionMs?: number;
439
- replayUpdate?: boolean;
440
- failFast?: boolean;
441
- timeoutMs?: number;
442
- retries?: number;
443
- artifactsDir?: string;
444
- reportJunit?: string;
445
- steps?: string;
446
- stepsFile?: string;
447
- findFirst?: boolean;
448
- findLast?: boolean;
449
- batchOnError?: 'stop';
450
- batchMaxSteps?: number;
451
- batchSteps?: Array<{
452
- command: string;
453
- positionals?: string[];
454
- flags?: Record<string, unknown>;
455
- }>;
456
- help: boolean;
457
- version: boolean;
458
- };
459
-
460
352
  export declare type ClipboardCommandOptions = (DeviceCommandBaseOptions & {
461
353
  action: 'read';
462
354
  }) | (DeviceCommandBaseOptions & {
@@ -476,10 +368,6 @@ declare type CommandActionResult<T extends string> = DaemonResponseData & {
476
368
  action?: T;
477
369
  };
478
370
 
479
- declare type CommandFlags = Omit<CliFlags, 'json' | 'help' | 'version' | 'batchSteps'> & {
480
- batchSteps?: BatchStep_2[];
481
- };
482
-
483
371
  export declare type CommandRequestResult = DaemonResponseData;
484
372
 
485
373
  export declare function createAgentDeviceClient(config?: AgentDeviceClientConfig, deps?: {
@@ -503,9 +391,7 @@ declare type DaemonError = {
503
391
  details?: Record<string, unknown>;
504
392
  };
505
393
 
506
- declare type DaemonInstallSource = MaterializeInstallSource;
507
-
508
- declare type DaemonInstallSource_2 = {
394
+ declare type DaemonInstallSource = {
509
395
  kind: 'url';
510
396
  url: string;
511
397
  headers?: Record<string, string>;
@@ -516,20 +402,13 @@ declare type DaemonInstallSource_2 = {
516
402
 
517
403
  declare type DaemonLockPolicy = 'reject' | 'strip';
518
404
 
519
- declare type DaemonRequest = Omit<DaemonRequest_2, 'token' | 'session' | 'flags' | 'meta'> & {
520
- token: string;
521
- session: string;
522
- flags?: CommandFlags;
523
- meta?: DaemonRequestMeta_2;
524
- };
525
-
526
- declare type DaemonRequest_2 = {
405
+ declare type DaemonRequest = {
527
406
  token?: string;
528
407
  session?: string;
529
408
  command: string;
530
409
  positionals: string[];
531
410
  flags?: Record<string, unknown>;
532
- runtime?: SessionRuntimeHints_2;
411
+ runtime?: SessionRuntimeHints;
533
412
  meta?: DaemonRequestMeta;
534
413
  };
535
414
 
@@ -545,7 +424,7 @@ declare type DaemonRequestMeta = {
545
424
  sessionIsolation?: 'none' | 'tenant';
546
425
  uploadedArtifactId?: string;
547
426
  clientArtifactPaths?: Record<string, string>;
548
- installSource?: DaemonInstallSource_2;
427
+ installSource?: DaemonInstallSource;
549
428
  retainMaterializedPaths?: boolean;
550
429
  materializedPathRetentionMs?: number;
551
430
  materializationId?: string;
@@ -553,25 +432,15 @@ declare type DaemonRequestMeta = {
553
432
  lockPlatform?: 'ios' | 'macos' | 'android' | 'linux' | 'apple';
554
433
  };
555
434
 
556
- declare type DaemonRequestMeta_2 = Omit<DaemonRequestMeta, 'installSource' | 'lockPlatform'> & {
557
- installSource?: DaemonInstallSource;
558
- lockPlatform?: PlatformSelector;
559
- leaseBackend?: 'ios-simulator';
560
- };
561
-
562
- declare type DaemonResponse = DaemonResponse_2;
563
-
564
- declare type DaemonResponse_2 = {
435
+ declare type DaemonResponse = {
565
436
  ok: true;
566
- data?: DaemonResponseData_2;
437
+ data?: DaemonResponseData;
567
438
  } | {
568
439
  ok: false;
569
440
  error: DaemonError;
570
441
  };
571
442
 
572
- declare type DaemonResponseData = DaemonResponseData_2;
573
-
574
- declare type DaemonResponseData_2 = Record<string, unknown> & {
443
+ declare type DaemonResponseData = Record<string, unknown> & {
575
444
  artifacts?: DaemonArtifact[];
576
445
  };
577
446
 
@@ -609,8 +478,6 @@ export declare type EnsureSimulatorResult = {
609
478
  identifiers: AgentDeviceIdentifiers;
610
479
  };
611
480
 
612
- declare type ErrorCode = 'INVALID_ARGS' | 'DEVICE_NOT_FOUND' | 'TOOL_MISSING' | 'APP_NOT_INSTALLED' | 'UNSUPPORTED_PLATFORM' | 'UNSUPPORTED_OPERATION' | 'COMMAND_FAILED' | 'SESSION_NOT_FOUND' | 'UNAUTHORIZED' | 'UNKNOWN';
613
-
614
481
  export declare type FillOptions = ClientCommandBaseOptions & SelectorSnapshotCommandOptions & InteractionTarget & {
615
482
  text: string;
616
483
  delayMs?: number;
@@ -706,15 +573,6 @@ export declare type MaterializationReleaseResult = {
706
573
  identifiers: AgentDeviceIdentifiers;
707
574
  };
708
575
 
709
- declare type MaterializeInstallSource = {
710
- kind: 'url';
711
- url: string;
712
- headers?: Record<string, string>;
713
- } | {
714
- kind: 'path';
715
- path: string;
716
- };
717
-
718
576
  declare type MetroBridgeResult = {
719
577
  enabled: boolean;
720
578
  baseUrl: string;
@@ -761,7 +619,7 @@ export declare type MetroPrepareOptions = {
761
619
  export declare type MetroPrepareResult = PrepareMetroRuntimeResult;
762
620
 
763
621
  /** Re-export of {@link SessionRuntimeHints} under the Metro-specific alias used by public API consumers. */
764
- declare type MetroRuntimeHints = SessionRuntimeHints_2;
622
+ declare type MetroRuntimeHints = SessionRuntimeHints;
765
623
 
766
624
  export declare type NetworkOptions = AgentDeviceRequestOverrides & {
767
625
  action?: 'dump' | 'log';
@@ -900,19 +758,6 @@ export declare type ScreenshotOverlayRef = {
900
758
  center: Point;
901
759
  };
902
760
 
903
- declare type ScrollIntoViewOptions_2 = ClientCommandBaseOptions & ({
904
- text: string;
905
- ref?: never;
906
- label?: never;
907
- } | {
908
- ref: string;
909
- label?: string;
910
- text?: never;
911
- }) & {
912
- maxScrolls?: number;
913
- };
914
- export { ScrollIntoViewOptions_2 as ScrollIntoViewOptions }
915
-
916
761
  declare type ScrollOptions_2 = ClientCommandBaseOptions & {
917
762
  direction: 'up' | 'down' | 'left' | 'right';
918
763
  amount?: number;
@@ -938,9 +783,7 @@ export declare type SessionCloseResult = {
938
783
 
939
784
  declare type SessionIsolationMode = 'none' | 'tenant';
940
785
 
941
- declare type SessionRuntimeHints = SessionRuntimeHints_2;
942
-
943
- declare type SessionRuntimeHints_2 = {
786
+ declare type SessionRuntimeHints = {
944
787
  platform?: 'ios' | 'android';
945
788
  metroHost?: string;
946
789
  metroPort?: number;
package/dist/src/index.js CHANGED
@@ -1,2 +1,3 @@
1
- import e from"node:net";import t from"node:http";import r from"node:https";import a from"node:fs";import n from"node:path";import{fileURLToPath as o}from"node:url";import{spawn as i}from"node:child_process";import s,{createHash as l}from"node:crypto";import{resolveUserPath as c,createRequestId as d,emitDiagnostic as u,withDiagnosticTimer as p,expandUserHomePath as m,AppError as f}from"./957.js";import{stopProcessForTakeover as h,prepareMetroRuntime as w,runCmdSync as y,isAgentDeviceDaemonProcess as v,runCmdDetached as I}from"./974.js";import{REMOTE_CONFIG_FIELD_SPECS as g,resolveRemoteConfigProfile as A,REMOTE_OPEN_PROFILE_KEYS as b}from"./924.js";function S(){let e=n.dirname(o(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=n.join(t,"package.json");if(a.existsSync(e))return t;t=n.dirname(t)}return e}function D(e){let t,r=(t=(e??"").trim())?c(t):n.join(m("~"),".agent-device");return{baseDir:r,infoPath:n.join(r,"daemon.json"),lockPath:n.join(r,"daemon.lock"),logPath:n.join(r,"daemon.log"),sessionsDir:n.join(r,"sessions")}}let _="sha256";async function M(e){let{localPath:o,baseUrl:s,token:l}=e,c=a.statSync(o),d=c.isDirectory(),u=n.basename(o),p=d?"app-bundle":"file",m=s.endsWith("/")?s:`${s}/`,h=d?void 0:await E(o);if(h){let e=await k({normalizedBase:m,token:l,hash:h,filename:u,sizeBytes:c.size,artifactType:p});if(e)return e}let w=new URL("upload",m),y="https:"===w.protocol?r:t,v={"x-artifact-type":p,"x-artifact-filename":u,"transfer-encoding":"chunked"};return h&&(v["x-artifact-hash"]=h,v["x-artifact-hash-algorithm"]=_),l&&(v.authorization=`Bearer ${l}`,v["x-agent-device-token"]=l),new Promise((e,t)=>{let r=y.request({protocol:w.protocol,host:w.hostname,port:w.port,method:"POST",path:w.pathname+w.search,headers:v},r=>{let a="";r.setEncoding("utf8"),r.on("data",e=>{a+=e}),r.on("end",()=>{clearTimeout(s);try{let r=JSON.parse(a);if(!r.ok||!r.uploadId)return void t(new f("COMMAND_FAILED",`Upload failed: ${a}`));e(r.uploadId)}catch{t(new f("COMMAND_FAILED",`Invalid upload response: ${a}`))}})}),s=setTimeout(()=>{r.destroy(),t(new f("COMMAND_FAILED","Artifact upload timed out",{timeoutMs:3e5,hint:"The upload to the remote daemon exceeded the 5-minute timeout."}))},3e5);if(r.on("error",e=>{clearTimeout(s),t(new f("COMMAND_FAILED","Failed to upload artifact to remote daemon",{hint:"Verify the remote daemon is reachable and supports artifact uploads."},e))}),d){let e=i("tar",["cf","-","-C",n.dirname(o),n.basename(o)],{stdio:["ignore","pipe","pipe"]});e.stdout.pipe(r),e.on("error",e=>{r.destroy(),t(new f("COMMAND_FAILED","Failed to create tar archive for app bundle",{},e))}),e.on("close",e=>{0!==e&&(r.destroy(),t(new f("COMMAND_FAILED",`tar failed with exit code ${e}`)))})}else{let e=a.createReadStream(o);e.pipe(r),e.on("error",e=>{r.destroy(),t(new f("COMMAND_FAILED","Failed to read local artifact",{},e))})}})}async function k(e){var t;let r=new URL("upload/preflight",e.normalizedBase),a={"content-type":"application/json"};e.token&&(a.authorization=`Bearer ${e.token}`,a["x-agent-device-token"]=e.token);let n=await fetch(r,{method:"POST",headers:a,signal:AbortSignal.timeout(3e4),body:JSON.stringify({hash:e.hash,hashAlgorithm:_,fileName:e.filename,sizeBytes:e.sizeBytes,artifactType:e.artifactType})}).catch(()=>void 0);if(!n?.ok)return;let o=await n.json().catch(()=>void 0);return(t=o)&&"object"==typeof t&&!0===t.ok&&!0===t.cacheHit&&"string"==typeof t.uploadId?o.uploadId:void 0}async function E(e){let t=l(_);return await new Promise((r,n)=>{a.createReadStream(e).on("data",e=>t.update(e)).on("error",e=>{n(new f("COMMAND_FAILED","Failed to read local artifact",{},e))}).on("end",r)}),t.digest("hex")}let P=/(?:^|[^\w$.])(?:import|export)\s+(?:type\s+)?(?:[^'"`]*?\s+from\s+)?['"]([^'"]+)['"]/gm,N=/import\(\s*['"]([^'"]+)['"]\s*\)/gm,T=[".ts",".tsx",".js",".jsx",".mjs",".cjs"];function x(e,t,r){t.lastIndex=0;let a=null;for(;null!==(a=t.exec(e));){let e=a[1]?.trim();e?.startsWith(".")&&r.add(e)}}function R(e){try{return a.statSync(e).isFile()?e:null}catch{return null}}let C=ef(),U=function(e=process.env.AGENT_DEVICE_DAEMON_STARTUP_TIMEOUT_MS){if(!e)return 15e3;let t=Number(e);return Number.isFinite(t)?Math.max(1e3,Math.floor(t)):15e3}(),L=function(e=process.env.AGENT_DEVICE_DAEMON_STARTUP_ATTEMPTS){if(!e)return 2;let t=Number(e);return Number.isFinite(t)?Math.min(5,Math.max(1,Math.floor(t))):2}(),O=["xcodebuild .*AgentDeviceRunnerUITests/RunnerTests/testCommand","xcodebuild .*AgentDeviceRunner\\.env\\.session-","xcodebuild build-for-testing .*ios-runner/AgentDeviceRunner/AgentDeviceRunner\\.xcodeproj"],F=new e.BlockList;async function $(t){let r=t.meta?.requestId??d(),a=!!(t.meta?.debug||t.flags?.verbose),n=function(t){let r,a,n,o=t.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR,i=function(e){let t;if(e){try{t=new URL(e)}catch(t){throw new f("INVALID_ARGS","Invalid daemon base URL",{daemonBaseUrl:e},t instanceof Error?t:void 0)}if("http:"!==t.protocol&&"https:"!==t.protocol)throw new f("INVALID_ARGS","Daemon base URL must use http or https",{daemonBaseUrl:e});return t.toString().replace(/\/+$/,"")}}(t.flags?.daemonBaseUrl??process.env.AGENT_DEVICE_DAEMON_BASE_URL),s=t.flags?.daemonAuthToken??process.env.AGENT_DEVICE_DAEMON_AUTH_TOKEN;var l=i,c=s;if(!(!l||"localhost"===(r=new URL(l).hostname.trim().toLowerCase().replace(/^\[(.*)\]$/,"$1"))||(e.isIPv4(r)?F.check(r,"ipv4"):!!e.isIPv6(r)&&F.check(r,"ipv6")))&&("string"!=typeof c||!(c.trim().length>0)))throw new f("INVALID_ARGS","Remote daemon base URL for non-loopback hosts requires daemon authentication",{daemonBaseUrl:l,hint:"Provide --daemon-auth-token or AGENT_DEVICE_DAEMON_AUTH_TOKEN when using a non-loopback remote daemon URL."});let d=t.flags?.daemonTransport??process.env.AGENT_DEVICE_DAEMON_TRANSPORT,u="auto"===(a=(d??"").trim().toLowerCase())?"auto":"socket"===a?"socket":"http"===a?"http":"auto";if(i&&"socket"===u)throw new f("INVALID_ARGS","Remote daemon base URL only supports HTTP transport. Remove --daemon-transport socket.",{daemonBaseUrl:i});let p="http"===(n=(t.flags?.daemonServerMode??process.env.AGENT_DEVICE_DAEMON_SERVER_MODE??("dual"===d?"dual":void 0)??"").trim().toLowerCase())?"http":"dual"===n?"dual":"socket";return{paths:D(o),transportPreference:u,serverMode:p,remoteBaseUrl:i,remoteAuthToken:s}}(t),o=function(e,t=process.env.AGENT_DEVICE_DAEMON_TIMEOUT_MS){if("test"!==e)return ef(t)}(t.command),i=await p("daemon_startup",async()=>await G(n),{requestId:r,session:t.session}),s=await j(t,i),l={...t,positionals:s.positionals,flags:s.flags,token:i.token,meta:{...t.meta??{},requestId:r,debug:a,cwd:t.meta?.cwd,tenantId:t.meta?.tenantId??t.flags?.tenant,runId:t.meta?.runId??t.flags?.runId,leaseId:t.meta?.leaseId??t.flags?.leaseId,sessionIsolation:t.meta?.sessionIsolation??t.flags?.sessionIsolation,lockPolicy:t.meta?.lockPolicy,lockPlatform:t.meta?.lockPlatform,...s.uploadedArtifactId?{uploadedArtifactId:s.uploadedArtifactId}:{},...s.clientArtifactPaths?{clientArtifactPaths:s.clientArtifactPaths}:{},...s.installSource?{installSource:s.installSource}:{}}};return u({level:"info",phase:"daemon_request_prepare",data:{requestId:r,command:t.command,session:t.session}}),await p("daemon_request",async()=>await ea(i,l,n.transportPreference,o),{requestId:r,command:t.command})}async function j(e,t){let r,o=[...e.positionals??[]],i=e.flags?{...e.flags}:void 0,s=e.meta?.installSource,l={};if(ed(t)){let a=function(e,t){if("screenshot"===e.command){let r=B(e,"path",".png");return t[0]?{field:"path",localPath:r,positionalIndex:0,positionalPath:V("screenshot",".png")}:{field:"path",localPath:r,positionalIndex:0,flagPath:V("screenshot",".png")}}if("record"===e.command&&"start"===(t[0]??"").toLowerCase()){let t=B(e,"outPath",".mp4",1);return{field:"outPath",localPath:t,positionalIndex:1,positionalPath:V("recording",n.extname(t)||".mp4")}}return null}(e,o);a&&(void 0!==a.positionalPath&&(o[a.positionalIndex]=a.positionalPath),void 0!==a.flagPath&&((i??={}).out=a.flagPath),l[a.field]=a.localPath);let c=await q(e,t);c&&(s=c.installSource,r=c.uploadedArtifactId??r)}if(!ed(t)||"install"!==e.command&&"reinstall"!==e.command||o.length<2)return{positionals:o,flags:i,installSource:s,uploadedArtifactId:r,...Object.keys(l).length>0?{clientArtifactPaths:l}:{}};let c=o[1];if(c.startsWith("remote:"))return o[1]=c.slice(7),{positionals:o,flags:i,...Object.keys(l).length>0?{clientArtifactPaths:l}:{}};let d=n.isAbsolute(c)?c:n.resolve(e.meta?.cwd??process.cwd(),c);return a.existsSync(d)?{positionals:o,flags:i,installSource:s,uploadedArtifactId:r=await M({localPath:d,baseUrl:t.baseUrl,token:t.token}),...Object.keys(l).length>0?{clientArtifactPaths:l}:{}}:{positionals:o,flags:i,...Object.keys(l).length>0?{clientArtifactPaths:l}:{}}}async function q(e,t){let r=e.meta?.installSource;if("install_source"!==e.command||!r||"path"!==r.kind)return null;let o=r.path.trim();if(!o)return{installSource:r};if(o.startsWith("remote:"))return{installSource:{...r,path:o.slice(7)}};let i=n.isAbsolute(o)?o:n.resolve(e.meta?.cwd??process.cwd(),o);if(!a.existsSync(i))return{installSource:{...r,path:i}};let s=await M({localPath:i,baseUrl:t.baseUrl,token:t.token});return{installSource:{...r,path:i},uploadedArtifactId:s}}function B(e,t,r,a=0){let o=e.positionals?.[a]??e.flags?.out,i=`${"path"===t?"screenshot":"recording"}-${Date.now()}${r}`,s=o&&o.trim().length>0?o:i;return n.isAbsolute(s)?s:n.resolve(e.meta?.cwd??process.cwd(),s)}function V(e,t){let r=t.startsWith(".")?t:`.${t}`;return n.posix.join("/tmp",`agent-device-${e}-${Date.now()}-${Math.random().toString(36).slice(2,8)}${r}`)}async function G(e){let t;if(e.remoteBaseUrl){let t={transport:"http",token:e.remoteAuthToken??"",pid:0,baseUrl:e.remoteBaseUrl};if(await ee(t,"http"))return t;throw new f("COMMAND_FAILED","Remote daemon is unavailable",{daemonBaseUrl:e.remoteBaseUrl,hint:"Verify AGENT_DEVICE_DAEMON_BASE_URL points to a reachable daemon with GET /health and POST /rpc."})}let r=W(e.paths.infoPath),o=function(){try{let e=S();return JSON.parse(a.readFileSync(n.join(e,"package.json"),"utf8")).version??"0.0.0"}catch{return"0.0.0"}}(),i=function(e,t=S()){try{let r=n.resolve(t),o=[n.resolve(e)],i=new Set,l=[];for(;o.length>0;){let e=o.pop();if(!e||i.has(e))continue;i.add(e);let t=a.statSync(e);if(!t.isFile())continue;let s=n.relative(r,e)||e;l.push(`${s}:${t.size}:${Math.trunc(t.mtimeMs)}`);let c=a.readFileSync(e,"utf8");for(let t of function(e){let t=new Set;return x(e,P,t),x(e,N,t),[...t]}(c)){let r=function(e,t){let r=n.resolve(n.dirname(e),t),a=R(r);if(a)return a;for(let e of T){let t=R(`${r}${e}`);if(t)return t}for(let e of T){let t=R(n.join(r,`index${e}`));if(t)return t}return null}(e,t);r&&o.push(r)}}let c=l.sort().join("|"),d=s.createHash("sha1").update(c).digest("hex");return`graph:${l.length}:${d}`}catch{return"unknown"}}((t=er()).useSrc?t.srcPath:t.distPath,t.root),l=!!r&&await ee(r,e.transportPreference);if(r&&r.version===o&&r.codeSignature===i&&l)return r;r&&(r.version!==o||r.codeSignature!==i||!l)&&(await J(r),Z(e.paths.infoPath)),function(e){let t=X(e);if(!t.hasLock||t.hasInfo)return;let r=Q(e.lockPath);if(!r)return Z(e.lockPath);v(r.pid,r.processStartTime)||Z(e.lockPath)}(e.paths);let c=0;for(let t=1;t<=L;t+=1){await et(e);let r=await z(U,e);if(r)return r;if(await K(e.paths)){c+=1;continue}let a=X(e.paths);if(!(t<L))break;if(!a.hasInfo&&!a.hasLock){await H(150);continue}}let d=X(e.paths);throw new f("COMMAND_FAILED","Failed to start daemon",{kind:"daemon_startup_failed",infoPath:e.paths.infoPath,lockPath:e.paths.lockPath,startupTimeoutMs:U,startupAttempts:L,lockRecoveryCount:c,metadataState:d,hint:function(e,t=D(process.env.AGENT_DEVICE_STATE_DIR)){return e.hasLock&&!e.hasInfo?`Detected ${t.lockPath} without ${t.infoPath}. If no agent-device daemon process is running, delete ${t.lockPath} and retry.`:e.hasLock&&e.hasInfo?`Daemon metadata may be stale. If no agent-device daemon process is running, delete ${t.infoPath} and ${t.lockPath}, then retry.`:`Daemon metadata is missing or stale. Delete ${t.infoPath} if present and retry.`}(d,e.paths)})}async function z(e,t){let r=Date.now();for(;Date.now()-r<e;){let e=W(t.paths.infoPath);if(e&&await ee(e,t.transportPreference))return e;await new Promise(e=>setTimeout(e,100))}return null}async function H(e){await new Promise(t=>setTimeout(t,e))}async function K(e){let t=X(e);if(!t.hasLock||t.hasInfo)return!1;let r=Q(e.lockPath);return r&&v(r.pid,r.processStartTime)&&await h(r.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:r.processStartTime}),Z(e.lockPath),!0}async function J(e){await h(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}function W(e){let t=Y(e);if(!t||"object"!=typeof t)return null;let r="string"==typeof t.token&&t.token.length>0?t.token:null;if(!r)return null;let a=Number.isInteger(t.port)&&Number(t.port)>0,n=Number.isInteger(t.httpPort)&&Number(t.httpPort)>0;if(!a&&!n)return null;let o=t.transport,i="string"==typeof t.version?t.version:void 0,s="string"==typeof t.codeSignature?t.codeSignature:void 0,l="string"==typeof t.processStartTime?t.processStartTime:void 0,c=Number.isInteger(t.pid)&&Number(t.pid)>0;return{token:r,port:a?Number(t.port):void 0,httpPort:n?Number(t.httpPort):void 0,transport:"socket"===o||"http"===o||"dual"===o?o:void 0,pid:c?Number(t.pid):0,version:i,codeSignature:s,processStartTime:l}}function Q(e){let t=Y(e);return t&&"object"==typeof t&&Number.isInteger(t.pid)&&Number(t.pid)>0?{pid:Number(t.pid),processStartTime:"string"==typeof t.processStartTime?t.processStartTime:void 0,startedAt:"number"==typeof t.startedAt?t.startedAt:void 0}:null}F.addSubnet("127.0.0.0",8,"ipv4"),F.addAddress("::1","ipv6"),F.addSubnet("::ffff:127.0.0.0",104,"ipv6");function X(e){return{hasInfo:a.existsSync(e.infoPath),hasLock:a.existsSync(e.lockPath)}}function Y(e){if(!a.existsSync(e))return null;try{return JSON.parse(a.readFileSync(e,"utf8"))}catch{return null}}function Z(e){try{a.existsSync(e)&&a.unlinkSync(e)}catch{}}async function ee(a,n){var o;return"http"===en(a,n)?await function(e){let a=e.baseUrl?eu(e.baseUrl,"health"):e.httpPort?`http://127.0.0.1:${e.httpPort}/health`:null;if(!a)return Promise.resolve(!1);let n=new URL(a),o="https:"===n.protocol?r:t,i=e.baseUrl?3e3:500;return new Promise(e=>{let t=o.request({protocol:n.protocol,host:n.hostname,port:n.port,path:n.pathname+n.search,method:"GET",timeout:i},t=>{t.resume(),e((t.statusCode??500)<500)});t.on("timeout",()=>{t.destroy(),e(!1)}),t.on("error",()=>{e(!1)}),t.end()})}(a):await ((o=a.port)?new Promise(t=>{let r=e.createConnection({host:"127.0.0.1",port:o},()=>{r.destroy(),t(!0)});r.on("error",()=>{t(!1)})}):Promise.resolve(!1))}async function et(e){let t=er(),r=t.useSrc?["--experimental-strip-types",t.srcPath]:[t.distPath],a={...process.env,AGENT_DEVICE_STATE_DIR:e.paths.baseDir,AGENT_DEVICE_DAEMON_SERVER_MODE:e.serverMode};I(process.execPath,r,{env:a})}function er(){let e=S(),t=n.join(e,"dist","src","daemon.js"),r=n.join(e,"src","daemon.ts"),o=a.existsSync(t),i=a.existsSync(r);if(!o&&!i)throw new f("COMMAND_FAILED","Daemon entry not found",{distPath:t,srcPath:r});return{root:e,distPath:t,srcPath:r,useSrc:process.execArgv.includes("--experimental-strip-types")?i:!o&&i}}async function ea(e,t,r,a){return"http"===en(e,r)?await ec(e,t,a):await el(e,t,a)}function en(e,t){if(e.baseUrl){if("socket"===t)throw new f("COMMAND_FAILED","Remote daemon endpoint only supports HTTP transport",{daemonBaseUrl:e.baseUrl});return"http"}if("http"===t||"socket"===t){var r=e,a=t;if(eo(r,a))return a;throw new f("COMMAND_FAILED","http"===a?"Daemon HTTP endpoint is unavailable":"Daemon socket endpoint is unavailable")}let n=("socket"===e.transport||"dual"===e.transport?["socket","http"]:["http","socket"]).find(t=>eo(e,t));if(n)return n;throw new f("COMMAND_FAILED","Daemon metadata has no reachable transport")}function eo(e,t){return"http"===t?!!e.httpPort:!!e.port}function ei(e,t,r,a,n,o){let i=n?{terminated:0}:function(){let e=0;try{for(let t of O){let r=y("pkill",["-f",t],{allowFailure:!0});0===r.exitCode&&(e+=1)}return{terminated:e}}catch(t){return{terminated:e,error:t instanceof Error?t.message:String(t)}}}(),s=n?{forcedKill:!1}:function(e,t){let r=!1;try{v(e.pid,e.processStartTime)&&(process.kill(e.pid,"SIGKILL"),r=!0)}catch{h(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}finally{Z(t.infoPath),Z(t.lockPath)}return{forcedKill:r}}(e,t);return u({level:"error",phase:"daemon_request_timeout",data:{timeoutMs:o,requestId:r,command:a,timedOutRunnerPidsTerminated:i.terminated,timedOutRunnerCleanupError:i.error,daemonPidReset:n?void 0:e.pid,daemonPidForceKilled:n?void 0:s.forcedKill,daemonBaseUrl:e.baseUrl}}),new f("COMMAND_FAILED","Daemon request timed out",{timeoutMs:o,requestId:r,hint:n?"Retry with --debug and verify the remote daemon URL, auth token, and remote host logs.":"Retry with --debug and check daemon diagnostics logs. Timed-out iOS runner xcodebuild processes were terminated when detected."})}function es(e,t,r){return u({level:"error",phase:"daemon_request_socket_error",data:{requestId:t,message:e instanceof Error?e.message:String(e)}}),new f("COMMAND_FAILED","Failed to communicate with daemon",{requestId:t,hint:r?"Retry command. If this persists, verify the remote daemon URL, auth token, and remote host reachability.":"Retry command. If this persists, clean stale daemon metadata and start a fresh session."},e instanceof Error?e:void 0)}async function el(t,r,a){let n=t.port;if(!n)throw new f("COMMAND_FAILED","Daemon socket endpoint is unavailable");return new Promise((o,i)=>{let s=e.createConnection({host:"127.0.0.1",port:n},()=>{s.write(`${JSON.stringify(r)}
2
- `)}),l=D(r.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),c="number"==typeof a?setTimeout(()=>{s.destroy(),i(ei(t,l,r.meta?.requestId,r.command,!1,a))},a):void 0,d="";s.setEncoding("utf8"),s.on("data",e=>{let t=(d+=e).indexOf("\n");if(-1===t)return;let a=d.slice(0,t).trim();if(a)try{let e=JSON.parse(a);s.end(),c&&clearTimeout(c),o(e)}catch(e){c&&clearTimeout(c),i(new f("COMMAND_FAILED","Invalid daemon response",{requestId:r.meta?.requestId,line:a},e instanceof Error?e:void 0))}}),s.on("error",e=>{c&&clearTimeout(c),i(es(e,r.meta?.requestId,!1))})})}async function ec(e,a,n){let o=e.baseUrl?new URL(eu(e.baseUrl,"rpc")):e.httpPort?new URL(`http://127.0.0.1:${e.httpPort}/rpc`):null;if(!o)throw new f("COMMAND_FAILED","Daemon HTTP endpoint is unavailable");let i=JSON.stringify({jsonrpc:"2.0",id:a.meta?.requestId??d(),method:"agent_device.command",params:a}),s={"content-type":"application/json","content-length":Buffer.byteLength(i)};return e.baseUrl&&e.token&&(s.authorization=`Bearer ${e.token}`,s["x-agent-device-token"]=e.token),await new Promise((l,c)=>{let d=D(a.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),u=("https:"===o.protocol?r:t).request({protocol:o.protocol,host:o.hostname,port:o.port,method:"POST",path:o.pathname+o.search,headers:s},t=>{let r="";t.setEncoding("utf8"),t.on("data",e=>{r+=e}),t.on("end",()=>{m&&clearTimeout(m);try{let t=JSON.parse(r);if(t.error){let e=t.error.data??{};c(new f(String(e.code??"COMMAND_FAILED"),String(e.message??t.error.message??"Daemon RPC request failed"),{..."object"==typeof e.details&&e.details?e.details:{},hint:"string"==typeof e.hint?e.hint:void 0,diagnosticId:"string"==typeof e.diagnosticId?e.diagnosticId:void 0,logPath:"string"==typeof e.logPath?e.logPath:void 0,requestId:a.meta?.requestId}));return}if(!t.result||"object"!=typeof t.result)return void c(new f("COMMAND_FAILED","Invalid daemon RPC response",{requestId:a.meta?.requestId}));if(e.baseUrl&&t.result.ok)return void ep(e,a,t.result).then(l).catch(c);l(t.result)}catch(e){m&&clearTimeout(m),c(new f("COMMAND_FAILED","Invalid daemon response",{requestId:a.meta?.requestId,line:r},e instanceof Error?e:void 0))}})}),p=ed(e),m="number"==typeof n?setTimeout(()=>{u.destroy(),c(ei(e,d,a.meta?.requestId,a.command,p,n))},n):void 0;u.on("error",e=>{m&&clearTimeout(m),c(es(e,a.meta?.requestId,p))}),u.write(i),u.end()})}function ed(e){return"string"==typeof e.baseUrl&&e.baseUrl.length>0}function eu(e,t){return new URL(t,e.endsWith("/")?e:`${e}/`).toString()}async function ep(e,t,r){let a=Array.isArray(r.data?.artifacts)?r.data.artifacts:[];if(0===a.length||!e.baseUrl)return r;let o=r.data?{...r.data}:{},i=[];for(let r of a){if(!r||"object"!=typeof r||"string"!=typeof r.artifactId){i.push(r);continue}let a=function(e,t){if(e.localPath&&e.localPath.trim().length>0)return e.localPath;let r=e.fileName?.trim()||`${e.field}-${Date.now()}`;return n.resolve(t.meta?.cwd??process.cwd(),r)}(r,t);await em({baseUrl:e.baseUrl,token:e.token,artifactId:r.artifactId,destinationPath:a,requestId:t.meta?.requestId}),o[r.field]=a,i.push({...r,localPath:a})}return o.artifacts=i,{ok:!0,data:o}}async function em(e){var o,i;let s,l=new URL((o=e.baseUrl,i=e.artifactId,s=o.endsWith("/")?o:`${o}/`,new URL(`upload/${encodeURIComponent(i)}`,s).toString())),c="https:"===l.protocol?r:t;await a.promises.mkdir(n.dirname(e.destinationPath),{recursive:!0}),await new Promise((t,r)=>{let n=!1,o=e.timeoutMs??C,i=o=>{if(!n){if(n=!0,clearTimeout(d),o)return void a.promises.rm(e.destinationPath,{force:!0}).finally(()=>r(o));t()}},s=c.request({protocol:l.protocol,host:l.hostname,port:l.port,method:"GET",path:l.pathname+l.search,headers:e.token?{authorization:`Bearer ${e.token}`,"x-agent-device-token":e.token}:void 0},t=>{if((t.statusCode??500)>=400){let r="";t.setEncoding("utf8"),t.on("data",e=>{r+=e}),t.on("end",()=>{i(new f("COMMAND_FAILED","Failed to download remote artifact",{artifactId:e.artifactId,statusCode:t.statusCode,requestId:e.requestId,body:r}))});return}let r=a.createWriteStream(e.destinationPath);r.on("error",e=>{i(e instanceof Error?e:Error(String(e)))}),t.on("error",e=>{i(e instanceof Error?e:Error(String(e)))}),t.on("aborted",()=>{i(new f("COMMAND_FAILED","Remote artifact download was interrupted",{artifactId:e.artifactId,requestId:e.requestId}))}),r.on("finish",()=>{r.close(()=>i())}),t.pipe(r)}),d=setTimeout(()=>{s.destroy(new f("COMMAND_FAILED","Remote artifact download timed out",{artifactId:e.artifactId,requestId:e.requestId,timeoutMs:o}))},o);s.on("error",t=>{t instanceof f?i(t):i(new f("COMMAND_FAILED","Failed to download remote artifact",{artifactId:e.artifactId,requestId:e.requestId,timeoutMs:o},t instanceof Error?t:void 0))}),s.end()})}function ef(e=process.env.AGENT_DEVICE_DAEMON_TIMEOUT_MS){if(!e)return 9e4;let t=Number(e);return Number.isFinite(t)?Math.max(1e3,Math.floor(t)):9e4}let eh="clipboard",ew="wait",ey=new Set(["id","role","text","label","value","appname","windowtitle"]),ev=new Set(["visible","hidden","editable","selected","enabled","hittable"]),eI=new Set([...ey,...ev]);function eg(e){let t=e.trim();if(!t)throw new f("INVALID_ARGS","Empty selector term");let r=t.indexOf("=");if(-1===r){let r=t.toLowerCase();if(!ev.has(r))throw new f("INVALID_ARGS",`Invalid selector term "${e}", expected key=value`);return{key:r,value:!0}}let a=t.slice(0,r).trim().toLowerCase(),n=t.slice(r+1).trim();if(!eI.has(a))throw new f("INVALID_ARGS",`Unknown selector key: ${a}`);if(!n)throw new f("INVALID_ARGS",`Missing selector value for key: ${a}`);if(ev.has(a)){let e,t="true"===(e=eb(n).toLowerCase())||"false"!==e&&null;if(null===t)throw new f("INVALID_ARGS",`Invalid boolean value for ${a}: ${n}`);return{key:a,value:t}}return{key:a,value:eb(n)}}function eA(e,t){return e?e===t?null:e:t}function eb(e){let t=e.trim();return t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1).replace(/\\(["'])/g,"$1"):t}function eS(e,t){let r=0;for(let a=t-1;a>=0&&"\\"===e[a];a-=1)r+=1;return r%2==1}let eD=g.map(e=>e.key);function e_(e){let t=e.appId??e.bundleId??e.packageName;return{session:e.session,appId:t,appBundleId:e.bundleId,package:e.packageName}}function eM(e,t,r){return{deviceId:t,deviceName:r,..."android"===e?{serial:t}:"ios"===e?{udid:t}:{}}}function ek(e,t,r,a){let n=r(e[t]);if(void 0===n)throw new f("COMMAND_FAILED",a,{response:e});return n}[...b];function eE(e,t){return ek(e,t,eC,`Daemon response is missing "${t}".`)}function eP(e,t){return eC(e[t])}function eN(e,t){var r;let a;return r=eC,null===(a=e[t])?null:r(a)}function eT(e,t){return ek(e,t,eL,`Daemon response has invalid "${t}".`)}function ex(e,t){return function(e){return"tv"===e||"mobile"===e||"desktop"===e?e:void 0}(e[t])??"mobile"}function eR(e,t){let r=e[t];if(!e$(r))return;let a="number"==typeof r.x?r.x:void 0,n="number"==typeof r.y?r.y:void 0,o="number"==typeof r.width?r.width:void 0,i="number"==typeof r.height?r.height:void 0;if(void 0!==a&&void 0!==n&&void 0!==o&&void 0!==i)return{x:a,y:n,width:o,height:i}}function eC(e){return"string"==typeof e&&e.length>0?e:void 0}function eU(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function eL(e){return"ios"===e||"macos"===e||"android"===e?e:void 0}function eO(e){return"simulator"===e||"emulator"===e||"device"===e?e:void 0}function eF(e){if(!e$(e))throw new f("COMMAND_FAILED","Daemon returned an unexpected response shape.",{value:e});return e}function e$(e){return"object"==typeof e&&null!==e}function ej(e){let t={};for(let[r,a]of Object.entries(e))void 0!==a&&(t[r]=a);return t}function eq(e,t){let r=eP(e,"bundleId"),a=eP(e,"package");return{app:eE(e,"app"),appPath:eE(e,"appPath"),platform:eT(e,"platform"),appId:r??a,bundleId:r,package:a,identifiers:e_({session:t,bundleId:r,packageName:a})}}function eB(e){let t=eF(e),r=eT(t,"platform"),a=eE(t,"id"),n=eE(t,"name");return{platform:r,target:ex(t,"target"),kind:ek(t,"kind",eO,'Daemon response has invalid "kind".'),id:a,name:n,booted:"boolean"==typeof t.booted?t.booted:void 0,identifiers:eM(r,a,n),ios:"ios"===r?{udid:a}:void 0,android:"android"===r?{serial:a}:void 0}}function eV(e){let t=eF(e),r=eT(t,"platform"),a=eE(t,"id"),n=eE(t,"name"),o=ex(t,"target"),i=eE(t,"device"),s={session:n,...eM(r,a,i)};return{name:n,createdAt:ek(t,"createdAt",eU,'Daemon response is missing numeric "createdAt".'),device:{platform:r,target:o,id:a,name:i,identifiers:s,ios:"ios"===r?{udid:a,simulatorSetPath:eN(t,"ios_simulator_device_set")}:void 0,android:"android"===r?{serial:a}:void 0},identifiers:s}}function eG(e){return e??"default"}function ez(e={},t={}){var r;let a,n=t.transport??$,o=eQ(e),i=async(t,r=[],a={})=>{let i=eW(e,o,a),s=await n({session:eG(i.session),command:t,positionals:r,flags:ej({stateDir:i.stateDir,daemonBaseUrl:i.daemonBaseUrl,daemonAuthToken:i.daemonAuthToken,daemonTransport:i.daemonTransport,daemonServerMode:i.daemonServerMode,tenant:i.tenant,sessionIsolation:i.sessionIsolation,runId:i.runId,leaseId:i.leaseId,platform:i.platform,target:i.target,device:i.device,udid:i.udid,serial:i.serial,iosSimulatorDeviceSet:i.iosSimulatorDeviceSet,androidDeviceAllowlist:i.androidDeviceAllowlist,runtime:i.simulatorRuntimeId,boot:i.boot,reuseExisting:i.reuseExisting,surface:i.surface,activity:i.activity,relaunch:i.relaunch,shutdown:i.shutdown,saveScript:i.saveScript,noRecord:i.noRecord,backMode:i.backMode,metroHost:i.metroHost,metroPort:i.metroPort,bundleUrl:i.bundleUrl,launchUrl:i.launchUrl,snapshotInteractiveOnly:i.interactiveOnly,snapshotCompact:i.compact,snapshotDepth:i.depth,snapshotScope:i.scope,snapshotRaw:i.raw,screenshotFullscreen:i.screenshotFullscreen,overlayRefs:i.overlayRefs,appsFilter:i.appsFilter,out:i.out,count:i.count,fps:i.fps,hideTouches:i.hideTouches,intervalMs:i.intervalMs,delayMs:i.delayMs,holdMs:i.holdMs,jitterPx:i.jitterPx,pixels:i.pixels,doubleTap:i.doubleTap,clickButton:i.clickButton,pauseMs:i.pauseMs,pattern:i.pattern,maxScrolls:i.maxScrolls,headless:i.headless,restart:i.restart,replayUpdate:i.replayUpdate,failFast:i.failFast,timeoutMs:i.timeoutMs,retries:i.retries,artifactsDir:i.artifactsDir,reportJunit:i.reportJunit,findFirst:i.findFirst,findLast:i.findLast,networkInclude:i.networkInclude,batchOnError:i.batchOnError,batchMaxSteps:i.batchMaxSteps,batchSteps:i.batchSteps,verbose:i.debug}),runtime:i.runtime,meta:ej({requestId:i.requestId,cwd:i.cwd,debug:i.debug,lockPolicy:i.lockPolicy,lockPlatform:i.lockPlatform,tenantId:i.tenant,runId:i.runId,leaseId:i.leaseId,sessionIsolation:i.sessionIsolation,installSource:i.installSource,retainMaterializedPaths:i.retainMaterializedPaths,materializedPathRetentionMs:i.materializedPathRetentionMs,materializationId:i.materializationId})});return s.ok||function(e){throw new f(e.code,e.message,{...e.details??{},hint:e.hint,diagnosticId:e.diagnosticId,logPath:e.logPath})}(s.error),s.data??{}},s=async(e={})=>{let t=await i("session_list",[],e);return(Array.isArray(t.sessions)?t.sessions:[]).map(eV)},l=async(e,t=[],r={})=>await i(e,t,r),c=(t={})=>eG(eW(e,o,t).session);return{command:(r=async e=>await i(e.command,e.positionals,e.options),a=async e=>await r(e),{wait:async e=>await a(function(e){if(1!==[void 0!==e.durationMs?"durationMs":void 0,void 0!==e.text?"text":void 0,void 0!==e.ref?"ref":void 0,void 0!==e.selector?"selector":void 0].filter(Boolean).length)throw new f("INVALID_ARGS","wait command requires exactly one of durationMs, text, ref, or selector.");if(void 0!==e.durationMs)return{command:ew,positionals:[String(e.durationMs)],options:e};let t=void 0!==e.timeoutMs?[String(e.timeoutMs)]:[];if(void 0!==e.text)return{command:ew,positionals:["text",e.text,...t],options:e};if(void 0!==e.ref)return{command:ew,positionals:[e.ref,...t],options:e};let r=e.selector;return function(e){if(!function(e){try{let t=e.trim();if(!t)throw new f("INVALID_ARGS","Selector expression cannot be empty");let r=function(e){let t=[],r="",a=null;for(let n=0;n<e.length;n+=1){let o=e[n];if(('"'===o||"'"===o)&&!eS(e,n)){a=eA(a,o),r+=o;continue}if(!a&&"|"===o&&"|"===e[n+1]){let a=r.trim();if(!a)throw new f("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);t.push(a),r="",n+=1;continue}r+=o}let n=r.trim();if(!n)throw new f("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);return t.push(n),t}(t);if(0===r.length)throw new f("INVALID_ARGS","Selector expression cannot be empty");return{raw:t,selectors:r.map(e=>(function(e){let t=e.trim();if(!t)throw new f("INVALID_ARGS","Selector segment cannot be empty");let r=function(e){let t=[],r="",a=null;for(let n=0;n<e.length;n+=1){let o=e[n];if(('"'===o||"'"===o)&&!eS(e,n)){a=eA(a,o),r+=o;continue}if(!a&&/\s/.test(o)){r.trim()&&t.push(r.trim()),r="";continue}r+=o}if(a)throw new f("INVALID_ARGS",`Unclosed quote in selector: ${e}`);return r.trim()&&t.push(r.trim()),t}(t);if(0===r.length)throw new f("INVALID_ARGS",`Invalid selector segment: ${e}`);return{raw:t,terms:r.map(eg)}})(e))}}catch{return null}}(e))throw new f("INVALID_ARGS",`Invalid wait selector: ${e}`)}(r),{command:ew,positionals:[r,...t],options:e}}(e)),alert:async(e={})=>{var t;return await a({command:"alert",positionals:[(t=e).action??"get",...void 0!==t.timeoutMs?[String(t.timeoutMs)]:[]],options:t})},appState:async(e={})=>await a({command:"appstate",positionals:[],options:e}),back:async(e={})=>await a({command:"back",positionals:[],options:{...e,backMode:e.mode}}),home:async(e={})=>await a({command:"home",positionals:[],options:e}),rotate:async e=>await a({command:"rotate",positionals:[e.orientation],options:e}),appSwitcher:async(e={})=>await a({command:"app-switcher",positionals:[],options:e}),keyboard:async(e={})=>await a({command:"keyboard",positionals:e.action?[e.action]:[],options:e}),clipboard:async e=>{var t;return await a("read"===(t=e).action?{command:eh,positionals:["read"],options:t}:{command:eh,positionals:["write",t.text],options:t})}}),devices:{list:async(e={})=>{let t=await i("devices",[],e);return(Array.isArray(t.devices)?t.devices:[]).map(eB)},boot:async(e={})=>await l("boot",[],e)},sessions:{list:async(e={})=>await s(e),close:async(e={})=>{let t=c(e),r=(await i("close",[],e)).shutdown;return{session:t,shutdown:"object"==typeof r&&null!==r?r:void 0,identifiers:{session:t}}}},simulators:{ensure:async e=>{let{runtime:t,...r}=e,a=await i("ensure-simulator",[],{...r,simulatorRuntimeId:t}),n=eE(a,"udid"),o=eE(a,"device");return{udid:n,device:o,runtime:eE(a,"runtime"),created:!0===a.created,booted:!0===a.booted,iosSimulatorDeviceSet:eN(a,"ios_simulator_device_set"),identifiers:{deviceId:n,deviceName:o,udid:n}}}},apps:{install:async e=>eq(await i("install",[e.app,e.appPath],e),c(e)),reinstall:async e=>eq(await i("reinstall",[e.app,e.appPath],e),c(e)),installFromSource:async e=>(function(e,t){let r=eP(e,"bundleId"),a=eP(e,"packageName"),n=r??a??eP(e,"appId"),o=eP(e,"launchTarget")??a??r??n;if(!o)throw new f("COMMAND_FAILED",'Daemon response is missing "launchTarget".',{response:e});return{appName:eP(e,"appName"),appId:n,bundleId:r,packageName:a,launchTarget:o,installablePath:eP(e,"installablePath"),archivePath:eP(e,"archivePath"),materializationId:eP(e,"materializationId"),materializationExpiresAt:eP(e,"materializationExpiresAt"),identifiers:e_({session:t,bundleId:r,packageName:a,appId:n})}})(await i("install_source",[],{...e,installSource:e.source,retainMaterializedPaths:e.retainPaths,materializedPathRetentionMs:e.retentionMs}),c(e)),list:async(e={})=>{let t=await i("apps",[],e);return Array.isArray(t.apps)?t.apps.filter(e=>"string"==typeof e):[]},open:async e=>{let t=c(e),r=e.app?e.url?[e.app,e.url]:[e.app]:[],a=await i("open",r,e),n=function(e){let t=e.platform,r=eP(e,"id"),a=eP(e,"device");if("ios"!==t&&"macos"!==t&&"android"!==t||!r||!a)return;let n=ex(e,"target"),o=eM(t,r,a);return{platform:t,target:n,id:r,name:a,identifiers:o,ios:"ios"===t?{udid:eP(e,"device_udid")??r,simulatorSetPath:eN(e,"ios_simulator_device_set")}:void 0,android:"android"===t?{serial:eP(e,"serial")??r}:void 0}}(a),o=eP(a,"appBundleId");return{session:t,appName:eP(a,"appName"),appBundleId:o,appId:o,startup:function(e){if(e$(e)&&"number"==typeof e.durationMs&&"string"==typeof e.measuredAt&&"string"==typeof e.method)return{durationMs:e.durationMs,measuredAt:e.measuredAt,method:e.method,appTarget:eP(e,"appTarget"),appBundleId:eP(e,"appBundleId")}}(a.startup),runtime:function(e){if(!e$(e))return;let t=e.platform,r=eP(e,"metroHost"),a="number"==typeof e.metroPort?e.metroPort:void 0;return{platform:"ios"===t||"android"===t?t:void 0,metroHost:r,metroPort:a,bundleUrl:eP(e,"bundleUrl"),launchUrl:eP(e,"launchUrl")}}(a.runtime),device:n,identifiers:{session:t,deviceId:n?.id,deviceName:n?.name,udid:n?.ios?.udid,serial:n?.android?.serial,appId:o,appBundleId:o}}},close:async(e={})=>{let t=c(e),r=(await i("close",e.app?[e.app]:[],e)).shutdown;return{session:t,closedApp:e.app,shutdown:"object"==typeof r&&null!==r?r:void 0,identifiers:{session:t}}},push:async e=>{var t;return await l("push",[e.app,"string"==typeof(t=e.payload)?t:JSON.stringify(t)],e)},triggerEvent:async e=>{var t;return await l("trigger-app-event",[(t=e).event,...t.payload?[JSON.stringify(t.payload)]:[]],e)}},materializations:{release:async e=>{var t;return{released:!0===(t=await i("release_materialized_paths",[],{...e,materializationId:e.materializationId})).released,materializationId:eE(t,"materializationId"),identifiers:{}}}},metro:{prepare:async t=>await w({projectRoot:t.projectRoot??e.cwd,kind:t.kind,publicBaseUrl:t.publicBaseUrl,proxyBaseUrl:t.proxyBaseUrl,proxyBearerToken:t.bearerToken,launchUrl:t.launchUrl,companionProfileKey:t.companionProfileKey,companionConsumerKey:t.companionConsumerKey,metroPort:t.port,listenHost:t.listenHost,statusHost:t.statusHost,startupTimeoutMs:t.startupTimeoutMs,probeTimeoutMs:t.probeTimeoutMs,reuseExisting:t.reuseExisting,installDependenciesIfNeeded:t.installDependenciesIfNeeded,runtimeFilePath:t.runtimeFilePath,logPath:t.logPath})},capture:{snapshot:async(e={})=>{var t;let r=c(e),a=await i("snapshot",[],e),n=eP(a,"appBundleId"),o="object"==typeof a.visibility&&null!==a.visibility?a.visibility:void 0;return{nodes:Array.isArray(t=a.nodes)?t:[],truncated:!0===a.truncated,appName:eP(a,"appName"),appBundleId:n,...o?{visibility:o}:{},warnings:Array.isArray(a.warnings)?a.warnings.filter(e=>"string"==typeof e):void 0,identifiers:{session:r,appId:n,appBundleId:n}}},screenshot:async(e={})=>{let t=c(e),r=await i("screenshot",e.path?[e.path]:[],{...e,screenshotFullscreen:e.fullscreen});return{path:eE(r,"path"),overlayRefs:function(e){let t=e.overlayRefs;if(!Array.isArray(t))return;let r=[];for(let e of t){if(!e$(e))continue;let t=eP(e,"ref"),a=eR(e,"rect"),n=eR(e,"overlayRect"),o=function(e,t){let r=e[t];if(!e$(r))return;let a="number"==typeof r.x?r.x:void 0,n="number"==typeof r.y?r.y:void 0;if(void 0!==a&&void 0!==n)return{x:a,y:n}}(e,"center");t&&a&&n&&o&&r.push({ref:t,label:eP(e,"label"),rect:a,overlayRect:n,center:o})}return r}(r),identifiers:{session:t}}},diff:async e=>await l("diff",[e.kind],{...e,interactiveOnly:e.interactiveOnly,compact:e.compact,depth:e.depth,scope:e.scope,raw:e.raw})},interactions:{click:async e=>await l("click",eH(e),{...e,clickButton:e.button}),press:async e=>await l("press",eH(e),e),longPress:async e=>await l("longpress",[String(e.x),String(e.y),...eJ(e.durationMs)],e),swipe:async e=>await l("swipe",[String(e.from.x),String(e.from.y),String(e.to.x),String(e.to.y),...eJ(e.durationMs)],e),focus:async e=>await l("focus",[String(e.x),String(e.y)],e),type:async e=>await l("type",[e.text],e),fill:async e=>await l("fill",[...eH(e),e.text],e),scroll:async e=>await l("scroll",[e.direction,...eJ(e.amount)],e),scrollIntoView:async e=>{var t;return await l("scrollintoview",void 0!==(t=e).ref?[t.ref,...eK(t.label)]:[t.text??""],e)},pinch:async e=>await l("pinch",[String(e.scale),...eJ(e.x),...eJ(e.y)],e),get:async e=>{var t;return await l("get",[e.format,...void 0!==(t=e).ref?[t.ref,...eK(t.label)]:[t.selector]],e)},is:async e=>await l("is",[e.predicate,e.selector,..."text"===e.predicate?[e.value]:[]],e),find:async e=>await l("find",function(e){let t=e.locator&&"any"!==e.locator?[e.locator,e.query]:[e.query];switch(e.action){case void 0:case"click":case"focus":case"exists":return e.action?[...t,e.action]:t;case"getText":return[...t,"get","text"];case"getAttrs":return[...t,"get","attrs"];case"wait":return[...t,"wait",...eJ(e.timeoutMs)];case"fill":case"type":return[...t,e.action,e.value]}}(e),{...e,findFirst:e.first,findLast:e.last})},replay:{run:async e=>await l("replay",[e.path],{...e,replayUpdate:e.update}),test:async e=>await l("test",e.paths,{...e,replayUpdate:e.update})},batch:{run:async e=>await l("batch",[],{...e,batchSteps:e.steps,batchOnError:e.onError,batchMaxSteps:e.maxSteps})},observability:{perf:async(e={})=>await l("perf",[],e),logs:async(e={})=>{var t;return await l("logs",[(t=e).action??"path",...eK(t.message)],e)},network:async(e={})=>{var t;return await l("network",[...(t=e).action?[t.action]:[],...eJ(t.limit)],{...e,networkInclude:e.include})}},recording:{record:async e=>await l("record",[e.action,...eK(e.path)],e),trace:async e=>await l("trace",[e.action,...eK(e.path)],e)},settings:{update:async e=>await l("settings",[e.setting,e.state,..."permission"in e?[e.permission]:[],..."mode"in e&&e.mode?[e.mode]:[]],e)}}}function eH(e){return void 0!==e.ref?[e.ref,...eK(e.label)]:void 0!==e.selector?[e.selector]:[String(e.x),String(e.y)]}function eK(e){return void 0===e?[]:[e]}function eJ(e){return void 0===e?[]:[String(e)]}function eW(e,t,r){return r.remoteConfig&&r.remoteConfig!==e.remoteConfig?{...eQ({...e,...r}),...e,...r}:{...t,...e,...r}}function eQ(e){var t;if(!e.remoteConfig)return{};let{runtime:r,...a}=(t={remoteConfig:e.remoteConfig,cwd:e.cwd??process.cwd(),env:process.env}).remoteConfig?{...function(e){let t={};for(let r of eD){let a=e[r];void 0!==a&&(t[r]=a)}return t}(A({configPath:t.remoteConfig,cwd:t.cwd,env:t.env}).profile),remoteConfig:t.remoteConfig}:{};return a}export{AppError}from"./957.js";export{ez as createAgentDeviceClient};
1
+ import e from"node:net";import t from"node:http";import r from"node:https";import a from"node:fs";import n from"node:path";import{fileURLToPath as o}from"node:url";import{AsyncLocalStorage as i}from"node:async_hooks";import s,{createHash as l}from"node:crypto";import"node:os";import{spawn as c}from"node:child_process";import{redactDiagnosticData as d,AppError as u}from"./152.js";import{resolveUserPath as p,expandUserHomePath as m}from"./267.js";import{stopProcessForTakeover as f,prepareMetroRuntime as h,runCmdSync as w,isAgentDeviceDaemonProcess as y,runCmdDetached as v}from"./974.js";import{REMOTE_CONFIG_FIELD_SPECS as g,resolveRemoteConfigProfile as I,REMOTE_OPEN_PROFILE_KEYS as A}from"./924.js";function b(){let e=n.dirname(o(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=n.join(t,"package.json");if(a.existsSync(e))return t;t=n.dirname(t)}return e}let S=new i;function _(){return s.randomBytes(8).toString("hex")}function D(e){let t=S.getStore();if(!t)return;let r={ts:new Date().toISOString(),level:e.level??"info",phase:e.phase,session:t.session,requestId:t.requestId,command:t.command,durationMs:e.durationMs,data:e.data?d(e.data):void 0};if(t.events.push(r),!t.debug)return;let n=`[agent-device][diag] ${JSON.stringify(r)}
2
+ `;try{t.logPath&&a.appendFile(t.logPath,n,()=>{}),t.traceLogPath&&a.appendFile(t.traceLogPath,n,()=>{}),t.logPath||t.traceLogPath||process.stderr.write(n)}catch{}}async function M(e,t,r){let a=Date.now();try{let n=await t();return D({level:"info",phase:e,durationMs:Date.now()-a,data:r}),n}catch(t){throw D({level:"error",phase:e,durationMs:Date.now()-a,data:{...r??{},error:t instanceof Error?t.message:String(t)}}),t}}function P(e){let t,r=(t=(e??"").trim())?p(t):n.join(m("~"),".agent-device");return{baseDir:r,infoPath:n.join(r,"daemon.json"),lockPath:n.join(r,"daemon.lock"),logPath:n.join(r,"daemon.log"),sessionsDir:n.join(r,"sessions")}}let E="sha256";async function k(e){let{localPath:o,baseUrl:i,token:s}=e,l=a.statSync(o),d=l.isDirectory(),p=n.basename(o),m=d?"app-bundle":"file",f=i.endsWith("/")?i:`${i}/`,h=d?void 0:await T(o);if(h){let e=await N({normalizedBase:f,token:s,hash:h,filename:p,sizeBytes:l.size,artifactType:m});if(e)return e}let w=new URL("upload",f),y="https:"===w.protocol?r:t,v={"x-artifact-type":m,"x-artifact-filename":p,"transfer-encoding":"chunked"};return h&&(v["x-artifact-hash"]=h,v["x-artifact-hash-algorithm"]=E),s&&(v.authorization=`Bearer ${s}`,v["x-agent-device-token"]=s),new Promise((e,t)=>{let r=y.request({protocol:w.protocol,host:w.hostname,port:w.port,method:"POST",path:w.pathname+w.search,headers:v},r=>{let a="";r.setEncoding("utf8"),r.on("data",e=>{a+=e}),r.on("end",()=>{clearTimeout(i);try{let r=JSON.parse(a);if(!r.ok||!r.uploadId)return void t(new u("COMMAND_FAILED",`Upload failed: ${a}`));e(r.uploadId)}catch{t(new u("COMMAND_FAILED",`Invalid upload response: ${a}`))}})}),i=setTimeout(()=>{r.destroy(),t(new u("COMMAND_FAILED","Artifact upload timed out",{timeoutMs:3e5,hint:"The upload to the remote daemon exceeded the 5-minute timeout."}))},3e5);if(r.on("error",e=>{clearTimeout(i),t(new u("COMMAND_FAILED","Failed to upload artifact to remote daemon",{hint:"Verify the remote daemon is reachable and supports artifact uploads."},e))}),d){let e=c("tar",["cf","-","-C",n.dirname(o),n.basename(o)],{stdio:["ignore","pipe","pipe"]});e.stdout.pipe(r),e.on("error",e=>{r.destroy(),t(new u("COMMAND_FAILED","Failed to create tar archive for app bundle",{},e))}),e.on("close",e=>{0!==e&&(r.destroy(),t(new u("COMMAND_FAILED",`tar failed with exit code ${e}`)))})}else{let e=a.createReadStream(o);e.pipe(r),e.on("error",e=>{r.destroy(),t(new u("COMMAND_FAILED","Failed to read local artifact",{},e))})}})}async function N(e){var t;let r=new URL("upload/preflight",e.normalizedBase),a={"content-type":"application/json"};e.token&&(a.authorization=`Bearer ${e.token}`,a["x-agent-device-token"]=e.token);let n=await fetch(r,{method:"POST",headers:a,signal:AbortSignal.timeout(3e4),body:JSON.stringify({hash:e.hash,hashAlgorithm:E,fileName:e.filename,sizeBytes:e.sizeBytes,artifactType:e.artifactType})}).catch(()=>void 0);if(!n?.ok)return;let o=await n.json().catch(()=>void 0);return(t=o)&&"object"==typeof t&&!0===t.ok&&!0===t.cacheHit&&"string"==typeof t.uploadId?o.uploadId:void 0}async function T(e){let t=l(E);return await new Promise((r,n)=>{a.createReadStream(e).on("data",e=>t.update(e)).on("error",e=>{n(new u("COMMAND_FAILED","Failed to read local artifact",{},e))}).on("end",r)}),t.digest("hex")}let x=/(?:^|[^\w$.])(?:import|export)\s+(?:type\s+)?(?:[^'"`]*?\s+from\s+)?['"]([^'"]+)['"]/gm,R=/import\(\s*['"]([^'"]+)['"]\s*\)/gm,C=[".ts",".tsx",".js",".jsx",".mjs",".cjs"];function L(e,t,r){t.lastIndex=0;let a=null;for(;null!==(a=t.exec(e));){let e=a[1]?.trim();e?.startsWith(".")&&r.add(e)}}function U(e){try{return a.statSync(e).isFile()?e:null}catch{return null}}let O=ey(),F=function(e=process.env.AGENT_DEVICE_DAEMON_STARTUP_TIMEOUT_MS){if(!e)return 15e3;let t=Number(e);return Number.isFinite(t)?Math.max(1e3,Math.floor(t)):15e3}(),$=function(e=process.env.AGENT_DEVICE_DAEMON_STARTUP_ATTEMPTS){if(!e)return 2;let t=Number(e);return Number.isFinite(t)?Math.min(5,Math.max(1,Math.floor(t))):2}(),j=["xcodebuild .*AgentDeviceRunnerUITests/RunnerTests/testCommand","xcodebuild .*AgentDeviceRunner\\.env\\.session-","xcodebuild build-for-testing .*ios-runner/AgentDeviceRunner/AgentDeviceRunner\\.xcodeproj"],q=new e.BlockList;async function B(t){let r=t.meta?.requestId??_(),a=!!(t.meta?.debug||t.flags?.verbose),n=function(t){let r,a,n,o=t.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR,i=function(e){let t;if(e){try{t=new URL(e)}catch(t){throw new u("INVALID_ARGS","Invalid daemon base URL",{daemonBaseUrl:e},t instanceof Error?t:void 0)}if("http:"!==t.protocol&&"https:"!==t.protocol)throw new u("INVALID_ARGS","Daemon base URL must use http or https",{daemonBaseUrl:e});return t.toString().replace(/\/+$/,"")}}(t.flags?.daemonBaseUrl??process.env.AGENT_DEVICE_DAEMON_BASE_URL),s=t.flags?.daemonAuthToken??process.env.AGENT_DEVICE_DAEMON_AUTH_TOKEN;var l=i,c=s;if(!(!l||"localhost"===(r=new URL(l).hostname.trim().toLowerCase().replace(/^\[(.*)\]$/,"$1"))||(e.isIPv4(r)?q.check(r,"ipv4"):!!e.isIPv6(r)&&q.check(r,"ipv6")))&&("string"!=typeof c||!(c.trim().length>0)))throw new u("INVALID_ARGS","Remote daemon base URL for non-loopback hosts requires daemon authentication",{daemonBaseUrl:l,hint:"Provide --daemon-auth-token or AGENT_DEVICE_DAEMON_AUTH_TOKEN when using a non-loopback remote daemon URL."});let d=t.flags?.daemonTransport??process.env.AGENT_DEVICE_DAEMON_TRANSPORT,p="auto"===(a=(d??"").trim().toLowerCase())?"auto":"socket"===a?"socket":"http"===a?"http":"auto";if(i&&"socket"===p)throw new u("INVALID_ARGS","Remote daemon base URL only supports HTTP transport. Remove --daemon-transport socket.",{daemonBaseUrl:i});let m="http"===(n=(t.flags?.daemonServerMode??process.env.AGENT_DEVICE_DAEMON_SERVER_MODE??("dual"===d?"dual":void 0)??"").trim().toLowerCase())?"http":"dual"===n?"dual":"socket";return{paths:P(o),transportPreference:p,serverMode:m,remoteBaseUrl:i,remoteAuthToken:s}}(t),o=function(e,t=process.env.AGENT_DEVICE_DAEMON_TIMEOUT_MS){if("test"!==e)return ey(t)}(t.command),i=await M("daemon_startup",async()=>await H(n),{requestId:r,session:t.session}),s=await G(t,i),l={...t,positionals:s.positionals,flags:s.flags,token:i.token,meta:{...t.meta??{},requestId:r,debug:a,cwd:t.meta?.cwd,tenantId:t.meta?.tenantId??t.flags?.tenant,runId:t.meta?.runId??t.flags?.runId,leaseId:t.meta?.leaseId??t.flags?.leaseId,sessionIsolation:t.meta?.sessionIsolation??t.flags?.sessionIsolation,lockPolicy:t.meta?.lockPolicy,lockPlatform:t.meta?.lockPlatform,...s.uploadedArtifactId?{uploadedArtifactId:s.uploadedArtifactId}:{},...s.clientArtifactPaths?{clientArtifactPaths:s.clientArtifactPaths}:{},...s.installSource?{installSource:s.installSource}:{}}};return D({level:"info",phase:"daemon_request_prepare",data:{requestId:r,command:t.command,session:t.session}}),await M("daemon_request",async()=>await ei(i,l,n.transportPreference,o),{requestId:r,command:t.command})}async function G(e,t){let r,o=[...e.positionals??[]],i=e.flags?{...e.flags}:void 0,s=e.meta?.installSource,l={};if(em(t)){let a=function(e,t){if("screenshot"===e.command){let r=z(e,"path",".png");return t[0]?{field:"path",localPath:r,positionalIndex:0,positionalPath:K("screenshot",".png")}:{field:"path",localPath:r,positionalIndex:0,flagPath:K("screenshot",".png")}}if("record"===e.command&&"start"===(t[0]??"").toLowerCase()){let t=z(e,"outPath",".mp4",1);return{field:"outPath",localPath:t,positionalIndex:1,positionalPath:K("recording",n.extname(t)||".mp4")}}return null}(e,o);a&&(void 0!==a.positionalPath&&(o[a.positionalIndex]=a.positionalPath),void 0!==a.flagPath&&((i??={}).out=a.flagPath),l[a.field]=a.localPath);let c=await V(e,t);c&&(s=c.installSource,r=c.uploadedArtifactId??r)}if(!em(t)||"install"!==e.command&&"reinstall"!==e.command||o.length<2)return{positionals:o,flags:i,installSource:s,uploadedArtifactId:r,...Object.keys(l).length>0?{clientArtifactPaths:l}:{}};let c=o[1];if(c.startsWith("remote:"))return o[1]=c.slice(7),{positionals:o,flags:i,...Object.keys(l).length>0?{clientArtifactPaths:l}:{}};let d=n.isAbsolute(c)?c:n.resolve(e.meta?.cwd??process.cwd(),c);return a.existsSync(d)?{positionals:o,flags:i,installSource:s,uploadedArtifactId:r=await k({localPath:d,baseUrl:t.baseUrl,token:t.token}),...Object.keys(l).length>0?{clientArtifactPaths:l}:{}}:{positionals:o,flags:i,...Object.keys(l).length>0?{clientArtifactPaths:l}:{}}}async function V(e,t){let r=e.meta?.installSource;if("install_source"!==e.command||!r||"path"!==r.kind)return null;let o=r.path.trim();if(!o)return{installSource:r};if(o.startsWith("remote:"))return{installSource:{...r,path:o.slice(7)}};let i=n.isAbsolute(o)?o:n.resolve(e.meta?.cwd??process.cwd(),o);if(!a.existsSync(i))return{installSource:{...r,path:i}};let s=await k({localPath:i,baseUrl:t.baseUrl,token:t.token});return{installSource:{...r,path:i},uploadedArtifactId:s}}function z(e,t,r,a=0){let o=e.positionals?.[a]??e.flags?.out,i=`${"path"===t?"screenshot":"recording"}-${Date.now()}${r}`,s=o&&o.trim().length>0?o:i;return n.isAbsolute(s)?s:n.resolve(e.meta?.cwd??process.cwd(),s)}function K(e,t){let r=t.startsWith(".")?t:`.${t}`;return n.posix.join("/tmp",`agent-device-${e}-${Date.now()}-${Math.random().toString(36).slice(2,8)}${r}`)}async function H(e){let t;if(e.remoteBaseUrl){let t={transport:"http",token:e.remoteAuthToken??"",pid:0,baseUrl:e.remoteBaseUrl};if(await ea(t,"http"))return t;throw new u("COMMAND_FAILED","Remote daemon is unavailable",{daemonBaseUrl:e.remoteBaseUrl,hint:"Verify AGENT_DEVICE_DAEMON_BASE_URL points to a reachable daemon with GET /health and POST /rpc."})}let r=Y(e.paths.infoPath),o=function(){try{let e=b();return JSON.parse(a.readFileSync(n.join(e,"package.json"),"utf8")).version??"0.0.0"}catch{return"0.0.0"}}(),i=function(e,t=b()){try{let r=n.resolve(t),o=[n.resolve(e)],i=new Set,l=[];for(;o.length>0;){let e=o.pop();if(!e||i.has(e))continue;i.add(e);let t=a.statSync(e);if(!t.isFile())continue;let s=n.relative(r,e)||e;l.push(`${s}:${t.size}:${Math.trunc(t.mtimeMs)}`);let c=a.readFileSync(e,"utf8");for(let t of function(e){let t=new Set;return L(e,x,t),L(e,R,t),[...t]}(c)){let r=function(e,t){let r=n.resolve(n.dirname(e),t),a=U(r);if(a)return a;for(let e of C){let t=U(`${r}${e}`);if(t)return t}for(let e of C){let t=U(n.join(r,`index${e}`));if(t)return t}return null}(e,t);r&&o.push(r)}}let c=l.sort().join("|"),d=s.createHash("sha1").update(c).digest("hex");return`graph:${l.length}:${d}`}catch{return"unknown"}}((t=eo()).useSrc?t.srcPath:t.distPath,t.root),l=!!r&&await ea(r,e.transportPreference);if(r&&r.version===o&&r.codeSignature===i&&l)return r;r&&(r.version!==o||r.codeSignature!==i||!l)&&(await X(r),er(e.paths.infoPath)),function(e){let t=ee(e);if(!t.hasLock||t.hasInfo)return;let r=Z(e.lockPath);if(!r)return er(e.lockPath);y(r.pid,r.processStartTime)||er(e.lockPath)}(e.paths);let c=0;for(let t=1;t<=$;t+=1){await en(e);let r=await J(F,e);if(r)return r;if(await Q(e.paths)){c+=1;continue}let a=ee(e.paths);if(!(t<$))break;if(!a.hasInfo&&!a.hasLock){await W(150);continue}}let d=ee(e.paths);throw new u("COMMAND_FAILED","Failed to start daemon",{kind:"daemon_startup_failed",infoPath:e.paths.infoPath,lockPath:e.paths.lockPath,startupTimeoutMs:F,startupAttempts:$,lockRecoveryCount:c,metadataState:d,hint:function(e,t=P(process.env.AGENT_DEVICE_STATE_DIR)){return e.hasLock&&!e.hasInfo?`Detected ${t.lockPath} without ${t.infoPath}. If no agent-device daemon process is running, delete ${t.lockPath} and retry.`:e.hasLock&&e.hasInfo?`Daemon metadata may be stale. If no agent-device daemon process is running, delete ${t.infoPath} and ${t.lockPath}, then retry.`:`Daemon metadata is missing or stale. Delete ${t.infoPath} if present and retry.`}(d,e.paths)})}async function J(e,t){let r=Date.now();for(;Date.now()-r<e;){let e=Y(t.paths.infoPath);if(e&&await ea(e,t.transportPreference))return e;await new Promise(e=>setTimeout(e,100))}return null}async function W(e){await new Promise(t=>setTimeout(t,e))}async function Q(e){let t=ee(e);if(!t.hasLock||t.hasInfo)return!1;let r=Z(e.lockPath);return r&&y(r.pid,r.processStartTime)&&await f(r.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:r.processStartTime}),er(e.lockPath),!0}async function X(e){await f(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}function Y(e){let t=et(e);if(!t||"object"!=typeof t)return null;let r="string"==typeof t.token&&t.token.length>0?t.token:null;if(!r)return null;let a=Number.isInteger(t.port)&&Number(t.port)>0,n=Number.isInteger(t.httpPort)&&Number(t.httpPort)>0;if(!a&&!n)return null;let o=t.transport,i="string"==typeof t.version?t.version:void 0,s="string"==typeof t.codeSignature?t.codeSignature:void 0,l="string"==typeof t.processStartTime?t.processStartTime:void 0,c=Number.isInteger(t.pid)&&Number(t.pid)>0;return{token:r,port:a?Number(t.port):void 0,httpPort:n?Number(t.httpPort):void 0,transport:"socket"===o||"http"===o||"dual"===o?o:void 0,pid:c?Number(t.pid):0,version:i,codeSignature:s,processStartTime:l}}function Z(e){let t=et(e);return t&&"object"==typeof t&&Number.isInteger(t.pid)&&Number(t.pid)>0?{pid:Number(t.pid),processStartTime:"string"==typeof t.processStartTime?t.processStartTime:void 0,startedAt:"number"==typeof t.startedAt?t.startedAt:void 0}:null}q.addSubnet("127.0.0.0",8,"ipv4"),q.addAddress("::1","ipv6"),q.addSubnet("::ffff:127.0.0.0",104,"ipv6");function ee(e){return{hasInfo:a.existsSync(e.infoPath),hasLock:a.existsSync(e.lockPath)}}function et(e){if(!a.existsSync(e))return null;try{return JSON.parse(a.readFileSync(e,"utf8"))}catch{return null}}function er(e){try{a.existsSync(e)&&a.unlinkSync(e)}catch{}}async function ea(a,n){var o;return"http"===es(a,n)?await function(e){let a=e.baseUrl?ef(e.baseUrl,"health"):e.httpPort?`http://127.0.0.1:${e.httpPort}/health`:null;if(!a)return Promise.resolve(!1);let n=new URL(a),o="https:"===n.protocol?r:t,i=e.baseUrl?3e3:500;return new Promise(e=>{let t=o.request({protocol:n.protocol,host:n.hostname,port:n.port,path:n.pathname+n.search,method:"GET",timeout:i},t=>{t.resume(),e((t.statusCode??500)<500)});t.on("timeout",()=>{t.destroy(),e(!1)}),t.on("error",()=>{e(!1)}),t.end()})}(a):await ((o=a.port)?new Promise(t=>{let r=e.createConnection({host:"127.0.0.1",port:o},()=>{r.destroy(),t(!0)});r.on("error",()=>{t(!1)})}):Promise.resolve(!1))}async function en(e){let t=eo(),r=t.useSrc?["--experimental-strip-types",t.srcPath]:[t.distPath],a={...process.env,AGENT_DEVICE_STATE_DIR:e.paths.baseDir,AGENT_DEVICE_DAEMON_SERVER_MODE:e.serverMode};v(process.execPath,r,{env:a})}function eo(){let e=b(),t=n.join(e,"dist","src","daemon.js"),r=n.join(e,"src","daemon.ts"),o=a.existsSync(t),i=a.existsSync(r);if(!o&&!i)throw new u("COMMAND_FAILED","Daemon entry not found",{distPath:t,srcPath:r});return{root:e,distPath:t,srcPath:r,useSrc:process.execArgv.includes("--experimental-strip-types")?i:!o&&i}}async function ei(e,t,r,a){return"http"===es(e,r)?await ep(e,t,a):await eu(e,t,a)}function es(e,t){if(e.baseUrl){if("socket"===t)throw new u("COMMAND_FAILED","Remote daemon endpoint only supports HTTP transport",{daemonBaseUrl:e.baseUrl});return"http"}if("http"===t||"socket"===t){var r=e,a=t;if(el(r,a))return a;throw new u("COMMAND_FAILED","http"===a?"Daemon HTTP endpoint is unavailable":"Daemon socket endpoint is unavailable")}let n=("socket"===e.transport||"dual"===e.transport?["socket","http"]:["http","socket"]).find(t=>el(e,t));if(n)return n;throw new u("COMMAND_FAILED","Daemon metadata has no reachable transport")}function el(e,t){return"http"===t?!!e.httpPort:!!e.port}function ec(e,t,r,a,n,o){let i=n?{terminated:0}:function(){let e=0;try{for(let t of j){let r=w("pkill",["-f",t],{allowFailure:!0});0===r.exitCode&&(e+=1)}return{terminated:e}}catch(t){return{terminated:e,error:t instanceof Error?t.message:String(t)}}}(),s=n?{forcedKill:!1}:function(e,t){let r=!1;try{y(e.pid,e.processStartTime)&&(process.kill(e.pid,"SIGKILL"),r=!0)}catch{f(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}finally{er(t.infoPath),er(t.lockPath)}return{forcedKill:r}}(e,t);return D({level:"error",phase:"daemon_request_timeout",data:{timeoutMs:o,requestId:r,command:a,timedOutRunnerPidsTerminated:i.terminated,timedOutRunnerCleanupError:i.error,daemonPidReset:n?void 0:e.pid,daemonPidForceKilled:n?void 0:s.forcedKill,daemonBaseUrl:e.baseUrl}}),new u("COMMAND_FAILED","Daemon request timed out",{timeoutMs:o,requestId:r,hint:n?"Retry with --debug and verify the remote daemon URL, auth token, and remote host logs.":"Retry with --debug and check daemon diagnostics logs. Timed-out iOS runner xcodebuild processes were terminated when detected."})}function ed(e,t,r){return D({level:"error",phase:"daemon_request_socket_error",data:{requestId:t,message:e instanceof Error?e.message:String(e)}}),new u("COMMAND_FAILED","Failed to communicate with daemon",{requestId:t,hint:r?"Retry command. If this persists, verify the remote daemon URL, auth token, and remote host reachability.":"Retry command. If this persists, clean stale daemon metadata and start a fresh session."},e instanceof Error?e:void 0)}async function eu(t,r,a){let n=t.port;if(!n)throw new u("COMMAND_FAILED","Daemon socket endpoint is unavailable");return new Promise((o,i)=>{let s=e.createConnection({host:"127.0.0.1",port:n},()=>{s.write(`${JSON.stringify(r)}
3
+ `)}),l=P(r.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),c="number"==typeof a?setTimeout(()=>{s.destroy(),i(ec(t,l,r.meta?.requestId,r.command,!1,a))},a):void 0,d="";s.setEncoding("utf8"),s.on("data",e=>{let t=(d+=e).indexOf("\n");if(-1===t)return;let a=d.slice(0,t).trim();if(a)try{let e=JSON.parse(a);s.end(),c&&clearTimeout(c),o(e)}catch(e){c&&clearTimeout(c),i(new u("COMMAND_FAILED","Invalid daemon response",{requestId:r.meta?.requestId,line:a},e instanceof Error?e:void 0))}}),s.on("error",e=>{c&&clearTimeout(c),i(ed(e,r.meta?.requestId,!1))})})}async function ep(e,a,n){let o=e.baseUrl?new URL(ef(e.baseUrl,"rpc")):e.httpPort?new URL(`http://127.0.0.1:${e.httpPort}/rpc`):null;if(!o)throw new u("COMMAND_FAILED","Daemon HTTP endpoint is unavailable");let i=JSON.stringify({jsonrpc:"2.0",id:a.meta?.requestId??_(),method:"agent_device.command",params:a}),s={"content-type":"application/json","content-length":Buffer.byteLength(i)};return e.baseUrl&&e.token&&(s.authorization=`Bearer ${e.token}`,s["x-agent-device-token"]=e.token),await new Promise((l,c)=>{let d=P(a.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),p=("https:"===o.protocol?r:t).request({protocol:o.protocol,host:o.hostname,port:o.port,method:"POST",path:o.pathname+o.search,headers:s},t=>{let r="";t.setEncoding("utf8"),t.on("data",e=>{r+=e}),t.on("end",()=>{f&&clearTimeout(f);try{let t=JSON.parse(r);if(t.error){let e=t.error.data??{};c(new u(String(e.code??"COMMAND_FAILED"),String(e.message??t.error.message??"Daemon RPC request failed"),{..."object"==typeof e.details&&e.details?e.details:{},hint:"string"==typeof e.hint?e.hint:void 0,diagnosticId:"string"==typeof e.diagnosticId?e.diagnosticId:void 0,logPath:"string"==typeof e.logPath?e.logPath:void 0,requestId:a.meta?.requestId}));return}if(!t.result||"object"!=typeof t.result)return void c(new u("COMMAND_FAILED","Invalid daemon RPC response",{requestId:a.meta?.requestId}));if(e.baseUrl&&t.result.ok)return void eh(e,a,t.result).then(l).catch(c);l(t.result)}catch(e){f&&clearTimeout(f),c(new u("COMMAND_FAILED","Invalid daemon response",{requestId:a.meta?.requestId,line:r},e instanceof Error?e:void 0))}})}),m=em(e),f="number"==typeof n?setTimeout(()=>{p.destroy(),c(ec(e,d,a.meta?.requestId,a.command,m,n))},n):void 0;p.on("error",e=>{f&&clearTimeout(f),c(ed(e,a.meta?.requestId,m))}),p.write(i),p.end()})}function em(e){return"string"==typeof e.baseUrl&&e.baseUrl.length>0}function ef(e,t){return new URL(t,e.endsWith("/")?e:`${e}/`).toString()}async function eh(e,t,r){let a=Array.isArray(r.data?.artifacts)?r.data.artifacts:[];if(0===a.length||!e.baseUrl)return r;let o=r.data?{...r.data}:{},i=[];for(let r of a){if(!r||"object"!=typeof r||"string"!=typeof r.artifactId){i.push(r);continue}let a=function(e,t){if(e.localPath&&e.localPath.trim().length>0)return e.localPath;let r=e.fileName?.trim()||`${e.field}-${Date.now()}`;return n.resolve(t.meta?.cwd??process.cwd(),r)}(r,t);await ew({baseUrl:e.baseUrl,token:e.token,artifactId:r.artifactId,destinationPath:a,requestId:t.meta?.requestId}),o[r.field]=a,i.push({...r,localPath:a})}return o.artifacts=i,{ok:!0,data:o}}async function ew(e){var o,i;let s,l=new URL((o=e.baseUrl,i=e.artifactId,s=o.endsWith("/")?o:`${o}/`,new URL(`upload/${encodeURIComponent(i)}`,s).toString())),c="https:"===l.protocol?r:t;await a.promises.mkdir(n.dirname(e.destinationPath),{recursive:!0}),await new Promise((t,r)=>{let n=!1,o=e.timeoutMs??O,i=o=>{if(!n){if(n=!0,clearTimeout(d),o)return void a.promises.rm(e.destinationPath,{force:!0}).finally(()=>r(o));t()}},s=c.request({protocol:l.protocol,host:l.hostname,port:l.port,method:"GET",path:l.pathname+l.search,headers:e.token?{authorization:`Bearer ${e.token}`,"x-agent-device-token":e.token}:void 0},t=>{if((t.statusCode??500)>=400){let r="";t.setEncoding("utf8"),t.on("data",e=>{r+=e}),t.on("end",()=>{i(new u("COMMAND_FAILED","Failed to download remote artifact",{artifactId:e.artifactId,statusCode:t.statusCode,requestId:e.requestId,body:r}))});return}let r=a.createWriteStream(e.destinationPath);r.on("error",e=>{i(e instanceof Error?e:Error(String(e)))}),t.on("error",e=>{i(e instanceof Error?e:Error(String(e)))}),t.on("aborted",()=>{i(new u("COMMAND_FAILED","Remote artifact download was interrupted",{artifactId:e.artifactId,requestId:e.requestId}))}),r.on("finish",()=>{r.close(()=>i())}),t.pipe(r)}),d=setTimeout(()=>{s.destroy(new u("COMMAND_FAILED","Remote artifact download timed out",{artifactId:e.artifactId,requestId:e.requestId,timeoutMs:o}))},o);s.on("error",t=>{t instanceof u?i(t):i(new u("COMMAND_FAILED","Failed to download remote artifact",{artifactId:e.artifactId,requestId:e.requestId,timeoutMs:o},t instanceof Error?t:void 0))}),s.end()})}function ey(e=process.env.AGENT_DEVICE_DAEMON_TIMEOUT_MS){if(!e)return 9e4;let t=Number(e);return Number.isFinite(t)?Math.max(1e3,Math.floor(t)):9e4}let ev="clipboard",eg="wait",eI=new Set(["id","role","text","label","value","appname","windowtitle"]),eA=new Set(["visible","hidden","editable","selected","enabled","hittable"]),eb=new Set([...eI,...eA]);function eS(e){let t=e.trim();if(!t)throw new u("INVALID_ARGS","Empty selector term");let r=t.indexOf("=");if(-1===r){let r=t.toLowerCase();if(!eA.has(r))throw new u("INVALID_ARGS",`Invalid selector term "${e}", expected key=value`);return{key:r,value:!0}}let a=t.slice(0,r).trim().toLowerCase(),n=t.slice(r+1).trim();if(!eb.has(a))throw new u("INVALID_ARGS",`Unknown selector key: ${a}`);if(!n)throw new u("INVALID_ARGS",`Missing selector value for key: ${a}`);if(eA.has(a)){let e,t="true"===(e=eD(n).toLowerCase())||"false"!==e&&null;if(null===t)throw new u("INVALID_ARGS",`Invalid boolean value for ${a}: ${n}`);return{key:a,value:t}}return{key:a,value:eD(n)}}function e_(e,t){return e?e===t?null:e:t}function eD(e){let t=e.trim();return t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1).replace(/\\(["'])/g,"$1"):t}function eM(e,t){let r=0;for(let a=t-1;a>=0&&"\\"===e[a];a-=1)r+=1;return r%2==1}let eP=g.map(e=>e.key);function eE(e){let t=e.appId??e.bundleId??e.packageName;return{session:e.session,appId:t,appBundleId:e.bundleId,package:e.packageName}}function ek(e,t,r){return{deviceId:t,deviceName:r,..."android"===e?{serial:t}:"ios"===e?{udid:t}:{}}}function eN(e,t,r,a){let n=r(e[t]);if(void 0===n)throw new u("COMMAND_FAILED",a,{response:e});return n}[...A];function eT(e,t){return eN(e,t,eO,`Daemon response is missing "${t}".`)}function ex(e,t){return eO(e[t])}function eR(e,t){var r;let a;return r=eO,null===(a=e[t])?null:r(a)}function eC(e,t){return eN(e,t,e$,`Daemon response has invalid "${t}".`)}function eL(e,t){return function(e){return"tv"===e||"mobile"===e||"desktop"===e?e:void 0}(e[t])??"mobile"}function eU(e,t){let r=e[t];if(!eB(r))return;let a="number"==typeof r.x?r.x:void 0,n="number"==typeof r.y?r.y:void 0,o="number"==typeof r.width?r.width:void 0,i="number"==typeof r.height?r.height:void 0;if(void 0!==a&&void 0!==n&&void 0!==o&&void 0!==i)return{x:a,y:n,width:o,height:i}}function eO(e){return"string"==typeof e&&e.length>0?e:void 0}function eF(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function e$(e){return"ios"===e||"macos"===e||"android"===e?e:void 0}function ej(e){return"simulator"===e||"emulator"===e||"device"===e?e:void 0}function eq(e){if(!eB(e))throw new u("COMMAND_FAILED","Daemon returned an unexpected response shape.",{value:e});return e}function eB(e){return"object"==typeof e&&null!==e}function eG(e){let t={};for(let[r,a]of Object.entries(e))void 0!==a&&(t[r]=a);return t}function eV(e,t){let r=ex(e,"bundleId"),a=ex(e,"package");return{app:eT(e,"app"),appPath:eT(e,"appPath"),platform:eC(e,"platform"),appId:r??a,bundleId:r,package:a,identifiers:eE({session:t,bundleId:r,packageName:a})}}function ez(e){let t=eq(e),r=eC(t,"platform"),a=eT(t,"id"),n=eT(t,"name");return{platform:r,target:eL(t,"target"),kind:eN(t,"kind",ej,'Daemon response has invalid "kind".'),id:a,name:n,booted:"boolean"==typeof t.booted?t.booted:void 0,identifiers:ek(r,a,n),ios:"ios"===r?{udid:a}:void 0,android:"android"===r?{serial:a}:void 0}}function eK(e){let t=eq(e),r=eC(t,"platform"),a=eT(t,"id"),n=eT(t,"name"),o=eL(t,"target"),i=eT(t,"device"),s={session:n,...ek(r,a,i)};return{name:n,createdAt:eN(t,"createdAt",eF,'Daemon response is missing numeric "createdAt".'),device:{platform:r,target:o,id:a,name:i,identifiers:s,ios:"ios"===r?{udid:a,simulatorSetPath:eR(t,"ios_simulator_device_set")}:void 0,android:"android"===r?{serial:a}:void 0},identifiers:s}}function eH(e){return e??"default"}function eJ(e={},t={}){var r;let a,n=t.transport??B,o=eZ(e),i=async(t,r=[],a={})=>{let i=eY(e,o,a),s=await n({session:eH(i.session),command:t,positionals:r,flags:eG({stateDir:i.stateDir,daemonBaseUrl:i.daemonBaseUrl,daemonAuthToken:i.daemonAuthToken,daemonTransport:i.daemonTransport,daemonServerMode:i.daemonServerMode,tenant:i.tenant,sessionIsolation:i.sessionIsolation,runId:i.runId,leaseId:i.leaseId,platform:i.platform,target:i.target,device:i.device,udid:i.udid,serial:i.serial,iosSimulatorDeviceSet:i.iosSimulatorDeviceSet,androidDeviceAllowlist:i.androidDeviceAllowlist,runtime:i.simulatorRuntimeId,boot:i.boot,reuseExisting:i.reuseExisting,surface:i.surface,activity:i.activity,relaunch:i.relaunch,shutdown:i.shutdown,saveScript:i.saveScript,noRecord:i.noRecord,backMode:i.backMode,metroHost:i.metroHost,metroPort:i.metroPort,bundleUrl:i.bundleUrl,launchUrl:i.launchUrl,snapshotInteractiveOnly:i.interactiveOnly,snapshotCompact:i.compact,snapshotDepth:i.depth,snapshotScope:i.scope,snapshotRaw:i.raw,screenshotFullscreen:i.screenshotFullscreen,overlayRefs:i.overlayRefs,appsFilter:i.appsFilter,out:i.out,count:i.count,fps:i.fps,hideTouches:i.hideTouches,intervalMs:i.intervalMs,delayMs:i.delayMs,holdMs:i.holdMs,jitterPx:i.jitterPx,pixels:i.pixels,doubleTap:i.doubleTap,clickButton:i.clickButton,pauseMs:i.pauseMs,pattern:i.pattern,headless:i.headless,restart:i.restart,replayUpdate:i.replayUpdate,failFast:i.failFast,timeoutMs:i.timeoutMs,retries:i.retries,artifactsDir:i.artifactsDir,reportJunit:i.reportJunit,findFirst:i.findFirst,findLast:i.findLast,networkInclude:i.networkInclude,batchOnError:i.batchOnError,batchMaxSteps:i.batchMaxSteps,batchSteps:i.batchSteps,verbose:i.debug}),runtime:i.runtime,meta:eG({requestId:i.requestId,cwd:i.cwd,debug:i.debug,lockPolicy:i.lockPolicy,lockPlatform:i.lockPlatform,tenantId:i.tenant,runId:i.runId,leaseId:i.leaseId,sessionIsolation:i.sessionIsolation,installSource:i.installSource,retainMaterializedPaths:i.retainMaterializedPaths,materializedPathRetentionMs:i.materializedPathRetentionMs,materializationId:i.materializationId})});return s.ok||function(e){throw new u(e.code,e.message,{...e.details??{},hint:e.hint,diagnosticId:e.diagnosticId,logPath:e.logPath})}(s.error),s.data??{}},s=async(e={})=>{let t=await i("session_list",[],e);return(Array.isArray(t.sessions)?t.sessions:[]).map(eK)},l=async(e,t=[],r={})=>await i(e,t,r),c=(t={})=>eH(eY(e,o,t).session);return{command:(r=async e=>await i(e.command,e.positionals,e.options),a=async e=>await r(e),{wait:async e=>await a(function(e){if(1!==[void 0!==e.durationMs?"durationMs":void 0,void 0!==e.text?"text":void 0,void 0!==e.ref?"ref":void 0,void 0!==e.selector?"selector":void 0].filter(Boolean).length)throw new u("INVALID_ARGS","wait command requires exactly one of durationMs, text, ref, or selector.");if(void 0!==e.durationMs)return{command:eg,positionals:[String(e.durationMs)],options:e};let t=void 0!==e.timeoutMs?[String(e.timeoutMs)]:[];if(void 0!==e.text)return{command:eg,positionals:["text",e.text,...t],options:e};if(void 0!==e.ref)return{command:eg,positionals:[e.ref,...t],options:e};let r=e.selector;return function(e){if(!function(e){try{let t=e.trim();if(!t)throw new u("INVALID_ARGS","Selector expression cannot be empty");let r=function(e){let t=[],r="",a=null;for(let n=0;n<e.length;n+=1){let o=e[n];if(('"'===o||"'"===o)&&!eM(e,n)){a=e_(a,o),r+=o;continue}if(!a&&"|"===o&&"|"===e[n+1]){let a=r.trim();if(!a)throw new u("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);t.push(a),r="",n+=1;continue}r+=o}let n=r.trim();if(!n)throw new u("INVALID_ARGS",`Invalid selector fallback expression: ${e}`);return t.push(n),t}(t);if(0===r.length)throw new u("INVALID_ARGS","Selector expression cannot be empty");return{raw:t,selectors:r.map(e=>(function(e){let t=e.trim();if(!t)throw new u("INVALID_ARGS","Selector segment cannot be empty");let r=function(e){let t=[],r="",a=null;for(let n=0;n<e.length;n+=1){let o=e[n];if(('"'===o||"'"===o)&&!eM(e,n)){a=e_(a,o),r+=o;continue}if(!a&&/\s/.test(o)){r.trim()&&t.push(r.trim()),r="";continue}r+=o}if(a)throw new u("INVALID_ARGS",`Unclosed quote in selector: ${e}`);return r.trim()&&t.push(r.trim()),t}(t);if(0===r.length)throw new u("INVALID_ARGS",`Invalid selector segment: ${e}`);return{raw:t,terms:r.map(eS)}})(e))}}catch{return null}}(e))throw new u("INVALID_ARGS",`Invalid wait selector: ${e}`)}(r),{command:eg,positionals:[r,...t],options:e}}(e)),alert:async(e={})=>{var t;return await a({command:"alert",positionals:[(t=e).action??"get",...void 0!==t.timeoutMs?[String(t.timeoutMs)]:[]],options:t})},appState:async(e={})=>await a({command:"appstate",positionals:[],options:e}),back:async(e={})=>await a({command:"back",positionals:[],options:{...e,backMode:e.mode}}),home:async(e={})=>await a({command:"home",positionals:[],options:e}),rotate:async e=>await a({command:"rotate",positionals:[e.orientation],options:e}),appSwitcher:async(e={})=>await a({command:"app-switcher",positionals:[],options:e}),keyboard:async(e={})=>await a({command:"keyboard",positionals:e.action?[e.action]:[],options:e}),clipboard:async e=>{var t;return await a("read"===(t=e).action?{command:ev,positionals:["read"],options:t}:{command:ev,positionals:["write",t.text],options:t})}}),devices:{list:async(e={})=>{let t=await i("devices",[],e);return(Array.isArray(t.devices)?t.devices:[]).map(ez)},boot:async(e={})=>await l("boot",[],e)},sessions:{list:async(e={})=>await s(e),close:async(e={})=>{let t=c(e),r=(await i("close",[],e)).shutdown;return{session:t,shutdown:"object"==typeof r&&null!==r?r:void 0,identifiers:{session:t}}}},simulators:{ensure:async e=>{let{runtime:t,...r}=e,a=await i("ensure-simulator",[],{...r,simulatorRuntimeId:t}),n=eT(a,"udid"),o=eT(a,"device");return{udid:n,device:o,runtime:eT(a,"runtime"),created:!0===a.created,booted:!0===a.booted,iosSimulatorDeviceSet:eR(a,"ios_simulator_device_set"),identifiers:{deviceId:n,deviceName:o,udid:n}}}},apps:{install:async e=>eV(await i("install",[e.app,e.appPath],e),c(e)),reinstall:async e=>eV(await i("reinstall",[e.app,e.appPath],e),c(e)),installFromSource:async e=>(function(e,t){let r=ex(e,"bundleId"),a=ex(e,"packageName"),n=r??a??ex(e,"appId"),o=ex(e,"launchTarget")??a??r??n;if(!o)throw new u("COMMAND_FAILED",'Daemon response is missing "launchTarget".',{response:e});return{appName:ex(e,"appName"),appId:n,bundleId:r,packageName:a,launchTarget:o,installablePath:ex(e,"installablePath"),archivePath:ex(e,"archivePath"),materializationId:ex(e,"materializationId"),materializationExpiresAt:ex(e,"materializationExpiresAt"),identifiers:eE({session:t,bundleId:r,packageName:a,appId:n})}})(await i("install_source",[],{...e,installSource:e.source,retainMaterializedPaths:e.retainPaths,materializedPathRetentionMs:e.retentionMs}),c(e)),list:async(e={})=>{let t=await i("apps",[],e);return Array.isArray(t.apps)?t.apps.filter(e=>"string"==typeof e):[]},open:async e=>{let t=c(e),r=e.app?e.url?[e.app,e.url]:[e.app]:[],a=await i("open",r,e),n=function(e){let t=e.platform,r=ex(e,"id"),a=ex(e,"device");if("ios"!==t&&"macos"!==t&&"android"!==t||!r||!a)return;let n=eL(e,"target"),o=ek(t,r,a);return{platform:t,target:n,id:r,name:a,identifiers:o,ios:"ios"===t?{udid:ex(e,"device_udid")??r,simulatorSetPath:eR(e,"ios_simulator_device_set")}:void 0,android:"android"===t?{serial:ex(e,"serial")??r}:void 0}}(a),o=ex(a,"appBundleId");return{session:t,appName:ex(a,"appName"),appBundleId:o,appId:o,startup:function(e){if(eB(e)&&"number"==typeof e.durationMs&&"string"==typeof e.measuredAt&&"string"==typeof e.method)return{durationMs:e.durationMs,measuredAt:e.measuredAt,method:e.method,appTarget:ex(e,"appTarget"),appBundleId:ex(e,"appBundleId")}}(a.startup),runtime:function(e){if(!eB(e))return;let t=e.platform,r=ex(e,"metroHost"),a="number"==typeof e.metroPort?e.metroPort:void 0;return{platform:"ios"===t||"android"===t?t:void 0,metroHost:r,metroPort:a,bundleUrl:ex(e,"bundleUrl"),launchUrl:ex(e,"launchUrl")}}(a.runtime),device:n,identifiers:{session:t,deviceId:n?.id,deviceName:n?.name,udid:n?.ios?.udid,serial:n?.android?.serial,appId:o,appBundleId:o}}},close:async(e={})=>{let t=c(e),r=(await i("close",e.app?[e.app]:[],e)).shutdown;return{session:t,closedApp:e.app,shutdown:"object"==typeof r&&null!==r?r:void 0,identifiers:{session:t}}},push:async e=>{var t;return await l("push",[e.app,"string"==typeof(t=e.payload)?t:JSON.stringify(t)],e)},triggerEvent:async e=>{var t;return await l("trigger-app-event",[(t=e).event,...t.payload?[JSON.stringify(t.payload)]:[]],e)}},materializations:{release:async e=>{var t;return{released:!0===(t=await i("release_materialized_paths",[],{...e,materializationId:e.materializationId})).released,materializationId:eT(t,"materializationId"),identifiers:{}}}},metro:{prepare:async t=>await h({projectRoot:t.projectRoot??e.cwd,kind:t.kind,publicBaseUrl:t.publicBaseUrl,proxyBaseUrl:t.proxyBaseUrl,proxyBearerToken:t.bearerToken,launchUrl:t.launchUrl,companionProfileKey:t.companionProfileKey,companionConsumerKey:t.companionConsumerKey,metroPort:t.port,listenHost:t.listenHost,statusHost:t.statusHost,startupTimeoutMs:t.startupTimeoutMs,probeTimeoutMs:t.probeTimeoutMs,reuseExisting:t.reuseExisting,installDependenciesIfNeeded:t.installDependenciesIfNeeded,runtimeFilePath:t.runtimeFilePath,logPath:t.logPath})},capture:{snapshot:async(e={})=>{var t;let r=c(e),a=await i("snapshot",[],e),n=ex(a,"appBundleId"),o="object"==typeof a.visibility&&null!==a.visibility?a.visibility:void 0;return{nodes:Array.isArray(t=a.nodes)?t:[],truncated:!0===a.truncated,appName:ex(a,"appName"),appBundleId:n,...o?{visibility:o}:{},warnings:Array.isArray(a.warnings)?a.warnings.filter(e=>"string"==typeof e):void 0,identifiers:{session:r,appId:n,appBundleId:n}}},screenshot:async(e={})=>{let t=c(e),r=await i("screenshot",e.path?[e.path]:[],{...e,screenshotFullscreen:e.fullscreen});return{path:eT(r,"path"),overlayRefs:function(e){let t=e.overlayRefs;if(!Array.isArray(t))return;let r=[];for(let e of t){if(!eB(e))continue;let t=ex(e,"ref"),a=eU(e,"rect"),n=eU(e,"overlayRect"),o=function(e,t){let r=e[t];if(!eB(r))return;let a="number"==typeof r.x?r.x:void 0,n="number"==typeof r.y?r.y:void 0;if(void 0!==a&&void 0!==n)return{x:a,y:n}}(e,"center");t&&a&&n&&o&&r.push({ref:t,label:ex(e,"label"),rect:a,overlayRect:n,center:o})}return r}(r),identifiers:{session:t}}},diff:async e=>await l("diff",[e.kind],{...e,interactiveOnly:e.interactiveOnly,compact:e.compact,depth:e.depth,scope:e.scope,raw:e.raw})},interactions:{click:async e=>await l("click",eW(e),{...e,clickButton:e.button}),press:async e=>await l("press",eW(e),e),longPress:async e=>await l("longpress",[String(e.x),String(e.y),...eX(e.durationMs)],e),swipe:async e=>await l("swipe",[String(e.from.x),String(e.from.y),String(e.to.x),String(e.to.y),...eX(e.durationMs)],e),focus:async e=>await l("focus",[String(e.x),String(e.y)],e),type:async e=>await l("type",[e.text],e),fill:async e=>await l("fill",[...eW(e),e.text],e),scroll:async e=>await l("scroll",[e.direction,...eX(e.amount)],e),pinch:async e=>await l("pinch",[String(e.scale),...eX(e.x),...eX(e.y)],e),get:async e=>{var t;return await l("get",[e.format,...void 0!==(t=e).ref?[t.ref,...eQ(t.label)]:[t.selector]],e)},is:async e=>await l("is",[e.predicate,e.selector,..."text"===e.predicate?[e.value]:[]],e),find:async e=>await l("find",function(e){let t=e.locator&&"any"!==e.locator?[e.locator,e.query]:[e.query];switch(e.action){case void 0:case"click":case"focus":case"exists":return e.action?[...t,e.action]:t;case"getText":return[...t,"get","text"];case"getAttrs":return[...t,"get","attrs"];case"wait":return[...t,"wait",...eX(e.timeoutMs)];case"fill":case"type":return[...t,e.action,e.value]}}(e),{...e,findFirst:e.first,findLast:e.last})},replay:{run:async e=>await l("replay",[e.path],{...e,replayUpdate:e.update}),test:async e=>await l("test",e.paths,{...e,replayUpdate:e.update})},batch:{run:async e=>await l("batch",[],{...e,batchSteps:e.steps,batchOnError:e.onError,batchMaxSteps:e.maxSteps})},observability:{perf:async(e={})=>await l("perf",[],e),logs:async(e={})=>{var t;return await l("logs",[(t=e).action??"path",...eQ(t.message)],e)},network:async(e={})=>{var t;return await l("network",[...(t=e).action?[t.action]:[],...eX(t.limit)],{...e,networkInclude:e.include})}},recording:{record:async e=>await l("record",[e.action,...eQ(e.path)],e),trace:async e=>await l("trace",[e.action,...eQ(e.path)],e)},settings:{update:async e=>await l("settings",[e.setting,e.state,..."permission"in e?[e.permission]:[],..."mode"in e&&e.mode?[e.mode]:[]],e)}}}function eW(e){return void 0!==e.ref?[e.ref,...eQ(e.label)]:void 0!==e.selector?[e.selector]:[String(e.x),String(e.y)]}function eQ(e){return void 0===e?[]:[e]}function eX(e){return void 0===e?[]:[String(e)]}function eY(e,t,r){return r.remoteConfig&&r.remoteConfig!==e.remoteConfig?{...eZ({...e,...r}),...e,...r}:{...t,...e,...r}}function eZ(e){var t;if(!e.remoteConfig)return{};let{runtime:r,...a}=(t={remoteConfig:e.remoteConfig,cwd:e.cwd??process.cwd(),env:process.env}).remoteConfig?{...function(e){let t={};for(let r of eP){let a=e[r];void 0!==a&&(t[r]=a)}return t}(I({configPath:t.remoteConfig,cwd:t.cwd,env:t.env}).profile),remoteConfig:t.remoteConfig}:{};return a}export{AppError}from"./152.js";export{centerOfRect}from"./57.js";export{eJ as createAgentDeviceClient};
@@ -179,6 +179,12 @@ export declare type PrepareRemoteMetroResult = {
179
179
  logPath: string;
180
180
  };
181
181
 
182
+ export declare function resolveRuntimeTransport(runtime: SessionRuntimeHints | undefined): {
183
+ host: string;
184
+ port: number;
185
+ scheme: 'http' | 'https';
186
+ } | undefined;
187
+
182
188
  declare type SessionRuntimeHints = {
183
189
  platform?: 'ios' | 'android';
184
190
  metroHost?: string;
package/dist/src/metro.js CHANGED
@@ -1 +1 @@
1
- import{buildMetroRuntimeHints as e,ensureMetroCompanion as t,stopMetroCompanion as r,prepareMetroRuntime as n}from"./974.js";async function o(e){let t=await n({projectRoot:e.projectRoot,kind:e.kind,publicBaseUrl:e.publicBaseUrl,proxyBaseUrl:e.proxyBaseUrl,proxyBearerToken:e.proxyBearerToken,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 i(e){let r=await t(e);return{pid:r.pid,started:r.spawned,logPath:r.logPath}}async function s(e){await r(e)}function a(t){return e(t,"ios")}function u(t){return e(t,"android")}export{buildBundleUrl,normalizeBaseUrl}from"./974.js";export{u as buildAndroidRuntimeHints,a as buildIosRuntimeHints,i as ensureMetroTunnel,o as prepareRemoteMetro,s as stopMetroTunnel};
1
+ import{URL as t}from"node:url";import"node:fs";import"node:os";import e from"node:path";import{buildMetroRuntimeHints as o,ensureMetroCompanion as r,stopMetroCompanion as n,prepareMetroRuntime as i}from"./974.js";import{AppError as s}from"./152.js";function l(t){let e=t?.trim();return e&&e.length>0?e:void 0}function u(t){if(Number.isInteger(t)&&!(t<=0)&&!(t>65535))return t}function a(e){return function(e){if(!e)return;let o=l(e.metroHost),r=u(e.metroPort),n="http",i=l(e.bundleUrl);if(i){var a;let e;try{e=new t(i)}catch(t){throw new s("INVALID_ARGS",`Invalid runtime bundle URL: ${i}`,{},t)}("http:"===e.protocol||"https:"===e.protocol)&&(o??=l(e.hostname),r??=u(e.port.length>0?Number(e.port):"https:"===(a=e.protocol)?443:"http:"===a?80:void 0),n="https:"===e.protocol?"https":"http")}if(o&&r)return{host:o,port:r,scheme:n}}(e)}async function p(t){let e=await i({projectRoot:t.projectRoot,kind:t.kind,publicBaseUrl:t.publicBaseUrl,proxyBaseUrl:t.proxyBaseUrl,proxyBearerToken:t.proxyBearerToken,launchUrl:t.launchUrl,companionProfileKey:t.profileKey,companionConsumerKey:t.consumerKey,metroPort:t.port,listenHost:t.listenHost,statusHost:t.statusHost,startupTimeoutMs:t.startupTimeoutMs,probeTimeoutMs:t.probeTimeoutMs,reuseExisting:t.reuseExisting,installDependenciesIfNeeded:t.installDependenciesIfNeeded,runtimeFilePath:t.runtimeFilePath,logPath:t.logPath,env:t.env});return{iosRuntime:e.iosRuntime,androidRuntime:e.androidRuntime,bridge:e.bridge,started:e.started,reused:e.reused,logPath:e.logPath}}async function d(t){let e=await r(t);return{pid:e.pid,started:e.spawned,logPath:e.logPath}}async function m(t){await n(t)}function c(t){return o(t,"ios")}function h(t){return o(t,"android")}e.join("cmdline-tools","latest","bin"),e.join("cmdline-tools","tools","bin");export{buildBundleUrl,normalizeBaseUrl}from"./974.js";export{h as buildAndroidRuntimeHints,c as buildIosRuntimeHints,d as ensureMetroTunnel,p as prepareRemoteMetro,a as resolveRuntimeTransport,m as stopMetroTunnel};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-device",
3
- "version": "0.11.15",
3
+ "version": "0.12.0",
4
4
  "description": "Unified control plane for physical and virtual devices via an agent-driven CLI.",
5
5
  "license": "MIT",
6
6
  "author": "Callstack",
@@ -18,7 +18,7 @@ Use this skill as a router with mandatory defaults. Read this file first. For no
18
18
  - In React Native dev or debug builds, check early for visible warning or error overlays, tooltips, and toasts that can steal focus or intercept taps. If they are not part of the requested behavior, dismiss them and continue. If you saw them, report them in the final summary.
19
19
  - Do not browse the web or use external sources unless the user explicitly asks.
20
20
  - Re-snapshot after meaningful UI changes instead of reusing stale refs.
21
- - Treat refs in default snapshot output as actionable-now, not durable identities. If a target is off-screen, use `scrollintoview` or scroll and re-snapshot.
21
+ - Treat refs in default snapshot output as actionable-now, not durable identities. If a target appears only in an off-screen summary, use `scroll <direction>` and re-snapshot until the target is visible.
22
22
  - Prefer `@ref` or selector targeting over raw coordinates.
23
23
  - Ensure the correct target is pinned and an app session is open before interacting.
24
24
  - Keep the loop short: `open` -> inspect/act -> verify if needed -> `close`.
@@ -38,7 +38,7 @@ Open this file when the app or screen is already running and you need to discove
38
38
  - `press`
39
39
  - `fill`
40
40
  - `type`
41
- - `scrollintoview`
41
+ - `scroll`
42
42
  - `wait`
43
43
  - `keyboard dismiss` when the keyboard obscures the next target
44
44
 
@@ -115,10 +115,10 @@ App: com.apple.Preferences
115
115
  ## Refs vs selectors
116
116
 
117
117
  - Use refs for discovery, debugging, and short local loops.
118
- - Use `scrollintoview @ref` when the target is already known from the current snapshot and you want the command to re-snapshot after each swipe until the element reaches the viewport safe band.
119
- - If `scrollintoview @ref` succeeds, prefer the returned `currentRef` for the next action.
118
+ - When a target appears only in a visible-first off-screen summary, such as `[off-screen below] ... "Battery"`, use `scroll down` and then `snapshot -i`. For `[off-screen above]`, use `scroll up` and then `snapshot -i`.
119
+ - For more than two repeated scroll checks, create a short shell loop instead of issuing each command by hand. Stop when the label appears or the snapshot stops changing.
120
120
  - Visible-first off-screen summaries are intentionally compact. If you need the full off-screen tree instead of a short summary, retry with `snapshot --raw`.
121
- - Cap long searches with `--max-scrolls <n>` when the list may be unbounded or the target may not exist.
121
+ - Cap long searches in the loop when the list may be unbounded or the target may not exist.
122
122
  - Use selectors for deterministic scripts, assertions, and replay-friendly actions.
123
123
  - Prefer selector or `@ref` targeting over raw coordinates.
124
124
  - For tap interactions, `press` is canonical and `click` is an equivalent alias.
@@ -132,11 +132,25 @@ agent-device press 'id="camera_row" || label="Camera" role=button'
132
132
  agent-device is visible 'id="camera_settings_anchor"'
133
133
  ```
134
134
 
135
+ Example loop:
136
+
137
+ ```bash
138
+ previous=''
139
+ for _ in 1 2 3 4 5 6; do
140
+ current="$(agent-device snapshot -i)"
141
+ printf '%s\n' "$current"
142
+ printf '%s\n' "$current" | grep -q 'Battery' && break
143
+ [ "$current" = "$previous" ] && break
144
+ previous="$current"
145
+ agent-device scroll down 0.5 >/dev/null
146
+ done
147
+ ```
148
+
135
149
  ## Interaction fallbacks
136
150
 
137
151
  When `press @ref` fails:
138
152
 
139
- 1. If the error says the ref is off-screen, run `scrollintoview @ref` and reuse the returned `currentRef` or take one fresh snapshot.
153
+ 1. If the error says the ref is off-screen, use the off-screen summary direction to run `scroll <direction>`, then take a fresh `snapshot -i`.
140
154
  2. Re-snapshot if the UI may have changed.
141
155
  3. Retry `press @ref` or a selector-based `press`.
142
156
  4. If `screenshot --overlay-refs --json` returned a reliable `overlayRefs[].center`, use `agent-device press <x> <y>`.
@@ -85,3 +85,4 @@ Troubleshooting:
85
85
  - If `menubar` is missing the expected menu, retry with `open <app> --platform macos --surface menubar` for menu bar apps, or make the app frontmost first and retry the generic menubar surface.
86
86
  - If the wrong menu opened, retry secondary-clicking the row or cell wrapper rather than the nested text node.
87
87
  - If the app has multiple windows, make the correct window frontmost before relying on refs.
88
+ - If overriding the local helper, set `AGENT_DEVICE_MACOS_HELPER_BIN` to an absolute executable path; relative helper paths are rejected.