@houwert/conductor 0.12.2 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -28
- package/dist/commands/install.js +1 -139
- package/dist/commands/logs.js +81 -85
- package/dist/daemon/client.js +2 -9
- package/dist/daemon/log-collector.js +91 -198
- package/dist/daemon/server.js +1 -11
- package/dist/drivers/log-sources/daemon.js +2 -9
- package/dist/drivers/log-sources/metro-discovery.js +208 -0
- package/dist/index.js +1 -21
- package/package.json +3 -6
- package/.claude-plugin/plugin.json +0 -9
- package/dist/commands/cheat-sheet.js +0 -109
- package/dist/postinstall.js +0 -12
- package/skills/conductor/SKILL.md +0 -825
- package/skills/conductor/references/flow-syntax.md +0 -182
- package/skills/skills.yaml +0 -8
|
@@ -1,825 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: conductor
|
|
3
|
-
version: 0.12.2
|
|
4
|
-
description: "Token-efficient CLI for mobile UI testing (iOS simulator + Android emulator), designed for AI agents"
|
|
5
|
-
metadata.openclaw:
|
|
6
|
-
category: service
|
|
7
|
-
requires:
|
|
8
|
-
bins: [conductor]
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# conductor
|
|
12
|
-
|
|
13
|
-
A token-efficient CLI for Conductor mobile UI testing, designed for AI agents.
|
|
14
|
-
|
|
15
|
-
## Usage
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
conductor <command> [args] [options]
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Global options:
|
|
22
|
-
- `--device <id>` — target device ID; also keys the session and daemon (auto-detected if omitted)
|
|
23
|
-
- `--device-name <name>` — target a booted device by its human-readable name (resolved to ID from booted devices); mutually exclusive with `--device`
|
|
24
|
-
- `--help` — show help
|
|
25
|
-
- `--json` — machine-readable JSON output (avoid unless you need structured parsing)
|
|
26
|
-
- `--verbose / -v` — log daemon calls, driver fallbacks, and raw output
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## Commands
|
|
31
|
-
|
|
32
|
-
### `list-devices`
|
|
33
|
-
|
|
34
|
-
List connected Android emulators and iOS simulators. Shows both booted (running) devices and available (shutdown) simulators/emulators that can be started with `start-device`.
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
conductor list-devices
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Output:
|
|
41
|
-
```
|
|
42
|
-
Booted devices:
|
|
43
|
-
android device emulator-5554 Pixel_6_API_33
|
|
44
|
-
ios booted ABC123-DEF456 iPhone 15
|
|
45
|
-
|
|
46
|
-
Available devices:
|
|
47
|
-
ios shutdown DEF789-ABC012 iPhone 16 Pro
|
|
48
|
-
android available Pixel_7_API_34 Pixel_7_API_34
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Exits with code 0 if any booted or available devices exist, 1 only if both lists are empty.
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
### `foreground-app`
|
|
56
|
-
|
|
57
|
-
Print the bundle ID (iOS) or package name (Android) of the currently open app.
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
conductor foreground-app
|
|
61
|
-
conductor foreground-app --device C59D3241-FB6A-4E3B-AE7B-A82D3C933889
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
### `list-apps`
|
|
67
|
-
|
|
68
|
-
List all installed app IDs (bundle IDs on iOS, package names on Android), sorted alphabetically. Does not require the driver to be running.
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
conductor list-apps
|
|
72
|
-
conductor list-apps --device emulator-5554
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
### `copy-app <bundleId>`
|
|
78
|
-
|
|
79
|
-
Copy an installed app from one iOS simulator to another. Useful when you have a compiled `.app` bundle on one simulator and want to install it on others without recompiling.
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
conductor copy-app com.example.myapp --from C59D3241-FB6A-4E3B-AE7B-A82D3C933889 --to 86B1BC33-7D83-47FF-AAFE-1BD70FC53038
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Flags:
|
|
86
|
-
- `--from <id>` — source device ID (must be an iOS simulator)
|
|
87
|
-
- `--to <id>` — target device ID (must be an iOS simulator)
|
|
88
|
-
|
|
89
|
-
Both flags are required. The command reads the `.app` bundle path from the source simulator and installs it on the target.
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
### `install-app <path>`
|
|
94
|
-
|
|
95
|
-
Install an app from a local `.app` bundle, `.ipa`, or `.apk` file onto the device.
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
conductor install-app ./build/MyApp.app
|
|
99
|
-
conductor install-app ./build/app-debug.apk --device emulator-5554
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
### `launch-app <appId>`
|
|
105
|
-
|
|
106
|
-
Launch an app by bundle ID and save it to the session.
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
conductor launch-app com.example.myapp
|
|
110
|
-
conductor launch-app com.example.myapp --device emulator-5554
|
|
111
|
-
conductor launch-app com.example.myapp --clear-state # wipe app data first
|
|
112
|
-
conductor launch-app com.example.myapp --clear-keychain # wipe keychain first
|
|
113
|
-
conductor launch-app com.example.myapp --no-stop-app # resume instead of restart
|
|
114
|
-
conductor launch-app com.example.myapp --argument env=staging --argument debug=true
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Flags:
|
|
118
|
-
- `--clear-state` — clear app data/state before launching
|
|
119
|
-
- `--clear-keychain` — clear keychain before launching (iOS: full keychain; Android: account credentials)
|
|
120
|
-
- `--no-stop-app` — do not stop the app before launching; brings it to the foreground instead of restarting it (default: app is stopped first)
|
|
121
|
-
- `--argument key=value` — set a launch argument; repeatable for multiple arguments
|
|
122
|
-
|
|
123
|
-
The `appId` and `deviceId` are persisted to `~/.conductor/sessions/<deviceId>.json` and reused by subsequent commands.
|
|
124
|
-
|
|
125
|
-
---
|
|
126
|
-
|
|
127
|
-
### `stop-app [<appId>]`
|
|
128
|
-
|
|
129
|
-
Stop the running app. Uses session `appId` if not specified.
|
|
130
|
-
|
|
131
|
-
```bash
|
|
132
|
-
conductor stop-app
|
|
133
|
-
conductor stop-app com.example.myapp
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
### `clear-state [<appId>]`
|
|
139
|
-
|
|
140
|
-
Clear app data/state without relaunching. Uses the session `appId` if not specified. On iOS this terminates the app, preserves the .app bundle, uninstalls, and reinstalls (clearing all user data). On Android this runs `pm clear`.
|
|
141
|
-
|
|
142
|
-
```bash
|
|
143
|
-
conductor clear-state
|
|
144
|
-
conductor clear-state com.example.myapp
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
### `uninstall-app <appId>`
|
|
150
|
-
|
|
151
|
-
Uninstall an app from the device. The app ID is required.
|
|
152
|
-
|
|
153
|
-
```bash
|
|
154
|
-
conductor uninstall-app com.example.myapp
|
|
155
|
-
conductor uninstall-app com.example.myapp --device emulator-5554
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
### `tap-on <element>`
|
|
161
|
-
|
|
162
|
-
Tap a UI element by its text label or accessibility ID. **Try the most obvious text label or ID first** — e.g. `"Sign In"`, `"Submit"`, `"btn_login"`. Only run `inspect` to look up the exact identifier if your first attempt fails. **For icon-only buttons (no visible text label), always run `inspect` first** to find the accessibility ID before tapping.
|
|
163
|
-
|
|
164
|
-
```bash
|
|
165
|
-
conductor tap-on "Sign In"
|
|
166
|
-
conductor tap-on --id "btn_login" # match by accessibility ID instead of text
|
|
167
|
-
conductor tap-on --text "Edit" # match by text only (not id)
|
|
168
|
-
conductor tap-on "Next" --index 1 # pick the 2nd match (0-based)
|
|
169
|
-
conductor tap-on "Add to cart" --long-press
|
|
170
|
-
conductor tap-on "Like" --double-tap
|
|
171
|
-
conductor tap-on "Delete" --optional # do not fail if not found
|
|
172
|
-
conductor tap-on "Edit" --below "Username" # tap "Edit" that is below the "Username" element
|
|
173
|
-
conductor tap-on "Submit" --above "Footer"
|
|
174
|
-
conductor tap-on ">" --right-of "Email"
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
Flags:
|
|
178
|
-
- `--id <id>` — match by accessibility ID / resourceId instead of text
|
|
179
|
-
- `--text <text>` — match by text only (not id); bare positional arg matches text OR id
|
|
180
|
-
- `--index <n>` — pick the nth match when multiple elements share the same text/id (0-based)
|
|
181
|
-
- `--long-press` — hold instead of tap
|
|
182
|
-
- `--double-tap` — double-tap the element
|
|
183
|
-
- `--optional` — do not fail if element is not found
|
|
184
|
-
- `--focused` — match only focused elements
|
|
185
|
-
- `--enabled` / `--no-enabled` — match by enabled state
|
|
186
|
-
- `--checked` / `--no-checked` — match by checked state
|
|
187
|
-
- `--selected` / `--no-selected` — match by selected state
|
|
188
|
-
- `--below <text>` — match element below the given reference element
|
|
189
|
-
- `--above <text>` — match element above the given reference element
|
|
190
|
-
- `--left-of <text>` — match element left of the given reference element
|
|
191
|
-
- `--right-of <text>` — match element right of the given reference element
|
|
192
|
-
|
|
193
|
-
---
|
|
194
|
-
|
|
195
|
-
### `input-text <text>`
|
|
196
|
-
|
|
197
|
-
Type text into the currently focused input field.
|
|
198
|
-
|
|
199
|
-
```bash
|
|
200
|
-
conductor input-text "hello@example.com"
|
|
201
|
-
conductor input-text "my password"
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
---
|
|
205
|
-
|
|
206
|
-
### `erase-text [n]`
|
|
207
|
-
|
|
208
|
-
Erase characters from the currently focused input field. Defaults to 50 characters.
|
|
209
|
-
|
|
210
|
-
```bash
|
|
211
|
-
conductor erase-text # erase 50 characters
|
|
212
|
-
conductor erase-text 10 # erase 10 characters
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
---
|
|
216
|
-
|
|
217
|
-
### `back`
|
|
218
|
-
|
|
219
|
-
Press the Android back button. **Android only** — iOS has no back button. On iOS, always run `inspect` first to find the exact label or accessibility ID of the in-app back button, then use `tap-on` to press it.
|
|
220
|
-
|
|
221
|
-
```bash
|
|
222
|
-
conductor back
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
---
|
|
226
|
-
|
|
227
|
-
### `press-key <key>`
|
|
228
|
-
|
|
229
|
-
Press a hardware or keyboard key. Valid keys: `Enter`, `Backspace`, `Home`, `End`, `Tab`, `Delete`, `Escape`, `VolumeUp`, `VolumeDown`, `Power`, `Lock`, `Back`, `Camera`, `Search`, `Remote Dpad Up`, `Remote Dpad Down`, `Remote Dpad Left`, `Remote Dpad Right`, `Remote Dpad Center`.
|
|
230
|
-
|
|
231
|
-
Not all keys are supported on all platforms — unsupported keys are silently ignored (e.g. `Back` on iOS). The `Remote Dpad *` keys are Android TV only.
|
|
232
|
-
|
|
233
|
-
```bash
|
|
234
|
-
conductor press-key Enter
|
|
235
|
-
conductor press-key Backspace
|
|
236
|
-
conductor press-key VolumeUp
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
### `hide-keyboard`
|
|
242
|
-
|
|
243
|
-
Dismiss the on-screen keyboard. On iOS, sends the return key; on Android, sends KEYCODE_ESCAPE.
|
|
244
|
-
|
|
245
|
-
```bash
|
|
246
|
-
conductor hide-keyboard
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
---
|
|
250
|
-
|
|
251
|
-
### `scroll`
|
|
252
|
-
|
|
253
|
-
Scroll the screen. Default direction is `down`.
|
|
254
|
-
|
|
255
|
-
```bash
|
|
256
|
-
conductor scroll
|
|
257
|
-
conductor scroll --direction up
|
|
258
|
-
conductor scroll --direction left
|
|
259
|
-
conductor scroll --direction right
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
Directions: `down` | `up` | `left` | `right`
|
|
263
|
-
|
|
264
|
-
---
|
|
265
|
-
|
|
266
|
-
### `swipe`
|
|
267
|
-
|
|
268
|
-
Perform a swipe gesture.
|
|
269
|
-
|
|
270
|
-
```bash
|
|
271
|
-
conductor swipe --direction UP
|
|
272
|
-
conductor swipe --direction DOWN
|
|
273
|
-
conductor swipe --direction LEFT
|
|
274
|
-
conductor swipe --direction RIGHT
|
|
275
|
-
conductor swipe --start 0.5,0.8 --end 0.5,0.2 # normalised coords (0–1)
|
|
276
|
-
conductor swipe --start 540,1600 --end 540,400 # absolute px
|
|
277
|
-
conductor swipe --direction UP --duration 1000 # slower swipe
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
Flags:
|
|
281
|
-
- `--direction <UP|DOWN|LEFT|RIGHT>` — directional swipe (case-insensitive)
|
|
282
|
-
- `--start <x,y>` — start coordinate; values ≤1 are treated as normalised (0–1), larger as absolute px
|
|
283
|
-
- `--end <x,y>` — end coordinate (same normalisation rule)
|
|
284
|
-
- `--duration <ms>` — swipe duration in milliseconds (default: 500); use `--start`/`--end` or `--direction`
|
|
285
|
-
|
|
286
|
-
---
|
|
287
|
-
|
|
288
|
-
### `scroll-until-visible <element>`
|
|
289
|
-
|
|
290
|
-
Scroll in a direction until an element is visible. Useful for long lists.
|
|
291
|
-
|
|
292
|
-
```bash
|
|
293
|
-
conductor scroll-until-visible "Checkout"
|
|
294
|
-
conductor scroll-until-visible --id "btn_submit"
|
|
295
|
-
conductor scroll-until-visible "Terms" --direction up
|
|
296
|
-
conductor scroll-until-visible "Privacy" --timeout 60000
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
Flags:
|
|
300
|
-
- `--id <id>` — match by accessibility ID / resourceId instead of text
|
|
301
|
-
- `--text <text>` — match by text only (not id)
|
|
302
|
-
- `--direction <down|up|left|right>` — scroll direction (default: `down`)
|
|
303
|
-
- `--timeout <ms>` — max time in milliseconds (default: 30 000)
|
|
304
|
-
|
|
305
|
-
---
|
|
306
|
-
|
|
307
|
-
### `assert-visible <element>`
|
|
308
|
-
|
|
309
|
-
Assert that a UI element is visible on screen. Exits with code 1 if not found. Try the expected text or ID directly — only use `inspect` if it fails and you need to find the exact identifier.
|
|
310
|
-
|
|
311
|
-
```bash
|
|
312
|
-
conductor assert-visible "Welcome"
|
|
313
|
-
conductor assert-visible --id "dashboard_title"
|
|
314
|
-
conductor assert-visible --text "Submit" # text-only match
|
|
315
|
-
conductor assert-visible "Loading..." --optional # do not fail if absent
|
|
316
|
-
conductor assert-visible "Dashboard" --timeout 30000 # wait up to 30 s
|
|
317
|
-
conductor assert-visible "Edit" --below "Username" # relative position
|
|
318
|
-
conductor assert-visible "Item" --index 2 # third match (0-based)
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
Flags:
|
|
322
|
-
- `--id <id>` — match by accessibility ID / resourceId instead of text
|
|
323
|
-
- `--text <text>` — match by text only (not id); bare positional arg matches text OR id
|
|
324
|
-
- `--index <n>` — pick the nth match (0-based)
|
|
325
|
-
- `--timeout <ms>` — max wait time in milliseconds (default: 17 000)
|
|
326
|
-
- `--optional` — succeed even if the element is not found (useful for conditional checks)
|
|
327
|
-
- `--focused` — match only focused elements
|
|
328
|
-
- `--enabled` / `--no-enabled` — match by enabled state
|
|
329
|
-
- `--checked` / `--no-checked` — match by checked state
|
|
330
|
-
- `--selected` / `--no-selected` — match by selected state
|
|
331
|
-
- `--below <text>` — match element below the given reference element
|
|
332
|
-
- `--above <text>` — match element above the given reference element
|
|
333
|
-
- `--left-of <text>` — match element left of the given reference element
|
|
334
|
-
- `--right-of <text>` — match element right of the given reference element
|
|
335
|
-
|
|
336
|
-
---
|
|
337
|
-
|
|
338
|
-
### `assert-not-visible <element>`
|
|
339
|
-
|
|
340
|
-
Assert that a UI element is **not** visible on screen. Fails (exit code 1) if the element is found. Use for verifying elements have been dismissed or hidden.
|
|
341
|
-
|
|
342
|
-
```bash
|
|
343
|
-
conductor assert-not-visible "Loading..."
|
|
344
|
-
conductor assert-not-visible --id "error_banner"
|
|
345
|
-
conductor assert-not-visible "Error" --timeout 5000 # wait up to 5 s for it to disappear
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
Flags:
|
|
349
|
-
- `--id <id>` — match by accessibility ID / resourceId instead of text
|
|
350
|
-
- `--text <text>` — match by text only (not id)
|
|
351
|
-
- `--index <n>` — pick the nth match (0-based)
|
|
352
|
-
- `--timeout <ms>` — max time to wait for element to disappear (default: 1 000)
|
|
353
|
-
|
|
354
|
-
---
|
|
355
|
-
|
|
356
|
-
### `open-link <url>`
|
|
357
|
-
|
|
358
|
-
Open a URL or deep link on the device. Works for both HTTP URLs and custom scheme deep links.
|
|
359
|
-
|
|
360
|
-
```bash
|
|
361
|
-
conductor open-link "https://example.com/reset-password"
|
|
362
|
-
conductor open-link "myapp://onboarding"
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
---
|
|
366
|
-
|
|
367
|
-
### `set-location --lat <n> --lng <n>`
|
|
368
|
-
|
|
369
|
-
Set the device's simulated GPS location.
|
|
370
|
-
|
|
371
|
-
```bash
|
|
372
|
-
conductor set-location --lat 52.3676 --lng 4.9041
|
|
373
|
-
conductor set-location --lat 37.7749 --lng -122.4194
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
---
|
|
377
|
-
|
|
378
|
-
### `set-orientation <portrait|landscape>`
|
|
379
|
-
|
|
380
|
-
Set the device orientation.
|
|
381
|
-
|
|
382
|
-
```bash
|
|
383
|
-
conductor set-orientation portrait
|
|
384
|
-
conductor set-orientation landscape
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
---
|
|
388
|
-
|
|
389
|
-
### `take-screenshot`
|
|
390
|
-
|
|
391
|
-
Take a screenshot of the current screen. **Prefer `inspect` over `take-screenshot`** when you need to understand what is on screen — the view hierarchy tells you element text, IDs, and structure without consuming a vision token. Use `take-screenshot` only for visual evidence, debugging rendering issues, or when a human needs to see the screen.
|
|
392
|
-
|
|
393
|
-
```bash
|
|
394
|
-
conductor take-screenshot
|
|
395
|
-
conductor take-screenshot --output /tmp/screen.png
|
|
396
|
-
conductor take-screenshot --output ./screenshots/login.png
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
Default output: `./screenshot-<timestamp>.png`
|
|
400
|
-
|
|
401
|
-
**Screenshot path guidance:** Always write to `/tmp/<agent-name>/` (e.g. `/tmp/tester-ios-iphone-16/screen.png`). This keeps files off the working directory, is unique per agent, and is the same folder used for YAML flows. Create it with `mkdir -p` before first use.
|
|
402
|
-
|
|
403
|
-
---
|
|
404
|
-
|
|
405
|
-
### `inspect`
|
|
406
|
-
|
|
407
|
-
Print the UI element hierarchy of the current screen. Use this when a `tap-on` or `assert-visible` fails and you need to discover the exact element text or accessibility ID.
|
|
408
|
-
|
|
409
|
-
The hierarchy shows each element's type, text, and accessibility ID (resourceId / accessibilityIdentifier). Use the `text` value or `id` value as the argument to `tap-on` / `assert-visible`.
|
|
410
|
-
|
|
411
|
-
```bash
|
|
412
|
-
conductor inspect
|
|
413
|
-
conductor inspect --dump # full hierarchy as JSON with a11y fields
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
`--dump` emits a11y-enriched JSON on all platforms. Each node carries
|
|
417
|
-
`accessibilityOrder` (0-based screen-reader nav index, or `null`), `traits` / `role`,
|
|
418
|
-
`nodeId` (stable per-capture path), and platform-specific fields:
|
|
419
|
-
`isAccessibilityElement` + `accessibilityLabel/Hint/Value` on iOS,
|
|
420
|
-
`contentDescription` + `screenReaderFocusable` + `importantForAccessibility` on Android,
|
|
421
|
-
`accessibleName` + `focusable` on web. Existing fields (`label`, `frame`, `identifier`,
|
|
422
|
-
`text`, `resource-id`, `bounds`, ...) are preserved.
|
|
423
|
-
|
|
424
|
-
---
|
|
425
|
-
|
|
426
|
-
### `capture-ui`
|
|
427
|
-
|
|
428
|
-
Bundle everything Argus needs for a single UI inspection: screenshot (PNG base64) +
|
|
429
|
-
full UI hierarchy (with a11y fields) + flat accessibility-order snapshot in one JSON
|
|
430
|
-
document. Intended primarily for programmatic consumers (Argus UI panel); humans
|
|
431
|
-
should prefer `inspect` + `take-screenshot`.
|
|
432
|
-
|
|
433
|
-
```bash
|
|
434
|
-
conductor capture-ui # prints bundle as JSON to stdout
|
|
435
|
-
conductor capture-ui --output /tmp/capture.json # writes bundle to file
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
Output shape:
|
|
439
|
-
|
|
440
|
-
```json
|
|
441
|
-
{
|
|
442
|
-
"version": 1,
|
|
443
|
-
"capturedAt": "2026-04-22T12:34:56.000Z",
|
|
444
|
-
"device": { "platform": "ios", "deviceId": "...", "width": 390, "height": 844 },
|
|
445
|
-
"screenshot": { "kind": "composite", "encoding": "png", "data": "<base64>" },
|
|
446
|
-
"hierarchy": { /* platform-native hierarchy with a11y fields on each node */ },
|
|
447
|
-
"a11ySnapshot": [
|
|
448
|
-
{ "nodeId": "0.2.1", "order": 0, "frame": {"x":0,"y":0,"w":80,"h":44},
|
|
449
|
-
"label": "Sign in", "hint": "", "role": "button", "traits": ["button"],
|
|
450
|
-
"announcement": "Sign in, button", "value": "",
|
|
451
|
-
"state": {"enabled": true, "selected": false, "focused": false} }
|
|
452
|
-
],
|
|
453
|
-
"capabilities": { "perViewPixels": false, "depthData": false }
|
|
454
|
-
}
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
`nodeId` is a stable per-capture path (dot-joined child indices) so Argus can
|
|
458
|
-
correlate flat snapshot entries back to hierarchy nodes. `capabilities.perViewPixels`
|
|
459
|
-
is `false` in v1; per-view pixel rendering is a future phase.
|
|
460
|
-
|
|
461
|
-
---
|
|
462
|
-
|
|
463
|
-
### `logs`
|
|
464
|
-
|
|
465
|
-
Stream app console/log output in real time. Auto-detects the best log source based on the platform:
|
|
466
|
-
|
|
467
|
-
- **Web** — captures Playwright browser console events
|
|
468
|
-
- **React Native (iOS/Android)** — connects to the Metro dev server via Chrome DevTools Protocol
|
|
469
|
-
- **Native iOS** — streams from the simulator via `simctl log stream`
|
|
470
|
-
- **Native Android** — streams from the device via `adb logcat`
|
|
471
|
-
|
|
472
|
-
```bash
|
|
473
|
-
conductor logs # auto-detect source and stream
|
|
474
|
-
conductor logs --source metro # force Metro CDP (for React Native JS logs)
|
|
475
|
-
conductor logs --source device # force platform-native logs (simctl/logcat)
|
|
476
|
-
conductor logs --level warn # only show warnings and errors
|
|
477
|
-
conductor logs --level error # only show errors
|
|
478
|
-
conductor logs --metro-port 8082 # custom Metro dev server port (default: 8081)
|
|
479
|
-
conductor logs --target 1 # connect to a specific Metro debugger target (0-based)
|
|
480
|
-
conductor logs --list # list available Metro debugger targets and exit
|
|
481
|
-
conductor logs --list --json # list targets as JSON (includes appId, logicalDeviceId)
|
|
482
|
-
conductor logs --json # NDJSON output (one JSON object per line)
|
|
483
|
-
```
|
|
484
|
-
|
|
485
|
-
For iOS/Android, auto-detection tries Metro first (2s timeout), then falls back to device logs. When multiple devices share one Metro instance, use `--list` to see available targets, then `--target <n>` to connect to a specific one. `--list` and `--source metro` don't require a running device session.
|
|
486
|
-
|
|
487
|
-
Exits on Ctrl+C. Each line in `--json` mode is a complete JSON object:
|
|
488
|
-
```json
|
|
489
|
-
{"timestamp":"...","level":"log","message":"User tapped sign-in","stackTrace":null,"source":"metro"}
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
---
|
|
493
|
-
|
|
494
|
-
### `run-flow <file>`
|
|
495
|
-
|
|
496
|
-
Execute a Maestro YAML flow file.
|
|
497
|
-
|
|
498
|
-
```bash
|
|
499
|
-
conductor run-flow ./flows/login.yaml
|
|
500
|
-
conductor run-flow ./flows/checkout.yaml --device emulator-5554
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
---
|
|
504
|
-
|
|
505
|
-
### `run-flow-inline <yaml>`
|
|
506
|
-
|
|
507
|
-
Execute inline Maestro YAML commands directly.
|
|
508
|
-
|
|
509
|
-
```bash
|
|
510
|
-
conductor run-flow-inline "- tapOn: \"Submit\"\n- assertVisible: \"Success\""
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
---
|
|
514
|
-
|
|
515
|
-
### `session`
|
|
516
|
-
|
|
517
|
-
Show, clear, or list device sessions.
|
|
518
|
-
|
|
519
|
-
```bash
|
|
520
|
-
conductor session # show session for auto-detected device
|
|
521
|
-
conductor session --device <id> # show session for a specific device
|
|
522
|
-
conductor session --clear --device <id> # clear a device's session
|
|
523
|
-
conductor session --list # list all device sessions
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
Sessions are stored in `~/.conductor/sessions/<deviceId>.json`.
|
|
527
|
-
|
|
528
|
-
---
|
|
529
|
-
|
|
530
|
-
### `cheat-sheet`
|
|
531
|
-
|
|
532
|
-
Print this command reference inline.
|
|
533
|
-
|
|
534
|
-
```bash
|
|
535
|
-
conductor cheat-sheet
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
---
|
|
539
|
-
|
|
540
|
-
### `install-skills`
|
|
541
|
-
|
|
542
|
-
Copy the conductor skill files into the current project's `.claude/skills/conductor/` directory, so AI agents in that project can use the `cheat-sheet` command locally.
|
|
543
|
-
|
|
544
|
-
```bash
|
|
545
|
-
conductor install-skills
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
---
|
|
549
|
-
|
|
550
|
-
### `daemon-start`
|
|
551
|
-
|
|
552
|
-
Start the background daemon for the current device session. The daemon keeps the driver process alive between commands, reducing startup overhead. Commands auto-start the driver directly if no daemon is running — this is optional.
|
|
553
|
-
|
|
554
|
-
```bash
|
|
555
|
-
conductor daemon-start
|
|
556
|
-
conductor daemon-start --device emulator-5554
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
---
|
|
560
|
-
|
|
561
|
-
### `daemon-stop`
|
|
562
|
-
|
|
563
|
-
Stop the daemon for a device session.
|
|
564
|
-
|
|
565
|
-
```bash
|
|
566
|
-
conductor daemon-stop
|
|
567
|
-
conductor daemon-stop --device emulator-5554
|
|
568
|
-
conductor daemon-stop --all # stop all running daemons
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
---
|
|
572
|
-
|
|
573
|
-
### `daemon-status`
|
|
574
|
-
|
|
575
|
-
Show whether the daemon is running for a device session.
|
|
576
|
-
|
|
577
|
-
```bash
|
|
578
|
-
conductor daemon-status
|
|
579
|
-
conductor daemon-status --device emulator-5554
|
|
580
|
-
```
|
|
581
|
-
|
|
582
|
-
---
|
|
583
|
-
|
|
584
|
-
### `device-pool`
|
|
585
|
-
|
|
586
|
-
Manage a shared pool of devices for concurrent multi-agent use. Pool state is stored in `~/.conductor/device-pool.json` with file-based locking so multiple agents can safely acquire/release devices without colliding.
|
|
587
|
-
|
|
588
|
-
```bash
|
|
589
|
-
conductor device-pool --list # list all devices and their pool status
|
|
590
|
-
conductor device-pool --acquire # claim a free device; prints its ID
|
|
591
|
-
conductor device-pool --release <id> # release a device back to the pool
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
Typical multi-agent workflow:
|
|
595
|
-
```bash
|
|
596
|
-
# Each agent acquires its own device before starting
|
|
597
|
-
DEVICE=$(conductor device-pool --acquire)
|
|
598
|
-
conductor launch-app com.example.myapp --device "$DEVICE"
|
|
599
|
-
# ... run tests ...
|
|
600
|
-
conductor device-pool --release "$DEVICE"
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
Stale acquisitions (whose process is no longer running) are automatically pruned on the next `--acquire`.
|
|
604
|
-
|
|
605
|
-
---
|
|
606
|
-
|
|
607
|
-
### `run-parallel`
|
|
608
|
-
|
|
609
|
-
Distribute a directory of Maestro YAML flow files across all booted devices, running them in parallel. Flows are assigned round-robin; results are aggregated and printed at the end.
|
|
610
|
-
|
|
611
|
-
```bash
|
|
612
|
-
conductor run-parallel --flows-dir ./tests
|
|
613
|
-
```
|
|
614
|
-
|
|
615
|
-
Exits with code 0 if all flows pass, 1 if any fail.
|
|
616
|
-
|
|
617
|
-
---
|
|
618
|
-
|
|
619
|
-
## Typical Agent Workflow
|
|
620
|
-
|
|
621
|
-
**Prefer individual CLI commands over `run-flow` / `run-flow-inline`.** Use `tap-on`, `input-text`, `scroll`, `swipe`, `assert-visible`, etc. directly — they are faster, give immediate feedback per step, and make failures easier to diagnose. Only reach for `run-flow` or `run-flow-inline` when you need Maestro-specific YAML features (conditional logic, `runScript`, retryTapIfNoChange`, etc.) that have no CLI equivalent.
|
|
622
|
-
|
|
623
|
-
**Always `launch-app` before interacting.** Never attempt to `tap-on`, `input-text`, `scroll`, or navigate before the app is launched and the session is set. `launch-app` both opens the app and saves the `appId`/`deviceId` to the session so all subsequent commands know which device and app to target.
|
|
624
|
-
|
|
625
|
-
**To understand what is on screen, run `inspect` first** — it gives you element text, IDs, and structure. Only take a `take-screenshot` when you need visual evidence or are debugging a rendering issue.
|
|
626
|
-
|
|
627
|
-
**Try obvious text labels and IDs first. Only run `inspect` when a `tap-on` or `assert-visible` fails and you need to discover the exact identifier.**
|
|
628
|
-
|
|
629
|
-
Single-agent (default session, no flag needed):
|
|
630
|
-
```bash
|
|
631
|
-
# 1. Check what devices are available
|
|
632
|
-
conductor list-devices
|
|
633
|
-
|
|
634
|
-
# 2. Launch the app — ALWAYS do this first (sets session, opens app)
|
|
635
|
-
conductor launch-app com.example.myapp --device emulator-5554
|
|
636
|
-
|
|
637
|
-
# 3. Interact using the most likely text labels or IDs
|
|
638
|
-
conductor tap-on "Sign In" # try the obvious label first
|
|
639
|
-
conductor tap-on "username_field" # or a guessed test ID
|
|
640
|
-
conductor input-text "user@example.com"
|
|
641
|
-
conductor tap-on "password_field"
|
|
642
|
-
conductor input-text "secret123"
|
|
643
|
-
conductor tap-on "Login"
|
|
644
|
-
|
|
645
|
-
# → If a tap fails, run inspect to find the real identifier:
|
|
646
|
-
conductor inspect
|
|
647
|
-
# → hierarchy shows text="Log in", id="btn_login" — retry with correct value
|
|
648
|
-
conductor tap-on "Log in"
|
|
649
|
-
|
|
650
|
-
# 4. Assert — try expected text directly
|
|
651
|
-
conductor assert-visible "Dashboard"
|
|
652
|
-
|
|
653
|
-
# 5. Inspect to understand what is on screen (prefer over screenshot)
|
|
654
|
-
conductor inspect
|
|
655
|
-
|
|
656
|
-
# 6. Screenshot only for visual evidence or rendering checks
|
|
657
|
-
conductor take-screenshot --output /tmp/tester-ios-iphone-16/post-login.png
|
|
658
|
-
|
|
659
|
-
# 7. Run a full flow only when CLI commands aren't sufficient
|
|
660
|
-
# Write the YAML to /tmp/<agent-name>/ first — never pass YAML inline, it doesn't work
|
|
661
|
-
mkdir -p /tmp/tester-ios-iphone-16
|
|
662
|
-
cat > /tmp/tester-ios-iphone-16/checkout.yaml << 'EOF'
|
|
663
|
-
appId: com.example.myapp
|
|
664
|
-
---
|
|
665
|
-
- tapOn: "Checkout"
|
|
666
|
-
- assertVisible: "Order confirmed"
|
|
667
|
-
EOF
|
|
668
|
-
conductor run-flow /tmp/tester-ios-iphone-16/checkout.yaml
|
|
669
|
-
```
|
|
670
|
-
|
|
671
|
-
---
|
|
672
|
-
|
|
673
|
-
## Session State
|
|
674
|
-
|
|
675
|
-
Each session is stored in `~/.conductor/sessions/<name>.json`:
|
|
676
|
-
|
|
677
|
-
```json
|
|
678
|
-
{
|
|
679
|
-
"appId": "com.example.myapp",
|
|
680
|
-
"deviceId": "emulator-5554"
|
|
681
|
-
}
|
|
682
|
-
```
|
|
683
|
-
|
|
684
|
-
- Set automatically by `launch-app`
|
|
685
|
-
- Cleared by `session --clear`
|
|
686
|
-
- Used by all interaction commands when `--device` is not specified
|
|
687
|
-
|
|
688
|
-
---
|
|
689
|
-
|
|
690
|
-
## Multi-Agent Parallel Testing
|
|
691
|
-
|
|
692
|
-
Pass `--device <id>` on every command. The device ID is the natural key — each device gets its own session file and its own daemon process (with a persistent direct driver connection). Two agents targeting different devices never share state or a daemon.
|
|
693
|
-
|
|
694
|
-
### Device assignment — do this first, before spawning agents
|
|
695
|
-
|
|
696
|
-
**CRITICAL: each device must be assigned to exactly one agent. Never give two agents the same device.**
|
|
697
|
-
|
|
698
|
-
Before spawning any agents, check how many devices are already booted with `list-devices`. If there are not enough for the number of agents you need, boot more with `start-device` — one call per additional device needed. Only then assign and spawn agents.
|
|
699
|
-
|
|
700
|
-
```bash
|
|
701
|
-
# 1. Check what's already booted
|
|
702
|
-
conductor list-devices
|
|
703
|
-
# ios booted C59D3241-... iPhone 16
|
|
704
|
-
# (only 1 device, but we need 2)
|
|
705
|
-
|
|
706
|
-
# 2. Boot another if needed
|
|
707
|
-
conductor start-device --platform ios
|
|
708
|
-
|
|
709
|
-
# 3. Re-check to get the new device's ID
|
|
710
|
-
conductor list-devices
|
|
711
|
-
# ios booted C59D3241-... iPhone 16
|
|
712
|
-
# ios booted 86B1BC33-... iPhone 15
|
|
713
|
-
```
|
|
714
|
-
|
|
715
|
-
Assign explicitly — one device per agent, no sharing.
|
|
716
|
-
|
|
717
|
-
```bash
|
|
718
|
-
conductor list-devices
|
|
719
|
-
# ios booted C59D3241-FB6A-4E3B-AE7B-A82D3C933889 iPhone 16
|
|
720
|
-
# ios booted 86B1BC33-7D83-47FF-AAFE-1BD70FC53038 iPhone 15
|
|
721
|
-
# android device emulator-5554 Pixel_6_API_33
|
|
722
|
-
```
|
|
723
|
-
|
|
724
|
-
Assign explicitly — one device per agent, no sharing:
|
|
725
|
-
```
|
|
726
|
-
Agent tester-ios-iphone-16 → --device C59D3241-FB6A-4E3B-AE7B-A82D3C933889
|
|
727
|
-
Agent tester-ios-iphone-15 → --device 86B1BC33-7D83-47FF-AAFE-1BD70FC53038
|
|
728
|
-
Agent tester-android-pixel-6 → --device emulator-5554
|
|
729
|
-
```
|
|
730
|
-
|
|
731
|
-
### Agent naming
|
|
732
|
-
|
|
733
|
-
Name each agent after the platform under test and the device it controls: `tester-${platform}-${device_name}` (lowercased, spaces replaced with hyphens). The platform prefix reflects what is being tested — it is not always `ios`. Examples:
|
|
734
|
-
- `tester-ios-iphone-16` — testing an iOS app on an iPhone 16 simulator
|
|
735
|
-
- `tester-android-pixel-6` — testing an Android app on a Pixel 6 emulator
|
|
736
|
-
- `tester-web-iphone-15` — testing a mobile web app on an iPhone 15 simulator
|
|
737
|
-
- `tester-payments-pixel-7` — testing a payments flow on a Pixel 7 emulator
|
|
738
|
-
|
|
739
|
-
This makes it immediately obvious which agent controls which device and prevents accidental reassignment.
|
|
740
|
-
|
|
741
|
-
### Setup — one `launch-app` per device
|
|
742
|
-
|
|
743
|
-
```bash
|
|
744
|
-
# Agent tester-ios-iphone-16
|
|
745
|
-
conductor launch-app com.example.myapp --device C59D3241-FB6A-4E3B-AE7B-A82D3C933889
|
|
746
|
-
|
|
747
|
-
# Agent tester-android-pixel-6
|
|
748
|
-
conductor launch-app com.example.myapp --device emulator-5554
|
|
749
|
-
```
|
|
750
|
-
|
|
751
|
-
**Every subsequent command carries `--device`:**
|
|
752
|
-
```bash
|
|
753
|
-
# Agent tester-ios-iphone-16
|
|
754
|
-
conductor tap-on "Sign In" --device C59D3241-FB6A-4E3B-AE7B-A82D3C933889
|
|
755
|
-
conductor input-text "user@a.com" --device C59D3241-FB6A-4E3B-AE7B-A82D3C933889
|
|
756
|
-
conductor inspect --device C59D3241-FB6A-4E3B-AE7B-A82D3C933889
|
|
757
|
-
|
|
758
|
-
# Agent tester-android-pixel-6 (runs fully in parallel)
|
|
759
|
-
conductor tap-on "Sign In" --device emulator-5554
|
|
760
|
-
conductor input-text "user@b.com" --device emulator-5554
|
|
761
|
-
conductor inspect --device emulator-5554
|
|
762
|
-
```
|
|
763
|
-
|
|
764
|
-
**Inspect all device sessions:**
|
|
765
|
-
```bash
|
|
766
|
-
conductor session --list
|
|
767
|
-
# Sessions:
|
|
768
|
-
# C59D3241-... appId=com.example.myapp deviceId=C59D3241-...
|
|
769
|
-
# emulator-5554 appId=com.example.myapp deviceId=emulator-5554
|
|
770
|
-
```
|
|
771
|
-
|
|
772
|
-
**Rules for multi-agent use:**
|
|
773
|
-
- Run `list-devices` first and assign devices before spawning agents — never let agents pick their own
|
|
774
|
-
- Each agent owns exactly one device; no two agents share a device
|
|
775
|
-
- Name agents `tester-ios-${device_name}` / `tester-android-${device_name}` so ownership is obvious
|
|
776
|
-
- Always pass `--device` on every command — this is how agents stay isolated
|
|
777
|
-
- Each device gets its own daemon (`~/.conductor/daemons/<deviceId>/`), keeping the driver process alive for the lifetime of the daemon
|
|
778
|
-
- Screenshot paths must be unique per device; use the device name in the path
|
|
779
|
-
|
|
780
|
-
---
|
|
781
|
-
|
|
782
|
-
## Output Modes
|
|
783
|
-
|
|
784
|
-
Output is human-readable by default:
|
|
785
|
-
```
|
|
786
|
-
✓ tap "Submit" — done
|
|
787
|
-
✗ assert-visible "Welcome" — element not found
|
|
788
|
-
```
|
|
789
|
-
|
|
790
|
-
Use `--json` only when you need structured output for parsing:
|
|
791
|
-
```json
|
|
792
|
-
{"status": "ok", "message": "tap \"Submit\" — done"}
|
|
793
|
-
{"status": "error", "message": "assert-visible \"Welcome\" — element not found"}
|
|
794
|
-
```
|
|
795
|
-
|
|
796
|
-
---
|
|
797
|
-
|
|
798
|
-
### `start-device`
|
|
799
|
-
|
|
800
|
-
Boot an iOS simulator or Android emulator. Creates the Simulator window and waits until the device is fully ready.
|
|
801
|
-
|
|
802
|
-
```bash
|
|
803
|
-
conductor start-device --platform ios
|
|
804
|
-
conductor start-device --platform android
|
|
805
|
-
conductor start-device --platform ios --os-version 18
|
|
806
|
-
conductor start-device --platform ios --device-type "iPhone 16 Pro"
|
|
807
|
-
conductor start-device --platform ios --name "Test Device"
|
|
808
|
-
conductor start-device --platform android --avd Pixel_6_API_33
|
|
809
|
-
```
|
|
810
|
-
|
|
811
|
-
Flags:
|
|
812
|
-
- `--platform <ios|android>` — required
|
|
813
|
-
- `--os-version <n>` — filter by OS version (iOS: e.g. `18`; Android: API level e.g. `33`)
|
|
814
|
-
- `--avd <name>` — Android AVD name to launch (default: first available AVD)
|
|
815
|
-
- `--name <name>` — set a custom name on the simulator after boot (iOS only)
|
|
816
|
-
- `--device-type <name>` — iOS device type to boot (e.g. `"iPhone 16 Pro"`); if no existing simulator matches, one is created automatically
|
|
817
|
-
|
|
818
|
-
iOS picks the first available iPhone simulator matching the OS version and device type filters (or any if unfiltered). Android launches the named AVD, or the first AVD found.
|
|
819
|
-
|
|
820
|
-
---
|
|
821
|
-
|
|
822
|
-
## Related Documentation
|
|
823
|
-
|
|
824
|
-
- [Conductor Flow Syntax](./references/flow-syntax.md)
|
|
825
|
-
|