@appetize/cli 0.29.0 → 0.29.1
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/CHANGELOG.md +6 -0
- package/cli.js +1 -1
- package/package.json +3 -3
- package/skills/appetize/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
package/cli.js
CHANGED
|
@@ -29,4 +29,4 @@ import at from"sade";import{p as I,n as C,a as f,b as x,U as r,l as de,c as M,d
|
|
|
29
29
|
`).flatMap(n=>pi(n,t)).join(`
|
|
30
30
|
`)}function pi(e,t){const n=[];let o="";for(const i of e.split(" ").flatMap(s=>ri(s,t)))o===""?o=i:o.length+1+i.length<=t?o=`${o} ${i}`:(n.push(o),o=i);return n.push(o),n}async function li(e,t={}){const n=await we(e,{signal:t.signal});return new Et(n,{...t,onClose:()=>{n.close(),t.onClose?.()}})}function di(e,t){if(e.controlSocket===void 0)throw new Error("Session is not ready");return li(e.controlSocket,{baseUrl:e.baseUrl,signal:t})}async function ui(e,t){if(e.controlSocket===void 0)throw new Error("Session is not ready");const n=await we(e.controlSocket,{signal:t}),{client:o}=Ot(n,{});return{close:()=>{n.close()},recordStart:async i=>await o.request("appetizeio.cli.record.start",i),recordStop:async()=>await o.request("appetizeio.cli.record.stop")}}function fi(e){return async({options:t,positionals:n,progress:o,signal:i})=>{const s=t["session-id"],a=At(typeof s=="string"?s:void 0);try{return await e({options:t,positionals:n,progress:o,record:a,signal:AbortSignal.any([i,me(a.pid)])})}catch(l){throw G.wrap(l,a.sessionId)}}}function mi(e){return async({options:t,positionals:n,progress:o,record:i,signal:s})=>{const a=await di(i,s);try{return await e({device:a,options:t,positionals:n,progress:o,signal:s})}finally{a.close()}}}function hi(e){return async({options:t,positionals:n,progress:o,record:i,signal:s})=>{const a=await ui(i,s);try{return await e({control:a,options:t,positionals:n,progress:o,signal:s})}finally{a.close()}}}const gi=["session-id",...Zt,...en,...sn,...mn,...Ln,...Hn,...Ro,...fo,...Oo,...Mo,...Ho],wi=["json","quiet"];function yi(e){const t=at("appetize").version(e).option("--json","Print the result as JSON").option("--quiet","Hide progress");return E(t,"session start <device>",n=>n.describe(["Start a device session and wait until it is ready","","Environment variables:","APPETIZE_API_TOKEN: Authorizes private APIs and targets; required with no target","APPETIZE_ENDPOINT: Appetize URL used when --endpoint is omitted"]).option("--device-android-shell","Android only: adb shell command run at start; needs --target").option("--device-appearance",`Device appearance: ${N.join(" or ")}`).option("--device-font-size",`Device font size: ${D.join(", ")}`).option("--device-ios-keyboards","iOS only: keyboard layout (e.g., en_US@sw=QWERTY); repeatable").option("--device-locale","Device locale as a BCP 47 tag (e.g., fr-CA)").option("--device-location","Device location as <lat>,<lng>; use = if latitude is negative").option("--device-orientation",`Initial orientation: ${L.join(", ")}`).option("--device-os-version","Device OS version (e.g., 17 for iOS, 14 for Android)").option("--device-timezone","Device IANA timezone (e.g., America/Toronto)").option("--endpoint","Appetize URL (e.g., https://appetize.io); overrides APPETIZE_ENDPOINT").option("--force","Replace a session with the same id",!1).option("--launch-app","App id to launch instead of the target app; needs --target").option("--launch-ios-args","iOS only: app launch argument, joined with = if it starts with -; repeatable; needs --target").option("--launch-params","App launch parameter as key=value, JSON when valid; repeatable; needs --target").option("--launch-permissions",`Grant app permissions: ${de.join(", ")}; needs --target`).option("--launch-url","URL opened once the session is ready").option("--no-device-android-soft-keyboard","Android only: hide the on-screen keyboard").option("--no-device-ios-autocorrect","iOS only: keep autocorrect off, its default").option("--no-launch-app","Skip launching the app; needs --target").option("--no-wait","Return without waiting for the device session to be ready").option("--proxy","Route traffic through a proxy: 'intercept' to capture it, or an http url").option("--session-id","Session id; defaults to a generated 3-word id").option("--target","App, build or app group id to run").example("session start iphone15pro --target app_a1b2c3").example("session start iphone15pro").example("session start pixel7 --target app_a1b2c3 --device-os-version 14").example("session start iphone15pro --target app_a1b2c3 --launch-url myapp://orders/42").example("session start iphone15pro --target app_a1b2c3 --launch-params debug=true --launch-params retries=3").example(`session start iphone15pro --target app_a1b2c3 --launch-params 'zip="90210"'`).example(`session start iphone15pro --target app_a1b2c3 --launch-params 'tags=["beta","qa"]'`).example("session start iphone15pro --device-orientation landscapeLeft --device-appearance dark").example("session start pixel7 --device-locale fr-CA --device-timezone America/Toronto").example("session start iphone15pro --target app_a1b2c3 --session-id demo --force"),vo,{fields:go}),le(t,"session stop",n=>n.describe("Stop a device session and await its exit").option("--no-wait","Return without waiting for the session to exit").example("session stop --no-wait"),Eo),p(t,"app clear",n=>n.describe("Wipe an app's data").option("--app","App id; defaults to every session app").example("app clear").example("app clear --app com.example.app"),Wt),p(t,"app launch",n=>n.describe(["Launch an app, or bring it to the foreground","","With --params or --ios-args, a running app is relaunched"]).option("--app","App id; defaults to the session's app").option("--ios-args","iOS only: launch argument, joined with = if it starts with -; repeatable").option("--params","Launch parameter as key=value, JSON when valid; repeatable").example("app launch").example("app launch --app com.apple.Preferences").example("app launch --params debug=true --params retries=3").example("app launch --ios-args=-debug --ios-args YES"),Vt),p(t,"app reinstall",n=>n.describe("Reinstall an app, then restart it").option("--app","Session app id; defaults to every session app").example("app reinstall").example("app reinstall --app com.example.app"),Ht),p(t,"app restart",n=>n.describe("Stop and relaunch an app").option("--app","App id; defaults to the last launched app").option("--ios-args","iOS only: launch argument, joined with = if it starts with -; repeatable").option("--params","Launch parameter as key=value, JSON when valid; repeatable").example("app restart").example("app restart --params debug=true"),Xt),p(t,"app terminate",n=>n.describe("Stop an app").option("--app","App id; defaults to every session app").example("app terminate").example("app terminate --app com.example.app"),Jt),p(t,"biometry authenticate",n=>n.describe("Answer the app's biometric prompt; on iOS, enroll with biometry enroll first").option("--no-match","Answer with a non-match").example("biometry authenticate").example("biometry authenticate --no-match"),Yt),p(t,"biometry enroll",n=>n.describe("Enroll biometry (iOS only)").option("--unenroll","Unenroll instead",!1).example("biometry enroll").example("biometry enroll --unenroll"),Qt),E(t,"build list",n=>n.describe(["List uploaded builds, printing session start targets","","Environment variables:","APPETIZE_API_TOKEN: Authorizes the listing request","APPETIZE_ENDPOINT: Appetize URL used when --endpoint is omitted"]).option("--app","App id to filter by (bundle or package id)").option("--endpoint","Appetize URL (e.g., https://appetize.io); overrides APPETIZE_ENDPOINT").option("--limit","Builds per page (default 10, max 200)").option("--page","Page of results to fetch (default 1)").example("build list").example("build list --app com.example.app").example("build list --app com.example.app --page 2"),on,{fields:nn}),E(t,"build upload <file>",n=>n.describe([`Upload a build (${H.join(", ")}) as a session start target`,"","Environment variables:","APPETIZE_API_TOKEN: Authorizes the upload","APPETIZE_ENDPOINT: Appetize URL used when --endpoint is omitted"]).option("--endpoint","Appetize URL (e.g., https://appetize.io); overrides APPETIZE_ENDPOINT").option("--note","Note to store with the build").option("--tags","Comma separated tags to store with the build").option("--timeout",`Milliseconds to wait with --wait (default ${String(Le)})`).option("--wait","Wait for the app id and version metadata to resolve").example("build upload ./app.apk").example("build upload ./app.zip").example("build upload ./app.apk --wait").example('build upload ./app.apk --tags beta,latest --note "PR 42"'),pn,{fields:cn}),E(t,"device list",n=>n.describe(["List device models and their supported OS versions","","Environment variables:","APPETIZE_ENDPOINT: Appetize URL used when --endpoint is omitted"]).option("--endpoint","Appetize URL (e.g., https://appetize.io); overrides APPETIZE_ENDPOINT").option("--platform","Platform to filter by: ios or android").example("device list").example("device list --platform ios"),gn,{fields:hn}),p(t,"fold <hinge>",n=>n.describe(`Fold a foldable device: ${K.join(", ")}`).example("fold closed").example("fold fullyOpen"),vn),p(t,"gesture <syntax>",n=>n.describe(["Play a single-pointer gesture",""," > press the pointer down"," / lift it"," {wait} pause 400 ms"," {wait=ms} pause ms"," {...} move the pointer:"," byText=Photo to the element with exactly that text"," byText=/photo/i to the element matching the regex",' byText="My photos" quote values with spaces'," byTestId=card to the element with that test id"," index=1 to the second match, from 0"," x=0 y=0 to a point in it; 0-1 spans it"," 0.5,0.6 to a screen position, each 0-1"," x=0.5 y=0.6 the same screen position"," duration=ms glide time while down; default 400","","Single-quote the gesture. A pointer still down at the end lifts.","Double taps need {wait=100} between taps; Android drops faster ones."]).option("--timeout","Milliseconds to wait for any elements").example("gesture '{byText=Photo}>/' # tap").example("gesture '{byText=Photo}>{wait=800}/' # long-press").example("gesture '{0.5,0.5}>/{wait=100}>/' # double tap").example("gesture '{byTestId=card}>{byTestId=drop-zone duration=600}/' # drag").example(`gesture '{byText="My photos" x=0 y=0}>{wait}/' # top-left corner`).example("gesture '{byText=/photo/i index=1}>{wait=800}/' # 2nd match").example("gesture '{byTestId=slider x=0}>{byTestId=slider x=1 duration=1000}/' # slide").example("gesture '{0.5,0.8}>{0.5,0.6}{0.8,0.6}{0.8,0.2}/' # path").example("gesture '{0.2,0.4}>{0.5,0.6 duration=300}/{0.2,0.7}>{0.8,0.7}/' # 2 strokes").example("gesture '{byTestId=card}>{wait=800}/' --timeout 5000 # wait for card"),bn),p(t,"inspect [output]",n=>ee(n.describe("Capture the view hierarchy of the screen, or of every element a selector matches")).option("--timeout","Milliseconds to wait for a match").option("--pretty","Pretty-print the JSON",!1).example("inspect").example("inspect --select-text 'Log in'").example("inspect hierarchy.json --pretty"),Dn),p(t,"keychain clear",n=>n.describe("Clear the entire device keychain (iOS only)").example("keychain clear"),zn),p(t,"media add <path>",n=>n.describe("Add a photo or video to the device's gallery").example("media add photo.png").example("media add ./clips/demo.mp4"),Rn),p(t,"open <url>",n=>n.describe("Open a URL or deep link on the device").example("open myapp://orders/42").example("open https://example.com"),Fn),p(t,"press <button>",n=>n.describe(`Press a device button: ${ke.join(", ")}; Android only: ${Ae.join(", ")}`).example("press home").example("press volumeUp"),Bn),it(t,"recording start <output>",n=>n.describe("Start recording the device video to an mp4 file").option("--force","Overwrite the file if it already exists",!1).example("recording start login-flow").example("recording start ./captures/checkout.mp4").example("recording start login-flow --force"),Gn),it(t,"recording stop",n=>n.describe("Stop recording the device video and close the file").example("recording stop"),Kn),p(t,"rotate <orientation>",n=>n.describe("Rotate the device to an orientation: portrait, landscapeLeft, landscapeRight or upsideDown").example("rotate portrait").example("rotate landscapeLeft"),Wn),p(t,"screenshot [output]",n=>ee(n.describe("Take a screenshot of the screen, or of one element")).option("--timeout","Milliseconds to wait for a match").example("screenshot").example("screenshot home-screen").example("screenshot ./shots/checkout.png").example("screenshot card --select-test-id card"),Xn),p(t,"settings set",n=>n.describe("Change device settings; pass at least one flag").option("--appearance",`Device appearance: ${N.join(" or ")}`).option("--font-size",`Device font size: ${D.join(", ")}`).option("--location","Device location as <lat>,<lng>").example("settings set --appearance dark").example("settings set --font-size xl --location 43.65,-79.38").example("settings set --location=-33.87,151.21"),Ao),p(t,"shake",n=>n.describe("Shake the device").example("shake"),ko),E(t,"skill install",n=>n.describe("Install the Appetize agent skill for coding agents").option("--agent","Agent to install for, repeatable; defaults to the agents detected").option("--scope","Install for the project (default) or the user").example("skill install").example("skill install --scope user").example("skill install --agent claude --agent cursor"),jo),p(t,"swipe",n=>te(te(n.describe(["Swipe in a direction, or along a path between two points","",`Pick a --direction with an optional --${w} starting point,`,`or both a --${w}-* and a --${y}-* point to form a path`,"",`Pick one per point: ${k({prefix:w})}`,` ${k({prefix:y})}`]).option("--direction",`Direction to travel: ${W.join(", ")}`).option("--distance","Travel as a fraction of the screen (0-1)"),{prefix:w}),{prefix:y}).option("--duration","Milliseconds the gesture travels for").option("--timeout",`Milliseconds to wait for any --${w}/--${y} elements`).example("swipe --direction up").example("swipe --direction up --distance 0.25").example("swipe --direction left --from-test-id card --from-index 1").example("swipe --from-position 0.5,0.75 --to-position 0.5,0.3").example("swipe --from-text 'Drag me' --from-y 0.9 --to-test-id drop-zone"),Bo),p(t,"tap",n=>te(n.describe(["Tap an element matched by a selector, or a screen position","",`Pick one of: ${k()}`])).option("--timeout","Milliseconds to wait for the element").example("tap --select-text 'Log in'").example("tap --select-test-id save").example("tap --select-text '/^Item \\d+/i' --select-index 2").example("tap --select-position 0.5,0.75").example("tap --select-test-id slider --select-x 0.9").example("tap --select-test-id dialog --select-x=-0.1").example("tap --select-test-id row --select-y=1.2"),Xo),p(t,"type <text>",n=>n.describe(`Type text; {...} presses a named key: ${$t.join(", ")}`).option("--delay","Milliseconds to wait between steps").example("type 'hello{Enter}'").example("type '{Backspace>16/}'").example("type 'user@example.com{Tab}hunter2{Enter}' --delay 50"),Jo),vi(t,$n)}function vi(e,t){const n=e.help.bind(e);return e.help=o=>{n(o),o||process.stdout.write(t)},e}function E(e,t,n,o,{fields:i}={}){const s=t.split(" ").filter(a=>!/^[<[]/.test(a)).join(" ");return n(e.command(t)).action(Si({action:o,fields:i,name:s}))}function le(e,t,n,o){return E(e,t,i=>n(i).option("--session-id","Target session id; defaults to the sole active session"),fi(o))}function p(e,t,n,o){return le(e,t,n,mi(o))}function it(e,t,n,o){return le(e,t,n,hi(o))}function st(e){return`${String(Date.now()-e)}ms`}function xi(e,t,n){if(e==="json")return JSON.stringify(t===void 0?{}:t,null,2);const o=t===void 0?"":ai(t,n,process.stdout.columns);return o===""?void 0:o}async function bi({action:e,fields:t=[],name:n},o){const i=Date.now();re(`invoked: ${process.argv.slice(2).join(" ")}`);const s=kt(o),{format:a,progress:l}=An(s.options,process.stdout,process.stderr),c=ei(l,process.stderr,s.signal);try{const u=xi(a,await e({...s,progress:c}),t);c.succeed(),u!==void 0&&process.stdout.write(`${u}
|
|
31
31
|
`),re(`ok in ${st(i)}`)}catch(u){c.fail(),process.stderr.write(`${Pt(a,u,{command:n})}
|
|
32
|
-
`),re(`failed after ${st(i)}:`,u),process.exitCode=1}}function Si(e){return(...t)=>{bi(e,t)}}yi("0.29.
|
|
32
|
+
`),re(`failed after ${st(i)}:`,u),process.exitCode=1}}function Si(e){return(...t)=>{bi(e,t)}}yi("0.29.1").parse(process.argv,{boolean:wi,string:gi});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appetize/cli",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.1",
|
|
4
4
|
"description": "Run, inspect and control iOS and Android apps on Appetize cloud devices",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"homepage": "https://appetize.io",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"debug": "^4.4.3",
|
|
33
33
|
"human-id": "^4.2.2",
|
|
34
34
|
"json-rpc-2.0": "^1.8.1",
|
|
35
|
-
"mediabunny": "^1.
|
|
35
|
+
"mediabunny": "^1.65.0",
|
|
36
36
|
"sade": "^1.8.1",
|
|
37
|
-
"ws": "^8.22.
|
|
37
|
+
"ws": "^8.22.1",
|
|
38
38
|
"yocto-spinner": "^1.2.2",
|
|
39
39
|
"zod": "^4.6.7"
|
|
40
40
|
}
|