@mentra/engine 0.1.0-dev.0 → 3.1.0-dev.10

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 (503) hide show
  1. package/README.md +55 -18
  2. package/build/bluetooth-sdk/debug.d.ts +2 -0
  3. package/build/bluetooth-sdk/debug.d.ts.map +1 -0
  4. package/build/bluetooth-sdk/debug.js +2 -0
  5. package/build/bluetooth-sdk/debug.js.map +1 -0
  6. package/build/bluetooth-sdk/index.d.ts +3 -0
  7. package/build/bluetooth-sdk/index.d.ts.map +1 -0
  8. package/build/bluetooth-sdk/index.js +3 -0
  9. package/build/bluetooth-sdk/index.js.map +1 -0
  10. package/build/bluetooth-sdk/photo-receiver.d.ts +3 -0
  11. package/build/bluetooth-sdk/photo-receiver.d.ts.map +1 -0
  12. package/build/bluetooth-sdk/photo-receiver.js +3 -0
  13. package/build/bluetooth-sdk/photo-receiver.js.map +1 -0
  14. package/build/bluetooth-sdk/react.d.ts +2 -0
  15. package/build/bluetooth-sdk/react.d.ts.map +1 -0
  16. package/build/bluetooth-sdk/react.js +2 -0
  17. package/build/bluetooth-sdk/react.js.map +1 -0
  18. package/build/bluetooth-sdk/types.d.ts +2 -0
  19. package/build/bluetooth-sdk/types.d.ts.map +1 -0
  20. package/build/bluetooth-sdk/types.js +2 -0
  21. package/build/bluetooth-sdk/types.js.map +1 -0
  22. package/build/engine.d.ts +24 -16
  23. package/build/engine.d.ts.map +1 -1
  24. package/build/engine.js +9 -4
  25. package/build/engine.js.map +1 -1
  26. package/build/facades/dev.d.ts +3 -2
  27. package/build/facades/dev.d.ts.map +1 -1
  28. package/build/facades/dev.js +18 -12
  29. package/build/facades/dev.js.map +1 -1
  30. package/build/facades/display.d.ts +1 -1
  31. package/build/facades/display.d.ts.map +1 -1
  32. package/build/facades/display.js +1 -1
  33. package/build/facades/display.js.map +1 -1
  34. package/build/facades/gallery.js +1 -1
  35. package/build/facades/gallery.js.map +1 -1
  36. package/build/facades/glasses.d.ts +4 -13
  37. package/build/facades/glasses.d.ts.map +1 -1
  38. package/build/facades/glasses.js +21 -6
  39. package/build/facades/glasses.js.map +1 -1
  40. package/build/facades/glassesSettings.d.ts.map +1 -1
  41. package/build/facades/glassesSettings.js +1 -0
  42. package/build/facades/glassesSettings.js.map +1 -1
  43. package/build/facades/miniapps.d.ts +4 -0
  44. package/build/facades/miniapps.d.ts.map +1 -1
  45. package/build/facades/miniapps.js +5 -0
  46. package/build/facades/miniapps.js.map +1 -1
  47. package/build/facades/ota.d.ts +14 -16
  48. package/build/facades/ota.d.ts.map +1 -1
  49. package/build/facades/ota.js +22 -7
  50. package/build/facades/ota.js.map +1 -1
  51. package/build/facades/pairing.d.ts +8 -5
  52. package/build/facades/pairing.d.ts.map +1 -1
  53. package/build/facades/pairing.js +12 -10
  54. package/build/facades/pairing.js.map +1 -1
  55. package/build/facades/permissions.d.ts +10 -0
  56. package/build/facades/permissions.d.ts.map +1 -1
  57. package/build/facades/permissions.js +10 -0
  58. package/build/facades/permissions.js.map +1 -1
  59. package/build/facades/phoneNotifications.d.ts +0 -4
  60. package/build/facades/phoneNotifications.d.ts.map +1 -1
  61. package/build/facades/phoneNotifications.js +4 -14
  62. package/build/facades/phoneNotifications.js.map +1 -1
  63. package/build/facades/speech.d.ts +7 -6
  64. package/build/facades/speech.d.ts.map +1 -1
  65. package/build/facades/speech.js +1 -1
  66. package/build/facades/speech.js.map +1 -1
  67. package/build/generated/releaseMetadata.d.ts +12 -0
  68. package/build/generated/releaseMetadata.d.ts.map +1 -0
  69. package/build/generated/releaseMetadata.js +10 -0
  70. package/build/generated/releaseMetadata.js.map +1 -0
  71. package/build/index.d.ts +10 -8
  72. package/build/index.d.ts.map +1 -1
  73. package/build/index.js +6 -5
  74. package/build/index.js.map +1 -1
  75. package/build/react/MentraLiveOtaFlow.d.ts +38 -0
  76. package/build/react/MentraLiveOtaFlow.d.ts.map +1 -0
  77. package/build/react/MentraLiveOtaFlow.js +528 -0
  78. package/build/react/MentraLiveOtaFlow.js.map +1 -0
  79. package/build/react/index.d.ts +2 -0
  80. package/build/react/index.d.ts.map +1 -0
  81. package/build/react/index.js +2 -0
  82. package/build/react/index.js.map +1 -0
  83. package/build/react/useEngineSnapshot.d.ts +2 -0
  84. package/build/react/useEngineSnapshot.d.ts.map +1 -0
  85. package/build/react/useEngineSnapshot.js +14 -0
  86. package/build/react/useEngineSnapshot.js.map +1 -0
  87. package/build/runtime/bootstrap.d.ts +24 -1
  88. package/build/runtime/bootstrap.d.ts.map +1 -1
  89. package/build/runtime/bootstrap.js +17 -0
  90. package/build/runtime/bootstrap.js.map +1 -1
  91. package/build/runtime/scanQrSeam.d.ts +7 -0
  92. package/build/runtime/scanQrSeam.d.ts.map +1 -0
  93. package/build/runtime/scanQrSeam.js +16 -0
  94. package/build/runtime/scanQrSeam.js.map +1 -0
  95. package/build/services/AppRegistry.d.ts +41 -57
  96. package/build/services/AppRegistry.d.ts.map +1 -1
  97. package/build/services/AppRegistry.js +208 -144
  98. package/build/services/AppRegistry.js.map +1 -1
  99. package/build/services/AudioCloudUplink.d.ts +12 -0
  100. package/build/services/AudioCloudUplink.d.ts.map +1 -1
  101. package/build/services/AudioCloudUplink.js +22 -1
  102. package/build/services/AudioCloudUplink.js.map +1 -1
  103. package/build/services/AudioPlaybackService.d.ts +41 -2
  104. package/build/services/AudioPlaybackService.d.ts.map +1 -1
  105. package/build/services/AudioPlaybackService.js +250 -33
  106. package/build/services/AudioPlaybackService.js.map +1 -1
  107. package/build/services/BlobStore.d.ts +2 -1
  108. package/build/services/BlobStore.d.ts.map +1 -1
  109. package/build/services/BlobStore.js +17 -9
  110. package/build/services/BlobStore.js.map +1 -1
  111. package/build/services/CloudAudioSubscriptionSync.d.ts.map +1 -1
  112. package/build/services/CloudAudioSubscriptionSync.js +13 -1
  113. package/build/services/CloudAudioSubscriptionSync.js.map +1 -1
  114. package/build/services/CloudClientService.d.ts +15 -0
  115. package/build/services/CloudClientService.d.ts.map +1 -1
  116. package/build/services/CloudClientService.js +93 -1
  117. package/build/services/CloudClientService.js.map +1 -1
  118. package/build/services/DeviceEventRouter.js +5 -5
  119. package/build/services/DeviceEventRouter.js.map +1 -1
  120. package/build/services/ForegroundLocationPermission.d.ts +13 -0
  121. package/build/services/ForegroundLocationPermission.d.ts.map +1 -0
  122. package/build/services/ForegroundLocationPermission.js +12 -0
  123. package/build/services/ForegroundLocationPermission.js.map +1 -0
  124. package/build/services/GlassesSettingsSync.d.ts.map +1 -1
  125. package/build/services/GlassesSettingsSync.js +9 -3
  126. package/build/services/GlassesSettingsSync.js.map +1 -1
  127. package/build/services/GlassesStatusProjection.d.ts +2 -1
  128. package/build/services/GlassesStatusProjection.d.ts.map +1 -1
  129. package/build/services/GlassesStatusProjection.js +15 -8
  130. package/build/services/GlassesStatusProjection.js.map +1 -1
  131. package/build/services/HotspotOtaTransport.d.ts +29 -0
  132. package/build/services/HotspotOtaTransport.d.ts.map +1 -0
  133. package/build/services/HotspotOtaTransport.js +146 -0
  134. package/build/services/HotspotOtaTransport.js.map +1 -0
  135. package/build/services/HotspotShutdown.d.ts +17 -0
  136. package/build/services/HotspotShutdown.d.ts.map +1 -0
  137. package/build/services/HotspotShutdown.js +29 -0
  138. package/build/services/HotspotShutdown.js.map +1 -0
  139. package/build/services/LocalDisplayManager.d.ts +15 -0
  140. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  141. package/build/services/LocalDisplayManager.js +106 -20
  142. package/build/services/LocalDisplayManager.js.map +1 -1
  143. package/build/services/LocalMiniappRuntime.d.ts +74 -2
  144. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  145. package/build/services/LocalMiniappRuntime.js +785 -291
  146. package/build/services/LocalMiniappRuntime.js.map +1 -1
  147. package/build/services/LocalMiniappStorage.d.ts +32 -0
  148. package/build/services/LocalMiniappStorage.d.ts.map +1 -0
  149. package/build/services/LocalMiniappStorage.js +66 -0
  150. package/build/services/LocalMiniappStorage.js.map +1 -0
  151. package/build/services/LocalMiniappUserIdentity.d.ts +20 -0
  152. package/build/services/LocalMiniappUserIdentity.d.ts.map +1 -0
  153. package/build/services/LocalMiniappUserIdentity.js +43 -0
  154. package/build/services/LocalMiniappUserIdentity.js.map +1 -0
  155. package/build/services/LocalSttFallbackCoordinator.d.ts +9 -6
  156. package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
  157. package/build/services/LocalSttFallbackCoordinator.js +18 -12
  158. package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
  159. package/build/services/MicStateCoordinator.d.ts +42 -1
  160. package/build/services/MicStateCoordinator.d.ts.map +1 -1
  161. package/build/services/MicStateCoordinator.js +141 -3
  162. package/build/services/MicStateCoordinator.js.map +1 -1
  163. package/build/services/MiniappLauncher.d.ts.map +1 -1
  164. package/build/services/MiniappLauncher.js +23 -3
  165. package/build/services/MiniappLauncher.js.map +1 -1
  166. package/build/services/MiniappLiveness.d.ts +12 -0
  167. package/build/services/MiniappLiveness.d.ts.map +1 -0
  168. package/build/services/MiniappLiveness.js +16 -0
  169. package/build/services/MiniappLiveness.js.map +1 -0
  170. package/build/services/NativeHttpResponse.d.ts +3 -0
  171. package/build/services/NativeHttpResponse.d.ts.map +1 -0
  172. package/build/services/NativeHttpResponse.js +6 -0
  173. package/build/services/NativeHttpResponse.js.map +1 -0
  174. package/build/services/OtaArtifactDownloader.d.ts +65 -0
  175. package/build/services/OtaArtifactDownloader.d.ts.map +1 -0
  176. package/build/services/OtaArtifactDownloader.js +226 -0
  177. package/build/services/OtaArtifactDownloader.js.map +1 -0
  178. package/build/services/OtaAutoChain.d.ts +34 -0
  179. package/build/services/OtaAutoChain.d.ts.map +1 -0
  180. package/build/services/OtaAutoChain.js +83 -0
  181. package/build/services/OtaAutoChain.js.map +1 -0
  182. package/build/services/OtaErrorMapping.d.ts +18 -0
  183. package/build/services/OtaErrorMapping.d.ts.map +1 -0
  184. package/build/services/OtaErrorMapping.js +63 -0
  185. package/build/services/OtaErrorMapping.js.map +1 -0
  186. package/build/services/OtaInstallCoordinator.d.ts +95 -15
  187. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  188. package/build/services/OtaInstallCoordinator.js +577 -130
  189. package/build/services/OtaInstallCoordinator.js.map +1 -1
  190. package/build/services/OtaService.d.ts.map +1 -1
  191. package/build/services/OtaService.js +8 -1
  192. package/build/services/OtaService.js.map +1 -1
  193. package/build/services/OtaUpdateCheckService.d.ts +39 -4
  194. package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
  195. package/build/services/OtaUpdateCheckService.js +138 -22
  196. package/build/services/OtaUpdateCheckService.js.map +1 -1
  197. package/build/services/PhoneCalendarHelpers.d.ts +32 -0
  198. package/build/services/PhoneCalendarHelpers.d.ts.map +1 -0
  199. package/build/services/PhoneCalendarHelpers.js +157 -0
  200. package/build/services/PhoneCalendarHelpers.js.map +1 -0
  201. package/build/services/PhoneCalendarService.d.ts +5 -0
  202. package/build/services/PhoneCalendarService.d.ts.map +1 -0
  203. package/build/services/PhoneCalendarService.js +45 -0
  204. package/build/services/PhoneCalendarService.js.map +1 -0
  205. package/build/services/PhoneCameraFovCoordinator.d.ts +30 -0
  206. package/build/services/PhoneCameraFovCoordinator.d.ts.map +1 -0
  207. package/build/services/PhoneCameraFovCoordinator.js +194 -0
  208. package/build/services/PhoneCameraFovCoordinator.js.map +1 -0
  209. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  210. package/build/services/PhoneNotificationsSync.js +12 -10
  211. package/build/services/PhoneNotificationsSync.js.map +1 -1
  212. package/build/services/PhonePhotoCoordinator.d.ts +26 -2
  213. package/build/services/PhonePhotoCoordinator.d.ts.map +1 -1
  214. package/build/services/PhonePhotoCoordinator.js +79 -9
  215. package/build/services/PhonePhotoCoordinator.js.map +1 -1
  216. package/build/services/PhoneStreamCoordinator.d.ts +8 -1
  217. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  218. package/build/services/PhoneStreamCoordinator.js +125 -13
  219. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  220. package/build/services/PhoneVideoCoordinator.d.ts +2 -0
  221. package/build/services/PhoneVideoCoordinator.d.ts.map +1 -1
  222. package/build/services/PhoneVideoCoordinator.js +10 -0
  223. package/build/services/PhoneVideoCoordinator.js.map +1 -1
  224. package/build/services/SceneRenderer.d.ts.map +1 -1
  225. package/build/services/SceneRenderer.js +15 -0
  226. package/build/services/SceneRenderer.js.map +1 -1
  227. package/build/services/SentenceTtsPipeline.d.ts +31 -0
  228. package/build/services/SentenceTtsPipeline.d.ts.map +1 -0
  229. package/build/services/SentenceTtsPipeline.js +67 -0
  230. package/build/services/SentenceTtsPipeline.js.map +1 -0
  231. package/build/services/SupportProfileSync.d.ts +22 -0
  232. package/build/services/SupportProfileSync.d.ts.map +1 -0
  233. package/build/services/SupportProfileSync.js +213 -0
  234. package/build/services/SupportProfileSync.js.map +1 -0
  235. package/build/services/TranscriptionRouting.d.ts +12 -0
  236. package/build/services/TranscriptionRouting.d.ts.map +1 -0
  237. package/build/services/TranscriptionRouting.js +24 -0
  238. package/build/services/TranscriptionRouting.js.map +1 -0
  239. package/build/services/TtsTextSanitizer.d.ts +5 -0
  240. package/build/services/TtsTextSanitizer.d.ts.map +1 -0
  241. package/build/services/TtsTextSanitizer.js +61 -0
  242. package/build/services/TtsTextSanitizer.js.map +1 -0
  243. package/build/services/WifiAdbRequest.d.ts +17 -0
  244. package/build/services/WifiAdbRequest.d.ts.map +1 -0
  245. package/build/services/WifiAdbRequest.js +35 -0
  246. package/build/services/WifiAdbRequest.js.map +1 -0
  247. package/build/services/asg/asgCameraApi.d.ts +43 -0
  248. package/build/services/asg/asgCameraApi.d.ts.map +1 -1
  249. package/build/services/asg/asgCameraApi.js +509 -26
  250. package/build/services/asg/asgCameraApi.js.map +1 -1
  251. package/build/services/asg/cameraRollExportCoordinator.d.ts +60 -0
  252. package/build/services/asg/cameraRollExportCoordinator.d.ts.map +1 -0
  253. package/build/services/asg/cameraRollExportCoordinator.js +618 -0
  254. package/build/services/asg/cameraRollExportCoordinator.js.map +1 -0
  255. package/build/services/asg/cameraRollExportLedger.d.ts +44 -0
  256. package/build/services/asg/cameraRollExportLedger.d.ts.map +1 -0
  257. package/build/services/asg/cameraRollExportLedger.js +78 -0
  258. package/build/services/asg/cameraRollExportLedger.js.map +1 -0
  259. package/build/services/asg/galleryNotices.d.ts +1 -1
  260. package/build/services/asg/galleryNotices.d.ts.map +1 -1
  261. package/build/services/asg/galleryNotices.js.map +1 -1
  262. package/build/services/asg/galleryPathMigration.d.ts +3 -0
  263. package/build/services/asg/galleryPathMigration.d.ts.map +1 -0
  264. package/build/services/asg/galleryPathMigration.js +26 -0
  265. package/build/services/asg/galleryPathMigration.js.map +1 -0
  266. package/build/services/asg/gallerySettingsService.d.ts +1 -0
  267. package/build/services/asg/gallerySettingsService.d.ts.map +1 -1
  268. package/build/services/asg/gallerySettingsService.js +2 -0
  269. package/build/services/asg/gallerySettingsService.js.map +1 -1
  270. package/build/services/asg/gallerySyncService.d.ts.map +1 -1
  271. package/build/services/asg/gallerySyncService.js +162 -56
  272. package/build/services/asg/gallerySyncService.js.map +1 -1
  273. package/build/services/asg/galleryTransferLedger.d.ts +58 -0
  274. package/build/services/asg/galleryTransferLedger.d.ts.map +1 -0
  275. package/build/services/asg/galleryTransferLedger.js +225 -0
  276. package/build/services/asg/galleryTransferLedger.js.map +1 -0
  277. package/build/services/asg/localNetworkTransport.d.ts +15 -0
  278. package/build/services/asg/localNetworkTransport.d.ts.map +1 -0
  279. package/build/services/asg/localNetworkTransport.js +144 -0
  280. package/build/services/asg/localNetworkTransport.js.map +1 -0
  281. package/build/services/asg/localStorageService.d.ts +18 -0
  282. package/build/services/asg/localStorageService.d.ts.map +1 -1
  283. package/build/services/asg/localStorageService.js +168 -19
  284. package/build/services/asg/localStorageService.js.map +1 -1
  285. package/build/services/asg/mediaProcessingQueue.d.ts +10 -0
  286. package/build/services/asg/mediaProcessingQueue.d.ts.map +1 -1
  287. package/build/services/asg/mediaProcessingQueue.js +193 -62
  288. package/build/services/asg/mediaProcessingQueue.js.map +1 -1
  289. package/build/services/audioPlaybackAssets.d.ts +8 -0
  290. package/build/services/audioPlaybackAssets.d.ts.map +1 -0
  291. package/build/services/audioPlaybackAssets.js +7 -0
  292. package/build/services/audioPlaybackAssets.js.map +1 -0
  293. package/build/services/gallerySyncClock.js +1 -1
  294. package/build/services/gallerySyncClock.js.map +1 -1
  295. package/build/services/glassesClockSync.d.ts.map +1 -1
  296. package/build/services/glassesClockSync.js +6 -2
  297. package/build/services/glassesClockSync.js.map +1 -1
  298. package/build/services/manifestActions.d.ts +7 -0
  299. package/build/services/manifestActions.d.ts.map +1 -0
  300. package/build/services/manifestActions.js +25 -0
  301. package/build/services/manifestActions.js.map +1 -0
  302. package/build/services/otaDisplayState.d.ts +23 -12
  303. package/build/services/otaDisplayState.d.ts.map +1 -1
  304. package/build/services/otaDisplayState.js +36 -14
  305. package/build/services/otaDisplayState.js.map +1 -1
  306. package/build/services/otaInstallPolicy.d.ts +16 -4
  307. package/build/services/otaInstallPolicy.d.ts.map +1 -1
  308. package/build/services/otaInstallPolicy.js +24 -3
  309. package/build/services/otaInstallPolicy.js.map +1 -1
  310. package/build/services/otaLegacyMapping.d.ts +1 -1
  311. package/build/services/otaLegacyMapping.d.ts.map +1 -1
  312. package/build/services/otaLegacyMapping.js.map +1 -1
  313. package/build/services/otaManifestPolicy.d.ts +14 -0
  314. package/build/services/otaManifestPolicy.d.ts.map +1 -0
  315. package/build/services/otaManifestPolicy.js +19 -0
  316. package/build/services/otaManifestPolicy.js.map +1 -0
  317. package/build/services/otaManifestUrl.d.ts +3 -0
  318. package/build/services/otaManifestUrl.d.ts.map +1 -0
  319. package/build/services/otaManifestUrl.js +38 -0
  320. package/build/services/otaManifestUrl.js.map +1 -0
  321. package/build/services/slimStreamStatus.d.ts +11 -0
  322. package/build/services/slimStreamStatus.d.ts.map +1 -1
  323. package/build/services/slimStreamStatus.js +14 -4
  324. package/build/services/slimStreamStatus.js.map +1 -1
  325. package/build/stores/apps.d.ts.map +1 -1
  326. package/build/stores/apps.js +11 -0
  327. package/build/stores/apps.js.map +1 -1
  328. package/build/stores/bluetoothSettingKeys.d.ts.map +1 -1
  329. package/build/stores/bluetoothSettingKeys.js +2 -0
  330. package/build/stores/bluetoothSettingKeys.js.map +1 -1
  331. package/build/stores/gallerySync.d.ts +2 -0
  332. package/build/stores/gallerySync.d.ts.map +1 -1
  333. package/build/stores/gallerySync.js +2 -0
  334. package/build/stores/gallerySync.js.map +1 -1
  335. package/build/stores/glasses.d.ts.map +1 -1
  336. package/build/stores/glasses.js +3 -0
  337. package/build/stores/glasses.js.map +1 -1
  338. package/build/stores/settings.d.ts +17 -0
  339. package/build/stores/settings.d.ts.map +1 -1
  340. package/build/stores/settings.js +157 -17
  341. package/build/stores/settings.js.map +1 -1
  342. package/build/types/applet.d.ts +2 -0
  343. package/build/types/applet.d.ts.map +1 -1
  344. package/build/types/applet.js.map +1 -1
  345. package/build/types/asg.d.ts +7 -0
  346. package/build/types/asg.d.ts.map +1 -1
  347. package/build/types/asg.js.map +1 -1
  348. package/build/types/enums.d.ts +2 -25
  349. package/build/types/enums.d.ts.map +1 -1
  350. package/build/types/enums.js +2 -27
  351. package/build/types/enums.js.map +1 -1
  352. package/build/types/hardware.d.ts +4 -13
  353. package/build/types/hardware.d.ts.map +1 -1
  354. package/build/types/hardware.js +15 -3
  355. package/build/types/hardware.js.map +1 -1
  356. package/build/types/index.d.ts +2 -6
  357. package/build/types/index.d.ts.map +1 -1
  358. package/build/types/index.js +2 -6
  359. package/build/types/index.js.map +1 -1
  360. package/build/utils/configuredDevHost.d.ts +6 -0
  361. package/build/utils/configuredDevHost.d.ts.map +1 -0
  362. package/build/utils/configuredDevHost.js +19 -0
  363. package/build/utils/configuredDevHost.js.map +1 -0
  364. package/build/utils/devMiniappLaunch.d.ts +19 -8
  365. package/build/utils/devMiniappLaunch.d.ts.map +1 -1
  366. package/build/utils/devMiniappLaunch.js +144 -21
  367. package/build/utils/devMiniappLaunch.js.map +1 -1
  368. package/build/utils/diagnosticContext.d.ts.map +1 -1
  369. package/build/utils/diagnosticContext.js +15 -17
  370. package/build/utils/diagnosticContext.js.map +1 -1
  371. package/build/utils/hardware/hardware.d.ts.map +1 -1
  372. package/build/utils/hardware/hardware.js +7 -3
  373. package/build/utils/hardware/hardware.js.map +1 -1
  374. package/build/utils/miniappDiagnostics.d.ts +64 -0
  375. package/build/utils/miniappDiagnostics.d.ts.map +1 -0
  376. package/build/utils/miniappDiagnostics.js +173 -0
  377. package/build/utils/miniappDiagnostics.js.map +1 -0
  378. package/build/utils/permissions/MediaLibraryPermissions.d.ts +11 -0
  379. package/build/utils/permissions/MediaLibraryPermissions.d.ts.map +1 -1
  380. package/build/utils/permissions/MediaLibraryPermissions.js +27 -4
  381. package/build/utils/permissions/MediaLibraryPermissions.js.map +1 -1
  382. package/build/utils/timers/timers.d.ts.map +1 -1
  383. package/build/utils/timers/timers.js +55 -18
  384. package/build/utils/timers/timers.js.map +1 -1
  385. package/package.json +59 -31
  386. package/src/assets/silence.wav +0 -0
  387. package/src/bluetooth-sdk/debug.ts +1 -0
  388. package/src/bluetooth-sdk/index.ts +2 -0
  389. package/src/bluetooth-sdk/photo-receiver.ts +2 -0
  390. package/src/bluetooth-sdk/react.ts +1 -0
  391. package/src/bluetooth-sdk/types.ts +1 -0
  392. package/src/engine.ts +9 -4
  393. package/src/facades/dev.ts +17 -12
  394. package/src/facades/display.ts +1 -1
  395. package/src/facades/gallery.ts +1 -1
  396. package/src/facades/glasses.ts +20 -7
  397. package/src/facades/glassesSettings.ts +5 -0
  398. package/src/facades/miniapps.ts +7 -0
  399. package/src/facades/ota.ts +23 -8
  400. package/src/facades/pairing.ts +15 -11
  401. package/src/facades/permissions.ts +14 -0
  402. package/src/facades/phoneNotifications.ts +4 -13
  403. package/src/facades/speech.ts +21 -7
  404. package/src/generated/releaseMetadata.ts +20 -0
  405. package/src/index.ts +39 -13
  406. package/src/react/MentraLiveOtaFlow.tsx +856 -0
  407. package/src/react/index.ts +7 -0
  408. package/src/react/useEngineSnapshot.ts +16 -0
  409. package/src/runtime/bootstrap.ts +33 -1
  410. package/src/runtime/scanQrSeam.ts +20 -0
  411. package/src/services/AppRegistry.ts +240 -149
  412. package/src/services/AudioCloudUplink.ts +23 -1
  413. package/src/services/AudioPlaybackService.ts +287 -38
  414. package/src/services/BlobStore.ts +22 -9
  415. package/src/services/CloudAudioSubscriptionSync.ts +13 -1
  416. package/src/services/CloudClientService.ts +96 -1
  417. package/src/services/DeviceEventRouter.ts +5 -5
  418. package/src/services/ForegroundLocationPermission.ts +24 -0
  419. package/src/services/GlassesSettingsSync.ts +9 -3
  420. package/src/services/GlassesStatusProjection.ts +16 -8
  421. package/src/services/HotspotOtaTransport.ts +184 -0
  422. package/src/services/HotspotShutdown.ts +41 -0
  423. package/src/services/LocalDisplayManager.ts +112 -21
  424. package/src/services/LocalMiniappRuntime.ts +967 -346
  425. package/src/services/LocalMiniappStorage.ts +82 -0
  426. package/src/services/LocalMiniappUserIdentity.ts +46 -0
  427. package/src/services/LocalSttFallbackCoordinator.ts +19 -12
  428. package/src/services/MicStateCoordinator.ts +169 -4
  429. package/src/services/MiniappLauncher.ts +30 -3
  430. package/src/services/MiniappLiveness.ts +24 -0
  431. package/src/services/NativeHttpResponse.ts +6 -0
  432. package/src/services/OtaArtifactDownloader.ts +312 -0
  433. package/src/services/OtaAutoChain.ts +106 -0
  434. package/src/services/OtaErrorMapping.ts +80 -0
  435. package/src/services/OtaInstallCoordinator.ts +649 -144
  436. package/src/services/OtaService.ts +11 -5
  437. package/src/services/OtaUpdateCheckService.ts +181 -21
  438. package/src/services/PhoneCalendarHelpers.ts +212 -0
  439. package/src/services/PhoneCalendarService.ts +65 -0
  440. package/src/services/PhoneCameraFovCoordinator.ts +210 -0
  441. package/src/services/PhoneNotificationsSync.ts +12 -10
  442. package/src/services/PhonePhotoCoordinator.ts +116 -18
  443. package/src/services/PhoneStreamCoordinator.ts +139 -25
  444. package/src/services/PhoneVideoCoordinator.ts +11 -0
  445. package/src/services/SceneRenderer.ts +16 -0
  446. package/src/services/SentenceTtsPipeline.ts +106 -0
  447. package/src/services/SupportProfileSync.ts +218 -0
  448. package/src/services/TranscriptionRouting.ts +37 -0
  449. package/src/services/TtsTextSanitizer.ts +66 -0
  450. package/src/services/WifiAdbRequest.ts +53 -0
  451. package/src/services/asg/asgCameraApi.ts +615 -33
  452. package/src/services/asg/cameraRollExportCoordinator.ts +627 -0
  453. package/src/services/asg/cameraRollExportLedger.ts +122 -0
  454. package/src/services/asg/galleryNotices.ts +1 -0
  455. package/src/services/asg/galleryPathMigration.ts +22 -0
  456. package/src/services/asg/gallerySettingsService.ts +3 -0
  457. package/src/services/asg/gallerySyncService.ts +184 -73
  458. package/src/services/asg/galleryTransferLedger.ts +312 -0
  459. package/src/services/asg/localNetworkTransport.ts +169 -0
  460. package/src/services/asg/localStorageService.ts +179 -24
  461. package/src/services/asg/mediaProcessingQueue.ts +209 -62
  462. package/src/services/audioPlaybackAssets.ts +8 -0
  463. package/src/services/gallerySyncClock.ts +1 -1
  464. package/src/services/glassesClockSync.ts +6 -2
  465. package/src/services/manifestActions.ts +30 -0
  466. package/src/services/otaDisplayState.ts +58 -17
  467. package/src/services/otaInstallPolicy.ts +30 -4
  468. package/src/services/otaLegacyMapping.ts +1 -1
  469. package/src/services/otaManifestPolicy.ts +42 -0
  470. package/src/services/otaManifestUrl.ts +44 -0
  471. package/src/services/slimStreamStatus.ts +16 -5
  472. package/src/stores/apps.ts +12 -0
  473. package/src/stores/bluetoothSettingKeys.ts +2 -0
  474. package/src/stores/gallerySync.ts +4 -0
  475. package/src/stores/glasses.ts +3 -0
  476. package/src/stores/settings.ts +179 -18
  477. package/src/types/applet.ts +2 -0
  478. package/src/types/asg.ts +7 -0
  479. package/src/types/enums.ts +3 -28
  480. package/src/types/hardware.ts +20 -13
  481. package/src/types/index.ts +2 -6
  482. package/src/utils/configuredDevHost.ts +17 -0
  483. package/src/utils/devMiniappLaunch.ts +151 -23
  484. package/src/utils/diagnosticContext.ts +17 -17
  485. package/src/utils/hardware/hardware.ts +7 -9
  486. package/src/utils/miniappDiagnostics.ts +226 -0
  487. package/src/utils/permissions/MediaLibraryPermissions.ts +35 -4
  488. package/src/utils/timers/timers.ts +57 -21
  489. package/build/devtools.d.ts +0 -11
  490. package/build/devtools.d.ts.map +0 -1
  491. package/build/devtools.js +0 -13
  492. package/build/devtools.js.map +0 -1
  493. package/build/internal.d.ts +0 -66
  494. package/build/internal.d.ts.map +0 -1
  495. package/build/internal.js +0 -89
  496. package/build/internal.js.map +0 -1
  497. package/build/services/asgOtaVersionUrl.d.ts +0 -2
  498. package/build/services/asgOtaVersionUrl.d.ts.map +0 -1
  499. package/build/services/asgOtaVersionUrl.js +0 -36
  500. package/build/services/asgOtaVersionUrl.js.map +0 -1
  501. package/src/devtools.ts +0 -13
  502. package/src/internal.ts +0 -158
  503. package/src/services/asgOtaVersionUrl.ts +0 -41
