@appetize/cli 0.18.0 → 0.20.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.
@@ -1,380 +1,96 @@
1
1
  # Command reference
2
2
 
3
- Every command, flag and output shape. `appetize <command> --help` prints the
4
- same flags at the terminal.
3
+ Behaviour that `appetize <command> --help` does not show. Run that for flags.
5
4
 
6
- - [Global](#global)
7
- - [session start](#session-start)
8
- - [session stop](#session-stop)
9
- - [build list](#build-list)
10
- - [build upload](#build-upload)
11
- - [device list](#device-list)
12
- - [inspect](#inspect)
13
- - [tap](#tap)
14
- - [swipe](#swipe)
15
- - [type](#type)
16
- - [press](#press)
17
- - [rotate](#rotate)
18
- - [open](#open)
19
- - [screenshot](#screenshot)
20
- - [recording start](#recording-start)
21
- - [recording stop](#recording-stop)
22
- - [skill install](#skill-install)
23
- - [Selectors](#selectors)
24
- - [Environment](#environment)
25
- - [Exit codes and output streams](#exit-codes-and-output-streams)
5
+ ## Output and exit codes
26
6
 
27
- ## Global
7
+ Results go to stdout and hold only what you could not know beforehand; `{}`
8
+ means the command ran. `APPETIZE_OUTPUT` (`json` or `text`) applies when neither
9
+ `--json` nor `--no-json` is passed.
28
10
 
29
- `--json` prints the result as JSON. End every command with it.
11
+ Failures exit non-zero and print `{ "error": { "message": "…" } }` to stderr,
12
+ plus `hints` when there is something to try, and `sessionDir` once a session is
13
+ found. An expired selector `--timeout` is a failure, which is what makes it a
14
+ wait.
30
15
 
31
- `--session-id <id>` is accepted by every command except those marked "Takes
32
- no session". On
33
- `session start` it names the session being created; everywhere else it picks
34
- which existing session to act on, defaulting to the sole active one, so pass it
35
- only when several are running.
16
+ Session directories live under `~/.appetize/cli/sessions/`, or
17
+ `$APPETIZE_CLI_HOME`.
36
18
 
37
19
  ## session start
38
20
 
39
- ```
40
- appetize session start <device-id> <target> [options]
41
- ```
42
-
43
- | Flag | Meaning |
44
- | --------------------- | ------------------------------------------------------ |
45
- | `--device-os-version` | Device OS version, e.g. `17` for iOS, `14` for Android |
46
- | `--endpoint` | Appetize URL; overrides `APPETIZE_ENDPOINT` |
47
- | `--no-wait` | Return without waiting for the device to be ready |
48
- | `--open` | URL opened once the session is ready |
49
- | `--proxy` | Proxy traffic: `intercept`, or a custom `http://` url |
50
- | `--session-id` | Name the session; defaults to a generated 3-word id |
51
-
52
- Spawns a daemon that holds the session open and prints JSON on stdout. Waiting
53
- (the default) prints
54
- `{ adbSerial, baseUrl, controlSocket, logs, sessionId, viewerUrl }`
55
- once the device is ready; `--no-wait` prints `{ logs, sessionId }` immediately.
56
- `adbSerial` is present only for an Android session, and only while adb is
57
- available to it: `adb connect <adbSerial>` attaches to the device until the
58
- session ends. `viewerUrl` opens the local browser viewer. `logs`
59
- carries a `device` and a `daemon` path, plus a `network` path — the JSONL capture
60
- of the app's traffic — when started with `--proxy` (or `--proxy intercept`). Progress phases are logged to stderr:
61
- `requesting`, `queued`, `starting`, `downloadingApp`, `installingApp`,
62
- `launchingApp`, `ready`.
63
-
64
- Network traffic can be proxied or intercepted for debugging. `--proxy` (or
65
- `--proxy intercept`) provisions the intercepting proxy so traffic can be
66
- captured; `--proxy http://host:port` routes through a proxy of your own, and the
67
- streaming server rejects any other scheme. Interception rewrites TLS, so an app
68
- that pins certificates may not work under it.
69
-
70
- `sessionId` is what every other command takes. A generated one looks like
71
- `tidy-pandas-jump`; a name you pass may use letters, digits, `.`, `-` and `_`,
72
- and starting a session with an id already in use fails.
21
+ - `--session-id` may use letters, digits, `.`, `-` and `_`. Reusing one fails
22
+ unless `--force` replaces that session.
23
+ - `--no-wait` returns just `sessionId` and `logs`, so you can work while the
24
+ device boots.
25
+ - `adbSerial` appears only while adb is available, and attaches until the
26
+ session ends.
27
+ - `--proxy http://host:port` routes through your own proxy; other schemes are
28
+ rejected. Interception rewrites TLS, so an app that pins certificates may
29
+ fail under it.
73
30
 
74
31
  ## session stop
75
32
 
76
- ```
77
- appetize session stop
78
- ```
79
-
80
- Signals the daemon to shut down and prints `{ ended: true, sessionId }`. Reports
81
- the id even if the daemon had already exited, so it is safe to call twice.
33
+ Waits for the daemon to exit, so logs are complete; `--no-wait` only signals
34
+ it. Safe to call twice.
82
35
 
83
36
  ## build list
84
37
 
85
- ```
86
- appetize build list [options]
87
- ```
88
-
89
- | Flag | Meaning |
90
- | ------------ | ------------------------------------------- |
91
- | `--app` | App id to filter by (bundle or package id) |
92
- | `--endpoint` | Appetize URL; overrides `APPETIZE_ENDPOINT` |
93
- | `--limit` | Builds per page (default 10, max 200) |
94
- | `--page` | Page of results to fetch (default 1) |
95
-
96
- Lists one page of the account's uploaded builds, newest first, printing
97
- `{ builds, nextPage, total }` on stdout. Each build's `id` is a target
98
- `session start` takes, alongside `appId`, `platform`, `versionName`,
99
- `buildNumber` and `created`. Pass a non-null `nextPage` back as `--page` to
100
- continue. Requires `APPETIZE_API_TOKEN`. Takes no session.
38
+ Newest first. Pass a non-null `nextPage` back as `--page` to continue.
101
39
 
102
40
  ## build upload
103
41
 
104
- ```
105
- appetize build upload <file> [options]
106
- ```
107
-
108
- | Flag | Meaning |
109
- | ------------ | --------------------------------------------------- |
110
- | `--endpoint` | Appetize URL; overrides `APPETIZE_ENDPOINT` |
111
- | `--note` | Note to store with the build |
112
- | `--tags` | Comma separated tags to store with the build |
113
- | `--timeout` | Milliseconds to wait with `--wait` (default 120000) |
114
- | `--wait` | Wait for the app id and version metadata to resolve |
115
-
116
- Uploads a `.apk`, `.apks`, `.zip` or `.tar.gz` and prints
117
- `{ created, id, platform }` on stdout, where `id` is a target `session start`
118
- takes. The id is also logged to stderr as soon as the upload lands.
119
-
120
- The extension decides the platform — `.apk` and `.apks` are Android, `.zip`,
121
- `.tar.gz` and `.tgz` are iOS — and any other file is refused before uploading.
122
-
123
- Appetize fills in `appId`, `versionName` and `buildNumber` by processing the
124
- file after the upload responds, which takes a few seconds and is not needed to
125
- start a session. `--wait` polls until they appear and prints the full
126
- `build list` shape, failing once `--timeout` elapses.
127
-
128
- Requires `APPETIZE_API_TOKEN`. Takes no session.
42
+ The extension decides the platform: `.apk` and `.apks` are Android, `.zip`,
43
+ `.tar.gz` and `.tgz` are iOS. `appId`, `versionName` and `buildNumber` resolve a
44
+ few seconds after upload and are not needed to start a session; `--wait` polls
45
+ for them.
129
46
 
130
47
  ## device list
131
48
 
132
- ```
133
- appetize device list [options]
134
- ```
135
-
136
- | Flag | Meaning |
137
- | ------------ | ------------------------------------------- |
138
- | `--endpoint` | Appetize URL; overrides `APPETIZE_ENDPOINT` |
139
- | `--platform` | Platform to filter by: `ios` or `android` |
140
-
141
- Lists available device models, printing `{ devices }` on stdout. Each device's
142
- `id` is the `<device-id>` `session start` takes, alongside `name`, `platform`
143
- and `osVersions` — the values `--device-os-version` accepts. Needs no
144
- `APPETIZE_API_TOKEN`. Takes no session.
49
+ Needs no `APPETIZE_API_TOKEN`. `osVersions` are the values
50
+ `--device-os-version` accepts.
145
51
 
146
52
  ## inspect
147
53
 
148
- ```
149
- appetize inspect [output] [options]
150
- ```
151
-
152
- | Flag | Meaning |
153
- | ----------------------------------------------------- | -------------------------------------------------- |
154
- | `--select-text`, `--select-test-id`, `--select-index` | Scope to one element — see [Selectors](#selectors) |
155
- | `--timeout` | Milliseconds to wait for the element |
156
- | `--pretty` | Indent the JSON |
157
-
158
- `output` defaults to `inspect.json`; `.json` is added if you omit an extension.
159
- Writes the hierarchy to that file and prints `{ bytes, nodes, output }` on stdout.
160
- The JSON is `{ platform, root }`, where each node carries `attributes`, `bounds`
161
- and `children`. Only currently visible elements appear.
162
-
163
- Unscoped output on an ordinary screen exceeds 100 KB, so prefer a selector and
164
- read the file.
165
-
166
- ## tap
167
-
168
- ```
169
- appetize tap [options]
170
- ```
171
-
172
- | Flag | Meaning |
173
- | ------------ | -------------------------------------------------------- |
174
- | `--select-*` | Element or position to tap — see [Selectors](#selectors) |
175
- | `--timeout` | Milliseconds to wait for the element |
176
-
177
- Prints `{ tapped: true }`. Combining an element selector with `--select-x` /
178
- `--select-y` taps a point within that element, and values outside `0-1` are
179
- allowed to reach just beyond its edge (e.g. `--select-x=-0.1`).
180
-
181
- ## swipe
182
-
183
- ```
184
- appetize swipe [options]
185
- ```
186
-
187
- | Flag | Meaning |
188
- | ------------- | ------------------------------------------------------ |
189
- | `--direction` | `down`, `left`, `right` or `up` |
190
- | `--distance` | Travel as a fraction of the screen (0-1) |
191
- | `--from-*` | Where the gesture starts — see [Selectors](#selectors) |
192
- | `--to-*` | Where the gesture ends — see [Selectors](#selectors) |
193
- | `--duration` | Milliseconds the gesture travels for |
194
- | `--timeout` | Milliseconds to wait for any `--from`/`--to` elements |
195
-
196
- Prints `{ swiped: true }`. Two shapes are valid: a `--direction` with an
197
- optional `--from-*` starting point, or both a `--from-*` and a `--to-*` point to
198
- form a path. Mixing `--direction` with `--to-*` is an error, as is `--distance`
199
- without `--direction`.
54
+ Writes `{ platform, root }`, where each node carries `attributes`, `bounds` and
55
+ `children`. `.json` is added if you omit an extension.
200
56
 
201
57
  ## type
202
58
 
203
- ```
204
- appetize type <text> [options]
205
- ```
206
-
207
- | Flag | Meaning |
208
- | --------- | ---------------------------------- |
209
- | `--delay` | Milliseconds to wait between steps |
210
-
211
- Prints `{ typed: true }`. Bare text is delivered as a string; `{...}` names a
212
- key delivered as a keystroke. Supported keys: `ArrowDown`, `ArrowLeft`,
213
- `ArrowRight`, `ArrowUp`, `Backspace`, `Enter`, `Tab`. A token may carry a repeat
214
- count as in `{Backspace>16/}`, and `{{` types a literal `{`. An unclosed token
215
- or an unsupported key name fails without typing anything.
216
-
217
- Typing does not focus a field — tap it first.
218
-
219
- ## press
220
-
221
- ```
222
- appetize press <button>
223
- ```
224
-
225
- Prints `{ pressed: true }`. Buttons: `home` and `lock` on both platforms;
226
- `back`, `menu`, `unlock`, `volumeUp` and `volumeDown` on Android only. Calling
227
- an Android-only button on iOS fails.
228
-
229
- There is no keyboard-dismiss action. To dismiss the soft keyboard, see the
230
- main page.
231
-
232
- ## rotate
233
-
234
- ```
235
- appetize rotate <orientation>
236
- ```
237
-
238
- Prints the resulting orientation. Orientations: `portrait`, `landscapeLeft`,
239
- `landscapeRight`, `upsideDown`.
59
+ Typing does not focus a field — tap it first. `{Backspace>16/}` repeats a key
60
+ and `{{` types a literal `{`. An unclosed token or unknown key fails without
61
+ typing anything.
240
62
 
241
63
  ## open
242
64
 
243
- ```
244
- appetize open <url>
245
- ```
246
-
247
- Prints `{ opened: true }`. `<url>` is a deep link (`myapp://orders/42`) or an
248
- http(s) URL. A malformed or scheme-less value is refused.
249
-
250
- ## screenshot
251
-
252
- ```
253
- appetize screenshot [output]
254
- ```
255
-
256
- `output` defaults to `screenshot`, and an extension matching the image is added
257
- if you omit one. Writes a PNG and prints `{ bytes, mimeType, output }`.
65
+ A scheme-less value is refused. iOS caps URLs at 2048 characters.
258
66
 
259
67
  ## recording start
260
68
 
261
- ```
262
- appetize recording start <output> [options]
263
- ```
264
-
265
- | Flag | Meaning |
266
- | --------- | --------------------------------------- |
267
- | `--force` | Overwrite the file if it already exists |
268
-
269
- Asks the daemon to open the device video stream and capture it to `output`,
270
- printing `{ path, recording: true }`. Capture is opt-in — video is lazy
271
- device-side, so nothing streams until this runs.
272
-
273
- `output` is required. `.mp4` is added if you omit an extension, any other
274
- extension is refused, and the directory must already exist. A relative path
275
- resolves against your shell rather than the daemon's.
276
-
277
- Starting while a recording is already running fails, whatever path you name —
278
- adopting it would hand you a file that began whenever the first call did.
279
-
280
- The daemon owns the stream and the file, so the command returns as soon as
281
- capture is running.
69
+ `.mp4` is added if you omit an extension, any other extension is refused, and
70
+ the directory must already exist. Starting while a recording runs fails, as
71
+ does an existing file without `--force`.
282
72
 
283
73
  ## recording stop
284
74
 
285
- ```
286
- appetize recording stop
287
- ```
288
-
289
- Closes the stream and the file, printing `{ path, recording: false }`. Fails
290
- with `No video recording in progress` when nothing is recording. Ending the
291
- session while recording closes the file too, noting it in `daemon.log`.
292
-
293
- A session that dies unexpectedly still leaves the file playable, losing at most
294
- the last second.
295
-
296
- Every frame is written at a constant 30fps, so playback does not track real
297
- time. A rotation keeps the original dimensions and is noted in `daemon.log`.
298
-
299
- Recording over an existing file fails unless you pass `--force`, so name a new
300
- file to record more than once in a session.
301
-
302
- `output` is resolved and checked by the CLI, so a bad path fails before the
303
- daemon is contacted.
75
+ Fails when nothing is recording. Ending the session closes the file too, and a
76
+ session that dies unexpectedly still leaves it playable, losing at most the
77
+ last second. A write failure is reported here, not when it happens.
304
78
 
305
- A write failure is reported once the recording stops, not when it happens.
79
+ Playback is a constant 30fps, so it does not track real time. A rotation keeps
80
+ the original dimensions.
306
81
 
307
82
  ## skill install
308
83
 
309
- ```
310
- appetize skill install [options]
311
- ```
312
-
313
- | Flag | Meaning |
314
- | --------- | -------------------------------------------------------------- |
315
- | `--agent` | `agents`, `claude`, `codex`, `copilot` or `cursor`; repeatable |
316
- | `--scope` | `project` (default), or `user` for your home directory |
317
-
318
- Installs this skill for each agent whose directory is already present — Claude
319
- Code reads `.claude/skills`, the others `.agents/skills` — and into both when
320
- none is found. `--agent` names agents directly instead. Takes no session.
84
+ Installs into each agent directory already present — `.claude/skills` for
85
+ Claude Code, `.agents/skills` for the others — and into both when none is.
321
86
 
322
87
  ## Selectors
323
88
 
324
- Selector flags come as a prefix naming which point is being selected, plus a
325
- suffix naming how. The prefixes:
326
-
327
- - `inspect --select-*`
328
- - `tap --select-*`
329
- - `swipe --from-*` and `swipe --to-*`
330
-
331
- Element suffixes, identical under every prefix:
332
-
333
- | Flag | Meaning |
334
- | -------------------- | --------------------------------------------------------------------------------------------------------------------- |
335
- | `--<prefix>-text` | Exact string, or `/regex/flags` such as `/sign in/i` |
336
- | `--<prefix>-test-id` | Exact match — on Android the whole `resource-id` or the part after its last `/`, on iOS the `accessibilityIdentifier` |
337
- | `--<prefix>-index` | Which match when several, 0-based |
338
-
339
- So `inspect --select-text 'Log in'`, `tap --select-test-id save` and
340
- `swipe --from-test-id card --to-test-id bin` all name elements the same way.
341
-
342
- Position suffixes, accepted by `tap` and `swipe` (not `inspect`):
343
-
344
- | Flag | Meaning |
345
- | ------------------------------ | -------------------------------------------------------------------------------------------- |
346
- | `--<prefix>-position` | Screen position as `x,y`, each 0-1, e.g. `0.5,0.75` |
347
- | `--<prefix>-x`, `--<prefix>-y` | Position within the matched element (0-1, negative/>1 to reach outside the selected element) |
348
-
349
- Pick one kind of match per point. Prefer a test id, then text; reach for a
350
- position when the target has no identity of its own, such as a canvas or a
351
- slider track.
352
-
353
- ## Environment
354
-
355
- | Variable | Meaning |
356
- | -------------------- | ---------------------------------------------- |
357
- | `APPETIZE_API_TOKEN` | Authorizes private APIs and session targets |
358
- | `APPETIZE_ENDPOINT` | Appetize URL used when `--endpoint` is omitted |
359
- | `APPETIZE_CLI_HOME` | Overrides the session state directory |
360
-
361
- Session state is written to `~/.appetize/cli/sessions/<session-id>/state.json`.
362
- Device logs stream as JSONL to `device.log.jsonl` beside it, and the daemon's
363
- own output goes to `daemon.log`.
364
-
365
- ## Exit codes and output streams
366
-
367
- Commands exit `0` on success and non-zero on failure, printing the message to
368
- stderr. A selector `--timeout` that expires is a failure, which is what makes it
369
- usable as a wait.
370
-
371
- Machine-readable results go to stdout as JSON. Progress and errors are always
372
- stderr.
373
-
374
- Common failures:
89
+ `inspect --select-*`, `tap --select-*` and `swipe --from-*`/`--to-*` share the
90
+ same suffixes. `-text` is an exact string or `/regex/flags`; `-index` is
91
+ 0-based. `-test-id` is an exact match: on Android the whole `resource-id` or the
92
+ part after its last `/`, on iOS the `accessibilityIdentifier`.
375
93
 
376
- | Message | What to check |
377
- | --------------------------- | --------------------------------------------------------------------------------- |
378
- | `No active sessions` | `session start` first; if one should be live, its `daemon.log` says why it exited |
379
- | `No session found for id X` | `endedReason` in the session's `state.json` |
380
- | `Session is not ready` | `state.phase` in `state.json` says how far it got |
94
+ Only `tap` and `swipe` take positions. `-position` is a screen `x,y`, each 0-1;
95
+ `-x` and `-y` place the point within the matched element, beyond its edge when
96
+ outside 0-1.