agent-device 0.17.10 → 0.18.1
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 +3 -2
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.17.10.apk → agent-device-android-multitouch-helper-0.18.1.apk} +0 -0
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.18.1.apk.sha256 +1 -0
- package/android-multitouch-helper/dist/{agent-device-android-multitouch-helper-0.17.10.manifest.json → agent-device-android-multitouch-helper-0.18.1.manifest.json} +4 -4
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.17.10.apk → agent-device-android-snapshot-helper-0.18.1.apk} +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.18.1.apk.sha256 +1 -0
- package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.17.10.manifest.json → agent-device-android-snapshot-helper-0.18.1.manifest.json} +6 -6
- package/apple-runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +156 -0
- package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj +10 -16
- package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +2 -0
- package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +3 -1
- package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +2 -0
- package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +2 -4
- package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +9 -36
- package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +2 -0
- package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +2 -0
- package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +2 -0
- package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +2 -0
- package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +0 -1
- package/dist/src/1986.js +1 -0
- package/dist/src/2748.js +1 -1
- package/dist/src/2948.js +21 -21
- package/dist/src/314.js +1 -1
- package/dist/src/3340.js +8 -7
- package/dist/src/432.js +4 -4
- package/dist/src/4418.js +1 -1
- package/dist/src/4428.js +3 -3
- package/dist/src/485.js +1 -0
- package/dist/src/495.js +5 -0
- package/dist/src/5484.js +1 -1
- package/dist/src/5643.js +1 -0
- package/dist/src/587.js +1 -1
- package/dist/src/5913.js +4 -0
- package/dist/src/6849.js +1 -0
- package/dist/src/6918.js +1 -1
- package/dist/src/6967.js +1 -0
- package/dist/src/760.js +1 -0
- package/dist/src/7871.js +1 -0
- package/dist/src/8225.js +1 -1
- package/dist/src/8391.js +1 -0
- package/dist/src/8404.js +2 -0
- package/dist/src/8414.js +2 -0
- package/dist/src/8875.js +1 -1
- package/dist/src/8936.js +1 -0
- package/dist/src/9414.js +1 -0
- package/dist/src/9581.js +5 -5
- package/dist/src/9624.js +1 -1
- package/dist/src/9722.js +37 -37
- package/dist/src/android-adb.d.ts +19 -54
- package/dist/src/android-adb.js +1 -1
- package/dist/src/android.js +5 -5
- package/dist/src/apps.js +9 -11
- package/dist/src/audio-probe.js +1 -0
- package/dist/src/batch.d.ts +50 -25
- package/dist/src/batch.js +1 -1
- package/dist/src/cli-help.js +104 -37
- package/dist/src/cli.js +36 -27
- package/dist/src/contracts.d.ts +46 -122
- package/dist/src/contracts.js +1 -1
- package/dist/src/debug-symbols.js +3 -0
- package/dist/src/devices.js +1 -1
- package/dist/src/devices~1.js +1 -1
- package/dist/src/devices~2.js +1 -1
- package/dist/src/find.js +1 -1
- package/dist/src/finders.d.ts +2 -6
- package/dist/src/finders.js +1 -1
- package/dist/src/generic.js +1 -9
- package/dist/src/handler-utils.js +1 -0
- package/dist/src/index.d.ts +500 -182
- package/dist/src/index.js +1 -1
- package/dist/src/input-actions.js +1 -1
- package/dist/src/input-actions~1.js +1 -1
- package/dist/src/install-source.d.ts +0 -26
- package/dist/src/install-source.js +1 -1
- package/dist/src/interaction.js +1 -1
- package/dist/src/interactor.js +1 -0
- package/dist/src/internal/bin.js +1 -1
- package/dist/src/internal/companion-tunnel.js +1 -1
- package/dist/src/internal/png-worker.js +1 -1
- package/dist/src/lease.js +1 -1
- package/dist/src/linux.js +1 -1
- package/dist/src/metro.d.ts +10 -134
- package/dist/src/metro.js +1 -1
- package/dist/src/notifications.js +1 -1
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-trace-recording.js +3 -3
- package/dist/src/record-trace.js +1 -1
- package/dist/src/remote-config.d.ts +28 -24
- package/dist/src/remote-config.js +1 -1
- package/dist/src/runner-sequence.js +1 -0
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/selectors.d.ts +7 -3
- package/dist/src/selectors.js +1 -1
- package/dist/src/server.js +2 -2
- package/dist/src/session.js +11 -8
- package/dist/src/simulator.js +2 -0
- package/dist/src/snapshot.js +2 -2
- package/dist/src/web.js +1 -1
- package/macos-helper/Sources/AgentDeviceMacOSHelper/AudioProbe.swift +297 -0
- package/macos-helper/Sources/AgentDeviceMacOSHelper/main.swift +30 -0
- package/package.json +15 -11
- package/server.json +2 -2
- package/android-multitouch-helper/README.md +0 -41
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.17.10.apk.sha256 +0 -1
- package/android-snapshot-helper/README.md +0 -88
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.17.10.apk.sha256 +0 -1
- package/dist/src/2133.js +0 -1
- package/dist/src/2577.js +0 -1
- package/dist/src/2954.js +0 -2
- package/dist/src/4291.js +0 -1
- package/dist/src/4723.js +0 -1
- package/dist/src/5628.js +0 -1
- package/dist/src/5910.js +0 -2
- package/dist/src/6024.js +0 -1
- package/dist/src/733.js +0 -1
- package/dist/src/7394.js +0 -6
- package/dist/src/8275.js +0 -2
- package/dist/src/8358.js +0 -4
- package/dist/src/9919.js +0 -6
- package/dist/src/android-snapshot-helper.d.ts +0 -359
- package/dist/src/android-snapshot-helper.js +0 -1
- package/dist/src/apple.js +0 -1
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.swift +0 -17
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunner/Assets.xcassets/AccentColor.colorset/Contents.json +0 -11
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunner/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -36
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunner/Assets.xcassets/AppIcon.appiconset/logo.jpg +0 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunner/Assets.xcassets/Contents.json +0 -6
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunner/Assets.xcassets/Logo.imageset/Contents.json +0 -21
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunner/Assets.xcassets/Logo.imageset/logo.jpg +0 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunner/Assets.xcassets/PoweredBy.imageset/Contents.json +0 -21
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunner/Assets.xcassets/PoweredBy.imageset/powered-by.png +0 -0
- package/ios-runner/AgentDeviceRunner/AgentDeviceRunner/ContentView.swift +0 -34
- package/ios-runner/README.md +0 -53
- package/ios-runner/RUNNER_PROTOCOL.md +0 -76
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/xcshareddata/xcschemes/AgentDeviceRunner.xcscheme +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests.entitlements +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.h +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.m +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Environment.swift +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Exceptions.swift +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/RecordingScripts/recording-resize.swift +0 -0
- /package/{ios-runner → apple-runner}/AgentDeviceRunner/RecordingScripts/recording-trim.swift +0 -0
- /package/dist/src/{4069.js → 4917.js} +0 -0
- /package/dist/src/{9836.js → 893.js} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
let e,t,r,
|
|
1
|
+
let e,t,r,a;import i from"node:fs";import o from"node:path";import{createHash as n}from"node:crypto";import s from"node:os";import{fileURLToPath as c}from"node:url";import{AppError as d}from"./485.js";import{runCmd as l}from"./8875.js";import{acquireProcessLock as u}from"./8391.js";import{readProcessStartTime as p}from"./9414.js";import{sleep as m}from"./5560.js";import{withDiagnosticTimer as h,emitDiagnostic as f}from"./432.js";import{androidDeviceForSerial as w,runAndroidAdb as g,pullAndroidAdbFile as y}from"./3340.js";import{runXcrun as _,SessionStore as v,IOS_RUNNER_CONTAINER_BUNDLE_IDS as P,requireCommandSupported as S,errorResponse as A,resolveImplicitSessionScope as b,resolvePublicSessionName as M,resolveTargetDevice as C,runAppleRunnerCommand as x}from"./9722.js";import{buildAppleRunnerRequestOptions as R,ensureDeviceReady as I}from"./8414.js";import{defaultRecordingPath as D,appendRecordingExtensionWhenMissing as N}from"./5643.js";import{resolveWebProvider as E}from"./provider.js";import{resolveRecordingProvider as $}from"./recording-provider.js";import{recordingQualityInputToExportQuality as O,RECORDING_EXPORT_QUALITIES as k}from"./2948.js";import{recordSessionAction as F}from"./handler-utils.js";function T(e=process.env){let t=z(),r=o.join(t,"home"),a=o.join(t,"module-cache");return i.mkdirSync(r,{recursive:!0}),i.mkdirSync(a,{recursive:!0}),{...e,HOME:r,CLANG_MODULE_CACHE_PATH:a}}async function L(e){let t=i.statSync(e.sourcePath),r=i.readFileSync(e.sourcePath),a=H(e.cacheName??o.basename(e.sourcePath,o.extname(e.sourcePath))),n=j(["2",process.platform,process.arch,o.resolve(e.sourcePath),t.size,r]),s=o.join(z(),"bin",`${a}-${n}`);return await K({sourcePath:e.sourcePath,executablePath:s,timeoutMs:e.timeoutMs}),s}async function U(e){let t=H(e.cacheName),r=j(["2",process.platform,process.arch,e.source]),a=o.join(z(),"sources",`${t}-${r}.swift`),i=o.join(z(),"bin",`${t}-${r}`);return await K({sourcePath:a,executablePath:i,sourceText:e.source,timeoutMs:e.timeoutMs}),i}function z(){let e=process.env.AGENT_DEVICE_SWIFT_CACHE_DIR?.trim();return e?o.resolve(e):o.join(s.tmpdir(),"agent-device-swift-cache")}async function K(e){if(V(e.executablePath))return;let t=o.dirname(e.executablePath);i.mkdirSync(t,{recursive:!0});let r=`${e.executablePath}.lock`,a=await G(r,e.executablePath,e.timeoutMs??12e4);if(!a)return;let n=i.mkdtempSync(o.join(t,`.${o.basename(e.executablePath)}.${process.pid}.`)),s=o.join(n,o.basename(e.executablePath));try{if(V(e.executablePath))return;void 0===e.sourceText||i.existsSync(e.sourcePath)||(i.mkdirSync(o.dirname(e.sourcePath),{recursive:!0}),i.writeFileSync(e.sourcePath,e.sourceText)),await l("xcrun",["swiftc",e.sourcePath,"-o",s],{timeoutMs:e.timeoutMs??12e4,env:T()}),i.renameSync(s,e.executablePath)}finally{i.rmSync(n,{recursive:!0,force:!0}),await a()}}async function G(e,t,r){if(V(t))return null;try{return await u({lockDirPath:e,owner:{pid:process.pid,startTime:p(process.pid),acquiredAtMs:Date.now()},timeoutMs:r,pollMs:25,ownerGraceMs:r,description:`Swift cache lock: ${e} (${r}ms)`})}catch(t){if(t instanceof d&&t.message.startsWith("Timed out waiting for Swift cache lock:"))throw new d("COMMAND_FAILED",t.message,{...t.details,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.`});throw t}}function V(e){try{return i.accessSync(e,i.constants.X_OK),i.statSync(e).isFile()}catch{return!1}}function H(e){return e.replaceAll(/[^A-Za-z0-9._-]/g,"-").replaceAll(/^-+|-+$/g,"")||"swift-helper"}function j(e){let t=n("sha256");for(let r of e)t.update(Buffer.isBuffer(r)?r:String(r)),t.update("\0");return t.digest("hex").slice(0,16)}let q=`
|
|
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 W(e){try{let t=a.statSync(e);if(!t.isFile()||t.size<=0)return!1}catch{return!1}let t=function(e){try{let t=a.openSync(e,"r");try{let e=a.fstatSync(t).size,r=0,i=[];for(;r+8<=e&&i.length<16;){let e=Buffer.alloc(8);if(8>a.readSync(t,e,0,8,r))break;let o=e.readUInt32BE(0),n=e.toString("latin1",4,8);if(i.push(n),1===o){let e=Buffer.alloc(8);if(8>a.readSync(t,e,0,8,r+8))break;o=Number(e.readBigUInt64BE(0))}if(!Number.isFinite(o)||o<=0)break;r+=o}return i}finally{a.closeSync(t)}}catch{return[]}}(e);return t.includes("ftyp")&&t.includes("moov")}function B(e){let t=o.parse(e);return o.join(t.dir,`${t.name}.gesture-telemetry.json`)}function X(e){return[...e].sort((e,t)=>e.tMs-t.tMs)}function Q(e){var t,r,i;let o,n,{recording:s,trimStartMs:c}=e,d=(o=B((t={videoPath:s.outPath,events:s.gestureEvents,trimStartMs:c}).videoPath),n={version:1,generatedAt:new Date().toISOString(),events:(r=t.events,(i=t.trimStartMs??0)>0?X(r.flatMap(e=>{let t=e.tMs-i,r="durationMs"in e?e.durationMs:void 0;return("number"==typeof r?t+r:t)<=0?[]:[{...e,tMs:Math.max(0,t)}]})):X(r))},a.writeFileSync(o,JSON.stringify(n,null,2)),o);return s.telemetryPath=d,d}function J(e){let t=o.dirname(c(import.meta.url)),r=[c(new URL(`./${e}`,import.meta.url)),o.resolve(t,`../../ios-runner/AgentDeviceRunner/RecordingScripts/${e}`),o.resolve(t,`../../../ios-runner/AgentDeviceRunner/RecordingScripts/${e}`),o.resolve(process.cwd(),`ios-runner/AgentDeviceRunner/RecordingScripts/${e}`)];for(let e of r)if(a.existsSync(e))return e;throw new d("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 Z(e){var t;let r,i,{videoPath:n,scriptPath:s,scriptArgs:c,commandDescription:u}=e;await G(n),await j(n);let m=(t=n,r=o.parse(t),i=`${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,o.join(r.dir,`.${r.name}.agent-device-${i}${r.ext||".mp4"}`));try{let e=await k({sourcePath:s});await l(e,["--input",n,"--output",m,...c],{timeoutMs:12e4,env:N()}),await j(m),a.renameSync(m,n)}catch(t){let e=t instanceof d?t:new d("COMMAND_FAILED",String(t),void 0,t instanceof Error?t:void 0);throw new d("COMMAND_FAILED",u,{...e.details,videoPath:n,script:s},e)}finally{a.rmSync(m,{force:!0})}}async function Y(e){let{videoPath:t,trimStartMs:i}=e;i>0&&await Z({videoPath:t,scriptPath:r??=J("recording-trim.swift"),scriptArgs:["--trim-start-ms",String(i)],commandDescription:"Failed to trim the start of the iOS recording"})}async function ee(e){let{videoPath:r,telemetryPath:i,exportQuality:a="medium",targetLabel:o="recording"}=e;await Z({videoPath:r,scriptPath:t??=J("recording-overlay.swift"),scriptArgs:["--events",i,"--quality",a],commandDescription:`Failed to add touch overlays to the ${o}`})}async function et(e){let{videoPath:t,maxSize:r,exportQuality:a="medium",targetLabel:o="recording"}=e;await Z({videoPath:t,scriptPath:i??=J("recording-resize.swift"),scriptArgs:["--max-size",String(r),"--quality",a],commandDescription:`Failed to resize the ${o}`})}function er(e){return e instanceof Error?e.message:String(e)}function ei(e,t){return e.stderr.trim()||e.stdout.trim()||`${t} exited with code ${e.exitCode}`}function ea(e,t,r=Date.now()){let i=Math.max(0,r-t.startedAt);return i>=1e3?{message:e,tooShort:!1}:{message:`${e}. Recording stopped after ${Math.round(i)}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:i,targetLabel:a}=e,o=Q({recording:t,trimStartMs:i});if(!t.showTouches)return void p({level:"debug",phase:"record_stop_overlay_skipped",data:{reason:"hide_touches"}});if(0===t.gestureEvents.length)return void p({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 h("record_stop_overlay_export",()=>r.overlayRecordingTouches({videoPath:t.outPath,telemetryPath:o,exportQuality:t.exportQuality,targetLabel:a}),{targetLabel:a,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=o.parse(e),i=r.ext||".mp4";return o.join(r.dir,`${r.name}.part-${String(t).padStart(3,"0")}${i}`)}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:i}=e;if(t.stopping)return;let a=await i();if(a)throw Error(a);if(t.stopping)return;let n=es(t),s=n.length+1,c=await r(o.posix.dirname(t.remotePath));t.remotePath=c.remotePath,t.remotePid=c.remotePid,n.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"}):(Q({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:i,remotePath:o,outPath:n}=e;for(let e=0;e<2;e+=1){em(n);let s=w(i),c=await y(o,n,{allowFailure:!0,device:s});if(0!==c.exitCode)t=ei(c,"adb pull");else{await r.waitForStableFile(n,{pollMs:250,attempts:20});let t=await r.isPlayableVideo(n);if(p({level:"debug",phase:"record_stop_android_pull_validation",data:{deviceId:i,remotePath:o,outPath:n,attempt:e+1,fileSize:function(e){try{return a.statSync(e).size}catch{return 0}}(n),playable:t}}),t)return;p({level:"warn",phase:"record_stop_android_invalid_video_retry",data:{deviceId:i,remotePath:o,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{a.rmSync(e,{force:!0})}catch{}}let ef={medium:8e6,high:2e7};async function eh(e,t,r){return await g(w(e),t,r)}async function ep(e,t){let r=await eh(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 ew(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 eg(e,t){let r,i=0;for(let a=0;a<20;a+=1){let a=await eh(e,["shell","stat","-c","%s",t],{allowFailure:!0}),o=0===a.exitCode?a.stdout.trim():"";if(o.length>0&&o===r){if((i+=1)>=4)return}else i=0;r=o,await f(250)}}async function ey(e,t,r){for(let i=0;i<8;i+=1){let a=await eh(e,["shell","stat","-c","%s",t],{allowFailure:!0}),o=0===a.exitCode?Number(a.stdout.trim()):NaN;if(Number.isFinite(o)&&o>0)return!0;if(!await ep(e,r))break;if(i+1>=2)return!0;await f(250)}return!1}async function e_(e){let{deviceId:t,maxSize:r}=e;if(void 0===r)return;let i=await eh(t,["shell","wm","size"],{allowFailure:!0}),a=i.stdout.match(/Override size:\s*(\d+)x(\d+)/)??i.stdout.match(/Physical size:\s*(\d+)x(\d+)/);if(0!==i.exitCode||!a)throw Error(`failed to resolve Android screen size for recording max-size: ${ei(i,"adb shell wm size")}`);var o={width:Number(a[1]),height:Number(a[2]),maxSize:r};let n=Math.max(o.width,o.height);if(n<=o.maxSize)return{width:o.width,height:o.height};let s=o.maxSize/n;return{width:ev(o.width,s),height:ev(o.height,s)}}function ev(e,t){return Math.max(2,2*Math.round(e*t/2))}async function eP(e,t){await eh(e,["shell","rm","-f",t],{allowFailure:!0})}async function eS(e,t){let r=await eh(e,["shell","kill","-9",t],{allowFailure:!0});return p({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 ew(e,t)}async function eA(e){var t;let r,i,{device:a,recordingSize:o,quality:n,preferredRemoteDir:s}=e,c="failed to start recording: Android screenrecord did not begin producing frames";for(let e of(t=Date.now(),r=`agent-device-recording-${t}.mp4`,i=["/sdcard","/data/local/tmp"],(s&&i.includes(s)?[s,...i.filter(e=>e!==s)]:i).map(e=>`${e}/${r}`))){let t=await eh(a.id,["shell",function(e,t,r){let i=["screenrecord"];return t&&i.push("--size",`${t.width}x${t.height}`),i.push("--bit-rate",String(ef[r])),i.push(e),`${i.join(" ")} >/dev/null 2>&1 & echo $!`}(e,o,n)],{allowFailure:!0});if(0!==t.exitCode){c=`failed to start recording: ${ei(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){c="failed to start recording: adb did not return a valid Android screenrecord pid",await eP(a.id,e);continue}if(p({level:"debug",phase:"record_start_android_started",data:{deviceId:a.id,remotePath:e,remotePid:r}}),await ey(a.id,e,r))return{remotePath:e,remotePid:r,startedAt:Date.now()};c="failed to start recording: Android screenrecord did not begin producing frames",await eS(a.id,r),await eP(a.id,e)}return{error:S("COMMAND_FAILED",c)}}async function eM(e){let t,{device:r,recordingBase:i}=e;try{t=await e_({deviceId:r.id,maxSize:i.maxSize})}catch(e){return S("COMMAND_FAILED",e instanceof Error?e.message:String(e))}let a=i.exportQuality??"medium",o=await eA({device:r,recordingSize:t,quality:a});if("error"in o)return o.error;let n={platform:"android",remotePath:o.remotePath,remotePid:o.remotePid,chunks:[{index:1,path:i.outPath,remotePath:o.remotePath}],...i,startedAt:o.startedAt};return!function e(t){let{recording:r,startNextChunk:i,finishCurrentChunk:a}=t,o=setTimeout(()=>{r.rotationPromise=ec({recording:r,startNextChunk:i,finishCurrentChunk:a}).catch(e=>{r.rotationFailedReason=e instanceof Error?e.message:String(e)}).finally(()=>{r.rotationPromise=void 0,r.stopping||r.rotationFailedReason||e({recording:r,startNextChunk:i,finishCurrentChunk:a})})},17e4);o.unref?.(),r.rotationTimer=o}({recording:n,finishCurrentChunk:async()=>await eC({device:r,recording:n,waitForRemoteFileStability:!1}),startNextChunk:async e=>{let i=await eA({device:r,recordingSize:t,quality:a,preferredRemoteDir:e});if("error"in i)throw Error(i.error.ok?"failed to start next Android recording chunk":i.error.error.message);return i}}),n}async function eC(e){let{device:t,recording:r,waitForRemoteFileStability:i=!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 a=await eh(t.id,["shell","kill","-2",r.remotePid],{allowFailure:!0});if(p({level:"debug",phase:"record_stop_android_signal",data:{deviceId:t.id,remotePath:r.remotePath,remotePid:r.remotePid,exitCode:a.exitCode,stdout:a.stdout.trim(),stderr:a.stderr.trim()}}),0!==a.exitCode)return await eb(t.id,r.remotePid,a);let o=await ex(t.id,r.remotePid);if(o)return o;i&&await eg(t.id,r.remotePath)}async function eb(e,t,r){if(await ep(e,t)&&!await eS(e,t))return`failed to stop recording: ${ei(r,"adb shell kill")}`}async function ex(e,t){if(!await ew(e,t)&&!await eS(e,t))return`failed to stop recording: Android screenrecord pid ${t} did not exit`}async function eI(e){let t,{deps:r,device:i,recording:a,stopRequestedAt:o}=e;p({level:"debug",phase:"record_stop_android_enter",data:{deviceId:i.id,remotePath:a.remotePath,remotePid:a.remotePid}}),a.stopping=!0,a.rotationTimer&&(clearTimeout(a.rotationTimer),a.rotationTimer=void 0),await a.rotationPromise;let n=await eC({device:i,recording:a});if(a.rotationFailedReason&&!n&&(a.warning??=`Android recording chunk rotation failed: ${a.rotationFailedReason}`),!n){let e=await el({deps:r,deviceId:i.id,chunks:es(a)});if(e)return await s(),S("COMMAND_FAILED",eD(e,a,o));await ed({recording:a,deps:r})}if(await s(),n)return S("COMMAND_FAILED",eD(n,a,o));if(t)return S("COMMAND_FAILED",t);return null;async function s(){for(let e of es(a)){let r=await eh(i.id,["shell","rm","-f",e.remotePath],{allowFailure:!0});p({level:"debug",phase:"record_stop_android_cleanup",data:{deviceId:i.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: ${ei(r,"adb shell rm")}`)}}}function eD(e,t,r){return ea(e,t,r).message}function e$(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,iosXctestrunFile:e.flags?.iosXctestrunFile,iosXctestDerivedDataPath:e.flags?.iosXctestDerivedDataPath,iosXctestEnvDir:e.flags?.iosXctestEnvDir}}async function eN(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o}=e,n=e$(r);try{await o.runIosRunnerCommand(i,{command:"recordStop",appBundleId:n},eR(t,a,r))}catch(e){p({level:"warn",phase:"record_stop_runner_failed",data:{platform:i.platform,kind:i.kind,deviceId:i.id,session:r.name,error:er(e)}})}}async function ek(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o}=e,n=e$(r);if(n)try{let e=await o.runIosRunnerCommand(i,{command:"snapshot",appBundleId:n,interactiveOnly:!0,depth:1},eR(t,a,r)),s=Date.now();if("number"==typeof e.currentUptimeMs&&Number.isFinite(e.currentUptimeMs)&&e.currentUptimeMs>0)return p({level:"debug",phase:"record_start_gesture_clock_anchor",data:{source:"warm_snapshot"}}),{gestureClockOriginAtMs:s,gestureClockOriginUptimeMs:e.currentUptimeMs};return}catch(e){p({level:"warn",phase:"record_start_simulator_runner_warm_failed",data:{deviceId:i.id,session:r.name,appBundleId:n,error:er(e)}});return}}async function eE(e){let t,r,{req:i,activeSession:a,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}`,h=eR(i,s,a),w=async()=>c.runIosRunnerCommand(n,{command:"recordStart",outPath:m,fps:d,maxSize:l.maxSize,appBundleId:u},h);try{let e=await w();t="number"==typeof e.recorderStartUptimeMs?e.recorderStartUptimeMs:void 0,r="number"==typeof e.targetAppReadyUptimeMs?e.targetAppReadyUptimeMs:void 0}catch(i){var g,y;if(!er(i).toLowerCase().includes("recording already in progress"))return S("COMMAND_FAILED",`failed to start recording: ${er(i)}`);p({level:"warn",phase:"record_start_runner_desynced",data:{platform:n.platform,kind:n.kind,deviceId:n.id,session:a.name,error:er(i)}});let e=(g=n.id,y=a.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},h)}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 eF(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o,fpsFlag:n,recordingBase:s,appBundleId:c}=e;try{await o.runIosRunnerCommand(i,{command:"recordStart",outPath:s.outPath,fps:n,maxSize:s.maxSize,appBundleId:c},eR(t,a,r))}catch(e){return S("COMMAND_FAILED",`failed to start recording: ${er(e)}`)}return{platform:"macos-runner",...s}}async function eO(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o,recording:n}=e;await eN({req:t,activeSession:r,device:i,logPath:a,deps:o});let s={stdout:"",stderr:"",exitCode:1};for(let e of I)if(0===(s=await o.runCmd("xcrun",["devicectl","device","copy","from","--device",i.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 eL(e){let{req:t,activeSession:r,device:i,logPath:a,deps:o,recording:n}=e;return await eN({req:t,activeSession:r,device:i,logPath:a,deps:o}),await eo({recording:n,deps:o,targetLabel:"macOS recording"}),null}async function eT(e){let{deps:t,recording:r}=e;r.child.kill("SIGINT");let i=await ez(r.wait,5e3);return i||(await eU(t,r,"SIGINT"),(i=await ez(r.wait,2e3))||(r.child.kill("SIGTERM"),await eU(t,r,"SIGTERM"),i=await ez(r.wait,2e3)))?i:(r.child.kill("SIGKILL"),await eU(t,r,"SIGKILL"),await ez(r.wait,2e3))}async function ez(e,t){return await Promise.race([e,f(t).then(()=>null)])}async function eU(e,t,r){await eG(e,t,r)||await eK(e,t.outPath,r)}async function eK(e,t,r){let i,a=`simctl.*recordVideo.*${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}`;try{i=await e.runCmd("pgrep",["-f",a],{allowFailure:!0})}catch(e){p({level:"warn",phase:"record_stop_ios_simulator_pgrep_failed",data:{outPath:t,signal:r,error:er(e)}});return}let o=eH(eq(i.stdout)),n=ej(o,r);p({level:n>0?"warn":"debug",phase:"record_stop_ios_simulator_signal_recorders",data:{outPath:t,signal:r,matchedPidCount:o.length,signaled:n,pgrepExitCode:i.exitCode}})}async function eG(e,t,r){let i=t.recorderPid??t.child.pid;if("number"!=typeof i||!Number.isInteger(i)||i<=0)return p({level:"debug",phase:"record_stop_ios_simulator_owned_recorder_unavailable",data:{outPath:t.outPath,signal:r,reason:"missing_recorder_pid"}}),!1;let a=await eV(e,i,t.outPath,r),o=eH([i,...a.pids]),n=ej(o,r);return p({level:n>0?"warn":"debug",phase:"record_stop_ios_simulator_signal_owned_recorder",data:{outPath:t.outPath,signal:r,recorderPid:i,childPidCount:a.pids.length,matchedPidCount:o.length,signaled:n,pgrepExitCode:a.exitCode}}),n>0}async function eV(e,t,r,i){let a;try{a=await e.runCmd("pgrep",["-P",String(t)],{allowFailure:!0})}catch(e){return p({level:"warn",phase:"record_stop_ios_simulator_owned_pgrep_failed",data:{outPath:r,signal:i,parentPid:t,error:er(e)}}),{pids:[]}}return{pids:eq(a.stdout),exitCode:a.exitCode}}function eH(e){return Array.from(new Set(e)).filter(e=>Number.isInteger(e)&&e>0&&e!==process.pid)}function ej(e,t){let r=0;for(let i of e)try{process.kill(i,t),r+=1}catch{}return r}function eq(e){return e.split(/\s+/).map(e=>Number(e)).filter(e=>Number.isInteger(e)&&e>0)}async function eW(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(a.statSync(e).size>0)return Date.now()}catch{}if(t+1>=2)break;await f(250)}return Date.now()}async function eX(e){let t,r,{req:i,activeSession:a,device:o,logPath:n,deps:s,recordingBase:c,resolvedOut:d}=e,l=c.showTouches?await ek({req:i,activeSession:a,device:o,logPath:n,deps:s}):void 0,{child:u,wait:m}=s.startIosSimulatorRecording({device:o,outPath:d}),f=await eB(d);if(l)t=l.gestureClockOriginAtMs,r=l.gestureClockOriginUptimeMs;else if(c.showTouches)try{let e=Date.now(),c=await s.runIosRunnerCommand(o,{command:"uptime",appBundleId:e$(a)},eR(i,n,a)),d=Date.now();t=Math.round((e+d)/2),r="number"==typeof c.currentUptimeMs?c.currentUptimeMs:void 0}catch{}return{platform:"ios",child:u,wait:m,...c,recorderPid:u.pid,startedAt:f,gestureClockOriginAtMs:void 0===r?void 0:t,gestureClockOriginUptimeMs:r}}async function eQ(e){let t,r,{req:i,sessionName:n,sessionStore:s,activeSession:c,device:d,logPath:l,deps:u}=e;if(c.recording)return S("INVALID_ARGS","recording already in progress");let m=i.flags?.fps,f=i.flags?.quality,h=i.flags?.screenshotMaxSize;if(void 0!==m&&(!Number.isInteger(m)||m<1||m>120))return S("INVALID_ARGS","fps must be an integer between 1 and 120");if(void 0!==f&&void 0===$(f))return S("INVALID_ARGS",`quality must be one of: ${R.join(", ")} (legacy numeric values 5-10 are accepted)`);if(void 0!==h&&(!Number.isInteger(h)||h<1))return S("INVALID_ARGS","max-size must be a positive integer");if(!A("record",d))return S("UNSUPPORTED_OPERATION","record is not supported on this device");let p=i.positionals?.[1]??`./recording-${Date.now()}.mp4`,w=v.expandHome(p,i.meta?.cwd),g=(r=$(i.flags?.quality),{outPath:w,clientOutPath:i.meta?.clientArtifactPaths?.outPath,startedAt:Date.now(),maxSize:i.flags?.screenshotMaxSize,exportQuality:r??"medium",showTouches:i.flags?.hideTouches!==!0,gestureEvents:[]});if(a.mkdirSync(o.dirname(w),{recursive:!0}),a.rmSync(w,{force:!0}),"ios"===d.platform&&"device"===d.kind){let e=e$(c);if(!e)return S("INVALID_ARGS","record on physical iOS devices requires an active app session; run open <app> first");t=await eE({req:i,activeSession:c,sessionStore:s,device:d,logPath:l,deps:u,fpsFlag:m,recordingBase:g,appBundleId:e})}else if("macos"===d.platform){let e=e$(c);if(!e)return S("INVALID_ARGS","record on macOS requires an active app session; run open <app> first");t=await eF({req:i,activeSession:c,device:d,logPath:l,deps:u,fpsFlag:m,recordingBase:g,appBundleId:e})}else t="ios"===d.platform?await eX({req:i,activeSession:c,device:d,logPath:l,deps:u,recordingBase:g,resolvedOut:w}):await eM({device:d,recordingBase:g});if("ok"in t)return t;c.recording=t,s.set(n,c);let y=s.ensureSessionDir(n);return s.recordAction(c,{command:i.command,positionals:i.positionals??[],flags:i.flags??{},result:{action:"start",showTouches:t.showTouches}}),{ok:!0,data:{recording:"started",outPath:t.clientOutPath??p,sessionStateDir:y,showTouches:t.showTouches}}}async function eJ(e){let{deps:t,device:r,recording:i,stopRequestedAt:a}=e;if("android"===i.platform)return await eI({deps:t,device:r,recording:i,stopRequestedAt:a});await h("record_stop_tail_settle",()=>t.waitForRecordingTail(i),{platform:i.platform,gestureEventCount:i.gestureEvents.length});let o=await h("record_stop_ios_simulator_process",()=>eT({deps:t,recording:i}),{outPath:i.outPath});if(!o)return eZ("failed to stop recording: simctl recordVideo did not exit after 5000ms and forced cleanup",i,a);if(0!==o.exitCode)return eZ(`failed to stop recording: ${ei(o,"simctl recordVideo")}`,i,a);if(await h("record_stop_video_stable",()=>t.waitForStableFile(i.outPath,{pollMs:150,attempts:12}),{outPath:i.outPath}),!await h("record_stop_video_playable_check",()=>t.isPlayableVideo(i.outPath),{outPath:i.outPath}))return eZ(`failed to stop recording: ${i.outPath} was not finalized into a playable video`,i,a);if(void 0!==i.maxSize)try{await h("record_stop_resize",()=>t.resizeRecording({videoPath:i.outPath,maxSize:i.maxSize,exportQuality:i.exportQuality,targetLabel:"iOS recording"}),{outPath:i.outPath,maxSize:i.maxSize})}catch(e){i.overlayWarning=`failed to resize recording: ${er(e)}`}return await h("record_stop_finalize_overlay",()=>eo({recording:i,deps:t,targetLabel:"iOS recording"}),{outPath:i.outPath,showTouches:i.showTouches,gestureEventCount:i.gestureEvents.length}),null}function eZ(e,t,r){let i=ea(e,t,r);return function(e){try{a.rmSync(e,{force:!0})}catch{}}(t.outPath),S("COMMAND_FAILED",i.message)}async function eY(e){var t;let r,i,{req:a,activeSession:n,device:s,logPath:c,deps:d}=e;if(!n.recording)return S("INVALID_ARGS","no active recording");let l=n.recording,u=Date.now(),m=l.invalidatedReason;n.recording=void 0;let f="ios-device-runner"===l.platform?await eO({req:a,activeSession:n,device:s,logPath:c,deps:d,recording:l}):"macos-runner"===l.platform?await eL({req:a,activeSession:n,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,i=[{field:"outPath",path:t.outPath,localPath:t.clientOutPath,fileName:o.basename(t.clientOutPath??t.outPath)}],r&&r.length>1&&i.push(...r.slice(1).map(e=>({field:"chunkPath",path:e.path,localPath:e0(t,e.index),fileName:o.basename(e0(t,e.index)??e.path)}))),t.telemetryPath&&i.push({field:"telemetryPath",path:t.telemetryPath,localPath:function(e){if(e.clientOutPath)return B(e.clientOutPath)}(t),fileName:o.basename(t.telemetryPath)}),{ok:!0,data:{recording:"stopped",outPath:t.outPath,telemetryPath:t.telemetryPath,artifacts:i,showTouches:t.showTouches,warning:t.warning,overlayWarning:t.overlayWarning,chunks:r?.map(e=>({index:e.index,path:e0(t,e.index)??e.path}))}}}function e0(e,t){if("android"===e.platform&&e.clientOutPath)return en(e.clientOutPath,t)}function e1(e,t,r,i={}){r.recordOnlySession&&(i.writeLog&&e.writeSessionLog(r),e.delete(t))}async function e2(e){let{req:t,sessionName:r,sessionStore:i,logPath:a}=e,o={runCmd:async(e,t,r)=>"xcrun"===e?await _(t,r):await l(e,t,r),startIosSimulatorRecording:e=>D().startIosSimulatorRecording(e),runIosRunnerCommand:P,waitForRecordingTail:eW,waitForStableFile:G,isPlayableVideo:V,trimRecordingStart:Y,resizeRecording:et,overlayRecordingTouches:ee},n=i.get(r),s=n?.device??await x(t.flags??{});n||await M(s);let c=n??{name:C(t),sessionScope:b(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 eQ({req:t,sessionName:r,sessionStore:i,activeSession:c,device:s,logPath:a,deps:o});let u=await eY({req:t,activeSession:c,device:s,logPath:a,deps:o});return u.ok?(i.recordAction(c,{command:t.command,positionals:t.positionals??[],flags:t.flags??{},result:{action:"stop",outPath:u.data?.outPath,showTouches:u.data?.showTouches}}),e1(i,r,c,{writeLog:!0})):e1(i,r,c),u}export{e2 as handleRecordCommand};
|
|
25
|
+
`.trim();async function W(e,t={}){let r,a=t.pollMs??150,o=t.attempts??12,n=0;for(let t=0;t<o;t+=1){let t=0;try{t=i.statSync(e).size}catch{t=0}if(t>0&&t===r){if((n+=1)>=2)return}else n=0;r=t,await m(a)}}async function B(e){try{let t=await Q(),r=await l(t,[e],{allowFailure:!0,timeoutMs:1e4,env:T()});if(0===r.exitCode)return!0;if(X(r.stderr,r.stdout))return Z(e);return!1}catch(r){var t;if((t=r)instanceof d&&("TOOL_MISSING"===t.code||X(String(t.details?.stderr??""),String(t.details?.stdout??""))))return Z(e);throw r}}async function Q(){e??=U({source:q,cacheName:"video-validator",timeoutMs:3e4});try{return await e}catch(t){throw e=void 0,t}}async function J(e,t={}){let r=t.pollMs??150,a=t.attempts??12;for(let t=0;t<a;t+=1){if(await B(e))return;await m(r)}}function X(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 Z(e){try{let t=i.statSync(e);if(!t.isFile()||t.size<=0)return!1}catch{return!1}let t=function(e){try{let t=i.openSync(e,"r");try{let e=i.fstatSync(t).size,r=0,a=[];for(;r+8<=e&&a.length<16;){let e=Buffer.alloc(8);if(8>i.readSync(t,e,0,8,r))break;let o=e.readUInt32BE(0),n=e.toString("latin1",4,8);if(a.push(n),1===o){let e=Buffer.alloc(8);if(8>i.readSync(t,e,0,8,r+8))break;o=Number(e.readBigUInt64BE(0))}if(!Number.isFinite(o)||o<=0)break;r+=o}return a}finally{i.closeSync(t)}}catch{return[]}}(e);return t.includes("ftyp")&&t.includes("moov")}function Y(e){let t=o.parse(e);return o.join(t.dir,`${t.name}.gesture-telemetry.json`)}function ee(e){return[...e].sort((e,t)=>e.tMs-t.tMs)}function et(e){var t,r,a;let o,n,{recording:s,trimStartMs:c}=e,d=(o=Y((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?ee(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)}]})):ee(r))},i.writeFileSync(o,JSON.stringify(n,null,2)),o);return s.telemetryPath=d,d}function er(e){let t=o.dirname(c(import.meta.url)),r=[c(new URL(`./${e}`,import.meta.url)),o.resolve(t,`../../apple-runner/AgentDeviceRunner/RecordingScripts/${e}`),o.resolve(t,`../../../apple-runner/AgentDeviceRunner/RecordingScripts/${e}`),o.resolve(process.cwd(),`apple-runner/AgentDeviceRunner/RecordingScripts/${e}`)];for(let e of r)if(i.existsSync(e))return e;throw new d("COMMAND_FAILED",`Missing recording helper script: ${e}`,{hint:"Ensure apple-runner/AgentDeviceRunner/RecordingScripts is present in this checkout or bundled with the package.",scriptName:e,searchedPaths:r})}async function ea(e){var t;let r,a,{videoPath:n,scriptPath:s,scriptArgs:c,commandDescription:u}=e;await W(n),await J(n);let p=(t=n,r=o.parse(t),a=`${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,o.join(r.dir,`.${r.name}.agent-device-${a}${r.ext||".mp4"}`));try{let e=await L({sourcePath:s});await l(e,["--input",n,"--output",p,...c],{timeoutMs:12e4,env:T()}),await J(p),i.renameSync(p,n)}catch(t){let e=t instanceof d?t:new d("COMMAND_FAILED",String(t),void 0,t instanceof Error?t:void 0);throw new d("COMMAND_FAILED",u,{...e.details,videoPath:n,script:s},e)}finally{i.rmSync(p,{force:!0})}}async function ei(e){let{videoPath:t,trimStartMs:a}=e;a>0&&await ea({videoPath:t,scriptPath:r??=er("recording-trim.swift"),scriptArgs:["--trim-start-ms",String(a)],commandDescription:"Failed to trim the start of the iOS recording"})}async function eo(e){let{videoPath:r,telemetryPath:a,exportQuality:i="medium",targetLabel:o="recording"}=e;await ea({videoPath:r,scriptPath:t??=er("recording-overlay.swift"),scriptArgs:["--events",a,"--quality",i],commandDescription:`Failed to add touch overlays to the ${o}`})}async function en(e){let{videoPath:t,maxSize:r,exportQuality:i="medium",targetLabel:o="recording"}=e;await ea({videoPath:t,scriptPath:a??=er("recording-resize.swift"),scriptArgs:["--max-size",String(r),"--quality",i],commandDescription:`Failed to resize the ${o}`})}function es(e){return e instanceof Error?e.message:String(e)}function ec(e,t){return e.stderr.trim()||e.stdout.trim()||`${t} exited with code ${e.exitCode}`}function ed(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 el(e){let{recording:t,deps:r,trimStartMs:a,targetLabel:i}=e,o=et({recording:t,trimStartMs:a});if(!t.showTouches)return void f({level:"debug",phase:"record_stop_overlay_skipped",data:{reason:"hide_touches"}});if(0===t.gestureEvents.length)return void f({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 h("record_stop_overlay_export",()=>r.overlayRecordingTouches({videoPath:t.outPath,telemetryPath:o,exportQuality:t.exportQuality,targetLabel:i}),{targetLabel:i,gestureEventCount:t.gestureEvents.length})}catch(e){t.overlayWarning??=`failed to overlay recording touches: ${es(e)}`}}function eu(e,t){if(1===t)return e;let r=o.parse(e),a=r.ext||".mp4";return o.join(r.dir,`${r.name}.part-${String(t).padStart(3,"0")}${a}`)}function ep(e){return e.chunks??=[{index:1,path:e.outPath,remotePath:e.remotePath}],e.chunks}async function em(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 n=ep(t),s=n.length+1,c=await r(o.posix.dirname(t.remotePath));t.remotePath=c.remotePath,t.remotePid=c.remotePid,n.push({index:s,path:eu(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 eh(e){let{recording:t,deps:r}=e;ep(t).length<=1?await el({recording:t,deps:r,targetLabel:"Android recording"}):(et({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 ef(e){for(let t of e.chunks){let r=await ew({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 ew(e){let t,{deps:r,deviceId:a,remotePath:o,outPath:n}=e;for(let e=0;e<2;e+=1){eg(n);let s=w(a),c=await y(o,n,{allowFailure:!0,device:s});if(0!==c.exitCode)t=ec(c,"adb pull");else{await r.waitForStableFile(n,{pollMs:250,attempts:20});let t=await r.isPlayableVideo(n);if(f({level:"debug",phase:"record_stop_android_pull_validation",data:{deviceId:a,remotePath:o,outPath:n,attempt:e+1,fileSize:function(e){try{return i.statSync(e).size}catch{return 0}}(n),playable:t}}),t)return;f({level:"warn",phase:"record_stop_android_invalid_video_retry",data:{deviceId:a,remotePath:o,outPath:n,attempt:e+1}})}e<1&&await m(750)}return t?`failed to copy recording from device: ${t}`:(eg(n),"failed to copy recording from device: pulled file is not a playable MP4")}function eg(e){try{i.rmSync(e,{force:!0})}catch{}}let ey={medium:8e6,high:2e7};async function e_(e,t,r){return await g(w(e),t,r)}async function ev(e,t){let r=await e_(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 eP(e,t){for(let r=0;r<40;r+=1){if(!await ev(e,t))return!0;await m(250)}return!await ev(e,t)}async function eS(e,t){let r,a=0;for(let i=0;i<20;i+=1){let i=await e_(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 m(250)}}async function eA(e,t,r){for(let a=0;a<8;a+=1){let i=await e_(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 ev(e,r))break;if(a+1>=2)return!0;await m(250)}return!1}async function eb(e){let{deviceId:t,maxSize:r}=e;if(void 0===r)return;let a=await e_(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 max-size: ${ec(a,"adb shell wm size")}`);var o={width:Number(i[1]),height:Number(i[2]),maxSize:r};let n=Math.max(o.width,o.height);if(n<=o.maxSize)return{width:o.width,height:o.height};let s=o.maxSize/n;return{width:eM(o.width,s),height:eM(o.height,s)}}function eM(e,t){return Math.max(2,2*Math.round(e*t/2))}async function eC(e,t){await e_(e,["shell","rm","-f",t],{allowFailure:!0})}async function ex(e,t){let r=await e_(e,["shell","kill","-9",t],{allowFailure:!0});return f({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 ev(e,t))&&await eP(e,t)}async function eR(e){var t;let r,a,{device:i,recordingSize:o,quality:n,preferredRemoteDir:s}=e,c="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"],(s&&a.includes(s)?[s,...a.filter(e=>e!==s)]:a).map(e=>`${e}/${r}`))){let t=await e_(i.id,["shell",function(e,t,r){let a=["screenrecord"];return t&&a.push("--size",`${t.width}x${t.height}`),a.push("--bit-rate",String(ey[r])),a.push(e),`${a.join(" ")} >/dev/null 2>&1 & echo $!`}(e,o,n)],{allowFailure:!0});if(0!==t.exitCode){c=`failed to start recording: ${ec(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){c="failed to start recording: adb did not return a valid Android screenrecord pid",await eC(i.id,e);continue}if(f({level:"debug",phase:"record_start_android_started",data:{deviceId:i.id,remotePath:e,remotePid:r}}),await eA(i.id,e,r))return{remotePath:e,remotePid:r,startedAt:Date.now()};c="failed to start recording: Android screenrecord did not begin producing frames",await ex(i.id,r),await eC(i.id,e)}return{error:A("COMMAND_FAILED",c)}}async function eI(e){let t,{device:r,recordingBase:a}=e;try{t=await eb({deviceId:r.id,maxSize:a.maxSize})}catch(e){return A("COMMAND_FAILED",e instanceof Error?e.message:String(e))}let i=a.exportQuality??"medium",o=await eR({device:r,recordingSize:t,quality:i});if("error"in o)return o.error;let n={platform:"android",remotePath:o.remotePath,remotePid:o.remotePid,chunks:[{index:1,path:a.outPath,remotePath:o.remotePath}],...a,startedAt:o.startedAt};return!function e(t){let{recording:r,startNextChunk:a,finishCurrentChunk:i}=t,o=setTimeout(()=>{r.rotationPromise=em({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:n,finishCurrentChunk:async()=>await eD({device:r,recording:n,waitForRemoteFileStability:!1}),startNextChunk:async e=>{let a=await eR({device:r,recordingSize:t,quality:i,preferredRemoteDir:e});if("error"in a)throw Error(a.error.ok?"failed to start next Android recording chunk":a.error.error.message);return a}}),n}async function eD(e){let{device:t,recording:r,waitForRemoteFileStability:a=!0}=e;await ev(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 e_(t.id,["shell","kill","-2",r.remotePid],{allowFailure:!0});if(f({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 eN(t.id,r.remotePid,i);let o=await eE(t.id,r.remotePid);if(o)return o;a&&await eS(t.id,r.remotePath)}async function eN(e,t,r){if(await ev(e,t)&&!await ex(e,t))return`failed to stop recording: ${ec(r,"adb shell kill")}`}async function eE(e,t){if(!await eP(e,t)&&!await ex(e,t))return`failed to stop recording: Android screenrecord pid ${t} did not exit`}async function e$(e){let t,{deps:r,device:a,recording:i,stopRequestedAt:o}=e;f({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 eD({device:a,recording:i});if(i.rotationFailedReason&&!n&&(i.warning??=`Android recording chunk rotation failed: ${i.rotationFailedReason}`),!n){let e=await ef({deps:r,deviceId:a.id,chunks:ep(i)});if(e)return await s(),A("COMMAND_FAILED",eO(e,i,o));await eh({recording:i,deps:r})}if(await s(),n)return A("COMMAND_FAILED",eO(n,i,o));if(t)return A("COMMAND_FAILED",t);return null;async function s(){for(let e of ep(i)){let r=await e_(a.id,["shell","rm","-f",e.remotePath],{allowFailure:!0});f({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: ${ec(r,"adb shell rm")}`)}}}function eO(e,t,r){return ed(e,t,r).message}function ek(e){let t=e.appBundleId?.trim();return t&&t.length>0?t:void 0}function eF(e,t,r){return R({req:e,logPath:t,traceLogPath:r.trace?.outPath})}async function eT(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o}=e,n=ek(r);try{await o.runAppleRunnerCommand(a,{command:"recordStop",appBundleId:n},eF(t,i,r))}catch(e){f({level:"warn",phase:"record_stop_runner_failed",data:{platform:a.platform,kind:a.kind,deviceId:a.id,session:r.name,error:es(e)}})}}async function eL(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o}=e,n=ek(r);if(n)try{let e=await o.runAppleRunnerCommand(a,{command:"snapshot",appBundleId:n,interactiveOnly:!0,depth:1},eF(t,i,r)),s=Date.now();if("number"==typeof e.currentUptimeMs&&Number.isFinite(e.currentUptimeMs)&&e.currentUptimeMs>0)return f({level:"debug",phase:"record_start_gesture_clock_anchor",data:{source:"warm_snapshot"}}),{gestureClockOriginAtMs:s,gestureClockOriginUptimeMs:e.currentUptimeMs};return}catch(e){f({level:"warn",phase:"record_start_simulator_runner_warm_failed",data:{deviceId:a.id,session:r.name,appBundleId:n,error:es(e)}});return}}async function eU(e){let t,r,{req:a,activeSession:i,sessionStore:o,device:n,logPath:s,deps:c,fpsFlag:d,recordingBase:l,appBundleId:u}=e,p=`agent-device-recording-${Date.now()}.mp4`,m=`tmp/${p}`,h=eF(a,s,i),w=async()=>c.runAppleRunnerCommand(n,{command:"recordStart",outPath:p,fps:d,maxSize:l.maxSize,appBundleId:u},h);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(!es(a).toLowerCase().includes("recording already in progress"))return A("COMMAND_FAILED",`failed to start recording: ${es(a)}`);f({level:"warn",phase:"record_start_runner_desynced",data:{platform:n.platform,kind:n.kind,deviceId:n.id,session:i.name,error:es(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 A("COMMAND_FAILED",`failed to start recording: recording already in progress in session '${e.name}'`);try{await c.runAppleRunnerCommand(n,{command:"recordStop",appBundleId:u},h)}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 A("COMMAND_FAILED",`failed to start recording: ${es(e)}`)}}return{platform:"ios-device-runner",remotePath:m,runnerStartedAtUptimeMs:t,targetAppReadyUptimeMs:r,...l}}async function ez(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,fpsFlag:n,recordingBase:s,appBundleId:c}=e;try{await o.runAppleRunnerCommand(a,{command:"recordStart",outPath:s.outPath,fps:n,maxSize:s.maxSize,appBundleId:c},eF(t,i,r))}catch(e){return A("COMMAND_FAILED",`failed to start recording: ${es(e)}`)}return{platform:"macos-runner",...s}}async function eK(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,recording:n}=e;await eT({req:t,activeSession:r,device:a,logPath:i,deps:o});let s={stdout:"",stderr:"",exitCode:1};for(let e of P)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 A("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 el({recording:n,deps:o,trimStartMs:c,targetLabel:"iOS recording"}),null}async function eG(e){let{req:t,activeSession:r,device:a,logPath:i,deps:o,recording:n}=e;return await eT({req:t,activeSession:r,device:a,logPath:i,deps:o}),await el({recording:n,deps:o,targetLabel:"macOS recording"}),null}async function eV(e){let{deps:t,recording:r}=e;r.child.kill("SIGINT");let a=await eH(r.wait,5e3);return a||(await ej(t,r,"SIGINT"),(a=await eH(r.wait,2e3))||(r.child.kill("SIGTERM"),await ej(t,r,"SIGTERM"),a=await eH(r.wait,2e3)))?a:(r.child.kill("SIGKILL"),await ej(t,r,"SIGKILL"),await eH(r.wait,2e3))}async function eH(e,t){return await Promise.race([e,m(t).then(()=>null)])}async function ej(e,t,r){await eW(e,t,r)||await eq(e,t.outPath,r)}async function eq(e,t,r){let a,i=`simctl.*recordVideo.*${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}`;try{a=await e.runCmd("pgrep",["-f",i],{allowFailure:!0})}catch(e){f({level:"warn",phase:"record_stop_ios_simulator_pgrep_failed",data:{outPath:t,signal:r,error:es(e)}});return}let o=eQ(eX(a.stdout)),n=eJ(o,r);f({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 eW(e,t,r){let a=t.recorderPid??t.child.pid;if("number"!=typeof a||!Number.isInteger(a)||a<=0)return f({level:"debug",phase:"record_stop_ios_simulator_owned_recorder_unavailable",data:{outPath:t.outPath,signal:r,reason:"missing_recorder_pid"}}),!1;let i=await eB(e,a,t.outPath,r),o=eQ([a,...i.pids]),n=eJ(o,r);return f({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 eB(e,t,r,a){let i;try{i=await e.runCmd("pgrep",["-P",String(t)],{allowFailure:!0})}catch(e){return f({level:"warn",phase:"record_stop_ios_simulator_owned_pgrep_failed",data:{outPath:r,signal:a,parentPid:t,error:es(e)}}),{pids:[]}}return{pids:eX(i.stdout),exitCode:i.exitCode}}function eQ(e){return Array.from(new Set(e)).filter(e=>Number.isInteger(e)&&e>0&&e!==process.pid)}function eJ(e,t){let r=0;for(let a of e)try{process.kill(a,t),r+=1}catch{}return r}function eX(e){return e.split(/\s+/).map(e=>Number(e)).filter(e=>Number.isInteger(e)&&e>0)}async function eZ(e){let t,r,{req:a,activeSession:i,device:o,logPath:n,deps:s,recordingBase:c,resolvedOut:d}=e,l=c.showTouches?await eL({req:a,activeSession:i,device:o,logPath:n,deps:s}):void 0,{child:u,wait:p}=s.startIosSimulatorRecording({device:o,outPath:d}),m=await e0(d);if(l)t=l.gestureClockOriginAtMs,r=l.gestureClockOriginUptimeMs;else if(c.showTouches)try{let e=Date.now(),c=await s.runAppleRunnerCommand(o,{command:"uptime",appBundleId:ek(i)},eF(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:u,wait:p,...c,recorderPid:u.pid,startedAt:m,gestureClockOriginAtMs:void 0===r?void 0:t,gestureClockOriginUptimeMs:r}}async function eY(e){let{deps:t,recording:r,stopRequestedAt:a}=e;await h("record_stop_tail_settle",()=>t.waitForRecordingTail(r),{platform:r.platform,gestureEventCount:r.gestureEvents.length});let i=await h("record_stop_ios_simulator_process",()=>eV({deps:t,recording:r}),{outPath:r.outPath});if(!i)return e1("failed to stop recording: simctl recordVideo did not exit after 5000ms and forced cleanup",r,a);if(0!==i.exitCode)return e1(`failed to stop recording: ${ec(i,"simctl recordVideo")}`,r,a);if(await h("record_stop_video_stable",()=>t.waitForStableFile(r.outPath,{pollMs:150,attempts:12}),{outPath:r.outPath}),!await h("record_stop_video_playable_check",()=>t.isPlayableVideo(r.outPath),{outPath:r.outPath}))return e1(`failed to stop recording: ${r.outPath} was not finalized into a playable video`,r,a);if(void 0!==r.maxSize)try{await h("record_stop_resize",()=>t.resizeRecording({videoPath:r.outPath,maxSize:r.maxSize,exportQuality:r.exportQuality,targetLabel:"iOS recording"}),{outPath:r.outPath,maxSize:r.maxSize})}catch(e){r.overlayWarning=`failed to resize recording: ${es(e)}`}return await h("record_stop_finalize_overlay",()=>el({recording:r,deps:t,targetLabel:"iOS recording"}),{outPath:r.outPath,showTouches:r.showTouches,gestureEventCount:r.gestureEvents.length}),null}async function e0(e){for(let t=0;t<2;t+=1){try{if(i.statSync(e).size>0)return Date.now()}catch{}if(t+1>=2)break;await m(250)}return Date.now()}function e1(e,t,r){let a=ed(e,t,r);return function(e){try{i.rmSync(e,{force:!0})}catch{}}(t.outPath),A("COMMAND_FAILED",a.message)}function e2(e){return"web"===e.platform?e5:"android"===e.platform?e6:"macos"===e.platform?e3:"ios"===e.platform&&"device"===e.kind?e8:"ios"===e.platform?e7:e9}function e4({req:e}){return e.positionals?.[1]??D(void 0)}let e5={validateStart:e=>(function(e){let t,r=(t=[],e.flags?.fps!==void 0&&t.push("--fps"),e.flags?.quality!==void 0&&t.push("--quality"),e.flags?.screenshotMaxSize!==void 0&&t.push("--max-size"),e.flags?.hideTouches!==void 0&&t.push("--hide-touches"),t);return r.length>0?A("INVALID_ARGS",`web recordings do not support ${r.join(", ")}; agent-browser records WebM directly`):null})(e),resolveOutputPath:function({req:e}){let t=e.positionals?.[1];return void 0===t?D("web"):N(t,".webm")},start:async({activeSession:e,recordingBase:t,resolvedOut:r})=>{var a;let i=(a=r,".webm"!==o.extname(a).toLowerCase()?A("INVALID_ARGS","web recordings must use a .webm output path"):null);if(i)return i;if(e.recordOnlySession)return A("INVALID_ARGS","record on web requires an active browser session; run open <url> --platform web first");let n=E();return n.startRecording?(await n.startRecording(r),{...t,outPath:r,startedAt:Date.now(),platform:"web",showTouches:!1}):A("UNSUPPORTED_OPERATION","record is not supported by this web provider")},stop:async({recording:e})=>await te({recording:e}),cleanupRecordOnlySession:async()=>{try{await E().close()}catch{}}},e8={resolveOutputPath:e4,start:async({req:e,activeSession:t,sessionStore:r,device:a,logPath:i,deps:o,fpsFlag:n,recordingBase:s})=>{let c=ek(t);return c?await eU({req:e,activeSession:t,sessionStore:r,device:a,logPath:i,deps:o,fpsFlag:n,recordingBase:s,appBundleId:c}):A("INVALID_ARGS","record on physical iOS devices requires an active app session; run open <app> first")},stop:async({req:e,activeSession:t,device:r,logPath:a,deps:i,recording:o})=>await eK({req:e,activeSession:t,device:r,logPath:a,deps:i,recording:o})},e3={resolveOutputPath:e4,start:async({req:e,activeSession:t,device:r,logPath:a,deps:i,fpsFlag:o,recordingBase:n})=>{let s=ek(t);return s?await ez({req:e,activeSession:t,device:r,logPath:a,deps:i,fpsFlag:o,recordingBase:n,appBundleId:s}):A("INVALID_ARGS","record on macOS requires an active app session; run open <app> first")},stop:async({req:e,activeSession:t,device:r,logPath:a,deps:i,recording:o})=>await eG({req:e,activeSession:t,device:r,logPath:a,deps:i,recording:o})},e7={resolveOutputPath:e4,start:async({req:e,activeSession:t,device:r,logPath:a,deps:i,recordingBase:o,resolvedOut:n})=>await eZ({req:e,activeSession:t,device:r,logPath:a,deps:i,recordingBase:o,resolvedOut:n}),stop:async({deps:e,recording:t,stopRequestedAt:r})=>await eY({deps:e,recording:t,stopRequestedAt:r})},e6={resolveOutputPath:e4,start:async({device:e,recordingBase:t})=>await eI({device:e,recordingBase:t}),stop:async({deps:e,device:t,recording:r,stopRequestedAt:a})=>await e$({deps:e,device:t,recording:r,stopRequestedAt:a})},e9={resolveOutputPath:e4,start:async()=>A("UNSUPPORTED_OPERATION","record is not supported on this device"),stop:async()=>A("UNSUPPORTED_OPERATION","record is not supported on this device")};async function te(e){let{recording:t}=e,r=E();if(!r.stopRecording)return A("UNSUPPORTED_OPERATION","record is not supported by this web provider");if(await r.stopRecording(),!function(e){try{return i.statSync(e).size>0}catch{return!1}}(t.outPath)){var a=t.outPath;try{i.rmSync(a,{force:!0})}catch{}return A("COMMAND_FAILED",`failed to stop recording: ${t.outPath} was not finalized into a WebM video`)}return null}async function tt(e){"ios"!==e.platform||0!==e.gestureEvents.length&&await m(350)}async function tr(e){let t,{req:r,sessionName:a,sessionStore:n,activeSession:s,device:c,logPath:d,deps:l}=e;if(s.recording)return A("INVALID_ARGS","recording already in progress");let u=r.flags?.fps,p=r.flags?.quality,m=r.flags?.screenshotMaxSize,h=e2(c),f=h.validateStart?.(r)??null;if(f)return f;if(void 0!==u&&(!Number.isInteger(u)||u<1||u>120))return A("INVALID_ARGS","fps must be an integer between 1 and 120");if(void 0!==p&&void 0===O(p))return A("INVALID_ARGS",`quality must be one of: ${k.join(", ")} (legacy numeric values 5-10 are accepted)`);if(void 0!==m&&(!Number.isInteger(m)||m<1))return A("INVALID_ARGS","max-size must be a positive integer");let w=S("record",c);if(w)return w;let g=h.resolveOutputPath({req:r}),y=v.expandHome(g,r.meta?.cwd),_=(t=O(r.flags?.quality),{outPath:y,clientOutPath:r.meta?.clientArtifactPaths?.outPath,startedAt:Date.now(),maxSize:r.flags?.screenshotMaxSize,exportQuality:t??"medium",showTouches:r.flags?.hideTouches!==!0,gestureEvents:[]});i.mkdirSync(o.dirname(y),{recursive:!0}),i.rmSync(y,{force:!0});let P=await h.start({req:r,activeSession:s,sessionStore:n,device:c,logPath:d,deps:l,fpsFlag:u,recordingBase:_,resolvedOut:y});if("ok"in P)return P;s.recording=P,n.set(a,s);let b=n.ensureSessionDir(a);return F(n,s,r,r.command,{action:"start",showTouches:P.showTouches}),{ok:!0,data:{recording:"started",outPath:P.clientOutPath??g,sessionStateDir:b,showTouches:P.showTouches}}}async function ta(e){var t;let r,a,{req:i,activeSession:n,device:s,logPath:c,deps:d}=e;if(!n.recording)return A("INVALID_ARGS","no active recording");let l=n.recording,u=Date.now(),p=l.invalidatedReason;n.recording=void 0;let m=await function(e){let{recording:t}=e;switch(t.platform){case"android":return e6.stop({...e,recording:t});case"ios":return e7.stop({...e,recording:t});case"ios-device-runner":return e8.stop({...e,recording:t});case"macos-runner":return e3.stop({...e,recording:t});case"web":return e5.stop({...e,recording:t})}return t}({req:i,activeSession:n,device:s,logPath:c,deps:d,recording:l,stopRequestedAt:u});if(m)return m;if(p&&"ios"===l.platform&&l.showTouches)l.overlayWarning??=`overlay unavailable: ${p}`;else if(p)return A("COMMAND_FAILED",p);return r="android"===(t=l).platform?t.chunks:void 0,a=[{field:"outPath",path:t.outPath,localPath:t.clientOutPath,fileName:o.basename(t.clientOutPath??t.outPath)}],r&&r.length>1&&a.push(...r.slice(1).map(e=>({field:"chunkPath",path:e.path,localPath:ti(t,e.index),fileName:o.basename(ti(t,e.index)??e.path)}))),t.telemetryPath&&a.push({field:"telemetryPath",path:t.telemetryPath,localPath:function(e){if(e.clientOutPath)return Y(e.clientOutPath)}(t),fileName:o.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:ti(t,e.index)??e.path}))}}}function ti(e,t){if("android"===e.platform&&e.clientOutPath)return eu(e.clientOutPath,t)}async function to(e,t,r,a={}){if(!r.recordOnlySession)return;let i=e2(r.device);await i.cleanupRecordOnlySession?.(r),a.writeLog&&e.writeSessionLog(r),e.delete(t)}async function tn(e){let{req:t,sessionName:r,sessionStore:a,logPath:i}=e,o={runCmd:async(e,t,r)=>"xcrun"===e?await _(t,r):await l(e,t,r),startIosSimulatorRecording:e=>$().startIosSimulatorRecording(e),runAppleRunnerCommand:x,waitForRecordingTail:tt,waitForStableFile:W,isPlayableVideo:B,trimRecordingStart:ei,resizeRecording:en,overlayRecordingTouches:eo},n=a.get(r),s=n?.device??await C(t.flags??{});n||await I(s);let c=n??{name:M(t),sessionScope:b(t),device:s,createdAt:Date.now(),recordOnlySession:!0,actions:[]},d=(t.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(d))return A("INVALID_ARGS","record requires start|stop");if("start"===d)return tr({req:t,sessionName:r,sessionStore:a,activeSession:c,device:s,logPath:i,deps:o});let u=await ta({req:t,activeSession:c,device:s,logPath:i,deps:o});return u.ok?(F(a,c,t,t.command,{action:"stop",outPath:u.data?.outPath,showTouches:u.data?.showTouches}),await to(a,r,c,{writeLog:!0})):await to(a,r,c),u}export{tn as handleRecordCommand};
|
package/dist/src/record-trace.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"node:fs";import e from"node:path";import{SessionStore as r,errorResponse as a}from"./9722.js";import{handleRecordCommand as o}from"./record-trace-recording.js";async function i(i){let{req:
|
|
1
|
+
import t from"node:fs";import e from"node:path";import{SessionStore as r,errorResponse as a}from"./9722.js";import{handleRecordCommand as o}from"./record-trace-recording.js";import{recordSessionAction as n}from"./handler-utils.js";async function i(i){let{req:s,sessionName:c,sessionStore:d,logPath:p}=i,m=s.command;if("record"===m)return o({req:s,sessionName:c,sessionStore:d,logPath:p});if("trace"===m){let o=(s.positionals?.[0]??"").toLowerCase();if(!["start","stop"].includes(o))return a("INVALID_ARGS","trace requires start|stop");let i=d.get(c);if(!i)return a("SESSION_NOT_FOUND","No active session");if("start"===o){if(i.trace)return a("INVALID_ARGS","trace already in progress");let o=s.positionals?.[1]??d.defaultTracePath(i),c=r.expandHome(o);return t.mkdirSync(e.dirname(c),{recursive:!0}),t.appendFileSync(c,""),i.trace={outPath:c,startedAt:Date.now()},n(d,i,s,m,{action:"start",outPath:c}),{ok:!0,data:{trace:"started",outPath:c}}}if(!i.trace)return a("INVALID_ARGS","no active trace");let p=i.trace.outPath;if(s.positionals?.[1]){let a=r.expandHome(s.positionals[1]);t.mkdirSync(e.dirname(a),{recursive:!0}),t.existsSync(p)?t.renameSync(p,a):t.appendFileSync(a,""),p=a}return i.trace=void 0,n(d,i,s,m,{action:"stop",outPath:p}),{ok:!0,data:{trace:"stopped",outPath:p}}}return null}export{i as handleRecordTraceCommands};
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
declare type CloudProviderProfileFields = {
|
|
2
|
+
providerApp?: string;
|
|
3
|
+
providerOsVersion?: string;
|
|
4
|
+
providerProject?: string;
|
|
5
|
+
providerBuild?: string;
|
|
6
|
+
providerSessionName?: string;
|
|
7
|
+
awsProjectArn?: string;
|
|
8
|
+
awsDeviceArn?: string;
|
|
9
|
+
awsAppArn?: string;
|
|
10
|
+
awsRegion?: string;
|
|
11
|
+
awsInteractionMode?: 'INTERACTIVE' | 'NO_VIDEO' | 'VIDEO_ONLY';
|
|
12
|
+
};
|
|
13
|
+
|
|
1
14
|
declare const DAEMON_SERVER_MODES: readonly ['socket', 'http', 'dual'];
|
|
2
15
|
|
|
3
16
|
declare const DAEMON_TRANSPORT_PREFERENCES: readonly ['auto', 'socket', 'http'];
|
|
@@ -36,17 +49,7 @@ declare type RemoteConfigMetroOptions = {
|
|
|
36
49
|
metroNoInstallDeps?: boolean;
|
|
37
50
|
};
|
|
38
51
|
|
|
39
|
-
export declare type RemoteConfigProfile = RemoteConfigMetroOptions & {
|
|
40
|
-
stateDir?: string;
|
|
41
|
-
daemonBaseUrl?: string;
|
|
42
|
-
daemonAuthToken?: string;
|
|
43
|
-
daemonTransport?: DaemonTransportPreference;
|
|
44
|
-
daemonServerMode?: DaemonServerMode;
|
|
45
|
-
tenant?: string;
|
|
46
|
-
sessionIsolation?: SessionIsolationMode;
|
|
47
|
-
runId?: string;
|
|
48
|
-
leaseId?: string;
|
|
49
|
-
leaseBackend?: LeaseBackend;
|
|
52
|
+
export declare type RemoteConfigProfile = RemoteConfigMetroOptions & CloudProviderProfileFields & RemoteConnectionProfileFields & {
|
|
50
53
|
platform?: PlatformSelector;
|
|
51
54
|
target?: DeviceTarget;
|
|
52
55
|
device?: string;
|
|
@@ -57,21 +60,22 @@ export declare type RemoteConfigProfile = RemoteConfigMetroOptions & {
|
|
|
57
60
|
session?: string;
|
|
58
61
|
};
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
declare type RemoteConnectionProfileFields = {
|
|
64
|
+
stateDir?: string;
|
|
65
|
+
daemonBaseUrl?: string;
|
|
66
|
+
daemonAuthToken?: string;
|
|
67
|
+
daemonTransport?: DaemonTransportPreference;
|
|
68
|
+
daemonServerMode?: DaemonServerMode;
|
|
69
|
+
tenant?: string;
|
|
70
|
+
sessionIsolation?: SessionIsolationMode;
|
|
71
|
+
runId?: string;
|
|
72
|
+
leaseId?: string;
|
|
73
|
+
leaseBackend?: LeaseBackend;
|
|
74
|
+
leaseProvider?: string;
|
|
75
|
+
deviceKey?: string;
|
|
76
|
+
clientId?: string;
|
|
69
77
|
};
|
|
70
78
|
|
|
71
|
-
export declare function resolveRemoteConfigPath(options: RemoteConfigProfileOptions): string;
|
|
72
|
-
|
|
73
|
-
export declare function resolveRemoteConfigProfile(options: RemoteConfigProfileOptions): ResolvedRemoteConfigProfile;
|
|
74
|
-
|
|
75
79
|
declare const SESSION_ISOLATION_MODES: readonly ['none', 'tenant'];
|
|
76
80
|
|
|
77
81
|
declare type SessionIsolationMode = (typeof SESSION_ISOLATION_MODES)[number];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export{};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{toAppErrorCode as e,AppError as t}from"./485.js";let n=["tap","doubleTap","longPress","drag"],r=20;function i(e,n,r){return new t("INVALID_ARGS",r,{stepIndex:e,kind:"string"==typeof n?n:void 0})}function s(e,t,n,r,s,o){if(!Number.isFinite(e)||e<t||e>n)throw i(r,s,`sequence step ${r} (${s}) ${o} must be between ${t} and ${n}`)}function o(e,o){if(!Array.isArray(e)||0===e.length)throw new t("INVALID_ARGS","sequence requires at least one step",{stepCount:Array.isArray(e)?e.length:0});if(e.length>r)throw new t("INVALID_ARGS",`sequence accepts at most ${r} steps, received ${e.length}`,{stepCount:e.length,maxSteps:r});return e.forEach((e,t)=>(function(e,t){var r;if(!("string"==typeof(r=e.kind)&&n.includes(r)))throw i(t,e.kind,`sequence step ${t} has unsupported kind "${String(e.kind)}"; allowed: ${n.join(", ")}`);if(!Number.isFinite(e.x)||!Number.isFinite(e.y))throw i(t,e.kind,`sequence step ${t} (${e.kind}) requires finite x and y`);if("drag"===e.kind&&(!Number.isFinite(e.x2)||!Number.isFinite(e.y2)))throw i(t,e.kind,`sequence step ${t} (drag) requires finite x2 and y2`);void 0!==e.durationMs&&s(e.durationMs,0,1e4,t,e.kind,"durationMs"),void 0!==e.pauseMs&&s(e.pauseMs,0,1e4,t,e.kind,"pauseMs")})(e,t)),{command:"sequence",steps:e,appBundleId:o}}function d(n){var r,i,s,o;let d=Array.isArray(r=n.sequenceResults)?r.map(e=>{let t=e??{};return{ok:!0===t.ok,kind:"string"==typeof t.kind?t.kind:"unknown",errorCode:"string"==typeof t.errorCode?t.errorCode:void 0,errorMessage:"string"==typeof t.errorMessage?t.errorMessage:void 0,gestureStartUptimeMs:"number"==typeof t.gestureStartUptimeMs?t.gestureStartUptimeMs:void 0,gestureEndUptimeMs:"number"==typeof t.gestureEndUptimeMs?t.gestureEndUptimeMs:void 0}}):[],u="number"==typeof n.completedSteps&&Number.isFinite(n.completedSteps)?n.completedSteps:d.filter(e=>e.ok).length,p="number"==typeof n.failedStepIndex&&Number.isFinite(n.failedStepIndex)?n.failedStepIndex:d.findIndex(e=>!e.ok)>=0?d.findIndex(e=>!e.ok):void 0;if(void 0!==p){let n,r,a;throw i=d,s=u,n=i[o=p],r=n?.kind??"step",a=n?.errorMessage??`sequence step ${o} (${r}) failed`,new t(e(n?.errorCode),a,{failedStepIndex:o,failedStepKind:r,completedSteps:s,sequenceResults:i,hint:"Run snapshot -i to inspect the current UI, then continue from the observed state."})}return{results:d,completedSteps:u,failedStepIndex:p}}export{r as MAX_RUNNER_SEQUENCE_STEPS,o as buildRunnerSequenceCommand,d as parseRunnerSequenceResult};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{setSessionSnapshot as e,createDaemonRuntimeSessionStore as t,captureSnapshot as r,dispatchCommand as n,runIosRunnerCommand as s,errorResponse as o,isCommandSupportedOnDevice as i,ensureDeviceReady as a,stopIosRunnerSession as l,resolveRunnerAppBundleId as c,getActiveAndroidSnapshotFreshness as u,resolveTargetDevice as d,context_contextFromFlags as f}from"./9722.js";import{closeIosApp as p}from"./apps.js";import{emitDiagnostic as m}from"./432.js";import{asAppError as g,normalizeError as h,AppError as y}from"./733.js";import{evaluateIsPredicate as w,isSupportedPredicate as S,localCommandPolicy as v,createAgentDevice as _}from"./2577.js";import{getAndroidAppState as I}from"./3340.js";import{splitIsSelectorArgs as x,normalizeType as k,prefersValueForReadableText as P,findNodeByLabel as N,extractNodeReadText as A}from"./9836.js";import{resolveRectCenter as R,isSparseSnapshotQualityVerdict as b,parseWaitPositionals as q}from"./2948.js";import{buildSnapshotPresentationKey as D,snapshotPresentationOptionsFromFlags as E}from"./4291.js";import{isApplePlatform as C}from"./5910.js";import{parseFindArgs as M,tryParseSelectorChain as O}from"./587.js";async function $(e,t,r){let n=e.get(t),s=n?.device??await d(r??{});return n||await a(s),{session:n,device:s}}async function F(e,t,r){let n=!e&&"ios"===t.platform;try{return await r()}finally{n&&(await l(t.id),await L(t))}}async function L(e){let t=c();await p(e,t).catch(r=>{m({level:"debug",phase:"ios_sessionless_runner_host_close_failed",data:{deviceId:e.id,bundleId:t,error:r instanceof Error?r.message:String(r)}})})}function K(e,t,r,n){t&&e.recordAction(t,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:n})}function U(e){let{session:t,sessionName:r,device:n,snapshot:s,appBundleId:o}=e;return t?{...t,snapshot:s,lastComparisonSafeSnapshot:s?.comparisonSafe===!0?s:t.lastComparisonSafeSnapshot}:{name:r,device:n,createdAt:Date.now(),appBundleId:o,snapshot:s,...s?.comparisonSafe===!0?{lastComparisonSafeSnapshot:s}:{},actions:[]}}function T(e,t={}){return{artifacts:function(e,t={}){let r=!0===t.plural?"do":"does";return{resolveInput:async()=>{throw new y("UNSUPPORTED_OPERATION",`${e} ${r} not resolve input artifacts`)},reserveOutput:async()=>{throw new y("UNSUPPORTED_OPERATION",`${e} ${r} not reserve output artifacts`)},createTempFile:async()=>{throw new y("UNSUPPORTED_OPERATION",`${e} ${r} not create temporary files`)}}}(e,t),policy:v()}}let G=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"]];function V(e,t){let r=function(e){if(!e)return[];let t=[];for(let[r,n]of G)void 0!==e[r]&&t.push(n);return t}(t);return 0===r.length?null:o("INVALID_ARGS",`${e} @ref does not support ${r.join(", ")}.`)}async function B(e,t){let r=await X(e);if(r)throw new y("COMMAND_FAILED",`press ${t} left ${e.appBundleId} and foregrounded ${r.foregroundPackage}. The tap likely escaped the app.`,r)}async function X(e){var t;if("android"!==e.device.platform||!e.appBundleId)return null;let r=await I(e.device),n=r.package?.trim();return n&&n!==e.appBundleId&&("com.android.settings"===(t=n)||"com.android.systemui"===t||"com.google.android.permissioncontroller"===t||t.includes("launcher"))?{expectedPackage:e.appBundleId,foregroundPackage:n,activity:r.activity,hint:"com.google.android.permissioncontroller"===n?"Dismiss or allow the permission prompt, then retry the smoke assertion.":"Use screenshot as visual truth, then take a fresh snapshot -i before retrying."}:null}function H(e){return"COMMAND_FAILED"===e.code&&"string"==typeof e.details?.expectedPackage&&"string"==typeof e.details?.foregroundPackage}function j(e,t){let r=Array.isArray(e.selectorChain)?e.selectorChain:void 0,n=J(e),s=n?.kind==="ref"?Y(n.ref):void 0,o=n?.kind==="selector"?n.selector:void 0,i={...s?{ref:s}:{},...o?{selector:o}:{},...r?{selectorChain:r}:{}};if("attrs"===t)return i;let a="string"==typeof e.text?e.text:"";return{...i,text:a,refLabel:function(e){let t=e.trim();if(!(!t||t.length>80||/[\r\n]/.test(t)))return t}(a)}}function z(e){let t=J(e);return{...t?.kind==="ref"?{ref:Y(t.ref)}:{},...t?.kind==="selector"?{selector:t.selector}:{},..."string"==typeof e.text?{text:e.text}:{},...e.node&&"object"==typeof e.node?{node:e.node}:{}}}function Q(e){let{selectorChain:t,...r}=e;return r}function W(e,t,r,n){let s=e.get(t);s&&e.recordAction(s,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:n})}function J(e){let t=e.target;return t&&"object"==typeof t?"ref"===t.kind&&"string"==typeof t.ref?{kind:"ref",ref:t.ref}:"selector"===t.kind&&"string"==typeof t.selector?{kind:"selector",selector:t.selector}:void 0:void 0}function Y(e){return e.startsWith("@")?e.slice(1):e}let Z=["application","window","tabbar","scrollbar","image"],ee=new Set(["tab bar"]);async function et(e,t){var r;if(t.ok||(r=t.error.message,!/^wait timed out for (?:selector|text): /i.test(r)))return t;let n=await er(e).catch(()=>null);return n?o(t.error.code,`${t.error.message}. Current surface: ${n.summary}.`,{...t.error.details??{},currentSurface:n.details}):t}async function er(e){let t=[...(await r({device:e.device,session:e.session,flags:{...e.req.flags,snapshotInteractiveOnly:!0},logPath:e.logPath??""})).snapshot.nodes].sort(es),n=en(t,6,{includeIdentifiers:!0});if(0===n.length)return null;let s=en(t.filter(e=>!ea(e)),4,{includeIdentifiers:!1}),o=en(t.filter(el),4,{includeIdentifiers:!0});return{summary:(s.length>0?s:n.slice(0,4)).join(", "),details:{labels:n,...o.length>0?{buttons:o}:{}}}}function en(e,t,r){let n=new Set,s=[];for(let o of e){let e=ei(o,r);if(!(!e||n.has(e))&&(n.add(e),s.push(e),s.length>=t))break}return s}function es(e,t){var r,n;return eo(e)-eo(t)||(r=e,n=t,r.rect&&n.rect?r.rect.y-n.rect.y||r.rect.x-n.rect.x:r.rect?-1:n.rect?1:(r.depth??0)-(n.depth??0)||r.index-n.index)}function eo(e){let t=!!ei(e,{includeIdentifiers:!1});return 2*!!ea(e)+ +!t}function ei(e,t){let r=(t.includeIdentifiers?[e.label,e.value,e.identifier]:[e.label,e.value]).map(e=>"string"==typeof e?e.trim():"").find(e=>e.length>0);return r?r.replace(/\s+/g," ").slice(0,80):""}function ea(e){let t=k(`${e.type??""} ${e.role??""} ${e.subrole??""}`),r=`${e.label??""} ${e.value??""}`.trim().toLowerCase();return Z.some(e=>t.includes(e))||ee.has(r)||r.endsWith(".fill")}function el(e){return k(`${e.type??""} ${e.role??""} ${e.subrole??""}`).includes("button")}function ec(e){var t;let{session:r,selectorExpression:n}=e;if(!r||"ios"!==r.device.platform||r.postGestureStabilization)return null;let s=O(n);if(!s||1!==s.selectors.length)return null;let o=s.selectors[0];if(!o||1!==o.terms.length)return null;let i=o.terms[0];return i&&"string"==typeof i.value&&("id"===(t=i.key)||"label"===t||"text"===t||"value"===t)?{key:i.key,value:i.value,raw:o.raw}:null}function eu(e,t={}){let r=g(e);if("ELEMENT_NOT_FOUND"===r.code)return!0===t.allowElementNotFound;if("COMMAND_FAILED"!==r.code)return!1;let n=r.message.toLowerCase();return n.includes("fetch failed")||n.includes("timed out")||n.includes("timeout")||n.includes("runner did not accept connection")||n.includes("invalid runner response")}async function ed(e){let{device:t,node:r,flags:s,appBundleId:o,traceOutPath:i,surface:a,contextFromFlags:l}=e,c=A(r),u=R(r.rect);if(!u||"ios"===t.platform&&c&&!P(r.type??""))return c;try{let e=await n(t,"read",[String(u.x),String(u.y)],void 0,{...l(s,o,i),surface:a}),d=e&&"object"==typeof e?e:void 0,f="string"==typeof d?.text?d.text:"";if(f.trim())return f;return m({level:"warn",phase:"interaction_read_fallback",data:{reason:"empty_backend_text",nodeRef:r.ref,surface:a,platform:t.platform}}),c}catch(e){return m({level:"warn",phase:"interaction_read_fallback",data:{reason:"backend_read_failed",nodeRef:r.ref,surface:a,platform:t.platform,error:e instanceof Error?e.message:String(e)}}),c}}function ef(t){let r,n,{session:s,sessionStore:o,sessionName:i}=t,a=0;return{capture:async l=>{var c,d;let f=Date.now(),p=(c=l,d=t.req.flags?.out,JSON.stringify({presentationKey:D(E(ew(c))),includeRects:!0===c.includeRects,outPath:c.outPath??d??null})),m=function(e){let{timestamp:t,lastSnapshotAt:r,lastSnapshotResult:n,lastSnapshotCacheKey:s,session:o,request:i,cacheKey:a}=e;if(!(i.cache?.forceFresh===!0||!n||s!==a||t-r>=750||u(o))&&!ey(o,i))return n}({timestamp:f,lastSnapshotAt:a,lastSnapshotResult:r,lastSnapshotCacheKey:n,session:s,request:l,cacheKey:p});if(m)return m;let g=function(e){var t,r;let{session:n,timestamp:s,request:o}=e,i=n?.snapshot;if(i&&(t=n,r=o,!(r.cache?.forceFresh===!0||r.cache?.useSessionSnapshot!==!0||u(t)||ey(t,r)))&&s-i.createdAt<750&&i.presentationKey===function(e){return D(E(ew(e)))}(o))return i}({session:s,timestamp:f,request:l});if(g)return a=g.createdAt,r={snapshot:g},n=p,r;let h=await ep({params:t,request:l}),y={snapshot:h};return function(t){let{session:r,sessionStore:n,sessionName:s,snapshot:o}=t;!r||b(o.snapshotQuality)||(e(r,o),n.set(s,r))}({session:s,sessionStore:o,sessionName:i,snapshot:h}),a=f,r=y,n=p,y}}}async function ep(e){var t;let{params:r,request:n}=e,s=await eh(r,n,n.snapshotScope);return n.recovery?.legacyIosSparse&&!(t=s).snapshotQuality&&"xctest"===t.backend&&1===t.nodes.length&&t.nodes[0]?.type==="Application"?await em({runtimeParams:r,request:n,policy:n.recovery.legacyIosSparse}):n.recovery?.sparseVerdictQueryScope?.shouldScope&&b(s.snapshotQuality)?await eg({runtimeParams:r,request:n,policy:n.recovery.sparseVerdictQueryScope,snapshot:s}):s}async function em(e){let{runtimeParams:t,request:r,policy:n}=e;try{return await eh(t,r,n.scope,!1)}catch(e){if(!n.shouldScope)throw e;return await eh(t,r,n.query,!1)}}async function eg(e){let{runtimeParams:t,request:r,policy:n,snapshot:s}=e;try{return await eh(t,r,n.query,!1)}catch{return s}}async function eh(e,t,n,s=t.flags?.snapshotInteractiveOnly){return(await r({device:e.device,session:e.session,flags:{...t.flags,snapshotInteractiveOnly:s},outPath:t.outPath??e.req.flags?.out,logPath:e.logPath??"",snapshotScope:n,includeRects:t.includeRects})).snapshot}function ey(e,t){return t.cache?.bypassForPostGestureStabilization===!0&&!!e?.postGestureStabilization}function ew(e){return void 0===e.snapshotScope?e.flags:{...e.flags,snapshotScope:e.snapshotScope}}function eS(r){return _({backend:function(e){let{req:t,session:r,device:n,logPath:s,sessionName:o,sessionStore:i}=e,a=ef({device:n,session:r,sessionStore:i,sessionName:o,req:t,logPath:s});return{platform:n.platform,captureSnapshot:async(e,r)=>{var s;let o,i={...t.flags,...(s=r,o={},s?.interactiveOnly!==void 0&&(o.snapshotInteractiveOnly=s.interactiveOnly),s?.scope!==void 0&&(o.snapshotScope=s.scope),s?.depth!==void 0&&(o.snapshotDepth=s.depth),s?.raw!==void 0&&(o.snapshotRaw=s.raw),o)},l=r?.includeRects===!0,c=r?.scope??t.flags?.snapshotScope,u="wait"===t.command||"find"===t.command||l&&"web"===n.platform;return await a.capture({flags:i,snapshotScope:c,includeRects:l,cache:{forceFresh:u,useSessionSnapshot:!0,bypassForPostGestureStabilization:!0}})},readText:async(o,i)=>({text:await ed({device:n,node:i,flags:t.flags,appBundleId:r?.appBundleId,traceOutPath:r?.trace?.outPath,surface:r?.surface,contextFromFlags:e.contextFromFlags??((e,t,r)=>f(s??"",e,t,r))})}),findText:async(t,r)=>({found:await e_(e,r)})}}(r),...T("selector commands",{plural:!0}),sessions:t({sessionName:r.sessionName,getSession:()=>r.session,recordOptions:{includeSnapshot:!0},setRecord:t=>{r.session&&t.snapshot&&(e(r.session,t.snapshot),r.sessionStore.set(r.sessionName,r.session))}})})}async function ev(e,t){let r=e.sessionStore.get(e.sessionName);if(!r&&t.requireSession)return{ok:!1,response:o("SESSION_NOT_FOUND","No active session. Run open first.")};let n=r?.device??await d(e.req.flags??{});return(r||await a(n),i(t.capability,n))?{ok:!0,runtime:eS({...e,session:r,device:n})}:{ok:!1,response:o("UNSUPPORTED_OPERATION",`${t.capability} is not supported on this device`)}}async function e_(e,t){let r=await eI(e,t);if(null!==r)return r;let n=await ex(e,t);return null!==n?n:await ek(e,t)}async function eI(e,t){return"macos"===e.device.platform&&e.session?.surface&&"app"!==e.session.surface?await ek(e,t):null}async function ex(e,t){var r,n,o;let i,a,l=C((r=e).device.platform)&&r.session?.appBundleId?{device:r.device,appBundleId:r.session.appBundleId,traceLogPath:r.session.trace?.outPath}:null;if(!l)return null;let c=await s(l.device,{command:"findText",text:t,appBundleId:l.appBundleId},(n=e,o=l,i=n.req.flags??{},a=n.req.meta??{},{verbose:i.verbose,logPath:n.logPath,traceLogPath:o.traceLogPath,requestId:a.requestId,iosXctestrunFile:i.iosXctestrunFile,iosXctestDerivedDataPath:i.iosXctestDerivedDataPath,iosXctestEnvDir:i.iosXctestEnvDir}));return c?.found===!0}async function ek(e,t){return!!N((await eP(e)).nodes,t)}async function eP(e){let t=ef({device:e.device,session:e.session,sessionStore:e.sessionStore,sessionName:e.sessionName,req:e.req,logPath:e.logPath}),{snapshot:r}=await t.capture({flags:{...e.req.flags,snapshotInteractiveOnly:!1},cache:{forceFresh:!0,bypassForPostGestureStabilization:!0}});return r}async function eN(e){var t;let{req:r}=e;if("find"!==r.command)return null;let n=r.positionals??[];if(0===n.length)return o("INVALID_ARGS","find requires a locator or text");let s=M(n);if(!s.query)return o("INVALID_ARGS","find requires a value");if(r.flags?.findFirst&&r.flags?.findLast)return o("INVALID_ARGS","find accepts only one of --first or --last");let i=s.action;if("exists"!==(t=i)&&"wait"!==t&&"get_text"!==t&&"get_attrs"!==t)return null;let a=await ev(e,{requireSession:!1,capability:"find"});return a.ok?await eF(async()=>{let t=await a.runtime.selectors.find({session:e.sessionName,requestId:r.meta?.requestId,locator:s.locator,query:s.query,action:i,timeoutMs:s.timeoutMs});return W(e.sessionStore,e.sessionName,r,function(e,t){if("exists"===t)return{found:!0};if("wait"===t)return{found:!0,waitedMs:e.waitedMs};let r="string"==typeof e.ref?e.ref:void 0;return"get_attrs"===t?{ref:r,action:"get attrs"}:{ref:r,action:"get text",text:"string"==typeof e.text?e.text:""}}(t,i)),"found"===t.kind?{found:!0,..."number"==typeof t.waitedMs?{waitedMs:t.waitedMs}:{}}:{..."string"==typeof t.ref?{ref:t.ref}:{},..."string"==typeof t.text?{text:t.text}:{},...t.node&&"object"==typeof t.node?{node:t.node}:{}}}):a.response}async function eA(e){let{req:t}=e;if("get"!==t.command)return null;let r=t.positionals?.[0];if("text"!==r&&"attrs"!==r)return o("INVALID_ARGS","get only supports text or attrs");let n=function(e){let t=e.positionals?.[1]??"";if(t.startsWith("@"))return{ok:!0,target:{kind:"ref",ref:t,fallbackLabel:e.positionals.length>2?e.positionals.slice(2).join(" ").trim():""}};let r=e.positionals?.slice(1).join(" ").trim()??"";return r?{ok:!0,target:{kind:"selector",selector:r}}:{ok:!1,response:o("INVALID_ARGS","get requires @ref or selector expression")}}(t);if(!n.ok)return n.response;if("ref"===n.target.kind){let e=V("get",t.flags);if(e)return e}if("selector"===n.target.kind){let t=await eq(e,r,n.target.selector);if(t)return t}let s=await ev(e,{requireSession:!0,capability:"get"});return s.ok?await eF(async()=>{let o=await s.runtime.selectors.get({session:e.sessionName,requestId:t.meta?.requestId,property:r,target:n.target});return W(e.sessionStore,e.sessionName,t,j(o,r)),z(o)}):s.response}async function eR(e){let{req:t}=e;if("is"!==t.command)return null;let r=(t.positionals?.[0]??"").toLowerCase();if(!S(r))return o("INVALID_ARGS","is requires predicate: visible|hidden|exists|editable|selected|text");let{split:n}=x(t.positionals??[]);if(!n)return o("INVALID_ARGS","is requires a selector expression");let s=n.rest.join(" ").trim();if("text"===r&&!s)return o("INVALID_ARGS","is text requires expected text value");if("text"!==r&&n.rest.length>0)return o("INVALID_ARGS",`is ${r} does not accept trailing values`);let i=await eD(e,r,n.selectorExpression,s);if(i)return i;let a=await ev(e,{requireSession:!0,capability:"is"});if(!a.ok)return a.response;let l=await eF(async()=>{let o=await a.runtime.selectors.is({session:e.sessionName,requestId:t.meta?.requestId,predicate:r,selector:n.selectorExpression,expectedText:s});return W(e.sessionStore,e.sessionName,t,o),Q(o)});return await eL(e,l,`is ${r}`)}async function eb(e){let{req:t,sessionName:r,sessionStore:n}=e,s=q(t.positionals??[]);if(!s)return o("INVALID_ARGS","wait requires a duration or text");let{session:a,device:l}=await $(n,r,t.flags);if("sleep"!==s.kind&&!i("wait",l))return o("UNSUPPORTED_OPERATION","wait is not supported on this device");if("selector"===s.kind){let t=await eE({...e,session:a,device:l,selectorExpression:s.selectorExpression,timeoutMs:s.timeoutMs});if(t)return t}let c=async()=>{let o=eS({...e,session:a,device:l}),i=await eF(async()=>{let e=await o.selectors.wait({session:r,requestId:t.meta?.requestId,target:function(e,t){if("sleep"===e.kind)return{kind:"sleep",durationMs:e.durationMs};if("selector"===e.kind)return{kind:"selector",selector:e.selectorExpression,timeoutMs:e.timeoutMs};if("ref"===e.kind){if(!t?.snapshot)throw new y("INVALID_ARGS","Ref wait requires an existing snapshot in session.");return{kind:"ref",ref:e.rawRef,timeoutMs:e.timeoutMs}}if(!e.text)throw new y("INVALID_ARGS","wait requires text");return{kind:"text",text:e.text,timeoutMs:e.timeoutMs}}(s,a)});return W(n,r,t,e),{waitedMs:e.waitedMs,..."string"==typeof e.text?{text:e.text}:{},..."string"==typeof e.selector?{selector:e.selector}:{}}}),c=await et({req:t,logPath:e.logPath,session:a,device:l},i);return await eL(e,c,"wait")};return"sleep"===s.kind?await c():await F(a,l,c)}async function eq(e,t,r){let n=e.sessionStore.get(e.sessionName),s=ec({session:n,selectorExpression:r});if(!n||!s||"text"===t&&"id"!==s.key)return null;let o=await eO(e,n,s);if(e$(o))return o.response;if(!o)return null;let i=function(e,t,r){if(!r.found||!r.node)return null;let n={target:{kind:"selector",selector:t},node:r.node,selectorChain:[t]};return"attrs"===e?{kind:"attrs",...n}:"string"!=typeof r.text?null:{kind:"text",...n,text:r.text}}(t,s.raw,o);return i?(W(e.sessionStore,e.sessionName,e.req,j(i,t)),{ok:!0,data:z(i)}):null}async function eD(e,t,r,n){var s,o,i,a,l;let c;if("hidden"===t)return null;let u=await eC(e,r);if(e$(u))return u.response;if(!u?.result.found||!u.result.node)return null;let d="exists"===t?{predicate:t,pass:!0,selector:u.selector.raw,matches:1,selectorChain:[u.selector.raw]}:(s=t,o=n,i=u.selector.raw,a=u.session,c=w({predicate:s,node:l=u.result.node,nodes:[l],expectedText:o,platform:a.device.platform}),{predicate:s,pass:c.pass,selector:i,..."text"===s?{text:c.actualText}:{},selectorChain:[i]});return d?(W(e.sessionStore,e.sessionName,e.req,d),{ok:!0,data:Q(d)}):null}async function eE(e){let t=ec({session:e.session,selectorExpression:e.selectorExpression});if(!e.session||!t)return null;let r=Date.now(),n=await eO(e,e.session,t);if(e$(n))return n.response;if(!n?.found)return null;let s={kind:"selector",selector:t.raw,waitedMs:Date.now()-r,selectorChain:[t.raw]};return W(e.sessionStore,e.sessionName,e.req,s),await et({req:e.req,logPath:e.logPath,session:e.session,device:e.device},{ok:!0,data:s})}async function eC(e,t){let r=e.sessionStore.get(e.sessionName),n=ec({session:r,selectorExpression:t});if(!r||!n)return null;let s=await eO(e,r,n);return e$(s)?s:s?{session:r,selector:n,result:s}:null}async function eM(e,t,r){let n=await s(t.device,{command:"querySelector",selectorKey:r.key,selectorValue:r.value,appBundleId:t.appBundleId},{verbose:!!e.req.flags?.verbose,logPath:e.logPath,traceLogPath:t.trace?.outPath,requestId:e.req.meta?.requestId,iosXctestrunFile:e.req.flags?.iosXctestrunFile,iosXctestDerivedDataPath:e.req.flags?.iosXctestDerivedDataPath,iosXctestEnvDir:e.req.flags?.iosXctestEnvDir}),o=!0===n.found,i=function(e){let t=e.nodes;if(!Array.isArray(t))return;let r=t[0];if(r&&"object"==typeof r)return r}(n);return{found:o,..."string"==typeof n.text?{text:n.text}:{},...i?{node:i}:{}}}async function eO(e,t,r){try{return await eM(e,t,r)}catch(e){if(eu(e,{allowElementNotFound:!0}))return null;return{kind:"error",response:{ok:!1,error:h(e)}}}}function e$(e){return null!==e&&"kind"in e&&"error"===e.kind}async function eF(e){try{return{ok:!0,data:await e()}}catch(t){let e=g(t);return o(e.code,e.message,e.details)}}async function eL(e,t,r){var n;let s;if(t.ok)return t;let i=e.sessionStore.get(e.sessionName);if(!i)return t;try{s=await X(i)}catch{return t}return s?o(t.error.code,`${r} failed because ${"com.google.android.permissioncontroller"===(n=s).foregroundPackage?`Android permission dialog is blocking ${n.expectedPackage}`:`${n.foregroundPackage} is foreground instead of ${n.expectedPackage}`}.`,{...t.error.details??{},...s,blockedBy:"android_foreground_surface",originalMessage:t.error.message}):t}export{B as assertAndroidPressStayedInApp,U as buildSnapshotSession,T as createDaemonRuntimePolicy,ef as createSelectorCaptureRuntime,eN as dispatchFindReadOnlyViaRuntime,eA as dispatchGetViaRuntime,eR as dispatchIsViaRuntime,eb as dispatchWaitViaRuntime,H as isAndroidEscapeError,eu as isDirectIosSelectorFallbackError,ec as readSimpleIosSelectorTarget,ed as readTextForNode,K as recordIfSession,V as refSnapshotFlagGuardResponse,$ as resolveSessionDevice,F as withSessionlessRunnerCleanup};
|
|
1
|
+
import{setSessionSnapshot as e,createDaemonRuntimeSessionStore as t,context_contextFromFlags as r,captureSnapshot as n,dispatchCommand as s,requireCommandSupported as o,stopIosRunnerSession as i,errorResponse as a,resolveRunnerAppBundleId as l,getActiveAndroidSnapshotFreshness as c,noActiveSessionError as u,resolveTargetDevice as f,runAppleRunnerCommand as d}from"./9722.js";import{closeIosApp as p}from"./apps.js";import{emitDiagnostic as m}from"./432.js";import{ensureDeviceReady as g,buildAppleRunnerRequestOptions as h}from"./8414.js";import{asAppError as y,normalizeError as w,AppError as S}from"./485.js";import{evaluateIsPredicate as _,isSupportedPredicate as v,localCommandPolicy as x,createAgentDevice as k}from"./7871.js";import{getAndroidAppState as I}from"./3340.js";import{splitIsSelectorArgs as A,normalizeType as P,prefersValueForReadableText as N,findNodeByLabel as R,extractNodeReadText as b}from"./893.js";import{resolveRectCenter as q,isSparseSnapshotQualityVerdict as C,parseWaitPositionals as M}from"./2948.js";import{buildSnapshotPresentationKey as E,snapshotPresentationOptionsFromFlags as D,isApplePlatform as $}from"./6849.js";import{parseFindArgs as L,tryParseSelectorChain as O}from"./587.js";async function K(e,t,r){let n=e.get(t),s=n?.device??await f(r??{});return n||await g(s),{session:n,device:s}}async function F(e,t,r){let n=!e&&"ios"===t.platform;try{return await r()}finally{n&&(await i(t.id),await G(t))}}async function G(e){let t=l();await p(e,t).catch(r=>{m({level:"debug",phase:"ios_sessionless_runner_host_close_failed",data:{deviceId:e.id,bundleId:t,error:r instanceof Error?r.message:String(r)}})})}function V(e,t,r,n){t&&e.recordAction(t,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:n})}function U(e){let{session:t,sessionName:r,device:n,snapshot:s,appBundleId:o}=e;return t?{...t,snapshot:s,lastComparisonSafeSnapshot:s?.comparisonSafe===!0?s:t.lastComparisonSafeSnapshot}:{name:r,device:n,createdAt:Date.now(),appBundleId:o,snapshot:s,...s?.comparisonSafe===!0?{lastComparisonSafeSnapshot:s}:{},actions:[]}}function T(e,t={}){return{artifacts:function(e,t={}){let r=!0===t.plural?"do":"does";return{resolveInput:async()=>{throw new S("UNSUPPORTED_OPERATION",`${e} ${r} not resolve input artifacts`)},reserveOutput:async()=>{throw new S("UNSUPPORTED_OPERATION",`${e} ${r} not reserve output artifacts`)},createTempFile:async()=>{throw new S("UNSUPPORTED_OPERATION",`${e} ${r} not create temporary files`)}}}(e,t),policy:x()}}let B=[["snapshotDepth","--depth"],["snapshotScope","--scope"],["snapshotRaw","--raw"]];function H(e,t){let r=function(e){if(!e)return[];let t=[];for(let[r,n]of B)void 0!==e[r]&&t.push(n);return t}(t);return 0===r.length?null:a("INVALID_ARGS",`${e} @ref does not support ${r.join(", ")}.`)}async function j(e,t){let r=await z(e);if(r)throw new S("COMMAND_FAILED",`press ${t} left ${e.appBundleId} and foregrounded ${r.foregroundPackage}. The tap likely escaped the app.`,r)}async function z(e){var t;if("android"!==e.device.platform||!e.appBundleId)return null;let r=await I(e.device),n=r.package?.trim();return n&&n!==e.appBundleId&&("com.android.settings"===(t=n)||"com.android.systemui"===t||"com.google.android.permissioncontroller"===t||t.includes("launcher"))?{expectedPackage:e.appBundleId,foregroundPackage:n,activity:r.activity,hint:"com.google.android.permissioncontroller"===n?"Dismiss or allow the permission prompt, then retry the smoke assertion.":"Use screenshot as visual truth, then take a fresh snapshot -i before retrying."}:null}function Q(e){return"COMMAND_FAILED"===e.code&&"string"==typeof e.details?.expectedPackage&&"string"==typeof e.details?.foregroundPackage}function W(e,t){let r=Array.isArray(e.selectorChain)?e.selectorChain:void 0,n=Z(e),s=n?.kind==="ref"?ee(n.ref):void 0,o=n?.kind==="selector"?n.selector:void 0,i={...s?{ref:s}:{},...o?{selector:o}:{},...r?{selectorChain:r}:{}};if("attrs"===t)return i;let a="string"==typeof e.text?e.text:"";return{...i,text:a,refLabel:function(e){let t=e.trim();if(!(!t||t.length>80||/[\r\n]/.test(t)))return t}(a)}}function J(e){let t=Z(e);return{...t?.kind==="ref"?{ref:ee(t.ref)}:{},...t?.kind==="selector"?{selector:t.selector}:{},..."string"==typeof e.text?{text:e.text}:{},...e.node&&"object"==typeof e.node?{node:e.node}:{}}}function X(e){let{selectorChain:t,...r}=e;return r}function Y(e,t,r,n){let s=e.get(t);s&&e.recordAction(s,{command:r.command,positionals:r.positionals??[],flags:r.flags??{},result:n})}function Z(e){let t=e.target;return t&&"object"==typeof t?"ref"===t.kind&&"string"==typeof t.ref?{kind:"ref",ref:t.ref}:"selector"===t.kind&&"string"==typeof t.selector?{kind:"selector",selector:t.selector}:void 0:void 0}function ee(e){return e.startsWith("@")?e.slice(1):e}let et=["application","window","tabbar","scrollbar","image"],er=new Set(["tab bar"]);async function en(e,t){var r;if(t.ok||(r=t.error.message,!/^wait timed out for (?:selector|text): /i.test(r)))return t;let n=await es(e).catch(()=>null);return n?a(t.error.code,`${t.error.message}. Current surface: ${n.summary}.`,{...t.error.details??{},currentSurface:n.details}):t}async function es(e){let t=[...(await n({device:e.device,session:e.session,flags:{...e.req.flags,snapshotInteractiveOnly:!0},logPath:e.logPath??""})).snapshot.nodes].sort(ei),r=eo(t,6,{includeIdentifiers:!0});if(0===r.length)return null;let s=eo(t.filter(e=>!ec(e)),4,{includeIdentifiers:!1}),o=eo(t.filter(eu),4,{includeIdentifiers:!0});return{summary:(s.length>0?s:r.slice(0,4)).join(", "),details:{labels:r,...o.length>0?{buttons:o}:{}}}}function eo(e,t,r){let n=new Set,s=[];for(let o of e){let e=el(o,r);if(!(!e||n.has(e))&&(n.add(e),s.push(e),s.length>=t))break}return s}function ei(e,t){var r,n;return ea(e)-ea(t)||(r=e,n=t,r.rect&&n.rect?r.rect.y-n.rect.y||r.rect.x-n.rect.x:r.rect?-1:n.rect?1:(r.depth??0)-(n.depth??0)||r.index-n.index)}function ea(e){let t=!!el(e,{includeIdentifiers:!1});return 2*!!ec(e)+ +!t}function el(e,t){let r=(t.includeIdentifiers?[e.label,e.value,e.identifier]:[e.label,e.value]).map(e=>"string"==typeof e?e.trim():"").find(e=>e.length>0);return r?r.replace(/\s+/g," ").slice(0,80):""}function ec(e){let t=P(`${e.type??""} ${e.role??""} ${e.subrole??""}`),r=`${e.label??""} ${e.value??""}`.trim().toLowerCase();return et.some(e=>t.includes(e))||er.has(r)||r.endsWith(".fill")}function eu(e){return P(`${e.type??""} ${e.role??""} ${e.subrole??""}`).includes("button")}function ef(e){var t;let{session:r,selectorExpression:n}=e;if(!r||"ios"!==r.device.platform||r.postGestureStabilization)return null;let s=O(n);if(!s||1!==s.selectors.length)return null;let o=s.selectors[0];if(!o||1!==o.terms.length)return null;let i=o.terms[0];return i&&"string"==typeof i.value&&("id"===(t=i.key)||"label"===t||"text"===t||"value"===t)?{key:i.key,value:i.value,raw:o.raw}:null}function ed(e,t={}){let r=y(e);if("ELEMENT_NOT_FOUND"===r.code)return!0===t.allowElementNotFound;if("COMMAND_FAILED"!==r.code)return!1;let n=r.message.toLowerCase();return n.includes("fetch failed")||n.includes("timed out")||n.includes("timeout")||n.includes("runner did not accept connection")||n.includes("invalid runner response")}async function ep(e){let{device:t,node:r,flags:n,appBundleId:o,traceOutPath:i,surface:a,contextFromFlags:l}=e,c=b(r),u=q(r.rect);if(!u||"ios"===t.platform&&c&&!N(r.type??""))return c;try{let e=await s(t,"read",[String(u.x),String(u.y)],void 0,{...l(n,o,i),surface:a}),f=e&&"object"==typeof e?e:void 0,d="string"==typeof f?.text?f.text:"";if(d.trim())return d;return m({level:"warn",phase:"interaction_read_fallback",data:{reason:"empty_backend_text",nodeRef:r.ref,surface:a,platform:t.platform}}),c}catch(e){return m({level:"warn",phase:"interaction_read_fallback",data:{reason:"backend_read_failed",nodeRef:r.ref,surface:a,platform:t.platform,error:e instanceof Error?e.message:String(e)}}),c}}function em(t){let r,n,{session:s,sessionStore:o,sessionName:i}=t,a=0;return{capture:async l=>{var u,f;let d=Date.now(),p=(u=l,f=t.req.flags?.out,JSON.stringify({presentationKey:E(D(e_(u))),includeRects:!0===u.includeRects,outPath:u.outPath??f??null})),m=function(e){let{timestamp:t,lastSnapshotAt:r,lastSnapshotResult:n,lastSnapshotCacheKey:s,session:o,request:i,cacheKey:a}=e;if(!(i.cache?.forceFresh===!0||!n||s!==a||t-r>=750||c(o))&&!eS(o,i))return n}({timestamp:d,lastSnapshotAt:a,lastSnapshotResult:r,lastSnapshotCacheKey:n,session:s,request:l,cacheKey:p});if(m)return m;let g=function(e){var t,r;let{session:n,timestamp:s,request:o}=e,i=n?.snapshot;if(i&&(t=n,r=o,!(r.cache?.forceFresh===!0||r.cache?.useSessionSnapshot!==!0||c(t)||eS(t,r)))&&s-i.createdAt<750&&i.presentationKey===function(e){return E(D(e_(e)))}(o))return i}({session:s,timestamp:d,request:l});if(g)return a=g.createdAt,r={snapshot:g},n=p,r;let h=await eg({params:t,request:l}),y={snapshot:h};return function(t){let{session:r,sessionStore:n,sessionName:s,snapshot:o}=t;!r||C(o.snapshotQuality)||(e(r,o),n.set(s,r))}({session:s,sessionStore:o,sessionName:i,snapshot:h}),a=d,r=y,n=p,y}}}async function eg(e){var t;let{params:r,request:n}=e,s=await ew(r,n,n.snapshotScope);return n.recovery?.legacyIosSparse&&!(t=s).snapshotQuality&&"xctest"===t.backend&&1===t.nodes.length&&t.nodes[0]?.type==="Application"?await eh({runtimeParams:r,request:n,policy:n.recovery.legacyIosSparse}):n.recovery?.sparseVerdictQueryScope?.shouldScope&&C(s.snapshotQuality)?await ey({runtimeParams:r,request:n,policy:n.recovery.sparseVerdictQueryScope,snapshot:s}):s}async function eh(e){let{runtimeParams:t,request:r,policy:n}=e;try{return await ew(t,r,n.scope,!1)}catch(e){if(!n.shouldScope)throw e;return await ew(t,r,n.query,!1)}}async function ey(e){let{runtimeParams:t,request:r,policy:n,snapshot:s}=e;try{return await ew(t,r,n.query,!1)}catch{return s}}async function ew(e,t,r,s=t.flags?.snapshotInteractiveOnly){return(await n({device:e.device,session:e.session,flags:{...t.flags,snapshotInteractiveOnly:s},outPath:t.outPath??e.req.flags?.out,logPath:e.logPath??"",snapshotScope:r,includeRects:t.includeRects})).snapshot}function eS(e,t){return t.cache?.bypassForPostGestureStabilization===!0&&!!e?.postGestureStabilization}function e_(e){return void 0===e.snapshotScope?e.flags:{...e.flags,snapshotScope:e.snapshotScope}}function ev(n){return k({backend:function(e){let{req:t,session:n,device:s,logPath:o,sessionName:i,sessionStore:a}=e,l=em({device:s,session:n,sessionStore:a,sessionName:i,req:t,logPath:o});return{platform:s.platform,captureSnapshot:async(e,r)=>{var n;let o,i={...t.flags,...(n=r,o={},n?.interactiveOnly!==void 0&&(o.snapshotInteractiveOnly=n.interactiveOnly),n?.scope!==void 0&&(o.snapshotScope=n.scope),n?.depth!==void 0&&(o.snapshotDepth=n.depth),n?.raw!==void 0&&(o.snapshotRaw=n.raw),o)},a=r?.includeRects===!0,c=r?.scope??t.flags?.snapshotScope,u="wait"===t.command||"find"===t.command||a&&"web"===s.platform;return await l.capture({flags:i,snapshotScope:c,includeRects:a,cache:{forceFresh:u,useSessionSnapshot:!0,bypassForPostGestureStabilization:!0}})},readText:async(i,a)=>({text:await ep({device:s,node:a,flags:t.flags,appBundleId:n?.appBundleId,traceOutPath:n?.trace?.outPath,surface:n?.surface,contextFromFlags:e.contextFromFlags??((e,t,n)=>r(o??"",e,t,n))})}),findText:async(t,r)=>({found:await ek(e,r)})}}(n),...T("selector commands",{plural:!0}),sessions:t({sessionName:n.sessionName,getSession:()=>n.session,recordOptions:{includeSnapshot:!0},setRecord:t=>{n.session&&t.snapshot&&(e(n.session,t.snapshot),n.sessionStore.set(n.sessionName,n.session))}})})}async function ex(e,t){let r=e.sessionStore.get(e.sessionName);if(!r&&t.requireSession)return{ok:!1,response:u()};let n=r?.device??await f(e.req.flags??{});r||await g(n);let s=o(t.capability,n);return s?{ok:!1,response:s}:{ok:!0,runtime:ev({...e,session:r,device:n})}}async function ek(e,t){let r=await eI(e,t);if(null!==r)return r;let n=await eA(e,t);return null!==n?n:await eP(e,t)}async function eI(e,t){return"macos"===e.device.platform&&e.session?.surface&&"app"!==e.session.surface?await eP(e,t):null}async function eA(e,t){var r,n,s;let o=$((r=e).device.platform)&&r.session?.appBundleId?{device:r.device,appBundleId:r.session.appBundleId,traceLogPath:r.session.trace?.outPath}:null;if(!o)return null;let i=await d(o.device,{command:"findText",text:t,appBundleId:o.appBundleId},(n=e,s=o,h({req:n.req,logPath:n.logPath,traceLogPath:s.traceLogPath})));return i?.found===!0}async function eP(e,t){return!!R((await eN(e)).nodes,t)}async function eN(e){let t=em({device:e.device,session:e.session,sessionStore:e.sessionStore,sessionName:e.sessionName,req:e.req,logPath:e.logPath}),{snapshot:r}=await t.capture({flags:{...e.req.flags,snapshotInteractiveOnly:!1},cache:{forceFresh:!0,bypassForPostGestureStabilization:!0}});return r}async function eR(e){var t;let{req:r}=e;if("find"!==r.command)return null;let n=r.positionals??[];if(0===n.length)return a("INVALID_ARGS","find requires a locator or text");let s=L(n);if(!s.query)return a("INVALID_ARGS","find requires a value");if(r.flags?.findFirst&&r.flags?.findLast)return a("INVALID_ARGS","find accepts only one of --first or --last");let o=s.action;if("exists"!==(t=o)&&"wait"!==t&&"get_text"!==t&&"get_attrs"!==t)return null;let i=await ex(e,{requireSession:!1,capability:"find"});return i.ok?await eF(async()=>{let t=await i.runtime.selectors.find({session:e.sessionName,requestId:r.meta?.requestId,locator:s.locator,query:s.query,action:o,timeoutMs:s.timeoutMs});return Y(e.sessionStore,e.sessionName,r,function(e,t){if("exists"===t)return{found:!0};if("wait"===t)return{found:!0,waitedMs:e.waitedMs};let r="string"==typeof e.ref?e.ref:void 0;return"get_attrs"===t?{ref:r,action:"get attrs"}:{ref:r,action:"get text",text:"string"==typeof e.text?e.text:""}}(t,o)),"found"===t.kind?{found:!0,..."number"==typeof t.waitedMs?{waitedMs:t.waitedMs}:{}}:{..."string"==typeof t.ref?{ref:t.ref}:{},..."string"==typeof t.text?{text:t.text}:{},...t.node&&"object"==typeof t.node?{node:t.node}:{}}}):i.response}async function eb(e){let{req:t}=e;if("get"!==t.command)return null;let r=t.positionals?.[0];if("text"!==r&&"attrs"!==r)return a("INVALID_ARGS","get only supports text or attrs");let n=function(e){let t=e.positionals?.[1]??"";if(t.startsWith("@"))return{ok:!0,target:{kind:"ref",ref:t,fallbackLabel:e.positionals.length>2?e.positionals.slice(2).join(" ").trim():""}};let r=e.positionals?.slice(1).join(" ").trim()??"";return r?{ok:!0,target:{kind:"selector",selector:r}}:{ok:!1,response:a("INVALID_ARGS","get requires @ref or selector expression")}}(t);if(!n.ok)return n.response;if("ref"===n.target.kind){let e=H("get",t.flags);if(e)return e}if("selector"===n.target.kind){let t=await eM(e,r,n.target.selector);if(t)return t}let s=await ex(e,{requireSession:!0,capability:"get"});return s.ok?await eF(async()=>{let o=await s.runtime.selectors.get({session:e.sessionName,requestId:t.meta?.requestId,property:r,target:n.target});return Y(e.sessionStore,e.sessionName,t,W(o,r)),J(o)}):s.response}async function eq(e){let{req:t}=e;if("is"!==t.command)return null;let r=(t.positionals?.[0]??"").toLowerCase();if(!v(r))return a("INVALID_ARGS","is requires predicate: visible|hidden|exists|editable|selected|text");let{split:n}=A(t.positionals??[]);if(!n)return a("INVALID_ARGS","is requires a selector expression");let s=n.rest.join(" ").trim();if("text"===r&&!s)return a("INVALID_ARGS","is text requires expected text value");if("text"!==r&&n.rest.length>0)return a("INVALID_ARGS",`is ${r} does not accept trailing values`);let o=await eE(e,r,n.selectorExpression,s);if(o)return o;let i=await ex(e,{requireSession:!0,capability:"is"});if(!i.ok)return i.response;let l=await eF(async()=>{let o=await i.runtime.selectors.is({session:e.sessionName,requestId:t.meta?.requestId,predicate:r,selector:n.selectorExpression,expectedText:s});return Y(e.sessionStore,e.sessionName,t,o),X(o)});return await eG(e,l,`is ${r}`)}async function eC(e){let{req:t,sessionName:r,sessionStore:n}=e,s=M(t.positionals??[]);if(!s)return a("INVALID_ARGS","wait requires a duration or text");let{session:i,device:l}=await K(n,r,t.flags);if("sleep"!==s.kind){let e=o("wait",l);if(e)return e}if("selector"===s.kind){let t=await eD({...e,session:i,device:l,selectorExpression:s.selectorExpression,timeoutMs:s.timeoutMs});if(t)return t}let c=async()=>{let o=ev({...e,session:i,device:l}),a=await eF(async()=>{let e=await o.selectors.wait({session:r,requestId:t.meta?.requestId,target:function(e,t){if("sleep"===e.kind)return{kind:"sleep",durationMs:e.durationMs};if("selector"===e.kind)return{kind:"selector",selector:e.selectorExpression,timeoutMs:e.timeoutMs};if("ref"===e.kind){if(!t?.snapshot)throw new S("INVALID_ARGS","Ref wait requires an existing snapshot in session.");return{kind:"ref",ref:e.rawRef,timeoutMs:e.timeoutMs}}if(!e.text)throw new S("INVALID_ARGS","wait requires text");return{kind:"text",text:e.text,timeoutMs:e.timeoutMs}}(s,i)});return Y(n,r,t,e),{waitedMs:e.waitedMs,..."string"==typeof e.text?{text:e.text}:{},..."string"==typeof e.selector?{selector:e.selector}:{}}}),c=await en({req:t,logPath:e.logPath,session:i,device:l},a);return await eG(e,c,"wait")};return"sleep"===s.kind?await c():await F(i,l,c)}async function eM(e,t,r){let n=e.sessionStore.get(e.sessionName),s=ef({session:n,selectorExpression:r});if(!n||!s||"text"===t&&"id"!==s.key)return null;let o=await eO(e,n,s);if(eK(o))return o.response;if(!o)return null;let i=function(e,t,r){if(!r.found||!r.node)return null;let n={target:{kind:"selector",selector:t},node:r.node,selectorChain:[t]};return"attrs"===e?{kind:"attrs",...n}:"string"!=typeof r.text?null:{kind:"text",...n,text:r.text}}(t,s.raw,o);return i?(Y(e.sessionStore,e.sessionName,e.req,W(i,t)),{ok:!0,data:J(i)}):null}async function eE(e,t,r,n){var s,o,i,a,l;let c;if("hidden"===t)return null;let u=await e$(e,r);if(eK(u))return u.response;if(!u?.result.found||!u.result.node)return null;let f="exists"===t?{predicate:t,pass:!0,selector:u.selector.raw,matches:1,selectorChain:[u.selector.raw]}:(s=t,o=n,i=u.selector.raw,a=u.session,c=_({predicate:s,node:l=u.result.node,nodes:[l],expectedText:o,platform:a.device.platform}),{predicate:s,pass:c.pass,selector:i,..."text"===s?{text:c.actualText}:{},selectorChain:[i]});return f?(Y(e.sessionStore,e.sessionName,e.req,f),{ok:!0,data:X(f)}):null}async function eD(e){let t=ef({session:e.session,selectorExpression:e.selectorExpression});if(!e.session||!t)return null;let r=Date.now(),n=await eO(e,e.session,t);if(eK(n))return n.response;if(!n?.found)return null;let s={kind:"selector",selector:t.raw,waitedMs:Date.now()-r,selectorChain:[t.raw]};return Y(e.sessionStore,e.sessionName,e.req,s),await en({req:e.req,logPath:e.logPath,session:e.session,device:e.device},{ok:!0,data:s})}async function e$(e,t){let r=e.sessionStore.get(e.sessionName),n=ef({session:r,selectorExpression:t});if(!r||!n)return null;let s=await eO(e,r,n);return eK(s)?s:s?{session:r,selector:n,result:s}:null}async function eL(e,t,r){let n=await d(t.device,{command:"querySelector",selectorKey:r.key,selectorValue:r.value,appBundleId:t.appBundleId},h({req:e.req,logPath:e.logPath,traceLogPath:t.trace?.outPath})),s=!0===n.found,o=function(e){let t=e.nodes;if(!Array.isArray(t))return;let r=t[0];if(r&&"object"==typeof r)return r}(n);return{found:s,..."string"==typeof n.text?{text:n.text}:{},...o?{node:o}:{}}}async function eO(e,t,r){try{return await eL(e,t,r)}catch(e){if(ed(e,{allowElementNotFound:!0}))return null;return{kind:"error",response:{ok:!1,error:w(e)}}}}function eK(e){return null!==e&&"kind"in e&&"error"===e.kind}async function eF(e){try{return{ok:!0,data:await e()}}catch(t){let e=y(t);return a(e.code,e.message,e.details)}}async function eG(e,t,r){var n;let s;if(t.ok)return t;let o=e.sessionStore.get(e.sessionName);if(!o)return t;try{s=await z(o)}catch{return t}return s?a(t.error.code,`${r} failed because ${"com.google.android.permissioncontroller"===(n=s).foregroundPackage?`Android permission dialog is blocking ${n.expectedPackage}`:`${n.foregroundPackage} is foreground instead of ${n.expectedPackage}`}.`,{...t.error.details??{},...s,blockedBy:"android_foreground_surface",originalMessage:t.error.message}):t}export{j as assertAndroidPressStayedInApp,U as buildSnapshotSession,T as createDaemonRuntimePolicy,em as createSelectorCaptureRuntime,eR as dispatchFindReadOnlyViaRuntime,eb as dispatchGetViaRuntime,eq as dispatchIsViaRuntime,eC as dispatchWaitViaRuntime,Q as isAndroidEscapeError,ed as isDirectIosSelectorFallbackError,ef as readSimpleIosSelectorTarget,ep as readTextForNode,V as recordIfSession,H as refSnapshotFlagGuardResponse,K as resolveSessionDevice,F as withSessionlessRunnerCleanup};
|
package/dist/src/selectors.d.ts
CHANGED
|
@@ -65,7 +65,7 @@ export declare function resolveSelectorChain(nodes: SnapshotState['nodes'], chai
|
|
|
65
65
|
disambiguateAmbiguous?: boolean;
|
|
66
66
|
}): SelectorResolution | null;
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
declare type Selector = {
|
|
69
69
|
raw: string;
|
|
70
70
|
terms: SelectorTerm[];
|
|
71
71
|
};
|
|
@@ -82,7 +82,7 @@ export declare type SelectorDiagnostics = {
|
|
|
82
82
|
|
|
83
83
|
declare type SelectorKey = 'id' | 'role' | 'text' | 'label' | 'value' | 'appname' | 'windowtitle' | 'visible' | 'hidden' | 'editable' | 'selected' | 'enabled' | 'hittable';
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
declare type SelectorResolution = {
|
|
86
86
|
node: SnapshotNode;
|
|
87
87
|
selector: Selector;
|
|
88
88
|
selectorIndex: number;
|
|
@@ -97,13 +97,17 @@ declare type SelectorTerm = {
|
|
|
97
97
|
|
|
98
98
|
declare type SnapshotBackend = 'xctest' | 'android' | 'macos-helper' | 'linux-atspi' | 'web';
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
declare type SnapshotNode = RawSnapshotNode & {
|
|
101
101
|
ref: string;
|
|
102
102
|
};
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
105
|
* Structured quality verdict computed once by the iOS runner's snapshot capture plan.
|
|
106
106
|
* The daemon renders it; it never re-derives degradation from node shapes.
|
|
107
|
+
*
|
|
108
|
+
* Defined here (the foundational snapshot type module) rather than in
|
|
109
|
+
* snapshot-quality.ts so SnapshotNode can reference it without a cyclic import;
|
|
110
|
+
* snapshot-quality.ts (the validation logic) re-exports it for existing callers.
|
|
107
111
|
*/
|
|
108
112
|
declare type SnapshotQualityVerdict = {
|
|
109
113
|
state: 'healthy' | 'recovered' | 'sparse';
|
package/dist/src/selectors.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{findSelectorChainMatch,formatSelectorFailure,isNodeEditable,isNodeVisible,resolveSelectorChain}from"./
|
|
1
|
+
export{findSelectorChainMatch,formatSelectorFailure,isNodeEditable,isNodeVisible,resolveSelectorChain}from"./893.js";export{isSelectorToken,parseSelectorChain,tryParseSelectorChain}from"./587.js";
|
package/dist/src/server.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
`)}export{
|
|
1
|
+
import{SESSION_SURFACES as e,isCommandName as t,stringSchema as r,DEVICE_ROTATIONS as n,listMcpCommandMetadata as o,booleanSchema as i,BACK_MODES as s,looseObjectSchema as a}from"./2948.js";import{DEVICE_TARGETS as c,RESPONSE_LEVELS as u,jsonRpcRequestSchema as d,PLATFORMS as p}from"./6849.js";import{formatCliOutput as l}from"./cli-output.js";import{readVersion as f}from"./1404.js";function m(e){return{type:"number",...e?{description:e}:{}}}function h(e,t){return{type:"string",enum:e,...t?{description:t}:{}}}function g(e){return{type:"string",const:e}}function y(e,t=[],r){return{type:"object",...r?{description:r}:{},properties:e,...t.length>0?{required:t}:{}}}let b={type:"array",items:{type:"string"}},w=y({x:m(),y:m(),width:m(),height:m()},["x","y","width","height"]),v=y({x:m(),y:m()},["x","y"]),x=y({index:m(),ref:r("Stable snapshot ref such as e12."),type:r(),role:r(),subrole:r(),label:r(),value:r(),identifier:r(),rect:w,enabled:i(),selected:i(),focused:i(),visibleToUser:i(),hittable:i(),depth:m(),parentIndex:m(),pid:m(),bundleId:r(),appName:r(),windowTitle:r(),surface:r(),hiddenContentAbove:i(),hiddenContentBelow:i(),interactionBlocked:h(["covered"]),presentationHints:b},["index","ref"],"Resolved snapshot node for the matched element."),k=y({kind:g("ref"),ref:r()},["kind","ref"]),C=y({kind:g("selector"),selector:r()},["kind","selector"]);function _(e={}){let t=e.properties??{},n=e.required??[];return{type:"object",oneOf:[y({kind:g("point"),point:v,...t},["kind","point",...n]),y({kind:g("ref"),point:v,target:k,node:x,selectorChain:b,refLabel:r(),...t},["kind","target",...n]),y({kind:g("selector"),point:v,target:C,node:x,selectorChain:b,refLabel:r(),...t},["kind","point","target","node","selectorChain",...n])]}}let S=a("Raw backend result passthrough."),E={platform:h(p),target:h(c),device:r("Human-readable device name."),id:r("Stable device id."),kind:h(["simulator","emulator","device"])},A=["platform","target","device","id","kind"],j=y({success:i(),exitCode:m(),stdout:r(),stderr:r(),error:a("Normalized error detail when shutdown failed.")},["success","exitCode","stdout","stderr"]),O={press:_({properties:{backendResult:S,message:r()}}),fill:_({properties:{text:r("Text submitted to the field."),warning:r(),backendResult:S,message:r()},required:["text"]}),longpress:_({properties:{durationMs:m(),backendResult:S,message:r()}}),boot:y({...E,booted:{type:"boolean",const:!0}},[...A,"booted"]),shutdown:y({...E,shutdown:j},[...A,"shutdown"]),viewport:y({width:m(),height:m(),message:r()},["width","height","message"]),home:y({action:g("home"),message:r()},["action","message"]),back:y({action:g("back"),mode:h(s),message:r()},["action","mode","message"]),rotate:y({action:g("rotate"),orientation:h(n),message:r()},["action","orientation","message"]),"app-switcher":y({action:g("app-switcher"),message:r()},["action","message"]),clipboard:{type:"object",oneOf:[y({action:g("read"),text:r()},["action","text"]),y({action:g("write"),textLength:m(),message:r()},["action","textLength","message"])]},appstate:{type:"object",oneOf:[y({platform:h(["ios","macos"]),appName:r(),appBundleId:r(),source:g("session"),surface:h(e),device_udid:r("iOS only — the session device UDID."),ios_simulator_device_set:{type:["string","null"],description:"iOS only — the simulator set path, or null when unknown."}},["platform","appName","source","surface"]),y({platform:g("android"),package:r(),activity:r()},["platform","package","activity"])]},keyboard:y({platform:h(["android","ios"]),action:h(["status","dismiss","enter"]),visible:i(),wasVisible:i(),dismissed:i(),attempts:m(),inputType:r(),type:h(["text","number","email","phone","password","datetime","unknown"]),inputMethodPackage:r(),focusedPackage:r(),focusedResourceId:r(),inputOwner:h(["app","ime","unknown"]),message:r()},["platform","action"])},R=function(e={}){return{execute:async(r,n)=>{var o;if(!t(r))throw Error(`Unknown command tool: ${r}`);let i=!(o=n)||"object"!=typeof o||Array.isArray(o)?{client:{},outputFormat:"optimized"}:{client:function(e){let t=e.stateDir,r=e.includeCost,n=e.responseLevel,o={};if(void 0!==t&&("string"!=typeof t||0===t.length))throw Error("Expected stateDir to be a non-empty string.");if("string"==typeof t&&(o.stateDir=t),void 0!==r&&"boolean"!=typeof r)throw Error("Expected includeCost to be a boolean.");!0===r&&(o.cost=!0);let i=function(e){if(void 0!==e){if("string"!=typeof e||!u.includes(e))throw Error("Expected responseLevel to be one of 'digest', 'default', or 'full'.");return e}}(n);return void 0!==i&&(o.responseLevel=i),o}(o),outputFormat:function(e){if(void 0===e)return"optimized";if("optimized"!==e&&"json"!==e)throw Error('Expected mcpOutputFormat to be "optimized" or "json".');return e}(o.mcpOutputFormat)},s=function(e){if(!e||"object"!=typeof e||Array.isArray(e))return e;let{stateDir:t,mcpOutputFormat:r,includeCost:n,responseLevel:o,...i}=e;return i}(n),a=await N(e,i.client),c=await (e.runCommand??L)(a,r,s);return{isError:!1,structuredContent:c,content:[{type:"text",text:function(e){if("json"===e.outputFormat||void 0!==e.responseLevel&&"default"!==e.responseLevel)return P(e.result);let t=l({name:e.name,input:e.input,result:e.result});return"string"==typeof t?.text?t.text:P(t?.data??e.result)}({name:r,input:s,result:c,outputFormat:i.outputFormat,responseLevel:i.client.responseLevel})}]}}}}();async function N(e,t){if(e.createClient)return await e.createClient(t);let{createAgentDeviceClient:r}=await import("./495.js");return r(t)}async function L(e,t,r){let n=await import("./command-surface.js");return await n.runCommand(e,t,r)}function P(e){return"string"==typeof e?e:JSON.stringify(e,null,2)}async function M(e){if("2.0"!==e.jsonrpc||"string"!=typeof e.method)return U(e.id??null,-32600,"Invalid JSON-RPC request.");if(void 0===e.id)return null;try{var t,r;return t=e.id,r=await K(e.method,e.params),{jsonrpc:"2.0",id:t,result:r}}catch(t){if(t instanceof F)return U(e.id,-32601,t.message);return U(e.id,-32602,t instanceof Error?t.message:String(t))}}async function K(e,t){switch(e){case"initialize":return{protocolVersion:"2025-11-25",capabilities:{tools:{}},serverInfo:{name:"agent-device",version:f()}};case"ping":return{};case"tools/list":return{tools:o().map(e=>{var t;let r=e.name in O?O[e.name]:void 0;return{name:e.name,description:e.description,inputSchema:{...t=e.inputSchema,properties:{...t.properties,stateDir:{type:"string",description:"Agent-device state directory."},mcpOutputFormat:{type:"string",enum:["optimized","json"],description:"MCP text content format. Defaults to optimized agent-friendly text; use json for JSON text. Structured content is always returned separately."},includeCost:{type:"boolean",description:"Include per-command agent-cost (cost.wallClockMs, …) in structuredContent. Defaults to off; the default response shape is unchanged."},responseLevel:{type:"string",enum:["digest","default","full"],description:"Response verbosity: token-cheap digest / default (today) / full. Defaults to default; the default response shape is unchanged."}}},...r?{outputSchema:r}:{}}})};case"tools/call":return await D(t);default:throw new F(`Unsupported MCP method: ${e}`)}}async function D(e){let t=function(e){if(!e||"object"!=typeof e||Array.isArray(e))throw Error("Expected object parameters.");return e}(e),r=function(e,t){let r=e[t];if("string"!=typeof r||0===r.length)throw Error(`Expected ${t} to be a non-empty string.`);return r}(t,"name");try{return await R.execute(r,t.arguments)}catch(e){return function(e,t=!1){return{isError:t,content:[{type:"text",text:e}]}}(e instanceof Error?e.message:String(e),!0)}}function U(e,t,r){return{jsonrpc:"2.0",id:e,error:{code:t,message:r}}}class F extends Error{}async function I(){let e=function(e={}){let t=e.handlePayload??z,r=e.write??$,n=Promise.resolve();return{push:e=>{var o;let i=Array.isArray(o=e)?1===o.length?q(o[0]):null:q(o);n=n.then(async()=>{let n=await t(e);n&&r(n)}).catch(e=>{r({jsonrpc:"2.0",id:i,error:{code:-32603,message:e instanceof Error?e.message:String(e)}})})},idle:async()=>{await n}}}(),t=new T(t=>{e.push(t)});process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{try{t.push(e)}catch(e){$({jsonrpc:"2.0",id:null,error:{code:-32700,message:e instanceof Error?e.message:String(e)}})}}),await new Promise(e=>{process.stdin.on("end",e),process.stdin.on("close",e),process.stdin.resume()}),await e.idle()}function z(e){return Array.isArray(e)?H(e):J(e)}async function H(e){let t=[];for(let n of e){var r;t.push(...(r=await J(n))?[r]:[])}return t.length>0?t:null}async function J(e){let t;try{t=d.parse(e)}catch{return{jsonrpc:"2.0",id:q(e),error:{code:-32600,message:"Invalid JSON-RPC request."}}}return M(t)}function q(e){if(e&&"object"==typeof e&&!Array.isArray(e)){let t=e.id;if("string"==typeof t||"number"==typeof t||null===t)return t}return null}class T{buffer="";sink;constructor(e){this.sink=e}push(e){for(this.buffer+=e;;){let e=this.tryReadLineMessage();if(void 0!==e){this.emit(e);continue}break}}tryReadLineMessage(){let e=this.buffer.indexOf("\n");if(-1===e)return;let t=this.buffer.slice(0,e).trim();return this.buffer=this.buffer.slice(e+1),t.length>0?t:void 0}emit(e){this.sink(JSON.parse(e))}}function $(e){process.stdout.write(`${JSON.stringify(e)}
|
|
2
|
+
`)}export{I as runAgentDeviceMcpServer};
|