agent-device 0.21.26 → 0.21.27

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.
Files changed (26) hide show
  1. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.26.apk → agent-device-android-ime-helper-0.21.27.apk} +0 -0
  2. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.27.apk.sha256 +1 -0
  3. package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.26.manifest.json → agent-device-android-ime-helper-0.21.27.manifest.json} +4 -4
  4. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.26.apk → agent-device-android-snapshot-helper-0.21.27.apk} +0 -0
  5. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.27.apk.sha256 +1 -0
  6. package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.26.manifest.json → agent-device-android-snapshot-helper-0.21.27.manifest.json} +7 -7
  7. package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +11 -4
  8. package/dist/src/app-log-reconnect.js +1 -1
  9. package/dist/src/app-log-runtime2.js +3 -3
  10. package/dist/src/cli-help.js +1 -1
  11. package/dist/src/client-types.d.ts +3 -3
  12. package/dist/src/command-metadata.js +4 -4
  13. package/dist/src/connection.js +5 -5
  14. package/dist/src/internal/bin.js +1 -1
  15. package/dist/src/internal/daemon.js +4 -4
  16. package/dist/src/ios-snapshot-adapter.js +1 -1
  17. package/dist/src/limrun.js +1 -1
  18. package/dist/src/proxy.js +2 -2
  19. package/dist/src/session-allocation.js +1 -1
  20. package/dist/src/session2.js +5 -5
  21. package/dist/src/src.js +1 -1
  22. package/dist/src/text-entry-focus.js +1 -1
  23. package/package.json +4 -1
  24. package/server.json +2 -2
  25. package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.26.apk.sha256 +0 -1
  26. package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.26.apk.sha256 +0 -1
