agent-device 0.16.4 → 0.16.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.16.4.apk → agent-device-android-multitouch-helper-0.16.6.apk} +0 -0
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.6.apk.sha256 +1 -0
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.16.4.manifest.json → agent-device-android-multitouch-helper-0.16.6.manifest.json} +4 -4
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.6.apk +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.6.apk.sha256 +1 -0
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.16.4.manifest.json → agent-device-android-snapshot-helper-0.16.6.manifest.json} +6 -6
- package/dist/src/1010.js +1 -0
- package/dist/src/1231.js +1 -1
- package/dist/src/1352.js +1 -0
- package/dist/src/1998.js +1 -0
- package/dist/src/208.js +1 -1
- package/dist/src/221.js +6 -6
- package/dist/src/2415.js +31 -0
- package/dist/src/2805.js +1 -0
- package/dist/src/5186.js +1 -0
- package/dist/src/5310.js +1 -0
- package/dist/src/5792.js +1 -0
- package/dist/src/6085.js +1 -0
- package/dist/src/6629.js +1 -0
- package/dist/src/8114.js +4 -0
- package/dist/src/8133.js +1 -0
- package/dist/src/8502.js +1 -0
- package/dist/src/8699.js +1 -0
- package/dist/src/8806.js +7 -0
- package/dist/src/940.js +1 -1
- package/dist/src/9404.js +1 -0
- package/dist/src/9471.js +1 -0
- package/dist/src/9533.js +1 -0
- package/dist/src/9542.js +3 -3
- package/dist/src/9639.js +1 -1
- package/dist/src/9671.js +1 -0
- package/dist/src/android-adb.js +1 -1
- package/dist/src/android-snapshot-helper.d.ts +2 -1
- package/dist/src/android-snapshot-helper.js +1 -1
- package/dist/src/android.js +5 -0
- package/dist/src/apple.js +1 -0
- package/dist/src/apps.js +13 -0
- package/dist/src/args.js +449 -0
- package/dist/src/batch.js +1 -1
- package/dist/src/cli.js +36 -492
- package/dist/src/command-metadata.js +1 -0
- package/dist/src/command-surface.js +1 -0
- package/dist/src/contracts.d.ts +1 -0
- package/dist/src/devices.js +1 -0
- package/dist/src/devices~1.js +1 -0
- package/dist/src/devices~2.js +1 -0
- package/dist/src/find.js +1 -0
- package/dist/src/finders.d.ts +1 -0
- package/dist/src/generic.js +9 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/input-actions.js +1 -0
- package/dist/src/input-actions~1.js +1 -0
- package/dist/src/interaction.js +1 -0
- package/dist/src/internal/bin.js +5 -2
- package/dist/src/internal/daemon.js +1 -100
- package/dist/src/lease.js +1 -0
- package/dist/src/linux.js +1 -0
- package/dist/src/notifications.js +1 -0
- package/dist/src/react-native.js +1 -0
- package/dist/src/record-trace.js +26 -0
- package/dist/src/recording-provider.js +1 -0
- package/dist/src/selector-runtime.js +1 -0
- package/dist/src/selectors.d.ts +1 -0
- package/dist/src/server.js +1 -1
- package/dist/src/session.js +29 -0
- package/dist/src/snapshot.js +2 -0
- package/package.json +4 -1
- package/server.json +2 -2
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.4.apk.sha256 +0 -1
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.4.apk +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.4.apk.sha256 +0 -1
- package/dist/src/1769.js +0 -7
- package/dist/src/6277.js +0 -4
- package/dist/src/7519.js +0 -1
- package/dist/src/89.js +0 -1
package/dist/src/cli.js
CHANGED
|
@@ -1,502 +1,46 @@
|
|
|
1
|
-
import e from"node:path";import{styleText as t}from"node:util";import{pathToFileURL as n}from"node:url";import
|
|
2
|
-
|
|
3
|
-
Version-matched operating guide for normal agent-device work.
|
|
4
|
-
|
|
5
|
-
Core loop:
|
|
6
|
-
devices/apps -> open -> snapshot or snapshot -i -> get/is/find/wait or press/fill/scroll/back -> verify -> close
|
|
7
|
-
|
|
8
|
-
Command shape:
|
|
9
|
-
Plans should use agent-device commands, not raw platform tools, pseudo commands, package-manager aliases, or helper prose.
|
|
10
|
-
Put subcommand first, then positionals, then flags:
|
|
11
|
-
agent-device open com.example.app --session checkout --platform android --relaunch
|
|
12
|
-
agent-device record start ./checkout.mp4 --session checkout
|
|
13
|
-
Snapshot refs look like @e12. After snapshot -i, use the exact @eN ref from that output.
|
|
14
|
-
If the exact ref is not known yet, first output snapshot -i, then use a concrete example shape like press @e12 in the next command; do not write @<ref>, @ref, @Label_Name, or @eN placeholders.
|
|
15
|
-
Close means agent-device close. App-owned back means back; system back means back --system.
|
|
16
|
-
Taps are press or click. Gestures use swipe, longpress, or gesture <pan|fling|pinch|rotate|transform>. Android pinch, rotate, and transform use provider-native touch injection when available, then the bundled multi-touch helper. iOS simulator transform uses private XCTest synthesis for a continuous two-finger pan/scale/rotation path; otherwise it reports UNSUPPORTED_OPERATION.
|
|
17
|
-
|
|
18
|
-
Bootstrap:
|
|
19
|
-
agent-device devices --platform ios
|
|
20
|
-
agent-device apps --platform android
|
|
21
|
-
agent-device open MyApp --platform ios --device "iPhone 17 Pro"
|
|
22
|
-
agent-device open <discovered-app-id> --session checkout --platform android
|
|
23
|
-
agent-device install com.example.app ./dist/app.apk --platform android
|
|
24
|
-
agent-device reinstall com.example.app ./build/MyApp.app --platform ios
|
|
25
|
-
agent-device install-from-source --github-actions-artifact org/repo:app-debug --platform android
|
|
26
|
-
agent-device open com.example.app --platform android --relaunch
|
|
27
|
-
If app id is unknown, plan devices, apps, then open <discovered-app-id>. Discovery is not enough when the task asks to open/start the app.
|
|
28
|
-
Install arguments are app/package id then artifact path. If the task says install, use install; use reinstall only when explicitly requested. Fresh runtime state is open --relaunch after install.
|
|
29
|
-
Do not open artifact paths or invent package ids. If apps lookup misses the target and no URL/artifact is provided, ask or stop.
|
|
30
|
-
|
|
31
|
-
Snapshots and refs:
|
|
32
|
-
snapshot reads visible state. snapshot -i gets current interactive refs only; it is the fast path when the next step is an interaction.
|
|
33
|
-
Default snapshot text is an agent-facing, token-efficient view for planning and targeting actions; use --raw or --json only when you need the full provider tree.
|
|
34
|
-
Snapshot legend:
|
|
35
|
-
@e12 [button] label="Add to cart" id="add-cart" enabled hittable -> press @e12 or press 'id="add-cart"'.
|
|
36
|
-
@e13 [textinput] label="Notes" preview="Leave at side..." truncated -> snapshot -s @e13 before reading.
|
|
37
|
-
@e14 [cell] label="Profiles" focused -> tvOS focus is currently on this row.
|
|
38
|
-
[off-screen below] 4 items: "Privacy", "About" -> scroll down, then snapshot -i; those are hints, not refs.
|
|
39
|
-
Re-snapshot after navigation, submit, typing/fill, modal/list/reload/dynamic changes when you need new refs.
|
|
40
|
-
Anti-pattern: snapshot -i followed by snapshot -i | grep ...
|
|
41
|
-
Refs from the first snapshot remain valid until you press, fill, type, scroll, go back, wait for async UI, or otherwise change app state.
|
|
42
|
-
After a mutation, prefer a known selector/label directly (for example press 'label="Send"') because interaction commands refresh interactive state internally. If you need to discover the new control, use snapshot -i, or snapshot -i -s "Composer" when a stable container label/id can scope the refresh.
|
|
43
|
-
For a targeted query, use find/get/is. If you truly need the full tree again, pass --force-full.
|
|
44
|
-
Off-screen summaries are scroll hints; use scroll, not swipe, then snapshot -i.
|
|
45
|
-
Missing target in a long list: use a short manual scroll + snapshot loop with a max attempt count. If a named target is summarized as off-screen below/above, use scroll down/up, then snapshot -i; do not use scroll bottom/top because the target may appear before the absolute list edge. Use scroll bottom/top only when the task explicitly asks for the list edge. Edge scrolls verify hidden content with snapshots and stop when no matching hidden content remains.
|
|
46
|
-
Truncated text/input previews: do not use get text first; expand with snapshot -s @ref (for example snapshot -s @e7), then read the scoped output.
|
|
47
|
-
Rare iOS accessibility gaps: if a row ref is shown disabled/hittable:false and press @ref reports success but no UI change, or a horizontal tab/filter bar is collapsed into one composite/seekbar with no child refs, run agent-device snapshot -i -c --json to read rects, compute the target center, press x y, then diff snapshot -i. Coordinates are fallback-only; document why you used them.
|
|
48
|
-
|
|
49
|
-
Selectors:
|
|
50
|
-
Use selectors as positional targets: id="field-email" or label="Allow".
|
|
51
|
-
Do not use CSS selectors, pseudo refs, --selector, --text, or raw x/y when refs/selectors exist.
|
|
52
|
-
agent-device fill 'id="catalog-search"' "tart" --delay-ms 80
|
|
53
|
-
agent-device press 'id="submit-order"'
|
|
54
|
-
agent-device is visible 'label="Online"'
|
|
55
|
-
agent-device get text 'id="quantity-value"'
|
|
56
|
-
|
|
57
|
-
Text entry:
|
|
58
|
-
fill replaces; type appends to focused field.
|
|
59
|
-
agent-device fill @e5 "qa@example.com"
|
|
60
|
-
agent-device fill 'id="field-email"' "qa@example.com"
|
|
61
|
-
agent-device press 'id="product-note"'
|
|
62
|
-
agent-device type "Handle with care" --delay-ms 80
|
|
63
|
-
Empty replacement is not a supported clear-field command: do not plan fill <target> "" or fill <target> ''. Prefer a visible clear/reset control; if the app exposes none, report the tool gap instead of inventing a clear command.
|
|
64
|
-
Debounced field with no result selector: agent-device wait 1000. Keyboard read-only: keyboard status/get. Blocked control: try keyboard dismiss when supported.
|
|
65
|
-
On iOS, prefer keyboard dismiss before manually pressing visible Done; the runner can use safe native keyboard controls and still reports unsupported layouts explicitly. If it returns UNSUPPORTED_OPERATION, prefer a visible app dismiss control, or use back --system only when system navigation is an acceptable side effect.
|
|
66
|
-
Search-as-you-type fields on iOS can drop characters when driven too fast; use --delay-ms on fill/type before trying clipboard paste.
|
|
67
|
-
iOS Allow Paste prompt cannot be exercised under XCUITest. To test paste-driven app behavior, prefill first with agent-device clipboard write "some text"; test the system prompt manually.
|
|
68
|
-
Android Gboard handwriting/stylus UI can capture text in an IME-owned input instead of the app field. If fill reports that input was captured by the keyboard/IME, use the diagnostic targetInput/actualInput details, inspect keyboard status/get if needed, and switch or disable handwriting outside the command plan before retrying. Do not keep retrying fill/type against the same field while the IME owns focus.
|
|
69
|
-
Android text entry is owned by agent-device: provider-native text injection when available, then chunk-safe ASCII shell input. Do not switch to raw adb, clipboard, or paste as an agent fallback. If non-ASCII is unsupported in the current backend, report the tool/device gap.
|
|
70
|
-
|
|
71
|
-
Session ordering:
|
|
72
|
-
Stateful commands against one --session must run serially. Do not run open/press/fill/type/scroll/back/alert/replay/batch/close commands in parallel against the same session.
|
|
73
|
-
It is fine to parallelize independent read-only collection or commands that use different sessions/devices.
|
|
74
|
-
|
|
75
|
-
Read-only and waits:
|
|
76
|
-
Read-only visible/state question: use snapshot/get/is/find.
|
|
77
|
-
agent-device snapshot
|
|
78
|
-
agent-device get text 'id="product-title"'
|
|
79
|
-
agent-device get attrs @e4
|
|
80
|
-
agent-device is visible 'label="Online"'
|
|
81
|
-
agent-device wait text "Refreshing metrics..." 3000
|
|
82
|
-
agent-device wait 'label="Ready"' 3000
|
|
83
|
-
agent-device find "Increment" press --json
|
|
84
|
-
For async/list text presence, prefer wait text over is visible when no interaction is needed.
|
|
85
|
-
Use snapshot -i only when refs are needed for an action or targeted query.
|
|
86
|
-
Ambiguous find: add --first or --last. If info is not visible/exposed, report that gap instead of typing/searching/navigating to reveal it.
|
|
87
|
-
|
|
88
|
-
Navigation and gestures:
|
|
89
|
-
Use scroll for lists; swipe for coordinate gestures/carousels; gesture pan for deliberate drags; gesture fling for fast directional throws.
|
|
90
|
-
For raw coordinate gestures, run snapshot -i first and choose a point near the center of the intended app-owned target. Avoid screen edges, tab bars, navigation bars, and home indicators because those areas can trigger system or app navigation instead of the gesture under test.
|
|
91
|
-
If app-owned back is ambiguous or has just misrouted, prefer a visible nav/back button ref, tab-bar ref, or deep link over repeated back/system back.
|
|
92
|
-
App-owned action sheets, menus, and camera/scan screens are normal UI. After opening one, run snapshot -i or wait for the option, press by label/ref, handle visible permission sheets through UI or platform-supported native alerts, then wait for a concrete result before returning to chat/form state.
|
|
93
|
-
Keep count/pause/pattern on one swipe; flags are --count, --pause-ms, --pattern ping-pong.
|
|
94
|
-
longpress accepts coordinates, @refs, or selectors. Prefer @ref/selector from snapshot -i; use coordinates only as a fallback when accessibility refs miss the exact target. Duration and gesture scale/center are positional:
|
|
95
|
-
agent-device longpress 300 500 800
|
|
96
|
-
agent-device longpress @e12 800
|
|
97
|
-
agent-device swipe 320 500 40 500 --count 8 --pause-ms 30 --pattern ping-pong
|
|
98
|
-
agent-device gesture pan 200 420 0 -80 500
|
|
99
|
-
agent-device gesture fling right 200 420 180
|
|
100
|
-
agent-device gesture pinch 0.5 200 400
|
|
101
|
-
agent-device gesture rotate 35 200 420
|
|
102
|
-
agent-device gesture transform 200 420 80 -40 2 35 700
|
|
103
|
-
iOS simulator transform uses private XCTest synthesis for a continuous two-finger pan/scale/rotation path; verify app metrics instead of assuming requested values map exactly to recognizer output.
|
|
104
|
-
Android transform injects a geometric two-finger path; app recognizers may report non-exact pan/scale/rotation. For Android combined transforms, verify qualitative state such as "pan changed yes" / "pinch changed yes" / "rotate changed yes" unless the app explicitly promises exact centroid metrics.
|
|
105
|
-
If Android needs exact app-state values, prefer isolated gesture pan, gesture pinch, or gesture rotate commands over one combined transform.
|
|
106
|
-
|
|
107
|
-
Validation and evidence:
|
|
108
|
-
Nearby mutation diff: agent-device diff snapshot -i.
|
|
109
|
-
Expected text/selector verification must include the exact text or selector via wait, is, get, or find; bare screenshots/snapshots are insufficient for named expectations.
|
|
110
|
-
Prefer provided testIDs/ids/selectors for verification; use visible text when no durable selector is provided.
|
|
111
|
-
If task says snapshot, use snapshot. If it asks visual evidence, use screenshot.
|
|
112
|
-
Icon/tappable visual proof: screenshot --overlay-refs. Flag is --overlay-refs.
|
|
113
|
-
Startup/frame health/CPU/memory: perf --json or metrics. Replay maintenance: replay -u ./flow.ad.
|
|
114
|
-
Recording: record start/stop. By default, stop burns touch overlays into the video; use record start --hide-touches for the fastest raw recording. For gesture-heavy iOS simulator proof videos, prefer --hide-touches because overlay timing depends on a stable runner session while gestures are executing. Tracing: trace start ./trace.log, trace stop ./trace.log. Paths are positional.
|
|
115
|
-
Stable known flow: batch ./steps.json, not workflow batch.
|
|
116
|
-
Inline batch JSON example:
|
|
117
|
-
agent-device batch --steps '[{"command":"open","input":{"app":"settings"}},{"command":"wait","input":{"kind":"duration","durationMs":100}}]'
|
|
118
|
-
Batch step keys are command, input, and optional runtime. Put command arguments inside input using the same fields as the MCP/Node command. CLI still accepts legacy positionals/flags steps with a deprecation warning until the next major version.
|
|
119
|
-
Never use args, step positionals, or flags for new batch JSON; put command inputs under input.
|
|
120
|
-
Android animations: settings animations off/on, not animations disable/restore.
|
|
121
|
-
Debug logs: logs clear --restart, logs mark, reproduce, then logs path; do not split clear/restart into separate stop/start commands.
|
|
122
|
-
Network headers: network dump --include headers; do not write network log headers.
|
|
123
|
-
Remote/cloud: connect to discover a cloud profile, or connect --remote-config ./remote-config.json for a local profile; then open, snapshot, disconnect.
|
|
124
|
-
macOS menu bar: open ... --platform macos --surface menubar; snapshot -i --platform macos --surface menubar.
|
|
125
|
-
|
|
126
|
-
React Native dev loop:
|
|
127
|
-
JS-only change with Metro connected:
|
|
128
|
-
agent-device metro reload
|
|
129
|
-
agent-device find "Home"
|
|
130
|
-
Do not use agent-device reload. Use open --relaunch for native startup reset.
|
|
131
|
-
React Native apps: use help react-native for Metro/Fast Refresh, DevTools routing, and RN-specific blockers; use react-native dismiss-overlay for LogBox/RedBox overlays.
|
|
132
|
-
Android RN/Expo Metro: direct Android URL opens to localhost/127.0.0.1/[::1] with a port auto-configure host reachability. Manual adb reverse tcp:<port> tcp:<port> is only needed for app/package launches or unsupported flows where the app cannot reach local Metro.
|
|
133
|
-
Expo Go is a host shell. Use a provided project URL instead of inventing a bundle id; if no URL is provided but a target/app name is provided, open that target and do not inspect project files to find one. On iOS, prefer host + URL when the host shell is known because direct URL open can report success while leaving the runner/shell focused; verify with snapshot -i after opening:
|
|
134
|
-
agent-device open "Expo Go" exp://127.0.0.1:8081 --platform ios
|
|
135
|
-
agent-device snapshot -i --platform ios
|
|
136
|
-
There is no open-url command; use open with the URL target or host + URL form.
|
|
137
|
-
Direct iOS URL open remains valid when no host shell is known, but verify that the app UI loaded:
|
|
138
|
-
agent-device open exp://127.0.0.1:8081 --platform ios
|
|
139
|
-
Android uses the URL target directly; do not write open <app> <url> there:
|
|
140
|
-
agent-device open exp://127.0.0.1:8081 --platform android
|
|
141
|
-
Android URL/deep-link opens infer the foreground package after launch when possible, so logs/perf can remain package-bound. If perf still says no package is associated, open the host package/app id first, then open the URL in the same session.
|
|
142
|
-
If apps lookup misses the project but shows Expo Go/dev-client and a project URL is available, open the URL/host shell; if no URL is available, ask instead of inventing an app id.
|
|
143
|
-
Expo Dev Client/development builds: open the installed dev-client app id/name; if a dev-client URL is provided, open that URL next. For Metro setup use metro prepare --kind expo.
|
|
144
|
-
|
|
145
|
-
Escalate:
|
|
146
|
-
help debugging logs, network, alerts, traces, flaky runtime failures
|
|
147
|
-
help react-devtools React Native performance, profiling, props/state/hooks, slow renders, rerenders
|
|
148
|
-
help react-native React Native app automation hazards, overlays, Metro, and routing
|
|
149
|
-
help remote remote/cloud config, tenant, lease, local service tunnels
|
|
150
|
-
help macos desktop, frontmost-app, menu bar surfaces
|
|
151
|
-
help dogfood exploratory QA report workflow`},debugging:{summary:"Targeted failure evidence without dumping stale context",body:`agent-device help debugging
|
|
152
|
-
|
|
153
|
-
Use this when behavior fails, hangs, times out, throws alerts, or needs runtime evidence.
|
|
154
|
-
|
|
155
|
-
Logs:
|
|
156
|
-
Keep log windows small. Prefer clear, mark, reproduce, then path.
|
|
157
|
-
agent-device logs clear --restart
|
|
158
|
-
agent-device logs mark "before diagnostics retry"
|
|
159
|
-
agent-device press 'id="load-diagnostics"'
|
|
160
|
-
agent-device logs path
|
|
161
|
-
Do not cat a full stale log into agent context. Open or grep only the relevant window when needed.
|
|
162
|
-
logs clear --restart is the compact command to clear old logs and start a fresh capture; do not split it into logs stop, logs clear, logs start.
|
|
163
|
-
On iOS simulators, logs scope by bundle id and resolved app executable, so use this instead of raw simctl log stream predicates.
|
|
164
|
-
For iOS simulator launch-time stdout/stderr, use --launch-console on the direct app launch:
|
|
165
|
-
agent-device open MyApp --platform ios --relaunch --launch-console ./artifacts/app.console.log
|
|
166
|
-
--launch-console is only for direct iOS simulator app launches, not URL opens.
|
|
167
|
-
|
|
168
|
-
Network:
|
|
169
|
-
Use network dump for recent session HTTP traffic parsed from app logs.
|
|
170
|
-
agent-device network dump --include headers
|
|
171
|
-
agent-device network dump 20 --include all
|
|
172
|
-
Use this instead of logs path when the question is request/response metadata.
|
|
173
|
-
network log is a supported alias, but network dump --include headers is the clearest plan form. Do not write network log headers.
|
|
174
|
-
|
|
175
|
-
Alerts:
|
|
176
|
-
Native and platform dialogs:
|
|
177
|
-
agent-device alert wait 3000
|
|
178
|
-
agent-device alert accept
|
|
179
|
-
agent-device alert dismiss
|
|
180
|
-
Android support is snapshot-derived for runtime permission prompts and native app dialogs. iOS support is runner-derived for XCTest alerts, app-owned modal popups with native blocking markers, and blocking system dialogs. Use cheap alert get for an immediate check; use alert wait <short-ms> only when a prompt may appear after async work.
|
|
181
|
-
If alert says no alert but a sheet is visibly on screen, treat it as app-owned UI:
|
|
182
|
-
agent-device snapshot -i
|
|
183
|
-
agent-device press 'label="Allow"'
|
|
184
|
-
Do not use settings permission to answer a dialog already on screen. Reserve settings permission for setup/resetting permission state before a flow.
|
|
185
|
-
|
|
186
|
-
Diagnostics and traces:
|
|
187
|
-
Use --debug for CLI/daemon diagnostic ids and log paths.
|
|
188
|
-
Use trace for low-level session diagnostics around one repro:
|
|
189
|
-
agent-device trace start ./traces/diagnostics.trace
|
|
190
|
-
agent-device press 'id="load-diagnostics"'
|
|
191
|
-
agent-device trace stop ./traces/diagnostics.trace
|
|
192
|
-
The trace path is positional. Do not use --path for trace start or trace stop.
|
|
193
|
-
|
|
194
|
-
Stabilizers:
|
|
195
|
-
Android animation-sensitive flows:
|
|
196
|
-
agent-device settings animations off
|
|
197
|
-
agent-device snapshot
|
|
198
|
-
agent-device settings animations on
|
|
199
|
-
Re-enable settings you changed before finishing.
|
|
200
|
-
|
|
201
|
-
React Native internals:
|
|
202
|
-
If the question is about React Native performance, profiling, props, state, hooks, render causes, slow components, or rerenders, use help react-devtools instead of inferring from screenshots or logs.`},"react-devtools":{summary:"React Native performance, profiling, and component internals",body:`agent-device help react-devtools
|
|
203
|
-
|
|
204
|
-
Use this for React Native performance/profiling and internals that the accessibility tree cannot expose: components, props, state, hooks, ownership, slow renders, and rerenders.
|
|
205
|
-
|
|
206
|
-
Core commands:
|
|
207
|
-
agent-device react-devtools start
|
|
208
|
-
agent-device react-devtools stop
|
|
209
|
-
agent-device react-devtools status
|
|
210
|
-
agent-device react-devtools wait --connected
|
|
211
|
-
agent-device react-devtools wait --component <ComponentName>
|
|
212
|
-
agent-device react-devtools count
|
|
213
|
-
agent-device react-devtools get tree --depth 3
|
|
214
|
-
agent-device react-devtools find <ComponentName>
|
|
215
|
-
agent-device react-devtools find <ComponentName> --exact
|
|
216
|
-
agent-device react-devtools get component @c5
|
|
217
|
-
agent-device react-devtools errors
|
|
218
|
-
agent-device react-devtools profile start
|
|
219
|
-
agent-device react-devtools profile stop
|
|
220
|
-
agent-device react-devtools profile slow --limit 5
|
|
221
|
-
agent-device react-devtools profile rerenders --limit 5
|
|
222
|
-
agent-device react-devtools profile report @c5
|
|
223
|
-
agent-device react-devtools profile timeline --limit 20
|
|
224
|
-
agent-device react-devtools profile export profile.json
|
|
225
|
-
agent-device react-devtools profile diff before.json after.json --limit 10
|
|
226
|
-
|
|
227
|
-
Profiling loop:
|
|
228
|
-
1. Verify the app is connected: react-devtools status, then wait --connected if needed.
|
|
229
|
-
2. If correlating with logs or network, run logs clear --restart before the first logs mark.
|
|
230
|
-
3. Start profiling immediately before the interaction.
|
|
231
|
-
4. Drive the interaction with normal agent-device commands and mark before/after the repro when timing matters.
|
|
232
|
-
5. Stop profiling.
|
|
233
|
-
6. Make one bounded first-pass survey: profile stop for the summary, profile slow --limit 5 once, profile rerenders --limit 5 once, and profile timeline --limit 20 only when commit timing matters.
|
|
234
|
-
7. Use profile report @cN for targeted render causes and changed props/state/hooks; use get component @cN for current props/state/hooks.
|
|
235
|
-
|
|
236
|
-
Rules:
|
|
237
|
-
Every React DevTools command is an agent-device subcommand: agent-device react-devtools ...
|
|
238
|
-
Do not write agent-devtools, agent-react-devtools, or bare react-devtools commands in final command plans.
|
|
239
|
-
Start with get tree --depth 3 or find <name>; use find --exact when fuzzy results are noisy.
|
|
240
|
-
@c refs reset after reload/remount. After reload, wait --connected and inspect again.
|
|
241
|
-
Keep the profile window narrow; unrelated navigation makes render data noisy.
|
|
242
|
-
Do not repeatedly raise broad profile slow limits such as --limit 50, --limit 200, or --limit 500. Drill into a specific @c ref with profile report unless you have a specific target that needs more rows.
|
|
243
|
-
For network evidence, use agent-device network dump --include headers; headers is not a positional argument.
|
|
244
|
-
For cross-platform validation with explicit device selectors, prefer isolated --state-dir and restart react-devtools between platforms.
|
|
245
|
-
Remote Android and iOS bridge runs normally through agent-device react-devtools; the CLI keeps the needed local service tunnel alive until agent-device react-devtools stop or disconnect. Expo support depends on the SDK's bundled React Native runtime.
|
|
246
|
-
Remote iOS apps attempt the legacy React DevTools websocket during JavaScript startup. If the app was already open before react-devtools start, run open <bundle-id> --platform ios --relaunch, then wait --connected.
|
|
247
|
-
|
|
248
|
-
Example:
|
|
249
|
-
agent-device react-devtools status
|
|
250
|
-
agent-device react-devtools wait --connected
|
|
251
|
-
agent-device logs clear --restart
|
|
252
|
-
agent-device logs mark "before catalog search"
|
|
253
|
-
agent-device react-devtools profile start
|
|
254
|
-
agent-device fill 'id="catalog-search"' "tart" --delay-ms 80
|
|
255
|
-
agent-device logs mark "after catalog search"
|
|
256
|
-
agent-device react-devtools profile stop
|
|
257
|
-
agent-device react-devtools profile slow --limit 5
|
|
258
|
-
agent-device react-devtools profile rerenders --limit 5
|
|
259
|
-
agent-device react-devtools profile timeline --limit 20
|
|
260
|
-
agent-device react-devtools profile report @c5
|
|
261
|
-
agent-device network dump --include headers
|
|
262
|
-
|
|
263
|
-
Use snapshot, screenshot, logs, network, and perf for device/app runtime evidence. Use react-devtools only when component internals or React rendering behavior matters.`},"react-native":{summary:"React Native app automation hazards and routing",body:`agent-device help react-native
|
|
264
|
-
|
|
265
|
-
Use this when the target app is React Native, Expo, or a React Native dev client.
|
|
266
|
-
This topic covers React Native-specific automation hazards and routes deeper
|
|
267
|
-
questions to the owning help topic.
|
|
268
|
-
|
|
269
|
-
Choose the next help topic:
|
|
270
|
-
Generic navigation, selectors, refs, verification, serial commands: help workflow.
|
|
271
|
-
Logs, network, diagnostics, traces, permission dialogs, or runtime failures: help debugging.
|
|
272
|
-
Component tree, props/state/hooks, slow renders, rerenders, or render causes: help react-devtools.
|
|
273
|
-
Remote/cloud config, leases, and local service tunnels: help remote.
|
|
274
|
-
|
|
275
|
-
React Native dev loop:
|
|
276
|
-
For "start from screen X" flows, prefer open --relaunch before the first snapshot so the app does not reuse a prior in-progress navigation state.
|
|
277
|
-
JS-only change with Metro connected:
|
|
278
|
-
agent-device metro reload
|
|
279
|
-
agent-device find "Home"
|
|
280
|
-
Do not use agent-device reload. Use open --relaunch for native startup reset.
|
|
281
|
-
Android RN/Expo Metro: direct Android localhost URL opens with a port auto-configure host reachability. For app/package launches, use help react-native if the app cannot reach local Metro.
|
|
282
|
-
Verify Metro from the same host context that owns Metro. If a sandboxed shell cannot curl localhost:8081/status but an unrestricted host shell can, Metro is running and the sandbox probe is not authoritative.
|
|
283
|
-
adb reverse only affects Android device-to-host traffic. It does not prove host-to-Metro reachability, and it does not fix a redbox caused by a stale or wrong Metro/app state.
|
|
284
|
-
Expo Go/dev clients are host shells. Use provided project URLs, verify with snapshot -i after opening, and ask instead of inventing app ids or URLs. Help workflow owns the full Expo URL command shapes.
|
|
285
|
-
|
|
286
|
-
Overlays and busy RN UIs:
|
|
287
|
-
If snapshot reports a React Native warning/error overlay, handle it before interacting with the app: run agent-device react-native dismiss-overlay, then agent-device snapshot -i -c. Use refs from the new snapshot.
|
|
288
|
-
Do not manually press warning/error text bodies, collapsed banner bodies, full-screen warning parents, or broad LogBox/RedBox refs. The dismiss-overlay command owns the narrow LogBox/RedBox targeting policy.
|
|
289
|
-
Report the overlay in the final summary. Use screenshot --overlay-refs before dismissing only if visual evidence is required.
|
|
290
|
-
If snapshot times out because the UI never becomes idle, Android accessibility may be blocked by busy or continuously changing app UI. After that timeout, use screenshot as visual truth instead of repeatedly retrying snapshots.
|
|
291
|
-
Android runtime permission dialogs and native alerts are handled by alert wait/accept/dismiss. If alert reports no alert, treat the visible surface as app-owned UI and use snapshot -i plus press by label/ref.
|
|
292
|
-
|
|
293
|
-
React DevTools routing:
|
|
294
|
-
Keep the agent-device react-devtools prefix on every React DevTools command.
|
|
295
|
-
Use help react-devtools for status/wait, component trees, props/state/hooks, profile windows, slow renders, rerenders, and remote bridge rules.
|
|
296
|
-
If React DevTools cannot connect, report status and continue with logs, network, perf, screenshot, and trace evidence instead of blocking the whole flow.
|
|
297
|
-
|
|
298
|
-
Slow-flow investigation:
|
|
299
|
-
Keep one named session, start with session list, open, and snapshot -i.
|
|
300
|
-
Use help react-devtools for the narrow React profile window.
|
|
301
|
-
Use help debugging for logs clear --restart, logs mark, network dump --include headers, perf --json, traces, and runtime failure evidence.
|
|
302
|
-
For 15-20s async work, use wait with the exact expected text or selector instead of repeated snapshots.
|
|
303
|
-
Report React render offenders separately from network/backend waits and device frame/CPU/memory findings.`},remote:{summary:"Remote config, tenant, lease, and remote host flow",body:`agent-device help remote
|
|
304
|
-
|
|
305
|
-
Use remote config or the cloud connection profile when a profile owns daemon URL, auth, tenant, run, lease, device scope, and Metro hints. Do not restate those as individual flags unless overriding intentionally.
|
|
306
|
-
|
|
307
|
-
Cloud profile flow:
|
|
308
|
-
agent-device connect
|
|
309
|
-
agent-device open com.example.app
|
|
310
|
-
agent-device snapshot
|
|
311
|
-
agent-device disconnect
|
|
312
|
-
|
|
313
|
-
Local profile flow:
|
|
314
|
-
agent-device connect --remote-config ./remote-config.json
|
|
315
|
-
agent-device open com.example.app
|
|
316
|
-
agent-device snapshot
|
|
317
|
-
agent-device disconnect
|
|
318
|
-
|
|
319
|
-
Script flow, per-command config:
|
|
320
|
-
agent-device open com.example.app --remote-config ./remote-config.json
|
|
321
|
-
agent-device snapshot --remote-config ./remote-config.json
|
|
322
|
-
agent-device disconnect --remote-config ./remote-config.json
|
|
323
|
-
|
|
324
|
-
Rules:
|
|
325
|
-
connect and disconnect are top-level commands. Do not write agent-device remote connect or agent-device remote disconnect.
|
|
326
|
-
Use connect without --remote-config when the cloud control plane owns the connection profile.
|
|
327
|
-
Prefer --remote-config over --daemon-base-url, --tenant, --run-id, and --lease-id when using a local profile.
|
|
328
|
-
For self-contained scripts, pass the same --remote-config to every operational command, including disconnect; a preceding connect is optional but not required.
|
|
329
|
-
For remote artifact installs, use install-from-source <url> or install-from-source --github-actions-artifact org/repo:artifact; do not download CI artifacts locally first.
|
|
330
|
-
After connect, let the active remote connection supply runtime hints.
|
|
331
|
-
For remote Android and iOS bridge React DevTools, run agent-device react-devtools normally. The CLI opens the needed local service tunnel for the DevTools daemon and keeps it alive until agent-device react-devtools stop or disconnect.
|
|
332
|
-
Use --debug when remote connection or transport errors need diagnostic ids and remote log hints.`},macos:{summary:"macOS desktop, frontmost-app, and menu bar surfaces",body:`agent-device help macos
|
|
333
|
-
|
|
334
|
-
Use macOS only when the task targets desktop apps, desktop surfaces, or menu bar extras.
|
|
335
|
-
|
|
336
|
-
Open and inspect:
|
|
337
|
-
agent-device open TextEdit --platform macos
|
|
338
|
-
agent-device snapshot -i --platform macos
|
|
339
|
-
|
|
340
|
-
Surfaces:
|
|
341
|
-
--surface app normal app session
|
|
342
|
-
--surface frontmost-app inspect whichever app is frontmost
|
|
343
|
-
--surface desktop desktop-wide surface
|
|
344
|
-
--surface menubar menu bar extras and menu bar-only apps
|
|
345
|
-
|
|
346
|
-
Menu bar app example:
|
|
347
|
-
agent-device open "Agent Device Tester Menu" --platform macos --surface menubar
|
|
348
|
-
agent-device snapshot -i --platform macos --surface menubar
|
|
349
|
-
|
|
350
|
-
Context menu example:
|
|
351
|
-
agent-device click @e66 --button secondary --platform macos
|
|
352
|
-
agent-device snapshot -i --platform macos
|
|
353
|
-
|
|
354
|
-
Rules:
|
|
355
|
-
Use open and snapshot -i for menu bar inspection. Do not output inspect as a command.
|
|
356
|
-
Context menus are not ambient UI: secondary-click a visible target, then re-snapshot and use the new menu-item refs.
|
|
357
|
-
Do not let iOS simulator-set scoping hide macOS desktop targets.
|
|
358
|
-
Prefer refs/selectors over raw coordinates.
|
|
359
|
-
macOS snapshot rects are window-space; use current refs or overlay refs instead of guessing coordinates.`},dogfood:{summary:"Exploratory QA workflow with reproducible evidence",body:`agent-device help dogfood
|
|
360
|
-
|
|
361
|
-
Use this when asked to dogfood, exploratory test, bug hunt, QA, or find issues in an app.
|
|
362
|
-
|
|
363
|
-
Goal:
|
|
364
|
-
Find user-visible issues from runtime behavior. Do not read app source or invent findings from code.
|
|
365
|
-
Produce a concise report with severity, repro commands, expected/actual behavior, and evidence paths.
|
|
366
|
-
|
|
367
|
-
Loop:
|
|
368
|
-
1. Identify target app/platform; ask only if missing.
|
|
369
|
-
2. Create output dirs and open a named session. If auth or OTP is required, sign in or ask the user for the code.
|
|
370
|
-
3. Capture baseline snapshot -i and screenshot.
|
|
371
|
-
4. Map top-level navigation, then exercise primary flows and edge states.
|
|
372
|
-
5. For each issue, capture evidence and write the finding immediately, then continue.
|
|
373
|
-
6. Close the session and reconcile the report summary.
|
|
374
|
-
Keep stateful commands serial within the same session. Parallel runs can pollute text fields, focus, alerts, and navigation state.
|
|
375
|
-
|
|
376
|
-
Coverage:
|
|
377
|
-
Navigation, forms, empty/error/loading states, offline or retry behavior, permissions, settings, accessibility labels, orientation/keyboard, and obvious performance stalls.
|
|
378
|
-
React Native warning/error overlays can be real findings or test blockers. Capture them, use react-native dismiss-overlay if unrelated, re-snapshot, and report them.
|
|
379
|
-
Expo Go/dev-client shells: use the provided exp:// or dev-client URL and record whether the shell, project load, or app UI is being tested. On iOS dogfood, prefer agent-device open "Expo Go" <url> when Expo Go is the known shell, then snapshot -i to confirm the project UI rather than the runner splash.
|
|
380
|
-
Android RN/Expo Metro: direct Android localhost URL opens with a port auto-configure host reachability.
|
|
381
|
-
Categories: visual, functional, UX, content, performance, diagnostics, permissions, accessibility.
|
|
382
|
-
Severity: critical blocks a core flow/data/crashes; high breaks a major feature; medium has friction or workaround; low is polish.
|
|
383
|
-
|
|
384
|
-
Evidence commands:
|
|
385
|
-
mkdir -p ./dogfood-output/screenshots ./dogfood-output/videos ./dogfood-output/traces
|
|
386
|
-
agent-device --session qa open <app> --platform ios
|
|
387
|
-
agent-device --session qa snapshot -i
|
|
388
|
-
agent-device --session qa screenshot ./dogfood-output/screenshots/initial.png
|
|
389
|
-
agent-device --session qa screenshot ./dogfood-output/screenshots/issue-001.png --overlay-refs
|
|
390
|
-
agent-device --session qa logs clear --restart
|
|
391
|
-
agent-device --session qa logs mark "issue-001 repro"
|
|
392
|
-
agent-device --session qa logs path
|
|
393
|
-
agent-device --session qa record start ./dogfood-output/videos/issue-001.mp4
|
|
394
|
-
agent-device --session qa record start ./dogfood-output/videos/benchmark.mp4 --hide-touches
|
|
395
|
-
agent-device --session qa record stop
|
|
396
|
-
agent-device --session qa close
|
|
397
|
-
|
|
398
|
-
Evidence rules:
|
|
399
|
-
Interactive/behavioral issues need step screenshots and usually a repro video.
|
|
400
|
-
Static/on-load issues can use one screenshot; set repro video to N/A.
|
|
401
|
-
Use screenshot --overlay-refs when showing the tappable target or broken state helps repro.
|
|
402
|
-
|
|
403
|
-
Report shape:
|
|
404
|
-
./dogfood-output/report.md
|
|
405
|
-
Include date, platform, target app, session, scope, severity counts, and issues.
|
|
406
|
-
For each finding: ID, severity, category, title, affected flow/screen, repro commands, expected, actual, evidence files, notes.
|
|
407
|
-
Target 5-10 well-evidenced issues when available. If no issues are found, report coverage completed and residual risk instead of claiming the app is bug-free.
|
|
408
|
-
|
|
409
|
-
Rules:
|
|
410
|
-
Findings must come from observed runtime behavior, not source reads.
|
|
411
|
-
Re-snapshot after each mutation.
|
|
412
|
-
Keep commands in the report reproducible; use selectors or refs from fresh snapshots, not guessed coordinates.
|
|
413
|
-
Prefer refs for exploration and selectors for deterministic replay.
|
|
414
|
-
Use logs, network, screenshot --overlay-refs, trace, perf, or react-devtools only when they add evidence to a specific issue.
|
|
415
|
-
Never delete screenshots, videos, traces, or report artifacts during a session.
|
|
416
|
-
Escalate to help debugging or help react-devtools when runtime symptoms require those tools.`}};function ey(e){let t=e.endsWith("?"),n=t?e.slice(0,-1):e;return t?`[${n}]`:`<${n}>`}function eb(e){return O().filter(t=>e.has(t.key)&&void 0!==t.usageLabel&&void 0!==t.usageDescription)}function eI(e,t){return ex(e,t.map(e=>({label:e.usageLabel??"",description:e.usageDescription??""})))}function ex(e,t){if(0===t.length)return`${e}
|
|
417
|
-
(none)`;let n=Math.max(...t.map(e=>e.label.length))+2,o=[e];for(let e of t)o.push(` ${e.label.padEnd(n)}${e.description}`);return o.join("\n")}function eA(e,t){return 0===t.length?`${e}
|
|
418
|
-
(none)`:[e,...t.map(e=>` ${e}`)].join("\n")}let e$=new Set(["config","remoteConfig","help","version","batchSteps","githubActionsArtifact"]),ek=new Set(["appsFilter","iosSimulatorDeviceSet","sessionLocked","sessionLockConflicts"]),eS=function(){let e=new Map;for(let t of O()){let n=e.get(t.key);n?n.push(t):e.set(t.key,[t])}let t=new Map;for(let e of D)t.set(e,new Set(["*"]));for(let e of p())for(let n of U(e).allowedFlags??[]){let o=t.get(n);o&&o.has("*")||(o?o.add(e):t.set(n,new Set([e])))}return[...e.entries()].map(([e,n])=>({key:e,flagDefinitions:n,config:{enabled:!e$.has(e),key:e},env:{names:ek.has(e)?[]:[G(e)]},supportsCommand(n){let o=t.get(e);return!!o&&(!!o.has("*")||!!n&&o.has(n))}})).sort((e,t)=>e.key.localeCompare(t.key))}(),eC=new Map(eS.map(e=>[e.key,e]));function eD(e,t){return eC.get(e)?.supportsCommand(t)??!1}function eN(e){let t=e.flagDefinitions.find(e=>void 0===e.setValue);if(t)return t;let n=function(e){let t=e.flagDefinitions[0];if(!t)throw Error(`Missing flag definition for option ${e.key}`);return t}(e);if("enum"===n.type){let t=n.enumValues??e.flagDefinitions.map(e=>e.setValue).filter(e=>void 0!==e);return{...n,setValue:void 0,enumValues:t}}return n}function eR(e){let t=e.indexOf("=");return -1===t?[e,void 0]:[e.slice(0,t),e.slice(t+1)]}function eU(e){return e.replace(/^-+/,"")}function e_(e){if(!e.startsWith("-")||"-"===e)return!1;let[t]=e.startsWith("--")?eR(e):[e,void 0];return void 0!==N(t)}function eE(){let e,t,n,o,r,s,a,i;return e=`agent-device <command> [args] [--json]
|
|
419
|
-
|
|
420
|
-
CLI to control iOS and Android devices for AI agents.
|
|
421
|
-
`,t=ex("Commands:",p().map(e=>{let t=U(e);return{name:e,schema:t,usage:function(e,t){if(t.listUsageOverride)return t.listUsageOverride;let n=(t.positionalArgs??[]).map(n=>{var o,r,s;let a,i,l,c;return o=e,r=t,i=(a=(s=n).endsWith("?"))?s.slice(0,-1):s,c=(l=/^[a-z-]+(?:\|[a-z-]+)+$/i.test(i))||void 0!==r.usageOverride&&r.usageOverride.startsWith(`${o} ${i}`),a?l?`[${i}]`:c?i:`[${i}]`:c?i:`<${i}>`});return[e,...n].join(" ")}(e,t)}}).map(e=>({label:e.usage,description:e.schema.summary??e.schema.helpDescription}))),n=eI("Flags:",eb(D)),o=eA("Agent Quickstart:",em),r=ex("Agent Workflows:",ef),s=eA("Configuration:",eh),a=ex("Environment:",eg),i=eA("Examples:",ev),`${e}
|
|
422
|
-
${t}
|
|
423
|
-
|
|
424
|
-
${n}
|
|
425
|
-
|
|
426
|
-
${o}
|
|
427
|
-
|
|
428
|
-
${r}
|
|
429
|
-
|
|
430
|
-
${s}
|
|
431
|
-
|
|
432
|
-
${a}
|
|
433
|
-
|
|
434
|
-
${i}
|
|
435
|
-
`}function eP(e){return"long-press"===e?"longpress":"metrics"===e?"perf":e}function eL(e){return e.width>0&&e.height>0}function eO(e,t){return!!e&&!!t&&e.x>=t.x-2&&e.y>=t.y-2&&e.x+e.width<=t.x+t.width+2&&e.y+e.height<=t.y+t.height+2}function eT(e){return"number"!=typeof e.parentIndex}function eM(e){let t=(e.type??"").toLowerCase(),n=(e.identifier??"").trim().toLowerCase();return t.includes("edittext")||t.includes("textfield")||"composer"===n}function eB(e){let t=(e.type??"").toLowerCase();return t.includes("scroll")||t.includes("list")||t.includes("recyclerview")}function ej(e){var t;let n=P(e);if(!n||n!==e.identifier?.trim()||(t=n,!/^[\w.]+:id\/[\w.-]+$/i.test(t)))return n;let o=(e.type??"").toLowerCase();return o.includes("view")||o.includes("layout")||o.includes("image")||o.includes("list")||o.includes("recyclerview")||o.includes("collection")?"":n}function eF(e){let t=e.trim().replace(/\s+/g," ").toLowerCase();return!t||/^(true|false|\d+)$/.test(t)?null:t}function eG(e,t){let n=[],o=[t];for(;o.length>0;){let t=o.pop();for(let r of e)r.parentIndex===t&&(n.push(r),o.push(r.index))}return n}function eV(e,t,n){let o=[t];for(;o.length>0;){let t=o.pop();if(!n.has(t))for(let r of(n.add(t),e))r.parentIndex!==t||n.has(r.index)||o.push(r.index)}}let eH=["button","switch","checkbox","radio"],eK=["button","image","textview","view"];function eq(e){if(!0===e.hittable||eM(e))return!1;let t=(e.type??"").toLowerCase();return t.includes("text")||t.includes("image")||t.includes("icon")}function ez(e){if(!e.rect||!eL(e.rect)||eT(e)||eM(e))return!1;let t=(e.type??"").toLowerCase();return"text"===t||eZ(t,eK)}function eJ(e,t){let n=eW(e);return eW(t)>n?t:e}function eW(e){let t=(e.type??"").toLowerCase(),n=0;return eZ(t,eH)?n+=100:t.includes("image")?n+=30:t.includes("textview")||"text"===t?n+=20:t.includes("view")&&(n+=10),!0===e.hittable&&(n+=20),!1!==e.enabled&&(n+=5),n}function eZ(e,t){return t.some(t=>e.includes(t))}function eX(e){process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
436
|
-
`)}function eY(e,t={}){let n=e instanceof K?H(e):e;process.stderr.write(`Error (${n.code}): ${n.message}
|
|
1
|
+
import e from"node:path";import{styleText as t}from"node:util";import{pathToFileURL as n}from"node:url";import r from"node:fs";import o from"node:crypto";import{buildMobileSnapshotPresentation as i,displayNodeLabel as a,buildSnapshotDisplayLines as s,localCommandPolicy as l,createAgentDevice as c,formatSnapshotLine as d}from"./9533.js";import{asAppError as u,normalizeError as f,AppError as p}from"./9152.js";import{ensureCompanionTunnel as m,stopCompanionTunnel as h}from"./1974.js";import{REACT_DEVTOOLS_COMPANION_RUN_ARG as g}from"./2301.js";import{REMOTE_CONFIG_FIELD_SPECS as v,resolveRemoteConfigProfile as w,resolveRemoteConfigPath as y}from"./208.js";import{createRequestId as I,withDiagnosticsScope as $,getDiagnosticsMeta as A,flushDiagnosticsToSessionFile as C,emitDiagnostic as S}from"./7599.js";import{resolveDaemonPaths as x}from"./8114.js";import{stopMetroTunnel as b}from"./metro.js";import{runCmdStreaming as _,runCmd as D}from"./9818.js";import{serializeCloseResult as N,serializeOpenResult as P,serializeInstallFromSourceResult as k,serializeDevice as R,serializeSessionListEntry as U,serializeDeployResult as E,serializeSnapshotResult as T}from"./2805.js";import{readCommandMessage as B}from"./1998.js";import{gestureCliReaders as M,selectorCliReaders as L,parseInstallSourceConfig as O,replayCliReaders as j,systemCliReaders as K,observabilityCliReaders as H,appCliReaders as F}from"./8699.js";import{captureCliReaders as G,commonInputFromFlags as V}from"./6085.js";import{interactionCliReaders as q}from"./8502.js";import{runCommand as J}from"./command-surface.js";import{resolveUserPath as z,expandUserHomePath as W}from"./3267.js";import{createLocalArtifactAdapter as Z}from"./7719.js";import{isClientBackedCliCommandName as Y}from"./5792.js";import{isCommandName as X}from"./command-metadata.js";import{mergeDefinedFlags as Q,parseOptionValueFromSource as ee,getConfigurableOptionSpecs as et,getOptionSpec as en,usage as er,parseRawArgs as eo,usageForCommand as ei,finalizeParsedArgs as ea}from"./args.js";import{readVersion as es}from"./9671.js";import{createAgentDeviceClient as el,sendToDaemon as ec}from"./9542.js";import{maybeRunUpgradeNotifier as ed}from"./113.js";function eu(e){return e.width>0&&e.height>0}function ef(e,t){return!!e&&!!t&&e.x>=t.x-2&&e.y>=t.y-2&&e.x+e.width<=t.x+t.width+2&&e.y+e.height<=t.y+t.height+2}function ep(e){return"number"!=typeof e.parentIndex}function em(e){let t=(e.type??"").toLowerCase(),n=(e.identifier??"").trim().toLowerCase();return t.includes("edittext")||t.includes("textfield")||"composer"===n}function eh(e){let t=(e.type??"").toLowerCase();return t.includes("scroll")||t.includes("list")||t.includes("recyclerview")}function eg(e){var t;let n=a(e);if(!n||n!==e.identifier?.trim()||(t=n,!/^[\w.]+:id\/[\w.-]+$/i.test(t)))return n;let r=(e.type??"").toLowerCase();return r.includes("view")||r.includes("layout")||r.includes("image")||r.includes("list")||r.includes("recyclerview")||r.includes("collection")?"":n}function ev(e){let t=e.trim().replace(/\s+/g," ").toLowerCase();return!t||/^(true|false|\d+)$/.test(t)?null:t}function ew(e,t){let n=[],r=[t];for(;r.length>0;){let t=r.pop();for(let o of e)o.parentIndex===t&&(n.push(o),r.push(o.index))}return n}function ey(e,t,n){let r=[t];for(;r.length>0;){let t=r.pop();if(!n.has(t))for(let o of(n.add(t),e))o.parentIndex!==t||n.has(o.index)||r.push(o.index)}}let eI=["button","switch","checkbox","radio"],e$=["button","image","textview","view"];function eA(e){if(!0===e.hittable||em(e))return!1;let t=(e.type??"").toLowerCase();return t.includes("text")||t.includes("image")||t.includes("icon")}function eC(e){if(!e.rect||!eu(e.rect)||ep(e)||em(e))return!1;let t=(e.type??"").toLowerCase();return"text"===t||eb(t,e$)}function eS(e,t){let n=ex(e);return ex(t)>n?t:e}function ex(e){let t=(e.type??"").toLowerCase(),n=0;return eb(t,eI)?n+=100:t.includes("image")?n+=30:t.includes("textview")||"text"===t?n+=20:t.includes("view")&&(n+=10),!0===e.hittable&&(n+=20),!1!==e.enabled&&(n+=5),n}function eb(e,t){return t.some(t=>e.includes(t))}function e_(e){process.stdout.write(`${JSON.stringify(e,null,2)}
|
|
2
|
+
`)}function eD(e,t={}){let n=e instanceof p?f(e):e;process.stderr.write(`Error (${n.code}): ${n.message}
|
|
437
3
|
`),n.hint&&process.stderr.write(`Hint: ${n.hint}
|
|
438
4
|
`),n.diagnosticId&&process.stderr.write(`Diagnostic ID: ${n.diagnosticId}
|
|
439
5
|
`),n.logPath&&process.stderr.write(`Diagnostics Log: ${n.logPath}
|
|
440
6
|
`),t.showDetails&&n.details&&process.stderr.write(`${JSON.stringify(n.details,null,2)}
|
|
441
|
-
`)}function
|
|
442
|
-
${e.summaryLines.join("\n")}`:""}function
|
|
443
|
-
`,{encoding:"utf8",mode:384}),
|
|
444
|
-
`),await
|
|
445
|
-
${
|
|
7
|
+
`)}function eN(e){return e&&e.summaryLines.length>0?`
|
|
8
|
+
${e.summaryLines.join("\n")}`:""}function eP(e){return`x=${e.x},y=${e.y},w=${e.width},h=${e.height}`}function ek(e){return e>0?`+${e}`:String(e)}function eR(e){let t=e.nearestText?` near ${JSON.stringify(e.nearestText)}`:"",n=e.regionIndex?` r${e.regionIndex}`:"";return`${e.likelyKind}${t}${n}`}function eU(e){return e.min===e.max?ek(e.min):`${ek(e.min)}..${ek(e.max)}`}function eE(t){let n=process.cwd(),r=e.relative(n,t);return""!==r&&(r.startsWith("..")||e.isAbsolute(r))?t:""===r?".":`.${e.sep}${r}`}function eT(e){return"number"==typeof e&&Number.isFinite(e)?e:0}function eB(){let e=process.env.FORCE_COLOR;return"string"==typeof e?"0"!==e:"string"!=typeof process.env.NO_COLOR&&!!process.stdout.isTTY}function eM(e,n){return n?t("dim",e):e}function eL(e){let t=e.warnings;return Array.isArray(t)?t.filter(e=>"string"==typeof e&&e.length>0):[]}function eO(e,t){var n;let r="scroll-area"===(n=e.type)||"list"===n||"collection"===n||"table"===n?n:null;if(!r)return[];let o=[];if(e.node.hiddenContentAbove&&"below"!==t&&o.push(`[content above ${r} hidden]`),e.node.hiddenContentBelow&&"above"!==t&&o.push(`[content below ${r} hidden]`),0===o.length)return[];let i=" ".repeat(e.depth+1);return o.map(e=>`${i}${e}`)}let ej={slug:"react-devtools-companion",runArg:g,displayName:"React DevTools companion"};async function eK(e){return await m({...e,definition:ej,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 eH(e){return await h({...e,definition:ej})}function eF(e){var t;let n,o=ez(e);if(!r.existsSync(o))return null;try{n=JSON.parse(r.readFileSync(o,"utf8"))}catch(t){return eQ(e,t),null}return!(!(t=n)||"object"!=typeof t||Array.isArray(t))&&1===t.version&&"string"==typeof t.session&&"string"==typeof t.remoteConfigPath&&"string"==typeof t.remoteConfigHash&&(void 0===t.daemon||"object"==typeof t.daemon&&null!==t.daemon&&!Array.isArray(t.daemon))&&"string"==typeof t.tenant&&"string"==typeof t.runId&&(void 0===t.leaseId||"string"==typeof t.leaseId)&&(void 0===t.leaseBackend||"string"==typeof t.leaseBackend)&&"string"==typeof t.connectedAt&&"string"==typeof t.updatedAt?n:(eQ(e),null)}function eG(t){let n=ez({stateDir:t.stateDir,session:t.state.session});r.mkdirSync(e.dirname(n),{recursive:!0}),eX(n,t.state),eX(eW(t.stateDir),{session:t.state.session})}function eV(e){return{baseUrl:function(e){if(!e)return;let t=new URL(e);for(let e of(t.username="",t.password="",[...t.searchParams.keys()]))/(auth|key|password|secret|token)/i.test(e)&&t.searchParams.delete(e);return t.toString().replace(/\/+$/,"")}(e.daemonBaseUrl),transport:e.daemonTransport,serverMode:e.daemonServerMode}}function eq(e){r.rmSync(ez(e),{force:!0});let t=eW(e.stateDir);eY(e.stateDir)===e.session&&r.rmSync(t,{force:!0})}function eJ(e){try{return o.createHash("sha256").update(r.readFileSync(e)).digest("hex")}catch(t){throw new p("INVALID_ARGS",`Remote config file not found: ${e}`,{cause:t instanceof Error?t.message:String(t)})}}function ez(t){return e.join(t.stateDir,"remote-connections",`${function(e){let t=e.replaceAll(/[^a-zA-Z0-9._-]/g,"_");if(!t)return"default";if(t===e)return t;let n=o.createHash("sha256").update(e).digest("hex").slice(0,8);return`${t}-${n}`}(t.session)}.json`)}function eW(t){return e.join(t,"remote-connections",".active-session.json")}function eZ(e){let t=eY(e.stateDir);return t?eF({stateDir:e.stateDir,session:t}):null}function eY(e){let t=eW(e);if(r.existsSync(t))try{let e=JSON.parse(r.readFileSync(t,"utf8"));return"string"==typeof e.session?e.session:void 0}catch{return}}function eX(e,t){r.writeFileSync(e,`${JSON.stringify(t,null,2)}
|
|
9
|
+
`,{encoding:"utf8",mode:384}),r.chmodSync(e,384)}function eQ(e,t){S({level:"warn",phase:"remote_connection_state_invalid",data:{session:e.session,cause:t instanceof Error?t.message:t?String(t):void 0}}),eq(e)}let e0=v.map(e=>e.key);function e1(e){let t={};for(let n of e0){let r=e[n];void 0!==r&&(t[n]=r)}return t}let e2=new Set(["connect","connection","close","disconnect","metro","session"]),e3=new Set(["open"]);async function e8(e){let t,n,{command:r,flags:o,client:i}=e;if(!o.remoteConfig)return{flags:o,runtime:e.runtime};let a=x(o.stateDir).baseDir,s=w({configPath:o.remoteConfig,cwd:process.cwd(),env:process.env}),l={...e1(s.profile),...o,remoteConfig:s.resolvedPath},c=eF({stateDir:a,session:l.session??"default"});if(c&&c.remoteConfigPath!==s.resolvedPath)throw new p("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:s.resolvedPath});let d=c??function(e,t){if(!e.tenant)throw new p("INVALID_ARGS","remote command requires tenant in remote config or via --tenant <id>.");if(!e.runId)throw new p("INVALID_ARGS","remote command requires runId in remote config or via --run-id <id>.");if(!e.daemonBaseUrl)throw new p("INVALID_ARGS","remote command requires daemonBaseUrl in remote config, config, env, or --daemon-base-url.");let n=new Date().toISOString();return{version:1,session:e.session??"default",remoteConfigPath:t,remoteConfigHash:eJ(t),daemon:eV(e),tenant:e.tenant,runId:e.runId,leaseId:e.leaseId,leaseBackend:e.leaseBackend??e7(e),platform:e.platform,target:e.target,connectedAt:n,updatedAt:n}}(l,s.resolvedPath),u={...l,session:d.session},f=function(e,t){if(e)return tt(e,t)?e:void 0}(d.runtime,u.platform)??e.runtime,m=d,h=!c;if(g=r,!e2.has(g)){let e=d.leaseBackend??function(e,t){let n=e7(e);if(n)return n;throw new p("INVALID_ARGS",`${t} requires --platform ios|android or --lease-backend when the remote connection has not resolved a lease yet.`)}(o,r);var g,v,y,I,$,A=d,C=o,S=e;if(A.leaseBackend&&A.leaseBackend!==S)throw new p("INVALID_ARGS","Active remote connection is already bound to a different lease backend. Re-run connect --force to replace it.",{session:A.session,leaseBackend:A.leaseBackend});if(A.platform&&C.platform&&A.platform!==C.platform)throw new p("INVALID_ARGS","Active remote connection is already bound to a different platform. Re-run connect --force to replace it.",{session:A.session,platform:A.platform});if(A.target&&C.target&&A.target!==C.target)throw new p("INVALID_ARGS","Active remote connection is already bound to a different target. Re-run connect --force to replace it.",{session:A.session,target:A.target});let t=await tn(i,m,e);u.leaseId=t.leaseId,u.leaseBackend=e,u.platform=m.platform??u.platform,u.target=m.target??u.target,(m.leaseId!==t.leaseId||m.leaseBackend!==e)&&(m={...m,leaseId:t.leaseId,leaseBackend:e,platform:m.platform??o.platform,target:m.target??o.target,updatedAt:new Date().toISOString()},h=!0)}if(v=r,y=e.batchSteps,(e3.has(v)||"batch"===v&&y&&y.some(e=>{let t=e.command.trim().toLowerCase();return e3.has(t)&&void 0===e.runtime}))&&te(u)&&(!m.leaseId&&u.leaseId&&(m={...m,leaseId:u.leaseId,leaseBackend:u.leaseBackend}),e.forceRuntimePrepare||!f||!tt(f,u.platform))){if(!m.leaseId)throw new p("INVALID_ARGS",`${r} requires a resolved remote lease before Metro runtime can be prepared.`);let e=await e5(u,i,d.remoteConfigPath,d.session,{tenantId:d.tenant,runId:d.runId,leaseId:m.leaseId});f=e.runtime;let o=(I=m.metro,$=e.cleanup,I?.projectRoot!==$?.projectRoot||I?.profileKey!==$?.profileKey||I?.consumerKey!==$?.consumerKey);t=o?m.metro:void 0,n=o?e.cleanup:void 0,m={...m,runtime:e.runtime,metro:e.cleanup,updatedAt:new Date().toISOString()},h=!0}if(h)try{eG({stateDir:a,state:m})}catch(e){throw await e9(n),e}return await e9(t),{flags:{...u,session:m.session,leaseId:m.leaseId,leaseBackend:m.leaseBackend,platform:m.platform??u.platform,target:m.target??u.target},runtime:f}}async function e5(e,t,n,r,o){if(!e.metroProjectRoot&&!e.metroPublicBaseUrl&&!e.metroProxyBaseUrl)return{};if("ios"!==e.platform&&"android"!==e.platform)throw new p("INVALID_ARGS",'Deferred Metro preparation requires platform "ios" or "android".');if(!e.metroPublicBaseUrl&&!e.metroProxyBaseUrl)throw new p("INVALID_ARGS","Deferred Metro preparation requires metroPublicBaseUrl or metroProxyBaseUrl when Metro settings are provided.");let i=await t.metro.prepare({projectRoot:e.metroProjectRoot,kind:e.metroKind,publicBaseUrl:e.metroPublicBaseUrl,proxyBaseUrl:e.metroProxyBaseUrl,bearerToken:e.metroBearerToken,bridgeScope:o,launchUrl:e.launchUrl,companionProfileKey:n,companionConsumerKey:r,port:e.metroPreparePort,listenHost:e.metroListenHost,statusHost:e.metroStatusHost,startupTimeoutMs:e.metroStartupTimeoutMs,probeTimeoutMs:e.metroProbeTimeoutMs,reuseExisting:!e.metroNoReuseExisting&&void 0,installDependenciesIfNeeded:!e.metroNoInstallDeps&&void 0,runtimeFilePath:e.metroRuntimeFile});return{runtime:"ios"===e.platform?i.iosRuntime:i.androidRuntime,cleanup:e.metroProxyBaseUrl?{projectRoot:i.projectRoot,profileKey:n,consumerKey:r}:void 0}}async function e9(e){if(e)try{await b(e)}catch{}}async function e6(e){try{await eH({projectRoot:process.cwd(),stateDir:e.stateDir,profileKey:e.state.remoteConfigPath,consumerKey:e.state.session})}catch{}}async function e4(e,t){if(t.leaseId)try{await e.leases.release({tenant:t.tenant,runId:t.runId,leaseId:t.leaseId,daemonBaseUrl:t.daemon?.baseUrl,daemonTransport:t.daemon?.transport,daemonServerMode:t.daemon?.serverMode})}catch{}}function e7(e){return e.leaseBackend?e.leaseBackend:"android"===e.platform?"android-instance":"ios"===e.platform?"ios-instance":void 0}function te(e){return!!(e.metroPublicBaseUrl||e.metroProxyBaseUrl||e.metroProjectRoot||e.metroKind)}function tt(e,t){return!e.platform||!t||"ios"!==t&&"android"!==t||e.platform===t}async function tn(e,t,n){if(t.leaseId&&t.leaseBackend===n){let r=await tr(e,t.leaseId,{tenant:t.tenant,runId:t.runId,leaseBackend:n});if(r)return r}return await e.leases.allocate({tenant:t.tenant,runId:t.runId,leaseBackend:n})}async function tr(e,t,n){try{return await e.leases.heartbeat({tenant:n.tenant,runId:n.runId,leaseId:t,leaseBackend:n.leaseBackend})}catch(e){var r;if((r=e)instanceof p&&"UNAUTHORIZED"===r.code&&(r.details?.reason==="LEASE_NOT_FOUND"||r.details?.reason==="LEASE_EXPIRED"||r.details?.reason==="LEASE_REVOKED"))return;throw e}}async function to(e){return await ti({command:e.command,flags:e.flags,stateDir:e.stateDir,allowInteractiveLogin:"connect"===e.command&&!e.flags.noLogin,env:e.env})}async function ti(e){let t=e.env??e.io?.env??process.env;if(!e.flags.daemonBaseUrl)return{flags:e.flags,source:"none"};if(ty(e.flags.daemonAuthToken))return{flags:e.flags,source:"flag"};if(ty(t.AGENT_DEVICE_DAEMON_AUTH_TOKEN))return{flags:e.flags,source:"env"};if(!function(e,t){if("1"===t.AGENT_DEVICE_CLOUD_AUTH||"true"===t.AGENT_DEVICE_CLOUD_AUTH||ty(t.AGENT_DEVICE_CLOUD_BASE_URL))return!0;try{let t=new URL(e).hostname.toLowerCase();return"agent-device.dev"===t||t.endsWith(".agent-device.dev")}catch{return!1}}(e.flags.daemonBaseUrl,t))return{flags:e.flags,source:"none"};let n=await td({stateDir:e.stateDir,flags:e.flags,env:t,io:e.io});if(n)return{flags:{...e.flags,daemonAuthToken:n.accessToken},source:"cli-session"};if(!e.allowInteractiveLogin){if(e.flags.noLogin)throw new p("UNAUTHORIZED","Remote daemon authentication is required.",{hint:"Run agent-device auth login, unset --no-login, or set AGENT_DEVICE_DAEMON_AUTH_TOKEN."});throw tm(e.command,t)}let r=await ts({stateDir:e.stateDir,flags:e.flags,env:t,io:e.io});return{flags:{...e.flags,daemonAuthToken:r.accessToken},source:"login"}}async function ta(e){let t=e.env??e.io?.env??process.env;if(ty(e.flags.daemonAuthToken))return{accessToken:e.flags.daemonAuthToken,cloudBaseUrl:th(t)};if(ty(t.AGENT_DEVICE_DAEMON_AUTH_TOKEN))return{accessToken:t.AGENT_DEVICE_DAEMON_AUTH_TOKEN,cloudBaseUrl:th(t)};let n=await td({stateDir:e.stateDir,flags:e.flags,env:t,io:e.io});if(n)return{accessToken:n.accessToken,cloudBaseUrl:n.cloudBaseUrl};if(e.flags.noLogin)throw new p("UNAUTHORIZED","Cloud connection profile authentication is required.",{hint:"Run agent-device auth login, unset --no-login, or set AGENT_DEVICE_DAEMON_AUTH_TOKEN."});let r=await ts({stateDir:e.stateDir,flags:e.flags,env:t,io:e.io,commandLabel:"agent-device connect"});return{accessToken:r.accessToken,cloudBaseUrl:r.session.cloudBaseUrl}}async function ts(t){let n,o,i,a=t.env??t.io?.env??process.env,s=(d=a,u=t.io,n=u?.stdinIsTTY??process.stdin.isTTY,o=u?.stdoutIsTTY??process.stdout.isTTY,"true"!==(f=d).CI&&"true"!==f.GITHUB_ACTIONS&&"true"!==f.BUILDKITE&&n&&o?(m=d).SSH_TTY||m.SSH_CONNECTION||"true"===m.CODESPACES||m.GITPOD_WORKSPACE_ID||"true"===m.REMOTE_CONTAINERS?"device-code":"local-browser":"non-interactive");if("non-interactive"===s)throw tm(t.commandLabel??"agent-device connect",a);let l=th(a),c=await tp({baseUrl:l,pathName:"/api/control-plane/device-auth/start",body:{client:"agent-device",tenant:t.flags.tenant,runId:t.flags.runId,daemonBaseUrl:t.flags.daemonBaseUrl,session:t.flags.session},fetchImpl:t.io?.fetch});var d,u,f,m,h,g,v=c;if(!ty(v.deviceCode)||!ty(v.userCode)||!ty(v.verificationUri))throw new p("COMMAND_FAILED","Cloud auth start returned an unusable response.");let w=c.verificationUriComplete??c.verificationUri,y="local-browser"===s?c.verificationUri:(h=c.verificationUri,g=c.userCode,(i=new URL(h)).searchParams.set("user_code",g),i.toString());"local-browser"===s?(tv(t.io,`Opening ${c.verificationUri}...
|
|
10
|
+
`),await tg(w,t.io)):tv(t.io,`Open this URL on your machine:
|
|
11
|
+
${y}
|
|
446
12
|
|
|
447
13
|
Waiting for approval for 10 minutes...
|
|
448
|
-
`);let
|
|
449
|
-
`,{mode:384});try{
|
|
14
|
+
`);let I=await tf({cloudBaseUrl:l,deviceCode:c.deviceCode,expiresIn:c.expiresIn,interval:c.interval,fetchImpl:t.io?.fetch,now:t.io?.now}),$=I.cliSession?.refreshCredential??I.cliSession?.refreshToken;if(!ty(I.accessToken)||!ty($))throw new p("UNAUTHORIZED","Device authorization did not return CLI credentials.");let A=new Date(t.io?.now?.()??Date.now()).toISOString(),C={version:1,id:I.cliSession?.id??`cli-${Date.now().toString(36)}`,cloudBaseUrl:l,workspaceId:I.cliSession?.workspaceId,accountId:I.cliSession?.accountId,name:I.cliSession?.name,refreshCredential:$,createdAt:A,expiresAt:I.cliSession?.expiresAt};return function(t){let n=tc(t.stateDir);r.mkdirSync(e.dirname(n),{recursive:!0,mode:448}),r.writeFileSync(n,`${JSON.stringify(t.session,null,2)}
|
|
15
|
+
`,{mode:384});try{r.chmodSync(n,384)}catch{}}({stateDir:t.stateDir,session:C}),{accessToken:I.accessToken,expiresAt:I.expiresAt,session:C}}function tl(e){let t=tc(e.stateDir);if(!r.existsSync(t))return null;try{let e=JSON.parse(r.readFileSync(t,"utf8"));if(1!==e.version||!ty(e.id)||!ty(e.cloudBaseUrl)||!ty(e.refreshCredential)||!ty(e.createdAt))return null;return e}catch{return null}}function tc(t){return e.join(t,"auth","cli-session.json")}async function td(e){let t=tl({stateDir:e.stateDir});return!t||tw(t.expiresAt,e.io?.now)?null:{accessToken:(await tu({session:t,flags:e.flags,env:e.env,io:e.io})).accessToken,cloudBaseUrl:th(e.env,t.cloudBaseUrl)}}async function tu(e){let t=th(e.env,e.session.cloudBaseUrl),n=await tp({baseUrl:t,pathName:"/api/control-plane/cli-session/refresh",body:{refreshCredential:e.session.refreshCredential,tenant:e.flags.tenant,runId:e.flags.runId,daemonBaseUrl:e.flags.daemonBaseUrl,session:e.flags.session},fetchImpl:e.io?.fetch});if(ty(n.accessToken))return{accessToken:n.accessToken,expiresAt:n.expiresAt};if("revoked"===n.status||"revoked"===n.error)throw new p("UNAUTHORIZED","Stored cloud CLI session was revoked.",{hint:"Run agent-device auth login again, or set AGENT_DEVICE_DAEMON_AUTH_TOKEN.",status:n.status,error:n.error});throw new p("UNAUTHORIZED","Failed to refresh CLI session.",{hint:"Run agent-device auth login again, or set AGENT_DEVICE_DAEMON_AUTH_TOKEN.",status:n.status,error:n.error})}async function tf(e){let t=e.now??Date.now,n=Math.min((e.expiresIn??600)*1e3,6e5),r=t()+n,o=Math.max(1e3,(e.interval??5)*1e3);for(;t()<r;){let t=await tp({baseUrl:e.cloudBaseUrl,pathName:"/api/control-plane/device-auth/poll",body:{deviceCode:e.deviceCode},fetchImpl:e.fetchImpl});if("approved"===t.status||ty(t.accessToken))return t;if("slow_down"===t.status||"slow_down"===t.error)o+=1e3;else if("authorization_pending"!==t.status&&"authorization_pending"!==t.error)throw new p("UNAUTHORIZED","Device authorization was not approved.",{status:t.status,error:t.error});await tI(o)}throw new p("TIMEOUT","Device authorization expired before approval.")}async function tp(e){let t=e.fetchImpl??fetch,n=await t(new URL(e.pathName,e.baseUrl),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e.body),signal:AbortSignal.timeout(15e3)}),r=await n.text(),o={};if(r.trim().length>0)try{o=JSON.parse(r)}catch(e){throw new p("COMMAND_FAILED",`Cloud auth endpoint returned invalid JSON (${n.status}).`,{status:n.status},e instanceof Error?e:void 0)}if(!n.ok)throw new p("UNAUTHORIZED","Cloud auth endpoint rejected the request.",{status:n.status,response:o});return o}function tm(e,t){let n=th(t);return new p("UNAUTHORIZED",`${e} cannot perform interactive login in CI or a non-interactive shell.`,{hint:`Create a service/API token: ${new URL("/api-keys",n).toString()} Then set AGENT_DEVICE_DAEMON_AUTH_TOKEN=adc_live_...`})}function th(e,t){let n=e.AGENT_DEVICE_CLOUD_BASE_URL??t??"https://cloud.agent-device.dev";try{return new URL(n).toString().replace(/\/+$/,"")}catch(e){throw new p("INVALID_ARGS","Invalid AGENT_DEVICE_CLOUD_BASE_URL.",{cloudBaseUrl:n},e instanceof Error?e:void 0)}}async function tg(e,t){if(t?.openBrowser)return void await t.openBrowser(e);let n=process.platform;try{"darwin"===n?await D("open",[e],{allowFailure:!0,timeoutMs:5e3}):"win32"===n?await D("cmd",["/c","start","",e],{allowFailure:!0,timeoutMs:5e3}):await D("xdg-open",[e],{allowFailure:!0,timeoutMs:5e3})}catch{tv(t,`Open this URL on your machine:
|
|
450
16
|
${e}
|
|
451
|
-
`)}}function
|
|
452
|
-
`,{mode:384});try{
|
|
453
|
-
`))}let tJ=async({flags:e,client:t})=>{var n,o,s,a;let i=y(e.stateDir).baseDir,l=e.remoteConfig?function(e){if(!e.remoteConfig)throw new K("INVALID_ARGS","connect requires --remote-config <path>.");let t=M({configPath:e.remoteConfig,cwd:process.cwd(),env:process.env});return{flags:e,remoteConfigPath:t.resolvedPath}}(e):await tK({flags:e,stateDir:i,cwd:process.cwd(),env:process.env}),c=l.flags,d=c.tenant,u=c.runId;if(!d)throw new K("INVALID_ARGS","connect requires tenant in remote config or via --tenant <id>.");if(!u)throw new K("INVALID_ARGS","connect requires runId in remote config or via --run-id <id>.");if(!c.daemonBaseUrl)throw new K("INVALID_ARGS","connect requires daemonBaseUrl in remote config, config, env, or --daemon-base-url.");let p=c.session?null:td({stateDir:i}),f=c.session??p?.session??function(e){for(let t=0;t<8;t+=1){let t=`adc-${r.randomBytes(3).toString("hex")}`;if(!to({stateDir:e,session:t}))return t}return`adc-${Date.now().toString(36)}-${r.randomBytes(2).toString("hex")}`}(i),m=ti(l.remoteConfigPath),h=ts(c),g=p?.session===f?p:to({stateDir:i,session:f});if(g&&(n=g,o={flags:c,session:f,remoteConfigPath:l.remoteConfigPath,remoteConfigHash:m,desiredLeaseBackend:tA(c),daemon:h},n.remoteConfigPath!==o.remoteConfigPath||n.remoteConfigHash!==o.remoteConfigHash||n.session!==o.session||n.tenant!==o.flags.tenant||n.runId!==o.flags.runId||void 0!==o.desiredLeaseBackend&&n.leaseBackend!==o.desiredLeaseBackend||void 0!==o.flags.platform&&n.platform!==o.flags.platform||void 0!==o.flags.target&&n.target!==o.flags.target||(s=n.daemon,a=o.daemon,(s?.baseUrl??void 0)!==(a?.baseUrl??void 0)||(s?.transport??void 0)!==(a?.transport??void 0)||(s?.serverMode??void 0)!==(a?.serverMode??void 0)))&&!c.force)throw new K("INVALID_ARGS","A different remote connection is already active for this session. Re-run connect with --force to replace it.",{session:f,remoteConfig:g.remoteConfigPath});let v=new Date().toISOString(),w={version:1,session:f,remoteConfigPath:l.remoteConfigPath,remoteConfigHash:m,daemon:h,tenant:d,runId:u,leaseId:g&&!c.force?g.leaseId:void 0,leaseBackend:g&&!c.force?g.leaseBackend:tA(c),platform:c.platform??(g&&!c.force?g.platform:void 0),target:c.target??(g&&!c.force?g.target:void 0),runtime:g&&!c.force?g.runtime:void 0,metro:g&&!c.force?g.metro:void 0,connectedAt:g&&!c.force?g.connectedAt:v,updatedAt:v};tr({stateDir:i,state:w}),g&&c.force&&(await tb(g.metro),await tI({stateDir:i,state:g}),await tx(t,g));let b=tX(w),I=function(e,t){if(!t.runtime&&(t$(e)||tQ(t.remoteConfigPath)))return tY(t.remoteConfigPath)}(c,w);return tz(c,t0(w,I),()=>[`Connected remote session "${f}" tenant "${d}" run "${u}" ${w.leaseId?`lease ${w.leaseId}`:"lease pending"}`,b?.message,I?.message].filter(e=>!!e).join("\n")),!0},tW=async({flags:e,client:t})=>{let n=e.session??"default",o=y(e.stateDir).baseDir,r=to({stateDir:o,session:n})??(e.session?null:td({stateDir:o}));if(!r)return tz(e,{connected:!1,session:n},()=>`No remote connection for "${n}".`),!0;let s=r.session;try{await t.sessions.close({shutdown:e.shutdown})}catch{}await tb(r.metro),await tI({stateDir:o,state:r});let a=!1;if(r.leaseId)try{a=(await t.leases.release({tenant:r.tenant,runId:r.runId,leaseId:r.leaseId})).released}catch{}return ta({stateDir:o,session:s}),tz(e,{connected:!1,session:s,released:a},()=>`Disconnected remote session "${s}".`),!0},tZ=async({positionals:e,flags:t})=>{if("status"!==e[0])throw new K("INVALID_ARGS","connection accepts only: status");let n=t.session??"default",o=y(t.stateDir).baseDir,r=to({stateDir:o,session:n})??(t.session?null:td({stateDir:o}));if(!r)return tz(t,{connected:!1,session:n},()=>`No remote connection for "${n}".`),!0;let s=tX(r),a=function(e){if(!e.runtime&&tQ(e.remoteConfigPath))return tY(e.remoteConfigPath)}(r);return tz(t,t0(r,a),()=>[`Connected remote session "${r.session}".`,`tenant=${r.tenant} runId=${r.runId} leaseId=${r.leaseId??"pending"} backend=${r.leaseBackend??"pending"}`,`remoteConfig=${r.remoteConfigPath}`,r.runtime?"metro=prepared":"metro=not-prepared",s?.message,a?.message].filter(e=>!!e).join("\n")),!0};function tX(e){if(!e.leaseId)return{status:"deferred",nextSteps:["agent-device install-from-source <artifact-url> --platform ios|android","agent-device open <app-id> --relaunch","agent-device snapshot -i","agent-device devices"],message:"Lease allocation is pending; run install-from-source, open, snapshot, or devices when ready to allocate or refresh the lease."+(void 0===e.platform&&void 0===e.leaseBackend?" Add --platform ios|android if the profile does not set a platform.":"")}}function tY(e){let t=`agent-device metro prepare --remote-config ${e}`;return{status:"deferred",nextStep:t,message:`Metro runtime is not prepared yet; it will be prepared automatically on first open, or run "${t}" to inspect it before launch.`}}function tQ(e){try{let t=M({configPath:e,cwd:process.cwd(),env:process.env}).profile;return!!(t.metroPublicBaseUrl||t.metroProxyBaseUrl||t.metroProjectRoot||t.metroKind)}catch{return!1}}function t0(e,t){let n=tX(e);return{connected:!0,session:e.session,tenant:e.tenant,runId:e.runId,leaseAllocated:!!e.leaseId,leaseId:e.leaseId,leaseBackend:e.leaseBackend,platform:e.platform,target:e.target,remoteConfig:e.remoteConfigPath,remoteConfigHash:e.remoteConfigHash,daemonBaseUrlFingerprint:function(e){if(e)return r.createHash("sha256").update(e).digest("hex").slice(0,12)}(e.daemon?.baseUrl),metro:e.metro?{prepared:!0,projectRoot:e.metro.projectRoot}:{prepared:!1},...n?{leasePreparation:n}:{},...t?{runtimePreparation:t}:{},connectedAt:e.connectedAt,updatedAt:e.updatedAt}}let t1=async({positionals:e,flags:t})=>{let n=e[0]??"status",r=y(t.stateDir).baseDir;if("status"===n){var s;let e,n=(e=t_({stateDir:(s={stateDir:r}).stateDir}))?{authenticated:!0,source:"cli-session",sessionId:e.id,cloudBaseUrl:e.cloudBaseUrl,workspaceId:e.workspaceId,accountId:e.accountId,name:e.name,createdAt:e.createdAt,expiresAt:e.expiresAt,expired:tG(e.expiresAt,s.now)}:{authenticated:!1,source:"none"};return tz(t,n,()=>{var e;return(e=n).authenticated?["Authenticated with cloud CLI session.",`cloud=${e.cloudBaseUrl}`,`session=${e.sessionId}`,e.workspaceId?`workspace=${e.workspaceId}`:null,e.accountId?`account=${e.accountId}`:null,e.expiresAt?`expiresAt=${e.expiresAt}`:null,e.expired?"status=expired":null].filter(e=>!!e).join("\n"):"Not authenticated."}),!0}if("login"===n){let e=await tU({stateDir:r,flags:t,commandLabel:"agent-device auth login"});return tz(t,{authenticated:!0,source:"cli-session",sessionId:e.session.id,cloudBaseUrl:e.session.cloudBaseUrl,workspaceId:e.session.workspaceId,accountId:e.session.accountId,expiresAt:e.session.expiresAt,agentTokenExpiresAt:e.expiresAt},()=>"Authenticated with cloud CLI session."),!0}if("logout"===n){let e,n=(e=tE({stateDir:r}.stateDir),!!o.existsSync(e)&&(o.rmSync(e,{force:!0}),!0));return tz(t,{authenticated:!1,removed:n},()=>n?"Removed stored cloud CLI session.":"No stored cloud CLI session."),!0}throw new K("INVALID_ARGS","auth accepts only: status, login, logout")};function t2(e){return t8(v(e))}function t3(e){let t=e.ref??"",n=e.x,o=e.y;return t&&"number"==typeof n&&"number"==typeof o?{data:e,text:`Tapped @${t} (${n}, ${o})`}:t8(e)}function t8(e){return{data:e,text:w(e)}}function t5(e){let t=e.kind?` ${e.kind}`:"",n=e.target?` target=${e.target}`:"",o="boolean"==typeof e.booted?` booted=${e.booted}`:"";return`${e.name} (${e.platform}${t}${n})${o}`}function t4(e,t,n){"string"==typeof n&&e.push(` ${t}: ${n}`)}function t6(e,t){return t.map(t=>void 0!==e[t]&&null!==e[t]?`${t}=${e[t]}`:"").filter(Boolean).join(" ")||void 0}function t7(e){if(!Array.isArray(e))return;let t=e.filter(e=>"string"==typeof e&&e.length>0);return t.length>0?t.join("\n"):void 0}function t9(e){return e.filter(e=>!!e).join("\n")||void 0}function ne(e,t){return e?`Performance: ${e}`:`Frame health: unavailable - ${t}`}function nt(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:void 0}function nn(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function no(e){return`${Number.isInteger(e)?e:e.toFixed(1)}%`}function nr(e){let t=Math.max(0,Math.round(e));if(t<1e3)return`${t}ms`;let n=Math.round(t/1e3);if(n<60)return`${n}s`;let o=Math.floor(n/60),r=n%60;return r>0?`${o}m ${r}s`:`${o}m`}function ns(e){return({result:t})=>e(t)}let na=ns(function(e){return t8(e)}),ni={boot:ns(function(e){let t=e.platform??"unknown",n=e.device??e.id??"unknown";return{data:e,text:`Boot ready: ${n} (${t})`}}),click:ns(t3),press:ns(t3),batch:ns(function(e){let t="number"==typeof e.total?e.total:0,n="number"==typeof e.executed?e.executed:0,o="number"==typeof e.totalDurationMs?e.totalDurationMs:void 0,r=[`Batch completed: ${n}/${t} steps${void 0!==o?` in ${o}ms`:""}`];for(let t of Array.isArray(e.results)?e.results:[]){let e=function(e){let t=nt(e);if(!t)return;let n="number"==typeof t.step?t.step:void 0,o="string"==typeof t.command?t.command:"step",r=!1!==t.ok,s=function(e,t,n){var o;return t?w(nt(e.data))??n:(o=nt(e.error),("string"==typeof o?.message&&o.message.length>0?o.message:null)??n)}(t,r,o),a=void 0!==n?`${n}. `:"- ",i="number"==typeof t.durationMs?t.durationMs:void 0,l=void 0!==i?` (${i}ms)`:"";return`${a}${r?"OK":"FAILED"} ${s}${l}`}(t);e&&r.push(e)}return{data:e,text:r.join("\n")}}),devices:ns(function(e){return{data:{devices:e.map($)},text:e.map(t5).join("\n")}}),apps:({input:e,result:t})=>{var n;return{data:{apps:(n={result:t,appsFilter:e.appsFilter}).result},stderr:"all"===n.appsFilter?"Showing all apps, including system apps.\n":"Showing user-installed apps. Use --all to include system apps.\n",text:n.result.length>0?n.result.join("\n"):"all"===n.appsFilter?"No apps found.":"No user-installed apps found."}},session:ns(function(e){let t={sessions:e.sessions.map(a)};return{data:t,text:JSON.stringify(t,null,2)}}),open:ns(function(e){return t8(h(e))}),close:ns(function(e){return t8(d(e))}),install:ns(t2),reinstall:ns(t2),"install-from-source":ns(function(e){return t8(x(e))}),snapshot:({input:e,result:t})=>{var n;let o;return{data:o=i((n={result:t,raw:e.raw,interactiveOnly:e.interactiveOnly,scope:e.scope,depth:e.depth}).result),jsonData:function(e){let{unchanged:t,...n}=e;return n}(o),text:function(e,t={}){var n,o,r,s;let a,i,l,c,d,u=e.nodes,p=Array.isArray(u)?u:[],f="string"==typeof e.backend?e.backend:void 0,m=function(e,t,n){let o;if(n.raw||!((o=e.androidSnapshot)&&"object"==typeof o&&"android-helper"===o.backend))return{nodes:t,filteredCount:0};let r=function(e){var t,n;if(0===e.length)return e;let o=new Set,r=new Map,s=new Map(e.map(e=>[e.index,e]));(function(e,t){for(let n of e)!n.rect||eL(n.rect)||eT(n)||eV(e,n.index,t)})(e,o),function(e,t,n,o){for(let r of e){if(n.has(r.index)||r.rect||eT(r))continue;let s=function(e,t,n){let o=e;for(;"number"==typeof o.parentIndex;){let e=t.get(o.parentIndex);if(!e)break;if(n(e))return e;o=e}return null}(r,t,eB);s&&function(e,t,n){if(!n)return;let o=e.get(t.index)??t;e.set(t.index,{...o,hiddenContentAbove:!0===o.hiddenContentAbove||"above"===n||void 0,hiddenContentBelow:!0===o.hiddenContentBelow||"below"===n||void 0})}(o,s,function(e,t){let n=t.filter(t=>t.parentIndex===e.parentIndex&&t.rect&&eL(t.rect)).map(e=>e.index);return 0===n.length?null:e.index<Math.min(...n)?"above":e.index>Math.max(...n)?"below":null}(r,e)),eV(e,r.index,n)}}(e,s,o,r);for(let n of e){if(o.has(n.index)||!(!0===(t=n).hittable&&!eM(t)&&t.rect&&eL(t.rect)&&0===ej(t).trim().length))continue;let s=function(e,t,n){let o=[],r=[],s=new Set;for(let a of e){if(n.has(a.index)||!eq(a)||!eO(a.rect,t.rect))continue;let e=ej(a).trim().replace(/\s+/g," "),i=eF(e);r.push(a.index),!e||!i||s.has(i)||(s.add(i),o.push(e))}return{label:o.join(", "),removableIndexes:r}}(eG(e,n.index),n,o);if(s.label)for(let t of(r.set(n.index,{...n,...r.get(n.index),label:s.label}),s.removableIndexes))eV(e,t,o)}for(let t of e){if(o.has(t.index)||!(!0===(n=t).hittable&&!eM(n)&&n.rect&&eL(n.rect)&&eF(ej(n))))continue;let r=eF(ej(t));if(!r)continue;let s=eG(e,t.index).filter(e=>!o.has(e.index)&&function(e,t,n){if(!ez(t)||!eO(t.rect,e.rect))return!1;let o=eF(ej(t));return!!(o&&n!==o&&n.includes(o))}(t,e,r));for(let t of s.filter(eq))eV(e,t.index,o);let a=s.filter(e=>!eq(e)),i=new Set(a.map(e=>eF(ej(e))).filter(e=>!!e));if(!(a.length<2)&&!(i.size<2))for(let t of a)eV(e,t.index,o)}let a=new Map;for(let t of e){if(o.has(t.index)||!ez(t))continue;let n=eF(ej(t));if(!n)continue;let s=a.get(n);if(s&&function(e,t,n){var o,r,s,a;return!n.has(e.index)&&(o=e.rect,r=t.rect,!o||!r||Math.abs(o.y+o.height/2-(r.y+r.height/2))<=Math.max(o.height,r.height,1))&&(s=e,a=t,(s.parentIndex===a.parentIndex?3>=Math.abs(s.index-a.index):s.parentIndex!==a.index&&a.parentIndex!==s.index&&1>=Math.abs((s.depth??0)-(a.depth??0))&&2>=Math.abs(s.index-a.index))||function(e,t){let n=e.parentIndex===t.index?t:t.parentIndex===e.index?e:null,o=n?.index===e.index?t:n?.index===t.index?e:null;return!!n&&!!o&&eJ(n,o).index===n.index}(e,t))}(s,t,o)){let i=function(e,t,n,o,r){var s,a,i;let l=eJ(t,n),c=l.index===t.index?n:t,d=(c.type??"").toLowerCase().includes("image")?"has image":null,u=r.get(l.index)??l,p=r.get(c.index)?.presentationHints??c.presentationHints;return r.set(l.index,{...u,presentationHints:(s=u.presentationHints,a=p,i=d,[...new Set([...Array.isArray(s)?s:[],...Array.isArray(a)?a:[],...i?[i]:[]])])}),eV(e,c.index,o),r.get(l.index)??l}(e,s,t,o,r);a.set(n,i);continue}a.set(n,t)}return e.filter(e=>!o.has(e.index)).map(e=>r.get(e.index)??e)}(t);return{nodes:r,filteredCount:t.length-r.length}}(e,p,t),h=(a="string"==typeof(n=e).appName?n.appName:void 0,i="string"==typeof n.appBundleId?n.appBundleId:void 0,l=[],a&&l.push(`Page: ${a}`),i&&l.push(`App: ${i}`),l.length>0?`${l.join("\n")}
|
|
454
|
-
`:""),
|
|
455
|
-
`:"",
|
|
456
|
-
Previous refs in this scope remain valid. Use find/get/is for a targeted query, or --force-full to re-emit.`:
|
|
457
|
-
${
|
|
17
|
+
`)}}function tv(e,t){(e?.stderr??process.stderr).write(t)}function tw(e,t){if(!e)return!1;let n=Date.parse(e);return!Number.isFinite(n)||n<=(t?.()??Date.now())}function ty(e){return"string"==typeof e&&e.trim().length>0}async function tI(e){await new Promise(t=>setTimeout(t,e))}async function t$(t){let n=await ta({stateDir:t.stateDir,flags:t.flags,env:t.env,io:{env:t.env,fetch:t.fetchImpl}}),i=await tA({cloudBaseUrl:n.cloudBaseUrl,accessToken:n.accessToken,fetchImpl:t.fetchImpl}),a=function(e){try{return w(e)}catch(n){let t=u(n);throw new p("COMMAND_FAILED","Cloud connection profile returned invalid remote config.",{generatedConfigPath:e.configPath,cause:t.message},t)}}({configPath:function(t){var n;let i=function e(t){return Array.isArray(t)?t.map(e):t&&"object"==typeof t?Object.fromEntries(Object.entries(t).filter(([,e])=>void 0!==e).sort(([e],[t])=>e.localeCompare(t)).map(([t,n])=>[t,e(n)])):t}(t.profile),a=e.join(t.stateDir,"remote-connections","generated");r.mkdirSync(a,{recursive:!0,mode:448});let s=e.join(a,`cloud-${(n=i,o.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0,16))}.json`);r.writeFileSync(s,`${JSON.stringify(i,null,2)}
|
|
18
|
+
`,{mode:384});try{r.chmodSync(s,384)}catch{}return s}({stateDir:t.stateDir,profile:i}),cwd:t.cwd,env:t.env});return{flags:{...e1(a.profile),...t.flags,remoteConfig:a.resolvedPath,daemonAuthToken:n.accessToken},remoteConfigPath:a.resolvedPath}}async function tA(e){let t=e.fetchImpl??fetch,n=await t(new URL("/api/control-plane/connection-profile",e.cloudBaseUrl),{method:"GET",headers:{authorization:`Bearer ${e.accessToken}`},signal:AbortSignal.timeout(15e3)}),r=await n.text(),o={};if(r.trim())try{o=JSON.parse(r)}catch(e){throw new p("COMMAND_FAILED",`Cloud connection profile endpoint returned invalid JSON (${n.status}).`,{status:n.status},e instanceof Error?e:void 0)}if(!n.ok)throw new p("UNAUTHORIZED","Cloud connection profile endpoint rejected the request.",{status:n.status,response:o});var i=o;if(!i||"object"!=typeof i||Array.isArray(i))throw new p("COMMAND_FAILED","Cloud connection profile response is invalid.");let a=i.connection;if(!a||"object"!=typeof a)throw new p("COMMAND_FAILED","Cloud connection profile response is missing profile.");if(void 0!==a.remoteConfigProfile){var s=a.remoteConfigProfile;if(!s||"object"!=typeof s||Array.isArray(s))throw new p("COMMAND_FAILED","Cloud connection profile remoteConfigProfile is invalid.");if(0===Object.keys(s).length)throw new p("COMMAND_FAILED","Cloud connection profile remoteConfigProfile is empty.");return s}throw new p("COMMAND_FAILED","Cloud connection profile did not include remoteConfigProfile.")}function tC(e,t,n){var r;if(e.json)return void e_({success:!0,data:t});let o=n?.();o&&(r=o,process.stdout.write(r.endsWith("\n")?r:`${r}
|
|
19
|
+
`))}let tS=async({flags:e,client:t})=>{var n,r,i,a;let s=x(e.stateDir).baseDir,l=e.remoteConfig?function(e){if(!e.remoteConfig)throw new p("INVALID_ARGS","connect requires --remote-config <path>.");let t=w({configPath:e.remoteConfig,cwd:process.cwd(),env:process.env});return{flags:e,remoteConfigPath:t.resolvedPath}}(e):await t$({flags:e,stateDir:s,cwd:process.cwd(),env:process.env}),c=l.flags,d=c.tenant,u=c.runId;if(!d)throw new p("INVALID_ARGS","connect requires tenant in remote config or via --tenant <id>.");if(!u)throw new p("INVALID_ARGS","connect requires runId in remote config or via --run-id <id>.");if(!c.daemonBaseUrl)throw new p("INVALID_ARGS","connect requires daemonBaseUrl in remote config, config, env, or --daemon-base-url.");let f=c.session?null:eZ({stateDir:s}),m=c.session??f?.session??function(e){for(let t=0;t<8;t+=1){let t=`adc-${o.randomBytes(3).toString("hex")}`;if(!eF({stateDir:e,session:t}))return t}return`adc-${Date.now().toString(36)}-${o.randomBytes(2).toString("hex")}`}(s),h=eJ(l.remoteConfigPath),g=eV(c),v=f?.session===m?f:eF({stateDir:s,session:m});if(v&&(n=v,r={flags:c,session:m,remoteConfigPath:l.remoteConfigPath,remoteConfigHash:h,desiredLeaseBackend:e7(c),daemon:g},n.remoteConfigPath!==r.remoteConfigPath||n.remoteConfigHash!==r.remoteConfigHash||n.session!==r.session||n.tenant!==r.flags.tenant||n.runId!==r.flags.runId||void 0!==r.desiredLeaseBackend&&n.leaseBackend!==r.desiredLeaseBackend||void 0!==r.flags.platform&&n.platform!==r.flags.platform||void 0!==r.flags.target&&n.target!==r.flags.target||(i=n.daemon,a=r.daemon,(i?.baseUrl??void 0)!==(a?.baseUrl??void 0)||(i?.transport??void 0)!==(a?.transport??void 0)||(i?.serverMode??void 0)!==(a?.serverMode??void 0)))&&!c.force)throw new p("INVALID_ARGS","A different remote connection is already active for this session. Re-run connect with --force to replace it.",{session:m,remoteConfig:v.remoteConfigPath});let y=new Date().toISOString(),I={version:1,session:m,remoteConfigPath:l.remoteConfigPath,remoteConfigHash:h,daemon:g,tenant:d,runId:u,leaseId:v&&!c.force?v.leaseId:void 0,leaseBackend:v&&!c.force?v.leaseBackend:e7(c),platform:c.platform??(v&&!c.force?v.platform:void 0),target:c.target??(v&&!c.force?v.target:void 0),runtime:v&&!c.force?v.runtime:void 0,metro:v&&!c.force?v.metro:void 0,connectedAt:v&&!c.force?v.connectedAt:y,updatedAt:y};eG({stateDir:s,state:I}),v&&c.force&&(await e9(v.metro),await e6({stateDir:s,state:v}),await e4(t,v));let $=tN(I),A=function(e,t){if(!t.runtime&&(te(e)||tk(t.remoteConfigPath)))return tP(t.remoteConfigPath)}(c,I);return tC(c,tR(I,A),()=>[`Connected remote session "${m}" tenant "${d}" run "${u}" ${I.leaseId?`lease ${I.leaseId}`:"lease pending"}`,$?.message,A?.message].filter(e=>!!e).join("\n")),!0},tx=async({flags:e,client:t})=>{let{session:n,stateDir:r,state:o}=t_(e);if(!o)return tD(e,n),!0;let i=o.session;try{await t.sessions.close({shutdown:e.shutdown})}catch{}await e9(o.metro),await e6({stateDir:r,state:o});let a=!1;if(o.leaseId)try{a=(await t.leases.release({tenant:o.tenant,runId:o.runId,leaseId:o.leaseId})).released}catch{}return eq({stateDir:r,session:i}),tC(e,{connected:!1,session:i,released:a},()=>`Disconnected remote session "${i}".`),!0},tb=async({positionals:e,flags:t})=>{if("status"!==e[0])throw new p("INVALID_ARGS","connection accepts only: status");let{session:n,state:r}=t_(t);if(!r)return tD(t,n),!0;let o=tN(r),i=function(e){if(!e.runtime&&tk(e.remoteConfigPath))return tP(e.remoteConfigPath)}(r);return tC(t,tR(r,i),()=>[`Connected remote session "${r.session}".`,`tenant=${r.tenant} runId=${r.runId} leaseId=${r.leaseId??"pending"} backend=${r.leaseBackend??"pending"}`,`remoteConfig=${r.remoteConfigPath}`,r.runtime?"metro=prepared":"metro=not-prepared",o?.message,i?.message].filter(e=>!!e).join("\n")),!0};function t_(e){let t=e.session??"default",n=x(e.stateDir).baseDir;return{session:t,stateDir:n,state:eF({stateDir:n,session:t})??(e.session?null:eZ({stateDir:n}))}}function tD(e,t){tC(e,{connected:!1,session:t},()=>`No remote connection for "${t}".`)}function tN(e){if(!e.leaseId)return{status:"deferred",nextSteps:["agent-device install-from-source <artifact-url> --platform ios|android","agent-device open <app-id> --relaunch","agent-device snapshot -i","agent-device devices"],message:"Lease allocation is pending; run install-from-source, open, snapshot, or devices when ready to allocate or refresh the lease."+(void 0===e.platform&&void 0===e.leaseBackend?" Add --platform ios|android if the profile does not set a platform.":"")}}function tP(e){let t=`agent-device metro prepare --remote-config ${e}`;return{status:"deferred",nextStep:t,message:`Metro runtime is not prepared yet; it will be prepared automatically on first open, or run "${t}" to inspect it before launch.`}}function tk(e){try{let t=w({configPath:e,cwd:process.cwd(),env:process.env}).profile;return!!(t.metroPublicBaseUrl||t.metroProxyBaseUrl||t.metroProjectRoot||t.metroKind)}catch{return!1}}function tR(e,t){let n=tN(e);return{connected:!0,session:e.session,tenant:e.tenant,runId:e.runId,leaseAllocated:!!e.leaseId,leaseId:e.leaseId,leaseBackend:e.leaseBackend,platform:e.platform,target:e.target,remoteConfig:e.remoteConfigPath,remoteConfigHash:e.remoteConfigHash,daemonBaseUrlFingerprint:function(e){if(e)return o.createHash("sha256").update(e).digest("hex").slice(0,12)}(e.daemon?.baseUrl),metro:e.metro?{prepared:!0,projectRoot:e.metro.projectRoot}:{prepared:!1},...n?{leasePreparation:n}:{},...t?{runtimePreparation:t}:{},connectedAt:e.connectedAt,updatedAt:e.updatedAt}}let tU=async({positionals:e,flags:t})=>{let n=e[0]??"status",o=x(t.stateDir).baseDir;if("status"===n){var i;let e,n=(e=tl({stateDir:(i={stateDir:o}).stateDir}))?{authenticated:!0,source:"cli-session",sessionId:e.id,cloudBaseUrl:e.cloudBaseUrl,workspaceId:e.workspaceId,accountId:e.accountId,name:e.name,createdAt:e.createdAt,expiresAt:e.expiresAt,expired:tw(e.expiresAt,i.now)}:{authenticated:!1,source:"none"};return tC(t,n,()=>{var e;return(e=n).authenticated?["Authenticated with cloud CLI session.",`cloud=${e.cloudBaseUrl}`,`session=${e.sessionId}`,e.workspaceId?`workspace=${e.workspaceId}`:null,e.accountId?`account=${e.accountId}`:null,e.expiresAt?`expiresAt=${e.expiresAt}`:null,e.expired?"status=expired":null].filter(e=>!!e).join("\n"):"Not authenticated."}),!0}if("login"===n){let e=await ts({stateDir:o,flags:t,commandLabel:"agent-device auth login"});return tC(t,{authenticated:!0,source:"cli-session",sessionId:e.session.id,cloudBaseUrl:e.session.cloudBaseUrl,workspaceId:e.session.workspaceId,accountId:e.session.accountId,expiresAt:e.session.expiresAt,agentTokenExpiresAt:e.expiresAt},()=>"Authenticated with cloud CLI session."),!0}if("logout"===n){let e,n=(e=tc({stateDir:o}.stateDir),!!r.existsSync(e)&&(r.rmSync(e,{force:!0}),!0));return tC(t,{authenticated:!1,removed:n},()=>n?"Removed stored cloud CLI session.":"No stored cloud CLI session."),!0}throw new p("INVALID_ARGS","auth accepts only: status, login, logout")};function tE(e){return tB(E(e))}function tT(e){let t=e.ref??"",n=e.x,r=e.y;return t&&"number"==typeof n&&"number"==typeof r?{data:e,text:`Tapped @${t} (${n}, ${r})`}:tB(e)}function tB(e){return{data:e,text:B(e)}}function tM(e){let t=e.kind?` ${e.kind}`:"",n=e.target?` target=${e.target}`:"",r="boolean"==typeof e.booted?` booted=${e.booted}`:"";return`${e.name} (${e.platform}${t}${n})${r}`}function tL(e,t,n){"string"==typeof n&&e.push(` ${t}: ${n}`)}function tO(e,t){return t.map(t=>void 0!==e[t]&&null!==e[t]?`${t}=${e[t]}`:"").filter(Boolean).join(" ")||void 0}function tj(e){if(!Array.isArray(e))return;let t=e.filter(e=>"string"==typeof e&&e.length>0);return t.length>0?t.join("\n"):void 0}function tK(e){return e.filter(e=>!!e).join("\n")||void 0}function tH(e,t){return e?`Performance: ${e}`:`Frame health: unavailable - ${t}`}function tF(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:void 0}function tG(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function tV(e){return`${Number.isInteger(e)?e:e.toFixed(1)}%`}function tq(e){let t=Math.max(0,Math.round(e));if(t<1e3)return`${t}ms`;let n=Math.round(t/1e3);if(n<60)return`${n}s`;let r=Math.floor(n/60),o=n%60;return o>0?`${r}m ${o}s`:`${r}m`}function tJ(e){return({result:t})=>e(t)}let tz=tJ(function(e){return tB(e)}),tW={boot:tJ(function(e){let t=e.platform??"unknown",n=e.device??e.id??"unknown";return{data:e,text:`Boot ready: ${n} (${t})`}}),click:tJ(tT),press:tJ(tT),batch:tJ(function(e){let t="number"==typeof e.total?e.total:0,n="number"==typeof e.executed?e.executed:0,r="number"==typeof e.totalDurationMs?e.totalDurationMs:void 0,o=[`Batch completed: ${n}/${t} steps${void 0!==r?` in ${r}ms`:""}`];for(let t of Array.isArray(e.results)?e.results:[]){let e=function(e){let t=tF(e);if(!t)return;let n="number"==typeof t.step?t.step:void 0,r="string"==typeof t.command?t.command:"step",o=!1!==t.ok,i=function(e,t,n){var r;return t?B(tF(e.data))??n:(r=tF(e.error),("string"==typeof r?.message&&r.message.length>0?r.message:null)??n)}(t,o,r),a=void 0!==n?`${n}. `:"- ",s="number"==typeof t.durationMs?t.durationMs:void 0,l=void 0!==s?` (${s}ms)`:"";return`${a}${o?"OK":"FAILED"} ${i}${l}`}(t);e&&o.push(e)}return{data:e,text:o.join("\n")}}),devices:tJ(function(e){return{data:{devices:e.map(R)},text:e.map(tM).join("\n")}}),apps:({input:e,result:t})=>{var n;return{data:{apps:(n={result:t,appsFilter:e.appsFilter}).result},stderr:"all"===n.appsFilter?"Showing all apps, including system apps.\n":"Showing user-installed apps. Use --all to include system apps.\n",text:n.result.length>0?n.result.join("\n"):"all"===n.appsFilter?"No apps found.":"No user-installed apps found."}},session:tJ(function(e){let t={sessions:e.sessions.map(U)};return{data:t,text:JSON.stringify(t,null,2)}}),open:tJ(function(e){return tB(P(e))}),close:tJ(function(e){return tB(N(e))}),install:tJ(tE),reinstall:tJ(tE),"install-from-source":tJ(function(e){return tB(k(e))}),snapshot:({input:e,result:t})=>{var n;let r;return{data:r=T((n={result:t,raw:e.raw,interactiveOnly:e.interactiveOnly,scope:e.scope,depth:e.depth}).result),jsonData:function(e){let{unchanged:t,...n}=e;return n}(r),text:function(e,t={}){var n,r,o,l;let c,u,f,p,m,h=e.nodes,g=Array.isArray(h)?h:[],v="string"==typeof e.backend?e.backend:void 0,w=function(e,t,n){let r;if(n.raw||!((r=e.androidSnapshot)&&"object"==typeof r&&"android-helper"===r.backend))return{nodes:t,filteredCount:0};let o=function(e){var t,n;if(0===e.length)return e;let r=new Set,o=new Map,i=new Map(e.map(e=>[e.index,e]));(function(e,t){for(let n of e)!n.rect||eu(n.rect)||ep(n)||ey(e,n.index,t)})(e,r),function(e,t,n,r){for(let o of e){if(n.has(o.index)||o.rect||ep(o))continue;let i=function(e,t,n){let r=e;for(;"number"==typeof r.parentIndex;){let e=t.get(r.parentIndex);if(!e)break;if(n(e))return e;r=e}return null}(o,t,eh);i&&function(e,t,n){if(!n)return;let r=e.get(t.index)??t;e.set(t.index,{...r,hiddenContentAbove:!0===r.hiddenContentAbove||"above"===n||void 0,hiddenContentBelow:!0===r.hiddenContentBelow||"below"===n||void 0})}(r,i,function(e,t){let n=t.filter(t=>t.parentIndex===e.parentIndex&&t.rect&&eu(t.rect)).map(e=>e.index);return 0===n.length?null:e.index<Math.min(...n)?"above":e.index>Math.max(...n)?"below":null}(o,e)),ey(e,o.index,n)}}(e,i,r,o);for(let n of e){if(r.has(n.index)||!(!0===(t=n).hittable&&!em(t)&&t.rect&&eu(t.rect)&&0===eg(t).trim().length))continue;let i=function(e,t,n){let r=[],o=[],i=new Set;for(let a of e){if(n.has(a.index)||!eA(a)||!ef(a.rect,t.rect))continue;let e=eg(a).trim().replace(/\s+/g," "),s=ev(e);o.push(a.index),!e||!s||i.has(s)||(i.add(s),r.push(e))}return{label:r.join(", "),removableIndexes:o}}(ew(e,n.index),n,r);if(i.label)for(let t of(o.set(n.index,{...n,...o.get(n.index),label:i.label}),i.removableIndexes))ey(e,t,r)}for(let t of e){if(r.has(t.index)||!(!0===(n=t).hittable&&!em(n)&&n.rect&&eu(n.rect)&&ev(eg(n))))continue;let o=ev(eg(t));if(!o)continue;let i=ew(e,t.index).filter(e=>!r.has(e.index)&&function(e,t,n){if(!eC(t)||!ef(t.rect,e.rect))return!1;let r=ev(eg(t));return!!(r&&n!==r&&n.includes(r))}(t,e,o));for(let t of i.filter(eA))ey(e,t.index,r);let a=i.filter(e=>!eA(e)),s=new Set(a.map(e=>ev(eg(e))).filter(e=>!!e));if(!(a.length<2)&&!(s.size<2))for(let t of a)ey(e,t.index,r)}let a=new Map;for(let t of e){if(r.has(t.index)||!eC(t))continue;let n=ev(eg(t));if(!n)continue;let i=a.get(n);if(i&&function(e,t,n){var r,o,i,a;return!n.has(e.index)&&(r=e.rect,o=t.rect,!r||!o||Math.abs(r.y+r.height/2-(o.y+o.height/2))<=Math.max(r.height,o.height,1))&&(i=e,a=t,(i.parentIndex===a.parentIndex?3>=Math.abs(i.index-a.index):i.parentIndex!==a.index&&a.parentIndex!==i.index&&1>=Math.abs((i.depth??0)-(a.depth??0))&&2>=Math.abs(i.index-a.index))||function(e,t){let n=e.parentIndex===t.index?t:t.parentIndex===e.index?e:null,r=n?.index===e.index?t:n?.index===t.index?e:null;return!!n&&!!r&&eS(n,r).index===n.index}(e,t))}(i,t,r)){let s=function(e,t,n,r,o){var i,a,s;let l=eS(t,n),c=l.index===t.index?n:t,d=(c.type??"").toLowerCase().includes("image")?"has image":null,u=o.get(l.index)??l,f=o.get(c.index)?.presentationHints??c.presentationHints;return o.set(l.index,{...u,presentationHints:(i=u.presentationHints,a=f,s=d,[...new Set([...Array.isArray(i)?i:[],...Array.isArray(a)?a:[],...s?[s]:[]])])}),ey(e,c.index,r),o.get(l.index)??l}(e,i,t,r,o);a.set(n,s);continue}a.set(n,t)}return e.filter(e=>!r.has(e.index)).map(e=>o.get(e.index)??e)}(t);return{nodes:o,filteredCount:t.length-o.length}}(e,g,t),y=(c="string"==typeof(n=e).appName?n.appName:void 0,u="string"==typeof n.appBundleId?n.appBundleId:void 0,f=[],c&&f.push(`Page: ${c}`),u&&f.push(`App: ${u}`),f.length>0?`${f.join("\n")}
|
|
20
|
+
`:""),I=function(e,t,n,r={nodes:t,filteredCount:0}){let o=eL(e),i=function(e,t){if(!0===t.scoped||!0===t.depthLimited||e.length>3)return null;let n=1===e.length?"node":"nodes";return`Hint: sparse accessibility snapshot returned ${e.length} ${n}. The app may expose limited accessibility metadata; run screenshot --overlay-refs for visual context.`}(t,n);i&&o.push(i),!n.raw&&r.filteredCount>0&&o.push(`Collapsed ${r.filteredCount} Android helper node${1===r.filteredCount?"":"s"} from the agent-facing text snapshot; use --raw or --json for the full hierarchy.`);let s=r.filteredCount>0?r.nodes:t;return!n.raw&&function(e){if(e.length<20)return!1;let t=new Map;for(let n of e){let e=(n.type??"").toLowerCase(),r=function(e){var t;let n=e.trim().replace(/\s+/g," ").toLowerCase();return!n||(t=n,/\S+@\S+\.\S+/.test(t))?null:n}(a(n));if(!r)continue;let o=`${e}|${r}`,i=t.get(o)??[];i.push(n),t.set(o,i)}let n=0;for(let e of t.values())!(e.length<=1)&&function(e){for(let r=0;r<e.length;r+=1)for(let o=r+1;o<e.length;o+=1){var t,n;if(t=e[r]?.rect,n=e[o]?.rect,!t||!n||!(t.x+t.width<=n.x+.5||n.x+n.width<=t.x+.5||t.y+t.height<=n.y+.5||n.y+n.height<=t.y+.5))return!0}return!1}(e)&&(n+=e.length);return n>=8}(s)&&o.push("Warning: possible repeated nav subtree detected."),o}(e,g,t,w),$=I.length>0?`${I.join("\n")}
|
|
21
|
+
`:"",A=t.raw?null:(p=e.unchanged)&&"object"==typeof p?"number"!=typeof p.ageMs||"number"!=typeof p.nodeCount?null:{ageMs:p.ageMs,nodeCount:p.nodeCount,interactiveOnly:!0===p.interactiveOnly||void 0,scope:"string"==typeof p.scope?p.scope:void 0}:null;if(A){let e;return`${y}${$}${e=function(e){if(e<1e3)return`${Math.round(e)}ms`;if(e<6e4)return`${(Math.round(e/100)/10).toFixed(1)}s`;let t=e/6e4;return t<60?`${(Math.round(10*t)/10).toFixed(1)}m`:`${(Math.round(t/60*10)/10).toFixed(1)}h`}((r=A).ageMs),r.scope?`Scoped snapshot unchanged for scope "${r.scope}" since previous read ${e} ago.
|
|
22
|
+
Previous refs in this scope remain valid. Use find/get/is for a targeted query, or --force-full to re-emit.`:r.interactiveOnly?`Interactive snapshot unchanged since previous read ${e} ago.
|
|
23
|
+
${r.nodeCount} visible nodes are unchanged. Previous @e refs are still valid. Use find/get/is for a targeted query, or --force-full to re-emit.`:`Snapshot unchanged since previous read ${e} ago.
|
|
458
24
|
Refs from the previous snapshot are still valid. Use --force-full to re-emit the tree, or use find/get/is for a targeted query.`}
|
|
459
|
-
`}let
|
|
460
|
-
${
|
|
461
|
-
${
|
|
462
|
-
`:t.flatten?`${
|
|
463
|
-
${
|
|
464
|
-
`:`${
|
|
465
|
-
${
|
|
466
|
-
`}(
|
|
467
|
-
`:"";if(
|
|
468
|
-
`;let u=(function(e){if(0===e.length)return e;let t=e.map((e,t)=>({index:t,kind:e.kind})).filter(e=>"added"===e.kind||"removed"===e.kind).map(e=>e.index);if(0===t.length)return e;let n=Array(e.length).fill(!1);for(let
|
|
469
|
-
`:"";if(!l)return`${d}${
|
|
470
|
-
`;let
|
|
471
|
-
`})(
|
|
472
|
-
`}),!0};function
|
|
473
|
-
`)
|
|
474
|
-
`),
|
|
475
|
-
`)
|
|
476
|
-
`
|
|
477
|
-
`),"
|
|
478
|
-
`)}(t);else{for(let t of e.failures)nf(t);for(let e of n)!function(e){let t=e.durationMs>0?` (${e.durationMs}ms)`:"";process.stdout.write(`FLAKY ${e.file} after ${e.attempts} attempts${t}
|
|
479
|
-
`)}(e)}let o="number"==typeof e.durationMs?e.durationMs:void 0,r=n.length>0?`, ${n.length} flaky`:"";return process.stdout.write(`Test summary: ${e.passed} passed, ${e.failed} failed${r}${void 0!==o?` in ${o}ms`:""}
|
|
480
|
-
`),+(e.failed>0)}(n,{verbose:s})}({suite:c,verbose:l.verbose,json:l.json,reportJunit:l.reportJunit}):(tz(l,c,()=>w(c)),0));return 0!==u&&process.exit(u),!0})]})),nx={connect:tJ,disconnect:tW,connection:tZ,auth:t1,screenshot:nu,diff:np,...nb,...nI};async function nA(e){let t=nx[e.command];if(!t)return!1;let n={...e.flags};return E(e.command,n),await t({...e,flags:n})}function n$(e,t,n){return n||(e.push(t),"")}async function nk(e,t,n){let{flags:o}=t,r=function(e){var t;if(!e?.metroProxyBaseUrl||"android-instance"!==(t=e.leaseBackend)&&"ios-instance"!==t)return null;let n=[],o={serverBaseUrl:n$(n,"metroProxyBaseUrl",e.metroProxyBaseUrl),bearerToken:n$(n,"metroBearerToken",e.metroBearerToken),tenantId:n$(n,"tenant",e.tenant),runId:n$(n,"runId",e.runId),leaseId:n$(n,"leaseId",e.leaseId)};if(n.length>0)throw new K("INVALID_ARGS",`react-devtools remote bridge requires ${n.join(", ")}.`,{missing:n});return o}(o);if(!r)return n();let s=t.stateDir??process.cwd(),a=t.session??o?.session??"default",i=o?.remoteConfig??`${r.tenantId}:${r.runId}:${r.leaseId}`;if("stop"===e[0])try{return await n()}finally{await tn({projectRoot:t.cwd??process.cwd(),stateDir:s,profileKey:i,consumerKey:a})}return await tt({projectRoot:t.cwd??process.cwd(),stateDir:s,serverBaseUrl:r.serverBaseUrl,bearerToken:r.bearerToken,bridgeScope:{tenantId:r.tenantId,runId:r.runId,leaseId:r.leaseId},session:a,profileKey:i,consumerKey:a,env:t.env??process.env}),await n()}async function nS(e,t={}){let n=t.cwd??process.cwd(),o=t.env??process.env,r=await nk(e,t,async()=>(await et("npm",["exec","--yes","--package","agent-react-devtools@0.4.0","--","agent-react-devtools",...e],{cwd:n,env:o,allowFailure:!0,onStdoutChunk:e=>{process.stdout.write(e)},onStderrChunk:e=>{process.stderr.write(e)}})).exitCode);var s,a=t.flags;return 0!==r&&"wait"===(s=e)[0]&&s.includes("--connected")&&"ios-instance"===a?.leaseBackend&&process.stderr.write("Hint: Remote iOS React DevTools connects during JavaScript startup.\nIf 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`.\n"),r}function nC(e,t={}){let n=nD(t),o={...e};return n.defaultPlatform&&void 0===o.platform&&(o.platform=n.defaultPlatform),o}function nD(e){var t,n,o,r;let s,a=e.env??process.env,i=e.inheritedPlatform??e.configuredPlatform??function(e){if(void 0===e)return;let t=e.trim().toLowerCase();if(t){if("ios"===t||"android"===t||"apple"===t)return t;throw new K("INVALID_ARGS",`Invalid AGENT_DEVICE_PLATFORM: ${e}. Use ios, android, or apple.`)}}(a.AGENT_DEVICE_PLATFORM),l="string"==typeof(t=e.configuredSession??a.AGENT_DEVICE_SESSION)&&t.trim().length>0;return{defaultPlatform:i,lockPolicy:(n=e.policyOverrides,o=a,r=l,(s=n?.sessionLock??n?.sessionLockConflicts??function(e){if(void 0===e)return;let t=e.trim().toLowerCase();if(t){if("reject"===t||"strip"===t)return t;throw new K("INVALID_ARGS",`Invalid session lock mode: ${e}. Use reject or strip.`)}}(o.AGENT_DEVICE_SESSION_LOCK))||(n?.sessionLocked===!0||r?"reject":void 0))}}let nN={sendToDaemon:ed},nR=new Set(["launchUrl","metroBearerToken","metroKind","metroListenHost","metroNoInstallDeps","metroNoReuseExisting","metroPreparePort","metroProbeTimeoutMs","metroProjectRoot","metroProxyBaseUrl","metroPublicBaseUrl","metroRuntimeFile","metroStartupTimeoutMs","metroStatusHost"]),nU=new Set(["connect","connection","close","disconnect","metro","session"]);async function n_(t,n=nN){let r=W(),s=f(),a=t.includes("--debug")||t.includes("--verbose")||t.includes("-v"),i=t.includes("--json"),l=function(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("--session"===n){let n=e[t+1]?.trim();if(n&&!n.startsWith("-"))return n;break}}return null}(t)??process.env.AGENT_DEVICE_SESSION??"default";await Z({session:l,requestId:r,command:t[0],debug:a},async()=>{var l,c,d,p,f,m,h,g,v,w,b,I,x,A,$,k,S;let C,R,U,P,L,T,j;try{let n,r,s,a,i,y,I,x;l={cwd:process.cwd(),env:process.env},n=function(e){let t={json:!1,help:!1,version:!1},n=null,o=[],r=[],s=!0;for(let l=0;l<e.length;l+=1){var a,i;let c=e[l];if(s&&"--"===c){s=!1;continue}if(!s){n?o.push(c):n=eP(c);continue}let d=c.startsWith("--"),u=c.startsWith("-")&&c.length>1;if(!d&&!u){n?o.push(c):n=eP(c);continue}let[p,f]=d?eR(c):[c,void 0],m=N(p);if(a=n,i=m,"react-devtools"===a&&(!i||!eD(i.key,a))){o.push(c);continue}if(!m){if(function(e,t,n){var o;if(o=n,!/^-\d+(\.\d+)?$/.test(o)||!e)return!1;let r=_(e);if(!r||r.allowsExtraPositionals)return!0;let s=r.positionalArgs??[];return 0!==s.length&&(t.length<s.length||s.some(e=>e.includes("?")))}(n,o,c)){n?o.push(c):n=c;continue}throw new K("INVALID_ARGS",`Unknown flag: ${p}`)}let h=function(e,t,n,o){if(void 0!==e.setValue){if(void 0!==n)throw new K("INVALID_ARGS",`Flag ${t} does not take a value.`);return{value:e.setValue,consumeNext:!1}}if("boolean"===e.type){if(void 0!==n)throw new K("INVALID_ARGS",`Flag ${t} does not take a value.`);return{value:!0,consumeNext:!1}}if("booleanOrString"===e.type){if(void 0!==n){if(0===n.trim().length)throw new K("INVALID_ARGS",`Flag ${t} requires a non-empty value when provided.`);return{value:n,consumeNext:!1}}return void 0===o||e_(o)||!function(e){let t=e.trim();return!(!t||/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(t))&&!!(t.startsWith("./")||t.startsWith("../")||t.startsWith("~/")||t.startsWith("/")||t.includes("/")||t.includes("\\"))}(o)?{value:!0,consumeNext:!1}:{value:o,consumeNext:!0}}let r=n??o;if(void 0===r||void 0===n&&e_(r))throw new K("INVALID_ARGS",`Flag ${t} requires a value.`);if("string"===e.type)return{value:r,consumeNext:void 0===n};if("enum"===e.type){if(!e.enumValues?.includes(r))throw new K("INVALID_ARGS",`Invalid ${eU(t)}: ${r}`);return{value:r,consumeNext:void 0===n}}let s=Number(r);if(!Number.isFinite(s)||"number"==typeof e.min&&s<e.min||"number"==typeof e.max&&s>e.max)throw new K("INVALID_ARGS",`Invalid ${eU(t)}: ${r}`);return{value:Math.floor(s),consumeNext:void 0===n}}(m,p,f,e[l+1]);h.consumeNext&&(l+=1);let g=t[m.key];if(m.multiple){let e=Array.isArray(g)?[...g,h.value]:void 0===g?[h.value]:[g,h.value];t[m.key]=e}else t[m.key]=h.value;r.push({key:m.key,token:p})}return{command:n,positionals:o,flags:t,warnings:[],providedFlags:r}}(t),r=l?.env??process.env,s=l?.cwd??process.cwd(),p=n.command,a=null!==p&&(c={remoteConfig:n.flags.remoteConfig,cwd:s,env:r}).remoteConfig?{...th(M({configPath:c.remoteConfig,cwd:c.cwd,env:c.env}).profile),remoteConfig:c.remoteConfig}:{},I=ep((i=(d={command:n.command,cwd:s,cliFlags:n.flags,env:r}).env??process.env,y=ep({},function(e){let t={};for(let n of e)ep(t,function(e,t){let n,r;if(!o.existsSync(e)){if(t)throw new K("INVALID_ARGS",`Config file not found: ${e}`);return{}}try{n=o.readFileSync(e,"utf8")}catch(t){throw new K("INVALID_ARGS",`Failed to read config file: ${e}`,{cause:t instanceof Error?t.message:String(t)})}try{r=JSON.parse(n)}catch(t){throw new K("INVALID_ARGS",`Invalid JSON in config file: ${e}`,{cause:t instanceof Error?t.message:String(t)})}if(!r||"object"!=typeof r||Array.isArray(r))throw new K("INVALID_ARGS",`Config file must contain a JSON object: ${e}`);return function(e,t){let n={};for(let[o,r]of Object.entries(e)){if("installSource"===o){n.installSource=u(r,t);continue}let e=eC.get(o);if(!e)throw new K("INVALID_ARGS",`Unknown config key "${o}" in ${t}.`);if(!e.config.enabled)throw new K("INVALID_ARGS",`Unsupported config key "${o}" in ${t}.`);n[o]=F(eN(e),r,t,o)}return n}(r,`config file ${e}`)}(n.path,n.required));return t}((f=d.cwd,m=d.cliFlags.config,h=i,(x=m??h.AGENT_DEVICE_CONFIG)?[{path:(g=x,v=f,w=h,ea(g,{cwd:v,env:w})),required:!0}]:[{path:(b=h,e.join(ei("~",{env:b}),".agent-device","config.json")),required:!1},{path:e.resolve(f,"agent-device.json"),required:!1}]))),ep(y,function(e,t){var n,o,r;let s={};for(let a of eS.filter(e=>e.config.enabled&&e.supportsCommand(t))){if("installSource"===a.key)continue;let t=a.env.names.map(t=>({name:t,value:e[t]})).find(e=>"string"==typeof e.value&&e.value.trim().length>0);t&&(s[a.key]=(n=t.value,o=`environment variable ${t.name}`,r=t.name,F(eN(a),n,o,r)))}return s}(i,d.command))),a),C={...function(e,t){let n=t?.strictFlags??!0,o=[...e.warnings],r=ep({json:!1,help:!1,version:!1},t?.defaultFlags??{});ep(r,e.flags);let s=e.providedFlags.filter(t=>!eD(t.key,e.command));if(s.length>0){var a,i;let t=s.map(e=>e.token),l=(a=e.command,i=t,a?1===i.length?`Flag ${i[0]} is not supported for command ${a}.`:`Flags ${i.join(", ")} are not supported for command ${a}.`:1===i.length?`Flag ${i[0]} requires a command that supports it.`:`Flags ${i.join(", ")} require a command that supports them.`);if(n)throw new K("INVALID_ARGS",l);for(let e of(o.push(l),s))delete r[e.key]}for(let t of Object.keys(r))void 0!==r[t]&&(eD(t,e.command)||delete r[t]);if(function(e){if("back"===e.command&&!(new Set(e.providedFlags.filter(e=>"backMode"===e.key).map(e=>e.token)).size<=1))throw new K("INVALID_ARGS","back accepts only one explicit mode flag: use either --in-app or --system.")}(e),E(e.command,r),"batch"===e.command&&1!=+!!r.steps+ +!!r.stepsFile)throw new K("INVALID_ARGS","batch requires exactly one step source: --steps or --steps-file.");return function(e){if(e.flags.help)return e;if("snapshot"===e.command&&e.flags.snapshotDiff){let{snapshotDiff:t,...n}=e.flags;return{command:"diff",positionals:["snapshot",...e.positionals],flags:n,warnings:e.warnings}}return e}({command:e.command,positionals:e.positionals,flags:r,warnings:o})}(n,{strictFlags:l?.strictFlags,defaultFlags:I}),providedFlags:n.providedFlags}}catch(t){Q({level:"error",phase:"cli_parse_failed",data:{error:t instanceof Error?t.message:String(t)}});let e=H(t,{diagnosticId:X().diagnosticId,logPath:Y({force:!0})??void 0});i?eX({success:!1,error:e}):eY(e,{showDetails:a}),process.exit(1);return}for(let e of C.warnings)process.stderr.write(`Warning: ${e}
|
|
481
|
-
`);C.flags.version&&(process.stdout.write(`${s}
|
|
482
|
-
`),process.exit(0));let G="help"===C.command,q=C.flags.help;if(G||q){G&&C.positionals.length>1&&(eY(new K("INVALID_ARGS","help accepts at most one command.")),process.exit(1));let e=G?C.positionals[0]:C.command;e||(process.stdout.write(`${eE()}
|
|
483
|
-
`),process.exit(0));let t=function(e){var t,n;let o,r=(o=ew[e])?`${o.body}
|
|
484
|
-
|
|
485
|
-
Related:
|
|
486
|
-
agent-device help command list and global flags
|
|
487
|
-
agent-device help <command> command-specific flags
|
|
488
|
-
agent-device help workflow normal app automation loop
|
|
489
|
-
`:null;if(r)return r;let s=_(e);if(!s)return null;let a=(t=e,(n=s).usageOverride?n.usageOverride:[t,...(n.positionalArgs??[]).map(ey),...(n.allowedFlags??[]).flatMap(e=>{var t;return(t=e,O().filter(e=>e.key===t)).map(e=>e.usageLabel??e.names[0])}).map(e=>`[${e}]`)].join(" ")),i=eb(new Set(s.allowedFlags??[])),l=eb(D),c=[];return i.length>0&&c.push(eI("Command flags:",i)),c.push(eI("Global flags:",l)),`agent-device ${a}
|
|
490
|
-
|
|
491
|
-
${s.helpDescription}
|
|
492
|
-
|
|
493
|
-
Usage:
|
|
494
|
-
agent-device ${a}
|
|
495
|
-
|
|
496
|
-
${c.join("\n\n")}
|
|
497
|
-
`}(eP(e));t&&(process.stdout.write(t),process.exit(0)),eY(new K("INVALID_ARGS",`Unknown command: ${e}`)),process.stdout.write(`${eE()}
|
|
498
|
-
`),process.exit(1)}C.command||(process.stdout.write(`${eE()}
|
|
499
|
-
`),process.exit(1));let{command:z,positionals:J}=C,W=new Set(C.providedFlags.map(e=>e.key));try{U=(R=nD({policyOverrides:C.flags,configuredPlatform:C.flags.platform,configuredSession:C.flags.session})).lockPolicy?{...C.flags}:nC(C.flags,{policyOverrides:C.flags,configuredPlatform:C.flags.platform,configuredSession:C.flags.session}),P=y(U.stateDir),L=U.session??"default",I={command:z,explicitFlagKeys:W,stateDir:P.baseDir,session:L,remoteConfig:U.remoteConfig,hasResolvedSession:void 0!==U.session},j=(T="connect"===I.command||"connection"===I.command?null:function(e){let t=e.validateRemoteConfigHash??!0,n=e.remoteConfig?B({configPath:e.remoteConfig,cwd:e.cwd,env:e.env}):void 0,o=to(e)??(e.allowActiveFallback?td({stateDir:e.stateDir}):null);if(!o||n&&o.remoteConfigPath!==n)return null;if(t&&ti(o.remoteConfigPath)!==o.remoteConfigHash)throw new K("INVALID_ARGS","Active remote connection config changed. Run agent-device connect --force to refresh it.",{remoteConfig:o.remoteConfigPath});let r=function(e,t){try{return M({configPath:e.remoteConfigPath,cwd:t.cwd,env:t.env}).profile}catch(e){if(!1===t.validateRemoteConfigHash)return{};throw e}}(o,e);return{runtime:o.runtime,flags:{...r,remoteConfig:o.remoteConfigPath,daemonBaseUrl:o.daemon?.baseUrl??r.daemonBaseUrl,daemonTransport:o.daemon?.transport??r.daemonTransport,daemonServerMode:o.daemon?.serverMode??r.daemonServerMode,tenant:o.tenant,sessionIsolation:"tenant",runId:o.runId,leaseId:o.leaseId,leaseBackend:o.leaseBackend,session:o.session,platform:o.platform??r.platform,target:o.target??r.target}}}({stateDir:I.stateDir,session:I.session,remoteConfig:I.remoteConfig,cwd:process.cwd(),env:process.env,allowActiveFallback:!I.explicitFlagKeys.has("session")&&(!I.remoteConfig||"disconnect"===I.command||!I.hasResolvedSession),validateRemoteConfigHash:"disconnect"!==I.command}))?function(e,t,n){let o={...e};for(let[e,r]of Object.entries(t))void 0!==r&&(n.has(e)||(o[e]=r));return o}(U,T.flags,W):U}catch(t){let e=H(V(t),{diagnosticId:X().diagnosticId,logPath:Y({force:!0})??void 0});C.flags.json?eX({success:!1,error:e}):eY(e,{showDetails:C.flags.verbose}),process.exit(1);return}let Z=null;try{let e;if("react-devtools"===z){let e=await nS(J,{flags:j,stateDir:P.baseDir,session:j.session??L,cwd:process.cwd(),env:process.env});process.exit(e);return}eu({command:z,currentVersion:s,stateDir:P.baseDir,flags:j});let t=T?.runtime,a=(e,t)=>({session:e.session??L,requestId:r,stateDir:e.stateDir,daemonBaseUrl:e.daemonBaseUrl,daemonAuthToken:e.daemonAuthToken,daemonTransport:e.daemonTransport,daemonServerMode:e.daemonServerMode,tenant:e.tenant,sessionIsolation:e.sessionIsolation,runId:e.runId,leaseId:e.leaseId,leaseBackend:e.leaseBackend,runtime:t,lockPolicy:R.lockPolicy,lockPlatform:R.defaultPlatform,cwd:process.cwd(),debug:!!e.verbose});if("batch"===z){if(J.length>0)throw new K("INVALID_ARGS","batch does not accept positional arguments.");e=function(e){let t,n,r,s="";if(e.steps)s=e.steps;else if(e.stepsFile)try{s=o.readFileSync(e.stepsFile,"utf8")}catch(n){let t=n instanceof Error?n.message:String(n);throw new K("INVALID_ARGS",`Failed to read --steps-file ${e.stepsFile}: ${t}`)}var a,i=s;try{r=JSON.parse(i)}catch{throw new K("INVALID_ARGS","Batch steps must be valid JSON.")}if(!Array.isArray(r)||0===r.length)throw new K("INVALID_ARGS","Batch steps must be a non-empty JSON array.");return a=r,t=!1,n=a.map((e,n)=>{var o,r,s,a,i,l;let c;if(o=e,null!==o&&"object"==typeof o&&!Array.isArray(o)&&"input"in o&&!("positionals"in o)&&!("flags"in o))return e;let d=function(e,t){if(!e||"object"!=typeof e||Array.isArray(e))throw new K("INVALID_ARGS",`Invalid batch step ${t}.`);var n=e,o=t;let r=Object.keys(n).filter(e=>!["command","positionals","flags","runtime"].includes(e));if(r.length>0)throw new K("INVALID_ARGS",`Batch step ${o} has unknown legacy field(s): ${r.join(", ")}.`);let s=function(e,t){let n="string"==typeof e?e.trim().toLowerCase():"";if(!n)throw new K("INVALID_ARGS",`Batch step ${t} requires command.`);if(er(n))return n;throw new K("INVALID_ARGS",`Batch step ${t} command is not available through command batch: ${String(e)}`)}(e.command,t),a=function(e,t){if(void 0!==e){if(!Array.isArray(e)||e.some(e=>"string"!=typeof e))throw new K("INVALID_ARGS",`Batch step ${t} positionals must contain only strings.`);return e}}(e.positionals,t),i=function(e,t){if(void 0!==e){if(!e||"object"!=typeof e||Array.isArray(e))throw new K("INVALID_ARGS",`Batch step ${t} flags must be an object.`);return e}}(e.flags,t);return{command:s,...void 0===a?{}:{positionals:a},...void 0===i?{}:{flags:i},...void 0===e.runtime?{}:{runtime:e.runtime}}}(e,n+1);return t=!0,c=(s=(r=d).command,a=r.positionals??[],l=r.flags,i={json:!1,help:!1,version:!1,...l},nl[s](a,i)),{command:r.command,input:c,...void 0===r.runtime?{}:{runtime:r.runtime}}}),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.\n'),n}(U)}if(x=z,"auth"!==x&&"connection"!==x&&(j=(await tD({command:z,flags:j,stateDir:P.baseDir,env:process.env})).flags),j.remoteConfig&&(A=z,!nU.has(A))){let o=ec(a(j,t),{transport:n.sendToDaemon}),r=await tw({command:z,flags:j,client:o,runtime:t,batchSteps:e,forceRuntimePrepare:function(e){for(let t of nR)if(e.has(t))return!0;return!1}(W)});j=r.flags,t=r.runtime}$={command:z,flags:j,runtime:t,explicitFlagKeys:W,hadConnectionDefaults:!!T},!("open"===$.command&&!$.runtime&&!$.flags.bundleUrl&&!$.flags.metroHost&&!$.flags.metroPort&&!$.flags.remoteConfig&&!$.hadConnectionDefaults&&((k=$.explicitFlagKeys).has("daemonBaseUrl")||k.has("daemonTransport")||k.has("tenant")||k.has("sessionIsolation")||k.has("runId")||k.has("leaseId")||k.has("leaseBackend")))||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.\n");let i=j.daemonBaseUrl;Z=!j.verbose||j.json||i?null:function(e){try{let t=o.existsSync(e)?o.statSync(e).size:0,n=!1,r=setInterval(()=>{if(!n&&o.existsSync(e))try{let n=o.statSync(e);if(n.size<t&&(t=0),n.size<=t)return;let r=o.openSync(e,"r");try{let e=Buffer.alloc(n.size-t);o.readSync(r,e,0,e.length,t),t=n.size,e.length>0&&process.stdout.write(e.toString("utf8"))}finally{o.closeSync(r)}}catch{}},200);return()=>{n=!0,clearInterval(r)}}catch{return null}}(P.logPath);let l=ec(a(j,t),{transport:function(e){let{command:t,flags:n,transport:o}=e;return"test"!==t||n.json?o:async e=>await o({...e,meta:{...e.meta,requestProgress:"replay-test"}})}({command:z,flags:j,transport:n.sendToDaemon})});if("batch"===z){if(!e)throw new K("INVALID_ARGS","batch requires --steps or --steps-file.");let t=e.map((e,t)=>({...e,input:R.lockPolicy&&void 0===U.platform?{...e.input}:nC(e.input,{policyOverrides:j,configuredPlatform:j.platform,configuredSession:j.session,inheritedPlatform:j.platform})}));if(await nA({command:z,positionals:J,flags:{...j,batchSteps:t},client:l}))return}else if("runtime"===z)throw new K("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 nA({command:z,positionals:J,flags:j,client:l}))return;throw new K("INVALID_ARGS",`Unknown command: ${z}`)}catch(n){let e=V(n),t=H(e,{diagnosticId:X().diagnosticId,logPath:Y({force:!0})??void 0});if("close"===z&&"COMMAND_FAILED"===(S=e).code&&(S.details?.kind==="daemon_startup_failed"||S.message.toLowerCase().includes("failed to start daemon")&&("string"==typeof S.details?.infoPath||"string"==typeof S.details?.lockPath))){j.json&&eX({success:!0,data:{closed:"session",source:"no-daemon"}});return}if(j.json)eX({success:!1,error:t});else if(eY(t,{showDetails:j.verbose}),j.verbose)try{let e=P.logPath;if(o.existsSync(e)){let t=o.readFileSync(e,"utf8").split("\n"),n=t.slice(Math.max(0,t.length-200)).join("\n");n.trim().length>0&&process.stderr.write(`
|
|
25
|
+
`}let C=t.raw||"macos-helper"===v?null:i(w.nodes),S=!!e.truncated,x=C?.nodes??g,b=t.raw||"macos-helper"===v?null:function(e,t,n,r,o=0){let i,a=(i=e.visibility)&&"object"==typeof i&&"boolean"==typeof i.partial&&"number"==typeof i.visibleNodeCount&&"number"==typeof i.totalNodeCount&&Array.isArray(i.reasons)?{partial:i.partial,visibleNodeCount:i.visibleNodeCount,totalNodeCount:i.totalNodeCount,reasons:i.reasons.filter(e=>"string"==typeof e)}:null;if(0===o&&a)return a;let s=(t?.hiddenCount??0)+o,l=!!t&&t.nodes.some(e=>e.hiddenContentAbove||e.hiddenContentBelow);return s>0?{partial:!0,visibleNodeCount:n,totalNodeCount:Math.max(r,a?.totalNodeCount??r),reasons:[...new Set([...a?.reasons??[],"offscreen-nodes"])]}:a||(l?{partial:!0,visibleNodeCount:n,totalNodeCount:n,reasons:[]}:null)}(e,C,x.length,g.length,w.filteredCount),_=(o=g.length,l=b,m=S?" (truncated)":"",l?.partial?l.totalNodeCount>l.visibleNodeCount?`Snapshot: ${l.visibleNodeCount} visible nodes (${l.totalNodeCount} total)${m}`:`Snapshot: ${l.visibleNodeCount} visible nodes${m}`:`Snapshot: ${o} nodes${m}`);return 0===g.length?`${y}${_}
|
|
26
|
+
${$}`:t.raw?`${y}${_}
|
|
27
|
+
${$}${g.map(e=>JSON.stringify(e)).join("\n")}
|
|
28
|
+
`:t.flatten?`${y}${_}
|
|
29
|
+
${$}${s(x,{summarizeTextSurfaces:!0}).flatMap(e=>[d(e.node,0,!1,e.type,{summarizeTextSurfaces:!0}),...eO({...e,depth:0})]).join("\n")}${eN(C)}
|
|
30
|
+
`:`${y}${_}
|
|
31
|
+
${$}${(function(e){let t=[],n=[],r=new Map(e.map(e=>[e.node.index,e.node])),o=e=>{for(var o,i,a;n.length>0&&(!e||(o=e,i=n[n.length-1],a=r,!function(e,t,n){let r=e;for(;"number"==typeof r.parentIndex;){if(r.parentIndex===t.index)return!0;let e=n.get(r.parentIndex);if(!e)break;r=e}return!1}(o.node,i.node,a)&&o.depth<=i.depth));)t.push(...eO(n.pop(),"below"))};for(let r of e)o(r),t.push(r.text),t.push(...eO(r,"above")),r.node.hiddenContentBelow&&n.push(r);return o(),t})(s(x,{summarizeTextSurfaces:!0})).join("\n")}${eN(C)}
|
|
32
|
+
`}(r,{raw:n.raw,flatten:n.interactiveOnly,scoped:"string"==typeof n.scope&&n.scope.trim().length>0,depthLimited:"number"==typeof n.depth})}},wait:tz,alert:tz,appstate:tJ(function(e){return{data:e,text:function(e){if("ios"===e.platform){let t=[`Foreground app: ${e.appName??e.appBundleId??"unknown"}`];return e.appBundleId&&t.push(`Bundle: ${e.appBundleId}`),e.source&&t.push(`Source: ${e.source}`),t.join("\n")}if("android"===e.platform){let t=[`Foreground app: ${e.package??"unknown"}`];return e.activity&&t.push(`Activity: ${e.activity}`),t.join("\n")}return null}(e)}}),back:tz,home:tz,rotate:tz,"app-switcher":tz,keyboard:tJ(function(e){if("android"===e.platform&&"status"===e.action){var t;let n=[`Keyboard visible: ${!0===e.visible?"yes":"no"}`,`Input type: ${e.type??e.inputType??"unknown"}`,`Input owner: ${e.inputOwner??"unknown"}`];return e.inputMethodPackage&&n.push(`Input method: ${e.inputMethodPackage}`),e.focusedPackage&&n.push(`Focused package: ${e.focusedPackage}`),e.focusedResourceId&&n.push(`Focused resource: ${e.focusedResourceId}`),n.push(`Next action: ${(t=e.visible,"ime"===e.inputOwner?"Focused input appears to be owned by the keyboard/IME; dismiss or change the IME before retrying text entry.":!0===t?"Keyboard is visible and focused input appears app-owned; fill/type can proceed.":"Keyboard is hidden; focus an app field before type, or use fill with a concrete target.")}`),{data:e,text:n.join("\n")}}return tB(e)}),clipboard:tJ(function(e){return"read"===e.action?{data:e,text:e.text}:tB(e)}),get:({input:e,result:t})=>{var n;let r;return r=(n={result:t,format:e.format}).result,"text"===n.format?{data:r,text:"string"==typeof r.text?r.text:""}:"attrs"===n.format?{data:r,text:JSON.stringify(r.node??{},null,2)}:tB(r)},is:tJ(function(e){return{data:e,text:`Passed: is ${e.predicate??"assertion"}`}}),find:tJ(function(e){return"string"==typeof e.text?{data:e,text:e.text}:"boolean"==typeof e.found?{data:e,text:`Found: ${e.found}`}:e.node?{data:e,text:JSON.stringify(e.node,null,2)}:tB(e)}),perf:tJ(function(e){return{data:e,text:function(e){var t,n,r;let o,i=tF(e.metrics),a=tF(i?.fps),s=(t=i,(o=[function(e){if(e?.available!==!0)return;let t=tG(e.usagePercent);return void 0!==t?`CPU ${tV(t)}`:void 0}(tF(t?.cpu)),function(e){let t;if(e?.available!==!0)return;let n=tG(e.residentMemoryKb)??tG(e.totalPssKb)??tG(e.totalRssKb);return void 0!==n?`memory ${t=n/1024,`${t>=10?Math.round(t):t.toFixed(1)}MB`}`:void 0}(tF(t?.memory))].filter(e=>!!e)).length>0?o.join(", "):void 0);if(!a)return tH(s,"missing frame metric");if(!1===a.available){return tH(s,"string"==typeof(n=a).reason&&n.reason.length>0?n.reason:"not available")}let l=function(e){let t=tG(e.droppedFramePercent),n=tG(e.droppedFrameCount);if(void 0!==t&&void 0!==n){var r,o,i;return[`dropped ${tV(t)}`,(r=n,void 0!==(o=tG(e.totalFrameCount))?`(${Math.round(r)}/${Math.round(o)} frames)`:`(${Math.round(r)} dropped frames)`),void 0!==(i=tG(e.sampleWindowMs))?`window ${tq(i)}`:""].filter(Boolean).join(" ")}}(a);if(!l)return tH(s,"missing dropped-frame summary");let c=[`Frame health: ${l}`],d=(Array.isArray(r=a.worstWindows)?r.filter(e=>!!e&&"object"==typeof e&&!Array.isArray(e)):[]).flatMap(e=>{let t=function(e){let t=tG(e.startOffsetMs),n=tG(e.endOffsetMs),r=tG(e.missedDeadlineFrameCount);if(void 0===t||void 0===n||void 0===r)return;let o=tG(e.worstFrameMs),i=void 0===o?"":`, worst ${tq(o)}`;return`- +${tq(t)}-+${tq(n)}: ${Math.round(r)} missed-deadline frames${i}`}(e);return t?[t]:[]});return d.length>0&&c.push("Worst windows:",...d),c.join("\n")}(e)}}),logs:tJ(function(e){var t;return{data:e,text:"string"==typeof e.path?e.path:"",stderr:tK([tO(e,["active","state","backend","sizeBytes"]),(t=e,["started","stopped","marked","cleared","restarted","removedRotatedFiles"].map(e=>{var n,r;return n=e,!0===(r=t[e])?`${n}=true`:"number"==typeof r?`${n}=${r}`:""}).filter(Boolean).join(" ")||void 0),"string"==typeof e.hint?e.hint:void 0,tj(e.notes)])}}),network:tJ(function(e){let t=[],n="string"==typeof e.path?e.path:"";n&&t.push(n);let r=Array.isArray(e.entries)?e.entries:[];if(0===r.length)t.push("No recent HTTP(s) entries found.");else for(let e of r)t.push(...function(e){let t=tF(e)??{},n="string"==typeof t.method?t.method:"HTTP",r="string"==typeof t.url?t.url:"<unknown-url>",o="number"==typeof t.status?` status=${t.status}`:"",i="string"==typeof t.timestamp?`${t.timestamp} `:"",a="number"==typeof t.durationMs?` durationMs=${t.durationMs}`:"",s=[`${i}${n} ${r}${o}${a}`];return tL(s,"headers",t.headers),tL(s,"request",t.requestBody),tL(s,"response",t.responseBody),s}(e));return{data:e,text:t.join("\n"),stderr:tK([tO(e,["active","state","backend","include","scannedLines","matchedLines"]),tj(e.notes)])}}),record:tJ(function(e){let t,n="string"==typeof e.outPath?e.outPath:"",r=Array.isArray(t=e.chunks)?t.flatMap(e=>e&&"object"==typeof e?"number"!=typeof e.index||"string"!=typeof e.path?[]:[{index:e.index,path:e.path}]:[]):[];if(r.length<=1){var o,i;let t;return{data:e,text:(o=e,t=[],(i=n)&&t.push(i),"string"==typeof o.warning&&t.push(`Warning: ${o.warning}`),"string"==typeof o.overlayWarning&&t.push(`Overlay warning: ${o.overlayWarning}`),t.join("\n"))}}let a=["Recording chunks:"];for(let e of r)a.push(` ${e.index}: ${e.path}`);return"string"==typeof e.telemetryPath&&a.push(`Telemetry: ${e.telemetryPath}`),"string"==typeof e.warning&&a.push(`Warning: ${e.warning}`),"string"==typeof e.overlayWarning&&a.push(`Overlay warning: ${e.overlayWarning}`),{data:e,text:a.join("\n")}}),metro:({input:e,result:t})=>{var n;return{data:(n={result:t,action:e.action}).result,text:"reload"===n.action?`Reloaded React Native apps via ${n.result.reloadUrl}`:JSON.stringify(n.result,null,2)}}},tZ={...F,...G,...q,...M,...L,...H,...j,...K,metro:function(e,t){let n=(e[0]??"").toLowerCase();if("prepare"!==n&&"reload"!==n)throw new p("INVALID_ARGS","metro requires a subcommand: prepare or reload");if("reload"===n)return{action:n,metroHost:t.metroHost,metroPort:t.metroPort,bundleUrl:t.bundleUrl,timeoutMs:t.metroProbeTimeoutMs};if(!t.metroPublicBaseUrl&&!t.metroProxyBaseUrl)throw new p("INVALID_ARGS","metro prepare requires --public-base-url <url> or --proxy-base-url <url>.");return{action:n,projectRoot:t.metroProjectRoot,kind:t.metroKind,port:t.metroPreparePort,listenHost:t.metroListenHost,statusHost:t.metroStatusHost,publicBaseUrl:t.metroPublicBaseUrl,proxyBaseUrl:t.metroProxyBaseUrl,bearerToken:t.metroBearerToken,bridgeScope:t.tenant&&t.runId&&t.leaseId?{tenantId:t.tenant,runId:t.runId,leaseId:t.leaseId}:void 0,startupTimeoutMs:t.metroStartupTimeoutMs,probeTimeoutMs:t.metroProbeTimeoutMs,reuseExisting:!t.metroNoReuseExisting&&void 0,installDependenciesIfNeeded:!t.metroNoInstallDeps&&void 0,runtimeFilePath:t.metroRuntimeFile}},batch:(e,t)=>({...V(t),steps:t.batchSteps??[],onError:t.batchOnError,maxSteps:t.batchMaxSteps,out:t.out})};async function tY(e){return(await tX(e)).result}async function tX(e){var t,n,r,o;let i=(t=e.command,n=e.positionals,r=e.flags,tZ[t](n,r)),a=await J(e.client,e.command,i);return{result:a,cliOutput:(o={name:e.command,input:i,result:a},tW[o.name]?.({input:o.input??{},result:o.result}))}}let tQ={connect:tS,disconnect:tx,connection:tb,auth:tU,screenshot:async({positionals:e,flags:t,client:n})=>{let r=await tY({client:n,command:"screenshot",positionals:e,flags:t});return tC(t,{path:r.path,...r.overlayRefs?{overlayRefs:r.overlayRefs}:{}},()=>r.overlayRefs?`Annotated ${r.overlayRefs.length} refs onto ${r.path}`:r.path),!0},diff:async({positionals:e,flags:n,client:r})=>{var o;if("snapshot"===e[0]){let o=await tY({client:r,command:"diff",positionals:e,flags:n});return tC(n,o,()=>(function(e){var n;let r=!0===e.baselineInitialized,o=e.summary??{},i=eT(o.additions),a=eT(o.removals),s=eT(o.unchanged),l=eB(),c=eL(e),d=c.length>0?`${c.join("\n")}
|
|
33
|
+
`:"";if(r)return`${d}Baseline initialized (${s} lines).
|
|
34
|
+
`;let u=(function(e){if(0===e.length)return e;let t=e.map((e,t)=>({index:t,kind:e.kind})).filter(e=>"added"===e.kind||"removed"===e.kind).map(e=>e.index);if(0===t.length)return e;let n=Array(e.length).fill(!1);for(let r of t){let t=Math.max(0,r-1),o=Math.min(e.length-1,r+1);for(let e=t;e<=o;e+=1)n[e]=!0}return e.filter((e,t)=>n[t])})(Array.isArray(e.lines)?e.lines:[]).map(e=>{let n="string"==typeof e.text?e.text:"";if("added"===e.kind){let e=n.startsWith(" ")?`+${n}`:`+ ${n}`;return l?t("green",e):e}if("removed"===e.kind){let e=n.startsWith(" ")?`-${n}`:`- ${n}`;return l?t("red",e):e}return l?t("dim",n):n}),f=u.length>0?`${u.join("\n")}
|
|
35
|
+
`:"";if(!l)return`${d}${f}${i} additions, ${a} removals, ${s} unchanged
|
|
36
|
+
`;let p=`${(n=String(i),t("green",n))} additions, ${t("red",String(a))} removals, ${t("dim",String(s))} unchanged`;return`${d}${f}${p}
|
|
37
|
+
`})(o)),!0}if("screenshot"!==e[0])return!1;let i=n.baseline;if(!i||"string"!=typeof i)throw new p("INVALID_ARGS","diff screenshot requires --baseline <path>");let a=z(i),s="string"==typeof n.out?z(n.out):void 0,d=e[1];if(e.length>2)throw new p("INVALID_ARGS","diff screenshot accepts at most one current screenshot path");let u=c({backend:(o=r,{platform:function(e){switch(e.platform){case"android":case"linux":case"macos":return e.platform;default:return"ios"}}(n),captureScreenshot:async(e,t,n)=>{let r=await o.capture.screenshot({path:t,session:e.session,overlayRefs:n?.overlayRefs,fullscreen:n?.fullscreen,stabilize:n?.stabilize,surface:n?.surface});return{path:r.path,...r.overlayRefs?{overlayRefs:r.overlayRefs}:{}}}}),artifacts:Z(),sessions:{get:e=>({name:e}),set:()=>{}},policy:l()}),f=await u.capture.diffScreenshot({session:n.session,baseline:{kind:"path",path:a},current:d?{kind:"path",path:z(d)}:{kind:"live"},...s?{out:{kind:"path",path:s}}:{},threshold:function(e){if(null!=e&&""!==e)return Number(e)}(n.threshold),overlayRefs:n.overlayRefs,surface:n.surface});return tC(n,f,()=>{var e,n,r,o;let i,a,s,l,c,d,u,p;return i=eB(),a=!0===f.match,s=f.dimensionMismatch,(l=[]).push(...function(e,n){if(!0===e.match){let e=n?t("green","✓"):"✓";return[`${e} Screenshots match.`]}let r=e.dimensionMismatch,o=n?t("red","✗"):"✗";if(r){let e=r.expected,t=r.actual;return[`${o} Screenshots have different dimensions: expected ${e?.width}x${e?.height}, got ${t?.width}x${t?.height}`]}let i=eT(e.differentPixels),a=eT(e.mismatchPercentage),s=0===a&&i>0?"<0.01":String(a),l=`${s}% pixels differ`;return[`${o} ${n?t("red",l):l}`]}(f,i)),l.push(...function(e,n,r){if(n)return[];let o=[];if(e.diffPath){let n=eE(e.diffPath),i=r?t("dim","Diff image:"):"Diff image:",a=r?t("green",n):n;o.push(` ${i} ${a}`)}if(e.currentOverlayPath){let n=eE(e.currentOverlayPath),i=r?t("dim","Current overlay:"):"Current overlay:",a=r?t("green",n):n,s=eT(e.currentOverlayRefCount),l=s>0?` (${s} refs)`:"";o.push(` ${i} ${a}${l}`)}return o}(f,a,i)),a||s||(l.push(...(e=f,n=i,c=eT(e.differentPixels),d=eT(e.totalPixels),u=n?t("red",String(c)):String(c),[` ${u} different / ${d} total pixels`])),l.push(...(r=f,o=i,0===(p=function(e){let t=[];for(let n of(e.ocr?.movementClusters??[]).slice(0,2))t.push(`text movement cluster: ${function(e){let t=e.slice(0,4).map(e=>JSON.stringify(e)),n=e.length>t.length?` +${e.length-t.length} more`:"";return`${t.join(", ")}${n}`}(n.texts)} dx=${eU(n.xRange)}px dy=${eU(n.yRange)}px`);let n=(e.nonTextDeltas??[]).filter(e=>["icon","toggle","chevron"].includes(e.likelyKind)).slice(0,3);n.length>0&&t.push(`non-text controls: ${n.map(eR).join("; ")}`);let r=(e.nonTextDeltas??[]).filter(e=>"separator"===e.likelyKind).slice(0,2);return r.length>0&&t.push(`non-text boundaries: ${r.map(eR).join("; ")}`),t.slice(0,6)}(r)).length?[]:[` ${eM("Hints:",o)}`,...p.map(e=>` - ${e}`)])),l.push(...function(e,t){let n=Array.isArray(e.regions)?e.regions:[];if(0===n.length)return[];let r=[` ${eM("Changed regions:",t)}`];for(let e of n.slice(0,5))r.push(...function(e){let t=0===e.shareOfDiffPercentage&&e.differentPixels>0?"<0.01":String(e.shareOfDiffPercentage),n=e.rect,r=[` ${e.index}. ${e.location} x=${n.x} y=${n.y} ${n.width}x${n.height}, ${t}% of diff, change=${e.dominantChange}`],o=function(e){let t=[e.size?`size=${e.size}`:null,e.shape?`shape=${e.shape}`:null,"number"==typeof e.densityPercentage?`density=${e.densityPercentage}%`:null,e.averageBaselineColorHex&&e.averageCurrentColorHex?`avgColor=${e.averageBaselineColorHex}->${e.averageCurrentColorHex}`:null,"number"==typeof e.baselineLuminance&&"number"==typeof e.currentLuminance?`luminance=${e.baselineLuminance}->${e.currentLuminance}`:null].filter(e=>null!==e);return t.length>0?t.join(" "):null}(e);o&&r.push(` ${o}`);let i=e.currentOverlayMatches?.[0];if(i){let e=i.label?` "${i.label}"`:"";r.push(` overlaps @${i.ref}${e}, ${i.regionCoveragePercentage}% of region`)}return r}(e));return r}(f,i)),l.push(...function(e,t){let n=e.ocr?.matches??[];if(0===n.length)return[];let r=n.slice(0,8),o=[` ${eM(`OCR text deltas (${e.ocr?.provider}; baselineBlocks=${e.ocr?.baselineBlocks} currentBlocks=${e.ocr?.currentBlocks}; showing ${r.length}/${n.length}; px):`,t)}`,` ${eM("item | text | movePx | sizeDeltaPx | bboxBaseline | bboxCurrent | confidence | issueHint",t)}`];for(let[e,t]of r.entries()){let n=t.delta;o.push(` ${e+1} | ${JSON.stringify(t.text)} | ${ek(n.x)},${ek(n.y)} | ${ek(n.width)},${ek(n.height)} | ${eP(t.baselineRect)} | ${eP(t.currentRect)} | ${t.confidence} | ${t.possibleTextMetricMismatch?"ocr-bbox-size-change":"-"}`)}return o}(f,i)),l.push(...function(e,t){let n=e.nonTextDeltas??[];if(0===n.length)return[];let r=n.slice(0,8),o=[` ${eM(`Non-text visual deltas (showing ${r.length}/${n.length}; px):`,t)}`,` ${eM("item | region | slot | kind | bboxCurrent | nearestText",t)}`];for(let e of r)o.push(` ${e.index} | ${e.regionIndex?`r${e.regionIndex}`:"-"} | ${e.slot} | ${e.likelyKind} | ${eP(e.rect)} | ${e.nearestText?JSON.stringify(e.nearestText):"-"}`);return o}(f,i))),`${l.join("\n")}
|
|
38
|
+
`}),!0}};async function t0(e){let t={...e.flags},n=tQ[e.command];return n?await n({...e,flags:t}):!!Y(e.command)&&await t1({...e,command:e.command,flags:t})}async function t1(e){let{runGenericClientBackedCommand:t}=await import("./generic.js");return await t(e)}function t2(e,t,n){return n||(e.push(t),"")}async function t3(e,t,n){let{flags:r}=t,o=function(e){var t;if(!e?.metroProxyBaseUrl||"android-instance"!==(t=e.leaseBackend)&&"ios-instance"!==t)return null;let n=[],r={serverBaseUrl:t2(n,"metroProxyBaseUrl",e.metroProxyBaseUrl),bearerToken:t2(n,"metroBearerToken",e.metroBearerToken),tenantId:t2(n,"tenant",e.tenant),runId:t2(n,"runId",e.runId),leaseId:t2(n,"leaseId",e.leaseId)};if(n.length>0)throw new p("INVALID_ARGS",`react-devtools remote bridge requires ${n.join(", ")}.`,{missing:n});return r}(r);if(!o)return n();let i=t.stateDir??process.cwd(),a=t.session??r?.session??"default",s=r?.remoteConfig??`${o.tenantId}:${o.runId}:${o.leaseId}`;if("stop"===e[0])try{return await n()}finally{await eH({projectRoot:t.cwd??process.cwd(),stateDir:i,profileKey:s,consumerKey:a})}return await eK({projectRoot:t.cwd??process.cwd(),stateDir:i,serverBaseUrl:o.serverBaseUrl,bearerToken:o.bearerToken,bridgeScope:{tenantId:o.tenantId,runId:o.runId,leaseId:o.leaseId},session:a,profileKey:s,consumerKey:a,env:t.env??process.env}),await n()}async function t8(e,t={}){let n=t.cwd??process.cwd(),r=t.env??process.env,o=await t3(e,t,async()=>(await _("npm",["exec","--yes","--package","agent-react-devtools@0.4.0","--","agent-react-devtools",...e],{cwd:n,env:r,allowFailure:!0,onStdoutChunk:e=>{process.stdout.write(e)},onStderrChunk:e=>{process.stderr.write(e)}})).exitCode);var i,a=t.flags;return 0!==o&&"wait"===(i=e)[0]&&i.includes("--connected")&&"ios-instance"===a?.leaseBackend&&process.stderr.write("Hint: Remote iOS React DevTools connects during JavaScript startup.\nIf 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`.\n"),o}function t5(e,t={}){let n=t9(t),r={...e};return n.defaultPlatform&&void 0===r.platform&&(r.platform=n.defaultPlatform),r}function t9(e){var t,n,r,o;let i,a=e.env??process.env,s=e.inheritedPlatform??e.configuredPlatform??function(e){if(void 0===e)return;let t=e.trim().toLowerCase();if(t){if("ios"===t||"android"===t||"apple"===t)return t;throw new p("INVALID_ARGS",`Invalid AGENT_DEVICE_PLATFORM: ${e}. Use ios, android, or apple.`)}}(a.AGENT_DEVICE_PLATFORM),l="string"==typeof(t=e.configuredSession??a.AGENT_DEVICE_SESSION)&&t.trim().length>0;return{defaultPlatform:s,lockPolicy:(n=e.policyOverrides,r=a,o=l,(i=n?.sessionLock??n?.sessionLockConflicts??function(e){if(void 0===e)return;let t=e.trim().toLowerCase();if(t){if("reject"===t||"strip"===t)return t;throw new p("INVALID_ARGS",`Invalid session lock mode: ${e}. Use reject or strip.`)}}(r.AGENT_DEVICE_SESSION_LOCK))||(n?.sessionLocked===!0||o?"reject":void 0))}}let t6={sendToDaemon:ec},t4=new Set(["launchUrl","metroBearerToken","metroKind","metroListenHost","metroNoInstallDeps","metroNoReuseExisting","metroPreparePort","metroProbeTimeoutMs","metroProjectRoot","metroProxyBaseUrl","metroPublicBaseUrl","metroRuntimeFile","metroStartupTimeoutMs","metroStatusHost"]),t7=new Set(["connect","connection","close","disconnect","metro","session"]);async function ne(t,n=t6){let o=I(),i=es(),a=t.includes("--debug")||t.includes("--verbose")||t.includes("-v"),s=t.includes("--json"),l=function(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("--session"===n){let n=e[t+1]?.trim();if(n&&!n.startsWith("-"))return n;break}}return null}(t)??process.env.AGENT_DEVICE_SESSION??"default";await $({session:l,requestId:o,command:t[0],debug:a},async()=>{var l,c,d,m,h,g,v,I,$,b,_,D,N,P,k,R,U;let E,T,B,M,L,j,K;try{let n,o,i,a,s,u,f,y;l={cwd:process.cwd(),env:process.env},n=eo(t),o=l?.env??process.env,i=l?.cwd??process.cwd(),m=n.command,a=null!==m&&(c={remoteConfig:n.flags.remoteConfig,cwd:i,env:o}).remoteConfig?{...e1(w({configPath:c.remoteConfig,cwd:c.cwd,env:c.env}).profile),remoteConfig:c.remoteConfig}:{},f=Q((s=(d={command:n.command,cwd:i,cliFlags:n.flags,env:o}).env??process.env,u=Q({},function(e){let t={};for(let n of e)Q(t,function(e,t){let n,o;if(!r.existsSync(e)){if(t)throw new p("INVALID_ARGS",`Config file not found: ${e}`);return{}}try{n=r.readFileSync(e,"utf8")}catch(t){throw new p("INVALID_ARGS",`Failed to read config file: ${e}`,{cause:t instanceof Error?t.message:String(t)})}try{o=JSON.parse(n)}catch(t){throw new p("INVALID_ARGS",`Invalid JSON in config file: ${e}`,{cause:t instanceof Error?t.message:String(t)})}if(!o||"object"!=typeof o||Array.isArray(o))throw new p("INVALID_ARGS",`Config file must contain a JSON object: ${e}`);return function(e,t){let n={};for(let[r,o]of Object.entries(e)){if("installSource"===r){n.installSource=O(o,t);continue}let e=en(r);if(!e)throw new p("INVALID_ARGS",`Unknown config key "${r}" in ${t}.`);if(!e.config.enabled)throw new p("INVALID_ARGS",`Unsupported config key "${r}" in ${t}.`);n[r]=ee(e,o,t,r)}return n}(o,`config file ${e}`)}(n.path,n.required));return t}((h=d.cwd,g=d.cliFlags.config,v=s,(y=g??v.AGENT_DEVICE_CONFIG)?[{path:(I=y,$=h,b=v,z(I,{cwd:$,env:b})),required:!0}]:[{path:(_=v,e.join(W("~",{env:_}),".agent-device","config.json")),required:!1},{path:e.resolve(h,"agent-device.json"),required:!1}]))),Q(u,function(e,t){let n={};for(let r of et(t)){if("installSource"===r.key)continue;let t=r.env.names.map(t=>({name:t,value:e[t]})).find(e=>"string"==typeof e.value&&e.value.trim().length>0);t&&(n[r.key]=ee(r,t.value,`environment variable ${t.name}`,t.name))}return n}(s,d.command))),a),E={...ea(n,{strictFlags:l?.strictFlags,defaultFlags:f}),providedFlags:n.providedFlags}}catch(t){S({level:"error",phase:"cli_parse_failed",data:{error:t instanceof Error?t.message:String(t)}});let e=f(t,{diagnosticId:A().diagnosticId,logPath:C({force:!0})??void 0});s?e_({success:!1,error:e}):eD(e,{showDetails:a}),process.exit(1);return}for(let e of E.warnings)process.stderr.write(`Warning: ${e}
|
|
39
|
+
`);E.flags.version&&(process.stdout.write(`${i}
|
|
40
|
+
`),process.exit(0));let H="help"===E.command,F=E.flags.help;if(H||F){H&&E.positionals.length>1&&(eD(new p("INVALID_ARGS","help accepts at most one command.")),process.exit(1));let e=H?E.positionals[0]:E.command;e||(process.stdout.write(`${er()}
|
|
41
|
+
`),process.exit(0));let t=ei(e);t&&(process.stdout.write(t),process.exit(0)),eD(new p("INVALID_ARGS",`Unknown command: ${e}`)),process.stdout.write(`${er()}
|
|
42
|
+
`),process.exit(1)}E.command||(process.stdout.write(`${er()}
|
|
43
|
+
`),process.exit(1));let{command:G,positionals:V}=E,q=new Set(E.providedFlags.map(e=>e.key));try{B=(T=t9({policyOverrides:E.flags,configuredPlatform:E.flags.platform,configuredSession:E.flags.session})).lockPolicy?{...E.flags}:t5(E.flags,{policyOverrides:E.flags,configuredPlatform:E.flags.platform,configuredSession:E.flags.session}),M=x(B.stateDir),L=B.session??"default",D={command:G,explicitFlagKeys:q,stateDir:M.baseDir,session:L,remoteConfig:B.remoteConfig,hasResolvedSession:void 0!==B.session},K=(j="connect"===D.command||"connection"===D.command?null:function(e){let t=e.validateRemoteConfigHash??!0,n=e.remoteConfig?y({configPath:e.remoteConfig,cwd:e.cwd,env:e.env}):void 0,r=eF(e)??(e.allowActiveFallback?eZ({stateDir:e.stateDir}):null);if(!r||n&&r.remoteConfigPath!==n)return null;if(t&&eJ(r.remoteConfigPath)!==r.remoteConfigHash)throw new p("INVALID_ARGS","Active remote connection config changed. Run agent-device connect --force to refresh it.",{remoteConfig:r.remoteConfigPath});let o=function(e,t){try{return w({configPath:e.remoteConfigPath,cwd:t.cwd,env:t.env}).profile}catch(e){if(!1===t.validateRemoteConfigHash)return{};throw e}}(r,e);return{runtime:r.runtime,flags:{...o,remoteConfig:r.remoteConfigPath,daemonBaseUrl:r.daemon?.baseUrl??o.daemonBaseUrl,daemonTransport:r.daemon?.transport??o.daemonTransport,daemonServerMode:r.daemon?.serverMode??o.daemonServerMode,tenant:r.tenant,sessionIsolation:"tenant",runId:r.runId,leaseId:r.leaseId,leaseBackend:r.leaseBackend,session:r.session,platform:r.platform??o.platform,target:r.target??o.target}}}({stateDir:D.stateDir,session:D.session,remoteConfig:D.remoteConfig,cwd:process.cwd(),env:process.env,allowActiveFallback:!D.explicitFlagKeys.has("session")&&(!D.remoteConfig||"disconnect"===D.command||!D.hasResolvedSession),validateRemoteConfigHash:"disconnect"!==D.command}))?function(e,t,n){let r={...e};for(let[e,o]of Object.entries(t))void 0!==o&&(n.has(e)||(r[e]=o));return r}(B,j.flags,q):B}catch(t){let e=f(u(t),{diagnosticId:A().diagnosticId,logPath:C({force:!0})??void 0});E.flags.json?e_({success:!1,error:e}):eD(e,{showDetails:E.flags.verbose}),process.exit(1);return}let J=null;try{let e;if("react-devtools"===G){let e=await t8(V,{flags:K,stateDir:M.baseDir,session:K.session??L,cwd:process.cwd(),env:process.env});process.exit(e);return}ed({command:G,currentVersion:i,stateDir:M.baseDir,flags:K});let t=j?.runtime,a=(e,t)=>({session:e.session??L,requestId:o,stateDir:e.stateDir,daemonBaseUrl:e.daemonBaseUrl,daemonAuthToken:e.daemonAuthToken,daemonTransport:e.daemonTransport,daemonServerMode:e.daemonServerMode,tenant:e.tenant,sessionIsolation:e.sessionIsolation,runId:e.runId,leaseId:e.leaseId,leaseBackend:e.leaseBackend,runtime:t,lockPolicy:T.lockPolicy,lockPlatform:T.defaultPlatform,cwd:process.cwd(),debug:!!e.verbose});if("batch"===G){if(V.length>0)throw new p("INVALID_ARGS","batch does not accept positional arguments.");e=function(e){let t,n,o,i="";if(e.steps)i=e.steps;else if(e.stepsFile)try{i=r.readFileSync(e.stepsFile,"utf8")}catch(n){let t=n instanceof Error?n.message:String(n);throw new p("INVALID_ARGS",`Failed to read --steps-file ${e.stepsFile}: ${t}`)}var a,s=i;try{o=JSON.parse(s)}catch{throw new p("INVALID_ARGS","Batch steps must be valid JSON.")}if(!Array.isArray(o)||0===o.length)throw new p("INVALID_ARGS","Batch steps must be a non-empty JSON array.");return a=o,t=!1,n=a.map((e,n)=>{var r,o,i,a,s,l;let c;if(r=e,null!==r&&"object"==typeof r&&!Array.isArray(r)&&"input"in r&&!("positionals"in r)&&!("flags"in r))return e;let d=function(e,t){if(!e||"object"!=typeof e||Array.isArray(e))throw new p("INVALID_ARGS",`Invalid batch step ${t}.`);var n=e,r=t;let o=Object.keys(n).filter(e=>!["command","positionals","flags","runtime"].includes(e));if(o.length>0)throw new p("INVALID_ARGS",`Batch step ${r} has unknown legacy field(s): ${o.join(", ")}.`);let i=function(e,t){let n="string"==typeof e?e.trim().toLowerCase():"";if(!n)throw new p("INVALID_ARGS",`Batch step ${t} requires command.`);if(X(n))return n;throw new p("INVALID_ARGS",`Batch step ${t} command is not available through command batch: ${String(e)}`)}(e.command,t),a=function(e,t){if(void 0!==e){if(!Array.isArray(e)||e.some(e=>"string"!=typeof e))throw new p("INVALID_ARGS",`Batch step ${t} positionals must contain only strings.`);return e}}(e.positionals,t),s=function(e,t){if(void 0!==e){if(!e||"object"!=typeof e||Array.isArray(e))throw new p("INVALID_ARGS",`Batch step ${t} flags must be an object.`);return e}}(e.flags,t);return{command:i,...void 0===a?{}:{positionals:a},...void 0===s?{}:{flags:s},...void 0===e.runtime?{}:{runtime:e.runtime}}}(e,n+1);return t=!0,c=(i=(o=d).command,a=o.positionals??[],l=o.flags,s={json:!1,help:!1,version:!1,...l},tZ[i](a,s)),{command:o.command,input:c,...void 0===o.runtime?{}:{runtime:o.runtime}}}),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.\n'),n}(B)}if(N=G,"auth"!==N&&"connection"!==N&&(K=(await to({command:G,flags:K,stateDir:M.baseDir,env:process.env})).flags),K.remoteConfig&&(P=G,!t7.has(P))){let r=el(a(K,t),{transport:n.sendToDaemon}),o=await e8({command:G,flags:K,client:r,runtime:t,batchSteps:e,forceRuntimePrepare:function(e){for(let t of t4)if(e.has(t))return!0;return!1}(q)});K=o.flags,t=o.runtime}k={command:G,flags:K,runtime:t,explicitFlagKeys:q,hadConnectionDefaults:!!j},!("open"===k.command&&!k.runtime&&!k.flags.bundleUrl&&!k.flags.metroHost&&!k.flags.metroPort&&!k.flags.remoteConfig&&!k.hadConnectionDefaults&&((R=k.explicitFlagKeys).has("daemonBaseUrl")||R.has("daemonTransport")||R.has("tenant")||R.has("sessionIsolation")||R.has("runId")||R.has("leaseId")||R.has("leaseBackend")))||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.\n");let s=K.daemonBaseUrl;J=!K.verbose||K.json||s?null:function(e){try{let t=r.existsSync(e)?r.statSync(e).size:0,n=!1,o=setInterval(()=>{if(!n&&r.existsSync(e))try{let n=r.statSync(e);if(n.size<t&&(t=0),n.size<=t)return;let o=r.openSync(e,"r");try{let e=Buffer.alloc(n.size-t);r.readSync(o,e,0,e.length,t),t=n.size,e.length>0&&process.stdout.write(e.toString("utf8"))}finally{r.closeSync(o)}}catch{}},200);return()=>{n=!0,clearInterval(o)}}catch{return null}}(M.logPath);let l=el(a(K,t),{transport:function(e){let{command:t,flags:n,transport:r}=e;return"test"!==t||n.json?r:async e=>await r({...e,meta:{...e.meta,requestProgress:"replay-test"}})}({command:G,flags:K,transport:n.sendToDaemon})});if("batch"===G){if(!e)throw new p("INVALID_ARGS","batch requires --steps or --steps-file.");let t=e.map((e,t)=>({...e,input:T.lockPolicy&&void 0===B.platform?{...e.input}:t5(e.input,{policyOverrides:K,configuredPlatform:K.platform,configuredSession:K.session,inheritedPlatform:K.platform})}));if(await t0({command:G,positionals:V,flags:{...K,batchSteps:t},client:l}))return}else if("runtime"===G)throw new p("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 t0({command:G,positionals:V,flags:K,client:l}))return;throw new p("INVALID_ARGS",`Unknown command: ${G}`)}catch(n){let e=u(n),t=f(e,{diagnosticId:A().diagnosticId,logPath:C({force:!0})??void 0});if("close"===G&&"COMMAND_FAILED"===(U=e).code&&(U.details?.kind==="daemon_startup_failed"||U.message.toLowerCase().includes("failed to start daemon")&&("string"==typeof U.details?.infoPath||"string"==typeof U.details?.lockPath))){K.json&&e_({success:!0,data:{closed:"session",source:"no-daemon"}});return}if(K.json)e_({success:!1,error:t});else if(eD(t,{showDetails:K.verbose}),K.verbose)try{let e=M.logPath;if(r.existsSync(e)){let t=r.readFileSync(e,"utf8").split("\n"),n=t.slice(Math.max(0,t.length-200)).join("\n");n.trim().length>0&&process.stderr.write(`
|
|
500
44
|
[daemon log]
|
|
501
45
|
${n}
|
|
502
|
-
`)}}catch{}
|
|
46
|
+
`)}}catch{}J&&J(),process.exit(1)}finally{J&&J()}})}n(process.argv[1]??"").href===import.meta.url&&ne(process.argv.slice(2)).catch(e=>{eD(f(u(e)),{showDetails:!0}),process.exit(1)});export{e_ as printJson,ne as runCli,tX as runCliCommandWithOutput,tC as writeCommandOutput};
|