agent-device 0.20.8 → 0.20.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/README.md +94 -31
  2. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.8.apk → agent-device-android-ime-helper-0.20.9.apk} +0 -0
  3. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.9.apk.sha256 +1 -0
  4. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.20.8.manifest.json → agent-device-android-ime-helper-0.20.9.manifest.json} +4 -4
  5. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.8.apk → agent-device-android-snapshot-helper-0.20.9.apk} +0 -0
  6. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.9.apk.sha256 +1 -0
  7. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.20.8.manifest.json → agent-device-android-snapshot-helper-0.20.9.manifest.json} +6 -6
  8. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +3 -0
  9. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +13 -7
  10. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +4 -112
  11. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +9 -1
  12. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXPresentation.swift +90 -0
  13. package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +46 -10
  14. package/dist/src/agent-browser-tool.js +1 -1
  15. package/dist/src/agent-device-client.js +2 -2
  16. package/dist/src/android-adb.d.ts +5 -5
  17. package/dist/src/android-adb.js +1 -1
  18. package/dist/src/android.js +1 -1
  19. package/dist/src/app-helpers.js +1 -1
  20. package/dist/src/app-lifecycle.js +6 -7
  21. package/dist/src/app-lifecycle2.js +2 -2
  22. package/dist/src/app-log-reconnect.js +1 -1
  23. package/dist/src/app-log-runtime.js +3 -3
  24. package/dist/src/app-resolution.js +2 -2
  25. package/dist/src/app-state.js +2 -0
  26. package/dist/src/appearance.js +1 -0
  27. package/dist/src/apps.js +5 -5
  28. package/dist/src/artifacts.d.ts +1 -1
  29. package/dist/src/audio-probe.js +1 -1
  30. package/dist/src/auth-session.js +1 -1
  31. package/dist/src/backend-snapshot-options.js +1 -0
  32. package/dist/src/boot-diagnostics.js +2 -2
  33. package/dist/src/bounds.js +1 -0
  34. package/dist/src/byte-limit-stream.js +1 -0
  35. package/dist/src/cancel.js +1 -0
  36. package/dist/src/cli-config.js +1 -1
  37. package/dist/src/cli-help.js +25 -20
  38. package/dist/src/cli.js +6 -6
  39. package/dist/src/client-metro.js +1 -1
  40. package/dist/src/command-catalog.js +1 -0
  41. package/dist/src/command-metadata.js +1 -1
  42. package/dist/src/command-schema.js +1 -1
  43. package/dist/src/command-surface.js +1 -1
  44. package/dist/src/config.js +1 -0
  45. package/dist/src/connection-runtime.js +1 -1
  46. package/dist/src/connection.js +3 -3
  47. package/dist/src/contracts.js +1 -1
  48. package/dist/src/debug-symbols.js +3 -3
  49. package/dist/src/device-claim-inspection.js +1 -1
  50. package/dist/src/device-input-state.js +1 -1
  51. package/dist/src/device-ready.js +1 -1
  52. package/dist/src/device2.js +2 -2
  53. package/dist/src/dispatch.js +1 -1
  54. package/dist/src/entities.js +1 -1
  55. package/dist/src/find.js +1 -1
  56. package/dist/src/finders.js +1 -1
  57. package/dist/src/generic-settle.js +1 -1
  58. package/dist/src/generic.js +1 -1
  59. package/dist/src/harmonyos.js +1 -1
  60. package/dist/src/hdc.js +1 -1
  61. package/dist/src/helper-package-install.js +1 -0
  62. package/dist/src/helper.js +2 -2
  63. package/dist/src/host-process.js +1 -1
  64. package/dist/src/ime-lifecycle.js +2 -9
  65. package/dist/src/ime-recovery-marker.js +1 -0
  66. package/dist/src/index.d.ts +24 -15
  67. package/dist/src/index.js +1 -1
  68. package/dist/src/input-actions.js +4 -4
  69. package/dist/src/input-actions2.js +1 -1
  70. package/dist/src/input-actions3.js +1 -1
  71. package/dist/src/install-artifact.js +1 -1
  72. package/dist/src/install-artifact2.js +1 -1
  73. package/dist/src/install-source2.js +1 -1
  74. package/dist/src/interaction-common.js +1 -0
  75. package/dist/src/interaction-runtime.js +1 -1
  76. package/dist/src/interaction-snapshot.js +1 -1
  77. package/dist/src/interaction.js +1 -1
  78. package/dist/src/interaction2.js +1 -0
  79. package/dist/src/interactor.js +2 -2
  80. package/dist/src/interactor2.js +1 -1
  81. package/dist/src/interactors.js +1 -0
  82. package/dist/src/internal/bin.js +1 -1
  83. package/dist/src/internal/daemon.js +4 -7
  84. package/dist/src/internal/png-worker.js +1 -1
  85. package/dist/src/inventory.js +1 -1
  86. package/dist/src/keyed-lock.js +1 -0
  87. package/dist/src/lease-context.js +1 -1
  88. package/dist/src/lease.js +1 -1
  89. package/dist/src/limrun-runtime-dependencies.js +1 -1
  90. package/dist/src/limrun.d.ts +3 -1
  91. package/dist/src/limrun.js +1 -1
  92. package/dist/src/linux.js +1 -1
  93. package/dist/src/location-coordinates.js +1 -0
  94. package/dist/src/logcat.js +1 -1
  95. package/dist/src/manifest.js +1 -1
  96. package/dist/src/observability.js +1 -0
  97. package/dist/src/owner-identity.js +1 -1
  98. package/dist/src/perf.js +4 -0
  99. package/dist/src/physical-device-control.js +1 -1
  100. package/dist/src/platform-runtime-app-log-process.js +1 -1
  101. package/dist/src/platform-runtime-host.js +1 -1
  102. package/dist/src/platform-runtime-network-web-transport.js +1 -1
  103. package/dist/src/platform-runtime-open-target.js +1 -0
  104. package/dist/src/platform-runtime-operation-host.js +4 -4
  105. package/dist/src/platform-runtime-runtime-hints.js +2 -0
  106. package/dist/src/platform-runtime-screen-recording-apple-runner-host.js +1 -1
  107. package/dist/src/platform-runtime-screen-recording-apple-runner-transport.js +1 -1
  108. package/dist/src/platform-runtime-screen-recording-apple-transport.js +1 -1
  109. package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
  110. package/dist/src/platform-runtime-screen-recording-web-host.js +1 -1
  111. package/dist/src/platform-runtime.js +1 -1
  112. package/dist/src/platform-runtime2.js +1 -0
  113. package/dist/src/plist.js +1 -1
  114. package/dist/src/png-resize.js +1 -0
  115. package/dist/src/png-worker-contract.js +1 -1
  116. package/dist/src/prepare-kind.js +13 -13
  117. package/dist/src/process-lock.js +1 -1
  118. package/dist/src/project-runtime.js +1 -0
  119. package/dist/src/provider-webdriver.js +1 -1
  120. package/dist/src/provider.js +1 -0
  121. package/dist/src/proxy.js +1 -1
  122. package/dist/src/react-native.js +1 -1
  123. package/dist/src/record-runtime.js +1 -1
  124. package/dist/src/record-trace.js +1 -1
  125. package/dist/src/recording-export-quality.js +1 -0
  126. package/dist/src/registry.js +29 -26
  127. package/dist/src/remote-config2.js +1 -1
  128. package/dist/src/replay.js +1 -1
  129. package/dist/src/reporting.js +3 -3
  130. package/dist/src/rolldown-runtime.js +1 -0
  131. package/dist/src/runner-client.js +1 -0
  132. package/dist/src/runner-disposal.js +1 -1
  133. package/dist/src/runner-lease.js +1 -1
  134. package/dist/src/runner-sequence.js +1 -1
  135. package/dist/src/runtime-transport.js +1 -1
  136. package/dist/src/runtime.js +3 -3
  137. package/dist/src/runtime2.js +4 -3
  138. package/dist/src/runtime3.js +1 -3
  139. package/dist/src/runtime4.js +3 -1
  140. package/dist/src/runtime5.js +1 -1
  141. package/dist/src/runtime6.js +1 -1
  142. package/dist/src/runtime7.js +1 -1
  143. package/dist/src/runtime8.js +1 -0
  144. package/dist/src/runtime9.js +1 -0
  145. package/dist/src/scoped-provider.js +1 -0
  146. package/dist/src/screenshot-density.js +1 -0
  147. package/dist/src/screenshot-geometry.js +1 -0
  148. package/dist/src/screenshot-result.js +4 -4
  149. package/dist/src/screenshot.js +1 -1
  150. package/dist/src/sdk-android-adb.d.ts +20 -2
  151. package/dist/src/sdk-android-adb.js +3 -3
  152. package/dist/src/sdk-batch-runner.js +3 -3
  153. package/dist/src/sdk-contracts.d.ts +3 -3
  154. package/dist/src/sdk-contracts.js +2 -2
  155. package/dist/src/sdk-device.js +1 -1
  156. package/dist/src/sdk-finders.js +1 -1
  157. package/dist/src/sdk-io.js +1 -1
  158. package/dist/src/sdk-remote-config.js +1 -1
  159. package/dist/src/sdk-selectors.d.ts +19 -19
  160. package/dist/src/sdk-selectors.js +1 -1
  161. package/dist/src/sdk.js +1 -0
  162. package/dist/src/selector-runtime.js +1 -1
  163. package/dist/src/selectors.d.ts +1 -1
  164. package/dist/src/selectors.js +1 -1
  165. package/dist/src/server.js +2 -2
  166. package/dist/src/session-target-evidence.js +1 -1
  167. package/dist/src/session.js +1 -30
  168. package/dist/src/session2.js +28 -0
  169. package/dist/src/setting-state.js +1 -1
  170. package/dist/src/shared.js +1 -1
  171. package/dist/src/simctl.js +1 -0
  172. package/dist/src/simulator-state.js +1 -0
  173. package/dist/src/simulator.js +1 -1
  174. package/dist/src/snapshot-runtime.js +1 -1
  175. package/dist/src/snapshot-session.js +1 -1
  176. package/dist/src/snapshot.js +8 -1
  177. package/dist/src/snapshot2.js +1 -1
  178. package/dist/src/snapshot3.js +1 -0
  179. package/dist/src/src.js +1 -1
  180. package/dist/src/src2.js +1 -2
  181. package/dist/src/src3.js +2 -1
  182. package/dist/src/string-enum.js +1 -0
  183. package/dist/src/tool-provider.js +3 -3
  184. package/dist/src/tool-provider2.js +1 -1
  185. package/dist/src/update-check.js +1 -1
  186. package/dist/src/verdict.js +1 -0
  187. package/dist/src/verified-file.js +1 -0
  188. package/dist/src/version.js +1 -1
  189. package/dist/src/video.js +1 -1
  190. package/dist/src/web.js +1 -1
  191. package/package.json +8 -4
  192. package/server.json +3 -3
  193. package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.8.apk.sha256 +0 -1
  194. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.8.apk.sha256 +0 -1
  195. package/dist/src/interaction-touch-reference-frame.js +0 -1
  196. package/dist/src/notifications.js +0 -1
  197. package/dist/src/platform-runtime-toolchain-host.js +0 -1
  198. package/dist/src/text-surface.js +0 -1
  199. package/dist/src/tool-provider3.js +0 -1
