@glassly/engine 0.1.0-dev.99 → 0.2.8-dev.445

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 (501) hide show
  1. package/README.md +28 -0
  2. package/build/engine.d.ts +32 -10
  3. package/build/engine.d.ts.map +1 -1
  4. package/build/engine.js +24 -16
  5. package/build/engine.js.map +1 -1
  6. package/build/facades/dev.d.ts +9 -0
  7. package/build/facades/dev.d.ts.map +1 -1
  8. package/build/facades/dev.js +9 -0
  9. package/build/facades/dev.js.map +1 -1
  10. package/build/facades/display.d.ts +10 -3
  11. package/build/facades/display.d.ts.map +1 -1
  12. package/build/facades/display.js +22 -8
  13. package/build/facades/display.js.map +1 -1
  14. package/build/facades/displayMirror.d.ts +11 -0
  15. package/build/facades/displayMirror.d.ts.map +1 -1
  16. package/build/facades/displayMirror.js +22 -0
  17. package/build/facades/displayMirror.js.map +1 -1
  18. package/build/facades/glasses.d.ts +31 -1
  19. package/build/facades/glasses.d.ts.map +1 -1
  20. package/build/facades/glasses.js +51 -0
  21. package/build/facades/glasses.js.map +1 -1
  22. package/build/facades/memory.d.ts +0 -6
  23. package/build/facades/memory.d.ts.map +1 -1
  24. package/build/facades/memory.js +1 -9
  25. package/build/facades/memory.js.map +1 -1
  26. package/build/facades/miniapps.d.ts +3 -1
  27. package/build/facades/miniapps.d.ts.map +1 -1
  28. package/build/facades/miniapps.js +3 -1
  29. package/build/facades/miniapps.js.map +1 -1
  30. package/build/facades/pairing.d.ts.map +1 -1
  31. package/build/facades/pairing.js +5 -4
  32. package/build/facades/pairing.js.map +1 -1
  33. package/build/facades/permissions.d.ts +2 -0
  34. package/build/facades/permissions.d.ts.map +1 -1
  35. package/build/facades/permissions.js +48 -0
  36. package/build/facades/permissions.js.map +1 -1
  37. package/build/facades/phoneNotifications.d.ts +20 -9
  38. package/build/facades/phoneNotifications.d.ts.map +1 -1
  39. package/build/facades/phoneNotifications.js +33 -26
  40. package/build/facades/phoneNotifications.js.map +1 -1
  41. package/build/facades/settings.d.ts.map +1 -1
  42. package/build/facades/settings.js +1 -2
  43. package/build/facades/settings.js.map +1 -1
  44. package/build/generated/releaseMetadata.js +4 -4
  45. package/build/generated/releaseMetadata.js.map +1 -1
  46. package/build/index.d.ts +6 -4
  47. package/build/index.d.ts.map +1 -1
  48. package/build/index.js +5 -4
  49. package/build/index.js.map +1 -1
  50. package/build/react/MentraLiveOtaFlow.js +2 -2
  51. package/build/react/MentraLiveOtaFlow.js.map +1 -1
  52. package/build/react/useMentraLiveOta.d.ts.map +1 -1
  53. package/build/react/useMentraLiveOta.js +8 -7
  54. package/build/react/useMentraLiveOta.js.map +1 -1
  55. package/build/runtime/config.d.ts +1 -1
  56. package/build/runtime/config.d.ts.map +1 -1
  57. package/build/runtime/config.js.map +1 -1
  58. package/build/services/AppRegistry.d.ts +4 -1
  59. package/build/services/AppRegistry.d.ts.map +1 -1
  60. package/build/services/AppRegistry.js +21 -1
  61. package/build/services/AppRegistry.js.map +1 -1
  62. package/build/services/BlobStore.d.ts +2 -2
  63. package/build/services/BlobStore.js +3 -3
  64. package/build/services/BlobStore.js.map +1 -1
  65. package/build/services/CloudClientService.d.ts +7 -3
  66. package/build/services/CloudClientService.d.ts.map +1 -1
  67. package/build/services/CloudClientService.js +34 -9
  68. package/build/services/CloudClientService.js.map +1 -1
  69. package/build/services/DevServerBridge.d.ts +1 -18
  70. package/build/services/DevServerBridge.d.ts.map +1 -1
  71. package/build/services/DevServerBridge.js +9 -8
  72. package/build/services/DevServerBridge.js.map +1 -1
  73. package/build/services/DeviceEventRouter.d.ts.map +1 -1
  74. package/build/services/DeviceEventRouter.js +58 -71
  75. package/build/services/DeviceEventRouter.js.map +1 -1
  76. package/build/services/DisplayDiagnostics.d.ts +5 -0
  77. package/build/services/DisplayDiagnostics.d.ts.map +1 -0
  78. package/build/services/DisplayDiagnostics.js +105 -0
  79. package/build/services/DisplayDiagnostics.js.map +1 -0
  80. package/build/services/DisplayProcessor.d.ts +6 -7
  81. package/build/services/DisplayProcessor.d.ts.map +1 -1
  82. package/build/services/DisplayProcessor.js +17 -45
  83. package/build/services/DisplayProcessor.js.map +1 -1
  84. package/build/services/GestureDotsService.d.ts +10 -4
  85. package/build/services/GestureDotsService.d.ts.map +1 -1
  86. package/build/services/GestureDotsService.js +70 -16
  87. package/build/services/GestureDotsService.js.map +1 -1
  88. package/build/services/GlassesKeyboardInput.d.ts +77 -0
  89. package/build/services/GlassesKeyboardInput.d.ts.map +1 -0
  90. package/build/services/GlassesKeyboardInput.js +261 -0
  91. package/build/services/GlassesKeyboardInput.js.map +1 -0
  92. package/build/services/GlassesSensorService.d.ts +47 -0
  93. package/build/services/GlassesSensorService.d.ts.map +1 -0
  94. package/build/services/GlassesSensorService.js +242 -0
  95. package/build/services/GlassesSensorService.js.map +1 -0
  96. package/build/services/GlassesTouchInput.d.ts +14 -0
  97. package/build/services/GlassesTouchInput.d.ts.map +1 -0
  98. package/build/services/GlassesTouchInput.js +107 -0
  99. package/build/services/GlassesTouchInput.js.map +1 -0
  100. package/build/services/HeadingService.d.ts +0 -6
  101. package/build/services/HeadingService.d.ts.map +1 -1
  102. package/build/services/HeadingService.js +0 -27
  103. package/build/services/HeadingService.js.map +1 -1
  104. package/build/services/HotspotShutdown.d.ts.map +1 -1
  105. package/build/services/HotspotShutdown.js +2 -1
  106. package/build/services/HotspotShutdown.js.map +1 -1
  107. package/build/services/JSCrashloopReportService.js +2 -2
  108. package/build/services/JSCrashloopReportService.js.map +1 -1
  109. package/build/services/JSRouter.d.ts +6 -4
  110. package/build/services/JSRouter.d.ts.map +1 -1
  111. package/build/services/JSRouter.js +15 -13
  112. package/build/services/JSRouter.js.map +1 -1
  113. package/build/services/LocalDisplayManager.d.ts +35 -7
  114. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  115. package/build/services/LocalDisplayManager.js +105 -27
  116. package/build/services/LocalDisplayManager.js.map +1 -1
  117. package/build/services/LocalMiniappDataMigration.d.ts +1 -1
  118. package/build/services/LocalMiniappDataMigration.js +1 -1
  119. package/build/services/LocalMiniappDataMigration.js.map +1 -1
  120. package/build/services/LocalMiniappRuntime.d.ts +64 -11
  121. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  122. package/build/services/LocalMiniappRuntime.js +441 -35
  123. package/build/services/LocalMiniappRuntime.js.map +1 -1
  124. package/build/services/LocalSttFallbackCoordinator.d.ts +3 -0
  125. package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
  126. package/build/services/LocalSttFallbackCoordinator.js +27 -8
  127. package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
  128. package/build/services/MemoryStore.d.ts +8 -33
  129. package/build/services/MemoryStore.d.ts.map +1 -1
  130. package/build/services/MemoryStore.js +30 -56
  131. package/build/services/MemoryStore.js.map +1 -1
  132. package/build/services/MiniappEngine.d.ts +3 -3
  133. package/build/services/MiniappEngine.js +7 -7
  134. package/build/services/MiniappEngine.js.map +1 -1
  135. package/build/services/MiniappLauncher.d.ts +8 -0
  136. package/build/services/MiniappLauncher.d.ts.map +1 -1
  137. package/build/services/MiniappLauncher.js +19 -1
  138. package/build/services/MiniappLauncher.js.map +1 -1
  139. package/build/services/MirrorService.d.ts +24 -0
  140. package/build/services/MirrorService.d.ts.map +1 -0
  141. package/build/services/MirrorService.js +132 -0
  142. package/build/services/MirrorService.js.map +1 -0
  143. package/build/services/NativeNavigationService.d.ts +2 -3
  144. package/build/services/NativeNavigationService.d.ts.map +1 -1
  145. package/build/services/NativeNavigationService.js +24 -21
  146. package/build/services/NativeNavigationService.js.map +1 -1
  147. package/build/services/NavigationHandlers.d.ts.map +1 -1
  148. package/build/services/NavigationHandlers.js +10 -5
  149. package/build/services/NavigationHandlers.js.map +1 -1
  150. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  151. package/build/services/OtaInstallCoordinator.js +24 -23
  152. package/build/services/OtaInstallCoordinator.js.map +1 -1
  153. package/build/services/PairedGlassesLatch.d.ts +7 -0
  154. package/build/services/PairedGlassesLatch.d.ts.map +1 -0
  155. package/build/services/PairedGlassesLatch.js +38 -0
  156. package/build/services/PairedGlassesLatch.js.map +1 -0
  157. package/build/services/PhoneActivity.d.ts +23 -0
  158. package/build/services/PhoneActivity.d.ts.map +1 -0
  159. package/build/services/PhoneActivity.js +111 -0
  160. package/build/services/PhoneActivity.js.map +1 -0
  161. package/build/services/PhoneCameraFovCoordinator.d.ts.map +1 -1
  162. package/build/services/PhoneCameraFovCoordinator.js +4 -3
  163. package/build/services/PhoneCameraFovCoordinator.js.map +1 -1
  164. package/build/services/PhoneNotificationsSync.d.ts +40 -6
  165. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  166. package/build/services/PhoneNotificationsSync.js +160 -84
  167. package/build/services/PhoneNotificationsSync.js.map +1 -1
  168. package/build/services/PhonePhotoCoordinator.d.ts.map +1 -1
  169. package/build/services/PhonePhotoCoordinator.js +6 -5
  170. package/build/services/PhonePhotoCoordinator.js.map +1 -1
  171. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  172. package/build/services/PhoneStreamCoordinator.js +8 -7
  173. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  174. package/build/services/STTModelManager.d.ts +3 -0
  175. package/build/services/STTModelManager.d.ts.map +1 -1
  176. package/build/services/STTModelManager.js +15 -1
  177. package/build/services/STTModelManager.js.map +1 -1
  178. package/build/services/SceneRenderer.d.ts +43 -2
  179. package/build/services/SceneRenderer.d.ts.map +1 -1
  180. package/build/services/SceneRenderer.js +123 -8
  181. package/build/services/SceneRenderer.js.map +1 -1
  182. package/build/services/StreamLifecycleController.d.ts.map +1 -1
  183. package/build/services/StreamLifecycleController.js +6 -14
  184. package/build/services/StreamLifecycleController.js.map +1 -1
  185. package/build/services/TTSModelManager.js +1 -1
  186. package/build/services/TTSModelManager.js.map +1 -1
  187. package/build/services/UIRouter.d.ts +2 -2
  188. package/build/services/UIRouter.js +5 -5
  189. package/build/services/UIRouter.js.map +1 -1
  190. package/build/services/asg/galleryPathMigration.js +1 -1
  191. package/build/services/asg/galleryPathMigration.js.map +1 -1
  192. package/build/services/asg/gallerySyncService.js +2 -2
  193. package/build/services/asg/gallerySyncService.js.map +1 -1
  194. package/build/services/asg/localStorageService.d.ts.map +1 -1
  195. package/build/services/asg/localStorageService.js +3 -3
  196. package/build/services/asg/localStorageService.js.map +1 -1
  197. package/build/services/debug/DashboardOpenTrace.d.ts +32 -0
  198. package/build/services/debug/DashboardOpenTrace.d.ts.map +1 -0
  199. package/build/services/debug/DashboardOpenTrace.js +128 -0
  200. package/build/services/debug/DashboardOpenTrace.js.map +1 -0
  201. package/build/services/debug/dashboardOpenTimeline.d.ts +77 -0
  202. package/build/services/debug/dashboardOpenTimeline.d.ts.map +1 -0
  203. package/build/services/debug/dashboardOpenTimeline.js +195 -0
  204. package/build/services/debug/dashboardOpenTimeline.js.map +1 -0
  205. package/build/services/effectiveCapabilities.d.ts.map +1 -1
  206. package/build/services/effectiveCapabilities.js +25 -13
  207. package/build/services/effectiveCapabilities.js.map +1 -1
  208. package/build/services/gestureClaims.d.ts +34 -15
  209. package/build/services/gestureClaims.d.ts.map +1 -1
  210. package/build/services/gestureClaims.js +42 -21
  211. package/build/services/gestureClaims.js.map +1 -1
  212. package/build/services/manifestFields.d.ts +9 -0
  213. package/build/services/manifestFields.d.ts.map +1 -0
  214. package/build/services/manifestFields.js +26 -0
  215. package/build/services/manifestFields.js.map +1 -0
  216. package/build/services/memoryStoreInstance.d.ts +0 -10
  217. package/build/services/memoryStoreInstance.d.ts.map +1 -1
  218. package/build/services/memoryStoreInstance.js +2 -31
  219. package/build/services/memoryStoreInstance.js.map +1 -1
  220. package/build/services/mirrorSnapshot.d.ts +16 -0
  221. package/build/services/mirrorSnapshot.d.ts.map +1 -0
  222. package/build/services/mirrorSnapshot.js +102 -0
  223. package/build/services/mirrorSnapshot.js.map +1 -0
  224. package/build/services/nativeNavigationGate.d.ts +8 -0
  225. package/build/services/nativeNavigationGate.d.ts.map +1 -0
  226. package/build/services/nativeNavigationGate.js +12 -0
  227. package/build/services/nativeNavigationGate.js.map +1 -0
  228. package/build/services/notificationAppCatalog.d.ts +7 -0
  229. package/build/services/notificationAppCatalog.d.ts.map +1 -0
  230. package/build/services/notificationAppCatalog.js +16 -0
  231. package/build/services/notificationAppCatalog.js.map +1 -0
  232. package/build/services/notifications/notificationPolicy.d.ts +55 -0
  233. package/build/services/notifications/notificationPolicy.d.ts.map +1 -0
  234. package/build/services/notifications/notificationPolicy.js +41 -0
  235. package/build/services/notifications/notificationPolicy.js.map +1 -0
  236. package/build/services/pages/OverlayPages.d.ts +26 -0
  237. package/build/services/pages/OverlayPages.d.ts.map +1 -0
  238. package/build/services/pages/OverlayPages.js +80 -0
  239. package/build/services/pages/OverlayPages.js.map +1 -0
  240. package/build/services/pages/PageHost.d.ts +32 -0
  241. package/build/services/pages/PageHost.d.ts.map +1 -0
  242. package/build/services/pages/PageHost.js +99 -0
  243. package/build/services/pages/PageHost.js.map +1 -0
  244. package/build/services/pages/overlayApps.d.ts +25 -0
  245. package/build/services/pages/overlayApps.d.ts.map +1 -0
  246. package/build/services/pages/overlayApps.js +66 -0
  247. package/build/services/pages/overlayApps.js.map +1 -0
  248. package/build/services/pages/pageEligibility.d.ts +9 -0
  249. package/build/services/pages/pageEligibility.d.ts.map +1 -0
  250. package/build/services/pages/pageEligibility.js +27 -0
  251. package/build/services/pages/pageEligibility.js.map +1 -0
  252. package/build/services/settingsBridge.d.ts +58 -0
  253. package/build/services/settingsBridge.d.ts.map +1 -0
  254. package/build/services/settingsBridge.js +90 -0
  255. package/build/services/settingsBridge.js.map +1 -0
  256. package/build/services/speakerProfileStoreInstance.js +1 -1
  257. package/build/services/speakerProfileStoreInstance.js.map +1 -1
  258. package/build/services/uiShim.d.ts +1 -1
  259. package/build/services/uiShim.js +4 -4
  260. package/build/services/uiShim.js.map +1 -1
  261. package/build/stores/appVisibility.d.ts +28 -0
  262. package/build/stores/appVisibility.d.ts.map +1 -1
  263. package/build/stores/appVisibility.js +34 -0
  264. package/build/stores/appVisibility.js.map +1 -1
  265. package/build/stores/apps.d.ts +13 -0
  266. package/build/stores/apps.d.ts.map +1 -1
  267. package/build/stores/apps.js +66 -12
  268. package/build/stores/apps.js.map +1 -1
  269. package/build/stores/bluetoothSettingKeys.js +1 -1
  270. package/build/stores/bluetoothSettingKeys.js.map +1 -1
  271. package/build/stores/display.d.ts +3 -3
  272. package/build/stores/display.d.ts.map +1 -1
  273. package/build/stores/display.js +16 -4
  274. package/build/stores/display.js.map +1 -1
  275. package/build/stores/glasses.d.ts.map +1 -1
  276. package/build/stores/glasses.js +14 -1
  277. package/build/stores/glasses.js.map +1 -1
  278. package/build/stores/settings.d.ts +1 -0
  279. package/build/stores/settings.d.ts.map +1 -1
  280. package/build/stores/settings.js +170 -44
  281. package/build/stores/settings.js.map +1 -1
  282. package/build/types/applet.d.ts +14 -1
  283. package/build/types/applet.d.ts.map +1 -1
  284. package/build/types/applet.js.map +1 -1
  285. package/build/types/capabilities/even-realities-g2.d.ts.map +1 -1
  286. package/build/types/capabilities/even-realities-g2.js +5 -0
  287. package/build/types/capabilities/even-realities-g2.js.map +1 -1
  288. package/build/types/capabilities/simulated-glasses.d.ts +1 -1
  289. package/build/types/capabilities/simulated-glasses.d.ts.map +1 -1
  290. package/build/types/capabilities/simulated-glasses.js +7 -4
  291. package/build/types/capabilities/simulated-glasses.js.map +1 -1
  292. package/build/types/hardware.d.ts +20 -1
  293. package/build/types/hardware.d.ts.map +1 -1
  294. package/build/types/hardware.js +2 -0
  295. package/build/types/hardware.js.map +1 -1
  296. package/build/types/index.d.ts +1 -0
  297. package/build/types/index.d.ts.map +1 -1
  298. package/build/types/index.js +1 -0
  299. package/build/types/index.js.map +1 -1
  300. package/build/utils/debouncedPatch.d.ts +3 -1
  301. package/build/utils/debouncedPatch.d.ts.map +1 -1
  302. package/build/utils/debouncedPatch.js +6 -3
  303. package/build/utils/debouncedPatch.js.map +1 -1
  304. package/build/utils/devMiniappLaunch.d.ts.map +1 -1
  305. package/build/utils/devMiniappLaunch.js +4 -3
  306. package/build/utils/devMiniappLaunch.js.map +1 -1
  307. package/build/utils/display/measurer/TextMeasurer.d.ts +2 -159
  308. package/build/utils/display/measurer/TextMeasurer.d.ts.map +1 -1
  309. package/build/utils/display/measurer/TextMeasurer.js +2 -272
  310. package/build/utils/display/measurer/TextMeasurer.js.map +1 -1
  311. package/build/utils/display/measurer/script-detection.d.ts +1 -52
  312. package/build/utils/display/measurer/script-detection.d.ts.map +1 -1
  313. package/build/utils/display/measurer/script-detection.js +2 -183
  314. package/build/utils/display/measurer/script-detection.js.map +1 -1
  315. package/build/utils/display/pixelArt.d.ts +3 -0
  316. package/build/utils/display/pixelArt.d.ts.map +1 -0
  317. package/build/utils/display/pixelArt.js +3 -0
  318. package/build/utils/display/pixelArt.js.map +1 -0
  319. package/build/utils/display/profiles/g1.d.ts +1 -32
  320. package/build/utils/display/profiles/g1.d.ts.map +1 -1
  321. package/build/utils/display/profiles/g1.js +2 -212
  322. package/build/utils/display/profiles/g1.js.map +1 -1
  323. package/build/utils/display/profiles/g2.d.ts +1 -12
  324. package/build/utils/display/profiles/g2.d.ts.map +1 -1
  325. package/build/utils/display/profiles/g2.js +2 -33
  326. package/build/utils/display/profiles/g2.js.map +1 -1
  327. package/build/utils/display/profiles/nex.d.ts +1 -9
  328. package/build/utils/display/profiles/nex.d.ts.map +1 -1
  329. package/build/utils/display/profiles/nex.js +2 -149
  330. package/build/utils/display/profiles/nex.js.map +1 -1
  331. package/build/utils/display/profiles/types.d.ts +1 -102
  332. package/build/utils/display/profiles/types.d.ts.map +1 -1
  333. package/build/utils/display/profiles/types.js.map +1 -1
  334. package/build/utils/display/profiles/z100.d.ts +1 -32
  335. package/build/utils/display/profiles/z100.d.ts.map +1 -1
  336. package/build/utils/display/profiles/z100.js +2 -181
  337. package/build/utils/display/profiles/z100.js.map +1 -1
  338. package/build/utils/display/scene/degrade.d.ts.map +1 -1
  339. package/build/utils/display/scene/degrade.js +4 -1
  340. package/build/utils/display/scene/degrade.js.map +1 -1
  341. package/build/utils/display/scene/differ.d.ts +13 -1
  342. package/build/utils/display/scene/differ.d.ts.map +1 -1
  343. package/build/utils/display/scene/differ.js +22 -0
  344. package/build/utils/display/scene/differ.js.map +1 -1
  345. package/build/utils/display/scene/index.d.ts +1 -1
  346. package/build/utils/display/scene/index.d.ts.map +1 -1
  347. package/build/utils/display/scene/index.js.map +1 -1
  348. package/build/utils/display/scene/process.d.ts.map +1 -1
  349. package/build/utils/display/scene/process.js +183 -11
  350. package/build/utils/display/scene/process.js.map +1 -1
  351. package/build/utils/display/scene/store.d.ts +1 -1
  352. package/build/utils/display/scene/store.d.ts.map +1 -1
  353. package/build/utils/display/scene/store.js +0 -0
  354. package/build/utils/display/scene/store.js.map +1 -1
  355. package/build/utils/display/scene/svgPath.d.ts +9 -0
  356. package/build/utils/display/scene/svgPath.d.ts.map +1 -0
  357. package/build/utils/display/scene/svgPath.js +167 -0
  358. package/build/utils/display/scene/svgPath.js.map +1 -0
  359. package/build/utils/display/scene/types.d.ts +2 -142
  360. package/build/utils/display/scene/types.d.ts.map +1 -1
  361. package/build/utils/display/scene/types.js +0 -0
  362. package/build/utils/display/scene/types.js.map +1 -1
  363. package/build/utils/display/wrapper/TextWrapper.d.ts +1 -101
  364. package/build/utils/display/wrapper/TextWrapper.d.ts.map +1 -1
  365. package/build/utils/display/wrapper/TextWrapper.js +2 -470
  366. package/build/utils/display/wrapper/TextWrapper.js.map +1 -1
  367. package/build/utils/display/wrapper/types.d.ts +2 -74
  368. package/build/utils/display/wrapper/types.d.ts.map +1 -1
  369. package/build/utils/display/wrapper/types.js +2 -10
  370. package/build/utils/display/wrapper/types.js.map +1 -1
  371. package/build/utils/hardware/hardware.js +1 -1
  372. package/build/utils/hardware/hardware.js.map +1 -1
  373. package/build/utils/timers/timers.d.ts.map +1 -1
  374. package/build/utils/timers/timers.js +26 -4
  375. package/build/utils/timers/timers.js.map +1 -1
  376. package/package.json +8 -8
  377. package/src/engine.ts +24 -15
  378. package/src/facades/dev.ts +10 -0
  379. package/src/facades/display.ts +21 -8
  380. package/src/facades/displayMirror.ts +29 -0
  381. package/src/facades/glasses.ts +61 -1
  382. package/src/facades/memory.ts +1 -12
  383. package/src/facades/miniapps.ts +4 -1
  384. package/src/facades/pairing.ts +8 -6
  385. package/src/facades/permissions.ts +46 -0
  386. package/src/facades/phoneNotifications.ts +45 -26
  387. package/src/facades/settings.ts +1 -2
  388. package/src/generated/releaseMetadata.ts +4 -4
  389. package/src/index.ts +6 -9
  390. package/src/react/MentraLiveOtaFlow.tsx +2 -2
  391. package/src/react/useMentraLiveOta.ts +14 -8
  392. package/src/runtime/config.ts +1 -1
  393. package/src/services/AppRegistry.ts +43 -7
  394. package/src/services/BlobStore.ts +3 -3
  395. package/src/services/CloudClientService.ts +37 -10
  396. package/src/services/DevServerBridge.ts +12 -10
  397. package/src/services/DeviceEventRouter.ts +73 -74
  398. package/src/services/DisplayDiagnostics.ts +107 -0
  399. package/src/services/DisplayProcessor.ts +20 -55
  400. package/src/services/GestureDotsService.ts +77 -16
  401. package/src/services/GlassesKeyboardInput.ts +306 -0
  402. package/src/services/GlassesSensorService.ts +233 -0
  403. package/src/services/GlassesTouchInput.ts +128 -0
  404. package/src/services/HeadingService.ts +0 -24
  405. package/src/services/HotspotShutdown.ts +3 -1
  406. package/src/services/JSCrashloopReportService.ts +2 -2
  407. package/src/services/JSRouter.ts +29 -14
  408. package/src/services/LocalDisplayManager.ts +112 -26
  409. package/src/services/LocalMiniappDataMigration.ts +1 -1
  410. package/src/services/LocalMiniappRuntime.ts +500 -43
  411. package/src/services/LocalSttFallbackCoordinator.ts +28 -7
  412. package/src/services/MemoryStore.ts +32 -80
  413. package/src/services/MiniappEngine.ts +7 -7
  414. package/src/services/MiniappLauncher.ts +25 -1
  415. package/src/services/MirrorService.ts +139 -0
  416. package/src/services/NativeNavigationService.ts +29 -25
  417. package/src/services/NavigationHandlers.ts +10 -5
  418. package/src/services/OtaInstallCoordinator.ts +36 -34
  419. package/src/services/PairedGlassesLatch.ts +39 -0
  420. package/src/services/PhoneActivity.ts +116 -0
  421. package/src/services/PhoneCameraFovCoordinator.ts +6 -4
  422. package/src/services/PhoneNotificationsSync.ts +185 -103
  423. package/src/services/PhonePhotoCoordinator.ts +9 -10
  424. package/src/services/PhoneStreamCoordinator.ts +13 -11
  425. package/src/services/STTModelManager.ts +15 -1
  426. package/src/services/SceneRenderer.ts +148 -10
  427. package/src/services/StreamLifecycleController.ts +9 -7
  428. package/src/services/TTSModelManager.ts +1 -1
  429. package/src/services/UIRouter.ts +5 -5
  430. package/src/services/asg/galleryPathMigration.ts +1 -1
  431. package/src/services/asg/gallerySyncService.ts +2 -2
  432. package/src/services/asg/localStorageService.ts +3 -3
  433. package/src/services/debug/DashboardOpenTrace.ts +153 -0
  434. package/src/services/debug/dashboardOpenTimeline.ts +237 -0
  435. package/src/services/effectiveCapabilities.ts +32 -13
  436. package/src/services/gestureClaims.ts +72 -23
  437. package/src/services/manifestFields.ts +23 -0
  438. package/src/services/memoryStoreInstance.ts +2 -34
  439. package/src/services/mirrorSnapshot.ts +116 -0
  440. package/src/services/nativeNavigationGate.ts +14 -0
  441. package/src/services/notificationAppCatalog.ts +21 -0
  442. package/src/services/notifications/notificationPolicy.ts +87 -0
  443. package/src/services/pages/OverlayPages.ts +86 -0
  444. package/src/services/pages/PageHost.ts +116 -0
  445. package/src/services/pages/overlayApps.ts +72 -0
  446. package/src/services/pages/pageEligibility.ts +30 -0
  447. package/src/services/settingsBridge.ts +111 -0
  448. package/src/services/speakerProfileStoreInstance.ts +1 -1
  449. package/src/services/uiShim.ts +4 -4
  450. package/src/stores/appVisibility.ts +45 -0
  451. package/src/stores/apps.ts +110 -32
  452. package/src/stores/bluetoothSettingKeys.ts +1 -1
  453. package/src/stores/display.ts +18 -7
  454. package/src/stores/glasses.ts +15 -1
  455. package/src/stores/settings.ts +175 -51
  456. package/src/types/applet.ts +14 -1
  457. package/src/types/capabilities/even-realities-g2.ts +6 -0
  458. package/src/types/capabilities/simulated-glasses.ts +8 -4
  459. package/src/types/hardware.ts +34 -1
  460. package/src/types/index.ts +1 -0
  461. package/src/utils/debouncedPatch.ts +8 -4
  462. package/src/utils/devMiniappLaunch.ts +5 -3
  463. package/src/utils/display/measurer/TextMeasurer.ts +3 -346
  464. package/src/utils/display/measurer/script-detection.ts +10 -218
  465. package/src/utils/display/pixelArt.ts +3 -0
  466. package/src/utils/display/profiles/g1.ts +2 -236
  467. package/src/utils/display/profiles/g2.ts +2 -39
  468. package/src/utils/display/profiles/nex.ts +2 -166
  469. package/src/utils/display/profiles/types.ts +9 -132
  470. package/src/utils/display/profiles/z100.ts +2 -198
  471. package/src/utils/display/scene/degrade.ts +4 -1
  472. package/src/utils/display/scene/differ.ts +25 -1
  473. package/src/utils/display/scene/index.ts +5 -0
  474. package/src/utils/display/scene/process.ts +196 -13
  475. package/src/utils/display/scene/store.ts +0 -0
  476. package/src/utils/display/scene/svgPath.ts +163 -0
  477. package/src/utils/display/scene/types.ts +0 -0
  478. package/src/utils/display/wrapper/TextWrapper.ts +2 -546
  479. package/src/utils/display/wrapper/types.ts +3 -102
  480. package/src/utils/hardware/hardware.ts +1 -1
  481. package/src/utils/timers/timers.ts +32 -4
  482. package/build/services/CustomDashboardModel.d.ts +0 -48
  483. package/build/services/CustomDashboardModel.d.ts.map +0 -1
  484. package/build/services/CustomDashboardModel.js +0 -73
  485. package/build/services/CustomDashboardModel.js.map +0 -1
  486. package/build/services/CustomDashboardService.d.ts +0 -63
  487. package/build/services/CustomDashboardService.d.ts.map +0 -1
  488. package/build/services/CustomDashboardService.js +0 -576
  489. package/build/services/CustomDashboardService.js.map +0 -1
  490. package/build/services/DashboardCoordinator.d.ts +0 -26
  491. package/build/services/DashboardCoordinator.d.ts.map +0 -1
  492. package/build/services/DashboardCoordinator.js +0 -125
  493. package/build/services/DashboardCoordinator.js.map +0 -1
  494. package/build/services/iosNotificationAppCatalog.d.ts +0 -13
  495. package/build/services/iosNotificationAppCatalog.d.ts.map +0 -1
  496. package/build/services/iosNotificationAppCatalog.js +0 -28
  497. package/build/services/iosNotificationAppCatalog.js.map +0 -1
  498. package/src/services/CustomDashboardModel.ts +0 -83
  499. package/src/services/CustomDashboardService.ts +0 -598
  500. package/src/services/DashboardCoordinator.ts +0 -121
  501. package/src/services/iosNotificationAppCatalog.ts +0 -39
