agent-device 0.16.10 → 0.16.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +8 -8
  2. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.16.10.apk → agent-device-android-multitouch-helper-0.16.12.apk} +0 -0
  3. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.12.apk.sha256 +1 -0
  4. package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.16.10.manifest.json → agent-device-android-multitouch-helper-0.16.12.manifest.json} +4 -4
  5. package/android-snapshot-helper/README.md +6 -0
  6. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.12.apk +0 -0
  7. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.12.apk.sha256 +1 -0
  8. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.16.10.manifest.json → agent-device-android-snapshot-helper-0.16.12.manifest.json} +6 -6
  9. package/dist/src/1352.js +1 -1
  10. package/dist/src/221.js +6 -6
  11. package/dist/src/2415.js +27 -27
  12. package/dist/src/2805.js +1 -1
  13. package/dist/src/4778.js +1 -0
  14. package/dist/src/5792.js +1 -1
  15. package/dist/src/6085.js +1 -1
  16. package/dist/src/6232.js +1 -1
  17. package/dist/src/8699.js +1 -1
  18. package/dist/src/9238.js +4 -0
  19. package/dist/src/9533.js +1 -1
  20. package/dist/src/9542.js +3 -3
  21. package/dist/src/apple.js +1 -1
  22. package/dist/src/args.js +54 -25
  23. package/dist/src/batch.d.ts +1 -0
  24. package/dist/src/cli.js +19 -19
  25. package/dist/src/command-metadata.js +1 -1
  26. package/dist/src/command-surface.js +1 -1
  27. package/dist/src/contracts.d.ts +1 -0
  28. package/dist/src/contracts.js +1 -1
  29. package/dist/src/generic.js +10 -10
  30. package/dist/src/index.d.ts +9 -0
  31. package/dist/src/record-trace.js +3 -3
  32. package/dist/src/session.js +9 -9
  33. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +7 -0
  34. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +109 -0
  35. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +123 -32
  36. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +36 -0
  37. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +11 -1
  38. package/package.json +1 -1
  39. package/server.json +2 -2
  40. package/skills/dogfood/SKILL.md +1 -1
  41. package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.10.apk.sha256 +0 -1
  42. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.10.apk +0 -0
  43. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.10.apk.sha256 +0 -1
  44. package/dist/src/2842.js +0 -1
  45. package/dist/src/8114.js +0 -4
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 o from"node:os";import n 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{formatRequestProgressEvent as h,consumeTextLines as y,shouldStreamRequestProgress as w,isDaemonResponseEnvelope as I,isDaemonProgressEnvelope as g,resolveDaemonPaths as v,resolveDaemonServerMode as A,computeDaemonCodeSignature as _,resolveDaemonTransportPreference as k}from"./8114.js";import{readVersion as P,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 C,PUBLIC_COMMANDS as N}from"./5792.js";import{sleep as U}from"./4829.js";import{reloadMetro as L,prepareMetroRuntime as R}from"./1974.js";import{normalizeSession as O,resolveSessionName as x,buildFlags as F,normalizeStartupSample as q,normalizeInstallFromSourceResult as B,normalizeMaterializationReleaseResult as $,prepareDaemonCommandRequest as j,readSnapshotNodes as H,normalizeRuntimeHints as K,normalizeDeployResult as G,normalizeOpenDevice as z,readScreenshotOverlayRefs as V,buildMeta as W,normalizeDevice as J}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 en({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 eo({normalizedBase:a,token:e.token,artifact:t})}finally{t.cleanup()}}async function et(e,t){var a,o,i;let s,l=r.statSync(e),c=n.basename(e),d=l.isDirectory(),u=("ios"===(a=t)||"android"===a?a:void 0)??(o=e,i=l,s=o.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(n.join(o.tmpdir(),`agent-device-upload-${l()}-`));t.push(a);let i=n.join(a,`${n.basename(e)}.tar.gz`);return await m("tar",["czf",i,"-C",n.dirname(e),n.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 eo(e){let{normalizedBase:t,token:a,artifact:r}=e,o=new URL("upload",t),n={"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&&(n.authorization=`Bearer ${a}`,n["x-agent-device-token"]=a);let i=await es({url:o,method:"POST",headers:n,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 en(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 o="https:"===e.url.protocol?a:t;return await new Promise((t,a)=>{let n=o.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(()=>{n.destroy(),a(new u("COMMAND_FAILED",e.timeoutMessage,{timeoutMs:3e5,...e.timeoutHint?{hint:e.timeoutHint}:{}}))},3e5);n.on("error",t=>{clearTimeout(s),a(new u("COMMAND_FAILED",e.errorMessage,e.errorHint?{hint:e.errorHint}:{},t))}),n.on("close",()=>clearTimeout(s)),i(r.createReadStream(e.payloadPath),n).catch(e=>{n.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 o=await r.json().catch(()=>void 0);if(!o?.ok||!o.uploadId)throw new u("COMMAND_FAILED","Invalid upload finalize response");return o.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=h(e);t&&process.stderr.write(`${t}
2
- `)}let eu=["xcodebuild .*AgentDeviceRunnerUITests/RunnerTests/testCommand","xcodebuild .*AgentDeviceRunner\\.env\\.session-","xcodebuild build-for-testing .*ios-runner/AgentDeviceRunner/AgentDeviceRunner\\.xcodeproj"],ep=new e.BlockList;async function em(t){var a,i,s,l,c,d;let p,m,f,h,y,w,I,g=t.meta?.requestId??b(),_=!!(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)?ep.check(r,"ipv4"):!!e.isIPv6(r)&&ep.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:A(e.flags?.daemonServerMode??process.env.AGENT_DEVICE_DAEMON_SERVER_MODE??("dual"===a?"dual":void 0))}}(a,y.remoteBaseUrl),{paths:v((I=(l=a,c=h,d=y.rawBaseUrl,eS(l.command)&&!c&&!d))?r.mkdtempSync(n.join(o.tmpdir(),"agent-device-replay-daemon-")):h),transportPreference:w.preference,serverMode:w.serverMode,ownedStateDir:I,remoteBaseUrl:y.remoteBaseUrl,remoteAuthToken:y.authToken}),S=function(e){if(e.command!==N.test)return e.command===N.replay&&"number"==typeof e.flags?.timeoutMs?e.flags.timeoutMs:9e4}(t),M=await D("daemon_startup",async()=>await ev(P),{requestId:g,session:t.session}),T=M.info,C=await ef(t,T),U={...t,positionals:C.positionals,flags:C.flags,token:T.token,meta:{...t.meta??{},requestId:g,debug:_,cwd:t.meta?.cwd,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}:{}}};E({level:"info",phase:"daemon_request_prepare",data:{requestId:g,command:t.command,session:t.session}});try{return await D("daemon_request",async()=>await eB(T,U,P.transportPreference,S),{requestId:g,command:t.command})}finally{await eP(t,M,P)}}async function ef(e,t){let a,r=[...e.positionals??[]],o=e.flags?{...e.flags}:void 0,i=e.meta?.installSource,s={};if(!eJ(t))return ey({positionals:r,flags:o,installSource:i,uploadedArtifactId:a,clientArtifactPaths:s});o=function(e,t,a,r){var o,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:eg("screenshot",".png")}:{field:"path",localPath:a,positionalIndex:0,flagPath:eg("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:eg("recording",n.extname(t)||".mp4")}}return null}(e,t);if(!s)return a;void 0!==s.positionalPath&&(t[s.positionalIndex]=s.positionalPath);let l=(o=a,void 0===(i=s.flagPath)?o:{...o??{},out:i});return r[s.field]=s.localPath,l}(e,r,o,s);let l=await ew(e,t);l&&(i=l.installSource,a=l.uploadedArtifactId??a);let c=()=>ey({positionals:r,flags:o,installSource:i,uploadedArtifactId:a,clientArtifactPaths:s});return"install"!==e.command&&"reinstall"!==e.command||(a=await eh(e,t,r)??a),c()}async function eh(e,t,a){var o,i;let s,l=a[1];if(void 0===l)return;if(l.startsWith("remote:")){a[1]=l.slice(7);return}let c=(o=l,i=e.meta?.cwd,s=n.isAbsolute(o)?o:n.resolve(i??process.cwd(),o),r.existsSync(s)?s:void 0);if(c)return await ee({localPath:c,baseUrl:t.baseUrl,token:t.token,platform:e.flags?.platform})}function ey(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 ew(e,t){let a=e.meta?.installSource;if("install_source"!==e.command||!a||"path"!==a.kind)return null;let o=a.path.trim();if(!o)return{installSource:a};if(o.startsWith("remote:"))return{installSource:{...a,path:o.slice(7)}};let i=n.isAbsolute(o)?o:n.resolve(e.meta?.cwd??process.cwd(),o);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 o=e.positionals?.[r]??e.flags?.out,i=`${"path"===t?"screenshot":"recording"}-${Date.now()}${a}`,s=o&&o.trim().length>0?o:i;return n.isAbsolute(s)?s:n.resolve(e.meta?.cwd??process.cwd(),s)}function eg(e,t){let a=t.startsWith(".")?t:`.${t}`;return n.posix.join("/tmp",`agent-device-${e}-${Date.now()}-${Math.random().toString(36).slice(2,8)}${a}`)}async function ev(e){if(e.remoteBaseUrl)return await eA(e);let t=await e_(e);return t?{info:t,startedByClient:!1}:(function(e){let t=eL(e);if(!t.hasLock||t.hasInfo)return;let a=eN(e.lockPath);if(!a)return eO(e.lockPath);M(a.pid,a.processStartTime)||eO(e.lockPath)}(e.paths),await ek(e))}async function eA(e){let t={transport:"http",token:e.remoteAuthToken??"",pid:0,baseUrl:e.remoteBaseUrl};if(await ex(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 e_(e){var t,a;let r,o=eM(e.paths.infoPath);if(!o)return null;let n=await ex(o,e.transportPreference);return(t=o,a=n,t.version===P()&&t.codeSignature===_((r=eq()).useSrc?r.srcPath:r.distPath,r.root)&&a)?o:(await eE(o),eO(e.paths.infoPath),null)}async function ek(e){let t,a=0,r=[];for(let o=1;o<=2;o+=1){try{await eF(e)}catch(a){if(t=a instanceof Error?a.message:String(a),r.push(await eU(e.paths,"start_error")),o<2){await U(150);continue}break}let n=await eb(15e3,e);if(n)return{info:n,startedByClient:!0};if(await eD(e.paths)){a+=1;continue}let i=eL(e.paths),s=o<2,l=await eU(e.paths,"startup_timeout",{stopLiveProcesses:!1});if(r.push(l),l.retainedInfoProcess||l.retainedLockProcess){let t=await eb(15e3,e);if(t)return{info:t,startedByClient:!0};break}if(!s)break;i.hasInfo||i.hasLock||await U(150)}let o=eL(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:o,hint:function(e,t=v(process.env.AGENT_DEVICE_STATE_DIR)){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, remove ${t.lockPath} after confirming no agent-device daemon process is running.`: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, remove both files after confirming no agent-device daemon process is running.`:"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."}(o,e.paths)})}async function eP(e,t,a){if(!eS(e.command)||!t.startedByClient&&!a.ownedStateDir||eJ(t.info))return;let o={pid:t.info.pid,removedInfo:!1,removedLock:!1,removedStateDir:!1,error:void 0};try{await eE(t.info)}catch(e){o.error=e instanceof Error?e.message:String(e)}finally{let e=r.existsSync(a.paths.infoPath);eO(a.paths.infoPath),o.removedInfo=e&&!r.existsSync(a.paths.infoPath);let t=r.existsSync(a.paths.lockPath);eO(a.paths.lockPath),o.removedLock=t&&!r.existsSync(a.paths.lockPath),a.ownedStateDir&&(r.rmSync(a.paths.baseDir,{recursive:!0,force:!0}),o.removedStateDir=!r.existsSync(a.paths.baseDir))}E({level:o.error?"warn":"info",phase:"daemon_replay_cleanup",data:o})}function eS(e){return e===N.replay||e===N.test}async function eb(e,t){let a=Date.now();for(;Date.now()-a<e;){let e=eM(t.paths.infoPath);if(e&&await ex(e,t.transportPreference))return e;await U(100)}return null}async function eD(e){let t=eL(e);if(!t.hasLock||t.hasInfo)return!1;let a=eN(e.lockPath);return!(a&&M(a.pid,a.processStartTime))&&(eO(e.lockPath),!0)}async function eE(e){await T(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}function eM(e){var t,a,r;let o,n,i=eR(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=(o=eC((a=i).port),n=eC(a.httpPort),void 0===o&&void 0===n?null:{port:o,httpPort:n});return l?{token:s,...l,transport:"socket"===(r=i.transport)||"http"===r||"dual"===r?r:void 0,pid:eC(i.pid)??0,version:eT(i.version),codeSignature:eT(i.codeSignature),processStartTime:eT(i.processStartTime)}:null}function eT(e){return"string"==typeof e?e:void 0}function eC(e){return Number.isInteger(e)&&Number(e)>0?Number(e):void 0}function eN(e){let t=eR(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}ep.addSubnet("127.0.0.0",8,"ipv4"),ep.addAddress("::1","ipv6"),ep.addSubnet("::ffff:127.0.0.0",104,"ipv6");async function eU(e,t,a={}){let o=a.stopLiveProcesses??!0,n={reason:t,removedInfo:!1,removedLock:!1,stoppedInfoProcess:!1,stoppedLockProcess:!1};try{var i,s,l,c;let t=r.existsSync(e.infoPath),a=eM(e.infoPath);if(a){let t=M(a.pid,a.processStartTime);t&&!o?n.retainedInfoProcess=!0:(t&&(await eE(a),n.stoppedInfoProcess=!0),i=e.infoPath,eO(i),n.removedInfo=!0)}else t&&(s=e.infoPath,eO(s),n.removedInfo=!0);let d=r.existsSync(e.lockPath),u=eN(e.lockPath);if(u){let t=M(u.pid,u.processStartTime);t&&!o?n.retainedLockProcess=!0:(t&&(await T(u.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:u.processStartTime}),n.stoppedLockProcess=!0),l=e.lockPath,eO(l),n.removedLock=!0)}else d&&(c=e.lockPath,eO(c),n.removedLock=!0)}catch(e){n.error=e instanceof Error?e.message:String(e)}return E({level:n.error?"warn":"info",phase:"daemon_startup_metadata_cleanup",data:n}),n}function eL(e){return{hasInfo:r.existsSync(e.infoPath),hasLock:r.existsSync(e.lockPath)}}function eR(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 ex(r,o){var n;return"http"===e$(r,o)?await function(e){let r=e.baseUrl?eY(e.baseUrl,"health"):e.httpPort?`http://127.0.0.1:${e.httpPort}/health`:null;if(!r)return Promise.resolve(!1);let o=new URL(r),n="https:"===o.protocol?a:t,i=e.baseUrl?3e3:500;return new Promise(e=>{let t=n.request({protocol:o.protocol,host:o.hostname,port:o.port,path:o.pathname+o.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 ((n=r.port)?new Promise(t=>{let a=!1,r=e.createConnection({host:"127.0.0.1",port:n},()=>{o(!0)}),o=e=>{a||(a=!0,r.destroy(),t(e))};r.setTimeout(500),r.on("timeout",()=>{o(!1)}),r.on("error",()=>{o(!1)})}):Promise.resolve(!1))}async function eF(e){let t=eq(),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 eq(){let e=S(),t=[n.join(e,"dist","src","internal","daemon.js"),n.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 o=t.find(e=>r.existsSync(e))??a,i=n.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:o,distPaths:t,srcPath:i,useSrc:process.execArgv.includes("--experimental-strip-types")?l:!s&&l}}async function eB(e,t,a,r){return"http"===e$(e,a)?await ez(e,t,r):await eG(e,t,r)}function e$(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(ej(a,r))return r;throw new u("COMMAND_FAILED","http"===r?"Daemon HTTP endpoint is unavailable":"Daemon socket endpoint is unavailable")}let o=("socket"===e.transport||"dual"===e.transport?["socket","http"]:["http","socket"]).find(t=>ej(e,t));if(o)return o;throw new u("COMMAND_FAILED","Daemon metadata has no reachable transport")}function ej(e,t){return"http"===t?!!e.httpPort:!!e.port}function eH(e,t,a,r,o,n){let i=o?{terminated:0}:function(){let e=0;try{for(let t of eu){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=!o&&"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:n,requestId:a,command:r,timedOutRunnerPidsTerminated:i.terminated,timedOutRunnerCleanupError:i.error,daemonPidReset:s?e.pid:void 0,daemonPidForceKilled:s?l.forcedKill:void 0,daemonPreservedAfterTimeout:!o&&!s,daemonBaseUrl:e.baseUrl}}),new u("COMMAND_FAILED","Daemon request timed out",{timeoutMs:n,requestId:a,hint:function(e){let{remote:t,resetDaemon:a,command:r}=e;return t?"Retry with --debug and verify the remote daemon URL, auth token, and remote host logs.":a?"Retry with --debug and check daemon diagnostics logs. Timed-out iOS runner xcodebuild processes were terminated when detected.":`Retry with --debug and check daemon diagnostics logs. The timed-out ${r??"request"} request was canceled and iOS runner work was aborted when detected; the daemon was kept alive so the session can still be closed or inspected.`}({remote:o,resetDaemon:s,command:r})})}function eK(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 eG(t,a,r){let o=t.port;if(!o)throw new u("COMMAND_FAILED","Daemon socket endpoint is unavailable");return new Promise((n,i)=>{let s=e.createConnection({host:"127.0.0.1",port:o},()=>{s.write(`${JSON.stringify(a)}
3
- `)}),l=v(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=y(p,e);for(let e of(p=t.buffer,t.lines))try{let t=JSON.parse(e);if(g(t)){ed(t.event);continue}let a=I(t)?t.response:t;c=!0,s.end(),d&&clearTimeout(d),n(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(eK(e,a.meta?.requestId,!1)))})})}async function ez(e,r,o){var n,i,s;let l,c=e.baseUrl?new URL(eY(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((n=r,i={includeTokenParam:!e.baseUrl},l=n.meta?.requestId??b(),"lease_allocate"!==(s=n.command)&&"lease_heartbeat"!==s&&"lease_release"!==s?{jsonrpc:"2.0",id:l,method:"agent_device.command",params:n}:{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"}}(n.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}}}(n,n.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((n,i)=>{let s=v(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:o,clearTimeout:n}=t,i="",s=!1,l=e=>{try{let t=JSON.parse(e);if(g(t))return ed(t.event),!1;if(I(t))return s=!0,n(),r(JSON.stringify(t.response)),!0;throw Error("Missing daemon progress response envelope")}catch(t){return s=!0,n(),o(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,n(),o(new u("COMMAND_FAILED","Invalid daemon response",{requestId:a.meta?.requestId,line:e})))}),e.on("error",e=>{s||(s=!0,n(),o(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=>eV(t,{info:e,req:r,resolve:n,reject:i})}):X(t).then(t=>{f&&clearTimeout(f),eV(t,{info:e,req:r,resolve:n,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=eJ(e),f="number"==typeof o?setTimeout(()=>{l.destroy(),i(eH(e,s,r.meta?.requestId,r.command,m,o))},o):void 0;l.on("error",e=>{f&&clearTimeout(f),i(eK(e,r.meta?.requestId,m))}),l.write(d),l.end()})}function eV(e,t){let{info:a,req:r,resolve:o,reject:n}=t;try{var i,s,l;let t=(i=e,JSON.parse(i));if(t.error){let e;return void n((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 n(new u("COMMAND_FAILED","Invalid daemon RPC response",{requestId:r.meta?.requestId}));eW(a,r,t.result,o,n)}catch(t){n(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,o){try{r(e.baseUrl&&a.ok?await eQ(e,t,a):a)}catch(e){o(e)}}function eJ(e){return"string"==typeof e.baseUrl&&e.baseUrl.length>0}function eY(e,t){return new URL(t,e.endsWith("/")?e:`${e}/`).toString()}async function eQ(e,t,a){let r=Array.isArray(a.data?.artifacts)?a.data.artifacts:[];if(0===r.length||!e.baseUrl)return a;let o=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 n.resolve(t.meta?.cwd??process.cwd(),r)}(a,t);await eX({baseUrl:e.baseUrl,token:e.token,artifactId:a.artifactId,destinationPath:r,requestId:t.meta?.requestId}),o[a.field]=r,i.push({...a,localPath:r})}return o.artifacts=i,{ok:!0,data:o}}async function eX(e){var o,s;let l,c=new URL((o=e.baseUrl,s=e.artifactId,l=o.endsWith("/")?o:`${o}/`,new URL(`artifacts/${encodeURIComponent(s)}`,l).toString())),d="https:"===c.protocol?a:t;await r.promises.mkdir(n.dirname(e.destinationPath),{recursive:!0}),await new Promise((t,a)=>{let o=!1,n=e.timeoutMs??9e4,s=n=>{if(!o){if(o=!0,clearTimeout(p),n)return void r.promises.rm(e.destinationPath,{force:!0}).finally(()=>a(n));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:n});s(t),l.destroy(t)},n);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:n},t instanceof Error?t:void 0))}),l.end()})}function eZ(e={},t={}){let a=t.transport??em,r=async(t,r=[],o={})=>{var n,i;let s=(n=e,i=o,{...n,...i}),l=await a({session:x(s.session),command:t,positionals:r,flags:F(s),runtime:s.runtime,meta:W(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??{}},o=async(e={})=>{let t=await r(C.sessionList,[],e);return(Array.isArray(t.sessions)?t.sessions:[]).map(O)},n=async(e,t={})=>{let a=j(e,t);return await r(a.command,a.positionals,a.options)},i=(t={})=>{var a,r;return x((a=e,r=t,{...a,...r}).session)};return{command:{wait:async e=>await n("wait",e),alert:async(e={})=>await n("alert",e),appState:async(e={})=>await n("appstate",e),back:async(e={})=>await n("back",e),home:async(e={})=>await n("home",e),rotate:async e=>await n("rotate",e),appSwitcher:async(e={})=>await n("app-switcher",e),keyboard:async(e={})=>await n("keyboard",e),clipboard:async e=>await n("clipboard",e),reactNative:async e=>await n("react-native",e)},devices:{list:async(e={})=>{let t=await n("devices",e);return(Array.isArray(t.devices)?t.devices:[]).map(J)},boot:async(e={})=>await n("boot",e)},sessions:{list:async(e={})=>await o(e),close:async(e={})=>{let t=i(e),a=(await n("close",e)).shutdown;return{session:t,shutdown:"object"==typeof a&&null!==a?a:void 0,identifiers:{session:t}}}},apps:{install:async e=>G(await n("install",e),i(e)),reinstall:async e=>G(await n("reinstall",e),i(e)),installFromSource:async e=>B(await n("install-from-source",e),i(e)),list:async(e={})=>{let t=await n("apps",e);return Array.isArray(t.apps)?t.apps.filter(e=>"string"==typeof e):[]},open:async e=>{let t=i(e),a=await n("open",e),r=z(a),o=Q(a,"appBundleId");return{session:t,appName:Q(a,"appName"),appBundleId:o,appId:o,startup:q(a.startup),runtime:K(a.runtime),device:r,identifiers:{session:t,deviceId:r?.id,deviceName:r?.name,udid:r?.ios?.udid,serial:r?.android?.serial,appId:o,appBundleId:o}}},close:async(e={})=>{let t=i(e),a=(await n("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 n("push",e),triggerEvent:async e=>await n("trigger-app-event",e)},materializations:{release:async e=>$(await r(C.releaseMaterializedPaths,[],{...e,materializationId:e.materializationId}))},leases:{allocate:async e=>e1(await r(C.leaseAllocate,[],{...e,leaseId:void 0,leaseTtlMs:e.ttlMs})),heartbeat:async e=>e1(await r(C.leaseHeartbeat,[],{...e,leaseTtlMs:e.ttlMs})),release:async e=>({released:!0===(await r(C.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 L({metroHost:t.metroHost,metroPort:t.metroPort,bundleUrl:t.bundleUrl,runtime:e.runtime,timeoutMs:t.timeoutMs})},capture:{snapshot:async(e={})=>{var t,a,r;let o,s,l,c,d,u=i(e);return t=await n("snapshot",e),a=u,o=Q(t,"appBundleId"),{nodes:H(t.nodes),truncated:!0===t.truncated,appName:Q(t,"appName"),appBundleId:o,...(s=e0((r=t).visibility),l=e0(r.androidSnapshot),c=e0(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:o,appBundleId:o}}},screenshot:async(e={})=>{let t=i(e),a=await n("screenshot",e);return{path:Y(a,"path"),overlayRefs:V(a),identifiers:{session:t}}},diff:async e=>await n("diff",e)},interactions:{click:async e=>await n("click",e),press:async e=>await n("press",e),longPress:async e=>await n("longpress",e),swipe:async e=>await n("swipe",e),pan:async e=>await n("gesture-pan",e),fling:async e=>await n("gesture-fling",e),swipeGesture:async e=>await n("gesture-swipe",e),focus:async e=>await n("focus",e),type:async e=>await n("type",e),fill:async e=>await n("fill",e),scroll:async e=>await n("scroll",e),pinch:async e=>await n("gesture-pinch",e),rotateGesture:async e=>await n("gesture-rotate",e),transformGesture:async e=>await n("gesture-transform",e),get:async e=>await n("get",e),is:async e=>await n("is",e),find:async e=>await n("find",e)},replay:{run:async e=>await n("replay",e),test:async e=>await n("test",e)},batch:{run:async e=>await n("batch",e)},observability:{perf:async(e={})=>await n("perf",e),logs:async(e={})=>await n("logs",e),network:async(e={})=>await n("network",e)},recording:{record:async e=>await n("record",e),trace:async e=>await n("trace",e)},settings:{update:async e=>await n("settings",e)}}}function e0(e){return"object"==typeof e&&null!==e?e:void 0}function e1(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{eZ as createAgentDeviceClient,em 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{formatRequestProgressEvent as h,consumeTextLines as y,shellQuote as w,shouldStreamRequestProgress as g,isDaemonResponseEnvelope as I,isDaemonProgressEnvelope as v,resolveDaemonPaths as A,resolveDaemonServerMode as _,computeDaemonCodeSignature as P,resolveDaemonTransportPreference as S}from"./9238.js";import{readVersion as k,findProjectRoot as b}from"./9671.js";import{createRequestId as D,withDiagnosticTimer as E,emitDiagnostic as M}from"./7599.js";import{isAgentDeviceDaemonProcess as T,stopProcessForTakeover as C}from"./8656.js";import{INTERNAL_COMMANDS as N,PUBLIC_COMMANDS as U}from"./5792.js";import{sleep as L}from"./4829.js";import{reloadMetro as R,prepareMetroRuntime as O}from"./1974.js";import{normalizeSession as x,resolveSessionName as F,buildFlags as q,normalizeStartupSample as B,normalizeInstallFromSourceResult as $,normalizeMaterializationReleaseResult as j,prepareDaemonCommandRequest as H,readSnapshotNodes as K,normalizeRuntimeHints as G,normalizeDeployResult as z,normalizeOpenDevice as V,readScreenshotOverlayRefs as W,buildMeta as J,normalizeDevice as Y}from"./8699.js";import{readRequiredString as Q,readOptionalString as X}from"./7455.js";function Z(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 ee="sha256";async function et(e){let t=await ea(e.localPath,e.platform),a=e.baseUrl.endsWith("/")?e.baseUrl:`${e.baseUrl}/`;try{let r=await ei({normalizedBase:a,token:e.token,artifact:t});if(r?.kind==="cache-hit")return r.uploadId;if(r?.kind==="direct-upload")try{return await es(t.payloadPath,r),await ec({normalizedBase:a,token:e.token,uploadId:r.uploadId})}catch{}return await eo({normalizedBase:a,token:e.token,artifact:t})}finally{t.cleanup()}}async function ea(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 er(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 ed(t),sizeBytes:a.size,cleanup:()=>en(p)}}catch(e){throw en(p),e}}async function er(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 en(e){for(let t of e)r.rmSync(t,{recursive:!0,force:!0})}async function eo(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":ee,"transfer-encoding":"chunked"};a&&(o.authorization=`Bearer ${a}`,o["x-agent-device-token"]=a);let i=await el({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 ei(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 es(e,t){let a=await el({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 el(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=>{Z(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 ec(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 ed(e){let t=s(ee),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 eu(e){let t=h(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,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=S(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,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}),k=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),b=await E("daemon_startup",async()=>await eA(P),{requestId:I,session:t.session}),T=b.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,k),{requestId:I,command:t.command})}finally{await ek(t,b,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 eS(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===k()&&t.codeSignature===P((r=eB()).useSrc?r.srcPath:r.distPath,r.root)&&a)?n:(await eM(n),ex(e.paths.infoPath),null)}async function eS(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 ek(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);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 eb(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=b(),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 iOS snapshot on the device, run agent-device prepare ios-runner --platform ios 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 iOS 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 iOS 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};
package/dist/src/apple.js CHANGED
@@ -1 +1 @@
1
- import{AppError as e}from"./9152.js";import{buildScrollGesturePlan as a}from"./9533.js";import{runMacOsScreenshotAction as n,runIosRunnerCommand as t}from"./2415.js";import{writeIosClipboardText as d,openIosApp as o,openIosDevice as r,screenshotIos as p,closeIosApp as i,setIosSetting as l,readIosClipboardText as c}from"./apps.js";import{withDiagnosticTimer as u}from"./7599.js";function s(e,a,n){return{command:"remotePress",remoteButton:e,...void 0!==n?{durationMs:n}:{},...void 0!==a?{appBundleId:a}:{}}}async function m(e,n,t,d,o,r,p){if("tv"===n.target)return B(await e(n,s(o,t.appBundleId),d),r);let i=p??await y(e,n,t,d),l=a({direction:o,amount:r?.amount,pixels:r?.pixels,referenceWidth:i.referenceWidth,referenceHeight:i.referenceHeight});return B(await e(n,{command:"drag",x:i.originX+l.x1,y:i.originY+l.y1,x2:i.originX+l.x2,y2:i.originY+l.y2,appBundleId:t.appBundleId},d),{amount:l.amount,pixels:l.pixels,preferProvidedPixels:!0})}async function y(a,n,t,d){let o=await a(n,{command:"interactionFrame",appBundleId:t.appBundleId},d),r=I(o.x),p=I(o.y),i=I(o.referenceWidth),l=I(o.referenceHeight);if(void 0===r||void 0===p||void 0===i||void 0===l)throw new e("COMMAND_FAILED","interactionFrame did not return a usable frame");return{originX:r,originY:p,referenceWidth:i,referenceHeight:l}}function I(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function B(e,a){var n;let{x1:t,y1:d,x2:o,y2:r}={x1:I((n=e).x),y1:I(n.y),x2:I(n.x2),y2:I(n.y2)},p=I(e.referenceWidth),i=I(e.referenceHeight),l=void 0!==t&&void 0!==o?Math.round(Math.abs(o-t)):void 0,c=void 0!==d&&void 0!==r?Math.round(Math.abs(r-d)):void 0,u=a?.preferProvidedPixels&&void 0!==a.pixels?a.pixels:l&&l>0?l:c&&c>0?c:void 0;return{...void 0!==t?{x1:t}:{},...void 0!==d?{y1:d}:{},...void 0!==o?{x2:o}:{},...void 0!==r?{y2:r}:{},...void 0!==p?{referenceWidth:p}:{},...void 0!==i?{referenceHeight:i}:{},...a?.amount!==void 0?{amount:a.amount}:{},...void 0!==u?{pixels:u}:{}}}function w(a,y){let I,{overrides:B,runnerOpts:w}={runnerOpts:I={verbose:y.verbose,logPath:y.logPath,traceLogPath:y.traceLogPath,requestId:y.requestId},overrides:{tap:async(e,n)=>await t(a,{command:"tap",x:e,y:n,appBundleId:y.appBundleId},I),tapElementSelector:async e=>await t(a,{command:"tap",selectorKey:e.key,selectorValue:e.value,allowNonHittableCoordinateFallback:e.allowNonHittableCoordinateFallback,appBundleId:y.appBundleId},I),doubleTap:async(e,n)=>await t(a,{command:"tapSeries",x:e,y:n,count:1,intervalMs:0,doubleTap:!0,appBundleId:y.appBundleId},I),swipe:async(e,n,d,o,r)=>await t(a,{command:"drag",x:e,y:n,x2:d,y2:o,durationMs:r,appBundleId:y.appBundleId},I),pan:async(e,n,d,o,r)=>await t(a,{command:"drag",x:e,y:n,x2:d,y2:o,durationMs:r??500,appBundleId:y.appBundleId},I),fling:async(e,n,d,o,r)=>await t(a,{command:"drag",x:e,y:n,x2:d,y2:o,durationMs:r??16,appBundleId:y.appBundleId},I),longPress:async(e,n,d)=>await t(a,{command:"longPress",x:e,y:n,durationMs:d,appBundleId:y.appBundleId},I),focus:async(e,n)=>await t(a,{command:"tap",x:e,y:n,appBundleId:y.appBundleId},I),type:async(e,n)=>{await t(a,{command:"type",text:e,delayMs:n,textEntryMode:"\n"===e?void 0:"append",appBundleId:y.appBundleId},I)},fillElementSelector:async(e,n,d)=>await t(a,{command:"type",selectorKey:e.key,selectorValue:e.value,allowNonHittableCoordinateFallback:e.allowNonHittableCoordinateFallback,text:n,delayMs:d,textEntryMode:"replace",appBundleId:y.appBundleId},I),fill:async(e,n,d,o)=>await t(a,{command:"type",x:e,y:n,text:d,delayMs:o,textEntryMode:"replace",appBundleId:y.appBundleId},I),scroll:async(e,n)=>await m(t,a,y,I,e,n),pinch:async(e,n,d)=>{await t(a,{command:"pinch",scale:e,x:n,y:d,appBundleId:y.appBundleId},I)},rotateGesture:async(e,n,d,o)=>{await t(a,{command:"rotateGesture",degrees:e,x:n,y:d,velocity:o,appBundleId:y.appBundleId},I)},transformGesture:async e=>await t(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:y.appBundleId},I)}};return{open:(e,n)=>o(a,e,{appBundleId:n?.appBundleId,launchConsole:n?.launchConsole,launchArgs:n?.launchArgs,url:n?.url}),openDevice:()=>r(a),close:e=>i(a,e),screenshot:async(e,t)=>{"macos"===a.platform&&t?.surface&&"app"!==t.surface?await n(e,{surface:t.surface,fullscreen:t.fullscreen}):await p(a,e,t?.appBundleId,t?.fullscreen,w)},snapshot:async n=>{var d;let o={nodes:Array.isArray((d=await u("snapshot_capture",async()=>await t(a,{command:"snapshot",appBundleId:n?.appBundleId,interactiveOnly:n?.interactiveOnly,compact:n?.compact,depth:n?.depth,scope:n?.scope,raw:n?.raw},w),{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 t(a,s("menu",y.appBundleId),w):await t(a,{command:"system"===e?"backSystem":"backInApp",appBundleId:y.appBundleId},w)},home:async()=>{"tv"===a.target?await t(a,s("home",y.appBundleId),w):await t(a,{command:"home",appBundleId:y.appBundleId},w)},rotate:async e=>{await t(a,{command:"rotate",orientation:e,appBundleId:y.appBundleId},w)},appSwitcher:async()=>{await t(a,{command:"appSwitcher",appBundleId:y.appBundleId},w)},readClipboard:()=>c(a),writeClipboard:e=>d(a,e),setSetting:(e,n,t,d)=>l(a,e,n,t,d),...B}}export{w as createAppleInteractor};
1
+ import{AppError as e}from"./9152.js";import{shouldUseSynthesizedIosDrag as a,runMacOsScreenshotAction as n,runIosRunnerCommand as t}from"./2415.js";import{buildScrollGesturePlan as d}from"./9533.js";import{writeIosClipboardText as o,openIosApp as r,openIosDevice as i,screenshotIos as p,closeIosApp as l,setIosSetting as s,readIosClipboardText as u}from"./apps.js";import{withDiagnosticTimer as c}from"./7599.js";function m(e,n,t,d,o,r,i,p){var l,s;let u=a(e),c=u?(l=i,s=p.synthesizedDefaultDurationMs,void 0===l?s:Math.min(1e4,Math.max(16,Math.round(l)))):i??p.legacyDefaultDurationMs;return{command:"drag",x:t,y:d,x2:o,y2:r,...void 0!==c?{durationMs:c}:{},...u?{synthesized:!0}:{},appBundleId:n.appBundleId}}function y(e,a,n){return{command:"remotePress",remoteButton:e,...void 0!==n?{durationMs:n}:{},...void 0!==a?{appBundleId:a}:{}}}async function I(e,a,n,t,o,r,i){if("tv"===a.target)return h(await e(a,y(o,n.appBundleId),t),r);let p=i??await f(e,a,n,t),l=d({direction:o,amount:r?.amount,pixels:r?.pixels,referenceWidth:p.referenceWidth,referenceHeight:p.referenceHeight});return h(await e(a,m(a,n,p.originX+l.x1,p.originY+l.y1,p.originX+l.x2,p.originY+l.y2,void 0,{synthesizedDefaultDurationMs:250}),t),{amount:l.amount,pixels:l.pixels,preferProvidedPixels:!0})}async function f(a,n,t,d){let o=await a(n,{command:"interactionFrame",appBundleId:t.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 h(e,a){var n;let{x1:t,y1:d,x2:o,y2:r}={x1:v((n=e).x),y1:v(n.y),x2:v(n.x2),y2:v(n.y2)},i=v(e.referenceWidth),p=v(e.referenceHeight),l=void 0!==t&&void 0!==o?Math.round(Math.abs(o-t)):void 0,s=void 0!==d&&void 0!==r?Math.round(Math.abs(r-d)):void 0,u=a?.preferProvidedPixels&&void 0!==a.pixels?a.pixels:l&&l>0?l:s&&s>0?s:void 0;return{...void 0!==t?{x1:t}:{},...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!==u?{pixels:u}:{}}}function B(a,d){let f,{overrides:v,runnerOpts:h}={runnerOpts:f={verbose:d.verbose,logPath:d.logPath,traceLogPath:d.traceLogPath,requestId:d.requestId},overrides:{tap:async(e,n)=>await t(a,{command:"tap",x:e,y:n,appBundleId:d.appBundleId},f),tapElementSelector:async e=>await t(a,{command:"tap",selectorKey:e.key,selectorValue:e.value,allowNonHittableCoordinateFallback:e.allowNonHittableCoordinateFallback,appBundleId:d.appBundleId},f),doubleTap:async(e,n)=>await t(a,{command:"tapSeries",x:e,y:n,count:1,intervalMs:0,doubleTap:!0,appBundleId:d.appBundleId},f),swipe:async(e,n,o,r,i)=>await t(a,m(a,d,e,n,o,r,i,{synthesizedDefaultDurationMs:250}),f),pan:async(e,n,o,r,i)=>await t(a,m(a,d,e,n,o,r,i,{synthesizedDefaultDurationMs:500,legacyDefaultDurationMs:500}),f),fling:async(e,n,o,r,i)=>await t(a,{command:"drag",x:e,y:n,x2:o,y2:r,durationMs:i??16,appBundleId:d.appBundleId},f),longPress:async(e,n,o)=>await t(a,{command:"longPress",x:e,y:n,durationMs:o,appBundleId:d.appBundleId},f),focus:async(e,n)=>await t(a,{command:"tap",x:e,y:n,appBundleId:d.appBundleId},f),type:async(e,n)=>{await t(a,{command:"type",text:e,delayMs:n,textEntryMode:"\n"===e?void 0:"append",appBundleId:d.appBundleId},f)},fillElementSelector:async(e,n,o)=>await t(a,{command:"type",selectorKey:e.key,selectorValue:e.value,allowNonHittableCoordinateFallback:e.allowNonHittableCoordinateFallback,text:n,delayMs:o,textEntryMode:"replace",appBundleId:d.appBundleId},f),fill:async(e,n,o,r)=>await t(a,{command:"type",x:e,y:n,text:o,delayMs:r,textEntryMode:"replace",appBundleId:d.appBundleId},f),scroll:async(e,n)=>await I(t,a,d,f,e,n),pinch:async(e,n,o)=>{await t(a,{command:"pinch",scale:e,x:n,y:o,appBundleId:d.appBundleId},f)},rotateGesture:async(e,n,o,r)=>{await t(a,{command:"rotateGesture",degrees:e,x:n,y:o,velocity:r,appBundleId:d.appBundleId},f)},transformGesture:async e=>await t(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:d.appBundleId},f)}};return{open:(e,n)=>r(a,e,{appBundleId:n?.appBundleId,launchConsole:n?.launchConsole,launchArgs:n?.launchArgs,url:n?.url}),openDevice:()=>i(a),close:e=>l(a,e),screenshot:async(e,t)=>{"macos"===a.platform&&t?.surface&&"app"!==t.surface?await n(e,{surface:t.surface,fullscreen:t.fullscreen}):await p(a,e,t?.appBundleId,t?.fullscreen,h)},snapshot:async n=>{var d;let o={nodes:Array.isArray((d=await c("snapshot_capture",async()=>await t(a,{command:"snapshot",appBundleId:n?.appBundleId,interactiveOnly:n?.interactiveOnly,compact:n?.compact,depth:n?.depth,scope:n?.scope,raw:n?.raw},h),{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 t(a,y("menu",d.appBundleId),h):await t(a,{command:"system"===e?"backSystem":"backInApp",appBundleId:d.appBundleId},h)},home:async()=>{"tv"===a.target?await t(a,y("home",d.appBundleId),h):await t(a,{command:"home",appBundleId:d.appBundleId},h)},rotate:async e=>{await t(a,{command:"rotate",orientation:e,appBundleId:d.appBundleId},h)},appSwitcher:async()=>{await t(a,{command:"appSwitcher",appBundleId:d.appBundleId},h)},readClipboard:()=>u(a),writeClipboard:e=>o(a,e),setSetting:(e,n,t,d)=>s(a,e,n,t,d),...v}}export{B as createAppleInteractor};
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 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.","Run mutating commands serially against one session; parallelize only read-only commands or separate sessions.","Before taking over a shared device, run session list and reuse the active session name when one already owns the device.","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; trace start ./path; trace stop ./path; network dump --include headers.","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:"Default 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
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 d}from"./9152.js";function c(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; trace start ./path; trace stop ./path; network dump --include headers.","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
 
@@ -24,8 +24,10 @@ Bootstrap:
24
24
  agent-device reinstall com.example.app ./build/MyApp.app --platform ios
25
25
  agent-device install-from-source --github-actions-artifact org/repo:app-debug --platform android
26
26
  agent-device open com.example.app --platform android --relaunch
27
+ agent-device prepare ios-runner --platform ios --timeout 240000
27
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.
28
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 iOS CI, run prepare ios-runner after boot/install and before replay/test. prepare ios-runner builds/reuses the XCTest runner and proves it can answer a lightweight command before the first snapshot pays that setup cost.
29
31
  Do not open artifact paths or invent package ids. If apps lookup misses the target and no URL/artifact is provided, ask or stop.
30
32
 
31
33
  Snapshots and refs:
@@ -69,7 +71,7 @@ Text entry:
69
71
  Android text entry is owned by agent-device: provider-native text injection when available, then chunk-safe ASCII shell input. Do not switch to raw adb, clipboard, or paste as an agent fallback. If non-ASCII is unsupported in the current backend, report the tool/device gap.
70
72
 
71
73
  Session ordering:
72
- Stateful commands against one --session must run serially. Do not run open/press/fill/type/scroll/back/alert/replay/batch/close commands in parallel against the same session.
74
+ Stateful commands within one session must run serially. Do not run open/press/fill/type/scroll/back/alert/replay/batch/close commands in parallel against the same session.
73
75
  It is fine to parallelize independent read-only collection or commands that use different sessions/devices.
74
76
 
75
77
  Read-only and waits:
@@ -241,7 +243,7 @@ Rules:
241
243
  Keep the profile window narrow; unrelated navigation makes render data noisy.
242
244
  Do not repeatedly raise broad profile slow limits such as --limit 50, --limit 200, or --limit 500. Drill into a specific @c ref with profile report unless you have a specific target that needs more rows.
243
245
  For network evidence, use agent-device network dump --include headers; headers is not a positional argument.
244
- For cross-platform validation with explicit device selectors, prefer isolated --state-dir and restart react-devtools between platforms.
246
+ For cross-platform validation with explicit device selectors, use separate sessions/devices and restart react-devtools between platforms.
245
247
  Remote Android and iOS bridge runs normally through agent-device react-devtools; the CLI keeps the needed local service tunnel alive until agent-device react-devtools stop or disconnect. Expo support depends on the SDK's bundled React Native runtime.
246
248
  Remote iOS apps attempt the legacy React DevTools websocket during JavaScript startup. If the app was already open before react-devtools start, run open <bundle-id> --platform ios --relaunch, then wait --connected.
247
249
 
@@ -297,11 +299,37 @@ React DevTools routing:
297
299
  If React DevTools cannot connect, report status and continue with logs, network, perf, screenshot, and trace evidence instead of blocking the whole flow.
298
300
 
299
301
  Slow-flow investigation:
300
- Keep one named session, start with session list, open, and snapshot -i.
302
+ Keep one session, open the app, and snapshot -i.
301
303
  Use help react-devtools for the narrow React profile window.
302
304
  Use help debugging for logs clear --restart, logs mark, network dump --include headers, perf --json, traces, and runtime failure evidence.
303
305
  For 15-20s async work, use wait with the exact expected text or selector instead of repeated snapshots.
304
- Report React render offenders separately from network/backend waits and device frame/CPU/memory findings.`},remote:{summary:"Remote config, tenant, lease, and remote host flow",body:`agent-device help remote
306
+ 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
307
+
308
+ Use this when the target is connected hardware instead of a simulator/emulator.
309
+ For simulator/emulator workflows, use help workflow.
310
+
311
+ Discovery:
312
+ agent-device devices --platform ios
313
+ agent-device devices --platform android
314
+ Use --device <name-or-udid> only when multiple devices are present.
315
+
316
+ iOS physical-device prerequisites:
317
+ Xcode and xcrun devicectl must be available from the selected Xcode.
318
+ The device must be paired/trusted, connected, unlocked when needed, and have Developer Mode enabled.
319
+ The AgentDeviceRunner XCTest host must be signed before commands can run on a physical device.
320
+ Start with Automatic Signing and only these env vars:
321
+ AGENT_DEVICE_IOS_TEAM_ID=ABCDE12345
322
+ AGENT_DEVICE_IOS_BUNDLE_ID=com.yourname.agentdevice.runner
323
+ Find team ids and Apple Development signing certificates with:
324
+ security find-identity -v -p codesigning
325
+ If Xcode cannot choose a profile, set AGENT_DEVICE_IOS_PROVISIONING_PROFILE to the profile name/specifier, not a file path.
326
+ AGENT_DEVICE_IOS_SIGNING_IDENTITY is optional; omit it unless xcodebuild asks for a specific identity.
327
+ The profile/team must allow AGENT_DEVICE_IOS_BUNDLE_ID and <id>.uitests.
328
+ First-run XCTest setup/build can take longer than normal commands; keep the device connected and use --debug to inspect signing/build diagnostics if setup times out.
329
+
330
+ Android physical-device prerequisites:
331
+ Enable USB debugging and confirm the device appears in agent-device devices --platform android.
332
+ Android does not need the iOS runner signing setup. For React Native/Expo Metro reachability, read help react-native.`},remote:{summary:"Remote config, tenant, lease, and remote host flow",body:`agent-device help remote
305
333
 
306
334
  Use remote config or the cloud connection profile when a profile owns daemon URL, auth, tenant, run, lease, device scope, and Metro hints. Do not restate those as individual flags unless overriding intentionally.
307
335
 
@@ -329,6 +357,7 @@ Rules:
329
357
  For self-contained scripts, pass the same --remote-config to every operational command, including disconnect; a preceding connect is optional but not required.
330
358
  For remote artifact installs, use install-from-source <url> or install-from-source --github-actions-artifact org/repo:artifact; do not download CI artifacts locally first.
331
359
  After connect, let the active remote connection supply runtime hints.
360
+ For connected phone/tablet setup and iOS signing prerequisites, read agent-device help physical-device.
332
361
  For remote Android and iOS bridge React DevTools, run agent-device react-devtools normally. The CLI opens the needed local service tunnel for the DevTools daemon and keeps it alive until agent-device react-devtools stop or disconnect.
333
362
  Use --debug when remote connection or transport errors need diagnostic ids and remote log hints.`},macos:{summary:"macOS desktop, frontmost-app, and menu bar surfaces",body:`agent-device help macos
334
363
 
@@ -367,7 +396,7 @@ Goal:
367
396
 
368
397
  Loop:
369
398
  1. Identify target app/platform; ask only if missing.
370
- 2. Create output dirs and open a named session. If auth or OTP is required, sign in or ask the user for the code.
399
+ 2. Create output dirs and open the app. If auth or OTP is required, sign in or ask the user for the code.
371
400
  3. Capture baseline snapshot -i and screenshot.
372
401
  4. Map top-level navigation, then exercise primary flows and edge states.
373
402
  5. For each issue, capture evidence and write the finding immediately, then continue.
@@ -384,17 +413,17 @@ Coverage:
384
413
 
385
414
  Evidence commands:
386
415
  mkdir -p ./dogfood-output/screenshots ./dogfood-output/videos ./dogfood-output/traces
387
- agent-device --session qa open <app> --platform ios
388
- agent-device --session qa snapshot -i
389
- agent-device --session qa screenshot ./dogfood-output/screenshots/initial.png
390
- agent-device --session qa screenshot ./dogfood-output/screenshots/issue-001.png --overlay-refs
391
- agent-device --session qa logs clear --restart
392
- agent-device --session qa logs mark "issue-001 repro"
393
- agent-device --session qa logs path
394
- agent-device --session qa record start ./dogfood-output/videos/issue-001.mp4
395
- agent-device --session qa record start ./dogfood-output/videos/benchmark.mp4 --hide-touches
396
- agent-device --session qa record stop
397
- agent-device --session qa close
416
+ agent-device open <app> --platform ios
417
+ agent-device snapshot -i
418
+ agent-device screenshot ./dogfood-output/screenshots/initial.png
419
+ agent-device screenshot ./dogfood-output/screenshots/issue-001.png --overlay-refs
420
+ agent-device logs clear --restart
421
+ agent-device logs mark "issue-001 repro"
422
+ agent-device logs path
423
+ agent-device record start ./dogfood-output/videos/issue-001.mp4
424
+ agent-device record start ./dogfood-output/videos/benchmark.mp4 --hide-touches
425
+ agent-device record stop
426
+ agent-device close
398
427
 
399
428
  Evidence rules:
400
429
  Interactive/behavioral issues need step screenshots and usually a repro video.
@@ -416,10 +445,10 @@ Rules:
416
445
  Never delete screenshots, videos, traces, or report artifacts during a session.
417
446
  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}
418
447
  (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}
419
- (none)`:[e,...t.map(e=>` ${e}`)].join("\n")}let x=new Set(["config","remoteConfig","help","version","batchSteps","githubActionsArtifact"]),R=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:R.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))}(),I=new Map(A.map(e=>[e.key,e]));function S(e){return I.get(e)}function U(e){return A.filter(t=>t.config.enabled&&t.supportsCommand(e))}function N(e,t){return S(e)?.supportsCommand(t)??!1}function D(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 E(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=F(u);continue}let f=u.startsWith("--"),h=u.startsWith("-")&&u.length>1;if(!f&&!h){a?s.push(u):a=F(u);continue}let[g,m]=f?O(u):[u,void 0],v=n(g);if(l=a,d=v,"react-devtools"===l&&(!d||!N(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||_(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&&_(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 ${L(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 ${L(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 C(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=>!N(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]&&(N(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 O(e){let t=e.indexOf("=");return -1===t?[e,void 0]:[e.slice(0,t),e.slice(t+1)]}function L(e){return e.replace(/^-+/,"")}function _(e){if(!e.startsWith("-")||"-"===e)return!1;let[t]=e.startsWith("--")?O(e):[e,void 0];return void 0!==n(t)}function M(){let o,a,n,r,i,l,c,d;return o=`agent-device <command> [args] [--json]
448
+ (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,c;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?O(u):[u,void 0],v=n(g);if(l=a,c=v,"react-devtools"===l&&(!c||!U(c.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 d("INVALID_ARGS",`Unknown flag: ${g}`)}let w=function(e,t,o,a){if(void 0!==e.setValue){if(void 0!==o)throw new d("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 d("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 d("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 d("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 d("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 d("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 C(e,t){let o=t?.strictFlags??!0,s=[...e.warnings],n=c({json:!1,help:!1,version:!1},t?.defaultFlags??{});c(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 d("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 d("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 d("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 O(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("--")?O(e):[e,void 0];return void 0!==n(t)}function T(){let o,a,n,r,i,l,d,c;return o=`agent-device <command> [args] [--json]
420
449
 
421
450
  CLI to control iOS and Android devices for AI agents.
422
- `,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}
451
+ `,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,d;return a=e,s=t,i=(r=(n=o).endsWith("?"))?n.slice(0,-1):n,d=(l=/^[a-z-]+(?:\|[a-z-]+)+$/i.test(i))||void 0!==s.usageOverride&&s.usageOverride.startsWith(`${a} ${i}`),r?l?`[${i}]`:d?i:`[${i}]`:d?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),d=y("Environment:",h),c=k("Examples:",g),`${o}
423
452
  ${a}
424
453
 
425
454
  ${n}
@@ -430,21 +459,21 @@ ${i}
430
459
 
431
460
  ${l}
432
461
 
433
- ${c}
434
-
435
462
  ${d}
436
- `}function T(e){return function(e){var t,a;let n,i=(n=m[e])?`${n.body}
463
+
464
+ ${c}
465
+ `}function F(e){return function(e){var t,a;let n,i=(n=m[e])?`${n.body}
437
466
 
438
467
  Related:
439
468
  agent-device help command list and global flags
440
469
  agent-device help <command> command-specific flags
441
470
  agent-device help workflow normal app automation loop
442
- `:null;if(i)return i;let l=o(e);if(!l)return null;let c=(t=e,(a=l).usageOverride?a.usageOverride:[t,...(a.positionalArgs??[]).map(v),...(a.allowedFlags??[]).flatMap(e=>{var t;return(t=e,r().filter(e=>e.key===t)).map(e=>e.usageLabel??e.names[0])}).map(e=>`[${e}]`)].join(" ")),d=w(new Set(l.allowedFlags??[])),p=w(s),u=[];return d.length>0&&u.push(b("Command flags:",d)),u.push(b("Global flags:",p)),`agent-device ${c}
471
+ `:null;if(i)return i;let l=o(e);if(!l)return null;let d=(t=e,(a=l).usageOverride?a.usageOverride:[t,...(a.positionalArgs??[]).map(v),...(a.allowedFlags??[]).flatMap(e=>{var t;return(t=e,r().filter(e=>e.key===t)).map(e=>e.usageLabel??e.names[0])}).map(e=>`[${e}]`)].join(" ")),c=w(new Set(l.allowedFlags??[])),p=w(s),u=[];return c.length>0&&u.push(b("Command flags:",c)),u.push(b("Global flags:",p)),`agent-device ${d}
443
472
 
444
473
  ${l.helpDescription}
445
474
 
446
475
  Usage:
447
- agent-device ${c}
476
+ agent-device ${d}
448
477
 
449
478
  ${u.join("\n\n")}
450
- `}(F(e))}function F(e){return"long-press"===e?"longpress":"metrics"===e?"perf":e}export{M as usage,C as finalizeParsedArgs,U as getConfigurableOptionSpecs,S as getOptionSpec,d as mergeDefinedFlags,D as parseOptionValueFromSource,E as parseRawArgs,T as usageForCommand};
479
+ `}(M(e))}function M(e){return"long-press"===e?"longpress":"metrics"===e?"perf":e}export{T as usage,C as finalizeParsedArgs,N as getConfigurableOptionSpecs,S as getOptionSpec,c as mergeDefinedFlags,E as parseOptionValueFromSource,D as parseRawArgs,F as usageForCommand};
@@ -82,6 +82,7 @@ declare type DaemonRequestMeta = {
82
82
  requestId?: string;
83
83
  debug?: boolean;
84
84
  cwd?: string;
85
+ sessionExplicit?: boolean;
85
86
  tenantId?: string;
86
87
  runId?: string;
87
88
  leaseId?: string;