@@ -42,6 +42,9 @@ const DEFAULT_TIMINGS = {
42
42
  ackTimeoutMs: 10_000,
43
43
  maxMissedAcks: 3,
44
44
  cloudflareStatusPollMs: 5_000,
45
+ // During WHIP startup, probe quickly so readiness is not quantized to the
46
+ // steady-state 5s monitoring cadence. The delay backs off on each miss.
47
+ cloudflareStartupPollInitialMs: 500,
45
48
  // Cloudflare typically needs ~5-10s after first frame before HLS is live.
46
49
  hlsReadinessInitialDelayMs: 5_000,
47
50
  hlsReadinessPollMs: 2_000,
@@ -140,6 +143,26 @@ export class PhoneStreamCoordinator {
140
143
  owns(streamId) {
141
144
  return this.current !== null && this.current.streamId === streamId;
142
145
  }
146
+ /** Report-safe stream ownership snapshot for incident diagnostics. */
147
+ getDiagnosticSnapshot() {
148
+ if (!this.current)
149
+ return { active: false };
150
+ return this.current.kind === "managed"
151
+ ? {
152
+ active: true,
153
+ kind: this.current.kind,
154
+ streamId: this.current.streamId,
155
+ subscribers: [...this.current.subscribers].sort(),
156
+ mode: this.current.mode,
157
+ playbackReady: this.current.hlsReady,
158
+ }
159
+ : {
160
+ active: true,
161
+ kind: this.current.kind,
162
+ streamId: this.current.streamId,
163
+ ownerPackageName: this.current.packageName,
164
+ };
165
+ }
143
166
  async startUnmanaged(packageName, opts) {
144
167
  // Pre-check the obvious-bad input before queueing — the lock is for
145
168
  // serializing state transitions, not for validating arguments.
@@ -171,6 +194,7 @@ export class PhoneStreamCoordinator {
171
194
  // undefined, expected an Object") — only include what was provided.
172
195
  ...(opts.video !== undefined ? { video: opts.video } : {}),
173
196
  ...(opts.audio !== undefined ? { audio: opts.audio } : {}),
197
+ ...(opts.authToken ? { authToken: opts.authToken } : {}),
174
198
  });
175
199
  const result = publisherStartResult(streamId, event);
176
200
  this.startLifecycle(streamId);
@@ -183,6 +207,15 @@ export class PhoneStreamCoordinator {
183
207
  });
184
208
  }
