@glassly/engine 0.1.0-dev.61 → 0.1.0-dev.89

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 (147) hide show
  1. package/README.md +6 -2
  2. package/build/engine.d.ts +7 -2
  3. package/build/engine.d.ts.map +1 -1
  4. package/build/engine.js +3 -0
  5. package/build/engine.js.map +1 -1
  6. package/build/facades/display.d.ts +10 -3
  7. package/build/facades/display.d.ts.map +1 -1
  8. package/build/facades/display.js +11 -1
  9. package/build/facades/display.js.map +1 -1
  10. package/build/facades/displayMirror.d.ts +2 -6
  11. package/build/facades/displayMirror.d.ts.map +1 -1
  12. package/build/facades/displayMirror.js +7 -14
  13. package/build/facades/displayMirror.js.map +1 -1
  14. package/build/facades/glasses.d.ts +1 -0
  15. package/build/facades/glasses.d.ts.map +1 -1
  16. package/build/facades/glasses.js +2 -0
  17. package/build/facades/glasses.js.map +1 -1
  18. package/build/facades/ota.d.ts +3 -1
  19. package/build/facades/ota.d.ts.map +1 -1
  20. package/build/facades/ota.js +4 -2
  21. package/build/facades/ota.js.map +1 -1
  22. package/build/facades/phoneNotifications.d.ts +12 -2
  23. package/build/facades/phoneNotifications.d.ts.map +1 -1
  24. package/build/facades/phoneNotifications.js +30 -10
  25. package/build/facades/phoneNotifications.js.map +1 -1
  26. package/build/generated/releaseMetadata.js +3 -3
  27. package/build/generated/releaseMetadata.js.map +1 -1
  28. package/build/index.d.ts +2 -2
  29. package/build/index.d.ts.map +1 -1
  30. package/build/index.js +2 -2
  31. package/build/index.js.map +1 -1
  32. package/build/react/MentraLiveOtaFlow.d.ts +6 -1
  33. package/build/react/MentraLiveOtaFlow.d.ts.map +1 -1
  34. package/build/react/MentraLiveOtaFlow.js +136 -14
  35. package/build/react/MentraLiveOtaFlow.js.map +1 -1
  36. package/build/react/useMentraLiveOta.d.ts +5 -1
  37. package/build/react/useMentraLiveOta.d.ts.map +1 -1
  38. package/build/react/useMentraLiveOta.js +135 -58
  39. package/build/react/useMentraLiveOta.js.map +1 -1
  40. package/build/services/CustomDashboardService.d.ts +11 -10
  41. package/build/services/CustomDashboardService.d.ts.map +1 -1
  42. package/build/services/CustomDashboardService.js +70 -69
  43. package/build/services/CustomDashboardService.js.map +1 -1
  44. package/build/services/DashboardCoordinator.d.ts +26 -0
  45. package/build/services/DashboardCoordinator.d.ts.map +1 -0
  46. package/build/services/DashboardCoordinator.js +125 -0
  47. package/build/services/DashboardCoordinator.js.map +1 -0
  48. package/build/services/DeviceEventRouter.d.ts.map +1 -1
  49. package/build/services/DeviceEventRouter.js +5 -7
  50. package/build/services/DeviceEventRouter.js.map +1 -1
  51. package/build/services/DisplayProcessor.d.ts +3 -3
  52. package/build/services/DisplayProcessor.js +3 -3
  53. package/build/services/DisplayProcessor.js.map +1 -1
  54. package/build/services/LocalDisplayManager.d.ts +12 -2
  55. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  56. package/build/services/LocalDisplayManager.js +68 -14
  57. package/build/services/LocalDisplayManager.js.map +1 -1
  58. package/build/services/OtaInstallCoordinator.d.ts +2 -0
  59. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  60. package/build/services/OtaInstallCoordinator.js +21 -1
  61. package/build/services/OtaInstallCoordinator.js.map +1 -1
  62. package/build/services/PhoneNotificationsSync.d.ts +13 -0
  63. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  64. package/build/services/PhoneNotificationsSync.js +101 -9
  65. package/build/services/PhoneNotificationsSync.js.map +1 -1
  66. package/build/services/PhonePhotoCoordinator.js +1 -1
  67. package/build/services/PhonePhotoCoordinator.js.map +1 -1
  68. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  69. package/build/services/PhoneStreamCoordinator.js +9 -6
  70. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  71. package/build/services/SceneRenderer.d.ts.map +1 -1
  72. package/build/services/SceneRenderer.js +5 -0
  73. package/build/services/SceneRenderer.js.map +1 -1
  74. package/build/services/StreamLifecycleController.d.ts +4 -0
  75. package/build/services/StreamLifecycleController.d.ts.map +1 -1
  76. package/build/services/StreamLifecycleController.js +4 -0
  77. package/build/services/StreamLifecycleController.js.map +1 -1
  78. package/build/services/effectiveCapabilities.d.ts +1 -1
  79. package/build/services/effectiveCapabilities.d.ts.map +1 -1
  80. package/build/services/effectiveCapabilities.js +9 -4
  81. package/build/services/effectiveCapabilities.js.map +1 -1
  82. package/build/services/iosNotificationAppCatalog.d.ts +13 -0
  83. package/build/services/iosNotificationAppCatalog.d.ts.map +1 -0
  84. package/build/services/iosNotificationAppCatalog.js +28 -0
  85. package/build/services/iosNotificationAppCatalog.js.map +1 -0
  86. package/build/services/otaInstallPolicy.d.ts +4 -0
  87. package/build/services/otaInstallPolicy.d.ts.map +1 -1
  88. package/build/services/otaInstallPolicy.js +4 -0
  89. package/build/services/otaInstallPolicy.js.map +1 -1
  90. package/build/stores/display.d.ts +4 -4
  91. package/build/stores/display.d.ts.map +1 -1
  92. package/build/stores/display.js +12 -36
  93. package/build/stores/display.js.map +1 -1
  94. package/build/stores/glasses.d.ts.map +1 -1
  95. package/build/stores/glasses.js +2 -0
  96. package/build/stores/glasses.js.map +1 -1
  97. package/build/stores/settings.d.ts.map +1 -1
  98. package/build/stores/settings.js +53 -2
  99. package/build/stores/settings.js.map +1 -1
  100. package/build/types/applet.d.ts +1 -1
  101. package/build/types/applet.js.map +1 -1
  102. package/build/types/capabilities/even-realities-g2.d.ts.map +1 -1
  103. package/build/types/capabilities/even-realities-g2.js +2 -2
  104. package/build/types/capabilities/even-realities-g2.js.map +1 -1
  105. package/build/types/hardware.d.ts.map +1 -1
  106. package/build/types/hardware.js +1 -0
  107. package/build/types/hardware.js.map +1 -1
  108. package/build/types/index.d.ts +1 -1
  109. package/build/types/index.d.ts.map +1 -1
  110. package/build/types/index.js +1 -1
  111. package/build/types/index.js.map +1 -1
  112. package/build/utils/display/composer/ColumnComposer.d.ts +1 -1
  113. package/build/utils/display/composer/ColumnComposer.js +1 -1
  114. package/build/utils/display/composer/ColumnComposer.js.map +1 -1
  115. package/package.json +8 -7
  116. package/src/engine.ts +3 -0
  117. package/src/facades/display.ts +11 -1
  118. package/src/facades/displayMirror.ts +7 -15
  119. package/src/facades/glasses.ts +2 -0
  120. package/src/facades/ota.ts +4 -2
  121. package/src/facades/phoneNotifications.ts +32 -10
  122. package/src/generated/releaseMetadata.ts +3 -3
  123. package/src/index.ts +2 -1
  124. package/src/react/MentraLiveOtaFlow.tsx +226 -41
  125. package/src/react/useMentraLiveOta.ts +146 -60
  126. package/src/services/CustomDashboardService.ts +67 -66
  127. package/src/services/DashboardCoordinator.ts +121 -0
  128. package/src/services/DeviceEventRouter.ts +5 -7
  129. package/src/services/DisplayProcessor.ts +3 -3
  130. package/src/services/LocalDisplayManager.ts +68 -16
  131. package/src/services/OtaInstallCoordinator.ts +21 -0
  132. package/src/services/PhoneNotificationsSync.ts +125 -9
  133. package/src/services/PhonePhotoCoordinator.ts +1 -1
  134. package/src/services/PhoneStreamCoordinator.ts +10 -6
  135. package/src/services/SceneRenderer.ts +4 -0
  136. package/src/services/StreamLifecycleController.ts +4 -0
  137. package/src/services/effectiveCapabilities.ts +9 -4
  138. package/src/services/iosNotificationAppCatalog.ts +39 -0
  139. package/src/services/otaInstallPolicy.ts +4 -0
  140. package/src/stores/display.ts +16 -48
  141. package/src/stores/glasses.ts +2 -0
  142. package/src/stores/settings.ts +53 -2
  143. package/src/types/applet.ts +1 -1
  144. package/src/types/capabilities/even-realities-g2.ts +3 -2
  145. package/src/types/hardware.ts +1 -0
  146. package/src/types/index.ts +1 -1
  147. package/src/utils/display/composer/ColumnComposer.ts +1 -1
package/README.md CHANGED
@@ -69,7 +69,7 @@ import BluetoothSdk from "@glassly/engine/bluetooth-sdk"
69
69
  ### Mentra Live OTA flow
70
70
 
71
71
  Bluetooth-only hosts can render the OTA flow without configuring or starting
72
- the authenticated cloud/miniapp runtime:
72
+ the authenticated cloud connection or miniapp runtime:
73
73
 
74
74
  ```tsx
75
75
  import {MentraLiveOtaFlow} from "@glassly/engine/ota"
@@ -91,10 +91,14 @@ const ota = useMentraLiveOta({onFinished, onOpenWifiSetup})
91
91
  // Render ota.state.screen and invoke ota.install(), retryInstall(), finish(), etc.
92
92
  ```
93
93
 
94
+ On `up_to_date`, use `ota.state.completedUpdate` to distinguish a completed
95
+ update session from a standalone check. `releaseTransition` is optional release
96
+ metadata and is not a completion marker.
97
+
94
98
  Customize presentation only. Engine must remain responsible for OTA ordering,
95
99
  hotspot staging, restart recovery, retries, and verification. The canonical
96
100
  stock and custom integration guide is
