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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (503) hide show
  1. package/README.md +55 -18
  2. package/build/bluetooth-sdk/debug.d.ts +2 -0
  3. package/build/bluetooth-sdk/debug.d.ts.map +1 -0
  4. package/build/bluetooth-sdk/debug.js +2 -0
  5. package/build/bluetooth-sdk/debug.js.map +1 -0
  6. package/build/bluetooth-sdk/index.d.ts +3 -0
  7. package/build/bluetooth-sdk/index.d.ts.map +1 -0
  8. package/build/bluetooth-sdk/index.js +3 -0
  9. package/build/bluetooth-sdk/index.js.map +1 -0
  10. package/build/bluetooth-sdk/photo-receiver.d.ts +3 -0
  11. package/build/bluetooth-sdk/photo-receiver.d.ts.map +1 -0
  12. package/build/bluetooth-sdk/photo-receiver.js +3 -0
  13. package/build/bluetooth-sdk/photo-receiver.js.map +1 -0
  14. package/build/bluetooth-sdk/react.d.ts +2 -0
  15. package/build/bluetooth-sdk/react.d.ts.map +1 -0
  16. package/build/bluetooth-sdk/react.js +2 -0
  17. package/build/bluetooth-sdk/react.js.map +1 -0
  18. package/build/bluetooth-sdk/types.d.ts +2 -0
  19. package/build/bluetooth-sdk/types.d.ts.map +1 -0
  20. package/build/bluetooth-sdk/types.js +2 -0
  21. package/build/bluetooth-sdk/types.js.map +1 -0
  22. package/build/engine.d.ts +24 -16
  23. package/build/engine.d.ts.map +1 -1
  24. package/build/engine.js +9 -4
  25. package/build/engine.js.map +1 -1
  26. package/build/facades/dev.d.ts +3 -2
  27. package/build/facades/dev.d.ts.map +1 -1
  28. package/build/facades/dev.js +18 -12
  29. package/build/facades/dev.js.map +1 -1
  30. package/build/facades/display.d.ts +1 -1
  31. package/build/facades/display.d.ts.map +1 -1
  32. package/build/facades/display.js +1 -1
  33. package/build/facades/display.js.map +1 -1
  34. package/build/facades/gallery.js +1 -1
  35. package/build/facades/gallery.js.map +1 -1
  36. package/build/facades/glasses.d.ts +4 -13
  37. package/build/facades/glasses.d.ts.map +1 -1
  38. package/build/facades/glasses.js +21 -6
  39. package/build/facades/glasses.js.map +1 -1
  40. package/build/facades/glassesSettings.d.ts.map +1 -1
  41. package/build/facades/glassesSettings.js +1 -0
  42. package/build/facades/glassesSettings.js.map +1 -1
  43. package/build/facades/miniapps.d.ts +4 -0
  44. package/build/facades/miniapps.d.ts.map +1 -1
  45. package/build/facades/miniapps.js +5 -0
  46. package/build/facades/miniapps.js.map +1 -1
  47. package/build/facades/ota.d.ts +14 -16
  48. package/build/facades/ota.d.ts.map +1 -1
  49. package/build/facades/ota.js +22 -7
  50. package/build/facades/ota.js.map +1 -1
  51. package/build/facades/pairing.d.ts +8 -5
  52. package/build/facades/pairing.d.ts.map +1 -1
  53. package/build/facades/pairing.js +12 -10
  54. package/build/facades/pairing.js.map +1 -1
  55. package/build/facades/permissions.d.ts +10 -0
  56. package/build/facades/permissions.d.ts.map +1 -1
  57. package/build/facades/permissions.js +10 -0
  58. package/build/facades/permissions.js.map +1 -1
  59. package/build/facades/phoneNotifications.d.ts +0 -4
  60. package/build/facades/phoneNotifications.d.ts.map +1 -1
  61. package/build/facades/phoneNotifications.js +4 -14
  62. package/build/facades/phoneNotifications.js.map +1 -1
  63. package/build/facades/speech.d.ts +7 -6
  64. package/build/facades/speech.d.ts.map +1 -1
  65. package/build/facades/speech.js +1 -1
  66. package/build/facades/speech.js.map +1 -1
  67. package/build/generated/releaseMetadata.d.ts +12 -0
  68. package/build/generated/releaseMetadata.d.ts.map +1 -0
  69. package/build/generated/releaseMetadata.js +10 -0
  70. package/build/generated/releaseMetadata.js.map +1 -0
  71. package/build/index.d.ts +10 -8
  72. package/build/index.d.ts.map +1 -1
  73. package/build/index.js +6 -5
  74. package/build/index.js.map +1 -1
  75. package/build/react/MentraLiveOtaFlow.d.ts +38 -0
  76. package/build/react/MentraLiveOtaFlow.d.ts.map +1 -0
  77. package/build/react/MentraLiveOtaFlow.js +528 -0
  78. package/build/react/MentraLiveOtaFlow.js.map +1 -0
  79. package/build/react/index.d.ts +2 -0
  80. package/build/react/index.d.ts.map +1 -0
  81. package/build/react/index.js +2 -0
  82. package/build/react/index.js.map +1 -0
  83. package/build/react/useEngineSnapshot.d.ts +2 -0
  84. package/build/react/useEngineSnapshot.d.ts.map +1 -0
  85. package/build/react/useEngineSnapshot.js +14 -0
  86. package/build/react/useEngineSnapshot.js.map +1 -0
  87. package/build/runtime/bootstrap.d.ts +24 -1
  88. package/build/runtime/bootstrap.d.ts.map +1 -1
  89. package/build/runtime/bootstrap.js +17 -0
  90. package/build/runtime/bootstrap.js.map +1 -1
  91. package/build/runtime/scanQrSeam.d.ts +7 -0
  92. package/build/runtime/scanQrSeam.d.ts.map +1 -0
  93. package/build/runtime/scanQrSeam.js +16 -0
  94. package/build/runtime/scanQrSeam.js.map +1 -0
  95. package/build/services/AppRegistry.d.ts +41 -57
  96. package/build/services/AppRegistry.d.ts.map +1 -1
  97. package/build/services/AppRegistry.js +208 -144
  98. package/build/services/AppRegistry.js.map +1 -1
  99. package/build/services/AudioCloudUplink.d.ts +12 -0
  100. package/build/services/AudioCloudUplink.d.ts.map +1 -1
  101. package/build/services/AudioCloudUplink.js +22 -1
  102. package/build/services/AudioCloudUplink.js.map +1 -1
  103. package/build/services/AudioPlaybackService.d.ts +41 -2
  104. package/build/services/AudioPlaybackService.d.ts.map +1 -1
  105. package/build/services/AudioPlaybackService.js +250 -33
  106. package/build/services/AudioPlaybackService.js.map +1 -1
  107. package/build/services/BlobStore.d.ts +2 -1
  108. package/build/services/BlobStore.d.ts.map +1 -1
  109. package/build/services/BlobStore.js +17 -9
  110. package/build/services/BlobStore.js.map +1 -1
  111. package/build/services/CloudAudioSubscriptionSync.d.ts.map +1 -1
  112. package/build/services/CloudAudioSubscriptionSync.js +13 -1
  113. package/build/services/CloudAudioSubscriptionSync.js.map +1 -1
  114. package/build/services/CloudClientService.d.ts +15 -0
  115. package/build/services/CloudClientService.d.ts.map +1 -1
  116. package/build/services/CloudClientService.js +93 -1
  117. package/build/services/CloudClientService.js.map +1 -1
  118. package/build/services/DeviceEventRouter.js +5 -5
  119. package/build/services/DeviceEventRouter.js.map +1 -1
  120. package/build/services/ForegroundLocationPermission.d.ts +13 -0
  121. package/build/services/ForegroundLocationPermission.d.ts.map +1 -0
  122. package/build/services/ForegroundLocationPermission.js +12 -0
  123. package/build/services/ForegroundLocationPermission.js.map +1 -0
  124. package/build/services/GlassesSettingsSync.d.ts.map +1 -1
  125. package/build/services/GlassesSettingsSync.js +9 -3
  126. package/build/services/GlassesSettingsSync.js.map +1 -1
  127. package/build/services/GlassesStatusProjection.d.ts +2 -1
  128. package/build/services/GlassesStatusProjection.d.ts.map +1 -1
  129. package/build/services/GlassesStatusProjection.js +15 -8
  130. package/build/services/GlassesStatusProjection.js.map +1 -1
  131. package/build/services/HotspotOtaTransport.d.ts +29 -0
  132. package/build/services/HotspotOtaTransport.d.ts.map +1 -0
  133. package/build/services/HotspotOtaTransport.js +146 -0
  134. package/build/services/HotspotOtaTransport.js.map +1 -0
  135. package/build/services/HotspotShutdown.d.ts +17 -0
  136. package/build/services/HotspotShutdown.d.ts.map +1 -0
  137. package/build/services/HotspotShutdown.js +29 -0
  138. package/build/services/HotspotShutdown.js.map +1 -0
  139. package/build/services/LocalDisplayManager.d.ts +15 -0
  140. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  141. package/build/services/LocalDisplayManager.js +106 -20
  142. package/build/services/LocalDisplayManager.js.map +1 -1
  143. package/build/services/LocalMiniappRuntime.d.ts +74 -2
  144. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  145. package/build/services/LocalMiniappRuntime.js +785 -291
  146. package/build/services/LocalMiniappRuntime.js.map +1 -1
  147. package/build/services/LocalMiniappStorage.d.ts +32 -0
  148. package/build/services/LocalMiniappStorage.d.ts.map +1 -0
  149. package/build/services/LocalMiniappStorage.js +66 -0
  150. package/build/services/LocalMiniappStorage.js.map +1 -0
  151. package/build/services/LocalMiniappUserIdentity.d.ts +20 -0
  152. package/build/services/LocalMiniappUserIdentity.d.ts.map +1 -0
  153. package/build/services/LocalMiniappUserIdentity.js +43 -0
  154. package/build/services/LocalMiniappUserIdentity.js.map +1 -0
  155. package/build/services/LocalSttFallbackCoordinator.d.ts +9 -6
  156. package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
  157. package/build/services/LocalSttFallbackCoordinator.js +18 -12
  158. package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
  159. package/build/services/MicStateCoordinator.d.ts +42 -1
  160. package/build/services/MicStateCoordinator.d.ts.map +1 -1
  161. package/build/services/MicStateCoordinator.js +141 -3
  162. package/build/services/MicStateCoordinator.js.map +1 -1
  163. package/build/services/MiniappLauncher.d.ts.map +1 -1
  164. package/build/services/MiniappLauncher.js +23 -3
  165. package/build/services/MiniappLauncher.js.map +1 -1
  166. package/build/services/MiniappLiveness.d.ts +12 -0
  167. package/build/services/MiniappLiveness.d.ts.map +1 -0
  168. package/build/services/MiniappLiveness.js +16 -0
  169. package/build/services/MiniappLiveness.js.map +1 -0
  170. package/build/services/NativeHttpResponse.d.ts +3 -0
  171. package/build/services/NativeHttpResponse.d.ts.map +1 -0
  172. package/build/services/NativeHttpResponse.js +6 -0
  173. package/build/services/NativeHttpResponse.js.map +1 -0
  174. package/build/services/OtaArtifactDownloader.d.ts +65 -0
  175. package/build/services/OtaArtifactDownloader.d.ts.map +1 -0
  176. package/build/services/OtaArtifactDownloader.js +226 -0
  177. package/build/services/OtaArtifactDownloader.js.map +1 -0
  178. package/build/services/OtaAutoChain.d.ts +34 -0
  179. package/build/services/OtaAutoChain.d.ts.map +1 -0
  180. package/build/services/OtaAutoChain.js +83 -0
  181. package/build/services/OtaAutoChain.js.map +1 -0
  182. package/build/services/OtaErrorMapping.d.ts +18 -0
  183. package/build/services/OtaErrorMapping.d.ts.map +1 -0
  184. package/build/services/OtaErrorMapping.js +63 -0
  185. package/build/services/OtaErrorMapping.js.map +1 -0
  186. package/build/services/OtaInstallCoordinator.d.ts +95 -15
  187. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  188. package/build/services/OtaInstallCoordinator.js +577 -130
  189. package/build/services/OtaInstallCoordinator.js.map +1 -1
  190. package/build/services/OtaService.d.ts.map +1 -1
  191. package/build/services/OtaService.js +8 -1
  192. package/build/services/OtaService.js.map +1 -1
  193. package/build/services/OtaUpdateCheckService.d.ts +39 -4
  194. package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
  195. package/build/services/OtaUpdateCheckService.js +138 -22
  196. package/build/services/OtaUpdateCheckService.js.map +1 -1
  197. package/build/services/PhoneCalendarHelpers.d.ts +32 -0
  198. package/build/services/PhoneCalendarHelpers.d.ts.map +1 -0
  199. package/build/services/PhoneCalendarHelpers.js +157 -0
  200. package/build/services/PhoneCalendarHelpers.js.map +1 -0
  201. package/build/services/PhoneCalendarService.d.ts +5 -0
  202. package/build/services/PhoneCalendarService.d.ts.map +1 -0
  203. package/build/services/PhoneCalendarService.js +45 -0
  204. package/build/services/PhoneCalendarService.js.map +1 -0
  205. package/build/services/PhoneCameraFovCoordinator.d.ts +30 -0
  206. package/build/services/PhoneCameraFovCoordinator.d.ts.map +1 -0
  207. package/build/services/PhoneCameraFovCoordinator.js +194 -0
  208. package/build/services/PhoneCameraFovCoordinator.js.map +1 -0
  209. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  210. package/build/services/PhoneNotificationsSync.js +12 -10
  211. package/build/services/PhoneNotificationsSync.js.map +1 -1
  212. package/build/services/PhonePhotoCoordinator.d.ts +26 -2
  213. package/build/services/PhonePhotoCoordinator.d.ts.map +1 -1
  214. package/build/services/PhonePhotoCoordinator.js +79 -9
  215. package/build/services/PhonePhotoCoordinator.js.map +1 -1
  216. package/build/services/PhoneStreamCoordinator.d.ts +8 -1
  217. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  218. package/build/services/PhoneStreamCoordinator.js +125 -13
  219. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  220. package/build/services/PhoneVideoCoordinator.d.ts +2 -0
  221. package/build/services/PhoneVideoCoordinator.d.ts.map +1 -1
  222. package/build/services/PhoneVideoCoordinator.js +10 -0
  223. package/build/services/PhoneVideoCoordinator.js.map +1 -1
  224. package/build/services/SceneRenderer.d.ts.map +1 -1
  225. package/build/services/SceneRenderer.js +15 -0
  226. package/build/services/SceneRenderer.js.map +1 -1
  227. package/build/services/SentenceTtsPipeline.d.ts +31 -0
  228. package/build/services/SentenceTtsPipeline.d.ts.map +1 -0
  229. package/build/services/SentenceTtsPipeline.js +67 -0
  230. package/build/services/SentenceTtsPipeline.js.map +1 -0
  231. package/build/services/SupportProfileSync.d.ts +22 -0
  232. package/build/services/SupportProfileSync.d.ts.map +1 -0
  233. package/build/services/SupportProfileSync.js +213 -0
  234. package/build/services/SupportProfileSync.js.map +1 -0
  235. package/build/services/TranscriptionRouting.d.ts +12 -0
  236. package/build/services/TranscriptionRouting.d.ts.map +1 -0
  237. package/build/services/TranscriptionRouting.js +24 -0
  238. package/build/services/TranscriptionRouting.js.map +1 -0
  239. package/build/services/TtsTextSanitizer.d.ts +5 -0
  240. package/build/services/TtsTextSanitizer.d.ts.map +1 -0
  241. package/build/services/TtsTextSanitizer.js +61 -0
  242. package/build/services/TtsTextSanitizer.js.map +1 -0
  243. package/build/services/WifiAdbRequest.d.ts +17 -0
  244. package/build/services/WifiAdbRequest.d.ts.map +1 -0
  245. package/build/services/WifiAdbRequest.js +35 -0
  246. package/build/services/WifiAdbRequest.js.map +1 -0
  247. package/build/services/asg/asgCameraApi.d.ts +43 -0
  248. package/build/services/asg/asgCameraApi.d.ts.map +1 -1
  249. package/build/services/asg/asgCameraApi.js +509 -26
  250. package/build/services/asg/asgCameraApi.js.map +1 -1
  251. package/build/services/asg/cameraRollExportCoordinator.d.ts +60 -0
  252. package/build/services/asg/cameraRollExportCoordinator.d.ts.map +1 -0
  253. package/build/services/asg/cameraRollExportCoordinator.js +618 -0
  254. package/build/services/asg/cameraRollExportCoordinator.js.map +1 -0
  255. package/build/services/asg/cameraRollExportLedger.d.ts +44 -0
  256. package/build/services/asg/cameraRollExportLedger.d.ts.map +1 -0
  257. package/build/services/asg/cameraRollExportLedger.js +78 -0
  258. package/build/services/asg/cameraRollExportLedger.js.map +1 -0
  259. package/build/services/asg/galleryNotices.d.ts +1 -1
  260. package/build/services/asg/galleryNotices.d.ts.map +1 -1
  261. package/build/services/asg/galleryNotices.js.map +1 -1
  262. package/build/services/asg/galleryPathMigration.d.ts +3 -0
  263. package/build/services/asg/galleryPathMigration.d.ts.map +1 -0
  264. package/build/services/asg/galleryPathMigration.js +26 -0
  265. package/build/services/asg/galleryPathMigration.js.map +1 -0
  266. package/build/services/asg/gallerySettingsService.d.ts +1 -0
  267. package/build/services/asg/gallerySettingsService.d.ts.map +1 -1
  268. package/build/services/asg/gallerySettingsService.js +2 -0
  269. package/build/services/asg/gallerySettingsService.js.map +1 -1
  270. package/build/services/asg/gallerySyncService.d.ts.map +1 -1
  271. package/build/services/asg/gallerySyncService.js +162 -56
  272. package/build/services/asg/gallerySyncService.js.map +1 -1
  273. package/build/services/asg/galleryTransferLedger.d.ts +58 -0
  274. package/build/services/asg/galleryTransferLedger.d.ts.map +1 -0
  275. package/build/services/asg/galleryTransferLedger.js +225 -0
  276. package/build/services/asg/galleryTransferLedger.js.map +1 -0
  277. package/build/services/asg/localNetworkTransport.d.ts +15 -0
  278. package/build/services/asg/localNetworkTransport.d.ts.map +1 -0
  279. package/build/services/asg/localNetworkTransport.js +144 -0
  280. package/build/services/asg/localNetworkTransport.js.map +1 -0
  281. package/build/services/asg/localStorageService.d.ts +18 -0
  282. package/build/services/asg/localStorageService.d.ts.map +1 -1
  283. package/build/services/asg/localStorageService.js +168 -19
  284. package/build/services/asg/localStorageService.js.map +1 -1
  285. package/build/services/asg/mediaProcessingQueue.d.ts +10 -0
  286. package/build/services/asg/mediaProcessingQueue.d.ts.map +1 -1
  287. package/build/services/asg/mediaProcessingQueue.js +193 -62
  288. package/build/services/asg/mediaProcessingQueue.js.map +1 -1
  289. package/build/services/audioPlaybackAssets.d.ts +8 -0
  290. package/build/services/audioPlaybackAssets.d.ts.map +1 -0
  291. package/build/services/audioPlaybackAssets.js +7 -0
  292. package/build/services/audioPlaybackAssets.js.map +1 -0
  293. package/build/services/gallerySyncClock.js +1 -1
  294. package/build/services/gallerySyncClock.js.map +1 -1
  295. package/build/services/glassesClockSync.d.ts.map +1 -1
  296. package/build/services/glassesClockSync.js +6 -2
  297. package/build/services/glassesClockSync.js.map +1 -1
  298. package/build/services/manifestActions.d.ts +7 -0
  299. package/build/services/manifestActions.d.ts.map +1 -0
  300. package/build/services/manifestActions.js +25 -0
  301. package/build/services/manifestActions.js.map +1 -0
  302. package/build/services/otaDisplayState.d.ts +23 -12
  303. package/build/services/otaDisplayState.d.ts.map +1 -1
  304. package/build/services/otaDisplayState.js +36 -14
  305. package/build/services/otaDisplayState.js.map +1 -1
  306. package/build/services/otaInstallPolicy.d.ts +16 -4
  307. package/build/services/otaInstallPolicy.d.ts.map +1 -1
  308. package/build/services/otaInstallPolicy.js +24 -3
  309. package/build/services/otaInstallPolicy.js.map +1 -1
  310. package/build/services/otaLegacyMapping.d.ts +1 -1
  311. package/build/services/otaLegacyMapping.d.ts.map +1 -1
  312. package/build/services/otaLegacyMapping.js.map +1 -1
  313. package/build/services/otaManifestPolicy.d.ts +14 -0
  314. package/build/services/otaManifestPolicy.d.ts.map +1 -0
  315. package/build/services/otaManifestPolicy.js +19 -0
  316. package/build/services/otaManifestPolicy.js.map +1 -0
  317. package/build/services/otaManifestUrl.d.ts +3 -0
  318. package/build/services/otaManifestUrl.d.ts.map +1 -0
  319. package/build/services/otaManifestUrl.js +38 -0
  320. package/build/services/otaManifestUrl.js.map +1 -0
  321. package/build/services/slimStreamStatus.d.ts +11 -0
  322. package/build/services/slimStreamStatus.d.ts.map +1 -1
  323. package/build/services/slimStreamStatus.js +14 -4
  324. package/build/services/slimStreamStatus.js.map +1 -1
  325. package/build/stores/apps.d.ts.map +1 -1
  326. package/build/stores/apps.js +11 -0
  327. package/build/stores/apps.js.map +1 -1
  328. package/build/stores/bluetoothSettingKeys.d.ts.map +1 -1
  329. package/build/stores/bluetoothSettingKeys.js +2 -0
  330. package/build/stores/bluetoothSettingKeys.js.map +1 -1
  331. package/build/stores/gallerySync.d.ts +2 -0
  332. package/build/stores/gallerySync.d.ts.map +1 -1
  333. package/build/stores/gallerySync.js +2 -0
  334. package/build/stores/gallerySync.js.map +1 -1
  335. package/build/stores/glasses.d.ts.map +1 -1
  336. package/build/stores/glasses.js +3 -0
  337. package/build/stores/glasses.js.map +1 -1
  338. package/build/stores/settings.d.ts +17 -0
  339. package/build/stores/settings.d.ts.map +1 -1
  340. package/build/stores/settings.js +157 -17
  341. package/build/stores/settings.js.map +1 -1
  342. package/build/types/applet.d.ts +2 -0
  343. package/build/types/applet.d.ts.map +1 -1
  344. package/build/types/applet.js.map +1 -1
  345. package/build/types/asg.d.ts +7 -0
  346. package/build/types/asg.d.ts.map +1 -1
  347. package/build/types/asg.js.map +1 -1
  348. package/build/types/enums.d.ts +2 -25
  349. package/build/types/enums.d.ts.map +1 -1
  350. package/build/types/enums.js +2 -27
  351. package/build/types/enums.js.map +1 -1
  352. package/build/types/hardware.d.ts +4 -13
  353. package/build/types/hardware.d.ts.map +1 -1
  354. package/build/types/hardware.js +15 -3
  355. package/build/types/hardware.js.map +1 -1
  356. package/build/types/index.d.ts +2 -6
  357. package/build/types/index.d.ts.map +1 -1
  358. package/build/types/index.js +2 -6
  359. package/build/types/index.js.map +1 -1
  360. package/build/utils/configuredDevHost.d.ts +6 -0
  361. package/build/utils/configuredDevHost.d.ts.map +1 -0
  362. package/build/utils/configuredDevHost.js +19 -0
  363. package/build/utils/configuredDevHost.js.map +1 -0
  364. package/build/utils/devMiniappLaunch.d.ts +19 -8
  365. package/build/utils/devMiniappLaunch.d.ts.map +1 -1
  366. package/build/utils/devMiniappLaunch.js +144 -21
  367. package/build/utils/devMiniappLaunch.js.map +1 -1
  368. package/build/utils/diagnosticContext.d.ts.map +1 -1
  369. package/build/utils/diagnosticContext.js +15 -17
  370. package/build/utils/diagnosticContext.js.map +1 -1
  371. package/build/utils/hardware/hardware.d.ts.map +1 -1
  372. package/build/utils/hardware/hardware.js +7 -3
  373. package/build/utils/hardware/hardware.js.map +1 -1
  374. package/build/utils/miniappDiagnostics.d.ts +64 -0
  375. package/build/utils/miniappDiagnostics.d.ts.map +1 -0
  376. package/build/utils/miniappDiagnostics.js +173 -0
  377. package/build/utils/miniappDiagnostics.js.map +1 -0
  378. package/build/utils/permissions/MediaLibraryPermissions.d.ts +11 -0
  379. package/build/utils/permissions/MediaLibraryPermissions.d.ts.map +1 -1
  380. package/build/utils/permissions/MediaLibraryPermissions.js +27 -4
  381. package/build/utils/permissions/MediaLibraryPermissions.js.map +1 -1
  382. package/build/utils/timers/timers.d.ts.map +1 -1
  383. package/build/utils/timers/timers.js +55 -18
  384. package/build/utils/timers/timers.js.map +1 -1
  385. package/package.json +59 -31
  386. package/src/assets/silence.wav +0 -0
  387. package/src/bluetooth-sdk/debug.ts +1 -0
  388. package/src/bluetooth-sdk/index.ts +2 -0
  389. package/src/bluetooth-sdk/photo-receiver.ts +2 -0
  390. package/src/bluetooth-sdk/react.ts +1 -0
  391. package/src/bluetooth-sdk/types.ts +1 -0
  392. package/src/engine.ts +9 -4
  393. package/src/facades/dev.ts +17 -12
  394. package/src/facades/display.ts +1 -1
  395. package/src/facades/gallery.ts +1 -1
  396. package/src/facades/glasses.ts +20 -7
  397. package/src/facades/glassesSettings.ts +5 -0
  398. package/src/facades/miniapps.ts +7 -0
  399. package/src/facades/ota.ts +23 -8
  400. package/src/facades/pairing.ts +15 -11
  401. package/src/facades/permissions.ts +14 -0
  402. package/src/facades/phoneNotifications.ts +4 -13
  403. package/src/facades/speech.ts +21 -7
  404. package/src/generated/releaseMetadata.ts +20 -0
  405. package/src/index.ts +39 -13
  406. package/src/react/MentraLiveOtaFlow.tsx +856 -0
  407. package/src/react/index.ts +7 -0
  408. package/src/react/useEngineSnapshot.ts +16 -0
  409. package/src/runtime/bootstrap.ts +33 -1
  410. package/src/runtime/scanQrSeam.ts +20 -0
  411. package/src/services/AppRegistry.ts +240 -149
  412. package/src/services/AudioCloudUplink.ts +23 -1
  413. package/src/services/AudioPlaybackService.ts +287 -38
  414. package/src/services/BlobStore.ts +22 -9
  415. package/src/services/CloudAudioSubscriptionSync.ts +13 -1
  416. package/src/services/CloudClientService.ts +96 -1
  417. package/src/services/DeviceEventRouter.ts +5 -5
  418. package/src/services/ForegroundLocationPermission.ts +24 -0
  419. package/src/services/GlassesSettingsSync.ts +9 -3
  420. package/src/services/GlassesStatusProjection.ts +16 -8
  421. package/src/services/HotspotOtaTransport.ts +184 -0
  422. package/src/services/HotspotShutdown.ts +41 -0
  423. package/src/services/LocalDisplayManager.ts +112 -21
  424. package/src/services/LocalMiniappRuntime.ts +967 -346
  425. package/src/services/LocalMiniappStorage.ts +82 -0
  426. package/src/services/LocalMiniappUserIdentity.ts +46 -0
  427. package/src/services/LocalSttFallbackCoordinator.ts +19 -12
  428. package/src/services/MicStateCoordinator.ts +169 -4
  429. package/src/services/MiniappLauncher.ts +30 -3
  430. package/src/services/MiniappLiveness.ts +24 -0
  431. package/src/services/NativeHttpResponse.ts +6 -0
  432. package/src/services/OtaArtifactDownloader.ts +312 -0
  433. package/src/services/OtaAutoChain.ts +106 -0
  434. package/src/services/OtaErrorMapping.ts +80 -0
  435. package/src/services/OtaInstallCoordinator.ts +649 -144
  436. package/src/services/OtaService.ts +11 -5
  437. package/src/services/OtaUpdateCheckService.ts +181 -21
  438. package/src/services/PhoneCalendarHelpers.ts +212 -0
  439. package/src/services/PhoneCalendarService.ts +65 -0
  440. package/src/services/PhoneCameraFovCoordinator.ts +210 -0
  441. package/src/services/PhoneNotificationsSync.ts +12 -10
  442. package/src/services/PhonePhotoCoordinator.ts +116 -18
  443. package/src/services/PhoneStreamCoordinator.ts +139 -25
  444. package/src/services/PhoneVideoCoordinator.ts +11 -0
  445. package/src/services/SceneRenderer.ts +16 -0
  446. package/src/services/SentenceTtsPipeline.ts +106 -0
  447. package/src/services/SupportProfileSync.ts +218 -0
  448. package/src/services/TranscriptionRouting.ts +37 -0
  449. package/src/services/TtsTextSanitizer.ts +66 -0
  450. package/src/services/WifiAdbRequest.ts +53 -0
  451. package/src/services/asg/asgCameraApi.ts +615 -33
  452. package/src/services/asg/cameraRollExportCoordinator.ts +627 -0
  453. package/src/services/asg/cameraRollExportLedger.ts +122 -0
  454. package/src/services/asg/galleryNotices.ts +1 -0
  455. package/src/services/asg/galleryPathMigration.ts +22 -0
  456. package/src/services/asg/gallerySettingsService.ts +3 -0
  457. package/src/services/asg/gallerySyncService.ts +184 -73
  458. package/src/services/asg/galleryTransferLedger.ts +312 -0
  459. package/src/services/asg/localNetworkTransport.ts +169 -0
  460. package/src/services/asg/localStorageService.ts +179 -24
  461. package/src/services/asg/mediaProcessingQueue.ts +209 -62
  462. package/src/services/audioPlaybackAssets.ts +8 -0
  463. package/src/services/gallerySyncClock.ts +1 -1
  464. package/src/services/glassesClockSync.ts +6 -2
  465. package/src/services/manifestActions.ts +30 -0
  466. package/src/services/otaDisplayState.ts +58 -17
  467. package/src/services/otaInstallPolicy.ts +30 -4
  468. package/src/services/otaLegacyMapping.ts +1 -1
  469. package/src/services/otaManifestPolicy.ts +42 -0
  470. package/src/services/otaManifestUrl.ts +44 -0
  471. package/src/services/slimStreamStatus.ts +16 -5
  472. package/src/stores/apps.ts +12 -0
  473. package/src/stores/bluetoothSettingKeys.ts +2 -0
  474. package/src/stores/gallerySync.ts +4 -0
  475. package/src/stores/glasses.ts +3 -0
  476. package/src/stores/settings.ts +179 -18
  477. package/src/types/applet.ts +2 -0
  478. package/src/types/asg.ts +7 -0
  479. package/src/types/enums.ts +3 -28
  480. package/src/types/hardware.ts +20 -13
  481. package/src/types/index.ts +2 -6
  482. package/src/utils/configuredDevHost.ts +17 -0
  483. package/src/utils/devMiniappLaunch.ts +151 -23
  484. package/src/utils/diagnosticContext.ts +17 -17
  485. package/src/utils/hardware/hardware.ts +7 -9
  486. package/src/utils/miniappDiagnostics.ts +226 -0
  487. package/src/utils/permissions/MediaLibraryPermissions.ts +35 -4
  488. package/src/utils/timers/timers.ts +57 -21
  489. package/build/devtools.d.ts +0 -11
  490. package/build/devtools.d.ts.map +0 -1
  491. package/build/devtools.js +0 -13
  492. package/build/devtools.js.map +0 -1
  493. package/build/internal.d.ts +0 -66
  494. package/build/internal.d.ts.map +0 -1
  495. package/build/internal.js +0 -89
  496. package/build/internal.js.map +0 -1
  497. package/build/services/asgOtaVersionUrl.d.ts +0 -2
  498. package/build/services/asgOtaVersionUrl.d.ts.map +0 -1
  499. package/build/services/asgOtaVersionUrl.js +0 -36
  500. package/build/services/asgOtaVersionUrl.js.map +0 -1
  501. package/src/devtools.ts +0 -13
  502. package/src/internal.ts +0 -158
  503. package/src/services/asgOtaVersionUrl.ts +0 -41
