agent-device 0.7.22 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -1
- package/dist/src/224.js +2 -2
- package/dist/src/bin.js +56 -56
- package/dist/src/client-types.d.ts +4 -2
- package/dist/src/daemon/request-lock-policy.d.ts +2 -0
- package/dist/src/daemon/session-selector.d.ts +7 -0
- package/dist/src/daemon/types.d.ts +3 -0
- package/dist/src/daemon-client.d.ts +2 -0
- package/dist/src/daemon.js +3 -3
- package/dist/src/utils/args.d.ts +15 -3
- package/dist/src/utils/cli-config.d.ts +9 -0
- package/dist/src/utils/cli-option-schema.d.ts +19 -0
- package/dist/src/utils/cli-options.d.ts +13 -0
- package/dist/src/utils/command-schema.d.ts +5 -0
- package/dist/src/utils/session-binding.d.ts +18 -0
- package/package.json +1 -1
- package/skills/agent-device/SKILL.md +36 -0
- package/skills/agent-device/references/remote-tenancy.md +11 -0
- package/skills/agent-device/references/session-management.md +36 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DaemonInstallSource, DaemonRequest, DaemonResponse, SessionRuntimeHints } from './daemon/types.ts';
|
|
1
|
+
import type { DaemonInstallSource, DaemonLockPolicy, DaemonRequest, DaemonResponse, SessionRuntimeHints } from './daemon/types.ts';
|
|
2
2
|
import type { DeviceKind, DeviceTarget, Platform, PlatformSelector } from './utils/device.ts';
|
|
3
3
|
import type { SnapshotNode } from './utils/snapshot.ts';
|
|
4
4
|
type DaemonTransportMode = 'auto' | 'socket' | 'http';
|
|
@@ -7,6 +7,8 @@ type SessionIsolationMode = 'none' | 'tenant';
|
|
|
7
7
|
export type AgentDeviceDaemonTransport = (req: Omit<DaemonRequest, 'token'>) => Promise<DaemonResponse>;
|
|
8
8
|
export type AgentDeviceClientConfig = {
|
|
9
9
|
session?: string;
|
|
10
|
+
lockPolicy?: DaemonLockPolicy;
|
|
11
|
+
lockPlatform?: PlatformSelector;
|
|
10
12
|
requestId?: string;
|
|
11
13
|
stateDir?: string;
|
|
12
14
|
daemonBaseUrl?: string;
|
|
@@ -20,7 +22,7 @@ export type AgentDeviceClientConfig = {
|
|
|
20
22
|
cwd?: string;
|
|
21
23
|
debug?: boolean;
|
|
22
24
|
};
|
|
23
|
-
export type AgentDeviceRequestOverrides = Pick<AgentDeviceClientConfig, 'session' | 'requestId' | 'tenant' | 'sessionIsolation' | 'runId' | 'leaseId' | 'cwd' | 'debug'>;
|
|
25
|
+
export type AgentDeviceRequestOverrides = Pick<AgentDeviceClientConfig, 'session' | 'lockPolicy' | 'lockPlatform' | 'requestId' | 'tenant' | 'sessionIsolation' | 'runId' | 'leaseId' | 'cwd' | 'debug'>;
|
|
24
26
|
export type AgentDeviceIdentifiers = {
|
|
25
27
|
session?: string;
|
|
26
28
|
deviceId?: string;
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
import type { CommandFlags } from '../core/dispatch.ts';
|
|
2
2
|
import type { SessionState } from './types.ts';
|
|
3
|
+
export type SessionSelectorConflictKey = 'platform' | 'target' | 'udid' | 'serial' | 'device' | 'iosSimulatorDeviceSet' | 'androidDeviceAllowlist';
|
|
4
|
+
export type SessionSelectorConflict = {
|
|
5
|
+
key: SessionSelectorConflictKey;
|
|
6
|
+
value: string;
|
|
7
|
+
};
|
|
3
8
|
export declare function assertSessionSelectorMatches(session: SessionState, flags?: CommandFlags): void;
|
|
9
|
+
export declare function listSessionSelectorConflicts(session: SessionState, flags?: CommandFlags): SessionSelectorConflict[];
|
|
10
|
+
export declare function formatSessionSelectorConflict(conflict: SessionSelectorConflict): string;
|
|
@@ -4,6 +4,7 @@ import type { DeviceInfo } from '../utils/device.ts';
|
|
|
4
4
|
import type { ExecResult } from '../utils/exec.ts';
|
|
5
5
|
import type { SnapshotState } from '../utils/snapshot.ts';
|
|
6
6
|
export type DaemonInstallSource = MaterializeInstallSource;
|
|
7
|
+
export type DaemonLockPolicy = 'reject' | 'strip';
|
|
7
8
|
export type DaemonRequest = {
|
|
8
9
|
token: string;
|
|
9
10
|
session: string;
|
|
@@ -27,6 +28,8 @@ export type DaemonRequest = {
|
|
|
27
28
|
retainMaterializedPaths?: boolean;
|
|
28
29
|
materializedPathRetentionMs?: number;
|
|
29
30
|
materializationId?: string;
|
|
31
|
+
lockPolicy?: DaemonLockPolicy;
|
|
32
|
+
lockPlatform?: 'ios' | 'android' | 'apple';
|
|
30
33
|
};
|
|
31
34
|
};
|
|
32
35
|
export type SessionRuntimeHints = {
|
|
@@ -6,6 +6,8 @@ export type OpenAppOptions = {
|
|
|
6
6
|
session?: string;
|
|
7
7
|
app?: string;
|
|
8
8
|
url?: string;
|
|
9
|
+
lockPolicy?: NonNullable<DaemonRequest['meta']>['lockPolicy'];
|
|
10
|
+
lockPlatform?: NonNullable<DaemonRequest['meta']>['lockPlatform'];
|
|
9
11
|
platform?: NonNullable<DaemonRequest['flags']>['platform'];
|
|
10
12
|
target?: NonNullable<DaemonRequest['flags']>['target'];
|
|
11
13
|
device?: NonNullable<DaemonRequest['flags']>['device'];
|
package/dist/src/daemon.js
CHANGED
|
@@ -33,9 +33,9 @@ ${t}`.toLowerCase().includes("timed out waiting for all destinations")?"Xcode de
|
|
|
33
33
|
</map>`)}function oS(e,t){let r=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return oN(e).replace(RegExp(`^\\s*<string name="${r}">[\\s\\S]*?<\\/string>\\n?`,"m"),"").replace(RegExp(`^\\s*<boolean name="${r}" value="(?:true|false)"\\s*\\/?>\\n?`,"m"),"")}function oD(e){let t=e?.trim();return t&&t.length>0?t:void 0}function oE(e){if("binary"!==rx(e))return;let t=rC(e);throw new h("INVALID_ARGS",t,{package:e,hint:t})}function ok(e){if(Number.isInteger(e)&&!(e<=0)&&!(e>65535))return e}function oL(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function oO(e,t){let r=`${e}
|
|
34
34
|
${t}`.toLowerCase();return["run-as: package not debuggable","run-as: permission denied","run-as: package is unknown","run-as: unknown package","is unknown","is not an application","could not set capabilities"].some(e=>r.includes(e))}function oM(e){if(0===e.length)return{selectorExpression:null,selectorTimeout:null};let t=e[e.length-1],r=/^\d+$/.test(t??""),a=nJ(r?e.slice(0,-1):e.slice());return!a||a.rest.length>0?{selectorExpression:null,selectorTimeout:null}:{selectorExpression:a.selectorExpression,selectorTimeout:r?t:null}}function ox(e){return!!e&&!Number.isNaN(Number(e))}let oC=ev(process.env.AGENT_DEVICE_INSTALL_SOURCE_RETAIN_TTL_MS,9e5,5e3),oR=new Map;async function oT(e){let t=await a.mkdtemp(i.join(C.tmpdir(),"agent-device-materialized-"));try{let r=await oF(e.installablePath,i.join(t,"installable")),a=e.archivePath?await oF(e.archivePath,i.join(t,"archive")):void 0,n=u.randomUUID(),o=e.ttlMs??oC,s=Date.now()+o,l=setTimeout(()=>{oP(n)},o);return oR.set(n,{rootPath:t,installablePath:r,archivePath:a,tenantId:e.tenantId,sessionName:e.sessionName,expiresAt:s,timer:l}),{materializationId:n,installablePath:r,...a?{archivePath:a}:{},expiresAt:new Date(s).toISOString()}}catch(e){throw await a.rm(t,{recursive:!0,force:!0}),e}}async function oP(e,t){let r=oR.get(e);if(!r)throw new h("INVALID_ARGS",`Materialized paths not found: ${e}`);if(r.tenantId&&r.tenantId!==t)throw new h("UNAUTHORIZED","Materialized paths belong to a different tenant");clearTimeout(r.timer),oR.delete(e),await a.rm(r.rootPath,{recursive:!0,force:!0})}async function o$(e){let t=Array.from(oR.entries()).filter(([,t])=>t.sessionName===e).map(([e])=>e);await Promise.all(t.map(async e=>{await oP(e)}))}async function oF(e,t){let r=await a.stat(e);await a.mkdir(t,{recursive:!0});let n=i.join(t,i.basename(e));return r.isDirectory()?await a.cp(e,n,{recursive:!0}):await a.copyFile(e,n),n}async function oU(e){var t;let r="ios"===(t=e.flags?.platform)||"android"===t?t:void 0;if(e.session){if(r&&e.session.device.platform!==r)throw new h("INVALID_ARGS",`install_from_source requested platform ${r}, but session is bound to ${e.session.device.platform}`);return await nS(e.session.device),e.session.device}if(!r)throw new h("INVALID_ARGS",'install_from_source requires platform "ios" or "android" when no session is provided');let a=await nv(e.flags??{});return await nS(a),a}async function oV(e){let{req:t,sessionName:r,sessionStore:a}=e,i=a.get(r);try{let e,n=function(e){let t=e.meta?.installSource;if(!t)throw new h("INVALID_ARGS","install_from_source requires a source payload");if("url"===t.kind){if(!t.url||0===t.url.trim().length)throw new h("INVALID_ARGS","install_from_source url source requires a non-empty url");return t}if(!t.path||0===t.path.trim().length)throw new h("INVALID_ARGS","install_from_source path source requires a non-empty path");return t}(t),o=function(e){let t=e.meta?.retainMaterializedPaths===!0,r=e.meta?.materializedPathRetentionMs;if(!t)return{enabled:!1};if(void 0!==r&&r<=0)throw new h("INVALID_ARGS","install_from_source retentionMs must be a positive integer");return{enabled:!0,ttlMs:r}}(t),s=await oU({session:i,flags:t.flags});if(!nb("install",s))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"install_from_source is not supported on this device"}};let l=eu(t.meta?.requestId);if("ios"===s.platform){let e,{installIosInstallablePath:d}=await Promise.resolve().then(()=>({installIosInstallablePath:iq})),{prepareIosInstallArtifact:u}=await Promise.resolve().then(()=>({prepareIosInstallArtifact:ig})),c=await u(n,{signal:l});try{if(o.enabled&&(e=await oT({archivePath:c.archivePath,installablePath:c.installablePath,tenantId:t.meta?.tenantId,sessionName:i?r:void 0,ttlMs:o.ttlMs})),await d(s,c.installablePath),!c.bundleId)throw new h("COMMAND_FAILED","Installed iOS app identity could not be resolved from the artifact");let n={...e?.archivePath?{archivePath:e.archivePath}:{},...e?{installablePath:e.installablePath}:{},bundleId:c.bundleId,...c.appName?{appName:c.appName}:{},launchTarget:c.bundleId,...e?{materializationId:e.materializationId,materializationExpiresAt:e.expiresAt}:{}};return i&&a.recordAction(i,{command:"install_source",positionals:[],flags:t.flags??{},result:n}),{ok:!0,data:n}}catch(r){throw e&&await oP(e.materializationId,t.meta?.tenantId).catch(()=>{}),r}finally{await c.cleanup()}}let{installAndroidInstallablePath:d}=await Promise.resolve().then(()=>({installAndroidInstallablePath:au})),{prepareAndroidInstallArtifact:u}=await Promise.resolve().then(()=>({prepareAndroidInstallArtifact:rJ})),c=await u(n,{signal:l});try{if(o.enabled&&(e=await oT({archivePath:c.archivePath,installablePath:c.installablePath,tenantId:t.meta?.tenantId,sessionName:i?r:void 0,ttlMs:o.ttlMs})),await d(s,c.installablePath),!c.packageName)throw new h("COMMAND_FAILED","Installed Android package identity could not be resolved from the artifact");let{inferAndroidAppName:n}=await Promise.resolve().then(()=>({inferAndroidAppName:r8})),l={...e?.archivePath?{archivePath:e.archivePath}:{},...e?{installablePath:e.installablePath}:{},packageName:c.packageName,appName:n(c.packageName),launchTarget:c.packageName,...e?{materializationId:e.materializationId,materializationExpiresAt:e.expiresAt}:{}};return i&&a.recordAction(i,{command:"install_source",positionals:[],flags:t.flags??{},result:l}),{ok:!0,data:l}}catch(r){throw e&&await oP(e.materializationId,t.meta?.tenantId).catch(()=>{}),r}finally{await c.cleanup()}}catch(e){return{ok:!1,error:A(e)}}}async function oG(e){let{req:t}=e;try{let e=t.meta?.materializationId?.trim();if(!e)throw new h("INVALID_ARGS","release_materialized_paths requires a materializationId");return await oP(e,t.meta?.tenantId),{ok:!0,data:{released:!0,materializationId:e}}}catch(e){return{ok:!1,error:A(e)}}}async function oj(e){let t,r,a,{deviceName:i,runtime:n,simulatorSetPath:o,reuseExisting:s,boot:l,ensureReady:d}=e;if("darwin"!==process.platform)throw new h("UNSUPPORTED_PLATFORM","ensure-simulator is only available on macOS");let u={simulatorSetPath:o??void 0};if(s){let e=await oB({deviceName:i,runtime:n,simctlOpts:u});e?(t=e.udid,r=e.runtime,a=!1):(t=(await oq({deviceName:i,runtime:n,simctlOpts:u})).udid,r=await oH(t,u),a=!0)}else t=(await oq({deviceName:i,runtime:n,simctlOpts:u})).udid,r=await oH(t,u),a=!0;let c=!1;if(l){let e={platform:"ios",id:t,name:i,kind:"simulator",target:"mobile",...o?{simulatorSetPath:o}:{}};await d(e),c=!0}return{udid:t,device:i,runtime:r,created:a,booted:c}}async function oB(e){let{deviceName:t,runtime:r,simctlOpts:a}=e,i=await c("xcrun",eE(["list","devices","-j"],a),{allowFailure:!0,timeoutMs:a9});if(0!==i.exitCode)return null;try{let e=JSON.parse(String(i.stdout??""));for(let[a,i]of Object.entries(e.devices??{}))if(!r||oW(a).includes(oW(r))){for(let e of i)if(e.isAvailable&&e.name.toLowerCase()===t.toLowerCase())return{udid:e.udid,runtime:a}}return null}catch{return null}}async function oq(e){let{deviceName:t,runtime:r,simctlOpts:a}=e,i=r?["create",t,t,r]:["create",t,t],n=await c("xcrun",eE(i,a),{allowFailure:!0});if(0!==n.exitCode)throw new h("COMMAND_FAILED","Failed to create iOS simulator",{deviceName:t,runtime:r,stdout:String(n.stdout??""),stderr:String(n.stderr??""),exitCode:n.exitCode,hint:"Ensure the device type and runtime identifiers are valid. Run `xcrun simctl list devicetypes` and `xcrun simctl list runtimes` to see available options."});let o=String(n.stdout??"").trim();if(!o)throw new h("COMMAND_FAILED","simctl create returned no UDID",{deviceName:t,runtime:r,stdout:String(n.stdout??""),stderr:String(n.stderr??"")});return{udid:o}}async function oH(e,t){let r=await c("xcrun",eE(["list","devices","-j"],t),{allowFailure:!0,timeoutMs:a9});if(0!==r.exitCode)return"";try{let t=JSON.parse(String(r.stdout??""));for(let[r,a]of Object.entries(t.devices??{}))if(a.some(t=>t.udid===e))return r;return""}catch{return""}}function oW(e){return e.toLowerCase().replace(/[._-]/g,"")}let oz='iOS appstate requires an active session on the target device. Run open first (for example: open --session sim --platform ios --device "<name>" <app>).',oJ=["platform","target","device","udid","serial","verbose","out"],oK=["platform","target","device","udid","serial","verbose","out"],oX=["path","start","stop","doctor","mark","clear"],oZ=`logs requires ${oX.slice(0,-1).join(", ")}, or ${oX.at(-1)}`,oY="Not implemented for this platform in this release.",oQ="open-command-roundtrip",o0=["platform","metroHost","metroPort","bundleUrl","launchUrl"],o1=ev(process.env.AGENT_DEVICE_IOS_SIMULATOR_POST_CLOSE_SETTLE_MS,300,0),o2=ev(process.env.AGENT_DEVICE_IOS_SIMULATOR_POST_OPEN_SETTLE_MS,300,0);function o3(e){return e?[e.metroHost,e.metroPort,e.bundleUrl,e.launchUrl].filter(e=>void 0!==e&&""!==e).length:0}function o4(e){let t=e?.trim();return t&&t.length>0?t:void 0}function o8(e,t){if(void 0!==e){if("string"!=typeof e)throw new h("INVALID_ARGS",`Invalid open runtime ${t}: expected string.`);return o4(e)}}function o6(e){if(void 0!==e){if(!Number.isInteger(e)||e<1||e>65535)throw new h("INVALID_ARGS",`Invalid runtime metroPort: ${String(e)}. Use an integer between 1 and 65535.`);return e}}function o5(e){try{return{ok:!0,data:function(e){let{req:t,sessionStore:r,sessionName:a,device:i}=e,n=r.getRuntimeHints(a),o=function(e){let{runtime:t,sessionName:r,platform:a}=e;if(void 0===t)return;if(!t||"object"!=typeof t||Array.isArray(t))throw new h("INVALID_ARGS","open runtime must be an object.");let i=Object.keys(t).find(e=>!o0.includes(e));if(i)throw new h("INVALID_ARGS",`Invalid open runtime field: ${i}. Supported fields are ${o0.join(", ")}.`);return{platform:function(e,t,r){if(void 0===e)return r;if("ios"!==e&&"android"!==e)throw new h("INVALID_ARGS",`Invalid open runtime platform: ${String(e)}. Use "ios" or "android".`);if(r&&e!==r)throw new h("INVALID_ARGS",`open runtime targets ${e}, but session "${t}" is bound to ${r}.`);return e}(t.platform,r,a),metroHost:o8(t.metroHost,"metroHost"),metroPort:function(e){if(void 0!==e){if("number"!=typeof e)throw new h("INVALID_ARGS","Invalid open runtime metroPort: expected integer.");return o6(e)}}(t.metroPort),bundleUrl:o8(t.bundleUrl,"bundleUrl"),launchUrl:o8(t.launchUrl,"launchUrl")}}({runtime:t.runtime,sessionName:a,platform:i.platform});return void 0===t.runtime?{runtime:function(e,t,r){let a=e.getRuntimeHints(t);if(a){if(a.platform&&r&&a.platform!==r.platform)throw new h("INVALID_ARGS",`Session runtime hints target ${a.platform}, but session "${t}" is bound to ${r.platform}. Clear the runtime hints or use a different session.`);return r?.platform&&a.platform!==r.platform?{...a,platform:r.platform}:a}}(r,a,i),previousRuntime:n,replacedStoredRuntime:!1}:{runtime:o&&o3(o)>0?o:void 0,previousRuntime:n,replacedStoredRuntime:!0}}(e)}}catch(t){let e=f(t);return{ok:!1,response:{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}}}async function o9(e){let{replacedStoredRuntime:t,previousRuntime:r,runtime:a,session:i,clearRuntimeHints:n}=e;!t||!i?.appBundleId||!of(r)||of(a)||await n({device:i.device,appId:i.appBundleId})}async function o7(e){var t,r,a,i,n,o;let{req:s,sessionName:l,sessionStore:d,logPath:u,device:c,dispatch:p,applyRuntimeHints:f,stopIosRunner:m,settleSimulator:h,openTarget:w,openPositionals:g,appBundleId:I,runtime:v,existingSession:A}=e,y=s.flags?.relaunch===!0,b=A?.trace?.outPath;if(y&&w){let e=I??w;await sc({device:c,closeTarget:e,stopIosRunner:m,dispatch:p,outFlag:s.flags?.out,context:{...nN(u,s.flags,I??A?.appBundleId,b)},settleSimulator:h})}await f({device:c,appId:I,runtime:v});let N=Date.now();await p(c,"open",g,s.flags?.out,{...nN(u,s.flags,I)}),await se({runtime:v,device:c,dispatch:p,req:s,logPath:u,appBundleId:I,traceLogPath:b,openPositionals:g});let _=w?(t=N,r=w,a=I,{durationMs:Math.max(0,Date.now()-t),measuredAt:new Date().toISOString(),method:oQ,appTarget:r,appBundleId:a}):void 0;await h(c,o2);let S=function(e){let{existingSession:t,sessionName:r,device:a,appBundleId:i,openTarget:n,saveScript:o}=e;return t?{...t,appBundleId:i,appName:n,recordSession:t.recordSession||o,snapshot:void 0}:{name:r,device:a,createdAt:Date.now(),appBundleId:i,appName:n,recordSession:o,actions:[]}}({existingSession:A,sessionName:l,device:c,appBundleId:I,openTarget:w,saveScript:!!s.flags?.saveScript});void 0!==s.runtime&&(i=d,n=l,(o=v)&&(0===o3(o)?i.clearRuntimeHints(n):i.setRuntimeHints(n,o)));let D=function(e){let{sessionName:t,appName:r,appBundleId:a,startup:i,device:n,runtime:o}=e,s={session:t};return r&&(s.appName=r),a&&(s.appBundleId=a),i&&(s.startup=i),o&&o3(o)>0&&(s.runtime=o),n&&(s.platform=n.platform,s.target=n.target??"mobile",s.device=n.name,s.id=n.id,s.kind=n.kind,"android"===n.platform&&(s.serial=n.id)),n?.platform==="ios"&&(s.device_udid=n.id,s.ios_simulator_device_set=n.simulatorSetPath??null),s}({sessionName:l,appName:w,appBundleId:I,startup:_,device:c,runtime:v});return!function(e){let{req:t,sessionStore:r,session:a,sessionName:i,runtime:n}=e;if(void 0!==t.runtime){if(n)return r.recordAction(a,{command:"runtime",positionals:["set"],flags:{...t.flags??{},platform:a.device.platform,metroHost:n.metroHost,metroPort:n.metroPort,bundleUrl:n.bundleUrl,launchUrl:n.launchUrl},result:{session:i,configured:!0,runtime:n}});r.recordAction(a,{command:"runtime",positionals:["clear"],flags:t.flags??{},result:{session:i,cleared:!0}})}}({req:s,sessionStore:d,session:S,sessionName:l,runtime:v}),d.recordAction(S,{command:"open",positionals:g,flags:s.flags??{},result:D}),d.set(l,S),{ok:!0,data:D}}async function se(e){let{runtime:t,device:r,dispatch:a,req:i,logPath:n,appBundleId:o,traceLogPath:s,openPositionals:l}=e,d=t?.launchUrl;if(!d||0===l.length||l.length>1)return;let u=l[0]?.trim();!u||rd(u)||await a(r,"open",[d],i.flags?.out,{...nN(n,i.flags,o,s)})}let st=["dump","log"],sr=`network requires ${st.join(" or ")}`,sa=["summary","headers","body","all"],si=`network include mode must be one of: ${sa.join(", ")}`;function sn(e,t,r){return t||so(r)?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} requires an active session or an explicit device selector (e.g. --platform ios).`}}}function so(e){return!!(e?.platform||e?.target||e?.device||e?.udid||e?.serial)}function ss(e){return"ios"===e.platform&&"simulator"===e.kind}async function sl(e,t){ss(e)&&!(t<=0)&&await new Promise(e=>setTimeout(e,t))}async function sd(e){let t=await c("adb",["-s",e.id,"emu","kill"],{allowFailure:!0,timeoutMs:15e3});return{success:0===t.exitCode,exitCode:t.exitCode,stdout:String(t.stdout??""),stderr:String(t.stderr??"")}}async function su(e){let{device:t,shutdownRequested:r,shutdownSimulator:a,shutdownAndroidEmulator:i}=e;if(r&&(ss(t)||"android"===t.platform&&"emulator"===t.kind))try{return ss(t)?await a(t):await i(t)}catch(t){let e=A(t);return{success:!1,exitCode:-1,stdout:"",stderr:e.message,error:e}}}async function sc(e){let{device:t,closeTarget:r,stopIosRunner:a,dispatch:i,outFlag:n,context:o,settleSimulator:s}=e;"ios"===t.platform&&await a(t.id),await i(t,"close",[r],n,o),await s(t,o1)}async function sp(e){let t=so(e.flags)||!e.session?await e.resolveTargetDeviceFn(e.flags??{}):e.session.device;return!1!==e.ensureReady&&await e.ensureReadyFn(t),t}function sf(e){let t=e.flags?.device?.trim();return t||(e.resolvedDevice?.platform==="android"&&"emulator"===e.resolvedDevice.kind?e.resolvedDevice.name:e.sessionDevice?.platform==="android"&&"emulator"===e.sessionDevice.kind?e.sessionDevice.name:void 0)}let sm=async({avdName:e,serial:t,headless:r})=>{let{ensureAndroidEmulatorBooted:a}=await Promise.resolve().then(()=>({ensureAndroidEmulatorBooted:rk}));return await a({avdName:e,serial:t,headless:r})};async function sh(e){let{req:t,sessionName:r,logPath:a,sessionStore:i,ensureReady:n,resolveDevice:o,dispatch:s,command:l,positionals:d,recordPositionals:u,deriveNextSession:c}=e,p=i.get(r),f=t.flags??{},m=sn(l,p,f);if(m)return m;let h=await sp({session:p,flags:f,ensureReadyFn:n,resolveTargetDeviceFn:o,ensureReady:!0});if(!nb(l,h))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:`${l} is not supported on this device`}};let w=await s(h,l,d,t.flags?.out,{...nN(a,t.flags,p?.appBundleId,p?.trace?.outPath)});if(p){let e=c?await c(p,w,h):p;i.recordAction(e,{command:l,positionals:u??d,flags:t.flags??{},result:w??{}}),e!==p&&i.set(r,e)}return{ok:!0,data:w??{}}}let sw={ios:async(e,t,r)=>{let{reinstallIosApp:a}=await Promise.resolve().then(()=>({reinstallIosApp:iB}));return await a(e,t,r)},android:async(e,t,r)=>{let{reinstallAndroidApp:a}=await Promise.resolve().then(()=>({reinstallAndroidApp:ap}));return await a(e,t,r)}},sg={ios:async(e,t,r)=>{let{installIosApp:a}=await Promise.resolve().then(()=>({installIosApp:ij})),i=await a(e,r,{appIdentifierHint:t});return{bundleId:i.bundleId,appName:i.appName,launchTarget:i.launchTarget}},android:async(e,t,r)=>{let{installAndroidApp:a}=await Promise.resolve().then(()=>({installAndroidApp:ac})),i=await a(e,r);return{package:i.packageName,appName:i.appName,launchTarget:i.launchTarget}}};async function sI(e){let{req:t,command:r,sessionName:a,sessionStore:i,ensureReady:n,resolveDevice:o,deployOps:s}=e,l=i.get(a),d=t.flags??{},u=sn(r,l,d);if(u)return u;let c=t.positionals?.[0]?.trim(),p=t.positionals?.[1]?.trim();if(!c||!p)return{ok:!1,error:{code:"INVALID_ARGS",message:`${r} requires: ${r} <app> <path-to-app-binary>`}};let f=t.meta?.uploadedArtifactId;try{let e,a=f?function(e,t){let r=tG.get(e);if(!r)throw new h("INVALID_ARGS",`Uploaded artifact not found: ${e}`);if(r.tenantId&&r.tenantId!==t)throw new h("UNAUTHORIZED","Uploaded artifact belongs to a different tenant");return clearTimeout(r.timer),r.artifactPath}(f,t.meta?.tenantId):tb.expandHome(p);if(!M.existsSync(a))return{ok:!1,error:{code:"INVALID_ARGS",message:`App binary not found: ${a}`}};let u=await sp({session:l,flags:d,ensureReadyFn:n,resolveTargetDeviceFn:o,ensureReady:!1});if(!nb(r,u))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:`${r} is not supported on this device`}};if("ios"===u.platform){let t=await s.ios(u,c,a),r=t.bundleId;e=r?{app:c,appPath:a,platform:"ios",appId:r,bundleId:r,appName:t.appName,launchTarget:t.launchTarget}:{app:c,appPath:a,platform:"ios",appName:t.appName,launchTarget:t.launchTarget}}else{let t=await s.android(u,c,a),r=t.package;e=r?{app:c,appPath:a,platform:"android",appId:r,package:r,packageName:r,appName:t.appName,launchTarget:t.launchTarget}:{app:c,appPath:a,platform:"android",appName:t.appName,launchTarget:t.launchTarget}}return l&&i.recordAction(l,{command:r,positionals:t.positionals??[],flags:t.flags??{},result:e}),{ok:!0,data:e}}finally{f&&tj(f)}}async function sv(e,t,r){if("ios"===e.platform&&t)return rd(t)?"device"===e.kind?ru(r,t):void 0:await sA(e,t)}async function sA(e,t){try{let{resolveIosApp:r}=await Promise.resolve().then(()=>({resolveIosApp:i$}));return await r(e,t)}catch{return}}async function sy(e,t){if(!("android"!==e.platform||!t||rd(t)))try{let{resolveAndroidApp:r}=await Promise.resolve().then(()=>({resolveAndroidApp:r0})),a=await r(e,t);return"package"===a.type?a.value:void 0}catch{return}}async function sb(e,t,r,a){return await sv(e,t,r)??await a(e,t)??("android"===e.platform&&t&&rd(t)?r:void 0)}async function sN(e){let{req:t,sessionName:r,sessionStore:a,ensureReady:i,resolveDevice:n}=e,o=a.get(r),s=t.flags??{},l=eW(s.platform);if(!o&&"string"==typeof s?.session&&s.session.trim().length>0)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"ios"===l?`No active session "${r}". Run open with --session ${r} first.`:`No active session "${r}". Run open with --session ${r} first, or omit --session to query by device selector.`}};let d=sn("appstate",o,s);if(d)return d;let u=o?.device.platform==="ios"&&function(e,t){if(!t)return!1;if(!so(e))return!0;let r=eW(e?.platform);return!(r&&r!==t.device.platform||e?.target&&e.target!==(t.device.target??"mobile")||e?.udid&&e.udid!==t.device.id||e?.serial&&e.serial!==t.device.id)&&(!e?.device||e.device.trim().toLowerCase()===t.device.name.trim().toLowerCase())}(s,o);if("ios"===l&&!u)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:oz}};if(u){let e=o.appName??o.appBundleId;return o.appName||o.appBundleId?{ok:!0,data:{platform:"ios",appName:e??"unknown",appBundleId:o.appBundleId,source:"session",device_udid:o.device.id,ios_simulator_device_set:o.device.simulatorSetPath??null}}:{ok:!1,error:{code:"COMMAND_FAILED",message:"No foreground app is tracked for this iOS session. Open an app in the session, then retry appstate."}}}let c=await sp({session:o,flags:s,ensureReadyFn:i,resolveTargetDeviceFn:n,ensureReady:!0});if("ios"===c.platform)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:oz}};let{getAndroidAppState:p}=await Promise.resolve().then(()=>({getAndroidAppState:r6})),f=await p(c);return{ok:!0,data:{platform:"android",package:f.package,activity:f.activity}}}async function s_(e){let{req:t,sessionName:r,logPath:a,sessionStore:i,ensureReady:n,resolveDevice:o,dispatch:s}=e,l=i.get(r),d=t.flags??{},u=sn("clipboard",l,d);if(u)return u;let c=(t.positionals?.[0]??"").toLowerCase();if("read"!==c&&"write"!==c)return{ok:!1,error:{code:"INVALID_ARGS",message:"clipboard requires a subcommand: read or write"}};let p=await sp({session:l,flags:d,ensureReadyFn:n,resolveTargetDeviceFn:o,ensureReady:!0});if(!nb("clipboard",p))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"clipboard is not supported on this device"}};let f=await s(p,"clipboard",t.positionals??[],t.flags?.out,{...nN(a,t.flags,l?.appBundleId,l?.trace?.outPath)});return l&&i.recordAction(l,{command:t.command,positionals:t.positionals??[],flags:t.flags??{},result:f??{}}),{ok:!0,data:{platform:p.platform,...f??{}}}}async function sS(e){var t,r;let{req:a,sessionName:i,logPath:n,sessionStore:o,invoke:s,dispatch:l,ensureReady:d,resolveTargetDevice:u,installOps:c=sg,reinstallOps:p=sw,stopIosRunner:m,appLogOps:w={start:tT,stop:tP},ensureAndroidEmulatorBoot:g=sm,resolveAndroidPackageForOpen:I=sy,applyRuntimeHints:v=oh,clearRuntimeHints:y=ow,settleSimulator:b,shutdownSimulator:N,shutdownAndroidEmulator:_}=e,S=l??nA,D=d??nS,E=u??nv,k=m??tt,L=b??sl,O=a.command;if("session_list"===O)return{ok:!0,data:{sessions:o.toArray().map(e=>({name:e.name,platform:e.device.platform,target:e.device.target??"mobile",device:e.device.name,id:e.device.id,createdAt:e.createdAt,..."ios"===e.device.platform&&{device_udid:e.device.id,ios_simulator_device_set:e.device.simulatorSetPath??null}}))}};if("runtime"===O){let e=(a.positionals?.[0]??"show").toLowerCase(),n=o.get(i),s=o.getRuntimeHints(i);if(!["set","show","clear"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"runtime requires set, show, or clear"}};if("clear"===e){of(s)&&n?.appBundleId&&await y({device:n.device,appId:n.appBundleId});let e=o.clearRuntimeHints(i);return{ok:!0,data:{session:i,cleared:e}}}if("show"===e)return{ok:!0,data:{session:i,configured:!!s,runtime:s}};let l=eW(a.flags?.platform)??s?.platform??n?.device.platform;if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"runtime set requires --platform when the session has not been opened yet."}};if(n&&n.device.platform!==l)return{ok:!1,error:{code:"INVALID_ARGS",message:`runtime set targets ${l}, but session "${i}" is already bound to ${n.device.platform}.`}};let d={platform:(t=a.flags,r={platform:l,metroHost:o4(t?.metroHost),metroPort:o6(t?.metroPort),bundleUrl:o4(t?.bundleUrl),launchUrl:o4(t?.launchUrl)}).platform??s?.platform,metroHost:r.metroHost??s?.metroHost,metroPort:r.metroPort??s?.metroPort,bundleUrl:r.bundleUrl??s?.bundleUrl,launchUrl:r.launchUrl??s?.launchUrl};return 0===o3(d)?{ok:!1,error:{code:"INVALID_ARGS",message:"runtime set requires at least one hint such as --metro-host, --metro-port, --bundle-url, or --launch-url."}}:(o.setRuntimeHints(i,d),{ok:!0,data:{session:i,configured:!0,runtime:d}})}if("ensure-simulator"===O)try{let e=a.flags??{},t=e.device,r=e.runtime,i=e_(e.iosSimulatorDeviceSet);if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:"ensure-simulator requires --device <name>"}};let n=!0===e.boot,o=!1!==e.reuseExisting,s=await oj({deviceName:t,runtime:r,simulatorSetPath:i,reuseExisting:o,boot:n,ensureReady:D});return{ok:!0,data:{udid:s.udid,device:s.device,runtime:s.runtime,ios_simulator_device_set:i??null,created:s.created,booted:s.booted}}}catch(t){let e=f(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}if("devices"===O)try{let e=[],t=e_(a.flags?.iosSimulatorDeviceSet),r=eD(a.flags?.androidDeviceAllowlist),i=eW(a.flags?.platform);if("android"===i){let{listAndroidDevices:t}=await Promise.resolve().then(()=>({listAndroidDevices:rb}));e.push(...await t({serialAllowlist:r}))}else if("ios"===i){let{listIosDevices:r}=await Promise.resolve().then(()=>({listIosDevices:ng}));e.push(...await r({simulatorSetPath:t}))}else{let{listAndroidDevices:a}=await Promise.resolve().then(()=>({listAndroidDevices:rb})),{listIosDevices:i}=await Promise.resolve().then(()=>({listIosDevices:ng}));try{e.push(...await a({serialAllowlist:r}))}catch{}try{e.push(...await i({simulatorSetPath:t}))}catch{}}let n=(a.flags?.target?e.filter(e=>(e.target??"mobile")===a.flags?.target):e).map(({simulatorSetPath:e,...t})=>t);return{ok:!0,data:{devices:n}}}catch(t){let e=f(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}if("apps"===O){let e=o.get(i),t=a.flags??{},r=sn(O,e,t);if(r)return r;let n=await sp({session:e,flags:t,ensureReadyFn:D,resolveTargetDeviceFn:E,ensureReady:!0});if(!nb("apps",n))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"apps is not supported on this device"}};let s=a.flags?.appsFilter??"all";if("ios"===n.platform){let{listIosApps:e}=await Promise.resolve().then(()=>({listIosApps:iK}));return{ok:!0,data:{apps:(await e(n,s)).map(e=>e.name&&e.name!==e.bundleId?`${e.name} (${e.bundleId})`:e.bundleId)}}}let{listAndroidApps:l}=await Promise.resolve().then(()=>({listAndroidApps:r1}));return{ok:!0,data:{apps:(await l(n,s)).map(e=>e.name&&e.name!==e.package?`${e.name} (${e.package})`:e.package)}}}if("boot"===O){let e,t=o.get(i),r=a.flags??{},n=sn(O,t,r);if(n)return n;let s="android"===(eW(r.platform)??t?.device.platform),l=!0===r.headless;if(l&&!s)return{ok:!1,error:{code:"INVALID_ARGS",message:"boot --headless is supported only for Android emulators."}};let d=sf({flags:r,sessionDevice:t?.device}),u=s&&!!d,c=!1;try{e=await sp({session:t,flags:r,ensureReadyFn:D,resolveTargetDeviceFn:E,ensureReady:!1})}catch(a){let t=f(a);if(s&&l&&!d&&"DEVICE_NOT_FOUND"===t.code)return{ok:!1,error:{code:"INVALID_ARGS",message:"boot --headless requires --device <avd-name> (or an Android emulator session target)."}};if(!u||"DEVICE_NOT_FOUND"!==t.code||!d)throw a;e=await g({avdName:d,serial:r.serial,headless:l}),c=!0}if(r.target&&(e.target??"mobile")!==r.target)return{ok:!1,error:{code:"DEVICE_NOT_FOUND",message:`No ${e.platform} device found matching --target ${r.target}.`}};if(s&&l){if("android"!==e.platform||"emulator"!==e.kind)return{ok:!1,error:{code:"INVALID_ARGS",message:"boot --headless is supported only for Android emulators."}};if(!c){let a=sf({flags:r,sessionDevice:t?.device,resolvedDevice:e});if(!a)return{ok:!1,error:{code:"INVALID_ARGS",message:"boot --headless requires --device <avd-name> (or an Android emulator session target)."}};e=await g({avdName:a,serial:r.serial,headless:!0})}await D(e)}else("android"!==e.platform||!0!==e.booted)&&await D(e);return nb("boot",e)?{ok:!0,data:{platform:e.platform,target:e.target??"mobile",device:e.name,id:e.id,kind:e.kind,booted:!0}}:{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"boot is not supported on this device"}}}if("appstate"===O)return await sN({req:a,sessionName:i,sessionStore:o,ensureReady:D,resolveDevice:E});if("clipboard"===O)return await s_({req:a,sessionName:i,logPath:n,sessionStore:o,ensureReady:D,resolveDevice:E,dispatch:S});if("keyboard"===O)return await sh({req:a,sessionName:i,logPath:n,sessionStore:o,ensureReady:D,resolveDevice:E,dispatch:S,command:"keyboard",positionals:a.positionals??[]});if("perf"===O){let e,t,r,a=o.get(i);return a?{ok:!0,data:(r=(t=(e=function(e){let t=[];for(let r of e){if("open"!==r.command)continue;let e=r.result?.startup;e&&"object"==typeof e&&"number"==typeof e.durationMs&&Number.isFinite(e.durationMs)&&"string"==typeof e.measuredAt&&0!==e.measuredAt.trim().length&&e.method===oQ&&t.push({durationMs:Math.max(0,Math.round(e.durationMs)),measuredAt:e.measuredAt,method:oQ,appTarget:"string"==typeof e.appTarget&&e.appTarget.length>0?e.appTarget:void 0,appBundleId:"string"==typeof e.appBundleId&&e.appBundleId.length>0?e.appBundleId:void 0})}return t.slice(-20)}(a.actions)).at(-1))?{available:!0,lastDurationMs:t.durationMs,lastMeasuredAt:t.measuredAt,method:oQ,sampleCount:e.length,samples:e}:{available:!1,reason:"No startup sample captured yet. Run open <app|url> in this session first.",method:oQ},{session:a.name,platform:a.device.platform,device:a.device.name,deviceId:a.device.id,metrics:{startup:r,fps:{available:!1,reason:oY},memory:{available:!1,reason:oY},cpu:{available:!1,reason:oY}},sampling:{startup:{method:oQ,description:"Elapsed wall-clock time around dispatching the open command for the active session app target.",unit:"ms"}}})}:{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"perf requires an active session. Run open first."}}}if("install"===O||"reinstall"===O)return await sI({req:a,command:O,sessionName:i,sessionStore:o,ensureReady:D,resolveDevice:E,deployOps:"install"===O?c:p});if("install_source"===O)return await oV({req:a,sessionName:i,sessionStore:o});if("release_materialized_paths"===O)return await oG({req:a});if("push"===O){let e,t=a.positionals?.[0]?.trim(),r=a.positionals?.[1]?.trim();if(!t||!r)return{ok:!1,error:{code:"INVALID_ARGS",message:"push requires <bundle|package> <payload.json|inline-json>"}};let s="file"===(e=nt(r,{subject:"Push payload",cwd:a.meta?.cwd,expandPath:(e,t)=>tb.expandHome(e,t)})).kind?e.path:e.text;return await sh({req:a,sessionName:i,logPath:n,sessionStore:o,ensureReady:D,resolveDevice:E,dispatch:S,command:"push",positionals:[t,s],recordPositionals:[t,r]})}if("trigger-app-event"===O)return await sh({req:a,sessionName:i,logPath:n,sessionStore:o,ensureReady:D,resolveDevice:E,dispatch:S,command:"trigger-app-event",positionals:a.positionals??[],deriveNextSession:async(e,t)=>{let r="string"==typeof t?.eventUrl?t.eventUrl:void 0,a=r?await sb(e.device,r,e.appBundleId,I)??e.appBundleId:e.appBundleId;return{...e,appBundleId:a}}});if("open"===O){let e=a.flags?.relaunch===!0;if(o.has(i)){let t=o.get(i),r=a.positionals?.[0],s=r??(e?t?.appName:void 0);if(!t||!s)return e?{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch requires an app name or an active session app."}}:{ok:!1,error:{code:"INVALID_ARGS",message:"Session already active. Close it first or pass a new --session name."}};if(e&&rd(s))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};if(e&&"android"===t.device.platform&&"binary"===rx(s))return{ok:!1,error:{code:"INVALID_ARGS",message:rC(s)}};await D(t.device);let l=await sb(t.device,s,t.appBundleId,I),d=o5({req:a,sessionStore:o,sessionName:i,device:t.device});if(!d.ok)return d.response;let{runtime:u,previousRuntime:c,replacedStoredRuntime:p}=d.data;await o9({replacedStoredRuntime:p,previousRuntime:c,runtime:u,session:t,clearRuntimeHints:y});let f=r?a.positionals??[]:[s];return await o7({req:a,sessionName:i,sessionStore:o,logPath:n,device:t.device,dispatch:S,applyRuntimeHints:v,stopIosRunner:k,settleSimulator:L,openTarget:s,openPositionals:f,appBundleId:l,runtime:u,existingSession:t})}let t=a.positionals?.[0];if(e&&!t)return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch requires an app argument."}};if(e&&t&&rd(t))return{ok:!1,error:{code:"INVALID_ARGS",message:"open --relaunch does not support URL targets."}};let r=await E(a.flags??{});if(e&&"android"===r.platform&&t&&"binary"===rx(t))return{ok:!1,error:{code:"INVALID_ARGS",message:rC(t)}};let s=o.toArray().find(e=>e.device.id===r.id);if(s)return{ok:!1,error:{code:"DEVICE_IN_USE",message:`Device is already in use by session "${s.name}".`,details:{session:s.name,deviceId:r.id,deviceName:r.name}}};await D(r);let l=await sb(r,t,void 0,I),d=o5({req:a,sessionStore:o,sessionName:i,device:r});if(!d.ok)return d.response;let{runtime:u}=d.data;return await o7({req:a,sessionName:i,sessionStore:o,logPath:n,device:r,dispatch:S,applyRuntimeHints:v,stopIosRunner:k,settleSimulator:L,openTarget:t,openPositionals:a.positionals??[],appBundleId:l,runtime:u})}if("replay"===O){let e=a.positionals?.[0];if(!e)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay requires a path"}};try{let t=tb.expandHome(e,a.meta?.cwd),r=M.readFileSync(t,"utf8"),l=r.trimStart()[0];if("{"===l||"["===l)return{ok:!1,error:{code:"INVALID_ARGS",message:"replay accepts .ad script files. JSON replay payloads are no longer supported."}};let d=function(e){let t=[];for(let r of e.split(/\r?\n/)){let e=function(e){let t=e.trim();if(0===t.length||t.startsWith("#"))return null;let r=function(e){let t=[],r=0;for(;r<e.length;){for(;r<e.length&&/\s/.test(e[r]);)r+=1;if(r>=e.length)break;if('"'===e[r]){let a=r+1,i=!1;for(;a<e.length;){let t=e[a];if('"'===t&&!i)break;i="\\"===t&&!i,"\\"!==t&&(i=!1),a+=1}if(a>=e.length)throw new h("INVALID_ARGS",`Invalid replay script line: ${e}`);let n=e.slice(r,a+1);t.push(JSON.parse(n)),r=a+1;continue}let a=r;for(;a<e.length&&!/\s/.test(e[a]);)a+=1;t.push(e.slice(r,a)),r=a}return t}(t);if(0===r.length)return null;let[a,...i]=r;if("context"===a)return null;let n={ts:Date.now(),command:a,positionals:[],flags:{}};if("snapshot"===a){n.positionals=[];for(let e=0;e<i.length;e+=1){let t=i[e];if("-i"===t){n.flags.snapshotInteractiveOnly=!0;continue}if("-c"===t){n.flags.snapshotCompact=!0;continue}if("--raw"===t){n.flags.snapshotRaw=!0;continue}if(("-d"===t||"--depth"===t)&&e+1<i.length){let t=Number(i[e+1]);Number.isFinite(t)&&t>=0&&(n.flags.snapshotDepth=Math.floor(t)),e+=1;continue}if(("-s"===t||"--scope"===t)&&e+1<i.length){n.flags.snapshotScope=i[e+1],e+=1;continue}if("--backend"===t&&e+1<i.length){e+=1;continue}}return n}if("open"===a){n.positionals=[];for(let e=0;e<i.length;e+=1){let t=i[e];if("--relaunch"===t){n.flags.relaunch=!0;continue}n.positionals.push(t)}return n}if("runtime"===a){let e=function(e){let t=[],r={};for(let a=0;a<e.length;a+=1){let i=e[a];if("--platform"===i&&a+1<e.length){let t=e[a+1];("ios"===t||"android"===t)&&(r.platform=t),a+=1;continue}if("--metro-host"===i&&a+1<e.length){r.metroHost=e[a+1],a+=1;continue}if("--metro-port"===i&&a+1<e.length){let t=ty(e[a+1]);null!==t&&(r.metroPort=t),a+=1;continue}if("--bundle-url"===i&&a+1<e.length){r.bundleUrl=e[a+1],a+=1;continue}if("--launch-url"===i&&a+1<e.length){r.launchUrl=e[a+1],a+=1;continue}t.push(i)}return{positionals:t,flags:r}}(i);return n.positionals=e.positionals,Object.assign(n.flags,e.flags),n}if(th(a)){let e=tA(a,i);if(Object.assign(n.flags,e.flags),0===e.positionals.length)return n;let t=e.positionals[0];if(t.startsWith("@"))return n.positionals=[t],e.positionals[1]&&(n.result={refLabel:e.positionals[1]}),n;let r=e.positionals[0],o=e.positionals[1];return ox(r)&&ox(o)&&e.positionals.length>=2?n.positionals=[r,o]:n.positionals=[e.positionals.join(" ")],n}if("fill"===a){if(i.length<2)return n.positionals=i,n;let e=i[0];return e.startsWith("@")?(i.length>=3?(n.positionals=[e,i.slice(2).join(" ")],n.result={refLabel:i[1]}):n.positionals=[e,i[1]],n):(n.positionals=[e,i.slice(1).join(" ")],n)}if("get"===a){if(i.length<2)return n.positionals=i,n;let e=i[0],t=i[1];return t.startsWith("@")?(n.positionals=[e,t],i[2]&&(n.result={refLabel:i[2]})):n.positionals=[e,i.slice(1).join(" ")],n}if("swipe"===a){let e=tA(a,i);return Object.assign(n.flags,e.flags),n.positionals=e.positionals,n}return n.positionals=i,n}(r);e&&t.push(e)}return t}(r),u=a.flags?.replayUpdate===!0,c=0;for(let e=0;e<d.length;e+=1){let r=d[e];if(!r||"replay"===r.command)continue;let l=await s({token:a.token,session:i,command:r.command,positionals:r.positionals??[],flags:sL(a.flags,r.flags),meta:a.meta});if(l.ok)continue;if(!u)return sk(l,r,e,t);let p=await sO({action:r,sessionName:i,logPath:n,sessionStore:o,dispatch:S});if(!p)return sk(l,r,e,t);if(d[e]=p,!(l=await s({token:a.token,session:i,command:p.command,positionals:p.positionals??[],flags:sL(a.flags,p.flags),meta:a.meta})).ok)return sk(l,p,e,t);c+=1}if(u&&c>0){let e=o.get(i);!function(e,t,r){let a=[];if(r){let e=r.device.name.replace(/"/g,'\\"'),t=r.device.kind?` kind=${r.device.kind}`:"",i=r.device.target?` target=${r.device.target}`:"";a.push(`context platform=${r.device.platform}${i} device="${e}"${t} theme=unknown`)}for(let e of t)a.push(function(e){let t=[e.command];if("snapshot"===e.command)return e.flags?.snapshotInteractiveOnly&&t.push("-i"),e.flags?.snapshotCompact&&t.push("-c"),"number"==typeof e.flags?.snapshotDepth&&t.push("-d",String(e.flags.snapshotDepth)),e.flags?.snapshotScope&&t.push("-s",tw(e.flags.snapshotScope)),e.flags?.snapshotRaw&&t.push("--raw"),t.join(" ");if("open"===e.command){for(let r of e.positionals??[])t.push(tw(r));return e.flags?.relaunch&&t.push("--relaunch"),t.join(" ")}if("runtime"===e.command){for(let r of e.positionals??[])t.push(tg(r));return tv(t,e.flags),t.join(" ")}for(let r of e.positionals??[])t.push(tw(r));return tI(t,e),t.join(" ")}(e));let i=`${a.join("\n")}
|
|
35
35
|
`,n=`${e}.tmp-${process.pid}-${Date.now()}`;M.writeFileSync(n,i),M.renameSync(n,e)}(t,d,e)}return{ok:!0,data:{replayed:d.length,healed:c,session:i}}}catch(t){let e=f(t);return{ok:!1,error:{code:e.code,message:e.message}}}}if("logs"===O){let e=o.get(i);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"logs requires an active session"}};let t=(a.positionals?.[0]??"path").toLowerCase(),r=!!a.flags?.restart;if(!oX.includes(t))return{ok:!1,error:{code:"INVALID_ARGS",message:oZ}};if(r&&"clear"!==t)return{ok:!1,error:{code:"INVALID_ARGS",message:"logs --restart is only supported with logs clear"}};if("path"===t){let t=o.resolveAppLogPath(i),r=function(e){if(!M.existsSync(e))return{exists:!1,sizeBytes:0};let t=M.statSync(e);return{exists:!0,sizeBytes:t.size,modifiedAt:t.mtime.toISOString()}}(t),a=e.appLog?.backend??("ios"===e.device.platform?"device"===e.device.kind?"ios-device":"ios-simulator":"android");return{ok:!0,data:{path:t,active:!!e.appLog,state:e.appLog?.getState()??"inactive",backend:a,sizeBytes:r.sizeBytes,modifiedAt:r.modifiedAt,startedAt:e.appLog?.startedAt?new Date(e.appLog.startedAt).toISOString():void 0,hint:'Grep the file for token-efficient debugging, e.g. grep -n "Error\\|Exception" <path>'}}}if("doctor"===t){let t=o.resolveAppLogPath(i),r=await t$(e.device,e.appBundleId);return{ok:!0,data:{path:t,active:!!e.appLog,state:e.appLog?.getState()??"inactive",checks:r.checks,notes:r.notes}}}if("mark"===t){let e,t=a.positionals?.slice(1).join(" ")??"",r=o.resolveAppLogPath(i);return tR(r),e=`[agent-device][mark][${new Date().toISOString()}] ${t.trim()||"marker"}
|
|
36
|
-
`,M.appendFileSync(r,e,"utf8"),{ok:!0,data:{path:r,marked:!0}}}if("clear"===t){if(e.appLog&&!r)return{ok:!1,error:{code:"INVALID_ARGS",message:"logs clear requires logs to be stopped first; run logs stop"}};if(r){if(!e.appBundleId)return{ok:!1,error:{code:"INVALID_ARGS",message:"logs clear --restart requires an app session; run open <app> first"}};if(!nb("logs",e.device))return{ok:!1,error:A(new h("UNSUPPORTED_OPERATION","logs is not supported on this device"))}}let t=o.resolveAppLogPath(i);if(r){e.appLog&&await w.stop(e.appLog);let r=tF(t),a=o.resolveAppLogPidPath(i);try{let n=await w.start(e.device,e.appBundleId,t,a),s={...e,appLog:{platform:e.device.platform,backend:n.backend,outPath:t,startedAt:n.startedAt,getState:n.getState,stop:n.stop,wait:n.wait}};return o.set(i,s),{ok:!0,data:{...r,restarted:!0}}}catch(r){let t=A(r);return o.set(i,{...e,appLog:void 0}),{ok:!1,error:t}}}return{ok:!0,data:tF(t)}}if("start"===t){if(e.appLog)return{ok:!1,error:{code:"INVALID_ARGS",message:"app log already streaming; run logs stop first"}};if(!e.appBundleId)return{ok:!1,error:{code:"INVALID_ARGS",message:"logs start requires an app session; run open <app> first"}};if(!nb("logs",e.device))return{ok:!1,error:A(new h("UNSUPPORTED_OPERATION","logs is not supported on this device"))};let t=o.resolveAppLogPath(i),r=o.resolveAppLogPidPath(i);try{let a=await w.start(e.device,e.appBundleId,t,r),n={...e,appLog:{platform:e.device.platform,backend:a.backend,outPath:t,startedAt:a.startedAt,getState:a.getState,stop:a.stop,wait:a.wait}};return o.set(i,n),{ok:!0,data:{path:t,started:!0}}}catch(e){return{ok:!1,error:A(e)}}}if("stop"===t){if(!e.appLog)return{ok:!1,error:{code:"INVALID_ARGS",message:"no app log stream active"}};let t=e.appLog.outPath;return await w.stop(e.appLog),o.set(i,{...e,appLog:void 0}),{ok:!0,data:{path:t,stopped:!0}}}}if("network"===O){let e=o.get(i);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"network requires an active session"}};let t=(a.positionals?.[0]??"dump").toLowerCase();if(!st.includes(t))return{ok:!1,error:{code:"INVALID_ARGS",message:sr}};let r=a.positionals?.[1],n=r?Number.parseInt(r,10):25;if(!Number.isInteger(n)||n<1||n>200)return{ok:!1,error:{code:"INVALID_ARGS",message:"network dump limit must be an integer in range 1..200"}};let s=(a.positionals?.[2]??"summary").toLowerCase();if(!sa.includes(s))return{ok:!1,error:{code:"INVALID_ARGS",message:si}};let l=function(e,t){let r=on(t?.maxEntries,25,1,200),a=t?.include??"summary",i=on(t?.maxPayloadChars,2048,64,16384),n=on(t?.maxScanLines,4e3,100,2e4);if(!M.existsSync(e))return{path:e,exists:!1,scannedLines:0,matchedLines:0,entries:[],include:a,limits:{maxEntries:r,maxPayloadChars:i,maxScanLines:n}};let o=M.readFileSync(e,"utf8").split("\n"),s=Math.max(0,o.length-n),l=o.slice(s),d=[];for(let e=l.length-1;e>=0&&d.length<r;e-=1){let t=l[e]?.trim();if(!t)continue;let r=function(e,t,r,a){let i=function(e){let t=e.indexOf("{");if(t<0)return null;let r=e.lastIndexOf("}");if(r<=t)return null;let a=e.slice(t,r+1);try{let e=JSON.parse(a);return e&&"object"==typeof e?e:null}catch{return null}}(e),n=ot(i,["method","httpMethod"]),o=ot(i,["url","requestUrl"]),s=function(e,t){if(!e)return null;for(let r of t){let t=e[r];if("number"==typeof t&&Number.isInteger(t))return t;if("string"==typeof t&&/^\d{3}$/.test(t.trim()))return Number.parseInt(t.trim(),10)}return null}(i,["status","statusCode","responseCode"]),l=n9.exec(e),d=/\bmethod["'=: ]+([A-Z]+)\b/i.exec(e),u=(n??d?.[1]??l?.[1])?.toUpperCase(),c=n7.exec(e),p=o??c?.[0];if(!p)return null;let f={method:u,url:p,status:s??function(e){for(let t of oe){let r=t.exec(e);if(!r)continue;let a=Number.parseInt(r[1]??"",10);if(Number.isInteger(a))return a}return null}(e)??void 0,timestamp:function(e){let t=/\b\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z)?\b/.exec(e);return t?.[0]}(e),raw:oi(e,a),line:t};if("headers"===r||"all"===r){let t=function(e,t){if(t){let e=t.headers??t.requestHeaders??t.responseHeaders;if(void 0!==e)return oa(e)}let r=/\bheaders?["'=: ]+(\{.*\})/i.exec(e);return r?.[1]?.trim()}(e,i);t&&(f.headers=oi(t,a))}if("body"===r||"all"===r){let t=or(e,i,["requestBody","body","payload","request"]),r=or(e,i,["responseBody","response"]);t&&(f.requestBody=oi(t,a)),r&&(f.responseBody=oi(r,a))}return f}(t,s+e+1,a,i);r&&d.push(r)}return{path:e,exists:!0,scannedLines:l.length,matchedLines:d.length,entries:d,include:a,limits:{maxEntries:r,maxPayloadChars:i,maxScanLines:n}}}(o.resolveAppLogPath(i),{maxEntries:n,include:s,maxPayloadChars:2048,maxScanLines:4e3}),d=e.appLog?.backend??("ios"===e.device.platform?"device"===e.device.kind?"ios-device":"ios-simulator":"android"),u=[];return e.appLog||u.push("Capture uses the session app log file. For fresh traffic, run logs clear --restart before reproducing requests."),0===l.entries.length&&u.push("No HTTP(s) entries were found in recent session app logs."),{ok:!0,data:{...l,active:!!e.appLog,state:e.appLog?.getState()??"inactive",backend:d,notes:u}}}if("batch"===O)return await sD(a,i,s);if("close"===O){let e=o.get(i);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}};e.appLog&&await w.stop(e.appLog),a.positionals&&a.positionals.length>0&&("ios"===e.device.platform&&await k(e.device.id),await S(e.device,"close",a.positionals,a.flags?.out,{...nN(n,a.flags,e.appBundleId,e.trace?.outPath)}),await L(e.device,o1)),"ios"===e.device.platform&&await k(e.device.id),of(o.getRuntimeHints(i))&&e.appBundleId&&await y({device:e.device,appId:e.appBundleId}).catch(()=>{}),o.recordAction(e,{command:O,positionals:a.positionals??[],flags:a.flags??{},result:{session:i}}),a.flags?.saveScript&&(e.recordSession=!0),o.writeSessionLog(e),await o$(i).catch(()=>{}),o.delete(i);let t=await su({device:e.device,shutdownRequested:a.flags?.shutdown,shutdownSimulator:N??im,shutdownAndroidEmulator:_??sd});return t?{ok:!0,data:{session:i,shutdown:t}}:{ok:!0,data:{session:i}}}return null}async function sD(e,t,r){let a=e.flags?.batchOnError??"stop";if("stop"!==a)return{ok:!1,error:{code:"INVALID_ARGS",message:`Unsupported batch on-error mode: ${a}.`}};let i=e.flags?.batchMaxSteps??U;if(!Number.isInteger(i)||i<1||i>1e3)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid batch max-steps: ${String(e.flags?.batchMaxSteps)}`}};try{let a=G(e.flags?.batchSteps,i),n=Date.now(),o=[];for(let i=0;i<a.length;i+=1){let n=a[i],s=await sE(e,t,n,r,i+1);if(!s.ok)return{ok:!1,error:{code:s.error.code,message:`Batch failed at step ${s.step} (${n.command}): ${s.error.message}`,hint:s.error.hint,diagnosticId:s.error.diagnosticId,logPath:s.error.logPath,details:{...s.error.details??{},step:s.step,command:n.command,positionals:n.positionals,executed:i,total:a.length,partialResults:o}}};o.push(s.result)}return{ok:!0,data:{total:a.length,executed:a.length,totalDurationMs:Date.now()-n,results:o}}}catch(t){let e=f(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}}async function sE(e,t,r,a,i){let n=Date.now(),o=await a({token:e.token,session:t,command:r.command,positionals:r.positionals,flags:function(e,t){let r={...t??{}};delete r.batchSteps,delete r.batchOnError,delete r.batchMaxSteps;let a=e??{};for(let e of oJ)void 0===r[e]&&void 0!==a[e]&&(r[e]=a[e]);return r}(e.flags,r.flags),runtime:r.runtime,meta:e.meta}),s=Date.now()-n;return o.ok?{ok:!0,step:i,result:{step:i,command:r.command,ok:!0,data:o.data??{},durationMs:s}}:{ok:!1,step:i,error:o.error}}function sk(e,t,r,a){if(e.ok)return e;let i=r+1,n=function(e){let t;return t=(e.positionals??[]).map(e=>tw(e)),[e.command,...t].join(" ")}(t),o={...e.error.details??{},replayPath:a,step:i,action:t.command,positionals:t.positionals??[]};return{ok:!1,error:{code:e.error.code,message:`Replay failed at step ${i} (${n}): ${e.error.message}`,hint:e.error.hint,diagnosticId:e.error.diagnosticId,logPath:e.error.logPath,details:o}}}function sL(e,t){let r={...t??{}},a=e??{};for(let e of oK)void 0===r[e]&&void 0!==a[e]&&(r[e]=a[e]);return r}async function sO(e){let{action:t,sessionName:r,logPath:a,sessionStore:i,dispatch:n}=e;if(!(th(t.command)||["fill","get","is","wait"].includes(t.command)))return null;let o=i.get(r);if(!o)return null;let s=th(t.command)||"fill"===t.command,l=th(t.command)||"fill"===t.command||"get"===t.command&&t.positionals?.[0]==="text",d=await sM(o,t,a,s,n,i);for(let e of function(e){let t=[],r=Array.isArray(e.result?.selectorChain)&&e.result?.selectorChain.every(e=>"string"==typeof e)?e.result.selectorChain:[];if(t.push(...r),th(e.command)){let r=e.positionals?.[0]??"";r&&!r.startsWith("@")&&t.push(e.positionals.join(" "))}if("fill"===e.command){let r=e.positionals?.[0]??"";r&&!r.startsWith("@")&&Number.isNaN(Number(r))&&t.push(r)}if("get"===e.command){let r=e.positionals?.[1]??"";r&&!r.startsWith("@")&&t.push(e.positionals.slice(1).join(" "))}if("is"===e.command){let{split:r}=nK(e.positionals);r&&t.push(r.selectorExpression)}if("wait"===e.command){let{selectorExpression:r}=oM(e.positionals??[]);r&&t.push(r)}let a="string"==typeof e.result?.refLabel?e.result.refLabel.trim():"";if(a.length>0){let r=JSON.stringify(a);"fill"===e.command?(t.push(`id=${r} editable=true`),t.push(`label=${r} editable=true`),t.push(`text=${r} editable=true`),t.push(`value=${r} editable=true`)):(t.push(`id=${r}`),t.push(`label=${r}`),t.push(`text=${r}`),t.push(`value=${r}`))}return tc(t).filter(e=>e.trim().length>0)}(t)){let r=nq(e);if(!r)continue;let a=nH(d.nodes,r,{platform:o.device.platform,requireRect:s,requireUnique:!0,disambiguateAmbiguous:l});if(!a)continue;let i=nY(a.node,o.device.platform,{action:th(t.command)?"click":"fill"===t.command?"fill":"get"}).join(" || ");if(th(t.command))return{...t,positionals:[i]};if("fill"===t.command){let e=tu(t);if(!e)continue;return{...t,positionals:[i,e]}}if("get"===t.command){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)continue;return{...t,positionals:[e,i]}}if("is"===t.command){let{predicate:e,split:r}=nK(t.positionals);if(!e)continue;let a=r?.rest.join(" ").trim()??"",n=[e,i];return"text"===e&&a.length>0&&n.push(a),{...t,positionals:n}}if("wait"===t.command){let{selectorTimeout:e}=oM(t.positionals??[]),r=[i];return e&&r.push(e),{...t,positionals:r}}}let u=function(e,t,r){if("get"!==e.command||e.positionals?.[0]!=="text")return null;let a=e.positionals?.[1];if(!a)return null;let i=nq(a);if(!i)return null;let n=new Set,o=!1;for(let e of i.selectors)for(let t of e.terms)"role"===t.key&&"string"==typeof t.value&&n.add(n$(t.value)),("text"===t.key||"label"===t.key||"value"===t.key)&&"string"==typeof t.value&&/^\d+$/.test(t.value.trim())&&(o=!0);if(!o)return null;let s=t.nodes.filter(e=>{let t=nU(e).trim();return!!/^\d+$/.test(t)&&(0===n.size||n.has(n$(e.type??"")))});if(0===s.length||1!==tc(s.map(e=>nU(e).trim())).length)return null;let l=s[0];if(!l)return null;let d=nY(l,r.device.platform,{action:"get"});return 0===d.length?null:{...e,positionals:["text",d.join(" || ")]}}(t,d,o);return u||null}async function sM(e,t,r,a,i,n){let o=await i(e.device,"snapshot",[],t.flags?.out,{...nN(r,{...t.flags??{},snapshotInteractiveOnly:a,snapshotCompact:a},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[],l={nodes:nL(t.flags?.snapshotRaw?s:nP(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend};return e.snapshot=l,n.set(e.name,e),l}function sx(e){if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}function sC(e,t){let r=V(e.type??"Element"),a=B(e,r),i=!1===e.enabled?"disabled":"enabled",n=!0===e.selected?"selected":"unselected",o=!0===e.hittable?"hittable":"not-hittable";return[String(t??e.depth??0),r,a,i,n,o].join("|")}function sR(e,t){return t.flatten?e.map(e=>({text:H(e,0,!1),comparable:sC(e,0)})):F(e).map(e=>({text:e.text,comparable:sC(e.node,e.depth)}))}function sT(e,t){return e.get(t)??0}async function sP(e){let{req:t,sessionName:r,logPath:a,sessionStore:i}=e,n=e.dispatchSnapshotCommand??nA,o=e.runnerCommand??tl,s=t.command;if("snapshot"===s){let{session:e,device:o}=await sU(i,r,t.flags);if(!nb("snapshot",o))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"snapshot is not supported on this device"}};let s=sF(t.flags?.snapshotScope,e);return s.ok?await sV(e,o,async()=>{let l=e?.appBundleId,d=await s$({dispatchSnapshotCommand:n,device:o,session:e,req:t,logPath:a,snapshotScope:s.scope}),u=e?{...e,snapshot:d.snapshot}:{name:r,device:o,createdAt:Date.now(),appBundleId:l,snapshot:d.snapshot,actions:[]};return sG(i,u,t,{nodes:d.snapshot.nodes.length,truncated:d.snapshot.truncated??!1}),i.set(r,u),{ok:!0,data:{nodes:d.snapshot.nodes,truncated:d.snapshot.truncated??!1,appName:u.appBundleId?u.appName??u.appBundleId:void 0,appBundleId:u.appBundleId}}}):s.response}if("diff"===s){if(t.positionals?.[0]!=="snapshot")return{ok:!1,error:{code:"INVALID_ARGS",message:"diff currently supports only: diff snapshot"}};let{session:e,device:o}=await sU(i,r,t.flags);if(!nb("diff",o))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"diff is not supported on this device"}};let s=sF(t.flags?.snapshotScope,e);if(!s.ok)return s.response;let l=t.flags?.snapshotInteractiveOnly===!0;return await sV(e,o,async()=>{let d=e?.appBundleId,u=(await s$({dispatchSnapshotCommand:n,device:o,session:e,req:t,logPath:a,snapshotScope:s.scope})).snapshot;if(!e?.snapshot){let a=function(e,t={}){return sR(e,t).length}(u.nodes,{flatten:l}),n=e?{...e,snapshot:u}:{name:r,device:o,createdAt:Date.now(),appBundleId:d,snapshot:u,actions:[]};return sG(i,n,t,{mode:"snapshot",baselineInitialized:!0,summary:{additions:0,removals:0,unchanged:a}}),i.set(r,n),{ok:!0,data:{mode:"snapshot",baselineInitialized:!0,summary:{additions:0,removals:0,unchanged:a},lines:[]}}}let c=function(e,t,r={}){let a=function(e,t){let r=e.length,a=t.length,i=r+a,n=new Map,o=[];n.set(1,0);for(let s=0;s<=i;s+=1){o.push(new Map(n));for(let i=-s;i<=s;i+=2){let l=i===-s||i!==s&&sT(n,i-1)<sT(n,i+1)?sT(n,i+1):sT(n,i-1)+1,d=l-i;for(;l<r&&d<a&&e[l].comparable===t[d].comparable;)l+=1,d+=1;if(n.set(i,l),l>=r&&d>=a)return function(e,t,r,a,i){let n=[],o=a,s=i;for(let a=e.length-1;a>=0;a-=1){let i=e[a],l=o-s,d=l===-a||l!==a&&sT(i,l-1)<sT(i,l+1)?l+1:l-1,u=sT(i,d),c=u-d;for(;o>u&&s>c;)n.push({kind:"unchanged",text:r[s-1].text}),o-=1,s-=1;if(0===a)break;o===u?(n.push({kind:"added",text:r[c].text}),s=c):(n.push({kind:"removed",text:t[u].text}),o=u)}return n.reverse(),n}(o,e,t,r,a)}}return[]}(sR(e,r),sR(t,r)),i={additions:0,removals:0,unchanged:0};for(let e of a)"added"===e.kind&&(i.additions+=1),"removed"===e.kind&&(i.removals+=1),"unchanged"===e.kind&&(i.unchanged+=1);return{summary:i,lines:a}}(e.snapshot.nodes,u.nodes,{flatten:l}),p={...e,snapshot:u};return sG(i,p,t,{mode:"snapshot",baselineInitialized:!1,summary:c.summary}),i.set(r,p),{ok:!0,data:{mode:"snapshot",baselineInitialized:!1,summary:c.summary,lines:c.lines}}})}if("wait"===s){let{session:e,device:o}=await sU(i,r,t.flags),s=function(e){if(0===e.length)return null;let t=sx(e[0]);if(null!==t)return{kind:"sleep",durationMs:t};if("text"===e[0]){let t=sx(e[e.length-1]);return{kind:"text",text:(null!==t?e.slice(1,-1).join(" "):e.slice(1).join(" ")).trim(),timeoutMs:t}}if(e[0].startsWith("@")){let t=sx(e[e.length-1]);return{kind:"ref",rawRef:e[0],timeoutMs:t}}let r=sx(e[e.length-1]),a=nJ(null!==r?e.slice(0,-1):e.slice());if(a&&0===a.rest.length){let e=nq(a.selectorExpression);if(e)return{kind:"selector",selector:e,selectorExpression:a.selectorExpression,timeoutMs:r}}return{kind:"text",text:(null!==r?e.slice(0,-1).join(" "):e.join(" ")).trim(),timeoutMs:r}}(t.positionals??[]);return s?"sleep"===s.kind?(await new Promise(e=>setTimeout(e,s.durationMs)),sG(i,e,t,{waitedMs:s.durationMs}),{ok:!0,data:{waitedMs:s.durationMs}}):nb("wait",o)?await sV(e,o,async()=>{let l,d;if("selector"===s.kind){let l=s.timeoutMs??1e4,d=Date.now();for(;Date.now()-d<l;){let l=await n(o,"snapshot",[],t.flags?.out,{...nN(a,{...t.flags,snapshotInteractiveOnly:!1,snapshotCompact:!1},e?.appBundleId,e?.trace?.outPath)}),u=l?.nodes??[],c=nL(t.flags?.snapshotRaw?u:nP(u));e&&(e.snapshot={nodes:c,truncated:l?.truncated,createdAt:Date.now(),backend:l?.backend},i.set(r,e));let p=nW(c,s.selector,{platform:o.platform});if(p)return sG(i,e,t,{selector:p.selector.raw,waitedMs:Date.now()-d}),{ok:!0,data:{selector:p.selector.raw,waitedMs:Date.now()-d}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for selector: ${s.selectorExpression}`}}}if("ref"===s.kind){if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref wait requires an existing snapshot in session."}};let t=nO(s.rawRef);if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref: ${s.rawRef}`}};let r=nM(e.snapshot.nodes,t),a=r?nR(r,e.snapshot.nodes):void 0;if(!a)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s.rawRef} not found or has no label`}};l=a,d=s.timeoutMs}else l=s.text,d=s.timeoutMs;if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires text"}};let u=d??1e4,c=Date.now();for(;Date.now()-c<u;){if("ios"===o.platform){let r=await tl(o,{command:"findText",text:l,appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:a,traceLogPath:e?.trace?.outPath,requestId:t.meta?.requestId});if(r?.found)return sG(i,e,t,{text:l,waitedMs:Date.now()-c}),{ok:!0,data:{text:l,waitedMs:Date.now()-c}}}else if("android"===o.platform&&nC(nL((await av(o,{scope:l})).nodes??[]),l))return sG(i,e,t,{text:l,waitedMs:Date.now()-c}),{ok:!0,data:{text:l,waitedMs:Date.now()-c}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for text: ${l}`}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"wait is not supported on this device"}}:{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires a duration or text"}}}if("alert"===s){let{session:e,device:n}=await sU(i,r,t.flags),s=(t.positionals?.[0]??"get").toLowerCase();return nb("alert",n)?await sV(e,n,async()=>{if("wait"===s){let r=sx(t.positionals?.[1])??1e4,s=Date.now();for(;Date.now()-s<r;){try{let r=await o(n,{command:"alert",action:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:a,traceLogPath:e?.trace?.outPath,requestId:t.meta?.requestId});return sG(i,e,t,r),{ok:!0,data:r}}catch{}await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"alert wait timed out"}}}let r="accept"===s||"dismiss"===s?s:"get",l={verbose:t.flags?.verbose,logPath:a,traceLogPath:e?.trace?.outPath,requestId:t.meta?.requestId};if("accept"===r||"dismiss"===r){let a,s=Date.now();for(;Date.now()-s<2e3;){try{let a=await o(n,{command:"alert",action:r,appBundleId:e?.appBundleId},l);return sG(i,e,t,a),{ok:!0,data:a}}catch(t){a=t;let e=String(t?.message??"").toLowerCase();if(!e.includes("alert not found")&&!e.includes("no alert"))break}await new Promise(e=>setTimeout(e,300))}throw a}let d=await o(n,{command:"alert",action:r,appBundleId:e?.appBundleId},l);return sG(i,e,t,d),{ok:!0,data:d}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"alert is only supported on iOS simulators"}}}if("settings"===s){let e=t.positionals?.[0]?.toLowerCase(),n=t.positionals?.[1]?.toLowerCase(),o=t.positionals?.[2]?.toLowerCase();if(!e||!n||"permission"===e&&!o)return{ok:!1,error:{code:"INVALID_ARGS",message:j}};let{session:s,device:l}=await sU(i,r,t.flags);return nb("settings",l)?await sV(s,l,async()=>{let r=s?.appBundleId,d="permission"===e?[e,n,o,t.positionals?.[3]??"",r??""]:[e,n,r??""],u=await nA(l,"settings",d,t.flags?.out,{...nN(a,t.flags,r,s?.trace?.outPath)});return sG(i,s,t,u??{setting:e,state:n}),{ok:!0,data:u??{setting:e,state:n}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"settings is not supported on this device"}}}return null}async function s$(e){let{dispatchSnapshotCommand:t,device:r,session:a,req:i,logPath:n,snapshotScope:o}=e,s=await t(r,"snapshot",[],i.flags?.out,{...nN(n,{...i.flags,snapshotScope:o},a?.appBundleId,a?.trace?.outPath)}),l=s?.nodes??[];return{snapshot:{nodes:nL(i.flags?.snapshotRaw?l:nP(l)),truncated:s?.truncated,createdAt:Date.now(),backend:s?.backend}}}function sF(e,t){if(!e||!e.trim().startsWith("@"))return{ok:!0,scope:e};if(!t?.snapshot)return{ok:!1,response:{ok:!1,error:{code:"INVALID_ARGS",message:"Ref scope requires an existing snapshot in session."}}};let r=nO(e.trim());if(!r)return{ok:!1,response:{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref scope: ${e}`}}};let a=nM(t.snapshot.nodes,r),i=a?nR(a,t.snapshot.nodes):void 0;return i?{ok:!0,scope:i}:{ok:!1,response:{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${e} not found or has no label`}}}}async function sU(e,t,r){let a=e.get(t),i=a?.device??await nv(r??{});return a||await nS(i),{session:a,device:i}}async function sV(e,t,r){let a=!e&&"ios"===t.platform;try{return await r()}finally{a&&await tt(t.id)}}function sG(e,t,r,a){t&&e.recordAction(t,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:a})}function sj(e,t,r,a={}){let i=sq(r);if(!i)return{matches:[],score:0};let n=0,o=[];for(let r of e){if(a.requireRect&&!r.rect)continue;let e=function(e,t,r){switch(t){case"role":return function(e,t){let r=function(e){let t=e.trim();return t?t=(t.split(".").pop()??t).replace(/XCUIElementType/gi,"").toLowerCase():""}(e??"");return r?r===t?2:+!!r.includes(t):0}(e.type,r);case"label":return sB(e.label,r);case"value":return sB(e.value,r);case"id":return sB(e.identifier,r);default:return Math.max(sB(e.label,r),sB(e.value,r),sB(e.identifier,r))}}(r,t,i);if(!(e<=0)){if(e>n){n=e,o.length=0,o.push(r);continue}e===n&&o.push(r)}}return{matches:o,score:n}}function sB(e,t){let r=sq(e??"");return r?r===t?2:+!!r.includes(t):0}function sq(e){return e.trim().toLowerCase().replace(/\s+/g," ")}async function sH(e){let{req:t,sessionName:r,logPath:a,sessionStore:i,invoke:n}=e,o=e.dispatch??nA,s=t.command;if("find"!==s)return null;let l=t.positionals??[];if(0===l.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a locator or text"}};let{locator:d,query:u,action:c,value:p,timeoutMs:f}=function(e){let t="any",r=0;["text","label","value","role","id"].includes(e[0])&&(t=e[0],r=1);let a=e[r]??"",i=e.slice(r+1);if(0===i.length)return{locator:t,query:a,action:"click"};let n=i[0].toLowerCase();if("get"===n){let e=i[1]?.toLowerCase();if("text"===e)return{locator:t,query:a,action:"get_text"};if("attrs"===e)return{locator:t,query:a,action:"get_attrs"};throw new h("INVALID_ARGS","find get only supports text or attrs")}if("wait"===n)return{locator:t,query:a,action:"wait",timeoutMs:sx(i[1])??void 0};if("exists"===n)return{locator:t,query:a,action:"exists"};if("click"===n)return{locator:t,query:a,action:"click"};if("focus"===n)return{locator:t,query:a,action:"focus"};if("fill"===n)return{locator:t,query:a,action:"fill",value:i.slice(1).join(" ")};if("type"===n)return{locator:t,query:a,action:"type",value:i.slice(1).join(" ")};throw new h("INVALID_ARGS",`Unsupported find action: ${i[0]}`)}(l);if(!u)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a value"}};let m=i.get(r);if(!m&&"exists"!==c&&"wait"!==c&&"get_text"!==c&&"get_attrs"!==c)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let w=m?.device??await nv(t.flags??{});m||await nS(w);let g=m?.appBundleId,I="role"!==d?u:void 0,v="click"===c||"focus"===c||"fill"===c||"type"===c,A=0,y=null,b=async()=>{let e=Date.now();if(y&&e-A<750)return{nodes:y};let n=await o(w,"snapshot",[],t.flags?.out,{...nN(a,{...t.flags,snapshotScope:I,snapshotInteractiveOnly:v,snapshotCompact:v},g,m?.trace?.outPath)}),s=n?.nodes??[],l=nL(t.flags?.snapshotRaw?s:nP(s));return A=e,y=l,m&&(m.snapshot={nodes:l,truncated:n?.truncated,createdAt:Date.now(),backend:n?.backend},i.set(r,m)),{nodes:l,truncated:n?.truncated,backend:n?.backend}};if("wait"===c){let e=f??1e4,r=Date.now();for(;Date.now()-r<e;){let{nodes:e}=await b();if(sj(e,d,u,{requireRect:!1}).matches[0])return m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0,waitedMs:Date.now()-r}}),{ok:!0,data:{found:!0,waitedMs:Date.now()-r}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"find wait timed out"}}}let{nodes:N}=await b(),_=sj(N,d,u,{requireRect:v});if(v&&_.matches.length>1){let e=_.matches.slice(0,8).map(e=>{let t=nU(e)||e.label||e.identifier||e.type||"";return`@${e.ref}${t?`(${t})`:""}`});return{ok:!1,error:{code:"AMBIGUOUS_MATCH",message:`find matched ${_.matches.length} elements for ${d} "${u}". Use a more specific locator or selector.`,details:{locator:d,query:u,matches:_.matches.length,candidates:e}}}}let S=_.matches[0]??null;if(!S)return{ok:!1,error:{code:"COMMAND_FAILED",message:"find did not match any element"}};let D="click"===c||"focus"===c||"fill"===c||"type"===c?function(e,t){if(t.hittable)return t;let r=t,a=new Set;for(;void 0!==r.parentIndex&&!a.has(r.ref);){a.add(r.ref);let t=e[r.parentIndex];if(!t)break;if(t.hittable)return t;r=t}return null}(N,S)??S:S,E=`@${D.ref}`,k={...t.flags??{},noRecord:!0};if("exists"===c)return m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0}}),{ok:!0,data:{found:!0}};if("get_text"===c){let e=nU(S);return m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{ref:E,action:"get text",text:e}}),{ok:!0,data:{ref:E,text:e,node:S}}}if("get_attrs"===c)return m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{ref:E,action:"get attrs"}}),{ok:!0,data:{ref:E,node:S}};if("click"===c){let e=await n({token:t.token,session:r,command:"click",positionals:[E],flags:k});if(!e.ok)return e;let a=D.rect?nx(D.rect):null,o={ref:E,locator:d,query:u};return a&&(o.x=a.x,o.y=a.y),m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{ref:E,action:"click",locator:d,query:u}}),{ok:!0,data:o}}if("fill"===c){if(!p)return{ok:!1,error:{code:"INVALID_ARGS",message:"find fill requires text"}};let e=await n({token:t.token,session:r,command:"fill",positionals:[E,p],flags:k});return e.ok&&m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{ref:E,action:"fill"}}),e}if("focus"===c){let e=S.rect?nx(S.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};let r=await o(w,"focus",[String(e.x),String(e.y)],t.flags?.out,{...nN(a,t.flags,m?.appBundleId,m?.trace?.outPath)});return m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{ref:E,action:"focus"}}),{ok:!0,data:r??{ref:E}}}if("type"===c){if(!p)return{ok:!1,error:{code:"INVALID_ARGS",message:"find type requires text"}};let e=S.rect?nx(S.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};await o(w,"focus",[String(e.x),String(e.y)],t.flags?.out,{...nN(a,t.flags,m?.appBundleId,m?.trace?.outPath)});let r=await o(w,"type",[p],t.flags?.out,{...nN(a,t.flags,m?.appBundleId,m?.trace?.outPath)});return m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{ref:E,action:"type"}}),{ok:!0,data:r??{ref:E}}}return null}function sW(e){return e instanceof Error?e.message:String(e)}function sz(e){let t=e.appBundleId?.trim();return t&&t.length>0?t:void 0}function sJ(e,t,r){return{verbose:e.flags?.verbose,logPath:t,traceLogPath:r.trace?.outPath}}async function sK(e){let{req:t,sessionName:r,sessionStore:a,logPath:n}=e,o=e.deps??{runCmd:c,runCmdBackground:d,runIosRunnerCommand:tl},s=t.command;if("record"===s){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"record requires start|stop"}};let d=a.get(r),c=d?.device??await nv(t.flags??{});d||await nS(c);let p=d??{name:r,device:c,createdAt:Date.now(),actions:[]};if("start"===e){if(p.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"recording already in progress"}};let e=t.flags?.fps;if(void 0!==e&&(!Number.isInteger(e)||e<1||e>120))return{ok:!1,error:{code:"INVALID_ARGS",message:"fps must be an integer between 1 and 120"}};let d=t.positionals?.[1]??`./recording-${Date.now()}.mp4`;if(!nb("record",c))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"record is not supported on this device"}};let f="ios"===c.platform&&"device"===c.kind?sz(p):void 0;if("ios"===c.platform&&"device"===c.kind&&!f)return{ok:!1,error:{code:"INVALID_ARGS",message:"record on physical iOS devices requires an active app session; run open <app> first"}};let m=tb.expandHome(d,t.meta?.cwd),h=t.meta?.clientArtifactPaths?.outPath;M.mkdirSync(i.dirname(m),{recursive:!0});let w=sJ(t,n,p);if("ios"===c.platform&&"device"===c.kind){let t=`agent-device-recording-${Date.now()}.mp4`,r=`tmp/${t}`,i=async()=>{await o.runIosRunnerCommand(c,{command:"recordStart",outPath:t,fps:e,appBundleId:f},w)};try{await i()}catch(e){if(!sW(e).toLowerCase().includes("recording already in progress"))return{ok:!1,error:{code:"COMMAND_FAILED",message:`failed to start recording: ${sW(e)}`}};{var l,u;D({level:"warn",phase:"record_start_runner_desynced",data:{platform:c.platform,kind:c.kind,deviceId:c.id,session:p.name,error:sW(e)}});let t=(l=c.id,u=p.name,a.toArray().find(e=>e.name!==u&&"ios"===e.device.platform&&"device"===e.device.kind&&e.device.id===l&&e.recording?.platform==="ios-device-runner"));if(t)return{ok:!1,error:{code:"COMMAND_FAILED",message:`failed to start recording: recording already in progress in session '${t.name}'`}};try{await o.runIosRunnerCommand(c,{command:"recordStop",appBundleId:f},w)}catch{}try{await i()}catch(e){return{ok:!1,error:{code:"COMMAND_FAILED",message:`failed to start recording: ${sW(e)}`}}}}}p.recording={platform:"ios-device-runner",outPath:m,clientOutPath:h,remotePath:r}}else if("ios"===c.platform){let{child:e,wait:t}=o.runCmdBackground("xcrun",ek(c,["io",c.id,"recordVideo",m]),{allowFailure:!0});p.recording={platform:"ios",outPath:m,clientOutPath:h,child:e,wait:t}}else{let e=`/sdcard/agent-device-recording-${Date.now()}.mp4`,{child:t,wait:r}=o.runCmdBackground("adb",["-s",c.id,"shell","screenrecord",e],{allowFailure:!0});p.recording={platform:"android",outPath:m,clientOutPath:h,remotePath:e,child:t,wait:r}}return a.set(r,p),a.recordAction(p,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start"}}),{ok:!0,data:{recording:"started",outPath:h??d}}}if(!p.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active recording"}};let f=p.recording;if("ios-device-runner"===f.platform){let e=sz(p);try{await o.runIosRunnerCommand(c,{command:"recordStop",appBundleId:e},sJ(t,n,p))}catch(e){D({level:"warn",phase:"record_stop_runner_failed",data:{platform:c.platform,kind:c.kind,deviceId:c.id,session:p.name,error:sW(e)}})}let r={stdout:"",stderr:"",exitCode:1};for(let e of e0)if(0===(r=await o.runCmd("xcrun",["devicectl","device","copy","from","--device",c.id,"--source",f.remotePath,"--destination",f.outPath,"--domain-type","appDataContainer","--domain-identifier",e],{allowFailure:!0})).exitCode)break;if(p.recording=void 0,0!==r.exitCode){let e=r.stderr.trim()||r.stdout.trim()||`devicectl exited with code ${r.exitCode}`;return{ok:!1,error:{code:"COMMAND_FAILED",message:`failed to copy recording from device: ${e}`}}}}else{f.child.kill("SIGINT");try{await f.wait}catch{}if("android"===f.platform&&f.remotePath)try{await o.runCmd("adb",["-s",c.id,"pull",f.remotePath,f.outPath],{allowFailure:!0}),await o.runCmd("adb",["-s",c.id,"shell","rm","-f",f.remotePath],{allowFailure:!0})}catch{}p.recording=void 0}a.recordAction(p,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:f.outPath}});let m=[{field:"outPath",path:f.outPath,localPath:f.clientOutPath,fileName:i.basename(f.clientOutPath??f.outPath)}];return{ok:!0,data:{recording:"stopped",outPath:f.outPath,artifacts:m}}}if("trace"===s){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"trace requires start|stop"}};let n=a.get(r);if(!n)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}};if("start"===e){if(n.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"trace already in progress"}};let e=t.positionals?.[1]??a.defaultTracePath(n),r=tb.expandHome(e);return M.mkdirSync(i.dirname(r),{recursive:!0}),M.appendFileSync(r,""),n.trace={outPath:r,startedAt:Date.now()},a.recordAction(n,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start",outPath:r}}),{ok:!0,data:{trace:"started",outPath:r}}}if(!n.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active trace"}};let o=n.trace.outPath;if(t.positionals?.[1]){let e=tb.expandHome(t.positionals[1]);M.mkdirSync(i.dirname(e),{recursive:!0}),M.existsSync(o)?M.renameSync(o,e):M.appendFileSync(e,""),o=e}return n.trace=void 0,a.recordAction(n,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:o}}),{ok:!0,data:{trace:"stopped",outPath:o}}}return null}function sX(e,t,r){return t>=e.x&&t<=e.x+e.width&&r>=e.y&&r<=e.y+e.height}function sZ(e){let t=null,r=-1;for(let a of e){let e=a.width*a.height;e>r&&(t=a,r=e)}return t}function sY(e,t,r){return Math.min(r,Math.max(t,Math.round(e)))}async function sQ(e){let{req:t,sessionName:r,sessionStore:a,contextFromFlags:i}=e,n=e.dispatch??nA,o=t.command;if("press"===o){let e=a.get(r);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!nb("press",e.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"press is not supported on this device"}};let s=function(e){if(e.length<2)return null;let t=Number(e[0]),r=Number(e[1]);return Number.isFinite(t)&&Number.isFinite(r)?{x:t,y:r}:null}(t.positionals??[]);if(s){let r=await n(e.device,"press",[String(s.x),String(s.y)],t.flags?.out,{...i(t.flags,e.appBundleId,e.trace?.outPath)});return a.recordAction(e,{command:o,positionals:t.positionals??[String(s.x),String(s.y)],flags:t.flags??{},result:r??{x:s.x,y:s.y}}),{ok:!0,data:r??{x:s.x,y:s.y}}}let l="click",d=t.positionals?.[0]??"";if(d.startsWith("@")){let r=s2("press",t.flags);if(r)return r;let s=t.positionals.length>1?t.positionals.slice(1).join(" ").trim():"",u=s3({session:e,refInput:d,fallbackLabel:s,requireRect:!0,invalidRefMessage:`${o} requires a ref like @e2`,notFoundMessage:`Ref ${d} not found or has no bounds`});if(!u.ok)return u.response;let{ref:c}=u.target,p=u.target.node,f=u.target.snapshotNodes,m=s4(p.rect);if(!m){let r=await s0(e,t.flags,a,i,{interactiveOnly:!0},n),o=nM(r.nodes,c),l=s.length>0?nC(r.nodes,s):null,d=s4(l?.rect),u=s4(o?.rect)?o:d?l:o??l,h=s4(u?.rect);u&&h&&(p=u,f=r.nodes,m=h)}if(!m)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${d} not found or has invalid bounds`}};let h=nR(p,f),w=nY(p,e.device.platform,{action:l}),{x:g,y:I}=m,v=await n(e.device,"press",[String(g),String(I)],t.flags?.out,{...i(t.flags,e.appBundleId,e.trace?.outPath)});return a.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:c,x:g,y:I,refLabel:h,selectorChain:w}}),{ok:!0,data:{...v??{},ref:c,x:g,y:I}}}let u=(t.positionals??[]).join(" ").trim();if(!u)return{ok:!1,error:{code:"INVALID_ARGS",message:`${o} requires @ref, selector expression, or x y coordinates`}};let c=nB(u),p=await s0(e,t.flags,a,i,{interactiveOnly:!0},n),f=await S("selector_resolve",()=>nH(p.nodes,c,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0}),{command:o});if(!f||!f.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:nz(c,f?.diagnostics??[],{unique:!0})}};let m=s4(f.node.rect);if(!m)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Selector ${f.selector.raw} resolved to invalid bounds`}};let{x:h,y:w}=m,g=await n(e.device,"press",[String(h),String(w)],t.flags?.out,{...i(t.flags,e.appBundleId,e.trace?.outPath)}),I=nY(f.node,e.device.platform,{action:l}),v=nR(f.node,p.nodes);return a.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{x:h,y:w,selector:f.selector.raw,selectorChain:I,refLabel:v}}),{ok:!0,data:{...g??{},selector:f.selector.raw,x:h,y:w}}}if("fill"===o){let e=a.get(r);if(e&&!nb("fill",e.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"fill is not supported on this device"}};if(t.positionals?.[0]?.startsWith("@")){if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let r=s2("fill",t.flags);if(r)return r;let s=t.positionals.length>=3?t.positionals[1]:"",l=t.positionals.length>=3?t.positionals.slice(2).join(" "):t.positionals.slice(1).join(" ");if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after ref"}};let d=s3({session:e,refInput:t.positionals[0],fallbackLabel:s,requireRect:!0,invalidRefMessage:"fill requires a ref like @e2",notFoundMessage:`Ref ${t.positionals[0]} not found or has no bounds`});if(!d.ok)return d.response;let{ref:u,node:c,snapshotNodes:p}=d.target;if(!c.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${t.positionals[0]} not found or has no bounds`}};let f=c.type??"",m=f&&!nF(f,e.device.platform)?`fill target ${t.positionals[0]} resolved to "${f}", attempting fill anyway.`:void 0,h=nR(c,p),w=nY(c,e.device.platform,{action:"fill"}),{x:g,y:I}=nx(c.rect),v={...await n(e.device,"fill",[String(g),String(I),l],t.flags?.out,{...i(t.flags,e.appBundleId,e.trace?.outPath)})??{ref:u,x:g,y:I}};return m&&(v.warning=m),a.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{...v,refLabel:h,selectorChain:w}}),{ok:!0,data:v}}if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=nJ(t.positionals??[],{preferTrailingValue:!0});if(s){if(0===s.rest.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let r=s.rest.join(" ").trim();if(!r)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let l=nB(s.selectorExpression),d=await s0(e,t.flags,a,i,{interactiveOnly:!0},n),u=await S("selector_resolve",()=>nH(d.nodes,l,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0}),{command:o});if(!u||!u.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:nz(l,u?.diagnostics??[],{unique:!0})}};let c=u.node,p=c.type??"",f=p&&!nF(p,e.device.platform)?`fill target ${u.selector.raw} resolved to "${p}", attempting fill anyway.`:void 0,{x:m,y:h}=nx(u.node.rect),w=await n(e.device,"fill",[String(m),String(h),r],t.flags?.out,{...i(t.flags,e.appBundleId,e.trace?.outPath)}),g=nY(c,e.device.platform,{action:"fill"}),I={...w??{x:m,y:h,text:r},selector:u.selector.raw,selectorChain:g,refLabel:nR(c,d.nodes)};return f&&(I.warning=f),a.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:I}),{ok:!0,data:I}}return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires x y text, @ref text, or selector text"}}}if("get"===o){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)return{ok:!1,error:{code:"INVALID_ARGS",message:"get only supports text or attrs"}};let s=a.get(r);if(!s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!nb("get",s.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"get is not supported on this device"}};let l=t.positionals?.[1]??"";if(l.startsWith("@")){let r=s2("get",t.flags);if(r)return r;let i=s3({session:s,refInput:l,fallbackLabel:t.positionals.length>2?t.positionals.slice(2).join(" ").trim():"",requireRect:!1,invalidRefMessage:"get text requires a ref like @e2",notFoundMessage:`Ref ${l} not found`});if(!i.ok)return i.response;let{ref:n,node:d}=i.target,u=nY(d,s.device.platform,{action:"get"});if("attrs"===e)return a.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:n,selectorChain:u}}),{ok:!0,data:{ref:n,node:d}};let c=nU(d);return a.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:n,text:c,refLabel:c||void 0,selectorChain:u}}),{ok:!0,data:{ref:n,text:c,node:d}}}let d=t.positionals.slice(1).join(" ").trim();if(!d)return{ok:!1,error:{code:"INVALID_ARGS",message:"get requires @ref or selector expression"}};let u=nB(d),c=await s0(s,t.flags,a,i,{interactiveOnly:!1},n),p=await S("selector_resolve",()=>nH(c.nodes,u,{platform:s.device.platform,requireRect:!1,requireUnique:!0,disambiguateAmbiguous:"text"===e}),{command:o});if(!p)return{ok:!1,error:{code:"COMMAND_FAILED",message:nz(u,[],{unique:!0})}};let f=p.node,m=nY(f,s.device.platform,{action:"get"});if("attrs"===e)return a.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{selector:p.selector.raw,selectorChain:m}}),{ok:!0,data:{selector:p.selector.raw,node:f}};let h=nU(f);return a.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{text:h,refLabel:h||void 0,selector:p.selector.raw,selectorChain:m}}),{ok:!0,data:{selector:p.selector.raw,text:h,node:f}}}if("is"===o){let e=(t.positionals?.[0]??"").toLowerCase();if(!["visible","hidden","exists","editable","selected","text"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires predicate: visible|hidden|exists|editable|selected|text"}};let s=a.get(r);if(!s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!nb("is",s.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"is is not supported on this device"}};let{split:l}=nK(t.positionals);if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires a selector expression"}};let d=l.rest.join(" ").trim();if("text"===e&&!d)return{ok:!1,error:{code:"INVALID_ARGS",message:"is text requires expected text value"}};if("text"!==e&&l.rest.length>0)return{ok:!1,error:{code:"INVALID_ARGS",message:`is ${e} does not accept trailing values`}};let u=nB(l.selectorExpression),c=await s0(s,t.flags,a,i,{interactiveOnly:!1},n);if("exists"===e){let r=nW(c.nodes,u,{platform:s.device.platform});return r?(a.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:r.selector.raw,selectorChain:u.selectors.map(e=>e.raw),pass:!0,matches:r.matches}}),{ok:!0,data:{predicate:e,pass:!0,selector:r.selector.raw,matches:r.matches}}):{ok:!1,error:{code:"COMMAND_FAILED",message:nz(u,[],{unique:!1})}}}let p=await S("selector_resolve",()=>nH(c.nodes,u,{platform:s.device.platform,requireUnique:!0}),{command:"is",predicate:e});if(!p)return{ok:!1,error:{code:"COMMAND_FAILED",message:nz(u,[],{unique:!0})}};let f=function(e){let{predicate:t,node:r,expectedText:a,platform:i}=e,n=nU(r),o=!1;switch(t){case"visible":o=nX(r);break;case"hidden":o=!nX(r);break;case"editable":o=nZ(r,i);break;case"selected":o=!0===r.selected;break;case"text":o=n===(a??"")}let s="text"===t?`expected="${a??""}" actual="${n}"`:`actual=${JSON.stringify({visible:nX(r),editable:nZ(r,i),selected:!0===r.selected})}`;return{pass:o,actualText:n,details:s}}({predicate:e,node:p.node,expectedText:d,platform:s.device.platform});return f.pass?(a.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:p.selector.raw,selectorChain:u.selectors.map(e=>e.raw),pass:!0,text:"text"===e?f.actualText:void 0}}),{ok:!0,data:{predicate:e,pass:!0,selector:p.selector.raw}}):{ok:!1,error:{code:"COMMAND_FAILED",message:`is ${e} failed for selector ${p.selector.raw}: ${f.details}`}}}if("scrollintoview"===o){let e=a.get(r);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!nb("scrollintoview",e.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"scrollintoview is not supported on this device"}};let s=t.positionals?.[0]??"";if(!s.startsWith("@"))return null;let l=s2("scrollintoview",t.flags);if(l)return l;let d=s3({session:e,refInput:s,fallbackLabel:t.positionals&&t.positionals.length>1?t.positionals.slice(1).join(" ").trim():"",requireRect:!0,invalidRefMessage:"scrollintoview requires a ref like @e2",notFoundMessage:`Ref ${s} not found or has no bounds`});if(!d.ok)return d.response;let{ref:u,node:c,snapshotNodes:p}=d.target;if(!c.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s} not found or has no bounds`}};let f=function(e,t){let r=nx(t),a=e.filter(e=>{var t;return!!(t=e.rect)&&Number.isFinite(t.x)&&Number.isFinite(t.y)&&Number.isFinite(t.width)&&Number.isFinite(t.height)}),i=a.filter(e=>{let t=(e.type??"").toLowerCase();return t.includes("application")||t.includes("window")}),n=sZ(i.map(e=>e.rect).filter(e=>sX(e,r.x,r.y)));if(n)return n;let o=sZ(i.map(e=>e.rect));if(o)return o;let s=sZ(a.map(e=>e.rect).filter(e=>sX(e,r.x,r.y)));return s||null}(p,c.rect);if(!f)return{ok:!1,error:{code:"COMMAND_FAILED",message:`scrollintoview could not infer viewport for ${s}`}};let m=function(e,t){var r,a;let i=Math.max(1,t.height),n=Math.max(1,t.width),o=t.y,s=t.y+i,l=t.x,d=t.x+n,u=o+.25*i,c=s-.25*i,p=Math.max(8,.1*n),f=e.y+e.height/2,m=e.x+e.width/2;if(f>=u&&f<=c)return null;let h=Math.round((r=m,a=l+p,Math.min(d-p,Math.max(a,r)))),w=Math.round(o+.86*i),g=Math.round(o+.14*i),I=Math.max(1,Math.abs(w-g));return f>c?{x:h,startY:w,endY:g,count:sY(Math.ceil((f-c)/I),1,50),direction:"down"}:{x:h,startY:g,endY:w,count:sY(Math.ceil((u-f)/I),1,50),direction:"up"}}(c.rect,f),h=nR(c,p),w=nY(c,e.device.platform,{action:"get"});if(!m)return a.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:u,attempts:0,alreadyVisible:!0,refLabel:h,selectorChain:w}}),{ok:!0,data:{ref:u,attempts:0,alreadyVisible:!0}};let g=await n(e.device,"swipe",[String(m.x),String(m.startY),String(m.x),String(m.endY),"16"],t.flags?.out,{...i(t.flags,e.appBundleId,e.trace?.outPath),count:m.count,pauseMs:0,pattern:"one-way"});return a.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{...g??{},ref:u,attempts:m.count,direction:m.direction,refLabel:h,selectorChain:w}}),{ok:!0,data:{...g??{},ref:u,attempts:m.count,direction:m.direction}}}return null}async function s0(e,t,r,a,i,n=nA){let o=await n(e.device,"snapshot",[],t?.out,{...a({...t??{},snapshotInteractiveOnly:i.interactiveOnly,snapshotCompact:i.interactiveOnly},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[];return e.snapshot={nodes:nL(t?.snapshotRaw?s:nP(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend},r.set(e.name,e),e.snapshot}let s1=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"]];function s2(e,t){let r=function(e){if(!e)return[];let t=[];for(let[r,a]of s1)void 0!==e[r]&&t.push(a);return t}(t);return 0===r.length?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} @ref does not support ${r.join(", ")}.`}}}function s3(e){let{session:t,refInput:r,fallbackLabel:a,requireRect:i,invalidRefMessage:n,notFoundMessage:o}=e;if(!t.snapshot)return{ok:!1,response:{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}}};let s=nO(r);if(!s)return{ok:!1,response:{ok:!1,error:{code:"INVALID_ARGS",message:n}}};let l=nM(t.snapshot.nodes,s);return((!l||i&&!l.rect)&&a.length>0&&(l=nC(t.snapshot.nodes,a)),l&&(!i||l.rect))?{ok:!0,target:{ref:s,node:l,snapshotNodes:t.snapshot.nodes}}:{ok:!1,response:{ok:!1,error:{code:"COMMAND_FAILED",message:o}}}}function s4(e){let t=function(e){if(!e)return null;let t=Number(e.x),r=Number(e.y),a=Number(e.width),i=Number(e.height);return Number.isFinite(t)&&Number.isFinite(r)&&Number.isFinite(a)&&Number.isFinite(i)&&!(a<0)&&!(i<0)?{x:t,y:r,width:a,height:i}:null}(e);if(!t)return null;let r=nx(t);return Number.isFinite(r.x)&&Number.isFinite(r.y)?r:null}function s8(e){return{tenantId:e.meta?.tenantId??e.flags?.tenant,runId:e.meta?.runId??e.flags?.runId,leaseId:e.meta?.leaseId??e.flags?.leaseId,leaseTtlMs:e.meta?.leaseTtlMs,leaseBackend:e.meta?.leaseBackend}}async function s6(e){let{req:t,leaseRegistry:r}=e,a=s8(t);switch(t.command){case"lease_allocate":return{ok:!0,data:{lease:r.allocateLease({tenantId:a.tenantId??"",runId:a.runId??"",backend:a.leaseBackend,ttlMs:a.leaseTtlMs})}};case"lease_heartbeat":return{ok:!0,data:{lease:r.heartbeatLease({leaseId:a.leaseId??"",tenantId:a.tenantId,runId:a.runId,ttlMs:a.leaseTtlMs})}};case"lease_release":return{ok:!0,data:r.releaseLease({leaseId:a.leaseId??"",tenantId:a.tenantId,runId:a.runId})};default:return null}}let s5=new Set(["session_list","devices","ensure-simulator","release_materialized_paths"]),s9=new Set(["session_list","devices","ensure-simulator","release_materialized_paths","lease_allocate","lease_heartbeat","lease_release"]);function s7(e,t,r,a){let i=_().requestId;return{...nN(e,t,r,a,i),requestId:i}}function le(e){M.existsSync(e)&&M.unlinkSync(e)}function lt(e){if(!M.existsSync(e))return null;try{let t=JSON.parse(M.readFileSync(e,"utf8"));if(!Number.isInteger(t.pid)||t.pid<=0)return null;return t}catch{return null}}function lr(e){let t=lt(e);if(!t||t.pid===process.pid)try{M.existsSync(e)&&M.unlinkSync(e)}catch{}}function la(e){if(void 0===e)return;let t=Number(e);if(Number.isInteger(t))return t}let{baseDir:li,infoPath:ln,lockPath:lo,logPath:ls,sessionsDir:ll}=P(process.env.AGENT_DEVICE_STATE_DIR),ld=T(process.env.AGENT_DEVICE_DAEMON_SERVER_MODE);var lu=ll;if(M.existsSync(lu))for(let e of M.readdirSync(lu,{withFileTypes:!0})){if(!e.isDirectory())continue;let t=i.join(lu,e.name,"app-log.pid");if(M.existsSync(t))try{let e=function(e){let t=e.trim();if(!t)return null;if(/^\d+$/.test(t))return{pid:Number.parseInt(t,10)};try{let e=JSON.parse(t);if(!Number.isInteger(e.pid)||e.pid<=0)return null;return e}catch{return null}}(M.readFileSync(t,"utf8"));if(e&&function(e){let t,r=L(e.pid);if(!r||e.startTime&&r!==e.startTime)return!1;let a=n(e.pid);return!!a&&!!((t=a.toLowerCase().replaceAll("\\","/")).includes("log stream")||t.includes("logcat")||t.includes("devicectl device log stream"))&&(!e.command||a===e.command)}(e))try{process.kill(e.pid,"SIGTERM")}catch{}}catch{}finally{t_(t)}}let lc=new tb(ll),lp=new ri({maxActiveSimulatorLeases:la(process.env.AGENT_DEVICE_MAX_SIMULATOR_LEASES),defaultLeaseTtlMs:la(process.env.AGENT_DEVICE_LEASE_TTL_MS),minLeaseTtlMs:la(process.env.AGENT_DEVICE_LEASE_MIN_TTL_MS),maxLeaseTtlMs:la(process.env.AGENT_DEVICE_LEASE_MAX_TTL_MS)}),lf=b(),lm=u.randomBytes(24).toString("hex"),lh=L(process.pid)??void 0,lw=function(){let e=process.argv[1];if(!e)return"unknown";try{let t=M.statSync(e),r=N(),a=i.relative(r,e)||e;return`${a}:${t.size}:${Math.trunc(t.mtimeMs)}`}catch{return"unknown"}}(),lg=function(e){let{logPath:t,token:r,sessionStore:a,leaseRegistry:n,trackDownloadableArtifact:s}=e,l=e.dispatchCommand??nA;async function d(e){let u="click"===e.command?{...e,command:"press"}:e,c=!!(u.meta?.debug||u.flags?.verbose);return await O({session:u.session,requestId:u.meta?.requestId,command:u.command,debug:c,logPath:t},async()=>{if(u.token!==r)return{ok:!1,error:A(new h("UNAUTHORIZED","Invalid token"))};try{let e=function(e){let t=y(e.meta?.sessionIsolation??e.flags?.sessionIsolation),r=e.meta?.tenantId??e.flags?.tenant,a=o(r);if(r&&!a)throw new h("INVALID_ARGS","Invalid tenant id. Use 1-128 chars: letters, numbers, dot, underscore, hyphen.");if("tenant"!==t)return e;if(!a)throw new h("INVALID_ARGS","session isolation mode tenant requires --tenant (or meta.tenantId).");return{...e,session:`${a}:${e.session||"default"}`,meta:{...e.meta,tenantId:a,sessionIsolation:t}}}(u);D({level:"info",phase:"request_start",data:{session:e.session,command:e.command,tenant:e.meta?.tenantId,isolation:e.meta?.sessionIsolation}});let r=e.command,c=t=>(function(e,t,r){let a=_();if(!t.ok){D({level:"error",phase:"request_failed",data:{code:t.error.code,message:t.error.message}});let e=x({force:!0})??void 0;return{ok:!1,error:A(new h(t.error.code,t.error.message,{...t.error.details??{},hint:t.error.hint,diagnosticId:t.error.diagnosticId,logPath:t.error.logPath}),{diagnosticId:a.diagnosticId,logPath:e})}}return D({level:"info",phase:"request_success"}),x(),{ok:!0,data:function(e,t,r){var a,n;let o;if(!t)return t;let s=(a=e,n=t,o=Array.isArray(n.artifacts)?[...n.artifacts]:[],"screenshot"!==a.command||o.some(e=>e?.field==="path")||"string"!=typeof n.path||o.push({field:"path",path:n.path,localPath:a.meta?.clientArtifactPaths?.path,fileName:i.basename(a.meta?.clientArtifactPaths?.path??n.path)}),o.filter(e=>!!(e&&"string"==typeof e.field&&"string"==typeof e.path&&"string"==typeof e.localPath&&e.localPath.length>0)));return 0===s.length?t:{...t,artifacts:s.map(t=>{let a=t.path;return{field:t.field,artifactId:r({artifactPath:a,tenantId:e.meta?.tenantId,fileName:t.fileName}),fileName:t.fileName,localPath:t.localPath}})}}(e,t.data,r)}})(e,t,s),p=s8(e);s9.has(r)||e.meta?.sessionIsolation!=="tenant"||n.assertLeaseAdmission({tenantId:p.tenantId,runId:p.runId,leaseId:p.leaseId,backend:p.leaseBackend});let f=function(e,t){var r;let a,i=e.session||"default";if(r=e,a=r.flags?.session,"string"==typeof a&&a.trim().length>0||"default"!==i||t.has(i))return i;let n=t.toArray();return 1===n.length?n[0].name:i}(e,a),m=a.get(f);m&&!s5.has(r)&&function(e,t){if(!t)return;let r=[],a=e.device,i=eW(t.platform);if(i&&i!==a.platform&&r.push(`--platform=${t.platform}`),t.target&&t.target!==(a.target??"mobile")&&r.push(`--target=${t.target}`),t.udid&&("ios"!==a.platform||t.udid!==a.id)&&r.push(`--udid=${t.udid}`),t.serial&&("android"!==a.platform||t.serial!==a.id)&&r.push(`--serial=${t.serial}`),t.device&&t.device.trim().toLowerCase()!==a.name.trim().toLowerCase()&&r.push(`--device=${t.device}`),t.iosSimulatorDeviceSet){let e=t.iosSimulatorDeviceSet.trim(),i=a.simulatorSetPath?.trim();("ios"!==a.platform||"simulator"!==a.kind||e!==i)&&r.push(`--ios-simulator-device-set=${t.iosSimulatorDeviceSet}`)}if(t.androidDeviceAllowlist){let e=eS(t.androidDeviceAllowlist);"android"===a.platform&&e.has(a.id)||r.push(`--android-device-allowlist=${t.androidDeviceAllowlist}`)}if(0!==r.length){var n;let t,a,i;throw new h("INVALID_ARGS",`Session "${e.name}" is bound to ${(t=(n=e).device.platform,a=n.device.name.trim(),i=n.device.id,`${t} device "${a}" (${i})`)} and cannot be used with ${r.join(", ")}. Use a different --session name or close this session first.`)}}(m,e.flags);let w=await s6({req:e,leaseRegistry:n});if(w)return c(w);let g=await sS({req:e,sessionName:f,logPath:t,sessionStore:a,invoke:d});if(g)return c(g);let I=await sP({req:e,sessionName:f,logPath:t,sessionStore:a});if(I)return c(I);let v=await sK({req:e,sessionName:f,sessionStore:a,logPath:t});if(v)return c(v);let b=await sH({req:e,sessionName:f,logPath:t,sessionStore:a,invoke:d});if(b)return c(b);let N=await sQ({req:e,sessionName:f,sessionStore:a,contextFromFlags:(e,r,a)=>s7(t,e,r,a)});if(N)return c(N);let S=a.get(f);if(!S)return c({ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}});if(!nb(r,S.device))return c({ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:`${r} is not supported on this device`}});let E=e.positionals??[],k=e.flags?.out,L="screenshot"===r&&E[0]?[tb.expandHome(E[0],e.meta?.cwd),...E.slice(1)]:E,O="screenshot"===r&&k?tb.expandHome(k,e.meta?.cwd):k,M="screenshot"===r?L:E,C="screenshot"===r&&O?{...e.flags??{},out:O}:e.flags??{},R=await l(S.device,r,L,O,{...s7(t,e.flags,S.appBundleId,S.trace?.outPath)});return a.recordAction(S,{command:r,positionals:M,flags:C,result:R??{}}),c({ok:!0,data:R??{}})}catch(r){D({level:"error",phase:"request_failed",data:{error:r instanceof Error?r.message:String(r)}});let e=_(),t=x({force:!0})??void 0;return{ok:!1,error:A(r,{diagnosticId:e.diagnosticId,logPath:t})}}})}return d}({logPath:ls,token:lm,sessionStore:lc,leaseRegistry:lp,trackDownloadableArtifact:function(e){let t=u.randomUUID(),r=setTimeout(()=>{tV(t)},9e5);return r.unref(),tU.set(t,{artifactPath:e.artifactPath,tenantId:e.tenantId,fileName:e.fileName,deleteAfterDownload:!1!==e.deleteAfterDownload,timer:r}),t}});!async function(){let e,t;if(!function(e,t,r){M.existsSync(e)||M.mkdirSync(e,{recursive:!0});let a=JSON.stringify(r,null,2),i=()=>{try{return M.writeFileSync(t,a,{flag:"wx",mode:384}),!0}catch(e){if("EEXIST"===e.code)return!1;throw e}};if(i())return!0;let n=lt(t);if(n?.pid&&n.pid!==process.pid&&s(n.pid,n.processStartTime))return!1;try{M.unlinkSync(t)}catch{}return i()}(li,lo,{pid:process.pid,version:lf,startedAt:Date.now(),processStartTime:lh})){process.stderr.write("Daemon lock is held by another process; exiting.\n"),process.exit(0);return}let r=[];try{var a;let i;if("socket"===ld||"dual"===ld){let t=R.createServer(e=>{let t="",r=0,a=new Set,i=!1,n=()=>{if(!i&&0!==r){for(let e of(i=!0,a))es(e);D({level:"warn",phase:"request_client_disconnected",data:{inFlightRequests:r}}),(async()=>{let e=Date.now()+15e3;for(;r>0&&Date.now()<e&&(await tr(),!(r<=0));)await new Promise(e=>setTimeout(e,200))})()}};e.setEncoding("utf8"),e.on("close",n),e.on("error",n),e.on("data",async i=>{let n=(t+=i).indexOf("\n");for(;-1!==n;){let i,o,s=t.slice(0,n).trim();if(t=t.slice(n+1),0===s.length){n=t.indexOf("\n");continue}r+=1;try{let e=JSON.parse(s);if(o=en(e.meta?.requestId,"socket"),e.meta={...e.meta,requestId:o},a.add(o),eo(o),ed(o))throw ec();i=await lg(e)}catch(e){i={ok:!1,error:A(e)}}finally{r-=1,o&&(a.delete(o),el(o))}e.destroyed||e.write(`${JSON.stringify(i)}
|
|
37
|
-
`),n=t.indexOf("\n")}})});r.push(t),e=await new Promise((e,r)=>{t.once("error",r),t.listen(0,"127.0.0.1",()=>{t.off("error",r);let a=t.address();"object"==typeof a&&a?.port?e(a.port):r(new h("COMMAND_FAILED","Failed to bind socket server"))})})}if("http"===
|
|
36
|
+
`,M.appendFileSync(r,e,"utf8"),{ok:!0,data:{path:r,marked:!0}}}if("clear"===t){if(e.appLog&&!r)return{ok:!1,error:{code:"INVALID_ARGS",message:"logs clear requires logs to be stopped first; run logs stop"}};if(r){if(!e.appBundleId)return{ok:!1,error:{code:"INVALID_ARGS",message:"logs clear --restart requires an app session; run open <app> first"}};if(!nb("logs",e.device))return{ok:!1,error:A(new h("UNSUPPORTED_OPERATION","logs is not supported on this device"))}}let t=o.resolveAppLogPath(i);if(r){e.appLog&&await w.stop(e.appLog);let r=tF(t),a=o.resolveAppLogPidPath(i);try{let n=await w.start(e.device,e.appBundleId,t,a),s={...e,appLog:{platform:e.device.platform,backend:n.backend,outPath:t,startedAt:n.startedAt,getState:n.getState,stop:n.stop,wait:n.wait}};return o.set(i,s),{ok:!0,data:{...r,restarted:!0}}}catch(r){let t=A(r);return o.set(i,{...e,appLog:void 0}),{ok:!1,error:t}}}return{ok:!0,data:tF(t)}}if("start"===t){if(e.appLog)return{ok:!1,error:{code:"INVALID_ARGS",message:"app log already streaming; run logs stop first"}};if(!e.appBundleId)return{ok:!1,error:{code:"INVALID_ARGS",message:"logs start requires an app session; run open <app> first"}};if(!nb("logs",e.device))return{ok:!1,error:A(new h("UNSUPPORTED_OPERATION","logs is not supported on this device"))};let t=o.resolveAppLogPath(i),r=o.resolveAppLogPidPath(i);try{let a=await w.start(e.device,e.appBundleId,t,r),n={...e,appLog:{platform:e.device.platform,backend:a.backend,outPath:t,startedAt:a.startedAt,getState:a.getState,stop:a.stop,wait:a.wait}};return o.set(i,n),{ok:!0,data:{path:t,started:!0}}}catch(e){return{ok:!1,error:A(e)}}}if("stop"===t){if(!e.appLog)return{ok:!1,error:{code:"INVALID_ARGS",message:"no app log stream active"}};let t=e.appLog.outPath;return await w.stop(e.appLog),o.set(i,{...e,appLog:void 0}),{ok:!0,data:{path:t,stopped:!0}}}}if("network"===O){let e=o.get(i);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"network requires an active session"}};let t=(a.positionals?.[0]??"dump").toLowerCase();if(!st.includes(t))return{ok:!1,error:{code:"INVALID_ARGS",message:sr}};let r=a.positionals?.[1],n=r?Number.parseInt(r,10):25;if(!Number.isInteger(n)||n<1||n>200)return{ok:!1,error:{code:"INVALID_ARGS",message:"network dump limit must be an integer in range 1..200"}};let s=(a.positionals?.[2]??"summary").toLowerCase();if(!sa.includes(s))return{ok:!1,error:{code:"INVALID_ARGS",message:si}};let l=function(e,t){let r=on(t?.maxEntries,25,1,200),a=t?.include??"summary",i=on(t?.maxPayloadChars,2048,64,16384),n=on(t?.maxScanLines,4e3,100,2e4);if(!M.existsSync(e))return{path:e,exists:!1,scannedLines:0,matchedLines:0,entries:[],include:a,limits:{maxEntries:r,maxPayloadChars:i,maxScanLines:n}};let o=M.readFileSync(e,"utf8").split("\n"),s=Math.max(0,o.length-n),l=o.slice(s),d=[];for(let e=l.length-1;e>=0&&d.length<r;e-=1){let t=l[e]?.trim();if(!t)continue;let r=function(e,t,r,a){let i=function(e){let t=e.indexOf("{");if(t<0)return null;let r=e.lastIndexOf("}");if(r<=t)return null;let a=e.slice(t,r+1);try{let e=JSON.parse(a);return e&&"object"==typeof e?e:null}catch{return null}}(e),n=ot(i,["method","httpMethod"]),o=ot(i,["url","requestUrl"]),s=function(e,t){if(!e)return null;for(let r of t){let t=e[r];if("number"==typeof t&&Number.isInteger(t))return t;if("string"==typeof t&&/^\d{3}$/.test(t.trim()))return Number.parseInt(t.trim(),10)}return null}(i,["status","statusCode","responseCode"]),l=n9.exec(e),d=/\bmethod["'=: ]+([A-Z]+)\b/i.exec(e),u=(n??d?.[1]??l?.[1])?.toUpperCase(),c=n7.exec(e),p=o??c?.[0];if(!p)return null;let f={method:u,url:p,status:s??function(e){for(let t of oe){let r=t.exec(e);if(!r)continue;let a=Number.parseInt(r[1]??"",10);if(Number.isInteger(a))return a}return null}(e)??void 0,timestamp:function(e){let t=/\b\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z)?\b/.exec(e);return t?.[0]}(e),raw:oi(e,a),line:t};if("headers"===r||"all"===r){let t=function(e,t){if(t){let e=t.headers??t.requestHeaders??t.responseHeaders;if(void 0!==e)return oa(e)}let r=/\bheaders?["'=: ]+(\{.*\})/i.exec(e);return r?.[1]?.trim()}(e,i);t&&(f.headers=oi(t,a))}if("body"===r||"all"===r){let t=or(e,i,["requestBody","body","payload","request"]),r=or(e,i,["responseBody","response"]);t&&(f.requestBody=oi(t,a)),r&&(f.responseBody=oi(r,a))}return f}(t,s+e+1,a,i);r&&d.push(r)}return{path:e,exists:!0,scannedLines:l.length,matchedLines:d.length,entries:d,include:a,limits:{maxEntries:r,maxPayloadChars:i,maxScanLines:n}}}(o.resolveAppLogPath(i),{maxEntries:n,include:s,maxPayloadChars:2048,maxScanLines:4e3}),d=e.appLog?.backend??("ios"===e.device.platform?"device"===e.device.kind?"ios-device":"ios-simulator":"android"),u=[];return e.appLog||u.push("Capture uses the session app log file. For fresh traffic, run logs clear --restart before reproducing requests."),0===l.entries.length&&u.push("No HTTP(s) entries were found in recent session app logs."),{ok:!0,data:{...l,active:!!e.appLog,state:e.appLog?.getState()??"inactive",backend:d,notes:u}}}if("batch"===O)return await sD(a,i,s);if("close"===O){let e=o.get(i);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}};e.appLog&&await w.stop(e.appLog),a.positionals&&a.positionals.length>0&&("ios"===e.device.platform&&await k(e.device.id),await S(e.device,"close",a.positionals,a.flags?.out,{...nN(n,a.flags,e.appBundleId,e.trace?.outPath)}),await L(e.device,o1)),"ios"===e.device.platform&&await k(e.device.id),of(o.getRuntimeHints(i))&&e.appBundleId&&await y({device:e.device,appId:e.appBundleId}).catch(()=>{}),o.recordAction(e,{command:O,positionals:a.positionals??[],flags:a.flags??{},result:{session:i}}),a.flags?.saveScript&&(e.recordSession=!0),o.writeSessionLog(e),await o$(i).catch(()=>{}),o.delete(i);let t=await su({device:e.device,shutdownRequested:a.flags?.shutdown,shutdownSimulator:N??im,shutdownAndroidEmulator:_??sd});return t?{ok:!0,data:{session:i,shutdown:t}}:{ok:!0,data:{session:i}}}return null}async function sD(e,t,r){let a=e.flags?.batchOnError??"stop";if("stop"!==a)return{ok:!1,error:{code:"INVALID_ARGS",message:`Unsupported batch on-error mode: ${a}.`}};let i=e.flags?.batchMaxSteps??U;if(!Number.isInteger(i)||i<1||i>1e3)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid batch max-steps: ${String(e.flags?.batchMaxSteps)}`}};try{let a=G(e.flags?.batchSteps,i),n=Date.now(),o=[];for(let i=0;i<a.length;i+=1){let n=a[i],s=await sE(e,t,n,r,i+1);if(!s.ok)return{ok:!1,error:{code:s.error.code,message:`Batch failed at step ${s.step} (${n.command}): ${s.error.message}`,hint:s.error.hint,diagnosticId:s.error.diagnosticId,logPath:s.error.logPath,details:{...s.error.details??{},step:s.step,command:n.command,positionals:n.positionals,executed:i,total:a.length,partialResults:o}}};o.push(s.result)}return{ok:!0,data:{total:a.length,executed:a.length,totalDurationMs:Date.now()-n,results:o}}}catch(t){let e=f(t);return{ok:!1,error:{code:e.code,message:e.message,details:e.details}}}}async function sE(e,t,r,a,i){let n=Date.now(),o=await a({token:e.token,session:t,command:r.command,positionals:r.positionals,flags:function(e,t){let r={...t??{}};delete r.batchSteps,delete r.batchOnError,delete r.batchMaxSteps;let a=e??{};for(let e of oJ)void 0===r[e]&&void 0!==a[e]&&(r[e]=a[e]);return r}(e.flags,r.flags),runtime:r.runtime,meta:e.meta}),s=Date.now()-n;return o.ok?{ok:!0,step:i,result:{step:i,command:r.command,ok:!0,data:o.data??{},durationMs:s}}:{ok:!1,step:i,error:o.error}}function sk(e,t,r,a){if(e.ok)return e;let i=r+1,n=function(e){let t;return t=(e.positionals??[]).map(e=>tw(e)),[e.command,...t].join(" ")}(t),o={...e.error.details??{},replayPath:a,step:i,action:t.command,positionals:t.positionals??[]};return{ok:!1,error:{code:e.error.code,message:`Replay failed at step ${i} (${n}): ${e.error.message}`,hint:e.error.hint,diagnosticId:e.error.diagnosticId,logPath:e.error.logPath,details:o}}}function sL(e,t){let r={...t??{}},a=e??{};for(let e of oK)void 0===r[e]&&void 0!==a[e]&&(r[e]=a[e]);return r}async function sO(e){let{action:t,sessionName:r,logPath:a,sessionStore:i,dispatch:n}=e;if(!(th(t.command)||["fill","get","is","wait"].includes(t.command)))return null;let o=i.get(r);if(!o)return null;let s=th(t.command)||"fill"===t.command,l=th(t.command)||"fill"===t.command||"get"===t.command&&t.positionals?.[0]==="text",d=await sM(o,t,a,s,n,i);for(let e of function(e){let t=[],r=Array.isArray(e.result?.selectorChain)&&e.result?.selectorChain.every(e=>"string"==typeof e)?e.result.selectorChain:[];if(t.push(...r),th(e.command)){let r=e.positionals?.[0]??"";r&&!r.startsWith("@")&&t.push(e.positionals.join(" "))}if("fill"===e.command){let r=e.positionals?.[0]??"";r&&!r.startsWith("@")&&Number.isNaN(Number(r))&&t.push(r)}if("get"===e.command){let r=e.positionals?.[1]??"";r&&!r.startsWith("@")&&t.push(e.positionals.slice(1).join(" "))}if("is"===e.command){let{split:r}=nK(e.positionals);r&&t.push(r.selectorExpression)}if("wait"===e.command){let{selectorExpression:r}=oM(e.positionals??[]);r&&t.push(r)}let a="string"==typeof e.result?.refLabel?e.result.refLabel.trim():"";if(a.length>0){let r=JSON.stringify(a);"fill"===e.command?(t.push(`id=${r} editable=true`),t.push(`label=${r} editable=true`),t.push(`text=${r} editable=true`),t.push(`value=${r} editable=true`)):(t.push(`id=${r}`),t.push(`label=${r}`),t.push(`text=${r}`),t.push(`value=${r}`))}return tc(t).filter(e=>e.trim().length>0)}(t)){let r=nq(e);if(!r)continue;let a=nH(d.nodes,r,{platform:o.device.platform,requireRect:s,requireUnique:!0,disambiguateAmbiguous:l});if(!a)continue;let i=nY(a.node,o.device.platform,{action:th(t.command)?"click":"fill"===t.command?"fill":"get"}).join(" || ");if(th(t.command))return{...t,positionals:[i]};if("fill"===t.command){let e=tu(t);if(!e)continue;return{...t,positionals:[i,e]}}if("get"===t.command){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)continue;return{...t,positionals:[e,i]}}if("is"===t.command){let{predicate:e,split:r}=nK(t.positionals);if(!e)continue;let a=r?.rest.join(" ").trim()??"",n=[e,i];return"text"===e&&a.length>0&&n.push(a),{...t,positionals:n}}if("wait"===t.command){let{selectorTimeout:e}=oM(t.positionals??[]),r=[i];return e&&r.push(e),{...t,positionals:r}}}let u=function(e,t,r){if("get"!==e.command||e.positionals?.[0]!=="text")return null;let a=e.positionals?.[1];if(!a)return null;let i=nq(a);if(!i)return null;let n=new Set,o=!1;for(let e of i.selectors)for(let t of e.terms)"role"===t.key&&"string"==typeof t.value&&n.add(n$(t.value)),("text"===t.key||"label"===t.key||"value"===t.key)&&"string"==typeof t.value&&/^\d+$/.test(t.value.trim())&&(o=!0);if(!o)return null;let s=t.nodes.filter(e=>{let t=nU(e).trim();return!!/^\d+$/.test(t)&&(0===n.size||n.has(n$(e.type??"")))});if(0===s.length||1!==tc(s.map(e=>nU(e).trim())).length)return null;let l=s[0];if(!l)return null;let d=nY(l,r.device.platform,{action:"get"});return 0===d.length?null:{...e,positionals:["text",d.join(" || ")]}}(t,d,o);return u||null}async function sM(e,t,r,a,i,n){let o=await i(e.device,"snapshot",[],t.flags?.out,{...nN(r,{...t.flags??{},snapshotInteractiveOnly:a,snapshotCompact:a},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[],l={nodes:nL(t.flags?.snapshotRaw?s:nP(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend};return e.snapshot=l,n.set(e.name,e),l}function sx(e){if(!e)return null;let t=Number(e);return Number.isFinite(t)?t:null}function sC(e,t){let r=V(e.type??"Element"),a=B(e,r),i=!1===e.enabled?"disabled":"enabled",n=!0===e.selected?"selected":"unselected",o=!0===e.hittable?"hittable":"not-hittable";return[String(t??e.depth??0),r,a,i,n,o].join("|")}function sR(e,t){return t.flatten?e.map(e=>({text:H(e,0,!1),comparable:sC(e,0)})):F(e).map(e=>({text:e.text,comparable:sC(e.node,e.depth)}))}function sT(e,t){return e.get(t)??0}async function sP(e){let{req:t,sessionName:r,logPath:a,sessionStore:i}=e,n=e.dispatchSnapshotCommand??nA,o=e.runnerCommand??tl,s=t.command;if("snapshot"===s){let{session:e,device:o}=await sU(i,r,t.flags);if(!nb("snapshot",o))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"snapshot is not supported on this device"}};let s=sF(t.flags?.snapshotScope,e);return s.ok?await sV(e,o,async()=>{let l=e?.appBundleId,d=await s$({dispatchSnapshotCommand:n,device:o,session:e,req:t,logPath:a,snapshotScope:s.scope}),u=e?{...e,snapshot:d.snapshot}:{name:r,device:o,createdAt:Date.now(),appBundleId:l,snapshot:d.snapshot,actions:[]};return sG(i,u,t,{nodes:d.snapshot.nodes.length,truncated:d.snapshot.truncated??!1}),i.set(r,u),{ok:!0,data:{nodes:d.snapshot.nodes,truncated:d.snapshot.truncated??!1,appName:u.appBundleId?u.appName??u.appBundleId:void 0,appBundleId:u.appBundleId}}}):s.response}if("diff"===s){if(t.positionals?.[0]!=="snapshot")return{ok:!1,error:{code:"INVALID_ARGS",message:"diff currently supports only: diff snapshot"}};let{session:e,device:o}=await sU(i,r,t.flags);if(!nb("diff",o))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"diff is not supported on this device"}};let s=sF(t.flags?.snapshotScope,e);if(!s.ok)return s.response;let l=t.flags?.snapshotInteractiveOnly===!0;return await sV(e,o,async()=>{let d=e?.appBundleId,u=(await s$({dispatchSnapshotCommand:n,device:o,session:e,req:t,logPath:a,snapshotScope:s.scope})).snapshot;if(!e?.snapshot){let a=function(e,t={}){return sR(e,t).length}(u.nodes,{flatten:l}),n=e?{...e,snapshot:u}:{name:r,device:o,createdAt:Date.now(),appBundleId:d,snapshot:u,actions:[]};return sG(i,n,t,{mode:"snapshot",baselineInitialized:!0,summary:{additions:0,removals:0,unchanged:a}}),i.set(r,n),{ok:!0,data:{mode:"snapshot",baselineInitialized:!0,summary:{additions:0,removals:0,unchanged:a},lines:[]}}}let c=function(e,t,r={}){let a=function(e,t){let r=e.length,a=t.length,i=r+a,n=new Map,o=[];n.set(1,0);for(let s=0;s<=i;s+=1){o.push(new Map(n));for(let i=-s;i<=s;i+=2){let l=i===-s||i!==s&&sT(n,i-1)<sT(n,i+1)?sT(n,i+1):sT(n,i-1)+1,d=l-i;for(;l<r&&d<a&&e[l].comparable===t[d].comparable;)l+=1,d+=1;if(n.set(i,l),l>=r&&d>=a)return function(e,t,r,a,i){let n=[],o=a,s=i;for(let a=e.length-1;a>=0;a-=1){let i=e[a],l=o-s,d=l===-a||l!==a&&sT(i,l-1)<sT(i,l+1)?l+1:l-1,u=sT(i,d),c=u-d;for(;o>u&&s>c;)n.push({kind:"unchanged",text:r[s-1].text}),o-=1,s-=1;if(0===a)break;o===u?(n.push({kind:"added",text:r[c].text}),s=c):(n.push({kind:"removed",text:t[u].text}),o=u)}return n.reverse(),n}(o,e,t,r,a)}}return[]}(sR(e,r),sR(t,r)),i={additions:0,removals:0,unchanged:0};for(let e of a)"added"===e.kind&&(i.additions+=1),"removed"===e.kind&&(i.removals+=1),"unchanged"===e.kind&&(i.unchanged+=1);return{summary:i,lines:a}}(e.snapshot.nodes,u.nodes,{flatten:l}),p={...e,snapshot:u};return sG(i,p,t,{mode:"snapshot",baselineInitialized:!1,summary:c.summary}),i.set(r,p),{ok:!0,data:{mode:"snapshot",baselineInitialized:!1,summary:c.summary,lines:c.lines}}})}if("wait"===s){let{session:e,device:o}=await sU(i,r,t.flags),s=function(e){if(0===e.length)return null;let t=sx(e[0]);if(null!==t)return{kind:"sleep",durationMs:t};if("text"===e[0]){let t=sx(e[e.length-1]);return{kind:"text",text:(null!==t?e.slice(1,-1).join(" "):e.slice(1).join(" ")).trim(),timeoutMs:t}}if(e[0].startsWith("@")){let t=sx(e[e.length-1]);return{kind:"ref",rawRef:e[0],timeoutMs:t}}let r=sx(e[e.length-1]),a=nJ(null!==r?e.slice(0,-1):e.slice());if(a&&0===a.rest.length){let e=nq(a.selectorExpression);if(e)return{kind:"selector",selector:e,selectorExpression:a.selectorExpression,timeoutMs:r}}return{kind:"text",text:(null!==r?e.slice(0,-1).join(" "):e.join(" ")).trim(),timeoutMs:r}}(t.positionals??[]);return s?"sleep"===s.kind?(await new Promise(e=>setTimeout(e,s.durationMs)),sG(i,e,t,{waitedMs:s.durationMs}),{ok:!0,data:{waitedMs:s.durationMs}}):nb("wait",o)?await sV(e,o,async()=>{let l,d;if("selector"===s.kind){let l=s.timeoutMs??1e4,d=Date.now();for(;Date.now()-d<l;){let l=await n(o,"snapshot",[],t.flags?.out,{...nN(a,{...t.flags,snapshotInteractiveOnly:!1,snapshotCompact:!1},e?.appBundleId,e?.trace?.outPath)}),u=l?.nodes??[],c=nL(t.flags?.snapshotRaw?u:nP(u));e&&(e.snapshot={nodes:c,truncated:l?.truncated,createdAt:Date.now(),backend:l?.backend},i.set(r,e));let p=nW(c,s.selector,{platform:o.platform});if(p)return sG(i,e,t,{selector:p.selector.raw,waitedMs:Date.now()-d}),{ok:!0,data:{selector:p.selector.raw,waitedMs:Date.now()-d}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for selector: ${s.selectorExpression}`}}}if("ref"===s.kind){if(!e?.snapshot)return{ok:!1,error:{code:"INVALID_ARGS",message:"Ref wait requires an existing snapshot in session."}};let t=nO(s.rawRef);if(!t)return{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref: ${s.rawRef}`}};let r=nM(e.snapshot.nodes,t),a=r?nR(r,e.snapshot.nodes):void 0;if(!a)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s.rawRef} not found or has no label`}};l=a,d=s.timeoutMs}else l=s.text,d=s.timeoutMs;if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires text"}};let u=d??1e4,c=Date.now();for(;Date.now()-c<u;){if("ios"===o.platform){let r=await tl(o,{command:"findText",text:l,appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:a,traceLogPath:e?.trace?.outPath,requestId:t.meta?.requestId});if(r?.found)return sG(i,e,t,{text:l,waitedMs:Date.now()-c}),{ok:!0,data:{text:l,waitedMs:Date.now()-c}}}else if("android"===o.platform&&nC(nL((await av(o,{scope:l})).nodes??[]),l))return sG(i,e,t,{text:l,waitedMs:Date.now()-c}),{ok:!0,data:{text:l,waitedMs:Date.now()-c}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:`wait timed out for text: ${l}`}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"wait is not supported on this device"}}:{ok:!1,error:{code:"INVALID_ARGS",message:"wait requires a duration or text"}}}if("alert"===s){let{session:e,device:n}=await sU(i,r,t.flags),s=(t.positionals?.[0]??"get").toLowerCase();return nb("alert",n)?await sV(e,n,async()=>{if("wait"===s){let r=sx(t.positionals?.[1])??1e4,s=Date.now();for(;Date.now()-s<r;){try{let r=await o(n,{command:"alert",action:"get",appBundleId:e?.appBundleId},{verbose:t.flags?.verbose,logPath:a,traceLogPath:e?.trace?.outPath,requestId:t.meta?.requestId});return sG(i,e,t,r),{ok:!0,data:r}}catch{}await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"alert wait timed out"}}}let r="accept"===s||"dismiss"===s?s:"get",l={verbose:t.flags?.verbose,logPath:a,traceLogPath:e?.trace?.outPath,requestId:t.meta?.requestId};if("accept"===r||"dismiss"===r){let a,s=Date.now();for(;Date.now()-s<2e3;){try{let a=await o(n,{command:"alert",action:r,appBundleId:e?.appBundleId},l);return sG(i,e,t,a),{ok:!0,data:a}}catch(t){a=t;let e=String(t?.message??"").toLowerCase();if(!e.includes("alert not found")&&!e.includes("no alert"))break}await new Promise(e=>setTimeout(e,300))}throw a}let d=await o(n,{command:"alert",action:r,appBundleId:e?.appBundleId},l);return sG(i,e,t,d),{ok:!0,data:d}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"alert is only supported on iOS simulators"}}}if("settings"===s){let e=t.positionals?.[0]?.toLowerCase(),n=t.positionals?.[1]?.toLowerCase(),o=t.positionals?.[2]?.toLowerCase();if(!e||!n||"permission"===e&&!o)return{ok:!1,error:{code:"INVALID_ARGS",message:j}};let{session:s,device:l}=await sU(i,r,t.flags);return nb("settings",l)?await sV(s,l,async()=>{let r=s?.appBundleId,d="permission"===e?[e,n,o,t.positionals?.[3]??"",r??""]:[e,n,r??""],u=await nA(l,"settings",d,t.flags?.out,{...nN(a,t.flags,r,s?.trace?.outPath)});return sG(i,s,t,u??{setting:e,state:n}),{ok:!0,data:u??{setting:e,state:n}}}):{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"settings is not supported on this device"}}}return null}async function s$(e){let{dispatchSnapshotCommand:t,device:r,session:a,req:i,logPath:n,snapshotScope:o}=e,s=await t(r,"snapshot",[],i.flags?.out,{...nN(n,{...i.flags,snapshotScope:o},a?.appBundleId,a?.trace?.outPath)}),l=s?.nodes??[];return{snapshot:{nodes:nL(i.flags?.snapshotRaw?l:nP(l)),truncated:s?.truncated,createdAt:Date.now(),backend:s?.backend}}}function sF(e,t){if(!e||!e.trim().startsWith("@"))return{ok:!0,scope:e};if(!t?.snapshot)return{ok:!1,response:{ok:!1,error:{code:"INVALID_ARGS",message:"Ref scope requires an existing snapshot in session."}}};let r=nO(e.trim());if(!r)return{ok:!1,response:{ok:!1,error:{code:"INVALID_ARGS",message:`Invalid ref scope: ${e}`}}};let a=nM(t.snapshot.nodes,r),i=a?nR(a,t.snapshot.nodes):void 0;return i?{ok:!0,scope:i}:{ok:!1,response:{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${e} not found or has no label`}}}}async function sU(e,t,r){let a=e.get(t),i=a?.device??await nv(r??{});return a||await nS(i),{session:a,device:i}}async function sV(e,t,r){let a=!e&&"ios"===t.platform;try{return await r()}finally{a&&await tt(t.id)}}function sG(e,t,r,a){t&&e.recordAction(t,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:a})}function sj(e,t,r,a={}){let i=sq(r);if(!i)return{matches:[],score:0};let n=0,o=[];for(let r of e){if(a.requireRect&&!r.rect)continue;let e=function(e,t,r){switch(t){case"role":return function(e,t){let r=function(e){let t=e.trim();return t?t=(t.split(".").pop()??t).replace(/XCUIElementType/gi,"").toLowerCase():""}(e??"");return r?r===t?2:+!!r.includes(t):0}(e.type,r);case"label":return sB(e.label,r);case"value":return sB(e.value,r);case"id":return sB(e.identifier,r);default:return Math.max(sB(e.label,r),sB(e.value,r),sB(e.identifier,r))}}(r,t,i);if(!(e<=0)){if(e>n){n=e,o.length=0,o.push(r);continue}e===n&&o.push(r)}}return{matches:o,score:n}}function sB(e,t){let r=sq(e??"");return r?r===t?2:+!!r.includes(t):0}function sq(e){return e.trim().toLowerCase().replace(/\s+/g," ")}async function sH(e){let{req:t,sessionName:r,logPath:a,sessionStore:i,invoke:n}=e,o=e.dispatch??nA,s=t.command;if("find"!==s)return null;let l=t.positionals??[];if(0===l.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a locator or text"}};let{locator:d,query:u,action:c,value:p,timeoutMs:f}=function(e){let t="any",r=0;["text","label","value","role","id"].includes(e[0])&&(t=e[0],r=1);let a=e[r]??"",i=e.slice(r+1);if(0===i.length)return{locator:t,query:a,action:"click"};let n=i[0].toLowerCase();if("get"===n){let e=i[1]?.toLowerCase();if("text"===e)return{locator:t,query:a,action:"get_text"};if("attrs"===e)return{locator:t,query:a,action:"get_attrs"};throw new h("INVALID_ARGS","find get only supports text or attrs")}if("wait"===n)return{locator:t,query:a,action:"wait",timeoutMs:sx(i[1])??void 0};if("exists"===n)return{locator:t,query:a,action:"exists"};if("click"===n)return{locator:t,query:a,action:"click"};if("focus"===n)return{locator:t,query:a,action:"focus"};if("fill"===n)return{locator:t,query:a,action:"fill",value:i.slice(1).join(" ")};if("type"===n)return{locator:t,query:a,action:"type",value:i.slice(1).join(" ")};throw new h("INVALID_ARGS",`Unsupported find action: ${i[0]}`)}(l);if(!u)return{ok:!1,error:{code:"INVALID_ARGS",message:"find requires a value"}};let m=i.get(r);if(!m&&"exists"!==c&&"wait"!==c&&"get_text"!==c&&"get_attrs"!==c)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let w=m?.device??await nv(t.flags??{});m||await nS(w);let g=m?.appBundleId,I="role"!==d?u:void 0,v="click"===c||"focus"===c||"fill"===c||"type"===c,A=0,y=null,b=async()=>{let e=Date.now();if(y&&e-A<750)return{nodes:y};let n=await o(w,"snapshot",[],t.flags?.out,{...nN(a,{...t.flags,snapshotScope:I,snapshotInteractiveOnly:v,snapshotCompact:v},g,m?.trace?.outPath)}),s=n?.nodes??[],l=nL(t.flags?.snapshotRaw?s:nP(s));return A=e,y=l,m&&(m.snapshot={nodes:l,truncated:n?.truncated,createdAt:Date.now(),backend:n?.backend},i.set(r,m)),{nodes:l,truncated:n?.truncated,backend:n?.backend}};if("wait"===c){let e=f??1e4,r=Date.now();for(;Date.now()-r<e;){let{nodes:e}=await b();if(sj(e,d,u,{requireRect:!1}).matches[0])return m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0,waitedMs:Date.now()-r}}),{ok:!0,data:{found:!0,waitedMs:Date.now()-r}};await new Promise(e=>setTimeout(e,300))}return{ok:!1,error:{code:"COMMAND_FAILED",message:"find wait timed out"}}}let{nodes:N}=await b(),_=sj(N,d,u,{requireRect:v});if(v&&_.matches.length>1){let e=_.matches.slice(0,8).map(e=>{let t=nU(e)||e.label||e.identifier||e.type||"";return`@${e.ref}${t?`(${t})`:""}`});return{ok:!1,error:{code:"AMBIGUOUS_MATCH",message:`find matched ${_.matches.length} elements for ${d} "${u}". Use a more specific locator or selector.`,details:{locator:d,query:u,matches:_.matches.length,candidates:e}}}}let S=_.matches[0]??null;if(!S)return{ok:!1,error:{code:"COMMAND_FAILED",message:"find did not match any element"}};let D="click"===c||"focus"===c||"fill"===c||"type"===c?function(e,t){if(t.hittable)return t;let r=t,a=new Set;for(;void 0!==r.parentIndex&&!a.has(r.ref);){a.add(r.ref);let t=e[r.parentIndex];if(!t)break;if(t.hittable)return t;r=t}return null}(N,S)??S:S,E=`@${D.ref}`,k={...t.flags??{},noRecord:!0};if("exists"===c)return m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{found:!0}}),{ok:!0,data:{found:!0}};if("get_text"===c){let e=nU(S);return m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{ref:E,action:"get text",text:e}}),{ok:!0,data:{ref:E,text:e,node:S}}}if("get_attrs"===c)return m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{ref:E,action:"get attrs"}}),{ok:!0,data:{ref:E,node:S}};if("click"===c){let e=await n({token:t.token,session:r,command:"click",positionals:[E],flags:k});if(!e.ok)return e;let a=D.rect?nx(D.rect):null,o={ref:E,locator:d,query:u};return a&&(o.x=a.x,o.y=a.y),m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{ref:E,action:"click",locator:d,query:u}}),{ok:!0,data:o}}if("fill"===c){if(!p)return{ok:!1,error:{code:"INVALID_ARGS",message:"find fill requires text"}};let e=await n({token:t.token,session:r,command:"fill",positionals:[E,p],flags:k});return e.ok&&m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{ref:E,action:"fill"}}),e}if("focus"===c){let e=S.rect?nx(S.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};let r=await o(w,"focus",[String(e.x),String(e.y)],t.flags?.out,{...nN(a,t.flags,m?.appBundleId,m?.trace?.outPath)});return m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{ref:E,action:"focus"}}),{ok:!0,data:r??{ref:E}}}if("type"===c){if(!p)return{ok:!1,error:{code:"INVALID_ARGS",message:"find type requires text"}};let e=S.rect?nx(S.rect):null;if(!e)return{ok:!1,error:{code:"COMMAND_FAILED",message:"matched element has no bounds"}};await o(w,"focus",[String(e.x),String(e.y)],t.flags?.out,{...nN(a,t.flags,m?.appBundleId,m?.trace?.outPath)});let r=await o(w,"type",[p],t.flags?.out,{...nN(a,t.flags,m?.appBundleId,m?.trace?.outPath)});return m&&i.recordAction(m,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{ref:E,action:"type"}}),{ok:!0,data:r??{ref:E}}}return null}function sW(e){return e instanceof Error?e.message:String(e)}function sz(e){let t=e.appBundleId?.trim();return t&&t.length>0?t:void 0}function sJ(e,t,r){return{verbose:e.flags?.verbose,logPath:t,traceLogPath:r.trace?.outPath}}async function sK(e){let{req:t,sessionName:r,sessionStore:a,logPath:n}=e,o=e.deps??{runCmd:c,runCmdBackground:d,runIosRunnerCommand:tl},s=t.command;if("record"===s){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"record requires start|stop"}};let d=a.get(r),c=d?.device??await nv(t.flags??{});d||await nS(c);let p=d??{name:r,device:c,createdAt:Date.now(),actions:[]};if("start"===e){if(p.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"recording already in progress"}};let e=t.flags?.fps;if(void 0!==e&&(!Number.isInteger(e)||e<1||e>120))return{ok:!1,error:{code:"INVALID_ARGS",message:"fps must be an integer between 1 and 120"}};let d=t.positionals?.[1]??`./recording-${Date.now()}.mp4`;if(!nb("record",c))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"record is not supported on this device"}};let f="ios"===c.platform&&"device"===c.kind?sz(p):void 0;if("ios"===c.platform&&"device"===c.kind&&!f)return{ok:!1,error:{code:"INVALID_ARGS",message:"record on physical iOS devices requires an active app session; run open <app> first"}};let m=tb.expandHome(d,t.meta?.cwd),h=t.meta?.clientArtifactPaths?.outPath;M.mkdirSync(i.dirname(m),{recursive:!0});let w=sJ(t,n,p);if("ios"===c.platform&&"device"===c.kind){let t=`agent-device-recording-${Date.now()}.mp4`,r=`tmp/${t}`,i=async()=>{await o.runIosRunnerCommand(c,{command:"recordStart",outPath:t,fps:e,appBundleId:f},w)};try{await i()}catch(e){if(!sW(e).toLowerCase().includes("recording already in progress"))return{ok:!1,error:{code:"COMMAND_FAILED",message:`failed to start recording: ${sW(e)}`}};{var l,u;D({level:"warn",phase:"record_start_runner_desynced",data:{platform:c.platform,kind:c.kind,deviceId:c.id,session:p.name,error:sW(e)}});let t=(l=c.id,u=p.name,a.toArray().find(e=>e.name!==u&&"ios"===e.device.platform&&"device"===e.device.kind&&e.device.id===l&&e.recording?.platform==="ios-device-runner"));if(t)return{ok:!1,error:{code:"COMMAND_FAILED",message:`failed to start recording: recording already in progress in session '${t.name}'`}};try{await o.runIosRunnerCommand(c,{command:"recordStop",appBundleId:f},w)}catch{}try{await i()}catch(e){return{ok:!1,error:{code:"COMMAND_FAILED",message:`failed to start recording: ${sW(e)}`}}}}}p.recording={platform:"ios-device-runner",outPath:m,clientOutPath:h,remotePath:r}}else if("ios"===c.platform){let{child:e,wait:t}=o.runCmdBackground("xcrun",ek(c,["io",c.id,"recordVideo",m]),{allowFailure:!0});p.recording={platform:"ios",outPath:m,clientOutPath:h,child:e,wait:t}}else{let e=`/sdcard/agent-device-recording-${Date.now()}.mp4`,{child:t,wait:r}=o.runCmdBackground("adb",["-s",c.id,"shell","screenrecord",e],{allowFailure:!0});p.recording={platform:"android",outPath:m,clientOutPath:h,remotePath:e,child:t,wait:r}}return a.set(r,p),a.recordAction(p,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start"}}),{ok:!0,data:{recording:"started",outPath:h??d}}}if(!p.recording)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active recording"}};let f=p.recording;if("ios-device-runner"===f.platform){let e=sz(p);try{await o.runIosRunnerCommand(c,{command:"recordStop",appBundleId:e},sJ(t,n,p))}catch(e){D({level:"warn",phase:"record_stop_runner_failed",data:{platform:c.platform,kind:c.kind,deviceId:c.id,session:p.name,error:sW(e)}})}let r={stdout:"",stderr:"",exitCode:1};for(let e of e0)if(0===(r=await o.runCmd("xcrun",["devicectl","device","copy","from","--device",c.id,"--source",f.remotePath,"--destination",f.outPath,"--domain-type","appDataContainer","--domain-identifier",e],{allowFailure:!0})).exitCode)break;if(p.recording=void 0,0!==r.exitCode){let e=r.stderr.trim()||r.stdout.trim()||`devicectl exited with code ${r.exitCode}`;return{ok:!1,error:{code:"COMMAND_FAILED",message:`failed to copy recording from device: ${e}`}}}}else{f.child.kill("SIGINT");try{await f.wait}catch{}if("android"===f.platform&&f.remotePath)try{await o.runCmd("adb",["-s",c.id,"pull",f.remotePath,f.outPath],{allowFailure:!0}),await o.runCmd("adb",["-s",c.id,"shell","rm","-f",f.remotePath],{allowFailure:!0})}catch{}p.recording=void 0}a.recordAction(p,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:f.outPath}});let m=[{field:"outPath",path:f.outPath,localPath:f.clientOutPath,fileName:i.basename(f.clientOutPath??f.outPath)}];return{ok:!0,data:{recording:"stopped",outPath:f.outPath,artifacts:m}}}if("trace"===s){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"trace requires start|stop"}};let n=a.get(r);if(!n)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session"}};if("start"===e){if(n.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"trace already in progress"}};let e=t.positionals?.[1]??a.defaultTracePath(n),r=tb.expandHome(e);return M.mkdirSync(i.dirname(r),{recursive:!0}),M.appendFileSync(r,""),n.trace={outPath:r,startedAt:Date.now()},a.recordAction(n,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start",outPath:r}}),{ok:!0,data:{trace:"started",outPath:r}}}if(!n.trace)return{ok:!1,error:{code:"INVALID_ARGS",message:"no active trace"}};let o=n.trace.outPath;if(t.positionals?.[1]){let e=tb.expandHome(t.positionals[1]);M.mkdirSync(i.dirname(e),{recursive:!0}),M.existsSync(o)?M.renameSync(o,e):M.appendFileSync(e,""),o=e}return n.trace=void 0,a.recordAction(n,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:o}}),{ok:!0,data:{trace:"stopped",outPath:o}}}return null}function sX(e,t,r){return t>=e.x&&t<=e.x+e.width&&r>=e.y&&r<=e.y+e.height}function sZ(e){let t=null,r=-1;for(let a of e){let e=a.width*a.height;e>r&&(t=a,r=e)}return t}function sY(e,t,r){return Math.min(r,Math.max(t,Math.round(e)))}async function sQ(e){let{req:t,sessionName:r,sessionStore:a,contextFromFlags:i}=e,n=e.dispatch??nA,o=t.command;if("press"===o){let e=a.get(r);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!nb("press",e.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"press is not supported on this device"}};let s=function(e){if(e.length<2)return null;let t=Number(e[0]),r=Number(e[1]);return Number.isFinite(t)&&Number.isFinite(r)?{x:t,y:r}:null}(t.positionals??[]);if(s){let r=await n(e.device,"press",[String(s.x),String(s.y)],t.flags?.out,{...i(t.flags,e.appBundleId,e.trace?.outPath)});return a.recordAction(e,{command:o,positionals:t.positionals??[String(s.x),String(s.y)],flags:t.flags??{},result:r??{x:s.x,y:s.y}}),{ok:!0,data:r??{x:s.x,y:s.y}}}let l="click",d=t.positionals?.[0]??"";if(d.startsWith("@")){let r=s2("press",t.flags);if(r)return r;let s=t.positionals.length>1?t.positionals.slice(1).join(" ").trim():"",u=s3({session:e,refInput:d,fallbackLabel:s,requireRect:!0,invalidRefMessage:`${o} requires a ref like @e2`,notFoundMessage:`Ref ${d} not found or has no bounds`});if(!u.ok)return u.response;let{ref:c}=u.target,p=u.target.node,f=u.target.snapshotNodes,m=s4(p.rect);if(!m){let r=await s0(e,t.flags,a,i,{interactiveOnly:!0},n),o=nM(r.nodes,c),l=s.length>0?nC(r.nodes,s):null,d=s4(l?.rect),u=s4(o?.rect)?o:d?l:o??l,h=s4(u?.rect);u&&h&&(p=u,f=r.nodes,m=h)}if(!m)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${d} not found or has invalid bounds`}};let h=nR(p,f),w=nY(p,e.device.platform,{action:l}),{x:g,y:I}=m,v=await n(e.device,"press",[String(g),String(I)],t.flags?.out,{...i(t.flags,e.appBundleId,e.trace?.outPath)});return a.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:c,x:g,y:I,refLabel:h,selectorChain:w}}),{ok:!0,data:{...v??{},ref:c,x:g,y:I}}}let u=(t.positionals??[]).join(" ").trim();if(!u)return{ok:!1,error:{code:"INVALID_ARGS",message:`${o} requires @ref, selector expression, or x y coordinates`}};let c=nB(u),p=await s0(e,t.flags,a,i,{interactiveOnly:!0},n),f=await S("selector_resolve",()=>nH(p.nodes,c,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0}),{command:o});if(!f||!f.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:nz(c,f?.diagnostics??[],{unique:!0})}};let m=s4(f.node.rect);if(!m)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Selector ${f.selector.raw} resolved to invalid bounds`}};let{x:h,y:w}=m,g=await n(e.device,"press",[String(h),String(w)],t.flags?.out,{...i(t.flags,e.appBundleId,e.trace?.outPath)}),I=nY(f.node,e.device.platform,{action:l}),v=nR(f.node,p.nodes);return a.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{x:h,y:w,selector:f.selector.raw,selectorChain:I,refLabel:v}}),{ok:!0,data:{...g??{},selector:f.selector.raw,x:h,y:w}}}if("fill"===o){let e=a.get(r);if(e&&!nb("fill",e.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"fill is not supported on this device"}};if(t.positionals?.[0]?.startsWith("@")){if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let r=s2("fill",t.flags);if(r)return r;let s=t.positionals.length>=3?t.positionals[1]:"",l=t.positionals.length>=3?t.positionals.slice(2).join(" "):t.positionals.slice(1).join(" ");if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after ref"}};let d=s3({session:e,refInput:t.positionals[0],fallbackLabel:s,requireRect:!0,invalidRefMessage:"fill requires a ref like @e2",notFoundMessage:`Ref ${t.positionals[0]} not found or has no bounds`});if(!d.ok)return d.response;let{ref:u,node:c,snapshotNodes:p}=d.target;if(!c.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${t.positionals[0]} not found or has no bounds`}};let f=c.type??"",m=f&&!nF(f,e.device.platform)?`fill target ${t.positionals[0]} resolved to "${f}", attempting fill anyway.`:void 0,h=nR(c,p),w=nY(c,e.device.platform,{action:"fill"}),{x:g,y:I}=nx(c.rect),v={...await n(e.device,"fill",[String(g),String(I),l],t.flags?.out,{...i(t.flags,e.appBundleId,e.trace?.outPath)})??{ref:u,x:g,y:I}};return m&&(v.warning=m),a.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{...v,refLabel:h,selectorChain:w}}),{ok:!0,data:v}}if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};let s=nJ(t.positionals??[],{preferTrailingValue:!0});if(s){if(0===s.rest.length)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let r=s.rest.join(" ").trim();if(!r)return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires text after selector"}};let l=nB(s.selectorExpression),d=await s0(e,t.flags,a,i,{interactiveOnly:!0},n),u=await S("selector_resolve",()=>nH(d.nodes,l,{platform:e.device.platform,requireRect:!0,requireUnique:!0,disambiguateAmbiguous:!0}),{command:o});if(!u||!u.node.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:nz(l,u?.diagnostics??[],{unique:!0})}};let c=u.node,p=c.type??"",f=p&&!nF(p,e.device.platform)?`fill target ${u.selector.raw} resolved to "${p}", attempting fill anyway.`:void 0,{x:m,y:h}=nx(u.node.rect),w=await n(e.device,"fill",[String(m),String(h),r],t.flags?.out,{...i(t.flags,e.appBundleId,e.trace?.outPath)}),g=nY(c,e.device.platform,{action:"fill"}),I={...w??{x:m,y:h,text:r},selector:u.selector.raw,selectorChain:g,refLabel:nR(c,d.nodes)};return f&&(I.warning=f),a.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:I}),{ok:!0,data:I}}return{ok:!1,error:{code:"INVALID_ARGS",message:"fill requires x y text, @ref text, or selector text"}}}if("get"===o){let e=t.positionals?.[0];if("text"!==e&&"attrs"!==e)return{ok:!1,error:{code:"INVALID_ARGS",message:"get only supports text or attrs"}};let s=a.get(r);if(!s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!nb("get",s.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"get is not supported on this device"}};let l=t.positionals?.[1]??"";if(l.startsWith("@")){let r=s2("get",t.flags);if(r)return r;let i=s3({session:s,refInput:l,fallbackLabel:t.positionals.length>2?t.positionals.slice(2).join(" ").trim():"",requireRect:!1,invalidRefMessage:"get text requires a ref like @e2",notFoundMessage:`Ref ${l} not found`});if(!i.ok)return i.response;let{ref:n,node:d}=i.target,u=nY(d,s.device.platform,{action:"get"});if("attrs"===e)return a.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:n,selectorChain:u}}),{ok:!0,data:{ref:n,node:d}};let c=nU(d);return a.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:n,text:c,refLabel:c||void 0,selectorChain:u}}),{ok:!0,data:{ref:n,text:c,node:d}}}let d=t.positionals.slice(1).join(" ").trim();if(!d)return{ok:!1,error:{code:"INVALID_ARGS",message:"get requires @ref or selector expression"}};let u=nB(d),c=await s0(s,t.flags,a,i,{interactiveOnly:!1},n),p=await S("selector_resolve",()=>nH(c.nodes,u,{platform:s.device.platform,requireRect:!1,requireUnique:!0,disambiguateAmbiguous:"text"===e}),{command:o});if(!p)return{ok:!1,error:{code:"COMMAND_FAILED",message:nz(u,[],{unique:!0})}};let f=p.node,m=nY(f,s.device.platform,{action:"get"});if("attrs"===e)return a.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{selector:p.selector.raw,selectorChain:m}}),{ok:!0,data:{selector:p.selector.raw,node:f}};let h=nU(f);return a.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{text:h,refLabel:h||void 0,selector:p.selector.raw,selectorChain:m}}),{ok:!0,data:{selector:p.selector.raw,text:h,node:f}}}if("is"===o){let e=(t.positionals?.[0]??"").toLowerCase();if(!["visible","hidden","exists","editable","selected","text"].includes(e))return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires predicate: visible|hidden|exists|editable|selected|text"}};let s=a.get(r);if(!s)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!nb("is",s.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"is is not supported on this device"}};let{split:l}=nK(t.positionals);if(!l)return{ok:!1,error:{code:"INVALID_ARGS",message:"is requires a selector expression"}};let d=l.rest.join(" ").trim();if("text"===e&&!d)return{ok:!1,error:{code:"INVALID_ARGS",message:"is text requires expected text value"}};if("text"!==e&&l.rest.length>0)return{ok:!1,error:{code:"INVALID_ARGS",message:`is ${e} does not accept trailing values`}};let u=nB(l.selectorExpression),c=await s0(s,t.flags,a,i,{interactiveOnly:!1},n);if("exists"===e){let r=nW(c.nodes,u,{platform:s.device.platform});return r?(a.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:r.selector.raw,selectorChain:u.selectors.map(e=>e.raw),pass:!0,matches:r.matches}}),{ok:!0,data:{predicate:e,pass:!0,selector:r.selector.raw,matches:r.matches}}):{ok:!1,error:{code:"COMMAND_FAILED",message:nz(u,[],{unique:!1})}}}let p=await S("selector_resolve",()=>nH(c.nodes,u,{platform:s.device.platform,requireUnique:!0}),{command:"is",predicate:e});if(!p)return{ok:!1,error:{code:"COMMAND_FAILED",message:nz(u,[],{unique:!0})}};let f=function(e){let{predicate:t,node:r,expectedText:a,platform:i}=e,n=nU(r),o=!1;switch(t){case"visible":o=nX(r);break;case"hidden":o=!nX(r);break;case"editable":o=nZ(r,i);break;case"selected":o=!0===r.selected;break;case"text":o=n===(a??"")}let s="text"===t?`expected="${a??""}" actual="${n}"`:`actual=${JSON.stringify({visible:nX(r),editable:nZ(r,i),selected:!0===r.selected})}`;return{pass:o,actualText:n,details:s}}({predicate:e,node:p.node,expectedText:d,platform:s.device.platform});return f.pass?(a.recordAction(s,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{predicate:e,selector:p.selector.raw,selectorChain:u.selectors.map(e=>e.raw),pass:!0,text:"text"===e?f.actualText:void 0}}),{ok:!0,data:{predicate:e,pass:!0,selector:p.selector.raw}}):{ok:!1,error:{code:"COMMAND_FAILED",message:`is ${e} failed for selector ${p.selector.raw}: ${f.details}`}}}if("scrollintoview"===o){let e=a.get(r);if(!e)return{ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}};if(!nb("scrollintoview",e.device))return{ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:"scrollintoview is not supported on this device"}};let s=t.positionals?.[0]??"";if(!s.startsWith("@"))return null;let l=s2("scrollintoview",t.flags);if(l)return l;let d=s3({session:e,refInput:s,fallbackLabel:t.positionals&&t.positionals.length>1?t.positionals.slice(1).join(" ").trim():"",requireRect:!0,invalidRefMessage:"scrollintoview requires a ref like @e2",notFoundMessage:`Ref ${s} not found or has no bounds`});if(!d.ok)return d.response;let{ref:u,node:c,snapshotNodes:p}=d.target;if(!c.rect)return{ok:!1,error:{code:"COMMAND_FAILED",message:`Ref ${s} not found or has no bounds`}};let f=function(e,t){let r=nx(t),a=e.filter(e=>{var t;return!!(t=e.rect)&&Number.isFinite(t.x)&&Number.isFinite(t.y)&&Number.isFinite(t.width)&&Number.isFinite(t.height)}),i=a.filter(e=>{let t=(e.type??"").toLowerCase();return t.includes("application")||t.includes("window")}),n=sZ(i.map(e=>e.rect).filter(e=>sX(e,r.x,r.y)));if(n)return n;let o=sZ(i.map(e=>e.rect));if(o)return o;let s=sZ(a.map(e=>e.rect).filter(e=>sX(e,r.x,r.y)));return s||null}(p,c.rect);if(!f)return{ok:!1,error:{code:"COMMAND_FAILED",message:`scrollintoview could not infer viewport for ${s}`}};let m=function(e,t){var r,a;let i=Math.max(1,t.height),n=Math.max(1,t.width),o=t.y,s=t.y+i,l=t.x,d=t.x+n,u=o+.25*i,c=s-.25*i,p=Math.max(8,.1*n),f=e.y+e.height/2,m=e.x+e.width/2;if(f>=u&&f<=c)return null;let h=Math.round((r=m,a=l+p,Math.min(d-p,Math.max(a,r)))),w=Math.round(o+.86*i),g=Math.round(o+.14*i),I=Math.max(1,Math.abs(w-g));return f>c?{x:h,startY:w,endY:g,count:sY(Math.ceil((f-c)/I),1,50),direction:"down"}:{x:h,startY:g,endY:w,count:sY(Math.ceil((u-f)/I),1,50),direction:"up"}}(c.rect,f),h=nR(c,p),w=nY(c,e.device.platform,{action:"get"});if(!m)return a.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{ref:u,attempts:0,alreadyVisible:!0,refLabel:h,selectorChain:w}}),{ok:!0,data:{ref:u,attempts:0,alreadyVisible:!0}};let g=await n(e.device,"swipe",[String(m.x),String(m.startY),String(m.x),String(m.endY),"16"],t.flags?.out,{...i(t.flags,e.appBundleId,e.trace?.outPath),count:m.count,pauseMs:0,pattern:"one-way"});return a.recordAction(e,{command:o,positionals:t.positionals??[],flags:t.flags??{},result:{...g??{},ref:u,attempts:m.count,direction:m.direction,refLabel:h,selectorChain:w}}),{ok:!0,data:{...g??{},ref:u,attempts:m.count,direction:m.direction}}}return null}async function s0(e,t,r,a,i,n=nA){let o=await n(e.device,"snapshot",[],t?.out,{...a({...t??{},snapshotInteractiveOnly:i.interactiveOnly,snapshotCompact:i.interactiveOnly},e.appBundleId,e.trace?.outPath)}),s=o?.nodes??[];return e.snapshot={nodes:nL(t?.snapshotRaw?s:nP(s)),truncated:o?.truncated,createdAt:Date.now(),backend:o?.backend},r.set(e.name,e),e.snapshot}let s1=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"]];function s2(e,t){let r=function(e){if(!e)return[];let t=[];for(let[r,a]of s1)void 0!==e[r]&&t.push(a);return t}(t);return 0===r.length?null:{ok:!1,error:{code:"INVALID_ARGS",message:`${e} @ref does not support ${r.join(", ")}.`}}}function s3(e){let{session:t,refInput:r,fallbackLabel:a,requireRect:i,invalidRefMessage:n,notFoundMessage:o}=e;if(!t.snapshot)return{ok:!1,response:{ok:!1,error:{code:"INVALID_ARGS",message:"No snapshot in session. Run snapshot first."}}};let s=nO(r);if(!s)return{ok:!1,response:{ok:!1,error:{code:"INVALID_ARGS",message:n}}};let l=nM(t.snapshot.nodes,s);return((!l||i&&!l.rect)&&a.length>0&&(l=nC(t.snapshot.nodes,a)),l&&(!i||l.rect))?{ok:!0,target:{ref:s,node:l,snapshotNodes:t.snapshot.nodes}}:{ok:!1,response:{ok:!1,error:{code:"COMMAND_FAILED",message:o}}}}function s4(e){let t=function(e){if(!e)return null;let t=Number(e.x),r=Number(e.y),a=Number(e.width),i=Number(e.height);return Number.isFinite(t)&&Number.isFinite(r)&&Number.isFinite(a)&&Number.isFinite(i)&&!(a<0)&&!(i<0)?{x:t,y:r,width:a,height:i}:null}(e);if(!t)return null;let r=nx(t);return Number.isFinite(r.x)&&Number.isFinite(r.y)?r:null}function s8(e){return{tenantId:e.meta?.tenantId??e.flags?.tenant,runId:e.meta?.runId??e.flags?.runId,leaseId:e.meta?.leaseId??e.flags?.leaseId,leaseTtlMs:e.meta?.leaseTtlMs,leaseBackend:e.meta?.leaseBackend}}async function s6(e){let{req:t,leaseRegistry:r}=e,a=s8(t);switch(t.command){case"lease_allocate":return{ok:!0,data:{lease:r.allocateLease({tenantId:a.tenantId??"",runId:a.runId??"",backend:a.leaseBackend,ttlMs:a.leaseTtlMs})}};case"lease_heartbeat":return{ok:!0,data:{lease:r.heartbeatLease({leaseId:a.leaseId??"",tenantId:a.tenantId,runId:a.runId,ttlMs:a.leaseTtlMs})}};case"lease_release":return{ok:!0,data:r.releaseLease({leaseId:a.leaseId??"",tenantId:a.tenantId,runId:a.runId})};default:return null}}function s5(e,t){if(!t)return[];let r=[],a=e.device,i=eW(t.platform);if(i&&i!==a.platform&&r.push({key:"platform",value:t.platform}),t.target&&t.target!==(a.target??"mobile")&&r.push({key:"target",value:t.target}),t.udid&&("ios"!==a.platform||t.udid!==a.id)&&r.push({key:"udid",value:t.udid}),t.serial&&("android"!==a.platform||t.serial!==a.id)&&r.push({key:"serial",value:t.serial}),t.device&&t.device.trim().toLowerCase()!==a.name.trim().toLowerCase()&&r.push({key:"device",value:t.device}),t.iosSimulatorDeviceSet){let e=t.iosSimulatorDeviceSet.trim(),i=a.simulatorSetPath?.trim();("ios"!==a.platform||"simulator"!==a.kind||e!==i)&&r.push({key:"iosSimulatorDeviceSet",value:t.iosSimulatorDeviceSet})}if(t.androidDeviceAllowlist){let e=eS(t.androidDeviceAllowlist);"android"===a.platform&&e.has(a.id)||r.push({key:"androidDeviceAllowlist",value:t.androidDeviceAllowlist})}return r}function s9(e){return`${function(e){switch(e){case"iosSimulatorDeviceSet":return"--ios-simulator-device-set";case"androidDeviceAllowlist":return"--android-device-allowlist";default:return`--${e}`}}(e.key)}=${e.value}`}let s7=["target","device","udid","serial","iosSimulatorDeviceSet","androidDeviceAllowlist"],le=new Set(["session_list","devices","ensure-simulator","release_materialized_paths"]),lt=new Set(["session_list","devices","ensure-simulator","release_materialized_paths","lease_allocate","lease_heartbeat","lease_release"]);function lr(e,t,r,a){let i=_().requestId;return{...nN(e,t,r,a,i),requestId:i}}function la(e){M.existsSync(e)&&M.unlinkSync(e)}function li(e){if(!M.existsSync(e))return null;try{let t=JSON.parse(M.readFileSync(e,"utf8"));if(!Number.isInteger(t.pid)||t.pid<=0)return null;return t}catch{return null}}function ln(e){let t=li(e);if(!t||t.pid===process.pid)try{M.existsSync(e)&&M.unlinkSync(e)}catch{}}function lo(e){if(void 0===e)return;let t=Number(e);if(Number.isInteger(t))return t}let{baseDir:ls,infoPath:ll,lockPath:ld,logPath:lu,sessionsDir:lc}=P(process.env.AGENT_DEVICE_STATE_DIR),lp=T(process.env.AGENT_DEVICE_DAEMON_SERVER_MODE);var lf=lc;if(M.existsSync(lf))for(let e of M.readdirSync(lf,{withFileTypes:!0})){if(!e.isDirectory())continue;let t=i.join(lf,e.name,"app-log.pid");if(M.existsSync(t))try{let e=function(e){let t=e.trim();if(!t)return null;if(/^\d+$/.test(t))return{pid:Number.parseInt(t,10)};try{let e=JSON.parse(t);if(!Number.isInteger(e.pid)||e.pid<=0)return null;return e}catch{return null}}(M.readFileSync(t,"utf8"));if(e&&function(e){let t,r=L(e.pid);if(!r||e.startTime&&r!==e.startTime)return!1;let a=n(e.pid);return!!a&&!!((t=a.toLowerCase().replaceAll("\\","/")).includes("log stream")||t.includes("logcat")||t.includes("devicectl device log stream"))&&(!e.command||a===e.command)}(e))try{process.kill(e.pid,"SIGTERM")}catch{}}catch{}finally{t_(t)}}let lm=new tb(lc),lh=new ri({maxActiveSimulatorLeases:lo(process.env.AGENT_DEVICE_MAX_SIMULATOR_LEASES),defaultLeaseTtlMs:lo(process.env.AGENT_DEVICE_LEASE_TTL_MS),minLeaseTtlMs:lo(process.env.AGENT_DEVICE_LEASE_MIN_TTL_MS),maxLeaseTtlMs:lo(process.env.AGENT_DEVICE_LEASE_MAX_TTL_MS)}),lw=b(),lg=u.randomBytes(24).toString("hex"),lI=L(process.pid)??void 0,lv=function(){let e=process.argv[1];if(!e)return"unknown";try{let t=M.statSync(e),r=N(),a=i.relative(r,e)||e;return`${a}:${t.size}:${Math.trunc(t.mtimeMs)}`}catch{return"unknown"}}(),lA=function(e){let{logPath:t,token:r,sessionStore:a,leaseRegistry:n,trackDownloadableArtifact:s}=e,l=e.dispatchCommand??nA;async function d(e){let u="click"===e.command?{...e,command:"press"}:e,c=!!(u.meta?.debug||u.flags?.verbose);return await O({session:u.session,requestId:u.meta?.requestId,command:u.command,debug:c,logPath:t},async()=>{if(u.token!==r)return{ok:!1,error:A(new h("UNAUTHORIZED","Invalid token"))};try{let e=function(e){let t=y(e.meta?.sessionIsolation??e.flags?.sessionIsolation),r=e.meta?.tenantId??e.flags?.tenant,a=o(r);if(r&&!a)throw new h("INVALID_ARGS","Invalid tenant id. Use 1-128 chars: letters, numbers, dot, underscore, hyphen.");if("tenant"!==t)return e;if(!a)throw new h("INVALID_ARGS","session isolation mode tenant requires --tenant (or meta.tenantId).");return{...e,session:`${a}:${e.session||"default"}`,meta:{...e.meta,tenantId:a,sessionIsolation:t}}}(u);D({level:"info",phase:"request_start",data:{session:e.session,command:e.command,tenant:e.meta?.tenantId,isolation:e.meta?.sessionIsolation}});let r=e.command,c=s8(e);lt.has(r)||e.meta?.sessionIsolation!=="tenant"||n.assertLeaseAdmission({tenantId:c.tenantId,runId:c.runId,leaseId:c.leaseId,backend:c.leaseBackend});let p=function(e,t){var r;let a,i=e.session||"default";if(r=e,a=r.flags?.session,"string"==typeof a&&a.trim().length>0||"default"!==i||t.has(i))return i;let n=t.toArray();return 1===n.length?n[0].name:i}(e,a),f=a.get(p),m=function(e,t){let r=e.meta?.lockPolicy;if(!r)return e;let a={...e.flags??{}},i=t?s5(t,a):function(e,t){let r=[],a=eW(t);for(let t of(void 0!==e.platform&&a&&eW(e.platform)!==a&&r.push({key:"platform",value:e.platform}),s7)){let a=e[t];"string"==typeof a&&a.trim().length>0&&r.push({key:t,value:a})}return r}(a,e.meta?.lockPlatform);if(0===i.length)return!t&&e.meta?.lockPlatform&&void 0===a.platform&&(a.platform=e.meta.lockPlatform),{...e,flags:a};if("strip"===r)return t?(function(e,t){for(let r of t)delete e[r.key]}(a,i),a.platform=t.device.platform):function(e,t){for(let t of s7)delete e[t];t&&(e.platform=t)}(a,e.meta?.lockPlatform),{...e,flags:a};throw new h("INVALID_ARGS",`${e.command} cannot override session lock policy with ${i.map(s9).join(", ")}. Unset those selectors or remove the request lock policy.`)}(e,f),w=e=>(function(e,t,r){let a=_();if(!t.ok){D({level:"error",phase:"request_failed",data:{code:t.error.code,message:t.error.message}});let e=x({force:!0})??void 0;return{ok:!1,error:A(new h(t.error.code,t.error.message,{...t.error.details??{},hint:t.error.hint,diagnosticId:t.error.diagnosticId,logPath:t.error.logPath}),{diagnosticId:a.diagnosticId,logPath:e})}}return D({level:"info",phase:"request_success"}),x(),{ok:!0,data:function(e,t,r){var a,n;let o;if(!t)return t;let s=(a=e,n=t,o=Array.isArray(n.artifacts)?[...n.artifacts]:[],"screenshot"!==a.command||o.some(e=>e?.field==="path")||"string"!=typeof n.path||o.push({field:"path",path:n.path,localPath:a.meta?.clientArtifactPaths?.path,fileName:i.basename(a.meta?.clientArtifactPaths?.path??n.path)}),o.filter(e=>!!(e&&"string"==typeof e.field&&"string"==typeof e.path&&"string"==typeof e.localPath&&e.localPath.length>0)));return 0===s.length?t:{...t,artifacts:s.map(t=>{let a=t.path;return{field:t.field,artifactId:r({artifactPath:a,tenantId:e.meta?.tenantId,fileName:t.fileName}),fileName:t.fileName,localPath:t.localPath}})}}(e,t.data,r)}})(m,e,s);!f||m.meta?.lockPolicy||le.has(r)||function(e,t){let r=s5(e,t);if(0!==r.length){var a;let t,i,n;throw new h("INVALID_ARGS",`Session "${e.name}" is bound to ${(t=(a=e).device.platform,i=a.device.name.trim(),n=a.device.id,`${t} device "${i}" (${n})`)} and cannot be used with ${r.map(s9).join(", ")}. Use a different --session name or close this session first.`)}}(f,m.flags);let g=await s6({req:m,leaseRegistry:n});if(g)return w(g);let I=await sS({req:m,sessionName:p,logPath:t,sessionStore:a,invoke:d});if(I)return w(I);let v=await sP({req:m,sessionName:p,logPath:t,sessionStore:a});if(v)return w(v);let b=await sK({req:m,sessionName:p,sessionStore:a,logPath:t});if(b)return w(b);let N=await sH({req:m,sessionName:p,logPath:t,sessionStore:a,invoke:d});if(N)return w(N);let S=await sQ({req:m,sessionName:p,sessionStore:a,contextFromFlags:(e,r,a)=>lr(t,e,r,a)});if(S)return w(S);let E=a.get(p);if(!E)return w({ok:!1,error:{code:"SESSION_NOT_FOUND",message:"No active session. Run open first."}});if(!nb(r,E.device))return w({ok:!1,error:{code:"UNSUPPORTED_OPERATION",message:`${r} is not supported on this device`}});let k=m.positionals??[],L=m.flags?.out,O="screenshot"===r&&k[0]?[tb.expandHome(k[0],m.meta?.cwd),...k.slice(1)]:k,M="screenshot"===r&&L?tb.expandHome(L,m.meta?.cwd):L,C="screenshot"===r?O:k,R="screenshot"===r&&M?{...m.flags??{},out:M}:m.flags??{},T=await l(E.device,r,O,M,{...lr(t,m.flags,E.appBundleId,E.trace?.outPath)});return a.recordAction(E,{command:r,positionals:C,flags:R,result:T??{}}),w({ok:!0,data:T??{}})}catch(r){D({level:"error",phase:"request_failed",data:{error:r instanceof Error?r.message:String(r)}});let e=_(),t=x({force:!0})??void 0;return{ok:!1,error:A(r,{diagnosticId:e.diagnosticId,logPath:t})}}})}return d}({logPath:lu,token:lg,sessionStore:lm,leaseRegistry:lh,trackDownloadableArtifact:function(e){let t=u.randomUUID(),r=setTimeout(()=>{tV(t)},9e5);return r.unref(),tU.set(t,{artifactPath:e.artifactPath,tenantId:e.tenantId,fileName:e.fileName,deleteAfterDownload:!1!==e.deleteAfterDownload,timer:r}),t}});!async function(){let e,t;if(!function(e,t,r){M.existsSync(e)||M.mkdirSync(e,{recursive:!0});let a=JSON.stringify(r,null,2),i=()=>{try{return M.writeFileSync(t,a,{flag:"wx",mode:384}),!0}catch(e){if("EEXIST"===e.code)return!1;throw e}};if(i())return!0;let n=li(t);if(n?.pid&&n.pid!==process.pid&&s(n.pid,n.processStartTime))return!1;try{M.unlinkSync(t)}catch{}return i()}(ls,ld,{pid:process.pid,version:lw,startedAt:Date.now(),processStartTime:lI})){process.stderr.write("Daemon lock is held by another process; exiting.\n"),process.exit(0);return}let r=[];try{var a;let i;if("socket"===lp||"dual"===lp){let t=R.createServer(e=>{let t="",r=0,a=new Set,i=!1,n=()=>{if(!i&&0!==r){for(let e of(i=!0,a))es(e);D({level:"warn",phase:"request_client_disconnected",data:{inFlightRequests:r}}),(async()=>{let e=Date.now()+15e3;for(;r>0&&Date.now()<e&&(await tr(),!(r<=0));)await new Promise(e=>setTimeout(e,200))})()}};e.setEncoding("utf8"),e.on("close",n),e.on("error",n),e.on("data",async i=>{let n=(t+=i).indexOf("\n");for(;-1!==n;){let i,o,s=t.slice(0,n).trim();if(t=t.slice(n+1),0===s.length){n=t.indexOf("\n");continue}r+=1;try{let e=JSON.parse(s);if(o=en(e.meta?.requestId,"socket"),e.meta={...e.meta,requestId:o},a.add(o),eo(o),ed(o))throw ec();i=await lA(e)}catch(e){i={ok:!1,error:A(e)}}finally{r-=1,o&&(a.delete(o),el(o))}e.destroyed||e.write(`${JSON.stringify(i)}
|
|
37
|
+
`),n=t.indexOf("\n")}})});r.push(t),e=await new Promise((e,r)=>{t.once("error",r),t.listen(0,"127.0.0.1",()=>{t.off("error",r);let a=t.address();"object"==typeof a&&a?.port?e(a.port):r(new h("COMMAND_FAILED","Failed to bind socket server"))})})}if("http"===lp||"dual"===lp){let e=await t5({handleRequest:lA,token:lg});r.push(e),t=await new Promise((t,r)=>{e.once("error",r),e.listen(0,"127.0.0.1",()=>{e.off("error",r);let a=e.address();"object"==typeof a&&a?.port?t(a.port):r(new h("COMMAND_FAILED","Failed to bind HTTP server"))})})}a={socketPort:e,httpPort:t,token:lg,version:lw,codeSignature:lv,processStartTime:lI},M.existsSync(ls)||M.mkdirSync(ls,{recursive:!0}),M.writeFileSync(lu,""),i=a.socketPort&&a.httpPort?"dual":a.httpPort?"http":"socket",M.writeFileSync(ll,JSON.stringify({port:a.socketPort,httpPort:a.httpPort,transport:i,token:a.token,pid:process.pid,version:a.version,codeSignature:a.codeSignature,processStartTime:a.processStartTime,stateDir:ls},null,2),{mode:384}),e&&process.stdout.write(`AGENT_DEVICE_DAEMON_PORT=${e}
|
|
38
38
|
`),t&&process.stdout.write(`AGENT_DEVICE_DAEMON_HTTP_PORT=${t}
|
|
39
39
|
`)}catch(t){let e=f(t);for(let t of(process.stderr.write(`Daemon error: ${e.message}
|
|
40
|
-
`),r))try{t.close(()=>{})}catch{}
|
|
40
|
+
`),r))try{t.close(()=>{})}catch{}la(ll),ln(ld),process.exit(1);return}let i=!1,n=async()=>{await Promise.all(r.map(async e=>{await new Promise(t=>{try{e.close(()=>t())}catch{t()}})}))},o=async()=>{if(!i){for(let e of(i=!0,await n(),lm.toArray()))lm.writeSessionLog(e);await ta(),la(ll),ln(ld),process.exit(0)}};process.on("SIGINT",()=>{o()}),process.on("SIGTERM",()=>{o()}),process.on("SIGHUP",()=>{o()}),process.on("uncaughtException",e=>{let t=e instanceof h?e:f(e);process.stderr.write(`Daemon error: ${t.message}
|
|
41
41
|
`),o()})}();
|
package/dist/src/utils/args.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CliFlags } from './command-schema.ts';
|
|
1
|
+
import { type CliFlags, type FlagKey } from './command-schema.ts';
|
|
2
2
|
type ParsedArgs = {
|
|
3
3
|
command: string | null;
|
|
4
4
|
positionals: string[];
|
|
@@ -8,8 +8,20 @@ type ParsedArgs = {
|
|
|
8
8
|
type ParseArgsOptions = {
|
|
9
9
|
strictFlags?: boolean;
|
|
10
10
|
};
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
type ParsedFlagRecord = {
|
|
12
|
+
key: FlagKey;
|
|
13
|
+
token: string;
|
|
14
|
+
};
|
|
15
|
+
type RawParsedArgs = ParsedArgs & {
|
|
16
|
+
providedFlags: ParsedFlagRecord[];
|
|
17
|
+
};
|
|
18
|
+
type FinalizeArgsOptions = ParseArgsOptions & {
|
|
19
|
+
defaultFlags?: Partial<CliFlags>;
|
|
20
|
+
};
|
|
21
|
+
export declare function parseArgs(argv: string[], options?: FinalizeArgsOptions): ParsedArgs;
|
|
22
|
+
export declare function parseRawArgs(argv: string[]): RawParsedArgs;
|
|
23
|
+
export declare function finalizeParsedArgs(parsed: RawParsedArgs, options?: FinalizeArgsOptions): ParsedArgs;
|
|
24
|
+
export declare function toDaemonFlags(flags: CliFlags): Omit<CliFlags, 'json' | 'config' | 'help' | 'version'>;
|
|
13
25
|
export declare function usage(): string;
|
|
14
26
|
export declare function usageForCommand(command: string): string | null;
|
|
15
27
|
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type CliFlags } from './command-schema.ts';
|
|
2
|
+
type EnvMap = Record<string, string | undefined>;
|
|
3
|
+
export declare function resolveConfigBackedFlagDefaults(options: {
|
|
4
|
+
command: string | null;
|
|
5
|
+
cwd: string;
|
|
6
|
+
cliFlags: CliFlags;
|
|
7
|
+
env?: EnvMap;
|
|
8
|
+
}): Partial<CliFlags>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type FlagDefinition, type FlagKey } from './command-schema.ts';
|
|
2
|
+
export type OptionSpec = {
|
|
3
|
+
key: FlagKey;
|
|
4
|
+
flagDefinitions: readonly FlagDefinition[];
|
|
5
|
+
config: {
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
key: string;
|
|
8
|
+
};
|
|
9
|
+
env: {
|
|
10
|
+
names: readonly string[];
|
|
11
|
+
};
|
|
12
|
+
supportsCommand(command: string | null): boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function getOptionSpecs(): readonly OptionSpec[];
|
|
15
|
+
export declare function getOptionSpec(key: FlagKey): OptionSpec | undefined;
|
|
16
|
+
export declare function getOptionSpecForToken(token: string): OptionSpec | undefined;
|
|
17
|
+
export declare function getConfigurableOptionSpecs(command: string | null): OptionSpec[];
|
|
18
|
+
export declare function isFlagSupportedForCommand(key: FlagKey, command: string | null): boolean;
|
|
19
|
+
export declare function parseOptionValueFromSource(spec: OptionSpec, value: unknown, sourceLabel: string, rawKey: string): unknown;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CliFlags } from './command-schema.ts';
|
|
2
|
+
type EnvMap = Record<string, string | undefined>;
|
|
3
|
+
export declare function resolveCliOptions(argv: string[], options?: {
|
|
4
|
+
cwd?: string;
|
|
5
|
+
env?: EnvMap;
|
|
6
|
+
strictFlags?: boolean;
|
|
7
|
+
}): {
|
|
8
|
+
command: string | null;
|
|
9
|
+
positionals: string[];
|
|
10
|
+
flags: CliFlags;
|
|
11
|
+
warnings: string[];
|
|
12
|
+
};
|
|
13
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type CliFlags = {
|
|
2
2
|
json: boolean;
|
|
3
|
+
config?: string;
|
|
3
4
|
stateDir?: string;
|
|
4
5
|
daemonBaseUrl?: string;
|
|
5
6
|
daemonAuthToken?: string;
|
|
@@ -9,6 +10,9 @@ export type CliFlags = {
|
|
|
9
10
|
sessionIsolation?: 'none' | 'tenant';
|
|
10
11
|
runId?: string;
|
|
11
12
|
leaseId?: string;
|
|
13
|
+
sessionLock?: 'reject' | 'strip';
|
|
14
|
+
sessionLocked?: boolean;
|
|
15
|
+
sessionLockConflicts?: 'reject' | 'strip';
|
|
12
16
|
platform?: 'ios' | 'android' | 'apple';
|
|
13
17
|
target?: 'mobile' | 'tv';
|
|
14
18
|
device?: string;
|
|
@@ -84,6 +88,7 @@ type CommandSchema = {
|
|
|
84
88
|
};
|
|
85
89
|
export declare const GLOBAL_FLAG_KEYS: Set<keyof CliFlags>;
|
|
86
90
|
export declare function getFlagDefinition(token: string): FlagDefinition | undefined;
|
|
91
|
+
export declare function getFlagDefinitions(): readonly FlagDefinition[];
|
|
87
92
|
export declare function getCommandSchema(command: string | null): CommandSchema | undefined;
|
|
88
93
|
export declare function getCliCommandNames(): string[];
|
|
89
94
|
export declare function getSchemaCapabilityKeys(): string[];
|