agent-device 0.16.10 → 0.16.11
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/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.16.10.apk → agent-device-android-multitouch-helper-0.16.11.apk} +0 -0
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.11.apk.sha256 +1 -0
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.16.10.manifest.json → agent-device-android-multitouch-helper-0.16.11.manifest.json} +4 -4
- package/android-snapshot-helper/README.md +6 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.11.apk +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.11.apk.sha256 +1 -0
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.16.10.manifest.json → agent-device-android-snapshot-helper-0.16.11.manifest.json} +6 -6
- package/dist/src/1352.js +1 -1
- package/dist/src/221.js +6 -6
- package/dist/src/2415.js +27 -27
- package/dist/src/2805.js +1 -1
- package/dist/src/4778.js +1 -0
- package/dist/src/5792.js +1 -1
- package/dist/src/6085.js +1 -1
- package/dist/src/6232.js +1 -1
- package/dist/src/8699.js +1 -1
- package/dist/src/9238.js +4 -0
- package/dist/src/9533.js +1 -1
- package/dist/src/9542.js +3 -3
- package/dist/src/apple.js +1 -1
- package/dist/src/args.js +54 -25
- package/dist/src/batch.d.ts +1 -0
- package/dist/src/cli.js +19 -19
- package/dist/src/command-metadata.js +1 -1
- package/dist/src/command-surface.js +1 -1
- package/dist/src/contracts.d.ts +1 -0
- package/dist/src/contracts.js +1 -1
- package/dist/src/generic.js +10 -10
- package/dist/src/index.d.ts +9 -0
- package/dist/src/record-trace.js +3 -3
- package/dist/src/session.js +9 -9
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +7 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +109 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +123 -32
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +36 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +11 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/dogfood/SKILL.md +1 -1
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.16.10.apk.sha256 +0 -1
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.10.apk +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.16.10.apk.sha256 +0 -1
- package/dist/src/2842.js +0 -1
- package/dist/src/8114.js +0 -4
package/dist/src/generic.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import t from"node:fs";import e from"node:path";import{AppError as r}from"./9152.js";import{runCliCommandWithOutput as s,printJson as i,writeCommandOutput as n}from"./cli.js";import{
|
|
1
|
+
import t from"node:fs";import e from"node:path";import{AppError as r}from"./9152.js";import{runCliCommandWithOutput as s,printJson as i,writeCommandOutput as n}from"./cli.js";import{formatDurationSeconds as o}from"./9238.js";import{readCommandMessage as a}from"./1998.js";function u(t){var r,s;let i,n,o=t.attempts>1?` after ${t.attempts} attempts`:"",a=y(t);for(let u of(process.stdout.write(`FAIL ${(i=h(r=t),n=e.basename(r.file),i&&i.length>0?`${JSON.stringify(i)} in ${n}`:n)}${o}${a}
|
|
2
2
|
`),process.stdout.write(` ${t.error?.message??"Unknown test failure"}
|
|
3
3
|
`),s=t,[s.error?.hint?`hint: ${s.error.hint}`:"",s.artifactsDir?`artifacts: ${s.artifactsDir}`:"",s.error?.logPath?`log: ${s.error.logPath}`:"",s.error?.diagnosticId?`diagnostic: ${s.error.diagnosticId}`:""].filter(Boolean)))process.stdout.write(` ${u}
|
|
4
|
-
`);for(let e of
|
|
5
|
-
`)}function
|
|
6
|
-
`}(i),"utf8")}catch(e){let t=e instanceof Error?e.message:String(e);throw new r("COMMAND_FAILED",`Failed to write JUnit report to ${s}: ${t}`)}}(
|
|
4
|
+
`);for(let e of l(t))process.stdout.write(` ${e}
|
|
5
|
+
`)}function l(r){var s,i,n;if("skipped"===r.status)return[];let a=(s=r).artifactsDir?e.join(s.artifactsDir,`attempt-${s.attempts}`,"replay-timing.ndjson"):void 0;if(!a)return[];let u=function(e){try{return t.readFileSync(e,"utf8").split(/\r?\n/).filter(t=>t.trim().length>0).flatMap(t=>{try{let e=JSON.parse(t);return m(e)?[e]:[]}catch{return[]}})}catch{return[]}}(a);if(0===u.length)return[];let l=[],$=[];for(let t of u){if("replay_action_start"===(i=t).type&&f(i)&&p(i,"line")&&d(i,"command")&&(void 0===i.positionals||Array.isArray(i.positionals))){l.push(t);continue}"replay_action_stop"===(n=t).type&&f(n)&&p(n,"line")&&d(n,"command")&&(void 0===n.ok||"boolean"==typeof n.ok)&&p(n,"durationMs")&&d(n,"errorCode")&&(void 0===n.resultTiming||m(n.resultTiming))&&$.push({stop:t,start:function(t,e){let r=e.command,s=t.findIndex(t=>t.step===e.step&&(void 0===r||void 0===t.command||t.command===r));if(!(s<0))return t.splice(s,1)[0]}(l,t)})}return 0===$.length?[]:[r.attempts>1?`steps (attempt ${r.attempts}):`:"steps:",...$.map(({stop:t,start:e})=>{var r,s,i,n,a,u;let l,f,p;return r=t,s=e,l=!1===r.ok?"[FAIL] ":!0===r.ok?"":"[info] ",` ${l}${i=s,n=r,[function(t){if(!t)return"unknown";if(!t.startsWith("__maestro"))return t;let e=t.slice(9);return e.length>0?e[0].toLowerCase()+e.slice(1):t}(i?.command??n.command),...(i?.positionals??[]).map(c)].join(" ")}${a=r,u=s,(p=["number"==typeof(f=u?.line??a.line)?`line ${f}`:"","number"==typeof a.durationMs?o(a.durationMs):"",a.errorCode??"",a.resultTiming?`timing ${JSON.stringify(a.resultTiming)}`:""].filter(Boolean)).length>0?` (${p.join(", ")})`:""}`})]}function f(t){return"number"==typeof t.step}function p(t,e){return void 0===t[e]||"number"==typeof t[e]}function d(t,e){return void 0===t[e]||"string"==typeof t[e]}function c(t){return"string"==typeof t?JSON.stringify(t):"number"==typeof t||"boolean"==typeof t?String(t):JSON.stringify(t)}function m(t){return!!t&&"object"==typeof t&&!Array.isArray(t)}function $(t){return"passed"===t.status&&t.attempts>1}function g(t){let r=h(t);return r&&r.length>0?JSON.stringify(r):e.basename(t.file)}function h(t){let e=t.title?.trim();return e&&e.length>0?e:void 0}function y(t){if("passed"===t.status&&t.attempts>1)return v(t);if("failed"===t.status&&t.attempts>1&&t.durationMs>0)return` (total ${o(t.durationMs)})`;let e="passed"===t.status&&"number"==typeof t.finalAttemptDurationMs?t.finalAttemptDurationMs:t.durationMs;return e>0?` (${o(e)})`:""}function v(t){let e=["number"==typeof t.finalAttemptDurationMs?`passed attempt ${o(t.finalAttemptDurationMs)}`:"",t.durationMs>0?`total ${o(t.durationMs)}`:""].filter(Boolean);return e.length>0?` (${e.join(", ")})`:""}function M(t,e,r={}){r.includeMessage&&t.push(`errorMessage: ${e.message}`),e.hint&&t.push(`hint: ${e.hint}`),e.diagnosticId&&t.push(`diagnosticId: ${e.diagnosticId}`),e.logPath&&t.push(`logPath: ${e.logPath}`),!1!==r.includeDetails&&S(t,e,r.detailsIndent)}function S(t,e,r){let s=e.details?JSON.stringify(e.details,null,r):void 0;s&&t.push(`details: ${s}`)}function _(t,e){e&&t.push(e)}function A(t){return(Math.max(0,t)/1e3).toFixed(3)}function w(t){return t.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}async function D({command:f,positionals:p,flags:d,client:c}){var m,k,b,j,C;"test"===f&&(({json:d.json}).json||process.stderr.write("Running replay suite...\n"));let{result:N,cliOutput:P}=await s({client:c,command:f,positionals:p,flags:d});if(P){m=d,k=P,!m.json&&k.stderr&&process.stderr.write(k.stderr),n(m,m.json?k.jsonData??k.data:k.data,()=>k.text)}else{let s=(b=f,j=d,C=N,"test"===b?function(s){let{suite:n,json:a,verbose:f,reportJunit:p}=s;return(p&&function(s,i){let n=e.dirname(s);try{t.mkdirSync(n,{recursive:!0}),t.writeFileSync(s,function(t){let r=['<?xml version="1.0" encoding="UTF-8"?>',"<testsuites>",` <testsuite name="agent-device replay suite" tests="${t.total}" failures="${t.failed}" skipped="${t.skipped}" time="${A(t.durationMs)}">`];for(let s of t.tests)r.push(...function(t){let r=w(h(t)??e.basename(t.file)),s=w("."===e.dirname(t.file)?t.file:e.dirname(t.file)),i=w(t.file),n=A(t.durationMs),o=[` <testcase classname="${s}" name="${r}" file="${i}" time="${n}">`];"failed"===t.status?o.push(` <failure message="${w(t.error.message)}">${w(function(t){let e=[t.error.message];return M(e,t.error,{includeDetails:!1}),t.artifactsDir&&e.push(`artifactsDir: ${t.artifactsDir}`),S(e,t.error,2),e.join("\n")}(t))}</failure>`):"skipped"===t.status&&o.push(` <skipped message="${w(t.message)}" />`);let a=function(t){let e=[`status: ${t.status}`,`durationMs: ${t.durationMs}`];return function(t,e){var r,s;_(t,"attempts"in e?`attempts: ${e.attempts}`:void 0),_(t,"session"in e?`session: ${e.session}`:void 0),_(t,"replayed"in e?`replayed: ${e.replayed}`:void 0),_(t,"healed"in e?`healed: ${e.healed}`:void 0),_(t,"artifactsDir"in e&&e.artifactsDir?`artifactsDir: ${e.artifactsDir}`:void 0),"failed"===e.status&&(r=t,s=e,r.push(`errorCode: ${s.error.code}`),M(r,s.error,{includeMessage:!0})),_(t,$(e)?"flaky: true":void 0)}(e,t),e.join("\n")}(t);return a&&o.push(` <system-out>${w(a)}</system-out>`),o.push(" </testcase>"),o}(s));return r.push(" </testsuite>"),r.push("</testsuites>"),`${r.join("\n")}
|
|
6
|
+
`}(i),"utf8")}catch(e){let t=e instanceof Error?e.message:String(e);throw new r("COMMAND_FAILED",`Failed to write JUnit report to ${s}: ${t}`)}}(p,n),a)?(i({success:!0,data:n}),+(n.failed>0)):function(t,e={}){let r=t.tests.filter($);if(e.verbose)for(let e of t.tests)!function(t){var e;if("failed"===t.status)return u(t);let r=y(t);for(let s of(process.stdout.write(`${"passed"===(e=t).status?"PASS":"skipped"===e.status?"SKIP":"INFO"} ${g(t)}${r}
|
|
7
7
|
`),"skipped"===t.status&&process.stdout.write(` ${t.message??"skipped"}
|
|
8
|
-
`),
|
|
9
|
-
`)}(e);else for(let e of t.tests){var s;"failed"===(s=e).status?
|
|
10
|
-
`)}let i="number"==typeof t.durationMs?t.durationMs:void 0,n=r.length>0?`, ${r.length} flaky`:"",
|
|
11
|
-
`),function(t){if(0!==t.length)for(let e of(process.stdout.write("Flaky tests:\n"),t))for(let t of(process.stdout.write(` PASS ${
|
|
12
|
-
`),e.attemptFailures??[])){let e="number"==typeof t.durationMs?` (${
|
|
13
|
-
`)}}(r),+(t.failed>0)}(n,{verbose:
|
|
8
|
+
`),l(t)))process.stdout.write(` ${s}
|
|
9
|
+
`)}(e);else for(let e of t.tests){var s;"failed"===(s=e).status?u(s):"passed"===s.status&&process.stdout.write(`PASS ${g(s)}${y(s)}
|
|
10
|
+
`)}let i="number"==typeof t.durationMs?t.durationMs:void 0,n=r.length>0?`, ${r.length} flaky`:"",a=void 0!==i?` in ${o(i)}`:"";return process.stdout.write(`Test summary: ${t.passed} passed, ${t.failed} failed${n}${a}
|
|
11
|
+
`),function(t){if(0!==t.length)for(let e of(process.stdout.write("Flaky tests:\n"),t))for(let t of(process.stdout.write(` PASS ${g(e)} after ${e.attempts} attempts${v(e)}
|
|
12
|
+
`),e.attemptFailures??[])){let e="number"==typeof t.durationMs?` (${o(t.durationMs)})`:"";process.stdout.write(` attempt ${t.attempt} failed${e}: ${t.message}
|
|
13
|
+
`)}}(r),+(t.failed>0)}(n,{verbose:f})}({suite:C,verbose:j.verbose,json:j.json,reportJunit:j.reportJunit}):(n(j,C,()=>a(C)),0));0!==s&&process.exit(s)}return!0}export{D as runGenericClientBackedCommand};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -110,6 +110,7 @@ export declare type AgentDeviceCommandClient = {
|
|
|
110
110
|
keyboard: (options?: KeyboardCommandOptions) => Promise<KeyboardCommandResult>;
|
|
111
111
|
clipboard: (options: ClipboardCommandOptions) => Promise<ClipboardCommandResult>;
|
|
112
112
|
reactNative: (options: ReactNativeCommandOptions) => Promise<CommandRequestResult>;
|
|
113
|
+
prepare: (options: PrepareCommandOptions) => Promise<CommandRequestResult>;
|
|
113
114
|
};
|
|
114
115
|
|
|
115
116
|
export declare type AgentDeviceDaemonTransport = (req: Omit<DaemonRequest, 'token'>) => Promise<DaemonResponse>;
|
|
@@ -312,6 +313,7 @@ export declare type AppOpenOptions = AgentDeviceRequestOverrides & AgentDeviceSe
|
|
|
312
313
|
|
|
313
314
|
export declare type AppOpenResult = {
|
|
314
315
|
session: string;
|
|
316
|
+
sessionStateDir?: string;
|
|
315
317
|
appName?: string;
|
|
316
318
|
appBundleId?: string;
|
|
317
319
|
appId?: string;
|
|
@@ -419,6 +421,7 @@ export declare type CaptureSnapshotOptions = AgentDeviceRequestOverrides & Agent
|
|
|
419
421
|
scope?: string;
|
|
420
422
|
raw?: boolean;
|
|
421
423
|
forceFull?: boolean;
|
|
424
|
+
timeoutMs?: number;
|
|
422
425
|
};
|
|
423
426
|
|
|
424
427
|
export declare type CaptureSnapshotResult = {
|
|
@@ -532,6 +535,7 @@ declare type DaemonRequestMeta = {
|
|
|
532
535
|
requestId?: string;
|
|
533
536
|
debug?: boolean;
|
|
534
537
|
cwd?: string;
|
|
538
|
+
sessionExplicit?: boolean;
|
|
535
539
|
tenantId?: string;
|
|
536
540
|
runId?: string;
|
|
537
541
|
leaseId?: string;
|
|
@@ -852,6 +856,11 @@ declare type PointTarget = {
|
|
|
852
856
|
label?: never;
|
|
853
857
|
};
|
|
854
858
|
|
|
859
|
+
declare type PrepareCommandOptions = ClientCommandBaseOptions & {
|
|
860
|
+
action: 'ios-runner';
|
|
861
|
+
timeoutMs?: number;
|
|
862
|
+
};
|
|
863
|
+
|
|
855
864
|
declare type PrepareMetroRuntimeResult = {
|
|
856
865
|
projectRoot: string;
|
|
857
866
|
kind: ResolvedMetroKind;
|
package/dist/src/record-trace.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
let e,t,r,a;import{__webpack_require__ as i}from"./rslib-runtime.js";import o from"node:fs";import n from"node:path";import{createHash as s}from"node:crypto";import c from"node:os";import{setTimeout as d}from"node:timers/promises";import{fileURLToPath as l}from"node:url";import{AppError as u}from"./9152.js";import{runCmd as m}from"./9818.js";import{sleep as f}from"./4829.js";import{withDiagnosticTimer as p,emitDiagnostic as h}from"./7599.js";import{androidDeviceForSerial as w,runAndroidAdb as g}from"./8806.js";import{pullAndroidAdbFile as y}from"./9639.js";import{runXcrun as _,SessionStore as v,runIosRunnerCommand as P,errorResponse as S,isCommandSupportedOnDevice as A,ensureDeviceReady as C,
|
|
1
|
+
let e,t,r,a;import{__webpack_require__ as i}from"./rslib-runtime.js";import o from"node:fs";import n from"node:path";import{createHash as s}from"node:crypto";import c from"node:os";import{setTimeout as d}from"node:timers/promises";import{fileURLToPath as l}from"node:url";import{AppError as u}from"./9152.js";import{runCmd as m}from"./9818.js";import{sleep as f}from"./4829.js";import{withDiagnosticTimer as p,emitDiagnostic as h}from"./7599.js";import{androidDeviceForSerial as w,runAndroidAdb as g}from"./8806.js";import{pullAndroidAdbFile as y}from"./9639.js";import{runXcrun as _,SessionStore as v,runIosRunnerCommand as P,errorResponse as S,isCommandSupportedOnDevice as A,ensureDeviceReady as C,resolvePublicSessionName as b,resolveImplicitSessionScope as M,resolveTargetDevice as I,IOS_RUNNER_CONTAINER_BUNDLE_IDS as x}from"./2415.js";import{resolveRecordingProvider as D}from"./recording-provider.js";import{PUBLIC_COMMANDS as R}from"./5792.js";var N={};function $(e=process.env){let t=F(),r=n.join(t,"home"),a=n.join(t,"module-cache");return o.mkdirSync(r,{recursive:!0}),o.mkdirSync(a,{recursive:!0}),{...e,HOME:r,CLANG_MODULE_CACHE_PATH:a}}async function E(e){let t=o.statSync(e.sourcePath),r=o.readFileSync(e.sourcePath),a=U(e.cacheName??n.basename(e.sourcePath,n.extname(e.sourcePath))),i=z(["2",process.platform,process.arch,n.resolve(e.sourcePath),t.size,r]),s=n.join(F(),"bin",`${a}-${i}`);return await O({sourcePath:e.sourcePath,executablePath:s,timeoutMs:e.timeoutMs}),s}async function k(e){let t=U(e.cacheName),r=z(["2",process.platform,process.arch,e.source]),a=n.join(F(),"sources",`${t}-${r}.swift`),i=n.join(F(),"bin",`${t}-${r}`);return await O({sourcePath:a,executablePath:i,sourceText:e.source,timeoutMs:e.timeoutMs}),i}function F(){let e=process.env.AGENT_DEVICE_SWIFT_CACHE_DIR?.trim();return e?n.resolve(e):n.join(c.tmpdir(),"agent-device-swift-cache")}async function O(e){if(T(e.executablePath))return;let t=n.dirname(e.executablePath);o.mkdirSync(t,{recursive:!0});let r=`${e.executablePath}.lock`;if(!await L(r,e.executablePath,e.timeoutMs??12e4))return;let a=o.mkdtempSync(n.join(t,`.${n.basename(e.executablePath)}.${process.pid}.`)),i=n.join(a,n.basename(e.executablePath));try{if(T(e.executablePath))return;void 0===e.sourceText||o.existsSync(e.sourcePath)||(o.mkdirSync(n.dirname(e.sourcePath),{recursive:!0}),o.writeFileSync(e.sourcePath,e.sourceText)),await m("xcrun",["swiftc",e.sourcePath,"-o",i],{timeoutMs:e.timeoutMs??12e4,env:$()}),o.renameSync(i,e.executablePath)}finally{o.rmSync(a,{recursive:!0,force:!0}),o.rmSync(r,{recursive:!0,force:!0})}}async function L(e,t,r){let a=Date.now()+r;for(;;){if(T(t))return!1;try{return o.mkdirSync(e),!0}catch(t){if("EEXIST"!==t.code)throw t;if(function(e,t){try{return Date.now()-o.statSync(e).mtimeMs>=t}catch{return!1}}(e,r)){o.rmSync(e,{recursive:!0,force:!0});continue}if(Date.now()>=a)throw new u("COMMAND_FAILED",`Timed out waiting for Swift cache lock: ${e} (${r}ms)`,{lockDir:e,timeoutMs:r,hint:`Another agent-device process may still be compiling this Swift helper. Retry shortly; if no agent-device process is active, remove "${e}" and retry.`});await d(25)}}}function T(e){try{return o.accessSync(e,o.constants.X_OK),o.statSync(e).isFile()}catch{return!1}}function U(e){return e.replaceAll(/[^A-Za-z0-9._-]/g,"-").replaceAll(/^-+|-+$/g,"")||"swift-helper"}function z(e){let t=s("sha256");for(let r of e)t.update(Buffer.isBuffer(r)?r:String(r)),t.update("\0");return t.digest("hex").slice(0,16)}i.r(N),i.d(N,{RECORD_TRACE_COMMAND_HANDLERS:()=>e2,handleRecordTraceCommands:()=>e4});let K=`
|
|
2
2
|
import Foundation
|
|
3
3
|
import AVFoundation
|
|
4
4
|
|
|
@@ -22,5 +22,5 @@ Task {
|
|
|
22
22
|
|
|
23
23
|
semaphore.wait()
|
|
24
24
|
exit(exitCode)
|
|
25
|
-
`.trim();async function
|
|
26
|
-
${t}`;return/\b(no such module ['"]AVFoundation['"]|unable to find utility ["']swiftc?["']|xcrun: error: unable to find utility ["']swiftc?["'])\b/i.test(r)}function H(e){try{let t=o.statSync(e);if(!t.isFile()||t.size<=0)return!1}catch{return!1}let t=function(e){try{let t=o.openSync(e,"r");try{let e=o.fstatSync(t).size,r=0,a=[];for(;r+8<=e&&a.length<16;){let e=Buffer.alloc(8);if(8>o.readSync(t,e,0,8,r))break;let i=e.readUInt32BE(0),n=e.toString("latin1",4,8);if(a.push(n),1===i){let e=Buffer.alloc(8);if(8>o.readSync(t,e,0,8,r+8))break;i=Number(e.readBigUInt64BE(0))}if(!Number.isFinite(i)||i<=0)break;r+=i}return a}finally{o.closeSync(t)}}catch{return[]}}(e);return t.includes("ftyp")&&t.includes("moov")}function j(e){let t=n.parse(e);return n.join(t.dir,`${t.name}.gesture-telemetry.json`)}function B(e){return[...e].sort((e,t)=>e.tMs-t.tMs)}function W(e){var t,r,a;let i,n,{recording:s,trimStartMs:c}=e,d=(i=j((t={videoPath:s.outPath,events:s.gestureEvents,trimStartMs:c}).videoPath),n={version:1,generatedAt:new Date().toISOString(),events:(r=t.events,(a=t.trimStartMs??0)>0?B(r.flatMap(e=>{let t=e.tMs-a,r="durationMs"in e?e.durationMs:void 0;return("number"==typeof r?t+r:t)<=0?[]:[{...e,tMs:Math.max(0,t)}]})):B(r))},o.writeFileSync(i,JSON.stringify(n,null,2)),i);return s.telemetryPath=d,d}function X(e){let t=n.dirname(l(import.meta.url)),r=[l(new URL(`./${e}`,import.meta.url)),n.resolve(t,`../../ios-runner/AgentDeviceRunner/RecordingScripts/${e}`),n.resolve(t,`../../../ios-runner/AgentDeviceRunner/RecordingScripts/${e}`),n.resolve(process.cwd(),`ios-runner/AgentDeviceRunner/RecordingScripts/${e}`)];for(let e of r)if(o.existsSync(e))return e;throw new u("COMMAND_FAILED",`Missing recording helper script: ${e}`,{hint:"Ensure ios-runner/AgentDeviceRunner/RecordingScripts is present in this checkout or bundled with the package.",scriptName:e,searchedPaths:r})}async function J(e){var t;let r,a,{videoPath:i,scriptPath:s,scriptArgs:c,commandDescription:d}=e;await z(i),await V(i);let l=(t=i,r=n.parse(t),a=`${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,n.join(r.dir,`.${r.name}.agent-device-${a}${r.ext||".mp4"}`));try{let e=await N({sourcePath:s});await m(e,["--input",i,"--output",l,...c],{timeoutMs:12e4,env:R()}),await V(l),o.renameSync(l,i)}catch(t){let e=t instanceof u?t:new u("COMMAND_FAILED",String(t),void 0,t instanceof Error?t:void 0);throw new u("COMMAND_FAILED",d,{...e.details,videoPath:i,script:s},e)}finally{o.rmSync(l,{force:!0})}}async function Z(e){let{videoPath:t,trimStartMs:a}=e;a>0&&await J({videoPath:t,scriptPath:r??=X("recording-trim.swift"),scriptArgs:["--trim-start-ms",String(a)],commandDescription:"Failed to trim the start of the iOS recording"})}async function Q(e){let{videoPath:r,telemetryPath:a,targetLabel:i="recording"}=e;await J({videoPath:r,scriptPath:t??=X("recording-overlay.swift"),scriptArgs:["--events",a],commandDescription:`Failed to add touch overlays to the ${i}`})}async function Y(e){let{videoPath:t,quality:r,targetLabel:i="recording"}=e;await J({videoPath:t,scriptPath:a??=X("recording-resize.swift"),scriptArgs:["--quality",String(r)],commandDescription:`Failed to resize the ${i}`})}function ee(e){return e instanceof Error?e.message:String(e)}function et(e,t){return e.stderr.trim()||e.stdout.trim()||`${t} exited with code ${e.exitCode}`}function er(e,t,r=Date.now()){let a=Math.max(0,r-t.startedAt);return a>=1e3?{message:e,tooShort:!1}:{message:`${e}. Recording stopped after ${Math.round(a)}ms; wait at least 1000ms between record start and record stop so the recorder can finalize a playable MP4`,tooShort:!0}}async function ea(e){let{recording:t,deps:r,trimStartMs:a,targetLabel:i}=e,o=W({recording:t,trimStartMs:a});if(!t.showTouches)return void h({level:"debug",phase:"record_stop_overlay_skipped",data:{reason:"hide_touches"}});if(0===t.gestureEvents.length)return void h({level:"debug",phase:"record_stop_overlay_skipped",data:{reason:"no_gesture_events"}});let n=function(e=process.platform){if("darwin"!==e)return"touch overlay burn-in is only available on macOS hosts; returning raw video plus gesture telemetry"}();if(n){t.overlayWarning??=n;return}try{await p("record_stop_overlay_export",()=>r.overlayRecordingTouches({videoPath:t.outPath,telemetryPath:o,targetLabel:i}),{targetLabel:i,gestureEventCount:t.gestureEvents.length})}catch(e){t.overlayWarning??=`failed to overlay recording touches: ${ee(e)}`}}function ei(e,t){if(1===t)return e;let r=n.parse(e),a=r.ext||".mp4";return n.join(r.dir,`${r.name}.part-${String(t).padStart(3,"0")}${a}`)}function eo(e){return e.chunks??=[{index:1,path:e.outPath,remotePath:e.remotePath}],e.chunks}async function en(e){let{recording:t,startNextChunk:r,finishCurrentChunk:a}=e;if(t.stopping)return;let i=await a();if(i)throw Error(i);if(t.stopping)return;let o=eo(t),s=o.length+1,c=await r(n.posix.dirname(t.remotePath));t.remotePath=c.remotePath,t.remotePid=c.remotePid,o.push({index:s,path:ei(t.outPath,s),remotePath:c.remotePath}),t.warning??="Android adb screenrecord is capped at 180s, so this recording was split into multiple MP4 chunks."}async function es(e){let{recording:t,deps:r}=e;eo(t).length<=1?await ea({recording:t,deps:r,targetLabel:"Android recording"}):(W({recording:t}),t.showTouches&&t.gestureEvents.length>0&&(t.overlayWarning??="touch overlay burn-in is skipped for chunked Android recordings; returning raw chunks plus gesture telemetry"))}async function ec(e){for(let t of e.chunks){let r=await ed({deps:e.deps,deviceId:e.deviceId,remotePath:t.remotePath,outPath:t.path});if(r)return`failed to copy recording chunk ${t.index}: ${r}`}}async function ed(e){let t,{deps:r,deviceId:a,remotePath:i,outPath:n}=e;for(let e=0;e<2;e+=1){el(n);let s=w(a),c=await y(i,n,{allowFailure:!0,device:s});if(0!==c.exitCode)t=et(c,"adb pull");else{await r.waitForStableFile(n,{pollMs:250,attempts:20});let t=await r.isPlayableVideo(n);if(h({level:"debug",phase:"record_stop_android_pull_validation",data:{deviceId:a,remotePath:i,outPath:n,attempt:e+1,fileSize:function(e){try{return o.statSync(e).size}catch{return 0}}(n),playable:t}}),t)return;h({level:"warn",phase:"record_stop_android_invalid_video_retry",data:{deviceId:a,remotePath:i,outPath:n,attempt:e+1}})}e<1&&await f(750)}return t?`failed to copy recording from device: ${t}`:(el(n),"failed to copy recording from device: pulled file is not a playable MP4")}function el(e){try{o.rmSync(e,{force:!0})}catch{}}async function eu(e,t,r){return await g(w(e),t,r)}async function em(e,t){let r=await eu(e,["shell","ps","-o","pid=","-p",t],{allowFailure:!0});return 0===r.exitCode&&r.stdout.split(/\s+/).map(e=>e.trim()).includes(t)}async function ef(e,t){for(let r=0;r<40;r+=1){if(!await em(e,t))return!0;await f(250)}return!await em(e,t)}async function ep(e,t){let r,a=0;for(let i=0;i<20;i+=1){let i=await eu(e,["shell","stat","-c","%s",t],{allowFailure:!0}),o=0===i.exitCode?i.stdout.trim():"";if(o.length>0&&o===r){if((a+=1)>=4)return}else a=0;r=o,await f(250)}}async function eh(e,t,r){for(let a=0;a<8;a+=1){let i=await eu(e,["shell","stat","-c","%s",t],{allowFailure:!0}),o=0===i.exitCode?Number(i.stdout.trim()):NaN;if(Number.isFinite(o)&&o>0)return!0;if(!await em(e,r))break;if(a+1>=2)return!0;await f(250)}return!1}async function ew(e){let{deviceId:t,quality:r}=e;if(void 0===r||r>=10)return;let a=await eu(t,["shell","wm","size"],{allowFailure:!0}),i=a.stdout.match(/Override size:\s*(\d+)x(\d+)/)??a.stdout.match(/Physical size:\s*(\d+)x(\d+)/);if(0!==a.exitCode||!i)throw Error(`failed to resolve Android screen size for recording quality: ${et(a,"adb shell wm size")}`);return{width:eg(Number(i[1]),r),height:eg(Number(i[2]),r)}}function eg(e,t){return Math.max(2,2*Math.round(e*t/10/2))}async function ey(e,t){await eu(e,["shell","rm","-f",t],{allowFailure:!0})}async function e_(e,t){let r=await eu(e,["shell","kill","-9",t],{allowFailure:!0});return h({level:"warn",phase:"record_stop_android_force_signal",data:{deviceId:e,remotePid:t,exitCode:r.exitCode,stdout:r.stdout.trim(),stderr:r.stderr.trim()}}),!(0!==r.exitCode&&await em(e,t))&&await ef(e,t)}async function ev(e){var t;let r,a,{device:i,recordingSize:o,preferredRemoteDir:n}=e,s="failed to start recording: Android screenrecord did not begin producing frames";for(let e of(t=Date.now(),r=`agent-device-recording-${t}.mp4`,a=["/sdcard","/data/local/tmp"],(n&&a.includes(n)?[n,...a.filter(e=>e!==n)]:a).map(e=>`${e}/${r}`))){let t=await eu(i.id,["shell",function(e,t){let r=["screenrecord"];return t&&r.push("--size",`${t.width}x${t.height}`),r.push(e),`${r.join(" ")} >/dev/null 2>&1 & echo $!`}(e,o)],{allowFailure:!0});if(0!==t.exitCode){s=`failed to start recording: ${et(t,"adb shell screenrecord")}`;continue}let r=t.stdout.split(/\r?\n/).map(e=>e.trim()).filter(e=>/^\d+$/.test(e)).at(-1);if(!r){s="failed to start recording: adb did not return a valid Android screenrecord pid",await ey(i.id,e);continue}if(h({level:"debug",phase:"record_start_android_started",data:{deviceId:i.id,remotePath:e,remotePid:r}}),await eh(i.id,e,r))return{remotePath:e,remotePid:r,startedAt:Date.now()};s="failed to start recording: Android screenrecord did not begin producing frames",await e_(i.id,r),await ey(i.id,e)}return{error:S("COMMAND_FAILED",s)}}async function eP(e){let t,{device:r,recordingBase:a}=e;try{t=await ew({deviceId:r.id,quality:a.quality})}catch(e){return S("COMMAND_FAILED",e instanceof Error?e.message:String(e))}let i=await ev({device:r,recordingSize:t});if("error"in i)return i.error;let o={platform:"android",remotePath:i.remotePath,remotePid:i.remotePid,chunks:[{index:1,path:a.outPath,remotePath:i.remotePath}],...a,startedAt:i.startedAt};return!function e(t){let{recording:r,startNextChunk:a,finishCurrentChunk:i}=t,o=setTimeout(()=>{r.rotationPromise=en({recording:r,startNextChunk:a,finishCurrentChunk:i}).catch(e=>{r.rotationFailedReason=e instanceof Error?e.message:String(e)}).finally(()=>{r.rotationPromise=void 0,r.stopping||r.rotationFailedReason||e({recording:r,startNextChunk:a,finishCurrentChunk:i})})},17e4);o.unref?.(),r.rotationTimer=o}({recording:o,finishCurrentChunk:async()=>await eS({device:r,recording:o,waitForRemoteFileStability:!1}),startNextChunk:async e=>{let a=await ev({device:r,recordingSize:t,preferredRemoteDir:e});if("error"in a)throw Error(a.error.ok?"failed to start next Android recording chunk":a.error.error.message);return a}}),o}async function eS(e){let{device:t,recording:r,waitForRemoteFileStability:a=!0}=e;await em(t.id,r.remotePid)||(r.warning??=function(e){if(!(Date.now()-e.startedAt<178e3))return"Android adb screenrecord stopped before record stop, likely after reaching the 180s platform limit. The MP4 may be truncated; final interactions after the limit are not in the video."}(r));let i=await eu(t.id,["shell","kill","-2",r.remotePid],{allowFailure:!0});if(h({level:"debug",phase:"record_stop_android_signal",data:{deviceId:t.id,remotePath:r.remotePath,remotePid:r.remotePid,exitCode:i.exitCode,stdout:i.stdout.trim(),stderr:i.stderr.trim()}}),0!==i.exitCode)return await eA(t.id,r.remotePid,i);let o=await eC(t.id,r.remotePid);if(o)return o;a&&await ep(t.id,r.remotePath)}async function eA(e,t,r){if(await em(e,t)&&!await e_(e,t))return`failed to stop recording: ${et(r,"adb shell kill")}`}async function eC(e,t){if(!await ef(e,t)&&!await e_(e,t))return`failed to stop recording: Android screenrecord pid ${t} did not exit`}async function eb(e){let t,{deps:r,device:a,recording:i,stopRequestedAt:o}=e;h({level:"debug",phase:"record_stop_android_enter",data:{deviceId:a.id,remotePath:i.remotePath,remotePid:i.remotePid}}),i.stopping=!0,i.rotationTimer&&(clearTimeout(i.rotationTimer),i.rotationTimer=void 0),await i.rotationPromise;let n=await eS({device:a,recording:i});if(i.rotationFailedReason&&!n&&(i.warning??=`Android recording chunk rotation failed: ${i.rotationFailedReason}`),!n){let e=await ec({deps:r,deviceId:a.id,chunks:eo(i)});if(e)return await s(),S("COMMAND_FAILED",eM(e,i,o));await es({recording:i,deps:r})}if(await s(),n)return S("COMMAND_FAILED",eM(n,i,o));if(t)return S("COMMAND_FAILED",t);return null;async function s(){for(let e of eo(i)){let r=await eu(a.id,["shell","rm","-f",e.remotePath],{allowFailure:!0});h({level:"debug",phase:"record_stop_android_cleanup",data:{deviceId:a.id,remotePath:e.remotePath,exitCode:r.exitCode,stdout:r.stdout.trim(),stderr:r.stderr.trim()}}),0===r.exitCode||n||(t=`failed to clean up remote recording: ${et(r,"adb shell rm")}`)}}}function eM(e,t,r){return er(e,t,r).message}function eI(e){let t=e.appBundleId?.trim();return t&&t.length>0?t:void 0}function ex(e,t,r){return{verbose:e.flags?.verbose,logPath:t,traceLogPath:r.trace?.outPath,requestId:e.meta?.requestId}}async function eD(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o}=e,n=eI(r);try{await o.runIosRunnerCommand(a,{command:"recordStop",appBundleId:n},ex(t,i,r))}catch(e){h({level:"warn",phase:"record_stop_runner_failed",data:{platform:a.platform,kind:a.kind,deviceId:a.id,session:r.name,error:ee(e)}})}}async function eR(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o}=e,n=eI(r);if(n)try{await o.runIosRunnerCommand(a,{command:"snapshot",appBundleId:n,interactiveOnly:!0,compact:!0,depth:1},ex(t,i,r))}catch(e){h({level:"warn",phase:"record_start_simulator_runner_warm_failed",data:{deviceId:a.id,session:r.name,appBundleId:n,error:ee(e)}})}}async function eN(e){let t,r,{req:a,activeSession:i,sessionStore:o,device:n,logPath:s,deps:c,fpsFlag:d,recordingBase:l,appBundleId:u}=e,m=`agent-device-recording-${Date.now()}.mp4`,f=`tmp/${m}`,p=ex(a,s,i),w=async()=>c.runIosRunnerCommand(n,{command:"recordStart",outPath:m,fps:d,quality:l.quality,appBundleId:u},p);try{let e=await w();t="number"==typeof e.recorderStartUptimeMs?e.recorderStartUptimeMs:void 0,r="number"==typeof e.targetAppReadyUptimeMs?e.targetAppReadyUptimeMs:void 0}catch(a){var g,y;if(!ee(a).toLowerCase().includes("recording already in progress"))return S("COMMAND_FAILED",`failed to start recording: ${ee(a)}`);h({level:"warn",phase:"record_start_runner_desynced",data:{platform:n.platform,kind:n.kind,deviceId:n.id,session:i.name,error:ee(a)}});let e=(g=n.id,y=i.name,o.toArray().find(e=>e.name!==y&&"ios"===e.device.platform&&"device"===e.device.kind&&e.device.id===g&&e.recording?.platform==="ios-device-runner"));if(e)return S("COMMAND_FAILED",`failed to start recording: recording already in progress in session '${e.name}'`);try{await c.runIosRunnerCommand(n,{command:"recordStop",appBundleId:u},p)}catch{}try{let e=await w();t="number"==typeof e.recorderStartUptimeMs?e.recorderStartUptimeMs:void 0,r="number"==typeof e.targetAppReadyUptimeMs?e.targetAppReadyUptimeMs:void 0}catch(e){return S("COMMAND_FAILED",`failed to start recording: ${ee(e)}`)}}return{platform:"ios-device-runner",remotePath:f,runnerStartedAtUptimeMs:t,targetAppReadyUptimeMs:r,...l}}async function e$(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,fpsFlag:n,recordingBase:s,appBundleId:c}=e;try{await o.runIosRunnerCommand(a,{command:"recordStart",outPath:s.outPath,fps:n,quality:s.quality,appBundleId:c},ex(t,i,r))}catch(e){return S("COMMAND_FAILED",`failed to start recording: ${ee(e)}`)}return{platform:"macos-runner",...s}}async function eE(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,recording:n}=e;await eD({req:t,activeSession:r,device:a,logPath:i,deps:o});let s={stdout:"",stderr:"",exitCode:1};for(let e of M)if(0===(s=await o.runCmd("xcrun",["devicectl","device","copy","from","--device",a.id,"--source",n.remotePath,"--destination",n.outPath,"--domain-type","appDataContainer","--domain-identifier",e],{allowFailure:!0})).exitCode)break;if(0!==s.exitCode){let e=s.stderr.trim()||s.stdout.trim()||`devicectl exited with code ${s.exitCode}`;return S("COMMAND_FAILED",`failed to copy recording from device: ${e}`)}let c="number"!=typeof n.runnerStartedAtUptimeMs||"number"!=typeof n.targetAppReadyUptimeMs?0:Math.max(0,n.targetAppReadyUptimeMs-n.runnerStartedAtUptimeMs);return c>0&&await o.trimRecordingStart({videoPath:n.outPath,trimStartMs:c}),await ea({recording:n,deps:o,trimStartMs:c,targetLabel:"iOS recording"}),null}async function ek(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,recording:n}=e;return await eD({req:t,activeSession:r,device:a,logPath:i,deps:o}),await ea({recording:n,deps:o,targetLabel:"macOS recording"}),null}async function eF(e){let{deps:t,recording:r}=e;r.child.kill("SIGINT");let a=await eO(r.wait,5e3);return a||(await eT(t,r,"SIGINT"),(a=await eO(r.wait,2e3))||(r.child.kill("SIGTERM"),await eT(t,r,"SIGTERM"),a=await eO(r.wait,2e3)))?a:(r.child.kill("SIGKILL"),await eT(t,r,"SIGKILL"),await eO(r.wait,2e3))}async function eO(e,t){return await Promise.race([e,f(t).then(()=>null)])}async function eT(e,t,r){await eU(e,t,r)||await eL(e,t.outPath,r)}async function eL(e,t,r){let a,i=`simctl.*recordVideo.*${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}`;try{a=await e.runCmd("pgrep",["-f",i],{allowFailure:!0})}catch(e){h({level:"warn",phase:"record_stop_ios_simulator_pgrep_failed",data:{outPath:t,signal:r,error:ee(e)}});return}let o=eK(eV(a.stdout)),n=eq(o,r);h({level:n>0?"warn":"debug",phase:"record_stop_ios_simulator_signal_recorders",data:{outPath:t,signal:r,matchedPidCount:o.length,signaled:n,pgrepExitCode:a.exitCode}})}async function eU(e,t,r){let a=t.recorderPid??t.child.pid;if("number"!=typeof a||!Number.isInteger(a)||a<=0)return h({level:"debug",phase:"record_stop_ios_simulator_owned_recorder_unavailable",data:{outPath:t.outPath,signal:r,reason:"missing_recorder_pid"}}),!1;let i=await ez(e,a,t.outPath,r),o=eK([a,...i.pids]),n=eq(o,r);return h({level:n>0?"warn":"debug",phase:"record_stop_ios_simulator_signal_owned_recorder",data:{outPath:t.outPath,signal:r,recorderPid:a,childPidCount:i.pids.length,matchedPidCount:o.length,signaled:n,pgrepExitCode:i.exitCode}}),n>0}async function ez(e,t,r,a){let i;try{i=await e.runCmd("pgrep",["-P",String(t)],{allowFailure:!0})}catch(e){return h({level:"warn",phase:"record_stop_ios_simulator_owned_pgrep_failed",data:{outPath:r,signal:a,parentPid:t,error:ee(e)}}),{pids:[]}}return{pids:eV(i.stdout),exitCode:i.exitCode}}function eK(e){return Array.from(new Set(e)).filter(e=>Number.isInteger(e)&&e>0&&e!==process.pid)}function eq(e,t){let r=0;for(let a of e)try{process.kill(a,t),r+=1}catch{}return r}function eV(e){return e.split(/\s+/).map(e=>Number(e)).filter(e=>Number.isInteger(e)&&e>0)}async function eG(e){"ios"!==e.platform||0!==e.gestureEvents.length&&await f(350)}async function eH(e){for(let t=0;t<2;t+=1){try{if(o.statSync(e).size>0)return Date.now()}catch{}if(t+1>=2)break;await f(250)}return Date.now()}async function ej(e){let t,r,{req:a,activeSession:i,device:o,logPath:n,deps:s,recordingBase:c,resolvedOut:d}=e;c.showTouches&&await eR({req:a,activeSession:i,device:o,logPath:n,deps:s});let{child:l,wait:u}=s.startIosSimulatorRecording({device:o,outPath:d}),m=await eH(d);if(c.showTouches)try{let e=Date.now(),c=await s.runIosRunnerCommand(o,{command:"uptime",appBundleId:eI(i)},ex(a,n,i)),d=Date.now();t=Math.round((e+d)/2),r="number"==typeof c.currentUptimeMs?c.currentUptimeMs:void 0}catch{}return{platform:"ios",child:l,wait:u,...c,recorderPid:l.pid,startedAt:m,gestureClockOriginAtMs:void 0===r?void 0:t,gestureClockOriginUptimeMs:r}}async function eB(e){let t,{req:r,sessionName:a,sessionStore:i,activeSession:s,device:c,logPath:d,deps:l}=e;if(s.recording)return S("INVALID_ARGS","recording already in progress");let u=r.flags?.fps,m=r.flags?.quality;if(void 0!==u&&(!Number.isInteger(u)||u<1||u>120))return S("INVALID_ARGS","fps must be an integer between 1 and 120");if(void 0!==m&&(!Number.isInteger(m)||m<5||m>10))return S("INVALID_ARGS","quality must be an integer between 5 and 10");if(!A("record",c))return S("UNSUPPORTED_OPERATION","record is not supported on this device");let f=r.positionals?.[1]??`./recording-${Date.now()}.mp4`,p=v.expandHome(f,r.meta?.cwd),h={outPath:p,clientOutPath:r.meta?.clientArtifactPaths?.outPath,startedAt:Date.now(),quality:r.flags?.quality,showTouches:r.flags?.hideTouches!==!0,gestureEvents:[]};if(o.mkdirSync(n.dirname(p),{recursive:!0}),o.rmSync(p,{force:!0}),"ios"===c.platform&&"device"===c.kind){let e=eI(s);if(!e)return S("INVALID_ARGS","record on physical iOS devices requires an active app session; run open <app> first");t=await eN({req:r,activeSession:s,sessionStore:i,device:c,logPath:d,deps:l,fpsFlag:u,recordingBase:h,appBundleId:e})}else if("macos"===c.platform){let e=eI(s);if(!e)return S("INVALID_ARGS","record on macOS requires an active app session; run open <app> first");t=await e$({req:r,activeSession:s,device:c,logPath:d,deps:l,fpsFlag:u,recordingBase:h,appBundleId:e})}else t="ios"===c.platform?await ej({req:r,activeSession:s,device:c,logPath:d,deps:l,recordingBase:h,resolvedOut:p}):await eP({device:c,recordingBase:h});return"ok"in t?t:(s.recording=t,i.set(a,s),i.recordAction(s,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:{action:"start",showTouches:t.showTouches}}),{ok:!0,data:{recording:"started",outPath:t.clientOutPath??f,showTouches:t.showTouches}})}async function eW(e){let{deps:t,device:r,recording:a,stopRequestedAt:i}=e;if("android"===a.platform)return await eb({deps:t,device:r,recording:a,stopRequestedAt:i});await p("record_stop_tail_settle",()=>t.waitForRecordingTail(a),{platform:a.platform,gestureEventCount:a.gestureEvents.length});let o=await p("record_stop_ios_simulator_process",()=>eF({deps:t,recording:a}),{outPath:a.outPath});if(!o)return eX("failed to stop recording: simctl recordVideo did not exit after 5000ms and forced cleanup",a,i);if(0!==o.exitCode)return eX(`failed to stop recording: ${et(o,"simctl recordVideo")}`,a,i);if(await p("record_stop_video_stable",()=>t.waitForStableFile(a.outPath,{pollMs:150,attempts:12}),{outPath:a.outPath}),!await p("record_stop_video_playable_check",()=>t.isPlayableVideo(a.outPath),{outPath:a.outPath}))return eX(`failed to stop recording: ${a.outPath} was not finalized into a playable video`,a,i);if(void 0!==a.quality&&a.quality<10){let e=a.quality;try{await p("record_stop_resize",()=>t.resizeRecording({videoPath:a.outPath,quality:e,targetLabel:"iOS recording"}),{outPath:a.outPath,quality:e})}catch(e){a.overlayWarning=`failed to resize recording: ${ee(e)}`}}return await p("record_stop_finalize_overlay",()=>ea({recording:a,deps:t,targetLabel:"iOS recording"}),{outPath:a.outPath,showTouches:a.showTouches,gestureEventCount:a.gestureEvents.length}),null}function eX(e,t,r){let a=er(e,t,r);return function(e){try{o.rmSync(e,{force:!0})}catch{}}(t.outPath),S("COMMAND_FAILED",a.message)}async function eJ(e){var t;let r,a,{req:i,activeSession:o,device:s,logPath:c,deps:d}=e;if(!o.recording)return S("INVALID_ARGS","no active recording");let l=o.recording,u=Date.now(),m=l.invalidatedReason;o.recording=void 0;let f="ios-device-runner"===l.platform?await eE({req:i,activeSession:o,device:s,logPath:c,deps:d,recording:l}):"macos-runner"===l.platform?await ek({req:i,activeSession:o,device:s,logPath:c,deps:d,recording:l}):await eW({deps:d,device:s,recording:l,stopRequestedAt:u});if(f)return f;if(m&&"ios"===l.platform&&l.showTouches)l.overlayWarning??=`overlay unavailable: ${m}`;else if(m)return S("COMMAND_FAILED",m);return r="android"===(t=l).platform?t.chunks:void 0,a=[{field:"outPath",path:t.outPath,localPath:t.clientOutPath,fileName:n.basename(t.clientOutPath??t.outPath)}],r&&r.length>1&&a.push(...r.slice(1).map(e=>({field:"chunkPath",path:e.path,localPath:eZ(t,e.index),fileName:n.basename(eZ(t,e.index)??e.path)}))),t.telemetryPath&&a.push({field:"telemetryPath",path:t.telemetryPath,localPath:function(e){if(e.clientOutPath)return j(e.clientOutPath)}(t),fileName:n.basename(t.telemetryPath)}),{ok:!0,data:{recording:"stopped",outPath:t.outPath,telemetryPath:t.telemetryPath,artifacts:a,showTouches:t.showTouches,warning:t.warning,overlayWarning:t.overlayWarning,chunks:r?.map(e=>({index:e.index,path:eZ(t,e.index)??e.path}))}}}function eZ(e,t){if("android"===e.platform&&e.clientOutPath)return ei(e.clientOutPath,t)}async function eQ(e){let{req:t,sessionName:r,sessionStore:a,logPath:i}=e,o={runCmd:async(e,t,r)=>"xcrun"===e?await _(t,r):await m(e,t,r),startIosSimulatorRecording:e=>I().startIosSimulatorRecording(e),runIosRunnerCommand:P,waitForRecordingTail:eG,waitForStableFile:z,isPlayableVideo:K,trimRecordingStart:Z,resizeRecording:Y,overlayRecordingTouches:Q},n=a.get(r),s=n?.device??await b(t.flags??{});n||await C(s);let c=n??{name:r,device:s,createdAt:Date.now(),actions:[]},d=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(d))return S("INVALID_ARGS","record requires start|stop");if("start"===d)return eB({req:t,sessionName:r,sessionStore:a,activeSession:c,device:s,logPath:i,deps:o});let l=await eJ({req:t,activeSession:c,device:s,logPath:i,deps:o});return l.ok&&a.recordAction(c,{command:t.command,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:l.data?.outPath,showTouches:l.data?.showTouches}}),l}let eY={[x.record]:!0,[x.trace]:!0};async function e0(e){let{req:t,sessionName:r,sessionStore:a,logPath:i}=e,s=t.command;if("record"===s)return eQ({req:t,sessionName:r,sessionStore:a,logPath:i});if("trace"===s){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return S("INVALID_ARGS","trace requires start|stop");let i=a.get(r);if(!i)return S("SESSION_NOT_FOUND","No active session");if("start"===e){if(i.trace)return S("INVALID_ARGS","trace already in progress");let e=t.positionals?.[1]??a.defaultTracePath(i),r=v.expandHome(e);return o.mkdirSync(n.dirname(r),{recursive:!0}),o.appendFileSync(r,""),i.trace={outPath:r,startedAt:Date.now()},a.recordAction(i,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start",outPath:r}}),{ok:!0,data:{trace:"started",outPath:r}}}if(!i.trace)return S("INVALID_ARGS","no active trace");let c=i.trace.outPath;if(t.positionals?.[1]){let e=v.expandHome(t.positionals[1]);o.mkdirSync(n.dirname(e),{recursive:!0}),o.existsSync(c)?o.renameSync(c,e):o.appendFileSync(e,""),c=e}return i.trace=void 0,a.recordAction(i,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:c}}),{ok:!0,data:{trace:"stopped",outPath:c}}}return null}export{D as record_trace_namespaceObject};
|
|
25
|
+
`.trim();async function q(e,t={}){let r,a=t.pollMs??150,i=t.attempts??12,n=0;for(let t=0;t<i;t+=1){let t=0;try{t=o.statSync(e).size}catch{t=0}if(t>0&&t===r){if((n+=1)>=2)return}else n=0;r=t,await f(a)}}async function V(e){try{let t=await G(),r=await m(t,[e],{allowFailure:!0,timeoutMs:1e4,env:$()});if(0===r.exitCode)return!0;if(j(r.stderr,r.stdout))return B(e);return!1}catch(r){var t;if((t=r)instanceof u&&("TOOL_MISSING"===t.code||j(String(t.details?.stderr??""),String(t.details?.stdout??""))))return B(e);throw r}}async function G(){e??=k({source:K,cacheName:"video-validator",timeoutMs:3e4});try{return await e}catch(t){throw e=void 0,t}}async function H(e,t={}){let r=t.pollMs??150,a=t.attempts??12;for(let t=0;t<a;t+=1){if(await V(e))return;await f(r)}}function j(e,t){let r=`${e}
|
|
26
|
+
${t}`;return/\b(no such module ['"]AVFoundation['"]|unable to find utility ["']swiftc?["']|xcrun: error: unable to find utility ["']swiftc?["'])\b/i.test(r)}function B(e){try{let t=o.statSync(e);if(!t.isFile()||t.size<=0)return!1}catch{return!1}let t=function(e){try{let t=o.openSync(e,"r");try{let e=o.fstatSync(t).size,r=0,a=[];for(;r+8<=e&&a.length<16;){let e=Buffer.alloc(8);if(8>o.readSync(t,e,0,8,r))break;let i=e.readUInt32BE(0),n=e.toString("latin1",4,8);if(a.push(n),1===i){let e=Buffer.alloc(8);if(8>o.readSync(t,e,0,8,r+8))break;i=Number(e.readBigUInt64BE(0))}if(!Number.isFinite(i)||i<=0)break;r+=i}return a}finally{o.closeSync(t)}}catch{return[]}}(e);return t.includes("ftyp")&&t.includes("moov")}function W(e){let t=n.parse(e);return n.join(t.dir,`${t.name}.gesture-telemetry.json`)}function X(e){return[...e].sort((e,t)=>e.tMs-t.tMs)}function J(e){var t,r,a;let i,n,{recording:s,trimStartMs:c}=e,d=(i=W((t={videoPath:s.outPath,events:s.gestureEvents,trimStartMs:c}).videoPath),n={version:1,generatedAt:new Date().toISOString(),events:(r=t.events,(a=t.trimStartMs??0)>0?X(r.flatMap(e=>{let t=e.tMs-a,r="durationMs"in e?e.durationMs:void 0;return("number"==typeof r?t+r:t)<=0?[]:[{...e,tMs:Math.max(0,t)}]})):X(r))},o.writeFileSync(i,JSON.stringify(n,null,2)),i);return s.telemetryPath=d,d}function Z(e){let t=n.dirname(l(import.meta.url)),r=[l(new URL(`./${e}`,import.meta.url)),n.resolve(t,`../../ios-runner/AgentDeviceRunner/RecordingScripts/${e}`),n.resolve(t,`../../../ios-runner/AgentDeviceRunner/RecordingScripts/${e}`),n.resolve(process.cwd(),`ios-runner/AgentDeviceRunner/RecordingScripts/${e}`)];for(let e of r)if(o.existsSync(e))return e;throw new u("COMMAND_FAILED",`Missing recording helper script: ${e}`,{hint:"Ensure ios-runner/AgentDeviceRunner/RecordingScripts is present in this checkout or bundled with the package.",scriptName:e,searchedPaths:r})}async function Q(e){var t;let r,a,{videoPath:i,scriptPath:s,scriptArgs:c,commandDescription:d}=e;await q(i),await H(i);let l=(t=i,r=n.parse(t),a=`${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,n.join(r.dir,`.${r.name}.agent-device-${a}${r.ext||".mp4"}`));try{let e=await E({sourcePath:s});await m(e,["--input",i,"--output",l,...c],{timeoutMs:12e4,env:$()}),await H(l),o.renameSync(l,i)}catch(t){let e=t instanceof u?t:new u("COMMAND_FAILED",String(t),void 0,t instanceof Error?t:void 0);throw new u("COMMAND_FAILED",d,{...e.details,videoPath:i,script:s},e)}finally{o.rmSync(l,{force:!0})}}async function Y(e){let{videoPath:t,trimStartMs:a}=e;a>0&&await Q({videoPath:t,scriptPath:r??=Z("recording-trim.swift"),scriptArgs:["--trim-start-ms",String(a)],commandDescription:"Failed to trim the start of the iOS recording"})}async function ee(e){let{videoPath:r,telemetryPath:a,targetLabel:i="recording"}=e;await Q({videoPath:r,scriptPath:t??=Z("recording-overlay.swift"),scriptArgs:["--events",a],commandDescription:`Failed to add touch overlays to the ${i}`})}async function et(e){let{videoPath:t,quality:r,targetLabel:i="recording"}=e;await Q({videoPath:t,scriptPath:a??=Z("recording-resize.swift"),scriptArgs:["--quality",String(r)],commandDescription:`Failed to resize the ${i}`})}function er(e){return e instanceof Error?e.message:String(e)}function ea(e,t){return e.stderr.trim()||e.stdout.trim()||`${t} exited with code ${e.exitCode}`}function ei(e,t,r=Date.now()){let a=Math.max(0,r-t.startedAt);return a>=1e3?{message:e,tooShort:!1}:{message:`${e}. Recording stopped after ${Math.round(a)}ms; wait at least 1000ms between record start and record stop so the recorder can finalize a playable MP4`,tooShort:!0}}async function eo(e){let{recording:t,deps:r,trimStartMs:a,targetLabel:i}=e,o=J({recording:t,trimStartMs:a});if(!t.showTouches)return void h({level:"debug",phase:"record_stop_overlay_skipped",data:{reason:"hide_touches"}});if(0===t.gestureEvents.length)return void h({level:"debug",phase:"record_stop_overlay_skipped",data:{reason:"no_gesture_events"}});let n=function(e=process.platform){if("darwin"!==e)return"touch overlay burn-in is only available on macOS hosts; returning raw video plus gesture telemetry"}();if(n){t.overlayWarning??=n;return}try{await p("record_stop_overlay_export",()=>r.overlayRecordingTouches({videoPath:t.outPath,telemetryPath:o,targetLabel:i}),{targetLabel:i,gestureEventCount:t.gestureEvents.length})}catch(e){t.overlayWarning??=`failed to overlay recording touches: ${er(e)}`}}function en(e,t){if(1===t)return e;let r=n.parse(e),a=r.ext||".mp4";return n.join(r.dir,`${r.name}.part-${String(t).padStart(3,"0")}${a}`)}function es(e){return e.chunks??=[{index:1,path:e.outPath,remotePath:e.remotePath}],e.chunks}async function ec(e){let{recording:t,startNextChunk:r,finishCurrentChunk:a}=e;if(t.stopping)return;let i=await a();if(i)throw Error(i);if(t.stopping)return;let o=es(t),s=o.length+1,c=await r(n.posix.dirname(t.remotePath));t.remotePath=c.remotePath,t.remotePid=c.remotePid,o.push({index:s,path:en(t.outPath,s),remotePath:c.remotePath}),t.warning??="Android adb screenrecord is capped at 180s, so this recording was split into multiple MP4 chunks."}async function ed(e){let{recording:t,deps:r}=e;es(t).length<=1?await eo({recording:t,deps:r,targetLabel:"Android recording"}):(J({recording:t}),t.showTouches&&t.gestureEvents.length>0&&(t.overlayWarning??="touch overlay burn-in is skipped for chunked Android recordings; returning raw chunks plus gesture telemetry"))}async function el(e){for(let t of e.chunks){let r=await eu({deps:e.deps,deviceId:e.deviceId,remotePath:t.remotePath,outPath:t.path});if(r)return`failed to copy recording chunk ${t.index}: ${r}`}}async function eu(e){let t,{deps:r,deviceId:a,remotePath:i,outPath:n}=e;for(let e=0;e<2;e+=1){em(n);let s=w(a),c=await y(i,n,{allowFailure:!0,device:s});if(0!==c.exitCode)t=ea(c,"adb pull");else{await r.waitForStableFile(n,{pollMs:250,attempts:20});let t=await r.isPlayableVideo(n);if(h({level:"debug",phase:"record_stop_android_pull_validation",data:{deviceId:a,remotePath:i,outPath:n,attempt:e+1,fileSize:function(e){try{return o.statSync(e).size}catch{return 0}}(n),playable:t}}),t)return;h({level:"warn",phase:"record_stop_android_invalid_video_retry",data:{deviceId:a,remotePath:i,outPath:n,attempt:e+1}})}e<1&&await f(750)}return t?`failed to copy recording from device: ${t}`:(em(n),"failed to copy recording from device: pulled file is not a playable MP4")}function em(e){try{o.rmSync(e,{force:!0})}catch{}}async function ef(e,t,r){return await g(w(e),t,r)}async function ep(e,t){let r=await ef(e,["shell","ps","-o","pid=","-p",t],{allowFailure:!0});return 0===r.exitCode&&r.stdout.split(/\s+/).map(e=>e.trim()).includes(t)}async function eh(e,t){for(let r=0;r<40;r+=1){if(!await ep(e,t))return!0;await f(250)}return!await ep(e,t)}async function ew(e,t){let r,a=0;for(let i=0;i<20;i+=1){let i=await ef(e,["shell","stat","-c","%s",t],{allowFailure:!0}),o=0===i.exitCode?i.stdout.trim():"";if(o.length>0&&o===r){if((a+=1)>=4)return}else a=0;r=o,await f(250)}}async function eg(e,t,r){for(let a=0;a<8;a+=1){let i=await ef(e,["shell","stat","-c","%s",t],{allowFailure:!0}),o=0===i.exitCode?Number(i.stdout.trim()):NaN;if(Number.isFinite(o)&&o>0)return!0;if(!await ep(e,r))break;if(a+1>=2)return!0;await f(250)}return!1}async function ey(e){let{deviceId:t,quality:r}=e;if(void 0===r||r>=10)return;let a=await ef(t,["shell","wm","size"],{allowFailure:!0}),i=a.stdout.match(/Override size:\s*(\d+)x(\d+)/)??a.stdout.match(/Physical size:\s*(\d+)x(\d+)/);if(0!==a.exitCode||!i)throw Error(`failed to resolve Android screen size for recording quality: ${ea(a,"adb shell wm size")}`);return{width:e_(Number(i[1]),r),height:e_(Number(i[2]),r)}}function e_(e,t){return Math.max(2,2*Math.round(e*t/10/2))}async function ev(e,t){await ef(e,["shell","rm","-f",t],{allowFailure:!0})}async function eP(e,t){let r=await ef(e,["shell","kill","-9",t],{allowFailure:!0});return h({level:"warn",phase:"record_stop_android_force_signal",data:{deviceId:e,remotePid:t,exitCode:r.exitCode,stdout:r.stdout.trim(),stderr:r.stderr.trim()}}),!(0!==r.exitCode&&await ep(e,t))&&await eh(e,t)}async function eS(e){var t;let r,a,{device:i,recordingSize:o,preferredRemoteDir:n}=e,s="failed to start recording: Android screenrecord did not begin producing frames";for(let e of(t=Date.now(),r=`agent-device-recording-${t}.mp4`,a=["/sdcard","/data/local/tmp"],(n&&a.includes(n)?[n,...a.filter(e=>e!==n)]:a).map(e=>`${e}/${r}`))){let t=await ef(i.id,["shell",function(e,t){let r=["screenrecord"];return t&&r.push("--size",`${t.width}x${t.height}`),r.push(e),`${r.join(" ")} >/dev/null 2>&1 & echo $!`}(e,o)],{allowFailure:!0});if(0!==t.exitCode){s=`failed to start recording: ${ea(t,"adb shell screenrecord")}`;continue}let r=t.stdout.split(/\r?\n/).map(e=>e.trim()).filter(e=>/^\d+$/.test(e)).at(-1);if(!r){s="failed to start recording: adb did not return a valid Android screenrecord pid",await ev(i.id,e);continue}if(h({level:"debug",phase:"record_start_android_started",data:{deviceId:i.id,remotePath:e,remotePid:r}}),await eg(i.id,e,r))return{remotePath:e,remotePid:r,startedAt:Date.now()};s="failed to start recording: Android screenrecord did not begin producing frames",await eP(i.id,r),await ev(i.id,e)}return{error:S("COMMAND_FAILED",s)}}async function eA(e){let t,{device:r,recordingBase:a}=e;try{t=await ey({deviceId:r.id,quality:a.quality})}catch(e){return S("COMMAND_FAILED",e instanceof Error?e.message:String(e))}let i=await eS({device:r,recordingSize:t});if("error"in i)return i.error;let o={platform:"android",remotePath:i.remotePath,remotePid:i.remotePid,chunks:[{index:1,path:a.outPath,remotePath:i.remotePath}],...a,startedAt:i.startedAt};return!function e(t){let{recording:r,startNextChunk:a,finishCurrentChunk:i}=t,o=setTimeout(()=>{r.rotationPromise=ec({recording:r,startNextChunk:a,finishCurrentChunk:i}).catch(e=>{r.rotationFailedReason=e instanceof Error?e.message:String(e)}).finally(()=>{r.rotationPromise=void 0,r.stopping||r.rotationFailedReason||e({recording:r,startNextChunk:a,finishCurrentChunk:i})})},17e4);o.unref?.(),r.rotationTimer=o}({recording:o,finishCurrentChunk:async()=>await eC({device:r,recording:o,waitForRemoteFileStability:!1}),startNextChunk:async e=>{let a=await eS({device:r,recordingSize:t,preferredRemoteDir:e});if("error"in a)throw Error(a.error.ok?"failed to start next Android recording chunk":a.error.error.message);return a}}),o}async function eC(e){let{device:t,recording:r,waitForRemoteFileStability:a=!0}=e;await ep(t.id,r.remotePid)||(r.warning??=function(e){if(!(Date.now()-e.startedAt<178e3))return"Android adb screenrecord stopped before record stop, likely after reaching the 180s platform limit. The MP4 may be truncated; final interactions after the limit are not in the video."}(r));let i=await ef(t.id,["shell","kill","-2",r.remotePid],{allowFailure:!0});if(h({level:"debug",phase:"record_stop_android_signal",data:{deviceId:t.id,remotePath:r.remotePath,remotePid:r.remotePid,exitCode:i.exitCode,stdout:i.stdout.trim(),stderr:i.stderr.trim()}}),0!==i.exitCode)return await eb(t.id,r.remotePid,i);let o=await eM(t.id,r.remotePid);if(o)return o;a&&await ew(t.id,r.remotePath)}async function eb(e,t,r){if(await ep(e,t)&&!await eP(e,t))return`failed to stop recording: ${ea(r,"adb shell kill")}`}async function eM(e,t){if(!await eh(e,t)&&!await eP(e,t))return`failed to stop recording: Android screenrecord pid ${t} did not exit`}async function eI(e){let t,{deps:r,device:a,recording:i,stopRequestedAt:o}=e;h({level:"debug",phase:"record_stop_android_enter",data:{deviceId:a.id,remotePath:i.remotePath,remotePid:i.remotePid}}),i.stopping=!0,i.rotationTimer&&(clearTimeout(i.rotationTimer),i.rotationTimer=void 0),await i.rotationPromise;let n=await eC({device:a,recording:i});if(i.rotationFailedReason&&!n&&(i.warning??=`Android recording chunk rotation failed: ${i.rotationFailedReason}`),!n){let e=await el({deps:r,deviceId:a.id,chunks:es(i)});if(e)return await s(),S("COMMAND_FAILED",ex(e,i,o));await ed({recording:i,deps:r})}if(await s(),n)return S("COMMAND_FAILED",ex(n,i,o));if(t)return S("COMMAND_FAILED",t);return null;async function s(){for(let e of es(i)){let r=await ef(a.id,["shell","rm","-f",e.remotePath],{allowFailure:!0});h({level:"debug",phase:"record_stop_android_cleanup",data:{deviceId:a.id,remotePath:e.remotePath,exitCode:r.exitCode,stdout:r.stdout.trim(),stderr:r.stderr.trim()}}),0===r.exitCode||n||(t=`failed to clean up remote recording: ${ea(r,"adb shell rm")}`)}}}function ex(e,t,r){return ei(e,t,r).message}function eD(e){let t=e.appBundleId?.trim();return t&&t.length>0?t:void 0}function eR(e,t,r){return{verbose:e.flags?.verbose,logPath:t,traceLogPath:r.trace?.outPath,requestId:e.meta?.requestId}}async function eN(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o}=e,n=eD(r);try{await o.runIosRunnerCommand(a,{command:"recordStop",appBundleId:n},eR(t,i,r))}catch(e){h({level:"warn",phase:"record_stop_runner_failed",data:{platform:a.platform,kind:a.kind,deviceId:a.id,session:r.name,error:er(e)}})}}async function e$(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o}=e,n=eD(r);if(n)try{await o.runIosRunnerCommand(a,{command:"snapshot",appBundleId:n,interactiveOnly:!0,compact:!0,depth:1},eR(t,i,r))}catch(e){h({level:"warn",phase:"record_start_simulator_runner_warm_failed",data:{deviceId:a.id,session:r.name,appBundleId:n,error:er(e)}})}}async function eE(e){let t,r,{req:a,activeSession:i,sessionStore:o,device:n,logPath:s,deps:c,fpsFlag:d,recordingBase:l,appBundleId:u}=e,m=`agent-device-recording-${Date.now()}.mp4`,f=`tmp/${m}`,p=eR(a,s,i),w=async()=>c.runIosRunnerCommand(n,{command:"recordStart",outPath:m,fps:d,quality:l.quality,appBundleId:u},p);try{let e=await w();t="number"==typeof e.recorderStartUptimeMs?e.recorderStartUptimeMs:void 0,r="number"==typeof e.targetAppReadyUptimeMs?e.targetAppReadyUptimeMs:void 0}catch(a){var g,y;if(!er(a).toLowerCase().includes("recording already in progress"))return S("COMMAND_FAILED",`failed to start recording: ${er(a)}`);h({level:"warn",phase:"record_start_runner_desynced",data:{platform:n.platform,kind:n.kind,deviceId:n.id,session:i.name,error:er(a)}});let e=(g=n.id,y=i.name,o.toArray().find(e=>e.name!==y&&"ios"===e.device.platform&&"device"===e.device.kind&&e.device.id===g&&e.recording?.platform==="ios-device-runner"));if(e)return S("COMMAND_FAILED",`failed to start recording: recording already in progress in session '${e.name}'`);try{await c.runIosRunnerCommand(n,{command:"recordStop",appBundleId:u},p)}catch{}try{let e=await w();t="number"==typeof e.recorderStartUptimeMs?e.recorderStartUptimeMs:void 0,r="number"==typeof e.targetAppReadyUptimeMs?e.targetAppReadyUptimeMs:void 0}catch(e){return S("COMMAND_FAILED",`failed to start recording: ${er(e)}`)}}return{platform:"ios-device-runner",remotePath:f,runnerStartedAtUptimeMs:t,targetAppReadyUptimeMs:r,...l}}async function ek(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,fpsFlag:n,recordingBase:s,appBundleId:c}=e;try{await o.runIosRunnerCommand(a,{command:"recordStart",outPath:s.outPath,fps:n,quality:s.quality,appBundleId:c},eR(t,i,r))}catch(e){return S("COMMAND_FAILED",`failed to start recording: ${er(e)}`)}return{platform:"macos-runner",...s}}async function eF(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,recording:n}=e;await eN({req:t,activeSession:r,device:a,logPath:i,deps:o});let s={stdout:"",stderr:"",exitCode:1};for(let e of x)if(0===(s=await o.runCmd("xcrun",["devicectl","device","copy","from","--device",a.id,"--source",n.remotePath,"--destination",n.outPath,"--domain-type","appDataContainer","--domain-identifier",e],{allowFailure:!0})).exitCode)break;if(0!==s.exitCode){let e=s.stderr.trim()||s.stdout.trim()||`devicectl exited with code ${s.exitCode}`;return S("COMMAND_FAILED",`failed to copy recording from device: ${e}`)}let c="number"!=typeof n.runnerStartedAtUptimeMs||"number"!=typeof n.targetAppReadyUptimeMs?0:Math.max(0,n.targetAppReadyUptimeMs-n.runnerStartedAtUptimeMs);return c>0&&await o.trimRecordingStart({videoPath:n.outPath,trimStartMs:c}),await eo({recording:n,deps:o,trimStartMs:c,targetLabel:"iOS recording"}),null}async function eO(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,recording:n}=e;return await eN({req:t,activeSession:r,device:a,logPath:i,deps:o}),await eo({recording:n,deps:o,targetLabel:"macOS recording"}),null}async function eL(e){let{deps:t,recording:r}=e;r.child.kill("SIGINT");let a=await eT(r.wait,5e3);return a||(await eU(t,r,"SIGINT"),(a=await eT(r.wait,2e3))||(r.child.kill("SIGTERM"),await eU(t,r,"SIGTERM"),a=await eT(r.wait,2e3)))?a:(r.child.kill("SIGKILL"),await eU(t,r,"SIGKILL"),await eT(r.wait,2e3))}async function eT(e,t){return await Promise.race([e,f(t).then(()=>null)])}async function eU(e,t,r){await eK(e,t,r)||await ez(e,t.outPath,r)}async function ez(e,t,r){let a,i=`simctl.*recordVideo.*${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}`;try{a=await e.runCmd("pgrep",["-f",i],{allowFailure:!0})}catch(e){h({level:"warn",phase:"record_stop_ios_simulator_pgrep_failed",data:{outPath:t,signal:r,error:er(e)}});return}let o=eV(eH(a.stdout)),n=eG(o,r);h({level:n>0?"warn":"debug",phase:"record_stop_ios_simulator_signal_recorders",data:{outPath:t,signal:r,matchedPidCount:o.length,signaled:n,pgrepExitCode:a.exitCode}})}async function eK(e,t,r){let a=t.recorderPid??t.child.pid;if("number"!=typeof a||!Number.isInteger(a)||a<=0)return h({level:"debug",phase:"record_stop_ios_simulator_owned_recorder_unavailable",data:{outPath:t.outPath,signal:r,reason:"missing_recorder_pid"}}),!1;let i=await eq(e,a,t.outPath,r),o=eV([a,...i.pids]),n=eG(o,r);return h({level:n>0?"warn":"debug",phase:"record_stop_ios_simulator_signal_owned_recorder",data:{outPath:t.outPath,signal:r,recorderPid:a,childPidCount:i.pids.length,matchedPidCount:o.length,signaled:n,pgrepExitCode:i.exitCode}}),n>0}async function eq(e,t,r,a){let i;try{i=await e.runCmd("pgrep",["-P",String(t)],{allowFailure:!0})}catch(e){return h({level:"warn",phase:"record_stop_ios_simulator_owned_pgrep_failed",data:{outPath:r,signal:a,parentPid:t,error:er(e)}}),{pids:[]}}return{pids:eH(i.stdout),exitCode:i.exitCode}}function eV(e){return Array.from(new Set(e)).filter(e=>Number.isInteger(e)&&e>0&&e!==process.pid)}function eG(e,t){let r=0;for(let a of e)try{process.kill(a,t),r+=1}catch{}return r}function eH(e){return e.split(/\s+/).map(e=>Number(e)).filter(e=>Number.isInteger(e)&&e>0)}async function ej(e){"ios"!==e.platform||0!==e.gestureEvents.length&&await f(350)}async function eB(e){for(let t=0;t<2;t+=1){try{if(o.statSync(e).size>0)return Date.now()}catch{}if(t+1>=2)break;await f(250)}return Date.now()}async function eW(e){let t,r,{req:a,activeSession:i,device:o,logPath:n,deps:s,recordingBase:c,resolvedOut:d}=e;c.showTouches&&await e$({req:a,activeSession:i,device:o,logPath:n,deps:s});let{child:l,wait:u}=s.startIosSimulatorRecording({device:o,outPath:d}),m=await eB(d);if(c.showTouches)try{let e=Date.now(),c=await s.runIosRunnerCommand(o,{command:"uptime",appBundleId:eD(i)},eR(a,n,i)),d=Date.now();t=Math.round((e+d)/2),r="number"==typeof c.currentUptimeMs?c.currentUptimeMs:void 0}catch{}return{platform:"ios",child:l,wait:u,...c,recorderPid:l.pid,startedAt:m,gestureClockOriginAtMs:void 0===r?void 0:t,gestureClockOriginUptimeMs:r}}async function eX(e){let t,{req:r,sessionName:a,sessionStore:i,activeSession:s,device:c,logPath:d,deps:l}=e;if(s.recording)return S("INVALID_ARGS","recording already in progress");let u=r.flags?.fps,m=r.flags?.quality;if(void 0!==u&&(!Number.isInteger(u)||u<1||u>120))return S("INVALID_ARGS","fps must be an integer between 1 and 120");if(void 0!==m&&(!Number.isInteger(m)||m<5||m>10))return S("INVALID_ARGS","quality must be an integer between 5 and 10");if(!A("record",c))return S("UNSUPPORTED_OPERATION","record is not supported on this device");let f=r.positionals?.[1]??`./recording-${Date.now()}.mp4`,p=v.expandHome(f,r.meta?.cwd),h={outPath:p,clientOutPath:r.meta?.clientArtifactPaths?.outPath,startedAt:Date.now(),quality:r.flags?.quality,showTouches:r.flags?.hideTouches!==!0,gestureEvents:[]};if(o.mkdirSync(n.dirname(p),{recursive:!0}),o.rmSync(p,{force:!0}),"ios"===c.platform&&"device"===c.kind){let e=eD(s);if(!e)return S("INVALID_ARGS","record on physical iOS devices requires an active app session; run open <app> first");t=await eE({req:r,activeSession:s,sessionStore:i,device:c,logPath:d,deps:l,fpsFlag:u,recordingBase:h,appBundleId:e})}else if("macos"===c.platform){let e=eD(s);if(!e)return S("INVALID_ARGS","record on macOS requires an active app session; run open <app> first");t=await ek({req:r,activeSession:s,device:c,logPath:d,deps:l,fpsFlag:u,recordingBase:h,appBundleId:e})}else t="ios"===c.platform?await eW({req:r,activeSession:s,device:c,logPath:d,deps:l,recordingBase:h,resolvedOut:p}):await eA({device:c,recordingBase:h});if("ok"in t)return t;s.recording=t,i.set(a,s);let w=i.ensureSessionDir(a);return i.recordAction(s,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:{action:"start",showTouches:t.showTouches}}),{ok:!0,data:{recording:"started",outPath:t.clientOutPath??f,sessionStateDir:w,showTouches:t.showTouches}}}async function eJ(e){let{deps:t,device:r,recording:a,stopRequestedAt:i}=e;if("android"===a.platform)return await eI({deps:t,device:r,recording:a,stopRequestedAt:i});await p("record_stop_tail_settle",()=>t.waitForRecordingTail(a),{platform:a.platform,gestureEventCount:a.gestureEvents.length});let o=await p("record_stop_ios_simulator_process",()=>eL({deps:t,recording:a}),{outPath:a.outPath});if(!o)return eZ("failed to stop recording: simctl recordVideo did not exit after 5000ms and forced cleanup",a,i);if(0!==o.exitCode)return eZ(`failed to stop recording: ${ea(o,"simctl recordVideo")}`,a,i);if(await p("record_stop_video_stable",()=>t.waitForStableFile(a.outPath,{pollMs:150,attempts:12}),{outPath:a.outPath}),!await p("record_stop_video_playable_check",()=>t.isPlayableVideo(a.outPath),{outPath:a.outPath}))return eZ(`failed to stop recording: ${a.outPath} was not finalized into a playable video`,a,i);if(void 0!==a.quality&&a.quality<10){let e=a.quality;try{await p("record_stop_resize",()=>t.resizeRecording({videoPath:a.outPath,quality:e,targetLabel:"iOS recording"}),{outPath:a.outPath,quality:e})}catch(e){a.overlayWarning=`failed to resize recording: ${er(e)}`}}return await p("record_stop_finalize_overlay",()=>eo({recording:a,deps:t,targetLabel:"iOS recording"}),{outPath:a.outPath,showTouches:a.showTouches,gestureEventCount:a.gestureEvents.length}),null}function eZ(e,t,r){let a=ei(e,t,r);return function(e){try{o.rmSync(e,{force:!0})}catch{}}(t.outPath),S("COMMAND_FAILED",a.message)}async function eQ(e){var t;let r,a,{req:i,activeSession:o,device:s,logPath:c,deps:d}=e;if(!o.recording)return S("INVALID_ARGS","no active recording");let l=o.recording,u=Date.now(),m=l.invalidatedReason;o.recording=void 0;let f="ios-device-runner"===l.platform?await eF({req:i,activeSession:o,device:s,logPath:c,deps:d,recording:l}):"macos-runner"===l.platform?await eO({req:i,activeSession:o,device:s,logPath:c,deps:d,recording:l}):await eJ({deps:d,device:s,recording:l,stopRequestedAt:u});if(f)return f;if(m&&"ios"===l.platform&&l.showTouches)l.overlayWarning??=`overlay unavailable: ${m}`;else if(m)return S("COMMAND_FAILED",m);return r="android"===(t=l).platform?t.chunks:void 0,a=[{field:"outPath",path:t.outPath,localPath:t.clientOutPath,fileName:n.basename(t.clientOutPath??t.outPath)}],r&&r.length>1&&a.push(...r.slice(1).map(e=>({field:"chunkPath",path:e.path,localPath:eY(t,e.index),fileName:n.basename(eY(t,e.index)??e.path)}))),t.telemetryPath&&a.push({field:"telemetryPath",path:t.telemetryPath,localPath:function(e){if(e.clientOutPath)return W(e.clientOutPath)}(t),fileName:n.basename(t.telemetryPath)}),{ok:!0,data:{recording:"stopped",outPath:t.outPath,telemetryPath:t.telemetryPath,artifacts:a,showTouches:t.showTouches,warning:t.warning,overlayWarning:t.overlayWarning,chunks:r?.map(e=>({index:e.index,path:eY(t,e.index)??e.path}))}}}function eY(e,t){if("android"===e.platform&&e.clientOutPath)return en(e.clientOutPath,t)}function e0(e,t,r,a={}){r.recordOnlySession&&(a.writeLog&&e.writeSessionLog(r),e.delete(t))}async function e1(e){let{req:t,sessionName:r,sessionStore:a,logPath:i}=e,o={runCmd:async(e,t,r)=>"xcrun"===e?await _(t,r):await m(e,t,r),startIosSimulatorRecording:e=>D().startIosSimulatorRecording(e),runIosRunnerCommand:P,waitForRecordingTail:ej,waitForStableFile:q,isPlayableVideo:V,trimRecordingStart:Y,resizeRecording:et,overlayRecordingTouches:ee},n=a.get(r),s=n?.device??await I(t.flags??{});n||await C(s);let c=n??{name:b(t),sessionScope:M(t),device:s,createdAt:Date.now(),recordOnlySession:!0,actions:[]},d=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(d))return S("INVALID_ARGS","record requires start|stop");if("start"===d)return eX({req:t,sessionName:r,sessionStore:a,activeSession:c,device:s,logPath:i,deps:o});let l=await eQ({req:t,activeSession:c,device:s,logPath:i,deps:o});return l.ok?(a.recordAction(c,{command:t.command,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:l.data?.outPath,showTouches:l.data?.showTouches}}),e0(a,r,c,{writeLog:!0})):e0(a,r,c),l}let e2={[R.record]:!0,[R.trace]:!0};async function e4(e){let{req:t,sessionName:r,sessionStore:a,logPath:i}=e,s=t.command;if("record"===s)return e1({req:t,sessionName:r,sessionStore:a,logPath:i});if("trace"===s){let e=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(e))return S("INVALID_ARGS","trace requires start|stop");let i=a.get(r);if(!i)return S("SESSION_NOT_FOUND","No active session");if("start"===e){if(i.trace)return S("INVALID_ARGS","trace already in progress");let e=t.positionals?.[1]??a.defaultTracePath(i),r=v.expandHome(e);return o.mkdirSync(n.dirname(r),{recursive:!0}),o.appendFileSync(r,""),i.trace={outPath:r,startedAt:Date.now()},a.recordAction(i,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{action:"start",outPath:r}}),{ok:!0,data:{trace:"started",outPath:r}}}if(!i.trace)return S("INVALID_ARGS","no active trace");let c=i.trace.outPath;if(t.positionals?.[1]){let e=v.expandHome(t.positionals[1]);o.mkdirSync(n.dirname(e),{recursive:!0}),o.existsSync(c)?o.renameSync(c,e):o.appendFileSync(e,""),c=e}return i.trace=void 0,a.recordAction(i,{command:s,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:c}}),{ok:!0,data:{trace:"stopped",outPath:c}}}return null}export{N as record_trace_namespaceObject};
|