package/dist/src/src.js CHANGED
@@ -2,4 +2,4 @@ import{r as e}from"./rolldown-runtime.js";import{P as t,V as n}from"./sdk-contra
2
2
  `)}\n${a}\n${t}`)}return a})(xt);var B=class extends L{create(e,t){let{reuseIfExists:n,wait:r,...i}=e;return this._client.post(`/v1/gradle_instances`,{query:{reuseIfExists:n,wait:r},body:i,...t})}list(e={},t){return this._client.getAPIList(`/v1/gradle_instances`,P,{query:e,...t})}delete(e,t){return this._client.delete(z`/v1/gradle_instances/${e}`,{...t,headers:R([{Accept:`*/*`},t?.headers])})}get(e,t){return this._client.get(z`/v1/gradle_instances/${e}`,t)}},Ct=class extends L{create(e,t){return this._client.post(`/v1/scoped_tokens`,{body:e,...t})}},wt=class extends L{list(e={},t){return this._client.get(`/v1/assets`,{query:e,...t})}delete(e,t){return this._client.delete(z`/v1/assets/${e}`,{...t,headers:R([{Accept:`*/*`},t?.headers])})}get(e,t={},n){return this._client.get(z`/v1/assets/${e}`,{query:t,...n})}getOrCreate(e,t){return this._client.put(`/v1/assets`,{body:e,...t})}};function Tt(e,t){if(!t)return{body:e};let n=0;return{body:new ReadableStream({pull(r){if(n>=e.length){r.close();return}let i=e.subarray(n,Math.min(n+262144,e.length));n+=i.length,r.enqueue(i),t(n,e.length)}}),duplex:`half`}}var Et=class extends wt{async getOrUpload(e,t){let n=await this.getOrCreate({name:e.name??xe(e.path),kind:e.kind??`App`,...e.platform&&{platform:e.platform},...e.ttl&&{ttl:e.ttl},...e.uploadOptions},t),r=await ye.readFile(e.path),i=Ce(`md5`).update(r).digest(`hex`);if(n.md5&&n.md5===i)return{id:n.id,name:n.name,signedDownloadUrl:n.signedDownloadUrl,kind:n.kind,...n.platform&&{platform:n.platform},md5:n.md5,...n.expiresAt&&{expiresAt:n.expiresAt}};let a=await s.fetch(n.signedUploadUrl,{headers:{...e.onUploadProgress&&{"Content-Length":r.length.toString()},"Content-Type":`application/octet-stream`},method:`PUT`,...Tt(r,e.onUploadProgress)});if(a.status!==200)throw Error(`Failed to upload asset: ${a.status} ${await a.text()}`);return{id:n.id,name:n.name,signedDownloadUrl:n.signedDownloadUrl,kind:n.kind,...n.platform&&{platform:n.platform},md5:i,...n.expiresAt&&{expiresAt:n.expiresAt}}}},Dt=class extends L{create(e,t){let{reuseIfExists:n,wait:r,...i}=e;return this._client.post(`/v1/ios_instances`,{query:{reuseIfExists:n,wait:r},body:i,...t})}list(e={},t){return this._client.getAPIList(`/v1/ios_instances`,P,{query:e,...t})}delete(e,t){return this._client.delete(z`/v1/ios_instances/${e}`,{...t,headers:R([{Accept:`*/*`},t?.headers])})}get(e,t){return this._client.get(z`/v1/ios_instances/${e}`,t)}},Ot=class extends Dt{listSessionArtifacts(e,t,n){return this._client.get(z`/v1/ios_instances/${e}/session_artifacts`,{query:t?{kind:t}:void 0,...n})}listRecordings(e,t){return this.listSessionArtifacts(e,`recording`,t)}listAppLogs(e,t){return this.listSessionArtifacts(e,`appLog`,t)}listEvents(e,t){return this.listSessionArtifacts(e,`eventLog`,t)}},kt=class extends L{create(e,t){let{reuseIfExists:n,wait:r,...i}=e;return this._client.post(`/v1/android_instances`,{query:{reuseIfExists:n,wait:r},body:i,...t})}list(e={},t){return this._client.getAPIList(`/v1/android_instances`,P,{query:e,...t})}delete(e,t){return this._client.delete(z`/v1/android_instances/${e}`,{...t,headers:R([{Accept:`*/*`},t?.headers])})}get(e,t){return this._client.get(z`/v1/android_instances/${e}`,t)}},At=class extends kt{listSessionArtifacts(e,t,n){return this._client.get(z`/v1/android_instances/${e}/session_artifacts`,{query:t?{kind:t}:void 0,...n})}listRecordings(e,t){return this.listSessionArtifacts(e,`recording`,t)}listAppLogs(e,t){return this.listSessionArtifacts(e,`appLog`,t)}listEvents(e,t){return this.listSessionArtifacts(e,`eventLog`,t)}},jt=class extends L{create(e,t){let{reuseIfExists:n,wait:r,...i}=e;return this._client.post(`/v1/xcode_instances`,{query:{reuseIfExists:n,wait:r},body:i,...t})}list(e={},t){return this._client.getAPIList(`/v1/xcode_instances`,P,{query:e,...t})}delete(e,t){return this._client.delete(z`/v1/xcode_instances/${e}`,{...t,headers:R([{Accept:`*/*`},t?.headers])})}get(e,t){return this._client.get(z`/v1/xcode_instances/${e}`,t)}};function Mt(e){let t=e.status.token;if(!t)throw Error(`Instance ${e.metadata.id} came back without its token: the credential used can read it but not control it.`);return t}var V=class extends Error{constructor(e){super(`Instance ${e} was gone before it reported any cache status`),this.instanceId=e,this.name=`XcodeCacheGoneError`}},H=class extends Error{constructor(e,t,n){super(`Timed out after ${Math.round(t/1e3)}s waiting for the cache ${e} to finish`+(n?` (last phase: ${n[e].phase})`:``)),this.side=e,this.timeoutMs=t,this.cache=n,this.name=`XcodeCacheTimeoutError`}};const Nt={restore:12e5,save:72e4};function Pt(e){return e===`disabled`||e===`unsupported`||e===`restored`||e===`skipped`||e===`failed`}function Ft(e){return e===`disabled`||e===`published`||e===`skipped`||e===`failed`||e===`timed_out`}function It(e,t){return t===`restore`?Pt(e.restore.phase):Ft(e.save.phase)}function Lt(e,t={}){let n=t.side??`restore`,r=t.timeoutMs??Nt[n],i=e.baseURL;for(;i.endsWith(`/`);)i=i.slice(0,-1);let a=`${i}/v1/xcode_instances/${encodeURIComponent(e.instanceId)}/cache`;return new Promise((i,o)=>{let c,l=``,u=!1,d=e=>{if(!u){u=!0,clearTimeout(f),t.signal?.removeEventListener(`abort`,p);try{g.close()}catch{}e()}},f=setTimeout(()=>d(()=>o(new H(n,r,c))),r),p=()=>d(()=>o(t.signal?.reason??Error(`aborted`))),m=!1,h=()=>{m||u||(m=!0,s.fetch(a,{headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`application/json`}}).then(async t=>{if(m=!1,t.status!==404){await t.text().catch(()=>void 0);return}d(()=>c?i({cache:c,gone:!0}):o(new V(e.instanceId)))}).catch(()=>{m=!1}))},g=te({url:a,fetch:async(t,n)=>{let r=await s.fetch(t,n);return r.status===404&&d(()=>c?i({cache:c,gone:!0}):o(new V(e.instanceId))),r},onConnect:()=>t.onOpen?.(),headers:{Authorization:`Bearer ${e.apiKey}`,Accept:`text/event-stream`},onMessage:e=>{if(e.event!==`cache`&&e.event!==`gone`)return;let r;try{r=JSON.parse(typeof e.data==`string`?e.data:String(e.data))}catch(e){d(()=>o(Error(`Cache stream sent unreadable data: ${e}`)));return}if(c=r,e.event===`gone`){h();return}let a=`${r.restore.phase}/${r.restore.reason??``}|${r.save.phase}/${r.save.reason??``}`;a!==l&&(l=a,t.onUpdate?.(r)),It(r,n)&&d(()=>i({cache:r,gone:!1}))}});if(t.signal?.aborted){p();return}t.signal?.addEventListener(`abort`,p,{once:!0})})}var Rt=class extends V{constructor(e){super(e),this.name=`XcodeSnapshotGoneError`,this.message=`Instance ${e} was gone before it reported any disk snapshot status`}},zt=class extends H{constructor(e){super(e.side,e.timeoutMs,e.cache),this.name=`XcodeSnapshotTimeoutError`,this.message=e.message.replace(`the cache `,`the disk snapshot `),this.snapshot=e.cache?U(e.cache):void 0}};function U(e){let{cacheKey:t,...n}=e.save;return{...e,save:{...n,...t===void 0?{}:{snapshotKey:t}}}}async function Bt(e,t={}){let{onUpdate:n,...r}=t;try{let t=await Lt(e,{...r,...n?{onUpdate:e=>n(U(e))}:{}});return{snapshot:U(t.cache),gone:t.gone}}catch(e){throw e instanceof V?new Rt(e.instanceId):e instanceof H?new zt(e):e}}function Vt(e,t){let n=b.resolve(e),r=`limsync-cache-${b.basename(n)}-${Se.createHash(`sha1`).update(n).digest(`hex`).slice(0,8)}`;return{cacheKey:r,basisCacheDir:t??b.join(be.tmpdir(),r)}}function W(e,t,n,r){return e.getOrCreate({name:t,ttl:n||`336h`,...r}).catch(e=>{let n=`Failed to create upload URL for asset '${t}': ${e instanceof Error?e.message:e}`;throw Error(n,{cause:e})})}const Ht=[`none`,`error`,`warn`,`info`,`debug`];function Ut(e,t){let n=Ht.indexOf(t),r=e=>n>=Ht.indexOf(e);return(t,n)=>{r(t)&&(t===`error`||t===`warn`?console[t](e,n):console.log(e,n))}}function Wt(e){return(t,n)=>{if(t.trim()===``)throw Error(`commandLine must not be empty`);if(n?.timeoutSeconds!==void 0&&(!Number.isInteger(n.timeoutSeconds)||n.timeoutSeconds<1||n.timeoutSeconds>21600))throw Error(`timeoutSeconds must be an integer between 1 and 21600`);let r={command:`run`,commandLine:t,cwd:n?.cwd??`.`,...n?.env?.length&&{env:n.env},...n?.timeoutSeconds!==void 0&&{timeoutSeconds:n.timeoutSeconds}};return _(r,e)}}const Gt=/^[A-Z0-9_]+$/,Kt=4096,qt=65536;function Jt(e){let t=Object.entries(e);if(t.length>32)throw Error(`too many build settings: got ${t.length}, max 32`);let n=0;for(let[e,r]of t){if(!Gt.test(e))throw Error(`invalid build setting key "${e}": keys must match ^[A-Z0-9_]+$`);if(typeof r!=`string`)throw Error(`invalid build setting value for "${e}": value must be a string`);let t=Buffer.byteLength(r,`utf8`);if(t>Kt)throw Error(`build setting value for "${e}" is too large: got ${t} bytes, max ${Kt} bytes`);if(n+=Buffer.byteLength(e,`utf8`)+t,n>qt)throw Error(`build settings payload is too large: max ${qt} bytes`)}}const Yt=e=>new Promise(t=>setTimeout(t,e)),Xt=/failed: (?:502|503|504)\b|\bEOF\b|ECONNRESET|ECONNREFUSED|socket hang up|fetch failed/i;function Zt(e){let t=e instanceof Error?e.message:String(e);return Xt.test(t)}async function Qt(e,t={}){let n=t.sleep??Yt,r=t.attempts??5,i=t.retryOn??Zt,a;for(let o=1;o<=r;o++)try{return await e()}catch(e){if(!i(e))throw e;if(a=e,o<r){let r=e instanceof Error?e.message:String(e);t.log?.(`Retrying after error (${r.trim()})...`),await n(2e3*o)}}throw a}function $t(e){if(e===``||e.startsWith(`/`)||e.includes(`\\`)||e.includes(`\0`))throw Error(`invalid sandbox home path from server: ${e}`);let t=e.split(`/`);if(t.some(e=>e===``||e===`.`||e===`..`))throw Error(`invalid sandbox home path from server: ${e}`);return t.join(`/`)}async function en(e,t){let n=await K(await s.fetch(`${e}/info`,{method:`GET`,headers:{Authorization:`Bearer ${t}`}}),`GET /info`);if(!n.homeDir)throw Error(`GET /info response is missing homeDir`);return{homeDir:$t(n.homeDir),...n.xcode?{xcode:n.xcode}:{}}}function tn(e){let t=new URL(e);if(t.protocol===`https:`)t.protocol=`wss:`;else if(t.protocol===`http:`)t.protocol=`ws:`;else throw Error(`Unsupported apiUrl protocol for rbe tunnel: ${t.protocol}`);return t.pathname=`${t.pathname.replace(/\/+$/,``)}/rbe/tunnel`,t.search=``,t.hash=``,t.toString()}var nn=class extends p{constructor(e){super(`Remote build execution is not available on this Xcode instance (${e} returned 404). Its limbuild may predate RBE support, or you may be targeting the wrong environment (for example production instead of staging).`),this.name=`RbeUnsupportedError`}},rn=class extends p{constructor(e){super(`Xcode version selection is not available on this sandbox (${e} returned 404): its daemon predates Xcode selection. Create a new sandbox, or omit the Xcode version to build with its default.`),this.name=`XcodeSelectionUnsupportedError`}};function an(e,t){return on(e,t,e=>new rn(e))}function G(e,t){return on(e,t,e=>new nn(e))}async function on(e,t,n){if(e.status===404)throw await e.text().catch(()=>void 0),n(t);return K(e,t)}async function K(e,t){let n=await e.text();if(!e.ok)throw de(t,e.status,n,e.headers);if(!n.trim())throw Error(`${t} returned an empty response`);return JSON.parse(n)}var sn=class extends jt{listBuildLogs(e,t){return this._client.get(z`/v1/xcode_instances/${e}/build_logs`,t)}listBazelBuildLogs(e,t){return this._client.get(z`/v1/xcode_instances/${e}/bazel_build_logs`,t)}create(e,t){if(e.spec?.snapshot!==void 0){let{snapshot:n,cache:r,...i}=e.spec;if(r!==void 0)throw Error(`Use either spec.snapshot or the legacy spec.cache, not both.`);return super.create({...e,spec:{...i,cache:n}},t)}return super.create(e,t)}getSnapshot(e,t){return this.getCache(e,t)._thenUnwrap(U)}bindSnapshotKey(e,t,n){return this.bindCacheKey(e,t,n)._thenUnwrap(U)}followSnapshot(e,t){let n=this._client.apiKey;if(!n)throw Error(`Following disk snapshot status needs an API key on the client`);return Bt({baseURL:this._client.baseURL,apiKey:n,instanceId:e},t)}getCache(e,t){return this._client.get(z`/v1/xcode_instances/${e}/cache`,t)}bindCacheKey(e,t,n){return this._client.put(z`/v1/xcode_instances/${e}/cache`,{body:{key:t},...n})}followCache(e,t){let n=this._client.apiKey;if(!n)throw Error(`Following cache status needs an API key on the client`);return Lt({baseURL:this._client.baseURL,apiKey:n,instanceId:e},t)}async createClient(e){let t,n;if(`instance`in e){if(!e.instance.status.apiUrl)throw Error(`Instance not ready: apiUrl is not available`);t=e.instance.status.apiUrl,n=Mt(e.instance)}else t=e.apiUrl,n=e.token;let r=Ut(`[XcodeInstance]`,e.logLevel??`info`),i=this._client,a,o=()=>(a??=en(t,n),a),c=async e=>{let r,i;if(`status`in e){if(!e.status.apiUrl)throw Error(`Simulator instance not ready: apiUrl is not available`);r=e.status.apiUrl,i=Mt(e)}else r=e.apiUrl,i=e.token;return K(await s.fetch(`${t}/simulator`,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${n}`},body:JSON.stringify({apiUrl:r,token:i})}),`POST /simulator`)},l=async e=>{try{return await i.iosInstances.delete(e),!0}catch{return!1}};return{async sync(e,i){let{cacheKey:a,basisCacheDir:s}=Vt(e,i?.basisCacheDir),c=i?.additionalFiles&&i.additionalFiles.length>0?await o():void 0,l=i?.additionalFiles?.map(e=>({localPath:e.localPath,remotePath:c&&e.remotePath.startsWith(`~/`)?`${c.homeDir}/${e.remotePath.slice(2)}`:e.remotePath})),u={apiUrl:t,token:n,udid:a,install:i?.install??!0,ignoreFn:await pe(e,{basisCacheDir:s,log:r,xcodeDefaults:!0,...i?.include?{include:i.include}:{},...i?.ignore?{additional:i.ignore}:{}}),basisCacheDir:s,watch:i?.watch??!0,launchMode:`ForegroundIfRunning`,log:r,syncSymlinks:!0,...l?{additionalFiles:l}:{},...i?.onSyncComplete?{onSyncComplete:i.onSyncComplete}:{}},d=await ie(e,u),f={};return d.bytesSent!==void 0&&(f.bytesSent=d.bytesSent),d.stopWatching&&(f.stopWatching=d.stopWatching),f},xcodebuild(e,a){if(a?.reactNative?.devServerURL&&e?.configuration===`Release`)throw Error(`reactNative.devServerURL is only supported for Debug builds`);if(e?.onlyTesting?.length&&e?.skipTesting?.length)throw Error(`onlyTesting and skipTesting are mutually exclusive; pass one`);if((e?.onlyTesting?.length||e?.skipTesting?.length)&&e?.action!==`build-for-testing`)throw Error(`onlyTesting/skipTesting require action: 'build-for-testing'`);if(e?.runTests===!1&&e?.action!==`build-for-testing`)throw Error(`runTests: false requires action: 'build-for-testing'`);if(e?.action===`build-for-testing`&&e.sdk!==void 0&&e.sdk!==`iphonesimulator`)throw Error(`action: 'build-for-testing' requires the iphonesimulator sdk`);a?.buildSettings&&Jt(a.buildSettings);let o={command:`xcodebuild`,...e&&{xcodebuild:e},...a?.xcodegen&&{xcodegen:a.xcodegen},...a?.reactNative&&{reactNative:a.reactNative},...a?.signing&&{signing:a.signing},...a?.cloudSigning&&{cloudSigning:a.cloudSigning},...a?.appstore&&{testflight:a.appstore},...a?.buildSettings&&{buildSettings:a.buildSettings},...a?.env?.length&&{env:a.env},...a?.gitInit!==void 0&&{gitInit:a.gitInit},...a?.logProcessor&&{logProcessor:a.logProcessor},...a?.webhook&&{webhook:a.webhook}},s={apiUrl:t,token:n,log:r,...a?.onXctestEvent&&{onXctestEvent:a.onXctestEvent}};if(a?.upload&&`assetName`in a.upload){let e=W(i.assets,a.upload.assetName,a.upload.ttl,a.upload.uploadOptions).then(e=>(o.signedUploadUrl=e.signedUploadUrl,o.assetId=e.id,o.additionalMetadata={signedDownloadUrl:e.signedDownloadUrl},o));return _(e,s)}return a?.upload&&`signedUploadUrl`in a.upload&&(o.signedUploadUrl=a.upload.signedUploadUrl),_(o,s)},run:Wt({apiUrl:t,token:n,log:r}),observeBuildLogs(e,i){return ce(e,{apiUrl:t,token:n,log:r,...i})},async getSimulator(){return K(await s.fetch(`${t}/simulator`,{method:`GET`,headers:{Authorization:`Bearer ${n}`}}),`GET /simulator`)},attachSimulator:c,attachNewSimulator:async()=>{let e=await i.iosInstances.create({wait:!0});try{await c(e)}catch(t){throw await l(e.metadata.id),t}return{iosInstanceId:e.metadata.id,simulator:e}},deleteSimulator:l,async getXcode(){return an(await s.fetch(`${t}/xcode`,{method:`GET`,headers:{Authorization:`Bearer ${n}`}}),`GET /xcode`)},async setXcode(e){return an(await s.fetch(`${t}/xcode`,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${n}`},body:JSON.stringify({version:e})}),`POST /xcode`)},getInfo:()=>en(t,n),async startRbe(e){return G(await s.fetch(`${t}/rbe`,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${n}`},body:JSON.stringify(e??{})}),`POST /rbe`)},async getRbe(){return G(await s.fetch(`${t}/rbe`,{method:`GET`,headers:{Authorization:`Bearer ${n}`}}),`GET /rbe`)},async stopRbe(){return G(await s.fetch(`${t}/rbe`,{method:`DELETE`,headers:{Authorization:`Bearer ${n}`}}),`DELETE /rbe`)},async startRbeTunnel(r){return se(tn(t),n,r?.host??`127.0.0.1`,r?.port??8980,{mode:`multiplexed`,logLevel:r?.logLevel??e.logLevel??`info`})},async uploadLatestRbeBuild(e){let r,a;if(e.assetName!==void 0){if(!e.assetName)throw Error(`assetName must not be empty`);let t=await W(i.assets,e.assetName,e.ttl);r=t.signedUploadUrl,a=t.signedDownloadUrl}else r=e.signedUploadUrl;return{...await Qt(async()=>{let e=await s.fetchLongRequest(`${t}/rbe/upload`,{method:`POST`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${n}`},body:JSON.stringify({signedUploadUrl:r})});if(e.status===404)throw await e.text().catch(()=>void 0),Error(`POST /rbe/upload returned 404: the instance may no longer exist, or its limbuild predates RBE upload support. Recreate the instance and retry.`);return K(e,`POST /rbe/upload`)},{retryOn:e=>Zt(e)||re(e,400)&&/no successful RBE app build/i.test(e.body)}),...a&&{signedDownloadUrl:a}}},async getActiveRbeBuilds(){return G(await s.fetch(`${t}/rbe/builds/active`,{method:`GET`,headers:{Authorization:`Bearer ${n}`}}),`GET /rbe/builds/active`)},async getRecentRbeBuilds(){return G(await s.fetch(`${t}/rbe/builds/recent`,{method:`GET`,headers:{Authorization:`Bearer ${n}`}}),`GET /rbe/builds/recent`)},waitForRbeBuildEnd(e,r){return new Promise((i,a)=>{if(r?.signal?.aborted){a(Error(`waiting for build ${e} was aborted`));return}let o=!1,c=()=>{f.close(),r?.signal?.removeEventListener(`abort`,d)},l=e=>{o||(o=!0,c(),i(e))},u=e=>{o||(o=!0,c(),a(e))},d=()=>u(Error(`waiting for build ${e} was aborted`)),f=te({url:`${t}/exec/${e}/events`,fetch:ue(s.fetch,t=>u(Error(`build event stream for ${e} is unreachable: ${t instanceof Error?t.message:t}`))),headers:{Authorization:`Bearer ${n}`},onMessage:t=>{if(t.event!==`end`)return;let n;try{n=JSON.parse(t.data)}catch(t){u(Error(`invalid build end event for ${e}: ${t}`));return}l(n)},onDisconnect:()=>{u(Error(`build event stream for ${e} ended without a terminal event (the build may have finished and its live stream been removed)`))}});r?.signal?.addEventListener(`abort`,d,{once:!0})})}}}};function cn(e){let t=e.endsWith(`/`)?e.slice(0,-1):e,n=t.split(`/`);return n[n.length-1]===`local.properties`||n.length===1&&(t===`.gradle`||t===`.kotlin`||t===`build`)}var ln=class extends B{listBuildLogs(e,t){return this._client.get(z`/v1/gradle_instances/${e}/build_logs`,t)}async createClient(e){let t,n;if(`instance`in e){if(!e.instance.status.apiUrl)throw Error(`Instance not ready: apiUrl is not available`);t=e.instance.status.apiUrl,n=Mt(e.instance)}else t=e.apiUrl,n=e.token;let r=Ut(`[GradleInstance]`,e.logLevel??`info`),i=this._client;return{async sync(e,i){let{cacheKey:a,basisCacheDir:o}=Vt(e,i?.basisCacheDir),s=i?.ignore,c={apiUrl:t,token:n,udid:a,install:!1,ignoreFn:await pe(e,{basisCacheDir:o,log:r,xcodeDefaults:!1,nestedGitignore:!0,additional:s?e=>cn(e)||s(e):cn,...i?.include?{include:i.include}:{}}),basisCacheDir:o,watch:i?.watch??!1,launchMode:`ForegroundIfRunning`,log:r,syncSymlinks:!0,...i?.additionalFiles&&{additionalFiles:i.additionalFiles},...i?.onSyncComplete&&{onSyncComplete:i.onSyncComplete}},l=await ie(e,c),u={};return l.bytesSent!==void 0&&(u.bytesSent=l.bytesSent),l.stopWatching&&(u.stopWatching=l.stopWatching),u},run:Wt({apiUrl:t,token:n,log:r}),gradlebuild(e){let a={command:`gradlebuild`,...e?.env?.length&&{env:e.env},...e?.tasks?.length&&{tasks:e.tasks},...e?.projectPath&&{projectPath:e.projectPath},...e?.reactNative&&{reactNative:e.reactNative},...e?.signing&&{signing:e.signing},...e?.webhook&&{webhook:e.webhook},...e?.playstore&&{playstore:e.playstore}};if(e?.upload&&`assetName`in e.upload){let o=W(i.assets,e.upload.assetName,e.upload.ttl).then(e=>(a.signedUploadUrl=e.signedUploadUrl,a.additionalMetadata={signedDownloadUrl:e.signedDownloadUrl},a));return _(o,{apiUrl:t,token:n,log:r})}return e?.upload&&`signedUploadUrl`in e.upload&&(a.signedUploadUrl=e.upload.signedUploadUrl),_(a,{apiUrl:t,token:n,log:r})},observeBuildLogs(e,i){return ce(e,{apiUrl:t,token:n,log:r,...i})}}}};B.prototype.listBuildLogs=ln.prototype.listBuildLogs,B.prototype.createClient=ln.prototype.createClient;const q=e=>{if(globalThis.process!==void 0)return globalThis.process.env?.[e]?.trim()||void 0;if(globalThis.Deno!==void 0)return globalThis.Deno.env?.get?.(e)?.trim()||void 0};var J,Y,X,un,Z=class{constructor({baseURL:e=q(`LIMRUN_BASE_URL`),apiKey:t=q(`LIM_API_KEY`)??null,...n}={}){J.add(this),X.set(this,void 0),this.androidInstances=new At(this),this.assets=new Et(this),this.iosInstances=new Ot(this),this.xcodeInstances=new sn(this),this.gradleInstances=new B(this),this.analytics=new _t(this),this.scopedTokens=new Ct(this);let r={apiKey:t,...n,baseURL:e||`https://api.limrun.com`};this.baseURL=r.baseURL,this.timeout=r.timeout??Y.DEFAULT_TIMEOUT,this.logger=r.logger??console;let i=`warn`;this.logLevel=i,this.logLevel=nt(r.logLevel,`ClientOptions.logLevel`,this)??nt(q(`LIMRUN_LOG`),`process.env['LIMRUN_LOG']`,this)??i,this.fetchOptions=r.fetchOptions,this.maxRetries=r.maxRetries??2,this.fetch=r.fetch??Xe(),C(this,X,et,`f`);let a=q(`LIMRUN_CUSTOM_HEADERS`);if(a){let e={};for(let t of a.split(`
3
3
  `)){let n=t.indexOf(`:`);n>=0&&(e[t.substring(0,n).trim()]=t.substring(n+1).trim())}r.defaultHeaders={...e,...r.defaultHeaders}}this._options=r,this.apiKey=t}withOptions(e){return new this.constructor({...this._options,baseURL:this.baseURL,maxRetries:this.maxRetries,timeout:this.timeout,logger:this.logger,logLevel:this.logLevel,fetch:this.fetch,fetchOptions:this.fetchOptions,apiKey:this.apiKey,...e})}defaultQuery(){return this._options.defaultQuery}validateHeaders({values:e,nulls:t}){if(!(this.apiKey&&e.get(`authorization`))&&!t.has(`authorization`))throw Error(`Could not resolve authentication method. Expected the apiKey to be set. Or for the "Authorization" headers to be explicitly omitted`)}async authHeaders(e){if(this.apiKey!=null)return R([{Authorization:`Bearer ${this.apiKey}`}])}stringifyQuery(e){return tt(e)}getUserAgent(){return`${this.constructor.name}/JS ${E}`}defaultIdempotencyKey(){return`stainless-node-retry-${Pe()}`}makeStatusError(e,t,n,r){return l.generate(e,t,n,r)}buildURL(e,t,n){let r=!w(this,J,`m`,un).call(this)&&n||this.baseURL,i=Ie(e)?new URL(e):new URL(r+(r.endsWith(`/`)&&e.startsWith(`/`)?e.slice(1):e)),a=this.defaultQuery(),o=Object.fromEntries(i.searchParams);return(!ze(a)||!ze(o))&&(t={...o,...a,...t}),typeof t==`object`&&t&&!Array.isArray(t)&&(i.search=this.stringifyQuery(t)),i.toString()}async prepareOptions(e){}async prepareRequest(e,{url:t,options:n}){}get(e,t){return this.methodRequest(`get`,e,t)}post(e,t){return this.methodRequest(`post`,e,t)}patch(e,t){return this.methodRequest(`patch`,e,t)}put(e,t){return this.methodRequest(`put`,e,t)}delete(e,t){return this.methodRequest(`delete`,e,t)}methodRequest(e,t,n){return this.request(Promise.resolve(n).then(n=>({method:e,path:t,...n})))}request(e,t=null){return new ot(this,this.makeRequest(e,t,void 0))}async makeRequest(e,t,n){let r=await e,i=r.maxRetries??this.maxRetries;t??=i,await this.prepareOptions(r);let{req:a,url:o,timeout:s}=await this.buildRequest(r,{retryCount:i-t});await this.prepareRequest(a,{url:o,options:r});let l=`log_`+(Math.random()*(1<<24)|0).toString(16).padStart(6,`0`),u=n===void 0?``:`, retryOf: ${n}`,d=Date.now();if(A(this).debug(`[${l}] sending request`,j({retryOfRequestLogID:n,method:r.method,url:o,options:r,headers:a.headers})),r.signal?.aborted)throw new c;let p=new AbortController,m=await this.fetchWithTimeout(o,a,s,p).catch(f),h=Date.now();if(m instanceof globalThis.Error){let e=`retrying, ${t} attempts remaining`;if(r.signal?.aborted)throw new c;let i=ee(m)||/timed? ?out/i.test(String(m)+(`cause`in m?String(m.cause):``));if(t)return A(this).info(`[${l}] connection ${i?`timed out`:`failed`} - ${e}`),A(this).debug(`[${l}] connection ${i?`timed out`:`failed`} (${e})`,j({retryOfRequestLogID:n,url:o,durationMs:h-d,message:m.message})),this.retryRequest(r,t,n??l);throw A(this).info(`[${l}] connection ${i?`timed out`:`failed`} - error; no more retries left`),A(this).debug(`[${l}] connection ${i?`timed out`:`failed`} (error; no more retries left)`,j({retryOfRequestLogID:n,url:o,durationMs:h-d,message:m.message})),i?new ne:new le({cause:m})}let g=`[${l}${u}] ${a.method} ${o} ${m.ok?`succeeded`:`failed`} with status ${m.status} in ${h-d}ms`;if(!m.ok){let e=await this.shouldRetry(m);if(t&&e){let e=`retrying, ${t} attempts remaining`;return await $e(m.body),A(this).info(`${g} - ${e}`),A(this).debug(`[${l}] response error (${e})`,j({retryOfRequestLogID:n,url:m.url,status:m.status,headers:m.headers,durationMs:h-d})),this.retryRequest(r,t,n??l,m.headers)}let i=e?`error; no more retries left`:`error; not retryable`;A(this).info(`${g} - ${i}`);let a=await m.text().catch(e=>f(e).message),o=He(a),s=o?void 0:a;throw A(this).debug(`[${l}] response error (${i})`,j({retryOfRequestLogID:n,url:m.url,status:m.status,headers:m.headers,message:s,durationMs:Date.now()-d})),this.makeStatusError(m.status,o,s,m.headers)}return A(this).info(g),A(this).debug(`[${l}] response start`,j({retryOfRequestLogID:n,url:m.url,status:m.status,headers:m.headers,durationMs:h-d})),{response:m,options:r,controller:p,requestLogID:l,retryOfRequestLogID:n,startTime:d}}getAPIList(e,t,n){return this.requestAPIList(t,n&&`then`in n?n.then(t=>({method:`get`,path:e,...t})):{method:`get`,path:e,...n})}requestAPIList(e,t){let n=this.makeRequest(t,null,void 0);return new ct(this,n,e)}async fetchWithTimeout(e,t,n,r){let{signal:i,method:a,...o}=t||{},s=this._makeAbort(r);i&&i.addEventListener(`abort`,s,{once:!0});let c=setTimeout(s,n),l=globalThis.ReadableStream&&o.body instanceof globalThis.ReadableStream||typeof o.body==`object`&&o.body!==null&&Symbol.asyncIterator in o.body,u={signal:r.signal,...l?{duplex:`half`}:{},method:`GET`,...o};a&&(u.method=a.toUpperCase());try{return await this.fetch.call(void 0,e,u)}finally{clearTimeout(c)}}async shouldRetry(e){let t=e.headers.get(`x-should-retry`);return t===`true`?!0:t===`false`?!1:e.status===408||e.status===409||e.status===429||e.status>=500}async retryRequest(e,t,n,r){let i,a=r?.get(`retry-after-ms`);if(a){let e=parseFloat(a);Number.isNaN(e)||(i=e)}let o=r?.get(`retry-after`);if(o&&!i){let e=parseFloat(o);i=Number.isNaN(e)?Date.parse(o)-Date.now():e*1e3}if(i===void 0){let n=e.maxRetries??this.maxRetries;i=this.calculateDefaultRetryTimeoutMillis(t,n)}return await fe(i),this.makeRequest(e,t-1,n)}calculateDefaultRetryTimeoutMillis(e,t){let n=t-e;return Math.min(.5*2**n,8)*(1-Math.random()*.25)*1e3}async buildRequest(e,{retryCount:t=0}={}){let n={...e},{method:r,path:i,query:a,defaultBaseURL:o}=n,s=this.buildURL(i,a,o);`timeout`in n&&Ve(`timeout`,n.timeout),n.timeout=n.timeout??this.timeout;let{bodyHeaders:c,body:l}=this.buildBody({options:n});return{req:{method:r,headers:await this.buildHeaders({options:e,method:r,bodyHeaders:c,retryCount:t}),...n.signal&&{signal:n.signal},...globalThis.ReadableStream&&l instanceof globalThis.ReadableStream&&{duplex:`half`},...l&&{body:l},...this.fetchOptions??{},...n.fetchOptions??{}},url:s,timeout:n.timeout}}async buildHeaders({options:e,method:t,bodyHeaders:n,retryCount:r}){let i={};this.idempotencyHeader&&t!==`get`&&(e.idempotencyKey||=this.defaultIdempotencyKey(),i[this.idempotencyHeader]=e.idempotencyKey);let a=R([i,{Accept:`application/json`,"User-Agent":this.getUserAgent(),"X-Stainless-Retry-Count":String(r),...e.timeout?{"X-Stainless-Timeout":String(Math.trunc(e.timeout/1e3))}:{},...Ye()},await this.authHeaders(e),this._options.defaultHeaders,n,e.headers]);return this.validateHeaders(a),a.values}_makeAbort(e){return()=>e.abort()}buildBody({options:e}){let{body:t,headers:n}=e;if(!t)return t==null&&`body`in e?w(this,X,`f`).call(this,{body:t,headers:R([n])}):{bodyHeaders:void 0,body:void 0};let r=R([n]);return ArrayBuffer.isView(t)||t instanceof ArrayBuffer||t instanceof DataView||typeof t==`string`&&r.values.has(`content-type`)||globalThis.Blob&&t instanceof globalThis.Blob||t instanceof FormData||t instanceof URLSearchParams||globalThis.ReadableStream&&t instanceof globalThis.ReadableStream?{bodyHeaders:void 0,body:t}:typeof t==`object`&&(Symbol.asyncIterator in t||Symbol.iterator in t&&`next`in t&&typeof t.next==`function`)?{bodyHeaders:void 0,body:Qe(t)}:typeof t==`object`&&r.values.get(`content-type`)===`application/x-www-form-urlencoded`?{bodyHeaders:{"content-type":`application/x-www-form-urlencoded`},body:this.stringifyQuery(t)}:w(this,X,`f`).call(this,{body:t,headers:r})}};Y=Z,X=new WeakMap,J=new WeakSet,un=function(){return this.baseURL!==`https://api.limrun.com`},Z.Limrun=Y,Z.DEFAULT_TIMEOUT=3e5,Z.LimrunError=p,Z.APIError=l,Z.APIConnectionError=le,Z.APIConnectionTimeoutError=ne,Z.APIUserAbortError=c,Z.NotFoundError=m,Z.ConflictError=o,Z.RateLimitError=u,Z.BadRequestError=oe,Z.AuthenticationError=h,Z.InternalServerError=ae,Z.PermissionDeniedError=g,Z.UnprocessableEntityError=d,Z.toFile=ht,Z.AndroidInstances=At,Z.Assets=Et,Z.IosInstances=Ot,Z.XcodeInstances=sn,Z.GradleInstances=B,Z.Analytics=_t,Z.ScopedTokens=Ct,process.platform;function dn(e){return e?.trim()||void 0}async function fn(e,t,n=`Limrun provider operation aborted`,r){if(!t)try{return await e}finally{r?.()}return await new Promise((i,a)=>{let o=()=>a(t.reason??Error(n));if(t.aborted){e.then(e=>{r?.(),i(e)},e=>{r?.(),a(e)}),o();return}t.addEventListener(`abort`,o,{once:!0}),e.then(e=>{t.removeEventListener(`abort`,o),r?.(),i(e)},e=>{t.removeEventListener(`abort`,o),r?.(),a(e)})})}function pn(){let e=new Set;return{wait:async(t,n,r)=>{let i,a=new Promise(t=>{i=()=>{e.delete(a),t()}});return e.add(a),await fn(t,n,r,i)},[Symbol.asyncDispose]:async()=>{for(;e.size>0;)await Promise.all(e)}}}async function Q(e,t,n,r){return await(e?e.wait(t,n,r):fn(t,n,r))}async function mn(e,t){let n=await he({apiUrl:e.apiUrl,adbUrl:e.adbUrl,token:e.token,logLevel:`warn`}),r={platform:`android`,lease:e.lease,instanceId:e.instanceId,ownership:e.ownership,device:e.device,client:n,token:e.token,dependencies:t},i={exec:async(e,t)=>await bn(r,e,t),text:async e=>{let{createLimrunAndroidTextInjector:t}=await import(`./android-text-entry.js`);await t(n)(e)}};return i.reverse=await t.android.createPortReverse(i.exec,{noRebind:e.ownership===`attached`}),Object.assign(r,{adbProvider:i})}function hn(e){return e.dependencies.android.createInteractor(e.device,e.adbProvider)}async function gn(e,t,n,r,i,a){i?.throwIfAborted();let o=dn(r?.packageNameHint);r?.relaunch&&o&&await t.dependencies.android.forceStopApp(async(e,n)=>await bn(t,e,n),o,i);let s=await Q(a,e.assets.getOrUpload({path:n,name:En(o,n)},{signal:i}),i);await Q(a,t.client.sendAsset(s.signedDownloadUrl),i),i?.throwIfAborted();let c=o?await t.dependencies.android.inferAppName(o):void 0;return{...o?{packageName:o,launchTarget:o}:{},...c?{appName:c}:{}}}async function _n(e,t){await e.adbProvider.reverse?.ensure({local:Tn(t.devicePort),remote:Tn(t.hostPort),ownerId:t.name})}async function vn(e){await e.adbTunnelPromise?.catch(()=>{});let t=e.adbSerial;t&&(await yn(e),await e.dependencies.host.runAdb(e.dependencies.android.hostAdbInvocation([`disconnect`,t]),{allowFailure:!0,timeoutMs:1e4}).catch(()=>{})),e.adbTunnel?.close(),e.adbTunnel=void 0,e.adbSerial=void 0,e.adbTunnelPromise=void 0}async function yn(e){let t=e.adbProvider.reverse;if(!t?.list)return;let n=await t.list().catch(()=>[]),r=new Set,i=[],a=e.ownership===`attached`;for(let e of n)e.ownerId?r.add(e.ownerId):a||i.push(e.local);await Promise.allSettled([...[...r].map(async e=>await t.removeAllOwned(e)),...i.map(async e=>await t.remove(e))])}async function bn(e,t,n){let{invocation:r,result:i}=await xn(e,t,n);return await Sn(r,i,n?.allowFailure,e.dependencies)}async function xn(e,t,n){let r=await Cn(e),i=e.dependencies.android.deviceAdbInvocation(r,t);return{invocation:i,result:await e.dependencies.host.runAdb(i,{allowFailure:n?.allowFailure,binaryStdout:n?.binaryStdout,stdin:n?.stdin,timeoutMs:n?.timeoutMs??3e4,signal:n?.signal})}}async function Sn(e,t,n,r){if(t.exitCode!==0&&n!==!0)throw await r.android.adbError(`Limrun Android ADB command failed`,t,e);return t}async function Cn(e){if(e.adbSerial)return e.adbSerial;let t=e.adbTunnelPromise??wn(e);e.adbTunnelPromise=t;try{return await t}catch(n){throw e.adbTunnelPromise===t&&(e.adbTunnelPromise=void 0),n}}async function wn(e){let t=await e.client.startAdbTunnel(),n=`${t.address.address}:${t.address.port}`;return e.adbTunnel=t,e.adbSerial=n,n}function Tn(e){if(!Number.isInteger(e)||e<1||e>65535)throw new t(`INVALID_ARGS`,`Invalid Android tcp reverse port: ${e}`);return`tcp:${e}`}function En(e,t){let n=y.extname(t)||`.apk`;return`${e?.replaceAll(/[^a-zA-Z0-9_.-]+/g,`-`)||`android-app`}${n}`}function Dn(e){let t=Array.isArray(e)?e:[e],n=[],r=(e,t,i)=>{let a=n.length;n.push(On(e,{index:a,depth:t,parentIndex:i}));for(let n of Fn(e))r(n,t+1,a)};for(let e of t)r(e,0);return n}function On(e,t){return{index:t.index,type:kn(e),role:An(e),label:jn(e),value:Mn(e),identifier:Nn(e),rect:Pn(e),enabled:e.enabled,selected:e.selected,hittable:e.hittable,depth:t.depth,parentIndex:t.parentIndex}}function kn(e){return e.elementType??e.type}function An(e){return e.role??kn(e)}function jn(e){return e.label??e.AXLabel??void 0}function Mn(e){return e.value??e.AXValue??void 0}function Nn(e){return e.identifier??e.AXUniqueId??void 0}function Pn(e){let t=e.rect??e.frame;if(!(!t||typeof t.x!=`number`||typeof t.y!=`number`||typeof t.width!=`number`||typeof t.height!=`number`))return{x:t.x,y:t.y,width:t.width,height:t.height}}function Fn(e){return e.children??e.nodes??e.elements??[]}function In(e){return e.key===`id`?{AXUniqueId:e.value}:e.key===`value`?{AXValue:e.value}:{AXLabel:e.value}}async function Ln(e,t){let n=await me({apiUrl:e.apiUrl,token:e.token,logLevel:`warn`});return{platform:`ios`,lease:e.lease,instanceId:e.instanceId,ownership:e.ownership,device:e.device,client:n,token:e.token,dependencies:t}}async function Rn(e,t,n,r,i,a){i?.throwIfAborted();let o=await Hn(n,t.dependencies);try{i?.throwIfAborted();let n=await Q(a,e.assets.getOrUpload({path:o.uploadPath,name:o.assetName},{signal:i}),i),s=(await zn(t,n.signedDownloadUrl,{md5:n.md5,relaunch:r?.relaunch,appIdentifierHint:r?.appIdentifierHint},i,a)).appId;return{...s?{bundleId:s,launchTarget:s}:{},...o.appName?{appName:o.appName}:{}}}finally{await o.cleanup()}}async function zn(e,n,r,i,a){i?.throwIfAborted();let o=await Q(a,e.client.listApps(),i).catch(e=>{if(i?.aborted)throw e}),s=dn((await Q(a,e.client.installApp(n,{md5:r?.md5,launchMode:r?.relaunch?`RelaunchIfRunning`:`ForegroundIfRunning`}),i)).bundleId),c=dn(r?.appIdentifierHint),l=[];for(let t of Wn){t>0&&await we(t,void 0,{signal:i}),l=await Q(a,e.client.listApps(),i);let n=qn({resultBundleId:s,requestedBundleId:c,beforeInstallApps:o,afterInstallApps:l});if(n)return{appId:n}}throw new t(`COMMAND_FAILED`,`Limrun iOS app installation could not be verified.`,{resultBundleId:s,requestedBundleId:c,installedUserApps:l.filter(Yn).map(e=>e.bundleId).sort()})}function Bn(e){return new Vn(e)}var Vn=class{session;constructor(e){this.session=e}async open(e,t){if(t?.url){await this.launch(e,t.launchArgs),await this.session.client.openUrl(t.url);return}if(r(e)){await this.session.client.openUrl(e);return}await this.launch(e,t?.launchArgs)}async launch(e,n=[]){let r=await this.session.dependencies.ios.resolveAppAlias(e);if(n.length===0){await this.session.client.launchApp(r);return}let i=await this.session.client.simctl([`launch`,`--terminate-running-process`,`booted`,r,...n]).wait();if(i.code!==0)throw new t(`COMMAND_FAILED`,`Limrun iOS could not launch ${r} with arguments.`,{bundleId:r,exitCode:i.code,stderr:i.stderr.trim()})}async openDevice(){}async close(e){e&&await this.session.client.terminateApp(await this.session.dependencies.ios.resolveAppAlias(e)).catch(()=>{})}async tap(e,t){await this.session.client.tap(e,t)}async tapElementSelector(e){await this.session.client.tapElement(In(e))}async doubleTap(e,t){await this.session.client.performActions([{type:`tap`,x:e,y:t},{type:`wait`,durationMs:Kn},{type:`tap`,x:e,y:t}])}async longPress(e,t,n=Gn){await this.session.client.performActions([{type:`touchDown`,x:e,y:t},{type:`wait`,durationMs:n},{type:`touchUp`,x:e,y:t}])}async focus(e,t){await this.tap(e,t)}async type(e,t){await this.enterText(e,t)}async fill(e,t,n,r){let{awaitLimrunTextEntryFocus:i,readLimrunUnambiguousTapTargets:a,readLimrunTextEntryFocus:o}=await import(`./text-entry-focus.js`),s=a(await this.session.client.elementTree(),e,t);await this.tap(e,t);let c=await i({targetsAtPoint:s,readFocus:async()=>o(await this.session.client.elementTree()),sleep:e=>we(e),x:e,y:t});return await this.session.client.pressKey(`a`,[`command`]),n.length===0?(await this.session.client.pressKey(`delete`),{textEntryReadiness:c}):(await this.enterText(n,r),{textEntryReadiness:c})}async enterText(e,t){if(t&&t>0){let r=0;try{for(let n of Array.from(e))await this.session.client.typeText(n,!1,{requireFocus:!1}),r+=1,await we(t)}catch(e){throw n(e,r)}return}await this.session.client.typeText(e,!1,{requireFocus:!1})}async scroll(e,t){await this.session.client.scroll(e,t?.pixels??300)}async screenshot(e){let{transcodeScreenshotToPngAsync:t}=await import(`./png-worker-client.js`).then(e=>e.s),n=await this.session.client.screenshot(),r=await t(Buffer.from(n.base64,`base64`),`Limrun iOS screenshot`);v.mkdirSync(y.dirname(e),{recursive:!0}),await v.promises.writeFile(e,r)}async snapshot(){let{captureLimrunIosSnapshot:e}=await import(`./ios-snapshot-adapter.js`);return await e(this.session)}async back(){await this.session.client.pressKey(`escape`)}async home(){await this.session.client.performActions([{type:`buttonDown`,button:`home`},{type:`buttonUp`,button:`home`}])}async readClipboard(){return(await this.pasteboard([`pbpaste`,`booted`])).stdout.replaceAll(`\r
4
4
  `,`
5
- `).replace(/\n$/,``)}async writeClipboard(e){await this.pasteboard([`pbcopy`,`booted`],e)}async pasteboard(e,n){let r=await this.session.client.simctl(e,n===void 0?void 0:{stdin:n}).wait();if(r.code!==0)throw new t(`COMMAND_FAILED`,`Limrun iOS could not access the clipboard.`,{command:e[0],exitCode:r.code,stderr:r.stderr.trim()});return r}async setOrientation(e){if(e===`portrait-upside-down`)throw Xn(`orientation`,`Limrun iOS direct sessions support portrait and landscape orientation, not portrait upside-down.`);await this.session.client.setOrientation(e===`portrait`?`Portrait`:`Landscape`)}async setSetting(e,t,n,r){let{setLimrunIosSetting:i}=await import(`./ios-settings.js`);return await i(this.session,e,t,n,r)}};async function Hn(e,t){if(!(await v.promises.stat(e)).isDirectory())return{uploadPath:e,assetName:y.basename(e),appName:Un(e),cleanup:async()=>{}};let n=await v.promises.mkdtemp(y.join(ve.tmpdir(),`agent-device-limrun-ios-app-`)),r=y.join(n,`${y.basename(e)}.zip`);try{await t.host.archiveDirectory({sourceDirectory:y.dirname(e),entryName:y.basename(e),archivePath:r})}catch(e){throw await v.promises.rm(n,{recursive:!0,force:!0}),e}return{uploadPath:r,assetName:y.basename(r),appName:await t.ios.readBundleAppName(e)??Un(e),cleanup:async()=>{await v.promises.rm(n,{recursive:!0,force:!0})}}}function Un(e){return y.basename(e).replace(/\.(?:app|ipa|apk|aab|zip)$/i,``)||void 0}const Wn=[0,250],Gn=800,Kn=80;function qn(e){let t=new Set(e.afterInstallApps.map(e=>e.bundleId));return(e.resultBundleId&&t.has(e.resultBundleId)?e.resultBundleId:void 0)??(e.requestedBundleId&&t.has(e.requestedBundleId)?e.requestedBundleId:void 0)??Jn(e.beforeInstallApps,e.afterInstallApps)}function Jn(e,t){if(!e)return;let n=new Set(e.map(e=>e.bundleId)),r=t.filter(e=>Yn(e)&&!n.has(e.bundleId));return r.length===1?r[0]?.bundleId:void 0}function Yn(e){return!e.bundleId.startsWith(`com.apple.`)&&!e.installType.toLowerCase().includes(`system`)}function Xn(e,n){throw new t(`UNSUPPORTED_OPERATION`,n,{command:e})}function $(e){return e.platform===`android`?Zn(e):Qn(e)}function Zn(e){let t=e.adbProvider.exec;return{platform:`android`,device:e.device,interactor:hn(e),adb:e.adbProvider,listApps:async n=>await e.dependencies.android.listApps(t,a(n)),getForegroundApp:async n=>await e.dependencies.android.getForegroundApp(e.device,t,n),pressKey:async(t,n)=>{await e.client.pressKey(t,n)},getKeyboardState:async()=>await e.dependencies.android.getKeyboardState(t),dismissKeyboard:async()=>await e.dependencies.android.dismissKeyboard(t),readLogs:async n=>await e.dependencies.android.readLogs(t,n),...$n(e.client,e.token,e.dependencies.host),installRemoteApp:async t=>{await e.client.sendAsset(t)}}}function Qn(e){return{platform:`ios`,device:e.device,interactor:Bn(e),viewport:{width:e.client.deviceInfo.screenWidth,height:e.client.deviceInfo.screenHeight},listApps:async t=>(await e.client.listApps()).filter(e=>a(t)===`all`||Yn(e)).map(e=>({id:e.bundleId,name:e.name,installType:e.installType})).sort((e,t)=>e.id.localeCompare(t.id)),pressKey:async(t,n)=>{await e.client.pressKey(t,n)},readLogs:async(t,n)=>await e.client.appLogTail(t,n),...$n(e.client,e.token,e.dependencies.host),installRemoteApp:async(t,n)=>await zn(e,t,n),runSimctl:t=>e.client.simctl(t)}}function $n(e,t,n){return{startRecording:async t=>{await e.startRecording(t)},stopRecording:async()=>({downloadUrl:await e.stopRecording({})}),downloadRecording:async e=>{await n.downloadFile({url:e.downloadUrl,headers:{Authorization:`Bearer ${t}`},destinationPath:e.outPath,timeoutMs:6e4})}}}const er=`agent-device-cli`;function tr(e){return{apiKey:e.apiKey,defaultHeaders:{"x-agent-device-client":er,"x-agent-device-version":e.clientVersion}}}function nr(e,n,r=`This daemon drives an existing Limrun instance with its own token. Set LIMRUN_API_KEY, or ask the instance owner to do this.`){if(e)return e;throw new t(`UNSUPPORTED_OPERATION`,`${n} requires a Limrun API key.`,{hint:r})}function rr(e){if(e.runtimeInstance!==void 0){let t=e.runtimeInstance.trim();if(!t)throw TypeError(`Limrun runtimeInstance must be a non-empty string`);return t}let t=JSON.stringify({provider:`limrun`,region:e.region?.trim().toLowerCase()||`default`,apiKey:e.apiKey,attachedIos:e.instances?.ios?.apiUrl,attachedAndroid:e.instances?.android?.apiUrl});return`principal-${_e(t,`agent-device:limrun-runtime-owner:v1`,32,{N:16384,r:8,p:1,maxmem:67108864}).toString(`hex`)}`}function ir(e,t,n){let r=new ar(e,t);if(!n?.includePlatformModule)return r;let a=i(x,rr(e));if(a.kind!==`provider-runtime`)throw TypeError(`Invalid Limrun runtime owner`);return Object.freeze({runtime:r,platformModule:Object.freeze({owner:a,loadRuntime:async e=>await or(r,a.instance,e)})})}var ar=class{limrun;sessions=new Map;keepAliveTimers=new Map;appAliases=new Map;options;dependencies;provider=x;leaseLifecycle={allocate:async(e,t)=>await this.allocate(e,t),release:async e=>await this.release(e)};appCatalog=async(e,t)=>{let{assertLimrunUploadedAppAccess:n,listLimrunAppAssets:r}=await import(`./app-catalog.js`);return n(e.publicNetworkOnly),(await r(this.orgClient(`Listing uploaded apps`,`Run open <bundle-or-package-id> first; apps then lists the apps installed on the instance.`),e.platform,t)).map(e=>e.name)};recoverExpiredLease=async e=>{if(e.leaseProvider!==this.provider||!De(e.backend))throw new t(`UNSUPPORTED_OPERATION`,`Limrun cannot recover this expired lease.`,{leaseId:e.leaseId,leaseProvider:e.leaseProvider,leaseBackend:e.backend});await this.release(e)};deviceInventoryProvider=async e=>{if(e.leaseProvider!==this.provider||!e.leaseId)return null;let t=this.sessions.get(e.leaseId);return t?e.platform&&e.platform!==t.platform?[]:[t.device]:null};constructor(e,t){if(!e.apiKey&&!e.instances?.ios&&!e.instances?.android)throw TypeError(`Limrun runtime requires an apiKey or instance access`);this.options=e,this.dependencies=t,this.limrun=e.apiKey?new Z(tr({apiKey:e.apiKey,clientVersion:t.clientVersion})):void 0}ownsDevice(e){return S(e.id)!==void 0}hasLiveSession(e){return this.getSessionForDevice(e)!==void 0}getInteractor(e,t){let n=this.getSessionForDevice(e);if(n)return n.platform===`ios`?Bn(n):hn(n)}getDeviceSession(e){let t=this.getSessionForDevice(e);return t?$(t):void 0}async installApp(e,t,n,r,i,a){return await this.installInstallablePath(e,n,{...r,appIdentifierHint:r?.appIdentifierHint??t,packageNameHint:r?.packageNameHint??t},i,a)}async installInstallablePath(e,t,n,r,i){let a=this.getSessionForDevice(e);if(!a)return;let o=this.orgClient(`Uploading an app`);return a.platform===`ios`?await Rn(o,a,t,n,r,i):await gn(o,a,t,n,r,i)}async configurePortReverse(e){let t=this.requireAndroidPortReverseSession(e.leaseId);if(t)return await _n(t,e),sr(e)}async shutdown(){let e=[...this.sessions.values()];await Promise.allSettled(e.map(async e=>await this.terminateSession(e))),this.sessions.clear(),this.appAliases.clear()}async allocate(e,t){if(e.leaseProvider!==this.provider)return;let n=De(e.backend);if(!n)return;let{rejectRefusedLimrunProfileFields:r}=await import(`./session-allocation.js`);r(t);let i=this.sessions.get(e.leaseId);if(i)return{limrunInstanceId:i.instanceId,device:i.device};let a=await this.attachSession(n,e)??await this.createSession(n,e,t);return this.sessions.set(e.leaseId,a),this.options.keepAlive&&this.startKeepAlive(e.leaseId,a),{limrunInstanceId:a.instanceId,device:a.device}}async attachSession(e,t){let{ios:n,android:r}=this.options.instances??{},i=n=>{let r=ke(n);return{lease:t,instanceId:r,ownership:`attached`,device:Oe(e,t,r)}};return e===`android`?r&&await mn({...i(r.apiUrl),...r},this.dependencies):n&&await Ln({...i(n.apiUrl),...n},this.dependencies)}async createSession(e,t,n){let{allocateLimrunAndroidSession:r,allocateLimrunIosSession:i,resolvePreinstalledAppId:a,resolveRequestedLimrunAppAsset:o}=await import(`./session-allocation.js`),s=this.orgClient(`Creating an instance`),c=await o(s,e,n),l=this.sessionAllocationParams(s,t,c),u=e===`ios`?await i(l):await r(l);if(c)try{let e=await a(u,c);this.appAliases.set(t.leaseId,{assetName:c.name,installedAppId:e})}catch(e){throw await this.terminateSession(u),e}return u}sessionAllocationParams(e,t,n){return{limrun:e,lease:t,metadata:this.buildInstanceMetadata(t),region:this.options.region,app:n,dependencies:this.dependencies}}buildInstanceMetadata(e){return{displayName:`agent-device-${e.tenantId}-${e.runId}`,labels:{tenantId:e.tenantId,runId:e.runId,leaseId:e.leaseId,provider:e.leaseProvider??`limrun`,source:er}}}async release(e){let t=this.sessions.get(e.leaseId);return t?(await this.terminateSession(t),this.sessions.delete(e.leaseId),this.appAliases.delete(e.leaseId),{limrunInstanceId:t.instanceId}):await this.releaseRecoveredSession(e)}async releaseRecoveredSession(e){let t=De(e.backend),n=this.limrun;if(!t||!n)return;let r=`provider=${x},leaseId=${e.leaseId}`,i=(t===`ios`?await n.iosInstances.list({labelSelector:r}):await n.androidInstances.list({labelSelector:r})).getPaginatedItems().map(e=>e.metadata.id);for(let e of i)t===`ios`?await n.iosInstances.delete(e):await n.androidInstances.delete(e);if(i.length!==0)return{limrunInstanceId:i[0],limrunInstanceCount:i.length}}startKeepAlive(e,t){let n=setInterval(()=>{try{t.client.keepAlive()}catch{}},3e4);n.unref(),this.keepAliveTimers.set(e,n)}async terminateSession(e){let t=e.lease.leaseId;if(clearInterval(this.keepAliveTimers.get(t)),this.keepAliveTimers.delete(t),e.client.disconnect(),e.platform===`android`&&await vn(e),e.ownership===`attached`)return;let n=this.orgClient(`Deleting an instance`);e.platform===`ios`?await n.iosInstances.delete(e.instanceId):await n.androidInstances.delete(e.instanceId)}orgClient(e,t){return nr(this.limrun,e,t)}getSessionForDevice(e){let t=S(e.id);if(!t)return;let n=this.sessions.get(t.leaseId);return n?.platform===t.platform?n:void 0}resolveAppReference(e,t){let n=S(e.id);if(!n)return t;let r=this.appAliases.get(n.leaseId);return r?.assetName===t?r.installedAppId:t}currentAppLogReader(e){let t=this.getSessionForDevice(e);if(!t)return;let n=$(t);return{platform:t.platform,leaseId:t.lease.leaseId,instanceId:t.instanceId,ownership:t.ownership,readLogs:async(e,t)=>n.platform===`ios`?await n.readLogs(e,t):await n.readLogs(t),[Symbol.asyncDispose]:async()=>void 0}}async reconnectAppLogReader(e,t){let{reconnectLimrunAppLogReader:n}=await import(`./app-log-reconnect.js`);return await n({limrun:this.limrun,instances:this.options.instances,descriptor:e,dependencies:this.dependencies,signal:t})}requireAndroidPortReverseSession(e){let t=this.sessions.get(e);if(!t||t.platform===`android`)return t;throw cr(`port reverse`,`Direct Limrun iOS sessions cannot reach local host ports; use a bridge public URL.`)}};async function or(e,n,r){let{createLimrunPlatformRuntimeOwner:i}=await import(`./app-log-runtime2.js`);return i({host:r,runtimeInstance:n,ownsDevice:t=>e.ownsDevice(t),hasLiveSession:t=>e.hasLiveSession(t),getInteractor:(t,n)=>e.getInteractor(t,n),getDeviceSession:t=>e.getDeviceSession(t),resolveAppReference:(t,n)=>e.resolveAppReference(t,n),openCurrent:async t=>e.currentAppLogReader(t),reconnect:async(t,n)=>await e.reconnectAppLogReader(t,n),listApps:async(n,r,i)=>{i.throwIfAborted();let a=e.getDeviceSession(n);if(!a)throw new t(`DEVICE_NOT_FOUND`,`Limrun app inventory session is unavailable`,{deviceId:n.id});return(await a.listApps(r)).map(e=>({id:e.id,name:e.name??e.id}))},getAppState:async(n,r)=>{r.throwIfAborted();let i=e.getDeviceSession(n);if(i?.platform!==`android`)throw new t(`UNSUPPORTED_OPERATION`,`Limrun Android appstate requires an active provider session`);let a=await i.getForegroundApp(r);return r.throwIfAborted(),{package:a?.appId,activity:a?.activity}},deployApp:async(t,n,r,i)=>await e.installApp(t,n.app,n.appPath,{relaunch:n.replaceExisting,appIdentifierHint:n.app,packageNameHint:n.app},r,i),deployMaterializedApp:async(t,n,r,i)=>await e.installInstallablePath(t,n.artifact.installablePath,{appIdentifierHint:n.artifact.bundleId,packageNameHint:n.artifact.packageName},r,i),configurePortReverse:async t=>await e.configurePortReverse(t)})}function sr(e){return{leaseId:e.leaseId,devicePort:e.devicePort,hostPort:e.hostPort,name:e.name}}function cr(e,n){throw new t(`UNSUPPORTED_OPERATION`,n,{command:e})}async function lr(e){let n=await fr(e);if(n)return n;if(!e.apiKey)throw new t(`INVALID_ARGS`,`Limrun verification requires an API key or instance access.`);let r=new Z({...tr({apiKey:e.apiKey,clientVersion:e.clientVersion}),timeout:15e3,maxRetries:0}),i={limit:1,...e.region?{region:e.region}:{}};try{e.platform===`android`?await r.androidInstances.list(i):await r.iosInstances.list(i)}catch(e){let n=ur(e);throw n===401||n===403?new t(`UNAUTHORIZED`,`Limrun rejected connection verification.`,{status:n,hint:`Check LIMRUN_API_KEY and its organization access.`}):new t(`COMMAND_FAILED`,`Limrun connection verification failed.`,{hint:`Check Limrun service access, LIMRUN_REGION, and network connectivity, then retry.`},e)}let a=e.platform===`android`?`Android`:`iOS`,o=e.platform===`android`?`emulator`:`simulator`;return{provider:`limrun`,service:`Limrun`,verificationMessage:`Credentials and ${a} instance access verified.`,device:{status:`deferred`,name:`Provider-selected ${a} ${o}`,platform:e.platform},app:{status:`missing`,message:`Run apps to choose an uploaded asset before allocation.`}}}function ur(e){if(!e||typeof e!=`object`)return;let t=e.status;return typeof t==`number`?t:void 0}function dr(e){let t={logLevel:`none`,maxReconnectAttempts:0},{ios:n,android:r}=e.instances??{};return e.platform===`android`?r&&import(`./instance-client.js`).then(e=>e.n).then(({createInstanceClient:e})=>e({...t,...r})):n&&import(`./ios-client.js`).then(e=>e.n).then(({createInstanceClient:e})=>e({...t,...n}))}async function fr(e){let n=dr(e);if(!n)return;let r=e.platform===`android`,i=r?`Android`:`iOS`;try{(await n).disconnect()}catch(e){throw new t(`COMMAND_FAILED`,`Limrun ${i} instance access failed.`,{hint:r?`Check LIM_ANDROID_INSTANCE_URL, LIM_ANDROID_INSTANCE_TOKEN, and LIM_ANDROID_INSTANCE_ADB_URL, and that the instance is still running.`:`Check LIM_IOS_INSTANCE_URL and LIM_IOS_INSTANCE_TOKEN, and that the instance is still running.`},e)}return{provider:`limrun`,service:`Limrun`,verificationMessage:`Existing ${i} instance access verified. A daemon started with these variables drives it without creating or deleting it.`,device:{status:`verified`,name:`Existing ${i} ${r?`emulator`:`simulator`}`,platform:e.platform},app:{status:`missing`,message:`Run open <bundle-or-package-id> to use an app installed on the instance.`}}}var pr=e({LIMRUN_PROFILE_FIELDS:()=>Te,LIMRUN_PROVIDER:()=>x,createLimrunRuntime:()=>ir,verifyLimrunConnection:()=>lr});export{je as _,$ as a,Fn as c,fn as d,pn as f,Oe as g,ke as h,nr as i,Pn as l,x as m,lr as n,Ln as o,Te as p,ir as r,Dn as s,pr as t,mn as u,S as v};
5
+ `).replace(/\n$/,``)}async writeClipboard(e){await this.pasteboard([`pbcopy`,`booted`],e)}async pasteboard(e,n){let r=await this.session.client.simctl(e,n===void 0?void 0:{stdin:n}).wait();if(r.code!==0)throw new t(`COMMAND_FAILED`,`Limrun iOS could not access the clipboard.`,{command:e[0],exitCode:r.code,stderr:r.stderr.trim()});return r}async setOrientation(e){if(e===`portrait-upside-down`)throw Xn(`orientation`,`Limrun iOS direct sessions support portrait and landscape orientation, not portrait upside-down.`);await this.session.client.setOrientation(e===`portrait`?`Portrait`:`Landscape`)}async setSetting(e,t,n,r){let{setLimrunIosSetting:i}=await import(`./ios-settings.js`);return await i(this.session,e,t,n,r)}};async function Hn(e,t){if(!(await v.promises.stat(e)).isDirectory())return{uploadPath:e,assetName:y.basename(e),appName:Un(e),cleanup:async()=>{}};let n=await v.promises.mkdtemp(y.join(ve.tmpdir(),`agent-device-limrun-ios-app-`)),r=y.join(n,`${y.basename(e)}.zip`);try{await t.host.archiveDirectory({sourceDirectory:y.dirname(e),entryName:y.basename(e),archivePath:r})}catch(e){throw await v.promises.rm(n,{recursive:!0,force:!0}),e}return{uploadPath:r,assetName:y.basename(r),appName:await t.ios.readBundleAppName(e)??Un(e),cleanup:async()=>{await v.promises.rm(n,{recursive:!0,force:!0})}}}function Un(e){return y.basename(e).replace(/\.(?:app|ipa|apk|aab|zip)$/i,``)||void 0}const Wn=[0,250],Gn=800,Kn=80;function qn(e){let t=new Set(e.afterInstallApps.map(e=>e.bundleId));return(e.resultBundleId&&t.has(e.resultBundleId)?e.resultBundleId:void 0)??(e.requestedBundleId&&t.has(e.requestedBundleId)?e.requestedBundleId:void 0)??Jn(e.beforeInstallApps,e.afterInstallApps)}function Jn(e,t){if(!e)return;let n=new Set(e.map(e=>e.bundleId)),r=t.filter(e=>Yn(e)&&!n.has(e.bundleId));return r.length===1?r[0]?.bundleId:void 0}function Yn(e){return!e.bundleId.startsWith(`com.apple.`)&&!e.installType.toLowerCase().includes(`system`)}function Xn(e,n){throw new t(`UNSUPPORTED_OPERATION`,n,{command:e})}function $(e){return e.platform===`android`?Zn(e):Qn(e)}function Zn(e){let t=e.adbProvider.exec;return{platform:`android`,device:e.device,interactor:hn(e),adb:e.adbProvider,listApps:async n=>await e.dependencies.android.listApps(t,a(n)),getForegroundApp:async n=>await e.dependencies.android.getForegroundApp(e.device,t,n),pressKey:async(t,n)=>{await e.client.pressKey(t,n)},getKeyboardState:async()=>await e.dependencies.android.getKeyboardState(t),dismissKeyboard:async()=>await e.dependencies.android.dismissKeyboard(t),readLogs:async n=>await e.dependencies.android.readLogs(t,n),...$n(e.client,e.token,e.dependencies.host),installRemoteApp:async t=>{await e.client.sendAsset(t)}}}function Qn(e){return{platform:`ios`,device:e.device,interactor:Bn(e),viewport:{width:e.client.deviceInfo.screenWidth,height:e.client.deviceInfo.screenHeight},listApps:async t=>(await e.client.listApps()).filter(e=>a(t)===`all`||Yn(e)).map(e=>({id:e.bundleId,name:e.name,installType:e.installType})).sort((e,t)=>e.id.localeCompare(t.id)),pressKey:async(t,n)=>{await e.client.pressKey(t,n)},readLogs:async(t,n)=>await e.client.appLogTail(t,n),...$n(e.client,e.token,e.dependencies.host),installRemoteApp:async(t,n)=>await zn(e,t,n),runSimctl:t=>e.client.simctl(t)}}function $n(e,t,n){return{startRecording:async t=>{await e.startRecording(t)},stopRecording:async()=>({downloadUrl:await e.stopRecording({})}),downloadRecording:async e=>{await n.downloadFile({url:e.downloadUrl,headers:{Authorization:`Bearer ${t}`},destinationPath:e.outPath,timeoutMs:6e4})}}}const er=`agent-device-cli`;function tr(e,n,r=`This daemon drives an existing Limrun instance with its own token. Set LIMRUN_API_KEY, or ask the instance owner to do this.`){if(e)return e;throw new t(`UNSUPPORTED_OPERATION`,`${n} requires a Limrun API key.`,{hint:r})}function nr(e){if(e.runtimeInstance!==void 0){let t=e.runtimeInstance.trim();if(!t)throw TypeError(`Limrun runtimeInstance must be a non-empty string`);return t}let t=JSON.stringify({provider:`limrun`,region:e.region?.trim().toLowerCase()||`default`,apiKey:e.apiKey,attachedIos:e.instances?.ios?.apiUrl,attachedAndroid:e.instances?.android?.apiUrl});return`principal-${_e(t,`agent-device:limrun-runtime-owner:v1`,32,{N:16384,r:8,p:1,maxmem:67108864}).toString(`hex`)}`}function rr(e,t,n){let r=new ir(e,t);if(!n?.includePlatformModule)return r;let a=i(x,nr(e));if(a.kind!==`provider-runtime`)throw TypeError(`Invalid Limrun runtime owner`);return Object.freeze({runtime:r,platformModule:Object.freeze({owner:a,loadRuntime:async e=>await ar(r,a.instance,e)})})}var ir=class{limrun;sessions=new Map;keepAliveTimers=new Map;appAliases=new Map;options;dependencies;provider=x;leaseLifecycle={allocate:async(e,t)=>await this.allocate(e,t),release:async e=>await this.release(e)};appCatalog=async(e,t)=>{let{assertLimrunUploadedAppAccess:n,listLimrunAppAssets:r}=await import(`./app-catalog.js`);return n(e.publicNetworkOnly),(await r(this.orgClient(`Listing uploaded apps`,`Run open <bundle-or-package-id> first; apps then lists the apps installed on the instance.`),e.platform,t)).map(e=>e.name)};recoverExpiredLease=async e=>{if(e.leaseProvider!==this.provider||!De(e.backend))throw new t(`UNSUPPORTED_OPERATION`,`Limrun cannot recover this expired lease.`,{leaseId:e.leaseId,leaseProvider:e.leaseProvider,leaseBackend:e.backend});await this.release(e)};deviceInventoryProvider=async e=>{if(e.leaseProvider!==this.provider||!e.leaseId)return null;let t=this.sessions.get(e.leaseId);return t?e.platform&&e.platform!==t.platform?[]:[t.device]:null};constructor(e,t){if(!e.apiKey&&!e.instances?.ios&&!e.instances?.android)throw TypeError(`Limrun runtime requires an apiKey or instance access`);this.options=e,this.dependencies=t,this.limrun=e.apiKey?new Z({apiKey:e.apiKey}):void 0}ownsDevice(e){return S(e.id)!==void 0}hasLiveSession(e){return this.getSessionForDevice(e)!==void 0}getInteractor(e,t){let n=this.getSessionForDevice(e);if(n)return n.platform===`ios`?Bn(n):hn(n)}getDeviceSession(e){let t=this.getSessionForDevice(e);return t?$(t):void 0}async installApp(e,t,n,r,i,a){return await this.installInstallablePath(e,n,{...r,appIdentifierHint:r?.appIdentifierHint??t,packageNameHint:r?.packageNameHint??t},i,a)}async installInstallablePath(e,t,n,r,i){let a=this.getSessionForDevice(e);if(!a)return;let o=this.orgClient(`Uploading an app`);return a.platform===`ios`?await Rn(o,a,t,n,r,i):await gn(o,a,t,n,r,i)}async configurePortReverse(e){let t=this.requireAndroidPortReverseSession(e.leaseId);if(t)return await _n(t,e),or(e)}async shutdown(){let e=[...this.sessions.values()];await Promise.allSettled(e.map(async e=>await this.terminateSession(e))),this.sessions.clear(),this.appAliases.clear()}async allocate(e,t){if(e.leaseProvider!==this.provider)return;let n=De(e.backend);if(!n)return;let{rejectRefusedLimrunProfileFields:r}=await import(`./session-allocation.js`);r(t);let i=this.sessions.get(e.leaseId);if(i)return{limrunInstanceId:i.instanceId,device:i.device};let a=await this.attachSession(n,e)??await this.createSession(n,e,t);return this.sessions.set(e.leaseId,a),this.options.keepAlive&&this.startKeepAlive(e.leaseId,a),{limrunInstanceId:a.instanceId,device:a.device}}async attachSession(e,t){let{ios:n,android:r}=this.options.instances??{},i=n=>{let r=ke(n);return{lease:t,instanceId:r,ownership:`attached`,device:Oe(e,t,r)}};return e===`android`?r&&await mn({...i(r.apiUrl),...r},this.dependencies):n&&await Ln({...i(n.apiUrl),...n},this.dependencies)}async createSession(e,t,n){let{allocateLimrunAndroidSession:r,allocateLimrunIosSession:i,resolvePreinstalledAppId:a,resolveRequestedLimrunAppAsset:o}=await import(`./session-allocation.js`),s=this.orgClient(`Creating an instance`),c=await o(s,e,n),l=this.sessionAllocationParams(s,t,c),u=e===`ios`?await i(l):await r(l);if(c)try{let e=await a(u,c);this.appAliases.set(t.leaseId,{assetName:c.name,installedAppId:e})}catch(e){throw await this.terminateSession(u),e}return u}sessionAllocationParams(e,t,n){return{limrun:e,lease:t,metadata:this.buildInstanceMetadata(t),region:this.options.region,app:n,dependencies:this.dependencies}}buildInstanceMetadata(e){return{displayName:`agent-device-${e.tenantId}-${e.runId}`,labels:{tenantId:e.tenantId,runId:e.runId,leaseId:e.leaseId,provider:e.leaseProvider??`limrun`,source:er}}}async release(e){let t=this.sessions.get(e.leaseId);return t?(await this.terminateSession(t),this.sessions.delete(e.leaseId),this.appAliases.delete(e.leaseId),{limrunInstanceId:t.instanceId}):await this.releaseRecoveredSession(e)}async releaseRecoveredSession(e){let t=De(e.backend),n=this.limrun;if(!t||!n)return;let r=`provider=${x},leaseId=${e.leaseId}`,i=(t===`ios`?await n.iosInstances.list({labelSelector:r}):await n.androidInstances.list({labelSelector:r})).getPaginatedItems().map(e=>e.metadata.id);for(let e of i)t===`ios`?await n.iosInstances.delete(e):await n.androidInstances.delete(e);if(i.length!==0)return{limrunInstanceId:i[0],limrunInstanceCount:i.length}}startKeepAlive(e,t){let n=setInterval(()=>{try{t.client.keepAlive()}catch{}},3e4);n.unref(),this.keepAliveTimers.set(e,n)}async terminateSession(e){let t=e.lease.leaseId;if(clearInterval(this.keepAliveTimers.get(t)),this.keepAliveTimers.delete(t),e.client.disconnect(),e.platform===`android`&&await vn(e),e.ownership===`attached`)return;let n=this.orgClient(`Deleting an instance`);e.platform===`ios`?await n.iosInstances.delete(e.instanceId):await n.androidInstances.delete(e.instanceId)}orgClient(e,t){return tr(this.limrun,e,t)}getSessionForDevice(e){let t=S(e.id);if(!t)return;let n=this.sessions.get(t.leaseId);return n?.platform===t.platform?n:void 0}resolveAppReference(e,t){let n=S(e.id);if(!n)return t;let r=this.appAliases.get(n.leaseId);return r?.assetName===t?r.installedAppId:t}currentAppLogReader(e){let t=this.getSessionForDevice(e);if(!t)return;let n=$(t);return{platform:t.platform,leaseId:t.lease.leaseId,instanceId:t.instanceId,ownership:t.ownership,readLogs:async(e,t)=>n.platform===`ios`?await n.readLogs(e,t):await n.readLogs(t),[Symbol.asyncDispose]:async()=>void 0}}async reconnectAppLogReader(e,t){let{reconnectLimrunAppLogReader:n}=await import(`./app-log-reconnect.js`);return await n({limrun:this.limrun,instances:this.options.instances,descriptor:e,dependencies:this.dependencies,signal:t})}requireAndroidPortReverseSession(e){let t=this.sessions.get(e);if(!t||t.platform===`android`)return t;throw sr(`port reverse`,`Direct Limrun iOS sessions cannot reach local host ports; use a bridge public URL.`)}};async function ar(e,n,r){let{createLimrunPlatformRuntimeOwner:i}=await import(`./app-log-runtime2.js`);return i({host:r,runtimeInstance:n,ownsDevice:t=>e.ownsDevice(t),hasLiveSession:t=>e.hasLiveSession(t),getInteractor:(t,n)=>e.getInteractor(t,n),getDeviceSession:t=>e.getDeviceSession(t),resolveAppReference:(t,n)=>e.resolveAppReference(t,n),openCurrent:async t=>e.currentAppLogReader(t),reconnect:async(t,n)=>await e.reconnectAppLogReader(t,n),listApps:async(n,r,i)=>{i.throwIfAborted();let a=e.getDeviceSession(n);if(!a)throw new t(`DEVICE_NOT_FOUND`,`Limrun app inventory session is unavailable`,{deviceId:n.id});return(await a.listApps(r)).map(e=>({id:e.id,name:e.name??e.id}))},getAppState:async(n,r)=>{r.throwIfAborted();let i=e.getDeviceSession(n);if(i?.platform!==`android`)throw new t(`UNSUPPORTED_OPERATION`,`Limrun Android appstate requires an active provider session`);let a=await i.getForegroundApp(r);return r.throwIfAborted(),{package:a?.appId,activity:a?.activity}},deployApp:async(t,n,r,i)=>await e.installApp(t,n.app,n.appPath,{relaunch:n.replaceExisting,appIdentifierHint:n.app,packageNameHint:n.app},r,i),deployMaterializedApp:async(t,n,r,i)=>await e.installInstallablePath(t,n.artifact.installablePath,{appIdentifierHint:n.artifact.bundleId,packageNameHint:n.artifact.packageName},r,i),configurePortReverse:async t=>await e.configurePortReverse(t)})}function or(e){return{leaseId:e.leaseId,devicePort:e.devicePort,hostPort:e.hostPort,name:e.name}}function sr(e,n){throw new t(`UNSUPPORTED_OPERATION`,n,{command:e})}async function cr(e){let n=await dr(e);if(n)return n;if(!e.apiKey)throw new t(`INVALID_ARGS`,`Limrun verification requires an API key or instance access.`);let r=new Z({apiKey:e.apiKey,timeout:15e3,maxRetries:0}),i={limit:1,...e.region?{region:e.region}:{}};try{e.platform===`android`?await r.androidInstances.list(i):await r.iosInstances.list(i)}catch(e){let n=lr(e);throw n===401||n===403?new t(`UNAUTHORIZED`,`Limrun rejected connection verification.`,{status:n,hint:`Check LIMRUN_API_KEY and its organization access.`}):new t(`COMMAND_FAILED`,`Limrun connection verification failed.`,{hint:`Check Limrun service access, LIMRUN_REGION, and network connectivity, then retry.`},e)}let a=e.platform===`android`?`Android`:`iOS`,o=e.platform===`android`?`emulator`:`simulator`;return{provider:`limrun`,service:`Limrun`,verificationMessage:`Credentials and ${a} instance access verified.`,device:{status:`deferred`,name:`Provider-selected ${a} ${o}`,platform:e.platform},app:{status:`missing`,message:`Run apps to choose an uploaded asset before allocation.`}}}function lr(e){if(!e||typeof e!=`object`)return;let t=e.status;return typeof t==`number`?t:void 0}function ur(e){let t={logLevel:`none`,maxReconnectAttempts:0},{ios:n,android:r}=e.instances??{};return e.platform===`android`?r&&import(`./instance-client.js`).then(e=>e.n).then(({createInstanceClient:e})=>e({...t,...r})):n&&import(`./ios-client.js`).then(e=>e.n).then(({createInstanceClient:e})=>e({...t,...n}))}async function dr(e){let n=ur(e);if(!n)return;let r=e.platform===`android`,i=r?`Android`:`iOS`;try{(await n).disconnect()}catch(e){throw new t(`COMMAND_FAILED`,`Limrun ${i} instance access failed.`,{hint:r?`Check LIM_ANDROID_INSTANCE_URL, LIM_ANDROID_INSTANCE_TOKEN, and LIM_ANDROID_INSTANCE_ADB_URL, and that the instance is still running.`:`Check LIM_IOS_INSTANCE_URL and LIM_IOS_INSTANCE_TOKEN, and that the instance is still running.`},e)}return{provider:`limrun`,service:`Limrun`,verificationMessage:`Existing ${i} instance access verified. A daemon started with these variables drives it without creating or deleting it.`,device:{status:`verified`,name:`Existing ${i} ${r?`emulator`:`simulator`}`,platform:e.platform},app:{status:`missing`,message:`Run open <bundle-or-package-id> to use an app installed on the instance.`}}}var fr=e({LIMRUN_PROFILE_FIELDS:()=>Te,LIMRUN_PROVIDER:()=>x,createLimrunRuntime:()=>rr,verifyLimrunConnection:()=>cr});export{Oe as _,tr as a,Dn as c,mn as d,fn as f,ke as g,x as h,er as i,Fn as l,Te as m,cr as n,$ as o,pn as p,rr as r,Ln as s,fr as t,Pn as u,je as v,S as y};
@@ -1 +1 @@
1
- import{P as e}from"./sdk-contracts.js";import{Jr as t,Kr as n,Qr as r}from"./sdk-batch.js";import{c as i,l as a}from"./src.js";function o(t,n,r,i){return new e(`COMMAND_FAILED`,`fill tapped (${t}, ${n}) but ${r}`,{reason:`text_entry_focus_not_observed`,x:t,y:n,...i,hint:`The tap most likely missed the field. Re-check the target with snapshot -i and fill the element it reports, rather than retrying the same coordinates.`})}function s(e){let n=[];for(let r of p(e)){let e=a(r);t(e)&&n.push({node:r,identity:m(r),rect:e})}return n}function c(e){let t=new Map;for(let n of e)t.set(n.identity,(t.get(n.identity)??0)+1);return t}function l(e){let t=s(e),n=c(t),i=null,a=1/0;for(let e of t){if(!e.node.traits?.includes(`IsEditing`))continue;let t=r(e.rect);t>=a||(i=n.get(e.identity)===1?{identity:e.identity,rect:e.rect}:{rect:e.rect},a=t)}return i}function u(e,t,r){let i=s(e),a=c(i),o=new Set;for(let e of i)a.get(e.identity)===1&&n(e.rect,t,r)&&o.add(e.identity);return o}async function d(e){let t=Date.now()+(e.timeoutMs??3e3),n=0,r=!1;for(;;){let i=await e.readFocus();if(n+=1,i&&(r=!0,f(i,e.targetsAtPoint,e.x,e.y)))return`focused-element`;if(Date.now()>=t)break;await e.sleep(150)}throw o(e.x,e.y,`nothing there took text-entry focus, so the text was not sent`,{samples:n,editingElementObserved:r})}function f(e,t,r,i){return n(e.rect,r,i)?!0:e.identity!==void 0&&t.has(e.identity)}function p(e){let t=[],n=e=>{t.push(e);for(let t of i(e))n(t)};for(let t of Array.isArray(e)?e:[e])n(t);return t}function m(e){return[e.pid??``,e.type??e.elementType??``,e.AXUniqueId??e.identifier??``,e.AXLabel??e.label??``].join(`\0`)}export{d as awaitLimrunTextEntryFocus,l as readLimrunTextEntryFocus,u as readLimrunUnambiguousTapTargets};
1
+ import{P as e}from"./sdk-contracts.js";import{Jr as t,Kr as n,Qr as r}from"./sdk-batch.js";import{l as i,u as a}from"./src.js";function o(t,n,r,i){return new e(`COMMAND_FAILED`,`fill tapped (${t}, ${n}) but ${r}`,{reason:`text_entry_focus_not_observed`,x:t,y:n,...i,hint:`The tap most likely missed the field. Re-check the target with snapshot -i and fill the element it reports, rather than retrying the same coordinates.`})}function s(e){let n=[];for(let r of p(e)){let e=a(r);t(e)&&n.push({node:r,identity:m(r),rect:e})}return n}function c(e){let t=new Map;for(let n of e)t.set(n.identity,(t.get(n.identity)??0)+1);return t}function l(e){let t=s(e),n=c(t),i=null,a=1/0;for(let e of t){if(!e.node.traits?.includes(`IsEditing`))continue;let t=r(e.rect);t>=a||(i=n.get(e.identity)===1?{identity:e.identity,rect:e.rect}:{rect:e.rect},a=t)}return i}function u(e,t,r){let i=s(e),a=c(i),o=new Set;for(let e of i)a.get(e.identity)===1&&n(e.rect,t,r)&&o.add(e.identity);return o}async function d(e){let t=Date.now()+(e.timeoutMs??3e3),n=0,r=!1;for(;;){let i=await e.readFocus();if(n+=1,i&&(r=!0,f(i,e.targetsAtPoint,e.x,e.y)))return`focused-element`;if(Date.now()>=t)break;await e.sleep(150)}throw o(e.x,e.y,`nothing there took text-entry focus, so the text was not sent`,{samples:n,editingElementObserved:r})}function f(e,t,r,i){return n(e.rect,r,i)?!0:e.identity!==void 0&&t.has(e.identity)}function p(e){let t=[],n=e=>{t.push(e);for(let t of i(e))n(t)};for(let t of Array.isArray(e)?e:[e])n(t);return t}function m(e){return[e.pid??``,e.type??e.elementType??``,e.AXUniqueId??e.identifier??``,e.AXLabel??e.label??``].join(`\0`)}export{d as awaitLimrunTextEntryFocus,l as readLimrunTextEntryFocus,u as readLimrunUnambiguousTapTargets};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-device",
3
- "version": "0.21.26",
3
+ "version": "0.21.27",
4
4
  "description": "Mobile app automation and verification for AI coding agents. CLI, MCP server, and typed Node.js API for iOS, Android, HarmonyOS, TV, web, macOS, and Linux.",
5
5
  "mcpName": "io.github.callstack/agent-device",
6
6
  "license": "MIT",
@@ -312,6 +312,7 @@
312
312
  "@agent-device/replay-test": "workspace:*",
313
313
  "@agent-device/selectors": "workspace:*",
314
314
  "@agent-device/session-journal": "workspace:*",
315
+ "@agent-device/test-kit": "workspace:*",
315
316
  "@agent-device/xml": "workspace:*",
316
317
  "@arethetypeswrong/cli": "^0.18.5",
317
318
  "@chenglou/freerange": "^0.0.4",
@@ -319,7 +320,9 @@
319
320
  "@nkzw/eslint-plugin": "^2.0.0",
320
321
  "@nkzw/oxlint-config": "^1.3.0",
321
322
  "@statelyai/graph": "2.4.0",
323
+ "@stryker-mutator/api": "10.0.0",
322
324
  "@stryker-mutator/core": "10.0.0",
325
+ "@stryker-mutator/instrumenter": "10.0.0",
323
326
  "@stryker-mutator/vitest-runner": "10.0.0",
324
327
  "@types/json-schema": "^7.0.15",
325
328
  "@types/node": "^22.20.1",
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "url": "https://github.com/callstack/agent-device",
8
8
  "source": "github"
9
9
  },
10
- "version": "0.21.26",
10
+ "version": "0.21.27",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "agent-device",
15
- "version": "0.21.26",
15
+ "version": "0.21.27",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  },
@@ -1 +0,0 @@
1
- 2fe083e955d1a7e65ef6307bc139988c54009eff7d616071715897ee87e59a49 agent-device-android-ime-helper-0.21.26.apk
@@ -1 +0,0 @@
1
- c69458d8784e3aa613a745a850ed660e57055a1634ee27fb228c91c67f587f5f agent-device-android-snapshot-helper-0.21.26.apk