185
209
  async startManaged(packageName, opts) {
210
+ const startupStartedAtMs = Date.now();
211
+ // streamId doesn't exist yet — it's minted a few lines below, once we
212
+ // know this is a fresh provision rather than a join onto an existing one.
213
+ console.info("[STREAM_STARTUP]", {
214
+ stage: "requested",
215
+ packageName,
216
+ ingest: opts.ingest,
217
+ elapsedMs: 0,
218
+ });
186
219
  // Two-phase: the entry-claim runs under the transition lock; the wait for
187
220
  // HLS readiness happens AFTER the lock releases so a long warm-up doesn't
188
221
  // block subsequent start/stop transitions on this coordinator.
@@ -227,9 +260,16 @@ export class PhoneStreamCoordinator {
227
260
  hlsReadyResolvers: [],
228
261
  hlsReadyRejecters: [],
229
262
  hlsAttempts: 0,
230
- connectAttempts: 0,
263
+ cloudflareAttempts: 0,
264
+ startupStartedAtMs,
231
265
  };
232
266
  this.current = entry;
267
+ console.info("[STREAM_STARTUP]", {
268
+ streamId,
269
+ stage: "provisioned",
270
+ mode,
271
+ elapsedMs: Date.now() - startupStartedAtMs,
272
+ });
233
273
  try {
234
274
  const event = await BluetoothSdk.startExternallyManagedStream({
235
275
  type: "start_stream",
@@ -241,6 +281,12 @@ export class PhoneStreamCoordinator {
241
281
  ...(opts.audio !== undefined ? { audio: opts.audio } : {}),
242
282
  });
243
283
  entry.publisherStart = publisherStartResult(streamId, event);
284
+ console.info("[STREAM_STARTUP]", {
285
+ streamId,
286
+ stage: "publisher_ready",
287
+ mode,
288
+ elapsedMs: Date.now() - startupStartedAtMs,
289
+ });
244
290
  }
245
291
  catch (err) {
246
292
  this.current = null;
@@ -260,6 +306,12 @@ export class PhoneStreamCoordinator {
260
306
  if (decision.kind === "join" && decision.immediate) {
261
307
  return decision.immediate;
262
308
  }
309
+ // The first Cloudflare probe runs immediately and can complete before the
310
+ // transition lock releases. Avoid stranding this caller after readiness
311
+ // resolvers have already been drained.
312
+ if (decision.entry.hlsReady) {
313
+ return managedStartResult(decision.entry);
314
+ }
263
315
  // Wait for playback readiness OUTSIDE the lock — readiness can take ~10s
264
316
  // and we don't want to block other transitions for that long.
265
317
  return new Promise((resolve, reject) => {
@@ -385,16 +437,38 @@ export class PhoneStreamCoordinator {
385
437
  this.lifecycle = ctrl;
386
438
  }
387
439
  startCloudflareStatusPoll(entry) {
388
- // webrtc mode: how many polls to wait for the first "connected" before
389
- // declaring the publisher never reached Cloudflare. Mirrors the HLS
390
- // readiness budget (~60s at the default 5s cadence).
391
- const maxConnectAttempts = Math.max(1, Math.ceil((this.timings.hlsReadinessMaxAttempts * this.timings.hlsReadinessPollMs) /
392
- this.timings.cloudflareStatusPollMs));
440
+ // Keep the existing ~60s readiness budget while decoupling it from probe
441
+ // count. Startup probes begin immediately and back off to the normal 5s
442
+ // monitoring cadence, so a just-connected publisher is noticed quickly
443
+ // without increasing steady-state traffic.
444
+ const connectTimeoutMs = Math.max(1, this.timings.hlsReadinessMaxAttempts * this.timings.hlsReadinessPollMs);
445
+ const pollingStartedAtMs = Date.now();
446
+ const scheduleNext = () => {
447
+ if (this.current !== entry)
448
+ return;
449
+ const waitingForWebRtc = entry.mode === "webrtc" && !entry.hlsReady;
450
+ const elapsedMs = Date.now() - pollingStartedAtMs;
451
+ const remainingMs = Math.max(0, connectTimeoutMs - elapsedMs);
452
+ const startupDelayMs = Math.min(this.timings.cloudflareStatusPollMs, this.timings.cloudflareStartupPollInitialMs * 2 ** Math.min(Math.max(0, entry.cloudflareAttempts - 1), 10));
453
+ const delayMs = waitingForWebRtc ? Math.min(startupDelayMs, remainingMs) : this.timings.cloudflareStatusPollMs;
454
+ entry.cloudflareTimer = setTimeout(() => void poll(), delayMs);
455
+ };
393
456
  const poll = async () => {
394
457
  if (this.current !== entry)
395
458
  return;
459
+ const requestStartedAtMs = Date.now();
460
+ let keepPolling = true;
461
+ entry.cloudflareAttempts += 1;
396
462
  try {
397
463
  const status = await getManagedStreamStatus(entry.liveInputId);
464
+ console.debug("[STREAM_STARTUP]", {
465
+ streamId: entry.streamId,
466
+ stage: "cloudflare_probe",
467
+ connected: status.isConnected,
468
+ attempt: entry.cloudflareAttempts,
469
+ requestMs: Date.now() - requestStartedAtMs,
470
+ elapsedMs: Date.now() - entry.startupStartedAtMs,
471
+ });
398
472
  this.fanout({
399
473
  streamId: entry.streamId,
400
474
  source: "cloudflare",
@@ -407,6 +481,13 @@ export class PhoneStreamCoordinator {
407
481
  if (entry.mode === "webrtc" && !entry.hlsReady) {
408
482
  if (status.isConnected) {
409
483
  entry.hlsReady = true;
484
+ console.info("[STREAM_STARTUP]", {
485
+ streamId: entry.streamId,
486
+ stage: "playback_ready",
487
+ mode: entry.mode,
488
+ probes: entry.cloudflareAttempts,
489
+ elapsedMs: Date.now() - entry.startupStartedAtMs,
490
+ });
410
491
  const result = managedStartResult(entry);
411
492
  for (const r of entry.hlsReadyResolvers)
412
493
  r(result);
@@ -420,9 +501,8 @@ export class PhoneStreamCoordinator {
420
501
  });
421
502
  }
422
503
  else {
423
- entry.connectAttempts += 1;
424
- if (entry.connectAttempts >= maxConnectAttempts) {
425
- const err = new Error(`WebRTC ingest never reached Cloudflare after ${entry.connectAttempts} status polls`);
504
+ if (Date.now() - pollingStartedAtMs >= connectTimeoutMs) {
505
+ const err = new Error(`WebRTC ingest never reached Cloudflare after ${connectTimeoutMs}ms`);
426
506
  for (const reject of entry.hlsReadyRejecters)
427
507
  reject(err);
428
508
  entry.hlsReadyResolvers = [];
@@ -439,16 +519,36 @@ export class PhoneStreamCoordinator {
439
519
  return;
440
520
  await this.teardownLocked("webrtc_not_connected");
441
521
  });
522
+ keepPolling = false;
442
523
  }
443
524
  }
444
525
  }
445
526
  }
446
527
  catch (err) {
447
528
  console.warn("[STREAM] cloudflare status poll failed:", err);
529
+ if (entry.mode === "webrtc" && !entry.hlsReady && Date.now() - pollingStartedAtMs >= connectTimeoutMs) {
530
+ const timeoutErr = new Error(`WebRTC ingest status could not be confirmed after ${connectTimeoutMs}ms`);
531
+ for (const reject of entry.hlsReadyRejecters)
532
+ reject(timeoutErr);
533
+ entry.hlsReadyResolvers = [];
534
+ entry.hlsReadyRejecters = [];
535
+ const targetStreamId = entry.streamId;
536
+ void this.runExclusive(async () => {
537
+ if (this.current?.streamId !== targetStreamId)
538
+ return;
539
+ await this.teardownLocked("webrtc_status_unavailable");
540
+ });
541
+ keepPolling = false;
542
+ }
543
+ }
544
+ finally {
545
+ if (keepPolling)
546
+ scheduleNext();
448
547
  }
449
548
  };
450
- entry.cloudflareTimer = setInterval(poll, this.timings.cloudflareStatusPollMs);
451
- // Don't fire immediately Cloudflare wouldn't have first-frame yet.
549
+ // The glasses publisher has already reported streaming, so the first
550
+ // status request is useful now. Most starts avoid the old blind 5s wait.
551
+ void poll();
452
552
  }
453
553
  startHlsReadinessPoll(entry) {
454
554
  // Skip the first few seconds — Cloudflare doesn't have first-frame yet,
@@ -464,6 +564,13 @@ export class PhoneStreamCoordinator {
464
564
  const res = await fetch(entry.hlsUrl, { method: "HEAD" });
465
565
  if (res.status === 200) {
466
566
  entry.hlsReady = true;
567
+ console.info("[STREAM_STARTUP]", {
568
+ streamId: entry.streamId,
569
+ stage: "playback_ready",
570
+ mode: entry.mode,
571
+ probes: entry.hlsAttempts,
572
+ elapsedMs: Date.now() - entry.startupStartedAtMs,
573
+ });
467
574
  if (entry.hlsTimer) {
468
575
  clearInterval(entry.hlsTimer);
469
576
  entry.hlsTimer = undefined;
@@ -552,7 +659,7 @@ export class PhoneStreamCoordinator {
552
659
  this.lifecycle = null;
553
660
  if (entry.kind === "managed") {
554
661
  if (entry.cloudflareTimer)
555
- clearInterval(entry.cloudflareTimer);
662
+ clearTimeout(entry.cloudflareTimer);
556
663
  if (entry.hlsTimer)
557
664
  clearInterval(entry.hlsTimer);
558
665
  // Reject any still-pending HLS readiness waiters.
@@ -597,12 +704,17 @@ function pickIngestUrl(p, preference) {
597
704
  //
598
705
  // "whip" preference flips the trade: sub-second WHEP playback for
599
706
  // live-monitor use cases, accepting no HLS and no recording.
707
+ // "rtmp" prefers TCP 443 ingest so networks that drop WHIP/SRT UDP still
708
+ // get HLS playback. Callers must request it explicitly; WHIP starts do not
709
+ // fall back to RTMP.
600
710
  //
601
711
  // Throw if none resolved so the caller's Promise rejects with a clear
602
712
  // message rather than the glasses' "unknown protocol" error.
603
713
  const url = preference === "whip"
604
714
  ? p.webrtcPublishUrl || p.srtUrl || p.rtmpUrl
605
- : p.srtUrl || p.rtmpUrl || p.webrtcPublishUrl;
715
+ : preference === "rtmp"
716
+ ? p.rtmpUrl || p.srtUrl || p.webrtcPublishUrl
717
+ : p.srtUrl || p.rtmpUrl || p.webrtcPublishUrl;
606
718
  if (!url) {
607
719
  throw new Error("Cloudflare provision returned no usable ingest URL");
608
720
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneStreamCoordinator.js","sourceRoot":"","sources":["../../src/services/PhoneStreamCoordinator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * PhoneStreamCoordinator — owns all local-miniapp streaming on the phone.\n *\n * Architecture:\n * miniapp → SDK → LocalMiniappRuntime → coordinator\n * coordinator → BluetoothSdk (BLE → glasses publisher)\n * coordinator ↔ cloudStreamApi (managed only, cloud-v2 runtime provisioning)\n * coordinator ↔ StreamLifecycleController (keep-alive heartbeat)\n * coordinator → status listeners → routed back to miniapp(s)\n *\n * Single-stream constraint:\n * At most ONE stream is active across all miniapps. The exception is that\n * multiple miniapps can subscribe to a single managed stream that's already\n * running — Cloudflare muxes one ingest into many viewers, so subscribers\n * share the same playback URLs. Refcounted; teardown happens when the last\n * subscriber releases.\n *\n * Status routing:\n * Glasses publisher status arrives over BLE as `stream_status` events with\n * a `streamId`. The coordinator's `owns(streamId)` lookup lets MantleManager\n * route phone-owned status events here (not to cloud). For managed streams,\n * we additionally poll Cloudflare's live-input status every 5s to surface\n * what the OTHER end of the pipe sees.\n *\n * Important: this is the ONLY place that mints `streamId`s for phone-owned\n * streams. We use a `phone-` prefix so they're trivially distinguishable from\n * cloud-minted IDs in logs and from cloud-SDK app streams that flow through\n * the legacy path.\n */\n\nimport BluetoothSdk from \"@mentra/bluetooth-sdk/internal\"\nimport type {\n KeepAliveAckEvent,\n StreamResolvedConfig,\n StreamStartRequest,\n StreamStatusEvent,\n} from \"@mentra/bluetooth-sdk/internal\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {useGlassesStore} from \"../stores/glasses\"\n\nimport {StreamLifecycleController, type LifecycleLogger} from \"./StreamLifecycleController\"\nimport {slimStreamStatusEvent, streamStatusSignature} from \"./slimStreamStatus\"\nimport {\n getManagedStreamStatus,\n provisionManagedStream,\n teardownManagedStream,\n type CloudflareStatus,\n type ProvisionResult,\n type RestreamDestinationInput,\n} from \"./cloudStreamApi\"\n\n/**\n * Default cadence + thresholds. Exposed via {@link CoordinatorTimings} so tests\n * can shorten them; production code should never override these.\n */\nconst DEFAULT_TIMINGS = {\n keepAliveIntervalMs: 15_000,\n ackTimeoutMs: 10_000,\n maxMissedAcks: 3,\n cloudflareStatusPollMs: 5_000,\n // Cloudflare typically needs ~5-10s after first frame before HLS is live.\n hlsReadinessInitialDelayMs: 5_000,\n hlsReadinessPollMs: 2_000,\n hlsReadinessMaxAttempts: 30,\n} as const\n\ntype TimingConfig = {[K in keyof typeof DEFAULT_TIMINGS]: number}\nexport type CoordinatorTimings = Partial<TimingConfig>\n\n// Console-backed minimal logger; replaces pino on the phone.\nconst consoleLogger: LifecycleLogger = {\n child: (bindings) => ({\n ...consoleLogger,\n debug: (...args) => console.debug(\"[STREAM]\", bindings, ...args),\n warn: (...args) => console.warn(\"[STREAM]\", bindings, ...args),\n error: (...args) => console.error(\"[STREAM]\", bindings, ...args),\n }),\n debug: (...args) => console.debug(\"[STREAM]\", ...args),\n warn: (...args) => console.warn(\"[STREAM]\", ...args),\n error: (...args) => console.error(\"[STREAM]\", ...args),\n}\n\nexport interface StartUnmanagedOptions {\n streamUrl: string\n video?: StreamStartRequest[\"video\"]\n audio?: StreamStartRequest[\"audio\"]\n sound?: boolean\n}\n\nexport interface StartManagedOptions {\n restreamDestinations?: RestreamDestinationInput[]\n video?: StreamStartRequest[\"video\"]\n audio?: StreamStartRequest[\"audio\"]\n sound?: boolean\n /**\n * Ingest protocol preference — a real latency/durability trade on Cloudflare:\n * - \"srt\" (default): SRT ingest -> LL-HLS playback (~10-20s glass-to-screen),\n * with shareable HLS/DASH URLs and automatic recording.\n * - \"whip\": WebRTC ingest -> WHEP playback (<1s glass-to-screen), but NO\n * HLS/DASH playback and NO recording (Cloudflare limitation; the returned\n * hlsUrl will serve 204 forever).\n */\n ingest?: \"srt\" | \"whip\"\n}\n\nexport interface StreamPublisherStartResult {\n streamId: string\n status: string\n resolvedConfig?: StreamResolvedConfig\n}\n\nexport interface ManagedStartResult extends StreamPublisherStartResult {\n liveInputId: string\n /** Playback mode this stream supports: \"hls\" (SRT/RTMP ingest — use hlsUrl/\n * dashUrl, recording on) or \"webrtc\" (WHIP ingest — use webrtcUrl/WHEP,\n * sub-second, no HLS, no recording). */\n mode: \"hls\" | \"webrtc\"\n hlsUrl: string\n dashUrl: string\n webrtcUrl?: string\n}\n\nexport type StreamStatusUpdate = {\n streamId: string\n source: \"glasses\" | \"cloudflare\" | \"coordinator\"\n status: string\n data?: Record<string, unknown>\n}\n\nexport type StatusSubscriber = (packageName: string, update: StreamStatusUpdate) => void\n\ninterface UnmanagedEntry {\n kind: \"unmanaged\"\n streamId: string\n packageName: string\n streamUrl: string\n}\n\ninterface ManagedEntry {\n kind: \"managed\"\n streamId: string\n liveInputId: string\n ingestUrl: string\n /** Playback mode the chosen ingest supports: SRT/RTMP feed HLS; WHIP feeds\n * only WHEP. Readiness is gated differently per mode. */\n mode: \"hls\" | \"webrtc\"\n hlsUrl: string\n dashUrl: string\n webrtcUrl?: string\n publisherStart?: StreamPublisherStartResult\n subscribers: Set<string>\n hlsReady: boolean\n hlsReadyResolvers: Array<(result: ManagedStartResult) => void>\n hlsReadyRejecters: Array<(err: Error) => void>\n cloudflareTimer?: ReturnType<typeof setInterval>\n hlsTimer?: ReturnType<typeof setInterval>\n hlsAttempts: number\n /** webrtc mode: CF status polls seen while waiting for first \"connected\". */\n connectAttempts: number\n}\n\ntype Entry = UnmanagedEntry | ManagedEntry\n\nexport class StreamConflictError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n /** Pipeline stage that failed (provision | command | publish | playback). */\n public readonly stage?: string,\n /** Transport in play at the failure (cloud-rest | ble | wifi). */\n public readonly transport?: string,\n ) {\n super(message)\n this.name = \"StreamConflictError\"\n }\n}\n\n/**\n * Fail fast if glasses aren't connected — BEFORE provisioning. Without this a\n * managed start would create a provider live input, fail the BLE command, and\n * tear the input down again: a slow, billable no-op with a confusing error.\n */\nfunction assertGlassesConnected(): void {\n if (!isGlassesConnected(useGlassesStore.getState().connection)) {\n throw new StreamConflictError(\n \"GLASSES_NOT_CONNECTED\",\n \"Glasses are not connected\",\n \"command\",\n \"ble\",\n )\n }\n}\n\nexport class PhoneStreamCoordinator {\n private current: Entry | null = null\n private lifecycle: StreamLifecycleController | null = null\n private statusSubscriber: StatusSubscriber | null = null\n private idCounter = 0\n private readonly timings: TimingConfig\n /**\n * Serializes state transitions (start, stop, teardown). Without it, a\n * second `start*` racing with the first can pass the `this.current === null`\n * pre-check while the first is still awaiting its provision/BLE work, and\n * end up provisioning two separate streams. A teardown racing with a start\n * can clear `current` mid-stop and let the start fire BLE writes that\n * collide with the in-flight stopStream.\n */\n private inFlight: Promise<void> = Promise.resolve()\n /** Drop identical stream_status fanouts within one session. */\n private lastFanoutSignature: string | null = null\n /** Send full resolvedConfig only once per stream session. */\n private resolvedConfigForwarded = false\n\n constructor(timings: CoordinatorTimings = {}) {\n this.timings = {...DEFAULT_TIMINGS, ...timings}\n }\n\n /**\n * Run `work` under the transition lock. Each call awaits the previous\n * transition before re-evaluating preconditions, so e.g. two concurrent\n * `startManaged` calls are observed sequentially.\n */\n private async runExclusive<T>(work: () => Promise<T>): Promise<T> {\n const prev = this.inFlight\n let release!: () => void\n this.inFlight = new Promise<void>((r) => (release = r))\n try {\n await prev\n return await work()\n } finally {\n release()\n }\n }\n\n /**\n * Register the function that routes status updates to miniapps.\n * LocalMiniappRuntime wires this so updates become EVENT envelopes on the\n * `stream_status` stream for every subscribing miniapp.\n */\n setStatusSubscriber(cb: StatusSubscriber): void {\n this.statusSubscriber = cb\n }\n\n /**\n * True when a phone-owned stream currently uses this streamId. For managed\n * streams, all subscribers share the same streamId so a single equality\n * check covers the multi-subscriber case.\n */\n owns(streamId: string): boolean {\n return this.current !== null && this.current.streamId === streamId\n }\n\n async startUnmanaged(\n packageName: string,\n opts: StartUnmanagedOptions,\n ): Promise<StreamPublisherStartResult> {\n // Pre-check the obvious-bad input before queueing — the lock is for\n // serializing state transitions, not for validating arguments.\n if (!opts.streamUrl || typeof opts.streamUrl !== \"string\") {\n throw new StreamConflictError(\"STREAM_URL_REQUIRED\", \"streamUrl is required\")\n }\n assertGlassesConnected()\n return this.runExclusive(async () => {\n if (this.current) {\n throw new StreamConflictError(\n \"STREAM_ALREADY_ACTIVE\",\n `A ${this.current.kind} stream is already active. Stop it before starting a new one.`,\n )\n }\n\n const streamId = this.mintId(\"u\")\n const entry: UnmanagedEntry = {\n kind: \"unmanaged\",\n streamId,\n packageName,\n streamUrl: opts.streamUrl,\n }\n // Claim the slot BEFORE the BLE call so a concurrent caller waiting on\n // the lock sees the in-progress entry and rejects with the conflict.\n this.current = entry\n\n try {\n const event = await BluetoothSdk.startExternallyManagedStream({\n type: \"start_stream\",\n streamUrl: opts.streamUrl,\n streamId,\n sound: opts.sound ?? true,\n // The native bridge rejects explicit `undefined` values (\"Value is\n // undefined, expected an Object\") — only include what was provided.\n ...(opts.video !== undefined ? {video: opts.video} : {}),\n ...(opts.audio !== undefined ? {audio: opts.audio} : {}),\n })\n const result = publisherStartResult(streamId, event)\n this.startLifecycle(streamId)\n return result\n } catch (err) {\n this.current = null\n throw err\n }\n })\n }\n\n async startManaged(\n packageName: string,\n opts: StartManagedOptions,\n ): Promise<ManagedStartResult> {\n // Two-phase: the entry-claim runs under the transition lock; the wait for\n // HLS readiness happens AFTER the lock releases so a long warm-up doesn't\n // block subsequent start/stop transitions on this coordinator.\n assertGlassesConnected()\n type JoinDecision =\n | {kind: \"join\"; entry: ManagedEntry; immediate: ManagedStartResult | null}\n | {kind: \"fresh\"; entry: ManagedEntry}\n\n const decision = await this.runExclusive(async (): Promise<JoinDecision> => {\n if (this.current && this.current.kind === \"unmanaged\") {\n throw new StreamConflictError(\n \"STREAM_ALREADY_ACTIVE\",\n \"An unmanaged stream is already active. Stop it before starting a managed stream.\",\n )\n }\n\n // Join an existing managed stream if one is already running.\n if (this.current && this.current.kind === \"managed\") {\n const existing = this.current\n // Restream destinations are immutable after provision — a second\n // caller trying to dictate destinations on an already-live stream\n // is a likely bug or a feature we don't yet support.\n if (opts.restreamDestinations && opts.restreamDestinations.length > 0) {\n throw new StreamConflictError(\n \"STREAM_DESTINATIONS_LOCKED\",\n \"Restream destinations cannot be modified on an already-running managed stream.\",\n )\n }\n existing.subscribers.add(packageName)\n const immediate: ManagedStartResult | null = existing.hlsReady\n ? managedStartResult(existing)\n : null\n return {kind: \"join\", entry: existing, immediate}\n }\n\n // Fresh provision — claim slot BEFORE awaiting Cloudflare so a\n // concurrent caller queued behind us sees a managed stream in flight\n // and joins instead of double-provisioning.\n const provision = await provisionManagedStream(opts.restreamDestinations)\n const streamId = this.mintId(\"m\")\n const ingestUrl = pickIngestUrl(provision, opts.ingest)\n const mode: ManagedEntry[\"mode\"] =\n ingestUrl === provision.webrtcPublishUrl ? \"webrtc\" : \"hls\"\n\n const entry: ManagedEntry = {\n kind: \"managed\",\n streamId,\n liveInputId: provision.liveInputId,\n ingestUrl,\n mode,\n hlsUrl: provision.hlsUrl,\n dashUrl: provision.dashUrl,\n webrtcUrl: provision.webrtcUrl,\n subscribers: new Set([packageName]),\n hlsReady: false,\n hlsReadyResolvers: [],\n hlsReadyRejecters: [],\n hlsAttempts: 0,\n connectAttempts: 0,\n }\n this.current = entry\n\n try {\n const event = await BluetoothSdk.startExternallyManagedStream({\n type: \"start_stream\",\n streamUrl: ingestUrl,\n streamId,\n sound: opts.sound ?? true,\n // See startUnmanaged: the native bridge rejects explicit `undefined`.\n ...(opts.video !== undefined ? {video: opts.video} : {}),\n ...(opts.audio !== undefined ? {audio: opts.audio} : {}),\n })\n entry.publisherStart = publisherStartResult(streamId, event)\n } catch (err) {\n this.current = null\n await teardownManagedStream(provision.liveInputId).catch(() => undefined)\n throw err\n }\n\n this.startLifecycle(streamId)\n this.startCloudflareStatusPoll(entry)\n // hls mode: readiness = a real HLS manifest exists. webrtc mode: HLS\n // never materializes (Cloudflare WHIP limitation) — readiness resolves\n // off the status poll's first \"connected\" instead.\n if (entry.mode === \"hls\") {\n this.startHlsReadinessPoll(entry)\n }\n return {kind: \"fresh\", entry}\n })\n\n if (decision.kind === \"join\" && decision.immediate) {\n return decision.immediate\n }\n\n // Wait for playback readiness OUTSIDE the lock — readiness can take ~10s\n // and we don't want to block other transitions for that long.\n return new Promise<ManagedStartResult>((resolve, reject) => {\n decision.entry.hlsReadyResolvers.push(resolve)\n decision.entry.hlsReadyRejecters.push(reject)\n })\n }\n\n async stop(packageName: string, streamId?: string): Promise<void> {\n await this.runExclusive(async () => {\n if (!this.current) return\n\n // If a streamId was passed but doesn't match, ignore — silent no-op\n // matches the cloud's tolerant behavior.\n if (streamId && this.current.streamId !== streamId) return\n\n if (this.current.kind === \"managed\") {\n const entry = this.current\n entry.subscribers.delete(packageName)\n if (entry.subscribers.size > 0) {\n // Other miniapps still subscribed; keep the stream alive.\n return\n }\n } else if (this.current.packageName !== packageName) {\n // Unmanaged stream: only the owner can stop it.\n return\n }\n\n await this.teardownLocked(\"explicit_stop\")\n })\n }\n\n /**\n * Called by MantleManager when a `stream_status` event arrives from glasses\n * and the registry says it's phone-owned.\n */\n handleGlassesStatus(event: StreamStatusEvent): void {\n if (!this.current) return\n if (event.streamId && event.streamId !== this.current.streamId) return\n\n this.lifecycle?.recordActivity()\n const includeResolvedConfig = !this.resolvedConfigForwarded && !!event.resolvedConfig\n if (includeResolvedConfig) this.resolvedConfigForwarded = true\n const slimData = slimStreamStatusEvent(event, {includeResolvedConfig})\n const signature = streamStatusSignature(slimData)\n if (signature === this.lastFanoutSignature) return\n this.lastFanoutSignature = signature\n\n this.fanout({\n streamId: this.current.streamId,\n source: \"glasses\",\n status: event.status,\n data: slimData,\n })\n\n // Glasses-reported TERMINAL states unwind the coordinator. Terminal means\n // the publisher gave up or stopped — NOT a transient `kind:\"error\"`: the\n // glasses publisher auto-recovers (error → reconnecting → reconnected),\n // and tearing down on the first hiccup deletes the live input out from\n // under a publisher that comes right back (it then retries into a dead\n // input forever). A publisher that errors and never recovers is reaped by\n // the keep-alive ack timeout. Queue the teardown through the transition\n // lock so it serializes with any start/stop currently in flight.\n const isStopped =\n (event.kind === \"lifecycle\" && event.status === \"stopped\") ||\n (event.kind === \"snapshot\" && event.status === \"stopped\")\n const isGiveUp = event.kind === \"reconnect\" && event.status === \"reconnect_failed\"\n if (isGiveUp || isStopped) {\n const reason = isGiveUp ? \"glasses_gave_up\" : \"glasses_stopped\"\n const targetStreamId = this.current.streamId\n void this.runExclusive(async () => {\n // The stream we wanted to tear down may already be gone (e.g. another\n // teardown won the lock and unwound it). Guard before acting.\n if (this.current?.streamId !== targetStreamId) return\n await this.teardownLocked(reason, {sendBleStop: false})\n })\n }\n }\n\n /** Called by MantleManager when a phone-owned keep_alive_ack arrives. */\n handleKeepAliveAck(event: KeepAliveAckEvent): void {\n if (!event.ackId) return\n this.lifecycle?.handleAck(event.ackId)\n }\n\n // ===========================================================================\n // Internal\n // ===========================================================================\n\n private mintId(prefix: \"u\" | \"m\"): string {\n this.idCounter += 1\n return `phone-${prefix}-${Date.now().toString(36)}-${this.idCounter}`\n }\n\n private startLifecycle(streamId: string): void {\n this.lifecycle?.dispose()\n const ctrl = new StreamLifecycleController(\n {\n logger: consoleLogger,\n streamId,\n keepAliveIntervalMs: this.timings.keepAliveIntervalMs,\n ackTimeoutMs: this.timings.ackTimeoutMs,\n maxMissedAcks: this.timings.maxMissedAcks,\n },\n {\n sendKeepAlive: async (ackId) => {\n await BluetoothSdk.sendExternallyManagedStreamKeepAlive({\n type: \"keep_stream_alive\",\n streamId,\n ackId,\n })\n },\n onTimeout: async () => {\n this.fanout({\n streamId,\n source: \"coordinator\",\n status: \"error\",\n data: {reason: \"keep_alive_timeout\"},\n })\n await this.runExclusive(async () => {\n // The stream this timeout was bound to may already be gone.\n if (this.current?.streamId !== streamId) return\n await this.teardownLocked(\"keep_alive_timeout\")\n })\n },\n },\n )\n ctrl.setActive(true)\n this.lifecycle = ctrl\n }\n\n private startCloudflareStatusPoll(entry: ManagedEntry): void {\n // webrtc mode: how many polls to wait for the first \"connected\" before\n // declaring the publisher never reached Cloudflare. Mirrors the HLS\n // readiness budget (~60s at the default 5s cadence).\n const maxConnectAttempts = Math.max(\n 1,\n Math.ceil(\n (this.timings.hlsReadinessMaxAttempts * this.timings.hlsReadinessPollMs) /\n this.timings.cloudflareStatusPollMs,\n ),\n )\n const poll = async () => {\n if (this.current !== entry) return\n try {\n const status: CloudflareStatus = await getManagedStreamStatus(entry.liveInputId)\n this.fanout({\n streamId: entry.streamId,\n source: \"cloudflare\",\n status: status.isConnected ? \"connected\" : \"disconnected\",\n data: status as unknown as Record<string, unknown>,\n })\n // webrtc mode readiness: first \"connected\" means WHEP playback is\n // available (WebRTC playback follows the ingest directly; there is no\n // manifest to probe).\n if (entry.mode === \"webrtc\" && !entry.hlsReady) {\n if (status.isConnected) {\n entry.hlsReady = true\n const result = managedStartResult(entry)\n for (const r of entry.hlsReadyResolvers) r(result)\n entry.hlsReadyResolvers = []\n entry.hlsReadyRejecters = []\n this.fanout({\n streamId: entry.streamId,\n source: \"coordinator\",\n status: \"webrtc_ready\",\n data: result as unknown as Record<string, unknown>,\n })\n } else {\n entry.connectAttempts += 1\n if (entry.connectAttempts >= maxConnectAttempts) {\n const err = new Error(\n `WebRTC ingest never reached Cloudflare after ${entry.connectAttempts} status polls`,\n )\n for (const reject of entry.hlsReadyRejecters) reject(err)\n entry.hlsReadyResolvers = []\n entry.hlsReadyRejecters = []\n this.fanout({\n streamId: entry.streamId,\n source: \"coordinator\",\n status: \"error\",\n data: {reason: \"webrtc_not_connected\"},\n })\n const targetStreamId = entry.streamId\n void this.runExclusive(async () => {\n if (this.current?.streamId !== targetStreamId) return\n await this.teardownLocked(\"webrtc_not_connected\")\n })\n }\n }\n }\n } catch (err) {\n console.warn(\"[STREAM] cloudflare status poll failed:\", err)\n }\n }\n entry.cloudflareTimer = setInterval(poll, this.timings.cloudflareStatusPollMs)\n // Don't fire immediately — Cloudflare wouldn't have first-frame yet.\n }\n\n private startHlsReadinessPoll(entry: ManagedEntry): void {\n // Skip the first few seconds — Cloudflare doesn't have first-frame yet,\n // and the HEAD requests would all 404 and burn battery.\n const tick = async () => {\n if (this.current !== entry) return\n entry.hlsAttempts += 1\n try {\n // Require a real manifest (200 with a body), not just res.ok — the\n // playback edge returns 204 No Content while the input has no\n // HLS-capable frames (e.g. WebRTC ingest), and 204 is \"ok\".\n const res = await fetch(entry.hlsUrl, {method: \"HEAD\"})\n if (res.status === 200) {\n entry.hlsReady = true\n if (entry.hlsTimer) {\n clearInterval(entry.hlsTimer)\n entry.hlsTimer = undefined\n }\n const result = managedStartResult(entry)\n for (const r of entry.hlsReadyResolvers) r(result)\n entry.hlsReadyResolvers = []\n entry.hlsReadyRejecters = []\n this.fanout({\n streamId: entry.streamId,\n source: \"coordinator\",\n status: \"hls_ready\",\n data: result as unknown as Record<string, unknown>,\n })\n return\n }\n } catch {\n // 404 / network blip is expected while Cloudflare warms up.\n }\n if (entry.hlsAttempts >= this.timings.hlsReadinessMaxAttempts) {\n if (entry.hlsTimer) {\n clearInterval(entry.hlsTimer)\n entry.hlsTimer = undefined\n }\n const err = new Error(\n `HLS playback URL did not become ready after ${this.timings.hlsReadinessMaxAttempts} attempts`,\n )\n for (const reject of entry.hlsReadyRejecters) reject(err)\n entry.hlsReadyResolvers = []\n entry.hlsReadyRejecters = []\n this.fanout({\n streamId: entry.streamId,\n source: \"coordinator\",\n status: \"error\",\n data: {reason: \"hls_not_ready\"},\n })\n const targetStreamId = entry.streamId\n void this.runExclusive(async () => {\n if (this.current?.streamId !== targetStreamId) return\n await this.teardownLocked(\"hls_not_ready\")\n })\n }\n }\n setTimeout(() => {\n // Guard: stream may have been torn down during the initial delay.\n if (this.current !== entry) return\n entry.hlsTimer = setInterval(tick, this.timings.hlsReadinessPollMs)\n }, this.timings.hlsReadinessInitialDelayMs)\n }\n\n private fanout(update: StreamStatusUpdate): void {\n if (!this.current || !this.statusSubscriber) return\n const targets =\n this.current.kind === \"managed\"\n ? Array.from(this.current.subscribers)\n : [this.current.packageName]\n for (const pkg of targets) {\n try {\n this.statusSubscriber(pkg, update)\n } catch (err) {\n console.warn(\"[STREAM] statusSubscriber threw:\", err)\n }\n }\n }\n\n /**\n * Run teardown of the currently-active stream. Caller must hold the\n * transition lock (see {@link runExclusive}). Keeps `this.current`\n * populated until BluetoothSdk.stopStream resolves so a concurrent caller\n * waiting on the lock can't claim the slot mid-stop and have its\n * startStream BLE write collide with our in-flight stopStream.\n */\n private async teardownLocked(reason: string, options: {sendBleStop?: boolean} = {}): Promise<void> {\n const entry = this.current\n if (!entry) return\n const sendBleStop = options.sendBleStop !== false\n\n this.lastFanoutSignature = null\n this.resolvedConfigForwarded = false\n\n // Dispose the lifecycle controller immediately so it doesn't fire one\n // more keep-alive against a stream we're tearing down. The transition\n // lock guarantees no new lifecycle is started concurrently.\n this.lifecycle?.dispose()\n this.lifecycle = null\n\n if (entry.kind === \"managed\") {\n if (entry.cloudflareTimer) clearInterval(entry.cloudflareTimer)\n if (entry.hlsTimer) clearInterval(entry.hlsTimer)\n // Reject any still-pending HLS readiness waiters.\n const pendingErr = new Error(`Stream torn down: ${reason}`)\n for (const reject of entry.hlsReadyRejecters) reject(pendingErr)\n entry.hlsReadyResolvers = []\n entry.hlsReadyRejecters = []\n // Cloudflare DELETE is fire-and-forget — failing it doesn't undo the\n // BLE stop we're about to do, and Cloudflare's idle-input scavenger\n // will eventually clean up leaked inputs.\n teardownManagedStream(entry.liveInputId).catch((err) => {\n console.warn(\"[STREAM] teardownManagedStream failed:\", err)\n })\n }\n\n try {\n if (sendBleStop) {\n await BluetoothSdk.stopStream()\n }\n } catch (err) {\n console.warn(\"[STREAM] BluetoothSdk.stopStream failed:\", err)\n } finally {\n // Release the slot AFTER the BLE stop finished, so the next start can\n // safely write its own start_stream without colliding with ours.\n // Only clear if we're still the active entry (defensive — runExclusive\n // serializes us, so this should always be true).\n if (this.current === entry) this.current = null\n }\n }\n}\n\nfunction pickIngestUrl(p: ProvisionResult, preference?: \"srt\" | \"whip\"): string {\n // Glasses' StreamCommandHandler detects protocol from URL prefix.\n //\n // Default priority: SRT > RTMP > WHIP. SRT first: Cloudflare's WebRTC (WHIP)\n // ingest does NOT feed HLS/DASH playback or recording — a WHIP-ingested\n // managed stream reports \"connected\" while its hlsUrl serves 204 forever,\n // which breaks the managed contract (subscribers share HLS playback). SRT\n // also survives office firewalls that kill RTMPS:443 mid-handshake.\n //\n // \"whip\" preference flips the trade: sub-second WHEP playback for\n // live-monitor use cases, accepting no HLS and no recording.\n //\n // Throw if none resolved so the caller's Promise rejects with a clear\n // message rather than the glasses' \"unknown protocol\" error.\n const url =\n preference === \"whip\"\n ? p.webrtcPublishUrl || p.srtUrl || p.rtmpUrl\n : p.srtUrl || p.rtmpUrl || p.webrtcPublishUrl\n if (!url) {\n throw new Error(\"Cloudflare provision returned no usable ingest URL\")\n }\n return url\n}\n\nfunction publisherStartResult(streamId: string, event?: StreamStatusEvent): StreamPublisherStartResult {\n return {\n streamId: event?.streamId || streamId,\n status: event?.status ?? \"streaming\",\n ...(event?.resolvedConfig ? {resolvedConfig: event.resolvedConfig} : {}),\n }\n}\n\nfunction managedStartResult(entry: ManagedEntry): ManagedStartResult {\n const publisher = entry.publisherStart ?? publisherStartResult(entry.streamId)\n return {\n ...publisher,\n streamId: entry.streamId,\n liveInputId: entry.liveInputId,\n mode: entry.mode,\n hlsUrl: entry.hlsUrl,\n dashUrl: entry.dashUrl,\n webrtcUrl: entry.webrtcUrl,\n }\n}\n\n// Singleton — coordinator's single-stream constraint is process-wide.\nexport const phoneStreamCoordinator = new PhoneStreamCoordinator()\n"]}
1
+ {"version":3,"file":"PhoneStreamCoordinator.js","sourceRoot":"","sources":["../../src/services/PhoneStreamCoordinator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * PhoneStreamCoordinator — owns all local-miniapp streaming on the phone.\n *\n * Architecture:\n * miniapp → SDK → LocalMiniappRuntime → coordinator\n * coordinator → BluetoothSdk (BLE → glasses publisher)\n * coordinator ↔ cloudStreamApi (managed only, cloud-v2 runtime provisioning)\n * coordinator ↔ StreamLifecycleController (keep-alive heartbeat)\n * coordinator → status listeners → routed back to miniapp(s)\n *\n * Single-stream constraint:\n * At most ONE stream is active across all miniapps. The exception is that\n * multiple miniapps can subscribe to a single managed stream that's already\n * running — Cloudflare muxes one ingest into many viewers, so subscribers\n * share the same playback URLs. Refcounted; teardown happens when the last\n * subscriber releases.\n *\n * Status routing:\n * Glasses publisher status arrives over BLE as `stream_status` events with\n * a `streamId`. The coordinator's `owns(streamId)` lookup lets MantleManager\n * route phone-owned status events here (not to cloud). For managed streams,\n * we additionally poll Cloudflare's live-input status every 5s to surface\n * what the OTHER end of the pipe sees.\n *\n * Important: this is the ONLY place that mints `streamId`s for phone-owned\n * streams. We use a `phone-` prefix so they're trivially distinguishable from\n * cloud-minted IDs in logs and from cloud-SDK app streams that flow through\n * the legacy path.\n */\n\nimport BluetoothSdk from \"@mentra/bluetooth-sdk/internal\"\nimport type {\n KeepAliveAckEvent,\n StreamResolvedConfig,\n StreamStartRequest,\n StreamStatusEvent,\n} from \"@mentra/bluetooth-sdk/internal\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {useGlassesStore} from \"../stores/glasses\"\n\nimport {StreamLifecycleController, type LifecycleLogger} from \"./StreamLifecycleController\"\nimport {slimStreamStatusEvent, streamStatusSignature} from \"./slimStreamStatus\"\nimport {\n getManagedStreamStatus,\n provisionManagedStream,\n teardownManagedStream,\n type CloudflareStatus,\n type ProvisionResult,\n type RestreamDestinationInput,\n} from \"./cloudStreamApi\"\n\n/**\n * Default cadence + thresholds. Exposed via {@link CoordinatorTimings} so tests\n * can shorten them; production code should never override these.\n */\nconst DEFAULT_TIMINGS = {\n keepAliveIntervalMs: 15_000,\n ackTimeoutMs: 10_000,\n maxMissedAcks: 3,\n cloudflareStatusPollMs: 5_000,\n // During WHIP startup, probe quickly so readiness is not quantized to the\n // steady-state 5s monitoring cadence. The delay backs off on each miss.\n cloudflareStartupPollInitialMs: 500,\n // Cloudflare typically needs ~5-10s after first frame before HLS is live.\n hlsReadinessInitialDelayMs: 5_000,\n hlsReadinessPollMs: 2_000,\n hlsReadinessMaxAttempts: 30,\n} as const\n\ntype TimingConfig = {[K in keyof typeof DEFAULT_TIMINGS]: number}\nexport type CoordinatorTimings = Partial<TimingConfig>\n\n// Console-backed minimal logger; replaces pino on the phone.\nconst consoleLogger: LifecycleLogger = {\n child: (bindings) => ({\n ...consoleLogger,\n debug: (...args) => console.debug(\"[STREAM]\", bindings, ...args),\n warn: (...args) => console.warn(\"[STREAM]\", bindings, ...args),\n error: (...args) => console.error(\"[STREAM]\", bindings, ...args),\n }),\n debug: (...args) => console.debug(\"[STREAM]\", ...args),\n warn: (...args) => console.warn(\"[STREAM]\", ...args),\n error: (...args) => console.error(\"[STREAM]\", ...args),\n}\n\nexport interface StartUnmanagedOptions {\n streamUrl: string\n video?: StreamStartRequest[\"video\"]\n audio?: StreamStartRequest[\"audio\"]\n sound?: boolean\n /** Optional Bearer token for WHIP Authorization (custom authenticated endpoints). */\n authToken?: string\n}\n\nexport interface StartManagedOptions {\n restreamDestinations?: RestreamDestinationInput[]\n video?: StreamStartRequest[\"video\"]\n audio?: StreamStartRequest[\"audio\"]\n sound?: boolean\n /**\n * Ingest protocol preference — a real latency/durability trade on Cloudflare:\n * - \"srt\" (default): SRT ingest -> LL-HLS playback (~10-20s glass-to-screen),\n * with shareable HLS/DASH URLs and automatic recording.\n * - \"whip\": WebRTC ingest -> WHEP playback (<1s glass-to-screen), but NO\n * HLS/DASH playback and NO recording (Cloudflare limitation; the returned\n * hlsUrl will serve 204 forever).\n * - \"rtmp\": RTMPS ingest over TCP 443 -> HLS playback. Survives networks\n * that drop WHIP/SRT UDP.\n */\n ingest?: \"srt\" | \"whip\" | \"rtmp\"\n}\n\nexport interface StreamPublisherStartResult {\n streamId: string\n status: string\n resolvedConfig?: StreamResolvedConfig\n}\n\nexport interface ManagedStartResult extends StreamPublisherStartResult {\n liveInputId: string\n /** Playback mode this stream supports: \"hls\" (SRT/RTMP ingest — use hlsUrl/\n * dashUrl, recording on) or \"webrtc\" (WHIP ingest — use webrtcUrl/WHEP,\n * sub-second, no HLS, no recording). */\n mode: \"hls\" | \"webrtc\"\n hlsUrl: string\n dashUrl: string\n webrtcUrl?: string\n}\n\nexport type StreamStatusUpdate = {\n streamId: string\n source: \"glasses\" | \"cloudflare\" | \"coordinator\"\n status: string\n data?: Record<string, unknown>\n}\n\nexport type StatusSubscriber = (packageName: string, update: StreamStatusUpdate) => void\n\ninterface UnmanagedEntry {\n kind: \"unmanaged\"\n streamId: string\n packageName: string\n streamUrl: string\n}\n\ninterface ManagedEntry {\n kind: \"managed\"\n streamId: string\n liveInputId: string\n ingestUrl: string\n /** Playback mode the chosen ingest supports: SRT/RTMP feed HLS; WHIP feeds\n * only WHEP. Readiness is gated differently per mode. */\n mode: \"hls\" | \"webrtc\"\n hlsUrl: string\n dashUrl: string\n webrtcUrl?: string\n publisherStart?: StreamPublisherStartResult\n subscribers: Set<string>\n hlsReady: boolean\n hlsReadyResolvers: Array<(result: ManagedStartResult) => void>\n hlsReadyRejecters: Array<(err: Error) => void>\n cloudflareTimer?: ReturnType<typeof setTimeout>\n hlsTimer?: ReturnType<typeof setInterval>\n hlsAttempts: number\n /** Cloudflare status probes made during this stream session. */\n cloudflareAttempts: number\n /** Wall-clock origin for end-to-end managed startup diagnostics. */\n startupStartedAtMs: number\n}\n\ntype Entry = UnmanagedEntry | ManagedEntry\n\nexport class StreamConflictError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n /** Pipeline stage that failed (provision | command | publish | playback). */\n public readonly stage?: string,\n /** Transport in play at the failure (cloud-rest | ble | wifi). */\n public readonly transport?: string,\n ) {\n super(message)\n this.name = \"StreamConflictError\"\n }\n}\n\n/**\n * Fail fast if glasses aren't connected — BEFORE provisioning. Without this a\n * managed start would create a provider live input, fail the BLE command, and\n * tear the input down again: a slow, billable no-op with a confusing error.\n */\nfunction assertGlassesConnected(): void {\n if (!isGlassesConnected(useGlassesStore.getState().connection)) {\n throw new StreamConflictError(\n \"GLASSES_NOT_CONNECTED\",\n \"Glasses are not connected\",\n \"command\",\n \"ble\",\n )\n }\n}\n\nexport class PhoneStreamCoordinator {\n private current: Entry | null = null\n private lifecycle: StreamLifecycleController | null = null\n private statusSubscriber: StatusSubscriber | null = null\n private idCounter = 0\n private readonly timings: TimingConfig\n /**\n * Serializes state transitions (start, stop, teardown). Without it, a\n * second `start*` racing with the first can pass the `this.current === null`\n * pre-check while the first is still awaiting its provision/BLE work, and\n * end up provisioning two separate streams. A teardown racing with a start\n * can clear `current` mid-stop and let the start fire BLE writes that\n * collide with the in-flight stopStream.\n */\n private inFlight: Promise<void> = Promise.resolve()\n /** Drop identical stream_status fanouts within one session. */\n private lastFanoutSignature: string | null = null\n /** Send full resolvedConfig only once per stream session. */\n private resolvedConfigForwarded = false\n\n constructor(timings: CoordinatorTimings = {}) {\n this.timings = {...DEFAULT_TIMINGS, ...timings}\n }\n\n /**\n * Run `work` under the transition lock. Each call awaits the previous\n * transition before re-evaluating preconditions, so e.g. two concurrent\n * `startManaged` calls are observed sequentially.\n */\n private async runExclusive<T>(work: () => Promise<T>): Promise<T> {\n const prev = this.inFlight\n let release!: () => void\n this.inFlight = new Promise<void>((r) => (release = r))\n try {\n await prev\n return await work()\n } finally {\n release()\n }\n }\n\n /**\n * Register the function that routes status updates to miniapps.\n * LocalMiniappRuntime wires this so updates become EVENT envelopes on the\n * `stream_status` stream for every subscribing miniapp.\n */\n setStatusSubscriber(cb: StatusSubscriber): void {\n this.statusSubscriber = cb\n }\n\n /**\n * True when a phone-owned stream currently uses this streamId. For managed\n * streams, all subscribers share the same streamId so a single equality\n * check covers the multi-subscriber case.\n */\n owns(streamId: string): boolean {\n return this.current !== null && this.current.streamId === streamId\n }\n\n /** Report-safe stream ownership snapshot for incident diagnostics. */\n getDiagnosticSnapshot(): Record<string, unknown> {\n if (!this.current) return {active: false}\n return this.current.kind === \"managed\"\n ? {\n active: true,\n kind: this.current.kind,\n streamId: this.current.streamId,\n subscribers: [...this.current.subscribers].sort(),\n mode: this.current.mode,\n playbackReady: this.current.hlsReady,\n }\n : {\n active: true,\n kind: this.current.kind,\n streamId: this.current.streamId,\n ownerPackageName: this.current.packageName,\n }\n }\n\n async startUnmanaged(\n packageName: string,\n opts: StartUnmanagedOptions,\n ): Promise<StreamPublisherStartResult> {\n // Pre-check the obvious-bad input before queueing — the lock is for\n // serializing state transitions, not for validating arguments.\n if (!opts.streamUrl || typeof opts.streamUrl !== \"string\") {\n throw new StreamConflictError(\"STREAM_URL_REQUIRED\", \"streamUrl is required\")\n }\n assertGlassesConnected()\n return this.runExclusive(async () => {\n if (this.current) {\n throw new StreamConflictError(\n \"STREAM_ALREADY_ACTIVE\",\n `A ${this.current.kind} stream is already active. Stop it before starting a new one.`,\n )\n }\n\n const streamId = this.mintId(\"u\")\n const entry: UnmanagedEntry = {\n kind: \"unmanaged\",\n streamId,\n packageName,\n streamUrl: opts.streamUrl,\n }\n // Claim the slot BEFORE the BLE call so a concurrent caller waiting on\n // the lock sees the in-progress entry and rejects with the conflict.\n this.current = entry\n\n try {\n const event = await BluetoothSdk.startExternallyManagedStream({\n type: \"start_stream\",\n streamUrl: opts.streamUrl,\n streamId,\n sound: opts.sound ?? true,\n // The native bridge rejects explicit `undefined` values (\"Value is\n // undefined, expected an Object\") — only include what was provided.\n ...(opts.video !== undefined ? {video: opts.video} : {}),\n ...(opts.audio !== undefined ? {audio: opts.audio} : {}),\n ...(opts.authToken ? {authToken: opts.authToken} : {}),\n })\n const result = publisherStartResult(streamId, event)\n this.startLifecycle(streamId)\n return result\n } catch (err) {\n this.current = null\n throw err\n }\n })\n }\n\n async startManaged(\n packageName: string,\n opts: StartManagedOptions,\n ): Promise<ManagedStartResult> {\n const startupStartedAtMs = Date.now()\n // streamId doesn't exist yet — it's minted a few lines below, once we\n // know this is a fresh provision rather than a join onto an existing one.\n console.info(\"[STREAM_STARTUP]\", {\n stage: \"requested\",\n packageName,\n ingest: opts.ingest,\n elapsedMs: 0,\n })\n // Two-phase: the entry-claim runs under the transition lock; the wait for\n // HLS readiness happens AFTER the lock releases so a long warm-up doesn't\n // block subsequent start/stop transitions on this coordinator.\n assertGlassesConnected()\n type JoinDecision =\n | {kind: \"join\"; entry: ManagedEntry; immediate: ManagedStartResult | null}\n | {kind: \"fresh\"; entry: ManagedEntry}\n\n const decision = await this.runExclusive(async (): Promise<JoinDecision> => {\n if (this.current && this.current.kind === \"unmanaged\") {\n throw new StreamConflictError(\n \"STREAM_ALREADY_ACTIVE\",\n \"An unmanaged stream is already active. Stop it before starting a managed stream.\",\n )\n }\n\n // Join an existing managed stream if one is already running.\n if (this.current && this.current.kind === \"managed\") {\n const existing = this.current\n // Restream destinations are immutable after provision — a second\n // caller trying to dictate destinations on an already-live stream\n // is a likely bug or a feature we don't yet support.\n if (opts.restreamDestinations && opts.restreamDestinations.length > 0) {\n throw new StreamConflictError(\n \"STREAM_DESTINATIONS_LOCKED\",\n \"Restream destinations cannot be modified on an already-running managed stream.\",\n )\n }\n existing.subscribers.add(packageName)\n const immediate: ManagedStartResult | null = existing.hlsReady\n ? managedStartResult(existing)\n : null\n return {kind: \"join\", entry: existing, immediate}\n }\n\n // Fresh provision — claim slot BEFORE awaiting Cloudflare so a\n // concurrent caller queued behind us sees a managed stream in flight\n // and joins instead of double-provisioning.\n const provision = await provisionManagedStream(opts.restreamDestinations)\n const streamId = this.mintId(\"m\")\n const ingestUrl = pickIngestUrl(provision, opts.ingest)\n const mode: ManagedEntry[\"mode\"] =\n ingestUrl === provision.webrtcPublishUrl ? \"webrtc\" : \"hls\"\n\n const entry: ManagedEntry = {\n kind: \"managed\",\n streamId,\n liveInputId: provision.liveInputId,\n ingestUrl,\n mode,\n hlsUrl: provision.hlsUrl,\n dashUrl: provision.dashUrl,\n webrtcUrl: provision.webrtcUrl,\n subscribers: new Set([packageName]),\n hlsReady: false,\n hlsReadyResolvers: [],\n hlsReadyRejecters: [],\n hlsAttempts: 0,\n cloudflareAttempts: 0,\n startupStartedAtMs,\n }\n this.current = entry\n\n console.info(\"[STREAM_STARTUP]\", {\n streamId,\n stage: \"provisioned\",\n mode,\n elapsedMs: Date.now() - startupStartedAtMs,\n })\n\n try {\n const event = await BluetoothSdk.startExternallyManagedStream({\n type: \"start_stream\",\n streamUrl: ingestUrl,\n streamId,\n sound: opts.sound ?? true,\n // See startUnmanaged: the native bridge rejects explicit `undefined`.\n ...(opts.video !== undefined ? {video: opts.video} : {}),\n ...(opts.audio !== undefined ? {audio: opts.audio} : {}),\n })\n entry.publisherStart = publisherStartResult(streamId, event)\n console.info(\"[STREAM_STARTUP]\", {\n streamId,\n stage: \"publisher_ready\",\n mode,\n elapsedMs: Date.now() - startupStartedAtMs,\n })\n } catch (err) {\n this.current = null\n await teardownManagedStream(provision.liveInputId).catch(() => undefined)\n throw err\n }\n\n this.startLifecycle(streamId)\n this.startCloudflareStatusPoll(entry)\n // hls mode: readiness = a real HLS manifest exists. webrtc mode: HLS\n // never materializes (Cloudflare WHIP limitation) — readiness resolves\n // off the status poll's first \"connected\" instead.\n if (entry.mode === \"hls\") {\n this.startHlsReadinessPoll(entry)\n }\n return {kind: \"fresh\", entry}\n })\n\n if (decision.kind === \"join\" && decision.immediate) {\n return decision.immediate\n }\n\n // The first Cloudflare probe runs immediately and can complete before the\n // transition lock releases. Avoid stranding this caller after readiness\n // resolvers have already been drained.\n if (decision.entry.hlsReady) {\n return managedStartResult(decision.entry)\n }\n\n // Wait for playback readiness OUTSIDE the lock — readiness can take ~10s\n // and we don't want to block other transitions for that long.\n return new Promise<ManagedStartResult>((resolve, reject) => {\n decision.entry.hlsReadyResolvers.push(resolve)\n decision.entry.hlsReadyRejecters.push(reject)\n })\n }\n\n async stop(packageName: string, streamId?: string): Promise<void> {\n await this.runExclusive(async () => {\n if (!this.current) return\n\n // If a streamId was passed but doesn't match, ignore — silent no-op\n // matches the cloud's tolerant behavior.\n if (streamId && this.current.streamId !== streamId) return\n\n if (this.current.kind === \"managed\") {\n const entry = this.current\n entry.subscribers.delete(packageName)\n if (entry.subscribers.size > 0) {\n // Other miniapps still subscribed; keep the stream alive.\n return\n }\n } else if (this.current.packageName !== packageName) {\n // Unmanaged stream: only the owner can stop it.\n return\n }\n\n await this.teardownLocked(\"explicit_stop\")\n })\n }\n\n /**\n * Called by MantleManager when a `stream_status` event arrives from glasses\n * and the registry says it's phone-owned.\n */\n handleGlassesStatus(event: StreamStatusEvent): void {\n if (!this.current) return\n if (event.streamId && event.streamId !== this.current.streamId) return\n\n this.lifecycle?.recordActivity()\n const includeResolvedConfig = !this.resolvedConfigForwarded && !!event.resolvedConfig\n if (includeResolvedConfig) this.resolvedConfigForwarded = true\n const slimData = slimStreamStatusEvent(event, {includeResolvedConfig})\n const signature = streamStatusSignature(slimData)\n if (signature === this.lastFanoutSignature) return\n this.lastFanoutSignature = signature\n\n this.fanout({\n streamId: this.current.streamId,\n source: \"glasses\",\n status: event.status,\n data: slimData,\n })\n\n // Glasses-reported TERMINAL states unwind the coordinator. Terminal means\n // the publisher gave up or stopped — NOT a transient `kind:\"error\"`: the\n // glasses publisher auto-recovers (error → reconnecting → reconnected),\n // and tearing down on the first hiccup deletes the live input out from\n // under a publisher that comes right back (it then retries into a dead\n // input forever). A publisher that errors and never recovers is reaped by\n // the keep-alive ack timeout. Queue the teardown through the transition\n // lock so it serializes with any start/stop currently in flight.\n const isStopped =\n (event.kind === \"lifecycle\" && event.status === \"stopped\") ||\n (event.kind === \"snapshot\" && event.status === \"stopped\")\n const isGiveUp = event.kind === \"reconnect\" && event.status === \"reconnect_failed\"\n if (isGiveUp || isStopped) {\n const reason = isGiveUp ? \"glasses_gave_up\" : \"glasses_stopped\"\n const targetStreamId = this.current.streamId\n void this.runExclusive(async () => {\n // The stream we wanted to tear down may already be gone (e.g. another\n // teardown won the lock and unwound it). Guard before acting.\n if (this.current?.streamId !== targetStreamId) return\n await this.teardownLocked(reason, {sendBleStop: false})\n })\n }\n }\n\n /** Called by MantleManager when a phone-owned keep_alive_ack arrives. */\n handleKeepAliveAck(event: KeepAliveAckEvent): void {\n if (!event.ackId) return\n this.lifecycle?.handleAck(event.ackId)\n }\n\n // ===========================================================================\n // Internal\n // ===========================================================================\n\n private mintId(prefix: \"u\" | \"m\"): string {\n this.idCounter += 1\n return `phone-${prefix}-${Date.now().toString(36)}-${this.idCounter}`\n }\n\n private startLifecycle(streamId: string): void {\n this.lifecycle?.dispose()\n const ctrl = new StreamLifecycleController(\n {\n logger: consoleLogger,\n streamId,\n keepAliveIntervalMs: this.timings.keepAliveIntervalMs,\n ackTimeoutMs: this.timings.ackTimeoutMs,\n maxMissedAcks: this.timings.maxMissedAcks,\n },\n {\n sendKeepAlive: async (ackId) => {\n await BluetoothSdk.sendExternallyManagedStreamKeepAlive({\n type: \"keep_stream_alive\",\n streamId,\n ackId,\n })\n },\n onTimeout: async () => {\n this.fanout({\n streamId,\n source: \"coordinator\",\n status: \"error\",\n data: {reason: \"keep_alive_timeout\"},\n })\n await this.runExclusive(async () => {\n // The stream this timeout was bound to may already be gone.\n if (this.current?.streamId !== streamId) return\n await this.teardownLocked(\"keep_alive_timeout\")\n })\n },\n },\n )\n ctrl.setActive(true)\n this.lifecycle = ctrl\n }\n\n private startCloudflareStatusPoll(entry: ManagedEntry): void {\n // Keep the existing ~60s readiness budget while decoupling it from probe\n // count. Startup probes begin immediately and back off to the normal 5s\n // monitoring cadence, so a just-connected publisher is noticed quickly\n // without increasing steady-state traffic.\n const connectTimeoutMs = Math.max(1, this.timings.hlsReadinessMaxAttempts * this.timings.hlsReadinessPollMs)\n const pollingStartedAtMs = Date.now()\n\n const scheduleNext = () => {\n if (this.current !== entry) return\n const waitingForWebRtc = entry.mode === \"webrtc\" && !entry.hlsReady\n const elapsedMs = Date.now() - pollingStartedAtMs\n const remainingMs = Math.max(0, connectTimeoutMs - elapsedMs)\n const startupDelayMs = Math.min(\n this.timings.cloudflareStatusPollMs,\n this.timings.cloudflareStartupPollInitialMs * 2 ** Math.min(Math.max(0, entry.cloudflareAttempts - 1), 10),\n )\n const delayMs = waitingForWebRtc ? Math.min(startupDelayMs, remainingMs) : this.timings.cloudflareStatusPollMs\n entry.cloudflareTimer = setTimeout(() => void poll(), delayMs)\n }\n\n const poll = async () => {\n if (this.current !== entry) return\n const requestStartedAtMs = Date.now()\n let keepPolling = true\n entry.cloudflareAttempts += 1\n try {\n const status: CloudflareStatus = await getManagedStreamStatus(entry.liveInputId)\n console.debug(\"[STREAM_STARTUP]\", {\n streamId: entry.streamId,\n stage: \"cloudflare_probe\",\n connected: status.isConnected,\n attempt: entry.cloudflareAttempts,\n requestMs: Date.now() - requestStartedAtMs,\n elapsedMs: Date.now() - entry.startupStartedAtMs,\n })\n this.fanout({\n streamId: entry.streamId,\n source: \"cloudflare\",\n status: status.isConnected ? \"connected\" : \"disconnected\",\n data: status as unknown as Record<string, unknown>,\n })\n // webrtc mode readiness: first \"connected\" means WHEP playback is\n // available (WebRTC playback follows the ingest directly; there is no\n // manifest to probe).\n if (entry.mode === \"webrtc\" && !entry.hlsReady) {\n if (status.isConnected) {\n entry.hlsReady = true\n console.info(\"[STREAM_STARTUP]\", {\n streamId: entry.streamId,\n stage: \"playback_ready\",\n mode: entry.mode,\n probes: entry.cloudflareAttempts,\n elapsedMs: Date.now() - entry.startupStartedAtMs,\n })\n const result = managedStartResult(entry)\n for (const r of entry.hlsReadyResolvers) r(result)\n entry.hlsReadyResolvers = []\n entry.hlsReadyRejecters = []\n this.fanout({\n streamId: entry.streamId,\n source: \"coordinator\",\n status: \"webrtc_ready\",\n data: result as unknown as Record<string, unknown>,\n })\n } else {\n if (Date.now() - pollingStartedAtMs >= connectTimeoutMs) {\n const err = new Error(`WebRTC ingest never reached Cloudflare after ${connectTimeoutMs}ms`)\n for (const reject of entry.hlsReadyRejecters) reject(err)\n entry.hlsReadyResolvers = []\n entry.hlsReadyRejecters = []\n this.fanout({\n streamId: entry.streamId,\n source: \"coordinator\",\n status: \"error\",\n data: {reason: \"webrtc_not_connected\"},\n })\n const targetStreamId = entry.streamId\n void this.runExclusive(async () => {\n if (this.current?.streamId !== targetStreamId) return\n await this.teardownLocked(\"webrtc_not_connected\")\n })\n keepPolling = false\n }\n }\n }\n } catch (err) {\n console.warn(\"[STREAM] cloudflare status poll failed:\", err)\n if (entry.mode === \"webrtc\" && !entry.hlsReady && Date.now() - pollingStartedAtMs >= connectTimeoutMs) {\n const timeoutErr = new Error(`WebRTC ingest status could not be confirmed after ${connectTimeoutMs}ms`)\n for (const reject of entry.hlsReadyRejecters) reject(timeoutErr)\n entry.hlsReadyResolvers = []\n entry.hlsReadyRejecters = []\n const targetStreamId = entry.streamId\n void this.runExclusive(async () => {\n if (this.current?.streamId !== targetStreamId) return\n await this.teardownLocked(\"webrtc_status_unavailable\")\n })\n keepPolling = false\n }\n } finally {\n if (keepPolling) scheduleNext()\n }\n }\n\n // The glasses publisher has already reported streaming, so the first\n // status request is useful now. Most starts avoid the old blind 5s wait.\n void poll()\n }\n\n private startHlsReadinessPoll(entry: ManagedEntry): void {\n // Skip the first few seconds — Cloudflare doesn't have first-frame yet,\n // and the HEAD requests would all 404 and burn battery.\n const tick = async () => {\n if (this.current !== entry) return\n entry.hlsAttempts += 1\n try {\n // Require a real manifest (200 with a body), not just res.ok — the\n // playback edge returns 204 No Content while the input has no\n // HLS-capable frames (e.g. WebRTC ingest), and 204 is \"ok\".\n const res = await fetch(entry.hlsUrl, {method: \"HEAD\"})\n if (res.status === 200) {\n entry.hlsReady = true\n console.info(\"[STREAM_STARTUP]\", {\n streamId: entry.streamId,\n stage: \"playback_ready\",\n mode: entry.mode,\n probes: entry.hlsAttempts,\n elapsedMs: Date.now() - entry.startupStartedAtMs,\n })\n if (entry.hlsTimer) {\n clearInterval(entry.hlsTimer)\n entry.hlsTimer = undefined\n }\n const result = managedStartResult(entry)\n for (const r of entry.hlsReadyResolvers) r(result)\n entry.hlsReadyResolvers = []\n entry.hlsReadyRejecters = []\n this.fanout({\n streamId: entry.streamId,\n source: \"coordinator\",\n status: \"hls_ready\",\n data: result as unknown as Record<string, unknown>,\n })\n return\n }\n } catch {\n // 404 / network blip is expected while Cloudflare warms up.\n }\n if (entry.hlsAttempts >= this.timings.hlsReadinessMaxAttempts) {\n if (entry.hlsTimer) {\n clearInterval(entry.hlsTimer)\n entry.hlsTimer = undefined\n }\n const err = new Error(\n `HLS playback URL did not become ready after ${this.timings.hlsReadinessMaxAttempts} attempts`,\n )\n for (const reject of entry.hlsReadyRejecters) reject(err)\n entry.hlsReadyResolvers = []\n entry.hlsReadyRejecters = []\n this.fanout({\n streamId: entry.streamId,\n source: \"coordinator\",\n status: \"error\",\n data: {reason: \"hls_not_ready\"},\n })\n const targetStreamId = entry.streamId\n void this.runExclusive(async () => {\n if (this.current?.streamId !== targetStreamId) return\n await this.teardownLocked(\"hls_not_ready\")\n })\n }\n }\n setTimeout(() => {\n // Guard: stream may have been torn down during the initial delay.\n if (this.current !== entry) return\n entry.hlsTimer = setInterval(tick, this.timings.hlsReadinessPollMs)\n }, this.timings.hlsReadinessInitialDelayMs)\n }\n\n private fanout(update: StreamStatusUpdate): void {\n if (!this.current || !this.statusSubscriber) return\n const targets =\n this.current.kind === \"managed\"\n ? Array.from(this.current.subscribers)\n : [this.current.packageName]\n for (const pkg of targets) {\n try {\n this.statusSubscriber(pkg, update)\n } catch (err) {\n console.warn(\"[STREAM] statusSubscriber threw:\", err)\n }\n }\n }\n\n /**\n * Run teardown of the currently-active stream. Caller must hold the\n * transition lock (see {@link runExclusive}). Keeps `this.current`\n * populated until BluetoothSdk.stopStream resolves so a concurrent caller\n * waiting on the lock can't claim the slot mid-stop and have its\n * startStream BLE write collide with our in-flight stopStream.\n */\n private async teardownLocked(reason: string, options: {sendBleStop?: boolean} = {}): Promise<void> {\n const entry = this.current\n if (!entry) return\n const sendBleStop = options.sendBleStop !== false\n\n this.lastFanoutSignature = null\n this.resolvedConfigForwarded = false\n\n // Dispose the lifecycle controller immediately so it doesn't fire one\n // more keep-alive against a stream we're tearing down. The transition\n // lock guarantees no new lifecycle is started concurrently.\n this.lifecycle?.dispose()\n this.lifecycle = null\n\n if (entry.kind === \"managed\") {\n if (entry.cloudflareTimer) clearTimeout(entry.cloudflareTimer)\n if (entry.hlsTimer) clearInterval(entry.hlsTimer)\n // Reject any still-pending HLS readiness waiters.\n const pendingErr = new Error(`Stream torn down: ${reason}`)\n for (const reject of entry.hlsReadyRejecters) reject(pendingErr)\n entry.hlsReadyResolvers = []\n entry.hlsReadyRejecters = []\n // Cloudflare DELETE is fire-and-forget — failing it doesn't undo the\n // BLE stop we're about to do, and Cloudflare's idle-input scavenger\n // will eventually clean up leaked inputs.\n teardownManagedStream(entry.liveInputId).catch((err) => {\n console.warn(\"[STREAM] teardownManagedStream failed:\", err)\n })\n }\n\n try {\n if (sendBleStop) {\n await BluetoothSdk.stopStream()\n }\n } catch (err) {\n console.warn(\"[STREAM] BluetoothSdk.stopStream failed:\", err)\n } finally {\n // Release the slot AFTER the BLE stop finished, so the next start can\n // safely write its own start_stream without colliding with ours.\n // Only clear if we're still the active entry (defensive — runExclusive\n // serializes us, so this should always be true).\n if (this.current === entry) this.current = null\n }\n }\n}\n\nfunction pickIngestUrl(p: ProvisionResult, preference?: \"srt\" | \"whip\" | \"rtmp\"): string {\n // Glasses' StreamCommandHandler detects protocol from URL prefix.\n //\n // Default priority: SRT > RTMP > WHIP. SRT first: Cloudflare's WebRTC (WHIP)\n // ingest does NOT feed HLS/DASH playback or recording — a WHIP-ingested\n // managed stream reports \"connected\" while its hlsUrl serves 204 forever,\n // which breaks the managed contract (subscribers share HLS playback). SRT\n // also survives office firewalls that kill RTMPS:443 mid-handshake.\n //\n // \"whip\" preference flips the trade: sub-second WHEP playback for\n // live-monitor use cases, accepting no HLS and no recording.\n // \"rtmp\" prefers TCP 443 ingest so networks that drop WHIP/SRT UDP still\n // get HLS playback. Callers must request it explicitly; WHIP starts do not\n // fall back to RTMP.\n //\n // Throw if none resolved so the caller's Promise rejects with a clear\n // message rather than the glasses' \"unknown protocol\" error.\n const url =\n preference === \"whip\"\n ? p.webrtcPublishUrl || p.srtUrl || p.rtmpUrl\n : preference === \"rtmp\"\n ? p.rtmpUrl || p.srtUrl || p.webrtcPublishUrl\n : p.srtUrl || p.rtmpUrl || p.webrtcPublishUrl\n if (!url) {\n throw new Error(\"Cloudflare provision returned no usable ingest URL\")\n }\n return url\n}\n\nfunction publisherStartResult(streamId: string, event?: StreamStatusEvent): StreamPublisherStartResult {\n return {\n streamId: event?.streamId || streamId,\n status: event?.status ?? \"streaming\",\n ...(event?.resolvedConfig ? {resolvedConfig: event.resolvedConfig} : {}),\n }\n}\n\nfunction managedStartResult(entry: ManagedEntry): ManagedStartResult {\n const publisher = entry.publisherStart ?? publisherStartResult(entry.streamId)\n return {\n ...publisher,\n streamId: entry.streamId,\n liveInputId: entry.liveInputId,\n mode: entry.mode,\n hlsUrl: entry.hlsUrl,\n dashUrl: entry.dashUrl,\n webrtcUrl: entry.webrtcUrl,\n }\n}\n\n// Singleton — coordinator's single-stream constraint is process-wide.\nexport const phoneStreamCoordinator = new PhoneStreamCoordinator()\n"]}
@@ -31,6 +31,8 @@ export declare class VideoError extends Error {
31
31
  export declare class PhoneVideoCoordinator {
32
32
  private readonly activeRecordings;
33
33
  private starting;
34
+ /** Report-safe recording ownership snapshot for incident diagnostics. */
35
+ getDiagnosticSnapshot(): Record<string, unknown>;
34
36
  startRecording(packageName: string, opts: VideoRecordingOpts): Promise<VideoRecordingStarted>;
35
37
  stopRecording(packageName: string, recordingId?: string, opts?: {
36
38
  uploadUrl?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneVideoCoordinator.d.ts","sourceRoot":"","sources":["../../src/services/PhoneVideoCoordinator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,qBAAa,UAAW,SAAQ,KAAK;aAEjB,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM;CAKlB;AAOD,qBAAa,qBAAqB;IAGhC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqC;IAItE,OAAO,CAAC,QAAQ,CAAQ;IAElB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAqD7F,aAAa,CACjB,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAA;KAAC,GACpD,OAAO,CAAC,IAAI,CAAC;IAgChB;;;;;OAKG;IACG,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBpD,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,gBAAgB;CAGzB;AAGD,eAAO,MAAM,qBAAqB,uBAA8B,CAAA"}
1
+ {"version":3,"file":"PhoneVideoCoordinator.d.ts","sourceRoot":"","sources":["../../src/services/PhoneVideoCoordinator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,qBAAa,UAAW,SAAQ,KAAK;aAEjB,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM;CAKlB;AAOD,qBAAa,qBAAqB;IAGhC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqC;IAItE,OAAO,CAAC,QAAQ,CAAQ;IAExB,yEAAyE;IACzE,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAU1C,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAqD7F,aAAa,CACjB,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAA;KAAC,GACpD,OAAO,CAAC,IAAI,CAAC;IAgChB;;;;;OAKG;IACG,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBpD,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,gBAAgB;CAGzB;AAGD,eAAO,MAAM,qBAAqB,uBAA8B,CAAA"}
@@ -32,6 +32,16 @@ export class PhoneVideoCoordinator {
32
32
  // Set while a start is dispatching, before it lands in activeRecordings, so a
33
33
  // second start that races the first is still rejected.
34
34
  starting = false;
35
+ /** Report-safe recording ownership snapshot for incident diagnostics. */
36
+ getDiagnosticSnapshot() {
37
+ return {
38
+ recordingOwners: [
39
+ ...new Set([...this.activeRecordings.values()].map((recording) => recording.packageName)),
40
+ ].sort(),
41
+ activeRecordingCount: this.activeRecordings.size,
42
+ startInProgress: this.starting,
43
+ };
44
+ }
35
45
  async startRecording(packageName, opts) {
36
46
  // Fail fast if glasses aren't connected — the BLE command would otherwise
37
47
  // be sent into the void.
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneVideoCoordinator.js","sourceRoot":"","sources":["../../src/services/PhoneVideoCoordinator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * PhoneVideoCoordinator — owns local-miniapp startVideoRecording()/\n * stopVideoRecording() end-to-end.\n *\n * Architecture:\n * miniapp → SDK → LocalMiniappRuntime → videoRecording runtime hook\n * → coordinator.startRecording(packageName, opts)\n * ├── (precheck) glasses connected\n * └── BluetoothSdk.startVideoRecording(requestId, save, sound, settings)\n *\n * Unlike photo, this has no cloud upload/long-poll; the promise resolves once\n * the glasses report a correlated video_recording_status for the request. The\n * active recordings map enforces single-recording (rejecting overlapping starts\n * so we never hand back a phantom id), validates stop ownership, and lets a\n * closed/crashed miniapp be cleaned up on unregister.\n */\n\nimport BluetoothSdk from \"@mentra/bluetooth-sdk/internal\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\n\nexport interface VideoRecordingOpts {\n width?: number\n height?: number\n fps?: number\n sound?: boolean\n save?: boolean\n}\n\nexport interface VideoRecordingStarted {\n recordingId: string\n}\n\nexport class VideoError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n ) {\n super(message)\n this.name = \"VideoError\"\n }\n}\n\ninterface ActiveRecording {\n packageName: string\n recordingId: string\n}\n\nexport class PhoneVideoCoordinator {\n // recordingId → active recording. Used to enforce single-recording, validate\n // stop requests, and clean up on miniapp unregister.\n private readonly activeRecordings = new Map<string, ActiveRecording>()\n\n // Set while a start is dispatching, before it lands in activeRecordings, so a\n // second start that races the first is still rejected.\n private starting = false\n\n async startRecording(packageName: string, opts: VideoRecordingOpts): Promise<VideoRecordingStarted> {\n // Fail fast if glasses aren't connected — the BLE command would otherwise\n // be sent into the void.\n if (!isGlassesConnected(useGlassesStore.getState().connection)) {\n throw new VideoError(\"GLASSES_NOT_CONNECTED\", \"Glasses are not connected\")\n }\n\n // The glasses can only record one video at a time. A second start while one\n // is already active (or in-flight) is ignored device-side, but we'd still\n // mint and return a *new* recordingId — and stopping that id would not stop\n // the real recording, leaving it to run until the max-recording timeout.\n // Reject up front so the caller gets an honest error instead of a phantom id.\n if (this.starting || this.activeRecordings.size > 0) {\n throw new VideoError(\"ALREADY_RECORDING\", \"A video recording is already in progress\")\n }\n\n this.starting = true\n try {\n const recordingId = `miniapp_video_${Date.now()}_${Math.round(Math.random() * 1e6)}`\n\n // Only forward settings when at least one field is set; otherwise let the\n // glasses use their saved button-video defaults. The native serializers\n // send each provided field independently, so an fps-only override still\n // applies (the glasses merge the missing fields onto their saved defaults).\n const settings =\n opts.width != null || opts.height != null || opts.fps != null\n ? {width: opts.width, height: opts.height, fps: opts.fps}\n : undefined\n\n try {\n const status = await BluetoothSdk.startVideoRecording(\n recordingId,\n opts.save ?? false,\n opts.sound ?? true,\n settings,\n )\n if (status.status !== \"recording_started\") {\n throw new VideoError(\n status.status || \"VIDEO_RECORDING_FAILED\",\n status.details || \"Glasses did not start recording\",\n )\n }\n } catch (err) {\n throw this.toVideoError(err, \"BLE_SEND_FAILED\")\n }\n\n this.activeRecordings.set(recordingId, {packageName, recordingId})\n return {recordingId}\n } finally {\n this.starting = false\n }\n }\n\n async stopRecording(\n packageName: string,\n recordingId?: string,\n opts?: {uploadUrl?: string; uploadAuthToken?: string},\n ): Promise<void> {\n if (!recordingId) {\n throw new VideoError(\"INVALID_RECORDING_ID\", \"recordingId is required to stop a recording\")\n }\n\n // Validate ownership before touching the glasses: a miniapp may only stop a\n // recording it started, and we shouldn't forward an unknown id.\n const active = this.activeRecordings.get(recordingId)\n if (!active || active.packageName !== packageName) {\n throw new VideoError(\"RECORDING_NOT_OWNED\", \"recordingId is not an active recording owned by this app\")\n }\n\n try {\n // When uploadUrl is set the glasses upload the finished clip there (multipart\n // POST); otherwise the recording stays on the glasses (local save).\n const status = await BluetoothSdk.stopVideoRecording(recordingId, opts?.uploadUrl, opts?.uploadAuthToken)\n if (status.status !== \"recording_stopped\") {\n throw new VideoError(status.status || \"VIDEO_STOP_FAILED\", status.details || \"Glasses did not stop recording\")\n }\n } catch (err) {\n const videoError = this.toVideoError(err, \"BLE_SEND_FAILED\")\n if (this.isAlreadyStopped(videoError)) {\n this.activeRecordings.delete(recordingId)\n return\n }\n // Leave the recording tracked so a retry (or unregister cleanup) can stop\n // it — only drop it once the glasses report a successful stop.\n throw videoError\n }\n this.activeRecordings.delete(recordingId)\n }\n\n /**\n * Stop every recording owned by a package. Called when a miniapp unregisters\n * (close/crash): the miniapp loses its recordingId, so without this the\n * glasses keep recording until the max-recording timeout or thermal shutdown.\n * Mirrors the streaming coordinator's per-app cleanup on disconnect.\n */\n async stopForApp(packageName: string): Promise<void> {\n const owned = [...this.activeRecordings.values()].filter((r) => r.packageName === packageName)\n for (const rec of owned) {\n try {\n const status = await BluetoothSdk.stopVideoRecording(rec.recordingId)\n // Only drop tracking once the glasses report a successful stop (mirrors\n // stopRecording). If we deleted on failure the glasses could still be\n // recording while the single-recording guard reads \"idle\", letting the\n // next start hand back a phantom id that can't stop the real recording.\n if (status.status === \"recording_stopped\") {\n this.activeRecordings.delete(rec.recordingId)\n }\n } catch (err) {\n const videoError = this.toVideoError(err, \"BLE_SEND_FAILED\")\n if (this.isAlreadyStopped(videoError)) {\n this.activeRecordings.delete(rec.recordingId)\n continue\n }\n console.warn(`[PhoneVideoCoordinator] failed to stop ${rec.recordingId} for ${packageName} on cleanup`, videoError)\n }\n }\n }\n\n private toVideoError(err: unknown, fallbackCode: string): VideoError {\n if (err instanceof VideoError) return err\n const code = (err as {code?: string})?.code\n const message = err instanceof Error ? err.message : String(err)\n return new VideoError(code || fallbackCode, message)\n }\n\n private isAlreadyStopped(error: VideoError): boolean {\n return error.code === \"not_recording\" || error.code === \"recording_stopped\"\n }\n}\n\n// Singleton — coordinator state is process-wide (mirrors phonePhotoCoordinator).\nexport const phoneVideoCoordinator = new PhoneVideoCoordinator()\n"]}
1
+ {"version":3,"file":"PhoneVideoCoordinator.js","sourceRoot":"","sources":["../../src/services/PhoneVideoCoordinator.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * PhoneVideoCoordinator — owns local-miniapp startVideoRecording()/\n * stopVideoRecording() end-to-end.\n *\n * Architecture:\n * miniapp → SDK → LocalMiniappRuntime → videoRecording runtime hook\n * → coordinator.startRecording(packageName, opts)\n * ├── (precheck) glasses connected\n * └── BluetoothSdk.startVideoRecording(requestId, save, sound, settings)\n *\n * Unlike photo, this has no cloud upload/long-poll; the promise resolves once\n * the glasses report a correlated video_recording_status for the request. The\n * active recordings map enforces single-recording (rejecting overlapping starts\n * so we never hand back a phantom id), validates stop ownership, and lets a\n * closed/crashed miniapp be cleaned up on unregister.\n */\n\nimport BluetoothSdk from \"@mentra/bluetooth-sdk/internal\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\n\nexport interface VideoRecordingOpts {\n width?: number\n height?: number\n fps?: number\n sound?: boolean\n save?: boolean\n}\n\nexport interface VideoRecordingStarted {\n recordingId: string\n}\n\nexport class VideoError extends Error {\n constructor(\n public readonly code: string,\n message: string,\n ) {\n super(message)\n this.name = \"VideoError\"\n }\n}\n\ninterface ActiveRecording {\n packageName: string\n recordingId: string\n}\n\nexport class PhoneVideoCoordinator {\n // recordingId → active recording. Used to enforce single-recording, validate\n // stop requests, and clean up on miniapp unregister.\n private readonly activeRecordings = new Map<string, ActiveRecording>()\n\n // Set while a start is dispatching, before it lands in activeRecordings, so a\n // second start that races the first is still rejected.\n private starting = false\n\n /** Report-safe recording ownership snapshot for incident diagnostics. */\n getDiagnosticSnapshot(): Record<string, unknown> {\n return {\n recordingOwners: [\n ...new Set([...this.activeRecordings.values()].map((recording) => recording.packageName)),\n ].sort(),\n activeRecordingCount: this.activeRecordings.size,\n startInProgress: this.starting,\n }\n }\n\n async startRecording(packageName: string, opts: VideoRecordingOpts): Promise<VideoRecordingStarted> {\n // Fail fast if glasses aren't connected — the BLE command would otherwise\n // be sent into the void.\n if (!isGlassesConnected(useGlassesStore.getState().connection)) {\n throw new VideoError(\"GLASSES_NOT_CONNECTED\", \"Glasses are not connected\")\n }\n\n // The glasses can only record one video at a time. A second start while one\n // is already active (or in-flight) is ignored device-side, but we'd still\n // mint and return a *new* recordingId — and stopping that id would not stop\n // the real recording, leaving it to run until the max-recording timeout.\n // Reject up front so the caller gets an honest error instead of a phantom id.\n if (this.starting || this.activeRecordings.size > 0) {\n throw new VideoError(\"ALREADY_RECORDING\", \"A video recording is already in progress\")\n }\n\n this.starting = true\n try {\n const recordingId = `miniapp_video_${Date.now()}_${Math.round(Math.random() * 1e6)}`\n\n // Only forward settings when at least one field is set; otherwise let the\n // glasses use their saved button-video defaults. The native serializers\n // send each provided field independently, so an fps-only override still\n // applies (the glasses merge the missing fields onto their saved defaults).\n const settings =\n opts.width != null || opts.height != null || opts.fps != null\n ? {width: opts.width, height: opts.height, fps: opts.fps}\n : undefined\n\n try {\n const status = await BluetoothSdk.startVideoRecording(\n recordingId,\n opts.save ?? false,\n opts.sound ?? true,\n settings,\n )\n if (status.status !== \"recording_started\") {\n throw new VideoError(\n status.status || \"VIDEO_RECORDING_FAILED\",\n status.details || \"Glasses did not start recording\",\n )\n }\n } catch (err) {\n throw this.toVideoError(err, \"BLE_SEND_FAILED\")\n }\n\n this.activeRecordings.set(recordingId, {packageName, recordingId})\n return {recordingId}\n } finally {\n this.starting = false\n }\n }\n\n async stopRecording(\n packageName: string,\n recordingId?: string,\n opts?: {uploadUrl?: string; uploadAuthToken?: string},\n ): Promise<void> {\n if (!recordingId) {\n throw new VideoError(\"INVALID_RECORDING_ID\", \"recordingId is required to stop a recording\")\n }\n\n // Validate ownership before touching the glasses: a miniapp may only stop a\n // recording it started, and we shouldn't forward an unknown id.\n const active = this.activeRecordings.get(recordingId)\n if (!active || active.packageName !== packageName) {\n throw new VideoError(\"RECORDING_NOT_OWNED\", \"recordingId is not an active recording owned by this app\")\n }\n\n try {\n // When uploadUrl is set the glasses upload the finished clip there (multipart\n // POST); otherwise the recording stays on the glasses (local save).\n const status = await BluetoothSdk.stopVideoRecording(recordingId, opts?.uploadUrl, opts?.uploadAuthToken)\n if (status.status !== \"recording_stopped\") {\n throw new VideoError(status.status || \"VIDEO_STOP_FAILED\", status.details || \"Glasses did not stop recording\")\n }\n } catch (err) {\n const videoError = this.toVideoError(err, \"BLE_SEND_FAILED\")\n if (this.isAlreadyStopped(videoError)) {\n this.activeRecordings.delete(recordingId)\n return\n }\n // Leave the recording tracked so a retry (or unregister cleanup) can stop\n // it — only drop it once the glasses report a successful stop.\n throw videoError\n }\n this.activeRecordings.delete(recordingId)\n }\n\n /**\n * Stop every recording owned by a package. Called when a miniapp unregisters\n * (close/crash): the miniapp loses its recordingId, so without this the\n * glasses keep recording until the max-recording timeout or thermal shutdown.\n * Mirrors the streaming coordinator's per-app cleanup on disconnect.\n */\n async stopForApp(packageName: string): Promise<void> {\n const owned = [...this.activeRecordings.values()].filter((r) => r.packageName === packageName)\n for (const rec of owned) {\n try {\n const status = await BluetoothSdk.stopVideoRecording(rec.recordingId)\n // Only drop tracking once the glasses report a successful stop (mirrors\n // stopRecording). If we deleted on failure the glasses could still be\n // recording while the single-recording guard reads \"idle\", letting the\n // next start hand back a phantom id that can't stop the real recording.\n if (status.status === \"recording_stopped\") {\n this.activeRecordings.delete(rec.recordingId)\n }\n } catch (err) {\n const videoError = this.toVideoError(err, \"BLE_SEND_FAILED\")\n if (this.isAlreadyStopped(videoError)) {\n this.activeRecordings.delete(rec.recordingId)\n continue\n }\n console.warn(`[PhoneVideoCoordinator] failed to stop ${rec.recordingId} for ${packageName} on cleanup`, videoError)\n }\n }\n }\n\n private toVideoError(err: unknown, fallbackCode: string): VideoError {\n if (err instanceof VideoError) return err\n const code = (err as {code?: string})?.code\n const message = err instanceof Error ? err.message : String(err)\n return new VideoError(code || fallbackCode, message)\n }\n\n private isAlreadyStopped(error: VideoError): boolean {\n return error.code === \"not_recording\" || error.code === \"recording_stopped\"\n }\n}\n\n// Singleton — coordinator state is process-wide (mirrors phonePhotoCoordinator).\nexport const phoneVideoCoordinator = new PhoneVideoCoordinator()\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"SceneRenderer.d.ts","sourceRoot":"","sources":["../../src/services/SceneRenderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,EAKL,KAAK,YAAY,EAEjB,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EAEvB,MAAM,wBAAwB,CAAA;AAI/B,2DAA2D;AAC3D,MAAM,MAAM,YAAY,GAAG;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAC,CAAA;AAEvE,MAAM,MAAM,eAAe;AACzB,mCAAmC;AACjC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAC;AACvD,0GAA0G;GACxG;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAC;AACrF,uDAAuD;GACrD;IAAC,IAAI,EAAE,YAAY,CAAA;CAAC,CAAA;AAExB,cAAM,aAAa;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA6B;IACpD,OAAO,CAAC,KAAK,CAAmB;WAElB,WAAW,IAAI,aAAa;IAW1C;;;OAGG;IACI,mBAAmB,IAAI,wBAAwB,GAAG,IAAI;IAyB7D;;;;OAIG;IACI,mBAAmB,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,wBAAwB,GAAG,iBAAiB,EAAE,GAAG,IAAI;IA+F5G;;;;;OAKG;IACI,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,QAAQ,EAAE,iBAAiB,EAAE,GAAG,eAAe;IAgC3G;;;;OAIG;IACI,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO;IAOpE,kDAAkD;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIpC;;;;;;;OAOG;IACI,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAIrE;;;;;OAKG;IACI,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAItD,OAAO,CAAC,SAAS;IAgBV,aAAa,IAAI,IAAI;IAIrB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE;CAGtE;AAED,QAAA,MAAM,aAAa,eAA8B,CAAA;AACjD,eAAe,aAAa,CAAA;AAC5B,OAAO,EAAC,aAAa,EAAC,CAAA"}
1
+ {"version":3,"file":"SceneRenderer.d.ts","sourceRoot":"","sources":["../../src/services/SceneRenderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAQH,OAAO,EAKL,KAAK,YAAY,EAEjB,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EAEvB,MAAM,wBAAwB,CAAA;AAI/B,2DAA2D;AAC3D,MAAM,MAAM,YAAY,GAAG;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAC,CAAA;AAEvE,MAAM,MAAM,eAAe;AACzB,mCAAmC;AACjC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAC;AACvD,0GAA0G;GACxG;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAC;AACrF,uDAAuD;GACrD;IAAC,IAAI,EAAE,YAAY,CAAA;CAAC,CAAA;AAExB,cAAM,aAAa;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA6B;IACpD,OAAO,CAAC,KAAK,CAAmB;WAElB,WAAW,IAAI,aAAa;IAW1C;;;OAGG;IACI,mBAAmB,IAAI,wBAAwB,GAAG,IAAI;IAyB7D;;;;OAIG;IACI,mBAAmB,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,wBAAwB,GAAG,iBAAiB,EAAE,GAAG,IAAI;IA+F5G;;;;;OAKG;IACI,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,QAAQ,EAAE,iBAAiB,EAAE,GAAG,eAAe;IAgC3G;;;;OAIG;IACI,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO;IAOpE,kDAAkD;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIpC;;;;;;;OAOG;IACI,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAIrE;;;;;OAKG;IACI,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAItD,OAAO,CAAC,SAAS;IA+BV,aAAa,IAAI,IAAI;IAIrB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE;CAGtE;AAED,QAAA,MAAM,aAAa,eAA8B,CAAA;AACjD,eAAe,aAAa,CAAA;AAC5B,OAAO,EAAC,aAAa,EAAC,CAAA"}