@appetize/cli 0.15.0 → 0.15.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/README.md +1 -1
- package/cli.js +1 -1
- package/package.json +1 -1
- package/skills/appetize/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -7,4 +7,4 @@ npm install -g @appetize/cli
|
|
|
7
7
|
appetize --help
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
Release notes are in [CHANGELOG
|
|
10
|
+
Release notes are in the [CHANGELOG](https://cdn.jsdelivr.net/npm/@appetize/cli/CHANGELOG.md), also included in the published package.
|
package/cli.js
CHANGED
|
@@ -16,4 +16,4 @@ import Se from"sade";import{d as U,D as ve,p as m,a as p,f as $,b as xe,i as Ee,
|
|
|
16
16
|
`)}function eo(e){const t=v(e,"--agent");if(!Xt(t))throw new Error(`--agent expects one of ${R.join(", ")}`);return t}function to(e){return e===void 0?void 0:(Array.isArray(e)?e:[e]).map(o=>eo(o))}function oo(e){if(e===void 0)return"project";const t=v(e,"--scope");if(!Zt(t))throw new Error(`--scope expects one of ${de.join(", ")}`);return t}const l="from",u="to",no=[...L({prefix:l}),...L({prefix:u})],io=async({device:e,options:t})=>{await e.swipe(co(t),lo(t)),process.stdout.write(`${JSON.stringify({swiped:!0},null,2)}
|
|
17
17
|
`)};function so(e){return I.some(t=>t===e)}function ro(e){if(e===void 0)return;const t=v(e,"--direction");if(!so(t))throw new Error(`--direction expects one of ${I.join(", ")}`);return t}function ao(e,t){const o=Te(e.distance,"--distance");if(E(e,{prefix:u})!==void 0)throw new Error(`--direction may not be combined with --${u}-*`);return{direction:t,distance:o,from:E(e,{prefix:l})}}function co(e){const t=ro(e.direction);return t===void 0?po(e):ao(e,t)}function po(e){if(e.distance!==void 0)throw new Error("--distance is only valid with --direction");return{from:me(E(e,{prefix:l}),l),to:me(E(e,{prefix:u}),u)}}function lo(e){const t=p(e.duration,"--duration"),o=p(e.timeout,"--timeout");return t===void 0&&o===void 0?void 0:{duration:t,timeout:o}}function me(e,t){if(e===void 0)throw new Error(`A swipe needs --direction, or both endpoints: pass one of ${S({prefix:t})}`);return e}const uo=L(),fo=async({device:e,options:t})=>{const o=te(t);await e.tap(o,ee(t)),process.stdout.write(`${JSON.stringify({tapped:!0},null,2)}
|
|
18
18
|
`)},mo=async({device:e,options:t,positionals:[o]})=>{await e.type(o,wo(t)),process.stdout.write(`${JSON.stringify({typed:!0},null,2)}
|
|
19
|
-
`)};function wo(e){const t=p(e.delay,"--delay");return t===void 0?void 0:{delay:t}}const z=Ze("appetize:cli");function ho(e,t){if(e.controlSocket===void 0)throw new Error("Session is not ready");return Ve(e.controlSocket,{baseUrl:e.baseUrl,signal:t})}async function go(e,t){if(e.controlSocket===void 0)throw new Error("Session is not ready");const o=await U(e.controlSocket,{signal:t}),{client:n}=De(o,{});return{close:()=>{o.close()},recordStart:async i=>await n.request("appetizeio.cli.record.start",i),recordStop:async()=>await n.request("appetizeio.cli.record.stop")}}function yo(e){return async(...t)=>{const{options:o,positionals:n}=Eo(t),i=o["session-id"],s=ke(typeof i=="string"?i:void 0),a=AbortSignal.any([J(),Le(s.pid)]);await e({options:o,positionals:n,record:s,signal:a})}}function So(e){return async({options:t,positionals:o,record:n,signal:i})=>{const s=await ho(n,i);try{await e({device:s,options:t,positionals:o,signal:i})}finally{s.close()}}}function vo(e){return async({options:t,positionals:o,record:n,signal:i})=>{const s=await go(n,i);try{await e({control:s,options:t,positionals:o,signal:i})}finally{s.close()}}}function xo(e){return typeof e=="object"&&e!==null}function Eo(e){const t=e.at(-1);return{options:xo(t)?t:{},positionals:e.slice(0,-1).filter(o=>typeof o=="string")}}const bo=["session-id",...We,...Qe,...ct,...wt,...Yt,...Rt,...no,...uo];function Oo(e){const t=Se("appetize").version(e);return g(t.command("session start <device-id> <target>").describe(["Start a device session and wait until it is ready","","Environment variables:","APPETIZE_API_TOKEN: Authorizes private APIs and session targets","APPETIZE_ENDPOINT: Appetize URL used when --endpoint is omitted"]).option("--device-os-version","Device OS version (e.g., 17 for iOS, 14 for Android)").option("--endpoint","Appetize URL (e.g., https://appetize.io); overrides APPETIZE_ENDPOINT").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").example("session start iphone15pro app_a1b2c3").example("session start pixel7 app_a1b2c3 --device-os-version 14"),jt),F(t.command("session stop").describe("Stop a running device session"),Mt),g(t.command("build list").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"),Ye),g(t.command("build upload <file>").describe([`Upload a build (${P.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(H)})`).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"'),ot),g(t.command("device list").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"),pt),f(t.command("press <button>").describe(`Press a device button: ${Z.join(", ")}; Android only: ${X.join(", ")}`).example("press home").example("press volumeUp"),vt),f(t.command("rotate <orientation>").describe("Rotate the device to an orientation: portrait, landscapeLeft, landscapeRight or upsideDown").example("rotate portrait").example("rotate landscapeLeft"),$t),f(t.command("screenshot [output]").describe("Take a screenshot of the current device screen").example("screenshot").example("screenshot home-screen").example("screenshot ./shots/checkout.png"),Pt),f(k(t.command("tap").describe(["Tap an element matched by a selector, or a screen position","",`Pick one of: ${S()}`])).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"),fo),f(k(k(t.command("swipe").describe(["Swipe in a direction, or along a path between two points","",`Pick a --direction with an optional --${l} starting point,`,`or both a --${l}-* and a --${u}-* point to form a path`,"",`Pick one per point: ${S({prefix:l})}`,` ${S({prefix:u})}`]).option("--direction",`Direction to travel: ${I.join(", ")}`).option("--distance","Travel as a fraction of the screen (0-1)"),{prefix:l}),{prefix:u}).option("--duration","Milliseconds the gesture travels for").option("--timeout",`Milliseconds to wait for any --${l}/--${u} 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"),io),f(Y(t.command("inspect [output]").describe("Capture the view hierarchy of the screen, or of an element matched by a selector")).option("--timeout","Milliseconds to wait for the element").option("--pretty","Pretty-print the JSON",!1).example("inspect").example("inspect --select-text 'Log in'").example("inspect hierarchy.json --pretty"),ht),f(t.command("type <text>").describe(`Type text; {...} presses a named key: ${_e.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"),mo),we(t.command("recording start <output>").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"),Et),we(t.command("recording stop").describe("Stop recording the device video and close the file").example("recording stop"),bt),g(t.command("skill install").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"),Qt),t}function g(e,t){return e.action(Io(t))}function F(e,t){return g(e.option("--session-id","Target session id; defaults to the sole active session"),yo(t))}function f(e,t){return F(e,So(t))}function we(e,t){return F(e,vo(t))}function he(e){return`${String(Date.now()-e)}ms`}async function $o(e,...t){const o=Date.now();z(`invoked: ${process.argv.slice(2).join(" ")}`);try{await e(...t),z(`ok in ${he(o)}`)}catch(n){const i=n instanceof Error?n.message:String(n);console.error(i),z(`failed after ${he(o)}:`,n),process.exitCode=1}}function Io(e){return(...t)=>{$o(e,...t)}}Re(),Oo("0.15.
|
|
19
|
+
`)};function wo(e){const t=p(e.delay,"--delay");return t===void 0?void 0:{delay:t}}const z=Ze("appetize:cli");function ho(e,t){if(e.controlSocket===void 0)throw new Error("Session is not ready");return Ve(e.controlSocket,{baseUrl:e.baseUrl,signal:t})}async function go(e,t){if(e.controlSocket===void 0)throw new Error("Session is not ready");const o=await U(e.controlSocket,{signal:t}),{client:n}=De(o,{});return{close:()=>{o.close()},recordStart:async i=>await n.request("appetizeio.cli.record.start",i),recordStop:async()=>await n.request("appetizeio.cli.record.stop")}}function yo(e){return async(...t)=>{const{options:o,positionals:n}=Eo(t),i=o["session-id"],s=ke(typeof i=="string"?i:void 0),a=AbortSignal.any([J(),Le(s.pid)]);await e({options:o,positionals:n,record:s,signal:a})}}function So(e){return async({options:t,positionals:o,record:n,signal:i})=>{const s=await ho(n,i);try{await e({device:s,options:t,positionals:o,signal:i})}finally{s.close()}}}function vo(e){return async({options:t,positionals:o,record:n,signal:i})=>{const s=await go(n,i);try{await e({control:s,options:t,positionals:o,signal:i})}finally{s.close()}}}function xo(e){return typeof e=="object"&&e!==null}function Eo(e){const t=e.at(-1);return{options:xo(t)?t:{},positionals:e.slice(0,-1).filter(o=>typeof o=="string")}}const bo=["session-id",...We,...Qe,...ct,...wt,...Yt,...Rt,...no,...uo];function Oo(e){const t=Se("appetize").version(e);return g(t.command("session start <device-id> <target>").describe(["Start a device session and wait until it is ready","","Environment variables:","APPETIZE_API_TOKEN: Authorizes private APIs and session targets","APPETIZE_ENDPOINT: Appetize URL used when --endpoint is omitted"]).option("--device-os-version","Device OS version (e.g., 17 for iOS, 14 for Android)").option("--endpoint","Appetize URL (e.g., https://appetize.io); overrides APPETIZE_ENDPOINT").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").example("session start iphone15pro app_a1b2c3").example("session start pixel7 app_a1b2c3 --device-os-version 14"),jt),F(t.command("session stop").describe("Stop a running device session"),Mt),g(t.command("build list").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"),Ye),g(t.command("build upload <file>").describe([`Upload a build (${P.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(H)})`).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"'),ot),g(t.command("device list").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"),pt),f(t.command("press <button>").describe(`Press a device button: ${Z.join(", ")}; Android only: ${X.join(", ")}`).example("press home").example("press volumeUp"),vt),f(t.command("rotate <orientation>").describe("Rotate the device to an orientation: portrait, landscapeLeft, landscapeRight or upsideDown").example("rotate portrait").example("rotate landscapeLeft"),$t),f(t.command("screenshot [output]").describe("Take a screenshot of the current device screen").example("screenshot").example("screenshot home-screen").example("screenshot ./shots/checkout.png"),Pt),f(k(t.command("tap").describe(["Tap an element matched by a selector, or a screen position","",`Pick one of: ${S()}`])).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"),fo),f(k(k(t.command("swipe").describe(["Swipe in a direction, or along a path between two points","",`Pick a --direction with an optional --${l} starting point,`,`or both a --${l}-* and a --${u}-* point to form a path`,"",`Pick one per point: ${S({prefix:l})}`,` ${S({prefix:u})}`]).option("--direction",`Direction to travel: ${I.join(", ")}`).option("--distance","Travel as a fraction of the screen (0-1)"),{prefix:l}),{prefix:u}).option("--duration","Milliseconds the gesture travels for").option("--timeout",`Milliseconds to wait for any --${l}/--${u} 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"),io),f(Y(t.command("inspect [output]").describe("Capture the view hierarchy of the screen, or of an element matched by a selector")).option("--timeout","Milliseconds to wait for the element").option("--pretty","Pretty-print the JSON",!1).example("inspect").example("inspect --select-text 'Log in'").example("inspect hierarchy.json --pretty"),ht),f(t.command("type <text>").describe(`Type text; {...} presses a named key: ${_e.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"),mo),we(t.command("recording start <output>").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"),Et),we(t.command("recording stop").describe("Stop recording the device video and close the file").example("recording stop"),bt),g(t.command("skill install").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"),Qt),t}function g(e,t){return e.action(Io(t))}function F(e,t){return g(e.option("--session-id","Target session id; defaults to the sole active session"),yo(t))}function f(e,t){return F(e,So(t))}function we(e,t){return F(e,vo(t))}function he(e){return`${String(Date.now()-e)}ms`}async function $o(e,...t){const o=Date.now();z(`invoked: ${process.argv.slice(2).join(" ")}`);try{await e(...t),z(`ok in ${he(o)}`)}catch(n){const i=n instanceof Error?n.message:String(n);console.error(i),z(`failed after ${he(o)}:`,n),process.exitCode=1}}function Io(e){return(...t)=>{$o(e,...t)}}Re(),Oo("0.15.1").parse(process.argv,{string:bo});
|
package/package.json
CHANGED