97
- [Update Mentra Live](https://docs.glassly.com/mentra-live/software-update).
101
+ [Update Mentra Live](https://docs.glassly.com/bluetooth-sdk/software-update).
98
102
 
99
103
  Released Engine packages contain a literal immutable OTA manifest URL and
100
104
  SHA-256 generated by coordinated release CI. Modern-glasses manifest selection
package/build/engine.d.ts CHANGED
@@ -256,6 +256,7 @@ export declare const engine: {
256
256
  setBlocklist: (packageNames: string[]) => import("typesafe-ts").AsyncResult<void, Error>;
257
257
  hasListenerPermission: () => Promise<boolean>;
258
258
  requestListenerPermission: () => Promise<boolean> | undefined;
259
+ ancsAuthorized: () => boolean;
259
260
  };
260
261
  /** OS permissions (check/request/openSettings + per-miniapp requirements). */
261
262
  permissions: {
@@ -333,8 +334,12 @@ export declare const engine: {
333
334
  mirror: {
334
335
  current(): import(".").DisplayMirrorEvent;
335
336
  onMirror(cb: (event: import(".").DisplayMirrorEvent) => void): () => void;
336
- view(): string;
337
- setView(view: "main" | "dashboard"): void;
337
+ };
338
+ dashboard: {
339
+ isOpen: () => boolean;
340
+ setHeadUp: (up: boolean) => void;
341
+ open: () => void;
342
+ close: () => void;
338
343
  };
339
344
  text: (text: string, x?: number | undefined, y?: number | undefined, size?: number | undefined) => Promise<void>;
340
345
  clear: () => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAC,SAAS,EAAkD,aAAa,EAAC,MAAM,qBAAqB,CAAA;AAwC5G,eAAO,MAAM,MAAM;IACjB,2EAA2E;;IAE3E,8EAA8E;;IAE9E;iDAC6C;;IAmF7C;;yCAEqC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA+BrC,mFAAmF;;;;;;;;IAEnF,sEAAsE;;;;;;;;;;;;IAEtE,qFAAqF;;;gBAxGvD,CAAC;mBAAkB,CAAA;;;gBAa/C,CARD;mBAAiB,CAAC;;;;;;;;;;;;;;;;IAqGnB,qEAAqE;;;;IAErE,sFAAsF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAEtF,6EAA6E;;;;;;;;;;;;IAE7E,iFAAiF;;;;;;;;;;;;;;;;;;;IAEjF,8CAA8C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAE9C,uDAAuD;;;;;;;;;;;;IAEvD,8EAA8E;;;;;;;IAE9E,qFAAqF;;;;IAErF,4EAA4E;;;;;;;;;;;IAE5E,+EAA+E;;;;;;;;;;;;;;;IAE/E,kFAAkF;;;;;+BA5H7B,CAAC;;;+BAUtC,CAAC;;;;;;;;;;;;IAoHjB,mFAAmF;;;;;;;;;;;;;;;;;;;;;;;;;CAGpF,CAAA"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAC,SAAS,EAAkD,aAAa,EAAC,MAAM,qBAAqB,CAAA;AAyC5G,eAAO,MAAM,MAAM;IACjB,2EAA2E;;IAE3E,8EAA8E;;IAE9E;iDAC6C;;IAoF7C;;yCAEqC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAgCrC,mFAAmF;;;;;;;;IAEnF,sEAAsE;;;;;;;;;;;;IAEtE,qFAAqF;;;gBA5GxD,CAAC;mBAClB,CAAC;;;gBAIyC,CAAC;mBAAiB,CAAC;;;;;;;;;;;;;;;;IAyGzE,qEAAqE;;;;IAErE,sFAAsF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAEtF,6EAA6E;;;;;;;;;;;;IAE7E,iFAAiF;;;;;;;;;;;;;;;;;;;IAEjF,8CAA8C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAE9C,uDAAuD;;;;;;;;;;;;;IAEvD,8EAA8E;;;;;;;IAE9E,qFAAqF;;;;IAErF,4EAA4E;;;;;;;;;;;IAE5E,+EAA+E;;;;;;;;;;;;;;;IAE/E,kFAAkF;;;;;+BA9HvC,CAAC;;;+BASyB,CAAC;;;;;;;;;;;;IAuHtE,mFAAmF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAGpF,CAAA"}
package/build/engine.js CHANGED
@@ -46,6 +46,7 @@ import { speakers } from "./facades/speakers";
46
46
  import { providerKeys } from "./facades/providerKeys";
47
47
  import { logBuffer } from "./utils/devLogging";
48
48
  import customDashboardService from "./services/CustomDashboardService";
49
+ import dashboardCoordinator from "./services/DashboardCoordinator";
49
50
  import gestureDotsService from "./services/GestureDotsService";
50
51
  export const engine = {
51
52
  /** Front door — hand engine auth + config, then start/stop the runtime. */
@@ -68,6 +69,7 @@ export const engine = {
68
69
  // syncs below react to them.
69
70
  startGlassesStatusProjection((changed) => localMiniappRuntime.forwardEvent("glasses_connection_state", changed));
70
71
  customDashboardService.start();
72
+ dashboardCoordinator.start();
71
73
  gestureDotsService.start();
72
74
  // Route the rest of the inbound device events (wifi/hotspot/gallery -> stores+bus,
73
75
  // photo/stream -> coordinators, button/touch/accel/head -> miniapps, save_setting ->
@@ -150,6 +152,7 @@ export const engine = {
150
152
  await safely("glasses settings sync", stopGlassesSettingsSync);
151
153
  await safely("glasses status projection", stopGlassesStatusProjection);
152
154
  await safely("device event router", stopDeviceEventRouter);
155
+ await safely("dashboard coordinator", () => dashboardCoordinator.stop());
153
156
  await safely("custom dashboard service", () => customDashboardService.stop());
154
157
  await safely("gesture dots service", () => gestureDotsService.stop());
155
158
  await safely("ota service", stopOtaService);
@@ -1 +1 @@
1
- {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `engine` — the namespaced OEM-facing engine API (the \"(A) host API\" from the\n * Phase-1 contract). Host UI calls `engine.<domain>.<method>()`; each domain is a\n * typed facade over the runtime. (Exported from the `@glassly/engine` module, whose\n * name stays `engine` in code; the public API surface is `engine`.)\n *\n * It grows one facade at a time. The migration-era flat store/service exports\n * remain in a private GlasslyOS host package; they shrink as screens move onto\n * `engine.*`.\n */\nimport {configure, start as bootstrapStart, stop as bootstrapStop, updateUiSeams} from \"./runtime/bootstrap\"\nimport {cloudClientService} from \"./services/CloudClientService\"\nimport {hydrateDeviceStore, demoteOrphanedDefaultWearable} from \"./services/DeviceStoreHydration\"\nimport {startGlassesSettingsSync, stopGlassesSettingsSync} from \"./services/GlassesSettingsSync\"\nimport {startGlassesStatusProjection, stopGlassesStatusProjection} from \"./services/GlassesStatusProjection\"\nimport {startOtaService, stopOtaService} from \"./services/OtaService\"\nimport {startAudioCloudUplink, stopAudioCloudUplink} from \"./services/AudioCloudUplink\"\nimport sonioxDirectService from \"./services/soniox/SonioxDirectService\"\nimport {startSupportProfileSync, stopSupportProfileSync} from \"./services/SupportProfileSync\"\nimport {startDeviceEventRouter, stopDeviceEventRouter} from \"./services/DeviceEventRouter\"\nimport {startPhoneNotificationsSync, stopPhoneNotificationsSync} from \"./services/PhoneNotificationsSync\"\nimport {startSpeakerIdSync, stopSpeakerIdSync} from \"./services/SpeakerIdSync\"\nimport {startCaptionsTesterReportService, stopCaptionsTesterReportService} from \"./services/CaptionsTesterReportService\"\nimport {startJSCrashloopReportService, stopJSCrashloopReportService} from \"./services/JSCrashloopReportService\"\nimport {ensureMiniappEngine, stopMiniappEngine} from \"./services/MiniappEngine\"\nimport localMiniappRuntime from \"./services/LocalMiniappRuntime\"\nimport displayProcessor from \"./services/DisplayProcessor\"\nimport {gallerySyncService} from \"./services/asg/gallerySyncService\"\nimport {glasses} from \"./facades/glasses\"\nimport {display} from \"./facades/display\"\nimport {speech} from \"./facades/speech\"\nimport {session} from \"./facades/session\"\nimport {settings} from \"./facades/settings\"\nimport {dev} from \"./facades/dev\"\nimport {reports} from \"./facades/reports\"\nimport {ota} from \"./facades/ota\"\nimport {gallery} from \"./facades/gallery\"\nimport {miniapps} from \"./facades/miniapps\"\nimport {pairing} from \"./facades/pairing\"\nimport {phoneNotifications} from \"./facades/phoneNotifications\"\nimport {permissions} from \"./facades/permissions\"\nimport {notifications} from \"./facades/notifications\"\nimport {maps} from \"./facades/maps\"\nimport {memory} from \"./facades/memory\"\nimport {speakers} from \"./facades/speakers\"\nimport {providerKeys} from \"./facades/providerKeys\"\nimport {logBuffer} from \"./utils/devLogging\"\nimport customDashboardService from \"./services/CustomDashboardService\"\nimport gestureDotsService from \"./services/GestureDotsService\"\n\nexport const engine = {\n /** Front door — hand engine auth + config, then start/stop the runtime. */\n configure,\n /** Merge host-UI seams on a running runtime (scan overlay, wifi setup, …). */\n updateUiSeams,\n /** Start the runtime: mark started + construct/connect the cloud client + begin\n * syncing device settings to the glasses. */\n async start() {\n // Reports attach recent phone logs, so engine owns console interception for\n // OEM hosts too. Idempotent; the Glassly host may start it earlier.\n logBuffer.startConsoleInterception()\n await bootstrapStart()\n // Construct + connect the cloud client so the documented configure()+start()\n // lifecycle yields a live engine.session for OEMs. Idempotent for repeated\n // start() calls.\n cloudClientService.init()\n // Project native device status -> the engine stores (the inbound feed the rest\n // of the runtime reads). Established first so the stores are live before the\n // syncs below react to them.\n startGlassesStatusProjection((changed) => localMiniappRuntime.forwardEvent(\"glasses_connection_state\", changed))\n customDashboardService.start()\n gestureDotsService.start()\n // Route the rest of the inbound device events (wifi/hotspot/gallery -> stores+bus,\n // photo/stream -> coordinators, button/touch/accel/head -> miniapps, save_setting ->\n // store, miniapp_selected -> launcher) so a bare OEM gets device data, not just the\n // Glassly app's MantleManager.\n startDeviceEventRouter()\n // Hydration contract: the native DeviceStore is in-memory and starts empty\n // every launch; seed it from the persisted settings BEFORE start() resolves\n // so every post-start getDefaultDevice() read is a trustworthy two-state\n // answer (no false \"absent\" for genuinely-paired users at cold start).\n // Ordered before startGlassesSettingsSync so the settings load can't\n // double-push through the change subscription. A failed hydration must not\n // brick the runtime: start() continues, and the guards that consume\n // hasDefaultDevice() see the rejection and fail open.\n try {\n await hydrateDeviceStore()\n // Boot repair: a persisted default_wearable without a native default\n // device (identity abandoned mid-pairing, or resurrected from the server\n // before identity stopped syncing) demotes to pending_wearable so hosts\n // render finish-pairing instead of a connect that would throw.\n await demoteOrphanedDefaultWearable()\n } catch (error) {\n console.warn(\"engine.start: device-store hydration failed:\", error instanceof Error ? error.message : error)\n }\n // Project the glasses' OTA events into the store for the engine.ota read surface.\n startOtaService()\n // Forward glasses mic_lc3 frames to the v2 cloud session so cloud transcription\n // works for any host (not just the Glassly app's host-side MantleManager fork).\n startAudioCloudUplink()\n // Phone -> Soniox direct transcription (user-supplied API key). Started\n // after device-store hydration so the persisted key is already loaded.\n sonioxDirectService.start()\n try {\n await cloudClientService.syncCoreTokenToBluetooth()\n } catch (error) {\n console.warn(\n \"engine.start: initial Cloud V2 core token sync failed:\",\n error instanceof Error ? error.message : error,\n )\n }\n startSupportProfileSync()\n // Push device-setting changes to the glasses for ANY host, so\n // engine.glasses.settings.set() reaches the device (not just the Glassly app).\n startGlassesSettingsSync()\n // Same for phone-notification config -> the native listener (Android).\n startPhoneNotificationsSync()\n // On-device speaker identification: sync the enabled setting + enrolled\n // profile centroids to the native SpeakerIdEngine, and start the resolver\n // that annotates cloud transcripts with resolved speaker identities.\n startSpeakerIdSync()\n // Android internal/e2e: laptop captions tester can broadcast a failure intent;\n // engine owns turning that into a Cloud V2 report.\n startCaptionsTesterReportService()\n // GlasslyJS crashloop-disabled is runtime state; engine owns filing the\n // automatic report while hosts only render alert/telemetry side effects.\n startJSCrashloopReportService()\n // Bring up the local-miniapp engine so a bare OEM can run GlasslyJS miniapps:\n // the LocalMiniappRuntime (registry + WebView bridge), the GlasslyJS router\n // (crust-bound spawn/dispatch pump + launcher wiring), the DisplayProcessor\n // (layout arbitration over the engine stores), and the gallery-sync service.\n // All idempotent — when the Glassly app's host bootstrap also calls these, the\n // second call is a no-op. Host-only crashloop telemetry is attached\n // separately by the Glassly app.\n localMiniappRuntime.initialize()\n ensureMiniappEngine()\n displayProcessor.attachToRuntime()\n gallerySyncService.initialize()\n },\n /** Stop the runtime: tear down the settings sync + cloud client + mark stopped.\n * Each step is guarded so one failing teardown can't skip the rest and leak\n * the remaining runtime services. */\n async stop() {\n const safely = async (label: string, step: () => unknown): Promise<void> => {\n try {\n await step()\n } catch (error) {\n console.warn(`engine.stop: ${label} failed:`, error)\n }\n }\n await safely(\"glasses settings sync\", stopGlassesSettingsSync)\n await safely(\"glasses status projection\", stopGlassesStatusProjection)\n await safely(\"device event router\", stopDeviceEventRouter)\n await safely(\"custom dashboard service\", () => customDashboardService.stop())\n await safely(\"gesture dots service\", () => gestureDotsService.stop())\n await safely(\"ota service\", stopOtaService)\n await safely(\"audio cloud uplink\", stopAudioCloudUplink)\n await safely(\"soniox direct service\", () => sonioxDirectService.stop())\n await safely(\"support profile sync\", stopSupportProfileSync)\n await safely(\"phone notifications sync\", stopPhoneNotificationsSync)\n await safely(\"speaker id sync\", stopSpeakerIdSync)\n await safely(\"captions tester report service\", stopCaptionsTesterReportService)\n await safely(\"glasslyjs crashloop report service\", stopJSCrashloopReportService)\n await safely(\"miniapp engine\", stopMiniappEngine)\n await safely(\"local miniapp runtime\", () => localMiniappRuntime.cleanup())\n await safely(\"display processor\", () => displayProcessor.detachFromRuntime())\n await safely(\"gallery sync service\", () => gallerySyncService.cleanup())\n await safely(\"cloud client\", () => cloudClientService.shutdown())\n await safely(\"bootstrap\", bootstrapStop)\n },\n glasses,\n speech,\n /** Cloud (cloud-v2) live-session status + account ops — engine owns the client. */\n session,\n /** Typed keyed user settings over the engine-owned settings store. */\n settings,\n /** Developer/debug surface (backend+cloud URL overrides, reconnect, min version). */\n dev,\n /** Bug-report, automatic runtime report, and feedback submission. */\n reports,\n /** Firmware OTA read/observe surface (updateAvailable/status + on* subscriptions). */\n ota,\n /** Gallery sync: status/onStatus · onNotice (host renders) · sync/cancel. */\n gallery,\n /** Miniapp lifecycle (the WebView bridge primitives are exported separately). */\n miniapps,\n /** First-time glasses discovery + pairing. */\n pairing,\n /** Phone→glasses notification forwarding (Android). */\n phoneNotifications,\n /** OS permissions (check/request/openSettings + per-miniapp requirements). */\n permissions,\n /** Inbound alerts engine→host (crashloop, version-incompatible, connection loss). */\n notifications,\n /** Maps providers — user-supplied Mapbox access token (Settings > Keys). */\n maps,\n /** Shared user-scoped memory timeline (Settings > Memory + session.memory). */\n memory,\n /** Device-local voice profiles for on-device speaker ID (Settings > Speakers). */\n speakers,\n /** AI/voice provider key validation — user-supplied keys (Settings > API Keys). */\n providerKeys,\n display,\n}\n"]}
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `engine` — the namespaced OEM-facing engine API (the \"(A) host API\" from the\n * Phase-1 contract). Host UI calls `engine.<domain>.<method>()`; each domain is a\n * typed facade over the runtime. (Exported from the `@glassly/engine` module, whose\n * name stays `engine` in code; the public API surface is `engine`.)\n *\n * It grows one facade at a time. The migration-era flat store/service exports\n * remain in a private GlasslyOS host package; they shrink as screens move onto\n * `engine.*`.\n */\nimport {configure, start as bootstrapStart, stop as bootstrapStop, updateUiSeams} from \"./runtime/bootstrap\"\nimport {cloudClientService} from \"./services/CloudClientService\"\nimport {hydrateDeviceStore, demoteOrphanedDefaultWearable} from \"./services/DeviceStoreHydration\"\nimport {startGlassesSettingsSync, stopGlassesSettingsSync} from \"./services/GlassesSettingsSync\"\nimport {startGlassesStatusProjection, stopGlassesStatusProjection} from \"./services/GlassesStatusProjection\"\nimport {startOtaService, stopOtaService} from \"./services/OtaService\"\nimport {startAudioCloudUplink, stopAudioCloudUplink} from \"./services/AudioCloudUplink\"\nimport sonioxDirectService from \"./services/soniox/SonioxDirectService\"\nimport {startSupportProfileSync, stopSupportProfileSync} from \"./services/SupportProfileSync\"\nimport {startDeviceEventRouter, stopDeviceEventRouter} from \"./services/DeviceEventRouter\"\nimport {startPhoneNotificationsSync, stopPhoneNotificationsSync} from \"./services/PhoneNotificationsSync\"\nimport {startSpeakerIdSync, stopSpeakerIdSync} from \"./services/SpeakerIdSync\"\nimport {startCaptionsTesterReportService, stopCaptionsTesterReportService} from \"./services/CaptionsTesterReportService\"\nimport {startJSCrashloopReportService, stopJSCrashloopReportService} from \"./services/JSCrashloopReportService\"\nimport {ensureMiniappEngine, stopMiniappEngine} from \"./services/MiniappEngine\"\nimport localMiniappRuntime from \"./services/LocalMiniappRuntime\"\nimport displayProcessor from \"./services/DisplayProcessor\"\nimport {gallerySyncService} from \"./services/asg/gallerySyncService\"\nimport {glasses} from \"./facades/glasses\"\nimport {display} from \"./facades/display\"\nimport {speech} from \"./facades/speech\"\nimport {session} from \"./facades/session\"\nimport {settings} from \"./facades/settings\"\nimport {dev} from \"./facades/dev\"\nimport {reports} from \"./facades/reports\"\nimport {ota} from \"./facades/ota\"\nimport {gallery} from \"./facades/gallery\"\nimport {miniapps} from \"./facades/miniapps\"\nimport {pairing} from \"./facades/pairing\"\nimport {phoneNotifications} from \"./facades/phoneNotifications\"\nimport {permissions} from \"./facades/permissions\"\nimport {notifications} from \"./facades/notifications\"\nimport {maps} from \"./facades/maps\"\nimport {memory} from \"./facades/memory\"\nimport {speakers} from \"./facades/speakers\"\nimport {providerKeys} from \"./facades/providerKeys\"\nimport {logBuffer} from \"./utils/devLogging\"\nimport customDashboardService from \"./services/CustomDashboardService\"\nimport dashboardCoordinator from \"./services/DashboardCoordinator\"\nimport gestureDotsService from \"./services/GestureDotsService\"\n\nexport const engine = {\n /** Front door — hand engine auth + config, then start/stop the runtime. */\n configure,\n /** Merge host-UI seams on a running runtime (scan overlay, wifi setup, …). */\n updateUiSeams,\n /** Start the runtime: mark started + construct/connect the cloud client + begin\n * syncing device settings to the glasses. */\n async start() {\n // Reports attach recent phone logs, so engine owns console interception for\n // OEM hosts too. Idempotent; the Glassly host may start it earlier.\n logBuffer.startConsoleInterception()\n await bootstrapStart()\n // Construct + connect the cloud client so the documented configure()+start()\n // lifecycle yields a live engine.session for OEMs. Idempotent for repeated\n // start() calls.\n cloudClientService.init()\n // Project native device status -> the engine stores (the inbound feed the rest\n // of the runtime reads). Established first so the stores are live before the\n // syncs below react to them.\n startGlassesStatusProjection((changed) => localMiniappRuntime.forwardEvent(\"glasses_connection_state\", changed))\n customDashboardService.start()\n dashboardCoordinator.start()\n gestureDotsService.start()\n // Route the rest of the inbound device events (wifi/hotspot/gallery -> stores+bus,\n // photo/stream -> coordinators, button/touch/accel/head -> miniapps, save_setting ->\n // store, miniapp_selected -> launcher) so a bare OEM gets device data, not just the\n // Glassly app's MantleManager.\n startDeviceEventRouter()\n // Hydration contract: the native DeviceStore is in-memory and starts empty\n // every launch; seed it from the persisted settings BEFORE start() resolves\n // so every post-start getDefaultDevice() read is a trustworthy two-state\n // answer (no false \"absent\" for genuinely-paired users at cold start).\n // Ordered before startGlassesSettingsSync so the settings load can't\n // double-push through the change subscription. A failed hydration must not\n // brick the runtime: start() continues, and the guards that consume\n // hasDefaultDevice() see the rejection and fail open.\n try {\n await hydrateDeviceStore()\n // Boot repair: a persisted default_wearable without a native default\n // device (identity abandoned mid-pairing, or resurrected from the server\n // before identity stopped syncing) demotes to pending_wearable so hosts\n // render finish-pairing instead of a connect that would throw.\n await demoteOrphanedDefaultWearable()\n } catch (error) {\n console.warn(\"engine.start: device-store hydration failed:\", error instanceof Error ? error.message : error)\n }\n // Project the glasses' OTA events into the store for the engine.ota read surface.\n startOtaService()\n // Forward glasses mic_lc3 frames to the v2 cloud session so cloud transcription\n // works for any host (not just the Glassly app's host-side MantleManager fork).\n startAudioCloudUplink()\n // Phone -> Soniox direct transcription (user-supplied API key). Started\n // after device-store hydration so the persisted key is already loaded.\n sonioxDirectService.start()\n try {\n await cloudClientService.syncCoreTokenToBluetooth()\n } catch (error) {\n console.warn(\n \"engine.start: initial Cloud V2 core token sync failed:\",\n error instanceof Error ? error.message : error,\n )\n }\n startSupportProfileSync()\n // Push device-setting changes to the glasses for ANY host, so\n // engine.glasses.settings.set() reaches the device (not just the Glassly app).\n startGlassesSettingsSync()\n // Same for phone-notification config -> the native listener (Android).\n startPhoneNotificationsSync()\n // On-device speaker identification: sync the enabled setting + enrolled\n // profile centroids to the native SpeakerIdEngine, and start the resolver\n // that annotates cloud transcripts with resolved speaker identities.\n startSpeakerIdSync()\n // Android internal/e2e: laptop captions tester can broadcast a failure intent;\n // engine owns turning that into a Cloud V2 report.\n startCaptionsTesterReportService()\n // GlasslyJS crashloop-disabled is runtime state; engine owns filing the\n // automatic report while hosts only render alert/telemetry side effects.\n startJSCrashloopReportService()\n // Bring up the local-miniapp engine so a bare OEM can run GlasslyJS miniapps:\n // the LocalMiniappRuntime (registry + WebView bridge), the GlasslyJS router\n // (crust-bound spawn/dispatch pump + launcher wiring), the DisplayProcessor\n // (layout arbitration over the engine stores), and the gallery-sync service.\n // All idempotent — when the Glassly app's host bootstrap also calls these, the\n // second call is a no-op. Host-only crashloop telemetry is attached\n // separately by the Glassly app.\n localMiniappRuntime.initialize()\n ensureMiniappEngine()\n displayProcessor.attachToRuntime()\n gallerySyncService.initialize()\n },\n /** Stop the runtime: tear down the settings sync + cloud client + mark stopped.\n * Each step is guarded so one failing teardown can't skip the rest and leak\n * the remaining runtime services. */\n async stop() {\n const safely = async (label: string, step: () => unknown): Promise<void> => {\n try {\n await step()\n } catch (error) {\n console.warn(`engine.stop: ${label} failed:`, error)\n }\n }\n await safely(\"glasses settings sync\", stopGlassesSettingsSync)\n await safely(\"glasses status projection\", stopGlassesStatusProjection)\n await safely(\"device event router\", stopDeviceEventRouter)\n await safely(\"dashboard coordinator\", () => dashboardCoordinator.stop())\n await safely(\"custom dashboard service\", () => customDashboardService.stop())\n await safely(\"gesture dots service\", () => gestureDotsService.stop())\n await safely(\"ota service\", stopOtaService)\n await safely(\"audio cloud uplink\", stopAudioCloudUplink)\n await safely(\"soniox direct service\", () => sonioxDirectService.stop())\n await safely(\"support profile sync\", stopSupportProfileSync)\n await safely(\"phone notifications sync\", stopPhoneNotificationsSync)\n await safely(\"speaker id sync\", stopSpeakerIdSync)\n await safely(\"captions tester report service\", stopCaptionsTesterReportService)\n await safely(\"glasslyjs crashloop report service\", stopJSCrashloopReportService)\n await safely(\"miniapp engine\", stopMiniappEngine)\n await safely(\"local miniapp runtime\", () => localMiniappRuntime.cleanup())\n await safely(\"display processor\", () => displayProcessor.detachFromRuntime())\n await safely(\"gallery sync service\", () => gallerySyncService.cleanup())\n await safely(\"cloud client\", () => cloudClientService.shutdown())\n await safely(\"bootstrap\", bootstrapStop)\n },\n glasses,\n speech,\n /** Cloud (cloud-v2) live-session status + account ops — engine owns the client. */\n session,\n /** Typed keyed user settings over the engine-owned settings store. */\n settings,\n /** Developer/debug surface (backend+cloud URL overrides, reconnect, min version). */\n dev,\n /** Bug-report, automatic runtime report, and feedback submission. */\n reports,\n /** Firmware OTA read/observe surface (updateAvailable/status + on* subscriptions). */\n ota,\n /** Gallery sync: status/onStatus · onNotice (host renders) · sync/cancel. */\n gallery,\n /** Miniapp lifecycle (the WebView bridge primitives are exported separately). */\n miniapps,\n /** First-time glasses discovery + pairing. */\n pairing,\n /** Phone→glasses notification forwarding (Android). */\n phoneNotifications,\n /** OS permissions (check/request/openSettings + per-miniapp requirements). */\n permissions,\n /** Inbound alerts engine→host (crashloop, version-incompatible, connection loss). */\n notifications,\n /** Maps providers — user-supplied Mapbox access token (Settings > Keys). */\n maps,\n /** Shared user-scoped memory timeline (Settings > Memory + session.memory). */\n memory,\n /** Device-local voice profiles for on-device speaker ID (Settings > Speakers). */\n speakers,\n /** AI/voice provider key validation — user-supplied keys (Settings > API Keys). */\n providerKeys,\n display,\n}\n"]}
@@ -6,12 +6,19 @@
6
6
  */
7
7
  import BluetoothSdk from "@glassly/bluetooth-sdk";
8
8
  export declare const display: {
9
- /** Mirrored-view read-model (current/onMirror/view/setView). */
9
+ /** Mirrored-view read-model (current/onMirror). */
10
10
  mirror: {
11
11
  current(): import("./displayMirror").DisplayMirrorEvent;
12
12
  onMirror(cb: (event: import("./displayMirror").DisplayMirrorEvent) => void): () => void;
13
- view(): string;
14
- setView(view: "main" | "dashboard"): void;
13
+ };
14
+ /** Phone-composed dashboard controls (simulated head-up, dev tools). */
15
+ dashboard: {
16
+ isOpen: () => boolean;
17
+ /** Open/close as if the head was raised/lowered (respects contextual_dashboard). */
18
+ setHeadUp: (up: boolean) => void;
19
+ /** Force open/close, ignoring contextual_dashboard (simulator button). */
20
+ open: () => void;
21
+ close: () => void;
15
22
  };
16
23
  /** Write raw text to the glasses display at (x, y) with a font size. */
17
24
  text: (text: string, x?: number | undefined, y?: number | undefined, size?: number | undefined) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../../src/facades/display.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,YAAY,MAAM,wBAAwB,CAAA;AAGjD,eAAO,MAAM,OAAO;IAClB,gEAAgE;;;;;;;IAEhE,wEAAwE;;IAExE,iCAAiC;iBACtB,OAAO,CAAC,IAAI,CAAC;CACzB,CAAA"}
1
+ {"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../../src/facades/display.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,YAAY,MAAM,wBAAwB,CAAA;AAIjD,eAAO,MAAM,OAAO;IAClB,mDAAmD;;;;;IAEnD,wEAAwE;;sBAE1D,OAAO;QACnB,oFAAoF;wBACpE,OAAO,KAAG,IAAI;QAC9B,0EAA0E;oBAChE,IAAI;qBACH,IAAI;;IAEjB,wEAAwE;;IAExE,iCAAiC;iBACtB,OAAO,CAAC,IAAI,CAAC;CACzB,CAAA"}
@@ -5,10 +5,20 @@
5
5
  * the engine DisplayProcessor / layout system, not these).
6
6
  */
7
7
  import BluetoothSdk from "@glassly/bluetooth-sdk";
8
+ import dashboardCoordinator from "../services/DashboardCoordinator";
8
9
  import { displayMirror } from "./displayMirror";
9
10
  export const display = {
10
- /** Mirrored-view read-model (current/onMirror/view/setView). */
11
+ /** Mirrored-view read-model (current/onMirror). */
11
12
  mirror: displayMirror,
13
+ /** Phone-composed dashboard controls (simulated head-up, dev tools). */
14
+ dashboard: {
15
+ isOpen: () => dashboardCoordinator.isOpen,
16
+ /** Open/close as if the head was raised/lowered (respects contextual_dashboard). */
17
+ setHeadUp: (up) => dashboardCoordinator.setHeadUp(up),
18
+ /** Force open/close, ignoring contextual_dashboard (simulator button). */
19
+ open: () => dashboardCoordinator.open(),
20
+ close: () => dashboardCoordinator.close(),
21
+ },
12
22
  /** Write raw text to the glasses display at (x, y) with a font size. */
13
23
  text: (...args) => BluetoothSdk.displayText(...args),
14
24
  /** Clear the glasses display. */
@@ -1 +1 @@
1
- {"version":3,"file":"display.js","sourceRoot":"","sources":["../../src/facades/display.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,YAAY,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAE7C,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,gEAAgE;IAChE,MAAM,EAAE,aAAa;IACrB,wEAAwE;IACxE,IAAI,EAAE,CAAC,GAAG,IAAiD,EAAE,EAAE,CAAC,YAAY,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC;IACjG,iCAAiC;IACjC,KAAK,EAAE,GAAkB,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE;CACxD,CAAA","sourcesContent":["/**\n * display facade — `engine.display`: the host/OEM display surface. `mirror`\n * exposes the mirrored-view read-model; `text`/`clear` are raw passthroughs to the\n * glasses display for dev tools and escape-hatch use (normal rendering goes through\n * the engine DisplayProcessor / layout system, not these).\n */\nimport BluetoothSdk from \"@glassly/bluetooth-sdk\"\nimport {displayMirror} from \"./displayMirror\"\n\nexport const display = {\n /** Mirrored-view read-model (current/onMirror/view/setView). */\n mirror: displayMirror,\n /** Write raw text to the glasses display at (x, y) with a font size. */\n text: (...args: Parameters<typeof BluetoothSdk.displayText>) => BluetoothSdk.displayText(...args),\n /** Clear the glasses display. */\n clear: (): Promise<void> => BluetoothSdk.clearDisplay(),\n}\n"]}
1
+ {"version":3,"file":"display.js","sourceRoot":"","sources":["../../src/facades/display.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,YAAY,MAAM,wBAAwB,CAAA;AACjD,OAAO,oBAAoB,MAAM,kCAAkC,CAAA;AACnE,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAE7C,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,mDAAmD;IACnD,MAAM,EAAE,aAAa;IACrB,wEAAwE;IACxE,SAAS,EAAE;QACT,MAAM,EAAE,GAAY,EAAE,CAAC,oBAAoB,CAAC,MAAM;QAClD,oFAAoF;QACpF,SAAS,EAAE,CAAC,EAAW,EAAQ,EAAE,CAAC,oBAAoB,CAAC,SAAS,CAAC,EAAE,CAAC;QACpE,0EAA0E;QAC1E,IAAI,EAAE,GAAS,EAAE,CAAC,oBAAoB,CAAC,IAAI,EAAE;QAC7C,KAAK,EAAE,GAAS,EAAE,CAAC,oBAAoB,CAAC,KAAK,EAAE;KAChD;IACD,wEAAwE;IACxE,IAAI,EAAE,CAAC,GAAG,IAAiD,EAAE,EAAE,CAAC,YAAY,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC;IACjG,iCAAiC;IACjC,KAAK,EAAE,GAAkB,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE;CACxD,CAAA","sourcesContent":["/**\n * display facade — `engine.display`: the host/OEM display surface. `mirror`\n * exposes the mirrored-view read-model; `text`/`clear` are raw passthroughs to the\n * glasses display for dev tools and escape-hatch use (normal rendering goes through\n * the engine DisplayProcessor / layout system, not these).\n */\nimport BluetoothSdk from \"@glassly/bluetooth-sdk\"\nimport dashboardCoordinator from \"../services/DashboardCoordinator\"\nimport {displayMirror} from \"./displayMirror\"\n\nexport const display = {\n /** Mirrored-view read-model (current/onMirror). */\n mirror: displayMirror,\n /** Phone-composed dashboard controls (simulated head-up, dev tools). */\n dashboard: {\n isOpen: (): boolean => dashboardCoordinator.isOpen,\n /** Open/close as if the head was raised/lowered (respects contextual_dashboard). */\n setHeadUp: (up: boolean): void => dashboardCoordinator.setHeadUp(up),\n /** Force open/close, ignoring contextual_dashboard (simulator button). */\n open: (): void => dashboardCoordinator.open(),\n close: (): void => dashboardCoordinator.close(),\n },\n /** Write raw text to the glasses display at (x, y) with a font size. */\n text: (...args: Parameters<typeof BluetoothSdk.displayText>) => BluetoothSdk.displayText(...args),\n /** Clear the glasses display. */\n clear: (): Promise<void> => BluetoothSdk.clearDisplay(),\n}\n"]}
@@ -3,13 +3,9 @@ export type DisplayMirrorEvent = Record<string, unknown> & {
3
3
  layout?: any;
4
4
  };
5
5
  export declare const displayMirror: {
6
- /** The display event currently shown on the previewed view (snapshot). */
6
+ /** The display event currently on the glasses (snapshot). */
7
7
  current(): DisplayMirrorEvent;
8
- /** Subscribe to changes of the previewed display event; returns an unsubscribe. */
8
+ /** Subscribe to changes of the mirrored display event; returns an unsubscribe. */
9
9
  onMirror(cb: (event: DisplayMirrorEvent) => void): () => void;
10
- /** Which view is previewed — the main screen or the dashboard. */
11
- view(): string;
12
- /** Switch the previewed view between `"main"` and `"dashboard"`. */
13
- setView(view: "main" | "dashboard"): void;
14
10
  };
15
11
  //# sourceMappingURL=displayMirror.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"displayMirror.d.ts","sourceRoot":"","sources":["../../src/facades/displayMirror.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,CAAA;CAAC,CAAA;AAExF,eAAO,MAAM,aAAa;IACxB,0EAA0E;eAC/D,kBAAkB;IAK7B,mFAAmF;iBACtE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,GAAG,MAAM,IAAI;IAI7D,kEAAkE;YAC1D,MAAM;IAGd,oEAAoE;kBACtD,MAAM,GAAG,WAAW,GAAG,IAAI;CAG1C,CAAA"}
1
+ {"version":3,"file":"displayMirror.d.ts","sourceRoot":"","sources":["../../src/facades/displayMirror.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,CAAA;CAAC,CAAA;AAExF,eAAO,MAAM,aAAa;IACxB,6DAA6D;eAClD,kBAAkB;IAK7B,kFAAkF;iBACrE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,GAAG,MAAM,IAAI;CAG9D,CAAA"}
@@ -1,29 +1,22 @@
1
1
  /**
2
2
  * glasses display mirror — the typed read facade over the (now engine-owned)
3
3
  * display store, for a phone-side preview of the glasses screen
4
- * (`engine.display.mirror`). The store is fed by the display paths
5
- * (LocalDisplayManager + the cloud path) via `setDisplayEvent`; this facade is the
6
- * read side: `current()` (snapshot) + `onMirror(cb)` (subscribe). The raw store is
7
- * also exposed as `engine.displayStore` (the Glassly-app escape hatch).
4
+ * (`engine.display.mirror`). The store holds the single frame currently on the
5
+ * glasses (dashboard switching happens upstream in DashboardCoordinator); this
6
+ * facade is the read side: `current()` (snapshot) + `onMirror(cb)` (subscribe).
7
+ * The raw store is also exposed as `engine.displayStore` (the Glassly-app
8
+ * escape hatch).
8
9
  */
9
10
  import { useDisplayStore } from "../stores/display";
10
11
  export const displayMirror = {
11
- /** The display event currently shown on the previewed view (snapshot). */
12
+ /** The display event currently on the glasses (snapshot). */
12
13
  current() {
13
14
  // Copy: the snapshot must not hand callers a mutable reference into the store.
14
15
  return { ...useDisplayStore.getState().currentEvent };
15
16
  },
16
- /** Subscribe to changes of the previewed display event; returns an unsubscribe. */
17
+ /** Subscribe to changes of the mirrored display event; returns an unsubscribe. */
17
18
  onMirror(cb) {
18
19
  return useDisplayStore.subscribe((s) => s.currentEvent, cb);
19
20
  },
20
- /** Which view is previewed — the main screen or the dashboard. */
21
- view() {
22
- return useDisplayStore.getState().view;
23
- },
24
- /** Switch the previewed view between `"main"` and `"dashboard"`. */
25
- setView(view) {
26
- useDisplayStore.getState().setView(view);
27
- },
28
21
  };
29
22
  //# sourceMappingURL=displayMirror.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"displayMirror.js","sourceRoot":"","sources":["../../src/facades/displayMirror.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AAIjD,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,0EAA0E;IAC1E,OAAO;QACL,+EAA+E;QAC/E,OAAO,EAAC,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC,YAAY,EAAC,CAAA;IACrD,CAAC;IAED,mFAAmF;IACnF,QAAQ,CAAC,EAAuC;QAC9C,OAAO,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAAA;IAC7D,CAAC;IAED,kEAAkE;IAClE,IAAI;QACF,OAAO,eAAe,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAA;IACxC,CAAC;IACD,oEAAoE;IACpE,OAAO,CAAC,IAA0B;QAChC,eAAe,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C,CAAC;CACF,CAAA","sourcesContent":["/**\n * glasses display mirror — the typed read facade over the (now engine-owned)\n * display store, for a phone-side preview of the glasses screen\n * (`engine.display.mirror`). The store is fed by the display paths\n * (LocalDisplayManager + the cloud path) via `setDisplayEvent`; this facade is the\n * read side: `current()` (snapshot) + `onMirror(cb)` (subscribe). The raw store is\n * also exposed as `engine.displayStore` (the Glassly-app escape hatch).\n */\nimport {useDisplayStore} from \"../stores/display\"\n\nexport type DisplayMirrorEvent = Record<string, unknown> & {view?: string; layout?: any}\n\nexport const displayMirror = {\n /** The display event currently shown on the previewed view (snapshot). */\n current(): DisplayMirrorEvent {\n // Copy: the snapshot must not hand callers a mutable reference into the store.\n return {...useDisplayStore.getState().currentEvent}\n },\n\n /** Subscribe to changes of the previewed display event; returns an unsubscribe. */\n onMirror(cb: (event: DisplayMirrorEvent) => void): () => void {\n return useDisplayStore.subscribe((s) => s.currentEvent, cb)\n },\n\n /** Which view is previewed — the main screen or the dashboard. */\n view(): string {\n return useDisplayStore.getState().view\n },\n /** Switch the previewed view between `\"main\"` and `\"dashboard\"`. */\n setView(view: \"main\" | \"dashboard\"): void {\n useDisplayStore.getState().setView(view)\n },\n}\n"]}
1
+ {"version":3,"file":"displayMirror.js","sourceRoot":"","sources":["../../src/facades/displayMirror.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AAIjD,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,6DAA6D;IAC7D,OAAO;QACL,+EAA+E;QAC/E,OAAO,EAAC,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC,YAAY,EAAC,CAAA;IACrD,CAAC;IAED,kFAAkF;IAClF,QAAQ,CAAC,EAAuC;QAC9C,OAAO,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAAA;IAC7D,CAAC;CACF,CAAA","sourcesContent":["/**\n * glasses display mirror — the typed read facade over the (now engine-owned)\n * display store, for a phone-side preview of the glasses screen\n * (`engine.display.mirror`). The store holds the single frame currently on the\n * glasses (dashboard switching happens upstream in DashboardCoordinator); this\n * facade is the read side: `current()` (snapshot) + `onMirror(cb)` (subscribe).\n * The raw store is also exposed as `engine.displayStore` (the Glassly-app\n * escape hatch).\n */\nimport {useDisplayStore} from \"../stores/display\"\n\nexport type DisplayMirrorEvent = Record<string, unknown> & {view?: string; layout?: any}\n\nexport const displayMirror = {\n /** The display event currently on the glasses (snapshot). */\n current(): DisplayMirrorEvent {\n // Copy: the snapshot must not hand callers a mutable reference into the store.\n return {...useDisplayStore.getState().currentEvent}\n },\n\n /** Subscribe to changes of the mirrored display event; returns an unsubscribe. */\n onMirror(cb: (event: DisplayMirrorEvent) => void): () => void {\n return useDisplayStore.subscribe((s) => s.currentEvent, cb)\n },\n}\n"]}
@@ -14,6 +14,7 @@ declare function projectStatus(): {
14
14
  micEnabled: boolean;
15
15
  vadEnabled: boolean;
16
16
  btClassic: boolean;
17
+ ancsAuthorized: boolean;
17
18
  };
18
19
  declare function projectControllerStatus(): {
19
20
  connected: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"glasses.d.ts","sourceRoot":"","sources":["../../src/facades/glasses.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAC,gBAAgB,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAC,MAAM,wBAAwB,CAAA;AAYhG,iBAAS,aAAa;;;;;;;;;;;;;;;EAarB;AAED,iBAAS,uBAAuB;;;;;EAQ/B;AAED,iBAAS,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;EAuBnB;AAED,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAA;AACpE,MAAM,MAAM,wBAAwB,GAAG,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAA;AACjF,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAA;AAEhE,eAAO,MAAM,OAAO;IAElB;;;;OAIG;0BACuB,OAAO,CAAC,IAAI,CAAC;sBASvB,OAAO,CAAC,IAAI,CAAC;kBACjB,OAAO,CAAC,IAAI,CAAC;IACzB,iDAAiD;sBACzB,MAAM,YAAY,cAAc,KAAG,OAAO,CAAC,IAAI,CAAC;IASxE,4DAA4D;4BACtC,OAAO,CAAC,IAAI,CAAC;IACnC,qDAAqD;yBAChC,MAAM,GAAG,IAAI,KAAG,OAAO,CAAC,IAAI,CAAC;IAClD;;;;;;OAMG;qBACkB,OAAO,CAAC,OAAO,CAAC;IAcrC;;;iEAG6D;0BACzC,OAAO;IAC3B;;;;;;;;;;OAUG;4BACmB,OAAO,CAAC,OAAO,CAAC;kBAG1B,qBAAqB;mBAClB,CAAC,MAAM,EAAE,qBAAqB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;gBAY3D,mBAAmB;iBAChB,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;;IAW/D,iFAAiF;;wBAI7D,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;yBAI/C,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;;8BAOzC,OAAO,CAAC,IAAI,CAAC;0BACjB,OAAO,CAAC,IAAI,CAAC;sBACjB,OAAO,CAAC,IAAI,CAAC;sBACb,wBAAwB;uBACrB,CAAC,MAAM,EAAE,wBAAwB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;;;QAcxE,8CAA8C;;QAE9C,qCAAqC;gCACb,MAAM;QAC9B,6EAA6E;oCACjD,OAAO,KAAG,OAAO,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;CAMtD,CAAA"}
1
+ {"version":3,"file":"glasses.d.ts","sourceRoot":"","sources":["../../src/facades/glasses.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAC,gBAAgB,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAC,MAAM,wBAAwB,CAAA;AAYhG,iBAAS,aAAa;;;;;;;;;;;;;;;;EAerB;AAED,iBAAS,uBAAuB;;;;;EAQ/B;AAED,iBAAS,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;EAuBnB;AAED,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAA;AACpE,MAAM,MAAM,wBAAwB,GAAG,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAA;AACjF,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAA;AAEhE,eAAO,MAAM,OAAO;IAElB;;;;OAIG;0BACuB,OAAO,CAAC,IAAI,CAAC;sBASvB,OAAO,CAAC,IAAI,CAAC;kBACjB,OAAO,CAAC,IAAI,CAAC;IACzB,iDAAiD;sBACzB,MAAM,YAAY,cAAc,KAAG,OAAO,CAAC,IAAI,CAAC;IASxE,4DAA4D;4BACtC,OAAO,CAAC,IAAI,CAAC;IACnC,qDAAqD;yBAChC,MAAM,GAAG,IAAI,KAAG,OAAO,CAAC,IAAI,CAAC;IAClD;;;;;;OAMG;qBACkB,OAAO,CAAC,OAAO,CAAC;IAcrC;;;iEAG6D;0BACzC,OAAO;IAC3B;;;;;;;;;;OAUG;4BACmB,OAAO,CAAC,OAAO,CAAC;kBAG1B,qBAAqB;mBAClB,CAAC,MAAM,EAAE,qBAAqB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;gBAY3D,mBAAmB;iBAChB,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;;IAW/D,iFAAiF;;wBAI7D,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;yBAI/C,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;;8BAOzC,OAAO,CAAC,IAAI,CAAC;0BACjB,OAAO,CAAC,IAAI,CAAC;sBACjB,OAAO,CAAC,IAAI,CAAC;sBACb,wBAAwB;uBACrB,CAAC,MAAM,EAAE,wBAAwB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;;;QAcxE,8CAA8C;;QAE9C,qCAAqC;gCACb,MAAM;QAC9B,6EAA6E;oCACjD,OAAO,KAAG,OAAO,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;CAMtD,CAAA"}
@@ -33,6 +33,8 @@ function projectStatus() {
33
33
  micEnabled: s.micEnabled,
34
34
  vadEnabled: s.voiceActivityDetectionEnabled,
35
35
  btClassic: s.bluetoothClassicConnected,
36
+ // iOS: glasses may read the phone's ANCS stream ("Share System Notifications").
37
+ ancsAuthorized: Boolean(s.ancsAuthorized),
36
38
  };
37
39
  }
38
40
  function projectControllerStatus() {
@@ -1 +1 @@
1
- {"version":3,"file":"glasses.js","sourceRoot":"","sources":["../../src/facades/glasses.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * glasses facade — the core `engine.glasses.*` surface: connection actions (over\n * the bluetooth-sdk passthrough), a curated status/info read-model (projected from\n * the engine-owned glasses store), capabilities (from the model table), and the\n * discrete input events. The `wifi` sub-facade is nested here.\n *\n * Read-models project the raw glasses store into the stable shape OEM UIs consume,\n * so the device store's field layout doesn't leak into every screen.\n */\n// Internal btsdk surface — connectSimulated/reconnect/controller live on the full\n// surface, not the public entry. Relative path (the alias doesn't resolve in engine's\n// standalone build); jest moduleNameMapper + tsconfig both resolve it.\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport type {ButtonPressEvent, ConnectOptions, Device, TouchEvent} from \"@glassly/bluetooth-sdk\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore, SETTINGS} from \"../stores/settings\"\nimport {hasDefaultDevice} from \"../services/DeviceStoreHydration\"\nimport {projectPairingIdentity} from \"../services/PairingIdentity\"\nimport {isGlassesConnected, isGlassesReady} from \"../services/GlassesReadiness\"\nimport {pushAllBluetoothSettings} from \"../services/GlassesSettingsSync\"\nimport {recordSupportProfileConnectionFailure} from \"../services/SupportProfileSync\"\nimport {getModelCapabilities, type DeviceTypes} from \"../types\"\nimport {glassesWifi} from \"./glassesWifi\"\nimport {glassesSettings} from \"./glassesSettings\"\n\nfunction projectStatus() {\n const s = useGlassesStore.getState()\n return {\n state: s.connection.state,\n fullyBooted: isGlassesReady(s.connection),\n battery: s.batteryLevel,\n charging: s.charging,\n case: {battery: s.caseBatteryLevel, charging: s.caseCharging, open: s.caseOpen, removed: s.caseRemoved},\n signal: s.signalStrength,\n micEnabled: s.micEnabled,\n vadEnabled: s.voiceActivityDetectionEnabled,\n btClassic: s.bluetoothClassicConnected,\n }\n}\n\nfunction projectControllerStatus() {\n const s = useGlassesStore.getState()\n return {\n connected: s.controllerConnected,\n fullyBooted: s.controllerFullyBooted,\n battery: s.controllerBatteryLevel,\n signal: s.controllerSignalStrength,\n }\n}\n\nfunction projectInfo() {\n const s = useGlassesStore.getState()\n return {\n model: s.deviceModel,\n style: s.style,\n color: s.color,\n bluetoothName: s.bluetoothName,\n firmwareVersion: s.firmwareVersion,\n mtkFirmware: s.mtkFirmwareVersion,\n besFirmware: s.besFirmwareVersion,\n appVersion: s.appVersion,\n androidVersion: s.androidVersion,\n serialNumber: s.serialNumber,\n buildNumber: s.buildNumber,\n btMac: s.bluetoothMacAddress,\n wifiMac: s.wifiMacAddress,\n leftMac: s.leftMacAddress,\n rightMac: s.rightMacAddress,\n sceneCanvasWidth: s.sceneCanvasWidth,\n sceneCanvasHeight: s.sceneCanvasHeight,\n // Copy: the snapshot must not hand callers a mutable reference into the store.\n wifi: {...s.wifi},\n }\n}\n\nexport type GlassesStatusSnapshot = ReturnType<typeof projectStatus>\nexport type ControllerStatusSnapshot = ReturnType<typeof projectControllerStatus>\nexport type GlassesInfoSnapshot = ReturnType<typeof projectInfo>\n\nexport const glasses = {\n // --- connection (bluetooth-sdk passthrough) ---\n /**\n * Reconnect the default wearable. Seeds the phone's current device settings to\n * native first, so they're primed before the connect handshake replays them to\n * the glasses (this used to be a host-side step before `connectDefault`).\n */\n connectDefault: async (): Promise<void> => {\n try {\n await pushAllBluetoothSettings()\n await BluetoothSdk.connectDefault()\n } catch (error) {\n recordSupportProfileConnectionFailure(error, \"connect_default\")\n throw error\n }\n },\n disconnect: (): Promise<void> => BluetoothSdk.disconnect(),\n forget: (): Promise<void> => BluetoothSdk.forget(),\n /** Connect to a specific (discovered) device. */\n connect: async (device: Device, options?: ConnectOptions): Promise<void> => {\n try {\n await pushAllBluetoothSettings()\n await BluetoothSdk.connect(device, options)\n } catch (error) {\n recordSupportProfileConnectionFailure(error, \"connect\")\n throw error\n }\n },\n /** Connect the built-in simulated glasses (dev/testing). */\n connectSimulated: (): Promise<void> => BluetoothSdk.connectSimulated(),\n /** Set a device as the `connectDefault()` target. */\n setDefault: (device: Device | null): Promise<void> => BluetoothSdk.setDefaultDevice(device),\n /**\n * Reconnect to the saved default glasses. Resolves `false` when there's nothing to\n * reconnect to (no default paired) or the connect attempt fails (e.g. BLE powered\n * off), `true` when already connected or after kicking off the connect. Never\n * rejects — the boolean contract holds. The host gates connectivity/permissions\n * (its UI) before calling.\n */\n reconnect: async (): Promise<boolean> => {\n const defaultWearable = useSettingsStore.getState().getSetting(SETTINGS.default_wearable.key)\n if (!defaultWearable) return false\n if (isGlassesConnected(useGlassesStore.getState().connection)) return true\n try {\n await pushAllBluetoothSettings()\n await BluetoothSdk.connectDefault()\n } catch (error) {\n recordSupportProfileConnectionFailure(error, \"reconnect\")\n console.warn(\"engine.glasses.reconnect failed:\", error)\n return false\n }\n return true\n },\n /** True when no glasses has ever been paired NOR selected (the pairing-identity\n * lifecycle is in its `none` state) — e.g. to route a first-run host into the\n * pairing/onboarding flow. A pending selection is not \"first\" — the host should\n * offer finish-pairing (see `engine.pairing.identity()`). */\n isFirstPairing: (): boolean => projectPairingIdentity().kind === \"none\",\n /**\n * True when the SDK has an actual default DEVICE to reconnect to. Distinct from\n * `default_wearable` (a model string that a pending or orphaned selection can\n * hold without any paired device): connectDefault() throws without a device, so\n * hosts should route to the pairing flow when this is false.\n *\n * Hydration-aware: awaits the device-store seed internally, so a cold-start\n * read can't see a false \"absent\" for genuinely-paired users. Rejects if\n * hydration failed — callers guarding destructive or routing decisions must\n * fail open (treat as \"has a device\").\n */\n hasDefaultDevice: (): Promise<boolean> => hasDefaultDevice(),\n\n // --- read-model (projected from the engine-owned glasses store) ---\n status: (): GlassesStatusSnapshot => projectStatus(),\n onStatus: (cb: (status: GlassesStatusSnapshot) => void): (() => void) => {\n // Dedupe: the glasses store updates on many fields; only fire when the\n // projected status snapshot actually changes.\n let last = JSON.stringify(projectStatus())\n return useGlassesStore.subscribe(() => {\n const snap = projectStatus()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n })\n },\n info: (): GlassesInfoSnapshot => projectInfo(),\n onInfo: (cb: (info: GlassesInfoSnapshot) => void): (() => void) => {\n let last = JSON.stringify(projectInfo())\n return useGlassesStore.subscribe(() => {\n const snap = projectInfo()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n })\n },\n capabilities: () => getModelCapabilities(useGlassesStore.getState().deviceModel as DeviceTypes),\n /** Ask the glasses to report fresh firmware/version info (updates the store). */\n requestVersionInfo: () => BluetoothSdk.requestVersionInfo(),\n\n // --- discrete input events (passthrough listeners) ---\n onButtonPress: (cb: (event: ButtonPressEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"button_press\", cb)\n return () => sub.remove()\n },\n onTouchGesture: (cb: (event: TouchEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"touch_event\", cb)\n return () => sub.remove()\n },\n\n // --- ring controller (optional input device) ---\n controller: {\n connectDefault: (): Promise<void> => BluetoothSdk.connectDefaultController(),\n disconnect: (): Promise<void> => BluetoothSdk.disconnectController(),\n forget: (): Promise<void> => BluetoothSdk.forgetController(),\n status: (): ControllerStatusSnapshot => projectControllerStatus(),\n onStatus: (cb: (status: ControllerStatusSnapshot) => void): (() => void) => {\n let last = JSON.stringify(projectControllerStatus())\n return useGlassesStore.subscribe(() => {\n const snap = projectControllerStatus()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n })\n },\n },\n\n // --- audio (glasses media-volume + own-app playback hint) ---\n audio: {\n /** Read the glasses' current media volume. */\n getMediaVolume: () => BluetoothSdk.getGlassesMediaVolume(),\n /** Set the glasses' media volume. */\n setMediaVolume: (level: number) => BluetoothSdk.setGlassesMediaVolume(level),\n /** Tell native whether THIS app is currently playing audio (for ducking). */\n setOwnAppPlaying: (playing: boolean): Promise<void> => BluetoothSdk.setOwnAppAudioPlaying(playing),\n },\n\n // --- sub-facades ---\n wifi: glassesWifi,\n settings: glassesSettings,\n}\n"]}
1
+ {"version":3,"file":"glasses.js","sourceRoot":"","sources":["../../src/facades/glasses.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * glasses facade — the core `engine.glasses.*` surface: connection actions (over\n * the bluetooth-sdk passthrough), a curated status/info read-model (projected from\n * the engine-owned glasses store), capabilities (from the model table), and the\n * discrete input events. The `wifi` sub-facade is nested here.\n *\n * Read-models project the raw glasses store into the stable shape OEM UIs consume,\n * so the device store's field layout doesn't leak into every screen.\n */\n// Internal btsdk surface — connectSimulated/reconnect/controller live on the full\n// surface, not the public entry. Relative path (the alias doesn't resolve in engine's\n// standalone build); jest moduleNameMapper + tsconfig both resolve it.\nimport BluetoothSdk from \"@glassly/bluetooth-sdk/internal\"\nimport type {ButtonPressEvent, ConnectOptions, Device, TouchEvent} from \"@glassly/bluetooth-sdk\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore, SETTINGS} from \"../stores/settings\"\nimport {hasDefaultDevice} from \"../services/DeviceStoreHydration\"\nimport {projectPairingIdentity} from \"../services/PairingIdentity\"\nimport {isGlassesConnected, isGlassesReady} from \"../services/GlassesReadiness\"\nimport {pushAllBluetoothSettings} from \"../services/GlassesSettingsSync\"\nimport {recordSupportProfileConnectionFailure} from \"../services/SupportProfileSync\"\nimport {getModelCapabilities, type DeviceTypes} from \"../types\"\nimport {glassesWifi} from \"./glassesWifi\"\nimport {glassesSettings} from \"./glassesSettings\"\n\nfunction projectStatus() {\n const s = useGlassesStore.getState()\n return {\n state: s.connection.state,\n fullyBooted: isGlassesReady(s.connection),\n battery: s.batteryLevel,\n charging: s.charging,\n case: {battery: s.caseBatteryLevel, charging: s.caseCharging, open: s.caseOpen, removed: s.caseRemoved},\n signal: s.signalStrength,\n micEnabled: s.micEnabled,\n vadEnabled: s.voiceActivityDetectionEnabled,\n btClassic: s.bluetoothClassicConnected,\n // iOS: glasses may read the phone's ANCS stream (\"Share System Notifications\").\n ancsAuthorized: Boolean(s.ancsAuthorized),\n }\n}\n\nfunction projectControllerStatus() {\n const s = useGlassesStore.getState()\n return {\n connected: s.controllerConnected,\n fullyBooted: s.controllerFullyBooted,\n battery: s.controllerBatteryLevel,\n signal: s.controllerSignalStrength,\n }\n}\n\nfunction projectInfo() {\n const s = useGlassesStore.getState()\n return {\n model: s.deviceModel,\n style: s.style,\n color: s.color,\n bluetoothName: s.bluetoothName,\n firmwareVersion: s.firmwareVersion,\n mtkFirmware: s.mtkFirmwareVersion,\n besFirmware: s.besFirmwareVersion,\n appVersion: s.appVersion,\n androidVersion: s.androidVersion,\n serialNumber: s.serialNumber,\n buildNumber: s.buildNumber,\n btMac: s.bluetoothMacAddress,\n wifiMac: s.wifiMacAddress,\n leftMac: s.leftMacAddress,\n rightMac: s.rightMacAddress,\n sceneCanvasWidth: s.sceneCanvasWidth,\n sceneCanvasHeight: s.sceneCanvasHeight,\n // Copy: the snapshot must not hand callers a mutable reference into the store.\n wifi: {...s.wifi},\n }\n}\n\nexport type GlassesStatusSnapshot = ReturnType<typeof projectStatus>\nexport type ControllerStatusSnapshot = ReturnType<typeof projectControllerStatus>\nexport type GlassesInfoSnapshot = ReturnType<typeof projectInfo>\n\nexport const glasses = {\n // --- connection (bluetooth-sdk passthrough) ---\n /**\n * Reconnect the default wearable. Seeds the phone's current device settings to\n * native first, so they're primed before the connect handshake replays them to\n * the glasses (this used to be a host-side step before `connectDefault`).\n */\n connectDefault: async (): Promise<void> => {\n try {\n await pushAllBluetoothSettings()\n await BluetoothSdk.connectDefault()\n } catch (error) {\n recordSupportProfileConnectionFailure(error, \"connect_default\")\n throw error\n }\n },\n disconnect: (): Promise<void> => BluetoothSdk.disconnect(),\n forget: (): Promise<void> => BluetoothSdk.forget(),\n /** Connect to a specific (discovered) device. */\n connect: async (device: Device, options?: ConnectOptions): Promise<void> => {\n try {\n await pushAllBluetoothSettings()\n await BluetoothSdk.connect(device, options)\n } catch (error) {\n recordSupportProfileConnectionFailure(error, \"connect\")\n throw error\n }\n },\n /** Connect the built-in simulated glasses (dev/testing). */\n connectSimulated: (): Promise<void> => BluetoothSdk.connectSimulated(),\n /** Set a device as the `connectDefault()` target. */\n setDefault: (device: Device | null): Promise<void> => BluetoothSdk.setDefaultDevice(device),\n /**\n * Reconnect to the saved default glasses. Resolves `false` when there's nothing to\n * reconnect to (no default paired) or the connect attempt fails (e.g. BLE powered\n * off), `true` when already connected or after kicking off the connect. Never\n * rejects — the boolean contract holds. The host gates connectivity/permissions\n * (its UI) before calling.\n */\n reconnect: async (): Promise<boolean> => {\n const defaultWearable = useSettingsStore.getState().getSetting(SETTINGS.default_wearable.key)\n if (!defaultWearable) return false\n if (isGlassesConnected(useGlassesStore.getState().connection)) return true\n try {\n await pushAllBluetoothSettings()\n await BluetoothSdk.connectDefault()\n } catch (error) {\n recordSupportProfileConnectionFailure(error, \"reconnect\")\n console.warn(\"engine.glasses.reconnect failed:\", error)\n return false\n }\n return true\n },\n /** True when no glasses has ever been paired NOR selected (the pairing-identity\n * lifecycle is in its `none` state) — e.g. to route a first-run host into the\n * pairing/onboarding flow. A pending selection is not \"first\" — the host should\n * offer finish-pairing (see `engine.pairing.identity()`). */\n isFirstPairing: (): boolean => projectPairingIdentity().kind === \"none\",\n /**\n * True when the SDK has an actual default DEVICE to reconnect to. Distinct from\n * `default_wearable` (a model string that a pending or orphaned selection can\n * hold without any paired device): connectDefault() throws without a device, so\n * hosts should route to the pairing flow when this is false.\n *\n * Hydration-aware: awaits the device-store seed internally, so a cold-start\n * read can't see a false \"absent\" for genuinely-paired users. Rejects if\n * hydration failed — callers guarding destructive or routing decisions must\n * fail open (treat as \"has a device\").\n */\n hasDefaultDevice: (): Promise<boolean> => hasDefaultDevice(),\n\n // --- read-model (projected from the engine-owned glasses store) ---\n status: (): GlassesStatusSnapshot => projectStatus(),\n onStatus: (cb: (status: GlassesStatusSnapshot) => void): (() => void) => {\n // Dedupe: the glasses store updates on many fields; only fire when the\n // projected status snapshot actually changes.\n let last = JSON.stringify(projectStatus())\n return useGlassesStore.subscribe(() => {\n const snap = projectStatus()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n })\n },\n info: (): GlassesInfoSnapshot => projectInfo(),\n onInfo: (cb: (info: GlassesInfoSnapshot) => void): (() => void) => {\n let last = JSON.stringify(projectInfo())\n return useGlassesStore.subscribe(() => {\n const snap = projectInfo()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n })\n },\n capabilities: () => getModelCapabilities(useGlassesStore.getState().deviceModel as DeviceTypes),\n /** Ask the glasses to report fresh firmware/version info (updates the store). */\n requestVersionInfo: () => BluetoothSdk.requestVersionInfo(),\n\n // --- discrete input events (passthrough listeners) ---\n onButtonPress: (cb: (event: ButtonPressEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"button_press\", cb)\n return () => sub.remove()\n },\n onTouchGesture: (cb: (event: TouchEvent) => void): (() => void) => {\n const sub = BluetoothSdk.addListener(\"touch_event\", cb)\n return () => sub.remove()\n },\n\n // --- ring controller (optional input device) ---\n controller: {\n connectDefault: (): Promise<void> => BluetoothSdk.connectDefaultController(),\n disconnect: (): Promise<void> => BluetoothSdk.disconnectController(),\n forget: (): Promise<void> => BluetoothSdk.forgetController(),\n status: (): ControllerStatusSnapshot => projectControllerStatus(),\n onStatus: (cb: (status: ControllerStatusSnapshot) => void): (() => void) => {\n let last = JSON.stringify(projectControllerStatus())\n return useGlassesStore.subscribe(() => {\n const snap = projectControllerStatus()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n })\n },\n },\n\n // --- audio (glasses media-volume + own-app playback hint) ---\n audio: {\n /** Read the glasses' current media volume. */\n getMediaVolume: () => BluetoothSdk.getGlassesMediaVolume(),\n /** Set the glasses' media volume. */\n setMediaVolume: (level: number) => BluetoothSdk.setGlassesMediaVolume(level),\n /** Tell native whether THIS app is currently playing audio (for ducking). */\n setOwnAppPlaying: (playing: boolean): Promise<void> => BluetoothSdk.setOwnAppAudioPlaying(playing),\n },\n\n // --- sub-facades ---\n wifi: glassesWifi,\n settings: glassesSettings,\n}\n"]}
@@ -3,10 +3,12 @@ import { type OtaInstallSnapshot } from "../services/OtaInstallCoordinator";
3
3
  import { type OtaCheckCurrentGlassesOptions, type OtaCheckCurrentGlassesResult } from "../services/OtaUpdateCheckService";
4
4
  declare function projectSnapshot(): {
5
5
  connected: boolean;
6
+ ready: boolean;
6
7
  buildNumber: string | null;
7
8
  appVersion: string | null;
8
9
  mtkFirmwareVersion: string | null;
9
10
  besFirmwareVersion: string | null;
11
+ batteryLevel: number | null;
10
12
  hotspotOtaVersion: number;
11
13
  wifiConnected: boolean;
12
14
  wifiStatusKnown: boolean;
@@ -22,7 +24,7 @@ export type { OtaProgress, OtaProgressStatus, OtaStatus, OtaUpdateInfo, ReleaseC
22
24
  export declare const ota: {
23
25
  /**
24
26
  * Start the device-status and OTA projections without starting the authenticated
25
- * cloud/miniapp runtime. This is the explicit entry point for Bluetooth-only hosts.
27
+ * cloud connection or miniapp runtime. This is the explicit entry point for Bluetooth-only hosts.
26
28
  * Idempotent and safe when the full engine runtime is already running.
27
29
  */
28
30
  initialize: () => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"ota.d.ts","sourceRoot":"","sources":["../../src/facades/ota.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,gBAAgB,EACjB,MAAM,wBAAwB,CAAA;AAG/B,OAAO,EAAwB,KAAK,kBAAkB,EAAC,MAAM,mCAAmC,CAAA;AAGhG,OAAO,EAEL,KAAK,6BAA6B,EAClC,KAAK,4BAA4B,EAClC,MAAM,mCAAmC,CAAA;AAE1C,iBAAS,eAAe;;;;;;;;;;;;;;;EAkBvB;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAA;AAC5D,YAAY,EACV,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,6BAA6B,EAC7B,4BAA4B,GAC7B,CAAA;AAED,eAAO,MAAM,GAAG;IAEd;;;;OAIG;;IAMH;;;;;OAKG;8BACuB,MAAM,GAAG,IAAI,KAAG,OAAO,CAAC,gBAAgB,CAAC;IAQnE;;;OAGG;;IAEH,sGAAsG;gCAC1E,6BAA6B;IACzD,wGAAwG;yCACnE,MAAM,GAAG,IAAI,cAAc,MAAM,GAAG,IAAI,KAAG,gBAAgB,EAAE;IAElG,+CAA+C;;IAE/C,yEAAyE;;IAMzE;;;;;OAKG;4CACqC,aAAa,CAAC,SAAS,CAAC,KAAG,aAAa,GAAG,IAAI;IAQvF;;;;;OAKG;;IAIH;;;OAGG;yCACkC,OAAO;IAE5C;;;;OAIG;;QAED,oFAAoF;0BAClE,4BAA4B;QAC9C,2FAA2F;;QAE3F,+EAA+E;;QAE/E,+EAA+E;;QAE/E,4EAA4E;;QAE5E,8FAA8F;;QAE9F,qFAAqF;wBACvE,kBAAkB;QAChC,iGAAiG;yBAChF,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;;IAGxE,8EAA8E;2BACzD,aAAa,GAAG,IAAI;IACzC,iFAAiF;kBACrE,SAAS,GAAG,IAAI;IAC5B,iEAAiE;oBACnD,WAAW;IAEzB,iGAAiG;4BACzE,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;IAU3E,uEAAuE;mBACxD,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;IAUhE,iEAAiE;qBAChD,CAAC,QAAQ,EAAE,WAAW,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;CAUhE,CAAA"}
1
+ {"version":3,"file":"ota.d.ts","sourceRoot":"","sources":["../../src/facades/ota.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,gBAAgB,EACjB,MAAM,wBAAwB,CAAA;AAG/B,OAAO,EAAwB,KAAK,kBAAkB,EAAC,MAAM,mCAAmC,CAAA;AAGhG,OAAO,EAEL,KAAK,6BAA6B,EAClC,KAAK,4BAA4B,EAClC,MAAM,mCAAmC,CAAA;AAE1C,iBAAS,eAAe;;;;;;;;;;;;;;;;;EAoBvB;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAA;AAC5D,YAAY,EACV,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,6BAA6B,EAC7B,4BAA4B,GAC7B,CAAA;AAED,eAAO,MAAM,GAAG;IAEd;;;;OAIG;;IAMH;;;;;OAKG;8BACuB,MAAM,GAAG,IAAI,KAAG,OAAO,CAAC,gBAAgB,CAAC;IAQnE;;;OAGG;;IAEH,sGAAsG;gCAC1E,6BAA6B;IACzD,wGAAwG;yCACnE,MAAM,GAAG,IAAI,cAAc,MAAM,GAAG,IAAI,KAAG,gBAAgB,EAAE;IAElG,+CAA+C;;IAE/C,yEAAyE;;IAMzE;;;;;OAKG;4CACqC,aAAa,CAAC,SAAS,CAAC,KAAG,aAAa,GAAG,IAAI;IAQvF;;;;;OAKG;;IAIH;;;OAGG;yCACkC,OAAO;IAE5C;;;;OAIG;;QAED,oFAAoF;0BAClE,4BAA4B;QAC9C,2FAA2F;;QAE3F,+EAA+E;;QAE/E,+EAA+E;;QAE/E,4EAA4E;;QAE5E,8FAA8F;;QAE9F,qFAAqF;wBACvE,kBAAkB;QAChC,iGAAiG;yBAChF,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;;IAGxE,8EAA8E;2BACzD,aAAa,GAAG,IAAI;IACzC,iFAAiF;kBACrE,SAAS,GAAG,IAAI;IAC5B,iEAAiE;oBACnD,WAAW;IAEzB,iGAAiG;4BACzE,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;IAU3E,uEAAuE;mBACxD,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;IAUhE,iEAAiE;qBAChD,CAAC,QAAQ,EAAE,WAAW,KAAK,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC;CAUhE,CAAA"}
@@ -9,7 +9,7 @@
9
9
  * progress screen is a pure renderer over its snapshot + attach/detach/retry/finish.
10
10
  */
11
11
  import BluetoothSdk from "@glassly/bluetooth-sdk";
12
- import { isGlassesConnected, useGlassesStore } from "../stores/glasses";
12
+ import { isGlassesConnected, isGlassesReady, useGlassesStore } from "../stores/glasses";
13
13
  import { resolveOtaManifestUrl } from "../services/otaManifestUrl";
14
14
  import { otaInstallCoordinator } from "../services/OtaInstallCoordinator";
15
15
  import { startGlassesStatusProjection } from "../services/GlassesStatusProjection";
@@ -19,10 +19,12 @@ function projectSnapshot() {
19
19
  const s = useGlassesStore.getState();
20
20
  return {
21
21
  connected: isGlassesConnected(s.connection),
22
+ ready: isGlassesReady(s.connection),
22
23
  buildNumber: s.buildNumber || null,
23
24
  appVersion: s.appVersion || null,
24
25
  mtkFirmwareVersion: s.mtkFirmwareVersion || null,
25
26
  besFirmwareVersion: s.besFirmwareVersion || null,
27
+ batteryLevel: s.batteryLevel >= 0 ? s.batteryLevel : null,
26
28
  hotspotOtaVersion: s.hotspotOtaVersion ?? 0,
27
29
  wifiConnected: s.wifi.state === "connected",
28
30
  wifiStatusKnown: s.wifiStatusKnown,
@@ -38,7 +40,7 @@ export const ota = {
38
40
  // --- actions ---
39
41
  /**
40
42
  * Start the device-status and OTA projections without starting the authenticated
41
- * cloud/miniapp runtime. This is the explicit entry point for Bluetooth-only hosts.
43
+ * cloud connection or miniapp runtime. This is the explicit entry point for Bluetooth-only hosts.
42
44
  * Idempotent and safe when the full engine runtime is already running.
43
45
  */
44
46
  initialize: async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"ota.js","sourceRoot":"","sources":["../../src/facades/ota.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ota facade — `engine.ota`: the OEM-facing OTA read/observe surface. Engine's\n * OtaService projects the glasses' OTA BLE events into the engine store; this facade\n * exposes the snapshot + change subscriptions behind the shared OTA flow exported by\n * `@glassly/engine/ota`. Hosts retain only their surrounding navigation and theme/i18n adapters.\n *\n * Availability checks live in engine. Install orchestration lives in engine too:\n * `installSession` fronts the OtaInstallCoordinator state machine (WP 8B) — the host\n * progress screen is a pure renderer over its snapshot + attach/detach/retry/finish.\n */\nimport BluetoothSdk from \"@glassly/bluetooth-sdk\"\nimport type {\n OtaProgress,\n OtaProgressStatus,\n OtaStartAckEvent,\n OtaStatus,\n OtaUpdateInfo,\n ReleaseChangelog,\n} from \"@glassly/bluetooth-sdk\"\nimport {isGlassesConnected, useGlassesStore} from \"../stores/glasses\"\nimport {resolveOtaManifestUrl} from \"../services/otaManifestUrl\"\nimport {otaInstallCoordinator, type OtaInstallSnapshot} from \"../services/OtaInstallCoordinator\"\nimport {startGlassesStatusProjection} from \"../services/GlassesStatusProjection\"\nimport {startOtaService} from \"../services/OtaService\"\nimport {\n checkCurrentGlassesForUpdate,\n type OtaCheckCurrentGlassesOptions,\n type OtaCheckCurrentGlassesResult,\n} from \"../services/OtaUpdateCheckService\"\n\nfunction projectSnapshot() {\n const s = useGlassesStore.getState()\n return {\n connected: isGlassesConnected(s.connection),\n buildNumber: s.buildNumber || null,\n appVersion: s.appVersion || null,\n mtkFirmwareVersion: s.mtkFirmwareVersion || null,\n besFirmwareVersion: s.besFirmwareVersion || null,\n hotspotOtaVersion: s.hotspotOtaVersion ?? 0,\n wifiConnected: s.wifi.state === \"connected\",\n wifiStatusKnown: s.wifiStatusKnown,\n manifestUrl: resolveOtaManifestUrl(s.otaVersionUrl, s.buildNumber),\n updateAvailable: s.otaUpdateAvailable,\n status: s.otaStatus,\n legacyProgress: s.otaProgress,\n inProgress: s.otaInProgress,\n mtkUpdatedThisSession: s.mtkUpdatedThisSession,\n }\n}\n\nexport type OtaSnapshot = ReturnType<typeof projectSnapshot>\nexport type {\n OtaProgress,\n OtaProgressStatus,\n OtaStatus,\n OtaUpdateInfo,\n ReleaseChangelog,\n OtaInstallSnapshot,\n OtaCheckCurrentGlassesOptions,\n OtaCheckCurrentGlassesResult,\n}\n\nexport const ota = {\n // --- actions ---\n /**\n * Start the device-status and OTA projections without starting the authenticated\n * cloud/miniapp runtime. This is the explicit entry point for Bluetooth-only hosts.\n * Idempotent and safe when the full engine runtime is already running.\n */\n initialize: async () => {\n const hydrated = startGlassesStatusProjection()\n startOtaService()\n await hydrated\n },\n /**\n * Start the firmware install with the resolved OTA manifest URL. Progress lands on\n * `status()`/`onStatus()`. (The host resolves the manifest URL — developer override,\n * host pin, or Engine release pin resolution stays with the OTA config; the stuck/retry watchdog is its own\n * resilience layer on top of this command.)\n */\n install: (otaVersionUrl?: string | null): Promise<OtaStartAckEvent> => BluetoothSdk.startOtaUpdate(otaVersionUrl),\n // Deferred: this facade entry was intended to become the engine-owned retry/check\n // action, but BluetoothSdk.checkForOtaUpdate() only returns a boolean. Exposing it\n // here would make callers think the rich otaUpdateAvailable read model is refreshed,\n // while the original view still depends on the host-side manifest compare to build\n // versionName/updates/totalSize. Keep that behavior unchanged until the whole rich\n // availability check moves into engine.\n // retry: () => BluetoothSdk.checkForOtaUpdate(),\n /**\n * Keep glasses awake while an OTA session is actively progressing.\n * @deprecated Legacy progress route (build < 37) only — removed with WP 8D.\n */\n ping: () => BluetoothSdk.ping(),\n /** Resolve and compare the current glasses against the OTA manifest, then update the OTA snapshot. */\n checkForUpdates: (options?: OtaCheckCurrentGlassesOptions) => checkCurrentGlassesForUpdate(options),\n /** Return bundled release changelogs crossed between two coordinated product versions, newest first. */\n getReleaseChangelogs: (fromVersion?: string | null, toVersion?: string | null): ReleaseChangelog[] =>\n BluetoothSdk.getReleaseChangelogs(fromVersion, toVersion),\n /** Clear the available-update prompt state. */\n clearUpdateAvailable: () => useGlassesStore.getState().setOtaUpdateAvailable(null),\n /** Clear active progress/status before entering a fresh install flow. */\n clearProgress: () => {\n const store = useGlassesStore.getState()\n store.setOtaProgress(null)\n store.setOtaStatus(null)\n },\n /**\n * Replace the pending update sequence after an in-flow firmware revalidation.\n * Kept as a named OTA policy operation so host screens do not mutate the raw\n * glasses store.\n * @deprecated Legacy progress route (build < 37) only — removed with WP 8D.\n */\n replacePendingUpdateSequence: (updates: OtaUpdateInfo[\"updates\"]): OtaUpdateInfo | null => {\n const store = useGlassesStore.getState()\n const current = store.otaUpdateAvailable\n if (!current) return null\n const next = {...current, updates}\n store.setOtaUpdateAvailable(next)\n return next\n },\n /**\n * Clear stale build metadata before returning to the update-check screen.\n * This forces the next version_info event through both the native observable\n * store and the engine projection after an APK reboot.\n * @deprecated Legacy progress route (build < 37) only — removed with WP 8D.\n */\n clearBuildNumberForNextCheck: () => {\n useGlassesStore.getState().setGlassesInfo({buildNumber: \"\"})\n },\n /**\n * Mark MTK as already applied during this app session until the glasses reboot/disconnect.\n * @deprecated Legacy progress route (build < 37) only — removed with WP 8D.\n */\n markMtkUpdatedThisSession: (updated: boolean) => useGlassesStore.getState().setMtkUpdatedThisSession(updated),\n\n /**\n * The OTA install state machine (unified `ota_status` sessions, build >= 37).\n * The host progress screen attaches on mount, detaches on unmount, and renders\n * the snapshot; every watchdog/retry/reconnect rule lives in the coordinator.\n */\n installSession: {\n /** Select Wi-Fi or capability-gated hotspot transport for this checked manifest. */\n prepare: (result: OtaCheckCurrentGlassesResult) => otaInstallCoordinator.prepare(result),\n /** Bind the machine to the mounted progress screen. Idempotent per attach/detach cycle. */\n attach: () => otaInstallCoordinator.attach(),\n /** Unbind on unmount: clears all timers/listeners and resets session state. */\n detach: () => otaInstallCoordinator.detach(),\n /** Retry after a failure: clear state and re-send ota_start (if connected). */\n retry: () => otaInstallCoordinator.retry(),\n /** Terminal cleanup for Continue/Done, including hotspot route teardown. */\n finish: () => otaInstallCoordinator.finish(),\n /** Abandon an interrupted session: await finish() then drop the session's status/progress. */\n discard: () => otaInstallCoordinator.discard(),\n /** Current install read model (displayState/errorMsg/lockout + glasses OTA data). */\n snapshot: (): OtaInstallSnapshot => otaInstallCoordinator.snapshot(),\n /** Subscribe to install snapshot changes (deduped on projected JSON). Returns an unsubscribe. */\n onSnapshot: (cb: (snapshot: OtaInstallSnapshot) => void): (() => void) => otaInstallCoordinator.onSnapshot(cb),\n },\n\n /** Current available-update info (versionName/updates/totalSize), or null. */\n updateAvailable: (): OtaUpdateInfo | null => useGlassesStore.getState().otaUpdateAvailable,\n /** Current OTA install status (stepType/phase/percent/status/error), or null. */\n status: (): OtaStatus | null => useGlassesStore.getState().otaStatus,\n /** Current OTA read model for update prompt/progress screens. */\n snapshot: (): OtaSnapshot => projectSnapshot(),\n\n /** Subscribe to OTA availability changes (info or null when cleared). Returns an unsubscribe. */\n onUpdateAvailable: (cb: (info: OtaUpdateInfo | null) => void): (() => void) => {\n let last = useGlassesStore.getState().otaUpdateAvailable\n return useGlassesStore.subscribe(() => {\n const info = useGlassesStore.getState().otaUpdateAvailable\n if (info === last) return\n last = info\n cb(info)\n })\n },\n\n /** Subscribe to OTA install status changes. Returns an unsubscribe. */\n onStatus: (cb: (status: OtaStatus | null) => void): (() => void) => {\n let last = useGlassesStore.getState().otaStatus\n return useGlassesStore.subscribe(() => {\n const status = useGlassesStore.getState().otaStatus\n if (status === last) return\n last = status\n cb(status)\n })\n },\n\n /** Subscribe to OTA snapshot changes. Returns an unsubscribe. */\n onSnapshot: (cb: (snapshot: OtaSnapshot) => void): (() => void) => {\n let last = JSON.stringify(projectSnapshot())\n return useGlassesStore.subscribe(() => {\n const snap = projectSnapshot()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n })\n },\n}\n"]}
1
+ {"version":3,"file":"ota.js","sourceRoot":"","sources":["../../src/facades/ota.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ota facade — `engine.ota`: the OEM-facing OTA read/observe surface. Engine's\n * OtaService projects the glasses' OTA BLE events into the engine store; this facade\n * exposes the snapshot + change subscriptions behind the shared OTA flow exported by\n * `@glassly/engine/ota`. Hosts retain only their surrounding navigation and theme/i18n adapters.\n *\n * Availability checks live in engine. Install orchestration lives in engine too:\n * `installSession` fronts the OtaInstallCoordinator state machine (WP 8B) — the host\n * progress screen is a pure renderer over its snapshot + attach/detach/retry/finish.\n */\nimport BluetoothSdk from \"@glassly/bluetooth-sdk\"\nimport type {\n OtaProgress,\n OtaProgressStatus,\n OtaStartAckEvent,\n OtaStatus,\n OtaUpdateInfo,\n ReleaseChangelog,\n} from \"@glassly/bluetooth-sdk\"\nimport {isGlassesConnected, isGlassesReady, useGlassesStore} from \"../stores/glasses\"\nimport {resolveOtaManifestUrl} from \"../services/otaManifestUrl\"\nimport {otaInstallCoordinator, type OtaInstallSnapshot} from \"../services/OtaInstallCoordinator\"\nimport {startGlassesStatusProjection} from \"../services/GlassesStatusProjection\"\nimport {startOtaService} from \"../services/OtaService\"\nimport {\n checkCurrentGlassesForUpdate,\n type OtaCheckCurrentGlassesOptions,\n type OtaCheckCurrentGlassesResult,\n} from \"../services/OtaUpdateCheckService\"\n\nfunction projectSnapshot() {\n const s = useGlassesStore.getState()\n return {\n connected: isGlassesConnected(s.connection),\n ready: isGlassesReady(s.connection),\n buildNumber: s.buildNumber || null,\n appVersion: s.appVersion || null,\n mtkFirmwareVersion: s.mtkFirmwareVersion || null,\n besFirmwareVersion: s.besFirmwareVersion || null,\n batteryLevel: s.batteryLevel >= 0 ? s.batteryLevel : null,\n hotspotOtaVersion: s.hotspotOtaVersion ?? 0,\n wifiConnected: s.wifi.state === \"connected\",\n wifiStatusKnown: s.wifiStatusKnown,\n manifestUrl: resolveOtaManifestUrl(s.otaVersionUrl, s.buildNumber),\n updateAvailable: s.otaUpdateAvailable,\n status: s.otaStatus,\n legacyProgress: s.otaProgress,\n inProgress: s.otaInProgress,\n mtkUpdatedThisSession: s.mtkUpdatedThisSession,\n }\n}\n\nexport type OtaSnapshot = ReturnType<typeof projectSnapshot>\nexport type {\n OtaProgress,\n OtaProgressStatus,\n OtaStatus,\n OtaUpdateInfo,\n ReleaseChangelog,\n OtaInstallSnapshot,\n OtaCheckCurrentGlassesOptions,\n OtaCheckCurrentGlassesResult,\n}\n\nexport const ota = {\n // --- actions ---\n /**\n * Start the device-status and OTA projections without starting the authenticated\n * cloud connection or miniapp runtime. This is the explicit entry point for Bluetooth-only hosts.\n * Idempotent and safe when the full engine runtime is already running.\n */\n initialize: async () => {\n const hydrated = startGlassesStatusProjection()\n startOtaService()\n await hydrated\n },\n /**\n * Start the firmware install with the resolved OTA manifest URL. Progress lands on\n * `status()`/`onStatus()`. (The host resolves the manifest URL — developer override,\n * host pin, or Engine release pin resolution stays with the OTA config; the stuck/retry watchdog is its own\n * resilience layer on top of this command.)\n */\n install: (otaVersionUrl?: string | null): Promise<OtaStartAckEvent> => BluetoothSdk.startOtaUpdate(otaVersionUrl),\n // Deferred: this facade entry was intended to become the engine-owned retry/check\n // action, but BluetoothSdk.checkForOtaUpdate() only returns a boolean. Exposing it\n // here would make callers think the rich otaUpdateAvailable read model is refreshed,\n // while the original view still depends on the host-side manifest compare to build\n // versionName/updates/totalSize. Keep that behavior unchanged until the whole rich\n // availability check moves into engine.\n // retry: () => BluetoothSdk.checkForOtaUpdate(),\n /**\n * Keep glasses awake while an OTA session is actively progressing.\n * @deprecated Legacy progress route (build < 37) only — removed with WP 8D.\n */\n ping: () => BluetoothSdk.ping(),\n /** Resolve and compare the current glasses against the OTA manifest, then update the OTA snapshot. */\n checkForUpdates: (options?: OtaCheckCurrentGlassesOptions) => checkCurrentGlassesForUpdate(options),\n /** Return bundled release changelogs crossed between two coordinated product versions, newest first. */\n getReleaseChangelogs: (fromVersion?: string | null, toVersion?: string | null): ReleaseChangelog[] =>\n BluetoothSdk.getReleaseChangelogs(fromVersion, toVersion),\n /** Clear the available-update prompt state. */\n clearUpdateAvailable: () => useGlassesStore.getState().setOtaUpdateAvailable(null),\n /** Clear active progress/status before entering a fresh install flow. */\n clearProgress: () => {\n const store = useGlassesStore.getState()\n store.setOtaProgress(null)\n store.setOtaStatus(null)\n },\n /**\n * Replace the pending update sequence after an in-flow firmware revalidation.\n * Kept as a named OTA policy operation so host screens do not mutate the raw\n * glasses store.\n * @deprecated Legacy progress route (build < 37) only — removed with WP 8D.\n */\n replacePendingUpdateSequence: (updates: OtaUpdateInfo[\"updates\"]): OtaUpdateInfo | null => {\n const store = useGlassesStore.getState()\n const current = store.otaUpdateAvailable\n if (!current) return null\n const next = {...current, updates}\n store.setOtaUpdateAvailable(next)\n return next\n },\n /**\n * Clear stale build metadata before returning to the update-check screen.\n * This forces the next version_info event through both the native observable\n * store and the engine projection after an APK reboot.\n * @deprecated Legacy progress route (build < 37) only — removed with WP 8D.\n */\n clearBuildNumberForNextCheck: () => {\n useGlassesStore.getState().setGlassesInfo({buildNumber: \"\"})\n },\n /**\n * Mark MTK as already applied during this app session until the glasses reboot/disconnect.\n * @deprecated Legacy progress route (build < 37) only — removed with WP 8D.\n */\n markMtkUpdatedThisSession: (updated: boolean) => useGlassesStore.getState().setMtkUpdatedThisSession(updated),\n\n /**\n * The OTA install state machine (unified `ota_status` sessions, build >= 37).\n * The host progress screen attaches on mount, detaches on unmount, and renders\n * the snapshot; every watchdog/retry/reconnect rule lives in the coordinator.\n */\n installSession: {\n /** Select Wi-Fi or capability-gated hotspot transport for this checked manifest. */\n prepare: (result: OtaCheckCurrentGlassesResult) => otaInstallCoordinator.prepare(result),\n /** Bind the machine to the mounted progress screen. Idempotent per attach/detach cycle. */\n attach: () => otaInstallCoordinator.attach(),\n /** Unbind on unmount: clears all timers/listeners and resets session state. */\n detach: () => otaInstallCoordinator.detach(),\n /** Retry after a failure: clear state and re-send ota_start (if connected). */\n retry: () => otaInstallCoordinator.retry(),\n /** Terminal cleanup for Continue/Done, including hotspot route teardown. */\n finish: () => otaInstallCoordinator.finish(),\n /** Abandon an interrupted session: await finish() then drop the session's status/progress. */\n discard: () => otaInstallCoordinator.discard(),\n /** Current install read model (displayState/errorMsg/lockout + glasses OTA data). */\n snapshot: (): OtaInstallSnapshot => otaInstallCoordinator.snapshot(),\n /** Subscribe to install snapshot changes (deduped on projected JSON). Returns an unsubscribe. */\n onSnapshot: (cb: (snapshot: OtaInstallSnapshot) => void): (() => void) => otaInstallCoordinator.onSnapshot(cb),\n },\n\n /** Current available-update info (versionName/updates/totalSize), or null. */\n updateAvailable: (): OtaUpdateInfo | null => useGlassesStore.getState().otaUpdateAvailable,\n /** Current OTA install status (stepType/phase/percent/status/error), or null. */\n status: (): OtaStatus | null => useGlassesStore.getState().otaStatus,\n /** Current OTA read model for update prompt/progress screens. */\n snapshot: (): OtaSnapshot => projectSnapshot(),\n\n /** Subscribe to OTA availability changes (info or null when cleared). Returns an unsubscribe. */\n onUpdateAvailable: (cb: (info: OtaUpdateInfo | null) => void): (() => void) => {\n let last = useGlassesStore.getState().otaUpdateAvailable\n return useGlassesStore.subscribe(() => {\n const info = useGlassesStore.getState().otaUpdateAvailable\n if (info === last) return\n last = info\n cb(info)\n })\n },\n\n /** Subscribe to OTA install status changes. Returns an unsubscribe. */\n onStatus: (cb: (status: OtaStatus | null) => void): (() => void) => {\n let last = useGlassesStore.getState().otaStatus\n return useGlassesStore.subscribe(() => {\n const status = useGlassesStore.getState().otaStatus\n if (status === last) return\n last = status\n cb(status)\n })\n },\n\n /** Subscribe to OTA snapshot changes. Returns an unsubscribe. */\n onSnapshot: (cb: (snapshot: OtaSnapshot) => void): (() => void) => {\n let last = JSON.stringify(projectSnapshot())\n return useGlassesStore.subscribe(() => {\n const snap = projectSnapshot()\n const key = JSON.stringify(snap)\n if (key === last) return\n last = key\n cb(snap)\n })\n },\n}\n"]}
@@ -1,11 +1,15 @@
1
1
  export declare const phoneNotifications: {
2
- /** The phone's installed apps (package, name, icon). (empty on iOS) */
2
+ /**
3
+ * Apps to show in the notification filter UI (package/bundle id, name, icon).
4
+ * Android: installed apps. iOS: seeded catalog ∪ glasses-reported apps
5
+ * (icons unavailable — iOS offers no cross-app icon lookup).
6
+ */
3
7
  installedApps: () => Promise<Array<{
4
8
  packageName: string;
5
9
  appName: string;
6
10
  icon: string | null;
7
11
  }>>;
8
- /** The per-app blocklist (package names whose notifications are NOT forwarded). */
12
+ /** The per-app blocklist (package/bundle ids whose notifications are NOT forwarded). */
9
13
  blocklist: () => string[];
10
14
  /** Replace the per-app blocklist. */
11
15
  setBlocklist: (packageNames: string[]) => import("typesafe-ts").AsyncResult<void, Error>;
@@ -13,5 +17,11 @@ export declare const phoneNotifications: {
13
17
  hasListenerPermission: () => Promise<boolean>;
14
18
  /** Open the OS settings to grant the notification-listener permission. (no-op on iOS) */
15
19
  requestListenerPermission: () => Promise<boolean> | undefined;
20
+ /**
21
+ * iOS: whether the connected glasses may read the phone's ANCS notification
22
+ * stream (the "Share System Notifications" Bluetooth consent). Always false
23
+ * on Android — Android capture is the notification listener instead.
24
+ */
25
+ ancsAuthorized: () => boolean;
16
26
  };
17
27
  //# sourceMappingURL=phoneNotifications.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"phoneNotifications.d.ts","sourceRoot":"","sources":["../../src/facades/phoneNotifications.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,kBAAkB;IAC7B,uEAAuE;yBACpD,OAAO,CAAC,KAAK,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAC,CAAC,CAAC;IAK9F,mFAAmF;qBACpE,MAAM,EAAE;IAIvB,qCAAqC;iCACR,MAAM,EAAE;IAGrC,qFAAqF;iCAC1D,OAAO,CAAC,OAAO,CAAC;IAI3C,yFAAyF;;CAK1F,CAAA"}
1
+ {"version":3,"file":"phoneNotifications.d.ts","sourceRoot":"","sources":["../../src/facades/phoneNotifications.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,kBAAkB;IAC7B;;;;OAIG;yBACgB,OAAO,CAAC,KAAK,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAC,CAAC,CAAC;IAY9F,wFAAwF;qBACzE,MAAM,EAAE;IAIvB,qCAAqC;iCACR,MAAM,EAAE;IAGrC,qFAAqF;iCAC1D,OAAO,CAAC,OAAO,CAAC;IAI3C,yFAAyF;;IAMzF;;;;OAIG;0BACiB,OAAO;CAC5B,CAAA"}