agent-device
Advanced tools
| import{AsyncLocalStorage as e}from"node:async_hooks";import t,{createHash as r}from"node:crypto";import n,{existsSync as i,promises as o}from"node:fs";import s from"node:os";import a from"node:path";import{spawn as l,spawnSync as c}from"node:child_process";import{URL as u,fileURLToPath as d,pathToFileURL as f}from"node:url";let m=new e,p=/(token|secret|password|authorization|cookie|api[_-]?key|access[_-]?key|private[_-]?key)/i,h=/(bearer\s+[a-z0-9._-]+|(?:api[_-]?key|token|secret|password)\s*[=:]\s*\S+)/i;function w(){return t.randomBytes(8).toString("hex")}async function g(e,r){let n={...e,diagnosticId:`${Date.now().toString(36)}-${t.randomBytes(4).toString("hex")}`,events:[]};return await m.run(n,r)}function S(){let e=m.getStore();return e?{diagnosticId:e.diagnosticId,requestId:e.requestId,session:e.session,command:e.command,debug:e.debug}:{}}function y(e){let t=m.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?I(e.data):void 0};if(t.events.push(r),!t.debug)return;let i=`[agent-device][diag] ${JSON.stringify(r)} | ||
| `;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 v(e,t,r){let n=Date.now();try{let i=await t();return y({level:"info",phase:e,durationMs:Date.now()-n,data:r}),i}catch(t){throw y({level:"error",phase:e,durationMs:Date.now()-n,data:{...r??{},error:t instanceof Error?t.message:String(t)}}),t}}function A(e={}){let t=m.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,"_"),r=new Date().toISOString().slice(0,10),i=a.join(s.homedir(),".agent-device","logs",e,r);n.mkdirSync(i,{recursive:!0});let o=new Date().toISOString().replace(/[:.]/g,"-"),l=a.join(i,`${o}-${t.diagnosticId}.ndjson`),c=t.events.map(e=>JSON.stringify(I(e)));return n.writeFileSync(l,`${c.join("\n")} | ||
| `),t.events=[],l}catch{return null}}function I(e){return function e(t,r,n){if(null==t)return t;if("string"==typeof t){var i=t,o=n;let e=i.trim();if(!e)return i;if(o&&p.test(o)||h.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 s={};for(let[n,i]of Object.entries(t)){if(p.test(n)){s[n]="[REDACTED]";continue}s[n]=e(i,r,n)}return s}(e,new WeakSet)}class E extends Error{code;details;cause;constructor(e,t,r,n){super(t),this.code=e,this.details=r,this.cause=n}}function x(e){return e instanceof E?e:e instanceof Error?new E("UNKNOWN",e.message,void 0,e):new E("UNKNOWN","Unknown error",{err:e})}function D(e,t={}){let r=x(e),n=r.details?I(r.details):void 0,i=n&&"string"==typeof n.hint?n.hint:void 0,o=(n&&"string"==typeof n.diagnosticId?n.diagnosticId:void 0)??t.diagnosticId,s=(n&&"string"==typeof n.logPath?n.logPath:void 0)??t.logPath,a=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"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."}}(r.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}(n),c=function(e,t,r){if("COMMAND_FAILED"!==e||r?.processExitError!==!0)return t;let n=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 r of e.split("\n")){let e=r.trim();if(e&&!t.some(t=>t.test(e)))return e.length>200?`${e.slice(0,200)}...`:e}return null}("string"==typeof r?.stderr?r.stderr:"");return n||t}(r.code,r.message,n);return{code:r.code,message:c,hint:a,diagnosticId:o,logPath:s,details:l}}let $="<wifi|airplane|location> <on|off>",b="appearance <light|dark|toggle>",N="faceid <match|nonmatch|enroll|unenroll>",_="touchid <match|nonmatch|enroll|unenroll>",L="fingerprint <match|nonmatch>",T="permission <grant|deny|reset> <camera|microphone|photos|contacts|contacts-limited|notifications|calendar|location|location-always|media-library|motion|reminders|siri> [full|limited]",M="permission <grant|reset> <accessibility|screen-recording|input-monitoring>",O=`settings ${$} | settings ${b} | settings ${N} | settings ${_} | settings ${L} | settings ${T} | settings ${M}`,C=`settings requires ${$}, ${b}, ${N}, ${_}, ${L}, ${T}, or ${M}`,R=["app","frontmost-app","desktop","menubar"];function P(e){let t=e?.trim().toLowerCase();if("app"===t||"frontmost-app"===t||"desktop"===t||"menubar"===t)return t;throw new E("INVALID_ARGS",`Invalid surface: ${e}. Use ${R.join("|")}.`)}function k(e){var t;let r,n=F(e.label),i=F(e.value),o=F(e.identifier),s=(t=o)&&!/^[\w.]+:id\/[\w.-]+$/i.test(t)&&!/^_?NS:\d+$/i.test(t)?o:"";return(r=B(e.type??"")).includes("textfield")||r.includes("securetextfield")||r.includes("searchfield")||r.includes("edittext")||r.includes("textview")||r.includes("textarea")?i||n||s:n||i||s}function F(e){return"string"==typeof e?e.trim():""}function B(e){let t=e.trim().replace(/XCUIElementType/gi,"").replace(/^AX/,"").toLowerCase(),r=Math.max(t.lastIndexOf("."),t.lastIndexOf("/"));return -1!==r&&(t=t.slice(r+1)),t}function j(e,t={}){let r=[],n=[];for(let i of e){let e=i.depth??0;for(;r.length>0&&e<=r[r.length-1];)r.pop();let o=i.label?.trim()||i.value?.trim()||i.identifier?.trim()||"",s=V(i.type??"Element"),a="group"===s&&!o;a&&r.push(e);let l=a?e:Math.max(0,e-r.length);n.push({node:i,depth:l,type:s,text:G(i,l,a,s,t)})}return n}function G(e,t,r,n,i={}){var o,s,a,l,c;let u,d,f=n??V(e.type??"Element"),m=(u=k(e),{text:u,isLargeSurface:d=function(e,t){if("text-view"===t||"text-field"===t||"search"===t)return!0;let r=B(e.type??""),n=`${e.role??""} ${e.subrole??""}`.toLowerCase();return r.includes("textview")||r.includes("textarea")||r.includes("textfield")||r.includes("securetextfield")||r.includes("searchfield")||r.includes("edittext")||n.includes("text area")||n.includes("text field")}(e,f),shouldSummarize:d&&!!(o=u)&&(o.length>80||/[\r\n]/.test(o))}),p=(s=e,a=f,l=i,c=m,l.summarizeTextSurfaces&&c.shouldSummarize&&function(e,t,r){let n=F(e.label);if(n&&n!==r)return n;let i=F(e.identifier);if(i&&!H(i)&&i!==r)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""}}(s,a,c.text)||U(s,a)),h=" ".repeat(t),w=e.ref?`@${e.ref}`:"",g=(function(e,t,r,n){let i,o=[];if(!1===e.enabled&&o.push("disabled"),!r.summarizeTextSurfaces||(!0===e.selected&&o.push("selected"),z(t)&&o.push("editable"),function(e,t){if("scroll-area"===t)return!0;let r=(e.type??"").toLowerCase(),n=`${e.role??""} ${e.subrole??""}`.toLowerCase();return r.includes("scroll")||n.includes("scroll")}(e,t)&&o.push("scrollable"),!n.shouldSummarize))return o;return o.push(`preview:"${((i=n.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,m).map(e=>` [${e}]`).join(""),S=p?` "${p}"`:"";return r?`${h}${w} [${f}]${g}`.trimEnd():`${h}${w} [${f}]${S}${g}`.trimEnd()}function U(e,t){let r=e.label?.trim(),n=e.value?.trim();if(z(t)){if(n)return n;if(r)return r}else if(r)return r;if(n)return n;let i=e.identifier?.trim();return!i||H(i)&&("group"===t||"image"===t||"list"===t||"collection"===t)?"":i}function V(e){let t=e.replace(/XCUIElementType/gi,"").toLowerCase(),r=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\./,"")),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 r?"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 z(e){return"text-field"===e||"text-view"===e||"search"===e}function H(e){return/^[\w.]+:id\/[\w.-]+$/i.test(e)}function q(){try{let e=J();return JSON.parse(n.readFileSync(a.join(e,"package.json"),"utf8")).version??"0.0.0"}catch{return"0.0.0"}}function J(){let e=a.dirname(d(import.meta.url)),t=e;for(let e=0;e<6;e+=1){let e=a.join(t,"package.json");if(n.existsSync(e))return t;t=a.dirname(t)}return e}async function W(e,t,r={}){return new Promise((n,i)=>{let o=l(e,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"],detached:r.detached}),s="",a=r.binaryStdout?Buffer.alloc(0):void 0,c="",u=!1,d=ee(r.timeoutMs),f=d?setTimeout(()=>{u=!0,o.kill("SIGKILL")},d):null;r.binaryStdout||o.stdout.setEncoding("utf8"),o.stderr.setEncoding("utf8"),void 0!==r.stdin&&o.stdin.write(r.stdin),o.stdin.end(),o.stdout.on("data",e=>{r.binaryStdout?a=Buffer.concat([a??Buffer.alloc(0),Buffer.isBuffer(e)?e:Buffer.from(e)]):s+=e}),o.stderr.on("data",e=>{c+=e}),o.on("error",r=>{(f&&clearTimeout(f),"ENOENT"===r.code)?i(new E("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},r)):i(new E("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},r))}),o.on("close",o=>{f&&clearTimeout(f);let l=o??1;u&&d?i(new E("COMMAND_FAILED",`${e} timed out after ${d}ms`,{cmd:e,args:t,stdout:s,stderr:c,exitCode:l,timeoutMs:d})):0===l||r.allowFailure?n({stdout:s,stderr:c,exitCode:l,stdoutBuffer:a}):i(new E("COMMAND_FAILED",`${e} exited with code ${l}`,{cmd:e,args:t,stdout:s,stderr:c,exitCode:l,processExitError:!0}))})})}async function K(e){try{var t;let{shell:r,args:n}=(t=e,"win32"===process.platform?{shell:"cmd.exe",args:["/c","where",t]}:{shell:"bash",args:["-lc",`command -v ${t}`]}),i=await W(r,n,{allowFailure:!0});return 0===i.exitCode&&i.stdout.trim().length>0}catch{return!1}}function X(e,t,r={}){let n=c(e,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"],encoding:r.binaryStdout?void 0:"utf8",input:r.stdin,timeout:ee(r.timeoutMs)});if(n.error){let i=n.error.code;if("ETIMEDOUT"===i)throw new E("COMMAND_FAILED",`${e} timed out after ${ee(r.timeoutMs)}ms`,{cmd:e,args:t,timeoutMs:ee(r.timeoutMs)},n.error);if("ENOENT"===i)throw new E("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},n.error);throw new E("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},n.error)}let i=r.binaryStdout?Buffer.isBuffer(n.stdout)?n.stdout:Buffer.from(n.stdout??""):void 0,o=r.binaryStdout?"":"string"==typeof n.stdout?n.stdout:(n.stdout??"").toString(),s="string"==typeof n.stderr?n.stderr:(n.stderr??"").toString(),a=n.status??1;if(0!==a&&!r.allowFailure)throw new E("COMMAND_FAILED",`${e} exited with code ${a}`,{cmd:e,args:t,stdout:o,stderr:s,exitCode:a,processExitError:!0});return{stdout:o,stderr:s,exitCode:a,stdoutBuffer:i}}function Z(e,t,r={}){l(e,t,{cwd:r.cwd,env:r.env,stdio:"ignore",detached:!0}).unref()}async function Q(e,t,r={}){return new Promise((n,i)=>{let o=l(e,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"],detached:r.detached});r.onSpawn?.(o);let s="",a="",c=r.binaryStdout?Buffer.alloc(0):void 0;r.binaryStdout||o.stdout.setEncoding("utf8"),o.stderr.setEncoding("utf8"),void 0!==r.stdin&&o.stdin.write(r.stdin),o.stdin.end(),o.stdout.on("data",e=>{if(r.binaryStdout){c=Buffer.concat([c??Buffer.alloc(0),Buffer.isBuffer(e)?e:Buffer.from(e)]);return}let t=String(e);s+=t,r.onStdoutChunk?.(t)}),o.stderr.on("data",e=>{let t=String(e);a+=t,r.onStderrChunk?.(t)}),o.on("error",r=>{"ENOENT"===r.code?i(new E("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},r)):i(new E("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},r))}),o.on("close",o=>{let l=o??1;0===l||r.allowFailure?n({stdout:s,stderr:a,exitCode:l,stdoutBuffer:c}):i(new E("COMMAND_FAILED",`${e} exited with code ${l}`,{cmd:e,args:t,stdout:s,stderr:a,exitCode:l,processExitError:!0}))})})}function Y(e,t,r={}){let n=l(e,t,{cwd:r.cwd,env:r.env,stdio:["ignore","pipe","pipe"],detached:r.detached}),i="",o="";n.stdout.setEncoding("utf8"),n.stderr.setEncoding("utf8"),n.stdout.on("data",e=>{i+=e}),n.stderr.on("data",e=>{o+=e});let s=new Promise((s,a)=>{n.on("error",r=>{"ENOENT"===r.code?a(new E("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},r)):a(new E("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},r))}),n.on("close",n=>{let l=n??1;0===l||r.allowFailure?s({stdout:i,stderr:o,exitCode:l}):a(new E("COMMAND_FAILED",`${e} exited with code ${l}`,{cmd:e,args:t,stdout:i,stderr:o,exitCode:l,processExitError:!0}))})});return{child:n,wait:s}}function ee(e){if(!Number.isFinite(e))return;let t=Math.floor(e);if(!(t<=0))return t}let et=[/(^|[\/\s"'=])dist\/src\/daemon\.js($|[\s"'])/,/(^|[\/\s"'=])src\/daemon\.ts($|[\s"'])/];function er(e){if(!Number.isInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch(e){return"EPERM"===e.code}}function en(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=X("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 ei(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=X("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 eo(e,t){let r;if(!er(e))return!1;if(t){let r=en(e);if(!r||r!==t)return!1}let n=ei(e);return!!n&&!!(r=n.toLowerCase().replaceAll("\\","/")).includes("agent-device")&&et.some(e=>e.test(r))}function es(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 ea(e,t){if(!er(e))return!0;let r=Date.now();for(;Date.now()-r<t;)if(await new Promise(e=>setTimeout(e,50)),!er(e))return!0;return!er(e)}async function el(e,t){!eo(e,t.expectedStartTime)||!es(e,"SIGTERM")||await ea(e,t.termTimeoutMs)||es(e,"SIGKILL")&&await ea(e,t.killTimeoutMs)}function ec(e){return e?.HOME?.trim()||s.homedir()}function eu(e,t={}){return"~"===e?ec(t.env):e.startsWith("~/")?a.join(ec(t.env),e.slice(2)):e}function ed(e,t={}){let r=eu(e,t);return a.isAbsolute(r)?r:a.resolve(t.cwd??process.cwd(),r)}function ef(e){let t,r=(t=(e??"").trim())?ed(t):a.join(eu("~"),".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")}}function em(e){let t=(e??"").trim().toLowerCase();return"http"===t?"http":"dual"===t?"dual":"socket"}function ep(e){let t=(e??"").trim().toLowerCase();return"auto"===t?"auto":"socket"===t?"socket":"http"===t?"http":"auto"}function eh(e){return"tenant"===(e??"").trim().toLowerCase()?"tenant":"none"}function ew(e){if(!e)return;let t=e.trim();if(t&&/^[a-zA-Z0-9._-]{1,128}$/.test(t))return t}let eg=100,eS=new Set(["batch","replay"]);function ey(e){let t;try{t=JSON.parse(e)}catch{throw new E("INVALID_ARGS","Batch steps must be valid JSON.")}if(!Array.isArray(t)||0===t.length)throw new E("INVALID_ARGS","Batch steps must be a non-empty JSON array.");return t}function ev(e,t){if(!Array.isArray(e)||0===e.length)throw new E("INVALID_ARGS","batch requires a non-empty batchSteps array.");if(e.length>t)throw new E("INVALID_ARGS",`batch has ${e.length} steps; max allowed is ${t}.`);let r=[];for(let t=0;t<e.length;t+=1){let n=e[t];if(!n||"object"!=typeof n)throw new E("INVALID_ARGS",`Invalid batch step at index ${t}.`);let i="string"==typeof n.command?n.command.trim().toLowerCase():"";if(!i)throw new E("INVALID_ARGS",`Batch step ${t+1} requires command.`);if(eS.has(i))throw new E("INVALID_ARGS",`Batch step ${t+1} cannot run ${i}.`);if(void 0!==n.positionals&&!Array.isArray(n.positionals))throw new E("INVALID_ARGS",`Batch step ${t+1} positionals must be an array.`);let o=n.positionals??[];if(o.some(e=>"string"!=typeof e))throw new E("INVALID_ARGS",`Batch step ${t+1} positionals must contain only strings.`);if(void 0!==n.flags&&("object"!=typeof n.flags||Array.isArray(n.flags)||!n.flags))throw new E("INVALID_ARGS",`Batch step ${t+1} flags must be an object.`);if(void 0!==n.runtime&&("object"!=typeof n.runtime||Array.isArray(n.runtime)||!n.runtime))throw new E("INVALID_ARGS",`Batch step ${t+1} runtime must be an object.`);r.push({command:i,positionals:o,flags:n.flags??{},runtime:n.runtime})}return r}export{TextDecoder,styleText}from"node:util";export{default as node_net}from"node:net";export{default as node_http}from"node:http";export{default as node_https}from"node:https";export{E as AppError,eg as DEFAULT_BATCH_MAX_STEPS,R as SESSION_SURFACES,C as SETTINGS_INVALID_ARGS_MESSAGE,O as SETTINGS_USAGE_OVERRIDE,x as asAppError,j as buildSnapshotDisplayLines,r as createHash,w as createRequestId,U as displayLabel,y as emitDiagnostic,i as existsSync,eu as expandUserHomePath,u as external_node_url_URL,k as extractReadableText,d as fileURLToPath,J as findProjectRoot,A as flushDiagnosticsToSessionFile,V as formatRole,G as formatSnapshotLine,S as getDiagnosticsMeta,eo as isAgentDeviceDaemonProcess,er as isProcessAlive,t as node_crypto,n as node_fs,s as node_os,a as node_path,D as normalizeError,ew as normalizeTenantId,ey as parseBatchStepsJson,P as parseSessionSurface,f as pathToFileURL,o as promises,ei as readProcessCommand,en as readProcessStartTime,q as readVersion,ef as resolveDaemonPaths,em as resolveDaemonServerMode,ep as resolveDaemonTransportPreference,eh as resolveSessionIsolationMode,ed as resolveUserPath,W as runCmd,Y as runCmdBackground,Z as runCmdDetached,Q as runCmdStreaming,X as runCmdSync,l as spawn,el as stopProcessForTakeover,ev as validateAndNormalizeBatchSteps,K as whichCmd,v as withDiagnosticTimer,g as withDiagnosticsScope}; |
+559
-5
@@ -1,5 +0,559 @@ | ||
| export { createAgentDeviceClient, type AgentDeviceClient, type AgentDeviceClientConfig, type AgentDeviceDevice, type AgentDeviceIdentifiers, type AgentDeviceSelectionOptions, type AgentDeviceSession, type AgentDeviceSessionDevice, type AppCloseOptions, type AppCloseResult, type AppDeployOptions, type AppDeployResult, type AppInstallFromSourceOptions, type AppInstallFromSourceResult, type AppOpenOptions, type AppOpenResult, type CaptureScreenshotOptions, type CaptureScreenshotResult, type CaptureSnapshotOptions, type CaptureSnapshotResult, type EnsureSimulatorOptions, type EnsureSimulatorResult, type MaterializationReleaseOptions, type MaterializationReleaseResult, type MetroPrepareOptions, type MetroPrepareResult, type SessionCloseResult, type StartupPerfSample, } from './client.ts'; | ||
| export { AppError, type NormalizedError } from './utils/errors.ts'; | ||
| export type { MetroPrepareKind, MetroRuntimeHints } from './client-metro.ts'; | ||
| export type { SessionRuntimeHints } from './daemon/types.ts'; | ||
| export type { SnapshotNode } from './utils/snapshot.ts'; | ||
| declare type AgentDeviceClient = { | ||
| devices: { | ||
| list: (options?: AgentDeviceRequestOverrides & AgentDeviceSelectionOptions) => Promise<AgentDeviceDevice[]>; | ||
| }; | ||
| sessions: { | ||
| list: (options?: AgentDeviceRequestOverrides) => Promise<AgentDeviceSession[]>; | ||
| close: (options?: AgentDeviceRequestOverrides & { | ||
| shutdown?: boolean; | ||
| }) => Promise<SessionCloseResult>; | ||
| }; | ||
| simulators: { | ||
| ensure: (options: EnsureSimulatorOptions) => Promise<EnsureSimulatorResult>; | ||
| }; | ||
| apps: { | ||
| install: (options: AppDeployOptions) => Promise<AppDeployResult>; | ||
| reinstall: (options: AppDeployOptions) => Promise<AppDeployResult>; | ||
| installFromSource: (options: AppInstallFromSourceOptions) => Promise<AppInstallFromSourceResult>; | ||
| open: (options: AppOpenOptions) => Promise<AppOpenResult>; | ||
| close: (options?: AppCloseOptions) => Promise<AppCloseResult>; | ||
| }; | ||
| materializations: { | ||
| release: (options: MaterializationReleaseOptions) => Promise<MaterializationReleaseResult>; | ||
| }; | ||
| metro: { | ||
| prepare: (options: MetroPrepareOptions) => Promise<MetroPrepareResult>; | ||
| }; | ||
| capture: { | ||
| snapshot: (options?: CaptureSnapshotOptions) => Promise<CaptureSnapshotResult>; | ||
| screenshot: (options?: CaptureScreenshotOptions) => Promise<CaptureScreenshotResult>; | ||
| }; | ||
| }; | ||
| declare type AgentDeviceClientConfig = { | ||
| session?: string; | ||
| lockPolicy?: DaemonLockPolicy; | ||
| lockPlatform?: PlatformSelector; | ||
| requestId?: string; | ||
| stateDir?: string; | ||
| daemonBaseUrl?: string; | ||
| daemonAuthToken?: string; | ||
| daemonTransport?: DaemonTransportMode; | ||
| daemonServerMode?: DaemonServerMode; | ||
| tenant?: string; | ||
| sessionIsolation?: SessionIsolationMode; | ||
| runId?: string; | ||
| leaseId?: string; | ||
| cwd?: string; | ||
| debug?: boolean; | ||
| }; | ||
| declare type AgentDeviceDaemonTransport = (req: Omit<DaemonRequest, 'token'>) => Promise<DaemonResponse>; | ||
| declare type AgentDeviceDevice = { | ||
| platform: Platform; | ||
| target: DeviceTarget; | ||
| kind: DeviceKind; | ||
| id: string; | ||
| name: string; | ||
| booted?: boolean; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| ios?: { | ||
| udid: string; | ||
| }; | ||
| android?: { | ||
| serial: string; | ||
| }; | ||
| }; | ||
| declare type AgentDeviceIdentifiers = { | ||
| session?: string; | ||
| deviceId?: string; | ||
| deviceName?: string; | ||
| udid?: string; | ||
| serial?: string; | ||
| appId?: string; | ||
| appBundleId?: string; | ||
| package?: string; | ||
| }; | ||
| declare type AgentDeviceRequestOverrides = Pick<AgentDeviceClientConfig, 'session' | 'lockPolicy' | 'lockPlatform' | 'requestId' | 'tenant' | 'sessionIsolation' | 'runId' | 'leaseId' | 'cwd' | 'debug'>; | ||
| declare type AgentDeviceSelectionOptions = { | ||
| platform?: PlatformSelector; | ||
| target?: DeviceTarget; | ||
| device?: string; | ||
| udid?: string; | ||
| serial?: string; | ||
| iosSimulatorDeviceSet?: string; | ||
| androidDeviceAllowlist?: string; | ||
| }; | ||
| declare type AgentDeviceSession = { | ||
| name: string; | ||
| createdAt: number; | ||
| device: AgentDeviceSessionDevice; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| declare type AgentDeviceSessionDevice = { | ||
| platform: Platform; | ||
| target: DeviceTarget; | ||
| id: string; | ||
| name: string; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| ios?: { | ||
| udid: string; | ||
| simulatorSetPath?: string | null; | ||
| }; | ||
| android?: { | ||
| serial: string; | ||
| }; | ||
| }; | ||
| declare type AppCloseOptions = AgentDeviceRequestOverrides & { | ||
| app?: string; | ||
| shutdown?: boolean; | ||
| }; | ||
| declare type AppCloseResult = { | ||
| session: string; | ||
| closedApp?: string; | ||
| shutdown?: Record<string, unknown>; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| declare type AppDeployOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & { | ||
| app: string; | ||
| appPath: string; | ||
| }; | ||
| declare type AppDeployResult = { | ||
| app: string; | ||
| appPath: string; | ||
| platform: Platform; | ||
| appId?: string; | ||
| bundleId?: string; | ||
| package?: string; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| export declare class AppError extends Error { | ||
| code: ErrorCode; | ||
| details?: AppErrorDetails; | ||
| cause?: unknown; | ||
| constructor(code: ErrorCode, message: string, details?: AppErrorDetails, cause?: unknown); | ||
| } | ||
| declare type AppErrorDetails = Record<string, unknown> & { | ||
| hint?: string; | ||
| diagnosticId?: string; | ||
| logPath?: string; | ||
| }; | ||
| declare type AppInstallFromSourceOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & { | ||
| source: DaemonInstallSource; | ||
| retainPaths?: boolean; | ||
| retentionMs?: number; | ||
| }; | ||
| declare type AppInstallFromSourceResult = { | ||
| appName?: string; | ||
| appId?: string; | ||
| bundleId?: string; | ||
| packageName?: string; | ||
| launchTarget: string; | ||
| installablePath?: string; | ||
| archivePath?: string; | ||
| materializationId?: string; | ||
| materializationExpiresAt?: string; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| declare type ApplePlatform = 'ios' | 'macos'; | ||
| declare type AppOpenOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & { | ||
| app: string; | ||
| url?: string; | ||
| activity?: string; | ||
| relaunch?: boolean; | ||
| saveScript?: boolean | string; | ||
| noRecord?: boolean; | ||
| runtime?: SessionRuntimeHints; | ||
| }; | ||
| declare type AppOpenResult = { | ||
| session: string; | ||
| appName?: string; | ||
| appBundleId?: string; | ||
| appId?: string; | ||
| startup?: StartupPerfSample; | ||
| runtime?: SessionRuntimeHints; | ||
| device?: AgentDeviceSessionDevice; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| declare type BatchStep = { | ||
| command: string; | ||
| positionals?: string[]; | ||
| flags?: Partial<CommandFlags>; | ||
| runtime?: unknown; | ||
| }; | ||
| declare type CaptureScreenshotOptions = AgentDeviceRequestOverrides & { | ||
| path?: string; | ||
| }; | ||
| declare type CaptureScreenshotResult = { | ||
| path: string; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| declare type CaptureSnapshotOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & { | ||
| interactiveOnly?: boolean; | ||
| compact?: boolean; | ||
| depth?: number; | ||
| scope?: string; | ||
| raw?: boolean; | ||
| }; | ||
| declare type CaptureSnapshotResult = { | ||
| nodes: SnapshotNode[]; | ||
| truncated: boolean; | ||
| appName?: string; | ||
| appBundleId?: string; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| declare type CliFlags = { | ||
| json: boolean; | ||
| config?: string; | ||
| remoteConfig?: string; | ||
| stateDir?: string; | ||
| daemonBaseUrl?: string; | ||
| daemonAuthToken?: string; | ||
| daemonTransport?: 'auto' | 'socket' | 'http'; | ||
| daemonServerMode?: 'socket' | 'http' | 'dual'; | ||
| tenant?: string; | ||
| sessionIsolation?: 'none' | 'tenant'; | ||
| runId?: string; | ||
| leaseId?: string; | ||
| sessionLock?: 'reject' | 'strip'; | ||
| sessionLocked?: boolean; | ||
| sessionLockConflicts?: 'reject' | 'strip'; | ||
| platform?: 'ios' | 'macos' | 'android' | 'apple'; | ||
| target?: 'mobile' | 'tv' | 'desktop'; | ||
| device?: string; | ||
| udid?: string; | ||
| serial?: string; | ||
| iosSimulatorDeviceSet?: string; | ||
| androidDeviceAllowlist?: string; | ||
| out?: string; | ||
| session?: string; | ||
| runtime?: string; | ||
| metroHost?: string; | ||
| metroPort?: number; | ||
| metroProjectRoot?: string; | ||
| metroKind?: 'auto' | 'react-native' | 'expo'; | ||
| metroPublicBaseUrl?: string; | ||
| metroProxyBaseUrl?: string; | ||
| metroBearerToken?: string; | ||
| metroPreparePort?: number; | ||
| metroListenHost?: string; | ||
| metroStatusHost?: string; | ||
| metroStartupTimeoutMs?: number; | ||
| metroProbeTimeoutMs?: number; | ||
| metroRuntimeFile?: string; | ||
| metroNoReuseExisting?: boolean; | ||
| metroNoInstallDeps?: boolean; | ||
| bundleUrl?: string; | ||
| launchUrl?: string; | ||
| boot?: boolean; | ||
| reuseExisting?: boolean; | ||
| verbose?: boolean; | ||
| snapshotInteractiveOnly?: boolean; | ||
| snapshotCompact?: boolean; | ||
| snapshotDepth?: number; | ||
| snapshotScope?: string; | ||
| snapshotRaw?: boolean; | ||
| baseline?: string; | ||
| threshold?: string; | ||
| appsFilter?: 'user-installed' | 'all'; | ||
| count?: number; | ||
| fps?: number; | ||
| hideTouches?: boolean; | ||
| intervalMs?: number; | ||
| holdMs?: number; | ||
| jitterPx?: number; | ||
| doubleTap?: boolean; | ||
| clickButton?: 'primary' | 'secondary' | 'middle'; | ||
| pauseMs?: number; | ||
| pattern?: 'one-way' | 'ping-pong'; | ||
| activity?: string; | ||
| header?: string[]; | ||
| saveScript?: boolean | string; | ||
| shutdown?: boolean; | ||
| relaunch?: boolean; | ||
| surface?: 'app' | 'frontmost-app' | 'desktop' | 'menubar'; | ||
| headless?: boolean; | ||
| restart?: boolean; | ||
| noRecord?: boolean; | ||
| retainPaths?: boolean; | ||
| retentionMs?: number; | ||
| replayUpdate?: boolean; | ||
| steps?: string; | ||
| stepsFile?: string; | ||
| batchOnError?: 'stop'; | ||
| batchMaxSteps?: number; | ||
| batchSteps?: Array<{ | ||
| command: string; | ||
| positionals?: string[]; | ||
| flags?: Record<string, unknown>; | ||
| }>; | ||
| help: boolean; | ||
| version: boolean; | ||
| }; | ||
| declare type CommandFlags = Omit<CliFlags, 'json' | 'help' | 'version' | 'batchSteps'> & { | ||
| batchSteps?: BatchStep[]; | ||
| }; | ||
| export declare function createAgentDeviceClient(config?: AgentDeviceClientConfig, deps?: { | ||
| transport?: AgentDeviceDaemonTransport; | ||
| }): AgentDeviceClient; | ||
| declare type DaemonArtifact = { | ||
| field: string; | ||
| artifactId?: string; | ||
| fileName?: string; | ||
| localPath?: string; | ||
| path?: string; | ||
| }; | ||
| declare type DaemonInstallSource = MaterializeInstallSource; | ||
| declare type DaemonLockPolicy = 'reject' | 'strip'; | ||
| declare type DaemonRequest = { | ||
| token: string; | ||
| session: string; | ||
| command: string; | ||
| positionals: string[]; | ||
| flags?: CommandFlags; | ||
| runtime?: SessionRuntimeHints; | ||
| meta?: { | ||
| requestId?: string; | ||
| debug?: boolean; | ||
| cwd?: string; | ||
| tenantId?: string; | ||
| runId?: string; | ||
| leaseId?: string; | ||
| leaseTtlMs?: number; | ||
| leaseBackend?: 'ios-simulator'; | ||
| sessionIsolation?: 'none' | 'tenant'; | ||
| uploadedArtifactId?: string; | ||
| clientArtifactPaths?: Record<string, string>; | ||
| installSource?: DaemonInstallSource; | ||
| retainMaterializedPaths?: boolean; | ||
| materializedPathRetentionMs?: number; | ||
| materializationId?: string; | ||
| lockPolicy?: DaemonLockPolicy; | ||
| lockPlatform?: PlatformSelector; | ||
| }; | ||
| }; | ||
| declare type DaemonResponse = { | ||
| ok: true; | ||
| data?: DaemonResponseData; | ||
| } | { | ||
| ok: false; | ||
| error: { | ||
| code: string; | ||
| message: string; | ||
| hint?: string; | ||
| diagnosticId?: string; | ||
| logPath?: string; | ||
| details?: Record<string, unknown>; | ||
| }; | ||
| }; | ||
| declare type DaemonResponseData = Record<string, unknown> & { | ||
| artifacts?: DaemonArtifact[]; | ||
| }; | ||
| declare type DaemonServerMode = 'socket' | 'http' | 'dual'; | ||
| declare type DaemonTransportMode = 'auto' | 'socket' | 'http'; | ||
| declare type DeviceKind = 'simulator' | 'emulator' | 'device'; | ||
| declare type DeviceTarget = 'mobile' | 'tv' | 'desktop'; | ||
| declare type EnsureSimulatorOptions = AgentDeviceRequestOverrides & { | ||
| device: string; | ||
| runtime?: string; | ||
| boot?: boolean; | ||
| reuseExisting?: boolean; | ||
| iosSimulatorDeviceSet?: string; | ||
| }; | ||
| declare type EnsureSimulatorResult = { | ||
| udid: string; | ||
| device: string; | ||
| runtime: string; | ||
| created: boolean; | ||
| booted: boolean; | ||
| iosSimulatorDeviceSet?: string | null; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| declare type ErrorCode = 'INVALID_ARGS' | 'DEVICE_NOT_FOUND' | 'TOOL_MISSING' | 'APP_NOT_INSTALLED' | 'UNSUPPORTED_PLATFORM' | 'UNSUPPORTED_OPERATION' | 'COMMAND_FAILED' | 'SESSION_NOT_FOUND' | 'UNAUTHORIZED' | 'UNKNOWN'; | ||
| declare type MaterializationReleaseOptions = AgentDeviceRequestOverrides & { | ||
| materializationId: string; | ||
| }; | ||
| declare type MaterializationReleaseResult = { | ||
| released: boolean; | ||
| materializationId: string; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| declare type MaterializeInstallSource = { | ||
| kind: 'url'; | ||
| url: string; | ||
| headers?: Record<string, string>; | ||
| } | { | ||
| kind: 'path'; | ||
| path: string; | ||
| }; | ||
| declare type MetroBridgeResult = { | ||
| enabled: boolean; | ||
| baseUrl: string; | ||
| statusUrl: string; | ||
| bundleUrl: string; | ||
| iosRuntime: MetroRuntimeHints; | ||
| androidRuntime: MetroRuntimeHints; | ||
| upstream: { | ||
| bundleUrl: string; | ||
| host: string; | ||
| port: number; | ||
| statusUrl: string; | ||
| }; | ||
| probe: { | ||
| reachable: boolean; | ||
| statusCode: number; | ||
| latencyMs: number; | ||
| detail: string; | ||
| }; | ||
| }; | ||
| declare type MetroPrepareKind = 'auto' | 'react-native' | 'expo'; | ||
| declare type MetroPrepareOptions = { | ||
| projectRoot?: string; | ||
| kind?: MetroPrepareKind; | ||
| publicBaseUrl: string; | ||
| proxyBaseUrl?: string; | ||
| bearerToken?: string; | ||
| port?: number; | ||
| listenHost?: string; | ||
| statusHost?: string; | ||
| startupTimeoutMs?: number; | ||
| probeTimeoutMs?: number; | ||
| reuseExisting?: boolean; | ||
| installDependenciesIfNeeded?: boolean; | ||
| runtimeFilePath?: string; | ||
| logPath?: string; | ||
| }; | ||
| declare type MetroPrepareResult = PrepareMetroRuntimeResult; | ||
| declare type MetroRuntimeHints = { | ||
| platform?: 'ios' | 'android'; | ||
| metroHost?: string; | ||
| metroPort?: number; | ||
| bundleUrl?: string; | ||
| launchUrl?: string; | ||
| }; | ||
| declare type Platform = ApplePlatform | 'android'; | ||
| declare type PlatformSelector = Platform | 'apple'; | ||
| declare type PrepareMetroRuntimeResult = { | ||
| projectRoot: string; | ||
| kind: ResolvedMetroKind; | ||
| dependenciesInstalled: boolean; | ||
| packageManager: string | null; | ||
| started: boolean; | ||
| reused: boolean; | ||
| pid: number; | ||
| logPath: string; | ||
| statusUrl: string; | ||
| runtimeFilePath: string | null; | ||
| iosRuntime: MetroRuntimeHints; | ||
| androidRuntime: MetroRuntimeHints; | ||
| bridge: MetroBridgeResult | null; | ||
| }; | ||
| declare type RawSnapshotNode = { | ||
| index: number; | ||
| type?: string; | ||
| role?: string; | ||
| subrole?: string; | ||
| label?: string; | ||
| value?: string; | ||
| identifier?: string; | ||
| rect?: Rect; | ||
| enabled?: boolean; | ||
| selected?: boolean; | ||
| hittable?: boolean; | ||
| depth?: number; | ||
| parentIndex?: number; | ||
| pid?: number; | ||
| bundleId?: string; | ||
| appName?: string; | ||
| windowTitle?: string; | ||
| surface?: string; | ||
| }; | ||
| declare type Rect = { | ||
| x: number; | ||
| y: number; | ||
| width: number; | ||
| height: number; | ||
| }; | ||
| declare type ResolvedMetroKind = Exclude<MetroPrepareKind, 'auto'>; | ||
| declare type SessionCloseResult = { | ||
| session: string; | ||
| shutdown?: Record<string, unknown>; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| declare type SessionIsolationMode = 'none' | 'tenant'; | ||
| declare type SessionRuntimeHints = { | ||
| platform?: 'ios' | 'android'; | ||
| metroHost?: string; | ||
| metroPort?: number; | ||
| bundleUrl?: string; | ||
| launchUrl?: string; | ||
| }; | ||
| declare type SnapshotNode = RawSnapshotNode & { | ||
| ref: string; | ||
| }; | ||
| declare type StartupPerfSample = { | ||
| durationMs: number; | ||
| measuredAt: string; | ||
| method: string; | ||
| appTarget?: string; | ||
| appBundleId?: string; | ||
| }; | ||
| export { } |
@@ -1,1 +0,3 @@ | ||
| export{AppError}from"./331.js";export{createAgentDeviceClient}from"./224.js"; | ||
| 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 d}from"node:child_process";import{fileURLToPath as u}from"node:url";let c=new a,m=/(token|secret|password|authorization|cookie|api[_-]?key|access[_-]?key|private[_-]?key)/i,p=/(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&&m.test(a)||p.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(m.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)} | ||
| `;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)} | ||
| `)}),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}; |
+2
-1
| { | ||
| "name": "agent-device", | ||
| "version": "0.10.2", | ||
| "version": "0.10.3", | ||
| "description": "Unified control plane for physical and virtual devices via an agent-driven CLI.", | ||
@@ -76,2 +76,3 @@ "license": "MIT", | ||
| "devDependencies": { | ||
| "@microsoft/api-extractor": "^7.52.10", | ||
| "@rslib/core": "0.19.4", | ||
@@ -78,0 +79,0 @@ "@types/node": "^22.0.0", |
@@ -5,13 +5,16 @@ # Bootstrap and Install | ||
| Open this file when you still need to choose the right target, start the right session, install or relaunch the app, or pin automation to one device before interacting. | ||
| Open this file when you still need to choose the right target, start the right session, install or relaunch the app, or pin automation to one device before interacting. This is the deterministic setup layer for sandbox, cloud, or other environments where install paths, device state, or app readiness may be uncertain. | ||
| ## Main commands to reach for first | ||
| ## Open-first path | ||
| - `devices` | ||
| - `apps` | ||
| - `ensure-simulator` | ||
| - `open` | ||
| - `install` or `reinstall` | ||
| - `close` | ||
| - `session list` | ||
| ## Install path | ||
| - `install` or `reinstall` | ||
| ## Most common mistake to avoid | ||
@@ -21,11 +24,51 @@ | ||
| ## Canonical loop | ||
| ## Deterministic setup rule | ||
| If there is no simulator, no app install, no open app session, or any uncertainty about where the app should come from, stay in this file and use deterministic setup commands or bootstrap scripts first. Do not improvise install paths or app-launch flows while exploring. | ||
| After setup is confirmed or completed, move to `exploration.md` before doing UI inspection or interaction. | ||
| ## Open-first rule | ||
| - If the user asks to test an app and does not provide an install artifact or explicit install instruction, try `open <app>` first. | ||
| - If `open <app>` fails, run `agent-device apps` and retry with a discovered app name before considering install steps. | ||
| - Do not install or reinstall on the first attempt unless the user explicitly asks for installation or provides a concrete artifact path or URL. | ||
| - When installation is required from a known location, prefer a checked-in shell script or other deterministic bootstrap command over ad hoc path guessing. | ||
| - If `open <app>` fails, or you are not sure which app name is available on the target, run `agent-device apps` first and choose from the discovered app list instead of guessing. | ||
| - Use `apps --platform <platform>` together with `--device`, `--udid`, or `--serial` when target selection matters. | ||
| - Once you have the correct app name, retry `open` with that exact discovered value. | ||
| ## Common starting points | ||
| These are examples, not required exact sequences. Use the smallest setup flow that matches the task. | ||
| ### Boot a simulator and open an app | ||
| ```bash | ||
| agent-device ensure-simulator --platform ios --device "iPhone 17 Pro" --boot | ||
| agent-device open MyApp --platform ios --device "iPhone 17 Pro" --relaunch | ||
| agent-device snapshot -i | ||
| agent-device close | ||
| ``` | ||
| ### Install an app artifact | ||
| ```bash | ||
| agent-device install com.example.app ./build/app.apk --platform android --serial emulator-5554 | ||
| ``` | ||
| ```bash | ||
| agent-device install com.example.app ./build/MyApp.app --platform ios --device "iPhone 17 Pro" | ||
| ``` | ||
| ## Install guidance | ||
| - Use `install <app> <path>` when the app may already be installed and you do not need a fresh-state reset. | ||
| - Use `reinstall <app> <path>` when you explicitly need uninstall plus install as one deterministic step. | ||
| - Keep install and open as separate phases. Do not turn them into one default command flow. | ||
| - Supported binary formats: | ||
| - Android: `.apk` and `.aab` | ||
| - iOS: `.app` and `.ipa` | ||
| - For iOS `.ipa` files, `<app>` is used as the bundle id or bundle name hint when the archive contains multiple app bundles. | ||
| - After install or reinstall, later use `open <app>` with the exact discovered or known package/bundle identifier, not the artifact path. | ||
| ## Choose the right starting point | ||
@@ -37,3 +80,2 @@ | ||
| - Android binary flow: use `install` or `reinstall` for `.apk` or `.aab`, then open by installed package name. | ||
| - Android React Native plus Metro flow: `reinstall <app> <apk>` first, then `open <package> --remote-config <path> --relaunch`. | ||
| - macOS desktop app flow: use `open <app> --platform macos`. Only load [macos-desktop.md](macos-desktop.md) if a desktop surface or macOS-specific behavior matters. | ||
@@ -101,4 +143,2 @@ | ||
| agent-device open MyApp --relaunch | ||
| agent-device snapshot -i | ||
| agent-device close | ||
| ``` | ||
@@ -118,6 +158,3 @@ | ||
| agent-device reinstall MyApp /path/to/app-debug.apk --serial emulator-5554 | ||
| agent-device --session-lock reject open com.example.myapp --relaunch | ||
| agent-device snapshot -i | ||
| agent-device close --shutdown | ||
| ``` | ||
@@ -159,9 +196,12 @@ | ||
| ## Install and open examples | ||
| ## Install examples | ||
| ```bash | ||
| agent-device reinstall MyApp /path/to/app-debug.apk --platform android --serial emulator-5554 | ||
| agent-device open com.example.myapp --remote-config ./agent-device.remote.json --relaunch | ||
| ``` | ||
| ```bash | ||
| agent-device install com.example.app ./build/MyApp.ipa --platform ios --device "iPhone 17 Pro" | ||
| ``` | ||
| Do not use `open <apk|aab> --relaunch` on Android. | ||
@@ -168,0 +208,0 @@ |
@@ -7,12 +7,34 @@ # Exploration | ||
| ## Main commands to reach for first | ||
| ## Read-only first | ||
| - If the question is what text, labels, or structure is visible on screen, start with plain `snapshot`. | ||
| - Escalate to `snapshot -i` only when you need refs such as `@e3` for interactive exploration or a requested action. | ||
| - If you intend to `press`, `fill`, or otherwise interact, start with `snapshot -i` and fall back to plain `snapshot` only if interactive refs are unavailable. | ||
| - Prefer `get`, `is`, or `find` before mutating the UI when a read-only command can answer the question. | ||
| - You may take the smallest reversible UI action needed to unblock inspection, such as dismissing a popup, closing an alert, or backing out of an unintended surface. | ||
| - Do not type or fill text just to make hidden information easier to access unless the user asked for that interaction. | ||
| - Do not use external sources to infer missing UI state unless the user explicitly asked. | ||
| - If the answer is not visible or exposed in the UI, report that gap instead of compensating with search, navigation, or text entry. | ||
| ## Decision shortcut | ||
| - User asks what is visible on screen: `snapshot` | ||
| - User asks for exact text from a known target: `get text` | ||
| - User asks you to tap, type, or choose an element: `snapshot -i`, then act | ||
| - UI does not expose the answer: say so plainly; do not browse or force the app into a new state unless asked | ||
| ## Read-only commands | ||
| - `snapshot` | ||
| - `get` | ||
| - `is` | ||
| - `find` | ||
| ## Interaction commands | ||
| - `snapshot -i` | ||
| - `press` | ||
| - `fill` | ||
| - `get` | ||
| - `is` | ||
| - `type` | ||
| - `wait` | ||
| - `find` | ||
@@ -23,4 +45,8 @@ ## Most common mistake to avoid | ||
| ## Canonical loop | ||
| ## Common example loops | ||
| These are examples, not required exact sequences. Adapt them to the app, state, and task at hand. | ||
| ### Interactive exploration loop | ||
| ```bash | ||
@@ -35,7 +61,17 @@ agent-device open Settings --platform ios | ||
| ### Screen verification loop | ||
| ```bash | ||
| agent-device open MyApp --platform ios | ||
| # perform the necessary actions to reach the state you need to verify | ||
| agent-device snapshot | ||
| # verify whether the expected element or text is present | ||
| agent-device close | ||
| ``` | ||
| ## Snapshot choices | ||
| - Use plain `snapshot` when you only need to verify whether visible text or structure is on screen. | ||
| - Use `snapshot -i` when you need refs such as `@e3` for interactive exploration. | ||
| - Treat large text-surface lines in `snapshot -i` as discovery output. If a node shows preview/truncation metadata, use `get text @ref` to expand the actual text after you choose the surface. | ||
| - Use `snapshot -i` when you need refs such as `@e3` for interactive exploration or for an intended interaction. | ||
| - Treat large text-surface lines in `snapshot -i` as discovery output. If a node shows preview or truncation metadata, use `get text @ref` only after you have already decided that `snapshot -i` is needed for that surface. | ||
| - Use `snapshot -i -s "Camera"` or `snapshot -i -s @e3` when you want a smaller, scoped result. | ||
@@ -80,2 +116,3 @@ | ||
| - Use `type` to append text to the current insertion point. | ||
| - Do not use `fill` or `type` just to make the app reveal information that is not currently visible unless the user asked for that interaction. | ||
@@ -116,2 +153,7 @@ ## Query and sync rules | ||
| Avoid this escalation path for visible-text questions: | ||
| - Do not jump from `snapshot -i` to `get text @ref`, then to web search, then to typing into a search box just to force the app to reveal the answer. | ||
| - Start with `snapshot`. If the text is not visible or exposed, report that directly. | ||
| Canonical QA loop: | ||
@@ -118,0 +160,0 @@ |
@@ -24,4 +24,3 @@ # macOS Desktop | ||
| agent-device open TextEdit --platform macos | ||
| agent-device snapshot -i | ||
| agent-device fill @e3 "desktop smoke test" | ||
| agent-device snapshot | ||
| agent-device close | ||
@@ -28,0 +27,0 @@ ``` |
@@ -5,6 +5,7 @@ # Remote Tenancy | ||
| Open this file only for remote daemon HTTP flows that require explicit daemon URL setup, authentication, lease allocation, or tenant-scoped command admission. | ||
| Open this file for remote daemon HTTP flows, including `--remote-config` launches, that let an agent running in a Linux sandbox talk to another `agent-device` instance on a remote macOS host in order to control devices that are not available locally. This file covers daemon URL setup, authentication, lease allocation, and tenant-scoped command admission. | ||
| ## Main commands to reach for first | ||
| - `agent-device open <app> --remote-config <path> --relaunch` | ||
| - `AGENT_DEVICE_DAEMON_BASE_URL=...` | ||
@@ -21,5 +22,17 @@ - `AGENT_DEVICE_DAEMON_AUTH_TOKEN=...` | ||
| ## Canonical loop | ||
| ## Preferred remote launch path | ||
| Use this when the agent needs the simplest remote control flow: a Linux sandbox agent talks over HTTP to `agent-device` on a remote macOS host and launches the target app through a checked-in `--remote-config` profile. | ||
| ```bash | ||
| agent-device open com.example.myapp --remote-config ./agent-device.remote.json --relaunch | ||
| ``` | ||
| - This is the preferred remote launch path for sandbox or cloud agents. | ||
| - For Android React Native relaunch flows, install or reinstall the APK first, then relaunch by installed package name. | ||
| - Do not use `open <apk|aab> --relaunch`; remote runtime hints are applied through the installed app sandbox. | ||
| ## Lease flow example | ||
| ```bash | ||
| export AGENT_DEVICE_DAEMON_BASE_URL=http://mac-host.example:4310 | ||
@@ -26,0 +39,0 @@ export AGENT_DEVICE_DAEMON_AUTH_TOKEN=<token> |
@@ -23,5 +23,4 @@ # Verification | ||
| agent-device open Settings --platform ios | ||
| agent-device snapshot -i | ||
| agent-device press @e5 | ||
| agent-device diff snapshot -i | ||
| # after using exploration to reach the state you want to verify | ||
| agent-device snapshot | ||
| agent-device screenshot /tmp/settings-proof.png | ||
@@ -28,0 +27,0 @@ agent-device close |
@@ -8,4 +8,26 @@ --- | ||
| Use this skill as a router. | ||
| Use this skill as a router with mandatory defaults. Read this file first. For normal device tasks, always load `references/bootstrap-install.md` and `references/exploration.md` before acting. Use bootstrap to confirm or establish deterministic setup. Use exploration for UI inspection, interaction, and verification once the app session is open. | ||
| ## Default operating rules | ||
| - Start conservative. Prefer read-only inspection before mutating the UI. | ||
| - Use plain `snapshot` when the task is to verify what text or structure is currently visible on screen. | ||
| - Use `snapshot -i` only when you need interactive refs such as `@e3` for a requested action or targeted query. | ||
| - Avoid speculative mutations. You may take the smallest reversible UI action needed to unblock inspection or complete the requested task, such as dismissing a popup, closing an alert, or clearing an unintended surface. | ||
| - Do not browse the web or use external sources unless the user explicitly asks. | ||
| - Re-snapshot after meaningful UI changes instead of reusing stale refs. | ||
| - Prefer `@ref` or selector targeting over raw coordinates. | ||
| - Ensure the correct target is pinned and an app session is open before interacting. | ||
| - Keep the loop short: `open` -> inspect/act -> verify if needed -> `close`. | ||
| ## Default flow | ||
| 1. Load [references/bootstrap-install.md](references/bootstrap-install.md) and [references/exploration.md](references/exploration.md) before acting on a normal device task. | ||
| 2. Use bootstrap first to confirm or establish the correct target, app install, and open app session. | ||
| 3. Once the app session is open and stable, use exploration for inspection, interaction, and verification. | ||
| 4. Start with plain `snapshot` if the goal is to read or verify what is visible. | ||
| 5. Escalate to `snapshot -i` only if you need refs for interactive exploration or a requested action. | ||
| 6. Use `get`, `is`, or `find` before mutating the UI when a read-only command can answer the question. | ||
| 7. End by capturing proof if needed, then `close`. | ||
| ## QA modes | ||
@@ -16,9 +38,8 @@ | ||
| ## Mental model | ||
| ## Required references | ||
| - First choose the correct target and open the app or session you want to work on. | ||
| - Then inspect the current UI with `snapshot -i` and pick targets from the actual UI state. | ||
| - Act with `press`, `fill`, `get`, `is`, `wait`, or `find`. | ||
| - Re-snapshot after meaningful UI changes instead of reusing stale refs. | ||
| - End by capturing proof if needed, then `close`. | ||
| - For every normal device task, after reading this file, load [references/bootstrap-install.md](references/bootstrap-install.md) first, then [references/exploration.md](references/exploration.md), before acting. | ||
| - Use bootstrap to confirm or establish deterministic setup, especially in sandbox or cloud environments. | ||
| - Use exploration once the app session is open and stable. | ||
| - Load additional references only when their scope is needed. | ||
@@ -29,14 +50,16 @@ ## Decision rules | ||
| - Use `snapshot -i` mainly for interactive exploration and choosing refs. | ||
| - Use `get`, `is`, or `find` when they can answer the question without changing UI state. | ||
| - Use `fill` to replace text. | ||
| - Use `type` to append text. | ||
| - If there is no simulator, no app install, or no open app session yet, switch to `bootstrap-install.md` instead of improvising setup steps. | ||
| - Use the smallest unblock action first when transient UI blocks inspection, but do not navigate, search, or enter new text just to make the UI reveal data unless the user asked for that interaction. | ||
| - Do not use external lookups to compensate for missing on-screen data unless the user asked for them. | ||
| - If the needed information is not exposed on screen, say that plainly instead of compensating with extra navigation, text entry, or web search. | ||
| - Prefer `@ref` or selector targeting over raw coordinates. | ||
| - Keep the default loop short: `open` -> explore/act -> optional debug or verify -> `close`. | ||
| ## Choose a reference | ||
| ## Additional references | ||
| - Pick target device, install, open, or manage sessions: [references/bootstrap-install.md](references/bootstrap-install.md) | ||
| - Need to discover UI, pick refs, wait, query, or interact: [references/exploration.md](references/exploration.md) | ||
| - Need logs, network, alerts, permissions, or failure triage: [references/debugging.md](references/debugging.md) | ||
| - Need screenshots, diff, recording, replay maintenance, or perf data: [references/verification.md](references/verification.md) | ||
| - Need desktop surfaces, menu bar behavior, or macOS-specific interaction rules: [references/macos-desktop.md](references/macos-desktop.md) | ||
| - Need to connect to a remote `agent-device` daemon over HTTP or use tenant leases: [references/remote-tenancy.md](references/remote-tenancy.md) | ||
| - Need remote HTTP transport, `--remote-config` launches, or tenant leases on a remote macOS host: [references/remote-tenancy.md](references/remote-tenancy.md) |
| import{createRequestId as e,node_path as t,isAgentDeviceDaemonProcess as r,runCmdDetached as a,readVersion as n,findProjectRoot as o,node_https as i,runCmdSync as s,withDiagnosticTimer as l,resolveDaemonTransportPreference as d,resolveUserPath as u,emitDiagnostic as c,spawn as p,AppError as m,node_fs as h,resolveDaemonPaths as f,node_net as I,resolveDaemonServerMode as g,stopProcessForTakeover as w,node_http as b}from"./331.js";async function y(e){let{localPath:r,baseUrl:a,token:n}=e,o=h.statSync(r).isDirectory(),s=t.basename(r),l=new URL("upload",a.endsWith("/")?a:`${a}/`),d="https:"===l.protocol?i:b,u={"x-artifact-type":o?"app-bundle":"file","x-artifact-filename":s,"transfer-encoding":"chunked"};return n&&(u.authorization=`Bearer ${n}`,u["x-agent-device-token"]=n),new Promise((e,a)=>{let n=d.request({protocol:l.protocol,host:l.hostname,port:l.port,method:"POST",path:l.pathname+l.search,headers:u},t=>{let r="";t.setEncoding("utf8"),t.on("data",e=>{r+=e}),t.on("end",()=>{clearTimeout(i);try{let t=JSON.parse(r);if(!t.ok||!t.uploadId)return void a(new m("COMMAND_FAILED",`Upload failed: ${r}`));e(t.uploadId)}catch{a(new m("COMMAND_FAILED",`Invalid upload response: ${r}`))}})}),i=setTimeout(()=>{n.destroy(),a(new m("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),a(new m("COMMAND_FAILED","Failed to upload artifact to remote daemon",{hint:"Verify the remote daemon is reachable and supports artifact uploads."},e))}),o){let e=p("tar",["cf","-","-C",t.dirname(r),t.basename(r)],{stdio:["ignore","pipe","pipe"]});e.stdout.pipe(n),e.on("error",e=>{n.destroy(),a(new m("COMMAND_FAILED","Failed to create tar archive for app bundle",{},e))}),e.on("close",e=>{0!==e&&(n.destroy(),a(new m("COMMAND_FAILED",`tar failed with exit code ${e}`)))})}else{let e=h.createReadStream(r);e.pipe(n),e.on("error",e=>{n.destroy(),a(new m("COMMAND_FAILED","Failed to read local artifact",{},e))})}})}let v=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}(),A=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}(),D=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}(),_=["xcodebuild .*AgentDeviceRunnerUITests/RunnerTests/testCommand","xcodebuild .*AgentDeviceRunner\\.env\\.session-","xcodebuild build-for-testing .*ios-runner/AgentDeviceRunner/AgentDeviceRunner\\.xcodeproj"];async function P(t){let r=t.meta?.requestId??e(),a=!!(t.meta?.debug||t.flags?.verbose),n=function(e){let t=e.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR,r=function(e){let t;if(e){try{t=new URL(e)}catch(t){throw new m("INVALID_ARGS","Invalid daemon base URL",{daemonBaseUrl:e},t instanceof Error?t:void 0)}if("http:"!==t.protocol&&"https:"!==t.protocol)throw new m("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,n=e.flags?.daemonTransport??process.env.AGENT_DEVICE_DAEMON_TRANSPORT,o=d(n);if(r&&"socket"===o)throw new m("INVALID_ARGS","Remote daemon base URL only supports HTTP transport. Remove --daemon-transport socket.",{daemonBaseUrl:r});let i=g(e.flags?.daemonServerMode??process.env.AGENT_DEVICE_DAEMON_SERVER_MODE??("dual"===n?"dual":void 0));return{paths:f(t),transportPreference:o,serverMode:i,remoteBaseUrl:r,remoteAuthToken:a}}(t),o=await l("daemon_startup",async()=>await N(n),{requestId:r,session:t.session}),i=await E(t,o),s={...t,positionals:i.positionals,flags:i.flags,token:o.token,meta:{...t.meta??{},requestId:r,debug:a,cwd:t.meta?.cwd,tenantId:t.meta?.tenantId??t.flags?.tenant,runId:t.meta?.runId??t.flags?.runId,leaseId:t.meta?.leaseId??t.flags?.leaseId,sessionIsolation:t.meta?.sessionIsolation??t.flags?.sessionIsolation,lockPolicy:t.meta?.lockPolicy,lockPlatform:t.meta?.lockPlatform,...i.uploadedArtifactId?{uploadedArtifactId:i.uploadedArtifactId}:{},...i.clientArtifactPaths?{clientArtifactPaths:i.clientArtifactPaths}:{},...i.installSource?{installSource:i.installSource}:{}}};return c({level:"info",phase:"daemon_request_prepare",data:{requestId:r,command:t.command,session:t.session}}),await l("daemon_request",async()=>await j(o,s,n.transportPreference),{requestId:r,command:t.command})}async function E(e,r){let a,n=[...e.positionals??[]],o=e.flags?{...e.flags}:void 0,i=e.meta?.installSource,s={};if(X(r)){let l=function(e,r){if("screenshot"===e.command){let t=M(e,"path",".png");return r[0]?{field:"path",localPath:t,positionalIndex:0,positionalPath:k("screenshot",".png")}:{field:"path",localPath:t,positionalIndex:0,flagPath:k("screenshot",".png")}}if("record"===e.command&&"start"===(r[0]??"").toLowerCase()){let r=M(e,"outPath",".mp4",1);return{field:"outPath",localPath:r,positionalIndex:1,positionalPath:k("recording",t.extname(r)||".mp4")}}return null}(e,n);l&&(void 0!==l.positionalPath&&(n[l.positionalIndex]=l.positionalPath),void 0!==l.flagPath&&((o??={}).out=l.flagPath),s[l.field]=l.localPath);let d=await S(e,r);d&&(i=d.installSource,a=d.uploadedArtifactId??a)}if(!X(r)||"install"!==e.command&&"reinstall"!==e.command||n.length<2)return{positionals:n,flags:o,installSource:i,uploadedArtifactId:a,...Object.keys(s).length>0?{clientArtifactPaths:s}:{}};let l=n[1];if(l.startsWith("remote:"))return n[1]=l.slice(7),{positionals:n,flags:o,...Object.keys(s).length>0?{clientArtifactPaths:s}:{}};let d=t.isAbsolute(l)?l:t.resolve(e.meta?.cwd??process.cwd(),l);return h.existsSync(d)?{positionals:n,flags:o,installSource:i,uploadedArtifactId:a=await y({localPath:d,baseUrl:r.baseUrl,token:r.token}),...Object.keys(s).length>0?{clientArtifactPaths:s}:{}}:{positionals:n,flags:o,...Object.keys(s).length>0?{clientArtifactPaths:s}:{}}}async function S(e,r){let a=e.meta?.installSource;if("install_source"!==e.command||!a||"path"!==a.kind)return null;let n=a.path.trim();if(!n)return{installSource:a};if(n.startsWith("remote:"))return{installSource:{...a,path:n.slice(7)}};let o=t.isAbsolute(n)?n:t.resolve(e.meta?.cwd??process.cwd(),n);if(!h.existsSync(o))return{installSource:{...a,path:o}};let i=await y({localPath:o,baseUrl:r.baseUrl,token:r.token});return{installSource:{...a,path:o},uploadedArtifactId:i}}function M(e,r,a,n=0){let o=e.positionals?.[n]??e.flags?.out,i=`${"path"===r?"screenshot":"recording"}-${Date.now()}${a}`,s=o&&o.trim().length>0?o:i;return t.isAbsolute(s)?s:t.resolve(e.meta?.cwd??process.cwd(),s)}function k(e,r){let a=r.startsWith(".")?r:`.${r}`;return t.posix.join("/tmp",`agent-device-${e}-${Date.now()}-${Math.random().toString(36).slice(2,8)}${a}`)}async function N(e){let a;if(e.remoteBaseUrl){let t={transport:"http",token:e.remoteAuthToken??"",pid:0,baseUrl:e.remoteBaseUrl};if(await q(t,"http"))return t;throw new m("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 i=x(e.paths.infoPath),s=n(),l=function(e,r=o()){try{let a=h.statSync(e),n=t.relative(r,e)||e;return`${n}:${a.size}:${Math.trunc(a.mtimeMs)}`}catch{return"unknown"}}((a=z()).useSrc?a.srcPath:a.distPath,a.root),d=!!i&&await q(i,e.transportPreference);if(i&&i.version===s&&i.codeSignature===l&&d)return i;i&&(i.version!==s||i.codeSignature!==l||!d)&&(await O(i),F(e.paths.infoPath)),function(e){let t=L(e);if(!t.hasLock||t.hasInfo)return;let a=C(e.lockPath);if(!a)return F(e.lockPath);r(a.pid,a.processStartTime)||F(e.lockPath)}(e.paths);let u=0;for(let t=1;t<=D;t+=1){await B(e);let r=await T(A,e);if(r)return r;if(await U(e.paths)){u+=1;continue}let a=L(e.paths);if(!(t<D))break;if(!a.hasInfo&&!a.hasLock){await R(150);continue}}let c=L(e.paths);throw new m("COMMAND_FAILED","Failed to start daemon",{kind:"daemon_startup_failed",infoPath:e.paths.infoPath,lockPath:e.paths.lockPath,startupTimeoutMs:A,startupAttempts:D,lockRecoveryCount:u,metadataState:c,hint:function(e,t=f(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 T(e,t){let r=Date.now();for(;Date.now()-r<e;){let e=x(t.paths.infoPath);if(e&&await q(e,t.transportPreference))return e;await new Promise(e=>setTimeout(e,100))}return null}async function R(e){await new Promise(t=>setTimeout(t,e))}async function U(e){let t=L(e);if(!t.hasLock||t.hasInfo)return!1;let a=C(e.lockPath);return a&&r(a.pid,a.processStartTime)&&await w(a.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:a.processStartTime}),F(e.lockPath),!0}async function O(e){await w(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}function x(e){let t=$(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 a=Number.isInteger(t.port)&&Number(t.port)>0,n=Number.isInteger(t.httpPort)&&Number(t.httpPort)>0;if(!a&&!n)return null;let o=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:a?Number(t.port):void 0,httpPort:n?Number(t.httpPort):void 0,transport:"socket"===o||"http"===o||"dual"===o?o:void 0,pid:d?Number(t.pid):0,version:i,codeSignature:s,processStartTime:l}}function C(e){let t=$(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 L(e){return{hasInfo:h.existsSync(e.infoPath),hasLock:h.existsSync(e.lockPath)}}function $(e){if(!h.existsSync(e))return null;try{return JSON.parse(h.readFileSync(e,"utf8"))}catch{return null}}function F(e){try{h.existsSync(e)&&h.unlinkSync(e)}catch{}}async function q(e,t){var r;return"http"===G(e,t)?await function(e){let t=e.baseUrl?Y(e.baseUrl,"health"):e.httpPort?`http://127.0.0.1:${e.httpPort}/health`:null;if(!t)return Promise.resolve(!1);let r=new URL(t),a="https:"===r.protocol?i:b,n=e.baseUrl?3e3:500;return new Promise(e=>{let t=a.request({protocol:r.protocol,host:r.hostname,port:r.port,path:r.pathname+r.search,method:"GET",timeout:n},t=>{t.resume(),e((t.statusCode??500)<500)});t.on("timeout",()=>{t.destroy(),e(!1)}),t.on("error",()=>{e(!1)}),t.end()})}(e):await ((r=e.port)?new Promise(e=>{let t=I.createConnection({host:"127.0.0.1",port:r},()=>{t.destroy(),e(!0)});t.on("error",()=>{e(!1)})}):Promise.resolve(!1))}async function B(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};a(process.execPath,r,{env:n})}function z(){let e=o(),r=t.join(e,"dist","src","daemon.js"),a=t.join(e,"src","daemon.ts"),n=h.existsSync(r),i=h.existsSync(a);if(!n&&!i)throw new m("COMMAND_FAILED","Daemon entry not found",{distPath:r,srcPath:a});return{root:e,distPath:r,srcPath:a,useSrc:process.execArgv.includes("--experimental-strip-types")?i:!n&&i}}async function j(e,t,r){return"http"===G(e,r)?await J(e,t):await H(e,t)}function G(e,t){if(e.baseUrl){if("socket"===t)throw new m("COMMAND_FAILED","Remote daemon endpoint only supports HTTP transport",{daemonBaseUrl:e.baseUrl});return"http"}if("http"===t||"socket"===t){var r=e,a=t;if(V(r,a))return a;throw new m("COMMAND_FAILED","http"===a?"Daemon HTTP endpoint is unavailable":"Daemon socket endpoint is unavailable")}let n=("socket"===e.transport||"dual"===e.transport?["socket","http"]:["http","socket"]).find(t=>V(e,t));if(n)return n;throw new m("COMMAND_FAILED","Daemon metadata has no reachable transport")}function V(e,t){return"http"===t?!!e.httpPort:!!e.port}async function H(e,t){let r=e.port;if(!r)throw new m("COMMAND_FAILED","Daemon socket endpoint is unavailable");return new Promise((a,n)=>{let o=I.createConnection({host:"127.0.0.1",port:r},()=>{o.write(`${JSON.stringify(t)} | ||
| `)}),i=setTimeout(()=>{o.destroy();let r=K(),a=W(e,f(t.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR));c({level:"error",phase:"daemon_request_timeout",data:{timeoutMs:v,requestId:t.meta?.requestId,command:t.command,timedOutRunnerPidsTerminated:r.terminated,timedOutRunnerCleanupError:r.error,daemonPidReset:e.pid,daemonPidForceKilled:a.forcedKill}}),n(new m("COMMAND_FAILED","Daemon request timed out",{timeoutMs:v,requestId:t.meta?.requestId,hint:"Retry with --debug and check daemon diagnostics logs. Timed-out iOS runner xcodebuild processes were terminated when detected."}))},v),s="";o.setEncoding("utf8"),o.on("data",e=>{let r=(s+=e).indexOf("\n");if(-1===r)return;let l=s.slice(0,r).trim();if(l)try{let e=JSON.parse(l);o.end(),clearTimeout(i),a(e)}catch(e){clearTimeout(i),n(new m("COMMAND_FAILED","Invalid daemon response",{requestId:t.meta?.requestId,line:l},e instanceof Error?e:void 0))}}),o.on("error",e=>{clearTimeout(i),c({level:"error",phase:"daemon_request_socket_error",data:{requestId:t.meta?.requestId,message:e instanceof Error?e.message:String(e)}}),n(new m("COMMAND_FAILED","Failed to communicate with daemon",{requestId:t.meta?.requestId,hint:"Retry command. If this persists, clean stale daemon metadata and start a fresh session."},e))})})}async function J(t,r){let a=t.baseUrl?new URL(Y(t.baseUrl,"rpc")):t.httpPort?new URL(`http://127.0.0.1:${t.httpPort}/rpc`):null;if(!a)throw new m("COMMAND_FAILED","Daemon HTTP endpoint is unavailable");let n=JSON.stringify({jsonrpc:"2.0",id:r.meta?.requestId??e(),method:"agent_device.command",params:r}),o={"content-type":"application/json","content-length":Buffer.byteLength(n)};return t.baseUrl&&t.token&&(o.authorization=`Bearer ${t.token}`,o["x-agent-device-token"]=t.token),await new Promise((e,s)=>{let l=f(r.flags?.stateDir??process.env.AGENT_DEVICE_STATE_DIR),d=("https:"===a.protocol?i:b).request({protocol:a.protocol,host:a.hostname,port:a.port,method:"POST",path:a.pathname+a.search,headers:o},a=>{let n="";a.setEncoding("utf8"),a.on("data",e=>{n+=e}),a.on("end",()=>{clearTimeout(u);try{let a=JSON.parse(n);if(a.error){let e=a.error.data??{};s(new m(String(e.code??"COMMAND_FAILED"),String(e.message??a.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:r.meta?.requestId}));return}if(!a.result||"object"!=typeof a.result)return void s(new m("COMMAND_FAILED","Invalid daemon RPC response",{requestId:r.meta?.requestId}));if(t.baseUrl&&a.result.ok)return void Q(t,r,a.result).then(e).catch(s);e(a.result)}catch(e){clearTimeout(u),s(new m("COMMAND_FAILED","Invalid daemon response",{requestId:r.meta?.requestId,line:n},e instanceof Error?e:void 0))}})}),u=setTimeout(()=>{d.destroy();let e=X(t)?{terminated:0}:K(),a=X(t)?{forcedKill:!1}:W(t,l);c({level:"error",phase:"daemon_request_timeout",data:{timeoutMs:v,requestId:r.meta?.requestId,command:r.command,timedOutRunnerPidsTerminated:e.terminated,timedOutRunnerCleanupError:e.error,daemonPidReset:X(t)?void 0:t.pid,daemonPidForceKilled:X(t)?void 0:a.forcedKill,daemonBaseUrl:t.baseUrl}}),s(new m("COMMAND_FAILED","Daemon request timed out",{timeoutMs:v,requestId:r.meta?.requestId,hint:X(t)?"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."}))},v);d.on("error",e=>{clearTimeout(u),c({level:"error",phase:"daemon_request_socket_error",data:{requestId:r.meta?.requestId,message:e instanceof Error?e.message:String(e)}}),s(new m("COMMAND_FAILED","Failed to communicate with daemon",{requestId:r.meta?.requestId,hint:X(t)?"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))}),d.write(n),d.end()})}function K(){let e=0;try{for(let t of _){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)}}}function W(e,t){let a=!1;try{r(e.pid,e.processStartTime)&&(process.kill(e.pid,"SIGKILL"),a=!0)}catch{w(e.pid,{termTimeoutMs:3e3,killTimeoutMs:1e3,expectedStartTime:e.processStartTime})}finally{F(t.infoPath),F(t.lockPath)}return{forcedKill:a}}function X(e){return"string"==typeof e.baseUrl&&e.baseUrl.length>0}function Y(e,t){return new URL(t,e.endsWith("/")?e:`${e}/`).toString()}async function Q(e,r,a){let n=Array.isArray(a.data?.artifacts)?a.data.artifacts:[];if(0===n.length||!e.baseUrl)return a;let o=a.data?{...a.data}:{},i=[];for(let a of n){if(!a||"object"!=typeof a||"string"!=typeof a.artifactId){i.push(a);continue}let n=function(e,r){if(e.localPath&&e.localPath.trim().length>0)return e.localPath;let a=e.fileName?.trim()||`${e.field}-${Date.now()}`;return t.resolve(r.meta?.cwd??process.cwd(),a)}(a,r);await Z({baseUrl:e.baseUrl,token:e.token,artifactId:a.artifactId,destinationPath:n,requestId:r.meta?.requestId}),o[a.field]=n,i.push({...a,localPath:n})}return o.artifacts=i,{ok:!0,data:o}}async function Z(e){var r,a;let n,o=new URL((r=e.baseUrl,a=e.artifactId,n=r.endsWith("/")?r:`${r}/`,new URL(`upload/${encodeURIComponent(a)}`,n).toString())),s="https:"===o.protocol?i:b;await h.promises.mkdir(t.dirname(e.destinationPath),{recursive:!0}),await new Promise((t,r)=>{let a=!1,n=e.timeoutMs??v,i=n=>{if(!a){if(a=!0,clearTimeout(d),n)return void h.promises.rm(e.destinationPath,{force:!0}).finally(()=>r(n));t()}},l=s.request({protocol:o.protocol,host:o.hostname,port:o.port,method:"GET",path:o.pathname+o.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 m("COMMAND_FAILED","Failed to download remote artifact",{artifactId:e.artifactId,statusCode:t.statusCode,requestId:e.requestId,body:r}))});return}let r=h.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 m("COMMAND_FAILED","Remote artifact download was interrupted",{artifactId:e.artifactId,requestId:e.requestId}))}),r.on("finish",()=>{r.close(()=>i())}),t.pipe(r)}),d=setTimeout(()=>{l.destroy(new m("COMMAND_FAILED","Remote artifact download timed out",{artifactId:e.artifactId,requestId:e.requestId,timeoutMs:n}))},n);l.on("error",t=>{t instanceof m?i(t):i(new m("COMMAND_FAILED","Failed to download remote artifact",{artifactId:e.artifactId,requestId:e.requestId,timeoutMs:n},t instanceof Error?t:void 0))}),l.end()})}function ee(e){return e.replace(/\/+$/,"")}function et(e){return"string"==typeof e&&e.trim()?ee(e.trim()):""}function er(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function ea(e,t,r){return u(e,{env:t,cwd:r})}function en(e){try{return h.accessSync(e,h.constants.F_OK),!0}catch{return!1}}function eo(e,t,r){if(null==e||""===e)return t;let a=Number.parseInt(String(e),10);return Number.isInteger(a)?Math.max(a,r):t}function ei(e,t){let r;return{platform:t,bundleUrl:((r=new URL(`${ee(e)}/index.bundle`)).searchParams.set("platform",t),r.searchParams.set("dev","true"),r.searchParams.set("minify","false"),r.toString())}}function es(e,t){return{platform:t,metroHost:er(e?.metro_host),metroPort:e?.metro_port,bundleUrl:er(e?.metro_bundle_url),launchUrl:er(e?.launch_url)}}function el(e){return`'${e.replace(/'/g,"'\"'\"'")}'`}async function ed(e){await new Promise(t=>setTimeout(t,e))}async function eu(e,t,r={}){try{let a=await fetch(e,{headers:r,signal:AbortSignal.timeout(t)});return{ok:a.ok,status:a.status,body:await a.text()}}catch(r){if(r instanceof Error&&"TimeoutError"===r.name)throw Error(`Timed out fetching ${e} after ${t}ms`);throw r}}async function ec(e,t){try{let r=await eu(e,t);return r.ok&&r.body.includes("packager-status:running")}catch{return!1}}async function ep(e){var t,r,a;let n;try{n=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 o=await n.text(),i=o?JSON.parse(o):{};if(!n.ok)throw Error(`/api/metro/bridge failed (${n.status}): ${JSON.stringify(i)}`);return{enabled:(a=i.data??i).enabled,baseUrl:a.base_url,statusUrl:a.status_url,bundleUrl:a.bundle_url,iosRuntime:es(a.ios_runtime,"ios"),androidRuntime:es(a.android_runtime,"android"),upstream:{bundleUrl:a.upstream.bundle_url,host:a.upstream.host,port:a.upstream.port,statusUrl:a.upstream.status_url},probe:{reachable:a.probe.reachable,statusCode:a.probe.status_code,latencyMs:a.probe.latency_ms,detail:a.probe.detail}}}async function em(e,t,r){let a=Date.now()+t;for(;Date.now()<a;){let t=Math.min(r,Math.max(a-Date.now(),1));if(await ec(e,t))return!0;let n=Math.min(500,Math.max(a-Date.now(),0));n>0&&await ed(n)}return!1}async function eh(e={}){let r=e.env??process.env,a=process.cwd(),n=ea(e.projectRoot??a,r,a),o=function(e,r){if("auto"!==r)return r;let a=function(e){let r=t.join(e,"package.json");if(!en(r))throw new m("INVALID_ARGS",`package.json not found at ${r}`);return JSON.parse(h.readFileSync(r,"utf8"))}(e);return"string"==typeof({...a.dependencies??{},...a.devDependencies??{}}).expo?"expo":"react-native"}(n,e.kind??"auto"),i=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 m("INVALID_ARGS",`Invalid Metro port: ${String(e)}. Use 1-65535.`);return r}(e.metroPort??8081,0),l=er(e.listenHost)??"0.0.0.0",d=er(e.statusHost)??"127.0.0.1",u=et(e.publicBaseUrl),c=eo(e.startupTimeoutMs,18e4,3e4),p=eo(e.probeTimeoutMs,1e4,1e3),f=e.reuseExisting??!0,I=e.installDependenciesIfNeeded??!0,g=e.runtimeFilePath?ea(e.runtimeFilePath,r,a):null,w=ea(e.logPath??t.join(n,".agent-device","metro.log"),r,a);if(!u)throw new m("INVALID_ARGS","metro prepare requires --public-base-url <url>.");let{proxyEnabled:b,proxyBaseUrl:y,proxyBearerToken:v}=function(e,t){if(e&&!t)throw new m("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 m("INVALID_ARGS","metro prepare requires --proxy-base-url when proxy auth is provided.");return{proxyEnabled:!!(e&&t),proxyBaseUrl:e,proxyBearerToken:t}}(et(e.proxyBaseUrl),er(e.proxyBearerToken)??""),A=I?function(e,r){if(function(e){try{return h.statSync(e).isDirectory()}catch{return!1}}(t.join(e,"node_modules")))return{installed:!1};let a=en(t.join(e,"pnpm-lock.yaml"))?{command:"pnpm",installArgs:["install"]}:en(t.join(e,"yarn.lock"))?{command:"yarn",installArgs:["install"]}:{command:"npm",installArgs:["install"]};return s(a.command,a.installArgs,{cwd:e,env:r}),{installed:!0,packageManager:a.command}}(n,r):{installed:!1},D=`http://${d}:${i}/status`,_=!1,P=!1,E=0;if(f&&await ec(D,p))P=!0;else if(_=!0,E=function(e,r,a,n,o,i){let l="expo"===r?{command:"npx",installArgs:["expo","start","--host","lan","--port",String(a)]}:{command:"npx",installArgs:["react-native","start","--host",n,"--port",String(a)]};h.mkdirSync(t.dirname(o),{recursive:!0});let d=[el(l.command),...l.installArgs.map(el)].join(" "),u=s("/bin/sh",["-c",`nohup ${d} >> ${el(o)} 2>&1 < /dev/null & echo $!`],{cwd:e,env:i}),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}}(n,o,i,l,w,r).pid,!await em(D,c,p))throw Error(`Metro did not become ready at ${D} within ${c}ms. Check ${w}.`);let S=ei(u,"ios"),M=ei(u,"android"),k=null,N=null;if(b)try{k=await ep({baseUrl:y,bearerToken:v,runtime:{metro_bundle_url:S.bundleUrl},timeoutMs:p})}catch(e){N=e instanceof Error?e.message:String(e)}if(b&&(!k||!1===k.probe.reachable)){var T,R;let e;throw Error((T=N,R=k,e=[`Metro bridge is required for this run but could not be configured via ${y}/api/metro/bridge.`],T&&e.push(`bridgeError=${T}`),R?.probe.reachable===!1&&e.push(`bridgeProbe=${R.probe.detail||`unreachable (status ${R.probe.statusCode||0})`}`),e.join(" ")))}let U=k?.iosRuntime??S,O=k?.androidRuntime??M,x={projectRoot:n,kind:o,dependenciesInstalled:A.installed,packageManager:A.packageManager??null,started:_,reused:P,pid:E,logPath:w,statusUrl:D,runtimeFilePath:g,iosRuntime:U,androidRuntime:O,bridge:k};return g&&(h.mkdirSync(t.dirname(g),{recursive:!0}),h.writeFileSync(g,JSON.stringify(x,null,2))),x}function ef(e){let t=e.appId??e.bundleId??e.packageName;return{session:e.session,appId:t,appBundleId:e.bundleId,package:e.packageName}}function eI(e,t,r){return{deviceId:t,deviceName:r,..."android"===e?{serial:t}:"ios"===e?{udid:t}:{}}}function eg(e,t={}){let r=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&&r?{serial:e.android?.serial??e.id}:{}}}function ew(e){return{name:e.name,...eg(e.device,{includeAndroidSerial:!1}),createdAt:e.createdAt}}function eb(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 ey(e){return{udid:e.udid,device:e.device,runtime:e.runtime,ios_simulator_device_set:e.iosSimulatorDeviceSet??null,created:e.created,booted:e.booted}}function ev(e){return{app:e.app,appPath:e.appPath,platform:e.platform,...e.appId?{appId:e.appId}:{},...e.bundleId?{bundleId:e.bundleId}:{},...e.package?{package:e.package}:{}}}function eA(e){return{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}:{}}}function eD(e){return{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?eg(e.device):{}}}function e_(e){return{session:e.session,...e.shutdown?{shutdown:e.shutdown}:{}}}function eP(e){return{nodes:e.nodes,truncated:e.truncated,...e.appName?{appName:e.appName}:{},...e.appBundleId?{appBundleId:e.appBundleId}:{}}}function eE(e,t){let r=eO(e,"bundleId"),a=eO(e,"package");return{app:eU(e,"app"),appPath:eU(e,"appPath"),platform:ex(e,"platform"),appId:r??a,bundleId:r,package:a,identifiers:ef({session:t,bundleId:r,packageName:a})}}function eS(e){var t;let r=eT(e),a=ex(r,"platform"),n=eU(r,"id"),o=eU(r,"name");return{platform:a,target:eC(r,"target"),kind:eL(r,t="kind",ez,`Daemon response has invalid "${t}".`),id:n,name:o,booted:"boolean"==typeof r.booted?r.booted:void 0,identifiers:eI(a,n,o),ios:"ios"===a?{udid:n}:void 0,android:"android"===a?{serial:n}:void 0}}function eM(e){var t;let r=eT(e),a=ex(r,"platform"),n=eU(r,"id"),o=eU(r,"name"),i=eC(r,"target"),s=eU(r,"device"),l={session:o,...eI(a,n,s)};return{name:o,createdAt:eL(r,t="createdAt",eq,`Daemon response is missing numeric "${t}".`),device:{platform:a,target:i,id:n,name:s,identifiers:l,ios:"ios"===a?{udid:n,simulatorSetPath:e$(r,"ios_simulator_device_set",eF)}:void 0,android:"android"===a?{serial:n}:void 0},identifiers:l}}function ek(e,t){return t??e??"default"}function eN(e){let t={};for(let[r,a]of Object.entries(e))void 0!==a&&(t[r]=a);return t}function eT(e){if(!eR(e))throw new m("COMMAND_FAILED","Daemon returned an unexpected response shape.",{value:e});return e}function eR(e){return"object"==typeof e&&null!==e}function eU(e,t){return eL(e,t,eF,`Daemon response is missing "${t}".`)}function eO(e,t){return eF(e[t])}function ex(e,t){return eL(e,t,eB,`Daemon response has invalid "${t}".`)}function eC(e,t){return ej(e[t])??"mobile"}function eL(e,t,r,a){let n=r(e[t]);if(void 0===n)throw new m("COMMAND_FAILED",a,{response:e});return n}function e$(e,t,r){let a=e[t];return null===a?null:r(a)}function eF(e){return"string"==typeof e&&e.length>0?e:void 0}function eq(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function eB(e){return"ios"===e||"macos"===e||"android"===e?e:void 0}function ez(e){return"simulator"===e||"emulator"===e||"device"===e?e:void 0}function ej(e){return"tv"===e||"mobile"===e||"desktop"===e?e:void 0}function eG(e={},t={}){let r=t.transport??P,a=async(t,a=[],n={})=>{let o={...e,...n},i=await r({session:ek(e.session,n.session),command:t,positionals:a,flags:eN({stateDir:o.stateDir,daemonBaseUrl:o.daemonBaseUrl,daemonAuthToken:o.daemonAuthToken,daemonTransport:o.daemonTransport,daemonServerMode:o.daemonServerMode,tenant:o.tenant,sessionIsolation:o.sessionIsolation,runId:o.runId,leaseId:o.leaseId,platform:o.platform,target:o.target,device:o.device,udid:o.udid,serial:o.serial,iosSimulatorDeviceSet:o.iosSimulatorDeviceSet,androidDeviceAllowlist:o.androidDeviceAllowlist,runtime:o.simulatorRuntimeId,boot:o.boot,reuseExisting:o.reuseExisting,activity:o.activity,relaunch:o.relaunch,shutdown:o.shutdown,saveScript:o.saveScript,noRecord:o.noRecord,metroHost:o.metroHost,metroPort:o.metroPort,bundleUrl:o.bundleUrl,launchUrl:o.launchUrl,snapshotInteractiveOnly:o.interactiveOnly,snapshotCompact:o.compact,snapshotDepth:o.depth,snapshotScope:o.scope,snapshotRaw:o.raw,verbose:o.debug}),runtime:o.runtime,meta:eN({requestId:o.requestId,cwd:o.cwd,debug:o.debug,lockPolicy:o.lockPolicy,lockPlatform:o.lockPlatform,tenantId:o.tenant,runId:o.runId,leaseId:o.leaseId,sessionIsolation:o.sessionIsolation,installSource:o.installSource,retainMaterializedPaths:o.retainMaterializedPaths,materializedPathRetentionMs:o.materializedPathRetentionMs,materializationId:o.materializationId})});if(!i.ok)throw new m(i.error.code,i.error.message,{...i.error.details??{},hint:i.error.hint,diagnosticId:i.error.diagnosticId,logPath:i.error.logPath});return i.data??{}},n=async(e={})=>{let t=await a("session_list",[],e);return(Array.isArray(t.sessions)?t.sessions:[]).map(eM)};return{devices:{list:async(e={})=>{let t=await a("devices",[],e);return(Array.isArray(t.devices)?t.devices:[]).map(eS)}},sessions:{list:async(e={})=>await n(e),close:async(t={})=>{let r=ek(e.session,t.session),n=(await a("close",[],t)).shutdown;return{session:r,shutdown:"object"==typeof n&&null!==n?n:void 0,identifiers:{session:r}}}},simulators:{ensure:async e=>{let{runtime:t,...r}=e,n=await a("ensure-simulator",[],{...r,simulatorRuntimeId:t}),o=eU(n,"udid"),i=eU(n,"device");return{udid:o,device:i,runtime:eU(n,"runtime"),created:!0===n.created,booted:!0===n.booted,iosSimulatorDeviceSet:e$(n,"ios_simulator_device_set",eF),identifiers:{deviceId:o,deviceName:i,udid:o}}}},apps:{install:async t=>eE(await a("install",[t.app,t.appPath],t),ek(e.session,t.session)),reinstall:async t=>eE(await a("reinstall",[t.app,t.appPath],t),ek(e.session,t.session)),installFromSource:async t=>(function(e,t){let r=eO(e,"bundleId"),a=eO(e,"packageName"),n=r??a??eO(e,"appId"),o=eO(e,"launchTarget")??a??r??n;if(!o)throw new m("COMMAND_FAILED",'Daemon response is missing "launchTarget".',{response:e});return{appName:eO(e,"appName"),appId:n,bundleId:r,packageName:a,launchTarget:o,installablePath:eO(e,"installablePath"),archivePath:eO(e,"archivePath"),materializationId:eO(e,"materializationId"),materializationExpiresAt:eO(e,"materializationExpiresAt"),identifiers:ef({session:t,bundleId:r,packageName:a,appId:n})}})(await a("install_source",[],{...t,installSource:t.source,retainMaterializedPaths:t.retainPaths,materializedPathRetentionMs:t.retentionMs}),ek(e.session,t.session)),open:async t=>{let r=ek(e.session,t.session),n=t.url?[t.app,t.url]:[t.app],o=await a("open",n,t),i=function(e){let t=e.platform,r=eO(e,"id"),a=eO(e,"device");if("ios"!==t&&"macos"!==t&&"android"!==t||!r||!a)return;let n=eC(e,"target"),o=eI(t,r,a);return{platform:t,target:n,id:r,name:a,identifiers:o,ios:"ios"===t?{udid:eO(e,"device_udid")??r,simulatorSetPath:e$(e,"ios_simulator_device_set",eF)}:void 0,android:"android"===t?{serial:eO(e,"serial")??r}:void 0}}(o),s=eO(o,"appBundleId");return{session:r,appName:eO(o,"appName"),appBundleId:s,appId:s,startup:function(e){if(eR(e)&&"number"==typeof e.durationMs&&"string"==typeof e.measuredAt&&"string"==typeof e.method)return{durationMs:e.durationMs,measuredAt:e.measuredAt,method:e.method,appTarget:eO(e,"appTarget"),appBundleId:eO(e,"appBundleId")}}(o.startup),runtime:function(e){if(!eR(e))return;let t=e.platform,r=eO(e,"metroHost"),a="number"==typeof e.metroPort?e.metroPort:void 0;return{platform:"ios"===t||"android"===t?t:void 0,metroHost:r,metroPort:a,bundleUrl:eO(e,"bundleUrl"),launchUrl:eO(e,"launchUrl")}}(o.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=ek(e.session,t.session),n=(await a("close",t.app?[t.app]:[],t)).shutdown;return{session:r,closedApp:t.app,shutdown:"object"==typeof n&&null!==n?n:void 0,identifiers:{session:r}}}},materializations:{release:async e=>{var t;return{released:!0===(t=await a("release_materialized_paths",[],{...e,materializationId:e.materializationId})).released,materializationId:eU(t,"materializationId"),identifiers:{}}}},metro:{prepare:async t=>await eh({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 n=ek(e.session,t.session),o=await a("snapshot",[],t),i=eO(o,"appBundleId");return{nodes:Array.isArray(r=o.nodes)?r:[],truncated:!0===o.truncated,appName:eO(o,"appName"),appBundleId:i,identifiers:{session:n,appId:i,appBundleId:i}}},screenshot:async(t={})=>{let r=ek(e.session,t.session);return{path:eU(await a("screenshot",t.path?[t.path]:[],t),"path"),identifiers:{session:r}}}}}}export{eG as createAgentDeviceClient,P as sendToDaemon,e_ as serializeCloseResult,ev as serializeDeployResult,eb as serializeDevice,ey as serializeEnsureSimulatorResult,eA as serializeInstallFromSourceResult,eD as serializeOpenResult,ew as serializeSessionListEntry,eP as serializeSnapshotResult}; |
| import{URL as e,fileURLToPath as t,pathToFileURL as r}from"node:url";import{spawn as n,spawnSync as o}from"node:child_process";import i from"node:os";import s,{existsSync as a,promises as d}from"node:fs";import u from"node:path";import{AsyncLocalStorage as c}from"node:async_hooks";import l,{createHash as f}from"node:crypto";let m=new c,p=/(token|secret|password|authorization|cookie|api[_-]?key|access[_-]?key|private[_-]?key)/i,h=/(bearer\s+[a-z0-9._-]+|(?:api[_-]?key|token|secret|password)\s*[=:]\s*\S+)/i;function g(){return l.randomBytes(8).toString("hex")}async function w(e,t){let r={...e,diagnosticId:`${Date.now().toString(36)}-${l.randomBytes(4).toString("hex")}`,events:[]};return await m.run(r,t)}function S(){let e=m.getStore();return e?{diagnosticId:e.diagnosticId,requestId:e.requestId,session:e.session,command:e.command,debug:e.debug}:{}}function E(e){let t=m.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?y(e.data):void 0};if(t.events.push(r),!t.debug)return;let n=`[agent-device][diag] ${JSON.stringify(r)} | ||
| `;try{t.logPath&&s.appendFile(t.logPath,n,()=>{}),t.traceLogPath&&s.appendFile(t.traceLogPath,n,()=>{}),t.logPath||t.traceLogPath||process.stderr.write(n)}catch{}}async function D(e,t,r){let n=Date.now();try{let o=await t();return E({level:"info",phase:e,durationMs:Date.now()-n,data:r}),o}catch(t){throw E({level:"error",phase:e,durationMs:Date.now()-n,data:{...r??{},error:t instanceof Error?t.message:String(t)}}),t}}function v(e={}){let t=m.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,"_"),r=new Date().toISOString().slice(0,10),n=u.join(i.homedir(),".agent-device","logs",e,r);s.mkdirSync(n,{recursive:!0});let o=new Date().toISOString().replace(/[:.]/g,"-"),a=u.join(n,`${o}-${t.diagnosticId}.ndjson`),d=t.events.map(e=>JSON.stringify(y(e)));return s.writeFileSync(a,`${d.join("\n")} | ||
| `),t.events=[],a}catch{return null}}function y(e){return function e(t,r,n){if(null==t)return t;if("string"==typeof t){var o=t,i=n;let e=o.trim();if(!e)return o;if(i&&p.test(i)||h.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 s={};for(let[n,o]of Object.entries(t)){if(p.test(n)){s[n]="[REDACTED]";continue}s[n]=e(o,r,n)}return s}(e,new WeakSet)}class I extends Error{code;details;cause;constructor(e,t,r,n){super(t),this.code=e,this.details=r,this.cause=n}}function M(e){return e instanceof I?e:e instanceof Error?new I("UNKNOWN",e.message,void 0,e):new I("UNKNOWN","Unknown error",{err:e})}function A(e,t={}){let r=M(e),n=r.details?y(r.details):void 0,o=n&&"string"==typeof n.hint?n.hint:void 0,i=(n&&"string"==typeof n.diagnosticId?n.diagnosticId:void 0)??t.diagnosticId,s=(n&&"string"==typeof n.logPath?n.logPath:void 0)??t.logPath,a=o??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"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."}}(r.code),d=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}(n),u=function(e,t,r){if("COMMAND_FAILED"!==e||r?.processExitError!==!0)return t;let n=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 r of e.split("\n")){let e=r.trim();if(e&&!t.some(t=>t.test(e)))return e.length>200?`${e.slice(0,200)}...`:e}return null}("string"==typeof r?.stderr?r.stderr:"");return n||t}(r.code,r.message,n);return{code:r.code,message:u,hint:a,diagnosticId:i,logPath:s,details:d}}async function N(e,t,r={}){return new Promise((o,i)=>{let s=n(e,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"],detached:r.detached}),a="",d=r.binaryStdout?Buffer.alloc(0):void 0,u="",c=!1,l=L(r.timeoutMs),f=l?setTimeout(()=>{c=!0,s.kill("SIGKILL")},l):null;r.binaryStdout||s.stdout.setEncoding("utf8"),s.stderr.setEncoding("utf8"),void 0!==r.stdin&&s.stdin.write(r.stdin),s.stdin.end(),s.stdout.on("data",e=>{r.binaryStdout?d=Buffer.concat([d??Buffer.alloc(0),Buffer.isBuffer(e)?e:Buffer.from(e)]):a+=e}),s.stderr.on("data",e=>{u+=e}),s.on("error",r=>{(f&&clearTimeout(f),"ENOENT"===r.code)?i(new I("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},r)):i(new I("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},r))}),s.on("close",n=>{f&&clearTimeout(f);let s=n??1;c&&l?i(new I("COMMAND_FAILED",`${e} timed out after ${l}ms`,{cmd:e,args:t,stdout:a,stderr:u,exitCode:s,timeoutMs:l})):0===s||r.allowFailure?o({stdout:a,stderr:u,exitCode:s,stdoutBuffer:d}):i(new I("COMMAND_FAILED",`${e} exited with code ${s}`,{cmd:e,args:t,stdout:a,stderr:u,exitCode:s,processExitError:!0}))})})}async function O(e){try{var t;let{shell:r,args:n}=(t=e,"win32"===process.platform?{shell:"cmd.exe",args:["/c","where",t]}:{shell:"bash",args:["-lc",`command -v ${t}`]}),o=await N(r,n,{allowFailure:!0});return 0===o.exitCode&&o.stdout.trim().length>0}catch{return!1}}function T(e,t,r={}){let n=o(e,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"],encoding:r.binaryStdout?void 0:"utf8",input:r.stdin,timeout:L(r.timeoutMs)});if(n.error){let o=n.error.code;if("ETIMEDOUT"===o)throw new I("COMMAND_FAILED",`${e} timed out after ${L(r.timeoutMs)}ms`,{cmd:e,args:t,timeoutMs:L(r.timeoutMs)},n.error);if("ENOENT"===o)throw new I("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},n.error);throw new I("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},n.error)}let i=r.binaryStdout?Buffer.isBuffer(n.stdout)?n.stdout:Buffer.from(n.stdout??""):void 0,s=r.binaryStdout?"":"string"==typeof n.stdout?n.stdout:(n.stdout??"").toString(),a="string"==typeof n.stderr?n.stderr:(n.stderr??"").toString(),d=n.status??1;if(0!==d&&!r.allowFailure)throw new I("COMMAND_FAILED",`${e} exited with code ${d}`,{cmd:e,args:t,stdout:s,stderr:a,exitCode:d,processExitError:!0});return{stdout:s,stderr:a,exitCode:d,stdoutBuffer:i}}function _(e,t,r={}){n(e,t,{cwd:r.cwd,env:r.env,stdio:"ignore",detached:!0}).unref()}async function P(e,t,r={}){return new Promise((o,i)=>{let s=n(e,t,{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"],detached:r.detached});r.onSpawn?.(s);let a="",d="",u=r.binaryStdout?Buffer.alloc(0):void 0;r.binaryStdout||s.stdout.setEncoding("utf8"),s.stderr.setEncoding("utf8"),void 0!==r.stdin&&s.stdin.write(r.stdin),s.stdin.end(),s.stdout.on("data",e=>{if(r.binaryStdout){u=Buffer.concat([u??Buffer.alloc(0),Buffer.isBuffer(e)?e:Buffer.from(e)]);return}let t=String(e);a+=t,r.onStdoutChunk?.(t)}),s.stderr.on("data",e=>{let t=String(e);d+=t,r.onStderrChunk?.(t)}),s.on("error",r=>{"ENOENT"===r.code?i(new I("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},r)):i(new I("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},r))}),s.on("close",n=>{let s=n??1;0===s||r.allowFailure?o({stdout:a,stderr:d,exitCode:s,stdoutBuffer:u}):i(new I("COMMAND_FAILED",`${e} exited with code ${s}`,{cmd:e,args:t,stdout:a,stderr:d,exitCode:s,processExitError:!0}))})})}function C(e,t,r={}){let o=n(e,t,{cwd:r.cwd,env:r.env,stdio:["ignore","pipe","pipe"],detached:r.detached}),i="",s="";o.stdout.setEncoding("utf8"),o.stderr.setEncoding("utf8"),o.stdout.on("data",e=>{i+=e}),o.stderr.on("data",e=>{s+=e});let a=new Promise((n,a)=>{o.on("error",r=>{"ENOENT"===r.code?a(new I("TOOL_MISSING",`${e} not found in PATH`,{cmd:e},r)):a(new I("COMMAND_FAILED",`Failed to run ${e}`,{cmd:e,args:t},r))}),o.on("close",o=>{let d=o??1;0===d||r.allowFailure?n({stdout:i,stderr:s,exitCode:d}):a(new I("COMMAND_FAILED",`${e} exited with code ${d}`,{cmd:e,args:t,stdout:i,stderr:s,exitCode:d,processExitError:!0}))})});return{child:o,wait:a}}function L(e){if(!Number.isFinite(e))return;let t=Math.floor(e);if(!(t<=0))return t}function F(){try{let e=$();return JSON.parse(s.readFileSync(u.join(e,"package.json"),"utf8")).version??"0.0.0"}catch{return"0.0.0"}}function $(){let e=u.dirname(t(import.meta.url)),r=e;for(let e=0;e<6;e+=1){let e=u.join(r,"package.json");if(s.existsSync(e))return r;r=u.dirname(r)}return e}let b=[/(^|[\/\s"'=])dist\/src\/daemon\.js($|[\s"'])/,/(^|[\/\s"'=])src\/daemon\.ts($|[\s"'])/];function k(e){if(!Number.isInteger(e)||e<=0)return!1;try{return process.kill(e,0),!0}catch(e){return"EPERM"===e.code}}function x(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=T("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 R(e){if(!Number.isInteger(e)||e<=0)return null;try{let t=T("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 j(e,t){let r;if(!k(e))return!1;if(t){let r=x(e);if(!r||r!==t)return!1}let n=R(e);return!!n&&!!(r=n.toLowerCase().replaceAll("\\","/")).includes("agent-device")&&b.some(e=>e.test(r))}function B(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 U(e,t){if(!k(e))return!0;let r=Date.now();for(;Date.now()-r<t;)if(await new Promise(e=>setTimeout(e,50)),!k(e))return!0;return!k(e)}async function H(e,t){!j(e,t.expectedStartTime)||!B(e,"SIGTERM")||await U(e,t.termTimeoutMs)||B(e,"SIGKILL")&&await U(e,t.killTimeoutMs)}function G(e){return e?.HOME?.trim()||i.homedir()}function q(e,t={}){return"~"===e?G(t.env):e.startsWith("~/")?u.join(G(t.env),e.slice(2)):e}function z(e,t={}){let r=q(e,t);return u.isAbsolute(r)?r:u.resolve(t.cwd??process.cwd(),r)}function K(e){let t,r=(t=(e??"").trim())?z(t):u.join(q("~"),".agent-device");return{baseDir:r,infoPath:u.join(r,"daemon.json"),lockPath:u.join(r,"daemon.lock"),logPath:u.join(r,"daemon.log"),sessionsDir:u.join(r,"sessions")}}function V(e){let t=(e??"").trim().toLowerCase();return"http"===t?"http":"dual"===t?"dual":"socket"}function W(e){let t=(e??"").trim().toLowerCase();return"auto"===t?"auto":"socket"===t?"socket":"http"===t?"http":"auto"}function J(e){return"tenant"===(e??"").trim().toLowerCase()?"tenant":"none"}function Z(e){if(!e)return;let t=e.trim();if(t&&/^[a-zA-Z0-9._-]{1,128}$/.test(t))return t}export{default as node_net}from"node:net";export{default as node_http}from"node:http";export{default as node_https}from"node:https";export{I as AppError,M as asAppError,f as createHash,g as createRequestId,E as emitDiagnostic,a as existsSync,q as expandUserHomePath,e as external_node_url_URL,t as fileURLToPath,$ as findProjectRoot,v as flushDiagnosticsToSessionFile,S as getDiagnosticsMeta,j as isAgentDeviceDaemonProcess,k as isProcessAlive,l as node_crypto,s as node_fs,i as node_os,u as node_path,A as normalizeError,Z as normalizeTenantId,r as pathToFileURL,d as promises,R as readProcessCommand,x as readProcessStartTime,F as readVersion,K as resolveDaemonPaths,V as resolveDaemonServerMode,W as resolveDaemonTransportPreference,J as resolveSessionIsolationMode,z as resolveUserPath,N as runCmd,C as runCmdBackground,_ as runCmdDetached,P as runCmdStreaming,T as runCmdSync,n as spawn,H as stopProcessForTakeover,O as whichCmd,D as withDiagnosticTimer,w as withDiagnosticsScope}; |
| import{AppError as e}from"./331.js";let t="<wifi|airplane|location> <on|off>",r="appearance <light|dark|toggle>",i="faceid <match|nonmatch|enroll|unenroll>",a="touchid <match|nonmatch|enroll|unenroll>",n="fingerprint <match|nonmatch>",s="permission <grant|deny|reset> <camera|microphone|photos|contacts|contacts-limited|notifications|calendar|location|location-always|media-library|motion|reminders|siri> [full|limited]",l="permission <grant|reset> <accessibility|screen-recording|input-monitoring>",o=`settings ${t} | settings ${r} | settings ${i} | settings ${a} | settings ${n} | settings ${s} | settings ${l}`,c=`settings requires ${t}, ${r}, ${i}, ${a}, ${n}, ${s}, or ${l}`,u=["app","frontmost-app","desktop","menubar"];function d(t){let r=t?.trim().toLowerCase();if("app"===r||"frontmost-app"===r||"desktop"===r||"menubar"===r)return r;throw new e("INVALID_ARGS",`Invalid surface: ${t}. Use ${u.join("|")}.`)}function p(e){var t;let r,i=f(e.label),a=f(e.value),n=f(e.identifier),s=(t=n)&&!/^[\w.]+:id\/[\w.-]+$/i.test(t)&&!/^_?NS:\d+$/i.test(t)?n:"";return(r=m(e.type??"")).includes("textfield")||r.includes("securetextfield")||r.includes("searchfield")||r.includes("edittext")||r.includes("textview")||r.includes("textarea")?a||i||s:i||a||s}function f(e){return"string"==typeof e?e.trim():""}function m(e){let t=e.trim().replace(/XCUIElementType/gi,"").replace(/^AX/,"").toLowerCase(),r=Math.max(t.lastIndexOf("."),t.lastIndexOf("/"));return -1!==r&&(t=t.slice(r+1)),t}function h(e,t={}){let r=[],i=[];for(let a of e){let e=a.depth??0;for(;r.length>0&&e<=r[r.length-1];)r.pop();let n=a.label?.trim()||a.value?.trim()||a.identifier?.trim()||"",s=x(a.type??"Element"),l="group"===s&&!n;l&&r.push(e);let o=l?e:Math.max(0,e-r.length);i.push({node:a,depth:o,type:s,text:w(a,o,l,s,t)})}return i}function w(e,t,r,i,a={}){var n,s,l,o,c;let u,d,h=i??x(e.type??"Element"),$=(u=p(e),{text:u,isLargeSurface:d=function(e,t){if("text-view"===t||"text-field"===t||"search"===t)return!0;let r=m(e.type??""),i=`${e.role??""} ${e.subrole??""}`.toLowerCase();return r.includes("textview")||r.includes("textarea")||r.includes("textfield")||r.includes("securetextfield")||r.includes("searchfield")||r.includes("edittext")||i.includes("text area")||i.includes("text field")}(e,h),shouldSummarize:d&&!!(n=u)&&(n.length>80||/[\r\n]/.test(n))}),A=(s=e,l=h,o=a,c=$,o.summarizeTextSurfaces&&c.shouldSummarize&&function(e,t,r){let i=f(e.label);if(i&&i!==r)return i;let a=f(e.identifier);if(a&&!S(a)&&a!==r)return a;switch(t){case"text":case"text-view":return"Text view";case"text-field":return"Text field";case"search":return"Search field";default:return""}}(s,l,c.text)||g(s,l)),y=" ".repeat(t),v=e.ref?`@${e.ref}`:"",I=(function(e,t,r,i){let a,n=[];if(!1===e.enabled&&n.push("disabled"),!r.summarizeTextSurfaces||(!0===e.selected&&n.push("selected"),b(t)&&n.push("editable"),function(e,t){if("scroll-area"===t)return!0;let r=(e.type??"").toLowerCase(),i=`${e.role??""} ${e.subrole??""}`.toLowerCase();return r.includes("scroll")||i.includes("scroll")}(e,t)&&n.push("scrollable"),!i.shouldSummarize))return n;return n.push(`preview:"${((a=i.text.replace(/\s+/g," ").trim()).length<=48?a:`${a.slice(0,45)}...`).replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`),n.push("truncated"),[...new Set(n)]})(e,h,a,$).map(e=>` [${e}]`).join(""),_=A?` "${A}"`:"";return r?`${y}${v} [${h}]${I}`.trimEnd():`${y}${v} [${h}]${_}${I}`.trimEnd()}function g(e,t){let r=e.label?.trim(),i=e.value?.trim();if(b(t)){if(i)return i;if(r)return r}else if(r)return r;if(i)return i;let a=e.identifier?.trim();return!a||S(a)&&("group"===t||"image"===t||"list"===t||"collection"===t)?"":a}function x(e){let t=e.replace(/XCUIElementType/gi,"").toLowerCase(),r=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\./,"")),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 r?"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 b(e){return"text-field"===e||"text-view"===e||"search"===e}function S(e){return/^[\w.]+:id\/[\w.-]+$/i.test(e)}let $=100,A=new Set(["batch","replay"]);function y(t){let r;try{r=JSON.parse(t)}catch{throw new e("INVALID_ARGS","Batch steps must be valid JSON.")}if(!Array.isArray(r)||0===r.length)throw new e("INVALID_ARGS","Batch steps must be a non-empty JSON array.");return r}function v(t,r){if(!Array.isArray(t)||0===t.length)throw new e("INVALID_ARGS","batch requires a non-empty batchSteps array.");if(t.length>r)throw new e("INVALID_ARGS",`batch has ${t.length} steps; max allowed is ${r}.`);let i=[];for(let r=0;r<t.length;r+=1){let a=t[r];if(!a||"object"!=typeof a)throw new e("INVALID_ARGS",`Invalid batch step at index ${r}.`);let n="string"==typeof a.command?a.command.trim().toLowerCase():"";if(!n)throw new e("INVALID_ARGS",`Batch step ${r+1} requires command.`);if(A.has(n))throw new e("INVALID_ARGS",`Batch step ${r+1} cannot run ${n}.`);if(void 0!==a.positionals&&!Array.isArray(a.positionals))throw new e("INVALID_ARGS",`Batch step ${r+1} positionals must be an array.`);let s=a.positionals??[];if(s.some(e=>"string"!=typeof e))throw new e("INVALID_ARGS",`Batch step ${r+1} positionals must contain only strings.`);if(void 0!==a.flags&&("object"!=typeof a.flags||Array.isArray(a.flags)||!a.flags))throw new e("INVALID_ARGS",`Batch step ${r+1} flags must be an object.`);if(void 0!==a.runtime&&("object"!=typeof a.runtime||Array.isArray(a.runtime)||!a.runtime))throw new e("INVALID_ARGS",`Batch step ${r+1} runtime must be an object.`);i.push({command:n,positionals:s,flags:a.flags??{},runtime:a.runtime})}return i}export{TextDecoder,styleText}from"node:util";export{$ as DEFAULT_BATCH_MAX_STEPS,u as SESSION_SURFACES,c as SETTINGS_INVALID_ARGS_MESSAGE,o as SETTINGS_USAGE_OVERRIDE,h as buildSnapshotDisplayLines,g as displayLabel,p as extractReadableText,x as formatRole,w as formatSnapshotLine,y as parseBatchStepsJson,d as parseSessionSurface,v as validateAndNormalizeBatchSteps}; |
| export {}; |
| import type { CliFlags } from './utils/command-schema.ts'; | ||
| import type { AgentDeviceClient } from './client.ts'; | ||
| export declare function tryRunClientBackedCommand(params: { | ||
| command: string; | ||
| positionals: string[]; | ||
| flags: CliFlags; | ||
| client: AgentDeviceClient; | ||
| }): Promise<boolean>; |
| import { sendToDaemon } from './daemon-client.ts'; | ||
| type CliDeps = { | ||
| sendToDaemon: typeof sendToDaemon; | ||
| }; | ||
| export declare function runCli(argv: string[], deps?: CliDeps): Promise<void>; | ||
| export {}; |
| export type MetroPrepareKind = 'auto' | 'react-native' | 'expo'; | ||
| type ResolvedMetroKind = Exclude<MetroPrepareKind, 'auto'>; | ||
| type EnvSource = NodeJS.ProcessEnv | Record<string, string | undefined>; | ||
| export type MetroRuntimeHints = { | ||
| platform?: 'ios' | 'android'; | ||
| metroHost?: string; | ||
| metroPort?: number; | ||
| bundleUrl?: string; | ||
| launchUrl?: string; | ||
| }; | ||
| export type MetroBridgeResult = { | ||
| enabled: boolean; | ||
| baseUrl: string; | ||
| statusUrl: string; | ||
| bundleUrl: string; | ||
| iosRuntime: MetroRuntimeHints; | ||
| androidRuntime: MetroRuntimeHints; | ||
| upstream: { | ||
| bundleUrl: string; | ||
| host: string; | ||
| port: number; | ||
| statusUrl: string; | ||
| }; | ||
| probe: { | ||
| reachable: boolean; | ||
| statusCode: number; | ||
| latencyMs: number; | ||
| detail: string; | ||
| }; | ||
| }; | ||
| export type PrepareMetroRuntimeOptions = { | ||
| projectRoot?: string; | ||
| kind?: MetroPrepareKind; | ||
| metroPort?: number | string; | ||
| listenHost?: string; | ||
| statusHost?: string; | ||
| publicBaseUrl?: string; | ||
| proxyBaseUrl?: string; | ||
| proxyBearerToken?: string; | ||
| startupTimeoutMs?: number | string; | ||
| probeTimeoutMs?: number | string; | ||
| reuseExisting?: boolean; | ||
| installDependenciesIfNeeded?: boolean; | ||
| runtimeFilePath?: string; | ||
| logPath?: string; | ||
| env?: EnvSource; | ||
| }; | ||
| export type PrepareMetroRuntimeResult = { | ||
| projectRoot: string; | ||
| kind: ResolvedMetroKind; | ||
| dependenciesInstalled: boolean; | ||
| packageManager: string | null; | ||
| started: boolean; | ||
| reused: boolean; | ||
| pid: number; | ||
| logPath: string; | ||
| statusUrl: string; | ||
| runtimeFilePath: string | null; | ||
| iosRuntime: MetroRuntimeHints; | ||
| androidRuntime: MetroRuntimeHints; | ||
| bridge: MetroBridgeResult | null; | ||
| }; | ||
| export declare function prepareMetroRuntime(input?: PrepareMetroRuntimeOptions): Promise<PrepareMetroRuntimeResult>; | ||
| export {}; |
| import type { CommandFlags } from './core/dispatch.ts'; | ||
| import type { DaemonRequest, SessionRuntimeHints } from './daemon/types.ts'; | ||
| import type { SnapshotNode } from './utils/snapshot.ts'; | ||
| import type { AgentDeviceDevice, AgentDeviceSession, AgentDeviceSessionDevice, AppDeployResult, AppInstallFromSourceResult, InternalRequestOptions, MaterializationReleaseResult, StartupPerfSample } from './client-types.ts'; | ||
| export declare const DEFAULT_SESSION_NAME = "default"; | ||
| export declare function normalizeDeployResult(data: Record<string, unknown>, session?: string): AppDeployResult; | ||
| export declare function normalizeInstallFromSourceResult(data: Record<string, unknown>, session?: string): AppInstallFromSourceResult; | ||
| export declare function normalizeMaterializationReleaseResult(data: Record<string, unknown>): MaterializationReleaseResult; | ||
| export declare function normalizeDevice(value: unknown): AgentDeviceDevice; | ||
| export declare function normalizeSession(value: unknown): AgentDeviceSession; | ||
| export declare function normalizeRuntimeHints(value: unknown): SessionRuntimeHints | undefined; | ||
| export declare function normalizeOpenDevice(value: Record<string, unknown>): AgentDeviceSessionDevice | undefined; | ||
| export declare function normalizeStartupSample(value: unknown): StartupPerfSample | undefined; | ||
| export declare function readSnapshotNodes(value: unknown): SnapshotNode[]; | ||
| export declare function buildFlags(options: InternalRequestOptions): CommandFlags; | ||
| export declare function buildMeta(options: InternalRequestOptions): DaemonRequest['meta']; | ||
| export declare function resolveSessionName(defaultSession: string | undefined, session: string | undefined): string; | ||
| export declare function readRequiredString(record: Record<string, unknown>, key: string): string; | ||
| export declare function readOptionalString(record: Record<string, unknown>, key: string): string | undefined; | ||
| export declare function readNullableString(record: Record<string, unknown>, key: string): string | null | undefined; |
| import type { AgentDeviceDevice, AgentDeviceIdentifiers, AgentDeviceSession, AgentDeviceSessionDevice, AppCloseResult, AppDeployResult, AppInstallFromSourceResult, AppOpenResult, CaptureSnapshotResult, EnsureSimulatorResult, SessionCloseResult } from './client-types.ts'; | ||
| import type { Platform } from './utils/device.ts'; | ||
| export declare function buildAppIdentifiers(params: { | ||
| session?: string; | ||
| bundleId?: string; | ||
| packageName?: string; | ||
| appId?: string; | ||
| }): AgentDeviceIdentifiers; | ||
| export declare function buildDeviceIdentifiers(platform: Platform, id: string, name: string): AgentDeviceIdentifiers; | ||
| export declare function serializeSessionDevice(device: AgentDeviceSessionDevice, options?: { | ||
| includeAndroidSerial?: boolean; | ||
| }): Record<string, unknown>; | ||
| export declare function serializeSessionListEntry(session: AgentDeviceSession): Record<string, unknown>; | ||
| export declare function serializeDevice(device: AgentDeviceDevice): Record<string, unknown>; | ||
| export declare function serializeEnsureSimulatorResult(result: EnsureSimulatorResult): Record<string, unknown>; | ||
| export declare function serializeDeployResult(result: AppDeployResult): Record<string, unknown>; | ||
| export declare function serializeInstallFromSourceResult(result: AppInstallFromSourceResult): Record<string, unknown>; | ||
| export declare function serializeOpenResult(result: AppOpenResult): Record<string, unknown>; | ||
| export declare function serializeCloseResult(result: SessionCloseResult | AppCloseResult): Record<string, unknown>; | ||
| export declare function serializeSnapshotResult(result: CaptureSnapshotResult): Record<string, unknown>; |
| import type { DaemonInstallSource, DaemonLockPolicy, DaemonRequest, DaemonResponse, SessionRuntimeHints } from './daemon/types.ts'; | ||
| import type { DeviceKind, DeviceTarget, Platform, PlatformSelector } from './utils/device.ts'; | ||
| import type { SnapshotNode } from './utils/snapshot.ts'; | ||
| import type { MetroPrepareKind, PrepareMetroRuntimeResult } from './client-metro.ts'; | ||
| type DaemonTransportMode = 'auto' | 'socket' | 'http'; | ||
| type DaemonServerMode = 'socket' | 'http' | 'dual'; | ||
| type SessionIsolationMode = 'none' | 'tenant'; | ||
| export type AgentDeviceDaemonTransport = (req: Omit<DaemonRequest, 'token'>) => Promise<DaemonResponse>; | ||
| export type AgentDeviceClientConfig = { | ||
| session?: string; | ||
| lockPolicy?: DaemonLockPolicy; | ||
| lockPlatform?: PlatformSelector; | ||
| requestId?: string; | ||
| stateDir?: string; | ||
| daemonBaseUrl?: string; | ||
| daemonAuthToken?: string; | ||
| daemonTransport?: DaemonTransportMode; | ||
| daemonServerMode?: DaemonServerMode; | ||
| tenant?: string; | ||
| sessionIsolation?: SessionIsolationMode; | ||
| runId?: string; | ||
| leaseId?: string; | ||
| cwd?: string; | ||
| debug?: boolean; | ||
| }; | ||
| export type AgentDeviceRequestOverrides = Pick<AgentDeviceClientConfig, 'session' | 'lockPolicy' | 'lockPlatform' | 'requestId' | 'tenant' | 'sessionIsolation' | 'runId' | 'leaseId' | 'cwd' | 'debug'>; | ||
| export type AgentDeviceIdentifiers = { | ||
| session?: string; | ||
| deviceId?: string; | ||
| deviceName?: string; | ||
| udid?: string; | ||
| serial?: string; | ||
| appId?: string; | ||
| appBundleId?: string; | ||
| package?: string; | ||
| }; | ||
| export type AgentDeviceSelectionOptions = { | ||
| platform?: PlatformSelector; | ||
| target?: DeviceTarget; | ||
| device?: string; | ||
| udid?: string; | ||
| serial?: string; | ||
| iosSimulatorDeviceSet?: string; | ||
| androidDeviceAllowlist?: string; | ||
| }; | ||
| export type AgentDeviceDevice = { | ||
| platform: Platform; | ||
| target: DeviceTarget; | ||
| kind: DeviceKind; | ||
| id: string; | ||
| name: string; | ||
| booted?: boolean; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| ios?: { | ||
| udid: string; | ||
| }; | ||
| android?: { | ||
| serial: string; | ||
| }; | ||
| }; | ||
| export type AgentDeviceSessionDevice = { | ||
| platform: Platform; | ||
| target: DeviceTarget; | ||
| id: string; | ||
| name: string; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| ios?: { | ||
| udid: string; | ||
| simulatorSetPath?: string | null; | ||
| }; | ||
| android?: { | ||
| serial: string; | ||
| }; | ||
| }; | ||
| export type AgentDeviceSession = { | ||
| name: string; | ||
| createdAt: number; | ||
| device: AgentDeviceSessionDevice; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| export type StartupPerfSample = { | ||
| durationMs: number; | ||
| measuredAt: string; | ||
| method: string; | ||
| appTarget?: string; | ||
| appBundleId?: string; | ||
| }; | ||
| export type SessionCloseResult = { | ||
| session: string; | ||
| shutdown?: Record<string, unknown>; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| export type EnsureSimulatorOptions = AgentDeviceRequestOverrides & { | ||
| device: string; | ||
| runtime?: string; | ||
| boot?: boolean; | ||
| reuseExisting?: boolean; | ||
| iosSimulatorDeviceSet?: string; | ||
| }; | ||
| export type EnsureSimulatorResult = { | ||
| udid: string; | ||
| device: string; | ||
| runtime: string; | ||
| created: boolean; | ||
| booted: boolean; | ||
| iosSimulatorDeviceSet?: string | null; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| export type AppDeployOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & { | ||
| app: string; | ||
| appPath: string; | ||
| }; | ||
| export type AppDeployResult = { | ||
| app: string; | ||
| appPath: string; | ||
| platform: Platform; | ||
| appId?: string; | ||
| bundleId?: string; | ||
| package?: string; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| export type AppOpenOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & { | ||
| app: string; | ||
| url?: string; | ||
| activity?: string; | ||
| relaunch?: boolean; | ||
| saveScript?: boolean | string; | ||
| noRecord?: boolean; | ||
| runtime?: SessionRuntimeHints; | ||
| }; | ||
| export type AppOpenResult = { | ||
| session: string; | ||
| appName?: string; | ||
| appBundleId?: string; | ||
| appId?: string; | ||
| startup?: StartupPerfSample; | ||
| runtime?: SessionRuntimeHints; | ||
| device?: AgentDeviceSessionDevice; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| export type AppCloseOptions = AgentDeviceRequestOverrides & { | ||
| app?: string; | ||
| shutdown?: boolean; | ||
| }; | ||
| export type AppCloseResult = { | ||
| session: string; | ||
| closedApp?: string; | ||
| shutdown?: Record<string, unknown>; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| export type AppInstallFromSourceOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & { | ||
| source: DaemonInstallSource; | ||
| retainPaths?: boolean; | ||
| retentionMs?: number; | ||
| }; | ||
| export type AppInstallFromSourceResult = { | ||
| appName?: string; | ||
| appId?: string; | ||
| bundleId?: string; | ||
| packageName?: string; | ||
| launchTarget: string; | ||
| installablePath?: string; | ||
| archivePath?: string; | ||
| materializationId?: string; | ||
| materializationExpiresAt?: string; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| export type MaterializationReleaseOptions = AgentDeviceRequestOverrides & { | ||
| materializationId: string; | ||
| }; | ||
| export type MaterializationReleaseResult = { | ||
| released: boolean; | ||
| materializationId: string; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| export type MetroPrepareOptions = { | ||
| projectRoot?: string; | ||
| kind?: MetroPrepareKind; | ||
| publicBaseUrl: string; | ||
| proxyBaseUrl?: string; | ||
| bearerToken?: string; | ||
| port?: number; | ||
| listenHost?: string; | ||
| statusHost?: string; | ||
| startupTimeoutMs?: number; | ||
| probeTimeoutMs?: number; | ||
| reuseExisting?: boolean; | ||
| installDependenciesIfNeeded?: boolean; | ||
| runtimeFilePath?: string; | ||
| logPath?: string; | ||
| }; | ||
| export type MetroPrepareResult = PrepareMetroRuntimeResult; | ||
| export type CaptureSnapshotOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & { | ||
| interactiveOnly?: boolean; | ||
| compact?: boolean; | ||
| depth?: number; | ||
| scope?: string; | ||
| raw?: boolean; | ||
| }; | ||
| export type CaptureSnapshotResult = { | ||
| nodes: SnapshotNode[]; | ||
| truncated: boolean; | ||
| appName?: string; | ||
| appBundleId?: string; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| export type CaptureScreenshotOptions = AgentDeviceRequestOverrides & { | ||
| path?: string; | ||
| }; | ||
| export type CaptureScreenshotResult = { | ||
| path: string; | ||
| identifiers: AgentDeviceIdentifiers; | ||
| }; | ||
| export type InternalRequestOptions = AgentDeviceClientConfig & AgentDeviceSelectionOptions & { | ||
| simulatorRuntimeId?: string; | ||
| runtime?: SessionRuntimeHints; | ||
| boot?: boolean; | ||
| reuseExisting?: boolean; | ||
| activity?: string; | ||
| relaunch?: boolean; | ||
| shutdown?: boolean; | ||
| saveScript?: boolean | string; | ||
| noRecord?: boolean; | ||
| metroHost?: string; | ||
| metroPort?: number; | ||
| bundleUrl?: string; | ||
| launchUrl?: string; | ||
| interactiveOnly?: boolean; | ||
| compact?: boolean; | ||
| depth?: number; | ||
| scope?: string; | ||
| raw?: boolean; | ||
| installSource?: DaemonInstallSource; | ||
| retainMaterializedPaths?: boolean; | ||
| materializedPathRetentionMs?: number; | ||
| materializationId?: string; | ||
| }; | ||
| export type AgentDeviceClient = { | ||
| devices: { | ||
| list: (options?: AgentDeviceRequestOverrides & AgentDeviceSelectionOptions) => Promise<AgentDeviceDevice[]>; | ||
| }; | ||
| sessions: { | ||
| list: (options?: AgentDeviceRequestOverrides) => Promise<AgentDeviceSession[]>; | ||
| close: (options?: AgentDeviceRequestOverrides & { | ||
| shutdown?: boolean; | ||
| }) => Promise<SessionCloseResult>; | ||
| }; | ||
| simulators: { | ||
| ensure: (options: EnsureSimulatorOptions) => Promise<EnsureSimulatorResult>; | ||
| }; | ||
| apps: { | ||
| install: (options: AppDeployOptions) => Promise<AppDeployResult>; | ||
| reinstall: (options: AppDeployOptions) => Promise<AppDeployResult>; | ||
| installFromSource: (options: AppInstallFromSourceOptions) => Promise<AppInstallFromSourceResult>; | ||
| open: (options: AppOpenOptions) => Promise<AppOpenResult>; | ||
| close: (options?: AppCloseOptions) => Promise<AppCloseResult>; | ||
| }; | ||
| materializations: { | ||
| release: (options: MaterializationReleaseOptions) => Promise<MaterializationReleaseResult>; | ||
| }; | ||
| metro: { | ||
| prepare: (options: MetroPrepareOptions) => Promise<MetroPrepareResult>; | ||
| }; | ||
| capture: { | ||
| snapshot: (options?: CaptureSnapshotOptions) => Promise<CaptureSnapshotResult>; | ||
| screenshot: (options?: CaptureScreenshotOptions) => Promise<CaptureScreenshotResult>; | ||
| }; | ||
| }; | ||
| export {}; |
| import type { AgentDeviceClient, AgentDeviceClientConfig, AgentDeviceDaemonTransport } from './client-types.ts'; | ||
| export declare function createAgentDeviceClient(config?: AgentDeviceClientConfig, deps?: { | ||
| transport?: AgentDeviceDaemonTransport; | ||
| }): AgentDeviceClient; | ||
| export type { AgentDeviceClient, AgentDeviceClientConfig, AgentDeviceDaemonTransport, AgentDeviceDevice, AgentDeviceIdentifiers, AgentDeviceRequestOverrides, AgentDeviceSelectionOptions, AgentDeviceSession, AgentDeviceSessionDevice, AppCloseOptions, AppCloseResult, AppDeployOptions, AppDeployResult, AppInstallFromSourceOptions, AppInstallFromSourceResult, AppOpenOptions, AppOpenResult, CaptureScreenshotOptions, CaptureScreenshotResult, CaptureSnapshotOptions, CaptureSnapshotResult, EnsureSimulatorOptions, EnsureSimulatorResult, MaterializationReleaseOptions, MaterializationReleaseResult, MetroPrepareOptions, MetroPrepareResult, SessionCloseResult, StartupPerfSample, } from './client-types.ts'; |
| import type { DeviceInfo } from '../utils/device.ts'; | ||
| type AppEventPayload = Record<string, unknown> | undefined; | ||
| export declare function parseTriggerAppEventArgs(positionals: string[]): { | ||
| eventName: string; | ||
| payload: AppEventPayload; | ||
| }; | ||
| export declare function resolveAppEventUrl(platform: DeviceInfo['platform'], eventName: string, payload?: AppEventPayload): string; | ||
| export {}; |
| import type { BatchStep, CommandFlags } from './dispatch.ts'; | ||
| export declare const DEFAULT_BATCH_MAX_STEPS = 100; | ||
| export type NormalizedBatchStep = { | ||
| command: string; | ||
| positionals: string[]; | ||
| flags: Partial<CommandFlags>; | ||
| runtime?: unknown; | ||
| }; | ||
| export type BatchStepResult = { | ||
| step: number; | ||
| command: string; | ||
| ok: true; | ||
| data: Record<string, unknown>; | ||
| durationMs: number; | ||
| }; | ||
| export declare function parseBatchStepsJson(raw: string): BatchStep[]; | ||
| export declare function validateAndNormalizeBatchSteps(steps: CommandFlags['batchSteps'], maxSteps: number): NormalizedBatchStep[]; |
| import { type DeviceInfo } from '../utils/device.ts'; | ||
| export declare function isCommandSupportedOnDevice(command: string, device: DeviceInfo): boolean; | ||
| export declare function listCapabilityCommands(): string[]; |
| import { AppError } from '../utils/errors.ts'; | ||
| export type ClickButton = 'primary' | 'secondary' | 'middle'; | ||
| type ClickButtonFlags = { | ||
| clickButton?: ClickButton; | ||
| }; | ||
| export declare function resolveClickButton(flags: ClickButtonFlags | undefined): ClickButton; | ||
| export declare function getClickButtonValidationError(options: { | ||
| commandLabel: string; | ||
| platform: string; | ||
| button: ClickButton; | ||
| count?: number; | ||
| intervalMs?: number; | ||
| holdMs?: number; | ||
| jitterPx?: number; | ||
| doubleTap?: boolean; | ||
| }): AppError | null; | ||
| export declare function buttonTag(button: ClickButton): {} | { | ||
| button: ClickButton; | ||
| }; | ||
| export {}; |
| export declare function readNotificationPayload(payloadArg: string): Promise<Record<string, unknown>>; |
| import { resolveDevice, type DeviceInfo, type PlatformSelector } from '../utils/device.ts'; | ||
| import { findBootableIosSimulator } from '../platforms/ios/devices.ts'; | ||
| import type { CliFlags } from '../utils/command-schema.ts'; | ||
| import type { DeviceTarget } from '../utils/device.ts'; | ||
| type ResolveDeviceFlags = Pick<CliFlags, 'platform' | 'target' | 'device' | 'udid' | 'serial' | 'iosSimulatorDeviceSet' | 'androidDeviceAllowlist'>; | ||
| type AppleDeviceSelector = { | ||
| platform?: Exclude<PlatformSelector, 'android'>; | ||
| target?: DeviceTarget; | ||
| deviceName?: string; | ||
| udid?: string; | ||
| serial?: string; | ||
| }; | ||
| type ResolveAppleDeviceDeps = { | ||
| resolveDevice: typeof resolveDevice; | ||
| findBootableSimulator: typeof findBootableIosSimulator; | ||
| }; | ||
| /** | ||
| * Resolves the best iOS device given pre-fetched candidates. When no explicit | ||
| * device selector was used, physical devices are rejected in favour of a | ||
| * bootable simulator discovered via `findBootableSimulator`. | ||
| * | ||
| * Exported for testing; production callers should use `resolveTargetDevice`. | ||
| */ | ||
| export declare function resolveAppleDevice(devices: DeviceInfo[], selector: AppleDeviceSelector, context: { | ||
| simulatorSetPath?: string; | ||
| }, deps: ResolveAppleDeviceDeps): Promise<DeviceInfo>; | ||
| export declare const resolveIosDevice: typeof resolveAppleDevice; | ||
| export declare function resolveTargetDevice(flags: ResolveDeviceFlags): Promise<DeviceInfo>; | ||
| export {}; |
| import type { DeviceInfo } from '../utils/device.ts'; | ||
| export declare function requireIntInRange(value: number, name: string, min: number, max: number): number; | ||
| export declare function clampIosSwipeDuration(durationMs: number): number; | ||
| export declare function shouldUseIosTapSeries(device: DeviceInfo, count: number, holdMs: number, jitterPx: number): boolean; | ||
| export declare function shouldUseIosDragSeries(device: DeviceInfo, count: number): boolean; | ||
| export declare function computeDeterministicJitter(index: number, jitterPx: number): [number, number]; | ||
| export declare function runRepeatedSeries(count: number, pauseMs: number, operation: (index: number) => Promise<void>): Promise<void>; |
| import type { DeviceInfo } from '../utils/device.ts'; | ||
| import type { SessionSurface } from './session-surface.ts'; | ||
| import type { CliFlags } from '../utils/command-schema.ts'; | ||
| import { shouldUseIosTapSeries, shouldUseIosDragSeries } from './dispatch-series.ts'; | ||
| export { resolveTargetDevice } from './dispatch-resolve.ts'; | ||
| export { shouldUseIosTapSeries, shouldUseIosDragSeries }; | ||
| export type BatchStep = { | ||
| command: string; | ||
| positionals?: string[]; | ||
| flags?: Partial<CommandFlags>; | ||
| runtime?: unknown; | ||
| }; | ||
| export type CommandFlags = Omit<CliFlags, 'json' | 'help' | 'version' | 'batchSteps'> & { | ||
| batchSteps?: BatchStep[]; | ||
| }; | ||
| export declare function dispatchCommand(device: DeviceInfo, command: string, positionals: string[], outPath?: string, context?: { | ||
| requestId?: string; | ||
| appBundleId?: string; | ||
| activity?: string; | ||
| verbose?: boolean; | ||
| logPath?: string; | ||
| traceLogPath?: string; | ||
| snapshotInteractiveOnly?: boolean; | ||
| snapshotCompact?: boolean; | ||
| snapshotDepth?: number; | ||
| snapshotScope?: string; | ||
| snapshotRaw?: boolean; | ||
| count?: number; | ||
| intervalMs?: number; | ||
| holdMs?: number; | ||
| jitterPx?: number; | ||
| doubleTap?: boolean; | ||
| clickButton?: 'primary' | 'secondary' | 'middle'; | ||
| pauseMs?: number; | ||
| pattern?: 'one-way' | 'ping-pong'; | ||
| surface?: SessionSurface; | ||
| }): Promise<Record<string, unknown> | void>; |
| export declare function isDeepLinkTarget(input: string): boolean; | ||
| export declare function isWebUrl(input: string): boolean; | ||
| export declare const IOS_SAFARI_BUNDLE_ID = "com.apple.mobilesafari"; | ||
| export declare function resolveIosDeviceDeepLinkBundleId(appBundleId: string | undefined, url: string): string | undefined; |
| export type SessionSurface = 'app' | 'frontmost-app' | 'desktop' | 'menubar'; | ||
| export declare const SESSION_SURFACES: readonly SessionSurface[]; | ||
| export declare function parseSessionSurface(value: string | undefined): SessionSurface; |
| export declare const SETTINGS_WIFI_USAGE = "<wifi|airplane|location> <on|off>"; | ||
| export declare const SETTINGS_APPEARANCE_USAGE = "appearance <light|dark|toggle>"; | ||
| export declare const SETTINGS_FACEID_USAGE = "faceid <match|nonmatch|enroll|unenroll>"; | ||
| export declare const SETTINGS_TOUCHID_USAGE = "touchid <match|nonmatch|enroll|unenroll>"; | ||
| export declare const SETTINGS_FINGERPRINT_USAGE = "fingerprint <match|nonmatch>"; | ||
| export declare const SETTINGS_PERMISSION_USAGE = "permission <grant|deny|reset> <camera|microphone|photos|contacts|contacts-limited|notifications|calendar|location|location-always|media-library|motion|reminders|siri> [full|limited]"; | ||
| export declare const SETTINGS_MACOS_PERMISSION_USAGE = "permission <grant|reset> <accessibility|screen-recording|input-monitoring>"; | ||
| export declare const SETTINGS_USAGE_OVERRIDE: string; | ||
| export declare const SETTINGS_INVALID_ARGS_MESSAGE = "settings requires <wifi|airplane|location> <on|off>, appearance <light|dark|toggle>, faceid <match|nonmatch|enroll|unenroll>, touchid <match|nonmatch|enroll|unenroll>, fingerprint <match|nonmatch>, permission <grant|deny|reset> <camera|microphone|photos|contacts|contacts-limited|notifications|calendar|location|location-always|media-library|motion|reminders|siri> [full|limited], or permission <grant|reset> <accessibility|screen-recording|input-monitoring>"; |
| import type { DaemonRequest as SharedDaemonRequest, DaemonResponse as SharedDaemonResponse } from './daemon/types.ts'; | ||
| import { type DaemonPaths } from './daemon/config.ts'; | ||
| export type DaemonRequest = SharedDaemonRequest; | ||
| export type DaemonResponse = SharedDaemonResponse; | ||
| export type OpenAppOptions = { | ||
| session?: string; | ||
| app?: string; | ||
| url?: string; | ||
| lockPolicy?: NonNullable<DaemonRequest['meta']>['lockPolicy']; | ||
| lockPlatform?: NonNullable<DaemonRequest['meta']>['lockPlatform']; | ||
| platform?: NonNullable<DaemonRequest['flags']>['platform']; | ||
| target?: NonNullable<DaemonRequest['flags']>['target']; | ||
| device?: NonNullable<DaemonRequest['flags']>['device']; | ||
| udid?: NonNullable<DaemonRequest['flags']>['udid']; | ||
| serial?: NonNullable<DaemonRequest['flags']>['serial']; | ||
| activity?: NonNullable<DaemonRequest['flags']>['activity']; | ||
| out?: NonNullable<DaemonRequest['flags']>['out']; | ||
| saveScript?: NonNullable<DaemonRequest['flags']>['saveScript']; | ||
| relaunch?: boolean; | ||
| runtime?: DaemonRequest['runtime']; | ||
| meta?: Omit<NonNullable<DaemonRequest['meta']>, 'uploadedArtifactId' | 'clientArtifactPaths'>; | ||
| }; | ||
| export declare function sendToDaemon(req: Omit<DaemonRequest, 'token'>): Promise<DaemonResponse>; | ||
| export declare function openApp(options?: OpenAppOptions): Promise<DaemonResponse>; | ||
| export declare function computeDaemonCodeSignature(entryPath: string, root?: string): string; | ||
| export declare function downloadRemoteArtifact(params: { | ||
| baseUrl: string; | ||
| token: string; | ||
| artifactId: string; | ||
| destinationPath: string; | ||
| requestId?: string; | ||
| timeoutMs?: number; | ||
| }): Promise<void>; | ||
| export declare function resolveDaemonRequestTimeoutMs(raw?: string | undefined): number; | ||
| export declare function resolveDaemonStartupTimeoutMs(raw?: string | undefined): number; | ||
| export declare function resolveDaemonStartupAttempts(raw?: string | undefined): number; | ||
| export declare function resolveDaemonStartupHint(state: { | ||
| hasInfo: boolean; | ||
| hasLock: boolean; | ||
| }, paths?: Pick<DaemonPaths, 'infoPath' | 'lockPath'>): string; |
| export {}; |
| import type { SessionAction } from './types.ts'; | ||
| export declare function inferFillText(action: SessionAction): string; | ||
| export declare function uniqueStrings(values: string[]): string[]; |
| import { openAndroidApp, snapshotAndroid, getAndroidAppState } from '../platforms/android/index.ts'; | ||
| import { runCmd } from '../utils/exec.ts'; | ||
| import type { SessionState } from './types.ts'; | ||
| export type AndroidBlockingDialogRecoveryResult = 'absent' | 'recovered' | 'failed'; | ||
| export declare function recoverAndroidBlockingSystemDialog(params: { | ||
| session: SessionState; | ||
| snapshotAndroidUi?: typeof snapshotAndroid; | ||
| reopenAndroidApp?: typeof openAndroidApp; | ||
| readAndroidAppState?: typeof getAndroidAppState; | ||
| execCommand?: typeof runCmd; | ||
| }): Promise<AndroidBlockingDialogRecoveryResult>; |
| import fs from 'node:fs'; | ||
| import { type AppLogResult } from './app-log-process.ts'; | ||
| export declare function assertAndroidPackageArgSafe(appBundleId: string): void; | ||
| export declare function startAndroidAppLog(deviceId: string, appBundleId: string, stream: fs.WriteStream, redactionPatterns: RegExp[], pidPath?: string): Promise<AppLogResult>; |
| import fs from 'node:fs'; | ||
| import { type AppLogResult } from './app-log-process.ts'; | ||
| export declare function buildAppleLogPredicate(appBundleId: string): string; | ||
| export declare function buildIosDeviceLogStreamArgs(deviceId: string): string[]; | ||
| export declare function startIosSimulatorAppLog(appBundleId: string, stream: fs.WriteStream, redactionPatterns: RegExp[], pidPath?: string): Promise<AppLogResult>; | ||
| export declare function startMacOsAppLog(appBundleId: string, stream: fs.WriteStream, redactionPatterns: RegExp[], pidPath?: string): Promise<AppLogResult>; | ||
| export declare function startIosDeviceAppLog(deviceId: string, stream: fs.WriteStream, redactionPatterns: RegExp[], pidPath?: string): Promise<AppLogResult>; |
| export declare const APP_LOG_PID_FILENAME = "app-log.pid"; | ||
| export type AppLogResult = { | ||
| backend: 'ios-simulator' | 'ios-device' | 'android' | 'macos'; | ||
| getState: () => 'active' | 'failed'; | ||
| startedAt: number; | ||
| stop: () => Promise<void>; | ||
| wait: Promise<{ | ||
| stdout: string; | ||
| stderr: string; | ||
| exitCode: number; | ||
| }>; | ||
| }; | ||
| export declare function writePidFile(pidPath: string | undefined, pid: number): void; | ||
| export declare function clearPidFile(pidPath: string | undefined): void; | ||
| export declare function cleanupStaleAppLogProcesses(sessionsDir: string): void; |
| import { spawn } from 'node:child_process'; | ||
| import fs from 'node:fs'; | ||
| import type { ExecResult } from '../utils/exec.ts'; | ||
| export declare function waitForChildExit(wait: Promise<ExecResult>, timeoutMs?: number): Promise<void>; | ||
| export declare function sleep(ms: number): Promise<void>; | ||
| export declare function createLineWriter(stream: fs.WriteStream, options: { | ||
| redactionPatterns: RegExp[]; | ||
| includeTokens?: string[]; | ||
| }): { | ||
| onChunk: (chunk: string) => void; | ||
| flush: () => void; | ||
| }; | ||
| export declare function attachChildToStream(child: ReturnType<typeof spawn>, stream: fs.WriteStream, options: { | ||
| endStreamOnClose: boolean; | ||
| writer: { | ||
| onChunk: (chunk: string) => void; | ||
| flush: () => void; | ||
| }; | ||
| }): Promise<ExecResult>; |
| import type { DeviceInfo } from '../utils/device.ts'; | ||
| import type { AppLogResult } from './app-log-process.ts'; | ||
| export type { AppLogResult } from './app-log-process.ts'; | ||
| export { APP_LOG_PID_FILENAME, cleanupStaleAppLogProcesses } from './app-log-process.ts'; | ||
| export { assertAndroidPackageArgSafe } from './app-log-android.ts'; | ||
| export { buildAppleLogPredicate, buildIosDeviceLogStreamArgs } from './app-log-ios.ts'; | ||
| export type AppLogDoctorResult = { | ||
| checks: Record<string, boolean>; | ||
| notes: string[]; | ||
| }; | ||
| export declare function rotateAppLogIfNeeded(outPath: string, config: { | ||
| maxBytes: number; | ||
| maxRotatedFiles: number; | ||
| }): void; | ||
| export declare function getAppLogPathMetadata(outPath: string): { | ||
| exists: boolean; | ||
| sizeBytes: number; | ||
| modifiedAt?: string; | ||
| }; | ||
| export declare function startAppLog(device: DeviceInfo, appBundleId: string, outPath: string, pidPath?: string): Promise<AppLogResult>; | ||
| export declare function stopAppLog(appLog: AppLogResult): Promise<void>; | ||
| export declare function runAppLogDoctor(device: DeviceInfo, appBundleId?: string): Promise<AppLogDoctorResult>; | ||
| export declare function appendAppLogMarker(outPath: string, marker: string): void; | ||
| export declare function clearAppLogFiles(outPath: string): { | ||
| path: string; | ||
| cleared: boolean; | ||
| removedRotatedFiles: number; | ||
| }; |
| export declare function extractTarInstallableArtifact(params: { | ||
| archivePath: string; | ||
| tempDir: string; | ||
| platform: 'ios' | 'android'; | ||
| expectedRootName?: string; | ||
| }): Promise<string>; | ||
| export declare function resolveTarArchiveRootName(params: { | ||
| archivePath: string; | ||
| platform: 'ios' | 'android'; | ||
| expectedRootName?: string; | ||
| }): Promise<string>; | ||
| export declare function readZipEntries(archivePath: string): Promise<string[] | null>; |
| export declare function sanitizeArtifactFilename(raw: string): string; | ||
| export declare function createArtifactTempDir(requestId?: string): string; | ||
| export declare function validateArtifactContentLength(rawLength: string | number | undefined): void; | ||
| export declare function streamReadableToFile(source: NodeJS.ReadableStream, destPath: string): Promise<void>; | ||
| export declare function downloadArtifactToTempDir(params: { | ||
| url: string; | ||
| headers?: Record<string, string>; | ||
| requestId?: string; | ||
| tempDir: string; | ||
| }): Promise<{ | ||
| archivePath: string; | ||
| }>; |
| export type MaterializeArtifactParams = { | ||
| platform: 'ios' | 'android'; | ||
| url: string; | ||
| headers?: Record<string, string>; | ||
| requestId?: string; | ||
| }; | ||
| export type MaterializedArtifact = { | ||
| archivePath: string; | ||
| installablePath: string; | ||
| detected: { | ||
| packageName?: string; | ||
| bundleId?: string; | ||
| appName?: string; | ||
| }; | ||
| }; | ||
| export declare function cleanupMaterializedArtifact(result: MaterializedArtifact): void; | ||
| export declare function materializeArtifact(params: MaterializeArtifactParams): Promise<MaterializedArtifact>; |
| export declare function trackDownloadableArtifact(params: { | ||
| artifactPath: string; | ||
| tenantId?: string; | ||
| fileName?: string; | ||
| deleteAfterDownload?: boolean; | ||
| }): string; | ||
| export declare function prepareDownloadableArtifact(artifactId: string, tenantId?: string): { | ||
| artifactPath: string; | ||
| fileName?: string; | ||
| deleteAfterDownload: boolean; | ||
| }; | ||
| export declare function cleanupDownloadableArtifact(artifactId: string): void; |
| export type DaemonServerMode = 'socket' | 'http' | 'dual'; | ||
| export type DaemonTransportPreference = 'auto' | 'socket' | 'http'; | ||
| export type SessionIsolationMode = 'none' | 'tenant'; | ||
| export type DaemonPaths = { | ||
| baseDir: string; | ||
| infoPath: string; | ||
| lockPath: string; | ||
| logPath: string; | ||
| sessionsDir: string; | ||
| }; | ||
| export declare function resolveDaemonPaths(stateDir: string | undefined): DaemonPaths; | ||
| export declare function resolveStateDir(raw: string | undefined): string; | ||
| export declare function resolveDaemonServerMode(raw: string | undefined): DaemonServerMode; | ||
| export declare function resolveDaemonTransportPreference(raw: string | undefined): DaemonTransportPreference; | ||
| export declare function resolveSessionIsolationMode(raw: string | undefined): SessionIsolationMode; | ||
| export declare function normalizeTenantId(raw: string | undefined): string | undefined; |
| import type { CommandFlags } from '../core/dispatch.ts'; | ||
| import type { SessionSurface } from '../core/session-surface.ts'; | ||
| export type DaemonCommandContext = { | ||
| requestId?: string; | ||
| appBundleId?: string; | ||
| activity?: string; | ||
| verbose?: boolean; | ||
| logPath?: string; | ||
| traceLogPath?: string; | ||
| snapshotInteractiveOnly?: boolean; | ||
| snapshotCompact?: boolean; | ||
| snapshotDepth?: number; | ||
| snapshotScope?: string; | ||
| snapshotRaw?: boolean; | ||
| count?: number; | ||
| intervalMs?: number; | ||
| holdMs?: number; | ||
| jitterPx?: number; | ||
| doubleTap?: boolean; | ||
| clickButton?: 'primary' | 'secondary' | 'middle'; | ||
| pauseMs?: number; | ||
| pattern?: 'one-way' | 'ping-pong'; | ||
| surface?: SessionSurface; | ||
| }; | ||
| export declare function contextFromFlags(logPath: string, flags: CommandFlags | undefined, appBundleId?: string, traceLogPath?: string, requestId?: string): DaemonCommandContext; |
| import type { DeviceInfo } from '../utils/device.ts'; | ||
| export declare function ensureDeviceReady(device: DeviceInfo): Promise<void>; | ||
| export declare function parseIosReadyPayload(payload: unknown): { | ||
| tunnelState?: string; | ||
| }; | ||
| export declare function resolveIosReadyHint(stdout: string, stderr: string): string; |
| import { dispatchCommand } from '../../core/dispatch.ts'; | ||
| import { type FindLocator } from '../../utils/finders.ts'; | ||
| import type { DaemonRequest, DaemonResponse } from '../types.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| export declare function handleFindCommands(params: { | ||
| req: DaemonRequest; | ||
| sessionName: string; | ||
| logPath: string; | ||
| sessionStore: SessionStore; | ||
| invoke: (req: DaemonRequest) => Promise<DaemonResponse>; | ||
| dispatch?: typeof dispatchCommand; | ||
| }): Promise<DaemonResponse | null>; | ||
| type FindAction = { | ||
| kind: 'click'; | ||
| } | { | ||
| kind: 'focus'; | ||
| } | { | ||
| kind: 'fill'; | ||
| value: string; | ||
| } | { | ||
| kind: 'type'; | ||
| value: string; | ||
| } | { | ||
| kind: 'get_text'; | ||
| } | { | ||
| kind: 'get_attrs'; | ||
| } | { | ||
| kind: 'exists'; | ||
| } | { | ||
| kind: 'wait'; | ||
| timeoutMs?: number; | ||
| }; | ||
| export declare function parseFindArgs(args: string[]): { | ||
| locator: FindLocator; | ||
| query: string; | ||
| action: FindAction['kind']; | ||
| value?: string; | ||
| timeoutMs?: number; | ||
| }; | ||
| export {}; |
| import { getRequestSignal } from '../request-cancel.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; | ||
| import type { MaterializeInstallSource } from '../../platforms/install-source.ts'; | ||
| type PreparedIosInstallArtifact = { | ||
| archivePath?: string; | ||
| installablePath: string; | ||
| bundleId?: string; | ||
| appName?: string; | ||
| cleanup: () => Promise<void>; | ||
| }; | ||
| type PreparedAndroidInstallArtifact = { | ||
| archivePath?: string; | ||
| installablePath: string; | ||
| packageName?: string; | ||
| cleanup: () => Promise<void>; | ||
| }; | ||
| declare function resolveInstallDevice(params: { | ||
| session: SessionState | undefined; | ||
| flags: DaemonRequest['flags'] | undefined; | ||
| }): Promise<SessionState['device']>; | ||
| export declare function handleInstallFromSourceCommand(params: { | ||
| req: DaemonRequest; | ||
| sessionName: string; | ||
| sessionStore: SessionStore; | ||
| deps?: { | ||
| resolveInstallDevice?: typeof resolveInstallDevice; | ||
| getRequestSignal?: typeof getRequestSignal; | ||
| prepareIosInstallArtifact?: (source: MaterializeInstallSource, options?: { | ||
| signal?: AbortSignal; | ||
| }) => Promise<PreparedIosInstallArtifact>; | ||
| installIosInstallablePath?: (device: SessionState['device'], installablePath: string) => Promise<void>; | ||
| prepareAndroidInstallArtifact?: (source: MaterializeInstallSource, options?: { | ||
| signal?: AbortSignal; | ||
| resolveIdentity?: boolean; | ||
| }) => Promise<PreparedAndroidInstallArtifact>; | ||
| installAndroidInstallablePathAndResolvePackageName?: (device: SessionState['device'], installablePath: string, packageNameHint?: string) => Promise<string | undefined>; | ||
| inferAndroidAppName?: (packageName: string) => string; | ||
| }; | ||
| }): Promise<DaemonResponse>; | ||
| export declare function handleReleaseMaterializedPathsCommand(params: { | ||
| req: DaemonRequest; | ||
| }): Promise<DaemonResponse>; | ||
| export {}; |
| import { dispatchCommand, type CommandFlags } from '../../core/dispatch.ts'; | ||
| import type { DaemonCommandContext } from '../context.ts'; | ||
| import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| export type ContextFromFlags = (flags: CommandFlags | undefined, appBundleId?: string, traceLogPath?: string) => DaemonCommandContext; | ||
| export type InteractionHandlerParams = { | ||
| req: DaemonRequest; | ||
| sessionName: string; | ||
| sessionStore: SessionStore; | ||
| contextFromFlags: ContextFromFlags; | ||
| dispatch: typeof dispatchCommand; | ||
| }; | ||
| export declare function buildTouchVisualizationResult(params: { | ||
| data: Record<string, unknown> | undefined; | ||
| fallbackX: number; | ||
| fallbackY: number; | ||
| referenceFrame?: { | ||
| referenceWidth: number; | ||
| referenceHeight: number; | ||
| }; | ||
| extra?: Record<string, unknown>; | ||
| }): Record<string, unknown>; | ||
| export declare function dispatchRecordedTouchInteraction(params: { | ||
| session: SessionState; | ||
| sessionStore: SessionStore; | ||
| requestCommand: string; | ||
| requestPositionals: string[]; | ||
| flags: CommandFlags | undefined; | ||
| contextFromFlags: ContextFromFlags; | ||
| dispatch: typeof dispatchCommand; | ||
| interactionCommand: string; | ||
| interactionPositionals: string[]; | ||
| outPath: string | undefined; | ||
| buildPayloads: (data: Record<string, unknown> | undefined) => { | ||
| result: Record<string, unknown>; | ||
| responseData?: Record<string, unknown>; | ||
| } | Promise<{ | ||
| result: Record<string, unknown>; | ||
| responseData?: Record<string, unknown>; | ||
| }>; | ||
| }): Promise<DaemonResponse>; |
| import type { CommandFlags } from '../../core/dispatch.ts'; | ||
| import type { DaemonResponse } from '../types.ts'; | ||
| export declare function refSnapshotFlagGuardResponse(command: 'press' | 'fill' | 'get' | 'scrollintoview', flags: CommandFlags | undefined): DaemonResponse | null; | ||
| export declare function unsupportedRefSnapshotFlags(flags: CommandFlags | undefined): string[]; |
| import type { DaemonResponse } from '../types.ts'; | ||
| import type { InteractionHandlerParams } from './interaction-common.ts'; | ||
| export declare function handleGetCommand(params: InteractionHandlerParams): Promise<DaemonResponse>; |
| import type { DaemonResponse } from '../types.ts'; | ||
| import type { InteractionHandlerParams } from './interaction-common.ts'; | ||
| export declare function handleIsCommand(params: InteractionHandlerParams): Promise<DaemonResponse>; |
| import { dispatchCommand, type CommandFlags } from '../../core/dispatch.ts'; | ||
| import type { SessionState } from '../types.ts'; | ||
| import type { SnapshotNode } from '../../utils/snapshot.ts'; | ||
| import type { ContextFromFlags } from './interaction-common.ts'; | ||
| export declare function readTextForNode(params: { | ||
| device: SessionState['device']; | ||
| node: SnapshotNode; | ||
| flags: CommandFlags | undefined; | ||
| appBundleId?: string; | ||
| traceOutPath?: string; | ||
| surface?: SessionState['surface']; | ||
| contextFromFlags: ContextFromFlags; | ||
| dispatch: typeof dispatchCommand; | ||
| }): Promise<string>; |
| import type { DaemonResponse } from '../types.ts'; | ||
| import type { InteractionHandlerParams } from './interaction-common.ts'; | ||
| export declare function handleScrollIntoViewCommand(params: InteractionHandlerParams): Promise<DaemonResponse | null>; |
| import { parseSelectorChain, resolveSelectorChain } from '../selectors.ts'; | ||
| import type { DaemonResponse, SessionState } from '../types.ts'; | ||
| import type { SessionStore } from '../session-store.ts'; | ||
| import { captureSnapshotForSession } from './interaction-snapshot.ts'; | ||
| import type { ContextFromFlags } from './interaction-common.ts'; | ||
| import { dispatchCommand, type CommandFlags } from '../../core/dispatch.ts'; | ||
| export declare function resolveSelectorTarget(params: { | ||
| command: string; | ||
| selectorExpression: string; | ||
| session: SessionState; | ||
| flags: CommandFlags | undefined; | ||
| sessionStore: SessionStore; | ||
| contextFromFlags: ContextFromFlags; | ||
| interactiveOnly: boolean; | ||
| requireRect: boolean; | ||
| requireUnique: boolean; | ||
| disambiguateAmbiguous: boolean; | ||
| dispatch: typeof dispatchCommand; | ||
| }): Promise<{ | ||
| ok: true; | ||
| chain: ReturnType<typeof parseSelectorChain>; | ||
| snapshot: Awaited<ReturnType<typeof captureSnapshotForSession>>; | ||
| resolved: NonNullable<Awaited<ReturnType<typeof resolveSelectorChain>>>; | ||
| } | { | ||
| ok: false; | ||
| response: DaemonResponse; | ||
| }>; |
| import { dispatchCommand, type CommandFlags } from '../../core/dispatch.ts'; | ||
| import type { SessionStore } from '../session-store.ts'; | ||
| import type { SessionState } from '../types.ts'; | ||
| import type { SnapshotState } from '../../utils/snapshot.ts'; | ||
| import type { ContextFromFlags } from './interaction-common.ts'; | ||
| export declare function captureSnapshotForSession(session: SessionState, flags: CommandFlags | undefined, sessionStore: SessionStore, contextFromFlags: ContextFromFlags, options: { | ||
| interactiveOnly: boolean; | ||
| }, dispatch?: typeof dispatchCommand): Promise<SnapshotState>; |
| import { type Rect, type SnapshotNode } from '../../utils/snapshot.ts'; | ||
| import type { DaemonResponse, SessionState } from '../types.ts'; | ||
| export declare function parseCoordinateTarget(positionals: string[]): { | ||
| x: number; | ||
| y: number; | ||
| } | null; | ||
| export declare function resolveRefTarget(params: { | ||
| session: SessionState; | ||
| refInput: string; | ||
| fallbackLabel: string; | ||
| requireRect: boolean; | ||
| invalidRefMessage: string; | ||
| notFoundMessage: string; | ||
| }): { | ||
| ok: true; | ||
| target: { | ||
| ref: string; | ||
| node: SnapshotNode; | ||
| snapshotNodes: SnapshotNode[]; | ||
| }; | ||
| } | { | ||
| ok: false; | ||
| response: DaemonResponse; | ||
| }; | ||
| export declare function resolveRectCenter(rect: Rect | undefined): { | ||
| x: number; | ||
| y: number; | ||
| } | null; |
| import { dispatchCommand, type CommandFlags } from '../../core/dispatch.ts'; | ||
| import { type SnapshotNode } from '../../utils/snapshot.ts'; | ||
| import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| import { getAndroidScreenSize } from '../../platforms/android/index.ts'; | ||
| import { type ContextFromFlags } from './interaction-common.ts'; | ||
| type CaptureSnapshotForSession = (session: SessionState, flags: CommandFlags | undefined, sessionStore: SessionStore, contextFromFlags: ContextFromFlags, options: { | ||
| interactiveOnly: boolean; | ||
| }, dispatch?: typeof dispatchCommand) => Promise<{ | ||
| nodes: SnapshotNode[]; | ||
| truncated?: boolean; | ||
| createdAt: number; | ||
| backend?: 'xctest' | 'android' | 'macos-helper'; | ||
| }>; | ||
| type ResolveRefTarget = ((params: { | ||
| session: SessionState; | ||
| refInput: string; | ||
| fallbackLabel: string; | ||
| requireRect: boolean; | ||
| invalidRefMessage: string; | ||
| notFoundMessage: string; | ||
| }) => { | ||
| ok: true; | ||
| target: { | ||
| ref: string; | ||
| node: SnapshotNode; | ||
| snapshotNodes: SnapshotNode[]; | ||
| }; | ||
| } | { | ||
| ok: false; | ||
| response: DaemonResponse; | ||
| }) | undefined; | ||
| type RefSnapshotFlagGuardResponse = (command: 'press' | 'fill' | 'get' | 'scrollintoview', flags: CommandFlags | undefined) => DaemonResponse | null; | ||
| export declare function handleTouchInteractionCommands(params: { | ||
| req: DaemonRequest; | ||
| sessionName: string; | ||
| sessionStore: SessionStore; | ||
| contextFromFlags: ContextFromFlags; | ||
| dispatch?: typeof dispatchCommand; | ||
| readAndroidScreenSize?: typeof getAndroidScreenSize; | ||
| captureSnapshotForSession: CaptureSnapshotForSession; | ||
| resolveRefTarget: NonNullable<ResolveRefTarget>; | ||
| refSnapshotFlagGuardResponse: RefSnapshotFlagGuardResponse; | ||
| }): Promise<DaemonResponse | null>; | ||
| export {}; |
| import { dispatchCommand } from '../../core/dispatch.ts'; | ||
| import type { DaemonResponse } from '../types.ts'; | ||
| import { getAndroidScreenSize } from '../../platforms/android/index.ts'; | ||
| import type { InteractionHandlerParams } from './interaction-common.ts'; | ||
| export { unsupportedRefSnapshotFlags } from './interaction-flags.ts'; | ||
| export declare function handleInteractionCommands(params: Omit<InteractionHandlerParams, 'dispatch'> & { | ||
| dispatch?: typeof dispatchCommand; | ||
| readAndroidScreenSize?: typeof getAndroidScreenSize; | ||
| }): Promise<DaemonResponse | null>; |
| import type { DaemonRequest, DaemonResponse } from '../types.ts'; | ||
| import type { LeaseRegistry } from '../lease-registry.ts'; | ||
| type LeaseHandlerArgs = { | ||
| req: DaemonRequest; | ||
| leaseRegistry: LeaseRegistry; | ||
| }; | ||
| export declare function handleLeaseCommands(args: LeaseHandlerArgs): Promise<DaemonResponse | null>; | ||
| export {}; |
| export declare const POLL_INTERVAL_MS = 300; | ||
| export declare const DEFAULT_TIMEOUT_MS = 10000; | ||
| export declare function parseTimeout(value: string | undefined): number | null; |
| import type { DaemonResponse, SessionState } from '../types.ts'; | ||
| import type { RecordTraceDeps } from './record-trace-recording.ts'; | ||
| type AndroidDevice = SessionState['device']; | ||
| type AndroidRecording = Extract<NonNullable<SessionState['recording']>, { | ||
| platform: 'android'; | ||
| }>; | ||
| type AndroidRecordingBase = Pick<AndroidRecording, 'outPath' | 'clientOutPath' | 'telemetryPath' | 'startedAt' | 'showTouches' | 'gestureEvents'>; | ||
| export declare function startAndroidRecording(params: { | ||
| deps: RecordTraceDeps; | ||
| device: AndroidDevice; | ||
| recordingBase: AndroidRecordingBase; | ||
| }): Promise<DaemonResponse | AndroidRecording>; | ||
| export declare function stopAndroidRecording(params: { | ||
| deps: RecordTraceDeps; | ||
| device: AndroidDevice; | ||
| recording: AndroidRecording; | ||
| }): Promise<DaemonResponse | null>; | ||
| export {}; |
| import { SessionStore } from '../session-store.ts'; | ||
| import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; | ||
| import type { RecordTraceDeps, RecordingBase } from './record-trace-recording.ts'; | ||
| export declare function normalizeAppBundleId(session: SessionState): string | undefined; | ||
| export declare function warmIosSimulatorRunner(params: { | ||
| req: DaemonRequest; | ||
| activeSession: SessionState; | ||
| device: SessionState['device']; | ||
| logPath?: string; | ||
| deps: RecordTraceDeps; | ||
| }): Promise<void>; | ||
| export declare function startIosDeviceRecording(params: { | ||
| req: DaemonRequest; | ||
| activeSession: SessionState; | ||
| sessionStore: SessionStore; | ||
| device: SessionState['device']; | ||
| logPath?: string; | ||
| deps: RecordTraceDeps; | ||
| fpsFlag: number | undefined; | ||
| recordingBase: RecordingBase; | ||
| appBundleId: string; | ||
| }): Promise<DaemonResponse | NonNullable<SessionState['recording']>>; | ||
| export declare function startMacOsRecording(params: { | ||
| req: DaemonRequest; | ||
| activeSession: SessionState; | ||
| device: SessionState['device']; | ||
| logPath?: string; | ||
| deps: RecordTraceDeps; | ||
| fpsFlag: number | undefined; | ||
| recordingBase: RecordingBase; | ||
| appBundleId: string; | ||
| }): Promise<DaemonResponse | NonNullable<SessionState['recording']>>; | ||
| export declare function stopIosDeviceRecording(params: { | ||
| req: DaemonRequest; | ||
| activeSession: SessionState; | ||
| device: SessionState['device']; | ||
| logPath?: string; | ||
| deps: RecordTraceDeps; | ||
| recording: Extract<NonNullable<SessionState['recording']>, { | ||
| platform: 'ios-device-runner'; | ||
| }>; | ||
| }): Promise<DaemonResponse | null>; | ||
| export declare function stopMacOsRecording(params: { | ||
| req: DaemonRequest; | ||
| activeSession: SessionState; | ||
| device: SessionState['device']; | ||
| logPath?: string; | ||
| deps: RecordTraceDeps; | ||
| recording: Extract<NonNullable<SessionState['recording']>, { | ||
| platform: 'macos-runner'; | ||
| }>; | ||
| }): Promise<DaemonResponse | null>; |
| import { SessionStore } from '../session-store.ts'; | ||
| import type { DaemonRequest, DaemonResponse, RecordingGestureEvent } from '../types.ts'; | ||
| import { runCmd, runCmdBackground } from '../../utils/exec.ts'; | ||
| import { isPlayableVideo, waitForStableFile } from '../../utils/video.ts'; | ||
| import { writeRecordingTelemetry } from '../recording-telemetry.ts'; | ||
| import { runIosRunnerCommand } from '../../platforms/ios/runner-client.ts'; | ||
| import { overlayRecordingTouches, trimRecordingStart } from '../../recording/overlay.ts'; | ||
| export type RecordTraceDeps = { | ||
| runCmd: typeof runCmd; | ||
| runCmdBackground: typeof runCmdBackground; | ||
| runIosRunnerCommand: typeof runIosRunnerCommand; | ||
| waitForStableFile: typeof waitForStableFile; | ||
| isPlayableVideo: typeof isPlayableVideo; | ||
| writeRecordingTelemetry: typeof writeRecordingTelemetry; | ||
| trimRecordingStart: typeof trimRecordingStart; | ||
| overlayRecordingTouches: typeof overlayRecordingTouches; | ||
| }; | ||
| export type RecordingBase = { | ||
| outPath: string; | ||
| clientOutPath?: string; | ||
| startedAt: number; | ||
| showTouches: boolean; | ||
| gestureEvents: RecordingGestureEvent[]; | ||
| }; | ||
| export declare function buildRecordTraceDeps(overrides?: Partial<RecordTraceDeps>): RecordTraceDeps; | ||
| export declare function handleRecordCommand(params: { | ||
| req: DaemonRequest; | ||
| sessionName: string; | ||
| sessionStore: SessionStore; | ||
| logPath?: string; | ||
| deps?: Partial<RecordTraceDeps>; | ||
| }): Promise<DaemonResponse>; |
| import type { DaemonRequest, DaemonResponse } from '../types.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| import { type RecordTraceDeps } from './record-trace-recording.ts'; | ||
| export declare function handleRecordTraceCommands(params: { | ||
| req: DaemonRequest; | ||
| sessionName: string; | ||
| sessionStore: SessionStore; | ||
| logPath?: string; | ||
| deps?: Partial<RecordTraceDeps>; | ||
| }): Promise<DaemonResponse | null>; |
| import type { DaemonRequest, DaemonResponse } from '../types.ts'; | ||
| export declare function runBatchCommands(req: DaemonRequest, sessionName: string, invoke: (req: DaemonRequest) => Promise<DaemonResponse>): Promise<DaemonResponse>; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| import { stopIosRunnerSession } from '../../platforms/ios/runner-client.ts'; | ||
| import { shutdownSimulator } from '../../platforms/ios/simulator.ts'; | ||
| import { clearRuntimeHintsFromApp } from '../runtime-hints.ts'; | ||
| import { settleIosSimulator } from './session-device-utils.ts'; | ||
| type AppLogStream = NonNullable<SessionState['appLog']>; | ||
| declare function shutdownAndroidEmulator(device: DeviceInfo): Promise<{ | ||
| success: boolean; | ||
| exitCode: number; | ||
| stdout: string; | ||
| stderr: string; | ||
| }>; | ||
| export type ShutdownAndroidEmulatorFn = typeof shutdownAndroidEmulator; | ||
| export declare function handleCloseCommand(params: { | ||
| req: DaemonRequest; | ||
| sessionName: string; | ||
| logPath: string; | ||
| sessionStore: SessionStore; | ||
| dispatch: (device: DeviceInfo, command: string, positionals: string[], out?: string, context?: Record<string, unknown>) => Promise<Record<string, unknown> | void>; | ||
| stopIosRunner?: typeof stopIosRunnerSession; | ||
| clearRuntimeHints?: typeof clearRuntimeHintsFromApp; | ||
| settleSimulator?: typeof settleIosSimulator; | ||
| shutdownSimulator?: typeof shutdownSimulator; | ||
| shutdownAndroidEmulator?: ShutdownAndroidEmulatorFn; | ||
| appLogOps: { | ||
| stop: (stream: AppLogStream) => Promise<void>; | ||
| }; | ||
| }): Promise<DaemonResponse>; | ||
| export {}; |
| import { resolveTargetDevice } from '../../core/dispatch.ts'; | ||
| import { ensureDeviceReady } from '../device-ready.ts'; | ||
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| import type { DaemonRequest, DaemonResponse } from '../types.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| export type ReinstallOps = { | ||
| ios: (device: DeviceInfo, app: string, appPath: string) => Promise<{ | ||
| bundleId: string; | ||
| }>; | ||
| android: (device: DeviceInfo, app: string, appPath: string) => Promise<{ | ||
| package: string; | ||
| }>; | ||
| }; | ||
| export type AppDeployOps = { | ||
| ios: (device: DeviceInfo, app: string, appPath: string) => Promise<{ | ||
| bundleId?: string; | ||
| appName?: string; | ||
| launchTarget?: string; | ||
| }>; | ||
| android: (device: DeviceInfo, app: string, appPath: string) => Promise<{ | ||
| package?: string; | ||
| appName?: string; | ||
| launchTarget?: string; | ||
| }>; | ||
| }; | ||
| export type InstallOps = AppDeployOps; | ||
| export declare const defaultReinstallOps: ReinstallOps; | ||
| export declare const defaultInstallOps: InstallOps; | ||
| export declare function handleAppDeployCommand(params: { | ||
| req: DaemonRequest; | ||
| command: 'install' | 'reinstall'; | ||
| sessionName: string; | ||
| sessionStore: SessionStore; | ||
| ensureReady: typeof ensureDeviceReady; | ||
| resolveDevice: typeof resolveTargetDevice; | ||
| deployOps: AppDeployOps; | ||
| }): Promise<DaemonResponse>; |
| import { type DeviceInfo } from '../../utils/device.ts'; | ||
| import { ensureDeviceReady } from '../device-ready.ts'; | ||
| import { resolveTargetDevice } from '../../core/dispatch.ts'; | ||
| import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; | ||
| export declare const IOS_SIMULATOR_POST_CLOSE_SETTLE_MS: number; | ||
| export declare const IOS_SIMULATOR_POST_OPEN_SETTLE_MS: number; | ||
| export declare function requireSessionOrExplicitSelector(command: string, session: SessionState | undefined, flags: DaemonRequest['flags'] | undefined): DaemonResponse | null; | ||
| export declare function hasExplicitDeviceSelector(flags: DaemonRequest['flags'] | undefined): boolean; | ||
| export declare function hasExplicitSessionFlag(flags: DaemonRequest['flags'] | undefined): boolean; | ||
| export declare function isIosSimulator(device: DeviceInfo): boolean; | ||
| export declare function isAndroidEmulator(device: DeviceInfo): boolean; | ||
| export declare function settleIosSimulator(device: DeviceInfo, delayMs: number): Promise<void>; | ||
| export declare function resolveCommandDevice(params: { | ||
| session: SessionState | undefined; | ||
| flags: DaemonRequest['flags'] | undefined; | ||
| ensureReadyFn: typeof ensureDeviceReady; | ||
| resolveTargetDeviceFn: typeof resolveTargetDevice; | ||
| ensureReady?: boolean; | ||
| }): Promise<DeviceInfo>; | ||
| export declare function refreshSessionDeviceIfNeeded(device: DeviceInfo, resolveTargetDeviceFn: typeof resolveTargetDevice): Promise<DeviceInfo>; | ||
| export declare function resolveAndroidEmulatorAvdName(params: { | ||
| flags: DaemonRequest['flags'] | undefined; | ||
| sessionDevice?: DeviceInfo; | ||
| resolvedDevice?: DeviceInfo; | ||
| }): string | undefined; | ||
| export declare function selectorTargetsSessionDevice(flags: DaemonRequest['flags'] | undefined, session: SessionState | undefined): boolean; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| export declare function resolveAndroidPackageForOpen(device: DeviceInfo, openTarget: string | undefined): Promise<string | undefined>; | ||
| export declare function resolveSessionAppBundleIdForTarget(device: DeviceInfo, openTarget: string | undefined, currentAppBundleId: string | undefined, resolveAndroidPackageForOpenFn: (device: DeviceInfo, openTarget: string | undefined) => Promise<string | undefined>): Promise<string | undefined>; |
| import { dispatchCommand, resolveTargetDevice } from '../../core/dispatch.ts'; | ||
| import { ensureDeviceReady } from '../device-ready.ts'; | ||
| import { stopIosRunnerSession } from '../../platforms/ios/runner-client.ts'; | ||
| import { applyRuntimeHintsToApp, clearRuntimeHintsFromApp } from '../runtime-hints.ts'; | ||
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| import type { DaemonRequest, DaemonResponse } from '../types.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| import { settleIosSimulator } from './session-device-utils.ts'; | ||
| export declare function handleOpenCommand(params: { | ||
| req: DaemonRequest; | ||
| sessionName: string; | ||
| logPath: string; | ||
| sessionStore: SessionStore; | ||
| dispatch: typeof dispatchCommand; | ||
| ensureReady: typeof ensureDeviceReady; | ||
| resolveDevice: typeof resolveTargetDevice; | ||
| applyRuntimeHints?: typeof applyRuntimeHintsToApp; | ||
| clearRuntimeHints?: typeof clearRuntimeHintsFromApp; | ||
| stopIosRunner?: typeof stopIosRunnerSession; | ||
| settleSimulator?: typeof settleIosSimulator; | ||
| resolveAndroidPackageForOpen?: (device: DeviceInfo, openTarget: string | undefined) => Promise<string | undefined>; | ||
| }): Promise<DaemonResponse>; |
| import type { SessionState } from '../types.ts'; | ||
| export declare function buildPerfResponseData(session: SessionState): Record<string, unknown>; |
| import type { SessionAction, SessionState } from '../types.ts'; | ||
| import type { SnapshotState } from '../../utils/snapshot.ts'; | ||
| export declare function parseSelectorWaitPositionals(positionals: string[]): { | ||
| selectorExpression: string | null; | ||
| selectorTimeout: string | null; | ||
| }; | ||
| export declare function collectReplaySelectorCandidates(action: SessionAction): string[]; | ||
| export declare function healNumericGetTextDrift(action: SessionAction, snapshot: SnapshotState, session: SessionState): SessionAction | null; |
| import type { SessionAction, SessionState } from '../types.ts'; | ||
| export declare function parseReplayScript(script: string): SessionAction[]; | ||
| export declare function writeReplayScript(filePath: string, actions: SessionAction[], session?: SessionState): void; |
| import type { DaemonRequest, DaemonResponse } from '../types.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| import { clearRuntimeHintsFromApp } from '../runtime-hints.ts'; | ||
| export declare function handleRuntimeCommand(params: { | ||
| req: DaemonRequest; | ||
| sessionName: string; | ||
| sessionStore: SessionStore; | ||
| clearRuntimeHints?: typeof clearRuntimeHintsFromApp; | ||
| }): Promise<DaemonResponse>; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| import type { CommandFlags } from '../../core/dispatch.ts'; | ||
| import type { DaemonRequest, DaemonResponse, SessionRuntimeHints, SessionState } from '../types.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| import { type clearRuntimeHintsFromApp } from '../runtime-hints.ts'; | ||
| type RuntimePlatform = NonNullable<SessionRuntimeHints['platform']>; | ||
| export declare function countConfiguredRuntimeHints(runtime: SessionRuntimeHints | undefined): number; | ||
| export declare function toRuntimePlatform(platform: CommandFlags['platform'] | DeviceInfo['platform'] | 'apple' | undefined): RuntimePlatform | undefined; | ||
| export declare function buildRuntimeHints(flags: CommandFlags | undefined, platform?: RuntimePlatform): SessionRuntimeHints; | ||
| export declare function mergeRuntimeHints(current: SessionRuntimeHints | undefined, next: SessionRuntimeHints): SessionRuntimeHints; | ||
| export declare function setSessionRuntimeHintsForOpen(sessionStore: SessionStore, sessionName: string, runtime: SessionRuntimeHints | undefined): SessionRuntimeHints | undefined; | ||
| declare function resolveOpenRuntimeHints(params: { | ||
| req: DaemonRequest; | ||
| sessionStore: SessionStore; | ||
| sessionName: string; | ||
| device: DeviceInfo; | ||
| }): { | ||
| runtime: SessionRuntimeHints | undefined; | ||
| previousRuntime: SessionRuntimeHints | undefined; | ||
| replacedStoredRuntime: boolean; | ||
| }; | ||
| export declare function tryResolveOpenRuntimeHints(params: Parameters<typeof resolveOpenRuntimeHints>[0]): { | ||
| ok: true; | ||
| data: ReturnType<typeof resolveOpenRuntimeHints>; | ||
| } | { | ||
| ok: false; | ||
| response: DaemonResponse; | ||
| }; | ||
| export declare function maybeClearRemovedRuntimeTransportHints(params: { | ||
| replacedStoredRuntime: boolean; | ||
| previousRuntime: SessionRuntimeHints | undefined; | ||
| runtime: SessionRuntimeHints | undefined; | ||
| session: SessionState | undefined; | ||
| clearRuntimeHints: typeof clearRuntimeHintsFromApp; | ||
| }): Promise<void>; | ||
| export {}; |
| export declare const STARTUP_SAMPLE_METHOD = "open-command-roundtrip"; | ||
| export declare const STARTUP_SAMPLE_DESCRIPTION = "Elapsed wall-clock time around dispatching the open command for the active session app target."; | ||
| export declare const PERF_STARTUP_SAMPLE_LIMIT = 20; | ||
| export declare const PERF_UNAVAILABLE_REASON = "Not implemented for this platform in this release."; | ||
| export type StartupPerfSample = { | ||
| durationMs: number; | ||
| measuredAt: string; | ||
| method: typeof STARTUP_SAMPLE_METHOD; | ||
| appTarget?: string; | ||
| appBundleId?: string; | ||
| }; |
| import { dispatchCommand, resolveTargetDevice } from '../../core/dispatch.ts'; | ||
| import { type DeviceInfo } from '../../utils/device.ts'; | ||
| import type { DaemonRequest, DaemonResponse } from '../types.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| import { ensureDeviceReady } from '../device-ready.ts'; | ||
| import { stopIosRunnerSession } from '../../platforms/ios/runner-client.ts'; | ||
| import { shutdownSimulator } from '../../platforms/ios/simulator.ts'; | ||
| import { startAppLog, stopAppLog } from '../app-log.ts'; | ||
| import { applyRuntimeHintsToApp, clearRuntimeHintsFromApp } from '../runtime-hints.ts'; | ||
| import { settleIosSimulator } from './session-device-utils.ts'; | ||
| import { type ShutdownAndroidEmulatorFn } from './session-close.ts'; | ||
| import { type InstallOps, type ReinstallOps } from './session-deploy.ts'; | ||
| type EnsureAndroidEmulatorBoot = (params: { | ||
| avdName: string; | ||
| serial?: string; | ||
| headless?: boolean; | ||
| }) => Promise<DeviceInfo>; | ||
| type ListAndroidDevices = typeof import('../../platforms/android/devices.ts').listAndroidDevices; | ||
| type ListAppleDevices = typeof import('../../platforms/ios/devices.ts').listAppleDevices; | ||
| export declare function handleSessionCommands(params: { | ||
| req: DaemonRequest; | ||
| sessionName: string; | ||
| logPath: string; | ||
| sessionStore: SessionStore; | ||
| invoke: (req: DaemonRequest) => Promise<DaemonResponse>; | ||
| dispatch?: typeof dispatchCommand; | ||
| ensureReady?: typeof ensureDeviceReady; | ||
| resolveTargetDevice?: typeof resolveTargetDevice; | ||
| installOps?: InstallOps; | ||
| reinstallOps?: ReinstallOps; | ||
| stopIosRunner?: typeof stopIosRunnerSession; | ||
| appLogOps?: { | ||
| start: typeof startAppLog; | ||
| stop: typeof stopAppLog; | ||
| }; | ||
| ensureAndroidEmulatorBoot?: EnsureAndroidEmulatorBoot; | ||
| resolveAndroidPackageForOpen?: (device: DeviceInfo, openTarget: string | undefined) => Promise<string | undefined>; | ||
| applyRuntimeHints?: typeof applyRuntimeHintsToApp; | ||
| clearRuntimeHints?: typeof clearRuntimeHintsFromApp; | ||
| settleSimulator?: typeof settleIosSimulator; | ||
| shutdownSimulator?: typeof shutdownSimulator; | ||
| shutdownAndroidEmulator?: ShutdownAndroidEmulatorFn; | ||
| listAndroidDevices?: ListAndroidDevices; | ||
| listAppleDevices?: ListAppleDevices; | ||
| listAppleApps?: (device: DeviceInfo, filter: 'user-installed' | 'all') => Promise<Array<{ | ||
| bundleId: string; | ||
| name?: string; | ||
| }>>; | ||
| }): Promise<DaemonResponse | null>; | ||
| export {}; |
| import { runIosRunnerCommand } from '../../platforms/ios/runner-client.ts'; | ||
| import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| type HandleAlertCommandParams = { | ||
| req: DaemonRequest; | ||
| logPath: string; | ||
| sessionStore: SessionStore; | ||
| session: SessionState | undefined; | ||
| device: SessionState['device']; | ||
| runnerCommand?: typeof runIosRunnerCommand; | ||
| }; | ||
| export declare function handleAlertCommand(params: HandleAlertCommandParams): Promise<DaemonResponse>; | ||
| export {}; |
| import { dispatchCommand, type CommandFlags } from '../../core/dispatch.ts'; | ||
| import { type RawSnapshotNode, type SnapshotState } from '../../utils/snapshot.ts'; | ||
| import type { DaemonResponse, SessionState } from '../types.ts'; | ||
| type CaptureSnapshotParams = { | ||
| dispatchSnapshotCommand: typeof dispatchCommand; | ||
| device: SessionState['device']; | ||
| session: SessionState | undefined; | ||
| flags: CommandFlags | undefined; | ||
| outPath?: string; | ||
| logPath: string; | ||
| snapshotScope?: string; | ||
| }; | ||
| type SnapshotData = { | ||
| nodes?: RawSnapshotNode[]; | ||
| truncated?: boolean; | ||
| backend?: 'xctest' | 'android' | 'macos-helper'; | ||
| }; | ||
| export declare function captureSnapshot(params: CaptureSnapshotParams): Promise<{ | ||
| snapshot: SnapshotState; | ||
| }>; | ||
| export declare function captureSnapshotData(params: CaptureSnapshotParams): Promise<SnapshotData>; | ||
| export declare function buildSnapshotState(data: { | ||
| nodes?: RawSnapshotNode[]; | ||
| truncated?: boolean; | ||
| backend?: 'xctest' | 'android' | 'macos-helper'; | ||
| }, snapshotRaw: boolean | undefined): SnapshotState; | ||
| export declare function resolveSnapshotScope(snapshotScope: string | undefined, session: SessionState | undefined): { | ||
| ok: true; | ||
| scope?: string; | ||
| } | { | ||
| ok: false; | ||
| response: DaemonResponse; | ||
| }; | ||
| export {}; |
| import type { DaemonRequest, SessionState } from '../types.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| export declare function resolveSessionDevice(sessionStore: SessionStore, sessionName: string, flags: DaemonRequest['flags']): Promise<{ | ||
| session: SessionState | undefined; | ||
| device: import("../../utils/device.ts").DeviceInfo; | ||
| }>; | ||
| export declare function withSessionlessRunnerCleanup<T>(session: SessionState | undefined, device: SessionState['device'], task: () => Promise<T>): Promise<T>; | ||
| export declare function recordIfSession(sessionStore: SessionStore, session: SessionState | undefined, req: DaemonRequest, result: Record<string, unknown>): void; | ||
| export declare function buildSnapshotSession(params: { | ||
| session: SessionState | undefined; | ||
| sessionName: string; | ||
| device: SessionState['device']; | ||
| snapshot: SessionState['snapshot']; | ||
| appBundleId?: string; | ||
| }): SessionState; |
| import { SessionStore } from '../session-store.ts'; | ||
| import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; | ||
| type ParsedSettingsArgs = { | ||
| setting: string; | ||
| state: string; | ||
| permissionTarget?: string; | ||
| }; | ||
| type HandleSettingsCommandParams = { | ||
| req: DaemonRequest; | ||
| logPath: string; | ||
| sessionStore: SessionStore; | ||
| session: SessionState | undefined; | ||
| device: SessionState['device']; | ||
| parsed: ParsedSettingsArgs; | ||
| }; | ||
| export declare function parseSettingsArgs(req: DaemonRequest): { | ||
| ok: true; | ||
| parsed: ParsedSettingsArgs; | ||
| } | { | ||
| ok: false; | ||
| response: DaemonResponse; | ||
| }; | ||
| export declare function handleSettingsCommand(params: HandleSettingsCommandParams): Promise<DaemonResponse>; | ||
| export {}; |
| import { dispatchCommand } from '../../core/dispatch.ts'; | ||
| import { runIosRunnerCommand } from '../../platforms/ios/runner-client.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| import { type SelectorChain } from '../selectors.ts'; | ||
| import type { DaemonRequest, DaemonResponse, SessionState } from '../types.ts'; | ||
| export type WaitParsed = { | ||
| kind: 'sleep'; | ||
| durationMs: number; | ||
| } | { | ||
| kind: 'ref'; | ||
| rawRef: string; | ||
| timeoutMs: number | null; | ||
| } | { | ||
| kind: 'selector'; | ||
| selector: SelectorChain; | ||
| selectorExpression: string; | ||
| timeoutMs: number | null; | ||
| } | { | ||
| kind: 'text'; | ||
| text: string; | ||
| timeoutMs: number | null; | ||
| }; | ||
| export declare function parseWaitArgs(args: string[]): WaitParsed | null; | ||
| type HandleWaitCommandParams = { | ||
| parsed: WaitParsed; | ||
| req: DaemonRequest; | ||
| sessionName: string; | ||
| logPath: string; | ||
| sessionStore: SessionStore; | ||
| session: SessionState | undefined; | ||
| device: SessionState['device']; | ||
| dispatchSnapshotCommand?: typeof dispatchCommand; | ||
| runnerCommand?: typeof runIosRunnerCommand; | ||
| }; | ||
| export declare function waitNeedsRunnerCleanup(parsed: WaitParsed): boolean; | ||
| export declare function handleWaitCommand(params: HandleWaitCommandParams): Promise<DaemonResponse>; | ||
| export {}; |
| import { dispatchCommand } from '../../core/dispatch.ts'; | ||
| import { runIosRunnerCommand } from '../../platforms/ios/runner-client.ts'; | ||
| import type { DaemonRequest, DaemonResponse } from '../types.ts'; | ||
| import { SessionStore } from '../session-store.ts'; | ||
| import { withSessionlessRunnerCleanup } from './snapshot-session.ts'; | ||
| import { parseWaitArgs } from './snapshot-wait.ts'; | ||
| export { parseWaitArgs }; | ||
| export declare function handleSnapshotCommands(params: { | ||
| req: DaemonRequest; | ||
| sessionName: string; | ||
| logPath: string; | ||
| sessionStore: SessionStore; | ||
| dispatchSnapshotCommand?: typeof dispatchCommand; | ||
| runnerCommand?: typeof runIosRunnerCommand; | ||
| sessionlessRunnerCleanup?: typeof withSessionlessRunnerCleanup; | ||
| }): Promise<DaemonResponse | null>; |
| import http, { type IncomingHttpHeaders } from 'node:http'; | ||
| import type { DaemonRequest, DaemonResponse } from './types.ts'; | ||
| type JsonRpcRequest = { | ||
| jsonrpc?: string; | ||
| id?: string | number | null; | ||
| method?: string; | ||
| params?: unknown; | ||
| }; | ||
| export type HttpAuthHookContext = { | ||
| headers: IncomingHttpHeaders; | ||
| rpcRequest: JsonRpcRequest; | ||
| daemonRequest: DaemonRequest; | ||
| }; | ||
| export type HttpAuthHookResult = boolean | void | { | ||
| ok?: boolean; | ||
| tenantId?: string; | ||
| code?: string; | ||
| message?: string; | ||
| details?: Record<string, unknown>; | ||
| }; | ||
| export type HttpAuthHook = (context: HttpAuthHookContext) => Promise<HttpAuthHookResult> | HttpAuthHookResult; | ||
| export declare function createDaemonHttpServer(options: { | ||
| handleRequest: (req: DaemonRequest) => Promise<DaemonResponse>; | ||
| token?: string; | ||
| }): Promise<http.Server>; | ||
| export {}; |
| import type { DaemonInstallSource, DaemonRequest } from './types.ts'; | ||
| export declare function resolveInstallSource(req: DaemonRequest): { | ||
| source: DaemonInstallSource; | ||
| cleanup: () => void; | ||
| }; |
| import type { Platform } from '../utils/device.ts'; | ||
| import type { SnapshotState } from '../utils/snapshot.ts'; | ||
| type IsPredicate = 'visible' | 'hidden' | 'exists' | 'editable' | 'selected' | 'text'; | ||
| export declare function isSupportedPredicate(input: string): input is IsPredicate; | ||
| export declare function evaluateIsPredicate(params: { | ||
| predicate: Exclude<IsPredicate, 'exists'>; | ||
| node: SnapshotState['nodes'][number]; | ||
| expectedText?: string; | ||
| platform: Platform; | ||
| }): { | ||
| pass: boolean; | ||
| actualText: string; | ||
| details: string; | ||
| }; | ||
| export {}; |
| import type { DaemonRequest } from './types.ts'; | ||
| export type LeaseScope = { | ||
| tenantId?: string; | ||
| runId?: string; | ||
| leaseId?: string; | ||
| leaseTtlMs?: number; | ||
| leaseBackend?: 'ios-simulator'; | ||
| }; | ||
| export declare function resolveLeaseScope(req: Pick<DaemonRequest, 'flags' | 'meta'>): LeaseScope; |
| export type LeaseBackend = 'ios-simulator'; | ||
| export type SimulatorLease = { | ||
| leaseId: string; | ||
| tenantId: string; | ||
| runId: string; | ||
| backend: LeaseBackend; | ||
| createdAt: number; | ||
| heartbeatAt: number; | ||
| expiresAt: number; | ||
| }; | ||
| export type LeaseRegistryOptions = { | ||
| maxActiveSimulatorLeases?: number; | ||
| defaultLeaseTtlMs?: number; | ||
| minLeaseTtlMs?: number; | ||
| maxLeaseTtlMs?: number; | ||
| now?: () => number; | ||
| }; | ||
| export type AllocateLeaseRequest = { | ||
| tenantId: string; | ||
| runId: string; | ||
| backend?: LeaseBackend; | ||
| ttlMs?: number; | ||
| }; | ||
| export type HeartbeatLeaseRequest = { | ||
| leaseId: string; | ||
| tenantId?: string; | ||
| runId?: string; | ||
| ttlMs?: number; | ||
| }; | ||
| export type ReleaseLeaseRequest = { | ||
| leaseId: string; | ||
| tenantId?: string; | ||
| runId?: string; | ||
| }; | ||
| export type AdmissionRequest = { | ||
| tenantId: string | undefined; | ||
| runId: string | undefined; | ||
| leaseId: string | undefined; | ||
| backend?: LeaseBackend; | ||
| }; | ||
| export declare class LeaseRegistry { | ||
| private readonly leases; | ||
| private readonly runBindings; | ||
| private readonly maxActiveSimulatorLeases; | ||
| private readonly defaultLeaseTtlMs; | ||
| private readonly minLeaseTtlMs; | ||
| private readonly maxLeaseTtlMs; | ||
| private readonly now; | ||
| constructor(options?: LeaseRegistryOptions); | ||
| allocateLease(request: AllocateLeaseRequest): SimulatorLease; | ||
| heartbeatLease(request: HeartbeatLeaseRequest): SimulatorLease; | ||
| releaseLease(request: ReleaseLeaseRequest): { | ||
| released: boolean; | ||
| }; | ||
| assertLeaseAdmission(request: AdmissionRequest): void; | ||
| listActiveLeases(): SimulatorLease[]; | ||
| private cleanupExpiredLeases; | ||
| private enforceCapacity; | ||
| private resolveLeaseTtlMs; | ||
| private refreshLease; | ||
| private bindingKey; | ||
| private assertOptionalScopeMatch; | ||
| } |
| export type RetainedMaterializedPaths = { | ||
| materializationId: string; | ||
| installablePath: string; | ||
| archivePath?: string; | ||
| expiresAt: string; | ||
| }; | ||
| export declare function retainMaterializedPaths(params: { | ||
| installablePath: string; | ||
| archivePath?: string; | ||
| tenantId?: string; | ||
| sessionName?: string; | ||
| ttlMs?: number; | ||
| }): Promise<RetainedMaterializedPaths>; | ||
| export declare function cleanupRetainedMaterializedPaths(materializationId: string, tenantId?: string): Promise<void>; | ||
| export declare function cleanupRetainedMaterializedPathsForSession(sessionName: string): Promise<void>; |
| type NetworkIncludeMode = 'summary' | 'headers' | 'body' | 'all'; | ||
| export type NetworkEntry = { | ||
| method?: string; | ||
| url: string; | ||
| status?: number; | ||
| timestamp?: string; | ||
| headers?: string; | ||
| requestBody?: string; | ||
| responseBody?: string; | ||
| raw: string; | ||
| line: number; | ||
| }; | ||
| export type NetworkDump = { | ||
| path: string; | ||
| exists: boolean; | ||
| scannedLines: number; | ||
| matchedLines: number; | ||
| entries: NetworkEntry[]; | ||
| include: NetworkIncludeMode; | ||
| limits: { | ||
| maxEntries: number; | ||
| maxPayloadChars: number; | ||
| maxScanLines: number; | ||
| }; | ||
| }; | ||
| export declare function readRecentNetworkTraffic(logPath: string, options?: { | ||
| maxEntries?: number; | ||
| include?: NetworkIncludeMode; | ||
| maxPayloadChars?: number; | ||
| maxScanLines?: number; | ||
| }): NetworkDump; | ||
| export {}; |
| export declare function formatRecordTraceError(error: unknown): string; | ||
| export declare function formatRecordTraceExecFailure(result: { | ||
| stdout: string; | ||
| stderr: string; | ||
| exitCode: number; | ||
| }, command: string): string; |
| import type { SessionState } from './types.ts'; | ||
| export declare function recordTouchVisualizationEvent(session: SessionState, command: string, positionals: string[], result: Record<string, unknown> | void, fallback?: Record<string, unknown>, startedAtMs?: number, finishedAtMs?: number): void; | ||
| export declare function augmentScrollVisualizationResult(session: SessionState, command: string, positionals: string[], result: Record<string, unknown> | void): Record<string, unknown> | void; |
| import type { RecordingGestureEvent } from './types.ts'; | ||
| type RecordingTelemetryState = { | ||
| outPath: string; | ||
| gestureEvents: RecordingGestureEvent[]; | ||
| telemetryPath?: string; | ||
| }; | ||
| export declare function deriveRecordingTelemetryPath(videoPath: string): string; | ||
| export declare function trimRecordingTelemetryEvents(events: RecordingGestureEvent[], trimStartMs: number): RecordingGestureEvent[]; | ||
| export declare function normalizeRecordingTelemetryEvents(events: RecordingGestureEvent[]): RecordingGestureEvent[]; | ||
| export declare function writeRecordingTelemetry(params: { | ||
| videoPath: string; | ||
| events: RecordingGestureEvent[]; | ||
| trimStartMs?: number; | ||
| }): string; | ||
| export declare function persistRecordingTelemetry(params: { | ||
| recording: RecordingTelemetryState; | ||
| trimStartMs?: number; | ||
| writeTelemetry?: typeof writeRecordingTelemetry; | ||
| }): string; | ||
| export {}; |
| type GestureTimingSource = { | ||
| recordingStartedAt: number; | ||
| gestureClockOriginAtMs?: number; | ||
| gestureClockOriginUptimeMs?: number; | ||
| runnerStartedAtUptimeMs?: number; | ||
| gestureStartUptimeMs?: number; | ||
| gestureEndUptimeMs?: number; | ||
| fallbackStartedAtMs: number; | ||
| fallbackFinishedAtMs: number; | ||
| }; | ||
| type GestureDurationSource = { | ||
| gestureStartUptimeMs?: number; | ||
| gestureEndUptimeMs?: number; | ||
| reportedDurationMs?: number; | ||
| fallbackStartedAtMs: number; | ||
| fallbackFinishedAtMs: number; | ||
| }; | ||
| type TapVisualizationOffsetSource = GestureTimingSource & { | ||
| gestureDurationMs: number; | ||
| }; | ||
| export declare function resolveGestureOffsetMs(source: GestureTimingSource): number; | ||
| export declare function resolveGestureDurationMs(source: GestureDurationSource): number; | ||
| export declare function resolveTapVisualizationOffsetMs(source: TapVisualizationOffsetSource): number; | ||
| export {}; |
| import { AppError } from '../utils/errors.ts'; | ||
| export declare function resolveRequestTrackingId(requestId: string | undefined, fallbackSeed?: unknown): string; | ||
| export declare function registerRequestAbort(requestId: string | undefined): void; | ||
| export declare function markRequestCanceled(requestId: string | undefined): void; | ||
| export declare function clearRequestCanceled(requestId: string | undefined): void; | ||
| export declare function isRequestCanceled(requestId: string | undefined): boolean; | ||
| export declare function getRequestSignal(requestId: string | undefined): AbortSignal | undefined; | ||
| export declare function createRequestCanceledError(): AppError; | ||
| export declare function isRequestCanceledError(error: unknown): boolean; |
| import type { SessionState, DaemonRequest } from './types.ts'; | ||
| export declare function applyRequestLockPolicy(req: DaemonRequest, existingSession?: SessionState): DaemonRequest; |
| import { dispatchCommand } from '../core/dispatch.ts'; | ||
| import type { DaemonRequest, DaemonResponse } from './types.ts'; | ||
| import { SessionStore } from './session-store.ts'; | ||
| import type { LeaseRegistry } from './lease-registry.ts'; | ||
| import { snapshotAndroid, openAndroidApp, getAndroidAppState } from '../platforms/android/index.ts'; | ||
| import { runCmd } from '../utils/exec.ts'; | ||
| export type RequestRouterDeps = { | ||
| logPath: string; | ||
| token: string; | ||
| sessionStore: SessionStore; | ||
| leaseRegistry: LeaseRegistry; | ||
| trackDownloadableArtifact: (opts: { | ||
| artifactPath: string; | ||
| tenantId?: string; | ||
| fileName?: string; | ||
| }) => string; | ||
| dispatchCommand?: typeof dispatchCommand; | ||
| snapshotAndroidUi?: typeof snapshotAndroid; | ||
| reopenAndroidApp?: typeof openAndroidApp; | ||
| readAndroidAppState?: typeof getAndroidAppState; | ||
| execCommand?: typeof runCmd; | ||
| }; | ||
| export declare function createRequestHandler(deps: RequestRouterDeps): (req: DaemonRequest) => Promise<DaemonResponse>; |
| import type { DeviceInfo } from '../utils/device.ts'; | ||
| import type { SessionRuntimeHints } from './types.ts'; | ||
| type ResolvedRuntimeTransport = { | ||
| host: string; | ||
| port: number; | ||
| scheme: 'http' | 'https'; | ||
| }; | ||
| export declare function hasRuntimeTransportHints(runtime: SessionRuntimeHints | undefined): boolean; | ||
| export declare function resolveRuntimeTransportHints(runtime: SessionRuntimeHints | undefined): ResolvedRuntimeTransport | undefined; | ||
| export declare function applyRuntimeHintsToApp(params: { | ||
| device: DeviceInfo; | ||
| appId?: string; | ||
| runtime: SessionRuntimeHints | undefined; | ||
| }): Promise<void>; | ||
| export declare function clearRuntimeHintsFromApp(params: { | ||
| device: DeviceInfo; | ||
| appId?: string; | ||
| }): Promise<void>; | ||
| export {}; |
| import type { SessionAction } from './types.ts'; | ||
| export declare function isClickLikeCommand(command: string): command is 'click' | 'press'; | ||
| export declare function formatScriptArg(value: string): string; | ||
| export declare function formatScriptArgQuoteIfNeeded(value: string): string; | ||
| export declare function formatScriptActionSummary(action: SessionAction): string; | ||
| export declare function appendScriptSeriesFlags(parts: string[], action: Pick<SessionAction, 'command' | 'flags'>): void; | ||
| export declare function appendRuntimeHintFlags(parts: string[], flags: Pick<SessionAction, 'flags'>['flags'] | { | ||
| platform?: 'ios' | 'android'; | ||
| metroHost?: string; | ||
| metroPort?: number; | ||
| bundleUrl?: string; | ||
| launchUrl?: string; | ||
| } | undefined): void; | ||
| export declare function appendRecordActionScriptArgs(parts: string[], action: SessionAction): void; | ||
| export declare function parseReplaySeriesFlags(command: string, args: string[]): { | ||
| positionals: string[]; | ||
| flags: SessionAction['flags']; | ||
| }; | ||
| export declare function parseReplayRuntimeFlags(args: string[]): { | ||
| positionals: string[]; | ||
| flags: { | ||
| platform?: 'ios' | 'android'; | ||
| metroHost?: string; | ||
| metroPort?: number; | ||
| bundleUrl?: string; | ||
| launchUrl?: string; | ||
| }; | ||
| }; |
| import { type RawSnapshotNode, type Rect } from '../utils/snapshot.ts'; | ||
| type ScrollIntoViewPlan = { | ||
| x: number; | ||
| startY: number; | ||
| endY: number; | ||
| count: number; | ||
| direction: 'up' | 'down'; | ||
| }; | ||
| export declare function resolveViewportRect(nodes: RawSnapshotNode[], targetRect: Rect): Rect | null; | ||
| export declare function buildScrollIntoViewPlan(targetRect: Rect, viewportRect: Rect): ScrollIntoViewPlan | null; | ||
| export declare function isRectWithinSafeViewportBand(targetRect: Rect, viewportRect: Rect): boolean; | ||
| export {}; |
| import type { Platform } from '../utils/device.ts'; | ||
| import type { SnapshotNode } from '../utils/snapshot.ts'; | ||
| export declare function buildSelectorChainForNode(node: SnapshotNode, _platform: Platform, options?: { | ||
| action?: 'click' | 'fill' | 'get'; | ||
| }): string[]; |
| import type { Platform } from '../utils/device.ts'; | ||
| import type { SnapshotNode } from '../utils/snapshot.ts'; | ||
| import type { Selector } from './selectors-parse.ts'; | ||
| export declare function matchesSelector(node: SnapshotNode, selector: Selector, platform: Platform): boolean; | ||
| export declare function isNodeVisible(node: SnapshotNode): boolean; | ||
| export declare function isNodeEditable(node: SnapshotNode, platform: Platform): boolean; |
| export type SelectorKey = 'id' | 'role' | 'text' | 'label' | 'value' | 'visible' | 'hidden' | 'editable' | 'selected' | 'enabled' | 'hittable'; | ||
| export type SelectorTerm = { | ||
| key: SelectorKey; | ||
| value: string | boolean; | ||
| }; | ||
| export type Selector = { | ||
| raw: string; | ||
| terms: SelectorTerm[]; | ||
| }; | ||
| export type SelectorChain = { | ||
| raw: string; | ||
| selectors: Selector[]; | ||
| }; | ||
| export declare function parseSelectorChain(expression: string): SelectorChain; | ||
| export declare function tryParseSelectorChain(expression: string): SelectorChain | null; | ||
| export declare function isSelectorToken(token: string): boolean; | ||
| export declare function splitSelectorFromArgs(args: string[], options?: { | ||
| preferTrailingValue?: boolean; | ||
| }): { | ||
| selectorExpression: string; | ||
| rest: string[]; | ||
| } | null; | ||
| export declare function splitIsSelectorArgs(positionals: string[]): { | ||
| predicate: string; | ||
| split: { | ||
| selectorExpression: string; | ||
| rest: string[]; | ||
| } | null; | ||
| }; |
| import type { Platform } from '../utils/device.ts'; | ||
| import type { SnapshotNode, SnapshotState } from '../utils/snapshot.ts'; | ||
| import type { Selector, SelectorChain } from './selectors-parse.ts'; | ||
| export type SelectorDiagnostics = { | ||
| selector: string; | ||
| matches: number; | ||
| }; | ||
| type SelectorResolution = { | ||
| node: SnapshotNode; | ||
| selector: Selector; | ||
| selectorIndex: number; | ||
| matches: number; | ||
| diagnostics: SelectorDiagnostics[]; | ||
| }; | ||
| export declare function resolveSelectorChain(nodes: SnapshotState['nodes'], chain: SelectorChain, options: { | ||
| platform: Platform; | ||
| requireRect?: boolean; | ||
| requireUnique?: boolean; | ||
| disambiguateAmbiguous?: boolean; | ||
| }): SelectorResolution | null; | ||
| export declare function findSelectorChainMatch(nodes: SnapshotState['nodes'], chain: SelectorChain, options: { | ||
| platform: Platform; | ||
| requireRect?: boolean; | ||
| }): { | ||
| selectorIndex: number; | ||
| selector: Selector; | ||
| matches: number; | ||
| diagnostics: SelectorDiagnostics[]; | ||
| } | null; | ||
| export declare function formatSelectorFailure(chain: SelectorChain, diagnostics: SelectorDiagnostics[], options: { | ||
| unique?: boolean; | ||
| }): string; | ||
| export {}; |
| export type { SelectorChain } from './selectors-parse.ts'; | ||
| export { parseSelectorChain, tryParseSelectorChain, isSelectorToken, splitSelectorFromArgs, splitIsSelectorArgs, } from './selectors-parse.ts'; | ||
| export { isNodeVisible, isNodeEditable } from './selectors-match.ts'; | ||
| export { resolveSelectorChain, findSelectorChainMatch, formatSelectorFailure, } from './selectors-resolve.ts'; | ||
| export { buildSelectorChainForNode } from './selectors-build.ts'; |
| import { readVersion } from '../utils/version.ts'; | ||
| import { readProcessStartTime } from '../utils/process-identity.ts'; | ||
| export type DaemonLockInfo = { | ||
| pid: number; | ||
| version: string; | ||
| startedAt: number; | ||
| processStartTime?: string; | ||
| }; | ||
| export declare function resolveDaemonCodeSignature(): string; | ||
| export declare function writeInfo(baseDir: string, infoPath: string, logPath: string, opts: { | ||
| socketPort?: number; | ||
| httpPort?: number; | ||
| token: string; | ||
| version: string; | ||
| codeSignature: string; | ||
| processStartTime: string | undefined; | ||
| }): void; | ||
| export declare function removeInfo(infoPath: string): void; | ||
| export declare function readLockInfo(lockPath: string): DaemonLockInfo | null; | ||
| export declare function acquireDaemonLock(baseDir: string, lockPath: string, lockData: DaemonLockInfo): boolean; | ||
| export declare function releaseDaemonLock(lockPath: string): void; | ||
| export declare function parseIntegerEnv(raw: string | undefined): number | undefined; | ||
| export { readVersion, readProcessStartTime }; |
| import type { SessionAction } from './types.ts'; | ||
| export declare function appendOpenActionScriptArgs(parts: string[], action: Pick<SessionAction, 'positionals' | 'flags' | 'runtime'>): void; | ||
| export declare function parseReplayOpenFlags(args: string[]): { | ||
| positionals: string[]; | ||
| flags: SessionAction['flags']; | ||
| runtime?: SessionAction['runtime']; | ||
| }; |
| import type { DaemonRequest } from './types.ts'; | ||
| import { SessionStore } from './session-store.ts'; | ||
| export declare function resolveEffectiveSessionName(req: DaemonRequest, sessionStore: SessionStore): string; |
| import type { CommandFlags } from '../core/dispatch.ts'; | ||
| import type { SessionState } from './types.ts'; | ||
| export type SessionSelectorConflictKey = 'platform' | 'target' | 'udid' | 'serial' | 'device' | 'iosSimulatorDeviceSet' | 'androidDeviceAllowlist'; | ||
| export type SessionSelectorConflict = { | ||
| key: SessionSelectorConflictKey; | ||
| value: string; | ||
| }; | ||
| export declare function assertSessionSelectorMatches(session: SessionState, flags?: CommandFlags): void; | ||
| export declare function listSessionSelectorConflicts(session: SessionState, flags?: CommandFlags): SessionSelectorConflict[]; | ||
| export declare function formatSessionSelectorConflict(conflict: SessionSelectorConflict): string; |
| import type { CommandFlags } from '../core/dispatch.ts'; | ||
| import type { SessionRuntimeHints, SessionState } from './types.ts'; | ||
| export declare class SessionStore { | ||
| private readonly sessions; | ||
| private readonly runtimeHints; | ||
| private readonly sessionsDir; | ||
| constructor(sessionsDir: string); | ||
| get(name: string): SessionState | undefined; | ||
| has(name: string): boolean; | ||
| set(name: string, session: SessionState): void; | ||
| delete(name: string): boolean; | ||
| values(): IterableIterator<SessionState>; | ||
| toArray(): SessionState[]; | ||
| getRuntimeHints(name: string): SessionRuntimeHints | undefined; | ||
| setRuntimeHints(name: string, hints: SessionRuntimeHints): void; | ||
| clearRuntimeHints(name: string): boolean; | ||
| recordAction(session: SessionState, entry: { | ||
| command: string; | ||
| positionals: string[]; | ||
| flags: CommandFlags; | ||
| runtime?: SessionRuntimeHints; | ||
| result?: Record<string, unknown>; | ||
| }): void; | ||
| writeSessionLog(session: SessionState): void; | ||
| defaultTracePath(session: SessionState): string; | ||
| /** Path to session-scoped app log file. Agent can grep this for token-efficient debugging. */ | ||
| resolveAppLogPath(sessionName: string): string; | ||
| resolveAppLogPidPath(sessionName: string): string; | ||
| static safeSessionName(name: string): string; | ||
| static expandHome(filePath: string, cwd?: string): string; | ||
| private resolveScriptPath; | ||
| private buildOptimizedActions; | ||
| } |
| import type { SnapshotNode } from '../utils/snapshot.ts'; | ||
| type SnapshotDiffLine = { | ||
| kind: 'added' | 'removed' | 'unchanged'; | ||
| text: string; | ||
| }; | ||
| type SnapshotDiffSummary = { | ||
| additions: number; | ||
| removals: number; | ||
| unchanged: number; | ||
| }; | ||
| type SnapshotDiffResult = { | ||
| summary: SnapshotDiffSummary; | ||
| lines: SnapshotDiffLine[]; | ||
| }; | ||
| type SnapshotDiffOptions = { | ||
| flatten?: boolean; | ||
| }; | ||
| export declare function buildSnapshotDiff(previousNodes: SnapshotNode[], currentNodes: SnapshotNode[], options?: SnapshotDiffOptions): SnapshotDiffResult; | ||
| export declare function countSnapshotComparableLines(nodes: SnapshotNode[], options?: SnapshotDiffOptions): number; | ||
| export {}; |
| import type { Platform } from '../utils/device.ts'; | ||
| import type { RawSnapshotNode, SnapshotState } from '../utils/snapshot.ts'; | ||
| export declare function findNodeByLabel(nodes: SnapshotState['nodes'], label: string): import("../index.ts").SnapshotNode | null; | ||
| export declare function resolveRefLabel(node: SnapshotState['nodes'][number], nodes: SnapshotState['nodes']): string | undefined; | ||
| export declare function pruneGroupNodes(nodes: RawSnapshotNode[]): RawSnapshotNode[]; | ||
| export declare function normalizeType(type: string): string; | ||
| export declare function isFillableType(type: string, platform: Platform): boolean; | ||
| export declare function findNearestHittableAncestor(nodes: SnapshotState['nodes'], node: SnapshotState['nodes'][number]): SnapshotState['nodes'][number] | null; | ||
| export declare function extractNodeText(node: SnapshotState['nodes'][number]): string; | ||
| export declare function extractNodeReadText(node: SnapshotState['nodes'][number]): string; |
| import type { SnapshotNode, SnapshotState } from '../utils/snapshot.ts'; | ||
| export type TouchReferenceFrame = { | ||
| referenceWidth: number; | ||
| referenceHeight: number; | ||
| }; | ||
| export declare function getSnapshotReferenceFrame(snapshot: SnapshotState | undefined): TouchReferenceFrame | undefined; | ||
| export declare function inferTouchReferenceFrame(nodes: Array<Pick<SnapshotNode, 'type' | 'rect'>>): TouchReferenceFrame | undefined; |
| import net from 'node:net'; | ||
| import type { Server as HttpServer } from 'node:http'; | ||
| import type { DaemonRequest, DaemonResponse } from './types.ts'; | ||
| export declare function createSocketServer(handleRequest: (req: DaemonRequest) => Promise<DaemonResponse>): net.Server; | ||
| export declare function listenNetServer(server: net.Server): Promise<number>; | ||
| export declare function listenHttpServer(server: HttpServer): Promise<number>; |
| import type { MaterializeInstallSource } from '../platforms/install-source.ts'; | ||
| import type { CommandFlags } from '../core/dispatch.ts'; | ||
| import type { SessionSurface } from '../core/session-surface.ts'; | ||
| import type { DeviceInfo, Platform, PlatformSelector } from '../utils/device.ts'; | ||
| import type { ExecResult } from '../utils/exec.ts'; | ||
| import type { SnapshotState } from '../utils/snapshot.ts'; | ||
| export type DaemonInstallSource = MaterializeInstallSource; | ||
| export type DaemonLockPolicy = 'reject' | 'strip'; | ||
| export type DaemonRequest = { | ||
| token: string; | ||
| session: string; | ||
| command: string; | ||
| positionals: string[]; | ||
| flags?: CommandFlags; | ||
| runtime?: SessionRuntimeHints; | ||
| meta?: { | ||
| requestId?: string; | ||
| debug?: boolean; | ||
| cwd?: string; | ||
| tenantId?: string; | ||
| runId?: string; | ||
| leaseId?: string; | ||
| leaseTtlMs?: number; | ||
| leaseBackend?: 'ios-simulator'; | ||
| sessionIsolation?: 'none' | 'tenant'; | ||
| uploadedArtifactId?: string; | ||
| clientArtifactPaths?: Record<string, string>; | ||
| installSource?: DaemonInstallSource; | ||
| retainMaterializedPaths?: boolean; | ||
| materializedPathRetentionMs?: number; | ||
| materializationId?: string; | ||
| lockPolicy?: DaemonLockPolicy; | ||
| lockPlatform?: PlatformSelector; | ||
| }; | ||
| }; | ||
| export type SessionRuntimeHints = { | ||
| platform?: 'ios' | 'android'; | ||
| metroHost?: string; | ||
| metroPort?: number; | ||
| bundleUrl?: string; | ||
| launchUrl?: string; | ||
| }; | ||
| export type DaemonArtifact = { | ||
| field: string; | ||
| artifactId?: string; | ||
| fileName?: string; | ||
| localPath?: string; | ||
| path?: string; | ||
| }; | ||
| export type DaemonResponseData = Record<string, unknown> & { | ||
| artifacts?: DaemonArtifact[]; | ||
| }; | ||
| export type DaemonResponse = { | ||
| ok: true; | ||
| data?: DaemonResponseData; | ||
| } | { | ||
| ok: false; | ||
| error: { | ||
| code: string; | ||
| message: string; | ||
| hint?: string; | ||
| diagnosticId?: string; | ||
| logPath?: string; | ||
| details?: Record<string, unknown>; | ||
| }; | ||
| }; | ||
| type RecordingTelemetryBase = { | ||
| tMs: number; | ||
| x: number; | ||
| y: number; | ||
| referenceWidth?: number; | ||
| referenceHeight?: number; | ||
| }; | ||
| type RecordingTelemetryTravel = RecordingTelemetryBase & { | ||
| x2: number; | ||
| y2: number; | ||
| durationMs: number; | ||
| }; | ||
| export type RecordingGestureEvent = (RecordingTelemetryBase & { | ||
| kind: 'tap' | 'longpress'; | ||
| durationMs?: number; | ||
| }) | (RecordingTelemetryTravel & { | ||
| kind: 'swipe'; | ||
| }) | (RecordingTelemetryTravel & { | ||
| kind: 'scroll'; | ||
| contentDirection: 'up' | 'down' | 'left' | 'right'; | ||
| amount?: number; | ||
| }) | (RecordingTelemetryTravel & { | ||
| kind: 'back-swipe'; | ||
| edge: 'left' | 'right'; | ||
| }) | (RecordingTelemetryBase & { | ||
| kind: 'pinch'; | ||
| scale: number; | ||
| durationMs: number; | ||
| }); | ||
| type SessionRecordingBase = { | ||
| outPath: string; | ||
| clientOutPath?: string; | ||
| telemetryPath?: string; | ||
| overlayWarning?: string; | ||
| startedAt: number; | ||
| showTouches: boolean; | ||
| gestureEvents: RecordingGestureEvent[]; | ||
| touchReferenceFrame?: { | ||
| referenceWidth: number; | ||
| referenceHeight: number; | ||
| }; | ||
| gestureClockOriginAtMs?: number; | ||
| gestureClockOriginUptimeMs?: number; | ||
| runnerSessionId?: string; | ||
| invalidatedReason?: string; | ||
| }; | ||
| export type SessionState = { | ||
| name: string; | ||
| device: DeviceInfo; | ||
| createdAt: number; | ||
| surface?: SessionSurface; | ||
| appBundleId?: string; | ||
| appName?: string; | ||
| snapshot?: SnapshotState; | ||
| trace?: { | ||
| outPath: string; | ||
| startedAt: number; | ||
| }; | ||
| recordSession?: boolean; | ||
| saveScriptPath?: string; | ||
| actions: SessionAction[]; | ||
| recording?: (SessionRecordingBase & { | ||
| platform: 'ios'; | ||
| child: ReturnType<typeof import('node:child_process').spawn>; | ||
| wait: Promise<ExecResult>; | ||
| remotePath?: string; | ||
| }) | (SessionRecordingBase & { | ||
| platform: 'android'; | ||
| remotePath: string; | ||
| remotePid: string; | ||
| }) | (SessionRecordingBase & { | ||
| platform: 'ios-device-runner'; | ||
| remotePath: string; | ||
| runnerStartedAtUptimeMs?: number; | ||
| targetAppReadyUptimeMs?: number; | ||
| }) | (SessionRecordingBase & { | ||
| platform: 'macos-runner'; | ||
| remotePath?: string; | ||
| }); | ||
| /** Session-scoped app log stream; logs written to outPath for agent to grep */ | ||
| appLog?: { | ||
| platform: Platform; | ||
| backend: 'ios-simulator' | 'ios-device' | 'android' | 'macos'; | ||
| outPath: string; | ||
| startedAt: number; | ||
| getState: () => 'active' | 'failed'; | ||
| stop: () => Promise<void>; | ||
| wait: Promise<ExecResult>; | ||
| }; | ||
| }; | ||
| export type SessionAction = { | ||
| ts: number; | ||
| command: string; | ||
| positionals: string[]; | ||
| runtime?: SessionRuntimeHints; | ||
| flags: Partial<CommandFlags> & { | ||
| snapshotInteractiveOnly?: boolean; | ||
| snapshotCompact?: boolean; | ||
| snapshotDepth?: number; | ||
| snapshotScope?: string; | ||
| snapshotRaw?: boolean; | ||
| saveScript?: boolean | string; | ||
| noRecord?: boolean; | ||
| }; | ||
| result?: Record<string, unknown>; | ||
| }; | ||
| export {}; |
| export declare function trackUploadedArtifact(params: { | ||
| artifactPath: string; | ||
| tempDir: string; | ||
| tenantId?: string; | ||
| }): string; | ||
| export declare function prepareUploadedArtifact(uploadId: string, tenantId?: string): string; | ||
| export declare function cleanupUploadedArtifact(uploadId: string): void; |
| import type { IncomingMessage } from 'node:http'; | ||
| export declare function receiveUpload(req: IncomingMessage): Promise<{ | ||
| artifactPath: string; | ||
| tempDir: string; | ||
| }>; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| export declare function adbArgs(device: DeviceInfo, args: string[]): string[]; | ||
| export declare function ensureAdb(): Promise<void>; | ||
| export declare function isClipboardShellUnsupported(stdout: string, stderr: string): boolean; | ||
| export declare function sleep(ms: number): Promise<void>; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| export declare function resolveAndroidApp(device: DeviceInfo, app: string): Promise<{ | ||
| type: 'intent' | 'package'; | ||
| value: string; | ||
| }>; | ||
| export declare function listAndroidApps(device: DeviceInfo, filter?: 'user-installed' | 'all'): Promise<Array<{ | ||
| package: string; | ||
| name: string; | ||
| }>>; | ||
| export declare function inferAndroidAppName(packageName: string): string; | ||
| export declare function getAndroidAppState(device: DeviceInfo): Promise<{ | ||
| package?: string; | ||
| activity?: string; | ||
| }>; | ||
| export declare function openAndroidApp(device: DeviceInfo, app: string, activity?: string): Promise<void>; | ||
| export declare function isAmStartError(stdout: string, stderr: string): boolean; | ||
| export declare function parseAndroidLaunchComponent(stdout: string): string | null; | ||
| export declare function openAndroidDevice(device: DeviceInfo): Promise<void>; | ||
| export declare function closeAndroidApp(device: DeviceInfo, app: string): Promise<void>; | ||
| export declare function installAndroidInstallablePath(device: DeviceInfo, installablePath: string): Promise<void>; | ||
| export declare function installAndroidInstallablePathAndResolvePackageName(device: DeviceInfo, installablePath: string, packageNameHint?: string): Promise<string | undefined>; | ||
| export declare function installAndroidApp(device: DeviceInfo, appPath: string): Promise<{ | ||
| archivePath?: string; | ||
| installablePath: string; | ||
| packageName?: string; | ||
| appName?: string; | ||
| launchTarget?: string; | ||
| }>; | ||
| export declare function reinstallAndroidApp(device: DeviceInfo, app: string, appPath: string): Promise<{ | ||
| package: string; | ||
| }>; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| type AndroidKeyboardType = 'text' | 'number' | 'email' | 'phone' | 'password' | 'datetime' | 'unknown'; | ||
| export type AndroidKeyboardState = { | ||
| visible: boolean; | ||
| inputType?: string; | ||
| type?: AndroidKeyboardType; | ||
| }; | ||
| export declare function getAndroidKeyboardState(device: DeviceInfo): Promise<AndroidKeyboardState>; | ||
| export declare function dismissAndroidKeyboard(device: DeviceInfo): Promise<{ | ||
| attempts: number; | ||
| wasVisible: boolean; | ||
| dismissed: boolean; | ||
| visible: boolean; | ||
| inputType?: string; | ||
| type?: AndroidKeyboardType; | ||
| }>; | ||
| export declare function readAndroidClipboardText(device: DeviceInfo): Promise<string>; | ||
| export declare function writeAndroidClipboardText(device: DeviceInfo, text: string): Promise<void>; | ||
| export {}; |
| import { runCmd } from '../../utils/exec.ts'; | ||
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| type AndroidDeviceDiscoveryOptions = { | ||
| serialAllowlist?: ReadonlySet<string>; | ||
| }; | ||
| type AndroidAdbRunner = typeof runCmd; | ||
| export declare function parseAndroidEmulatorAvdNameOutput(rawOutput: string): string | undefined; | ||
| export declare function resolveAndroidEmulatorAvdName(serial: string, runAdb?: AndroidAdbRunner): Promise<string | undefined>; | ||
| export declare function parseAndroidTargetFromCharacteristics(rawOutput: string): 'tv' | null; | ||
| export declare function parseAndroidFeatureListForTv(rawOutput: string): boolean; | ||
| export declare function listAndroidDevices(options?: AndroidDeviceDiscoveryOptions): Promise<DeviceInfo[]>; | ||
| export declare function parseAndroidAvdList(rawOutput: string): string[]; | ||
| export declare function resolveAndroidAvdName(avdNames: string[], requestedName: string): string | undefined; | ||
| export declare function ensureAndroidEmulatorBooted(params: { | ||
| avdName: string; | ||
| serial?: string; | ||
| timeoutMs?: number; | ||
| headless?: boolean; | ||
| }): Promise<DeviceInfo>; | ||
| export declare function ensureAndroidEmulatorHeadlessBooted(params: { | ||
| avdName: string; | ||
| serial?: string; | ||
| timeoutMs?: number; | ||
| }): Promise<DeviceInfo>; | ||
| export declare function waitForAndroidBoot(serial: string, timeoutMs?: number): Promise<void>; | ||
| export {}; |
| export { ensureAdb } from './adb.ts'; | ||
| export { resolveAndroidApp, listAndroidApps, inferAndroidAppName, getAndroidAppState, openAndroidApp, isAmStartError, parseAndroidLaunchComponent, openAndroidDevice, closeAndroidApp, installAndroidInstallablePath, installAndroidInstallablePathAndResolvePackageName, installAndroidApp, reinstallAndroidApp, } from './app-lifecycle.ts'; | ||
| export { pressAndroid, swipeAndroid, backAndroid, homeAndroid, appSwitcherAndroid, longPressAndroid, typeAndroid, focusAndroid, fillAndroid, readAndroidTextAtPoint, scrollAndroid, scrollIntoViewAndroid, getAndroidScreenSize, } from './input-actions.ts'; | ||
| export { type AndroidKeyboardState, getAndroidKeyboardState, dismissAndroidKeyboard, readAndroidClipboardText, writeAndroidClipboardText, } from './device-input-state.ts'; | ||
| export { setAndroidSetting } from './settings.ts'; | ||
| export { pushAndroidNotification } from './notifications.ts'; | ||
| export { snapshotAndroid } from './snapshot.ts'; | ||
| export { screenshotAndroid } from './screenshot.ts'; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| export declare function pressAndroid(device: DeviceInfo, x: number, y: number): Promise<void>; | ||
| export declare function swipeAndroid(device: DeviceInfo, x1: number, y1: number, x2: number, y2: number, durationMs?: number): Promise<void>; | ||
| export declare function backAndroid(device: DeviceInfo): Promise<void>; | ||
| export declare function homeAndroid(device: DeviceInfo): Promise<void>; | ||
| export declare function appSwitcherAndroid(device: DeviceInfo): Promise<void>; | ||
| export declare function longPressAndroid(device: DeviceInfo, x: number, y: number, durationMs?: number): Promise<void>; | ||
| export declare function typeAndroid(device: DeviceInfo, text: string): Promise<void>; | ||
| export declare function focusAndroid(device: DeviceInfo, x: number, y: number): Promise<void>; | ||
| export declare function fillAndroid(device: DeviceInfo, x: number, y: number, text: string): Promise<void>; | ||
| export declare function scrollAndroid(device: DeviceInfo, direction: string, amount?: number): Promise<void>; | ||
| export declare function scrollIntoViewAndroid(device: DeviceInfo, text: string): Promise<void>; | ||
| export declare function getAndroidScreenSize(device: DeviceInfo): Promise<{ | ||
| width: number; | ||
| height: number; | ||
| }>; | ||
| export declare function readAndroidTextAtPoint(device: DeviceInfo, x: number, y: number): Promise<string | null>; |
| import { type MaterializeInstallSource } from '../install-source.ts'; | ||
| export type PreparedAndroidInstallArtifact = { | ||
| archivePath?: string; | ||
| installablePath: string; | ||
| packageName?: string; | ||
| cleanup: () => Promise<void>; | ||
| }; | ||
| export declare function prepareAndroidInstallArtifact(source: MaterializeInstallSource, options?: { | ||
| signal?: AbortSignal; | ||
| resolveIdentity?: boolean; | ||
| }): Promise<PreparedAndroidInstallArtifact>; |
| export declare function resolveAndroidArchivePackageName(archivePath: string): Promise<string | undefined>; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| type AndroidBroadcastPayload = { | ||
| action?: string; | ||
| receiver?: string; | ||
| extras?: Record<string, unknown>; | ||
| }; | ||
| export declare function pushAndroidNotification(device: DeviceInfo, packageName: string, payload: AndroidBroadcastPayload): Promise<{ | ||
| action: string; | ||
| extrasCount: number; | ||
| }>; | ||
| export {}; |
| export type AndroidAppTargetKind = 'package' | 'binary' | 'other'; | ||
| export declare function classifyAndroidAppTarget(target: string): AndroidAppTargetKind; | ||
| export declare function looksLikeAndroidPackageName(value: string): boolean; | ||
| export declare function formatAndroidInstalledPackageRequiredMessage(target: string): string; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| type ScreenshotAndroidDeps = { | ||
| enableDemoMode: (device: DeviceInfo) => Promise<void>; | ||
| settle: (ms: number) => Promise<void>; | ||
| capture: (device: DeviceInfo, outPath: string) => Promise<void>; | ||
| disableDemoMode: (device: DeviceInfo) => Promise<void>; | ||
| }; | ||
| export declare function screenshotAndroid(device: DeviceInfo, outPath: string, deps?: ScreenshotAndroidDeps): Promise<void>; | ||
| /** | ||
| * Enable Android demo mode and set deterministic time in status bar | ||
| * for consistent screenshots. | ||
| */ | ||
| export declare function enableAndroidDemoMode(device: DeviceInfo): Promise<void>; | ||
| /** Disable demo mode and restore the live status bar. */ | ||
| export declare function disableAndroidDemoMode(device: DeviceInfo): Promise<void>; | ||
| export {}; |
| export declare function resolveAndroidSdkRoots(env?: NodeJS.ProcessEnv): string[]; | ||
| export declare function ensureAndroidSdkPathConfigured(env?: NodeJS.ProcessEnv): Promise<void>; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| import { type PermissionSettingOptions } from '../permission-utils.ts'; | ||
| export declare function setAndroidSetting(device: DeviceInfo, setting: string, state: string, appPackage?: string, options?: PermissionSettingOptions): Promise<Record<string, unknown> | void>; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| import type { RawSnapshotNode, SnapshotOptions } from '../../utils/snapshot.ts'; | ||
| export declare function snapshotAndroid(device: DeviceInfo, options?: SnapshotOptions): Promise<{ | ||
| nodes: RawSnapshotNode[]; | ||
| truncated?: boolean; | ||
| }>; | ||
| export declare function dumpUiHierarchy(device: DeviceInfo): Promise<string>; |
| import type { RawSnapshotNode, Rect, SnapshotOptions } from '../../utils/snapshot.ts'; | ||
| export declare function findBounds(xml: string, query: string): { | ||
| x: number; | ||
| y: number; | ||
| } | null; | ||
| export declare function parseUiHierarchy(xml: string, maxNodes: number, options: SnapshotOptions): { | ||
| nodes: RawSnapshotNode[]; | ||
| truncated?: boolean; | ||
| }; | ||
| export declare function readNodeAttributes(node: string): { | ||
| text: string | null; | ||
| desc: string | null; | ||
| resourceId: string | null; | ||
| className: string | null; | ||
| bounds: string | null; | ||
| clickable?: boolean; | ||
| enabled?: boolean; | ||
| focusable?: boolean; | ||
| focused?: boolean; | ||
| }; | ||
| export declare function parseBounds(bounds: string | null): Rect | undefined; |
| export type AppearanceAction = 'light' | 'dark' | 'toggle'; | ||
| export declare function parseAppearanceAction(state: string): AppearanceAction; |
| type BootFailureReason = 'IOS_BOOT_TIMEOUT' | 'IOS_RUNNER_CONNECT_TIMEOUT' | 'IOS_TOOL_MISSING' | 'ANDROID_BOOT_TIMEOUT' | 'ADB_TRANSPORT_UNAVAILABLE' | 'CI_RESOURCE_STARVATION_SUSPECTED' | 'BOOT_COMMAND_FAILED' | 'UNKNOWN'; | ||
| type BootDiagnosticContext = { | ||
| platform?: 'ios' | 'android'; | ||
| phase?: 'boot' | 'connect' | 'transport'; | ||
| }; | ||
| export declare function classifyBootFailure(input: { | ||
| error?: unknown; | ||
| message?: string; | ||
| stdout?: string; | ||
| stderr?: string; | ||
| context?: BootDiagnosticContext; | ||
| }): BootFailureReason; | ||
| export declare function bootFailureHint(reason: BootFailureReason): string; | ||
| export {}; |
| export type MaterializeInstallSource = { | ||
| kind: 'url'; | ||
| url: string; | ||
| headers?: Record<string, string>; | ||
| } | { | ||
| kind: 'path'; | ||
| path: string; | ||
| }; | ||
| type MaterializeInstallableOptions = { | ||
| source: MaterializeInstallSource; | ||
| isInstallablePath: (candidatePath: string, stat: { | ||
| isFile(): boolean; | ||
| isDirectory(): boolean; | ||
| }) => boolean; | ||
| installableLabel: string; | ||
| allowArchiveExtraction?: boolean; | ||
| signal?: AbortSignal; | ||
| downloadTimeoutMs?: number; | ||
| }; | ||
| export type MaterializedInstallable = { | ||
| archivePath?: string; | ||
| installablePath: string; | ||
| cleanup: () => Promise<void>; | ||
| }; | ||
| export declare function materializeInstallablePath(options: MaterializeInstallableOptions): Promise<MaterializedInstallable>; | ||
| export declare function expandSourcePath(inputPath: string): string; | ||
| export declare function validateDownloadSourceUrl(parsedUrl: URL): Promise<void>; | ||
| export declare function isTrustedInstallSourceUrl(sourceUrl: string | URL): boolean; | ||
| export {}; |
| import type { IosAppInfo } from './devicectl.ts'; | ||
| export declare function filterAppleAppsByBundlePrefix(apps: IosAppInfo[], filter: 'user-installed' | 'all'): IosAppInfo[]; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| import { type PermissionSettingOptions } from '../permission-utils.ts'; | ||
| import { type IosAppInfo } from './devicectl.ts'; | ||
| export { screenshotIos, shouldFallbackToRunnerForIosScreenshot, shouldRetryIosSimulatorScreenshot, } from './screenshot.ts'; | ||
| type InstallIosAppOptions = { | ||
| appIdentifierHint?: string; | ||
| }; | ||
| export declare function resolveIosApp(device: DeviceInfo, app: string): Promise<string>; | ||
| export declare function openIosApp(device: DeviceInfo, app: string, options?: { | ||
| appBundleId?: string; | ||
| url?: string; | ||
| }): Promise<void>; | ||
| export declare function openIosDevice(device: DeviceInfo): Promise<void>; | ||
| export declare function closeIosApp(device: DeviceInfo, app: string): Promise<void>; | ||
| export declare function uninstallIosApp(device: DeviceInfo, app: string): Promise<{ | ||
| bundleId: string; | ||
| }>; | ||
| export declare function installIosApp(device: DeviceInfo, appPath: string, options?: InstallIosAppOptions): Promise<{ | ||
| archivePath?: string; | ||
| installablePath: string; | ||
| bundleId?: string; | ||
| appName?: string; | ||
| launchTarget?: string; | ||
| }>; | ||
| export declare function reinstallIosApp(device: DeviceInfo, app: string, appPath: string): Promise<{ | ||
| bundleId: string; | ||
| }>; | ||
| export declare function installIosInstallablePath(device: DeviceInfo, installablePath: string): Promise<void>; | ||
| export declare function readIosClipboardText(device: DeviceInfo): Promise<string>; | ||
| export declare function writeIosClipboardText(device: DeviceInfo, text: string): Promise<void>; | ||
| export declare function pushIosNotification(device: DeviceInfo, bundleId: string, payload: Record<string, unknown>): Promise<void>; | ||
| export declare function setIosSetting(device: DeviceInfo, setting: string, state: string, appBundleId?: string, options?: PermissionSettingOptions): Promise<Record<string, unknown> | void>; | ||
| export declare function listIosApps(device: DeviceInfo, filter?: 'user-installed' | 'all'): Promise<IosAppInfo[]>; | ||
| export declare function listSimulatorApps(device: DeviceInfo): Promise<IosAppInfo[]>; |
| export declare const IOS_BOOT_TIMEOUT_MS: number; | ||
| export declare const IOS_SIMCTL_LIST_TIMEOUT_MS: number; | ||
| export declare const IOS_APP_LAUNCH_TIMEOUT_MS: number; | ||
| export declare const IOS_DEVICECTL_TIMEOUT_MS: number; | ||
| export declare const IOS_SIMULATOR_FOCUS_TIMEOUT_MS: number; | ||
| export declare const IOS_SIMULATOR_SCREENSHOT_TIMEOUT_MS: number; | ||
| export declare const IOS_RUNNER_SCREENSHOT_COPY_TIMEOUT_MS: number; | ||
| export declare const IOS_SIMULATOR_SCREENSHOT_RETRY_MAX_ATTEMPTS = 5; | ||
| export declare const IOS_SIMULATOR_SCREENSHOT_RETRY_BASE_DELAY_MS = 1000; | ||
| export declare const IOS_SIMULATOR_SCREENSHOT_RETRY_MAX_DELAY_MS = 5000; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| export type IosAppInfo = { | ||
| bundleId: string; | ||
| name: string; | ||
| }; | ||
| export declare function runIosDevicectl(args: string[], context: { | ||
| action: string; | ||
| deviceId: string; | ||
| }): Promise<void>; | ||
| export declare function listIosDeviceApps(device: DeviceInfo, filter: 'user-installed' | 'all'): Promise<IosAppInfo[]>; | ||
| export declare function parseIosDeviceAppsPayload(payload: unknown): IosAppInfo[]; | ||
| export declare const IOS_DEVICECTL_DEFAULT_HINT = "Ensure the iOS device is unlocked, trusted, and available in Xcode > Devices, then retry."; | ||
| export declare function resolveIosDevicectlHint(stdout: string, stderr: string): string | null; |
| import type { DeviceInfo, DeviceTarget } from '../../utils/device.ts'; | ||
| type DevicectlAppleDevice = { | ||
| identifier?: string; | ||
| name?: string; | ||
| hardwareProperties?: { | ||
| platform?: string; | ||
| udid?: string; | ||
| productType?: string; | ||
| }; | ||
| deviceProperties?: { | ||
| name?: string; | ||
| productType?: string; | ||
| deviceType?: string; | ||
| }; | ||
| connectionProperties?: { | ||
| tunnelState?: string; | ||
| }; | ||
| }; | ||
| type IosDeviceDiscoveryOptions = { | ||
| simulatorSetPath?: string; | ||
| }; | ||
| export declare function isAppleProductType(productType: string): boolean; | ||
| export declare function isAppleTvProductType(productType: string): boolean; | ||
| export declare function resolveAppleTargetFromDevicectlDevice(device: DevicectlAppleDevice): DeviceTarget; | ||
| export declare function isSupportedAppleDevicectlDevice(device: DevicectlAppleDevice): boolean; | ||
| type FindBootableSimulatorOptions = IosDeviceDiscoveryOptions & { | ||
| target?: DeviceTarget; | ||
| }; | ||
| /** | ||
| * Finds an available iOS simulator by querying simctl directly. This is used | ||
| * as a fallback when `listIosDevices` returned no simulators (e.g. all filtered | ||
| * out) or only a physical device. Only simulators with `isAvailable: true` are | ||
| * considered so the caller can safely boot the result. | ||
| * | ||
| * Returns `null` when no suitable simulator can be found. | ||
| */ | ||
| export declare function findBootableIosSimulator(options?: FindBootableSimulatorOptions): Promise<DeviceInfo | null>; | ||
| export declare function listAppleDevices(options?: IosDeviceDiscoveryOptions): Promise<DeviceInfo[]>; | ||
| export declare const listIosDevices: typeof listAppleDevices; | ||
| export {}; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| export type EnsureSimulatorResult = { | ||
| udid: string; | ||
| device: string; | ||
| runtime: string; | ||
| created: boolean; | ||
| booted: boolean; | ||
| }; | ||
| type EnsureSimulatorOptions = { | ||
| deviceName: string; | ||
| runtime?: string; | ||
| simulatorSetPath?: string | null; | ||
| reuseExisting: boolean; | ||
| boot: boolean; | ||
| ensureReady: (device: DeviceInfo) => Promise<void>; | ||
| }; | ||
| export declare function ensureSimulatorExists(options: EnsureSimulatorOptions): Promise<EnsureSimulatorResult>; | ||
| export {}; |
| export { closeIosApp, installIosApp, installIosInstallablePath, listIosApps, listSimulatorApps, openIosApp, openIosDevice, pushIosNotification, readIosClipboardText, reinstallIosApp, resolveIosApp, screenshotIos, setIosSetting, uninstallIosApp, writeIosClipboardText, } from './apps.ts'; | ||
| export { ensureBootedSimulator } from './simulator.ts'; | ||
| export { parseIosDeviceAppsPayload, type IosAppInfo } from './devicectl.ts'; |
| import { type MaterializeInstallSource } from '../install-source.ts'; | ||
| type InstallIosArtifactOptions = { | ||
| appIdentifierHint?: string; | ||
| signal?: AbortSignal; | ||
| }; | ||
| export type PreparedIosInstallArtifact = { | ||
| archivePath?: string; | ||
| installablePath: string; | ||
| bundleId?: string; | ||
| appName?: string; | ||
| cleanup: () => Promise<void>; | ||
| }; | ||
| export declare function prepareIosInstallArtifact(source: MaterializeInstallSource, options?: InstallIosArtifactOptions): Promise<PreparedIosInstallArtifact>; | ||
| export declare function readIosBundleInfo(appBundlePath: string): Promise<{ | ||
| bundleId?: string; | ||
| appName?: string; | ||
| }>; | ||
| export {}; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| export type LaunchFailureReason = 'ARCH_MISMATCH' | 'APP_NOT_INSTALLED' | 'PERSISTENT_LAUNCH_FAIL' | 'UNKNOWN'; | ||
| type LaunchProbeResult = { | ||
| installed: boolean; | ||
| simulatorCompatible?: boolean; | ||
| }; | ||
| export declare function isSimulatorLaunchFBSError(error: unknown): boolean; | ||
| export declare function probeSimulatorLaunchContext(device: DeviceInfo, bundleId: string): Promise<LaunchProbeResult>; | ||
| export declare function classifyLaunchFailure(probe: LaunchProbeResult): LaunchFailureReason; | ||
| export declare function launchFailureHint(reason: LaunchFailureReason): string; | ||
| export {}; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| import type { IosAppInfo } from './devicectl.ts'; | ||
| export declare function resolveMacOsApp(app: string): Promise<string>; | ||
| export declare function openMacOsApp(_device: DeviceInfo, app: string, options?: { | ||
| appBundleId?: string; | ||
| url?: string; | ||
| }): Promise<void>; | ||
| export declare function closeMacOsApp(_device: DeviceInfo, app: string): Promise<void>; | ||
| export declare function readMacOsClipboardText(): Promise<string>; | ||
| export declare function writeMacOsClipboardText(text: string): Promise<void>; | ||
| export declare function setMacOsAppearance(state: string): Promise<void>; | ||
| export declare function listMacApps(filter?: 'user-installed' | 'all'): Promise<IosAppInfo[]>; |
| import type { SessionSurface } from '../../core/session-surface.ts'; | ||
| export type MacOsPermissionTarget = 'accessibility' | 'screen-recording' | 'input-monitoring'; | ||
| export type MacOsSnapshotNode = { | ||
| index: number; | ||
| type?: string; | ||
| role?: string; | ||
| subrole?: string; | ||
| label?: string; | ||
| value?: string; | ||
| identifier?: string; | ||
| rect?: { | ||
| x: number; | ||
| y: number; | ||
| width: number; | ||
| height: number; | ||
| }; | ||
| enabled?: boolean; | ||
| selected?: boolean; | ||
| hittable?: boolean; | ||
| depth?: number; | ||
| parentIndex?: number; | ||
| pid?: number; | ||
| bundleId?: string; | ||
| appName?: string; | ||
| windowTitle?: string; | ||
| surface?: string; | ||
| }; | ||
| export declare function resolveMacOsHelperPackageRootFrom(modulePath: string): string; | ||
| export declare function resolveFrontmostMacOsApp(): Promise<{ | ||
| bundleId?: string; | ||
| appName?: string; | ||
| pid?: number; | ||
| }>; | ||
| export declare function quitMacOsApp(bundleId: string): Promise<{ | ||
| bundleId: string; | ||
| running: boolean; | ||
| terminated: boolean; | ||
| forceTerminated: boolean; | ||
| }>; | ||
| export declare function runMacOsPermissionAction(action: 'grant' | 'reset', target: MacOsPermissionTarget): Promise<{ | ||
| target: MacOsPermissionTarget; | ||
| granted: boolean; | ||
| requested: boolean; | ||
| openedSettings: boolean; | ||
| action: 'grant' | 'reset'; | ||
| message?: string; | ||
| }>; | ||
| export declare function runMacOsAlertAction(action: 'get' | 'accept' | 'dismiss', options?: { | ||
| bundleId?: string; | ||
| surface?: SessionSurface; | ||
| }): Promise<{ | ||
| title?: string; | ||
| role?: string; | ||
| buttons?: string[]; | ||
| action?: string; | ||
| bundleId?: string; | ||
| }>; | ||
| export declare function runMacOsSnapshotAction(surface: Exclude<SessionSurface, 'app'>): Promise<{ | ||
| surface: Exclude<SessionSurface, 'app'>; | ||
| nodes: MacOsSnapshotNode[]; | ||
| truncated: boolean; | ||
| backend: 'macos-helper'; | ||
| }>; | ||
| export declare function runMacOsReadTextAction(x: number, y: number, options?: { | ||
| bundleId?: string; | ||
| surface?: SessionSurface; | ||
| }): Promise<{ | ||
| text: string; | ||
| }>; |
| export declare function readInfoPlistString(infoPlistPath: string, key: string): Promise<string | undefined>; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| import type { ClickButton } from '../../core/click-button.ts'; | ||
| export type RunnerCommand = { | ||
| command: 'tap' | 'mouseClick' | 'tapSeries' | 'longPress' | 'drag' | 'dragSeries' | 'type' | 'swipe' | 'findText' | 'readText' | 'snapshot' | 'screenshot' | 'back' | 'home' | 'appSwitcher' | 'alert' | 'pinch' | 'recordStart' | 'recordStop' | 'uptime' | 'shutdown'; | ||
| appBundleId?: string; | ||
| text?: string; | ||
| action?: 'get' | 'accept' | 'dismiss'; | ||
| x?: number; | ||
| y?: number; | ||
| button?: ClickButton; | ||
| count?: number; | ||
| intervalMs?: number; | ||
| doubleTap?: boolean; | ||
| pauseMs?: number; | ||
| pattern?: 'one-way' | 'ping-pong'; | ||
| x2?: number; | ||
| y2?: number; | ||
| durationMs?: number; | ||
| direction?: 'up' | 'down' | 'left' | 'right'; | ||
| scale?: number; | ||
| outPath?: string; | ||
| fps?: number; | ||
| interactiveOnly?: boolean; | ||
| compact?: boolean; | ||
| depth?: number; | ||
| scope?: string; | ||
| raw?: boolean; | ||
| clearFirst?: boolean; | ||
| }; | ||
| export declare function runIosRunnerCommand(device: DeviceInfo, command: RunnerCommand, options?: { | ||
| verbose?: boolean; | ||
| logPath?: string; | ||
| traceLogPath?: string; | ||
| requestId?: string; | ||
| }): Promise<Record<string, unknown>>; | ||
| export { isRetryableRunnerError, shouldRetryRunnerConnectError, resolveRunnerEarlyExitHint, } from './runner-errors.ts'; | ||
| export { resolveRunnerDestination, resolveRunnerBuildDestination, resolveRunnerMaxConcurrentDestinationsFlag, resolveRunnerSigningBuildSettings, resolveRunnerBundleBuildSettings, assertSafeDerivedCleanup, IOS_RUNNER_CONTAINER_BUNDLE_IDS, } from './runner-xctestrun.ts'; | ||
| export { getRunnerSessionSnapshot, stopIosRunnerSession, abortAllIosRunnerSessions, stopAllIosRunnerSessions, } from './runner-session.ts'; |
| import { AppError } from '../../utils/errors.ts'; | ||
| import type { RunnerCommand } from './runner-client.ts'; | ||
| import type { RunnerSession } from './runner-session.ts'; | ||
| export declare function isRetryableRunnerError(err: unknown): boolean; | ||
| export declare function shouldRetryRunnerConnectError(error: unknown): boolean; | ||
| export declare function resolveRunnerEarlyExitHint(message: string, stdout: string, stderr: string): string; | ||
| export declare function buildRunnerConnectError(params: { | ||
| port: number; | ||
| endpoints: string[]; | ||
| logPath?: string; | ||
| lastError: unknown; | ||
| }): AppError; | ||
| export declare function buildRunnerEarlyExitError(params: { | ||
| session: RunnerSession; | ||
| port: number; | ||
| logPath?: string; | ||
| }): Promise<AppError>; | ||
| export declare function resolveSigningFailureHint(error: AppError): string | undefined; | ||
| export declare function isReadOnlyRunnerCommand(command: RunnerCommand['command']): boolean; | ||
| export declare function assertRunnerRequestActive(requestId: string | undefined): void; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| export declare function repairMacOsRunnerProductsIfNeeded(device: DeviceInfo, productPaths: string[], xctestrunPath: string): Promise<void>; | ||
| export declare function isExpectedRunnerRepairFailure(error: unknown): boolean; |
| import { type ExecResult, type ExecBackgroundResult } from '../../utils/exec.ts'; | ||
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| import type { RunnerCommand } from './runner-client.ts'; | ||
| export type RunnerSession = { | ||
| sessionId: string; | ||
| device: DeviceInfo; | ||
| deviceId: string; | ||
| port: number; | ||
| xctestrunPath: string; | ||
| jsonPath: string; | ||
| testPromise: Promise<ExecResult>; | ||
| child: ExecBackgroundResult['child']; | ||
| ready: boolean; | ||
| }; | ||
| export declare function ensureRunnerSession(device: DeviceInfo, options: { | ||
| verbose?: boolean; | ||
| logPath?: string; | ||
| traceLogPath?: string; | ||
| }): Promise<RunnerSession>; | ||
| export declare function getRunnerSessionSnapshot(deviceId: string): { | ||
| sessionId: string; | ||
| alive: boolean; | ||
| } | null; | ||
| export declare function stopRunnerSession(session: RunnerSession): Promise<void>; | ||
| export declare function stopIosRunnerSession(deviceId: string): Promise<void>; | ||
| export declare function abortAllIosRunnerSessions(): Promise<void>; | ||
| export declare function stopAllIosRunnerSessions(): Promise<void>; | ||
| export declare function validateRunnerDevice(device: DeviceInfo): void; | ||
| export declare function executeRunnerCommandWithSession(device: DeviceInfo, session: RunnerSession, command: RunnerCommand, logPath: string | undefined, timeoutMs: number, signal?: AbortSignal): Promise<Record<string, unknown>>; | ||
| export declare function parseRunnerResponse(response: Response, session: RunnerSession, logPath?: string): Promise<Record<string, unknown>>; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| import type { RunnerCommand } from './runner-client.ts'; | ||
| import type { RunnerSession } from './runner-session.ts'; | ||
| export declare const RUNNER_STARTUP_TIMEOUT_MS: number; | ||
| export declare const RUNNER_COMMAND_TIMEOUT_MS: number; | ||
| export declare const RUNNER_DESTINATION_TIMEOUT_SECONDS: number; | ||
| export declare function waitForRunner(device: DeviceInfo, port: number, command: RunnerCommand, logPath?: string, timeoutMs?: number, session?: RunnerSession, signal?: AbortSignal): Promise<Response>; | ||
| export declare function getFreePort(): Promise<number>; | ||
| export declare function logChunk(chunk: string, logPath?: string, traceLogPath?: string, verbose?: boolean): void; | ||
| export declare function cleanupTempFile(filePath: string): void; |
| export declare function xctestrunReferencesExistingProducts(xctestrunPath: string): boolean; | ||
| export declare function resolveExistingXctestrunProductPaths(xctestrunPath: string): string[] | null; |
| import { type DeviceInfo } from '../../utils/device.ts'; | ||
| export { xctestrunReferencesExistingProducts } from './runner-xctestrun-products.ts'; | ||
| export declare const runnerPrepProcesses: Set<import("child_process").ChildProcess>; | ||
| export declare const IOS_RUNNER_CONTAINER_BUNDLE_IDS: string[]; | ||
| type EnsureXctestrunDeps = { | ||
| findProjectRoot: () => string; | ||
| findXctestrun: (root: string, device?: DeviceInfo) => string | null; | ||
| xctestrunReferencesProjectRoot: (xctestrunPath: string, projectRoot: string) => boolean; | ||
| resolveExistingXctestrunProductPaths: (xctestrunPath: string) => string[] | null; | ||
| repairRunnerProductsIfNeeded: (device: DeviceInfo, productPaths: string[], xctestrunPath: string) => Promise<void>; | ||
| assertSafeDerivedCleanup: (derivedPath: string) => void; | ||
| cleanRunnerDerivedArtifacts: (derivedPath: string) => void; | ||
| buildRunnerXctestrun: (device: DeviceInfo, projectPath: string, derived: string, options: { | ||
| verbose?: boolean; | ||
| logPath?: string; | ||
| traceLogPath?: string; | ||
| }) => Promise<void>; | ||
| }; | ||
| export declare function ensureXctestrun(device: DeviceInfo, options: { | ||
| verbose?: boolean; | ||
| logPath?: string; | ||
| traceLogPath?: string; | ||
| }, deps?: EnsureXctestrunDeps): Promise<string>; | ||
| export declare function shouldDeleteRunnerDerivedRootEntry(entryName: string): boolean; | ||
| export declare function findXctestrun(root: string, device?: DeviceInfo): string | null; | ||
| export declare function scoreXctestrunCandidate(candidatePath: string, device: DeviceInfo): number; | ||
| export declare function xctestrunReferencesProjectRoot(xctestrunPath: string, projectRoot: string): boolean; | ||
| export declare function prepareXctestrunWithEnv(xctestrunPath: string, envVars: Record<string, string>, suffix: string): Promise<{ | ||
| xctestrunPath: string; | ||
| jsonPath: string; | ||
| }>; | ||
| export declare function resolveRunnerDestination(device: DeviceInfo): string; | ||
| export declare function resolveRunnerBuildDestination(device: DeviceInfo): string; | ||
| export declare function resolveRunnerMaxConcurrentDestinationsFlag(device: DeviceInfo): string; | ||
| export declare function resolveRunnerSigningBuildSettings(env?: NodeJS.ProcessEnv, forDevice?: boolean, platform?: DeviceInfo['platform']): string[]; | ||
| export declare function resolveRunnerBundleBuildSettings(env?: NodeJS.ProcessEnv): string[]; | ||
| export declare function resolveRunnerPerformanceBuildSettings(): string[]; | ||
| export declare function assertSafeDerivedCleanup(derivedPath: string, env?: NodeJS.ProcessEnv): void; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| export declare function prepareSimulatorStatusBarForScreenshot(device: DeviceInfo): Promise<() => Promise<void>>; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| type SimulatorScreenshotFlowDeps = { | ||
| ensureBooted: (device: DeviceInfo) => Promise<void>; | ||
| prepareStatusBarForScreenshot: (device: DeviceInfo) => Promise<() => Promise<void>>; | ||
| captureWithRetry: (device: DeviceInfo, outPath: string) => Promise<void>; | ||
| captureWithRunner: (device: DeviceInfo, outPath: string, appBundleId?: string) => Promise<void>; | ||
| shouldFallbackToRunner: (error: unknown) => boolean; | ||
| }; | ||
| export declare function screenshotIos(device: DeviceInfo, outPath: string, appBundleId?: string): Promise<void>; | ||
| export declare function captureSimulatorScreenshotWithFallback(device: DeviceInfo, outPath: string, appBundleId?: string, deps?: SimulatorScreenshotFlowDeps): Promise<void>; | ||
| export declare function resolveSimulatorRunnerScreenshotCandidatePaths(containerPath: string, remoteFileName: string): string[]; | ||
| export declare function shouldFallbackToRunnerForIosScreenshot(error: unknown): boolean; | ||
| export declare function shouldRetryIosSimulatorScreenshot(error: unknown): boolean; | ||
| export { prepareSimulatorStatusBarForScreenshot } from './screenshot-status-bar.ts'; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| type SimctlArgsOptions = { | ||
| simulatorSetPath?: string; | ||
| }; | ||
| export declare function buildSimctlArgs(args: string[], options?: SimctlArgsOptions): string[]; | ||
| export declare function buildSimctlArgsForDevice(device: DeviceInfo, args: string[]): string[]; | ||
| export {}; |
| import type { DeviceInfo } from '../../utils/device.ts'; | ||
| export declare function ensureSimulator(device: DeviceInfo, command: string): void; | ||
| export declare function focusIosSimulatorWindow(): Promise<void>; | ||
| export declare function ensureBootedSimulator(device: DeviceInfo): Promise<void>; | ||
| export declare function shutdownSimulator(device: DeviceInfo): Promise<{ | ||
| success: boolean; | ||
| exitCode: number; | ||
| stdout: string; | ||
| stderr: string; | ||
| }>; | ||
| export declare function getSimulatorState(deviceOrUdid: DeviceInfo | string): Promise<string | null>; |
| export type PermissionAction = 'grant' | 'deny' | 'reset'; | ||
| export type PermissionTarget = 'camera' | 'microphone' | 'photos' | 'contacts' | 'contacts-limited' | 'notifications' | 'calendar' | 'location' | 'location-always' | 'media-library' | 'motion' | 'reminders' | 'siri'; | ||
| export type PermissionSettingOptions = { | ||
| permissionTarget?: string; | ||
| permissionMode?: string; | ||
| }; | ||
| export declare const PERMISSION_TARGETS: readonly PermissionTarget[]; | ||
| export declare function parsePermissionAction(action: string): PermissionAction; | ||
| export declare function parsePermissionTarget(value: string | undefined): PermissionTarget; |
| export declare function getRecordingOverlaySupportWarning(hostPlatform?: NodeJS.Platform): string | undefined; | ||
| export declare function trimRecordingStart(params: { | ||
| videoPath: string; | ||
| trimStartMs: number; | ||
| }): Promise<void>; | ||
| export declare function overlayRecordingTouches(params: { | ||
| videoPath: string; | ||
| telemetryPath: string; | ||
| targetLabel?: string; | ||
| }): Promise<void>; |
| type UploadArtifactOptions = { | ||
| localPath: string; | ||
| baseUrl: string; | ||
| token: string; | ||
| }; | ||
| export declare function uploadArtifact(options: UploadArtifactOptions): Promise<string>; | ||
| export {}; |
| import { type CliFlags, type FlagKey } from './command-schema.ts'; | ||
| type ParsedArgs = { | ||
| command: string | null; | ||
| positionals: string[]; | ||
| flags: CliFlags; | ||
| warnings: string[]; | ||
| }; | ||
| type ParseArgsOptions = { | ||
| strictFlags?: boolean; | ||
| }; | ||
| type ParsedFlagRecord = { | ||
| key: FlagKey; | ||
| token: string; | ||
| }; | ||
| type RawParsedArgs = ParsedArgs & { | ||
| providedFlags: ParsedFlagRecord[]; | ||
| }; | ||
| type FinalizeArgsOptions = ParseArgsOptions & { | ||
| defaultFlags?: Partial<CliFlags>; | ||
| }; | ||
| export declare function parseArgs(argv: string[], options?: FinalizeArgsOptions): ParsedArgs; | ||
| export declare function parseRawArgs(argv: string[]): RawParsedArgs; | ||
| export declare function finalizeParsedArgs(parsed: RawParsedArgs, options?: FinalizeArgsOptions): ParsedArgs; | ||
| export declare function toDaemonFlags(flags: CliFlags): Omit<CliFlags, 'json' | 'config' | 'remoteConfig' | 'help' | 'version'>; | ||
| export declare function usage(): string; | ||
| export declare function usageForCommand(command: string): string | null; | ||
| export {}; |
| import { type CliFlags, type FlagKey } from './command-schema.ts'; | ||
| type EnvMap = Record<string, string | undefined>; | ||
| export declare function resolveConfigBackedFlagDefaults(options: { | ||
| command: string | null; | ||
| cwd: string; | ||
| cliFlags: CliFlags; | ||
| env?: EnvMap; | ||
| }): Partial<CliFlags>; | ||
| export declare function readEnvFlagDefaultsForKeys(env: EnvMap, keys: readonly FlagKey[]): Partial<CliFlags>; | ||
| export {}; |
| import { type FlagDefinition, type FlagKey } from './command-schema.ts'; | ||
| export type OptionSpec = { | ||
| key: FlagKey; | ||
| flagDefinitions: readonly FlagDefinition[]; | ||
| config: { | ||
| enabled: boolean; | ||
| key: string; | ||
| }; | ||
| env: { | ||
| names: readonly string[]; | ||
| }; | ||
| supportsCommand(command: string | null): boolean; | ||
| }; | ||
| export declare function getOptionSpecs(): readonly OptionSpec[]; | ||
| export declare function getOptionSpec(key: FlagKey): OptionSpec | undefined; | ||
| export declare function getOptionSpecForToken(token: string): OptionSpec | undefined; | ||
| export declare function getConfigurableOptionSpecs(command: string | null): OptionSpec[]; | ||
| export declare function isFlagSupportedForCommand(key: FlagKey, command: string | null): boolean; | ||
| export declare function parseOptionValueFromSource(spec: OptionSpec, value: unknown, sourceLabel: string, rawKey: string): unknown; |
| import type { CliFlags } from './command-schema.ts'; | ||
| type EnvMap = Record<string, string | undefined>; | ||
| export declare function resolveCliOptions(argv: string[], options?: { | ||
| cwd?: string; | ||
| env?: EnvMap; | ||
| strictFlags?: boolean; | ||
| }): { | ||
| command: string | null; | ||
| positionals: string[]; | ||
| flags: CliFlags; | ||
| warnings: string[]; | ||
| }; | ||
| export {}; |
| export type CliFlags = { | ||
| json: boolean; | ||
| config?: string; | ||
| remoteConfig?: string; | ||
| stateDir?: string; | ||
| daemonBaseUrl?: string; | ||
| daemonAuthToken?: string; | ||
| daemonTransport?: 'auto' | 'socket' | 'http'; | ||
| daemonServerMode?: 'socket' | 'http' | 'dual'; | ||
| tenant?: string; | ||
| sessionIsolation?: 'none' | 'tenant'; | ||
| runId?: string; | ||
| leaseId?: string; | ||
| sessionLock?: 'reject' | 'strip'; | ||
| sessionLocked?: boolean; | ||
| sessionLockConflicts?: 'reject' | 'strip'; | ||
| platform?: 'ios' | 'macos' | 'android' | 'apple'; | ||
| target?: 'mobile' | 'tv' | 'desktop'; | ||
| device?: string; | ||
| udid?: string; | ||
| serial?: string; | ||
| iosSimulatorDeviceSet?: string; | ||
| androidDeviceAllowlist?: string; | ||
| out?: string; | ||
| session?: string; | ||
| runtime?: string; | ||
| metroHost?: string; | ||
| metroPort?: number; | ||
| metroProjectRoot?: string; | ||
| metroKind?: 'auto' | 'react-native' | 'expo'; | ||
| metroPublicBaseUrl?: string; | ||
| metroProxyBaseUrl?: string; | ||
| metroBearerToken?: string; | ||
| metroPreparePort?: number; | ||
| metroListenHost?: string; | ||
| metroStatusHost?: string; | ||
| metroStartupTimeoutMs?: number; | ||
| metroProbeTimeoutMs?: number; | ||
| metroRuntimeFile?: string; | ||
| metroNoReuseExisting?: boolean; | ||
| metroNoInstallDeps?: boolean; | ||
| bundleUrl?: string; | ||
| launchUrl?: string; | ||
| boot?: boolean; | ||
| reuseExisting?: boolean; | ||
| verbose?: boolean; | ||
| snapshotInteractiveOnly?: boolean; | ||
| snapshotCompact?: boolean; | ||
| snapshotDepth?: number; | ||
| snapshotScope?: string; | ||
| snapshotRaw?: boolean; | ||
| baseline?: string; | ||
| threshold?: string; | ||
| appsFilter?: 'user-installed' | 'all'; | ||
| count?: number; | ||
| fps?: number; | ||
| hideTouches?: boolean; | ||
| intervalMs?: number; | ||
| holdMs?: number; | ||
| jitterPx?: number; | ||
| doubleTap?: boolean; | ||
| clickButton?: 'primary' | 'secondary' | 'middle'; | ||
| pauseMs?: number; | ||
| pattern?: 'one-way' | 'ping-pong'; | ||
| activity?: string; | ||
| header?: string[]; | ||
| saveScript?: boolean | string; | ||
| shutdown?: boolean; | ||
| relaunch?: boolean; | ||
| surface?: 'app' | 'frontmost-app' | 'desktop' | 'menubar'; | ||
| headless?: boolean; | ||
| restart?: boolean; | ||
| noRecord?: boolean; | ||
| retainPaths?: boolean; | ||
| retentionMs?: number; | ||
| replayUpdate?: boolean; | ||
| steps?: string; | ||
| stepsFile?: string; | ||
| batchOnError?: 'stop'; | ||
| batchMaxSteps?: number; | ||
| batchSteps?: Array<{ | ||
| command: string; | ||
| positionals?: string[]; | ||
| flags?: Record<string, unknown>; | ||
| }>; | ||
| help: boolean; | ||
| version: boolean; | ||
| }; | ||
| export type FlagKey = keyof CliFlags; | ||
| type FlagType = 'boolean' | 'int' | 'enum' | 'string' | 'booleanOrString'; | ||
| export type FlagDefinition = { | ||
| key: FlagKey; | ||
| names: readonly string[]; | ||
| type: FlagType; | ||
| multiple?: boolean; | ||
| enumValues?: readonly string[]; | ||
| min?: number; | ||
| max?: number; | ||
| setValue?: CliFlags[FlagKey]; | ||
| usageLabel?: string; | ||
| usageDescription?: string; | ||
| }; | ||
| type CommandSchema = { | ||
| helpDescription: string; | ||
| summary?: string; | ||
| positionalArgs: readonly string[]; | ||
| allowsExtraPositionals?: boolean; | ||
| allowedFlags: readonly FlagKey[]; | ||
| defaults?: Partial<CliFlags>; | ||
| skipCapabilityCheck?: boolean; | ||
| usageOverride?: string; | ||
| listUsageOverride?: string; | ||
| }; | ||
| export declare const GLOBAL_FLAG_KEYS: Set<keyof CliFlags>; | ||
| export declare function getFlagDefinition(token: string): FlagDefinition | undefined; | ||
| export declare function getFlagDefinitions(): readonly FlagDefinition[]; | ||
| export declare function getCommandSchema(command: string | null): CommandSchema | undefined; | ||
| export declare function getCliCommandNames(): string[]; | ||
| export declare function getSchemaCapabilityKeys(): string[]; | ||
| export declare function isStrictFlagModeEnabled(value: string | undefined): boolean; | ||
| export declare function buildUsageText(): string; | ||
| export declare function buildCommandUsageText(commandName: string): string | null; | ||
| export {}; |
| export declare function resolveIosSimulatorDeviceSetPath(flagValue: string | undefined, env?: NodeJS.ProcessEnv): string | undefined; | ||
| export declare function parseSerialAllowlist(value: string): Set<string>; | ||
| export declare function resolveAndroidSerialAllowlist(flagValue: string | undefined, env?: NodeJS.ProcessEnv): ReadonlySet<string> | undefined; |
| export type ApplePlatform = 'ios' | 'macos'; | ||
| export type Platform = ApplePlatform | 'android'; | ||
| export type PlatformSelector = Platform | 'apple'; | ||
| export type DeviceKind = 'simulator' | 'emulator' | 'device'; | ||
| export type DeviceTarget = 'mobile' | 'tv' | 'desktop'; | ||
| export type DeviceInfo = { | ||
| platform: Platform; | ||
| id: string; | ||
| name: string; | ||
| kind: DeviceKind; | ||
| target?: DeviceTarget; | ||
| booted?: boolean; | ||
| simulatorSetPath?: string; | ||
| }; | ||
| type DeviceSelector = { | ||
| platform?: PlatformSelector; | ||
| target?: DeviceTarget; | ||
| deviceName?: string; | ||
| udid?: string; | ||
| serial?: string; | ||
| }; | ||
| type DeviceSelectionContext = { | ||
| simulatorSetPath?: string; | ||
| }; | ||
| export declare function normalizePlatformSelector(platform: PlatformSelector | undefined): PlatformSelector | undefined; | ||
| export declare function isApplePlatform(platform: Platform | PlatformSelector | undefined): platform is ApplePlatform | 'apple'; | ||
| export declare function matchesPlatformSelector(platform: Platform, selector: PlatformSelector | undefined): boolean; | ||
| export declare function resolveApplePlatformName(platformOrTarget: ApplePlatform | DeviceTarget | undefined): 'iOS' | 'tvOS' | 'macOS'; | ||
| export declare function resolveAppleSimulatorSetPathForSelector(params: { | ||
| simulatorSetPath?: string; | ||
| platform?: PlatformSelector; | ||
| target?: DeviceTarget; | ||
| }): string | undefined; | ||
| export declare function resolveDevice(devices: DeviceInfo[], selector: DeviceSelector, context?: DeviceSelectionContext): Promise<DeviceInfo>; | ||
| export {}; |
| type DiagnosticLevel = 'info' | 'warn' | 'error' | 'debug'; | ||
| type DiagnosticsScopeOptions = { | ||
| session?: string; | ||
| requestId?: string; | ||
| command?: string; | ||
| debug?: boolean; | ||
| logPath?: string; | ||
| traceLogPath?: string; | ||
| }; | ||
| export declare function createRequestId(): string; | ||
| export declare function withDiagnosticsScope<T>(options: DiagnosticsScopeOptions, fn: () => Promise<T> | T): Promise<T>; | ||
| export declare function getDiagnosticsMeta(): { | ||
| diagnosticId?: string; | ||
| requestId?: string; | ||
| session?: string; | ||
| command?: string; | ||
| debug?: boolean; | ||
| }; | ||
| export declare function emitDiagnostic(event: { | ||
| level?: DiagnosticLevel; | ||
| phase: string; | ||
| durationMs?: number; | ||
| data?: Record<string, unknown>; | ||
| }): void; | ||
| export declare function withDiagnosticTimer<T>(phase: string, fn: () => Promise<T> | T, data?: Record<string, unknown>): Promise<T>; | ||
| export declare function flushDiagnosticsToSessionFile(options?: { | ||
| force?: boolean; | ||
| }): string | null; | ||
| export declare function redactDiagnosticData<T>(input: T): T; | ||
| export {}; |
| type ErrorCode = 'INVALID_ARGS' | 'DEVICE_NOT_FOUND' | 'TOOL_MISSING' | 'APP_NOT_INSTALLED' | 'UNSUPPORTED_PLATFORM' | 'UNSUPPORTED_OPERATION' | 'COMMAND_FAILED' | 'SESSION_NOT_FOUND' | 'UNAUTHORIZED' | 'UNKNOWN'; | ||
| type AppErrorDetails = Record<string, unknown> & { | ||
| hint?: string; | ||
| diagnosticId?: string; | ||
| logPath?: string; | ||
| }; | ||
| export type NormalizedError = { | ||
| code: string; | ||
| message: string; | ||
| hint?: string; | ||
| diagnosticId?: string; | ||
| logPath?: string; | ||
| details?: Record<string, unknown>; | ||
| }; | ||
| export declare class AppError extends Error { | ||
| code: ErrorCode; | ||
| details?: AppErrorDetails; | ||
| cause?: unknown; | ||
| constructor(code: ErrorCode, message: string, details?: AppErrorDetails, cause?: unknown); | ||
| } | ||
| export declare function asAppError(err: unknown): AppError; | ||
| export declare function normalizeError(err: unknown, context?: { | ||
| diagnosticId?: string; | ||
| logPath?: string; | ||
| }): NormalizedError; | ||
| export {}; |
| import { spawn } from 'node:child_process'; | ||
| export type ExecResult = { | ||
| stdout: string; | ||
| stderr: string; | ||
| exitCode: number; | ||
| stdoutBuffer?: Buffer; | ||
| }; | ||
| type ExecOptions = { | ||
| cwd?: string; | ||
| env?: NodeJS.ProcessEnv; | ||
| allowFailure?: boolean; | ||
| binaryStdout?: boolean; | ||
| stdin?: string | Buffer; | ||
| timeoutMs?: number; | ||
| detached?: boolean; | ||
| }; | ||
| type ExecStreamOptions = ExecOptions & { | ||
| onStdoutChunk?: (chunk: string) => void; | ||
| onStderrChunk?: (chunk: string) => void; | ||
| onSpawn?: (child: ReturnType<typeof spawn>) => void; | ||
| }; | ||
| export type ExecBackgroundResult = { | ||
| child: ReturnType<typeof spawn>; | ||
| wait: Promise<ExecResult>; | ||
| }; | ||
| export declare function runCmd(cmd: string, args: string[], options?: ExecOptions): Promise<ExecResult>; | ||
| export declare function whichCmd(cmd: string): Promise<boolean>; | ||
| export declare function runCmdSync(cmd: string, args: string[], options?: ExecOptions): ExecResult; | ||
| export declare function runCmdDetached(cmd: string, args: string[], options?: ExecOptions): void; | ||
| export declare function runCmdStreaming(cmd: string, args: string[], options?: ExecStreamOptions): Promise<ExecResult>; | ||
| export declare function runCmdBackground(cmd: string, args: string[], options?: ExecOptions): ExecBackgroundResult; | ||
| export {}; |
| import type { SnapshotNode } from './snapshot.ts'; | ||
| export type FindLocator = 'any' | 'text' | 'label' | 'value' | 'role' | 'id'; | ||
| type FindMatchOptions = { | ||
| requireRect?: boolean; | ||
| }; | ||
| type FindBestMatches = { | ||
| matches: SnapshotNode[]; | ||
| score: number; | ||
| }; | ||
| export declare function findNodeByLocator(nodes: SnapshotNode[], locator: FindLocator, query: string, options?: FindMatchOptions): SnapshotNode | null; | ||
| export declare function findBestMatchesByLocator(nodes: SnapshotNode[], locator: FindLocator, query: string, options?: FindMatchOptions): FindBestMatches; | ||
| export {}; |
| import type { DeviceInfo } from './device.ts'; | ||
| import type { PermissionSettingOptions } from '../platforms/permission-utils.ts'; | ||
| export type RunnerContext = { | ||
| requestId?: string; | ||
| appBundleId?: string; | ||
| verbose?: boolean; | ||
| logPath?: string; | ||
| traceLogPath?: string; | ||
| }; | ||
| type Interactor = { | ||
| open(app: string, options?: { | ||
| activity?: string; | ||
| appBundleId?: string; | ||
| url?: string; | ||
| }): Promise<void>; | ||
| openDevice(): Promise<void>; | ||
| close(app: string): Promise<void>; | ||
| tap(x: number, y: number): Promise<Record<string, unknown> | void>; | ||
| doubleTap(x: number, y: number): Promise<Record<string, unknown> | void>; | ||
| swipe(x1: number, y1: number, x2: number, y2: number, durationMs?: number): Promise<Record<string, unknown> | void>; | ||
| longPress(x: number, y: number, durationMs?: number): Promise<Record<string, unknown> | void>; | ||
| focus(x: number, y: number): Promise<Record<string, unknown> | void>; | ||
| type(text: string): Promise<void>; | ||
| fill(x: number, y: number, text: string): Promise<Record<string, unknown> | void>; | ||
| scroll(direction: string, amount?: number): Promise<Record<string, unknown> | void>; | ||
| scrollIntoView(text: string): Promise<{ | ||
| attempts?: number; | ||
| } | void>; | ||
| screenshot(outPath: string, appBundleId?: string): Promise<void>; | ||
| back(): Promise<void>; | ||
| home(): Promise<void>; | ||
| appSwitcher(): Promise<void>; | ||
| readClipboard(): Promise<string>; | ||
| writeClipboard(text: string): Promise<void>; | ||
| setSetting(setting: string, state: string, appId?: string, options?: PermissionSettingOptions): Promise<Record<string, unknown> | void>; | ||
| }; | ||
| export declare function getInteractor(device: DeviceInfo, runnerContext: RunnerContext): Interactor; | ||
| export {}; |
| export declare function looksLikeInlineJson(value: string): boolean; |
| export declare function withKeyedLock<T>(locks: Map<string, Promise<unknown>>, key: string, task: () => Promise<T>): Promise<T>; |
| import { AppError, type NormalizedError } from './errors.ts'; | ||
| import type { ScreenshotDiffResult } from './screenshot-diff.ts'; | ||
| type JsonResult = { | ||
| success: true; | ||
| data?: unknown; | ||
| } | { | ||
| success: false; | ||
| error: { | ||
| code: string; | ||
| message: string; | ||
| hint?: string; | ||
| diagnosticId?: string; | ||
| logPath?: string; | ||
| details?: Record<string, unknown>; | ||
| }; | ||
| }; | ||
| export declare function printJson(result: JsonResult): void; | ||
| export declare function printHumanError(err: AppError | NormalizedError, options?: { | ||
| showDetails?: boolean; | ||
| }): void; | ||
| export declare function formatSnapshotText(data: Record<string, unknown>, options?: { | ||
| raw?: boolean; | ||
| flatten?: boolean; | ||
| }): string; | ||
| export declare function formatSnapshotDiffText(data: Record<string, unknown>): string; | ||
| export declare function formatScreenshotDiffText(data: ScreenshotDiffResult): string; | ||
| export {}; |
| type EnvMap = Record<string, string | undefined>; | ||
| type PathResolutionOptions = { | ||
| cwd?: string; | ||
| env?: EnvMap; | ||
| }; | ||
| export declare function expandUserHomePath(inputPath: string, options?: PathResolutionOptions): string; | ||
| export declare function resolveUserPath(inputPath: string, options?: PathResolutionOptions): string; | ||
| export {}; |
| export type ResolvedPayloadInput = { | ||
| kind: 'file'; | ||
| path: string; | ||
| } | { | ||
| kind: 'inline'; | ||
| text: string; | ||
| }; | ||
| export declare function resolvePayloadInput(value: string, options?: { | ||
| subject?: string; | ||
| cwd?: string; | ||
| expandPath?: (value: string, cwd?: string) => string; | ||
| }): ResolvedPayloadInput; |
| export declare function isProcessAlive(pid: number): boolean; | ||
| export declare function readProcessStartTime(pid: number): string | null; | ||
| export declare function readProcessCommand(pid: number): string | null; | ||
| export declare function isAgentDeviceDaemonCommand(command: string): boolean; | ||
| export declare function isAgentDeviceDaemonProcess(pid: number, expectedStartTime?: string): boolean; | ||
| export declare function waitForProcessExit(pid: number, timeoutMs: number): Promise<boolean>; | ||
| export declare function stopProcessForTakeover(pid: number, options: { | ||
| termTimeoutMs: number; | ||
| killTimeoutMs: number; | ||
| expectedStartTime?: string; | ||
| }): Promise<void>; |
| import type { CliFlags } from './command-schema.ts'; | ||
| type EnvMap = Record<string, string | undefined>; | ||
| export declare const REMOTE_OPEN_FLAG_KEYS: readonly ["remoteConfig", "session", "platform", "daemonBaseUrl", "daemonAuthToken", "daemonTransport", "metroProjectRoot", "metroKind", "metroPublicBaseUrl", "metroProxyBaseUrl", "metroBearerToken", "metroPreparePort", "metroListenHost", "metroStatusHost", "metroStartupTimeoutMs", "metroProbeTimeoutMs", "metroRuntimeFile", "metroNoReuseExisting", "metroNoInstallDeps"]; | ||
| export declare function loadRemoteConfigFile(options: { | ||
| configPath: string; | ||
| cwd: string; | ||
| env?: EnvMap; | ||
| }): Partial<CliFlags>; | ||
| export declare function resolveRemoteConfigDefaults(options: { | ||
| cliFlags: CliFlags; | ||
| cwd: string; | ||
| env: EnvMap; | ||
| }): Partial<CliFlags>; | ||
| export declare function pickRemoteOpenDefaults(defaultFlags: Partial<CliFlags>): Partial<CliFlags>; | ||
| export {}; |
| import type { AgentDeviceClient } from '../client.ts'; | ||
| import type { CliFlags } from './command-schema.ts'; | ||
| export declare function resolveRemoteOpenRuntime(flags: CliFlags, client: AgentDeviceClient): Promise<{ | ||
| platform?: 'ios' | 'android'; | ||
| metroHost?: string; | ||
| metroPort?: number; | ||
| bundleUrl?: string; | ||
| launchUrl?: string; | ||
| } | undefined>; |
| type RetryOptions = { | ||
| attempts?: number; | ||
| baseDelayMs?: number; | ||
| maxDelayMs?: number; | ||
| jitter?: number; | ||
| shouldRetry?: (error: unknown, attempt: number) => boolean; | ||
| }; | ||
| type RetryPolicy = { | ||
| maxAttempts: number; | ||
| baseDelayMs: number; | ||
| maxDelayMs: number; | ||
| jitter: number; | ||
| shouldRetry?: (error: unknown, attempt: number) => boolean; | ||
| }; | ||
| type RetryAttemptContext = { | ||
| attempt: number; | ||
| maxAttempts: number; | ||
| deadline?: Deadline; | ||
| }; | ||
| type TimeoutProfile = { | ||
| startupMs: number; | ||
| operationMs: number; | ||
| totalMs: number; | ||
| }; | ||
| type RetryTelemetryEvent = { | ||
| phase?: string; | ||
| event: 'attempt_failed' | 'retry_scheduled' | 'succeeded' | 'exhausted'; | ||
| attempt: number; | ||
| maxAttempts: number; | ||
| delayMs?: number; | ||
| elapsedMs?: number; | ||
| remainingMs?: number; | ||
| reason?: string; | ||
| }; | ||
| export declare function isEnvTruthy(value: string | undefined): boolean; | ||
| export declare const TIMEOUT_PROFILES: Record<string, TimeoutProfile>; | ||
| export declare class Deadline { | ||
| private readonly startedAtMs; | ||
| private readonly expiresAtMs; | ||
| private constructor(); | ||
| static fromTimeoutMs(timeoutMs: number, nowMs?: number): Deadline; | ||
| remainingMs(nowMs?: number): number; | ||
| elapsedMs(nowMs?: number): number; | ||
| isExpired(nowMs?: number): boolean; | ||
| } | ||
| export declare function retryWithPolicy<T>(fn: (context: RetryAttemptContext) => Promise<T>, policy?: Partial<RetryPolicy>, options?: { | ||
| deadline?: Deadline; | ||
| phase?: string; | ||
| signal?: AbortSignal; | ||
| classifyReason?: (error: unknown) => string | undefined; | ||
| onEvent?: (event: RetryTelemetryEvent) => void; | ||
| }): Promise<T>; | ||
| export declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>; | ||
| export {}; |
| export type ScreenshotDimensionMismatch = { | ||
| expected: { | ||
| width: number; | ||
| height: number; | ||
| }; | ||
| actual: { | ||
| width: number; | ||
| height: number; | ||
| }; | ||
| }; | ||
| export type ScreenshotDiffResult = { | ||
| diffPath?: string; | ||
| totalPixels: number; | ||
| differentPixels: number; | ||
| mismatchPercentage: number; | ||
| match: boolean; | ||
| dimensionMismatch?: ScreenshotDimensionMismatch; | ||
| }; | ||
| export type ScreenshotDiffOptions = { | ||
| threshold?: number; | ||
| outputPath?: string; | ||
| }; | ||
| export declare function compareScreenshots(baselinePath: string, currentPath: string, options?: ScreenshotDiffOptions): Promise<ScreenshotDiffResult>; |
| import type { CliFlags } from './command-schema.ts'; | ||
| import type { DaemonLockPolicy } from '../daemon/types.ts'; | ||
| export type BindingSettings = { | ||
| defaultPlatform?: CliFlags['platform']; | ||
| lockPolicy?: DaemonLockPolicy; | ||
| }; | ||
| type BindingPolicyOverrides = Pick<Partial<CliFlags>, 'sessionLock' | 'sessionLocked' | 'sessionLockConflicts'>; | ||
| type LockableFlags = Pick<Partial<CliFlags>, 'platform' | 'target' | 'device' | 'udid' | 'serial' | 'iosSimulatorDeviceSet' | 'androidDeviceAllowlist'>; | ||
| type BindingOptions = { | ||
| env?: NodeJS.ProcessEnv; | ||
| policyOverrides?: BindingPolicyOverrides; | ||
| configuredPlatform?: CliFlags['platform']; | ||
| configuredSession?: string; | ||
| inheritedPlatform?: CliFlags['platform']; | ||
| }; | ||
| export declare function applyDefaultPlatformBinding<T extends LockableFlags>(flags: T, options?: BindingOptions): T; | ||
| export declare function resolveBindingSettings(options: BindingOptions): BindingSettings; | ||
| export {}; |
| import type { SnapshotNode } from './snapshot.ts'; | ||
| type SnapshotDisplayLine = { | ||
| node: SnapshotNode; | ||
| depth: number; | ||
| type: string; | ||
| text: string; | ||
| }; | ||
| type SnapshotLineFormatOptions = { | ||
| summarizeTextSurfaces?: boolean; | ||
| }; | ||
| export declare function buildSnapshotDisplayLines(nodes: SnapshotNode[], options?: SnapshotLineFormatOptions): SnapshotDisplayLine[]; | ||
| export declare function formatSnapshotLine(node: SnapshotNode, depth: number, hiddenGroup: boolean, normalizedType?: string, options?: SnapshotLineFormatOptions): string; | ||
| export declare function displayLabel(node: SnapshotNode, type: string): string; | ||
| export declare function formatRole(type: string): string; | ||
| export {}; |
| export type Rect = { | ||
| x: number; | ||
| y: number; | ||
| width: number; | ||
| height: number; | ||
| }; | ||
| export type SnapshotOptions = { | ||
| interactiveOnly?: boolean; | ||
| compact?: boolean; | ||
| depth?: number; | ||
| scope?: string; | ||
| raw?: boolean; | ||
| }; | ||
| export type RawSnapshotNode = { | ||
| index: number; | ||
| type?: string; | ||
| role?: string; | ||
| subrole?: string; | ||
| label?: string; | ||
| value?: string; | ||
| identifier?: string; | ||
| rect?: Rect; | ||
| enabled?: boolean; | ||
| selected?: boolean; | ||
| hittable?: boolean; | ||
| depth?: number; | ||
| parentIndex?: number; | ||
| pid?: number; | ||
| bundleId?: string; | ||
| appName?: string; | ||
| windowTitle?: string; | ||
| surface?: string; | ||
| }; | ||
| export type SnapshotNode = RawSnapshotNode & { | ||
| ref: string; | ||
| }; | ||
| export type SnapshotState = { | ||
| nodes: SnapshotNode[]; | ||
| createdAt: number; | ||
| truncated?: boolean; | ||
| backend?: 'xctest' | 'android' | 'macos-helper'; | ||
| }; | ||
| export declare function attachRefs(nodes: RawSnapshotNode[]): SnapshotNode[]; | ||
| export declare function normalizeRef(input: string): string | null; | ||
| export declare function findNodeByRef(nodes: SnapshotNode[], ref: string): SnapshotNode | null; | ||
| export declare function centerOfRect(rect: Rect): { | ||
| x: number; | ||
| y: number; | ||
| }; |
| type TextSurfaceNode = { | ||
| type?: string; | ||
| label?: string; | ||
| value?: string; | ||
| identifier?: string; | ||
| role?: string; | ||
| subrole?: string; | ||
| }; | ||
| export declare function extractReadableText(node: TextSurfaceNode): string; | ||
| export declare function isLargeTextSurface(node: TextSurfaceNode, displayType?: string): boolean; | ||
| export declare function buildTextPreview(text: string): string; | ||
| export declare function describeTextSurface(node: TextSurfaceNode, displayType?: string): { | ||
| text: string; | ||
| isLargeSurface: boolean; | ||
| shouldSummarize: boolean; | ||
| }; | ||
| export declare function shouldSummarizeTextSurface(text: string): boolean; | ||
| export declare function trimText(value: unknown): string; | ||
| export {}; |
| export declare function resolveTimeoutMs(raw: string | undefined, fallback: number, min: number): number; | ||
| /** Alias for `resolveTimeoutMs` — semantically marks the caller expects seconds. */ | ||
| export declare const resolveTimeoutSeconds: typeof resolveTimeoutMs; |
| export declare function readVersion(): string; | ||
| export declare function findProjectRoot(): string; |
| export declare function waitForStableFile(filePath: string, options?: { | ||
| pollMs?: number; | ||
| attempts?: number; | ||
| }): Promise<void>; | ||
| export declare function isPlayableVideo(filePath: string): Promise<boolean>; | ||
| export declare function waitForPlayableVideo(filePath: string, options?: { | ||
| pollMs?: number; | ||
| attempts?: number; | ||
| }): Promise<void>; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 53 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 53 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
6
-14.29%947335
-8.95%6
20%53
-77.45%2396
-54.67%107
13.83%9
50%