@appetize/cli 0.27.0 → 0.28.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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @appetize/cli
2
2
 
3
+ ## 0.28.0
4
+
5
+ ### Minor Changes
6
+
7
+ - feat(screenshot): `--select-*` captures only the matched element
8
+
9
+ ### Patch Changes
10
+
11
+ - docs: describe element and position selectors in `--help`
12
+
3
13
  ## 0.27.0
4
14
 
5
15
  ### Minor Changes
package/cli.js CHANGED
@@ -1,12 +1,32 @@
1
1
  #!/usr/bin/env node
2
- import Qe from"sade";import{p as U,a as f,b as h,f as j,U as a,d as F,c as et,i as tt,e as nt,g as ot,r as re,h as I,j as $,k as it,D as st,s as rt,l as at,m as ae,n as ce,S as C,o as M,q as B,t as b,u as T,v as D,w as pe,x as ct,y as pt,z as le,A as lt,B as de,C as dt,E as ut,F as ft,G as mt,H as ue,I as fe,J as ht,K as gt,L as wt,M as yt,N as vt,O as bt}from"./stream-Cbdd68uZ.js";import{existsSync as me,openAsBlob as xt,openSync as St,closeSync as Et}from"node:fs";import{basename as q,extname as Ot,resolve as he,dirname as At,join as x,relative as Pt}from"node:path";import{writeFile as ge,stat as kt,rm as It,cp as $t}from"node:fs/promises";import{spawn as Tt}from"node:child_process";import{fileURLToPath as we}from"node:url";import{humanId as Dt}from"human-id";import{homedir as Lt}from"node:os";import Nt from"debug";import _t from"yocto-spinner";import zt from"cli-table3";import"json-rpc-2.0";import"zod/mini";import"stream";import"module";import"node:net";const G=["closed","fullyOpen","partiallyOpen"],ye=["back","menu","unlock","volumeDown","volumeUp"],ve=["home","lock"],be=[...ve,...ye],K=["down","left","right","up"],Rt=async({device:e,options:t})=>{await e.authenticateBiometry({match:t.match!==!1})},Ut=async({device:e,options:t})=>{const n=U(t.unenroll)??!1;await e.settings.setBiometryEnrolled(!n)},xe=[{header:"Target",key:"id"},{header:"App",key:"appId"},{header:"Platform",key:"platform"},{header:"Version",key:"versionName"},{header:"Build",key:"buildNumber"},{header:"Uploaded",key:"created"}];function Z({appId:e,buildNumber:t,created:n,id:o,platform:i,versionName:s}){return{appId:e,buildNumber:t,created:n,id:o,platform:i,versionName:s}}const jt=["app"],Ft=10,Ct=[{fields:xe,key:"builds"},{header:"Total",key:"total"},{header:"Next page",key:"nextPage"}];async function Mt({options:e,signal:t}){const n=f(e.app,"--app"),o=h(e.limit,"--limit")??Ft,i=h(e.page,"--page"),s={limit:String(o)};n!==void 0&&(s.appId=n),i!==void 0&&(s.page=String(i));const r=await j("/api/v2/builds",{commandOptions:e,query:s,signal:t});return{builds:r.builds.map(Z),nextPage:r.nextPage,total:r.total}}const Bt=["note","tags"],Se=12e4,qt=2e3,V=[".apk",".apks",".zip",".tar.gz",".tgz"],Gt={".apk":"android",".apks":"android",".tar.gz":"ios",".tgz":"ios",".zip":"ios"},Kt=xe;async function Zt({options:e,positionals:[t],progress:n,signal:o}){const i=Xt(t),s=q(t);n.status(`Uploading ${s}`);const r=await Ht(t,i,e,o);if(e.wait!==!0)return n.status(`Uploaded ${s}`),Z(r);n.step(`Uploaded ${s}`),n.status("Waiting for build metadata");const p=await Vt(r.id,e,o);return n.status(`Processed ${s}`),Z(p)}async function Vt(e,t,n){const o=h(t.timeout,"--timeout")??Se,i=Date.now()+o;for(;;){const s=await j(`/api/v2/builds/${e}`,{commandOptions:t,signal:n});if(s.appId!==void 0)return s;if(Date.now()>=i)throw new Error(`Build ${e} is still being processed after ${String(o)}ms; find it with 'appetize build list'`);await F(qt,n)}}async function Wt(e){if(!me(e))throw new Error(`No build file at ${e}`);return await xt(e)}function Xt(e){const t=e.toLowerCase(),n=V.find(o=>t.endsWith(o));if(n===void 0)throw new a(`Cannot tell the platform of ${q(e)}; upload a ${V.join(", ")} file`);return Gt[n]}async function Ht(e,t,n,o){const i=new FormData;i.set("platform",t);const s=f(n.note,"--note");s!==void 0&&i.set("note",s);const r=f(n.tags,"--tags");return r!==void 0&&i.set("tags",r),i.set("file",await Wt(e),q(e)),await j("/api/v2/builds",{body:i,commandOptions:n,method:"POST",signal:o})}const Jt=["platform"],Yt=[{fields:[{header:"ID",key:"id"},{header:"Name",key:"name"},{header:"Platform",key:"platform"},{header:"OS versions",key:"osVersions"}],key:"devices"}];async function Qt({options:e,signal:t}){const n=en(e.platform);return{devices:(await et(e,t)).filter(i=>n===void 0||i.platform===n).map(tn)}}function en(e){const t=f(e,"--platform");if(t!==void 0){if(!tt(t))throw new a(`--platform expects one of: ${nt.join(", ")}`);return t}}function tn({id:e,name:t,osVersions:n,platform:o}){return{id:e,name:t,osVersions:n,platform:o}}const nn="cli-fold",on=async({device:e,positionals:[t]})=>{if(!sn(t))throw new a(`Hinge must be one of: ${G.join(", ")}`);await e.fold(t)};function sn(e){return G.some(t=>t===e)}const rn=async({device:e,options:t,positionals:[n]})=>{await e.gesture(cn(n),an(t))};function an(e){const t=h(e.timeout,"--timeout");return t===void 0?void 0:{timeout:t}}function cn(e){let t;try{t=ot(e)}catch(n){throw n instanceof SyntaxError?new a(n.message):n}if(t.length===0)throw new a("Expected at least one gesture step");return t}const pn="APPETIZE_OUTPUT";function ln(e,t,n){return{format:dn(e,t),progress:un(e,n)}}function W(e,{extension:t,strict:n=!1}){const o=Ot(e);if(o==="")return`${e}${t}`;if(n&&o!==t)throw new a(`The output must be a path ending in ${t}`);return e}function dn(e,t){if(e.json===!0)return"json";if(e.json===!1)return"text";const n=process.env[pn]?.trim();return n==="json"||n==="text"?n:t.isTTY===!0?"text":"json"}function un(e,t){return e.quiet===!0?"silent":t.isTTY===!0&&process.env.TERM!=="dumb"&&!("CI"in process.env)?"interactive":"silent"}const S=["text","testId"],g="select",O={testId:{help:"Test id to match by exact string",suffix:"test-id"},text:{help:'Text to match by exact string or regex (e.g., "Sign in" or "/sign in/i")',suffix:"text"}},X="index",L="position",H="x",J="y";function Ee(e,{prefix:t=g}={}){return S.reduce((n,o)=>{const{help:i,suffix:s}=O[o];return n.option(`--${l(t,s)}`,i)},e).option(`--${l(t,X)}`,"Which match when multiple (0-based)")}function Y(e,t={}){const{prefix:n=g}=t;return Ee(e,t).option(`--${l(n,L)}`,"Screen position to use as x,y (0-1), e.g., 0.5,0.75").option(`--${l(n,H)}`,"Horizontal position within the element (0-1, outside allowed)").option(`--${l(n,J)}`,"Vertical position within the element (0-1, outside allowed)")}function Oe(e,{prefix:t=g}={}){const n=ee(e,t);if(n.length===0)throw new a(`A selector is required: pass one of ${_(S,t)}`);if(n.length>1)throw new a(`Only one selector may be given, got ${_(n,t)}`);const[o]=n,i=Te(e,l(t,O[o].suffix)),s=h(e[l(t,X)],`--${l(t,X)}`);switch(o){case"testId":return{by:"testId",index:s,testId:i};case"text":return{by:"text",index:s,text:hn(i)}}}function Ae(e){const t=h(e.timeout,"--timeout");return t===void 0?void 0:{timeout:t}}function fn(e,{prefix:t=g}={}){return Ie(e,t)?Oe(e,{prefix:t}):void 0}function N(e,{prefix:t=g}={}){return Ie(e,t)?Pe(e,{prefix:t}):void 0}function Pe(e,{prefix:t=g}={}){const n=l(t,L),o=e[n],i=$e(e,l(t,H)),s=$e(e,l(t,J));if(o===void 0){if(ee(e,t).length===0)throw new a(`A selector is required: pass one of ${A({prefix:t})}`);return{...Oe(e,{prefix:t}),x:i,y:s}}if(ee(e,t).length>0)throw new a(`--${n} may not be combined with ${_(S,t)}`);if(i!==void 0||s!==void 0)throw new a(`--${n} may not be combined with --${l(t,H)} or --${l(t,J)}`);return mn(Te(e,n),n)}function A(e={}){const{prefix:t=g}=e;return`${_(S,t)}, --${l(t,L)}`}function Q(e={}){const{prefix:t=g}=e;return[...ke(e),l(t,L)]}function ke({prefix:e=g}={}){return S.map(t=>l(e,O[t].suffix))}function ee(e,t){return S.filter(n=>e[l(t,O[n].suffix)]!==void 0)}function l(...e){return e.filter(Boolean).join("-")}function _(e,t){return e.map(n=>`--${l(t,O[n].suffix)}`).join(", ")}function Ie(e,t){return Object.entries(e).some(([n,o])=>o!==void 0&&n.startsWith(`${t}-`))}function $e(e,t){return h(e[t],`--${t}`)}function mn(e,t){const n=e.split(",");if(n.length!==2)throw new a(`--${t} expects x,y (0-1), e.g., 0.5,0.75`);const[o,i]=n.map(s=>Number(s.trim()));return{by:"position",x:re(o,`--${t}`),y:re(i,`--${t}`)}}function hn(e){const t=/^\/(.*)\/([a-z]*)$/s.exec(e);return t?new RegExp(t[1],t[2]):e}function Te(e,t){return I(e[t],`--${t}`)}const gn=ke(),wn=async({device:e,options:t,positionals:[n]})=>{const o=yn(n),i=fn(t),s=await e.inspect(i,Ae(t)),r=vn(s,t.pretty===!0);await ge(o,r);const p=Buffer.byteLength(r),c=De(s.roots);return i?{bytes:p,matches:s.roots.length,nodes:c,output:o}:{bytes:p,nodes:c,output:o}};function De(e){return e.reduce((t,n)=>t+1+De(n.children),0)}function yn(e="inspect"){return W(e,{extension:".json"})}function vn(e,t){return`${JSON.stringify(e,null,t?2:void 0)}
3
- `}const bn=async({device:e})=>{await e.settings.clearKeychain()},xn=async({device:e,positionals:[t]})=>{await e.open(Sn(t))};function Sn(e){if(typeof e=="string")try{return new URL(e),e}catch{throw new a("<url> expects a URL, e.g., myapp://orders/42")}throw new a("<url> expects a URL, e.g., myapp://orders/42")}const En=new Set(be),On=async({device:e,positionals:[t]})=>{if(!An(t))throw new a(`Button must be one of: ${be.join(", ")}`);await e.press(t)};function An(e){return typeof e=="string"&&En.has(e)}const Pn=async({control:e,options:t,positionals:[n]})=>{const{path:o}=await e.recordStart({force:t.force===!0,path:In(n)});return{path:o}},kn=async({control:e})=>{const{path:t}=await e.recordStop();return{path:t}};function In(e){const t=he(W(e,{extension:".mp4",strict:!0})),n=At(t);if(!me(n))throw new a(`No such directory: ${n}`);return t}const $n=async({device:e,positionals:[t]})=>{if(!Tn(t))throw new a(`Orientation must be one of: ${$.join(", ")}`);await e.rotate(t)};function Tn(e){return $.some(t=>t===e)}const Dn=async({device:e,positionals:[t]})=>{const n=await e.screenshot(),o=Nn(n.mimeType,t);return await ge(o,n.data),{bytes:n.data.byteLength,output:o}};function Ln(e){const t=e.split("/").at(1);return`.${t==="jpeg"?"jpg":t??"png"}`}function Nn(e,t="screenshot"){return W(t,{extension:Ln(e)})}function _n(e){const t=we(new URL("./daemon.js",import.meta.url)),n=St(it(e.sessionId),"a"),o=Tt(process.execPath,[t],{detached:!0,env:{...process.env,[st]:rt(e),DEBUG:zn(process.env.DEBUG)},stdio:["ignore",n,n]});if(Et(n),o.unref(),o.pid===void 0)throw new Error("Failed to spawn the session daemon");return o.pid}function zn(e){return e?`${e},appetize:daemon`:"appetize:daemon"}const Rn=/^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/i;function Un(){return Dt({capitalize:!1,separator:"-"})}function jn(e){if(!Rn.test(e))throw new a('--session-id must use letters, digits, ".", "-" or "_"');return e}const Le=1e4;function Ne(e){if(!at(e))return!1;try{process.kill(e.pid,"SIGTERM")}catch{}return!0}async function _e(e,t){(Ne(e)||Cn(e))&&await Fn(e,t)}async function Fn({pid:e,sessionId:t},n){const o=ce(e);try{await F(Le,AbortSignal.any([n,o]))}catch(i){if(o.aborted||!ae(e))return;throw i}throw new C(`Session ${t} did not stop in time`,{sessionId:t})}function Cn({endedAt:e,pid:t}){return e!==void 0&&Date.now()-Date.parse(e)<Le&&ae(t)}const Mn=["device-android-shell","device-appearance","device-font-size","device-ios-keyboards","device-locale","device-location","device-orientation","device-os-version","device-timezone"];function Bn(e,t){const n=M({shell:f(e["device-android-shell"],"--device-android-shell"),softKeyboard:U(e["device-android-soft-keyboard"],{defaultValue:!0})}),o=M({autocorrect:U(e["device-ios-autocorrect"],{defaultValue:!0}),keyboards:B(e["device-ios-keyboards"],"--device-ios-keyboards")});return{android:n,appearance:b(e["device-appearance"],D,"--device-appearance"),fontSize:b(e["device-font-size"],T,"--device-font-size"),id:t,ios:o,locale:f(e["device-locale"],"--device-locale"),location:ze(e["device-location"],"--device-location"),orientation:b(e["device-orientation"],$,"--device-orientation"),osVersion:f(e["device-os-version"],"--device-os-version"),timezone:f(e["device-timezone"],"--device-timezone")}}function ze(e,t){const n=f(e,t);if(n===void 0)return;const o=n.split(",").map(r=>r.trim());if(o.length!==2||!o.every(qn))throw new a(`${t} expects <lat>,<lng>, e.g., ${t}=-43.65,79.38`);const[i,s]=o.map(Number);if(Math.abs(i)>90||Math.abs(s)>180)throw new a(`${t} expects latitude within \xB190 and longitude within \xB1180`);return{latitude:i,longitude:s}}function qn(e){return e!==""&&Number.isFinite(Number(e))}const Gn=["launch-app","launch-ios-args","launch-params","launch-permissions","launch-url","open"];function Kn(e){const t=B(e["launch-ios-args"],"--launch-ios-args");return M({app:Zn(e["launch-app"]),ios:t===void 0?void 0:{args:t},params:Vn(e["launch-params"]),permissions:b(e["launch-permissions"],pe,"--launch-permissions"),url:f(e["launch-url"],"--launch-url")??f(e.open,"--open")})}function Zn(e){return e===!1?!1:f(e,"--launch-app")}function Vn(e){const t=B(e,"--launch-params");if(t!==void 0)return Object.fromEntries(t.map(Wn))}function Wn(e){const t=e.indexOf("=");if(t<1)throw new a("--launch-params expects key=value, e.g., --launch-params debug=true");return[e.slice(0,t),Xn(e.slice(t+1))]}function Xn(e){try{return JSON.parse(e)}catch{return e}}const Re={"device.id":"<device>","device.location.latitude":"--device-location latitude","device.location.longitude":"--device-location longitude"};function Hn(e,{endpointFromEnv:t=!1}={}){const n=t?{...Re,endpoint:"APPETIZE_ENDPOINT"}:Re;try{ct(e)}catch(o){throw o instanceof pt?new a(o.issues.map(i=>Jn(i,n)).join(`
4
- `)):o}}function Jn({code:e,message:t,path:n},o){const i=Yn(n,o);switch(e){case"invalidValue":return`${i} ${t.replace(/^expected /,"expects ")}`;case"needsLaunchApp":return`${i} has no effect without --target, or with --no-launch-app`;case"needsTarget":return`${i} has no effect without --target`}}function Yn(e,t){return t[e]??`--${e.split(".").filter(n=>!/^\d+$/.test(n)).map(n=>n.replaceAll(/([A-Z])/g,"-$1").toLowerCase()).join("-")}`}const Qn=[...Mn,...Gn,"proxy","target"],eo=150,to={downloadingApp:"Downloading the app",installingApp:"Installing the app",launchingApp:"Launching the app",queued:"Queued",ready:"Device ready",requesting:"Requesting a device",starting:"Starting the device"},no=[{header:"Session ID",key:"sessionId"},{header:"Web view",key:"viewerUrl"},{format:e=>`adb connect ${e}`,header:"adb",key:"adbSerial"},{header:"Host",key:"baseUrl"},{fields:[{header:"Device",key:"device"},{header:"Daemon",key:"daemon"},{header:"Network",key:"network"}],header:"Logs",key:"logs"},{hidden:!0,key:"controlSocket"}];async function oo(e,{onPhase:t,signal:n}){let o;for(;;){const i=de(e),s=i?.state?.phase;if(s&&s!==o&&(o=s,t?.(s)),i&&s==="ready"&&i.controlSocket!==void 0)return{...i,controlSocket:i.controlSocket};if(s==="error")throw new Error(i?.endedReason??"The session failed to start");if(s==="ended")throw new Error(i?.endedReason??"The session ended");await F(eo,n)}}function io(e){if(!(e===void 0||e===!1)){if(e===""||e==="intercept")return"intercept";if(typeof e!="string"||!ro(e))throw new a("--proxy expects 'intercept' or an http:// url, e.g., http://proxy:8080");return e}}async function so({options:e,positionals:t,progress:n,signal:o}){const[i]=t,s=f(e.target,"--target")??co(e);if(s===void 0&&!process.env.APPETIZE_API_TOKEN)throw new a("APPETIZE_API_TOKEN must be set without a target",["Set APPETIZE_API_TOKEN, or pass --target"]);const r=f(e["session-id"],"--session-id"),p=e.force===!0;if(p&&r===void 0)throw new a("--force needs --session-id");const c=r===void 0?Un():jn(r),d={device:Bn(e,i),endpoint:lt(e),featureFlags:le(),launch:Kn(e),proxy:io(e.proxy),sessionId:c,target:s};Hn(d,{endpointFromEnv:e.endpoint===void 0});const m=de(c);if(m!==void 0){if(!p)throw new dt(c);n.status(`Replacing session ${c}`),await _e(m,o),ut(c)}_n(d),n.step(`Session ${c}`);const v=ft(c,d.proxy);if(e.wait===!1)return{logs:v,sessionId:c};const P=await oo(c,{onPhase:k=>{ao(k)&&n.status(to[k]??"Starting the session")},signal:o}).catch(k=>{throw C.wrap(k,c)});return{adbSerial:P.adbSerial,baseUrl:P.baseUrl,controlSocket:P.controlSocket,logs:v,sessionId:c,viewerUrl:P.viewerUrl}}function ro(e){return e.startsWith("http://")}function ao(e){return e!=="ended"&&e!=="error"}function co(e){const t=e._;return Array.isArray(t)&&typeof t[0]=="string"?t[0]:void 0}const po=async({options:e,progress:t,record:n,signal:o})=>(e.wait===!1?Ne(n):(t.status(`Stopping session ${n.sessionId}`),await _e(n,o)),{sessionId:n.sessionId}),lo=["appearance","font-size","location"],uo=async({device:e,options:t})=>{const n=b(t.appearance,D,"--appearance"),o=b(t["font-size"],T,"--font-size"),i=ze(t.location,"--location");if(!n&&!o&&!i)throw new a("Pass at least one of --appearance, --font-size or --location");n&&await e.settings.setAppearance(n),o&&await e.settings.setFontSize(o),i&&await e.settings.setLocation(i)},fo=async({device:e})=>{await e.shake()},Ue="appetize",mo=we(new URL(`./skills/${Ue}/`,import.meta.url)),te=["agents","claude","codex","copilot","cursor"],je=["project","user"],ho={agents:".agents",claude:".claude",codex:".codex",copilot:".copilot",cursor:".cursor"},go=x(".agents","skills"),wo=["claude","agents"];function yo(e){return te.some(t=>t===e)}function vo(e){return je.some(t=>t===e)}async function bo(e){const t=e.agents??await xo(e),n=await Promise.all(t.map(o=>Fe(o,e)));return[...new Set(n.flat())]}async function Fe(e,t){const n=Ce(e,t);if(e==="claude")return[x(n,"skills")];const o=x(t.root,go);return t.scope==="project"?[o]:await Me(n)?[o,x(n,"skills")]:[o]}function Ce(e,t){if(e==="claude"&&t.scope==="user"){const n=process.env.CLAUDE_CONFIG_DIR?.trim();if(n)return he(n)}return x(t.root,ho[e])}async function xo(e){const n=(await Promise.all(te.map(async o=>await So(o,e)?o:void 0))).filter(o=>o!==void 0);return n.length>0?n:wo}async function So(e,t){const n=[Ce(e,t),...await Fe(e,t)];return(await Promise.all(n.map(i=>Me(i)))).includes(!0)}async function Me(e){try{return(await kt(e)).isDirectory()}catch{return!1}}const Eo=["agent","scope"];async function Oo({options:e,signal:t}){const n=ko(e.scope),o=Po(e.agent),i=n==="user"?Lt():process.cwd(),s=await bo({agents:o,root:i,scope:n}),r=[];for(const p of s){t.throwIfAborted();const c=x(p,Ue);await It(c,{force:!0,recursive:!0}),await $t(mo,c,{recursive:!0}),r.push(n==="user"?c:Pt(i,c))}return{installed:r}}function Ao(e){const t=I(e,"--agent");if(!yo(t))throw new a(`--agent expects one of ${te.join(", ")}`);return t}function Po(e){return e===void 0?void 0:(Array.isArray(e)?e:[e]).map(n=>Ao(n))}function ko(e){if(e===void 0)return"project";const t=I(e,"--scope");if(!vo(t))throw new a(`--scope expects one of ${je.join(", ")}`);return t}const w="from",y="to",Io=[...Q({prefix:w}),...Q({prefix:y})],$o=async({device:e,options:t})=>{await e.swipe(No(t),zo(t))};function To(e){return K.some(t=>t===e)}function Do(e){if(e===void 0)return;const t=I(e,"--direction");if(!To(t))throw new a(`--direction expects one of ${K.join(", ")}`);return t}function Lo(e,t){const n=mt(e.distance,"--distance");if(N(e,{prefix:y})!==void 0)throw new a(`--direction may not be combined with --${y}-*`);return{direction:t,distance:n,from:N(e,{prefix:w})}}function No(e){const t=Do(e.direction);return t===void 0?_o(e):Lo(e,t)}function _o(e){if(e.distance!==void 0)throw new a("--distance is only valid with --direction");return{from:Be(N(e,{prefix:w}),w),to:Be(N(e,{prefix:y}),y)}}function zo(e){const t=h(e.duration,"--duration"),n=h(e.timeout,"--timeout");return t===void 0&&n===void 0?void 0:{duration:t,timeout:n}}function Be(e,t){if(e===void 0)throw new a(`A swipe needs --direction, or both endpoints: pass one of ${A({prefix:t})}`);return e}const Ro=Q(),Uo=async({device:e,options:t})=>{const n=Pe(t);await e.tap(n,Ae(t))},jo=async({device:e,options:t,positionals:[n]})=>{await e.type(n,Fo(t))};function Fo(e){const t=h(e.delay,"--delay");return t===void 0?void 0:{delay:t}}const ne=Nt("appetize:cli"),Co={fail:()=>{},status:()=>{},step:()=>{},succeed:()=>{}};function Mo(e,t,n){return e==="interactive"?Bo(t,n):Co}function Bo(e,t){const n=_t({handleSignals:!1,stream:e});let o=0;const i=()=>{n.stop()};t.addEventListener("abort",i,{once:!0});const s=()=>(process.off("exit",i),n.isSpinning);return{fail:()=>{s()&&n.stop()},status:r=>{if(!t.aborted){if(n.isSpinning){n.text=r;return}o=Date.now(),process.once("exit",i),n.start(r)}},step:r=>{e.write(`\u25B8 ${r}
5
- `)},succeed:()=>{if(!s())return;const r=Math.round((Date.now()-o)/1e3);n.success(`${n.text} (${String(r)}s)`)}}}const qo=new Set(["adb","api","apk","id","os","url"]),Go={bottom:"","bottom-left":"","bottom-mid":"","bottom-right":"",left:"","left-mid":"",mid:"","mid-mid":"",middle:" ",right:"","right-mid":"",top:"","top-left":"","top-mid":"","top-right":""},qe=2,Ko=10;function Zo(e){return e.split(/(?=[A-Z])/).map((t,n)=>{const o=t.toLowerCase();return qo.has(o)?o.toUpperCase():n===0?o.charAt(0).toUpperCase()+o.slice(1):o}).join(" ")}function Vo(e,t,n){return ie(e)?Xe(e,t,n):z(e,{fields:t,key:""},n)}function Wo(e,t){const n=[];for(let o=0;o<e.length;o+=t)n.push(e.slice(o,o+t));return n.length===0?[e]:n}function Ge(e){return new zt({chars:Go,head:e,style:{border:[],head:[],"padding-left":0,"padding-right":qe}})}function Ke(e,t){return e.find(n=>n.key===t)}function Ze(e,t){return t?.header??Zo(e)}function oe(e){return e!=null}function ie(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Ve(e){return["boolean","number","string"].includes(typeof e)}function We(e,t){const n=new Set(t),o=e.map(s=>s.key).filter(s=>n.has(s)),i=new Set(e.filter(s=>s.hidden).map(s=>s.key));return[...new Set([...o,...t])].filter(s=>!i.has(s))}function Xe(e,t,n){const o=new Map(Object.entries(e)),i=We(t,[...o.keys()]).filter(m=>oe(o.get(m))),s=i.map(m=>Ke(t,m)),r=i.map((m,v)=>Ze(m,s[v])),p=Math.max(0,...r.map(m=>m.length))+qe,c=n===void 0?void 0:n-p-1,d=Ge();return d.push(...i.map((m,v)=>[r[v],z(o.get(m),s[v],c)])),He(d.toString())}function Xo(e,t){const n=e.map(r=>new Map(Object.entries(r))),o=We(t,n.flatMap(r=>[...r.keys()])),i=o.map(r=>Ke(t,r)),s=Ge(o.map((r,p)=>Ze(r,i[p])));for(const r of n)s.push(o.map((p,c)=>{const d=r.get(p);return oe(d)?z(d,i[c]):""}));return He(s.toString())}function z(e,t,n){if(t?.format)return R(t.format(e),n);if(Ve(e))return R(ue(e),n);if(Array.isArray(e)){const o=e.filter(oe);return o.every(Ve)?R(ue(o),n):o.every(ie)?Xo(o,t?.fields??[]):o.map(i=>z(i,t,n)).join(`
6
- `)}return ie(e)?Xe(e,t?.fields??[],n):R(String(e),n)}function He(e){return e.split(`
2
+ import et from"sade";import{p as U,a as f,b as h,f as j,U as a,d as F,c as tt,i as nt,e as ot,g as it,r as pe,h as I,j as $,k as st,D as rt,s as at,l as ct,m as le,n as de,S as C,o as M,q as B,t as x,u as T,v as L,w as ue,x as pt,y as lt,z as fe,A as dt,B as me,C as ut,E as ft,F as mt,G as ht,H as he,I as ge,J as gt,K as wt,L as yt,M as vt,N as xt,O as bt}from"./stream-X6gIBNIh.js";import{existsSync as we,openAsBlob as St,openSync as Et,closeSync as Ot}from"node:fs";import{basename as q,extname as At,resolve as ye,dirname as Pt,join as b,relative as kt}from"node:path";import{writeFile as ve,stat as It,rm as $t,cp as Tt}from"node:fs/promises";import{spawn as Lt}from"node:child_process";import{fileURLToPath as xe}from"node:url";import{humanId as Dt}from"human-id";import{homedir as Nt}from"node:os";import _t from"debug";import zt from"yocto-spinner";import Rt from"cli-table3";import"json-rpc-2.0";import"zod/mini";import"stream";import"module";import"node:net";const G=["closed","fullyOpen","partiallyOpen"],be=["back","menu","unlock","volumeDown","volumeUp"],Se=["home","lock"],Ee=[...Se,...be],K=["down","left","right","up"],Ut=async({device:e,options:t})=>{await e.authenticateBiometry({match:t.match!==!1})},jt=async({device:e,options:t})=>{const n=U(t.unenroll)??!1;await e.settings.setBiometryEnrolled(!n)},Oe=[{header:"Target",key:"id"},{header:"App",key:"appId"},{header:"Platform",key:"platform"},{header:"Version",key:"versionName"},{header:"Build",key:"buildNumber"},{header:"Uploaded",key:"created"}];function Z({appId:e,buildNumber:t,created:n,id:o,platform:i,versionName:s}){return{appId:e,buildNumber:t,created:n,id:o,platform:i,versionName:s}}const Ft=["app"],Ct=10,Mt=[{fields:Oe,key:"builds"},{header:"Total",key:"total"},{header:"Next page",key:"nextPage"}];async function Bt({options:e,signal:t}){const n=f(e.app,"--app"),o=h(e.limit,"--limit")??Ct,i=h(e.page,"--page"),s={limit:String(o)};n!==void 0&&(s.appId=n),i!==void 0&&(s.page=String(i));const r=await j("/api/v2/builds",{commandOptions:e,query:s,signal:t});return{builds:r.builds.map(Z),nextPage:r.nextPage,total:r.total}}const qt=["note","tags"],Ae=12e4,Gt=2e3,V=[".apk",".apks",".zip",".tar.gz",".tgz"],Kt={".apk":"android",".apks":"android",".tar.gz":"ios",".tgz":"ios",".zip":"ios"},Zt=Oe;async function Vt({options:e,positionals:[t],progress:n,signal:o}){const i=Xt(t),s=q(t);n.status(`Uploading ${s}`);const r=await Jt(t,i,e,o);if(e.wait!==!0)return n.status(`Uploaded ${s}`),Z(r);n.step(`Uploaded ${s}`),n.status("Waiting for build metadata");const p=await Wt(r.id,e,o);return n.status(`Processed ${s}`),Z(p)}async function Wt(e,t,n){const o=h(t.timeout,"--timeout")??Ae,i=Date.now()+o;for(;;){const s=await j(`/api/v2/builds/${e}`,{commandOptions:t,signal:n});if(s.appId!==void 0)return s;if(Date.now()>=i)throw new Error(`Build ${e} is still being processed after ${String(o)}ms; find it with 'appetize build list'`);await F(Gt,n)}}async function Ht(e){if(!we(e))throw new Error(`No build file at ${e}`);return await St(e)}function Xt(e){const t=e.toLowerCase(),n=V.find(o=>t.endsWith(o));if(n===void 0)throw new a(`Cannot tell the platform of ${q(e)}; upload a ${V.join(", ")} file`);return Kt[n]}async function Jt(e,t,n,o){const i=new FormData;i.set("platform",t);const s=f(n.note,"--note");s!==void 0&&i.set("note",s);const r=f(n.tags,"--tags");return r!==void 0&&i.set("tags",r),i.set("file",await Ht(e),q(e)),await j("/api/v2/builds",{body:i,commandOptions:n,method:"POST",signal:o})}const Yt=["platform"],Qt=[{fields:[{header:"ID",key:"id"},{header:"Name",key:"name"},{header:"Platform",key:"platform"},{header:"OS versions",key:"osVersions"}],key:"devices"}];async function en({options:e,signal:t}){const n=tn(e.platform);return{devices:(await tt(e,t)).filter(i=>n===void 0||i.platform===n).map(nn)}}function tn(e){const t=f(e,"--platform");if(t!==void 0){if(!nt(t))throw new a(`--platform expects one of: ${ot.join(", ")}`);return t}}function nn({id:e,name:t,osVersions:n,platform:o}){return{id:e,name:t,osVersions:n,platform:o}}const on="cli-fold",sn=async({device:e,positionals:[t]})=>{if(!rn(t))throw new a(`Hinge must be one of: ${G.join(", ")}`);await e.fold(t)};function rn(e){return G.some(t=>t===e)}const an=async({device:e,options:t,positionals:[n]})=>{await e.gesture(pn(n),cn(t))};function cn(e){const t=h(e.timeout,"--timeout");return t===void 0?void 0:{timeout:t}}function pn(e){let t;try{t=it(e)}catch(n){throw n instanceof SyntaxError?new a(n.message):n}if(t.length===0)throw new a("Expected at least one gesture step");return t}const ln="APPETIZE_OUTPUT";function dn(e,t,n){return{format:un(e,t),progress:fn(e,n)}}function W(e,{extension:t,strict:n=!1}){const o=At(e);if(o==="")return`${e}${t}`;if(n&&o!==t)throw new a(`The output must be a path ending in ${t}`);return e}function un(e,t){if(e.json===!0)return"json";if(e.json===!1)return"text";const n=process.env[ln]?.trim();return n==="json"||n==="text"?n:t.isTTY===!0?"text":"json"}function fn(e,t){return e.quiet===!0?"silent":t.isTTY===!0&&process.env.TERM!=="dumb"&&!("CI"in process.env)?"interactive":"silent"}const S=["text","testId"],g="select",O={testId:{help:"Test id to match by exact string",suffix:"test-id"},text:{help:'Text to match by exact string or regex (e.g., "Sign in" or "/sign in/i")',suffix:"text"}},mn=` Element selectors
3
+ Commands that act on UI elements (e.g., inspect) can take one or more
4
+ element selectors, as flags ending in one of:
5
+
6
+ -text Exact string or /regex/flags, matched against
7
+ Android: text, content-desc, hintText
8
+ iOS: title (or value when empty), label, placeholderValue
9
+ -test-id Exact id
10
+ Android: resource-id, whole or after its last /
11
+ iOS: accessibilityIdentifier
12
+ -index Which match when multiple (0-based)
13
+
14
+ Position selectors
15
+ Commands that act on a screen position (e.g., tap) take position
16
+ selectors. Every element selector is a position selector, at the
17
+ element's center by default.
18
+
19
+ -position Screen position x,y (0-1)
20
+ -x, -y Position within the element (0-1, outside allowed)
21
+
22
+ `,H="index",D="position",X="x",J="y";function Y(e,{prefix:t=g}={}){return S.reduce((n,o)=>{const{help:i,suffix:s}=O[o];return n.option(`--${l(t,s)}`,i)},e).option(`--${l(t,H)}`,"Which match when multiple (0-based)")}function Q(e,t={}){const{prefix:n=g}=t;return Y(e,t).option(`--${l(n,D)}`,"Screen position to use as x,y (0-1), e.g., 0.5,0.75").option(`--${l(n,X)}`,"Horizontal position within the element (0-1, outside allowed)").option(`--${l(n,J)}`,"Vertical position within the element (0-1, outside allowed)")}function Pe(e,{prefix:t=g}={}){const n=oe(e,t);if(n.length===0)throw new a(`A selector is required: pass one of ${_(S,t)}`);if(n.length>1)throw new a(`Only one selector may be given, got ${_(n,t)}`);const[o]=n,i=Le(e,l(t,O[o].suffix)),s=h(e[l(t,H)],`--${l(t,H)}`);switch(o){case"testId":return{by:"testId",index:s,testId:i};case"text":return{by:"text",index:s,text:gn(i)}}}function ee(e){const t=h(e.timeout,"--timeout");return t===void 0?void 0:{timeout:t}}function ke(e,{prefix:t=g}={}){return $e(e,t)?Pe(e,{prefix:t}):void 0}function N(e,{prefix:t=g}={}){return $e(e,t)?Ie(e,{prefix:t}):void 0}function Ie(e,{prefix:t=g}={}){const n=l(t,D),o=e[n],i=Te(e,l(t,X)),s=Te(e,l(t,J));if(o===void 0){if(oe(e,t).length===0)throw new a(`A selector is required: pass one of ${A({prefix:t})}`);return{...Pe(e,{prefix:t}),x:i,y:s}}if(oe(e,t).length>0)throw new a(`--${n} may not be combined with ${_(S,t)}`);if(i!==void 0||s!==void 0)throw new a(`--${n} may not be combined with --${l(t,X)} or --${l(t,J)}`);return hn(Le(e,n),n)}function A(e={}){const{prefix:t=g}=e;return`${_(S,t)}, --${l(t,D)}`}function te(e={}){const{prefix:t=g}=e;return[...ne(e),l(t,D)]}function ne({prefix:e=g}={}){return S.map(t=>l(e,O[t].suffix))}function oe(e,t){return S.filter(n=>e[l(t,O[n].suffix)]!==void 0)}function l(...e){return e.filter(Boolean).join("-")}function _(e,t){return e.map(n=>`--${l(t,O[n].suffix)}`).join(", ")}function $e(e,t){return Object.entries(e).some(([n,o])=>o!==void 0&&n.startsWith(`${t}-`))}function Te(e,t){return h(e[t],`--${t}`)}function hn(e,t){const n=e.split(",");if(n.length!==2)throw new a(`--${t} expects x,y (0-1), e.g., 0.5,0.75`);const[o,i]=n.map(s=>Number(s.trim()));return{by:"position",x:pe(o,`--${t}`),y:pe(i,`--${t}`)}}function gn(e){const t=/^\/(.*)\/([a-z]*)$/s.exec(e);return t?new RegExp(t[1],t[2]):e}function Le(e,t){return I(e[t],`--${t}`)}const wn=ne(),yn=async({device:e,options:t,positionals:[n]})=>{const o=vn(n),i=ke(t),s=await e.inspect(i,ee(t)),r=xn(s,t.pretty===!0);await ve(o,r);const p=Buffer.byteLength(r),c=De(s.roots);return i?{bytes:p,matches:s.roots.length,nodes:c,output:o}:{bytes:p,nodes:c,output:o}};function De(e){return e.reduce((t,n)=>t+1+De(n.children),0)}function vn(e="inspect"){return W(e,{extension:".json"})}function xn(e,t){return`${JSON.stringify(e,null,t?2:void 0)}
23
+ `}const bn=async({device:e})=>{await e.settings.clearKeychain()},Sn=async({device:e,positionals:[t]})=>{await e.open(En(t))};function En(e){if(typeof e=="string")try{return new URL(e),e}catch{throw new a("<url> expects a URL, e.g., myapp://orders/42")}throw new a("<url> expects a URL, e.g., myapp://orders/42")}const On=new Set(Ee),An=async({device:e,positionals:[t]})=>{if(!Pn(t))throw new a(`Button must be one of: ${Ee.join(", ")}`);await e.press(t)};function Pn(e){return typeof e=="string"&&On.has(e)}const kn=async({control:e,options:t,positionals:[n]})=>{const{path:o}=await e.recordStart({force:t.force===!0,path:$n(n)});return{path:o}},In=async({control:e})=>{const{path:t}=await e.recordStop();return{path:t}};function $n(e){const t=ye(W(e,{extension:".mp4",strict:!0})),n=Pt(t);if(!we(n))throw new a(`No such directory: ${n}`);return t}const Tn=async({device:e,positionals:[t]})=>{if(!Ln(t))throw new a(`Orientation must be one of: ${$.join(", ")}`);await e.rotate(t)};function Ln(e){return $.some(t=>t===e)}const Dn=ne(),Nn=async({device:e,options:t,positionals:[n]})=>{const o=await e.screenshot(ke(t),ee(t)),i=zn(o.mimeType,n);return await ve(i,o.data),{bytes:o.data.byteLength,output:i}};function _n(e){const t=e.split("/").at(1);return`.${t==="jpeg"?"jpg":t??"png"}`}function zn(e,t="screenshot"){return W(t,{extension:_n(e)})}function Rn(e){const t=xe(new URL("./daemon.js",import.meta.url)),n=Et(st(e.sessionId),"a"),o=Lt(process.execPath,[t],{detached:!0,env:{...process.env,[rt]:at(e),DEBUG:Un(process.env.DEBUG)},stdio:["ignore",n,n]});if(Ot(n),o.unref(),o.pid===void 0)throw new Error("Failed to spawn the session daemon");return o.pid}function Un(e){return e?`${e},appetize:daemon`:"appetize:daemon"}const jn=/^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/i;function Fn(){return Dt({capitalize:!1,separator:"-"})}function Cn(e){if(!jn.test(e))throw new a('--session-id must use letters, digits, ".", "-" or "_"');return e}const Ne=1e4;function _e(e){if(!ct(e))return!1;try{process.kill(e.pid,"SIGTERM")}catch{}return!0}async function ze(e,t){(_e(e)||Bn(e))&&await Mn(e,t)}async function Mn({pid:e,sessionId:t},n){const o=de(e);try{await F(Ne,AbortSignal.any([n,o]))}catch(i){if(o.aborted||!le(e))return;throw i}throw new C(`Session ${t} did not stop in time`,{sessionId:t})}function Bn({endedAt:e,pid:t}){return e!==void 0&&Date.now()-Date.parse(e)<Ne&&le(t)}const qn=["device-android-shell","device-appearance","device-font-size","device-ios-keyboards","device-locale","device-location","device-orientation","device-os-version","device-timezone"];function Gn(e,t){const n=M({shell:f(e["device-android-shell"],"--device-android-shell"),softKeyboard:U(e["device-android-soft-keyboard"],{defaultValue:!0})}),o=M({autocorrect:U(e["device-ios-autocorrect"],{defaultValue:!0}),keyboards:B(e["device-ios-keyboards"],"--device-ios-keyboards")});return{android:n,appearance:x(e["device-appearance"],L,"--device-appearance"),fontSize:x(e["device-font-size"],T,"--device-font-size"),id:t,ios:o,locale:f(e["device-locale"],"--device-locale"),location:Re(e["device-location"],"--device-location"),orientation:x(e["device-orientation"],$,"--device-orientation"),osVersion:f(e["device-os-version"],"--device-os-version"),timezone:f(e["device-timezone"],"--device-timezone")}}function Re(e,t){const n=f(e,t);if(n===void 0)return;const o=n.split(",").map(r=>r.trim());if(o.length!==2||!o.every(Kn))throw new a(`${t} expects <lat>,<lng>, e.g., ${t}=-43.65,79.38`);const[i,s]=o.map(Number);if(Math.abs(i)>90||Math.abs(s)>180)throw new a(`${t} expects latitude within \xB190 and longitude within \xB1180`);return{latitude:i,longitude:s}}function Kn(e){return e!==""&&Number.isFinite(Number(e))}const Zn=["launch-app","launch-ios-args","launch-params","launch-permissions","launch-url","open"];function Vn(e){const t=B(e["launch-ios-args"],"--launch-ios-args");return M({app:Wn(e["launch-app"]),ios:t===void 0?void 0:{args:t},params:Hn(e["launch-params"]),permissions:x(e["launch-permissions"],ue,"--launch-permissions"),url:f(e["launch-url"],"--launch-url")??f(e.open,"--open")})}function Wn(e){return e===!1?!1:f(e,"--launch-app")}function Hn(e){const t=B(e,"--launch-params");if(t!==void 0)return Object.fromEntries(t.map(Xn))}function Xn(e){const t=e.indexOf("=");if(t<1)throw new a("--launch-params expects key=value, e.g., --launch-params debug=true");return[e.slice(0,t),Jn(e.slice(t+1))]}function Jn(e){try{return JSON.parse(e)}catch{return e}}const Ue={"device.id":"<device>","device.location.latitude":"--device-location latitude","device.location.longitude":"--device-location longitude"};function Yn(e,{endpointFromEnv:t=!1}={}){const n=t?{...Ue,endpoint:"APPETIZE_ENDPOINT"}:Ue;try{pt(e)}catch(o){throw o instanceof lt?new a(o.issues.map(i=>Qn(i,n)).join(`
24
+ `)):o}}function Qn({code:e,message:t,path:n},o){const i=eo(n,o);switch(e){case"invalidValue":return`${i} ${t.replace(/^expected /,"expects ")}`;case"needsLaunchApp":return`${i} has no effect without --target, or with --no-launch-app`;case"needsTarget":return`${i} has no effect without --target`}}function eo(e,t){return t[e]??`--${e.split(".").filter(n=>!/^\d+$/.test(n)).map(n=>n.replaceAll(/([A-Z])/g,"-$1").toLowerCase()).join("-")}`}const to=[...qn,...Zn,"proxy","target"],no=150,oo={downloadingApp:"Downloading the app",installingApp:"Installing the app",launchingApp:"Launching the app",queued:"Queued",ready:"Device ready",requesting:"Requesting a device",starting:"Starting the device"},io=[{header:"Session ID",key:"sessionId"},{header:"Web view",key:"viewerUrl"},{format:e=>`adb connect ${e}`,header:"adb",key:"adbSerial"},{header:"Host",key:"baseUrl"},{fields:[{header:"Device",key:"device"},{header:"Daemon",key:"daemon"},{header:"Network",key:"network"}],header:"Logs",key:"logs"},{hidden:!0,key:"controlSocket"}];async function so(e,{onPhase:t,signal:n}){let o;for(;;){const i=me(e),s=i?.state?.phase;if(s&&s!==o&&(o=s,t?.(s)),i&&s==="ready"&&i.controlSocket!==void 0)return{...i,controlSocket:i.controlSocket};if(s==="error")throw new Error(i?.endedReason??"The session failed to start");if(s==="ended")throw new Error(i?.endedReason??"The session ended");await F(no,n)}}function ro(e){if(!(e===void 0||e===!1)){if(e===""||e==="intercept")return"intercept";if(typeof e!="string"||!co(e))throw new a("--proxy expects 'intercept' or an http:// url, e.g., http://proxy:8080");return e}}async function ao({options:e,positionals:t,progress:n,signal:o}){const[i]=t,s=f(e.target,"--target")??lo(e);if(s===void 0&&!process.env.APPETIZE_API_TOKEN)throw new a("APPETIZE_API_TOKEN must be set without a target",["Set APPETIZE_API_TOKEN, or pass --target"]);const r=f(e["session-id"],"--session-id"),p=e.force===!0;if(p&&r===void 0)throw new a("--force needs --session-id");const c=r===void 0?Fn():Cn(r),d={device:Gn(e,i),endpoint:dt(e),featureFlags:fe(),launch:Vn(e),proxy:ro(e.proxy),sessionId:c,target:s};Yn(d,{endpointFromEnv:e.endpoint===void 0});const m=me(c);if(m!==void 0){if(!p)throw new ut(c);n.status(`Replacing session ${c}`),await ze(m,o),ft(c)}Rn(d),n.step(`Session ${c}`);const v=mt(c,d.proxy);if(e.wait===!1)return{logs:v,sessionId:c};const P=await so(c,{onPhase:k=>{po(k)&&n.status(oo[k]??"Starting the session")},signal:o}).catch(k=>{throw C.wrap(k,c)});return{adbSerial:P.adbSerial,baseUrl:P.baseUrl,controlSocket:P.controlSocket,logs:v,sessionId:c,viewerUrl:P.viewerUrl}}function co(e){return e.startsWith("http://")}function po(e){return e!=="ended"&&e!=="error"}function lo(e){const t=e._;return Array.isArray(t)&&typeof t[0]=="string"?t[0]:void 0}const uo=async({options:e,progress:t,record:n,signal:o})=>(e.wait===!1?_e(n):(t.status(`Stopping session ${n.sessionId}`),await ze(n,o)),{sessionId:n.sessionId}),fo=["appearance","font-size","location"],mo=async({device:e,options:t})=>{const n=x(t.appearance,L,"--appearance"),o=x(t["font-size"],T,"--font-size"),i=Re(t.location,"--location");if(!n&&!o&&!i)throw new a("Pass at least one of --appearance, --font-size or --location");n&&await e.settings.setAppearance(n),o&&await e.settings.setFontSize(o),i&&await e.settings.setLocation(i)},ho=async({device:e})=>{await e.shake()},je="appetize",go=xe(new URL(`./skills/${je}/`,import.meta.url)),ie=["agents","claude","codex","copilot","cursor"],Fe=["project","user"],wo={agents:".agents",claude:".claude",codex:".codex",copilot:".copilot",cursor:".cursor"},yo=b(".agents","skills"),vo=["claude","agents"];function xo(e){return ie.some(t=>t===e)}function bo(e){return Fe.some(t=>t===e)}async function So(e){const t=e.agents??await Eo(e),n=await Promise.all(t.map(o=>Ce(o,e)));return[...new Set(n.flat())]}async function Ce(e,t){const n=Me(e,t);if(e==="claude")return[b(n,"skills")];const o=b(t.root,yo);return t.scope==="project"?[o]:await Be(n)?[o,b(n,"skills")]:[o]}function Me(e,t){if(e==="claude"&&t.scope==="user"){const n=process.env.CLAUDE_CONFIG_DIR?.trim();if(n)return ye(n)}return b(t.root,wo[e])}async function Eo(e){const n=(await Promise.all(ie.map(async o=>await Oo(o,e)?o:void 0))).filter(o=>o!==void 0);return n.length>0?n:vo}async function Oo(e,t){const n=[Me(e,t),...await Ce(e,t)];return(await Promise.all(n.map(i=>Be(i)))).includes(!0)}async function Be(e){try{return(await It(e)).isDirectory()}catch{return!1}}const Ao=["agent","scope"];async function Po({options:e,signal:t}){const n=$o(e.scope),o=Io(e.agent),i=n==="user"?Nt():process.cwd(),s=await So({agents:o,root:i,scope:n}),r=[];for(const p of s){t.throwIfAborted();const c=b(p,je);await $t(c,{force:!0,recursive:!0}),await Tt(go,c,{recursive:!0}),r.push(n==="user"?c:kt(i,c))}return{installed:r}}function ko(e){const t=I(e,"--agent");if(!xo(t))throw new a(`--agent expects one of ${ie.join(", ")}`);return t}function Io(e){return e===void 0?void 0:(Array.isArray(e)?e:[e]).map(n=>ko(n))}function $o(e){if(e===void 0)return"project";const t=I(e,"--scope");if(!bo(t))throw new a(`--scope expects one of ${Fe.join(", ")}`);return t}const w="from",y="to",To=[...te({prefix:w}),...te({prefix:y})],Lo=async({device:e,options:t})=>{await e.swipe(zo(t),Uo(t))};function Do(e){return K.some(t=>t===e)}function No(e){if(e===void 0)return;const t=I(e,"--direction");if(!Do(t))throw new a(`--direction expects one of ${K.join(", ")}`);return t}function _o(e,t){const n=ht(e.distance,"--distance");if(N(e,{prefix:y})!==void 0)throw new a(`--direction may not be combined with --${y}-*`);return{direction:t,distance:n,from:N(e,{prefix:w})}}function zo(e){const t=No(e.direction);return t===void 0?Ro(e):_o(e,t)}function Ro(e){if(e.distance!==void 0)throw new a("--distance is only valid with --direction");return{from:qe(N(e,{prefix:w}),w),to:qe(N(e,{prefix:y}),y)}}function Uo(e){const t=h(e.duration,"--duration"),n=h(e.timeout,"--timeout");return t===void 0&&n===void 0?void 0:{duration:t,timeout:n}}function qe(e,t){if(e===void 0)throw new a(`A swipe needs --direction, or both endpoints: pass one of ${A({prefix:t})}`);return e}const jo=te(),Fo=async({device:e,options:t})=>{const n=Ie(t);await e.tap(n,ee(t))},Co=async({device:e,options:t,positionals:[n]})=>{await e.type(n,Mo(t))};function Mo(e){const t=h(e.delay,"--delay");return t===void 0?void 0:{delay:t}}const se=_t("appetize:cli"),Bo={fail:()=>{},status:()=>{},step:()=>{},succeed:()=>{}};function qo(e,t,n){return e==="interactive"?Go(t,n):Bo}function Go(e,t){const n=zt({handleSignals:!1,stream:e});let o=0;const i=()=>{n.stop()};t.addEventListener("abort",i,{once:!0});const s=()=>(process.off("exit",i),n.isSpinning);return{fail:()=>{s()&&n.stop()},status:r=>{if(!t.aborted){if(n.isSpinning){n.text=r;return}o=Date.now(),process.once("exit",i),n.start(r)}},step:r=>{e.write(`\u25B8 ${r}
25
+ `)},succeed:()=>{if(!s())return;const r=Math.round((Date.now()-o)/1e3);n.success(`${n.text} (${String(r)}s)`)}}}const Ko=new Set(["adb","api","apk","id","os","url"]),Zo={bottom:"","bottom-left":"","bottom-mid":"","bottom-right":"",left:"","left-mid":"",mid:"","mid-mid":"",middle:" ",right:"","right-mid":"",top:"","top-left":"","top-mid":"","top-right":""},Ge=2,Vo=10;function Wo(e){return e.split(/(?=[A-Z])/).map((t,n)=>{const o=t.toLowerCase();return Ko.has(o)?o.toUpperCase():n===0?o.charAt(0).toUpperCase()+o.slice(1):o}).join(" ")}function Ho(e,t,n){return ae(e)?Xe(e,t,n):z(e,{fields:t,key:""},n)}function Xo(e,t){const n=[];for(let o=0;o<e.length;o+=t)n.push(e.slice(o,o+t));return n.length===0?[e]:n}function Ke(e){return new Rt({chars:Zo,head:e,style:{border:[],head:[],"padding-left":0,"padding-right":Ge}})}function Ze(e,t){return e.find(n=>n.key===t)}function Ve(e,t){return t?.header??Wo(e)}function re(e){return e!=null}function ae(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function We(e){return["boolean","number","string"].includes(typeof e)}function He(e,t){const n=new Set(t),o=e.map(s=>s.key).filter(s=>n.has(s)),i=new Set(e.filter(s=>s.hidden).map(s=>s.key));return[...new Set([...o,...t])].filter(s=>!i.has(s))}function Xe(e,t,n){const o=new Map(Object.entries(e)),i=He(t,[...o.keys()]).filter(m=>re(o.get(m))),s=i.map(m=>Ze(t,m)),r=i.map((m,v)=>Ve(m,s[v])),p=Math.max(0,...r.map(m=>m.length))+Ge,c=n===void 0?void 0:n-p-1,d=Ke();return d.push(...i.map((m,v)=>[r[v],z(o.get(m),s[v],c)])),Je(d.toString())}function Jo(e,t){const n=e.map(r=>new Map(Object.entries(r))),o=He(t,n.flatMap(r=>[...r.keys()])),i=o.map(r=>Ze(t,r)),s=Ke(o.map((r,p)=>Ve(r,i[p])));for(const r of n)s.push(o.map((p,c)=>{const d=r.get(p);return re(d)?z(d,i[c]):""}));return Je(s.toString())}function z(e,t,n){if(t?.format)return R(t.format(e),n);if(We(e))return R(he(e),n);if(Array.isArray(e)){const o=e.filter(re);return o.every(We)?R(he(o),n):o.every(ae)?Jo(o,t?.fields??[]):o.map(i=>z(i,t,n)).join(`
26
+ `)}return ae(e)?Xe(e,t?.fields??[],n):R(String(e),n)}function Je(e){return e.split(`
7
27
  `).map(t=>t.trimEnd()).join(`
8
- `)}function R(e,t){return t===void 0||t<Ko?e:e.split(`
9
- `).flatMap(n=>Ho(n,t)).join(`
10
- `)}function Ho(e,t){const n=[];let o="";for(const i of e.split(" ").flatMap(s=>Wo(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 Jo(e,t={}){const n=await fe(e,{signal:t.signal});return new ht(n,{...t,onClose:()=>{n.close(),t.onClose?.()}})}function Yo(e,t){if(e.controlSocket===void 0)throw new Error("Session is not ready");return Jo(e.controlSocket,{baseUrl:e.baseUrl,signal:t})}async function Qo(e,t){if(e.controlSocket===void 0)throw new Error("Session is not ready");const n=await fe(e.controlSocket,{signal:t}),{client:o}=gt(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 ei(e){return async({options:t,positionals:n,progress:o,signal:i})=>{const s=t["session-id"],r=wt(typeof s=="string"?s:void 0);try{return await e({options:t,positionals:n,progress:o,record:r,signal:AbortSignal.any([i,ce(r.pid)])})}catch(p){throw C.wrap(p,r.sessionId)}}}function ti(e){return async({options:t,positionals:n,progress:o,record:i,signal:s})=>{const r=await Yo(i,s);try{return await e({device:r,options:t,positionals:n,progress:o,signal:s})}finally{r.close()}}}function ni(e){return async({options:t,positionals:n,progress:o,record:i,signal:s})=>{const r=await Qo(i,s);try{return await e({control:r,options:t,positionals:n,progress:o,signal:s})}finally{r.close()}}}const oi=["session-id",...jt,...Bt,...Jt,...gn,...Eo,...Qn,...lo,...Io,...Ro],ii=["json","quiet"];function si(e){const t=Qe("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: ${D.join(" or ")}`).option("--device-font-size",`Device font size: ${T.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: ${$.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: ${pe.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"),so,{fields:no}),se(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"),po),u(t,"biometry authenticate",n=>n.describe("Answer the app's biometric prompt").option("--no-match","Answer with a non-match").example("biometry authenticate").example("biometry authenticate --no-match"),Rt),u(t,"biometry enroll",n=>n.describe("Enroll biometry (iOS only)").option("--unenroll","Unenroll instead",!1).example("biometry enroll").example("biometry enroll --unenroll"),Ut),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"),Mt,{fields:Ct}),E(t,"build upload <file>",n=>n.describe([`Upload a build (${V.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(Se)})`).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"'),Zt,{fields:Kt}),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"),Qt,{fields:Yt}),nn in le()&&u(t,"fold <hinge>",n=>n.describe(`Fold a foldable device: ${G.join(", ")}`).example("fold closed").example("fold fullyOpen"),on),u(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"),rn),u(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"),wn),u(t,"keychain clear",n=>n.describe("Clear the entire device keychain (iOS only)").example("keychain clear"),bn),u(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"),xn),u(t,"press <button>",n=>n.describe(`Press a device button: ${ve.join(", ")}; Android only: ${ye.join(", ")}`).example("press home").example("press volumeUp"),On),Je(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"),Pn),Je(t,"recording stop",n=>n.describe("Stop recording the device video and close the file").example("recording stop"),kn),u(t,"rotate <orientation>",n=>n.describe("Rotate the device to an orientation: portrait, landscapeLeft, landscapeRight or upsideDown").example("rotate portrait").example("rotate landscapeLeft"),$n),u(t,"screenshot [output]",n=>n.describe("Take a screenshot of the current device screen").example("screenshot").example("screenshot home-screen").example("screenshot ./shots/checkout.png"),Dn),u(t,"settings set",n=>n.describe("Change device settings; pass at least one flag").option("--appearance",`Device appearance: ${D.join(" or ")}`).option("--font-size",`Device font size: ${T.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"),uo),u(t,"shake",n=>n.describe("Shake the device").example("shake"),fo),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"),Oo),u(t,"swipe",n=>Y(Y(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: ${A({prefix:w})}`,` ${A({prefix:y})}`]).option("--direction",`Direction to travel: ${K.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"),$o),u(t,"tap",n=>Y(n.describe(["Tap an element matched by a selector, or a screen position","",`Pick one of: ${A()}`])).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"),Uo),u(t,"type <text>",n=>n.describe(`Type text; {...} presses a named key: ${bt.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),t}function E(e,t,n,o,{fields:i}={}){const s=t.split(" ").filter(r=>!/^[<[]/.test(r)).join(" ");return n(e.command(t)).action(ci({action:o,fields:i,name:s}))}function se(e,t,n,o){return E(e,t,i=>n(i).option("--session-id","Target session id; defaults to the sole active session"),ei(o))}function u(e,t,n,o){return se(e,t,n,ti(o))}function Je(e,t,n,o){return se(e,t,n,ni(o))}function Ye(e){return`${String(Date.now()-e)}ms`}function ri(e,t,n){if(e==="json")return JSON.stringify(t===void 0?{}:t,null,2);const o=t===void 0?"":Vo(t,n,process.stdout.columns);return o===""?void 0:o}async function ai({action:e,fields:t=[],name:n},o){const i=Date.now();ne(`invoked: ${process.argv.slice(2).join(" ")}`);const s=yt(o),{format:r,progress:p}=ln(s.options,process.stdout,process.stderr),c=Mo(p,process.stderr,s.signal);try{const d=ri(r,await e({...s,progress:c}),t);c.succeed(),d!==void 0&&process.stdout.write(`${d}
11
- `),ne(`ok in ${Ye(i)}`)}catch(d){c.fail(),process.stderr.write(`${vt(r,d,{command:n})}
12
- `),ne(`failed after ${Ye(i)}:`,d),process.exitCode=1}}function ci(e){return(...t)=>{ai(e,t)}}si("0.27.0").parse(process.argv,{boolean:ii,string:oi});
28
+ `)}function R(e,t){return t===void 0||t<Vo?e:e.split(`
29
+ `).flatMap(n=>Yo(n,t)).join(`
30
+ `)}function Yo(e,t){const n=[];let o="";for(const i of e.split(" ").flatMap(s=>Xo(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 Qo(e,t={}){const n=await ge(e,{signal:t.signal});return new gt(n,{...t,onClose:()=>{n.close(),t.onClose?.()}})}function ei(e,t){if(e.controlSocket===void 0)throw new Error("Session is not ready");return Qo(e.controlSocket,{baseUrl:e.baseUrl,signal:t})}async function ti(e,t){if(e.controlSocket===void 0)throw new Error("Session is not ready");const n=await ge(e.controlSocket,{signal:t}),{client:o}=wt(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 ni(e){return async({options:t,positionals:n,progress:o,signal:i})=>{const s=t["session-id"],r=yt(typeof s=="string"?s:void 0);try{return await e({options:t,positionals:n,progress:o,record:r,signal:AbortSignal.any([i,de(r.pid)])})}catch(p){throw C.wrap(p,r.sessionId)}}}function oi(e){return async({options:t,positionals:n,progress:o,record:i,signal:s})=>{const r=await ei(i,s);try{return await e({device:r,options:t,positionals:n,progress:o,signal:s})}finally{r.close()}}}function ii(e){return async({options:t,positionals:n,progress:o,record:i,signal:s})=>{const r=await ti(i,s);try{return await e({control:r,options:t,positionals:n,progress:o,signal:s})}finally{r.close()}}}const si=["session-id",...Ft,...qt,...Yt,...wn,...Dn,...Ao,...to,...fo,...To,...jo],ri=["json","quiet"];function ai(e){const t=et("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: ${L.join(" or ")}`).option("--device-font-size",`Device font size: ${T.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: ${$.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: ${ue.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"),ao,{fields:io}),ce(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"),uo),u(t,"biometry authenticate",n=>n.describe("Answer the app's biometric prompt").option("--no-match","Answer with a non-match").example("biometry authenticate").example("biometry authenticate --no-match"),Ut),u(t,"biometry enroll",n=>n.describe("Enroll biometry (iOS only)").option("--unenroll","Unenroll instead",!1).example("biometry enroll").example("biometry enroll --unenroll"),jt),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"),Bt,{fields:Mt}),E(t,"build upload <file>",n=>n.describe([`Upload a build (${V.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(Ae)})`).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"'),Vt,{fields:Zt}),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"),en,{fields:Qt}),on in fe()&&u(t,"fold <hinge>",n=>n.describe(`Fold a foldable device: ${G.join(", ")}`).example("fold closed").example("fold fullyOpen"),sn),u(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"),an),u(t,"inspect [output]",n=>Y(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"),yn),u(t,"keychain clear",n=>n.describe("Clear the entire device keychain (iOS only)").example("keychain clear"),bn),u(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"),Sn),u(t,"press <button>",n=>n.describe(`Press a device button: ${Se.join(", ")}; Android only: ${be.join(", ")}`).example("press home").example("press volumeUp"),An),Ye(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"),kn),Ye(t,"recording stop",n=>n.describe("Stop recording the device video and close the file").example("recording stop"),In),u(t,"rotate <orientation>",n=>n.describe("Rotate the device to an orientation: portrait, landscapeLeft, landscapeRight or upsideDown").example("rotate portrait").example("rotate landscapeLeft"),Tn),u(t,"screenshot [output]",n=>Y(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"),Nn),u(t,"settings set",n=>n.describe("Change device settings; pass at least one flag").option("--appearance",`Device appearance: ${L.join(" or ")}`).option("--font-size",`Device font size: ${T.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"),mo),u(t,"shake",n=>n.describe("Shake the device").example("shake"),ho),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"),Po),u(t,"swipe",n=>Q(Q(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: ${A({prefix:w})}`,` ${A({prefix:y})}`]).option("--direction",`Direction to travel: ${K.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"),Lo),u(t,"tap",n=>Q(n.describe(["Tap an element matched by a selector, or a screen position","",`Pick one of: ${A()}`])).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),u(t,"type <text>",n=>n.describe(`Type text; {...} presses a named key: ${bt.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"),Co),ci(t,mn)}function ci(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(r=>!/^[<[]/.test(r)).join(" ");return n(e.command(t)).action(di({action:o,fields:i,name:s}))}function ce(e,t,n,o){return E(e,t,i=>n(i).option("--session-id","Target session id; defaults to the sole active session"),ni(o))}function u(e,t,n,o){return ce(e,t,n,oi(o))}function Ye(e,t,n,o){return ce(e,t,n,ii(o))}function Qe(e){return`${String(Date.now()-e)}ms`}function pi(e,t,n){if(e==="json")return JSON.stringify(t===void 0?{}:t,null,2);const o=t===void 0?"":Ho(t,n,process.stdout.columns);return o===""?void 0:o}async function li({action:e,fields:t=[],name:n},o){const i=Date.now();se(`invoked: ${process.argv.slice(2).join(" ")}`);const s=vt(o),{format:r,progress:p}=dn(s.options,process.stdout,process.stderr),c=qo(p,process.stderr,s.signal);try{const d=pi(r,await e({...s,progress:c}),t);c.succeed(),d!==void 0&&process.stdout.write(`${d}
31
+ `),se(`ok in ${Qe(i)}`)}catch(d){c.fail(),process.stderr.write(`${xt(r,d,{command:n})}
32
+ `),se(`failed after ${Qe(i)}:`,d),process.exitCode=1}}function di(e){return(...t)=>{li(e,t)}}ai("0.28.0").parse(process.argv,{boolean:ri,string:si});
package/daemon.js CHANGED
@@ -1,2 +1,2 @@
1
- import{T as _,P as A,Q as F,R as T,V as z,J as V,W as j,y as v,X as N,x as H,Y as Q,Z as J,_ as $,$ as G,a0 as K,F as Z,a1 as X,a2 as Y,a3 as B,a4 as ee}from"./stream-Cbdd68uZ.js";import b from"debug";import{createServer as R}from"node:net";import{pipeline as P}from"node:stream";import{rm as te}from"node:fs/promises";import{readFileSync as ne,createWriteStream as oe,existsSync as re,rmSync as se}from"node:fs";import{createServer as ie}from"node:http";import{WebSocketServer as ae}from"ws";import{pipeline as ce}from"node:stream/promises";import{EncodedVideoPacketSource as de,Output as ue,Mp4OutputFormat as le,EncodedPacket as fe,FilePathTarget as pe}from"mediabunny";import"json-rpc-2.0";import"zod/mini";import"node:path";import"node:os";import"stream";import"module";const he=b("appetize.io:transport:ws-ping"),we=3e4;function ge(e){let t=!0;const n=()=>{t=!0};e.on("pong",n);const o=setInterval(()=>{if(!t){clearInterval(o),he("liveness ping went unanswered, terminating"),e.terminate();return}e.readyState===e.OPEN&&(t=!1,e.ping())},we);return{destroy(){clearInterval(o),e.off("pong",n)},pong:n}}const w=b("appetize.io:trace:transport:websocket");class L extends _{constructor(t,{signal:n}={}){super(async r=>{this.#e.send(A(r))},{signal:n}),this.wsOrUrl=t,typeof t=="string"?(this.#e=new WebSocket(t),this.#e.binaryType="arraybuffer"):this.#e=t;const o=this.#e;if(o instanceof WebSocket?(o.addEventListener("open",this.#s),o.addEventListener("message",this.#r),o.addEventListener("error",this.#o),o.addEventListener("close",this.#n),this.#t.push(()=>{o.removeEventListener("open",this.#s),o.removeEventListener("message",this.#r),o.removeEventListener("error",this.#o),o.removeEventListener("close",this.#n)})):(o.addEventListener("open",this.#s),o.addEventListener("message",this.#r),o.addEventListener("error",this.#o),o.addEventListener("close",this.#n),this.#t.push(()=>{o.removeEventListener("open",this.#s),o.removeEventListener("message",this.#r),o.removeEventListener("error",this.#o),o.removeEventListener("close",this.#n)})),this.send=o.send.bind(o),"ping"in o){const{destroy:r,pong:s}=ge(o);this.#c=s,this.#t.push(r)}}wsOrUrl;onclose;onerror;onmessage;onopen;send;get bufferedAmount(){return this.#e.bufferedAmount}get readyState(){return this.#e.readyState}#t=[];#i=!1;#a=!1;#c;#e;close({destroyWs:t=!1,...n}={}){this.closed||(this.#i=t,super.close(n))}handleClose(t){w("close()");for(const n of this.#t.splice(0))n();if((typeof this.wsOrUrl=="string"||this.#i)&&(this.#e.close(t.code,t.reason),"terminate"in this.#e)){const n=this.#e;setTimeout(()=>{n.terminate()},100)}this.#d(),super.handleClose(t)}#d(t){this.#a||(this.#a=!0,this.onclose?.(t))}#n=t=>{w("handleWebSocketClose()",t);try{this.#d(t)}finally{this.close()}};#o=t=>{w("handleWebSocketError()",t),this.onerror?.(t)};#r=async t=>{w("handleWebSocketMessage()",t),this.#c?.();const n=this.#u(t);if(!n){w("handleWebSocketMessage() -> legacy"),this.onmessage?.(t);return}w("handleWebSocketMessage() -> jsonrpc",n),this[F].dispatchMessage(n)};#s=t=>{w("handleWebSocketOpen()",t),this.onopen?.(t)};#u(t){try{const n=T(new Uint8Array(t.data));return z(n)?n:null}catch{return null}}}function S(e){return typeof e=="object"&&e!==null}function I(e){try{const t=T(new Uint8Array(e.data));if(S(t)&&typeof t.name=="string")return{name:t.name,payload:"payload"in t?t.payload:void 0}}catch{}}async function ve(e,t={}){const n=t.baseUrl??(typeof e=="string"?new URL(e).origin:void 0),o=typeof e=="string"?new L(e,{signal:t.signal}):e,r=new V(o,{...t,baseUrl:n});return typeof e=="string"&&await ye(o,r,t),r}function ye(e,t,n){const o=j(n)?.onLegacyMessage;return new Promise((r,s)=>{e.onopen=()=>{r()},e.onclose=()=>{t.close(),n.signal?.aborted?s(n.signal.reason):s(new Error("The websocket was closed before the device connection was established"))},e.onerror=s,e.onmessage=i=>{const c=I(i);c&&o?.(c)}})}const me=b("appetize:client:render-video-to-mp4"),k=30;async function be(e,t){await e.pipeTo(Ee(t))}function Se(e){return{decoderConfig:{codec:"avc1.640028",codedHeight:e.height,codedWidth:e.width}}}function Ee(e){let t;const n=async()=>{await t?.output.finalize()};return new WritableStream({abort:n,close:n,write:async o=>{if(o.data.byteLength!==0)try{t??=await Te(ke(o),e()),Le(t,o),await Ae(t,o)}catch(r){throw await t?.output.cancel(),r}}})}async function Te(e,t){const n=new de("avc"),o=new ue({format:new le({fastStart:"fragmented"}),target:t});return o.addVideoTrack(n,{frameRate:k}),await o.start(),{frame:0,output:o,seen:{height:e.height,width:e.width},size:{height:e.height,width:e.width},source:n}}function Le(e,t){if(t.type!=="key")return;const{seen:n,size:o}=e;t.width===n.width&&t.height===n.height||(e.seen={height:t.height,width:t.width},me(`video resized to ${String(t.width)}x${String(t.height)}; keeping ${String(o.width)}x${String(o.height)}`))}function ke(e){if(e.type!=="key")throw new Error("Video stream started without a key chunk");return e}async function Ae(e,t){const n=new fe(t.data,t.type,e.frame/k,1/k);e.frame+=1,await e.source.add(n,Se(e.size))}async function $e(e,{onStateChange:t,signal:n}){n?.throwIfAborted(),t?.({phase:"starting"}),await new Promise((o,r)=>{const s=()=>{i(),r(n?.reason)},i=()=>{e.onmessage=void 0,e.onclose=void 0,n?.removeEventListener("abort",s)};e.onmessage=c=>{const a=Re(I(c));a&&(t?.(a),a.phase==="ready"&&(i(),o()))},e.onclose=()=>{i(),r(new Error("The device closed before it was ready"))},n?.addEventListener("abort",s,{once:!0})})}function Re(e){if(!e)return;if(e.name==="ready")return{phase:"ready"};if(e.name!=="launchStatus")return;switch(S(e.payload)&&typeof e.payload.status=="string"?e.payload.status:void 0){case"downloadingApp":return{phase:"downloadingApp"};case"installingApp":return{phase:"installingApp"};case"launchingApp":return{phase:"launchingApp"};default:return}}function Pe(){return typeof process>"u"?void 0:process}function Ie(e){return e==="intercept"?"interceptOnDemand":e}function We(e,{onClose:t,onEvent:n,signal:o}={}){return new Promise((r,s)=>{const i=new WebSocket(e);i.binaryType="arraybuffer",i.onopen=()=>{W(i,{capabilities:{},t:"handshake"})},i.onmessage=c=>{Ue(i,c,{onEvent:n,onHandshake:a=>{r({close:()=>{i.close()},id:a})}})},i.onclose=()=>{s(new v("disconnected",`The connection to ${e} closed`)),t?.()},o?.addEventListener("abort",()=>{s(o.reason),i.close()},{once:!0})})}function Ue(e,t,n){const o=T(new Uint8Array(t.data));S(o)&&(o.t==="handshake"&&typeof o.id=="string"?n.onHandshake(o.id):o.name==="ping"?W(e,{name:"pong"}):typeof o.name=="string"&&n.onEvent?.(o.name,o.payload))}function W(e,t){e.send(A(t))}async function Ce(e,t,{apiToken:n,onQueue:o,signal:r}={}){const s=N();s.promise.catch(()=>{}),Oe(s.reject,r);const i=await We(xe(e),{onClose:()=>{s.reject(De())},onEvent:(c,a)=>{Me(c,a,s,o)},signal:r});return qe(e,{apiToken:n,data:t,socketid:i.id},r).catch(s.reject),await s.promise.finally(()=>{i.close()})}function De(){return new v("disconnected","The webserver connection closed before a session was granted")}function U(e,t){return S(e)?e[t]:void 0}function Me(e,t,n,o){if(e==="queue"||e==="concurrentQueue"){const r=e==="queue"?Fe(t):ze(t);r&&o?.(r)}else e==="newSession"?_e(n,t):e==="userError"&&n.reject(new v("denied",Ve(t)))}function C(e,t){const n=U(e,t);return typeof n=="number"?n:void 0}function y(e,t){const n=U(e,t);return typeof n=="string"?n:void 0}async function qe(e,t,n){const o=await fetch(new URL("/api/session/request",e),{body:JSON.stringify(t),headers:{"content-type":"application/json"},method:"POST",signal:n});if(!o.ok)throw new v("denied",await je(o))}function Oe(e,t){t?.addEventListener("abort",()=>{e(t.reason)},{once:!0})}function xe(e){const t=new URL("/client",e);return t.protocol=t.protocol==="http:"?"ws:":"wss:",t.href}function _e(e,t){const n=y(t,"path"),o=y(t,"sessionToken");n!==void 0&&o!==void 0?e.resolve({path:n,sessionToken:o}):e.reject(new v("denied","The granted session is missing its address"))}function Fe(e){const t=C(e,"position");return t===void 0?void 0:{position:t,reason:"capacity"}}function ze(e){const t=C(e,"position");if(t!==void 0)return{limit:y(e,"name")??"Queue",position:t,reason:"concurrency"}}function Ve(e){return y(e,"message")??"The session request was denied"}async function je(e){const t=`The webserver rejected the session request (${String(e.status)})`,n=await e.json().catch(()=>{}),o=y(n,"message");return o?`${t}: ${o}`:t}async function Ne({apiToken:e=He(),codec:t="h264",device:n,endpoint:o="https://appetize.io",launch:r,onStateChange:s,proxy:i,signal:c,target:a}){c?.throwIfAborted(),H({apiToken:e,codec:t,device:n,endpoint:o,launch:r,proxy:i,target:a}),s?.({phase:"requesting"});const d=await Ce(o,{codec:t,noVideo:!0,proxy:Ie(i),publicKey:a,...J(n),...Q(r)},{apiToken:e,onQueue:h=>{s?.({phase:"queued",queue:h})},signal:c});return{baseUrl:new URL(d.path).origin,sessionToken:d.sessionToken,url:Qe(d)}}function He(){return Pe()?.env.APPETIZE_API_TOKEN}function Qe(e){const t=new URL("/client",e.path);return t.protocol=t.protocol==="http:"?"ws:":"wss:",t.searchParams.set("sessionToken",e.sessionToken),t.href}async function Je(e,t){await be(e,()=>new pe(t,{chunked:!1}))}async function Ge(e,{host:t="127.0.0.1",onError:n,port:o=0,signal:r}={}){r?.throwIfAborted();let s;const i=()=>{const u=s;s=void 0,u&&(u.tunnel?.close(),u.socket.destroy())},c=async u=>{const{socket:l}=u;try{const f=await e.debug.createAdbTunnel();if(l.destroyed){f.close();return}u.tunnel=f,f.closed.catch(E=>{n?.(E)}),Ke(l,f)}catch(f){l.destroyed||n?.(f),l.destroy()}},a=R(u=>{i();const l={socket:u};s=l,u.once("close",()=>{s===l&&(s=void 0)}),c(l)}),d=()=>{r?.removeEventListener("abort",d),i(),a.close()};r?.addEventListener("abort",d,{once:!0});const h=await Ze(a,t,o);return{close:d,url:new URL(`adb://${h.address}:${String(h.port)}`)}}function Ke(e,t){P(t.readable,e,D),P(e,async n=>{const o=t.writable.getWriter();try{for await(const r of n)await o.write(r);await o.close()}catch(r){throw await o.abort(r),r}},D)}function D(){}function Ze(e,t,n){return new Promise((o,r)=>{e.once("error",r),e.listen({host:t,port:n},()=>{e.off("error",r);const s=e.address();if(s===null||typeof s=="string"){r(new Error("The listener has no tcp address"));return}o(s)})})}async function Xe(e,{methods:t={},path:n,signal:o}){o?.throwIfAborted(),await te(n,{force:!0});const r=new Set,s=R(c=>{r.add(c),c.once("close",()=>{r.delete(c)}),$(new G(c),e,{leftEffects:()=>t})}),i=()=>{o?.removeEventListener("abort",i);for(const c of r)c.destroy();s.close()};return await Ye(s,n),o?.aborted&&(i(),o.throwIfAborted()),o?.addEventListener("abort",i,{once:!0}),{close:i,path:n}}function Ye(e,t){return new Promise((n,o)=>{e.once("error",o),e.listen(t,()=>{e.off("error",o),n()})})}const Be=b("appetize:daemon");function p(e){Be(e)}function et(e,{message:t="The operation timed out"}={}){const n=new AbortController,o=setTimeout(()=>{n.abort(new Error(t))},e),r=()=>{clearTimeout(o)};return{cancel:r,signal:n.signal,[Symbol.dispose]:r}}var tt=(e,t)=>(t=Symbol[e])?t:Symbol.for("Symbol."+e),M=e=>{throw TypeError(e)},nt=(e,t,n)=>{if(t!=null){typeof t!="object"&&typeof t!="function"&&M("Object expected");var o,r;o===void 0&&(o=t[tt("dispose")]),typeof o!="function"&&M("Object not disposable"),r&&(o=function(){try{r.call(this)}catch(s){return Promise.reject(s)}}),e.push([n,o,t])}return t},ot=(e,t,n)=>{var o=typeof SuppressedError=="function"?SuppressedError:function(i,c,a,d){return d=Error(a),d.name="SuppressedError",d.error=i,d.suppressed=c,d},r=i=>t=n?new o(i,t,"An error was suppressed during disposal"):(n=!0,i),s=i=>{for(;i=e.pop();)try{var c=i[1]&&i[1].call(i[2]);if(i[0])return Promise.resolve(c).then(s,a=>(r(a),s()))}catch(a){r(a)}if(n)throw t};return s()};const rt=1e4;async function st(e,t,{onClose:n,signal:o}={}){var r=[];try{const c=nt(r,et(rt,{message:"Timed out waiting for session request state"})),a=AbortSignal.any([o,c.signal].filter(g=>!!g)),d=g=>{t.save({state:g}),p(g.phase),g.phase!=="requesting"&&c.cancel()},{featureFlags:h,sessionId:u,...l}=e,{baseUrl:f,url:E}=await Ne({...l,onStateChange:d,signal:a});t.save({baseUrl:f});const m=new L(E,{signal:a});return await $e(m,{onStateChange:d,signal:a}),m.onclose=()=>{n?.("The device connection closed")},{device:await ve(m,{baseUrl:f,signal:a}),transport:m}}catch(c){var s=c,i=!0}finally{ot(r,s,i)}}function it(){const e=K(),t=Z(e.sessionId,e.proxy),n=X({device:e.device,endpoint:e.endpoint,open:e.launch?.url,pid:process.pid,proxy:e.proxy,sessionId:e.sessionId,startedAt:new Date().toISOString(),target:e.target});return n.save({logs:t}),{config:e,logs:t,store:n}}async function at(e,t,n,{signal:o}={}){try{if(!await ct(t))return;const r=await Ge(e,{onError:s=>{p(`adb endpoint error: ${q(s)}`)},signal:o});n.save({adbSerial:r.url.host}),p(`adb endpoint listening at ${r.url.host}`)}catch(r){p(`adb endpoint unavailable: ${q(r)}`)}}async function ct(e){return await Y(e.device.id,{endpoint:e.endpoint})==="android"}function q(e){return e instanceof Error?e.message:String(e)}async function dt(e,t,n,{recorder:o,signal:r}){const{path:s}=await Xe(e,{methods:{"appetizeio.cli.record.start":async i=>await o.start(i),"appetizeio.cli.record.stop":async()=>await o.stop()},path:B(n),signal:r});return t.save({controlSocket:s}),s}const O="127.0.0.1";async function ut(e,t,n={}){try{const o=await lt(e,n);return t.save({viewerUrl:o.url.href}),p(`web endpoint listening at ${o.url.href}`),o}catch(o){p(`web endpoint unavailable: ${gt(o)}`);return}}async function lt(e,{port:t=0,signal:n,viewerHtml:o=ne(new URL("./viewer.html",import.meta.url),"utf8")}){n?.throwIfAborted();const r=ie((u,l)=>{pt(u,l,o)}),s=new Set,i=new ae({noServer:!0,perMessageDeflate:!1});let c;i.on("connection",u=>{if(s.size>0){u.close(1013,"Viewer already connected");return}s.add(u);const l=new L(u);$(l,e),u.once("close",()=>{s.delete(u)})}),r.on("upgrade",(u,l,f)=>{ht(i,u,l,f)});const a=()=>(c??=new Promise((u,l)=>{n?.removeEventListener("abort",d);for(const f of s)ft(f);i.close(),r.close(f=>{f?l(f):u()})}),c),d=()=>{a()};await wt(r,t),n?.addEventListener("abort",d,{once:!0});const h=r.address();if(!h||typeof h=="string")throw await a(),new Error("Web address unavailable");return{close:a,url:new URL(`http://${O}:${String(h.port)}/`)}}function ft(e){e.close(1001)}function pt(e,t,n){if(e.method==="GET"&&e.url==="/"){t.writeHead(200,{"Cache-Control":"no-store","Content-Type":"text/html; charset=utf-8"}),t.end(n);return}t.writeHead(404).end()}function ht(e,t,n,o){if(t.url!=="/ws"){n.destroy();return}e.handleUpgrade(t,n,o,r=>{e.emit("connection",r,t)})}function wt(e,t){return new Promise((n,o)=>{const r=s=>{o(s)};e.once("error",r),e.listen(t,O,()=>{e.removeListener("error",r),n()})})}function gt(e){return e instanceof Error?e.message:String(e)}function vt(e){const t=new AbortController,n=yt(e,t),o=ee();return o.addEventListener("abort",()=>{const{reason:r}=o;n(r instanceof Error?r.message:String(r))},{once:!0}),{shutdown:n,signal:t.signal}}function yt(e,t){let n=!1;return o=>{n||(n=!0,e.save({endedAt:new Date().toISOString(),endedReason:o,state:{phase:"ended"}}),p(o),t.abort())}}async function x(e,t,{label:n,signal:o}){try{const r=await e();await ce(r.readable.pipeThrough(mt()),oe(t,{flags:"a"}))}catch(r){if(o?.aborted)return;const s=r instanceof Error?r.message:String(r);p(`${n} ended with an error: ${s}`)}}function mt(){return new TransformStream({transform:(e,t)=>{t.enqueue(`${JSON.stringify(e)}
1
+ import{T as _,P as A,Q as F,R as T,V as z,J as V,W as j,y as v,X as N,x as H,Y as Q,Z as J,_ as $,$ as G,a0 as K,F as Z,a1 as X,a2 as Y,a3 as B,a4 as ee}from"./stream-X6gIBNIh.js";import b from"debug";import{createServer as R}from"node:net";import{pipeline as P}from"node:stream";import{rm as te}from"node:fs/promises";import{readFileSync as ne,createWriteStream as oe,existsSync as re,rmSync as se}from"node:fs";import{createServer as ie}from"node:http";import{WebSocketServer as ae}from"ws";import{pipeline as ce}from"node:stream/promises";import{EncodedVideoPacketSource as de,Output as ue,Mp4OutputFormat as le,EncodedPacket as fe,FilePathTarget as pe}from"mediabunny";import"json-rpc-2.0";import"zod/mini";import"node:path";import"node:os";import"stream";import"module";const he=b("appetize.io:transport:ws-ping"),we=3e4;function ge(e){let t=!0;const n=()=>{t=!0};e.on("pong",n);const o=setInterval(()=>{if(!t){clearInterval(o),he("liveness ping went unanswered, terminating"),e.terminate();return}e.readyState===e.OPEN&&(t=!1,e.ping())},we);return{destroy(){clearInterval(o),e.off("pong",n)},pong:n}}const w=b("appetize.io:trace:transport:websocket");class L extends _{constructor(t,{signal:n}={}){super(async r=>{this.#e.send(A(r))},{signal:n}),this.wsOrUrl=t,typeof t=="string"?(this.#e=new WebSocket(t),this.#e.binaryType="arraybuffer"):this.#e=t;const o=this.#e;if(o instanceof WebSocket?(o.addEventListener("open",this.#s),o.addEventListener("message",this.#r),o.addEventListener("error",this.#o),o.addEventListener("close",this.#n),this.#t.push(()=>{o.removeEventListener("open",this.#s),o.removeEventListener("message",this.#r),o.removeEventListener("error",this.#o),o.removeEventListener("close",this.#n)})):(o.addEventListener("open",this.#s),o.addEventListener("message",this.#r),o.addEventListener("error",this.#o),o.addEventListener("close",this.#n),this.#t.push(()=>{o.removeEventListener("open",this.#s),o.removeEventListener("message",this.#r),o.removeEventListener("error",this.#o),o.removeEventListener("close",this.#n)})),this.send=o.send.bind(o),"ping"in o){const{destroy:r,pong:s}=ge(o);this.#c=s,this.#t.push(r)}}wsOrUrl;onclose;onerror;onmessage;onopen;send;get bufferedAmount(){return this.#e.bufferedAmount}get readyState(){return this.#e.readyState}#t=[];#i=!1;#a=!1;#c;#e;close({destroyWs:t=!1,...n}={}){this.closed||(this.#i=t,super.close(n))}handleClose(t){w("close()");for(const n of this.#t.splice(0))n();if((typeof this.wsOrUrl=="string"||this.#i)&&(this.#e.close(t.code,t.reason),"terminate"in this.#e)){const n=this.#e;setTimeout(()=>{n.terminate()},100)}this.#d(),super.handleClose(t)}#d(t){this.#a||(this.#a=!0,this.onclose?.(t))}#n=t=>{w("handleWebSocketClose()",t);try{this.#d(t)}finally{this.close()}};#o=t=>{w("handleWebSocketError()",t),this.onerror?.(t)};#r=async t=>{w("handleWebSocketMessage()",t),this.#c?.();const n=this.#u(t);if(!n){w("handleWebSocketMessage() -> legacy"),this.onmessage?.(t);return}w("handleWebSocketMessage() -> jsonrpc",n),this[F].dispatchMessage(n)};#s=t=>{w("handleWebSocketOpen()",t),this.onopen?.(t)};#u(t){try{const n=T(new Uint8Array(t.data));return z(n)?n:null}catch{return null}}}function S(e){return typeof e=="object"&&e!==null}function I(e){try{const t=T(new Uint8Array(e.data));if(S(t)&&typeof t.name=="string")return{name:t.name,payload:"payload"in t?t.payload:void 0}}catch{}}async function ve(e,t={}){const n=t.baseUrl??(typeof e=="string"?new URL(e).origin:void 0),o=typeof e=="string"?new L(e,{signal:t.signal}):e,r=new V(o,{...t,baseUrl:n});return typeof e=="string"&&await ye(o,r,t),r}function ye(e,t,n){const o=j(n)?.onLegacyMessage;return new Promise((r,s)=>{e.onopen=()=>{r()},e.onclose=()=>{t.close(),n.signal?.aborted?s(n.signal.reason):s(new Error("The websocket was closed before the device connection was established"))},e.onerror=s,e.onmessage=i=>{const c=I(i);c&&o?.(c)}})}const me=b("appetize:client:render-video-to-mp4"),k=30;async function be(e,t){await e.pipeTo(Ee(t))}function Se(e){return{decoderConfig:{codec:"avc1.640028",codedHeight:e.height,codedWidth:e.width}}}function Ee(e){let t;const n=async()=>{await t?.output.finalize()};return new WritableStream({abort:n,close:n,write:async o=>{if(o.data.byteLength!==0)try{t??=await Te(ke(o),e()),Le(t,o),await Ae(t,o)}catch(r){throw await t?.output.cancel(),r}}})}async function Te(e,t){const n=new de("avc"),o=new ue({format:new le({fastStart:"fragmented"}),target:t});return o.addVideoTrack(n,{frameRate:k}),await o.start(),{frame:0,output:o,seen:{height:e.height,width:e.width},size:{height:e.height,width:e.width},source:n}}function Le(e,t){if(t.type!=="key")return;const{seen:n,size:o}=e;t.width===n.width&&t.height===n.height||(e.seen={height:t.height,width:t.width},me(`video resized to ${String(t.width)}x${String(t.height)}; keeping ${String(o.width)}x${String(o.height)}`))}function ke(e){if(e.type!=="key")throw new Error("Video stream started without a key chunk");return e}async function Ae(e,t){const n=new fe(t.data,t.type,e.frame/k,1/k);e.frame+=1,await e.source.add(n,Se(e.size))}async function $e(e,{onStateChange:t,signal:n}){n?.throwIfAborted(),t?.({phase:"starting"}),await new Promise((o,r)=>{const s=()=>{i(),r(n?.reason)},i=()=>{e.onmessage=void 0,e.onclose=void 0,n?.removeEventListener("abort",s)};e.onmessage=c=>{const a=Re(I(c));a&&(t?.(a),a.phase==="ready"&&(i(),o()))},e.onclose=()=>{i(),r(new Error("The device closed before it was ready"))},n?.addEventListener("abort",s,{once:!0})})}function Re(e){if(!e)return;if(e.name==="ready")return{phase:"ready"};if(e.name!=="launchStatus")return;switch(S(e.payload)&&typeof e.payload.status=="string"?e.payload.status:void 0){case"downloadingApp":return{phase:"downloadingApp"};case"installingApp":return{phase:"installingApp"};case"launchingApp":return{phase:"launchingApp"};default:return}}function Pe(){return typeof process>"u"?void 0:process}function Ie(e){return e==="intercept"?"interceptOnDemand":e}function We(e,{onClose:t,onEvent:n,signal:o}={}){return new Promise((r,s)=>{const i=new WebSocket(e);i.binaryType="arraybuffer",i.onopen=()=>{W(i,{capabilities:{},t:"handshake"})},i.onmessage=c=>{Ue(i,c,{onEvent:n,onHandshake:a=>{r({close:()=>{i.close()},id:a})}})},i.onclose=()=>{s(new v("disconnected",`The connection to ${e} closed`)),t?.()},o?.addEventListener("abort",()=>{s(o.reason),i.close()},{once:!0})})}function Ue(e,t,n){const o=T(new Uint8Array(t.data));S(o)&&(o.t==="handshake"&&typeof o.id=="string"?n.onHandshake(o.id):o.name==="ping"?W(e,{name:"pong"}):typeof o.name=="string"&&n.onEvent?.(o.name,o.payload))}function W(e,t){e.send(A(t))}async function Ce(e,t,{apiToken:n,onQueue:o,signal:r}={}){const s=N();s.promise.catch(()=>{}),Oe(s.reject,r);const i=await We(xe(e),{onClose:()=>{s.reject(De())},onEvent:(c,a)=>{Me(c,a,s,o)},signal:r});return qe(e,{apiToken:n,data:t,socketid:i.id},r).catch(s.reject),await s.promise.finally(()=>{i.close()})}function De(){return new v("disconnected","The webserver connection closed before a session was granted")}function U(e,t){return S(e)?e[t]:void 0}function Me(e,t,n,o){if(e==="queue"||e==="concurrentQueue"){const r=e==="queue"?Fe(t):ze(t);r&&o?.(r)}else e==="newSession"?_e(n,t):e==="userError"&&n.reject(new v("denied",Ve(t)))}function C(e,t){const n=U(e,t);return typeof n=="number"?n:void 0}function y(e,t){const n=U(e,t);return typeof n=="string"?n:void 0}async function qe(e,t,n){const o=await fetch(new URL("/api/session/request",e),{body:JSON.stringify(t),headers:{"content-type":"application/json"},method:"POST",signal:n});if(!o.ok)throw new v("denied",await je(o))}function Oe(e,t){t?.addEventListener("abort",()=>{e(t.reason)},{once:!0})}function xe(e){const t=new URL("/client",e);return t.protocol=t.protocol==="http:"?"ws:":"wss:",t.href}function _e(e,t){const n=y(t,"path"),o=y(t,"sessionToken");n!==void 0&&o!==void 0?e.resolve({path:n,sessionToken:o}):e.reject(new v("denied","The granted session is missing its address"))}function Fe(e){const t=C(e,"position");return t===void 0?void 0:{position:t,reason:"capacity"}}function ze(e){const t=C(e,"position");if(t!==void 0)return{limit:y(e,"name")??"Queue",position:t,reason:"concurrency"}}function Ve(e){return y(e,"message")??"The session request was denied"}async function je(e){const t=`The webserver rejected the session request (${String(e.status)})`,n=await e.json().catch(()=>{}),o=y(n,"message");return o?`${t}: ${o}`:t}async function Ne({apiToken:e=He(),codec:t="h264",device:n,endpoint:o="https://appetize.io",launch:r,onStateChange:s,proxy:i,signal:c,target:a}){c?.throwIfAborted(),H({apiToken:e,codec:t,device:n,endpoint:o,launch:r,proxy:i,target:a}),s?.({phase:"requesting"});const d=await Ce(o,{codec:t,noVideo:!0,proxy:Ie(i),publicKey:a,...J(n),...Q(r)},{apiToken:e,onQueue:h=>{s?.({phase:"queued",queue:h})},signal:c});return{baseUrl:new URL(d.path).origin,sessionToken:d.sessionToken,url:Qe(d)}}function He(){return Pe()?.env.APPETIZE_API_TOKEN}function Qe(e){const t=new URL("/client",e.path);return t.protocol=t.protocol==="http:"?"ws:":"wss:",t.searchParams.set("sessionToken",e.sessionToken),t.href}async function Je(e,t){await be(e,()=>new pe(t,{chunked:!1}))}async function Ge(e,{host:t="127.0.0.1",onError:n,port:o=0,signal:r}={}){r?.throwIfAborted();let s;const i=()=>{const u=s;s=void 0,u&&(u.tunnel?.close(),u.socket.destroy())},c=async u=>{const{socket:l}=u;try{const f=await e.debug.createAdbTunnel();if(l.destroyed){f.close();return}u.tunnel=f,f.closed.catch(E=>{n?.(E)}),Ke(l,f)}catch(f){l.destroyed||n?.(f),l.destroy()}},a=R(u=>{i();const l={socket:u};s=l,u.once("close",()=>{s===l&&(s=void 0)}),c(l)}),d=()=>{r?.removeEventListener("abort",d),i(),a.close()};r?.addEventListener("abort",d,{once:!0});const h=await Ze(a,t,o);return{close:d,url:new URL(`adb://${h.address}:${String(h.port)}`)}}function Ke(e,t){P(t.readable,e,D),P(e,async n=>{const o=t.writable.getWriter();try{for await(const r of n)await o.write(r);await o.close()}catch(r){throw await o.abort(r),r}},D)}function D(){}function Ze(e,t,n){return new Promise((o,r)=>{e.once("error",r),e.listen({host:t,port:n},()=>{e.off("error",r);const s=e.address();if(s===null||typeof s=="string"){r(new Error("The listener has no tcp address"));return}o(s)})})}async function Xe(e,{methods:t={},path:n,signal:o}){o?.throwIfAborted(),await te(n,{force:!0});const r=new Set,s=R(c=>{r.add(c),c.once("close",()=>{r.delete(c)}),$(new G(c),e,{leftEffects:()=>t})}),i=()=>{o?.removeEventListener("abort",i);for(const c of r)c.destroy();s.close()};return await Ye(s,n),o?.aborted&&(i(),o.throwIfAborted()),o?.addEventListener("abort",i,{once:!0}),{close:i,path:n}}function Ye(e,t){return new Promise((n,o)=>{e.once("error",o),e.listen(t,()=>{e.off("error",o),n()})})}const Be=b("appetize:daemon");function p(e){Be(e)}function et(e,{message:t="The operation timed out"}={}){const n=new AbortController,o=setTimeout(()=>{n.abort(new Error(t))},e),r=()=>{clearTimeout(o)};return{cancel:r,signal:n.signal,[Symbol.dispose]:r}}var tt=(e,t)=>(t=Symbol[e])?t:Symbol.for("Symbol."+e),M=e=>{throw TypeError(e)},nt=(e,t,n)=>{if(t!=null){typeof t!="object"&&typeof t!="function"&&M("Object expected");var o,r;o===void 0&&(o=t[tt("dispose")]),typeof o!="function"&&M("Object not disposable"),r&&(o=function(){try{r.call(this)}catch(s){return Promise.reject(s)}}),e.push([n,o,t])}return t},ot=(e,t,n)=>{var o=typeof SuppressedError=="function"?SuppressedError:function(i,c,a,d){return d=Error(a),d.name="SuppressedError",d.error=i,d.suppressed=c,d},r=i=>t=n?new o(i,t,"An error was suppressed during disposal"):(n=!0,i),s=i=>{for(;i=e.pop();)try{var c=i[1]&&i[1].call(i[2]);if(i[0])return Promise.resolve(c).then(s,a=>(r(a),s()))}catch(a){r(a)}if(n)throw t};return s()};const rt=1e4;async function st(e,t,{onClose:n,signal:o}={}){var r=[];try{const c=nt(r,et(rt,{message:"Timed out waiting for session request state"})),a=AbortSignal.any([o,c.signal].filter(g=>!!g)),d=g=>{t.save({state:g}),p(g.phase),g.phase!=="requesting"&&c.cancel()},{featureFlags:h,sessionId:u,...l}=e,{baseUrl:f,url:E}=await Ne({...l,onStateChange:d,signal:a});t.save({baseUrl:f});const m=new L(E,{signal:a});return await $e(m,{onStateChange:d,signal:a}),m.onclose=()=>{n?.("The device connection closed")},{device:await ve(m,{baseUrl:f,signal:a}),transport:m}}catch(c){var s=c,i=!0}finally{ot(r,s,i)}}function it(){const e=K(),t=Z(e.sessionId,e.proxy),n=X({device:e.device,endpoint:e.endpoint,open:e.launch?.url,pid:process.pid,proxy:e.proxy,sessionId:e.sessionId,startedAt:new Date().toISOString(),target:e.target});return n.save({logs:t}),{config:e,logs:t,store:n}}async function at(e,t,n,{signal:o}={}){try{if(!await ct(t))return;const r=await Ge(e,{onError:s=>{p(`adb endpoint error: ${q(s)}`)},signal:o});n.save({adbSerial:r.url.host}),p(`adb endpoint listening at ${r.url.host}`)}catch(r){p(`adb endpoint unavailable: ${q(r)}`)}}async function ct(e){return await Y(e.device.id,{endpoint:e.endpoint})==="android"}function q(e){return e instanceof Error?e.message:String(e)}async function dt(e,t,n,{recorder:o,signal:r}){const{path:s}=await Xe(e,{methods:{"appetizeio.cli.record.start":async i=>await o.start(i),"appetizeio.cli.record.stop":async()=>await o.stop()},path:B(n),signal:r});return t.save({controlSocket:s}),s}const O="127.0.0.1";async function ut(e,t,n={}){try{const o=await lt(e,n);return t.save({viewerUrl:o.url.href}),p(`web endpoint listening at ${o.url.href}`),o}catch(o){p(`web endpoint unavailable: ${gt(o)}`);return}}async function lt(e,{port:t=0,signal:n,viewerHtml:o=ne(new URL("./viewer.html",import.meta.url),"utf8")}){n?.throwIfAborted();const r=ie((u,l)=>{pt(u,l,o)}),s=new Set,i=new ae({noServer:!0,perMessageDeflate:!1});let c;i.on("connection",u=>{if(s.size>0){u.close(1013,"Viewer already connected");return}s.add(u);const l=new L(u);$(l,e),u.once("close",()=>{s.delete(u)})}),r.on("upgrade",(u,l,f)=>{ht(i,u,l,f)});const a=()=>(c??=new Promise((u,l)=>{n?.removeEventListener("abort",d);for(const f of s)ft(f);i.close(),r.close(f=>{f?l(f):u()})}),c),d=()=>{a()};await wt(r,t),n?.addEventListener("abort",d,{once:!0});const h=r.address();if(!h||typeof h=="string")throw await a(),new Error("Web address unavailable");return{close:a,url:new URL(`http://${O}:${String(h.port)}/`)}}function ft(e){e.close(1001)}function pt(e,t,n){if(e.method==="GET"&&e.url==="/"){t.writeHead(200,{"Cache-Control":"no-store","Content-Type":"text/html; charset=utf-8"}),t.end(n);return}t.writeHead(404).end()}function ht(e,t,n,o){if(t.url!=="/ws"){n.destroy();return}e.handleUpgrade(t,n,o,r=>{e.emit("connection",r,t)})}function wt(e,t){return new Promise((n,o)=>{const r=s=>{o(s)};e.once("error",r),e.listen(t,O,()=>{e.removeListener("error",r),n()})})}function gt(e){return e instanceof Error?e.message:String(e)}function vt(e){const t=new AbortController,n=yt(e,t),o=ee();return o.addEventListener("abort",()=>{const{reason:r}=o;n(r instanceof Error?r.message:String(r))},{once:!0}),{shutdown:n,signal:t.signal}}function yt(e,t){let n=!1;return o=>{n||(n=!0,e.save({endedAt:new Date().toISOString(),endedReason:o,state:{phase:"ended"}}),p(o),t.abort())}}async function x(e,t,{label:n,signal:o}){try{const r=await e();await ce(r.readable.pipeThrough(mt()),oe(t,{flags:"a"}))}catch(r){if(o?.aborted)return;const s=r instanceof Error?r.message:String(r);p(`${n} ended with an error: ${s}`)}}function mt(){return new TransformStream({transform:(e,t)=>{t.enqueue(`${JSON.stringify(e)}
2
2
  `)}})}async function bt(e,t,{signal:n}={}){await x(async()=>await e.debug.createLogStream({signal:n}),t,{label:"log capture",signal:n})}async function St(e,t,{signal:n}={}){await x(async()=>await e.debug.createProxyInterceptStream({signal:n}),t,{label:"network capture",signal:n})}function Et(e,t,{signal:n}={}){let o,r;const s=async(a,{force:d=!1}={})=>{if(re(a)){if(!d)throw new Error(`A recording already exists at ${a}; pass --force to overwrite`);se(a)}const h=await e.video.createStream({signal:n});return o={done:Tt(h,a),path:a,stream:h},t.save({video:{path:a,recording:!0}}),p(`recording video to ${a}`),{path:a}},i=async a=>{a.stream.close();try{await a.done,t.save({video:{path:a.path,recording:!1}})}finally{o=void 0,r=void 0}},c=async()=>{if(!o)return;const{path:a}=o;await i(o),p(`closed the video recording: ${a}`)};return n?.addEventListener("abort",()=>{c()},{once:!0}),{start:async({force:a=!1,path:d})=>{const h=o?.path??r;if(h!==void 0)throw new Error(`Already recording to ${h}`);r=d;try{return await s(d,{force:a})}finally{r=void 0}},stop:async()=>{if(!o)throw new Error("No video recording in progress");const{path:a}=o;return await i(o),{path:a}}}}async function Tt(e,t){try{await Je(e.readable,t)}catch(n){p(`video capture ended with an error: ${String(n)}`)}}async function Lt(){const{config:e,logs:t,store:n}=it();p(`recording to ${n.path}`);const{shutdown:o,signal:r}=vt(n);try{const{device:s,transport:i}=await st(e,n,{onClose:o,signal:r}),c=Et(s,n,{signal:r});await at(s,e,n,{signal:r}),await ut(i,n,{signal:r});const a=await dt(i,n,e.sessionId,{recorder:c,signal:r});p(`control socket listening at ${a}`),bt(s,t.device,{signal:r}),p(`recording device logs to ${t.device}`),t.network&&(St(s,t.network,{signal:r}),p(`recording network events to ${t.network}`))}catch(s){if(r.aborted)return;const i=s instanceof Error?s.message:String(s);n.save({endedAt:new Date().toISOString(),endedReason:i,state:{message:i,phase:"error"}}),p(`error: ${i}`),process.exitCode=1}}Lt();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appetize/cli",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
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,7 +32,7 @@
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.62.0",
35
+ "mediabunny": "^1.62.1",
36
36
  "sade": "^1.8.1",
37
37
  "ws": "^8.22.0",
38
38
  "yocto-spinner": "^1.2.2",
@@ -10,7 +10,7 @@ description: >-
10
10
  or answering what is currently on screen — even if Appetize is not mentioned
11
11
  by name.
12
12
  metadata:
13
- cli-version: '0.27.0'
13
+ cli-version: '0.28.0'
14
14
  ---
15
15
 
16
16
  # Appetize
@@ -117,7 +117,7 @@ appetize screenshot after-login --json
117
117
 
118
118
  Screenshot after each meaningful step. A tap that "succeeded" only means the
119
119
  gesture was delivered — the app may have shown an error, stayed put, or
120
- navigated somewhere unexpected.
120
+ navigated somewhere unexpected. Add `--select-*` to capture one element.
121
121
 
122
122
  ### 4. Wait by inspecting, not by sleeping
123
123
 
@@ -128,11 +128,15 @@ Claude Code, `.agents/skills` for the others — and into both when none is.
128
128
 
129
129
  ## Selectors
130
130
 
131
- `inspect --select-*`, `tap --select-*` and `swipe --from-*`/`--to-*` share the
132
- same suffixes. `-text` is an exact string or `/regex/flags`; `-index` is
133
- 0-based. `-test-id` is an exact match: on Android the whole `resource-id` or the
134
- part after its last `/`, on iOS the `accessibilityIdentifier`.
135
-
136
- Only `tap` and `swipe` take positions. `-position` is a screen `x,y`, each 0-1;
137
- `-x` and `-y` place the point within the matched element, beyond its edge when
138
- outside 0-1.
131
+ Element selectors are flags ending in:
132
+
133
+ - `-text`: an exact string or `/regex/flags`.
134
+ - `-test-id`: an exact id; on Android the whole `resource-id` or the part after
135
+ its last `/`, on iOS the `accessibilityIdentifier`.
136
+ - `-index`: which match when several, 0-based.
137
+
138
+ Every element selector is also a position selector, at the element's center by
139
+ default:
140
+
141
+ - `-position`: a screen `x,y`, each 0-1.
142
+ - `-x` and `-y`: the point within the element, beyond its edge outside 0-1.
@@ -0,0 +1,4 @@
1
+ import Ze from"debug";import{JSONRPCServerAndClient as Nr,JSONRPCServer as zr,JSONRPCClient as Dr,JSONRPC as Br}from"json-rpc-2.0";import*as m from"zod/mini";import{writeFileSync as jr,renameSync as Fr,mkdirSync as Lt,readFileSync as $r,rmSync as Vr,readdirSync as qr}from"node:fs";import{sep as Kr,join as oe}from"node:path";import{homedir as Pt}from"node:os";import{Transform as Wr}from"stream";import{createRequire as Jr}from"module";import{connect as Gr}from"node:net";class Hr extends Event{code;reason;wasClean;constructor(e,r={}){super(e,r),this.code=r.code??0,this.reason=r.reason??"",this.wasClean=r.wasClean??!1}}function Zr(t,e){const r="CloseEvent"in globalThis?globalThis.CloseEvent:Hr;return new r(t,e)}class Yr{#e=new EventTarget;addEventListener(e,r,s){return this.#e.addEventListener(e,r,s),()=>{this.#e.removeEventListener(e,r,s)}}removeEventListener(e,r,s){this.#e.removeEventListener(e,r,s)}dispatchEvent(e){return this.#e.dispatchEvent(e)}}const U=Symbol.for("@appetizeio/rpc:rpcMethods");class Qr extends Yr{send;constructor(e){super(),this.send=e}dispatchClose(e){return this.dispatchEvent(Zr("close",e))}dispatchMessage(e){return this.dispatchEvent(new CustomEvent("message",{detail:e}))}}const Xr="@appetizeio/rpc:playwrightAdapter";class xe{closed=!1;[U];#e;constructor(e,{signal:r}={}){this[U]=new Qr(e),this.#e=r,this.#e?.addEventListener("abort",this.#r)}close(e={}){this.closed||(this.closed=!0,this.handleClose(e))}handleClose(e){this.#e?.removeEventListener("abort",this.#r),this[U].dispatchClose(e)}#r=()=>{this.close({code:1e3,reason:String(this.#e?.reason)})}}const Ce=Symbol.for(Xr);function es(t){window[Ce]&&console.warn("playwright exposed more than once, overwriting previous adapter");const e={send(r){t[U].send(r)},subscribe(r){return t[U].addEventListener("message",s=>{r(s.detail)})}};return window[Ce]=e,()=>{window[Ce]===e&&delete window[Ce]}}const Ct=Ze("appetize.io:error:rpc");function Ye(t,e){const r=new Nr(new zr({errorListener:Ct}),ts(t),{errorListener:Ct}),s=t[U].addEventListener("message",({detail:i})=>{r.receiveAndSend(i)}),n=t[U].addEventListener("close",()=>{s(),r.rejectAllPendingRequests("Transport closed")},{once:!0});for(const i in e){const a=e[i];a&&r.addMethod(i,a.bind(e))}return{rpc:r,unsub:()=>{s(),n()}}}function _t(t,e){const{rpc:r,unsub:s}=Ye(t,e);return{client:r.client,unsub:s}}function _e(t,e){const r=t[U].addEventListener("message",({detail:n})=>{if("method"in n)try{e({method:n.method,param:n.params})}catch{}}),s=t[U].addEventListener("close",r,{once:!0});return()=>{r(),s()}}function ts(t){return new Dr(t[U].send.bind(t[U]),()=>Math.floor(Math.random()*Number.MAX_SAFE_INTEGER)+1)}function Qe(t,e,{leftEffects:r=()=>({}),notifiesLeft:s,notifiesRight:n,rightEffects:i=()=>({})}={}){const a=Ye(t,{}),y=Ye(e,{}),p=[()=>{a.unsub(),y.unsub()}],A=()=>{for(const _ of p)try{_()}catch{}},E=y.rpc.client,R=a.rpc.client;p.push(t[U].addEventListener("close",()=>{A()},{once:!0}),e[U].addEventListener("close",()=>{A()},{once:!0}));const M=r(E,R);for(const _ in M){const L=M[_];L&&a.rpc.addMethod(_,L)}const N=i(R,E);for(const _ in N){const L=N[_];L&&y.rpc.addMethod(_,L)}return Nt(a.rpc,y.rpc,{notifies:s}),Nt(y.rpc,a.rpc,{notifies:n}),A}function Nt(t,e,{notifies:r}={}){t.applyServerMiddleware(async(s,n)=>{const i=r?.();if(i){const{id:y,...p}=n;i[U].send(p)}if(t.hasMethod(n.method))return await s(n);if(!n.id)return e.notify(n.method,n.params),null;const a=await e.request(n.method,n.params);return{id:n.id,jsonrpc:Br,result:a}})}function re(t,...e){t.notify(e[0],e[1],e[2])}const rs=(...t)=>_t(...t),ss=["h264","jpeg"],Xe=["ArrowDown","ArrowLeft","ArrowRight","ArrowUp","Backspace","Enter","Tab"],zt=["landscapeLeft","landscapeRight","portrait","upsideDown"],ns=["dark","light"],is=["xs","s","m","l","xl","xxl","xxxl"];function et(){const t=new xe(async r=>{e[U].dispatchMessage(r)}),e=new xe(async r=>{t[U].dispatchMessage(r)});return t[U].addEventListener("close",()=>{e.close()},{once:!0}),e[U].addEventListener("close",()=>{t.close()},{once:!0}),[t,e]}function Dt(t,e){const r=[],s=()=>{for(const n of r)n()};r.push(t[U].addEventListener("message",n=>e[U].send(n.detail))),r.push(t[U].addEventListener("close",s,{once:!0})),r.push(e[U].addEventListener("message",n=>t[U].send(n.detail))),r.push(e[U].addEventListener("close",s,{once:!0}))}function tt(t){return!!t&&typeof t=="object"&&"jsonrpc"in t}const ae=Ze("appetize.io:trace:transport:iframe");class Bt extends xe{onmessage;#e;#r;constructor(e,{origin:r="*",signal:s}={}){super(async n=>{e.postMessage(n,this.#e)},{signal:s}),this.#r=e,this.#e=r,window.addEventListener("message",this.#t)}postMessage(e,r,s){ae("postMessage(legacy)",{message:e,targetOrigin:r,transfer:s}),this.#r.postMessage(e,r??this.#e,s)}handleClose(e){ae("close()"),window.removeEventListener("message",this.#t),super.handleClose(e)}#t=async e=>{if(ae("handleMessage()",e),e.source!==this.#r||(ae("handleMessage() -> source ok"),this.#e!=="*"&&e.origin!==this.#e))return;ae("handleMessage() -> origin ok");const r=e.data;if(!tt(r)){ae("handleMessage() -> legacy"),this.onmessage?.(e);return}ae("handleMessage() -> jsonrpc"),this[U].dispatchMessage(r)}}function os(t){return{clearState:async e=>{await t.request("appetizeio.device.server.app.clearState",{appId:e})},launch:async e=>{await t.request("appetizeio.device.server.app.launch",{appId:e})},reinstall:async e=>{await t.request("appetizeio.device.server.app.reinstall",{appId:e})},restart:async e=>{await t.request("appetizeio.device.server.app.restart",{appId:e})},terminate:async e=>{await t.request("appetizeio.device.server.app.terminate",{appId:e})}}}function Ne(){let t,e;return{promise:new Promise((s,n)=>{e=s,t=n}),reject:t,resolve:e}}const as=2*1024*1024,jt=Ze("appetize:client:acked-writable-stream");class cs{unacked=0;#e;#r;#t;#s;#n=[];#o;constructor(e){this.#t=e,this.#o=e.window??as}abort(e){this.#i(e),this.#t.onAbort?.(e)}async close(){await this.#c(()=>this.unacked===0),this.#t.onClose?.()}fail(e){this.#i(e)&&this.#t.onError?.(e)}start(e){this.#e=e}write(e){const r=e.byteLength;return this.unacked+=r,this.#t.send(e).then(()=>{this.unacked-=r,this.#s&&(this.#s.acked+=r),this.#a()},s=>{this.fail(s)}),this.unacked>=this.#o&&!this.#s&&(this.#s={acked:0,at:performance.now()},jt("window full: %d bytes unacked, writes stalled",this.unacked)),this.#c(()=>this.unacked<this.#o)}#a(){if(this.#s&&this.unacked<=this.#o/2){const e=performance.now()-this.#s.at;jt("pressure relieved after %dms: %d bytes acked, %s MB/s",Math.round(e),this.#s.acked,(this.#s.acked/Math.max(e,1)/1e3).toFixed(2)),this.#s=void 0}this.#n=this.#n.filter(e=>e.until()?(e.resolve(void 0),!1):!0)}#i(e){if(this.#r)return!1;this.#r={reason:e},this.#e.error(e);const r=this.#n;this.#n=[];for(const s of r)s.reject(e);return!0}#c(e){if(e())return Promise.resolve();const r={...Ne(),until:e};return this.#n.push(r),r.promise}}class ls extends WritableStream{get unacked(){return this.#e.unacked}#e;constructor(e){const r=new cs(e);super({abort:s=>{r.abort(s)},close:()=>r.close(),start:s=>{r.start(s)},write:s=>r.write(s)}),this.#e=r}fail(e){this.#e.fail(e)}}const fs=1024*1024;class us{#e;#r=!1;#t;#s;constructor(e){this.#t=e}cancel(e){this.#n(),this.#t?.(e)}close(){this.#n()&&this.#e.close()}error(e){this.#n()&&this.#e.error(e)}pull(){const e=this.#s;this.#s=void 0,e?.resolve(void 0)}async push(e){if(this.#r)return;this.#s??=Ne();const{promise:r}=this.#s;this.#e.enqueue(e),await r}start(e){this.#e=e}#n(){return this.#r?!1:(this.#r=!0,this.pull(),!0)}}class ds extends ReadableStream{#e;constructor({highWaterMark:e=fs,onCancel:r}={}){const s=new us(r);super({cancel:n=>{s.cancel(n)},pull:()=>{s.pull()},start:n=>{s.start(n)}},new ByteLengthQueuingStrategy({highWaterMark:e})),this.#e=s}close(){this.#e.close()}error(e){this.#e.error(e)}push(e){return this.#e.push(e)}}class hs{closed;readable;writable;#e=[];#r;#t;#s;#n;#o;#a="starting";constructor(e,{onClose:r,receiveBuffer:s,sendWindow:n,signal:i}){if(this.#o=e,this.#r=Ne(),this.closed=this.#r.promise,this.closed.catch(ps),this.#s=new ds({highWaterMark:s,onCancel:()=>{this.#i(void 0)}}),this.readable=this.#s,this.#n=new ls({onAbort:()=>{this.#i(void 0)},onClose:()=>{this.#i(void 0)},onError:a=>{this.#i(a)},send:a=>e.request("appetizeio.device.server.debug.adbTunnel.data",{data:a}),window:n}),this.writable=this.#n,i){const a=()=>{this.#i(i.reason)};i.addEventListener("abort",a,{once:!0}),this.#e.push(()=>{i.removeEventListener("abort",a)})}this.#e.push(()=>{r(this)})}close=()=>{this.#i(void 0)};detach(){this.#i(void 0,{notifyServer:!1})}async handleData(e){this.#a==="open"&&await this.#s.push(e)}handleStopped({error:e}){this.#a==="open"&&this.#i(e?new Error(e):void 0)}async start(){try{await this.#o.request("appetizeio.device.server.debug.adbTunnel.start",void 0)}catch(e){throw this.#i(e),e}if(this.#a==="closed")throw this.#t?this.#t:new Error("The adb tunnel closed before it started");this.#a="open"}async[Symbol.asyncDispose](){this.close();try{await this.closed}catch{}}#i(e,{notifyServer:r=!0}={}){if(this.#a!=="closed"){this.#a="closed",this.#t=e,r&&re(this.#o,"appetizeio.device.server.debug.adbTunnel.stop",void 0),e?(this.#s.error(e),this.#n.fail(e)):(this.#s.close(),this.#n.fail(new Error("The adb tunnel has stopped")));for(const s of this.#e)s();e?this.#r.reject(e):this.#r.resolve(void 0)}}}function ps(){}class ys{readable;ready;#e=[];#r=!1;#t;#s;#n;constructor({label:e,onClose:r,signal:s}){this.#s=e,this.ready=new Promise((i,a)=>{this.#n={reject:a,resolve:i}});let n;if(this.readable=new ReadableStream({cancel:()=>{this.#a()},start:i=>{n=i}}),!n)throw new Error("ReadableStream controller was not initialized");if(this.#t=n,s){const i=()=>{this.fail(s.reason)};s.addEventListener("abort",i,{once:!0}),this.#e.push(()=>{s.removeEventListener("abort",i)})}this.#e.push(()=>{r(this)})}close=()=>{this.#o()};fail(e){this.#o(e)}handleItem(e){this.#t.enqueue(e)}markReady(){if(!this.#n)return;const{resolve:e}=this.#n;this.#n=void 0,e(this)}async*[Symbol.asyncIterator](){const e=this.readable.getReader();try{for(let r=await e.read();!r.done;r=await e.read())yield r.value}finally{await e.cancel()}}#o(e){this.#r||(this.#t.close(),this.#a(e))}#a(e){this.#r=!0;for(const r of this.#e)r();this.#n?.reject(e??new Error(`${this.#s} closed before it started`)),this.#n=void 0}}class ws{get subscribers(){return this.#t}#e;#r;#t=new Set;constructor(e){this.#e=e}close(){this.#r=void 0;for(const e of[...this.#t])e.close();this.#t.clear()}add(e){this.#t.add(e)}ensureStarted(){return this.#r||(this.#r=(async()=>{try{return await this.#e.start()}catch(e){throw this.#r=void 0,e}})()),this.#r}release(e){this.#t.delete(e),!(this.#t.size>0||!this.#r)&&(this.#r=void 0,this.#e.stop())}}class Ft extends ws{#e;constructor(e){super({start:e.start,stop:e.stop}),this.#e=e.label}async createStream({signal:e}={}){e?.throwIfAborted();const r=new ys({label:this.#e,onClose:s=>{this.release(s)},signal:e});this.add(r);try{await this.ensureStarted(),r.markReady()}catch(s){r.fail(s)}return await r.ready}handleNotification(e){for(const r of this.subscribers)r.handleItem(e)}}class gs{#e;#r;#t;#s;constructor(e){this.#s=e,this.#r=new Ft({label:"Log stream",start:()=>e.request("appetizeio.device.server.debug.log.start"),stop:()=>{re(e,"appetizeio.device.server.debug.log.stop",void 0)}}),this.#t=new Ft({label:"Proxy intercept stream",start:()=>e.request("appetizeio.device.server.debug.proxyIntercept.start"),stop:()=>{re(e,"appetizeio.device.server.debug.proxyIntercept.stop",void 0)}})}close(){this.#e?.detach(),this.#r.close(),this.#t.close()}async createAdbTunnel({signal:e}={}){e?.throwIfAborted(),this.#e?.close();const r=new hs(this.#s,{onClose:()=>{this.#e=void 0},signal:e});return this.#e=r,await r.start(),r}async createLogStream(e){return await this.#r.createStream(e)}async createProxyInterceptStream(e){return await this.#t.createStream(e)}async handleAdbTunnelData({data:e}){await this.#e?.handleData(e)}handleAdbTunnelStopped(e){this.#e?.handleStopped(e)}handleLogEntry(e){this.#r.handleNotification(e)}handleProxyInterceptEvent(e){this.#t.handleNotification(e)}}function ze(t){return{...ms(t),index:t.index,within:t.within?ze(t.within):void 0}}function ms(t){switch(t.by){case"testId":return{by:"testId",testId:t.testId};case"text":return{by:"text",text:bs(t.text)}}}function bs(t){return typeof t=="string"?{kind:"exact",value:t}:{flags:t.flags,kind:"regex",source:t.source}}const Es=400,$t=["byTestId","byText","duration","index","x","y"],Ss=new Set($t),vs=/^-?(?:\d+(?:\.\d*)?|\.\d+)$/u,As=/^[a-z]$/u,rt=/^\s$/u;class z extends SyntaxError{constructor(e,r,s){super(`${e} at position ${String(r)}`,{cause:s})}}class xs{#e=0;#r;constructor(e){this.#r=e}parse(){const e=[];for(;this.#e<this.#r.length;){const r=this.#i();if(rt.test(r))this.#e++;else if(r===">"){if(this.#r.charAt(this.#e+1)===">")throw new z('Force press ">>" is not supported',this.#e);this.#e++,e.push({kind:"down"})}else if(r==="/")this.#e++,e.push({kind:"up"});else if(r==="{")e.push(this.#o());else{const[s]=this.#r.slice(this.#e,this.#e+2);throw new z(`Unexpected ${JSON.stringify(s)}; write selectors as {...}`,this.#e)}}return e}#t(){const e=this.#e,r=this.#c(s=>s!=="="&&!st(s));if(this.#i()!=="=")return{kind:"bare",name:r,offset:e};if(r.length===0)throw new z('Expected an attribute name before "="',e);return this.#e++,{kind:"attribute",name:r,offset:e,value:this.#a()}}#s(){const e=this.#e;this.#e++;let r="";for(;;){const s=this.#i();if(this.#e++,s==='"')return r;if(s==="\\"){const n=this.#i();if(n!=='"'&&n!=="\\"&&n!=="")throw new z('Only \\" and \\\\ escapes are supported',this.#e-1);this.#e++,r+=n}else{if(s==="")throw new z("Unclosed string",e);r+=s}}}#n(){const e=this.#e;this.#e++;let r="",s=!1;for(;;){const i=this.#i();if(i==="")throw new z("Unclosed regex",e);if(this.#e++,i==="/"&&!s)break;r+=i,i==="\\"?(r+=this.#i(),this.#e++):i==="["?s=!0:i==="]"&&(s=!1)}const n=this.#c(i=>As.test(i));try{return new RegExp(r,n)}catch(i){throw new z(`Invalid regex /${r}/${n}`,e,i)}}#o(){const e=this.#e;this.#e++;let r;const s={offsets:{}};for(let n=0;;n++){this.#l();const i=this.#i();if(i==="")throw new z('Unclosed "{"',e);if(i==="}"){if(this.#e++,n===0)throw new z('Expected a selector or wait in "{}"',e);return r===void 0?Is(s,e):{duration:r,kind:"wait"}}const a=this.#t();if(n===0&&a.name==="wait")r=a.kind==="attribute"?qt(a.value,"wait"):Es;else{if(r!==void 0)throw new z('"wait" takes no other attributes',a.offset);ks(s,a,n)}}}#a(){const e=this.#e,r=this.#i();let s;if(r==='"')s={kind:"quoted",offset:e,text:this.#s()};else if(r==="/")s={kind:"regex",offset:e,regex:this.#n()};else{const n=this.#c(i=>!st(i));if(n.length===0)throw new z('Expected a value after "="',e);return{kind:"raw",offset:e,text:n}}if(!st(this.#i()))throw new z('Expected a space or "}" after the value',this.#e);return s}#i(){return this.#r.charAt(this.#e)}#c(e){const r=this.#e;for(;this.#e<this.#r.length&&e(this.#i());)this.#e++;return this.#r.slice(r,this.#e)}#l(){this.#c(e=>rt.test(e))}}function Vt(t){return new xs(t).parse()}function ks(t,e,r){const{name:s,offset:n}=e;if(e.kind==="attribute"){nt(t,s,n,e.value);return}if(!s.includes(","))throw new z(`Expected key=value, got ${JSON.stringify(s)}`,n);if(r>0)throw new z("Write the x,y position first",n);const[i,a,...y]=s.split(",");if(y.length>0)throw new z("Expected a screen position as x,y",n);const p=n+i.length+1;nt(t,"x",n,{kind:"raw",offset:n,text:i}),nt(t,"y",p,{kind:"raw",offset:p,text:a})}function Os(t){return Ss.has(t)}function st(t){return t===""||t==="}"||rt.test(t)}function nt(t,e,r,s){if(!Os(e))throw new z(`Unknown attribute ${JSON.stringify(e)}; expected one of ${$t.join(", ")}`,r);if(t.offsets[e]!==void 0)throw new z(`Duplicate attribute ${JSON.stringify(e)}`,r);switch(t.offsets[e]=r,e){case"byTestId":case"byText":if(t.byText!==void 0||t.byTestId!==void 0)throw new z("Only one of byText and byTestId may be given",r);e==="byText"?t.byText=Ms(s):t.byTestId=Rs(s);return;default:t[e]=qt(s,e)}}function Is(t,e){return{duration:t.duration,kind:"move",to:Ts(t,e)}}function qt(t,e){if(t.kind!=="raw"||!vs.test(t.text))throw new z(`"${e}" takes a number`,t.offset);return Number(t.text)}function Ts(t,e){const{byTestId:r,byText:s,index:n,offsets:i,x:a,y}=t;if(s!==void 0)return{by:"text",index:n,text:s,x:a,y};if(r!==void 0)return{by:"testId",index:n,testId:r,x:a,y};if(i.index!==void 0)throw new z('"index" needs byText or byTestId',i.index);if(a===void 0||y===void 0)throw new z("A screen position needs x and y",e);return{by:"position",x:a,y}}function Rs(t){if(t.kind==="regex")throw new z('"byTestId" takes a string',t.offset);return t.text}function Ms(t){return t.kind==="regex"?t.regex:t.text}function ke(t){return t.by==="position"?{by:"position",x:t.x,y:t.y}:{...ze(t),x:t.x,y:t.y}}function Us(t){return t.map(e=>e.kind==="move"?{duration:e.duration,kind:"move",to:ke(e.to)}:e)}function Ls(t){return{press:e=>{re(t,"appetizeio.device.server.keyboard.press",{key:e})}}}function Ps({onRequest:t,parent:e,proxyInlet:r,signal:s}){const n=new Bt(e,{signal:s}),i=[()=>{n.close()}];return Dt(n,r),t&&i.push(_e(n,a=>{t(a,{source:"parent"})})),{cleanup:()=>{for(const a of i)a()},notifies:()=>e===globalThis.window?void 0:n}}function Cs({onRequest:t,proxyInlet:e}){const[r,s]=et(),i=[es(s),()=>{r.close()}];return Dt(r,e),t&&i.push(_e(r,a=>{t(a,{source:"parent"})})),{cleanup:()=>{for(const a of i)a()},notifies:()=>r}}function _s(t){return{clearKeychain:async()=>{await t.request("appetizeio.device.server.settings.clearKeychain")},setAppearance:async e=>{await t.request("appetizeio.device.server.settings.setAppearance",{appearance:e})},setBiometryEnrolled:async e=>{await t.request("appetizeio.device.server.settings.setBiometryEnrolled",{enrolled:e})},setFontSize:async e=>{await t.request("appetizeio.device.server.settings.setFontSize",{fontSize:e})},setLocation:async({latitude:e,longitude:r})=>{await t.request("appetizeio.device.server.settings.setLocation",{latitude:e,longitude:r})}}}function Ns(t){return t.to!==void 0?{from:ke(t.from),kind:"path",to:ke(t.to)}:{direction:t.direction,distance:t.distance,from:t.from?ke(t.from):void 0,kind:"direction"}}function zs(t){return{down:(e,r)=>{re(t,"appetizeio.device.server.touch.down",{x:e,y:r})},move:(e,r)=>{re(t,"appetizeio.device.server.touch.move",{x:e,y:r})},up:(e,r)=>{re(t,"appetizeio.device.server.touch.up",{x:e,y:r})}}}const Ds=new Set(Xe),Bs=/^(?<key>[A-Za-z]+)>(?<repeat>\d+)\/$/u,js=/(\{\{|\{[^{}]*\})/u;function Fs(t){const e=[];let r="";function s(){r.length>0&&(e.push({kind:"text",text:r}),r="")}for(const[n,i]of t.split(js).entries())if(n%2===0){if(i.includes("{"))throw new Error(`Unclosed "{" in ${JSON.stringify(t)}`);r+=i}else i==="{{"?r+="{":(s(),e.push(Vs(i.slice(1,-1))));return s(),e}function $s(t,e){if(!Ds.has(t))throw e.endsWith(">")?new Error(`Holding a key is not supported: {${e}}`):e.startsWith("/")?new Error(`Releasing a key is not supported: {${e}}`):new Error(`{${e}} does not name a supported key. Supported: ${Xe.join(", ")}. Type characters as text, and escape a literal "{" as "{{".`)}function Vs(t){const e=Bs.exec(t)?.groups,r=e?.key??t;if($s(r,t),!e)return{key:r,kind:"press"};const s=Number(e.repeat);if(s<1)throw new Error(`Repeat for {${t}} must be at least 1`);return{key:r,kind:"press",repeat:s}}const qs=Symbol("@appetizeio/client:v1Compat");function Kt(t){return t?.[qs]}class Ks{readable;ready;get dimensions(){return this.#s}#e=[];#r=!1;#t;#s;#n;#o;constructor({onClose:e,requestKeyFrame:r,signal:s}){this.#n=r,this.ready=new Promise((i,a)=>{this.#o={reject:a,resolve:i}});let n;if(this.readable=new ReadableStream({cancel:()=>{this.#i()},start:i=>{n=i}}),!n)throw new Error("ReadableStream controller was not initialized");if(this.#t=n,s){const i=()=>{this.fail(s.reason)};s.addEventListener("abort",i,{once:!0}),this.#e.push(()=>{s.removeEventListener("abort",i)})}this.#e.push(()=>{e(this)})}close=()=>{this.#a()};fail(e){this.#a(e)}handleChunk(e){if(this.#o){if(e.type!=="key")return;const{resolve:r}=this.#o;this.#o=void 0,this.#s={height:e.height,width:e.width},r(this)}else e.type==="key"&&(this.#s={height:e.height,width:e.width});this.#t.enqueue(e)}requestKeyFrame=()=>{this.#r||this.#n()};async*[Symbol.asyncIterator](){const e=this.readable.getReader();try{for(let r=await e.read();!r.done;r=await e.read())yield r.value}finally{await e.cancel()}}#a(e){this.#r||(this.#t.close(),this.#i(e))}#i(e){this.#r=!0;for(const r of this.#e)r();this.#o?.reject(e??new Error("Video stream closed before the first key chunk")),this.#o=void 0}}class Ws{#e=!1;#r;#t;#s=new Set;constructor(e){this.#r=e}close(){this.#t=void 0;for(const e of[...this.#s])e.close()}async createStream({signal:e}={}){e?.throwIfAborted();const r=new Ks({onClose:s=>{this.#o(s)},requestKeyFrame:()=>{this.#a()},signal:e});this.#s.add(r);try{await this.#n()}catch(s){r.fail(s)}return await r.ready}handleChunk(e){e.type==="key"&&(this.#e=!1);for(const r of this.#s)r.handleChunk(e)}#n(){return this.#t?(this.#a(),this.#t):(this.#e=!0,this.#t=(async()=>{try{await this.#r.request("appetizeio.device.server.video.start")}catch(e){throw this.#t=void 0,e}})(),this.#t)}#o(e){this.#s.delete(e),!(this.#s.size>0||!this.#t)&&(this.#t=void 0,re(this.#r,"appetizeio.device.server.video.stop",void 0))}#a(){this.#e||(this.#e=!0,re(this.#r,"appetizeio.device.server.video.requestKeyFrame",void 0))}}class Js{app;debug;keyboard;settings;touch;video;#e;#r=[];#t;constructor(e,r={}){const s=Kt(r)??{},{baseUrl:n,onClose:i,signal:a}=r;this.#e=n?Gs(n):void 0;const{onRequest:y,parent:p,proxyRequest:A}=s,E=Zs(y);a?.addEventListener("abort",this.close);const[R,M]=et(),[N,_]=et();this.#r.push(()=>{R.close(),M.close(),N.close(),_.close()}),this.#r.push(Qe(_,e,{leftEffects:A}));const L=p==="playwright"?Cs({onRequest:E,proxyInlet:N}):p?Ps({onRequest:E,parent:p,proxyInlet:N,signal:a}):void 0;L&&this.#r.push(L.cleanup),this.#r.push(Qe(M,N,{notifiesLeft:L?.notifies}));const{client:Y,unsub:ve}=rs(R,{"appetizeio.device.client.debug.adbTunnel.data":q=>Q.handleAdbTunnelData(q),"appetizeio.device.client.debug.adbTunnel.stopped":async q=>{Q.handleAdbTunnelStopped(q)},"appetizeio.device.client.debug.log.entry":async q=>{Q.handleLogEntry(q)},"appetizeio.device.client.debug.proxyIntercept.event":async q=>{Q.handleProxyInterceptEvent(q)},"appetizeio.device.client.video.chunk":async q=>{k.handleChunk(q)}});this.#t=Y,this.#r.push(ve);const Q=new gs(this.#t);this.#r.push(()=>{Q.close()});const k=new Ws(this.#t);this.#r.push(()=>{k.close()}),this.app=os(this.#t),this.debug=Q,this.keyboard=Ls(this.#t),this.settings=_s(this.#t),this.touch=zs(this.#t),this.video=k,E&&(this.#r.push(_e(M,q=>{E(q,{source:"self"})})),e instanceof Bt&&this.#r.push(_e(e,q=>{E(q,{source:"iframe"})}))),this.#r.push(()=>i?.())}async addMedia(e,r={}){const{url:s}=await this.#t.request("appetizeio.device.server.media.requestUrl"),n=this.#e?new URL(s,this.#e).href:s,i=await fetch(n,{body:e,headers:{"Content-Type":e.type},method:"POST"});if(!i.ok){const p=await i.text();throw new Error(`Media upload failed: ${String(i.status)} ${p}`.trim())}const a=await i.json(),y=r.name??("name"in e?String(e.name):"");await this.#t.request("appetizeio.device.server.addMedia",{name:y||void 0,url:Hs(a)})}async authenticateBiometry({match:e=!0}={}){await this.#t.request("appetizeio.device.server.authenticateBiometry",{match:e})}close=()=>{for(const e of this.#r)try{e()}catch(r){console.error("Device cleanup error:",r)}};async fold(e){await this.#t.request("appetizeio.device.server.fold",{hinge:e})}async gesture(e,r){await this.#t.request("appetizeio.device.server.gesture",{options:r,steps:Us(typeof e=="string"?Vt(e):e)})}async inspect(e,r){return await this.#t.request("appetizeio.device.server.inspect",{options:r,selector:e?ze(e):void 0})}async open(e){new URL(e),await this.#t.request("appetizeio.device.server.open",{url:e})}async press(e){await this.#t.request("appetizeio.device.server.press",{action:e})}async rotate(e){await this.#t.request("appetizeio.device.server.rotate",{orientation:e})}async screenshot(e,r){const s=await this.#t.request("appetizeio.device.server.screenshot",{options:r,selector:e?ze(e):void 0}),n=this.#e?new URL(s.url,this.#e).href:s.url,i=await fetch(n);if(!i.ok)throw new Error(`Screenshot download failed: ${String(i.status)} ${i.statusText}`);const a=i.headers.get("content-type")??"application/octet-stream";return{data:new Uint8Array(await i.arrayBuffer()),mimeType:a}}async shake(){await this.#t.request("appetizeio.device.server.shake")}async swipe(e,r){await this.#t.request("appetizeio.device.server.swipe",{gesture:Ns(e),options:r})}async tap(e,r){await this.#t.request("appetizeio.device.server.tap",{options:r,selector:ke(e)})}async type(e,r){await this.#t.request("appetizeio.device.server.type",{options:r,steps:Fs(e)})}}function Gs(t){const e=new URL(t);return e.protocol==="ws:"?e.protocol="http:":e.protocol==="wss:"&&(e.protocol="https:"),e.href}function Hs(t){if(typeof t=="object"&&t!==null&&"url"in t&&typeof t.url=="string")return t.url;throw new Error("Media upload returned no URL")}const Zs=t=>{if(t)return(e,r)=>{e.method.startsWith("appetizeio.device.server.")&&t(e,r)}};let it;try{it=new TextDecoder}catch{}let w,D,f=0;const ot=[],Ys=105,Qs=57342,Xs=57343,Wt=57337,Jt=6,pe={};let De=11281e4,ye=1681e4,en=1671e4,we=en,ce=ot,Oe=0,O={},B,Ie,Te=0,le=0,j,H,F=[],at=[],W,K,X,Gt={useRecords:!1,mapsAsObjects:!0},Re=!1,Ht=2;try{new Function("")}catch{Ht=1/0}class Se{constructor(e){if(e&&((e.keyMap||e._keyMap)&&!e.useRecords&&(e.useRecords=!1,e.mapsAsObjects=!0),e.useRecords===!1&&e.mapsAsObjects===void 0&&(e.mapsAsObjects=!0),e.getStructures&&(e.getShared=e.getStructures),e.getShared&&!e.structures&&((e.structures=[]).uninitialized=!0),e.keyMap)){this.mapKey=new Map;for(let[r,s]of Object.entries(e.keyMap))this.mapKey.set(s,r)}Object.assign(this,e)}decodeKey(e){return this.keyMap&&this.mapKey.get(e)||e}encodeKey(e){return this.keyMap&&this.keyMap.hasOwnProperty(e)?this.keyMap[e]:e}encodeKeys(e){if(!this._keyMap)return e;let r=new Map;for(let[s,n]of Object.entries(e))r.set(this._keyMap.hasOwnProperty(s)?this._keyMap[s]:s,n);return r}decodeKeys(e){if(!this._keyMap||e.constructor.name!="Map")return e;if(!this._mapKey){this._mapKey=new Map;for(let[s,n]of Object.entries(this._keyMap))this._mapKey.set(n,s)}let r={};return e.forEach((s,n)=>r[Z(this._mapKey.has(n)?this._mapKey.get(n):n)]=s),r}mapDecode(e,r){let s=this.decode(e);return this._keyMap&&s.constructor.name==="Array"?s.map(n=>this.decodeKeys(n)):s}decode(e,r){if(w)return sr(()=>{ut();try{return this?this.decode(e,r):Se.prototype.decode.call(Gt,e,r)}catch(s){throw s&&typeof s=="object"&&Zt.add(s),s}});D=r>-1?r:e.length,f=0,Oe=0,le=0,Ie=null,ce=ot,j=null,w=e;try{K=e.dataView||(e.dataView=new DataView(e.buffer,e.byteOffset,e.byteLength))}catch(s){throw w=null,e instanceof Uint8Array?s:new Error("Source must be a Uint8Array or Buffer but was a "+(e&&typeof e=="object"?e.constructor.name:typeof e))}if(this instanceof Se){if(O=this,W=this.sharedValues&&(this.pack?new Array(this.maxPrivatePackedValues||16).concat(this.sharedValues):this.sharedValues),this.structures)return B=this.structures,je();(!B||B.length>0)&&(B=[])}else O=Gt,(!B||B.length>0)&&(B=[]),W=null;return je()}decodeMultiple(e,r){let s,n=0;try{let i=e.length;Re=!0;let a=this?this.decode(e,i):ht.decode(e,i);if(r){if(r(a)===!1)return;for(;f<i;)if(n=f,r(je())===!1)return}else{for(s=[a];f<i;)n=f,s.push(je());return s}}catch(i){throw i.lastPosition=n,i.values=s,i}finally{Re=!1,ut()}}}const Zt=new WeakSet;let Be;function je(){try{Be=D-f;let t=I();if(j){if(f>=j.postBundlePosition)throw new Error("Unexpected bundle position");f=j.postBundlePosition,j=null}if(f==D)B=null,w=null,H&&(H=null);else if(f>D){let e=new Error("Unexpected end of CBOR data");throw e.incomplete=!0,e}else if(!Re)throw new Error("Data read, but end of buffer not reached");return t}catch(t){throw ut(),Zt.has(t)?t.incomplete&&(t.incomplete=!1):(t instanceof RangeError&&f+8>D||t&&typeof t.message=="string"&&t.message.startsWith("Unexpected end of buffer"))&&(t.incomplete=!0),t}}function J(){let t=new Error("Unexpected end of CBOR data");return t.incomplete=!0,t}function I(){if(!(f<D))throw J();let t=w[f++],e=t>>5;if(t=t&31,t>23)switch(t){case 24:if(f>=D)throw J();t=w[f++];break;case 25:if(e==7)return nn();t=K.getUint16(f),f+=2;break;case 26:if(e==7){let r=K.getFloat32(f);if(O.useFloat32>2){let s=dt[(w[f]&127)<<1|w[f+1]>>7];return f+=4,(s*r+(r>0?.5:-.5)>>0)/s}return f+=4,r}if(t=K.getUint32(f),f+=4,e===1)return-1-t;break;case 27:if(e==7){let r=K.getFloat64(f);return f+=8,r}if(e>1){if(K.getUint32(f)>0)throw new Error("JavaScript does not support arrays, maps, or strings with length over 4294967295");t=K.getUint32(f+4)}else O.int64AsNumber?(t=K.getUint32(f)*4294967296,t+=K.getUint32(f+4)):t=K.getBigUint64(f);f+=8;break;case 31:switch(e){case 2:case 3:throw new Error("Indefinite length not supported for byte or text strings");case 4:let r=[],s,n=0;for(;(s=I())!=pe;){if(n>=De)throw new Error(`Array length exceeds ${De}`);r[n++]=s}return e==4?r:e==3?r.join(""):Buffer.concat(r);case 5:let i;if(O.mapsAsObjects){let a={},y=0;if(O.keyMap)for(;(i=I())!=pe;){if(y++>=we)throw new Error(`Property count exceeds ${we}`);a[Z(O.decodeKey(i))]=I()}else for(;(i=I())!=pe;){if(y++>=we)throw new Error(`Property count exceeds ${we}`);a[Z(i)]=I()}return a}else{X&&(O.mapsAsObjects=!0,X=!1);let a=new Map;if(O.keyMap){let y=0;for(;(i=I())!=pe;){if(y++>=ye)throw new Error(`Map size exceeds ${ye}`);a.set(O.decodeKey(i),I())}}else{let y=0;for(;(i=I())!=pe;){if(y++>=ye)throw new Error(`Map size exceeds ${ye}`);a.set(i,I())}}return a}case 7:return pe;default:throw new Error("Invalid major type for indefinite length "+e)}default:throw new Error("Unknown token "+t)}switch(e){case 0:return t;case 1:return~t;case 2:if(t>D-f)throw J();return sn(t);case 3:if(le>=f)return Ie.slice(f-Te,(f+=t)-Te);if(t>D-f)throw J();if(le==0&&D<140&&t<32){let n=t<16?Xt(t):rn(t);if(n!=null)return n}return Qt(t);case 4:if(t>De)throw new Error(`Array length exceeds ${De}`);if(t>D-f||(Be-=t)<0)throw J();let r=new Array(t);for(let n=0;n<t;n++)r[n]=I();return r;case 5:if(O.mapsAsObjects){if(t>we)throw new Error(`Property count exceeds ${we}`);if(t>(D-f)/2)throw J();let n={};if(O.keyMap)for(let i=0;i<t;i++)n[Z(O.decodeKey(I()))]=I();else for(let i=0;i<t;i++)n[Z(I())]=I();return n}else{if(X&&(O.mapsAsObjects=!0,X=!1),t>ye)throw new Error(`Map size exceeds ${ye}`);if(t>(D-f)/2)throw J();let n=new Map;if(O.keyMap)for(let i=0;i<t;i++)n.set(O.decodeKey(I()),I());else for(let i=0;i<t;i++)n.set(I(),I());return n}case 6:if(t>=Wt){let n=B[t&8191];if(n)return n.read||(n.read=ct(n)),n.read();if(t<65536){if(t==Xs){let i=me(),a=I(),y=I();lt(a,y);let p={};if(O.keyMap)for(let A=2;A<i;A++){let E=O.decodeKey(y[A-2]);p[Z(E)]=I()}else for(let A=2;A<i;A++){let E=y[A-2];p[Z(E)]=I()}return p}else if(t==Qs){let i=me(),a=I();for(let y=2;y<i;y++)lt(a++,I());return I()}else if(t==Wt)return un();if(O.getShared&&(ft(),n=B[t&8191],n))return n.read||(n.read=ct(n)),n.read()}}let s=F[t];if(s)return s.handlesRead?s(I):s(I());{let n=I();for(let i=0;i<at.length;i++){let a=at[i](t,n);if(a!==void 0)return a}return new fe(n,t)}case 7:switch(t){case 20:return!1;case 21:return!0;case 22:return null;case 23:return;default:let n=(W||ue())[t];if(n!==void 0)return n;throw new Error("Unknown token "+t)}default:throw isNaN(t)?J():new Error("Unknown CBOR token "+t)}}const Yt=/^[a-zA-Z_$][a-zA-Z\d_$]*$/;function ct(t){if(!t)throw new Error("Structure is required in record definition");function e(){let r=w[f++];if(r=r&31,r>23)switch(r){case 24:r=w[f++];break;case 25:r=K.getUint16(f),f+=2;break;case 26:r=K.getUint32(f),f+=4;break;default:throw new Error("Expected array header, but got "+w[f-1])}let s=this.compiledReader;for(;s;){if(s.propertyCount===r)return s(I);s=s.next}if(this.slowReads++>=Ht){let i=this.length==r?this:this.slice(0,r);return s=O.keyMap?new Function("r","return {"+i.map(a=>O.decodeKey(a)).map(a=>Yt.test(a)?Z(a)+":r()":"["+JSON.stringify(a)+"]:r()").join(",")+"}"):new Function("r","return {"+i.map(a=>Yt.test(a)?Z(a)+":r()":"["+JSON.stringify(a)+"]:r()").join(",")+"}"),this.compiledReader&&(s.next=this.compiledReader),s.propertyCount=r,this.compiledReader=s,s(I)}let n={};if(O.keyMap)for(let i=0;i<r;i++)n[Z(O.decodeKey(this[i]))]=I();else for(let i=0;i<r;i++)n[Z(this[i])]=I();return n}return t.slowReads=0,e}function Z(t){if(typeof t=="string")return t==="__proto__"?"__proto_":t;if(typeof t=="number"||typeof t=="boolean"||typeof t=="bigint")return t.toString();if(t==null)return t+"";throw new Error("Invalid property name type "+typeof t)}let Qt=Fe;function tn(t){Qt=e();function e(r){return function(n){let i=ce[Oe++];if(i==null){if(j)return Fe(n);let y=t(f,D,n,w);if(typeof y=="string")i=y,ce=ot;else if(ce=y,Oe=1,le=1,i=ce[0],i===void 0)throw new Error("Unexpected end of buffer")}let a=i.length;return a<=n?(f+=n,i):(Ie=i,Te=f,le=f+a,f+=n,i.slice(0,n))}}}function Fe(t){let e;if(t<16&&(e=Xt(t)))return e;if(t>64&&it)return it.decode(w.subarray(f,f+=t));const r=f+t,s=[];for(e="";f<r;){const n=w[f++];if((n&128)===0)s.push(n);else if((n&224)===192)if(n<194||f>=r||(w[f]&192)!==128)s.push(65533);else{const i=w[f++]&63;s.push((n&31)<<6|i)}else if((n&240)===224){const i=f<r?w[f]:0;if(f>=r||(i&192)!==128||n===224&&i<160||n===237&&i>=160)s.push(65533);else if(f++,f>=r||(w[f]&192)!==128)s.push(65533);else{const a=w[f++]&63;s.push((n&31)<<12|(i&63)<<6|a)}}else if((n&248)===240){const i=f<r?w[f]:0;if(n>244||f>=r||(i&192)!==128||n===240&&i<144||n===244&&i>=144)s.push(65533);else if(f++,f>=r||(w[f]&192)!==128)s.push(65533);else{const a=w[f++]&63;if(f>=r||(w[f]&192)!==128)s.push(65533);else{const y=w[f++]&63;let p=(n&7)<<18|(i&63)<<12|a<<6|y;p-=65536,s.push(p>>>10&1023|55296),s.push(56320|p&1023)}}}else s.push(65533);s.length>=4096&&(e+=V.apply(String,s),s.length=0)}return s.length>0&&(e+=V.apply(String,s)),e}let V=String.fromCharCode;function rn(t){let e=f,r=new Array(t);for(let s=0;s<t;s++){const n=w[f++];if((n&128)>0){f=e;return}r[s]=n}return V.apply(String,r)}function Xt(t){if(t<4)if(t<2){if(t===0)return"";{let e=w[f++];if((e&128)>1){f-=1;return}return V(e)}}else{let e=w[f++],r=w[f++];if((e&128)>0||(r&128)>0){f-=2;return}if(t<3)return V(e,r);let s=w[f++];if((s&128)>0){f-=3;return}return V(e,r,s)}else{let e=w[f++],r=w[f++],s=w[f++],n=w[f++];if((e&128)>0||(r&128)>0||(s&128)>0||(n&128)>0){f-=4;return}if(t<6){if(t===4)return V(e,r,s,n);{let i=w[f++];if((i&128)>0){f-=5;return}return V(e,r,s,n,i)}}else if(t<8){let i=w[f++],a=w[f++];if((i&128)>0||(a&128)>0){f-=6;return}if(t<7)return V(e,r,s,n,i,a);let y=w[f++];if((y&128)>0){f-=7;return}return V(e,r,s,n,i,a,y)}else{let i=w[f++],a=w[f++],y=w[f++],p=w[f++];if((i&128)>0||(a&128)>0||(y&128)>0||(p&128)>0){f-=8;return}if(t<10){if(t===8)return V(e,r,s,n,i,a,y,p);{let A=w[f++];if((A&128)>0){f-=9;return}return V(e,r,s,n,i,a,y,p,A)}}else if(t<12){let A=w[f++],E=w[f++];if((A&128)>0||(E&128)>0){f-=10;return}if(t<11)return V(e,r,s,n,i,a,y,p,A,E);let R=w[f++];if((R&128)>0){f-=11;return}return V(e,r,s,n,i,a,y,p,A,E,R)}else{let A=w[f++],E=w[f++],R=w[f++],M=w[f++];if((A&128)>0||(E&128)>0||(R&128)>0||(M&128)>0){f-=12;return}if(t<14){if(t===12)return V(e,r,s,n,i,a,y,p,A,E,R,M);{let N=w[f++];if((N&128)>0){f-=13;return}return V(e,r,s,n,i,a,y,p,A,E,R,M,N)}}else{let N=w[f++],_=w[f++];if((N&128)>0||(_&128)>0){f-=14;return}if(t<15)return V(e,r,s,n,i,a,y,p,A,E,R,M,N,_);let L=w[f++];if((L&128)>0){f-=15;return}return V(e,r,s,n,i,a,y,p,A,E,R,M,N,_,L)}}}}}function sn(t){return O.copyBuffers?Uint8Array.prototype.slice.call(w,f,f+=t):w.subarray(f,f+=t)}let er=new Float32Array(1),$e=new Uint8Array(er.buffer,0,4);function nn(){let t=w[f++],e=w[f++],r=(t&127)>>2;if(r===31)return e||t&3?NaN:t&128?-1/0:1/0;if(r===0){let s=((t&3)<<8|e)/16777216;return t&128?-s:s}return $e[3]=t&128|(r>>1)+56,$e[2]=(t&7)<<5|e>>3,$e[1]=e<<5,$e[0]=0,er[0]}new Array(4096);class fe{constructor(e,r){this.value=e,this.tag=r}}F[0]=t=>new Date(t),F[1]=t=>new Date(Math.round(t*1e3)),F[2]=t=>{let e=BigInt(0);for(let r=0,s=t.byteLength;r<s;r++)e=BigInt(t[r])+(e<<BigInt(8));return e},F[3]=t=>BigInt(-1)-F[2](t),F[4]=t=>+(t[1]+"e"+t[0]),F[5]=t=>t[1]*Math.exp(t[0]*Math.log(2));const lt=(t,e)=>{t=t-57344;let r=B[t];r&&r.isShared&&((B.restoreStructures||(B.restoreStructures=[]))[t]=r),B[t]=e,e.read=ct(e)};F[Ys]=t=>{let e=t.length,r=t[1];lt(t[0],r);let s={};for(let n=2;n<e;n++){let i=r[n-2];s[Z(i)]=t[n]}return s},F[14]=t=>j?j[0].slice(j.position0,j.position0+=t):new fe(t,14),F[15]=t=>j?j[1].slice(j.position1,j.position1+=t):new fe(t,15);let on={Error,RegExp};F[27]=t=>(on[t[0]]||Error)(t[1],t[2]);const tr=t=>{if(!(f<D))throw J();if(w[f++]!=132)throw new Error("Packed values structure must be followed by a 4 element array");let e=t();if(!Array.isArray(e)||!e.length)throw new Error("Packed values structure must be followed by a 4 element array");return W=W?e.concat(W.slice(e.length)):e,W.prefixes=t(),W.suffixes=t(),t()};tr.handlesRead=!0,F[51]=tr,F[Jt]=t=>{if(!W)if(O.getShared)ft();else return new fe(t,Jt);if(typeof t=="number")return W[16+(t>=0?2*t:-2*t-1)];throw new Error("No support for non-integer packed references yet")},F[28]=t=>{H||(H=new Map,H.id=0);let e=H.id++,r=f,s=w[f],n;s>>5==4?n=[]:n={};let i={target:n};H.set(e,i);let a=t();return i.used?(Object.getPrototypeOf(n)!==Object.getPrototypeOf(a)&&(f=r,n=a,H.set(e,{target:n}),a=t()),Object.assign(n,a)):(i.target=a,a)},F[28].handlesRead=!0,F[29]=t=>{let e=H.get(t);return e.used=!0,e.target},F[258]=t=>new Set(t),(F[259]=t=>{let e=O;if(e.mapsAsObjects){e.mapsAsObjects=!1,X=!0;try{return t()}finally{X&&(e.mapsAsObjects=!0,X=!1)}}return t()}).handlesRead=!0;function ge(t,e){return typeof t=="string"?t+e:t instanceof Array?t.concat(e):Object.assign({},t,e)}function ue(){if(!W)if(O.getShared)ft();else throw new Error("No packed values available");return W}const an=1399353956;at.push((t,e)=>{if(t>=225&&t<=255)return ge(ue().prefixes[t-224],e);if(t>=28704&&t<=32767)return ge(ue().prefixes[t-28672],e);if(t>=1879052288&&t<=2147483647)return ge(ue().prefixes[t-1879048192],e);if(t>=216&&t<=223)return ge(e,ue().suffixes[t-216]);if(t>=27647&&t<=28671)return ge(e,ue().suffixes[t-27639]);if(t>=1811940352&&t<=1879048191)return ge(e,ue().suffixes[t-1811939328]);if(t==an)return{packedValues:W,structures:B.slice(0),version:e};if(t==55799)return e});const cn=new Uint8Array(new Uint16Array([1]).buffer)[0]==1,rr=[Uint8Array,Uint8ClampedArray,Uint16Array,Uint32Array,typeof BigUint64Array>"u"?{name:"BigUint64Array"}:BigUint64Array,Int8Array,Int16Array,Int32Array,typeof BigInt64Array>"u"?{name:"BigInt64Array"}:BigInt64Array,Float32Array,Float64Array],ln=[64,68,69,70,71,72,77,78,79,85,86];for(let t=0;t<rr.length;t++)fn(rr[t],ln[t]);function fn(t,e){let r="get"+t.name.slice(0,-5),s;typeof t=="function"?s=t.BYTES_PER_ELEMENT:t=null;for(let n=0;n<2;n++){if(!n&&s==1)continue;let i=s==2?1:s==4?2:s==8?3:0;F[n?e:e-4]=s==1||n==cn?a=>{if(!t)throw new Error("Could not find typed array for code "+e);return!O.copyBuffers&&(s===1||s===2&&!(a.byteOffset&1)||s===4&&!(a.byteOffset&3)||s===8&&!(a.byteOffset&7))?new t(a.buffer,a.byteOffset,a.byteLength>>i):new t(Uint8Array.prototype.slice.call(a,0).buffer)}:a=>{if(!t)throw new Error("Could not find typed array for code "+e);let y=new DataView(a.buffer,a.byteOffset,a.byteLength),p=a.length>>i,A=new t(p),E=y[r];for(let R=0;R<p;R++)A[R]=E.call(y,R<<i,n);return A}}}function un(){let t=me(),e=f+I();for(let s=2;s<t;s++){let n=me();f+=n}let r=f;return f=e,j=[Fe(me()),Fe(me())],j.position0=0,j.position1=0,j.postBundlePosition=f,f=r,I()}function me(){if(!(f<D))throw J();let t=w[f++]&31;if(t>23)switch(t){case 24:if(f>=D)throw J();t=w[f++];break;case 25:if(f+2>D)throw J();t=K.getUint16(f),f+=2;break;case 26:if(f+4>D)throw J();t=K.getUint32(f),f+=4;break}return t}function ft(){if(O.getShared){let t=sr(()=>(w=null,O.getShared()))||{},e=t.structures||[];O.sharedVersion=t.version,W=O.sharedValues=t.packedValues,B===!0?O.structures=B=e:B.splice.apply(B,[0,e.length].concat(e))}}function sr(t){let e=D,r=f,s=Oe,n=Te,i=le,a=Ie,y=ce,p=H,A=j,E=new Uint8Array(w.slice(0,D)),R=B,M=O,N=Re,_=X,L=Be;X=!1;try{return t()}finally{D=e,f=r,Oe=s,Te=n,le=i,Ie=a,ce=y,H=p,j=A,w=E,Re=N,B=R,O=M,X=_,Be=L,K=new DataView(w.buffer,w.byteOffset,w.byteLength)}}function ut(){w=null,H=null,B=null}const dt=new Array(147);for(let t=0;t<256;t++)dt[t]=+("1e"+Math.floor(45.15-t*.30103));let ht=new Se({useRecords:!1});const dn=ht.decode;ht.decodeMultiple;let Ve;try{Ve=new TextEncoder}catch{}let pt,nr;const qe=typeof globalThis=="object"&&globalThis.Buffer,Me=typeof qe<"u",yt=Me?qe.allocUnsafeSlow:Uint8Array,ir=Me?qe:Uint8Array,or=256,ar=Me?4294967296:2144337920;let wt,l,P,o=0,se,$=null;const hn=61440,pn=/[\u0080-\uFFFF]/,G=Symbol("record-id");class cr extends Se{constructor(e){super(e),this.offset=0;let r,s,n,i,a;e=e||{};let y=ir.prototype.utf8Write?function(c,g){return l.utf8Write(c,g,l.byteLength-g)}:Ve&&Ve.encodeInto?function(c,g){return Ve.encodeInto(c,l.subarray(g)).written}:!1,p=this,A=e.structures||e.saveStructures,E=e.maxSharedStructures;if(E==null&&(E=A?128:0),E>8190)throw new Error("Maximum maxSharedStructure is 8190");let R=e.sequential;R&&(E=0),this.structures||(this.structures=[]),this.saveStructures&&(this.saveShared=this.saveStructures);let M,N,_=e.sharedValues,L;if(_){L=Object.create(null);for(let c=0,g=_.length;c<g;c++)L[_[c]]=c}let Y=[],ve=0,Q=0;this.mapEncode=function(c,g){return this._keyMap&&!this._mapped&&c.constructor.name==="Array"&&(c=c.map(d=>this.encodeKeys(d))),this.encode(c,g)},this.encode=function(c,g){if(l||(l=new yt(8192),P=new DataView(l.buffer,0,8192),o=0),se=l.length-10,se-o<2048?(l=new yt(l.length),P=new DataView(l.buffer,0,l.length),se=l.length-10,o=0):g===dr&&(o=o+7&2147483640),r=o,p.useSelfDescribedHeader&&(P.setUint32(o,3654940416),o+=3),a=p.structuredClone?new Map:null,p.bundleStrings&&typeof c!="string"?($=[],$.size=1/0):$=null,s=p.structures,s){if(s.uninitialized){let h=p.getShared()||{};p.structures=s=h.structures||[],p.sharedVersion=h.version;let u=p.sharedValues=h.packedValues;if(u){L={};for(let b=0,S=u.length;b<S;b++)L[u[b]]=b}}let d=s.length;if(d>E&&!R&&(d=E),!s.transitions){s.transitions=Object.create(null);for(let h=0;h<d;h++){let u=s[h];if(!u)continue;let b,S=s.transitions;for(let v=0,x=u.length;v<x;v++){S[G]===void 0&&(S[G]=h);let T=u[v];b=S[T],b||(b=S[T]=Object.create(null)),S=b}S[G]=h|1048576}}R||(s.nextId=d)}if(n&&(n=!1),i=s||[],N=L,e.pack){let d=new Map;if(d.values=[],d.encoder=p,d.maxValues=e.maxPrivatePackedValues||(L?16:1/0),d.objectMap=L||!1,d.samplingPackedValues=M,Ke(c,d),d.values.length>0){l[o++]=216,l[o++]=51,ee(4);let h=d.values;k(h),ee(0),ee(0),N=Object.create(L||null);for(let u=0,b=h.length;u<b;u++)N[h[u]]=u}}wt=g&Et;try{if(wt)return;if(k(c),$&&$.position&&ur(r,k),p.offset=o,a&&a.idsToInsert){o+=a.idsToInsert.length*2,o>se&&de(o),p.offset=o;let d=gn(l.subarray(r,o),a.idsToInsert);return a=null,d}return g&dr?(l.start=r,l.end=o,l):l.subarray(r,o)}finally{if(s){if(Q<10&&Q++,s.length>E&&(s.length=E),ve>1e4)s.transitions=null,Q=0,ve=0,Y.length>0&&(Y=[]);else if(Y.length>0&&!R){for(let d=0,h=Y.length;d<h;d++)Y[d][G]=void 0;Y=[]}}if(n&&p.saveShared){p.structures.length>E&&(p.structures=p.structures.slice(0,E));let d=l.subarray(r,o);return p.updateSharedData()===!1?p.encode(c):d}g&bn&&(o=r)}},this.findCommonStringsToPack=()=>(M=new Map,L||(L=Object.create(null)),c=>{let g=c&&c.threshold||4,d=this.pack?c.maxPrivatePackedValues||16:0;_||(_=this.sharedValues=[]);for(let[h,u]of M)u.count>g&&(L[h]=d++,_.push(h),n=!0);for(;this.saveShared&&this.updateSharedData()===!1;);M=null});const k=c=>{o>se&&(l=de(o));var g=typeof c,d;if(g==="string"){if(N){let S=N[c];if(S>=0){S<16?l[o++]=S+224:(l[o++]=198,S&1?k(15-S>>1):k(S-16>>1));return}else if(M&&!e.pack){let v=M.get(c);v?v.count++:M.set(c,{count:1})}}let h=c.length;if($&&h>=4&&h<1024){if(($.size+=h)>hn){let v,x=($[0]?$[0].length*3+$[1].length:0)+10;o+x>se&&(l=de(o+x)),l[o++]=217,l[o++]=223,l[o++]=249,l[o++]=$.position?132:130,l[o++]=26,v=o-r,o+=4,$.position&&ur(r,k),$=["",""],$.size=0,$.position=v}let S=pn.test(c);$[S?0:1]+=c,l[o++]=S?206:207,k(h);return}let u;h<32?u=1:h<256?u=2:h<65536?u=3:u=5;let b=h*3;if(o+b>se&&(l=de(o+b)),h<64||!y){let S,v,x,T=o+u;for(S=0;S<h;S++)v=c.charCodeAt(S),v<128?l[T++]=v:v<2048?(l[T++]=v>>6|192,l[T++]=v&63|128):(v&64512)===55296&&((x=c.charCodeAt(S+1))&64512)===56320?(v=65536+((v&1023)<<10)+(x&1023),S++,l[T++]=v>>18|240,l[T++]=v>>12&63|128,l[T++]=v>>6&63|128,l[T++]=v&63|128):(l[T++]=v>>12|224,l[T++]=v>>6&63|128,l[T++]=v&63|128);d=T-o-u}else d=y(c,o+u,b);d<24?l[o++]=96|d:d<256?(u<2&&l.copyWithin(o+2,o+1,o+1+d),l[o++]=120,l[o++]=d):d<65536?(u<3&&l.copyWithin(o+3,o+2,o+2+d),l[o++]=121,l[o++]=d>>8,l[o++]=d&255):(u<5&&l.copyWithin(o+5,o+3,o+3+d),l[o++]=122,P.setUint32(o,d),o+=4),o+=d}else if(g==="number")if(!this.alwaysUseFloat&&c>>>0===c)c<24?l[o++]=c:c<256?(l[o++]=24,l[o++]=c):c<65536?(l[o++]=25,l[o++]=c>>8,l[o++]=c&255):(l[o++]=26,P.setUint32(o,c),o+=4);else if(!this.alwaysUseFloat&&c>>0===c)c>=-24?l[o++]=31-c:c>=-256?(l[o++]=56,l[o++]=~c):c>=-65536?(l[o++]=57,P.setUint16(o,~c),o+=2):(l[o++]=58,P.setUint32(o,~c),o+=4);else if(!this.alwaysUseFloat&&c<0&&c>=-4294967296&&Math.floor(c)===c)l[o++]=58,P.setUint32(o,-1-c),o+=4;else{let h;if((h=this.useFloat32)>0&&c<4294967296&&c>=-2147483648){l[o++]=250,P.setFloat32(o,c);let u;if(h<4||(u=c*dt[(l[o]&127)<<1|l[o+1]>>7])>>0===u){o+=4;return}else o--}l[o++]=251,P.setFloat64(o,c),o+=8}else if(g==="object")if(!c)l[o++]=246;else{if(a){let u=a.get(c);if(u){if(l[o++]=216,l[o++]=29,l[o++]=25,!u.references){let b=a.idsToInsert||(a.idsToInsert=[]);u.references=[],b.push(u)}u.references.push(o-r),o+=2;return}else a.set(c,{offset:o-r})}let h=c.constructor;if(h===Object)this.skipFunction===!0&&(c=Object.fromEntries([...Object.keys(c).filter(u=>typeof c[u]!="function").map(u=>[u,c[u]])])),q(c);else if(h===Array){d=c.length,d<24?l[o++]=128|d:ee(d);for(let u=0;u<d;u++)k(c[u])}else if(h===Map)if((this.mapsAsObjects?this.useTag259ForMaps!==!1:this.useTag259ForMaps)&&(l[o++]=217,l[o++]=1,l[o++]=3),d=c.size,d<24?l[o++]=160|d:d<256?(l[o++]=184,l[o++]=d):d<65536?(l[o++]=185,l[o++]=d>>8,l[o++]=d&255):(l[o++]=186,P.setUint32(o,d),o+=4),p.keyMap)for(let[u,b]of c)k(p.encodeKey(u)),k(b);else for(let[u,b]of c)k(u),k(b);else{for(let u=0,b=pt.length;u<b;u++){let S=nr[u];if(c instanceof S){let v=pt[u],x=v.tag;x==null&&(x=v.getTag&&v.getTag.call(this,c)),x<24?l[o++]=192|x:x<256?(l[o++]=216,l[o++]=x):x<65536?(l[o++]=217,l[o++]=x>>8,l[o++]=x&255):x>-1&&(l[o++]=218,P.setUint32(o,x),o+=4),v.encode.call(this,c,k,de);return}}if(c[Symbol.iterator]){if(wt){let u=new Error("Iterable should be serialized as iterator");throw u.iteratorNotHandled=!0,u}l[o++]=159;for(let u of c)k(u);l[o++]=255;return}if(c[Symbol.asyncIterator]||gt(c)){let u=new Error("Iterable/blob should be serialized as iterator");throw u.iteratorNotHandled=!0,u}if(this.useToJSON&&c.toJSON){const u=c.toJSON();if(u!==c)return k(u)}q(c)}}else if(g==="boolean")l[o++]=c?245:244;else if(g==="bigint"){if(c<BigInt(1)<<BigInt(64)&&c>=0)l[o++]=27,P.setBigUint64(o,c);else if(c>-(BigInt(1)<<BigInt(64))&&c<0)l[o++]=59,P.setBigUint64(o,-c-BigInt(1));else if(this.largeBigIntToFloat)l[o++]=251,P.setFloat64(o,Number(c));else{c>=BigInt(0)?l[o++]=194:(l[o++]=195,c=BigInt(-1)-c);let h=[];for(;c;)h.push(Number(c&BigInt(255))),c>>=BigInt(8);mt(new Uint8Array(h.reverse()),de);return}o+=8}else if(g==="undefined")l[o++]=247;else throw new Error("Unknown type: "+g)},q=this.useRecords===!1?this.variableMapSize?c=>{let g=Object.keys(c),d=Object.values(c),h=g.length;if(h<24?l[o++]=160|h:h<256?(l[o++]=184,l[o++]=h):h<65536?(l[o++]=185,l[o++]=h>>8,l[o++]=h&255):(l[o++]=186,P.setUint32(o,h),o+=4),p.keyMap)for(let u=0;u<h;u++)k(p.encodeKey(g[u])),k(d[u]);else for(let u=0;u<h;u++)k(g[u]),k(d[u])}:c=>{l[o++]=185;let g=o-r;o+=2;let d=0;if(p.keyMap)for(let h in c)(typeof c.hasOwnProperty!="function"||c.hasOwnProperty(h))&&(k(p.encodeKey(h)),k(c[h]),d++);else for(let h in c)(typeof c.hasOwnProperty!="function"||c.hasOwnProperty(h))&&(k(h),k(c[h]),d++);l[g+++r]=d>>8,l[g+r]=d&255}:(c,g)=>{let d,h=i.transitions||(i.transitions=Object.create(null)),u=0,b=0,S,v;if(this.keyMap){v=Object.keys(c).map(T=>this.encodeKey(T)),b=v.length;for(let T=0;T<b;T++){let Ut=v[T];d=h[Ut],d||(d=h[Ut]=Object.create(null),u++),h=d}}else for(let T in c)(typeof c.hasOwnProperty!="function"||c.hasOwnProperty(T))&&(d=h[T],d||(h[G]&1048576&&(S=h[G]&65535),d=h[T]=Object.create(null),u++),h=d,b++);let x=h[G];if(x!==void 0)x&=65535,l[o++]=217,l[o++]=x>>8|224,l[o++]=x&255;else if(v||(v=h.__keys__||(h.__keys__=Object.keys(c))),S===void 0?(x=i.nextId++,x||(x=0,i.nextId=1),x>=or&&(i.nextId=(x=E)+1)):x=S,i[x]=v,x<E){l[o++]=217,l[o++]=x>>8|224,l[o++]=x&255,h=i.transitions;for(let T=0;T<b;T++)(h[G]===void 0||h[G]&1048576)&&(h[G]=x),h=h[v[T]];h[G]=x|1048576,n=!0}else{if(h[G]=x,P.setUint32(o,3655335680),o+=3,u&&(ve+=Q*u),Y.length>=or-E&&(Y.shift()[G]=void 0),Y.push(h),ee(b+2),k(57344+x),k(v),g)return;for(let T in c)(typeof c.hasOwnProperty!="function"||c.hasOwnProperty(T))&&k(c[T]);return}if(b<24?l[o++]=128|b:ee(b),!g)for(let T in c)(typeof c.hasOwnProperty!="function"||c.hasOwnProperty(T))&&k(c[T])},de=c=>{let g;if(c>16777216){if(c-r>ar)throw new Error("Encoded buffer would be larger than maximum buffer size");g=Math.min(ar,Math.round(Math.max((c-r)*(c>67108864?1.25:2),4194304)/4096)*4096)}else g=(Math.max(c-r<<2,l.length-1)>>12)+1<<12;let d=new yt(g);return P=new DataView(d.buffer,0,g),l.copy?l.copy(d,0,r,c):d.set(l.slice(r,c)),o-=r,r=0,se=d.length-10,l=d};let ie=100,Tt=1e3;this.encodeAsIterable=function(c,g){return Rt(c,g,he)},this.encodeAsAsyncIterable=function(c,g){return Rt(c,g,Mt)};function*he(c,g,d){let h=c.constructor;if(h===Object){let u=p.useRecords!==!1;u?q(c,!0):lr(Object.keys(c).length,160);for(let b in c){let S=c[b];u||k(b),S&&typeof S=="object"?g[b]?yield*he(S,g[b]):yield*He(S,g,b):k(S)}}else if(h===Array){let u=c.length;ee(u);for(let b=0;b<u;b++){let S=c[b];S&&(typeof S=="object"||o-r>ie)?g.element?yield*he(S,g.element):yield*He(S,g,"element"):k(S)}}else if(c[Symbol.iterator]&&!c.buffer){l[o++]=159;for(let u of c)u&&(typeof u=="object"||o-r>ie)?g.element?yield*he(u,g.element):yield*He(u,g,"element"):k(u);l[o++]=255}else gt(c)?(lr(c.size,64),yield l.subarray(r,o),yield c,Ae()):c[Symbol.asyncIterator]?(l[o++]=159,yield l.subarray(r,o),yield c,Ae(),l[o++]=255):k(c);d&&o>r?yield l.subarray(r,o):o-r>ie&&(yield l.subarray(r,o),Ae())}function*He(c,g,d){let h=o-r;try{k(c),o-r>ie&&(yield l.subarray(r,o),Ae())}catch(u){if(u.iteratorNotHandled)g[d]={},o=r+h,yield*he.call(this,c,g[d]);else throw u}}function Ae(){ie=Tt,p.encode(null,Et)}function Rt(c,g,d){return g&&g.chunkThreshold?ie=Tt=g.chunkThreshold:ie=100,c&&typeof c=="object"?(p.encode(null,Et),d(c,p.iterateProperties||(p.iterateProperties={}),!0)):[p.encode(c)]}async function*Mt(c,g){for(let d of he(c,g,!0)){let h=d.constructor;if(h===ir||h===Uint8Array)yield d;else if(gt(d)){let u=d.stream().getReader(),b;for(;!(b=await u.read()).done;)yield b.value}else if(d[Symbol.asyncIterator])for await(let u of d)Ae(),u?yield*Mt(u,g.async||(g.async={})):yield p.encode(u);else yield d}}}useBuffer(e){l=e,P=new DataView(l.buffer,l.byteOffset,l.byteLength),o=0}clearSharedData(){this.structures&&(this.structures=[]),this.sharedValues&&(this.sharedValues=void 0)}updateSharedData(){let e=this.sharedVersion||0;this.sharedVersion=e+1;let r=this.structures.slice(0),s=new fr(r,this.sharedValues,this.sharedVersion),n=this.saveShared(s,i=>(i&&i.version||0)==e);return n===!1?(s=this.getShared()||{},this.structures=s.structures||[],this.sharedValues=s.packedValues,this.sharedVersion=s.version,this.structures.nextId=this.structures.length):r.forEach((i,a)=>this.structures[a]=i),n}}function lr(t,e){t<24?l[o++]=e|t:t<256?(l[o++]=e|24,l[o++]=t):t<65536?(l[o++]=e|25,l[o++]=t>>8,l[o++]=t&255):(l[o++]=e|26,P.setUint32(o,t),o+=4)}class fr{constructor(e,r,s){this.structures=e,this.packedValues=r,this.version=s}}function ee(t){t<24?l[o++]=128|t:t<256?(l[o++]=152,l[o++]=t):t<65536?(l[o++]=153,l[o++]=t>>8,l[o++]=t&255):(l[o++]=154,P.setUint32(o,t),o+=4)}const yn=typeof Blob>"u"?function(){}:Blob;function gt(t){if(t instanceof yn)return!0;let e=t[Symbol.toStringTag];return e==="Blob"||e==="File"}function Ke(t,e){switch(typeof t){case"string":if(t.length>3){if(e.objectMap[t]>-1||e.values.length>=e.maxValues)return;let s=e.get(t);if(s)++s.count==2&&e.values.push(t);else if(e.set(t,{count:1}),e.samplingPackedValues){let n=e.samplingPackedValues.get(t);n?n.count++:e.samplingPackedValues.set(t,{count:1})}}break;case"object":if(t)if(t instanceof Array)for(let s=0,n=t.length;s<n;s++)Ke(t[s],e);else{let s=!e.encoder.useRecords;for(var r in t)t.hasOwnProperty(r)&&(s&&Ke(r,e),Ke(t[r],e))}break;case"function":console.log(t)}}const wn=new Uint8Array(new Uint16Array([1]).buffer)[0]==1;nr=[Date,Set,Error,RegExp,fe,ArrayBuffer,Uint8Array,Uint8ClampedArray,Uint16Array,Uint32Array,typeof BigUint64Array>"u"?function(){}:BigUint64Array,Int8Array,Int16Array,Int32Array,typeof BigInt64Array>"u"?function(){}:BigInt64Array,Float32Array,Float64Array,fr],pt=[{tag:1,encode(t,e){let r=t.getTime()/1e3;(this.useTimestamp32||t.getMilliseconds()===0)&&r>=0&&r<4294967296?(l[o++]=26,P.setUint32(o,r),o+=4):(l[o++]=251,P.setFloat64(o,r),o+=8)}},{tag:258,encode(t,e){let r=Array.from(t);e(r)}},{tag:27,encode(t,e){e([t.name,t.message])}},{tag:27,encode(t,e){e(["RegExp",t.source,t.flags])}},{getTag(t){return t.tag},encode(t,e){e(t.value)}},{encode(t,e,r){mt(t,r)}},{getTag(t){if(t.constructor===Uint8Array&&(this.tagUint8Array||Me&&this.tagUint8Array!==!1))return 64},encode(t,e,r){mt(t,r)}},te(68,1),te(69,2),te(70,4),te(71,8),te(72,1),te(77,2),te(78,4),te(79,8),te(85,4),te(86,8),{encode(t,e){let r=t.packedValues||[],s=t.structures||[];if(r.values.length>0){l[o++]=216,l[o++]=51,ee(4);let n=r.values;e(n),ee(0),ee(0),packedObjectMap=Object.create(sharedPackedObjectMap||null);for(let i=0,a=n.length;i<a;i++)packedObjectMap[n[i]]=i}if(s){P.setUint32(o,3655335424),o+=3;let n=s.slice(0);n.unshift(57344),n.push(new fe(t.version,1399353956)),e(n)}else e(new fe(t.version,1399353956))}}];function te(t,e){return!wn&&e>1&&(t-=4),{tag:t,encode:function(s,n){let i=s.byteLength,a=s.byteOffset||0,y=s.buffer||s;n(Me?qe.from(y,a,i):new Uint8Array(y,a,i))}}}function mt(t,e){let r=t.byteLength;r<24?l[o++]=64+r:r<256?(l[o++]=88,l[o++]=r):r<65536?(l[o++]=89,l[o++]=r>>8,l[o++]=r&255):(l[o++]=90,P.setUint32(o,r),o+=4),o+r>=l.length&&e(o+r),l.set(t.buffer?t:new Uint8Array(t),o),o+=r}function gn(t,e){let r,s=e.length*2,n=t.length-s;e.sort((i,a)=>i.offset>a.offset?1:-1);for(let i=0;i<e.length;i++){let a=e[i];a.id=i;for(let y of a.references)t[y++]=i>>8,t[y]=i&255}for(;r=e.pop();){let i=r.offset;t.copyWithin(i+s,i,n),s-=2;let a=i+s;t[a++]=216,t[a++]=28,n=i}return t}function ur(t,e){P.setUint32($.position+t,o-$.position-t+1);let r=$;$=null,e(r[0]),e(r[1])}let bt=new cr({useRecords:!1});const mn=bt.encode;bt.encodeAsIterable,bt.encodeAsAsyncIterable;const dr=512,bn=1024,Et=2048;class En extends Wr{constructor(e){e||(e={}),e.objectMode=!0,super(e),e.structures=[],this.decoder=e.decoder||new Se(e)}_transform(e,r,s){this.incompleteBuffer&&(e=Buffer.concat([this.incompleteBuffer,e]),this.incompleteBuffer=null);let n,i;try{n=this.decoder.decodeMultiple(e)}catch(a){n=a.values,a.incomplete?this.incompleteBuffer=e.slice(a.lastPosition):i=a}for(let a of n||[])a===null&&(a=this.getNullValue()),this.push(a);s(i)}getNullValue(){return Symbol.for(null)}}const Sn=process.env.CBOR_NATIVE_ACCELERATION_DISABLED!==void 0&&process.env.CBOR_NATIVE_ACCELERATION_DISABLED.toLowerCase()==="true";if(!Sn){let t;try{typeof require=="function"?t=require("cbor-extract"):t=Jr(import.meta.url)("cbor-extract"),t&&tn(t.extractStrings)}catch{}}const hr=ns,pr=is;function vn(t){return{adbShellCommand:t.android?.shell,appearance:t.appearance,deviceType:t.id,disableVirtualKeyboard:t.android?.softKeyboard===!1?!0:void 0,fontSize:t.fontSize,iosAutocorrect:t.ios?.autocorrect,iosKeyboard:t.ios?.keyboards,language:t.locale,locale:t.locale,location:t.location&&[t.location.latitude,t.location.longitude],orientation:t.orientation,osVersion:t.osVersion,timezone:t.timezone}}const yr=["grant"];function An(t){return{grantPermissions:t?.permissions==="grant"?!0:void 0,launchApp:t?.app,launchArgs:t?.ios?.args,launchUrl:t?.url,params:t?.params}}class wr extends Error{code;issues;constructor(e,r,s=[]){super(r),this.name="SessionRequestError",this.code=e,this.issues=s}}const C=t=>`expected ${t}`,be=t=>m.string(C(t)).check(m.minLength(1,C(t))),Ue=t=>m.literal(t,C(`one of ${t.join(", ")}`)),Ee=t=>m.object(t,C("an object")),gr=()=>m.boolean(C("true or false")),mr=t=>m.number(C("a number")).check(m.gte(-t,C(`at least -${String(t)}`)),m.lte(t,C(`at most ${String(t)}`))),xn=/^\w{2}(?:_\w{2})?@sw(=[\w-]+)?$/,kn=/^[a-z0-9_.=:/ ()"-]+$/i,On='letters, digits or _ . = : / ( ) " - and spaces',In=/^[A-Za-z]{2,3}([-_][A-Za-z0-9]{2,8})*$/;function Tn(t){if(!In.test(t))return!1;try{return new Intl.Locale(t.replaceAll("_","-")),!0}catch{return!1}}function Rn(t){try{return new Intl.DateTimeFormat(void 0,{timeZone:t}),!0}catch{return!1}}const St={code:"needsTarget"},br={code:"needsLaunchApp"},vt="has no effect without a target",Er="has no effect without a target, or with launch.app: false";function Sr({launch:t}){return t?.app!==!1}const Mn=m.object({apiToken:m.optional(be("an API token")),codec:m.optional(Ue(ss)),device:Ee({android:m.optional(Ee({shell:m.optional(be("a shell command")),softKeyboard:m.optional(gr())})),appearance:m.optional(Ue(hr)),fontSize:m.optional(Ue(pr)),id:be("a device id"),ios:m.optional(Ee({autocorrect:m.optional(gr()),keyboards:m.optional(m.array(m.string(C("a keyboard layout")).check(m.regex(xn,C("a layout like en_US@sw=QWERTY"))),C("a list of keyboard layouts")))})),locale:m.optional(m.string(C("a BCP 47 tag, e.g. fr-CA")).check(m.refine(Tn,{error:C("a BCP 47 tag, e.g. fr-CA")}))),location:m.optional(Ee({latitude:mr(90),longitude:mr(180)})),orientation:m.optional(Ue(zt)),osVersion:m.optional(be("an OS version")),timezone:m.optional(m.string(C("an IANA timezone")).check(m.refine(Rn,{error:C("an IANA timezone, e.g. America/Toronto")})))}),endpoint:m.optional(m.url(C("a URL"))),launch:m.optional(Ee({app:m.optional(m.union([be("an app id"),m.literal(!1)],C("an app id or false"))),ios:m.optional(Ee({args:m.optional(m.array(m.string(C("an argument")).check(m.regex(kn,C(On))),C("a list of arguments")).check(m.maxLength(20,C("at most 20 arguments"))))})),params:m.optional(m.custom(t=>typeof t=="object"&&t!==null&&!Array.isArray(t),C("an object"))),permissions:m.optional(Ue(yr)),url:m.optional(m.url(C("a URL")))})),proxy:m.optional(m.union([m.literal("intercept"),m.templateLiteral(["http://",m.string()])],C("intercept or an http:// URL"))),target:m.optional(be("an app, build or group id"))}).check(m.refine(t=>t.device.android?.shell===void 0||t.target!==void 0,{error:vt,params:St,path:["device","android","shell"]}),m.refine(t=>t.launch?.app===void 0||t.target!==void 0,{error:vt,params:St,path:["launch","app"]}),m.refine(t=>t.launch?.permissions===void 0||t.target!==void 0,{error:vt,params:St,path:["launch","permissions"]}),m.refine(t=>t.launch?.params===void 0||t.target!==void 0&&Sr(t),{error:Er,params:br,path:["launch","params"]}),m.refine(t=>t.launch?.ios?.args===void 0||t.target!==void 0&&Sr(t),{error:Er,params:br,path:["launch","ios","args"]}));function Un(t){if(t.code==="custom"){const e=t.params?.code;if(e==="needsTarget"||e==="needsLaunchApp")return e}return"invalidValue"}function Ln(t){const e=Mn.safeParse(t);if(e.success)return;const r=e.error.issues.map(s=>({code:Un(s),message:s.message,path:s.path.join(".")}));throw new wr("invalid",r.map(({message:s,path:n})=>`${n}: ${s}`).join(`
2
+ `),r)}const Pn={dateStyle:"medium",timeStyle:"short"},Cn=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;function We(t){return Array.isArray(t)?t.map(We).join(", "):typeof t=="boolean"?t?"yes":"no":typeof t=="number"?t.toLocaleString():_n(t)}function _n(t){if(Cn.test(t)){const r=new Date(t);if(!Number.isNaN(r.getTime()))return r.toLocaleString(void 0,Pn)}const e=Pt();return t===e?"~":t.startsWith(`${e}${Kr}`)?`~${t.slice(e.length)}`:t}function vr(){return process.env.APPETIZE_CLI_HOME??oe(Pt(),".appetize","cli","sessions")}function At(t){return oe(vr(),t)}class Je extends Error{details;constructor(e,r={},s){super(e,s===void 0?void 0:{cause:s}),this.name="CommandError",this.details=r}hints(e){return[]}}class Pe extends Je{sessionDir;constructor(e,{cause:r,details:s={},sessionId:n}){const i=At(n);super(e,Object.assign({sessionDir:i},s),r),this.name="SessionError",this.sessionDir=i}static wrap(e,r){if(e instanceof Pe||e instanceof ne)return e;const s=e instanceof Error?e.message:String(e),n=e instanceof Je?e.details:{};return new Pe(s,{cause:e,details:n,sessionId:r})}hints(){return[`Check the session logs for details: ${We(this.sessionDir)}`]}}class Nn extends Pe{constructor(e){super(`Session ${e} already exists`,{sessionId:e}),this.name="SessionAlreadyExistsError"}hints(){return[`Pass --force to replace it, deleting ${We(this.sessionDir)}`]}}class ne extends Je{#e;constructor(e,r=[]){super(e),this.name="UsageError",this.#e=r}hints({command:e}){return[...this.#e,`Run 'appetize ${e} --help' for usage`]}}function zn(t,e,r){const s=e instanceof Error?e.message:String(e);if(!(e instanceof Je))return t==="json"?JSON.stringify({error:{message:s}},null,2):`\u2716 ${s}`;const n=e.hints(r);if(t==="json"){const i=n.length>0?{hints:n}:{};return JSON.stringify({error:{message:s,...e.details,...i}},null,2)}return[`\u2716 ${s}`,...n.map(i=>` ${i}`)].join(`
3
+ `)}function xt(t){try{return process.kill(t,0),!0}catch{return!1}}const Dn=["SIGINT","SIGTERM"],Bn=150;function jn(t){const e=new AbortController,r=setInterval(()=>{xt(t)||(clearInterval(r),e.abort(new Error("The device session exited")))},Bn);return r.unref(),e.signal}function Ar(){const t=new AbortController;for(const e of Dn)process.once(e,()=>{t.abort(new Error(`Received ${e}`))});return t.signal}function Fn(t,e){return new Promise((r,s)=>{if(e.aborted){s(xr(e.reason));return}const n=()=>{clearTimeout(i),s(xr(e.reason))},i=setTimeout(()=>{e.removeEventListener("abort",n),r()},t);e.addEventListener("abort",n,{once:!0})})}function xr(t){return t instanceof Error?t:new Error(String(t))}function $n(t){return Object.values(t).some(e=>e!==void 0)?t:void 0}function Vn(t,{defaultValue:e=!1}={}){return t===!e?!e:void 0}function qn(t,e,r){const s=Ge(t,r);if(s===void 0)return;const n=e.find(i=>i===s);if(n===void 0)throw new ne(`${r} expects one of ${e.join(", ")}`);return n}function Kn(t){const e=t.at(-1);return{options:Gn(e)?e:{},positionals:t.slice(0,-1).filter(r=>typeof r=="string"),signal:Ar()}}function Wn(t,e){const r=kr(t,e);return r===void 0?void 0:Or(r,e)}function kr(t,e){if(t===void 0)return;const r=typeof t=="boolean"?Number.NaN:Number(t);if(!Number.isFinite(r))throw new ne(`${e} expects a number, e.g., ${e}=-0.1`);return r}function Jn(t,e){return t===void 0?void 0:(Array.isArray(t)?t:[t]).map(s=>Ir(s,e))}function Ge(t,e){if(t!==void 0){if(typeof t!="string"||t==="")throw new ne(`${e} needs a value`);return t}}function Or(t,e){if(!(t>=0&&t<=1))throw new ne(`${e} expects a number between 0 and 1`);return t}function Ir(t,e){const r=Ge(t,e);if(r===void 0)throw new ne(`${e} needs a value`);return r}function Gn(t){return typeof t=="object"&&t!==null}const Hn="APPETIZEIO_FEATURE_FLAGS";function Zn(){const t={};for(const e of(process.env[Hn]??"").split(",")){const r=e.trim();r!==""&&(t[r]=!0)}return t}function Yn(){const t=process.env.APPETIZE_API_TOKEN;if(!t)throw new Error("APPETIZE_API_TOKEN must be set");return t}function Tr(t){return Ge(t.endpoint,"--endpoint")??Qn()}function Qn(){const t=process.env.APPETIZE_ENDPOINT?.trim();return t===""?void 0:t}const Xn="https://appetize.io";async function Rr(t,{authorize:e=!0,commandOptions:r={},query:s={},...n}={}){const i=new URL(t,Tr(r)??Xn);for(const[p,A]of Object.entries(s))i.searchParams.set(p,A);const a=new Headers(n.headers);e&&a.set("x-api-key",Yn());const y=await fetch(i,{...n,headers:a});if(!y.ok)throw new Error(await ti(y));return await y.json()}function ei(t){if(!(typeof t!="object"||t===null||!("message"in t)))return typeof t.message=="string"?t.message:void 0}async function ti(t){const e=`The webserver rejected the request (${String(t.status)})`,r=await t.json().catch(()=>{}),s=ei(r);return s?`${e}: ${s}`:e}const Mr=["android","ios"];function Ur(t={},e){return Rr("/api/v2/service/devices",{authorize:!1,commandOptions:t,signal:e})}function ri(t){return Mr.includes(t)}async function si(t,e={}){return(await Ur(e)).find(s=>s.id===t)?.platform}function ni(t){let e={...t,updatedAt:new Date().toISOString()};return{path:Ot(e.sessionId),save:r=>(e={...e,...r,updatedAt:new Date().toISOString()},di(e),e)}}function Lr(t){const e=t.state?.phase;return e==="ended"||e==="error"?!1:xt(t.pid)}function ii(){const t=vr();return Lt(t,{recursive:!0}),qr(t).map(e=>kt(e)).filter(e=>e!==void 0)}function oi(t){if(t!==void 0){const r=kt(t);if(!r)throw new Error(`No session found for id ${t}`);return r}const e=ii().filter(Lr);if(e.length===0)throw new Error("No active sessions");if(e.length>1)throw new ne("There are multiple active sessions",["Specify --session-id to pick one"]);return e[0]}function kt(t){try{return JSON.parse($r(Ot(t),"utf8"))}catch{return}}function ai(t){Vr(At(t),{force:!0,recursive:!0})}function ci(t){return oe(Le(t),"control.sock")}function Pr(t){return oe(Le(t),"daemon.log")}function li(t){return oe(Le(t),"device.log.jsonl")}function fi(t,e){return{daemon:Pr(t),device:li(t),...e==="intercept"?{network:ui(t)}:void 0}}function ui(t){return oe(Le(t),"network.jsonl")}function Le(t){const e=At(t);return Lt(e,{recursive:!0}),e}function Ot(t){return oe(Le(t),"state.json")}function di(t){const e=Ot(t.sessionId),r=`${e}.tmp`;jr(r,`${JSON.stringify(t,null,2)}
4
+ `),Fr(r,e)}const It="APPETIZE_DAEMON_CONFIG";function hi(){const t=process.env[It];if(!t)throw new Error(`${It} is not set`);return JSON.parse(t)}function pi(t){return JSON.stringify(t)}function yi(){let t,e;return{promise:new Promise((s,n)=>{e=s,t=n}),reject:t,resolve:e}}const Cr={useRecords:!1},wi=new cr(Cr);class _r extends xe{#e=[];#r;constructor(e,{signal:r}={}){super(async a=>{this.#r.write(wi.encode(a))},{signal:r}),this.#r=e;const s=new En(Cr);s.on("data",a=>{tt(a)&&this[U].dispatchMessage(a)}),s.on("error",()=>{this.close()});const n=a=>{s.write(a)},i=()=>{this.close()};e.on("data",n),e.once("close",i),e.once("error",i),this.#e.push(()=>{e.off("data",n),e.off("close",i),e.off("error",i),s.destroy()})}handleClose(e){for(const r of this.#e.splice(0))r();super.handleClose(e)}}function gi(t,e){const{promise:r,reject:s,resolve:n}=yi(),i=Gr(t),a=e?.signal,y=()=>{i.off("connect",E),i.off("error",R),a?.removeEventListener("abort",A)},p=M=>{y(),i.destroy(),s(M)},A=()=>{p(new Error("The connection was aborted"))},E=()=>{y();const M=new _r(i,e);M[U].addEventListener("close",()=>{i.destroy()},{once:!0}),n(M)},R=M=>{p(M)};return a?.aborted?(p(new Error("The connection was aborted")),r):(i.once("connect",E),i.once("error",R),a?.addEventListener("abort",A,{once:!0}),r)}export{_r as $,Tr as A,kt as B,Nn as C,It as D,ai as E,fi as F,Wn as G,We as H,gi as I,Js as J,_t as K,oi as L,Kn as M,zn as N,Xe as O,mn as P,U as Q,dn as R,Pe as S,xe as T,ne as U,tt as V,Kt as W,Ne as X,An as Y,vn as Z,Qe as _,Ge as a,hi as a0,ni as a1,si as a2,ci as a3,Ar as a4,kr as b,Ur as c,Fn as d,Mr as e,Rr as f,Vt as g,Ir as h,ri as i,zt as j,Pr as k,Lr as l,xt as m,jn as n,$n as o,Vn as p,Jn as q,Or as r,pi as s,qn as t,pr as u,hr as v,yr as w,Ln as x,wr as y,Zn as z};