agent-device 0.11.0 → 0.11.2
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/dist/src/1~rslib-runtime.js +1 -0
- package/dist/src/916.js +3 -0
- package/dist/src/bin.js +58 -64
- package/dist/src/daemon.js +41 -40
- package/dist/src/index.d.ts +19 -0
- package/dist/src/index.js +3 -3
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +14 -3
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +10 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +1 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +19 -7
- package/macos-helper/Sources/AgentDeviceMacOSHelper/SnapshotTraversal.swift +235 -34
- package/macos-helper/Sources/AgentDeviceMacOSHelper/main.swift +121 -2
- package/package.json +14 -14
- package/skills/agent-device/references/exploration.md +13 -0
- package/skills/agent-device/references/macos-desktop.md +3 -2
- package/skills/agent-device/references/verification.md +3 -1
- package/dist/src/36.js +0 -3
package/dist/src/index.d.ts
CHANGED
|
@@ -175,6 +175,7 @@ declare type ApplePlatform = 'ios' | 'macos';
|
|
|
175
175
|
declare type AppOpenOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
|
|
176
176
|
app: string;
|
|
177
177
|
url?: string;
|
|
178
|
+
surface?: 'app' | 'frontmost-app' | 'desktop' | 'menubar';
|
|
178
179
|
activity?: string;
|
|
179
180
|
relaunch?: boolean;
|
|
180
181
|
saveScript?: boolean | string;
|
|
@@ -202,10 +203,12 @@ declare type BatchStep = {
|
|
|
202
203
|
|
|
203
204
|
declare type CaptureScreenshotOptions = AgentDeviceRequestOverrides & {
|
|
204
205
|
path?: string;
|
|
206
|
+
overlayRefs?: boolean;
|
|
205
207
|
};
|
|
206
208
|
|
|
207
209
|
declare type CaptureScreenshotResult = {
|
|
208
210
|
path: string;
|
|
211
|
+
overlayRefs?: ScreenshotOverlayRef[];
|
|
209
212
|
identifiers: AgentDeviceIdentifiers;
|
|
210
213
|
};
|
|
211
214
|
|
|
@@ -276,6 +279,8 @@ declare type CliFlags = {
|
|
|
276
279
|
snapshotDepth?: number;
|
|
277
280
|
snapshotScope?: string;
|
|
278
281
|
snapshotRaw?: boolean;
|
|
282
|
+
overlayRefs?: boolean;
|
|
283
|
+
screenshotFullscreen?: boolean;
|
|
279
284
|
baseline?: string;
|
|
280
285
|
threshold?: string;
|
|
281
286
|
appsFilter?: 'user-installed' | 'all';
|
|
@@ -309,6 +314,7 @@ declare type CliFlags = {
|
|
|
309
314
|
timeoutMs?: number;
|
|
310
315
|
retries?: number;
|
|
311
316
|
artifactsDir?: string;
|
|
317
|
+
reportJunit?: string;
|
|
312
318
|
steps?: string;
|
|
313
319
|
stepsFile?: string;
|
|
314
320
|
batchOnError?: 'stop';
|
|
@@ -490,6 +496,11 @@ declare type Platform = ApplePlatform | 'android';
|
|
|
490
496
|
|
|
491
497
|
declare type PlatformSelector = Platform | 'apple';
|
|
492
498
|
|
|
499
|
+
declare type Point = {
|
|
500
|
+
x: number;
|
|
501
|
+
y: number;
|
|
502
|
+
};
|
|
503
|
+
|
|
493
504
|
declare type PrepareMetroRuntimeResult = {
|
|
494
505
|
projectRoot: string;
|
|
495
506
|
kind: ResolvedMetroKind;
|
|
@@ -536,6 +547,14 @@ declare type Rect = {
|
|
|
536
547
|
|
|
537
548
|
declare type ResolvedMetroKind = Exclude<MetroPrepareKind, 'auto'>;
|
|
538
549
|
|
|
550
|
+
declare type ScreenshotOverlayRef = {
|
|
551
|
+
ref: string;
|
|
552
|
+
label?: string;
|
|
553
|
+
rect: Rect;
|
|
554
|
+
overlayRect: Rect;
|
|
555
|
+
center: Point;
|
|
556
|
+
};
|
|
557
|
+
|
|
539
558
|
declare type SessionCloseResult = {
|
|
540
559
|
session: string;
|
|
541
560
|
shutdown?: Record<string, unknown>;
|
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import e from"node:net";import t from"node:http";import r from"node:https";import n from"node:fs";import o from"node:path";import{AsyncLocalStorage as a}from"node:async_hooks";import i from"node:crypto";import s from"node:os";import{spawn as l,spawnSync as
|
|
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 I(e,t,r={}){let n=d(e,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"],encoding:r.binaryStdout?void 0:"utf8",input:r.stdin,timeout:y(r.timeoutMs)});if(n.error){let o=n.error.code;if("ETIMEDOUT"===o)throw new w("COMMAND_FAILED",`${e} timed out after ${y(r.timeoutMs)}ms`,{cmd:e,args:t,timeoutMs:y(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 y(e){if(!Number.isFinite(e))return;let t=Math.floor(e);if(!(t<=0))return t}function b(){let e=o.dirname(u(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 v=[/(^|[\/\s"'=])dist\/src\/daemon\.js($|[\s"'])/,/(^|[\/\s"'=])src\/daemon\.ts($|[\s"'])/];function A(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(!A(e))return!1;if(t){let r=function(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=I("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=I("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")&&v.some(e=>e.test(r))}function E(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(!A(e))return!0;let r=Date.now();for(;Date.now()-r<t;)if(await new Promise(e=>setTimeout(e,50)),!A(e))return!0;return!A(e)}async function S(e,t){!D(e,t.expectedStartTime)||!E(e,"SIGTERM")||await M(e,t.termTimeoutMs)||E(e,"SIGKILL")&&await M(e,t.killTimeoutMs)}function _(e){return e?.HOME?.trim()||s.homedir()}function P(e,t={}){return"~"===e?_(t.env):e.startsWith("~/")?o.join(_(t.env),e.slice(2)):e}function k(e,t={}){let r=P(e,t);return o.isAbsolute(r)?r:o.resolve(t.cwd??process.cwd(),r)}function T(e){let t,r=(t=(e??"").trim())?k(t):o.join(P("~"),".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 N(e){let{localPath:a,baseUrl:i,token:s}=e,d=n.statSync(a).isDirectory(),u=o.basename(a),c=new URL("upload",i.endsWith("/")?i:`${i}/`),m="https:"===c.protocol?r:t,p={"x-artifact-type":d?"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,t)=>{let r=m.request({protocol:c.protocol,host:c.hostname,port:c.port,method:"POST",path:c.pathname+c.search,headers:p},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))}),d){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 R=function(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}(),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}(),U=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 x(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:T(n),transportPreference:s,serverMode:l,remoteBaseUrl:o,remoteAuthToken:a}}(e),o=await g("daemon_startup",async()=>await j(n),{requestId:t,session:e.session}),a=await L(e,o),i={...e,positionals:a.positionals,flags:a.flags,token:o.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,...a.uploadedArtifactId?{uploadedArtifactId:a.uploadedArtifactId}:{},...a.clientArtifactPaths?{clientArtifactPaths:a.clientArtifactPaths}:{},...a.installSource?{installSource:a.installSource}:{}}};return h({level:"info",phase:"daemon_request_prepare",data:{requestId:t,command:e.command,session:e.session}}),await g("daemon_request",async()=>await ee(o,i,n.transportPreference),{requestId:t,command:e.command})}async function L(e,t){let r,a=[...e.positionals??[]],i=e.flags?{...e.flags}:void 0,s=e.meta?.installSource,l={};if(es(t)){let n=function(e,t){if("screenshot"===e.command){let r=F(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=F(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 d=await $(e,t);d&&(s=d.installSource,r=d.uploadedArtifactId??r)}if(!es(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 d=a[1];if(d.startsWith("remote:"))return a[1]=d.slice(7),{positionals:a,flags:i,...Object.keys(l).length>0?{clientArtifactPaths:l}:{}};let u=o.isAbsolute(d)?d:o.resolve(e.meta?.cwd??process.cwd(),d);return n.existsSync(u)?{positionals:a,flags:i,installSource:s,uploadedArtifactId:r=await N({localPath:u,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 $(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 N({localPath:i,baseUrl:t.baseUrl,token:t.token});return{installSource:{...r,path:i},uploadedArtifactId:s}}function F(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 j(e){let t;if(e.remoteBaseUrl){let t={transport:"http",token:e.remoteAuthToken??"",pid:0,baseUrl:e.remoteBaseUrl};if(await Y(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=H(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=Z()).useSrc?t.srcPath:t.distPath,t.root),s=!!r&&await Y(r,e.transportPreference);if(r&&r.version===a&&r.codeSignature===i&&s)return r;r&&(r.version!==a||r.codeSignature!==i||!s)&&(await z(r),X(e.paths.infoPath)),function(e){let t=K(e);if(!t.hasLock||t.hasInfo)return;let r=J(e.lockPath);if(!r)return X(e.lockPath);D(r.pid,r.processStartTime)||X(e.lockPath)}(e.paths);let l=0;for(let t=1;t<=U;t+=1){await Q(e);let r=await B(O,e);if(r)return r;if(await V(e.paths)){l+=1;continue}let n=K(e.paths);if(!(t<U))break;if(!n.hasInfo&&!n.hasLock){await G(150);continue}}let d=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:U,lockRecoveryCount:l,metadataState:d,hint:function(e,t=T(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.`}(d,e.paths)})}async function B(e,t){let r=Date.now();for(;Date.now()-r<e;){let e=H(t.paths.infoPath);if(e&&await Y(e,t.transportPreference))return e;await new Promise(e=>setTimeout(e,100))}return null}async function G(e){await new Promise(t=>setTimeout(t,e))}async function V(e){let t=K(e);if(!t.hasLock||t.hasInfo)return!1;let r=J(e.lockPath);return r&&D(r.pid,r.processStartTime)&&await S(r.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:r.processStartTime}),X(e.lockPath),!0}async function z(e){await S(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}function H(e){let t=W(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,d=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:d?Number(t.pid):0,version:i,codeSignature:s,processStartTime:l}}function J(e){let t=W(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 W(e){if(!n.existsSync(e))return null;try{return JSON.parse(n.readFileSync(e,"utf8"))}catch{return null}}function X(e){try{n.existsSync(e)&&n.unlinkSync(e)}catch{}}async function Y(n,o){var a;return"http"===et(n,o)?await function(e){let n=e.baseUrl?el(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 Q(e){let t=Z(),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};!function(e,t,r={}){l(e,t,{cwd:r.cwd,env:r.env,stdio:"ignore",detached:!0}).unref()}(process.execPath,r,{env:n})}function Z(){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 ee(e,t,r){return"http"===et(e,r)?await eo(e,t):await en(e,t)}function et(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(er(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=>er(e,t));if(o)return o;throw new w("COMMAND_FAILED","Daemon metadata has no reachable transport")}function er(e,t){return"http"===t?!!e.httpPort:!!e.port}async function en(t,r){let n=t.port;if(!n)throw new w("COMMAND_FAILED","Daemon socket endpoint is unavailable");return new Promise((o,a)=>{let i=e.createConnection({host:"127.0.0.1",port:n},()=>{i.write(`${JSON.stringify(r)}
|
|
3
|
-
`)}),s=setTimeout(()=>{i.destroy();let e=ea(),n=ei(t,T(r.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR));h({level:"error",phase:"daemon_request_timeout",data:{timeoutMs:R,requestId:r.meta?.requestId,command:r.command,timedOutRunnerPidsTerminated:e.terminated,timedOutRunnerCleanupError:e.error,daemonPidReset:t.pid,daemonPidForceKilled:n.forcedKill}}),a(new w("COMMAND_FAILED","Daemon request timed out",{timeoutMs:R,requestId:r.meta?.requestId,hint:"Retry with --debug and check daemon diagnostics logs. Timed-out iOS runner xcodebuild processes were terminated when detected."}))},R),l="";i.setEncoding("utf8"),i.on("data",e=>{let t=(l+=e).indexOf("\n");if(-1===t)return;let n=l.slice(0,t).trim();if(n)try{let e=JSON.parse(n);i.end(),clearTimeout(s),o(e)}catch(e){clearTimeout(s),a(new w("COMMAND_FAILED","Invalid daemon response",{requestId:r.meta?.requestId,line:n},e instanceof Error?e:void 0))}}),i.on("error",e=>{clearTimeout(s),h({level:"error",phase:"daemon_request_socket_error",data:{requestId:r.meta?.requestId,message:e instanceof Error?e.message:String(e)}}),a(new w("COMMAND_FAILED","Failed to communicate with daemon",{requestId:r.meta?.requestId,hint:"Retry command. If this persists, clean stale daemon metadata and start a fresh session."},e))})})}async function eo(e,n){let o=e.baseUrl?new URL(el(e.baseUrl,"rpc")):e.httpPort?new URL(`http://127.0.0.1:${e.httpPort}/rpc`):null;if(!o)throw new w("COMMAND_FAILED","Daemon HTTP endpoint is unavailable");let a=JSON.stringify({jsonrpc:"2.0",id:n.meta?.requestId??f(),method:"agent_device.command",params:n}),i={"content-type":"application/json","content-length":Buffer.byteLength(a)};return e.baseUrl&&e.token&&(i.authorization=`Bearer ${e.token}`,i["x-agent-device-token"]=e.token),await new Promise((s,l)=>{let d=T(n.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),u=("https:"===o.protocol?r:t).request({protocol:o.protocol,host:o.hostname,port:o.port,method:"POST",path:o.pathname+o.search,headers:i},t=>{let r="";t.setEncoding("utf8"),t.on("data",e=>{r+=e}),t.on("end",()=>{clearTimeout(c);try{let t=JSON.parse(r);if(t.error){let e=t.error.data??{};l(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 l(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(s).catch(l);s(t.result)}catch(e){clearTimeout(c),l(new w("COMMAND_FAILED","Invalid daemon response",{requestId:n.meta?.requestId,line:r},e instanceof Error?e:void 0))}})}),c=setTimeout(()=>{u.destroy();let t=es(e)?{terminated:0}:ea(),r=es(e)?{forcedKill:!1}:ei(e,d);h({level:"error",phase:"daemon_request_timeout",data:{timeoutMs:R,requestId:n.meta?.requestId,command:n.command,timedOutRunnerPidsTerminated:t.terminated,timedOutRunnerCleanupError:t.error,daemonPidReset:es(e)?void 0:e.pid,daemonPidForceKilled:es(e)?void 0:r.forcedKill,daemonBaseUrl:e.baseUrl}}),l(new w("COMMAND_FAILED","Daemon request timed out",{timeoutMs:R,requestId:n.meta?.requestId,hint:es(e)?"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."}))},R);u.on("error",t=>{clearTimeout(c),h({level:"error",phase:"daemon_request_socket_error",data:{requestId:n.meta?.requestId,message:t instanceof Error?t.message:String(t)}}),l(new w("COMMAND_FAILED","Failed to communicate with daemon",{requestId:n.meta?.requestId,hint:es(e)?"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."},t))}),u.write(a),u.end()})}function ea(){let e=0;try{for(let t of C){let r=I("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)}}}function ei(e,t){let r=!1;try{D(e.pid,e.processStartTime)&&(process.kill(e.pid,"SIGKILL"),r=!0)}catch{S(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}finally{X(t.infoPath),X(t.lockPath)}return{forcedKill:r}}function es(e){return"string"==typeof e.baseUrl&&e.baseUrl.length>0}function el(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 eu({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 eu(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())),d="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??R,i=a=>{if(!o){if(o=!0,clearTimeout(u),a)return void n.promises.rm(e.destinationPath,{force:!0}).finally(()=>r(a));t()}},s=d.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)}),u=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 ec(e){return e.replace(/\/+$/,"")}function em(e){return"string"==typeof e&&e.trim()?ec(e.trim()):""}function ep(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function ef(e,t,r){return k(e,{env:t,cwd:r})}function eh(e){try{return n.accessSync(e,n.constants.F_OK),!0}catch{return!1}}function eg(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 ew(e,t){let r;return{platform:t,bundleUrl:((r=new URL(`${ec(e)}/index.bundle`)).searchParams.set("platform",t),r.searchParams.set("dev","true"),r.searchParams.set("minify","false"),r.toString())}}function eI(e,t){return{platform:t,metroHost:ep(e?.metro_host),metroPort:e?.metro_port,bundleUrl:ep(e?.metro_bundle_url),launchUrl:ep(e?.launch_url)}}function ey(e){return`'${e.replace(/'/g,"'\"'\"'")}'`}async function eb(e){await new Promise(t=>setTimeout(t,e))}async function ev(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 eA(e,t){try{let r=await ev(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:eI(n.ios_runtime,"ios"),androidRuntime:eI(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 eE(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 eA(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=ef(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(!eh(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,t){if(null==e||""===e)return 8081;let r=Number.parseInt(String(e),10);if(!Number.isInteger(r)||r<1||r>65535)throw new w("INVALID_ARGS",`Invalid Metro port: ${String(e)}. Use 1-65535.`);return r}(e.metroPort??8081,0),l=ep(e.listenHost)??"0.0.0.0",d=ep(e.statusHost)??"127.0.0.1",u=em(e.publicBaseUrl),c=eg(e.startupTimeoutMs,18e4,3e4),m=eg(e.probeTimeoutMs,1e4,1e3),p=e.reuseExisting??!0,f=e.installDependenciesIfNeeded??!0,h=e.runtimeFilePath?ef(e.runtimeFilePath,t,r):null,g=ef(e.logPath??o.join(a,".agent-device","metro.log"),t,r);if(!u)throw new w("INVALID_ARGS","metro prepare requires --public-base-url <url>.");let{proxyEnabled:y,proxyBaseUrl:b,proxyBearerToken:v}=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}}(em(e.proxyBaseUrl),ep(e.proxyBearerToken)??""),A=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=eh(o.join(e,"pnpm-lock.yaml"))?{command:"pnpm",installArgs:["install"]}:eh(o.join(e,"yarn.lock"))?{command:"yarn",installArgs:["install"]}:{command:"npm",installArgs:["install"]};return I(r.command,r.installArgs,{cwd:e,env:t}),{installed:!0,packageManager:r.command}}(a,t):{installed:!1},D=`http://${d}:${s}/status`,E=!1,M=!1,S=0;if(p&&await eA(D,m))M=!0;else if(E=!0,S=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 d=[ey(l.command),...l.installArgs.map(ey)].join(" "),u=I("/bin/sh",["-c",`nohup ${d} >> ${ey(i)} 2>&1 < /dev/null & echo $!`],{cwd:e,env:s}),c=Number.parseInt(u.stdout.trim(),10);if(!Number.isInteger(c)||c<=0)throw Error(`Failed to start Metro. Expected a child PID in stdout, got "${u.stdout.trim()}".`);return{pid:c}}(a,i,s,l,g,t).pid,!await eE(D,c,m))throw Error(`Metro did not become ready at ${D} within ${c}ms. Check ${g}.`);let _=ew(u,"ios"),P=ew(u,"android"),k=null,T=null;if(y)try{k=await eD({baseUrl:b,bearerToken:v,runtime:{metro_bundle_url:_.bundleUrl},timeoutMs:m})}catch(e){T=e instanceof Error?e.message:String(e)}if(y&&(!k||!1===k.probe.reachable)){var N,R;let e;throw Error((N=T,R=k,e=[`Metro bridge is required for this run but could not be configured via ${b}/api/metro/bridge.`],N&&e.push(`bridgeError=${N}`),R?.probe.reachable===!1&&e.push(`bridgeProbe=${R.probe.detail||`unreachable (status ${R.probe.statusCode||0})`}`),e.join(" ")))}let O=k?.iosRuntime??_,U=k?.androidRuntime??P,C={projectRoot:a,kind:i,dependenciesInstalled:A.installed,packageManager:A.packageManager??null,started:E,reused:M,pid:S,logPath:g,statusUrl:D,runtimeFilePath:h,iosRuntime:O,androidRuntime:U,bridge:k};return h&&(n.mkdirSync(o.dirname(h),{recursive:!0}),n.writeFileSync(h,JSON.stringify(C,null,2))),C}function eS(e){let t=e.appId??e.bundleId??e.packageName;return{session:e.session,appId:t,appBundleId:e.bundleId,package:e.packageName}}function e_(e,t,r){return{deviceId:t,deviceName:r,..."android"===e?{serial:t}:"ios"===e?{udid:t}:{}}}function eP(e,t){let r=ex(e,"bundleId"),n=ex(e,"package");return{app:eC(e,"app"),appPath:eC(e,"appPath"),platform:eL(e,"platform"),appId:r??n,bundleId:r,package:n,identifiers:eS({session:t,bundleId:r,packageName:n})}}function ek(e){var t;let r=eO(e),n=eL(r,"platform"),o=eC(r,"id"),a=eC(r,"name");return{platform:n,target:e$(r,"target"),kind:eF(r,t="kind",eV,`Daemon response has invalid "${t}".`),id:o,name:a,booted:"boolean"==typeof r.booted?r.booted:void 0,identifiers:e_(n,o,a),ios:"ios"===n?{udid:o}:void 0,android:"android"===n?{serial:o}:void 0}}function eT(e){var t;let r=eO(e),n=eL(r,"platform"),o=eC(r,"id"),a=eC(r,"name"),i=e$(r,"target"),s=eC(r,"device"),l={session:a,...e_(n,o,s)};return{name:a,createdAt:eF(r,t="createdAt",eB,`Daemon response is missing numeric "${t}".`),device:{platform:n,target:i,id:o,name:s,identifiers:l,ios:"ios"===n?{udid:o,simulatorSetPath:eq(r,"ios_simulator_device_set",ej)}:void 0,android:"android"===n?{serial:o}:void 0},identifiers:l}}function eN(e,t){return t??e??"default"}function eR(e){let t={};for(let[r,n]of Object.entries(e))void 0!==n&&(t[r]=n);return t}function eO(e){if(!eU(e))throw new w("COMMAND_FAILED","Daemon returned an unexpected response shape.",{value:e});return e}function eU(e){return"object"==typeof e&&null!==e}function eC(e,t){return eF(e,t,ej,`Daemon response is missing "${t}".`)}function ex(e,t){return ej(e[t])}function eL(e,t){return eF(e,t,eG,`Daemon response has invalid "${t}".`)}function e$(e,t){return ez(e[t])??"mobile"}function eF(e,t,r,n){let o=r(e[t]);if(void 0===o)throw new w("COMMAND_FAILED",n,{response:e});return o}function eq(e,t,r){let n=e[t];return null===n?null:r(n)}function ej(e){return"string"==typeof e&&e.length>0?e:void 0}function eB(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function eG(e){return"ios"===e||"macos"===e||"android"===e?e:void 0}function eV(e){return"simulator"===e||"emulator"===e||"device"===e?e:void 0}function ez(e){return"tv"===e||"mobile"===e||"desktop"===e?e:void 0}function eH(e={},t={}){let r=t.transport??x,n=async(t,n=[],o={})=>{let a={...e,...o},i=await r({session:eN(e.session,o.session),command:t,positionals:n,flags:eR({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,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,verbose:a.debug}),runtime:a.runtime,meta:eR({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(eT)};return{devices:{list:async(e={})=>{let t=await n("devices",[],e);return(Array.isArray(t.devices)?t.devices:[]).map(ek)}},sessions:{list:async(e={})=>await o(e),close:async(t={})=>{let r=eN(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=eC(o,"udid"),i=eC(o,"device");return{udid:a,device:i,runtime:eC(o,"runtime"),created:!0===o.created,booted:!0===o.booted,iosSimulatorDeviceSet:eq(o,"ios_simulator_device_set",ej),identifiers:{deviceId:a,deviceName:i,udid:a}}}},apps:{install:async t=>eP(await n("install",[t.app,t.appPath],t),eN(e.session,t.session)),reinstall:async t=>eP(await n("reinstall",[t.app,t.appPath],t),eN(e.session,t.session)),installFromSource:async t=>(function(e,t){let r=ex(e,"bundleId"),n=ex(e,"packageName"),o=r??n??ex(e,"appId"),a=ex(e,"launchTarget")??n??r??o;if(!a)throw new w("COMMAND_FAILED",'Daemon response is missing "launchTarget".',{response:e});return{appName:ex(e,"appName"),appId:o,bundleId:r,packageName:n,launchTarget:a,installablePath:ex(e,"installablePath"),archivePath:ex(e,"archivePath"),materializationId:ex(e,"materializationId"),materializationExpiresAt:ex(e,"materializationExpiresAt"),identifiers:eS({session:t,bundleId:r,packageName:n,appId:o})}})(await n("install_source",[],{...t,installSource:t.source,retainMaterializedPaths:t.retainPaths,materializedPathRetentionMs:t.retentionMs}),eN(e.session,t.session)),open:async t=>{let r=eN(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=ex(e,"id"),n=ex(e,"device");if("ios"!==t&&"macos"!==t&&"android"!==t||!r||!n)return;let o=e$(e,"target"),a=e_(t,r,n);return{platform:t,target:o,id:r,name:n,identifiers:a,ios:"ios"===t?{udid:ex(e,"device_udid")??r,simulatorSetPath:eq(e,"ios_simulator_device_set",ej)}:void 0,android:"android"===t?{serial:ex(e,"serial")??r}:void 0}}(a),s=ex(a,"appBundleId");return{session:r,appName:ex(a,"appName"),appBundleId:s,appId:s,startup:function(e){if(eU(e)&&"number"==typeof e.durationMs&&"string"==typeof e.measuredAt&&"string"==typeof e.method)return{durationMs:e.durationMs,measuredAt:e.measuredAt,method:e.method,appTarget:ex(e,"appTarget"),appBundleId:ex(e,"appBundleId")}}(a.startup),runtime:function(e){if(!eU(e))return;let t=e.platform,r=ex(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:ex(e,"bundleUrl"),launchUrl:ex(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=eN(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:eC(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=eN(e.session,t.session),a=await n("snapshot",[],t),i=ex(a,"appBundleId");return{nodes:Array.isArray(r=a.nodes)?r:[],truncated:!0===a.truncated,appName:ex(a,"appName"),appBundleId:i,identifiers:{session:o,appId:i,appBundleId:i}}},screenshot:async(t={})=>{let r=eN(e.session,t.session);return{path:eC(await n("screenshot",t.path?[t.path]:[],t),"path"),identifiers:{session:r}}}}}}export{eH as createAgentDeviceClient,w as AppError};
|
|
1
|
+
import e from"node:net";import t from"node:http";import r from"node:https";import n from"node:fs";import o from"node:path";import{AsyncLocalStorage as a}from"node:async_hooks";import i from"node:crypto";import s from"node:os";import{spawn as l,spawnSync as u}from"node:child_process";import{fileURLToPath as d}from"node:url";let c=new a,p=/(token|secret|password|authorization|cookie|api[_-]?key|access[_-]?key|private[_-]?key)/i,m=/(bearer\s+[a-z0-9._-]+|(?:api[_-]?key|token|secret|password)\s*[=:]\s*\S+)/i;function f(){return i.randomBytes(8).toString("hex")}function h(e){let t=c.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&&p.test(a)||m.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(p.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 o=`[agent-device][diag] ${JSON.stringify(r)}
|
|
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");return{nodes:Array.isArray(r=a.nodes)?r:[],truncated:!0===a.truncated,appName:eN(a,"appName"),appBundleId:i,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};
|
package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift
CHANGED
|
@@ -528,15 +528,26 @@ extension RunnerTests {
|
|
|
528
528
|
needsPostSnapshotInteractionDelay = true
|
|
529
529
|
return Response(ok: true, data: snapshotFast(app: activeApp, options: options))
|
|
530
530
|
case .screenshot:
|
|
531
|
-
|
|
532
|
-
|
|
531
|
+
let screenshot: XCUIScreenshot
|
|
532
|
+
#if os(macOS)
|
|
533
|
+
// macOS keeps the app-targeted capture behavior for window-level screenshots.
|
|
533
534
|
if let bundleId = command.appBundleId, !bundleId.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
534
535
|
let targetApp = XCUIApplication(bundleIdentifier: bundleId)
|
|
535
536
|
targetApp.activate()
|
|
537
|
+
activeApp = targetApp
|
|
536
538
|
// Brief wait for the app transition animation to complete
|
|
537
539
|
Thread.sleep(forTimeInterval: 0.5)
|
|
538
540
|
}
|
|
539
|
-
|
|
541
|
+
if command.fullscreen == true {
|
|
542
|
+
screenshot = XCUIScreen.main.screenshot()
|
|
543
|
+
} else if let bundleId = command.appBundleId, !bundleId.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
544
|
+
screenshot = screenshotRoot(app: activeApp).screenshot()
|
|
545
|
+
} else {
|
|
546
|
+
screenshot = XCUIScreen.main.screenshot()
|
|
547
|
+
}
|
|
548
|
+
#else
|
|
549
|
+
screenshot = XCUIScreen.main.screenshot()
|
|
550
|
+
#endif
|
|
540
551
|
guard let pngData = runnerPngData(for: screenshot.image) else {
|
|
541
552
|
return Response(ok: false, error: ErrorPayload(message: "Failed to encode screenshot as PNG"))
|
|
542
553
|
}
|
|
@@ -38,6 +38,16 @@ extension RunnerTests {
|
|
|
38
38
|
return image
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
func screenshotRoot(app: XCUIApplication) -> XCUIElement {
|
|
42
|
+
#if os(macOS)
|
|
43
|
+
let windows = app.windows.allElementsBoundByIndex
|
|
44
|
+
if let window = windows.first(where: { $0.exists && !$0.frame.isNull && !$0.frame.isEmpty }) {
|
|
45
|
+
return window
|
|
46
|
+
}
|
|
47
|
+
#endif
|
|
48
|
+
return app
|
|
49
|
+
}
|
|
50
|
+
|
|
41
51
|
func stopRecordingIfNeeded() {
|
|
42
52
|
guard let recorder = activeRecording else { return }
|
|
43
53
|
do {
|
|
@@ -21,27 +21,39 @@ extension RunnerTests {
|
|
|
21
21
|
status: 413,
|
|
22
22
|
response: Response(ok: false, error: ErrorPayload(message: "request too large"))
|
|
23
23
|
)
|
|
24
|
-
|
|
25
|
-
connection.cancel()
|
|
24
|
+
self.sendResponse(response, over: connection) { [weak self] in
|
|
26
25
|
self?.finish()
|
|
27
|
-
}
|
|
26
|
+
}
|
|
28
27
|
return
|
|
29
28
|
}
|
|
30
29
|
let combined = buffer + data
|
|
31
30
|
if let body = self.parseRequest(data: combined) {
|
|
32
31
|
let result = self.handleRequestBody(body)
|
|
33
|
-
|
|
34
|
-
connection.cancel()
|
|
32
|
+
self.sendResponse(result.data, over: connection) { [weak self] in
|
|
35
33
|
if result.shouldFinish {
|
|
36
|
-
self
|
|
34
|
+
self?.finish()
|
|
37
35
|
}
|
|
38
|
-
}
|
|
36
|
+
}
|
|
39
37
|
} else {
|
|
40
38
|
self.receiveRequest(connection: connection, buffer: combined)
|
|
41
39
|
}
|
|
42
40
|
}
|
|
43
41
|
}
|
|
44
42
|
|
|
43
|
+
private func sendResponse(
|
|
44
|
+
_ response: Data,
|
|
45
|
+
over connection: NWConnection,
|
|
46
|
+
afterSend: @escaping () -> Void = {}
|
|
47
|
+
) {
|
|
48
|
+
connection.send(content: response, isComplete: true, completion: .contentProcessed { error in
|
|
49
|
+
if let error {
|
|
50
|
+
NSLog("AGENT_DEVICE_RUNNER_SEND_FAILED=%@", String(describing: error))
|
|
51
|
+
}
|
|
52
|
+
connection.cancel()
|
|
53
|
+
afterSend()
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
45
57
|
private func parseRequest(data: Data) -> Data? {
|
|
46
58
|
guard let headerEnd = data.range(of: Data("\r\n\r\n".utf8)) else {
|
|
47
59
|
return nil
|