@glassly/engine 0.1.0-dev.1 → 0.1.0-dev.101

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 (343) hide show
  1. package/README.md +57 -19
  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/ota-transport.d.ts +2 -0
  11. package/build/bluetooth-sdk/ota-transport.d.ts.map +1 -0
  12. package/build/bluetooth-sdk/ota-transport.js +2 -0
  13. package/build/bluetooth-sdk/ota-transport.js.map +1 -0
  14. package/build/bluetooth-sdk/photo-receiver.d.ts +3 -0
  15. package/build/bluetooth-sdk/photo-receiver.d.ts.map +1 -0
  16. package/build/bluetooth-sdk/photo-receiver.js +3 -0
  17. package/build/bluetooth-sdk/photo-receiver.js.map +1 -0
  18. package/build/bluetooth-sdk/react.d.ts +2 -0
  19. package/build/bluetooth-sdk/react.d.ts.map +1 -0
  20. package/build/bluetooth-sdk/react.js +2 -0
  21. package/build/bluetooth-sdk/react.js.map +1 -0
  22. package/build/bluetooth-sdk/types.d.ts +2 -0
  23. package/build/bluetooth-sdk/types.d.ts.map +1 -0
  24. package/build/bluetooth-sdk/types.js +2 -0
  25. package/build/bluetooth-sdk/types.js.map +1 -0
  26. package/build/engine.d.ts +30 -18
  27. package/build/engine.d.ts.map +1 -1
  28. package/build/engine.js +12 -3
  29. package/build/engine.js.map +1 -1
  30. package/build/facades/dev.d.ts +1 -1
  31. package/build/facades/display.d.ts +11 -4
  32. package/build/facades/display.d.ts.map +1 -1
  33. package/build/facades/display.js +12 -2
  34. package/build/facades/display.js.map +1 -1
  35. package/build/facades/displayMirror.d.ts +2 -6
  36. package/build/facades/displayMirror.d.ts.map +1 -1
  37. package/build/facades/displayMirror.js +7 -14
  38. package/build/facades/displayMirror.js.map +1 -1
  39. package/build/facades/glasses.d.ts +6 -13
  40. package/build/facades/glasses.d.ts.map +1 -1
  41. package/build/facades/glasses.js +7 -3
  42. package/build/facades/glasses.js.map +1 -1
  43. package/build/facades/ota.d.ts +21 -21
  44. package/build/facades/ota.d.ts.map +1 -1
  45. package/build/facades/ota.js +26 -10
  46. package/build/facades/ota.js.map +1 -1
  47. package/build/facades/pairing.d.ts +11 -5
  48. package/build/facades/pairing.d.ts.map +1 -1
  49. package/build/facades/pairing.js +93 -17
  50. package/build/facades/pairing.js.map +1 -1
  51. package/build/facades/phoneNotifications.d.ts +12 -2
  52. package/build/facades/phoneNotifications.d.ts.map +1 -1
  53. package/build/facades/phoneNotifications.js +30 -10
  54. package/build/facades/phoneNotifications.js.map +1 -1
  55. package/build/facades/speakers.d.ts +1 -1
  56. package/build/facades/speakers.d.ts.map +1 -1
  57. package/build/facades/speakers.js.map +1 -1
  58. package/build/facades/speech.d.ts +7 -6
  59. package/build/facades/speech.d.ts.map +1 -1
  60. package/build/facades/speech.js +1 -1
  61. package/build/facades/speech.js.map +1 -1
  62. package/build/generated/releaseMetadata.d.ts +12 -0
  63. package/build/generated/releaseMetadata.d.ts.map +1 -0
  64. package/build/generated/releaseMetadata.js +10 -0
  65. package/build/generated/releaseMetadata.js.map +1 -0
  66. package/build/index.d.ts +9 -6
  67. package/build/index.d.ts.map +1 -1
  68. package/build/index.js +7 -5
  69. package/build/index.js.map +1 -1
  70. package/build/react/MentraLiveOtaFlow.d.ts +44 -0
  71. package/build/react/MentraLiveOtaFlow.d.ts.map +1 -0
  72. package/build/react/MentraLiveOtaFlow.js +322 -0
  73. package/build/react/MentraLiveOtaFlow.js.map +1 -0
  74. package/build/react/index.d.ts +3 -0
  75. package/build/react/index.d.ts.map +1 -0
  76. package/build/react/index.js +3 -0
  77. package/build/react/index.js.map +1 -0
  78. package/build/react/useEngineSnapshot.d.ts +2 -0
  79. package/build/react/useEngineSnapshot.d.ts.map +1 -0
  80. package/build/react/useEngineSnapshot.js +14 -0
  81. package/build/react/useEngineSnapshot.js.map +1 -0
  82. package/build/react/useMentraLiveOta.d.ts +84 -0
  83. package/build/react/useMentraLiveOta.d.ts.map +1 -0
  84. package/build/react/useMentraLiveOta.js +627 -0
  85. package/build/react/useMentraLiveOta.js.map +1 -0
  86. package/build/services/AppRegistry.d.ts +8 -0
  87. package/build/services/AppRegistry.d.ts.map +1 -1
  88. package/build/services/AppRegistry.js +15 -0
  89. package/build/services/AppRegistry.js.map +1 -1
  90. package/build/services/BlobStore.d.ts +2 -0
  91. package/build/services/BlobStore.d.ts.map +1 -1
  92. package/build/services/BlobStore.js +63 -10
  93. package/build/services/BlobStore.js.map +1 -1
  94. package/build/services/CustomDashboardModel.d.ts +48 -0
  95. package/build/services/CustomDashboardModel.d.ts.map +1 -0
  96. package/build/services/CustomDashboardModel.js +73 -0
  97. package/build/services/CustomDashboardModel.js.map +1 -0
  98. package/build/services/CustomDashboardService.d.ts +55 -0
  99. package/build/services/CustomDashboardService.d.ts.map +1 -0
  100. package/build/services/CustomDashboardService.js +520 -0
  101. package/build/services/CustomDashboardService.js.map +1 -0
  102. package/build/services/DashboardCoordinator.d.ts +26 -0
  103. package/build/services/DashboardCoordinator.d.ts.map +1 -0
  104. package/build/services/DashboardCoordinator.js +125 -0
  105. package/build/services/DashboardCoordinator.js.map +1 -0
  106. package/build/services/DeviceEventRouter.d.ts.map +1 -1
  107. package/build/services/DeviceEventRouter.js +45 -6
  108. package/build/services/DeviceEventRouter.js.map +1 -1
  109. package/build/services/DisplayProcessor.d.ts +3 -3
  110. package/build/services/DisplayProcessor.js +3 -3
  111. package/build/services/DisplayProcessor.js.map +1 -1
  112. package/build/services/GestureDotsService.d.ts +40 -0
  113. package/build/services/GestureDotsService.d.ts.map +1 -0
  114. package/build/services/GestureDotsService.js +167 -0
  115. package/build/services/GestureDotsService.js.map +1 -0
  116. package/build/services/GlassesStatusProjection.d.ts +15 -1
  117. package/build/services/GlassesStatusProjection.d.ts.map +1 -1
  118. package/build/services/GlassesStatusProjection.js +18 -11
  119. package/build/services/GlassesStatusProjection.js.map +1 -1
  120. package/build/services/HotspotOtaTransport.d.ts.map +1 -1
  121. package/build/services/HotspotOtaTransport.js +8 -7
  122. package/build/services/HotspotOtaTransport.js.map +1 -1
  123. package/build/services/LocalDisplayManager.d.ts +55 -3
  124. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  125. package/build/services/LocalDisplayManager.js +196 -16
  126. package/build/services/LocalDisplayManager.js.map +1 -1
  127. package/build/services/LocalMiniappRuntime.d.ts +13 -0
  128. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  129. package/build/services/LocalMiniappRuntime.js +32 -0
  130. package/build/services/LocalMiniappRuntime.js.map +1 -1
  131. package/build/services/MiniappExitGesture.d.ts +33 -0
  132. package/build/services/MiniappExitGesture.d.ts.map +1 -0
  133. package/build/services/MiniappExitGesture.js +54 -0
  134. package/build/services/MiniappExitGesture.js.map +1 -0
  135. package/build/services/OtaAutoChain.d.ts +41 -0
  136. package/build/services/OtaAutoChain.d.ts.map +1 -0
  137. package/build/services/OtaAutoChain.js +91 -0
  138. package/build/services/OtaAutoChain.js.map +1 -0
  139. package/build/services/OtaErrorMapping.d.ts +18 -0
  140. package/build/services/OtaErrorMapping.d.ts.map +1 -0
  141. package/build/services/OtaErrorMapping.js +63 -0
  142. package/build/services/OtaErrorMapping.js.map +1 -0
  143. package/build/services/OtaInstallCoordinator.d.ts +26 -3
  144. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  145. package/build/services/OtaInstallCoordinator.js +38 -13
  146. package/build/services/OtaInstallCoordinator.js.map +1 -1
  147. package/build/services/OtaService.d.ts.map +1 -1
  148. package/build/services/OtaService.js +1 -1
  149. package/build/services/OtaService.js.map +1 -1
  150. package/build/services/OtaUpdateCheckService.d.ts +4 -1
  151. package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
  152. package/build/services/OtaUpdateCheckService.js +28 -16
  153. package/build/services/OtaUpdateCheckService.js.map +1 -1
  154. package/build/services/PhoneNotificationsSync.d.ts +13 -0
  155. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  156. package/build/services/PhoneNotificationsSync.js +101 -9
  157. package/build/services/PhoneNotificationsSync.js.map +1 -1
  158. package/build/services/PhonePhotoCoordinator.js +1 -1
  159. package/build/services/PhonePhotoCoordinator.js.map +1 -1
  160. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  161. package/build/services/PhoneStreamCoordinator.js +9 -6
  162. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  163. package/build/services/SceneRenderer.d.ts.map +1 -1
  164. package/build/services/SceneRenderer.js +5 -0
  165. package/build/services/SceneRenderer.js.map +1 -1
  166. package/build/services/StreamLifecycleController.d.ts +4 -0
  167. package/build/services/StreamLifecycleController.d.ts.map +1 -1
  168. package/build/services/StreamLifecycleController.js +4 -0
  169. package/build/services/StreamLifecycleController.js.map +1 -1
  170. package/build/services/asg/localNetworkTransport.d.ts.map +1 -1
  171. package/build/services/asg/localNetworkTransport.js +13 -12
  172. package/build/services/asg/localNetworkTransport.js.map +1 -1
  173. package/build/services/effectiveCapabilities.d.ts +1 -1
  174. package/build/services/effectiveCapabilities.d.ts.map +1 -1
  175. package/build/services/effectiveCapabilities.js +26 -7
  176. package/build/services/effectiveCapabilities.js.map +1 -1
  177. package/build/services/gestureClaims.d.ts +18 -0
  178. package/build/services/gestureClaims.d.ts.map +1 -0
  179. package/build/services/gestureClaims.js +24 -0
  180. package/build/services/gestureClaims.js.map +1 -0
  181. package/build/services/gestureDotGlyphs.d.ts +8 -0
  182. package/build/services/gestureDotGlyphs.d.ts.map +1 -0
  183. package/build/services/gestureDotGlyphs.js +13 -0
  184. package/build/services/gestureDotGlyphs.js.map +1 -0
  185. package/build/services/glassesClockSync.d.ts.map +1 -1
  186. package/build/services/glassesClockSync.js +5 -1
  187. package/build/services/glassesClockSync.js.map +1 -1
  188. package/build/services/iosNotificationAppCatalog.d.ts +13 -0
  189. package/build/services/iosNotificationAppCatalog.d.ts.map +1 -0
  190. package/build/services/iosNotificationAppCatalog.js +28 -0
  191. package/build/services/iosNotificationAppCatalog.js.map +1 -0
  192. package/build/services/otaDisplayState.d.ts +1 -1
  193. package/build/services/otaDisplayState.d.ts.map +1 -1
  194. package/build/services/otaDisplayState.js.map +1 -1
  195. package/build/services/otaInstallPolicy.d.ts +5 -1
  196. package/build/services/otaInstallPolicy.d.ts.map +1 -1
  197. package/build/services/otaInstallPolicy.js +4 -0
  198. package/build/services/otaInstallPolicy.js.map +1 -1
  199. package/build/services/otaLegacyMapping.d.ts +1 -1
  200. package/build/services/otaLegacyMapping.d.ts.map +1 -1
  201. package/build/services/otaLegacyMapping.js.map +1 -1
  202. package/build/services/otaManifestPolicy.d.ts +14 -0
  203. package/build/services/otaManifestPolicy.d.ts.map +1 -0
  204. package/build/services/otaManifestPolicy.js +20 -0
  205. package/build/services/otaManifestPolicy.js.map +1 -0
  206. package/build/services/otaManifestUrl.d.ts +2 -1
  207. package/build/services/otaManifestUrl.d.ts.map +1 -1
  208. package/build/services/otaManifestUrl.js +24 -32
  209. package/build/services/otaManifestUrl.js.map +1 -1
  210. package/build/services/otaReleaseVersion.d.ts +10 -0
  211. package/build/services/otaReleaseVersion.d.ts.map +1 -0
  212. package/build/services/otaReleaseVersion.js +14 -0
  213. package/build/services/otaReleaseVersion.js.map +1 -0
  214. package/build/stores/appVisibility.d.ts +3 -0
  215. package/build/stores/appVisibility.d.ts.map +1 -0
  216. package/build/stores/appVisibility.js +3 -0
  217. package/build/stores/appVisibility.js.map +1 -0
  218. package/build/stores/apps.d.ts.map +1 -1
  219. package/build/stores/apps.js +7 -1
  220. package/build/stores/apps.js.map +1 -1
  221. package/build/stores/core.d.ts +3 -3
  222. package/build/stores/core.d.ts.map +1 -1
  223. package/build/stores/core.js.map +1 -1
  224. package/build/stores/display.d.ts +4 -4
  225. package/build/stores/display.d.ts.map +1 -1
  226. package/build/stores/display.js +12 -36
  227. package/build/stores/display.js.map +1 -1
  228. package/build/stores/glasses.d.ts +10 -4
  229. package/build/stores/glasses.d.ts.map +1 -1
  230. package/build/stores/glasses.js +2 -0
  231. package/build/stores/glasses.js.map +1 -1
  232. package/build/stores/settings.d.ts.map +1 -1
  233. package/build/stores/settings.js +140 -5
  234. package/build/stores/settings.js.map +1 -1
  235. package/build/types/applet.d.ts +3 -1
  236. package/build/types/applet.d.ts.map +1 -1
  237. package/build/types/applet.js +0 -3
  238. package/build/types/applet.js.map +1 -1
  239. package/build/types/capabilities/even-realities-g2.d.ts.map +1 -1
  240. package/build/types/capabilities/even-realities-g2.js +2 -2
  241. package/build/types/capabilities/even-realities-g2.js.map +1 -1
  242. package/build/types/enums.d.ts +1 -5
  243. package/build/types/enums.d.ts.map +1 -1
  244. package/build/types/enums.js +1 -8
  245. package/build/types/enums.js.map +1 -1
  246. package/build/types/hardware.d.ts +1 -1
  247. package/build/types/hardware.d.ts.map +1 -1
  248. package/build/types/hardware.js +1 -0
  249. package/build/types/hardware.js.map +1 -1
  250. package/build/types/index.d.ts +3 -7
  251. package/build/types/index.d.ts.map +1 -1
  252. package/build/types/index.js +3 -7
  253. package/build/types/index.js.map +1 -1
  254. package/build/utils/display/composer/ColumnComposer.d.ts +1 -1
  255. package/build/utils/display/composer/ColumnComposer.js +1 -1
  256. package/build/utils/display/composer/ColumnComposer.js.map +1 -1
  257. package/build/utils/display/scene/process.d.ts.map +1 -1
  258. package/build/utils/display/scene/process.js +4 -3
  259. package/build/utils/display/scene/process.js.map +1 -1
  260. package/build/utils/display/scene/types.d.ts +6 -2
  261. package/build/utils/display/scene/types.d.ts.map +1 -1
  262. package/build/utils/display/scene/types.js.map +1 -1
  263. package/package.json +69 -35
  264. package/src/bluetooth-sdk/debug.ts +1 -0
  265. package/src/bluetooth-sdk/index.ts +2 -0
  266. package/src/bluetooth-sdk/ota-transport.ts +1 -0
  267. package/src/bluetooth-sdk/photo-receiver.ts +2 -0
  268. package/src/bluetooth-sdk/react.ts +1 -0
  269. package/src/bluetooth-sdk/types.ts +1 -0
  270. package/src/engine.ts +12 -3
  271. package/src/facades/display.ts +12 -2
  272. package/src/facades/displayMirror.ts +7 -15
  273. package/src/facades/glasses.ts +8 -4
  274. package/src/facades/ota.ts +36 -11
  275. package/src/facades/pairing.ts +103 -23
  276. package/src/facades/phoneNotifications.ts +32 -10
  277. package/src/facades/speakers.ts +1 -1
  278. package/src/facades/speech.ts +21 -7
  279. package/src/generated/releaseMetadata.ts +20 -0
  280. package/src/index.ts +31 -5
  281. package/src/react/MentraLiveOtaFlow.tsx +754 -0
  282. package/src/react/index.ts +21 -0
  283. package/src/react/useEngineSnapshot.ts +16 -0
  284. package/src/react/useMentraLiveOta.ts +793 -0
  285. package/src/services/AppRegistry.ts +16 -0
  286. package/src/services/BlobStore.ts +57 -10
  287. package/src/services/CustomDashboardModel.ts +83 -0
  288. package/src/services/CustomDashboardService.ts +544 -0
  289. package/src/services/DashboardCoordinator.ts +121 -0
  290. package/src/services/DeviceEventRouter.ts +48 -8
  291. package/src/services/DisplayProcessor.ts +3 -3
  292. package/src/services/GestureDotsService.ts +164 -0
  293. package/src/services/GlassesStatusProjection.ts +20 -11
  294. package/src/services/HotspotOtaTransport.ts +8 -7
  295. package/src/services/LocalDisplayManager.ts +198 -18
  296. package/src/services/LocalMiniappRuntime.ts +37 -0
  297. package/src/services/MiniappExitGesture.ts +76 -0
  298. package/src/services/OtaAutoChain.ts +130 -0
  299. package/src/services/OtaErrorMapping.ts +80 -0
  300. package/src/services/OtaInstallCoordinator.ts +40 -13
  301. package/src/services/OtaService.ts +1 -2
  302. package/src/services/OtaUpdateCheckService.ts +33 -19
  303. package/src/services/PhoneNotificationsSync.ts +125 -9
  304. package/src/services/PhonePhotoCoordinator.ts +1 -1
  305. package/src/services/PhoneStreamCoordinator.ts +10 -6
  306. package/src/services/SceneRenderer.ts +4 -0
  307. package/src/services/StreamLifecycleController.ts +4 -0
  308. package/src/services/asg/localNetworkTransport.ts +20 -19
  309. package/src/services/effectiveCapabilities.ts +27 -7
  310. package/src/services/gestureClaims.ts +28 -0
  311. package/src/services/gestureDotGlyphs.ts +15 -0
  312. package/src/services/glassesClockSync.ts +5 -1
  313. package/src/services/iosNotificationAppCatalog.ts +39 -0
  314. package/src/services/otaDisplayState.ts +1 -1
  315. package/src/services/otaInstallPolicy.ts +5 -1
  316. package/src/services/otaLegacyMapping.ts +1 -1
  317. package/src/services/otaManifestPolicy.ts +43 -0
  318. package/src/services/otaManifestUrl.ts +27 -36
  319. package/src/services/otaReleaseVersion.ts +22 -0
  320. package/src/stores/appVisibility.ts +3 -0
  321. package/src/stores/apps.ts +7 -1
  322. package/src/stores/core.ts +4 -4
  323. package/src/stores/display.ts +16 -48
  324. package/src/stores/glasses.ts +15 -6
  325. package/src/stores/settings.ts +140 -5
  326. package/src/types/applet.ts +3 -1
  327. package/src/types/capabilities/even-realities-g2.ts +3 -2
  328. package/src/types/enums.ts +1 -9
  329. package/src/types/hardware.ts +2 -1
  330. package/src/types/index.ts +3 -7
  331. package/src/utils/display/composer/ColumnComposer.ts +1 -1
  332. package/src/utils/display/scene/process.ts +4 -3
  333. package/src/utils/display/scene/types.ts +0 -0
  334. package/build/devtools.d.ts +0 -11
  335. package/build/devtools.d.ts.map +0 -1
  336. package/build/devtools.js +0 -13
  337. package/build/devtools.js.map +0 -1
  338. package/build/internal.d.ts +0 -67
  339. package/build/internal.d.ts.map +0 -1
  340. package/build/internal.js +0 -90
  341. package/build/internal.js.map +0 -1
  342. package/src/devtools.ts +0 -13
  343. package/src/internal.ts +0 -155