@@ -7,10 +7,13 @@ import { BgTimer } from "../../utils/timers";
7
7
  import { localStorageService } from "./localStorageService";
8
8
  import { validateDownloadedMediaFile } from "./galleryMediaValidation";
9
9
  import { reportInvalidGalleryMedia } from "./GalleryMediaIntegrityReportService";
10
+ import { galleryTransferLedger } from "./galleryTransferLedger";
11
+ import { localNetworkTransport } from "./localNetworkTransport";
10
12
  export class AsgCameraApiClient {
11
13
  baseUrl;
12
14
  port;
13
15
  lastRequestTime = 0;
16
+ galleryCapabilities;
14
17
  constructor(serverUrl, port = 8089) {
15
18
  this.port = port;
16
19
  this.baseUrl = serverUrl || `http://localhost:${port}`;
@@ -34,6 +37,7 @@ export class AsgCameraApiClient {
34
37
  if (this.baseUrl !== newUrl) {
35
38
  this.baseUrl = newUrl;
36
39
  this.port = newPort;
40
+ this.galleryCapabilities = undefined;
37
41
  // console.log(`[ASG Camera API] Server changed from ${oldUrl} to ${this.baseUrl}`)
38
42
  }
39
43
  else {
@@ -49,10 +53,9 @@ export class AsgCameraApiClient {
49
53
  /**
50
54
  * Rate limiting helper - ensures minimum delay between requests
51
55
  */
52
- async rateLimit() {
56
+ async rateLimit(minDelay = 500) {
53
57
  const now = Date.now();
54
58
  const timeSinceLastRequest = now - this.lastRequestTime;
55
- const minDelay = 500; // 500ms minimum delay between requests
56
59
  if (timeSinceLastRequest < minDelay) {
57
60
  const delay = minDelay - timeSinceLastRequest;
58
61
  console.log(`[ASG Camera API] Rate limiting: waiting ${delay}ms`);
@@ -63,7 +66,7 @@ export class AsgCameraApiClient {
63
66
  /**
64
67
  * Make a request to the ASG Camera Server with rate limiting and retry logic
65
68
  */
66
- async makeRequest(endpoint, options, retries = 5) {
69
+ async makeRequest(endpoint, options, retries = 5, writeIntervalMs = 500) {
67
70
  const url = `${this.baseUrl}${endpoint}`;
68
71
  const method = options?.method || "GET";
69
72
  console.log(`[ASG Camera API] makeRequest called with endpoint: ${endpoint}`);
@@ -79,8 +82,8 @@ export class AsgCameraApiClient {
79
82
  const startTime = Date.now();
80
83
  try {
81
84
  // Apply rate limiting only for non-GET requests
82
- if (method !== "GET") {
83
- await this.rateLimit();
85
+ if (method !== "GET" && writeIntervalMs > 0) {
86
+ await this.rateLimit(writeIntervalMs);
84
87
  }
85
88
  // Prepare headers - don't set Content-Type for GET requests
86
89
  const headers = {
@@ -96,7 +99,7 @@ export class AsgCameraApiClient {
96
99
  console.log(`[ASG Camera API] Making fetch request to: ${url}`);
97
100
  console.log(`[ASG Camera API] Headers being sent:`, headers);
98
101
  // N4: Add 30s timeout to all fetch calls in makeRequest
99
- const response = await fetch(url, {
102
+ const response = await localNetworkTransport.fetch(url, {
100
103
  headers,
101
104
  ...options,
102
105
  signal: options?.signal || this.createTimeoutSignal(30000),
@@ -117,7 +120,7 @@ export class AsgCameraApiClient {
117
120
  const retryDelay = Math.min(Math.pow(2, 6 - retries) * 1000, 10000);
118
121
  console.log(`[ASG Camera API] Rate limited, retrying in ${retryDelay}ms (${retries} retries left)`);
119
122
  await new Promise((resolve) => BgTimer.setTimeout(() => resolve(), retryDelay));
120
- return this.makeRequest(endpoint, options, retries - 1);
123
+ return this.makeRequest(endpoint, options, retries - 1, writeIntervalMs);
121
124
  }
122
125
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
123
126
  }
@@ -126,7 +129,12 @@ export class AsgCameraApiClient {
126
129
  console.log(`[ASG Camera API] Response content-type: ${contentType}`);
127
130
  if (contentType?.includes("application/json")) {
128
131
  const data = await response.json();
129
- console.log(`[ASG Camera API] JSON Response received:`, data);
132
+ console.log(`[ASG Camera API] JSON response received`, {
133
+ endpoint,
134
+ status: data?.status,
135
+ apiVersion: data?.data?.api_version,
136
+ captureCount: data?.data?.captures?.length,
137
+ });
130
138
  return data;
131
139
  }
132
140
  else if (contentType?.includes("image/") || contentType?.includes("application/octet-stream")) {
@@ -218,7 +226,7 @@ export class AsgCameraApiClient {
218
226
  // Use browser-like headers since we know the browser works
219
227
  try {
220
228
  console.log(`[ASG Camera API] Making direct fetch to gallery endpoint`);
221
- const response = await fetch(galleryUrl, {
229
+ const response = await localNetworkTransport.fetch(galleryUrl, {
222
230
  method: "GET",
223
231
  headers: {
224
232
  "Accept": "application/json",
@@ -338,7 +346,7 @@ export class AsgCameraApiClient {
338
346
  for (const endpoint of testEndpoints) {
339
347
  try {
340
348
  console.log(`[ASG Camera API] Testing endpoint: ${endpoint}`);
341
- const response = await fetch(`${this.baseUrl}${endpoint}`, {
349
+ const response = await localNetworkTransport.fetch(`${this.baseUrl}${endpoint}`, {
342
350
  method: "HEAD",
343
351
  headers: {
344
352
  "Accept": "*/*",
@@ -360,7 +368,7 @@ export class AsgCameraApiClient {
360
368
  if (endpoint === "/api/gallery") {
361
369
  try {
362
370
  console.log(`[ASG Camera API] Trying GET request for /api/gallery...`);
363
- const getResponse = await fetch(`${this.baseUrl}${endpoint}`, {
371
+ const getResponse = await localNetworkTransport.fetch(`${this.baseUrl}${endpoint}`, {
364
372
  method: "GET",
365
373
  headers: {
366
374
  "Accept": "application/json",
@@ -454,7 +462,7 @@ export class AsgCameraApiClient {
454
462
  // Use a simple HEAD request to check reachability
455
463
  const controller = new AbortController();
456
464
  const timeoutId = BgTimer.setTimeout(() => controller.abort(), 3000); // 3 second timeout
457
- const response = await fetch(`${this.baseUrl}/api/health`, {
465
+ const response = await localNetworkTransport.fetch(`${this.baseUrl}/api/health`, {
458
466
  method: "HEAD",
459
467
  signal: controller.signal,
460
468
  });
@@ -502,6 +510,90 @@ export class AsgCameraApiClient {
502
510
  });
503
511
  return response;
504
512
  }
513
+ /** Probe the additive gallery protocol. A 404 cleanly falls back to legacy v2. */
514
+ async getGalleryCapabilities() {
515
+ if (this.galleryCapabilities !== undefined)
516
+ return this.galleryCapabilities;
517
+ try {
518
+ const response = await localNetworkTransport.fetch(`${this.baseUrl}/api/v3/capabilities`, {
519
+ headers: { "Accept": "application/json", "User-Agent": "MentraOS-Mobile/1.0" },
520
+ signal: this.createTimeoutSignal(5000),
521
+ });
522
+ if (response.status === 404) {
523
+ this.galleryCapabilities = null;
524
+ return null;
525
+ }
526
+ if (!response.ok)
527
+ throw new Error(`HTTP ${response.status}`);
528
+ const payload = await response.json();
529
+ const capabilities = (payload.data || payload);
530
+ this.galleryCapabilities = capabilities.api_version >= 3 ? capabilities : null;
531
+ return this.galleryCapabilities;
532
+ }
533
+ catch (error) {
534
+ // A failed probe must not break old firmware. Do not permanently cache transient failures.
535
+ console.warn("[ASG Camera API] v3 capability probe failed; using legacy sync for this attempt", error);
536
+ return null;
537
+ }
538
+ }
539
+ /** Fetch all lightweight v3 manifest pages, newest first. */
540
+ async getV3Manifest() {
541
+ const capabilities = await this.getGalleryCapabilities();
542
+ if (!capabilities)
543
+ return null;
544
+ const captures = [];
545
+ let cursor;
546
+ let serverTime = Date.now();
547
+ let totalCount = 0;
548
+ do {
549
+ const params = new URLSearchParams({ limit: String(capabilities.max_manifest_page_size || 100) });
550
+ if (cursor)
551
+ params.set("cursor", cursor);
552
+ const response = await this.makeRequest(`/api/v3/manifest?${params.toString()}`);
553
+ const data = response.data || response;
554
+ captures.push(...(data.captures || []));
555
+ serverTime = data.server_time || serverTime;
556
+ totalCount = data.total_count || captures.length;
557
+ cursor = data.has_more && data.next_cursor ? data.next_cursor : undefined;
558
+ } while (cursor);
559
+ return {
560
+ api_version: 3,
561
+ captures,
562
+ has_more: false,
563
+ next_cursor: null,
564
+ total_count: totalCount,
565
+ server_time: serverTime,
566
+ };
567
+ }
568
+ async getV3FileHash(fileName) {
569
+ const response = await localNetworkTransport.fetch(`${this.baseUrl}/api/v3/hash?file=${encodeURIComponent(fileName)}`, {
570
+ headers: { "Accept": "application/json", "User-Agent": "MentraOS-Mobile/1.0" },
571
+ signal: this.createTimeoutSignal(10 * 60 * 1000),
572
+ }, 10 * 60 * 1000);
573
+ if (!response.ok)
574
+ throw new Error(`Hash request failed: HTTP ${response.status}`);
575
+ const payload = await response.json();
576
+ return (payload.data || payload);
577
+ }
578
+ async acknowledgeCapture(captureId, ackId) {
579
+ const response = await this.makeRequest("/api/v3/ack", {
580
+ method: "POST",
581
+ body: JSON.stringify({ capture_id: captureId, ack_id: ackId }),
582
+ }, 5, 0);
583
+ const data = response.data || response;
584
+ if (!data.success && !data.already_trashed) {
585
+ throw new Error(data.message || `Glasses did not acknowledge ${captureId}`);
586
+ }
587
+ }
588
+ async restoreCapture(captureId) {
589
+ const response = await this.makeRequest("/api/v3/restore", {
590
+ method: "POST",
591
+ body: JSON.stringify({ capture_id: captureId }),
592
+ }, 5, 0);
593
+ const data = response.data || response;
594
+ if (!data.success)
595
+ throw new Error(data.message || `Glasses did not restore ${captureId}`);
596
+ }
505
597
  /**
506
598
  * Batch sync files from server with controlled concurrency.
507
599
  * Used by the legacy executeDownload path for old asg_client firmware
@@ -572,11 +664,6 @@ export class AsgCameraApiClient {
572
664
  results.failed.push(result.error);
573
665
  }
574
666
  }
575
- // Small delay between batches to prevent overwhelming the server
576
- if (i + CONCURRENCY_LIMIT < files.length) {
577
- console.log(`[ASG Camera API] Waiting 300ms between batches`);
578
- await new Promise((resolve) => BgTimer.setTimeout(() => resolve(), 300));
579
- }
580
667
  }
581
668
  console.log(`[ASG Camera API] Batch sync completed: ${results.downloaded.length} downloaded, ${results.failed.length} failed`);
582
669
  return results;
@@ -585,7 +672,287 @@ export class AsgCameraApiClient {
585
672
  * Download all files in a capture group sequentially.
586
673
  * Reports aggregate byte progress across all files in the group.
587
674
  */
675
+ async downloadResumableCaptureFile(captureId, file, localFilePath, onProgress, abortSignal) {
676
+ const segmentBytes = 16 * 1024 * 1024;
677
+ const segmentCount = Math.max(1, Math.ceil(file.size / segmentBytes));
678
+ const generation = (file.etag || `${file.size}`).replace(/[^A-Za-z0-9_-]/g, "").slice(0, 48) || "legacy";
679
+ const partPath = (index) => `${localFilePath}.partial.${generation}.${index}`;
680
+ const assemblingPath = `${localFilePath}.assembling.${generation}`;
681
+ const transferStartedAt = Date.now();
682
+ let networkDurationMs = 0;
683
+ let networkBytes = 0;
684
+ if (await RNFS.exists(localFilePath)) {
685
+ const existing = await RNFS.stat(localFilePath);
686
+ if (existing.size === file.size) {
687
+ if (file.etag) {
688
+ const remoteHash = file.sha256 ? { sha256: file.sha256, etag: file.etag } : await this.getV3FileHash(file.name);
689
+ if (remoteHash.etag && remoteHash.etag !== file.etag) {
690
+ throw new Error("Source generation changed before existing-file verification");
691
+ }
692
+ const localHash = await RNFS.hash(localFilePath, "sha256");
693
+ if (localHash.toLowerCase() === remoteHash.sha256.toLowerCase()) {
694
+ galleryTransferLedger.markFileHash(captureId, file.name, localHash.toLowerCase());
695
+ onProgress?.(file.size);
696
+ return;
697
+ }
698
+ console.warn(`[ASG Camera API] Existing file failed SHA-256 verification; replacing ${file.name}`);
699
+ }
700
+ else {
701
+ // Legacy servers have no generation/hash endpoint. Preserve old-client
702
+ // compatibility and let the media validator below reject corrupt data.
703
+ onProgress?.(file.size);
704
+ return;
705
+ }
706
+ }
707
+ await RNFS.moveFile(localFilePath, `${localFilePath}.recovery.${Date.now()}`).catch(() => { });
708
+ }
709
+ let completedBytes = 0;
710
+ let resumedSegmentCount = 0;
711
+ for (let index = 0; index < segmentCount; index++) {
712
+ const start = index * segmentBytes;
713
+ const expectedLength = Math.min(segmentBytes, file.size - start);
714
+ if (await RNFS.exists(partPath(index))) {
715
+ const stat = await RNFS.stat(partPath(index));
716
+ if (stat.size === expectedLength) {
717
+ completedBytes += expectedLength;
718
+ resumedSegmentCount++;
719
+ galleryTransferLedger.markSegmentComplete(captureId, file.name, index);
720
+ continue;
721
+ }
722
+ await RNFS.unlink(partPath(index)).catch(() => { });
723
+ }
724
+ let completed = false;
725
+ let lastError;
726
+ for (let attempt = 0; attempt < 5 && !completed; attempt++) {
727
+ if (abortSignal?.aborted)
728
+ throw new Error("Sync cancelled");
729
+ await RNFS.unlink(partPath(index)).catch(() => { });
730
+ const end = start + expectedLength - 1;
731
+ let responseHeaders = {};
732
+ const { jobId, promise } = localNetworkTransport.downloadFile({
733
+ fromUrl: `${this.baseUrl}/api/download?file=${encodeURIComponent(file.name)}`,
734
+ toFile: partPath(index),
735
+ headers: {
736
+ "Accept": "*/*",
737
+ "User-Agent": "MentraOS-Mobile/1.0",
738
+ "Range": `bytes=${start}-${end}`,
739
+ ...(file.etag ? { "If-Range": file.etag } : {}),
740
+ },
741
+ connectionTimeout: 300000,
742
+ readTimeout: 300000,
743
+ backgroundTimeout: 24 * 60 * 60 * 1000,
744
+ progressDivider: 2,
745
+ progressInterval: 250,
746
+ begin: (response) => {
747
+ responseHeaders = response.headers || {};
748
+ },
749
+ progress: (progress) => onProgress?.(completedBytes + (progress.bytesWritten || 0)),
750
+ });
751
+ let abortPollTimer;
752
+ if (abortSignal) {
753
+ abortPollTimer = BgTimer.setInterval(() => {
754
+ if (abortSignal.aborted)
755
+ localNetworkTransport.stopDownload(jobId);
756
+ }, 500);
757
+ }
758
+ const attemptStartedAt = Date.now();
759
+ try {
760
+ const result = await promise;
761
+ const attemptDurationMs = Math.max(1, Date.now() - attemptStartedAt);
762
+ networkDurationMs += attemptDurationMs;
763
+ networkBytes += result.bytesWritten || 0;
764
+ if (abortSignal?.aborted)
765
+ throw new Error("Sync cancelled");
766
+ const header = (name) => {
767
+ const key = Object.keys(responseHeaders).find((candidate) => candidate.toLowerCase() === name.toLowerCase());
768
+ return key ? responseHeaders[key] : undefined;
769
+ };
770
+ if (result.statusCode === 200 && index === 0 && result.bytesWritten === file.size) {
771
+ // Older servers ignore Range. A full fixed/chunked response is still compatible;
772
+ // commit it through the same staging path and skip the remaining segments.
773
+ await RNFS.unlink(assemblingPath).catch(() => { });
774
+ await RNFS.moveFile(partPath(index), assemblingPath);
775
+ completedBytes = file.size;
776
+ completed = true;
777
+ break;
778
+ }
779
+ if (result.statusCode !== 206)
780
+ throw new Error(`HTTP ${result.statusCode}; expected 206`);
781
+ if (result.bytesWritten !== expectedLength) {
782
+ throw new Error(`Segment ${index} truncated: ${result.bytesWritten}/${expectedLength} bytes`);
783
+ }
784
+ const contentRange = header("content-range");
785
+ if (contentRange !== `bytes ${start}-${end}/${file.size}`) {
786
+ throw new Error(`Invalid Content-Range: ${contentRange || "missing"}`);
787
+ }
788
+ const responseEtag = header("etag");
789
+ if (file.etag && responseEtag && responseEtag !== file.etag) {
790
+ throw new Error("Source generation changed during transfer");
791
+ }
792
+ galleryTransferLedger.markSegmentComplete(captureId, file.name, index);
793
+ completedBytes += expectedLength;
794
+ completed = true;
795
+ console.log("[ASG Camera API] Gallery segment complete", {
796
+ captureId,
797
+ file: file.name,
798
+ segment: index,
799
+ segmentCount,
800
+ attempt: attempt + 1,
801
+ status: result.statusCode,
802
+ bytes: result.bytesWritten,
803
+ durationMs: attemptDurationMs,
804
+ throughputMbps: (((result.bytesWritten || 0) * 8) / attemptDurationMs / 1000).toFixed(2),
805
+ etag: file.etag,
806
+ });
807
+ }
808
+ catch (error) {
809
+ lastError = error;
810
+ if (error instanceof Error && error.message === "Sync cancelled")
811
+ throw error;
812
+ console.warn("[ASG Camera API] Gallery segment attempt failed", {
813
+ captureId,
814
+ file: file.name,
815
+ segment: index,
816
+ segmentCount,
817
+ attempt: attempt + 1,
818
+ rangeStart: start,
819
+ rangeEnd: end,
820
+ message: error instanceof Error ? error.message : String(error),
821
+ });
822
+ await RNFS.unlink(partPath(index)).catch(() => { });
823
+ if (attempt < 4) {
824
+ const delay = Math.min(1000 * 2 ** attempt, 10000) + Math.floor(Math.random() * 500);
825
+ await new Promise((resolve) => BgTimer.setTimeout(resolve, delay));
826
+ }
827
+ }
828
+ finally {
829
+ if (abortPollTimer !== undefined)
830
+ BgTimer.clearInterval(abortPollTimer);
831
+ }
832
+ }
833
+ if (!completed)
834
+ throw lastError || new Error(`Segment ${index} failed`);
835
+ if (completedBytes === file.size && (await RNFS.exists(assemblingPath)))
836
+ break;
837
+ }
838
+ if (!(await RNFS.exists(assemblingPath))) {
839
+ await RNFS.unlink(assemblingPath).catch(() => { });
840
+ await RNFS.writeFile(assemblingPath, "", "utf8");
841
+ for (let index = 0; index < segmentCount; index++) {
842
+ const data = await RNFS.readFile(partPath(index), "base64");
843
+ await RNFS.appendFile(assemblingPath, data, "base64");
844
+ }
845
+ }
846
+ const assembledStat = await RNFS.stat(assemblingPath);
847
+ if (assembledStat.size !== file.size) {
848
+ await RNFS.moveFile(assemblingPath, `${assemblingPath}.invalid.${Date.now()}`).catch(() => { });
849
+ throw new Error(`Assembled file has ${assembledStat.size}/${file.size} bytes`);
850
+ }
851
+ const verificationStartedAt = Date.now();
852
+ const capabilities = await this.getGalleryCapabilities();
853
+ if (capabilities?.sha256) {
854
+ const remoteHash = file.sha256 ? { sha256: file.sha256, etag: file.etag } : await this.getV3FileHash(file.name);
855
+ if (file.etag && remoteHash.etag && file.etag !== remoteHash.etag) {
856
+ throw new Error("Source generation changed before integrity verification");
857
+ }
858
+ const localHash = await RNFS.hash(assemblingPath, "sha256");
859
+ if (localHash.toLowerCase() !== remoteHash.sha256.toLowerCase()) {
860
+ await RNFS.moveFile(assemblingPath, `${assemblingPath}.invalid.${Date.now()}`).catch(() => { });
861
+ throw new Error(`SHA-256 mismatch for ${file.name}`);
862
+ }
863
+ galleryTransferLedger.markFileHash(captureId, file.name, localHash.toLowerCase());
864
+ }
865
+ await RNFS.moveFile(assemblingPath, localFilePath);
866
+ for (let index = 0; index < segmentCount; index++)
867
+ await RNFS.unlink(partPath(index)).catch(() => { });
868
+ console.log("[ASG Camera API] Gallery file committed", {
869
+ captureId,
870
+ file: file.name,
871
+ bytes: file.size,
872
+ segments: segmentCount,
873
+ networkDurationMs,
874
+ networkMbps: networkDurationMs > 0 ? ((networkBytes * 8) / networkDurationMs / 1000).toFixed(2) : "0.00",
875
+ verificationDurationMs: Date.now() - verificationStartedAt,
876
+ totalDurationMs: Date.now() - transferStartedAt,
877
+ resumedSegments: resumedSegmentCount,
878
+ });
879
+ onProgress?.(file.size);
880
+ }
588
881
  async downloadCapture(capture, onProgress, abortSignal) {
882
+ if (capture.files.some((file) => !file.size || file.size <= 0)) {
883
+ // Preserve support for malformed manifests from pre-v2 development firmware.
884
+ return this.downloadCaptureLegacyUnsafe(capture, onProgress, abortSignal);
885
+ }
886
+ const captureDir = localStorageService.getPhotoFilePath(capture.capture_id);
887
+ if (!(await RNFS.exists(captureDir)))
888
+ await RNFS.mkdir(captureDir);
889
+ const capabilities = await this.getGalleryCapabilities();
890
+ galleryTransferLedger.ensureCapture(capture, capabilities?.api_version || 2);
891
+ galleryTransferLedger.transition(capture.capture_id, "TRANSFERRING");
892
+ const sortedFiles = [...capture.files].sort((a, b) => {
893
+ const order = { bracket: 0, primary: 1, sidecar: 2 };
894
+ return (order[a.role] ?? 1) - (order[b.role] ?? 1);
895
+ });
896
+ let completedCaptureBytes = 0;
897
+ let primaryPath = "";
898
+ const bracketPaths = [];
899
+ let sidecarPath;
900
+ try {
901
+ for (const file of sortedFiles) {
902
+ const leafName = file.name.includes("/") ? file.name.split("/").pop() : file.name;
903
+ const localFilePath = `${captureDir}/${leafName}`;
904
+ await this.downloadResumableCaptureFile(capture.capture_id, file, localFilePath, (fileBytes) => onProgress?.(completedCaptureBytes + fileBytes, capture.total_size), abortSignal);
905
+ const isVideo = !!file.name.match(/\.(mp4|mov|avi|webm|mkv)$/i);
906
+ const mediaKind = file.role === "sidecar" ? "unknown" : isVideo ? "video" : "photo";
907
+ try {
908
+ await validateDownloadedMediaFile({
909
+ path: localFilePath,
910
+ name: file.name,
911
+ expectedSize: file.size,
912
+ mediaKind,
913
+ });
914
+ }
915
+ catch (validationError) {
916
+ reportInvalidGalleryMedia({
917
+ name: file.name,
918
+ path: localFilePath,
919
+ mediaKind,
920
+ stage: "download_capture_validation",
921
+ reason: validationError?.message || String(validationError),
922
+ expectedSize: file.size,
923
+ captureId: capture.capture_id,
924
+ duration: capture.duration,
925
+ });
926
+ await RNFS.moveFile(localFilePath, `${localFilePath}.invalid.${Date.now()}`).catch(() => { });
927
+ throw validationError;
928
+ }
929
+ completedCaptureBytes += file.size;
930
+ if (file.role === "primary")
931
+ primaryPath = localFilePath;
932
+ else if (file.role === "bracket")
933
+ bracketPaths.push(localFilePath);
934
+ else if (file.role === "sidecar")
935
+ sidecarPath = localFilePath;
936
+ }
937
+ if (!primaryPath && sortedFiles.length > 0) {
938
+ const leafName = sortedFiles[0].name.includes("/") ? sortedFiles[0].name.split("/").pop() : sortedFiles[0].name;
939
+ primaryPath = `${captureDir}/${leafName}`;
940
+ }
941
+ const primaryFile = sortedFiles.find((file) => file.role === "primary") || sortedFiles[0];
942
+ const thumbnailPath = capture.type === "video" && primaryFile
943
+ ? await this.downloadVideoThumbnail(primaryFile.name, `${captureDir}/.thumb.jpg`, abortSignal)
944
+ : undefined;
945
+ galleryTransferLedger.transition(capture.capture_id, "VERIFIED");
946
+ onProgress?.(capture.total_size, capture.total_size);
947
+ return { captureDir, primaryPath, bracketPaths, sidecarPath, thumbnailPath };
948
+ }
949
+ catch (error) {
950
+ galleryTransferLedger.recordFailure(capture.capture_id, error);
951
+ throw error;
952
+ }
953
+ }
954
+ /** Previous direct-to-final implementation kept for binary-source compatibility only. */
955
+ async downloadCaptureLegacyUnsafe(capture, onProgress, abortSignal) {
589
956
  const captureDir = localStorageService.getPhotoFilePath(capture.capture_id);
590
957
  console.log(`[ASG Camera API] downloadCapture: ${capture.capture_id} (${capture.files.length} files) -> ${captureDir}`);
591
958
  // Ensure capture directory exists
@@ -616,7 +983,7 @@ export class AsgCameraApiClient {
616
983
  try {
617
984
  // Throttle progress: only fire when bytesWritten changes meaningfully
618
985
  let lastReportedBytes = -1;
619
- const { jobId, promise: dlPromise } = RNFS.downloadFile({
986
+ const { jobId, promise: dlPromise } = localNetworkTransport.downloadFile({
620
987
  fromUrl: downloadUrl,
621
988
  toFile: localFilePath,
622
989
  headers: {
@@ -640,12 +1007,12 @@ export class AsgCameraApiClient {
640
1007
  let abortPollTimer;
641
1008
  if (abortSignal) {
642
1009
  if (abortSignal.aborted) {
643
- RNFS.stopDownload(jobId);
1010
+ localNetworkTransport.stopDownload(jobId);
644
1011
  throw new Error("Sync cancelled");
645
1012
  }
646
1013
  abortPollTimer = BgTimer.setInterval(() => {
647
1014
  if (abortSignal.aborted) {
648
- RNFS.stopDownload(jobId);
1015
+ localNetworkTransport.stopDownload(jobId);
649
1016
  }
650
1017
  }, 500);
651
1018
  }
@@ -720,7 +1087,69 @@ export class AsgCameraApiClient {
720
1087
  if (onProgress) {
721
1088
  onProgress(totalBytes, totalBytes);
722
1089
  }
723
- return { captureDir, primaryPath, bracketPaths, sidecarPath };
1090
+ const primaryFile = sortedFiles.find((file) => file.role === "primary") || sortedFiles[0];
1091
+ const thumbnailPath = capture.type === "video" && primaryFile
1092
+ ? await this.downloadVideoThumbnail(primaryFile.name, `${captureDir}/.thumb.jpg`, abortSignal)
1093
+ : undefined;
1094
+ return { captureDir, primaryPath, bracketPaths, sidecarPath, thumbnailPath };
1095
+ }
1096
+ /** Download an on-demand video thumbnail while the source capture is still on the glasses. */
1097
+ async downloadVideoThumbnail(sourceFileName, thumbnailPath, abortSignal) {
1098
+ const partialPath = `${thumbnailPath}.partial`;
1099
+ const parentDir = thumbnailPath.substring(0, thumbnailPath.lastIndexOf("/"));
1100
+ try {
1101
+ if (parentDir && !(await RNFS.exists(parentDir))) {
1102
+ await RNFS.mkdir(parentDir);
1103
+ }
1104
+ await RNFS.unlink(partialPath).catch(() => { });
1105
+ const { jobId, promise } = localNetworkTransport.downloadFile({
1106
+ fromUrl: `${this.baseUrl}/api/photo?file=${encodeURIComponent(sourceFileName)}`,
1107
+ toFile: partialPath,
1108
+ headers: {
1109
+ "Accept": "image/jpeg",
1110
+ "User-Agent": "MentraOS-Mobile/1.0",
1111
+ },
1112
+ connectionTimeout: 60000,
1113
+ readTimeout: 60000,
1114
+ progressDivider: 10,
1115
+ });
1116
+ let abortPollTimer;
1117
+ if (abortSignal) {
1118
+ if (abortSignal.aborted) {
1119
+ localNetworkTransport.stopDownload(jobId);
1120
+ throw new Error("Sync cancelled");
1121
+ }
1122
+ abortPollTimer = BgTimer.setInterval(() => {
1123
+ if (abortSignal.aborted)
1124
+ localNetworkTransport.stopDownload(jobId);
1125
+ }, 500);
1126
+ }
1127
+ let result;
1128
+ try {
1129
+ result = await promise;
1130
+ }
1131
+ finally {
1132
+ if (abortPollTimer !== undefined)
1133
+ BgTimer.clearInterval(abortPollTimer);
1134
+ }
1135
+ if (abortSignal?.aborted)
1136
+ throw new Error("Sync cancelled");
1137
+ if (result.statusCode !== 200 || result.bytesWritten <= 0) {
1138
+ throw new Error(`HTTP ${result.statusCode}, ${result.bytesWritten} bytes`);
1139
+ }
1140
+ await RNFS.unlink(thumbnailPath).catch(() => { });
1141
+ await RNFS.moveFile(partialPath, thumbnailPath);
1142
+ console.log(`[ASG Camera API] Downloaded video thumbnail for ${sourceFileName}`);
1143
+ return thumbnailPath;
1144
+ }
1145
+ catch (error) {
1146
+ await RNFS.unlink(partialPath).catch(() => { });
1147
+ if (abortSignal?.aborted || (error instanceof Error && error.message === "Sync cancelled")) {
1148
+ throw new Error("Sync cancelled");
1149
+ }
1150
+ console.warn(`[ASG Camera API] Failed to download video thumbnail for ${sourceFileName}:`, error);
1151
+ return undefined;
1152
+ }
724
1153
  }
725
1154
  /**
726
1155
  * Delete files from server
@@ -733,7 +1162,7 @@ export class AsgCameraApiClient {
733
1162
  const response = await this.makeRequest("/api/delete-files", {
734
1163
  method: "POST",
735
1164
  body: JSON.stringify({ files: fileNames }),
736
- });
1165
+ }, 5, 0);
737
1166
  // Parse the response format from the ASG server
738
1167
  const responseData = response;
739
1168
  if (responseData.data && responseData.data.results) {
@@ -770,6 +1199,60 @@ export class AsgCameraApiClient {
770
1199
  * Download a file from the server and save to filesystem
771
1200
  */
772
1201
  async downloadFile(filename, includeThumbnail = false, onProgress, abortSignal, expectedSize) {
1202
+ // The legacy public shape did not require a size. Keep that rare call compatible while all
1203
+ // sync manifests use the verified resumable path below.
1204
+ if (!expectedSize || expectedSize <= 0) {
1205
+ return this.downloadFileLegacyUnsafe(filename, includeThumbnail, onProgress, abortSignal, expectedSize);
1206
+ }
1207
+ const localFilePath = localStorageService.getPhotoFilePath(filename);
1208
+ const parentDir = localFilePath.substring(0, localFilePath.lastIndexOf("/"));
1209
+ if (!(await RNFS.exists(parentDir)))
1210
+ await RNFS.mkdir(parentDir);
1211
+ const lowerName = filename.toLowerCase();
1212
+ const isVideo = /\.(mp4|mov|avi|webm|mkv)$/.test(lowerName);
1213
+ const captureId = filename.includes("/")
1214
+ ? filename.substring(0, filename.indexOf("/"))
1215
+ : filename.replace(/\.imu\.json$/i, "").replace(/\.[^.]+$/, "");
1216
+ const capture = {
1217
+ capture_id: captureId,
1218
+ type: isVideo ? "video" : "photo",
1219
+ timestamp: Date.now(),
1220
+ total_size: expectedSize,
1221
+ files: [{ name: filename, size: expectedSize, role: "primary" }],
1222
+ };
1223
+ const capabilities = await this.getGalleryCapabilities();
1224
+ galleryTransferLedger.ensureCapture(capture, capabilities?.api_version || 1);
1225
+ galleryTransferLedger.transition(captureId, "TRANSFERRING");
1226
+ try {
1227
+ await this.downloadResumableCaptureFile(captureId, capture.files[0], localFilePath, (bytes) => onProgress?.(Math.min(100, Math.round((bytes / expectedSize) * 100))), abortSignal);
1228
+ await validateDownloadedMediaFile({
1229
+ path: localFilePath,
1230
+ name: filename,
1231
+ expectedSize,
1232
+ mediaKind: isVideo ? "video" : "photo",
1233
+ });
1234
+ galleryTransferLedger.transition(captureId, "VERIFIED");
1235
+ const thumbnailPath = includeThumbnail && isVideo
1236
+ ? await this.downloadVideoThumbnail(filename, localStorageService.getThumbnailFilePath(filename), abortSignal)
1237
+ : undefined;
1238
+ return {
1239
+ filePath: localFilePath,
1240
+ thumbnailPath,
1241
+ mime_type: isVideo
1242
+ ? "video/mp4"
1243
+ : lowerName.endsWith(".png")
1244
+ ? "image/png"
1245
+ : lowerName.endsWith(".avif")
1246
+ ? "image/avif"
1247
+ : "image/jpeg",
1248
+ };
1249
+ }
1250
+ catch (error) {
1251
+ galleryTransferLedger.recordFailure(captureId, error);
1252
+ throw error;
1253
+ }
1254
+ }
1255
+ async downloadFileLegacyUnsafe(filename, includeThumbnail = false, onProgress, abortSignal, expectedSize) {
773
1256
  console.log(`[ASG Camera API] Downloading file: ${filename}`);
774
1257
  try {
775
1258
  // Get the local file path where we'll save this
@@ -795,7 +1278,7 @@ export class AsgCameraApiClient {
795
1278
  let expectedContentLength = 0;
796
1279
  // Throttle progress: only call onProgress when percentage actually changes
797
1280
  let lastReportedProgress = -1;
798
- const { jobId, promise: downloadPromise } = RNFS.downloadFile({
1281
+ const { jobId, promise: downloadPromise } = localNetworkTransport.downloadFile({
799
1282
  fromUrl: downloadUrl,
800
1283
  toFile: localFilePath,
801
1284
  headers: {
@@ -836,12 +1319,12 @@ export class AsgCameraApiClient {
836
1319
  let abortPollTimer;
837
1320
  if (abortSignal) {
838
1321
  if (abortSignal.aborted) {
839
- RNFS.stopDownload(jobId);
1322
+ localNetworkTransport.stopDownload(jobId);
840
1323
  throw new Error("Sync cancelled");
841
1324
  }
842
1325
  abortPollTimer = BgTimer.setInterval(() => {
843
1326
  if (abortSignal.aborted) {
844
- RNFS.stopDownload(jobId);
1327
+ localNetworkTransport.stopDownload(jobId);
845
1328
  }
846
1329
  }, 500);
847
1330
  }
@@ -938,7 +1421,7 @@ export class AsgCameraApiClient {
938
1421
  console.log(`[ASG Camera API] Using /api/photo endpoint for video thumbnail`);
939
1422
  // The server's /api/photo endpoint serves thumbnails for video files
940
1423
  // It detects video files and automatically generates/serves thumbnails instead of the full video
941
- const thumbResult = await RNFS.downloadFile({
1424
+ const thumbResult = await localNetworkTransport.downloadFile({
942
1425
  fromUrl: `${this.baseUrl}/api/photo?file=${encodeURIComponent(filename)}`,
943
1426
  toFile: localThumbnailPath,
944
1427
  headers: {