@@ -1,8 +1,8 @@
1
- import{n as e}from"./version.js";import{mn as t}from"./sdk-batch-runner.js";import{Qt as n}from"./prepare-kind.js";import{S as r,d as i,m as a}from"./registry.js";import{i as o,r as s}from"./command-schema.js";const c=[`Flows: launchApp; runFlow file/inline with platform, visibility, and limited boolean conditions; onFlowStart/onFlowComplete; repeat.times and retry.`,`Interactions: tapOn, doubleTapOn, longPressOn, inputText, eraseText, openLink, hideKeyboard, basic pressKey, and back; targets support index, childOf, label, points, and optional.`,`Assertions and navigation: assertVisible, assertNotVisible, extendedWaitUntil, scroll, scrollUntilVisible, absolute/percentage/target swipe, takeScreenshot, waitForAnimationToEnd, and stopApp.`,`Scripts: ordered runScript file/env scripts with http.post, json, and output variables.`],l=[`Runtime: iOS and Android only; launchApp.clearState supports Android and iOS simulators, launch arguments are Apple-only, and standalone device utility/state commands are unsupported.`,`Expressions: when.true supports boolean literals and maestro.platform comparisons; repeat.while, evalScript, and broader JavaScript expressions are unsupported.`,`Environment: flow env is the default, AD_VAR_* overrides it, and CLI -e KEY=VALUE wins over both.`,`Failure diagnostics: resolved targets and runFlow paths are rendered, while inputText payloads remain hidden; do not place secrets in diagnostic identifiers.`,`Trust: runScript executes trusted scripts, may make http.post network requests, and is not a security sandbox; output keys cannot contain a dot.`,`Errors and tracking: unsupported commands and fields fail with source context when available; open a focused issue only when implementation work is planned.`];function u(){return[`Supported subset:`,...c.map(e=>` - ${e}`),``,`Boundaries:`,...l.map(e=>` - ${e}`)].join(`
2
- `)}function d(e){let t=e.endsWith(`?`),n=t?e.slice(0,-1):e;return t?`[${n}]`:`<${n}>`}function f(e){return r().filter(t=>t.key===e)}function p(e,t){if(t.usageOverride)return t.usageOverride;let n=(t.positionalArgs??[]).map(d),r=(t.allowedFlags??[]).flatMap(e=>f(e).map(e=>e.usageLabel??e.names[0])).map(e=>`[${e}]`);return[e,...n,...r].join(` `)}function m(){return`agent-device <command> [args] [--json]
1
+ import{Yn as e}from"./sdk-batch-runner.js";import{i as t}from"./command-catalog.js";import{S as n,d as r,m as i}from"./registry.js";import{n as a}from"./version.js";import{i as o,r as s}from"./command-schema.js";const c=[`Flows: launchApp; runFlow file/inline with platform, visibility, and limited boolean conditions; onFlowStart/onFlowComplete; repeat.times and retry.`,`Interactions: tapOn, doubleTapOn, longPressOn, inputText, eraseText, openLink, hideKeyboard, basic pressKey, and back; targets support index, childOf, label, points, and optional.`,`Assertions and navigation: assertVisible, assertNotVisible, extendedWaitUntil, scroll, scrollUntilVisible, absolute/percentage/target swipe, takeScreenshot, waitForAnimationToEnd, and stopApp.`,`Scripts: ordered runScript file/env scripts with http.post, json, and output variables.`],l=[`Runtime: iOS and Android only; launchApp.clearState supports Android and iOS simulators, launch arguments are Apple-only, and standalone device utility/state commands are unsupported.`,`Expressions: when.true supports boolean literals and maestro.platform comparisons; repeat.while, evalScript, and broader JavaScript expressions are unsupported.`,`Environment: flow env is the default, AD_VAR_* overrides it, and CLI -e KEY=VALUE wins over both.`,`Failure diagnostics: resolved targets and runFlow paths are rendered, while inputText payloads remain hidden; do not place secrets in diagnostic identifiers.`,`Trust: runScript executes trusted scripts, may make http.post network requests, and is not a security sandbox; output keys cannot contain a dot.`,`Errors and tracking: unsupported commands and fields fail with source context when available; open a focused issue only when implementation work is planned.`];function u(){return[`Supported subset:`,...c.map(e=>` - ${e}`),``,`Boundaries:`,...l.map(e=>` - ${e}`)].join(`
2
+ `)}function d(e){let t=e.endsWith(`?`),n=t?e.slice(0,-1):e;return t?`[${n}]`:`<${n}>`}function f(e){return n().filter(t=>t.key===e)}function p(e,t){if(t.usageOverride)return t.usageOverride;let n=(t.positionalArgs??[]).map(d),r=(t.allowedFlags??[]).flatMap(e=>f(e).map(e=>e.usageLabel??e.names[0])).map(e=>`[${e}]`);return[e,...n,...r].join(` `)}function m(){return`agent-device <command> [args] [--json]
3
3
 
4
4
  Automates iOS, Android, macOS, TV, and web apps for AI agents.
5
- All ${n().length} commands: agent-device help commands
5
+ All ${t().length} commands: agent-device help commands
6
6
 
7
7
  Start:
8
8
  When starting a task with a known app, first run:
@@ -24,7 +24,7 @@ Loop:
24
24
  Targets:
25
25
  Copy refs exactly: @e12, @e12~s4. Keep @ and any ~sN pin; refs go stale
26
26
  after mutations. A literal @handle is label="@handle", not a bare ref.
27
- Prefer refs, then id/label/role selectors. Selector keys: ${t.join(` `)}.
27
+ Prefer refs, then id/label/role selectors. Selector keys: ${e.join(` `)}.
28
28
  Coordinates are last resort: after snapshot -i shows no semantic target, or a
29
29
  sparse/AX-unavailable warning says its refs and selectors are invalid.
30
30
  Then screenshot, press <x> <y>, and re-snapshot on the changed screen.
@@ -259,7 +259,7 @@ Vega OS:
259
259
  vega virtual-device stop
260
260
  Use a component ID from the app package or Vega SDK tooling; agent-device app inventory is not yet supported.
261
261
  Use --serial VirtualDevice for explicit VVD selection.
262
- agent-device does not boot the VVD implicitly.
262
+ The VVD is never booted implicitly; start it with vega virtual-device start.
263
263
  Snapshot, screenshot, selectors, install, touch/text/gesture, logs, and performance commands remain unsupported until their Vega backends are implemented.
264
264
 
265
265
  Buttons:
@@ -368,7 +368,7 @@ Diagnostics and traces:
368
368
  agent-device perf cpu profile report --kind xctrace --out ./artifacts/app-profile.json
369
369
  agent-device perf trace start --kind xctrace --template "Animation Hitches" --out ./artifacts/hitches.trace
370
370
  agent-device perf trace stop --kind xctrace --out ./artifacts/hitches.trace
371
- perf xctrace returns artifact paths and compact metadata only. Do not dump .trace contents into context.
371
+ perf xctrace keeps the .trace artifact on disk; CPU report returns a bounded weighted top-function summary. Do not dump .trace contents into context.
372
372
  For Android native CPU/trace evidence, use perf artifacts instead of raw adb/simpleperf/perfetto output:
373
373
  agent-device perf cpu profile start --kind simpleperf --out /tmp/cpu.perf.data
374
374
  agent-device perf cpu profile stop --kind simpleperf
@@ -376,6 +376,10 @@ Diagnostics and traces:
376
376
  agent-device perf trace start --kind perfetto --out /tmp/app.perfetto-trace
377
377
  agent-device perf trace stop --kind perfetto
378
378
  Treat native perf output as the agent evidence: for example, state=stopped, outPath=/tmp/app.perfetto-trace, sizeBytes=5392410, method=adb-shell-perfetto. The 5.3 MB raw trace stays in the artifact.
379
+ CPU reports return at most ten top functions in structured data and print five in default CLI output.
380
+ Prefer explicit perf frames, perf memory, perf cpu, or perf trace forms. Bare perf, perf sample, perf metrics, and metrics are deprecated compatibility forms until the next major release.
381
+ On Android, those deprecated aggregate forms retain the released dumpsys cpuinfo point sample for compatibility; new profiling workflows should use perf cpu profile.
382
+ Startup duration belongs to the open command's startup result; read it there instead of using aggregate perf.
379
383
 
380
384
  Memory diagnostics:
381
385
  Use perf memory when the symptom is leak/growth/OOM suspicion and you need agent-readable evidence.
@@ -386,7 +390,7 @@ Memory diagnostics:
386
390
  {"metrics":{"memory":{"available":true,"totalPssKb":562958,"totalRssKb":570304,"topConsumers":[{"name":"Dalvik Heap","pssKb":213456}]}}}
387
391
  Example default snapshot output:
388
392
  Memory artifact (android-hprof): /tmp/app.hprof (42MB)
389
- Prefer perf memory sample over raw dumpsys/leaks output for first-pass agent diagnosis: it keeps arrays bounded, preserves the same memory source as perf metrics, and returns only memory data instead of startup/CPU/frame noise.
393
+ Prefer perf memory sample over raw dumpsys/leaks output for first-pass agent diagnosis: it keeps arrays bounded and returns only relevant memory evidence.
390
394
  Prefer perf memory snapshot over printing heap/memgraph data: snapshots return path, size, kind, method, and support metadata while the large artifact stays on disk for external inspection.
391
395
  Unsupported platforms return artifact.available=false with reason/hint; do not pretend a heap or memgraph was captured.
392
396
 
@@ -464,7 +468,7 @@ Example:
464
468
  agent-device react-devtools profile report @c5
465
469
  agent-device network dump --include headers
466
470
 
467
- Use snapshot, screenshot, logs, network, and perf metrics for device/app runtime evidence. Use react-devtools only when component internals or React rendering behavior matters.`},cdp:{summary:`React Native CDP targets, JS heap snapshots, and leak triage`,body:`agent-device help cdp
471
+ Use snapshot, screenshot, logs, network, perf frames, and perf memory for device/app runtime evidence. Use react-devtools when component internals or React rendering behavior matters.`},cdp:{summary:`React Native CDP targets, JS heap snapshots, and leak triage`,body:`agent-device help cdp
468
472
 
469
473
  Use this when a React Native or Expo app exposes a CDP target through Metro and
470
474
  the task needs JavaScript heap growth checks, heap snapshot diffs, allocation
@@ -570,7 +574,7 @@ Overlays and busy RN UIs:
570
574
  React DevTools routing:
571
575
  Keep the agent-device react-devtools prefix on every React DevTools command.
572
576
  Use help react-devtools for status/wait, component trees, props/state/hooks, profile windows, slow renders, rerenders, and remote bridge rules.
573
- If React DevTools cannot connect, report status and continue with logs, network, perf metrics, screenshot, and trace evidence instead of blocking the whole flow.
577
+ If React DevTools cannot connect, report status and continue with logs, network, perf frames, perf memory, screenshot, and trace evidence instead of blocking the whole flow.
574
578
 
575
579
  CDP memory routing:
576
580
  Keep the agent-device cdp prefix on every CDP command.
@@ -587,7 +591,7 @@ Slow-flow investigation:
587
591
  If the task says to open the app, include the open command even when it also describes the current screen.
588
592
  Use help react-devtools for the narrow React profile window. Profiling plans need both status and wait --connected before profile start.
589
593
  Check status before wait/profile. Do not substitute react-devtools start for status; start launches the helper, while status reports connection state.
590
- Use help debugging for logs clear --restart, logs mark, network dump --include headers, perf metrics --json, traces, and runtime failure evidence.
594
+ Use help debugging for logs clear --restart, logs mark, network dump --include headers, perf frames, perf memory, native profiles, traces, and runtime failure evidence.
591
595
  For 15-20s async work, use wait with the exact expected text or selector instead of repeated snapshots.
592
596
  Report React render offenders separately from network/backend waits and device frame/CPU/memory findings.`},"physical-device":{summary:`Connected phone/tablet setup and iOS signing prerequisites`,body:`agent-device help physical-device
593
597
 
@@ -770,6 +774,7 @@ Open and inspect:
770
774
  agent-device snapshot -i --platform macos
771
775
 
772
776
  Surfaces:
777
+ --surface is an open flag; the session keeps it, so later commands do not repeat it.
773
778
  --surface app normal app session
774
779
  --surface frontmost-app inspect whichever app is frontmost
775
780
  --surface desktop desktop-wide surface
@@ -777,7 +782,7 @@ Surfaces:
777
782
 
778
783
  Menu bar app example:
779
784
  agent-device open "Agent Device Tester Menu" --platform macos --surface menubar
780
- agent-device snapshot -i --platform macos --surface menubar
785
+ agent-device snapshot -i --platform macos
781
786
 
782
787
  Context menu example:
783
788
  agent-device click @e66 --button secondary --platform macos
@@ -793,7 +798,7 @@ Rules:
793
798
  Use --platform web only for the minimal browser command loop exposed through agent-device.
794
799
 
795
800
  Dependency:
796
- agent-device uses a managed, pinned agent-browser backend for browser mechanics. agent-device owns command/session/replay integration, selectors/refs at the command surface, and artifact routing; agent-browser owns browser launch, page control, screenshots, and browser-specific behavior.
801
+ Browser mechanics come from a managed, pinned agent-browser backend. agent-device owns command/session/replay integration, selectors/refs at the command surface, and artifact routing; agent-browser owns browser launch, page control, screenshots, and browser-specific behavior.
797
802
  Use --platform web when a browser step belongs inside an agent-device session, replay, batch, MCP, or typed-client flow. Use agent-browser directly for standalone web automation.
798
803
  Before first use, set up and verify the managed backend:
799
804
  agent-device web setup
@@ -890,7 +895,7 @@ Rules:
890
895
  scroll takes direction then amount and does not support a selector or --settle: agent-device scroll down 3.
891
896
  Keep commands in the report reproducible; use selectors or refs from fresh snapshots, not guessed coordinates.
892
897
  Prefer refs for exploration and selectors for deterministic replay.
893
- Use logs, network, screenshot --overlay-refs, trace, perf metrics, perf frames, or react-devtools only when they add evidence to a specific issue.
898
+ Use logs, network, screenshot --overlay-refs, trace, perf frames, perf memory, native profiles, or react-devtools only when they add evidence to a specific issue.
894
899
  Never delete screenshots, videos, traces, or report artifacts during a session.
895
900
  Escalate to help debugging or help react-devtools when runtime symptoms require those tools.`},validate:{summary:`Engineering self-validation with device evidence and cleanup`,body:`agent-device help validate
896
901
 
@@ -921,7 +926,7 @@ Evidence:
921
926
  Visual claim: screenshot, optionally screenshot --overlay-refs when target mapping matters.
922
927
  Runtime/logging claim: logs clear --restart, logs mark, reproduce, logs path.
923
928
  Network claim: network dump --include headers when headers are relevant.
924
- Performance claim: perf metrics, perf frames, perf memory sample, or trace artifacts with bounded output.
929
+ Performance claim: perf frames, perf memory sample, native profile reports, or trace artifacts with bounded output.
925
930
  Replay/regression claim: replay or test through the public command path.
926
931
 
927
932
  Report:
@@ -930,27 +935,27 @@ Report:
930
935
 
931
936
  Full command catalog. Use agent-device help <command> for exact flags and behavior.
932
937
 
933
- ${A(n().map(e=>{let t=s(e);return{name:e,schema:t,usage:x(e,t)}}))}
938
+ ${A(t().map(e=>{let t=s(e);return{name:e,schema:t,usage:x(e,t)}}))}
934
939
 
935
- ${D(`Global Flags:`,T(i))}
940
+ ${D(`Global Flags:`,T(r))}
936
941
 
937
942
  ${k(`Configuration:`,h)}
938
943
 
939
944
  ${O(`Environment:`,g)}
940
945
 
941
946
  ${k(`Examples:`,_)}
942
- `}function w(){return S()}function T(e){return r().filter(t=>e.has(t.key)&&t.usageLabel!==void 0&&t.usageDescription!==void 0)}function E(e,t){return t?e.map(e=>{let n=t[e.key];return n===void 0?e:{...e,usageDescription:n}}):e}function D(e,t){return O(e,t.map(e=>({label:e.usageLabel??``,description:e.usageDescription??``})))}function O(e,t){if(t.length===0)return`${e}\n (none)`;let n=Math.max(...t.map(e=>Math.min(e.label.length,26)))+2,r=[e];for(let e of t){let t=e.label.length<=26?e.label.padEnd(n):`${e.label} `;r.push(` ${t}${e.description}`)}return r.join(`
947
+ `}function w(){return S()}function T(e){return n().filter(t=>e.has(t.key)&&t.usageLabel!==void 0&&t.usageDescription!==void 0)}function E(e,t){return t?e.map(e=>{let n=t[e.key];return n===void 0?e:{...e,usageDescription:n}}):e}function D(e,t){return O(e,t.map(e=>({label:e.usageLabel??``,description:e.usageDescription??``})))}function O(e,t){if(t.length===0)return`${e}\n (none)`;let n=Math.max(...t.map(e=>Math.min(e.label.length,26)))+2,r=[e];for(let e of t){let t=e.label.length<=26?e.label.padEnd(n):`${e.label} `;r.push(` ${t}${e.description}`)}return r.join(`
943
948
  `)}function k(e,t){return t.length===0?`${e}\n (none)`:[e,...t.map(e=>` ${e}`)].join(`
944
- `)}function A(e){return O(`Commands:`,e.map(e=>({label:e.usage,description:e.schema.text.summary})))}function j(e){let t=M(e);if(t)return t;let n=o(e);if(!n)return null;let r=p(e,n),i=E(T(new Set(n.allowedFlags??[])),n.flagDescriptionOverrides),s=[];i.length>0&&s.push(D(`Command flags:`,i));let c=s.length>0?`\n\n${s.join(`
949
+ `)}function A(e){return O(`Commands:`,e.map(e=>({label:e.usage,description:e.schema.text.summary})))}function j(e){let t=M(e);if(t)return t;let n=o(e);if(!n)return null;let r=p(e,n),a=E(T(new Set(n.allowedFlags??[])),n.flagDescriptionOverrides),s=[];a.length>0&&s.push(D(`Command flags:`,a));let c=s.length>0?`\n\n${s.join(`
945
950
 
946
951
  `)}`:``;return`Usage:
947
952
  agent-device ${r}
948
953
 
949
- ${a(n.text)}${c}
954
+ ${i(n.text)}${c}
950
955
  `}function M(e){let t=y[e];return t?`${N(e,e===`commands`?C():t.body)}
951
956
 
952
957
  Related:
953
958
  agent-device help <command> command-specific flags
954
959
  agent-device help manual-qa routine QA loop
955
960
  agent-device help workflow full automation reference
956
- `:null}function N(t,n){let r=`agent-device help ${t}`;return n.startsWith(r)?`agent-device ${e()} — ${t}${n.slice(r.length)}`:n}export{j as buildCommandUsageText,w as buildUsageText};
961
+ `:null}function N(e,t){let n=`agent-device help ${e}`;return t.startsWith(n)?`agent-device ${a()} — ${e}${t.slice(n.length)}`:t}export{j as buildCommandUsageText,w as buildUsageText};
package/dist/src/cli.js CHANGED
@@ -1,7 +1,7 @@
1
- import{S as e,_ as t,v as n,w as r}from"./sdk-contracts.js";import{Tt as i,n as a}from"./version.js";import{Gn as o,f as s}from"./sdk-batch-runner.js";import{Qt as c,Yt as l,Zt as u,vt as d,yt as f}from"./prepare-kind.js";import{n as p,r as m}from"./agent-device-client.js";import{_ as h}from"./screenshot-result.js";import{S as g,x as _}from"./registry.js";import{n as v}from"./command-metadata.js";import{A as ee,L as te,M as y,N as b,P as ne,S as re,j as x}from"./sdk-android-adb.js";import{n as ie,t as ae}from"./internal/bin.js";import{i as S,n as C,t as oe}from"./command-schema.js";import{t as se}from"./update-check.js";import{n as w,r as T,t as ce}from"./cli-config.js";import{exitAfterFlush as E}from"./process-exit.js";import{D as le,E as ue,S as de,T as fe,h as pe,n as me,w as he}from"./connection-runtime.js";import{t as ge}from"./cli-grammar.js";import{i as _e}from"./auth-session.js";import D from"node:fs";import{pathToFileURL as ve}from"node:url";const O=`open <app> --relaunch`,ye={start:{command:`open`,example:O},restart:{command:`open`,example:O},touch:{command:`press`,example:`press`},input:{command:`fill`,example:`fill`},settext:{command:`fill`,example:`fill`},entertext:{command:`fill`,example:`fill`},screencap:{command:`screenshot`,example:`screenshot`},capture:{command:`screenshot`,example:`screenshot`},dismiss:{command:`keyboard`,example:`keyboard dismiss`},"get-text":{command:`get`,example:`get text`},gettext:{command:`get`,example:`get text`},get_text:{command:`get`,example:`get text`},"open-url":{command:`open`,example:`open <url>`},"close-session":{command:`close`,example:`close`}};function k(e){if(e.length<=2)return[];let t=c(),n=t.filter(t=>t.startsWith(e));if(n.length>0)return n.sort((e,t)=>e.length-t.length||e.localeCompare(t)).slice(0,3);let r=A(e),i=t.map(t=>({name:t,distance:j(e,t)})).filter(e=>e.distance<=r);if(i.length===0)return[];let a=Math.min(...i.map(e=>e.distance));return i.filter(e=>e.distance===a).sort((e,t)=>e.name.localeCompare(t.name)).slice(0,3).map(e=>e.name)}function A(e){return e.length<4?1:e.length<=6?2:3}function j(e,t){return e===t?0:e.startsWith(t)||t.startsWith(e)?Math.abs(e.length-t.length):M(e,t)}function M(e,t){let n=e.length+1,r=t.length+1,i=Array.from({length:n},()=>Array(r).fill(0));for(let e=0;e<n;e+=1)i[e][0]=e;for(let e=0;e<r;e+=1)i[0][e]=e;for(let a=1;a<n;a+=1)for(let n=1;n<r;n+=1){let r=e[a-1]===t[n-1]?0:1;i[a][n]=Math.min(i[a-1][n]+1,i[a][n-1]+1,i[a-1][n-1]+r)}return i[n-1][r-1]}function N(e){let t=e.toLowerCase(),n=ye[t]?.example;if(n)return n;let r=k(t);if(r.length!==0)return r.length===1?r[0]:`one of: ${r.join(`, `)}`}const P=new Set([`--bundle-id`,`--bundleid`,`--bundle`,`--package`,`--package-name`,`--packagename`,`--app-id`,`--appid`,`--pkg`]),be=new Set([`--session-locked`,`--session-lock-conflicts`]);function xe(e){return P.has(e.toLowerCase())?`Unknown flag: ${e}. The app or bundle id is a positional argument, e.g. ${O}.`:be.has(e.toLowerCase())?`Unknown flag: ${e}. Use --session-lock reject|strip instead.`:e.toLowerCase()===i.cliToken?`Unknown flag: ${e}. ${i.migration.screenshot}. ${i.migration.record}.`:`Unknown flag: ${e}`}function F(e){let n={json:!1,help:!1,version:!1},r=null,i=null,a=[],o=[],s=[],c=!0;for(let o=0;o<e.length;o+=1){let l=e[o];if(c&&l===`--`){c=!1;continue}if(!c){r?a.push(l):(i=l,r=B(l));continue}if(Te(r)){a.push(l);continue}let u=l.startsWith(`--`),d=l.startsWith(`-`)&&l.length>1;if(!u&&!d){r?a.push(l):(i=l,r=B(l));continue}let[f,p]=u?I(l):[l,void 0];if(Ce(r,f))continue;let m=Ee(f,r);if(we(r,m)){a.push(l);continue}if(!m){if(je(r,a,l)){r?a.push(l):r=l;continue}throw new t(`INVALID_ARGS`,xe(f))}let h=ke(m,f,p,e[o+1]);h.consumeNext&&(o+=1);let g=n[m.key];if(m.multiple){let e=Array.isArray(g)?[...g,h.value]:g===void 0?[h.value]:[g,h.value];n[m.key]=e}else n[m.key]=h.value;s.push({key:m.key,token:f})}return Se(i,n),{command:r,positionals:a,flags:n,warnings:o,providedFlags:s}}function Se(e,t){if(e)for(let n of ae(e)?.impliedFlags??[])t[n]=!0}function Ce(e,t){return t===`-c`&&(e===`snapshot`||e===`diff`)}function we(e,t){return e===`react-devtools`?t?!w(t.key,e):!0:!1}function Te(e){return e===`cdp`}function Ee(e,t){let n=g().filter(t=>t.names.includes(e));if(n.length<=1)return n[0]??_(e);if(t){let e=n.find(e=>w(e.key,t));if(e)return e}return _(e)}function De(e,n){let r=n?.strictFlags??!0,i=[...e.warnings],a=T({json:!1,help:!1,version:!1},n?.defaultFlags??{});if(T(a,e.flags),e.command&&!u(e.command)&&!a.help){let n=N(e.command);throw new t(`INVALID_ARGS`,n?`Unknown command: ${e.command}. Did you mean ${n}?`:`Unknown command: ${e.command}`)}let o=e.providedFlags.filter(t=>!w(t.key,e.command));if(o.length>0){let n=o.map(e=>e.token),s=Pe(e.command,n);if(r)throw new t(`INVALID_ARGS`,s);i.push(s);for(let e of o)delete a[e.key]}for(let t of Object.keys(a))a[t]!==void 0&&(w(t,e.command)||delete a[t]);Oe(e),oe(e.command,a);let s=Ne({command:e.command,positionals:e.positionals,flags:a,warnings:i});if(C(s.command,s.positionals),s.command===`batch`&&+!!a.steps+ +!!a.stepsFile!=1)throw new t(`INVALID_ARGS`,`batch requires exactly one step source: --steps or --steps-file.`);return s}function Oe(e){if(e.command!==`back`)return;let n=e.providedFlags.filter(e=>e.key===`backMode`);if(!(new Set(n.map(e=>e.token)).size<=1))throw new t(`INVALID_ARGS`,`back accepts only one explicit mode flag: use either --in-app or --system.`)}function I(e){let t=e.indexOf(`=`);return t===-1?[e,void 0]:[e.slice(0,t),e.slice(t+1)]}function ke(e,n,r,i){if(e.setValue!==void 0){if(r!==void 0)throw new t(`INVALID_ARGS`,`Flag ${n} does not take a value.`);return{value:e.setValue,consumeNext:!1}}if(e.type===`boolean`){if(r!==void 0)throw new t(`INVALID_ARGS`,`Flag ${n} does not take a value.`);return{value:!0,consumeNext:!1}}if(e.type===`booleanOrString`){if(r!==void 0){if(r.trim().length===0)throw new t(`INVALID_ARGS`,`Flag ${n} requires a non-empty value when provided.`);return{value:r,consumeNext:!1}}return i===void 0||R(i)?{value:!0,consumeNext:!1}:Ae(i)?{value:i,consumeNext:!0}:{value:!0,consumeNext:!1}}let a=r??i;if(a===void 0||r===void 0&&R(a))throw new t(`INVALID_ARGS`,`Flag ${n} requires a value.`);if(e.type===`string`)return{value:a,consumeNext:r===void 0};if(e.type===`enum`){if(!e.enumValues?.includes(a))throw new t(`INVALID_ARGS`,`Invalid ${L(n)}: ${a}`);return{value:a,consumeNext:r===void 0}}let o=Number(a);if(!Number.isFinite(o)||typeof e.min==`number`&&o<e.min||typeof e.max==`number`&&o>e.max)throw new t(`INVALID_ARGS`,`Invalid ${L(n)}: ${a}`);return{value:e.type===`int`?Math.floor(o):o,consumeNext:r===void 0}}function L(e){return e.replace(/^-+/,``)}function R(e){if(!e.startsWith(`-`)||e===`-`)return!1;let[t]=e.startsWith(`--`)?I(e):[e,void 0];return _(t)!==void 0}function Ae(e){let t=e.trim();return!t||/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(t)?!1:!!(t.startsWith(`./`)||t.startsWith(`../`)||t.startsWith(`~/`)||t.startsWith(`/`)||t.includes(`/`)||t.includes(`\\`))}function je(e,t,n){if(!Me(n)||!e)return!1;let r=S(e);if(!r||r.allowsExtraPositionals)return!0;let i=r.positionalArgs??[];return i.length===0?!1:t.length<i.length?!0:i.some(e=>e.includes(`?`))}function Me(e){return/^-\d+(\.\d+)?$/.test(e)}function Ne(e){if(e.flags.help)return e;if(e.command===`snapshot`&&e.flags.snapshotDiff){let{snapshotDiff:t,...n}=e.flags;return{command:`diff`,positionals:[`snapshot`,...e.positionals],flags:n,warnings:e.warnings}}return e}function Pe(e,t){return e?t.length===1?`Flag ${t[0]} is not supported for command ${e}.`:`Flags ${t.join(`, `)} are not supported for command ${e}.`:t.length===1?`Flag ${t[0]} requires a command that supports it.`:`Flags ${t.join(`, `)} require a command that supports them.`}async function z(){let{buildUsageText:e}=await import(`./cli-help.js`);return e()}async function Fe(e){let{buildCommandUsageText:t}=await import(`./cli-help.js`);return t(B(e))}function B(e){if(e.toLowerCase()===`rotate`)throw new t(`INVALID_ARGS`,`rotate was renamed to orientation; for the two-finger gesture use: gesture rotate`);return ie(e)}function Ie(e){return v(e)}const Le={connect:async()=>(await import(`./connection.js`)).connectCommand,disconnect:async()=>(await import(`./connection.js`)).disconnectCommand,connection:async()=>(await import(`./connection.js`)).connectionCommand,auth:async()=>(await import(`./auth.js`)).authCommand,daemon:async()=>(await import(`./daemon.js`)).daemonCommand,device:async()=>(await import(`./device2.js`)).deviceCommand,proxy:async()=>(await import(`./proxy.js`)).proxyCommand,replay:async()=>(await import(`./replay.js`)).replayCommand,screenshot:async()=>(await import(`./screenshot.js`)).screenshotCommand,diff:async()=>(await import(`./screenshot.js`)).diffCommand};async function V(e){let t={...e.flags},n=Le[e.command];return n&&await(await n())({...e,flags:t})?!0:Ie(e.command)?await Re({...e,command:e.command,flags:t}):!1}async function Re(e){let{runGenericClientBackedCommand:t}=await import(`./generic.js`);return await t(e)}function ze(e){return[`exec`,`--yes`,`--package`,`agent-react-devtools@0.4.0`,`--`,`agent-react-devtools`,...e]}function Be(e){return e===`ios-instance`}function Ve(e){return e[0]===`wait`&&e.includes(`--connected`)}function He(e,t,n){n===0||!Ve(e)||!Be(t?.leaseBackend)||process.stderr.write([`Hint: Remote iOS React DevTools connects during JavaScript startup.`,"If the app was already open before `agent-device react-devtools start`, relaunch it with `agent-device open <bundle-id> --platform ios --relaunch`, then retry `agent-device react-devtools wait --connected`.",``].join(`
2
- `))}function H(e,t,n){return n||(e.push(t),``)}function Ue(e){if(!e?.metroProxyBaseUrl||!fe(e.leaseBackend))return null;let n=[],r={serverBaseUrl:H(n,`metroProxyBaseUrl`,e.metroProxyBaseUrl),bearerToken:H(n,`metroBearerToken`,e.metroBearerToken),tenantId:H(n,`tenant`,e.tenant),runId:H(n,`runId`,e.runId),leaseId:H(n,`leaseId`,e.leaseId)};if(n.length>0)throw new t(`INVALID_ARGS`,`react-devtools remote bridge requires ${n.join(`, `)}.`,{missing:n});return r}async function We(e,t,n){let{flags:r}=t,i=Ue(r);if(!i)return n();let a=t.stateDir??process.cwd(),o=t.session??r?.session??`default`,s=r?.remoteConfig??`${i.tenantId}:${i.runId}:${i.leaseId}`;if(e[0]===`stop`)try{return await n()}finally{await le({projectRoot:t.cwd??process.cwd(),stateDir:a,profileKey:s,consumerKey:o})}return await ue({projectRoot:t.cwd??process.cwd(),stateDir:a,serverBaseUrl:i.serverBaseUrl,bearerToken:i.bearerToken,bridgeScope:{tenantId:i.tenantId,runId:i.runId,leaseId:i.leaseId},session:o,profileKey:s,consumerKey:o,env:t.env??process.env}),await n()}function Ge(e,t){if(e[0]!==`start`)return!1;let{flags:n}=t;return n?n.leaseProvider===`limrun`&&n.leaseBackend===`android-instance`&&n.metroProxyBaseUrl===void 0&&t.configureDirectPortReverse!==void 0:!1}async function Ke(e,t={}){let n=t.cwd??process.cwd(),r=t.env??process.env,i=await We(e,t,async()=>(Ge(e,t)&&await t.configureDirectPortReverse?.(),(await re(`npm`,ze(e),{cwd:n,env:r,allowFailure:!0,onStdoutChunk:e=>{process.stdout.write(e)},onStderrChunk:e=>{process.stderr.write(e)}})).exitCode));return He(e,t.flags,i),i}async function qe(e,n){switch(e[0]){case`setup`:{Je(n.flags.json);let{setupManagedAgentBrowser:e}=await import(`./agent-browser-tool.js`).then(e=>e.t),t=await e({stateDir:n.stateDir});return Ye(n.flags.json,t),0}case`doctor`:{let{doctorManagedAgentBrowser:e}=await import(`./agent-browser-tool.js`).then(e=>e.t),t=await e({stateDir:n.stateDir});return Xe(n.flags.json,t.exitCode===0?`Web backend is healthy.`:`Web backend doctor reported issues.`,t),t.exitCode}default:throw new t(`INVALID_ARGS`,`web requires setup or doctor`)}}function Je(e){e||process.stdout.write(`Setting up managed agent-browser backend (downloads if needed)...
3
- `)}function Ye(e,t){if(e){f({success:!0,data:{status:U(t)}});return}process.stdout.write(`Managed web backend installed.\nagent-browser available at: ${t.binaryPath}\n`)}function Xe(e,t,n){if(e){f({success:!0,data:Ze(n)});return}process.stdout.write(`${t}\n`)}function U(e){let{socketDir:t,...n}=e;return n}function Ze(e){let t=e.status;return Qe(t)?{...e,status:U(t)}:e}function Qe(e){return typeof e==`object`&&!!e&&`socketDir`in e&&`installDir`in e&&`binaryPath`in e}function $e(e){let n;try{n=JSON.parse(e)}catch{throw new t(`INVALID_ARGS`,`Batch steps must be valid JSON.`)}if(!Array.isArray(n)||n.length===0)throw new t(`INVALID_ARGS`,`Batch steps must be a non-empty JSON array.`);return et(n)}function et(e){let t=!1,n=e.map((e,n)=>{if(nt(e))return rt(e,n+1);let r=it(e,n+1);return t=!0,tt(r)});return t&&process.stderr.write(`Warning: batch steps using positionals/flags are deprecated and will be removed in the next major version. Use {"command":"...","input":{...}} steps instead.
4
- `),n}function tt(e){let t=ge(e.command,e.positionals??[],lt(e.flags));return{command:e.command,input:t,...e.runtime===void 0?{}:{runtime:e.runtime}}}function nt(e){return o(e)&&`input`in e&&!(`positionals`in e)&&!(`flags`in e)}function rt(e,t){let n=s(e.runtime,t),{runtime:r,...i}=e;return{...i,...n===void 0?{}:{runtime:n}}}function it(e,n){if(!o(e))throw new t(`INVALID_ARGS`,`Invalid batch step ${n}.`);ot(e,n);let r=at(e.command,n),i=st(e.positionals,n);C(r,i??[],`Batch step ${n}`);let a=ct(e.flags,n),c=s(e.runtime,n);return{command:r,...i===void 0?{}:{positionals:i},...a===void 0?{}:{flags:a},...c===void 0?{}:{runtime:c}}}function at(e,n){let r=typeof e==`string`?e.trim().toLowerCase():``;if(!r)throw new t(`INVALID_ARGS`,`Batch step ${n} requires command.`);if(v(r))return r;throw new t(`INVALID_ARGS`,`Batch step ${n} command is not available through command batch: ${String(e)}`)}function ot(e,n){let r=Object.keys(e).filter(e=>![`command`,`positionals`,`flags`,`runtime`].includes(e));if(r.length>0)throw new t(`INVALID_ARGS`,`Batch step ${n} has unknown legacy field(s): ${r.join(`, `)}.`)}function st(e,n){if(e!==void 0){if(!Array.isArray(e)||e.some(e=>typeof e!=`string`))throw new t(`INVALID_ARGS`,`Batch step ${n} positionals must contain only strings.`);return e}}function ct(e,n){if(e!==void 0){if(!o(e))throw new t(`INVALID_ARGS`,`Batch step ${n} flags must be an object.`);return e}}function lt(e){return{json:!1,help:!1,version:!1,...e}}function W(e,t={}){let n=G(t),r={...e};return n.defaultPlatform&&r.platform===void 0&&(r.platform=n.defaultPlatform),r}function G(e){let t=e.env??process.env,n=e.inheritedPlatform??e.configuredPlatform??dt(t.AGENT_DEVICE_PLATFORM),r=pt(e.configuredSession??t.AGENT_DEVICE_SESSION);return{defaultPlatform:n,lockPolicy:ut(e.policyOverrides,t,r)}}function ut(e,t,n){let r=e?.sessionLock??ft(t.AGENT_DEVICE_SESSION_LOCK);if(r)return r;if(n)return`reject`}function dt(e){if(e===void 0)return;let n=e.trim().toLowerCase();if(n){if(n===`ios`||n===`android`||n===`apple`)return n;throw new t(`INVALID_ARGS`,`Invalid AGENT_DEVICE_PLATFORM: ${e}. Use ios, android, or apple.`)}}function ft(e){if(e===void 0)return;let n=e.trim().toLowerCase();if(n){if(n===`reject`||n===`strip`)return n;throw new t(`INVALID_ARGS`,`Invalid session lock mode: ${e}. Use reject or strip.`)}}function pt(e){return typeof e==`string`&&e.trim().length>0}function mt(e,t){let n=F(e),r=t?.env??process.env,i=t?.cwd??process.cwd(),a=ht(n.command)?pe({remoteConfig:n.flags.remoteConfig,cwd:i,env:r}):{},o=T(ce({command:n.command,cwd:i,cliFlags:n.flags,env:r}),a);return{...De(n,{strictFlags:t?.strictFlags,defaultFlags:o}),providedFlags:n.providedFlags}}function ht(e){return e!==null}const gt={sendToDaemon:m},_t=new Set([`launchUrl`,`kind`,`metroBearerToken`,`metroKind`,`metroListenHost`,`metroNoInstallDeps`,`metroNoReuseExisting`,`metroPreparePort`,`metroProbeTimeoutMs`,`metroProjectRoot`,`metroProxyBaseUrl`,`metroPublicBaseUrl`,`metroRuntimeFile`,`metroStartupTimeoutMs`,`metroStatusHost`]),vt=new Set([`connect`,`connection`,`close`,`daemon`,`device`,`disconnect`,`metro`,`proxy`,`session`]);async function K(e,t=gt){let n=ee(),r=a(),i=Ot(e),o=e.includes(`--json`);await te({session:Bt(e)??process.env.AGENT_DEVICE_SESSION??`default`,requestId:n,command:e[0],debug:i},async()=>{let{parsed:a,command:s,positionals:c}=await yt(e,{version:r,jsonRequested:o,debugEnabled:i}),l=Z(s,a.providedFlags),u=await bt(a,{command:s,positionals:c,requestId:n,debugOutputEnabled:l});q(u.effectiveFlags);let d=null;try{if(s===`react-devtools`){await E(await xt(u,t));return}if(s===`web`){await E(await qe(c,{flags:u.effectiveFlags,stateDir:u.daemonPaths.baseDir}));return}if(se({command:s,currentVersion:r,stateDir:u.daemonPaths.baseDir,flags:u.effectiveFlags}),await St(u,t),q(u.effectiveFlags),s===`cdp`){await E(await he(c,{flags:u.effectiveFlags,runtime:u.resolvedRuntime,cwd:process.cwd(),env:process.env}));return}d=Ct(u);let e=await wt(u);await Tt(u,p(J(u),{transport:zt({command:s,flags:u.effectiveFlags,replayTestReporterRuntime:e,transport:t.sendToDaemon})}),e)}catch(e){await Et(e,u,d)}finally{d&&d()}})}function q(e){e.daemonAuthToken&&ne(e.daemonAuthToken)}async function yt(n,r){let i;try{i=mt(n,{cwd:process.cwd(),env:process.env})}catch(t){x({level:`error`,phase:`cli_parse_failed`,data:{error:t instanceof Error?t.message:String(t)}});let n=e(t,{diagnosticId:b().diagnosticId,logPath:y({force:!0})??void 0});return r.jsonRequested?f({success:!1,error:n}):d(n,{showDetails:r.debugEnabled}),E(1)}for(let e of i.warnings)process.stderr.write(`Warning: ${e}\n`);if(i.flags.version)return process.stdout.write(`${r.version}\n`),E(0);let a=i.command===`help`,o=i.flags.help;if(a||o){if(a&&i.positionals.length>1)return d(new t(`INVALID_ARGS`,`help accepts at most one command.`)),E(1);let e=a?i.positionals[0]:i.command;if(!e)return process.stdout.write(`${await z()}\n`),E(0);let n=await Fe(e);return n?(process.stdout.write(n),E(0)):(d(new t(`INVALID_ARGS`,kt(e))),process.stdout.write(`${await z()}\n`),E(1))}return i.command?{parsed:i,command:i.command,positionals:i.positionals}:(process.stdout.write(`${await z()}\n`),E(1))}async function bt(t,r){let i=new Set(t.providedFlags.map(e=>e.key));try{let e=G({policyOverrides:t.flags,configuredPlatform:t.flags.platform,configuredSession:t.flags.session}),n=e.lockPolicy?{...t.flags}:W(t.flags,{policyOverrides:t.flags,configuredPlatform:t.flags.platform,configuredSession:t.flags.session}),a=h(n.stateDir),o=n.session??`default`,s=Mt({command:r.command,explicitFlagKeys:i,stateDir:a.baseDir,session:o,remoteConfig:n.remoteConfig,hasResolvedSession:n.session!==void 0}),c=s?Lt(n,s.flags,i):n;return{...r,binding:e,flags:n,daemonPaths:a,sessionName:o,connectionDefaults:s,explicitFlagKeys:i,effectiveFlags:c,resolvedRuntime:s?.runtime,connectionMetadata:s?.connection,parsedBatchSteps:void 0}}catch(i){let a=e(n(i),{diagnosticId:b().diagnosticId,logPath:y({force:!0})??void 0});return t.flags.json?f({success:!1,error:a}):d(a,{showDetails:r.debugOutputEnabled}),E(1)}}async function xt(e,t){let{daemonAuthToken:n,...i}=e.effectiveFlags;return await Ke(e.positionals,{flags:{...i,leaseProvider:e.connectionDefaults?.connection?.leaseProvider},stateDir:e.daemonPaths.baseDir,session:e.effectiveFlags.session??e.sessionName,cwd:process.cwd(),env:process.env,configureDirectPortReverse:async()=>{let a=await t.sendToDaemon({command:l.runtime,positionals:[`port-reverse`],flags:{...i,leaseProvider:e.connectionDefaults?.connection?.leaseProvider,devicePort:8097,hostPort:8097,portReverseName:`react-devtools`},session:e.effectiveFlags.session??e.sessionName},{authToken:n});a.ok||r(a.error)}})}async function St(e,n){if(e.command===`batch`){if(e.positionals.length>0)throw new t(`INVALID_ARGS`,`batch does not accept positional arguments.`);e.parsedBatchSteps=At(e.flags)}if(Pt(e.command)&&(e.effectiveFlags=(await _e({command:e.command,flags:e.effectiveFlags,stateDir:e.daemonPaths.baseDir,env:process.env})).flags),e.effectiveFlags.remoteConfig&&Nt(e.command)){let t=p(J(e),{transport:Q(n.sendToDaemon)}),r=await me({command:e.command,flags:e.effectiveFlags,client:t,runtime:e.resolvedRuntime,positionals:e.positionals,batchSteps:e.parsedBatchSteps,forceRuntimePrepare:Rt(e.explicitFlagKeys)});e.effectiveFlags=r.flags,e.resolvedRuntime=r.runtime,e.connectionMetadata=r.connection}Ft({command:e.command,flags:e.effectiveFlags,runtime:e.resolvedRuntime,explicitFlagKeys:e.explicitFlagKeys,hadConnectionDefaults:!!e.connectionDefaults})&&process.stderr.write(`Warning: open is using explicit remote daemon or tenant flags without saved Metro runtime hints. React Native apps may launch without bundle/runtime hints; prefer connect --remote-config <path> first or pass --remote-config <path> on this command.
5
- `)}function J(e){let t=e.effectiveFlags,n=e.connectionMetadata;return{session:t.session,requestId:e.requestId,stateDir:t.stateDir,daemonBaseUrl:t.daemonBaseUrl,daemonAuthToken:t.daemonAuthToken,daemonTransport:t.daemonTransport,daemonServerMode:t.daemonServerMode,tenant:t.tenant,sessionIsolation:t.sessionIsolation,runId:t.runId,leaseId:t.leaseId,leaseBackend:t.leaseBackend,leaseProvider:n?.leaseProvider,clientId:n?.clientId,deviceKey:n?.deviceKey,providerApp:t.providerApp,providerOsVersion:t.providerOsVersion,providerProject:t.providerProject,providerBuild:t.providerBuild,providerSessionName:t.providerSessionName,providerDeviceOrientation:t.providerDeviceOrientation,providerGeoLocation:t.providerGeoLocation,providerTimezone:t.providerTimezone,providerLanguage:t.providerLanguage,providerLocale:t.providerLocale,providerNetworkProfile:t.providerNetworkProfile,providerCustomNetwork:t.providerCustomNetwork,providerNoResignApp:t.providerNoResignApp,awsProjectArn:t.awsProjectArn,awsDeviceArn:t.awsDeviceArn,awsAppArn:t.awsAppArn,awsRegion:t.awsRegion,awsInteractionMode:t.awsInteractionMode,runtime:e.resolvedRuntime,lockPolicy:e.binding.lockPolicy,lockPlatform:e.binding.defaultPlatform,cwd:process.cwd(),debug:e.debugOutputEnabled,cost:t.cost,responseLevel:t.responseLevel}}function Ct(e){let t=e.effectiveFlags.daemonBaseUrl;return e.debugOutputEnabled&&!e.effectiveFlags.json&&!t?Vt(e.daemonPaths.logPath):null}async function wt(e){if(e.command!==`test`)return;let{createReplayTestReporterRuntime:t}=await import(`./reporting.js`);return t({debug:e.debugOutputEnabled,verbose:e.effectiveFlags.verbose,json:e.effectiveFlags.json,reporter:e.effectiveFlags.reporter,reportJunit:e.effectiveFlags.reportJunit})}async function Tt(e,n,r){let{command:i,positionals:a,effectiveFlags:o}=e;if(i===`batch`){if(!e.parsedBatchSteps)throw new t(`INVALID_ARGS`,`batch requires --steps or --steps-file.`);let s=e.parsedBatchSteps.map((t,n)=>({...t,input:e.binding.lockPolicy&&e.flags.platform===void 0?{...t.input}:W(t.input,{policyOverrides:o,configuredPlatform:o.platform,configuredSession:o.session,inheritedPlatform:o.platform})}));if(await V({command:i,positionals:a,flags:{...o,batchSteps:s},client:n,debug:e.debugOutputEnabled,replayTestReporterRuntime:r}))return}else if(i===`runtime`)throw new t(`INVALID_ARGS`,`runtime command was removed. Use connect --remote-config <path> for remote runs, or metro prepare --remote-config <path> for inspection.`);else if(await V({command:i,positionals:a,flags:o,client:n,debug:e.debugOutputEnabled,replayTestReporterRuntime:r}))return;throw new t(`INVALID_ARGS`,X(i))}async function Et(t,r,i){let a=n(t),o=e(a,{diagnosticId:b().diagnosticId,logPath:y({force:!0})??void 0});if(r.command===`close`&&jt(a)){r.effectiveFlags.json&&f({success:!0,data:{closed:`session`,source:`no-daemon`}});return}r.effectiveFlags.json?f({success:!1,error:o}):(d(o,{showDetails:r.debugOutputEnabled}),r.debugOutputEnabled&&Dt(r.daemonPaths.logPath)),i&&i(),await E(1)}const Y=64e3;function Dt(e){try{if(D.existsSync(e)){let t=D.readFileSync(e,`utf8`).split(`
1
+ import{C as e,T as t,_ as n,v as r}from"./sdk-contracts.js";import{_ as i}from"./location-coordinates.js";import{kr as a,l as o}from"./sdk-batch-runner.js";import{mt as s,pt as c}from"./prepare-kind.js";import{n as l,r as u}from"./agent-device-client.js";import{_ as d}from"./screenshot-result.js";import{i as f,r as p,t as m}from"./command-catalog.js";import{S as h,x as g}from"./registry.js";import{n as _}from"./command-metadata.js";import{A as v,I as ee,M as y,N as te,j as b,k as ne,x as re}from"./sdk-android-adb.js";import{n as ie}from"./version.js";import{n as ae,t as x}from"./internal/bin.js";import{i as oe,n as S,t as se}from"./command-schema.js";import{t as ce}from"./update-check.js";import{n as C,r as w,t as le}from"./cli-config.js";import{exitAfterFlush as T}from"./process-exit.js";import{D as ue,E as de,S as fe,T as pe,h as me,n as he,w as ge}from"./connection-runtime.js";import{t as _e}from"./cli-grammar.js";import{i as ve}from"./auth-session.js";import E from"node:fs";import{pathToFileURL as ye}from"node:url";const D=`open <app> --relaunch`,O={start:{command:`open`,example:D},restart:{command:`open`,example:D},touch:{command:`press`,example:`press`},input:{command:`fill`,example:`fill`},settext:{command:`fill`,example:`fill`},entertext:{command:`fill`,example:`fill`},screencap:{command:`screenshot`,example:`screenshot`},capture:{command:`screenshot`,example:`screenshot`},dismiss:{command:`keyboard`,example:`keyboard dismiss`},"get-text":{command:`get`,example:`get text`},gettext:{command:`get`,example:`get text`},get_text:{command:`get`,example:`get text`},"open-url":{command:`open`,example:`open <url>`},"close-session":{command:`close`,example:`close`}};function k(e){if(e.length<=2)return[];let t=f(),n=t.filter(t=>t.startsWith(e));if(n.length>0)return n.sort((e,t)=>e.length-t.length||e.localeCompare(t)).slice(0,3);let r=A(e),i=t.map(t=>({name:t,distance:j(e,t)})).filter(e=>e.distance<=r);if(i.length===0)return[];let a=Math.min(...i.map(e=>e.distance));return i.filter(e=>e.distance===a).sort((e,t)=>e.name.localeCompare(t.name)).slice(0,3).map(e=>e.name)}function A(e){return e.length<4?1:e.length<=6?2:3}function j(e,t){return e===t?0:e.startsWith(t)||t.startsWith(e)?Math.abs(e.length-t.length):M(e,t)}function M(e,t){let n=e.length+1,r=t.length+1,i=Array.from({length:n},()=>Array(r).fill(0));for(let e=0;e<n;e+=1)i[e][0]=e;for(let e=0;e<r;e+=1)i[0][e]=e;for(let a=1;a<n;a+=1)for(let n=1;n<r;n+=1){let r=e[a-1]===t[n-1]?0:1;i[a][n]=Math.min(i[a-1][n]+1,i[a][n-1]+1,i[a-1][n-1]+r)}return i[n-1][r-1]}function N(e){let t=e.toLowerCase(),n=O[t]?.example;if(n)return n;let r=k(t);if(r.length!==0)return r.length===1?r[0]:`one of: ${r.join(`, `)}`}const P=new Set([`--bundle-id`,`--bundleid`,`--bundle`,`--package`,`--package-name`,`--packagename`,`--app-id`,`--appid`,`--pkg`]),be=new Set([`--session-locked`,`--session-lock-conflicts`]);function xe(e){return P.has(e.toLowerCase())?`Unknown flag: ${e}. The app or bundle id is a positional argument, e.g. ${D}.`:be.has(e.toLowerCase())?`Unknown flag: ${e}. Use --session-lock reject|strip instead.`:e.toLowerCase()===i.cliToken?`Unknown flag: ${e}. ${i.migration.screenshot}. ${i.migration.record}.`:`Unknown flag: ${e}`}function F(e){let t={json:!1,help:!1,version:!1},r=null,i=null,a=[],o=[],s=[],c=!0;for(let o=0;o<e.length;o+=1){let l=e[o];if(c&&l===`--`){c=!1;continue}if(!c){r?a.push(l):(i=l,r=B(l));continue}if(Te(r)){a.push(l);continue}let u=l.startsWith(`--`),d=l.startsWith(`-`)&&l.length>1;if(!u&&!d){r?a.push(l):(i=l,r=B(l));continue}let[f,p]=u?I(l):[l,void 0];if(Ce(r,f))continue;let m=Ee(f,r);if(we(r,m)){a.push(l);continue}if(!m){if(je(r,a,l)){r?a.push(l):r=l;continue}throw new n(`INVALID_ARGS`,xe(f))}let h=ke(m,f,p,e[o+1]);h.consumeNext&&(o+=1);let g=t[m.key];if(m.multiple){let e=Array.isArray(g)?[...g,h.value]:g===void 0?[h.value]:[g,h.value];t[m.key]=e}else t[m.key]=h.value;s.push({key:m.key,token:f})}return Se(i,t),{command:r,positionals:a,flags:t,warnings:o,providedFlags:s}}function Se(e,t){if(e)for(let n of x(e)?.impliedFlags??[])t[n]=!0}function Ce(e,t){return t===`-c`&&(e===`snapshot`||e===`diff`)}function we(e,t){return e===`react-devtools`?t?!C(t.key,e):!0:!1}function Te(e){return e===`cdp`}function Ee(e,t){let n=h().filter(t=>t.names.includes(e));if(n.length<=1)return n[0]??g(e);if(t){let e=n.find(e=>C(e.key,t));if(e)return e}return g(e)}function De(e,t){let r=t?.strictFlags??!0,i=[...e.warnings],a=w({json:!1,help:!1,version:!1},t?.defaultFlags??{});if(w(a,e.flags),e.command&&!p(e.command)&&!a.help){let t=N(e.command);throw new n(`INVALID_ARGS`,t?`Unknown command: ${e.command}. Did you mean ${t}?`:`Unknown command: ${e.command}`)}let o=e.providedFlags.filter(t=>!C(t.key,e.command));if(o.length>0){let t=o.map(e=>e.token),s=Pe(e.command,t);if(r)throw new n(`INVALID_ARGS`,s);i.push(s);for(let e of o)delete a[e.key]}for(let t of Object.keys(a))a[t]!==void 0&&(C(t,e.command)||delete a[t]);Oe(e),se(e.command,a);let s=Ne({command:e.command,positionals:e.positionals,flags:a,warnings:i});if(S(s.command,s.positionals),s.command===`batch`&&+!!a.steps+ +!!a.stepsFile!=1)throw new n(`INVALID_ARGS`,`batch requires exactly one step source: --steps or --steps-file.`);return s}function Oe(e){if(e.command!==`back`)return;let t=e.providedFlags.filter(e=>e.key===`backMode`);if(!(new Set(t.map(e=>e.token)).size<=1))throw new n(`INVALID_ARGS`,`back accepts only one explicit mode flag: use either --in-app or --system.`)}function I(e){let t=e.indexOf(`=`);return t===-1?[e,void 0]:[e.slice(0,t),e.slice(t+1)]}function ke(e,t,r,i){if(e.setValue!==void 0){if(r!==void 0)throw new n(`INVALID_ARGS`,`Flag ${t} does not take a value.`);return{value:e.setValue,consumeNext:!1}}if(e.type===`boolean`){if(r!==void 0)throw new n(`INVALID_ARGS`,`Flag ${t} does not take a value.`);return{value:!0,consumeNext:!1}}if(e.type===`booleanOrString`){if(r!==void 0){if(r.trim().length===0)throw new n(`INVALID_ARGS`,`Flag ${t} requires a non-empty value when provided.`);return{value:r,consumeNext:!1}}return i===void 0||R(i)?{value:!0,consumeNext:!1}:Ae(i)?{value:i,consumeNext:!0}:{value:!0,consumeNext:!1}}let a=r??i;if(a===void 0||r===void 0&&R(a))throw new n(`INVALID_ARGS`,`Flag ${t} requires a value.`);if(e.type===`string`)return{value:a,consumeNext:r===void 0};if(e.type===`enum`){if(!e.enumValues?.includes(a))throw new n(`INVALID_ARGS`,`Invalid ${L(t)}: ${a}`);return{value:a,consumeNext:r===void 0}}let o=Number(a);if(!Number.isFinite(o)||typeof e.min==`number`&&o<e.min||typeof e.max==`number`&&o>e.max)throw new n(`INVALID_ARGS`,`Invalid ${L(t)}: ${a}`);return{value:e.type===`int`?Math.floor(o):o,consumeNext:r===void 0}}function L(e){return e.replace(/^-+/,``)}function R(e){if(!e.startsWith(`-`)||e===`-`)return!1;let[t]=e.startsWith(`--`)?I(e):[e,void 0];return g(t)!==void 0}function Ae(e){let t=e.trim();return!t||/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(t)?!1:!!(t.startsWith(`./`)||t.startsWith(`../`)||t.startsWith(`~/`)||t.startsWith(`/`)||t.includes(`/`)||t.includes(`\\`))}function je(e,t,n){if(!Me(n)||!e)return!1;let r=oe(e);if(!r||r.allowsExtraPositionals)return!0;let i=r.positionalArgs??[];return i.length===0?!1:t.length<i.length?!0:i.some(e=>e.includes(`?`))}function Me(e){return/^-\d+(\.\d+)?$/.test(e)}function Ne(e){if(e.flags.help)return e;if(e.command===`snapshot`&&e.flags.snapshotDiff){let{snapshotDiff:t,...n}=e.flags;return{command:`diff`,positionals:[`snapshot`,...e.positionals],flags:n,warnings:e.warnings}}return e}function Pe(e,t){return e?t.length===1?`Flag ${t[0]} is not supported for command ${e}.`:`Flags ${t.join(`, `)} are not supported for command ${e}.`:t.length===1?`Flag ${t[0]} requires a command that supports it.`:`Flags ${t.join(`, `)} require a command that supports them.`}async function z(){let{buildUsageText:e}=await import(`./cli-help.js`);return e()}async function Fe(e){let{buildCommandUsageText:t}=await import(`./cli-help.js`);return t(B(e))}function B(e){if(e.toLowerCase()===`rotate`)throw new n(`INVALID_ARGS`,`rotate was renamed to orientation; for the two-finger gesture use: gesture rotate`);return ae(e)}function Ie(e){return _(e)}const Le={connect:async()=>(await import(`./connection.js`)).connectCommand,disconnect:async()=>(await import(`./connection.js`)).disconnectCommand,connection:async()=>(await import(`./connection.js`)).connectionCommand,auth:async()=>(await import(`./auth.js`)).authCommand,daemon:async()=>(await import(`./daemon.js`)).daemonCommand,device:async()=>(await import(`./device2.js`)).deviceCommand,proxy:async()=>(await import(`./proxy.js`)).proxyCommand,replay:async()=>(await import(`./replay.js`)).replayCommand,screenshot:async()=>(await import(`./screenshot.js`)).screenshotCommand,diff:async()=>(await import(`./screenshot.js`)).diffCommand};async function V(e){let t={...e.flags},n=Le[e.command];return n&&await(await n())({...e,flags:t})?!0:Ie(e.command)?await Re({...e,command:e.command,flags:t}):!1}async function Re(e){let{runGenericClientBackedCommand:t}=await import(`./generic.js`);return await t(e)}function ze(e){return[`exec`,`--yes`,`--package`,`agent-react-devtools@0.4.0`,`--`,`agent-react-devtools`,...e]}function Be(e){return e===`ios-instance`}function Ve(e){return e[0]===`wait`&&e.includes(`--connected`)}function He(e,t,n){n===0||!Ve(e)||!Be(t?.leaseBackend)||process.stderr.write([`Hint: Remote iOS React DevTools connects during JavaScript startup.`,"If the app was already open before `agent-device react-devtools start`, relaunch it with `agent-device open <bundle-id> --platform ios --relaunch`, then retry `agent-device react-devtools wait --connected`.",``].join(`
2
+ `))}function H(e,t,n){return n||(e.push(t),``)}function Ue(e){if(!e?.metroProxyBaseUrl||!pe(e.leaseBackend))return null;let t=[],r={serverBaseUrl:H(t,`metroProxyBaseUrl`,e.metroProxyBaseUrl),bearerToken:H(t,`metroBearerToken`,e.metroBearerToken),tenantId:H(t,`tenant`,e.tenant),runId:H(t,`runId`,e.runId),leaseId:H(t,`leaseId`,e.leaseId)};if(t.length>0)throw new n(`INVALID_ARGS`,`react-devtools remote bridge requires ${t.join(`, `)}.`,{missing:t});return r}async function We(e,t,n){let{flags:r}=t,i=Ue(r);if(!i)return n();let a=t.stateDir??process.cwd(),o=t.session??r?.session??`default`,s=r?.remoteConfig??`${i.tenantId}:${i.runId}:${i.leaseId}`;if(e[0]===`stop`)try{return await n()}finally{await ue({projectRoot:t.cwd??process.cwd(),stateDir:a,profileKey:s,consumerKey:o})}return await de({projectRoot:t.cwd??process.cwd(),stateDir:a,serverBaseUrl:i.serverBaseUrl,bearerToken:i.bearerToken,bridgeScope:{tenantId:i.tenantId,runId:i.runId,leaseId:i.leaseId},session:o,profileKey:s,consumerKey:o,env:t.env??process.env}),await n()}function Ge(e,t){if(e[0]!==`start`)return!1;let{flags:n}=t;return n?n.leaseProvider===`limrun`&&n.leaseBackend===`android-instance`&&n.metroProxyBaseUrl===void 0&&t.configureDirectPortReverse!==void 0:!1}async function Ke(e,t={}){let n=t.cwd??process.cwd(),r=t.env??process.env,i=await We(e,t,async()=>(Ge(e,t)&&await t.configureDirectPortReverse?.(),(await re(`npm`,ze(e),{cwd:n,env:r,allowFailure:!0,onStdoutChunk:e=>{process.stdout.write(e)},onStderrChunk:e=>{process.stderr.write(e)}})).exitCode));return He(e,t.flags,i),i}async function qe(e,t){switch(e[0]){case`setup`:{Je(t.flags.json);let{setupManagedAgentBrowser:e}=await import(`./agent-browser-tool.js`).then(e=>e.t),n=await e({stateDir:t.stateDir});return Ye(t.flags.json,n),0}case`doctor`:{let{doctorManagedAgentBrowser:e}=await import(`./agent-browser-tool.js`).then(e=>e.t),n=await e({stateDir:t.stateDir});return Xe(t.flags.json,n.exitCode===0?`Web backend is healthy.`:`Web backend doctor reported issues.`,n),n.exitCode}default:throw new n(`INVALID_ARGS`,`web requires setup or doctor`)}}function Je(e){e||process.stdout.write(`Setting up managed agent-browser backend (downloads if needed)...
3
+ `)}function Ye(e,t){if(e){s({success:!0,data:{status:U(t)}});return}process.stdout.write(`Managed web backend installed.\nagent-browser available at: ${t.binaryPath}\n`)}function Xe(e,t,n){if(e){s({success:!0,data:Ze(n)});return}process.stdout.write(`${t}\n`)}function U(e){let{socketDir:t,...n}=e;return n}function Ze(e){let t=e.status;return Qe(t)?{...e,status:U(t)}:e}function Qe(e){return typeof e==`object`&&!!e&&`socketDir`in e&&`installDir`in e&&`binaryPath`in e}function $e(e){let t;try{t=JSON.parse(e)}catch{throw new n(`INVALID_ARGS`,`Batch steps must be valid JSON.`)}if(!Array.isArray(t)||t.length===0)throw new n(`INVALID_ARGS`,`Batch steps must be a non-empty JSON array.`);return et(t)}function et(e){let t=!1,n=e.map((e,n)=>{if(nt(e))return rt(e,n+1);let r=it(e,n+1);return t=!0,tt(r)});return t&&process.stderr.write(`Warning: batch steps using positionals/flags are deprecated and will be removed in the next major version. Use {"command":"...","input":{...}} steps instead.
4
+ `),n}function tt(e){let t=_e(e.command,e.positionals??[],lt(e.flags));return{command:e.command,input:t,...e.runtime===void 0?{}:{runtime:e.runtime}}}function nt(e){return a(e)&&`input`in e&&!(`positionals`in e)&&!(`flags`in e)}function rt(e,t){let n=o(e.runtime,t),{runtime:r,...i}=e;return{...i,...n===void 0?{}:{runtime:n}}}function it(e,t){if(!a(e))throw new n(`INVALID_ARGS`,`Invalid batch step ${t}.`);ot(e,t);let r=at(e.command,t),i=st(e.positionals,t);S(r,i??[],`Batch step ${t}`);let s=ct(e.flags,t),c=o(e.runtime,t);return{command:r,...i===void 0?{}:{positionals:i},...s===void 0?{}:{flags:s},...c===void 0?{}:{runtime:c}}}function at(e,t){let r=typeof e==`string`?e.trim().toLowerCase():``;if(!r)throw new n(`INVALID_ARGS`,`Batch step ${t} requires command.`);if(_(r))return r;throw new n(`INVALID_ARGS`,`Batch step ${t} command is not available through command batch: ${String(e)}`)}function ot(e,t){let r=Object.keys(e).filter(e=>![`command`,`positionals`,`flags`,`runtime`].includes(e));if(r.length>0)throw new n(`INVALID_ARGS`,`Batch step ${t} has unknown legacy field(s): ${r.join(`, `)}.`)}function st(e,t){if(e!==void 0){if(!Array.isArray(e)||e.some(e=>typeof e!=`string`))throw new n(`INVALID_ARGS`,`Batch step ${t} positionals must contain only strings.`);return e}}function ct(e,t){if(e!==void 0){if(!a(e))throw new n(`INVALID_ARGS`,`Batch step ${t} flags must be an object.`);return e}}function lt(e){return{json:!1,help:!1,version:!1,...e}}function W(e,t={}){let n=G(t),r={...e};return n.defaultPlatform&&r.platform===void 0&&(r.platform=n.defaultPlatform),r}function G(e){let t=e.env??process.env,n=e.inheritedPlatform??e.configuredPlatform??dt(t.AGENT_DEVICE_PLATFORM),r=pt(e.configuredSession??t.AGENT_DEVICE_SESSION);return{defaultPlatform:n,lockPolicy:ut(e.policyOverrides,t,r)}}function ut(e,t,n){let r=e?.sessionLock??ft(t.AGENT_DEVICE_SESSION_LOCK);if(r)return r;if(n)return`reject`}function dt(e){if(e===void 0)return;let t=e.trim().toLowerCase();if(t){if(t===`ios`||t===`android`||t===`apple`)return t;throw new n(`INVALID_ARGS`,`Invalid AGENT_DEVICE_PLATFORM: ${e}. Use ios, android, or apple.`)}}function ft(e){if(e===void 0)return;let t=e.trim().toLowerCase();if(t){if(t===`reject`||t===`strip`)return t;throw new n(`INVALID_ARGS`,`Invalid session lock mode: ${e}. Use reject or strip.`)}}function pt(e){return typeof e==`string`&&e.trim().length>0}function mt(e,t){let n=F(e),r=t?.env??process.env,i=t?.cwd??process.cwd(),a=ht(n.command)?me({remoteConfig:n.flags.remoteConfig,cwd:i,env:r}):{},o=w(le({command:n.command,cwd:i,cliFlags:n.flags,env:r}),a);return{...De(n,{strictFlags:t?.strictFlags,defaultFlags:o}),providedFlags:n.providedFlags}}function ht(e){return e!==null}m.runtime;async function gt(e){let{command:t,request:n,transport:r,transportOptions:i}=e;return await r({...n,command:t},i)}const _t={sendToDaemon:u},vt=new Set([`launchUrl`,`kind`,`metroBearerToken`,`metroKind`,`metroListenHost`,`metroNoInstallDeps`,`metroNoReuseExisting`,`metroPreparePort`,`metroProbeTimeoutMs`,`metroProjectRoot`,`metroProxyBaseUrl`,`metroPublicBaseUrl`,`metroRuntimeFile`,`metroStartupTimeoutMs`,`metroStatusHost`]),yt=new Set([`connect`,`connection`,`close`,`daemon`,`device`,`disconnect`,`metro`,`proxy`,`session`]);async function K(e,t=_t){let n=ne(),r=ie(),i=kt(e),a=e.includes(`--json`);await ee({session:Vt(e)??process.env.AGENT_DEVICE_SESSION??`default`,requestId:n,command:e[0],debug:i},async()=>{let{parsed:o,command:s,positionals:c}=await bt(e,{version:r,jsonRequested:a,debugEnabled:i}),u=X(s,o.providedFlags),d=await xt(o,{command:s,positionals:c,requestId:n,debugOutputEnabled:u});q(d.effectiveFlags);let f=null;try{if(s===`react-devtools`){await T(await St(d,t));return}if(s===`web`){await T(await qe(c,{flags:d.effectiveFlags,stateDir:d.daemonPaths.baseDir}));return}if(ce({command:s,currentVersion:r,stateDir:d.daemonPaths.baseDir,flags:d.effectiveFlags}),await Ct(d,t),q(d.effectiveFlags),s===`cdp`){await T(await ge(c,{flags:d.effectiveFlags,runtime:d.resolvedRuntime,cwd:process.cwd(),env:process.env}));return}f=wt(d);let e=await Tt(d);await Et(d,l(J(d),{transport:Bt({command:s,flags:d.effectiveFlags,replayTestReporterRuntime:e,transport:t.sendToDaemon})}),e)}catch(e){await Dt(e,d,f)}finally{f&&f()}})}function q(e){e.daemonAuthToken&&te(e.daemonAuthToken)}async function bt(t,r){let i;try{i=mt(t,{cwd:process.cwd(),env:process.env})}catch(t){v({level:`error`,phase:`cli_parse_failed`,data:{error:t instanceof Error?t.message:String(t)}});let n=e(t,{diagnosticId:y().diagnosticId,logPath:b({force:!0})??void 0});return r.jsonRequested?s({success:!1,error:n}):c(n,{showDetails:r.debugEnabled}),T(1)}for(let e of i.warnings)process.stderr.write(`Warning: ${e}\n`);if(i.flags.version)return process.stdout.write(`${r.version}\n`),T(0);let a=i.command===`help`,o=i.flags.help;if(a||o){if(a&&i.positionals.length>1)return c(new n(`INVALID_ARGS`,`help accepts at most one command.`)),T(1);let e=a?i.positionals[0]:i.command;if(!e)return process.stdout.write(`${await z()}\n`),T(0);let t=await Fe(e);return t?(process.stdout.write(t),T(0)):(c(new n(`INVALID_ARGS`,At(e))),process.stdout.write(`${await z()}\n`),T(1))}return i.command?{parsed:i,command:i.command,positionals:i.positionals}:(process.stdout.write(`${await z()}\n`),T(1))}async function xt(t,n){let i=new Set(t.providedFlags.map(e=>e.key));try{let e=G({policyOverrides:t.flags,configuredPlatform:t.flags.platform,configuredSession:t.flags.session}),r=e.lockPolicy?{...t.flags}:W(t.flags,{policyOverrides:t.flags,configuredPlatform:t.flags.platform,configuredSession:t.flags.session}),a=d(r.stateDir),o=r.session??`default`,s=Pt({command:n.command,explicitFlagKeys:i,stateDir:a.baseDir,session:o,remoteConfig:r.remoteConfig,hasResolvedSession:r.session!==void 0}),c=s?Rt(r,s.flags,i):r;return{...n,binding:e,flags:r,daemonPaths:a,sessionName:o,connectionDefaults:s,explicitFlagKeys:i,effectiveFlags:c,resolvedRuntime:s?.runtime,connectionMetadata:s?.connection,parsedBatchSteps:void 0}}catch(i){let a=e(r(i),{diagnosticId:y().diagnosticId,logPath:b({force:!0})??void 0});return t.flags.json?s({success:!1,error:a}):c(a,{showDetails:n.debugOutputEnabled}),T(1)}}async function St(e,n){let{daemonAuthToken:r,...i}=e.effectiveFlags;return await Ke(e.positionals,{flags:{...i,leaseProvider:e.connectionDefaults?.connection?.leaseProvider},stateDir:e.daemonPaths.baseDir,session:e.effectiveFlags.session??e.sessionName,cwd:process.cwd(),env:process.env,configureDirectPortReverse:async()=>{let a=await gt({route:`react-devtools`,command:m.runtime,request:{positionals:[`port-reverse`],flags:{...i,leaseProvider:e.connectionDefaults?.connection?.leaseProvider,devicePort:8097,hostPort:8097,portReverseName:`react-devtools`},session:e.effectiveFlags.session??e.sessionName},transport:n.sendToDaemon,transportOptions:{authToken:r}});a.ok||t(a.error)}})}async function Ct(e,t){if(e.command===`batch`){if(e.positionals.length>0)throw new n(`INVALID_ARGS`,`batch does not accept positional arguments.`);e.parsedBatchSteps=Mt(e.flags)}if(Ft(e.command)&&(e.effectiveFlags=(await ve({command:e.command,flags:e.effectiveFlags,stateDir:e.daemonPaths.baseDir,env:process.env})).flags),e.effectiveFlags.remoteConfig&&Z(e.command)){let n=l(J(e),{transport:Q(t.sendToDaemon)}),r=await he({command:e.command,flags:e.effectiveFlags,client:n,runtime:e.resolvedRuntime,positionals:e.positionals,batchSteps:e.parsedBatchSteps,forceRuntimePrepare:zt(e.explicitFlagKeys)});e.effectiveFlags=r.flags,e.resolvedRuntime=r.runtime,e.connectionMetadata=r.connection}It({command:e.command,flags:e.effectiveFlags,runtime:e.resolvedRuntime,explicitFlagKeys:e.explicitFlagKeys,hadConnectionDefaults:!!e.connectionDefaults})&&process.stderr.write(`Warning: open is using explicit remote daemon or tenant flags without saved Metro runtime hints. React Native apps may launch without bundle/runtime hints; prefer connect --remote-config <path> first or pass --remote-config <path> on this command.
5
+ `)}function J(e){let t=e.effectiveFlags,n=e.connectionMetadata;return{session:t.session,requestId:e.requestId,stateDir:t.stateDir,daemonBaseUrl:t.daemonBaseUrl,daemonAuthToken:t.daemonAuthToken,daemonTransport:t.daemonTransport,daemonServerMode:t.daemonServerMode,tenant:t.tenant,sessionIsolation:t.sessionIsolation,runId:t.runId,leaseId:t.leaseId,leaseBackend:t.leaseBackend,leaseProvider:n?.leaseProvider,clientId:n?.clientId,deviceKey:n?.deviceKey,providerApp:t.providerApp,providerOsVersion:t.providerOsVersion,providerProject:t.providerProject,providerBuild:t.providerBuild,providerSessionName:t.providerSessionName,providerDeviceOrientation:t.providerDeviceOrientation,providerGeoLocation:t.providerGeoLocation,providerTimezone:t.providerTimezone,providerLanguage:t.providerLanguage,providerLocale:t.providerLocale,providerNetworkProfile:t.providerNetworkProfile,providerCustomNetwork:t.providerCustomNetwork,providerNoResignApp:t.providerNoResignApp,awsProjectArn:t.awsProjectArn,awsDeviceArn:t.awsDeviceArn,awsAppArn:t.awsAppArn,awsRegion:t.awsRegion,awsInteractionMode:t.awsInteractionMode,runtime:e.resolvedRuntime,lockPolicy:e.binding.lockPolicy,lockPlatform:e.binding.defaultPlatform,cwd:process.cwd(),debug:e.debugOutputEnabled,cost:t.cost,responseLevel:t.responseLevel}}function wt(e){let t=e.effectiveFlags.daemonBaseUrl;return e.debugOutputEnabled&&!e.effectiveFlags.json&&!t?Ht(e.daemonPaths.logPath):null}async function Tt(e){if(e.command!==`test`)return;let{createReplayTestReporterRuntime:t}=await import(`./reporting.js`);return t({debug:e.debugOutputEnabled,verbose:e.effectiveFlags.verbose,json:e.effectiveFlags.json,reporter:e.effectiveFlags.reporter,reportJunit:e.effectiveFlags.reportJunit})}async function Et(e,t,r){let{command:i,positionals:a,effectiveFlags:o}=e;if(i===`batch`){if(!e.parsedBatchSteps)throw new n(`INVALID_ARGS`,`batch requires --steps or --steps-file.`);let s=e.parsedBatchSteps.map((t,n)=>({...t,input:e.binding.lockPolicy&&e.flags.platform===void 0?{...t.input}:W(t.input,{policyOverrides:o,configuredPlatform:o.platform,configuredSession:o.session,inheritedPlatform:o.platform})}));if(await V({command:i,positionals:a,flags:{...o,batchSteps:s},client:t,debug:e.debugOutputEnabled,replayTestReporterRuntime:r}))return}else if(i===`runtime`)throw new n(`INVALID_ARGS`,`runtime command was removed. Use connect --remote-config <path> for remote runs, or metro prepare --remote-config <path> for inspection.`);else if(await V({command:i,positionals:a,flags:o,client:t,debug:e.debugOutputEnabled,replayTestReporterRuntime:r}))return;throw new n(`INVALID_ARGS`,jt(i))}async function Dt(t,n,i){let a=r(t),o=e(a,{diagnosticId:y().diagnosticId,logPath:b({force:!0})??void 0});if(n.command===`close`&&Nt(a)){n.effectiveFlags.json&&s({success:!0,data:{closed:`session`,source:`no-daemon`}});return}n.effectiveFlags.json?s({success:!1,error:o}):(c(o,{showDetails:n.debugOutputEnabled}),n.debugOutputEnabled&&Ot(n.daemonPaths.logPath)),i&&i(),await T(1)}const Y=64e3;function Ot(e){try{if(E.existsSync(e)){let t=E.readFileSync(e,`utf8`).split(`
6
6
  `),n=t.slice(Math.max(0,t.length-200)).join(`
7
- `);n.length>Y&&(n=n.slice(n.length-Y)),n.trim().length>0&&process.stderr.write(`\n[daemon log]\n${n}\n`)}}catch{}}function Ot(e){try{let t=F(e);return Z(t.command??``,t.providedFlags)}catch{return e.includes(`--debug`)||e.includes(`-v`)||e.includes(`--verbose`)}}function kt(e){let t=N(e);return t?`Unknown command: ${e}. Did you mean ${t}?`:`Unknown command: ${e}`}function X(e){return u(e)?(x({level:`error`,phase:`cli_known_command_unhandled`,data:{command:e}}),`Command is registered but no CLI handler accepted it: ${e}`):`Unknown command: ${e}`}function Z(e,t){return t.some(t=>t.key===`verbose`&&(t.token===`--debug`||t.token===`-v`||e!==`test`))}function At(e){let n=``;if(e.steps)n=e.steps;else if(e.stepsFile)try{n=D.readFileSync(e.stepsFile,`utf8`)}catch(n){let r=n instanceof Error?n.message:String(n);throw new t(`INVALID_ARGS`,`Failed to read --steps-file ${e.stepsFile}: ${r}`)}return $e(n)}function jt(e){return e.code===`COMMAND_FAILED`?e.details?.kind===`daemon_startup_failed`?!0:e.message.toLowerCase().includes(`failed to start daemon`)?typeof e.details?.infoPath==`string`||typeof e.details?.lockPath==`string`:!1:!1}function Mt(e){return e.command===`connect`||e.command===`connection`||e.command===`daemon`||e.command===`proxy`?null:de({stateDir:e.stateDir,session:e.session,remoteConfig:e.remoteConfig,cwd:process.cwd(),env:process.env,allowActiveFallback:!e.explicitFlagKeys.has(`session`)&&(!e.remoteConfig||e.command===`disconnect`||!e.hasResolvedSession),validateRemoteConfigHash:e.command!==`disconnect`})}function Nt(e){return!vt.has(e)}function Pt(e){return e!==`auth`&&e!==`connection`&&e!==`daemon`&&e!==`device`&&e!==`proxy`}function Ft(e){return e.command!==`open`||e.runtime||e.flags.bundleUrl||e.flags.metroHost||e.flags.metroPort||e.flags.remoteConfig||e.hadConnectionDefaults?!1:It(e.explicitFlagKeys)}function It(e){return e.has(`daemonBaseUrl`)||e.has(`daemonTransport`)||e.has(`tenant`)||e.has(`sessionIsolation`)||e.has(`runId`)||e.has(`leaseId`)||e.has(`leaseBackend`)}function Lt(e,t,n){let r={...e};for(let[e,i]of Object.entries(t))i!==void 0&&(n.has(e)||(r[e]=i));return r}function Rt(e){for(let t of _t)if(e.has(t))return!0;return!1}function zt(e){let{command:t,flags:n,replayTestReporterRuntime:r,transport:i}=e;return n.json?Q(i):async(e,n)=>{let a=t===`test`&&r?{...n,onProgress:r.onProgress}:n;return await $(i,{...e,meta:{...e.meta,requestProgress:t===`test`?`replay-test`:`command`}},a)}}function Q(e){return async(t,n)=>await $(e,t,n)}async function $(e,t,n){return await e(t,n)}function Bt(e){for(let t=0;t<e.length;t+=1){let n=e[t];if(n.startsWith(`--session=`)){let e=n.slice(10).trim();return e.length>0?e:null}if(n===`--session`){let n=e[t+1]?.trim();return n&&!n.startsWith(`-`)?n:null}}return null}ve(process.argv[1]??``).href===import.meta.url&&K(process.argv.slice(2)).catch(async t=>{d(e(n(t)),{showDetails:!0}),await E(1)});function Vt(e){try{let t=D.existsSync(e)?D.statSync(e).size:0,n=!1,r=setInterval(()=>{if(!n&&D.existsSync(e))try{let n=D.statSync(e);if(n.size<t&&(t=0),n.size<=t)return;let r=D.openSync(e,`r`);try{let e=Buffer.alloc(n.size-t);D.readSync(r,e,0,e.length,t),t=n.size,e.length>0&&process.stdout.write(e.toString(`utf8`))}finally{D.closeSync(r)}}catch{}},200);return()=>{n=!0,clearInterval(r)}}catch{return null}}export{K as runCli};
7
+ `);n.length>Y&&(n=n.slice(n.length-Y)),n.trim().length>0&&process.stderr.write(`\n[daemon log]\n${n}\n`)}}catch{}}function kt(e){try{let t=F(e);return X(t.command??``,t.providedFlags)}catch{return e.includes(`--debug`)||e.includes(`-v`)||e.includes(`--verbose`)}}function At(e){let t=N(e);return t?`Unknown command: ${e}. Did you mean ${t}?`:`Unknown command: ${e}`}function jt(e){return p(e)?(v({level:`error`,phase:`cli_known_command_unhandled`,data:{command:e}}),`Command is registered but no CLI handler accepted it: ${e}`):`Unknown command: ${e}`}function X(e,t){return t.some(t=>t.key===`verbose`&&(t.token===`--debug`||t.token===`-v`||e!==`test`))}function Mt(e){let t=``;if(e.steps)t=e.steps;else if(e.stepsFile)try{t=E.readFileSync(e.stepsFile,`utf8`)}catch(t){let r=t instanceof Error?t.message:String(t);throw new n(`INVALID_ARGS`,`Failed to read --steps-file ${e.stepsFile}: ${r}`)}return $e(t)}function Nt(e){return e.code===`COMMAND_FAILED`?e.details?.kind===`daemon_startup_failed`?!0:e.message.toLowerCase().includes(`failed to start daemon`)?typeof e.details?.infoPath==`string`||typeof e.details?.lockPath==`string`:!1:!1}function Pt(e){return e.command===`connect`||e.command===`connection`||e.command===`daemon`||e.command===`proxy`?null:fe({stateDir:e.stateDir,session:e.session,remoteConfig:e.remoteConfig,cwd:process.cwd(),env:process.env,allowActiveFallback:!e.explicitFlagKeys.has(`session`)&&(!e.remoteConfig||e.command===`disconnect`||!e.hasResolvedSession),validateRemoteConfigHash:e.command!==`disconnect`})}function Z(e){return!yt.has(e)}function Ft(e){return e!==`auth`&&e!==`connection`&&e!==`daemon`&&e!==`device`&&e!==`proxy`}function It(e){return e.command!==`open`||e.runtime||e.flags.bundleUrl||e.flags.metroHost||e.flags.metroPort||e.flags.remoteConfig||e.hadConnectionDefaults?!1:Lt(e.explicitFlagKeys)}function Lt(e){return e.has(`daemonBaseUrl`)||e.has(`daemonTransport`)||e.has(`tenant`)||e.has(`sessionIsolation`)||e.has(`runId`)||e.has(`leaseId`)||e.has(`leaseBackend`)}function Rt(e,t,n){let r={...e};for(let[e,i]of Object.entries(t))i!==void 0&&(n.has(e)||(r[e]=i));return r}function zt(e){for(let t of vt)if(e.has(t))return!0;return!1}function Bt(e){let{command:t,flags:n,replayTestReporterRuntime:r,transport:i}=e;return n.json?Q(i):async(e,n)=>{let a=t===`test`&&r?{...n,onProgress:r.onProgress}:n;return await $(i,{...e,meta:{...e.meta,requestProgress:t===`test`?`replay-test`:`command`}},a)}}function Q(e){return async(t,n)=>await $(e,t,n)}async function $(e,t,n){return await e(t,n)}function Vt(e){for(let t=0;t<e.length;t+=1){let n=e[t];if(n.startsWith(`--session=`)){let e=n.slice(10).trim();return e.length>0?e:null}if(n===`--session`){let n=e[t+1]?.trim();return n&&!n.startsWith(`-`)?n:null}}return null}ye(process.argv[1]??``).href===import.meta.url&&K(process.argv.slice(2)).catch(async t=>{c(e(r(t)),{showDetails:!0}),await T(1)});function Ht(e){try{let t=E.existsSync(e)?E.statSync(e).size:0,n=!1,r=setInterval(()=>{if(!n&&E.existsSync(e))try{let n=E.statSync(e);if(n.size<t&&(t=0),n.size<=t)return;let r=E.openSync(e,`r`);try{let e=Buffer.alloc(n.size-t);E.readSync(r,e,0,e.length,t),t=n.size,e.length>0&&process.stdout.write(e.toString(`utf8`))}finally{E.closeSync(r)}}catch{}},200);return()=>{n=!0,clearInterval(r)}}catch{return null}}export{K as runCli};
@@ -1 +1 @@
1
- import{_ as e,v as t}from"./sdk-contracts.js";import{C as n,b as r}from"./sdk-android-adb.js";import{n as i}from"./path-resolution.js";import{t as a}from"./timeouts.js";import{c as o,d as s,n as c,s as l}from"./host-process.js";import{a as u,c as d,d as f,f as ee,g as te,h as p,i as ne,l as re,m as ie,n as m,o as h,p as g,s as ae,t as oe,u as se}from"./url.js";import{a as ce,i as le,t as ue}from"./runtime-transport.js";import _ from"node:path";import{createHash as de}from"node:crypto";import v from"node:fs";import{fileURLToPath as fe}from"node:url";const y=`companion-tunnel`;function b(e){return de(`sha256`).update(e).digest(`hex`)}function x(e){return e?.trim()?e.trim():void 0}function pe(e){if(!e||typeof e!=`object`||Array.isArray(e))return;let t=e;if(!(typeof t.tenantId!=`string`||typeof t.runId!=`string`||typeof t.leaseId!=`string`))return{tenantId:t.tenantId,runId:t.runId,leaseId:t.leaseId}}function me(e,t){return e.tenantId===t.tenantId&&e.runId===t.runId&&e.leaseId===t.leaseId}function S(e,t,n,r){let i=r??_.join(e,`.agent-device`);if(!n)return{statePath:_.join(i,`${t.slug}.json`),logPath:_.join(i,`${t.slug}.log`)};let a=b(n).slice(0,12),o=_.join(i,t.slug);return{statePath:_.join(o,`${t.slug}-${a}.json`),logPath:_.join(o,`${t.slug}-${a}.log`)}}function C(e){try{let t=JSON.parse(v.readFileSync(e,`utf8`));if(!Number.isInteger(t.pid)||Number(t.pid)<=0||typeof t.serverBaseUrl!=`string`||typeof t.localBaseUrl!=`string`||typeof t.tokenHash!=`string`||t.tokenHash.length===0)return null;let n=Array.isArray(t.consumers)?t.consumers.filter(e=>typeof e==`string`&&e.length>0):[];return{pid:Number(t.pid),startTime:typeof t.startTime==`string`?t.startTime:void 0,command:typeof t.command==`string`?t.command:void 0,serverBaseUrl:t.serverBaseUrl,localBaseUrl:t.localBaseUrl,launchUrl:x(typeof t.launchUrl==`string`?t.launchUrl:void 0),registerPath:x(typeof t.registerPath==`string`?t.registerPath:void 0),unregisterPath:x(typeof t.unregisterPath==`string`?t.unregisterPath:void 0),devicePort:Number.isInteger(t.devicePort)?Number(t.devicePort):void 0,session:x(typeof t.session==`string`?t.session:void 0),bridgeScope:pe(t.bridgeScope),tokenHash:t.tokenHash,consumers:n}}catch{return null}}function w(e,t){v.mkdirSync(_.dirname(e),{recursive:!0}),v.writeFileSync(e,`${JSON.stringify(t,null,2)}\n`,`utf8`)}function he(e){v.mkdirSync(_.dirname(e),{recursive:!0}),v.closeSync(v.openSync(e,`a`))}function ge(e){try{v.unlinkSync(e)}catch{}}function _e(e){try{v.unlinkSync(e)}catch{}}function T(e){try{v.readdirSync(e).length===0&&v.rmdirSync(e)}catch{}}function E(e,t){let n=_.dirname(e.statePath),r=_.dirname(e.logPath);ge(e.statePath),_e(e.logPath),T(n),r!==n&&T(r),_.basename(n)===t.slug&&T(_.dirname(n))}function D(e,t){return e.includes(t.runArg)}function ve(e,t){if(!c(e.pid))return!1;if(e.startTime){let t=o(e.pid);if(!t||t!==e.startTime)return!1}let n=l(e.pid);return!n||!D(n,t.definition)||!e.bridgeScope?!1:e.serverBaseUrl===m(t.serverBaseUrl)&&e.localBaseUrl===m(t.localBaseUrl)&&e.launchUrl===x(t.launchUrl)&&e.registerPath===x(t.registerPath)&&e.unregisterPath===x(t.unregisterPath)&&e.devicePort===t.devicePort&&e.session===x(t.session)&&me(e.bridgeScope,t.bridgeScope)&&e.tokenHash===b(t.bearerToken)}function O(e){return x(e.consumerKey)??x(e.profileKey)??null}function k(e,t){return!t||e.consumers.includes(t)?e:{...e,consumers:[...e.consumers,t]}}function ye(e,t){return t?{...e,consumers:e.consumers.filter(e=>e!==t)}:{...e,consumers:[]}}async function A(e,t){if(!c(e.pid))return;let n=l(e.pid);if(!(!n||!D(n,t))){try{process.kill(e.pid,`SIGTERM`)}catch(e){let t=e.code;if(t===`ESRCH`||t===`EPERM`)return;throw e}if(!await s(e.pid,1e3)){try{process.kill(e.pid,`SIGKILL`)}catch(e){let t=e.code;if(t===`ESRCH`||t===`EPERM`)return;throw e}await s(e.pid,1e3)}}}function be(e,t){let n={...t};return n[ee]=m(e.serverBaseUrl),n[ne]=e.bearerToken,n[ae]=m(e.localBaseUrl),n[ie]=S(e.projectRoot,e.definition,e.profileKey,e.stateDir).statePath,n[f]=e.bridgeScope.tenantId,n[se]=e.bridgeScope.runId,n[re]=e.bridgeScope.leaseId,e.launchUrl?.trim()?n[h]=e.launchUrl.trim():delete n[h],e.registerPath?.trim()?n[d]=e.registerPath.trim():delete n[d],e.unregisterPath?.trim()?n[p]=e.unregisterPath.trim():delete n[p],e.devicePort===void 0?delete n[u]:n[u]=String(e.devicePort),e.session?.trim()?n[g]=e.session.trim():delete n[g],n}function xe(e){let t=fe(import.meta.url),n=_.extname(t)||`.js`,r=[_.join(_.dirname(t),`${y}${n}`),_.join(_.dirname(t),`internal`,`${y}${n}`)].find(e=>v.existsSync(e));if(!r)throw Error(`${e.displayName} entrypoint not found. Rebuild the package to include the companion worker entry.`);return r}function Se(e,t){let n=xe(e.definition),i=n.endsWith(`.ts`)?[`--experimental-strip-types`]:[];v.mkdirSync(_.dirname(t),{recursive:!0});let a=v.openSync(t,`a`),s=0;try{s=r(process.execPath,[...i,n,e.definition.runArg],{env:be(e,e.env??process.env),stdio:[`ignore`,a,a]})}finally{v.closeSync(a)}if(!Number.isInteger(s)||s<=0)throw Error(`Failed to start ${e.definition.displayName} process.`);return{pid:s,startTime:o(s)??void 0,command:l(s)??void 0,serverBaseUrl:m(e.serverBaseUrl),localBaseUrl:m(e.localBaseUrl),launchUrl:x(e.launchUrl),registerPath:x(e.registerPath),unregisterPath:x(e.unregisterPath),devicePort:e.devicePort,session:x(e.session),bridgeScope:e.bridgeScope,tokenHash:b(e.bearerToken),consumers:[]}}async function j(e){let t=O(e),n=S(e.projectRoot,e.definition,e.profileKey,e.stateDir),r=C(n.statePath);if(r&&ve(r,e)){let e=k(r,t);return e!==r&&w(n.statePath,e),{pid:r.pid,spawned:!1,statePath:n.statePath,logPath:n.logPath}}r&&(await A(r,e.definition),E(n,e.definition)),he(n.statePath);let i;try{i=Se(e,n.logPath),w(n.statePath,k(i,t))}catch(t){throw i&&await A(i,e.definition).catch(()=>{}),E(n,e.definition),t}return{pid:i.pid,spawned:!0,statePath:n.statePath,logPath:n.logPath}}async function M(e){let t=O(e),n=S(e.projectRoot,e.definition,e.profileKey,e.stateDir),r=C(n.statePath);if(!r)return E(n,e.definition),{stopped:!1,statePath:n.statePath};let i=ye(r,t);return i.consumers.length>0?(w(n.statePath,i),{stopped:!1,statePath:n.statePath}):(await A(r,e.definition),E(n,e.definition),{stopped:!0,statePath:n.statePath})}const N={slug:`metro-companion`,runArg:te,displayName:`Metro companion`};async function Ce(e){return await j({...e,definition:N,registerPath:e.registerPath??`/api/metro/companion/register`})}async function we(e){return await M({...e,definition:N})}const P=8081,F=`.expo/.virtual-metro-entry.bundle`;function I(t,n){if(t==null||t===``)return n;let r=Number.parseInt(String(t),10);if(!Number.isInteger(r)||r<1||r>65535)throw new e(`INVALID_ARGS`,`Invalid Metro port: ${String(t)}. Use 1-65535.`);return r}function Te(t){let n=R(t.bundleUrl),r=n??t.runtime?.bundleUrl,i=!!n,a=!!R(r),o=ue({metroHost:Ee(t,i,a),metroPort:De(t,i,a),bundleUrl:r});if(!o)throw new e(`INVALID_ARGS`,`Unable to resolve Metro host and port for reload.`);let s=Oe(r);return{reloadUrl:L(o,o.scheme,`${s}/reload`),messageSocketUrl:L(o,o.scheme===`https`?`wss`:`ws`,`${s}/message`)}}function Ee(e,t,n){return R(e.metroHost)??(t?void 0:R(e.runtime?.metroHost))??(n?void 0:`localhost`)}function De(e,t,n){if(e.metroPort!==void 0)return I(e.metroPort,P);if(!t)return e.runtime?.metroPort??(n?void 0:P)}function Oe(e){let t=R(e);if(!t)return``;let n;try{n=new URL(t)}catch{return``}let r=n.pathname.replace(/\/+$/,``),i=`/${F}`;if(r.endsWith(i))return r.slice(0,-i.length);let a=r.lastIndexOf(`/`);return(a>=0?r.slice(a+1):r).endsWith(`.bundle`)&&a>0?r.slice(0,a):``}function L(e,t,n){let r=new URL(`${t}://localhost`);return r.hostname=e.host,r.port=String(e.port),r.pathname=n,r.toString()}function R(e){return typeof e==`string`&&e.trim()?e.trim():void 0}function z(e){return typeof e==`string`&&e.trim()?m(e.trim()):``}function B(e){return typeof e==`string`&&e.trim()?e.trim():void 0}function V(e,t,n){return i(e,{env:t,cwd:n})}function H(e){try{return v.accessSync(e,v.constants.F_OK),!0}catch{return!1}}function ke(e){try{return v.statSync(e).isDirectory()}catch{return!1}}function U(t){let n=_.join(t,`package.json`),r=ce(t);if(!r)throw new e(`INVALID_ARGS`,`package.json not found at ${n}`);return r}const Ae=[{file:`pnpm-lock.yaml`,command:`pnpm`},{file:`yarn.lock`,command:`yarn`},{file:`bun.lock`,command:`bun`},{file:`bun.lockb`,command:`bun`},{file:`package-lock.json`,command:`npm`}];function je(e){let t=e;for(;;){for(let{file:e,command:n}of Ae)if(H(_.join(t,e)))return{command:n,installArgs:[`install`]};if(H(_.join(t,`.git`)))return null;let e=_.dirname(t);if(e===t)return null;t=e}}function Me(e){return je(e)??{command:`npm`,installArgs:[`install`]}}function Ne(e,t){if(t!==`auto`)return t;let n=le(e);return n===`expo`||n===`repack`?n:`react-native`}function W(e,t){return typeof{...e.dependencies??{},...e.devDependencies??{}}[t]==`string`}function G(e,t){return[`js`,`mjs`,`cjs`,`ts`,`mts`,`cts`].some(n=>H(_.join(e,`${t}.config.${n}`)))}function Pe(e,t){return G(e,`rspack`)?`rspack`:G(e,`webpack`)?`webpack`:W(t,`@rspack/core`)?`rspack`:W(t,`webpack`)?`webpack`:`rspack`}function K(e,t,n){if(e==null||e===``)return t;let r=Number.parseInt(String(e),10);return Number.isInteger(r)?Math.max(r,n):t}function Fe(e){return e===`expo`?F:`index.bundle`}function q(e,t,n){return{platform:t,bundleUrl:oe(e,t,Fe(n))}}function J(e,t){return{platform:t,metroHost:B(e?.metro_host),metroPort:e?.metro_port,bundleUrl:B(e?.metro_bundle_url),launchUrl:B(e?.launch_url)}}function Ie(e,t){if(ke(_.join(e,`node_modules`)))return{installed:!1};let r=Me(e);try{n(r.command,r.installArgs,{cwd:e,env:t})}catch(e){throw Le(e,r)}return{installed:!0,packageManager:r.command}}function Le(n,r){let i=t(n),a=`Dependency install failed using detected package manager "${r.command}". If dependencies are already installed (for example via a monorepo root install), pass --no-install-deps to skip this step, or install manually with "${r.command} ${r.installArgs.join(` `)}" from the project root.`;return new e(i.code,i.message,{...i.details??{},hint:a,packageManager:r.command},i)}async function Y(e){await a(e)}async function X(t,n,r={}){try{let e=await fetch(t,{headers:r,signal:AbortSignal.timeout(n)});return{ok:e.ok,status:e.status,body:await e.text()}}catch(r){throw r instanceof Error&&r.name===`TimeoutError`?new e(`COMMAND_FAILED`,`Timed out fetching ${t} after ${n}ms`):r}}async function Re(e,t){try{let n=await X(e,t);return n.ok&&n.body.includes(`packager-status:running`)}catch{return!1}}function ze(e,t,n,r){return e===`expo`?{command:`npx`,installArgs:[`expo`,`start`,`--host`,`lan`,`--port`,String(n)]}:e===`repack`?{command:`npx`,installArgs:[`react-native`,t===`webpack`?`webpack-start`:`rspack-start`,`--host`,r,`--port`,String(n)]}:{command:`npx`,installArgs:[`react-native`,`start`,`--host`,r,`--port`,String(n)]}}function Be(t,n,i,a,o,s,c){let l=ze(n,i,a,o);v.mkdirSync(_.dirname(s),{recursive:!0});let u=v.openSync(s,`a`),d=0;try{d=r(l.command,l.installArgs,{cwd:t,env:c,stdio:[`ignore`,u,u]})}finally{v.closeSync(u)}if(!Number.isInteger(d)||d<=0)throw new e(`COMMAND_FAILED`,`Failed to start React Native dev server. Expected a detached child PID.`);return{pid:d}}async function Ve(e){if(!(!Number.isInteger(e)||e<=0)){try{process.kill(e,`SIGTERM`)}catch(e){let t=e.code;if(t===`ESRCH`||t===`EPERM`)return;throw e}if(!await s(e,1e3)){try{process.kill(e,`SIGKILL`)}catch(e){let t=e.code;if(t===`ESRCH`||t===`EPERM`)return;throw e}await s(e,1e3)}}}function He(e,t){return{Authorization:`Bearer ${t}`,"Content-Type":`application/json`,...e.includes(`ngrok`)?{"ngrok-skip-browser-warning":`1`}:{}}}function Z(t,n){let r=new e(`COMMAND_FAILED`,t);return r.retryable=n,r}function Ue(e,t){return!!(e>=500||e===408||e===425||e===429||JSON.stringify(t).includes(`Metro companion is not connected`))}function We(e){return!!(e&&typeof e==`object`&&`retryable`in e&&e.retryable===!0)}async function Q(e){let t;try{t=await fetch(`${e.baseUrl}/api/metro/bridge`,{method:`POST`,headers:He(e.baseUrl,e.bearerToken),body:JSON.stringify({...e.scope,...e.runtime?{ios_runtime:e.runtime}:{},timeout_ms:e.timeoutMs}),signal:AbortSignal.timeout(e.timeoutMs)})}catch(t){throw t instanceof Error&&t.name===`TimeoutError`?Z(`/api/metro/bridge timed out after ${e.timeoutMs}ms calling ${e.baseUrl}/api/metro/bridge`,!0):Z(t instanceof Error?t.message:String(t),!0)}let n=Ge(await t.text(),t.status,e.baseUrl);if(!t.ok)throw Z(`/api/metro/bridge failed (${t.status}): ${JSON.stringify(n)}`,Ue(t.status,n));return qe(n)}function Ge(e,t,n){if(!e)return{};try{let t=JSON.parse(e);if(!t||typeof t!=`object`||Array.isArray(t))throw Error(`Expected a JSON object`);return t}catch(r){let i=e.slice(0,200);throw Z(`/api/metro/bridge returned invalid JSON (${t}) from ${n}: ${r instanceof Error?r.message:String(r)}. body=${JSON.stringify(i)}`,Ue(t,e))}}function Ke(e){return{enabled:e.enabled,baseUrl:e.base_url,statusUrl:e.status_url??``,bundleUrl:e.bundle_url??``,iosRuntime:J(e.ios_runtime,`ios`),androidRuntime:J(e.android_runtime,`android`),upstream:{bundleUrl:e.upstream.bundle_url??``,host:e.upstream.host??``,port:e.upstream.port??0,statusUrl:e.upstream.status_url??``},probe:{reachable:e.probe.reachable,statusCode:e.probe.status_code,latencyMs:e.probe.latency_ms,detail:e.probe.detail}}}function qe(e){let t=e.data??e;if(!t||typeof t!=`object`||Array.isArray(t))throw Z(`/api/metro/bridge returned malformed descriptor: Expected a JSON object.`,!1);try{return Ke(t)}catch(e){throw Z(`/api/metro/bridge returned malformed descriptor: ${e instanceof Error?e.message:String(e)}`,!1)}}function $(e,t,n,r,i){let a=[`Metro bridge is required for this run but could not be configured via ${e}/api/metro/bridge.`];return t&&a.push(`bridgeError=${t}`),n?.probe.reachable===!1&&a.push(`bridgeProbe=${n.probe.detail||`unreachable (status ${n.probe.statusCode||0})`}`),r&&r!==t&&a.push(`initialBridgeError=${r}`),i&&a.push(`metroCompanionLog=${i}`),a.join(` `)}function Je(t,n){if(!n?.iosRuntime.bundleUrl)throw new e(`COMMAND_FAILED`,$(t,`bridge descriptor is missing ios_runtime.metro_bundle_url`,n))}function Ye(t,n,r){let i=n||B(r.AGENT_DEVICE_METRO_BEARER_TOKEN)||``,a=i||t&&B(r.AGENT_DEVICE_DAEMON_AUTH_TOKEN)||``;if(t&&!a)throw new e(`INVALID_ARGS`,`metro prepare requires proxy auth when --proxy-base-url is provided. Pass --bearer-token or set AGENT_DEVICE_METRO_BEARER_TOKEN or AGENT_DEVICE_DAEMON_AUTH_TOKEN.`);if(!t&&i)throw new e(`INVALID_ARGS`,`metro prepare requires --proxy-base-url when proxy auth is provided.`);return{proxyEnabled:!!(t&&a),proxyBaseUrl:t,proxyBearerToken:a}}function Xe(t){if(!t?.tenantId||!t.runId||!t.leaseId)throw new e(`INVALID_ARGS`,`metro prepare with proxy requires tenantId, runId, and leaseId bridge scope.`);return t}function Ze(t,n){if(!(t||n))throw new e(`INVALID_ARGS`,`metro prepare requires --public-base-url <url> or --proxy-base-url <url>.`)}function Qe(e,t,n){return e.runtimeFilePath?V(e.runtimeFilePath,t,n):null}function $e(e,t,n,r){return V(e.logPath??_.join(r,`.agent-device`,`metro.log`),t,n)}function et(e){let t=e.env??process.env,n=process.cwd(),r=V(e.projectRoot??n,t,n),i=e.kind??`auto`,a=null,o;i===`auto`?(a=U(r),o=Ne(a,i)):o=i;let s=null;o===`repack`&&(a??=U(r),s=Pe(r,a));let c=z(e.publicBaseUrl),l=z(e.proxyBaseUrl);Ze(c,l);let{proxyEnabled:u,proxyBaseUrl:d,proxyBearerToken:f}=Ye(l,B(e.proxyBearerToken)??``,t);return{env:t,projectRoot:r,kind:o,repackBundler:s,metroPort:I(e.metroPort??8081,8081),listenHost:B(e.listenHost)??`0.0.0.0`,statusHost:B(e.statusHost)??`127.0.0.1`,publicBaseUrl:c,proxyBaseUrl:d,proxyBearerToken:f,bridgeScope:u?Xe(e.bridgeScope):null,startupTimeoutMs:K(e.startupTimeoutMs,18e4,3e4),probeTimeoutMs:K(e.probeTimeoutMs,1e4,1e3),reuseExisting:e.reuseExisting??!0,installProjectDeps:e.installDependenciesIfNeeded??!0,runtimeFilePath:Qe(e,t,n),logPath:$e(e,t,n,r)}}async function tt(e,t,n){let r=Date.now()+t;for(;Date.now()<r;){let t=r-Date.now();if(await Re(e,Math.min(n,Math.max(t,1))))return!0;let i=Math.min(500,Math.max(r-Date.now(),0));i>0&&await Y(i)}return!1}async function nt(t){let n=Date.now()+t.startupTimeoutMs,r=null,i=null;for(;Date.now()<n;){try{let e=await Q({baseUrl:t.baseUrl,bearerToken:t.bearerToken,scope:t.scope,runtime:t.runtime,timeoutMs:t.probeTimeoutMs});if(e.probe.reachable!==!1)return e;r=e,i=null}catch(n){if(i=n instanceof Error?n.message:String(n),!We(n))throw new e(`COMMAND_FAILED`,$(t.baseUrl,i,r,t.initialBridgeError,t.companionLogPath),void 0,n)}let a=Math.min(1e3,Math.max(n-Date.now(),0));a>0&&await Y(a)}throw new e(`COMMAND_FAILED`,$(t.baseUrl,i,r,t.initialBridgeError,t.companionLogPath))}async function rt(t){let n=`http://${t.statusHost}:${t.metroPort}/status`;if(t.reuseExisting&&await Re(n,t.probeTimeoutMs))return{started:!1,reused:!0,pid:0,statusUrl:n};let r=Be(t.projectRoot,t.kind,t.repackBundler,t.metroPort,t.listenHost,t.logPath,t.env);if(await tt(n,t.startupTimeoutMs,t.probeTimeoutMs))return{started:!0,reused:!1,pid:r.pid,statusUrl:n};throw await Ve(r.pid).catch(()=>{}),new e(`COMMAND_FAILED`,`React Native dev server did not become ready at ${n} within ${t.startupTimeoutMs}ms. Check ${t.logPath}.`,{logPath:t.logPath})}async function it(e,t){let n=t.bridgeScope;if(!n)return null;let r=null,i=null;try{r=await Q({baseUrl:t.proxyBaseUrl,bearerToken:t.proxyBearerToken,scope:n,timeoutMs:t.probeTimeoutMs})}catch(e){if(!We(e))throw e;i=e instanceof Error?e.message:String(e)}return(!r||r.probe.reachable===!1)&&(r=await at(e,t,n,r,i)),Je(t.proxyBaseUrl,r),r}async function at(t,n,r,i,a){let o;try{o=(await Ce({projectRoot:n.projectRoot,serverBaseUrl:n.proxyBaseUrl,bearerToken:n.proxyBearerToken,bridgeScope:r,localBaseUrl:`http://${n.statusHost}:${n.metroPort}`,launchUrl:B(t.launchUrl),profileKey:B(t.companionProfileKey),consumerKey:B(t.companionConsumerKey),env:n.env})).logPath}catch(t){throw new e(`COMMAND_FAILED`,$(n.proxyBaseUrl,t instanceof Error?t.message:String(t),i,a),void 0,t)}try{return await nt({baseUrl:n.proxyBaseUrl,bearerToken:n.proxyBearerToken,scope:r,probeTimeoutMs:n.probeTimeoutMs,startupTimeoutMs:n.startupTimeoutMs,initialBridgeError:a,companionLogPath:o})}catch(e){throw e instanceof Error?e:Error(String(e))}}function ot(e,t){return{baseIosRuntime:e?q(e,`ios`,t):{platform:`ios`},baseAndroidRuntime:e?q(e,`android`,t):{platform:`android`}}}function st(e,t){e&&(v.mkdirSync(_.dirname(e),{recursive:!0}),v.writeFileSync(e,JSON.stringify(t,null,2)))}async function ct(e={}){let t=et(e),n=t.installProjectDeps?Ie(t.projectRoot,t.env):{installed:!1},r=await rt(t),{baseIosRuntime:i,baseAndroidRuntime:a}=ot(t.publicBaseUrl,t.kind),o=await it(e,t),s=o?.iosRuntime??i,c=o?.androidRuntime??a,l={projectRoot:t.projectRoot,kind:t.kind,dependenciesInstalled:n.installed,packageManager:n.packageManager??null,started:r.started,reused:r.reused,pid:r.pid,logPath:t.logPath,statusUrl:r.statusUrl,runtimeFilePath:t.runtimeFilePath,iosRuntime:s,androidRuntime:c,bridge:o};return st(t.runtimeFilePath,l),l}function lt(e){let t=e.trimStart().slice(0,15).toLowerCase();return t.startsWith(`<!doctype`)||t.startsWith(`<html`)}async function ut(t,n){await new Promise((r,i)=>{let a=new WebSocket(t),o=t=>{clearTimeout(s);try{a.close()}catch{}i(new e(`COMMAND_FAILED`,t))},s=setTimeout(()=>{o(`Timed out broadcasting reload to ${t} after ${n}ms`)},n);a.addEventListener(`open`,()=>{a.send(JSON.stringify({version:2,method:`reload`}));let e=()=>{if(a.bufferedAmount===0){clearTimeout(s),a.close(),r();return}setTimeout(e,20)};e()}),a.addEventListener(`error`,()=>{o(`Failed to reach the dev server message socket at ${t}`)})})}async function dt(t={}){let n=K(t.timeoutMs,1e4,1e3),{reloadUrl:r,messageSocketUrl:i}=Te(t),a=await X(r,n);if(a.ok&&!lt(a.body))return{reloaded:!0,reloadUrl:r,status:a.status,body:a.body,transport:`http`};try{await ut(i,n)}catch(t){throw new e(`COMMAND_FAILED`,`React Native dev server reload failed (${a.status}).`,{reloadUrl:r,status:a.status,body:a.body.slice(0,500),messageSocketUrl:i,hint:`Verify Metro or Re.Pack is running and the target React Native app is connected to this dev server instance.`},t instanceof Error?t:void 0)}return{reloaded:!0,reloadUrl:i,status:a.status,body:``,transport:`message-socket`}}export{M as a,j as i,dt as n,we as r,ct as t};
1
+ import{_ as e,v as t}from"./sdk-contracts.js";import{S as n,y as r}from"./sdk-android-adb.js";import{n as i}from"./path-resolution.js";import{t as a}from"./timeouts.js";import{c as o,d as s,n as c,s as l}from"./host-process.js";import{a as u,c as d,d as f,f as ee,g as te,h as p,i as ne,l as re,m as ie,n as m,o as h,p as g,s as ae,t as oe,u as se}from"./url.js";import{n as ce,r as le}from"./project-runtime.js";import{t as ue}from"./runtime-transport.js";import _ from"node:path";import{createHash as de}from"node:crypto";import v from"node:fs";import{fileURLToPath as fe}from"node:url";const y=`companion-tunnel`;function b(e){return de(`sha256`).update(e).digest(`hex`)}function x(e){return e?.trim()?e.trim():void 0}function pe(e){if(!e||typeof e!=`object`||Array.isArray(e))return;let t=e;if(!(typeof t.tenantId!=`string`||typeof t.runId!=`string`||typeof t.leaseId!=`string`))return{tenantId:t.tenantId,runId:t.runId,leaseId:t.leaseId}}function me(e,t){return e.tenantId===t.tenantId&&e.runId===t.runId&&e.leaseId===t.leaseId}function S(e,t,n,r){let i=r??_.join(e,`.agent-device`);if(!n)return{statePath:_.join(i,`${t.slug}.json`),logPath:_.join(i,`${t.slug}.log`)};let a=b(n).slice(0,12),o=_.join(i,t.slug);return{statePath:_.join(o,`${t.slug}-${a}.json`),logPath:_.join(o,`${t.slug}-${a}.log`)}}function C(e){try{let t=JSON.parse(v.readFileSync(e,`utf8`));if(!Number.isInteger(t.pid)||Number(t.pid)<=0||typeof t.serverBaseUrl!=`string`||typeof t.localBaseUrl!=`string`||typeof t.tokenHash!=`string`||t.tokenHash.length===0)return null;let n=Array.isArray(t.consumers)?t.consumers.filter(e=>typeof e==`string`&&e.length>0):[];return{pid:Number(t.pid),startTime:typeof t.startTime==`string`?t.startTime:void 0,command:typeof t.command==`string`?t.command:void 0,serverBaseUrl:t.serverBaseUrl,localBaseUrl:t.localBaseUrl,launchUrl:x(typeof t.launchUrl==`string`?t.launchUrl:void 0),registerPath:x(typeof t.registerPath==`string`?t.registerPath:void 0),unregisterPath:x(typeof t.unregisterPath==`string`?t.unregisterPath:void 0),devicePort:Number.isInteger(t.devicePort)?Number(t.devicePort):void 0,session:x(typeof t.session==`string`?t.session:void 0),bridgeScope:pe(t.bridgeScope),tokenHash:t.tokenHash,consumers:n}}catch{return null}}function w(e,t){v.mkdirSync(_.dirname(e),{recursive:!0}),v.writeFileSync(e,`${JSON.stringify(t,null,2)}\n`,`utf8`)}function he(e){v.mkdirSync(_.dirname(e),{recursive:!0}),v.closeSync(v.openSync(e,`a`))}function ge(e){try{v.unlinkSync(e)}catch{}}function _e(e){try{v.unlinkSync(e)}catch{}}function T(e){try{v.readdirSync(e).length===0&&v.rmdirSync(e)}catch{}}function E(e,t){let n=_.dirname(e.statePath),r=_.dirname(e.logPath);ge(e.statePath),_e(e.logPath),T(n),r!==n&&T(r),_.basename(n)===t.slug&&T(_.dirname(n))}function D(e,t){return e.includes(t.runArg)}function ve(e,t){if(!c(e.pid))return!1;if(e.startTime){let t=o(e.pid);if(!t||t!==e.startTime)return!1}let n=l(e.pid);return!n||!D(n,t.definition)||!e.bridgeScope?!1:e.serverBaseUrl===m(t.serverBaseUrl)&&e.localBaseUrl===m(t.localBaseUrl)&&e.launchUrl===x(t.launchUrl)&&e.registerPath===x(t.registerPath)&&e.unregisterPath===x(t.unregisterPath)&&e.devicePort===t.devicePort&&e.session===x(t.session)&&me(e.bridgeScope,t.bridgeScope)&&e.tokenHash===b(t.bearerToken)}function O(e){return x(e.consumerKey)??x(e.profileKey)??null}function k(e,t){return!t||e.consumers.includes(t)?e:{...e,consumers:[...e.consumers,t]}}function ye(e,t){return t?{...e,consumers:e.consumers.filter(e=>e!==t)}:{...e,consumers:[]}}async function A(e,t){if(!c(e.pid))return;let n=l(e.pid);if(!(!n||!D(n,t))){try{process.kill(e.pid,`SIGTERM`)}catch(e){let t=e.code;if(t===`ESRCH`||t===`EPERM`)return;throw e}if(!await s(e.pid,1e3)){try{process.kill(e.pid,`SIGKILL`)}catch(e){let t=e.code;if(t===`ESRCH`||t===`EPERM`)return;throw e}await s(e.pid,1e3)}}}function be(e,t){let n={...t};return n[ee]=m(e.serverBaseUrl),n[ne]=e.bearerToken,n[ae]=m(e.localBaseUrl),n[ie]=S(e.projectRoot,e.definition,e.profileKey,e.stateDir).statePath,n[f]=e.bridgeScope.tenantId,n[se]=e.bridgeScope.runId,n[re]=e.bridgeScope.leaseId,e.launchUrl?.trim()?n[h]=e.launchUrl.trim():delete n[h],e.registerPath?.trim()?n[d]=e.registerPath.trim():delete n[d],e.unregisterPath?.trim()?n[p]=e.unregisterPath.trim():delete n[p],e.devicePort===void 0?delete n[u]:n[u]=String(e.devicePort),e.session?.trim()?n[g]=e.session.trim():delete n[g],n}function xe(e){let t=fe(import.meta.url),n=_.extname(t)||`.js`,r=[_.join(_.dirname(t),`${y}${n}`),_.join(_.dirname(t),`internal`,`${y}${n}`)].find(e=>v.existsSync(e));if(!r)throw Error(`${e.displayName} entrypoint not found. Rebuild the package to include the companion worker entry.`);return r}function Se(e,t){let n=xe(e.definition),i=n.endsWith(`.ts`)?[`--experimental-strip-types`]:[];v.mkdirSync(_.dirname(t),{recursive:!0});let a=v.openSync(t,`a`),s=0;try{s=r(process.execPath,[...i,n,e.definition.runArg],{env:be(e,e.env??process.env),stdio:[`ignore`,a,a]})}finally{v.closeSync(a)}if(!Number.isInteger(s)||s<=0)throw Error(`Failed to start ${e.definition.displayName} process.`);return{pid:s,startTime:o(s)??void 0,command:l(s)??void 0,serverBaseUrl:m(e.serverBaseUrl),localBaseUrl:m(e.localBaseUrl),launchUrl:x(e.launchUrl),registerPath:x(e.registerPath),unregisterPath:x(e.unregisterPath),devicePort:e.devicePort,session:x(e.session),bridgeScope:e.bridgeScope,tokenHash:b(e.bearerToken),consumers:[]}}async function j(e){let t=O(e),n=S(e.projectRoot,e.definition,e.profileKey,e.stateDir),r=C(n.statePath);if(r&&ve(r,e)){let e=k(r,t);return e!==r&&w(n.statePath,e),{pid:r.pid,spawned:!1,statePath:n.statePath,logPath:n.logPath}}r&&(await A(r,e.definition),E(n,e.definition)),he(n.statePath);let i;try{i=Se(e,n.logPath),w(n.statePath,k(i,t))}catch(t){throw i&&await A(i,e.definition).catch(()=>{}),E(n,e.definition),t}return{pid:i.pid,spawned:!0,statePath:n.statePath,logPath:n.logPath}}async function M(e){let t=O(e),n=S(e.projectRoot,e.definition,e.profileKey,e.stateDir),r=C(n.statePath);if(!r)return E(n,e.definition),{stopped:!1,statePath:n.statePath};let i=ye(r,t);return i.consumers.length>0?(w(n.statePath,i),{stopped:!1,statePath:n.statePath}):(await A(r,e.definition),E(n,e.definition),{stopped:!0,statePath:n.statePath})}const N={slug:`metro-companion`,runArg:te,displayName:`Metro companion`};async function Ce(e){return await j({...e,definition:N,registerPath:e.registerPath??`/api/metro/companion/register`})}async function we(e){return await M({...e,definition:N})}const P=8081,F=`.expo/.virtual-metro-entry.bundle`;function I(t,n){if(t==null||t===``)return n;let r=Number.parseInt(String(t),10);if(!Number.isInteger(r)||r<1||r>65535)throw new e(`INVALID_ARGS`,`Invalid Metro port: ${String(t)}. Use 1-65535.`);return r}function Te(t){let n=R(t.bundleUrl),r=n??t.runtime?.bundleUrl,i=!!n,a=!!R(r),o=ue({metroHost:Ee(t,i,a),metroPort:De(t,i,a),bundleUrl:r});if(!o)throw new e(`INVALID_ARGS`,`Unable to resolve Metro host and port for reload.`);let s=Oe(r);return{reloadUrl:L(o,o.scheme,`${s}/reload`),messageSocketUrl:L(o,o.scheme===`https`?`wss`:`ws`,`${s}/message`)}}function Ee(e,t,n){return R(e.metroHost)??(t?void 0:R(e.runtime?.metroHost))??(n?void 0:`localhost`)}function De(e,t,n){if(e.metroPort!==void 0)return I(e.metroPort,P);if(!t)return e.runtime?.metroPort??(n?void 0:P)}function Oe(e){let t=R(e);if(!t)return``;let n;try{n=new URL(t)}catch{return``}let r=n.pathname.replace(/\/+$/,``),i=`/${F}`;if(r.endsWith(i))return r.slice(0,-i.length);let a=r.lastIndexOf(`/`);return(a>=0?r.slice(a+1):r).endsWith(`.bundle`)&&a>0?r.slice(0,a):``}function L(e,t,n){let r=new URL(`${t}://localhost`);return r.hostname=e.host,r.port=String(e.port),r.pathname=n,r.toString()}function R(e){return typeof e==`string`&&e.trim()?e.trim():void 0}function z(e){return typeof e==`string`&&e.trim()?m(e.trim()):``}function B(e){return typeof e==`string`&&e.trim()?e.trim():void 0}function V(e,t,n){return i(e,{env:t,cwd:n})}function H(e){try{return v.accessSync(e,v.constants.F_OK),!0}catch{return!1}}function ke(e){try{return v.statSync(e).isDirectory()}catch{return!1}}function U(t){let n=_.join(t,`package.json`),r=le(t);if(!r)throw new e(`INVALID_ARGS`,`package.json not found at ${n}`);return r}const Ae=[{file:`pnpm-lock.yaml`,command:`pnpm`},{file:`yarn.lock`,command:`yarn`},{file:`bun.lock`,command:`bun`},{file:`bun.lockb`,command:`bun`},{file:`package-lock.json`,command:`npm`}];function je(e){let t=e;for(;;){for(let{file:e,command:n}of Ae)if(H(_.join(t,e)))return{command:n,installArgs:[`install`]};if(H(_.join(t,`.git`)))return null;let e=_.dirname(t);if(e===t)return null;t=e}}function Me(e){return je(e)??{command:`npm`,installArgs:[`install`]}}function Ne(e,t){if(t!==`auto`)return t;let n=ce(e);return n===`expo`||n===`repack`?n:`react-native`}function W(e,t){return typeof{...e.dependencies??{},...e.devDependencies??{}}[t]==`string`}function G(e,t){return[`js`,`mjs`,`cjs`,`ts`,`mts`,`cts`].some(n=>H(_.join(e,`${t}.config.${n}`)))}function Pe(e,t){return G(e,`rspack`)?`rspack`:G(e,`webpack`)?`webpack`:W(t,`@rspack/core`)?`rspack`:W(t,`webpack`)?`webpack`:`rspack`}function K(e,t,n){if(e==null||e===``)return t;let r=Number.parseInt(String(e),10);return Number.isInteger(r)?Math.max(r,n):t}function Fe(e){return e===`expo`?F:`index.bundle`}function q(e,t,n){return{platform:t,bundleUrl:oe(e,t,Fe(n))}}function J(e,t){return{platform:t,metroHost:B(e?.metro_host),metroPort:e?.metro_port,bundleUrl:B(e?.metro_bundle_url),launchUrl:B(e?.launch_url)}}function Ie(e,t){if(ke(_.join(e,`node_modules`)))return{installed:!1};let r=Me(e);try{n(r.command,r.installArgs,{cwd:e,env:t})}catch(e){throw Le(e,r)}return{installed:!0,packageManager:r.command}}function Le(n,r){let i=t(n),a=`Dependency install failed using detected package manager "${r.command}". If dependencies are already installed (for example via a monorepo root install), pass --no-install-deps to skip this step, or install manually with "${r.command} ${r.installArgs.join(` `)}" from the project root.`;return new e(i.code,i.message,{...i.details??{},hint:a,packageManager:r.command},i)}async function Y(e){await a(e)}async function X(t,n,r={}){try{let e=await fetch(t,{headers:r,signal:AbortSignal.timeout(n)});return{ok:e.ok,status:e.status,body:await e.text()}}catch(r){throw r instanceof Error&&r.name===`TimeoutError`?new e(`COMMAND_FAILED`,`Timed out fetching ${t} after ${n}ms`):r}}async function Re(e,t){try{let n=await X(e,t);return n.ok&&n.body.includes(`packager-status:running`)}catch{return!1}}function ze(e,t,n,r){return e===`expo`?{command:`npx`,installArgs:[`expo`,`start`,`--host`,`lan`,`--port`,String(n)]}:e===`repack`?{command:`npx`,installArgs:[`react-native`,t===`webpack`?`webpack-start`:`rspack-start`,`--host`,r,`--port`,String(n)]}:{command:`npx`,installArgs:[`react-native`,`start`,`--host`,r,`--port`,String(n)]}}function Be(t,n,i,a,o,s,c){let l=ze(n,i,a,o);v.mkdirSync(_.dirname(s),{recursive:!0});let u=v.openSync(s,`a`),d=0;try{d=r(l.command,l.installArgs,{cwd:t,env:c,stdio:[`ignore`,u,u]})}finally{v.closeSync(u)}if(!Number.isInteger(d)||d<=0)throw new e(`COMMAND_FAILED`,`Failed to start React Native dev server. Expected a detached child PID.`);return{pid:d}}async function Ve(e){if(!(!Number.isInteger(e)||e<=0)){try{process.kill(e,`SIGTERM`)}catch(e){let t=e.code;if(t===`ESRCH`||t===`EPERM`)return;throw e}if(!await s(e,1e3)){try{process.kill(e,`SIGKILL`)}catch(e){let t=e.code;if(t===`ESRCH`||t===`EPERM`)return;throw e}await s(e,1e3)}}}function He(e,t){return{Authorization:`Bearer ${t}`,"Content-Type":`application/json`,...e.includes(`ngrok`)?{"ngrok-skip-browser-warning":`1`}:{}}}function Z(t,n){let r=new e(`COMMAND_FAILED`,t);return r.retryable=n,r}function Ue(e,t){return!!(e>=500||e===408||e===425||e===429||JSON.stringify(t).includes(`Metro companion is not connected`))}function We(e){return!!(e&&typeof e==`object`&&`retryable`in e&&e.retryable===!0)}async function Q(e){let t;try{t=await fetch(`${e.baseUrl}/api/metro/bridge`,{method:`POST`,headers:He(e.baseUrl,e.bearerToken),body:JSON.stringify({...e.scope,...e.runtime?{ios_runtime:e.runtime}:{},timeout_ms:e.timeoutMs}),signal:AbortSignal.timeout(e.timeoutMs)})}catch(t){throw t instanceof Error&&t.name===`TimeoutError`?Z(`/api/metro/bridge timed out after ${e.timeoutMs}ms calling ${e.baseUrl}/api/metro/bridge`,!0):Z(t instanceof Error?t.message:String(t),!0)}let n=Ge(await t.text(),t.status,e.baseUrl);if(!t.ok)throw Z(`/api/metro/bridge failed (${t.status}): ${JSON.stringify(n)}`,Ue(t.status,n));return qe(n)}function Ge(e,t,n){if(!e)return{};try{let t=JSON.parse(e);if(!t||typeof t!=`object`||Array.isArray(t))throw Error(`Expected a JSON object`);return t}catch(r){let i=e.slice(0,200);throw Z(`/api/metro/bridge returned invalid JSON (${t}) from ${n}: ${r instanceof Error?r.message:String(r)}. body=${JSON.stringify(i)}`,Ue(t,e))}}function Ke(e){return{enabled:e.enabled,baseUrl:e.base_url,statusUrl:e.status_url??``,bundleUrl:e.bundle_url??``,iosRuntime:J(e.ios_runtime,`ios`),androidRuntime:J(e.android_runtime,`android`),upstream:{bundleUrl:e.upstream.bundle_url??``,host:e.upstream.host??``,port:e.upstream.port??0,statusUrl:e.upstream.status_url??``},probe:{reachable:e.probe.reachable,statusCode:e.probe.status_code,latencyMs:e.probe.latency_ms,detail:e.probe.detail}}}function qe(e){let t=e.data??e;if(!t||typeof t!=`object`||Array.isArray(t))throw Z(`/api/metro/bridge returned malformed descriptor: Expected a JSON object.`,!1);try{return Ke(t)}catch(e){throw Z(`/api/metro/bridge returned malformed descriptor: ${e instanceof Error?e.message:String(e)}`,!1)}}function $(e,t,n,r,i){let a=[`Metro bridge is required for this run but could not be configured via ${e}/api/metro/bridge.`];return t&&a.push(`bridgeError=${t}`),n?.probe.reachable===!1&&a.push(`bridgeProbe=${n.probe.detail||`unreachable (status ${n.probe.statusCode||0})`}`),r&&r!==t&&a.push(`initialBridgeError=${r}`),i&&a.push(`metroCompanionLog=${i}`),a.join(` `)}function Je(t,n){if(!n?.iosRuntime.bundleUrl)throw new e(`COMMAND_FAILED`,$(t,`bridge descriptor is missing ios_runtime.metro_bundle_url`,n))}function Ye(t,n,r){let i=n||B(r.AGENT_DEVICE_METRO_BEARER_TOKEN)||``,a=i||t&&B(r.AGENT_DEVICE_DAEMON_AUTH_TOKEN)||``;if(t&&!a)throw new e(`INVALID_ARGS`,`metro prepare requires proxy auth when --proxy-base-url is provided. Pass --bearer-token or set AGENT_DEVICE_METRO_BEARER_TOKEN or AGENT_DEVICE_DAEMON_AUTH_TOKEN.`);if(!t&&i)throw new e(`INVALID_ARGS`,`metro prepare requires --proxy-base-url when proxy auth is provided.`);return{proxyEnabled:!!(t&&a),proxyBaseUrl:t,proxyBearerToken:a}}function Xe(t){if(!t?.tenantId||!t.runId||!t.leaseId)throw new e(`INVALID_ARGS`,`metro prepare with proxy requires tenantId, runId, and leaseId bridge scope.`);return t}function Ze(t,n){if(!(t||n))throw new e(`INVALID_ARGS`,`metro prepare requires --public-base-url <url> or --proxy-base-url <url>.`)}function Qe(e,t,n){return e.runtimeFilePath?V(e.runtimeFilePath,t,n):null}function $e(e,t,n,r){return V(e.logPath??_.join(r,`.agent-device`,`metro.log`),t,n)}function et(e){let t=e.env??process.env,n=process.cwd(),r=V(e.projectRoot??n,t,n),i=e.kind??`auto`,a=null,o;i===`auto`?(a=U(r),o=Ne(a,i)):o=i;let s=null;o===`repack`&&(a??=U(r),s=Pe(r,a));let c=z(e.publicBaseUrl),l=z(e.proxyBaseUrl);Ze(c,l);let{proxyEnabled:u,proxyBaseUrl:d,proxyBearerToken:f}=Ye(l,B(e.proxyBearerToken)??``,t);return{env:t,projectRoot:r,kind:o,repackBundler:s,metroPort:I(e.metroPort??8081,8081),listenHost:B(e.listenHost)??`0.0.0.0`,statusHost:B(e.statusHost)??`127.0.0.1`,publicBaseUrl:c,proxyBaseUrl:d,proxyBearerToken:f,bridgeScope:u?Xe(e.bridgeScope):null,startupTimeoutMs:K(e.startupTimeoutMs,18e4,3e4),probeTimeoutMs:K(e.probeTimeoutMs,1e4,1e3),reuseExisting:e.reuseExisting??!0,installProjectDeps:e.installDependenciesIfNeeded??!0,runtimeFilePath:Qe(e,t,n),logPath:$e(e,t,n,r)}}async function tt(e,t,n){let r=Date.now()+t;for(;Date.now()<r;){let t=r-Date.now();if(await Re(e,Math.min(n,Math.max(t,1))))return!0;let i=Math.min(500,Math.max(r-Date.now(),0));i>0&&await Y(i)}return!1}async function nt(t){let n=Date.now()+t.startupTimeoutMs,r=null,i=null;for(;Date.now()<n;){try{let e=await Q({baseUrl:t.baseUrl,bearerToken:t.bearerToken,scope:t.scope,runtime:t.runtime,timeoutMs:t.probeTimeoutMs});if(e.probe.reachable!==!1)return e;r=e,i=null}catch(n){if(i=n instanceof Error?n.message:String(n),!We(n))throw new e(`COMMAND_FAILED`,$(t.baseUrl,i,r,t.initialBridgeError,t.companionLogPath),void 0,n)}let a=Math.min(1e3,Math.max(n-Date.now(),0));a>0&&await Y(a)}throw new e(`COMMAND_FAILED`,$(t.baseUrl,i,r,t.initialBridgeError,t.companionLogPath))}async function rt(t){let n=`http://${t.statusHost}:${t.metroPort}/status`;if(t.reuseExisting&&await Re(n,t.probeTimeoutMs))return{started:!1,reused:!0,pid:0,statusUrl:n};let r=Be(t.projectRoot,t.kind,t.repackBundler,t.metroPort,t.listenHost,t.logPath,t.env);if(await tt(n,t.startupTimeoutMs,t.probeTimeoutMs))return{started:!0,reused:!1,pid:r.pid,statusUrl:n};throw await Ve(r.pid).catch(()=>{}),new e(`COMMAND_FAILED`,`React Native dev server did not become ready at ${n} within ${t.startupTimeoutMs}ms. Check ${t.logPath}.`,{logPath:t.logPath})}async function it(e,t){let n=t.bridgeScope;if(!n)return null;let r=null,i=null;try{r=await Q({baseUrl:t.proxyBaseUrl,bearerToken:t.proxyBearerToken,scope:n,timeoutMs:t.probeTimeoutMs})}catch(e){if(!We(e))throw e;i=e instanceof Error?e.message:String(e)}return(!r||r.probe.reachable===!1)&&(r=await at(e,t,n,r,i)),Je(t.proxyBaseUrl,r),r}async function at(t,n,r,i,a){let o;try{o=(await Ce({projectRoot:n.projectRoot,serverBaseUrl:n.proxyBaseUrl,bearerToken:n.proxyBearerToken,bridgeScope:r,localBaseUrl:`http://${n.statusHost}:${n.metroPort}`,launchUrl:B(t.launchUrl),profileKey:B(t.companionProfileKey),consumerKey:B(t.companionConsumerKey),env:n.env})).logPath}catch(t){throw new e(`COMMAND_FAILED`,$(n.proxyBaseUrl,t instanceof Error?t.message:String(t),i,a),void 0,t)}try{return await nt({baseUrl:n.proxyBaseUrl,bearerToken:n.proxyBearerToken,scope:r,probeTimeoutMs:n.probeTimeoutMs,startupTimeoutMs:n.startupTimeoutMs,initialBridgeError:a,companionLogPath:o})}catch(e){throw e instanceof Error?e:Error(String(e))}}function ot(e,t){return{baseIosRuntime:e?q(e,`ios`,t):{platform:`ios`},baseAndroidRuntime:e?q(e,`android`,t):{platform:`android`}}}function st(e,t){e&&(v.mkdirSync(_.dirname(e),{recursive:!0}),v.writeFileSync(e,JSON.stringify(t,null,2)))}async function ct(e={}){let t=et(e),n=t.installProjectDeps?Ie(t.projectRoot,t.env):{installed:!1},r=await rt(t),{baseIosRuntime:i,baseAndroidRuntime:a}=ot(t.publicBaseUrl,t.kind),o=await it(e,t),s=o?.iosRuntime??i,c=o?.androidRuntime??a,l={projectRoot:t.projectRoot,kind:t.kind,dependenciesInstalled:n.installed,packageManager:n.packageManager??null,started:r.started,reused:r.reused,pid:r.pid,logPath:t.logPath,statusUrl:r.statusUrl,runtimeFilePath:t.runtimeFilePath,iosRuntime:s,androidRuntime:c,bridge:o};return st(t.runtimeFilePath,l),l}function lt(e){let t=e.trimStart().slice(0,15).toLowerCase();return t.startsWith(`<!doctype`)||t.startsWith(`<html`)}async function ut(t,n){await new Promise((r,i)=>{let a=new WebSocket(t),o=t=>{clearTimeout(s);try{a.close()}catch{}i(new e(`COMMAND_FAILED`,t))},s=setTimeout(()=>{o(`Timed out broadcasting reload to ${t} after ${n}ms`)},n);a.addEventListener(`open`,()=>{a.send(JSON.stringify({version:2,method:`reload`}));let e=()=>{if(a.bufferedAmount===0){clearTimeout(s),a.close(),r();return}setTimeout(e,20)};e()}),a.addEventListener(`error`,()=>{o(`Failed to reach the dev server message socket at ${t}`)})})}async function dt(t={}){let n=K(t.timeoutMs,1e4,1e3),{reloadUrl:r,messageSocketUrl:i}=Te(t),a=await X(r,n);if(a.ok&&!lt(a.body))return{reloaded:!0,reloadUrl:r,status:a.status,body:a.body,transport:`http`};try{await ut(i,n)}catch(t){throw new e(`COMMAND_FAILED`,`React Native dev server reload failed (${a.status}).`,{reloadUrl:r,status:a.status,body:a.body.slice(0,500),messageSocketUrl:i,hint:`Verify Metro or Re.Pack is running and the target React Native app is connected to this dev server instance.`},t instanceof Error?t:void 0)}return{reloaded:!0,reloadUrl:i,status:a.status,body:``,transport:`message-socket`}}export{M as a,j as i,dt as n,we as r,ct as t};
@@ -0,0 +1 @@
1
+ import{g as e}from"./sdk-batch-runner.js";const t=s(`public`),n=s(`internal`),r=s(`local-cli`),i={help:`help`};function a(){return[...Object.values(t),...Object.values(r)].sort()}function o(e){return Object.values(i).includes(e)||Object.values(n).includes(e)?!0:a().includes(e)}function s(t){let n={};for(let[r,i]of e(t))n[r]=i;return n}export{a as i,t as n,o as r,n as t};
@@ -1 +1 @@
1
- import{b as e}from"./sdk-batch-runner.js";import{o as t}from"./registry.js";const n=t(),r=new Map(n.map(e=>[e.name,e]));function i(){return[...n]}function a(){return e().map(e=>{let t=s(e);if(!t)throw Error(`Missing command metadata for MCP-exposed command: ${e}`);return t})}function o(e){return r.has(e)}function s(e){return r.get(e)}export{a as i,o as n,i as r,s as t};
1
+ import{_ as e}from"./sdk-batch-runner.js";import{o as t}from"./registry.js";const n=t(),r=new Map(n.map(e=>[e.name,e]));function i(){return[...n]}function a(){return e().map(e=>{let t=s(e);if(!t)throw Error(`Missing command metadata for MCP-exposed command: ${e}`);return t})}function o(e){return r.has(e)}function s(e){return r.get(e)}export{a as i,o as n,i as r,s as t};
@@ -1,4 +1,4 @@
1
- import{_ as e}from"./sdk-contracts.js";import{f as t,r as n,u as r}from"./registry.js";import{r as i}from"./command-metadata.js";const a={cdp:{text:{summary:`Inspect CDP targets, JS heap, and leaks`,description:`Run CDP commands for React Native diagnostics, JS heap usage, heap snapshots, and leak analysis`},usageOverride:`cdp [...args]`,listUsageOverride:`cdp`,positionalArgs:[`args?`],allowsExtraPositionals:!0,supportedFlags:r},auth:{text:{summary:`Manage cloud login state`,description:`Manage cloud login state used by remote daemon and cloud device workflows.`},usageOverride:`auth status|login|logout`,listUsageOverride:`auth`,positionalArgs:[`status|login|logout`],supportedFlags:[`remoteConfig`,`stateDir`]},daemon:{text:{summary:`Stop a local daemon and clean up resources`,description:`Stop a local daemon after verifying its PID/start-time identity. Use --clean to remove retained Apple runner processes and leases owned by that daemon.`},usageOverride:`daemon stop [--state-dir <path>] [--clean]`,listUsageOverride:`daemon stop`,positionalArgs:[`stop`],allowedFlags:[`clean`],supportedFlags:[`stateDir`]},device:{text:{summary:`Inspect local advisory device ownership without daemon side effects`,description:`Inspect advisory host-local device ownership claims without starting or contacting a daemon. --stale only inspects proven-stale claims; it does not reclaim claims or clean platform resources.`},usageOverride:`device status [--platform <platform>] [--udid <udid>] [--serial <serial>] [--stale]`,listUsageOverride:`device status`,positionalArgs:[`status`],allowedFlags:[`stale`],supportedFlags:[`platform`,`device`,`udid`,`serial`]},connect:{text:{summary:`Configure remote or provider state`,description:`Configure remote access without allocating a device. Direct providers validate credentials/resources before saving state and print the exact device/app preparation needed before open. AGENT_DEVICE_CLOUD_BASE_URL is the bridge/control-plane API origin; use AGENT_DEVICE_DAEMON_AUTH_TOKEN=adc_live_... for CI/service-token automation.`},usageOverride:`connect [cloud|proxy|limrun|browserstack|aws-device-farm] [--remote-config <path>] [--daemon-base-url <url>] [--tenant <id>] [--run-id <id>] [--lease-id <id>] [--lease-backend <backend>] [--force] [--no-login]`,listUsageOverride:`connect`,positionalArgs:[`provider?`],allowedFlags:`remoteConfig.daemonBaseUrl.tenant.runId.leaseId.leaseBackend.providerApp.providerOsVersion.providerProject.providerBuild.providerSessionName.providerDeviceOrientation.providerGeoLocation.providerTimezone.providerLanguage.providerLocale.providerNetworkProfile.providerCustomNetwork.providerNoResignApp.awsProjectArn.awsDeviceArn.awsAppArn.awsRegion.awsInteractionMode.force.noLogin`.split(`.`),supportedFlags:[`stateDir`,`daemonAuthToken`,`session`,`platform`,`device`,...t,`launchUrl`]},connection:{text:{summary:`Inspect the saved remote connection`,description:`Inspect active remote connection state`},usageOverride:`connection status`,listUsageOverride:`connection`,positionalArgs:[`status`],supportedFlags:[`remoteConfig`,`stateDir`,`session`]},disconnect:{text:{summary:`Clear remote connection state and release leases`,description:`Disconnect remote daemon state, stop owned Metro companion, and release lease`},listUsageOverride:`disconnect`,allowedFlags:[`shutdown`],supportedFlags:[`remoteConfig`,`stateDir`,`session`]},mcp:{text:{summary:`Start MCP server`,description:`Start the official stdio MCP server. It exposes structured command tools backed by the agent-device client.`}},proxy:{text:{summary:`Expose a local daemon through an HTTP tunnel`,description:`Expose the local daemon HTTP contract through a tunnel-friendly reverse proxy.
1
+ import{_ as e}from"./sdk-contracts.js";import{f as t,r as n,u as r}from"./registry.js";import{r as i}from"./command-metadata.js";const a={cdp:{text:{summary:`Inspect CDP targets, JS heap, and leaks`,description:`Run CDP commands for React Native diagnostics, JS heap usage, heap snapshots, and leak analysis`},usageOverride:`cdp [...args]`,listUsageOverride:`cdp`,positionalArgs:[`args?`],allowsExtraPositionals:!0,supportedFlags:r},auth:{text:{summary:`Manage cloud login state`,description:`Manage cloud login state used by remote daemon and cloud device workflows.`},usageOverride:`auth status|login|logout`,listUsageOverride:`auth`,positionalArgs:[`status|login|logout`],supportedFlags:[`remoteConfig`,`stateDir`]},daemon:{text:{summary:`Stop a local daemon and clean up resources`,description:`Stop a local daemon after verifying its PID/start-time identity. Use --clean to remove retained Apple runner processes and leases owned by that daemon.`},usageOverride:`daemon stop [--state-dir <path>] [--clean]`,listUsageOverride:`daemon stop`,positionalArgs:[`stop`],allowedFlags:[`clean`],supportedFlags:[`stateDir`]},device:{text:{summary:`Inspect enforced local device ownership without daemon side effects`,description:`Inspect enforced host-local device ownership claims without starting or contacting a daemon. --stale only inspects proven-stale claims; automatic reclamation occurs during open or daemon startup after exact-owner resource reconciliation.`},usageOverride:`device status [--platform <platform>] [--udid <udid>] [--serial <serial>] [--stale]`,listUsageOverride:`device status`,positionalArgs:[`status`],allowedFlags:[`stale`],supportedFlags:[`platform`,`device`,`udid`,`serial`]},connect:{text:{summary:`Configure remote or provider state`,description:`Configure remote access without allocating a device. Direct providers validate credentials/resources before saving state and print the exact device/app preparation needed before open. AGENT_DEVICE_CLOUD_BASE_URL is the bridge/control-plane API origin; use AGENT_DEVICE_DAEMON_AUTH_TOKEN=adc_live_... for CI/service-token automation.`},usageOverride:`connect [cloud|proxy|limrun|browserstack|aws-device-farm] [--remote-config <path>] [--daemon-base-url <url>] [--tenant <id>] [--run-id <id>] [--lease-id <id>] [--lease-backend <backend>] [--force] [--no-login]`,listUsageOverride:`connect`,positionalArgs:[`provider?`],allowedFlags:`remoteConfig.daemonBaseUrl.tenant.runId.leaseId.leaseBackend.providerApp.providerOsVersion.providerProject.providerBuild.providerSessionName.providerDeviceOrientation.providerGeoLocation.providerTimezone.providerLanguage.providerLocale.providerNetworkProfile.providerCustomNetwork.providerNoResignApp.awsProjectArn.awsDeviceArn.awsAppArn.awsRegion.awsInteractionMode.force.noLogin`.split(`.`),supportedFlags:[`stateDir`,`daemonAuthToken`,`session`,`platform`,`device`,...t,`launchUrl`]},connection:{text:{summary:`Inspect the saved remote connection`,description:`Inspect active remote connection state`},usageOverride:`connection status`,listUsageOverride:`connection`,positionalArgs:[`status`],supportedFlags:[`remoteConfig`,`stateDir`,`session`]},disconnect:{text:{summary:`Clear remote connection state and release leases`,description:`Disconnect remote daemon state, stop owned Metro companion, and release lease`},listUsageOverride:`disconnect`,allowedFlags:[`shutdown`],supportedFlags:[`remoteConfig`,`stateDir`,`session`]},mcp:{text:{summary:`Start MCP server`,description:`Start the official stdio MCP server. It exposes structured command tools backed by the agent-device client.`}},proxy:{text:{summary:`Expose a local daemon through an HTTP tunnel`,description:`Expose the local daemon HTTP contract through a tunnel-friendly reverse proxy.
2
2
 
3
3
  Run this on the host that has access to simulators/devices, expose the printed local proxy URL through a tunnel, then point another machine at the tunnel URL with connect proxy.
4
4
 
@@ -1 +1 @@
1
- import{o as e}from"./png-worker-contract.js";import{a as t}from"./registry.js";var n=e({runCommand:()=>a});const r=t(),i=new Map(r.map(e=>[e.name,e]));async function a(e,t,n){return await o(t).invoke(e,n)}function o(e){return i.get(e)}export{a as n,n as t};
1
+ import{n as e}from"./rolldown-runtime.js";import{a as t}from"./registry.js";var n=e({runCommand:()=>a});const r=t(),i=new Map(r.map(e=>[e.name,e]));async function a(e,t,n){return await o(t).invoke(e,n)}function o(e){return i.get(e)}export{a as n,n as t};
@@ -0,0 +1 @@
1
+ const e=new Set;function t(t={}){let r=new Map,i=t.now??(()=>Date.now()),a=e=>{e?.timer&&clearTimeout(e.timer)},o={get(e){let t=r.get(e);if(t){if(t.expiresAt!==void 0&&t.expiresAt<=i()){o.delete(e);return}return t.value}},set(e,o){a(r.get(e));let s=n(t.ttlMs,i),c={value:o,expiresAt:s};t.scheduleExpiry&&s!==void 0&&(c.timer=setTimeout(()=>{r.delete(e)},Math.max(0,s-i())),c.timer.unref?.()),r.set(e,c)},delete(e){let t=r.get(e);return t?(a(t),r.delete(e)):!1},clear(){for(let e of r.values())a(e);r.clear()}};return e.add(o.clear),o}function n(e,t){if(!(e===void 0||!Number.isFinite(e)))return t()+Math.max(0,e)}const r=18e4,i=6e4,a=6e4,o=2e4,s=12e4,c=1e4,l=15e3,u=2e4,d=15e3,f=2e4,p=1e3,m=5e3;export{f as a,p as c,u as d,l as f,s as i,m as l,r as n,i as o,t as p,o as r,c as s,a as t,d as u};
@@ -1 +1 @@
1
- import{_ as e}from"./sdk-contracts.js";import{_ as t,a as n,b as r,c as i}from"./sdk-device.js";import{Tn as a,Xt as o,Yt as s,t as c,wn as l,xn as u}from"./prepare-kind.js";import{_ as d}from"./screenshot-result.js";import{t as f}from"./sdk-remote-config.js";import{S as p,j as m}from"./sdk-android-adb.js";import{v as h}from"./url.js";import{a as g,i as _}from"./client-metro.js";import{n as ee}from"./metro2.js";import{n as te,r as v,t as ne}from"./remote-config2.js";import{a as y,i as b}from"./src3.js";import x from"node:path";import S from"node:crypto";import C from"node:fs";const w={slug:`react-devtools-companion`,runArg:h,displayName:`React DevTools companion`};async function T(e){return await _({...e,definition:w,localBaseUrl:e.localBaseUrl??`http://127.0.0.1:8097`,registerPath:`/api/react-devtools/companion/register`,unregisterPath:`/api/react-devtools/companion/unregister`,devicePort:e.devicePort??8097})}async function E(e){return await g({...e,definition:w})}function D(e){return e===`android-instance`||e===`ios-instance`}function re(e){return[`exec`,`--yes`,`--package`,`agent-cdp@1.6.1`,`--`,`agent-cdp`,...e]}function ie(e){return e.some(e=>e===`--url`||e.startsWith(`--url=`))}function O(e){return e[0]===`target`&&(e[1]===`list`||e[1]===`select`)&&!ie(e)}function ae(e){let t=new URL(e);t.search=``,t.hash=``;let n=t.pathname.replace(/\/+$/,``);return t.pathname=n.endsWith(`/index.bundle`)?n.slice(0,-13)||`/`:n||`/`,t.toString().replace(/\/+$/,``)}function oe(e){let t=e?.metroPublicBaseUrl?.trim();return t?ae(t):null}function se(t,n={}){if(!O(t)||!n.flags?.metroProxyBaseUrl||!D(n.flags.leaseBackend))return t;let r=oe(n.flags);if(!r)throw new e(`INVALID_ARGS`,`cdp remote bridge target discovery requires a React Native dev-server public base URL.`,{hint:`Include metroPublicBaseUrl in the remote config so cdp can reach the local or tunneled Metro/Re.Pack CDP endpoint without bridge proxy authentication.`});return[...t,`--url`,r]}async function ce(e,t={}){return(await p(`npm`,re(se(e,t)),{cwd:t.cwd??process.cwd(),env:t.env??process.env,allowFailure:!0,onStdoutChunk:e=>{process.stdout.write(e)},onStderrChunk:e=>{process.stderr.write(e)}})).exitCode}function k(e){let t=P(e);if(!C.existsSync(t))return null;let n;try{n=JSON.parse(C.readFileSync(t,`utf8`))}catch(t){return z(e,t),null}return me(n)?n:(z(e),null)}function A(e){let t=P({stateDir:e.stateDir,session:e.state.session});C.mkdirSync(x.dirname(t),{recursive:!0}),R(t,e.state),R(F(e.stateDir),{session:e.state.session})}function j(e){return{baseUrl:pe(e.daemonBaseUrl),transport:e.daemonTransport,serverMode:e.daemonServerMode}}function M(e){C.rmSync(P(e),{force:!0});let t=F(e.stateDir);L(e.stateDir)===e.session&&C.rmSync(t,{force:!0})}function le(t){let n=t.validateRemoteConfigHash??!0,r=t.remoteConfig?te({configPath:t.remoteConfig,cwd:t.cwd,env:t.env}):void 0,i=k(t)??(t.allowActiveFallback?I({stateDir:t.stateDir}):null);if(!i||r&&i.remoteConfigPath!==r)return null;if(n&&N(i.remoteConfigPath)!==i.remoteConfigHash)throw new e(`INVALID_ARGS`,`Active remote connection config changed. Run agent-device connect --force to refresh it.`,{remoteConfig:i.remoteConfigPath});let o=fe(i,t),s=u(i);return{runtime:i.runtime,connection:a(s),flags:{...o,remoteConfig:i.remoteConfigPath,daemonBaseUrl:i.daemon?.baseUrl??o.daemonBaseUrl,daemonAuthToken:o.daemonAuthToken,daemonTransport:i.daemon?.transport??o.daemonTransport,daemonServerMode:i.daemon?.serverMode??o.daemonServerMode,...l(s),sessionIsolation:`tenant`,session:i.session,platform:i.platform??o.platform,target:i.target??o.target}}}function ue(e){return a(u(e))}function N(t){try{return S.createHash(`sha256`).update(C.readFileSync(t)).digest(`hex`)}catch(n){throw new e(`INVALID_ARGS`,`Remote config file not found: ${t}`,{cause:n instanceof Error?n.message:String(n)})}}function P(e){return x.join(e.stateDir,`remote-connections`,`${B(e.session)}.json`)}function F(e){return x.join(e,`remote-connections`,`.active-session.json`)}function I(e){let t=L(e.stateDir);return t?k({stateDir:e.stateDir,session:t}):null}function L(e){let t=F(e);if(C.existsSync(t))try{let e=JSON.parse(C.readFileSync(t,`utf8`));return typeof e.session==`string`?e.session:void 0}catch{return}}function de(e){if(e)return S.createHash(`sha256`).update(e).digest(`hex`).slice(0,12)}function fe(e,t){try{return v({configPath:e.remoteConfigPath,cwd:t.cwd,env:t.env}).profile}catch(e){if(t.validateRemoteConfigHash===!1)return{};throw e}}function R(e,t){let n=`${e}.${process.pid}.${S.randomUUID()}.tmp`;try{C.writeFileSync(n,`${JSON.stringify(t,null,2)}\n`,{encoding:`utf8`,mode:384}),C.renameSync(n,e)}catch(e){throw C.rmSync(n,{force:!0}),e}}function pe(e){if(!e)return;let t=new URL(e);t.username=``,t.password=``;for(let e of[...t.searchParams.keys()])/(auth|key|password|secret|token)/i.test(e)&&t.searchParams.delete(e);return t.toString().replace(/\/+$/,``)}function z(e,t){m({level:`warn`,phase:`remote_connection_state_invalid`,data:{session:e.session,cause:t instanceof Error?t.message:t?String(t):void 0}}),M(e)}function B(e){let t=e.replaceAll(/[^a-zA-Z0-9._-]/g,`_`);return t?t===e?t:`${t}-${S.createHash(`sha256`).update(e).digest(`hex`).slice(0,8)}`:`default`}function me(e){if(!e||typeof e!=`object`||Array.isArray(e))return!1;let t=e;return t.version===1&&he(t,[`session`,`remoteConfigPath`,`remoteConfigHash`,`tenant`,`runId`,`connectedAt`,`updatedAt`])&&ge(t,[`leaseId`,`leaseBackend`,`leaseProvider`,`deviceKey`,`clientId`])&&_e(t.daemon)}function he(e,t){return t.every(t=>typeof e[t]==`string`)}function ge(e,t){return t.every(t=>e[t]===void 0||typeof e[t]==`string`)}function _e(e){return e===void 0?!0:!e||typeof e!=`object`||Array.isArray(e)?!1:ve(e)}function ve(e){let t=e;return(t.baseUrl===void 0||typeof t.baseUrl==`string`)&&(t.authToken===void 0||typeof t.authToken==`string`)&&(t.transport===void 0||typeof t.transport==`string`)&&(t.serverMode===void 0||typeof t.serverMode==`string`)}const ye=f.map(e=>e.key);function V(e){let t={};for(let n of ye){let r=e[n];r!==void 0&&(t[n]=r)}return t}function be(e){return e.remoteConfig?{...V(v({configPath:e.remoteConfig,cwd:e.cwd,env:e.env}).profile),remoteConfig:e.remoteConfig}:{}}function xe(e){return e===`cloud`||e===`proxy`||H(e)}function H(e){return e===`limrun`||y(e)}function Se(){return[`cloud`,`proxy`,b.browserStack,b.awsDeviceFarm,`limrun`].join(`, `)}function U(e){return!H(e)}function Ce(e){return e===`proxy`?`proxy`:H(e)?`direct-device-provider`:`remote-provider`}function W(e){return{providerDeviceOrientation:e.providerDeviceOrientation,providerGeoLocation:e.providerGeoLocation,providerTimezone:e.providerTimezone,providerLanguage:e.providerLanguage,providerLocale:e.providerLocale,providerNetworkProfile:e.providerNetworkProfile,providerCustomNetwork:e.providerCustomNetwork,providerNoResignApp:e.providerNoResignApp}}function we(e){return{metroProjectRoot:e.metroProjectRoot,metroKind:e.metroKind,metroPublicBaseUrl:e.metroPublicBaseUrl,metroProxyBaseUrl:e.metroProxyBaseUrl,metroPreparePort:e.metroPreparePort,metroListenHost:e.metroListenHost,metroStatusHost:e.metroStatusHost,metroStartupTimeoutMs:e.metroStartupTimeoutMs,metroProbeTimeoutMs:e.metroProbeTimeoutMs,metroRuntimeFile:e.metroRuntimeFile,metroNoReuseExisting:e.metroNoReuseExisting,metroNoInstallDeps:e.metroNoInstallDeps,launchUrl:e.launchUrl}}const G=new Set([`artifacts`,`connect`,`connection`,`close`,`disconnect`,`metro`,`session`]),K=new Set([`open`]),Te=new Set([o.open,o.install,o.reinstall,s.installSource]);async function Ee(t){let{command:n,flags:r,client:i}=t;if(!r.remoteConfig)return{flags:r,runtime:t.runtime};let a=d(r.stateDir).baseDir,o=v({configPath:r.remoteConfig,cwd:process.cwd(),env:process.env}),s={...V(o.profile),...r,remoteConfig:o.resolvedPath},c=k({stateDir:a,session:s.session??`default`});if(c&&c.remoteConfigPath!==o.resolvedPath)throw new e(`INVALID_ARGS`,`A different remote connection is already active for this session. Run connect --force or disconnect before using a different --remote-config.`,{session:c.session,activeRemoteConfig:c.remoteConfigPath,requestedRemoteConfig:o.resolvedPath});let l=c??Ye(s,o.resolvedPath,o.profile),u={...s,session:l.session},f=Je(l.runtime,u.platform)??t.runtime,p=l,m=!c,h,g=Pe(p);if(g.shouldAllocate(n)){let e=await je({command:n,client:i,state:l,nextState:p,nextFlags:u,policy:g});p=e.state,m||=e.changed,h=e.acquiredLeaseForCleanup}let _=await De({command:n,flags:u,client:i,state:l,nextState:p,runtime:f,positionals:t.positionals,batchSteps:t.batchSteps,forceRuntimePrepare:t.forceRuntimePrepare});return p=_.state,f=_.runtime,m||=_.changed,await Ae({changed:m,stateDir:a,state:p,client:i,acquiredLeaseForCleanup:h,preparedMetroCleanupOnFailure:_.preparedMetroCleanupOnFailure,metroCleanupToStop:_.metroCleanupToStop}),{flags:{...u,session:p.session,leaseId:p.leaseId,leaseBackend:p.leaseBackend,platform:p.platform??u.platform,target:p.target??u.target},runtime:f,connection:ue(p)}}async function De(t){let{command:n,flags:r,state:i,client:a}=t,o=Oe(t.nextState,r),s=t.runtime;if(!Ke(n,r,t.batchSteps,t.positionals)||!Z(r)||!ke(t.forceRuntimePrepare,s,r.platform))return{state:o,runtime:s,changed:!1};if(!o.leaseId)throw new e(`INVALID_ARGS`,`${n} requires a resolved remote lease before Metro runtime can be prepared.`);let c=await Re(r,a,i.remoteConfigPath,i.session,{tenantId:i.tenant,runId:i.runId,leaseId:o.leaseId}),l=!qe(o.metro,c.cleanup);return o={...o,runtime:c.runtime,metro:c.cleanup,updatedAt:new Date().toISOString()},{state:o,runtime:c.runtime,changed:!0,metroCleanupToStop:l?t.nextState.metro:void 0,preparedMetroCleanupOnFailure:l?c.cleanup:void 0}}function Oe(e,t){return e.leaseId||!t.leaseId?e:{...e,leaseId:t.leaseId,leaseBackend:t.leaseBackend}}function ke(e,t,n){return e===!0||!t||!Q(t,n)}async function Ae(e){if(e.changed)try{A({stateDir:e.stateDir,state:e.state})}catch(t){throw await q(e.preparedMetroCleanupOnFailure),await We(e.client,e.state,e.acquiredLeaseForCleanup),t}await q(e.metroCleanupToStop)}async function je(e){let{command:t,client:n,state:r,nextFlags:i,policy:a}=e,o=r.leaseBackend??X(i),s=e.nextState,c=await a.resolveLeaseState({command:t,client:n,state:s,flags:i,leaseBackend:o});s=c.state,c.device&&$(i,c.device);let l=s.leaseBackend??o??Ge(i,t);tt(r,i,l);let u=await Xe(n,s,l,a,i),d=u.lease;return i.leaseId=d.leaseId,i.leaseBackend=l,i.platform=s.platform??i.platform,i.target=s.target??i.target,Me(s,d,l)?{state:s,changed:!1,acquiredLeaseForCleanup:u.acquired?d:void 0}:{state:Ne(s,d,l,i),changed:!0,acquiredLeaseForCleanup:u.acquired?d:void 0}}function Me(e,t,n){return e.leaseId===t.leaseId&&e.leaseBackend===n&&e.deviceKey===(t.deviceKey??e.deviceKey)}function Ne(e,t,n,r){return{...e,leaseId:t.leaseId,leaseBackend:n,leaseProvider:t.leaseProvider??e.leaseProvider,clientId:t.clientId??e.clientId,deviceKey:t.deviceKey??e.deviceKey,platform:e.platform??r.platform,target:e.target??r.target,updatedAt:new Date().toISOString()}}function Pe(e){return e.leaseProvider===`proxy`?Ie:y(e.leaseProvider)?Le:Fe}const Fe={shouldAllocate:e=>!G.has(e),ttlMs:()=>void 0,resolveLeaseState:async e=>({state:e.state})},Ie={shouldAllocate:e=>e!==`devices`&&!G.has(e),ttlMs:()=>3e5,resolveLeaseState:Ze},Le={shouldAllocate:e=>!G.has(e),ttlMs:()=>6e5,resolveLeaseState:async e=>({state:e.state})};async function Re(t,n,r,i,a){if(!t.metroProjectRoot&&!t.metroPublicBaseUrl&&!t.metroProxyBaseUrl)return{};if(t.platform!==`ios`&&t.platform!==`android`)throw new e(`INVALID_ARGS`,`Deferred Metro preparation requires platform "ios" or "android".`);if(!t.metroPublicBaseUrl&&!t.metroProxyBaseUrl)throw new e(`INVALID_ARGS`,`Deferred Metro preparation requires metroPublicBaseUrl or metroProxyBaseUrl when Metro settings are provided.`);let o=await n.metro.prepare({projectRoot:t.metroProjectRoot,kind:c(t.metroKind),publicBaseUrl:t.metroPublicBaseUrl,proxyBaseUrl:t.metroProxyBaseUrl,bearerToken:t.metroBearerToken,bridgeScope:a,launchUrl:t.launchUrl,companionProfileKey:r,companionConsumerKey:i,port:t.metroPreparePort,listenHost:t.metroListenHost,statusHost:t.metroStatusHost,startupTimeoutMs:t.metroStartupTimeoutMs,probeTimeoutMs:t.metroProbeTimeoutMs,reuseExisting:t.metroNoReuseExisting?!1:void 0,installDependenciesIfNeeded:t.metroNoInstallDeps?!1:void 0,runtimeFilePath:t.metroRuntimeFile});return{runtime:t.platform===`ios`?o.iosRuntime:o.androidRuntime,cleanup:t.metroProxyBaseUrl?{projectRoot:o.projectRoot,profileKey:r,consumerKey:i}:void 0}}async function q(e){if(e)try{await ee(e)}catch{}}async function ze(e){try{await E({projectRoot:process.cwd(),stateDir:e.stateDir,profileKey:e.state.remoteConfigPath,consumerKey:e.state.session})}catch{}}async function J(e,t,n){return t.leaseId?await e.leases.release({tenant:t.tenant,runId:t.runId,leaseId:t.leaseId,leaseBackend:t.leaseBackend,daemonBaseUrl:t.daemon?.baseUrl,daemonAuthToken:n,daemonTransport:t.daemon?.transport,daemonServerMode:t.daemon?.serverMode,leaseProvider:t.leaseProvider,clientId:t.clientId,deviceKey:t.deviceKey}):{released:!1}}function Be(e){let t=Ve(e.previous,e.cwd,e.env);return t?{canAuthenticate:!0,daemonAuthToken:t}:e.previous.daemon?.baseUrl===e.nextDaemonBaseUrl?{canAuthenticate:!0,daemonAuthToken:e.ambientDaemonAuthToken}:{canAuthenticate:!1}}function Ve(e,t,n){try{let{profile:r}=ne({configPath:e.remoteConfigPath,cwd:t,env:n});return r.daemonAuthToken&&He(e,r.daemonBaseUrl)?r.daemonAuthToken:void 0}catch{return}}function He(e,t){let n=j({daemonBaseUrl:t})?.baseUrl;return n!==void 0&&n===e.daemon?.baseUrl}async function Ue(e,t,n){if(!t.leaseId)return;let r=Be({previous:t,nextDaemonBaseUrl:n.nextDaemonBaseUrl,ambientDaemonAuthToken:n.ambientDaemonAuthToken,cwd:n.cwd,env:n.env});if(!r.canAuthenticate)return Y(t,`no credential known to belong to that endpoint was available`);try{await J(e,t,r.daemonAuthToken);return}catch{return Y(t,`the release request failed`)}}function Y(e,t){return{status:`unreleased`,message:`Could not release the previous lease ${e.leaseId} (tenant ${e.tenant}, run ${e.runId}) at ${e.daemon?.baseUrl??`its daemon`}: ${t}. It was left in place — release it manually if it is still active.`}}async function We(e,t,n){if(n)try{await e.leases.release({tenant:t.tenant,runId:t.runId,leaseId:n.leaseId,leaseBackend:t.leaseBackend??n.backend,leaseProvider:t.leaseProvider??n.leaseProvider,clientId:t.clientId??n.clientId,deviceKey:t.deviceKey??n.deviceKey})}catch{}}function X(e){if(e.leaseBackend)return e.leaseBackend;if(e.platform===`android`)return`android-instance`;if(e.platform===`ios`)return`ios-instance`}function Ge(t,n){let r=X(t);if(r)return r;throw new e(`INVALID_ARGS`,`${n} requires --platform ios|android or --lease-backend when the remote connection has not resolved a lease yet.`)}function Ke(e,t,n,r=[]){return e===`cdp`?O(r)&&!!t.metroPublicBaseUrl:K.has(e)?!0:e!==`batch`||!n?!1:n.some(e=>{let t=e.command.trim().toLowerCase();return K.has(t)&&e.runtime===void 0})}function Z(e){let t=e.metroKind;return!!(e.metroPublicBaseUrl||e.metroProxyBaseUrl||e.metroProjectRoot||t)}function Q(e,t){return!e.platform||!t||t!==`ios`&&t!==`android`?!0:e.platform===t}function qe(e,t){return e?.projectRoot===t?.projectRoot&&e?.profileKey===t?.profileKey&&e?.consumerKey===t?.consumerKey}function Je(e,t){if(e)return Q(e,t)?e:void 0}function Ye(t,n,r={}){if(!t.tenant)throw new e(`INVALID_ARGS`,`remote command requires tenant in remote config or via --tenant <id>.`);if(!t.runId)throw new e(`INVALID_ARGS`,`remote command requires runId in remote config or via --run-id <id>.`);if(!t.daemonBaseUrl&&U(r.leaseProvider))throw new e(`INVALID_ARGS`,`remote command requires daemonBaseUrl in remote config, config, env, or --daemon-base-url.`);let i=new Date().toISOString();return{version:1,session:t.session??`default`,remoteConfigPath:n,remoteConfigHash:N(n),daemon:j(t),tenant:t.tenant,runId:t.runId,leaseId:t.leaseId,leaseBackend:t.leaseBackend??X(t),leaseProvider:r.leaseProvider,clientId:r.clientId,deviceKey:r.deviceKey,platform:t.platform,target:t.target,connectedAt:i,updatedAt:i}}async function Xe(e,t,n,r,i){if(t.leaseId&&t.leaseBackend===n){let i=await nt(e,t.leaseId,{tenant:t.tenant,runId:t.runId,leaseBackend:n,leaseProvider:t.leaseProvider,clientId:t.clientId,deviceKey:t.deviceKey,ttlMs:r.ttlMs(t)});if(i)return{lease:i,acquired:!1}}return{lease:await e.leases.allocate({tenant:t.tenant,runId:t.runId,leaseBackend:n,leaseProvider:t.leaseProvider,clientId:t.clientId,deviceKey:t.deviceKey,ttlMs:r.ttlMs(t),platform:t.platform??i.platform,target:t.target??i.target,device:i.device,udid:i.udid,serial:i.serial,providerApp:i.providerApp,providerOsVersion:i.providerOsVersion,providerProject:i.providerProject,providerBuild:i.providerBuild,providerSessionName:i.providerSessionName,...W(i),awsProjectArn:i.awsProjectArn,awsDeviceArn:i.awsDeviceArn,awsAppArn:i.awsAppArn,awsRegion:i.awsRegion,awsInteractionMode:i.awsInteractionMode}),acquired:!0}}async function Ze(t){if(!Te.has(t.command)){if(t.state.leaseId&&t.state.deviceKey)return{state:t.state};throw new e(`INVALID_ARGS`,`No active proxy device lease for this session; run open first.`)}let n=await Qe(t.client,t.flags),r=$e(n);return{state:{...t.state,deviceKey:r,leaseBackend:t.state.leaseBackend??t.leaseBackend??et(n),platform:t.state.platform??n.platform,target:t.state.target??n.target,updatedAt:new Date().toISOString()},device:n}}function $(e,t){if(e.platform=t.platform,e.target=t.target??e.target,i(t)){e.udid=t.id;return}t.platform===`android`&&(e.serial=t.id)}async function Qe(e,t){return await r((await e.devices.list({platform:t.platform,target:t.target,device:t.device,udid:t.udid,serial:t.serial,iosSimulatorDeviceSet:t.iosSimulatorDeviceSet,androidDeviceAllowlist:t.androidDeviceAllowlist})).map(e=>({...n(e.platform),id:e.id,name:e.name,kind:e.kind,target:e.target,booted:e.booted})),{platform:t.platform,target:t.target,deviceName:t.device,udid:t.udid,serial:t.serial})}function $e(e){return`${t(e)}:${e.target??`mobile`}:${e.id}`}function et(e){if(i(e))return`ios-instance`;if(e.platform===`android`)return`android-instance`}function tt(t,n,r){if(t.leaseBackend&&t.leaseBackend!==r)throw new e(`INVALID_ARGS`,`Active remote connection is already bound to a different lease backend. Re-run connect --force to replace it.`,{session:t.session,leaseBackend:t.leaseBackend});if(t.platform&&n.platform&&t.platform!==n.platform)throw new e(`INVALID_ARGS`,`Active remote connection is already bound to a different platform. Re-run connect --force to replace it.`,{session:t.session,platform:t.platform});if(t.target&&n.target&&t.target!==n.target)throw new e(`INVALID_ARGS`,`Active remote connection is already bound to a different target. Re-run connect --force to replace it.`,{session:t.session,target:t.target})}async function nt(e,t,n){try{return await e.leases.heartbeat({tenant:n.tenant,runId:n.runId,leaseId:t,leaseBackend:n.leaseBackend,leaseProvider:n.leaseProvider,clientId:n.clientId,deviceKey:n.deviceKey,ttlMs:n.ttlMs})}catch(e){if(rt(e))return;throw e}}function rt(t){return!(t instanceof e)||t.code!==`UNAUTHORIZED`?!1:t.details?.reason===`LEASE_NOT_FOUND`||t.details?.reason===`LEASE_EXPIRED`||t.details?.reason===`LEASE_REVOKED`}export{A as C,E as D,T as E,le as S,D as T,de as _,X as a,k as b,W as c,Ce as d,U as f,j as g,be as h,J as i,we as l,V as m,Ee as n,q as o,xe as p,Ue as r,ze as s,Z as t,Se as u,N as v,ce as w,M as x,I as y};
1
+ import{_ as e}from"./sdk-contracts.js";import{_ as t,a as n,b as r,c as i}from"./sdk-device.js";import{Kt as a,Xt as o,Yt as s,t as c}from"./prepare-kind.js";import{_ as l}from"./screenshot-result.js";import{n as u,t as d}from"./command-catalog.js";import{t as f}from"./sdk-remote-config.js";import{A as p,x as m}from"./sdk-android-adb.js";import{v as h}from"./url.js";import{a as g,i as _}from"./client-metro.js";import{n as ee}from"./metro2.js";import{n as te,r as v,t as ne}from"./remote-config2.js";import{a as y,i as b}from"./src2.js";import x from"node:path";import S from"node:crypto";import C from"node:fs";const w={slug:`react-devtools-companion`,runArg:h,displayName:`React DevTools companion`};async function T(e){return await _({...e,definition:w,localBaseUrl:e.localBaseUrl??`http://127.0.0.1:8097`,registerPath:`/api/react-devtools/companion/register`,unregisterPath:`/api/react-devtools/companion/unregister`,devicePort:e.devicePort??8097})}async function E(e){return await g({...e,definition:w})}function D(e){return e===`android-instance`||e===`ios-instance`}function re(e){return[`exec`,`--yes`,`--package`,`agent-cdp@1.6.1`,`--`,`agent-cdp`,...e]}function ie(e){return e.some(e=>e===`--url`||e.startsWith(`--url=`))}function O(e){return e[0]===`target`&&(e[1]===`list`||e[1]===`select`)&&!ie(e)}function ae(e){let t=new URL(e);t.search=``,t.hash=``;let n=t.pathname.replace(/\/+$/,``);return t.pathname=n.endsWith(`/index.bundle`)?n.slice(0,-13)||`/`:n||`/`,t.toString().replace(/\/+$/,``)}function oe(e){let t=e?.metroPublicBaseUrl?.trim();return t?ae(t):null}function se(t,n={}){if(!O(t)||!n.flags?.metroProxyBaseUrl||!D(n.flags.leaseBackend))return t;let r=oe(n.flags);if(!r)throw new e(`INVALID_ARGS`,`cdp remote bridge target discovery requires a React Native dev-server public base URL.`,{hint:`Include metroPublicBaseUrl in the remote config so cdp can reach the local or tunneled Metro/Re.Pack CDP endpoint without bridge proxy authentication.`});return[...t,`--url`,r]}async function ce(e,t={}){return(await m(`npm`,re(se(e,t)),{cwd:t.cwd??process.cwd(),env:t.env??process.env,allowFailure:!0,onStdoutChunk:e=>{process.stdout.write(e)},onStderrChunk:e=>{process.stderr.write(e)}})).exitCode}function k(e){let t=P(e);if(!C.existsSync(t))return null;let n;try{n=JSON.parse(C.readFileSync(t,`utf8`))}catch(t){return z(e,t),null}return me(n)?n:(z(e),null)}function A(e){let t=P({stateDir:e.stateDir,session:e.state.session});C.mkdirSync(x.dirname(t),{recursive:!0}),R(t,e.state),R(F(e.stateDir),{session:e.state.session})}function j(e){return{baseUrl:pe(e.daemonBaseUrl),transport:e.daemonTransport,serverMode:e.daemonServerMode}}function M(e){C.rmSync(P(e),{force:!0});let t=F(e.stateDir);L(e.stateDir)===e.session&&C.rmSync(t,{force:!0})}function le(t){let n=t.validateRemoteConfigHash??!0,r=t.remoteConfig?te({configPath:t.remoteConfig,cwd:t.cwd,env:t.env}):void 0,i=k(t)??(t.allowActiveFallback?I({stateDir:t.stateDir}):null);if(!i||r&&i.remoteConfigPath!==r)return null;if(n&&N(i.remoteConfigPath)!==i.remoteConfigHash)throw new e(`INVALID_ARGS`,`Active remote connection config changed. Run agent-device connect --force to refresh it.`,{remoteConfig:i.remoteConfigPath});let c=fe(i,t),l=a(i);return{runtime:i.runtime,connection:o(l),flags:{...c,remoteConfig:i.remoteConfigPath,daemonBaseUrl:i.daemon?.baseUrl??c.daemonBaseUrl,daemonAuthToken:c.daemonAuthToken,daemonTransport:i.daemon?.transport??c.daemonTransport,daemonServerMode:i.daemon?.serverMode??c.daemonServerMode,...s(l),sessionIsolation:`tenant`,session:i.session,platform:i.platform??c.platform,target:i.target??c.target}}}function ue(e){return o(a(e))}function N(t){try{return S.createHash(`sha256`).update(C.readFileSync(t)).digest(`hex`)}catch(n){throw new e(`INVALID_ARGS`,`Remote config file not found: ${t}`,{cause:n instanceof Error?n.message:String(n)})}}function P(e){return x.join(e.stateDir,`remote-connections`,`${B(e.session)}.json`)}function F(e){return x.join(e,`remote-connections`,`.active-session.json`)}function I(e){let t=L(e.stateDir);return t?k({stateDir:e.stateDir,session:t}):null}function L(e){let t=F(e);if(C.existsSync(t))try{let e=JSON.parse(C.readFileSync(t,`utf8`));return typeof e.session==`string`?e.session:void 0}catch{return}}function de(e){if(e)return S.createHash(`sha256`).update(e).digest(`hex`).slice(0,12)}function fe(e,t){try{return v({configPath:e.remoteConfigPath,cwd:t.cwd,env:t.env}).profile}catch(e){if(t.validateRemoteConfigHash===!1)return{};throw e}}function R(e,t){let n=`${e}.${process.pid}.${S.randomUUID()}.tmp`;try{C.writeFileSync(n,`${JSON.stringify(t,null,2)}\n`,{encoding:`utf8`,mode:384}),C.renameSync(n,e)}catch(e){throw C.rmSync(n,{force:!0}),e}}function pe(e){if(!e)return;let t=new URL(e);t.username=``,t.password=``;for(let e of[...t.searchParams.keys()])/(auth|key|password|secret|token)/i.test(e)&&t.searchParams.delete(e);return t.toString().replace(/\/+$/,``)}function z(e,t){p({level:`warn`,phase:`remote_connection_state_invalid`,data:{session:e.session,cause:t instanceof Error?t.message:t?String(t):void 0}}),M(e)}function B(e){let t=e.replaceAll(/[^a-zA-Z0-9._-]/g,`_`);return t?t===e?t:`${t}-${S.createHash(`sha256`).update(e).digest(`hex`).slice(0,8)}`:`default`}function me(e){if(!e||typeof e!=`object`||Array.isArray(e))return!1;let t=e;return t.version===1&&he(t,[`session`,`remoteConfigPath`,`remoteConfigHash`,`tenant`,`runId`,`connectedAt`,`updatedAt`])&&ge(t,[`leaseId`,`leaseBackend`,`leaseProvider`,`deviceKey`,`clientId`])&&_e(t.daemon)}function he(e,t){return t.every(t=>typeof e[t]==`string`)}function ge(e,t){return t.every(t=>e[t]===void 0||typeof e[t]==`string`)}function _e(e){return e===void 0?!0:!e||typeof e!=`object`||Array.isArray(e)?!1:ve(e)}function ve(e){let t=e;return(t.baseUrl===void 0||typeof t.baseUrl==`string`)&&(t.authToken===void 0||typeof t.authToken==`string`)&&(t.transport===void 0||typeof t.transport==`string`)&&(t.serverMode===void 0||typeof t.serverMode==`string`)}const ye=f.map(e=>e.key);function V(e){let t={};for(let n of ye){let r=e[n];r!==void 0&&(t[n]=r)}return t}function be(e){return e.remoteConfig?{...V(v({configPath:e.remoteConfig,cwd:e.cwd,env:e.env}).profile),remoteConfig:e.remoteConfig}:{}}function xe(e){return e===`cloud`||e===`proxy`||H(e)}function H(e){return e===`limrun`||y(e)}function Se(){return[`cloud`,`proxy`,b.browserStack,b.awsDeviceFarm,`limrun`].join(`, `)}function U(e){return!H(e)}function Ce(e){return e===`proxy`?`proxy`:H(e)?`direct-device-provider`:`remote-provider`}function W(e){return{providerDeviceOrientation:e.providerDeviceOrientation,providerGeoLocation:e.providerGeoLocation,providerTimezone:e.providerTimezone,providerLanguage:e.providerLanguage,providerLocale:e.providerLocale,providerNetworkProfile:e.providerNetworkProfile,providerCustomNetwork:e.providerCustomNetwork,providerNoResignApp:e.providerNoResignApp}}function we(e){return{metroProjectRoot:e.metroProjectRoot,metroKind:e.metroKind,metroPublicBaseUrl:e.metroPublicBaseUrl,metroProxyBaseUrl:e.metroProxyBaseUrl,metroPreparePort:e.metroPreparePort,metroListenHost:e.metroListenHost,metroStatusHost:e.metroStatusHost,metroStartupTimeoutMs:e.metroStartupTimeoutMs,metroProbeTimeoutMs:e.metroProbeTimeoutMs,metroRuntimeFile:e.metroRuntimeFile,metroNoReuseExisting:e.metroNoReuseExisting,metroNoInstallDeps:e.metroNoInstallDeps,launchUrl:e.launchUrl}}const G=new Set([`artifacts`,`connect`,`connection`,`close`,`disconnect`,`metro`,`session`]),K=new Set([`open`]),Te=new Set([u.open,u.install,u.reinstall,d.installSource]);async function Ee(t){let{command:n,flags:r,client:i}=t;if(!r.remoteConfig)return{flags:r,runtime:t.runtime};let a=l(r.stateDir).baseDir,o=v({configPath:r.remoteConfig,cwd:process.cwd(),env:process.env}),s={...V(o.profile),...r,remoteConfig:o.resolvedPath},c=k({stateDir:a,session:s.session??`default`});if(c&&c.remoteConfigPath!==o.resolvedPath)throw new e(`INVALID_ARGS`,`A different remote connection is already active for this session. Run connect --force or disconnect before using a different --remote-config.`,{session:c.session,activeRemoteConfig:c.remoteConfigPath,requestedRemoteConfig:o.resolvedPath});let u=c??Ye(s,o.resolvedPath,o.profile),d={...s,session:u.session},f=Je(u.runtime,d.platform)??t.runtime,p=u,m=!c,h,g=Pe(p);if(g.shouldAllocate(n)){let e=await je({command:n,client:i,state:u,nextState:p,nextFlags:d,policy:g});p=e.state,m||=e.changed,h=e.acquiredLeaseForCleanup}let _=await De({command:n,flags:d,client:i,state:u,nextState:p,runtime:f,positionals:t.positionals,batchSteps:t.batchSteps,forceRuntimePrepare:t.forceRuntimePrepare});return p=_.state,f=_.runtime,m||=_.changed,await Ae({changed:m,stateDir:a,state:p,client:i,acquiredLeaseForCleanup:h,preparedMetroCleanupOnFailure:_.preparedMetroCleanupOnFailure,metroCleanupToStop:_.metroCleanupToStop}),{flags:{...d,session:p.session,leaseId:p.leaseId,leaseBackend:p.leaseBackend,platform:p.platform??d.platform,target:p.target??d.target},runtime:f,connection:ue(p)}}async function De(t){let{command:n,flags:r,state:i,client:a}=t,o=Oe(t.nextState,r),s=t.runtime;if(!Ke(n,r,t.batchSteps,t.positionals)||!Z(r)||!ke(t.forceRuntimePrepare,s,r.platform))return{state:o,runtime:s,changed:!1};if(!o.leaseId)throw new e(`INVALID_ARGS`,`${n} requires a resolved remote lease before Metro runtime can be prepared.`);let c=await Re(r,a,i.remoteConfigPath,i.session,{tenantId:i.tenant,runId:i.runId,leaseId:o.leaseId}),l=!qe(o.metro,c.cleanup);return o={...o,runtime:c.runtime,metro:c.cleanup,updatedAt:new Date().toISOString()},{state:o,runtime:c.runtime,changed:!0,metroCleanupToStop:l?t.nextState.metro:void 0,preparedMetroCleanupOnFailure:l?c.cleanup:void 0}}function Oe(e,t){return e.leaseId||!t.leaseId?e:{...e,leaseId:t.leaseId,leaseBackend:t.leaseBackend}}function ke(e,t,n){return e===!0||!t||!Q(t,n)}async function Ae(e){if(e.changed)try{A({stateDir:e.stateDir,state:e.state})}catch(t){throw await q(e.preparedMetroCleanupOnFailure),await We(e.client,e.state,e.acquiredLeaseForCleanup),t}await q(e.metroCleanupToStop)}async function je(e){let{command:t,client:n,state:r,nextFlags:i,policy:a}=e,o=r.leaseBackend??X(i),s=e.nextState,c=await a.resolveLeaseState({command:t,client:n,state:s,flags:i,leaseBackend:o});s=c.state,c.device&&$(i,c.device);let l=s.leaseBackend??o??Ge(i,t);tt(r,i,l);let u=await Xe(n,s,l,a,i),d=u.lease;return i.leaseId=d.leaseId,i.leaseBackend=l,i.platform=s.platform??i.platform,i.target=s.target??i.target,Me(s,d,l)?{state:s,changed:!1,acquiredLeaseForCleanup:u.acquired?d:void 0}:{state:Ne(s,d,l,i),changed:!0,acquiredLeaseForCleanup:u.acquired?d:void 0}}function Me(e,t,n){return e.leaseId===t.leaseId&&e.leaseBackend===n&&e.deviceKey===(t.deviceKey??e.deviceKey)}function Ne(e,t,n,r){return{...e,leaseId:t.leaseId,leaseBackend:n,leaseProvider:t.leaseProvider??e.leaseProvider,clientId:t.clientId??e.clientId,deviceKey:t.deviceKey??e.deviceKey,platform:e.platform??r.platform,target:e.target??r.target,updatedAt:new Date().toISOString()}}function Pe(e){return e.leaseProvider===`proxy`?Ie:y(e.leaseProvider)?Le:Fe}const Fe={shouldAllocate:e=>!G.has(e),ttlMs:()=>void 0,resolveLeaseState:async e=>({state:e.state})},Ie={shouldAllocate:e=>e!==`devices`&&!G.has(e),ttlMs:()=>3e5,resolveLeaseState:Ze},Le={shouldAllocate:e=>!G.has(e),ttlMs:()=>6e5,resolveLeaseState:async e=>({state:e.state})};async function Re(t,n,r,i,a){if(!t.metroProjectRoot&&!t.metroPublicBaseUrl&&!t.metroProxyBaseUrl)return{};if(t.platform!==`ios`&&t.platform!==`android`)throw new e(`INVALID_ARGS`,`Deferred Metro preparation requires platform "ios" or "android".`);if(!t.metroPublicBaseUrl&&!t.metroProxyBaseUrl)throw new e(`INVALID_ARGS`,`Deferred Metro preparation requires metroPublicBaseUrl or metroProxyBaseUrl when Metro settings are provided.`);let o=await n.metro.prepare({projectRoot:t.metroProjectRoot,kind:c(t.metroKind),publicBaseUrl:t.metroPublicBaseUrl,proxyBaseUrl:t.metroProxyBaseUrl,bearerToken:t.metroBearerToken,bridgeScope:a,launchUrl:t.launchUrl,companionProfileKey:r,companionConsumerKey:i,port:t.metroPreparePort,listenHost:t.metroListenHost,statusHost:t.metroStatusHost,startupTimeoutMs:t.metroStartupTimeoutMs,probeTimeoutMs:t.metroProbeTimeoutMs,reuseExisting:t.metroNoReuseExisting?!1:void 0,installDependenciesIfNeeded:t.metroNoInstallDeps?!1:void 0,runtimeFilePath:t.metroRuntimeFile});return{runtime:t.platform===`ios`?o.iosRuntime:o.androidRuntime,cleanup:t.metroProxyBaseUrl?{projectRoot:o.projectRoot,profileKey:r,consumerKey:i}:void 0}}async function q(e){if(e)try{await ee(e)}catch{}}async function ze(e){try{await E({projectRoot:process.cwd(),stateDir:e.stateDir,profileKey:e.state.remoteConfigPath,consumerKey:e.state.session})}catch{}}async function J(e,t,n){return t.leaseId?await e.leases.release({tenant:t.tenant,runId:t.runId,leaseId:t.leaseId,leaseBackend:t.leaseBackend,daemonBaseUrl:t.daemon?.baseUrl,daemonAuthToken:n,daemonTransport:t.daemon?.transport,daemonServerMode:t.daemon?.serverMode,leaseProvider:t.leaseProvider,clientId:t.clientId,deviceKey:t.deviceKey}):{released:!1}}function Be(e){let t=Ve(e.previous,e.cwd,e.env);return t?{canAuthenticate:!0,daemonAuthToken:t}:e.previous.daemon?.baseUrl===e.nextDaemonBaseUrl?{canAuthenticate:!0,daemonAuthToken:e.ambientDaemonAuthToken}:{canAuthenticate:!1}}function Ve(e,t,n){try{let{profile:r}=ne({configPath:e.remoteConfigPath,cwd:t,env:n});return r.daemonAuthToken&&He(e,r.daemonBaseUrl)?r.daemonAuthToken:void 0}catch{return}}function He(e,t){let n=j({daemonBaseUrl:t})?.baseUrl;return n!==void 0&&n===e.daemon?.baseUrl}async function Ue(e,t,n){if(!t.leaseId)return;let r=Be({previous:t,nextDaemonBaseUrl:n.nextDaemonBaseUrl,ambientDaemonAuthToken:n.ambientDaemonAuthToken,cwd:n.cwd,env:n.env});if(!r.canAuthenticate)return Y(t,`no credential known to belong to that endpoint was available`);try{await J(e,t,r.daemonAuthToken);return}catch{return Y(t,`the release request failed`)}}function Y(e,t){return{status:`unreleased`,message:`Could not release the previous lease ${e.leaseId} (tenant ${e.tenant}, run ${e.runId}) at ${e.daemon?.baseUrl??`its daemon`}: ${t}. It was left in place — release it manually if it is still active.`}}async function We(e,t,n){if(n)try{await e.leases.release({tenant:t.tenant,runId:t.runId,leaseId:n.leaseId,leaseBackend:t.leaseBackend??n.backend,leaseProvider:t.leaseProvider??n.leaseProvider,clientId:t.clientId??n.clientId,deviceKey:t.deviceKey??n.deviceKey})}catch{}}function X(e){if(e.leaseBackend)return e.leaseBackend;if(e.platform===`android`)return`android-instance`;if(e.platform===`ios`)return`ios-instance`}function Ge(t,n){let r=X(t);if(r)return r;throw new e(`INVALID_ARGS`,`${n} requires --platform ios|android or --lease-backend when the remote connection has not resolved a lease yet.`)}function Ke(e,t,n,r=[]){return e===`cdp`?O(r)&&!!t.metroPublicBaseUrl:K.has(e)?!0:e!==`batch`||!n?!1:n.some(e=>{let t=e.command.trim().toLowerCase();return K.has(t)&&e.runtime===void 0})}function Z(e){let t=e.metroKind;return!!(e.metroPublicBaseUrl||e.metroProxyBaseUrl||e.metroProjectRoot||t)}function Q(e,t){return!e.platform||!t||t!==`ios`&&t!==`android`?!0:e.platform===t}function qe(e,t){return e?.projectRoot===t?.projectRoot&&e?.profileKey===t?.profileKey&&e?.consumerKey===t?.consumerKey}function Je(e,t){if(e)return Q(e,t)?e:void 0}function Ye(t,n,r={}){if(!t.tenant)throw new e(`INVALID_ARGS`,`remote command requires tenant in remote config or via --tenant <id>.`);if(!t.runId)throw new e(`INVALID_ARGS`,`remote command requires runId in remote config or via --run-id <id>.`);if(!t.daemonBaseUrl&&U(r.leaseProvider))throw new e(`INVALID_ARGS`,`remote command requires daemonBaseUrl in remote config, config, env, or --daemon-base-url.`);let i=new Date().toISOString();return{version:1,session:t.session??`default`,remoteConfigPath:n,remoteConfigHash:N(n),daemon:j(t),tenant:t.tenant,runId:t.runId,leaseId:t.leaseId,leaseBackend:t.leaseBackend??X(t),leaseProvider:r.leaseProvider,clientId:r.clientId,deviceKey:r.deviceKey,platform:t.platform,target:t.target,connectedAt:i,updatedAt:i}}async function Xe(e,t,n,r,i){if(t.leaseId&&t.leaseBackend===n){let i=await nt(e,t.leaseId,{tenant:t.tenant,runId:t.runId,leaseBackend:n,leaseProvider:t.leaseProvider,clientId:t.clientId,deviceKey:t.deviceKey,ttlMs:r.ttlMs(t)});if(i)return{lease:i,acquired:!1}}return{lease:await e.leases.allocate({tenant:t.tenant,runId:t.runId,leaseBackend:n,leaseProvider:t.leaseProvider,clientId:t.clientId,deviceKey:t.deviceKey,ttlMs:r.ttlMs(t),platform:t.platform??i.platform,target:t.target??i.target,device:i.device,udid:i.udid,serial:i.serial,providerApp:i.providerApp,providerOsVersion:i.providerOsVersion,providerProject:i.providerProject,providerBuild:i.providerBuild,providerSessionName:i.providerSessionName,...W(i),awsProjectArn:i.awsProjectArn,awsDeviceArn:i.awsDeviceArn,awsAppArn:i.awsAppArn,awsRegion:i.awsRegion,awsInteractionMode:i.awsInteractionMode}),acquired:!0}}async function Ze(t){if(!Te.has(t.command)){if(t.state.leaseId&&t.state.deviceKey)return{state:t.state};throw new e(`INVALID_ARGS`,`No active proxy device lease for this session; run open first.`)}let n=await Qe(t.client,t.flags),r=$e(n);return{state:{...t.state,deviceKey:r,leaseBackend:t.state.leaseBackend??t.leaseBackend??et(n),platform:t.state.platform??n.platform,target:t.state.target??n.target,updatedAt:new Date().toISOString()},device:n}}function $(e,t){if(e.platform=t.platform,e.target=t.target??e.target,i(t)){e.udid=t.id;return}t.platform===`android`&&(e.serial=t.id)}async function Qe(e,t){return await r((await e.devices.list({platform:t.platform,target:t.target,device:t.device,udid:t.udid,serial:t.serial,iosSimulatorDeviceSet:t.iosSimulatorDeviceSet,androidDeviceAllowlist:t.androidDeviceAllowlist})).map(e=>({...n(e.platform),id:e.id,name:e.name,kind:e.kind,target:e.target,booted:e.booted})),{platform:t.platform,target:t.target,deviceName:t.device,udid:t.udid,serial:t.serial})}function $e(e){return`${t(e)}:${e.target??`mobile`}:${e.id}`}function et(e){if(i(e))return`ios-instance`;if(e.platform===`android`)return`android-instance`}function tt(t,n,r){if(t.leaseBackend&&t.leaseBackend!==r)throw new e(`INVALID_ARGS`,`Active remote connection is already bound to a different lease backend. Re-run connect --force to replace it.`,{session:t.session,leaseBackend:t.leaseBackend});if(t.platform&&n.platform&&t.platform!==n.platform)throw new e(`INVALID_ARGS`,`Active remote connection is already bound to a different platform. Re-run connect --force to replace it.`,{session:t.session,platform:t.platform});if(t.target&&n.target&&t.target!==n.target)throw new e(`INVALID_ARGS`,`Active remote connection is already bound to a different target. Re-run connect --force to replace it.`,{session:t.session,target:t.target})}async function nt(e,t,n){try{return await e.leases.heartbeat({tenant:n.tenant,runId:n.runId,leaseId:t,leaseBackend:n.leaseBackend,leaseProvider:n.leaseProvider,clientId:n.clientId,deviceKey:n.deviceKey,ttlMs:n.ttlMs})}catch(e){if(rt(e))return;throw e}}function rt(t){return!(t instanceof e)||t.code!==`UNAUTHORIZED`?!1:t.details?.reason===`LEASE_NOT_FOUND`||t.details?.reason===`LEASE_EXPIRED`||t.details?.reason===`LEASE_REVOKED`}export{A as C,E as D,T as E,le as S,D as T,de as _,X as a,k as b,W as c,Ce as d,U as f,j as g,be as h,J as i,we as l,V as m,Ee as n,q as o,xe as p,Ue as r,ze as s,Z as t,Se as u,N as v,ce as w,M as x,I as y};