agent-device 0.11.8 → 0.11.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -9
- package/dist/src/995.js +3 -0
- package/dist/src/bin.js +67 -66
- package/dist/src/daemon.js +46 -45
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +4 -3
- package/package.json +9 -2
- package/skills/agent-device/SKILL.md +1 -0
- package/skills/agent-device/references/remote-tenancy.md +11 -30
- package/dist/src/168.js +0 -3
package/dist/src/index.d.ts
CHANGED
|
@@ -477,6 +477,9 @@ declare type MetroPrepareOptions = {
|
|
|
477
477
|
publicBaseUrl: string;
|
|
478
478
|
proxyBaseUrl?: string;
|
|
479
479
|
bearerToken?: string;
|
|
480
|
+
launchUrl?: string;
|
|
481
|
+
companionProfileKey?: string;
|
|
482
|
+
companionConsumerKey?: string;
|
|
480
483
|
port?: number;
|
|
481
484
|
listenHost?: string;
|
|
482
485
|
statusHost?: string;
|
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
`;try{t.logPath&&n.appendFile(t.logPath,o,()=>{}),t.traceLogPath&&n.appendFile(t.traceLogPath,o,()=>{}),t.logPath||t.traceLogPath||process.stderr.write(o)}catch{}}async function g(e,t,r){let n=Date.now();try{let o=await t();return h({level:"info",phase:e,durationMs:Date.now()-n,data:r}),o}catch(t){throw h({level:"error",phase:e,durationMs:Date.now()-n,data:{...r??{},error:t instanceof Error?t.message:String(t)}}),t}}class w extends Error{code;details;cause;constructor(e,t,r,n){super(t),this.code=e,this.details=r,this.cause=n}}function y(e,t,r={}){let n=u(e,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"],encoding:r.binaryStdout?void 0:"utf8",input:r.stdin,timeout:v(r.timeoutMs)});if(n.error){let o=n.error.code;if("ETIMEDOUT"===o)throw new w("COMMAND_FAILED",`${e} timed out after ${v(r.timeoutMs)}ms`,{cmd:e,args:t,timeoutMs:v(r.timeoutMs)},n.error);if("ENOENT"===o)throw new w("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},n.error);throw new w("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},n.error)}let o=r.binaryStdout?Buffer.isBuffer(n.stdout)?n.stdout:Buffer.from(n.stdout??""):void 0,a=r.binaryStdout?"":"string"==typeof n.stdout?n.stdout:(n.stdout??"").toString(),i="string"==typeof n.stderr?n.stderr:(n.stderr??"").toString(),s=n.status??1;if(0!==s&&!r.allowFailure)throw new w("COMMAND_FAILED",`${e} exited with code ${s}`,{cmd:e,args:t,stdout:a,stderr:i,exitCode:s,processExitError:!0});return{stdout:a,stderr:i,exitCode:s,stdoutBuffer:o}}function I(e,t,r={}){let n=l(e,t,{cwd:r.cwd,env:r.env,stdio:r.stdio??"ignore",detached:!0});return n.unref(),n.pid??0}function v(e){if(!Number.isFinite(e))return;let t=Math.floor(e);if(!(t<=0))return t}function b(){let e=o.dirname(d(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=o.join(t,"package.json");if(n.existsSync(e))return t;t=o.dirname(t)}return e}let A=[/(^|[/\s"'=])dist\/src\/daemon\.js($|[\s"'])/,/(^|[/\s"'=])src\/daemon\.ts($|[\s"'])/];function E(e){if(!Number.isInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch(e){return"EPERM"===e.code}}function D(e,t){let r;if(!E(e))return!1;if(t){let r=function(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=y("ps",["-p",String(e),"-o","lstart="],{allowFailure:!0,timeoutMs:1e3});if(0!==t.exitCode)return null;let r=t.stdout.trim();return r.length>0?r:null}catch{return null}}(e);if(!r||r!==t)return!1}let n=function(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=y("ps",["-p",String(e),"-o","command="],{allowFailure:!0,timeoutMs:1e3});if(0!==t.exitCode)return null;let r=t.stdout.trim();return r.length>0?r:null}catch{return null}}(e);return!!n&&!!(r=n.toLowerCase().replaceAll("\\","/")).includes("agent-device")&&A.some(e=>e.test(r))}function S(e,t){try{return process.kill(e,t),!0}catch(t){let e=t.code;if("ESRCH"===e||"EPERM"===e)return!1;throw t}}async function M(e,t){if(!E(e))return!0;let r=Date.now();for(;Date.now()-r<t;)if(await new Promise(e=>setTimeout(e,50)),!E(e))return!0;return!E(e)}async function _(e,t){!D(e,t.expectedStartTime)||!S(e,"SIGTERM")||await M(e,t.termTimeoutMs)||S(e,"SIGKILL")&&await M(e,t.killTimeoutMs)}function P(e){return e?.HOME?.trim()||s.homedir()}function k(e,t={}){return"~"===e?P(t.env):e.startsWith("~/")?o.join(P(t.env),e.slice(2)):e}function T(e,t={}){let r=k(e,t);return o.isAbsolute(r)?r:o.resolve(t.cwd??process.cwd(),r)}function N(e){let t,r=(t=(e??"").trim())?T(t):o.join(k("~"),".agent-device");return{baseDir:r,infoPath:o.join(r,"daemon.json"),lockPath:o.join(r,"daemon.lock"),logPath:o.join(r,"daemon.log"),sessionsDir:o.join(r,"sessions")}}async function R(e){let{localPath:a,baseUrl:i,token:s}=e,u=n.statSync(a).isDirectory(),d=o.basename(a),c=new URL("upload",i.endsWith("/")?i:`${i}/`),p="https:"===c.protocol?r:t,m={"x-artifact-type":u?"app-bundle":"file","x-artifact-filename":d,"transfer-encoding":"chunked"};return s&&(m.authorization=`Bearer ${s}`,m["x-agent-device-token"]=s),new Promise((e,t)=>{let r=p.request({protocol:c.protocol,host:c.hostname,port:c.port,method:"POST",path:c.pathname+c.search,headers:m},r=>{let n="";r.setEncoding("utf8"),r.on("data",e=>{n+=e}),r.on("end",()=>{clearTimeout(i);try{let r=JSON.parse(n);if(!r.ok||!r.uploadId)return void t(new w("COMMAND_FAILED",`Upload failed: ${n}`));e(r.uploadId)}catch{t(new w("COMMAND_FAILED",`Invalid upload response: ${n}`))}})}),i=setTimeout(()=>{r.destroy(),t(new w("COMMAND_FAILED","Artifact upload timed out",{timeoutMs:3e5,hint:"The upload to the remote daemon exceeded the 5-minute timeout."}))},3e5);if(r.on("error",e=>{clearTimeout(i),t(new w("COMMAND_FAILED","Failed to upload artifact to remote daemon",{hint:"Verify the remote daemon is reachable and supports artifact uploads."},e))}),u){let e=l("tar",["cf","-","-C",o.dirname(a),o.basename(a)],{stdio:["ignore","pipe","pipe"]});e.stdout.pipe(r),e.on("error",e=>{r.destroy(),t(new w("COMMAND_FAILED","Failed to create tar archive for app bundle",{},e))}),e.on("close",e=>{0!==e&&(r.destroy(),t(new w("COMMAND_FAILED",`tar failed with exit code ${e}`)))})}else{let e=n.createReadStream(a);e.pipe(r),e.on("error",e=>{r.destroy(),t(new w("COMMAND_FAILED","Failed to read local artifact",{},e))})}})}let U=ep(),O=function(e=process.env.AGENT_DEVICE_DAEMON_STARTUP_TIMEOUT_MS){if(!e)return 15e3;let t=Number(e);return Number.isFinite(t)?Math.max(1e3,Math.floor(t)):15e3}(),x=function(e=process.env.AGENT_DEVICE_DAEMON_STARTUP_ATTEMPTS){if(!e)return 2;let t=Number(e);return Number.isFinite(t)?Math.min(5,Math.max(1,Math.floor(t))):2}(),C=["xcodebuild .*AgentDeviceRunnerUITests/RunnerTests/testCommand","xcodebuild .*AgentDeviceRunner\\.env\\.session-","xcodebuild build-for-testing .*ios-runner/AgentDeviceRunner/AgentDeviceRunner\\.xcodeproj"];async function L(e){let t=e.meta?.requestId??f(),r=!!(e.meta?.debug||e.flags?.verbose),n=function(e){let t,r,n=e.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR,o=function(e){let t;if(e){try{t=new URL(e)}catch(t){throw new w("INVALID_ARGS","Invalid daemon base URL",{daemonBaseUrl:e},t instanceof Error?t:void 0)}if("http:"!==t.protocol&&"https:"!==t.protocol)throw new w("INVALID_ARGS","Daemon base URL must use http or https",{daemonBaseUrl:e});return t.toString().replace(/\/+$/,"")}}(e.flags?.daemonBaseUrl??process.env.AGENT_DEVICE_DAEMON_BASE_URL),a=e.flags?.daemonAuthToken??process.env.AGENT_DEVICE_DAEMON_AUTH_TOKEN,i=e.flags?.daemonTransport??process.env.AGENT_DEVICE_DAEMON_TRANSPORT,s="auto"===(t=(i??"").trim().toLowerCase())?"auto":"socket"===t?"socket":"http"===t?"http":"auto";if(o&&"socket"===s)throw new w("INVALID_ARGS","Remote daemon base URL only supports HTTP transport. Remove --daemon-transport socket.",{daemonBaseUrl:o});let l="http"===(r=(e.flags?.daemonServerMode??process.env.AGENT_DEVICE_DAEMON_SERVER_MODE??("dual"===i?"dual":void 0)??"").trim().toLowerCase())?"http":"dual"===r?"dual":"socket";return{paths:N(n),transportPreference:s,serverMode:l,remoteBaseUrl:o,remoteAuthToken:a}}(e),o=function(e,t=process.env.AGENT_DEVICE_DAEMON_TIMEOUT_MS){if("test"!==e)return ep(t)}(e.command),a=await g("daemon_startup",async()=>await B(n),{requestId:t,session:e.session}),i=await $(e,a),s={...e,positionals:i.positionals,flags:i.flags,token:a.token,meta:{...e.meta??{},requestId:t,debug:r,cwd:e.meta?.cwd,tenantId:e.meta?.tenantId??e.flags?.tenant,runId:e.meta?.runId??e.flags?.runId,leaseId:e.meta?.leaseId??e.flags?.leaseId,sessionIsolation:e.meta?.sessionIsolation??e.flags?.sessionIsolation,lockPolicy:e.meta?.lockPolicy,lockPlatform:e.meta?.lockPlatform,...i.uploadedArtifactId?{uploadedArtifactId:i.uploadedArtifactId}:{},...i.clientArtifactPaths?{clientArtifactPaths:i.clientArtifactPaths}:{},...i.installSource?{installSource:i.installSource}:{}}};return h({level:"info",phase:"daemon_request_prepare",data:{requestId:t,command:e.command,session:e.session}}),await g("daemon_request",async()=>await et(a,s,n.transportPreference,o),{requestId:t,command:e.command})}async function $(e,t){let r,a=[...e.positionals??[]],i=e.flags?{...e.flags}:void 0,s=e.meta?.installSource,l={};if(el(t)){let n=function(e,t){if("screenshot"===e.command){let r=j(e,"path",".png");return t[0]?{field:"path",localPath:r,positionalIndex:0,positionalPath:q("screenshot",".png")}:{field:"path",localPath:r,positionalIndex:0,flagPath:q("screenshot",".png")}}if("record"===e.command&&"start"===(t[0]??"").toLowerCase()){let t=j(e,"outPath",".mp4",1);return{field:"outPath",localPath:t,positionalIndex:1,positionalPath:q("recording",o.extname(t)||".mp4")}}return null}(e,a);n&&(void 0!==n.positionalPath&&(a[n.positionalIndex]=n.positionalPath),void 0!==n.flagPath&&((i??={}).out=n.flagPath),l[n.field]=n.localPath);let u=await F(e,t);u&&(s=u.installSource,r=u.uploadedArtifactId??r)}if(!el(t)||"install"!==e.command&&"reinstall"!==e.command||a.length<2)return{positionals:a,flags:i,installSource:s,uploadedArtifactId:r,...Object.keys(l).length>0?{clientArtifactPaths:l}:{}};let u=a[1];if(u.startsWith("remote:"))return a[1]=u.slice(7),{positionals:a,flags:i,...Object.keys(l).length>0?{clientArtifactPaths:l}:{}};let d=o.isAbsolute(u)?u:o.resolve(e.meta?.cwd??process.cwd(),u);return n.existsSync(d)?{positionals:a,flags:i,installSource:s,uploadedArtifactId:r=await R({localPath:d,baseUrl:t.baseUrl,token:t.token}),...Object.keys(l).length>0?{clientArtifactPaths:l}:{}}:{positionals:a,flags:i,...Object.keys(l).length>0?{clientArtifactPaths:l}:{}}}async function F(e,t){let r=e.meta?.installSource;if("install_source"!==e.command||!r||"path"!==r.kind)return null;let a=r.path.trim();if(!a)return{installSource:r};if(a.startsWith("remote:"))return{installSource:{...r,path:a.slice(7)}};let i=o.isAbsolute(a)?a:o.resolve(e.meta?.cwd??process.cwd(),a);if(!n.existsSync(i))return{installSource:{...r,path:i}};let s=await R({localPath:i,baseUrl:t.baseUrl,token:t.token});return{installSource:{...r,path:i},uploadedArtifactId:s}}function j(e,t,r,n=0){let a=e.positionals?.[n]??e.flags?.out,i=`${"path"===t?"screenshot":"recording"}-${Date.now()}${r}`,s=a&&a.trim().length>0?a:i;return o.isAbsolute(s)?s:o.resolve(e.meta?.cwd??process.cwd(),s)}function q(e,t){let r=t.startsWith(".")?t:`.${t}`;return o.posix.join("/tmp",`agent-device-${e}-${Date.now()}-${Math.random().toString(36).slice(2,8)}${r}`)}async function B(e){let t;if(e.remoteBaseUrl){let t={transport:"http",token:e.remoteAuthToken??"",pid:0,baseUrl:e.remoteBaseUrl};if(await Q(t,"http"))return t;throw new w("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."})}let r=J(e.paths.infoPath),a=function(){try{let e=b();return JSON.parse(n.readFileSync(o.join(e,"package.json"),"utf8")).version??"0.0.0"}catch{return"0.0.0"}}(),i=function(e,t=b()){try{let r=n.statSync(e),a=o.relative(t,e)||e;return`${a}:${r.size}:${Math.trunc(r.mtimeMs)}`}catch{return"unknown"}}((t=ee()).useSrc?t.srcPath:t.distPath,t.root),s=!!r&&await Q(r,e.transportPreference);if(r&&r.version===a&&r.codeSignature===i&&s)return r;r&&(r.version!==a||r.codeSignature!==i||!s)&&(await H(r),Y(e.paths.infoPath)),function(e){let t=K(e);if(!t.hasLock||t.hasInfo)return;let r=W(e.lockPath);if(!r)return Y(e.lockPath);D(r.pid,r.processStartTime)||Y(e.lockPath)}(e.paths);let l=0;for(let t=1;t<=x;t+=1){await Z(e);let r=await G(O,e);if(r)return r;if(await z(e.paths)){l+=1;continue}let n=K(e.paths);if(!(t<x))break;if(!n.hasInfo&&!n.hasLock){await V(150);continue}}let u=K(e.paths);throw new w("COMMAND_FAILED","Failed to start daemon",{kind:"daemon_startup_failed",infoPath:e.paths.infoPath,lockPath:e.paths.lockPath,startupTimeoutMs:O,startupAttempts:x,lockRecoveryCount:l,metadataState:u,hint:function(e,t=N(process.env.AGENT_DEVICE_STATE_DIR)){return e.hasLock&&!e.hasInfo?`Detected ${t.lockPath} without ${t.infoPath}. If no agent-device daemon process is running, delete ${t.lockPath} and retry.`:e.hasLock&&e.hasInfo?`Daemon metadata may be stale. If no agent-device daemon process is running, delete ${t.infoPath} and ${t.lockPath}, then retry.`:`Daemon metadata is missing or stale. Delete ${t.infoPath} if present and retry.`}(u,e.paths)})}async function G(e,t){let r=Date.now();for(;Date.now()-r<e;){let e=J(t.paths.infoPath);if(e&&await Q(e,t.transportPreference))return e;await new Promise(e=>setTimeout(e,100))}return null}async function V(e){await new Promise(t=>setTimeout(t,e))}async function z(e){let t=K(e);if(!t.hasLock||t.hasInfo)return!1;let r=W(e.lockPath);return r&&D(r.pid,r.processStartTime)&&await _(r.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:r.processStartTime}),Y(e.lockPath),!0}async function H(e){await _(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}function J(e){let t=X(e);if(!t||"object"!=typeof t)return null;let r="string"==typeof t.token&&t.token.length>0?t.token:null;if(!r)return null;let n=Number.isInteger(t.port)&&Number(t.port)>0,o=Number.isInteger(t.httpPort)&&Number(t.httpPort)>0;if(!n&&!o)return null;let a=t.transport,i="string"==typeof t.version?t.version:void 0,s="string"==typeof t.codeSignature?t.codeSignature:void 0,l="string"==typeof t.processStartTime?t.processStartTime:void 0,u=Number.isInteger(t.pid)&&Number(t.pid)>0;return{token:r,port:n?Number(t.port):void 0,httpPort:o?Number(t.httpPort):void 0,transport:"socket"===a||"http"===a||"dual"===a?a:void 0,pid:u?Number(t.pid):0,version:i,codeSignature:s,processStartTime:l}}function W(e){let t=X(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}function K(e){return{hasInfo:n.existsSync(e.infoPath),hasLock:n.existsSync(e.lockPath)}}function X(e){if(!n.existsSync(e))return null;try{return JSON.parse(n.readFileSync(e,"utf8"))}catch{return null}}function Y(e){try{n.existsSync(e)&&n.unlinkSync(e)}catch{}}async function Q(n,o){var a;return"http"===er(n,o)?await function(e){let n=e.baseUrl?eu(e.baseUrl,"health"):e.httpPort?`http://127.0.0.1:${e.httpPort}/health`:null;if(!n)return Promise.resolve(!1);let o=new URL(n),a="https:"===o.protocol?r:t,i=e.baseUrl?3e3:500;return new Promise(e=>{let t=a.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()})}(n):await ((a=n.port)?new Promise(t=>{let r=e.createConnection({host:"127.0.0.1",port:a},()=>{r.destroy(),t(!0)});r.on("error",()=>{t(!1)})}):Promise.resolve(!1))}async function Z(e){let t=ee(),r=t.useSrc?["--experimental-strip-types",t.srcPath]:[t.distPath],n={...process.env,AGENT_DEVICE_STATE_DIR:e.paths.baseDir,AGENT_DEVICE_DAEMON_SERVER_MODE:e.serverMode};I(process.execPath,r,{env:n})}function ee(){let e=b(),t=o.join(e,"dist","src","daemon.js"),r=o.join(e,"src","daemon.ts"),a=n.existsSync(t),i=n.existsSync(r);if(!a&&!i)throw new w("COMMAND_FAILED","Daemon entry not found",{distPath:t,srcPath:r});return{root:e,distPath:t,srcPath:r,useSrc:process.execArgv.includes("--experimental-strip-types")?i:!a&&i}}async function et(e,t,r,n){return"http"===er(e,r)?await es(e,t,n):await ei(e,t,n)}function er(e,t){if(e.baseUrl){if("socket"===t)throw new w("COMMAND_FAILED","Remote daemon endpoint only supports HTTP transport",{daemonBaseUrl:e.baseUrl});return"http"}if("http"===t||"socket"===t){var r=e,n=t;if(en(r,n))return n;throw new w("COMMAND_FAILED","http"===n?"Daemon HTTP endpoint is unavailable":"Daemon socket endpoint is unavailable")}let o=("socket"===e.transport||"dual"===e.transport?["socket","http"]:["http","socket"]).find(t=>en(e,t));if(o)return o;throw new w("COMMAND_FAILED","Daemon metadata has no reachable transport")}function en(e,t){return"http"===t?!!e.httpPort:!!e.port}function eo(e,t,r,n,o,a){let i=o?{terminated:0}:function(){let e=0;try{for(let t of C){let r=y("pkill",["-f",t],{allowFailure:!0});0===r.exitCode&&(e+=1)}return{terminated:e}}catch(t){return{terminated:e,error:t instanceof Error?t.message:String(t)}}}(),s=o?{forcedKill:!1}:function(e,t){let r=!1;try{D(e.pid,e.processStartTime)&&(process.kill(e.pid,"SIGKILL"),r=!0)}catch{_(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}finally{Y(t.infoPath),Y(t.lockPath)}return{forcedKill:r}}(e,t);return h({level:"error",phase:"daemon_request_timeout",data:{timeoutMs:a,requestId:r,command:n,timedOutRunnerPidsTerminated:i.terminated,timedOutRunnerCleanupError:i.error,daemonPidReset:o?void 0:e.pid,daemonPidForceKilled:o?void 0:s.forcedKill,daemonBaseUrl:e.baseUrl}}),new w("COMMAND_FAILED","Daemon request timed out",{timeoutMs:a,requestId:r,hint:o?"Retry with --debug and verify the remote daemon URL, auth token, and remote host logs.":"Retry with --debug and check daemon diagnostics logs. Timed-out iOS runner xcodebuild processes were terminated when detected."})}function ea(e,t,r){return h({level:"error",phase:"daemon_request_socket_error",data:{requestId:t,message:e instanceof Error?e.message:String(e)}}),new w("COMMAND_FAILED","Failed to communicate with daemon",{requestId:t,hint:r?"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 ei(t,r,n){let o=t.port;if(!o)throw new w("COMMAND_FAILED","Daemon socket endpoint is unavailable");return new Promise((a,i)=>{let s=e.createConnection({host:"127.0.0.1",port:o},()=>{s.write(`${JSON.stringify(r)}
|
|
3
|
-
`)}),l=N(r.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),u="number"==typeof n?setTimeout(()=>{s.destroy(),i(eo(t,l,r.meta?.requestId,r.command,!1,n))},n):void 0,d="";s.setEncoding("utf8"),s.on("data",e=>{let t=(d+=e).indexOf("\n");if(-1===t)return;let n=d.slice(0,t).trim();if(n)try{let e=JSON.parse(n);s.end(),u&&clearTimeout(u),a(e)}catch(e){u&&clearTimeout(u),i(new w("COMMAND_FAILED","Invalid daemon response",{requestId:r.meta?.requestId,line:n},e instanceof Error?e:void 0))}}),s.on("error",e=>{u&&clearTimeout(u),i(ea(e,r.meta?.requestId,!1))})})}async function es(e,n,o){let a=e.baseUrl?new URL(eu(e.baseUrl,"rpc")):e.httpPort?new URL(`http://127.0.0.1:${e.httpPort}/rpc`):null;if(!a)throw new w("COMMAND_FAILED","Daemon HTTP endpoint is unavailable");let i=JSON.stringify({jsonrpc:"2.0",id:n.meta?.requestId??f(),method:"agent_device.command",params:n}),s={"content-type":"application/json","content-length":Buffer.byteLength(i)};return e.baseUrl&&e.token&&(s.authorization=`Bearer ${e.token}`,s["x-agent-device-token"]=e.token),await new Promise((l,u)=>{let d=N(n.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),c=("https:"===a.protocol?r:t).request({protocol:a.protocol,host:a.hostname,port:a.port,method:"POST",path:a.pathname+a.search,headers:s},t=>{let r="";t.setEncoding("utf8"),t.on("data",e=>{r+=e}),t.on("end",()=>{m&&clearTimeout(m);try{let t=JSON.parse(r);if(t.error){let e=t.error.data??{};u(new w(String(e.code??"COMMAND_FAILED"),String(e.message??t.error.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:n.meta?.requestId}));return}if(!t.result||"object"!=typeof t.result)return void u(new w("COMMAND_FAILED","Invalid daemon RPC response",{requestId:n.meta?.requestId}));if(e.baseUrl&&t.result.ok)return void ed(e,n,t.result).then(l).catch(u);l(t.result)}catch(e){m&&clearTimeout(m),u(new w("COMMAND_FAILED","Invalid daemon response",{requestId:n.meta?.requestId,line:r},e instanceof Error?e:void 0))}})}),p=el(e),m="number"==typeof o?setTimeout(()=>{c.destroy(),u(eo(e,d,n.meta?.requestId,n.command,p,o))},o):void 0;c.on("error",e=>{m&&clearTimeout(m),u(ea(e,n.meta?.requestId,p))}),c.write(i),c.end()})}function el(e){return"string"==typeof e.baseUrl&&e.baseUrl.length>0}function eu(e,t){return new URL(t,e.endsWith("/")?e:`${e}/`).toString()}async function ed(e,t,r){let n=Array.isArray(r.data?.artifacts)?r.data.artifacts:[];if(0===n.length||!e.baseUrl)return r;let a=r.data?{...r.data}:{},i=[];for(let r of n){if(!r||"object"!=typeof r||"string"!=typeof r.artifactId){i.push(r);continue}let n=function(e,t){if(e.localPath&&e.localPath.trim().length>0)return e.localPath;let r=e.fileName?.trim()||`${e.field}-${Date.now()}`;return o.resolve(t.meta?.cwd??process.cwd(),r)}(r,t);await ec({baseUrl:e.baseUrl,token:e.token,artifactId:r.artifactId,destinationPath:n,requestId:t.meta?.requestId}),a[r.field]=n,i.push({...r,localPath:n})}return a.artifacts=i,{ok:!0,data:a}}async function ec(e){var a,i;let s,l=new URL((a=e.baseUrl,i=e.artifactId,s=a.endsWith("/")?a:`${a}/`,new URL(`upload/${encodeURIComponent(i)}`,s).toString())),u="https:"===l.protocol?r:t;await n.promises.mkdir(o.dirname(e.destinationPath),{recursive:!0}),await new Promise((t,r)=>{let o=!1,a=e.timeoutMs??U,i=a=>{if(!o){if(o=!0,clearTimeout(d),a)return void n.promises.rm(e.destinationPath,{force:!0}).finally(()=>r(a));t()}},s=u.request({protocol:l.protocol,host:l.hostname,port:l.port,method:"GET",path:l.pathname+l.search,headers:e.token?{authorization:`Bearer ${e.token}`,"x-agent-device-token":e.token}:void 0},t=>{if((t.statusCode??500)>=400){let r="";t.setEncoding("utf8"),t.on("data",e=>{r+=e}),t.on("end",()=>{i(new w("COMMAND_FAILED","Failed to download remote artifact",{artifactId:e.artifactId,statusCode:t.statusCode,requestId:e.requestId,body:r}))});return}let r=n.createWriteStream(e.destinationPath);r.on("error",e=>{i(e instanceof Error?e:Error(String(e)))}),t.on("error",e=>{i(e instanceof Error?e:Error(String(e)))}),t.on("aborted",()=>{i(new w("COMMAND_FAILED","Remote artifact download was interrupted",{artifactId:e.artifactId,requestId:e.requestId}))}),r.on("finish",()=>{r.close(()=>i())}),t.pipe(r)}),d=setTimeout(()=>{s.destroy(new w("COMMAND_FAILED","Remote artifact download timed out",{artifactId:e.artifactId,requestId:e.requestId,timeoutMs:a}))},a);s.on("error",t=>{t instanceof w?i(t):i(new w("COMMAND_FAILED","Failed to download remote artifact",{artifactId:e.artifactId,requestId:e.requestId,timeoutMs:a},t instanceof Error?t:void 0))}),s.end()})}function ep(e=process.env.AGENT_DEVICE_DAEMON_TIMEOUT_MS){if(!e)return 9e4;let t=Number(e);return Number.isFinite(t)?Math.max(1e3,Math.floor(t)):9e4}function em(e){return e.replace(/\/+$/,"")}function ef(e){return"string"==typeof e&&e.trim()?em(e.trim()):""}function eh(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function eg(e,t,r){return T(e,{env:t,cwd:r})}function ew(e){try{return n.accessSync(e,n.constants.F_OK),!0}catch{return!1}}function ey(e,t,r){if(null==e||""===e)return t;let n=Number.parseInt(String(e),10);return Number.isInteger(n)?Math.max(n,r):t}function eI(e,t){let r;return{platform:t,bundleUrl:((r=new URL(`${em(e)}/index.bundle`)).searchParams.set("platform",t),r.searchParams.set("dev","true"),r.searchParams.set("minify","false"),r.toString())}}function ev(e,t){return{platform:t,metroHost:eh(e?.metro_host),metroPort:e?.metro_port,bundleUrl:eh(e?.metro_bundle_url),launchUrl:eh(e?.launch_url)}}async function eb(e){await new Promise(t=>setTimeout(t,e))}async function eA(e,t,r={}){try{let n=await fetch(e,{headers:r,signal:AbortSignal.timeout(t)});return{ok:n.ok,status:n.status,body:await n.text()}}catch(r){if(r instanceof Error&&"TimeoutError"===r.name)throw Error(`Timed out fetching ${e} after ${t}ms`);throw r}}async function eE(e,t){try{let r=await eA(e,t);return r.ok&&r.body.includes("packager-status:running")}catch{return!1}}async function eD(e){var t,r,n;let o;try{o=await fetch(`${e.baseUrl}/api/metro/bridge`,{method:"POST",headers:(t=e.baseUrl,r=e.bearerToken,{Authorization:`Bearer ${r}`,"Content-Type":"application/json",...t.includes("ngrok")?{"ngrok-skip-browser-warning":"1"}:{}}),body:JSON.stringify({ios_runtime:e.runtime,timeout_ms:e.timeoutMs}),signal:AbortSignal.timeout(e.timeoutMs)})}catch(t){if(t instanceof Error&&"TimeoutError"===t.name)throw Error(`/api/metro/bridge timed out after ${e.timeoutMs}ms calling ${e.baseUrl}/api/metro/bridge`);throw t}let a=await o.text(),i=a?JSON.parse(a):{};if(!o.ok)throw Error(`/api/metro/bridge failed (${o.status}): ${JSON.stringify(i)}`);return{enabled:(n=i.data??i).enabled,baseUrl:n.base_url,statusUrl:n.status_url,bundleUrl:n.bundle_url,iosRuntime:ev(n.ios_runtime,"ios"),androidRuntime:ev(n.android_runtime,"android"),upstream:{bundleUrl:n.upstream.bundle_url,host:n.upstream.host,port:n.upstream.port,statusUrl:n.upstream.status_url},probe:{reachable:n.probe.reachable,statusCode:n.probe.status_code,latencyMs:n.probe.latency_ms,detail:n.probe.detail}}}async function eS(e,t,r){let n=Date.now()+t;for(;Date.now()<n;){let t=Math.min(r,Math.max(n-Date.now(),1));if(await eE(e,t))return!0;let o=Math.min(500,Math.max(n-Date.now(),0));o>0&&await eb(o)}return!1}async function eM(e={}){let t=e.env??process.env,r=process.cwd(),a=eg(e.projectRoot??r,t,r),i=function(e,t){if("auto"!==t)return t;let r=function(e){let t=o.join(e,"package.json");if(!ew(t))throw new w("INVALID_ARGS",`package.json not found at ${t}`);return JSON.parse(n.readFileSync(t,"utf8"))}(e);return"string"==typeof({...r.dependencies??{},...r.devDependencies??{}}).expo?"expo":"react-native"}(a,e.kind??"auto"),s=function(e){if(null==e||""===e)return 8081;let t=Number.parseInt(String(e),10);if(!Number.isInteger(t)||t<1||t>65535)throw new w("INVALID_ARGS",`Invalid Metro port: ${String(e)}. Use 1-65535.`);return t}(e.metroPort??8081),l=eh(e.listenHost)??"0.0.0.0",u=eh(e.statusHost)??"127.0.0.1",d=ef(e.publicBaseUrl),c=ey(e.startupTimeoutMs,18e4,3e4),p=ey(e.probeTimeoutMs,1e4,1e3),m=e.reuseExisting??!0,f=e.installDependenciesIfNeeded??!0,h=e.runtimeFilePath?eg(e.runtimeFilePath,t,r):null,g=eg(e.logPath??o.join(a,".agent-device","metro.log"),t,r);if(!d)throw new w("INVALID_ARGS","metro prepare requires --public-base-url <url>.");let{proxyEnabled:v,proxyBaseUrl:b,proxyBearerToken:A}=function(e,t){if(e&&!t)throw new w("INVALID_ARGS","metro prepare requires proxy auth when --proxy-base-url is provided. Pass --bearer-token or set AGENT_DEVICE_PROXY_TOKEN.");if(!e&&t)throw new w("INVALID_ARGS","metro prepare requires --proxy-base-url when proxy auth is provided.");return{proxyEnabled:!!(e&&t),proxyBaseUrl:e,proxyBearerToken:t}}(ef(e.proxyBaseUrl),eh(e.proxyBearerToken)??""),E=f?function(e,t){if(function(e){try{return n.statSync(e).isDirectory()}catch{return!1}}(o.join(e,"node_modules")))return{installed:!1};let r=ew(o.join(e,"pnpm-lock.yaml"))?{command:"pnpm",installArgs:["install"]}:ew(o.join(e,"yarn.lock"))?{command:"yarn",installArgs:["install"]}:{command:"npm",installArgs:["install"]};return y(r.command,r.installArgs,{cwd:e,env:t}),{installed:!0,packageManager:r.command}}(a,t):{installed:!1},D=`http://${u}:${s}/status`,S=!1,M=!1,_=0;if(m&&await eE(D,p))M=!0;else if(S=!0,_=function(e,t,r,a,i,s){let l="expo"===t?{command:"npx",installArgs:["expo","start","--host","lan","--port",String(r)]}:{command:"npx",installArgs:["react-native","start","--host",a,"--port",String(r)]};n.mkdirSync(o.dirname(i),{recursive:!0});let u=n.openSync(i,"a"),d=0;try{d=I(l.command,l.installArgs,{cwd:e,env:s,stdio:["ignore",u,u]})}finally{n.closeSync(u)}if(!Number.isInteger(d)||d<=0)throw Error("Failed to start Metro. Expected a detached child PID.");return{pid:d}}(a,i,s,l,g,t).pid,!await eS(D,c,p))throw Error(`Metro did not become ready at ${D} within ${c}ms. Check ${g}.`);let P=eI(d,"ios"),k=eI(d,"android"),T=null,N=null;if(v)try{T=await eD({baseUrl:b,bearerToken:A,runtime:{metro_bundle_url:P.bundleUrl},timeoutMs:p})}catch(e){N=e instanceof Error?e.message:String(e)}if(v&&(!T||!1===T.probe.reachable)){var R,U;let e;throw Error((R=N,U=T,e=[`Metro bridge is required for this run but could not be configured via ${b}/api/metro/bridge.`],R&&e.push(`bridgeError=${R}`),U?.probe.reachable===!1&&e.push(`bridgeProbe=${U.probe.detail||`unreachable (status ${U.probe.statusCode||0})`}`),e.join(" ")))}let O=T?.iosRuntime??P,x=T?.androidRuntime??k,C={projectRoot:a,kind:i,dependenciesInstalled:E.installed,packageManager:E.packageManager??null,started:S,reused:M,pid:_,logPath:g,statusUrl:D,runtimeFilePath:h,iosRuntime:O,androidRuntime:x,bridge:T};return h&&(n.mkdirSync(o.dirname(h),{recursive:!0}),n.writeFileSync(h,JSON.stringify(C,null,2))),C}function e_(e){let t=e.appId??e.bundleId??e.packageName;return{session:e.session,appId:t,appBundleId:e.bundleId,package:e.packageName}}function eP(e,t,r){return{deviceId:t,deviceName:r,..."android"===e?{serial:t}:"ios"===e?{udid:t}:{}}}function ek(e,t,r,n){let o=r(e[t]);if(void 0===o)throw new w("COMMAND_FAILED",n,{response:e});return o}function eT(e,t){return ek(e,t,eC,`Daemon response is missing "${t}".`)}function eN(e,t){return eC(e[t])}function eR(e,t){var r;let n;return r=eC,null===(n=e[t])?null:r(n)}function eU(e,t){return ek(e,t,e$,`Daemon response has invalid "${t}".`)}function eO(e,t){return function(e){return"tv"===e||"mobile"===e||"desktop"===e?e:void 0}(e[t])??"mobile"}function ex(e,t){let r=e[t];if(!eq(r))return;let n="number"==typeof r.x?r.x:void 0,o="number"==typeof r.y?r.y:void 0,a="number"==typeof r.width?r.width:void 0,i="number"==typeof r.height?r.height:void 0;if(void 0!==n&&void 0!==o&&void 0!==a&&void 0!==i)return{x:n,y:o,width:a,height:i}}function eC(e){return"string"==typeof e&&e.length>0?e:void 0}function eL(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function e$(e){return"ios"===e||"macos"===e||"android"===e?e:void 0}function eF(e){return"simulator"===e||"emulator"===e||"device"===e?e:void 0}function ej(e){if(!eq(e))throw new w("COMMAND_FAILED","Daemon returned an unexpected response shape.",{value:e});return e}function eq(e){return"object"==typeof e&&null!==e}function eB(e){let t={};for(let[r,n]of Object.entries(e))void 0!==n&&(t[r]=n);return t}function eG(e,t){let r=eN(e,"bundleId"),n=eN(e,"package");return{app:eT(e,"app"),appPath:eT(e,"appPath"),platform:eU(e,"platform"),appId:r??n,bundleId:r,package:n,identifiers:e_({session:t,bundleId:r,packageName:n})}}function eV(e){let t=ej(e),r=eU(t,"platform"),n=eT(t,"id"),o=eT(t,"name");return{platform:r,target:eO(t,"target"),kind:ek(t,"kind",eF,'Daemon response has invalid "kind".'),id:n,name:o,booted:"boolean"==typeof t.booted?t.booted:void 0,identifiers:eP(r,n,o),ios:"ios"===r?{udid:n}:void 0,android:"android"===r?{serial:n}:void 0}}function ez(e){let t=ej(e),r=eU(t,"platform"),n=eT(t,"id"),o=eT(t,"name"),a=eO(t,"target"),i=eT(t,"device"),s={session:o,...eP(r,n,i)};return{name:o,createdAt:ek(t,"createdAt",eL,'Daemon response is missing numeric "createdAt".'),device:{platform:r,target:a,id:n,name:i,identifiers:s,ios:"ios"===r?{udid:n,simulatorSetPath:eR(t,"ios_simulator_device_set")}:void 0,android:"android"===r?{serial:n}:void 0},identifiers:s}}function eH(e,t){return t??e??"default"}function eJ(e={},t={}){let r=t.transport??L,n=async(t,n=[],o={})=>{let a={...e,...o},i=await r({session:eH(e.session,o.session),command:t,positionals:n,flags:eB({stateDir:a.stateDir,daemonBaseUrl:a.daemonBaseUrl,daemonAuthToken:a.daemonAuthToken,daemonTransport:a.daemonTransport,daemonServerMode:a.daemonServerMode,tenant:a.tenant,sessionIsolation:a.sessionIsolation,runId:a.runId,leaseId:a.leaseId,platform:a.platform,target:a.target,device:a.device,udid:a.udid,serial:a.serial,iosSimulatorDeviceSet:a.iosSimulatorDeviceSet,androidDeviceAllowlist:a.androidDeviceAllowlist,runtime:a.simulatorRuntimeId,boot:a.boot,reuseExisting:a.reuseExisting,surface:a.surface,activity:a.activity,relaunch:a.relaunch,shutdown:a.shutdown,saveScript:a.saveScript,noRecord:a.noRecord,metroHost:a.metroHost,metroPort:a.metroPort,bundleUrl:a.bundleUrl,launchUrl:a.launchUrl,snapshotInteractiveOnly:a.interactiveOnly,snapshotCompact:a.compact,snapshotDepth:a.depth,snapshotScope:a.scope,snapshotRaw:a.raw,overlayRefs:a.overlayRefs,verbose:a.debug}),runtime:a.runtime,meta:eB({requestId:a.requestId,cwd:a.cwd,debug:a.debug,lockPolicy:a.lockPolicy,lockPlatform:a.lockPlatform,tenantId:a.tenant,runId:a.runId,leaseId:a.leaseId,sessionIsolation:a.sessionIsolation,installSource:a.installSource,retainMaterializedPaths:a.retainMaterializedPaths,materializedPathRetentionMs:a.materializedPathRetentionMs,materializationId:a.materializationId})});if(!i.ok)throw new w(i.error.code,i.error.message,{...i.error.details??{},hint:i.error.hint,diagnosticId:i.error.diagnosticId,logPath:i.error.logPath});return i.data??{}},o=async(e={})=>{let t=await n("session_list",[],e);return(Array.isArray(t.sessions)?t.sessions:[]).map(ez)};return{devices:{list:async(e={})=>{let t=await n("devices",[],e);return(Array.isArray(t.devices)?t.devices:[]).map(eV)}},sessions:{list:async(e={})=>await o(e),close:async(t={})=>{let r=eH(e.session,t.session),o=(await n("close",[],t)).shutdown;return{session:r,shutdown:"object"==typeof o&&null!==o?o:void 0,identifiers:{session:r}}}},simulators:{ensure:async e=>{let{runtime:t,...r}=e,o=await n("ensure-simulator",[],{...r,simulatorRuntimeId:t}),a=eT(o,"udid"),i=eT(o,"device");return{udid:a,device:i,runtime:eT(o,"runtime"),created:!0===o.created,booted:!0===o.booted,iosSimulatorDeviceSet:eR(o,"ios_simulator_device_set"),identifiers:{deviceId:a,deviceName:i,udid:a}}}},apps:{install:async t=>eG(await n("install",[t.app,t.appPath],t),eH(e.session,t.session)),reinstall:async t=>eG(await n("reinstall",[t.app,t.appPath],t),eH(e.session,t.session)),installFromSource:async t=>(function(e,t){let r=eN(e,"bundleId"),n=eN(e,"packageName"),o=r??n??eN(e,"appId"),a=eN(e,"launchTarget")??n??r??o;if(!a)throw new w("COMMAND_FAILED",'Daemon response is missing "launchTarget".',{response:e});return{appName:eN(e,"appName"),appId:o,bundleId:r,packageName:n,launchTarget:a,installablePath:eN(e,"installablePath"),archivePath:eN(e,"archivePath"),materializationId:eN(e,"materializationId"),materializationExpiresAt:eN(e,"materializationExpiresAt"),identifiers:e_({session:t,bundleId:r,packageName:n,appId:o})}})(await n("install_source",[],{...t,installSource:t.source,retainMaterializedPaths:t.retainPaths,materializedPathRetentionMs:t.retentionMs}),eH(e.session,t.session)),open:async t=>{let r=eH(e.session,t.session),o=t.url?[t.app,t.url]:[t.app],a=await n("open",o,t),i=function(e){let t=e.platform,r=eN(e,"id"),n=eN(e,"device");if("ios"!==t&&"macos"!==t&&"android"!==t||!r||!n)return;let o=eO(e,"target"),a=eP(t,r,n);return{platform:t,target:o,id:r,name:n,identifiers:a,ios:"ios"===t?{udid:eN(e,"device_udid")??r,simulatorSetPath:eR(e,"ios_simulator_device_set")}:void 0,android:"android"===t?{serial:eN(e,"serial")??r}:void 0}}(a),s=eN(a,"appBundleId");return{session:r,appName:eN(a,"appName"),appBundleId:s,appId:s,startup:function(e){if(eq(e)&&"number"==typeof e.durationMs&&"string"==typeof e.measuredAt&&"string"==typeof e.method)return{durationMs:e.durationMs,measuredAt:e.measuredAt,method:e.method,appTarget:eN(e,"appTarget"),appBundleId:eN(e,"appBundleId")}}(a.startup),runtime:function(e){if(!eq(e))return;let t=e.platform,r=eN(e,"metroHost"),n="number"==typeof e.metroPort?e.metroPort:void 0;return{platform:"ios"===t||"android"===t?t:void 0,metroHost:r,metroPort:n,bundleUrl:eN(e,"bundleUrl"),launchUrl:eN(e,"launchUrl")}}(a.runtime),device:i,identifiers:{session:r,deviceId:i?.id,deviceName:i?.name,udid:i?.ios?.udid,serial:i?.android?.serial,appId:s,appBundleId:s}}},close:async(t={})=>{let r=eH(e.session,t.session),o=(await n("close",t.app?[t.app]:[],t)).shutdown;return{session:r,closedApp:t.app,shutdown:"object"==typeof o&&null!==o?o:void 0,identifiers:{session:r}}}},materializations:{release:async e=>{var t;return{released:!0===(t=await n("release_materialized_paths",[],{...e,materializationId:e.materializationId})).released,materializationId:eT(t,"materializationId"),identifiers:{}}}},metro:{prepare:async t=>await eM({projectRoot:t.projectRoot??e.cwd,kind:t.kind,publicBaseUrl:t.publicBaseUrl,proxyBaseUrl:t.proxyBaseUrl,proxyBearerToken:t.bearerToken,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})},capture:{snapshot:async(t={})=>{var r;let o=eH(e.session,t.session),a=await n("snapshot",[],t),i=eN(a,"appBundleId"),s="object"==typeof a.visibility&&null!==a.visibility?a.visibility:void 0;return{nodes:Array.isArray(r=a.nodes)?r:[],truncated:!0===a.truncated,appName:eN(a,"appName"),appBundleId:i,...s?{visibility:s}:{},warnings:Array.isArray(a.warnings)?a.warnings.filter(e=>"string"==typeof e):void 0,identifiers:{session:o,appId:i,appBundleId:i}}},screenshot:async(t={})=>{let r=eH(e.session,t.session),o=await n("screenshot",t.path?[t.path]:[],t);return{path:eT(o,"path"),overlayRefs:function(e){let t=e.overlayRefs;if(!Array.isArray(t))return;let r=[];for(let e of t){if(!eq(e))continue;let t=eN(e,"ref"),n=ex(e,"rect"),o=ex(e,"overlayRect"),a=function(e,t){let r=e[t];if(!eq(r))return;let n="number"==typeof r.x?r.x:void 0,o="number"==typeof r.y?r.y:void 0;if(void 0!==n&&void 0!==o)return{x:n,y:o}}(e,"center");t&&n&&o&&a&&r.push({ref:t,label:eN(e,"label"),rect:n,overlayRect:o,center:a})}return r}(o),identifiers:{session:r}}}}}}export{eJ as createAgentDeviceClient,w as AppError};
|
|
1
|
+
let e;import t from"node:net";import r from"node:http";import n from"node:https";import o from"node:fs";import a from"node:path";import{AsyncLocalStorage as i}from"node:async_hooks";import s,{createHash as l}from"node:crypto";import u from"node:os";import"node:fs/promises";import{spawn as c,spawnSync as d}from"node:child_process";import{fileURLToPath as m,pathToFileURL as p}from"node:url";import{setTimeout as f}from"node:timers/promises";let h=new i,y=/(token|secret|password|authorization|cookie|api[_-]?key|access[_-]?key|private[_-]?key)/i,g=/(bearer\s+[a-z0-9._-]+|(?:api[_-]?key|token|secret|password)\s*[=:]\s*\S+)/i;function w(){return s.randomBytes(8).toString("hex")}function v(e){let t=h.getStore();if(!t)return;let r={ts:new Date().toISOString(),level:e.level??"info",phase:e.phase,session:t.session,requestId:t.requestId,command:t.command,durationMs:e.durationMs,data:e.data?function e(t,r,n){if(null==t)return t;if("string"==typeof t){var o=t,a=n;let e=o.trim();if(!e)return o;if(a&&y.test(a)||g.test(e))return"[REDACTED]";let r=function(e){try{let t=new URL(e);return t.search&&(t.search="?REDACTED"),(t.username||t.password)&&(t.username="REDACTED",t.password="REDACTED"),t.toString()}catch{return null}}(e);return r||(e.length>400?`${e.slice(0,200)}...<truncated>`:e)}if("object"!=typeof t)return t;if(r.has(t))return"[Circular]";if(r.add(t),Array.isArray(t))return t.map(t=>e(t,r));let i={};for(let[n,o]of Object.entries(t)){if(y.test(n)){i[n]="[REDACTED]";continue}i[n]=e(o,r,n)}return i}(e.data,new WeakSet):void 0};if(t.events.push(r),!t.debug)return;let n=`[agent-device][diag] ${JSON.stringify(r)}
|
|
2
|
+
`;try{t.logPath&&o.appendFile(t.logPath,n,()=>{}),t.traceLogPath&&o.appendFile(t.traceLogPath,n,()=>{}),t.logPath||t.traceLogPath||process.stderr.write(n)}catch{}}async function b(e,t,r){let n=Date.now();try{let o=await t();return v({level:"info",phase:e,durationMs:Date.now()-n,data:r}),o}catch(t){throw v({level:"error",phase:e,durationMs:Date.now()-n,data:{...r??{},error:t instanceof Error?t.message:String(t)}}),t}}class I extends Error{code;details;cause;constructor(e,t,r,n){super(t),this.code=e,this.details=r,this.cause=n}}let E=/^[A-Za-z0-9][A-Za-z0-9._+-]*$/;function S(e,t,r={}){let n=_(e),o=d(n,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"],encoding:r.binaryStdout?void 0:"utf8",input:r.stdin,timeout:P(r.timeoutMs),shell:!1});if(o.error){let a=o.error.code;if("ETIMEDOUT"===a)throw new I("COMMAND_FAILED",`${n} timed out after ${P(r.timeoutMs)}ms`,{cmd:e,args:t,timeoutMs:P(r.timeoutMs)},o.error);if("ENOENT"===a)throw new I("TOOL_MISSING",`${n} not found in PATH`,{cmd:e},o.error);throw new I("COMMAND_FAILED",`Failed to run ${n}`,{cmd:e,args:t},o.error)}let a=r.binaryStdout?Buffer.isBuffer(o.stdout)?o.stdout:Buffer.from(o.stdout??""):void 0,i=r.binaryStdout?"":"string"==typeof o.stdout?o.stdout:(o.stdout??"").toString(),s="string"==typeof o.stderr?o.stderr:(o.stderr??"").toString(),l=o.status??1;if(0!==l&&!r.allowFailure)throw new I("COMMAND_FAILED",`${n} exited with code ${l}`,{cmd:e,args:t,stdout:i,stderr:s,exitCode:l,processExitError:!0});return{stdout:i,stderr:s,exitCode:l,stdoutBuffer:a}}function A(e,t,r={}){let n=c(_(e),t,{cwd:r.cwd,env:r.env,stdio:r.stdio??"ignore",detached:!0,shell:!1});return n.unref(),n.pid??0}function _(e){var t;let r,n=(t={allowRelativePath:!0},!(r=e.trim())||r.includes("\0")?null:a.isAbsolute(r)?r:r.includes("/")||r.includes("\\")?t.allowRelativePath?r:null:E.test(r)?r:null);if(!n)throw new I("INVALID_ARGS",`Invalid executable command: ${JSON.stringify(e)}`,{cmd:e});return n}function P(e){if(!Number.isFinite(e))return;let t=Math.floor(e);if(!(t<=0))return t}function D(){let e=a.dirname(m(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=a.join(t,"package.json");if(o.existsSync(e))return t;t=a.dirname(t)}return e}let k=[/(^|[/\s"'=])dist\/src\/daemon\.js($|[\s"'])/,/(^|[/\s"'=])src\/daemon\.ts($|[\s"'])/];function M(e){if(!Number.isInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch(e){return"EPERM"===e.code}}function T(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=S("ps",["-p",String(e),"-o","lstart="],{allowFailure:!0,timeoutMs:1e3});if(0!==t.exitCode)return null;let r=t.stdout.trim();return r.length>0?r:null}catch{return null}}function N(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=S("ps",["-p",String(e),"-o","command="],{allowFailure:!0,timeoutMs:1e3});if(0!==t.exitCode)return null;let r=t.stdout.trim();return r.length>0?r:null}catch{return null}}function U(e,t){let r;if(!M(e))return!1;if(t){let r=T(e);if(!r||r!==t)return!1}let n=N(e);return!!n&&!!(r=n.toLowerCase().replaceAll("\\","/")).includes("agent-device")&&k.some(e=>e.test(r))}function R(e,t){try{return process.kill(e,t),!0}catch(t){let e=t.code;if("ESRCH"===e||"EPERM"===e)return!1;throw t}}async function O(e,t){if(!M(e))return!0;let r=Date.now();for(;Date.now()-r<t;)if(await new Promise(e=>setTimeout(e,50)),!M(e))return!0;return!M(e)}async function L(e,t){!U(e,t.expectedStartTime)||!R(e,"SIGTERM")||await O(e,t.termTimeoutMs)||R(e,"SIGKILL")&&await O(e,t.killTimeoutMs)}function C(e){return e?.HOME?.trim()||u.homedir()}function x(e,t={}){return"~"===e?C(t.env):e.startsWith("~/")?a.join(C(t.env),e.slice(2)):e}function $(e,t={}){let r=x(e,t);return a.isAbsolute(r)?r:a.resolve(t.cwd??process.cwd(),r)}function B(e){let t,r=(t=(e??"").trim())?$(t):a.join(x("~"),".agent-device");return{baseDir:r,infoPath:a.join(r,"daemon.json"),lockPath:a.join(r,"daemon.lock"),logPath:a.join(r,"daemon.log"),sessionsDir:a.join(r,"sessions")}}async function F(e){let{localPath:t,baseUrl:i,token:s}=e,l=o.statSync(t).isDirectory(),u=a.basename(t),d=new URL("upload",i.endsWith("/")?i:`${i}/`),m="https:"===d.protocol?n:r,p={"x-artifact-type":l?"app-bundle":"file","x-artifact-filename":u,"transfer-encoding":"chunked"};return s&&(p.authorization=`Bearer ${s}`,p["x-agent-device-token"]=s),new Promise((e,r)=>{let n=m.request({protocol:d.protocol,host:d.hostname,port:d.port,method:"POST",path:d.pathname+d.search,headers:p},t=>{let n="";t.setEncoding("utf8"),t.on("data",e=>{n+=e}),t.on("end",()=>{clearTimeout(i);try{let t=JSON.parse(n);if(!t.ok||!t.uploadId)return void r(new I("COMMAND_FAILED",`Upload failed: ${n}`));e(t.uploadId)}catch{r(new I("COMMAND_FAILED",`Invalid upload response: ${n}`))}})}),i=setTimeout(()=>{n.destroy(),r(new I("COMMAND_FAILED","Artifact upload timed out",{timeoutMs:3e5,hint:"The upload to the remote daemon exceeded the 5-minute timeout."}))},3e5);if(n.on("error",e=>{clearTimeout(i),r(new I("COMMAND_FAILED","Failed to upload artifact to remote daemon",{hint:"Verify the remote daemon is reachable and supports artifact uploads."},e))}),l){let e=c("tar",["cf","-","-C",a.dirname(t),a.basename(t)],{stdio:["ignore","pipe","pipe"]});e.stdout.pipe(n),e.on("error",e=>{n.destroy(),r(new I("COMMAND_FAILED","Failed to create tar archive for app bundle",{},e))}),e.on("close",e=>{0!==e&&(n.destroy(),r(new I("COMMAND_FAILED",`tar failed with exit code ${e}`)))})}else{let e=o.createReadStream(t);e.pipe(n),e.on("error",e=>{n.destroy(),r(new I("COMMAND_FAILED","Failed to read local artifact",{},e))})}})}let j=/(?:^|[^\w$.])(?:import|export)\s+(?:type\s+)?(?:[^'"`]*?\s+from\s+)?['"]([^'"]+)['"]/gm,q=/import\(\s*['"]([^'"]+)['"]\s*\)/gm,G=[".ts",".tsx",".js",".jsx",".mjs",".cjs"];function V(e,t,r){t.lastIndex=0;let n=null;for(;null!==(n=t.exec(e));){let e=n[1]?.trim();e?.startsWith(".")&&r.add(e)}}function H(e){try{return o.statSync(e).isFile()?e:null}catch{return null}}let z=eP(),K=function(e=process.env.AGENT_DEVICE_DAEMON_STARTUP_TIMEOUT_MS){if(!e)return 15e3;let t=Number(e);return Number.isFinite(t)?Math.max(1e3,Math.floor(t)):15e3}(),J=function(e=process.env.AGENT_DEVICE_DAEMON_STARTUP_ATTEMPTS){if(!e)return 2;let t=Number(e);return Number.isFinite(t)?Math.min(5,Math.max(1,Math.floor(t))):2}(),W=["xcodebuild .*AgentDeviceRunnerUITests/RunnerTests/testCommand","xcodebuild .*AgentDeviceRunner\\.env\\.session-","xcodebuild build-for-testing .*ios-runner/AgentDeviceRunner/AgentDeviceRunner\\.xcodeproj"],Z=new t.BlockList;async function X(e){let r=e.meta?.requestId??w(),n=!!(e.meta?.debug||e.flags?.verbose),o=function(e){let r,n,o,a=e.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR,i=function(e){let t;if(e){try{t=new URL(e)}catch(t){throw new I("INVALID_ARGS","Invalid daemon base URL",{daemonBaseUrl:e},t instanceof Error?t:void 0)}if("http:"!==t.protocol&&"https:"!==t.protocol)throw new I("INVALID_ARGS","Daemon base URL must use http or https",{daemonBaseUrl:e});return t.toString().replace(/\/+$/,"")}}(e.flags?.daemonBaseUrl??process.env.AGENT_DEVICE_DAEMON_BASE_URL),s=e.flags?.daemonAuthToken??process.env.AGENT_DEVICE_DAEMON_AUTH_TOKEN;var l=i,u=s;if(!(!l||"localhost"===(r=new URL(l).hostname.trim().toLowerCase().replace(/^\[(.*)\]$/,"$1"))||(t.isIPv4(r)?Z.check(r,"ipv4"):!!t.isIPv6(r)&&Z.check(r,"ipv6")))&&("string"!=typeof u||!(u.trim().length>0)))throw new I("INVALID_ARGS","Remote daemon base URL for non-loopback hosts requires daemon authentication",{daemonBaseUrl:l,hint:"Provide --daemon-auth-token or AGENT_DEVICE_DAEMON_AUTH_TOKEN when using a non-loopback remote daemon URL."});let c=e.flags?.daemonTransport??process.env.AGENT_DEVICE_DAEMON_TRANSPORT,d="auto"===(n=(c??"").trim().toLowerCase())?"auto":"socket"===n?"socket":"http"===n?"http":"auto";if(i&&"socket"===d)throw new I("INVALID_ARGS","Remote daemon base URL only supports HTTP transport. Remove --daemon-transport socket.",{daemonBaseUrl:i});let m="http"===(o=(e.flags?.daemonServerMode??process.env.AGENT_DEVICE_DAEMON_SERVER_MODE??("dual"===c?"dual":void 0)??"").trim().toLowerCase())?"http":"dual"===o?"dual":"socket";return{paths:B(a),transportPreference:d,serverMode:m,remoteBaseUrl:i,remoteAuthToken:s}}(e),a=function(e,t=process.env.AGENT_DEVICE_DAEMON_TIMEOUT_MS){if("test"!==e)return eP(t)}(e.command),i=await b("daemon_startup",async()=>await er(o),{requestId:r,session:e.session}),s=await Y(e,i),l={...e,positionals:s.positionals,flags:s.flags,token:i.token,meta:{...e.meta??{},requestId:r,debug:n,cwd:e.meta?.cwd,tenantId:e.meta?.tenantId??e.flags?.tenant,runId:e.meta?.runId??e.flags?.runId,leaseId:e.meta?.leaseId??e.flags?.leaseId,sessionIsolation:e.meta?.sessionIsolation??e.flags?.sessionIsolation,lockPolicy:e.meta?.lockPolicy,lockPlatform:e.meta?.lockPlatform,...s.uploadedArtifactId?{uploadedArtifactId:s.uploadedArtifactId}:{},...s.clientArtifactPaths?{clientArtifactPaths:s.clientArtifactPaths}:{},...s.installSource?{installSource:s.installSource}:{}}};return v({level:"info",phase:"daemon_request_prepare",data:{requestId:r,command:e.command,session:e.session}}),await b("daemon_request",async()=>await eh(i,l,o.transportPreference,a),{requestId:r,command:e.command})}async function Y(e,t){let r,n=[...e.positionals??[]],i=e.flags?{...e.flags}:void 0,s=e.meta?.installSource,l={};if(eE(t)){let o=function(e,t){if("screenshot"===e.command){let r=ee(e,"path",".png");return t[0]?{field:"path",localPath:r,positionalIndex:0,positionalPath:et("screenshot",".png")}:{field:"path",localPath:r,positionalIndex:0,flagPath:et("screenshot",".png")}}if("record"===e.command&&"start"===(t[0]??"").toLowerCase()){let t=ee(e,"outPath",".mp4",1);return{field:"outPath",localPath:t,positionalIndex:1,positionalPath:et("recording",a.extname(t)||".mp4")}}return null}(e,n);o&&(void 0!==o.positionalPath&&(n[o.positionalIndex]=o.positionalPath),void 0!==o.flagPath&&((i??={}).out=o.flagPath),l[o.field]=o.localPath);let u=await Q(e,t);u&&(s=u.installSource,r=u.uploadedArtifactId??r)}if(!eE(t)||"install"!==e.command&&"reinstall"!==e.command||n.length<2)return{positionals:n,flags:i,installSource:s,uploadedArtifactId:r,...Object.keys(l).length>0?{clientArtifactPaths:l}:{}};let u=n[1];if(u.startsWith("remote:"))return n[1]=u.slice(7),{positionals:n,flags:i,...Object.keys(l).length>0?{clientArtifactPaths:l}:{}};let c=a.isAbsolute(u)?u:a.resolve(e.meta?.cwd??process.cwd(),u);return o.existsSync(c)?{positionals:n,flags:i,installSource:s,uploadedArtifactId:r=await F({localPath:c,baseUrl:t.baseUrl,token:t.token}),...Object.keys(l).length>0?{clientArtifactPaths:l}:{}}:{positionals:n,flags:i,...Object.keys(l).length>0?{clientArtifactPaths:l}:{}}}async function Q(e,t){let r=e.meta?.installSource;if("install_source"!==e.command||!r||"path"!==r.kind)return null;let n=r.path.trim();if(!n)return{installSource:r};if(n.startsWith("remote:"))return{installSource:{...r,path:n.slice(7)}};let i=a.isAbsolute(n)?n:a.resolve(e.meta?.cwd??process.cwd(),n);if(!o.existsSync(i))return{installSource:{...r,path:i}};let s=await F({localPath:i,baseUrl:t.baseUrl,token:t.token});return{installSource:{...r,path:i},uploadedArtifactId:s}}function ee(e,t,r,n=0){let o=e.positionals?.[n]??e.flags?.out,i=`${"path"===t?"screenshot":"recording"}-${Date.now()}${r}`,s=o&&o.trim().length>0?o:i;return a.isAbsolute(s)?s:a.resolve(e.meta?.cwd??process.cwd(),s)}function et(e,t){let r=t.startsWith(".")?t:`.${t}`;return a.posix.join("/tmp",`agent-device-${e}-${Date.now()}-${Math.random().toString(36).slice(2,8)}${r}`)}async function er(e){let t;if(e.remoteBaseUrl){let t={transport:"http",token:e.remoteAuthToken??"",pid:0,baseUrl:e.remoteBaseUrl};if(await em(t,"http"))return t;throw new I("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."})}let r=es(e.paths.infoPath),n=function(){try{let e=D();return JSON.parse(o.readFileSync(a.join(e,"package.json"),"utf8")).version??"0.0.0"}catch{return"0.0.0"}}(),i=function(e,t=D()){try{let r=a.resolve(t),n=[a.resolve(e)],i=new Set,l=[];for(;n.length>0;){let e=n.pop();if(!e||i.has(e))continue;i.add(e);let t=o.statSync(e);if(!t.isFile())continue;let s=a.relative(r,e)||e;l.push(`${s}:${t.size}:${Math.trunc(t.mtimeMs)}`);let u=o.readFileSync(e,"utf8");for(let t of function(e){let t=new Set;return V(e,j,t),V(e,q,t),[...t]}(u)){let r=function(e,t){let r=a.resolve(a.dirname(e),t),n=H(r);if(n)return n;for(let e of G){let t=H(`${r}${e}`);if(t)return t}for(let e of G){let t=H(a.join(r,`index${e}`));if(t)return t}return null}(e,t);r&&n.push(r)}}let u=l.sort().join("|"),c=s.createHash("sha1").update(u).digest("hex");return`graph:${l.length}:${c}`}catch{return"unknown"}}((t=ef()).useSrc?t.srcPath:t.distPath,t.root),l=!!r&&await em(r,e.transportPreference);if(r&&r.version===n&&r.codeSignature===i&&l)return r;r&&(r.version!==n||r.codeSignature!==i||!l)&&(await ei(r),ed(e.paths.infoPath)),function(e){let t=eu(e);if(!t.hasLock||t.hasInfo)return;let r=el(e.lockPath);if(!r)return ed(e.lockPath);U(r.pid,r.processStartTime)||ed(e.lockPath)}(e.paths);let u=0;for(let t=1;t<=J;t+=1){await ep(e);let r=await en(K,e);if(r)return r;if(await ea(e.paths)){u+=1;continue}let n=eu(e.paths);if(!(t<J))break;if(!n.hasInfo&&!n.hasLock){await eo(150);continue}}let c=eu(e.paths);throw new I("COMMAND_FAILED","Failed to start daemon",{kind:"daemon_startup_failed",infoPath:e.paths.infoPath,lockPath:e.paths.lockPath,startupTimeoutMs:K,startupAttempts:J,lockRecoveryCount:u,metadataState:c,hint:function(e,t=B(process.env.AGENT_DEVICE_STATE_DIR)){return e.hasLock&&!e.hasInfo?`Detected ${t.lockPath} without ${t.infoPath}. If no agent-device daemon process is running, delete ${t.lockPath} and retry.`:e.hasLock&&e.hasInfo?`Daemon metadata may be stale. If no agent-device daemon process is running, delete ${t.infoPath} and ${t.lockPath}, then retry.`:`Daemon metadata is missing or stale. Delete ${t.infoPath} if present and retry.`}(c,e.paths)})}async function en(e,t){let r=Date.now();for(;Date.now()-r<e;){let e=es(t.paths.infoPath);if(e&&await em(e,t.transportPreference))return e;await new Promise(e=>setTimeout(e,100))}return null}async function eo(e){await new Promise(t=>setTimeout(t,e))}async function ea(e){let t=eu(e);if(!t.hasLock||t.hasInfo)return!1;let r=el(e.lockPath);return r&&U(r.pid,r.processStartTime)&&await L(r.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:r.processStartTime}),ed(e.lockPath),!0}async function ei(e){await L(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}function es(e){let t=ec(e);if(!t||"object"!=typeof t)return null;let r="string"==typeof t.token&&t.token.length>0?t.token:null;if(!r)return null;let n=Number.isInteger(t.port)&&Number(t.port)>0,o=Number.isInteger(t.httpPort)&&Number(t.httpPort)>0;if(!n&&!o)return null;let a=t.transport,i="string"==typeof t.version?t.version:void 0,s="string"==typeof t.codeSignature?t.codeSignature:void 0,l="string"==typeof t.processStartTime?t.processStartTime:void 0,u=Number.isInteger(t.pid)&&Number(t.pid)>0;return{token:r,port:n?Number(t.port):void 0,httpPort:o?Number(t.httpPort):void 0,transport:"socket"===a||"http"===a||"dual"===a?a:void 0,pid:u?Number(t.pid):0,version:i,codeSignature:s,processStartTime:l}}function el(e){let t=ec(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}Z.addSubnet("127.0.0.0",8,"ipv4"),Z.addAddress("::1","ipv6"),Z.addSubnet("::ffff:127.0.0.0",104,"ipv6");function eu(e){return{hasInfo:o.existsSync(e.infoPath),hasLock:o.existsSync(e.lockPath)}}function ec(e){if(!o.existsSync(e))return null;try{return JSON.parse(o.readFileSync(e,"utf8"))}catch{return null}}function ed(e){try{o.existsSync(e)&&o.unlinkSync(e)}catch{}}async function em(e,o){var a;return"http"===ey(e,o)?await function(e){let t=e.baseUrl?eS(e.baseUrl,"health"):e.httpPort?`http://127.0.0.1:${e.httpPort}/health`:null;if(!t)return Promise.resolve(!1);let o=new URL(t),a="https:"===o.protocol?n:r,i=e.baseUrl?3e3:500;return new Promise(e=>{let t=a.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()})}(e):await ((a=e.port)?new Promise(e=>{let r=t.createConnection({host:"127.0.0.1",port:a},()=>{r.destroy(),e(!0)});r.on("error",()=>{e(!1)})}):Promise.resolve(!1))}async function ep(e){let t=ef(),r=t.useSrc?["--experimental-strip-types",t.srcPath]:[t.distPath],n={...process.env,AGENT_DEVICE_STATE_DIR:e.paths.baseDir,AGENT_DEVICE_DAEMON_SERVER_MODE:e.serverMode};A(process.execPath,r,{env:n})}function ef(){let e=D(),t=a.join(e,"dist","src","daemon.js"),r=a.join(e,"src","daemon.ts"),n=o.existsSync(t),i=o.existsSync(r);if(!n&&!i)throw new I("COMMAND_FAILED","Daemon entry not found",{distPath:t,srcPath:r});return{root:e,distPath:t,srcPath:r,useSrc:process.execArgv.includes("--experimental-strip-types")?i:!n&&i}}async function eh(e,t,r,n){return"http"===ey(e,r)?await eI(e,t,n):await eb(e,t,n)}function ey(e,t){if(e.baseUrl){if("socket"===t)throw new I("COMMAND_FAILED","Remote daemon endpoint only supports HTTP transport",{daemonBaseUrl:e.baseUrl});return"http"}if("http"===t||"socket"===t){var r=e,n=t;if(eg(r,n))return n;throw new I("COMMAND_FAILED","http"===n?"Daemon HTTP endpoint is unavailable":"Daemon socket endpoint is unavailable")}let o=("socket"===e.transport||"dual"===e.transport?["socket","http"]:["http","socket"]).find(t=>eg(e,t));if(o)return o;throw new I("COMMAND_FAILED","Daemon metadata has no reachable transport")}function eg(e,t){return"http"===t?!!e.httpPort:!!e.port}function ew(e,t,r,n,o,a){let i=o?{terminated:0}:function(){let e=0;try{for(let t of W){let r=S("pkill",["-f",t],{allowFailure:!0});0===r.exitCode&&(e+=1)}return{terminated:e}}catch(t){return{terminated:e,error:t instanceof Error?t.message:String(t)}}}(),s=o?{forcedKill:!1}:function(e,t){let r=!1;try{U(e.pid,e.processStartTime)&&(process.kill(e.pid,"SIGKILL"),r=!0)}catch{L(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}finally{ed(t.infoPath),ed(t.lockPath)}return{forcedKill:r}}(e,t);return v({level:"error",phase:"daemon_request_timeout",data:{timeoutMs:a,requestId:r,command:n,timedOutRunnerPidsTerminated:i.terminated,timedOutRunnerCleanupError:i.error,daemonPidReset:o?void 0:e.pid,daemonPidForceKilled:o?void 0:s.forcedKill,daemonBaseUrl:e.baseUrl}}),new I("COMMAND_FAILED","Daemon request timed out",{timeoutMs:a,requestId:r,hint:o?"Retry with --debug and verify the remote daemon URL, auth token, and remote host logs.":"Retry with --debug and check daemon diagnostics logs. Timed-out iOS runner xcodebuild processes were terminated when detected."})}function ev(e,t,r){return v({level:"error",phase:"daemon_request_socket_error",data:{requestId:t,message:e instanceof Error?e.message:String(e)}}),new I("COMMAND_FAILED","Failed to communicate with daemon",{requestId:t,hint:r?"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 eb(e,r,n){let o=e.port;if(!o)throw new I("COMMAND_FAILED","Daemon socket endpoint is unavailable");return new Promise((a,i)=>{let s=t.createConnection({host:"127.0.0.1",port:o},()=>{s.write(`${JSON.stringify(r)}
|
|
3
|
+
`)}),l=B(r.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),u="number"==typeof n?setTimeout(()=>{s.destroy(),i(ew(e,l,r.meta?.requestId,r.command,!1,n))},n):void 0,c="";s.setEncoding("utf8"),s.on("data",e=>{let t=(c+=e).indexOf("\n");if(-1===t)return;let n=c.slice(0,t).trim();if(n)try{let e=JSON.parse(n);s.end(),u&&clearTimeout(u),a(e)}catch(e){u&&clearTimeout(u),i(new I("COMMAND_FAILED","Invalid daemon response",{requestId:r.meta?.requestId,line:n},e instanceof Error?e:void 0))}}),s.on("error",e=>{u&&clearTimeout(u),i(ev(e,r.meta?.requestId,!1))})})}async function eI(e,t,o){let a=e.baseUrl?new URL(eS(e.baseUrl,"rpc")):e.httpPort?new URL(`http://127.0.0.1:${e.httpPort}/rpc`):null;if(!a)throw new I("COMMAND_FAILED","Daemon HTTP endpoint is unavailable");let i=JSON.stringify({jsonrpc:"2.0",id:t.meta?.requestId??w(),method:"agent_device.command",params:t}),s={"content-type":"application/json","content-length":Buffer.byteLength(i)};return e.baseUrl&&e.token&&(s.authorization=`Bearer ${e.token}`,s["x-agent-device-token"]=e.token),await new Promise((l,u)=>{let c=B(t.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),d=("https:"===a.protocol?n:r).request({protocol:a.protocol,host:a.hostname,port:a.port,method:"POST",path:a.pathname+a.search,headers:s},r=>{let n="";r.setEncoding("utf8"),r.on("data",e=>{n+=e}),r.on("end",()=>{p&&clearTimeout(p);try{let r=JSON.parse(n);if(r.error){let e=r.error.data??{};u(new I(String(e.code??"COMMAND_FAILED"),String(e.message??r.error.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:t.meta?.requestId}));return}if(!r.result||"object"!=typeof r.result)return void u(new I("COMMAND_FAILED","Invalid daemon RPC response",{requestId:t.meta?.requestId}));if(e.baseUrl&&r.result.ok)return void eA(e,t,r.result).then(l).catch(u);l(r.result)}catch(e){p&&clearTimeout(p),u(new I("COMMAND_FAILED","Invalid daemon response",{requestId:t.meta?.requestId,line:n},e instanceof Error?e:void 0))}})}),m=eE(e),p="number"==typeof o?setTimeout(()=>{d.destroy(),u(ew(e,c,t.meta?.requestId,t.command,m,o))},o):void 0;d.on("error",e=>{p&&clearTimeout(p),u(ev(e,t.meta?.requestId,m))}),d.write(i),d.end()})}function eE(e){return"string"==typeof e.baseUrl&&e.baseUrl.length>0}function eS(e,t){return new URL(t,e.endsWith("/")?e:`${e}/`).toString()}async function eA(e,t,r){let n=Array.isArray(r.data?.artifacts)?r.data.artifacts:[];if(0===n.length||!e.baseUrl)return r;let o=r.data?{...r.data}:{},i=[];for(let r of n){if(!r||"object"!=typeof r||"string"!=typeof r.artifactId){i.push(r);continue}let n=function(e,t){if(e.localPath&&e.localPath.trim().length>0)return e.localPath;let r=e.fileName?.trim()||`${e.field}-${Date.now()}`;return a.resolve(t.meta?.cwd??process.cwd(),r)}(r,t);await e_({baseUrl:e.baseUrl,token:e.token,artifactId:r.artifactId,destinationPath:n,requestId:t.meta?.requestId}),o[r.field]=n,i.push({...r,localPath:n})}return o.artifacts=i,{ok:!0,data:o}}async function e_(e){var t,i;let s,l=new URL((t=e.baseUrl,i=e.artifactId,s=t.endsWith("/")?t:`${t}/`,new URL(`upload/${encodeURIComponent(i)}`,s).toString())),u="https:"===l.protocol?n:r;await o.promises.mkdir(a.dirname(e.destinationPath),{recursive:!0}),await new Promise((t,r)=>{let n=!1,a=e.timeoutMs??z,i=a=>{if(!n){if(n=!0,clearTimeout(c),a)return void o.promises.rm(e.destinationPath,{force:!0}).finally(()=>r(a));t()}},s=u.request({protocol:l.protocol,host:l.hostname,port:l.port,method:"GET",path:l.pathname+l.search,headers:e.token?{authorization:`Bearer ${e.token}`,"x-agent-device-token":e.token}:void 0},t=>{if((t.statusCode??500)>=400){let r="";t.setEncoding("utf8"),t.on("data",e=>{r+=e}),t.on("end",()=>{i(new I("COMMAND_FAILED","Failed to download remote artifact",{artifactId:e.artifactId,statusCode:t.statusCode,requestId:e.requestId,body:r}))});return}let r=o.createWriteStream(e.destinationPath);r.on("error",e=>{i(e instanceof Error?e:Error(String(e)))}),t.on("error",e=>{i(e instanceof Error?e:Error(String(e)))}),t.on("aborted",()=>{i(new I("COMMAND_FAILED","Remote artifact download was interrupted",{artifactId:e.artifactId,requestId:e.requestId}))}),r.on("finish",()=>{r.close(()=>i())}),t.pipe(r)}),c=setTimeout(()=>{s.destroy(new I("COMMAND_FAILED","Remote artifact download timed out",{artifactId:e.artifactId,requestId:e.requestId,timeoutMs:a}))},a);s.on("error",t=>{t instanceof I?i(t):i(new I("COMMAND_FAILED","Failed to download remote artifact",{artifactId:e.artifactId,requestId:e.requestId,timeoutMs:a},t instanceof Error?t:void 0))}),s.end()})}function eP(e=process.env.AGENT_DEVICE_DAEMON_TIMEOUT_MS){if(!e)return 9e4;let t=Number(e);return Number.isFinite(t)?Math.max(1e3,Math.floor(t)):9e4}let eD="--agent-device-run-metro-companion",ek="AGENT_DEVICE_METRO_COMPANION_SERVER_BASE_URL",eM="AGENT_DEVICE_METRO_COMPANION_BEARER_TOKEN",eT="AGENT_DEVICE_METRO_COMPANION_LOCAL_BASE_URL",eN="AGENT_DEVICE_METRO_COMPANION_LAUNCH_URL";function eU(e){return e.replace(/\/+$/,"")}async function eR(e){var t,r;let n=await fetch(`${eU(e.serverBaseUrl)}/api/metro/companion/register`,{method:"POST",headers:(t=e.serverBaseUrl,r=e.bearerToken,{authorization:`Bearer ${r}`,"content-type":"application/json",...t.includes("ngrok")?{"ngrok-skip-browser-warning":"1"}:{}}),body:JSON.stringify({local_base_url:eU(e.localBaseUrl),...e.launchUrl?{launch_url:e.launchUrl}:{}})}),o=await n.json();if(!n.ok||!0!==o.ok||"string"!=typeof o.data?.ws_url)throw Error(`Failed to register Metro companion: ${JSON.stringify(o)}`);return{wsUrl:o.data.ws_url}}async function eO(e){return"string"==typeof e?Buffer.from(e,"utf8"):e instanceof ArrayBuffer?Buffer.from(e):ArrayBuffer.isView(e)?Buffer.from(e.buffer,e.byteOffset,e.byteLength):"u">typeof Blob&&e instanceof Blob?Buffer.from(await e.arrayBuffer()):Buffer.from(String(e),"utf8")}async function eL(e){return JSON.parse((await eO(e.data)).toString("utf8"))}function eC(e,t){1===e.readyState&&e.send(JSON.stringify(t))}async function ex(e,t){1!==e.readyState&&await new Promise((r,n)=>{let o=()=>{s(),r()},a=()=>{s(),n(Error(`${t} WebSocket failed before opening.`))},i=()=>{s(),n(Error(`${t} WebSocket closed before opening.`))},s=()=>{e.removeEventListener("open",o),e.removeEventListener("error",a),e.removeEventListener("close",i)};e.addEventListener("open",o,{once:!0}),e.addEventListener("error",a,{once:!0}),e.addEventListener("close",i,{once:!0})})}async function e$(e){e.readyState>=WebSocket.CLOSING||await new Promise(t=>{let r=()=>{n(),t()},n=()=>{e.removeEventListener("close",r),e.removeEventListener("error",r)};e.addEventListener("close",r,{once:!0}),e.addEventListener("error",r,{once:!0}),e.readyState>=WebSocket.CLOSING&&r()})}function eB(e,t,r){try{e.close(t,r)}catch{}}async function eF(e,t,r,n){var o,a;switch(t.type){case"ping":return void eC(e,{type:"pong",timestamp:t.timestamp});case"http-request":try{let n=await fetch(new URL(t.path,`${eU(r.localBaseUrl)}/`),{method:t.method,headers:t.headers,...t.bodyBase64?{body:Buffer.from(t.bodyBase64,"base64")}:{}}),o=Buffer.from(await n.arrayBuffer());eC(e,{type:"http-response",requestId:t.requestId,status:n.status,headers:Object.fromEntries(n.headers.entries()),...o.length>0?{bodyBase64:o.toString("base64")}:{}})}catch(r){eC(e,{type:"http-error",requestId:t.requestId,message:r instanceof Error?r.message:String(r)})}return;case"ws-open":{let a,i=new WebSocket((o=r.localBaseUrl,(a=new URL(t.path,`${eU(o)}/`)).protocol="https:"===a.protocol?"wss:":"ws:",a.toString()));i.binaryType="arraybuffer";let s=!1;i.addEventListener("message",r=>{(async()=>{if(!s)return;let n=await eO(r.data);eC(e,{type:"ws-frame",streamId:t.streamId,dataBase64:n.toString("base64"),binary:"string"!=typeof r.data})})().catch(e=>{console.error(e instanceof Error?e.message:String(e))})}),i.addEventListener("close",r=>{n.delete(t.streamId),s&&eC(e,{type:"ws-close",streamId:t.streamId,code:r.code,reason:r.reason})}),i.addEventListener("error",()=>{s&&eC(e,{type:"ws-close",streamId:t.streamId,code:1011,reason:"Upstream WebSocket error."})}),n.set(t.streamId,i);try{await ex(i,"Upstream"),s=!0,eC(e,{type:"ws-open-result",streamId:t.streamId,success:!0,headers:{}})}catch(r){n.delete(t.streamId),eB(i,1011,"open failed"),eC(e,{type:"ws-open-result",streamId:t.streamId,success:!1,error:r instanceof Error?r.message:String(r)})}return}case"ws-frame":{let e=n.get(t.streamId);if(!e||1!==e.readyState)return;let r=Buffer.from(t.dataBase64,"base64");e.send(t.binary?r:r.toString("utf8"));return}case"ws-close":{let e=n.get(t.streamId);if(!e)return;n.delete(t.streamId),eB(e,"number"==typeof(a=t.code)&&Number.isInteger(a)&&(1e3===a||a>=3e3&&a<=4999||a>=1001&&a<=1015&&1004!==a&&1005!==a&&1006!==a)?a:1011,t.reason??"bridge requested close");return}}}async function ej(e){let t=new Map;for(;;){try{let r=await eR(e),n=new WebSocket(r.wsUrl);n.binaryType="arraybuffer",await ex(n,"Bridge"),n.addEventListener("message",r=>{(async()=>{let o=await eL(r);await eF(n,o,e,t)})().catch(e=>{console.error(e instanceof Error?e.message:String(e))})}),await e$(n),t.forEach(e=>eB(e,1012,"bridge disconnected")),t.clear()}catch(e){console.error(e instanceof Error?e.message:String(e))}await f(1e3)}}async function eq(e,t){let r=function(e,t){if(e[0]!==eD)return null;let r=t[ek]?.trim(),n=t[eM]?.trim(),o=t[eT]?.trim();if(!r||!n||!o)throw Error("Metro companion worker is missing required environment configuration.");return{serverBaseUrl:r,bearerToken:n,localBaseUrl:o,launchUrl:t[eN]?.trim()||void 0}}(e,t);return!!r&&(await ej(r),!0)}function eG(e){return l("sha256").update(e).digest("hex")}function eV(e){return e?.trim()?e.trim():void 0}function eH(e,t){o.mkdirSync(a.dirname(e),{recursive:!0}),o.writeFileSync(e,`${JSON.stringify(t,null,2)}
|
|
4
|
+
`,"utf8")}function ez(e){return e.includes(eD)}function eK(e,t){return!t||e.consumers.includes(t)?e:{...e,consumers:[...e.consumers,t]}}async function eJ(e){if(!M(e.pid))return;let t=N(e.pid);if(t&&ez(t)){try{process.kill(e.pid,"SIGTERM")}catch(t){let e=t.code;if("ESRCH"===e||"EPERM"===e)return;throw t}if(!await O(e.pid,1e3)){try{process.kill(e.pid,"SIGKILL")}catch(t){let e=t.code;if("ESRCH"===e||"EPERM"===e)return;throw t}await O(e.pid,1e3)}}}async function eW(e){let t=eV(e.consumerKey)??eV(e.profileKey)??null,r=function(e,t){let r=a.join(e,".agent-device");if(!t)return{statePath:a.join(r,"metro-companion.json"),logPath:a.join(r,"metro-companion.log")};let n=eG(t).slice(0,12),o=a.join(r,"metro-companion");return{statePath:a.join(o,`metro-companion-${n}.json`),logPath:a.join(o,`metro-companion-${n}.log`)}}(e.projectRoot,e.profileKey),n=function(e){try{let t=JSON.parse(o.readFileSync(e,"utf8"));if(!Number.isInteger(t.pid)||0>=Number(t.pid)||"string"!=typeof t.serverBaseUrl||"string"!=typeof t.localBaseUrl||"string"!=typeof t.tokenHash||0===t.tokenHash.length)return null;let r=Array.isArray(t.consumers)?t.consumers.filter(e=>"string"==typeof e&&e.length>0):[];return{pid:Number(t.pid),startTime:"string"==typeof t.startTime?t.startTime:void 0,command:"string"==typeof t.command?t.command:void 0,serverBaseUrl:t.serverBaseUrl,localBaseUrl:t.localBaseUrl,launchUrl:eV("string"==typeof t.launchUrl?t.launchUrl:void 0),tokenHash:t.tokenHash,consumers:r}}catch{return null}}(r.statePath);if(n&&function(e,t){if(!M(e.pid))return!1;if(e.startTime){let t=T(e.pid);if(!t||t!==e.startTime)return!1}let r=N(e.pid);return!!r&&!!ez(r)&&e.serverBaseUrl===eU(t.serverBaseUrl)&&e.localBaseUrl===eU(t.localBaseUrl)&&e.launchUrl===eV(t.launchUrl)&&e.tokenHash===eG(t.bearerToken)}(n,e)){let e=eK(n,t);return e!==n&&eH(r.statePath,e),{pid:n.pid,spawned:!1,statePath:r.statePath,logPath:r.logPath}}if(n){await eJ(n);var i=r.statePath;try{o.unlinkSync(i)}catch{}}let s=function(e,t){let r=m(import.meta.url),n=r.endsWith(".ts")?["--experimental-strip-types"]:[];o.mkdirSync(a.dirname(t),{recursive:!0});let i=o.openSync(t,"a"),s=0;try{let t;s=A(process.execPath,[...n,r,eD],{env:(t={...e.env??process.env,[ek]:eU(e.serverBaseUrl),[eM]:e.bearerToken,[eT]:eU(e.localBaseUrl)},e.launchUrl?.trim()?t[eN]=e.launchUrl.trim():delete t[eN],t),stdio:["ignore",i,i]})}finally{o.closeSync(i)}if(!Number.isInteger(s)||s<=0)throw Error("Failed to start Metro companion process.");return{pid:s,startTime:T(s)??void 0,command:N(s)??void 0,serverBaseUrl:eU(e.serverBaseUrl),localBaseUrl:eU(e.localBaseUrl),launchUrl:eV(e.launchUrl),tokenHash:eG(e.bearerToken),consumers:[]}}(e,r.logPath);return eH(r.statePath,eK(s,t)),{pid:s.pid,spawned:!0,statePath:r.statePath,logPath:r.logPath}}function eZ(e){return"string"==typeof e&&e.trim()?eU(e.trim()):""}function eX(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function eY(e,t,r){return $(e,{env:t,cwd:r})}function eQ(e){try{return o.accessSync(e,o.constants.F_OK),!0}catch{return!1}}function e0(e,t,r){if(null==e||""===e)return t;let n=Number.parseInt(String(e),10);return Number.isInteger(n)?Math.max(n,r):t}function e1(e,t){let r;return{platform:t,bundleUrl:((r=new URL(`${eU(e)}/index.bundle`)).searchParams.set("platform",t),r.searchParams.set("dev","true"),r.searchParams.set("minify","false"),r.toString())}}function e3(e,t){return{platform:t,metroHost:eX(e?.metro_host),metroPort:e?.metro_port,bundleUrl:eX(e?.metro_bundle_url),launchUrl:eX(e?.launch_url)}}async function e4(e){await new Promise(t=>setTimeout(t,e))}async function e5(e,t,r={}){try{let n=await fetch(e,{headers:r,signal:AbortSignal.timeout(t)});return{ok:n.ok,status:n.status,body:await n.text()}}catch(r){if(r instanceof Error&&"TimeoutError"===r.name)throw Error(`Timed out fetching ${e} after ${t}ms`);throw r}}async function e8(e,t){try{let r=await e5(e,t);return r.ok&&r.body.includes("packager-status:running")}catch{return!1}}function e2(e,t){let r=Error(e);return r.retryable=t,r}async function e6(e){var t,r,n,o;let a;try{a=await fetch(`${e.baseUrl}/api/metro/bridge`,{method:"POST",headers:(t=e.baseUrl,r=e.bearerToken,{Authorization:`Bearer ${r}`,"Content-Type":"application/json",...t.includes("ngrok")?{"ngrok-skip-browser-warning":"1"}:{}}),body:JSON.stringify({ios_runtime:e.runtime,timeout_ms:e.timeoutMs}),signal:AbortSignal.timeout(e.timeoutMs)})}catch(t){if(t instanceof Error&&"TimeoutError"===t.name)throw e2(`/api/metro/bridge timed out after ${e.timeoutMs}ms calling ${e.baseUrl}/api/metro/bridge`,!0);throw e2(t instanceof Error?t.message:String(t),!0)}let i=await a.text(),s=i?JSON.parse(i):{};if(!a.ok)throw e2(`/api/metro/bridge failed (${a.status}): ${JSON.stringify(s)}`,(n=a.status,!!(n>=500||408===n||425===n||429===n||JSON.stringify(s).includes("Metro companion is not connected"))));return{enabled:(o=s.data??s).enabled,baseUrl:o.base_url,statusUrl:o.status_url,bundleUrl:o.bundle_url,iosRuntime:e3(o.ios_runtime,"ios"),androidRuntime:e3(o.android_runtime,"android"),upstream:{bundleUrl:o.upstream.bundle_url,host:o.upstream.host,port:o.upstream.port,statusUrl:o.upstream.status_url},probe:{reachable:o.probe.reachable,statusCode:o.probe.status_code,latencyMs:o.probe.latency_ms,detail:o.probe.detail}}}function e7(e,t,r,n,o){let a=[`Metro bridge is required for this run but could not be configured via ${e}/api/metro/bridge.`];return t&&a.push(`bridgeError=${t}`),r?.probe.reachable===!1&&a.push(`bridgeProbe=${r.probe.detail||`unreachable (status ${r.probe.statusCode||0})`}`),n&&n!==t&&a.push(`initialBridgeError=${n}`),o&&a.push(`metroCompanionLog=${o}`),a.join(" ")}async function e9(e,t,r){let n=Date.now()+t;for(;Date.now()<n;){let t=Math.min(r,Math.max(n-Date.now(),1));if(await e8(e,t))return!0;let o=Math.min(500,Math.max(n-Date.now(),0));o>0&&await e4(o)}return!1}async function te(e){let t=Date.now()+e.startupTimeoutMs,r=null,n=null;for(;Date.now()<t;){try{let t=await e6({baseUrl:e.baseUrl,bearerToken:e.bearerToken,runtime:e.runtime,timeoutMs:e.probeTimeoutMs});if(!1!==t.probe.reachable)return t;r=t,n=null}catch(e){if(n=e instanceof Error?e.message:String(e),!(e&&"object"==typeof e&&"retryable"in e&&!0===e.retryable))break}let o=Math.min(1e3,Math.max(t-Date.now(),0));o>0&&await e4(o)}throw Error(e7(e.baseUrl,n,r,e.initialBridgeError,e.companionLogPath))}async function tt(e={}){let t=e.env??process.env,r=process.cwd(),n=eY(e.projectRoot??r,t,r),i=function(e,t){if("auto"!==t)return t;let r=function(e){let t=a.join(e,"package.json");if(!eQ(t))throw new I("INVALID_ARGS",`package.json not found at ${t}`);return JSON.parse(o.readFileSync(t,"utf8"))}(e);return"string"==typeof({...r.dependencies??{},...r.devDependencies??{}}).expo?"expo":"react-native"}(n,e.kind??"auto"),s=function(e){if(null==e||""===e)return 8081;let t=Number.parseInt(String(e),10);if(!Number.isInteger(t)||t<1||t>65535)throw new I("INVALID_ARGS",`Invalid Metro port: ${String(e)}. Use 1-65535.`);return t}(e.metroPort??8081),l=eX(e.listenHost)??"0.0.0.0",u=eX(e.statusHost)??"127.0.0.1",c=eZ(e.publicBaseUrl),d=e0(e.startupTimeoutMs,18e4,3e4),m=e0(e.probeTimeoutMs,1e4,1e3),p=e.reuseExisting??!0,f=e.installDependenciesIfNeeded??!0,h=e.runtimeFilePath?eY(e.runtimeFilePath,t,r):null,y=eY(e.logPath??a.join(n,".agent-device","metro.log"),t,r);if(!c)throw new I("INVALID_ARGS","metro prepare requires --public-base-url <url>.");let{proxyEnabled:g,proxyBaseUrl:w,proxyBearerToken:v}=function(e,t){if(e&&!t)throw new I("INVALID_ARGS","metro prepare requires proxy auth when --proxy-base-url is provided. Pass --bearer-token or set AGENT_DEVICE_PROXY_TOKEN.");if(!e&&t)throw new I("INVALID_ARGS","metro prepare requires --proxy-base-url when proxy auth is provided.");return{proxyEnabled:!!(e&&t),proxyBaseUrl:e,proxyBearerToken:t}}(eZ(e.proxyBaseUrl),eX(e.proxyBearerToken)??""),b=f?function(e,t){if(function(e){try{return o.statSync(e).isDirectory()}catch{return!1}}(a.join(e,"node_modules")))return{installed:!1};let r=eQ(a.join(e,"pnpm-lock.yaml"))?{command:"pnpm",installArgs:["install"]}:eQ(a.join(e,"yarn.lock"))?{command:"yarn",installArgs:["install"]}:{command:"npm",installArgs:["install"]};return S(r.command,r.installArgs,{cwd:e,env:t}),{installed:!0,packageManager:r.command}}(n,t):{installed:!1},E=`http://${u}:${s}/status`,_=!1,P=!1,D=0;if(p&&await e8(E,m))P=!0;else if(_=!0,D=function(e,t,r,n,i,s){let l="expo"===t?{command:"npx",installArgs:["expo","start","--host","lan","--port",String(r)]}:{command:"npx",installArgs:["react-native","start","--host",n,"--port",String(r)]};o.mkdirSync(a.dirname(i),{recursive:!0});let u=o.openSync(i,"a"),c=0;try{c=A(l.command,l.installArgs,{cwd:e,env:s,stdio:["ignore",u,u]})}finally{o.closeSync(u)}if(!Number.isInteger(c)||c<=0)throw Error("Failed to start Metro. Expected a detached child PID.");return{pid:c}}(n,i,s,l,y,t).pid,!await e9(E,d,m))throw Error(`Metro did not become ready at ${E} within ${d}ms. Check ${y}.`);let k=e1(c,"ios"),M=e1(c,"android"),T=null,N=null;if(g)try{T=await e6({baseUrl:w,bearerToken:v,runtime:{metro_bundle_url:k.bundleUrl},timeoutMs:m})}catch(e){N=e instanceof Error?e.message:String(e)}if(g&&(!T||!1===T.probe.reachable)){let r;try{r=(await eW({projectRoot:n,serverBaseUrl:w,bearerToken:v,localBaseUrl:`http://${u}:${s}`,launchUrl:eX(e.launchUrl),profileKey:eX(e.companionProfileKey),consumerKey:eX(e.companionConsumerKey),env:t})).logPath}catch(e){throw Error(e7(w,e instanceof Error?e.message:String(e),T,N))}try{T=await te({baseUrl:w,bearerToken:v,runtime:{metro_bundle_url:k.bundleUrl},probeTimeoutMs:m,startupTimeoutMs:d,initialBridgeError:N,companionLogPath:r})}catch(e){throw e instanceof Error?e:Error(String(e))}}let U=T?.iosRuntime??k,R=T?.androidRuntime??M,O={projectRoot:n,kind:i,dependenciesInstalled:b.installed,packageManager:b.packageManager??null,started:_,reused:P,pid:D,logPath:y,statusUrl:E,runtimeFilePath:h,iosRuntime:U,androidRuntime:R,bridge:T};return h&&(o.mkdirSync(a.dirname(h),{recursive:!0}),o.writeFileSync(h,JSON.stringify(O,null,2))),O}function tr(e){let t=e.appId??e.bundleId??e.packageName;return{session:e.session,appId:t,appBundleId:e.bundleId,package:e.packageName}}function tn(e,t,r){return{deviceId:t,deviceName:r,..."android"===e?{serial:t}:"ios"===e?{udid:t}:{}}}function to(e,t,r,n){let o=r(e[t]);if(void 0===o)throw new I("COMMAND_FAILED",n,{response:e});return o}(e=process.argv[1])&&p(a.resolve(e)).href===import.meta.url&&eq(process.argv.slice(2),process.env).catch(e=>{if(e instanceof Error&&e.message.includes("missing required environment")){console.error(e.message),process.exitCode=1;return}console.error(e instanceof Error?e.stack??e.message:String(e)),process.exitCode=1});function ta(e,t){return to(e,t,td,`Daemon response is missing "${t}".`)}function ti(e,t){return td(e[t])}function ts(e,t){var r;let n;return r=td,null===(n=e[t])?null:r(n)}function tl(e,t){return to(e,t,tp,`Daemon response has invalid "${t}".`)}function tu(e,t){return function(e){return"tv"===e||"mobile"===e||"desktop"===e?e:void 0}(e[t])??"mobile"}function tc(e,t){let r=e[t];if(!ty(r))return;let n="number"==typeof r.x?r.x:void 0,o="number"==typeof r.y?r.y:void 0,a="number"==typeof r.width?r.width:void 0,i="number"==typeof r.height?r.height:void 0;if(void 0!==n&&void 0!==o&&void 0!==a&&void 0!==i)return{x:n,y:o,width:a,height:i}}function td(e){return"string"==typeof e&&e.length>0?e:void 0}function tm(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function tp(e){return"ios"===e||"macos"===e||"android"===e?e:void 0}function tf(e){return"simulator"===e||"emulator"===e||"device"===e?e:void 0}function th(e){if(!ty(e))throw new I("COMMAND_FAILED","Daemon returned an unexpected response shape.",{value:e});return e}function ty(e){return"object"==typeof e&&null!==e}function tg(e){let t={};for(let[r,n]of Object.entries(e))void 0!==n&&(t[r]=n);return t}function tw(e,t){let r=ti(e,"bundleId"),n=ti(e,"package");return{app:ta(e,"app"),appPath:ta(e,"appPath"),platform:tl(e,"platform"),appId:r??n,bundleId:r,package:n,identifiers:tr({session:t,bundleId:r,packageName:n})}}function tv(e){let t=th(e),r=tl(t,"platform"),n=ta(t,"id"),o=ta(t,"name");return{platform:r,target:tu(t,"target"),kind:to(t,"kind",tf,'Daemon response has invalid "kind".'),id:n,name:o,booted:"boolean"==typeof t.booted?t.booted:void 0,identifiers:tn(r,n,o),ios:"ios"===r?{udid:n}:void 0,android:"android"===r?{serial:n}:void 0}}function tb(e){let t=th(e),r=tl(t,"platform"),n=ta(t,"id"),o=ta(t,"name"),a=tu(t,"target"),i=ta(t,"device"),s={session:o,...tn(r,n,i)};return{name:o,createdAt:to(t,"createdAt",tm,'Daemon response is missing numeric "createdAt".'),device:{platform:r,target:a,id:n,name:i,identifiers:s,ios:"ios"===r?{udid:n,simulatorSetPath:ts(t,"ios_simulator_device_set")}:void 0,android:"android"===r?{serial:n}:void 0},identifiers:s}}function tI(e,t){return t??e??"default"}function tE(e={},t={}){let r=t.transport??X,n=async(t,n=[],o={})=>{let a={...e,...o},i=await r({session:tI(e.session,o.session),command:t,positionals:n,flags:tg({stateDir:a.stateDir,daemonBaseUrl:a.daemonBaseUrl,daemonAuthToken:a.daemonAuthToken,daemonTransport:a.daemonTransport,daemonServerMode:a.daemonServerMode,tenant:a.tenant,sessionIsolation:a.sessionIsolation,runId:a.runId,leaseId:a.leaseId,platform:a.platform,target:a.target,device:a.device,udid:a.udid,serial:a.serial,iosSimulatorDeviceSet:a.iosSimulatorDeviceSet,androidDeviceAllowlist:a.androidDeviceAllowlist,runtime:a.simulatorRuntimeId,boot:a.boot,reuseExisting:a.reuseExisting,surface:a.surface,activity:a.activity,relaunch:a.relaunch,shutdown:a.shutdown,saveScript:a.saveScript,noRecord:a.noRecord,metroHost:a.metroHost,metroPort:a.metroPort,bundleUrl:a.bundleUrl,launchUrl:a.launchUrl,snapshotInteractiveOnly:a.interactiveOnly,snapshotCompact:a.compact,snapshotDepth:a.depth,snapshotScope:a.scope,snapshotRaw:a.raw,overlayRefs:a.overlayRefs,verbose:a.debug}),runtime:a.runtime,meta:tg({requestId:a.requestId,cwd:a.cwd,debug:a.debug,lockPolicy:a.lockPolicy,lockPlatform:a.lockPlatform,tenantId:a.tenant,runId:a.runId,leaseId:a.leaseId,sessionIsolation:a.sessionIsolation,installSource:a.installSource,retainMaterializedPaths:a.retainMaterializedPaths,materializedPathRetentionMs:a.materializedPathRetentionMs,materializationId:a.materializationId})});if(!i.ok)throw new I(i.error.code,i.error.message,{...i.error.details??{},hint:i.error.hint,diagnosticId:i.error.diagnosticId,logPath:i.error.logPath});return i.data??{}},o=async(e={})=>{let t=await n("session_list",[],e);return(Array.isArray(t.sessions)?t.sessions:[]).map(tb)};return{devices:{list:async(e={})=>{let t=await n("devices",[],e);return(Array.isArray(t.devices)?t.devices:[]).map(tv)}},sessions:{list:async(e={})=>await o(e),close:async(t={})=>{let r=tI(e.session,t.session),o=(await n("close",[],t)).shutdown;return{session:r,shutdown:"object"==typeof o&&null!==o?o:void 0,identifiers:{session:r}}}},simulators:{ensure:async e=>{let{runtime:t,...r}=e,o=await n("ensure-simulator",[],{...r,simulatorRuntimeId:t}),a=ta(o,"udid"),i=ta(o,"device");return{udid:a,device:i,runtime:ta(o,"runtime"),created:!0===o.created,booted:!0===o.booted,iosSimulatorDeviceSet:ts(o,"ios_simulator_device_set"),identifiers:{deviceId:a,deviceName:i,udid:a}}}},apps:{install:async t=>tw(await n("install",[t.app,t.appPath],t),tI(e.session,t.session)),reinstall:async t=>tw(await n("reinstall",[t.app,t.appPath],t),tI(e.session,t.session)),installFromSource:async t=>(function(e,t){let r=ti(e,"bundleId"),n=ti(e,"packageName"),o=r??n??ti(e,"appId"),a=ti(e,"launchTarget")??n??r??o;if(!a)throw new I("COMMAND_FAILED",'Daemon response is missing "launchTarget".',{response:e});return{appName:ti(e,"appName"),appId:o,bundleId:r,packageName:n,launchTarget:a,installablePath:ti(e,"installablePath"),archivePath:ti(e,"archivePath"),materializationId:ti(e,"materializationId"),materializationExpiresAt:ti(e,"materializationExpiresAt"),identifiers:tr({session:t,bundleId:r,packageName:n,appId:o})}})(await n("install_source",[],{...t,installSource:t.source,retainMaterializedPaths:t.retainPaths,materializedPathRetentionMs:t.retentionMs}),tI(e.session,t.session)),open:async t=>{let r=tI(e.session,t.session),o=t.url?[t.app,t.url]:[t.app],a=await n("open",o,t),i=function(e){let t=e.platform,r=ti(e,"id"),n=ti(e,"device");if("ios"!==t&&"macos"!==t&&"android"!==t||!r||!n)return;let o=tu(e,"target"),a=tn(t,r,n);return{platform:t,target:o,id:r,name:n,identifiers:a,ios:"ios"===t?{udid:ti(e,"device_udid")??r,simulatorSetPath:ts(e,"ios_simulator_device_set")}:void 0,android:"android"===t?{serial:ti(e,"serial")??r}:void 0}}(a),s=ti(a,"appBundleId");return{session:r,appName:ti(a,"appName"),appBundleId:s,appId:s,startup:function(e){if(ty(e)&&"number"==typeof e.durationMs&&"string"==typeof e.measuredAt&&"string"==typeof e.method)return{durationMs:e.durationMs,measuredAt:e.measuredAt,method:e.method,appTarget:ti(e,"appTarget"),appBundleId:ti(e,"appBundleId")}}(a.startup),runtime:function(e){if(!ty(e))return;let t=e.platform,r=ti(e,"metroHost"),n="number"==typeof e.metroPort?e.metroPort:void 0;return{platform:"ios"===t||"android"===t?t:void 0,metroHost:r,metroPort:n,bundleUrl:ti(e,"bundleUrl"),launchUrl:ti(e,"launchUrl")}}(a.runtime),device:i,identifiers:{session:r,deviceId:i?.id,deviceName:i?.name,udid:i?.ios?.udid,serial:i?.android?.serial,appId:s,appBundleId:s}}},close:async(t={})=>{let r=tI(e.session,t.session),o=(await n("close",t.app?[t.app]:[],t)).shutdown;return{session:r,closedApp:t.app,shutdown:"object"==typeof o&&null!==o?o:void 0,identifiers:{session:r}}}},materializations:{release:async e=>{var t;return{released:!0===(t=await n("release_materialized_paths",[],{...e,materializationId:e.materializationId})).released,materializationId:ta(t,"materializationId"),identifiers:{}}}},metro:{prepare:async t=>await tt({projectRoot:t.projectRoot??e.cwd,kind:t.kind,publicBaseUrl:t.publicBaseUrl,proxyBaseUrl:t.proxyBaseUrl,proxyBearerToken:t.bearerToken,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})},capture:{snapshot:async(t={})=>{var r;let o=tI(e.session,t.session),a=await n("snapshot",[],t),i=ti(a,"appBundleId"),s="object"==typeof a.visibility&&null!==a.visibility?a.visibility:void 0;return{nodes:Array.isArray(r=a.nodes)?r:[],truncated:!0===a.truncated,appName:ti(a,"appName"),appBundleId:i,...s?{visibility:s}:{},warnings:Array.isArray(a.warnings)?a.warnings.filter(e=>"string"==typeof e):void 0,identifiers:{session:o,appId:i,appBundleId:i}}},screenshot:async(t={})=>{let r=tI(e.session,t.session),o=await n("screenshot",t.path?[t.path]:[],t);return{path:ta(o,"path"),overlayRefs:function(e){let t=e.overlayRefs;if(!Array.isArray(t))return;let r=[];for(let e of t){if(!ty(e))continue;let t=ti(e,"ref"),n=tc(e,"rect"),o=tc(e,"overlayRect"),a=function(e,t){let r=e[t];if(!ty(r))return;let n="number"==typeof r.x?r.x:void 0,o="number"==typeof r.y?r.y:void 0;if(void 0!==n&&void 0!==o)return{x:n,y:o}}(e,"center");t&&n&&o&&a&&r.push({ref:t,label:ti(e,"label"),rect:n,overlayRect:o,center:a})}return r}(o),identifiers:{session:r}}}}}}export{tE as createAgentDeviceClient,I as AppError};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-device",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.10",
|
|
4
4
|
"description": "Unified control plane for physical and virtual devices via an agent-driven CLI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Callstack",
|
|
@@ -63,6 +63,11 @@
|
|
|
63
63
|
"README.md",
|
|
64
64
|
"LICENSE"
|
|
65
65
|
],
|
|
66
|
+
"pnpm": {
|
|
67
|
+
"overrides": {
|
|
68
|
+
"lodash-es": "4.18.1"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
66
71
|
"keywords": [
|
|
67
72
|
"agent",
|
|
68
73
|
"device",
|
|
@@ -74,16 +79,18 @@
|
|
|
74
79
|
"android"
|
|
75
80
|
],
|
|
76
81
|
"dependencies": {
|
|
82
|
+
"fast-xml-parser": "^5.5.10",
|
|
77
83
|
"pngjs": "^7.0.0"
|
|
78
84
|
},
|
|
79
85
|
"devDependencies": {
|
|
80
|
-
"@microsoft/api-extractor": "^7.
|
|
86
|
+
"@microsoft/api-extractor": "^7.58.1",
|
|
81
87
|
"@rslib/core": "0.20.1",
|
|
82
88
|
"@types/node": "^22.0.0",
|
|
83
89
|
"@types/pngjs": "^6.0.5",
|
|
84
90
|
"oxfmt": "^0.42.0",
|
|
85
91
|
"oxlint": "^1.57.0",
|
|
86
92
|
"typescript": "^6.0.2",
|
|
93
|
+
"vite": "^8.0.7",
|
|
87
94
|
"vitest": "^4.1.2"
|
|
88
95
|
}
|
|
89
96
|
}
|
|
@@ -70,3 +70,4 @@ Use this skill as a router with mandatory defaults. Read this file first. For no
|
|
|
70
70
|
- Need screenshots, diff, recording, replay maintenance, or perf data: [references/verification.md](references/verification.md)
|
|
71
71
|
- Need desktop surfaces, menu bar behavior, or macOS-specific interaction rules: [references/macos-desktop.md](references/macos-desktop.md)
|
|
72
72
|
- Need remote HTTP transport, `--remote-config` launches, or tenant leases on a remote macOS host: [references/remote-tenancy.md](references/remote-tenancy.md)
|
|
73
|
+
This includes remote React Native runs where `agent-device` now prepares Metro locally and manages the local Metro companion tunnel automatically.
|
|
@@ -9,9 +9,6 @@ Open this file for remote daemon HTTP flows, including `--remote-config` launche
|
|
|
9
9
|
- `agent-device open <app> --remote-config <path> --relaunch`
|
|
10
10
|
- `AGENT_DEVICE_DAEMON_BASE_URL=...`
|
|
11
11
|
- `AGENT_DEVICE_DAEMON_AUTH_TOKEN=...`
|
|
12
|
-
- `curl ... agent_device.lease.allocate`
|
|
13
|
-
- `curl ... agent_device.lease.heartbeat`
|
|
14
|
-
- `curl ... agent_device.lease.release`
|
|
15
12
|
- `agent-device --tenant ... --session-isolation tenant --run-id ... --lease-id ...`
|
|
16
13
|
|
|
17
14
|
## Most common mistake to avoid
|
|
@@ -27,20 +24,17 @@ agent-device open com.example.myapp --remote-config ./agent-device.remote.json -
|
|
|
27
24
|
```
|
|
28
25
|
|
|
29
26
|
- This is the preferred remote launch path for sandbox or cloud agents.
|
|
27
|
+
- `agent-device` prepares local Metro and auto-starts the local Metro companion tunnel when the remote bridge needs a path back to the developer machine.
|
|
28
|
+
- `close --remote-config ...` cleans up the managed companion process for that project/profile, but leaves the developer’s Metro server running.
|
|
30
29
|
- For Android React Native relaunch flows, install or reinstall the APK first, then relaunch by installed package name.
|
|
31
30
|
- Do not use `open <apk|aab> --relaunch`; remote runtime hints are applied through the installed app sandbox.
|
|
32
31
|
|
|
33
32
|
## Lease flow example
|
|
34
33
|
|
|
35
34
|
```bash
|
|
36
|
-
export AGENT_DEVICE_DAEMON_BASE_URL
|
|
35
|
+
export AGENT_DEVICE_DAEMON_BASE_URL=<trusted-daemon-base-url>
|
|
37
36
|
export AGENT_DEVICE_DAEMON_AUTH_TOKEN=<token>
|
|
38
37
|
|
|
39
|
-
curl -sS "${AGENT_DEVICE_DAEMON_BASE_URL}/rpc" \
|
|
40
|
-
-H "content-type: application/json" \
|
|
41
|
-
-H "Authorization: Bearer <token>" \
|
|
42
|
-
-d '{"jsonrpc":"2.0","id":"alloc-1","method":"agent_device.lease.allocate","params":{"tenantId":"acme","runId":"run-123","ttlMs":60000}}'
|
|
43
|
-
|
|
44
38
|
agent-device \
|
|
45
39
|
--tenant acme \
|
|
46
40
|
--session-isolation tenant \
|
|
@@ -49,34 +43,20 @@ agent-device \
|
|
|
49
43
|
session list --json
|
|
50
44
|
```
|
|
51
45
|
|
|
52
|
-
|
|
46
|
+
Low-level lease operations exist for host-side automation, but do not point them at arbitrary hosts. The remote daemon executes device-control commands, so only use a trusted daemon base URL and an auth token managed by the same operator boundary.
|
|
53
47
|
|
|
54
|
-
|
|
55
|
-
curl -sS "${AGENT_DEVICE_DAEMON_BASE_URL}/rpc" \
|
|
56
|
-
-H "content-type: application/json" \
|
|
57
|
-
-H "Authorization: Bearer <token>" \
|
|
58
|
-
-d '{"jsonrpc":"2.0","id":"hb-1","method":"agent_device.lease.heartbeat","params":{"leaseId":"<lease-id>","ttlMs":60000}}'
|
|
59
|
-
|
|
60
|
-
curl -sS "${AGENT_DEVICE_DAEMON_BASE_URL}/rpc" \
|
|
61
|
-
-H "content-type: application/json" \
|
|
62
|
-
-H "Authorization: Bearer <token>" \
|
|
63
|
-
-d '{"jsonrpc":"2.0","id":"rel-1","method":"agent_device.lease.release","params":{"leaseId":"<lease-id>"}}'
|
|
64
|
-
```
|
|
48
|
+
Lease lifecycle methods exposed by the daemon:
|
|
65
49
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
-H "content-type: application/json" \
|
|
71
|
-
-H "Authorization: Bearer <token>" \
|
|
72
|
-
-d '{"jsonrpc":"2.0","id":"cmd-1","method":"agent_device.command","params":{"session":"qa-ios","command":"snapshot","positionals":[],"meta":{"lockPolicy":"reject","lockPlatform":"ios","tenantId":"acme","runId":"run-123","leaseId":"<lease-id>"}}}'
|
|
73
|
-
```
|
|
50
|
+
- `agent_device.lease.allocate`
|
|
51
|
+
- `agent_device.lease.heartbeat`
|
|
52
|
+
- `agent_device.lease.release`
|
|
53
|
+
- `agent_device.command`
|
|
74
54
|
|
|
75
55
|
## Transport prerequisites
|
|
76
56
|
|
|
77
57
|
- Start the daemon in HTTP mode with `AGENT_DEVICE_DAEMON_SERVER_MODE=http|dual`.
|
|
78
58
|
- Point the client at the remote host with `AGENT_DEVICE_DAEMON_BASE_URL=http(s)://host:port[/base-path]`.
|
|
79
|
-
-
|
|
59
|
+
- For non-loopback remote hosts, set `AGENT_DEVICE_DAEMON_AUTH_TOKEN` or `--daemon-auth-token`. The client rejects non-loopback remote daemon URLs without auth.
|
|
80
60
|
- Direct JSON-RPC callers can authenticate with request params, `Authorization: Bearer <token>`, or `x-agent-device-token`.
|
|
81
61
|
- Prefer an auth hook such as `AGENT_DEVICE_HTTP_AUTH_HOOK` when the host needs caller validation or tenant injection.
|
|
82
62
|
|
|
@@ -117,4 +97,5 @@ The CLI sends `AGENT_DEVICE_DAEMON_AUTH_TOKEN` in both the JSON-RPC request toke
|
|
|
117
97
|
- Missing tenant, run, or lease fields in tenant-isolation mode should fail as `INVALID_ARGS`.
|
|
118
98
|
- Inactive or scope-mismatched leases should fail as `UNAUTHORIZED`.
|
|
119
99
|
- Inspect logs on the remote host during remote debugging. Client-side `--debug` does not tail a local daemon log once `AGENT_DEVICE_DAEMON_BASE_URL` is set.
|
|
100
|
+
- Do not point `AGENT_DEVICE_DAEMON_BASE_URL` at untrusted hosts. Remote daemon requests can launch apps and execute interaction commands.
|
|
120
101
|
- Treat daemon auth tokens and lease identifiers as sensitive operational data.
|
package/dist/src/168.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{AsyncLocalStorage as e}from"node:async_hooks";import t from"node:crypto";import n from"node:fs";import r from"node:os";import i from"node:path";import{fileURLToPath as o}from"node:url";import{spawn as a,spawnSync as s}from"node:child_process";import{PNG as l}from"pngjs";let u=new e,d=/(token|secret|password|authorization|cookie|api[_-]?key|access[_-]?key|private[_-]?key)/i,c=/(bearer\s+[a-z0-9._-]+|(?:api[_-]?key|token|secret|password)\s*[=:]\s*\S+)/i;function f(){return t.randomBytes(8).toString("hex")}async function p(e,n){let r={...e,diagnosticId:`${Date.now().toString(36)}-${t.randomBytes(4).toString("hex")}`,events:[]};return await u.run(r,n)}function m(){let e=u.getStore();return e?{diagnosticId:e.diagnosticId,requestId:e.requestId,session:e.session,command:e.command,debug:e.debug}:{}}function h(e){let t=u.getStore();if(!t)return;let r={ts:new Date().toISOString(),level:e.level??"info",phase:e.phase,session:t.session,requestId:t.requestId,command:t.command,durationMs:e.durationMs,data:e.data?v(e.data):void 0};if(t.events.push(r),!t.debug)return;let i=`[agent-device][diag] ${JSON.stringify(r)}
|
|
2
|
-
`;try{t.logPath&&n.appendFile(t.logPath,i,()=>{}),t.traceLogPath&&n.appendFile(t.traceLogPath,i,()=>{}),t.logPath||t.traceLogPath||process.stderr.write(i)}catch{}}async function g(e,t,n){let r=Date.now();try{let i=await t();return h({level:"info",phase:e,durationMs:Date.now()-r,data:n}),i}catch(t){throw h({level:"error",phase:e,durationMs:Date.now()-r,data:{...n??{},error:t instanceof Error?t.message:String(t)}}),t}}function w(e={}){let t=u.getStore();if(!t||!e.force&&!t.debug||0===t.events.length)return null;try{let e=(t.session??"default").replace(/[^a-zA-Z0-9._-]/g,"_"),o=new Date().toISOString().slice(0,10),a=i.join(r.homedir(),".agent-device","logs",e,o);n.mkdirSync(a,{recursive:!0});let s=new Date().toISOString().replace(/[:.]/g,"-"),l=i.join(a,`${s}-${t.diagnosticId}.ndjson`),u=t.events.map(e=>JSON.stringify(v(e)));return n.writeFileSync(l,`${u.join("\n")}
|
|
3
|
-
`),t.events=[],l}catch{return null}}function v(e){return function e(t,n,r){if(null==t)return t;if("string"==typeof t){var i=t,o=r;let e=i.trim();if(!e)return i;if(o&&d.test(o)||c.test(e))return"[REDACTED]";let n=function(e){try{let t=new URL(e);return t.search&&(t.search="?REDACTED"),(t.username||t.password)&&(t.username="REDACTED",t.password="REDACTED"),t.toString()}catch{return null}}(e);return n||(e.length>400?`${e.slice(0,200)}...<truncated>`:e)}if("object"!=typeof t)return t;if(n.has(t))return"[Circular]";if(n.add(t),Array.isArray(t))return t.map(t=>e(t,n));let a={};for(let[r,i]of Object.entries(t)){if(d.test(r)){a[r]="[REDACTED]";continue}a[r]=e(i,n,r)}return a}(e,new WeakSet)}class b extends Error{code;details;cause;constructor(e,t,n,r){super(t),this.code=e,this.details=n,this.cause=r}}function S(e){return e instanceof b?e:e instanceof Error?new b("UNKNOWN",e.message,void 0,e):new b("UNKNOWN","Unknown error",{err:e})}function I(e,t={}){let n=S(e),r=n.details?v(n.details):void 0,i=r&&"string"==typeof r.hint?r.hint:void 0,o=(r&&"string"==typeof r.diagnosticId?r.diagnosticId:void 0)??t.diagnosticId,a=(r&&"string"==typeof r.logPath?r.logPath:void 0)??t.logPath,s=i??function(e){switch(e){case"INVALID_ARGS":return"Check command arguments and run --help for usage examples.";case"SESSION_NOT_FOUND":return"Run open first or pass an explicit device selector.";case"TOOL_MISSING":return"Install required platform tooling and ensure it is available in PATH.";case"DEVICE_NOT_FOUND":return"Verify the target device is booted/connected and selectors match.";case"APP_NOT_INSTALLED":return"Run apps to discover the exact installed package or bundle id, or install the app before open.";case"UNSUPPORTED_OPERATION":return"This command is not available for the selected platform/device.";case"COMMAND_FAILED":default:return"Retry with --debug and inspect diagnostics log for details.";case"UNAUTHORIZED":return"Refresh daemon metadata and retry the command."}}(n.code),l=function(e){if(!e)return;let t={...e};return delete t.hint,delete t.diagnosticId,delete t.logPath,Object.keys(t).length>0?t:void 0}(r),u=function(e,t,n){if("COMMAND_FAILED"!==e||n?.processExitError!==!0)return t;let r=function(e){let t=[/^an error was encountered processing the command/i,/^underlying error\b/i,/^simulator device failed to complete the requested operation/i];for(let n of e.split("\n")){let e=n.trim();if(e&&!t.some(t=>t.test(e)))return e.length>200?`${e.slice(0,200)}...`:e}return null}("string"==typeof n?.stderr?n.stderr:"");return r||t}(n.code,n.message,r);return{code:n.code,message:u,hint:s,diagnosticId:o,logPath:a,details:l}}let y="<wifi|airplane|location> <on|off>",x="appearance <light|dark|toggle>",A="faceid <match|nonmatch|enroll|unenroll>",$="touchid <match|nonmatch|enroll|unenroll>",N="fingerprint <match|nonmatch>",E="permission <grant|deny|reset> <camera|microphone|photos|contacts|contacts-limited|notifications|calendar|location|location-always|media-library|motion|reminders|siri> [full|limited]",D="permission <grant|reset> <accessibility|screen-recording|input-monitoring>",M=`macOS supports only settings ${x} and settings ${D}. wifi|airplane|location remain unsupported on macOS.`,L=`settings ${y} | settings ${x} | settings ${A} | settings ${$} | settings ${N} | settings ${E} | settings ${D}`,T=`settings requires ${y}, ${x}, ${A}, ${$}, ${N}, ${E}, or ${D}`;function O(e){let t=e.trim().toLowerCase();return"appearance"===t||"permission"===t}function C(e){return`Unsupported macOS setting: ${e}. ${M}`}let _=["app","frontmost-app","desktop","menubar"];function k(e){let t=e?.trim().toLowerCase();if("app"===t||"frontmost-app"===t||"desktop"===t||"menubar"===t)return t;throw new b("INVALID_ARGS",`Invalid surface: ${e}. Use ${_.join("|")}.`)}function R(e){var t;let n,r=P(e.label),i=P(e.value),o=P(e.identifier),a=(t=o)&&!/^[\w.]+:id\/[\w.-]+$/i.test(t)&&!/^_?NS:\d+$/i.test(t)?o:"";return(n=F(e.type??"")).includes("textfield")||n.includes("securetextfield")||n.includes("searchfield")||n.includes("edittext")||n.includes("textview")||n.includes("textarea")?i||r||a:r||i||a}function P(e){return"string"==typeof e?e.trim():""}function F(e){let t=e.trim().replace(/XCUIElementType/gi,"").replace(/^AX/,"").toLowerCase(),n=Math.max(t.lastIndexOf("."),t.lastIndexOf("/"));return -1!==n&&(t=t.slice(n+1)),t}function B(e,t={}){let n=[],r=[];for(let i of e){let e=i.depth??0,o=i.label?.trim()||i.value?.trim()||i.identifier?.trim()||"",a=G(i.type??"Element");if("group"===a&&!o)continue;for(;n.length>0&&e<=n[n.length-1];)n.pop();let s=n.length;n.push(e),r.push({node:i,depth:s,type:a,text:z(i,s,!1,a,t)})}return r}function z(e,t,n,r,i={}){var o,a,s,l,u;let d,c,f=r??G(e.type??"Element"),p=(d=R(e),{text:d,isLargeSurface:c=function(e,t){if("text-view"===t||"text-field"===t||"search"===t)return!0;let n=F(e.type??""),r=`${e.role??""} ${e.subrole??""}`.toLowerCase();return n.includes("textview")||n.includes("textarea")||n.includes("textfield")||n.includes("securetextfield")||n.includes("searchfield")||n.includes("edittext")||r.includes("text area")||r.includes("text field")}(e,f),shouldSummarize:c&&!!(o=d)&&(o.length>80||/[\r\n]/.test(o))}),m=(a=e,s=f,l=i,u=p,l.summarizeTextSurfaces&&u.shouldSummarize&&function(e,t,n){let r=P(e.label);if(r&&r!==n)return r;let i=P(e.identifier);if(i&&!U(i)&&i!==n)return i;switch(t){case"text":case"text-view":return"Text view";case"text-field":return"Text field";case"search":return"Search field";default:return""}}(a,s,u.text)||j(a,s)),h=" ".repeat(t),g=e.ref?`@${e.ref}`:"",w=(function(e,t,n,r){let i,o=[];if(!1===e.enabled&&o.push("disabled"),!n.summarizeTextSurfaces||(!0===e.selected&&o.push("selected"),V(t)&&o.push("editable"),function(e,t){if("scroll-area"===t)return!0;let n=(e.type??"").toLowerCase(),r=`${e.role??""} ${e.subrole??""}`.toLowerCase();return n.includes("scroll")||r.includes("scroll")}(e,t)&&o.push("scrollable"),!r.shouldSummarize))return o;return o.push(`preview:"${((i=r.text.replace(/\s+/g," ").trim()).length<=48?i:`${i.slice(0,45)}...`).replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`),o.push("truncated"),[...new Set(o)]})(e,f,i,p).map(e=>` [${e}]`).join(""),v=m?` "${m}"`:"";return n?`${h}${g} [${f}]${w}`.trimEnd():`${h}${g} [${f}]${v}${w}`.trimEnd()}function j(e,t){var n,r;let i,o=e.label?.trim();if(o&&(n=t,r=o,("scroll-area"===n||"list"===n||"collection"===n||"table"===n)&&(i=r.trim().toLowerCase())&&/^(vertical|horizontal)\s+scroll\s+bar(?:,?\s*\d+\s+pages?)?$/.test(i)))return"";let a=e.value?.trim();if(V(t)){if(a)return a;if(o)return o}else if(o)return o;if(a)return a;let s=e.identifier?.trim();return!s||U(s)&&("group"===t||"image"===t||"list"===t||"collection"===t)?"":s}function G(e){let t=e.replace(/XCUIElementType/gi,"").toLowerCase(),n=e.includes(".")&&(e.startsWith("android.")||e.startsWith("androidx.")||e.startsWith("com."));switch(t.includes(".")&&(t=t.replace(/^android\.widget\./,"").replace(/^android\.view\./,"").replace(/^android\.webkit\./,"").replace(/^androidx\./,"").replace(/^com\.google\.android\./,"").replace(/^com\.android\./,""),n&&t.includes(".")&&(t=t.slice(t.lastIndexOf(".")+1))),t){case"application":return"application";case"navigationbar":return"navigation-bar";case"tabbar":return"tab-bar";case"button":case"imagebutton":return"button";case"link":return"link";case"cell":return"cell";case"statictext":case"checkedtextview":return"text";case"textfield":case"edittext":return"text-field";case"textview":return n?"text":"text-view";case"textarea":return"text-view";case"switch":return"switch";case"slider":return"slider";case"image":case"imageview":return"image";case"webview":return"webview";case"framelayout":case"linearlayout":case"relativelayout":case"constraintlayout":case"viewgroup":case"view":case"group":return"group";case"listview":case"recyclerview":return"list";case"collectionview":return"collection";case"searchfield":return"search";case"segmentedcontrol":return"segmented-control";case"window":return"window";case"checkbox":return"checkbox";case"radio":return"radio";case"menuitem":return"menu-item";case"toolbar":return"toolbar";case"scrollarea":case"scrollview":case"nestedscrollview":return"scroll-area";case"table":return"table";default:return t||"element"}}function V(e){return"text-field"===e||"text-view"===e||"search"===e}function U(e){return/^[\w.]+:id\/[\w.-]+$/i.test(e)}function q(e){return e.map((e,t)=>({...e,ref:`e${t+1}`}))}function H(e){let t=e.trim();return t.startsWith("@")?t.slice(1)||null:t.startsWith("e")?t:null}function W(e,t){return e.find(e=>e.ref===t)??null}function J(e){return{x:Math.round(e.x+e.width/2),y:Math.round(e.y+e.height/2)}}function K(e,t){let n=J(t),r=e.filter(e=>{var t;return!!(t=e.rect)&&Number.isFinite(t.x)&&Number.isFinite(t.y)&&Number.isFinite(t.width)&&Number.isFinite(t.height)}),i=r.filter(e=>{let t=(e.type??"").toLowerCase();return t.includes("application")||t.includes("window")}),o=Q(i.map(e=>e.rect).filter(e=>Z(e,n.x,n.y)));if(o)return o;let a=Q(i.map(e=>e.rect));if(a)return a;let s=Q(r.map(e=>e.rect).filter(e=>Z(e,n.x,n.y)));return s||null}function X(e,t){let n=Math.max(1,t.height),r=t.y,i=t.y+n,o=r+.25*n,a=i-.25*n,s=e.y+e.height/2;return s<o?Math.ceil(o-s):s>a?Math.ceil(s-a):0}function Z(e,t,n){return t>=e.x&&t<=e.x+e.width&&n>=e.y&&n<=e.y+e.height}function Q(e){let t=null,n=-1;for(let r of e){let e=r.width*r.height;e>n&&(t=r,n=e)}return t}function Y(e,t,n,r){return Math.max(e,n)<=Math.min(t,r)}function ee(e){let t=new Map;for(let[n,r]of e.entries())t.set(r.index,n);let n=[],r=[];for(let[i,o]of e.entries()){let e=Math.max(0,o.depth??0);for(;r.length>0&&e<=r[r.length-1].depth;)r.pop();let a="number"==typeof o.parentIndex?t.get(o.parentIndex):void 0,s="number"==typeof a&&a<i?a:r[r.length-1]?.index;n.push({...o,index:i,depth:e,parentIndex:s}),r.push({depth:e,index:i})}return n}function et(e){return new Map(e.map(e=>[e.index,e]))}function en(e){let t=`${e??""}`.toLowerCase();return t.includes("scroll")||t.includes("recyclerview")||t.includes("listview")||t.includes("gridview")||t.includes("collectionview")||"table"===t}function er(e){return!!en(e.type)||`${e.role??""} ${e.subrole??""}`.toLowerCase().includes("scroll")}function ei(e){if(0===e.length)return{nodes:e,hiddenCount:0,summaryLines:[]};let t=et(e),n=new Set,r=[];for(let i of e){if(eo(i,e,t)){!function(e,t,n){let r=e,i=new Set;for(;r&&!i.has(r.index);)i.add(r.index),t.add(r.index),r="number"==typeof r.parentIndex?n.get(r.parentIndex):void 0}(i,n,t);continue}r.push(i)}let i=function(e,t,n,r){let i=new Map,o=new Set;for(let e of t){if(!e.rect)continue;let t=eu(e,n,r);if(!t?.rect)continue;let a=es(e.rect,t.rect);if(!a)continue;let s=i.get(t.index)??new Set;s.add(a),i.set(t.index,s),o.add(e.index)}return function(e,t,n,r){for(let i of e){let e=function(e){let t=function(e,t){if(!(e?.trim().toLowerCase()??"").includes("vertical scroll bar"))return null;let n=function(e){if(!e)return null;let t=/^(\d{1,3})%$/.exec(e.trim());if(!t)return null;let n=Number(t[1]);return Number.isFinite(n)?Math.max(0,Math.min(100,n)):null}(t);return null===n?null:n<=1?{above:!1,below:!0}:n>=99?{above:!0,below:!1}:{above:!0,below:!0}}(e.label,e.value);if(!t)return null;let n=new Set;return t.above&&n.add("above"),t.below&&n.add("below"),n.size>0?n:null}(i);if(!e||0===e.size)continue;let o=eu(i,t,n);if(!o)continue;let a=r.get(o.index)??new Set;for(let t of e)a.add(t);r.set(o.index,a)}}(e,n,r,i),{directionsByContainer:i,coveredNodeIndexes:o}}(e,r,n,t),o=0===n.size?e:e.filter(e=>n.has(e.index));return{nodes:o.map(e=>(function(e,t){let n=t.get(e.index);if(!n||0===n.size)return e;let r=!!(!0===e.hiddenContentAbove||n.has("above"))||void 0,i=!!(!0===e.hiddenContentBelow||n.has("below"))||void 0;return{...e,hiddenContentAbove:r,hiddenContentBelow:i}})(e,i.directionsByContainer)),hiddenCount:0===n.size?0:e.length-o.length,summaryLines:function(e,t,n){let r=new Map;for(let i of e){let e=function(e,t,n){if(!e.rect)return null;let r=ea(e,t,n);return r?es(e.rect,r):null}(i,t,n);if(!e)continue;let o=r.get(e)??[];o.push(i),r.set(e,o)}return["above","below"].flatMap(e=>{let t=r.get(e);if(!t||0===t.length)return[];let n=(function(e){let t=new Set,n=[];for(let r of e){let e=el(r);!e||t.has(e)||(t.add(e),n.push(e))}return n})(t).slice(0,3).map(e=>`"${e}"`),i=1===t.length?"interactive item":"interactive items",o=n.length>0?`: ${n.join(", ")}`:"";return[`[off-screen ${e}] ${t.length} ${i}${o}`]})}(r.filter(e=>!i.coveredNodeIndexes.has(e.index)&&function(e){if(!0===e.hittable)return!0;let t=(e.type??"").toLowerCase();return t.includes("button")||t.includes("link")||t.includes("textfield")||t.includes("edittext")||t.includes("searchfield")||t.includes("checkbox")||t.includes("radio")||t.includes("switch")||t.includes("menuitem")||!!el(e)}(e)),e,t)}}function eo(e,t,n=et(t)){var r;if(!e.rect)return!0;let i=ea(e,t,n);return!i||(r=e.rect,Y(r.x,r.x+r.width,i.x,i.x+i.width)&&Y(r.y,r.y+r.height,i.y,i.y+i.height))}function ea(e,t,n=et(t)){let r=function(e,t){let n="number"==typeof e.parentIndex?t.get(e.parentIndex):void 0,r=new Set;for(;n&&!r.has(n.index);){if(r.add(n.index),n.rect&&er(n))return n.rect;n="number"==typeof n.parentIndex?t.get(n.parentIndex):void 0}return null}(e,n);return r||K(t,e.rect??{x:0,y:0,width:0,height:0})}function es(e,t){return e.y+e.height<=t.y?"above":e.y>=t.y+t.height?"below":null}function el(e){return e.label?.trim()||e.value?.trim()||e.identifier?.trim()||""}function eu(e,t,n){let r="number"==typeof e.parentIndex?n.get(e.parentIndex):void 0,i=new Set;for(;r&&!i.has(r.index);){if(i.add(r.index),t.has(r.index)&&er(r))return r;r="number"==typeof r.parentIndex?n.get(r.parentIndex):void 0}return null}function ed(){try{let e=ec();return JSON.parse(n.readFileSync(i.join(e,"package.json"),"utf8")).version??"0.0.0"}catch{return"0.0.0"}}function ec(){let e=i.dirname(o(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=i.join(t,"package.json");if(n.existsSync(e))return t;t=i.dirname(t)}return e}function ef(e){return e?{message:e}:{}}function ep(e,t){return t?{...e,message:t}:e}function em(e){return"string"==typeof e?.message&&e.message.length>0?e.message:null}async function eh(e,t,n={}){return new Promise((r,i)=>{let o=a(e,t,{cwd:n.cwd,env:n.env,stdio:["pipe","pipe","pipe"],detached:n.detached}),s="",l=n.binaryStdout?Buffer.alloc(0):void 0,u="",d=!1,c=eI(n.timeoutMs),f=c?setTimeout(()=>{d=!0,o.kill("SIGKILL")},c):null;n.binaryStdout||o.stdout.setEncoding("utf8"),o.stderr.setEncoding("utf8"),void 0!==n.stdin&&o.stdin.write(n.stdin),o.stdin.end(),o.stdout.on("data",e=>{n.binaryStdout?l=Buffer.concat([l??Buffer.alloc(0),Buffer.isBuffer(e)?e:Buffer.from(e)]):s+=e}),o.stderr.on("data",e=>{u+=e}),o.on("error",n=>{(f&&clearTimeout(f),"ENOENT"===n.code)?i(new b("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},n)):i(new b("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},n))}),o.on("close",o=>{f&&clearTimeout(f);let a=o??1;d&&c?i(new b("COMMAND_FAILED",`${e} timed out after ${c}ms`,{cmd:e,args:t,stdout:s,stderr:u,exitCode:a,timeoutMs:c})):0===a||n.allowFailure?r({stdout:s,stderr:u,exitCode:a,stdoutBuffer:l}):i(new b("COMMAND_FAILED",`${e} exited with code ${a}`,{cmd:e,args:t,stdout:s,stderr:u,exitCode:a,processExitError:!0}))})})}async function eg(e){try{var t;let{shell:n,args:r}=(t=e,"win32"===process.platform?{shell:"cmd.exe",args:["/c","where",t]}:{shell:"bash",args:["-lc",`command -v ${t}`]}),i=await eh(n,r,{allowFailure:!0});return 0===i.exitCode&&i.stdout.trim().length>0}catch{return!1}}function ew(e,t,n={}){let r=s(e,t,{cwd:n.cwd,env:n.env,stdio:["pipe","pipe","pipe"],encoding:n.binaryStdout?void 0:"utf8",input:n.stdin,timeout:eI(n.timeoutMs)});if(r.error){let i=r.error.code;if("ETIMEDOUT"===i)throw new b("COMMAND_FAILED",`${e} timed out after ${eI(n.timeoutMs)}ms`,{cmd:e,args:t,timeoutMs:eI(n.timeoutMs)},r.error);if("ENOENT"===i)throw new b("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},r.error);throw new b("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},r.error)}let i=n.binaryStdout?Buffer.isBuffer(r.stdout)?r.stdout:Buffer.from(r.stdout??""):void 0,o=n.binaryStdout?"":"string"==typeof r.stdout?r.stdout:(r.stdout??"").toString(),a="string"==typeof r.stderr?r.stderr:(r.stderr??"").toString(),l=r.status??1;if(0!==l&&!n.allowFailure)throw new b("COMMAND_FAILED",`${e} exited with code ${l}`,{cmd:e,args:t,stdout:o,stderr:a,exitCode:l,processExitError:!0});return{stdout:o,stderr:a,exitCode:l,stdoutBuffer:i}}function ev(e,t,n={}){let r=a(e,t,{cwd:n.cwd,env:n.env,stdio:n.stdio??"ignore",detached:!0});return r.unref(),r.pid??0}async function eb(e,t,n={}){return new Promise((r,i)=>{let o=a(e,t,{cwd:n.cwd,env:n.env,stdio:["pipe","pipe","pipe"],detached:n.detached});n.onSpawn?.(o);let s="",l="",u=n.binaryStdout?Buffer.alloc(0):void 0,d=!1,c=eI(n.timeoutMs),f=c?setTimeout(()=>{d=!0,o.kill("SIGKILL")},c):null;n.binaryStdout||o.stdout.setEncoding("utf8"),o.stderr.setEncoding("utf8"),void 0!==n.stdin&&o.stdin.write(n.stdin),o.stdin.end(),o.stdout.on("data",e=>{if(n.binaryStdout){u=Buffer.concat([u??Buffer.alloc(0),Buffer.isBuffer(e)?e:Buffer.from(e)]);return}let t=String(e);s+=t,n.onStdoutChunk?.(t)}),o.stderr.on("data",e=>{let t=String(e);l+=t,n.onStderrChunk?.(t)}),o.on("error",n=>{(f&&clearTimeout(f),"ENOENT"===n.code)?i(new b("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},n)):i(new b("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},n))}),o.on("close",o=>{f&&clearTimeout(f);let a=o??1;d&&c?i(new b("COMMAND_FAILED",`${e} timed out after ${c}ms`,{cmd:e,args:t,stdout:s,stderr:l,exitCode:a,timeoutMs:c})):0===a||n.allowFailure?r({stdout:s,stderr:l,exitCode:a,stdoutBuffer:u}):i(new b("COMMAND_FAILED",`${e} exited with code ${a}`,{cmd:e,args:t,stdout:s,stderr:l,exitCode:a,processExitError:!0}))})})}function eS(e,t,n={}){let r=a(e,t,{cwd:n.cwd,env:n.env,stdio:["ignore","pipe","pipe"],detached:n.detached}),i="",o="";r.stdout.setEncoding("utf8"),r.stderr.setEncoding("utf8"),r.stdout.on("data",e=>{i+=e}),r.stderr.on("data",e=>{o+=e});let s=new Promise((a,s)=>{r.on("error",n=>{"ENOENT"===n.code?s(new b("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},n)):s(new b("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},n))}),r.on("close",r=>{let l=r??1;0===l||n.allowFailure?a({stdout:i,stderr:o,exitCode:l}):s(new b("COMMAND_FAILED",`${e} exited with code ${l}`,{cmd:e,args:t,stdout:i,stderr:o,exitCode:l,processExitError:!0}))})});return{child:r,wait:s}}function eI(e){if(!Number.isFinite(e))return;let t=Math.floor(e);if(!(t<=0))return t}let ey=[/(^|[/\s"'=])dist\/src\/daemon\.js($|[\s"'])/,/(^|[/\s"'=])src\/daemon\.ts($|[\s"'])/];function ex(e){if(!Number.isInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch(e){return"EPERM"===e.code}}function eA(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=ew("ps",["-p",String(e),"-o","lstart="],{allowFailure:!0,timeoutMs:1e3});if(0!==t.exitCode)return null;let n=t.stdout.trim();return n.length>0?n:null}catch{return null}}function e$(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=ew("ps",["-p",String(e),"-o","command="],{allowFailure:!0,timeoutMs:1e3});if(0!==t.exitCode)return null;let n=t.stdout.trim();return n.length>0?n:null}catch{return null}}function eN(e,t){let n;if(!ex(e))return!1;if(t){let n=eA(e);if(!n||n!==t)return!1}let r=e$(e);return!!r&&!!(n=r.toLowerCase().replaceAll("\\","/")).includes("agent-device")&&ey.some(e=>e.test(n))}function eE(e,t){try{return process.kill(e,t),!0}catch(t){let e=t.code;if("ESRCH"===e||"EPERM"===e)return!1;throw t}}async function eD(e,t){if(!ex(e))return!0;let n=Date.now();for(;Date.now()-n<t;)if(await new Promise(e=>setTimeout(e,50)),!ex(e))return!0;return!ex(e)}async function eM(e,t){!eN(e,t.expectedStartTime)||!eE(e,"SIGTERM")||await eD(e,t.termTimeoutMs)||eE(e,"SIGKILL")&&await eD(e,t.killTimeoutMs)}function eL(e){return e?.HOME?.trim()||r.homedir()}function eT(e,t={}){return"~"===e?eL(t.env):e.startsWith("~/")?i.join(eL(t.env),e.slice(2)):e}function eO(e,t={}){let n=eT(e,t);return i.isAbsolute(n)?n:i.resolve(t.cwd??process.cwd(),n)}function eC(e){let t,n=(t=(e??"").trim())?eO(t):i.join(eT("~"),".agent-device");return{baseDir:n,infoPath:i.join(n,"daemon.json"),lockPath:i.join(n,"daemon.lock"),logPath:i.join(n,"daemon.log"),sessionsDir:i.join(n,"sessions")}}function e_(e){let t=(e??"").trim().toLowerCase();return"http"===t?"http":"dual"===t?"dual":"socket"}function ek(e){let t=(e??"").trim().toLowerCase();return"auto"===t?"auto":"socket"===t?"socket":"http"===t?"http":"auto"}function eR(e){return"tenant"===(e??"").trim().toLowerCase()?"tenant":"none"}function eP(e){if(!e)return;let t=e.trim();if(t&&/^[a-zA-Z0-9._-]{1,128}$/.test(t))return t}let eF=100,eB=new Set(["batch","replay"]),ez=new Set(["command","positionals","flags","runtime"]);function ej(e){let t;try{t=JSON.parse(e)}catch{throw new b("INVALID_ARGS","Batch steps must be valid JSON.")}if(!Array.isArray(t)||0===t.length)throw new b("INVALID_ARGS","Batch steps must be a non-empty JSON array.");return t}function eG(e,t){if(!Array.isArray(e)||0===e.length)throw new b("INVALID_ARGS","batch requires a non-empty batchSteps array.");if(e.length>t)throw new b("INVALID_ARGS",`batch has ${e.length} steps; max allowed is ${t}.`);let n=[];for(let t=0;t<e.length;t+=1){let r=e[t];if(!r||"object"!=typeof r)throw new b("INVALID_ARGS",`Invalid batch step at index ${t}.`);let i=Object.keys(r).filter(e=>!ez.has(e));if(i.length>0){let e=i.map(e=>`"${e}"`).join(", ");throw new b("INVALID_ARGS",`Batch step ${t+1} has unknown field(s): ${e}. Allowed fields: command, positionals, flags, runtime.`)}let o="string"==typeof r.command?r.command.trim().toLowerCase():"";if(!o)throw new b("INVALID_ARGS",`Batch step ${t+1} requires command.`);if(eB.has(o))throw new b("INVALID_ARGS",`Batch step ${t+1} cannot run ${o}.`);if(void 0!==r.positionals&&!Array.isArray(r.positionals))throw new b("INVALID_ARGS",`Batch step ${t+1} positionals must be an array.`);let a=r.positionals??[];if(a.some(e=>"string"!=typeof e))throw new b("INVALID_ARGS",`Batch step ${t+1} positionals must contain only strings.`);if(void 0!==r.flags&&("object"!=typeof r.flags||Array.isArray(r.flags)||!r.flags))throw new b("INVALID_ARGS",`Batch step ${t+1} flags must be an object.`);if(void 0!==r.runtime&&("object"!=typeof r.runtime||Array.isArray(r.runtime)||!r.runtime))throw new b("INVALID_ARGS",`Batch step ${t+1} runtime must be an object.`);n.push({command:o,positionals:a,flags:r.flags??{},runtime:r.runtime})}return n}function eV(e){let t=e.appId??e.bundleId??e.packageName;return{session:e.session,appId:t,appBundleId:e.bundleId,package:e.packageName}}function eU(e,t,n){return{deviceId:t,deviceName:n,..."android"===e?{serial:t}:"ios"===e?{udid:t}:{}}}function eq(e,t={}){let n=t.includeAndroidSerial??!0;return{platform:e.platform,target:e.target,device:e.name,id:e.id,..."ios"===e.platform?{device_udid:e.ios?.udid??e.id,ios_simulator_device_set:e.ios?.simulatorSetPath??null}:{},..."android"===e.platform&&n?{serial:e.android?.serial??e.id}:{}}}function eH(e){return{name:e.name,...eq(e.device,{includeAndroidSerial:!1}),createdAt:e.createdAt}}function eW(e){return{platform:e.platform,id:e.id,name:e.name,kind:e.kind,target:e.target,..."boolean"==typeof e.booted?{booted:e.booted}:{}}}function eJ(e){let t=e.created?"Created":"Reused",n=e.booted?" (booted)":"";return ep({udid:e.udid,device:e.device,runtime:e.runtime,ios_simulator_device_set:e.iosSimulatorDeviceSet??null,created:e.created,booted:e.booted},`${t}: ${e.device} ${e.udid}${n}`)}function eK(e){return e.bundleId??e.package??e.app}function eX(e){return ep({app:e.app,appPath:e.appPath,platform:e.platform,...e.appId?{appId:e.appId}:{},...e.bundleId?{bundleId:e.bundleId}:{},...e.package?{package:e.package}:{}},`Installed: ${eK(e)}`)}function eZ(e){return e.appName??e.bundleId??e.packageName??e.launchTarget}function eQ(e){return ep({launchTarget:e.launchTarget,...e.appName?{appName:e.appName}:{},...e.appId?{appId:e.appId}:{},...e.bundleId?{bundleId:e.bundleId}:{},...e.packageName?{package:e.packageName}:{},...e.installablePath?{installablePath:e.installablePath}:{},...e.archivePath?{archivePath:e.archivePath}:{},...e.materializationId?{materializationId:e.materializationId}:{},...e.materializationExpiresAt?{materializationExpiresAt:e.materializationExpiresAt}:{}},`Installed: ${eZ(e)}`)}function eY(e){let t=e.appName??e.appBundleId??e.session;return ep({session:e.session,...e.appName?{appName:e.appName}:{},...e.appBundleId?{appBundleId:e.appBundleId}:{},...e.startup?{startup:e.startup}:{},...e.runtime?{runtime:e.runtime}:{},...e.device?eq(e.device):{}},t?`Opened: ${t}`:"Opened")}function e0(e){return{session:e.session,...e.shutdown?{shutdown:e.shutdown}:{},...ef(e.session?`Closed: ${e.session}`:"Closed")}}function e1(e){return{nodes:e.nodes,truncated:e.truncated,...e.appName?{appName:e.appName}:{},...e.appBundleId?{appBundleId:e.appBundleId}:{},...e.visibility?{visibility:e.visibility}:{},...e.warnings&&e.warnings.length>0?{warnings:e.warnings}:{}}}function e8(e,t){try{return l.sync.read(e)}catch(e){throw new b("COMMAND_FAILED",`Failed to decode ${t} as PNG`,{label:t,reason:e instanceof Error?e.message:String(e)})}}export{b as AppError,eF as DEFAULT_BATCH_MAX_STEPS,_ as SESSION_SURFACES,T as SETTINGS_INVALID_ARGS_MESSAGE,L as SETTINGS_USAGE_OVERRIDE,S as asAppError,q as attachRefs,eV as buildAppIdentifiers,eU as buildDeviceIdentifiers,ei as buildMobileSnapshotPresentation,B as buildSnapshotDisplayLines,J as centerOfRect,Z as containsPoint,f as createRequestId,e8 as decodePng,j as displayLabel,X as distanceFromSafeViewportBand,h as emitDiagnostic,eT as expandUserHomePath,R as extractReadableText,W as findNodeByRef,ec as findProjectRoot,w as flushDiagnosticsToSessionFile,G as formatRole,z as formatSnapshotLine,m as getDiagnosticsMeta,C as getUnsupportedMacOsSettingMessage,eN as isAgentDeviceDaemonProcess,O as isMacOsSettingSupported,eo as isNodeVisibleInEffectiveViewport,ex as isProcessAlive,en as isScrollableType,I as normalizeError,H as normalizeRef,ee as normalizeSnapshotTree,eP as normalizeTenantId,ej as parseBatchStepsJson,k as parseSessionSurface,Q as pickLargestRect,em as readCommandMessage,e$ as readProcessCommand,eA as readProcessStartTime,ed as readVersion,eC as resolveDaemonPaths,e_ as resolveDaemonServerMode,ek as resolveDaemonTransportPreference,eK as resolveDeployResultTarget,ea as resolveEffectiveViewportRect,eZ as resolveInstallFromSourceResultTarget,eR as resolveSessionIsolationMode,eO as resolveUserPath,K as resolveViewportRect,eh as runCmd,eS as runCmdBackground,ev as runCmdDetached,eb as runCmdStreaming,ew as runCmdSync,e0 as serializeCloseResult,eX as serializeDeployResult,eW as serializeDevice,eJ as serializeEnsureSimulatorResult,eQ as serializeInstallFromSourceResult,eY as serializeOpenResult,eH as serializeSessionListEntry,e1 as serializeSnapshotResult,eM as stopProcessForTakeover,ef as successText,eG as validateAndNormalizeBatchSteps,eg as whichCmd,g as withDiagnosticTimer,p as withDiagnosticsScope,ep as withSuccessText};
|