@@ -0,0 +1,33 @@
1
+ export declare const MINIAPP_EXIT_NOTICE_MS = 1500;
2
+ interface ExitGestureApp {
3
+ packageName: string;
4
+ name: string;
5
+ running: boolean;
6
+ }
7
+ interface ExitGestureState {
8
+ apps: ExitGestureApp[];
9
+ foregroundedPackage: string | null;
10
+ clearForeground: () => void;
11
+ stop: (packageName: string) => Promise<void>;
12
+ }
13
+ export interface ExitGestureDependencies {
14
+ getState: () => ExitGestureState;
15
+ getVisiblePackageName: () => string | null;
16
+ beginExitNotice: (packageName: string, text: string) => boolean;
17
+ finishExitNotice: (packageName: string) => void;
18
+ cancelExitNotice: (packageName: string) => void;
19
+ setTimeout: (callback: () => void, delay: number) => number;
20
+ clearTimeout: (timerId: number) => void;
21
+ }
22
+ /** Owns the short double-tap → exit-notice → normal-stop handoff. */
23
+ export declare class MiniappExitGesture {
24
+ private readonly deps;
25
+ private pending;
26
+ constructor(deps: ExitGestureDependencies);
27
+ /** Returns true when the system gesture was consumed by a visible miniapp exit. */
28
+ tryExitVisibleMiniapp(): boolean;
29
+ /** Cancel an in-flight handoff when the device-event router shuts down. */
30
+ reset(): void;
31
+ }
32
+ export {};
33
+ //# sourceMappingURL=MiniappExitGesture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MiniappExitGesture.d.ts","sourceRoot":"","sources":["../../src/services/MiniappExitGesture.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,OAAO,CAAA;AAE1C,UAAU,cAAc;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,UAAU,gBAAgB;IACxB,IAAI,EAAE,cAAc,EAAE,CAAA;IACtB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7C;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,gBAAgB,CAAA;IAChC,qBAAqB,EAAE,MAAM,MAAM,GAAG,IAAI,CAAA;IAC1C,eAAe,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;IAC/D,gBAAgB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/C,gBAAgB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/C,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IAC3D,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC;AAED,qEAAqE;AACrE,qBAAa,kBAAkB;IAGV,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFxC,OAAO,CAAC,OAAO,CAAsD;gBAEjC,IAAI,EAAE,uBAAuB;IAEjE,mFAAmF;IAC5E,qBAAqB,IAAI,OAAO;IAmCvC,2EAA2E;IACpE,KAAK,IAAI,IAAI;CAOrB"}
@@ -0,0 +1,54 @@
1
+ export const MINIAPP_EXIT_NOTICE_MS = 1500;
2
+ /** Owns the short double-tap → exit-notice → normal-stop handoff. */
3
+ export class MiniappExitGesture {
4
+ deps;
5
+ pending = null;
6
+ constructor(deps) {
7
+ this.deps = deps;
8
+ }
9
+ /** Returns true when the system gesture was consumed by a visible miniapp exit. */
10
+ tryExitVisibleMiniapp() {
11
+ // Swallow repeat taps while the notice is already counting down.
12
+ if (this.pending)
13
+ return true;
14
+ const packageName = this.deps.getVisiblePackageName();
15
+ if (!packageName)
16
+ return false;
17
+ const app = this.deps.getState().apps.find((candidate) => candidate.packageName === packageName);
18
+ if (!app?.running)
19
+ return false;
20
+ const name = app.name.trim() || packageName;
21
+ if (!this.deps.beginExitNotice(packageName, `Exiting ${name}…`))
22
+ return false;
23
+ const timerId = this.deps.setTimeout(() => {
24
+ const state = this.deps.getState();
25
+ if (state.foregroundedPackage === packageName)
26
+ state.clearForeground();
27
+ const stillRunning = state.apps.some((candidate) => candidate.packageName === packageName && candidate.running);
28
+ const stop = stillRunning ? state.stop(packageName) : Promise.resolve();
29
+ void stop
30
+ .catch((error) => {
31
+ console.error(`MiniappExitGesture: failed to stop ${packageName}:`, error);
32
+ })
33
+ .finally(() => {
34
+ if (this.pending?.packageName === packageName)
35
+ this.pending = null;
36
+ // LocalMiniappRuntime.onUnmount normally finishes the notice. This
37
+ // fallback also handles a failed/no-op teardown.
38
+ this.deps.finishExitNotice(packageName);
39
+ });
40
+ }, MINIAPP_EXIT_NOTICE_MS);
41
+ this.pending = { packageName, timerId };
42
+ return true;
43
+ }
44
+ /** Cancel an in-flight handoff when the device-event router shuts down. */
45
+ reset() {
46
+ if (!this.pending)
47
+ return;
48
+ const { packageName, timerId } = this.pending;
49
+ this.pending = null;
50
+ this.deps.clearTimeout(timerId);
51
+ this.deps.cancelExitNotice(packageName);
52
+ }
53
+ }
54
+ //# sourceMappingURL=MiniappExitGesture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MiniappExitGesture.js","sourceRoot":"","sources":["../../src/services/MiniappExitGesture.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAA;AAyB1C,qEAAqE;AACrE,MAAM,OAAO,kBAAkB;IAGO;IAF5B,OAAO,GAAkD,IAAI,CAAA;IAErE,YAAoC,IAA6B;QAA7B,SAAI,GAAJ,IAAI,CAAyB;IAAG,CAAC;IAErE,mFAAmF;IAC5E,qBAAqB;QAC1B,iEAAiE;QACjE,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAA;QAE7B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAA;QACrD,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAA;QAE9B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,WAAW,KAAK,WAAW,CAAC,CAAA;QAChG,IAAI,CAAC,GAAG,EAAE,OAAO;YAAE,OAAO,KAAK,CAAA;QAE/B,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,WAAW,CAAA;QAC3C,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,WAAW,IAAI,GAAG,CAAC;YAAE,OAAO,KAAK,CAAA;QAE7E,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE;YACxC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAA;YAClC,IAAI,KAAK,CAAC,mBAAmB,KAAK,WAAW;gBAAE,KAAK,CAAC,eAAe,EAAE,CAAA;YAEtE,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,WAAW,KAAK,WAAW,IAAI,SAAS,CAAC,OAAO,CAAC,CAAA;YAC/G,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAA;YACvE,KAAK,IAAI;iBACN,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBACf,OAAO,CAAC,KAAK,CAAC,sCAAsC,WAAW,GAAG,EAAE,KAAK,CAAC,CAAA;YAC5E,CAAC,CAAC;iBACD,OAAO,CAAC,GAAG,EAAE;gBACZ,IAAI,IAAI,CAAC,OAAO,EAAE,WAAW,KAAK,WAAW;oBAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;gBAClE,mEAAmE;gBACnE,iDAAiD;gBACjD,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAA;YACzC,CAAC,CAAC,CAAA;QACN,CAAC,EAAE,sBAAsB,CAAC,CAAA;QAE1B,IAAI,CAAC,OAAO,GAAG,EAAC,WAAW,EAAE,OAAO,EAAC,CAAA;QACrC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,2EAA2E;IACpE,KAAK;QACV,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAM;QACzB,MAAM,EAAC,WAAW,EAAE,OAAO,EAAC,GAAG,IAAI,CAAC,OAAO,CAAA;QAC3C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACnB,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAA;QAC/B,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAA;IACzC,CAAC;CACF","sourcesContent":["export const MINIAPP_EXIT_NOTICE_MS = 1500\n\ninterface ExitGestureApp {\n packageName: string\n name: string\n running: boolean\n}\n\ninterface ExitGestureState {\n apps: ExitGestureApp[]\n foregroundedPackage: string | null\n clearForeground: () => void\n stop: (packageName: string) => Promise<void>\n}\n\nexport interface ExitGestureDependencies {\n getState: () => ExitGestureState\n getVisiblePackageName: () => string | null\n beginExitNotice: (packageName: string, text: string) => boolean\n finishExitNotice: (packageName: string) => void\n cancelExitNotice: (packageName: string) => void\n setTimeout: (callback: () => void, delay: number) => number\n clearTimeout: (timerId: number) => void\n}\n\n/** Owns the short double-tap → exit-notice → normal-stop handoff. */\nexport class MiniappExitGesture {\n private pending: {packageName: string; timerId: number} | null = null\n\n public constructor(private readonly deps: ExitGestureDependencies) {}\n\n /** Returns true when the system gesture was consumed by a visible miniapp exit. */\n public tryExitVisibleMiniapp(): boolean {\n // Swallow repeat taps while the notice is already counting down.\n if (this.pending) return true\n\n const packageName = this.deps.getVisiblePackageName()\n if (!packageName) return false\n\n const app = this.deps.getState().apps.find((candidate) => candidate.packageName === packageName)\n if (!app?.running) return false\n\n const name = app.name.trim() || packageName\n if (!this.deps.beginExitNotice(packageName, `Exiting ${name}…`)) return false\n\n const timerId = this.deps.setTimeout(() => {\n const state = this.deps.getState()\n if (state.foregroundedPackage === packageName) state.clearForeground()\n\n const stillRunning = state.apps.some((candidate) => candidate.packageName === packageName && candidate.running)\n const stop = stillRunning ? state.stop(packageName) : Promise.resolve()\n void stop\n .catch((error) => {\n console.error(`MiniappExitGesture: failed to stop ${packageName}:`, error)\n })\n .finally(() => {\n if (this.pending?.packageName === packageName) this.pending = null\n // LocalMiniappRuntime.onUnmount normally finishes the notice. This\n // fallback also handles a failed/no-op teardown.\n this.deps.finishExitNotice(packageName)\n })\n }, MINIAPP_EXIT_NOTICE_MS)\n\n this.pending = {packageName, timerId}\n return true\n }\n\n /** Cancel an in-flight handoff when the device-event router shuts down. */\n public reset(): void {\n if (!this.pending) return\n const {packageName, timerId} = this.pending\n this.pending = null\n this.deps.clearTimeout(timerId)\n this.deps.cancelExitNotice(packageName)\n }\n}\n"]}
@@ -0,0 +1,41 @@
1
+ import type { OtaCheckCurrentGlassesResult } from "./OtaUpdateCheckService";
2
+ export declare const MAX_OTA_AUTO_CHAIN_PASSES = 8;
3
+ export declare const OTA_AUTO_CHAIN_RECONNECT_TIMEOUT_MS = 120000;
4
+ export type OtaAutoChainReleaseRange = {
5
+ fromVersion: string | null;
6
+ toVersion: string | null;
7
+ /** Exact coordinated release identity for the OTA pin, absent for legacy manifests. */
8
+ releaseVersion?: string | null;
9
+ };
10
+ export type OtaAutoChainAdvanceResult = {
11
+ advance: true;
12
+ passCount: number;
13
+ } | {
14
+ advance: false;
15
+ reason: "inactive" | "duplicate" | "max_passes" | "downgrade_not_approved";
16
+ };
17
+ /**
18
+ * Identify the exact update offer that produced one OTA pass. Including the
19
+ * observed versions and manifest body lets a changed manifest advance while a
20
+ * stale post-install version_info response is rejected as a duplicate.
21
+ */
22
+ export declare function otaAutoChainFingerprint(result: OtaCheckCurrentGlassesResult): string;
23
+ /** Start a chain after the user explicitly approves its first update pass. */
24
+ export declare function beginOtaAutoChain(initialFingerprint: string, approvedDowngrade: boolean, releaseRange: OtaAutoChainReleaseRange): void;
25
+ export declare function isOtaAutoChainActive(): boolean;
26
+ export declare function otaAutoChainReleaseRange(): OtaAutoChainReleaseRange | null;
27
+ /** End the current chain. Safe to call when no chain is active. */
28
+ export declare function stopOtaAutoChain(): void;
29
+ /**
30
+ * Start (or continue) the bounded wait for glasses that are rebooting between
31
+ * passes. Re-renders must not extend the original deadline indefinitely.
32
+ */
33
+ export declare function otaAutoChainReconnectWaitRemaining(now?: number): number | null;
34
+ /** A successful reconnect allows a future pass to establish a fresh wait. */
35
+ export declare function clearOtaAutoChainReconnectWait(): void;
36
+ /**
37
+ * Admit the next pass of an active chain. Repeated offers, unexpectedly
38
+ * destructive downgrades, and runaway chains fail closed and end automation.
39
+ */
40
+ export declare function tryAdvanceOtaAutoChain(fingerprint: string, isDowngrade: boolean, targetVersion: string | null, releaseVersion?: string | null): OtaAutoChainAdvanceResult;
41
+ //# sourceMappingURL=OtaAutoChain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OtaAutoChain.d.ts","sourceRoot":"","sources":["../../src/services/OtaAutoChain.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,4BAA4B,EAAC,MAAM,yBAAyB,CAAA;AAEzE,eAAO,MAAM,yBAAyB,IAAI,CAAA;AAC1C,eAAO,MAAM,mCAAmC,SAAU,CAAA;AAU1D,MAAM,MAAM,wBAAwB,GAAG;IACrC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,uFAAuF;IACvF,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,yBAAyB,GACjC;IAAC,OAAO,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAC,GAClC;IAAC,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,YAAY,GAAG,wBAAwB,CAAA;CAAC,CAAA;AAIhG;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,4BAA4B,GAAG,MAAM,CAkBpF;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAC/B,kBAAkB,EAAE,MAAM,EAC1B,iBAAiB,EAAE,OAAO,EAC1B,YAAY,EAAE,wBAAwB,GACrC,IAAI,CAQN;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,wBAAgB,wBAAwB,IAAI,wBAAwB,GAAG,IAAI,CAE1E;AAED,mEAAmE;AACnE,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED;;;GAGG;AACH,wBAAgB,kCAAkC,CAAC,GAAG,SAAoB,GAAG,MAAM,GAAG,IAAI,CAKzF;AAED,6EAA6E;AAC7E,wBAAgB,8BAA8B,IAAI,IAAI,CAErD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,OAAO,EACpB,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,cAAc,GAAE,MAAM,GAAG,IAAW,GACnC,yBAAyB,CAyB3B"}
@@ -0,0 +1,91 @@
1
+ export const MAX_OTA_AUTO_CHAIN_PASSES = 8;
2
+ export const OTA_AUTO_CHAIN_RECONNECT_TIMEOUT_MS = 120_000;
3
+ let session = null;
4
+ /**
5
+ * Identify the exact update offer that produced one OTA pass. Including the
6
+ * observed versions and manifest body lets a changed manifest advance while a
7
+ * stale post-install version_info response is rejected as a duplicate.
8
+ */
9
+ export function otaAutoChainFingerprint(result) {
10
+ return JSON.stringify({
11
+ manifestUrl: result.manifestUrl ?? "",
12
+ manifestBody: result.manifestBody ?? "",
13
+ buildNumber: result.buildNumber ?? "",
14
+ mtkFirmwareVersion: result.mtkFirmwareVersion ?? "",
15
+ besFirmwareVersion: result.besFirmwareVersion ?? "",
16
+ updates: result.updates,
17
+ targetBuildNumber: result.updateInfo?.versionCode ?? result.latestVersionInfo?.versionCode ?? 0,
18
+ targetBesVersion: result.besVersion ?? "",
19
+ mtkPatch: result.mtkPatch
20
+ ? {
21
+ startFirmware: result.mtkPatch.start_firmware,
22
+ endFirmware: result.mtkPatch.end_firmware,
23
+ url: result.mtkPatch.url,
24
+ }
25
+ : null,
26
+ });
27
+ }
28
+ /** Start a chain after the user explicitly approves its first update pass. */
29
+ export function beginOtaAutoChain(initialFingerprint, approvedDowngrade, releaseRange) {
30
+ session = {
31
+ approvedDowngrade,
32
+ passCount: 1,
33
+ releaseRange: { ...releaseRange },
34
+ reconnectDeadline: null,
35
+ seenFingerprints: new Set([initialFingerprint]),
36
+ };
37
+ }
38
+ export function isOtaAutoChainActive() {
39
+ return session !== null;
40
+ }
41
+ export function otaAutoChainReleaseRange() {
42
+ return session ? { ...session.releaseRange } : null;
43
+ }
44
+ /** End the current chain. Safe to call when no chain is active. */
45
+ export function stopOtaAutoChain() {
46
+ session = null;
47
+ }
48
+ /**
49
+ * Start (or continue) the bounded wait for glasses that are rebooting between
50
+ * passes. Re-renders must not extend the original deadline indefinitely.
51
+ */
52
+ export function otaAutoChainReconnectWaitRemaining(now = performance.now()) {
53
+ if (!session)
54
+ return null;
55
+ session.reconnectDeadline ??= now + OTA_AUTO_CHAIN_RECONNECT_TIMEOUT_MS;
56
+ return Math.max(0, session.reconnectDeadline - now);
57
+ }
58
+ /** A successful reconnect allows a future pass to establish a fresh wait. */
59
+ export function clearOtaAutoChainReconnectWait() {
60
+ if (session)
61
+ session.reconnectDeadline = null;
62
+ }
63
+ /**
64
+ * Admit the next pass of an active chain. Repeated offers, unexpectedly
65
+ * destructive downgrades, and runaway chains fail closed and end automation.
66
+ */
67
+ export function tryAdvanceOtaAutoChain(fingerprint, isDowngrade, targetVersion, releaseVersion = null) {
68
+ if (!session) {
69
+ return { advance: false, reason: "inactive" };
70
+ }
71
+ if (isDowngrade && !session.approvedDowngrade) {
72
+ stopOtaAutoChain();
73
+ return { advance: false, reason: "downgrade_not_approved" };
74
+ }
75
+ if (session.seenFingerprints.has(fingerprint)) {
76
+ stopOtaAutoChain();
77
+ return { advance: false, reason: "duplicate" };
78
+ }
79
+ if (session.passCount >= MAX_OTA_AUTO_CHAIN_PASSES) {
80
+ stopOtaAutoChain();
81
+ return { advance: false, reason: "max_passes" };
82
+ }
83
+ session.seenFingerprints.add(fingerprint);
84
+ session.passCount += 1;
85
+ if (targetVersion)
86
+ session.releaseRange.toVersion = targetVersion;
87
+ if (releaseVersion)
88
+ session.releaseRange.releaseVersion = releaseVersion;
89
+ return { advance: true, passCount: session.passCount };
90
+ }
91
+ //# sourceMappingURL=OtaAutoChain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OtaAutoChain.js","sourceRoot":"","sources":["../../src/services/OtaAutoChain.ts"],"names":[],"mappings":"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","sourcesContent":["import type {OtaCheckCurrentGlassesResult} from \"./OtaUpdateCheckService\"\n\nexport const MAX_OTA_AUTO_CHAIN_PASSES = 8\nexport const OTA_AUTO_CHAIN_RECONNECT_TIMEOUT_MS = 120_000\n\ntype OtaAutoChainSession = {\n approvedDowngrade: boolean\n passCount: number\n releaseRange: OtaAutoChainReleaseRange\n reconnectDeadline: number | null\n seenFingerprints: Set<string>\n}\n\nexport type OtaAutoChainReleaseRange = {\n fromVersion: string | null\n toVersion: string | null\n /** Exact coordinated release identity for the OTA pin, absent for legacy manifests. */\n releaseVersion?: string | null\n}\n\nexport type OtaAutoChainAdvanceResult =\n | {advance: true; passCount: number}\n | {advance: false; reason: \"inactive\" | \"duplicate\" | \"max_passes\" | \"downgrade_not_approved\"}\n\nlet session: OtaAutoChainSession | null = null\n\n/**\n * Identify the exact update offer that produced one OTA pass. Including the\n * observed versions and manifest body lets a changed manifest advance while a\n * stale post-install version_info response is rejected as a duplicate.\n */\nexport function otaAutoChainFingerprint(result: OtaCheckCurrentGlassesResult): string {\n return JSON.stringify({\n manifestUrl: result.manifestUrl ?? \"\",\n manifestBody: result.manifestBody ?? \"\",\n buildNumber: result.buildNumber ?? \"\",\n mtkFirmwareVersion: result.mtkFirmwareVersion ?? \"\",\n besFirmwareVersion: result.besFirmwareVersion ?? \"\",\n updates: result.updates,\n targetBuildNumber: result.updateInfo?.versionCode ?? result.latestVersionInfo?.versionCode ?? 0,\n targetBesVersion: result.besVersion ?? \"\",\n mtkPatch: result.mtkPatch\n ? {\n startFirmware: result.mtkPatch.start_firmware,\n endFirmware: result.mtkPatch.end_firmware,\n url: result.mtkPatch.url,\n }\n : null,\n })\n}\n\n/** Start a chain after the user explicitly approves its first update pass. */\nexport function beginOtaAutoChain(\n initialFingerprint: string,\n approvedDowngrade: boolean,\n releaseRange: OtaAutoChainReleaseRange,\n): void {\n session = {\n approvedDowngrade,\n passCount: 1,\n releaseRange: {...releaseRange},\n reconnectDeadline: null,\n seenFingerprints: new Set([initialFingerprint]),\n }\n}\n\nexport function isOtaAutoChainActive(): boolean {\n return session !== null\n}\n\nexport function otaAutoChainReleaseRange(): OtaAutoChainReleaseRange | null {\n return session ? {...session.releaseRange} : null\n}\n\n/** End the current chain. Safe to call when no chain is active. */\nexport function stopOtaAutoChain(): void {\n session = null\n}\n\n/**\n * Start (or continue) the bounded wait for glasses that are rebooting between\n * passes. Re-renders must not extend the original deadline indefinitely.\n */\nexport function otaAutoChainReconnectWaitRemaining(now = performance.now()): number | null {\n if (!session) return null\n\n session.reconnectDeadline ??= now + OTA_AUTO_CHAIN_RECONNECT_TIMEOUT_MS\n return Math.max(0, session.reconnectDeadline - now)\n}\n\n/** A successful reconnect allows a future pass to establish a fresh wait. */\nexport function clearOtaAutoChainReconnectWait(): void {\n if (session) session.reconnectDeadline = null\n}\n\n/**\n * Admit the next pass of an active chain. Repeated offers, unexpectedly\n * destructive downgrades, and runaway chains fail closed and end automation.\n */\nexport function tryAdvanceOtaAutoChain(\n fingerprint: string,\n isDowngrade: boolean,\n targetVersion: string | null,\n releaseVersion: string | null = null,\n): OtaAutoChainAdvanceResult {\n if (!session) {\n return {advance: false, reason: \"inactive\"}\n }\n\n if (isDowngrade && !session.approvedDowngrade) {\n stopOtaAutoChain()\n return {advance: false, reason: \"downgrade_not_approved\"}\n }\n\n if (session.seenFingerprints.has(fingerprint)) {\n stopOtaAutoChain()\n return {advance: false, reason: \"duplicate\"}\n }\n\n if (session.passCount >= MAX_OTA_AUTO_CHAIN_PASSES) {\n stopOtaAutoChain()\n return {advance: false, reason: \"max_passes\"}\n }\n\n session.seenFingerprints.add(fingerprint)\n session.passCount += 1\n if (targetVersion) session.releaseRange.toVersion = targetVersion\n if (releaseVersion) session.releaseRange.releaseVersion = releaseVersion\n return {advance: true, passCount: session.passCount}\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import type { OtaProgress, OtaStatus } from "../facades/ota";
2
+ export declare const BES_INSTALL_RESTART_MESSAGE = "Restart your glasses to safely exit firmware update mode before trying again";
3
+ /**
4
+ * Offer Change WiFi whenever the OTA flow failed while the glasses were in the
5
+ * download step (any download failure — network, SSL, size cap, verify, etc.).
6
+ *
7
+ * Also covers phone-side watchdog failures that fire while the store still shows
8
+ * an active download phase (stall / global timeout mid-download).
9
+ */
10
+ export declare function shouldShowChangeWifiForOtaDownloadFailure(otaStatus: OtaStatus | null | undefined, otaProgress: OtaProgress | null | undefined, localErrorMessage: string): boolean;
11
+ export declare function getOtaErrorMessage(error?: string): string;
12
+ /**
13
+ * Once a BES install has started, any failure conservatively requires a glasses restart. This
14
+ * avoids adding a BES-specific error code to the wire protocol and, more importantly, never offers
15
+ * an unsafe retry when a generic failure or silence may mean BES entered its raw OTA parser.
16
+ */
17
+ export declare function shouldRequireGlassesRebootForBesFailure(otaStatus: OtaStatus | null | undefined, _otaProgress: OtaProgress | null | undefined, localErrorMessage: string): boolean;
18
+ //# sourceMappingURL=OtaErrorMapping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OtaErrorMapping.d.ts","sourceRoot":"","sources":["../../src/services/OtaErrorMapping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAE,SAAS,EAAC,MAAM,gBAAgB,CAAA;AAE1D,eAAO,MAAM,2BAA2B,iFACwC,CAAA;AAehF;;;;;;GAMG;AACH,wBAAgB,yCAAyC,CACvD,SAAS,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,EACvC,WAAW,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,EAC3C,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAWT;AAED,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAqBzD;AAED;;;;GAIG;AACH,wBAAgB,uCAAuC,CACrD,SAAS,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,EACvC,YAAY,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,EAC5C,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAKT"}
@@ -0,0 +1,63 @@
1
+ export const BES_INSTALL_RESTART_MESSAGE = "Restart your glasses to safely exit firmware update mode before trying again";
2
+ function isDownloadPhaseSnapshot(otaStatus, otaProgress) {
3
+ if (otaStatus?.phase === "download") {
4
+ return true;
5
+ }
6
+ if (otaProgress?.stage === "download") {
7
+ return true;
8
+ }
9
+ return false;
10
+ }
11
+ /**
12
+ * Offer Change WiFi whenever the OTA flow failed while the glasses were in the
13
+ * download step (any download failure — network, SSL, size cap, verify, etc.).
14
+ *
15
+ * Also covers phone-side watchdog failures that fire while the store still shows
16
+ * an active download phase (stall / global timeout mid-download).
17
+ */
18
+ export function shouldShowChangeWifiForOtaDownloadFailure(otaStatus, otaProgress, localErrorMessage) {
19
+ if (otaStatus?.status === "failed" && otaStatus.phase === "download") {
20
+ return true;
21
+ }
22
+ if (otaProgress?.status === "FAILED" && otaProgress.stage === "download") {
23
+ return true;
24
+ }
25
+ if (localErrorMessage && isDownloadPhaseSnapshot(otaStatus, otaProgress)) {
26
+ return true;
27
+ }
28
+ return false;
29
+ }
30
+ export function getOtaErrorMessage(error) {
31
+ switch (error) {
32
+ case "no_internet":
33
+ return "Glasses WiFi has no internet connection";
34
+ case "clock_skew":
35
+ return "Glasses clock is wrong — syncing time from your phone, then retrying update check";
36
+ case "ssl_error":
37
+ return "Secure connection failed — try a different WiFi network";
38
+ case "download_failed":
39
+ return "Download failed — check glasses WiFi connection";
40
+ case "firmware_too_large":
41
+ return "Firmware file is unexpectedly large — please contact support";
42
+ case "firmware_verify_failed":
43
+ return "Firmware verification failed — please try again or contact support";
44
+ case "apk_verify_failed":
45
+ return "Update verification failed — please try again or contact support";
46
+ case "install_failed":
47
+ return "Install failed — please try again";
48
+ default:
49
+ return error || "Update failed";
50
+ }
51
+ }
52
+ /**
53
+ * Once a BES install has started, any failure conservatively requires a glasses restart. This
54
+ * avoids adding a BES-specific error code to the wire protocol and, more importantly, never offers
55
+ * an unsafe retry when a generic failure or silence may mean BES entered its raw OTA parser.
56
+ */
57
+ export function shouldRequireGlassesRebootForBesFailure(otaStatus, _otaProgress, localErrorMessage) {
58
+ if (otaStatus?.stepType !== "bes" || otaStatus.phase !== "install") {
59
+ return false;
60
+ }
61
+ return otaStatus.status === "failed" || Boolean(localErrorMessage);
62
+ }
63
+ //# sourceMappingURL=OtaErrorMapping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OtaErrorMapping.js","sourceRoot":"","sources":["../../src/services/OtaErrorMapping.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,2BAA2B,GACtC,8EAA8E,CAAA;AAEhF,SAAS,uBAAuB,CAC9B,SAAuC,EACvC,WAA2C;IAE3C,IAAI,SAAS,EAAE,KAAK,KAAK,UAAU,EAAE,CAAC;QACpC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,WAAW,EAAE,KAAK,KAAK,UAAU,EAAE,CAAC;QACtC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yCAAyC,CACvD,SAAuC,EACvC,WAA2C,EAC3C,iBAAyB;IAEzB,IAAI,SAAS,EAAE,MAAM,KAAK,QAAQ,IAAI,SAAS,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QACrE,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,WAAW,EAAE,MAAM,KAAK,QAAQ,IAAI,WAAW,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QACzE,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,iBAAiB,IAAI,uBAAuB,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,CAAC;QACzE,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,aAAa;YAChB,OAAO,yCAAyC,CAAA;QAClD,KAAK,YAAY;YACf,OAAO,mFAAmF,CAAA;QAC5F,KAAK,WAAW;YACd,OAAO,yDAAyD,CAAA;QAClE,KAAK,iBAAiB;YACpB,OAAO,iDAAiD,CAAA;QAC1D,KAAK,oBAAoB;YACvB,OAAO,8DAA8D,CAAA;QACvE,KAAK,wBAAwB;YAC3B,OAAO,oEAAoE,CAAA;QAC7E,KAAK,mBAAmB;YACtB,OAAO,kEAAkE,CAAA;QAC3E,KAAK,gBAAgB;YACnB,OAAO,mCAAmC,CAAA;QAC5C;YACE,OAAO,KAAK,IAAI,eAAe,CAAA;IACnC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uCAAuC,CACrD,SAAuC,EACvC,YAA4C,EAC5C,iBAAyB;IAEzB,IAAI,SAAS,EAAE,QAAQ,KAAK,KAAK,IAAI,SAAS,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACnE,OAAO,KAAK,CAAA;IACd,CAAC;IACD,OAAO,SAAS,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAA;AACpE,CAAC","sourcesContent":["import type {OtaProgress, OtaStatus} from \"../facades/ota\"\n\nexport const BES_INSTALL_RESTART_MESSAGE =\n \"Restart your glasses to safely exit firmware update mode before trying again\"\n\nfunction isDownloadPhaseSnapshot(\n otaStatus: OtaStatus | null | undefined,\n otaProgress: OtaProgress | null | undefined,\n): boolean {\n if (otaStatus?.phase === \"download\") {\n return true\n }\n if (otaProgress?.stage === \"download\") {\n return true\n }\n return false\n}\n\n/**\n * Offer Change WiFi whenever the OTA flow failed while the glasses were in the\n * download step (any download failure — network, SSL, size cap, verify, etc.).\n *\n * Also covers phone-side watchdog failures that fire while the store still shows\n * an active download phase (stall / global timeout mid-download).\n */\nexport function shouldShowChangeWifiForOtaDownloadFailure(\n otaStatus: OtaStatus | null | undefined,\n otaProgress: OtaProgress | null | undefined,\n localErrorMessage: string,\n): boolean {\n if (otaStatus?.status === \"failed\" && otaStatus.phase === \"download\") {\n return true\n }\n if (otaProgress?.status === \"FAILED\" && otaProgress.stage === \"download\") {\n return true\n }\n if (localErrorMessage && isDownloadPhaseSnapshot(otaStatus, otaProgress)) {\n return true\n }\n return false\n}\n\nexport function getOtaErrorMessage(error?: string): string {\n switch (error) {\n case \"no_internet\":\n return \"Glasses WiFi has no internet connection\"\n case \"clock_skew\":\n return \"Glasses clock is wrong — syncing time from your phone, then retrying update check\"\n case \"ssl_error\":\n return \"Secure connection failed — try a different WiFi network\"\n case \"download_failed\":\n return \"Download failed — check glasses WiFi connection\"\n case \"firmware_too_large\":\n return \"Firmware file is unexpectedly large — please contact support\"\n case \"firmware_verify_failed\":\n return \"Firmware verification failed — please try again or contact support\"\n case \"apk_verify_failed\":\n return \"Update verification failed — please try again or contact support\"\n case \"install_failed\":\n return \"Install failed — please try again\"\n default:\n return error || \"Update failed\"\n }\n}\n\n/**\n * Once a BES install has started, any failure conservatively requires a glasses restart. This\n * avoids adding a BES-specific error code to the wire protocol and, more importantly, never offers\n * an unsafe retry when a generic failure or silence may mean BES entered its raw OTA parser.\n */\nexport function shouldRequireGlassesRebootForBesFailure(\n otaStatus: OtaStatus | null | undefined,\n _otaProgress: OtaProgress | null | undefined,\n localErrorMessage: string,\n): boolean {\n if (otaStatus?.stepType !== \"bes\" || otaStatus.phase !== \"install\") {\n return false\n }\n return otaStatus.status === \"failed\" || Boolean(localErrorMessage)\n}\n"]}
@@ -1,4 +1,23 @@
1
- import type { OtaProgress, OtaStatus } from "@glassly/bluetooth-sdk/internal";
1
+ /**
2
+ * OtaInstallCoordinator — engine-owned OTA install state machine (WP 8B).
3
+ *
4
+ * Owns every timer/retry/sequencing rule that used to live in the host screen
5
+ * `mobile/src/app/ota/progress.tsx`: the global session timeout, serialized
6
+ * ota_start retries, the stuck-at-zero watchdog, the progress-stall watchdog keyed
7
+ * on a staleness signature, connect-edge arbitration (initial mount vs
8
+ * reconnect vs post-APK reboot), the ota_query_status reply fallback, the ping
9
+ * keepalive, the ota_start_ack / mtk_update_complete listeners, and terminal
10
+ * cleanup. The host progress screen is a pure renderer over
11
+ * `snapshot()`/`onSnapshot()` plus the `attach()/detach()/retry()/finish()`
12
+ * commands (exposed as `engine.ota.installSession`).
13
+ *
14
+ * The reaction pass mirrors the old component's effect ordering: a
15
+ * pass diffs the store-derived inputs (connected / otaStatus / otaProgress /
16
+ * displayState / stall signature) exactly like the old effect dep arrays, and
17
+ * state written mid-pass queues a follow-up pass — the same way a setState
18
+ * during React effects scheduled a re-render after the current effect batch.
19
+ */
20
+ import { type OtaProgress, type OtaStatus } from "@glassly/bluetooth-sdk";
2
21
  import { type HotspotOtaPhase } from "./HotspotOtaTransport";