@@ -33,6 +33,7 @@ import { useGlassesStore } from "../stores/glasses";
33
33
  import { StreamLifecycleController } from "./StreamLifecycleController";
34
34
  import { slimStreamStatusEvent, streamStatusSignature } from "./slimStreamStatus";
35
35
  import { getManagedStreamStatus, provisionManagedStream, teardownManagedStream, } from "./cloudStreamApi";
36
+ import { BgTimer } from "../utils/timers";
36
37
  /**
37
38
  * Default cadence + thresholds. Exposed via {@link CoordinatorTimings} so tests
38
39
  * can shorten them; production code should never override these.
@@ -449,7 +450,7 @@ export class PhoneStreamCoordinator {
449
450
  const remainingMs = Math.max(0, connectTimeoutMs - elapsedMs);
450
451
  const startupDelayMs = Math.min(this.timings.cloudflareStatusPollMs, this.timings.cloudflareStartupPollInitialMs * 2 ** Math.min(Math.max(0, entry.cloudflareAttempts - 1), 10));
451
452
  const delayMs = waitingForWebRtc ? Math.min(startupDelayMs, remainingMs) : this.timings.cloudflareStatusPollMs;
452
- entry.cloudflareTimer = setTimeout(() => void poll(), delayMs);
453
+ entry.cloudflareTimer = BgTimer.setTimeout(() => void poll(), delayMs);
453
454
  };
454
455
  const poll = async () => {
455
456
  if (this.current !== entry)
@@ -570,7 +571,7 @@ export class PhoneStreamCoordinator {
570
571
  elapsedMs: Date.now() - entry.startupStartedAtMs,
571
572
  });
572
573
  if (entry.hlsTimer) {
573
- clearInterval(entry.hlsTimer);
574
+ BgTimer.clearInterval(entry.hlsTimer);
574
575
  entry.hlsTimer = undefined;
575
576
  }
576
577
  const result = managedStartResult(entry);
@@ -592,7 +593,7 @@ export class PhoneStreamCoordinator {
592
593
  }
593
594
  if (entry.hlsAttempts >= this.timings.hlsReadinessMaxAttempts) {
594
595
  if (entry.hlsTimer) {
595
- clearInterval(entry.hlsTimer);
596
+ BgTimer.clearInterval(entry.hlsTimer);
596
597
  entry.hlsTimer = undefined;
597
598
  }
598
599
  const err = new Error(`HLS playback URL did not become ready after ${this.timings.hlsReadinessMaxAttempts} attempts`);
@@ -614,11 +615,11 @@ export class PhoneStreamCoordinator {
614
615
  });
615
616
  }
616
617
  };
617
- setTimeout(() => {
618
+ BgTimer.setTimeout(() => {
618
619
  // Guard: stream may have been torn down during the initial delay.
619
620
  if (this.current !== entry)
620
621
  return;
621
- entry.hlsTimer = setInterval(tick, this.timings.hlsReadinessPollMs);
622
+ entry.hlsTimer = BgTimer.setInterval(tick, this.timings.hlsReadinessPollMs);
622
623
  }, this.timings.hlsReadinessInitialDelayMs);
623
624
  }
624
625
  fanout(update) {
@@ -655,9 +656,9 @@ export class PhoneStreamCoordinator {
655
656
  this.lifecycle = null;
656
657
  if (entry.kind === "managed") {
657
658
  if (entry.cloudflareTimer)
658
- clearTimeout(entry.cloudflareTimer);
659
+ BgTimer.clearTimeout(entry.cloudflareTimer);
659
660
  if (entry.hlsTimer)
660
- clearInterval(entry.hlsTimer);
661
+ BgTimer.clearInterval(entry.hlsTimer);
661
662
  // Reject any still-pending HLS readiness waiters.
662
663
  const pendingErr = new Error(`Stream torn down: ${reason}`);
663
664
  for (const reject of entry.hlsReadyRejecters)
@@ -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 \"@glassly/bluetooth-sdk/internal\"\nimport type {\n KeepAliveAckEvent,\n StreamResolvedConfig,\n StreamStartRequest,\n StreamStatusEvent,\n} from \"@glassly/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(\"GLASSES_NOT_CONNECTED\", \"Glasses are not connected\", \"command\", \"ble\")\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(packageName: string, opts: StartUnmanagedOptions): 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(packageName: string, opts: StartManagedOptions): 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 ? managedStartResult(existing) : 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\"] = 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 = this.current.kind === \"managed\" ? Array.from(this.current.subscribers) : [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 }\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 if (entry.kind === \"managed\") {\n // Start remote cleanup only after the publisher has stopped, but do not\n // hold the local transition lock on an unbounded network request. The\n // runtime's durable cleanup queue owns retries if this request fails or\n // the app exits before Cloudflare finishes finalizing the recording.\n void teardownManagedStream(entry.liveInputId).catch((err) => {\n console.warn(\"[STREAM] teardownManagedStream failed:\", err)\n })\n }\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"]}
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 \"@glassly/bluetooth-sdk/internal\"\nimport type {\n KeepAliveAckEvent,\n StreamResolvedConfig,\n StreamStartRequest,\n StreamStatusEvent,\n} from \"@glassly/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\nimport {BgTimer} from \"../utils/timers\"\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?: number\n hlsTimer?: number\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(\"GLASSES_NOT_CONNECTED\", \"Glasses are not connected\", \"command\", \"ble\")\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(packageName: string, opts: StartUnmanagedOptions): 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(packageName: string, opts: StartManagedOptions): 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 ? managedStartResult(existing) : 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\"] = 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 = BgTimer.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 BgTimer.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 BgTimer.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 BgTimer.setTimeout(() => {\n // Guard: stream may have been torn down during the initial delay.\n if (this.current !== entry) return\n entry.hlsTimer = BgTimer.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 = this.current.kind === \"managed\" ? Array.from(this.current.subscribers) : [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) BgTimer.clearTimeout(entry.cloudflareTimer)\n if (entry.hlsTimer) BgTimer.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 }\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 if (entry.kind === \"managed\") {\n // Start remote cleanup only after the publisher has stopped, but do not\n // hold the local transition lock on an unbounded network request. The\n // runtime's durable cleanup queue owns retries if this request fails or\n // the app exits before Cloudflare finishes finalizing the recording.\n void teardownManagedStream(entry.liveInputId).catch((err) => {\n console.warn(\"[STREAM] teardownManagedStream failed:\", err)\n })\n }\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,7 @@ declare class STTModelManager {
31
31
  private static instance;
32
32
  private downloadJobId?;
33
33
  private currentLanguage;
34
+ private modelReadyListeners;
34
35
  private modelBaseUrl;
35
36
  private languages;
36
37
  private constructor();
@@ -52,6 +53,8 @@ declare class STTModelManager {
52
53
  deleteModel(code?: string): Promise<void>;
53
54
  activateLanguage(code: string): Promise<void>;
54
55
  private setNativeModelPath;
56
+ /** Fired after a downloaded/selected model's path has reached native. */
57
+ onModelReady(listener: () => void): () => void;
55
58
  getStorageInfo(): Promise<{
56
59
  free: number;
57
60
  total: number;
@@ -1 +1 @@
1
- {"version":3,"file":"STTModelManager.d.ts","sourceRoot":"","sources":["../../src/services/STTModelManager.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,YAAY,GAAG,KAAK,GAAG,UAAU,CAAA;CACxC;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,YAAY,GAAG,KAAK,GAAG,UAAU,CAAA;IACvC,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,YAAY,EAAE,MAAM,CAAA;CACrB;AAID,cAAM,eAAe;IACnB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAiB;IACxC,OAAO,CAAC,aAAa,CAAC,CAAQ;IAC9B,OAAO,CAAC,eAAe,CAAmB;IAC1C,OAAO,CAAC,YAAY,CAAwE;IAE5F,OAAO,CAAC,SAAS,CAiIhB;IAED,OAAO;IAEP,MAAM,CAAC,WAAW,IAAI,eAAe;IAO/B,iCAAiC,IAAI,OAAO,CAAC,MAAM,CAAC;IAe1D,kBAAkB,IAAI,MAAM;IAI5B,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAMtC,mBAAmB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;IAK1C,qBAAqB,IAAI,cAAc,EAAE;IAIzC,iBAAiB,IAAI,MAAM;IAK3B,YAAY,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;IAKnC,wEAAwE;IACxE,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAInC,gBAAgB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAuBjD,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAqBrD,kBAAkB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAQ7C,aAAa,CACjB,IAAI,CAAC,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,EACjD,oBAAoB,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,GAC5D,OAAO,CAAC,IAAI,CAAC;IAqFV,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAO/B,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQzC,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAgBrC,kBAAkB;IAI1B,cAAc,IAAI,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC;IAQ9D,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIlC,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAO1C;AAED,QAAA,MAAM,QAAQ,iBAAgC,CAAA;AAC9C,OAAO,EAAC,eAAe,EAAC,CAAA;AACxB,eAAe,QAAQ,CAAA"}
1
+ {"version":3,"file":"STTModelManager.d.ts","sourceRoot":"","sources":["../../src/services/STTModelManager.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,YAAY,GAAG,KAAK,GAAG,UAAU,CAAA;CACxC;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,YAAY,GAAG,KAAK,GAAG,UAAU,CAAA;IACvC,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,YAAY,EAAE,MAAM,CAAA;CACrB;AAID,cAAM,eAAe;IACnB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAiB;IACxC,OAAO,CAAC,aAAa,CAAC,CAAQ;IAC9B,OAAO,CAAC,eAAe,CAAmB;IAC1C,OAAO,CAAC,mBAAmB,CAAwB;IACnD,OAAO,CAAC,YAAY,CAAwE;IAE5F,OAAO,CAAC,SAAS,CAiIhB;IAED,OAAO;IAEP,MAAM,CAAC,WAAW,IAAI,eAAe;IAO/B,iCAAiC,IAAI,OAAO,CAAC,MAAM,CAAC;IAe1D,kBAAkB,IAAI,MAAM;IAI5B,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAMtC,mBAAmB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;IAK1C,qBAAqB,IAAI,cAAc,EAAE;IAIzC,iBAAiB,IAAI,MAAM;IAK3B,YAAY,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;IAKnC,wEAAwE;IACxE,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAInC,gBAAgB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAuBjD,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAqBrD,kBAAkB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAQ7C,aAAa,CACjB,IAAI,CAAC,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,EACjD,oBAAoB,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,GAC5D,OAAO,CAAC,IAAI,CAAC;IAqFV,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAO/B,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQzC,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAgBrC,kBAAkB;IAWhC,yEAAyE;IACzE,YAAY,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAKxC,cAAc,IAAI,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC;IAQ9D,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIlC,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAO1C;AAED,QAAA,MAAM,QAAQ,iBAAgC,CAAA;AAC9C,OAAO,EAAC,eAAe,EAAC,CAAA;AACxB,eAAe,QAAQ,CAAA"}
@@ -6,6 +6,7 @@ class STTModelManager {
6
6
  static instance;
7
7
  downloadJobId;
8
8
  currentLanguage = DEFAULT_LANGUAGE;
9
+ modelReadyListeners = new Set();
9
10
  modelBaseUrl = "https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/";
10
11
  languages = {
11
12
  "en": {
@@ -339,7 +340,20 @@ class STTModelManager {
339
340
  await this.setNativeModelPath(modelPath, language.languageCode);
340
341
  }
341
342
  async setNativeModelPath(path, languageCode) {
342
- BluetoothSdk.setSttModelDetails(path, languageCode);
343
+ await BluetoothSdk.setSttModelDetails(path, languageCode);
344
+ for (const listener of this.modelReadyListeners) {
345
+ try {
346
+ listener();
347
+ }
348
+ catch (error) {
349
+ console.warn("STTModelManager: model-ready listener failed:", error);
350
+ }
351
+ }
352
+ }
353
+ /** Fired after a downloaded/selected model's path has reached native. */
354
+ onModelReady(listener) {
355
+ this.modelReadyListeners.add(listener);
356
+ return () => this.modelReadyListeners.delete(listener);
343
357
  }
344
358
  async getStorageInfo() {
345
359
  const fsInfo = await RNFS.getFSInfo();
@@ -1 +1 @@
1
- {"version":3,"file":"STTModelManager.js","sourceRoot":"","sources":["../../src/services/STTModelManager.ts"],"names":[],"mappings":"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","sourcesContent":["import BluetoothSdk from \"@glassly/bluetooth-sdk\"\nimport {Platform} from \"react-native\"\nimport * as RNFS from \"@dr.pogodin/react-native-fs\"\n\nexport interface LanguageInfo {\n code: string\n displayName: string\n size: number\n language: string\n downloaded: boolean\n path?: string\n type: \"transducer\" | \"ctc\" | \"nemotron\"\n}\n\nexport interface DownloadProgress {\n jobId: number\n bytesWritten: number\n contentLength: number\n percentage: number\n}\n\nexport interface ExtractionProgress {\n percentage: number\n currentFile?: string\n}\n\nexport interface LanguageConfig {\n code: string\n displayName: string\n fileName: string\n downloadUrl?: string\n size: number\n type: \"transducer\" | \"ctc\" | \"nemotron\"\n requiredFiles: string[]\n languageCode: string\n}\n\nconst DEFAULT_LANGUAGE = \"en\"\n\nclass STTModelManager {\n private static instance: STTModelManager\n private downloadJobId?: number\n private currentLanguage = DEFAULT_LANGUAGE\n private modelBaseUrl = \"https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/\"\n\n private languages: Record<string, LanguageConfig> = {\n \"en\": {\n code: \"en\",\n displayName: \"English\",\n fileName: \"sherpa-onnx-streaming-zipformer-en-2023-06-21-mobile\",\n size: 349 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\"encoder.onnx\", \"decoder.onnx\", \"joiner.onnx\", \"tokens.txt\"],\n languageCode: \"en-US\",\n },\n \"fr\": {\n code: \"fr\",\n displayName: \"Français\",\n fileName: \"sherpa-onnx-streaming-zipformer-fr-kroko-2025-08-06\",\n size: 57 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\"encoder.onnx\", \"decoder.onnx\", \"joiner.onnx\", \"tokens.txt\"],\n languageCode: \"fr-FR\",\n },\n \"de\": {\n code: \"de\",\n displayName: \"Deutsch\",\n fileName: \"sherpa-onnx-streaming-zipformer-de-kroko-2025-08-06\",\n size: 58 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\"encoder.onnx\", \"decoder.onnx\", \"joiner.onnx\", \"tokens.txt\"],\n languageCode: \"de-DE\",\n },\n \"es\": {\n code: \"es\",\n displayName: \"Español\",\n fileName: \"sherpa-onnx-streaming-zipformer-es-kroko-2025-08-06\",\n size: 124 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\"encoder.onnx\", \"decoder.onnx\", \"joiner.onnx\", \"tokens.txt\"],\n languageCode: \"es-ES\",\n },\n \"zh\": {\n code: \"zh\",\n displayName: \"中文\",\n fileName: \"sherpa-onnx-streaming-zipformer-zh-2025-06-30\",\n size: 150 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\"encoder.onnx\", \"decoder.onnx\", \"joiner.onnx\", \"tokens.txt\"],\n languageCode: \"zh-CN\",\n },\n \"ko\": {\n code: \"ko\",\n displayName: \"한국어\",\n fileName: \"sherpa-onnx-streaming-zipformer-korean-2024-06-16\",\n size: 200 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\"encoder.onnx\", \"decoder.onnx\", \"joiner.onnx\", \"tokens.txt\"],\n languageCode: \"ko-KR\",\n },\n // Alternative English streaming models, kept alongside \"en\" so the three can\n // be compared on-device. Both are newer than the 2023 Zipformer used for\n // \"en\" and are a fraction of its 349 MB.\n //\n // Kroko: same family as the fr/de/es entries above, and the smallest English\n // option here. Ships bare encoder/decoder/joiner .onnx (no int8 variants).\n \"en-kroko\": {\n code: \"en-kroko\",\n displayName: \"English (Kroko, 55 MB)\",\n fileName: \"sherpa-onnx-streaming-zipformer-en-kroko-2025-08-06\",\n size: 55 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\"encoder.onnx\", \"decoder.onnx\", \"joiner.onnx\", \"tokens.txt\"],\n languageCode: \"en-US\",\n },\n // NeMo streaming fast-conformer, 480 ms chunk. Upstream publishes 80/480/1040 ms;\n // 480 ms is the middle latency/accuracy point. Unlike Kroko this ships only\n // int8 weights, so requiredFiles uses the .int8.onnx names — the archive has\n // no bare .onnx files at all.\n //\n // sherpa-onnx loads it through the plain transducer config (type \"transducer\",\n // not \"nemotron\"): the nemotron branch in SherpaOnnxTranscriber exists for the\n // Nemotron models below, which need a different modelType string to decode\n // correctly. This one is a stock NeMo transducer.\n \"en-nemo-fast-conformer\": {\n code: \"en-nemo-fast-conformer\",\n displayName: \"English (NeMo Fast-Conformer 480ms, 101 MB)\",\n fileName: \"sherpa-onnx-nemo-streaming-fast-conformer-transducer-en-480ms-int8\",\n size: 101 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\n \"encoder.int8.onnx\",\n \"decoder.int8.onnx\",\n \"joiner.int8.onnx\",\n \"tokens.txt\",\n ],\n languageCode: \"en-US\",\n },\n // NVIDIA Nemotron streaming ASR. These are model choices rather than plain\n // languages, so they get their own codes and sort after the per-language\n // Zipformers above.\n //\n // Both ship int8 encoder/decoder/joiner — the same file shape as a Zipformer\n // transducer — and are loaded through the identical transducer config;\n // sherpa-onnx dispatches to its NeMo implementation from the ONNX metadata.\n // Requires sherpa-onnx >= 1.13.4 (see the pins in the Android build.gradle\n // and scripts/setup-sherpa-onnx-optional.sh).\n //\n // The 560ms chunk variant is chosen for both: upstream publishes\n // 80/160/320/560/1120ms, trading latency against accuracy, and 560ms is the\n // middle ground that stays usable for live captions. These are ~450 MB\n // downloads — substantially larger than every other model here.\n \"nemotron-en\": {\n code: \"nemotron-en\",\n displayName: \"Nemotron English (high accuracy)\",\n fileName: \"sherpa-onnx-nemotron-speech-streaming-en-0.6b-560ms-int8-2026-04-25\",\n size: 442 * 1024 * 1024,\n type: \"nemotron\",\n requiredFiles: [\"encoder.int8.onnx\", \"decoder.int8.onnx\", \"joiner.int8.onnx\", \"tokens.txt\"],\n languageCode: \"en-US\",\n },\n // Nemotron-3.5 covers 40 language-locales with automatic language detection,\n // so there is no single BCP-47 tag for it. We report en-US as the nominal\n // tag because downstream consumers require one; the model itself is not\n // restricted to English.\n \"nemotron-3.5\": {\n code: \"nemotron-3.5\",\n displayName: \"Nemotron 3.5 Multilingual (40 languages)\",\n fileName: \"sherpa-onnx-nemotron-3.5-asr-streaming-0.6b-560ms-int8-2026-06-11\",\n size: 453 * 1024 * 1024,\n type: \"nemotron\",\n requiredFiles: [\"encoder.int8.onnx\", \"decoder.int8.onnx\", \"joiner.int8.onnx\", \"tokens.txt\"],\n languageCode: \"en-US\",\n },\n }\n\n private constructor() {}\n\n static getInstance(): STTModelManager {\n if (!STTModelManager.instance) {\n STTModelManager.instance = new STTModelManager()\n }\n return STTModelManager.instance\n }\n\n async getCurrentLanguageFromPreferences(): Promise<string> {\n try {\n const path = await BluetoothSdk.getSttModelPath()\n const code = path && path.length > 0 ? this.getLanguageFromPath(path) : \"\"\n if (code && this.languages[code]) {\n this.currentLanguage = code\n return code\n }\n return \"\"\n } catch (error) {\n console.error(\"Error getting current STT language from preferences:\", error)\n return \"\"\n }\n }\n\n getCurrentLanguage(): string {\n return this.currentLanguage\n }\n\n setCurrentLanguage(code: string): void {\n if (this.languages[code]) {\n this.currentLanguage = code\n }\n }\n\n getBcp47ForLanguage(code?: string): string {\n const id = code || this.currentLanguage\n return this.languages[id]?.languageCode ?? \"en-US\"\n }\n\n getAvailableLanguages(): LanguageConfig[] {\n return Object.values(this.languages)\n }\n\n getModelDirectory(): string {\n const baseDir = Platform.OS === \"ios\" ? RNFS.DocumentDirectoryPath : RNFS.DocumentDirectoryPath\n return `${baseDir}/stt_models`\n }\n\n getModelPath(code?: string): string {\n const id = code || this.currentLanguage\n return `${this.getModelDirectory()}/${id}`\n }\n\n /** Last path segment is the language code (the on-disk folder name). */\n getLanguageFromPath(path: string): string {\n return path.split(\"/\").pop() || \"\"\n }\n\n async isModelAvailable(code?: string): Promise<boolean> {\n try {\n const id = code || this.currentLanguage\n const language = this.languages[id]\n if (!language) return false\n\n const modelPath = this.getModelPath(id)\n for (const file of language.requiredFiles) {\n const filePath = `${modelPath}/${file}`\n const exists = await RNFS.exists(filePath)\n if (!exists) {\n console.log(`Missing required STT file: ${file} at ${filePath}`)\n return false\n }\n }\n\n return await BluetoothSdk.validateSttModel(modelPath)\n } catch (error) {\n console.error(\"Error checking STT model availability:\", error)\n return false\n }\n }\n\n async getLanguageInfo(code?: string): Promise<LanguageInfo> {\n const id = code || this.currentLanguage\n const language = this.languages[id]\n if (!language) {\n throw new Error(`Language ${id} not found`)\n }\n\n const downloaded = await this.isModelAvailable(id)\n const path = downloaded ? this.getModelPath(id) : undefined\n\n return {\n code: id,\n displayName: language.displayName,\n size: language.size,\n language: language.languageCode,\n downloaded,\n path,\n type: language.type,\n }\n }\n\n async getAllLanguageInfo(): Promise<LanguageInfo[]> {\n const infos: LanguageInfo[] = []\n for (const code of Object.keys(this.languages)) {\n infos.push(await this.getLanguageInfo(code))\n }\n return infos\n }\n\n async downloadModel(\n code?: string,\n onProgress?: (progress: DownloadProgress) => void,\n onExtractionProgress?: (progress: ExtractionProgress) => void,\n ): Promise<void> {\n const id = code || this.currentLanguage\n const language = this.languages[id]\n if (!language) {\n throw new Error(`Language ${id} not found`)\n }\n\n const modelUrl = language.downloadUrl ?? `${this.modelBaseUrl}${language.fileName}.tar.bz2`\n const tempPath = `${RNFS.TemporaryDirectoryPath}/${language.fileName}.tar.bz2`\n const modelDir = this.getModelDirectory()\n const finalPath = this.getModelPath(id)\n\n try {\n await RNFS.mkdir(modelDir, {NSURLIsExcludedFromBackupKey: true})\n\n const downloadOptions = {\n fromUrl: modelUrl,\n toFile: tempPath,\n progress: (res: RNFS.DownloadProgressCallbackResultT) => {\n const percentage = Math.round((res.bytesWritten / res.contentLength) * 100)\n onProgress?.({\n jobId: res.jobId,\n bytesWritten: res.bytesWritten,\n contentLength: res.contentLength,\n percentage,\n })\n },\n progressDivider: 10,\n begin: (res: RNFS.DownloadBeginCallbackResultT) => {\n console.log(\"Download started:\", res)\n },\n connectionTimeout: 30000,\n readTimeout: 30000,\n }\n\n const result = await RNFS.downloadFile(downloadOptions)\n this.downloadJobId = result.jobId\n\n const downloadResult = await result.promise\n\n if (downloadResult.statusCode !== 200) {\n throw new Error(`Download failed with status code: ${downloadResult.statusCode}`)\n }\n\n onExtractionProgress?.({percentage: 0})\n\n const subscription = BluetoothSdk.addListener(\"extraction_progress\", (event) => {\n onExtractionProgress?.({percentage: event.percentage})\n })\n\n try {\n const extractionResult = await BluetoothSdk.extractTarBz2(tempPath, finalPath)\n if (!extractionResult) {\n throw new Error(\"Native extraction returned failure status\")\n }\n } catch (extractError) {\n console.error(\"Native extraction failed:\", extractError)\n throw extractError\n } finally {\n subscription.remove()\n }\n\n onExtractionProgress?.({percentage: 100})\n\n await RNFS.unlink(tempPath)\n\n if (id === this.currentLanguage) {\n await this.setNativeModelPath(finalPath, language.languageCode)\n }\n } catch (error) {\n // Best-effort cleanup; never let it mask the original error.\n try {\n if (await RNFS.exists(tempPath)) await RNFS.unlink(tempPath)\n } catch (cleanupError) {\n console.warn(\"STTModelManager: temp cleanup failed:\", cleanupError)\n }\n try {\n if (await RNFS.exists(finalPath)) await RNFS.unlink(finalPath)\n } catch (cleanupError) {\n console.warn(\"STTModelManager: final cleanup failed:\", cleanupError)\n }\n throw error\n }\n }\n\n async cancelDownload(): Promise<void> {\n if (this.downloadJobId !== undefined) {\n await RNFS.stopDownload(this.downloadJobId)\n this.downloadJobId = undefined\n }\n }\n\n async deleteModel(code?: string): Promise<void> {\n const id = code || this.currentLanguage\n const modelPath = this.getModelPath(id)\n if (await RNFS.exists(modelPath)) {\n await RNFS.unlink(modelPath)\n }\n }\n\n async activateLanguage(code: string): Promise<void> {\n const language = this.languages[code]\n if (!language) {\n throw new Error(`Language ${code} not found`)\n }\n\n const isAvailable = await this.isModelAvailable(code)\n if (!isAvailable) {\n throw new Error(`Language ${code} model is not downloaded`)\n }\n\n this.currentLanguage = code\n const modelPath = this.getModelPath(code)\n await this.setNativeModelPath(modelPath, language.languageCode)\n }\n\n private async setNativeModelPath(path: string, languageCode: string): Promise<void> {\n BluetoothSdk.setSttModelDetails(path, languageCode)\n }\n\n async getStorageInfo(): Promise<{free: number; total: number}> {\n const fsInfo = await RNFS.getFSInfo()\n return {\n free: fsInfo.freeSpace,\n total: fsInfo.totalSpace,\n }\n }\n\n formatBytes(bytes: number): string {\n return STTModelManager.formatBytes(bytes)\n }\n\n static formatBytes(bytes: number): string {\n if (bytes === 0) return \"0 Bytes\"\n const k = 1024\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\"]\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + \" \" + sizes[i]\n }\n}\n\nconst instance = STTModelManager.getInstance()\nexport {STTModelManager}\nexport default instance\n"]}
1
+ {"version":3,"file":"STTModelManager.js","sourceRoot":"","sources":["../../src/services/STTModelManager.ts"],"names":[],"mappings":"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","sourcesContent":["import BluetoothSdk from \"@glassly/bluetooth-sdk\"\nimport {Platform} from \"react-native\"\nimport * as RNFS from \"@dr.pogodin/react-native-fs\"\n\nexport interface LanguageInfo {\n code: string\n displayName: string\n size: number\n language: string\n downloaded: boolean\n path?: string\n type: \"transducer\" | \"ctc\" | \"nemotron\"\n}\n\nexport interface DownloadProgress {\n jobId: number\n bytesWritten: number\n contentLength: number\n percentage: number\n}\n\nexport interface ExtractionProgress {\n percentage: number\n currentFile?: string\n}\n\nexport interface LanguageConfig {\n code: string\n displayName: string\n fileName: string\n downloadUrl?: string\n size: number\n type: \"transducer\" | \"ctc\" | \"nemotron\"\n requiredFiles: string[]\n languageCode: string\n}\n\nconst DEFAULT_LANGUAGE = \"en\"\n\nclass STTModelManager {\n private static instance: STTModelManager\n private downloadJobId?: number\n private currentLanguage = DEFAULT_LANGUAGE\n private modelReadyListeners = new Set<() => void>()\n private modelBaseUrl = \"https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/\"\n\n private languages: Record<string, LanguageConfig> = {\n \"en\": {\n code: \"en\",\n displayName: \"English\",\n fileName: \"sherpa-onnx-streaming-zipformer-en-2023-06-21-mobile\",\n size: 349 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\"encoder.onnx\", \"decoder.onnx\", \"joiner.onnx\", \"tokens.txt\"],\n languageCode: \"en-US\",\n },\n \"fr\": {\n code: \"fr\",\n displayName: \"Français\",\n fileName: \"sherpa-onnx-streaming-zipformer-fr-kroko-2025-08-06\",\n size: 57 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\"encoder.onnx\", \"decoder.onnx\", \"joiner.onnx\", \"tokens.txt\"],\n languageCode: \"fr-FR\",\n },\n \"de\": {\n code: \"de\",\n displayName: \"Deutsch\",\n fileName: \"sherpa-onnx-streaming-zipformer-de-kroko-2025-08-06\",\n size: 58 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\"encoder.onnx\", \"decoder.onnx\", \"joiner.onnx\", \"tokens.txt\"],\n languageCode: \"de-DE\",\n },\n \"es\": {\n code: \"es\",\n displayName: \"Español\",\n fileName: \"sherpa-onnx-streaming-zipformer-es-kroko-2025-08-06\",\n size: 124 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\"encoder.onnx\", \"decoder.onnx\", \"joiner.onnx\", \"tokens.txt\"],\n languageCode: \"es-ES\",\n },\n \"zh\": {\n code: \"zh\",\n displayName: \"中文\",\n fileName: \"sherpa-onnx-streaming-zipformer-zh-2025-06-30\",\n size: 150 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\"encoder.onnx\", \"decoder.onnx\", \"joiner.onnx\", \"tokens.txt\"],\n languageCode: \"zh-CN\",\n },\n \"ko\": {\n code: \"ko\",\n displayName: \"한국어\",\n fileName: \"sherpa-onnx-streaming-zipformer-korean-2024-06-16\",\n size: 200 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\"encoder.onnx\", \"decoder.onnx\", \"joiner.onnx\", \"tokens.txt\"],\n languageCode: \"ko-KR\",\n },\n // Alternative English streaming models, kept alongside \"en\" so the three can\n // be compared on-device. Both are newer than the 2023 Zipformer used for\n // \"en\" and are a fraction of its 349 MB.\n //\n // Kroko: same family as the fr/de/es entries above, and the smallest English\n // option here. Ships bare encoder/decoder/joiner .onnx (no int8 variants).\n \"en-kroko\": {\n code: \"en-kroko\",\n displayName: \"English (Kroko, 55 MB)\",\n fileName: \"sherpa-onnx-streaming-zipformer-en-kroko-2025-08-06\",\n size: 55 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\"encoder.onnx\", \"decoder.onnx\", \"joiner.onnx\", \"tokens.txt\"],\n languageCode: \"en-US\",\n },\n // NeMo streaming fast-conformer, 480 ms chunk. Upstream publishes 80/480/1040 ms;\n // 480 ms is the middle latency/accuracy point. Unlike Kroko this ships only\n // int8 weights, so requiredFiles uses the .int8.onnx names — the archive has\n // no bare .onnx files at all.\n //\n // sherpa-onnx loads it through the plain transducer config (type \"transducer\",\n // not \"nemotron\"): the nemotron branch in SherpaOnnxTranscriber exists for the\n // Nemotron models below, which need a different modelType string to decode\n // correctly. This one is a stock NeMo transducer.\n \"en-nemo-fast-conformer\": {\n code: \"en-nemo-fast-conformer\",\n displayName: \"English (NeMo Fast-Conformer 480ms, 101 MB)\",\n fileName: \"sherpa-onnx-nemo-streaming-fast-conformer-transducer-en-480ms-int8\",\n size: 101 * 1024 * 1024,\n type: \"transducer\",\n requiredFiles: [\n \"encoder.int8.onnx\",\n \"decoder.int8.onnx\",\n \"joiner.int8.onnx\",\n \"tokens.txt\",\n ],\n languageCode: \"en-US\",\n },\n // NVIDIA Nemotron streaming ASR. These are model choices rather than plain\n // languages, so they get their own codes and sort after the per-language\n // Zipformers above.\n //\n // Both ship int8 encoder/decoder/joiner — the same file shape as a Zipformer\n // transducer — and are loaded through the identical transducer config;\n // sherpa-onnx dispatches to its NeMo implementation from the ONNX metadata.\n // Requires sherpa-onnx >= 1.13.4 (see the pins in the Android build.gradle\n // and scripts/setup-sherpa-onnx-optional.sh).\n //\n // The 560ms chunk variant is chosen for both: upstream publishes\n // 80/160/320/560/1120ms, trading latency against accuracy, and 560ms is the\n // middle ground that stays usable for live captions. These are ~450 MB\n // downloads — substantially larger than every other model here.\n \"nemotron-en\": {\n code: \"nemotron-en\",\n displayName: \"Nemotron English (high accuracy)\",\n fileName: \"sherpa-onnx-nemotron-speech-streaming-en-0.6b-560ms-int8-2026-04-25\",\n size: 442 * 1024 * 1024,\n type: \"nemotron\",\n requiredFiles: [\"encoder.int8.onnx\", \"decoder.int8.onnx\", \"joiner.int8.onnx\", \"tokens.txt\"],\n languageCode: \"en-US\",\n },\n // Nemotron-3.5 covers 40 language-locales with automatic language detection,\n // so there is no single BCP-47 tag for it. We report en-US as the nominal\n // tag because downstream consumers require one; the model itself is not\n // restricted to English.\n \"nemotron-3.5\": {\n code: \"nemotron-3.5\",\n displayName: \"Nemotron 3.5 Multilingual (40 languages)\",\n fileName: \"sherpa-onnx-nemotron-3.5-asr-streaming-0.6b-560ms-int8-2026-06-11\",\n size: 453 * 1024 * 1024,\n type: \"nemotron\",\n requiredFiles: [\"encoder.int8.onnx\", \"decoder.int8.onnx\", \"joiner.int8.onnx\", \"tokens.txt\"],\n languageCode: \"en-US\",\n },\n }\n\n private constructor() {}\n\n static getInstance(): STTModelManager {\n if (!STTModelManager.instance) {\n STTModelManager.instance = new STTModelManager()\n }\n return STTModelManager.instance\n }\n\n async getCurrentLanguageFromPreferences(): Promise<string> {\n try {\n const path = await BluetoothSdk.getSttModelPath()\n const code = path && path.length > 0 ? this.getLanguageFromPath(path) : \"\"\n if (code && this.languages[code]) {\n this.currentLanguage = code\n return code\n }\n return \"\"\n } catch (error) {\n console.error(\"Error getting current STT language from preferences:\", error)\n return \"\"\n }\n }\n\n getCurrentLanguage(): string {\n return this.currentLanguage\n }\n\n setCurrentLanguage(code: string): void {\n if (this.languages[code]) {\n this.currentLanguage = code\n }\n }\n\n getBcp47ForLanguage(code?: string): string {\n const id = code || this.currentLanguage\n return this.languages[id]?.languageCode ?? \"en-US\"\n }\n\n getAvailableLanguages(): LanguageConfig[] {\n return Object.values(this.languages)\n }\n\n getModelDirectory(): string {\n const baseDir = Platform.OS === \"ios\" ? RNFS.DocumentDirectoryPath : RNFS.DocumentDirectoryPath\n return `${baseDir}/stt_models`\n }\n\n getModelPath(code?: string): string {\n const id = code || this.currentLanguage\n return `${this.getModelDirectory()}/${id}`\n }\n\n /** Last path segment is the language code (the on-disk folder name). */\n getLanguageFromPath(path: string): string {\n return path.split(\"/\").pop() || \"\"\n }\n\n async isModelAvailable(code?: string): Promise<boolean> {\n try {\n const id = code || this.currentLanguage\n const language = this.languages[id]\n if (!language) return false\n\n const modelPath = this.getModelPath(id)\n for (const file of language.requiredFiles) {\n const filePath = `${modelPath}/${file}`\n const exists = await RNFS.exists(filePath)\n if (!exists) {\n console.log(`Missing required STT file: ${file} at ${filePath}`)\n return false\n }\n }\n\n return await BluetoothSdk.validateSttModel(modelPath)\n } catch (error) {\n console.error(\"Error checking STT model availability:\", error)\n return false\n }\n }\n\n async getLanguageInfo(code?: string): Promise<LanguageInfo> {\n const id = code || this.currentLanguage\n const language = this.languages[id]\n if (!language) {\n throw new Error(`Language ${id} not found`)\n }\n\n const downloaded = await this.isModelAvailable(id)\n const path = downloaded ? this.getModelPath(id) : undefined\n\n return {\n code: id,\n displayName: language.displayName,\n size: language.size,\n language: language.languageCode,\n downloaded,\n path,\n type: language.type,\n }\n }\n\n async getAllLanguageInfo(): Promise<LanguageInfo[]> {\n const infos: LanguageInfo[] = []\n for (const code of Object.keys(this.languages)) {\n infos.push(await this.getLanguageInfo(code))\n }\n return infos\n }\n\n async downloadModel(\n code?: string,\n onProgress?: (progress: DownloadProgress) => void,\n onExtractionProgress?: (progress: ExtractionProgress) => void,\n ): Promise<void> {\n const id = code || this.currentLanguage\n const language = this.languages[id]\n if (!language) {\n throw new Error(`Language ${id} not found`)\n }\n\n const modelUrl = language.downloadUrl ?? `${this.modelBaseUrl}${language.fileName}.tar.bz2`\n const tempPath = `${RNFS.TemporaryDirectoryPath}/${language.fileName}.tar.bz2`\n const modelDir = this.getModelDirectory()\n const finalPath = this.getModelPath(id)\n\n try {\n await RNFS.mkdir(modelDir, {NSURLIsExcludedFromBackupKey: true})\n\n const downloadOptions = {\n fromUrl: modelUrl,\n toFile: tempPath,\n progress: (res: RNFS.DownloadProgressCallbackResultT) => {\n const percentage = Math.round((res.bytesWritten / res.contentLength) * 100)\n onProgress?.({\n jobId: res.jobId,\n bytesWritten: res.bytesWritten,\n contentLength: res.contentLength,\n percentage,\n })\n },\n progressDivider: 10,\n begin: (res: RNFS.DownloadBeginCallbackResultT) => {\n console.log(\"Download started:\", res)\n },\n connectionTimeout: 30000,\n readTimeout: 30000,\n }\n\n const result = await RNFS.downloadFile(downloadOptions)\n this.downloadJobId = result.jobId\n\n const downloadResult = await result.promise\n\n if (downloadResult.statusCode !== 200) {\n throw new Error(`Download failed with status code: ${downloadResult.statusCode}`)\n }\n\n onExtractionProgress?.({percentage: 0})\n\n const subscription = BluetoothSdk.addListener(\"extraction_progress\", (event) => {\n onExtractionProgress?.({percentage: event.percentage})\n })\n\n try {\n const extractionResult = await BluetoothSdk.extractTarBz2(tempPath, finalPath)\n if (!extractionResult) {\n throw new Error(\"Native extraction returned failure status\")\n }\n } catch (extractError) {\n console.error(\"Native extraction failed:\", extractError)\n throw extractError\n } finally {\n subscription.remove()\n }\n\n onExtractionProgress?.({percentage: 100})\n\n await RNFS.unlink(tempPath)\n\n if (id === this.currentLanguage) {\n await this.setNativeModelPath(finalPath, language.languageCode)\n }\n } catch (error) {\n // Best-effort cleanup; never let it mask the original error.\n try {\n if (await RNFS.exists(tempPath)) await RNFS.unlink(tempPath)\n } catch (cleanupError) {\n console.warn(\"STTModelManager: temp cleanup failed:\", cleanupError)\n }\n try {\n if (await RNFS.exists(finalPath)) await RNFS.unlink(finalPath)\n } catch (cleanupError) {\n console.warn(\"STTModelManager: final cleanup failed:\", cleanupError)\n }\n throw error\n }\n }\n\n async cancelDownload(): Promise<void> {\n if (this.downloadJobId !== undefined) {\n await RNFS.stopDownload(this.downloadJobId)\n this.downloadJobId = undefined\n }\n }\n\n async deleteModel(code?: string): Promise<void> {\n const id = code || this.currentLanguage\n const modelPath = this.getModelPath(id)\n if (await RNFS.exists(modelPath)) {\n await RNFS.unlink(modelPath)\n }\n }\n\n async activateLanguage(code: string): Promise<void> {\n const language = this.languages[code]\n if (!language) {\n throw new Error(`Language ${code} not found`)\n }\n\n const isAvailable = await this.isModelAvailable(code)\n if (!isAvailable) {\n throw new Error(`Language ${code} model is not downloaded`)\n }\n\n this.currentLanguage = code\n const modelPath = this.getModelPath(code)\n await this.setNativeModelPath(modelPath, language.languageCode)\n }\n\n private async setNativeModelPath(path: string, languageCode: string): Promise<void> {\n await BluetoothSdk.setSttModelDetails(path, languageCode)\n for (const listener of this.modelReadyListeners) {\n try {\n listener()\n } catch (error) {\n console.warn(\"STTModelManager: model-ready listener failed:\", error)\n }\n }\n }\n\n /** Fired after a downloaded/selected model's path has reached native. */\n onModelReady(listener: () => void): () => void {\n this.modelReadyListeners.add(listener)\n return () => this.modelReadyListeners.delete(listener)\n }\n\n async getStorageInfo(): Promise<{free: number; total: number}> {\n const fsInfo = await RNFS.getFSInfo()\n return {\n free: fsInfo.freeSpace,\n total: fsInfo.totalSpace,\n }\n }\n\n formatBytes(bytes: number): string {\n return STTModelManager.formatBytes(bytes)\n }\n\n static formatBytes(bytes: number): string {\n if (bytes === 0) return \"0 Bytes\"\n const k = 1024\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\"]\n const i = Math.floor(Math.log(bytes) / Math.log(k))\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + \" \" + sizes[i]\n }\n}\n\nconst instance = STTModelManager.getInstance()\nexport {STTModelManager}\nexport default instance\n"]}
@@ -17,11 +17,19 @@ export type LegacyLayout = {
17
17
  [key: string]: unknown;
18
18
  };
19
19
  export type SceneEmitResult =
20
- /** A SceneFrame went to native. */
20
+ /**
21
+ * A SceneFrame was committed. `sent` is false when the frame stayed
22
+ * phone-side (dashboard covering main). `settleMs` is how long the device
23
+ * keeps animating after it has the frame: the longest transition among the
24
+ * elements this frame changed, 0 on devices that jump.
25
+ */
21
26
  {
22
27
  kind: "scene";
23
28
  degraded: boolean;
24
29
  dropped: string[];
30
+ seq: number;
31
+ sent: boolean;
32
+ settleMs: number;
25
33
  }
26
34
  /** Degrade path: caller routes this legacy layout through the existing pipeline (null layout ⇒ clear). */
27
35
  | {
@@ -34,9 +42,22 @@ export type SceneEmitResult =
34
42
  | {
35
43
  kind: "no-display";
36
44
  };
45
+ /** How a frame ended up, as reported by native (or inferred from a later frame's report). */
46
+ export interface ScenePresentation {
47
+ /** The device had the frame's content (this frame or a later one that folded it in). */
48
+ presented: boolean;
49
+ /** A later frame's report settled this one. */
50
+ superseded: boolean;
51
+ reason?: string;
52
+ }
37
53
  declare class SceneRenderer {
38
54
  private static instance;
39
55
  private store;
56
+ private seqCounter;
57
+ private pending;
58
+ private presentedSub;
59
+ private settledSub;
60
+ private settledListeners;
40
61
  static getInstance(): SceneRenderer;
41
62
  /**
42
63
  * Scene capabilities of the currently-selected device, or null when it has no
@@ -62,8 +83,27 @@ declare class SceneRenderer {
62
83
  * Returns false when nothing is retained.
63
84
  */
64
85
  replayApp(appId: string, view: "main" | "dashboard"): boolean;
65
- /** Forget an app's retained scenes (app stop). */
86
+ /** Forget an app's retained scenes (app stop). Frames still in flight will never show. */
66
87
  clearApp(appId: string): void;
88
+ /**
89
+ * Resolves once native reports frame `seq` on the device, or a later frame
90
+ * that folded it in. Never rejects; `timeoutMs` bounds a frame the device
91
+ * never acknowledges (link stalls, page rebuilding).
92
+ */
93
+ whenPresented(seq: number, timeoutMs: number, appId: string): Promise<ScenePresentation>;
94
+ private ensurePresentedListener;
95
+ /**
96
+ * Fires with the host frame number once the glasses report that every
97
+ * animation the frame started has ended (Glassly CFW revision 27+). The
98
+ * device echoes the low 16 bits of the seq; the full number is recovered
99
+ * against the latest frame sent.
100
+ */
101
+ onSettled(listener: (seq: number) => void): {
102
+ remove: () => void;
103
+ };
104
+ /** A report for `seq` settles it and every earlier frame (folded in by the native pacer). */
105
+ private onFramePresented;
106
+ private settlePending;
67
107
  /**
68
108
  * Reset the diff baseline for (app, view) without touching the device.
69
109
  * Used when a legacy layout bypasses the scene path (clear_view /
@@ -80,6 +120,7 @@ declare class SceneRenderer {
80
120
  * restore-as-replay source; only incremental diffing restarts from empty.
81
121
  */
82
122
  markViewStale(view: "main" | "dashboard"): void;
123
+ /** Returns false when the frame stayed phone-side (no presentation report will follow). */
83
124
  private sendFrame;
84
125
  _resetForTest(): void;
85
126
  _lastFrameForTest(appId: string, view: string): FrameElement[];