@kaeawc/auto-mobile 0.0.92 → 0.0.93

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.
Files changed (257) hide show
  1. package/dist/build-info.json +12 -12
  2. package/dist/schemas/test-plan.schema.json +370 -52
  3. package/dist/src/{chunk-jhnbfj19.js → chunk-00167wan.js} +5 -5
  4. package/dist/src/chunk-0jrrsnc9.js +6 -0
  5. package/dist/src/chunk-0n6swkwg.js +5 -0
  6. package/dist/src/chunk-0qsy1qsx.js +57 -0
  7. package/dist/src/chunk-11m8wj8j.js +237 -0
  8. package/dist/src/{chunk-4pfftt07.js → chunk-1p7xf5kb.js} +3 -3
  9. package/dist/src/chunk-23p5f095.js +4 -0
  10. package/dist/src/{chunk-a4k9rt7z.js → chunk-26k317v5.js} +3 -3
  11. package/dist/src/chunk-284ptqa4.js +7 -0
  12. package/dist/src/chunk-28re4xrg.js +4 -0
  13. package/dist/src/chunk-2f9rkmhx.js +5 -0
  14. package/dist/src/chunk-2fxdh48t.js +4 -0
  15. package/dist/src/chunk-2tbdrbt6.js +4 -0
  16. package/dist/src/chunk-2tqzq5qh.js +971 -0
  17. package/dist/src/chunk-32gvv9b4.js +21 -0
  18. package/dist/src/chunk-3f3t1x97.js +8 -0
  19. package/dist/src/chunk-3fspkbc7.js +4 -0
  20. package/dist/src/chunk-3qwrzxh0.js +11 -0
  21. package/dist/src/chunk-3sy6ahhz.js +26 -0
  22. package/dist/src/chunk-3vg455e6.js +4 -0
  23. package/dist/src/chunk-3xkpk44w.js +5 -0
  24. package/dist/src/chunk-3y6eykz9.js +4 -0
  25. package/dist/src/chunk-49bsqdx3.js +4 -0
  26. package/dist/src/chunk-4hw3665t.js +42 -0
  27. package/dist/src/chunk-58wzr3mt.js +5 -0
  28. package/dist/src/chunk-5d2hft6c.js +5 -0
  29. package/dist/src/chunk-5snsn9cf.js +4 -0
  30. package/dist/src/chunk-5tm9mn47.js +54 -0
  31. package/dist/src/chunk-5zc77a5t.js +4 -0
  32. package/dist/src/chunk-64j0majr.js +19 -0
  33. package/dist/src/chunk-65y8v4td.js +17 -0
  34. package/dist/src/chunk-6awz7vhq.js +5 -0
  35. package/dist/src/{chunk-stfzjr9y.js → chunk-6bznss4t.js} +5 -5
  36. package/dist/src/chunk-6ep7f5ge.js +4 -0
  37. package/dist/src/{chunk-0dfnyfkh.js → chunk-6wt9rgjf.js} +3 -3
  38. package/dist/src/chunk-6xmss8er.js +8 -0
  39. package/dist/src/chunk-710rxz9y.js +5 -0
  40. package/dist/src/chunk-7cs2da62.js +5 -0
  41. package/dist/src/chunk-7g103b54.js +4 -0
  42. package/dist/src/chunk-7j5vj4pa.js +4 -0
  43. package/dist/src/chunk-7rz9898k.js +5 -0
  44. package/dist/src/chunk-7tw8x05v.js +4 -0
  45. package/dist/src/chunk-7zy586cb.js +4 -0
  46. package/dist/src/{chunk-r0f1q783.js → chunk-8037tzzj.js} +3 -3
  47. package/dist/src/chunk-84nanf8s.js +5 -0
  48. package/dist/src/{chunk-950qcq1e.js → chunk-8g1922rp.js} +4 -4
  49. package/dist/src/chunk-8gvwcv2p.js +4 -0
  50. package/dist/src/{chunk-sz1a7p43.js → chunk-8ph29g99.js} +1 -1
  51. package/dist/src/chunk-8thrmfat.js +4 -0
  52. package/dist/src/chunk-8zgzzs7k.js +78 -0
  53. package/dist/src/chunk-97b1zmaq.js +4 -0
  54. package/dist/src/chunk-9fth9ayh.js +4 -0
  55. package/dist/src/chunk-9j6anhjr.js +4 -0
  56. package/dist/src/{chunk-w7zsh13a.js → chunk-aabchzxk.js} +4 -4
  57. package/dist/src/{chunk-wry6skrm.js → chunk-ayp07wbb.js} +1 -1
  58. package/dist/src/chunk-bfjwqk8q.js +4 -0
  59. package/dist/src/{chunk-jdqvcq0a.js → chunk-bfw6125s.js} +3 -3
  60. package/dist/src/chunk-bnhscm2n.js +4 -0
  61. package/dist/src/chunk-bxmsax22.js +5 -0
  62. package/dist/src/chunk-c1vs4g1n.js +4 -0
  63. package/dist/src/chunk-ct3e7yng.js +4 -0
  64. package/dist/src/{chunk-8en08r7q.js → chunk-cyy71ggq.js} +3 -3
  65. package/dist/src/chunk-ddzkz92z.js +4 -0
  66. package/dist/src/chunk-dgrbdpb7.js +9 -0
  67. package/dist/src/chunk-drdz8hee.js +5 -0
  68. package/dist/src/chunk-e98dqnc9.js +145 -0
  69. package/dist/src/chunk-eersndy7.js +5 -0
  70. package/dist/src/chunk-eggq53ks.js +4 -0
  71. package/dist/src/chunk-f2dya7cs.js +4 -0
  72. package/dist/src/chunk-fbakd6cw.js +4 -0
  73. package/dist/src/chunk-fgb98zjj.js +5 -0
  74. package/dist/src/chunk-fmpad823.js +5 -0
  75. package/dist/src/{chunk-v90es00d.js → chunk-g04bwyj3.js} +2 -2
  76. package/dist/src/chunk-g0xr8zw0.js +4 -0
  77. package/dist/src/chunk-g3rjs80x.js +4 -0
  78. package/dist/src/chunk-g78rtrfv.js +4 -0
  79. package/dist/src/{chunk-r20pe1nv.js → chunk-g7tmjbkt.js} +3 -3
  80. package/dist/src/chunk-gabgv3y5.js +4 -0
  81. package/dist/src/chunk-gmr45m8f.js +4 -0
  82. package/dist/src/chunk-gsyfebqj.js +4 -0
  83. package/dist/src/chunk-h5rpn9wr.js +247 -0
  84. package/dist/src/chunk-hb1x7v55.js +4 -0
  85. package/dist/src/chunk-hrfytkxh.js +168 -0
  86. package/dist/src/chunk-j4yjzbak.js +4 -0
  87. package/dist/src/chunk-jd6a5v6p.js +5 -0
  88. package/dist/src/chunk-jh6vhqzx.js +5 -0
  89. package/dist/src/chunk-kn5spq15.js +4 -0
  90. package/dist/src/{chunk-w2mwa0jk.js → chunk-m94qs0f0.js} +2 -2
  91. package/dist/src/chunk-mjrps0ac.js +7 -0
  92. package/dist/src/chunk-n03dmn5q.js +5 -0
  93. package/dist/src/chunk-n72qhwd9.js +4 -0
  94. package/dist/src/{chunk-km0dy5vc.js → chunk-ndt1ccxm.js} +9 -9
  95. package/dist/src/chunk-nr6y0n4y.js +5 -0
  96. package/dist/src/chunk-pcsen2vs.js +4 -0
  97. package/dist/src/chunk-ppfr0b8h.js +4 -0
  98. package/dist/src/chunk-q10bdnat.js +4 -0
  99. package/dist/src/chunk-q9xmq595.js +4 -0
  100. package/dist/src/{chunk-2v7tw1nj.js → chunk-qcxzr2my.js} +3 -3
  101. package/dist/src/chunk-qf6nynk6.js +7 -0
  102. package/dist/src/chunk-qyqypn28.js +5 -0
  103. package/dist/src/chunk-r2epb56s.js +4 -0
  104. package/dist/src/chunk-r5bxavdx.js +311 -0
  105. package/dist/src/{chunk-r14wwk37.js → chunk-r6z1eztj.js} +12 -12
  106. package/dist/src/{chunk-d57432wq.js → chunk-r7bpkgff.js} +2 -2
  107. package/dist/src/chunk-r8jf3d92.js +4 -0
  108. package/dist/src/{chunk-9rkfyt6q.js → chunk-rbpdk7m0.js} +3 -3
  109. package/dist/src/chunk-rc0dbypm.js +4 -0
  110. package/dist/src/chunk-rhcty4ms.js +4 -0
  111. package/dist/src/chunk-rpvckv3p.js +4 -0
  112. package/dist/src/{chunk-s1zq65ny.js → chunk-rqc9ap3x.js} +2 -2
  113. package/dist/src/chunk-rvvy1bte.js +7 -0
  114. package/dist/src/chunk-ry0mz41y.js +4 -0
  115. package/dist/src/{chunk-34zxqcmq.js → chunk-s3jc6cwp.js} +3 -3
  116. package/dist/src/chunk-sfv61xjb.js +6 -0
  117. package/dist/src/chunk-t6bfvfkg.js +4 -0
  118. package/dist/src/chunk-t6c8akcm.js +5 -0
  119. package/dist/src/chunk-t7w8rctc.js +4 -0
  120. package/dist/src/chunk-thbh3jew.js +5 -0
  121. package/dist/src/chunk-v2wd4n3e.js +5 -0
  122. package/dist/src/{chunk-zg67wjkh.js → chunk-v6pya0dm.js} +3 -3
  123. package/dist/src/chunk-vy3bwm1d.js +17 -0
  124. package/dist/src/chunk-w3vs79ex.js +27 -0
  125. package/dist/src/chunk-w4zga19f.js +4 -0
  126. package/dist/src/chunk-w5b2jwk3.js +14 -0
  127. package/dist/src/chunk-w7hkv422.js +5 -0
  128. package/dist/src/{chunk-qv2918jt.js → chunk-waqwcjyn.js} +2 -2
  129. package/dist/src/{chunk-b4cxdzwy.js → chunk-wbh8spm5.js} +7 -7
  130. package/dist/src/{chunk-ajsrz473.js → chunk-wyrkw6hw.js} +3 -3
  131. package/dist/src/chunk-xa73x2cx.js +5 -0
  132. package/dist/src/{chunk-eka89q3k.js → chunk-xgan0qqp.js} +1 -1
  133. package/dist/src/chunk-y2vs6jwg.js +4 -0
  134. package/dist/src/chunk-yattm52s.js +4 -0
  135. package/dist/src/{chunk-9y5bcy28.js → chunk-yb97j25b.js} +4 -4
  136. package/dist/src/{chunk-c9nk1r78.js → chunk-ybbtagbt.js} +3 -3
  137. package/dist/src/chunk-ycaj2sgf.js +9 -0
  138. package/dist/src/chunk-yf7ympyz.js +325 -0
  139. package/dist/src/chunk-yjawdzk8.js +4 -0
  140. package/dist/src/chunk-z637nqg8.js +11 -0
  141. package/dist/src/{chunk-0w1spptt.js → chunk-zgja5f3p.js} +4 -4
  142. package/dist/src/chunk-zs1sghs9.js +4 -0
  143. package/dist/src/daemon/processEntry.js +2 -2
  144. package/dist/src/index.js +4 -4
  145. package/dist/src/runtime/bunRuntimeGuard.js +2 -2
  146. package/dist/src/utils/workers/iosBundleExtractWorker.js +2 -2
  147. package/ios/CameraPosterInjection/Injection/CameraPosterInjection.m +466 -0
  148. package/package.json +3 -2
  149. package/schemas/test-plan.schema.json +370 -52
  150. package/dist/src/chunk-09442h7v.js +0 -4
  151. package/dist/src/chunk-0e4gm1kj.js +0 -5
  152. package/dist/src/chunk-0eb8576f.js +0 -4
  153. package/dist/src/chunk-0rjm4nx7.js +0 -5
  154. package/dist/src/chunk-1zmh2eak.js +0 -4
  155. package/dist/src/chunk-28k63ha3.js +0 -5
  156. package/dist/src/chunk-2d4wmp8b.js +0 -4
  157. package/dist/src/chunk-2e6s75qv.js +0 -78
  158. package/dist/src/chunk-32jpxz2y.js +0 -323
  159. package/dist/src/chunk-3jmvqygr.js +0 -238
  160. package/dist/src/chunk-3swvfhqt.js +0 -5
  161. package/dist/src/chunk-44bt78m9.js +0 -5
  162. package/dist/src/chunk-488vt2bp.js +0 -4
  163. package/dist/src/chunk-4fgdhcwx.js +0 -5
  164. package/dist/src/chunk-4jp2se4w.js +0 -4
  165. package/dist/src/chunk-4vhr9e4t.js +0 -4
  166. package/dist/src/chunk-4xd6zs3y.js +0 -15
  167. package/dist/src/chunk-51567pg8.js +0 -4
  168. package/dist/src/chunk-5agqsfev.js +0 -4
  169. package/dist/src/chunk-5d706gmr.js +0 -4
  170. package/dist/src/chunk-5kd4rqk7.js +0 -5
  171. package/dist/src/chunk-61t538bm.js +0 -4
  172. package/dist/src/chunk-6d3mmqxt.js +0 -54
  173. package/dist/src/chunk-6hyt398x.js +0 -5
  174. package/dist/src/chunk-6mnr8kpm.js +0 -4
  175. package/dist/src/chunk-79q5xsdc.js +0 -42
  176. package/dist/src/chunk-7bdgand3.js +0 -4
  177. package/dist/src/chunk-84cp9dw5.js +0 -4
  178. package/dist/src/chunk-86f2h5ww.js +0 -5
  179. package/dist/src/chunk-8cghfzb2.js +0 -19
  180. package/dist/src/chunk-9tf2p9y9.js +0 -5
  181. package/dist/src/chunk-a2sc4jm0.js +0 -5
  182. package/dist/src/chunk-aa5ng3d1.js +0 -4
  183. package/dist/src/chunk-b3h0a6ca.js +0 -4
  184. package/dist/src/chunk-b6a737qt.js +0 -4
  185. package/dist/src/chunk-bn64x5z5.js +0 -5
  186. package/dist/src/chunk-bvh649za.js +0 -12
  187. package/dist/src/chunk-c9tyfg4j.js +0 -26
  188. package/dist/src/chunk-cfsdfqnm.js +0 -4
  189. package/dist/src/chunk-cxyf04jc.js +0 -7
  190. package/dist/src/chunk-da08tx5v.js +0 -4
  191. package/dist/src/chunk-f2tc08m2.js +0 -4
  192. package/dist/src/chunk-f8tnk179.js +0 -5
  193. package/dist/src/chunk-f9s1kkhx.js +0 -5
  194. package/dist/src/chunk-fbp8a4rx.js +0 -4
  195. package/dist/src/chunk-fd2z0sze.js +0 -4
  196. package/dist/src/chunk-fk927f4q.js +0 -54
  197. package/dist/src/chunk-fxmr3a0f.js +0 -4
  198. package/dist/src/chunk-gaqwhd44.js +0 -4
  199. package/dist/src/chunk-ggtd5x5a.js +0 -5
  200. package/dist/src/chunk-gndcfrm7.js +0 -4
  201. package/dist/src/chunk-h3n4adc6.js +0 -310
  202. package/dist/src/chunk-h5pjbb83.js +0 -4
  203. package/dist/src/chunk-hgnnh302.js +0 -4
  204. package/dist/src/chunk-hmke8nhx.js +0 -5
  205. package/dist/src/chunk-j2bx12hn.js +0 -4
  206. package/dist/src/chunk-j70y1a2g.js +0 -4
  207. package/dist/src/chunk-js1jpyxp.js +0 -6
  208. package/dist/src/chunk-kbjq0qvb.js +0 -4
  209. package/dist/src/chunk-kfwx57xd.js +0 -4
  210. package/dist/src/chunk-kzwctna2.js +0 -4
  211. package/dist/src/chunk-m9ehyehy.js +0 -4
  212. package/dist/src/chunk-mct91p1f.js +0 -4
  213. package/dist/src/chunk-mf9spd4g.js +0 -5
  214. package/dist/src/chunk-mgrcansb.js +0 -4
  215. package/dist/src/chunk-mqc1d0eb.js +0 -6
  216. package/dist/src/chunk-n1zmpk0z.js +0 -4
  217. package/dist/src/chunk-ncyjf63g.js +0 -4
  218. package/dist/src/chunk-nr64d21q.js +0 -973
  219. package/dist/src/chunk-nwm2pq5v.js +0 -168
  220. package/dist/src/chunk-pm06b05d.js +0 -9
  221. package/dist/src/chunk-q50s00ry.js +0 -4
  222. package/dist/src/chunk-qwye1958.js +0 -14
  223. package/dist/src/chunk-r8z9pfve.js +0 -5
  224. package/dist/src/chunk-rdpgkjf6.js +0 -4
  225. package/dist/src/chunk-rkr0dks9.js +0 -5
  226. package/dist/src/chunk-rn84t3sf.js +0 -11
  227. package/dist/src/chunk-s123kn7n.js +0 -4
  228. package/dist/src/chunk-s73qn3fx.js +0 -4
  229. package/dist/src/chunk-sjn0nj4p.js +0 -5
  230. package/dist/src/chunk-sns0c35v.js +0 -7
  231. package/dist/src/chunk-t4dr5c8v.js +0 -8
  232. package/dist/src/chunk-tc8k9c1j.js +0 -4
  233. package/dist/src/chunk-teznhase.js +0 -4
  234. package/dist/src/chunk-ty9xgmz4.js +0 -4
  235. package/dist/src/chunk-tyazf3sk.js +0 -145
  236. package/dist/src/chunk-v6npjrjm.js +0 -5
  237. package/dist/src/chunk-vbvtzkwz.js +0 -5
  238. package/dist/src/chunk-vefd8qee.js +0 -8
  239. package/dist/src/chunk-vs9r72ta.js +0 -17
  240. package/dist/src/chunk-w88k0bgr.js +0 -4
  241. package/dist/src/chunk-wzdt0enk.js +0 -4
  242. package/dist/src/chunk-xhhv854c.js +0 -4
  243. package/dist/src/chunk-y58270hd.js +0 -4
  244. package/dist/src/chunk-yk0hmbex.js +0 -4
  245. package/dist/src/chunk-ympy4fdz.js +0 -28
  246. package/dist/src/chunk-yw436axy.js +0 -4
  247. package/dist/src/chunk-z04jrpjy.js +0 -5
  248. package/dist/src/chunk-z2xznczw.js +0 -4
  249. package/dist/src/chunk-z4xnrgps.js +0 -4
  250. package/dist/src/chunk-z7csyszv.js +0 -4
  251. package/dist/src/chunk-z8t0v07j.js +0 -11
  252. package/dist/src/chunk-z916af47.js +0 -228
  253. package/dist/src/chunk-zcr8he8q.js +0 -5
  254. package/dist/src/chunk-zd9vmkzn.js +0 -9
  255. package/dist/src/chunk-zj939v0h.js +0 -5
  256. package/dist/src/chunk-zkdjwtc1.js +0 -4
  257. package/dist/src/chunk-zkv5kzt6.js +0 -4