3
22
  import type { OtaCheckCurrentGlassesResult } from "./OtaUpdateCheckService";
4
23
  import { type DisplayState } from "./otaDisplayState";
@@ -30,6 +49,8 @@ export interface OtaInstallSnapshot {
30
49
  /** Pre-ota_start phone staging/join state for a hotspot attempt. */
31
50
  hotspotPhase: HotspotOtaPhase;
32
51
  hotspotArtifactPercent: number | null;
52
+ /** Transport selected from the checked glasses capabilities and Wi-Fi state. */
53
+ transport: "wifi" | "hotspot";
33
54
  }
34
55
  declare class OtaInstallCoordinator {
35
56
  private attached;
@@ -65,6 +86,7 @@ declare class OtaInstallCoordinator {
65
86
  private pingInterval;
66
87
  private queryReplyTimeout;
67
88
  private continueLockoutTimer;
89
+ private restartRecoveryTimeout;
68
90
  private legacyApkSettleTimer;
69
91
  private mtkStallDetectTimer;
70
92
  private mtkSimTickTimer;
@@ -106,14 +128,14 @@ declare class OtaInstallCoordinator {
106
128
  * stays in the screen): clear the selected update, and after an APK step
107
129
  * clear the stale build number so the next check re-reads version_info.
108
130
  */
109
- finish(): void;
131
+ finish(): Promise<void>;
110
132
  /**
111
133
  * Abandon an interrupted session (the super-mode skip): terminal cleanup
112
134
  * plus dropping the session's status/progress read-state, so
113
135
  * deriveDisplayState doesn't resurrect the stale install when the host
114
136
  * navigates away and back through /ota routes.
115
137
  */
116
- discard(): void;
138
+ discard(): Promise<void>;
117
139
  snapshot(): OtaInstallSnapshot;
118
140
  /**
119
141
  * True while the recovery worker owns the downgrade transaction (after the apk install
@@ -257,6 +279,7 @@ declare class OtaInstallCoordinator {
257
279
  private clearPingInterval;
258
280
  private clearQueryReplyTimeout;
259
281
  private clearContinueLockoutTimer;
282
+ private clearRestartRecoveryTimeout;
260
283
  private clearLegacyApkSettleTimer;
261
284
  private clearMtkSimulationTimers;
262
285
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"OtaInstallCoordinator.d.ts","sourceRoot":"","sources":["../../src/services/OtaInstallCoordinator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"OtaInstallCoordinator.d.ts","sourceRoot":"","sources":["../../src/services/OtaInstallCoordinator.ts"],"names":[],"mappings":"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"}
@@ -17,13 +17,13 @@
17
17
  * state written mid-pass queues a follow-up pass — the same way a setState
18
18
  * during React effects scheduled a re-render after the current effect batch.
19
19
  */
20
- import BluetoothSdk from "@glassly/bluetooth-sdk/internal";
20
+ import BluetoothSdk from "@glassly/bluetooth-sdk";
21
21
  import GlobalEventEmitter from "../utils/GlobalEventEmitter";
22
22
  import { isGlassesConnected, useGlassesStore } from "../stores/glasses";
23
23
  import { resolveOtaManifestUrl } from "./otaManifestUrl";
24
24
  import { hotspotOtaTransport } from "./HotspotOtaTransport";
25
25
  import { deriveDisplayState } from "./otaDisplayState";
26
- import { BES_CONTINUE_LOCKOUT_MS, DOWNLOAD_STUCK_TIMEOUT_MS, GLOBAL_OTA_TIMEOUT_MS, LEGACY_APK_COMPLETION_SETTLE_MS, LEGACY_BES_CONTINUE_LOCKOUT_MS, LEGACY_DOWNLOAD_STUCK_TIMEOUT_MS, LEGACY_GLOBAL_OTA_TIMEOUT_MS, LEGACY_MTK_INSTALL_TIMEOUT_MS, LEGACY_MTK_SIM_CAP_PERCENT, LEGACY_MTK_SIM_FLOOR_PERCENT, LEGACY_MTK_SIM_TICK_MS, LEGACY_MTK_STALL_DETECT_MS, LEGACY_MTK_STALL_ZONE_MAX_PERCENT, LEGACY_MTK_STALL_ZONE_MIN_PERCENT, LEGACY_PING_INTERVAL_MS, LEGACY_PROGRESS_TIMEOUT_MS, LEGACY_RETRY_INTERVAL_MS, MAX_RETRIES, MTK_INSTALL_TIMEOUT_MS, OtaProgressMessages, PING_INTERVAL_MS, PROGRESS_TIMEOUT_MS, QUERY_REPLY_TIMEOUT_MS, RETRY_INTERVAL_MS, isLegacyShapedOtaSession, isLegacyShapedOtaStatus, selectOtaProtocolProfile, } from "./otaInstallPolicy";
26
+ import { BES_CONTINUE_LOCKOUT_MS, BES_RESTART_TIMEOUT_MS, DOWNLOAD_STUCK_TIMEOUT_MS, GLOBAL_OTA_TIMEOUT_MS, LEGACY_APK_COMPLETION_SETTLE_MS, LEGACY_BES_CONTINUE_LOCKOUT_MS, LEGACY_BES_RESTART_TIMEOUT_MS, LEGACY_DOWNLOAD_STUCK_TIMEOUT_MS, LEGACY_GLOBAL_OTA_TIMEOUT_MS, LEGACY_MTK_INSTALL_TIMEOUT_MS, LEGACY_MTK_SIM_CAP_PERCENT, LEGACY_MTK_SIM_FLOOR_PERCENT, LEGACY_MTK_SIM_TICK_MS, LEGACY_MTK_STALL_DETECT_MS, LEGACY_MTK_STALL_ZONE_MAX_PERCENT, LEGACY_MTK_STALL_ZONE_MIN_PERCENT, LEGACY_PING_INTERVAL_MS, LEGACY_PROGRESS_TIMEOUT_MS, LEGACY_RETRY_INTERVAL_MS, MAX_RETRIES, MTK_INSTALL_TIMEOUT_MS, OtaProgressMessages, PING_INTERVAL_MS, PROGRESS_TIMEOUT_MS, QUERY_REPLY_TIMEOUT_MS, RETRY_INTERVAL_MS, isLegacyShapedOtaSession, isLegacyShapedOtaStatus, selectOtaProtocolProfile, } from "./otaInstallPolicy";
27
27
  function isTerminalForWatchdog(d) {
28
28
  return d === "complete" || d === "failed" || d === "restarting";
29
29
  }
@@ -156,6 +156,7 @@ class OtaInstallCoordinator {
156
156
  pingInterval = null;
157
157
  queryReplyTimeout = null;
158
158
  continueLockoutTimer = null;
159
+ restartRecoveryTimeout = null;
159
160
  legacyApkSettleTimer = null;
160
161
  mtkStallDetectTimer = null;
161
162
  mtkSimTickTimer = null;
@@ -306,7 +307,7 @@ class OtaInstallCoordinator {
306
307
  void this.sendOtaStartWithWatchdogs();
307
308
  }
308
309
  else {
309
- void BluetoothSdk.sendOtaQueryStatus().catch(() => { });
310
+ void BluetoothSdk.queryOtaStatus().catch(() => { });
310
311
  this.armQueryReplyFallback("retry");
311
312
  }
312
313
  }
@@ -349,16 +350,17 @@ class OtaInstallCoordinator {
349
350
  * stays in the screen): clear the selected update, and after an APK step
350
351
  * clear the stale build number so the next check re-reads version_info.
351
352
  */
352
- finish() {
353
+ async finish() {
353
354
  if (this.otaStartOwnership?.outcome !== "pending") {
354
355
  this.otaStartOwnership = null;
355
356
  }
356
357
  useGlassesStore.getState().setOtaUpdateAvailable(null);
357
358
  if (this.apkStepSeen) {
358
- BluetoothSdk.updateGlasses({ buildNumber: "" });
359
359
  useGlassesStore.getState().setGlassesInfo({ buildNumber: "" });
360
360
  }
361
- void this.teardownHotspotTransport();
361
+ // The post-install check needs internet again. Do not let callers leave the
362
+ // terminal screen while Android still owns the no-internet hotspot route.
363
+ await this.teardownHotspotTransport();
362
364
  }
363
365
  /**
364
366
  * Abandon an interrupted session (the super-mode skip): terminal cleanup
@@ -366,8 +368,8 @@ class OtaInstallCoordinator {
366
368
  * deriveDisplayState doesn't resurrect the stale install when the host
367
369
  * navigates away and back through /ota routes.
368
370
  */
369
- discard() {
370
- this.finish();
371
+ async discard() {
372
+ await this.finish();
371
373
  const store = useGlassesStore.getState();
372
374
  store.setOtaStatus(null);
373
375
  store.setOtaProgress(null);
@@ -403,6 +405,7 @@ class OtaInstallCoordinator {
403
405
  versionChangePhase: this.deriveVersionChangePhase(connected),
404
406
  hotspotPhase: this.hotspotPhase,
405
407
  hotspotArtifactPercent: this.hotspotArtifactPercent,
408
+ transport: this.selectedTransport,
406
409
  };
407
410
  }
408
411
  /**
@@ -848,12 +851,24 @@ class OtaInstallCoordinator {
848
851
  // (15s unified, 35s for legacy-shaped sessions — WP 8C-f).
849
852
  if (displayStateChanged && displayState === "restarting") {
850
853
  this.clearContinueLockoutTimer();
854
+ this.clearRestartRecoveryTimeout();
851
855
  const lockoutMs = legacySession ? LEGACY_BES_CONTINUE_LOCKOUT_MS : BES_CONTINUE_LOCKOUT_MS;
856
+ const restartTimeoutMs = legacySession ? LEGACY_BES_RESTART_TIMEOUT_MS : BES_RESTART_TIMEOUT_MS;
852
857
  this.setContinueButtonDisabled(true);
853
858
  this.continueLockoutTimer = setTimeout(() => {
854
859
  this.continueLockoutTimer = null;
855
860
  this.setContinueButtonDisabled(false);
856
861
  }, lockoutMs);
862
+ this.restartRecoveryTimeout = setTimeout(() => {
863
+ this.restartRecoveryTimeout = null;
864
+ if (this.computeDisplayStateNow() !== "restarting")
865
+ return;
866
+ console.log(`[OTA_PROGRESS] watchdog: glasses did not finish restarting in ${restartTimeoutMs}ms`);
867
+ this.setErrorMsg(OtaProgressMessages.restartTimeout);
868
+ }, restartTimeoutMs);
869
+ }
870
+ else if (displayStateChanged) {
871
+ this.clearRestartRecoveryTimeout();
857
872
  }
858
873
  // Legacy MTK install stall simulation (WP 8C-e): display-only. The simulation
859
874
  // timers only ever touch the projected snapshot percent — they never feed back
@@ -908,7 +923,7 @@ class OtaInstallCoordinator {
908
923
  if (this.otaStartOwnership?.outcome === "pending") {
909
924
  void this.sendOtaStartWithWatchdogs();
910
925
  }
911
- void BluetoothSdk.sendOtaQueryStatus();
926
+ void BluetoothSdk.queryOtaStatus().catch(() => { });
912
927
  this.armQueryReplyFallback("reconnect");
913
928
  return true;
914
929
  }
@@ -946,7 +961,7 @@ class OtaInstallCoordinator {
946
961
  if (noSessionYet) {
947
962
  if (!isIdleStatus && this.otaStartOwnership?.outcome === "acknowledged") {
948
963
  console.log("[OTA_PROGRESS] initial mount, acknowledged session has no cached status — reconciling");
949
- void BluetoothSdk.sendOtaQueryStatus().catch(() => { });
964
+ void BluetoothSdk.queryOtaStatus().catch(() => { });
950
965
  this.armQueryReplyFallback("initial-mount");
951
966
  return;
952
967
  }
@@ -961,7 +976,7 @@ class OtaInstallCoordinator {
961
976
  }
962
977
  else {
963
978
  console.log("[OTA_PROGRESS] initial mount, session exists, sending ota_query_status");
964
- void BluetoothSdk.sendOtaQueryStatus();
979
+ void BluetoothSdk.queryOtaStatus().catch(() => { });
965
980
  this.armQueryReplyFallback("initial-mount");
966
981
  }
967
982
  }
@@ -988,7 +1003,7 @@ class OtaInstallCoordinator {
988
1003
  this.clearProgressTimeout();
989
1004
  this.onFirstActivity();
990
1005
  this.onFirstNonZeroProgress();
991
- void BluetoothSdk.sendOtaQueryStatus();
1006
+ void BluetoothSdk.queryOtaStatus().catch(() => { });
992
1007
  useGlassesStore.getState().setMtkUpdatedThisSession(true);
993
1008
  };
994
1009
  // --- legacy (< 37) compatibility, ported from progress-legacy.tsx (WP 8C) ---
@@ -1148,7 +1163,7 @@ class OtaInstallCoordinator {
1148
1163
  const s = useGlassesStore.getState().otaStatus;
1149
1164
  if (s?.stepType === "apk" && s.phase === "install" && s.status === "in_progress") {
1150
1165
  this.apkInstallPollInFlight = true;
1151
- void BluetoothSdk.sendOtaQueryStatus()
1166
+ void BluetoothSdk.queryOtaStatus()
1152
1167
  .catch(() => { })
1153
1168
  .finally(() => {
1154
1169
  this.apkInstallPollInFlight = false;
@@ -1290,6 +1305,9 @@ class OtaInstallCoordinator {
1290
1305
  otaVersionUrl = this.hotspotManifestUrl;
1291
1306
  this.maybeArmStuckWatchdog();
1292
1307
  }
1308
+ if (!otaVersionUrl) {
1309
+ throw new Error("OTA is disabled because this build has no immutable manifest pin");
1310
+ }
1293
1311
  console.log(`[OTA_PROGRESS] sending ota_start with ${this.selectedTransport} manifest URL: ${otaVersionUrl}`);
1294
1312
  nativeStartAttempted = true;
1295
1313
  await BluetoothSdk.startOtaUpdate(otaVersionUrl);
@@ -1449,6 +1467,12 @@ class OtaInstallCoordinator {
1449
1467
  this.continueLockoutTimer = null;
1450
1468
  }
1451
1469
  }
1470
+ clearRestartRecoveryTimeout() {
1471
+ if (this.restartRecoveryTimeout) {
1472
+ clearTimeout(this.restartRecoveryTimeout);
1473
+ this.restartRecoveryTimeout = null;
1474
+ }
1475
+ }
1452
1476
  clearLegacyApkSettleTimer() {
1453
1477
  if (this.legacyApkSettleTimer) {
1454
1478
  clearTimeout(this.legacyApkSettleTimer);
@@ -1484,6 +1508,7 @@ class OtaInstallCoordinator {
1484
1508
  this.clearPingInterval();
1485
1509
  this.clearLegacyApkSettleTimer();
1486
1510
  this.clearMtkSimulationTimers();
1511
+ this.clearRestartRecoveryTimeout();
1487
1512
  }
1488
1513
  }
1489
1514
  /** Singleton — one install session machine per app process (matches the single progress screen). */