agent-device 0.16.14 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.16.14.apk → agent-device-android-multitouch-helper-0.17.1.apk} +0 -0
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.17.1.apk.sha256 +1 -0
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.16.14.manifest.json → agent-device-android-multitouch-helper-0.17.1.manifest.json} +4 -4
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.16.14.apk → agent-device-android-snapshot-helper-0.17.1.apk} +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.17.1.apk.sha256 +1 -0
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.16.14.manifest.json → agent-device-android-snapshot-helper-0.17.1.manifest.json} +6 -6
- package/dist/src/1352.js +1 -1
- package/dist/src/221.js +4 -4
- package/dist/src/2415.js +29 -29
- package/dist/src/2805.js +1 -1
- package/dist/src/6232.js +1 -1
- package/dist/src/7599.js +4 -3
- package/dist/src/8020.js +1 -0
- package/dist/src/8699.js +1 -1
- package/dist/src/9238.js +3 -3
- package/dist/src/940.js +1 -1
- package/dist/src/9533.js +1 -1
- package/dist/src/9542.js +3 -3
- package/dist/src/android-snapshot-helper.d.ts +1 -0
- package/dist/src/apple.js +1 -1
- package/dist/src/apps.js +1 -1
- package/dist/src/args.js +15 -10
- package/dist/src/cli.js +9 -9
- package/dist/src/command-metadata.js +1 -1
- package/dist/src/contracts.d.ts +1 -0
- package/dist/src/find.js +1 -1
- package/dist/src/finders.d.ts +1 -0
- package/dist/src/generic.js +12 -10
- package/dist/src/index.d.ts +20 -1
- package/dist/src/interaction.js +1 -1
- package/dist/src/record-trace-recording.js +26 -0
- package/dist/src/record-trace.js +1 -26
- package/dist/src/selectors.d.ts +1 -0
- package/dist/src/session.js +11 -11
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +4 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +71 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +41 -7
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +160 -13
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +11 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Exceptions.swift +12 -4
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +26 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +8 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +7 -1
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +571 -56
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +21 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +11 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +13 -2
- package/ios-runner/README.md +13 -0
- package/package.json +2 -2
- package/server.json +2 -2
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.14.apk.sha256 +0 -1
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.14.apk.sha256 +0 -1
package/dist/src/9542.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import e from"node:net";import t from"node:http";import a from"node:https";import r from"node:fs";import n from"node:os";import o from"node:path";import{pipeline as i}from"node:stream/promises";import{createHash as s,randomUUID as l}from"node:crypto";import{Writable as c}from"node:stream";import{toAppErrorCode as d,AppError as u}from"./9152.js";import{runCmdSync as p,runCmd as m,runCmdDetached as f}from"./9818.js";import{
|
|
2
|
-
`)}let ep=["xcodebuild .*AgentDeviceRunnerUITests/RunnerTests/testCommand","xcodebuild .*AgentDeviceRunner\\.env\\.session-","xcodebuild build-for-testing .*ios-runner/AgentDeviceRunner/AgentDeviceRunner\\.xcodeproj"],em=new e.BlockList;async function ef(t){var a,i,s,l,c,d;let p,m,f,h,y,w,g,I=t.meta?.requestId??D(),v=!!(t.meta?.debug||t.flags?.verbose),P=(h=(i=a=t,i.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),y=(s=a,m=function(e){let t;if(e){try{t=new URL(e)}catch(t){throw new u("INVALID_ARGS","Invalid daemon base URL",{daemonBaseUrl:e},t instanceof Error?t:void 0)}if("http:"!==t.protocol&&"https:"!==t.protocol)throw new u("INVALID_ARGS","Daemon base URL must use http or https",{daemonBaseUrl:e});return t.toString().replace(/\/+$/,"")}}(p=s.flags?.daemonBaseUrl??process.env.AGENT_DEVICE_DAEMON_BASE_URL),function(t,a){let r;if(!(!t||"localhost"===(r=new URL(t).hostname.trim().toLowerCase().replace(/^\[(.*)\]$/,"$1"))||(e.isIPv4(r)?em.check(r,"ipv4"):!!e.isIPv6(r)&&em.check(r,"ipv6")))&&("string"!=typeof a||!(a.trim().length>0)))throw new u("INVALID_ARGS","Remote daemon base URL for non-loopback hosts requires daemon authentication",{daemonBaseUrl:t,hint:"Provide --daemon-auth-token or AGENT_DEVICE_DAEMON_AUTH_TOKEN when using a non-loopback remote daemon URL."})}(m,f=s.flags?.daemonAuthToken??process.env.AGENT_DEVICE_DAEMON_AUTH_TOKEN),{rawBaseUrl:p,remoteBaseUrl:m,authToken:f}),w=function(e,t){let a=e.flags?.daemonTransport??process.env.AGENT_DEVICE_DAEMON_TRANSPORT,r=k(a);if(t&&"socket"===r)throw new u("INVALID_ARGS","Remote daemon base URL only supports HTTP transport. Remove --daemon-transport socket.",{daemonBaseUrl:t});return{preference:r,serverMode:_(e.flags?.daemonServerMode??process.env.AGENT_DEVICE_DAEMON_SERVER_MODE??("dual"===a?"dual":void 0))}}(a,y.remoteBaseUrl),{paths:A((g=(l=a,c=h,d=y.rawBaseUrl,eS(l.command)&&!c&&!d))?r.mkdtempSync(o.join(n.tmpdir(),"agent-device-replay-daemon-")):h),transportPreference:w.preference,serverMode:w.serverMode,ownedStateDir:g,remoteBaseUrl:y.remoteBaseUrl,remoteAuthToken:y.authToken}),b=function(e){if(e.command!==U.test){var t;return"number"==typeof e.flags?.timeoutMs&&((t=e.command)===U.prepare||t===U.replay||t===U.snapshot)?e.flags.timeoutMs:e.command===U.prepare?24e4:9e4}}(t),S=await E("daemon_startup",async()=>await eA(P),{requestId:I,session:t.session}),T=S.info,C=await eh(t,T),N={...t,positionals:C.positionals,flags:C.flags,token:T.token,meta:{...t.meta??{},requestId:I,debug:v,cwd:t.meta?.cwd,sessionExplicit:t.meta?.sessionExplicit,tenantId:t.meta?.tenantId??t.flags?.tenant,runId:t.meta?.runId??t.flags?.runId,leaseId:t.meta?.leaseId??t.flags?.leaseId,sessionIsolation:t.meta?.sessionIsolation??t.flags?.sessionIsolation,lockPolicy:t.meta?.lockPolicy,lockPlatform:t.meta?.lockPlatform,...C.uploadedArtifactId?{uploadedArtifactId:C.uploadedArtifactId}:{},...C.clientArtifactPaths?{clientArtifactPaths:C.clientArtifactPaths}:{},...C.installSource?{installSource:C.installSource}:{}}};M({level:"info",phase:"daemon_request_prepare",data:{requestId:I,command:t.command,session:t.session}});try{return await E("daemon_request",async()=>await e$(T,N,P.transportPreference,b),{requestId:I,command:t.command})}finally{await eb(t,S,P)}}async function eh(e,t){let a,r=[...e.positionals??[]],n=e.flags?{...e.flags}:void 0,i=e.meta?.installSource,s={};if(!eY(t))return ew({positionals:r,flags:n,installSource:i,uploadedArtifactId:a,clientArtifactPaths:s});n=function(e,t,a,r){var n,i;let s=function(e,t){if("screenshot"===e.command){let a=eI(e,"path",".png");return t[0]?{field:"path",localPath:a,positionalIndex:0,positionalPath:ev("screenshot",".png")}:{field:"path",localPath:a,positionalIndex:0,flagPath:ev("screenshot",".png")}}if("record"===e.command&&"start"===(t[0]??"").toLowerCase()){let t=eI(e,"outPath",".mp4",1);return{field:"outPath",localPath:t,positionalIndex:1,positionalPath:ev("recording",o.extname(t)||".mp4")}}return null}(e,t);if(!s)return a;void 0!==s.positionalPath&&(t[s.positionalIndex]=s.positionalPath);let l=(n=a,void 0===(i=s.flagPath)?n:{...n??{},out:i});return r[s.field]=s.localPath,l}(e,r,n,s);let l=await eg(e,t);l&&(i=l.installSource,a=l.uploadedArtifactId??a);let c=()=>ew({positionals:r,flags:n,installSource:i,uploadedArtifactId:a,clientArtifactPaths:s});return"install"!==e.command&&"reinstall"!==e.command||(a=await ey(e,t,r)??a),c()}async function ey(e,t,a){var n,i;let s,l=a[1];if(void 0===l)return;if(l.startsWith("remote:")){a[1]=l.slice(7);return}let c=(n=l,i=e.meta?.cwd,s=o.isAbsolute(n)?n:o.resolve(i??process.cwd(),n),r.existsSync(s)?s:void 0);if(c)return await et({localPath:c,baseUrl:t.baseUrl,token:t.token,platform:e.flags?.platform})}function ew(e){return{positionals:e.positionals,flags:e.flags,installSource:e.installSource,uploadedArtifactId:e.uploadedArtifactId,...Object.keys(e.clientArtifactPaths).length>0?{clientArtifactPaths:e.clientArtifactPaths}:{}}}async function eg(e,t){let a=e.meta?.installSource;if("install_source"!==e.command||!a||"path"!==a.kind)return null;let n=a.path.trim();if(!n)return{installSource:a};if(n.startsWith("remote:"))return{installSource:{...a,path:n.slice(7)}};let i=o.isAbsolute(n)?n:o.resolve(e.meta?.cwd??process.cwd(),n);if(!r.existsSync(i))return{installSource:{...a,path:i}};let s=await et({localPath:i,baseUrl:t.baseUrl,token:t.token,platform:e.flags?.platform});return{installSource:{...a,path:i},uploadedArtifactId:s}}function eI(e,t,a,r=0){let n=e.positionals?.[r]??e.flags?.out,i=`${"path"===t?"screenshot":"recording"}-${Date.now()}${a}`,s=n&&n.trim().length>0?n:i;return o.isAbsolute(s)?s:o.resolve(e.meta?.cwd??process.cwd(),s)}function ev(e,t){let a=t.startsWith(".")?t:`.${t}`;return o.posix.join("/tmp",`agent-device-${e}-${Date.now()}-${Math.random().toString(36).slice(2,8)}${a}`)}async function eA(e){if(e.remoteBaseUrl)return await e_(e);let t=await eP(e);return t?{info:t,startedByClient:!1}:(function(e){let t=eR(e);if(!t.hasLock||t.hasInfo)return;let a=eU(e.lockPath);if(!a)return ex(e.lockPath);T(a.pid,a.processStartTime)||ex(e.lockPath)}(e.paths),await ek(e))}async function e_(e){let t={transport:"http",token:e.remoteAuthToken??"",pid:0,baseUrl:e.remoteBaseUrl};if(await eF(t,"http"))return{info:t,startedByClient:!1};throw new u("COMMAND_FAILED","Remote daemon is unavailable",{daemonBaseUrl:e.remoteBaseUrl,hint:"Verify AGENT_DEVICE_DAEMON_BASE_URL points to a reachable daemon with GET /health and POST /rpc."})}async function eP(e){var t,a;let r,n=eT(e.paths.infoPath);if(!n)return null;let o=await eF(n,e.transportPreference);return(t=n,a=o,t.version===b()&&t.codeSignature===P((r=eB()).useSrc?r.srcPath:r.distPath,r.root)&&a)?n:(await eM(n),ex(e.paths.infoPath),null)}async function ek(e){let t,a=0,r=[];for(let n=1;n<=2;n+=1){try{await eq(e)}catch(a){if(t=a instanceof Error?a.message:String(a),r.push(await eL(e.paths,"start_error")),n<2){await L(150);continue}break}let o=await eD(15e3,e);if(o)return{info:o,startedByClient:!0};if(await eE(e.paths)){a+=1;continue}let i=eR(e.paths),s=n<2,l=await eL(e.paths,"startup_timeout",{stopLiveProcesses:!1});if(r.push(l),l.retainedInfoProcess||l.retainedLockProcess){let t=await eD(15e3,e);if(t)return{info:t,startedByClient:!0};break}if(!s)break;i.hasInfo||i.hasLock||await L(150)}let n=eR(e.paths);throw new u("COMMAND_FAILED","Failed to start daemon",{kind:"daemon_startup_failed",infoPath:e.paths.infoPath,lockPath:e.paths.lockPath,startupTimeoutMs:15e3,startupAttempts:2,lockRecoveryCount:a,cleanupResults:r,startError:t,metadataState:n,hint:function(e,t=A(process.env.AGENT_DEVICE_STATE_DIR)){var a;let r=(a=t,`rm -f ${w(a.infoPath)} ${w(a.lockPath)}`);return e.hasLock&&!e.hasInfo?`agent-device attempted to clean stale daemon metadata automatically, but ${t.lockPath} still exists without ${t.infoPath}. Retry with --debug; if this persists after confirming no agent-device daemon process is running, run: ${r}`:e.hasLock&&e.hasInfo?`agent-device attempted to clean stale daemon metadata automatically, but ${t.infoPath} and ${t.lockPath} still remain. Retry with --debug; if this persists after confirming no agent-device daemon process is running, run: ${r}`:e.hasInfo?`agent-device did not observe reachable daemon metadata after retrying, and ${t.infoPath} still remains. Stale metadata was cleaned automatically when safe; retry with --debug. If this persists after confirming no agent-device daemon process is running, run: ${r}`:`agent-device did not observe reachable daemon metadata after retrying. Stale metadata was cleaned automatically when safe; retry with --debug and check daemon diagnostics logs. If stale metadata returns after confirming no agent-device daemon process is running, run: ${r}`}(n,e.paths)})}async function eb(e,t,a){if(!eS(e.command)||!t.startedByClient&&!a.ownedStateDir||eY(t.info))return;let n={pid:t.info.pid,removedInfo:!1,removedLock:!1,removedStateDir:!1,error:void 0};try{await eM(t.info)}catch(e){n.error=e instanceof Error?e.message:String(e)}finally{let e=r.existsSync(a.paths.infoPath);ex(a.paths.infoPath),n.removedInfo=e&&!r.existsSync(a.paths.infoPath);let t=r.existsSync(a.paths.lockPath);ex(a.paths.lockPath),n.removedLock=t&&!r.existsSync(a.paths.lockPath),a.ownedStateDir&&(r.rmSync(a.paths.baseDir,{recursive:!0,force:!0}),n.removedStateDir=!r.existsSync(a.paths.baseDir))}M({level:n.error?"warn":"info",phase:"daemon_replay_cleanup",data:n})}function eS(e){return e===U.replay||e===U.test}async function eD(e,t){let a=Date.now();for(;Date.now()-a<e;){let e=eT(t.paths.infoPath);if(e&&await eF(e,t.transportPreference))return e;await L(100)}return null}async function eE(e){let t=eR(e);if(!t.hasLock||t.hasInfo)return!1;let a=eU(e.lockPath);return!(a&&T(a.pid,a.processStartTime))&&(ex(e.lockPath),!0)}async function eM(e){await C(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}function eT(e){var t,a,r;let n,o,i=eO(e);if(!i||"object"!=typeof i)return null;let s="string"==typeof(t=i).token&&t.token.length>0?t.token:null;if(!s)return null;let l=(n=eN((a=i).port),o=eN(a.httpPort),void 0===n&&void 0===o?null:{port:n,httpPort:o});return l?{token:s,...l,transport:"socket"===(r=i.transport)||"http"===r||"dual"===r?r:void 0,pid:eN(i.pid)??0,version:eC(i.version),codeSignature:eC(i.codeSignature),processStartTime:eC(i.processStartTime)}:null}function eC(e){return"string"==typeof e?e:void 0}function eN(e){return Number.isInteger(e)&&Number(e)>0?Number(e):void 0}function eU(e){let t=eO(e);return t&&"object"==typeof t&&Number.isInteger(t.pid)&&Number(t.pid)>0?{pid:Number(t.pid),processStartTime:"string"==typeof t.processStartTime?t.processStartTime:void 0,startedAt:"number"==typeof t.startedAt?t.startedAt:void 0}:null}em.addSubnet("127.0.0.0",8,"ipv4"),em.addAddress("::1","ipv6"),em.addSubnet("::ffff:127.0.0.0",104,"ipv6");async function eL(e,t,a={}){let n=a.stopLiveProcesses??!0,o={reason:t,removedInfo:!1,removedLock:!1,stoppedInfoProcess:!1,stoppedLockProcess:!1};try{var i,s,l,c;let t=r.existsSync(e.infoPath),a=eT(e.infoPath);if(a){let t=T(a.pid,a.processStartTime);t&&!n?o.retainedInfoProcess=!0:(t&&(await eM(a),o.stoppedInfoProcess=!0),i=e.infoPath,ex(i),o.removedInfo=!0)}else t&&(s=e.infoPath,ex(s),o.removedInfo=!0);let d=r.existsSync(e.lockPath),u=eU(e.lockPath);if(u){let t=T(u.pid,u.processStartTime);t&&!n?o.retainedLockProcess=!0:(t&&(await C(u.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:u.processStartTime}),o.stoppedLockProcess=!0),l=e.lockPath,ex(l),o.removedLock=!0)}else d&&(c=e.lockPath,ex(c),o.removedLock=!0)}catch(e){o.error=e instanceof Error?e.message:String(e)}return M({level:o.error?"warn":"info",phase:"daemon_startup_metadata_cleanup",data:o}),o}function eR(e){return{hasInfo:r.existsSync(e.infoPath),hasLock:r.existsSync(e.lockPath)}}function eO(e){if(!r.existsSync(e))return null;try{return JSON.parse(r.readFileSync(e,"utf8"))}catch{return null}}function ex(e){try{r.existsSync(e)&&r.unlinkSync(e)}catch{}}async function eF(r,n){var o;return"http"===ej(r,n)?await function(e){let r=e.baseUrl?eQ(e.baseUrl,"health"):e.httpPort?`http://127.0.0.1:${e.httpPort}/health`:null;if(!r)return Promise.resolve(!1);let n=new URL(r),o="https:"===n.protocol?a:t,i=e.baseUrl?3e3:500;return new Promise(e=>{let t=o.request({protocol:n.protocol,host:n.hostname,port:n.port,path:n.pathname+n.search,method:"GET",timeout:i},t=>{t.resume(),e((t.statusCode??500)<500)});t.on("timeout",()=>{t.destroy(),e(!1)}),t.on("error",()=>{e(!1)}),t.end()})}(r):await ((o=r.port)?new Promise(t=>{let a=!1,r=e.createConnection({host:"127.0.0.1",port:o},()=>{n(!0)}),n=e=>{a||(a=!0,r.destroy(),t(e))};r.setTimeout(500),r.on("timeout",()=>{n(!1)}),r.on("error",()=>{n(!1)})}):Promise.resolve(!1))}async function eq(e){let t=eB(),a=t.useSrc?["--experimental-strip-types",t.srcPath]:[t.distPath],r={...process.env,AGENT_DEVICE_STATE_DIR:e.paths.baseDir,AGENT_DEVICE_DAEMON_SERVER_MODE:e.serverMode};f(process.execPath,a,{env:r})}function eB(){let e=S(),t=[o.join(e,"dist","src","internal","daemon.js"),o.join(e,"dist","src","daemon.js")],a=t[0];if(void 0===a)throw new u("COMMAND_FAILED","Daemon dist path list is empty");let n=t.find(e=>r.existsSync(e))??a,i=o.join(e,"src","daemon.ts"),s=t.some(e=>r.existsSync(e)),l=r.existsSync(i);if(!s&&!l)throw new u("COMMAND_FAILED","Daemon entry not found",{distPaths:t,srcPath:i});return{root:e,distPath:n,distPaths:t,srcPath:i,useSrc:process.execArgv.includes("--experimental-strip-types")?l:!s&&l}}async function e$(e,t,a,r){return"http"===ej(e,a)?await eV(e,t,r):await ez(e,t,r)}function ej(e,t){if(e.baseUrl){if("socket"===t)throw new u("COMMAND_FAILED","Remote daemon endpoint only supports HTTP transport",{daemonBaseUrl:e.baseUrl});return"http"}if("http"===t||"socket"===t){var a=e,r=t;if(eH(a,r))return r;throw new u("COMMAND_FAILED","http"===r?"Daemon HTTP endpoint is unavailable":"Daemon socket endpoint is unavailable")}let n=("socket"===e.transport||"dual"===e.transport?["socket","http"]:["http","socket"]).find(t=>eH(e,t));if(n)return n;throw new u("COMMAND_FAILED","Daemon metadata has no reachable transport")}function eH(e,t){return"http"===t?!!e.httpPort:!!e.port}function eK(e,t,a,r,n,o){let i=n?{terminated:0}:function(){let e=0;try{for(let t of ep){let a=p("pkill",["-f",t],{allowFailure:!0});0===a.exitCode&&(e+=1)}return{terminated:e}}catch(t){return{terminated:e,error:t instanceof Error?t.message:String(t)}}}(),s=!n&&"snapshot"!==r,l=s?function(e,t){let a=!1;try{T(e.pid,e.processStartTime)&&(process.kill(e.pid,"SIGKILL"),a=!0)}catch{C(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}finally{ex(t.infoPath),ex(t.lockPath)}return{forcedKill:a}}(e,t):{forcedKill:!1};return M({level:"error",phase:"daemon_request_timeout",data:{timeoutMs:o,requestId:a,command:r,timedOutRunnerPidsTerminated:i.terminated,timedOutRunnerCleanupError:i.error,daemonPidReset:s?e.pid:void 0,daemonPidForceKilled:s?l.forcedKill:void 0,daemonPreservedAfterTimeout:!n&&!s,daemonBaseUrl:e.baseUrl}}),new u("COMMAND_FAILED","Daemon request timed out",{timeoutMs:o,requestId:a,hint:function(e){let{remote:t,resetDaemon:a,command:r}=e;if(t)return"Retry with --debug and verify the remote daemon URL, auth token, and remote host logs.";if(!a){let e=r===U.snapshot?" If this was the first Apple-platform snapshot on the device, run agent-device prepare ios-runner with the same --platform before snapshot/test so runner startup is handled explicitly.":"";return`Retry with --debug and check daemon diagnostics logs. The timed-out ${r??"request"} request was canceled and Apple runner work was aborted when detected; the daemon was kept alive so the session can still be closed or inspected.${e}`}return"Retry with --debug and check daemon diagnostics logs. Timed-out Apple runner xcodebuild processes were terminated when detected."}({remote:n,resetDaemon:s,command:r})})}function eG(e,t,a){return M({level:"error",phase:"daemon_request_socket_error",data:{requestId:t,message:e instanceof Error?e.message:String(e)}}),new u("COMMAND_FAILED","Failed to communicate with daemon",{requestId:t,hint:a?"Retry command. If this persists, verify the remote daemon URL, auth token, and remote host reachability.":"Retry command. If this persists, clean stale daemon metadata and start a fresh session."},e instanceof Error?e:void 0)}async function ez(t,a,r){let n=t.port;if(!n)throw new u("COMMAND_FAILED","Daemon socket endpoint is unavailable");return new Promise((o,i)=>{let s=e.createConnection({host:"127.0.0.1",port:n},()=>{s.write(`${JSON.stringify(a)}
|
|
3
|
-
`)}),l=A(a.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),c=!1,d="number"==typeof r?setTimeout(()=>{c=!0,s.destroy(),i(eK(t,l,a.meta?.requestId,a.command,!1,r))},r):void 0,p="";s.setEncoding("utf8"),s.on("data",e=>{if(c)return;let t=y(p,e);for(let e of(p=t.buffer,t.lines))try{let t=JSON.parse(e);if(v(t)){eu(t.event);continue}let a=I(t)?t.response:t;c=!0,s.end(),d&&clearTimeout(d),o(a);return}catch(t){c=!0,d&&clearTimeout(d),i(new u("COMMAND_FAILED","Invalid daemon response",{requestId:a.meta?.requestId,line:e},t instanceof Error?t:void 0));return}}),s.on("error",e=>{c||(c=!0,d&&clearTimeout(d),i(eG(e,a.meta?.requestId,!1)))})})}async function eV(e,r,n){var o,i,s;let l,c=e.baseUrl?new URL(eQ(e.baseUrl,"rpc")):e.httpPort?new URL(`http://127.0.0.1:${e.httpPort}/rpc`):null;if(!c)throw new u("COMMAND_FAILED","Daemon HTTP endpoint is unavailable");let d=JSON.stringify((o=r,i={includeTokenParam:!e.baseUrl},l=o.meta?.requestId??D(),"lease_allocate"!==(s=o.command)&&"lease_heartbeat"!==s&&"lease_release"!==s?{jsonrpc:"2.0",id:l,method:"agent_device.command",params:o}:{jsonrpc:"2.0",id:l,method:function(e){switch(e){case"lease_allocate":return"agent_device.lease.allocate";case"lease_heartbeat":return"agent_device.lease.heartbeat";case"lease_release":return"agent_device.lease.release"}}(o.command),params:function(e,t,a){let r={...a.includeTokenParam?{token:e.token}:{},session:e.session,tenantId:e.meta?.tenantId,runId:e.meta?.runId};switch(t){case"lease_allocate":return{...r,ttlMs:e.meta?.leaseTtlMs,backend:e.meta?.leaseBackend};case"lease_heartbeat":return{...r,leaseId:e.meta?.leaseId,ttlMs:e.meta?.leaseTtlMs};case"lease_release":return{...r,leaseId:e.meta?.leaseId}}}(o,o.command,i)})),p={"content-type":"application/json","content-length":Buffer.byteLength(d)};return e.baseUrl&&e.token&&(p.authorization=`Bearer ${e.token}`,p["x-agent-device-token"]=e.token),await new Promise((o,i)=>{let s=A(r.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),l=("https:"===c.protocol?a:t).request({protocol:c.protocol,host:c.hostname,port:c.port,method:"POST",path:c.pathname+c.search,headers:p},t=>{var a;(a=t.headers?.["content-type"],g(r)&&String(Array.isArray(a)?a.join(","):a??"").includes("application/x-ndjson"))?function(e,t){let{req:a,handleResponseBody:r,reject:n,clearTimeout:o}=t,i="",s=!1,l=e=>{try{let t=JSON.parse(e);if(v(t))return eu(t.event),!1;if(I(t))return s=!0,o(),r(JSON.stringify(t.response)),!0;throw Error("Missing daemon progress response envelope")}catch(t){return s=!0,o(),n(new u("COMMAND_FAILED","Invalid daemon response",{requestId:a.meta?.requestId,line:e},t instanceof Error?t:void 0)),!0}};e.setEncoding("utf8"),e.on("data",e=>{if(s)return;let t=y(i,e);for(let e of(i=t.buffer,t.lines))if(e&&l(e))return}),e.on("end",()=>{if(s)return;let e=i.trim();e&&l(e)||(s=!0,o(),n(new u("COMMAND_FAILED","Invalid daemon response",{requestId:a.meta?.requestId,line:e})))}),e.on("error",e=>{s||(s=!0,o(),n(new u("COMMAND_FAILED","Failed to read daemon response",{requestId:a.meta?.requestId},e instanceof Error?e:void 0)))})}(t,{req:r,reject:i,clearTimeout:()=>{f&&clearTimeout(f)},handleResponseBody:t=>eW(t,{info:e,req:r,resolve:o,reject:i})}):Z(t).then(t=>{f&&clearTimeout(f),eW(t,{info:e,req:r,resolve:o,reject:i})}).catch(e=>{f&&clearTimeout(f),i(new u("COMMAND_FAILED","Failed to read daemon response",{requestId:r.meta?.requestId},e instanceof Error?e:void 0))})}),m=eY(e),f="number"==typeof n?setTimeout(()=>{l.destroy(),i(eK(e,s,r.meta?.requestId,r.command,m,n))},n):void 0;l.on("error",e=>{f&&clearTimeout(f),i(eG(e,r.meta?.requestId,m))}),l.write(d),l.end()})}function eW(e,t){let{info:a,req:r,resolve:n,reject:o}=t;try{var i,s,l;let t=(i=e,JSON.parse(i));if(t.error){let e;return void o((s=t.error,l=r.meta?.requestId,e=s.data??{},new u(d(null!=e.code?String(e.code):void 0,"COMMAND_FAILED"),String(e.message??s.message??"Daemon RPC request failed"),{..."object"==typeof e.details&&e.details?e.details:{},hint:"string"==typeof e.hint?e.hint:void 0,diagnosticId:"string"==typeof e.diagnosticId?e.diagnosticId:void 0,logPath:"string"==typeof e.logPath?e.logPath:void 0,requestId:l})))}if(!t.result||"object"!=typeof t.result)return void o(new u("COMMAND_FAILED","Invalid daemon RPC response",{requestId:r.meta?.requestId}));eJ(a,r,t.result,n,o)}catch(t){o(new u("COMMAND_FAILED","Invalid daemon response",{requestId:r.meta?.requestId,line:e},t instanceof Error?t:void 0))}}async function eJ(e,t,a,r,n){try{r(e.baseUrl&&a.ok?await eX(e,t,a):a)}catch(e){n(e)}}function eY(e){return"string"==typeof e.baseUrl&&e.baseUrl.length>0}function eQ(e,t){return new URL(t,e.endsWith("/")?e:`${e}/`).toString()}async function eX(e,t,a){let r=Array.isArray(a.data?.artifacts)?a.data.artifacts:[];if(0===r.length||!e.baseUrl)return a;let n=a.data?{...a.data}:{},i=[];for(let a of r){if(!a||"object"!=typeof a||"string"!=typeof a.artifactId){i.push(a);continue}let r=function(e,t){if(e.localPath&&e.localPath.trim().length>0)return e.localPath;let a=t.meta?.clientArtifactPaths?.[e.field];if(a&&a.trim().length>0)return a;let r=e.fileName?.trim()||`${e.field}-${Date.now()}`;return o.resolve(t.meta?.cwd??process.cwd(),r)}(a,t);await eZ({baseUrl:e.baseUrl,token:e.token,artifactId:a.artifactId,destinationPath:r,requestId:t.meta?.requestId}),n[a.field]=r,i.push({...a,localPath:r})}return n.artifacts=i,{ok:!0,data:n}}async function eZ(e){var n,s;let l,c=new URL((n=e.baseUrl,s=e.artifactId,l=n.endsWith("/")?n:`${n}/`,new URL(`artifacts/${encodeURIComponent(s)}`,l).toString())),d="https:"===c.protocol?a:t;await r.promises.mkdir(o.dirname(e.destinationPath),{recursive:!0}),await new Promise((t,a)=>{let n=!1,o=e.timeoutMs??9e4,s=o=>{if(!n){if(n=!0,clearTimeout(p),o)return void r.promises.rm(e.destinationPath,{force:!0}).finally(()=>a(o));t()}},l=d.request({protocol:c.protocol,host:c.hostname,port:c.port,method:"GET",path:c.pathname+c.search,headers:e.token?{authorization:`Bearer ${e.token}`,"x-agent-device-token":e.token}:void 0},t=>{if((t.statusCode??500)>=400){let a="";t.setEncoding("utf8"),t.on("data",e=>{a+=e}),t.on("end",()=>{s(new u("COMMAND_FAILED","Failed to download remote artifact",{artifactId:e.artifactId,statusCode:t.statusCode,requestId:e.requestId,body:a}))});return}t.on("aborted",()=>{s(new u("COMMAND_FAILED","Remote artifact download was interrupted",{artifactId:e.artifactId,requestId:e.requestId}))}),i(t,r.createWriteStream(e.destinationPath)).then(()=>s(),e=>s(e instanceof Error?e:Error(String(e))))}),p=setTimeout(()=>{let t=new u("COMMAND_FAILED","Remote artifact download timed out",{artifactId:e.artifactId,requestId:e.requestId,timeoutMs:o});s(t),l.destroy(t)},o);l.on("error",t=>{t instanceof u?s(t):s(new u("COMMAND_FAILED","Failed to download remote artifact",{artifactId:e.artifactId,requestId:e.requestId,timeoutMs:o},t instanceof Error?t:void 0))}),l.end()})}function e0(e={},t={}){let a=t.transport??ef,r=async(t,r=[],n={})=>{var o,i;let s=(o=e,i=n,{...o,...i}),l=await a({session:F(s.session),command:t,positionals:r,flags:q(s),runtime:s.runtime,meta:J(s)});return l.ok||function(e){throw new u(d(e.code),e.message,{...e.details??{},hint:e.hint,diagnosticId:e.diagnosticId,logPath:e.logPath})}(l.error),l.data??{}},n=async(e={})=>{let t=await r(N.sessionList,[],e);return(Array.isArray(t.sessions)?t.sessions:[]).map(x)},o=async(e,t={})=>{let a=H(e,t);return await r(a.command,a.positionals,a.options)},i=(t={})=>{var a,r;return F((a=e,r=t,{...a,...r}).session)};return{command:{wait:async e=>await o("wait",e),alert:async(e={})=>await o("alert",e),appState:async(e={})=>await o("appstate",e),back:async(e={})=>await o("back",e),home:async(e={})=>await o("home",e),rotate:async e=>await o("rotate",e),appSwitcher:async(e={})=>await o("app-switcher",e),keyboard:async(e={})=>await o("keyboard",e),clipboard:async e=>await o("clipboard",e),reactNative:async e=>await o("react-native",e),prepare:async e=>await o("prepare",e)},devices:{list:async(e={})=>{let t=await o("devices",e);return(Array.isArray(t.devices)?t.devices:[]).map(Y)},boot:async(e={})=>await o("boot",e)},sessions:{list:async(e={})=>await n(e),close:async(e={})=>{let t=i(e),a=(await o("close",e)).shutdown;return{session:t,shutdown:"object"==typeof a&&null!==a?a:void 0,identifiers:{session:t}}}},apps:{install:async e=>z(await o("install",e),i(e)),reinstall:async e=>z(await o("reinstall",e),i(e)),installFromSource:async e=>$(await o("install-from-source",e),i(e)),list:async(e={})=>{let t=await o("apps",e);return Array.isArray(t.apps)?t.apps.filter(e=>"string"==typeof e):[]},open:async e=>{let t=i(e),a=await o("open",e),r=V(a),n=X(a,"appBundleId");return{session:t,sessionStateDir:X(a,"sessionStateDir"),appName:X(a,"appName"),appBundleId:n,appId:n,startup:B(a.startup),runtime:G(a.runtime),device:r,identifiers:{session:t,deviceId:r?.id,deviceName:r?.name,udid:r?.ios?.udid,serial:r?.android?.serial,appId:n,appBundleId:n}}},close:async(e={})=>{let t=i(e),a=(await o("close",e)).shutdown;return{session:t,closedApp:e.app,shutdown:"object"==typeof a&&null!==a?a:void 0,identifiers:{session:t}}},push:async e=>await o("push",e),triggerEvent:async e=>await o("trigger-app-event",e)},materializations:{release:async e=>j(await r(N.releaseMaterializedPaths,[],{...e,materializationId:e.materializationId}))},leases:{allocate:async e=>e5(await r(N.leaseAllocate,[],{...e,leaseId:void 0,leaseTtlMs:e.ttlMs})),heartbeat:async e=>e5(await r(N.leaseHeartbeat,[],{...e,leaseTtlMs:e.ttlMs})),release:async e=>({released:!0===(await r(N.leaseRelease,[],e)).released})},metro:{prepare:async t=>await O({projectRoot:t.projectRoot??e.cwd,kind:t.kind,publicBaseUrl:t.publicBaseUrl,proxyBaseUrl:t.proxyBaseUrl,proxyBearerToken:t.bearerToken,bridgeScope:t.bridgeScope,launchUrl:t.launchUrl,companionProfileKey:t.companionProfileKey,companionConsumerKey:t.companionConsumerKey,metroPort:t.port,listenHost:t.listenHost,statusHost:t.statusHost,startupTimeoutMs:t.startupTimeoutMs,probeTimeoutMs:t.probeTimeoutMs,reuseExisting:t.reuseExisting,installDependenciesIfNeeded:t.installDependenciesIfNeeded,runtimeFilePath:t.runtimeFilePath,logPath:t.logPath}),reload:async(t={})=>await R({metroHost:t.metroHost,metroPort:t.metroPort,bundleUrl:t.bundleUrl,runtime:e.runtime,timeoutMs:t.timeoutMs})},capture:{snapshot:async(e={})=>{var t,a,r;let n,s,l,c,d,u=i(e);return t=await o("snapshot",e),a=u,n=X(t,"appBundleId"),{nodes:K(t.nodes),truncated:!0===t.truncated,appName:X(t,"appName"),appBundleId:n,...(s=e1((r=t).visibility),l=e1(r.androidSnapshot),c=e1(r.unchanged),d=Array.isArray(r.warnings)?r.warnings.filter(e=>"string"==typeof e):void 0,{...s?{visibility:s}:{},...l?{androidSnapshot:l}:{},...c?{unchanged:c}:{},...d?{warnings:d}:{}}),identifiers:{session:a,appId:n,appBundleId:n}}},screenshot:async(e={})=>{let t=i(e),a=await o("screenshot",e);return{path:Q(a,"path"),overlayRefs:W(a),identifiers:{session:t}}},diff:async e=>await o("diff",e)},interactions:{click:async e=>await o("click",e),press:async e=>await o("press",e),longPress:async e=>await o("longpress",e),swipe:async e=>await o("swipe",e),pan:async e=>await o("gesture-pan",e),fling:async e=>await o("gesture-fling",e),swipeGesture:async e=>await o("gesture-swipe",e),focus:async e=>await o("focus",e),type:async e=>await o("type",e),fill:async e=>await o("fill",e),scroll:async e=>await o("scroll",e),pinch:async e=>await o("gesture-pinch",e),rotateGesture:async e=>await o("gesture-rotate",e),transformGesture:async e=>await o("gesture-transform",e),get:async e=>await o("get",e),is:async e=>await o("is",e),find:async e=>await o("find",e)},replay:{run:async e=>await o("replay",e),test:async e=>await o("test",e)},batch:{run:async e=>await o("batch",e)},observability:{perf:async(e={})=>await o("perf",e),logs:async(e={})=>await o("logs",e),network:async(e={})=>await o("network",e)},recording:{record:async e=>await o("record",e),trace:async e=>await o("trace",e)},settings:{update:async e=>await o("settings",e)}}}function e1(e){return"object"==typeof e&&null!==e?e:void 0}function e5(e){let t=e.lease;if(!t||"object"!=typeof t||Array.isArray(t))throw Error("Invalid lease response from daemon");return{leaseId:Q(t,"leaseId"),tenantId:Q(t,"tenantId"),runId:Q(t,"runId"),backend:Q(t,"backend"),createdAt:"number"==typeof t.createdAt?t.createdAt:void 0,heartbeatAt:"number"==typeof t.heartbeatAt?t.heartbeatAt:void 0,expiresAt:"number"==typeof t.expiresAt?t.expiresAt:void 0}}export{e0 as createAgentDeviceClient,ef as sendToDaemon};
|
|
1
|
+
import e from"node:net";import t from"node:http";import a from"node:https";import r from"node:fs";import n from"node:os";import o from"node:path";import{pipeline as i}from"node:stream/promises";import{createHash as s,randomUUID as l}from"node:crypto";import{Writable as c}from"node:stream";import{toAppErrorCode as d,AppError as u}from"./9152.js";import{runCmdSync as p,runCmd as m,runCmdDetached as f}from"./9818.js";import{consumeTextLines as h,shellQuote as y,shouldStreamRequestProgress as w,isDaemonResponseEnvelope as g,resolveDaemonPaths as I,isDaemonProgressEnvelope as v,resolveDaemonServerMode as A,computeDaemonCodeSignature as _,resolveDaemonTransportPreference as P}from"./9238.js";import{readVersion as k,findProjectRoot as S}from"./9671.js";import{createRequestId as b,withDiagnosticTimer as D,emitDiagnostic as E}from"./7599.js";import{isAgentDeviceDaemonProcess as M,stopProcessForTakeover as T}from"./8656.js";import{INTERNAL_COMMANDS as N,PUBLIC_COMMANDS as C}from"./5792.js";import{sleep as L}from"./4829.js";import{reloadMetro as U,prepareMetroRuntime as R}from"./1974.js";import{normalizeSession as x,resolveSessionName as O,buildFlags as $,normalizeStartupSample as F,normalizeInstallFromSourceResult as B,normalizeMaterializationReleaseResult as q,prepareDaemonCommandRequest as j,readSnapshotNodes as K,normalizeRuntimeHints as H,normalizeDeployResult as G,normalizeOpenDevice as z,readScreenshotOverlayRefs as V,buildMeta as J,normalizeDevice as W}from"./8699.js";import{readRequiredString as Y,readOptionalString as Q}from"./7455.js";function X(e){return new Promise((t,a)=>{let r="";e.setEncoding("utf8"),e.on("data",e=>{r+=e}),e.on("end",()=>t(r)),e.on("error",a)})}let Z="sha256";async function ee(e){let t=await et(e.localPath,e.platform),a=e.baseUrl.endsWith("/")?e.baseUrl:`${e.baseUrl}/`;try{let r=await eo({normalizedBase:a,token:e.token,artifact:t});if(r?.kind==="cache-hit")return r.uploadId;if(r?.kind==="direct-upload")try{return await ei(t.payloadPath,r),await el({normalizedBase:a,token:e.token,uploadId:r.uploadId})}catch{}return await en({normalizedBase:a,token:e.token,artifact:t})}finally{t.cleanup()}}async function et(e,t){var a,n,i;let s,l=r.statSync(e),c=o.basename(e),d=l.isDirectory(),u=("ios"===(a=t)||"android"===a?a:void 0)??(n=e,i=l,s=n.toLowerCase(),i.isDirectory()&&s.endsWith(".app")||s.endsWith(".ipa")?"ios":s.endsWith(".apk")||s.endsWith(".aab")?"android":void 0),p=[];try{let t=d?await ea(e,p):e,a=r.statSync(t);return{payloadPath:t,fileName:c,artifactType:d?"app-bundle":"file",platform:u,contentType:d?"application/gzip":"application/octet-stream",sha256:await ec(t),sizeBytes:a.size,cleanup:()=>er(p)}}catch(e){throw er(p),e}}async function ea(e,t){let a=r.mkdtempSync(o.join(n.tmpdir(),`agent-device-upload-${l()}-`));t.push(a);let i=o.join(a,`${o.basename(e)}.tar.gz`);return await m("tar",["czf",i,"-C",o.dirname(e),o.basename(e)],{env:{...process.env,COPYFILE_DISABLE:"1"}}),i}function er(e){for(let t of e)r.rmSync(t,{recursive:!0,force:!0})}async function en(e){let{normalizedBase:t,token:a,artifact:r}=e,n=new URL("upload",t),o={"content-type":r.contentType,"x-artifact-type":r.artifactType,"x-artifact-filename":r.fileName,"x-artifact-hash":r.sha256,"x-artifact-hash-algorithm":Z,"transfer-encoding":"chunked"};a&&(o.authorization=`Bearer ${a}`,o["x-agent-device-token"]=a);let i=await es({url:n,method:"POST",headers:o,payloadPath:r.payloadPath,timeoutMessage:"Artifact upload timed out",timeoutHint:"The upload to the remote daemon exceeded the 5-minute timeout.",errorMessage:"Failed to upload artifact to remote daemon",errorHint:"Verify the remote daemon is reachable and supports artifact uploads."});try{let e=JSON.parse(i.body);if(!e.ok||!e.uploadId)throw new u("COMMAND_FAILED",`Upload failed: ${i.body}`);return e.uploadId}catch(e){if(e instanceof u)throw e;throw new u("COMMAND_FAILED",`Invalid upload response: ${i.body}`)}}async function eo(e){let t=new URL("upload/preflight",e.normalizedBase),a={"content-type":"application/json"};e.token&&(a.authorization=`Bearer ${e.token}`,a["x-agent-device-token"]=e.token);let r=await fetch(t,{method:"POST",headers:a,signal:AbortSignal.timeout(3e4),body:JSON.stringify({sha256:e.artifact.sha256,fileName:e.artifact.fileName,sizeBytes:e.artifact.sizeBytes,artifactType:e.artifact.artifactType,...e.artifact.platform?{platform:e.artifact.platform}:{},contentType:e.artifact.contentType})}).catch(()=>void 0);if(r?.ok)return function(e){var t;if(!e||"object"!=typeof e||!0!==e.ok||"string"!=typeof e.uploadId)return;if(!0===e.cacheHit)return{kind:"cache-hit",uploadId:e.uploadId};let a=e.upload;if(!a||"string"!=typeof a.url)return;let r=a.headers??{};if(!(!(t=r)||"object"!=typeof t||Array.isArray(t))&&Object.values(t).every(e=>"string"==typeof e))return{kind:"direct-upload",uploadId:e.uploadId,url:a.url,headers:r}}(await r.json().catch(()=>void 0))}async function ei(e,t){let a=await es({url:new URL(t.url),method:"PUT",headers:t.headers,payloadPath:e,timeoutMessage:"Direct artifact upload timed out",timeoutHint:"The direct upload ticket did not accept the artifact within the timeout.",errorMessage:"Failed to upload artifact with direct upload ticket"});if(a.statusCode<200||a.statusCode>=300)throw new u("COMMAND_FAILED","Direct artifact upload failed",{statusCode:a.statusCode,statusMessage:a.statusMessage})}async function es(e){let n="https:"===e.url.protocol?a:t;return await new Promise((t,a)=>{let o=n.request({protocol:e.url.protocol,host:e.url.hostname,port:e.url.port,method:e.method,path:e.url.pathname+e.url.search,headers:e.headers},e=>{X(e).then(a=>{clearTimeout(s),t({statusCode:e.statusCode??500,statusMessage:e.statusMessage,body:a})}).catch(a)}),s=setTimeout(()=>{o.destroy(),a(new u("COMMAND_FAILED",e.timeoutMessage,{timeoutMs:3e5,...e.timeoutHint?{hint:e.timeoutHint}:{}}))},3e5);o.on("error",t=>{clearTimeout(s),a(new u("COMMAND_FAILED",e.errorMessage,e.errorHint?{hint:e.errorHint}:{},t))}),o.on("close",()=>clearTimeout(s)),i(r.createReadStream(e.payloadPath),o).catch(e=>{o.destroy(),a(new u("COMMAND_FAILED","Failed to read local artifact",{},e instanceof Error?e:Error(String(e))))})})}async function el(e){let t=new URL("upload/finalize",e.normalizedBase),a={"content-type":"application/json"};e.token&&(a.authorization=`Bearer ${e.token}`,a["x-agent-device-token"]=e.token);let r=await fetch(t,{method:"POST",headers:a,signal:AbortSignal.timeout(3e4),body:JSON.stringify({uploadId:e.uploadId})}).catch(e=>{throw new u("COMMAND_FAILED","Failed to finalize direct artifact upload",{},e)});if(!r.ok)throw new u("COMMAND_FAILED","Direct artifact upload finalize failed",{status:r.status,statusText:r.statusText});let n=await r.json().catch(()=>void 0);if(!n?.ok||!n.uploadId)throw new u("COMMAND_FAILED","Invalid upload finalize response");return n.uploadId}async function ec(e){let t=s(Z),a=new c({write(e,a,r){t.update(e),r()}});return await i(r.createReadStream(e),a).catch(e=>{throw new u("COMMAND_FAILED","Failed to read local artifact",{},e instanceof Error?e:void 0)}),t.digest("hex")}function ed(e){let t=Math.max(0,e)/1e3;return t>=10?`${t.toFixed(1)}s`:t>=1?`${t.toFixed(2)}s`:`${t.toFixed(3).replace(/0+$/,"").replace(/\.$/,"")}s`}function eu(e){let t=function(e){var t,a,r;let n,i;if("replay-test"!==e.type||"pass"===e.status||"fail"===e.status&&!e.retrying)return;let s=(t=e,(n=t.title?.trim())?JSON.stringify(n):o.basename(t.file)),l=void 0!==e.durationMs?` (${i=ed((a=e).durationMs??0),a.attempt&&a.attempt>1&&!a.retrying?`total ${i}`:i})`:"",c=void 0===(r=e).attempt?"":"fail"===r.status&&r.retrying&&void 0!==r.maxAttempts?` attempt ${r.attempt}/${r.maxAttempts} retrying`:r.attempt>1?` after ${r.attempt} attempts`:"",d=e.message?.replace(/\s+/g," ").trim();return"skip"===e.status?[`SKIP ${s}`,d?` ${d}`:""].filter(Boolean).join("\n"):[`FAIL ${s}${c}${l}`,d?` ${d}`:"",e.artifactsDir?` artifacts: ${e.artifactsDir}`:""].filter(Boolean).join("\n")}(e);t&&process.stderr.write(`${t}
|
|
2
|
+
`)}let ep=["xcodebuild .*AgentDeviceRunnerUITests/RunnerTests/testCommand","xcodebuild .*AgentDeviceRunner\\.env\\.session-","xcodebuild build-for-testing .*ios-runner/AgentDeviceRunner/AgentDeviceRunner\\.xcodeproj"],em=new e.BlockList;async function ef(t){var a,i,s,l,c,d;let p,m,f,h,y,w,g,v=t.meta?.requestId??b(),_=!!(t.meta?.debug||t.flags?.verbose),k=(h=(i=a=t,i.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),y=(s=a,m=function(e){let t;if(e){try{t=new URL(e)}catch(t){throw new u("INVALID_ARGS","Invalid daemon base URL",{daemonBaseUrl:e},t instanceof Error?t:void 0)}if("http:"!==t.protocol&&"https:"!==t.protocol)throw new u("INVALID_ARGS","Daemon base URL must use http or https",{daemonBaseUrl:e});return t.toString().replace(/\/+$/,"")}}(p=s.flags?.daemonBaseUrl??process.env.AGENT_DEVICE_DAEMON_BASE_URL),function(t,a){let r;if(!(!t||"localhost"===(r=new URL(t).hostname.trim().toLowerCase().replace(/^\[(.*)\]$/,"$1"))||(e.isIPv4(r)?em.check(r,"ipv4"):!!e.isIPv6(r)&&em.check(r,"ipv6")))&&("string"!=typeof a||!(a.trim().length>0)))throw new u("INVALID_ARGS","Remote daemon base URL for non-loopback hosts requires daemon authentication",{daemonBaseUrl:t,hint:"Provide --daemon-auth-token or AGENT_DEVICE_DAEMON_AUTH_TOKEN when using a non-loopback remote daemon URL."})}(m,f=s.flags?.daemonAuthToken??process.env.AGENT_DEVICE_DAEMON_AUTH_TOKEN),{rawBaseUrl:p,remoteBaseUrl:m,authToken:f}),w=function(e,t){let a=e.flags?.daemonTransport??process.env.AGENT_DEVICE_DAEMON_TRANSPORT,r=P(a);if(t&&"socket"===r)throw new u("INVALID_ARGS","Remote daemon base URL only supports HTTP transport. Remove --daemon-transport socket.",{daemonBaseUrl:t});return{preference:r,serverMode:A(e.flags?.daemonServerMode??process.env.AGENT_DEVICE_DAEMON_SERVER_MODE??("dual"===a?"dual":void 0))}}(a,y.remoteBaseUrl),{paths:I((g=(l=a,c=h,d=y.rawBaseUrl,eb(l.command)&&!c&&!d))?r.mkdtempSync(o.join(n.tmpdir(),"agent-device-replay-daemon-")):h),transportPreference:w.preference,serverMode:w.serverMode,ownedStateDir:g,remoteBaseUrl:y.remoteBaseUrl,remoteAuthToken:y.authToken}),S=function(e){if(e.command!==C.test){var t;return"number"==typeof e.flags?.timeoutMs&&((t=e.command)===C.prepare||t===C.replay||t===C.snapshot)?e.flags.timeoutMs:e.command===C.prepare?24e4:9e4}}(t),M=await D("daemon_startup",async()=>await eA(k),{requestId:v,session:t.session}),T=M.info,N=await eh(t,T),L={...t,positionals:N.positionals,flags:N.flags,token:T.token,meta:{...t.meta??{},requestId:v,debug:_,cwd:t.meta?.cwd,sessionExplicit:t.meta?.sessionExplicit,tenantId:t.meta?.tenantId??t.flags?.tenant,runId:t.meta?.runId??t.flags?.runId,leaseId:t.meta?.leaseId??t.flags?.leaseId,sessionIsolation:t.meta?.sessionIsolation??t.flags?.sessionIsolation,lockPolicy:t.meta?.lockPolicy,lockPlatform:t.meta?.lockPlatform,...N.uploadedArtifactId?{uploadedArtifactId:N.uploadedArtifactId}:{},...N.clientArtifactPaths?{clientArtifactPaths:N.clientArtifactPaths}:{},...N.installSource?{installSource:N.installSource}:{}}};E({level:"info",phase:"daemon_request_prepare",data:{requestId:v,command:t.command,session:t.session}});try{return await D("daemon_request",async()=>await eq(T,L,k.transportPreference,S),{requestId:v,command:t.command})}finally{await eS(t,M,k)}}async function eh(e,t){let a,r=[...e.positionals??[]],n=e.flags?{...e.flags}:void 0,i=e.meta?.installSource,s={};if(!eY(t))return ew({positionals:r,flags:n,installSource:i,uploadedArtifactId:a,clientArtifactPaths:s});n=function(e,t,a,r){var n,i;let s=function(e,t){if("screenshot"===e.command){let a=eI(e,"path",".png");return t[0]?{field:"path",localPath:a,positionalIndex:0,positionalPath:ev("screenshot",".png")}:{field:"path",localPath:a,positionalIndex:0,flagPath:ev("screenshot",".png")}}if("record"===e.command&&"start"===(t[0]??"").toLowerCase()){let t=eI(e,"outPath",".mp4",1);return{field:"outPath",localPath:t,positionalIndex:1,positionalPath:ev("recording",o.extname(t)||".mp4")}}return null}(e,t);if(!s)return a;void 0!==s.positionalPath&&(t[s.positionalIndex]=s.positionalPath);let l=(n=a,void 0===(i=s.flagPath)?n:{...n??{},out:i});return r[s.field]=s.localPath,l}(e,r,n,s);let l=await eg(e,t);l&&(i=l.installSource,a=l.uploadedArtifactId??a);let c=()=>ew({positionals:r,flags:n,installSource:i,uploadedArtifactId:a,clientArtifactPaths:s});return"install"!==e.command&&"reinstall"!==e.command||(a=await ey(e,t,r)??a),c()}async function ey(e,t,a){var n,i;let s,l=a[1];if(void 0===l)return;if(l.startsWith("remote:")){a[1]=l.slice(7);return}let c=(n=l,i=e.meta?.cwd,s=o.isAbsolute(n)?n:o.resolve(i??process.cwd(),n),r.existsSync(s)?s:void 0);if(c)return await ee({localPath:c,baseUrl:t.baseUrl,token:t.token,platform:e.flags?.platform})}function ew(e){return{positionals:e.positionals,flags:e.flags,installSource:e.installSource,uploadedArtifactId:e.uploadedArtifactId,...Object.keys(e.clientArtifactPaths).length>0?{clientArtifactPaths:e.clientArtifactPaths}:{}}}async function eg(e,t){let a=e.meta?.installSource;if("install_source"!==e.command||!a||"path"!==a.kind)return null;let n=a.path.trim();if(!n)return{installSource:a};if(n.startsWith("remote:"))return{installSource:{...a,path:n.slice(7)}};let i=o.isAbsolute(n)?n:o.resolve(e.meta?.cwd??process.cwd(),n);if(!r.existsSync(i))return{installSource:{...a,path:i}};let s=await ee({localPath:i,baseUrl:t.baseUrl,token:t.token,platform:e.flags?.platform});return{installSource:{...a,path:i},uploadedArtifactId:s}}function eI(e,t,a,r=0){let n=e.positionals?.[r]??e.flags?.out,i=`${"path"===t?"screenshot":"recording"}-${Date.now()}${a}`,s=n&&n.trim().length>0?n:i;return o.isAbsolute(s)?s:o.resolve(e.meta?.cwd??process.cwd(),s)}function ev(e,t){let a=t.startsWith(".")?t:`.${t}`;return o.posix.join("/tmp",`agent-device-${e}-${Date.now()}-${Math.random().toString(36).slice(2,8)}${a}`)}async function eA(e){if(e.remoteBaseUrl)return await e_(e);let t=await eP(e);return t?{info:t,startedByClient:!1}:(function(e){let t=eR(e);if(!t.hasLock||t.hasInfo)return;let a=eL(e.lockPath);if(!a)return eO(e.lockPath);M(a.pid,a.processStartTime)||eO(e.lockPath)}(e.paths),await ek(e))}async function e_(e){let t={transport:"http",token:e.remoteAuthToken??"",pid:0,baseUrl:e.remoteBaseUrl};if(await e$(t,"http"))return{info:t,startedByClient:!1};throw new u("COMMAND_FAILED","Remote daemon is unavailable",{daemonBaseUrl:e.remoteBaseUrl,hint:"Verify AGENT_DEVICE_DAEMON_BASE_URL points to a reachable daemon with GET /health and POST /rpc."})}async function eP(e){var t,a;let r,n=eT(e.paths.infoPath);if(!n)return null;let o=await e$(n,e.transportPreference);return(t=n,a=o,t.version===k()&&t.codeSignature===_((r=eB()).useSrc?r.srcPath:r.distPath,r.root)&&a)?n:(await eM(n),eO(e.paths.infoPath),null)}async function ek(e){let t,a=0,r=[];for(let n=1;n<=2;n+=1){try{await eF(e)}catch(a){if(t=a instanceof Error?a.message:String(a),r.push(await eU(e.paths,"start_error")),n<2){await L(150);continue}break}let o=await eD(15e3,e);if(o)return{info:o,startedByClient:!0};if(await eE(e.paths)){a+=1;continue}let i=eR(e.paths),s=n<2,l=await eU(e.paths,"startup_timeout",{stopLiveProcesses:!1});if(r.push(l),l.retainedInfoProcess||l.retainedLockProcess){let t=await eD(15e3,e);if(t)return{info:t,startedByClient:!0};break}if(!s)break;i.hasInfo||i.hasLock||await L(150)}let n=eR(e.paths);throw new u("COMMAND_FAILED","Failed to start daemon",{kind:"daemon_startup_failed",infoPath:e.paths.infoPath,lockPath:e.paths.lockPath,startupTimeoutMs:15e3,startupAttempts:2,lockRecoveryCount:a,cleanupResults:r,startError:t,metadataState:n,hint:function(e,t=I(process.env.AGENT_DEVICE_STATE_DIR)){var a;let r=(a=t,`rm -f ${y(a.infoPath)} ${y(a.lockPath)}`);return e.hasLock&&!e.hasInfo?`agent-device attempted to clean stale daemon metadata automatically, but ${t.lockPath} still exists without ${t.infoPath}. Retry with --debug; if this persists after confirming no agent-device daemon process is running, run: ${r}`:e.hasLock&&e.hasInfo?`agent-device attempted to clean stale daemon metadata automatically, but ${t.infoPath} and ${t.lockPath} still remain. Retry with --debug; if this persists after confirming no agent-device daemon process is running, run: ${r}`:e.hasInfo?`agent-device did not observe reachable daemon metadata after retrying, and ${t.infoPath} still remains. Stale metadata was cleaned automatically when safe; retry with --debug. If this persists after confirming no agent-device daemon process is running, run: ${r}`:`agent-device did not observe reachable daemon metadata after retrying. Stale metadata was cleaned automatically when safe; retry with --debug and check daemon diagnostics logs. If stale metadata returns after confirming no agent-device daemon process is running, run: ${r}`}(n,e.paths)})}async function eS(e,t,a){if(!eb(e.command)||!t.startedByClient&&!a.ownedStateDir||eY(t.info))return;let n={pid:t.info.pid,removedInfo:!1,removedLock:!1,removedStateDir:!1,error:void 0};try{await eM(t.info)}catch(e){n.error=e instanceof Error?e.message:String(e)}finally{let e=r.existsSync(a.paths.infoPath);eO(a.paths.infoPath),n.removedInfo=e&&!r.existsSync(a.paths.infoPath);let t=r.existsSync(a.paths.lockPath);eO(a.paths.lockPath),n.removedLock=t&&!r.existsSync(a.paths.lockPath),a.ownedStateDir&&(r.rmSync(a.paths.baseDir,{recursive:!0,force:!0}),n.removedStateDir=!r.existsSync(a.paths.baseDir))}E({level:n.error?"warn":"info",phase:"daemon_replay_cleanup",data:n})}function eb(e){return e===C.replay||e===C.test}async function eD(e,t){let a=Date.now();for(;Date.now()-a<e;){let e=eT(t.paths.infoPath);if(e&&await e$(e,t.transportPreference))return e;await L(100)}return null}async function eE(e){let t=eR(e);if(!t.hasLock||t.hasInfo)return!1;let a=eL(e.lockPath);return!(a&&M(a.pid,a.processStartTime))&&(eO(e.lockPath),!0)}async function eM(e){await T(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}function eT(e){var t,a,r;let n,o,i=ex(e);if(!i||"object"!=typeof i)return null;let s="string"==typeof(t=i).token&&t.token.length>0?t.token:null;if(!s)return null;let l=(n=eC((a=i).port),o=eC(a.httpPort),void 0===n&&void 0===o?null:{port:n,httpPort:o});return l?{token:s,...l,transport:"socket"===(r=i.transport)||"http"===r||"dual"===r?r:void 0,pid:eC(i.pid)??0,version:eN(i.version),codeSignature:eN(i.codeSignature),processStartTime:eN(i.processStartTime)}:null}function eN(e){return"string"==typeof e?e:void 0}function eC(e){return Number.isInteger(e)&&Number(e)>0?Number(e):void 0}function eL(e){let t=ex(e);return t&&"object"==typeof t&&Number.isInteger(t.pid)&&Number(t.pid)>0?{pid:Number(t.pid),processStartTime:"string"==typeof t.processStartTime?t.processStartTime:void 0,startedAt:"number"==typeof t.startedAt?t.startedAt:void 0}:null}em.addSubnet("127.0.0.0",8,"ipv4"),em.addAddress("::1","ipv6"),em.addSubnet("::ffff:127.0.0.0",104,"ipv6");async function eU(e,t,a={}){let n=a.stopLiveProcesses??!0,o={reason:t,removedInfo:!1,removedLock:!1,stoppedInfoProcess:!1,stoppedLockProcess:!1};try{var i,s,l,c;let t=r.existsSync(e.infoPath),a=eT(e.infoPath);if(a){let t=M(a.pid,a.processStartTime);t&&!n?o.retainedInfoProcess=!0:(t&&(await eM(a),o.stoppedInfoProcess=!0),i=e.infoPath,eO(i),o.removedInfo=!0)}else t&&(s=e.infoPath,eO(s),o.removedInfo=!0);let d=r.existsSync(e.lockPath),u=eL(e.lockPath);if(u){let t=M(u.pid,u.processStartTime);t&&!n?o.retainedLockProcess=!0:(t&&(await T(u.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:u.processStartTime}),o.stoppedLockProcess=!0),l=e.lockPath,eO(l),o.removedLock=!0)}else d&&(c=e.lockPath,eO(c),o.removedLock=!0)}catch(e){o.error=e instanceof Error?e.message:String(e)}return E({level:o.error?"warn":"info",phase:"daemon_startup_metadata_cleanup",data:o}),o}function eR(e){return{hasInfo:r.existsSync(e.infoPath),hasLock:r.existsSync(e.lockPath)}}function ex(e){if(!r.existsSync(e))return null;try{return JSON.parse(r.readFileSync(e,"utf8"))}catch{return null}}function eO(e){try{r.existsSync(e)&&r.unlinkSync(e)}catch{}}async function e$(r,n){var o;return"http"===ej(r,n)?await function(e){let r=e.baseUrl?eQ(e.baseUrl,"health"):e.httpPort?`http://127.0.0.1:${e.httpPort}/health`:null;if(!r)return Promise.resolve(!1);let n=new URL(r),o="https:"===n.protocol?a:t,i=e.baseUrl?3e3:500;return new Promise(e=>{let t=o.request({protocol:n.protocol,host:n.hostname,port:n.port,path:n.pathname+n.search,method:"GET",timeout:i},t=>{t.resume(),e((t.statusCode??500)<500)});t.on("timeout",()=>{t.destroy(),e(!1)}),t.on("error",()=>{e(!1)}),t.end()})}(r):await ((o=r.port)?new Promise(t=>{let a=!1,r=e.createConnection({host:"127.0.0.1",port:o},()=>{n(!0)}),n=e=>{a||(a=!0,r.destroy(),t(e))};r.setTimeout(500),r.on("timeout",()=>{n(!1)}),r.on("error",()=>{n(!1)})}):Promise.resolve(!1))}async function eF(e){let t=eB(),a=t.useSrc?["--experimental-strip-types",t.srcPath]:[t.distPath],r={...process.env,AGENT_DEVICE_STATE_DIR:e.paths.baseDir,AGENT_DEVICE_DAEMON_SERVER_MODE:e.serverMode};f(process.execPath,a,{env:r})}function eB(){let e=S(),t=[o.join(e,"dist","src","internal","daemon.js"),o.join(e,"dist","src","daemon.js")],a=t[0];if(void 0===a)throw new u("COMMAND_FAILED","Daemon dist path list is empty");let n=t.find(e=>r.existsSync(e))??a,i=o.join(e,"src","daemon.ts"),s=t.some(e=>r.existsSync(e)),l=r.existsSync(i);if(!s&&!l)throw new u("COMMAND_FAILED","Daemon entry not found",{distPaths:t,srcPath:i});return{root:e,distPath:n,distPaths:t,srcPath:i,useSrc:process.execArgv.includes("--experimental-strip-types")?l:!s&&l}}async function eq(e,t,a,r){return"http"===ej(e,a)?await eV(e,t,r):await ez(e,t,r)}function ej(e,t){if(e.baseUrl){if("socket"===t)throw new u("COMMAND_FAILED","Remote daemon endpoint only supports HTTP transport",{daemonBaseUrl:e.baseUrl});return"http"}if("http"===t||"socket"===t){var a=e,r=t;if(eK(a,r))return r;throw new u("COMMAND_FAILED","http"===r?"Daemon HTTP endpoint is unavailable":"Daemon socket endpoint is unavailable")}let n=("socket"===e.transport||"dual"===e.transport?["socket","http"]:["http","socket"]).find(t=>eK(e,t));if(n)return n;throw new u("COMMAND_FAILED","Daemon metadata has no reachable transport")}function eK(e,t){return"http"===t?!!e.httpPort:!!e.port}function eH(e,t,a,r,n,o){let i=n?{terminated:0}:function(){let e=0;try{for(let t of ep){let a=p("pkill",["-f",t],{allowFailure:!0});0===a.exitCode&&(e+=1)}return{terminated:e}}catch(t){return{terminated:e,error:t instanceof Error?t.message:String(t)}}}(),s=!n&&"snapshot"!==r,l=s?function(e,t){let a=!1;try{M(e.pid,e.processStartTime)&&(process.kill(e.pid,"SIGKILL"),a=!0)}catch{T(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}finally{eO(t.infoPath),eO(t.lockPath)}return{forcedKill:a}}(e,t):{forcedKill:!1};return E({level:"error",phase:"daemon_request_timeout",data:{timeoutMs:o,requestId:a,command:r,timedOutRunnerPidsTerminated:i.terminated,timedOutRunnerCleanupError:i.error,daemonPidReset:s?e.pid:void 0,daemonPidForceKilled:s?l.forcedKill:void 0,daemonPreservedAfterTimeout:!n&&!s,daemonBaseUrl:e.baseUrl}}),new u("COMMAND_FAILED","Daemon request timed out",{timeoutMs:o,requestId:a,hint:function(e){let{remote:t,resetDaemon:a,command:r}=e;if(t)return"Retry with --debug and verify the remote daemon URL, auth token, and remote host logs.";if(!a){let e=r===C.snapshot?" If this was the first Apple-platform snapshot on the device, run agent-device prepare ios-runner with the same --platform before snapshot/test so runner startup is handled explicitly.":"";return`Retry with --debug and check daemon diagnostics logs. The timed-out ${r??"request"} request was canceled and Apple runner work was aborted when detected; the daemon was kept alive so the session can still be closed or inspected.${e}`}return"Retry with --debug and check daemon diagnostics logs. Timed-out Apple runner xcodebuild processes were terminated when detected."}({remote:n,resetDaemon:s,command:r})})}function eG(e,t,a){return E({level:"error",phase:"daemon_request_socket_error",data:{requestId:t,message:e instanceof Error?e.message:String(e)}}),new u("COMMAND_FAILED","Failed to communicate with daemon",{requestId:t,hint:a?"Retry command. If this persists, verify the remote daemon URL, auth token, and remote host reachability.":"Retry command. If this persists, clean stale daemon metadata and start a fresh session."},e instanceof Error?e:void 0)}async function ez(t,a,r){let n=t.port;if(!n)throw new u("COMMAND_FAILED","Daemon socket endpoint is unavailable");return new Promise((o,i)=>{let s=e.createConnection({host:"127.0.0.1",port:n},()=>{s.write(`${JSON.stringify(a)}
|
|
3
|
+
`)}),l=I(a.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),c=!1,d="number"==typeof r?setTimeout(()=>{c=!0,s.destroy(),i(eH(t,l,a.meta?.requestId,a.command,!1,r))},r):void 0,p="";s.setEncoding("utf8"),s.on("data",e=>{if(c)return;let t=h(p,e);for(let e of(p=t.buffer,t.lines))try{let t=JSON.parse(e);if(v(t)){eu(t.event);continue}let a=g(t)?t.response:t;c=!0,s.end(),d&&clearTimeout(d),o(a);return}catch(t){c=!0,d&&clearTimeout(d),i(new u("COMMAND_FAILED","Invalid daemon response",{requestId:a.meta?.requestId,line:e},t instanceof Error?t:void 0));return}}),s.on("error",e=>{c||(c=!0,d&&clearTimeout(d),i(eG(e,a.meta?.requestId,!1)))})})}async function eV(e,r,n){var o,i,s;let l,c=e.baseUrl?new URL(eQ(e.baseUrl,"rpc")):e.httpPort?new URL(`http://127.0.0.1:${e.httpPort}/rpc`):null;if(!c)throw new u("COMMAND_FAILED","Daemon HTTP endpoint is unavailable");let d=JSON.stringify((o=r,i={includeTokenParam:!e.baseUrl},l=o.meta?.requestId??b(),"lease_allocate"!==(s=o.command)&&"lease_heartbeat"!==s&&"lease_release"!==s?{jsonrpc:"2.0",id:l,method:"agent_device.command",params:o}:{jsonrpc:"2.0",id:l,method:function(e){switch(e){case"lease_allocate":return"agent_device.lease.allocate";case"lease_heartbeat":return"agent_device.lease.heartbeat";case"lease_release":return"agent_device.lease.release"}}(o.command),params:function(e,t,a){let r={...a.includeTokenParam?{token:e.token}:{},session:e.session,tenantId:e.meta?.tenantId,runId:e.meta?.runId};switch(t){case"lease_allocate":return{...r,ttlMs:e.meta?.leaseTtlMs,backend:e.meta?.leaseBackend};case"lease_heartbeat":return{...r,leaseId:e.meta?.leaseId,ttlMs:e.meta?.leaseTtlMs};case"lease_release":return{...r,leaseId:e.meta?.leaseId}}}(o,o.command,i)})),p={"content-type":"application/json","content-length":Buffer.byteLength(d)};return e.baseUrl&&e.token&&(p.authorization=`Bearer ${e.token}`,p["x-agent-device-token"]=e.token),await new Promise((o,i)=>{let s=I(r.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),l=("https:"===c.protocol?a:t).request({protocol:c.protocol,host:c.hostname,port:c.port,method:"POST",path:c.pathname+c.search,headers:p},t=>{var a;(a=t.headers?.["content-type"],w(r)&&String(Array.isArray(a)?a.join(","):a??"").includes("application/x-ndjson"))?function(e,t){let{req:a,handleResponseBody:r,reject:n,clearTimeout:o}=t,i="",s=!1,l=e=>{try{let t=JSON.parse(e);if(v(t))return eu(t.event),!1;if(g(t))return s=!0,o(),r(JSON.stringify(t.response)),!0;throw Error("Missing daemon progress response envelope")}catch(t){return s=!0,o(),n(new u("COMMAND_FAILED","Invalid daemon response",{requestId:a.meta?.requestId,line:e},t instanceof Error?t:void 0)),!0}};e.setEncoding("utf8"),e.on("data",e=>{if(s)return;let t=h(i,e);for(let e of(i=t.buffer,t.lines))if(e&&l(e))return}),e.on("end",()=>{if(s)return;let e=i.trim();e&&l(e)||(s=!0,o(),n(new u("COMMAND_FAILED","Invalid daemon response",{requestId:a.meta?.requestId,line:e})))}),e.on("error",e=>{s||(s=!0,o(),n(new u("COMMAND_FAILED","Failed to read daemon response",{requestId:a.meta?.requestId},e instanceof Error?e:void 0)))})}(t,{req:r,reject:i,clearTimeout:()=>{f&&clearTimeout(f)},handleResponseBody:t=>eJ(t,{info:e,req:r,resolve:o,reject:i})}):X(t).then(t=>{f&&clearTimeout(f),eJ(t,{info:e,req:r,resolve:o,reject:i})}).catch(e=>{f&&clearTimeout(f),i(new u("COMMAND_FAILED","Failed to read daemon response",{requestId:r.meta?.requestId},e instanceof Error?e:void 0))})}),m=eY(e),f="number"==typeof n?setTimeout(()=>{l.destroy(),i(eH(e,s,r.meta?.requestId,r.command,m,n))},n):void 0;l.on("error",e=>{f&&clearTimeout(f),i(eG(e,r.meta?.requestId,m))}),l.write(d),l.end()})}function eJ(e,t){let{info:a,req:r,resolve:n,reject:o}=t;try{var i,s,l;let t=(i=e,JSON.parse(i));if(t.error){let e;return void o((s=t.error,l=r.meta?.requestId,e=s.data??{},new u(d(null!=e.code?String(e.code):void 0,"COMMAND_FAILED"),String(e.message??s.message??"Daemon RPC request failed"),{..."object"==typeof e.details&&e.details?e.details:{},hint:"string"==typeof e.hint?e.hint:void 0,diagnosticId:"string"==typeof e.diagnosticId?e.diagnosticId:void 0,logPath:"string"==typeof e.logPath?e.logPath:void 0,requestId:l})))}if(!t.result||"object"!=typeof t.result)return void o(new u("COMMAND_FAILED","Invalid daemon RPC response",{requestId:r.meta?.requestId}));eW(a,r,t.result,n,o)}catch(t){o(new u("COMMAND_FAILED","Invalid daemon response",{requestId:r.meta?.requestId,line:e},t instanceof Error?t:void 0))}}async function eW(e,t,a,r,n){try{r(e.baseUrl&&a.ok?await eX(e,t,a):a)}catch(e){n(e)}}function eY(e){return"string"==typeof e.baseUrl&&e.baseUrl.length>0}function eQ(e,t){return new URL(t,e.endsWith("/")?e:`${e}/`).toString()}async function eX(e,t,a){let r=Array.isArray(a.data?.artifacts)?a.data.artifacts:[];if(0===r.length||!e.baseUrl)return a;let n=a.data?{...a.data}:{},i=[];for(let a of r){if(!a||"object"!=typeof a||"string"!=typeof a.artifactId){i.push(a);continue}let r=function(e,t){if(e.localPath&&e.localPath.trim().length>0)return e.localPath;let a=t.meta?.clientArtifactPaths?.[e.field];if(a&&a.trim().length>0)return a;let r=e.fileName?.trim()||`${e.field}-${Date.now()}`;return o.resolve(t.meta?.cwd??process.cwd(),r)}(a,t);await eZ({baseUrl:e.baseUrl,token:e.token,artifactId:a.artifactId,destinationPath:r,requestId:t.meta?.requestId}),n[a.field]=r,i.push({...a,localPath:r})}return n.artifacts=i,{ok:!0,data:n}}async function eZ(e){var n,s;let l,c=new URL((n=e.baseUrl,s=e.artifactId,l=n.endsWith("/")?n:`${n}/`,new URL(`artifacts/${encodeURIComponent(s)}`,l).toString())),d="https:"===c.protocol?a:t;await r.promises.mkdir(o.dirname(e.destinationPath),{recursive:!0}),await new Promise((t,a)=>{let n=!1,o=e.timeoutMs??9e4,s=o=>{if(!n){if(n=!0,clearTimeout(p),o)return void r.promises.rm(e.destinationPath,{force:!0}).finally(()=>a(o));t()}},l=d.request({protocol:c.protocol,host:c.hostname,port:c.port,method:"GET",path:c.pathname+c.search,headers:e.token?{authorization:`Bearer ${e.token}`,"x-agent-device-token":e.token}:void 0},t=>{if((t.statusCode??500)>=400){let a="";t.setEncoding("utf8"),t.on("data",e=>{a+=e}),t.on("end",()=>{s(new u("COMMAND_FAILED","Failed to download remote artifact",{artifactId:e.artifactId,statusCode:t.statusCode,requestId:e.requestId,body:a}))});return}t.on("aborted",()=>{s(new u("COMMAND_FAILED","Remote artifact download was interrupted",{artifactId:e.artifactId,requestId:e.requestId}))}),i(t,r.createWriteStream(e.destinationPath)).then(()=>s(),e=>s(e instanceof Error?e:Error(String(e))))}),p=setTimeout(()=>{let t=new u("COMMAND_FAILED","Remote artifact download timed out",{artifactId:e.artifactId,requestId:e.requestId,timeoutMs:o});s(t),l.destroy(t)},o);l.on("error",t=>{t instanceof u?s(t):s(new u("COMMAND_FAILED","Failed to download remote artifact",{artifactId:e.artifactId,requestId:e.requestId,timeoutMs:o},t instanceof Error?t:void 0))}),l.end()})}function e0(e={},t={}){let a=t.transport??ef,r=async(t,r=[],n={})=>{var o,i;let s=(o=e,i=n,{...o,...i}),l=await a({session:O(s.session),command:t,positionals:r,flags:$(s),runtime:s.runtime,meta:J(s)});return l.ok||function(e){throw new u(d(e.code),e.message,{...e.details??{},hint:e.hint,diagnosticId:e.diagnosticId,logPath:e.logPath})}(l.error),l.data??{}},n=async(e={})=>{let t=await r(N.sessionList,[],e);return(Array.isArray(t.sessions)?t.sessions:[]).map(x)},o=async(e,t={})=>{let a=j(e,t);return await r(a.command,a.positionals,a.options)},i=(t={})=>{var a,r;return O((a=e,r=t,{...a,...r}).session)};return{command:{wait:async e=>await o("wait",e),alert:async(e={})=>await o("alert",e),appState:async(e={})=>await o("appstate",e),back:async(e={})=>await o("back",e),home:async(e={})=>await o("home",e),rotate:async e=>await o("rotate",e),appSwitcher:async(e={})=>await o("app-switcher",e),keyboard:async(e={})=>await o("keyboard",e),clipboard:async e=>await o("clipboard",e),reactNative:async e=>await o("react-native",e),prepare:async e=>await o("prepare",e)},devices:{list:async(e={})=>{let t=await o("devices",e);return(Array.isArray(t.devices)?t.devices:[]).map(W)},boot:async(e={})=>await o("boot",e)},sessions:{list:async(e={})=>await n(e),close:async(e={})=>{let t=i(e),a=(await o("close",e)).shutdown;return{session:t,shutdown:"object"==typeof a&&null!==a?a:void 0,identifiers:{session:t}}}},apps:{install:async e=>G(await o("install",e),i(e)),reinstall:async e=>G(await o("reinstall",e),i(e)),installFromSource:async e=>B(await o("install-from-source",e),i(e)),list:async(e={})=>{let t=await o("apps",e);return Array.isArray(t.apps)?t.apps.filter(e=>"string"==typeof e):[]},open:async e=>{let t=i(e),a=await o("open",e),r=z(a),n=Q(a,"appBundleId");return{session:t,sessionStateDir:Q(a,"sessionStateDir"),appName:Q(a,"appName"),appBundleId:n,appId:n,startup:F(a.startup),runtime:H(a.runtime),device:r,identifiers:{session:t,deviceId:r?.id,deviceName:r?.name,udid:r?.ios?.udid,serial:r?.android?.serial,appId:n,appBundleId:n}}},close:async(e={})=>{let t=i(e),a=(await o("close",e)).shutdown;return{session:t,closedApp:e.app,shutdown:"object"==typeof a&&null!==a?a:void 0,identifiers:{session:t}}},push:async e=>await o("push",e),triggerEvent:async e=>await o("trigger-app-event",e)},materializations:{release:async e=>q(await r(N.releaseMaterializedPaths,[],{...e,materializationId:e.materializationId}))},leases:{allocate:async e=>e2(await r(N.leaseAllocate,[],{...e,leaseId:void 0,leaseTtlMs:e.ttlMs})),heartbeat:async e=>e2(await r(N.leaseHeartbeat,[],{...e,leaseTtlMs:e.ttlMs})),release:async e=>({released:!0===(await r(N.leaseRelease,[],e)).released})},metro:{prepare:async t=>await R({projectRoot:t.projectRoot??e.cwd,kind:t.kind,publicBaseUrl:t.publicBaseUrl,proxyBaseUrl:t.proxyBaseUrl,proxyBearerToken:t.bearerToken,bridgeScope:t.bridgeScope,launchUrl:t.launchUrl,companionProfileKey:t.companionProfileKey,companionConsumerKey:t.companionConsumerKey,metroPort:t.port,listenHost:t.listenHost,statusHost:t.statusHost,startupTimeoutMs:t.startupTimeoutMs,probeTimeoutMs:t.probeTimeoutMs,reuseExisting:t.reuseExisting,installDependenciesIfNeeded:t.installDependenciesIfNeeded,runtimeFilePath:t.runtimeFilePath,logPath:t.logPath}),reload:async(t={})=>await U({metroHost:t.metroHost,metroPort:t.metroPort,bundleUrl:t.bundleUrl,runtime:e.runtime,timeoutMs:t.timeoutMs})},capture:{snapshot:async(e={})=>{var t,a,r;let n,s,l,c,d,u=i(e);return t=await o("snapshot",e),a=u,n=Q(t,"appBundleId"),{nodes:K(t.nodes),truncated:!0===t.truncated,appName:Q(t,"appName"),appBundleId:n,...(s=e1((r=t).visibility),l=e1(r.androidSnapshot),c=e1(r.unchanged),d=Array.isArray(r.warnings)?r.warnings.filter(e=>"string"==typeof e):void 0,{...s?{visibility:s}:{},...l?{androidSnapshot:l}:{},...c?{unchanged:c}:{},...d?{warnings:d}:{}}),identifiers:{session:a,appId:n,appBundleId:n}}},screenshot:async(e={})=>{let t=i(e),a=await o("screenshot",e);return{path:Y(a,"path"),overlayRefs:V(a),identifiers:{session:t}}},diff:async e=>await o("diff",e)},interactions:{click:async e=>await o("click",e),press:async e=>await o("press",e),longPress:async e=>await o("longpress",e),swipe:async e=>await o("swipe",e),pan:async e=>await o("gesture-pan",e),fling:async e=>await o("gesture-fling",e),swipeGesture:async e=>await o("gesture-swipe",e),focus:async e=>await o("focus",e),type:async e=>await o("type",e),fill:async e=>await o("fill",e),scroll:async e=>await o("scroll",e),pinch:async e=>await o("gesture-pinch",e),rotateGesture:async e=>await o("gesture-rotate",e),transformGesture:async e=>await o("gesture-transform",e),get:async e=>await o("get",e),is:async e=>await o("is",e),find:async e=>await o("find",e)},replay:{run:async e=>await o("replay",e),test:async e=>await o("test",e)},batch:{run:async e=>await o("batch",e)},observability:{perf:async(e={})=>await o("perf",e),logs:async(e={})=>await o("logs",e),network:async(e={})=>await o("network",e)},recording:{record:async e=>await o("record",e),trace:async e=>await o("trace",e)},settings:{update:async e=>await o("settings",e)}}}function e1(e){return"object"==typeof e&&null!==e?e:void 0}function e2(e){let t=e.lease;if(!t||"object"!=typeof t||Array.isArray(t))throw Error("Invalid lease response from daemon");return{leaseId:Y(t,"leaseId"),tenantId:Y(t,"tenantId"),runId:Y(t,"runId"),backend:Y(t,"backend"),createdAt:"number"==typeof t.createdAt?t.createdAt:void 0,heartbeatAt:"number"==typeof t.heartbeatAt?t.heartbeatAt:void 0,expiresAt:"number"==typeof t.expiresAt?t.expiresAt:void 0}}export{e0 as createAgentDeviceClient,ed as formatDurationSeconds,ef as sendToDaemon};
|
package/dist/src/apple.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AppError as e}from"./9152.js";import{
|
|
1
|
+
import{AppError as e}from"./9152.js";import{buildScrollGesturePlan as a}from"./9533.js";import{runMacOsScreenshotAction as t,runIosRunnerCommand as n}from"./2415.js";import{writeIosClipboardText as d,openIosApp as o,openIosDevice as r,screenshotIos as i,closeIosApp as p,setIosSetting as l,readIosClipboardText as u}from"./apps.js";import{withDiagnosticTimer as c}from"./7599.js";function s(e,a,t,n){return{command:"tap",x:t,y:n,..."ios"===e.platform&&"tv"!==e.target?{synthesized:!0}:{},appBundleId:a.appBundleId}}function m(e,a,t,n,d,o,r,i){var p,l;let u="ios"===e.platform&&"tv"!==e.target?(p=r,l=i.defaultDurationMs,void 0===p?l:Math.min(1e4,Math.max(16,Math.round(p)))):r??i.legacyDefaultDurationMs;return{command:"drag",x:t,y:n,x2:d,y2:o,...void 0!==u?{durationMs:u}:{},appBundleId:a.appBundleId}}function y(e,a,t){return{command:"remotePress",remoteButton:e,...void 0!==t?{durationMs:t}:{},...void 0!==a?{appBundleId:a}:{}}}async function f(e,t,n,d,o,r,i){if("tv"===t.target)return w(await e(t,y(o,n.appBundleId),d),r);let p=i??await I(e,t,n,d),l=a({direction:o,amount:r?.amount,pixels:r?.pixels,referenceWidth:p.referenceWidth,referenceHeight:p.referenceHeight});return w(await e(t,m(t,n,p.originX+l.x1,p.originY+l.y1,p.originX+l.x2,p.originY+l.y2,void 0,{defaultDurationMs:250}),d),{amount:l.amount,pixels:l.pixels,preferProvidedPixels:!0})}async function I(a,t,n,d){let o=await a(t,{command:"interactionFrame",appBundleId:n.appBundleId},d),r=v(o.x),i=v(o.y),p=v(o.referenceWidth),l=v(o.referenceHeight);if(void 0===r||void 0===i||void 0===p||void 0===l)throw new e("COMMAND_FAILED","interactionFrame did not return a usable frame");return{originX:r,originY:i,referenceWidth:p,referenceHeight:l}}function v(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function w(e,a){var t;let{x1:n,y1:d,x2:o,y2:r}={x1:v((t=e).x),y1:v(t.y),x2:v(t.x2),y2:v(t.y2)},i=v(e.referenceWidth),p=v(e.referenceHeight),l=void 0!==n&&void 0!==o?Math.round(Math.abs(o-n)):void 0,u=void 0!==d&&void 0!==r?Math.round(Math.abs(r-d)):void 0,c=a?.preferProvidedPixels&&void 0!==a.pixels?a.pixels:l&&l>0?l:u&&u>0?u:void 0;return{...void 0!==n?{x1:n}:{},...void 0!==d?{y1:d}:{},...void 0!==o?{x2:o}:{},...void 0!==r?{y2:r}:{},...void 0!==i?{referenceWidth:i}:{},...void 0!==p?{referenceHeight:p}:{},...a?.amount!==void 0?{amount:a.amount}:{},...void 0!==c?{pixels:c}:{}}}function B(a,I){let v,{overrides:w,runnerOpts:B}={runnerOpts:v={verbose:I.verbose,logPath:I.logPath,traceLogPath:I.traceLogPath,requestId:I.requestId},overrides:{tap:async(e,t)=>await n(a,s(a,I,e,t),v),tapElementSelector:async e=>await n(a,{command:"tap",selectorKey:e.key,selectorValue:e.value,allowNonHittableCoordinateFallback:e.allowNonHittableCoordinateFallback,appBundleId:I.appBundleId},v),doubleTap:async(e,t)=>await n(a,{command:"tapSeries",x:e,y:t,count:1,intervalMs:0,doubleTap:!0,appBundleId:I.appBundleId},v),swipe:async(e,t,d,o,r)=>await n(a,m(a,I,e,t,d,o,r,{defaultDurationMs:250}),v),pan:async(e,t,d,o,r)=>await n(a,m(a,I,e,t,d,o,r,{defaultDurationMs:500,legacyDefaultDurationMs:500}),v),fling:async(e,t,d,o,r)=>await n(a,{command:"drag",x:e,y:t,x2:d,y2:o,durationMs:r??16,appBundleId:I.appBundleId},v),longPress:async(e,t,d)=>await n(a,{command:"longPress",x:e,y:t,durationMs:d,appBundleId:I.appBundleId},v),focus:async(e,t)=>await n(a,s(a,I,e,t),v),type:async(e,t)=>{await n(a,{command:"type",text:e,delayMs:t,textEntryMode:"\n"===e?void 0:"append",appBundleId:I.appBundleId},v)},fillElementSelector:async(e,t,d)=>await n(a,{command:"type",selectorKey:e.key,selectorValue:e.value,allowNonHittableCoordinateFallback:e.allowNonHittableCoordinateFallback,text:t,delayMs:d,textEntryMode:"replace",appBundleId:I.appBundleId},v),fill:async(e,t,d,o)=>await n(a,{command:"type",x:e,y:t,text:d,delayMs:o,textEntryMode:"replace",appBundleId:I.appBundleId},v),scroll:async(e,t)=>await f(n,a,I,v,e,t),pinch:async(e,t,d)=>{await n(a,{command:"pinch",scale:e,x:t,y:d,appBundleId:I.appBundleId},v)},rotateGesture:async(e,t,d,o)=>{await n(a,{command:"rotateGesture",degrees:e,x:t,y:d,velocity:o,appBundleId:I.appBundleId},v)},transformGesture:async e=>await n(a,{command:"transformGesture",x:e.x,y:e.y,dx:e.dx,dy:e.dy,scale:e.scale,degrees:e.degrees,durationMs:e.durationMs,appBundleId:I.appBundleId},v)}};return{open:(e,t)=>o(a,e,{appBundleId:t?.appBundleId,launchConsole:t?.launchConsole,launchArgs:t?.launchArgs,url:t?.url}),openDevice:()=>r(a),close:e=>p(a,e),screenshot:async(e,n)=>{"macos"===a.platform&&n?.surface&&"app"!==n.surface?await t(e,{surface:n.surface,fullscreen:n.fullscreen}):await i(a,e,n?.appBundleId,n?.fullscreen,B)},snapshot:async t=>{var d;let o={nodes:Array.isArray((d=await c("snapshot_capture",async()=>await n(a,{command:"snapshot",appBundleId:t?.appBundleId,interactiveOnly:t?.interactiveOnly,compact:t?.compact,depth:t?.depth,scope:t?.scope,raw:t?.raw},B),{backend:"xctest"})).nodes)?d.nodes:void 0,truncated:"boolean"==typeof d.truncated?d.truncated:void 0},r=o.nodes??[];if(0===r.length&&"simulator"===a.kind)throw new e("COMMAND_FAILED","XCTest snapshot returned 0 nodes on iOS simulator.");return{nodes:r,truncated:o.truncated??!1,backend:"xctest"}},back:async e=>{"tv"===a.target?await n(a,y("menu",I.appBundleId),B):await n(a,{command:"system"===e?"backSystem":"backInApp",appBundleId:I.appBundleId},B)},home:async()=>{"tv"===a.target?await n(a,y("home",I.appBundleId),B):await n(a,{command:"home",appBundleId:I.appBundleId},B)},rotate:async e=>{await n(a,{command:"rotate",orientation:e,appBundleId:I.appBundleId},B)},appSwitcher:async()=>{await n(a,{command:"appSwitcher",appBundleId:I.appBundleId},B)},readClipboard:()=>u(a),writeClipboard:e=>d(a,e),setSetting:(e,t,n,d)=>l(a,e,t,n,d),...w}}export{B as createAppleInteractor};
|
package/dist/src/apps.js
CHANGED
|
@@ -3,7 +3,7 @@ ${i}
|
|
|
3
3
|
${a}`.toLowerCase();return r.includes("unknown option '--device'")||r.includes("unknown subcommand")&&r.includes("screenshot")||r.includes("unrecognized subcommand")&&r.includes("screenshot")}(t))throw t;eP(e,"devicectl_screenshot",t)}await eL(e,t,i,a,r)}async function eN(e,t,i,a,r=eC,o){if("simulator"!==e.kind)throw new n("UNSUPPORTED_OPERATION","Simulator screenshot fallback flow supports only iOS simulators");let l="object"==typeof a&&null!==a?a:r;await l.ensureBooted(e);let s=async()=>{};try{s=await l.prepareStatusBarForScreenshot(e)}catch(t){ex(e,"prepare_failed",t)}try{try{await l.captureWithRetry(e,t);return}catch(t){if(!l.shouldFallbackToRunner(t))throw t;eP(e,"simctl_screenshot",t)}await l.captureWithRunner(e,t,i,"boolean"==typeof a?a:void 0,o)}finally{await s().catch(t=>ex(e,"restore_failed",t))}}async function eS(e,t){let i=G.fromTimeoutMs(2e4);await B(async({deadline:i})=>{var a;await (a=["io",e.id,"screenshot",t],p(e,a,{timeoutMs:Math.max(1e3,i?.remainingMs()??2e4)}))},{maxAttempts:5,baseDelayMs:1e3,maxDelayMs:5e3,jitter:.2,shouldRetry:e=>eF(e)},{deadline:i,phase:"ios_simulator_screenshot"})}async function eL(e,i,a,r,o){let l=(await u(e,{command:"screenshot",appBundleId:a,fullscreen:r},o)).message;if(!l)throw new n("COMMAND_FAILED","Failed to capture iOS screenshot: runner returned no file path");"macos"===e.platform?await t.copyFile(l,i):"simulator"===e.kind?await eM(e,l,i):await eD(e,l,i)}async function eD(e,t,i){let a=G.fromTimeoutMs(2e4),r={exitCode:1,stdout:"",stderr:""};for(let n of l)if(0===(r=await o(["devicectl","device","copy","from","--device",e.id,"--source",t,"--destination",i,"--domain-type","appDataContainer","--domain-identifier",n],{allowFailure:!0,timeoutMs:eO(a,2e4,"runner screenshot copy")})).exitCode)return;let s=r.stderr.trim()||r.stdout.trim()||`devicectl exited with code ${r.exitCode}`;throw new n("COMMAND_FAILED",`Failed to capture iOS screenshot: ${s}`)}async function eM(e,i,r){let o=G.fromTimeoutMs(2e4),s="Unable to locate runner container for simulator screenshot";for(let n of l){var d;let l=await (d=["get_app_container",e.id,n,"data"],p(e,d,{allowFailure:!0,timeoutMs:eO(o,2e4,"runner screenshot container lookup")}));if(0!==l.exitCode){let e=l.stderr.trim();e&&(s=e);continue}let u=l.stdout.trim();if(!u){s="simctl get_app_container returned empty output";continue}for(let e of function(e,t){let i=a.resolve(e),r=t.trim();if(!r)return[];let n=[],o=new Set,l=e=>{let t=a.normalize(e);o.has(t)||(o.add(t),n.push(t))},s=r.replace(/^\/+/,""),d=s.replace(/\\/g,"/");if(s&&l(a.join(i,s)),a.isAbsolute(r)&&l(a.normalize(r)),d.startsWith("tmp/"))l(a.join(i,d));else{let e=d.lastIndexOf("/tmp/");if(e>=0){let t=d.slice(e+1);l(a.join(i,t))}}let u=a.basename(r);return u&&l(a.join(i,"tmp",u)),n}(u,i))try{await t.copyFile(e,r);return}catch(e){s=e instanceof Error?e.message:String(e)}}throw new n("COMMAND_FAILED",`Failed to capture iOS screenshot: ${s}`)}function eO(e,t,i){let a=e.remainingMs();if(a>0)return a;throw new n("COMMAND_FAILED",`iOS ${i} timed out after ${t}ms`,{timeoutMs:t,step:i})}function eP(e,t,i){let a=ep(i);V({level:"warn",phase:"ios_screenshot_fallback",data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,from:t,to:"runner",...a}})}function ex(e,t,i){V({level:"warn",phase:`ios_screenshot_status_bar_${t}`,data:{platform:e.platform,deviceKind:e.kind,deviceId:e.id,...ep(i)}})}function eF(e){if(!(e instanceof n)||"COMMAND_FAILED"!==e.code)return!1;let t=e.details??{},i="string"==typeof t.stdout?t.stdout:"",a="string"==typeof t.stderr?t.stderr:"",r=Array.isArray(t.args)?t.args.filter(e=>"string"==typeof e).join(" "):"",o=`${e.message}
|
|
4
4
|
${i}
|
|
5
5
|
${a}
|
|
6
|
-
${r}`.toLowerCase();return o.includes("timeout waiting for screen surfaces")||o.includes("nsposixerrordomain")&&o.includes("code=60")&&o.includes("screenshot")||o.includes("timed out")&&o.includes("screenshot")}let eR={settings:"com.apple.Preferences"},e$=E(),eE=null;function ek(e){return{platform:"ios",deviceId:e.id,variant:e.kind}}function eT(e,t,i){return o(m(e,t),i)}function eU(e){return e.includes("not installed")||e.includes("not found")||e.includes("no such file")}async function eB(e,t){if("macos"===e.platform)return await ea(t);let i=t.trim();if(i.includes("."))return i;let a=eR[i.toLowerCase()];if(a)return a;let r=ek(e),o=e$.get(r,i);if(o)return o;let l=("simulator"===e.kind?await e2(e):await v(e,"all")).filter(e=>e.name.toLowerCase()===i.toLowerCase()),s=l[0];if(void 0!==s&&1===l.length)return e$.set(r,i,s.bundleId);if(l.length>1)throw new n("INVALID_ARGS",`Multiple apps matched "${t}"`,{matches:l});throw new n("APP_NOT_INSTALLED",`No app found matching "${t}"`)}async function eG(e,t){var i;let r;if("ios"!==e.platform||"simulator"!==e.kind)return;let n=(i=t,r=/^([A-Za-z][A-Za-z0-9+.-]*):/.exec(i.trim()),r?.[1]?.toLowerCase());if(!n)return;let o=await e3(e),l=[];for(let e of o)!e.bundleId.startsWith("com.callstack.agentdevice.runner")&&e.path&&(await e5(a.join(e.path,"Info.plist"))).has(n)&&l.push(e);let s=l.filter(e=>"User"===e.applicationType);return 1===s.length?s[0]?.bundleId:s.length>1?void 0:1===l.length?l[0]?.bundleId:void 0}async function eV(e,t,i){let a=i?.launchConsole?.trim(),r=i?.launchArgs;if(a&&("ios"!==e.platform||"simulator"!==e.kind))throw new n("UNSUPPORTED_OPERATION",S);if("macos"===e.platform){if(r&&r.length>0)throw new n("UNSUPPORTED_OPERATION","--launch-args is not supported on macOS; launch arguments are currently iOS-only.");await er(e,t,i);return}let o=i?.url?.trim();if(o){if(a)throw new n("INVALID_ARGS",d);if(!k(o))throw new n("INVALID_ARGS","open <app> <url> requires a valid URL target");if("simulator"===e.kind)
|
|
6
|
+
${r}`.toLowerCase();return o.includes("timeout waiting for screen surfaces")||o.includes("nsposixerrordomain")&&o.includes("code=60")&&o.includes("screenshot")||o.includes("timed out")&&o.includes("screenshot")}let eR={settings:"com.apple.Preferences"},e$=E(),eE=null;function ek(e){return{platform:"ios",deviceId:e.id,variant:e.kind}}function eT(e,t,i){return o(m(e,t),i)}function eU(e){return e.includes("not installed")||e.includes("not found")||e.includes("no such file")}async function eB(e,t){if("macos"===e.platform)return await ea(t);let i=t.trim();if(i.includes("."))return i;let a=eR[i.toLowerCase()];if(a)return a;let r=ek(e),o=e$.get(r,i);if(o)return o;let l=("simulator"===e.kind?await e2(e):await v(e,"all")).filter(e=>e.name.toLowerCase()===i.toLowerCase()),s=l[0];if(void 0!==s&&1===l.length)return e$.set(r,i,s.bundleId);if(l.length>1)throw new n("INVALID_ARGS",`Multiple apps matched "${t}"`,{matches:l});throw new n("APP_NOT_INSTALLED",`No app found matching "${t}"`)}async function eG(e,t){var i;let r;if("ios"!==e.platform||"simulator"!==e.kind)return;let n=(i=t,r=/^([A-Za-z][A-Za-z0-9+.-]*):/.exec(i.trim()),r?.[1]?.toLowerCase());if(!n)return;let o=await e3(e),l=[];for(let e of o)!e.bundleId.startsWith("com.callstack.agentdevice.runner")&&e.path&&(await e5(a.join(e.path,"Info.plist"))).has(n)&&l.push(e);let s=l.filter(e=>"User"===e.applicationType);return 1===s.length?s[0]?.bundleId:s.length>1?void 0:1===l.length?l[0]?.bundleId:void 0}async function eV(e,t,i){let a=i?.launchConsole?.trim(),r=i?.launchArgs;if(a&&("ios"!==e.platform||"simulator"!==e.kind))throw new n("UNSUPPORTED_OPERATION",S);if("macos"===e.platform){if(r&&r.length>0)throw new n("UNSUPPORTED_OPERATION","--launch-args is not supported on macOS; launch arguments are currently iOS-only.");await er(e,t,i);return}let o=i?.url?.trim();if(o){if(a)throw new n("INVALID_ARGS",d);if(!k(o))throw new n("INVALID_ARGS","open <app> <url> requires a valid URL target");if("simulator"===e.kind){let a=i?.appBundleId??await eB(e,t);await tt(e,a,{...r?{launchArgs:r}:{}}),await ej(e,o,void 0);return}let l=U(i?.appBundleId??await eB(e,t),o);if(!l)throw new n("INVALID_ARGS","Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.");await tr(e,l,{payloadUrl:o,launchArgs:r});return}let l=t.trim();if(k(l)){if(a)throw new n("INVALID_ARGS",d);if("simulator"===e.kind)return void await ej(e,l,r);let t=U(i?.appBundleId,l);if(!t)throw new n("INVALID_ARGS","Deep link open on iOS devices requires an active app bundle ID. Open the app first, then open the URL.");return void await tr(e,t,{payloadUrl:l,launchArgs:r})}let s=i?.appBundleId??await eB(e,t);"simulator"===e.kind?await tt(e,s,{...a?{launchConsole:a}:{},...r?{launchArgs:r}:{}}):await tr(e,s,{launchArgs:r})}async function ej(e,t,i){if(i&&i.length>0)throw new n("INVALID_ARGS","--launch-args is not supported with iOS simulator URL opens (simctl openurl ignores launch args). Launch the app first with --launch-args, then issue the URL open in a separate call.");await b(e),await eT(e,["openurl",e.id,t])}async function eW(e){"macos"===e.platform||"simulator"!==e.kind||"Booted"!==await L(e)&&await b(e)}async function eK(e,t){if("macos"===e.platform)return void await en(e,t);let i=await eB(e,t);if("simulator"===e.kind){await b(e);let t=m(e,["terminate",e.id,i]),a=await o(t,{allowFailure:!0,timeoutMs:15e3});if(0!==a.exitCode){if(a.stderr.toLowerCase().includes("found nothing to terminate"))return;throw new n("COMMAND_FAILED",`xcrun exited with code ${a.exitCode}`,{cmd:"xcrun",args:t,stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode})}return}await A(["device","process","terminate","--device",e.id,i],{action:"terminate iOS app",deviceId:e.id})}async function eH(e,i){if("ios"!==e.platform||"simulator"!==e.kind)throw new n("UNSUPPORTED_OPERATION","Clearing app state is currently supported only on iOS simulators.");let r=await eB(e,i);await b(e),await eK(e,r);let o=await eT(e,["get_app_container",e.id,r,"data"],{allowFailure:!0});if(0!==o.exitCode)throw new n("COMMAND_FAILED",`simctl get_app_container failed for ${r}`,{stdout:o.stdout,stderr:o.stderr,exitCode:o.exitCode});let l=o.stdout.trim();if(!l)throw new n("COMMAND_FAILED",`simctl get_app_container returned an empty data container path for ${r}`);let s=await t.readdir(l);return await Promise.all(s.map(e=>t.rm(a.join(l,e),{recursive:!0,force:!0}))),{bundleId:r,containerPath:l}}async function ez(e,t){return await e$.invalidateWhile(ek(e),async()=>{let i=await eB(e,t);if("simulator"!==e.kind){let t=["devicectl","device","uninstall","app","--device",e.id,i],a=await o(t,{allowFailure:!0,timeoutMs:2e4});if(0!==a.exitCode){let r=String(a.stdout??""),o=String(a.stderr??"");if(!eU(`${r}
|
|
7
7
|
${o}`.toLowerCase()))throw new n("COMMAND_FAILED",`Failed to uninstall iOS app ${i}`,{cmd:"xcrun",args:t,exitCode:a.exitCode,stdout:r,stderr:o,deviceId:e.id,hint:s(r,o)??f})}return{bundleId:i}}await b(e);let a=await eT(e,["uninstall",e.id,i],{allowFailure:!0});if(0!==a.exitCode&&!eU(`${a.stdout}
|
|
8
8
|
${a.stderr}`.toLowerCase()))throw new n("COMMAND_FAILED",`simctl uninstall failed for ${i}`,{stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode});return{bundleId:i}})}async function eq(e,t,i){let a=await q({kind:"path",path:t},i);try{return await eZ(e,a.installablePath),{archivePath:a.archivePath,installablePath:a.installablePath,bundleId:a.bundleId,appName:a.appName,launchTarget:a.bundleId}}finally{await a.cleanup()}}async function eJ(e,t,i){return await e$.invalidateWhile(ek(e),async()=>{let{bundleId:a}=await ez(e,t);return await eq(e,i,{appIdentifierHint:t}),{bundleId:a}})}async function eZ(e,t){await e$.invalidateWhile(ek(e),async()=>{"simulator"!==e.kind?await A(["device","install","app","--device",e.id,t],{action:"install iOS app",deviceId:e.id}):(await b(e),await eT(e,["install",e.id,t]))})}async function eQ(e){if("macos"===e.platform)return await eo();w(e,"clipboard"),await b(e);let t=await eT(e,["pbpaste",e.id],{allowFailure:!0});if(0!==t.exitCode)throw new n("COMMAND_FAILED","Failed to read iOS simulator clipboard",{stdout:t.stdout,stderr:t.stderr,exitCode:t.exitCode});return t.stdout.replace(/\r\n/g,"\n").replace(/\n$/,"")}async function eX(e,t){if("macos"===e.platform)return void await el(t);w(e,"clipboard"),await b(e);let i=await eT(e,["pbcopy",e.id],{allowFailure:!0,stdin:t});if(0!==i.exitCode)throw new n("COMMAND_FAILED","Failed to write iOS simulator clipboard",{stdout:i.stdout,stderr:i.stderr,exitCode:i.exitCode})}async function eY(e,r,n){w(e,"push"),await b(e);let o=await t.mkdtemp(a.join(i.tmpdir(),"agent-device-ios-push-")),l=a.join(o,"payload.apns");try{await t.writeFile(l,`${JSON.stringify(n)}
|
|
9
9
|
`,"utf8"),await eT(e,["push",e.id,r,l])}finally{await t.rm(o,{recursive:!0,force:!0})}}async function e0(e,t,i,a,r){if("macos"===e.platform){let e=t.toLowerCase();if("appearance"===e)return void await ed(i);if("permission"===e){let e=$(i);if("deny"===e)throw new n("INVALID_ARGS",W("permission"));let t=function(e){let t=e?.trim().toLowerCase();if("accessibility"===t||"screen-recording"===t||"input-monitoring"===t)return t;throw new n("INVALID_ARGS","Unsupported macOS permission target. Use accessibility|screen-recording|input-monitoring.")}(r?.permissionTarget);return await h(e,t)}throw new n("INVALID_ARGS",W(t))}w(e,"settings"),await b(e);let o=t.toLowerCase();switch(o){case"clear-app-state":{if("clear"!==i.toLowerCase())throw new n("INVALID_ARGS","settings clear-app-state only supports clear.");if(!a)throw new n("INVALID_ARGS","settings clear-app-state requires an app id or an active app session.");let t=await eH(e,a);return{bundleId:t.bundleId,containerPath:t.containerPath,cleared:!0}}case"wifi":{let t=F(i);await eT(e,["status_bar",e.id,"override","--wifiMode",t?"active":"failed"]);return}case"airplane":return void(F(i)?await eT(e,["status_bar",e.id,"override","--dataNetwork","hide","--wifiMode","failed","--wifiBars","0","--cellularMode","failed","--cellularBars","0","--operatorName",""]):await eT(e,["status_bar",e.id,"clear"]));case"location":{if("set"===i.toLowerCase()){let{latitude:t,longitude:i}=j(r);return await eT(e,["location",e.id,"set",`${t},${i}`]),{latitude:t,longitude:i}}let t=F(i);if(!a)throw new n("INVALID_ARGS","location setting requires an active app in session");await eT(e,["privacy",e.id,t?"grant":"revoke","location",a]);return}case"faceid":case"touchid":{let t=e9[o],a=function(e,t){let i=e.trim().toLowerCase();if("match"===i)return"match";if("nonmatch"===i)return"nonmatch";if("enroll"===i)return"enroll";if("unenroll"===i)return"unenroll";throw new n("INVALID_ARGS",`Invalid ${t} state: ${e}. Use match|nonmatch|enroll|unenroll.`)}(i,o);await te(e,a,{settingName:o,label:t.label,modalityAliases:t.modalityAliases});return}case"appearance":{let t=await e4(e,i);await eT(e,["ui",e.id,"appearance",t]);return}case"permission":{var l;if(!a)throw new n("INVALID_ARGS","permission setting requires an active app in session");let t="deny"===(l=$(i))?"revoke":l,o=function(e,t){let i=R(e);if("photos"!==i&&t?.trim())throw new n("INVALID_ARGS",`Permission mode is only supported for photos. Received: ${t}.`);if("camera"===i)return"camera";if("microphone"===i)return"microphone";if("contacts"===i)return"contacts";if("contacts-limited"===i)return"contacts-limited";if("notifications"===i)return"notifications";if("calendar"===i)return"calendar";if("location"===i)return"location";if("location-always"===i)return"location-always";if("media-library"===i)return"media-library";if("motion"===i)return"motion";if("reminders"===i)return"reminders";if("siri"===i)return"siri";if("photos"===i){let e=t?.trim().toLowerCase();if(!e||"full"===e)return"photos";if("limited"===e)return"photos-add";throw new n("INVALID_ARGS",`Invalid photos mode: ${t}. Use full|limited.`)}throw new n("INVALID_ARGS",`Unsupported permission target: ${e}. Use camera|microphone|photos|contacts|contacts-limited|notifications|calendar|location|location-always|media-library|motion|reminders|siri.`)}(r?.permissionTarget,r?.permissionMode);await e8(e,t,o,a);return}default:throw new n("INVALID_ARGS",`Unsupported setting: ${t}`)}}async function e1(e,t){return"macos"===e.platform?await eu(t):"simulator"===e.kind?I(await e2(e),t):await v(e,t)}async function e2(e){return(await e3(e)).map(e=>({bundleId:e.bundleId,name:e.name}))}async function e3(e){let t=(await eT(e,["listapps",e.id],{allowFailure:!0})).stdout.trim();if(!t)return[];let i=null;if(t.startsWith("{"))try{i=JSON.parse(t)}catch{i=null}if(!i&&t.startsWith("{"))try{let e=await g("plutil",["-convert","json","-o","-","-"],{allowFailure:!0,stdin:t});0===e.exitCode&&e.stdout.trim().startsWith("{")&&(i=JSON.parse(e.stdout))}catch{i=null}return i?Object.entries(i).map(([e,t])=>{let i=function(e){if(e.Path)return e.Path;if(e.Bundle)try{return r(e.Bundle)}catch{return}}(t);return{bundleId:e,name:t.CFBundleDisplayName??t.CFBundleName??e,...i?{path:i}:{},...t.ApplicationType?{applicationType:t.ApplicationType}:{}}}):[]}async function e5(e){let t=await g("plutil",["-convert","json","-o","-",e],{allowFailure:!0});if(0!==t.exitCode)return new Set;try{let e=JSON.parse(t.stdout),i=new Set;for(let t of e.CFBundleURLTypes??[])if(Array.isArray(t.CFBundleURLSchemes))for(let e of t.CFBundleURLSchemes)"string"==typeof e&&e.trim()&&i.add(e.trim().toLowerCase());return i}catch{return new Set}}async function e4(e,t){let i=P(t);if("toggle"!==i)return i;let a=await eT(e,["ui",e.id,"appearance"],{allowFailure:!0});if(0!==a.exitCode)throw new n("COMMAND_FAILED","Failed to read current iOS appearance",{stdout:a.stdout,stderr:a.stderr,exitCode:a.exitCode});let r=function(e,t){let i=/\b(light|dark|unsupported|unknown)\b/i.exec(`${e}
|
package/dist/src/args.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{listCliCommandNames as e}from"./5792.js";import{getCliCommandSchema as t,getCommandSchema as o,applyCommandDefaults as a,GLOBAL_FLAG_KEYS as s,getFlagDefinition as n,getFlagDefinitions as r}from"./1352.js";import{parseSourceValue as i,buildPrimaryEnvVarName as l}from"./1010.js";import{AppError as c}from"./9152.js";function d(e,t){for(let[o,a]of Object.entries(t))void 0!==a&&(e[o]=a);return e}let p=[{label:"help workflow",description:"Normal bootstrap, exploration, and validation loop"},{label:"help debugging",description:"Logs, network, alerts, diagnostics, and traces"},{label:"help react-native",description:"React Native app automation hazards, overlays, Metro, and routing"},{label:"help react-devtools",description:"React Native performance, profiling, component tree, and renders"},{label:"help physical-device",description:"Connected phone/tablet setup and iOS signing prerequisites"},{label:"help remote",description:"Remote/cloud config, tenants, leases, and local service tunnels"},{label:"help macos",description:"Desktop, frontmost-app, and menu bar surfaces"},{label:"help dogfood",description:"Exploratory QA report workflow"}],u=["Default loop: devices/apps -> open -> snapshot -i -> press/fill/get/is/wait/find -> verify -> close.",'Use selectors or refs as positional targets: id="submit", label="Allow", or @e12 from snapshot -i.',"Plain snapshot reads state; snapshot -i refreshes current interactive refs only.","Default snapshot text is an agent-facing, token-efficient view for planning and targeting actions.","Read-only visible/state question: use snapshot/get/is/find; use snapshot -i only when refs are needed.","Anti-pattern: snapshot -i followed by snapshot -i | grep ...; prior refs stay valid until app state changes, and --force-full is the explicit full re-read.","Truncated text/input preview: expand first with snapshot -s @e12, not get text.","React Native apps: read help react-native for Metro, DevTools routing, and RN-specific blockers; use react-native dismiss-overlay for LogBox/RedBox overlays.","Android RN/Expo Metro: direct Android localhost URL opens with a port auto-configure host reachability.",'Expo Go/dev clients: use the provided URL when given; on iOS prefer open "Expo Go" <url>; Android URL opens infer the foreground package for logs/perf when possible.',"Install flows: install/install-from-source first, then open the installed id with --relaunch.",'Text: fill \'id="field-email"\' "qa@example.com" replaces; type appends after press.','Clearing text: do not use fill <target> ""; use a visible clear/reset control or report that clearing is unsupported.',"Android IME capture: if fill says input was captured by the keyboard/IME, inspect keyboard state and switch/disable handwriting before retrying; do not loop fill/type.","Implicit default sessions are scoped to the current worktree; use --session only when intentionally sharing a named session.","Run mutating commands serially within one session; parallelize only read-only commands or separate sessions/devices.","Clipboard limits: iOS Allow Paste cannot be automated through XCUITest; prefill with clipboard write. Android non-ASCII should use fill/type, not raw adb input.","After mutation: refs are stale. If the next target is known, use its selector directly; otherwise refresh with snapshot -i, scoped with -s when a stable container is known.","Raw coordinates are fallback-only: use snapshot -i -c --json rects when iOS refs no-op or child refs are missing.",'Batch JSON steps use "command" and structured "input"; legacy "positionals"/"flags" steps still run in CLI but are deprecated until the next major version.',"Navigation: app-owned back uses back; system back uses back --system.","Verification commands must name the expected text/selector; bare screenshots/snapshots are not enough.","Debug evidence: logs clear --restart/mark/path
|
|
1
|
+
import{listCliCommandNames as e}from"./5792.js";import{getCliCommandSchema as t,getCommandSchema as o,applyCommandDefaults as a,GLOBAL_FLAG_KEYS as s,getFlagDefinition as n,getFlagDefinitions as r}from"./1352.js";import{parseSourceValue as i,buildPrimaryEnvVarName as l}from"./1010.js";import{AppError as c}from"./9152.js";function d(e,t){for(let[o,a]of Object.entries(t))void 0!==a&&(e[o]=a);return e}let p=[{label:"help workflow",description:"Normal bootstrap, exploration, and validation loop"},{label:"help debugging",description:"Logs, network, alerts, diagnostics, and traces"},{label:"help react-native",description:"React Native app automation hazards, overlays, Metro, and routing"},{label:"help react-devtools",description:"React Native performance, profiling, component tree, and renders"},{label:"help physical-device",description:"Connected phone/tablet setup and iOS signing prerequisites"},{label:"help remote",description:"Remote/cloud config, tenants, leases, and local service tunnels"},{label:"help macos",description:"Desktop, frontmost-app, and menu bar surfaces"},{label:"help dogfood",description:"Exploratory QA report workflow"}],u=["Default loop: devices/apps -> open -> snapshot -i -> press/fill/get/is/wait/find -> verify -> close.",'Use selectors or refs as positional targets: id="submit", label="Allow", or @e12 from snapshot -i.',"Plain snapshot reads state; snapshot -i refreshes current interactive refs only.","Default snapshot text is an agent-facing, token-efficient view for planning and targeting actions.","Read-only visible/state question: use snapshot/get/is/find; use snapshot -i only when refs are needed.","Anti-pattern: snapshot -i followed by snapshot -i | grep ...; prior refs stay valid until app state changes, and --force-full is the explicit full re-read.","Truncated text/input preview: expand first with snapshot -s @e12, not get text.","React Native apps: read help react-native for Metro, DevTools routing, and RN-specific blockers; use react-native dismiss-overlay for LogBox/RedBox overlays.","Android RN/Expo Metro: direct Android localhost URL opens with a port auto-configure host reachability.",'Expo Go/dev clients: use the provided URL when given; on iOS prefer open "Expo Go" <url>; Android URL opens infer the foreground package for logs/perf when possible.',"Install flows: install/install-from-source first, then open the installed id with --relaunch.",'Text: fill \'id="field-email"\' "qa@example.com" replaces; type appends after press.','Clearing text: do not use fill <target> ""; use a visible clear/reset control or report that clearing is unsupported.',"Android IME capture: if fill says input was captured by the keyboard/IME, inspect keyboard state and switch/disable handwriting before retrying; do not loop fill/type.","Implicit default sessions are scoped to the current worktree; use --session only when intentionally sharing a named session.","Run mutating commands serially within one session; parallelize only read-only commands or separate sessions/devices.","Clipboard limits: iOS Allow Paste cannot be automated through XCUITest; prefill with clipboard write. Android non-ASCII should use fill/type, not raw adb input.","After mutation: refs are stale. If the next target is known, use its selector directly; otherwise refresh with snapshot -i, scoped with -s when a stable container is known.","Raw coordinates are fallback-only: use snapshot -i -c --json rects when iOS refs no-op or child refs are missing.",'Batch JSON steps use "command" and structured "input"; legacy "positionals"/"flags" steps still run in CLI but are deprecated until the next major version.',"Navigation: app-owned back uses back; system back uses back --system.","Verification commands must name the expected text/selector; bare screenshots/snapshots are not enough.","Debug evidence: Session state contains request diagnostics and runner.log; use logs clear --restart/mark/path, trace, and network dump --include headers for app evidence.","Use agent-device commands in final plans; raw platform tools, pseudo commands, and helper prose are wrong.","Full operating guide: agent-device help workflow. Exploratory QA: agent-device help dogfood."],f=["Default config files: ~/.agent-device/config.json, ./agent-device.json","Use --config <path> or AGENT_DEVICE_CONFIG to load one explicit config file."],h=[{label:"AGENT_DEVICE_SESSION",description:"Explicit session name"},{label:"AGENT_DEVICE_PLATFORM",description:"Default platform binding"},{label:"AGENT_DEVICE_SESSION_LOCK",description:"Bound-session conflict mode"},{label:"AGENT_DEVICE_DAEMON_BASE_URL",description:"Connect to remote daemon"},{label:"AGENT_DEVICE_DAEMON_AUTH_TOKEN",description:"Remote daemon service/API token"},{label:"AGENT_DEVICE_CLOUD_BASE_URL",description:"Bridge/control-plane API origin for cloud auth and /api-keys"}],g=["agent-device open Settings --platform ios","agent-device open TextEdit --platform macos","agent-device snapshot -i","agent-device react-devtools get tree --depth 3",'agent-device fill @e3 "test@example.com"',"agent-device replay ./session.ad","agent-device test ./suite --platform android"],m={workflow:{summary:"Normal agent-device bootstrap, exploration, and validation loop",body:`agent-device help workflow
|
|
2
2
|
|
|
3
3
|
Version-matched operating guide for normal agent-device work.
|
|
4
4
|
|
|
@@ -27,8 +27,8 @@ Bootstrap:
|
|
|
27
27
|
agent-device prepare ios-runner --platform ios --timeout 240000
|
|
28
28
|
If app id is unknown, plan devices, apps, then open <discovered-app-id>. Discovery is not enough when the task asks to open/start the app.
|
|
29
29
|
Install arguments are app/package id then artifact path. If the task says install, use install; use reinstall only when explicitly requested. Fresh runtime state is open --relaunch after install.
|
|
30
|
-
In Apple CI, run prepare ios-runner after boot/install and before replay/test. prepare ios-runner builds/reuses the XCTest runner, health-checks it with a lightweight command, and retries one stuck/non-connecting runner launch before the first snapshot pays that setup cost.
|
|
31
|
-
CI may cache ~/.agent-device/ios-runner/derived with an exact key that includes the agent-device package and Xcode version. Avoid broad restore-key fallbacks; prepare ios-runner already recovers bad restored runner artifacts and one retryable non-connecting runner launch.
|
|
30
|
+
In Apple CI, run prepare ios-runner after boot/install and before replay/test. prepare ios-runner builds/reuses the XCTest runner, health-checks it with a lightweight command, and retries one stuck/non-connecting runner launch before the first snapshot pays that setup cost. If the replay/test step starts a separate daemon, run clean:daemon after prepare so the prepared runner does not keep a live lease owned by the prepare daemon.
|
|
31
|
+
CI may cache ~/.agent-device/ios-runner/derived with an exact key that includes the agent-device package and Xcode version. Avoid broad restore-key fallbacks; prepare ios-runner already recovers bad restored runner artifacts and one retryable non-connecting runner launch. Runner build/start output is written to the session's runner.log; daemon.log is for daemon lifecycle/startup issues.
|
|
32
32
|
Do not open artifact paths or invent package ids. If apps lookup misses the target and no URL/artifact is provided, ask or stop.
|
|
33
33
|
|
|
34
34
|
Snapshots and refs:
|
|
@@ -113,7 +113,8 @@ Validation and evidence:
|
|
|
113
113
|
Prefer provided testIDs/ids/selectors for verification; use visible text when no durable selector is provided.
|
|
114
114
|
If task says snapshot, use snapshot. If it asks visual evidence, use screenshot.
|
|
115
115
|
Icon/tappable visual proof: screenshot --overlay-refs. Flag is --overlay-refs.
|
|
116
|
-
|
|
116
|
+
If snapshot returns a sparse/AX-unavailable state, refs are not reliable. Use plain screenshot, not screenshot --overlay-refs, navigate with coordinates if needed, then retry snapshot -i after reaching another screen; the AX failure may be screen-specific.
|
|
117
|
+
Startup/CPU/memory/frame first pass: perf metrics --json (bare perf and metrics are aliases). Focused frame/jank health: perf frames --json. Replay maintenance: replay -u ./flow.ad.
|
|
117
118
|
Recording: record start/stop. By default, stop burns touch overlays into the video; use record start --hide-touches for the fastest raw recording. Android adb screenrecord has a 180s platform limit, so longer Android recordings are returned as multiple MP4 chunks. For gesture-heavy iOS simulator proof videos, prefer --hide-touches because overlay timing depends on a stable runner session while gestures are executing. Tracing: trace start ./trace.log, trace stop ./trace.log. Paths are positional.
|
|
118
119
|
Stable known flow: batch ./steps.json, not workflow batch.
|
|
119
120
|
Inline batch JSON example:
|
|
@@ -188,6 +189,9 @@ Alerts:
|
|
|
188
189
|
|
|
189
190
|
Diagnostics and traces:
|
|
190
191
|
Use --debug for CLI/daemon diagnostic ids and log paths.
|
|
192
|
+
Open output includes Session state; JSON also includes runnerLogPath and requestLogPath.
|
|
193
|
+
Session requests/<request-id>.ndjson holds daemon request diagnostics; session runner.log holds Apple runner/xcodebuild output.
|
|
194
|
+
daemon.log is global daemon lifecycle evidence, not the primary per-run log.
|
|
191
195
|
Use trace for low-level session diagnostics around one repro:
|
|
192
196
|
agent-device trace start ./traces/diagnostics.trace
|
|
193
197
|
agent-device press 'id="load-diagnostics"'
|
|
@@ -263,7 +267,7 @@ Example:
|
|
|
263
267
|
agent-device react-devtools profile report @c5
|
|
264
268
|
agent-device network dump --include headers
|
|
265
269
|
|
|
266
|
-
Use snapshot, screenshot, logs, network, and perf for device/app runtime evidence. Use react-devtools only when component internals or React rendering behavior matters.`},"react-native":{summary:"React Native app automation hazards and routing",body:`agent-device help react-native
|
|
270
|
+
Use snapshot, screenshot, logs, network, and perf metrics for device/app runtime evidence. Use react-devtools only when component internals or React rendering behavior matters.`},"react-native":{summary:"React Native app automation hazards and routing",body:`agent-device help react-native
|
|
267
271
|
|
|
268
272
|
Use this when the target app is React Native, Expo, or a React Native dev client.
|
|
269
273
|
This topic covers React Native-specific automation hazards and routes deeper
|
|
@@ -292,17 +296,18 @@ Overlays and busy RN UIs:
|
|
|
292
296
|
Do not manually press warning/error text bodies, collapsed banner bodies, full-screen warning parents, or broad LogBox/RedBox refs. The dismiss-overlay command owns the narrow LogBox/RedBox targeting policy.
|
|
293
297
|
Report the overlay in the final summary. Use screenshot --overlay-refs before dismissing only if visual evidence is required.
|
|
294
298
|
If snapshot times out because the UI never becomes idle, Android accessibility may be blocked by busy or continuously changing app UI. After that timeout, use screenshot as visual truth instead of repeatedly retrying snapshots.
|
|
299
|
+
If iOS snapshot reports AX unavailable or returns only a sparse root, the current screen's accessibility state is invalid. Use plain screenshot as visual truth, coordinate navigation to leave the bad screen, then take a fresh snapshot -i before returning to selector/@ref commands.
|
|
295
300
|
Android runtime permission dialogs and native alerts are handled by alert wait/accept/dismiss. If alert reports no alert, treat the visible surface as app-owned UI and use snapshot -i plus press by label/ref.
|
|
296
301
|
|
|
297
302
|
React DevTools routing:
|
|
298
303
|
Keep the agent-device react-devtools prefix on every React DevTools command.
|
|
299
304
|
Use help react-devtools for status/wait, component trees, props/state/hooks, profile windows, slow renders, rerenders, and remote bridge rules.
|
|
300
|
-
If React DevTools cannot connect, report status and continue with logs, network, perf, screenshot, and trace evidence instead of blocking the whole flow.
|
|
305
|
+
If React DevTools cannot connect, report status and continue with logs, network, perf metrics, screenshot, and trace evidence instead of blocking the whole flow.
|
|
301
306
|
|
|
302
307
|
Slow-flow investigation:
|
|
303
308
|
Keep one session, open the app, and snapshot -i.
|
|
304
309
|
Use help react-devtools for the narrow React profile window.
|
|
305
|
-
Use help debugging for logs clear --restart, logs mark, network dump --include headers, perf --json, traces, and runtime failure evidence.
|
|
310
|
+
Use help debugging for logs clear --restart, logs mark, network dump --include headers, perf metrics --json, traces, and runtime failure evidence.
|
|
306
311
|
For 15-20s async work, use wait with the exact expected text or selector instead of repeated snapshots.
|
|
307
312
|
Report React render offenders separately from network/backend waits and device frame/CPU/memory findings.`},"physical-device":{summary:"Connected phone/tablet setup and iOS signing prerequisites",body:`agent-device help physical-device
|
|
308
313
|
|
|
@@ -442,11 +447,11 @@ Rules:
|
|
|
442
447
|
Re-snapshot after each mutation.
|
|
443
448
|
Keep commands in the report reproducible; use selectors or refs from fresh snapshots, not guessed coordinates.
|
|
444
449
|
Prefer refs for exploration and selectors for deterministic replay.
|
|
445
|
-
Use logs, network, screenshot --overlay-refs, trace, perf, or react-devtools only when they add evidence to a specific issue.
|
|
450
|
+
Use logs, network, screenshot --overlay-refs, trace, perf metrics, perf frames, or react-devtools only when they add evidence to a specific issue.
|
|
446
451
|
Never delete screenshots, videos, traces, or report artifacts during a session.
|
|
447
452
|
Escalate to help debugging or help react-devtools when runtime symptoms require those tools.`}};function v(e){let t=e.endsWith("?"),o=t?e.slice(0,-1):e;return t?`[${o}]`:`<${o}>`}function w(e){return r().filter(t=>e.has(t.key)&&void 0!==t.usageLabel&&void 0!==t.usageDescription)}function b(e,t){return y(e,t.map(e=>({label:e.usageLabel??"",description:e.usageDescription??""})))}function y(e,t){if(0===t.length)return`${e}
|
|
448
453
|
(none)`;let o=Math.max(...t.map(e=>e.label.length))+2,a=[e];for(let e of t)a.push(` ${e.label.padEnd(o)}${e.description}`);return a.join("\n")}function k(e,t){return 0===t.length?`${e}
|
|
449
|
-
(none)`:[e,...t.map(e=>` ${e}`)].join("\n")}let x=new Set(["config","remoteConfig","help","version","batchSteps","githubActionsArtifact"]),I=new Set(["appsFilter","iosSimulatorDeviceSet","sessionLocked","sessionLockConflicts"]),A=function(){let o=new Map;for(let e of r()){let t=o.get(e.key);t?t.push(e):o.set(e.key,[e])}let a=new Map;for(let e of s)a.set(e,new Set(["*"]));for(let o of e())for(let e of t(o).allowedFlags??[]){let t=a.get(e);t&&t.has("*")||(t?t.add(o):a.set(e,new Set([o])))}return[...o.entries()].map(([e,t])=>({key:e,flagDefinitions:t,config:{enabled:!x.has(e),key:e},env:{names:I.has(e)?[]:[l(e)]},supportsCommand(t){let o=a.get(e);return!!o&&(!!o.has("*")||!!t&&o.has(t))}})).sort((e,t)=>e.key.localeCompare(t.key))}(),R=new Map(A.map(e=>[e.key,e]));function S(e){return R.get(e)}function N(e){return A.filter(t=>t.config.enabled&&t.supportsCommand(e))}function U(e,t){return S(e)?.supportsCommand(t)??!1}function E(e,t,o,a){return i(function(e){let t=e.flagDefinitions.find(e=>void 0===e.setValue);if(t)return t;let o=function(e){let t=e.flagDefinitions[0];if(!t)throw Error(`Missing flag definition for option ${e.key}`);return t}(e);if("enum"===o.type){let t=o.enumValues??e.flagDefinitions.map(e=>e.setValue).filter(e=>void 0!==e);return{...o,setValue:void 0,enumValues:t}}return o}(e),t,o,a)}function D(e){let t={json:!1,help:!1,version:!1},a=null,s=[],r=[],i=!0;for(let p=0;p<e.length;p+=1){var l,d;let u=e[p];if(i&&"--"===u){i=!1;continue}if(!i){a?s.push(u):a=M(u);continue}let f=u.startsWith("--"),h=u.startsWith("-")&&u.length>1;if(!f&&!h){a?s.push(u):a=M(u);continue}let[g,m]=f?
|
|
454
|
+
(none)`:[e,...t.map(e=>` ${e}`)].join("\n")}let x=new Set(["config","remoteConfig","help","version","batchSteps","githubActionsArtifact"]),I=new Set(["appsFilter","iosSimulatorDeviceSet","sessionLocked","sessionLockConflicts"]),A=function(){let o=new Map;for(let e of r()){let t=o.get(e.key);t?t.push(e):o.set(e.key,[e])}let a=new Map;for(let e of s)a.set(e,new Set(["*"]));for(let o of e())for(let e of t(o).allowedFlags??[]){let t=a.get(e);t&&t.has("*")||(t?t.add(o):a.set(e,new Set([o])))}return[...o.entries()].map(([e,t])=>({key:e,flagDefinitions:t,config:{enabled:!x.has(e),key:e},env:{names:I.has(e)?[]:[l(e)]},supportsCommand(t){let o=a.get(e);return!!o&&(!!o.has("*")||!!t&&o.has(t))}})).sort((e,t)=>e.key.localeCompare(t.key))}(),R=new Map(A.map(e=>[e.key,e]));function S(e){return R.get(e)}function N(e){return A.filter(t=>t.config.enabled&&t.supportsCommand(e))}function U(e,t){return S(e)?.supportsCommand(t)??!1}function E(e,t,o,a){return i(function(e){let t=e.flagDefinitions.find(e=>void 0===e.setValue);if(t)return t;let o=function(e){let t=e.flagDefinitions[0];if(!t)throw Error(`Missing flag definition for option ${e.key}`);return t}(e);if("enum"===o.type){let t=o.enumValues??e.flagDefinitions.map(e=>e.setValue).filter(e=>void 0!==e);return{...o,setValue:void 0,enumValues:t}}return o}(e),t,o,a)}function D(e){let t={json:!1,help:!1,version:!1},a=null,s=[],r=[],i=!0;for(let p=0;p<e.length;p+=1){var l,d;let u=e[p];if(i&&"--"===u){i=!1;continue}if(!i){a?s.push(u):a=M(u);continue}let f=u.startsWith("--"),h=u.startsWith("-")&&u.length>1;if(!f&&!h){a?s.push(u):a=M(u);continue}let[g,m]=f?C(u):[u,void 0],v=n(g);if(l=a,d=v,"react-devtools"===l&&(!d||!U(d.key,l))){s.push(u);continue}if(!v){if(function(e,t,a){var s;if(s=a,!/^-\d+(\.\d+)?$/.test(s)||!e)return!1;let n=o(e);if(!n||n.allowsExtraPositionals)return!0;let r=n.positionalArgs??[];return 0!==r.length&&(t.length<r.length||r.some(e=>e.includes("?")))}(a,s,u)){a?s.push(u):a=u;continue}throw new c("INVALID_ARGS",`Unknown flag: ${g}`)}let w=function(e,t,o,a){if(void 0!==e.setValue){if(void 0!==o)throw new c("INVALID_ARGS",`Flag ${t} does not take a value.`);return{value:e.setValue,consumeNext:!1}}if("boolean"===e.type){if(void 0!==o)throw new c("INVALID_ARGS",`Flag ${t} does not take a value.`);return{value:!0,consumeNext:!1}}if("booleanOrString"===e.type){if(void 0!==o){if(0===o.trim().length)throw new c("INVALID_ARGS",`Flag ${t} requires a non-empty value when provided.`);return{value:o,consumeNext:!1}}return void 0===a||L(a)||!function(e){let t=e.trim();return!(!t||/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(t))&&!!(t.startsWith("./")||t.startsWith("../")||t.startsWith("~/")||t.startsWith("/")||t.includes("/")||t.includes("\\"))}(a)?{value:!0,consumeNext:!1}:{value:a,consumeNext:!0}}let s=o??a;if(void 0===s||void 0===o&&L(s))throw new c("INVALID_ARGS",`Flag ${t} requires a value.`);if("string"===e.type)return{value:s,consumeNext:void 0===o};if("enum"===e.type){if(!e.enumValues?.includes(s))throw new c("INVALID_ARGS",`Invalid ${_(t)}: ${s}`);return{value:s,consumeNext:void 0===o}}let n=Number(s);if(!Number.isFinite(n)||"number"==typeof e.min&&n<e.min||"number"==typeof e.max&&n>e.max)throw new c("INVALID_ARGS",`Invalid ${_(t)}: ${s}`);return{value:Math.floor(n),consumeNext:void 0===o}}(v,g,m,e[p+1]);w.consumeNext&&(p+=1);let b=t[v.key];if(v.multiple){let e=Array.isArray(b)?[...b,w.value]:void 0===b?[w.value]:[b,w.value];t[v.key]=e}else t[v.key]=w.value;r.push({key:v.key,token:g})}return{command:a,positionals:s,flags:t,warnings:[],providedFlags:r}}function O(e,t){let o=t?.strictFlags??!0,s=[...e.warnings],n=d({json:!1,help:!1,version:!1},t?.defaultFlags??{});d(n,e.flags);let r=e.providedFlags.filter(t=>!U(t.key,e.command));if(r.length>0){var i,l;let t=r.map(e=>e.token),a=(i=e.command,l=t,i?1===l.length?`Flag ${l[0]} is not supported for command ${i}.`:`Flags ${l.join(", ")} are not supported for command ${i}.`:1===l.length?`Flag ${l[0]} requires a command that supports it.`:`Flags ${l.join(", ")} require a command that supports them.`);if(o)throw new c("INVALID_ARGS",a);for(let e of(s.push(a),r))delete n[e.key]}for(let t of Object.keys(n))void 0!==n[t]&&(U(t,e.command)||delete n[t]);if(function(e){if("back"===e.command&&!(new Set(e.providedFlags.filter(e=>"backMode"===e.key).map(e=>e.token)).size<=1))throw new c("INVALID_ARGS","back accepts only one explicit mode flag: use either --in-app or --system.")}(e),a(e.command,n),"batch"===e.command&&1!=+!!n.steps+ +!!n.stepsFile)throw new c("INVALID_ARGS","batch requires exactly one step source: --steps or --steps-file.");return function(e){if(e.flags.help)return e;if("snapshot"===e.command&&e.flags.snapshotDiff){let{snapshotDiff:t,...o}=e.flags;return{command:"diff",positionals:["snapshot",...e.positionals],flags:o,warnings:e.warnings}}return e}({command:e.command,positionals:e.positionals,flags:n,warnings:s})}function C(e){let t=e.indexOf("=");return -1===t?[e,void 0]:[e.slice(0,t),e.slice(t+1)]}function _(e){return e.replace(/^-+/,"")}function L(e){if(!e.startsWith("-")||"-"===e)return!1;let[t]=e.startsWith("--")?C(e):[e,void 0];return void 0!==n(t)}function T(){let o,a,n,r,i,l,c,d;return o=`agent-device <command> [args] [--json]
|
|
450
455
|
|
|
451
456
|
CLI to control iOS and Android devices for AI agents.
|
|
452
457
|
`,a=y("Commands:",e().map(e=>{let o=t(e);return{name:e,schema:o,usage:function(e,t){if(t.listUsageOverride)return t.listUsageOverride;let o=(t.positionalArgs??[]).map(o=>{var a,s,n;let r,i,l,c;return a=e,s=t,i=(r=(n=o).endsWith("?"))?n.slice(0,-1):n,c=(l=/^[a-z-]+(?:\|[a-z-]+)+$/i.test(i))||void 0!==s.usageOverride&&s.usageOverride.startsWith(`${a} ${i}`),r?l?`[${i}]`:c?i:`[${i}]`:c?i:`<${i}>`});return[e,...o].join(" ")}(e,o)}}).map(e=>({label:e.usage,description:e.schema.summary??e.schema.helpDescription}))),n=b("Flags:",w(s)),r=k("Agent Quickstart:",u),i=y("Agent Workflows:",p),l=k("Configuration:",f),c=y("Environment:",h),d=k("Examples:",g),`${o}
|
|
@@ -477,4 +482,4 @@ Usage:
|
|
|
477
482
|
agent-device ${c}
|
|
478
483
|
|
|
479
484
|
${u.join("\n\n")}
|
|
480
|
-
`}(M(e))}function M(e){return"long-press"===e?"longpress":"metrics"===e?"perf":e}export{T as usage,
|
|
485
|
+
`}(M(e))}function M(e){return"long-press"===e?"longpress":"metrics"===e?"perf":e}export{T as usage,O as finalizeParsedArgs,N as getConfigurableOptionSpecs,S as getOptionSpec,d as mergeDefinedFlags,E as parseOptionValueFromSource,D as parseRawArgs,F as usageForCommand};
|