@@ -0,0 +1,27 @@
1
+ // @bun
2
+ import{i,Qe,di,fn,Q,No,Cd,Od,l,at,e}from"./chunk-z637nqg8.js";import{f,I,Be,Hu}from"./chunk-58wzr3mt.js";import{B,po,Sa}from"./chunk-yf7ympyz.js";import{je,Je,oo,re,U,yB,zr,ce,v,Rn,z,Rz,L,ys,rR,b,Yo,vt,De,tY,cne,sY,an,bi,ji,Jv,Mz,so,Pp,Ap,ld,zn,pk,Bz,wne,N,Qo,Ur,r$,BD,gj,x,Ce,E}from"./chunk-r5bxavdx.js";import{n,a,h,S,s,ts,_}from"./chunk-4hw3665t.js";import{C4,Yy,M4,qc}from"./chunk-hrfytkxh.js";import{dA,gS,yS}from"./chunk-v2wd4n3e.js";import{Uf,F1,XE,L1}from"./chunk-g7tmjbkt.js";import{gn,c5,iee,j0,lV,Ci,HA,K0,b_,oee,jc,fm}from"./chunk-284ptqa4.js";import{gu}from"./chunk-0qsy1qsx.js";import{jL}from"./chunk-v6pya0dm.js";import{Id}from"./chunk-6wt9rgjf.js";import{r,t,u}from"./chunk-rbpdk7m0.js";import{Nt}from"./chunk-qcxzr2my.js";var On={invalid_type:"invalid_type",too_big:"too_big",too_small:"too_small",invalid_format:"invalid_format",not_multiple_of:"not_multiple_of",unrecognized_keys:"unrecognized_keys",invalid_union:"invalid_union",invalid_key:"invalid_key",invalid_element:"invalid_element",invalid_value:"invalid_value",custom:"custom"};function Oe(o){return o==="ios"||o==="tvos"}function xG(o){return o==="android"||Oe(o)}function _M(o,d){if(xG(o))return o;throw new t(`${d} is not supported on ${Od[o]} yet. Target an Android, iOS or Apple TV device instead.`)}import{promises as zo}from"fs";var jo=new Set(["moov","trak","mdia","minf","stbl"]),Ko={avc1:"h264",avc3:"h264",hvc1:"hevc",hev1:"hevc"},we=8,We=16,Wo=8;function qe(o,d,c,p){if(c<we)return;let m=o.readUInt32BE(d),g=o.toString("latin1",d+4,d+8),w=we;if(m===1){if(c<We)return;let R=o.readUInt32BE(d+8),y=o.readUInt32BE(d+12);m=R*4294967296+y,w=We}else if(m===0)m=p;if(m<w)return;return{size:m,type:g,headerSize:w}}function vr(o,d,c){let p=d;while(p+we<=c){let m=qe(o,p,c-p,c-p);if(!m||p+m.size>c)return;let g=p+m.headerSize,w=p+m.size;if(jo.has(m.type)){let R=vr(o,g,w);if(R)return R}else if(m.type==="stsd"){let R=qo(o,g,w);if(R)return R}p+=m.size}return}function qo(o,d,c){let p=d+Wo;while(p+we<=c){let m=qe(o,p,c-p,c-p);if(!m||p+m.size>c)return;let g=Ko[m.type.toLowerCase()];if(g)return g;p+=m.size}return}async function br(o){let d=await zo.open(o,"r");try{let{size:c}=await d.stat(),p=Buffer.alloc(We),m=0;while(m+we<=c){let{bytesRead:g}=await d.read(p,0,We,m),w=qe(p,0,g,c-m);if(!w)return;if(w.type==="moov"){let R=Math.min(w.size,c-m),y=Buffer.alloc(R);return await d.read(y,0,R,m),{moov:y,headerSize:w.headerSize}}m+=w.size}return}finally{await d.close()}}async function Jo(o){let d=await br(o);return d&&vr(d.moov,d.headerSize,d.moov.length)}var Xo=4;function Zo(o,d,c){let p=o[d],m=p===1?16:8,g=d+Xo+m,w=g+4;if(w+(p===1?8:4)>c)return;let y=o.readUInt32BE(g),P=p===1?o.readUInt32BE(w)*4294967296+o.readUInt32BE(w+4):o.readUInt32BE(w);if(y===0)return;return Math.round(P/y*1000)}function Rr(o,d,c){let p=d;while(p+we<=c){let m=qe(o,p,c-p,c-p);if(!m||p+m.size>c)return;let g=p+m.headerSize,w=p+m.size;if(m.type==="moov"){let R=Rr(o,g,w);if(R!==void 0)return R}else if(m.type==="mvhd")return Zo(o,g,w);p+=m.size}return}async function en(o){let d=await br(o);return d&&Rr(d.moov,d.headerSize,d.moov.length)}function ye(o,d){return o.durationMs?.(d)??Promise.resolve(void 0)}var ae={async codec(o){try{return await Jo(o)}catch(d){e.warn(`[RecordingCodec] Failed to probe codec for ${o}: ${d}`,d);return}},async durationMs(o){try{return await en(o)}catch(d){e.warn(`[RecordingCodec] Failed to probe duration for ${o}: ${d}`,d);return}}};import{promises as Po}from"fs";import pr from"path";import{promises as Sr}from"fs";import ve from"path";class J extends t{retainOwnership;retainedCapturePath;constructor(o,d){super(o,d);this.name="VideoCaptureFinalizationError",this.retainOwnership=d?.retainOwnership??!1,this.retainedCapturePath=d?.retainedCapturePath}}class X extends Error{handle;constructor(o,d,c){super(o,c);this.handle=d;this.name="VideoCaptureStartCleanupError"}}function rn(o){let d={qualityPreset:o.qualityPreset,targetBitrateKbps:o.targetBitrateKbps,maxThroughputMbps:o.maxThroughputMbps,fps:o.fps,maxArchiveSizeMb:o.maxArchiveSizeMb,format:o.format};if(o.resolution)d.resolution=o.resolution;return d}function on(o,d){return o.transitions??(d?[]:void 0)}function nn(o,d){if(o.physicalDisplayId=d.warning?void 0:d.physicalDisplayId??o.physicalDisplayId,o.warning=d.warning,o.recordedPanel=d.recordedPanel??o.recordedPanel,d.warning?.includes("rejected --display-id"))o.recordedPanel=void 0}function sn(o,d){let c=[...o.warning?[o.warning]:[],...d?.warnings??[]];return c.length>0?c:void 0}class Xe{backend;archiveRoot;log;idGenerator;now;securePermissions;fileSystem;activeRecordings=new Map;stoppingRecordings=new Map;forceStoppingRecordings=new Map;constructor(o){this.backend=o.backend,this.archiveRoot=o.archiveRoot??Qe(fn.VIDEO_ARCHIVE),this.log=o.logger??e,this.idGenerator=dn(o.idGenerator),this.now=o.now??(()=>new Date),this.securePermissions=o.securePermissions??wne,this.fileSystem=o.fileSystem??Sr}async startRecording(o={}){let d=Uf(o.config),c=this.idGenerator.next(),p=this.now().toISOString(),m=un(o.outputName),g=m?`${m}-${c}`:c,w=this.getRecordingDir(g);await this.securePermissions.ensureSecureDir(w);let R=ln(g,p,d.format),y=ve.join(w,R),P=new AbortController,A={recordingId:c,outputDirectory:w,outputPath:y,fileName:R,startedAt:p,device:o.device,maxDurationSeconds:o.maxDurationSeconds,abortSignal:je(o.abortSignal,P.signal),display:o.display,recordingPanel:o.recordingPanel,physicalDisplayId:o.physicalDisplayId,audio:o.audio,...d},k=Promise.resolve().then(async()=>await this.backend.start(A)),C={recordingId:c,outputPath:y,fileName:R,startedAt:p,config:d,outputName:o.outputName,recordedPanel:o.recordingPanel,physicalDisplayId:o.physicalDisplayId,deviceId:o.device?.deviceId,startPromise:k,startAbortController:P,forceStopRequested:!1};this.activeRecordings.set(c,C);try{let T=await k;if(C.handle=T,C.outputPath=T.outputPath||y,C.fileName=ve.basename(C.outputPath),C.liveCapturePath=T.liveCapturePath,C.inProgressSidecarPaths=T.inProgressSidecarPaths,C.startedAt=T.startedAt||p,C.config=rn(T.effectiveConfig??d),nn(C,T),C.forceStopRequested||this.activeRecordings.get(c)!==C)throw Error(`Recording ${c} was force-stopped while it was starting.`);return{recordingId:c,outputPath:C.outputPath,fileName:C.fileName,startedAt:C.startedAt,config:C.config,outputName:C.outputName,recordedPanel:C.recordedPanel,physicalDisplayId:C.physicalDisplayId,warning:C.warning,liveCapturePath:C.liveCapturePath}}catch(T){return await this.handleStartFailure(T,C)}}listActiveRecordingIds(){return Array.from(this.activeRecordings.keys())}getCaptureTeardown(o){let d=this.activeRecordings.get(o)?.handle;return d?this.backend.describeCaptureTeardown?.(d):void 0}getInProgressFiles(o){let d=this.activeRecordings.get(o);return d?[d.liveCapturePath??d.outputPath,...d.inProgressSidecarPaths??[]]:[]}hasActiveRecordingForDevice(o){return Array.from(this.activeRecordings.values()).some((d)=>d.deviceId===o)}async stopRecording(o){let d=this.stoppingRecordings.get(o);if(d)return d;let c=this.activeRecordings.get(o);if(!c)throw Error(`No active recording found for id ${o}`);let p=this.stopActiveRecording(c);this.stoppingRecordings.set(o,p);try{return await p}finally{this.stoppingRecordings.delete(o)}}async stopActiveRecording(o){let d=o.recordingId,c=o.handle??await o.startPromise,p;try{p=await this.backend.stop(c)}catch(k){throw await this.handleStopFailure(o,k)}this.assertGracefulStopOwnership(o);let m=p.endedAt??this.now().toISOString(),g=p.outputPath||o.outputPath,w=ve.basename(g);await this.securePermissions.secureFile(g);let R=await this.safeStat(g);this.assertGracefulStopOwnership(o);let y=p.sizeBytes??R?.size??0,P=p.durationMs??mn(o.startedAt,m),A={recordingId:o.recordingId,fileName:w,filePath:g,format:cn(g,o.config.format),sizeBytes:y,durationMs:P,videoDurationMs:p.videoDurationMs,codec:p.codec,outputName:o.outputName,createdAt:o.startedAt,startedAt:o.startedAt,endedAt:m,lastAccessedAt:m,config:o.config,recordedPanel:p.recordedPanel??o.recordedPanel,transitions:on(p,o.recordedPanel),warnings:sn(o,p),...p.hasAudioTrack===void 0?{}:{hasAudioTrack:p.hasAudioTrack}};return this.activeRecordings.delete(d),A}assertGracefulStopOwnership(o){if(this.activeRecordings.get(o.recordingId)!==o||o.forceStopRequested)throw Error(`Recording ${o.recordingId} was force-stopped while it was stopping.`)}async handleStopFailure(o,d){let c=o.recordingId;if(d instanceof Ap)return u(d,`Recording ${c} could not be confirmed stopped; the capture process may still be running on the device`);if(d instanceof J){if(d.retainOwnership)return new J(`Recording ${c} ownership retained; stop again to finalize its device artifact: ${d.message}`,{cause:d,retainOwnership:!0});this.activeRecordings.delete(c);try{if(d.retainedCapturePath)await this.fileSystem.rm(o.outputPath,{force:!0});else await this.removeRecordingArtifacts(c,o.outputPath)}catch(p){this.log.warn(`[VideoRecorderService] Failed to remove unusable recording artifacts for ${c}: ${r(p)}`,p)}return d}return d}async forceStopRecording(o,d){await this.forceStopOrDiscardRecording(o,!1,d)}async discardRecording(o,d){await this.forceStopOrDiscardRecording(o,!0,d)}async forceStopOrDiscardRecording(o,d,c){let p=this.activeRecordings.get(o)?.outputPath,m=this.forceStoppingRecordings.get(o);if(m){if(await m,d)await this.removeRecordingArtifacts(o,p);return}let g=this.activeRecordings.get(o);if(!g)throw Error(`No active recording found for id ${o}`);let w=this.backend.forceStop;if(!w&&g.handle)throw Error("Video capture backend does not support force stopping recordings.");g.forceStopRequested=!0;let R=this.forceStopActiveRecording(g,w,d,c);this.forceStoppingRecordings.set(o,R);try{await R}finally{this.forceStoppingRecordings.delete(o)}}async forceStopActiveRecording(o,d,c,p){o.startAbortController.abort();let m=await this.resolveForceStopHandle(o,c);if(!m)return;if(!d)throw o.forceStopRequested=!1,Error("Video capture backend does not support force stopping recordings.");try{if(await d.call(this.backend,m,p),this.activeRecordings.delete(o.recordingId),c)await this.removeRecordingArtifacts(o.recordingId,o.outputPath)}catch(g){if(this.activeRecordings.get(o.recordingId)===o)o.forceStopRequested=!1;throw g}}retainStartCleanupHandle(o,d){if(!(o instanceof X))return!1;return d.handle=o.handle,!0}async handleStartFailure(o,d){if(this.retainStartCleanupHandle(o,d)){try{if(!d.forceStopRequested)await this.forceStopOrDiscardRecording(d.recordingId,!0)}catch(c){this.log.warn(`[VideoRecorderService] Failed to retry cleanup for ${d.recordingId}: ${String(c)}`,c)}throw o}if(this.activeRecordings.get(d.recordingId)===d&&!d.forceStopRequested)this.activeRecordings.delete(d.recordingId);if(!d.forceStopRequested)try{await this.removeRecordingArtifacts(d.recordingId,d.outputPath)}catch(c){this.log.warn(`[VideoRecorderService] Failed to remove aborted recording directory for ${d.recordingId}: ${String(c)}`)}throw o}async resolveForceStopHandle(o,d){try{return o.handle??await o.startPromise}catch(c){if(this.retainStartCleanupHandle(c,o))return c.handle;if(this.log.debug(`[VideoRecorderService] Starting recording ${o.recordingId} ended during force stop: ${c}`),this.activeRecordings.delete(o.recordingId),d)await this.removeRecordingArtifacts(o.recordingId,o.outputPath);return}}async removeRecordingArtifacts(o,d){let c=this.activeRecordings.get(o),p=d??c?.outputPath;if(!p)return;await this.fileSystem.rm(ve.dirname(p),{recursive:!0,force:!0})}getRecordingDir(o){return ve.join(this.archiveRoot,o)}async safeStat(o){try{return await Sr.stat(o)}catch(d){return this.log.warn(`[VideoRecorderService] Missing recording file at ${o}: ${r(d)}`,d),null}}}var dn=(o)=>{if(o===void 0)return B;if(typeof o==="function")return{next:o};return o};function cn(o,d){let c=ve.extname(o).slice(1).toLowerCase();return c==="mp4"||c==="mov"?c:d}function ln(o,d,c){let p=pn(d);return`${o}-${p}.${c}`}function un(o){if(!o)return"";return o.replace(/[^A-Za-z0-9._-]+/g,"-").slice(0,64).replace(/^-+|-+$/g,"")}function pn(o){return o.replace(/[-:]/g,"").replace(/\.\d{3}Z$/,"Z")}function mn(o,d){let c=Date.parse(o),p=Date.parse(d);if(Number.isNaN(c)||Number.isNaN(p))return;return Math.max(0,p-c)}import{createWriteStream as cs}from"fs";import{rm as ls}from"fs/promises";import{constants as hi}from"fs";import{open as Aa,stat as Ea}from"fs/promises";var Pr=-120,CG=-50,Er=1,hn=3,wn=65534;function se(o){return new t(`Unsupported audio file: ${o}. Provide an uncompressed PCM WAV file (8/16/24/32-bit integer or 32-bit float).`)}function wt(o,d){return String.fromCharCode(o.getUint8(d),o.getUint8(d+1),o.getUint8(d+2),o.getUint8(d+3))}function yn(o,d,c){if(c<16)throw se("fmt chunk is too short");let p=o.getUint16(d,!0);if(p===wn&&c>=26)p=o.getUint16(d+24,!0);return{formatTag:p,channels:o.getUint16(d+2,!0),sampleRate:o.getUint32(d+4,!0),bitsPerSample:o.getUint16(d+14,!0)}}function vn(o){let{formatTag:d,bitsPerSample:c}=o;if(d===hn&&c===32)return(p,m)=>Math.round(Math.max(-1,Math.min(1,p.getFloat32(m,!0)))*32767);if(d!==Er)throw se(`format tag ${d} is compressed or unknown`);switch(c){case 8:return(p,m)=>p.getUint8(m)-128<<8;case 16:return(p,m)=>p.getInt16(m,!0);case 24:return(p,m)=>p.getInt16(m+1,!0);case 32:return(p,m)=>p.getInt32(m,!0)>>16;default:throw se(`${c}-bit samples are not supported`)}}function bn(o,d,c,p){let m=p.bitsPerSample/8,g=Math.floor(Math.min(c,o.byteLength-d)/m),w=vn(p),R=new Int16Array(g);for(let y=0;y<g;y++)R[y]=w(o,d+y*m);return R}function Cr(o){let d=new DataView(o.buffer,o.byteOffset,o.byteLength);if(o.byteLength<12||wt(d,0)!=="RIFF"||wt(d,8)!=="WAVE")throw se("missing RIFF/WAVE header");let c,p=12;while(p+8<=o.byteLength){let m=wt(d,p),g=d.getUint32(p+4,!0),w=p+8;if(m==="fmt ")c=yn(d,w,g);else if(m==="data"){if(!c)throw se("data chunk precedes the fmt chunk");if(c.channels<1||c.sampleRate<1)throw se("fmt chunk declares no channels or no sample rate");let R=bn(d,w,g,c);return{sampleRate:c.sampleRate,channels:c.channels,samples:R}}p=w+g+g%2}throw se("no data chunk")}function G8(o){let d=o.samples.length*2,c=new Uint8Array(44+d),p=new DataView(c.buffer),m=(g,w)=>{for(let R=0;R<w.length;R++)p.setUint8(g+R,w.charCodeAt(R))};m(0,"RIFF"),p.setUint32(4,36+d,!0),m(8,"WAVE"),m(12,"fmt "),p.setUint32(16,16,!0),p.setUint16(20,Er,!0),p.setUint16(22,o.channels,!0),p.setUint32(24,o.sampleRate,!0),p.setUint32(28,o.sampleRate*o.channels*2,!0),p.setUint16(32,o.channels*2,!0),p.setUint16(34,16,!0),m(36,"data"),p.setUint32(40,d,!0);for(let g=0;g<o.samples.length;g++)p.setInt16(44+g*2,o.samples[g],!0);return c}function Ye(o){if(o.channels===1)return o;let d=Math.floor(o.samples.length/o.channels),c=new Int16Array(d);for(let p=0;p<d;p++){let m=0;for(let g=0;g<o.channels;g++)m+=o.samples[p*o.channels+g];c[p]=Math.round(m/o.channels)}return{sampleRate:o.sampleRate,channels:1,samples:c}}function Ze(o,d){if(o.channels!==1)throw Error("resampleMono expects mono PCM");if(o.sampleRate===d||o.samples.length===0)return{...o,sampleRate:d};let c=o.sampleRate/d,p=Math.max(1,Math.floor(o.samples.length/c)),m=new Int16Array(p),g=o.samples.length-1;for(let w=0;w<p;w++){let R=w*c,y=Math.min(Math.floor(R),g),P=Math.min(y+1,g),A=R-y;m[w]=Math.round(o.samples[y]*(1-A)+o.samples[P]*A)}return{sampleRate:d,channels:1,samples:m}}function xr(o){return Math.max(-32768,Math.min(32767,Math.round(o)))}function kr(o,d){if(d===0)return o;let c=10**(d/20);return{...o,samples:o.samples.map((p)=>xr(p*c))}}function $e(o){let d=o.samples.length/o.channels;return Math.round(d/o.sampleRate*1000)}function yt(o){if(o<=0)return Pr;return Math.max(Pr,Math.round(20*Math.log10(o/32768)*10)/10)}function Ar(o,d,c){if(c<=d)return 0;let p=0;for(let m=d;m<c;m++)p+=o[m]*o[m];return Math.sqrt(p/(c-d))}function Sn(o){let d=0;for(let c of o)d=Math.max(d,Math.abs(c));return d}function Tr(o,d={}){let c=d.silenceThresholdDbfs??CG,p=Math.max(o.channels,Math.round((d.windowMs??20)/1000*o.sampleRate)*o.channels),{samples:m}=o,g=0,w=0;for(let R=0;R<m.length;R+=p)if(g++,yt(Ar(m,R,Math.min(R+p,m.length)))>=c)w++;return{durationMs:$e(o),rmsDbfs:yt(Ar(m,0,m.length)),peakDbfs:yt(Sn(m)),activeRatio:g===0?0:Math.round(w/g*1000)/1000,silent:w===0}}function Mr(o){let d=o.sampleRate??48000,c=o.frequencyHz??1000,p=32767*10**((o.amplitudeDbfs??-6)/20),m=Math.round(o.durationMs/1000*d),g=new Int16Array(m);for(let w=0;w<m;w++)g[w]=xr(p*Math.sin(2*Math.PI*c*w/d));return{sampleRate:d,channels:1,samples:g}}function et(o){if(!Number.isSafeInteger(o)||o<0)throw RangeError(`varint must be a non-negative safe integer, got ${o}`);let d=[],c=o;while(c>=128)d.push(c%128|128),c=Math.floor(c/128);return d.push(c),d}class tt{chunks=[];varint(o,d){if(d!==0)this.chunks.push(Uint8Array.from([...et(o*8+0),...et(d)]));return this}bytes(o,d){if(d.byteLength>0){let c=[...et(o*8+2),...et(d.byteLength)];this.chunks.push(Uint8Array.from(c),d)}return this}finish(){let o=new Uint8Array(this.chunks.reduce((c,p)=>c+p.byteLength,0)),d=0;for(let c of this.chunks)o.set(c,d),d+=c.byteLength;return o}}function Rt(o){let d=0,c=1;for(;;){if(o.offset>=o.bytes.byteLength)throw RangeError("truncated varint");let p=o.bytes[o.offset++];if(d+=(p&127)*c,p<128)return d;c*=128}}function bt(o,d){if(o.offset+d>o.bytes.byteLength)throw RangeError("truncated field");let c=o.bytes.subarray(o.offset,o.offset+d);return o.offset+=d,c}function Pn(o,d){switch(d){case 0:return{kind:"varint",value:Rt(o)};case 2:return{kind:"bytes",value:bt(o,Rt(o))};case 1:bt(o,8);return;case 5:bt(o,4);return;default:throw RangeError(`unsupported wire type ${d}`)}}function St(o){let d={bytes:o,offset:0},c=[];while(d.offset<o.byteLength){let p=Rt(d),m=Pn(d,p%8);if(m)c.push({field:Math.floor(p/8),value:m})}return c}var de=48000,oe={samplingRate:1,channels:2,format:3,mode:4},be={format:1,timestamp:2,audio:3};function rt(o){return new tt().varint(oe.samplingRate,o.samplingRate).varint(oe.channels,o.channels==="stereo"?1:0).varint(oe.format,o.format==="s16"?1:0).varint(oe.mode,o.mode==="realtime"?1:0).finish()}function Ir(o){let d=new tt;if(o.format)d.bytes(be.format,rt(o.format));return d.varint(be.timestamp,o.timestampUs??0).bytes(be.audio,o.audio).finish()}function An(o){let d={samplingRate:44100,channels:"mono",format:"u8",mode:"unspecified"};for(let{field:c,value:p}of St(o)){if(p.kind!=="varint")continue;if(c===oe.samplingRate)d.samplingRate=p.value;else if(c===oe.channels)d.channels=p.value===1?"stereo":"mono";else if(c===oe.format)d.format=p.value===1?"s16":"u8";else if(c===oe.mode)d.mode=p.value===1?"realtime":"unspecified"}return d}function Pt(o){let d={audio:new Uint8Array(0)};for(let{field:c,value:p}of St(o))if(c===be.format&&p.kind==="bytes")d.format=An(p.value);else if(c===be.timestamp&&p.kind==="varint")d.timestampUs=p.value;else if(c===be.audio&&p.kind==="bytes")d.audio=p.value;return d}var le=5;function Fr(o){let d=new Uint8Array(le+o.byteLength);return new DataView(d.buffer).setUint32(1,o.byteLength,!1),d.set(o,le),d}class At{maxMessageBytes;buffer=new Uint8Array(0);constructor(o=16777216){this.maxMessageBytes=o}push(o){this.buffer=En(this.buffer,o);let d=[];while(this.buffer.byteLength>=le){let c=new DataView(this.buffer.buffer,this.buffer.byteOffset,this.buffer.byteLength);if(c.getUint8(0)!==0)throw Error("received a compressed gRPC message; compression was not negotiated");let p=c.getUint32(1,!1);if(p>this.maxMessageBytes)throw Error(`gRPC message of ${p} bytes exceeds the ${this.maxMessageBytes}-byte limit`);if(this.buffer.byteLength<le+p)break;d.push(this.buffer.slice(le,le+p)),this.buffer=this.buffer.subarray(le+p)}return d}get pendingBytes(){return this.buffer.byteLength}}function En(o,d){if(o.byteLength===0)return d;let c=new Uint8Array(o.byteLength+d.byteLength);return c.set(o,0),c.set(d,o.byteLength),c}var V={OK:0,CANCELLED:1,UNKNOWN:2,INVALID_ARGUMENT:3,DEADLINE_EXCEEDED:4,NOT_FOUND:5,PERMISSION_DENIED:7,FAILED_PRECONDITION:9,UNIMPLEMENTED:12,UNAVAILABLE:14,UNAUTHENTICATED:16};function Cn(o){try{return decodeURIComponent(o)}catch(d){return e.debug(`grpc-message was not valid percent-encoding: ${r(d)}`),o}}function Dr(o){if(o===void 0)return;return Array.isArray(o)?o[0]:String(o)}function Et(...o){for(let d of o){let c=Dr(d?.["grpc-status"]);if(c!==void 0){let p=Number.parseInt(c,10);return{code:Number.isNaN(p)?V.UNKNOWN:p,message:Cn(Dr(d?.["grpc-message"])??"")}}}return}class D extends t{code;grpcStatus;constructor(o,d,c,p){super(`[${o}] ${d}`);this.code=o;this.grpcStatus=c;this.retryable=p?.retryable,this.remedy=p?.remedy}retryable;remedy}var it="Grant macOS Microphone permission to the host app that runs the emulator in System Settings > Privacy and Security > Microphone, or restart the emulator.";function Vr(o){return new D("audio_host_microphone_unavailable",`Emulator ${o} reported a host microphone voice failure. ${it}`,void 0,{retryable:!1,remedy:it})}function Or(o){return new D("audio_repeat_injection_unsafe",`An injection has already been started on emulator ${o} in this boot; another may crash it. Stop any earlier injection before restarting. ${it}`,void 0,{retryable:!1,remedy:it})}var _r="Relaunch the emulator with -grpc-use-token, or with -grpc <port> and no JWT, then retry.";function $r(o){return o.code===V.UNAUTHENTICATED||o.code===V.PERMISSION_DENIED||o.code===V.INVALID_ARGUMENT&&/\bJWT\b/i.test(o.message)}function Hr(o,d){return new D("audio_grpc_auth_rejected",`The emulator refused AutoMobile's gRPC credentials for audio ${o} (gRPC status ${d.code}: ${d.message||"no detail"}). ${_r}`,d,{retryable:!1,remedy:_r})}function Ct(o){if($r(o))return Hr("injection",o);switch(o.code){case V.FAILED_PRECONDITION:return new D("audio_injection_busy","Another audio injection is already active on this emulator. Stop it before starting a new one.",o);case V.UNIMPLEMENTED:case V.UNAVAILABLE:return new D("audio_injection_unsupported",`The emulator's gRPC audio injection is unavailable (${o.message||"no detail"}). Use an emulator build with gRPC enabled and do not launch it with -no-audio.`,o);default:return new D("audio_grpc_failed",`Emulator audio injection failed with gRPC status ${o.code}: ${o.message||"no detail"}`,o)}}function xt(o){if($r(o))return Hr("capture",o);let d=o.code===V.UNIMPLEMENTED||o.code===V.UNAVAILABLE;return new D(d?"audio_capture_unsupported":"audio_grpc_failed",`Emulator audio capture failed with gRPC status ${o.code}: ${o.message||"no detail"}`,o)}function Br(o){return new D("audio_emulator_exited",`Emulator ${o} exited during audio injection. Restart the emulator and start a new injection.`)}class kt{sampleRate;nowUs;nextUs;constructor(o,d){this.sampleRate=o;this.nowUs=d;this.nextUs=d()}before(o){let d=this.nowUs(),c=o.timestampUs??0,m=c>0&&(Math.abs(c-d)<1e7||Math.abs(c-this.nextUs)<1e7)?c:d,g=Math.min(m-this.nextUs,300000000);if(this.nextUs=Math.max(this.nextUs,m)+o.audio.byteLength/2/this.sampleRate*1e6,g<50000)return;return new Uint8Array(Math.round(g*this.sampleRate/1e6)*2)}}var Nr="/android.emulation.control.EmulatorController",xn=`${Nr}/injectAudio`,Lr=`${Nr}/streamAudio`,kn=20,Tn=200,Mn=2000;function In(o){let d=Ye(o);return d.sampleRate>de?Ze(d,de):d}function Dn(o){let d=new Uint8Array(o.length*2),c=new DataView(d.buffer);for(let p=0;p<o.length;p++)c.setInt16(p*2,o[p],!0);return d}function Fn(o){let d=new DataView(o.buffer,o.byteOffset,o.byteLength),c=new Int16Array(Math.floor(o.byteLength/2));for(let p=0;p<c.length;p++)c[p]=d.getInt16(p*2,!0);return c}function _n(o){let d,c,p=new Promise((m)=>{c=m});return{get endReason(){return d},ended:p,end(m){if(d)return;d=m,o.cancel(),c()}}}class Tt{transport;authorizer;timer;constructor(o,d,c=i){this.transport=o;this.authorizer=d;this.timer=c}async injectAudio(o,d={}){let c=In(o),p=await this.open(xn,d.beforeStreamOpen),m,g=new Promise((y)=>{m=this.timer.setTimeout(()=>y(void 0),Tn)});try{let y=await l([p.status(),p.responseHeaders(),g],{timer:this.timer,label:"Audio injection acceptance"});if(y&&y.code!==V.OK&&y.code!==V.CANCELLED)throw Ct(y)}finally{this.timer.clearTimeout(m)}let w=_n(p),R=this.streamInjection(p,c,d,w);return R.catch((y)=>e.debug(`audio injection ended with ${r(y)}`)),{durationMs:$e(c),sampleRate:c.sampleRate,done:R,stop:()=>(w.end("stopped"),R)}}async captureAudio(o){let d=Math.min(o.sampleRate??de,de),c={samplingRate:d,channels:"mono",format:"s16"},p=await this.open(Lr);await p.write(rt(c)),p.end();let m=Math.round(o.durationMs/1000*d),g=!1,w=this.timer.setTimeout(()=>{g=!0,p.cancel()},o.durationMs+Mn),R=[],y=0;try{for await(let C of p.messages()){let T=Fn(Pt(C).audio);if(R.push(T),y+=T.length,y>=m){p.cancel();break}}}finally{this.timer.clearTimeout(w)}let P=await p.status();if(y===0&&!g&&P.code!==V.OK&&P.code!==V.CANCELLED)throw xt(P);let A=new Int16Array(Math.min(y,m)),k=0;for(let C of R){let T=C.subarray(0,A.length-k);A.set(T,k),k+=T.length}return{pcm:{sampleRate:d,channels:1,samples:A},timedOut:g&&y<m}}async streamCapture(o,d={}){let c=Math.min(d.sampleRate??de,de),p=await this.open(Lr);await p.write(rt({samplingRate:c,channels:"mono",format:"s16"})),p.end();let m=new kt(c,()=>this.timer.now()*1000),g=(async()=>{try{for await(let y of p.messages()){let P=Pt(y),A=m.before(P);if(A)await o(A);await o(P.audio)}}catch(y){e.debug(`audio stream ended: ${r(y)}`)}})(),w,R=async()=>{p.cancel(),await g;let y=await p.status();if(y.code!==V.OK&&y.code!==V.CANCELLED)throw xt(y)};return{sampleRate:c,stop:()=>(w??=R(),w)}}async close(){this.transport.close(),await this.authorizer.dispose()}async open(o,d){let c=await this.authorizer.authorization(o);return d?.(),this.transport.open(o,c?{authorization:c}:{})}async streamInjection(o,d,c,p){let m={call:o,pcm:d,options:c,control:p,framesQueued:0},g=c.maxDurationMs===void 0?void 0:this.timer.setTimeout(()=>p.end("max_duration"),c.maxDurationMs);try{if(c.startDelayMs)await this.delayUnlessEnded(c.startDelayMs,p);await this.sendAll(m)}finally{if(g)this.timer.clearTimeout(g)}o.end();let w=await o.status();if(w.code!==V.OK&&w.code!==V.CANCELLED)throw Ct(w);return{framesQueued:m.framesQueued,framesPlayed:this.framesPlayed(m),reason:p.endReason??"completed"}}async sendAll(o){try{let d=!1;do d=await this.sendPass(o);while(d&&o.options.loop&&o.pcm.samples.length>0)}catch(d){e.debug(`audio injection write ended: ${r(d)}`)}}delayUnlessEnded(o,d){return new Promise((c)=>{let p=this.timer.setTimeout(c,o);d.ended.then(()=>{this.timer.clearTimeout(p),c()})})}playbackNowMs(){return this.timer.monotonicNow?.()??this.timer.now()}framesPlayed(o){if(o.playbackStartMs===void 0)return 0;let d=Math.max(0,this.playbackNowMs()-o.playbackStartMs),c=Math.floor(d*o.pcm.sampleRate/1000);return Math.min(o.framesQueued,c)}async sendPass(o){let{pcm:d}=o,c={samplingRate:d.sampleRate,channels:"mono",format:"s16"},p=Math.max(1,Math.round(kn/1000*d.sampleRate));for(let m=0;m<d.samples.length;m+=p){if(o.control.endReason)return!1;let g=Dn(d.samples.subarray(m,m+p));o.playbackStartMs??=this.playbackNowMs(),await o.call.write(Ir({format:o.framesQueued===0?c:void 0,timestampUs:this.timer.now()*1000,audio:g})),o.framesQueued+=g.byteLength/2}return!0}}import{generateKeyPairSync as Vn,sign as $n}from"crypto";import Hn from"path";var Bn="android-studio",Ln=900,Gr=10,jr=100;function Mt(o){return Buffer.from(o).toString("base64url")}class Kr{jwksDir;activeJwksFile;fs;registration;keyFile;ids;timer;constructor(o,d,c,{ids:p=B,timer:m=i}={}){this.jwksDir=o;this.activeJwksFile=d;this.fs=c;this.ids=p,this.timer=m}async authorization(o){this.registration??=this.register();let{kid:d,privateKey:c}=await this.registration,p=Math.floor(this.timer.now()/1000),m=Mt(JSON.stringify({alg:"ES512",kid:d})),g=Mt(JSON.stringify({iss:Bn,aud:[o],iat:p,exp:p+Ln})),w=$n("sha512",Buffer.from(`${m}.${g}`),{key:c,dsaEncoding:"ieee-p1363"});return`Bearer ${m}.${g}.${Mt(w)}`}async dispose(){let o=this.keyFile;if(this.keyFile=void 0,this.registration=void 0,!o)return;try{await this.fs.unlink(o)}catch(d){e.debug(`emulator JWK ${o} already gone: ${r(d)}`)}}async register(){let{publicKey:o,privateKey:d}=Vn("ec",{namedCurve:"P-521"}),c=this.ids.next(),p={...o.export({format:"jwk"}),use:"sig",alg:"ES512",key_ops:["verify"],kid:c};this.keyFile=Hn.join(this.jwksDir,`${c}.jwk`),await this.fs.writeFile(this.keyFile,JSON.stringify({keys:[p]}));for(let m=0;m<Gr;m++){if(await this.activeSetHas(c))return{kid:c,privateKey:d};await this.timer.sleep(jr)}throw await this.dispose(),new t(`The emulator did not load AutoMobile's gRPC key from ${this.jwksDir} within ${Gr*jr}ms. Restart the emulator, or launch it with -grpc-use-token.`)}async activeSetHas(o){try{return JSON.parse(await this.fs.readFile(this.activeJwksFile)).keys?.some((c)=>c.kid===o)??!1}catch(d){return e.debug(`emulator active JWKS not readable yet: ${r(d)}`),!1}}}function Wr(o,d){switch(o.kind){case"token":return{authorization:async()=>`Bearer ${o.token}`,dispose:async()=>{}};case"jwt":return new Kr(o.jwksDir,o.activeJwksFile,d);case"none":return{authorization:async()=>{return},dispose:async()=>{}}}}import Nn from"http2";import qr from"http2";class It{flow;highWater;lowWater;messages=[];paused=!1;constructor(o,d=64,c=16){this.flow=o;this.highWater=d;this.lowWater=c}push(o){if(this.messages.push(...o),!this.paused&&this.messages.length>=this.highWater)this.paused=!0,this.flow.pause()}shift(){let o=this.messages.shift();if(this.paused&&this.messages.length<=this.lowWater)this.paused=!1,this.flow.resume();return o}}class Dt{stream;decoder=new At;queue;wake;finished=!1;headers;finalStatus;initialStatus;constructor(o){this.stream=o;this.queue=new It({pause:()=>o.pause(),resume:()=>o.resume()}),this.initialStatus=new Promise((d)=>{o.on("response",(c)=>{this.headers=c,d(Et(c))})}),o.on("data",(d)=>this.onData(d)),this.finalStatus=new Promise((d)=>{let c;o.on("trailers",(p)=>{c=p}),o.on("error",(p)=>{d({code:V.UNAVAILABLE,message:r(p)})}),o.on("close",()=>{this.finish(),d(this.statusAtClose(c))})})}write(o){return new Promise((d,c)=>{if(this.stream.destroyed||this.stream.writableEnded){c(Error("gRPC stream is closed"));return}let p=()=>c(Error("gRPC stream closed before the write completed"));this.stream.once("close",p),this.stream.write(Fr(o),(m)=>{if(this.stream.off("close",p),m)c(m);else d()})})}end(){if(!this.stream.writableEnded)this.stream.end()}async*messages(){for(;;){let o=this.queue.shift();if(o)yield o;else if(this.finished)return;else await new Promise((d)=>{this.wake=d})}}status(){return this.finalStatus}responseHeaders(){return this.initialStatus}cancel(){if(!this.stream.destroyed)this.stream.close(qr.constants.NGHTTP2_CANCEL)}onData(o){try{this.queue.push(this.decoder.push(o))}catch(d){this.stream.destroy(d instanceof Error?d:Error(r(d)))}this.notify()}finish(){this.finished=!0,this.notify()}notify(){let o=this.wake;this.wake=void 0,o?.()}statusAtClose(o){let d=Et(o,this.headers);if(d)return d;if(this.stream.rstCode===qr.constants.NGHTTP2_CANCEL)return{code:V.CANCELLED,message:"cancelled"};return{code:V.UNKNOWN,message:`stream closed without a gRPC status (HTTP ${this.headers?.[":status"]??"none"})`}}}class Ft{address;session;constructor(o){this.address=o}open(o,d){let p=this.connect().request({":method":"POST",":path":o,"content-type":"application/grpc",te:"trailers",...d});return new Dt(p)}close(){this.session?.close(),this.session=void 0}connect(){if(!this.session||this.session.closed||this.session.destroyed){let o=Nn.connect(`http://${this.address}`);o.on("error",(d)=>{e.debug(`emulator gRPC session error: ${r(d)}`)}),this.session=o}return this.session}}var ue=n().min(1).max(256),Un=ts("type",[s({key:ue,type:_("string"),value:n()}).strict(),s({key:ue,type:_("int"),value:a().int()}).strict(),s({key:ue,type:_("long"),value:a().int()}).strict(),s({key:ue,type:_("float"),value:a().finite()}).strict(),s({key:ue,type:_("boolean"),value:h()}).strict(),s({key:ue,type:_("stringArray"),value:S(n())}).strict(),s({key:ue,type:_("uri"),value:n()}).strict()]),Y8={component:n().min(1).optional().describe('A class inside appId: ".Receiver", "com.app.Receiver", or "com.app/.Receiver"'),action:n().min(1).optional().describe("Intent action, e.g. com.app.DEBUG_TOGGLE"),data:n().min(1).optional().describe("Data URI"),mimeType:n().min(1).optional(),categories:S(n().min(1)).max(16).optional(),extras:S(Un).max(64).optional().describe("Typed extras")},Gn={string:"--es",int:"--ei",long:"--el",float:"--ef",boolean:"--ez",stringArray:"--esa",uri:"--eu"};function jn(o){return o.map((d)=>d.replace(/\\/g,"\\\\").replace(/,/g,"\\,")).join(",")}function Kn(o){let d=o.type==="stringArray"?jn(o.value):String(o.value);return[Gn[o.type],b(o.key),b(d)]}function Wn(o,d){let c=d.indexOf("/");if(c>=0){let p=d.slice(0,c),m=d.slice(c+1);if(p!==o||m.length===0)throw new t(`Component "${d}" is outside ${o}; name a class inside the app under test.`);return`${o}/${m}`}return`${o}/${d}`}function _t(o,d){return[...d.action?["-a",b(d.action)]:[],...d.data?["-d",b(d.data)]:[],...d.mimeType?["-t",b(d.mimeType)]:[],...(d.categories??[]).flatMap((c)=>["-c",b(c)]),...d.component?["-n",b(Wn(o,d.component))]:["-p",b(o)],...(d.extras??[]).flatMap(Kn)]}var qn=30000;function Jn(o){return["am","broadcast",...o.userId===void 0?[]:["--user",String(o.userId)],..._t(o.appId,o.intent)].join(" ")}var Xn=/Broadcast completed: result=(-?\d+)(?:, data="([\s\S]*?)")?\s*$/m,Yn=/not allowed to send broadcast ([^\s]+?)(?: from|\s|$)/;function WE(o){let d=Xn.exec(o);if(d)return{delivered:!0,resultCode:Number(d[1]),...d[2]===void 0?{}:{resultData:d[2]},output:o};let c=Yn.exec(o);if(c)return{delivered:!1,errorCode:"protected_broadcast",error:`${c[1]} is a protected system broadcast; only the system can send it, not the adb shell. Use an app-defined action the receiver also accepts.`,output:o};if(/SecurityException|Permission Denial/.test(o))return{delivered:!1,errorCode:"permission_denied",error:"The broadcast was refused: the shell lacks a permission it requires.",output:o};return{delivered:!1,errorCode:"broadcast_failed",error:"am broadcast did not report completion.",output:o}}var Qn=1e4;function Zn(o){return["cmd","package","query-receivers","--components",...o.userId===void 0?[]:["--user",String(o.userId)],..._t(o.appId,o.intent)].join(" ")}class xM{adb;constructor(o){this.adb=o}async send(o,d){let c=await this.precheck(o,d);if(c)return c;let p=Jn(o);try{let m=await this.adb.execute(["shell",p],{timeoutMs:qn,noRetry:!0,signal:d});return WE(`${m.stdout}
3
+ ${m.stderr}`.trim())}catch(m){if(d?.aborted)throw m;return e.warn(`[sendBroadcast] am broadcast failed: ${r(m)}`,m),WE(r(m))}}async precheck(o,d){let c=await this.probe(`pm list packages${o.userId===void 0?"":` --user ${o.userId}`}`,d);if(c!==null&&!ji(c,o.appId))return{delivered:!1,errorCode:"package_not_installed",error:`${o.appId} is not installed, so nothing can receive the broadcast.`,output:c};if(!o.intent.component)return null;let p=await this.probe(Zn(o),d);if(p!==null&&!p.includes(`${o.appId}/`))return{delivered:!1,errorCode:"no_matching_receiver",error:`${o.appId} declares no receiver ${o.intent.component} for ${o.intent.action??"this intent"}; the broadcast was not sent.`,output:p};return null}async probe(o,d){try{return(await this.adb.execute(["shell",o],{timeoutMs:Qn,noRetry:!0,signal:d})).stdout}catch(c){if(d?.aborted)throw c;return e.warn(`[sendBroadcast] precheck "${o}" failed: ${r(c)}`,c),null}}}var Xr=60000,Ot=3720000;function G(o,d){return new D("audio_guest_capture_unavailable",`Cannot verify an open guest microphone capture on ${o}: ${d}. Injection was refused to protect the emulator. The guest microphone capture is provided by the CtrlProxy app. Stop competing recordings and retry. AutoMobile does not enable adb root.`)}var ea=`${vt}/.GuestAudioCaptureReceiver`,ta="dev.jasonpearson.automobile.GUEST_AUDIO_CAPTURE",Vt=2000;class $t{adbFor;discovery;ids;now;timeoutMs;pollMs;settleMs;timer;options;constructor(o,d,c,{timer:p=i,...m}={}){this.adbFor=o;this.discovery=d;this.ids=c;if(this.timer=p,this.options=m,this.now=p.monotonicNow?.bind(p)??p.now.bind(p),this.timeoutMs=m.timeoutMs??8000,this.pollMs=m.pollMs??100,this.settleMs=m.settleMs??3000,![this.timeoutMs,this.pollMs,this.settleMs].every(Number.isFinite)||Math.min(this.timeoutMs,this.pollMs)<=0||this.settleMs<0||this.settleMs>=this.timeoutMs)throw new D("audio_guest_capture_unavailable","Invalid guest capture polling or settle deadline.")}async open(o,{leaseMs:d}){let c=this.now()+this.timeoutMs,p,m,g=!1,w=!1,R;try{R=this.adbFor(o)}catch(A){throw G(o,r(A))}let y=async(A,k)=>{let C=k-this.now();if(C<=0)throw G(o,"capture verification deadline expired");let T=await R.execute(["shell","am","broadcast","-n",ea,"-a",ta,"--es","op",A,"--es","lease",Yo(m??""),...A==="start"?["--el","leaseTimeoutMs",String(d)]:[]],{timeoutMs:Math.min(Vt,C),noRetry:!0,signal:new AbortController().signal});if(this.now()>=k)throw G(o,"capture verification deadline expired");if(T.error)throw G(o,T.error);let M=WE(`${T.stdout}
4
+ ${T.stderr}`),O="the CtrlProxy on the device has no guest capture receiver; reinstall the CtrlProxy APK";if(!M.delivered){let F=M.output.trim().slice(0,2048);switch(M.errorCode){case"permission_denied":throw G(o,`the adb shell was refused the CtrlProxy receiver, which needs android.permission.DUMP (held by the adb shell): ${F}`);case"no_matching_receiver":throw G(o,O);case"protected_broadcast":throw G(o,`the guest capture broadcast is protected: ${F}`);default:if(M.output.trimStart().startsWith("Error: receiver not found"))throw G(o,O);throw G(o,`the guest capture broadcast failed: ${F}`)}}if(!M.resultData)throw G(o,O);return M.resultData.slice(0,2048)},P=async()=>{if(w||!g)return;w=!0;try{if(p===void 0||await this.emulatorPidFor(o)!==p){e.debug(`Skipping guest capture cleanup on ${o}: emulator identity changed`);return}}catch(A){e.debug(`Skipping guest capture cleanup on ${o}: ${r(A)}`);return}try{let A=await y("stop",this.now()+Vt),k=A.split(" ",1)[0];if(k!=="stopped"&&k!=="absent")throw new t(`Guest capture stop refused: ${A}`)}catch(A){throw u(A,`Failed to close guest audio capture on ${o}`)}};try{if(p=await this.emulatorPidFor(o),await this.ensureRecordAudioPermission(R,o,c),m=this.ids.next(),!m||/[\r\n\0]/.test(m))throw G(o,"the capture lease is invalid");g=!0;let A=await y("start",c);if(A.split(" ",1)[0]!=="running")throw G(o,A);let k=this.now();while(this.now()<c){let C=this.now();if(C-k>=this.settleMs)return{close:P};await this.timer.sleep(Math.min(this.pollMs,c-C));let T=await y("status",c);if(T.split(" ",1)[0]!=="running")throw G(o,T)}throw G(o,"capture verification deadline expired")}catch(A){throw await P().catch((k)=>{e.warn(`Failed guest capture start cleanup on ${o}: ${r(k)}`)}),A instanceof D?A:G(o,r(A))}}async ensureRecordAudioPermission(o,d,c){let p=()=>{let P=c-this.now();if(P<=0)throw G(d,"capture verification deadline expired");return Math.min(Vt,P)},m=(P,A)=>{if(P instanceof D)return P;return p(),G(d,`the RECORD_AUDIO check could not ${A}: ${r(P)}`)},g;try{p();let P=this.options.resolveSetupUser;if(P)g=await l(()=>P(d),{timer:this.timer,timeoutMs:c-this.now(),label:"CtrlProxy setup user resolution",timeoutError:()=>G(d,"capture verification deadline expired")});p()}catch(P){throw m(P,"read")}let w=(P)=>new D("audio_record_permission_not_granted",`${Jv} is not granted to ${vt}: ${P}. Run: adb shell pm grant ${vt} ${Jv}`),R=async()=>{let P;try{P=await o.execute(["shell","dumpsys","package",vt],{noRetry:!0,timeoutMs:p()})}catch(k){throw m(k,"read")}if(p(),P.error)throw m(P.error,"read");let A=gn(P.stdout,vt,g)?.runtimePermissions.get(Jv);if(!A)throw w("the runtime permission row is absent");return A.state==="granted"};if(await R())return;let y=await Mz(o,g,p(),p);if(!y.granted&&y.transportError instanceof D)throw y.transportError;if(p(),!y.granted){if("transportError"in y)throw m(y.transportError,"grant");throw w(y.reason)}if(!await R())throw w("the runtime permission remains ungranted after pm grant")}async emulatorPidFor(o){let d=ce(o)?await this.discovery.findByConsolePort(Number(o.slice(9))):void 0;if(!d||!Number.isSafeInteger(d.pid)||d.pid<1)throw G(o,"the emulator incarnation could not be identified");return d.pid}}var ra=52428800,ia=2000,oa=600000,Yr=64,na=128;function Qr(o,d,c){if(c!==void 0&&d!==c)throw new t(`Audio injection '${o}' is on ${d}, not ${c}. Stop it on ${d}.`)}function Ht(o,d){return new t(`Audio file ${o} is larger than the ${d}-byte limit. Use a shorter clip.`)}function Zr(o,d){return new D(o,d)}class Bt{deps;injectionStarts=new Map;reservedDevices=new Set;active=new Map;hostLoopbackStarting=!1;finished=new Map;constructor(o){this.deps=o}async startInjection(o,d){if(this.reservedDevices.has(o))throw new D("audio_injection_busy",`An audio injection is starting or active on ${o}. Stop it before starting another.`);this.reservedDevices.add(o);try{return await this.startReservedInjection(o,d)}catch(c){throw this.reservedDevices.delete(o),u(c,`Cannot start audio injection on ${o}`)}}async startReservedInjection(o,d){let c=await this.routeFor(o,"audio_injection_unsupported");this.assertInjectionSafe(o,c.endpoint);let p=this.reserveHostLoopback(c.route),m;try{m=await this.openInjection(o,c,d)}finally{p()}let{injection:g,injectionId:w}=m,R=this.settle(o,c.endpoint,m);this.active.set(w,{deviceId:o,route:c.route,owner:d.sessionId,injection:g,result:R});let y=()=>{this.active.delete(w),this.reservedDevices.delete(o),this.finished.set(w,{deviceId:o,result:R});while(this.finished.size>Yr)this.finished.delete(this.finished.keys().next().value)};return R.then(y,(P)=>{e.warn(`Audio injection ${w} on ${o} failed: ${r(P)}`),y()}),{injectionId:w,deviceId:o,durationMs:g.durationMs,sampleRate:g.sampleRate,route:c.route}}async stopInjection(o,d){let c=this.finished.get(o);if(c)return Qr(o,c.deviceId,d),c.result;let p=this.active.get(o);if(!p)throw new t(`No audio injection '${o}' on this server. Only the last ${Yr} ended injections are kept, and a server restart forgets them all; start a new injection.`);return Qr(o,p.deviceId,d),await p.injection.stop().catch((m)=>{e.debug(`audio injection ${o} stop: ${r(m)}`)}),p.result}stopDevice(o){return this.stopMatching((d)=>d.deviceId===o)}stopOwnedBy(o,d){let c=(p)=>p.deviceId===o&&p.owner===d;return[...this.active.values()].some(c)?this.stopMatching(c):void 0}stopOwnerless(o){let d=(c)=>c.deviceId===o&&c.owner===void 0;return[...this.active.values()].some(d)?this.stopMatching(d):void 0}async stopMatching(o){let d=[...this.active].filter(([,c])=>o(c)).map(([c])=>c);return await Promise.all(d.map((c)=>this.stopInjection(c).catch((p)=>{e.warn(`Failed to stop audio injection ${c}: ${r(p)}`)}))),d.length}activeInjectionIds(o){return[...this.active].filter(([,d])=>d.deviceId===o).map(([d])=>d)}async capture(o,d){let c=await this.routeFor(o,"audio_capture_unsupported"),p=c.open();try{let{pcm:m,timedOut:g}=await p.captureAudio({durationMs:d.durationMs,sampleRate:d.sampleRate});return{...Tr(m,{silenceThresholdDbfs:d.silenceThresholdDbfs}),pcm:m,timedOut:g,route:c.route}}finally{await p.close()}}async startStream(o,d,c={}){let m=(await this.routeFor(o,"audio_capture_unsupported")).open(),g;try{g=await m.streamCapture(d,c)}catch(y){throw await m.close(),y}let w,R=async()=>{try{await g.stop()}finally{await m.close()}};return{sampleRate:g.sampleRate,stop:()=>(w??=R(),w)}}async settle(o,d,c){let{injectionId:p,injection:m,session:g,guestCapture:w}=c;try{return{injectionId:p,...await m.done}}catch(R){throw d?await this.classifyInjectionError(o,d,R):u(R,`Audio injection on ${o} failed`)}finally{await this.closeInjectionResources(w,g)}}async closeInjectionResources(o,d){let c=!0;for(let p of[o,d])try{await p?.close()}catch(m){e.warn(`Failed to close audio injection resource: ${r(m)}`),c=!1}return c}async classifyInjectionError(o,d,c){if(c instanceof D&&c.code==="audio_host_microphone_unavailable")return c;try{let p=await this.deps.discovery.findByConsolePort(d.consolePort);if(!p||p.pid!==d.pid)return Br(o)}catch(p){e.warn(`Cannot check emulator ${o} after audio injection failure: ${r(p)}`)}return u(c,`Audio injection on ${o} failed`)}async openInjection(o,d,c){let p=kr(await this.loadSource(c.source),c.gainDb??0);this.assertInjectionSafe(o,d.endpoint);let m=Math.ceil(p.samples.length/p.channels/p.sampleRate*1000),g=c.maxDurationMs??(c.loop?oa:void 0),w=g??m,R=(c.startDelayMs??0)+w+Xr;if(d.endpoint&&R>Ot)throw new t(`Guest microphone capture lease exceeds the ${Ot} ms (62 minute) limit. Reduce startDelayMs or maxDurationMs, or use a shorter clip.`);let y=d.endpoint?await this.deps.guestCapture.open(o,{leaseMs:R}):void 0,P;try{this.assertInjectionSafe(o,d.endpoint),P=d.open();let A=this.deps.ids.next();if(d.endpoint)this.recordInjectionStart(o,d.endpoint);let k=await P.injectAudio(p,{loop:c.loop,startDelayMs:c.startDelayMs,maxDurationMs:g,beforeStreamOpen:()=>this.assertInjectionSafe(o,d.endpoint,1)});return{session:P,injection:k,injectionId:A,guestCapture:y}}catch(A){throw await this.closeInjectionResources(y,P),d.endpoint?await this.classifyInjectionError(o,d.endpoint,A):u(A,`Audio injection on ${o} failed`)}}recordInjectionStart(o,d){this.pruneInjectionHistory(o,d.pid);let c=`${o}:${d.pid}`;this.injectionStarts.set(c,(this.injectionStarts.get(c)??0)+1);while(this.injectionStarts.size>na){let p=this.injectionStarts.keys().next().value;if(p!==void 0)this.injectionStarts.delete(p)}}pruneInjectionHistory(o,d){let c=`${o}:`,p=`${c}${d}`;for(let m of this.injectionStarts.keys())if(m.startsWith(c)&&m!==p)this.injectionStarts.delete(m)}assertInjectionSafe(o,d,c=0){if(!d)return;if(this.pruneInjectionHistory(o,d.pid),this.deps.outputTail?.hostMicrophoneFailed(o,d.pid))throw Vr(o);if((this.injectionStarts.get(`${o}:${d.pid}`)??0)>c)throw Or(o)}reserveHostLoopback(o){if(o!=="host-loopback")return()=>{};let d=[...this.active].find(([,c])=>c.route==="host-loopback");if(this.hostLoopbackStarting||d){let c=d?`Audio injection '${d[0]}' on ${d[1].deviceId} is`:"Another injection is starting and";throw new D("audio_injection_busy",`${c} using the host loopback device, which every iOS Simulator on this host records from. Stop it before starting another.`)}return this.hostLoopbackStarting=!0,()=>{this.hostLoopbackStarting=!1}}async routeFor(o,d){let c=this.deps.createHostLoopbackSession;if(c&&z({deviceId:o})==="simulator")return{route:"host-loopback",open:c};let p=await this.endpointFor(o,d);return{route:"emulator-grpc",endpoint:p,open:()=>this.deps.createSession(p)}}async endpointFor(o,d){if(!ce(o))throw Zr(d,`Audio injection and capture support local Android emulators and iOS Simulators; '${o}' is neither.`);let c=Number.parseInt(o.slice(9),10),p=await this.deps.discovery.findByConsolePort(c);if(!p)throw Zr(d,`Emulator ${o} does not advertise a gRPC endpoint. Use an emulator build with gRPC enabled (the default on recent builds) running on this host.`);return p}async loadSource(o){if(o.kind==="tone")return Mr({frequencyHz:o.frequencyHz,durationMs:o.durationMs??ia});let d=this.deps.maxSourceBytes??ra,c=await this.deps.readFile(o.path,d);if(c.byteLength>d)throw Ht(o.path,d);return Cr(c)}}var aa="audio-loopback-ready:",ti="audio-loopback-error:",ei=5000;function sa(o){let d=o.slice(ti.length).trim(),c=d.indexOf(" ");return c<0?{code:d,message:""}:{code:d.slice(0,c),message:d.slice(c+1)}}function ri(o,d,c){let p={inject:["no_loopback_input","audio_injection_unsupported"],capture:["no_loopback_output","audio_capture_unsupported"]},[m,g]=p[o];if(d?.code===m)return new D(g,d.message);let w=d?`${d.code}: ${d.message}`:`the helper exited with code ${c??"none"} and reported no error`;return new D("audio_loopback_failed",`The host audio loopback ${o==="inject"?"playback":"capture"} failed (${w}).`)}async function ii(o){let d=o.launch(),c=null,p,m=()=>{},g=new Promise((C)=>{m=C}),w="";d.stderr.on("data",(C)=>{w+=C.toString();let T=w.split(`
5
+ `);w=T.pop()??"";for(let M of T)if(M.startsWith(aa))m();else if(M.startsWith(ti))p=sa(M);else if(M.trim())e.debug(`audio-loopback-helper: ${M}`)});let{onStdout:R}=o,y=!1;if(R)d.stdout.on("data",(C)=>{let T=R(C);if(T instanceof Promise){y=!0,d.stdout.pause();let M=()=>{y=!1,d.stdout.resume()};T.then(M,M)}});d.stdin.on("error",(C)=>{e.debug(`audio-loopback-helper stdin closed: ${C.message}`)});let P=new Promise((C)=>{d.once("exit",(T)=>{c=T,C(T)}),d.once("error",(T)=>{p??={code:"spawn_failed",message:T.message},C(null)})}),A=new Promise((C)=>{d.stdout.once("end",C),d.stdout.once("close",C),d.stdout.once("error",()=>C())}),k={process:d,exited:P,outputEnded:A,sinkBusy:()=>y,failure:()=>ri(o.operation,p,c)};return await da(k,g,o,()=>p),k}async function da(o,d,c,p){let m=await new Promise((g)=>{let w=c.timer.setTimeout(()=>g("timeout"),ei),R=(y)=>{c.timer.clearTimeout(w),g(y)};d.then(()=>R("ready")),o.exited.then((y)=>R({code:y}))});if(m==="ready")return;if(m==="timeout")throw o.process.kill("SIGKILL"),new D("audio_loopback_failed",`The audio loopback helper did not open its device within ${ei} ms.`);throw ri(c.operation,p(),m.code)}var ca=8000,oi=48000,ni=48000,la=100,ua=5000,pa=2000,ai=1000;function ma(o,d){return new D(o,`iOS Simulator audio needs the released audio-loopback helper on a macOS host. Check release asset availability, or set ${d} to the path of an audio-loopback-helper executable.`)}function ot(o){return o<ca||o>oi?oi:o}function fa(o,d){let c=[];for(let p=0;p<o.length;p+=d){let m=o.subarray(p,p+d);c.push(new Uint8Array(m.buffer,m.byteOffset,m.byteLength))}return c}function ga(o,d,c){return new Promise((p)=>{if(c.then(p),o.write(d))p();else o.once("drain",()=>p())})}class Lt{deps;envName;timer;live=new Set;constructor(o,d){this.deps=o;this.envName=d;this.timer=o.timer??i}async injectAudio(o,d={}){let c=Ye(o),p=ot(c.sampleRate)===c.sampleRate?c:Ze(c,ot(c.sampleRate)),m=await this.start("inject",["play","--sample-rate",String(p.sampleRate)]),g,w=!1,R=()=>{},y=new Promise((F)=>{R=F}),P=()=>{w=!0,R()},A=(F)=>{if(g===void 0)g=F,P(),m.process.kill("SIGTERM")},k=d.maxDurationMs===void 0?void 0:this.timer.setTimeout(()=>A("max_duration"),d.maxDurationMs),C=0,T,M=this.feed(m,p,d,{ended:y,isEnded:()=>w,progress:{started:()=>{T??=this.timer.monotonicNow?.()??this.timer.now()},queued:(F)=>{C+=F}}}),O=(async()=>{let F=await m.exited;if(P(),k)this.timer.clearTimeout(k);await M;let j=this.framesPlayed(T,C,p.sampleRate);if(g)return{framesQueued:C,framesPlayed:j,reason:g};if(F===0)return{framesQueued:C,framesPlayed:j,reason:"completed"};throw m.failure()})();return{durationMs:$e(p),sampleRate:p.sampleRate,done:O,stop:async()=>(A("stopped"),O)}}framesPlayed(o,d,c){if(o===void 0)return 0;let p=this.timer.monotonicNow?.()??this.timer.now(),m=Math.max(0,p-o),g=Math.floor(m*c/1000);return Math.min(d,g)}async captureAudio(o){let d=ot(o.sampleRate??ni),c=[],p=await this.start("capture",["capture","--duration-ms",String(o.durationMs),"--sample-rate",String(d)],(A)=>{c.push(A)}),m=!1,g=this.timer.setTimeout(()=>{m=!0,p.process.kill("SIGTERM")},o.durationMs+ua),w=await p.exited;if(this.timer.clearTimeout(g),!m&&w!==0)throw p.failure();await this.untilOutputEnded(p);let R=Buffer.concat(c),y=new Int16Array(R.buffer.slice(R.byteOffset,R.byteOffset+(R.byteLength&-2))),P=Math.floor(o.durationMs*d/1000);return{pcm:{sampleRate:d,channels:1,samples:y},timedOut:m||y.length<P}}async streamCapture(o,d={}){let c=ot(d.sampleRate??ni),p=await this.start("capture",["capture","--until-stopped","--sample-rate",String(c)],o),m=async()=>{p.process.stdin.end();let w=this.idleDeadline(p,pa,()=>p.process.kill("SIGTERM")),R=await p.exited;w();let y=await this.untilOutputEnded(p);if(R!==0)throw p.failure();if(!y)throw new D("audio_loopback_failed",`The audio-loopback helper's output did not finish within ${ai} ms of its exit, so the end of the capture may be missing.`)},g;return{sampleRate:c,stop:()=>(g??=m(),g)}}untilOutputEnded(o){return new Promise((d)=>{let c=this.idleDeadline(o,ai,()=>d(!1));o.outputEnded.then(()=>{c(),d(!0)})})}idleDeadline(o,d,c){let p,m=()=>{p=this.timer.setTimeout(()=>o.sinkBusy()?m():c(),d)};return m(),()=>this.timer.clearTimeout(p)}async close(){for(let o of this.live)o.kill("SIGTERM");this.live.clear()}async start(o,d,c){let p=await this.deps.helperPath();if(!p)throw ma(o==="inject"?"audio_injection_unsupported":"audio_capture_unsupported",this.envName);let m=await ii({launch:()=>this.deps.launchHelper(p,d),operation:o,timer:this.timer,onStdout:c});return this.live.add(m.process),m.exited.then(()=>this.live.delete(m.process)),m}async feed(o,d,c,p){let{ended:m,isEnded:g,progress:w}=p,R=c.startDelayMs;if(R)await new Promise((k)=>{let C=this.timer.setTimeout(k,R);m.then(()=>{this.timer.clearTimeout(C),k()})});let y=Math.max(1,Math.round(d.sampleRate*la/1000)),P=fa(d.samples,y),A=o.process.stdin;if(P.length===0){A.end();return}do for(let k of P){if(g())return;w.started(),await ga(A,k,m),w.queued(k.byteLength/2)}while(c.loop&&!g());if(!g())A.end()}}import{existsSync as Ra}from"fs";var pi=Nt(Id());import*as K from"fs/promises";import He from"path";var si="audio-loopback-helper",ci=448,li=384,ui=448,ha=30000,wa=tY,nt="audio-loopback-helper",ya="audio-loopback-helper.json";class Ut{downloader;checksumCalculator;cacheDir;usesDefaultCacheDir;timer;cache;env;expectedChecksumOverride;releaseUrlOverride;downloadTimeoutMs;platform;inFlight=null;constructor(o={}){this.downloader=o.downloader??new bi,this.checksumCalculator=o.checksumCalculator??new an,this.cacheDir=o.cacheDir??Qe(si),this.usesDefaultCacheDir=o.cacheDir===void 0,this.timer=o.timer??i,this.cache=new so({downloader:this.downloader,checksumCalculator:this.checksumCalculator,timer:this.timer}),this.env=o.env??process.env,this.expectedChecksumOverride=o.expectedChecksum,this.releaseUrlOverride=o.releaseUrl,this.downloadTimeoutMs=o.downloadTimeoutMs??ha,this.platform=o.platform??process.platform}async ensure(){if(this.platform!=="darwin")return;if(this.inFlight)return this.inFlight;return this.inFlight=this.doEnsure().catch((o)=>{throw u(o,"Unable to resolve the released audio-loopback helper")}).finally(()=>{this.inFlight=null}),this.inFlight}async doEnsure(){let o=this.expectedChecksumOverride??sY(this.env);if(o.length===0)return e.info("[AUDIO_LOOPBACK_HELPER] Expected checksum unknown for the pinned version; no release helper can be trusted."),null;let d=await this.tryCache(o);if(d)return e.info("[AUDIO_LOOPBACK_HELPER] Reusing verified release helper",{path:d}),d;return this.download(o)}get helperPath(){return He.join(this.cacheDir,nt)}get metadataPath(){return He.join(this.cacheDir,ya)}async tryCache(o){return await this.cache.isCached({label:"[AUDIO_LOOPBACK_HELPER]",subject:"helper",expectedSha256:o,metadataPath:this.metadataPath,parseMetadata:(c)=>va(c)?c:null,verifyArtifact:(c)=>this.isHelperExecutable(c.size)})?this.helperPath:null}async isHelperExecutable(o){try{let d=await K.stat(this.helperPath);return!(!d.isFile()||d.size!==o||process.platform!=="win32"&&(d.mode&73)===0)}catch(d){if(d instanceof Error&&"code"in d&&d.code==="ENOENT")e.debug("[AUDIO_LOOPBACK_HELPER] No cached helper executable");else e.warn("[AUDIO_LOOPBACK_HELPER] Unable to inspect cached helper",d);return!1}}async ensureSecureCacheDir(){if(this.usesDefaultCacheDir)return di(si);return await K.mkdir(this.cacheDir,{recursive:!0,mode:ci}),await K.chmod(this.cacheDir,ci),this.cacheDir}async download(o){let d=await this.ensureSecureCacheDir(),c=He.join(d,`${wa}.download`),p=He.join(d,`${nt}.download`);return await this.cache.fetch({label:"[AUDIO_LOOPBACK_HELPER]",expectedSha256:o,url:this.releaseUrlOverride??cne(this.env),archivePath:c,metadataPath:this.metadataPath,metadataMode:li,downloadTimeoutMs:this.downloadTimeoutMs,afterDownload:(m)=>K.chmod(m,li),mismatchError:(m,g)=>new t(`audio-loopback-helper checksum verification failed. Expected: ${m}, Got: ${g}. The downloaded release asset is corrupted or tampered.`),timeoutError:()=>new t(`Timed out downloading audio-loopback-helper after ${this.downloadTimeoutMs}ms. Check GitHub Release asset availability or set AUTOMOBILE_IOS_AUDIO_LOOPBACK_HELPER for local development.`),publish:async(m,g)=>{await K.writeFile(p,ba(m),{mode:ui}),await K.chmod(p,ui);let{size:w}=await K.stat(p);return await K.rename(p,this.helperPath),e.info("[AUDIO_LOOPBACK_HELPER] Downloaded and verified release helper",{path:this.helperPath,sha256:g}),{version:De(this.env),sha256:g??o,size:w,downloadedAt:this.timer.now()}},discard:()=>K.rm(p,{force:!0})}),this.helperPath}}function va(o){return typeof o==="object"&&o!==null&&"sha256"in o&&typeof o.sha256==="string"&&"size"in o&&typeof o.size==="number"}function ba(o){let d;try{d=new pi.default(o)}catch(p){throw new t(`audio-loopback-helper release asset is not a valid zip archive: ${r(p)}`)}let c=d.getEntries().filter((p)=>!p.isDirectory&&He.posix.basename(p.entryName)===nt);if(c.length!==1)throw new t(`audio-loopback-helper release asset must contain exactly one ${nt}.`);try{return c[0].getData()}catch(p){if(p instanceof t)throw p;throw new t(`Unable to extract audio-loopback-helper release asset: ${r(p)}`)}}var mi;function Pa(){return mi??=new Ut,mi}async function fi(o=process.env,d={}){let c=I(f.IOS_AUDIO_LOOPBACK_HELPER,o);if(c!==void 0)return c&&(d.exists??Ra)(c)?c:void 0;if((d.platform??process.platform)!=="darwin")return;try{return await(d.provider??Pa()).ensure()??void 0}catch(p){e.warn("Unable to resolve the released audio-loopback helper",p);return}}function gi(o,d){return new re().spawn(o,d,{stdio:["pipe","pipe","pipe"]})}var Ca=65536;function wi(o){return new t(`Audio file ${o} is not a regular file.`)}async function xa(o,d){if(!(await Ea(o)).isFile())throw wi(o);let c=await Aa(o,hi.O_RDONLY|(hi.O_NONBLOCK??0));try{if(!(await c.stat()).isFile())throw wi(o);let p=[],m=0;for(;;){let g=new Uint8Array(Ca),{bytesRead:w}=await c.read(g,0,g.byteLength,null);if(w===0)break;if(m+=w,m>d)throw Ht(o,d);p.push(g.subarray(0,w))}return Buffer.concat(p,m)}finally{await c.close()}}function ka(){let o=new Ur,d=new r$(o);return new Bt({discovery:d,outputTail:gj,guestCapture:new $t((c)=>v.create({deviceId:c,platform:"android",name:c}),d,B,{resolveSetupUser:async(c)=>{let p=v.create({deviceId:c,name:c,platform:"android"}),m=await zr.read(p,c);return rR(p,{formFactor:yB(m)?"automotive":void 0})}}),createSession:(c)=>new Tt(new Ft(c.address),Wr(c.auth,o)),createHostLoopbackSession:()=>new Lt({helperPath:()=>fi(),launchHelper:gi},f.IOS_AUDIO_LOOPBACK_HELPER.name),readFile:xa,ids:B})}var Lf=ka();function Ta(o={}){if(o.explicitPath)return o.explicitPath;let d=o.env??process.env;for(let c of o.environmentKeys??[f.FFMPEG.name]){let p=d[c];if(p)return p}return"ffmpeg"}var Ma=new re,Ia=(o,d,c)=>Ma.spawn(o,d,c);class pe{binaryPath;spawnProcess;timer;constructor(o={}){this.binaryPath=Ta({explicitPath:o.binaryPath??o.explicitPath,environmentKeys:o.environmentKeys,env:o.env}),this.spawnProcess=o.spawn??Ia,this.timer=o.timer??i}start(o){let d=this.spawnProcess(this.binaryPath,o.args,{stdio:o.stdio??["ignore","pipe","pipe"]}),c=ld(d);return c.exitPromise.catch(()=>{return}),{process:d,tracker:c}}async run(o){let d=this.start(o),c=[];d.process.stdout?.on("data",(m)=>{c.push(m.toString())});let p=this.abortOnSignal(d.process,o.signal);try{await zn(d.process,d.tracker.exitPromise,{timeoutMs:o.timeoutMs,forceKillTimeoutMs:o.forceKillTimeoutMs,timer:this.timer,signal:null})}catch(m){throw this.commandError(o,d.tracker,m)}finally{p()}if(d.tracker.exitState.exitCode!==0||d.tracker.exitState.signal)throw this.commandError(o,d.tracker);return{stdout:c.join(""),stderr:d.tracker.stderr.join(""),exitCode:d.tracker.exitState.exitCode,signal:d.tracker.exitState.signal}}async probe(o={}){let d=o.timeoutMs===void 0?void 0:this.timer.now()+o.timeoutMs,c=async(y,P)=>await this.run({args:y,context:P,timeoutMs:d===void 0?void 0:Math.max(0,d-this.timer.now()),forceKillTimeoutMs:d===void 0?void 0:0,signal:o.signal}),p=await c(["-version"],"FFmpeg version probe"),m=/ffmpeg version (\S+)/.exec(p.stdout)?.[1];if(!m)throw new t("FFmpeg version probe returned invalid version output.");let w=(await c(["-hide_banner","-encoders"],"FFmpeg encoder probe")).stdout.split(`
6
+ `).filter((y)=>y.trim().startsWith("V")).map((y)=>y.trim().split(/\s+/)[1]).filter((y)=>Boolean(y)),R=(o.requiredEncoders??[]).filter((y)=>!w.includes(y));if(R.length>0)throw new t(`FFmpeg is missing required encoder(s): ${R.join(", ")}.`);return{version:m,encoders:w}}pipe(o){if(!o.source||!o.destination)throw new t(`Cannot pipe ${o.context}: source or destination stream is unavailable.`);let d=!1,c=(p)=>{if(d)return;d=!0,e.warn(`[FfmpegClient] ${o.context} pipe failed: ${p.message}; stopping both processes.`);for(let m of o.processes)m.kill("SIGKILL")};o.source.on("error",c),o.destination.on("error",c),o.source.pipe(o.destination)}abortOnSignal(o,d){if(!d)return()=>{return};let c=()=>{if(o.exitCode===null&&!o.killed)o.kill("SIGKILL")};if(d.aborted)return c(),()=>{return};return d.addEventListener("abort",c,{once:!0}),()=>d.removeEventListener("abort",c)}commandError(o,d,c){let p=[`FFmpeg ${o.context} failed.`,`command: ${this.binaryPath} ${o.args.join(" ")}`,`exitCode: ${d.exitState.exitCode??"null"}`,`signal: ${d.exitState.signal??"null"}`,`stderr: ${d.stderr.join("").trim()||"(empty)"}`,c instanceof Error?`cause: ${c.message}`:void 0].filter((m)=>Boolean(m));return new t(p.join(`
7
+ `))}}function Da(o){return`${o}.audio.pcm`}function DG(o,d,c){if(c&&o==="android"&&(d??0)>Ci)throw new t(`audio is supported for Android recordings up to ${Ci} s; set maxDuration to that or less, or record without audio.`)}var Fa="The recording has no audio track: an earlier stop failed and its audio was discarded.";class Gt{inner;deps;tracks=new Map;lostTracks=new Set;constructor(o,d){this.inner=o;this.deps=d;if(o.describeCaptureTeardown)this.describeCaptureTeardown=o.describeCaptureTeardown.bind(o);if(o.forceStopOrphanedCapture)this.forceStopOrphanedCapture=o.forceStopOrphanedCapture.bind(o)}describeCaptureTeardown;forceStopOrphanedCapture;async start(o){if(!o.audio)return this.inner.start(o);let d=o.device?.deviceId;if(!d)throw new t("A recording with audio needs a target device.");DG(o.device?.platform,o.maxDurationSeconds,o.audio),await this.deps.muxer.ensureAvailable();let c=Da(o.outputPath),p=this.deps.openPcmFile(c),m=_a(p),g;try{g=await this.deps.source.startStream(d,m.sink)}catch(A){throw await this.discardFile(p,c),A}let w=this.deps.now(),R,y=this.deps.now();try{R=await this.inner.start(o)}catch(A){throw await this.discard({stream:g,abandon:m.abandon,file:p,pcmPath:c,leadMs:0}),A}let P=Math.max(0,y-w);return this.tracks.set(R.recordingId,{stream:g,abandon:m.abandon,file:p,pcmPath:c,leadMs:P}),{...R,inProgressSidecarPaths:[...R.inProgressSidecarPaths??[],c]}}async stop(o){let d=this.tracks.get(o.recordingId);if(!d){if(!this.lostTracks.has(o.recordingId))return this.inner.stop(o);let c=await this.inner.stop(o);return this.lostTracks.delete(o.recordingId),yi(c,Fa)}try{return await this.stopWithTrack(o,d)}finally{this.tracks.delete(o.recordingId)}}async forceStop(o,d){this.lostTracks.delete(o.recordingId);let c=this.tracks.get(o.recordingId);if(c)this.tracks.delete(o.recordingId),this.discardOnce(c);await this.inner.forceStop?.(o,d)}async stopWithTrack(o,d){let c;try{c=await this.inner.stop(o)}catch(m){if(await this.discardOnce(d),this.tracks.has(o.recordingId))this.lostTracks.add(o.recordingId);throw m}let p=d.discarded?"The recording has no audio track: it was force-stopped while stopping.":await this.addTrack(d,c.outputPath);return yi(c,p)}async addTrack(o,d){let c;try{await o.stream.stop(),c=o.file.close();let p=await c;await this.deps.muxer.mux({videoPath:d,pcmPath:o.pcmPath,pcmBytes:p,sampleRate:o.stream.sampleRate,leadMs:o.leadMs});return}catch(p){return e.warn(`Recording audio track for ${d} failed: ${r(p)}`),`The recording has no audio track: ${r(p)}`}finally{if(c)await this.removeQuietly(o.pcmPath);else await this.discardFile(o.file,o.pcmPath)}}discardOnce(o){return o.discarded??=this.discard(o),o.discarded}async discard(o){o.abandon(),await o.stream.stop().catch((d)=>{e.debug(`audio track stop during discard: ${r(d)}`)}),await this.discardFile(o.file,o.pcmPath)}async discardFile(o,d){await o.close().catch((c)=>{e.debug(`audio track file close during discard: ${r(c)}`)}),await this.removeQuietly(d)}async removeQuietly(o){await this.deps.removeFile(o).catch((d)=>{e.warn(`Failed to remove temporary audio ${o}: ${r(d)}`)})}}function yi(o,d){let c=[...o.warnings??[],...d?[d]:[]];return{...o,...d?{}:{sizeBytes:void 0},hasAudioTrack:d===void 0,warnings:c.length>0?c:void 0}}function _a(o){let d=!1,c;return{sink:(p)=>{if(d)return;let m=o.write(p);if(!(m instanceof Promise))return;return new Promise((g)=>{let w=()=>{if(c===w)c=void 0;g()};c=w,m.then(w,w)})},abandon:()=>{d=!0,c?.()}}}import{platform as Wa}from"os";import qa from"path";function vi(o,d){let c=o?.panels.find((p)=>p.key===d.key)?.sizePx;return c&&c.width>0&&c.height>0?c:void 0}function Ri(o,d){if(o.key==="unknown")return"an unresolved panel";return`panel "${o.key}" (${o.role}${d?`, ${d.width}x${d.height}`:""})`}function Va(o,d){return o!==void 0&&d!==void 0&&o.width===d.width&&o.height===d.height}function Si(o,d,c,p){let m=vi(o,d);return c.flatMap((g,w)=>{if(g===p||g.to.key===d.key)return[];let R=vi(o,g.to),y=c[w+1],P=y?`from ${g.atMs}ms to ${y.atMs}ms`:`after ${g.atMs}ms`,A=Va(m,R)?"may show an inactive panel":`are cropped or letterboxed to the recorded ${m?`${m.width}x${m.height} `:""}size`;return[`Android recording kept capturing ${Ri(d,m)} after the active display changed to ${Ri(g.to,R)} at ${g.atMs}ms; frames ${P} ${A}. Stop the recording before setPosture or a fold and start a new one after it to capture the new panel at its full size.`]})}var Ai={key:"unknown",role:"unknown"};function Ei(o,d){let c=(o.displays?.panels??[]).filter(Cd);if(c.length<2)return;let p=c.find((g)=>g.key===d?.key),m=p?{key:p.key,role:p.role}:Ai;return{panel:m,activePanel:m}}function xi(o,d,c){if(!d)return;if(!c)return Ai;let p=o?.displays?.panels.find((m)=>m.key===c);return{key:c,role:p?.role??"unknown"}}function ki(o,d,c){if(o.platform!=="ios")return d;let p=c??d.panel;return{...d,panel:p,activePanel:p}}function Ti(o,d,c,p){return o.platform==="ios"&&p!==void 0&&p.key!=="unknown"&&d.key==="unknown"&&c.length===0}function Pi(o){return o.key==="unknown"?"the starting panel":`panel "${o.key}" (${o.role})`}function Oa(o,d){return d.map((c)=>`iOS recording kept capturing ${Pi(o)} after the active display changed to ${Pi(c.to)} at ${c.atMs}ms; frames after that point may show an inactive panel.`)}function $a(o){if(o.device.platform==="ios")return Oa(o.recordedPanel,o.transitions);if(o.device.platform==="android")return Si(o.device.displays,o.recordedPanel,o.transitions,o.seededTransition);return[]}function Mi(o){return[...o.warning?[o.warning]:[],...$a(o)]}async function Le(o,d,c,{callerSignal:p,timeoutMessage:m}){p?.throwIfAborted();let g=new AbortController,w=p?AbortSignal.any([p,g.signal]):g.signal,R=Error(m),y=Math.max(0,d-c.now()),P=o(w),A=c.setTimeout(()=>g.abort(R),y);try{return await l(P,{timer:c,signal:w,label:"FFmpeg operation"})}finally{c.clearTimeout(A)}}function te(o,d){if(o?.aborted)throw new t(`${d} recording start was cancelled during shutdown.`)}class zt{ffmpegClient;platformProvider;timer;startTimeoutMs;hwAccelCache=new Map;ffmpegProbeResult;constructor(o,d,c,p){this.ffmpegClient=o;this.platformProvider=d;this.timer=c;this.startTimeoutMs=p}async detectHardwareAccel(){let o=this.platformProvider(),d=o;if(this.hwAccelCache.has(d))return this.hwAccelCache.get(d);let c;if(o==="darwin")c=await this.detectVideoToolbox();else if(o==="linux")c=await this.detectLinuxHwAccel();else c={encoder:"libx264",available:!1,description:`Unsupported platform: ${o}`};return this.hwAccelCache.set(d,c),c}async detectVideoToolbox(){try{if((await this.listEncoders()).some((c)=>c.includes("h264_videotoolbox")))return{encoder:"h264_videotoolbox",available:!0,description:"macOS VideoToolbox (hardware acceleration)"};return{encoder:"libx264",available:!1,description:"VideoToolbox not available"}}catch(o){return e.warn(`[FfmpegVideo] Failed to detect VideoToolbox: ${o}`),{encoder:"libx264",available:!1,description:"VideoToolbox detection failed"}}}async detectLinuxHwAccel(){try{let o=await this.listEncoders();if(o.some((p)=>p.includes("h264_nvenc")))return{encoder:"h264_nvenc",available:!0,description:"NVIDIA NVENC (hardware acceleration)"};if(o.some((p)=>p.includes("h264_vaapi")))return{encoder:"h264_vaapi",available:!0,description:"VAAPI (hardware acceleration)"};return{encoder:"libx264",available:!1,description:"No hardware acceleration available (VAAPI/NVENC not found)"}}catch(o){return e.warn(`[FfmpegVideo] Failed to detect Linux HW accel: ${o}`),{encoder:"libx264",available:!1,description:"Hardware acceleration detection failed"}}}async listEncoders(){return(await this.probeFfmpeg()).encoders}async ensureFfmpegAvailable(o,d){try{await this.checkFfmpegVersion(o,d)}catch(c){throw te(d,"iOS"),new t(`FFmpeg is not available. Please install FFmpeg to use video recording.
8
+ macOS: brew install ffmpeg
9
+ Linux: apt-get install ffmpeg or yum install ffmpeg
10
+ Error: ${c}`)}}async checkFfmpegVersion(o,d){let{version:c}=await this.probeFfmpeg(o,d);e.debug(`[FfmpegVideo] Found FFmpeg ${c}`)}async probeFfmpeg(o,d){if(this.ffmpegProbeResult)return this.ffmpegProbeResult;let p=await(o===void 0?this.ffmpegClient.probe():Le(async(m)=>await this.ffmpegClient.probe({timeoutMs:Math.max(0,o-this.timer.now()),signal:m}),o,this.timer,{callerSignal:d,timeoutMessage:`Timed out checking FFmpeg availability within ${this.startTimeoutMs()}ms`}));return this.ffmpegProbeResult=p,p}}class Ne extends t{}function Ii(o){return o!==void 0&&o!==0}function Di(o){return Ii(o.exitCode)||o.signal!==void 0&&o.signal!==null}function Re(o,d,c,{tracker:p,outputPath:m}){return[d,m?`output: ${m}`:void 0,`command: ${o} ${c.join(" ")}`,`exitCode: ${p.exitState.exitCode??"null"}`,`signal: ${p.exitState.signal??"null"}`,`stderr:
11
+ ${p.stderr.join("").trim()||"(empty)"}`].filter((w)=>Boolean(w)).join(`
12
+ `)}function Ue(o,d,c,{tracker:p,outputPath:m}){return[o,m?`output: ${m}`:void 0,`command: ${d} ${c.join(" ")}`,`exitCode: ${p.exitState.exitCode??"null"}`,`signal: ${p.exitState.signal??"null"}`,`stderr:
13
+ ${p.stderr.join("").trim()||"(empty)"}`].filter((w)=>Boolean(w)).join(`
14
+ `)}function jt(o,d){if(Ii(d.exitState.exitCode))e.warn(`[FfmpegVideo] ${o} exited with code ${d.exitState.exitCode}: ${d.stderr.join("")}`);if(d.stderr.length>0)e.info(`[FfmpegVideo] ${o} stderr: ${d.stderr.join("")}`)}async function Fi(o,d,c,{recordingFileProbe:p,binaryPath:m}){let g=await p.size(o);if(g===null)throw new t(Re(m,"FFmpeg output file missing",d,{tracker:c,outputPath:o}));if(!g)throw new t(Re(m,"FFmpeg output file is empty",d,{tracker:c,outputPath:o}))}var _i=60000,Ha=500,Kt=1800000,Vi=600000;function Oi(o){return!o.resolution}function $i(o,d,c=Kt){if(Oi(o))return _i;if(d===void 0||!Number.isFinite(d))return c;let p=o.maxDurationSeconds&&o.maxDurationSeconds>0?Math.min(d,o.maxDurationSeconds):d;return Math.min(c,Math.max(_i,Math.ceil(Math.max(0,p)*Ha)))}var Ba=5000;function Hi(o,d){if(d===void 0)return o;let c=d-Ba;return c<=0?void 0:Math.min(o,c)}function st(o,d,c){let p=[];if(c.type==="pipe")p.push("-f","mp4","-i","pipe:0");else if(p.push("-i",c.path),Oi(o)){if(p.push("-c","copy"),p.push("-movflags","+faststart"),o.maxDurationSeconds&&o.maxDurationSeconds>0)p.push("-t",String(o.maxDurationSeconds));return p.push("-y"),p.push(o.outputPath),p}if(p.push("-r",String(o.fps)),o.resolution)p.push("-vf",`scale=${o.resolution.width}:${o.resolution.height}`);if(d.available)p.push("-c:v",d.encoder),e.info(`[FfmpegVideo] Using hardware acceleration: ${d.description}`);else p.push("-c:v","libx264"),p.push("-preset","ultrafast"),e.warn("[FfmpegVideo] Hardware acceleration unavailable, falling back to software encoding");if(p.push("-b:v",`${o.targetBitrateKbps}k`),p.push("-maxrate",`${o.targetBitrateKbps}k`),p.push("-bufsize",`${o.targetBitrateKbps*2}k`),p.push("-profile:v","baseline"),p.push("-level","3.0"),p.push("-pix_fmt","yuv420p"),p.push("-movflags","+faststart"),o.maxDurationSeconds&&o.maxDurationSeconds>0)p.push("-t",String(o.maxDurationSeconds));return p.push("-y"),p.push(o.outputPath),p}import{promises as Bi}from"fs";var La=15000,Na=100,Ua=1000,Wt={async size(o){try{return(await Bi.stat(o)).size}catch(d){if(d.code!=="ENOENT")throw u(d,`Failed to stat recording ${o}`);return e.debug(`src/features/video/FfmpegVideoProcessingBackend.ts recording file stat failed: ${d}`,d),null}}},Li={async remove(o){await Bi.rm(o,{force:!0})}};async function Ni(o,d={}){let c=d.probe??Wt,p=d.timer??i,m=d.timeoutMs??La,g=No(d.backoff??Q({initialDelayMs:Na,maxDelayMs:Ua})),w=p.now()+m,R=0,y=!1,P=null;for(;;){R++;let A=await c.size(o);if(A!==null){if(y=!0,A>0&&A===P)return A}if(P=A,p.now()>=w){let k=Ga(y,P);throw Error(`iOS recording file not ready at ${o} after ${m}ms (${R} probes): ${k}`)}await p.sleep(g.delayForAttempt(R))}}function Ga(o,d){return!o?"never appeared":d===null?"disappeared after appearing":d===0?"stayed empty (0 bytes)":`stopped at ${d} bytes but never stabilized`}class qt{ffmpegClient;timer;recordingFileProbe;captureFileRemover;codecProbe;detectHardwareAccel;constructor(o){this.ffmpegClient=o.ffmpegClient,this.timer=o.timer,this.recordingFileProbe=o.recordingFileProbe,this.captureFileRemover=o.captureFileRemover,this.codecProbe=o.codecProbe,this.detectHardwareAccel=o.detectHardwareAccel}async postProcessRecording(o){let d=o.capturePath;if(!d)throw new t("Missing iOS capture path for FFmpeg processing.");try{await Ni(d,{timer:this.timer,probe:this.recordingFileProbe})}catch(c){let p=r(c);throw new t(Ue(`iOS recording file missing at ${d}: ${p}`,"xcrun simctl",o.captureArgs??["io",o.config.device?.deviceId??"(unknown-device)","recordVideo",d],{tracker:o.captureTracker,outputPath:d}))}try{return await this.runFfmpegPostProcess(o,d),{kind:"processed"}}catch(c){if(c instanceof Ap)throw c;return this.keepRawCaptureAfterPostProcessFailure(o,d,c)}}async keepRawCaptureAfterPostProcessFailure(o,d,c){if(await this.removePartialPostProcessOutput(o.config.outputPath),!await this.isRawCaptureUsable(o,d))throw e.warn(`[FfmpegVideo] iOS post-processing failed and the raw capture is not playable: ${r(c)}`,c),c;e.warn(`[FfmpegVideo] iOS post-processing did not finish; keeping the unprocessed capture: ${r(c)}`,c);let p=r(c).split(`
15
+ `)[0];return{kind:"kept-raw",warning:`iOS ${c instanceof Ne?"post-processing ran out of time":"post-processing failed"} (${p}); returning the unprocessed simulator capture, which has not been remuxed or scaled to the requested resolution.`}}async removePartialPostProcessOutput(o){try{await this.captureFileRemover.remove(o)}catch(d){e.warn(`[FfmpegVideo] Failed to remove the partial post-processed output: ${r(d)}`,d)}}async isRawCaptureUsable(o,d){if(o.captureTracker.exitState.signal==="SIGKILL")return!1;try{return await this.codecProbe.codec(d)!==void 0}catch(c){return e.warn(`[FfmpegVideo] Could not probe the raw capture ${d}: ${r(c)}`,c),!1}}async runFfmpegPostProcess(o,d){let c=this.requestBoundedPostProcessBudgetMs(o.config),p=await this.detectHardwareAccel(),m=st(o.config,p,{type:"file",path:d});e.info(`[FfmpegVideo] Starting ffmpeg post-process: ${this.ffmpegClient.binaryPath} ${m.join(" ")}`);let{process:g}=this.ffmpegClient.start({args:m,context:"iOS recording post-processing",stdio:["ignore","pipe","pipe"]});try{await pk(g)}catch(R){throw u(R,"Failed to start FFmpeg post-processing")}let w=ld(g);if(o.ffmpegTracker=w,await zn(g,w.exitPromise,{timeoutMs:c,signal:null,timer:this.timer}),Di(w.exitState)){let{exitCode:R,signal:y}=w.exitState;if(y==="SIGKILL")throw new Ne(Re(this.ffmpegClient.binaryPath,`FFmpeg post-processing timed out after ${c}ms`,m,{tracker:w}));throw new t(Re(this.ffmpegClient.binaryPath,`FFmpeg post-processing failed (exit ${R??`signal ${y}`})`,m,{tracker:w}))}await Fi(o.config.outputPath,m,w,{recordingFileProbe:this.recordingFileProbe,binaryPath:this.ffmpegClient.binaryPath})}requestBoundedPostProcessBudgetMs(o){let d=oo(),c=$i(o,this.recordedSeconds(o),d?Kt:Vi),p=d?.getDeadlineMs?.(),m=Hi(c,p===void 0?void 0:p-this.timer.now());if(m===void 0)throw new Ne("the stop request has no time left for post-processing");return m}recordedSeconds(o){let d=Date.parse(o.startedAt);return Number.isNaN(d)?void 0:(this.timer.now()-d)/1000}}var Ui=async(o,d)=>{let c=x.getInstance(o);if(!await c.ensureConnected())return null;let m=await c.requestHierarchySyncWithoutObservationStreamPush(void 0,!1,void 0,d)?c.getScreenScaleMetadata():null;return m?{width:m.pixelWidth,height:m.pixelHeight}:null};var Gi=(o)=>new L(o);var zi=["Recording started"];function Ki(o){return Array.isArray(o)?o:[o]}function Jt(o,d){let c=o.stderr.join("");return Ki(d).some((p)=>c.includes(p))}async function Wi(o,d,c,p={}){let m=Ki(d),g=m.join(" or "),w=p.timer??i;if(Jt(o,m))return;if(o.exitState.endedAt!==void 0||o.process.exitCode!==null&&o.process.exitCode!==void 0||o.process.signalCode!==null&&o.process.signalCode!==void 0)throw Error(`Process exited before ${g}`);p.signal?.throwIfAborted();let R=o.process.stderr;if(!R)throw Error(`Cannot wait for ${g}: process stderr is not captured`);let y;await new Promise((P,A)=>{let k=!1,C=()=>{if(R.off("data",M),o.process.off("exit",O),o.process.off("error",F),p.signal?.removeEventListener("abort",j),y)w.clearTimeout(y)},T=(q)=>{if(k)return;k=!0,C(),q()},M=()=>{if(Jt(o,m))T(P)},O=()=>{T(()=>A(Error(`Process exited before ${g}`)))},F=(q)=>{T(()=>A(q))},j=()=>{T(()=>A(p.signal?.reason??Error("Recording start was cancelled.")))};R.on("data",M),o.process.once("exit",O),o.process.once("error",F),p.signal?.addEventListener("abort",j,{once:!0}),M(),y=w.setTimeout(()=>{if(Jt(o,m)){T(P);return}T(()=>A(Error(`Timed out waiting for ${g}`)))},c)})}async function qi(o,d,c,p){let m=Math.max(0,d-c.now()),g=new AbortController,w=c.setTimeout(()=>g.abort(Error(p)),m);try{return await l(o,{timer:c,signal:g.signal,label:"FFmpeg operation"})}finally{if(w)c.clearTimeout(w)}}function Xt(o){if(o&&o.exitCode===null&&!o.killed)o.kill("SIGKILL")}function Ji(o,d){throw te(d,"Android"),e.error(`[FfmpegVideo] Failed to start Android recording: ${o}`),u(o,"Failed to start Android recording")}var Xi=15000,Yi=2;var Yt={readStartToken:Sa,isRunning:(o)=>at(o,{debugLog:e.debug}),signal:(o,d)=>{process.kill(o,d)}},za=30000,ja=4000,Ka=1000,Qi=100;async function Zi(o,d={control:Yt,timer:i},c={mode:"user"}){let{control:p,timer:m}=d,g=o.hostPid,w=()=>{if(!p.isRunning(g))return!1;let y=p.readStartToken(g);if(y===void 0){if(!p.isRunning(g))return!1;throw new t(`Cannot prove orphan host recorder identity for pid ${g}; retry teardown when its start token can be read.`)}return y===o.hostStartToken},R=async(y)=>{for(let P=0;P<y;P+=Qi){if(!p.isRunning(g))return!0;await m.sleep(Qi)}return!p.isRunning(g)};try{if(!w())return;p.signal(g,"SIGINT");let y=c.mode==="background";if(await R(y?ja:za))return;if(y)throw new t(`Orphan host recorder pid ${g} teardown is unconfirmed; retry after MOV finalization completes.`);if(!w())return;if(p.signal(g,"SIGKILL"),await R(Ka))return;if(!w())return;throw new t(`Cannot confirm orphan host recorder pid ${g} stopped; retry teardown before starting another recording.`)}catch(y){throw e.warn(`[HostRecorderTeardown] Teardown unconfirmed for pid ${g}`,y),u(y,`Failed to stop orphan host recorder pid ${g}`)}}var Ja=30000,eo=2000,to=500,ro=3000,Xa=250;function Ya(o,d,c,p){return{kind:"ffmpeg",platform:"ios",captureTracker:o,capturePath:d,captureArgs:c,config:p}}class dt{adbFactory;iosRecordingStartTimeoutMs=Xi;iosRecordingStartMaxAttempts=Yi;simctlFactory;ffmpegClient;codecProbe;timer;captureFileRemover;liveScreenPixels;recordingFileProbe;readHostStartToken;hostRecorderControl;constructor(o=v,d={}){this.adbFactory=o;this.simctlFactory=d.simctlFactory??Gi,this.ffmpegClient=d.ffmpegClient??new pe,this.timer=d.timer??i,this.liveScreenPixels=d.liveScreenPixels??Ui;let c=dt.resolveHostSeams(d);this.capabilities=new zt(this.ffmpegClient,c.platformProvider,this.timer,()=>this.iosRecordingStartTimeoutMs),this.codecProbe=c.codecProbe,this.captureFileRemover=c.captureFileRemover,this.recordingFileProbe=c.recordingFileProbe,this.readHostStartToken=c.readHostStartToken,this.hostRecorderControl=c.hostRecorderControl}static resolveHostSeams(o){return{platformProvider:o.platformProvider??Wa,codecProbe:o.codecProbe??ae,captureFileRemover:o.captureFileRemover??Li,recordingFileProbe:o.recordingFileProbe??Wt,readHostStartToken:o.readHostStartToken??Sa,hostRecorderControl:o.hostRecorderControl??Yt}}describeCaptureTeardown(o){let d=o.backendHandle;if(d?.kind!=="ffmpeg"||d.platform!=="ios")return;if(d.captureTeardown)return d.captureTeardown;let c=d.captureTracker.process.pid,p=d.config.device?.deviceId;try{let m=c===void 0?void 0:this.readHostStartToken(c);if(!c||!m||!p){e.warn("[FfmpegVideo] Cannot persist iOS host recorder teardown identity: pid, start token or udid unreadable");return}return d.captureTeardown={kind:"ios-host-recorder",hostPid:c,hostStartToken:m,udid:p,capturePath:d.capturePath},d.captureTeardown}catch(m){e.warn("[FfmpegVideo] Cannot read iOS host recorder start token",m);return}}async forceStopOrphanedCapture(o,d,c){if(d.kind!=="ios-host-recorder")throw new t("FFmpeg orphan teardown requires an iOS host recorder identity.");await Zi(d,{control:this.hostRecorderControl,timer:this.timer},c)}async start(o){let d=o.device;if(!d)throw new t("Device is required to start video recording.");let c=Oe(d.platform),p=c?this.timer.now()+this.iosRecordingStartTimeoutMs:void 0;if(await this.ensureFfmpegAvailable(p,c?o.abortSignal:void 0),d.platform==="android")return this.startAndroid(d,o);if(c)return this.startIos(d,o,p);throw new t(`Unsupported platform for video recording: ${d.platform}`)}async stop(o){let d=o.backendHandle;if(!d)throw Error("Missing backend handle for FFmpeg video recording.");await zn(d.captureTracker.process,d.captureTracker.exitPromise,{timeoutMs:d.platform==="ios"?Ja:Pp,timer:this.timer});try{let{outputPath:c,warnings:p}=await this.finalizeCaptureOutput(d,o),m=await this.recordingFileProbe.size(c);if(!m)throw new t("The finalized recording is missing or contains zero bytes.");let g=await this.codecProbe.codec(c),w=await ye(this.codecProbe,c);if(c!==d.capturePath)await this.removeRawCapture(d.capturePath);if(jt("capture",d.captureTracker),d.ffmpegTracker)jt("ffmpeg",d.ffmpegTracker);return{recordingId:o.recordingId,outputPath:c,startedAt:o.startedAt,endedAt:d.captureTracker.exitState.endedAt??new Date().toISOString(),sizeBytes:m,codec:g,videoDurationMs:w,...p.length>0&&{warnings:p}}}catch(c){throw await this.buildFinalizationError(d,c)}}async finalizeCaptureOutput(o,d){if(o.platform==="ios"){let c=await this.postProcessRecording(o);return c.kind==="kept-raw"?{outputPath:o.capturePath??d.outputPath,warnings:[c.warning]}:{outputPath:d.outputPath,warnings:[]}}if(o.ffmpegTracker)await zn(o.ffmpegTracker.process,o.ffmpegTracker.exitPromise,{timer:this.timer});return{outputPath:d.outputPath,warnings:[]}}postProcessRecording(o){return new qt({ffmpegClient:this.ffmpegClient,timer:this.timer,recordingFileProbe:this.recordingFileProbe,captureFileRemover:this.captureFileRemover,codecProbe:this.codecProbe,detectHardwareAccel:()=>this.detectHardwareAccel()}).postProcessRecording(o)}async removeRawCapture(o){if(!o)return;try{await this.captureFileRemover.remove(o)}catch(d){e.warn(`[FfmpegVideo] Failed to remove raw recording: ${r(d)}`,d)}}async buildFinalizationError(o,d){let c=await this.cleanupStartingTrackers([o.ffmpegTracker].filter((m)=>m!==void 0));if(c)e.warn(`[FfmpegVideo] Failed to clean up recording encoder: ${r(c)}`,c);let p=o.capturePath;if(p)try{let m=await this.recordingFileProbe.size(p);if(m===null||m===0)await this.captureFileRemover.remove(p),p=void 0}catch(m){e.warn(`[FfmpegVideo] Could not inspect or remove raw recording; retaining ${p}: ${r(m)}`,m)}return new J(p?`Capture exited but finalization failed. Raw capture retained at ${p}. ${r(d)}`:`Capture exited but the recording produced no usable video (zero bytes / finalization failed). Start a new recording. ${r(d)}`,{cause:d,retainedCapturePath:p})}async forceStop(o){let d=o.backendHandle;if(!d)throw Error("Missing backend handle for FFmpeg video recording.");let c=[d.ffmpegTracker,d.captureTracker].filter((g)=>g!==void 0),m=(await Promise.allSettled(c.map(async(g)=>{await zn(g.process,g.exitPromise,{timeoutMs:0,forceKillTimeoutMs:Pp,signal:"SIGKILL",timer:this.timer})}))).find((g)=>g.status==="rejected");if(m)throw m.reason}async startAndroid(o,d){let c=this.adbFactory.create(o),p=["exec-out","screenrecord"];if(d.physicalDisplayId!==void 0)p.push("--display-id",d.physicalDisplayId);p.push("-"),e.info(`[FfmpegVideo] Starting screenrecord: ${p.join(" ")}`);let m=await c.spawn(p),g=ld(m),w;if(d.physicalDisplayId!==void 0&&o.apiLevel===void 0){if(await j0(g,this.timer))w="Android screenrecord rejected --display-id; recording the default display.",e.warn(`[FfmpegVideo] ${w}`),m=await c.spawn(["exec-out","screenrecord","-"]),g=ld(m)}let R,y,P=()=>{Xt(m),Xt(R)},A=()=>P();d.abortSignal?.addEventListener("abort",A,{once:!0});try{te(d.abortSignal,"Android"),m.stderr.on("data",(F)=>{let j=F.toString();e.info(`[FfmpegVideo] screenrecord stderr: ${j.trim()}`)});let k=0;m.stdout.on("data",(F)=>{if(k+=F.length,k%102400===0)e.info(`[FfmpegVideo] Received ${k} bytes from screenrecord`)}),e.info("[FfmpegVideo] screenrecord process spawned");let C=await this.detectHardwareAccel();te(d.abortSignal,"Android");let T=st(d,C,{type:"pipe"});te(d.abortSignal,"Android"),e.info(`[FfmpegVideo] Starting ffmpeg: ${this.ffmpegClient.binaryPath} ${T.join(" ")}`);let M=this.ffmpegClient.start({args:T,context:"Android screen recording encoder",stdio:["pipe","pipe","pipe"]});R=M.process,y=M.tracker,te(d.abortSignal,"Android"),this.ffmpegClient.pipe({source:m.stdout,destination:R.stdin,context:"Android screen recording",processes:[m,R]}),e.info("[FfmpegVideo] Piped screenrecord stdout to ffmpeg stdin"),await pk(R),e.info("[FfmpegVideo] ffmpeg process spawned");let O={kind:"ffmpeg",platform:"android",captureTracker:g,ffmpegTracker:y,config:d};return{recordingId:d.recordingId,outputPath:d.outputPath,startedAt:d.startedAt,warning:w,physicalDisplayId:w?void 0:d.physicalDisplayId,backendHandle:O}}catch(k){P();let C=await this.cleanupStartingTrackers([g,...y?[y]:[]]);if(C)throw new X(`Failed to clean up Android recording after startup failed: ${r(C)}`,{recordingId:d.recordingId,outputPath:d.outputPath,startedAt:d.startedAt,backendHandle:{kind:"ffmpeg",platform:"android",captureTracker:g,ffmpegTracker:y,config:d}},{cause:k});return Ji(k,d.abortSignal)}finally{d.abortSignal?.removeEventListener("abort",A)}}async cleanupStartingTrackers(o){let c=(await Promise.allSettled(o.map((p)=>zn(p.process,p.exitPromise,{timeoutMs:0,forceKillTimeoutMs:Pp,signal:"SIGKILL",timer:this.timer})))).map((p,m)=>({result:p,tracker:o[m]})).filter((p)=>p.result.status==="rejected"&&p.tracker.process.pid!==void 0).map((p)=>p.result.reason);return c.length>0?AggregateError(c,"Failed to reap recording startup processes"):void 0}async recordingDisplayArg(o,d,c){let p=c-this.timer.now();if(p<=0||d.platform==="tvos")return;let m;try{let g=Math.min(p,eo);m=await Le((w)=>o.enumerateDisplays(d.deviceId,g,w),this.timer.now()+g,this.timer,{timeoutMessage:"Timed out enumerating iOS simulator recording displays"})}catch(g){e.warn(`Unable to enumerate iOS simulator recording displays: ${r(g)}`);return}if(m.length===0){e.warn(`Unable to select the live recording display for ${d.name}: no Integrated screens found`);return}if(m.length===1)return;try{let g=Math.min(c-this.timer.now(),eo);if(g<=0){e.warn(`Unable to select the live recording display for ${d.name}: start deadline expired`);return}let w=await Le(()=>this.liveScreenPixels(d,g),this.timer.now()+g,this.timer,{timeoutMessage:"Timed out probing the live iOS simulator display"}),R=w?Rz(m,w.width,w.height):null;if(R?.name)return R.name;e.warn(`Unable to select the live recording display for ${d.name}: no uniquely matching Integrated screen with a Device Name`);return}catch(g){e.warn(`Unable to select the live recording display for ${d.name}: ${r(g)}`);return}}recordingCommandArgs(o,d,c){return["io",o,"recordVideo",...d?[`--display=${d}`]:[],c]}async startIos(o,d,c){if(te(d.abortSignal,"iOS"),this.timer.now()>=c)throw new t(`Failed to start iOS recording within ${this.iosRecordingStartTimeoutMs}ms.`);let p=this.simctlFactory(o),m=await Le(async(A)=>await p.isAvailable({timeoutMs:Math.max(0,c-this.timer.now()),signal:A}),c,this.timer,{callerSignal:d.abortSignal,timeoutMessage:`Timed out checking simctl availability within ${this.iosRecordingStartTimeoutMs}ms`});if(te(d.abortSignal,"iOS"),this.timer.now()>=c)throw new t(`Failed to start iOS recording within ${this.iosRecordingStartTimeoutMs}ms.`);if(!m)throw new t("simctl is not available. Install Xcode command line tools.");let g=await this.recordingDisplayArg(p,o,c),w=`${d.recordingId}-raw`,R=Math.max(1,this.iosRecordingStartMaxAttempts),y=[],P;for(let A=1;A<=R;A++){if(te(d.abortSignal,"iOS"),c-this.timer.now()<=0)break;let C=qa.join(d.outputDirectory,A===1?`${w}.mov`:`${w}-attempt${A}.mov`),T=this.recordingCommandArgs(o.deviceId,g,C);if(P!==void 0)await this.removeStaleCapture(P);P=C;let M=await p.startCommandArgs(T,{stdio:["ignore","ignore","pipe"]}),O=ld(M),F=!1;try{te(d.abortSignal,"iOS"),await qi(pk(M),c,this.timer,`Timed out waiting for simctl to spawn within ${this.iosRecordingStartTimeoutMs}ms`),F=!0;let j=R-A+1,q=Math.max(0,c-this.timer.now()),_e=Math.min(to,q)*j,Ve=Math.max(0,Math.floor((q-_e)/j));await Wi(O,zi,Ve,{signal:d.abortSignal,timer:this.timer});let he=Ya(O,C,T,d);return{recordingId:d.recordingId,outputPath:d.outputPath,startedAt:d.startedAt,liveCapturePath:C,recordedPanel:xi(o,d.recordingPanel,g),backendHandle:he}}catch(j){let q=await this.describeFailedIosStartAttempt({attempt:A,captureTracker:O,config:d,device:o,error:j,maxAttempts:R,simctl:p,spawned:F,startDeadlineMs:c});if(y.push(q.description),q.cleanupError){let _e=Ue(`Failed to clean up iOS recording after startup failed: ${q.description}`,"xcrun simctl",T,{tracker:O});throw new X(_e,{recordingId:d.recordingId,outputPath:d.outputPath,startedAt:d.startedAt,backendHandle:{kind:"ffmpeg",platform:"ios",captureTracker:O,capturePath:C,config:d}},{cause:q.cleanupError})}if(A>=R)throw new t(Ue(`Failed to start iOS recording after ${R} attempt(s): ${y.join(" | ")}`,"xcrun simctl",T,{tracker:O}))}}throw new t(`Failed to start iOS recording within ${this.iosRecordingStartTimeoutMs}ms`+(y.length>0?`: ${y.join(" | ")}`:"."))}async removeStaleCapture(o){try{await this.captureFileRemover.remove(o)}catch(d){e.warn(`[FfmpegVideo] Failed to remove stale iOS capture ${o}: ${r(d)}`,d)}}async describeFailedIosStartAttempt(o){let d=await this.cleanupFailedIosStart(o.captureTracker,o.startDeadlineMs),c=`attempt ${o.attempt}/${o.maxAttempts}: ${o.error}`;if(!o.spawned&&o.captureTracker.process.pid===void 0)throw new t(`Failed to start iOS recording: ${o.error}`);if(d)return{description:`${c}; cleanup failed: ${r(d)}`,cleanupError:d};if(o.config.abortSignal?.aborted)throw new t("iOS recording start was cancelled during shutdown.");if(!o.spawned)throw new t(`Failed to start iOS recording: ${o.error}`);let p=await this.diagnoseFailedIosStart(o.simctl,o.device,o.startDeadlineMs,{isFinalAttempt:o.attempt>=o.maxAttempts,abortSignal:o.config.abortSignal});return e.warn(`[FfmpegVideo] iOS recording start attempt ${o.attempt}/${o.maxAttempts} failed: ${o.error} (${p})`),{description:`${c}; ${p}`}}async cleanupFailedIosStart(o,d){let c=Math.max(0,Math.min(to,d-this.timer.now()));try{await zn(o.process,o.exitPromise,{timeoutMs:c,forceKillTimeoutMs:0,timer:this.timer,signal:"SIGKILL"});return}catch(p){return p}}async diagnoseFailedIosStart(o,d,c,{isFinalAttempt:p,abortSignal:m}){if(p)return await this.captureSimulatorDiagnostics(o,d,ro,m);let g=Math.max(0,Math.min(Xa,c-this.timer.now()));return g>0?await this.captureSimulatorDiagnostics(o,d,g,m):"simulator state unknown: start budget reserved for the pending retry"}async captureSimulatorDiagnostics(o,d,c,p){let m=Math.min(ro,c),g=this.timer.now();try{let w=await ys(g+m,this.timer,p,async(y,P)=>await o.executeCommandArgs(["list","devices",d.deviceId],P,y)),R=(w.stdout||w.stderr||"").replace(/\s+/g," ").trim();return R?`simulator state: ${R.slice(0,500)}`:"simulator state: (empty)"}catch(w){if(p?.aborted)return e.debug("[FfmpegVideo] Simulator state probe cancelled by shutdown"),"simulator state probe cancelled by shutdown";let R=r(w);if(e.debug(`[FfmpegVideo] Simulator state probe failed: ${R}`),this.timer.now()-g>=m)return`simulator state unknown: state probe exceeded its ${m}ms budget (host may be loaded): ${R}`;return`simulator state unavailable: ${R}`}}capabilities;detectHardwareAccel(){return this.capabilities.detectHardwareAccel()}ensureFfmpegAvailable(o,d){return this.capabilities.ensureFfmpegAvailable(o,d)}}class Qt{executor;timeoutMs;constructor(o=new re,d=15000){this.executor=o;this.timeoutMs=d}async list(o,d){let c=await this.executor.executeCommand(o,["--list-devices"],{timeoutMs:this.timeoutMs,signal:d});return io(c.stdout)}}function io(o){let d;try{d=JSON.parse(o)}catch(p){throw new t(`screen-capture-helper --list-devices returned unparseable output: ${r(p)}`)}let c=d?.devices;if(!Array.isArray(c))throw new t("screen-capture-helper --list-devices returned no `devices` array; the helper may be out of date.");return c.map((p)=>p).filter((p)=>typeof p?.uniqueID==="string"&&p.uniqueID.length>0).map((p)=>({uniqueID:p.uniqueID,localizedName:p.localizedName??p.uniqueID,modelID:p.modelID??"",manufacturer:p.manufacturer??""}))}var no=30000,Zt=20,ao=8388608;function er(o){return o.replace(/-/g,"").toLowerCase()}function tr(o){let{resolution:d}=o;return{codec:"h264",bitrate:{kind:"explicitBps",bps:o.targetBitrateKbps*1000},...d?{maxLongSide:Math.max(2,Math.max(d.width,d.height)&-2)}:{}}}function rr(o){let d=["-use_wallclock_as_timestamps","1","-f","h264","-i","pipe:0","-c:v","copy","-fps_mode","passthrough","-movflags","+faststart"];if(o.maxDurationSeconds&&o.maxDurationSeconds>0)d.push("-t",String(o.maxDurationSeconds));return d.push("-y",o.outputPath),d}class ir{ffmpegClient;helperProvider;deviceLister;createHelper;platformProvider;fileSize;encoderFinalizeTimeoutMs;now;timer;env;helperPathExists;constructor(o={}){this.ffmpegClient=o.ffmpegClient??new pe,this.helperProvider=o.helperProvider??qc.getInstance(),this.deviceLister=o.deviceLister??new Qt,this.createHelper=o.createHelper??((d)=>new Yy(d)),this.platformProvider=o.platformProvider??(()=>process.platform),this.fileSize=o.fileSize??Bz,this.encoderFinalizeTimeoutMs=o.encoderFinalizeTimeoutMs??no,this.now=o.now??Date.now,this.timer=o.timer??i,this.env=o.env??process.env,this.helperPathExists=o.helperPathExists}async start(o){let d=o.device;if(!d)throw new t("Device is required to start video recording.");if(this.platformProvider()!=="darwin")throw new t("Physical iOS video recording requires macOS: the capture path uses CoreMediaIO/AVFoundation through the screen-capture-helper, which only exists on macOS.");let{abortSignal:c}=o;me(c),await this.ensureFfmpegAvailable(c),me(c);let p=await this.resolveHelperBinary(c);me(c);let m=await this.resolveCaptureUniqueId(p,d,c);me(c);let g={framesWritten:0,framesDropped:0,stopRequested:!1,helperExitWasRequested:!1,helperStderr:[]},w=this.createHelper({binaryPath:p,target:{kind:"device",deviceId:m,encode:tr(o)}});w.on("stderr",(y)=>{if(g.helperStderr.push(y),g.helperStderr.length>Zt)g.helperStderr.shift()}),w.on("malformed",(y)=>{if(g.helperStderr.push(`malformed record from the helper (${y.reason})
16
+ `),g.helperStderr.length>Zt)g.helperStderr.shift()}),w.on("exit",(y)=>{g.helperExit=y,g.helperExitWasRequested=g.stopRequested}),w.on("error",(y)=>{g.helperError??=y instanceof Error?y:Error(String(y));let P=y;if(P.syscall?.startsWith("spawn ")&&["ENOENT","EACCES","EAGAIN","EMFILE","ENFILE"].includes(P.code??"")&&!g.encoder&&!g.helperExit)g.helperSpawnError??=y;e.warn(`[IosPhysicalVideo] capture helper error: ${r(y)}`)}),w.on("encodedVideo",(y)=>this.onEncodedVideo(y,g,o));let R={recordingId:o.recordingId,outputPath:o.outputPath,startedAt:o.startedAt,effectiveConfig:o,backendHandle:{kind:"ios-physical",helper:w,state:g,config:o}};try{await ct(Promise.resolve(w.start()),c)}catch(y){try{await this.abandonStartedCapture(w,g)}catch(P){throw new X(`Failed to clean up physical iOS recording after startup failed: ${r(P)}`,R,{cause:y})}throw y}if(c?.aborted){try{await this.abandonStartedCapture(w,g)}catch(y){throw new X(`Failed to clean up physical iOS recording after startup was cancelled: ${r(y)}`,R)}me(c)}return R}async stop(o){let d=o.backendHandle;if(!d||d.kind!=="ios-physical")throw Error("Missing backend handle for physical iOS video recording.");let{helper:c,state:p}=d;p.stopRequested=!0;let m=this.now();try{if(await c.stop(),p.helperSpawnError)throw new t(this.buildNoFramesMessage(p),{cause:p.helperSpawnError});if(!p.helperExit)throw new Ap("Physical iOS capture helper exit was not observed; it may still be running.");return await this.finalizeStoppedRecording(o,d,m)}catch(g){if(!p.helperExit&&!p.helperSpawnError)throw g;throw await this.buildFinalizationError(p,g)}}async buildFinalizationError(o,d){try{if(o.encoder&&o.encoderTracker)await zn(o.encoder,o.encoderTracker.exitPromise,{timeoutMs:0,signal:"SIGKILL",timer:this.timer})}catch(c){e.warn(`[IosPhysicalVideo] Failed to clean up recording encoder: ${r(c)}`,c)}return new J(o.helperSpawnError?`Physical iOS capture never started. Start a new recording. ${this.buildNoFramesMessage(o)}`:`Physical iOS capture exited but the recording produced no usable video (zero bytes / finalization failed). Start a new recording. ${r(d)}`,{cause:o.helperSpawnError??d})}async finalizeStoppedRecording(o,d,c){let{state:p}=d,m=p.encoder;if(!m)throw new t(this.buildNoFramesMessage(p));if(m.stdin?.end(),p.encoderTracker)await zn(m,p.encoderTracker.exitPromise,{timeoutMs:this.encoderFinalizeTimeoutMs,signal:null,timer:this.timer});this.assertEncoderSucceeded(p,o.outputPath),this.assertHelperSucceeded(p,o.outputPath);let g=await this.fileSize(o.outputPath);if(!g)throw new t("The finalized physical iOS recording is missing or contains zero bytes.");return this.logCaptureAccounting(p,o),{recordingId:o.recordingId,outputPath:o.outputPath,startedAt:o.startedAt,endedAt:new Date(c).toISOString(),sizeBytes:g,codec:"h264"}}async forceStop(o){let d=o.backendHandle;if(!d||d.kind!=="ios-physical")throw Error("Missing backend handle for physical iOS video recording.");d.state.stopRequested=!0;let c=[d.helper.stop()],p=d.state.encoder;if(p&&d.state.encoderTracker)c.push(zn(p,d.state.encoderTracker.exitPromise,{timeoutMs:0,forceKillTimeoutMs:Pp,signal:"SIGKILL",timer:this.timer}));else if(p&&p.exitCode===null)p.kill("SIGKILL");let g=(await Promise.allSettled(c)).filter((w)=>w.status==="rejected").map((w)=>w.reason);if(g.length>0)throw AggregateError(g,`Failed to fully stop physical iOS recording: ${g.map(r).join("; ")}`)}onEncodedVideo(o,d,c){if(d.stopRequested&&!d.encoder)return;if(!d.encoder){if(!o.keyframe){d.framesDropped+=1;return}this.startEncoder(d,c)}let p=d.encoder?.stdin;if(!p||p.writableEnded){d.framesDropped+=1;return}if(p.writableLength>ao)d.awaitingKeyframe=!0;if(d.awaitingKeyframe){if(!o.keyframe||p.writableNeedDrain){d.framesDropped+=1;return}d.awaitingKeyframe=!1}p.write(o.payload),d.framesWritten+=1}startEncoder(o,d){let c=rr(d);e.info(`[IosPhysicalVideo] starting muxer: ${this.ffmpegClient.binaryPath} ${c.join(" ")}`);let p=this.ffmpegClient.start({args:c,context:"physical iOS screen recording muxer",stdio:["pipe","pipe","pipe"]});o.encoder=p.process,o.encoderTracker=p.tracker,p.process.stdin?.on("error",(m)=>{e.debug(`[IosPhysicalVideo] muxer stdin error (expected on muxer exit): ${m}`)})}logCaptureAccounting(o,d){if(e.info(`[IosPhysicalVideo] recording ${d.recordingId} wrote ${o.framesWritten} encoded record(s) to ${d.outputPath}.`),o.framesDropped>0)e.debug(`[IosPhysicalVideo] dropped ${o.framesDropped} encoded record(s) that arrived before the first keyframe or while the muxer stalled.`)}async ensureFfmpegAvailable(o){try{await ct(this.ffmpegClient.probe({signal:o}),o)}catch(d){throw me(o),new t(`FFmpeg is not available. Please install FFmpeg to use video recording.
17
+ macOS: brew install ffmpeg
18
+ Error: ${r(d)}`)}}assertEncoderSucceeded(o,d){let c=o.encoderTracker?.exitState;if(!c)return;if(!(c.exitCode!==void 0&&c.exitCode!==null&&c.exitCode!==0||Boolean(c.signal)))return;let m=o.encoderTracker?.stderr.join("").trim();throw new t(`FFmpeg failed to finalize the physical iOS recording at ${d} (exitCode: ${c.exitCode??"null"}, signal: ${c.signal??"null"}).`+(m?`
19
+ ffmpeg: ${m}`:""))}assertHelperSucceeded(o,d){let c=o.helperStderr.join("").trim(),p=c?`
20
+ screen-capture-helper: ${c}`:"";if(o.helperError)throw new t(`The iOS capture helper failed during the recording, so ${d} is truncated: ${r(o.helperError)}.${p}`);let m=o.helperExit;if(!m||o.helperExitWasRequested)return;let g=m.signal!==null?`signal ${m.signal}`:`exit code ${m.code??"null"}`;throw new t(`The iOS capture helper terminated with ${g} before the recording was stopped, so ${d} is truncated. The device was most likely unplugged or lost its capture session.${p}`)}async abandonStartedCapture(o,d){d.stopRequested=!0,await o.stop();let c=[],p=d.encoder;if(p&&d.encoderTracker)c.push(zn(p,d.encoderTracker.exitPromise,{timeoutMs:0,forceKillTimeoutMs:Pp,signal:"SIGKILL",timer:this.timer}));else if(p&&p.exitCode===null)p.kill("SIGKILL");let g=(await Promise.allSettled(c)).filter((w)=>w.status==="rejected").map((w)=>w.reason);if(g.length>0)throw AggregateError(g,`Failed to abandon physical iOS recording start: ${g.map(r).join("; ")}`)}buildNoFramesMessage(o){let d=o.helperStderr.join("").trim();if(o.helperError)return`The iOS capture helper could not be run, so no recording was produced: ${r(o.helperError)}.`+(d?`
21
+ screen-capture-helper: ${d}`:"");return`No frames were captured from the physical iOS device${o.helperExit?` (helper exited code=${o.helperExit.code??"null"} signal=${o.helperExit.signal??"null"})`:""}, so no recording was produced. Connect the device over USB and accept the Trust This Computer prompt, then retry.`+(d?`
22
+ screen-capture-helper: ${d}`:"")}async resolveHelperBinary(o){let d=gS(this.env);if(d)return yS(d,{env:this.env,exists:this.helperPathExists});let c=await ct(this.helperProvider.ensure(o),o);if(!c)throw new t(`Physical iOS video recording requires the signed screen-capture-helper from the matching GitHub Release, which could not be resolved. For local development, build it with \`bash scripts/ios/swift-build.sh\` and set ${dA} to the resulting absolute path.`);return c}async resolveCaptureUniqueId(o,d,c){let p=await ct(this.deviceLister.list(o,c),c);if(p.length===0)throw new t(`No muxed external capture devices found for ${d.deviceId}. Connect the iPhone/iPad over USB, accept the Trust This Computer prompt, and make sure it is not already being captured by QuickTime.`);let m=p.find((y)=>y.uniqueID===d.deviceId);if(m)return m.uniqueID;let g=er(d.deviceId),w=p.find((y)=>er(y.uniqueID)===g);if(w)return w.uniqueID;if(p.length===1)return e.warn(`[IosPhysicalVideo] capture device uniqueID ${p[0].uniqueID} does not match ${d.deviceId}; using the only attached device.`),p[0].uniqueID;let R=p.map((y)=>`${y.uniqueID} (${y.localizedName})`).join(", ");throw new t(`Could not match device ${d.deviceId} to an AVFoundation capture device. Attached: ${R}. Disconnect the other devices, or record the matching UDID.`)}}function me(o){if(o?.aborted)throw new t("Physical iOS recording start was cancelled during shutdown.")}async function ct(o,d){if(me(d),!d)return o;let c=new AbortController,p=()=>c.abort(new t("Physical iOS recording start was cancelled during shutdown."));d.addEventListener("abort",p,{once:!0});try{return await l(o,{timer:i,signal:c.signal,label:"Physical iOS recording start"})}finally{if(p)d.removeEventListener("abort",p)}}function co(o){return new t(`Unsupported platform for video recording: ${o}`)}var Qa=1000,lo=Error("Video capture graceful exit deadline elapsed"),or=5,Za=300,lt=3,es=500,uo=4000,rs=1e4;function is(o){return F1(o.targetBitrateKbps,o.maxThroughputMbps)}class nr{adbFactory;timer;codecProbe;constructor(o=v,d=i,c=ae){this.adbFactory=o;this.timer=d;this.codecProbe=c}async start(o){let d=o.device;if(!d)throw new t("Device is required to start video recording.");if(d.platform==="android")return this.startAndroid(d,o);if(d.platform==="ios")throw new t("iOS video recording is not handled by PlatformVideoCaptureBackend. Route iOS devices through FfmpegVideoProcessingBackend (HybridVideoCaptureBackend does this automatically).");throw co(d.platform)}describeCaptureTeardown(o){let d=o.backendHandle;if(d?.kind!=="android")return;return{kind:"android-screenrecord",devicePid:d.devicePid?.pid,deviceTempPath:d.deviceTempPath}}async forceStopOrphanedCapture(o,d){let c=d.devicePid;if(c===void 0)throw new t(`Cannot identify orphan recorder on ${o.deviceId} without a pid.`);let p=this.adbFactory.create(o);try{if(await this.signalDeviceRecorder(p,d,c,{signal:9,orphan:!0})==="not-ours")return;if(!await this.confirmDeviceRecorderPidExited(p,d,c))throw new t(`Cannot confirm orphan recorder pid ${c} stopped on ${o.deviceId}.`)}catch(m){throw u(m,`Failed to stop orphan recorder pid ${c} on ${o.deviceId}`)}}async stop(o){let d=o.backendHandle;if(!d||d.kind!=="android")throw Error("Missing backend handle for video recording.");e.info(`[VideoCapture] Stopping recording ${o.recordingId}`),this.markStopRequested(d);let c=this.adbFactory.create(d.device);await this.requestDeviceRecorderStop(c,d);try{await l(d.exitPromise,{timer:this.timer,timeoutMs:HA.gracefulExit,label:"Video capture graceful exit",timeoutError:()=>lo,onTimeout:()=>{if(d.process.exitCode===null&&!d.process.killed)e.info("[VideoCapture] Sending SIGINT to host adb after pkill wait"),d.process.kill("SIGINT")}})}catch(g){if(g!==lo)throw g}if(d.process.exitCode===null)e.warn("[VideoCapture] screenrecord still running after SIGINT; sending SIGKILL"),d.process.kill("SIGKILL"),await d.exitPromise;else await d.exitPromise;e.info(`[VideoCapture] Process exited with code: ${d.exitState.exitCode}, signal: ${d.exitState.signal}`);let p=this.adbFactory.create(d.device),m=!1;try{try{if(e.info("[VideoCapture] Waiting 1 second for file to finalize on device"),await this.timer.sleep(Qa),m=!await this.waitForDeviceFileToFinalize(p,d),m)throw new t(`Device recording ${o.recordingId} did not finish writing before the finalization deadline. The device copy was retained; try stopping the recording again before retrying the pull.`);e.info(`[VideoCapture] Pulling file from device: ${d.deviceTempPath} -> ${o.outputPath}`),await this.pullRecordingFileWithRetry(p,["pull",d.deviceTempPath,o.outputPath],o.recordingId)}finally{await this.cleanupDeviceRecording(p,d,m)}let g=await Bz(o.outputPath);if(!g)throw new t("The pulled recording is missing or contains zero bytes.");e.info(`[VideoCapture] Final file size: ${g} bytes`),e.debug(`[VideoCapture] Output file at ${o.outputPath}`);let w=await this.codecProbe.codec(o.outputPath),R=await ye(this.codecProbe,o.outputPath);if(d.stderr.length>0)e.info(`[VideoCapture] Stderr output: ${K0(d.stderr)}`);let y=this.recordingWarnings(d,g,R);return{recordingId:o.recordingId,outputPath:o.outputPath,startedAt:o.startedAt,endedAt:d.exitState.endedAt??new Date().toISOString(),sizeBytes:g,codec:w,videoDurationMs:R,...y.length>0&&{warnings:y}}}catch(g){throw this.finalizationError(d,m,g)}}recordingWarnings(o,d,c){let p=[],m=this.earlyExitDescription(o);if(m)p.push(`The Android recorder ${m} before the stop was requested; the recording may end sooner than requested.`);let g=(o.stopRequestedAtMs??this.timer.now())-o.launchedAtMs;if(d<1e4||c!==void 0&&g>=3000&&c<g/2)p.push("The Android video is much shorter than the recording window and should not be used as evidence.");return p}markStopRequested(o){if(!o.stopRequested)o.stopRequested=!0,o.stopRequestedAtMs=this.timer.now(),o.exitedBeforeStop=b_(o.exitState,o.stderr)!==void 0}earlyExitDescription(o){return o.exitedBeforeStop?b_(o.exitState,o.stderr):void 0}finalizationError(o,d,c){let p=this.earlyExitDescription(o);if(p&&!d)return e.warn(`[VideoCapture] No usable recording after the recorder exited early: ${r(c)}`,c),new J(`Android recorder ${p}, before the stop was requested, so the recording produced no usable video. Start a new recording.`,{cause:c,retainOwnership:!1});return new J(d?`Android capture exited but finalization failed: ${r(c)}`:`Android capture exited but the recording produced no usable video (zero bytes / finalization failed). Start a new recording. ${r(c)}`,{cause:c,retainOwnership:d})}async requestDeviceRecorderStop(o,d){let c=d.devicePid?.pid;try{if(c!==void 0){let m=await this.signalDeviceRecorder(o,d,c,{signal:2});e.info(`[VideoCapture] Device recorder pid ${c} SIGINT: ${m}`);return}let p=await o.executeCommand("shell pkill -2 screenrecord",HA.pkillOpts);e.info(`[VideoCapture] Device pkill -2 screenrecord completed (out=${p.stdout.trim().slice(0,120)} err=${p.stderr.trim().slice(0,160)})`)}catch(p){e.warn(`[VideoCapture] Device-side SIGINT of screenrecord failed; will rely on host SIGINT: ${r(p)}`)}}async signalDeviceRecorder(o,d,c,{signal:p,orphan:m=!1}){let g;try{g=await this.readDeviceRecorderCmdline(o,c)}catch(R){if(!m)throw R;throw new jc(`Cannot reach device to inspect orphan recorder pid ${c}; retry when the device answers.`,{cause:R})}if(g.error){if(!m)throw u(g.error,`Could not inspect recorder pid ${c}`);throw new jc(`Cannot reach device to inspect orphan recorder pid ${c}; retry when the device answers.`,{cause:g.error})}if(!c5(g.stdout,d.deviceTempPath))return e.warn(`[VideoCapture] Device pid ${c} is no longer this recording's screenrecord; not signalling it`),"not-ours";let w=await o.executeCommand(`shell kill -${p} ${c}`,{timeoutMs:uo});if(w.error)throw u(w.error,`Could not signal recorder pid ${c}`);return"signalled"}readDeviceRecorderCmdline(o,d){return o.executeCommand(`shell 'cat /proc/${d}/cmdline 2>/dev/null; true'`,{timeoutMs:uo,noRetry:!0})}async forceKillDeviceRecorder(o,d,c,p){let m=d.devicePid?.pid;if(m===void 0&&p?.deviceWide===!1){e.warn(`[VideoCapture] Skipping device-wide screenrecord kill for ${c}; its device pid is unknown so the device-side recorder may run until its own time limit`);return}try{if(m===void 0)await o.executeCommand("shell pkill -9 screenrecord",HA.pkillOpts);else await this.signalDeviceRecorder(o,d,m,{signal:9});return}catch(g){return e.warn(`[VideoCapture] Device-side force-stop failed: ${r(g)}`),`screenrecord force-stop failed: ${r(g)}`}}async cleanupDeviceRecording(o,d,c){if(c)e.warn(`[VideoCapture] Retaining unstable device file ${d.deviceTempPath} for recovery`);else{e.info("[VideoCapture] Cleaning up temp file on device");let p=["shell","rm",d.deviceTempPath];try{let m=await o.spawn(p);await new Promise((g)=>{m.once("exit",(w)=>{if(w===0)e.info("[VideoCapture] Temp file cleaned up");else e.warn(`[VideoCapture] Failed to clean up temp file: rm exited with code ${w}`);g()}),m.once("error",(w)=>{e.warn(`[VideoCapture] Failed to clean up temp file: ${w}`),g()})})}catch(m){e.warn(`[VideoCapture] Failed to clean up temp file: ${m}`)}}}async readDeviceFileSizeBytes(o,d){try{let c=await o.executeCommand(`shell stat -c %s ${d}`,{timeoutMs:HA.probe,noRetry:!0}),p=Number.parseInt(c.stdout.trim(),10);return Number.isFinite(p)?p:null}catch(c){return e.debug(`[VideoCapture] Failed to stat device file ${d}: ${r(c)}`),null}}async waitForDeviceFileToFinalize(o,d){let c=d.deviceTempPath,p=-1,m=!1,g=!1;for(let w=0;w<or;w++){let R=await this.readDeviceFileSizeBytes(o,c);if(m||=R!==null,g||=R!==null&&R>0,R!==null&&R>0&&R===p)return e.info(`[VideoCapture] Device file finalized at ${R} bytes`),!0;p=R??-1,await this.timer.sleep(Za)}if(g)return e.warn(`[VideoCapture] Device file ${c} did not visibly stabilize after ${or} checks`),!1;if(m){if(!await this.confirmDeviceScreenrecordExited(o,d))return!1;throw new t(`Device recording remained empty (zero bytes) after ${or} checks with capture exit confirmed.`)}return e.warn(`[VideoCapture] Could not observe device file ${c}; attempting bounded pull recovery`),!0}async confirmDeviceScreenrecordExited(o,d){let c=d.devicePid?.pid;if(c!==void 0)return this.confirmDeviceRecorderPidExited(o,d,c);try{let p=await o.executeCommand(`shell 'pidof screenrecord; printf "pidof-status:%s\\n" "$?"'`,{timeoutMs:HA.probe,noRetry:!0});return!p.error&&p.stderr.trim()===""&&p.stdout.trim()==="pidof-status:1"}catch(p){return e.warn("[VideoCapture] Could not confirm device screenrecord exit",p),!1}}async confirmDeviceRecorderPidExited(o,d,c){try{let p=await this.readDeviceRecorderCmdline(o,c);if(p.error)return!1;return!c5(p.stdout,d.deviceTempPath)}catch(p){return e.warn(`[VideoCapture] Could not confirm device recorder pid ${c} exit`,p),!1}}async pullRecordingFile(o,d){let c=await o.spawn(d);await new Promise((p,m)=>{c.once("exit",(g)=>{if(g===0)e.info("[VideoCapture] File pulled successfully"),p();else m(Error(`adb pull failed with exit code ${g}`))}),c.once("error",(g)=>m(g))})}async pullRecordingFileWithRetry(o,d,c){let p;for(let m=1;m<=lt;m++)try{await this.pullRecordingFile(o,d);return}catch(g){if(p=g,e.warn(`[VideoCapture] adb pull attempt ${m}/${lt} failed for recording ${c}: ${r(g)}`),m<lt)await this.timer.sleep(es)}throw new t(`Failed to pull video recording ${c} from device after ${lt} attempts: ${r(p)}`,{cause:p})}async forceStop(o,d){let c=o.backendHandle;if(!c||c.kind!=="android")throw Error("Missing backend handle for video recording.");if(this.markStopRequested(c),d?.hostOnly){try{await zn(c.process,c.exitPromise,{timeoutMs:0,forceKillTimeoutMs:Pp,signal:"SIGKILL",timer:this.timer})}catch(y){throw new t(`Failed to fully stop Android recording ${o.recordingId}: host adb process cleanup failed: ${r(y)}`,{cause:y})}return}let p=zn(c.process,c.exitPromise,{timeoutMs:0,forceKillTimeoutMs:Pp,signal:"SIGKILL",timer:this.timer}).then(()=>{return},(y)=>y),m=this.adbFactory.create(c.device),g=[],w=await this.forceKillDeviceRecorder(m,c,o.recordingId,d);if(w)g.push(w);try{await m.execute(["shell","rm","-f",c.deviceTempPath],{timeoutMs:HA.tempFileRemove})}catch(y){e.warn(`[VideoCapture] Device temp-file cleanup failed: ${y}`),g.push(`device temp-file cleanup failed: ${r(y)}`)}let R=await p;if(R)g.push(`host adb process cleanup failed: ${r(R)}`);if(g.length>0)throw new t(`Failed to fully discard Android recording ${o.recordingId}: ${g.join("; ")}`)}async startAndroid(o,d){let c=this.adbFactory.create(o),p=d.physicalDisplayId===void 0?await lV(o,c,{request:d.display,signal:d.abortSignal,timer:this.timer}):void 0,m=d.physicalDisplayId??p?.physicalId,g=is(d),w=Math.max(1,Math.round(g*1000)),R=this.resolveAndroidTimeLimit(d.maxDurationSeconds);if(d.maxDurationSeconds&&d.maxDurationSeconds>Ci)e.warn(`[VideoCapture] Android screenrecord caps at ${Ci}s; requested ${d.maxDurationSeconds}s.`);let y=[`/sdcard/auto-mobile-${d.recordingId}.mp4`,`/sdcard/auto-mobile-${d.recordingId}-retry.mp4`],P=["screenrecord","--bit-rate",String(w),"--time-limit",String(R)];if(m!==void 0)P.push("--display-id",m);if(d.resolution)P.push("--size",`${d.resolution.width}x${d.resolution.height}`);e.info("[VideoCapture] Starting Android recording"),e.debug(`[VideoCapture] Screenrecord argv: ${P.join(" ")}`),e.debug(`[VideoCapture] Device temp paths: ${y.join(", ")}`),e.debug(`[VideoCapture] Output path: ${d.outputPath}`),e.info(`[VideoCapture] Bitrate: ${g}kbps (${w}bps), Time limit: ${R}s`);let A={device:o,physicalDisplayId:m,timer:this.timer,deviceTempPaths:y,cleanupFailedStart:(he,Go)=>this.cleanupFailedAndroidStartDetached(c,Go,he)},{process:k,exitState:C,exitPromise:T,stderr:M,devicePid:O,warning:F,launchedAtMs:j,physicalDisplayId:q,deviceTempPath:_e}=await oee(c,P,d,A),Ve={kind:"android",process:k,exitState:C,exitPromise:T,stderr:M,device:o,deviceTempPath:_e,devicePid:O,launchedAtMs:j};return T.then(()=>{if((!Ve.stopRequested||Ve.exitedBeforeStop)&&b_(C,M))e.warn(`[VideoCapture] Android recorder exited with code ${C.exitCode}, signal ${C.signal} after ${this.timer.now()-j} ms since launch: ${K0(M)||"stderr was empty"}`)}).catch((he)=>{e.warn(`[VideoCapture] Could not track Android recorder exit: ${r(he)}`,he)}),{recordingId:d.recordingId,outputPath:d.outputPath,startedAt:d.startedAt,warning:F,physicalDisplayId:q,backendHandle:Ve}}async cleanupFailedAndroidStartDetached(o,d,c){try{await Je(()=>l(this.cleanupFailedAndroidStart(o,d,c),{timer:this.timer,timeoutMs:rs,label:"Failed video start cleanup"}))}catch(p){e.warn(`[VideoCapture] Failed-start cleanup did not finish: ${r(p)}`,p)}}async cleanupFailedAndroidStart(o,d,c){let p=c.pid;try{if(p===void 0)e.warn("[VideoCapture] Failed start left no known device pid; not signalling screenrecord by name");else{let m=await this.signalDeviceRecorder(o,{deviceTempPath:d},p,{signal:9});e.info(`[VideoCapture] Failed-start device recorder pid ${p} SIGKILL: ${m}`)}}catch(m){e.warn(`[VideoCapture] Could not stop the device recorder after a failed start: ${r(m)}`,m)}await this.cleanupDeviceRecording(o,{deviceTempPath:d},!1)}resolveAndroidTimeLimit(o){if(o&&o>0)return Math.min(o,Ci);return Ci}}class CM{ffmpegBackend;platformBackend;physicalIosBackend;constructor(o=new dt,d=new nr,c=new ir){this.ffmpegBackend=o,this.platformBackend=d,this.physicalIosBackend=c}async start(o){let d=o.device;if(!d)throw new t("Device is required to start video recording.");let c=this.selectBackend(d),p;try{p=await c.start(o)}catch(m){if(m instanceof X)throw new X(m.message,this.wrapHandle(m.handle,c),{cause:m});throw m}return this.wrapHandle(p,c)}wrapHandle(o,d){return{...o,backendHandle:{kind:"hybrid",backend:this.backendKind(d),handle:o}}}describeCaptureTeardown(o){let d=o.backendHandle;if(!d||d.kind!=="hybrid")return;return this.backendFor(d.backend).describeCaptureTeardown?.(d.handle)}async forceStopOrphanedCapture(o,d,c){let p=d.kind==="ios-host-recorder"?this.ffmpegBackend:this.platformBackend;if(d.kind==="android-screenrecord"&&!o)throw new t("Device is required to stop an orphan Android recorder.");if(!p.forceStopOrphanedCapture)throw new t("Selected backend does not support orphan capture teardown.");if(c)await p.forceStopOrphanedCapture(o,d,c);else await p.forceStopOrphanedCapture(o,d)}async stop(o){let d=o.backendHandle;if(!d||d.kind!=="hybrid")throw Error("Missing backend handle for hybrid video recording.");return this.backendFor(d.backend).stop(d.handle)}async forceStop(o,d){let c=o.backendHandle;if(!c||c.kind!=="hybrid")throw Error("Missing backend handle for hybrid video recording.");let p=this.backendFor(c.backend);if(!p.forceStop)throw Error("Selected video capture backend does not support force stopping recordings.");await p.forceStop(c.handle,d)}backendKind(o){if(o===this.ffmpegBackend)return"ffmpeg";return o===this.physicalIosBackend?"ios-physical":"platform"}backendFor(o){if(o==="ffmpeg")return this.ffmpegBackend;return o==="ios-physical"?this.physicalIosBackend:this.platformBackend}selectBackend(o){if(o.platform==="ios")return Rn(o.deviceId)?this.physicalIosBackend:this.ffmpegBackend;if(o.platform==="tvos")return this.ffmpegBackend;if(o.platform==="android"){if(Be(f.ANDROID_VIDEO_USE_FFMPEG_PIPE))return e.info("[VideoCapture] Android: using exec-out screenrecord \u2192 ffmpeg (AUTOMOBILE_ANDROID_VIDEO_USE_FFMPEG_PIPE)"),this.ffmpegBackend}return this.platformBackend}}import{rename as os,rm as ns}from"fs/promises";var as=120000,ss=1e4;function ds(o,d){let c=o.pcmBytes>0?[...o.leadMs>0?["-ss",(o.leadMs/1000).toFixed(3)]:[],"-f","s16le","-ar",String(o.sampleRate),"-ac","1","-i",o.pcmPath]:["-f","lavfi","-i",`anullsrc=r=${o.sampleRate}:cl=mono`];return["-y","-hide_banner","-loglevel","error","-i",o.videoPath,...c,"-map","0:v:0","-map","1:a:0","-c:v","copy","-c:a","aac","-b:a","128k","-af","apad","-shortest","-movflags","+faststart",d]}class ar{client;files;constructor(o,d={rename:os,remove:(c)=>ns(c,{force:!0})}){this.client=o;this.files=d}async ensureAvailable(){try{await this.client.probe({timeoutMs:ss})}catch(o){throw new t(`A recording with audio needs FFmpeg to add the audio track (${r(o)}). Install FFmpeg (brew install ffmpeg, or apt-get install ffmpeg) or record without audio.`)}}async mux(o){let d=`${o.videoPath}.with-audio.mp4`;try{let c=await this.client.run({args:ds(o,d),context:"recording audio track mux",timeoutMs:as});if(c.exitCode!==0)throw Error(`ffmpeg exited with ${c.exitCode??c.signal}: ${c.stderr.trim().slice(-400)}`);await this.files.rename(d,o.videoPath)}catch(c){throw await this.files.remove(d).catch((p)=>{e.warn(`Failed to remove partial mux output ${d}: ${r(p)}`)}),c}}}function us(o){let d=cs(o,{mode:384}),c=0,p;d.on("error",(g)=>{p??=g});let m;return{write:(g)=>{if(p)return;if(c+=g.byteLength,d.write(g))return;return new Promise((w)=>{let R=()=>{d.off("drain",R),d.off("close",R),w()};d.on("drain",R),d.on("close",R)})},close:()=>(m??=new Promise((g,w)=>{let R=()=>p?w(p):g(c);if(d.destroyed){R();return}d.once("close",R),d.end()}),m)}}function ps(){return{source:Lf,muxer:new ar(new pe),openPcmFile:us,removeFile:(o)=>ls(o,{force:!0}),now:()=>i.now()}}function sr(){return e.debug("[VideoRecording] Using HybridVideoCaptureBackend (iOS FFmpeg, Android platform)"),new Gt(new CM,ps())}function ne(o){return{recordingId:o.recordingId,fileName:o.fileName,filePath:o.filePath,format:o.format,sizeBytes:o.sizeBytes,durationMs:o.durationMs,videoDurationMs:o.videoDurationMs,codec:o.codec,outputName:o.outputName,createdAt:o.createdAt,startedAt:o.startedAt,endedAt:o.endedAt,lastAccessedAt:o.lastAccessedAt,config:o.config,highlights:o.highlights,recordedPanel:o.recordedPanel,transitions:o.transitions,...o.warnings?{warnings:o.warnings}:{},...o.hasAudioTrack===void 0?{}:{hasAudioTrack:o.hasAudioTrack}}}function mo(o,d){return{status:"completed",captureTeardown:void 0,ownerSessionUuid:d,outputName:o.outputName,fileName:o.fileName,filePath:o.filePath,format:o.format,sizeBytes:o.sizeBytes,durationMs:o.durationMs,codec:o.codec,endedAt:o.endedAt,lastAccessedAt:o.lastAccessedAt,config:o.config,highlights:o.highlights,recordedPanel:o.recordedPanel,transitions:o.transitions,warnings:o.warnings,hasAudioTrack:o.hasAudioTrack}}var Bd={LATEST:"automobile:video/latest",ARCHIVE:"automobile:video/archive",ARCHIVE_ITEM:"automobile:video/archive/{recordingId}"};function DM(o){return`automobile:video/archive/${o}`}var fo=1,go=(o,d,c,p)=>[[c,p],[p,c]].some(([m,g])=>Math.abs(o-m)<=fo&&Math.abs(d-g)<=fo);function ms(o,d){if(d)return d.screenScale??(()=>null);return()=>gu(o).getScreenScaleMetadata()}function fs(o,d){let{sourceWidth:c,sourceHeight:p}=o;if(!d||typeof c!=="number"||typeof p!=="number"||typeof o.sourceScale==="number"&&o.sourceScale>0)return;let{nativeScale:m,pixelWidth:g,pixelHeight:w}=d;if(go(c,p,g/m,w/m))return 1;if(go(c,p,g,w))return m;return}var ho=Error("Simulator highlight deadline elapsed"),gs=new Map,hs=0,ws=s({requestId:n(),success:h(),error:n().optional()});class dr{device;executor;timer;resolveHelper;screenScale;hosts;supportedHelpers=new Map;fallback;constructor(o,d){this.device=o;this.fallback=d?.fallback??(()=>gu(o)),this.executor=d?.executor??new re,this.timer=d?.timer??i,this.screenScale=ms(o,d),this.hosts=d?new Map:gs,this.resolveHelper=d?.resolveHelper??(async()=>{if(gS())return yS();return qc.getInstance().ensure()})}async requestAddHighlight(o,d,c=5000){let p=new AbortController;try{return await l(this.sendHighlight(o,d,p.signal,c),{timer:this.timer,timeoutMs:c,label:"Simulator highlight",timeoutError:()=>ho,onTimeout:()=>p.abort()})}catch(m){if(m===ho)return e.warn(`[SimulatorHighlights] highlight request timed out after ${c}ms`),{success:!1,error:"Simulator highlight timed out"};throw m}}async sendHighlight(o,d,c,p){try{let m=await this.resolveHelper();if(c.throwIfAborted(),!m)return await this.fallback().requestAddHighlight(o,d,p);let g=this.supportedHelpers.get(m);if(g===void 0){let A=await this.executor.executeCommand(m,["--help"],{timeoutMs:p,signal:c});c.throwIfAborted(),g=A.stderr.split(/\r?\n/).some((k)=>M4(k)==="simulator-highlights"),this.supportedHelpers.set(m,g)}if(!g)return await this.fallback().requestAddHighlight(o,d,p);let w=`${m}:${this.device.deviceId}`,R=String(++hs),y=JSON.stringify({requestId:R,id:o,shape:this.withSourceScale(d)}),P=this.hosts.get(w);if(!P)P={child:this.executor.spawn(m,["--highlight-simulator",this.device.name,"--highlight-json",y],{stdio:["pipe","pipe","pipe"]}),pending:new Map},this.hosts.set(w,P),this.watchHost(w,P);else{let A=this.waitForReply(P,R,c);return P.child.stdin?.write(`${y}
23
+ `),await A}return await this.waitForReply(P,R,c)}catch(m){return e.warn(`[SimulatorHighlights] Failed to send highlight: ${r(m)}`,m),{success:!1,error:String(m)}}}withSourceScale(o){let d=fs(o.bounds,this.screenScale());return d===void 0?o:{...o,bounds:{...o.bounds,sourceScale:d}}}waitForReply(o,d,c){return new Promise((p)=>{let m=(w)=>{c.removeEventListener("abort",g),o.pending.delete(d),p(w)},g=()=>m({success:!1,error:"Simulator highlight timed out"});if(o.pending.set(d,m),c.addEventListener("abort",g,{once:!0}),c.aborted)g()})}watchHost(o,d){let c="",p="",m=!1,g=(w)=>{if(m)return;if(m=!0,this.hosts.get(o)===d)this.hosts.delete(o);for(let R of d.pending.values())R({success:!1,error:w});d.child.kill()};d.child.stdout?.on("data",(w)=>{if(m)return;if(c+=w.toString(),c.length>65536){g("Invalid highlight acknowledgement");return}let R;while((R=c.indexOf(`
24
+ `))>=0){let y=c.slice(0,R);c=c.slice(R+1);try{let P=ws.parse(JSON.parse(y));d.pending.get(P.requestId)?.({success:P.success,...P.error?{error:P.error}:{}})}catch(P){g(`Invalid highlight acknowledgement: ${P}`);return}}}),d.child.stderr?.on("data",(w)=>{p=(p+w.toString()).slice(-4096)}),d.child.stdin?.on("error",(w)=>g(String(w))),d.child.once("error",(w)=>g(String(w))),d.child.once("exit",()=>g(p.trim()||"Highlight helper exited before drawing"))}}var vs=5000,cr=(o)=>o===null?void 0:o,wo={x:a().int(),y:a().int(),width:a().int().positive("bounds.width must be a positive integer"),height:a().int().positive("bounds.height must be a positive integer"),sourceWidth:a().int().positive().nullable().optional(),sourceHeight:a().int().positive().nullable().optional()},yo=(o,d)=>{let c=cr(o.sourceWidth)!==void 0,p=cr(o.sourceHeight)!==void 0;if(c!==p)d.addIssue({code:On.custom,message:"bounds.sourceWidth and bounds.sourceHeight must be provided together"});if(cr(o.sourceScale)!==void 0&&!c)d.addIssue({code:On.custom,message:"bounds.sourceScale requires bounds.sourceWidth and bounds.sourceHeight"})},vo=(o)=>s({type:_("circle"),bounds:o}).strict(),_1=vo(s(wo).strict().superRefine(yo)),bs=vo(s({...wo,sourceScale:a().finite().positive().nullable().optional()}).strict().superRefine(yo)),Rs=s({success:h(),error:n().nullable().optional(),requestId:n().optional(),timestamp:a().optional()}).passthrough();class lr{device;adbFactory;highlightClient;constructor(o,d=v,c){if(this.device=o,o.platform==="ios")this.adbFactory=v;else if(d&&typeof d.create==="function")this.adbFactory=d;else if(d){let p=d;this.adbFactory={create:()=>p}}else this.adbFactory=v;this.highlightClient=c||this.createDefaultHighlightClient(o)}async addHighlight(o,d,c={}){let p=this.parseHighlightId(o),m=this.parseHighlightShape(d),g=c.timeoutMs??vs,w=await this.highlightClient.requestAddHighlight(p,m,g,new U);return this.parseHighlightResponse(w)}parseHighlightId(o){let d=n().min(1,"Highlight id must be a non-empty string").safeParse(o);if(!d.success)throw new t(d.error.issues.map((c)=>c.message).join("; "));return d.data}parseHighlightShape(o){let d=bs.safeParse(o);if(!d.success){let c=d.error.issues.map((p)=>p.message).join("; ");throw new t(c)}return d.data}parseHighlightResponse(o){let d=Rs.safeParse(o);if(!d.success){let c=d.error.issues.map((p)=>p.message).join("; ");throw new t(`Invalid highlight response: ${c}`)}return d.data}createDefaultHighlightClient(o){if(o.platform==="android")return E.getInstance(o,this.adbFactory);if(o.platform==="ios"){if(z({deviceId:o.deviceId})==="simulator")return new dr(o);return x.getInstance(o)}throw new t(`Visual highlights are not supported on ${o.platform} devices.`)}}class zE{deviceReadiness;visualHighlightFactory;constructor(o,d=(c)=>new lr(c)){this.deviceReadiness=o,this.visualHighlightFactory=d}async addHighlight(o,d,c){let m=await(await this.resolveHighlight(c)).addHighlight(o,d,{timeoutMs:c.timeoutMs});if(!m.success)throw new t(m.error||"Failed to add highlight");return m}async resolveHighlight(o){if(o.device){if(o.device.platform!==o.platform)throw new t(`Highlight platform mismatch: requested ${o.platform}, device is ${o.device.platform}.`);return this.visualHighlightFactory(o.device)}if(o.platform!=="android"&&o.platform!=="ios")throw new t(`Visual highlights are not supported on ${o.platform} devices.`);if(!this.deviceReadiness)throw new t("Supply a resolved device or inject device readiness for visual highlights.");let d=await this.deviceReadiness.ensureDeviceReady(o.platform,o.deviceId);return this.visualHighlightFactory(d)}}var Ss=30,Ps=300,OG=3600,As=1200,Es=86400000,Cs=7,xs=60,ks=15,Ts=5000;function ur(o,d,c){return Hu(o,d,(p)=>e.warn(`[VideoRecording] Ignoring invalid retention env value "${p}"; using ${c}`))??c}function Ao(o=process.env){let d=ur(f.VIDEO_RETENTION_DAYS,o,Cs),c=ur(f.VIDEO_RETENTION_SWEEP_MINUTES,o,xs),p=ur(f.VIDEO_INPROGRESS_CHECK_SECONDS,o,ks);return{ttlMs:Math.max(0,Math.floor(d*Es)),sweepIntervalMs:Math.max(1000,Math.floor(c*60000)),inProgressCheckIntervalMs:Math.max(1000,Math.floor(p*1000))}}var Ul=5000;async function Eo(o,d,c){return lV(o,v.create(o),{request:d,signal:c})}var W=null,Se,fr=!0,Ge=0,fe=null,Ke=new Set,ze=new Map,Ms=600000,xe=new Map;function OM(o){let d=xe.get(o);if(d)d.timer.clearTimeout(d.handle),xe.delete(o)}function J8(o,d={}){OM(o);let c=d.timer??i,p={timer:c,handle:c.setTimeout(()=>{if(xe.get(o)===p)OM(o)},d.expiryMs??Ms)};xe.set(o,p)}var ke=new Map,Te=new Map,ft=new Map,ut;function X8(o){ut=o}function x1(){return ut}var Z=new Map,Pe=new Map,Ae=new Map,Y=new Set,ie=new Map;function Is(o,d){if(!d)return{key:"unknown",role:"unknown"};return{key:d.key,role:o.displays?.panels.find((c)=>c.key===d.key)?.role??d.role}}function Ds(o,d,c,{startedAt:p,timer:m}){let g=Date.parse(p),w={recordedPanel:c.panel,currentPanel:c.activePanel,transitions:[],unsubscribe:()=>{return},device:d,warning:c.warning};if(c.activePanel.key!==c.panel.key)w.seededTransition={atMs:0,from:c.panel,to:c.activePanel},w.transitions.push(w.seededTransition);w.unsubscribe=Ce.subscribe(d.deviceId,(R)=>{let y=w.transitions.at(-1),P=Is(d,R);if(y?.to.key==="unknown"&&R){if(y.from.key===P.key&&y.from.role===P.role){w.transitions.pop();return}y.to=P,w.currentPanel=P;return}if(R&&R.key===w.currentPanel.key)return;if(Ti(d,w.currentPanel,w.transitions,R)){w.currentPanel=P;return}if(w.transitions.push({atMs:Math.max(0,Math.round(m.now()-g)),from:w.currentPanel,to:P}),R)w.currentPanel=P}),ie.set(o,w)}async function Fs(o,d,c){if(d.device.platform==="android"){if(d.physicalDisplayId!==void 0){let p=d.device.displays?.panels.find((g)=>g.key===d.physicalDisplayId),m={key:d.physicalDisplayId,role:p?.role??"unknown"};return{panel:m,physicalId:d.physicalDisplayId,activePanel:d.activePanel??m}}return o.resolveAndroidDisplay(d.device,d.display,c)}if(d.display!==void 0)throw new t("display selection for videoRecording is supported on Android only.");if(d.device.platform==="ios")return Ei(d.device,Ce.observedPanel(d.device.deviceId));return}async function Co(o,d,c,p){let m=ie.get(o);if(!m)return;m.unsubscribe(),ie.delete(o);let g=m.transitions.at(-1),R=!(p?.hostOnly||Y.has(o))&&m.device.platform!=="ios";if(g?.to.key==="unknown"&&R)try{let P=await c.resolveAndroidDisplay(m.device,"active");if(P)g.to=P.activePanel}catch(P){e.warn(`[VideoRecording] Could not resolve display after transition: ${r(P)}`)}m.transitions=m.transitions.filter((P)=>P.from.key!==P.to.key||P.from.role!==P.to.role),d.recordedPanel=m.recordedPanel,d.transitions=m.transitions;let y=Mi(m);d.warnings=y.length>0?[...d.warnings??[],...y]:d.warnings}var Me=new Map,Ee=null;async function _s(){return sr()}async function xo(){let o=await _s();return new Xe({backend:o})}async function Vs(o){if(!Se){let c=Os(o).catch((p)=>{if(Se===c)Se=void 0;throw p});Se=c}await Se}async function MM(){return H()}function ko(o,d,c){return Z.has(o)||d.videoRecorderService.listActiveRecordingIds().includes(o)||d.daemonSessionId!==void 0&&c===d.daemonSessionId}async function ee(o,d){try{if(!ko(o,d)){let p=await d.recordingRepository.getRecording(o);if(!d.daemonSessionId||p?.recorderDaemonSessionId!==d.daemonSessionId)return}let c=d.videoRecorderService.getCaptureTeardown(o);await d.recordingRepository.updateRecording(o,{captureTeardown:Y.has(o)&&c?.kind!=="ios-host-recorder"?void 0:c})}catch(c){e.warn(`[VideoRecording] Failed to refresh capture teardown descriptor for ${o}`,c)}}async function Os(o){let d=await o.recordingRepository.listRecordings({status:"recording",excludeLiveDaemonSessionIds:o.liveDaemonSessionIds,ownDaemonSessionId:o.daemonSessionId});if(W===o)Gs(o);if(d.length===0)return;let c=o.now().toISOString();for(let p of d){let m=await o.statFileSize(p.filePath),g=gr(p.startedAt,c);await o.recordingRepository.updateRecording(p.recordingId,{status:"interrupted",endedAt:c,lastAccessedAt:c,sizeBytes:m,durationMs:g})}e.info(`[VideoRecording] Marked ${d.length} recording(s) as interrupted after restart`)}async function H(){if(!W)W={videoRecorderService:await xo(),recordingRepository:new L1,configRepository:C4(),highlightClient:new zE,timer:i,now:()=>new Date,liveDaemonSessionIds:new Set,daemonSessionId:void 0,retentionPolicy:Ao(),statFileSize:mt,codecProbe:ae,resolveAndroidDisplay:Eo};let o=W;return await Vs(o),o}async function Z8(o){let d=W??await $s(o);o={daemonSessionId:d.daemonSessionId,...o},W={videoRecorderService:o.videoRecorderService??d.videoRecorderService,recordingRepository:o.recordingRepository??d.recordingRepository,configRepository:o.configRepository??d.configRepository,highlightClient:o.highlightClient??d.highlightClient,timer:o.timer??d.timer,now:o.now??d.now,liveDaemonSessionIds:o.liveDaemonSessionIds??d.liveDaemonSessionIds,daemonSessionId:o.daemonSessionId,retentionPolicy:o.retentionPolicy??d.retentionPolicy,...To(o,d),resolveAndroidDisplay:o.resolveAndroidDisplay??d.resolveAndroidDisplay},Hs()}function To(o,d={statFileSize:mt,codecProbe:ae}){return{statFileSize:o.statFileSize??d.statFileSize,codecProbe:o.codecProbe??d.codecProbe}}async function $s(o){return{videoRecorderService:o.videoRecorderService??await xo(),recordingRepository:o.recordingRepository??new L1,configRepository:o.configRepository??C4(),highlightClient:o.highlightClient??new zE,timer:o.timer??i,now:o.now??(()=>new Date),liveDaemonSessionIds:o.liveDaemonSessionIds??new Set,daemonSessionId:o.daemonSessionId,retentionPolicy:o.retentionPolicy??Ao(),...To(o),resolveAndroidDisplay:o.resolveAndroidDisplay??Eo}}function Hs(){for(let o of xe.keys())OM(o);for(let{timer:o,handle:d}of ke.values())o.clearTimeout(d);ke.clear(),Z.clear();for(let{timer:o,handle:d}of Me.values())o.clearInterval(d);if(Me.clear(),Ee)Ee.timer.clearInterval(Ee.handle),Ee=null;for(let o of Te.values())for(let d of o.timers)o.timer.clearTimeout(d);Te.clear(),ft.clear(),Pe.clear(),Y.clear(),Ae.clear();for(let o of ie.values())o.unsubscribe();ie.clear(),fr=!0,Ge=0;for(let o of Ke)o.abort();Ke.clear(),fe?.resolve(),fe=null,ze.clear(),Se=void 0}function Bs(o){let d=o.device.deviceId,c=o.abortSignal;if(c?.throwIfAborted(),!fr)throw new t("Video recording is unavailable while the daemon shuts down.");if(xe.has(d))throw new t(`VM snapshot restore is in progress on device ${d}. Retry video recording after it completes.`);if(ze.has(d))throw new t(`Video recording start already in progress for device ${d}.`);ze.set(d,o),Ge++;let p=new AbortController;return Ke.add(p),{abortSignal:je(c,p.signal),complete:()=>{if(ze.delete(d),Ge--,Ke.delete(p),Ge===0)fe?.resolve(),fe=null}}}async function Q8(){fr=!1;for(let o of Ke)o.abort();if(Ge===0)return;if(!fe){let o;fe={promise:new Promise((c)=>{o=c}),resolve:o}}await fe.promise}function Mo(o,d){return{qualityPreset:d.qualityPreset??o.qualityPreset,targetBitrateKbps:d.targetBitrateKbps??o.targetBitrateKbps,maxThroughputMbps:d.maxThroughputMbps??o.maxThroughputMbps,fps:d.fps??o.fps,maxArchiveSizeMb:d.maxArchiveSizeMb??o.maxArchiveSizeMb,format:d.format??o.format,resolution:d.resolution??o.resolution}}function Io(o){return{qualityPreset:o.qualityPreset,targetBitrateKbps:o.targetBitrateKbps,maxThroughputMbps:o.maxThroughputMbps,fps:o.fps,maxArchiveSizeMb:o.maxArchiveSizeMb,format:o.format,resolution:o.resolution}}function Ls(o,d){if(o===void 0)return Ss;if(!Number.isFinite(o)||o<=0)throw new t("maxDuration must be a positive number of seconds.");let c=Oe(d)?OG:Ps;if(o>c)throw new t(`maxDuration must be <= ${c} seconds.`);return Math.round(o)}async function Ns(o,d){if(d)return d;let{recordingRepository:c}=o,p=new Set(o.videoRecorderService.listActiveRecordingIds()),m=(await c.listRecordings({status:"recording"})).filter((g)=>p.has(g.recordingId));if(m.length===0)throw new t("No active video recording found. Provide recordingId.");if(m.length>1)throw new t("Multiple active video recordings found. Provide recordingId.");return m[0].recordingId}function Us(o,d,c){if(!Number.isFinite(d)||d<=0)return;let{timer:p}=c,m=Math.max(1,Math.round(d*1000)),g=p.setTimeout(()=>{if(W!==c)return;ht(c,o).catch((w)=>{e.warn(`[VideoRecording] Failed to auto-stop recording ${o}: ${w}`)})},m);ke.set(o,{timer:p,handle:g})}function Ie(o){let d=ke.get(o);if(d)d.timer.clearTimeout(d.handle),ke.delete(o)}function Gs(o){if(Ee)return;let{retentionPolicy:d,timer:c}=o;if(d.ttlMs<=0)return;let p=c.setInterval(()=>{if(W!==o)return;zs(o).catch((m)=>{e.warn(`[VideoRecording] TTL retention sweep failed: ${m}`)})},d.sweepIntervalMs);Ee={timer:c,handle:p}}async function zs(o){let{recordingRepository:d,retentionPolicy:c,now:p}=o;if(c.ttlMs<=0)return[];let m=p().getTime()-c.ttlMs,g=await d.listRecordings({status:XE}),w=[];for(let R of g){let y=Date.parse(R.createdAt);if(Number.isNaN(y)||y>m)continue;try{if(await Lo(R.recordingId,o))w.push(R.recordingId)}catch(P){e.warn(`[VideoRecording] Failed to prune expired recording ${R.recordingId}: ${P}`)}}if(w.length>0)e.info(`[VideoRecording] TTL sweep pruned ${w.length} recording(s) older than ${c.ttlMs} ms`);return w}function Do(o,d,c,p){if(!Number.isFinite(c)||c<=0)return;let{timer:m,retentionPolicy:g}=p,w=m.setInterval(()=>{js(o,d,c,p).catch((R)=>{e.warn(`[VideoRecording] In-progress size check failed for ${o}: ${R}`)})},g.inProgressCheckIntervalMs);Me.set(o,{timer:m,handle:w})}function ge(o){let d=Me.get(o);if(d)d.timer.clearInterval(d.handle),Me.delete(o)}async function gt(o,d){if(W!==d)return;if(!d.videoRecorderService.listActiveRecordingIds().includes(o))return;let c=await d.recordingRepository.getRecording(o);if(!c||c.status!=="recording")return;if(W!==d)return;if(!d.videoRecorderService.listActiveRecordingIds().includes(o))return;Ie(o);let p=d.timer.setTimeout(()=>{if(W!==d)return;ht(d,o).catch((g)=>{e.warn(`[VideoRecording] Retained safety stop failed for ${o}: ${g}`)})},Ts);ke.set(o,{timer:d.timer,handle:p});let m=Math.floor((c.config.maxArchiveSizeMb??0)*1024*1024);ge(o),Do(o,d.videoRecorderService.getInProgressFiles(o),m,d)}async function js(o,d,c,p){if(W!==p||!Me.has(o))return;let{statFileSize:m}=p,g=(y)=>m===mt?mt(y,!0):m(y),R=(await Promise.all(d.map(g))).reduce((y,P)=>y+P,0);if(W!==p||R<c)return;e.warn(`[VideoRecording] In-progress recording ${o} reached size cap (${R} >= ${c} bytes); stopping to protect disk.`),ge(o),await ht(p,o)}function Ks(o){let d=ft.get(o);if(!d)return null;return Te.get(d)??null}function Fo(o,d){if(!Number.isFinite(d))return 0;return Math.max(0,Math.round(d-o.startedAtMs))}function bo(o){let d=Math.max(0,Math.round(o));return Number((d/1000).toFixed(3))}function _o(o,d,c){let p=Fo(o,c);o.highlights.push({description:d.description,shape:d.shape,appearedAtMs:p,disappearedAtMs:p+As})}function MG(o=i){return po("highlight",o)}function Vo(o,d){if(o.highlights.length===0)return;let c=Date.parse(d),p=Fo(o,c);return o.highlights.map((m)=>{let g=Math.min(m.disappearedAtMs,p);return{description:m.description,shape:m.shape,timeline:{appearedAtSeconds:bo(m.appearedAtMs),disappearedAtSeconds:bo(g)}}}).sort((m,g)=>m.timeline.appearedAtSeconds-g.timeline.appearedAtSeconds)}function Ws(o,d,c,p){let m=Date.parse(c);return{recordingId:o,deviceId:d.deviceId,platform:d.platform,startedAtMs:Number.isNaN(m)?p.now():m,highlights:[],timer:p,timers:new Set}}function pt(o){let d=Te.get(o);if(!d)return null;for(let c of d.timers)d.timer.clearTimeout(c);return d.timers.clear(),Te.delete(o),ft.delete(d.deviceId),d}function Oo(o){let d=o.timing?.startTimeMs??0;if(!Number.isFinite(d)||d<0)throw new t("highlight.timing.startTimeMs must be >= 0.");return{startMs:d}}async function qs(o,d,c,p){let m={device:d,deviceId:d.deviceId,platform:d.platform},g=[],w=(y,P)=>{let A=Math.max(0,Math.round(y));if(A===0){g.push(P());return}let k=o.timer.setTimeout(()=>{P()},A);o.timers.add(k)},R=async(y)=>{try{let P=MG();await p.highlightClient.addHighlight(P,y.shape,m),_o(o,y,p.now().getTime())}catch(P){e.warn(`[VideoRecording] Failed to add highlight: ${P}`)}};for(let y of c){let{startMs:P}=Oo(y);w(P,()=>R(y))}if(g.length>0)await Promise.all(g)}async function Js(o){let{configRepository:d}=await H(),c=await d.getConfig(),p=c?Io(c):N.getVideoRecordingDefaults();return Mo(p,o)}async function mt(o,d=!1){try{return(await Po.stat(o)).size}catch(c){if(d&&c instanceof Error&&"code"in c&&c.code==="ENOENT")e.debug(`[VideoRecording] Missing recording file at ${BD(o)} during capture; the host file may be unavailable until finalization (Android adb pull).`);else e.warn(`[VideoRecording] Missing recording file at ${BD(o)}`,c);return 0}}function gr(o,d){let c=Date.parse(o),p=Date.parse(d);if(Number.isNaN(c)||Number.isNaN(p))return;return Math.max(0,p-c)}async function Fe(o){let d=new Set([Bd.LATEST,Bd.ARCHIVE]);for(let c of o)d.add(DM(c));await jL(Array.from(d))}async function NG(){let{configRepository:o}=await H(),d=await o.getConfig();if(d)return d;return Uf(N.getVideoRecordingDefaults())}async function eX(o){let d=await H(),{configRepository:c}=d;if(o===null){await c.clearConfig();let R=Uf(N.getVideoRecordingDefaults()),y=await mr(d,R.maxArchiveSizeMb);return{config:R,evictedRecordingIds:y.evictedRecordingIds}}let p=await NG(),m=Mo(Io(p),o),g=Uf(m);await c.setConfig(g);let w=await mr(d,g.maxArchiveSizeMb);return{config:g,evictedRecordingIds:w.evictedRecordingIds}}async function Xs(o,d,c){let{videoRecorderService:p,recordingRepository:m}=o,g=ut?.list(d.deviceId)??[];if(g.length>0)try{await ut.stop(d.deviceId)}catch(y){throw new fm(d.deviceId,g.map((P)=>P.recordingId),y)}if(c.throwIfAborted(),p.hasActiveRecordingForDevice(d.deviceId))throw new t(`Video recording already active for device ${d.deviceId}.`);let w=await m.listRecordings({status:"recording",deviceId:d.deviceId});c.throwIfAborted();let R=new Set(p.listActiveRecordingIds());if(w.some((y)=>R.has(y.recordingId)))throw new t(`Video recording already active for device ${d.deviceId}.`);if(w.length>0)await Promise.all(w.map((y)=>ql(y.recordingId)))}function Ys(o,d,c,{abortSignal:p,recordingDisplay:m}){return{outputName:o.outputName,config:d,device:o.device,maxDurationSeconds:c,abortSignal:p,display:o.display,recordingPanel:m?.panel,physicalDisplayId:m?.physicalId,audio:o.audio}}async function Qs(o,d,c,p){yr();let m={ownerSessionUuid:d.ownerSessionUuid,deviceId:d.device.deviceId};Z.set(c.recordingId,m);let{recordingRepository:g,videoRecorderService:w}=o,R=m.ownerSessionUuid;if(await g.insertRecording({recordingId:c.recordingId,captureTeardown:w.getCaptureTeardown(c.recordingId),deviceId:d.device.deviceId,platform:_M(d.device.platform,"Video recording"),status:"recording",outputName:c.outputName,fileName:c.fileName,filePath:c.outputPath,format:c.config.format,sizeBytes:0,durationMs:void 0,codec:void 0,createdAt:c.startedAt,startedAt:c.startedAt,endedAt:void 0,lastAccessedAt:c.startedAt,config:c.config,recordedPanel:c.recordedPanel,transitions:c.recordedPanel?[]:void 0,ownerSessionUuid:R,recorderDaemonSessionId:o.daemonSessionId}),p.throwIfAborted(),m.ownerSessionUuid!==R)await g.updateRecording(c.recordingId,{ownerSessionUuid:m.ownerSessionUuid})}function Zs(o){if(_M(o.device.platform,"Video recording"),o.audio&&o.device.platform==="tvos")throw new t("audio is not supported for Apple TV recordings; record without audio.")}async function jE(o){Zs(o),o={...o};let d=Bs(o),c;try{let p=await H();d.abortSignal.throwIfAborted();let{videoRecorderService:m,timer:g}=p;await Xs(p,o.device,d.abortSignal);let w=o.configOverrides??{},R=await Js(w),y=Ls(o.maxDurationSeconds,o.device.platform),P=o.highlights??[],A=await Fs(p,o,d.abortSignal);for(let T of P)Oo(T);if(c=await m.startRecording(Ys(o,R,y,{abortSignal:d.abortSignal,recordingDisplay:A})),d.abortSignal.throwIfAborted(),iee(c,A),await Qs(p,o,c,d.abortSignal),A&&c.recordedPanel){let T=ki(o.device,A,c.recordedPanel);Ds(c.recordingId,o.device,T,{startedAt:c.startedAt,timer:g})}let k=Ws(c.recordingId,o.device,c.startedAt,g);if(Te.set(c.recordingId,k),ft.set(o.device.deviceId,c.recordingId),P.length>0)await qs(k,o.device,P,p);Us(c.recordingId,y,p);let C=Math.floor((c.config.maxArchiveSizeMb??0)*1024*1024);return Do(c.recordingId,m.getInProgressFiles(c.recordingId),C,p),d.abortSignal.throwIfAborted(),c}catch(p){if(c)try{await MP(c.recordingId,o.rollbackOptions?.())}catch(m){throw e.warn(`[VideoRecording] Failed to roll back recording ${c.recordingId}: ${m}`,m),AggregateError([p,m],`${r(p)}; recording rollback failed: ${r(m)}`)}throw p}finally{d.complete()}}async function ed(o,d,c){if(c?.hostOnly)Y.add(o),await gt(o,d).catch((p)=>{e.warn(`[VideoRecording] Failed to re-arm retained host-only rollback safety for ${o}: ${r(p)}`,p)});await ee(o,d)}async function MP(o,d){ie.get(o)?.unsubscribe(),ie.delete(o);let c=await H(),{videoRecorderService:p,recordingRepository:m}=c,g=p.listActiveRecordingIds().includes(o);if(g)try{await p.discardRecording(o,d)}catch(y){if(p.listActiveRecordingIds().includes(o))throw await ed(o,c,d),y;if(await ee(o,c),Ie(o),ge(o),(await m.getRecording(o))?.status==="recording")await ql(o);throw y}await ee(o,c),Ie(o),ge(o);let w=await m.getRecording(o);if(w?.status==="recording")await ql(o);if(pt(o),!g&&w)await Uo(w.filePath);if(await m.deleteRecording(o))await Fe([o])}async function Hl(o){let d=await H();return $o(d,o)}async function C1(o){let d=await H();return ht(d,o)}function ht(o,d){return Je(()=>$o(o,d))}async function $o(o,d){let c=await Ns(o,d);if(Y.has(c))return Ho(c,o);let p=Pe.get(c);if(p)return p;let m=rd(c,o);Pe.set(c,m);try{return await m}finally{if(Pe.get(c)===m)Pe.delete(c);if(Y.has(c)&&!o.videoRecorderService.listActiveRecordingIds().includes(c))Y.delete(c)}}async function td(o,d){let c=Ae.get(o);if(c)throw await c,u(d,`Failed to stop video recording ${o}`)}async function rd(o,d){let{videoRecorderService:c,recordingRepository:p,now:m}=d,g=Z.get(o),w;try{w=await c.stopRecording(o)}catch(P){if(await td(o,P),c.listActiveRecordingIds().includes(o)){await ee(o,d),e.warn(`[VideoRecording] Stop of ${o} could not confirm teardown; the service retained ownership, so its recording row stays active: ${P}`);try{await gt(o,d)}catch(k){e.warn(`[VideoRecording] Failed to re-arm retained safety enforcement for ${o}: ${k}`,k)}}else try{await hr(o,d)}catch(k){e.warn(`[VideoRecording] Failed to clean up orphaned recording ${o} after a failed stop: ${k}`,k)}throw u(P,`Failed to stop video recording ${o}`)}await ee(o,d),await Co(o,w,d),Ie(o),ge(o);let R=pt(o);if(R){let P=Vo(R,w.endedAt??m().toISOString());if(P)w.highlights=P}await p.updateRecording(o,mo(w,g?.ownerSessionUuid));let y=await mr(d,w.config.maxArchiveSizeMb,o);if(y.maxSizeBytes>0&&y.currentSizeBytes>y.maxSizeBytes)w.warnings=[...w.warnings??[],`Archive size ${y.currentSizeBytes} bytes exceeds limit ${y.maxSizeBytes} bytes after eviction; just-finished recording ${o} was kept.`];return await Fe([w.recordingId]),{metadata:w,evictedRecordingIds:y.evictedRecordingIds}}async function ql(o){let d=await H();await hr(o,d)}async function tX(o,d){let c=await H(),p=await c.recordingRepository.getRecording(o);if(!p||await c.statFileSize(p.filePath)>0)return;let m=await c.statFileSize(d);if(m<=0)return;let g=pr.extname(d).slice(1).toLowerCase();if(g!=="mov"&&g!=="mp4"){e.warn(`[VideoRecording] Unsupported orphan capture container: ${g}`);return}let w=await c.codecProbe.codec(d);if(w===void 0){e.warn(`[VideoRecording] Could not probe the raw orphan capture at ${BD(d)}`);return}let R=await ye(c.codecProbe,d),y={filePath:d,fileName:pr.basename(d),format:g,sizeBytes:m,codec:w,videoDurationMs:R};return await c.recordingRepository.updateRecording(o,y),await Fe([o]),{...ne(p),...y}}async function hr(o,d,c){let{recordingRepository:p,now:m,statFileSize:g}=d,w=Z.get(o);Ie(o),ge(o);let R=await p.getRecording(o);if(!R||R.status!=="recording"){await ee(o,d),ie.get(o)?.unsubscribe(),ie.delete(o),pt(o);return}await Co(o,R,d,c);let y=m().toISOString(),P=pt(o),A=P?Vo(P,y):R.highlights;await p.updateRecording(o,{status:"interrupted",...ko(o,d,R.recorderDaemonSessionId)?{captureTeardown:c?.hostOnly||Y.has(o)?void 0:d.videoRecorderService.getCaptureTeardown(o)}:{},ownerSessionUuid:w?.ownerSessionUuid??R.ownerSessionUuid,endedAt:y,lastAccessedAt:y,sizeBytes:await g(R.filePath),durationMs:gr(R.startedAt,y),highlights:A,recordedPanel:R.recordedPanel,transitions:R.transitions}),await Fe([o])}async function rX(o){let d=await H();await Ho(o,d)}async function Ho(o,d){let c=Ae.get(o);if(c)return c;Y.add(o);let p=Pe.get(o);if(p&&!d.videoRecorderService.listActiveRecordingIds().includes(o))try{await ee(o,d);let g=await d.recordingRepository.getRecording(o);if(g&&g.status!=="recording")return{metadata:ne(g),evictedRecordingIds:[]};return await id(o,p,d)}finally{Y.delete(o)}let m=Bo(o,d);Ae.set(o,m);try{return await m}finally{if(Ae.get(o)===m)Ae.delete(o);if(!d.videoRecorderService.listActiveRecordingIds().includes(o))Y.delete(o)}}async function id(o,d,c){let p=!1;try{return await l(d,{timer:c.timer,timeoutMs:Ul,label:`Stop of replaced-device recording ${o}`,onTimeout:()=>{p=!0}})}catch(m){if(!p)throw m;return e.warn(`[VideoRecording] Settling replaced-device recording on the host: ${r(m)}`,m),Bo(o,c)}}async function Bo(o,d){let c=Z.get(o);if(await ee(o,d),d.videoRecorderService.listActiveRecordingIds().includes(o))try{await d.videoRecorderService.forceStopRecording(o,{hostOnly:!0})}catch(m){if(d.videoRecorderService.listActiveRecordingIds().includes(o))throw await ee(o,d),await gt(o,d).catch((g)=>{e.warn(`[VideoRecording] Failed to re-arm retained safety enforcement for ${o}: ${r(g)}`,g)}),new wr(o,c?.deviceId,m);e.warn(`[VideoRecording] Host-only force-stop of ${o} failed: ${r(m)}`,m)}await hr(o,d,{hostOnly:!0});let p=await d.recordingRepository.getRecording(o);if(!p)throw new t(`No video recording found for id ${o}`);return{metadata:ne(p),evictedRecordingIds:[]}}async function NM(o){let d=await H();try{await d.videoRecorderService.forceStopRecording(o,Y.has(o)?{hostOnly:!0}:void 0)}catch(c){throw await ee(o,d),u(c,`Failed to force-stop video recording ${o}`)}await ee(o,d)}class wr extends t{recordingId;deviceId;constructor(o,d,c){super(`Teardown of video recording ${o}${d?` on ${d}`:""} is unconfirmed; the capture process may still be running on the device. Retry stopping the original recording or recover the device.`,{cause:c});this.recordingId=o;this.deviceId=d;this.name="VideoRecordingTeardownUnconfirmedError"}}async function nX(o){let d=Z.get(o),c=await H();try{await c.videoRecorderService.forceStopRecording(o,Y.has(o)?{hostOnly:!0}:void 0)}catch(g){if(e.warn(`[VideoRecording] Force-stop of ${o} failed: ${r(g)}`,g),c.videoRecorderService.listActiveRecordingIds().includes(o)){await ee(o,c);try{await gt(o,c)}catch(w){e.warn(`[VideoRecording] Failed to re-arm retained safety enforcement for ${o}: ${r(w)}`,w)}throw new wr(o,d?.deviceId,g)}}Ie(o),ge(o);let p=await c.recordingRepository.getRecording(o);if(!p||p.status!=="recording"){await ee(o,c);return}let m=c.now().toISOString();await c.recordingRepository.updateRecording(o,{status:"incomplete",captureTeardown:void 0,ownerSessionUuid:d?.ownerSessionUuid,endedAt:m,lastAccessedAt:m,sizeBytes:await c.statFileSize(p.filePath),durationMs:gr(p.startedAt,m)}),await Fe([o])}async function iX(o,d){let c=Ks(o.deviceId);if(!c)return;let{now:p}=await H();_o(c,d,p().getTime())}async function nc(o={}){let{recordingRepository:d}=await H();return(await d.listRecordings({status:"recording",deviceId:o.deviceId,platform:o.platform})).map((p)=>{let m=Z.get(p.recordingId);return m?{...p,ownerSessionUuid:m.ownerSessionUuid}:p})}function NP(){return W?.videoRecorderService.listActiveRecordingIds()??[]}function yr(){let o=new Set(NP());for(let d of Z.keys())if(!o.has(d))Z.delete(d)}function oX(o,d){let c=ze.get(o);if(c&&c.ownerSessionUuid===void 0)c.ownerSessionUuid=d;yr();let p=[];for(let[m,g]of Z)if(g.deviceId===o&&g.ownerSessionUuid===void 0){g.ownerSessionUuid=d;let{recordingRepository:w}=W;p.push(w.updateRecording(m,{ownerSessionUuid:d}).catch(()=>w.updateRecording(m,{ownerSessionUuid:d})))}return p.length>0?Promise.all(p).then(()=>{return}):void 0}function D1(o,d){return yr(),[...Z].filter(([,c])=>c.ownerSessionUuid===o&&c.deviceId===d).map(([c])=>c)}function FM(o){return NP().includes(o)?Z.get(o)??{}:void 0}async function sX(o={}){let{recordingRepository:d}=await H();return(await d.listRecordings({status:XE,orderByLastAccessed:"desc",ownerSessionUuid:o.ownerSessionUuid})).map(ne)}async function Wu(o,d){let{recordingRepository:c,now:p}=await H(),m=await c.getRecording(o,{ownerSessionUuid:d?.ownerSessionUuid,ownerBaseSessionUuid:d?.ownerBaseSessionUuid});if(!m||m.status==="recording")return null;let g=ne(m);if(d?.touch!==!1){let w=p().toISOString();await c.touchRecording(o,w),g.lastAccessedAt=w}return g}async function od(o,{statFileSize:d}){return o.sizeBytes>0&&await d(o.filePath)>0}var Ro=20;async function So(o,d){for(let c of o)if(await od(c,d))return c;return}async function aX(o={}){let d=await H(),c={status:XE,orderByStartedAt:"desc",ownerSessionUuid:o.ownerSessionUuid},p=await d.recordingRepository.listRecordings({...c,limit:Ro}),m=await So(p,d);if(m)return{recording:ne(m)};if(p.length===Ro){let w=new Set(p.map((y)=>y.recordingId));p=await d.recordingRepository.listRecordings(c);let R=await So(p.filter((y)=>!w.has(y.recordingId)),d);if(R)return{recording:ne(R)}}let g=p[0];return{recording:null,newestWithoutFile:g&&{recordingId:g.recordingId,status:g.status}}}async function Lo(o,d){let{recordingRepository:c}=d,p=await c.getRecording(o);if(!p)return!1;if(p.status==="recording")throw Error(`Cannot delete active recording ${o}`);await Uo(p.filePath);let m=await c.deleteRecording(o);if(m)await Fe([o]);return m}async function Uo(o){let d=pr.dirname(o);if(!await Qo(d))return;await Po.rm(d,{recursive:!0,force:!0})}async function mr(o,d,c){let p=Math.max(0,Math.floor(d*1024*1024)),{recordingRepository:m}=o,g=await m.listRecordings({status:XE,orderByLastAccessed:"asc"}),w=g.reduce((y,P)=>y+(P.sizeBytes??0),0);if(p===0||w<=p)return{evictedRecordingIds:[],currentSizeBytes:w,maxSizeBytes:p};let R=[];for(let y of g){if(y.recordingId===c)continue;if(w<=p)break;try{if(await Lo(y.recordingId,o))R.push(y.recordingId),w-=y.sizeBytes??0}catch(P){e.warn(`[VideoRecording] Failed to evict recording ${y.recordingId}: ${P}`)}}if(w>p)e.warn(`[VideoRecording] Archive size ${w} bytes still exceeds limit ${p} bytes after eviction`);return{evictedRecordingIds:R,currentSizeBytes:w,maxSizeBytes:p}}async function FG(o){let{recordingRepository:d}=await H(),c=await d.getRecording(o);return c?{deviceId:c.deviceId,ownerSessionUuid:c.ownerSessionUuid}:null}async function O1(o,d){let{recordingRepository:c}=await H();return(await c.getRecording(o,{ownerSessionUuid:d?.ownerSessionUuid}))?.status}
25
+ export{On,xG,_M,CG,G8,Y8,WE,xM,Lf,DG,CM,Bd,DM,_1,zE,OG,Ul,OM,J8,X8,x1,MM,Z8,Q8,MG,NG,eX,jE,MP,Hl,C1,ql,tX,rX,NM,nX,iX,nc,NP,oX,D1,FM,sX,Wu,aX,FG,O1};
26
+
27
+ //# debugId=4B8E3334A964424C64756E2164756E21
@@ -0,0 +1,4 @@
1
+ // @bun
2
+ import{PK}from"./chunk-cyy71ggq.js";import"./chunk-z637nqg8.js";import"./chunk-58wzr3mt.js";export{PK as stopManagedAdbServer};
3
+
4
+ //# debugId=C5D53CB8A295148864756E2164756E21
@@ -0,0 +1,14 @@
1
+ // @bun
2
+ import{fi,Ql,$o,om}from"./chunk-hrfytkxh.js";import{Fr,Od,e}from"./chunk-z637nqg8.js";import{R,ap,ti,ls,rk,ut,ad,bp,Sp,Pu,Tn,_c}from"./chunk-r5bxavdx.js";import{nu}from"./chunk-0n6swkwg.js";import{t}from"./chunk-rbpdk7m0.js";var k={success:"boolean",error:"string"};function zu(n,i,o){if(n.hasStructured)n.envelope.structuredContent=i;if(n.textPart)n.textPart.text=o??_c(i);D(n.envelope,i)}function D(n,i){let o=n;for(let[s,r]of Object.entries(k)){if(!(s in o))continue;if(typeof i[s]===r)o[s]=i[s];else delete o[s]}}var wI=["getAndroid","getApple","startDevice","provisionDevice"],uS=`Call ${ap.length>2?`${ap.slice(0,-1).join(", ")}, or ${ap[ap.length-1]}`:ap.join(" or ")} to acquire a new device session.`;function E(n,i){if(!i||i.releaseReason!=="heartbeat-timeout"&&i.releaseReason!=="missing-first-heartbeat")return n;let{ageMs:o,timeoutMs:s}=i.heartbeat,r=i.releaseReason==="heartbeat-timeout"?s+ls:s;return`${n} No heartbeat for ${o} ms (limit ${r} ms; set AUTOMOBILE_SESSION_HEARTBEAT_TIMEOUT_MS to change).`}function mN(n){return{error:{code:"no_active_device_session",message:n.message,reason:n.reason,...fi(n.reason),...p()}}}function p(n){return{retryable:!1,nextAction:ti,...n===void 0?{}:{ownerPid:n},recovery:{action:"acquire_replacement_session",tools:[...ap]}}}function RI(n){if(!O6(n))return;let i=Tn(n)?.payload,o=i?.error,s=o!==null&&typeof o==="object"&&!Array.isArray(o)?o:i,{sessionUuid:r,reason:a}=s??{};return{...typeof r==="string"&&r.length>0?{sessionUuid:r}:{},...typeof a==="string"&&a.length>0?{reason:a}:{}}}function eA({message:n,sessionUuid:i,reason:o,release:s,idleTimeoutMs:r}){return{error:{code:$o,message:E(n,s),sessionUuid:i,reason:o,...fi(o),...Ql(o,r??s?.cliIdleTimeoutMs),...p(s?.ownerPid),...s?{release:s}:{}}}}function fN(n){let i=om(n);if(!i)return;return eA({message:i.message,sessionUuid:i.sessionUuid,reason:i.releaseReason,idleTimeoutMs:i.details.idleTimeoutMs})}function PI(n){if(!(n instanceof Error)||n.name!=="SessionRecoveryIdentityLossError")return;let i=(u)=>Reflect.get(n,u),o=i("sessionUuid"),s=i("terminalReleaseReason"),r=i("reason");if(typeof o!=="string"||typeof s!=="string"||typeof r!=="string")return;let a=i("target"),c=a!==null&&typeof a==="object"?Reflect.get(a,"deviceId"):void 0,d=i("deviceId")??c,l=i("ownerPid");return{message:n.message,sessionUuid:o,reason:s,recoveryReason:r,...typeof d==="string"?{deviceId:d}:{},...typeof l==="number"?{ownerPid:l}:{}}}function x6(n){return{sessionUuid:n.sessionUuid,reason:n.reason,...fi(n.reason),recoveryReason:n.recoveryReason,...n.deviceId===void 0?{}:{deviceId:n.deviceId},...n.ownerPid===void 0?{}:{ownerPid:n.ownerPid}}}function wZ(n){let{error:i}=eA({message:n.message,sessionUuid:n.sessionUuid,reason:n.reason});return{error:{...i,...p(n.ownerPid),...x6(n)}}}function Lr(n){return wI.includes(n)}function hN(n){let i=Tn(n)?.payload,o=i?.runtime,s=o&&typeof o==="object"?o.session:void 0,a=(s&&typeof s==="object"?s.sessionUuid:void 0)??i?.sessionId;return typeof a==="string"&&a.trim().length>0?a:void 0}function f(n){let i=Tn(n)?.payload;if(!i||"platform"in i||"runtime"in i)return i;let o=i.device;if(o&&typeof o==="object"&&!Array.isArray(o))return o;return}function RZ(n){let i=f(n)?.runtime;if(!i||typeof i!=="object"||!("deviceId"in i))return;let{deviceId:o}=i;return typeof o==="string"&&o.trim().length>0?o:void 0}function gN(n){let i=Tn(n)?.payload,o=i?.sessionUuid;if(typeof o!=="string"||o.trim().length===0)return;let s=i?.deviceId;return typeof s==="string"&&s.trim().length>0?{sessionUuid:o,deviceId:s}:{sessionUuid:o}}function PZ(n){let i=f(n)?.platform;return i==="android"||i==="ios"?i:void 0}function C6(n){let i=f(n)?.platform;return typeof i==="string"&&Object.hasOwn(Od,i)?i:void 0}function AZ(n){let i=f(n)?.formFactor;return typeof i==="string"?i:void 0}function D6(n){if(!n||typeof n!=="object"||!("isError"in n)||n.isError!==!0)return!1;let i=Tn(n)?.payload?.error;return i!==null&&typeof i==="object"&&"code"in i&&i.code===nu}function ky(n){return n==="setActiveDevice"||Lr(n)}function TZ(n){if(!n||typeof n!=="object"||!("isError"in n)||n.isError!==!0)return;let i=Tn(n)?.payload;if(!i||i.code!==ad&&i.code!==bp)return;let{retryAfterMs:o}=i;return typeof o==="number"&&Number.isFinite(o)&&o>=0?{retryAfterMs:o}:{}}function kZ(n){if(!D6(n))return;let i=Tn(n)?.payload?.error;if(!i||typeof i!=="object"||!("sessionUuid"in i)||typeof i.sessionUuid!=="string"||!("remainingMs"in i)||typeof i.remainingMs!=="number"||!Number.isFinite(i.remainingMs))return;return{sessionUuid:i.sessionUuid,remainingMs:i.remainingMs}}var P=["session_ownership_lost","no_active_device_session",Sp];function S(n){return typeof n.code==="string"&&P.includes(n.code)||n.nextAction===ti}function O6(n){if(!n||typeof n!=="object"||n.isError!==!0)return!1;let i=n.content;if(!n.structuredContent&&!Array.isArray(i))return!1;let s=Tn(n,(a)=>{e.debug("[MCP] Session-invalid probe: error result was not JSON",{error:a})})?.payload;if(!s)return!1;if(S(s))return!0;let r=s.error;return r!==null&&typeof r==="object"&&!Array.isArray(r)&&S(r)}function I(n){return typeof n==="object"&&n!==null}function Ui(n,i){let o=n.params;if(o&&typeof o==="object"&&Object.hasOwn(o,i))return o[i];return I(n)?n[i]:void 0}var x=(n)=>typeof n==="object"&&n!==null&&!Array.isArray(n),Ey=(n)=>{let i=[],o=[],s=!1,r=!1;if(!n)return{labels:i,definitions:o,hasDefinitions:s,hasLabels:r};for(let a of n){if(typeof a==="string"){i.push(a),r=!0;continue}if(x(a)){i.push(a.label),o.push(a),s=!0;continue}}return{labels:i,definitions:o,hasDefinitions:s,hasLabels:r}},EZ=(n)=>Ey(n).labels,IZ=(n,i)=>Ey(n).definitions.find((o)=>o.label===i)?.platform;function g(n){return`${n.positions.join(",")}|${n.arrived.map((i)=>i?1:0).join("")}`}function v(n,i,o){return n[o].events[i.positions[o]]}function L(n,i){return n.every((o,s)=>i.positions[s]>=o.events.length)}function b(n,i,o){let s=[];for(let r=0;r<n.length;r++)if(i.arrived[r]&&v(n,i,r)?.lock===o)s.push(r);return s}function _(n,i,o){let s=v(n,i,o),r=[...i.positions],a=[...i.arrived],c=[...b(n,i,s.lock),o];if(c.length<s.deviceCount)return a[o]=!0,{state:{positions:r,arrived:a}};for(let l of c)r[l]+=1,a[l]=!1;let d=c.map((l)=>n[l].device).sort().join(", ");return{state:{positions:r,arrived:a},release:`"${s.lock}" {${d}}`}}function A(n,i){let o=[];for(let s=0;s<n.length;s++)if(!i.arrived[s]&&v(n,i,s)!==void 0)o.push(_(n,i,s));return o}function O(n){return n.positions.reduce((i,o)=>i+o,0)}function T(n,i){let o=[],s=i;while(s!==void 0){let r=n.get(s);if(r?.release)o.push(r.release);s=r?.parent}return o.reverse()}function M(n,i,o){let{state:s}=o,r=[],a=[];for(let c=0;c<n.length;c++){let d=v(n,s,c);if(d===void 0){a.push(n[c].device);continue}let l=b(n,s,d.lock).map((u)=>n[u].device);r.push({device:n[c].device,event:d,waitingWith:l})}return{stalled:r,finished:a,releasedGenerations:T(i,o.key)}}function m(n,i){let o=i;while(n.get(o)!==o)o=n.get(o);return o}function U(n){let i=new Map,o=(r,a)=>{let c=`${r}:${a}`;if(!i.has(c))i.set(c,c);return c};for(let r of n){let a=o("device",r.device);for(let c of r.events)i.set(m(i,o("lock",c.lock)),m(i,a))}let s=new Map;for(let r of n){let a=m(i,`device:${r.device}`);s.set(a,[...s.get(a)??[],r])}return Array.from(s.values())}function y(n,i=1e4){for(let o of U(n)){let r=o.some((a)=>a.events.some((c)=>c.unmodeled===!0))?null:N(o,i);if(r!==null)return r}return null}function N(n,i){let o={positions:n.map(()=>0),arrived:n.map(()=>!1)},s=new Map([[g(o),{}]]),r=[o],a;while(r.length>0){let c=r.pop();if(L(n,c))return null;let d=g(c),l=A(n,c);if(l.length===0){let u=O(c);if(a===void 0||u>a.progress)a={key:d,state:c,progress:u};continue}for(let u of l){let h=g(u.state);if(s.has(h))continue;if(s.size>=i)return null;s.set(h,{parent:d,release:u.release}),r.push(u.state)}}return a===void 0?null:M(n,s,a)}function j(n){let{device:i,event:o,waitingWith:s}=n;return`device "${i}" waits at ${o.tool} lock "${o.lock}" (step ${o.stepIndex}; ${s.length}/${o.deviceCount} arrived: ${s.join(", ")})`}function w(n){let i=Array.from(new Set(n.stalled.map((r)=>`"${r.event.lock}"`))),o=n.releasedGenerations.length>0?n.releasedGenerations.join(" -> "):"none",s=n.finished.length>0?` Tracks already finished (cannot arrive again): ${n.finished.join(", ")}.`:"";return`barrier/criticalSection coordination can never complete: under every possible arrival order at least one device track deadlocks. One reachable stall: ${n.stalled.map(j).join("; ")}. Locks involved: ${i.join(", ")}. Generations released before the stall: ${o}.${s} Each device's track runs sequentially, so a device blocked at one lock never reaches the later lock its peers are waiting at. Reorder the coordination steps so devices visit shared locks in a consistent order, or add the arrivals the stalled locks are missing.`}var F=new Set(["barrier","criticalSection"]);function Iy(n){return F.has(n)}function C(n){return typeof n==="string"&&n.length>0}function B(n){return typeof n==="number"&&Number.isInteger(n)&&n>=1}function V(n){return typeof n==="object"&&n!==null}var G=new Set(["android","ios","tvos","visionos","watchos"]);class Gf{static validate(n){if(!n.name||typeof n.name!=="string")throw new t("Plan must have a valid name");if(!n.steps||!Array.isArray(n.steps))throw new t("Plan must have a steps array");if(this.validateMultiDeviceRequirements(n),n.devices!==void 0)this.validateDevicesField(n),this.validateDeviceLabelsPresent(n);this.validateCoordinationLocks(n)}static validateUntrusted(n){if(!V(n))throw new t("Plan must have a valid name");this.validate(n)}static validateDevicesField(n){if(!Array.isArray(n.devices))throw new t("Plan 'devices' field must be an array of device labels");if(n.devices.length===0)throw new t("Plan 'devices' array cannot be empty. Remove the field or specify at least one device.");let{labels:i,definitions:o,hasDefinitions:s,hasLabels:r}=Ey(n.devices);if(s&&r)throw new t("Plan 'devices' must be a list of labels or a list of objects with label/platform (do not mix formats).");if(s)for(let c of o)this.validateDeviceDefinition(c);if(i.length!==n.devices.length)throw new t("Plan 'devices' entries must be strings or objects with label/platform.");if(new Set(i).size!==i.length)throw new t(`Plan 'devices' array contains duplicate labels: [${i.join(", ")}]`);for(let c of i)if(typeof c!=="string"||c.trim()==="")throw new t(`Invalid device label: ${JSON.stringify(c)}. Device labels must be non-empty strings.`)}static validateDeviceDefinition(n){if(!n.label||n.label.trim()==="")throw new t(`Invalid device label: ${JSON.stringify(n.label)}. Device labels must be non-empty strings.`);if(!n.platform||!G.has(n.platform))throw new t(`Invalid device platform for ${n.label}: ${JSON.stringify(n.platform)}.`)}static validateDeviceLabelsPresent(n){if(!n.devices||n.devices.length===0)return;let i=new Set(Ey(n.devices).labels),o={mismatchedInCriticalSection:[],missingLabels:[],invalidLabels:[],missingInCriticalSection:[],invalidInCriticalSection:[]};for(let l=0;l<n.steps.length;l++){let u=n.steps[l];if(this.recordStepDeviceLabel(u,l,i,o),u.tool==="criticalSection")this.recordCriticalSectionDeviceLabels(u,l,i,o)}let{missingLabels:s,invalidLabels:r,missingInCriticalSection:a,invalidInCriticalSection:c}=o,d=[...o.mismatchedInCriticalSection];if(s.length>0){let l=s.map((u)=>`step ${u.index} (${u.tool})`).join(", ");d.push(`Plan declares 'devices' field but the following steps are missing 'device' parameter: ${l}`)}if(r.length>0){let l=r.map((u)=>`step ${u.index} (${u.tool}): device="${u.device}"`).join(", ");d.push(`Plan declares devices [${Array.from(i).join(", ")}] but the following steps use invalid device labels: ${l}`)}if(a.length>0){let l=a.map((u)=>`step ${u.parentIndex}.steps[${u.subIndex}] (${u.tool})`).join(", ");d.push(`Every step inside a criticalSection must declare a 'device' parameter, but the following sub-steps are missing it: ${l}`)}if(c.length>0){let l=c.map((u)=>`step ${u.parentIndex}.steps[${u.subIndex}] (${u.tool}): device="${u.device}"`).join(", ");d.push(`Plan declares devices [${Array.from(i).join(", ")}] but the following criticalSection sub-steps use invalid device labels: ${l}`)}if(d.length>0)throw new t(d.join(`
3
+ `))}static recordStepDeviceLabel(n,i,o,s){let r=Ui(n,"device");if(r===void 0||r===null||r==="")s.missingLabels.push({index:i,tool:n.tool});else if(typeof r!=="string"||!o.has(r))s.invalidLabels.push({index:i,tool:n.tool,device:String(r)})}static recordCriticalSectionDeviceLabels(n,i,o,s){let r=n.params?.steps;if(!Array.isArray(r))return;let a=Ui(n,"device"),c=typeof a==="string"&&o.has(a)?a:void 0;for(let d=0;d<r.length;d++)this.recordCriticalSectionSubstepLabel(r[d],i,d,{labelErrors:s,deviceSet:o,ownerDevice:c})}static recordCriticalSectionSubstepLabel(n,i,o,{labelErrors:s,deviceSet:r,ownerDevice:a}){if(!n||typeof n!=="object")return;let d=n.params?.device,l=String(n.tool??"unknown");if(d===void 0||d===null||d==="")s.missingInCriticalSection.push({parentIndex:i,subIndex:o,tool:l});else if(typeof d!=="string"||!r.has(d))s.invalidInCriticalSection.push({parentIndex:i,subIndex:o,tool:l,device:String(d)});else if(a!==void 0&&d!==a)s.mismatchedInCriticalSection.push(`step ${i}.steps[${o}] (${l}): device="${d}" differs from criticalSection owner device="${a}". Put it in its own step for that device or use a separate criticalSection step.`)}static validateCoordinationLocks(n){this.validateNoAuthoredLockNamespace(n),this.validateCriticalSectionLocks(n),this.validateBarrierParams(n),this.validateCoordinationTimeouts(n),this.validateNoCrossToolLockSharing(n),this.validateBarrierConsistentDeviceCount(n),this.validateBarrierDistinctDeviceCounts(n),this.validateBarrierGenerationCompleteness(n),this.validateBarrierExcessDeviceArrivals(n),this.validateCoordinationScheduleFeasibility(n)}static validateCoordinationScheduleFeasibility(n){let i=this.collectCoordinationTracks(n);if(i===null||i.length<2)return;let o=y(i);if(o!==null)throw new t(w(o))}static collectCoordinationTracks(n){let i=new Map;for(let o=0;o<n.steps.length;o++){let s=n.steps[o];if(!Iy(s.tool))continue;let r=this.resolveCoordinationEvent(s,o);if(r===null)return null;let a=i.get(r.device)??{device:r.device,events:[]};a.events.push(r.event),i.set(r.device,a)}return Array.from(i.values())}static resolveCoordinationEvent(n,i){let o=Ui(n,"device"),s=Ui(n,"lock"),r=Ui(n,"deviceCount");if(!C(o)||!C(s)||!B(r))return null;let a=n.optional===!0||this.hasNestedCoordination(n);return{device:o,event:{tool:n.tool,lock:s,deviceCount:r,stepIndex:i,unmodeled:a}}}static hasNestedCoordination(n){let i=Ui(n,"steps");if(!Array.isArray(i))return!1;return i.some((o)=>typeof o==="object"&&o!==null&&("tool"in o)&&Iy(String(o.tool)))}static validateCoordinationTimeouts(n){let i=[];for(let o=0;o<n.steps.length;o++){let s=n.steps[o];if(s.tool!=="criticalSection"&&s.tool!=="barrier")continue;let r=Ui(s,"timeout");if(r===void 0)continue;if(typeof r!=="number"||!Number.isInteger(r)||r<1||r>Fr)i.push(`${s.tool} step ${o} declares 'timeout' of ${JSON.stringify(r)}, which must be a positive integer no greater than setTimeout's usable range (${Fr}ms, ~24.8 days) -- Node/Bun clamp larger delays to 1ms instead of honoring them.`)}if(i.length>0)throw new t(i.join(`
4
+ `))}static validateNoAuthoredLockNamespace(n){let i=[];for(let o=0;o<n.steps.length;o++){let s=n.steps[o];if(Ui(s,"__lockNamespace")!==void 0)i.push(`step ${o} (${s.tool}) sets '__lockNamespace', which is a reserved field injected internally by the plan executor. Authored plans must not set it.`)}if(i.length>0)throw new t(i.join(`
5
+ `))}static validateNoCrossToolLockSharing(n){let i=new Set,o=new Set;for(let r of n.steps){if(r.tool!=="criticalSection"&&r.tool!=="barrier")continue;let a=Ui(r,"lock");if(typeof a!=="string"||a.length===0)continue;(r.tool==="criticalSection"?i:o).add(a)}let s=Array.from(i).filter((r)=>o.has(r));if(s.length>0)throw new t(`lock name${s.length===1?"":"s"} ${s.map((r)=>`"${r}"`).join(", ")} ${s.length===1?"is":"are"} used by both a criticalSection step and a barrier step. Both tools share the runtime coordinator's lock namespace and expected-count state, so mixing tool types on the same lock name is racy and can pair mismatched participants or overwrite the expected count. Use a distinct lock name per tool type.`)}static validateCriticalSectionLocks(n){let i=this.collectCriticalSectionLockUsage(n),o=[];for(let[s,r]of i.entries())this.validateCriticalSectionLock(s,r,o);if(o.length>0)throw new t(o.join(`
6
+ `))}static collectCriticalSectionLockUsage(n){let i=new Map;for(let o=0;o<n.steps.length;o++){let s=n.steps[o];if(s.tool!=="criticalSection")continue;let r=Ui(s,"lock");if(typeof r!=="string"||r.length===0)continue;let a=i.get(r)??[];a.push({stepIndex:o,device:Ui(s,"device"),deviceCount:Ui(s,"deviceCount")}),i.set(r,a)}return i}static validateCriticalSectionLock(n,i,o){let s=new Set(i.map((a)=>a.deviceCount).filter((a)=>typeof a==="number"));if(s.size>1){let a=i.map((c)=>`step ${c.stepIndex} deviceCount=${String(c.deviceCount)}`).join(", ");o.push(`criticalSection lock "${n}" has inconsistent deviceCount values: ${a}. All steps sharing a lock must declare the same deviceCount.`);return}let r=s.size===1?s.values().next().value:void 0;if(r!==void 0&&i.length!==r)o.push(`criticalSection lock "${n}" declares deviceCount=${r} but ${i.length} step${i.length===1?"":"s"} reference${i.length===1?"s":""} it. Every participating device needs its own criticalSection step with this lock.`);this.validateCriticalSectionDevices(n,i,o)}static validateCriticalSectionDevices(n,i,o){let s=new Map;for(let r of i){if(typeof r.device!=="string"||r.device.length===0)continue;let a=s.get(r.device)??[];a.push(r.stepIndex),s.set(r.device,a)}for(let[r,a]of s.entries())if(a.length>1)o.push(`criticalSection lock "${n}" is entered twice by device "${r}" (steps ${a.join(", ")}). Each device can participate in a given lock at most once.`)}static validateBarrierParams(n){let i=[];for(let o=0;o<n.steps.length;o++){let s=n.steps[o];if(s.tool!=="barrier")continue;let r=Ui(s,"lock");if(typeof r!=="string"||r.length===0)i.push(`barrier step ${o} is missing a non-empty 'lock' parameter.`);let a=Ui(s,"deviceCount");if(typeof a!=="number"||!Number.isInteger(a)||a<1)i.push(`barrier step ${o} must declare a positive integer 'deviceCount', got ${JSON.stringify(a)}.`)}if(i.length>0)throw new t(i.join(`
7
+ `))}static validateBarrierDistinctDeviceCounts(n){let i=this.collectBarrierLockUsage(n),o=[];for(let[s,r]of i.entries())for(let a of r.deviceCounts)if(r.devices.size<a){let c=Array.from(r.devices).join(", ")||"none";o.push(`barrier lock "${s}" declares deviceCount=${a} but only ${r.devices.size} distinct device${r.devices.size===1?"":"s"} (${c}) ever target it in this plan. No round can ever complete.`)}if(o.length>0)throw new t(o.join(`
8
+ `))}static validateBarrierConsistentDeviceCount(n){let i=this.collectBarrierLockUsage(n),o=[];for(let[s,r]of i.entries()){if(r.deviceCounts.size<=1)continue;let a=Array.from(r.deviceCounts).sort((c,d)=>c-d).join(", ");o.push(`barrier lock "${s}" is reused with inconsistent deviceCount values (${a}). The runtime coordinator keeps a single shared expected count per lock name, so mixed-count reuse is racy and can deadlock depending on arrival order. Use a distinct lock name for each deviceCount instead.`)}if(o.length>0)throw new t(o.join(`
9
+ `))}static validateBarrierGenerationCompleteness(n){let i=this.collectBarrierLockUsage(n),o=[];for(let[s,r]of i.entries()){if(r.deviceCounts.size!==1)continue;let a=r.deviceCounts.values().next().value;if(a<1||r.arrivals%a===0)continue;o.push(`barrier lock "${s}" has ${r.arrivals} total arrival${r.arrivals===1?"":"s"} across the plan, which is not a multiple of its deviceCount=${a}. At least one generation is necessarily incomplete and would deadlock waiting for a device that never arrives.`)}if(o.length>0)throw new t(o.join(`
10
+ `))}static validateBarrierExcessDeviceArrivals(n){let i=this.collectBarrierLockUsage(n),o=[];for(let[s,r]of i.entries()){if(r.deviceCounts.size!==1)continue;let a=r.deviceCounts.values().next().value;if(a<1||r.arrivals%a!==0)continue;let c=r.arrivals/a;for(let[d,l]of r.deviceOccurrences.entries()){if(l<=c)continue;o.push(`barrier lock "${s}" has ${c} generation${c===1?"":"s"} available (deviceCount=${a}, ${r.arrivals} total arrivals), but device "${d}" arrives ${l} times. A device can participate in a lock at most once per generation, since each device's track executes sequentially, so this device would deadlock waiting for a generation that never admits it again.`)}}if(o.length>0)throw new t(o.join(`
11
+ `))}static collectBarrierLockUsage(n){let i=new Map;for(let o of n.steps)if(o.tool==="barrier")this.recordBarrierLockUsage(i,o);return i}static recordBarrierLockUsage(n,i){let o=Ui(i,"lock");if(typeof o!=="string"||o.length===0)return;let s=n.get(o)??{devices:new Set,deviceCounts:new Set,arrivals:0,deviceOccurrences:new Map};s.arrivals+=1;let r=Ui(i,"device");if(typeof r==="string"&&r.length>0)s.devices.add(r),s.deviceOccurrences.set(r,(s.deviceOccurrences.get(r)??0)+1);let a=Ui(i,"deviceCount");if(typeof a==="number"&&Number.isInteger(a)&&a>=1)s.deviceCounts.add(a);n.set(o,s)}static hasMultiDeviceFeatures(n){if(n.devices&&n.devices.length>0)return!0;for(let i of n.steps){if(i.tool==="criticalSection"||i.tool==="barrier")return!0;if(i.params?.device!==void 0)return!0}return!1}static validateMultiDeviceRequirements(n){if(this.hasMultiDeviceFeatures(n)&&(!n.devices||n.devices.length===0))throw new t("Plan uses multi-device features (device labels or criticalSection/barrier) but does not declare 'devices' field. Add a 'devices' array at the top level of your plan.")}}function Qp(n,i){return Math.round(Math.min(1,Math.max(0,n/i))*1e4)/1e4}function yN(n,i,o=!1){let s=R.sessionManager();if(!s)return;let r=n?s.getSessionForDevice(n)??void 0:void 0;if(!r&&o){let{error:a}=mN({message:(n?`deviceGestureRecording start refused: no session of yours holds device '${n}'. `:"deviceGestureRecording start refused: the session holds no device. ")+"Call getAndroid or getApple to acquire a session on it first.",reason:"device_not_held"});return{success:!1,...a,error:a.message}}if(!n)return;try{Pu({action:"deviceGestureRecording start",deviceId:n,ownerSessionUuid:r,requesterSessionUuid:i,sessionManager:s,remedy:rk})}catch(a){if(!(a instanceof ut))throw a;return e.warn(`[DeviceGestureRecording] ${a.message}`),{success:!1,error:a.message,...a.toPayload()}}return}class xH extends t{payload;code="no_active_device_session";retryable=!1;constructor(n,i){super(n);this.payload=i;this.name="DeviceGestureRecordingStartUnheldError"}toPayload(){return this.payload}get reason(){return this.payload.reason}get nextAction(){return this.payload.nextAction}}function _Z(n,i,o=!0){let s=yN(n,i,o);if(!s)return;let r=s.error,a={...s};if(delete a.success,delete a.error,"code"in s&&s.code==="device_owned_by_other_session"&&n)throw new ut("deviceGestureRecording start",n,i,{remedy:rk,message:r});throw new xH(r,a)}
12
+ export{zu,wI,uS,mN,RI,eA,fN,PI,x6,wZ,Lr,hN,RZ,gN,PZ,C6,AZ,D6,ky,TZ,kZ,O6,Ui,Ey,EZ,IZ,Iy,Gf,Qp,yN,xH,_Z};
13
+
14
+ //# debugId=796FABA4F19F771D64756E2164756E21
@@ -0,0 +1,5 @@
1
+ // @bun
2
+ import{i,WO,we,Ra,e}from"./chunk-z637nqg8.js";import{me,U,o,pd,kn,N,Qm,pa,ft,Mt,wi,nt,tf,ve,Zo,Kn,Ee,Hr,qr,x}from"./chunk-r5bxavdx.js";import{IC}from"./chunk-7rz9898k.js";import{yW,MW,NW,OT,LW}from"./chunk-0qsy1qsx.js";import{Qh,Vo,KC,yi}from"./chunk-rvvy1bte.js";import{r,t,u,St}from"./chunk-rbpdk7m0.js";var Kc=5000,EF=2500,rl=30000,ou=1770000;var IF=800,S5=500;async function uv(n,s,c,{timeoutMs:a=IF,context:l="while TalkBack is on",platform:d="android"}={}){let p=s.now()+Math.max(0,a);while(s.now()<p){o(c);let m=await n(p-s.now());if(o(c),m&&s.now()<=p)return m;let h=p-s.now();if(h>0)await s.sleep(Math.min(S5,h))}throw o(c),new t(d==="ios"?`Unable to retrieve a fresh tap hierarchy: iOS runner failed to produce a view hierarchy ${l}. Observe again.`:`Unable to retrieve a fresh tap hierarchy: hierarchy unavailable from the accessibility service ${l}. Observe again and check that the accessibility service is running.`)}async function _F(n){let{adb:s,x:c,y:a,durationMs:l,signal:d,assertCurrent:p,logTag:m}=n;o(d);let h=Vo(l),f=Hr(l),b=async(y)=>{p?.();let w=await s.executeCommand(y,{timeoutMs:f,signal:d});KC(y,w)};try{try{await b(`shell input touchscreen swipe ${c} ${a} ${c} ${a} ${h}`)}catch(y){if(e.warn(`[${m}] touch input swipe failed: ${r(y)}`,y),IC(y,l,d),y instanceof Ee)throw y;o(d),await b(`shell input swipe ${c} ${a} ${c} ${a} ${h}`)}}catch(y){if(e.warn(`[${m}] Android long press failed: ${r(y)}`,y),IC(y,l,d),y instanceof Ee)throw y;throw u(y,"Android long press failed")}}function SV(n,s=""){return`Element with exact text '${n}'${s} is no longer present; re-observe and pick it again.`}function tp(n,s){return n.x>=s.left&&n.x<s.right&&n.y>=s.top&&n.y<s.bottom}function xee(n){return!!n&&n.left<n.right&&n.top<n.bottom}function Gc(n,s){let c=nt(s);return(c&&n.find((a)=>a.source===c))??n.find((a)=>a.element&&a.bounds&&Mt(a.bounds,s.bounds)&&a.nativeId===s["resource-id"]&&a.nodeKey===s["view-id"])}function wV(n,s,c){let a=c==="ios"?qr(s):void 0,l=nt(n);return a&&(!l||a.nodes.some((d)=>d.source===l))?a.hierarchy:s}function Cee(n){return n===void 0?"touch":"none"}class So extends t{reason;constructor(n,s){super(n);this.reason=s}}function xF(n){return E(n)}function V(n,s){return E(n,T(n)).approvedClickNodes.has(s)}function k(n){let s=new Set(["button","icon","view","id","com","android","systemui"]);return new Set(n.toLowerCase().split(/[^a-z0-9]+/).filter((c)=>c.length>1&&!s.has(c)))}function P(n,s,c){let a=ft(n)??n,{approvedClickNodes:l}=E(a,T(a)),d=k(c.split("/").pop()??c),p=new ve().project(a).filter((m)=>m.source!==s&&l.has(m.source)).map((m)=>m.properties.text||m.properties["content-desc"]||"").filter((m)=>m!==""&&[...k(m)].some((h)=>d.has(h)));return[...new Set(p)]}function E(n,s=new Set){let c={hiddenNodes:new Set,approvedClickNodes:new Set,nodeClickTargets:s},a=Qm(n),l=O(n,c),d=a&&a!==n?O(a,c):void 0,{hiddenNodes:p,approvedClickNodes:m}=c;if(p.size===0)return{hierarchy:n,hiddenNodes:p,approvedClickNodes:m};if(d)pa(l,d);return{hierarchy:l,hiddenNodes:p,approvedClickNodes:m}}function F(n,s){let c=we(n);return c["visible-to-user"]===!1&&(s||typeof c["resource-id"]==="string"&&c["resource-id"].startsWith("com.android.systemui"))}function O(n,{hiddenNodes:s,approvedClickNodes:c,nodeClickTargets:a}){let l=s.size,d=new Set(n.windows?.filter((f)=>f.packageName?.startsWith("com.android.systemui")).map((f)=>f.id));function p(f,b=!1){if(Array.isArray(f))return f.flatMap((g)=>p(g,b));let y=we(f),w=b||typeof y.windowId==="number"&&d.has(y.windowId),S=Ra(f).flatMap((g)=>p(g,w)),C=F(f,w);if(C&&!a.has(f))return s.add(f),S;let v=Ra(f),A=S.length===v.length&&S.every((g,_)=>g===v[_])?f:{...f,node:S};if(C)c.add(f),c.add(A);return[A]}let m=n.hierarchy.node?p(n.hierarchy.node):[],h=n.windows?.map((f)=>({...f,hierarchy:f.hierarchy?{node:p(f.hierarchy,f.packageName?.startsWith("com.android.systemui"))}:void 0}));if(s.size===l)return{...n};return{...n,hierarchy:{...n.hierarchy,node:{node:m}},windows:h}}function T(n){let s=ft(n)??null;if(!Qh(s))return new Set;let c=new ve().project(s);return new Set(c.filter((a)=>{if(a.properties["visible-to-user"]!==!1||!a.bounds||a.properties["hierarchy-source"]==="uiautomator"||!kn(a.properties.actions,"click"))return!1;let l=yi(a.properties);if(!l)return!1;let d=c.filter((p)=>tf(p.properties,l));return new Set(d.map((p)=>p.source)).size===1&&!!d[0]?.bounds&&wi(d[0].bounds,a.bounds,0)}).map((a)=>a.source))}function X(n,s,c){let a=s.selection.matchedElement??s.selection.element,l=a&&nt(a);if(!l||!c.has(l))return;let d=n.text??n.textAny?.join(" | ")??n.elementId??n.testTag;return new So(`Target '${d}' is hidden XR system chrome (visible-to-user false, systemui). Use observe to find the visible control or tapAt with screen coordinates from the screenshot.`,"no-visible-tap-area")}function D(n,s){let a=(n.selection.matchedElement??n.selection.element)?.["visible-to-user"]===!1?s():void 0;if(a)return{...n,selection:{...n.selection,element:null,matchedElement:void 0},visibilityError:a};return n.selection.element||n.selection.matchedElement?n:{...n,visibilityError:s()}}function Dee(n,s,c){let a=E(s,n.action==="tap"?T(s):void 0);if(a.hiddenNodes.size===0)return c(n,s);let l={...n,selectionStrategy:"first",index:void 0},d=()=>X(n,c(l,s),a.hiddenNodes);try{return D(c(n,a.hierarchy),d)}catch(p){if(!(p instanceof t))throw p;let m=c(l,a.hierarchy);if(m.selection.element||m.selection.matchedElement)throw p;let h=d();if(!h)throw p;return{selection:{element:null,indexInMatches:-1,totalMatches:0,strategy:n.selectionStrategy??"first"},containerFound:!0,visibilityError:h}}}function Pm(){return new t("The element's bounds are in an XR panel coordinate space and are not tappable screen coordinates. A safe accessibility node click is unavailable. Use tapAt with screen coordinates from the screenshot instead.")}function I(n){return new t(`The accessibility node click was sent to CtrlProxy but its result was not confirmed (${n}), and the screen did not visibly change. The click may still have landed. Do not retry automatically: observe the screen first.`)}function B(n){return n.dispatched===!0&&n.acknowledged===!1}function W(n,s){if(s===void 0||!n.success)return;if(n.effect?.screenChanged===!0){n.warnings=[...n.warnings??[],`CtrlProxy did not confirm the accessibility node click (${s}), but the screen changed after it (${n.effect.basis}), so the click is reported as landed.`];return}n.success=!1,n.error=I(s).message,delete n.warnings}function Oee(n){return{onUnconfirmedClick:(s)=>{n.xrClick={...n.xrClick,unconfirmed:s}},onHiddenChromeClick:(s)=>{n.xrClick={...n.xrClick,hiddenChrome:s}}}}function Mee(n,s){W(n,s?.unconfirmed),L(n,s?.hiddenChrome)}function q(n){let s=n.alternatives.length?`Select the labeled control instead: ${n.alternatives.map((c)=>`tapOn {text: "${c}"}`).join(" or ")}, or observe and use tapAt with screen coordinates from the screenshot.`:"Observe the screen and use tapAt with screen coordinates from the screenshot.";return new t(`Target '${n.label}' is hidden XR system chrome (visible-to-user false, systemui): its accessibility node accepted the click but the screen did not change, so the control was not activated. ${s}`)}function L(n,s){if(!s||!n.success||n.effect?.screenChanged!==!1)return;n.success=!1,n.error=q(s).message,delete n.warnings}function j(n,s){let c=n.class??n.className,l=Boolean(n.text||n["content-desc"])?void 0:s,d={className:typeof c==="string"?H(c):void 0,text:H(n.text),contentDesc:H(n["content-desc"]),labelText:H(l?.text),labelContentDesc:H(l?.["content-desc"])};return Object.values(d).some((p)=>p!==void 0)?d:void 0}function H(n){return n||void 0}function z(n,s,c,a){let l=ft(c.resolvedHierarchy)??null;if(n!=="tap"||s["hierarchy-source"]==="uiautomator"||!s.bounds||!Qh(l))throw Pm();let d=new ve().project({...l,windows:pd(l.hierarchy,l.windows)}),p=K(d,s,c,{hierarchy:l,resolveContainer:a}),m=(h,f)=>{let b=yi(h);if(!b){let w=d.filter((C)=>C.bounds&&wi(C.bounds,h.bounds,0)&&kn(C.properties.actions,"click"));if(new Set(w.map((C)=>C.source)).size!==1||!R(d,w[0],p))return;let S=j(w[0].properties,f??w[0].hoistedLabelSources?.[0]?.properties);if(!S)return;return{expectedBounds:h.bounds,identityHints:S}}let y=d.filter((w)=>tf(w.properties,b));return new Set(y.map((w)=>w.source)).size===1&&y[0]?.bounds&&wi(y[0].bounds,h.bounds,0)?{selector:b,expectedBounds:h.bounds}:void 0};if(kn(s.actions,"click")){let h=m(s);if(h)return{...h,...h.selector&&s["visible-to-user"]===!1&&Y(d,s,l)?{allowInvisible:!0,hiddenChrome:G(d,s,l)}:{}};throw Pm()}return Q(s,d,m,p)}function Y(n,s,c){let a=Gc(n,s);return a!==void 0&&V(c,a.source)}function G(n,s,c){let a=s["resource-id"]??"",l=Gc(n,s);return{label:a||s["content-desc"]||s.text||"",alternatives:l?P(c,l.source,a):[]}}function K(n,s,c,{hierarchy:a,resolveContainer:l}){let d=c.container!==void 0,p=d?l?.(a):void 0,m=p?Gc(n,p):void 0,h=m?n.indexOf(m):-1,f=Gc(n,s),b=m&&f&&R(n,m,{scoped:!0,containerIndex:n.indexOf(f)}),y={scoped:d&&!b,containerIndex:h};if(y.scoped&&(!f||!R(n,f,y)))throw Pm();return y}function R(n,s,c){if(!c.scoped)return!0;for(let a=s.parentIndex;a!==void 0;a=n[a].parentIndex)if(a===c.containerIndex)return!0;return!1}function J(n,s,c){let a=[];for(let d=s.parentIndex;d!==void 0;d=n[d].parentIndex)a.push(d);if(!c.scoped)return a;let l=a.indexOf(c.containerIndex);if(l<0)throw Pm();return a.slice(0,l)}function Q(n,s,c,a){let l=yi(n),d=s.filter((p)=>p.bounds&&wi(p.bounds,n.bounds,0)&&p.properties.text===n.text&&!kn(p.properties.actions,"click")&&(n["view-id"]?p.properties["view-id"]===n["view-id"]:l&&tf(p.properties,l)));if(new Set(d.map((p)=>p.source)).size!==1)throw Pm();for(let p of J(s,d[0],a)){let m=s[p],h=m.bounds;if(!h||h.left>n.bounds.left||h.top>n.bounds.top||h.right<n.bounds.right||h.bottom<n.bounds.bottom||!kn(m.properties.actions,"click"))continue;let f=c({...m.properties,bounds:h},{...d[0].properties,bounds:n.bounds});if(f)return f}throw Pm()}async function CF(n,{action:s,element:c,signal:a,options:l,resolveContainer:d}){o(a);let{selector:p,hiddenChrome:m,...h}=z(s,c,l??{},l&&d&&((b)=>d(b,l))),f=await Z(n,{target:{selector:p,...h},signal:a,displayFence:l?.displayFence});if(ee(f,l),f.success&&m)l?.onHiddenChromeClick?.(m)}async function Z(n,{target:{selector:s,...c},signal:a,displayFence:l}){try{if(!await n().supportsNodeActionSelectors(void 0,a,!0))throw Pm();o(a),l?.assertCurrent();let d=await n().requestNodeAction("click",s,{signal:a,...c});return o(a),d}catch(d){if(o(a),d instanceof Ee)throw d;throw new t(Pm().message,{cause:d})}}function ee(n,s){if(n.success){s?.onTalkBackActivation?.();return}if(B(n)){let c=n.error||"no reply from CtrlProxy";if(e.warn(`[XrNodeClick] Click was dispatched but not confirmed: ${c}`),s?.onUnconfirmedClick){s.onUnconfirmedClick(c);return}throw I(c)}throw Pm()}var re=300,cC=1500,DF=100;async function OF(n,s,c,{preTapHash:a,signal:l}){l=me(l),o(l),await n.sleep(re),o(l);let d;try{d=await s(cC)}catch(m){if(l?.aborted||m instanceof Error&&m.name==="AbortError"||m instanceof Ee||m instanceof Kn)throw m;return e.warn(`[androidGhostTapRetry] Post-tap hierarchy unreadable: ${r(m)}`,m),{status:"unavailable"}}if(o(l),!d||!a)return{status:"unavailable"};let p=c(d);if(!p)return{status:"unavailable"};if(p!==a)return{status:"changed"};return{status:"unchanged",hierarchy:d}}async function M(n,s,c,a){let d=(a?.timer??i).now()+s,p=await n.requestHierarchySync(new U,N.isRawElementSearchEnabled(),c,{timeoutMs:s}),m=p?n.convertToViewHierarchyResult(p.hierarchy):null;if(!m)return null;if(WO(m)&&a)return yW(m,a,d,c);return m}function MF(n,s){if(!n)return!0;let c=s?.hierarchy;return Boolean(c&&typeof c==="object"&&"error"in c&&c.error)}async function NF(n,s){let{device:c}=n,{signal:a,screenSize:l}=s,d=Math.max(0,s.timeoutMs),p=n.timer.now()+d;switch(c.platform){case"android":{let m=await M(n.androidService,d,a,{adb:n.adb,timer:n.timer});return m?n.prepareForResponse(m,l):null}case"ios":{let m=x.getInstance(c),h=s.forceCapture?await m.requestHierarchySyncForTapRevalidation(void 0,!1,a,d):await m.requestHierarchySync(void 0,!1,a,{timeoutMs:d});if(!h?.hierarchy)return null;let f=n.viewHierarchy.normalizeIosHierarchy(m.convertToViewHierarchyResult(h.hierarchy)),b=h[Zo];if(b==="device"||b==="client-cache")Object.assign(f,{[Zo]:b});let y=n.prepareForResponse(f,l);return await NW(y,n.sdkBridge??MW(c,n.timer),a,Math.max(0,p-n.timer.now())),await LW(y,n.sdkWeb??OT(c,n.timer),a,Math.max(0,p-n.timer.now())),y}default:throw St(c.platform,n.actionLabel)}}async function FF(n){let{timer:s,signal:c}=n,a=n.startTime+n.durationMs,l=n.maxRequests??Number.POSITIVE_INFINITY,d=0,p=n.startTime;while(s.now()<a&&d<l){if(o(c),n.pacing==="sleepFirst")await s.sleep(n.intervalMs);else{let h=Math.min(p,a)-s.now();if(h>0)await s.sleep(h),o(c)}if(s.now()>=a)break;p=s.now()+n.intervalMs;let m=await n.fetch(Math.max(0,a-s.now()));if(d+=1,!m)continue;if(n.discardLateResult&&s.now()>=a)break;if(n.accept(m))break}return{requestCount:d}}
3
+ export{Kc,EF,rl,ou,IF,S5,uv,_F,SV,tp,xee,Gc,wV,Cee,So,xF,Dee,Pm,Oee,Mee,CF,cC,DF,OF,MF,NF,FF};
4
+
5
+ //# debugId=90253353EBD8CD8564756E2164756E21
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- import"./chunk-mf9spd4g.js";import{i}from"./chunk-z8t0v07j.js";import{ke,$r}from"./chunk-32jpxz2y.js";class r{resolveDatabase;timer;constructor({resolveDatabase:e=ke,timer:o=i}={}){this.resolveDatabase=e,this.timer=o}async list(e){let o=await this.resolveDatabase().selectFrom("session_tool_overrides").select(["tool_name","enabled"]).where("session_uuid","=",e).execute();return new Map(o.map((s)=>[s.tool_name,s.enabled!==0]))}async set(e,o,s){await this.resolveDatabase().insertInto("session_tool_overrides").values({session_uuid:e,tool_name:o,enabled:s?1:0}).onConflict((t)=>t.columns(["session_uuid","tool_name"]).doUpdateSet({enabled:s?1:0,updated_at:$r(this.timer)})).execute()}async setMany(e,o){if(o.length===0)return;let s=$r(this.timer);await this.resolveDatabase().transaction().execute(async(t)=>{for(let n of o)await t.insertInto("session_tool_overrides").values({session_uuid:e,tool_name:n.toolName,enabled:n.enabled?1:0}).onConflict((a)=>a.columns(["session_uuid","tool_name"]).doUpdateSet({enabled:n.enabled?1:0,updated_at:s})).execute()})}async deleteSession(e){await this.resolveDatabase().deleteFrom("session_tool_overrides").where("session_uuid","=",e).execute()}}export{r as SqliteSessionToolSelectionRepository};
2
+ import"./chunk-58wzr3mt.js";import{i}from"./chunk-z637nqg8.js";import{_e,Vr}from"./chunk-yf7ympyz.js";class r{resolveDatabase;timer;constructor({resolveDatabase:e=_e,timer:o=i}={}){this.resolveDatabase=e,this.timer=o}async list(e){let o=await this.resolveDatabase().selectFrom("session_tool_overrides").select(["tool_name","enabled"]).where("session_uuid","=",e).execute();return new Map(o.map((s)=>[s.tool_name,s.enabled!==0]))}async set(e,o,s){await this.resolveDatabase().insertInto("session_tool_overrides").values({session_uuid:e,tool_name:o,enabled:s?1:0}).onConflict((t)=>t.columns(["session_uuid","tool_name"]).doUpdateSet({enabled:s?1:0,updated_at:Vr(this.timer)})).execute()}async setMany(e,o){if(o.length===0)return;let s=Vr(this.timer);await this.resolveDatabase().transaction().execute(async(t)=>{for(let n of o)await t.insertInto("session_tool_overrides").values({session_uuid:e,tool_name:n.toolName,enabled:n.enabled?1:0}).onConflict((a)=>a.columns(["session_uuid","tool_name"]).doUpdateSet({enabled:n.enabled?1:0,updated_at:s})).execute()})}async deleteSession(e){await this.resolveDatabase().deleteFrom("session_tool_overrides").where("session_uuid","=",e).execute()}}export{r as SqliteSessionToolSelectionRepository};
3
3
 
4
- //# debugId=791F67D4D6BB860864756E2164756E21
4
+ //# debugId=C4C39C8FFA4B8F5164756E2164756E21